@@ -62,7 +73,10 @@ const translateValue = computed(() => {
{{ $t('CONVERSATION.REPLYBOX.PRIVATE_NOTE') }}
diff --git a/app/javascript/dashboard/components/widgets/conversation/ConversationCard.vue b/app/javascript/dashboard/components/widgets/conversation/ConversationCard.vue
index 57958ec0f..5cc59f8c8 100644
--- a/app/javascript/dashboard/components/widgets/conversation/ConversationCard.vue
+++ b/app/javascript/dashboard/components/widgets/conversation/ConversationCard.vue
@@ -3,6 +3,7 @@ import { computed, ref } from 'vue';
import { useRouter } from 'vue-router';
import { useStore, useMapGetter } from 'dashboard/composables/store';
import { getLastMessage } from 'dashboard/helper/conversationHelper';
+import { useVoiceCallStatus } from 'dashboard/composables/useVoiceCallStatus';
import { frontendURL, conversationUrl } from 'dashboard/helper/URLHelper';
import Avatar from 'next/avatar/Avatar.vue';
import MessagePreview from './MessagePreview.vue';
@@ -82,6 +83,16 @@ const isInboxNameVisible = computed(() => !activeInbox.value);
const lastMessageInChat = computed(() => getLastMessage(props.chat));
+const callStatus = computed(
+ () => props.chat.additional_attributes?.call_status
+);
+const callDirection = computed(
+ () => props.chat.additional_attributes?.call_direction
+);
+
+const { labelKey: voiceLabelKey, listIconColor: voiceIconColor } =
+ useVoiceCallStatus(callStatus, callDirection);
+
const inboxId = computed(() => props.chat.inbox_id);
const inbox = computed(() => {
@@ -306,14 +317,30 @@ const deleteConversation = () => {
>
{{ currentContact.name }}
+
+
+
+ {{ $t(voiceLabelKey) }}
+
+
diff --git a/app/javascript/dashboard/components/widgets/conversation/MessagesView.vue b/app/javascript/dashboard/components/widgets/conversation/MessagesView.vue
index 1f850cd74..3cb46c05f 100644
--- a/app/javascript/dashboard/components/widgets/conversation/MessagesView.vue
+++ b/app/javascript/dashboard/components/widgets/conversation/MessagesView.vue
@@ -4,6 +4,7 @@ import { ref, provide } from 'vue';
import { useConfig } from 'dashboard/composables/useConfig';
import { useKeyboardEvents } from 'dashboard/composables/useKeyboardEvents';
import { useAI } from 'dashboard/composables/useAI';
+import { useSnakeCase } from 'dashboard/composables/useTransformKeys';
// components
import ReplyBox from './ReplyBox.vue';
@@ -437,6 +438,11 @@ export default {
makeMessagesRead() {
this.$store.dispatch('markMessagesRead', { id: this.currentChat.id });
},
+ async handleMessageRetry(message) {
+ if (!message) return;
+ const payload = useSnakeCase(message);
+ await this.$store.dispatch('sendMessageWithData', payload);
+ },
},
};
@@ -465,6 +471,7 @@ export default {
:is-an-email-channel="isAnEmailChannel"
:inbox-supports-reply-to="inboxSupportsReplyTo"
:messages="getMessages"
+ @retry="handleMessageRetry"
>
diff --git a/app/javascript/dashboard/components/widgets/conversation/ReplyBox.vue b/app/javascript/dashboard/components/widgets/conversation/ReplyBox.vue
index 3db4779b0..4e3124a3c 100644
--- a/app/javascript/dashboard/components/widgets/conversation/ReplyBox.vue
+++ b/app/javascript/dashboard/components/widgets/conversation/ReplyBox.vue
@@ -170,6 +170,9 @@ export default {
}
return true;
},
+ isReplyRestricted() {
+ return !this.currentChat?.can_reply && !this.isAWhatsAppChannel;
+ },
inboxId() {
return this.currentChat.inbox_id;
},
@@ -1070,6 +1073,7 @@ export default {
{
it('returns fontSizeOptions with correct structure', () => {
const { fontSizeOptions } = useFontSize();
- expect(fontSizeOptions).toHaveLength(5);
- expect(fontSizeOptions[0]).toHaveProperty('value');
- expect(fontSizeOptions[0]).toHaveProperty('label');
+ expect(fontSizeOptions.value).toHaveLength(5);
+ expect(fontSizeOptions.value[0]).toHaveProperty('value');
+ expect(fontSizeOptions.value[0]).toHaveProperty('label');
// Check specific options
- expect(fontSizeOptions.find(option => option.value === '16px')).toEqual({
+ expect(
+ fontSizeOptions.value.find(option => option.value === '16px')
+ ).toEqual({
value: '16px',
label:
'PROFILE_SETTINGS.FORM.INTERFACE_SECTION.FONT_SIZE.OPTIONS.DEFAULT',
});
- expect(fontSizeOptions.find(option => option.value === '14px')).toEqual({
+ expect(
+ fontSizeOptions.value.find(option => option.value === '14px')
+ ).toEqual({
value: '14px',
label:
'PROFILE_SETTINGS.FORM.INTERFACE_SECTION.FONT_SIZE.OPTIONS.SMALLER',
@@ -143,12 +147,12 @@ describe('useFontSize', () => {
const { fontSizeOptions } = useFontSize();
// Check that translation is applied
- expect(fontSizeOptions.find(option => option.value === '14px').label).toBe(
- 'Smaller'
- );
- expect(fontSizeOptions.find(option => option.value === '16px').label).toBe(
- 'Default'
- );
+ expect(
+ fontSizeOptions.value.find(option => option.value === '14px').label
+ ).toBe('Smaller');
+ expect(
+ fontSizeOptions.value.find(option => option.value === '16px').label
+ ).toBe('Default');
// Verify translation function was called with correct keys
expect(mockTranslate).toHaveBeenCalledWith(
diff --git a/app/javascript/dashboard/composables/useAutomationValues.js b/app/javascript/dashboard/composables/useAutomationValues.js
index abc44f66b..5279f15e4 100644
--- a/app/javascript/dashboard/composables/useAutomationValues.js
+++ b/app/javascript/dashboard/composables/useAutomationValues.js
@@ -104,6 +104,7 @@ export default function useAutomationValues() {
contacts: contacts.value,
customAttributes: getters['attributes/getAttributes'].value,
inboxes: inboxes.value,
+ labels: labels.value,
statusFilterOptions: statusFilterOptions.value,
priorityOptions: priorityOptions.value,
messageTypeOptions: messageTypeOptions.value,
diff --git a/app/javascript/dashboard/composables/useFontSize.js b/app/javascript/dashboard/composables/useFontSize.js
index d7177a5fb..92d6f9e72 100644
--- a/app/javascript/dashboard/composables/useFontSize.js
+++ b/app/javascript/dashboard/composables/useFontSize.js
@@ -77,8 +77,8 @@ export const useFontSize = () => {
* Font size options for select dropdown
* @type {Array<{value: string, label: string}>}
*/
- const fontSizeOptions = FONT_SIZE_NAMES.map(name =>
- createFontSizeOption(t, name)
+ const fontSizeOptions = computed(() =>
+ FONT_SIZE_NAMES.map(name => createFontSizeOption(t, name))
);
/**
diff --git a/app/javascript/dashboard/composables/useVoiceCallStatus.js b/app/javascript/dashboard/composables/useVoiceCallStatus.js
new file mode 100644
index 000000000..111dab2ca
--- /dev/null
+++ b/app/javascript/dashboard/composables/useVoiceCallStatus.js
@@ -0,0 +1,161 @@
+import { computed, unref } from 'vue';
+
+const CALL_STATUSES = {
+ IN_PROGRESS: 'in-progress',
+ RINGING: 'ringing',
+ NO_ANSWER: 'no-answer',
+ BUSY: 'busy',
+ FAILED: 'failed',
+ COMPLETED: 'completed',
+ CANCELED: 'canceled',
+};
+
+const CALL_DIRECTIONS = {
+ INBOUND: 'inbound',
+ OUTBOUND: 'outbound',
+};
+
+/**
+ * Composable for handling voice call status display logic
+ * @param {Ref|string} statusRef - Call status (ringing, in-progress, etc.)
+ * @param {Ref|string} directionRef - Call direction (inbound, outbound)
+ * @returns {Object} UI properties for displaying call status
+ */
+export function useVoiceCallStatus(statusRef, directionRef) {
+ const status = computed(() => unref(statusRef)?.toString());
+ const direction = computed(() => unref(directionRef)?.toString());
+
+ // Status group helpers
+ const isFailedStatus = computed(() =>
+ [
+ CALL_STATUSES.NO_ANSWER,
+ CALL_STATUSES.BUSY,
+ CALL_STATUSES.FAILED,
+ ].includes(status.value)
+ );
+ const isEndedStatus = computed(() =>
+ [CALL_STATUSES.COMPLETED, CALL_STATUSES.CANCELED].includes(status.value)
+ );
+ const isOutbound = computed(
+ () => direction.value === CALL_DIRECTIONS.OUTBOUND
+ );
+
+ const labelKey = computed(() => {
+ const s = status.value;
+
+ if (s === CALL_STATUSES.IN_PROGRESS) {
+ return isOutbound.value
+ ? 'CONVERSATION.VOICE_CALL.OUTGOING_CALL'
+ : 'CONVERSATION.VOICE_CALL.CALL_IN_PROGRESS';
+ }
+
+ if (s === CALL_STATUSES.RINGING) {
+ return isOutbound.value
+ ? 'CONVERSATION.VOICE_CALL.OUTGOING_CALL'
+ : 'CONVERSATION.VOICE_CALL.INCOMING_CALL';
+ }
+
+ if (s === CALL_STATUSES.NO_ANSWER) {
+ return 'CONVERSATION.VOICE_CALL.MISSED_CALL';
+ }
+
+ if (isFailedStatus.value) {
+ return 'CONVERSATION.VOICE_CALL.NO_ANSWER';
+ }
+
+ if (isEndedStatus.value) {
+ return 'CONVERSATION.VOICE_CALL.CALL_ENDED';
+ }
+
+ return 'CONVERSATION.VOICE_CALL.INCOMING_CALL';
+ });
+
+ const subtextKey = computed(() => {
+ const s = status.value;
+
+ if (s === CALL_STATUSES.RINGING) {
+ return 'CONVERSATION.VOICE_CALL.NOT_ANSWERED_YET';
+ }
+
+ if (s === CALL_STATUSES.IN_PROGRESS) {
+ return isOutbound.value
+ ? 'CONVERSATION.VOICE_CALL.THEY_ANSWERED'
+ : 'CONVERSATION.VOICE_CALL.YOU_ANSWERED';
+ }
+
+ if (isFailedStatus.value) {
+ return 'CONVERSATION.VOICE_CALL.NO_ANSWER';
+ }
+
+ if (isEndedStatus.value) {
+ return 'CONVERSATION.VOICE_CALL.CALL_ENDED';
+ }
+
+ return 'CONVERSATION.VOICE_CALL.NOT_ANSWERED_YET';
+ });
+
+ const bubbleIconName = computed(() => {
+ const s = status.value;
+
+ if (s === CALL_STATUSES.IN_PROGRESS) {
+ return isOutbound.value
+ ? 'i-ph-phone-outgoing-fill'
+ : 'i-ph-phone-incoming-fill';
+ }
+
+ if (isFailedStatus.value) {
+ return 'i-ph-phone-x-fill';
+ }
+
+ // For ringing/completed/canceled show direction when possible
+ return isOutbound.value
+ ? 'i-ph-phone-outgoing-fill'
+ : 'i-ph-phone-incoming-fill';
+ });
+
+ const bubbleIconBg = computed(() => {
+ const s = status.value;
+
+ if (s === CALL_STATUSES.IN_PROGRESS) {
+ return 'bg-n-teal-9';
+ }
+
+ if (isFailedStatus.value) {
+ return 'bg-n-ruby-9';
+ }
+
+ if (isEndedStatus.value) {
+ return 'bg-n-slate-11';
+ }
+
+ // default (e.g., ringing)
+ return 'bg-n-teal-9 animate-pulse';
+ });
+
+ const listIconColor = computed(() => {
+ const s = status.value;
+
+ if (s === CALL_STATUSES.IN_PROGRESS || s === CALL_STATUSES.RINGING) {
+ return 'text-n-teal-9';
+ }
+
+ if (isFailedStatus.value) {
+ return 'text-n-ruby-9';
+ }
+
+ if (isEndedStatus.value) {
+ return 'text-n-slate-11';
+ }
+
+ return 'text-n-teal-9';
+ });
+
+ return {
+ status,
+ labelKey,
+ subtextKey,
+ bubbleIconName,
+ bubbleIconBg,
+ listIconColor,
+ };
+}
diff --git a/app/javascript/dashboard/constants/globals.js b/app/javascript/dashboard/constants/globals.js
index be3fa986c..a62aaf272 100644
--- a/app/javascript/dashboard/constants/globals.js
+++ b/app/javascript/dashboard/constants/globals.js
@@ -35,6 +35,8 @@ export default {
HELP_CENTER_DOCS_URL:
'https://www.chatwoot.com/docs/product/others/help-center',
TESTIMONIAL_URL: 'https://testimonials.cdn.chatwoot.com/content.json',
+ WHATSAPP_EMBEDDED_SIGNUP_DOCS_URL:
+ 'https://developers.facebook.com/docs/whatsapp/embedded-signup/custom-flows/onboarding-business-app-users#limitations',
SMALL_SCREEN_BREAKPOINT: 768,
AVAILABILITY_STATUS_KEYS: ['online', 'busy', 'offline'],
SNOOZE_OPTIONS: {
diff --git a/app/javascript/dashboard/featureFlags.js b/app/javascript/dashboard/featureFlags.js
index 28b6b09b7..85c21365c 100644
--- a/app/javascript/dashboard/featureFlags.js
+++ b/app/javascript/dashboard/featureFlags.js
@@ -1,6 +1,7 @@
export const FEATURE_FLAGS = {
AGENT_BOTS: 'agent_bots',
AGENT_MANAGEMENT: 'agent_management',
+ ASSIGNMENT_V2: 'assignment_v2',
AUTO_RESOLVE_CONVERSATIONS: 'auto_resolve_conversations',
AUTOMATIONS: 'automations',
CAMPAIGNS: 'campaigns',
@@ -37,8 +38,8 @@ export const FEATURE_FLAGS = {
REPORT_V4: 'report_v4',
CHANNEL_INSTAGRAM: 'channel_instagram',
CONTACT_CHATWOOT_SUPPORT_TEAM: 'contact_chatwoot_support_team',
- WHATSAPP_EMBEDDED_SIGNUP: 'whatsapp_embedded_signup',
CAPTAIN_V2: 'captain_integration_v2',
+ SAML: 'saml',
};
export const PREMIUM_FEATURES = [
diff --git a/app/javascript/dashboard/helper/automationHelper.js b/app/javascript/dashboard/helper/automationHelper.js
index 3723fd4d5..3e5f46f90 100644
--- a/app/javascript/dashboard/helper/automationHelper.js
+++ b/app/javascript/dashboard/helper/automationHelper.js
@@ -124,6 +124,7 @@ export const getConditionOptions = ({
customAttributes,
inboxes,
languages,
+ labels,
statusFilterOptions,
teams,
type,
@@ -150,6 +151,7 @@ export const getConditionOptions = ({
country_code: countries,
message_type: messageTypeOptions,
priority: priorityOptions,
+ labels: generateConditionOptions(labels, 'title'),
};
return conditionFilterMaps[type];
diff --git a/app/javascript/dashboard/helper/commons.js b/app/javascript/dashboard/helper/commons.js
index b12d1aa3d..3be7538bb 100644
--- a/app/javascript/dashboard/helper/commons.js
+++ b/app/javascript/dashboard/helper/commons.js
@@ -96,3 +96,18 @@ export const sanitizeVariableSearchKey = (searchKey = '') => {
.replace(/,/g, '') // remove commas
.trim();
};
+
+/**
+ * Convert underscore-separated string to title case.
+ * Eg. "round_robin" => "Round Robin"
+ * @param {string} str
+ * @returns {string}
+ */
+export const formatToTitleCase = str => {
+ return (
+ str
+ ?.replace(/_/g, ' ')
+ .replace(/\b\w/g, l => l.toUpperCase())
+ .trim() || ''
+ );
+};
diff --git a/app/javascript/dashboard/helper/featureHelper.js b/app/javascript/dashboard/helper/featureHelper.js
index ee61b0656..910a6bed6 100644
--- a/app/javascript/dashboard/helper/featureHelper.js
+++ b/app/javascript/dashboard/helper/featureHelper.js
@@ -19,6 +19,7 @@ const FEATURE_HELP_URLS = {
team_management: 'https://chwt.app/hc/teams',
webhook: 'https://chwt.app/hc/webhooks',
billing: 'https://chwt.app/pricing',
+ saml: 'https://chwt.app/hc/saml',
};
export function getHelpUrlForFeature(featureName) {
diff --git a/app/javascript/dashboard/helper/specs/commons.spec.js b/app/javascript/dashboard/helper/specs/commons.spec.js
index 466cdcf45..d892d3a94 100644
--- a/app/javascript/dashboard/helper/specs/commons.spec.js
+++ b/app/javascript/dashboard/helper/specs/commons.spec.js
@@ -5,6 +5,7 @@ import {
convertToCategorySlug,
convertToPortalSlug,
sanitizeVariableSearchKey,
+ formatToTitleCase,
} from '../commons';
describe('#getTypingUsersText', () => {
@@ -142,3 +143,51 @@ describe('sanitizeVariableSearchKey', () => {
expect(sanitizeVariableSearchKey()).toBe('');
});
});
+
+describe('formatToTitleCase', () => {
+ it('converts underscore-separated string to title case', () => {
+ expect(formatToTitleCase('round_robin')).toBe('Round Robin');
+ });
+
+ it('converts single word to title case', () => {
+ expect(formatToTitleCase('priority')).toBe('Priority');
+ });
+
+ it('converts multiple underscores to title case', () => {
+ expect(formatToTitleCase('auto_assignment_policy')).toBe(
+ 'Auto Assignment Policy'
+ );
+ });
+
+ it('handles already capitalized words', () => {
+ expect(formatToTitleCase('HIGH_PRIORITY')).toBe('HIGH PRIORITY');
+ });
+
+ it('handles mixed case with underscores', () => {
+ expect(formatToTitleCase('first_Name_last')).toBe('First Name Last');
+ });
+
+ it('handles empty string', () => {
+ expect(formatToTitleCase('')).toBe('');
+ });
+
+ it('handles null input', () => {
+ expect(formatToTitleCase(null)).toBe('');
+ });
+
+ it('handles undefined input', () => {
+ expect(formatToTitleCase(undefined)).toBe('');
+ });
+
+ it('handles string without underscores', () => {
+ expect(formatToTitleCase('hello')).toBe('Hello');
+ });
+
+ it('handles string with numbers', () => {
+ expect(formatToTitleCase('priority_1_high')).toBe('Priority 1 High');
+ });
+
+ it('handles leading and trailing underscores', () => {
+ expect(formatToTitleCase('_leading_trailing_')).toBe('Leading Trailing');
+ });
+});
diff --git a/app/javascript/dashboard/helper/specs/templateHelper.spec.js b/app/javascript/dashboard/helper/specs/templateHelper.spec.js
index 6e0661152..375e38a2d 100644
--- a/app/javascript/dashboard/helper/specs/templateHelper.spec.js
+++ b/app/javascript/dashboard/helper/specs/templateHelper.spec.js
@@ -218,6 +218,7 @@ describe('templateHelper', () => {
expect(result.header).toEqual({
media_url: '',
media_type: 'document',
+ media_name: '',
});
expect(result.body).toEqual({
1: '',
diff --git a/app/javascript/dashboard/helper/templateHelper.js b/app/javascript/dashboard/helper/templateHelper.js
index 5c9bbff05..1fb61d760 100644
--- a/app/javascript/dashboard/helper/templateHelper.js
+++ b/app/javascript/dashboard/helper/templateHelper.js
@@ -51,6 +51,11 @@ export const buildTemplateParameters = (template, hasMediaHeaderValue) => {
if (!allVariables.header) allVariables.header = {};
allVariables.header.media_url = '';
allVariables.header.media_type = headerComponent.format.toLowerCase();
+
+ // For document templates, include media_name field for filename support
+ if (headerComponent.format.toLowerCase() === 'document') {
+ allVariables.header.media_name = '';
+ }
}
// Process button variables
diff --git a/app/javascript/dashboard/i18n/locale/am/automation.json b/app/javascript/dashboard/i18n/locale/am/automation.json
index 80274f488..43245a1d5 100644
--- a/app/javascript/dashboard/i18n/locale/am/automation.json
+++ b/app/javascript/dashboard/i18n/locale/am/automation.json
@@ -177,7 +177,8 @@
"REFERER_LINK": "Referrer Link",
"ASSIGNEE_NAME": "Assignee",
"TEAM_NAME": "Team",
- "PRIORITY": "Priority"
+ "PRIORITY": "Priority",
+ "LABELS": "Labels"
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/am/contact.json b/app/javascript/dashboard/i18n/locale/am/contact.json
index b63f2e70b..84f4f0b58 100644
--- a/app/javascript/dashboard/i18n/locale/am/contact.json
+++ b/app/javascript/dashboard/i18n/locale/am/contact.json
@@ -610,6 +610,15 @@
"SEND_MESSAGE": "Send message"
}
},
+ "TWILIO_OPTIONS": {
+ "LABEL": "Select template",
+ "SEARCH_PLACEHOLDER": "Search templates",
+ "EMPTY_STATE": "No templates found",
+ "TEMPLATE_PARSER": {
+ "BACK": "Go back",
+ "SEND_MESSAGE": "Send message"
+ }
+ },
"ACTION_BUTTONS": {
"DISCARD": "Discard",
"SEND": "Send ({keyCode})"
diff --git a/app/javascript/dashboard/i18n/locale/am/contactFilters.json b/app/javascript/dashboard/i18n/locale/am/contactFilters.json
index bb3221c6e..4c62f0789 100644
--- a/app/javascript/dashboard/i18n/locale/am/contactFilters.json
+++ b/app/javascript/dashboard/i18n/locale/am/contactFilters.json
@@ -48,7 +48,8 @@
"CREATED_AT": "Created At",
"LAST_ACTIVITY": "Last Activity",
"REFERER_LINK": "Referrer link",
- "BLOCKED": "Blocked"
+ "BLOCKED": "Blocked",
+ "LABELS": "Labels"
},
"GROUPS": {
"STANDARD_FILTERS": "Standard Filters",
diff --git a/app/javascript/dashboard/i18n/locale/am/contentTemplates.json b/app/javascript/dashboard/i18n/locale/am/contentTemplates.json
new file mode 100644
index 000000000..a9b1d54c4
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/am/contentTemplates.json
@@ -0,0 +1,51 @@
+{
+ "CONTENT_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Twilio Templates",
+ "SUBTITLE": "Select the Twilio template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Search Templates",
+ "NO_TEMPLATES_FOUND": "No templates found for",
+ "NO_CONTENT": "No content",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Category",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No Twilio templates available. Click refresh to sync templates from Twilio.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Language",
+ "TEMPLATE_BODY": "Template Body",
+ "CATEGORY": "Category"
+ },
+ "TYPES": {
+ "MEDIA": "Media",
+ "QUICK_REPLY": "Quick Reply",
+ "TEXT": "Text"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variables",
+ "LANGUAGE": "Language",
+ "CATEGORY": "Category",
+ "VARIABLE_PLACEHOLDER": "Enter {variable} value",
+ "GO_BACK_LABEL": "Go Back",
+ "SEND_MESSAGE_LABEL": "Send Message",
+ "FORM_ERROR_MESSAGE": "Please fill all variables before sending",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "MEDIA_URL_LABEL": "Enter full media URL",
+ "MEDIA_URL_PLACEHOLDER": "https://example.com/image.jpg"
+ },
+ "FORM": {
+ "BACK_BUTTON": "Back",
+ "SEND_MESSAGE_BUTTON": "Send Message"
+ }
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/am/conversation.json b/app/javascript/dashboard/i18n/locale/am/conversation.json
index bd875dc35..ecd318834 100644
--- a/app/javascript/dashboard/i18n/locale/am/conversation.json
+++ b/app/javascript/dashboard/i18n/locale/am/conversation.json
@@ -35,6 +35,11 @@
"API_HOURS_WINDOW": "ለዚህ ውይይት መመለስ በ{hours} ሰአታት ውስጥ ብቻ ይቻላል",
"NOT_ASSIGNED_TO_YOU": "This conversation is not assigned to you. Would you like to assign this conversation to yourself?",
"ASSIGN_TO_ME": "Assign to me",
+ "BOT_HANDOFF_MESSAGE": "You are responding to a conversation which is currently handled by an assistant or a bot.",
+ "BOT_HANDOFF_ACTION": "Mark open and assign to you",
+ "BOT_HANDOFF_REOPEN_ACTION": "Mark conversation open",
+ "BOT_HANDOFF_SUCCESS": "Conversation has been handed over to you",
+ "BOT_HANDOFF_ERROR": "Failed to take over the conversation. Please try again.",
"TWILIO_WHATSAPP_CAN_REPLY": "You can only reply to this conversation using a template message due to",
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "24 hour message window restriction",
"OLD_INSTAGRAM_INBOX_REPLY_BANNER": "ይህ የInstagram መለያ ወደ አዲሱ የInstagram ቻናል ገቢ ሳጥን ተዛውሯል። ሁሉም አዲስ መልዕክቶች በዚያ ይታያሉ። ከአሁን ጀምሮ ከዚህ ውይይት መልዕክቶች መላክ አትችሉም።",
@@ -66,6 +71,17 @@
"SHOW_LABELS": "Show labels",
"HIDE_LABELS": "Hide labels"
},
+ "VOICE_CALL": {
+ "INCOMING_CALL": "Incoming call",
+ "OUTGOING_CALL": "Outgoing call",
+ "CALL_IN_PROGRESS": "Call in progress",
+ "NO_ANSWER": "No answer",
+ "MISSED_CALL": "Missed call",
+ "CALL_ENDED": "Call ended",
+ "NOT_ANSWERED_YET": "Not answered yet",
+ "THEY_ANSWERED": "They answered",
+ "YOU_ANSWERED": "You answered"
+ },
"HEADER": {
"RESOLVE_ACTION": "Resolve",
"REOPEN_ACTION": "Reopen",
diff --git a/app/javascript/dashboard/i18n/locale/am/helpCenter.json b/app/javascript/dashboard/i18n/locale/am/helpCenter.json
index fd2b1a788..0ab8d62ff 100644
--- a/app/javascript/dashboard/i18n/locale/am/helpCenter.json
+++ b/app/javascript/dashboard/i18n/locale/am/helpCenter.json
@@ -741,7 +741,8 @@
"LIVE_CHAT_WIDGET": {
"LABEL": "Live chat widget",
"PLACEHOLDER": "Select live chat widget",
- "HELP_TEXT": "Select a live chat widget that will appear on your help center"
+ "HELP_TEXT": "Select a live chat widget that will appear on your help center",
+ "NONE_OPTION": "No widget"
},
"BRAND_COLOR": {
"LABEL": "Brand color"
@@ -807,6 +808,58 @@
"ERROR_MESSAGE": "Unable to update portal"
}
}
+ },
+ "PDF_UPLOAD": {
+ "TITLE": "Upload PDF Document",
+ "DESCRIPTION": "Upload a PDF document to automatically generate FAQs using AI",
+ "DRAG_DROP_TEXT": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "ADDITIONAL_CONTEXT_LABEL": "Additional Context (Optional)",
+ "ADDITIONAL_CONTEXT_PLACEHOLDER": "Provide any additional context or instructions for FAQ generation...",
+ "UPLOADING": "Uploading...",
+ "UPLOAD": "Upload & Process",
+ "CANCEL": "Cancel",
+ "ERROR_INVALID_TYPE": "Please select a valid PDF file",
+ "ERROR_FILE_TOO_LARGE": "File size must be less than 512MB",
+ "ERROR_UPLOAD_FAILED": "Failed to upload PDF. Please try again."
+ },
+ "PDF_DOCUMENTS": {
+ "TITLE": "PDF Documents",
+ "DESCRIPTION": "Manage uploaded PDF documents and generate FAQs from them",
+ "UPLOAD_PDF": "Upload PDF",
+ "UPLOAD_FIRST_PDF": "Upload your first PDF",
+ "UPLOADED_BY": "Uploaded by",
+ "GENERATE_FAQS": "Generate FAQs",
+ "GENERATING": "Generating...",
+ "CONFIRM_DELETE": "Are you sure you want to delete {filename}?",
+ "EMPTY_STATE": {
+ "TITLE": "No PDF documents yet",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQs using AI"
+ },
+ "STATUS": {
+ "UPLOADED": "Ready",
+ "PROCESSING": "Processing",
+ "PROCESSED": "Completed",
+ "FAILED": "Failed"
+ }
+ },
+ "CONTENT_GENERATION": {
+ "TITLE": "Content Generation",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQ content using AI",
+ "UPLOAD_TITLE": "Upload PDF Document",
+ "DRAG_DROP": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "UPLOADING": "Processing document...",
+ "UPLOAD_SUCCESS": "Document processed successfully!",
+ "UPLOAD_ERROR": "Failed to upload document. Please try again.",
+ "INVALID_FILE_TYPE": "Please select a valid PDF file",
+ "FILE_TOO_LARGE": "File size must be less than 512MB",
+ "GENERATED_CONTENT": "Generated FAQ Content",
+ "PUBLISH_SELECTED": "Publish Selected",
+ "PUBLISHING": "Publishing...",
+ "FROM_DOCUMENT": "From document",
+ "NO_CONTENT": "No generated content available. Upload a PDF document to get started.",
+ "LOADING": "Loading generated content..."
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/am/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/am/inboxMgmt.json
index 6f44ec046..a525921db 100644
--- a/app/javascript/dashboard/i18n/locale/am/inboxMgmt.json
+++ b/app/javascript/dashboard/i18n/locale/am/inboxMgmt.json
@@ -272,8 +272,8 @@
},
"SUBMIT_BUTTON": "Create WhatsApp Channel",
"EMBEDDED_SIGNUP": {
- "TITLE": "Quick Setup with Meta",
- "DESC": "You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
+ "TITLE": "Quick setup with Meta",
+ "DESC": "Use the WhatsApp Embedded Signup flow to quickly connect new numbers. You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
"BENEFITS": {
"TITLE": "Benefits of Embedded Signup:",
"EASY_SETUP": "No manual configuration required",
@@ -281,9 +281,8 @@
"AUTO_CONFIG": "Automatic webhook and phone number configuration"
},
"LEARN_MORE": {
- "TEXT": "To learn more about integrated signup, pricing, and limitations, visit",
- "LINK_TEXT": "this link.",
- "LINK_URL": "https://developers.facebook.com/docs/whatsapp/embedded-signup/custom-flows/onboarding-business-app-users#limitations"
+ "TEXT": "To learn more about integrated signup, pricing, and limitations, visit {link}.",
+ "LINK_TEXT": "this link"
},
"SUBMIT_BUTTON": "Connect with WhatsApp Business",
"AUTH_PROCESSING": "Authenticating with Meta",
@@ -296,7 +295,9 @@
"INVALID_BUSINESS_DATA": "Invalid business data received from Facebook. Please try again.",
"SIGNUP_ERROR": "Signup error occurred",
"AUTH_NOT_COMPLETED": "Authentication not completed. Please restart the process.",
- "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured"
+ "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured",
+ "MANUAL_FALLBACK": "If your number is already connected to the WhatsApp Business Platform (API), or if you’re a tech provider onboarding your own number, please use the {link} flow",
+ "MANUAL_LINK_TEXT": "manual setup flow"
},
"API": {
"ERROR_MESSAGE": "We were not able to save the WhatsApp channel"
@@ -424,7 +425,51 @@
},
"AUTH": {
"TITLE": "Choose a channel",
- "DESC": "Chatwoot supports live-chat widgets, Facebook Messenger, WhatsApp, Emails, etc., as channels. If you want to build a custom channel, you can create it using the API channel. To get started, choose one of the channels below."
+ "DESC": "Chatwoot supports live-chat widgets, Facebook Messenger, WhatsApp, Emails, etc., as channels. If you want to build a custom channel, you can create it using the API channel. To get started, choose one of the channels below.",
+ "TITLE_NEXT": "Complete the setup",
+ "TITLE_FINISH": "Voilà!",
+ "CHANNEL": {
+ "WEBSITE": {
+ "TITLE": "Website",
+ "DESCRIPTION": "Create a live-chat widget"
+ },
+ "FACEBOOK": {
+ "TITLE": "Facebook",
+ "DESCRIPTION": "Connect your Facebook page"
+ },
+ "WHATSAPP": {
+ "TITLE": "WhatsApp",
+ "DESCRIPTION": "Support your customers on WhatsApp"
+ },
+ "EMAIL": {
+ "TITLE": "Email",
+ "DESCRIPTION": "Connect with Gmail, Outlook, or other providers"
+ },
+ "SMS": {
+ "TITLE": "SMS",
+ "DESCRIPTION": "Integrate SMS channel with Twilio or bandwidth"
+ },
+ "API": {
+ "TITLE": "API",
+ "DESCRIPTION": "Make a custom channel using our API"
+ },
+ "TELEGRAM": {
+ "TITLE": "Telegram",
+ "DESCRIPTION": "Configure Telegram channel using Bot token"
+ },
+ "LINE": {
+ "TITLE": "Line",
+ "DESCRIPTION": "Integrate your Line channel"
+ },
+ "INSTAGRAM": {
+ "TITLE": "Instagram",
+ "DESCRIPTION": "Connect your instagram account"
+ },
+ "VOICE": {
+ "TITLE": "Voice",
+ "DESCRIPTION": "Integrate with Twilio Voice"
+ }
+ }
},
"AGENTS": {
"TITLE": "Agents",
@@ -478,7 +523,10 @@
"MESSAGE": "You can now engage with your customers through your new Channel. Happy supporting",
"BUTTON_TEXT": "Take me there",
"MORE_SETTINGS": "More settings",
- "WEBSITE_SUCCESS": "You have successfully finished creating a website channel. Copy the code shown below and paste it on your website. Next time a customer use the live chat, the conversation will automatically appear on your inbox."
+ "WEBSITE_SUCCESS": "You have successfully finished creating a website channel. Copy the code shown below and paste it on your website. Next time a customer use the live chat, the conversation will automatically appear on your inbox.",
+ "WHATSAPP_QR_INSTRUCTION": "Scan the QR code above to quickly test your WhatsApp inbox",
+ "MESSENGER_QR_INSTRUCTION": "Scan the QR code above to quickly test your Facebook Messenger inbox",
+ "TELEGRAM_QR_INSTRUCTION": "Scan the QR code above to quickly test your Telegram inbox"
},
"REAUTH": "Reauthorize",
"VIEW": "View",
@@ -868,9 +916,18 @@
"SCRIPT_SETTINGS": "\n window.chatwootSettings = {options};"
},
"EMAIL_PROVIDERS": {
- "MICROSOFT": "Microsoft",
- "GOOGLE": "Google",
- "OTHER_PROVIDERS": "Other Providers"
+ "MICROSOFT": {
+ "TITLE": "Microsoft",
+ "DESCRIPTION": "Connect with Microsoft"
+ },
+ "GOOGLE": {
+ "TITLE": "Google",
+ "DESCRIPTION": "Connect with Google"
+ },
+ "OTHER_PROVIDERS": {
+ "TITLE": "Other Providers",
+ "DESCRIPTION": "Connect with Other Providers"
+ }
},
"CHANNELS": {
"MESSENGER": "Messenger",
diff --git a/app/javascript/dashboard/i18n/locale/am/integrations.json b/app/javascript/dashboard/i18n/locale/am/integrations.json
index be9281284..c59ec66df 100644
--- a/app/javascript/dashboard/i18n/locale/am/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/am/integrations.json
@@ -704,11 +704,28 @@
"ERROR_MESSAGE": "There was an error creating the document, please try again."
},
"FORM": {
+ "TYPE": {
+ "LABEL": "Document Type",
+ "URL": "URL",
+ "PDF": "PDF File"
+ },
"URL": {
"LABEL": "URL",
"PLACEHOLDER": "Enter the URL of the document",
"ERROR": "Please provide a valid URL for the document"
},
+ "PDF_FILE": {
+ "LABEL": "PDF File",
+ "CHOOSE_FILE": "Choose PDF file",
+ "ERROR": "Please select a PDF file",
+ "HELP_TEXT": "Maximum file size: 10MB",
+ "INVALID_TYPE": "Please select a valid PDF file",
+ "TOO_LARGE": "File size exceeds 10MB limit"
+ },
+ "NAME": {
+ "LABEL": "Document Name (Optional)",
+ "PLACEHOLDER": "Enter a name for the document"
+ },
"ASSISTANT": {
"LABEL": "Assistant",
"PLACEHOLDER": "Select the assistant",
@@ -744,6 +761,7 @@
"SELECTED": "{count} selected",
"SELECT_ALL": "Select all ({count})",
"UNSELECT_ALL": "Unselect all ({count})",
+ "SEARCH_PLACEHOLDER": "Search FAQs...",
"BULK_APPROVE_BUTTON": "Approve",
"BULK_DELETE_BUTTON": "Delete",
"BULK_APPROVE": {
diff --git a/app/javascript/dashboard/i18n/locale/am/mfa.json b/app/javascript/dashboard/i18n/locale/am/mfa.json
new file mode 100644
index 000000000..f7556fdcf
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/am/mfa.json
@@ -0,0 +1,106 @@
+{
+ "MFA_SETTINGS": {
+ "TITLE": "Two-Factor Authentication",
+ "SUBTITLE": "Secure your account with TOTP-based authentication",
+ "DESCRIPTION": "Add an extra layer of security to your account using a time-based one-time password (TOTP)",
+ "STATUS_TITLE": "Authentication Status",
+ "STATUS_DESCRIPTION": "Manage your two-factor authentication settings and backup recovery codes",
+ "ENABLED": "Enabled",
+ "DISABLED": "Disabled",
+ "STATUS_ENABLED": "Two-factor authentication is active",
+ "STATUS_ENABLED_DESC": "Your account is protected with an additional layer of security",
+ "ENABLE_BUTTON": "Enable Two-Factor Authentication",
+ "ENHANCE_SECURITY": "Enhance Your Account Security",
+ "ENHANCE_SECURITY_DESC": "Two-factor authentication adds an extra layer of security by requiring a verification code from your authenticator app in addition to your password.",
+ "SETUP": {
+ "STEP_NUMBER_1": "1",
+ "STEP_NUMBER_2": "2",
+ "STEP1_TITLE": "Scan QR Code with Your Authenticator App",
+ "STEP1_DESCRIPTION": "Use Google Authenticator, Authy, or any TOTP-compatible app",
+ "LOADING_QR": "Loading...",
+ "MANUAL_ENTRY": "Can't scan? Enter code manually",
+ "SECRET_KEY": "Secret Key",
+ "COPY": "Copy",
+ "ENTER_CODE": "Enter the 6-digit code from your authenticator app",
+ "ENTER_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify & Continue",
+ "CANCEL": "Cancel",
+ "ERROR_STARTING": "MFA not enabled. Please contact administrator.",
+ "INVALID_CODE": "Invalid verification code",
+ "SECRET_COPIED": "Secret key copied to clipboard",
+ "SUCCESS": "Two-factor authentication has been enabled successfully"
+ },
+ "BACKUP": {
+ "TITLE": "Save Your Backup Codes",
+ "DESCRIPTION": "Keep these codes safe. Each can be used once if you lose access to your authenticator",
+ "IMPORTANT": "Important:",
+ "IMPORTANT_NOTE": " Save these codes in a secure location. You won't be able to see them again.",
+ "DOWNLOAD": "Download",
+ "COPY_ALL": "Copy All",
+ "CONFIRM": "I have saved my backup codes in a secure location and understand that I won't be able to see them again",
+ "COMPLETE_SETUP": "Complete Setup",
+ "CODES_COPIED": "Backup codes copied to clipboard"
+ },
+ "MANAGEMENT": {
+ "BACKUP_CODES": "Backup Codes",
+ "BACKUP_CODES_DESC": "Generate new codes if you've lost or used your existing ones",
+ "REGENERATE": "Regenerate Backup Codes",
+ "DISABLE_MFA": "Disable 2FA",
+ "DISABLE_MFA_DESC": "Remove two-factor authentication from your account",
+ "DISABLE_BUTTON": "Disable Two-Factor Authentication"
+ },
+ "DISABLE": {
+ "TITLE": "Disable Two-Factor Authentication",
+ "DESCRIPTION": "You'll need to enter your password and a verification code to disable two-factor authentication.",
+ "PASSWORD": "Password",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Disable 2FA",
+ "CANCEL": "Cancel",
+ "SUCCESS": "Two-factor authentication has been disabled",
+ "ERROR": "Failed to disable MFA. Please check your credentials."
+ },
+ "REGENERATE": {
+ "TITLE": "Regenerate Backup Codes",
+ "DESCRIPTION": "This will invalidate your existing backup codes and generate new ones. Enter your verification code to continue.",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Generate New Codes",
+ "CANCEL": "Cancel",
+ "NEW_CODES_TITLE": "New Backup Codes Generated",
+ "NEW_CODES_DESC": "Your old backup codes have been invalidated. Save these new codes in a secure location.",
+ "CODES_IMPORTANT": "Important:",
+ "CODES_IMPORTANT_NOTE": " Each code can only be used once. Save them before closing this window.",
+ "DOWNLOAD_CODES": "Download Codes",
+ "COPY_ALL_CODES": "Copy All Codes",
+ "CODES_SAVED": "I've Saved My Codes",
+ "SUCCESS": "New backup codes have been generated",
+ "ERROR": "Failed to regenerate backup codes"
+ }
+ },
+ "MFA_VERIFICATION": {
+ "TITLE": "Two-Factor Authentication",
+ "DESCRIPTION": "Enter your verification code to continue",
+ "AUTHENTICATOR_APP": "Authenticator App",
+ "BACKUP_CODE": "Backup Code",
+ "ENTER_OTP_CODE": "Enter 6-digit code from your authenticator app",
+ "ENTER_BACKUP_CODE": "Enter one of your backup codes",
+ "BACKUP_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify",
+ "TRY_ANOTHER_METHOD": "Try another verification method",
+ "CANCEL_LOGIN": "Cancel and return to login",
+ "HELP_TEXT": "Having trouble signing in?",
+ "LEARN_MORE": "Learn more about 2FA",
+ "HELP_MODAL": {
+ "TITLE": "Two-Factor Authentication Help",
+ "AUTHENTICATOR_TITLE": "Using an Authenticator App",
+ "AUTHENTICATOR_DESC": "Open your authenticator app (Google Authenticator, Authy, etc.) and enter the 6-digit code shown for your account.",
+ "BACKUP_TITLE": "Using a Backup Code",
+ "BACKUP_DESC": "If you don't have access to your authenticator app, you can use one of the backup codes you saved when setting up 2FA. Each code can only be used once.",
+ "CONTACT_TITLE": "Need More Help?",
+ "CONTACT_DESC_CLOUD": "If you've lost access to both your authenticator app and backup codes, please reach out to Chatwoot support for assistance.",
+ "CONTACT_DESC_SELF_HOSTED": "If you've lost access to both your authenticator app and backup codes, please contact your administrator for assistance."
+ },
+ "VERIFICATION_FAILED": "Verification failed. Please try again."
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/am/settings.json b/app/javascript/dashboard/i18n/locale/am/settings.json
index d547538db..9ddc3b805 100644
--- a/app/javascript/dashboard/i18n/locale/am/settings.json
+++ b/app/javascript/dashboard/i18n/locale/am/settings.json
@@ -51,6 +51,13 @@
"LARGER": "Larger",
"EXTRA_LARGE": "Extra Large"
}
+ },
+ "LANGUAGE": {
+ "TITLE": "Preferred Language",
+ "NOTE": "Choose the language you want to use.",
+ "UPDATE_SUCCESS": "Your Language settings have been updated successfully",
+ "UPDATE_ERROR": "There is an error while updating the language settings, please try again",
+ "USE_ACCOUNT_DEFAULT": "Use account default"
}
},
"MESSAGE_SIGNATURE_SECTION": {
@@ -73,6 +80,11 @@
"NOTE": "Updating your password would reset your logins in multiple devices.",
"BTN_TEXT": "Change password"
},
+ "SECURITY_SECTION": {
+ "TITLE": "Security",
+ "NOTE": "Manage additional security features for your account.",
+ "MFA_BUTTON": "Manage Two-Factor Authentication"
+ },
"ACCESS_TOKEN": {
"TITLE": "Access Token",
"NOTE": "This token can be used if you are building an API based integration",
@@ -330,6 +342,7 @@
"REPORTS_LABEL": "Labels",
"REPORTS_INBOX": "Inbox",
"REPORTS_TEAM": "Team",
+ "AGENT_ASSIGNMENT": "Agent Assignment",
"SET_AVAILABILITY_TITLE": "Set yourself as",
"SET_YOUR_AVAILABILITY": "Set your availability",
"SLA": "SLA",
@@ -350,7 +363,8 @@
"INFO_TEXT": "Let the system automatically mark you offline when you aren't using the app or dashboard.",
"INFO_SHORT": "Automatically mark offline when you aren't using the app."
},
- "DOCS": "Read docs"
+ "DOCS": "Read docs",
+ "SECURITY": "Security"
},
"BILLING_SETTINGS": {
"TITLE": "Billing",
@@ -382,6 +396,77 @@
},
"NO_BILLING_USER": "Your billing account is being configured. Please refresh the page and try again."
},
+ "SECURITY_SETTINGS": {
+ "TITLE": "Security",
+ "DESCRIPTION": "Manage your account security settings.",
+ "LINK_TEXT": "Learn more about SAML SSO",
+ "SAML": {
+ "TITLE": "SAML SSO",
+ "NOTE": "Configure SAML single sign-on for your account. Users will authenticate through your identity provider instead of using email/password.",
+ "ACS_URL": {
+ "LABEL": "ACS URL",
+ "TOOLTIP": "Assertion Consumer Service URL - Configure this URL in your IdP as the destination for SAML responses"
+ },
+ "SSO_URL": {
+ "LABEL": "SSO URL",
+ "HELP": "The URL where SAML authentication requests will be sent",
+ "PLACEHOLDER": "https://your-idp.com/saml/sso"
+ },
+ "CERTIFICATE": {
+ "LABEL": "Signing certificate in PEM format",
+ "HELP": "The public certificate from your identity provider used to verify SAML responses",
+ "PLACEHOLDER": "-----BEGIN CERTIFICATE-----\nMIIC..."
+ },
+ "FINGERPRINT": {
+ "LABEL": "Fingerprint",
+ "TOOLTIP": "SHA-1 fingerprint of the certificate - Use this to verify the certificate in your IdP configuration"
+ },
+ "COPY_SUCCESS": "Copied to clipboard",
+ "SP_ENTITY_ID": {
+ "LABEL": "SP Entity ID",
+ "HELP": "Unique identifier for this application as a service provider (auto-generated).",
+ "TOOLTIP": "Unique identifier for Chatwoot as the Service Provider - Configure this in your IdP settings"
+ },
+ "IDP_ENTITY_ID": {
+ "LABEL": "Identity Provider Entity ID",
+ "HELP": "Unique identifier for your identity provider (usually found in IdP configuration)",
+ "PLACEHOLDER": "https://your-idp.com/saml"
+ },
+ "UPDATE_BUTTON": "Update SAML Settings",
+ "API": {
+ "SUCCESS": "SAML settings updated successfully",
+ "ERROR": "Failed to update SAML settings",
+ "ERROR_LOADING": "Failed to load SAML settings",
+ "DISABLED": "SAML settings disabled successfully"
+ },
+ "VALIDATION": {
+ "REQUIRED_FIELDS": "SSO URL, Identity Provider Entity ID, and Certificate are required fields",
+ "SSO_URL_ERROR": "Please enter a valid SSO URL",
+ "CERTIFICATE_ERROR": "Certificate is required",
+ "IDP_ENTITY_ID_ERROR": "Identity Provider Entity ID is required"
+ },
+ "ENTERPRISE_PAYWALL": {
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade to an Enterprise plan to access SAML single sign-on and other advanced security features.",
+ "ASK_ADMIN": "Please reach out to your administrator for the upgrade."
+ },
+ "PAYWALL": {
+ "TITLE": "Upgrade to enable SAML SSO",
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade your plan to get access to SAML single sign-on and other advanced features.",
+ "UPGRADE_NOW": "Upgrade now",
+ "CANCEL_ANYTIME": "You can change or cancel your plan anytime"
+ },
+ "ATTRIBUTE_MAPPING": {
+ "TITLE": "SAML Attribute Setup",
+ "DESCRIPTION": "The following attribute mappings must be configured in your identity provider"
+ },
+ "INFO_SECTION": {
+ "TITLE": "Service Provider Information",
+ "TOOLTIP": "Copy these values and configure them in your Identity Provider to establish the SAML connection"
+ }
+ }
+ },
"CREATE_ACCOUNT": {
"NO_ACCOUNT_WARNING": "Uh oh! We could not find any Chatwoot accounts. Please create a new account to continue.",
"NEW_ACCOUNT": "New Account",
@@ -418,5 +503,255 @@
"SWITCH_TO_REPLY": "Switch to Reply",
"TOGGLE_SNOOZE_DROPDOWN": "Toggle snooze dropdown"
}
+ },
+ "ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent assignment",
+ "DESCRIPTION": "Define policies to effectively manage workload and route conversations based on the needs of inboxes and agents. Learn more here"
+ },
+ "ASSIGNMENT_POLICY": {
+ "TITLE": "Assignment policy",
+ "DESCRIPTION": "Manage how conversations get assigned in inboxes.",
+ "FEATURES": [
+ "Assign by conversations evenly or by available capacity",
+ "Add fair distribution rules to avoid overloading any agent",
+ "Add inboxes to a policy - one policy per inbox"
+ ]
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "TITLE": "Agent capacity policy",
+ "DESCRIPTION": "Manage workload for agents.",
+ "FEATURES": [
+ "Define maximum conversations per inbox",
+ "Create exceptions based on labels and time",
+ "Add agents to a policy - one policy per agent"
+ ]
+ }
+ },
+ "AGENT_ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Assignment policy",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "ORDER": "Order",
+ "PRIORITY": "Priority",
+ "ACTIVE": "Active",
+ "INACTIVE": "Inactive",
+ "POPOVER": "Added inboxes",
+ "EDIT": "Edit"
+ },
+ "NO_RECORDS_FOUND": "No assignment policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create assignment policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy created successfully",
+ "ERROR_MESSAGE": "Failed to create assignment policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit assignment policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_INBOX_DIALOG": {
+ "TITLE": "Add inbox",
+ "DESCRIPTION": "{inboxName} inbox is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Cancel"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update assignment policy"
+ },
+ "INBOX_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Inbox added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Inbox removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove inbox from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Description:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "STATUS": {
+ "LABEL": "Status:",
+ "PLACEHOLDER": "Select status",
+ "ACTIVE": "Policy is active",
+ "INACTIVE": "Policy is inactive"
+ },
+ "ASSIGNMENT_ORDER": {
+ "LABEL": "Assignment order",
+ "ROUND_ROBIN": {
+ "LABEL": "Round robin",
+ "DESCRIPTION": "Assign conversations evenly among agents."
+ },
+ "BALANCED": {
+ "LABEL": "Balanced",
+ "DESCRIPTION": "Assign conversations based on available capacity."
+ }
+ },
+ "ASSIGNMENT_PRIORITY": {
+ "LABEL": "Assignment priority",
+ "EARLIEST_CREATED": {
+ "LABEL": "Earliest created",
+ "DESCRIPTION": "The conversation that was created first gets assigned first."
+ },
+ "LONGEST_WAITING": {
+ "LABEL": "Longest waiting",
+ "DESCRIPTION": "The conversation waiting the longest gets assigned first."
+ }
+ },
+ "FAIR_DISTRIBUTION": {
+ "LABEL": "Fair distribution policy",
+ "DESCRIPTION": "Set the maximum number of conversations that can be assigned per agent within a time window to avoid overloading any one agent. This required field defaults to 100 conversations per hour.",
+ "INPUT_MAX": "Assign max",
+ "DURATION": "Conversations per agent in every"
+ },
+ "INBOXES": {
+ "LABEL": "Added inboxes",
+ "DESCRIPTION": "Add inboxes for which this policy will be applicable.",
+ "ADD_BUTTON": "Add inbox",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select inboxes to add",
+ "ADD_BUTTON": "Add"
+ },
+ "EMPTY_STATE": "No inboxes added to this policy, add an inbox to get started",
+ "API": {
+ "SUCCESS_MESSAGE": "Inbox successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Assignment policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete assignment policy"
+ }
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent capacity",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "POPOVER": "Added agents",
+ "EDIT": "Edit"
+ },
+ "NO_RECORDS_FOUND": "No agent capacity policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create agent capacity policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy created successfully",
+ "ERROR_MESSAGE": "Failed to create agent capacity policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit agent capacity policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_AGENT_DIALOG": {
+ "TITLE": "Add agent",
+ "DESCRIPTION": "{agentName} is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Cancel"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update agent capacity policy"
+ },
+ "AGENT_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Agent added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Agent removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove agent from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Description:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "INBOX_CAPACITY_LIMIT": {
+ "LABEL": "Inbox capacity limits",
+ "ADD_BUTTON": "Add inbox",
+ "FIELD": {
+ "SELECT_INBOX": "Select inbox",
+ "MAX_CONVERSATIONS": "Max conversations",
+ "SET_LIMIT": "Set limit"
+ },
+ "EMPTY_STATE": "No inbox limit set"
+ },
+ "EXCLUSION_RULES": {
+ "LABEL": "Exclusion rules",
+ "DESCRIPTION": "Conversations that satisfy the following conditions would not count towards agent capacity",
+ "TAGS": {
+ "LABEL": "Exclude conversations tagged with specific labels",
+ "ADD_TAG": "add tag",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select tags to add"
+ },
+ "EMPTY_STATE": "No tags added to this policy."
+ },
+ "DURATION": {
+ "LABEL": "Exclude conversations older than a specified duration",
+ "PLACEHOLDER": "Set time"
+ }
+ },
+ "USERS": {
+ "LABEL": "Assigned agents",
+ "DESCRIPTION": "Add agents for which this policy will be applicable.",
+ "ADD_BUTTON": "Add agent",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select agents to add",
+ "ADD_BUTTON": "Add"
+ },
+ "EMPTY_STATE": "No agents added",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Agent capacity policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete agent capacity policy"
+ }
+ },
+ "DELETE_POLICY": {
+ "TITLE": "Delete policy",
+ "DESCRIPTION": "Are you sure you want to delete this policy? This action cannot be undone.",
+ "CONFIRM_BUTTON_LABEL": "Delete",
+ "CANCEL_BUTTON_LABEL": "Cancel"
+ }
}
}
diff --git a/app/javascript/dashboard/i18n/locale/am/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/am/whatsappTemplates.json
index 5f53faaa8..cf28312dc 100644
--- a/app/javascript/dashboard/i18n/locale/am/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/am/whatsappTemplates.json
@@ -40,6 +40,7 @@
"BUTTON_LABEL": "Button {index}",
"COUPON_CODE": "Enter coupon code (max 15 chars)",
"MEDIA_URL_LABEL": "Enter {type} URL",
+ "DOCUMENT_NAME_PLACEHOLDER": "Enter document filename (e.g., Invoice_2025.pdf)",
"BUTTON_PARAMETER": "Enter button parameter"
}
}
diff --git a/app/javascript/dashboard/i18n/locale/ar/automation.json b/app/javascript/dashboard/i18n/locale/ar/automation.json
index 788652a04..abdbcbe32 100644
--- a/app/javascript/dashboard/i18n/locale/ar/automation.json
+++ b/app/javascript/dashboard/i18n/locale/ar/automation.json
@@ -177,7 +177,8 @@
"REFERER_LINK": "Referrer Link",
"ASSIGNEE_NAME": "المكلَّف",
"TEAM_NAME": "الفريق",
- "PRIORITY": "الأولوية"
+ "PRIORITY": "الأولوية",
+ "LABELS": "الوسوم"
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/ar/contact.json b/app/javascript/dashboard/i18n/locale/ar/contact.json
index 2fb5bccaa..d9588349d 100644
--- a/app/javascript/dashboard/i18n/locale/ar/contact.json
+++ b/app/javascript/dashboard/i18n/locale/ar/contact.json
@@ -610,6 +610,15 @@
"SEND_MESSAGE": "إرسال الرسالة"
}
},
+ "TWILIO_OPTIONS": {
+ "LABEL": "Select template",
+ "SEARCH_PLACEHOLDER": "Search templates",
+ "EMPTY_STATE": "No templates found",
+ "TEMPLATE_PARSER": {
+ "BACK": "العودة للخلف",
+ "SEND_MESSAGE": "إرسال الرسالة"
+ }
+ },
"ACTION_BUTTONS": {
"DISCARD": "Discard",
"SEND": "Send ({keyCode})"
diff --git a/app/javascript/dashboard/i18n/locale/ar/contactFilters.json b/app/javascript/dashboard/i18n/locale/ar/contactFilters.json
index b2a38da02..eb18cc456 100644
--- a/app/javascript/dashboard/i18n/locale/ar/contactFilters.json
+++ b/app/javascript/dashboard/i18n/locale/ar/contactFilters.json
@@ -48,7 +48,8 @@
"CREATED_AT": "تم إنشاؤها في",
"LAST_ACTIVITY": "آخر نشاط",
"REFERER_LINK": "رابط المرجع",
- "BLOCKED": "محظور"
+ "BLOCKED": "محظور",
+ "LABELS": "الوسوم"
},
"GROUPS": {
"STANDARD_FILTERS": "الفلاتر القياسية",
diff --git a/app/javascript/dashboard/i18n/locale/ar/contentTemplates.json b/app/javascript/dashboard/i18n/locale/ar/contentTemplates.json
new file mode 100644
index 000000000..cdb8b3968
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/ar/contentTemplates.json
@@ -0,0 +1,51 @@
+{
+ "CONTENT_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Twilio Templates",
+ "SUBTITLE": "Select the Twilio template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "نماذج البحث",
+ "NO_TEMPLATES_FOUND": "لم يتم العثور على قوالب",
+ "NO_CONTENT": "لا يوجد محتوى",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "الفئة",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No Twilio templates available. Click refresh to sync templates from Twilio.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "اللغة",
+ "TEMPLATE_BODY": "نص القالب",
+ "CATEGORY": "الفئة"
+ },
+ "TYPES": {
+ "MEDIA": "Media",
+ "QUICK_REPLY": "Quick Reply",
+ "TEXT": "النص"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "المتغيرات",
+ "LANGUAGE": "اللغة",
+ "CATEGORY": "الفئة",
+ "VARIABLE_PLACEHOLDER": "أدخل قيمة {variable}",
+ "GO_BACK_LABEL": "العودة للخلف",
+ "SEND_MESSAGE_LABEL": "إرسال الرسالة",
+ "FORM_ERROR_MESSAGE": "يرجى ملء جميع المتغيرات قبل الإرسال",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "MEDIA_URL_LABEL": "Enter full media URL",
+ "MEDIA_URL_PLACEHOLDER": "https://example.com/image.jpg"
+ },
+ "FORM": {
+ "BACK_BUTTON": "العودة",
+ "SEND_MESSAGE_BUTTON": "إرسال الرسالة"
+ }
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/ar/conversation.json b/app/javascript/dashboard/i18n/locale/ar/conversation.json
index 45a488aaf..a66530232 100644
--- a/app/javascript/dashboard/i18n/locale/ar/conversation.json
+++ b/app/javascript/dashboard/i18n/locale/ar/conversation.json
@@ -35,6 +35,11 @@
"API_HOURS_WINDOW": "You can only reply to this conversation within {hours} hours",
"NOT_ASSIGNED_TO_YOU": "لم يتم تعيين هذه المحادثة لك. هل ترغب في تعيين هذه المحادثة لنفسك؟",
"ASSIGN_TO_ME": "إسناد لي",
+ "BOT_HANDOFF_MESSAGE": "You are responding to a conversation which is currently handled by an assistant or a bot.",
+ "BOT_HANDOFF_ACTION": "Mark open and assign to you",
+ "BOT_HANDOFF_REOPEN_ACTION": "Mark conversation open",
+ "BOT_HANDOFF_SUCCESS": "Conversation has been handed over to you",
+ "BOT_HANDOFF_ERROR": "Failed to take over the conversation. Please try again.",
"TWILIO_WHATSAPP_CAN_REPLY": "يمكنك فقط الرد على هذه المحادثة باستخدام رسالة قالب بسبب",
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "قيد نافذة الـ 24 ساعة",
"OLD_INSTAGRAM_INBOX_REPLY_BANNER": "This Instagram account was migrated to the new Instagram channel inbox. All new messages will show up there. You won’t be able to send messages from this conversation anymore.",
@@ -66,6 +71,17 @@
"SHOW_LABELS": "إظهار السمات",
"HIDE_LABELS": "إخفاء السمات"
},
+ "VOICE_CALL": {
+ "INCOMING_CALL": "Incoming call",
+ "OUTGOING_CALL": "Outgoing call",
+ "CALL_IN_PROGRESS": "Call in progress",
+ "NO_ANSWER": "No answer",
+ "MISSED_CALL": "Missed call",
+ "CALL_ENDED": "Call ended",
+ "NOT_ANSWERED_YET": "Not answered yet",
+ "THEY_ANSWERED": "They answered",
+ "YOU_ANSWERED": "You answered"
+ },
"HEADER": {
"RESOLVE_ACTION": "حل المحادثة",
"REOPEN_ACTION": "إعادة فتح",
diff --git a/app/javascript/dashboard/i18n/locale/ar/helpCenter.json b/app/javascript/dashboard/i18n/locale/ar/helpCenter.json
index 8c1495aba..13265de6c 100644
--- a/app/javascript/dashboard/i18n/locale/ar/helpCenter.json
+++ b/app/javascript/dashboard/i18n/locale/ar/helpCenter.json
@@ -741,7 +741,8 @@
"LIVE_CHAT_WIDGET": {
"LABEL": "Live chat widget",
"PLACEHOLDER": "Select live chat widget",
- "HELP_TEXT": "Select a live chat widget that will appear on your help center"
+ "HELP_TEXT": "Select a live chat widget that will appear on your help center",
+ "NONE_OPTION": "No widget"
},
"BRAND_COLOR": {
"LABEL": "Brand color"
@@ -807,6 +808,58 @@
"ERROR_MESSAGE": "Unable to update portal"
}
}
+ },
+ "PDF_UPLOAD": {
+ "TITLE": "Upload PDF Document",
+ "DESCRIPTION": "Upload a PDF document to automatically generate FAQs using AI",
+ "DRAG_DROP_TEXT": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "ADDITIONAL_CONTEXT_LABEL": "Additional Context (Optional)",
+ "ADDITIONAL_CONTEXT_PLACEHOLDER": "Provide any additional context or instructions for FAQ generation...",
+ "UPLOADING": "جاري الرفع...",
+ "UPLOAD": "Upload & Process",
+ "CANCEL": "إلغاء",
+ "ERROR_INVALID_TYPE": "Please select a valid PDF file",
+ "ERROR_FILE_TOO_LARGE": "File size must be less than 512MB",
+ "ERROR_UPLOAD_FAILED": "Failed to upload PDF. Please try again."
+ },
+ "PDF_DOCUMENTS": {
+ "TITLE": "PDF Documents",
+ "DESCRIPTION": "Manage uploaded PDF documents and generate FAQs from them",
+ "UPLOAD_PDF": "Upload PDF",
+ "UPLOAD_FIRST_PDF": "Upload your first PDF",
+ "UPLOADED_BY": "Uploaded by",
+ "GENERATE_FAQS": "Generate FAQs",
+ "GENERATING": "Generating...",
+ "CONFIRM_DELETE": "Are you sure you want to delete {filename}?",
+ "EMPTY_STATE": {
+ "TITLE": "No PDF documents yet",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQs using AI"
+ },
+ "STATUS": {
+ "UPLOADED": "Ready",
+ "PROCESSING": "Processing",
+ "PROCESSED": "مكتمل",
+ "FAILED": "Failed"
+ }
+ },
+ "CONTENT_GENERATION": {
+ "TITLE": "Content Generation",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQ content using AI",
+ "UPLOAD_TITLE": "Upload PDF Document",
+ "DRAG_DROP": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "UPLOADING": "Processing document...",
+ "UPLOAD_SUCCESS": "Document processed successfully!",
+ "UPLOAD_ERROR": "Failed to upload document. Please try again.",
+ "INVALID_FILE_TYPE": "Please select a valid PDF file",
+ "FILE_TOO_LARGE": "File size must be less than 512MB",
+ "GENERATED_CONTENT": "Generated FAQ Content",
+ "PUBLISH_SELECTED": "Publish Selected",
+ "PUBLISHING": "Publishing...",
+ "FROM_DOCUMENT": "From document",
+ "NO_CONTENT": "No generated content available. Upload a PDF document to get started.",
+ "LOADING": "Loading generated content..."
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/ar/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/ar/inboxMgmt.json
index 77d953e7a..cdf63b707 100644
--- a/app/javascript/dashboard/i18n/locale/ar/inboxMgmt.json
+++ b/app/javascript/dashboard/i18n/locale/ar/inboxMgmt.json
@@ -272,8 +272,8 @@
},
"SUBMIT_BUTTON": "إنشاء قناة واتساب",
"EMBEDDED_SIGNUP": {
- "TITLE": "Quick Setup with Meta",
- "DESC": "You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
+ "TITLE": "Quick setup with Meta",
+ "DESC": "Use the WhatsApp Embedded Signup flow to quickly connect new numbers. You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
"BENEFITS": {
"TITLE": "Benefits of Embedded Signup:",
"EASY_SETUP": "No manual configuration required",
@@ -281,9 +281,8 @@
"AUTO_CONFIG": "Automatic webhook and phone number configuration"
},
"LEARN_MORE": {
- "TEXT": "To learn more about integrated signup, pricing, and limitations, visit",
- "LINK_TEXT": "this link.",
- "LINK_URL": "https://developers.facebook.com/docs/whatsapp/embedded-signup/custom-flows/onboarding-business-app-users#limitations"
+ "TEXT": "To learn more about integrated signup, pricing, and limitations, visit {link}.",
+ "LINK_TEXT": "this link"
},
"SUBMIT_BUTTON": "Connect with WhatsApp Business",
"AUTH_PROCESSING": "Authenticating with Meta",
@@ -296,7 +295,9 @@
"INVALID_BUSINESS_DATA": "Invalid business data received from Facebook. Please try again.",
"SIGNUP_ERROR": "Signup error occurred",
"AUTH_NOT_COMPLETED": "Authentication not completed. Please restart the process.",
- "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured"
+ "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured",
+ "MANUAL_FALLBACK": "If your number is already connected to the WhatsApp Business Platform (API), or if you’re a tech provider onboarding your own number, please use the {link} flow",
+ "MANUAL_LINK_TEXT": "manual setup flow"
},
"API": {
"ERROR_MESSAGE": "لم نتمكن من حفظ قناة واتساب"
@@ -424,7 +425,51 @@
},
"AUTH": {
"TITLE": "اختر قناة",
- "DESC": "يدعم أدوات الدردشة الحية، والميسنجر الفيسبوك، وملفات التويتر الشخصية، و WhatsApp، ورسائل البريد الإلكتروني، إلخ، كقنوات. إذا كنت ترغب في إنشاء قناة مخصصة، يمكنك إنشاءها باستخدام قناة API. للبدء، اختر إحدى القنوات أدناه."
+ "DESC": "يدعم أدوات الدردشة الحية، والميسنجر الفيسبوك، وملفات التويتر الشخصية، و WhatsApp، ورسائل البريد الإلكتروني، إلخ، كقنوات. إذا كنت ترغب في إنشاء قناة مخصصة، يمكنك إنشاءها باستخدام قناة API. للبدء، اختر إحدى القنوات أدناه.",
+ "TITLE_NEXT": "Complete the setup",
+ "TITLE_FINISH": "Voilà!",
+ "CHANNEL": {
+ "WEBSITE": {
+ "TITLE": "الموقع الإلكتروني",
+ "DESCRIPTION": "Create a live-chat widget"
+ },
+ "FACEBOOK": {
+ "TITLE": "فيسبوك",
+ "DESCRIPTION": "Connect your Facebook page"
+ },
+ "WHATSAPP": {
+ "TITLE": "واتساب",
+ "DESCRIPTION": "Support your customers on WhatsApp"
+ },
+ "EMAIL": {
+ "TITLE": "البريد الإلكتروني",
+ "DESCRIPTION": "Connect with Gmail, Outlook, or other providers"
+ },
+ "SMS": {
+ "TITLE": "SMS",
+ "DESCRIPTION": "Integrate SMS channel with Twilio or bandwidth"
+ },
+ "API": {
+ "TITLE": "API",
+ "DESCRIPTION": "Make a custom channel using our API"
+ },
+ "TELEGRAM": {
+ "TITLE": "تيليجرام",
+ "DESCRIPTION": "Configure Telegram channel using Bot token"
+ },
+ "LINE": {
+ "TITLE": "Line",
+ "DESCRIPTION": "Integrate your Line channel"
+ },
+ "INSTAGRAM": {
+ "TITLE": "Instagram",
+ "DESCRIPTION": "Connect your instagram account"
+ },
+ "VOICE": {
+ "TITLE": "Voice",
+ "DESCRIPTION": "Integrate with Twilio Voice"
+ }
+ }
},
"AGENTS": {
"TITLE": "وكيل الدعم",
@@ -478,7 +523,10 @@
"MESSAGE": "يمكنك الآن التواصل مع عملائك من خلال قناتك الجديدة",
"BUTTON_TEXT": "خذني إلى هناك",
"MORE_SETTINGS": "المزيد من الإعدادات",
- "WEBSITE_SUCCESS": "لقد انتهيت بنجاح من إنشاء قناة دردشة مباشرة لموقعك. انسخ الرمز الموضح أدناه وقم بإضافته إلى موقع الويب الخاص بك. في المرة القادمة التي يستخدم فيها العميل الدردشة المباشرة، ستظهر المحادثة تلقائياً على صندوق الوارد الخاص بك."
+ "WEBSITE_SUCCESS": "لقد انتهيت بنجاح من إنشاء قناة دردشة مباشرة لموقعك. انسخ الرمز الموضح أدناه وقم بإضافته إلى موقع الويب الخاص بك. في المرة القادمة التي يستخدم فيها العميل الدردشة المباشرة، ستظهر المحادثة تلقائياً على صندوق الوارد الخاص بك.",
+ "WHATSAPP_QR_INSTRUCTION": "Scan the QR code above to quickly test your WhatsApp inbox",
+ "MESSENGER_QR_INSTRUCTION": "Scan the QR code above to quickly test your Facebook Messenger inbox",
+ "TELEGRAM_QR_INSTRUCTION": "Scan the QR code above to quickly test your Telegram inbox"
},
"REAUTH": "إعادة التصريح",
"VIEW": "عرض",
@@ -868,9 +916,18 @@
"SCRIPT_SETTINGS": "\n window.chatwootSettings = {options};"
},
"EMAIL_PROVIDERS": {
- "MICROSOFT": "Microsoft",
- "GOOGLE": "Google",
- "OTHER_PROVIDERS": "Other Providers"
+ "MICROSOFT": {
+ "TITLE": "Microsoft",
+ "DESCRIPTION": "Connect with Microsoft"
+ },
+ "GOOGLE": {
+ "TITLE": "Google",
+ "DESCRIPTION": "Connect with Google"
+ },
+ "OTHER_PROVIDERS": {
+ "TITLE": "Other Providers",
+ "DESCRIPTION": "Connect with Other Providers"
+ }
},
"CHANNELS": {
"MESSENGER": "Messenger",
diff --git a/app/javascript/dashboard/i18n/locale/ar/integrations.json b/app/javascript/dashboard/i18n/locale/ar/integrations.json
index bc17130c1..ca7e7c553 100644
--- a/app/javascript/dashboard/i18n/locale/ar/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/ar/integrations.json
@@ -704,11 +704,28 @@
"ERROR_MESSAGE": "There was an error creating the document, please try again."
},
"FORM": {
+ "TYPE": {
+ "LABEL": "Document Type",
+ "URL": "الرابط",
+ "PDF": "PDF File"
+ },
"URL": {
"LABEL": "الرابط",
"PLACEHOLDER": "Enter the URL of the document",
"ERROR": "Please provide a valid URL for the document"
},
+ "PDF_FILE": {
+ "LABEL": "PDF File",
+ "CHOOSE_FILE": "Choose PDF file",
+ "ERROR": "Please select a PDF file",
+ "HELP_TEXT": "Maximum file size: 10MB",
+ "INVALID_TYPE": "Please select a valid PDF file",
+ "TOO_LARGE": "File size exceeds 10MB limit"
+ },
+ "NAME": {
+ "LABEL": "Document Name (Optional)",
+ "PLACEHOLDER": "Enter a name for the document"
+ },
"ASSISTANT": {
"LABEL": "Assistant",
"PLACEHOLDER": "Select the assistant",
@@ -744,6 +761,7 @@
"SELECTED": "{count} selected",
"SELECT_ALL": "Select all ({count})",
"UNSELECT_ALL": "Unselect all ({count})",
+ "SEARCH_PLACEHOLDER": "Search FAQs...",
"BULK_APPROVE_BUTTON": "Approve",
"BULK_DELETE_BUTTON": "حذف",
"BULK_APPROVE": {
diff --git a/app/javascript/dashboard/i18n/locale/ar/mfa.json b/app/javascript/dashboard/i18n/locale/ar/mfa.json
new file mode 100644
index 000000000..bffa8cefe
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/ar/mfa.json
@@ -0,0 +1,106 @@
+{
+ "MFA_SETTINGS": {
+ "TITLE": "Two-Factor Authentication",
+ "SUBTITLE": "Secure your account with TOTP-based authentication",
+ "DESCRIPTION": "Add an extra layer of security to your account using a time-based one-time password (TOTP)",
+ "STATUS_TITLE": "Authentication Status",
+ "STATUS_DESCRIPTION": "Manage your two-factor authentication settings and backup recovery codes",
+ "ENABLED": "مفعل",
+ "DISABLED": "معطّل",
+ "STATUS_ENABLED": "Two-factor authentication is active",
+ "STATUS_ENABLED_DESC": "Your account is protected with an additional layer of security",
+ "ENABLE_BUTTON": "Enable Two-Factor Authentication",
+ "ENHANCE_SECURITY": "Enhance Your Account Security",
+ "ENHANCE_SECURITY_DESC": "Two-factor authentication adds an extra layer of security by requiring a verification code from your authenticator app in addition to your password.",
+ "SETUP": {
+ "STEP_NUMBER_1": "1",
+ "STEP_NUMBER_2": "2",
+ "STEP1_TITLE": "Scan QR Code with Your Authenticator App",
+ "STEP1_DESCRIPTION": "Use Google Authenticator, Authy, or any TOTP-compatible app",
+ "LOADING_QR": "جار التحميل...",
+ "MANUAL_ENTRY": "Can't scan? Enter code manually",
+ "SECRET_KEY": "Secret Key",
+ "COPY": "نسخ",
+ "ENTER_CODE": "Enter the 6-digit code from your authenticator app",
+ "ENTER_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify & Continue",
+ "CANCEL": "إلغاء",
+ "ERROR_STARTING": "MFA not enabled. Please contact administrator.",
+ "INVALID_CODE": "Invalid verification code",
+ "SECRET_COPIED": "Secret key copied to clipboard",
+ "SUCCESS": "Two-factor authentication has been enabled successfully"
+ },
+ "BACKUP": {
+ "TITLE": "Save Your Backup Codes",
+ "DESCRIPTION": "Keep these codes safe. Each can be used once if you lose access to your authenticator",
+ "IMPORTANT": "Important:",
+ "IMPORTANT_NOTE": " Save these codes in a secure location. You won't be able to see them again.",
+ "DOWNLOAD": "تحميل",
+ "COPY_ALL": "Copy All",
+ "CONFIRM": "I have saved my backup codes in a secure location and understand that I won't be able to see them again",
+ "COMPLETE_SETUP": "Complete Setup",
+ "CODES_COPIED": "Backup codes copied to clipboard"
+ },
+ "MANAGEMENT": {
+ "BACKUP_CODES": "Backup Codes",
+ "BACKUP_CODES_DESC": "Generate new codes if you've lost or used your existing ones",
+ "REGENERATE": "Regenerate Backup Codes",
+ "DISABLE_MFA": "Disable 2FA",
+ "DISABLE_MFA_DESC": "Remove two-factor authentication from your account",
+ "DISABLE_BUTTON": "Disable Two-Factor Authentication"
+ },
+ "DISABLE": {
+ "TITLE": "Disable Two-Factor Authentication",
+ "DESCRIPTION": "You'll need to enter your password and a verification code to disable two-factor authentication.",
+ "PASSWORD": "كلمة المرور",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Disable 2FA",
+ "CANCEL": "إلغاء",
+ "SUCCESS": "Two-factor authentication has been disabled",
+ "ERROR": "Failed to disable MFA. Please check your credentials."
+ },
+ "REGENERATE": {
+ "TITLE": "Regenerate Backup Codes",
+ "DESCRIPTION": "This will invalidate your existing backup codes and generate new ones. Enter your verification code to continue.",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Generate New Codes",
+ "CANCEL": "إلغاء",
+ "NEW_CODES_TITLE": "New Backup Codes Generated",
+ "NEW_CODES_DESC": "Your old backup codes have been invalidated. Save these new codes in a secure location.",
+ "CODES_IMPORTANT": "Important:",
+ "CODES_IMPORTANT_NOTE": " Each code can only be used once. Save them before closing this window.",
+ "DOWNLOAD_CODES": "Download Codes",
+ "COPY_ALL_CODES": "Copy All Codes",
+ "CODES_SAVED": "I've Saved My Codes",
+ "SUCCESS": "New backup codes have been generated",
+ "ERROR": "Failed to regenerate backup codes"
+ }
+ },
+ "MFA_VERIFICATION": {
+ "TITLE": "Two-Factor Authentication",
+ "DESCRIPTION": "Enter your verification code to continue",
+ "AUTHENTICATOR_APP": "Authenticator App",
+ "BACKUP_CODE": "Backup Code",
+ "ENTER_OTP_CODE": "Enter 6-digit code from your authenticator app",
+ "ENTER_BACKUP_CODE": "Enter one of your backup codes",
+ "BACKUP_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify",
+ "TRY_ANOTHER_METHOD": "Try another verification method",
+ "CANCEL_LOGIN": "Cancel and return to login",
+ "HELP_TEXT": "Having trouble signing in?",
+ "LEARN_MORE": "Learn more about 2FA",
+ "HELP_MODAL": {
+ "TITLE": "Two-Factor Authentication Help",
+ "AUTHENTICATOR_TITLE": "Using an Authenticator App",
+ "AUTHENTICATOR_DESC": "Open your authenticator app (Google Authenticator, Authy, etc.) and enter the 6-digit code shown for your account.",
+ "BACKUP_TITLE": "Using a Backup Code",
+ "BACKUP_DESC": "If you don't have access to your authenticator app, you can use one of the backup codes you saved when setting up 2FA. Each code can only be used once.",
+ "CONTACT_TITLE": "Need More Help?",
+ "CONTACT_DESC_CLOUD": "If you've lost access to both your authenticator app and backup codes, please reach out to Chatwoot support for assistance.",
+ "CONTACT_DESC_SELF_HOSTED": "If you've lost access to both your authenticator app and backup codes, please contact your administrator for assistance."
+ },
+ "VERIFICATION_FAILED": "Verification failed. Please try again."
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/ar/settings.json b/app/javascript/dashboard/i18n/locale/ar/settings.json
index f0990ffe6..1a24cf67f 100644
--- a/app/javascript/dashboard/i18n/locale/ar/settings.json
+++ b/app/javascript/dashboard/i18n/locale/ar/settings.json
@@ -51,6 +51,13 @@
"LARGER": "Larger",
"EXTRA_LARGE": "Extra Large"
}
+ },
+ "LANGUAGE": {
+ "TITLE": "Preferred Language",
+ "NOTE": "Choose the language you want to use.",
+ "UPDATE_SUCCESS": "Your Language settings have been updated successfully",
+ "UPDATE_ERROR": "There is an error while updating the language settings, please try again",
+ "USE_ACCOUNT_DEFAULT": "Use account default"
}
},
"MESSAGE_SIGNATURE_SECTION": {
@@ -73,6 +80,11 @@
"NOTE": "تعديل كلمة المرور الخاصة بك سيعيد ضبط تسجيلات الدخول الخاصة بك في الأجهزة الأخرى.",
"BTN_TEXT": "تغيير كلمة المرور"
},
+ "SECURITY_SECTION": {
+ "TITLE": "Security",
+ "NOTE": "Manage additional security features for your account.",
+ "MFA_BUTTON": "Manage Two-Factor Authentication"
+ },
"ACCESS_TOKEN": {
"TITLE": "رمز المصادقة",
"NOTE": "يمكن استخدام هذا رمز المصادقة إذا كنت تبني تطبيقات API للتكامل مع Chatwoot",
@@ -330,6 +342,7 @@
"REPORTS_LABEL": "الوسوم",
"REPORTS_INBOX": "صندوق الوارد",
"REPORTS_TEAM": "الفريق",
+ "AGENT_ASSIGNMENT": "Agent Assignment",
"SET_AVAILABILITY_TITLE": "تعيين نفسك كـ",
"SET_YOUR_AVAILABILITY": "قم بتعيين توافرك",
"SLA": "SLA",
@@ -350,7 +363,8 @@
"INFO_TEXT": "السماح للنظام بوضع علامة غير متصل تلقائياً عند عدم استخدام التطبيق أو لوحة التحكم.",
"INFO_SHORT": "Automatically mark offline when you aren't using the app."
},
- "DOCS": "قراءة المستندات"
+ "DOCS": "قراءة المستندات",
+ "SECURITY": "Security"
},
"BILLING_SETTINGS": {
"TITLE": "الفواتير",
@@ -382,6 +396,77 @@
},
"NO_BILLING_USER": "حساب الفوترة الخاص بك قيد الإعداد. الرجاء تحديث الصفحة وحاول مرة أخرى."
},
+ "SECURITY_SETTINGS": {
+ "TITLE": "Security",
+ "DESCRIPTION": "Manage your account security settings.",
+ "LINK_TEXT": "Learn more about SAML SSO",
+ "SAML": {
+ "TITLE": "SAML SSO",
+ "NOTE": "Configure SAML single sign-on for your account. Users will authenticate through your identity provider instead of using email/password.",
+ "ACS_URL": {
+ "LABEL": "ACS URL",
+ "TOOLTIP": "Assertion Consumer Service URL - Configure this URL in your IdP as the destination for SAML responses"
+ },
+ "SSO_URL": {
+ "LABEL": "SSO URL",
+ "HELP": "The URL where SAML authentication requests will be sent",
+ "PLACEHOLDER": "https://your-idp.com/saml/sso"
+ },
+ "CERTIFICATE": {
+ "LABEL": "Signing certificate in PEM format",
+ "HELP": "The public certificate from your identity provider used to verify SAML responses",
+ "PLACEHOLDER": "-----BEGIN CERTIFICATE-----\nMIIC..."
+ },
+ "FINGERPRINT": {
+ "LABEL": "Fingerprint",
+ "TOOLTIP": "SHA-1 fingerprint of the certificate - Use this to verify the certificate in your IdP configuration"
+ },
+ "COPY_SUCCESS": "تم نسخ الكود إلى الحافظة بنجاح",
+ "SP_ENTITY_ID": {
+ "LABEL": "SP Entity ID",
+ "HELP": "Unique identifier for this application as a service provider (auto-generated).",
+ "TOOLTIP": "Unique identifier for Chatwoot as the Service Provider - Configure this in your IdP settings"
+ },
+ "IDP_ENTITY_ID": {
+ "LABEL": "Identity Provider Entity ID",
+ "HELP": "Unique identifier for your identity provider (usually found in IdP configuration)",
+ "PLACEHOLDER": "https://your-idp.com/saml"
+ },
+ "UPDATE_BUTTON": "Update SAML Settings",
+ "API": {
+ "SUCCESS": "SAML settings updated successfully",
+ "ERROR": "Failed to update SAML settings",
+ "ERROR_LOADING": "Failed to load SAML settings",
+ "DISABLED": "SAML settings disabled successfully"
+ },
+ "VALIDATION": {
+ "REQUIRED_FIELDS": "SSO URL, Identity Provider Entity ID, and Certificate are required fields",
+ "SSO_URL_ERROR": "Please enter a valid SSO URL",
+ "CERTIFICATE_ERROR": "Certificate is required",
+ "IDP_ENTITY_ID_ERROR": "Identity Provider Entity ID is required"
+ },
+ "ENTERPRISE_PAYWALL": {
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade to an Enterprise plan to access SAML single sign-on and other advanced security features.",
+ "ASK_ADMIN": "Please reach out to your administrator for the upgrade."
+ },
+ "PAYWALL": {
+ "TITLE": "Upgrade to enable SAML SSO",
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade your plan to get access to SAML single sign-on and other advanced features.",
+ "UPGRADE_NOW": "Upgrade now",
+ "CANCEL_ANYTIME": "You can change or cancel your plan anytime"
+ },
+ "ATTRIBUTE_MAPPING": {
+ "TITLE": "SAML Attribute Setup",
+ "DESCRIPTION": "The following attribute mappings must be configured in your identity provider"
+ },
+ "INFO_SECTION": {
+ "TITLE": "Service Provider Information",
+ "TOOLTIP": "Copy these values and configure them in your Identity Provider to establish the SAML connection"
+ }
+ }
+ },
"CREATE_ACCOUNT": {
"NO_ACCOUNT_WARNING": "أوه! لم نتمكن من العثور على الحساب. الرجاء إنشاء حساب جديد للمتابعة.",
"NEW_ACCOUNT": "حساب جديد",
@@ -418,5 +503,255 @@
"SWITCH_TO_REPLY": "التبديل إلى الرد",
"TOGGLE_SNOOZE_DROPDOWN": "تبديل القائمة المنسدلة"
}
+ },
+ "ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent assignment",
+ "DESCRIPTION": "Define policies to effectively manage workload and route conversations based on the needs of inboxes and agents. Learn more here"
+ },
+ "ASSIGNMENT_POLICY": {
+ "TITLE": "Assignment policy",
+ "DESCRIPTION": "Manage how conversations get assigned in inboxes.",
+ "FEATURES": [
+ "Assign by conversations evenly or by available capacity",
+ "Add fair distribution rules to avoid overloading any agent",
+ "Add inboxes to a policy - one policy per inbox"
+ ]
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "TITLE": "Agent capacity policy",
+ "DESCRIPTION": "Manage workload for agents.",
+ "FEATURES": [
+ "Define maximum conversations per inbox",
+ "Create exceptions based on labels and time",
+ "Add agents to a policy - one policy per agent"
+ ]
+ }
+ },
+ "AGENT_ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Assignment policy",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "ORDER": "Order",
+ "PRIORITY": "الأولوية",
+ "ACTIVE": "مفعل",
+ "INACTIVE": "Inactive",
+ "POPOVER": "Added inboxes",
+ "EDIT": "تعديل"
+ },
+ "NO_RECORDS_FOUND": "No assignment policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create assignment policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy created successfully",
+ "ERROR_MESSAGE": "Failed to create assignment policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit assignment policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_INBOX_DIALOG": {
+ "TITLE": "Add inbox",
+ "DESCRIPTION": "{inboxName} inbox is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "إلغاء"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update assignment policy"
+ },
+ "INBOX_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Inbox added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Inbox removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove inbox from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "الوصف:",
+ "PLACEHOLDER": "أدخل الوصف"
+ },
+ "STATUS": {
+ "LABEL": "الحالة:",
+ "PLACEHOLDER": "اختر الحالة",
+ "ACTIVE": "Policy is active",
+ "INACTIVE": "Policy is inactive"
+ },
+ "ASSIGNMENT_ORDER": {
+ "LABEL": "Assignment order",
+ "ROUND_ROBIN": {
+ "LABEL": "Round robin",
+ "DESCRIPTION": "Assign conversations evenly among agents."
+ },
+ "BALANCED": {
+ "LABEL": "Balanced",
+ "DESCRIPTION": "Assign conversations based on available capacity."
+ }
+ },
+ "ASSIGNMENT_PRIORITY": {
+ "LABEL": "Assignment priority",
+ "EARLIEST_CREATED": {
+ "LABEL": "Earliest created",
+ "DESCRIPTION": "The conversation that was created first gets assigned first."
+ },
+ "LONGEST_WAITING": {
+ "LABEL": "Longest waiting",
+ "DESCRIPTION": "The conversation waiting the longest gets assigned first."
+ }
+ },
+ "FAIR_DISTRIBUTION": {
+ "LABEL": "Fair distribution policy",
+ "DESCRIPTION": "Set the maximum number of conversations that can be assigned per agent within a time window to avoid overloading any one agent. This required field defaults to 100 conversations per hour.",
+ "INPUT_MAX": "Assign max",
+ "DURATION": "Conversations per agent in every"
+ },
+ "INBOXES": {
+ "LABEL": "Added inboxes",
+ "DESCRIPTION": "Add inboxes for which this policy will be applicable.",
+ "ADD_BUTTON": "Add inbox",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select inboxes to add",
+ "ADD_BUTTON": "إضافة"
+ },
+ "EMPTY_STATE": "No inboxes added to this policy, add an inbox to get started",
+ "API": {
+ "SUCCESS_MESSAGE": "Inbox successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Assignment policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete assignment policy"
+ }
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent capacity",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "POPOVER": "Added agents",
+ "EDIT": "تعديل"
+ },
+ "NO_RECORDS_FOUND": "No agent capacity policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create agent capacity policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy created successfully",
+ "ERROR_MESSAGE": "Failed to create agent capacity policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit agent capacity policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_AGENT_DIALOG": {
+ "TITLE": "Add agent",
+ "DESCRIPTION": "{agentName} is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "إلغاء"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update agent capacity policy"
+ },
+ "AGENT_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Agent added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Agent removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove agent from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "الوصف:",
+ "PLACEHOLDER": "أدخل الوصف"
+ },
+ "INBOX_CAPACITY_LIMIT": {
+ "LABEL": "Inbox capacity limits",
+ "ADD_BUTTON": "Add inbox",
+ "FIELD": {
+ "SELECT_INBOX": "Select inbox",
+ "MAX_CONVERSATIONS": "Max conversations",
+ "SET_LIMIT": "Set limit"
+ },
+ "EMPTY_STATE": "No inbox limit set"
+ },
+ "EXCLUSION_RULES": {
+ "LABEL": "Exclusion rules",
+ "DESCRIPTION": "Conversations that satisfy the following conditions would not count towards agent capacity",
+ "TAGS": {
+ "LABEL": "Exclude conversations tagged with specific labels",
+ "ADD_TAG": "add tag",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select tags to add"
+ },
+ "EMPTY_STATE": "No tags added to this policy."
+ },
+ "DURATION": {
+ "LABEL": "Exclude conversations older than a specified duration",
+ "PLACEHOLDER": "Set time"
+ }
+ },
+ "USERS": {
+ "LABEL": "Assigned agents",
+ "DESCRIPTION": "Add agents for which this policy will be applicable.",
+ "ADD_BUTTON": "Add agent",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select agents to add",
+ "ADD_BUTTON": "إضافة"
+ },
+ "EMPTY_STATE": "No agents added",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Agent capacity policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete agent capacity policy"
+ }
+ },
+ "DELETE_POLICY": {
+ "TITLE": "Delete policy",
+ "DESCRIPTION": "Are you sure you want to delete this policy? This action cannot be undone.",
+ "CONFIRM_BUTTON_LABEL": "حذف",
+ "CANCEL_BUTTON_LABEL": "إلغاء"
+ }
}
}
diff --git a/app/javascript/dashboard/i18n/locale/ar/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/ar/whatsappTemplates.json
index 247af32c4..d0f4c1be4 100644
--- a/app/javascript/dashboard/i18n/locale/ar/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/ar/whatsappTemplates.json
@@ -40,6 +40,7 @@
"BUTTON_LABEL": "Button {index}",
"COUPON_CODE": "Enter coupon code (max 15 chars)",
"MEDIA_URL_LABEL": "Enter {type} URL",
+ "DOCUMENT_NAME_PLACEHOLDER": "Enter document filename (e.g., Invoice_2025.pdf)",
"BUTTON_PARAMETER": "Enter button parameter"
}
}
diff --git a/app/javascript/dashboard/i18n/locale/az/automation.json b/app/javascript/dashboard/i18n/locale/az/automation.json
index 80274f488..43245a1d5 100644
--- a/app/javascript/dashboard/i18n/locale/az/automation.json
+++ b/app/javascript/dashboard/i18n/locale/az/automation.json
@@ -177,7 +177,8 @@
"REFERER_LINK": "Referrer Link",
"ASSIGNEE_NAME": "Assignee",
"TEAM_NAME": "Team",
- "PRIORITY": "Priority"
+ "PRIORITY": "Priority",
+ "LABELS": "Labels"
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/az/contact.json b/app/javascript/dashboard/i18n/locale/az/contact.json
index 4dd081bd4..12b2d097e 100644
--- a/app/javascript/dashboard/i18n/locale/az/contact.json
+++ b/app/javascript/dashboard/i18n/locale/az/contact.json
@@ -610,6 +610,15 @@
"SEND_MESSAGE": "Send message"
}
},
+ "TWILIO_OPTIONS": {
+ "LABEL": "Select template",
+ "SEARCH_PLACEHOLDER": "Search templates",
+ "EMPTY_STATE": "No templates found",
+ "TEMPLATE_PARSER": {
+ "BACK": "Go back",
+ "SEND_MESSAGE": "Send message"
+ }
+ },
"ACTION_BUTTONS": {
"DISCARD": "Discard",
"SEND": "Send ({keyCode})"
diff --git a/app/javascript/dashboard/i18n/locale/az/contactFilters.json b/app/javascript/dashboard/i18n/locale/az/contactFilters.json
index bb3221c6e..4c62f0789 100644
--- a/app/javascript/dashboard/i18n/locale/az/contactFilters.json
+++ b/app/javascript/dashboard/i18n/locale/az/contactFilters.json
@@ -48,7 +48,8 @@
"CREATED_AT": "Created At",
"LAST_ACTIVITY": "Last Activity",
"REFERER_LINK": "Referrer link",
- "BLOCKED": "Blocked"
+ "BLOCKED": "Blocked",
+ "LABELS": "Labels"
},
"GROUPS": {
"STANDARD_FILTERS": "Standard Filters",
diff --git a/app/javascript/dashboard/i18n/locale/az/contentTemplates.json b/app/javascript/dashboard/i18n/locale/az/contentTemplates.json
new file mode 100644
index 000000000..a9b1d54c4
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/az/contentTemplates.json
@@ -0,0 +1,51 @@
+{
+ "CONTENT_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Twilio Templates",
+ "SUBTITLE": "Select the Twilio template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Search Templates",
+ "NO_TEMPLATES_FOUND": "No templates found for",
+ "NO_CONTENT": "No content",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Category",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No Twilio templates available. Click refresh to sync templates from Twilio.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Language",
+ "TEMPLATE_BODY": "Template Body",
+ "CATEGORY": "Category"
+ },
+ "TYPES": {
+ "MEDIA": "Media",
+ "QUICK_REPLY": "Quick Reply",
+ "TEXT": "Text"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variables",
+ "LANGUAGE": "Language",
+ "CATEGORY": "Category",
+ "VARIABLE_PLACEHOLDER": "Enter {variable} value",
+ "GO_BACK_LABEL": "Go Back",
+ "SEND_MESSAGE_LABEL": "Send Message",
+ "FORM_ERROR_MESSAGE": "Please fill all variables before sending",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "MEDIA_URL_LABEL": "Enter full media URL",
+ "MEDIA_URL_PLACEHOLDER": "https://example.com/image.jpg"
+ },
+ "FORM": {
+ "BACK_BUTTON": "Back",
+ "SEND_MESSAGE_BUTTON": "Send Message"
+ }
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/az/conversation.json b/app/javascript/dashboard/i18n/locale/az/conversation.json
index 308f24f51..9fd39b70f 100644
--- a/app/javascript/dashboard/i18n/locale/az/conversation.json
+++ b/app/javascript/dashboard/i18n/locale/az/conversation.json
@@ -35,6 +35,11 @@
"API_HOURS_WINDOW": "You can only reply to this conversation within {hours} hours",
"NOT_ASSIGNED_TO_YOU": "This conversation is not assigned to you. Would you like to assign this conversation to yourself?",
"ASSIGN_TO_ME": "Assign to me",
+ "BOT_HANDOFF_MESSAGE": "You are responding to a conversation which is currently handled by an assistant or a bot.",
+ "BOT_HANDOFF_ACTION": "Mark open and assign to you",
+ "BOT_HANDOFF_REOPEN_ACTION": "Mark conversation open",
+ "BOT_HANDOFF_SUCCESS": "Conversation has been handed over to you",
+ "BOT_HANDOFF_ERROR": "Failed to take over the conversation. Please try again.",
"TWILIO_WHATSAPP_CAN_REPLY": "You can only reply to this conversation using a template message due to",
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "24 hour message window restriction",
"OLD_INSTAGRAM_INBOX_REPLY_BANNER": "This Instagram account was migrated to the new Instagram channel inbox. All new messages will show up there. You won’t be able to send messages from this conversation anymore.",
@@ -66,6 +71,17 @@
"SHOW_LABELS": "Show labels",
"HIDE_LABELS": "Hide labels"
},
+ "VOICE_CALL": {
+ "INCOMING_CALL": "Incoming call",
+ "OUTGOING_CALL": "Outgoing call",
+ "CALL_IN_PROGRESS": "Call in progress",
+ "NO_ANSWER": "No answer",
+ "MISSED_CALL": "Missed call",
+ "CALL_ENDED": "Call ended",
+ "NOT_ANSWERED_YET": "Not answered yet",
+ "THEY_ANSWERED": "They answered",
+ "YOU_ANSWERED": "You answered"
+ },
"HEADER": {
"RESOLVE_ACTION": "Resolve",
"REOPEN_ACTION": "Reopen",
diff --git a/app/javascript/dashboard/i18n/locale/az/helpCenter.json b/app/javascript/dashboard/i18n/locale/az/helpCenter.json
index fd2b1a788..0ab8d62ff 100644
--- a/app/javascript/dashboard/i18n/locale/az/helpCenter.json
+++ b/app/javascript/dashboard/i18n/locale/az/helpCenter.json
@@ -741,7 +741,8 @@
"LIVE_CHAT_WIDGET": {
"LABEL": "Live chat widget",
"PLACEHOLDER": "Select live chat widget",
- "HELP_TEXT": "Select a live chat widget that will appear on your help center"
+ "HELP_TEXT": "Select a live chat widget that will appear on your help center",
+ "NONE_OPTION": "No widget"
},
"BRAND_COLOR": {
"LABEL": "Brand color"
@@ -807,6 +808,58 @@
"ERROR_MESSAGE": "Unable to update portal"
}
}
+ },
+ "PDF_UPLOAD": {
+ "TITLE": "Upload PDF Document",
+ "DESCRIPTION": "Upload a PDF document to automatically generate FAQs using AI",
+ "DRAG_DROP_TEXT": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "ADDITIONAL_CONTEXT_LABEL": "Additional Context (Optional)",
+ "ADDITIONAL_CONTEXT_PLACEHOLDER": "Provide any additional context or instructions for FAQ generation...",
+ "UPLOADING": "Uploading...",
+ "UPLOAD": "Upload & Process",
+ "CANCEL": "Cancel",
+ "ERROR_INVALID_TYPE": "Please select a valid PDF file",
+ "ERROR_FILE_TOO_LARGE": "File size must be less than 512MB",
+ "ERROR_UPLOAD_FAILED": "Failed to upload PDF. Please try again."
+ },
+ "PDF_DOCUMENTS": {
+ "TITLE": "PDF Documents",
+ "DESCRIPTION": "Manage uploaded PDF documents and generate FAQs from them",
+ "UPLOAD_PDF": "Upload PDF",
+ "UPLOAD_FIRST_PDF": "Upload your first PDF",
+ "UPLOADED_BY": "Uploaded by",
+ "GENERATE_FAQS": "Generate FAQs",
+ "GENERATING": "Generating...",
+ "CONFIRM_DELETE": "Are you sure you want to delete {filename}?",
+ "EMPTY_STATE": {
+ "TITLE": "No PDF documents yet",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQs using AI"
+ },
+ "STATUS": {
+ "UPLOADED": "Ready",
+ "PROCESSING": "Processing",
+ "PROCESSED": "Completed",
+ "FAILED": "Failed"
+ }
+ },
+ "CONTENT_GENERATION": {
+ "TITLE": "Content Generation",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQ content using AI",
+ "UPLOAD_TITLE": "Upload PDF Document",
+ "DRAG_DROP": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "UPLOADING": "Processing document...",
+ "UPLOAD_SUCCESS": "Document processed successfully!",
+ "UPLOAD_ERROR": "Failed to upload document. Please try again.",
+ "INVALID_FILE_TYPE": "Please select a valid PDF file",
+ "FILE_TOO_LARGE": "File size must be less than 512MB",
+ "GENERATED_CONTENT": "Generated FAQ Content",
+ "PUBLISH_SELECTED": "Publish Selected",
+ "PUBLISHING": "Publishing...",
+ "FROM_DOCUMENT": "From document",
+ "NO_CONTENT": "No generated content available. Upload a PDF document to get started.",
+ "LOADING": "Loading generated content..."
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/az/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/az/inboxMgmt.json
index 6f44ec046..a525921db 100644
--- a/app/javascript/dashboard/i18n/locale/az/inboxMgmt.json
+++ b/app/javascript/dashboard/i18n/locale/az/inboxMgmt.json
@@ -272,8 +272,8 @@
},
"SUBMIT_BUTTON": "Create WhatsApp Channel",
"EMBEDDED_SIGNUP": {
- "TITLE": "Quick Setup with Meta",
- "DESC": "You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
+ "TITLE": "Quick setup with Meta",
+ "DESC": "Use the WhatsApp Embedded Signup flow to quickly connect new numbers. You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
"BENEFITS": {
"TITLE": "Benefits of Embedded Signup:",
"EASY_SETUP": "No manual configuration required",
@@ -281,9 +281,8 @@
"AUTO_CONFIG": "Automatic webhook and phone number configuration"
},
"LEARN_MORE": {
- "TEXT": "To learn more about integrated signup, pricing, and limitations, visit",
- "LINK_TEXT": "this link.",
- "LINK_URL": "https://developers.facebook.com/docs/whatsapp/embedded-signup/custom-flows/onboarding-business-app-users#limitations"
+ "TEXT": "To learn more about integrated signup, pricing, and limitations, visit {link}.",
+ "LINK_TEXT": "this link"
},
"SUBMIT_BUTTON": "Connect with WhatsApp Business",
"AUTH_PROCESSING": "Authenticating with Meta",
@@ -296,7 +295,9 @@
"INVALID_BUSINESS_DATA": "Invalid business data received from Facebook. Please try again.",
"SIGNUP_ERROR": "Signup error occurred",
"AUTH_NOT_COMPLETED": "Authentication not completed. Please restart the process.",
- "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured"
+ "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured",
+ "MANUAL_FALLBACK": "If your number is already connected to the WhatsApp Business Platform (API), or if you’re a tech provider onboarding your own number, please use the {link} flow",
+ "MANUAL_LINK_TEXT": "manual setup flow"
},
"API": {
"ERROR_MESSAGE": "We were not able to save the WhatsApp channel"
@@ -424,7 +425,51 @@
},
"AUTH": {
"TITLE": "Choose a channel",
- "DESC": "Chatwoot supports live-chat widgets, Facebook Messenger, WhatsApp, Emails, etc., as channels. If you want to build a custom channel, you can create it using the API channel. To get started, choose one of the channels below."
+ "DESC": "Chatwoot supports live-chat widgets, Facebook Messenger, WhatsApp, Emails, etc., as channels. If you want to build a custom channel, you can create it using the API channel. To get started, choose one of the channels below.",
+ "TITLE_NEXT": "Complete the setup",
+ "TITLE_FINISH": "Voilà!",
+ "CHANNEL": {
+ "WEBSITE": {
+ "TITLE": "Website",
+ "DESCRIPTION": "Create a live-chat widget"
+ },
+ "FACEBOOK": {
+ "TITLE": "Facebook",
+ "DESCRIPTION": "Connect your Facebook page"
+ },
+ "WHATSAPP": {
+ "TITLE": "WhatsApp",
+ "DESCRIPTION": "Support your customers on WhatsApp"
+ },
+ "EMAIL": {
+ "TITLE": "Email",
+ "DESCRIPTION": "Connect with Gmail, Outlook, or other providers"
+ },
+ "SMS": {
+ "TITLE": "SMS",
+ "DESCRIPTION": "Integrate SMS channel with Twilio or bandwidth"
+ },
+ "API": {
+ "TITLE": "API",
+ "DESCRIPTION": "Make a custom channel using our API"
+ },
+ "TELEGRAM": {
+ "TITLE": "Telegram",
+ "DESCRIPTION": "Configure Telegram channel using Bot token"
+ },
+ "LINE": {
+ "TITLE": "Line",
+ "DESCRIPTION": "Integrate your Line channel"
+ },
+ "INSTAGRAM": {
+ "TITLE": "Instagram",
+ "DESCRIPTION": "Connect your instagram account"
+ },
+ "VOICE": {
+ "TITLE": "Voice",
+ "DESCRIPTION": "Integrate with Twilio Voice"
+ }
+ }
},
"AGENTS": {
"TITLE": "Agents",
@@ -478,7 +523,10 @@
"MESSAGE": "You can now engage with your customers through your new Channel. Happy supporting",
"BUTTON_TEXT": "Take me there",
"MORE_SETTINGS": "More settings",
- "WEBSITE_SUCCESS": "You have successfully finished creating a website channel. Copy the code shown below and paste it on your website. Next time a customer use the live chat, the conversation will automatically appear on your inbox."
+ "WEBSITE_SUCCESS": "You have successfully finished creating a website channel. Copy the code shown below and paste it on your website. Next time a customer use the live chat, the conversation will automatically appear on your inbox.",
+ "WHATSAPP_QR_INSTRUCTION": "Scan the QR code above to quickly test your WhatsApp inbox",
+ "MESSENGER_QR_INSTRUCTION": "Scan the QR code above to quickly test your Facebook Messenger inbox",
+ "TELEGRAM_QR_INSTRUCTION": "Scan the QR code above to quickly test your Telegram inbox"
},
"REAUTH": "Reauthorize",
"VIEW": "View",
@@ -868,9 +916,18 @@
"SCRIPT_SETTINGS": "\n window.chatwootSettings = {options};"
},
"EMAIL_PROVIDERS": {
- "MICROSOFT": "Microsoft",
- "GOOGLE": "Google",
- "OTHER_PROVIDERS": "Other Providers"
+ "MICROSOFT": {
+ "TITLE": "Microsoft",
+ "DESCRIPTION": "Connect with Microsoft"
+ },
+ "GOOGLE": {
+ "TITLE": "Google",
+ "DESCRIPTION": "Connect with Google"
+ },
+ "OTHER_PROVIDERS": {
+ "TITLE": "Other Providers",
+ "DESCRIPTION": "Connect with Other Providers"
+ }
},
"CHANNELS": {
"MESSENGER": "Messenger",
diff --git a/app/javascript/dashboard/i18n/locale/az/integrations.json b/app/javascript/dashboard/i18n/locale/az/integrations.json
index be9281284..c59ec66df 100644
--- a/app/javascript/dashboard/i18n/locale/az/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/az/integrations.json
@@ -704,11 +704,28 @@
"ERROR_MESSAGE": "There was an error creating the document, please try again."
},
"FORM": {
+ "TYPE": {
+ "LABEL": "Document Type",
+ "URL": "URL",
+ "PDF": "PDF File"
+ },
"URL": {
"LABEL": "URL",
"PLACEHOLDER": "Enter the URL of the document",
"ERROR": "Please provide a valid URL for the document"
},
+ "PDF_FILE": {
+ "LABEL": "PDF File",
+ "CHOOSE_FILE": "Choose PDF file",
+ "ERROR": "Please select a PDF file",
+ "HELP_TEXT": "Maximum file size: 10MB",
+ "INVALID_TYPE": "Please select a valid PDF file",
+ "TOO_LARGE": "File size exceeds 10MB limit"
+ },
+ "NAME": {
+ "LABEL": "Document Name (Optional)",
+ "PLACEHOLDER": "Enter a name for the document"
+ },
"ASSISTANT": {
"LABEL": "Assistant",
"PLACEHOLDER": "Select the assistant",
@@ -744,6 +761,7 @@
"SELECTED": "{count} selected",
"SELECT_ALL": "Select all ({count})",
"UNSELECT_ALL": "Unselect all ({count})",
+ "SEARCH_PLACEHOLDER": "Search FAQs...",
"BULK_APPROVE_BUTTON": "Approve",
"BULK_DELETE_BUTTON": "Delete",
"BULK_APPROVE": {
diff --git a/app/javascript/dashboard/i18n/locale/az/mfa.json b/app/javascript/dashboard/i18n/locale/az/mfa.json
new file mode 100644
index 000000000..f7556fdcf
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/az/mfa.json
@@ -0,0 +1,106 @@
+{
+ "MFA_SETTINGS": {
+ "TITLE": "Two-Factor Authentication",
+ "SUBTITLE": "Secure your account with TOTP-based authentication",
+ "DESCRIPTION": "Add an extra layer of security to your account using a time-based one-time password (TOTP)",
+ "STATUS_TITLE": "Authentication Status",
+ "STATUS_DESCRIPTION": "Manage your two-factor authentication settings and backup recovery codes",
+ "ENABLED": "Enabled",
+ "DISABLED": "Disabled",
+ "STATUS_ENABLED": "Two-factor authentication is active",
+ "STATUS_ENABLED_DESC": "Your account is protected with an additional layer of security",
+ "ENABLE_BUTTON": "Enable Two-Factor Authentication",
+ "ENHANCE_SECURITY": "Enhance Your Account Security",
+ "ENHANCE_SECURITY_DESC": "Two-factor authentication adds an extra layer of security by requiring a verification code from your authenticator app in addition to your password.",
+ "SETUP": {
+ "STEP_NUMBER_1": "1",
+ "STEP_NUMBER_2": "2",
+ "STEP1_TITLE": "Scan QR Code with Your Authenticator App",
+ "STEP1_DESCRIPTION": "Use Google Authenticator, Authy, or any TOTP-compatible app",
+ "LOADING_QR": "Loading...",
+ "MANUAL_ENTRY": "Can't scan? Enter code manually",
+ "SECRET_KEY": "Secret Key",
+ "COPY": "Copy",
+ "ENTER_CODE": "Enter the 6-digit code from your authenticator app",
+ "ENTER_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify & Continue",
+ "CANCEL": "Cancel",
+ "ERROR_STARTING": "MFA not enabled. Please contact administrator.",
+ "INVALID_CODE": "Invalid verification code",
+ "SECRET_COPIED": "Secret key copied to clipboard",
+ "SUCCESS": "Two-factor authentication has been enabled successfully"
+ },
+ "BACKUP": {
+ "TITLE": "Save Your Backup Codes",
+ "DESCRIPTION": "Keep these codes safe. Each can be used once if you lose access to your authenticator",
+ "IMPORTANT": "Important:",
+ "IMPORTANT_NOTE": " Save these codes in a secure location. You won't be able to see them again.",
+ "DOWNLOAD": "Download",
+ "COPY_ALL": "Copy All",
+ "CONFIRM": "I have saved my backup codes in a secure location and understand that I won't be able to see them again",
+ "COMPLETE_SETUP": "Complete Setup",
+ "CODES_COPIED": "Backup codes copied to clipboard"
+ },
+ "MANAGEMENT": {
+ "BACKUP_CODES": "Backup Codes",
+ "BACKUP_CODES_DESC": "Generate new codes if you've lost or used your existing ones",
+ "REGENERATE": "Regenerate Backup Codes",
+ "DISABLE_MFA": "Disable 2FA",
+ "DISABLE_MFA_DESC": "Remove two-factor authentication from your account",
+ "DISABLE_BUTTON": "Disable Two-Factor Authentication"
+ },
+ "DISABLE": {
+ "TITLE": "Disable Two-Factor Authentication",
+ "DESCRIPTION": "You'll need to enter your password and a verification code to disable two-factor authentication.",
+ "PASSWORD": "Password",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Disable 2FA",
+ "CANCEL": "Cancel",
+ "SUCCESS": "Two-factor authentication has been disabled",
+ "ERROR": "Failed to disable MFA. Please check your credentials."
+ },
+ "REGENERATE": {
+ "TITLE": "Regenerate Backup Codes",
+ "DESCRIPTION": "This will invalidate your existing backup codes and generate new ones. Enter your verification code to continue.",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Generate New Codes",
+ "CANCEL": "Cancel",
+ "NEW_CODES_TITLE": "New Backup Codes Generated",
+ "NEW_CODES_DESC": "Your old backup codes have been invalidated. Save these new codes in a secure location.",
+ "CODES_IMPORTANT": "Important:",
+ "CODES_IMPORTANT_NOTE": " Each code can only be used once. Save them before closing this window.",
+ "DOWNLOAD_CODES": "Download Codes",
+ "COPY_ALL_CODES": "Copy All Codes",
+ "CODES_SAVED": "I've Saved My Codes",
+ "SUCCESS": "New backup codes have been generated",
+ "ERROR": "Failed to regenerate backup codes"
+ }
+ },
+ "MFA_VERIFICATION": {
+ "TITLE": "Two-Factor Authentication",
+ "DESCRIPTION": "Enter your verification code to continue",
+ "AUTHENTICATOR_APP": "Authenticator App",
+ "BACKUP_CODE": "Backup Code",
+ "ENTER_OTP_CODE": "Enter 6-digit code from your authenticator app",
+ "ENTER_BACKUP_CODE": "Enter one of your backup codes",
+ "BACKUP_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify",
+ "TRY_ANOTHER_METHOD": "Try another verification method",
+ "CANCEL_LOGIN": "Cancel and return to login",
+ "HELP_TEXT": "Having trouble signing in?",
+ "LEARN_MORE": "Learn more about 2FA",
+ "HELP_MODAL": {
+ "TITLE": "Two-Factor Authentication Help",
+ "AUTHENTICATOR_TITLE": "Using an Authenticator App",
+ "AUTHENTICATOR_DESC": "Open your authenticator app (Google Authenticator, Authy, etc.) and enter the 6-digit code shown for your account.",
+ "BACKUP_TITLE": "Using a Backup Code",
+ "BACKUP_DESC": "If you don't have access to your authenticator app, you can use one of the backup codes you saved when setting up 2FA. Each code can only be used once.",
+ "CONTACT_TITLE": "Need More Help?",
+ "CONTACT_DESC_CLOUD": "If you've lost access to both your authenticator app and backup codes, please reach out to Chatwoot support for assistance.",
+ "CONTACT_DESC_SELF_HOSTED": "If you've lost access to both your authenticator app and backup codes, please contact your administrator for assistance."
+ },
+ "VERIFICATION_FAILED": "Verification failed. Please try again."
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/az/settings.json b/app/javascript/dashboard/i18n/locale/az/settings.json
index d547538db..9ddc3b805 100644
--- a/app/javascript/dashboard/i18n/locale/az/settings.json
+++ b/app/javascript/dashboard/i18n/locale/az/settings.json
@@ -51,6 +51,13 @@
"LARGER": "Larger",
"EXTRA_LARGE": "Extra Large"
}
+ },
+ "LANGUAGE": {
+ "TITLE": "Preferred Language",
+ "NOTE": "Choose the language you want to use.",
+ "UPDATE_SUCCESS": "Your Language settings have been updated successfully",
+ "UPDATE_ERROR": "There is an error while updating the language settings, please try again",
+ "USE_ACCOUNT_DEFAULT": "Use account default"
}
},
"MESSAGE_SIGNATURE_SECTION": {
@@ -73,6 +80,11 @@
"NOTE": "Updating your password would reset your logins in multiple devices.",
"BTN_TEXT": "Change password"
},
+ "SECURITY_SECTION": {
+ "TITLE": "Security",
+ "NOTE": "Manage additional security features for your account.",
+ "MFA_BUTTON": "Manage Two-Factor Authentication"
+ },
"ACCESS_TOKEN": {
"TITLE": "Access Token",
"NOTE": "This token can be used if you are building an API based integration",
@@ -330,6 +342,7 @@
"REPORTS_LABEL": "Labels",
"REPORTS_INBOX": "Inbox",
"REPORTS_TEAM": "Team",
+ "AGENT_ASSIGNMENT": "Agent Assignment",
"SET_AVAILABILITY_TITLE": "Set yourself as",
"SET_YOUR_AVAILABILITY": "Set your availability",
"SLA": "SLA",
@@ -350,7 +363,8 @@
"INFO_TEXT": "Let the system automatically mark you offline when you aren't using the app or dashboard.",
"INFO_SHORT": "Automatically mark offline when you aren't using the app."
},
- "DOCS": "Read docs"
+ "DOCS": "Read docs",
+ "SECURITY": "Security"
},
"BILLING_SETTINGS": {
"TITLE": "Billing",
@@ -382,6 +396,77 @@
},
"NO_BILLING_USER": "Your billing account is being configured. Please refresh the page and try again."
},
+ "SECURITY_SETTINGS": {
+ "TITLE": "Security",
+ "DESCRIPTION": "Manage your account security settings.",
+ "LINK_TEXT": "Learn more about SAML SSO",
+ "SAML": {
+ "TITLE": "SAML SSO",
+ "NOTE": "Configure SAML single sign-on for your account. Users will authenticate through your identity provider instead of using email/password.",
+ "ACS_URL": {
+ "LABEL": "ACS URL",
+ "TOOLTIP": "Assertion Consumer Service URL - Configure this URL in your IdP as the destination for SAML responses"
+ },
+ "SSO_URL": {
+ "LABEL": "SSO URL",
+ "HELP": "The URL where SAML authentication requests will be sent",
+ "PLACEHOLDER": "https://your-idp.com/saml/sso"
+ },
+ "CERTIFICATE": {
+ "LABEL": "Signing certificate in PEM format",
+ "HELP": "The public certificate from your identity provider used to verify SAML responses",
+ "PLACEHOLDER": "-----BEGIN CERTIFICATE-----\nMIIC..."
+ },
+ "FINGERPRINT": {
+ "LABEL": "Fingerprint",
+ "TOOLTIP": "SHA-1 fingerprint of the certificate - Use this to verify the certificate in your IdP configuration"
+ },
+ "COPY_SUCCESS": "Copied to clipboard",
+ "SP_ENTITY_ID": {
+ "LABEL": "SP Entity ID",
+ "HELP": "Unique identifier for this application as a service provider (auto-generated).",
+ "TOOLTIP": "Unique identifier for Chatwoot as the Service Provider - Configure this in your IdP settings"
+ },
+ "IDP_ENTITY_ID": {
+ "LABEL": "Identity Provider Entity ID",
+ "HELP": "Unique identifier for your identity provider (usually found in IdP configuration)",
+ "PLACEHOLDER": "https://your-idp.com/saml"
+ },
+ "UPDATE_BUTTON": "Update SAML Settings",
+ "API": {
+ "SUCCESS": "SAML settings updated successfully",
+ "ERROR": "Failed to update SAML settings",
+ "ERROR_LOADING": "Failed to load SAML settings",
+ "DISABLED": "SAML settings disabled successfully"
+ },
+ "VALIDATION": {
+ "REQUIRED_FIELDS": "SSO URL, Identity Provider Entity ID, and Certificate are required fields",
+ "SSO_URL_ERROR": "Please enter a valid SSO URL",
+ "CERTIFICATE_ERROR": "Certificate is required",
+ "IDP_ENTITY_ID_ERROR": "Identity Provider Entity ID is required"
+ },
+ "ENTERPRISE_PAYWALL": {
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade to an Enterprise plan to access SAML single sign-on and other advanced security features.",
+ "ASK_ADMIN": "Please reach out to your administrator for the upgrade."
+ },
+ "PAYWALL": {
+ "TITLE": "Upgrade to enable SAML SSO",
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade your plan to get access to SAML single sign-on and other advanced features.",
+ "UPGRADE_NOW": "Upgrade now",
+ "CANCEL_ANYTIME": "You can change or cancel your plan anytime"
+ },
+ "ATTRIBUTE_MAPPING": {
+ "TITLE": "SAML Attribute Setup",
+ "DESCRIPTION": "The following attribute mappings must be configured in your identity provider"
+ },
+ "INFO_SECTION": {
+ "TITLE": "Service Provider Information",
+ "TOOLTIP": "Copy these values and configure them in your Identity Provider to establish the SAML connection"
+ }
+ }
+ },
"CREATE_ACCOUNT": {
"NO_ACCOUNT_WARNING": "Uh oh! We could not find any Chatwoot accounts. Please create a new account to continue.",
"NEW_ACCOUNT": "New Account",
@@ -418,5 +503,255 @@
"SWITCH_TO_REPLY": "Switch to Reply",
"TOGGLE_SNOOZE_DROPDOWN": "Toggle snooze dropdown"
}
+ },
+ "ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent assignment",
+ "DESCRIPTION": "Define policies to effectively manage workload and route conversations based on the needs of inboxes and agents. Learn more here"
+ },
+ "ASSIGNMENT_POLICY": {
+ "TITLE": "Assignment policy",
+ "DESCRIPTION": "Manage how conversations get assigned in inboxes.",
+ "FEATURES": [
+ "Assign by conversations evenly or by available capacity",
+ "Add fair distribution rules to avoid overloading any agent",
+ "Add inboxes to a policy - one policy per inbox"
+ ]
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "TITLE": "Agent capacity policy",
+ "DESCRIPTION": "Manage workload for agents.",
+ "FEATURES": [
+ "Define maximum conversations per inbox",
+ "Create exceptions based on labels and time",
+ "Add agents to a policy - one policy per agent"
+ ]
+ }
+ },
+ "AGENT_ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Assignment policy",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "ORDER": "Order",
+ "PRIORITY": "Priority",
+ "ACTIVE": "Active",
+ "INACTIVE": "Inactive",
+ "POPOVER": "Added inboxes",
+ "EDIT": "Edit"
+ },
+ "NO_RECORDS_FOUND": "No assignment policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create assignment policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy created successfully",
+ "ERROR_MESSAGE": "Failed to create assignment policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit assignment policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_INBOX_DIALOG": {
+ "TITLE": "Add inbox",
+ "DESCRIPTION": "{inboxName} inbox is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Cancel"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update assignment policy"
+ },
+ "INBOX_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Inbox added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Inbox removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove inbox from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Description:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "STATUS": {
+ "LABEL": "Status:",
+ "PLACEHOLDER": "Select status",
+ "ACTIVE": "Policy is active",
+ "INACTIVE": "Policy is inactive"
+ },
+ "ASSIGNMENT_ORDER": {
+ "LABEL": "Assignment order",
+ "ROUND_ROBIN": {
+ "LABEL": "Round robin",
+ "DESCRIPTION": "Assign conversations evenly among agents."
+ },
+ "BALANCED": {
+ "LABEL": "Balanced",
+ "DESCRIPTION": "Assign conversations based on available capacity."
+ }
+ },
+ "ASSIGNMENT_PRIORITY": {
+ "LABEL": "Assignment priority",
+ "EARLIEST_CREATED": {
+ "LABEL": "Earliest created",
+ "DESCRIPTION": "The conversation that was created first gets assigned first."
+ },
+ "LONGEST_WAITING": {
+ "LABEL": "Longest waiting",
+ "DESCRIPTION": "The conversation waiting the longest gets assigned first."
+ }
+ },
+ "FAIR_DISTRIBUTION": {
+ "LABEL": "Fair distribution policy",
+ "DESCRIPTION": "Set the maximum number of conversations that can be assigned per agent within a time window to avoid overloading any one agent. This required field defaults to 100 conversations per hour.",
+ "INPUT_MAX": "Assign max",
+ "DURATION": "Conversations per agent in every"
+ },
+ "INBOXES": {
+ "LABEL": "Added inboxes",
+ "DESCRIPTION": "Add inboxes for which this policy will be applicable.",
+ "ADD_BUTTON": "Add inbox",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select inboxes to add",
+ "ADD_BUTTON": "Add"
+ },
+ "EMPTY_STATE": "No inboxes added to this policy, add an inbox to get started",
+ "API": {
+ "SUCCESS_MESSAGE": "Inbox successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Assignment policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete assignment policy"
+ }
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent capacity",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "POPOVER": "Added agents",
+ "EDIT": "Edit"
+ },
+ "NO_RECORDS_FOUND": "No agent capacity policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create agent capacity policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy created successfully",
+ "ERROR_MESSAGE": "Failed to create agent capacity policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit agent capacity policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_AGENT_DIALOG": {
+ "TITLE": "Add agent",
+ "DESCRIPTION": "{agentName} is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Cancel"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update agent capacity policy"
+ },
+ "AGENT_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Agent added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Agent removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove agent from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Description:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "INBOX_CAPACITY_LIMIT": {
+ "LABEL": "Inbox capacity limits",
+ "ADD_BUTTON": "Add inbox",
+ "FIELD": {
+ "SELECT_INBOX": "Select inbox",
+ "MAX_CONVERSATIONS": "Max conversations",
+ "SET_LIMIT": "Set limit"
+ },
+ "EMPTY_STATE": "No inbox limit set"
+ },
+ "EXCLUSION_RULES": {
+ "LABEL": "Exclusion rules",
+ "DESCRIPTION": "Conversations that satisfy the following conditions would not count towards agent capacity",
+ "TAGS": {
+ "LABEL": "Exclude conversations tagged with specific labels",
+ "ADD_TAG": "add tag",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select tags to add"
+ },
+ "EMPTY_STATE": "No tags added to this policy."
+ },
+ "DURATION": {
+ "LABEL": "Exclude conversations older than a specified duration",
+ "PLACEHOLDER": "Set time"
+ }
+ },
+ "USERS": {
+ "LABEL": "Assigned agents",
+ "DESCRIPTION": "Add agents for which this policy will be applicable.",
+ "ADD_BUTTON": "Add agent",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select agents to add",
+ "ADD_BUTTON": "Add"
+ },
+ "EMPTY_STATE": "No agents added",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Agent capacity policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete agent capacity policy"
+ }
+ },
+ "DELETE_POLICY": {
+ "TITLE": "Delete policy",
+ "DESCRIPTION": "Are you sure you want to delete this policy? This action cannot be undone.",
+ "CONFIRM_BUTTON_LABEL": "Delete",
+ "CANCEL_BUTTON_LABEL": "Cancel"
+ }
}
}
diff --git a/app/javascript/dashboard/i18n/locale/az/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/az/whatsappTemplates.json
index 5f53faaa8..cf28312dc 100644
--- a/app/javascript/dashboard/i18n/locale/az/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/az/whatsappTemplates.json
@@ -40,6 +40,7 @@
"BUTTON_LABEL": "Button {index}",
"COUPON_CODE": "Enter coupon code (max 15 chars)",
"MEDIA_URL_LABEL": "Enter {type} URL",
+ "DOCUMENT_NAME_PLACEHOLDER": "Enter document filename (e.g., Invoice_2025.pdf)",
"BUTTON_PARAMETER": "Enter button parameter"
}
}
diff --git a/app/javascript/dashboard/i18n/locale/bg/automation.json b/app/javascript/dashboard/i18n/locale/bg/automation.json
index 006369305..d953bad7f 100644
--- a/app/javascript/dashboard/i18n/locale/bg/automation.json
+++ b/app/javascript/dashboard/i18n/locale/bg/automation.json
@@ -177,7 +177,8 @@
"REFERER_LINK": "Referrer Link",
"ASSIGNEE_NAME": "Assignee",
"TEAM_NAME": "Team",
- "PRIORITY": "Priority"
+ "PRIORITY": "Priority",
+ "LABELS": "Labels"
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/bg/contact.json b/app/javascript/dashboard/i18n/locale/bg/contact.json
index 7fa33de75..b29aa05b9 100644
--- a/app/javascript/dashboard/i18n/locale/bg/contact.json
+++ b/app/javascript/dashboard/i18n/locale/bg/contact.json
@@ -610,6 +610,15 @@
"SEND_MESSAGE": "Изпрати съобщение"
}
},
+ "TWILIO_OPTIONS": {
+ "LABEL": "Select template",
+ "SEARCH_PLACEHOLDER": "Search templates",
+ "EMPTY_STATE": "No templates found",
+ "TEMPLATE_PARSER": {
+ "BACK": "Go back",
+ "SEND_MESSAGE": "Изпрати съобщение"
+ }
+ },
"ACTION_BUTTONS": {
"DISCARD": "Discard",
"SEND": "Send ({keyCode})"
diff --git a/app/javascript/dashboard/i18n/locale/bg/contactFilters.json b/app/javascript/dashboard/i18n/locale/bg/contactFilters.json
index 4597c469c..052867d95 100644
--- a/app/javascript/dashboard/i18n/locale/bg/contactFilters.json
+++ b/app/javascript/dashboard/i18n/locale/bg/contactFilters.json
@@ -48,7 +48,8 @@
"CREATED_AT": "Created At",
"LAST_ACTIVITY": "Последна активност",
"REFERER_LINK": "Referrer link",
- "BLOCKED": "Blocked"
+ "BLOCKED": "Blocked",
+ "LABELS": "Labels"
},
"GROUPS": {
"STANDARD_FILTERS": "Standard Filters",
diff --git a/app/javascript/dashboard/i18n/locale/bg/contentTemplates.json b/app/javascript/dashboard/i18n/locale/bg/contentTemplates.json
new file mode 100644
index 000000000..a9b1d54c4
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/bg/contentTemplates.json
@@ -0,0 +1,51 @@
+{
+ "CONTENT_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Twilio Templates",
+ "SUBTITLE": "Select the Twilio template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Search Templates",
+ "NO_TEMPLATES_FOUND": "No templates found for",
+ "NO_CONTENT": "No content",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Category",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No Twilio templates available. Click refresh to sync templates from Twilio.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Language",
+ "TEMPLATE_BODY": "Template Body",
+ "CATEGORY": "Category"
+ },
+ "TYPES": {
+ "MEDIA": "Media",
+ "QUICK_REPLY": "Quick Reply",
+ "TEXT": "Text"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variables",
+ "LANGUAGE": "Language",
+ "CATEGORY": "Category",
+ "VARIABLE_PLACEHOLDER": "Enter {variable} value",
+ "GO_BACK_LABEL": "Go Back",
+ "SEND_MESSAGE_LABEL": "Send Message",
+ "FORM_ERROR_MESSAGE": "Please fill all variables before sending",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "MEDIA_URL_LABEL": "Enter full media URL",
+ "MEDIA_URL_PLACEHOLDER": "https://example.com/image.jpg"
+ },
+ "FORM": {
+ "BACK_BUTTON": "Back",
+ "SEND_MESSAGE_BUTTON": "Send Message"
+ }
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/bg/conversation.json b/app/javascript/dashboard/i18n/locale/bg/conversation.json
index ec7189e4c..b8d15ad41 100644
--- a/app/javascript/dashboard/i18n/locale/bg/conversation.json
+++ b/app/javascript/dashboard/i18n/locale/bg/conversation.json
@@ -35,6 +35,11 @@
"API_HOURS_WINDOW": "You can only reply to this conversation within {hours} hours",
"NOT_ASSIGNED_TO_YOU": "This conversation is not assigned to you. Would you like to assign this conversation to yourself?",
"ASSIGN_TO_ME": "Assign to me",
+ "BOT_HANDOFF_MESSAGE": "You are responding to a conversation which is currently handled by an assistant or a bot.",
+ "BOT_HANDOFF_ACTION": "Mark open and assign to you",
+ "BOT_HANDOFF_REOPEN_ACTION": "Mark conversation open",
+ "BOT_HANDOFF_SUCCESS": "Conversation has been handed over to you",
+ "BOT_HANDOFF_ERROR": "Failed to take over the conversation. Please try again.",
"TWILIO_WHATSAPP_CAN_REPLY": "You can only reply to this conversation using a template message due to",
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "24 hour message window restriction",
"OLD_INSTAGRAM_INBOX_REPLY_BANNER": "This Instagram account was migrated to the new Instagram channel inbox. All new messages will show up there. You won’t be able to send messages from this conversation anymore.",
@@ -66,6 +71,17 @@
"SHOW_LABELS": "Show labels",
"HIDE_LABELS": "Hide labels"
},
+ "VOICE_CALL": {
+ "INCOMING_CALL": "Incoming call",
+ "OUTGOING_CALL": "Outgoing call",
+ "CALL_IN_PROGRESS": "Call in progress",
+ "NO_ANSWER": "No answer",
+ "MISSED_CALL": "Missed call",
+ "CALL_ENDED": "Call ended",
+ "NOT_ANSWERED_YET": "Not answered yet",
+ "THEY_ANSWERED": "They answered",
+ "YOU_ANSWERED": "You answered"
+ },
"HEADER": {
"RESOLVE_ACTION": "Resolve",
"REOPEN_ACTION": "Reopen",
diff --git a/app/javascript/dashboard/i18n/locale/bg/helpCenter.json b/app/javascript/dashboard/i18n/locale/bg/helpCenter.json
index 3a6ee3cfe..863df4e5c 100644
--- a/app/javascript/dashboard/i18n/locale/bg/helpCenter.json
+++ b/app/javascript/dashboard/i18n/locale/bg/helpCenter.json
@@ -741,7 +741,8 @@
"LIVE_CHAT_WIDGET": {
"LABEL": "Live chat widget",
"PLACEHOLDER": "Select live chat widget",
- "HELP_TEXT": "Select a live chat widget that will appear on your help center"
+ "HELP_TEXT": "Select a live chat widget that will appear on your help center",
+ "NONE_OPTION": "No widget"
},
"BRAND_COLOR": {
"LABEL": "Brand color"
@@ -807,6 +808,58 @@
"ERROR_MESSAGE": "Unable to update portal"
}
}
+ },
+ "PDF_UPLOAD": {
+ "TITLE": "Upload PDF Document",
+ "DESCRIPTION": "Upload a PDF document to automatically generate FAQs using AI",
+ "DRAG_DROP_TEXT": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "ADDITIONAL_CONTEXT_LABEL": "Additional Context (Optional)",
+ "ADDITIONAL_CONTEXT_PLACEHOLDER": "Provide any additional context or instructions for FAQ generation...",
+ "UPLOADING": "Качване...",
+ "UPLOAD": "Upload & Process",
+ "CANCEL": "Отмени",
+ "ERROR_INVALID_TYPE": "Please select a valid PDF file",
+ "ERROR_FILE_TOO_LARGE": "File size must be less than 512MB",
+ "ERROR_UPLOAD_FAILED": "Failed to upload PDF. Please try again."
+ },
+ "PDF_DOCUMENTS": {
+ "TITLE": "PDF Documents",
+ "DESCRIPTION": "Manage uploaded PDF documents and generate FAQs from them",
+ "UPLOAD_PDF": "Upload PDF",
+ "UPLOAD_FIRST_PDF": "Upload your first PDF",
+ "UPLOADED_BY": "Uploaded by",
+ "GENERATE_FAQS": "Generate FAQs",
+ "GENERATING": "Generating...",
+ "CONFIRM_DELETE": "Are you sure you want to delete {filename}?",
+ "EMPTY_STATE": {
+ "TITLE": "No PDF documents yet",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQs using AI"
+ },
+ "STATUS": {
+ "UPLOADED": "Ready",
+ "PROCESSING": "Processing",
+ "PROCESSED": "Завършено",
+ "FAILED": "Failed"
+ }
+ },
+ "CONTENT_GENERATION": {
+ "TITLE": "Content Generation",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQ content using AI",
+ "UPLOAD_TITLE": "Upload PDF Document",
+ "DRAG_DROP": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "UPLOADING": "Processing document...",
+ "UPLOAD_SUCCESS": "Document processed successfully!",
+ "UPLOAD_ERROR": "Failed to upload document. Please try again.",
+ "INVALID_FILE_TYPE": "Please select a valid PDF file",
+ "FILE_TOO_LARGE": "File size must be less than 512MB",
+ "GENERATED_CONTENT": "Generated FAQ Content",
+ "PUBLISH_SELECTED": "Publish Selected",
+ "PUBLISHING": "Publishing...",
+ "FROM_DOCUMENT": "From document",
+ "NO_CONTENT": "No generated content available. Upload a PDF document to get started.",
+ "LOADING": "Loading generated content..."
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/bg/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/bg/inboxMgmt.json
index f741d22fe..280559832 100644
--- a/app/javascript/dashboard/i18n/locale/bg/inboxMgmt.json
+++ b/app/javascript/dashboard/i18n/locale/bg/inboxMgmt.json
@@ -272,8 +272,8 @@
},
"SUBMIT_BUTTON": "Create WhatsApp Channel",
"EMBEDDED_SIGNUP": {
- "TITLE": "Quick Setup with Meta",
- "DESC": "You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
+ "TITLE": "Quick setup with Meta",
+ "DESC": "Use the WhatsApp Embedded Signup flow to quickly connect new numbers. You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
"BENEFITS": {
"TITLE": "Benefits of Embedded Signup:",
"EASY_SETUP": "No manual configuration required",
@@ -281,9 +281,8 @@
"AUTO_CONFIG": "Automatic webhook and phone number configuration"
},
"LEARN_MORE": {
- "TEXT": "To learn more about integrated signup, pricing, and limitations, visit",
- "LINK_TEXT": "this link.",
- "LINK_URL": "https://developers.facebook.com/docs/whatsapp/embedded-signup/custom-flows/onboarding-business-app-users#limitations"
+ "TEXT": "To learn more about integrated signup, pricing, and limitations, visit {link}.",
+ "LINK_TEXT": "this link"
},
"SUBMIT_BUTTON": "Connect with WhatsApp Business",
"AUTH_PROCESSING": "Authenticating with Meta",
@@ -296,7 +295,9 @@
"INVALID_BUSINESS_DATA": "Invalid business data received from Facebook. Please try again.",
"SIGNUP_ERROR": "Signup error occurred",
"AUTH_NOT_COMPLETED": "Authentication not completed. Please restart the process.",
- "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured"
+ "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured",
+ "MANUAL_FALLBACK": "If your number is already connected to the WhatsApp Business Platform (API), or if you’re a tech provider onboarding your own number, please use the {link} flow",
+ "MANUAL_LINK_TEXT": "manual setup flow"
},
"API": {
"ERROR_MESSAGE": "We were not able to save the WhatsApp channel"
@@ -424,7 +425,51 @@
},
"AUTH": {
"TITLE": "Choose a channel",
- "DESC": "Chatwoot supports live-chat widgets, Facebook Messenger, Twitter profiles, WhatsApp, Emails, etc., as channels. If you want to build a custom channel, you can create it using the API channel. To get started, choose one of the channels below."
+ "DESC": "Chatwoot supports live-chat widgets, Facebook Messenger, Twitter profiles, WhatsApp, Emails, etc., as channels. If you want to build a custom channel, you can create it using the API channel. To get started, choose one of the channels below.",
+ "TITLE_NEXT": "Complete the setup",
+ "TITLE_FINISH": "Voilà!",
+ "CHANNEL": {
+ "WEBSITE": {
+ "TITLE": "Website",
+ "DESCRIPTION": "Create a live-chat widget"
+ },
+ "FACEBOOK": {
+ "TITLE": "Facebook",
+ "DESCRIPTION": "Connect your Facebook page"
+ },
+ "WHATSAPP": {
+ "TITLE": "WhatsApp",
+ "DESCRIPTION": "Support your customers on WhatsApp"
+ },
+ "EMAIL": {
+ "TITLE": "Email",
+ "DESCRIPTION": "Connect with Gmail, Outlook, or other providers"
+ },
+ "SMS": {
+ "TITLE": "SMS",
+ "DESCRIPTION": "Integrate SMS channel with Twilio or bandwidth"
+ },
+ "API": {
+ "TITLE": "API",
+ "DESCRIPTION": "Make a custom channel using our API"
+ },
+ "TELEGRAM": {
+ "TITLE": "Telegram",
+ "DESCRIPTION": "Configure Telegram channel using Bot token"
+ },
+ "LINE": {
+ "TITLE": "Line",
+ "DESCRIPTION": "Integrate your Line channel"
+ },
+ "INSTAGRAM": {
+ "TITLE": "Instagram",
+ "DESCRIPTION": "Connect your instagram account"
+ },
+ "VOICE": {
+ "TITLE": "Voice",
+ "DESCRIPTION": "Integrate with Twilio Voice"
+ }
+ }
},
"AGENTS": {
"TITLE": "Агенти",
@@ -478,7 +523,10 @@
"MESSAGE": "You can now engage with your customers through your new Channel. Happy supporting",
"BUTTON_TEXT": "Take me there",
"MORE_SETTINGS": "More settings",
- "WEBSITE_SUCCESS": "You have successfully finished creating a website channel. Copy the code shown below and paste it on your website. Next time a customer use the live chat, the conversation will automatically appear on your inbox."
+ "WEBSITE_SUCCESS": "You have successfully finished creating a website channel. Copy the code shown below and paste it on your website. Next time a customer use the live chat, the conversation will automatically appear on your inbox.",
+ "WHATSAPP_QR_INSTRUCTION": "Scan the QR code above to quickly test your WhatsApp inbox",
+ "MESSENGER_QR_INSTRUCTION": "Scan the QR code above to quickly test your Facebook Messenger inbox",
+ "TELEGRAM_QR_INSTRUCTION": "Scan the QR code above to quickly test your Telegram inbox"
},
"REAUTH": "Reauthorize",
"VIEW": "View",
@@ -868,9 +916,18 @@
"SCRIPT_SETTINGS": "\n window.chatwootSettings = {options};"
},
"EMAIL_PROVIDERS": {
- "MICROSOFT": "Microsoft",
- "GOOGLE": "Google",
- "OTHER_PROVIDERS": "Other Providers"
+ "MICROSOFT": {
+ "TITLE": "Microsoft",
+ "DESCRIPTION": "Connect with Microsoft"
+ },
+ "GOOGLE": {
+ "TITLE": "Google",
+ "DESCRIPTION": "Connect with Google"
+ },
+ "OTHER_PROVIDERS": {
+ "TITLE": "Other Providers",
+ "DESCRIPTION": "Connect with Other Providers"
+ }
},
"CHANNELS": {
"MESSENGER": "Messenger",
diff --git a/app/javascript/dashboard/i18n/locale/bg/integrations.json b/app/javascript/dashboard/i18n/locale/bg/integrations.json
index 1069559a6..0ebfc0fc8 100644
--- a/app/javascript/dashboard/i18n/locale/bg/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/bg/integrations.json
@@ -704,11 +704,28 @@
"ERROR_MESSAGE": "There was an error creating the document, please try again."
},
"FORM": {
+ "TYPE": {
+ "LABEL": "Document Type",
+ "URL": "URL",
+ "PDF": "PDF File"
+ },
"URL": {
"LABEL": "URL",
"PLACEHOLDER": "Enter the URL of the document",
"ERROR": "Please provide a valid URL for the document"
},
+ "PDF_FILE": {
+ "LABEL": "PDF File",
+ "CHOOSE_FILE": "Choose PDF file",
+ "ERROR": "Please select a PDF file",
+ "HELP_TEXT": "Maximum file size: 10MB",
+ "INVALID_TYPE": "Please select a valid PDF file",
+ "TOO_LARGE": "File size exceeds 10MB limit"
+ },
+ "NAME": {
+ "LABEL": "Document Name (Optional)",
+ "PLACEHOLDER": "Enter a name for the document"
+ },
"ASSISTANT": {
"LABEL": "Assistant",
"PLACEHOLDER": "Select the assistant",
@@ -744,6 +761,7 @@
"SELECTED": "{count} selected",
"SELECT_ALL": "Select all ({count})",
"UNSELECT_ALL": "Unselect all ({count})",
+ "SEARCH_PLACEHOLDER": "Search FAQs...",
"BULK_APPROVE_BUTTON": "Approve",
"BULK_DELETE_BUTTON": "Изтрий",
"BULK_APPROVE": {
diff --git a/app/javascript/dashboard/i18n/locale/bg/mfa.json b/app/javascript/dashboard/i18n/locale/bg/mfa.json
new file mode 100644
index 000000000..cc58c4378
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/bg/mfa.json
@@ -0,0 +1,106 @@
+{
+ "MFA_SETTINGS": {
+ "TITLE": "Two-Factor Authentication",
+ "SUBTITLE": "Secure your account with TOTP-based authentication",
+ "DESCRIPTION": "Add an extra layer of security to your account using a time-based one-time password (TOTP)",
+ "STATUS_TITLE": "Authentication Status",
+ "STATUS_DESCRIPTION": "Manage your two-factor authentication settings and backup recovery codes",
+ "ENABLED": "Enabled",
+ "DISABLED": "Disabled",
+ "STATUS_ENABLED": "Two-factor authentication is active",
+ "STATUS_ENABLED_DESC": "Your account is protected with an additional layer of security",
+ "ENABLE_BUTTON": "Enable Two-Factor Authentication",
+ "ENHANCE_SECURITY": "Enhance Your Account Security",
+ "ENHANCE_SECURITY_DESC": "Two-factor authentication adds an extra layer of security by requiring a verification code from your authenticator app in addition to your password.",
+ "SETUP": {
+ "STEP_NUMBER_1": "1",
+ "STEP_NUMBER_2": "2",
+ "STEP1_TITLE": "Scan QR Code with Your Authenticator App",
+ "STEP1_DESCRIPTION": "Use Google Authenticator, Authy, or any TOTP-compatible app",
+ "LOADING_QR": "Loading...",
+ "MANUAL_ENTRY": "Can't scan? Enter code manually",
+ "SECRET_KEY": "Secret Key",
+ "COPY": "Copy",
+ "ENTER_CODE": "Enter the 6-digit code from your authenticator app",
+ "ENTER_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify & Continue",
+ "CANCEL": "Отмени",
+ "ERROR_STARTING": "MFA not enabled. Please contact administrator.",
+ "INVALID_CODE": "Invalid verification code",
+ "SECRET_COPIED": "Secret key copied to clipboard",
+ "SUCCESS": "Two-factor authentication has been enabled successfully"
+ },
+ "BACKUP": {
+ "TITLE": "Save Your Backup Codes",
+ "DESCRIPTION": "Keep these codes safe. Each can be used once if you lose access to your authenticator",
+ "IMPORTANT": "Important:",
+ "IMPORTANT_NOTE": " Save these codes in a secure location. You won't be able to see them again.",
+ "DOWNLOAD": "Download",
+ "COPY_ALL": "Copy All",
+ "CONFIRM": "I have saved my backup codes in a secure location and understand that I won't be able to see them again",
+ "COMPLETE_SETUP": "Complete Setup",
+ "CODES_COPIED": "Backup codes copied to clipboard"
+ },
+ "MANAGEMENT": {
+ "BACKUP_CODES": "Backup Codes",
+ "BACKUP_CODES_DESC": "Generate new codes if you've lost or used your existing ones",
+ "REGENERATE": "Regenerate Backup Codes",
+ "DISABLE_MFA": "Disable 2FA",
+ "DISABLE_MFA_DESC": "Remove two-factor authentication from your account",
+ "DISABLE_BUTTON": "Disable Two-Factor Authentication"
+ },
+ "DISABLE": {
+ "TITLE": "Disable Two-Factor Authentication",
+ "DESCRIPTION": "You'll need to enter your password and a verification code to disable two-factor authentication.",
+ "PASSWORD": "Password",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Disable 2FA",
+ "CANCEL": "Отмени",
+ "SUCCESS": "Two-factor authentication has been disabled",
+ "ERROR": "Failed to disable MFA. Please check your credentials."
+ },
+ "REGENERATE": {
+ "TITLE": "Regenerate Backup Codes",
+ "DESCRIPTION": "This will invalidate your existing backup codes and generate new ones. Enter your verification code to continue.",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Generate New Codes",
+ "CANCEL": "Отмени",
+ "NEW_CODES_TITLE": "New Backup Codes Generated",
+ "NEW_CODES_DESC": "Your old backup codes have been invalidated. Save these new codes in a secure location.",
+ "CODES_IMPORTANT": "Important:",
+ "CODES_IMPORTANT_NOTE": " Each code can only be used once. Save them before closing this window.",
+ "DOWNLOAD_CODES": "Download Codes",
+ "COPY_ALL_CODES": "Copy All Codes",
+ "CODES_SAVED": "I've Saved My Codes",
+ "SUCCESS": "New backup codes have been generated",
+ "ERROR": "Failed to regenerate backup codes"
+ }
+ },
+ "MFA_VERIFICATION": {
+ "TITLE": "Two-Factor Authentication",
+ "DESCRIPTION": "Enter your verification code to continue",
+ "AUTHENTICATOR_APP": "Authenticator App",
+ "BACKUP_CODE": "Backup Code",
+ "ENTER_OTP_CODE": "Enter 6-digit code from your authenticator app",
+ "ENTER_BACKUP_CODE": "Enter one of your backup codes",
+ "BACKUP_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify",
+ "TRY_ANOTHER_METHOD": "Try another verification method",
+ "CANCEL_LOGIN": "Cancel and return to login",
+ "HELP_TEXT": "Having trouble signing in?",
+ "LEARN_MORE": "Learn more about 2FA",
+ "HELP_MODAL": {
+ "TITLE": "Two-Factor Authentication Help",
+ "AUTHENTICATOR_TITLE": "Using an Authenticator App",
+ "AUTHENTICATOR_DESC": "Open your authenticator app (Google Authenticator, Authy, etc.) and enter the 6-digit code shown for your account.",
+ "BACKUP_TITLE": "Using a Backup Code",
+ "BACKUP_DESC": "If you don't have access to your authenticator app, you can use one of the backup codes you saved when setting up 2FA. Each code can only be used once.",
+ "CONTACT_TITLE": "Need More Help?",
+ "CONTACT_DESC_CLOUD": "If you've lost access to both your authenticator app and backup codes, please reach out to Chatwoot support for assistance.",
+ "CONTACT_DESC_SELF_HOSTED": "If you've lost access to both your authenticator app and backup codes, please contact your administrator for assistance."
+ },
+ "VERIFICATION_FAILED": "Verification failed. Please try again."
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/bg/settings.json b/app/javascript/dashboard/i18n/locale/bg/settings.json
index 1d0eae470..1b7fabeaf 100644
--- a/app/javascript/dashboard/i18n/locale/bg/settings.json
+++ b/app/javascript/dashboard/i18n/locale/bg/settings.json
@@ -51,6 +51,13 @@
"LARGER": "Larger",
"EXTRA_LARGE": "Extra Large"
}
+ },
+ "LANGUAGE": {
+ "TITLE": "Preferred Language",
+ "NOTE": "Choose the language you want to use.",
+ "UPDATE_SUCCESS": "Your Language settings have been updated successfully",
+ "UPDATE_ERROR": "There is an error while updating the language settings, please try again",
+ "USE_ACCOUNT_DEFAULT": "Use account default"
}
},
"MESSAGE_SIGNATURE_SECTION": {
@@ -73,6 +80,11 @@
"NOTE": "Updating your password would reset your logins in multiple devices.",
"BTN_TEXT": "Change password"
},
+ "SECURITY_SECTION": {
+ "TITLE": "Security",
+ "NOTE": "Manage additional security features for your account.",
+ "MFA_BUTTON": "Manage Two-Factor Authentication"
+ },
"ACCESS_TOKEN": {
"TITLE": "Access Token",
"NOTE": "This token can be used if you are building an API based integration",
@@ -330,6 +342,7 @@
"REPORTS_LABEL": "Labels",
"REPORTS_INBOX": "Входяща кутия",
"REPORTS_TEAM": "Team",
+ "AGENT_ASSIGNMENT": "Agent Assignment",
"SET_AVAILABILITY_TITLE": "Set yourself as",
"SET_YOUR_AVAILABILITY": "Set your availability",
"SLA": "SLA",
@@ -350,7 +363,8 @@
"INFO_TEXT": "Let the system automatically mark you offline when you aren't using the app or dashboard.",
"INFO_SHORT": "Automatically mark offline when you aren't using the app."
},
- "DOCS": "Read docs"
+ "DOCS": "Read docs",
+ "SECURITY": "Security"
},
"BILLING_SETTINGS": {
"TITLE": "Billing",
@@ -382,6 +396,77 @@
},
"NO_BILLING_USER": "Your billing account is being configured. Please refresh the page and try again."
},
+ "SECURITY_SETTINGS": {
+ "TITLE": "Security",
+ "DESCRIPTION": "Manage your account security settings.",
+ "LINK_TEXT": "Learn more about SAML SSO",
+ "SAML": {
+ "TITLE": "SAML SSO",
+ "NOTE": "Configure SAML single sign-on for your account. Users will authenticate through your identity provider instead of using email/password.",
+ "ACS_URL": {
+ "LABEL": "ACS URL",
+ "TOOLTIP": "Assertion Consumer Service URL - Configure this URL in your IdP as the destination for SAML responses"
+ },
+ "SSO_URL": {
+ "LABEL": "SSO URL",
+ "HELP": "The URL where SAML authentication requests will be sent",
+ "PLACEHOLDER": "https://your-idp.com/saml/sso"
+ },
+ "CERTIFICATE": {
+ "LABEL": "Signing certificate in PEM format",
+ "HELP": "The public certificate from your identity provider used to verify SAML responses",
+ "PLACEHOLDER": "-----BEGIN CERTIFICATE-----\nMIIC..."
+ },
+ "FINGERPRINT": {
+ "LABEL": "Fingerprint",
+ "TOOLTIP": "SHA-1 fingerprint of the certificate - Use this to verify the certificate in your IdP configuration"
+ },
+ "COPY_SUCCESS": "Code copied to clipboard successfully",
+ "SP_ENTITY_ID": {
+ "LABEL": "SP Entity ID",
+ "HELP": "Unique identifier for this application as a service provider (auto-generated).",
+ "TOOLTIP": "Unique identifier for Chatwoot as the Service Provider - Configure this in your IdP settings"
+ },
+ "IDP_ENTITY_ID": {
+ "LABEL": "Identity Provider Entity ID",
+ "HELP": "Unique identifier for your identity provider (usually found in IdP configuration)",
+ "PLACEHOLDER": "https://your-idp.com/saml"
+ },
+ "UPDATE_BUTTON": "Update SAML Settings",
+ "API": {
+ "SUCCESS": "SAML settings updated successfully",
+ "ERROR": "Failed to update SAML settings",
+ "ERROR_LOADING": "Failed to load SAML settings",
+ "DISABLED": "SAML settings disabled successfully"
+ },
+ "VALIDATION": {
+ "REQUIRED_FIELDS": "SSO URL, Identity Provider Entity ID, and Certificate are required fields",
+ "SSO_URL_ERROR": "Please enter a valid SSO URL",
+ "CERTIFICATE_ERROR": "Certificate is required",
+ "IDP_ENTITY_ID_ERROR": "Identity Provider Entity ID is required"
+ },
+ "ENTERPRISE_PAYWALL": {
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade to an Enterprise plan to access SAML single sign-on and other advanced security features.",
+ "ASK_ADMIN": "Please reach out to your administrator for the upgrade."
+ },
+ "PAYWALL": {
+ "TITLE": "Upgrade to enable SAML SSO",
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade your plan to get access to SAML single sign-on and other advanced features.",
+ "UPGRADE_NOW": "Upgrade now",
+ "CANCEL_ANYTIME": "You can change or cancel your plan anytime"
+ },
+ "ATTRIBUTE_MAPPING": {
+ "TITLE": "SAML Attribute Setup",
+ "DESCRIPTION": "The following attribute mappings must be configured in your identity provider"
+ },
+ "INFO_SECTION": {
+ "TITLE": "Service Provider Information",
+ "TOOLTIP": "Copy these values and configure them in your Identity Provider to establish the SAML connection"
+ }
+ }
+ },
"CREATE_ACCOUNT": {
"NO_ACCOUNT_WARNING": "Uh oh! We could not find any Chatwoot accounts. Please create a new account to continue.",
"NEW_ACCOUNT": "New Account",
@@ -418,5 +503,255 @@
"SWITCH_TO_REPLY": "Switch to Reply",
"TOGGLE_SNOOZE_DROPDOWN": "Toggle snooze dropdown"
}
+ },
+ "ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent assignment",
+ "DESCRIPTION": "Define policies to effectively manage workload and route conversations based on the needs of inboxes and agents. Learn more here"
+ },
+ "ASSIGNMENT_POLICY": {
+ "TITLE": "Assignment policy",
+ "DESCRIPTION": "Manage how conversations get assigned in inboxes.",
+ "FEATURES": [
+ "Assign by conversations evenly or by available capacity",
+ "Add fair distribution rules to avoid overloading any agent",
+ "Add inboxes to a policy - one policy per inbox"
+ ]
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "TITLE": "Agent capacity policy",
+ "DESCRIPTION": "Manage workload for agents.",
+ "FEATURES": [
+ "Define maximum conversations per inbox",
+ "Create exceptions based on labels and time",
+ "Add agents to a policy - one policy per agent"
+ ]
+ }
+ },
+ "AGENT_ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Assignment policy",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "ORDER": "Order",
+ "PRIORITY": "Priority",
+ "ACTIVE": "Активен",
+ "INACTIVE": "Inactive",
+ "POPOVER": "Added inboxes",
+ "EDIT": "Редактирай"
+ },
+ "NO_RECORDS_FOUND": "No assignment policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create assignment policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy created successfully",
+ "ERROR_MESSAGE": "Failed to create assignment policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit assignment policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_INBOX_DIALOG": {
+ "TITLE": "Add inbox",
+ "DESCRIPTION": "{inboxName} inbox is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Отмени"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update assignment policy"
+ },
+ "INBOX_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Inbox added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Inbox removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove inbox from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Описание:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "STATUS": {
+ "LABEL": "Статус:",
+ "PLACEHOLDER": "Select status",
+ "ACTIVE": "Policy is active",
+ "INACTIVE": "Policy is inactive"
+ },
+ "ASSIGNMENT_ORDER": {
+ "LABEL": "Assignment order",
+ "ROUND_ROBIN": {
+ "LABEL": "Round robin",
+ "DESCRIPTION": "Assign conversations evenly among agents."
+ },
+ "BALANCED": {
+ "LABEL": "Balanced",
+ "DESCRIPTION": "Assign conversations based on available capacity."
+ }
+ },
+ "ASSIGNMENT_PRIORITY": {
+ "LABEL": "Assignment priority",
+ "EARLIEST_CREATED": {
+ "LABEL": "Earliest created",
+ "DESCRIPTION": "The conversation that was created first gets assigned first."
+ },
+ "LONGEST_WAITING": {
+ "LABEL": "Longest waiting",
+ "DESCRIPTION": "The conversation waiting the longest gets assigned first."
+ }
+ },
+ "FAIR_DISTRIBUTION": {
+ "LABEL": "Fair distribution policy",
+ "DESCRIPTION": "Set the maximum number of conversations that can be assigned per agent within a time window to avoid overloading any one agent. This required field defaults to 100 conversations per hour.",
+ "INPUT_MAX": "Assign max",
+ "DURATION": "Conversations per agent in every"
+ },
+ "INBOXES": {
+ "LABEL": "Added inboxes",
+ "DESCRIPTION": "Add inboxes for which this policy will be applicable.",
+ "ADD_BUTTON": "Add inbox",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select inboxes to add",
+ "ADD_BUTTON": "Добавяне"
+ },
+ "EMPTY_STATE": "No inboxes added to this policy, add an inbox to get started",
+ "API": {
+ "SUCCESS_MESSAGE": "Inbox successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Assignment policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete assignment policy"
+ }
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent capacity",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "POPOVER": "Added agents",
+ "EDIT": "Редактирай"
+ },
+ "NO_RECORDS_FOUND": "No agent capacity policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create agent capacity policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy created successfully",
+ "ERROR_MESSAGE": "Failed to create agent capacity policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit agent capacity policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_AGENT_DIALOG": {
+ "TITLE": "Add agent",
+ "DESCRIPTION": "{agentName} is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Отмени"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update agent capacity policy"
+ },
+ "AGENT_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Agent added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Agent removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove agent from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Описание:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "INBOX_CAPACITY_LIMIT": {
+ "LABEL": "Inbox capacity limits",
+ "ADD_BUTTON": "Add inbox",
+ "FIELD": {
+ "SELECT_INBOX": "Select inbox",
+ "MAX_CONVERSATIONS": "Max conversations",
+ "SET_LIMIT": "Set limit"
+ },
+ "EMPTY_STATE": "No inbox limit set"
+ },
+ "EXCLUSION_RULES": {
+ "LABEL": "Exclusion rules",
+ "DESCRIPTION": "Conversations that satisfy the following conditions would not count towards agent capacity",
+ "TAGS": {
+ "LABEL": "Exclude conversations tagged with specific labels",
+ "ADD_TAG": "add tag",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select tags to add"
+ },
+ "EMPTY_STATE": "No tags added to this policy."
+ },
+ "DURATION": {
+ "LABEL": "Exclude conversations older than a specified duration",
+ "PLACEHOLDER": "Set time"
+ }
+ },
+ "USERS": {
+ "LABEL": "Assigned agents",
+ "DESCRIPTION": "Add agents for which this policy will be applicable.",
+ "ADD_BUTTON": "Add agent",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select agents to add",
+ "ADD_BUTTON": "Добавяне"
+ },
+ "EMPTY_STATE": "No agents added",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Agent capacity policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete agent capacity policy"
+ }
+ },
+ "DELETE_POLICY": {
+ "TITLE": "Delete policy",
+ "DESCRIPTION": "Are you sure you want to delete this policy? This action cannot be undone.",
+ "CONFIRM_BUTTON_LABEL": "Изтрий",
+ "CANCEL_BUTTON_LABEL": "Отмени"
+ }
}
}
diff --git a/app/javascript/dashboard/i18n/locale/bg/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/bg/whatsappTemplates.json
index 5f53faaa8..cf28312dc 100644
--- a/app/javascript/dashboard/i18n/locale/bg/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/bg/whatsappTemplates.json
@@ -40,6 +40,7 @@
"BUTTON_LABEL": "Button {index}",
"COUPON_CODE": "Enter coupon code (max 15 chars)",
"MEDIA_URL_LABEL": "Enter {type} URL",
+ "DOCUMENT_NAME_PLACEHOLDER": "Enter document filename (e.g., Invoice_2025.pdf)",
"BUTTON_PARAMETER": "Enter button parameter"
}
}
diff --git a/app/javascript/dashboard/i18n/locale/ca/automation.json b/app/javascript/dashboard/i18n/locale/ca/automation.json
index d961c762a..6482bea60 100644
--- a/app/javascript/dashboard/i18n/locale/ca/automation.json
+++ b/app/javascript/dashboard/i18n/locale/ca/automation.json
@@ -177,7 +177,8 @@
"REFERER_LINK": "Referrer Link",
"ASSIGNEE_NAME": "Cessionari",
"TEAM_NAME": "Equip",
- "PRIORITY": "Prioritat"
+ "PRIORITY": "Prioritat",
+ "LABELS": "Etiquetes"
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/ca/contact.json b/app/javascript/dashboard/i18n/locale/ca/contact.json
index 92648bd8e..7e67256b7 100644
--- a/app/javascript/dashboard/i18n/locale/ca/contact.json
+++ b/app/javascript/dashboard/i18n/locale/ca/contact.json
@@ -610,6 +610,15 @@
"SEND_MESSAGE": "Envia missatge"
}
},
+ "TWILIO_OPTIONS": {
+ "LABEL": "Select template",
+ "SEARCH_PLACEHOLDER": "Search templates",
+ "EMPTY_STATE": "No templates found",
+ "TEMPLATE_PARSER": {
+ "BACK": "Torna",
+ "SEND_MESSAGE": "Envia missatge"
+ }
+ },
"ACTION_BUTTONS": {
"DISCARD": "Discard",
"SEND": "Send ({keyCode})"
diff --git a/app/javascript/dashboard/i18n/locale/ca/contactFilters.json b/app/javascript/dashboard/i18n/locale/ca/contactFilters.json
index 5cafccbdf..92eea6e1c 100644
--- a/app/javascript/dashboard/i18n/locale/ca/contactFilters.json
+++ b/app/javascript/dashboard/i18n/locale/ca/contactFilters.json
@@ -48,7 +48,8 @@
"CREATED_AT": "Creat per",
"LAST_ACTIVITY": "Darrera activitat",
"REFERER_LINK": "Enllaç de referència",
- "BLOCKED": "Blocat"
+ "BLOCKED": "Blocat",
+ "LABELS": "Etiquetes"
},
"GROUPS": {
"STANDARD_FILTERS": "Filtres estàndard",
diff --git a/app/javascript/dashboard/i18n/locale/ca/contentTemplates.json b/app/javascript/dashboard/i18n/locale/ca/contentTemplates.json
new file mode 100644
index 000000000..51522c0ac
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/ca/contentTemplates.json
@@ -0,0 +1,51 @@
+{
+ "CONTENT_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Twilio Templates",
+ "SUBTITLE": "Select the Twilio template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Cerca plantilles",
+ "NO_TEMPLATES_FOUND": "No s'han trobat plantilles per a",
+ "NO_CONTENT": "Sense contingut",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Categoria",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No Twilio templates available. Click refresh to sync templates from Twilio.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Idioma",
+ "TEMPLATE_BODY": "Cos de la plantilla",
+ "CATEGORY": "Categoria"
+ },
+ "TYPES": {
+ "MEDIA": "Media",
+ "QUICK_REPLY": "Quick Reply",
+ "TEXT": "Llista"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variables",
+ "LANGUAGE": "Idioma",
+ "CATEGORY": "Categoria",
+ "VARIABLE_PLACEHOLDER": "Introdueix el valor {variable}",
+ "GO_BACK_LABEL": "Torna enrere",
+ "SEND_MESSAGE_LABEL": "Envia missatge",
+ "FORM_ERROR_MESSAGE": "Omple totes les variables abans d'enviar-les",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "MEDIA_URL_LABEL": "Enter full media URL",
+ "MEDIA_URL_PLACEHOLDER": "https://example.com/image.jpg"
+ },
+ "FORM": {
+ "BACK_BUTTON": "Enrere",
+ "SEND_MESSAGE_BUTTON": "Envia missatge"
+ }
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/ca/conversation.json b/app/javascript/dashboard/i18n/locale/ca/conversation.json
index 220cafc46..4c692c7c5 100644
--- a/app/javascript/dashboard/i18n/locale/ca/conversation.json
+++ b/app/javascript/dashboard/i18n/locale/ca/conversation.json
@@ -35,6 +35,11 @@
"API_HOURS_WINDOW": "You can only reply to this conversation within {hours} hours",
"NOT_ASSIGNED_TO_YOU": "Aquesta conversa no està assignada a tu. Vols assignar-te-la?",
"ASSIGN_TO_ME": "Assigna'm",
+ "BOT_HANDOFF_MESSAGE": "You are responding to a conversation which is currently handled by an assistant or a bot.",
+ "BOT_HANDOFF_ACTION": "Mark open and assign to you",
+ "BOT_HANDOFF_REOPEN_ACTION": "Mark conversation open",
+ "BOT_HANDOFF_SUCCESS": "Conversation has been handed over to you",
+ "BOT_HANDOFF_ERROR": "Failed to take over the conversation. Please try again.",
"TWILIO_WHATSAPP_CAN_REPLY": "Només pots respondre a aquesta conversa mitjançant una plantilla de missatge a causa de",
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "Restricció de finestra de missatges de 24 hores",
"OLD_INSTAGRAM_INBOX_REPLY_BANNER": "This Instagram account was migrated to the new Instagram channel inbox. All new messages will show up there. You won’t be able to send messages from this conversation anymore.",
@@ -66,6 +71,17 @@
"SHOW_LABELS": "Mostra etiquetes",
"HIDE_LABELS": "Amaga etiquetes"
},
+ "VOICE_CALL": {
+ "INCOMING_CALL": "Incoming call",
+ "OUTGOING_CALL": "Outgoing call",
+ "CALL_IN_PROGRESS": "Call in progress",
+ "NO_ANSWER": "No answer",
+ "MISSED_CALL": "Missed call",
+ "CALL_ENDED": "Call ended",
+ "NOT_ANSWERED_YET": "Not answered yet",
+ "THEY_ANSWERED": "They answered",
+ "YOU_ANSWERED": "You answered"
+ },
"HEADER": {
"RESOLVE_ACTION": "Resoldre",
"REOPEN_ACTION": "Tornar a obrir",
diff --git a/app/javascript/dashboard/i18n/locale/ca/helpCenter.json b/app/javascript/dashboard/i18n/locale/ca/helpCenter.json
index fd66b6abd..f7d247cf5 100644
--- a/app/javascript/dashboard/i18n/locale/ca/helpCenter.json
+++ b/app/javascript/dashboard/i18n/locale/ca/helpCenter.json
@@ -741,7 +741,8 @@
"LIVE_CHAT_WIDGET": {
"LABEL": "Live chat widget",
"PLACEHOLDER": "Select live chat widget",
- "HELP_TEXT": "Select a live chat widget that will appear on your help center"
+ "HELP_TEXT": "Select a live chat widget that will appear on your help center",
+ "NONE_OPTION": "No widget"
},
"BRAND_COLOR": {
"LABEL": "Brand color"
@@ -807,6 +808,58 @@
"ERROR_MESSAGE": "Unable to update portal"
}
}
+ },
+ "PDF_UPLOAD": {
+ "TITLE": "Upload PDF Document",
+ "DESCRIPTION": "Upload a PDF document to automatically generate FAQs using AI",
+ "DRAG_DROP_TEXT": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "ADDITIONAL_CONTEXT_LABEL": "Additional Context (Optional)",
+ "ADDITIONAL_CONTEXT_PLACEHOLDER": "Provide any additional context or instructions for FAQ generation...",
+ "UPLOADING": "S'està carregant...",
+ "UPLOAD": "Upload & Process",
+ "CANCEL": "Cancel·la",
+ "ERROR_INVALID_TYPE": "Please select a valid PDF file",
+ "ERROR_FILE_TOO_LARGE": "File size must be less than 512MB",
+ "ERROR_UPLOAD_FAILED": "Failed to upload PDF. Please try again."
+ },
+ "PDF_DOCUMENTS": {
+ "TITLE": "PDF Documents",
+ "DESCRIPTION": "Manage uploaded PDF documents and generate FAQs from them",
+ "UPLOAD_PDF": "Upload PDF",
+ "UPLOAD_FIRST_PDF": "Upload your first PDF",
+ "UPLOADED_BY": "Uploaded by",
+ "GENERATE_FAQS": "Generate FAQs",
+ "GENERATING": "Generant...",
+ "CONFIRM_DELETE": "Are you sure you want to delete {filename}?",
+ "EMPTY_STATE": {
+ "TITLE": "No PDF documents yet",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQs using AI"
+ },
+ "STATUS": {
+ "UPLOADED": "Ready",
+ "PROCESSING": "Processing",
+ "PROCESSED": "Completat",
+ "FAILED": "Failed"
+ }
+ },
+ "CONTENT_GENERATION": {
+ "TITLE": "Content Generation",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQ content using AI",
+ "UPLOAD_TITLE": "Upload PDF Document",
+ "DRAG_DROP": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "UPLOADING": "Processing document...",
+ "UPLOAD_SUCCESS": "Document processed successfully!",
+ "UPLOAD_ERROR": "Failed to upload document. Please try again.",
+ "INVALID_FILE_TYPE": "Please select a valid PDF file",
+ "FILE_TOO_LARGE": "File size must be less than 512MB",
+ "GENERATED_CONTENT": "Generated FAQ Content",
+ "PUBLISH_SELECTED": "Publish Selected",
+ "PUBLISHING": "Publishing...",
+ "FROM_DOCUMENT": "From document",
+ "NO_CONTENT": "No generated content available. Upload a PDF document to get started.",
+ "LOADING": "Loading generated content..."
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/ca/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/ca/inboxMgmt.json
index f51f560fd..40f4e03d1 100644
--- a/app/javascript/dashboard/i18n/locale/ca/inboxMgmt.json
+++ b/app/javascript/dashboard/i18n/locale/ca/inboxMgmt.json
@@ -272,8 +272,8 @@
},
"SUBMIT_BUTTON": "Crea un canal de WhatsApp",
"EMBEDDED_SIGNUP": {
- "TITLE": "Quick Setup with Meta",
- "DESC": "You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
+ "TITLE": "Quick setup with Meta",
+ "DESC": "Use the WhatsApp Embedded Signup flow to quickly connect new numbers. You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
"BENEFITS": {
"TITLE": "Benefits of Embedded Signup:",
"EASY_SETUP": "No manual configuration required",
@@ -281,9 +281,8 @@
"AUTO_CONFIG": "Automatic webhook and phone number configuration"
},
"LEARN_MORE": {
- "TEXT": "To learn more about integrated signup, pricing, and limitations, visit",
- "LINK_TEXT": "this link.",
- "LINK_URL": "https://developers.facebook.com/docs/whatsapp/embedded-signup/custom-flows/onboarding-business-app-users#limitations"
+ "TEXT": "To learn more about integrated signup, pricing, and limitations, visit {link}.",
+ "LINK_TEXT": "this link"
},
"SUBMIT_BUTTON": "Connect with WhatsApp Business",
"AUTH_PROCESSING": "Authenticating with Meta",
@@ -296,7 +295,9 @@
"INVALID_BUSINESS_DATA": "Invalid business data received from Facebook. Please try again.",
"SIGNUP_ERROR": "Signup error occurred",
"AUTH_NOT_COMPLETED": "Authentication not completed. Please restart the process.",
- "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured"
+ "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured",
+ "MANUAL_FALLBACK": "If your number is already connected to the WhatsApp Business Platform (API), or if you’re a tech provider onboarding your own number, please use the {link} flow",
+ "MANUAL_LINK_TEXT": "manual setup flow"
},
"API": {
"ERROR_MESSAGE": "No hem pogut desar el canal WhatsApp"
@@ -424,7 +425,51 @@
},
"AUTH": {
"TITLE": "Escull un canal",
- "DESC": "Chatwoot admet widgets de xat en directe, Facebook Messenger, WhatsApp, correus electrònics, etc., com a canals. Si voleu crear un canal personalitzat, podeu crear-lo mitjançant el canal API. Per començar, tria un dels canals següents."
+ "DESC": "Chatwoot admet widgets de xat en directe, Facebook Messenger, WhatsApp, correus electrònics, etc., com a canals. Si voleu crear un canal personalitzat, podeu crear-lo mitjançant el canal API. Per començar, tria un dels canals següents.",
+ "TITLE_NEXT": "Complete the setup",
+ "TITLE_FINISH": "Voilà!",
+ "CHANNEL": {
+ "WEBSITE": {
+ "TITLE": "Lloc web",
+ "DESCRIPTION": "Create a live-chat widget"
+ },
+ "FACEBOOK": {
+ "TITLE": "Facebook",
+ "DESCRIPTION": "Connect your Facebook page"
+ },
+ "WHATSAPP": {
+ "TITLE": "WhatsApp",
+ "DESCRIPTION": "Support your customers on WhatsApp"
+ },
+ "EMAIL": {
+ "TITLE": "Correu electrònic",
+ "DESCRIPTION": "Connect with Gmail, Outlook, or other providers"
+ },
+ "SMS": {
+ "TITLE": "SMS",
+ "DESCRIPTION": "Integrate SMS channel with Twilio or bandwidth"
+ },
+ "API": {
+ "TITLE": "API",
+ "DESCRIPTION": "Make a custom channel using our API"
+ },
+ "TELEGRAM": {
+ "TITLE": "Telegram",
+ "DESCRIPTION": "Configure Telegram channel using Bot token"
+ },
+ "LINE": {
+ "TITLE": "Line",
+ "DESCRIPTION": "Integrate your Line channel"
+ },
+ "INSTAGRAM": {
+ "TITLE": "Instagram",
+ "DESCRIPTION": "Connect your instagram account"
+ },
+ "VOICE": {
+ "TITLE": "Voice",
+ "DESCRIPTION": "Integrate with Twilio Voice"
+ }
+ }
},
"AGENTS": {
"TITLE": "Agents",
@@ -478,7 +523,10 @@
"MESSAGE": "Ja podeu interactuar amb els vostres clients a través del vostre canal nou. Feliç suport",
"BUTTON_TEXT": "Porta'm allà",
"MORE_SETTINGS": "Més configuracions",
- "WEBSITE_SUCCESS": "Heu finalitzat amb èxit la creació d'un canal web. Copieu el codi que es mostra a continuació i enganxeu-lo al lloc web. La propera vegada que un client utilitzi el xat en directe, la conversa apareixerà automàticament a la safata d'entrada."
+ "WEBSITE_SUCCESS": "Heu finalitzat amb èxit la creació d'un canal web. Copieu el codi que es mostra a continuació i enganxeu-lo al lloc web. La propera vegada que un client utilitzi el xat en directe, la conversa apareixerà automàticament a la safata d'entrada.",
+ "WHATSAPP_QR_INSTRUCTION": "Scan the QR code above to quickly test your WhatsApp inbox",
+ "MESSENGER_QR_INSTRUCTION": "Scan the QR code above to quickly test your Facebook Messenger inbox",
+ "TELEGRAM_QR_INSTRUCTION": "Scan the QR code above to quickly test your Telegram inbox"
},
"REAUTH": "Reautoritza",
"VIEW": "Veure",
@@ -868,9 +916,18 @@
"SCRIPT_SETTINGS": "\n window.chatwootSettings = {options};"
},
"EMAIL_PROVIDERS": {
- "MICROSOFT": "Microsoft",
- "GOOGLE": "Google",
- "OTHER_PROVIDERS": "Altres proveïdors"
+ "MICROSOFT": {
+ "TITLE": "Microsoft",
+ "DESCRIPTION": "Connect with Microsoft"
+ },
+ "GOOGLE": {
+ "TITLE": "Google",
+ "DESCRIPTION": "Connect with Google"
+ },
+ "OTHER_PROVIDERS": {
+ "TITLE": "Altres proveïdors",
+ "DESCRIPTION": "Connect with Other Providers"
+ }
},
"CHANNELS": {
"MESSENGER": "Messenger",
diff --git a/app/javascript/dashboard/i18n/locale/ca/integrations.json b/app/javascript/dashboard/i18n/locale/ca/integrations.json
index 2668ce217..29d1d9546 100644
--- a/app/javascript/dashboard/i18n/locale/ca/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/ca/integrations.json
@@ -704,11 +704,28 @@
"ERROR_MESSAGE": "There was an error creating the document, please try again."
},
"FORM": {
+ "TYPE": {
+ "LABEL": "Document Type",
+ "URL": "URL",
+ "PDF": "PDF File"
+ },
"URL": {
"LABEL": "URL",
"PLACEHOLDER": "Enter the URL of the document",
"ERROR": "Please provide a valid URL for the document"
},
+ "PDF_FILE": {
+ "LABEL": "PDF File",
+ "CHOOSE_FILE": "Choose PDF file",
+ "ERROR": "Please select a PDF file",
+ "HELP_TEXT": "Maximum file size: 10MB",
+ "INVALID_TYPE": "Please select a valid PDF file",
+ "TOO_LARGE": "File size exceeds 10MB limit"
+ },
+ "NAME": {
+ "LABEL": "Document Name (Optional)",
+ "PLACEHOLDER": "Enter a name for the document"
+ },
"ASSISTANT": {
"LABEL": "Assistant",
"PLACEHOLDER": "Select the assistant",
@@ -744,6 +761,7 @@
"SELECTED": "{count} selected",
"SELECT_ALL": "Select all ({count})",
"UNSELECT_ALL": "Unselect all ({count})",
+ "SEARCH_PLACEHOLDER": "Search FAQs...",
"BULK_APPROVE_BUTTON": "Approve",
"BULK_DELETE_BUTTON": "Esborrar",
"BULK_APPROVE": {
diff --git a/app/javascript/dashboard/i18n/locale/ca/mfa.json b/app/javascript/dashboard/i18n/locale/ca/mfa.json
new file mode 100644
index 000000000..f3d135d40
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/ca/mfa.json
@@ -0,0 +1,106 @@
+{
+ "MFA_SETTINGS": {
+ "TITLE": "Two-Factor Authentication",
+ "SUBTITLE": "Secure your account with TOTP-based authentication",
+ "DESCRIPTION": "Add an extra layer of security to your account using a time-based one-time password (TOTP)",
+ "STATUS_TITLE": "Authentication Status",
+ "STATUS_DESCRIPTION": "Manage your two-factor authentication settings and backup recovery codes",
+ "ENABLED": "Habilita",
+ "DISABLED": "Inhabilita",
+ "STATUS_ENABLED": "Two-factor authentication is active",
+ "STATUS_ENABLED_DESC": "Your account is protected with an additional layer of security",
+ "ENABLE_BUTTON": "Enable Two-Factor Authentication",
+ "ENHANCE_SECURITY": "Enhance Your Account Security",
+ "ENHANCE_SECURITY_DESC": "Two-factor authentication adds an extra layer of security by requiring a verification code from your authenticator app in addition to your password.",
+ "SETUP": {
+ "STEP_NUMBER_1": "1",
+ "STEP_NUMBER_2": "2",
+ "STEP1_TITLE": "Scan QR Code with Your Authenticator App",
+ "STEP1_DESCRIPTION": "Use Google Authenticator, Authy, or any TOTP-compatible app",
+ "LOADING_QR": "Loading...",
+ "MANUAL_ENTRY": "Can't scan? Enter code manually",
+ "SECRET_KEY": "Secret Key",
+ "COPY": "Copia",
+ "ENTER_CODE": "Enter the 6-digit code from your authenticator app",
+ "ENTER_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify & Continue",
+ "CANCEL": "Cancel·la",
+ "ERROR_STARTING": "MFA not enabled. Please contact administrator.",
+ "INVALID_CODE": "Invalid verification code",
+ "SECRET_COPIED": "Secret key copied to clipboard",
+ "SUCCESS": "Two-factor authentication has been enabled successfully"
+ },
+ "BACKUP": {
+ "TITLE": "Save Your Backup Codes",
+ "DESCRIPTION": "Keep these codes safe. Each can be used once if you lose access to your authenticator",
+ "IMPORTANT": "Important:",
+ "IMPORTANT_NOTE": " Save these codes in a secure location. You won't be able to see them again.",
+ "DOWNLOAD": "Descarrega",
+ "COPY_ALL": "Copy All",
+ "CONFIRM": "I have saved my backup codes in a secure location and understand that I won't be able to see them again",
+ "COMPLETE_SETUP": "Complete Setup",
+ "CODES_COPIED": "Backup codes copied to clipboard"
+ },
+ "MANAGEMENT": {
+ "BACKUP_CODES": "Backup Codes",
+ "BACKUP_CODES_DESC": "Generate new codes if you've lost or used your existing ones",
+ "REGENERATE": "Regenerate Backup Codes",
+ "DISABLE_MFA": "Disable 2FA",
+ "DISABLE_MFA_DESC": "Remove two-factor authentication from your account",
+ "DISABLE_BUTTON": "Disable Two-Factor Authentication"
+ },
+ "DISABLE": {
+ "TITLE": "Disable Two-Factor Authentication",
+ "DESCRIPTION": "You'll need to enter your password and a verification code to disable two-factor authentication.",
+ "PASSWORD": "Contrasenya",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Disable 2FA",
+ "CANCEL": "Cancel·la",
+ "SUCCESS": "Two-factor authentication has been disabled",
+ "ERROR": "Failed to disable MFA. Please check your credentials."
+ },
+ "REGENERATE": {
+ "TITLE": "Regenerate Backup Codes",
+ "DESCRIPTION": "This will invalidate your existing backup codes and generate new ones. Enter your verification code to continue.",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Generate New Codes",
+ "CANCEL": "Cancel·la",
+ "NEW_CODES_TITLE": "New Backup Codes Generated",
+ "NEW_CODES_DESC": "Your old backup codes have been invalidated. Save these new codes in a secure location.",
+ "CODES_IMPORTANT": "Important:",
+ "CODES_IMPORTANT_NOTE": " Each code can only be used once. Save them before closing this window.",
+ "DOWNLOAD_CODES": "Download Codes",
+ "COPY_ALL_CODES": "Copy All Codes",
+ "CODES_SAVED": "I've Saved My Codes",
+ "SUCCESS": "New backup codes have been generated",
+ "ERROR": "Failed to regenerate backup codes"
+ }
+ },
+ "MFA_VERIFICATION": {
+ "TITLE": "Two-Factor Authentication",
+ "DESCRIPTION": "Enter your verification code to continue",
+ "AUTHENTICATOR_APP": "Authenticator App",
+ "BACKUP_CODE": "Backup Code",
+ "ENTER_OTP_CODE": "Enter 6-digit code from your authenticator app",
+ "ENTER_BACKUP_CODE": "Enter one of your backup codes",
+ "BACKUP_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify",
+ "TRY_ANOTHER_METHOD": "Try another verification method",
+ "CANCEL_LOGIN": "Cancel and return to login",
+ "HELP_TEXT": "Having trouble signing in?",
+ "LEARN_MORE": "Learn more about 2FA",
+ "HELP_MODAL": {
+ "TITLE": "Two-Factor Authentication Help",
+ "AUTHENTICATOR_TITLE": "Using an Authenticator App",
+ "AUTHENTICATOR_DESC": "Open your authenticator app (Google Authenticator, Authy, etc.) and enter the 6-digit code shown for your account.",
+ "BACKUP_TITLE": "Using a Backup Code",
+ "BACKUP_DESC": "If you don't have access to your authenticator app, you can use one of the backup codes you saved when setting up 2FA. Each code can only be used once.",
+ "CONTACT_TITLE": "Need More Help?",
+ "CONTACT_DESC_CLOUD": "If you've lost access to both your authenticator app and backup codes, please reach out to Chatwoot support for assistance.",
+ "CONTACT_DESC_SELF_HOSTED": "If you've lost access to both your authenticator app and backup codes, please contact your administrator for assistance."
+ },
+ "VERIFICATION_FAILED": "Verification failed. Please try again."
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/ca/settings.json b/app/javascript/dashboard/i18n/locale/ca/settings.json
index dbd5f5cad..f843b0b6d 100644
--- a/app/javascript/dashboard/i18n/locale/ca/settings.json
+++ b/app/javascript/dashboard/i18n/locale/ca/settings.json
@@ -51,6 +51,13 @@
"LARGER": "Larger",
"EXTRA_LARGE": "Extra Large"
}
+ },
+ "LANGUAGE": {
+ "TITLE": "Preferred Language",
+ "NOTE": "Choose the language you want to use.",
+ "UPDATE_SUCCESS": "Your Language settings have been updated successfully",
+ "UPDATE_ERROR": "There is an error while updating the language settings, please try again",
+ "USE_ACCOUNT_DEFAULT": "Use account default"
}
},
"MESSAGE_SIGNATURE_SECTION": {
@@ -73,6 +80,11 @@
"NOTE": "L'actualització de la contrasenya restableix els vostres inicis de sessió en múltiples dispositius.",
"BTN_TEXT": "Canvia la contrasenya"
},
+ "SECURITY_SECTION": {
+ "TITLE": "Security",
+ "NOTE": "Manage additional security features for your account.",
+ "MFA_BUTTON": "Manage Two-Factor Authentication"
+ },
"ACCESS_TOKEN": {
"TITLE": "Token d'accés",
"NOTE": "Aquest token es pot utilitzar si creeu una integració basada en l'API",
@@ -330,6 +342,7 @@
"REPORTS_LABEL": "Etiquetes",
"REPORTS_INBOX": "Safata d'entrada",
"REPORTS_TEAM": "Equip",
+ "AGENT_ASSIGNMENT": "Agent Assignment",
"SET_AVAILABILITY_TITLE": "Posa't com",
"SET_YOUR_AVAILABILITY": "Estableix la vostra disponibilitat",
"SLA": "SLA",
@@ -350,7 +363,8 @@
"INFO_TEXT": "Permet que el sistema et marqui automàticament fora de línia quan no facis servir l'aplicació o el tauler.",
"INFO_SHORT": "Automatically mark offline when you aren't using the app."
},
- "DOCS": "Llegir documents"
+ "DOCS": "Llegir documents",
+ "SECURITY": "Security"
},
"BILLING_SETTINGS": {
"TITLE": "Facturació",
@@ -382,6 +396,77 @@
},
"NO_BILLING_USER": "El teu compte de facturació s'està configurant. Actualitza la pàgina i torna-ho a provar."
},
+ "SECURITY_SETTINGS": {
+ "TITLE": "Security",
+ "DESCRIPTION": "Manage your account security settings.",
+ "LINK_TEXT": "Learn more about SAML SSO",
+ "SAML": {
+ "TITLE": "SAML SSO",
+ "NOTE": "Configure SAML single sign-on for your account. Users will authenticate through your identity provider instead of using email/password.",
+ "ACS_URL": {
+ "LABEL": "ACS URL",
+ "TOOLTIP": "Assertion Consumer Service URL - Configure this URL in your IdP as the destination for SAML responses"
+ },
+ "SSO_URL": {
+ "LABEL": "SSO URL",
+ "HELP": "The URL where SAML authentication requests will be sent",
+ "PLACEHOLDER": "https://your-idp.com/saml/sso"
+ },
+ "CERTIFICATE": {
+ "LABEL": "Signing certificate in PEM format",
+ "HELP": "The public certificate from your identity provider used to verify SAML responses",
+ "PLACEHOLDER": "-----BEGIN CERTIFICATE-----\nMIIC..."
+ },
+ "FINGERPRINT": {
+ "LABEL": "Fingerprint",
+ "TOOLTIP": "SHA-1 fingerprint of the certificate - Use this to verify the certificate in your IdP configuration"
+ },
+ "COPY_SUCCESS": "El codi s'ha copiat al porta-retalls amb èxit",
+ "SP_ENTITY_ID": {
+ "LABEL": "SP Entity ID",
+ "HELP": "Unique identifier for this application as a service provider (auto-generated).",
+ "TOOLTIP": "Unique identifier for Chatwoot as the Service Provider - Configure this in your IdP settings"
+ },
+ "IDP_ENTITY_ID": {
+ "LABEL": "Identity Provider Entity ID",
+ "HELP": "Unique identifier for your identity provider (usually found in IdP configuration)",
+ "PLACEHOLDER": "https://your-idp.com/saml"
+ },
+ "UPDATE_BUTTON": "Update SAML Settings",
+ "API": {
+ "SUCCESS": "SAML settings updated successfully",
+ "ERROR": "Failed to update SAML settings",
+ "ERROR_LOADING": "Failed to load SAML settings",
+ "DISABLED": "SAML settings disabled successfully"
+ },
+ "VALIDATION": {
+ "REQUIRED_FIELDS": "SSO URL, Identity Provider Entity ID, and Certificate are required fields",
+ "SSO_URL_ERROR": "Please enter a valid SSO URL",
+ "CERTIFICATE_ERROR": "Certificate is required",
+ "IDP_ENTITY_ID_ERROR": "Identity Provider Entity ID is required"
+ },
+ "ENTERPRISE_PAYWALL": {
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade to an Enterprise plan to access SAML single sign-on and other advanced security features.",
+ "ASK_ADMIN": "Posa't en contacte amb el vostre administrador per obtenir l'actualització."
+ },
+ "PAYWALL": {
+ "TITLE": "Upgrade to enable SAML SSO",
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade your plan to get access to SAML single sign-on and other advanced features.",
+ "UPGRADE_NOW": "Actualitza ara",
+ "CANCEL_ANYTIME": "Pots canviar o cancel·lar el teu pla en qualsevol moment"
+ },
+ "ATTRIBUTE_MAPPING": {
+ "TITLE": "SAML Attribute Setup",
+ "DESCRIPTION": "The following attribute mappings must be configured in your identity provider"
+ },
+ "INFO_SECTION": {
+ "TITLE": "Service Provider Information",
+ "TOOLTIP": "Copy these values and configure them in your Identity Provider to establish the SAML connection"
+ }
+ }
+ },
"CREATE_ACCOUNT": {
"NO_ACCOUNT_WARNING": "Uh oh! No hem trobat cap compte de Chatwoot. Crea un compte nou per continuar.",
"NEW_ACCOUNT": "Compte nou",
@@ -418,5 +503,255 @@
"SWITCH_TO_REPLY": "Canvia a Respon",
"TOGGLE_SNOOZE_DROPDOWN": "Commuta el menú desplegable de posposar"
}
+ },
+ "ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent assignment",
+ "DESCRIPTION": "Define policies to effectively manage workload and route conversations based on the needs of inboxes and agents. Learn more here"
+ },
+ "ASSIGNMENT_POLICY": {
+ "TITLE": "Assignment policy",
+ "DESCRIPTION": "Manage how conversations get assigned in inboxes.",
+ "FEATURES": [
+ "Assign by conversations evenly or by available capacity",
+ "Add fair distribution rules to avoid overloading any agent",
+ "Add inboxes to a policy - one policy per inbox"
+ ]
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "TITLE": "Agent capacity policy",
+ "DESCRIPTION": "Manage workload for agents.",
+ "FEATURES": [
+ "Define maximum conversations per inbox",
+ "Create exceptions based on labels and time",
+ "Add agents to a policy - one policy per agent"
+ ]
+ }
+ },
+ "AGENT_ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Assignment policy",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "ORDER": "Order",
+ "PRIORITY": "Prioritat",
+ "ACTIVE": "Actiu",
+ "INACTIVE": "Inactive",
+ "POPOVER": "Added inboxes",
+ "EDIT": "Edita"
+ },
+ "NO_RECORDS_FOUND": "No assignment policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create assignment policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy created successfully",
+ "ERROR_MESSAGE": "Failed to create assignment policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit assignment policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_INBOX_DIALOG": {
+ "TITLE": "Add inbox",
+ "DESCRIPTION": "{inboxName} inbox is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Cancel·la"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update assignment policy"
+ },
+ "INBOX_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Inbox added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Inbox removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove inbox from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Descripció:",
+ "PLACEHOLDER": "Introdueix la descripció"
+ },
+ "STATUS": {
+ "LABEL": "Estat:",
+ "PLACEHOLDER": "Selecciona l'estat",
+ "ACTIVE": "Policy is active",
+ "INACTIVE": "Policy is inactive"
+ },
+ "ASSIGNMENT_ORDER": {
+ "LABEL": "Assignment order",
+ "ROUND_ROBIN": {
+ "LABEL": "Round robin",
+ "DESCRIPTION": "Assign conversations evenly among agents."
+ },
+ "BALANCED": {
+ "LABEL": "Balanced",
+ "DESCRIPTION": "Assign conversations based on available capacity."
+ }
+ },
+ "ASSIGNMENT_PRIORITY": {
+ "LABEL": "Assignment priority",
+ "EARLIEST_CREATED": {
+ "LABEL": "Earliest created",
+ "DESCRIPTION": "The conversation that was created first gets assigned first."
+ },
+ "LONGEST_WAITING": {
+ "LABEL": "Longest waiting",
+ "DESCRIPTION": "The conversation waiting the longest gets assigned first."
+ }
+ },
+ "FAIR_DISTRIBUTION": {
+ "LABEL": "Fair distribution policy",
+ "DESCRIPTION": "Set the maximum number of conversations that can be assigned per agent within a time window to avoid overloading any one agent. This required field defaults to 100 conversations per hour.",
+ "INPUT_MAX": "Assign max",
+ "DURATION": "Conversations per agent in every"
+ },
+ "INBOXES": {
+ "LABEL": "Added inboxes",
+ "DESCRIPTION": "Add inboxes for which this policy will be applicable.",
+ "ADD_BUTTON": "Add inbox",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select inboxes to add",
+ "ADD_BUTTON": "Afegir"
+ },
+ "EMPTY_STATE": "No inboxes added to this policy, add an inbox to get started",
+ "API": {
+ "SUCCESS_MESSAGE": "Inbox successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Assignment policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete assignment policy"
+ }
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent capacity",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "POPOVER": "Added agents",
+ "EDIT": "Edita"
+ },
+ "NO_RECORDS_FOUND": "No agent capacity policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create agent capacity policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy created successfully",
+ "ERROR_MESSAGE": "Failed to create agent capacity policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit agent capacity policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_AGENT_DIALOG": {
+ "TITLE": "Add agent",
+ "DESCRIPTION": "{agentName} is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Cancel·la"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update agent capacity policy"
+ },
+ "AGENT_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Agent added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Agent removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove agent from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Descripció:",
+ "PLACEHOLDER": "Introdueix la descripció"
+ },
+ "INBOX_CAPACITY_LIMIT": {
+ "LABEL": "Inbox capacity limits",
+ "ADD_BUTTON": "Add inbox",
+ "FIELD": {
+ "SELECT_INBOX": "Select inbox",
+ "MAX_CONVERSATIONS": "Max conversations",
+ "SET_LIMIT": "Set limit"
+ },
+ "EMPTY_STATE": "No inbox limit set"
+ },
+ "EXCLUSION_RULES": {
+ "LABEL": "Exclusion rules",
+ "DESCRIPTION": "Conversations that satisfy the following conditions would not count towards agent capacity",
+ "TAGS": {
+ "LABEL": "Exclude conversations tagged with specific labels",
+ "ADD_TAG": "add tag",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select tags to add"
+ },
+ "EMPTY_STATE": "No tags added to this policy."
+ },
+ "DURATION": {
+ "LABEL": "Exclude conversations older than a specified duration",
+ "PLACEHOLDER": "Set time"
+ }
+ },
+ "USERS": {
+ "LABEL": "Assigned agents",
+ "DESCRIPTION": "Add agents for which this policy will be applicable.",
+ "ADD_BUTTON": "Add agent",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select agents to add",
+ "ADD_BUTTON": "Afegir"
+ },
+ "EMPTY_STATE": "No agents added",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Agent capacity policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete agent capacity policy"
+ }
+ },
+ "DELETE_POLICY": {
+ "TITLE": "Delete policy",
+ "DESCRIPTION": "Are you sure you want to delete this policy? This action cannot be undone.",
+ "CONFIRM_BUTTON_LABEL": "Esborrar",
+ "CANCEL_BUTTON_LABEL": "Cancel·la"
+ }
}
}
diff --git a/app/javascript/dashboard/i18n/locale/ca/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/ca/whatsappTemplates.json
index e0b132286..bd2910734 100644
--- a/app/javascript/dashboard/i18n/locale/ca/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/ca/whatsappTemplates.json
@@ -40,6 +40,7 @@
"BUTTON_LABEL": "Button {index}",
"COUPON_CODE": "Enter coupon code (max 15 chars)",
"MEDIA_URL_LABEL": "Enter {type} URL",
+ "DOCUMENT_NAME_PLACEHOLDER": "Enter document filename (e.g., Invoice_2025.pdf)",
"BUTTON_PARAMETER": "Enter button parameter"
}
}
diff --git a/app/javascript/dashboard/i18n/locale/cs/automation.json b/app/javascript/dashboard/i18n/locale/cs/automation.json
index 6733fa929..372a89c15 100644
--- a/app/javascript/dashboard/i18n/locale/cs/automation.json
+++ b/app/javascript/dashboard/i18n/locale/cs/automation.json
@@ -177,7 +177,8 @@
"REFERER_LINK": "Referrer Link",
"ASSIGNEE_NAME": "Assignee",
"TEAM_NAME": "Team",
- "PRIORITY": "Priority"
+ "PRIORITY": "Priority",
+ "LABELS": "Štítky"
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/cs/contact.json b/app/javascript/dashboard/i18n/locale/cs/contact.json
index abeffa589..c339338ad 100644
--- a/app/javascript/dashboard/i18n/locale/cs/contact.json
+++ b/app/javascript/dashboard/i18n/locale/cs/contact.json
@@ -610,6 +610,15 @@
"SEND_MESSAGE": "Send message"
}
},
+ "TWILIO_OPTIONS": {
+ "LABEL": "Select template",
+ "SEARCH_PLACEHOLDER": "Search templates",
+ "EMPTY_STATE": "No templates found",
+ "TEMPLATE_PARSER": {
+ "BACK": "Go back",
+ "SEND_MESSAGE": "Send message"
+ }
+ },
"ACTION_BUTTONS": {
"DISCARD": "Discard",
"SEND": "Send ({keyCode})"
diff --git a/app/javascript/dashboard/i18n/locale/cs/contactFilters.json b/app/javascript/dashboard/i18n/locale/cs/contactFilters.json
index 255ba36a6..715ff8a7a 100644
--- a/app/javascript/dashboard/i18n/locale/cs/contactFilters.json
+++ b/app/javascript/dashboard/i18n/locale/cs/contactFilters.json
@@ -48,7 +48,8 @@
"CREATED_AT": "Vytvořeno",
"LAST_ACTIVITY": "Poslední aktivita",
"REFERER_LINK": "Odkazující odkaz",
- "BLOCKED": "Blocked"
+ "BLOCKED": "Blocked",
+ "LABELS": "Štítky"
},
"GROUPS": {
"STANDARD_FILTERS": "Standardní filtry",
diff --git a/app/javascript/dashboard/i18n/locale/cs/contentTemplates.json b/app/javascript/dashboard/i18n/locale/cs/contentTemplates.json
new file mode 100644
index 000000000..f98c668d2
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/cs/contentTemplates.json
@@ -0,0 +1,51 @@
+{
+ "CONTENT_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Twilio Templates",
+ "SUBTITLE": "Select the Twilio template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Search Templates",
+ "NO_TEMPLATES_FOUND": "No templates found for",
+ "NO_CONTENT": "No content",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Category",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No Twilio templates available. Click refresh to sync templates from Twilio.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Language",
+ "TEMPLATE_BODY": "Template Body",
+ "CATEGORY": "Category"
+ },
+ "TYPES": {
+ "MEDIA": "Media",
+ "QUICK_REPLY": "Quick Reply",
+ "TEXT": "Text"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variables",
+ "LANGUAGE": "Language",
+ "CATEGORY": "Category",
+ "VARIABLE_PLACEHOLDER": "Enter {variable} value",
+ "GO_BACK_LABEL": "Go Back",
+ "SEND_MESSAGE_LABEL": "Send Message",
+ "FORM_ERROR_MESSAGE": "Please fill all variables before sending",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "MEDIA_URL_LABEL": "Enter full media URL",
+ "MEDIA_URL_PLACEHOLDER": "https://example.com/image.jpg"
+ },
+ "FORM": {
+ "BACK_BUTTON": "Zpět",
+ "SEND_MESSAGE_BUTTON": "Send Message"
+ }
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/cs/conversation.json b/app/javascript/dashboard/i18n/locale/cs/conversation.json
index c9ab4282a..12bea0321 100644
--- a/app/javascript/dashboard/i18n/locale/cs/conversation.json
+++ b/app/javascript/dashboard/i18n/locale/cs/conversation.json
@@ -35,6 +35,11 @@
"API_HOURS_WINDOW": "You can only reply to this conversation within {hours} hours",
"NOT_ASSIGNED_TO_YOU": "Tato konverzace vám není přiřazena. Chcete si přiřadit tuto konverzaci?",
"ASSIGN_TO_ME": "Přiřadit mi",
+ "BOT_HANDOFF_MESSAGE": "You are responding to a conversation which is currently handled by an assistant or a bot.",
+ "BOT_HANDOFF_ACTION": "Mark open and assign to you",
+ "BOT_HANDOFF_REOPEN_ACTION": "Mark conversation open",
+ "BOT_HANDOFF_SUCCESS": "Conversation has been handed over to you",
+ "BOT_HANDOFF_ERROR": "Failed to take over the conversation. Please try again.",
"TWILIO_WHATSAPP_CAN_REPLY": "Na tuto konverzaci můžete odpovědět pouze pomocí šablony zprávy z důvodu",
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "24 hodinové omezení okna",
"OLD_INSTAGRAM_INBOX_REPLY_BANNER": "This Instagram account was migrated to the new Instagram channel inbox. All new messages will show up there. You won’t be able to send messages from this conversation anymore.",
@@ -66,6 +71,17 @@
"SHOW_LABELS": "Show labels",
"HIDE_LABELS": "Hide labels"
},
+ "VOICE_CALL": {
+ "INCOMING_CALL": "Incoming call",
+ "OUTGOING_CALL": "Outgoing call",
+ "CALL_IN_PROGRESS": "Call in progress",
+ "NO_ANSWER": "No answer",
+ "MISSED_CALL": "Missed call",
+ "CALL_ENDED": "Call ended",
+ "NOT_ANSWERED_YET": "Not answered yet",
+ "THEY_ANSWERED": "They answered",
+ "YOU_ANSWERED": "You answered"
+ },
"HEADER": {
"RESOLVE_ACTION": "Vyřešit",
"REOPEN_ACTION": "Znovu otevřít",
diff --git a/app/javascript/dashboard/i18n/locale/cs/helpCenter.json b/app/javascript/dashboard/i18n/locale/cs/helpCenter.json
index a414a6773..eb059daec 100644
--- a/app/javascript/dashboard/i18n/locale/cs/helpCenter.json
+++ b/app/javascript/dashboard/i18n/locale/cs/helpCenter.json
@@ -741,7 +741,8 @@
"LIVE_CHAT_WIDGET": {
"LABEL": "Live chat widget",
"PLACEHOLDER": "Select live chat widget",
- "HELP_TEXT": "Select a live chat widget that will appear on your help center"
+ "HELP_TEXT": "Select a live chat widget that will appear on your help center",
+ "NONE_OPTION": "No widget"
},
"BRAND_COLOR": {
"LABEL": "Brand color"
@@ -807,6 +808,58 @@
"ERROR_MESSAGE": "Unable to update portal"
}
}
+ },
+ "PDF_UPLOAD": {
+ "TITLE": "Upload PDF Document",
+ "DESCRIPTION": "Upload a PDF document to automatically generate FAQs using AI",
+ "DRAG_DROP_TEXT": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "ADDITIONAL_CONTEXT_LABEL": "Additional Context (Optional)",
+ "ADDITIONAL_CONTEXT_PLACEHOLDER": "Provide any additional context or instructions for FAQ generation...",
+ "UPLOADING": "Nahrávání...",
+ "UPLOAD": "Upload & Process",
+ "CANCEL": "Zrušit",
+ "ERROR_INVALID_TYPE": "Please select a valid PDF file",
+ "ERROR_FILE_TOO_LARGE": "File size must be less than 512MB",
+ "ERROR_UPLOAD_FAILED": "Failed to upload PDF. Please try again."
+ },
+ "PDF_DOCUMENTS": {
+ "TITLE": "PDF Documents",
+ "DESCRIPTION": "Manage uploaded PDF documents and generate FAQs from them",
+ "UPLOAD_PDF": "Upload PDF",
+ "UPLOAD_FIRST_PDF": "Upload your first PDF",
+ "UPLOADED_BY": "Uploaded by",
+ "GENERATE_FAQS": "Generate FAQs",
+ "GENERATING": "Generating...",
+ "CONFIRM_DELETE": "Are you sure you want to delete {filename}?",
+ "EMPTY_STATE": {
+ "TITLE": "No PDF documents yet",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQs using AI"
+ },
+ "STATUS": {
+ "UPLOADED": "Ready",
+ "PROCESSING": "Processing",
+ "PROCESSED": "Completed",
+ "FAILED": "Failed"
+ }
+ },
+ "CONTENT_GENERATION": {
+ "TITLE": "Content Generation",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQ content using AI",
+ "UPLOAD_TITLE": "Upload PDF Document",
+ "DRAG_DROP": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "UPLOADING": "Processing document...",
+ "UPLOAD_SUCCESS": "Document processed successfully!",
+ "UPLOAD_ERROR": "Failed to upload document. Please try again.",
+ "INVALID_FILE_TYPE": "Please select a valid PDF file",
+ "FILE_TOO_LARGE": "File size must be less than 512MB",
+ "GENERATED_CONTENT": "Generated FAQ Content",
+ "PUBLISH_SELECTED": "Publish Selected",
+ "PUBLISHING": "Publishing...",
+ "FROM_DOCUMENT": "From document",
+ "NO_CONTENT": "No generated content available. Upload a PDF document to get started.",
+ "LOADING": "Loading generated content..."
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/cs/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/cs/inboxMgmt.json
index 8be8f7da5..e957efc50 100644
--- a/app/javascript/dashboard/i18n/locale/cs/inboxMgmt.json
+++ b/app/javascript/dashboard/i18n/locale/cs/inboxMgmt.json
@@ -272,8 +272,8 @@
},
"SUBMIT_BUTTON": "Create WhatsApp Channel",
"EMBEDDED_SIGNUP": {
- "TITLE": "Quick Setup with Meta",
- "DESC": "You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
+ "TITLE": "Quick setup with Meta",
+ "DESC": "Use the WhatsApp Embedded Signup flow to quickly connect new numbers. You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
"BENEFITS": {
"TITLE": "Benefits of Embedded Signup:",
"EASY_SETUP": "No manual configuration required",
@@ -281,9 +281,8 @@
"AUTO_CONFIG": "Automatic webhook and phone number configuration"
},
"LEARN_MORE": {
- "TEXT": "To learn more about integrated signup, pricing, and limitations, visit",
- "LINK_TEXT": "this link.",
- "LINK_URL": "https://developers.facebook.com/docs/whatsapp/embedded-signup/custom-flows/onboarding-business-app-users#limitations"
+ "TEXT": "To learn more about integrated signup, pricing, and limitations, visit {link}.",
+ "LINK_TEXT": "this link"
},
"SUBMIT_BUTTON": "Connect with WhatsApp Business",
"AUTH_PROCESSING": "Authenticating with Meta",
@@ -296,7 +295,9 @@
"INVALID_BUSINESS_DATA": "Invalid business data received from Facebook. Please try again.",
"SIGNUP_ERROR": "Signup error occurred",
"AUTH_NOT_COMPLETED": "Authentication not completed. Please restart the process.",
- "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured"
+ "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured",
+ "MANUAL_FALLBACK": "If your number is already connected to the WhatsApp Business Platform (API), or if you’re a tech provider onboarding your own number, please use the {link} flow",
+ "MANUAL_LINK_TEXT": "manual setup flow"
},
"API": {
"ERROR_MESSAGE": "We were not able to save the WhatsApp channel"
@@ -424,7 +425,51 @@
},
"AUTH": {
"TITLE": "Choose a channel",
- "DESC": "Chatwoot supports live-chat widgets, Facebook Messenger, Twitter profiles, WhatsApp, Emails, etc., as channels. If you want to build a custom channel, you can create it using the API channel. To get started, choose one of the channels below."
+ "DESC": "Chatwoot supports live-chat widgets, Facebook Messenger, Twitter profiles, WhatsApp, Emails, etc., as channels. If you want to build a custom channel, you can create it using the API channel. To get started, choose one of the channels below.",
+ "TITLE_NEXT": "Complete the setup",
+ "TITLE_FINISH": "Voilà!",
+ "CHANNEL": {
+ "WEBSITE": {
+ "TITLE": "Website",
+ "DESCRIPTION": "Create a live-chat widget"
+ },
+ "FACEBOOK": {
+ "TITLE": "Facebook",
+ "DESCRIPTION": "Connect your Facebook page"
+ },
+ "WHATSAPP": {
+ "TITLE": "WhatsApp",
+ "DESCRIPTION": "Support your customers on WhatsApp"
+ },
+ "EMAIL": {
+ "TITLE": "E-mailová adresa",
+ "DESCRIPTION": "Connect with Gmail, Outlook, or other providers"
+ },
+ "SMS": {
+ "TITLE": "SMS",
+ "DESCRIPTION": "Integrate SMS channel with Twilio or bandwidth"
+ },
+ "API": {
+ "TITLE": "API",
+ "DESCRIPTION": "Make a custom channel using our API"
+ },
+ "TELEGRAM": {
+ "TITLE": "Telegram",
+ "DESCRIPTION": "Configure Telegram channel using Bot token"
+ },
+ "LINE": {
+ "TITLE": "Line",
+ "DESCRIPTION": "Integrate your Line channel"
+ },
+ "INSTAGRAM": {
+ "TITLE": "Instagram",
+ "DESCRIPTION": "Connect your instagram account"
+ },
+ "VOICE": {
+ "TITLE": "Voice",
+ "DESCRIPTION": "Integrate with Twilio Voice"
+ }
+ }
},
"AGENTS": {
"TITLE": "Agenti",
@@ -478,7 +523,10 @@
"MESSAGE": "You can now engage with your customers through your new Channel. Happy supporting",
"BUTTON_TEXT": "Vezmi mě tam",
"MORE_SETTINGS": "More settings",
- "WEBSITE_SUCCESS": "Úspěšně jste dokončili vytvoření webového kanálu. Zkopírujte kód zobrazený níže a vložte jej na vaše webové stránky. Když zákazník příště použije živý chat, konverzace se automaticky objeví ve vaší doručené poště."
+ "WEBSITE_SUCCESS": "Úspěšně jste dokončili vytvoření webového kanálu. Zkopírujte kód zobrazený níže a vložte jej na vaše webové stránky. Když zákazník příště použije živý chat, konverzace se automaticky objeví ve vaší doručené poště.",
+ "WHATSAPP_QR_INSTRUCTION": "Scan the QR code above to quickly test your WhatsApp inbox",
+ "MESSENGER_QR_INSTRUCTION": "Scan the QR code above to quickly test your Facebook Messenger inbox",
+ "TELEGRAM_QR_INSTRUCTION": "Scan the QR code above to quickly test your Telegram inbox"
},
"REAUTH": "Znovu autorizovat",
"VIEW": "Zobrazit",
@@ -868,9 +916,18 @@
"SCRIPT_SETTINGS": "\n window.chatwootSettings = {options};"
},
"EMAIL_PROVIDERS": {
- "MICROSOFT": "Microsoft",
- "GOOGLE": "Google",
- "OTHER_PROVIDERS": "Other Providers"
+ "MICROSOFT": {
+ "TITLE": "Microsoft",
+ "DESCRIPTION": "Connect with Microsoft"
+ },
+ "GOOGLE": {
+ "TITLE": "Google",
+ "DESCRIPTION": "Connect with Google"
+ },
+ "OTHER_PROVIDERS": {
+ "TITLE": "Other Providers",
+ "DESCRIPTION": "Connect with Other Providers"
+ }
},
"CHANNELS": {
"MESSENGER": "Messenger",
diff --git a/app/javascript/dashboard/i18n/locale/cs/integrations.json b/app/javascript/dashboard/i18n/locale/cs/integrations.json
index 26bc11c0c..5480257cc 100644
--- a/app/javascript/dashboard/i18n/locale/cs/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/cs/integrations.json
@@ -704,11 +704,28 @@
"ERROR_MESSAGE": "There was an error creating the document, please try again."
},
"FORM": {
+ "TYPE": {
+ "LABEL": "Document Type",
+ "URL": "URL",
+ "PDF": "PDF File"
+ },
"URL": {
"LABEL": "URL",
"PLACEHOLDER": "Enter the URL of the document",
"ERROR": "Please provide a valid URL for the document"
},
+ "PDF_FILE": {
+ "LABEL": "PDF File",
+ "CHOOSE_FILE": "Choose PDF file",
+ "ERROR": "Please select a PDF file",
+ "HELP_TEXT": "Maximum file size: 10MB",
+ "INVALID_TYPE": "Please select a valid PDF file",
+ "TOO_LARGE": "File size exceeds 10MB limit"
+ },
+ "NAME": {
+ "LABEL": "Document Name (Optional)",
+ "PLACEHOLDER": "Enter a name for the document"
+ },
"ASSISTANT": {
"LABEL": "Assistant",
"PLACEHOLDER": "Select the assistant",
@@ -744,6 +761,7 @@
"SELECTED": "{count} selected",
"SELECT_ALL": "Select all ({count})",
"UNSELECT_ALL": "Unselect all ({count})",
+ "SEARCH_PLACEHOLDER": "Search FAQs...",
"BULK_APPROVE_BUTTON": "Approve",
"BULK_DELETE_BUTTON": "Vymazat",
"BULK_APPROVE": {
diff --git a/app/javascript/dashboard/i18n/locale/cs/mfa.json b/app/javascript/dashboard/i18n/locale/cs/mfa.json
new file mode 100644
index 000000000..7729b0c9a
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/cs/mfa.json
@@ -0,0 +1,106 @@
+{
+ "MFA_SETTINGS": {
+ "TITLE": "Two-Factor Authentication",
+ "SUBTITLE": "Secure your account with TOTP-based authentication",
+ "DESCRIPTION": "Add an extra layer of security to your account using a time-based one-time password (TOTP)",
+ "STATUS_TITLE": "Authentication Status",
+ "STATUS_DESCRIPTION": "Manage your two-factor authentication settings and backup recovery codes",
+ "ENABLED": "Povoleno",
+ "DISABLED": "Zakázáno",
+ "STATUS_ENABLED": "Two-factor authentication is active",
+ "STATUS_ENABLED_DESC": "Your account is protected with an additional layer of security",
+ "ENABLE_BUTTON": "Enable Two-Factor Authentication",
+ "ENHANCE_SECURITY": "Enhance Your Account Security",
+ "ENHANCE_SECURITY_DESC": "Two-factor authentication adds an extra layer of security by requiring a verification code from your authenticator app in addition to your password.",
+ "SETUP": {
+ "STEP_NUMBER_1": "1",
+ "STEP_NUMBER_2": "2",
+ "STEP1_TITLE": "Scan QR Code with Your Authenticator App",
+ "STEP1_DESCRIPTION": "Use Google Authenticator, Authy, or any TOTP-compatible app",
+ "LOADING_QR": "Loading...",
+ "MANUAL_ENTRY": "Can't scan? Enter code manually",
+ "SECRET_KEY": "Secret Key",
+ "COPY": "Kopírovat",
+ "ENTER_CODE": "Enter the 6-digit code from your authenticator app",
+ "ENTER_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify & Continue",
+ "CANCEL": "Zrušit",
+ "ERROR_STARTING": "MFA not enabled. Please contact administrator.",
+ "INVALID_CODE": "Invalid verification code",
+ "SECRET_COPIED": "Secret key copied to clipboard",
+ "SUCCESS": "Two-factor authentication has been enabled successfully"
+ },
+ "BACKUP": {
+ "TITLE": "Save Your Backup Codes",
+ "DESCRIPTION": "Keep these codes safe. Each can be used once if you lose access to your authenticator",
+ "IMPORTANT": "Important:",
+ "IMPORTANT_NOTE": " Save these codes in a secure location. You won't be able to see them again.",
+ "DOWNLOAD": "Stáhnout",
+ "COPY_ALL": "Copy All",
+ "CONFIRM": "I have saved my backup codes in a secure location and understand that I won't be able to see them again",
+ "COMPLETE_SETUP": "Complete Setup",
+ "CODES_COPIED": "Backup codes copied to clipboard"
+ },
+ "MANAGEMENT": {
+ "BACKUP_CODES": "Backup Codes",
+ "BACKUP_CODES_DESC": "Generate new codes if you've lost or used your existing ones",
+ "REGENERATE": "Regenerate Backup Codes",
+ "DISABLE_MFA": "Disable 2FA",
+ "DISABLE_MFA_DESC": "Remove two-factor authentication from your account",
+ "DISABLE_BUTTON": "Disable Two-Factor Authentication"
+ },
+ "DISABLE": {
+ "TITLE": "Disable Two-Factor Authentication",
+ "DESCRIPTION": "You'll need to enter your password and a verification code to disable two-factor authentication.",
+ "PASSWORD": "Heslo",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Disable 2FA",
+ "CANCEL": "Zrušit",
+ "SUCCESS": "Two-factor authentication has been disabled",
+ "ERROR": "Failed to disable MFA. Please check your credentials."
+ },
+ "REGENERATE": {
+ "TITLE": "Regenerate Backup Codes",
+ "DESCRIPTION": "This will invalidate your existing backup codes and generate new ones. Enter your verification code to continue.",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Generate New Codes",
+ "CANCEL": "Zrušit",
+ "NEW_CODES_TITLE": "New Backup Codes Generated",
+ "NEW_CODES_DESC": "Your old backup codes have been invalidated. Save these new codes in a secure location.",
+ "CODES_IMPORTANT": "Important:",
+ "CODES_IMPORTANT_NOTE": " Each code can only be used once. Save them before closing this window.",
+ "DOWNLOAD_CODES": "Download Codes",
+ "COPY_ALL_CODES": "Copy All Codes",
+ "CODES_SAVED": "I've Saved My Codes",
+ "SUCCESS": "New backup codes have been generated",
+ "ERROR": "Failed to regenerate backup codes"
+ }
+ },
+ "MFA_VERIFICATION": {
+ "TITLE": "Two-Factor Authentication",
+ "DESCRIPTION": "Enter your verification code to continue",
+ "AUTHENTICATOR_APP": "Authenticator App",
+ "BACKUP_CODE": "Backup Code",
+ "ENTER_OTP_CODE": "Enter 6-digit code from your authenticator app",
+ "ENTER_BACKUP_CODE": "Enter one of your backup codes",
+ "BACKUP_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify",
+ "TRY_ANOTHER_METHOD": "Try another verification method",
+ "CANCEL_LOGIN": "Cancel and return to login",
+ "HELP_TEXT": "Having trouble signing in?",
+ "LEARN_MORE": "Learn more about 2FA",
+ "HELP_MODAL": {
+ "TITLE": "Two-Factor Authentication Help",
+ "AUTHENTICATOR_TITLE": "Using an Authenticator App",
+ "AUTHENTICATOR_DESC": "Open your authenticator app (Google Authenticator, Authy, etc.) and enter the 6-digit code shown for your account.",
+ "BACKUP_TITLE": "Using a Backup Code",
+ "BACKUP_DESC": "If you don't have access to your authenticator app, you can use one of the backup codes you saved when setting up 2FA. Each code can only be used once.",
+ "CONTACT_TITLE": "Need More Help?",
+ "CONTACT_DESC_CLOUD": "If you've lost access to both your authenticator app and backup codes, please reach out to Chatwoot support for assistance.",
+ "CONTACT_DESC_SELF_HOSTED": "If you've lost access to both your authenticator app and backup codes, please contact your administrator for assistance."
+ },
+ "VERIFICATION_FAILED": "Verification failed. Please try again."
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/cs/settings.json b/app/javascript/dashboard/i18n/locale/cs/settings.json
index e143bc911..c0a307d56 100644
--- a/app/javascript/dashboard/i18n/locale/cs/settings.json
+++ b/app/javascript/dashboard/i18n/locale/cs/settings.json
@@ -51,6 +51,13 @@
"LARGER": "Larger",
"EXTRA_LARGE": "Extra Large"
}
+ },
+ "LANGUAGE": {
+ "TITLE": "Preferred Language",
+ "NOTE": "Choose the language you want to use.",
+ "UPDATE_SUCCESS": "Your Language settings have been updated successfully",
+ "UPDATE_ERROR": "There is an error while updating the language settings, please try again",
+ "USE_ACCOUNT_DEFAULT": "Use account default"
}
},
"MESSAGE_SIGNATURE_SECTION": {
@@ -73,6 +80,11 @@
"NOTE": "Aktualizace hesla by obnovila vaše přihlašovací údaje na více zařízeních.",
"BTN_TEXT": "Change password"
},
+ "SECURITY_SECTION": {
+ "TITLE": "Security",
+ "NOTE": "Manage additional security features for your account.",
+ "MFA_BUTTON": "Manage Two-Factor Authentication"
+ },
"ACCESS_TOKEN": {
"TITLE": "Přístupový token",
"NOTE": "Tento token může být použit při vytváření integrace založené na API",
@@ -330,6 +342,7 @@
"REPORTS_LABEL": "Štítky",
"REPORTS_INBOX": "Inbox",
"REPORTS_TEAM": "Team",
+ "AGENT_ASSIGNMENT": "Agent Assignment",
"SET_AVAILABILITY_TITLE": "Set yourself as",
"SET_YOUR_AVAILABILITY": "Nastavte svou dostupnost",
"SLA": "SLA",
@@ -350,7 +363,8 @@
"INFO_TEXT": "Let the system automatically mark you offline when you aren't using the app or dashboard.",
"INFO_SHORT": "Automatically mark offline when you aren't using the app."
},
- "DOCS": "Read docs"
+ "DOCS": "Read docs",
+ "SECURITY": "Security"
},
"BILLING_SETTINGS": {
"TITLE": "Billing",
@@ -382,6 +396,77 @@
},
"NO_BILLING_USER": "Your billing account is being configured. Please refresh the page and try again."
},
+ "SECURITY_SETTINGS": {
+ "TITLE": "Security",
+ "DESCRIPTION": "Manage your account security settings.",
+ "LINK_TEXT": "Learn more about SAML SSO",
+ "SAML": {
+ "TITLE": "SAML SSO",
+ "NOTE": "Configure SAML single sign-on for your account. Users will authenticate through your identity provider instead of using email/password.",
+ "ACS_URL": {
+ "LABEL": "ACS URL",
+ "TOOLTIP": "Assertion Consumer Service URL - Configure this URL in your IdP as the destination for SAML responses"
+ },
+ "SSO_URL": {
+ "LABEL": "SSO URL",
+ "HELP": "The URL where SAML authentication requests will be sent",
+ "PLACEHOLDER": "https://your-idp.com/saml/sso"
+ },
+ "CERTIFICATE": {
+ "LABEL": "Signing certificate in PEM format",
+ "HELP": "The public certificate from your identity provider used to verify SAML responses",
+ "PLACEHOLDER": "-----BEGIN CERTIFICATE-----\nMIIC..."
+ },
+ "FINGERPRINT": {
+ "LABEL": "Fingerprint",
+ "TOOLTIP": "SHA-1 fingerprint of the certificate - Use this to verify the certificate in your IdP configuration"
+ },
+ "COPY_SUCCESS": "Kód byl úspěšně zkopírován do schránky",
+ "SP_ENTITY_ID": {
+ "LABEL": "SP Entity ID",
+ "HELP": "Unique identifier for this application as a service provider (auto-generated).",
+ "TOOLTIP": "Unique identifier for Chatwoot as the Service Provider - Configure this in your IdP settings"
+ },
+ "IDP_ENTITY_ID": {
+ "LABEL": "Identity Provider Entity ID",
+ "HELP": "Unique identifier for your identity provider (usually found in IdP configuration)",
+ "PLACEHOLDER": "https://your-idp.com/saml"
+ },
+ "UPDATE_BUTTON": "Update SAML Settings",
+ "API": {
+ "SUCCESS": "SAML settings updated successfully",
+ "ERROR": "Failed to update SAML settings",
+ "ERROR_LOADING": "Failed to load SAML settings",
+ "DISABLED": "SAML settings disabled successfully"
+ },
+ "VALIDATION": {
+ "REQUIRED_FIELDS": "SSO URL, Identity Provider Entity ID, and Certificate are required fields",
+ "SSO_URL_ERROR": "Please enter a valid SSO URL",
+ "CERTIFICATE_ERROR": "Certificate is required",
+ "IDP_ENTITY_ID_ERROR": "Identity Provider Entity ID is required"
+ },
+ "ENTERPRISE_PAYWALL": {
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade to an Enterprise plan to access SAML single sign-on and other advanced security features.",
+ "ASK_ADMIN": "Please reach out to your administrator for the upgrade."
+ },
+ "PAYWALL": {
+ "TITLE": "Upgrade to enable SAML SSO",
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade your plan to get access to SAML single sign-on and other advanced features.",
+ "UPGRADE_NOW": "Upgrade now",
+ "CANCEL_ANYTIME": "You can change or cancel your plan anytime"
+ },
+ "ATTRIBUTE_MAPPING": {
+ "TITLE": "SAML Attribute Setup",
+ "DESCRIPTION": "The following attribute mappings must be configured in your identity provider"
+ },
+ "INFO_SECTION": {
+ "TITLE": "Service Provider Information",
+ "TOOLTIP": "Copy these values and configure them in your Identity Provider to establish the SAML connection"
+ }
+ }
+ },
"CREATE_ACCOUNT": {
"NO_ACCOUNT_WARNING": "Uh oh! We could not find any Chatwoot accounts. Please create a new account to continue.",
"NEW_ACCOUNT": "Nový účet",
@@ -418,5 +503,255 @@
"SWITCH_TO_REPLY": "Switch to Reply",
"TOGGLE_SNOOZE_DROPDOWN": "Toggle snooze dropdown"
}
+ },
+ "ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent assignment",
+ "DESCRIPTION": "Define policies to effectively manage workload and route conversations based on the needs of inboxes and agents. Learn more here"
+ },
+ "ASSIGNMENT_POLICY": {
+ "TITLE": "Assignment policy",
+ "DESCRIPTION": "Manage how conversations get assigned in inboxes.",
+ "FEATURES": [
+ "Assign by conversations evenly or by available capacity",
+ "Add fair distribution rules to avoid overloading any agent",
+ "Add inboxes to a policy - one policy per inbox"
+ ]
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "TITLE": "Agent capacity policy",
+ "DESCRIPTION": "Manage workload for agents.",
+ "FEATURES": [
+ "Define maximum conversations per inbox",
+ "Create exceptions based on labels and time",
+ "Add agents to a policy - one policy per agent"
+ ]
+ }
+ },
+ "AGENT_ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Assignment policy",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "ORDER": "Order",
+ "PRIORITY": "Priority",
+ "ACTIVE": "Active",
+ "INACTIVE": "Inactive",
+ "POPOVER": "Added inboxes",
+ "EDIT": "Upravit"
+ },
+ "NO_RECORDS_FOUND": "No assignment policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create assignment policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy created successfully",
+ "ERROR_MESSAGE": "Failed to create assignment policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit assignment policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_INBOX_DIALOG": {
+ "TITLE": "Add inbox",
+ "DESCRIPTION": "{inboxName} inbox is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Zrušit"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update assignment policy"
+ },
+ "INBOX_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Inbox added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Inbox removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove inbox from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Description:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "STATUS": {
+ "LABEL": "Stav:",
+ "PLACEHOLDER": "Select status",
+ "ACTIVE": "Policy is active",
+ "INACTIVE": "Policy is inactive"
+ },
+ "ASSIGNMENT_ORDER": {
+ "LABEL": "Assignment order",
+ "ROUND_ROBIN": {
+ "LABEL": "Round robin",
+ "DESCRIPTION": "Assign conversations evenly among agents."
+ },
+ "BALANCED": {
+ "LABEL": "Balanced",
+ "DESCRIPTION": "Assign conversations based on available capacity."
+ }
+ },
+ "ASSIGNMENT_PRIORITY": {
+ "LABEL": "Assignment priority",
+ "EARLIEST_CREATED": {
+ "LABEL": "Earliest created",
+ "DESCRIPTION": "The conversation that was created first gets assigned first."
+ },
+ "LONGEST_WAITING": {
+ "LABEL": "Longest waiting",
+ "DESCRIPTION": "The conversation waiting the longest gets assigned first."
+ }
+ },
+ "FAIR_DISTRIBUTION": {
+ "LABEL": "Fair distribution policy",
+ "DESCRIPTION": "Set the maximum number of conversations that can be assigned per agent within a time window to avoid overloading any one agent. This required field defaults to 100 conversations per hour.",
+ "INPUT_MAX": "Assign max",
+ "DURATION": "Conversations per agent in every"
+ },
+ "INBOXES": {
+ "LABEL": "Added inboxes",
+ "DESCRIPTION": "Add inboxes for which this policy will be applicable.",
+ "ADD_BUTTON": "Add inbox",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select inboxes to add",
+ "ADD_BUTTON": "Přidat"
+ },
+ "EMPTY_STATE": "No inboxes added to this policy, add an inbox to get started",
+ "API": {
+ "SUCCESS_MESSAGE": "Inbox successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Assignment policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete assignment policy"
+ }
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent capacity",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "POPOVER": "Added agents",
+ "EDIT": "Upravit"
+ },
+ "NO_RECORDS_FOUND": "No agent capacity policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create agent capacity policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy created successfully",
+ "ERROR_MESSAGE": "Failed to create agent capacity policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit agent capacity policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_AGENT_DIALOG": {
+ "TITLE": "Add agent",
+ "DESCRIPTION": "{agentName} is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Zrušit"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update agent capacity policy"
+ },
+ "AGENT_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Agent added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Agent removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove agent from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Description:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "INBOX_CAPACITY_LIMIT": {
+ "LABEL": "Inbox capacity limits",
+ "ADD_BUTTON": "Add inbox",
+ "FIELD": {
+ "SELECT_INBOX": "Select inbox",
+ "MAX_CONVERSATIONS": "Max conversations",
+ "SET_LIMIT": "Set limit"
+ },
+ "EMPTY_STATE": "No inbox limit set"
+ },
+ "EXCLUSION_RULES": {
+ "LABEL": "Exclusion rules",
+ "DESCRIPTION": "Conversations that satisfy the following conditions would not count towards agent capacity",
+ "TAGS": {
+ "LABEL": "Exclude conversations tagged with specific labels",
+ "ADD_TAG": "add tag",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select tags to add"
+ },
+ "EMPTY_STATE": "No tags added to this policy."
+ },
+ "DURATION": {
+ "LABEL": "Exclude conversations older than a specified duration",
+ "PLACEHOLDER": "Set time"
+ }
+ },
+ "USERS": {
+ "LABEL": "Assigned agents",
+ "DESCRIPTION": "Add agents for which this policy will be applicable.",
+ "ADD_BUTTON": "Add agent",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select agents to add",
+ "ADD_BUTTON": "Přidat"
+ },
+ "EMPTY_STATE": "No agents added",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Agent capacity policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete agent capacity policy"
+ }
+ },
+ "DELETE_POLICY": {
+ "TITLE": "Delete policy",
+ "DESCRIPTION": "Are you sure you want to delete this policy? This action cannot be undone.",
+ "CONFIRM_BUTTON_LABEL": "Vymazat",
+ "CANCEL_BUTTON_LABEL": "Zrušit"
+ }
}
}
diff --git a/app/javascript/dashboard/i18n/locale/cs/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/cs/whatsappTemplates.json
index 5f53faaa8..cf28312dc 100644
--- a/app/javascript/dashboard/i18n/locale/cs/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/cs/whatsappTemplates.json
@@ -40,6 +40,7 @@
"BUTTON_LABEL": "Button {index}",
"COUPON_CODE": "Enter coupon code (max 15 chars)",
"MEDIA_URL_LABEL": "Enter {type} URL",
+ "DOCUMENT_NAME_PLACEHOLDER": "Enter document filename (e.g., Invoice_2025.pdf)",
"BUTTON_PARAMETER": "Enter button parameter"
}
}
diff --git a/app/javascript/dashboard/i18n/locale/da/automation.json b/app/javascript/dashboard/i18n/locale/da/automation.json
index 372422cfc..833c7a0a4 100644
--- a/app/javascript/dashboard/i18n/locale/da/automation.json
+++ b/app/javascript/dashboard/i18n/locale/da/automation.json
@@ -177,7 +177,8 @@
"REFERER_LINK": "Referrer Link",
"ASSIGNEE_NAME": "Assignee",
"TEAM_NAME": "Team",
- "PRIORITY": "Priority"
+ "PRIORITY": "Priority",
+ "LABELS": "Etiketter"
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/da/contact.json b/app/javascript/dashboard/i18n/locale/da/contact.json
index f5cdcf1b8..25b040fa0 100644
--- a/app/javascript/dashboard/i18n/locale/da/contact.json
+++ b/app/javascript/dashboard/i18n/locale/da/contact.json
@@ -610,6 +610,15 @@
"SEND_MESSAGE": "Send besked"
}
},
+ "TWILIO_OPTIONS": {
+ "LABEL": "Select template",
+ "SEARCH_PLACEHOLDER": "Search templates",
+ "EMPTY_STATE": "No templates found",
+ "TEMPLATE_PARSER": {
+ "BACK": "Gå tilbage",
+ "SEND_MESSAGE": "Send besked"
+ }
+ },
"ACTION_BUTTONS": {
"DISCARD": "Discard",
"SEND": "Send ({keyCode})"
diff --git a/app/javascript/dashboard/i18n/locale/da/contactFilters.json b/app/javascript/dashboard/i18n/locale/da/contactFilters.json
index 87f8cecff..47167bda8 100644
--- a/app/javascript/dashboard/i18n/locale/da/contactFilters.json
+++ b/app/javascript/dashboard/i18n/locale/da/contactFilters.json
@@ -48,7 +48,8 @@
"CREATED_AT": "Oprettet Den",
"LAST_ACTIVITY": "Sidste Aktivitet",
"REFERER_LINK": "Link til reference",
- "BLOCKED": "Blocked"
+ "BLOCKED": "Blocked",
+ "LABELS": "Etiketter"
},
"GROUPS": {
"STANDARD_FILTERS": "Standard Filtre",
diff --git a/app/javascript/dashboard/i18n/locale/da/contentTemplates.json b/app/javascript/dashboard/i18n/locale/da/contentTemplates.json
new file mode 100644
index 000000000..398a507c6
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/da/contentTemplates.json
@@ -0,0 +1,51 @@
+{
+ "CONTENT_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Twilio Templates",
+ "SUBTITLE": "Select the Twilio template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Søg Skabeloner",
+ "NO_TEMPLATES_FOUND": "Ingen skabeloner fundet for",
+ "NO_CONTENT": "No content",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Kategori",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No Twilio templates available. Click refresh to sync templates from Twilio.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Sprog",
+ "TEMPLATE_BODY": "Skabelon Krop",
+ "CATEGORY": "Kategori"
+ },
+ "TYPES": {
+ "MEDIA": "Media",
+ "QUICK_REPLY": "Quick Reply",
+ "TEXT": "Tekst"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variabler",
+ "LANGUAGE": "Sprog",
+ "CATEGORY": "Kategori",
+ "VARIABLE_PLACEHOLDER": "Indtast {variable} værdi",
+ "GO_BACK_LABEL": "Gå Tilbage",
+ "SEND_MESSAGE_LABEL": "Send Besked",
+ "FORM_ERROR_MESSAGE": "Udfyld venligst alle variabler før afsendelse",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "MEDIA_URL_LABEL": "Enter full media URL",
+ "MEDIA_URL_PLACEHOLDER": "https://example.com/image.jpg"
+ },
+ "FORM": {
+ "BACK_BUTTON": "Tilbage",
+ "SEND_MESSAGE_BUTTON": "Send Besked"
+ }
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/da/conversation.json b/app/javascript/dashboard/i18n/locale/da/conversation.json
index 54de51b46..eb18255e7 100644
--- a/app/javascript/dashboard/i18n/locale/da/conversation.json
+++ b/app/javascript/dashboard/i18n/locale/da/conversation.json
@@ -35,6 +35,11 @@
"API_HOURS_WINDOW": "You can only reply to this conversation within {hours} hours",
"NOT_ASSIGNED_TO_YOU": "Denne samtale er ikke tildelt dig. Vil du tildele denne samtale til dig selv?",
"ASSIGN_TO_ME": "Tildel til mig",
+ "BOT_HANDOFF_MESSAGE": "You are responding to a conversation which is currently handled by an assistant or a bot.",
+ "BOT_HANDOFF_ACTION": "Mark open and assign to you",
+ "BOT_HANDOFF_REOPEN_ACTION": "Mark conversation open",
+ "BOT_HANDOFF_SUCCESS": "Conversation has been handed over to you",
+ "BOT_HANDOFF_ERROR": "Failed to take over the conversation. Please try again.",
"TWILIO_WHATSAPP_CAN_REPLY": "Du kan kun svare på denne samtale ved hjælp af en skabelon besked på grund af",
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "24 timers beskedvindue begrænsning",
"OLD_INSTAGRAM_INBOX_REPLY_BANNER": "This Instagram account was migrated to the new Instagram channel inbox. All new messages will show up there. You won’t be able to send messages from this conversation anymore.",
@@ -66,6 +71,17 @@
"SHOW_LABELS": "Vis etiketter",
"HIDE_LABELS": "Skjul etiketter"
},
+ "VOICE_CALL": {
+ "INCOMING_CALL": "Incoming call",
+ "OUTGOING_CALL": "Outgoing call",
+ "CALL_IN_PROGRESS": "Call in progress",
+ "NO_ANSWER": "No answer",
+ "MISSED_CALL": "Missed call",
+ "CALL_ENDED": "Call ended",
+ "NOT_ANSWERED_YET": "Not answered yet",
+ "THEY_ANSWERED": "They answered",
+ "YOU_ANSWERED": "You answered"
+ },
"HEADER": {
"RESOLVE_ACTION": "Løs",
"REOPEN_ACTION": "Genåben",
diff --git a/app/javascript/dashboard/i18n/locale/da/helpCenter.json b/app/javascript/dashboard/i18n/locale/da/helpCenter.json
index 0d9d71d63..4d7d68b9c 100644
--- a/app/javascript/dashboard/i18n/locale/da/helpCenter.json
+++ b/app/javascript/dashboard/i18n/locale/da/helpCenter.json
@@ -741,7 +741,8 @@
"LIVE_CHAT_WIDGET": {
"LABEL": "Live chat widget",
"PLACEHOLDER": "Select live chat widget",
- "HELP_TEXT": "Select a live chat widget that will appear on your help center"
+ "HELP_TEXT": "Select a live chat widget that will appear on your help center",
+ "NONE_OPTION": "No widget"
},
"BRAND_COLOR": {
"LABEL": "Brand color"
@@ -807,6 +808,58 @@
"ERROR_MESSAGE": "Unable to update portal"
}
}
+ },
+ "PDF_UPLOAD": {
+ "TITLE": "Upload PDF Document",
+ "DESCRIPTION": "Upload a PDF document to automatically generate FAQs using AI",
+ "DRAG_DROP_TEXT": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "ADDITIONAL_CONTEXT_LABEL": "Additional Context (Optional)",
+ "ADDITIONAL_CONTEXT_PLACEHOLDER": "Provide any additional context or instructions for FAQ generation...",
+ "UPLOADING": "Uploader...",
+ "UPLOAD": "Upload & Process",
+ "CANCEL": "Annuller",
+ "ERROR_INVALID_TYPE": "Please select a valid PDF file",
+ "ERROR_FILE_TOO_LARGE": "File size must be less than 512MB",
+ "ERROR_UPLOAD_FAILED": "Failed to upload PDF. Please try again."
+ },
+ "PDF_DOCUMENTS": {
+ "TITLE": "PDF Documents",
+ "DESCRIPTION": "Manage uploaded PDF documents and generate FAQs from them",
+ "UPLOAD_PDF": "Upload PDF",
+ "UPLOAD_FIRST_PDF": "Upload your first PDF",
+ "UPLOADED_BY": "Uploaded by",
+ "GENERATE_FAQS": "Generate FAQs",
+ "GENERATING": "Generating...",
+ "CONFIRM_DELETE": "Are you sure you want to delete {filename}?",
+ "EMPTY_STATE": {
+ "TITLE": "No PDF documents yet",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQs using AI"
+ },
+ "STATUS": {
+ "UPLOADED": "Ready",
+ "PROCESSING": "Processing",
+ "PROCESSED": "Afsluttet",
+ "FAILED": "Failed"
+ }
+ },
+ "CONTENT_GENERATION": {
+ "TITLE": "Content Generation",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQ content using AI",
+ "UPLOAD_TITLE": "Upload PDF Document",
+ "DRAG_DROP": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "UPLOADING": "Processing document...",
+ "UPLOAD_SUCCESS": "Document processed successfully!",
+ "UPLOAD_ERROR": "Failed to upload document. Please try again.",
+ "INVALID_FILE_TYPE": "Please select a valid PDF file",
+ "FILE_TOO_LARGE": "File size must be less than 512MB",
+ "GENERATED_CONTENT": "Generated FAQ Content",
+ "PUBLISH_SELECTED": "Publish Selected",
+ "PUBLISHING": "Publishing...",
+ "FROM_DOCUMENT": "From document",
+ "NO_CONTENT": "No generated content available. Upload a PDF document to get started.",
+ "LOADING": "Loading generated content..."
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/da/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/da/inboxMgmt.json
index ea84bf92c..88553086f 100644
--- a/app/javascript/dashboard/i18n/locale/da/inboxMgmt.json
+++ b/app/javascript/dashboard/i18n/locale/da/inboxMgmt.json
@@ -272,8 +272,8 @@
},
"SUBMIT_BUTTON": "Opret WhatsApp Kanal",
"EMBEDDED_SIGNUP": {
- "TITLE": "Quick Setup with Meta",
- "DESC": "You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
+ "TITLE": "Quick setup with Meta",
+ "DESC": "Use the WhatsApp Embedded Signup flow to quickly connect new numbers. You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
"BENEFITS": {
"TITLE": "Benefits of Embedded Signup:",
"EASY_SETUP": "No manual configuration required",
@@ -281,9 +281,8 @@
"AUTO_CONFIG": "Automatic webhook and phone number configuration"
},
"LEARN_MORE": {
- "TEXT": "To learn more about integrated signup, pricing, and limitations, visit",
- "LINK_TEXT": "this link.",
- "LINK_URL": "https://developers.facebook.com/docs/whatsapp/embedded-signup/custom-flows/onboarding-business-app-users#limitations"
+ "TEXT": "To learn more about integrated signup, pricing, and limitations, visit {link}.",
+ "LINK_TEXT": "this link"
},
"SUBMIT_BUTTON": "Connect with WhatsApp Business",
"AUTH_PROCESSING": "Authenticating with Meta",
@@ -296,7 +295,9 @@
"INVALID_BUSINESS_DATA": "Invalid business data received from Facebook. Please try again.",
"SIGNUP_ERROR": "Signup error occurred",
"AUTH_NOT_COMPLETED": "Authentication not completed. Please restart the process.",
- "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured"
+ "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured",
+ "MANUAL_FALLBACK": "If your number is already connected to the WhatsApp Business Platform (API), or if you’re a tech provider onboarding your own number, please use the {link} flow",
+ "MANUAL_LINK_TEXT": "manual setup flow"
},
"API": {
"ERROR_MESSAGE": "Vi kunne ikke gemme WhatsApp-kanalen"
@@ -424,7 +425,51 @@
},
"AUTH": {
"TITLE": "Vælg en kanal",
- "DESC": "Chatwoot understøtter live-chat-widgets, Facebook Messenger, Twitter-profiler, WhatsApp, E-mails, osv., som kanaler. Hvis du ønsker at bygge en brugerdefineret kanal, kan du oprette den ved hjælp af API-kanalen. For at komme i gang, vælg en af kanalerne nedenfor."
+ "DESC": "Chatwoot understøtter live-chat-widgets, Facebook Messenger, Twitter-profiler, WhatsApp, E-mails, osv., som kanaler. Hvis du ønsker at bygge en brugerdefineret kanal, kan du oprette den ved hjælp af API-kanalen. For at komme i gang, vælg en af kanalerne nedenfor.",
+ "TITLE_NEXT": "Complete the setup",
+ "TITLE_FINISH": "Voilà!",
+ "CHANNEL": {
+ "WEBSITE": {
+ "TITLE": "Website",
+ "DESCRIPTION": "Create a live-chat widget"
+ },
+ "FACEBOOK": {
+ "TITLE": "Facebook",
+ "DESCRIPTION": "Connect your Facebook page"
+ },
+ "WHATSAPP": {
+ "TITLE": "WhatsApp",
+ "DESCRIPTION": "Support your customers on WhatsApp"
+ },
+ "EMAIL": {
+ "TITLE": "E-mail",
+ "DESCRIPTION": "Connect with Gmail, Outlook, or other providers"
+ },
+ "SMS": {
+ "TITLE": "SMS",
+ "DESCRIPTION": "Integrate SMS channel with Twilio or bandwidth"
+ },
+ "API": {
+ "TITLE": "API",
+ "DESCRIPTION": "Make a custom channel using our API"
+ },
+ "TELEGRAM": {
+ "TITLE": "Telegram",
+ "DESCRIPTION": "Configure Telegram channel using Bot token"
+ },
+ "LINE": {
+ "TITLE": "Line",
+ "DESCRIPTION": "Integrate your Line channel"
+ },
+ "INSTAGRAM": {
+ "TITLE": "Instagram",
+ "DESCRIPTION": "Connect your instagram account"
+ },
+ "VOICE": {
+ "TITLE": "Voice",
+ "DESCRIPTION": "Integrate with Twilio Voice"
+ }
+ }
},
"AGENTS": {
"TITLE": "Agenter",
@@ -478,7 +523,10 @@
"MESSAGE": "Du kan nu engagere dig med dine kunder gennem din nye kanal. Glædelig supportering",
"BUTTON_TEXT": "Tag mig med dertil",
"MORE_SETTINGS": "Flere indstillinger",
- "WEBSITE_SUCCESS": "Du er færdig med at oprette en hjemmeside kanal. Kopier koden vist nedenfor og indsæt den på din hjemmeside. Næste gang en kunde bruger live chat, vil samtalen automatisk vises i din indbakke."
+ "WEBSITE_SUCCESS": "Du er færdig med at oprette en hjemmeside kanal. Kopier koden vist nedenfor og indsæt den på din hjemmeside. Næste gang en kunde bruger live chat, vil samtalen automatisk vises i din indbakke.",
+ "WHATSAPP_QR_INSTRUCTION": "Scan the QR code above to quickly test your WhatsApp inbox",
+ "MESSENGER_QR_INSTRUCTION": "Scan the QR code above to quickly test your Facebook Messenger inbox",
+ "TELEGRAM_QR_INSTRUCTION": "Scan the QR code above to quickly test your Telegram inbox"
},
"REAUTH": "Genautorisér",
"VIEW": "Vis",
@@ -868,9 +916,18 @@
"SCRIPT_SETTINGS": "\n window.chatwootSettings = {options};"
},
"EMAIL_PROVIDERS": {
- "MICROSOFT": "Microsoft",
- "GOOGLE": "Google",
- "OTHER_PROVIDERS": "Other Providers"
+ "MICROSOFT": {
+ "TITLE": "Microsoft",
+ "DESCRIPTION": "Connect with Microsoft"
+ },
+ "GOOGLE": {
+ "TITLE": "Google",
+ "DESCRIPTION": "Connect with Google"
+ },
+ "OTHER_PROVIDERS": {
+ "TITLE": "Other Providers",
+ "DESCRIPTION": "Connect with Other Providers"
+ }
},
"CHANNELS": {
"MESSENGER": "Messenger",
diff --git a/app/javascript/dashboard/i18n/locale/da/integrations.json b/app/javascript/dashboard/i18n/locale/da/integrations.json
index c4b498a3a..f0ee6e29f 100644
--- a/app/javascript/dashboard/i18n/locale/da/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/da/integrations.json
@@ -704,11 +704,28 @@
"ERROR_MESSAGE": "There was an error creating the document, please try again."
},
"FORM": {
+ "TYPE": {
+ "LABEL": "Document Type",
+ "URL": "URL",
+ "PDF": "PDF File"
+ },
"URL": {
"LABEL": "URL",
"PLACEHOLDER": "Enter the URL of the document",
"ERROR": "Please provide a valid URL for the document"
},
+ "PDF_FILE": {
+ "LABEL": "PDF File",
+ "CHOOSE_FILE": "Choose PDF file",
+ "ERROR": "Please select a PDF file",
+ "HELP_TEXT": "Maximum file size: 10MB",
+ "INVALID_TYPE": "Please select a valid PDF file",
+ "TOO_LARGE": "File size exceeds 10MB limit"
+ },
+ "NAME": {
+ "LABEL": "Document Name (Optional)",
+ "PLACEHOLDER": "Enter a name for the document"
+ },
"ASSISTANT": {
"LABEL": "Assistant",
"PLACEHOLDER": "Select the assistant",
@@ -744,6 +761,7 @@
"SELECTED": "{count} selected",
"SELECT_ALL": "Select all ({count})",
"UNSELECT_ALL": "Unselect all ({count})",
+ "SEARCH_PLACEHOLDER": "Search FAQs...",
"BULK_APPROVE_BUTTON": "Approve",
"BULK_DELETE_BUTTON": "Slet",
"BULK_APPROVE": {
diff --git a/app/javascript/dashboard/i18n/locale/da/mfa.json b/app/javascript/dashboard/i18n/locale/da/mfa.json
new file mode 100644
index 000000000..440c89657
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/da/mfa.json
@@ -0,0 +1,106 @@
+{
+ "MFA_SETTINGS": {
+ "TITLE": "Two-Factor Authentication",
+ "SUBTITLE": "Secure your account with TOTP-based authentication",
+ "DESCRIPTION": "Add an extra layer of security to your account using a time-based one-time password (TOTP)",
+ "STATUS_TITLE": "Authentication Status",
+ "STATUS_DESCRIPTION": "Manage your two-factor authentication settings and backup recovery codes",
+ "ENABLED": "Aktiveret",
+ "DISABLED": "Deaktiveret",
+ "STATUS_ENABLED": "Two-factor authentication is active",
+ "STATUS_ENABLED_DESC": "Your account is protected with an additional layer of security",
+ "ENABLE_BUTTON": "Enable Two-Factor Authentication",
+ "ENHANCE_SECURITY": "Enhance Your Account Security",
+ "ENHANCE_SECURITY_DESC": "Two-factor authentication adds an extra layer of security by requiring a verification code from your authenticator app in addition to your password.",
+ "SETUP": {
+ "STEP_NUMBER_1": "1",
+ "STEP_NUMBER_2": "2",
+ "STEP1_TITLE": "Scan QR Code with Your Authenticator App",
+ "STEP1_DESCRIPTION": "Use Google Authenticator, Authy, or any TOTP-compatible app",
+ "LOADING_QR": "Loading...",
+ "MANUAL_ENTRY": "Can't scan? Enter code manually",
+ "SECRET_KEY": "Secret Key",
+ "COPY": "Kopiér",
+ "ENTER_CODE": "Enter the 6-digit code from your authenticator app",
+ "ENTER_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify & Continue",
+ "CANCEL": "Annuller",
+ "ERROR_STARTING": "MFA not enabled. Please contact administrator.",
+ "INVALID_CODE": "Invalid verification code",
+ "SECRET_COPIED": "Secret key copied to clipboard",
+ "SUCCESS": "Two-factor authentication has been enabled successfully"
+ },
+ "BACKUP": {
+ "TITLE": "Save Your Backup Codes",
+ "DESCRIPTION": "Keep these codes safe. Each can be used once if you lose access to your authenticator",
+ "IMPORTANT": "Important:",
+ "IMPORTANT_NOTE": " Save these codes in a secure location. You won't be able to see them again.",
+ "DOWNLOAD": "Download",
+ "COPY_ALL": "Copy All",
+ "CONFIRM": "I have saved my backup codes in a secure location and understand that I won't be able to see them again",
+ "COMPLETE_SETUP": "Complete Setup",
+ "CODES_COPIED": "Backup codes copied to clipboard"
+ },
+ "MANAGEMENT": {
+ "BACKUP_CODES": "Backup Codes",
+ "BACKUP_CODES_DESC": "Generate new codes if you've lost or used your existing ones",
+ "REGENERATE": "Regenerate Backup Codes",
+ "DISABLE_MFA": "Disable 2FA",
+ "DISABLE_MFA_DESC": "Remove two-factor authentication from your account",
+ "DISABLE_BUTTON": "Disable Two-Factor Authentication"
+ },
+ "DISABLE": {
+ "TITLE": "Disable Two-Factor Authentication",
+ "DESCRIPTION": "You'll need to enter your password and a verification code to disable two-factor authentication.",
+ "PASSWORD": "Adgangskode",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Disable 2FA",
+ "CANCEL": "Annuller",
+ "SUCCESS": "Two-factor authentication has been disabled",
+ "ERROR": "Failed to disable MFA. Please check your credentials."
+ },
+ "REGENERATE": {
+ "TITLE": "Regenerate Backup Codes",
+ "DESCRIPTION": "This will invalidate your existing backup codes and generate new ones. Enter your verification code to continue.",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Generate New Codes",
+ "CANCEL": "Annuller",
+ "NEW_CODES_TITLE": "New Backup Codes Generated",
+ "NEW_CODES_DESC": "Your old backup codes have been invalidated. Save these new codes in a secure location.",
+ "CODES_IMPORTANT": "Important:",
+ "CODES_IMPORTANT_NOTE": " Each code can only be used once. Save them before closing this window.",
+ "DOWNLOAD_CODES": "Download Codes",
+ "COPY_ALL_CODES": "Copy All Codes",
+ "CODES_SAVED": "I've Saved My Codes",
+ "SUCCESS": "New backup codes have been generated",
+ "ERROR": "Failed to regenerate backup codes"
+ }
+ },
+ "MFA_VERIFICATION": {
+ "TITLE": "Two-Factor Authentication",
+ "DESCRIPTION": "Enter your verification code to continue",
+ "AUTHENTICATOR_APP": "Authenticator App",
+ "BACKUP_CODE": "Backup Code",
+ "ENTER_OTP_CODE": "Enter 6-digit code from your authenticator app",
+ "ENTER_BACKUP_CODE": "Enter one of your backup codes",
+ "BACKUP_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify",
+ "TRY_ANOTHER_METHOD": "Try another verification method",
+ "CANCEL_LOGIN": "Cancel and return to login",
+ "HELP_TEXT": "Having trouble signing in?",
+ "LEARN_MORE": "Learn more about 2FA",
+ "HELP_MODAL": {
+ "TITLE": "Two-Factor Authentication Help",
+ "AUTHENTICATOR_TITLE": "Using an Authenticator App",
+ "AUTHENTICATOR_DESC": "Open your authenticator app (Google Authenticator, Authy, etc.) and enter the 6-digit code shown for your account.",
+ "BACKUP_TITLE": "Using a Backup Code",
+ "BACKUP_DESC": "If you don't have access to your authenticator app, you can use one of the backup codes you saved when setting up 2FA. Each code can only be used once.",
+ "CONTACT_TITLE": "Need More Help?",
+ "CONTACT_DESC_CLOUD": "If you've lost access to both your authenticator app and backup codes, please reach out to Chatwoot support for assistance.",
+ "CONTACT_DESC_SELF_HOSTED": "If you've lost access to both your authenticator app and backup codes, please contact your administrator for assistance."
+ },
+ "VERIFICATION_FAILED": "Verification failed. Please try again."
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/da/settings.json b/app/javascript/dashboard/i18n/locale/da/settings.json
index e1a930e5b..3c312894d 100644
--- a/app/javascript/dashboard/i18n/locale/da/settings.json
+++ b/app/javascript/dashboard/i18n/locale/da/settings.json
@@ -51,6 +51,13 @@
"LARGER": "Larger",
"EXTRA_LARGE": "Extra Large"
}
+ },
+ "LANGUAGE": {
+ "TITLE": "Preferred Language",
+ "NOTE": "Choose the language you want to use.",
+ "UPDATE_SUCCESS": "Your Language settings have been updated successfully",
+ "UPDATE_ERROR": "There is an error while updating the language settings, please try again",
+ "USE_ACCOUNT_DEFAULT": "Use account default"
}
},
"MESSAGE_SIGNATURE_SECTION": {
@@ -73,6 +80,11 @@
"NOTE": "Opdatering af din adgangskode vil nulstille dine logins på flere enheder.",
"BTN_TEXT": "Skift adgangskode"
},
+ "SECURITY_SECTION": {
+ "TITLE": "Security",
+ "NOTE": "Manage additional security features for your account.",
+ "MFA_BUTTON": "Manage Two-Factor Authentication"
+ },
"ACCESS_TOKEN": {
"TITLE": "Adgangs Token",
"NOTE": "Denne token kan bruges, hvis du bygger en API-baseret integration",
@@ -330,6 +342,7 @@
"REPORTS_LABEL": "Etiketter",
"REPORTS_INBOX": "Indbakke",
"REPORTS_TEAM": "Team",
+ "AGENT_ASSIGNMENT": "Agent Assignment",
"SET_AVAILABILITY_TITLE": "Sæt dig selv som",
"SET_YOUR_AVAILABILITY": "Indstil din tilgængelighed",
"SLA": "SLA",
@@ -350,7 +363,8 @@
"INFO_TEXT": "Let the system automatically mark you offline when you aren't using the app or dashboard.",
"INFO_SHORT": "Automatically mark offline when you aren't using the app."
},
- "DOCS": "Læs dokumenter"
+ "DOCS": "Læs dokumenter",
+ "SECURITY": "Security"
},
"BILLING_SETTINGS": {
"TITLE": "Fakturering",
@@ -382,6 +396,77 @@
},
"NO_BILLING_USER": "Din faktureringskonto er ved at blive konfigureret. Opdater venligst siden og prøv igen."
},
+ "SECURITY_SETTINGS": {
+ "TITLE": "Security",
+ "DESCRIPTION": "Manage your account security settings.",
+ "LINK_TEXT": "Learn more about SAML SSO",
+ "SAML": {
+ "TITLE": "SAML SSO",
+ "NOTE": "Configure SAML single sign-on for your account. Users will authenticate through your identity provider instead of using email/password.",
+ "ACS_URL": {
+ "LABEL": "ACS URL",
+ "TOOLTIP": "Assertion Consumer Service URL - Configure this URL in your IdP as the destination for SAML responses"
+ },
+ "SSO_URL": {
+ "LABEL": "SSO URL",
+ "HELP": "The URL where SAML authentication requests will be sent",
+ "PLACEHOLDER": "https://your-idp.com/saml/sso"
+ },
+ "CERTIFICATE": {
+ "LABEL": "Signing certificate in PEM format",
+ "HELP": "The public certificate from your identity provider used to verify SAML responses",
+ "PLACEHOLDER": "-----BEGIN CERTIFICATE-----\nMIIC..."
+ },
+ "FINGERPRINT": {
+ "LABEL": "Fingerprint",
+ "TOOLTIP": "SHA-1 fingerprint of the certificate - Use this to verify the certificate in your IdP configuration"
+ },
+ "COPY_SUCCESS": "Kode kopieret til udklipsholder med succes",
+ "SP_ENTITY_ID": {
+ "LABEL": "SP Entity ID",
+ "HELP": "Unique identifier for this application as a service provider (auto-generated).",
+ "TOOLTIP": "Unique identifier for Chatwoot as the Service Provider - Configure this in your IdP settings"
+ },
+ "IDP_ENTITY_ID": {
+ "LABEL": "Identity Provider Entity ID",
+ "HELP": "Unique identifier for your identity provider (usually found in IdP configuration)",
+ "PLACEHOLDER": "https://your-idp.com/saml"
+ },
+ "UPDATE_BUTTON": "Update SAML Settings",
+ "API": {
+ "SUCCESS": "SAML settings updated successfully",
+ "ERROR": "Failed to update SAML settings",
+ "ERROR_LOADING": "Failed to load SAML settings",
+ "DISABLED": "SAML settings disabled successfully"
+ },
+ "VALIDATION": {
+ "REQUIRED_FIELDS": "SSO URL, Identity Provider Entity ID, and Certificate are required fields",
+ "SSO_URL_ERROR": "Please enter a valid SSO URL",
+ "CERTIFICATE_ERROR": "Certificate is required",
+ "IDP_ENTITY_ID_ERROR": "Identity Provider Entity ID is required"
+ },
+ "ENTERPRISE_PAYWALL": {
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade to an Enterprise plan to access SAML single sign-on and other advanced security features.",
+ "ASK_ADMIN": "Please reach out to your administrator for the upgrade."
+ },
+ "PAYWALL": {
+ "TITLE": "Upgrade to enable SAML SSO",
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade your plan to get access to SAML single sign-on and other advanced features.",
+ "UPGRADE_NOW": "Upgrade now",
+ "CANCEL_ANYTIME": "You can change or cancel your plan anytime"
+ },
+ "ATTRIBUTE_MAPPING": {
+ "TITLE": "SAML Attribute Setup",
+ "DESCRIPTION": "The following attribute mappings must be configured in your identity provider"
+ },
+ "INFO_SECTION": {
+ "TITLE": "Service Provider Information",
+ "TOOLTIP": "Copy these values and configure them in your Identity Provider to establish the SAML connection"
+ }
+ }
+ },
"CREATE_ACCOUNT": {
"NO_ACCOUNT_WARNING": "Uh oh! Vi kunne ikke finde nogen Chatwoot-konti. Opret venligst en ny konto for at fortsætte.",
"NEW_ACCOUNT": "Ny Konto",
@@ -418,5 +503,255 @@
"SWITCH_TO_REPLY": "Skift til svar",
"TOGGLE_SNOOZE_DROPDOWN": "Skift snooze dropdown"
}
+ },
+ "ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent assignment",
+ "DESCRIPTION": "Define policies to effectively manage workload and route conversations based on the needs of inboxes and agents. Learn more here"
+ },
+ "ASSIGNMENT_POLICY": {
+ "TITLE": "Assignment policy",
+ "DESCRIPTION": "Manage how conversations get assigned in inboxes.",
+ "FEATURES": [
+ "Assign by conversations evenly or by available capacity",
+ "Add fair distribution rules to avoid overloading any agent",
+ "Add inboxes to a policy - one policy per inbox"
+ ]
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "TITLE": "Agent capacity policy",
+ "DESCRIPTION": "Manage workload for agents.",
+ "FEATURES": [
+ "Define maximum conversations per inbox",
+ "Create exceptions based on labels and time",
+ "Add agents to a policy - one policy per agent"
+ ]
+ }
+ },
+ "AGENT_ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Assignment policy",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "ORDER": "Order",
+ "PRIORITY": "Priority",
+ "ACTIVE": "Aktiv",
+ "INACTIVE": "Inactive",
+ "POPOVER": "Added inboxes",
+ "EDIT": "Rediger"
+ },
+ "NO_RECORDS_FOUND": "No assignment policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create assignment policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy created successfully",
+ "ERROR_MESSAGE": "Failed to create assignment policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit assignment policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_INBOX_DIALOG": {
+ "TITLE": "Add inbox",
+ "DESCRIPTION": "{inboxName} inbox is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Annuller"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update assignment policy"
+ },
+ "INBOX_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Inbox added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Inbox removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove inbox from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Beskrivelse:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "STATUS": {
+ "LABEL": "Status:",
+ "PLACEHOLDER": "Select status",
+ "ACTIVE": "Policy is active",
+ "INACTIVE": "Policy is inactive"
+ },
+ "ASSIGNMENT_ORDER": {
+ "LABEL": "Assignment order",
+ "ROUND_ROBIN": {
+ "LABEL": "Round robin",
+ "DESCRIPTION": "Assign conversations evenly among agents."
+ },
+ "BALANCED": {
+ "LABEL": "Balanced",
+ "DESCRIPTION": "Assign conversations based on available capacity."
+ }
+ },
+ "ASSIGNMENT_PRIORITY": {
+ "LABEL": "Assignment priority",
+ "EARLIEST_CREATED": {
+ "LABEL": "Earliest created",
+ "DESCRIPTION": "The conversation that was created first gets assigned first."
+ },
+ "LONGEST_WAITING": {
+ "LABEL": "Longest waiting",
+ "DESCRIPTION": "The conversation waiting the longest gets assigned first."
+ }
+ },
+ "FAIR_DISTRIBUTION": {
+ "LABEL": "Fair distribution policy",
+ "DESCRIPTION": "Set the maximum number of conversations that can be assigned per agent within a time window to avoid overloading any one agent. This required field defaults to 100 conversations per hour.",
+ "INPUT_MAX": "Assign max",
+ "DURATION": "Conversations per agent in every"
+ },
+ "INBOXES": {
+ "LABEL": "Added inboxes",
+ "DESCRIPTION": "Add inboxes for which this policy will be applicable.",
+ "ADD_BUTTON": "Add inbox",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select inboxes to add",
+ "ADD_BUTTON": "Tilføj"
+ },
+ "EMPTY_STATE": "No inboxes added to this policy, add an inbox to get started",
+ "API": {
+ "SUCCESS_MESSAGE": "Inbox successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Assignment policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete assignment policy"
+ }
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent capacity",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "POPOVER": "Added agents",
+ "EDIT": "Rediger"
+ },
+ "NO_RECORDS_FOUND": "No agent capacity policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create agent capacity policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy created successfully",
+ "ERROR_MESSAGE": "Failed to create agent capacity policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit agent capacity policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_AGENT_DIALOG": {
+ "TITLE": "Add agent",
+ "DESCRIPTION": "{agentName} is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Annuller"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update agent capacity policy"
+ },
+ "AGENT_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Agent added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Agent removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove agent from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Beskrivelse:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "INBOX_CAPACITY_LIMIT": {
+ "LABEL": "Inbox capacity limits",
+ "ADD_BUTTON": "Add inbox",
+ "FIELD": {
+ "SELECT_INBOX": "Select inbox",
+ "MAX_CONVERSATIONS": "Max conversations",
+ "SET_LIMIT": "Set limit"
+ },
+ "EMPTY_STATE": "No inbox limit set"
+ },
+ "EXCLUSION_RULES": {
+ "LABEL": "Exclusion rules",
+ "DESCRIPTION": "Conversations that satisfy the following conditions would not count towards agent capacity",
+ "TAGS": {
+ "LABEL": "Exclude conversations tagged with specific labels",
+ "ADD_TAG": "add tag",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select tags to add"
+ },
+ "EMPTY_STATE": "No tags added to this policy."
+ },
+ "DURATION": {
+ "LABEL": "Exclude conversations older than a specified duration",
+ "PLACEHOLDER": "Set time"
+ }
+ },
+ "USERS": {
+ "LABEL": "Assigned agents",
+ "DESCRIPTION": "Add agents for which this policy will be applicable.",
+ "ADD_BUTTON": "Add agent",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select agents to add",
+ "ADD_BUTTON": "Tilføj"
+ },
+ "EMPTY_STATE": "No agents added",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Agent capacity policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete agent capacity policy"
+ }
+ },
+ "DELETE_POLICY": {
+ "TITLE": "Delete policy",
+ "DESCRIPTION": "Are you sure you want to delete this policy? This action cannot be undone.",
+ "CONFIRM_BUTTON_LABEL": "Slet",
+ "CANCEL_BUTTON_LABEL": "Annuller"
+ }
}
}
diff --git a/app/javascript/dashboard/i18n/locale/da/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/da/whatsappTemplates.json
index 34699398c..a964730aa 100644
--- a/app/javascript/dashboard/i18n/locale/da/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/da/whatsappTemplates.json
@@ -40,6 +40,7 @@
"BUTTON_LABEL": "Button {index}",
"COUPON_CODE": "Enter coupon code (max 15 chars)",
"MEDIA_URL_LABEL": "Enter {type} URL",
+ "DOCUMENT_NAME_PLACEHOLDER": "Enter document filename (e.g., Invoice_2025.pdf)",
"BUTTON_PARAMETER": "Enter button parameter"
}
}
diff --git a/app/javascript/dashboard/i18n/locale/de/automation.json b/app/javascript/dashboard/i18n/locale/de/automation.json
index b6cef93e1..b93b39656 100644
--- a/app/javascript/dashboard/i18n/locale/de/automation.json
+++ b/app/javascript/dashboard/i18n/locale/de/automation.json
@@ -177,7 +177,8 @@
"REFERER_LINK": "Referrer Link",
"ASSIGNEE_NAME": "Zugewiesener",
"TEAM_NAME": "Team",
- "PRIORITY": "Priorität"
+ "PRIORITY": "Priorität",
+ "LABELS": "Labels"
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/de/contact.json b/app/javascript/dashboard/i18n/locale/de/contact.json
index 124ed9caa..1424656ee 100644
--- a/app/javascript/dashboard/i18n/locale/de/contact.json
+++ b/app/javascript/dashboard/i18n/locale/de/contact.json
@@ -17,7 +17,7 @@
"IP_ADDRESS": "IP-Adresse",
"CREATED_AT_LABEL": "Erstellt",
"NEW_MESSAGE": "Neue Nachricht",
- "CALL": "Call",
+ "CALL": "Anruf",
"CALL_UNDER_DEVELOPMENT": "Calling is under development",
"VOICE_INBOX_PICKER": {
"TITLE": "Choose a voice inbox"
@@ -610,6 +610,15 @@
"SEND_MESSAGE": "Nachricht senden"
}
},
+ "TWILIO_OPTIONS": {
+ "LABEL": "Vorlage auswählen",
+ "SEARCH_PLACEHOLDER": "Vorlagen suchen",
+ "EMPTY_STATE": "Keine Vorlagen gefunden",
+ "TEMPLATE_PARSER": {
+ "BACK": "Zurück",
+ "SEND_MESSAGE": "Nachricht senden"
+ }
+ },
"ACTION_BUTTONS": {
"DISCARD": "Verwerfen",
"SEND": "Senden ({keyCode})"
diff --git a/app/javascript/dashboard/i18n/locale/de/contactFilters.json b/app/javascript/dashboard/i18n/locale/de/contactFilters.json
index 778edb21f..acfd8a6e1 100644
--- a/app/javascript/dashboard/i18n/locale/de/contactFilters.json
+++ b/app/javascript/dashboard/i18n/locale/de/contactFilters.json
@@ -48,7 +48,8 @@
"CREATED_AT": "Erstellt am",
"LAST_ACTIVITY": "Letzte Aktivität",
"REFERER_LINK": "Verweis-Link",
- "BLOCKED": "Blockiert"
+ "BLOCKED": "Blockiert",
+ "LABELS": "Labels"
},
"GROUPS": {
"STANDARD_FILTERS": "Standardfilter",
diff --git a/app/javascript/dashboard/i18n/locale/de/contentTemplates.json b/app/javascript/dashboard/i18n/locale/de/contentTemplates.json
new file mode 100644
index 000000000..95f4163f0
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/de/contentTemplates.json
@@ -0,0 +1,51 @@
+{
+ "CONTENT_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Twilio Templates",
+ "SUBTITLE": "Select the Twilio template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Vorlagen suchen",
+ "NO_TEMPLATES_FOUND": "Keine Vorlagen gefunden für",
+ "NO_CONTENT": "Kein Inhalt",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Kategorie",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No Twilio templates available. Click refresh to sync templates from Twilio.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Sprache",
+ "TEMPLATE_BODY": "Vorlagenbody",
+ "CATEGORY": "Kategorie"
+ },
+ "TYPES": {
+ "MEDIA": "Media",
+ "QUICK_REPLY": "Quick Reply",
+ "TEXT": "Text"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variablen",
+ "LANGUAGE": "Sprache",
+ "CATEGORY": "Kategorie",
+ "VARIABLE_PLACEHOLDER": "Geben Sie den Wert {variable} ein",
+ "GO_BACK_LABEL": "Zurück",
+ "SEND_MESSAGE_LABEL": "Nachricht senden",
+ "FORM_ERROR_MESSAGE": "Bitte füllen Sie vor dem Absenden alle Variablen aus",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "MEDIA_URL_LABEL": "Enter full media URL",
+ "MEDIA_URL_PLACEHOLDER": "https://example.com/image.jpg"
+ },
+ "FORM": {
+ "BACK_BUTTON": "Zurück",
+ "SEND_MESSAGE_BUTTON": "Nachricht senden"
+ }
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/de/conversation.json b/app/javascript/dashboard/i18n/locale/de/conversation.json
index ca1e2033d..719190eee 100644
--- a/app/javascript/dashboard/i18n/locale/de/conversation.json
+++ b/app/javascript/dashboard/i18n/locale/de/conversation.json
@@ -35,6 +35,11 @@
"API_HOURS_WINDOW": "Sie können auf diese Unterhaltung nur innerhalb von {hours} Stunden antworten",
"NOT_ASSIGNED_TO_YOU": "Diese Konversation ist Ihnen nicht zugeordnet. Möchten Sie dieses Gespräch sich selbst zuordnen?",
"ASSIGN_TO_ME": "Mir zuweisen",
+ "BOT_HANDOFF_MESSAGE": "Sie antworten auf eine Unterhaltung, die derzeit von einem Assistenten oder einem Bot bearbeitet wird.",
+ "BOT_HANDOFF_ACTION": "Mark open and assign to you",
+ "BOT_HANDOFF_REOPEN_ACTION": "Mark conversation open",
+ "BOT_HANDOFF_SUCCESS": "Conversation has been handed over to you",
+ "BOT_HANDOFF_ERROR": "Failed to take over the conversation. Please try again.",
"TWILIO_WHATSAPP_CAN_REPLY": "Sie können auf diese Konversation nur mit einer Nachrichtenvorlage antworten wegen",
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "24-Stunden-Nachrichtenfenster-Beschränkung",
"OLD_INSTAGRAM_INBOX_REPLY_BANNER": "Dieser Instagram-Account wurde in den neuen Instagram-Kanal übertragen. Alle neuen Nachrichten werden dort erscheinen. Sie werden keine Nachrichten mehr von dieser Unterhaltung senden können.",
@@ -66,6 +71,17 @@
"SHOW_LABELS": "Labels anzeigen",
"HIDE_LABELS": "Labels ausblenden"
},
+ "VOICE_CALL": {
+ "INCOMING_CALL": "Incoming call",
+ "OUTGOING_CALL": "Outgoing call",
+ "CALL_IN_PROGRESS": "Call in progress",
+ "NO_ANSWER": "No answer",
+ "MISSED_CALL": "Missed call",
+ "CALL_ENDED": "Call ended",
+ "NOT_ANSWERED_YET": "Not answered yet",
+ "THEY_ANSWERED": "They answered",
+ "YOU_ANSWERED": "You answered"
+ },
"HEADER": {
"RESOLVE_ACTION": "Fall schließen",
"REOPEN_ACTION": "Wieder öffnen",
diff --git a/app/javascript/dashboard/i18n/locale/de/generalSettings.json b/app/javascript/dashboard/i18n/locale/de/generalSettings.json
index e029c0157..bf67ac1b0 100644
--- a/app/javascript/dashboard/i18n/locale/de/generalSettings.json
+++ b/app/javascript/dashboard/i18n/locale/de/generalSettings.json
@@ -59,7 +59,7 @@
},
"MESSAGE": {
"LABEL": "Custom auto-resolution message",
- "PLACEHOLDER": "Conversation was marked resolved by system due to 15 days of inactivity",
+ "PLACEHOLDER": "Die Unterhaltung wurde durch das System aufgrund von 15 Tagen Inaktivität geschlossen",
"HELP": "Message sent to the customer after conversation is auto-resolved"
},
"PREFERENCES": "Einstellungen",
@@ -115,7 +115,7 @@
},
"UPDATE_BUTTON": "Aktualisieren",
"MESSAGE_LABEL": "Custom resolution message",
- "MESSAGE_PLACEHOLDER": "Conversation was marked resolved by system due to 15 days of inactivity",
+ "MESSAGE_PLACEHOLDER": "Die Unterhaltung wurde durch das System aufgrund von 15 Tagen Inaktivität geschlossen",
"MESSAGE_HELP": "This message is sent to the customer when a conversation is automatically resolved by the system due to inactivity."
},
"FEATURES": {
diff --git a/app/javascript/dashboard/i18n/locale/de/helpCenter.json b/app/javascript/dashboard/i18n/locale/de/helpCenter.json
index 6e94dd832..3be2485d1 100644
--- a/app/javascript/dashboard/i18n/locale/de/helpCenter.json
+++ b/app/javascript/dashboard/i18n/locale/de/helpCenter.json
@@ -741,7 +741,8 @@
"LIVE_CHAT_WIDGET": {
"LABEL": "Live chat widget",
"PLACEHOLDER": "Select live chat widget",
- "HELP_TEXT": "Select a live chat widget that will appear on your help center"
+ "HELP_TEXT": "Select a live chat widget that will appear on your help center",
+ "NONE_OPTION": "No widget"
},
"BRAND_COLOR": {
"LABEL": "Brand color"
@@ -807,6 +808,58 @@
"ERROR_MESSAGE": "Unable to update portal"
}
}
+ },
+ "PDF_UPLOAD": {
+ "TITLE": "Upload PDF Document",
+ "DESCRIPTION": "Upload a PDF document to automatically generate FAQs using AI",
+ "DRAG_DROP_TEXT": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "ADDITIONAL_CONTEXT_LABEL": "Additional Context (Optional)",
+ "ADDITIONAL_CONTEXT_PLACEHOLDER": "Provide any additional context or instructions for FAQ generation...",
+ "UPLOADING": "Hochladen...",
+ "UPLOAD": "Upload & Process",
+ "CANCEL": "Stornieren",
+ "ERROR_INVALID_TYPE": "Please select a valid PDF file",
+ "ERROR_FILE_TOO_LARGE": "File size must be less than 512MB",
+ "ERROR_UPLOAD_FAILED": "Failed to upload PDF. Please try again."
+ },
+ "PDF_DOCUMENTS": {
+ "TITLE": "PDF Documents",
+ "DESCRIPTION": "Manage uploaded PDF documents and generate FAQs from them",
+ "UPLOAD_PDF": "Upload PDF",
+ "UPLOAD_FIRST_PDF": "Upload your first PDF",
+ "UPLOADED_BY": "Uploaded by",
+ "GENERATE_FAQS": "Generate FAQs",
+ "GENERATING": "Generieren...",
+ "CONFIRM_DELETE": "Are you sure you want to delete {filename}?",
+ "EMPTY_STATE": {
+ "TITLE": "No PDF documents yet",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQs using AI"
+ },
+ "STATUS": {
+ "UPLOADED": "Ready",
+ "PROCESSING": "Processing",
+ "PROCESSED": "Erledigt",
+ "FAILED": "Failed"
+ }
+ },
+ "CONTENT_GENERATION": {
+ "TITLE": "Content Generation",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQ content using AI",
+ "UPLOAD_TITLE": "Upload PDF Document",
+ "DRAG_DROP": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "UPLOADING": "Processing document...",
+ "UPLOAD_SUCCESS": "Document processed successfully!",
+ "UPLOAD_ERROR": "Failed to upload document. Please try again.",
+ "INVALID_FILE_TYPE": "Please select a valid PDF file",
+ "FILE_TOO_LARGE": "File size must be less than 512MB",
+ "GENERATED_CONTENT": "Generated FAQ Content",
+ "PUBLISH_SELECTED": "Publish Selected",
+ "PUBLISHING": "Publishing...",
+ "FROM_DOCUMENT": "From document",
+ "NO_CONTENT": "No generated content available. Upload a PDF document to get started.",
+ "LOADING": "Loading generated content..."
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/de/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/de/inboxMgmt.json
index 705b24467..9e7176dd3 100644
--- a/app/javascript/dashboard/i18n/locale/de/inboxMgmt.json
+++ b/app/javascript/dashboard/i18n/locale/de/inboxMgmt.json
@@ -272,8 +272,8 @@
},
"SUBMIT_BUTTON": "WhatsApp-Kanal erstellen",
"EMBEDDED_SIGNUP": {
- "TITLE": "Quick Setup with Meta",
- "DESC": "You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
+ "TITLE": "Quick setup with Meta",
+ "DESC": "Use the WhatsApp Embedded Signup flow to quickly connect new numbers. You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
"BENEFITS": {
"TITLE": "Benefits of Embedded Signup:",
"EASY_SETUP": "No manual configuration required",
@@ -281,9 +281,8 @@
"AUTO_CONFIG": "Automatic webhook and phone number configuration"
},
"LEARN_MORE": {
- "TEXT": "To learn more about integrated signup, pricing, and limitations, visit",
- "LINK_TEXT": "this link.",
- "LINK_URL": "https://developers.facebook.com/docs/whatsapp/embedded-signup/custom-flows/onboarding-business-app-users#limitations"
+ "TEXT": "To learn more about integrated signup, pricing, and limitations, visit {link}.",
+ "LINK_TEXT": "this link"
},
"SUBMIT_BUTTON": "Connect with WhatsApp Business",
"AUTH_PROCESSING": "Authenticating with Meta",
@@ -296,7 +295,9 @@
"INVALID_BUSINESS_DATA": "Invalid business data received from Facebook. Please try again.",
"SIGNUP_ERROR": "Signup error occurred",
"AUTH_NOT_COMPLETED": "Authentication not completed. Please restart the process.",
- "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured"
+ "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured",
+ "MANUAL_FALLBACK": "If your number is already connected to the WhatsApp Business Platform (API), or if you’re a tech provider onboarding your own number, please use the {link} flow",
+ "MANUAL_LINK_TEXT": "manual setup flow"
},
"API": {
"ERROR_MESSAGE": "Wir konnten den WhatsApp-Kanal nicht speichern"
@@ -424,7 +425,51 @@
},
"AUTH": {
"TITLE": "Wählen Sie einen Kanal",
- "DESC": "Chatwoot unterstützt Live-Chat-Widgets, Facebook Messenger, Twitter-Profile, WhatsApp, E-Mails usw. als Kanäle. Wenn Sie einen benutzerdefinierten Kanal erstellen möchten, können Sie ihn mithilfe des API-Kanals erstellen. Wählen Sie zunächst einen der folgenden Kanäle aus."
+ "DESC": "Chatwoot unterstützt Live-Chat-Widgets, Facebook Messenger, Twitter-Profile, WhatsApp, E-Mails usw. als Kanäle. Wenn Sie einen benutzerdefinierten Kanal erstellen möchten, können Sie ihn mithilfe des API-Kanals erstellen. Wählen Sie zunächst einen der folgenden Kanäle aus.",
+ "TITLE_NEXT": "Complete the setup",
+ "TITLE_FINISH": "Voilà!",
+ "CHANNEL": {
+ "WEBSITE": {
+ "TITLE": "Webseite",
+ "DESCRIPTION": "Create a live-chat widget"
+ },
+ "FACEBOOK": {
+ "TITLE": "Facebook",
+ "DESCRIPTION": "Connect your Facebook page"
+ },
+ "WHATSAPP": {
+ "TITLE": "WhatsApp",
+ "DESCRIPTION": "Support your customers on WhatsApp"
+ },
+ "EMAIL": {
+ "TITLE": "E-Mail",
+ "DESCRIPTION": "Connect with Gmail, Outlook, or other providers"
+ },
+ "SMS": {
+ "TITLE": "SMS",
+ "DESCRIPTION": "Integrate SMS channel with Twilio or bandwidth"
+ },
+ "API": {
+ "TITLE": "API",
+ "DESCRIPTION": "Make a custom channel using our API"
+ },
+ "TELEGRAM": {
+ "TITLE": "Telegramm",
+ "DESCRIPTION": "Configure Telegram channel using Bot token"
+ },
+ "LINE": {
+ "TITLE": "Line",
+ "DESCRIPTION": "Integrate your Line channel"
+ },
+ "INSTAGRAM": {
+ "TITLE": "Instagram",
+ "DESCRIPTION": "Connect your instagram account"
+ },
+ "VOICE": {
+ "TITLE": "Voice",
+ "DESCRIPTION": "Integrate with Twilio Voice"
+ }
+ }
},
"AGENTS": {
"TITLE": "Agenten",
@@ -478,7 +523,10 @@
"MESSAGE": "Sie können jetzt über Ihren neuen Kanal mit Ihren Kunden in Kontakt treten. Fröhliches Unterstützen",
"BUTTON_TEXT": "Bring mich dahin",
"MORE_SETTINGS": "Weitere Einstellungen",
- "WEBSITE_SUCCESS": "Sie haben die Erstellung eines Website-Kanals erfolgreich abgeschlossen. Kopieren Sie den unten gezeigten Code und fügen Sie ihn in Ihre Website ein. Wenn ein Kunde das nächste Mal den Live-Chat verwendet, wird die Konversation automatisch in Ihrem Posteingang angezeigt."
+ "WEBSITE_SUCCESS": "Sie haben die Erstellung eines Website-Kanals erfolgreich abgeschlossen. Kopieren Sie den unten gezeigten Code und fügen Sie ihn in Ihre Website ein. Wenn ein Kunde das nächste Mal den Live-Chat verwendet, wird die Konversation automatisch in Ihrem Posteingang angezeigt.",
+ "WHATSAPP_QR_INSTRUCTION": "Scan the QR code above to quickly test your WhatsApp inbox",
+ "MESSENGER_QR_INSTRUCTION": "Scan the QR code above to quickly test your Facebook Messenger inbox",
+ "TELEGRAM_QR_INSTRUCTION": "Scan the QR code above to quickly test your Telegram inbox"
},
"REAUTH": "Neu autorisieren",
"VIEW": "Aussicht",
@@ -868,9 +916,18 @@
"SCRIPT_SETTINGS": "\n window.chatwootSettings = {options};"
},
"EMAIL_PROVIDERS": {
- "MICROSOFT": "Microsoft",
- "GOOGLE": "Google",
- "OTHER_PROVIDERS": "Andere Anbieter"
+ "MICROSOFT": {
+ "TITLE": "Microsoft",
+ "DESCRIPTION": "Connect with Microsoft"
+ },
+ "GOOGLE": {
+ "TITLE": "Google",
+ "DESCRIPTION": "Connect with Google"
+ },
+ "OTHER_PROVIDERS": {
+ "TITLE": "Andere Anbieter",
+ "DESCRIPTION": "Connect with Other Providers"
+ }
},
"CHANNELS": {
"MESSENGER": "Messenger",
diff --git a/app/javascript/dashboard/i18n/locale/de/integrations.json b/app/javascript/dashboard/i18n/locale/de/integrations.json
index 060389c52..a8c115ea1 100644
--- a/app/javascript/dashboard/i18n/locale/de/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/de/integrations.json
@@ -704,11 +704,28 @@
"ERROR_MESSAGE": "There was an error creating the document, please try again."
},
"FORM": {
+ "TYPE": {
+ "LABEL": "Document Type",
+ "URL": "URL",
+ "PDF": "PDF File"
+ },
"URL": {
"LABEL": "URL",
"PLACEHOLDER": "Enter the URL of the document",
"ERROR": "Please provide a valid URL for the document"
},
+ "PDF_FILE": {
+ "LABEL": "PDF File",
+ "CHOOSE_FILE": "Choose PDF file",
+ "ERROR": "Please select a PDF file",
+ "HELP_TEXT": "Maximum file size: 10MB",
+ "INVALID_TYPE": "Please select a valid PDF file",
+ "TOO_LARGE": "File size exceeds 10MB limit"
+ },
+ "NAME": {
+ "LABEL": "Document Name (Optional)",
+ "PLACEHOLDER": "Enter a name for the document"
+ },
"ASSISTANT": {
"LABEL": "Assistant",
"PLACEHOLDER": "Select the assistant",
@@ -744,6 +761,7 @@
"SELECTED": "{count} selected",
"SELECT_ALL": "Select all ({count})",
"UNSELECT_ALL": "Unselect all ({count})",
+ "SEARCH_PLACEHOLDER": "Search FAQs...",
"BULK_APPROVE_BUTTON": "Approve",
"BULK_DELETE_BUTTON": "Löschen",
"BULK_APPROVE": {
diff --git a/app/javascript/dashboard/i18n/locale/de/mfa.json b/app/javascript/dashboard/i18n/locale/de/mfa.json
new file mode 100644
index 000000000..de9b27cf7
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/de/mfa.json
@@ -0,0 +1,106 @@
+{
+ "MFA_SETTINGS": {
+ "TITLE": "Two-Factor Authentication",
+ "SUBTITLE": "Secure your account with TOTP-based authentication",
+ "DESCRIPTION": "Add an extra layer of security to your account using a time-based one-time password (TOTP)",
+ "STATUS_TITLE": "Authentication Status",
+ "STATUS_DESCRIPTION": "Manage your two-factor authentication settings and backup recovery codes",
+ "ENABLED": "Aktiviert",
+ "DISABLED": "Deaktiviert",
+ "STATUS_ENABLED": "Two-factor authentication is active",
+ "STATUS_ENABLED_DESC": "Your account is protected with an additional layer of security",
+ "ENABLE_BUTTON": "Enable Two-Factor Authentication",
+ "ENHANCE_SECURITY": "Enhance Your Account Security",
+ "ENHANCE_SECURITY_DESC": "Two-factor authentication adds an extra layer of security by requiring a verification code from your authenticator app in addition to your password.",
+ "SETUP": {
+ "STEP_NUMBER_1": "1",
+ "STEP_NUMBER_2": "2",
+ "STEP1_TITLE": "Scan QR Code with Your Authenticator App",
+ "STEP1_DESCRIPTION": "Use Google Authenticator, Authy, or any TOTP-compatible app",
+ "LOADING_QR": "Laden...",
+ "MANUAL_ENTRY": "Can't scan? Enter code manually",
+ "SECRET_KEY": "Secret Key",
+ "COPY": "Kopieren",
+ "ENTER_CODE": "Enter the 6-digit code from your authenticator app",
+ "ENTER_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify & Continue",
+ "CANCEL": "Stornieren",
+ "ERROR_STARTING": "MFA not enabled. Please contact administrator.",
+ "INVALID_CODE": "Invalid verification code",
+ "SECRET_COPIED": "Secret key copied to clipboard",
+ "SUCCESS": "Two-factor authentication has been enabled successfully"
+ },
+ "BACKUP": {
+ "TITLE": "Save Your Backup Codes",
+ "DESCRIPTION": "Keep these codes safe. Each can be used once if you lose access to your authenticator",
+ "IMPORTANT": "Important:",
+ "IMPORTANT_NOTE": " Save these codes in a secure location. You won't be able to see them again.",
+ "DOWNLOAD": "Herunterladen",
+ "COPY_ALL": "Copy All",
+ "CONFIRM": "I have saved my backup codes in a secure location and understand that I won't be able to see them again",
+ "COMPLETE_SETUP": "Complete Setup",
+ "CODES_COPIED": "Backup codes copied to clipboard"
+ },
+ "MANAGEMENT": {
+ "BACKUP_CODES": "Backup Codes",
+ "BACKUP_CODES_DESC": "Generate new codes if you've lost or used your existing ones",
+ "REGENERATE": "Regenerate Backup Codes",
+ "DISABLE_MFA": "Disable 2FA",
+ "DISABLE_MFA_DESC": "Remove two-factor authentication from your account",
+ "DISABLE_BUTTON": "Disable Two-Factor Authentication"
+ },
+ "DISABLE": {
+ "TITLE": "Disable Two-Factor Authentication",
+ "DESCRIPTION": "You'll need to enter your password and a verification code to disable two-factor authentication.",
+ "PASSWORD": "Passwort",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Disable 2FA",
+ "CANCEL": "Stornieren",
+ "SUCCESS": "Two-factor authentication has been disabled",
+ "ERROR": "Failed to disable MFA. Please check your credentials."
+ },
+ "REGENERATE": {
+ "TITLE": "Regenerate Backup Codes",
+ "DESCRIPTION": "This will invalidate your existing backup codes and generate new ones. Enter your verification code to continue.",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Generate New Codes",
+ "CANCEL": "Stornieren",
+ "NEW_CODES_TITLE": "New Backup Codes Generated",
+ "NEW_CODES_DESC": "Your old backup codes have been invalidated. Save these new codes in a secure location.",
+ "CODES_IMPORTANT": "Important:",
+ "CODES_IMPORTANT_NOTE": " Each code can only be used once. Save them before closing this window.",
+ "DOWNLOAD_CODES": "Download Codes",
+ "COPY_ALL_CODES": "Copy All Codes",
+ "CODES_SAVED": "I've Saved My Codes",
+ "SUCCESS": "New backup codes have been generated",
+ "ERROR": "Failed to regenerate backup codes"
+ }
+ },
+ "MFA_VERIFICATION": {
+ "TITLE": "Two-Factor Authentication",
+ "DESCRIPTION": "Enter your verification code to continue",
+ "AUTHENTICATOR_APP": "Authenticator App",
+ "BACKUP_CODE": "Backup Code",
+ "ENTER_OTP_CODE": "Enter 6-digit code from your authenticator app",
+ "ENTER_BACKUP_CODE": "Enter one of your backup codes",
+ "BACKUP_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify",
+ "TRY_ANOTHER_METHOD": "Try another verification method",
+ "CANCEL_LOGIN": "Cancel and return to login",
+ "HELP_TEXT": "Having trouble signing in?",
+ "LEARN_MORE": "Learn more about 2FA",
+ "HELP_MODAL": {
+ "TITLE": "Two-Factor Authentication Help",
+ "AUTHENTICATOR_TITLE": "Using an Authenticator App",
+ "AUTHENTICATOR_DESC": "Open your authenticator app (Google Authenticator, Authy, etc.) and enter the 6-digit code shown for your account.",
+ "BACKUP_TITLE": "Using a Backup Code",
+ "BACKUP_DESC": "If you don't have access to your authenticator app, you can use one of the backup codes you saved when setting up 2FA. Each code can only be used once.",
+ "CONTACT_TITLE": "Need More Help?",
+ "CONTACT_DESC_CLOUD": "If you've lost access to both your authenticator app and backup codes, please reach out to Chatwoot support for assistance.",
+ "CONTACT_DESC_SELF_HOSTED": "If you've lost access to both your authenticator app and backup codes, please contact your administrator for assistance."
+ },
+ "VERIFICATION_FAILED": "Verification failed. Please try again."
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/de/settings.json b/app/javascript/dashboard/i18n/locale/de/settings.json
index 71c12b477..5a97adb72 100644
--- a/app/javascript/dashboard/i18n/locale/de/settings.json
+++ b/app/javascript/dashboard/i18n/locale/de/settings.json
@@ -51,6 +51,13 @@
"LARGER": "Larger",
"EXTRA_LARGE": "Extra Large"
}
+ },
+ "LANGUAGE": {
+ "TITLE": "Preferred Language",
+ "NOTE": "Choose the language you want to use.",
+ "UPDATE_SUCCESS": "Your Language settings have been updated successfully",
+ "UPDATE_ERROR": "There is an error while updating the language settings, please try again",
+ "USE_ACCOUNT_DEFAULT": "Use account default"
}
},
"MESSAGE_SIGNATURE_SECTION": {
@@ -73,6 +80,11 @@
"NOTE": "Durch das Aktualisieren Ihres Kennworts werden Ihre Anmeldungen auf mehreren Geräten zurückgesetzt.",
"BTN_TEXT": "Passwort ändern"
},
+ "SECURITY_SECTION": {
+ "TITLE": "Security",
+ "NOTE": "Manage additional security features for your account.",
+ "MFA_BUTTON": "Manage Two-Factor Authentication"
+ },
"ACCESS_TOKEN": {
"TITLE": "Zugangstoken",
"NOTE": "Dieses Token kann verwendet werden, wenn Sie eine API-basierte Integration erstellen",
@@ -330,6 +342,7 @@
"REPORTS_LABEL": "Labels",
"REPORTS_INBOX": "Posteingang",
"REPORTS_TEAM": "Team",
+ "AGENT_ASSIGNMENT": "Agent Assignment",
"SET_AVAILABILITY_TITLE": "Setzen Sie sich als",
"SET_YOUR_AVAILABILITY": "Legen Sie Ihre Verfügbarkeit fest",
"SLA": "SLA",
@@ -350,7 +363,8 @@
"INFO_TEXT": "Lassen Sie sich vom System automatisch als offline markieren, wenn Sie die App oder das Dashboard nicht verwenden.",
"INFO_SHORT": "Automatically mark offline when you aren't using the app."
},
- "DOCS": "Dokumente lesen"
+ "DOCS": "Dokumente lesen",
+ "SECURITY": "Security"
},
"BILLING_SETTINGS": {
"TITLE": "Rechnungen",
@@ -382,6 +396,77 @@
},
"NO_BILLING_USER": "Ihr Rechnungskonto wird konfiguriert. Bitte aktualisieren Sie die Seite und versuchen Sie es erneut."
},
+ "SECURITY_SETTINGS": {
+ "TITLE": "Security",
+ "DESCRIPTION": "Manage your account security settings.",
+ "LINK_TEXT": "Learn more about SAML SSO",
+ "SAML": {
+ "TITLE": "SAML SSO",
+ "NOTE": "Configure SAML single sign-on for your account. Users will authenticate through your identity provider instead of using email/password.",
+ "ACS_URL": {
+ "LABEL": "ACS URL",
+ "TOOLTIP": "Assertion Consumer Service URL - Configure this URL in your IdP as the destination for SAML responses"
+ },
+ "SSO_URL": {
+ "LABEL": "SSO URL",
+ "HELP": "The URL where SAML authentication requests will be sent",
+ "PLACEHOLDER": "https://your-idp.com/saml/sso"
+ },
+ "CERTIFICATE": {
+ "LABEL": "Signing certificate in PEM format",
+ "HELP": "The public certificate from your identity provider used to verify SAML responses",
+ "PLACEHOLDER": "-----BEGIN CERTIFICATE-----\nMIIC..."
+ },
+ "FINGERPRINT": {
+ "LABEL": "Fingerprint",
+ "TOOLTIP": "SHA-1 fingerprint of the certificate - Use this to verify the certificate in your IdP configuration"
+ },
+ "COPY_SUCCESS": "Code erfolgreich in die Zwischenablage kopiert",
+ "SP_ENTITY_ID": {
+ "LABEL": "SP Entity ID",
+ "HELP": "Unique identifier for this application as a service provider (auto-generated).",
+ "TOOLTIP": "Unique identifier for Chatwoot as the Service Provider - Configure this in your IdP settings"
+ },
+ "IDP_ENTITY_ID": {
+ "LABEL": "Identity Provider Entity ID",
+ "HELP": "Unique identifier for your identity provider (usually found in IdP configuration)",
+ "PLACEHOLDER": "https://your-idp.com/saml"
+ },
+ "UPDATE_BUTTON": "Update SAML Settings",
+ "API": {
+ "SUCCESS": "SAML settings updated successfully",
+ "ERROR": "Failed to update SAML settings",
+ "ERROR_LOADING": "Failed to load SAML settings",
+ "DISABLED": "SAML settings disabled successfully"
+ },
+ "VALIDATION": {
+ "REQUIRED_FIELDS": "SSO URL, Identity Provider Entity ID, and Certificate are required fields",
+ "SSO_URL_ERROR": "Please enter a valid SSO URL",
+ "CERTIFICATE_ERROR": "Certificate is required",
+ "IDP_ENTITY_ID_ERROR": "Identity Provider Entity ID is required"
+ },
+ "ENTERPRISE_PAYWALL": {
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade to an Enterprise plan to access SAML single sign-on and other advanced security features.",
+ "ASK_ADMIN": "Please reach out to your administrator for the upgrade."
+ },
+ "PAYWALL": {
+ "TITLE": "Upgrade to enable SAML SSO",
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade your plan to get access to SAML single sign-on and other advanced features.",
+ "UPGRADE_NOW": "Upgrade now",
+ "CANCEL_ANYTIME": "You can change or cancel your plan anytime"
+ },
+ "ATTRIBUTE_MAPPING": {
+ "TITLE": "SAML Attribute Setup",
+ "DESCRIPTION": "The following attribute mappings must be configured in your identity provider"
+ },
+ "INFO_SECTION": {
+ "TITLE": "Service Provider Information",
+ "TOOLTIP": "Copy these values and configure them in your Identity Provider to establish the SAML connection"
+ }
+ }
+ },
"CREATE_ACCOUNT": {
"NO_ACCOUNT_WARNING": "Oh oh! Wir konnten keine Chatwoot-Konten finden. Bitte erstellen Sie ein neues Konto um fortzufahren.",
"NEW_ACCOUNT": "Neuer Account",
@@ -418,5 +503,255 @@
"SWITCH_TO_REPLY": "Zur Antwort wechseln",
"TOGGLE_SNOOZE_DROPDOWN": "Schlummer-Dropdown ein-/ausblenden"
}
+ },
+ "ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent assignment",
+ "DESCRIPTION": "Define policies to effectively manage workload and route conversations based on the needs of inboxes and agents. Learn more here"
+ },
+ "ASSIGNMENT_POLICY": {
+ "TITLE": "Assignment policy",
+ "DESCRIPTION": "Manage how conversations get assigned in inboxes.",
+ "FEATURES": [
+ "Assign by conversations evenly or by available capacity",
+ "Add fair distribution rules to avoid overloading any agent",
+ "Add inboxes to a policy - one policy per inbox"
+ ]
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "TITLE": "Agent capacity policy",
+ "DESCRIPTION": "Manage workload for agents.",
+ "FEATURES": [
+ "Define maximum conversations per inbox",
+ "Create exceptions based on labels and time",
+ "Add agents to a policy - one policy per agent"
+ ]
+ }
+ },
+ "AGENT_ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Assignment policy",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "ORDER": "Order",
+ "PRIORITY": "Priorität",
+ "ACTIVE": "Aktiv",
+ "INACTIVE": "Inactive",
+ "POPOVER": "Added inboxes",
+ "EDIT": "Bearbeiten"
+ },
+ "NO_RECORDS_FOUND": "No assignment policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create assignment policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy created successfully",
+ "ERROR_MESSAGE": "Failed to create assignment policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit assignment policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_INBOX_DIALOG": {
+ "TITLE": "Add inbox",
+ "DESCRIPTION": "{inboxName} inbox is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Stornieren"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update assignment policy"
+ },
+ "INBOX_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Inbox added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Inbox removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove inbox from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Beschreibung:",
+ "PLACEHOLDER": "Beschreibung eingeben"
+ },
+ "STATUS": {
+ "LABEL": "Status:",
+ "PLACEHOLDER": "Status auswählen",
+ "ACTIVE": "Policy is active",
+ "INACTIVE": "Policy is inactive"
+ },
+ "ASSIGNMENT_ORDER": {
+ "LABEL": "Assignment order",
+ "ROUND_ROBIN": {
+ "LABEL": "Round robin",
+ "DESCRIPTION": "Assign conversations evenly among agents."
+ },
+ "BALANCED": {
+ "LABEL": "Balanced",
+ "DESCRIPTION": "Assign conversations based on available capacity."
+ }
+ },
+ "ASSIGNMENT_PRIORITY": {
+ "LABEL": "Assignment priority",
+ "EARLIEST_CREATED": {
+ "LABEL": "Earliest created",
+ "DESCRIPTION": "The conversation that was created first gets assigned first."
+ },
+ "LONGEST_WAITING": {
+ "LABEL": "Longest waiting",
+ "DESCRIPTION": "The conversation waiting the longest gets assigned first."
+ }
+ },
+ "FAIR_DISTRIBUTION": {
+ "LABEL": "Fair distribution policy",
+ "DESCRIPTION": "Set the maximum number of conversations that can be assigned per agent within a time window to avoid overloading any one agent. This required field defaults to 100 conversations per hour.",
+ "INPUT_MAX": "Assign max",
+ "DURATION": "Conversations per agent in every"
+ },
+ "INBOXES": {
+ "LABEL": "Added inboxes",
+ "DESCRIPTION": "Add inboxes for which this policy will be applicable.",
+ "ADD_BUTTON": "Add inbox",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select inboxes to add",
+ "ADD_BUTTON": "Hinzufügen"
+ },
+ "EMPTY_STATE": "No inboxes added to this policy, add an inbox to get started",
+ "API": {
+ "SUCCESS_MESSAGE": "Inbox successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Assignment policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete assignment policy"
+ }
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent capacity",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "POPOVER": "Added agents",
+ "EDIT": "Bearbeiten"
+ },
+ "NO_RECORDS_FOUND": "No agent capacity policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create agent capacity policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy created successfully",
+ "ERROR_MESSAGE": "Failed to create agent capacity policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit agent capacity policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_AGENT_DIALOG": {
+ "TITLE": "Add agent",
+ "DESCRIPTION": "{agentName} is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Stornieren"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update agent capacity policy"
+ },
+ "AGENT_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Agent added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Agent removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove agent from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Beschreibung:",
+ "PLACEHOLDER": "Beschreibung eingeben"
+ },
+ "INBOX_CAPACITY_LIMIT": {
+ "LABEL": "Inbox capacity limits",
+ "ADD_BUTTON": "Add inbox",
+ "FIELD": {
+ "SELECT_INBOX": "Select inbox",
+ "MAX_CONVERSATIONS": "Max conversations",
+ "SET_LIMIT": "Set limit"
+ },
+ "EMPTY_STATE": "No inbox limit set"
+ },
+ "EXCLUSION_RULES": {
+ "LABEL": "Exclusion rules",
+ "DESCRIPTION": "Conversations that satisfy the following conditions would not count towards agent capacity",
+ "TAGS": {
+ "LABEL": "Exclude conversations tagged with specific labels",
+ "ADD_TAG": "add tag",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select tags to add"
+ },
+ "EMPTY_STATE": "No tags added to this policy."
+ },
+ "DURATION": {
+ "LABEL": "Exclude conversations older than a specified duration",
+ "PLACEHOLDER": "Set time"
+ }
+ },
+ "USERS": {
+ "LABEL": "Assigned agents",
+ "DESCRIPTION": "Add agents for which this policy will be applicable.",
+ "ADD_BUTTON": "Add agent",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select agents to add",
+ "ADD_BUTTON": "Hinzufügen"
+ },
+ "EMPTY_STATE": "No agents added",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Agent capacity policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete agent capacity policy"
+ }
+ },
+ "DELETE_POLICY": {
+ "TITLE": "Delete policy",
+ "DESCRIPTION": "Are you sure you want to delete this policy? This action cannot be undone.",
+ "CONFIRM_BUTTON_LABEL": "Löschen",
+ "CANCEL_BUTTON_LABEL": "Stornieren"
+ }
}
}
diff --git a/app/javascript/dashboard/i18n/locale/de/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/de/whatsappTemplates.json
index 87f5c6a2c..6d9836199 100644
--- a/app/javascript/dashboard/i18n/locale/de/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/de/whatsappTemplates.json
@@ -40,6 +40,7 @@
"BUTTON_LABEL": "Button {index}",
"COUPON_CODE": "Enter coupon code (max 15 chars)",
"MEDIA_URL_LABEL": "Enter {type} URL",
+ "DOCUMENT_NAME_PLACEHOLDER": "Enter document filename (e.g., Invoice_2025.pdf)",
"BUTTON_PARAMETER": "Enter button parameter"
}
}
diff --git a/app/javascript/dashboard/i18n/locale/el/automation.json b/app/javascript/dashboard/i18n/locale/el/automation.json
index 27afa3306..67ffae506 100644
--- a/app/javascript/dashboard/i18n/locale/el/automation.json
+++ b/app/javascript/dashboard/i18n/locale/el/automation.json
@@ -177,7 +177,8 @@
"REFERER_LINK": "Referrer Link",
"ASSIGNEE_NAME": "Assignee",
"TEAM_NAME": "Ομάδα",
- "PRIORITY": "Priority"
+ "PRIORITY": "Priority",
+ "LABELS": "Ετικέτες"
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/el/contact.json b/app/javascript/dashboard/i18n/locale/el/contact.json
index e9a94b540..12f297962 100644
--- a/app/javascript/dashboard/i18n/locale/el/contact.json
+++ b/app/javascript/dashboard/i18n/locale/el/contact.json
@@ -610,6 +610,15 @@
"SEND_MESSAGE": "Αποστολή μηνύματος"
}
},
+ "TWILIO_OPTIONS": {
+ "LABEL": "Select template",
+ "SEARCH_PLACEHOLDER": "Search templates",
+ "EMPTY_STATE": "No templates found",
+ "TEMPLATE_PARSER": {
+ "BACK": "Πίσω",
+ "SEND_MESSAGE": "Αποστολή μηνύματος"
+ }
+ },
"ACTION_BUTTONS": {
"DISCARD": "Discard",
"SEND": "Send ({keyCode})"
diff --git a/app/javascript/dashboard/i18n/locale/el/contactFilters.json b/app/javascript/dashboard/i18n/locale/el/contactFilters.json
index 8a43cb6b0..cc2bb6234 100644
--- a/app/javascript/dashboard/i18n/locale/el/contactFilters.json
+++ b/app/javascript/dashboard/i18n/locale/el/contactFilters.json
@@ -48,7 +48,8 @@
"CREATED_AT": "Δημιουργήθηκε στις",
"LAST_ACTIVITY": "Τελευταία Δραστηριότητα",
"REFERER_LINK": "Σύνδεσμος αναφοράς",
- "BLOCKED": "Blocked"
+ "BLOCKED": "Blocked",
+ "LABELS": "Ετικέτες"
},
"GROUPS": {
"STANDARD_FILTERS": "Τυπικά Φίλτρα",
diff --git a/app/javascript/dashboard/i18n/locale/el/contentTemplates.json b/app/javascript/dashboard/i18n/locale/el/contentTemplates.json
new file mode 100644
index 000000000..af5f698fd
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/el/contentTemplates.json
@@ -0,0 +1,51 @@
+{
+ "CONTENT_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Twilio Templates",
+ "SUBTITLE": "Select the Twilio template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Αναζήτηση Προτύπων",
+ "NO_TEMPLATES_FOUND": "Δεν βρέθηκαν πρότυπα για",
+ "NO_CONTENT": "No content",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Κατηγορία",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No Twilio templates available. Click refresh to sync templates from Twilio.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Γλώσσα",
+ "TEMPLATE_BODY": "Σώμα Προτύπου",
+ "CATEGORY": "Κατηγορία"
+ },
+ "TYPES": {
+ "MEDIA": "Media",
+ "QUICK_REPLY": "Quick Reply",
+ "TEXT": "Κείμενο"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Μεταβλητές",
+ "LANGUAGE": "Γλώσσα",
+ "CATEGORY": "Κατηγορία",
+ "VARIABLE_PLACEHOLDER": "Εισάγετε τιμή για {variable}",
+ "GO_BACK_LABEL": "Πίσω",
+ "SEND_MESSAGE_LABEL": "Αποστολή μηνύματος",
+ "FORM_ERROR_MESSAGE": "Παρακαλώ συμπληρώστε όλες τις μεταβλητές πριν την αποστολή",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "MEDIA_URL_LABEL": "Enter full media URL",
+ "MEDIA_URL_PLACEHOLDER": "https://example.com/image.jpg"
+ },
+ "FORM": {
+ "BACK_BUTTON": "Πίσω",
+ "SEND_MESSAGE_BUTTON": "Αποστολή μηνύματος"
+ }
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/el/conversation.json b/app/javascript/dashboard/i18n/locale/el/conversation.json
index c68a26a96..94017c2c2 100644
--- a/app/javascript/dashboard/i18n/locale/el/conversation.json
+++ b/app/javascript/dashboard/i18n/locale/el/conversation.json
@@ -35,6 +35,11 @@
"API_HOURS_WINDOW": "You can only reply to this conversation within {hours} hours",
"NOT_ASSIGNED_TO_YOU": "Αυτή η συνομιλία δεν έχει ανατεθεί σε εσάς. Θα θέλατε να αντιστοιχίσετε αυτή τη συνομιλία στον εαυτό σας;",
"ASSIGN_TO_ME": "Ανάθεση σε μένα",
+ "BOT_HANDOFF_MESSAGE": "You are responding to a conversation which is currently handled by an assistant or a bot.",
+ "BOT_HANDOFF_ACTION": "Mark open and assign to you",
+ "BOT_HANDOFF_REOPEN_ACTION": "Mark conversation open",
+ "BOT_HANDOFF_SUCCESS": "Conversation has been handed over to you",
+ "BOT_HANDOFF_ERROR": "Failed to take over the conversation. Please try again.",
"TWILIO_WHATSAPP_CAN_REPLY": "Μπορείτε να απαντήσετε μόνο σε αυτή τη συνομιλία χρησιμοποιώντας ένα πρότυπο μήνυμα επειδή",
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "του περιορισμού των 24 ωρών",
"OLD_INSTAGRAM_INBOX_REPLY_BANNER": "This Instagram account was migrated to the new Instagram channel inbox. All new messages will show up there. You won’t be able to send messages from this conversation anymore.",
@@ -66,6 +71,17 @@
"SHOW_LABELS": "Προβολή ετικετών",
"HIDE_LABELS": "Απόκρυψη ετικετών"
},
+ "VOICE_CALL": {
+ "INCOMING_CALL": "Incoming call",
+ "OUTGOING_CALL": "Outgoing call",
+ "CALL_IN_PROGRESS": "Call in progress",
+ "NO_ANSWER": "No answer",
+ "MISSED_CALL": "Missed call",
+ "CALL_ENDED": "Call ended",
+ "NOT_ANSWERED_YET": "Not answered yet",
+ "THEY_ANSWERED": "They answered",
+ "YOU_ANSWERED": "You answered"
+ },
"HEADER": {
"RESOLVE_ACTION": "Επίλυση",
"REOPEN_ACTION": "Επαναφορά",
diff --git a/app/javascript/dashboard/i18n/locale/el/helpCenter.json b/app/javascript/dashboard/i18n/locale/el/helpCenter.json
index b7ddaf650..40ecccb93 100644
--- a/app/javascript/dashboard/i18n/locale/el/helpCenter.json
+++ b/app/javascript/dashboard/i18n/locale/el/helpCenter.json
@@ -741,7 +741,8 @@
"LIVE_CHAT_WIDGET": {
"LABEL": "Live chat widget",
"PLACEHOLDER": "Select live chat widget",
- "HELP_TEXT": "Select a live chat widget that will appear on your help center"
+ "HELP_TEXT": "Select a live chat widget that will appear on your help center",
+ "NONE_OPTION": "No widget"
},
"BRAND_COLOR": {
"LABEL": "Brand color"
@@ -807,6 +808,58 @@
"ERROR_MESSAGE": "Unable to update portal"
}
}
+ },
+ "PDF_UPLOAD": {
+ "TITLE": "Upload PDF Document",
+ "DESCRIPTION": "Upload a PDF document to automatically generate FAQs using AI",
+ "DRAG_DROP_TEXT": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "ADDITIONAL_CONTEXT_LABEL": "Additional Context (Optional)",
+ "ADDITIONAL_CONTEXT_PLACEHOLDER": "Provide any additional context or instructions for FAQ generation...",
+ "UPLOADING": "Ανέβασμα...",
+ "UPLOAD": "Upload & Process",
+ "CANCEL": "Άκυρο",
+ "ERROR_INVALID_TYPE": "Please select a valid PDF file",
+ "ERROR_FILE_TOO_LARGE": "File size must be less than 512MB",
+ "ERROR_UPLOAD_FAILED": "Failed to upload PDF. Please try again."
+ },
+ "PDF_DOCUMENTS": {
+ "TITLE": "PDF Documents",
+ "DESCRIPTION": "Manage uploaded PDF documents and generate FAQs from them",
+ "UPLOAD_PDF": "Upload PDF",
+ "UPLOAD_FIRST_PDF": "Upload your first PDF",
+ "UPLOADED_BY": "Uploaded by",
+ "GENERATE_FAQS": "Generate FAQs",
+ "GENERATING": "Generating...",
+ "CONFIRM_DELETE": "Are you sure you want to delete {filename}?",
+ "EMPTY_STATE": {
+ "TITLE": "No PDF documents yet",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQs using AI"
+ },
+ "STATUS": {
+ "UPLOADED": "Ready",
+ "PROCESSING": "Processing",
+ "PROCESSED": "Ολοκληρώθηκε",
+ "FAILED": "Failed"
+ }
+ },
+ "CONTENT_GENERATION": {
+ "TITLE": "Content Generation",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQ content using AI",
+ "UPLOAD_TITLE": "Upload PDF Document",
+ "DRAG_DROP": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "UPLOADING": "Processing document...",
+ "UPLOAD_SUCCESS": "Document processed successfully!",
+ "UPLOAD_ERROR": "Failed to upload document. Please try again.",
+ "INVALID_FILE_TYPE": "Please select a valid PDF file",
+ "FILE_TOO_LARGE": "File size must be less than 512MB",
+ "GENERATED_CONTENT": "Generated FAQ Content",
+ "PUBLISH_SELECTED": "Publish Selected",
+ "PUBLISHING": "Publishing...",
+ "FROM_DOCUMENT": "From document",
+ "NO_CONTENT": "No generated content available. Upload a PDF document to get started.",
+ "LOADING": "Loading generated content..."
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/el/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/el/inboxMgmt.json
index 224cae5d0..cf0d9f332 100644
--- a/app/javascript/dashboard/i18n/locale/el/inboxMgmt.json
+++ b/app/javascript/dashboard/i18n/locale/el/inboxMgmt.json
@@ -272,8 +272,8 @@
},
"SUBMIT_BUTTON": "Δημιουργία Καναλιού WhatsApp",
"EMBEDDED_SIGNUP": {
- "TITLE": "Quick Setup with Meta",
- "DESC": "You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
+ "TITLE": "Quick setup with Meta",
+ "DESC": "Use the WhatsApp Embedded Signup flow to quickly connect new numbers. You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
"BENEFITS": {
"TITLE": "Benefits of Embedded Signup:",
"EASY_SETUP": "No manual configuration required",
@@ -281,9 +281,8 @@
"AUTO_CONFIG": "Automatic webhook and phone number configuration"
},
"LEARN_MORE": {
- "TEXT": "To learn more about integrated signup, pricing, and limitations, visit",
- "LINK_TEXT": "this link.",
- "LINK_URL": "https://developers.facebook.com/docs/whatsapp/embedded-signup/custom-flows/onboarding-business-app-users#limitations"
+ "TEXT": "To learn more about integrated signup, pricing, and limitations, visit {link}.",
+ "LINK_TEXT": "this link"
},
"SUBMIT_BUTTON": "Connect with WhatsApp Business",
"AUTH_PROCESSING": "Authenticating with Meta",
@@ -296,7 +295,9 @@
"INVALID_BUSINESS_DATA": "Invalid business data received from Facebook. Please try again.",
"SIGNUP_ERROR": "Signup error occurred",
"AUTH_NOT_COMPLETED": "Authentication not completed. Please restart the process.",
- "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured"
+ "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured",
+ "MANUAL_FALLBACK": "If your number is already connected to the WhatsApp Business Platform (API), or if you’re a tech provider onboarding your own number, please use the {link} flow",
+ "MANUAL_LINK_TEXT": "manual setup flow"
},
"API": {
"ERROR_MESSAGE": "Δεν ήμασταν σε θέση να αποθηκεύσουμε το κανάλι WhatsApp"
@@ -424,7 +425,51 @@
},
"AUTH": {
"TITLE": "Επιλογή Καναλιού",
- "DESC": "Το Chatwoot υποστηρίζει widget live-chat, σελίδα Facebook, προφίλ Twitter, Whatsapp, Email κλπ., ως κανάλια. Αν θέλετε να δημιουργήσετε ένα προσαρμοσμένο κανάλι, μπορείτε να το δημιουργήσετε χρησιμοποιώντας το κανάλι API. Επιλέξτε ένα κανάλι από τις παρακάτω επιλογές για να συνεχίσετε."
+ "DESC": "Το Chatwoot υποστηρίζει widget live-chat, σελίδα Facebook, προφίλ Twitter, Whatsapp, Email κλπ., ως κανάλια. Αν θέλετε να δημιουργήσετε ένα προσαρμοσμένο κανάλι, μπορείτε να το δημιουργήσετε χρησιμοποιώντας το κανάλι API. Επιλέξτε ένα κανάλι από τις παρακάτω επιλογές για να συνεχίσετε.",
+ "TITLE_NEXT": "Complete the setup",
+ "TITLE_FINISH": "Voilà!",
+ "CHANNEL": {
+ "WEBSITE": {
+ "TITLE": "Website",
+ "DESCRIPTION": "Create a live-chat widget"
+ },
+ "FACEBOOK": {
+ "TITLE": "Facebook",
+ "DESCRIPTION": "Connect your Facebook page"
+ },
+ "WHATSAPP": {
+ "TITLE": "WhatsApp",
+ "DESCRIPTION": "Support your customers on WhatsApp"
+ },
+ "EMAIL": {
+ "TITLE": "Email",
+ "DESCRIPTION": "Connect with Gmail, Outlook, or other providers"
+ },
+ "SMS": {
+ "TITLE": "SMS",
+ "DESCRIPTION": "Integrate SMS channel with Twilio or bandwidth"
+ },
+ "API": {
+ "TITLE": "API",
+ "DESCRIPTION": "Make a custom channel using our API"
+ },
+ "TELEGRAM": {
+ "TITLE": "Telegram",
+ "DESCRIPTION": "Configure Telegram channel using Bot token"
+ },
+ "LINE": {
+ "TITLE": "Line",
+ "DESCRIPTION": "Integrate your Line channel"
+ },
+ "INSTAGRAM": {
+ "TITLE": "Instagram",
+ "DESCRIPTION": "Connect your instagram account"
+ },
+ "VOICE": {
+ "TITLE": "Voice",
+ "DESCRIPTION": "Integrate with Twilio Voice"
+ }
+ }
},
"AGENTS": {
"TITLE": "Πράκτορες",
@@ -478,7 +523,10 @@
"MESSAGE": "Μπορείτε να συνομιλείτε με τους πελάτες σας από το νέο κανάλι. Καλή υποστήριξη",
"BUTTON_TEXT": "Μετάβαση",
"MORE_SETTINGS": "Περισσότερες ρυθμίσεις",
- "WEBSITE_SUCCESS": "Επιτυχής δημιουργία του καναλιού ιστοσελίδας. Αντιγράψτε τον κώδικα που παρουσιάζεται παρακάτω, και τοποθετήστε τον στην ιστοσελίδα σας. Την επόμενη φορά που κάποιος πελάτης χρησιμοποιήσει το 'live chat', η συνομιλία θα εμφανιστεί στο κιβώτιο εισερχομένων σας."
+ "WEBSITE_SUCCESS": "Επιτυχής δημιουργία του καναλιού ιστοσελίδας. Αντιγράψτε τον κώδικα που παρουσιάζεται παρακάτω, και τοποθετήστε τον στην ιστοσελίδα σας. Την επόμενη φορά που κάποιος πελάτης χρησιμοποιήσει το 'live chat', η συνομιλία θα εμφανιστεί στο κιβώτιο εισερχομένων σας.",
+ "WHATSAPP_QR_INSTRUCTION": "Scan the QR code above to quickly test your WhatsApp inbox",
+ "MESSENGER_QR_INSTRUCTION": "Scan the QR code above to quickly test your Facebook Messenger inbox",
+ "TELEGRAM_QR_INSTRUCTION": "Scan the QR code above to quickly test your Telegram inbox"
},
"REAUTH": "Εκ νέου εξουσιοδότηση",
"VIEW": "Προβολή",
@@ -868,9 +916,18 @@
"SCRIPT_SETTINGS": "\n window.chatwootSettings = {options};"
},
"EMAIL_PROVIDERS": {
- "MICROSOFT": "Microsoft",
- "GOOGLE": "Google",
- "OTHER_PROVIDERS": "Other Providers"
+ "MICROSOFT": {
+ "TITLE": "Microsoft",
+ "DESCRIPTION": "Connect with Microsoft"
+ },
+ "GOOGLE": {
+ "TITLE": "Google",
+ "DESCRIPTION": "Connect with Google"
+ },
+ "OTHER_PROVIDERS": {
+ "TITLE": "Other Providers",
+ "DESCRIPTION": "Connect with Other Providers"
+ }
},
"CHANNELS": {
"MESSENGER": "Messenger",
diff --git a/app/javascript/dashboard/i18n/locale/el/integrations.json b/app/javascript/dashboard/i18n/locale/el/integrations.json
index 488107d41..f0e2bf220 100644
--- a/app/javascript/dashboard/i18n/locale/el/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/el/integrations.json
@@ -704,11 +704,28 @@
"ERROR_MESSAGE": "There was an error creating the document, please try again."
},
"FORM": {
+ "TYPE": {
+ "LABEL": "Document Type",
+ "URL": "URL",
+ "PDF": "PDF File"
+ },
"URL": {
"LABEL": "URL",
"PLACEHOLDER": "Enter the URL of the document",
"ERROR": "Please provide a valid URL for the document"
},
+ "PDF_FILE": {
+ "LABEL": "PDF File",
+ "CHOOSE_FILE": "Choose PDF file",
+ "ERROR": "Please select a PDF file",
+ "HELP_TEXT": "Maximum file size: 10MB",
+ "INVALID_TYPE": "Please select a valid PDF file",
+ "TOO_LARGE": "File size exceeds 10MB limit"
+ },
+ "NAME": {
+ "LABEL": "Document Name (Optional)",
+ "PLACEHOLDER": "Enter a name for the document"
+ },
"ASSISTANT": {
"LABEL": "Assistant",
"PLACEHOLDER": "Select the assistant",
@@ -744,6 +761,7 @@
"SELECTED": "{count} selected",
"SELECT_ALL": "Select all ({count})",
"UNSELECT_ALL": "Unselect all ({count})",
+ "SEARCH_PLACEHOLDER": "Search FAQs...",
"BULK_APPROVE_BUTTON": "Approve",
"BULK_DELETE_BUTTON": "Διαγραφή",
"BULK_APPROVE": {
diff --git a/app/javascript/dashboard/i18n/locale/el/mfa.json b/app/javascript/dashboard/i18n/locale/el/mfa.json
new file mode 100644
index 000000000..856e9778f
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/el/mfa.json
@@ -0,0 +1,106 @@
+{
+ "MFA_SETTINGS": {
+ "TITLE": "Two-Factor Authentication",
+ "SUBTITLE": "Secure your account with TOTP-based authentication",
+ "DESCRIPTION": "Add an extra layer of security to your account using a time-based one-time password (TOTP)",
+ "STATUS_TITLE": "Authentication Status",
+ "STATUS_DESCRIPTION": "Manage your two-factor authentication settings and backup recovery codes",
+ "ENABLED": "Ενεργό",
+ "DISABLED": "Ανενεργό",
+ "STATUS_ENABLED": "Two-factor authentication is active",
+ "STATUS_ENABLED_DESC": "Your account is protected with an additional layer of security",
+ "ENABLE_BUTTON": "Enable Two-Factor Authentication",
+ "ENHANCE_SECURITY": "Enhance Your Account Security",
+ "ENHANCE_SECURITY_DESC": "Two-factor authentication adds an extra layer of security by requiring a verification code from your authenticator app in addition to your password.",
+ "SETUP": {
+ "STEP_NUMBER_1": "1",
+ "STEP_NUMBER_2": "2",
+ "STEP1_TITLE": "Scan QR Code with Your Authenticator App",
+ "STEP1_DESCRIPTION": "Use Google Authenticator, Authy, or any TOTP-compatible app",
+ "LOADING_QR": "Loading...",
+ "MANUAL_ENTRY": "Can't scan? Enter code manually",
+ "SECRET_KEY": "Secret Key",
+ "COPY": "Αντιγραφή",
+ "ENTER_CODE": "Enter the 6-digit code from your authenticator app",
+ "ENTER_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify & Continue",
+ "CANCEL": "Άκυρο",
+ "ERROR_STARTING": "MFA not enabled. Please contact administrator.",
+ "INVALID_CODE": "Invalid verification code",
+ "SECRET_COPIED": "Secret key copied to clipboard",
+ "SUCCESS": "Two-factor authentication has been enabled successfully"
+ },
+ "BACKUP": {
+ "TITLE": "Save Your Backup Codes",
+ "DESCRIPTION": "Keep these codes safe. Each can be used once if you lose access to your authenticator",
+ "IMPORTANT": "Important:",
+ "IMPORTANT_NOTE": " Save these codes in a secure location. You won't be able to see them again.",
+ "DOWNLOAD": "Κατέβασμα",
+ "COPY_ALL": "Copy All",
+ "CONFIRM": "I have saved my backup codes in a secure location and understand that I won't be able to see them again",
+ "COMPLETE_SETUP": "Complete Setup",
+ "CODES_COPIED": "Backup codes copied to clipboard"
+ },
+ "MANAGEMENT": {
+ "BACKUP_CODES": "Backup Codes",
+ "BACKUP_CODES_DESC": "Generate new codes if you've lost or used your existing ones",
+ "REGENERATE": "Regenerate Backup Codes",
+ "DISABLE_MFA": "Disable 2FA",
+ "DISABLE_MFA_DESC": "Remove two-factor authentication from your account",
+ "DISABLE_BUTTON": "Disable Two-Factor Authentication"
+ },
+ "DISABLE": {
+ "TITLE": "Disable Two-Factor Authentication",
+ "DESCRIPTION": "You'll need to enter your password and a verification code to disable two-factor authentication.",
+ "PASSWORD": "Κωδικός",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Disable 2FA",
+ "CANCEL": "Άκυρο",
+ "SUCCESS": "Two-factor authentication has been disabled",
+ "ERROR": "Failed to disable MFA. Please check your credentials."
+ },
+ "REGENERATE": {
+ "TITLE": "Regenerate Backup Codes",
+ "DESCRIPTION": "This will invalidate your existing backup codes and generate new ones. Enter your verification code to continue.",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Generate New Codes",
+ "CANCEL": "Άκυρο",
+ "NEW_CODES_TITLE": "New Backup Codes Generated",
+ "NEW_CODES_DESC": "Your old backup codes have been invalidated. Save these new codes in a secure location.",
+ "CODES_IMPORTANT": "Important:",
+ "CODES_IMPORTANT_NOTE": " Each code can only be used once. Save them before closing this window.",
+ "DOWNLOAD_CODES": "Download Codes",
+ "COPY_ALL_CODES": "Copy All Codes",
+ "CODES_SAVED": "I've Saved My Codes",
+ "SUCCESS": "New backup codes have been generated",
+ "ERROR": "Failed to regenerate backup codes"
+ }
+ },
+ "MFA_VERIFICATION": {
+ "TITLE": "Two-Factor Authentication",
+ "DESCRIPTION": "Enter your verification code to continue",
+ "AUTHENTICATOR_APP": "Authenticator App",
+ "BACKUP_CODE": "Backup Code",
+ "ENTER_OTP_CODE": "Enter 6-digit code from your authenticator app",
+ "ENTER_BACKUP_CODE": "Enter one of your backup codes",
+ "BACKUP_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify",
+ "TRY_ANOTHER_METHOD": "Try another verification method",
+ "CANCEL_LOGIN": "Cancel and return to login",
+ "HELP_TEXT": "Having trouble signing in?",
+ "LEARN_MORE": "Learn more about 2FA",
+ "HELP_MODAL": {
+ "TITLE": "Two-Factor Authentication Help",
+ "AUTHENTICATOR_TITLE": "Using an Authenticator App",
+ "AUTHENTICATOR_DESC": "Open your authenticator app (Google Authenticator, Authy, etc.) and enter the 6-digit code shown for your account.",
+ "BACKUP_TITLE": "Using a Backup Code",
+ "BACKUP_DESC": "If you don't have access to your authenticator app, you can use one of the backup codes you saved when setting up 2FA. Each code can only be used once.",
+ "CONTACT_TITLE": "Need More Help?",
+ "CONTACT_DESC_CLOUD": "If you've lost access to both your authenticator app and backup codes, please reach out to Chatwoot support for assistance.",
+ "CONTACT_DESC_SELF_HOSTED": "If you've lost access to both your authenticator app and backup codes, please contact your administrator for assistance."
+ },
+ "VERIFICATION_FAILED": "Verification failed. Please try again."
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/el/settings.json b/app/javascript/dashboard/i18n/locale/el/settings.json
index 336e96111..a71c447f9 100644
--- a/app/javascript/dashboard/i18n/locale/el/settings.json
+++ b/app/javascript/dashboard/i18n/locale/el/settings.json
@@ -51,6 +51,13 @@
"LARGER": "Larger",
"EXTRA_LARGE": "Extra Large"
}
+ },
+ "LANGUAGE": {
+ "TITLE": "Preferred Language",
+ "NOTE": "Choose the language you want to use.",
+ "UPDATE_SUCCESS": "Your Language settings have been updated successfully",
+ "UPDATE_ERROR": "There is an error while updating the language settings, please try again",
+ "USE_ACCOUNT_DEFAULT": "Use account default"
}
},
"MESSAGE_SIGNATURE_SECTION": {
@@ -73,6 +80,11 @@
"NOTE": "Η ενημέρωση του κωδικού κα επαναφέρει τα logins σε όλες τις συσκευές που έχετε συνδεθεί.",
"BTN_TEXT": "Αλλαγή κωδικού πρόσβασης"
},
+ "SECURITY_SECTION": {
+ "TITLE": "Security",
+ "NOTE": "Manage additional security features for your account.",
+ "MFA_BUTTON": "Manage Two-Factor Authentication"
+ },
"ACCESS_TOKEN": {
"TITLE": "Κώδικας Πρόσβασης (Access Token)",
"NOTE": "Χρησιμοποιείται σε περίπτωση εξωτερικής ενοποίησης της εφαρμογής με κώδικα (API)",
@@ -330,6 +342,7 @@
"REPORTS_LABEL": "Ετικέτες",
"REPORTS_INBOX": "Εισερχόμενα",
"REPORTS_TEAM": "Ομάδα",
+ "AGENT_ASSIGNMENT": "Agent Assignment",
"SET_AVAILABILITY_TITLE": "Ορίστε τον εαυτό σας ως",
"SET_YOUR_AVAILABILITY": "Ορίστε τη διαθεσιμότητά σας",
"SLA": "SLA",
@@ -350,7 +363,8 @@
"INFO_TEXT": "Αφήστε το σύστημα να σας σηματοδοτήσει αυτόματα εκτός σύνδεσης, όταν δεν χρησιμοποιείτε την εφαρμογή ή τον πίνακα ελέγχου.",
"INFO_SHORT": "Automatically mark offline when you aren't using the app."
},
- "DOCS": "Ανάγνωση εγγράφων"
+ "DOCS": "Ανάγνωση εγγράφων",
+ "SECURITY": "Security"
},
"BILLING_SETTINGS": {
"TITLE": "Χρεώσεις",
@@ -382,6 +396,77 @@
},
"NO_BILLING_USER": "Ο λογαριασμός χρέωσης έχει ρυθμιστεί. Παρακαλώ ανανεώστε τη σελίδα και προσπαθήστε ξανά."
},
+ "SECURITY_SETTINGS": {
+ "TITLE": "Security",
+ "DESCRIPTION": "Manage your account security settings.",
+ "LINK_TEXT": "Learn more about SAML SSO",
+ "SAML": {
+ "TITLE": "SAML SSO",
+ "NOTE": "Configure SAML single sign-on for your account. Users will authenticate through your identity provider instead of using email/password.",
+ "ACS_URL": {
+ "LABEL": "ACS URL",
+ "TOOLTIP": "Assertion Consumer Service URL - Configure this URL in your IdP as the destination for SAML responses"
+ },
+ "SSO_URL": {
+ "LABEL": "SSO URL",
+ "HELP": "The URL where SAML authentication requests will be sent",
+ "PLACEHOLDER": "https://your-idp.com/saml/sso"
+ },
+ "CERTIFICATE": {
+ "LABEL": "Signing certificate in PEM format",
+ "HELP": "The public certificate from your identity provider used to verify SAML responses",
+ "PLACEHOLDER": "-----BEGIN CERTIFICATE-----\nMIIC..."
+ },
+ "FINGERPRINT": {
+ "LABEL": "Fingerprint",
+ "TOOLTIP": "SHA-1 fingerprint of the certificate - Use this to verify the certificate in your IdP configuration"
+ },
+ "COPY_SUCCESS": "Ο κώδικας αντιγράφτηκε με επιτυχία στο πρόχειρο",
+ "SP_ENTITY_ID": {
+ "LABEL": "SP Entity ID",
+ "HELP": "Unique identifier for this application as a service provider (auto-generated).",
+ "TOOLTIP": "Unique identifier for Chatwoot as the Service Provider - Configure this in your IdP settings"
+ },
+ "IDP_ENTITY_ID": {
+ "LABEL": "Identity Provider Entity ID",
+ "HELP": "Unique identifier for your identity provider (usually found in IdP configuration)",
+ "PLACEHOLDER": "https://your-idp.com/saml"
+ },
+ "UPDATE_BUTTON": "Update SAML Settings",
+ "API": {
+ "SUCCESS": "SAML settings updated successfully",
+ "ERROR": "Failed to update SAML settings",
+ "ERROR_LOADING": "Failed to load SAML settings",
+ "DISABLED": "SAML settings disabled successfully"
+ },
+ "VALIDATION": {
+ "REQUIRED_FIELDS": "SSO URL, Identity Provider Entity ID, and Certificate are required fields",
+ "SSO_URL_ERROR": "Please enter a valid SSO URL",
+ "CERTIFICATE_ERROR": "Certificate is required",
+ "IDP_ENTITY_ID_ERROR": "Identity Provider Entity ID is required"
+ },
+ "ENTERPRISE_PAYWALL": {
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade to an Enterprise plan to access SAML single sign-on and other advanced security features.",
+ "ASK_ADMIN": "Please reach out to your administrator for the upgrade."
+ },
+ "PAYWALL": {
+ "TITLE": "Upgrade to enable SAML SSO",
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade your plan to get access to SAML single sign-on and other advanced features.",
+ "UPGRADE_NOW": "Upgrade now",
+ "CANCEL_ANYTIME": "You can change or cancel your plan anytime"
+ },
+ "ATTRIBUTE_MAPPING": {
+ "TITLE": "SAML Attribute Setup",
+ "DESCRIPTION": "The following attribute mappings must be configured in your identity provider"
+ },
+ "INFO_SECTION": {
+ "TITLE": "Service Provider Information",
+ "TOOLTIP": "Copy these values and configure them in your Identity Provider to establish the SAML connection"
+ }
+ }
+ },
"CREATE_ACCOUNT": {
"NO_ACCOUNT_WARNING": "Ωχ! Δεν μπορέσαμε να βρούμε κανένα λογαριασμό Chatwoot. Παρακαλούμε δημιουργήστε ένα νέο λογαριασμό για να συνεχίσετε.",
"NEW_ACCOUNT": "Νέος Λογαριασμός",
@@ -418,5 +503,255 @@
"SWITCH_TO_REPLY": "Εναλλαγή σε απάντηση",
"TOGGLE_SNOOZE_DROPDOWN": "Εναλλαγή αναβολής dropdown"
}
+ },
+ "ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent assignment",
+ "DESCRIPTION": "Define policies to effectively manage workload and route conversations based on the needs of inboxes and agents. Learn more here"
+ },
+ "ASSIGNMENT_POLICY": {
+ "TITLE": "Assignment policy",
+ "DESCRIPTION": "Manage how conversations get assigned in inboxes.",
+ "FEATURES": [
+ "Assign by conversations evenly or by available capacity",
+ "Add fair distribution rules to avoid overloading any agent",
+ "Add inboxes to a policy - one policy per inbox"
+ ]
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "TITLE": "Agent capacity policy",
+ "DESCRIPTION": "Manage workload for agents.",
+ "FEATURES": [
+ "Define maximum conversations per inbox",
+ "Create exceptions based on labels and time",
+ "Add agents to a policy - one policy per agent"
+ ]
+ }
+ },
+ "AGENT_ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Assignment policy",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "ORDER": "Order",
+ "PRIORITY": "Priority",
+ "ACTIVE": "Ενεργή",
+ "INACTIVE": "Inactive",
+ "POPOVER": "Added inboxes",
+ "EDIT": "Επεξεργασία"
+ },
+ "NO_RECORDS_FOUND": "No assignment policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create assignment policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy created successfully",
+ "ERROR_MESSAGE": "Failed to create assignment policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit assignment policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_INBOX_DIALOG": {
+ "TITLE": "Add inbox",
+ "DESCRIPTION": "{inboxName} inbox is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Άκυρο"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update assignment policy"
+ },
+ "INBOX_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Inbox added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Inbox removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove inbox from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Περιγραφή:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "STATUS": {
+ "LABEL": "Κατάσταση:",
+ "PLACEHOLDER": "Select status",
+ "ACTIVE": "Policy is active",
+ "INACTIVE": "Policy is inactive"
+ },
+ "ASSIGNMENT_ORDER": {
+ "LABEL": "Assignment order",
+ "ROUND_ROBIN": {
+ "LABEL": "Round robin",
+ "DESCRIPTION": "Assign conversations evenly among agents."
+ },
+ "BALANCED": {
+ "LABEL": "Balanced",
+ "DESCRIPTION": "Assign conversations based on available capacity."
+ }
+ },
+ "ASSIGNMENT_PRIORITY": {
+ "LABEL": "Assignment priority",
+ "EARLIEST_CREATED": {
+ "LABEL": "Earliest created",
+ "DESCRIPTION": "The conversation that was created first gets assigned first."
+ },
+ "LONGEST_WAITING": {
+ "LABEL": "Longest waiting",
+ "DESCRIPTION": "The conversation waiting the longest gets assigned first."
+ }
+ },
+ "FAIR_DISTRIBUTION": {
+ "LABEL": "Fair distribution policy",
+ "DESCRIPTION": "Set the maximum number of conversations that can be assigned per agent within a time window to avoid overloading any one agent. This required field defaults to 100 conversations per hour.",
+ "INPUT_MAX": "Assign max",
+ "DURATION": "Conversations per agent in every"
+ },
+ "INBOXES": {
+ "LABEL": "Added inboxes",
+ "DESCRIPTION": "Add inboxes for which this policy will be applicable.",
+ "ADD_BUTTON": "Add inbox",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select inboxes to add",
+ "ADD_BUTTON": "Προσθήκη"
+ },
+ "EMPTY_STATE": "No inboxes added to this policy, add an inbox to get started",
+ "API": {
+ "SUCCESS_MESSAGE": "Inbox successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Assignment policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete assignment policy"
+ }
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent capacity",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "POPOVER": "Added agents",
+ "EDIT": "Επεξεργασία"
+ },
+ "NO_RECORDS_FOUND": "No agent capacity policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create agent capacity policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy created successfully",
+ "ERROR_MESSAGE": "Failed to create agent capacity policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit agent capacity policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_AGENT_DIALOG": {
+ "TITLE": "Add agent",
+ "DESCRIPTION": "{agentName} is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Άκυρο"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update agent capacity policy"
+ },
+ "AGENT_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Agent added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Agent removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove agent from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Περιγραφή:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "INBOX_CAPACITY_LIMIT": {
+ "LABEL": "Inbox capacity limits",
+ "ADD_BUTTON": "Add inbox",
+ "FIELD": {
+ "SELECT_INBOX": "Select inbox",
+ "MAX_CONVERSATIONS": "Max conversations",
+ "SET_LIMIT": "Set limit"
+ },
+ "EMPTY_STATE": "No inbox limit set"
+ },
+ "EXCLUSION_RULES": {
+ "LABEL": "Exclusion rules",
+ "DESCRIPTION": "Conversations that satisfy the following conditions would not count towards agent capacity",
+ "TAGS": {
+ "LABEL": "Exclude conversations tagged with specific labels",
+ "ADD_TAG": "add tag",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select tags to add"
+ },
+ "EMPTY_STATE": "No tags added to this policy."
+ },
+ "DURATION": {
+ "LABEL": "Exclude conversations older than a specified duration",
+ "PLACEHOLDER": "Set time"
+ }
+ },
+ "USERS": {
+ "LABEL": "Assigned agents",
+ "DESCRIPTION": "Add agents for which this policy will be applicable.",
+ "ADD_BUTTON": "Add agent",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select agents to add",
+ "ADD_BUTTON": "Προσθήκη"
+ },
+ "EMPTY_STATE": "No agents added",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Agent capacity policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete agent capacity policy"
+ }
+ },
+ "DELETE_POLICY": {
+ "TITLE": "Delete policy",
+ "DESCRIPTION": "Are you sure you want to delete this policy? This action cannot be undone.",
+ "CONFIRM_BUTTON_LABEL": "Διαγραφή",
+ "CANCEL_BUTTON_LABEL": "Άκυρο"
+ }
}
}
diff --git a/app/javascript/dashboard/i18n/locale/el/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/el/whatsappTemplates.json
index 42b710727..48aa2fbcc 100644
--- a/app/javascript/dashboard/i18n/locale/el/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/el/whatsappTemplates.json
@@ -40,6 +40,7 @@
"BUTTON_LABEL": "Button {index}",
"COUPON_CODE": "Enter coupon code (max 15 chars)",
"MEDIA_URL_LABEL": "Enter {type} URL",
+ "DOCUMENT_NAME_PLACEHOLDER": "Enter document filename (e.g., Invoice_2025.pdf)",
"BUTTON_PARAMETER": "Enter button parameter"
}
}
diff --git a/app/javascript/dashboard/i18n/locale/en/automation.json b/app/javascript/dashboard/i18n/locale/en/automation.json
index 80274f488..43245a1d5 100644
--- a/app/javascript/dashboard/i18n/locale/en/automation.json
+++ b/app/javascript/dashboard/i18n/locale/en/automation.json
@@ -177,7 +177,8 @@
"REFERER_LINK": "Referrer Link",
"ASSIGNEE_NAME": "Assignee",
"TEAM_NAME": "Team",
- "PRIORITY": "Priority"
+ "PRIORITY": "Priority",
+ "LABELS": "Labels"
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/en/contact.json b/app/javascript/dashboard/i18n/locale/en/contact.json
index a7db8480f..21e8dd1b2 100644
--- a/app/javascript/dashboard/i18n/locale/en/contact.json
+++ b/app/javascript/dashboard/i18n/locale/en/contact.json
@@ -555,10 +555,12 @@
"WROTE": "wrote",
"YOU": "You",
"SAVE": "Save note",
+ "ADD_NOTE": "Add contact note",
"EXPAND": "Expand",
"COLLAPSE": "Collapse",
"NO_NOTES": "No notes, you can add notes from the contact details page.",
- "EMPTY_STATE": "There are no notes associated to this contact. You can add a note by typing in the box above."
+ "EMPTY_STATE": "There are no notes associated to this contact. You can add a note by typing in the box above.",
+ "CONVERSATION_EMPTY_STATE": "There are no notes yet. Use the Add note button to create one."
}
},
"EMPTY_STATE": {
diff --git a/app/javascript/dashboard/i18n/locale/en/conversation.json b/app/javascript/dashboard/i18n/locale/en/conversation.json
index 93f375e7f..9fd39b70f 100644
--- a/app/javascript/dashboard/i18n/locale/en/conversation.json
+++ b/app/javascript/dashboard/i18n/locale/en/conversation.json
@@ -71,6 +71,17 @@
"SHOW_LABELS": "Show labels",
"HIDE_LABELS": "Hide labels"
},
+ "VOICE_CALL": {
+ "INCOMING_CALL": "Incoming call",
+ "OUTGOING_CALL": "Outgoing call",
+ "CALL_IN_PROGRESS": "Call in progress",
+ "NO_ANSWER": "No answer",
+ "MISSED_CALL": "Missed call",
+ "CALL_ENDED": "Call ended",
+ "NOT_ANSWERED_YET": "Not answered yet",
+ "THEY_ANSWERED": "They answered",
+ "YOU_ANSWERED": "You answered"
+ },
"HEADER": {
"RESOLVE_ACTION": "Resolve",
"REOPEN_ACTION": "Reopen",
diff --git a/app/javascript/dashboard/i18n/locale/en/helpCenter.json b/app/javascript/dashboard/i18n/locale/en/helpCenter.json
index 16f108c0e..b47af9181 100644
--- a/app/javascript/dashboard/i18n/locale/en/helpCenter.json
+++ b/app/javascript/dashboard/i18n/locale/en/helpCenter.json
@@ -741,7 +741,8 @@
"LIVE_CHAT_WIDGET": {
"LABEL": "Live chat widget",
"PLACEHOLDER": "Select live chat widget",
- "HELP_TEXT": "Select a live chat widget that will appear on your help center"
+ "HELP_TEXT": "Select a live chat widget that will appear on your help center",
+ "NONE_OPTION": "No widget"
},
"BRAND_COLOR": {
"LABEL": "Brand color"
diff --git a/app/javascript/dashboard/i18n/locale/en/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/en/inboxMgmt.json
index f171914db..a525921db 100644
--- a/app/javascript/dashboard/i18n/locale/en/inboxMgmt.json
+++ b/app/javascript/dashboard/i18n/locale/en/inboxMgmt.json
@@ -272,8 +272,8 @@
},
"SUBMIT_BUTTON": "Create WhatsApp Channel",
"EMBEDDED_SIGNUP": {
- "TITLE": "Quick Setup with Meta",
- "DESC": "You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
+ "TITLE": "Quick setup with Meta",
+ "DESC": "Use the WhatsApp Embedded Signup flow to quickly connect new numbers. You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
"BENEFITS": {
"TITLE": "Benefits of Embedded Signup:",
"EASY_SETUP": "No manual configuration required",
@@ -281,9 +281,8 @@
"AUTO_CONFIG": "Automatic webhook and phone number configuration"
},
"LEARN_MORE": {
- "TEXT": "To learn more about integrated signup, pricing, and limitations, visit",
- "LINK_TEXT": "this link.",
- "LINK_URL": "https://developers.facebook.com/docs/whatsapp/embedded-signup/custom-flows/onboarding-business-app-users#limitations"
+ "TEXT": "To learn more about integrated signup, pricing, and limitations, visit {link}.",
+ "LINK_TEXT": "this link"
},
"SUBMIT_BUTTON": "Connect with WhatsApp Business",
"AUTH_PROCESSING": "Authenticating with Meta",
@@ -296,7 +295,9 @@
"INVALID_BUSINESS_DATA": "Invalid business data received from Facebook. Please try again.",
"SIGNUP_ERROR": "Signup error occurred",
"AUTH_NOT_COMPLETED": "Authentication not completed. Please restart the process.",
- "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured"
+ "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured",
+ "MANUAL_FALLBACK": "If your number is already connected to the WhatsApp Business Platform (API), or if you’re a tech provider onboarding your own number, please use the {link} flow",
+ "MANUAL_LINK_TEXT": "manual setup flow"
},
"API": {
"ERROR_MESSAGE": "We were not able to save the WhatsApp channel"
diff --git a/app/javascript/dashboard/i18n/locale/en/index.js b/app/javascript/dashboard/i18n/locale/en/index.js
index bc4a8312a..e93dcd88e 100644
--- a/app/javascript/dashboard/i18n/locale/en/index.js
+++ b/app/javascript/dashboard/i18n/locale/en/index.js
@@ -36,6 +36,7 @@ import sla from './sla.json';
import teamsSettings from './teamsSettings.json';
import whatsappTemplates from './whatsappTemplates.json';
import contentTemplates from './contentTemplates.json';
+import mfa from './mfa.json';
export default {
...advancedFilters,
@@ -76,4 +77,5 @@ export default {
...teamsSettings,
...whatsappTemplates,
...contentTemplates,
+ ...mfa,
};
diff --git a/app/javascript/dashboard/i18n/locale/en/integrations.json b/app/javascript/dashboard/i18n/locale/en/integrations.json
index c4399b0e9..8a812dff3 100644
--- a/app/javascript/dashboard/i18n/locale/en/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/en/integrations.json
@@ -759,6 +759,7 @@
"SELECTED": "{count} selected",
"SELECT_ALL": "Select all ({count})",
"UNSELECT_ALL": "Unselect all ({count})",
+ "SEARCH_PLACEHOLDER": "Search FAQs...",
"BULK_APPROVE_BUTTON": "Approve",
"BULK_DELETE_BUTTON": "Delete",
"BULK_APPROVE": {
diff --git a/app/javascript/dashboard/i18n/locale/en/login.json b/app/javascript/dashboard/i18n/locale/en/login.json
index ec5658db2..864c76359 100644
--- a/app/javascript/dashboard/i18n/locale/en/login.json
+++ b/app/javascript/dashboard/i18n/locale/en/login.json
@@ -22,6 +22,20 @@
},
"FORGOT_PASSWORD": "Forgot your password?",
"CREATE_NEW_ACCOUNT": "Create a new account",
- "SUBMIT": "Login"
+ "SUBMIT": "Login",
+ "SAML": {
+ "LABEL": "Log in via SSO",
+ "TITLE": "Initiate Single Sign-on (SSO)",
+ "SUBTITLE": "Enter your work email to access your organization",
+ "BACK_TO_LOGIN": "Login via Password",
+ "WORK_EMAIL": {
+ "LABEL": "Work Email",
+ "PLACEHOLDER": "Enter your work email"
+ },
+ "SUBMIT": "Continue with SSO",
+ "API": {
+ "ERROR_MESSAGE": "SSO authentication failed"
+ }
+ }
}
}
diff --git a/app/javascript/dashboard/i18n/locale/en/mfa.json b/app/javascript/dashboard/i18n/locale/en/mfa.json
new file mode 100644
index 000000000..f7556fdcf
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/en/mfa.json
@@ -0,0 +1,106 @@
+{
+ "MFA_SETTINGS": {
+ "TITLE": "Two-Factor Authentication",
+ "SUBTITLE": "Secure your account with TOTP-based authentication",
+ "DESCRIPTION": "Add an extra layer of security to your account using a time-based one-time password (TOTP)",
+ "STATUS_TITLE": "Authentication Status",
+ "STATUS_DESCRIPTION": "Manage your two-factor authentication settings and backup recovery codes",
+ "ENABLED": "Enabled",
+ "DISABLED": "Disabled",
+ "STATUS_ENABLED": "Two-factor authentication is active",
+ "STATUS_ENABLED_DESC": "Your account is protected with an additional layer of security",
+ "ENABLE_BUTTON": "Enable Two-Factor Authentication",
+ "ENHANCE_SECURITY": "Enhance Your Account Security",
+ "ENHANCE_SECURITY_DESC": "Two-factor authentication adds an extra layer of security by requiring a verification code from your authenticator app in addition to your password.",
+ "SETUP": {
+ "STEP_NUMBER_1": "1",
+ "STEP_NUMBER_2": "2",
+ "STEP1_TITLE": "Scan QR Code with Your Authenticator App",
+ "STEP1_DESCRIPTION": "Use Google Authenticator, Authy, or any TOTP-compatible app",
+ "LOADING_QR": "Loading...",
+ "MANUAL_ENTRY": "Can't scan? Enter code manually",
+ "SECRET_KEY": "Secret Key",
+ "COPY": "Copy",
+ "ENTER_CODE": "Enter the 6-digit code from your authenticator app",
+ "ENTER_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify & Continue",
+ "CANCEL": "Cancel",
+ "ERROR_STARTING": "MFA not enabled. Please contact administrator.",
+ "INVALID_CODE": "Invalid verification code",
+ "SECRET_COPIED": "Secret key copied to clipboard",
+ "SUCCESS": "Two-factor authentication has been enabled successfully"
+ },
+ "BACKUP": {
+ "TITLE": "Save Your Backup Codes",
+ "DESCRIPTION": "Keep these codes safe. Each can be used once if you lose access to your authenticator",
+ "IMPORTANT": "Important:",
+ "IMPORTANT_NOTE": " Save these codes in a secure location. You won't be able to see them again.",
+ "DOWNLOAD": "Download",
+ "COPY_ALL": "Copy All",
+ "CONFIRM": "I have saved my backup codes in a secure location and understand that I won't be able to see them again",
+ "COMPLETE_SETUP": "Complete Setup",
+ "CODES_COPIED": "Backup codes copied to clipboard"
+ },
+ "MANAGEMENT": {
+ "BACKUP_CODES": "Backup Codes",
+ "BACKUP_CODES_DESC": "Generate new codes if you've lost or used your existing ones",
+ "REGENERATE": "Regenerate Backup Codes",
+ "DISABLE_MFA": "Disable 2FA",
+ "DISABLE_MFA_DESC": "Remove two-factor authentication from your account",
+ "DISABLE_BUTTON": "Disable Two-Factor Authentication"
+ },
+ "DISABLE": {
+ "TITLE": "Disable Two-Factor Authentication",
+ "DESCRIPTION": "You'll need to enter your password and a verification code to disable two-factor authentication.",
+ "PASSWORD": "Password",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Disable 2FA",
+ "CANCEL": "Cancel",
+ "SUCCESS": "Two-factor authentication has been disabled",
+ "ERROR": "Failed to disable MFA. Please check your credentials."
+ },
+ "REGENERATE": {
+ "TITLE": "Regenerate Backup Codes",
+ "DESCRIPTION": "This will invalidate your existing backup codes and generate new ones. Enter your verification code to continue.",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Generate New Codes",
+ "CANCEL": "Cancel",
+ "NEW_CODES_TITLE": "New Backup Codes Generated",
+ "NEW_CODES_DESC": "Your old backup codes have been invalidated. Save these new codes in a secure location.",
+ "CODES_IMPORTANT": "Important:",
+ "CODES_IMPORTANT_NOTE": " Each code can only be used once. Save them before closing this window.",
+ "DOWNLOAD_CODES": "Download Codes",
+ "COPY_ALL_CODES": "Copy All Codes",
+ "CODES_SAVED": "I've Saved My Codes",
+ "SUCCESS": "New backup codes have been generated",
+ "ERROR": "Failed to regenerate backup codes"
+ }
+ },
+ "MFA_VERIFICATION": {
+ "TITLE": "Two-Factor Authentication",
+ "DESCRIPTION": "Enter your verification code to continue",
+ "AUTHENTICATOR_APP": "Authenticator App",
+ "BACKUP_CODE": "Backup Code",
+ "ENTER_OTP_CODE": "Enter 6-digit code from your authenticator app",
+ "ENTER_BACKUP_CODE": "Enter one of your backup codes",
+ "BACKUP_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify",
+ "TRY_ANOTHER_METHOD": "Try another verification method",
+ "CANCEL_LOGIN": "Cancel and return to login",
+ "HELP_TEXT": "Having trouble signing in?",
+ "LEARN_MORE": "Learn more about 2FA",
+ "HELP_MODAL": {
+ "TITLE": "Two-Factor Authentication Help",
+ "AUTHENTICATOR_TITLE": "Using an Authenticator App",
+ "AUTHENTICATOR_DESC": "Open your authenticator app (Google Authenticator, Authy, etc.) and enter the 6-digit code shown for your account.",
+ "BACKUP_TITLE": "Using a Backup Code",
+ "BACKUP_DESC": "If you don't have access to your authenticator app, you can use one of the backup codes you saved when setting up 2FA. Each code can only be used once.",
+ "CONTACT_TITLE": "Need More Help?",
+ "CONTACT_DESC_CLOUD": "If you've lost access to both your authenticator app and backup codes, please reach out to Chatwoot support for assistance.",
+ "CONTACT_DESC_SELF_HOSTED": "If you've lost access to both your authenticator app and backup codes, please contact your administrator for assistance."
+ },
+ "VERIFICATION_FAILED": "Verification failed. Please try again."
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/en/settings.json b/app/javascript/dashboard/i18n/locale/en/settings.json
index d547538db..9ddc3b805 100644
--- a/app/javascript/dashboard/i18n/locale/en/settings.json
+++ b/app/javascript/dashboard/i18n/locale/en/settings.json
@@ -51,6 +51,13 @@
"LARGER": "Larger",
"EXTRA_LARGE": "Extra Large"
}
+ },
+ "LANGUAGE": {
+ "TITLE": "Preferred Language",
+ "NOTE": "Choose the language you want to use.",
+ "UPDATE_SUCCESS": "Your Language settings have been updated successfully",
+ "UPDATE_ERROR": "There is an error while updating the language settings, please try again",
+ "USE_ACCOUNT_DEFAULT": "Use account default"
}
},
"MESSAGE_SIGNATURE_SECTION": {
@@ -73,6 +80,11 @@
"NOTE": "Updating your password would reset your logins in multiple devices.",
"BTN_TEXT": "Change password"
},
+ "SECURITY_SECTION": {
+ "TITLE": "Security",
+ "NOTE": "Manage additional security features for your account.",
+ "MFA_BUTTON": "Manage Two-Factor Authentication"
+ },
"ACCESS_TOKEN": {
"TITLE": "Access Token",
"NOTE": "This token can be used if you are building an API based integration",
@@ -330,6 +342,7 @@
"REPORTS_LABEL": "Labels",
"REPORTS_INBOX": "Inbox",
"REPORTS_TEAM": "Team",
+ "AGENT_ASSIGNMENT": "Agent Assignment",
"SET_AVAILABILITY_TITLE": "Set yourself as",
"SET_YOUR_AVAILABILITY": "Set your availability",
"SLA": "SLA",
@@ -350,7 +363,8 @@
"INFO_TEXT": "Let the system automatically mark you offline when you aren't using the app or dashboard.",
"INFO_SHORT": "Automatically mark offline when you aren't using the app."
},
- "DOCS": "Read docs"
+ "DOCS": "Read docs",
+ "SECURITY": "Security"
},
"BILLING_SETTINGS": {
"TITLE": "Billing",
@@ -382,6 +396,77 @@
},
"NO_BILLING_USER": "Your billing account is being configured. Please refresh the page and try again."
},
+ "SECURITY_SETTINGS": {
+ "TITLE": "Security",
+ "DESCRIPTION": "Manage your account security settings.",
+ "LINK_TEXT": "Learn more about SAML SSO",
+ "SAML": {
+ "TITLE": "SAML SSO",
+ "NOTE": "Configure SAML single sign-on for your account. Users will authenticate through your identity provider instead of using email/password.",
+ "ACS_URL": {
+ "LABEL": "ACS URL",
+ "TOOLTIP": "Assertion Consumer Service URL - Configure this URL in your IdP as the destination for SAML responses"
+ },
+ "SSO_URL": {
+ "LABEL": "SSO URL",
+ "HELP": "The URL where SAML authentication requests will be sent",
+ "PLACEHOLDER": "https://your-idp.com/saml/sso"
+ },
+ "CERTIFICATE": {
+ "LABEL": "Signing certificate in PEM format",
+ "HELP": "The public certificate from your identity provider used to verify SAML responses",
+ "PLACEHOLDER": "-----BEGIN CERTIFICATE-----\nMIIC..."
+ },
+ "FINGERPRINT": {
+ "LABEL": "Fingerprint",
+ "TOOLTIP": "SHA-1 fingerprint of the certificate - Use this to verify the certificate in your IdP configuration"
+ },
+ "COPY_SUCCESS": "Copied to clipboard",
+ "SP_ENTITY_ID": {
+ "LABEL": "SP Entity ID",
+ "HELP": "Unique identifier for this application as a service provider (auto-generated).",
+ "TOOLTIP": "Unique identifier for Chatwoot as the Service Provider - Configure this in your IdP settings"
+ },
+ "IDP_ENTITY_ID": {
+ "LABEL": "Identity Provider Entity ID",
+ "HELP": "Unique identifier for your identity provider (usually found in IdP configuration)",
+ "PLACEHOLDER": "https://your-idp.com/saml"
+ },
+ "UPDATE_BUTTON": "Update SAML Settings",
+ "API": {
+ "SUCCESS": "SAML settings updated successfully",
+ "ERROR": "Failed to update SAML settings",
+ "ERROR_LOADING": "Failed to load SAML settings",
+ "DISABLED": "SAML settings disabled successfully"
+ },
+ "VALIDATION": {
+ "REQUIRED_FIELDS": "SSO URL, Identity Provider Entity ID, and Certificate are required fields",
+ "SSO_URL_ERROR": "Please enter a valid SSO URL",
+ "CERTIFICATE_ERROR": "Certificate is required",
+ "IDP_ENTITY_ID_ERROR": "Identity Provider Entity ID is required"
+ },
+ "ENTERPRISE_PAYWALL": {
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade to an Enterprise plan to access SAML single sign-on and other advanced security features.",
+ "ASK_ADMIN": "Please reach out to your administrator for the upgrade."
+ },
+ "PAYWALL": {
+ "TITLE": "Upgrade to enable SAML SSO",
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade your plan to get access to SAML single sign-on and other advanced features.",
+ "UPGRADE_NOW": "Upgrade now",
+ "CANCEL_ANYTIME": "You can change or cancel your plan anytime"
+ },
+ "ATTRIBUTE_MAPPING": {
+ "TITLE": "SAML Attribute Setup",
+ "DESCRIPTION": "The following attribute mappings must be configured in your identity provider"
+ },
+ "INFO_SECTION": {
+ "TITLE": "Service Provider Information",
+ "TOOLTIP": "Copy these values and configure them in your Identity Provider to establish the SAML connection"
+ }
+ }
+ },
"CREATE_ACCOUNT": {
"NO_ACCOUNT_WARNING": "Uh oh! We could not find any Chatwoot accounts. Please create a new account to continue.",
"NEW_ACCOUNT": "New Account",
@@ -418,5 +503,255 @@
"SWITCH_TO_REPLY": "Switch to Reply",
"TOGGLE_SNOOZE_DROPDOWN": "Toggle snooze dropdown"
}
+ },
+ "ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent assignment",
+ "DESCRIPTION": "Define policies to effectively manage workload and route conversations based on the needs of inboxes and agents. Learn more here"
+ },
+ "ASSIGNMENT_POLICY": {
+ "TITLE": "Assignment policy",
+ "DESCRIPTION": "Manage how conversations get assigned in inboxes.",
+ "FEATURES": [
+ "Assign by conversations evenly or by available capacity",
+ "Add fair distribution rules to avoid overloading any agent",
+ "Add inboxes to a policy - one policy per inbox"
+ ]
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "TITLE": "Agent capacity policy",
+ "DESCRIPTION": "Manage workload for agents.",
+ "FEATURES": [
+ "Define maximum conversations per inbox",
+ "Create exceptions based on labels and time",
+ "Add agents to a policy - one policy per agent"
+ ]
+ }
+ },
+ "AGENT_ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Assignment policy",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "ORDER": "Order",
+ "PRIORITY": "Priority",
+ "ACTIVE": "Active",
+ "INACTIVE": "Inactive",
+ "POPOVER": "Added inboxes",
+ "EDIT": "Edit"
+ },
+ "NO_RECORDS_FOUND": "No assignment policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create assignment policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy created successfully",
+ "ERROR_MESSAGE": "Failed to create assignment policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit assignment policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_INBOX_DIALOG": {
+ "TITLE": "Add inbox",
+ "DESCRIPTION": "{inboxName} inbox is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Cancel"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update assignment policy"
+ },
+ "INBOX_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Inbox added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Inbox removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove inbox from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Description:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "STATUS": {
+ "LABEL": "Status:",
+ "PLACEHOLDER": "Select status",
+ "ACTIVE": "Policy is active",
+ "INACTIVE": "Policy is inactive"
+ },
+ "ASSIGNMENT_ORDER": {
+ "LABEL": "Assignment order",
+ "ROUND_ROBIN": {
+ "LABEL": "Round robin",
+ "DESCRIPTION": "Assign conversations evenly among agents."
+ },
+ "BALANCED": {
+ "LABEL": "Balanced",
+ "DESCRIPTION": "Assign conversations based on available capacity."
+ }
+ },
+ "ASSIGNMENT_PRIORITY": {
+ "LABEL": "Assignment priority",
+ "EARLIEST_CREATED": {
+ "LABEL": "Earliest created",
+ "DESCRIPTION": "The conversation that was created first gets assigned first."
+ },
+ "LONGEST_WAITING": {
+ "LABEL": "Longest waiting",
+ "DESCRIPTION": "The conversation waiting the longest gets assigned first."
+ }
+ },
+ "FAIR_DISTRIBUTION": {
+ "LABEL": "Fair distribution policy",
+ "DESCRIPTION": "Set the maximum number of conversations that can be assigned per agent within a time window to avoid overloading any one agent. This required field defaults to 100 conversations per hour.",
+ "INPUT_MAX": "Assign max",
+ "DURATION": "Conversations per agent in every"
+ },
+ "INBOXES": {
+ "LABEL": "Added inboxes",
+ "DESCRIPTION": "Add inboxes for which this policy will be applicable.",
+ "ADD_BUTTON": "Add inbox",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select inboxes to add",
+ "ADD_BUTTON": "Add"
+ },
+ "EMPTY_STATE": "No inboxes added to this policy, add an inbox to get started",
+ "API": {
+ "SUCCESS_MESSAGE": "Inbox successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Assignment policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete assignment policy"
+ }
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent capacity",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "POPOVER": "Added agents",
+ "EDIT": "Edit"
+ },
+ "NO_RECORDS_FOUND": "No agent capacity policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create agent capacity policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy created successfully",
+ "ERROR_MESSAGE": "Failed to create agent capacity policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit agent capacity policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_AGENT_DIALOG": {
+ "TITLE": "Add agent",
+ "DESCRIPTION": "{agentName} is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Cancel"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update agent capacity policy"
+ },
+ "AGENT_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Agent added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Agent removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove agent from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Description:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "INBOX_CAPACITY_LIMIT": {
+ "LABEL": "Inbox capacity limits",
+ "ADD_BUTTON": "Add inbox",
+ "FIELD": {
+ "SELECT_INBOX": "Select inbox",
+ "MAX_CONVERSATIONS": "Max conversations",
+ "SET_LIMIT": "Set limit"
+ },
+ "EMPTY_STATE": "No inbox limit set"
+ },
+ "EXCLUSION_RULES": {
+ "LABEL": "Exclusion rules",
+ "DESCRIPTION": "Conversations that satisfy the following conditions would not count towards agent capacity",
+ "TAGS": {
+ "LABEL": "Exclude conversations tagged with specific labels",
+ "ADD_TAG": "add tag",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select tags to add"
+ },
+ "EMPTY_STATE": "No tags added to this policy."
+ },
+ "DURATION": {
+ "LABEL": "Exclude conversations older than a specified duration",
+ "PLACEHOLDER": "Set time"
+ }
+ },
+ "USERS": {
+ "LABEL": "Assigned agents",
+ "DESCRIPTION": "Add agents for which this policy will be applicable.",
+ "ADD_BUTTON": "Add agent",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select agents to add",
+ "ADD_BUTTON": "Add"
+ },
+ "EMPTY_STATE": "No agents added",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Agent capacity policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete agent capacity policy"
+ }
+ },
+ "DELETE_POLICY": {
+ "TITLE": "Delete policy",
+ "DESCRIPTION": "Are you sure you want to delete this policy? This action cannot be undone.",
+ "CONFIRM_BUTTON_LABEL": "Delete",
+ "CANCEL_BUTTON_LABEL": "Cancel"
+ }
}
}
diff --git a/app/javascript/dashboard/i18n/locale/en/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/en/whatsappTemplates.json
index 5f53faaa8..cf28312dc 100644
--- a/app/javascript/dashboard/i18n/locale/en/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/en/whatsappTemplates.json
@@ -40,6 +40,7 @@
"BUTTON_LABEL": "Button {index}",
"COUPON_CODE": "Enter coupon code (max 15 chars)",
"MEDIA_URL_LABEL": "Enter {type} URL",
+ "DOCUMENT_NAME_PLACEHOLDER": "Enter document filename (e.g., Invoice_2025.pdf)",
"BUTTON_PARAMETER": "Enter button parameter"
}
}
diff --git a/app/javascript/dashboard/i18n/locale/es/agentBots.json b/app/javascript/dashboard/i18n/locale/es/agentBots.json
index 6b3e904b6..747570e05 100644
--- a/app/javascript/dashboard/i18n/locale/es/agentBots.json
+++ b/app/javascript/dashboard/i18n/locale/es/agentBots.json
@@ -87,11 +87,11 @@
"ERRORS": {
"NAME": "El nombre del bot es obligatorio",
"URL": "Dirección del webhook es requerida",
- "VALID_URL": "Please enter a valid URL starting with http:// or https://"
+ "VALID_URL": "Por favor, introduzca una URL válida comenzando con http:// o https://"
},
"CANCEL": "Cancelar",
- "CREATE": "Create Bot",
- "UPDATE": "Update Bot"
+ "CREATE": "Crear Bot",
+ "UPDATE": "Actualizar Bot"
},
"WEBHOOK": {
"DESCRIPTION": "Configure el webhook del bot para integrarse con sus servicios personalizados. El bot recibirá y procesará eventos de conversaciones y podrá responder a ellos."
diff --git a/app/javascript/dashboard/i18n/locale/es/automation.json b/app/javascript/dashboard/i18n/locale/es/automation.json
index 33bec1729..18fcb3652 100644
--- a/app/javascript/dashboard/i18n/locale/es/automation.json
+++ b/app/javascript/dashboard/i18n/locale/es/automation.json
@@ -177,7 +177,8 @@
"REFERER_LINK": "Enlace de referencia",
"ASSIGNEE_NAME": "Asignado a",
"TEAM_NAME": "Equipo",
- "PRIORITY": "Prioridad"
+ "PRIORITY": "Prioridad",
+ "LABELS": "Etiquetas"
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/es/campaign.json b/app/javascript/dashboard/i18n/locale/es/campaign.json
index bcbfec249..ff7bcce65 100644
--- a/app/javascript/dashboard/i18n/locale/es/campaign.json
+++ b/app/javascript/dashboard/i18n/locale/es/campaign.json
@@ -142,7 +142,7 @@
"NEW_CAMPAIGN": "Crear campaña",
"EMPTY_STATE": {
"TITLE": "No WhatsApp campaigns are available",
- "SUBTITLE": "Launch a WhatsApp campaign to reach your customers directly. Send offers or make announcements with ease. Click 'Create campaign' to get started."
+ "SUBTITLE": "Lanza una campaña de WhatsApp para conectar con tus clientes directamente. Envía ofertas o has anuncios fácilmente. Haz clic en 'Crear campaña' para comenzar."
},
"CARD": {
"STATUS": {
@@ -155,7 +155,7 @@
}
},
"CREATE": {
- "TITLE": "Create WhatsApp campaign",
+ "TITLE": "Crear campaña de WhatsApp",
"CANCEL_BUTTON_TEXT": "Cancelar",
"CREATE_BUTTON_TEXT": "Crear",
"FORM": {
diff --git a/app/javascript/dashboard/i18n/locale/es/contact.json b/app/javascript/dashboard/i18n/locale/es/contact.json
index 08fcbbaf9..939c0a7be 100644
--- a/app/javascript/dashboard/i18n/locale/es/contact.json
+++ b/app/javascript/dashboard/i18n/locale/es/contact.json
@@ -610,6 +610,15 @@
"SEND_MESSAGE": "Enviar mensaje"
}
},
+ "TWILIO_OPTIONS": {
+ "LABEL": "Seleccionar plantilla",
+ "SEARCH_PLACEHOLDER": "Buscar plantillas",
+ "EMPTY_STATE": "No se encontraron plantillas",
+ "TEMPLATE_PARSER": {
+ "BACK": "Volver",
+ "SEND_MESSAGE": "Enviar mensaje"
+ }
+ },
"ACTION_BUTTONS": {
"DISCARD": "Descartar",
"SEND": "Enviar ({keyCode})"
diff --git a/app/javascript/dashboard/i18n/locale/es/contactFilters.json b/app/javascript/dashboard/i18n/locale/es/contactFilters.json
index 64cacd968..f291692bc 100644
--- a/app/javascript/dashboard/i18n/locale/es/contactFilters.json
+++ b/app/javascript/dashboard/i18n/locale/es/contactFilters.json
@@ -48,7 +48,8 @@
"CREATED_AT": "Creado el",
"LAST_ACTIVITY": "Última actividad",
"REFERER_LINK": "Enlace de referencia",
- "BLOCKED": "Bloqueado"
+ "BLOCKED": "Bloqueado",
+ "LABELS": "Etiquetas"
},
"GROUPS": {
"STANDARD_FILTERS": "Filtros estándar",
diff --git a/app/javascript/dashboard/i18n/locale/es/contentTemplates.json b/app/javascript/dashboard/i18n/locale/es/contentTemplates.json
new file mode 100644
index 000000000..a9187b6dd
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/es/contentTemplates.json
@@ -0,0 +1,51 @@
+{
+ "CONTENT_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Twilio Templates",
+ "SUBTITLE": "Select the Twilio template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Buscar plantillas",
+ "NO_TEMPLATES_FOUND": "No se encontraron plantillas para",
+ "NO_CONTENT": "Sin contenido",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Categoría",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No Twilio templates available. Click refresh to sync templates from Twilio.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Idioma",
+ "TEMPLATE_BODY": "Cuerpo de plantilla",
+ "CATEGORY": "Categoría"
+ },
+ "TYPES": {
+ "MEDIA": "Media",
+ "QUICK_REPLY": "Quick Reply",
+ "TEXT": "Texto"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variables",
+ "LANGUAGE": "Idioma",
+ "CATEGORY": "Categoría",
+ "VARIABLE_PLACEHOLDER": "Introduzca el valor de {variable}",
+ "GO_BACK_LABEL": "Volver",
+ "SEND_MESSAGE_LABEL": "Enviar mensaje",
+ "FORM_ERROR_MESSAGE": "Por favor, rellene todas las variables antes de enviar",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "MEDIA_URL_LABEL": "Ingrese la URL completa",
+ "MEDIA_URL_PLACEHOLDER": "https://example.com/image.jpg"
+ },
+ "FORM": {
+ "BACK_BUTTON": "Atrás",
+ "SEND_MESSAGE_BUTTON": "Enviar mensaje"
+ }
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/es/conversation.json b/app/javascript/dashboard/i18n/locale/es/conversation.json
index 3ef706051..5b02ceaba 100644
--- a/app/javascript/dashboard/i18n/locale/es/conversation.json
+++ b/app/javascript/dashboard/i18n/locale/es/conversation.json
@@ -35,6 +35,11 @@
"API_HOURS_WINDOW": "Solo puedes responder a esta conversación dentro de {hours} horas",
"NOT_ASSIGNED_TO_YOU": "Esta conversación no te está asignada. ¿Quieres asignarla a ti mismo?",
"ASSIGN_TO_ME": "Asignar a mi",
+ "BOT_HANDOFF_MESSAGE": "You are responding to a conversation which is currently handled by an assistant or a bot.",
+ "BOT_HANDOFF_ACTION": "Mark open and assign to you",
+ "BOT_HANDOFF_REOPEN_ACTION": "Mark conversation open",
+ "BOT_HANDOFF_SUCCESS": "Conversation has been handed over to you",
+ "BOT_HANDOFF_ERROR": "Failed to take over the conversation. Please try again.",
"TWILIO_WHATSAPP_CAN_REPLY": "Sólo puede responder a esta conversación usando una plantilla de mensaje debido a",
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "Restricción de la ventana de mensajes de 24 horas",
"OLD_INSTAGRAM_INBOX_REPLY_BANNER": "Esta cuenta de Instagram fue migrada a la nueva bandeja de entrada del canal Instagram. Todos los nuevos mensajes aparecerán allí. Ya no podrás enviar mensajes de esta conversación.",
@@ -66,6 +71,17 @@
"SHOW_LABELS": "Mostrar etiquetas",
"HIDE_LABELS": "Ocultar etiquetas"
},
+ "VOICE_CALL": {
+ "INCOMING_CALL": "Incoming call",
+ "OUTGOING_CALL": "Outgoing call",
+ "CALL_IN_PROGRESS": "Call in progress",
+ "NO_ANSWER": "No answer",
+ "MISSED_CALL": "Missed call",
+ "CALL_ENDED": "Call ended",
+ "NOT_ANSWERED_YET": "Not answered yet",
+ "THEY_ANSWERED": "They answered",
+ "YOU_ANSWERED": "You answered"
+ },
"HEADER": {
"RESOLVE_ACTION": "Resolver",
"REOPEN_ACTION": "Reabrir",
diff --git a/app/javascript/dashboard/i18n/locale/es/helpCenter.json b/app/javascript/dashboard/i18n/locale/es/helpCenter.json
index 0a7039883..bff0af50c 100644
--- a/app/javascript/dashboard/i18n/locale/es/helpCenter.json
+++ b/app/javascript/dashboard/i18n/locale/es/helpCenter.json
@@ -741,7 +741,8 @@
"LIVE_CHAT_WIDGET": {
"LABEL": "Live chat widget",
"PLACEHOLDER": "Select live chat widget",
- "HELP_TEXT": "Select a live chat widget that will appear on your help center"
+ "HELP_TEXT": "Select a live chat widget that will appear on your help center",
+ "NONE_OPTION": "No widget"
},
"BRAND_COLOR": {
"LABEL": "Brand color"
@@ -807,6 +808,58 @@
"ERROR_MESSAGE": "Unable to update portal"
}
}
+ },
+ "PDF_UPLOAD": {
+ "TITLE": "Upload PDF Document",
+ "DESCRIPTION": "Upload a PDF document to automatically generate FAQs using AI",
+ "DRAG_DROP_TEXT": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "ADDITIONAL_CONTEXT_LABEL": "Additional Context (Optional)",
+ "ADDITIONAL_CONTEXT_PLACEHOLDER": "Provide any additional context or instructions for FAQ generation...",
+ "UPLOADING": "Subiendo...",
+ "UPLOAD": "Upload & Process",
+ "CANCEL": "Cancelar",
+ "ERROR_INVALID_TYPE": "Please select a valid PDF file",
+ "ERROR_FILE_TOO_LARGE": "File size must be less than 512MB",
+ "ERROR_UPLOAD_FAILED": "Failed to upload PDF. Please try again."
+ },
+ "PDF_DOCUMENTS": {
+ "TITLE": "PDF Documents",
+ "DESCRIPTION": "Manage uploaded PDF documents and generate FAQs from them",
+ "UPLOAD_PDF": "Upload PDF",
+ "UPLOAD_FIRST_PDF": "Upload your first PDF",
+ "UPLOADED_BY": "Uploaded by",
+ "GENERATE_FAQS": "Generate FAQs",
+ "GENERATING": "Generando...",
+ "CONFIRM_DELETE": "¿Está seguro que desea borrar {filename}?",
+ "EMPTY_STATE": {
+ "TITLE": "No PDF documents yet",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQs using AI"
+ },
+ "STATUS": {
+ "UPLOADED": "Ready",
+ "PROCESSING": "Processing",
+ "PROCESSED": "Completado",
+ "FAILED": "Failed"
+ }
+ },
+ "CONTENT_GENERATION": {
+ "TITLE": "Content Generation",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQ content using AI",
+ "UPLOAD_TITLE": "Upload PDF Document",
+ "DRAG_DROP": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "UPLOADING": "Processing document...",
+ "UPLOAD_SUCCESS": "Document processed successfully!",
+ "UPLOAD_ERROR": "Failed to upload document. Please try again.",
+ "INVALID_FILE_TYPE": "Please select a valid PDF file",
+ "FILE_TOO_LARGE": "File size must be less than 512MB",
+ "GENERATED_CONTENT": "Generated FAQ Content",
+ "PUBLISH_SELECTED": "Publish Selected",
+ "PUBLISHING": "Publishing...",
+ "FROM_DOCUMENT": "From document",
+ "NO_CONTENT": "No generated content available. Upload a PDF document to get started.",
+ "LOADING": "Loading generated content..."
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/es/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/es/inboxMgmt.json
index bb587c4cf..37919345d 100644
--- a/app/javascript/dashboard/i18n/locale/es/inboxMgmt.json
+++ b/app/javascript/dashboard/i18n/locale/es/inboxMgmt.json
@@ -272,8 +272,8 @@
},
"SUBMIT_BUTTON": "Crear canal de WhatsApp",
"EMBEDDED_SIGNUP": {
- "TITLE": "Quick Setup with Meta",
- "DESC": "You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
+ "TITLE": "Quick setup with Meta",
+ "DESC": "Use the WhatsApp Embedded Signup flow to quickly connect new numbers. You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
"BENEFITS": {
"TITLE": "Benefits of Embedded Signup:",
"EASY_SETUP": "No manual configuration required",
@@ -281,9 +281,8 @@
"AUTO_CONFIG": "Automatic webhook and phone number configuration"
},
"LEARN_MORE": {
- "TEXT": "To learn more about integrated signup, pricing, and limitations, visit",
- "LINK_TEXT": "this link.",
- "LINK_URL": "https://developers.facebook.com/docs/whatsapp/embedded-signup/custom-flows/onboarding-business-app-users#limitations"
+ "TEXT": "To learn more about integrated signup, pricing, and limitations, visit {link}.",
+ "LINK_TEXT": "this link"
},
"SUBMIT_BUTTON": "Connect with WhatsApp Business",
"AUTH_PROCESSING": "Authenticating with Meta",
@@ -296,7 +295,9 @@
"INVALID_BUSINESS_DATA": "Invalid business data received from Facebook. Please try again.",
"SIGNUP_ERROR": "Signup error occurred",
"AUTH_NOT_COMPLETED": "Authentication not completed. Please restart the process.",
- "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured"
+ "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured",
+ "MANUAL_FALLBACK": "If your number is already connected to the WhatsApp Business Platform (API), or if you’re a tech provider onboarding your own number, please use the {link} flow",
+ "MANUAL_LINK_TEXT": "manual setup flow"
},
"API": {
"ERROR_MESSAGE": "No pudimos guardar el canal de WhatsApp"
@@ -424,7 +425,51 @@
},
"AUTH": {
"TITLE": "Elija un canal",
- "DESC": "Chatwoot soporta widgets de Live Chat, Facebook Messenger, perfiles de Twitter, WhatsApp, correos electrónicos, etc., como canales. Si quieres construir un canal personalizado, puedes crearlo usando el canal API. Para empezar, elige uno de los canales a continuación."
+ "DESC": "Chatwoot soporta widgets de Live Chat, Facebook Messenger, perfiles de Twitter, WhatsApp, correos electrónicos, etc., como canales. Si quieres construir un canal personalizado, puedes crearlo usando el canal API. Para empezar, elige uno de los canales a continuación.",
+ "TITLE_NEXT": "Complete the setup",
+ "TITLE_FINISH": "Voilà!",
+ "CHANNEL": {
+ "WEBSITE": {
+ "TITLE": "Sitio web",
+ "DESCRIPTION": "Create a live-chat widget"
+ },
+ "FACEBOOK": {
+ "TITLE": "Facebook",
+ "DESCRIPTION": "Connect your Facebook page"
+ },
+ "WHATSAPP": {
+ "TITLE": "WhatsApp",
+ "DESCRIPTION": "Support your customers on WhatsApp"
+ },
+ "EMAIL": {
+ "TITLE": "E-mail",
+ "DESCRIPTION": "Connect with Gmail, Outlook, or other providers"
+ },
+ "SMS": {
+ "TITLE": "SMS",
+ "DESCRIPTION": "Integrate SMS channel with Twilio or bandwidth"
+ },
+ "API": {
+ "TITLE": "API",
+ "DESCRIPTION": "Make a custom channel using our API"
+ },
+ "TELEGRAM": {
+ "TITLE": "Telegram",
+ "DESCRIPTION": "Configure Telegram channel using Bot token"
+ },
+ "LINE": {
+ "TITLE": "Line",
+ "DESCRIPTION": "Integrate your Line channel"
+ },
+ "INSTAGRAM": {
+ "TITLE": "Instagram",
+ "DESCRIPTION": "Connect your instagram account"
+ },
+ "VOICE": {
+ "TITLE": "Voice",
+ "DESCRIPTION": "Integrate with Twilio Voice"
+ }
+ }
},
"AGENTS": {
"TITLE": "Agentes",
@@ -478,7 +523,10 @@
"MESSAGE": "Ahora puedes colaborar con tus clientes a través de tu nuevo canal. Feliz soporte",
"BUTTON_TEXT": "Llévame allí",
"MORE_SETTINGS": "Más ajustes",
- "WEBSITE_SUCCESS": "Has terminado de crear un canal del sitio web. Copia el código que se muestra a continuación y pégalo en tu sitio web. La próxima vez que un cliente use el chat en vivo, la conversación aparecerá automáticamente en su bandeja de entrada."
+ "WEBSITE_SUCCESS": "Has terminado de crear un canal del sitio web. Copia el código que se muestra a continuación y pégalo en tu sitio web. La próxima vez que un cliente use el chat en vivo, la conversación aparecerá automáticamente en su bandeja de entrada.",
+ "WHATSAPP_QR_INSTRUCTION": "Scan the QR code above to quickly test your WhatsApp inbox",
+ "MESSENGER_QR_INSTRUCTION": "Scan the QR code above to quickly test your Facebook Messenger inbox",
+ "TELEGRAM_QR_INSTRUCTION": "Scan the QR code above to quickly test your Telegram inbox"
},
"REAUTH": "Reautorizar",
"VIEW": "Ver",
@@ -868,9 +916,18 @@
"SCRIPT_SETTINGS": "\n window.chatwootSettings = {options};"
},
"EMAIL_PROVIDERS": {
- "MICROSOFT": "Microsoft",
- "GOOGLE": "Google",
- "OTHER_PROVIDERS": "Otros proveedores"
+ "MICROSOFT": {
+ "TITLE": "Microsoft",
+ "DESCRIPTION": "Connect with Microsoft"
+ },
+ "GOOGLE": {
+ "TITLE": "Google",
+ "DESCRIPTION": "Connect with Google"
+ },
+ "OTHER_PROVIDERS": {
+ "TITLE": "Otros proveedores",
+ "DESCRIPTION": "Connect with Other Providers"
+ }
},
"CHANNELS": {
"MESSENGER": "Messenger",
diff --git a/app/javascript/dashboard/i18n/locale/es/integrations.json b/app/javascript/dashboard/i18n/locale/es/integrations.json
index 37ac7c869..aa720521d 100644
--- a/app/javascript/dashboard/i18n/locale/es/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/es/integrations.json
@@ -704,11 +704,28 @@
"ERROR_MESSAGE": "There was an error creating the document, please try again."
},
"FORM": {
+ "TYPE": {
+ "LABEL": "Document Type",
+ "URL": "URL",
+ "PDF": "PDF File"
+ },
"URL": {
"LABEL": "URL",
"PLACEHOLDER": "Enter the URL of the document",
"ERROR": "Please provide a valid URL for the document"
},
+ "PDF_FILE": {
+ "LABEL": "PDF File",
+ "CHOOSE_FILE": "Choose PDF file",
+ "ERROR": "Please select a PDF file",
+ "HELP_TEXT": "Maximum file size: 10MB",
+ "INVALID_TYPE": "Please select a valid PDF file",
+ "TOO_LARGE": "File size exceeds 10MB limit"
+ },
+ "NAME": {
+ "LABEL": "Document Name (Optional)",
+ "PLACEHOLDER": "Enter a name for the document"
+ },
"ASSISTANT": {
"LABEL": "Assistant",
"PLACEHOLDER": "Select the assistant",
@@ -744,6 +761,7 @@
"SELECTED": "{count} selected",
"SELECT_ALL": "Select all ({count})",
"UNSELECT_ALL": "Unselect all ({count})",
+ "SEARCH_PLACEHOLDER": "Search FAQs...",
"BULK_APPROVE_BUTTON": "Approve",
"BULK_DELETE_BUTTON": "Eliminar",
"BULK_APPROVE": {
diff --git a/app/javascript/dashboard/i18n/locale/es/mfa.json b/app/javascript/dashboard/i18n/locale/es/mfa.json
new file mode 100644
index 000000000..04897bde6
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/es/mfa.json
@@ -0,0 +1,106 @@
+{
+ "MFA_SETTINGS": {
+ "TITLE": "Two-Factor Authentication",
+ "SUBTITLE": "Secure your account with TOTP-based authentication",
+ "DESCRIPTION": "Add an extra layer of security to your account using a time-based one-time password (TOTP)",
+ "STATUS_TITLE": "Authentication Status",
+ "STATUS_DESCRIPTION": "Manage your two-factor authentication settings and backup recovery codes",
+ "ENABLED": "Activado",
+ "DISABLED": "Deshabilitado",
+ "STATUS_ENABLED": "Two-factor authentication is active",
+ "STATUS_ENABLED_DESC": "Your account is protected with an additional layer of security",
+ "ENABLE_BUTTON": "Enable Two-Factor Authentication",
+ "ENHANCE_SECURITY": "Enhance Your Account Security",
+ "ENHANCE_SECURITY_DESC": "Two-factor authentication adds an extra layer of security by requiring a verification code from your authenticator app in addition to your password.",
+ "SETUP": {
+ "STEP_NUMBER_1": "1",
+ "STEP_NUMBER_2": "2",
+ "STEP1_TITLE": "Scan QR Code with Your Authenticator App",
+ "STEP1_DESCRIPTION": "Use Google Authenticator, Authy, or any TOTP-compatible app",
+ "LOADING_QR": "Cargando...",
+ "MANUAL_ENTRY": "Can't scan? Enter code manually",
+ "SECRET_KEY": "Secret Key",
+ "COPY": "Copiar",
+ "ENTER_CODE": "Enter the 6-digit code from your authenticator app",
+ "ENTER_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify & Continue",
+ "CANCEL": "Cancelar",
+ "ERROR_STARTING": "MFA not enabled. Please contact administrator.",
+ "INVALID_CODE": "Invalid verification code",
+ "SECRET_COPIED": "Secret key copied to clipboard",
+ "SUCCESS": "Two-factor authentication has been enabled successfully"
+ },
+ "BACKUP": {
+ "TITLE": "Save Your Backup Codes",
+ "DESCRIPTION": "Keep these codes safe. Each can be used once if you lose access to your authenticator",
+ "IMPORTANT": "Important:",
+ "IMPORTANT_NOTE": " Save these codes in a secure location. You won't be able to see them again.",
+ "DOWNLOAD": "Descargar",
+ "COPY_ALL": "Copy All",
+ "CONFIRM": "I have saved my backup codes in a secure location and understand that I won't be able to see them again",
+ "COMPLETE_SETUP": "Complete Setup",
+ "CODES_COPIED": "Backup codes copied to clipboard"
+ },
+ "MANAGEMENT": {
+ "BACKUP_CODES": "Backup Codes",
+ "BACKUP_CODES_DESC": "Generate new codes if you've lost or used your existing ones",
+ "REGENERATE": "Regenerate Backup Codes",
+ "DISABLE_MFA": "Disable 2FA",
+ "DISABLE_MFA_DESC": "Remove two-factor authentication from your account",
+ "DISABLE_BUTTON": "Disable Two-Factor Authentication"
+ },
+ "DISABLE": {
+ "TITLE": "Disable Two-Factor Authentication",
+ "DESCRIPTION": "You'll need to enter your password and a verification code to disable two-factor authentication.",
+ "PASSWORD": "Contraseña",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Disable 2FA",
+ "CANCEL": "Cancelar",
+ "SUCCESS": "Two-factor authentication has been disabled",
+ "ERROR": "Failed to disable MFA. Please check your credentials."
+ },
+ "REGENERATE": {
+ "TITLE": "Regenerate Backup Codes",
+ "DESCRIPTION": "This will invalidate your existing backup codes and generate new ones. Enter your verification code to continue.",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Generate New Codes",
+ "CANCEL": "Cancelar",
+ "NEW_CODES_TITLE": "New Backup Codes Generated",
+ "NEW_CODES_DESC": "Your old backup codes have been invalidated. Save these new codes in a secure location.",
+ "CODES_IMPORTANT": "Important:",
+ "CODES_IMPORTANT_NOTE": " Each code can only be used once. Save them before closing this window.",
+ "DOWNLOAD_CODES": "Download Codes",
+ "COPY_ALL_CODES": "Copy All Codes",
+ "CODES_SAVED": "I've Saved My Codes",
+ "SUCCESS": "New backup codes have been generated",
+ "ERROR": "Failed to regenerate backup codes"
+ }
+ },
+ "MFA_VERIFICATION": {
+ "TITLE": "Two-Factor Authentication",
+ "DESCRIPTION": "Enter your verification code to continue",
+ "AUTHENTICATOR_APP": "Authenticator App",
+ "BACKUP_CODE": "Backup Code",
+ "ENTER_OTP_CODE": "Enter 6-digit code from your authenticator app",
+ "ENTER_BACKUP_CODE": "Enter one of your backup codes",
+ "BACKUP_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify",
+ "TRY_ANOTHER_METHOD": "Try another verification method",
+ "CANCEL_LOGIN": "Cancel and return to login",
+ "HELP_TEXT": "Having trouble signing in?",
+ "LEARN_MORE": "Learn more about 2FA",
+ "HELP_MODAL": {
+ "TITLE": "Two-Factor Authentication Help",
+ "AUTHENTICATOR_TITLE": "Using an Authenticator App",
+ "AUTHENTICATOR_DESC": "Open your authenticator app (Google Authenticator, Authy, etc.) and enter the 6-digit code shown for your account.",
+ "BACKUP_TITLE": "Using a Backup Code",
+ "BACKUP_DESC": "If you don't have access to your authenticator app, you can use one of the backup codes you saved when setting up 2FA. Each code can only be used once.",
+ "CONTACT_TITLE": "Need More Help?",
+ "CONTACT_DESC_CLOUD": "If you've lost access to both your authenticator app and backup codes, please reach out to Chatwoot support for assistance.",
+ "CONTACT_DESC_SELF_HOSTED": "If you've lost access to both your authenticator app and backup codes, please contact your administrator for assistance."
+ },
+ "VERIFICATION_FAILED": "Verification failed. Please try again."
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/es/settings.json b/app/javascript/dashboard/i18n/locale/es/settings.json
index f2662bc72..35be4335c 100644
--- a/app/javascript/dashboard/i18n/locale/es/settings.json
+++ b/app/javascript/dashboard/i18n/locale/es/settings.json
@@ -51,6 +51,13 @@
"LARGER": "Larger",
"EXTRA_LARGE": "Extra Large"
}
+ },
+ "LANGUAGE": {
+ "TITLE": "Preferred Language",
+ "NOTE": "Choose the language you want to use.",
+ "UPDATE_SUCCESS": "Your Language settings have been updated successfully",
+ "UPDATE_ERROR": "There is an error while updating the language settings, please try again",
+ "USE_ACCOUNT_DEFAULT": "Use account default"
}
},
"MESSAGE_SIGNATURE_SECTION": {
@@ -73,6 +80,11 @@
"NOTE": "Actualizar tu contraseña restablecería tus entradas en varios dispositivos.",
"BTN_TEXT": "Cambiar contraseña"
},
+ "SECURITY_SECTION": {
+ "TITLE": "Security",
+ "NOTE": "Manage additional security features for your account.",
+ "MFA_BUTTON": "Manage Two-Factor Authentication"
+ },
"ACCESS_TOKEN": {
"TITLE": "Token de acceso",
"NOTE": "Este token puede ser usado si estás construyendo una integración basada en API",
@@ -226,7 +238,7 @@
"APPEARANCE": "Cambiar apariencia",
"SUPER_ADMIN_CONSOLE": "Consola SuperAdmin",
"DOCS": "Leer la documentación",
- "CHANGELOG": "Changelog",
+ "CHANGELOG": "Notas de versión",
"LOGOUT": "Cerrar sesión"
},
"APP_GLOBAL": {
@@ -330,6 +342,7 @@
"REPORTS_LABEL": "Etiquetas",
"REPORTS_INBOX": "Bandeja de entrada",
"REPORTS_TEAM": "Equipo",
+ "AGENT_ASSIGNMENT": "Agent Assignment",
"SET_AVAILABILITY_TITLE": "Ponte como",
"SET_YOUR_AVAILABILITY": "Establecer su disponibilidad",
"SLA": "SLA",
@@ -350,7 +363,8 @@
"INFO_TEXT": "Permite que el sistema te marque automáticamente sin conexión cuando no estás usando la aplicación o el tablero.",
"INFO_SHORT": "Marcar automáticamente sin conexión cuando no está usando la aplicación."
},
- "DOCS": "Leer documentos"
+ "DOCS": "Leer documentos",
+ "SECURITY": "Security"
},
"BILLING_SETTINGS": {
"TITLE": "Facturación",
@@ -382,6 +396,77 @@
},
"NO_BILLING_USER": "Tu cuenta de facturación está siendo configurada. Por favor, actualiza la página e inténtalo de nuevo."
},
+ "SECURITY_SETTINGS": {
+ "TITLE": "Security",
+ "DESCRIPTION": "Manage your account security settings.",
+ "LINK_TEXT": "Learn more about SAML SSO",
+ "SAML": {
+ "TITLE": "SAML SSO",
+ "NOTE": "Configure SAML single sign-on for your account. Users will authenticate through your identity provider instead of using email/password.",
+ "ACS_URL": {
+ "LABEL": "ACS URL",
+ "TOOLTIP": "Assertion Consumer Service URL - Configure this URL in your IdP as the destination for SAML responses"
+ },
+ "SSO_URL": {
+ "LABEL": "SSO URL",
+ "HELP": "The URL where SAML authentication requests will be sent",
+ "PLACEHOLDER": "https://your-idp.com/saml/sso"
+ },
+ "CERTIFICATE": {
+ "LABEL": "Signing certificate in PEM format",
+ "HELP": "The public certificate from your identity provider used to verify SAML responses",
+ "PLACEHOLDER": "-----BEGIN CERTIFICATE-----\nMIIC..."
+ },
+ "FINGERPRINT": {
+ "LABEL": "Fingerprint",
+ "TOOLTIP": "SHA-1 fingerprint of the certificate - Use this to verify the certificate in your IdP configuration"
+ },
+ "COPY_SUCCESS": "Código copiado al portapapeles con éxito",
+ "SP_ENTITY_ID": {
+ "LABEL": "SP Entity ID",
+ "HELP": "Unique identifier for this application as a service provider (auto-generated).",
+ "TOOLTIP": "Unique identifier for Chatwoot as the Service Provider - Configure this in your IdP settings"
+ },
+ "IDP_ENTITY_ID": {
+ "LABEL": "Identity Provider Entity ID",
+ "HELP": "Unique identifier for your identity provider (usually found in IdP configuration)",
+ "PLACEHOLDER": "https://your-idp.com/saml"
+ },
+ "UPDATE_BUTTON": "Update SAML Settings",
+ "API": {
+ "SUCCESS": "SAML settings updated successfully",
+ "ERROR": "Failed to update SAML settings",
+ "ERROR_LOADING": "Failed to load SAML settings",
+ "DISABLED": "SAML settings disabled successfully"
+ },
+ "VALIDATION": {
+ "REQUIRED_FIELDS": "SSO URL, Identity Provider Entity ID, and Certificate are required fields",
+ "SSO_URL_ERROR": "Please enter a valid SSO URL",
+ "CERTIFICATE_ERROR": "Certificate is required",
+ "IDP_ENTITY_ID_ERROR": "Identity Provider Entity ID is required"
+ },
+ "ENTERPRISE_PAYWALL": {
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade to an Enterprise plan to access SAML single sign-on and other advanced security features.",
+ "ASK_ADMIN": "Por favor, comuníquese con su administrador para la actualización."
+ },
+ "PAYWALL": {
+ "TITLE": "Upgrade to enable SAML SSO",
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade your plan to get access to SAML single sign-on and other advanced features.",
+ "UPGRADE_NOW": "Actualizar ahora",
+ "CANCEL_ANYTIME": "Puede cambiar o cancelar su plan en cualquier momento"
+ },
+ "ATTRIBUTE_MAPPING": {
+ "TITLE": "SAML Attribute Setup",
+ "DESCRIPTION": "The following attribute mappings must be configured in your identity provider"
+ },
+ "INFO_SECTION": {
+ "TITLE": "Service Provider Information",
+ "TOOLTIP": "Copy these values and configure them in your Identity Provider to establish the SAML connection"
+ }
+ }
+ },
"CREATE_ACCOUNT": {
"NO_ACCOUNT_WARNING": "¡Oh oh! No hemos podido encontrar ninguna cuenta de \"Chatwoot\". Por favor, crea una nueva cuenta para continuar.",
"NEW_ACCOUNT": "Nueva cuenta",
@@ -418,5 +503,255 @@
"SWITCH_TO_REPLY": "Cambiar a respuesta",
"TOGGLE_SNOOZE_DROPDOWN": "Cambiar el menú desplegable"
}
+ },
+ "ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent assignment",
+ "DESCRIPTION": "Define policies to effectively manage workload and route conversations based on the needs of inboxes and agents. Learn more here"
+ },
+ "ASSIGNMENT_POLICY": {
+ "TITLE": "Assignment policy",
+ "DESCRIPTION": "Manage how conversations get assigned in inboxes.",
+ "FEATURES": [
+ "Assign by conversations evenly or by available capacity",
+ "Add fair distribution rules to avoid overloading any agent",
+ "Add inboxes to a policy - one policy per inbox"
+ ]
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "TITLE": "Agent capacity policy",
+ "DESCRIPTION": "Manage workload for agents.",
+ "FEATURES": [
+ "Define maximum conversations per inbox",
+ "Create exceptions based on labels and time",
+ "Add agents to a policy - one policy per agent"
+ ]
+ }
+ },
+ "AGENT_ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Assignment policy",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "ORDER": "Order",
+ "PRIORITY": "Prioridad",
+ "ACTIVE": "Activo",
+ "INACTIVE": "Inactive",
+ "POPOVER": "Added inboxes",
+ "EDIT": "Editar"
+ },
+ "NO_RECORDS_FOUND": "No assignment policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create assignment policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy created successfully",
+ "ERROR_MESSAGE": "Failed to create assignment policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit assignment policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_INBOX_DIALOG": {
+ "TITLE": "Add inbox",
+ "DESCRIPTION": "{inboxName} inbox is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Cancelar"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update assignment policy"
+ },
+ "INBOX_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Inbox added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Inbox removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove inbox from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Descripción:",
+ "PLACEHOLDER": "Introducir descripción"
+ },
+ "STATUS": {
+ "LABEL": "Estado:",
+ "PLACEHOLDER": "Seleccionar estado",
+ "ACTIVE": "Policy is active",
+ "INACTIVE": "Policy is inactive"
+ },
+ "ASSIGNMENT_ORDER": {
+ "LABEL": "Assignment order",
+ "ROUND_ROBIN": {
+ "LABEL": "Round robin",
+ "DESCRIPTION": "Assign conversations evenly among agents."
+ },
+ "BALANCED": {
+ "LABEL": "Balanced",
+ "DESCRIPTION": "Assign conversations based on available capacity."
+ }
+ },
+ "ASSIGNMENT_PRIORITY": {
+ "LABEL": "Assignment priority",
+ "EARLIEST_CREATED": {
+ "LABEL": "Earliest created",
+ "DESCRIPTION": "The conversation that was created first gets assigned first."
+ },
+ "LONGEST_WAITING": {
+ "LABEL": "Longest waiting",
+ "DESCRIPTION": "The conversation waiting the longest gets assigned first."
+ }
+ },
+ "FAIR_DISTRIBUTION": {
+ "LABEL": "Fair distribution policy",
+ "DESCRIPTION": "Set the maximum number of conversations that can be assigned per agent within a time window to avoid overloading any one agent. This required field defaults to 100 conversations per hour.",
+ "INPUT_MAX": "Assign max",
+ "DURATION": "Conversations per agent in every"
+ },
+ "INBOXES": {
+ "LABEL": "Added inboxes",
+ "DESCRIPTION": "Add inboxes for which this policy will be applicable.",
+ "ADD_BUTTON": "Add inbox",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select inboxes to add",
+ "ADD_BUTTON": "Añadir"
+ },
+ "EMPTY_STATE": "No inboxes added to this policy, add an inbox to get started",
+ "API": {
+ "SUCCESS_MESSAGE": "Inbox successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Assignment policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete assignment policy"
+ }
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent capacity",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "POPOVER": "Added agents",
+ "EDIT": "Editar"
+ },
+ "NO_RECORDS_FOUND": "No agent capacity policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create agent capacity policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy created successfully",
+ "ERROR_MESSAGE": "Failed to create agent capacity policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit agent capacity policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_AGENT_DIALOG": {
+ "TITLE": "Add agent",
+ "DESCRIPTION": "{agentName} is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Cancelar"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update agent capacity policy"
+ },
+ "AGENT_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Agent added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Agent removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove agent from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Descripción:",
+ "PLACEHOLDER": "Introducir descripción"
+ },
+ "INBOX_CAPACITY_LIMIT": {
+ "LABEL": "Inbox capacity limits",
+ "ADD_BUTTON": "Add inbox",
+ "FIELD": {
+ "SELECT_INBOX": "Select inbox",
+ "MAX_CONVERSATIONS": "Max conversations",
+ "SET_LIMIT": "Set limit"
+ },
+ "EMPTY_STATE": "No inbox limit set"
+ },
+ "EXCLUSION_RULES": {
+ "LABEL": "Exclusion rules",
+ "DESCRIPTION": "Conversations that satisfy the following conditions would not count towards agent capacity",
+ "TAGS": {
+ "LABEL": "Exclude conversations tagged with specific labels",
+ "ADD_TAG": "add tag",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select tags to add"
+ },
+ "EMPTY_STATE": "No tags added to this policy."
+ },
+ "DURATION": {
+ "LABEL": "Exclude conversations older than a specified duration",
+ "PLACEHOLDER": "Set time"
+ }
+ },
+ "USERS": {
+ "LABEL": "Assigned agents",
+ "DESCRIPTION": "Add agents for which this policy will be applicable.",
+ "ADD_BUTTON": "Add agent",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select agents to add",
+ "ADD_BUTTON": "Añadir"
+ },
+ "EMPTY_STATE": "No agents added",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Agent capacity policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete agent capacity policy"
+ }
+ },
+ "DELETE_POLICY": {
+ "TITLE": "Delete policy",
+ "DESCRIPTION": "Are you sure you want to delete this policy? This action cannot be undone.",
+ "CONFIRM_BUTTON_LABEL": "Eliminar",
+ "CANCEL_BUTTON_LABEL": "Cancelar"
+ }
}
}
diff --git a/app/javascript/dashboard/i18n/locale/es/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/es/whatsappTemplates.json
index 06ceacb68..ee49f84da 100644
--- a/app/javascript/dashboard/i18n/locale/es/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/es/whatsappTemplates.json
@@ -40,6 +40,7 @@
"BUTTON_LABEL": "Button {index}",
"COUPON_CODE": "Enter coupon code (max 15 chars)",
"MEDIA_URL_LABEL": "Enter {type} URL",
+ "DOCUMENT_NAME_PLACEHOLDER": "Enter document filename (e.g., Invoice_2025.pdf)",
"BUTTON_PARAMETER": "Enter button parameter"
}
}
diff --git a/app/javascript/dashboard/i18n/locale/fa/agentBots.json b/app/javascript/dashboard/i18n/locale/fa/agentBots.json
index e5af6099c..6d64c887e 100644
--- a/app/javascript/dashboard/i18n/locale/fa/agentBots.json
+++ b/app/javascript/dashboard/i18n/locale/fa/agentBots.json
@@ -2,13 +2,13 @@
"AGENT_BOTS": {
"HEADER": "رباتها",
"LOADING_EDITOR": "در حال بارگیری ویرایشگر...",
- "DESCRIPTION": "Agent Bots are like the most fabulous members of your team. They can handle the small stuff, so you can focus on the stuff that matters. Give them a try. You can manage your bots from this page or create new ones using the 'Add Bot' button.",
+ "DESCRIPTION": "رباتهای عامل مانند شگفتانگیزترین اعضای تیم شما هستند. آنها میتوانند کارهای کوچک را انجام دهند، بنابراین شما میتوانید روی چیزهای مهم تمرکز کنید. آنها را امتحان کنید. میتوانید رباتهای خود را از این صفحه مدیریت کنید یا با استفاده از دکمه «افزودن ربات»، رباتهای جدیدی ایجاد کنید.",
"LEARN_MORE": "Learn about agent bots",
- "GLOBAL_BOT": "System bot",
+ "GLOBAL_BOT": "ربات سیستمی",
"GLOBAL_BOT_BADGE": "سیستم",
"AVATAR": {
- "SUCCESS_DELETE": "Bot avatar deleted successfully",
- "ERROR_DELETE": "Error deleting bot avatar, please try again"
+ "SUCCESS_DELETE": "آواتار ربات با موفقیت حذف شد",
+ "ERROR_DELETE": "خطا هنگام حذف آواتار ربات، لطفا مجدد امتحان کنید"
},
"BOT_CONFIGURATION": {
"TITLE": "انتخاب یک ربات عامل",
@@ -22,7 +22,7 @@
"SELECT_PLACEHOLDER": "انتخاب ربات"
},
"ADD": {
- "TITLE": "Add Bot",
+ "TITLE": "افزودن ربات",
"CANCEL_BUTTON_TEXT": "انصراف",
"API": {
"SUCCESS_MESSAGE": "ربات با موفقیت اضافه شد.",
@@ -30,10 +30,10 @@
}
},
"LIST": {
- "404": "No bots found. You can create a bot by clicking the 'Add Bot' button.",
+ "404": "هیچ رباتی یافت نشد. شما میتوانید با کلیک روی دکمه «افزودن ربات» یک ربات ایجاد کنید.",
"LOADING": "در حال گرفتن رباتها...",
"TABLE_HEADER": {
- "DETAILS": "Bot Details",
+ "DETAILS": "جزئیات ربات",
"URL": "آدرس URL وب هوک"
}
},
@@ -42,7 +42,7 @@
"TITLE": "حذف ربات",
"CONFIRM": {
"TITLE": "تاییدیه حذف",
- "MESSAGE": "Are you sure you want to delete {name}?",
+ "MESSAGE": "آیا مطمئنید که میخواهید {name} را حذف کنید؟",
"YES": "بله، حذف شود",
"NO": "نه، بماند"
},
@@ -61,18 +61,18 @@
},
"ACCESS_TOKEN": {
"TITLE": "توکن دسترسی",
- "DESCRIPTION": "Copy the access token and save it securely",
- "COPY_SUCCESSFUL": "Access token copied to clipboard",
- "RESET_SUCCESS": "Access token regenerated successfully",
- "RESET_ERROR": "Unable to regenerate access token. Please try again"
+ "DESCRIPTION": "توکن دسترسی را کپی کرده و در جای امن ذخیره کنید",
+ "COPY_SUCCESSFUL": "توکن دسترسی در کلیپبورد کپی شد",
+ "RESET_SUCCESS": "توکن دسترسی با موفقیت بازسازی شد",
+ "RESET_ERROR": "خطا در بازسازی توکن دسترسی. لطفا مجدد امتحان کنید"
},
"FORM": {
"AVATAR": {
- "LABEL": "Bot avatar"
+ "LABEL": "آواتار ربات"
},
"NAME": {
"LABEL": "نام ربات",
- "PLACEHOLDER": "Enter bot name",
+ "PLACEHOLDER": "نام ربات را وارد کنید",
"REQUIRED": "نام ربات الزامی است"
},
"DESCRIPTION": {
@@ -82,19 +82,19 @@
"WEBHOOK_URL": {
"LABEL": "آدرس URL وب هوک",
"PLACEHOLDER": "https://example.com/webhook",
- "REQUIRED": "Webhook URL is required"
+ "REQUIRED": "URL وبهوک الزامی است"
},
"ERRORS": {
"NAME": "نام ربات الزامی است",
- "URL": "Webhook URL is required",
- "VALID_URL": "Please enter a valid URL starting with http:// or https://"
+ "URL": "URL وبهوک الزامی است",
+ "VALID_URL": "لطفا یک URL معتبر که با http:// یا https:// شروع میشود وارد کنید"
},
"CANCEL": "انصراف",
- "CREATE": "Create Bot",
- "UPDATE": "Update Bot"
+ "CREATE": "ایجاد ربات",
+ "UPDATE": "بهروزرسانی ربات"
},
"WEBHOOK": {
- "DESCRIPTION": "Configure a webhook bot to integrate with your custom services. The bot will receive and process events from conversations and can respond to them."
+ "DESCRIPTION": "یک ربات وبهوک برای ادغام با سرویسهای سفارشیتان پیکربندی کنید. این ربات رویدادها را از مکالمات دریافت کرده و پردازش میکند و میتواند به آنها پاسخ دهد."
},
"TYPES": {
"WEBHOOK": "وبهوک ربات"
diff --git a/app/javascript/dashboard/i18n/locale/fa/automation.json b/app/javascript/dashboard/i18n/locale/fa/automation.json
index 3e02e4a69..033f2d37d 100644
--- a/app/javascript/dashboard/i18n/locale/fa/automation.json
+++ b/app/javascript/dashboard/i18n/locale/fa/automation.json
@@ -177,7 +177,8 @@
"REFERER_LINK": "Referrer Link",
"ASSIGNEE_NAME": "مسئول",
"TEAM_NAME": "تیم",
- "PRIORITY": "اولویت"
+ "PRIORITY": "اولویت",
+ "LABELS": "برچسبها"
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/fa/contact.json b/app/javascript/dashboard/i18n/locale/fa/contact.json
index 174fb7e31..d1affb9f3 100644
--- a/app/javascript/dashboard/i18n/locale/fa/contact.json
+++ b/app/javascript/dashboard/i18n/locale/fa/contact.json
@@ -17,7 +17,7 @@
"IP_ADDRESS": "آدرس آیپی",
"CREATED_AT_LABEL": "ایجاد شده",
"NEW_MESSAGE": "پیام جدید",
- "CALL": "Call",
+ "CALL": "تماس",
"CALL_UNDER_DEVELOPMENT": "Calling is under development",
"VOICE_INBOX_PICKER": {
"TITLE": "Choose a voice inbox"
@@ -610,6 +610,15 @@
"SEND_MESSAGE": "ارسال پیام"
}
},
+ "TWILIO_OPTIONS": {
+ "LABEL": "Select template",
+ "SEARCH_PLACEHOLDER": "Search templates",
+ "EMPTY_STATE": "No templates found",
+ "TEMPLATE_PARSER": {
+ "BACK": "بازگشت",
+ "SEND_MESSAGE": "ارسال پیام"
+ }
+ },
"ACTION_BUTTONS": {
"DISCARD": "Discard",
"SEND": "Send ({keyCode})"
diff --git a/app/javascript/dashboard/i18n/locale/fa/contactFilters.json b/app/javascript/dashboard/i18n/locale/fa/contactFilters.json
index 5e92d6ae6..0587542e7 100644
--- a/app/javascript/dashboard/i18n/locale/fa/contactFilters.json
+++ b/app/javascript/dashboard/i18n/locale/fa/contactFilters.json
@@ -48,7 +48,8 @@
"CREATED_AT": "ایجاد شده در",
"LAST_ACTIVITY": "آخرین فعالیت",
"REFERER_LINK": "پیوند ارجاعدهنده",
- "BLOCKED": "مسدود شده"
+ "BLOCKED": "مسدود شده",
+ "LABELS": "برچسبها"
},
"GROUPS": {
"STANDARD_FILTERS": "فیلترهای استاندارد",
diff --git a/app/javascript/dashboard/i18n/locale/fa/contentTemplates.json b/app/javascript/dashboard/i18n/locale/fa/contentTemplates.json
new file mode 100644
index 000000000..a14569c18
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/fa/contentTemplates.json
@@ -0,0 +1,51 @@
+{
+ "CONTENT_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Twilio Templates",
+ "SUBTITLE": "Select the Twilio template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "جستجوی الگوها",
+ "NO_TEMPLATES_FOUND": "هیچ قالبی برای",
+ "NO_CONTENT": "فاقد محتوا",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "دستهبندی",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No Twilio templates available. Click refresh to sync templates from Twilio.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "زبان",
+ "TEMPLATE_BODY": "بدنه الگو",
+ "CATEGORY": "دستهبندی"
+ },
+ "TYPES": {
+ "MEDIA": "Media",
+ "QUICK_REPLY": "Quick Reply",
+ "TEXT": "متن"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "متغیرها",
+ "LANGUAGE": "زبان",
+ "CATEGORY": "دستهبندی",
+ "VARIABLE_PLACEHOLDER": "مقدار {variable} را وارد کنید",
+ "GO_BACK_LABEL": "بازگشت",
+ "SEND_MESSAGE_LABEL": "ارسال پیام",
+ "FORM_ERROR_MESSAGE": "لطفا قبل از ارسال همه متغیرها را پر کنید",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "MEDIA_URL_LABEL": "Enter full media URL",
+ "MEDIA_URL_PLACEHOLDER": "https://example.com/image.jpg"
+ },
+ "FORM": {
+ "BACK_BUTTON": "بازگشت",
+ "SEND_MESSAGE_BUTTON": "ارسال پیام"
+ }
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/fa/conversation.json b/app/javascript/dashboard/i18n/locale/fa/conversation.json
index bc1afdefb..864674c88 100644
--- a/app/javascript/dashboard/i18n/locale/fa/conversation.json
+++ b/app/javascript/dashboard/i18n/locale/fa/conversation.json
@@ -35,6 +35,11 @@
"API_HOURS_WINDOW": "You can only reply to this conversation within {hours} hours",
"NOT_ASSIGNED_TO_YOU": "این گفتگو به شما اختصاص داده نشده است. آیا می خواهید این گفتگو را به خودتان اختصاص دهید؟",
"ASSIGN_TO_ME": "اختصاص به من",
+ "BOT_HANDOFF_MESSAGE": "You are responding to a conversation which is currently handled by an assistant or a bot.",
+ "BOT_HANDOFF_ACTION": "Mark open and assign to you",
+ "BOT_HANDOFF_REOPEN_ACTION": "Mark conversation open",
+ "BOT_HANDOFF_SUCCESS": "Conversation has been handed over to you",
+ "BOT_HANDOFF_ERROR": "Failed to take over the conversation. Please try again.",
"TWILIO_WHATSAPP_CAN_REPLY": "شما فقط می توانید با استفاده از یک پیام الگو به این مکالمه پاسخ دهید",
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "محدودیت ۲۴ ساعته پنجره پیام",
"OLD_INSTAGRAM_INBOX_REPLY_BANNER": "This Instagram account was migrated to the new Instagram channel inbox. All new messages will show up there. You won’t be able to send messages from this conversation anymore.",
@@ -66,6 +71,17 @@
"SHOW_LABELS": "مشاهده کردن برچسبها",
"HIDE_LABELS": "پنهان کردن برچسبها"
},
+ "VOICE_CALL": {
+ "INCOMING_CALL": "Incoming call",
+ "OUTGOING_CALL": "Outgoing call",
+ "CALL_IN_PROGRESS": "Call in progress",
+ "NO_ANSWER": "No answer",
+ "MISSED_CALL": "Missed call",
+ "CALL_ENDED": "Call ended",
+ "NOT_ANSWERED_YET": "Not answered yet",
+ "THEY_ANSWERED": "They answered",
+ "YOU_ANSWERED": "You answered"
+ },
"HEADER": {
"RESOLVE_ACTION": "حل شد",
"REOPEN_ACTION": "دوباره باز کنید",
diff --git a/app/javascript/dashboard/i18n/locale/fa/helpCenter.json b/app/javascript/dashboard/i18n/locale/fa/helpCenter.json
index 244e35243..db4346f68 100644
--- a/app/javascript/dashboard/i18n/locale/fa/helpCenter.json
+++ b/app/javascript/dashboard/i18n/locale/fa/helpCenter.json
@@ -741,7 +741,8 @@
"LIVE_CHAT_WIDGET": {
"LABEL": "Live chat widget",
"PLACEHOLDER": "Select live chat widget",
- "HELP_TEXT": "Select a live chat widget that will appear on your help center"
+ "HELP_TEXT": "Select a live chat widget that will appear on your help center",
+ "NONE_OPTION": "No widget"
},
"BRAND_COLOR": {
"LABEL": "Brand color"
@@ -807,6 +808,58 @@
"ERROR_MESSAGE": "Unable to update portal"
}
}
+ },
+ "PDF_UPLOAD": {
+ "TITLE": "Upload PDF Document",
+ "DESCRIPTION": "Upload a PDF document to automatically generate FAQs using AI",
+ "DRAG_DROP_TEXT": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "ADDITIONAL_CONTEXT_LABEL": "Additional Context (Optional)",
+ "ADDITIONAL_CONTEXT_PLACEHOLDER": "Provide any additional context or instructions for FAQ generation...",
+ "UPLOADING": "در حال آپلود...",
+ "UPLOAD": "Upload & Process",
+ "CANCEL": "انصراف",
+ "ERROR_INVALID_TYPE": "Please select a valid PDF file",
+ "ERROR_FILE_TOO_LARGE": "File size must be less than 512MB",
+ "ERROR_UPLOAD_FAILED": "Failed to upload PDF. Please try again."
+ },
+ "PDF_DOCUMENTS": {
+ "TITLE": "PDF Documents",
+ "DESCRIPTION": "Manage uploaded PDF documents and generate FAQs from them",
+ "UPLOAD_PDF": "Upload PDF",
+ "UPLOAD_FIRST_PDF": "Upload your first PDF",
+ "UPLOADED_BY": "Uploaded by",
+ "GENERATE_FAQS": "Generate FAQs",
+ "GENERATING": "در حال تولید...",
+ "CONFIRM_DELETE": "Are you sure you want to delete {filename}?",
+ "EMPTY_STATE": {
+ "TITLE": "No PDF documents yet",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQs using AI"
+ },
+ "STATUS": {
+ "UPLOADED": "Ready",
+ "PROCESSING": "Processing",
+ "PROCESSED": "تکمیل شد",
+ "FAILED": "Failed"
+ }
+ },
+ "CONTENT_GENERATION": {
+ "TITLE": "Content Generation",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQ content using AI",
+ "UPLOAD_TITLE": "Upload PDF Document",
+ "DRAG_DROP": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "UPLOADING": "Processing document...",
+ "UPLOAD_SUCCESS": "Document processed successfully!",
+ "UPLOAD_ERROR": "Failed to upload document. Please try again.",
+ "INVALID_FILE_TYPE": "Please select a valid PDF file",
+ "FILE_TOO_LARGE": "File size must be less than 512MB",
+ "GENERATED_CONTENT": "Generated FAQ Content",
+ "PUBLISH_SELECTED": "Publish Selected",
+ "PUBLISHING": "Publishing...",
+ "FROM_DOCUMENT": "From document",
+ "NO_CONTENT": "No generated content available. Upload a PDF document to get started.",
+ "LOADING": "Loading generated content..."
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/fa/inbox.json b/app/javascript/dashboard/i18n/locale/fa/inbox.json
index 298efae8c..bbd453668 100644
--- a/app/javascript/dashboard/i18n/locale/fa/inbox.json
+++ b/app/javascript/dashboard/i18n/locale/fa/inbox.json
@@ -74,21 +74,21 @@
"DELETE_ALL_READ": "حذف همه اعلان های خوانده شده"
},
"REAUTHORIZE": {
- "TITLE": "Reauthorization Required",
- "DESCRIPTION": "Your WhatsApp connection has expired. Please reconnect to continue receiving and sending messages.",
- "BUTTON_TEXT": "Reconnect WhatsApp",
- "LOADING_FACEBOOK": "Loading Facebook SDK...",
- "SUCCESS": "WhatsApp reconnected successfully",
- "ERROR": "Failed to reconnect WhatsApp. Please try again.",
- "WHATSAPP_APP_ID_MISSING": "WhatsApp App ID is not configured. Please contact your administrator.",
- "WHATSAPP_CONFIG_ID_MISSING": "WhatsApp Configuration ID is not configured. Please contact your administrator.",
- "CONFIGURATION_ERROR": "Configuration error occurred during reauthorization.",
- "FACEBOOK_LOAD_ERROR": "Failed to load Facebook SDK. Please try again.",
+ "TITLE": "احراز هویت مجدد لازم است",
+ "DESCRIPTION": "اتصال واتساپ شما منقضی شده است. لطفا برای ادامهی دریافت و ارسال پیامها، مجدد متصل شوید.",
+ "BUTTON_TEXT": "اتصال مجدد واتساپ",
+ "LOADING_FACEBOOK": "درحال بارگزاری SDK فیسبوک...",
+ "SUCCESS": "واتساپ با موفقیت متصل شد",
+ "ERROR": "اتصال مجدد به واتساپ با شکست مواجه شد. لطفا مجدد امتحان کنید.",
+ "WHATSAPP_APP_ID_MISSING": "شناسه اپلیکیشن واتساپ پیکربندی نشده است. لطفا با مدیر خود تماس بگیرید.",
+ "WHATSAPP_CONFIG_ID_MISSING": "شناسه پیکربندی واتساپ تنظیم نشده است. لطفا با مدیر خود تماس بگیرید.",
+ "CONFIGURATION_ERROR": "خطای پیکربندی هنگام احراز هویت مجدد رخ داد.",
+ "FACEBOOK_LOAD_ERROR": "بارگزاری SDK فیسبوک با شکست مواجه شد. لطفا مجدد امتحان کنید.",
"TROUBLESHOOTING": {
- "TITLE": "Troubleshooting",
- "POPUP_BLOCKED": "Ensure pop-ups are allowed for this site",
- "COOKIES": "Third-party cookies must be enabled",
- "ADMIN_ACCESS": "You need admin access to the WhatsApp Business Account"
+ "TITLE": "عیبیابی",
+ "POPUP_BLOCKED": "مطمئن شوید نمایش پاپ-آپها برای این سایت مجاز است",
+ "COOKIES": "کوکیهای شخص ثالث باید فعال باشند",
+ "ADMIN_ACCESS": "شما به دسترسی ادمین حساب کاربری واتساپ بیزینس نیاز دارد"
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/fa/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/fa/inboxMgmt.json
index 870699abb..8e834f5fc 100644
--- a/app/javascript/dashboard/i18n/locale/fa/inboxMgmt.json
+++ b/app/javascript/dashboard/i18n/locale/fa/inboxMgmt.json
@@ -272,8 +272,8 @@
},
"SUBMIT_BUTTON": "ایجاد کانال واتساپ",
"EMBEDDED_SIGNUP": {
- "TITLE": "Quick Setup with Meta",
- "DESC": "You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
+ "TITLE": "Quick setup with Meta",
+ "DESC": "Use the WhatsApp Embedded Signup flow to quickly connect new numbers. You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
"BENEFITS": {
"TITLE": "Benefits of Embedded Signup:",
"EASY_SETUP": "No manual configuration required",
@@ -281,9 +281,8 @@
"AUTO_CONFIG": "Automatic webhook and phone number configuration"
},
"LEARN_MORE": {
- "TEXT": "To learn more about integrated signup, pricing, and limitations, visit",
- "LINK_TEXT": "this link.",
- "LINK_URL": "https://developers.facebook.com/docs/whatsapp/embedded-signup/custom-flows/onboarding-business-app-users#limitations"
+ "TEXT": "To learn more about integrated signup, pricing, and limitations, visit {link}.",
+ "LINK_TEXT": "this link"
},
"SUBMIT_BUTTON": "Connect with WhatsApp Business",
"AUTH_PROCESSING": "Authenticating with Meta",
@@ -296,7 +295,9 @@
"INVALID_BUSINESS_DATA": "Invalid business data received from Facebook. Please try again.",
"SIGNUP_ERROR": "Signup error occurred",
"AUTH_NOT_COMPLETED": "Authentication not completed. Please restart the process.",
- "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured"
+ "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured",
+ "MANUAL_FALLBACK": "If your number is already connected to the WhatsApp Business Platform (API), or if you’re a tech provider onboarding your own number, please use the {link} flow",
+ "MANUAL_LINK_TEXT": "manual setup flow"
},
"API": {
"ERROR_MESSAGE": "ما نتوانستیم کانال WhatsApp را ذخیره کنیم"
@@ -424,7 +425,51 @@
},
"AUTH": {
"TITLE": "کانالی را انتخاب کنید",
- "DESC": "Chatwoot از ویجت های چت زنده، فیس بوک مسنجر، پروفایل های توییتر، واتساپ، ایمیل ها و غیره به عنوان کانال پشتیبانی می کند. اگر می خواهید یک کانال سفارشی بسازید، می توانید آن را با استفاده از کانال API ایجاد کنید. برای شروع، یکی از کانال های زیر را انتخاب کنید."
+ "DESC": "Chatwoot از ویجت های چت زنده، فیس بوک مسنجر، پروفایل های توییتر، واتساپ، ایمیل ها و غیره به عنوان کانال پشتیبانی می کند. اگر می خواهید یک کانال سفارشی بسازید، می توانید آن را با استفاده از کانال API ایجاد کنید. برای شروع، یکی از کانال های زیر را انتخاب کنید.",
+ "TITLE_NEXT": "Complete the setup",
+ "TITLE_FINISH": "Voilà!",
+ "CHANNEL": {
+ "WEBSITE": {
+ "TITLE": "وب سایت",
+ "DESCRIPTION": "Create a live-chat widget"
+ },
+ "FACEBOOK": {
+ "TITLE": "فیسبوک",
+ "DESCRIPTION": "Connect your Facebook page"
+ },
+ "WHATSAPP": {
+ "TITLE": "WhatsApp",
+ "DESCRIPTION": "Support your customers on WhatsApp"
+ },
+ "EMAIL": {
+ "TITLE": "ایمیل",
+ "DESCRIPTION": "Connect with Gmail, Outlook, or other providers"
+ },
+ "SMS": {
+ "TITLE": "SMS",
+ "DESCRIPTION": "Integrate SMS channel with Twilio or bandwidth"
+ },
+ "API": {
+ "TITLE": "API",
+ "DESCRIPTION": "Make a custom channel using our API"
+ },
+ "TELEGRAM": {
+ "TITLE": "Telegram",
+ "DESCRIPTION": "Configure Telegram channel using Bot token"
+ },
+ "LINE": {
+ "TITLE": "Line",
+ "DESCRIPTION": "Integrate your Line channel"
+ },
+ "INSTAGRAM": {
+ "TITLE": "Instagram",
+ "DESCRIPTION": "Connect your instagram account"
+ },
+ "VOICE": {
+ "TITLE": "Voice",
+ "DESCRIPTION": "Integrate with Twilio Voice"
+ }
+ }
},
"AGENTS": {
"TITLE": "ایجنت ها",
@@ -478,7 +523,10 @@
"MESSAGE": "حالا از طریق این کانال جدید میتوانید با مشتریان صحبت کنید. به امید موفقیت",
"BUTTON_TEXT": "نشانم بده",
"MORE_SETTINGS": "تنظیمات بیشتر",
- "WEBSITE_SUCCESS": "ساختن کانال وب سایت با موفقیت انجام شد. قطعه کد زیر را کپی کرده و در سایت خود قرار دهید. در صورتیکه مشتری از ویجت پشتیبانی آنلاین استفاده کند گفتگوی شما در این صندوق ورودی ظاهر میشود."
+ "WEBSITE_SUCCESS": "ساختن کانال وب سایت با موفقیت انجام شد. قطعه کد زیر را کپی کرده و در سایت خود قرار دهید. در صورتیکه مشتری از ویجت پشتیبانی آنلاین استفاده کند گفتگوی شما در این صندوق ورودی ظاهر میشود.",
+ "WHATSAPP_QR_INSTRUCTION": "Scan the QR code above to quickly test your WhatsApp inbox",
+ "MESSENGER_QR_INSTRUCTION": "Scan the QR code above to quickly test your Facebook Messenger inbox",
+ "TELEGRAM_QR_INSTRUCTION": "Scan the QR code above to quickly test your Telegram inbox"
},
"REAUTH": "احراز هویت مجدد",
"VIEW": "نمایش",
@@ -616,8 +664,8 @@
"WHATSAPP_CONNECT_ERROR": "Failed to connect to WhatsApp Business. Please try again.",
"WHATSAPP_RECONFIGURE_SUCCESS": "Successfully reconfigured WhatsApp Business!",
"WHATSAPP_RECONFIGURE_ERROR": "Failed to reconfigure WhatsApp Business. Please try again.",
- "WHATSAPP_APP_ID_MISSING": "WhatsApp App ID is not configured. Please contact your administrator.",
- "WHATSAPP_CONFIG_ID_MISSING": "WhatsApp Configuration ID is not configured. Please contact your administrator.",
+ "WHATSAPP_APP_ID_MISSING": "شناسه اپلیکیشن واتساپ پیکربندی نشده است. لطفا با مدیر خود تماس بگیرید.",
+ "WHATSAPP_CONFIG_ID_MISSING": "شناسه پیکربندی واتساپ تنظیم نشده است. لطفا با مدیر خود تماس بگیرید.",
"WHATSAPP_LOGIN_CANCELLED": "WhatsApp login was cancelled. Please try again.",
"WHATSAPP_WEBHOOK_TITLE": "توکن تایید Webhook",
"WHATSAPP_WEBHOOK_SUBHEADER": "این توکن برای تأیید صحت نقطه پایانی webhook استفاده می شود.",
@@ -868,9 +916,18 @@
"SCRIPT_SETTINGS": "\n window.chatwootSettings = {options};"
},
"EMAIL_PROVIDERS": {
- "MICROSOFT": "مایکروسافت",
- "GOOGLE": "Google",
- "OTHER_PROVIDERS": "سایر ارائه دهندگان"
+ "MICROSOFT": {
+ "TITLE": "مایکروسافت",
+ "DESCRIPTION": "Connect with Microsoft"
+ },
+ "GOOGLE": {
+ "TITLE": "Google",
+ "DESCRIPTION": "Connect with Google"
+ },
+ "OTHER_PROVIDERS": {
+ "TITLE": "سایر ارائه دهندگان",
+ "DESCRIPTION": "Connect with Other Providers"
+ }
},
"CHANNELS": {
"MESSENGER": "Messenger",
diff --git a/app/javascript/dashboard/i18n/locale/fa/integrations.json b/app/javascript/dashboard/i18n/locale/fa/integrations.json
index 18c95d245..e911eb7e3 100644
--- a/app/javascript/dashboard/i18n/locale/fa/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/fa/integrations.json
@@ -704,11 +704,28 @@
"ERROR_MESSAGE": "There was an error creating the document, please try again."
},
"FORM": {
+ "TYPE": {
+ "LABEL": "Document Type",
+ "URL": "URL",
+ "PDF": "PDF File"
+ },
"URL": {
"LABEL": "URL",
"PLACEHOLDER": "Enter the URL of the document",
"ERROR": "Please provide a valid URL for the document"
},
+ "PDF_FILE": {
+ "LABEL": "PDF File",
+ "CHOOSE_FILE": "Choose PDF file",
+ "ERROR": "Please select a PDF file",
+ "HELP_TEXT": "Maximum file size: 10MB",
+ "INVALID_TYPE": "Please select a valid PDF file",
+ "TOO_LARGE": "File size exceeds 10MB limit"
+ },
+ "NAME": {
+ "LABEL": "Document Name (Optional)",
+ "PLACEHOLDER": "Enter a name for the document"
+ },
"ASSISTANT": {
"LABEL": "Assistant",
"PLACEHOLDER": "Select the assistant",
@@ -744,6 +761,7 @@
"SELECTED": "{count} selected",
"SELECT_ALL": "Select all ({count})",
"UNSELECT_ALL": "Unselect all ({count})",
+ "SEARCH_PLACEHOLDER": "Search FAQs...",
"BULK_APPROVE_BUTTON": "Approve",
"BULK_DELETE_BUTTON": "حذف",
"BULK_APPROVE": {
diff --git a/app/javascript/dashboard/i18n/locale/fa/mfa.json b/app/javascript/dashboard/i18n/locale/fa/mfa.json
new file mode 100644
index 000000000..e1031ace2
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/fa/mfa.json
@@ -0,0 +1,106 @@
+{
+ "MFA_SETTINGS": {
+ "TITLE": "Two-Factor Authentication",
+ "SUBTITLE": "Secure your account with TOTP-based authentication",
+ "DESCRIPTION": "Add an extra layer of security to your account using a time-based one-time password (TOTP)",
+ "STATUS_TITLE": "Authentication Status",
+ "STATUS_DESCRIPTION": "Manage your two-factor authentication settings and backup recovery codes",
+ "ENABLED": "فعال شد",
+ "DISABLED": "غیرفعال شد",
+ "STATUS_ENABLED": "Two-factor authentication is active",
+ "STATUS_ENABLED_DESC": "Your account is protected with an additional layer of security",
+ "ENABLE_BUTTON": "Enable Two-Factor Authentication",
+ "ENHANCE_SECURITY": "Enhance Your Account Security",
+ "ENHANCE_SECURITY_DESC": "Two-factor authentication adds an extra layer of security by requiring a verification code from your authenticator app in addition to your password.",
+ "SETUP": {
+ "STEP_NUMBER_1": "1",
+ "STEP_NUMBER_2": "0",
+ "STEP1_TITLE": "Scan QR Code with Your Authenticator App",
+ "STEP1_DESCRIPTION": "Use Google Authenticator, Authy, or any TOTP-compatible app",
+ "LOADING_QR": "Loading...",
+ "MANUAL_ENTRY": "Can't scan? Enter code manually",
+ "SECRET_KEY": "Secret Key",
+ "COPY": "کپی",
+ "ENTER_CODE": "Enter the 6-digit code from your authenticator app",
+ "ENTER_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify & Continue",
+ "CANCEL": "انصراف",
+ "ERROR_STARTING": "MFA not enabled. Please contact administrator.",
+ "INVALID_CODE": "Invalid verification code",
+ "SECRET_COPIED": "Secret key copied to clipboard",
+ "SUCCESS": "Two-factor authentication has been enabled successfully"
+ },
+ "BACKUP": {
+ "TITLE": "Save Your Backup Codes",
+ "DESCRIPTION": "Keep these codes safe. Each can be used once if you lose access to your authenticator",
+ "IMPORTANT": "Important:",
+ "IMPORTANT_NOTE": " Save these codes in a secure location. You won't be able to see them again.",
+ "DOWNLOAD": "دانلود",
+ "COPY_ALL": "Copy All",
+ "CONFIRM": "I have saved my backup codes in a secure location and understand that I won't be able to see them again",
+ "COMPLETE_SETUP": "Complete Setup",
+ "CODES_COPIED": "Backup codes copied to clipboard"
+ },
+ "MANAGEMENT": {
+ "BACKUP_CODES": "Backup Codes",
+ "BACKUP_CODES_DESC": "Generate new codes if you've lost or used your existing ones",
+ "REGENERATE": "Regenerate Backup Codes",
+ "DISABLE_MFA": "Disable 2FA",
+ "DISABLE_MFA_DESC": "Remove two-factor authentication from your account",
+ "DISABLE_BUTTON": "Disable Two-Factor Authentication"
+ },
+ "DISABLE": {
+ "TITLE": "Disable Two-Factor Authentication",
+ "DESCRIPTION": "You'll need to enter your password and a verification code to disable two-factor authentication.",
+ "PASSWORD": "رمز عبور",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Disable 2FA",
+ "CANCEL": "انصراف",
+ "SUCCESS": "Two-factor authentication has been disabled",
+ "ERROR": "Failed to disable MFA. Please check your credentials."
+ },
+ "REGENERATE": {
+ "TITLE": "Regenerate Backup Codes",
+ "DESCRIPTION": "This will invalidate your existing backup codes and generate new ones. Enter your verification code to continue.",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Generate New Codes",
+ "CANCEL": "انصراف",
+ "NEW_CODES_TITLE": "New Backup Codes Generated",
+ "NEW_CODES_DESC": "Your old backup codes have been invalidated. Save these new codes in a secure location.",
+ "CODES_IMPORTANT": "Important:",
+ "CODES_IMPORTANT_NOTE": " Each code can only be used once. Save them before closing this window.",
+ "DOWNLOAD_CODES": "Download Codes",
+ "COPY_ALL_CODES": "Copy All Codes",
+ "CODES_SAVED": "I've Saved My Codes",
+ "SUCCESS": "New backup codes have been generated",
+ "ERROR": "Failed to regenerate backup codes"
+ }
+ },
+ "MFA_VERIFICATION": {
+ "TITLE": "Two-Factor Authentication",
+ "DESCRIPTION": "Enter your verification code to continue",
+ "AUTHENTICATOR_APP": "Authenticator App",
+ "BACKUP_CODE": "Backup Code",
+ "ENTER_OTP_CODE": "Enter 6-digit code from your authenticator app",
+ "ENTER_BACKUP_CODE": "Enter one of your backup codes",
+ "BACKUP_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify",
+ "TRY_ANOTHER_METHOD": "Try another verification method",
+ "CANCEL_LOGIN": "Cancel and return to login",
+ "HELP_TEXT": "Having trouble signing in?",
+ "LEARN_MORE": "Learn more about 2FA",
+ "HELP_MODAL": {
+ "TITLE": "Two-Factor Authentication Help",
+ "AUTHENTICATOR_TITLE": "Using an Authenticator App",
+ "AUTHENTICATOR_DESC": "Open your authenticator app (Google Authenticator, Authy, etc.) and enter the 6-digit code shown for your account.",
+ "BACKUP_TITLE": "Using a Backup Code",
+ "BACKUP_DESC": "If you don't have access to your authenticator app, you can use one of the backup codes you saved when setting up 2FA. Each code can only be used once.",
+ "CONTACT_TITLE": "Need More Help?",
+ "CONTACT_DESC_CLOUD": "If you've lost access to both your authenticator app and backup codes, please reach out to Chatwoot support for assistance.",
+ "CONTACT_DESC_SELF_HOSTED": "If you've lost access to both your authenticator app and backup codes, please contact your administrator for assistance."
+ },
+ "VERIFICATION_FAILED": "Verification failed. Please try again."
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/fa/settings.json b/app/javascript/dashboard/i18n/locale/fa/settings.json
index 32f54fdfe..84c1c7455 100644
--- a/app/javascript/dashboard/i18n/locale/fa/settings.json
+++ b/app/javascript/dashboard/i18n/locale/fa/settings.json
@@ -51,6 +51,13 @@
"LARGER": "Larger",
"EXTRA_LARGE": "Extra Large"
}
+ },
+ "LANGUAGE": {
+ "TITLE": "Preferred Language",
+ "NOTE": "Choose the language you want to use.",
+ "UPDATE_SUCCESS": "Your Language settings have been updated successfully",
+ "UPDATE_ERROR": "There is an error while updating the language settings, please try again",
+ "USE_ACCOUNT_DEFAULT": "Use account default"
}
},
"MESSAGE_SIGNATURE_SECTION": {
@@ -73,6 +80,11 @@
"NOTE": "تغییر دادن رمز عبور باعث میشود مجبور شوید دوباره به سیستم وارد شوید",
"BTN_TEXT": "تغییر رمز عبور"
},
+ "SECURITY_SECTION": {
+ "TITLE": "Security",
+ "NOTE": "Manage additional security features for your account.",
+ "MFA_BUTTON": "Manage Two-Factor Authentication"
+ },
"ACCESS_TOKEN": {
"TITLE": "توکن دسترسی",
"NOTE": "از این توکن برای دسترسی از طریق API استفاده میشود",
@@ -80,8 +92,8 @@
"RESET": "Reset",
"CONFIRM_RESET": "Are you sure?",
"CONFIRM_HINT": "Click again to confirm",
- "RESET_SUCCESS": "Access token regenerated successfully",
- "RESET_ERROR": "Unable to regenerate access token. Please try again"
+ "RESET_SUCCESS": "توکن دسترسی با موفقیت بازسازی شد",
+ "RESET_ERROR": "خطا در بازسازی توکن دسترسی. لطفا مجدد امتحان کنید"
},
"AUDIO_NOTIFICATIONS_SECTION": {
"TITLE": "Audio Alerts",
@@ -330,6 +342,7 @@
"REPORTS_LABEL": "برچسبها",
"REPORTS_INBOX": "صندوق ورودی",
"REPORTS_TEAM": "تیم",
+ "AGENT_ASSIGNMENT": "Agent Assignment",
"SET_AVAILABILITY_TITLE": "خود را به عنوان",
"SET_YOUR_AVAILABILITY": "در دسترس بودن خود را تنظیم کنید",
"SLA": "SLA",
@@ -350,7 +363,8 @@
"INFO_TEXT": "هنگامی که از برنامه یا پیشخوان استفاده نمیکنید، به سیستم اجازه دهید به طور خودکار شما را به صورت آفلاین علامت گذاری کند.",
"INFO_SHORT": "Automatically mark offline when you aren't using the app."
},
- "DOCS": "اسناد را بخوانید"
+ "DOCS": "اسناد را بخوانید",
+ "SECURITY": "Security"
},
"BILLING_SETTINGS": {
"TITLE": "صورتحساب",
@@ -382,6 +396,77 @@
},
"NO_BILLING_USER": "صورتحساب حساب شما در حال پیکربندی است. لطفا صفحه را مجددا بارگزاری کرده و دوباره تلاش کنید."
},
+ "SECURITY_SETTINGS": {
+ "TITLE": "Security",
+ "DESCRIPTION": "Manage your account security settings.",
+ "LINK_TEXT": "Learn more about SAML SSO",
+ "SAML": {
+ "TITLE": "SAML SSO",
+ "NOTE": "Configure SAML single sign-on for your account. Users will authenticate through your identity provider instead of using email/password.",
+ "ACS_URL": {
+ "LABEL": "ACS URL",
+ "TOOLTIP": "Assertion Consumer Service URL - Configure this URL in your IdP as the destination for SAML responses"
+ },
+ "SSO_URL": {
+ "LABEL": "SSO URL",
+ "HELP": "The URL where SAML authentication requests will be sent",
+ "PLACEHOLDER": "https://your-idp.com/saml/sso"
+ },
+ "CERTIFICATE": {
+ "LABEL": "Signing certificate in PEM format",
+ "HELP": "The public certificate from your identity provider used to verify SAML responses",
+ "PLACEHOLDER": "-----BEGIN CERTIFICATE-----\nMIIC..."
+ },
+ "FINGERPRINT": {
+ "LABEL": "Fingerprint",
+ "TOOLTIP": "SHA-1 fingerprint of the certificate - Use this to verify the certificate in your IdP configuration"
+ },
+ "COPY_SUCCESS": "کد به حافظه کپی شد",
+ "SP_ENTITY_ID": {
+ "LABEL": "SP Entity ID",
+ "HELP": "Unique identifier for this application as a service provider (auto-generated).",
+ "TOOLTIP": "Unique identifier for Chatwoot as the Service Provider - Configure this in your IdP settings"
+ },
+ "IDP_ENTITY_ID": {
+ "LABEL": "Identity Provider Entity ID",
+ "HELP": "Unique identifier for your identity provider (usually found in IdP configuration)",
+ "PLACEHOLDER": "https://your-idp.com/saml"
+ },
+ "UPDATE_BUTTON": "Update SAML Settings",
+ "API": {
+ "SUCCESS": "SAML settings updated successfully",
+ "ERROR": "Failed to update SAML settings",
+ "ERROR_LOADING": "Failed to load SAML settings",
+ "DISABLED": "SAML settings disabled successfully"
+ },
+ "VALIDATION": {
+ "REQUIRED_FIELDS": "SSO URL, Identity Provider Entity ID, and Certificate are required fields",
+ "SSO_URL_ERROR": "Please enter a valid SSO URL",
+ "CERTIFICATE_ERROR": "Certificate is required",
+ "IDP_ENTITY_ID_ERROR": "Identity Provider Entity ID is required"
+ },
+ "ENTERPRISE_PAYWALL": {
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade to an Enterprise plan to access SAML single sign-on and other advanced security features.",
+ "ASK_ADMIN": "لطفاً برای ارتقا با ادمین خود تماس بگیرید."
+ },
+ "PAYWALL": {
+ "TITLE": "Upgrade to enable SAML SSO",
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade your plan to get access to SAML single sign-on and other advanced features.",
+ "UPGRADE_NOW": "حالا ارتقا دهید",
+ "CANCEL_ANYTIME": "You can change or cancel your plan anytime"
+ },
+ "ATTRIBUTE_MAPPING": {
+ "TITLE": "SAML Attribute Setup",
+ "DESCRIPTION": "The following attribute mappings must be configured in your identity provider"
+ },
+ "INFO_SECTION": {
+ "TITLE": "Service Provider Information",
+ "TOOLTIP": "Copy these values and configure them in your Identity Provider to establish the SAML connection"
+ }
+ }
+ },
"CREATE_ACCOUNT": {
"NO_ACCOUNT_WARNING": "اوه اوه! ما هیچ حسابی روی Chatwoot پاز شما پیدا نکردیم. لطفاً برای ادامه یک حساب جدید ایجاد کنید.",
"NEW_ACCOUNT": "حسابکاربری جدید",
@@ -418,5 +503,255 @@
"SWITCH_TO_REPLY": "رفتن به پاسخ",
"TOGGLE_SNOOZE_DROPDOWN": "تغییر حالت بازکردن تعویق"
}
+ },
+ "ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent assignment",
+ "DESCRIPTION": "Define policies to effectively manage workload and route conversations based on the needs of inboxes and agents. Learn more here"
+ },
+ "ASSIGNMENT_POLICY": {
+ "TITLE": "Assignment policy",
+ "DESCRIPTION": "Manage how conversations get assigned in inboxes.",
+ "FEATURES": [
+ "Assign by conversations evenly or by available capacity",
+ "Add fair distribution rules to avoid overloading any agent",
+ "Add inboxes to a policy - one policy per inbox"
+ ]
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "TITLE": "Agent capacity policy",
+ "DESCRIPTION": "Manage workload for agents.",
+ "FEATURES": [
+ "Define maximum conversations per inbox",
+ "Create exceptions based on labels and time",
+ "Add agents to a policy - one policy per agent"
+ ]
+ }
+ },
+ "AGENT_ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Assignment policy",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "ORDER": "Order",
+ "PRIORITY": "اولویت",
+ "ACTIVE": "فعال",
+ "INACTIVE": "Inactive",
+ "POPOVER": "Added inboxes",
+ "EDIT": "ویرایش"
+ },
+ "NO_RECORDS_FOUND": "No assignment policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create assignment policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy created successfully",
+ "ERROR_MESSAGE": "Failed to create assignment policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit assignment policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_INBOX_DIALOG": {
+ "TITLE": "Add inbox",
+ "DESCRIPTION": "{inboxName} inbox is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "انصراف"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update assignment policy"
+ },
+ "INBOX_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Inbox added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Inbox removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove inbox from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "توضیحات:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "STATUS": {
+ "LABEL": "وضعیت:",
+ "PLACEHOLDER": "Select status",
+ "ACTIVE": "Policy is active",
+ "INACTIVE": "Policy is inactive"
+ },
+ "ASSIGNMENT_ORDER": {
+ "LABEL": "Assignment order",
+ "ROUND_ROBIN": {
+ "LABEL": "Round robin",
+ "DESCRIPTION": "Assign conversations evenly among agents."
+ },
+ "BALANCED": {
+ "LABEL": "Balanced",
+ "DESCRIPTION": "Assign conversations based on available capacity."
+ }
+ },
+ "ASSIGNMENT_PRIORITY": {
+ "LABEL": "Assignment priority",
+ "EARLIEST_CREATED": {
+ "LABEL": "Earliest created",
+ "DESCRIPTION": "The conversation that was created first gets assigned first."
+ },
+ "LONGEST_WAITING": {
+ "LABEL": "Longest waiting",
+ "DESCRIPTION": "The conversation waiting the longest gets assigned first."
+ }
+ },
+ "FAIR_DISTRIBUTION": {
+ "LABEL": "Fair distribution policy",
+ "DESCRIPTION": "Set the maximum number of conversations that can be assigned per agent within a time window to avoid overloading any one agent. This required field defaults to 100 conversations per hour.",
+ "INPUT_MAX": "Assign max",
+ "DURATION": "Conversations per agent in every"
+ },
+ "INBOXES": {
+ "LABEL": "Added inboxes",
+ "DESCRIPTION": "Add inboxes for which this policy will be applicable.",
+ "ADD_BUTTON": "Add inbox",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select inboxes to add",
+ "ADD_BUTTON": "افزودن"
+ },
+ "EMPTY_STATE": "No inboxes added to this policy, add an inbox to get started",
+ "API": {
+ "SUCCESS_MESSAGE": "Inbox successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Assignment policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete assignment policy"
+ }
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent capacity",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "POPOVER": "Added agents",
+ "EDIT": "ویرایش"
+ },
+ "NO_RECORDS_FOUND": "No agent capacity policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create agent capacity policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy created successfully",
+ "ERROR_MESSAGE": "Failed to create agent capacity policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit agent capacity policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_AGENT_DIALOG": {
+ "TITLE": "Add agent",
+ "DESCRIPTION": "{agentName} is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "انصراف"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update agent capacity policy"
+ },
+ "AGENT_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Agent added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Agent removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove agent from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "توضیحات:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "INBOX_CAPACITY_LIMIT": {
+ "LABEL": "Inbox capacity limits",
+ "ADD_BUTTON": "Add inbox",
+ "FIELD": {
+ "SELECT_INBOX": "Select inbox",
+ "MAX_CONVERSATIONS": "Max conversations",
+ "SET_LIMIT": "Set limit"
+ },
+ "EMPTY_STATE": "No inbox limit set"
+ },
+ "EXCLUSION_RULES": {
+ "LABEL": "Exclusion rules",
+ "DESCRIPTION": "Conversations that satisfy the following conditions would not count towards agent capacity",
+ "TAGS": {
+ "LABEL": "Exclude conversations tagged with specific labels",
+ "ADD_TAG": "add tag",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select tags to add"
+ },
+ "EMPTY_STATE": "No tags added to this policy."
+ },
+ "DURATION": {
+ "LABEL": "Exclude conversations older than a specified duration",
+ "PLACEHOLDER": "Set time"
+ }
+ },
+ "USERS": {
+ "LABEL": "Assigned agents",
+ "DESCRIPTION": "Add agents for which this policy will be applicable.",
+ "ADD_BUTTON": "Add agent",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select agents to add",
+ "ADD_BUTTON": "افزودن"
+ },
+ "EMPTY_STATE": "No agents added",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Agent capacity policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete agent capacity policy"
+ }
+ },
+ "DELETE_POLICY": {
+ "TITLE": "Delete policy",
+ "DESCRIPTION": "Are you sure you want to delete this policy? This action cannot be undone.",
+ "CONFIRM_BUTTON_LABEL": "حذف",
+ "CANCEL_BUTTON_LABEL": "انصراف"
+ }
}
}
diff --git a/app/javascript/dashboard/i18n/locale/fa/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/fa/whatsappTemplates.json
index ed5d76d1c..d8b2f0281 100644
--- a/app/javascript/dashboard/i18n/locale/fa/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/fa/whatsappTemplates.json
@@ -40,6 +40,7 @@
"BUTTON_LABEL": "Button {index}",
"COUPON_CODE": "Enter coupon code (max 15 chars)",
"MEDIA_URL_LABEL": "Enter {type} URL",
+ "DOCUMENT_NAME_PLACEHOLDER": "Enter document filename (e.g., Invoice_2025.pdf)",
"BUTTON_PARAMETER": "Enter button parameter"
}
}
diff --git a/app/javascript/dashboard/i18n/locale/fi/automation.json b/app/javascript/dashboard/i18n/locale/fi/automation.json
index 740761ca5..ea8ecab1d 100644
--- a/app/javascript/dashboard/i18n/locale/fi/automation.json
+++ b/app/javascript/dashboard/i18n/locale/fi/automation.json
@@ -177,7 +177,8 @@
"REFERER_LINK": "Referrer Link",
"ASSIGNEE_NAME": "Assignee",
"TEAM_NAME": "Team",
- "PRIORITY": "Priority"
+ "PRIORITY": "Priority",
+ "LABELS": "Tunnisteet"
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/fi/contact.json b/app/javascript/dashboard/i18n/locale/fi/contact.json
index 2854f50e0..d12b53b9f 100644
--- a/app/javascript/dashboard/i18n/locale/fi/contact.json
+++ b/app/javascript/dashboard/i18n/locale/fi/contact.json
@@ -610,6 +610,15 @@
"SEND_MESSAGE": "Lähetä viesti"
}
},
+ "TWILIO_OPTIONS": {
+ "LABEL": "Select template",
+ "SEARCH_PLACEHOLDER": "Search templates",
+ "EMPTY_STATE": "No templates found",
+ "TEMPLATE_PARSER": {
+ "BACK": "Mene takaisin",
+ "SEND_MESSAGE": "Lähetä viesti"
+ }
+ },
"ACTION_BUTTONS": {
"DISCARD": "Discard",
"SEND": "Send ({keyCode})"
diff --git a/app/javascript/dashboard/i18n/locale/fi/contactFilters.json b/app/javascript/dashboard/i18n/locale/fi/contactFilters.json
index ff4f63919..1455520ce 100644
--- a/app/javascript/dashboard/i18n/locale/fi/contactFilters.json
+++ b/app/javascript/dashboard/i18n/locale/fi/contactFilters.json
@@ -48,7 +48,8 @@
"CREATED_AT": "Created At",
"LAST_ACTIVITY": "Last Activity",
"REFERER_LINK": "Referrer link",
- "BLOCKED": "Blocked"
+ "BLOCKED": "Blocked",
+ "LABELS": "Tunnisteet"
},
"GROUPS": {
"STANDARD_FILTERS": "Standard Filters",
diff --git a/app/javascript/dashboard/i18n/locale/fi/contentTemplates.json b/app/javascript/dashboard/i18n/locale/fi/contentTemplates.json
new file mode 100644
index 000000000..d9509a6a6
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/fi/contentTemplates.json
@@ -0,0 +1,51 @@
+{
+ "CONTENT_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Twilio Templates",
+ "SUBTITLE": "Select the Twilio template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Etsi Pohjia",
+ "NO_TEMPLATES_FOUND": "No templates found for",
+ "NO_CONTENT": "No content",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Category",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No Twilio templates available. Click refresh to sync templates from Twilio.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Language",
+ "TEMPLATE_BODY": "Template Body",
+ "CATEGORY": "Category"
+ },
+ "TYPES": {
+ "MEDIA": "Media",
+ "QUICK_REPLY": "Quick Reply",
+ "TEXT": "Text"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Muuttujat",
+ "LANGUAGE": "Language",
+ "CATEGORY": "Category",
+ "VARIABLE_PLACEHOLDER": "Enter {variable} value",
+ "GO_BACK_LABEL": "Mene Takaisin",
+ "SEND_MESSAGE_LABEL": "Lähetä Viesti",
+ "FORM_ERROR_MESSAGE": "Täytä kaikki muuttujat ennen lähettämistä",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "MEDIA_URL_LABEL": "Enter full media URL",
+ "MEDIA_URL_PLACEHOLDER": "https://example.com/image.jpg"
+ },
+ "FORM": {
+ "BACK_BUTTON": "Takaisin",
+ "SEND_MESSAGE_BUTTON": "Lähetä Viesti"
+ }
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/fi/conversation.json b/app/javascript/dashboard/i18n/locale/fi/conversation.json
index 0d18260ef..929b96c41 100644
--- a/app/javascript/dashboard/i18n/locale/fi/conversation.json
+++ b/app/javascript/dashboard/i18n/locale/fi/conversation.json
@@ -35,6 +35,11 @@
"API_HOURS_WINDOW": "You can only reply to this conversation within {hours} hours",
"NOT_ASSIGNED_TO_YOU": "Tätä keskustelua ei ole määritetty sinulle. Haluatko siirtää tämän keskustelun itsellesi?",
"ASSIGN_TO_ME": "Siirrä minulle",
+ "BOT_HANDOFF_MESSAGE": "You are responding to a conversation which is currently handled by an assistant or a bot.",
+ "BOT_HANDOFF_ACTION": "Mark open and assign to you",
+ "BOT_HANDOFF_REOPEN_ACTION": "Mark conversation open",
+ "BOT_HANDOFF_SUCCESS": "Conversation has been handed over to you",
+ "BOT_HANDOFF_ERROR": "Failed to take over the conversation. Please try again.",
"TWILIO_WHATSAPP_CAN_REPLY": "You can only reply to this conversation using a template message due to",
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "24h vastausikkuna",
"OLD_INSTAGRAM_INBOX_REPLY_BANNER": "This Instagram account was migrated to the new Instagram channel inbox. All new messages will show up there. You won’t be able to send messages from this conversation anymore.",
@@ -66,6 +71,17 @@
"SHOW_LABELS": "Show labels",
"HIDE_LABELS": "Hide labels"
},
+ "VOICE_CALL": {
+ "INCOMING_CALL": "Incoming call",
+ "OUTGOING_CALL": "Outgoing call",
+ "CALL_IN_PROGRESS": "Call in progress",
+ "NO_ANSWER": "No answer",
+ "MISSED_CALL": "Missed call",
+ "CALL_ENDED": "Call ended",
+ "NOT_ANSWERED_YET": "Not answered yet",
+ "THEY_ANSWERED": "They answered",
+ "YOU_ANSWERED": "You answered"
+ },
"HEADER": {
"RESOLVE_ACTION": "Ratkaise",
"REOPEN_ACTION": "Uudelleenavaa",
diff --git a/app/javascript/dashboard/i18n/locale/fi/helpCenter.json b/app/javascript/dashboard/i18n/locale/fi/helpCenter.json
index af4621aea..6b593e9b6 100644
--- a/app/javascript/dashboard/i18n/locale/fi/helpCenter.json
+++ b/app/javascript/dashboard/i18n/locale/fi/helpCenter.json
@@ -741,7 +741,8 @@
"LIVE_CHAT_WIDGET": {
"LABEL": "Live chat widget",
"PLACEHOLDER": "Select live chat widget",
- "HELP_TEXT": "Select a live chat widget that will appear on your help center"
+ "HELP_TEXT": "Select a live chat widget that will appear on your help center",
+ "NONE_OPTION": "No widget"
},
"BRAND_COLOR": {
"LABEL": "Brand color"
@@ -807,6 +808,58 @@
"ERROR_MESSAGE": "Unable to update portal"
}
}
+ },
+ "PDF_UPLOAD": {
+ "TITLE": "Upload PDF Document",
+ "DESCRIPTION": "Upload a PDF document to automatically generate FAQs using AI",
+ "DRAG_DROP_TEXT": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "ADDITIONAL_CONTEXT_LABEL": "Additional Context (Optional)",
+ "ADDITIONAL_CONTEXT_PLACEHOLDER": "Provide any additional context or instructions for FAQ generation...",
+ "UPLOADING": "Lähetetään...",
+ "UPLOAD": "Upload & Process",
+ "CANCEL": "Peruuta",
+ "ERROR_INVALID_TYPE": "Please select a valid PDF file",
+ "ERROR_FILE_TOO_LARGE": "File size must be less than 512MB",
+ "ERROR_UPLOAD_FAILED": "Failed to upload PDF. Please try again."
+ },
+ "PDF_DOCUMENTS": {
+ "TITLE": "PDF Documents",
+ "DESCRIPTION": "Manage uploaded PDF documents and generate FAQs from them",
+ "UPLOAD_PDF": "Upload PDF",
+ "UPLOAD_FIRST_PDF": "Upload your first PDF",
+ "UPLOADED_BY": "Uploaded by",
+ "GENERATE_FAQS": "Generate FAQs",
+ "GENERATING": "Generating...",
+ "CONFIRM_DELETE": "Are you sure you want to delete {filename}?",
+ "EMPTY_STATE": {
+ "TITLE": "No PDF documents yet",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQs using AI"
+ },
+ "STATUS": {
+ "UPLOADED": "Ready",
+ "PROCESSING": "Processing",
+ "PROCESSED": "Completed",
+ "FAILED": "Failed"
+ }
+ },
+ "CONTENT_GENERATION": {
+ "TITLE": "Content Generation",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQ content using AI",
+ "UPLOAD_TITLE": "Upload PDF Document",
+ "DRAG_DROP": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "UPLOADING": "Processing document...",
+ "UPLOAD_SUCCESS": "Document processed successfully!",
+ "UPLOAD_ERROR": "Failed to upload document. Please try again.",
+ "INVALID_FILE_TYPE": "Please select a valid PDF file",
+ "FILE_TOO_LARGE": "File size must be less than 512MB",
+ "GENERATED_CONTENT": "Generated FAQ Content",
+ "PUBLISH_SELECTED": "Publish Selected",
+ "PUBLISHING": "Publishing...",
+ "FROM_DOCUMENT": "From document",
+ "NO_CONTENT": "No generated content available. Upload a PDF document to get started.",
+ "LOADING": "Loading generated content..."
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/fi/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/fi/inboxMgmt.json
index 9570d8afb..0cf02c095 100644
--- a/app/javascript/dashboard/i18n/locale/fi/inboxMgmt.json
+++ b/app/javascript/dashboard/i18n/locale/fi/inboxMgmt.json
@@ -272,8 +272,8 @@
},
"SUBMIT_BUTTON": "Create WhatsApp Channel",
"EMBEDDED_SIGNUP": {
- "TITLE": "Quick Setup with Meta",
- "DESC": "You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
+ "TITLE": "Quick setup with Meta",
+ "DESC": "Use the WhatsApp Embedded Signup flow to quickly connect new numbers. You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
"BENEFITS": {
"TITLE": "Benefits of Embedded Signup:",
"EASY_SETUP": "No manual configuration required",
@@ -281,9 +281,8 @@
"AUTO_CONFIG": "Automatic webhook and phone number configuration"
},
"LEARN_MORE": {
- "TEXT": "To learn more about integrated signup, pricing, and limitations, visit",
- "LINK_TEXT": "this link.",
- "LINK_URL": "https://developers.facebook.com/docs/whatsapp/embedded-signup/custom-flows/onboarding-business-app-users#limitations"
+ "TEXT": "To learn more about integrated signup, pricing, and limitations, visit {link}.",
+ "LINK_TEXT": "this link"
},
"SUBMIT_BUTTON": "Connect with WhatsApp Business",
"AUTH_PROCESSING": "Authenticating with Meta",
@@ -296,7 +295,9 @@
"INVALID_BUSINESS_DATA": "Invalid business data received from Facebook. Please try again.",
"SIGNUP_ERROR": "Signup error occurred",
"AUTH_NOT_COMPLETED": "Authentication not completed. Please restart the process.",
- "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured"
+ "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured",
+ "MANUAL_FALLBACK": "If your number is already connected to the WhatsApp Business Platform (API), or if you’re a tech provider onboarding your own number, please use the {link} flow",
+ "MANUAL_LINK_TEXT": "manual setup flow"
},
"API": {
"ERROR_MESSAGE": "We were not able to save the WhatsApp channel"
@@ -424,7 +425,51 @@
},
"AUTH": {
"TITLE": "Choose a channel",
- "DESC": "Chatwoot tukee live-chat-widgetejä, Facebook Messenger, WhatsApp, sähköpostit jne. kanavina. Jos haluat rakentaa mukautetun kanavan, voit luoda sen API-kanavalla. Päästäksesi alkuun, valitse jokin kanava alta."
+ "DESC": "Chatwoot tukee live-chat-widgetejä, Facebook Messenger, WhatsApp, sähköpostit jne. kanavina. Jos haluat rakentaa mukautetun kanavan, voit luoda sen API-kanavalla. Päästäksesi alkuun, valitse jokin kanava alta.",
+ "TITLE_NEXT": "Complete the setup",
+ "TITLE_FINISH": "Voilà!",
+ "CHANNEL": {
+ "WEBSITE": {
+ "TITLE": "Website",
+ "DESCRIPTION": "Create a live-chat widget"
+ },
+ "FACEBOOK": {
+ "TITLE": "Facebook",
+ "DESCRIPTION": "Connect your Facebook page"
+ },
+ "WHATSAPP": {
+ "TITLE": "WhatsApp",
+ "DESCRIPTION": "Support your customers on WhatsApp"
+ },
+ "EMAIL": {
+ "TITLE": "Sähköposti",
+ "DESCRIPTION": "Connect with Gmail, Outlook, or other providers"
+ },
+ "SMS": {
+ "TITLE": "SMS",
+ "DESCRIPTION": "Integrate SMS channel with Twilio or bandwidth"
+ },
+ "API": {
+ "TITLE": "API",
+ "DESCRIPTION": "Make a custom channel using our API"
+ },
+ "TELEGRAM": {
+ "TITLE": "Telegram",
+ "DESCRIPTION": "Configure Telegram channel using Bot token"
+ },
+ "LINE": {
+ "TITLE": "Line",
+ "DESCRIPTION": "Integrate your Line channel"
+ },
+ "INSTAGRAM": {
+ "TITLE": "Instagram",
+ "DESCRIPTION": "Connect your instagram account"
+ },
+ "VOICE": {
+ "TITLE": "Voice",
+ "DESCRIPTION": "Integrate with Twilio Voice"
+ }
+ }
},
"AGENTS": {
"TITLE": "Edustajat",
@@ -478,7 +523,10 @@
"MESSAGE": "You can now engage with your customers through your new Channel. Happy supporting",
"BUTTON_TEXT": "Vie minut sinne",
"MORE_SETTINGS": "More settings",
- "WEBSITE_SUCCESS": "Olet onnistuneesti lisännyt sivuston chat-widgetin. Kopioi alla näkyvä koodi ja liitä se verkkosivuillesi. Seuraavalla kerralla kun asiakas käyttää live-keskustelua, keskustelu ilmestyy automaattisesti saapuneet-kansioon."
+ "WEBSITE_SUCCESS": "Olet onnistuneesti lisännyt sivuston chat-widgetin. Kopioi alla näkyvä koodi ja liitä se verkkosivuillesi. Seuraavalla kerralla kun asiakas käyttää live-keskustelua, keskustelu ilmestyy automaattisesti saapuneet-kansioon.",
+ "WHATSAPP_QR_INSTRUCTION": "Scan the QR code above to quickly test your WhatsApp inbox",
+ "MESSENGER_QR_INSTRUCTION": "Scan the QR code above to quickly test your Facebook Messenger inbox",
+ "TELEGRAM_QR_INSTRUCTION": "Scan the QR code above to quickly test your Telegram inbox"
},
"REAUTH": "Uudelleenvaltuuta",
"VIEW": "Näytä",
@@ -868,9 +916,18 @@
"SCRIPT_SETTINGS": "\n window.chatwootSettings = {options};"
},
"EMAIL_PROVIDERS": {
- "MICROSOFT": "Microsoft",
- "GOOGLE": "Google",
- "OTHER_PROVIDERS": "Other Providers"
+ "MICROSOFT": {
+ "TITLE": "Microsoft",
+ "DESCRIPTION": "Connect with Microsoft"
+ },
+ "GOOGLE": {
+ "TITLE": "Google",
+ "DESCRIPTION": "Connect with Google"
+ },
+ "OTHER_PROVIDERS": {
+ "TITLE": "Other Providers",
+ "DESCRIPTION": "Connect with Other Providers"
+ }
},
"CHANNELS": {
"MESSENGER": "Messenger",
diff --git a/app/javascript/dashboard/i18n/locale/fi/integrations.json b/app/javascript/dashboard/i18n/locale/fi/integrations.json
index 378242517..c49e18379 100644
--- a/app/javascript/dashboard/i18n/locale/fi/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/fi/integrations.json
@@ -704,11 +704,28 @@
"ERROR_MESSAGE": "There was an error creating the document, please try again."
},
"FORM": {
+ "TYPE": {
+ "LABEL": "Document Type",
+ "URL": "URL",
+ "PDF": "PDF File"
+ },
"URL": {
"LABEL": "URL",
"PLACEHOLDER": "Enter the URL of the document",
"ERROR": "Please provide a valid URL for the document"
},
+ "PDF_FILE": {
+ "LABEL": "PDF File",
+ "CHOOSE_FILE": "Choose PDF file",
+ "ERROR": "Please select a PDF file",
+ "HELP_TEXT": "Maximum file size: 10MB",
+ "INVALID_TYPE": "Please select a valid PDF file",
+ "TOO_LARGE": "File size exceeds 10MB limit"
+ },
+ "NAME": {
+ "LABEL": "Document Name (Optional)",
+ "PLACEHOLDER": "Enter a name for the document"
+ },
"ASSISTANT": {
"LABEL": "Assistant",
"PLACEHOLDER": "Select the assistant",
@@ -744,6 +761,7 @@
"SELECTED": "{count} selected",
"SELECT_ALL": "Select all ({count})",
"UNSELECT_ALL": "Unselect all ({count})",
+ "SEARCH_PLACEHOLDER": "Search FAQs...",
"BULK_APPROVE_BUTTON": "Approve",
"BULK_DELETE_BUTTON": "Poista",
"BULK_APPROVE": {
diff --git a/app/javascript/dashboard/i18n/locale/fi/mfa.json b/app/javascript/dashboard/i18n/locale/fi/mfa.json
new file mode 100644
index 000000000..ad2b19764
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/fi/mfa.json
@@ -0,0 +1,106 @@
+{
+ "MFA_SETTINGS": {
+ "TITLE": "Two-Factor Authentication",
+ "SUBTITLE": "Secure your account with TOTP-based authentication",
+ "DESCRIPTION": "Add an extra layer of security to your account using a time-based one-time password (TOTP)",
+ "STATUS_TITLE": "Authentication Status",
+ "STATUS_DESCRIPTION": "Manage your two-factor authentication settings and backup recovery codes",
+ "ENABLED": "Käytössä",
+ "DISABLED": "Pois käytöstä",
+ "STATUS_ENABLED": "Two-factor authentication is active",
+ "STATUS_ENABLED_DESC": "Your account is protected with an additional layer of security",
+ "ENABLE_BUTTON": "Enable Two-Factor Authentication",
+ "ENHANCE_SECURITY": "Enhance Your Account Security",
+ "ENHANCE_SECURITY_DESC": "Two-factor authentication adds an extra layer of security by requiring a verification code from your authenticator app in addition to your password.",
+ "SETUP": {
+ "STEP_NUMBER_1": "1",
+ "STEP_NUMBER_2": "2",
+ "STEP1_TITLE": "Scan QR Code with Your Authenticator App",
+ "STEP1_DESCRIPTION": "Use Google Authenticator, Authy, or any TOTP-compatible app",
+ "LOADING_QR": "Loading...",
+ "MANUAL_ENTRY": "Can't scan? Enter code manually",
+ "SECRET_KEY": "Secret Key",
+ "COPY": "Kopioi",
+ "ENTER_CODE": "Enter the 6-digit code from your authenticator app",
+ "ENTER_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify & Continue",
+ "CANCEL": "Peruuta",
+ "ERROR_STARTING": "MFA not enabled. Please contact administrator.",
+ "INVALID_CODE": "Invalid verification code",
+ "SECRET_COPIED": "Secret key copied to clipboard",
+ "SUCCESS": "Two-factor authentication has been enabled successfully"
+ },
+ "BACKUP": {
+ "TITLE": "Save Your Backup Codes",
+ "DESCRIPTION": "Keep these codes safe. Each can be used once if you lose access to your authenticator",
+ "IMPORTANT": "Important:",
+ "IMPORTANT_NOTE": " Save these codes in a secure location. You won't be able to see them again.",
+ "DOWNLOAD": "Lataa",
+ "COPY_ALL": "Copy All",
+ "CONFIRM": "I have saved my backup codes in a secure location and understand that I won't be able to see them again",
+ "COMPLETE_SETUP": "Complete Setup",
+ "CODES_COPIED": "Backup codes copied to clipboard"
+ },
+ "MANAGEMENT": {
+ "BACKUP_CODES": "Backup Codes",
+ "BACKUP_CODES_DESC": "Generate new codes if you've lost or used your existing ones",
+ "REGENERATE": "Regenerate Backup Codes",
+ "DISABLE_MFA": "Disable 2FA",
+ "DISABLE_MFA_DESC": "Remove two-factor authentication from your account",
+ "DISABLE_BUTTON": "Disable Two-Factor Authentication"
+ },
+ "DISABLE": {
+ "TITLE": "Disable Two-Factor Authentication",
+ "DESCRIPTION": "You'll need to enter your password and a verification code to disable two-factor authentication.",
+ "PASSWORD": "Salasana",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Disable 2FA",
+ "CANCEL": "Peruuta",
+ "SUCCESS": "Two-factor authentication has been disabled",
+ "ERROR": "Failed to disable MFA. Please check your credentials."
+ },
+ "REGENERATE": {
+ "TITLE": "Regenerate Backup Codes",
+ "DESCRIPTION": "This will invalidate your existing backup codes and generate new ones. Enter your verification code to continue.",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Generate New Codes",
+ "CANCEL": "Peruuta",
+ "NEW_CODES_TITLE": "New Backup Codes Generated",
+ "NEW_CODES_DESC": "Your old backup codes have been invalidated. Save these new codes in a secure location.",
+ "CODES_IMPORTANT": "Important:",
+ "CODES_IMPORTANT_NOTE": " Each code can only be used once. Save them before closing this window.",
+ "DOWNLOAD_CODES": "Download Codes",
+ "COPY_ALL_CODES": "Copy All Codes",
+ "CODES_SAVED": "I've Saved My Codes",
+ "SUCCESS": "New backup codes have been generated",
+ "ERROR": "Failed to regenerate backup codes"
+ }
+ },
+ "MFA_VERIFICATION": {
+ "TITLE": "Two-Factor Authentication",
+ "DESCRIPTION": "Enter your verification code to continue",
+ "AUTHENTICATOR_APP": "Authenticator App",
+ "BACKUP_CODE": "Backup Code",
+ "ENTER_OTP_CODE": "Enter 6-digit code from your authenticator app",
+ "ENTER_BACKUP_CODE": "Enter one of your backup codes",
+ "BACKUP_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify",
+ "TRY_ANOTHER_METHOD": "Try another verification method",
+ "CANCEL_LOGIN": "Cancel and return to login",
+ "HELP_TEXT": "Having trouble signing in?",
+ "LEARN_MORE": "Learn more about 2FA",
+ "HELP_MODAL": {
+ "TITLE": "Two-Factor Authentication Help",
+ "AUTHENTICATOR_TITLE": "Using an Authenticator App",
+ "AUTHENTICATOR_DESC": "Open your authenticator app (Google Authenticator, Authy, etc.) and enter the 6-digit code shown for your account.",
+ "BACKUP_TITLE": "Using a Backup Code",
+ "BACKUP_DESC": "If you don't have access to your authenticator app, you can use one of the backup codes you saved when setting up 2FA. Each code can only be used once.",
+ "CONTACT_TITLE": "Need More Help?",
+ "CONTACT_DESC_CLOUD": "If you've lost access to both your authenticator app and backup codes, please reach out to Chatwoot support for assistance.",
+ "CONTACT_DESC_SELF_HOSTED": "If you've lost access to both your authenticator app and backup codes, please contact your administrator for assistance."
+ },
+ "VERIFICATION_FAILED": "Verification failed. Please try again."
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/fi/settings.json b/app/javascript/dashboard/i18n/locale/fi/settings.json
index 99faa10bd..f5755c52a 100644
--- a/app/javascript/dashboard/i18n/locale/fi/settings.json
+++ b/app/javascript/dashboard/i18n/locale/fi/settings.json
@@ -51,6 +51,13 @@
"LARGER": "Larger",
"EXTRA_LARGE": "Extra Large"
}
+ },
+ "LANGUAGE": {
+ "TITLE": "Preferred Language",
+ "NOTE": "Choose the language you want to use.",
+ "UPDATE_SUCCESS": "Your Language settings have been updated successfully",
+ "UPDATE_ERROR": "There is an error while updating the language settings, please try again",
+ "USE_ACCOUNT_DEFAULT": "Use account default"
}
},
"MESSAGE_SIGNATURE_SECTION": {
@@ -73,6 +80,11 @@
"NOTE": "Salasanan vaihtaminen kirjaa sinut ulos muilta laitteilta.",
"BTN_TEXT": "Change password"
},
+ "SECURITY_SECTION": {
+ "TITLE": "Security",
+ "NOTE": "Manage additional security features for your account.",
+ "MFA_BUTTON": "Manage Two-Factor Authentication"
+ },
"ACCESS_TOKEN": {
"TITLE": "Access Token",
"NOTE": "Tätä tunnusta voidaan käyttää, jos olet rakentamassa API-pohjaista integraatiota",
@@ -330,6 +342,7 @@
"REPORTS_LABEL": "Tunnisteet",
"REPORTS_INBOX": "Inbox",
"REPORTS_TEAM": "Team",
+ "AGENT_ASSIGNMENT": "Agent Assignment",
"SET_AVAILABILITY_TITLE": "Set yourself as",
"SET_YOUR_AVAILABILITY": "Set your availability",
"SLA": "SLA",
@@ -350,7 +363,8 @@
"INFO_TEXT": "Let the system automatically mark you offline when you aren't using the app or dashboard.",
"INFO_SHORT": "Automatically mark offline when you aren't using the app."
},
- "DOCS": "Read docs"
+ "DOCS": "Read docs",
+ "SECURITY": "Security"
},
"BILLING_SETTINGS": {
"TITLE": "Billing",
@@ -382,6 +396,77 @@
},
"NO_BILLING_USER": "Your billing account is being configured. Please refresh the page and try again."
},
+ "SECURITY_SETTINGS": {
+ "TITLE": "Security",
+ "DESCRIPTION": "Manage your account security settings.",
+ "LINK_TEXT": "Learn more about SAML SSO",
+ "SAML": {
+ "TITLE": "SAML SSO",
+ "NOTE": "Configure SAML single sign-on for your account. Users will authenticate through your identity provider instead of using email/password.",
+ "ACS_URL": {
+ "LABEL": "ACS URL",
+ "TOOLTIP": "Assertion Consumer Service URL - Configure this URL in your IdP as the destination for SAML responses"
+ },
+ "SSO_URL": {
+ "LABEL": "SSO URL",
+ "HELP": "The URL where SAML authentication requests will be sent",
+ "PLACEHOLDER": "https://your-idp.com/saml/sso"
+ },
+ "CERTIFICATE": {
+ "LABEL": "Signing certificate in PEM format",
+ "HELP": "The public certificate from your identity provider used to verify SAML responses",
+ "PLACEHOLDER": "-----BEGIN CERTIFICATE-----\nMIIC..."
+ },
+ "FINGERPRINT": {
+ "LABEL": "Fingerprint",
+ "TOOLTIP": "SHA-1 fingerprint of the certificate - Use this to verify the certificate in your IdP configuration"
+ },
+ "COPY_SUCCESS": "Koodi kopioitu leikepöydälle onnistuneesti",
+ "SP_ENTITY_ID": {
+ "LABEL": "SP Entity ID",
+ "HELP": "Unique identifier for this application as a service provider (auto-generated).",
+ "TOOLTIP": "Unique identifier for Chatwoot as the Service Provider - Configure this in your IdP settings"
+ },
+ "IDP_ENTITY_ID": {
+ "LABEL": "Identity Provider Entity ID",
+ "HELP": "Unique identifier for your identity provider (usually found in IdP configuration)",
+ "PLACEHOLDER": "https://your-idp.com/saml"
+ },
+ "UPDATE_BUTTON": "Update SAML Settings",
+ "API": {
+ "SUCCESS": "SAML settings updated successfully",
+ "ERROR": "Failed to update SAML settings",
+ "ERROR_LOADING": "Failed to load SAML settings",
+ "DISABLED": "SAML settings disabled successfully"
+ },
+ "VALIDATION": {
+ "REQUIRED_FIELDS": "SSO URL, Identity Provider Entity ID, and Certificate are required fields",
+ "SSO_URL_ERROR": "Please enter a valid SSO URL",
+ "CERTIFICATE_ERROR": "Certificate is required",
+ "IDP_ENTITY_ID_ERROR": "Identity Provider Entity ID is required"
+ },
+ "ENTERPRISE_PAYWALL": {
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade to an Enterprise plan to access SAML single sign-on and other advanced security features.",
+ "ASK_ADMIN": "Please reach out to your administrator for the upgrade."
+ },
+ "PAYWALL": {
+ "TITLE": "Upgrade to enable SAML SSO",
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade your plan to get access to SAML single sign-on and other advanced features.",
+ "UPGRADE_NOW": "Upgrade now",
+ "CANCEL_ANYTIME": "You can change or cancel your plan anytime"
+ },
+ "ATTRIBUTE_MAPPING": {
+ "TITLE": "SAML Attribute Setup",
+ "DESCRIPTION": "The following attribute mappings must be configured in your identity provider"
+ },
+ "INFO_SECTION": {
+ "TITLE": "Service Provider Information",
+ "TOOLTIP": "Copy these values and configure them in your Identity Provider to establish the SAML connection"
+ }
+ }
+ },
"CREATE_ACCOUNT": {
"NO_ACCOUNT_WARNING": "Uh oh! We could not find any Chatwoot accounts. Please create a new account to continue.",
"NEW_ACCOUNT": "Uusi tili",
@@ -418,5 +503,255 @@
"SWITCH_TO_REPLY": "Switch to Reply",
"TOGGLE_SNOOZE_DROPDOWN": "Toggle snooze dropdown"
}
+ },
+ "ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent assignment",
+ "DESCRIPTION": "Define policies to effectively manage workload and route conversations based on the needs of inboxes and agents. Learn more here"
+ },
+ "ASSIGNMENT_POLICY": {
+ "TITLE": "Assignment policy",
+ "DESCRIPTION": "Manage how conversations get assigned in inboxes.",
+ "FEATURES": [
+ "Assign by conversations evenly or by available capacity",
+ "Add fair distribution rules to avoid overloading any agent",
+ "Add inboxes to a policy - one policy per inbox"
+ ]
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "TITLE": "Agent capacity policy",
+ "DESCRIPTION": "Manage workload for agents.",
+ "FEATURES": [
+ "Define maximum conversations per inbox",
+ "Create exceptions based on labels and time",
+ "Add agents to a policy - one policy per agent"
+ ]
+ }
+ },
+ "AGENT_ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Assignment policy",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "ORDER": "Order",
+ "PRIORITY": "Priority",
+ "ACTIVE": "Active",
+ "INACTIVE": "Inactive",
+ "POPOVER": "Added inboxes",
+ "EDIT": "Muokkaa"
+ },
+ "NO_RECORDS_FOUND": "No assignment policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create assignment policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy created successfully",
+ "ERROR_MESSAGE": "Failed to create assignment policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit assignment policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_INBOX_DIALOG": {
+ "TITLE": "Add inbox",
+ "DESCRIPTION": "{inboxName} inbox is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Peruuta"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update assignment policy"
+ },
+ "INBOX_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Inbox added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Inbox removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove inbox from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Kuvaus:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "STATUS": {
+ "LABEL": "Tila:",
+ "PLACEHOLDER": "Select status",
+ "ACTIVE": "Policy is active",
+ "INACTIVE": "Policy is inactive"
+ },
+ "ASSIGNMENT_ORDER": {
+ "LABEL": "Assignment order",
+ "ROUND_ROBIN": {
+ "LABEL": "Round robin",
+ "DESCRIPTION": "Assign conversations evenly among agents."
+ },
+ "BALANCED": {
+ "LABEL": "Balanced",
+ "DESCRIPTION": "Assign conversations based on available capacity."
+ }
+ },
+ "ASSIGNMENT_PRIORITY": {
+ "LABEL": "Assignment priority",
+ "EARLIEST_CREATED": {
+ "LABEL": "Earliest created",
+ "DESCRIPTION": "The conversation that was created first gets assigned first."
+ },
+ "LONGEST_WAITING": {
+ "LABEL": "Longest waiting",
+ "DESCRIPTION": "The conversation waiting the longest gets assigned first."
+ }
+ },
+ "FAIR_DISTRIBUTION": {
+ "LABEL": "Fair distribution policy",
+ "DESCRIPTION": "Set the maximum number of conversations that can be assigned per agent within a time window to avoid overloading any one agent. This required field defaults to 100 conversations per hour.",
+ "INPUT_MAX": "Assign max",
+ "DURATION": "Conversations per agent in every"
+ },
+ "INBOXES": {
+ "LABEL": "Added inboxes",
+ "DESCRIPTION": "Add inboxes for which this policy will be applicable.",
+ "ADD_BUTTON": "Add inbox",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select inboxes to add",
+ "ADD_BUTTON": "Add"
+ },
+ "EMPTY_STATE": "No inboxes added to this policy, add an inbox to get started",
+ "API": {
+ "SUCCESS_MESSAGE": "Inbox successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Assignment policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete assignment policy"
+ }
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent capacity",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "POPOVER": "Added agents",
+ "EDIT": "Muokkaa"
+ },
+ "NO_RECORDS_FOUND": "No agent capacity policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create agent capacity policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy created successfully",
+ "ERROR_MESSAGE": "Failed to create agent capacity policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit agent capacity policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_AGENT_DIALOG": {
+ "TITLE": "Add agent",
+ "DESCRIPTION": "{agentName} is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Peruuta"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update agent capacity policy"
+ },
+ "AGENT_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Agent added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Agent removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove agent from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Kuvaus:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "INBOX_CAPACITY_LIMIT": {
+ "LABEL": "Inbox capacity limits",
+ "ADD_BUTTON": "Add inbox",
+ "FIELD": {
+ "SELECT_INBOX": "Select inbox",
+ "MAX_CONVERSATIONS": "Max conversations",
+ "SET_LIMIT": "Set limit"
+ },
+ "EMPTY_STATE": "No inbox limit set"
+ },
+ "EXCLUSION_RULES": {
+ "LABEL": "Exclusion rules",
+ "DESCRIPTION": "Conversations that satisfy the following conditions would not count towards agent capacity",
+ "TAGS": {
+ "LABEL": "Exclude conversations tagged with specific labels",
+ "ADD_TAG": "add tag",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select tags to add"
+ },
+ "EMPTY_STATE": "No tags added to this policy."
+ },
+ "DURATION": {
+ "LABEL": "Exclude conversations older than a specified duration",
+ "PLACEHOLDER": "Set time"
+ }
+ },
+ "USERS": {
+ "LABEL": "Assigned agents",
+ "DESCRIPTION": "Add agents for which this policy will be applicable.",
+ "ADD_BUTTON": "Add agent",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select agents to add",
+ "ADD_BUTTON": "Add"
+ },
+ "EMPTY_STATE": "No agents added",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Agent capacity policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete agent capacity policy"
+ }
+ },
+ "DELETE_POLICY": {
+ "TITLE": "Delete policy",
+ "DESCRIPTION": "Are you sure you want to delete this policy? This action cannot be undone.",
+ "CONFIRM_BUTTON_LABEL": "Poista",
+ "CANCEL_BUTTON_LABEL": "Peruuta"
+ }
}
}
diff --git a/app/javascript/dashboard/i18n/locale/fi/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/fi/whatsappTemplates.json
index 337134e31..f84ba29ec 100644
--- a/app/javascript/dashboard/i18n/locale/fi/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/fi/whatsappTemplates.json
@@ -40,6 +40,7 @@
"BUTTON_LABEL": "Button {index}",
"COUPON_CODE": "Enter coupon code (max 15 chars)",
"MEDIA_URL_LABEL": "Enter {type} URL",
+ "DOCUMENT_NAME_PLACEHOLDER": "Enter document filename (e.g., Invoice_2025.pdf)",
"BUTTON_PARAMETER": "Enter button parameter"
}
}
diff --git a/app/javascript/dashboard/i18n/locale/fr/auditLogs.json b/app/javascript/dashboard/i18n/locale/fr/auditLogs.json
index fde634766..8a4cd8c93 100644
--- a/app/javascript/dashboard/i18n/locale/fr/auditLogs.json
+++ b/app/javascript/dashboard/i18n/locale/fr/auditLogs.json
@@ -71,7 +71,7 @@
"EDIT": "{agentName} updated the account configuration (#{id})"
},
"CONVERSATION": {
- "DELETE": "{agentName} deleted conversation #{id}"
+ "DELETE": "{agentName} a supprimé la conversation #{id}"
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/fr/automation.json b/app/javascript/dashboard/i18n/locale/fr/automation.json
index 11a1ddfe6..985f9f35a 100644
--- a/app/javascript/dashboard/i18n/locale/fr/automation.json
+++ b/app/javascript/dashboard/i18n/locale/fr/automation.json
@@ -131,7 +131,7 @@
"CONVERSATION_CREATED": "Conversation créée",
"CONVERSATION_UPDATED": "Conversation mise à jour",
"MESSAGE_CREATED": "Message créé",
- "CONVERSATION_RESOLVED": "Conversation Resolved",
+ "CONVERSATION_RESOLVED": "Conversation terminée",
"CONVERSATION_OPENED": "Conversation ouverte"
},
"ACTIONS": {
@@ -153,8 +153,8 @@
"OPEN_CONVERSATION": "Ouvrir la conversation"
},
"MESSAGE_TYPES": {
- "INCOMING": "Incoming Message",
- "OUTGOING": "Outgoing Message"
+ "INCOMING": "Boite de réception",
+ "OUTGOING": "Message envoyé"
},
"PRIORITY_TYPES": {
"NONE": "Aucun",
@@ -177,7 +177,8 @@
"REFERER_LINK": "Lien de référence",
"ASSIGNEE_NAME": "Assignee",
"TEAM_NAME": "Équipes",
- "PRIORITY": "Priorité"
+ "PRIORITY": "Priorité",
+ "LABELS": "Étiquettes"
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/fr/campaign.json b/app/javascript/dashboard/i18n/locale/fr/campaign.json
index 7b4da13fe..b2bbede32 100644
--- a/app/javascript/dashboard/i18n/locale/fr/campaign.json
+++ b/app/javascript/dashboard/i18n/locale/fr/campaign.json
@@ -138,11 +138,11 @@
}
},
"WHATSAPP": {
- "HEADER_TITLE": "WhatsApp campaigns",
+ "HEADER_TITLE": "Campagnes WhatsApp",
"NEW_CAMPAIGN": "Create campaign",
"EMPTY_STATE": {
- "TITLE": "No WhatsApp campaigns are available",
- "SUBTITLE": "Launch a WhatsApp campaign to reach your customers directly. Send offers or make announcements with ease. Click 'Create campaign' to get started."
+ "TITLE": "Aucune campagne WhatsApp n’est disponible",
+ "SUBTITLE": "Lancez une campagne WhatsApp pour toucher directement vos clients. Envoyez des offres ou faites des annonces en toute simplicité. Cliquez sur “Créer une campagne” pour commencer."
},
"CARD": {
"STATUS": {
@@ -155,7 +155,7 @@
}
},
"CREATE": {
- "TITLE": "Create WhatsApp campaign",
+ "TITLE": "Créer une campagne WhatsApp",
"CANCEL_BUTTON_TEXT": "Annuler",
"CREATE_BUTTON_TEXT": "Créer",
"FORM": {
@@ -170,15 +170,15 @@
"ERROR": "La boîte de réception est requise"
},
"TEMPLATE": {
- "LABEL": "WhatsApp Template",
- "PLACEHOLDER": "Select a template",
- "INFO": "Select a template to use for this campaign.",
- "ERROR": "Template is required",
+ "LABEL": "Modèle WhatsApp",
+ "PLACEHOLDER": "Sélectionner un modèle",
+ "INFO": "Sélectionnez un modèle pour cette campagne.",
+ "ERROR": "Un modèle est requis",
"PREVIEW_TITLE": "Traiter {templateName}",
"LANGUAGE": "Langue",
"CATEGORY": "Catégorie",
"VARIABLES_LABEL": "Variables",
- "VARIABLE_PLACEHOLDER": "Enter value for {variable}"
+ "VARIABLE_PLACEHOLDER": "Entrez une valeur pour {variable}"
},
"AUDIENCE": {
"LABEL": "Audience",
@@ -195,7 +195,7 @@
"CANCEL": "Annuler"
},
"API": {
- "SUCCESS_MESSAGE": "WhatsApp campaign created successfully",
+ "SUCCESS_MESSAGE": "Campagne WhatsApp créée avec succès",
"ERROR_MESSAGE": "Une erreur est survenue, veuillez réessayer."
}
}
diff --git a/app/javascript/dashboard/i18n/locale/fr/components.json b/app/javascript/dashboard/i18n/locale/fr/components.json
index 491f0d493..49535efce 100644
--- a/app/javascript/dashboard/i18n/locale/fr/components.json
+++ b/app/javascript/dashboard/i18n/locale/fr/components.json
@@ -51,6 +51,6 @@
"PLACEHOLDER": "Entrez la durée"
},
"CHANNEL_SELECTOR": {
- "COMING_SOON": "Coming Soon!"
+ "COMING_SOON": "Bientôt disponible !"
}
}
diff --git a/app/javascript/dashboard/i18n/locale/fr/contact.json b/app/javascript/dashboard/i18n/locale/fr/contact.json
index 882638b78..723b90371 100644
--- a/app/javascript/dashboard/i18n/locale/fr/contact.json
+++ b/app/javascript/dashboard/i18n/locale/fr/contact.json
@@ -17,10 +17,10 @@
"IP_ADDRESS": "Adresse IP",
"CREATED_AT_LABEL": "Créé",
"NEW_MESSAGE": "Nouveau message",
- "CALL": "Call",
- "CALL_UNDER_DEVELOPMENT": "Calling is under development",
+ "CALL": "Appel",
+ "CALL_UNDER_DEVELOPMENT": "Appel en cours de développement",
"VOICE_INBOX_PICKER": {
- "TITLE": "Choose a voice inbox"
+ "TITLE": "Choisir une boîte de réception vocale"
},
"CONVERSATIONS": {
"NO_RECORDS_FOUND": "Il n'y a aucune conversation précédente associée à ce contact.",
@@ -290,7 +290,7 @@
"HEADER": {
"TITLE": "Contacts",
"SEARCH_TITLE": "Search contacts",
- "ACTIVE_TITLE": "Active contacts",
+ "ACTIVE_TITLE": "Contacts actifs",
"SEARCH_PLACEHOLDER": "Search...",
"MESSAGE_BUTTON": "Message",
"SEND_MESSAGE": "Envoyer un message",
@@ -465,8 +465,8 @@
}
},
"DELETE_CONTACT": {
- "MESSAGE": "This action is permanent and irreversible.",
- "BUTTON": "Delete now"
+ "MESSAGE": "Cette action est permanente et irréversible.",
+ "BUTTON": "Supprimer maintenant"
}
},
"DETAILS": {
@@ -476,7 +476,7 @@
"DELETE_CONTACT": "Supprimer le contact",
"DELETE_DIALOG": {
"TITLE": "Confirmer la suppression",
- "DESCRIPTION": "Are you sure you want to delete this contact?",
+ "DESCRIPTION": "Êtes-vous sûr de vouloir supprimer ce contact ?",
"CONFIRM": "Oui, supprimer",
"API": {
"SUCCESS_MESSAGE": "Contact supprimé avec succès",
@@ -566,7 +566,7 @@
"BUTTON_LABEL": "Add contact",
"SEARCH_EMPTY_STATE_TITLE": "Aucun contact ne correspond à votre recherche 🔍",
"LIST_EMPTY_STATE_TITLE": "No contacts available in this view 📋",
- "ACTIVE_EMPTY_STATE_TITLE": "No contacts are active at the moment 🌙"
+ "ACTIVE_EMPTY_STATE_TITLE": "Aucun contact n'est actif pour le moment 🌙"
}
},
"COMPOSE_NEW_CONVERSATION": {
@@ -610,6 +610,15 @@
"SEND_MESSAGE": "Envoyer un message"
}
},
+ "TWILIO_OPTIONS": {
+ "LABEL": "Select template",
+ "SEARCH_PLACEHOLDER": "Search templates",
+ "EMPTY_STATE": "No templates found",
+ "TEMPLATE_PARSER": {
+ "BACK": "Retour",
+ "SEND_MESSAGE": "Envoyer un message"
+ }
+ },
"ACTION_BUTTONS": {
"DISCARD": "Discard",
"SEND": "Send ({keyCode})"
diff --git a/app/javascript/dashboard/i18n/locale/fr/contactFilters.json b/app/javascript/dashboard/i18n/locale/fr/contactFilters.json
index d5789e05c..414ea9414 100644
--- a/app/javascript/dashboard/i18n/locale/fr/contactFilters.json
+++ b/app/javascript/dashboard/i18n/locale/fr/contactFilters.json
@@ -48,7 +48,8 @@
"CREATED_AT": "Créé le",
"LAST_ACTIVITY": "Dernière activité",
"REFERER_LINK": "Lien de référence",
- "BLOCKED": "Blocked"
+ "BLOCKED": "Blocked",
+ "LABELS": "Étiquettes"
},
"GROUPS": {
"STANDARD_FILTERS": "Filtres standards",
diff --git a/app/javascript/dashboard/i18n/locale/fr/contentTemplates.json b/app/javascript/dashboard/i18n/locale/fr/contentTemplates.json
new file mode 100644
index 000000000..acd139f27
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/fr/contentTemplates.json
@@ -0,0 +1,51 @@
+{
+ "CONTENT_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Modèles Twilio",
+ "SUBTITLE": "Sélectionnez le modèle Twilio que vous souhaitez envoyer",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configurer le modèle : {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Rechercher des modèles",
+ "NO_TEMPLATES_FOUND": "Aucun modèle trouvé pour",
+ "NO_CONTENT": "No content",
+ "HEADER": "En-tête",
+ "BODY": "Corps",
+ "FOOTER": "Pied de page",
+ "BUTTONS": "Boutons",
+ "CATEGORY": "Catégorie",
+ "MEDIA_CONTENT": "Contenu média",
+ "MEDIA_CONTENT_FALLBACK": "contenu multimédia",
+ "NO_TEMPLATES_AVAILABLE": "Aucun modèle Twilio disponible. Cliquez sur Actualiser pour synchroniser les modèles de Twilio.",
+ "REFRESH_BUTTON": "Rafraîchir les modèles",
+ "REFRESH_SUCCESS": "Mise à jour des modèles. La mise à jour peut prendre quelques minutes.",
+ "REFRESH_ERROR": "Échec de la mise à jour des modèles. Veuillez réessayer.",
+ "LABELS": {
+ "LANGUAGE": "Langue",
+ "TEMPLATE_BODY": "Corps du modèle",
+ "CATEGORY": "Catégorie"
+ },
+ "TYPES": {
+ "MEDIA": "Média",
+ "QUICK_REPLY": "Réponse rapide",
+ "TEXT": "Texte"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variables",
+ "LANGUAGE": "Langue",
+ "CATEGORY": "Catégorie",
+ "VARIABLE_PLACEHOLDER": "Entrez la valeur de {variable}",
+ "GO_BACK_LABEL": "Retour",
+ "SEND_MESSAGE_LABEL": "Envoyer un message",
+ "FORM_ERROR_MESSAGE": "Veuillez remplir toutes les variables avant d'envoyer",
+ "MEDIA_HEADER_LABEL": "En-tête {type}",
+ "MEDIA_URL_LABEL": "Saisissez l'URL complète du média",
+ "MEDIA_URL_PLACEHOLDER": "https://example.com/image.jpg"
+ },
+ "FORM": {
+ "BACK_BUTTON": "Précédent",
+ "SEND_MESSAGE_BUTTON": "Envoyer un message"
+ }
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/fr/conversation.json b/app/javascript/dashboard/i18n/locale/fr/conversation.json
index 544e68e4e..101a5f7f7 100644
--- a/app/javascript/dashboard/i18n/locale/fr/conversation.json
+++ b/app/javascript/dashboard/i18n/locale/fr/conversation.json
@@ -35,6 +35,11 @@
"API_HOURS_WINDOW": "Vous ne pouvez répondre à cette conversation que dans un délai de {hours} heures",
"NOT_ASSIGNED_TO_YOU": "Cette conversation ne vous est pas assignée. Voulez-vous vous assigner cette conversation ?",
"ASSIGN_TO_ME": "M’assigner la conversation",
+ "BOT_HANDOFF_MESSAGE": "Vous répondez à une conversation actuellement gérée par un assistant ou un bot.",
+ "BOT_HANDOFF_ACTION": "Ouvrir et m’attribuer",
+ "BOT_HANDOFF_REOPEN_ACTION": "Marquer la conversation comme ouverte",
+ "BOT_HANDOFF_SUCCESS": "La conversation vous a été attribuée",
+ "BOT_HANDOFF_ERROR": "Impossible de reprendre la conversation. Veuillez réessayer.",
"TWILIO_WHATSAPP_CAN_REPLY": "Vous pouvez seulement répondre à cette conversation en utilisant un modèle de message en raison de",
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "Restriction de fenêtre de message de 24 heures",
"OLD_INSTAGRAM_INBOX_REPLY_BANNER": "Ce compte Instagram a été migré vers la nouvelle boîte de réception du canal Instagram. Tous les nouveaux messages y apparaîtront. Vous ne pourrez plus envoyer de messages depuis cette conversation.",
@@ -66,11 +71,22 @@
"SHOW_LABELS": "Afficher les étiquettes",
"HIDE_LABELS": "Masquer les étiquettes"
},
+ "VOICE_CALL": {
+ "INCOMING_CALL": "Appel entrant",
+ "OUTGOING_CALL": "Appel sortant",
+ "CALL_IN_PROGRESS": "Appel en cours",
+ "NO_ANSWER": "Pas de réponse",
+ "MISSED_CALL": "Appel manqué",
+ "CALL_ENDED": "Appel terminé",
+ "NOT_ANSWERED_YET": "Pas encore répondu",
+ "THEY_ANSWERED": "Il a répondu",
+ "YOU_ANSWERED": "Vous avez répondu"
+ },
"HEADER": {
"RESOLVE_ACTION": "Résoudre",
"REOPEN_ACTION": "Ré-ouvrir",
"OPEN_ACTION": "Ouvert",
- "MORE_ACTIONS": "More actions",
+ "MORE_ACTIONS": "Plus d'actions",
"OPEN": "Plus",
"CLOSE": "Fermer",
"DETAILS": "détails",
@@ -123,8 +139,8 @@
}
},
"DELETE_CONVERSATION": {
- "TITLE": "Delete conversation #{conversationId}",
- "DESCRIPTION": "Are you sure you want to delete this conversation?",
+ "TITLE": "Supprimer la conversation #{conversationId}",
+ "DESCRIPTION": "Êtes-vous sûr de vouloir supprimer cette conversation ?",
"CONFIRM": "Supprimer"
},
"CARD_CONTEXT_MENU": {
@@ -143,10 +159,10 @@
"ASSIGN_LABEL": "Assigner une étiquette",
"AGENTS_LOADING": "Chargement des agents...",
"ASSIGN_TEAM": "Assigner une équipe",
- "DELETE": "Delete conversation",
- "OPEN_IN_NEW_TAB": "Open in new tab",
- "COPY_LINK": "Copy conversation link",
- "COPY_LINK_SUCCESS": "Conversation link copied to clipboard",
+ "DELETE": "Supprimer la conversation",
+ "OPEN_IN_NEW_TAB": "Ouvrir dans un nouvel onglet",
+ "COPY_LINK": "Copier le lien de la conversation",
+ "COPY_LINK_SUCCESS": "Le lien de conversation a été copié dans le presse-papiers",
"API": {
"AGENT_ASSIGNMENT": {
"SUCCESFUL": "Conversation id {conversationId} assignée à \"{agentName}\"",
@@ -221,8 +237,8 @@
"ASSIGN_LABEL_SUCCESFUL": "Étiquette attribuée avec succès",
"ASSIGN_LABEL_FAILED": "Échec de l'attribution de l'étiquette",
"CHANGE_TEAM": "L'équipe de conversation a été modifiée",
- "SUCCESS_DELETE_CONVERSATION": "Conversation deleted successfully",
- "FAIL_DELETE_CONVERSATION": "Couldn't delete conversation! Try again",
+ "SUCCESS_DELETE_CONVERSATION": "Conversation supprimée avec succès",
+ "FAIL_DELETE_CONVERSATION": "Impossible de supprimer la conversation ! Veuillez réessayer",
"FILE_SIZE_LIMIT": "Le fichier dépasse la limite de {MAXIMUM_SUPPORTED_FILE_UPLOAD_SIZE} pour les pièces jointes",
"MESSAGE_ERROR": "Impossible d'envoyer ce message, veuillez réessayer plus tard",
"SENT_BY": "Envoyé par:",
diff --git a/app/javascript/dashboard/i18n/locale/fr/helpCenter.json b/app/javascript/dashboard/i18n/locale/fr/helpCenter.json
index fb49a6786..0b41f6da7 100644
--- a/app/javascript/dashboard/i18n/locale/fr/helpCenter.json
+++ b/app/javascript/dashboard/i18n/locale/fr/helpCenter.json
@@ -741,7 +741,8 @@
"LIVE_CHAT_WIDGET": {
"LABEL": "Live chat widget",
"PLACEHOLDER": "Select live chat widget",
- "HELP_TEXT": "Select a live chat widget that will appear on your help center"
+ "HELP_TEXT": "Select a live chat widget that will appear on your help center",
+ "NONE_OPTION": "No widget"
},
"BRAND_COLOR": {
"LABEL": "Brand color"
@@ -807,6 +808,58 @@
"ERROR_MESSAGE": "Unable to update portal"
}
}
+ },
+ "PDF_UPLOAD": {
+ "TITLE": "Upload PDF Document",
+ "DESCRIPTION": "Upload a PDF document to automatically generate FAQs using AI",
+ "DRAG_DROP_TEXT": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "ADDITIONAL_CONTEXT_LABEL": "Additional Context (Optional)",
+ "ADDITIONAL_CONTEXT_PLACEHOLDER": "Provide any additional context or instructions for FAQ generation...",
+ "UPLOADING": "Téléversement...",
+ "UPLOAD": "Upload & Process",
+ "CANCEL": "Annuler",
+ "ERROR_INVALID_TYPE": "Please select a valid PDF file",
+ "ERROR_FILE_TOO_LARGE": "File size must be less than 512MB",
+ "ERROR_UPLOAD_FAILED": "Failed to upload PDF. Please try again."
+ },
+ "PDF_DOCUMENTS": {
+ "TITLE": "PDF Documents",
+ "DESCRIPTION": "Manage uploaded PDF documents and generate FAQs from them",
+ "UPLOAD_PDF": "Upload PDF",
+ "UPLOAD_FIRST_PDF": "Upload your first PDF",
+ "UPLOADED_BY": "Uploaded by",
+ "GENERATE_FAQS": "Generate FAQs",
+ "GENERATING": "Génération en cours...",
+ "CONFIRM_DELETE": "Êtes-vous sûr de vouloir supprimer {filename} ?",
+ "EMPTY_STATE": {
+ "TITLE": "No PDF documents yet",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQs using AI"
+ },
+ "STATUS": {
+ "UPLOADED": "Ready",
+ "PROCESSING": "Processing",
+ "PROCESSED": "Terminé",
+ "FAILED": "Failed"
+ }
+ },
+ "CONTENT_GENERATION": {
+ "TITLE": "Content Generation",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQ content using AI",
+ "UPLOAD_TITLE": "Upload PDF Document",
+ "DRAG_DROP": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "UPLOADING": "Processing document...",
+ "UPLOAD_SUCCESS": "Document processed successfully!",
+ "UPLOAD_ERROR": "Failed to upload document. Please try again.",
+ "INVALID_FILE_TYPE": "Please select a valid PDF file",
+ "FILE_TOO_LARGE": "File size must be less than 512MB",
+ "GENERATED_CONTENT": "Generated FAQ Content",
+ "PUBLISH_SELECTED": "Publish Selected",
+ "PUBLISHING": "Publishing...",
+ "FROM_DOCUMENT": "From document",
+ "NO_CONTENT": "No generated content available. Upload a PDF document to get started.",
+ "LOADING": "Loading generated content..."
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/fr/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/fr/inboxMgmt.json
index ed8c885d6..8c2b30c7d 100644
--- a/app/javascript/dashboard/i18n/locale/fr/inboxMgmt.json
+++ b/app/javascript/dashboard/i18n/locale/fr/inboxMgmt.json
@@ -272,8 +272,8 @@
},
"SUBMIT_BUTTON": "Créer le canal WhatsApp",
"EMBEDDED_SIGNUP": {
- "TITLE": "Quick Setup with Meta",
- "DESC": "You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
+ "TITLE": "Quick setup with Meta",
+ "DESC": "Use the WhatsApp Embedded Signup flow to quickly connect new numbers. You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
"BENEFITS": {
"TITLE": "Benefits of Embedded Signup:",
"EASY_SETUP": "No manual configuration required",
@@ -281,9 +281,8 @@
"AUTO_CONFIG": "Automatic webhook and phone number configuration"
},
"LEARN_MORE": {
- "TEXT": "To learn more about integrated signup, pricing, and limitations, visit",
- "LINK_TEXT": "this link.",
- "LINK_URL": "https://developers.facebook.com/docs/whatsapp/embedded-signup/custom-flows/onboarding-business-app-users#limitations"
+ "TEXT": "To learn more about integrated signup, pricing, and limitations, visit {link}.",
+ "LINK_TEXT": "this link"
},
"SUBMIT_BUTTON": "Connect with WhatsApp Business",
"AUTH_PROCESSING": "Authenticating with Meta",
@@ -296,7 +295,9 @@
"INVALID_BUSINESS_DATA": "Invalid business data received from Facebook. Please try again.",
"SIGNUP_ERROR": "Signup error occurred",
"AUTH_NOT_COMPLETED": "Authentication not completed. Please restart the process.",
- "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured"
+ "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured",
+ "MANUAL_FALLBACK": "If your number is already connected to the WhatsApp Business Platform (API), or if you’re a tech provider onboarding your own number, please use the {link} flow",
+ "MANUAL_LINK_TEXT": "manual setup flow"
},
"API": {
"ERROR_MESSAGE": "Nous n'avons pas pu enregistrer le canal WhatsApp"
@@ -424,7 +425,51 @@
},
"AUTH": {
"TITLE": "Choisir un canal",
- "DESC": "Chatwoot supports live-chat widgets, Facebook Messenger, Twitter profiles, WhatsApp, Emails, etc., as channels. If you want to build a custom channel, you can create it using the API channel. To get started, choose one of the channels below."
+ "DESC": "Chatwoot supports live-chat widgets, Facebook Messenger, Twitter profiles, WhatsApp, Emails, etc., as channels. If you want to build a custom channel, you can create it using the API channel. To get started, choose one of the channels below.",
+ "TITLE_NEXT": "Complete the setup",
+ "TITLE_FINISH": "Voilà!",
+ "CHANNEL": {
+ "WEBSITE": {
+ "TITLE": "Site internet",
+ "DESCRIPTION": "Create a live-chat widget"
+ },
+ "FACEBOOK": {
+ "TITLE": "Facebook",
+ "DESCRIPTION": "Connect your Facebook page"
+ },
+ "WHATSAPP": {
+ "TITLE": "WhatsApp",
+ "DESCRIPTION": "Support your customers on WhatsApp"
+ },
+ "EMAIL": {
+ "TITLE": "Courriel",
+ "DESCRIPTION": "Connect with Gmail, Outlook, or other providers"
+ },
+ "SMS": {
+ "TITLE": "SMS",
+ "DESCRIPTION": "Integrate SMS channel with Twilio or bandwidth"
+ },
+ "API": {
+ "TITLE": "API",
+ "DESCRIPTION": "Make a custom channel using our API"
+ },
+ "TELEGRAM": {
+ "TITLE": "Telegram",
+ "DESCRIPTION": "Configure Telegram channel using Bot token"
+ },
+ "LINE": {
+ "TITLE": "Line",
+ "DESCRIPTION": "Integrate your Line channel"
+ },
+ "INSTAGRAM": {
+ "TITLE": "Instagram",
+ "DESCRIPTION": "Connect your instagram account"
+ },
+ "VOICE": {
+ "TITLE": "Voice",
+ "DESCRIPTION": "Integrate with Twilio Voice"
+ }
+ }
},
"AGENTS": {
"TITLE": "Agents",
@@ -478,7 +523,10 @@
"MESSAGE": "Vous pouvez maintenant vous impliquer auprès de vos clients par le biais de votre nouveau canal. Bonne assistance",
"BUTTON_TEXT": "Emmenez-moi là",
"MORE_SETTINGS": "Plus de paramètres",
- "WEBSITE_SUCCESS": "Vous avez terminé avec succès la création d'un canal Web. Copiez le code affiché ci-dessous et collez-le sur votre site web. La prochaine fois qu'un client utilisera le chat en direct, la conversation apparaîtra automatiquement dans votre boîte de réception."
+ "WEBSITE_SUCCESS": "Vous avez terminé avec succès la création d'un canal Web. Copiez le code affiché ci-dessous et collez-le sur votre site web. La prochaine fois qu'un client utilisera le chat en direct, la conversation apparaîtra automatiquement dans votre boîte de réception.",
+ "WHATSAPP_QR_INSTRUCTION": "Scan the QR code above to quickly test your WhatsApp inbox",
+ "MESSENGER_QR_INSTRUCTION": "Scan the QR code above to quickly test your Facebook Messenger inbox",
+ "TELEGRAM_QR_INSTRUCTION": "Scan the QR code above to quickly test your Telegram inbox"
},
"REAUTH": "Réautoriser",
"VIEW": "Voir",
@@ -868,9 +916,18 @@
"SCRIPT_SETTINGS": "\n window.chatwootSettings = {options};"
},
"EMAIL_PROVIDERS": {
- "MICROSOFT": "Microsoft",
- "GOOGLE": "Google",
- "OTHER_PROVIDERS": "Autres fournisseurs"
+ "MICROSOFT": {
+ "TITLE": "Microsoft",
+ "DESCRIPTION": "Connect with Microsoft"
+ },
+ "GOOGLE": {
+ "TITLE": "Google",
+ "DESCRIPTION": "Connect with Google"
+ },
+ "OTHER_PROVIDERS": {
+ "TITLE": "Autres fournisseurs",
+ "DESCRIPTION": "Connect with Other Providers"
+ }
},
"CHANNELS": {
"MESSENGER": "Messenger",
diff --git a/app/javascript/dashboard/i18n/locale/fr/integrations.json b/app/javascript/dashboard/i18n/locale/fr/integrations.json
index 9ad0fb8fd..379f91eb9 100644
--- a/app/javascript/dashboard/i18n/locale/fr/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/fr/integrations.json
@@ -704,11 +704,28 @@
"ERROR_MESSAGE": "There was an error creating the document, please try again."
},
"FORM": {
+ "TYPE": {
+ "LABEL": "Document Type",
+ "URL": "URL",
+ "PDF": "PDF File"
+ },
"URL": {
"LABEL": "URL",
"PLACEHOLDER": "Enter the URL of the document",
"ERROR": "Please provide a valid URL for the document"
},
+ "PDF_FILE": {
+ "LABEL": "PDF File",
+ "CHOOSE_FILE": "Choose PDF file",
+ "ERROR": "Please select a PDF file",
+ "HELP_TEXT": "Maximum file size: 10MB",
+ "INVALID_TYPE": "Please select a valid PDF file",
+ "TOO_LARGE": "File size exceeds 10MB limit"
+ },
+ "NAME": {
+ "LABEL": "Document Name (Optional)",
+ "PLACEHOLDER": "Enter a name for the document"
+ },
"ASSISTANT": {
"LABEL": "Assistant",
"PLACEHOLDER": "Select the assistant",
@@ -744,6 +761,7 @@
"SELECTED": "{count} selected",
"SELECT_ALL": "Select all ({count})",
"UNSELECT_ALL": "Unselect all ({count})",
+ "SEARCH_PLACEHOLDER": "Search FAQs...",
"BULK_APPROVE_BUTTON": "Approve",
"BULK_DELETE_BUTTON": "Supprimer",
"BULK_APPROVE": {
diff --git a/app/javascript/dashboard/i18n/locale/fr/mfa.json b/app/javascript/dashboard/i18n/locale/fr/mfa.json
new file mode 100644
index 000000000..e540427ad
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/fr/mfa.json
@@ -0,0 +1,106 @@
+{
+ "MFA_SETTINGS": {
+ "TITLE": "Two-Factor Authentication",
+ "SUBTITLE": "Secure your account with TOTP-based authentication",
+ "DESCRIPTION": "Add an extra layer of security to your account using a time-based one-time password (TOTP)",
+ "STATUS_TITLE": "Authentication Status",
+ "STATUS_DESCRIPTION": "Manage your two-factor authentication settings and backup recovery codes",
+ "ENABLED": "Activé",
+ "DISABLED": "Désactivé",
+ "STATUS_ENABLED": "Two-factor authentication is active",
+ "STATUS_ENABLED_DESC": "Your account is protected with an additional layer of security",
+ "ENABLE_BUTTON": "Enable Two-Factor Authentication",
+ "ENHANCE_SECURITY": "Enhance Your Account Security",
+ "ENHANCE_SECURITY_DESC": "Two-factor authentication adds an extra layer of security by requiring a verification code from your authenticator app in addition to your password.",
+ "SETUP": {
+ "STEP_NUMBER_1": "1",
+ "STEP_NUMBER_2": "2",
+ "STEP1_TITLE": "Scan QR Code with Your Authenticator App",
+ "STEP1_DESCRIPTION": "Use Google Authenticator, Authy, or any TOTP-compatible app",
+ "LOADING_QR": "Loading...",
+ "MANUAL_ENTRY": "Can't scan? Enter code manually",
+ "SECRET_KEY": "Secret Key",
+ "COPY": "Copier",
+ "ENTER_CODE": "Enter the 6-digit code from your authenticator app",
+ "ENTER_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify & Continue",
+ "CANCEL": "Annuler",
+ "ERROR_STARTING": "MFA not enabled. Please contact administrator.",
+ "INVALID_CODE": "Invalid verification code",
+ "SECRET_COPIED": "Secret key copied to clipboard",
+ "SUCCESS": "Two-factor authentication has been enabled successfully"
+ },
+ "BACKUP": {
+ "TITLE": "Save Your Backup Codes",
+ "DESCRIPTION": "Keep these codes safe. Each can be used once if you lose access to your authenticator",
+ "IMPORTANT": "Important:",
+ "IMPORTANT_NOTE": " Save these codes in a secure location. You won't be able to see them again.",
+ "DOWNLOAD": "Télécharger",
+ "COPY_ALL": "Copy All",
+ "CONFIRM": "I have saved my backup codes in a secure location and understand that I won't be able to see them again",
+ "COMPLETE_SETUP": "Complete Setup",
+ "CODES_COPIED": "Backup codes copied to clipboard"
+ },
+ "MANAGEMENT": {
+ "BACKUP_CODES": "Backup Codes",
+ "BACKUP_CODES_DESC": "Generate new codes if you've lost or used your existing ones",
+ "REGENERATE": "Regenerate Backup Codes",
+ "DISABLE_MFA": "Disable 2FA",
+ "DISABLE_MFA_DESC": "Remove two-factor authentication from your account",
+ "DISABLE_BUTTON": "Disable Two-Factor Authentication"
+ },
+ "DISABLE": {
+ "TITLE": "Disable Two-Factor Authentication",
+ "DESCRIPTION": "You'll need to enter your password and a verification code to disable two-factor authentication.",
+ "PASSWORD": "Mot de passe",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Disable 2FA",
+ "CANCEL": "Annuler",
+ "SUCCESS": "Two-factor authentication has been disabled",
+ "ERROR": "Failed to disable MFA. Please check your credentials."
+ },
+ "REGENERATE": {
+ "TITLE": "Regenerate Backup Codes",
+ "DESCRIPTION": "This will invalidate your existing backup codes and generate new ones. Enter your verification code to continue.",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Generate New Codes",
+ "CANCEL": "Annuler",
+ "NEW_CODES_TITLE": "New Backup Codes Generated",
+ "NEW_CODES_DESC": "Your old backup codes have been invalidated. Save these new codes in a secure location.",
+ "CODES_IMPORTANT": "Important:",
+ "CODES_IMPORTANT_NOTE": " Each code can only be used once. Save them before closing this window.",
+ "DOWNLOAD_CODES": "Download Codes",
+ "COPY_ALL_CODES": "Copy All Codes",
+ "CODES_SAVED": "I've Saved My Codes",
+ "SUCCESS": "New backup codes have been generated",
+ "ERROR": "Failed to regenerate backup codes"
+ }
+ },
+ "MFA_VERIFICATION": {
+ "TITLE": "Two-Factor Authentication",
+ "DESCRIPTION": "Enter your verification code to continue",
+ "AUTHENTICATOR_APP": "Authenticator App",
+ "BACKUP_CODE": "Backup Code",
+ "ENTER_OTP_CODE": "Enter 6-digit code from your authenticator app",
+ "ENTER_BACKUP_CODE": "Enter one of your backup codes",
+ "BACKUP_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify",
+ "TRY_ANOTHER_METHOD": "Try another verification method",
+ "CANCEL_LOGIN": "Cancel and return to login",
+ "HELP_TEXT": "Having trouble signing in?",
+ "LEARN_MORE": "Learn more about 2FA",
+ "HELP_MODAL": {
+ "TITLE": "Two-Factor Authentication Help",
+ "AUTHENTICATOR_TITLE": "Using an Authenticator App",
+ "AUTHENTICATOR_DESC": "Open your authenticator app (Google Authenticator, Authy, etc.) and enter the 6-digit code shown for your account.",
+ "BACKUP_TITLE": "Using a Backup Code",
+ "BACKUP_DESC": "If you don't have access to your authenticator app, you can use one of the backup codes you saved when setting up 2FA. Each code can only be used once.",
+ "CONTACT_TITLE": "Need More Help?",
+ "CONTACT_DESC_CLOUD": "If you've lost access to both your authenticator app and backup codes, please reach out to Chatwoot support for assistance.",
+ "CONTACT_DESC_SELF_HOSTED": "If you've lost access to both your authenticator app and backup codes, please contact your administrator for assistance."
+ },
+ "VERIFICATION_FAILED": "Verification failed. Please try again."
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/fr/settings.json b/app/javascript/dashboard/i18n/locale/fr/settings.json
index 96ac90636..a951d668b 100644
--- a/app/javascript/dashboard/i18n/locale/fr/settings.json
+++ b/app/javascript/dashboard/i18n/locale/fr/settings.json
@@ -51,6 +51,13 @@
"LARGER": "Larger",
"EXTRA_LARGE": "Extra Large"
}
+ },
+ "LANGUAGE": {
+ "TITLE": "Preferred Language",
+ "NOTE": "Choose the language you want to use.",
+ "UPDATE_SUCCESS": "Your Language settings have been updated successfully",
+ "UPDATE_ERROR": "There is an error while updating the language settings, please try again",
+ "USE_ACCOUNT_DEFAULT": "Use account default"
}
},
"MESSAGE_SIGNATURE_SECTION": {
@@ -73,6 +80,11 @@
"NOTE": "Mettre à jour votre mot de passe réinitialisera vos connexions sur plusieurs appareils.",
"BTN_TEXT": "Modifier le mot de passe"
},
+ "SECURITY_SECTION": {
+ "TITLE": "Security",
+ "NOTE": "Manage additional security features for your account.",
+ "MFA_BUTTON": "Manage Two-Factor Authentication"
+ },
"ACCESS_TOKEN": {
"TITLE": "Jeton d'accès",
"NOTE": "Ce jeton peut être utilisé si vous construisez une intégration basée sur l'API",
@@ -330,6 +342,7 @@
"REPORTS_LABEL": "Étiquettes",
"REPORTS_INBOX": "Boîte de réception",
"REPORTS_TEAM": "Équipes",
+ "AGENT_ASSIGNMENT": "Agent Assignment",
"SET_AVAILABILITY_TITLE": "Se définir comme",
"SET_YOUR_AVAILABILITY": "Définissez votre disponibilité",
"SLA": "SLA",
@@ -350,7 +363,8 @@
"INFO_TEXT": "Laissez le système vous passer automatiquement hors ligne lorsque vous n'utilisez pas l'application ou le tableau de bord.",
"INFO_SHORT": "Automatically mark offline when you aren't using the app."
},
- "DOCS": "Lire la documentation"
+ "DOCS": "Lire la documentation",
+ "SECURITY": "Security"
},
"BILLING_SETTINGS": {
"TITLE": "Facturation",
@@ -382,6 +396,77 @@
},
"NO_BILLING_USER": "Votre compte de facturation est en cours de configuration. Veuillez actualiser la page et réessayer."
},
+ "SECURITY_SETTINGS": {
+ "TITLE": "Security",
+ "DESCRIPTION": "Manage your account security settings.",
+ "LINK_TEXT": "Learn more about SAML SSO",
+ "SAML": {
+ "TITLE": "SAML SSO",
+ "NOTE": "Configure SAML single sign-on for your account. Users will authenticate through your identity provider instead of using email/password.",
+ "ACS_URL": {
+ "LABEL": "ACS URL",
+ "TOOLTIP": "Assertion Consumer Service URL - Configure this URL in your IdP as the destination for SAML responses"
+ },
+ "SSO_URL": {
+ "LABEL": "SSO URL",
+ "HELP": "The URL where SAML authentication requests will be sent",
+ "PLACEHOLDER": "https://your-idp.com/saml/sso"
+ },
+ "CERTIFICATE": {
+ "LABEL": "Signing certificate in PEM format",
+ "HELP": "The public certificate from your identity provider used to verify SAML responses",
+ "PLACEHOLDER": "-----BEGIN CERTIFICATE-----\nMIIC..."
+ },
+ "FINGERPRINT": {
+ "LABEL": "Fingerprint",
+ "TOOLTIP": "SHA-1 fingerprint of the certificate - Use this to verify the certificate in your IdP configuration"
+ },
+ "COPY_SUCCESS": "Code copié dans le presse-papier avec succès",
+ "SP_ENTITY_ID": {
+ "LABEL": "SP Entity ID",
+ "HELP": "Unique identifier for this application as a service provider (auto-generated).",
+ "TOOLTIP": "Unique identifier for Chatwoot as the Service Provider - Configure this in your IdP settings"
+ },
+ "IDP_ENTITY_ID": {
+ "LABEL": "Identity Provider Entity ID",
+ "HELP": "Unique identifier for your identity provider (usually found in IdP configuration)",
+ "PLACEHOLDER": "https://your-idp.com/saml"
+ },
+ "UPDATE_BUTTON": "Update SAML Settings",
+ "API": {
+ "SUCCESS": "SAML settings updated successfully",
+ "ERROR": "Failed to update SAML settings",
+ "ERROR_LOADING": "Failed to load SAML settings",
+ "DISABLED": "SAML settings disabled successfully"
+ },
+ "VALIDATION": {
+ "REQUIRED_FIELDS": "SSO URL, Identity Provider Entity ID, and Certificate are required fields",
+ "SSO_URL_ERROR": "Please enter a valid SSO URL",
+ "CERTIFICATE_ERROR": "Certificate is required",
+ "IDP_ENTITY_ID_ERROR": "Identity Provider Entity ID is required"
+ },
+ "ENTERPRISE_PAYWALL": {
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade to an Enterprise plan to access SAML single sign-on and other advanced security features.",
+ "ASK_ADMIN": "Please reach out to your administrator for the upgrade."
+ },
+ "PAYWALL": {
+ "TITLE": "Upgrade to enable SAML SSO",
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade your plan to get access to SAML single sign-on and other advanced features.",
+ "UPGRADE_NOW": "Upgrade now",
+ "CANCEL_ANYTIME": "You can change or cancel your plan anytime"
+ },
+ "ATTRIBUTE_MAPPING": {
+ "TITLE": "SAML Attribute Setup",
+ "DESCRIPTION": "The following attribute mappings must be configured in your identity provider"
+ },
+ "INFO_SECTION": {
+ "TITLE": "Service Provider Information",
+ "TOOLTIP": "Copy these values and configure them in your Identity Provider to establish the SAML connection"
+ }
+ }
+ },
"CREATE_ACCOUNT": {
"NO_ACCOUNT_WARNING": "Oh oh ! Nous n'avons pas trouvé de compte Chatwoot. Veuillez créer un nouveau compte pour continuer.",
"NEW_ACCOUNT": "Nouveau compte",
@@ -418,5 +503,255 @@
"SWITCH_TO_REPLY": "Basculer vers la réponse",
"TOGGLE_SNOOZE_DROPDOWN": "Activer/désactiver la liste déroulante de répétition"
}
+ },
+ "ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent assignment",
+ "DESCRIPTION": "Define policies to effectively manage workload and route conversations based on the needs of inboxes and agents. Learn more here"
+ },
+ "ASSIGNMENT_POLICY": {
+ "TITLE": "Assignment policy",
+ "DESCRIPTION": "Manage how conversations get assigned in inboxes.",
+ "FEATURES": [
+ "Assign by conversations evenly or by available capacity",
+ "Add fair distribution rules to avoid overloading any agent",
+ "Add inboxes to a policy - one policy per inbox"
+ ]
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "TITLE": "Agent capacity policy",
+ "DESCRIPTION": "Manage workload for agents.",
+ "FEATURES": [
+ "Define maximum conversations per inbox",
+ "Create exceptions based on labels and time",
+ "Add agents to a policy - one policy per agent"
+ ]
+ }
+ },
+ "AGENT_ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Assignment policy",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "ORDER": "Order",
+ "PRIORITY": "Priorité",
+ "ACTIVE": "Actif",
+ "INACTIVE": "Inactive",
+ "POPOVER": "Added inboxes",
+ "EDIT": "Modifier"
+ },
+ "NO_RECORDS_FOUND": "No assignment policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create assignment policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy created successfully",
+ "ERROR_MESSAGE": "Failed to create assignment policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit assignment policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_INBOX_DIALOG": {
+ "TITLE": "Add inbox",
+ "DESCRIPTION": "{inboxName} inbox is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Annuler"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update assignment policy"
+ },
+ "INBOX_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Inbox added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Inbox removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove inbox from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Description:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "STATUS": {
+ "LABEL": "État:",
+ "PLACEHOLDER": "Select status",
+ "ACTIVE": "Policy is active",
+ "INACTIVE": "Policy is inactive"
+ },
+ "ASSIGNMENT_ORDER": {
+ "LABEL": "Assignment order",
+ "ROUND_ROBIN": {
+ "LABEL": "Round robin",
+ "DESCRIPTION": "Assign conversations evenly among agents."
+ },
+ "BALANCED": {
+ "LABEL": "Balanced",
+ "DESCRIPTION": "Assign conversations based on available capacity."
+ }
+ },
+ "ASSIGNMENT_PRIORITY": {
+ "LABEL": "Assignment priority",
+ "EARLIEST_CREATED": {
+ "LABEL": "Earliest created",
+ "DESCRIPTION": "The conversation that was created first gets assigned first."
+ },
+ "LONGEST_WAITING": {
+ "LABEL": "Longest waiting",
+ "DESCRIPTION": "The conversation waiting the longest gets assigned first."
+ }
+ },
+ "FAIR_DISTRIBUTION": {
+ "LABEL": "Fair distribution policy",
+ "DESCRIPTION": "Set the maximum number of conversations that can be assigned per agent within a time window to avoid overloading any one agent. This required field defaults to 100 conversations per hour.",
+ "INPUT_MAX": "Assign max",
+ "DURATION": "Conversations per agent in every"
+ },
+ "INBOXES": {
+ "LABEL": "Added inboxes",
+ "DESCRIPTION": "Add inboxes for which this policy will be applicable.",
+ "ADD_BUTTON": "Add inbox",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select inboxes to add",
+ "ADD_BUTTON": "Ajouter"
+ },
+ "EMPTY_STATE": "No inboxes added to this policy, add an inbox to get started",
+ "API": {
+ "SUCCESS_MESSAGE": "Inbox successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Assignment policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete assignment policy"
+ }
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent capacity",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "POPOVER": "Added agents",
+ "EDIT": "Modifier"
+ },
+ "NO_RECORDS_FOUND": "No agent capacity policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create agent capacity policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy created successfully",
+ "ERROR_MESSAGE": "Failed to create agent capacity policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit agent capacity policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_AGENT_DIALOG": {
+ "TITLE": "Add agent",
+ "DESCRIPTION": "{agentName} is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Annuler"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update agent capacity policy"
+ },
+ "AGENT_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Agent added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Agent removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove agent from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Description:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "INBOX_CAPACITY_LIMIT": {
+ "LABEL": "Inbox capacity limits",
+ "ADD_BUTTON": "Add inbox",
+ "FIELD": {
+ "SELECT_INBOX": "Select inbox",
+ "MAX_CONVERSATIONS": "Max conversations",
+ "SET_LIMIT": "Set limit"
+ },
+ "EMPTY_STATE": "No inbox limit set"
+ },
+ "EXCLUSION_RULES": {
+ "LABEL": "Exclusion rules",
+ "DESCRIPTION": "Conversations that satisfy the following conditions would not count towards agent capacity",
+ "TAGS": {
+ "LABEL": "Exclude conversations tagged with specific labels",
+ "ADD_TAG": "add tag",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select tags to add"
+ },
+ "EMPTY_STATE": "No tags added to this policy."
+ },
+ "DURATION": {
+ "LABEL": "Exclude conversations older than a specified duration",
+ "PLACEHOLDER": "Set time"
+ }
+ },
+ "USERS": {
+ "LABEL": "Assigned agents",
+ "DESCRIPTION": "Add agents for which this policy will be applicable.",
+ "ADD_BUTTON": "Add agent",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select agents to add",
+ "ADD_BUTTON": "Ajouter"
+ },
+ "EMPTY_STATE": "No agents added",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Agent capacity policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete agent capacity policy"
+ }
+ },
+ "DELETE_POLICY": {
+ "TITLE": "Delete policy",
+ "DESCRIPTION": "Are you sure you want to delete this policy? This action cannot be undone.",
+ "CONFIRM_BUTTON_LABEL": "Supprimer",
+ "CANCEL_BUTTON_LABEL": "Annuler"
+ }
}
}
diff --git a/app/javascript/dashboard/i18n/locale/fr/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/fr/whatsappTemplates.json
index 71d0685cb..c9d3babc0 100644
--- a/app/javascript/dashboard/i18n/locale/fr/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/fr/whatsappTemplates.json
@@ -40,6 +40,7 @@
"BUTTON_LABEL": "Button {index}",
"COUPON_CODE": "Enter coupon code (max 15 chars)",
"MEDIA_URL_LABEL": "Enter {type} URL",
+ "DOCUMENT_NAME_PLACEHOLDER": "Enter document filename (e.g., Invoice_2025.pdf)",
"BUTTON_PARAMETER": "Enter button parameter"
}
}
diff --git a/app/javascript/dashboard/i18n/locale/he/automation.json b/app/javascript/dashboard/i18n/locale/he/automation.json
index 6166c90a3..392f0aab4 100644
--- a/app/javascript/dashboard/i18n/locale/he/automation.json
+++ b/app/javascript/dashboard/i18n/locale/he/automation.json
@@ -177,7 +177,8 @@
"REFERER_LINK": "Referrer Link",
"ASSIGNEE_NAME": "Assignee",
"TEAM_NAME": "צוות",
- "PRIORITY": "Priority"
+ "PRIORITY": "Priority",
+ "LABELS": "תוויות"
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/he/contact.json b/app/javascript/dashboard/i18n/locale/he/contact.json
index 7dd5d1191..7a5d98cdc 100644
--- a/app/javascript/dashboard/i18n/locale/he/contact.json
+++ b/app/javascript/dashboard/i18n/locale/he/contact.json
@@ -610,6 +610,15 @@
"SEND_MESSAGE": "שלח הודעה"
}
},
+ "TWILIO_OPTIONS": {
+ "LABEL": "Select template",
+ "SEARCH_PLACEHOLDER": "Search templates",
+ "EMPTY_STATE": "No templates found",
+ "TEMPLATE_PARSER": {
+ "BACK": "חזור",
+ "SEND_MESSAGE": "שלח הודעה"
+ }
+ },
"ACTION_BUTTONS": {
"DISCARD": "Discard",
"SEND": "Send ({keyCode})"
diff --git a/app/javascript/dashboard/i18n/locale/he/contactFilters.json b/app/javascript/dashboard/i18n/locale/he/contactFilters.json
index d135ffe04..b91a602a0 100644
--- a/app/javascript/dashboard/i18n/locale/he/contactFilters.json
+++ b/app/javascript/dashboard/i18n/locale/he/contactFilters.json
@@ -48,7 +48,8 @@
"CREATED_AT": "הוקם ב",
"LAST_ACTIVITY": "פעילות אחרונה",
"REFERER_LINK": "קישור מפנה",
- "BLOCKED": "Blocked"
+ "BLOCKED": "Blocked",
+ "LABELS": "תוויות"
},
"GROUPS": {
"STANDARD_FILTERS": "סננים סטנדרטיים",
diff --git a/app/javascript/dashboard/i18n/locale/he/contentTemplates.json b/app/javascript/dashboard/i18n/locale/he/contentTemplates.json
new file mode 100644
index 000000000..cb297885b
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/he/contentTemplates.json
@@ -0,0 +1,51 @@
+{
+ "CONTENT_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Twilio Templates",
+ "SUBTITLE": "Select the Twilio template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "חפש תבניות",
+ "NO_TEMPLATES_FOUND": "לא נמצאו תבניות עבור",
+ "NO_CONTENT": "No content",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "קטגוריה",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No Twilio templates available. Click refresh to sync templates from Twilio.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "שפה",
+ "TEMPLATE_BODY": "גוף התבנית",
+ "CATEGORY": "קטגוריה"
+ },
+ "TYPES": {
+ "MEDIA": "Media",
+ "QUICK_REPLY": "Quick Reply",
+ "TEXT": "טקסט"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "משתנים",
+ "LANGUAGE": "שפה",
+ "CATEGORY": "קטגוריה",
+ "VARIABLE_PLACEHOLDER": "הזן ערך {variable}",
+ "GO_BACK_LABEL": "חזור",
+ "SEND_MESSAGE_LABEL": "לשלוח הודעה",
+ "FORM_ERROR_MESSAGE": "נא למלא את כל המשתנים לפני השליחה",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "MEDIA_URL_LABEL": "Enter full media URL",
+ "MEDIA_URL_PLACEHOLDER": "https://example.com/image.jpg"
+ },
+ "FORM": {
+ "BACK_BUTTON": "חזור",
+ "SEND_MESSAGE_BUTTON": "לשלוח הודעה"
+ }
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/he/conversation.json b/app/javascript/dashboard/i18n/locale/he/conversation.json
index 866086a59..55eb3e99f 100644
--- a/app/javascript/dashboard/i18n/locale/he/conversation.json
+++ b/app/javascript/dashboard/i18n/locale/he/conversation.json
@@ -35,6 +35,11 @@
"API_HOURS_WINDOW": "You can only reply to this conversation within {hours} hours",
"NOT_ASSIGNED_TO_YOU": "השיחה לא שייכת לך, האם תרצה לשייך אותה אליך?",
"ASSIGN_TO_ME": "שייך לעצמך",
+ "BOT_HANDOFF_MESSAGE": "You are responding to a conversation which is currently handled by an assistant or a bot.",
+ "BOT_HANDOFF_ACTION": "Mark open and assign to you",
+ "BOT_HANDOFF_REOPEN_ACTION": "Mark conversation open",
+ "BOT_HANDOFF_SUCCESS": "Conversation has been handed over to you",
+ "BOT_HANDOFF_ERROR": "Failed to take over the conversation. Please try again.",
"TWILIO_WHATSAPP_CAN_REPLY": "אתה יכול להשיב לשיחה זו רק באמצעות הודעת תבנית בשל",
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "הגבלת חלון הודעות של 24 שעות",
"OLD_INSTAGRAM_INBOX_REPLY_BANNER": "This Instagram account was migrated to the new Instagram channel inbox. All new messages will show up there. You won’t be able to send messages from this conversation anymore.",
@@ -66,6 +71,17 @@
"SHOW_LABELS": "הצג תויות",
"HIDE_LABELS": "הסתר תוויות"
},
+ "VOICE_CALL": {
+ "INCOMING_CALL": "Incoming call",
+ "OUTGOING_CALL": "Outgoing call",
+ "CALL_IN_PROGRESS": "Call in progress",
+ "NO_ANSWER": "No answer",
+ "MISSED_CALL": "Missed call",
+ "CALL_ENDED": "Call ended",
+ "NOT_ANSWERED_YET": "Not answered yet",
+ "THEY_ANSWERED": "They answered",
+ "YOU_ANSWERED": "You answered"
+ },
"HEADER": {
"RESOLVE_ACTION": "פתרון",
"REOPEN_ACTION": "פתח מחדש",
diff --git a/app/javascript/dashboard/i18n/locale/he/helpCenter.json b/app/javascript/dashboard/i18n/locale/he/helpCenter.json
index 6e3c181a9..3aaa6a91d 100644
--- a/app/javascript/dashboard/i18n/locale/he/helpCenter.json
+++ b/app/javascript/dashboard/i18n/locale/he/helpCenter.json
@@ -741,7 +741,8 @@
"LIVE_CHAT_WIDGET": {
"LABEL": "Live chat widget",
"PLACEHOLDER": "Select live chat widget",
- "HELP_TEXT": "Select a live chat widget that will appear on your help center"
+ "HELP_TEXT": "Select a live chat widget that will appear on your help center",
+ "NONE_OPTION": "No widget"
},
"BRAND_COLOR": {
"LABEL": "Brand color"
@@ -807,6 +808,58 @@
"ERROR_MESSAGE": "Unable to update portal"
}
}
+ },
+ "PDF_UPLOAD": {
+ "TITLE": "Upload PDF Document",
+ "DESCRIPTION": "Upload a PDF document to automatically generate FAQs using AI",
+ "DRAG_DROP_TEXT": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "ADDITIONAL_CONTEXT_LABEL": "Additional Context (Optional)",
+ "ADDITIONAL_CONTEXT_PLACEHOLDER": "Provide any additional context or instructions for FAQ generation...",
+ "UPLOADING": "מעלה...",
+ "UPLOAD": "Upload & Process",
+ "CANCEL": "ביטול",
+ "ERROR_INVALID_TYPE": "Please select a valid PDF file",
+ "ERROR_FILE_TOO_LARGE": "File size must be less than 512MB",
+ "ERROR_UPLOAD_FAILED": "Failed to upload PDF. Please try again."
+ },
+ "PDF_DOCUMENTS": {
+ "TITLE": "PDF Documents",
+ "DESCRIPTION": "Manage uploaded PDF documents and generate FAQs from them",
+ "UPLOAD_PDF": "Upload PDF",
+ "UPLOAD_FIRST_PDF": "Upload your first PDF",
+ "UPLOADED_BY": "Uploaded by",
+ "GENERATE_FAQS": "Generate FAQs",
+ "GENERATING": "Generating...",
+ "CONFIRM_DELETE": "Are you sure you want to delete {filename}?",
+ "EMPTY_STATE": {
+ "TITLE": "No PDF documents yet",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQs using AI"
+ },
+ "STATUS": {
+ "UPLOADED": "Ready",
+ "PROCESSING": "Processing",
+ "PROCESSED": "הושלם",
+ "FAILED": "Failed"
+ }
+ },
+ "CONTENT_GENERATION": {
+ "TITLE": "Content Generation",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQ content using AI",
+ "UPLOAD_TITLE": "Upload PDF Document",
+ "DRAG_DROP": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "UPLOADING": "Processing document...",
+ "UPLOAD_SUCCESS": "Document processed successfully!",
+ "UPLOAD_ERROR": "Failed to upload document. Please try again.",
+ "INVALID_FILE_TYPE": "Please select a valid PDF file",
+ "FILE_TOO_LARGE": "File size must be less than 512MB",
+ "GENERATED_CONTENT": "Generated FAQ Content",
+ "PUBLISH_SELECTED": "Publish Selected",
+ "PUBLISHING": "Publishing...",
+ "FROM_DOCUMENT": "From document",
+ "NO_CONTENT": "No generated content available. Upload a PDF document to get started.",
+ "LOADING": "Loading generated content..."
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/he/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/he/inboxMgmt.json
index 9032c3c3d..88a98b856 100644
--- a/app/javascript/dashboard/i18n/locale/he/inboxMgmt.json
+++ b/app/javascript/dashboard/i18n/locale/he/inboxMgmt.json
@@ -272,8 +272,8 @@
},
"SUBMIT_BUTTON": "צור ערוץ וואטסאפ",
"EMBEDDED_SIGNUP": {
- "TITLE": "Quick Setup with Meta",
- "DESC": "You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
+ "TITLE": "Quick setup with Meta",
+ "DESC": "Use the WhatsApp Embedded Signup flow to quickly connect new numbers. You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
"BENEFITS": {
"TITLE": "Benefits of Embedded Signup:",
"EASY_SETUP": "No manual configuration required",
@@ -281,9 +281,8 @@
"AUTO_CONFIG": "Automatic webhook and phone number configuration"
},
"LEARN_MORE": {
- "TEXT": "To learn more about integrated signup, pricing, and limitations, visit",
- "LINK_TEXT": "this link.",
- "LINK_URL": "https://developers.facebook.com/docs/whatsapp/embedded-signup/custom-flows/onboarding-business-app-users#limitations"
+ "TEXT": "To learn more about integrated signup, pricing, and limitations, visit {link}.",
+ "LINK_TEXT": "this link"
},
"SUBMIT_BUTTON": "Connect with WhatsApp Business",
"AUTH_PROCESSING": "Authenticating with Meta",
@@ -296,7 +295,9 @@
"INVALID_BUSINESS_DATA": "Invalid business data received from Facebook. Please try again.",
"SIGNUP_ERROR": "Signup error occurred",
"AUTH_NOT_COMPLETED": "Authentication not completed. Please restart the process.",
- "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured"
+ "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured",
+ "MANUAL_FALLBACK": "If your number is already connected to the WhatsApp Business Platform (API), or if you’re a tech provider onboarding your own number, please use the {link} flow",
+ "MANUAL_LINK_TEXT": "manual setup flow"
},
"API": {
"ERROR_MESSAGE": "לא הצלחנו לשמור את ערוץ הוואטסאפ"
@@ -424,7 +425,51 @@
},
"AUTH": {
"TITLE": "בחר ערוץ",
- "DESC": "אנו תומכים בווידג'ט של צ'אט חי, עמוד פייסבוק, פרופיל טוויטר, WhatsApp, דוא\"ל וכו', כערוצי תקשורת. אם אתה רוצה לבנות ערוץ מותאם אישית, אתה יכול ליצור אותו באמצעות ערוץ ה-API. בחר ערוץ אחד מהאפשרויות מטה כדי להמשיך."
+ "DESC": "אנו תומכים בווידג'ט של צ'אט חי, עמוד פייסבוק, פרופיל טוויטר, WhatsApp, דוא\"ל וכו', כערוצי תקשורת. אם אתה רוצה לבנות ערוץ מותאם אישית, אתה יכול ליצור אותו באמצעות ערוץ ה-API. בחר ערוץ אחד מהאפשרויות מטה כדי להמשיך.",
+ "TITLE_NEXT": "Complete the setup",
+ "TITLE_FINISH": "Voilà!",
+ "CHANNEL": {
+ "WEBSITE": {
+ "TITLE": "Website",
+ "DESCRIPTION": "Create a live-chat widget"
+ },
+ "FACEBOOK": {
+ "TITLE": "פייסבוק",
+ "DESCRIPTION": "Connect your Facebook page"
+ },
+ "WHATSAPP": {
+ "TITLE": "WhatsApp",
+ "DESCRIPTION": "Support your customers on WhatsApp"
+ },
+ "EMAIL": {
+ "TITLE": "אימייל",
+ "DESCRIPTION": "Connect with Gmail, Outlook, or other providers"
+ },
+ "SMS": {
+ "TITLE": "SMS",
+ "DESCRIPTION": "Integrate SMS channel with Twilio or bandwidth"
+ },
+ "API": {
+ "TITLE": "API",
+ "DESCRIPTION": "Make a custom channel using our API"
+ },
+ "TELEGRAM": {
+ "TITLE": "Telegram",
+ "DESCRIPTION": "Configure Telegram channel using Bot token"
+ },
+ "LINE": {
+ "TITLE": "Line",
+ "DESCRIPTION": "Integrate your Line channel"
+ },
+ "INSTAGRAM": {
+ "TITLE": "Instagram",
+ "DESCRIPTION": "Connect your instagram account"
+ },
+ "VOICE": {
+ "TITLE": "Voice",
+ "DESCRIPTION": "Integrate with Twilio Voice"
+ }
+ }
},
"AGENTS": {
"TITLE": "סוכנים",
@@ -478,7 +523,10 @@
"MESSAGE": "כעת תוכל ליצור קשר עם הלקוחות שלך דרך הערוץ החדש שלך. תמיכה שמחה",
"BUTTON_TEXT": "קח אותי לשם",
"MORE_SETTINGS": "הגדרות נוספות",
- "WEBSITE_SUCCESS": "סיימת בהצלחה ליצור ערוץ אתר אינטרנט. העתק את הקוד המוצג למטה והדבק אותו באתר שלך. בפעם הבאה שלקוח ישתמש בצ'אט החי, השיחה תופיע אוטומטית בתיבת הדואר הנכנס שלך."
+ "WEBSITE_SUCCESS": "סיימת בהצלחה ליצור ערוץ אתר אינטרנט. העתק את הקוד המוצג למטה והדבק אותו באתר שלך. בפעם הבאה שלקוח ישתמש בצ'אט החי, השיחה תופיע אוטומטית בתיבת הדואר הנכנס שלך.",
+ "WHATSAPP_QR_INSTRUCTION": "Scan the QR code above to quickly test your WhatsApp inbox",
+ "MESSENGER_QR_INSTRUCTION": "Scan the QR code above to quickly test your Facebook Messenger inbox",
+ "TELEGRAM_QR_INSTRUCTION": "Scan the QR code above to quickly test your Telegram inbox"
},
"REAUTH": "הרשאה מחדש",
"VIEW": "צפה",
@@ -868,9 +916,18 @@
"SCRIPT_SETTINGS": "\n window.chatwootSettings = {options};"
},
"EMAIL_PROVIDERS": {
- "MICROSOFT": "מיקרוסופט",
- "GOOGLE": "Google",
- "OTHER_PROVIDERS": "ספקים אחרים"
+ "MICROSOFT": {
+ "TITLE": "מיקרוסופט",
+ "DESCRIPTION": "Connect with Microsoft"
+ },
+ "GOOGLE": {
+ "TITLE": "Google",
+ "DESCRIPTION": "Connect with Google"
+ },
+ "OTHER_PROVIDERS": {
+ "TITLE": "ספקים אחרים",
+ "DESCRIPTION": "Connect with Other Providers"
+ }
},
"CHANNELS": {
"MESSENGER": "Messenger",
diff --git a/app/javascript/dashboard/i18n/locale/he/integrations.json b/app/javascript/dashboard/i18n/locale/he/integrations.json
index 37388725a..a1b30db1a 100644
--- a/app/javascript/dashboard/i18n/locale/he/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/he/integrations.json
@@ -704,11 +704,28 @@
"ERROR_MESSAGE": "There was an error creating the document, please try again."
},
"FORM": {
+ "TYPE": {
+ "LABEL": "Document Type",
+ "URL": "URL",
+ "PDF": "PDF File"
+ },
"URL": {
"LABEL": "URL",
"PLACEHOLDER": "Enter the URL of the document",
"ERROR": "Please provide a valid URL for the document"
},
+ "PDF_FILE": {
+ "LABEL": "PDF File",
+ "CHOOSE_FILE": "Choose PDF file",
+ "ERROR": "Please select a PDF file",
+ "HELP_TEXT": "Maximum file size: 10MB",
+ "INVALID_TYPE": "Please select a valid PDF file",
+ "TOO_LARGE": "File size exceeds 10MB limit"
+ },
+ "NAME": {
+ "LABEL": "Document Name (Optional)",
+ "PLACEHOLDER": "Enter a name for the document"
+ },
"ASSISTANT": {
"LABEL": "Assistant",
"PLACEHOLDER": "Select the assistant",
@@ -744,6 +761,7 @@
"SELECTED": "{count} selected",
"SELECT_ALL": "Select all ({count})",
"UNSELECT_ALL": "Unselect all ({count})",
+ "SEARCH_PLACEHOLDER": "Search FAQs...",
"BULK_APPROVE_BUTTON": "Approve",
"BULK_DELETE_BUTTON": "מחק",
"BULK_APPROVE": {
diff --git a/app/javascript/dashboard/i18n/locale/he/mfa.json b/app/javascript/dashboard/i18n/locale/he/mfa.json
new file mode 100644
index 000000000..3fcf6b1d9
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/he/mfa.json
@@ -0,0 +1,106 @@
+{
+ "MFA_SETTINGS": {
+ "TITLE": "Two-Factor Authentication",
+ "SUBTITLE": "Secure your account with TOTP-based authentication",
+ "DESCRIPTION": "Add an extra layer of security to your account using a time-based one-time password (TOTP)",
+ "STATUS_TITLE": "Authentication Status",
+ "STATUS_DESCRIPTION": "Manage your two-factor authentication settings and backup recovery codes",
+ "ENABLED": "מופעל",
+ "DISABLED": "כבוי",
+ "STATUS_ENABLED": "Two-factor authentication is active",
+ "STATUS_ENABLED_DESC": "Your account is protected with an additional layer of security",
+ "ENABLE_BUTTON": "Enable Two-Factor Authentication",
+ "ENHANCE_SECURITY": "Enhance Your Account Security",
+ "ENHANCE_SECURITY_DESC": "Two-factor authentication adds an extra layer of security by requiring a verification code from your authenticator app in addition to your password.",
+ "SETUP": {
+ "STEP_NUMBER_1": "1",
+ "STEP_NUMBER_2": "2",
+ "STEP1_TITLE": "Scan QR Code with Your Authenticator App",
+ "STEP1_DESCRIPTION": "Use Google Authenticator, Authy, or any TOTP-compatible app",
+ "LOADING_QR": "Loading...",
+ "MANUAL_ENTRY": "Can't scan? Enter code manually",
+ "SECRET_KEY": "Secret Key",
+ "COPY": "עותק",
+ "ENTER_CODE": "Enter the 6-digit code from your authenticator app",
+ "ENTER_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify & Continue",
+ "CANCEL": "ביטול",
+ "ERROR_STARTING": "MFA not enabled. Please contact administrator.",
+ "INVALID_CODE": "Invalid verification code",
+ "SECRET_COPIED": "Secret key copied to clipboard",
+ "SUCCESS": "Two-factor authentication has been enabled successfully"
+ },
+ "BACKUP": {
+ "TITLE": "Save Your Backup Codes",
+ "DESCRIPTION": "Keep these codes safe. Each can be used once if you lose access to your authenticator",
+ "IMPORTANT": "Important:",
+ "IMPORTANT_NOTE": " Save these codes in a secure location. You won't be able to see them again.",
+ "DOWNLOAD": "הורד",
+ "COPY_ALL": "Copy All",
+ "CONFIRM": "I have saved my backup codes in a secure location and understand that I won't be able to see them again",
+ "COMPLETE_SETUP": "Complete Setup",
+ "CODES_COPIED": "Backup codes copied to clipboard"
+ },
+ "MANAGEMENT": {
+ "BACKUP_CODES": "Backup Codes",
+ "BACKUP_CODES_DESC": "Generate new codes if you've lost or used your existing ones",
+ "REGENERATE": "Regenerate Backup Codes",
+ "DISABLE_MFA": "Disable 2FA",
+ "DISABLE_MFA_DESC": "Remove two-factor authentication from your account",
+ "DISABLE_BUTTON": "Disable Two-Factor Authentication"
+ },
+ "DISABLE": {
+ "TITLE": "Disable Two-Factor Authentication",
+ "DESCRIPTION": "You'll need to enter your password and a verification code to disable two-factor authentication.",
+ "PASSWORD": "סיסמה",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Disable 2FA",
+ "CANCEL": "ביטול",
+ "SUCCESS": "Two-factor authentication has been disabled",
+ "ERROR": "Failed to disable MFA. Please check your credentials."
+ },
+ "REGENERATE": {
+ "TITLE": "Regenerate Backup Codes",
+ "DESCRIPTION": "This will invalidate your existing backup codes and generate new ones. Enter your verification code to continue.",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Generate New Codes",
+ "CANCEL": "ביטול",
+ "NEW_CODES_TITLE": "New Backup Codes Generated",
+ "NEW_CODES_DESC": "Your old backup codes have been invalidated. Save these new codes in a secure location.",
+ "CODES_IMPORTANT": "Important:",
+ "CODES_IMPORTANT_NOTE": " Each code can only be used once. Save them before closing this window.",
+ "DOWNLOAD_CODES": "Download Codes",
+ "COPY_ALL_CODES": "Copy All Codes",
+ "CODES_SAVED": "I've Saved My Codes",
+ "SUCCESS": "New backup codes have been generated",
+ "ERROR": "Failed to regenerate backup codes"
+ }
+ },
+ "MFA_VERIFICATION": {
+ "TITLE": "Two-Factor Authentication",
+ "DESCRIPTION": "Enter your verification code to continue",
+ "AUTHENTICATOR_APP": "Authenticator App",
+ "BACKUP_CODE": "Backup Code",
+ "ENTER_OTP_CODE": "Enter 6-digit code from your authenticator app",
+ "ENTER_BACKUP_CODE": "Enter one of your backup codes",
+ "BACKUP_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify",
+ "TRY_ANOTHER_METHOD": "Try another verification method",
+ "CANCEL_LOGIN": "Cancel and return to login",
+ "HELP_TEXT": "Having trouble signing in?",
+ "LEARN_MORE": "Learn more about 2FA",
+ "HELP_MODAL": {
+ "TITLE": "Two-Factor Authentication Help",
+ "AUTHENTICATOR_TITLE": "Using an Authenticator App",
+ "AUTHENTICATOR_DESC": "Open your authenticator app (Google Authenticator, Authy, etc.) and enter the 6-digit code shown for your account.",
+ "BACKUP_TITLE": "Using a Backup Code",
+ "BACKUP_DESC": "If you don't have access to your authenticator app, you can use one of the backup codes you saved when setting up 2FA. Each code can only be used once.",
+ "CONTACT_TITLE": "Need More Help?",
+ "CONTACT_DESC_CLOUD": "If you've lost access to both your authenticator app and backup codes, please reach out to Chatwoot support for assistance.",
+ "CONTACT_DESC_SELF_HOSTED": "If you've lost access to both your authenticator app and backup codes, please contact your administrator for assistance."
+ },
+ "VERIFICATION_FAILED": "Verification failed. Please try again."
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/he/settings.json b/app/javascript/dashboard/i18n/locale/he/settings.json
index d6c5fe90f..bc4a2ec7c 100644
--- a/app/javascript/dashboard/i18n/locale/he/settings.json
+++ b/app/javascript/dashboard/i18n/locale/he/settings.json
@@ -51,6 +51,13 @@
"LARGER": "Larger",
"EXTRA_LARGE": "Extra Large"
}
+ },
+ "LANGUAGE": {
+ "TITLE": "Preferred Language",
+ "NOTE": "Choose the language you want to use.",
+ "UPDATE_SUCCESS": "Your Language settings have been updated successfully",
+ "UPDATE_ERROR": "There is an error while updating the language settings, please try again",
+ "USE_ACCOUNT_DEFAULT": "Use account default"
}
},
"MESSAGE_SIGNATURE_SECTION": {
@@ -73,6 +80,11 @@
"NOTE": "עדכון הסיסמה שלך יאפס את הכניסות שלך במספר מכשירים.",
"BTN_TEXT": "שנה סיסמא"
},
+ "SECURITY_SECTION": {
+ "TITLE": "Security",
+ "NOTE": "Manage additional security features for your account.",
+ "MFA_BUTTON": "Manage Two-Factor Authentication"
+ },
"ACCESS_TOKEN": {
"TITLE": "אסימון",
"NOTE": "משמש לחיבורי API",
@@ -330,6 +342,7 @@
"REPORTS_LABEL": "תוויות",
"REPORTS_INBOX": "תיבת הדואר הנכנס",
"REPORTS_TEAM": "צוות",
+ "AGENT_ASSIGNMENT": "Agent Assignment",
"SET_AVAILABILITY_TITLE": "הגדר את עצמך בתור",
"SET_YOUR_AVAILABILITY": "הגדר את הזמינות שלך",
"SLA": "SLA",
@@ -350,7 +363,8 @@
"INFO_TEXT": "תן למערכת לסמן אותך באופן אוטומטי במצב לא מקוון כשאתה לא משתמש באפליקציה או בלוח המחוונים.",
"INFO_SHORT": "Automatically mark offline when you aren't using the app."
},
- "DOCS": "קרא מסמכים"
+ "DOCS": "קרא מסמכים",
+ "SECURITY": "Security"
},
"BILLING_SETTINGS": {
"TITLE": "חיוב",
@@ -382,6 +396,77 @@
},
"NO_BILLING_USER": "חשבון החיוב שלך מוגדר. אנא רענן את הדף ונסה שוב."
},
+ "SECURITY_SETTINGS": {
+ "TITLE": "Security",
+ "DESCRIPTION": "Manage your account security settings.",
+ "LINK_TEXT": "Learn more about SAML SSO",
+ "SAML": {
+ "TITLE": "SAML SSO",
+ "NOTE": "Configure SAML single sign-on for your account. Users will authenticate through your identity provider instead of using email/password.",
+ "ACS_URL": {
+ "LABEL": "ACS URL",
+ "TOOLTIP": "Assertion Consumer Service URL - Configure this URL in your IdP as the destination for SAML responses"
+ },
+ "SSO_URL": {
+ "LABEL": "SSO URL",
+ "HELP": "The URL where SAML authentication requests will be sent",
+ "PLACEHOLDER": "https://your-idp.com/saml/sso"
+ },
+ "CERTIFICATE": {
+ "LABEL": "Signing certificate in PEM format",
+ "HELP": "The public certificate from your identity provider used to verify SAML responses",
+ "PLACEHOLDER": "-----BEGIN CERTIFICATE-----\nMIIC..."
+ },
+ "FINGERPRINT": {
+ "LABEL": "Fingerprint",
+ "TOOLTIP": "SHA-1 fingerprint of the certificate - Use this to verify the certificate in your IdP configuration"
+ },
+ "COPY_SUCCESS": "הקוד הועתק ללוח בהצלחה",
+ "SP_ENTITY_ID": {
+ "LABEL": "SP Entity ID",
+ "HELP": "Unique identifier for this application as a service provider (auto-generated).",
+ "TOOLTIP": "Unique identifier for Chatwoot as the Service Provider - Configure this in your IdP settings"
+ },
+ "IDP_ENTITY_ID": {
+ "LABEL": "Identity Provider Entity ID",
+ "HELP": "Unique identifier for your identity provider (usually found in IdP configuration)",
+ "PLACEHOLDER": "https://your-idp.com/saml"
+ },
+ "UPDATE_BUTTON": "Update SAML Settings",
+ "API": {
+ "SUCCESS": "SAML settings updated successfully",
+ "ERROR": "Failed to update SAML settings",
+ "ERROR_LOADING": "Failed to load SAML settings",
+ "DISABLED": "SAML settings disabled successfully"
+ },
+ "VALIDATION": {
+ "REQUIRED_FIELDS": "SSO URL, Identity Provider Entity ID, and Certificate are required fields",
+ "SSO_URL_ERROR": "Please enter a valid SSO URL",
+ "CERTIFICATE_ERROR": "Certificate is required",
+ "IDP_ENTITY_ID_ERROR": "Identity Provider Entity ID is required"
+ },
+ "ENTERPRISE_PAYWALL": {
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade to an Enterprise plan to access SAML single sign-on and other advanced security features.",
+ "ASK_ADMIN": "Please reach out to your administrator for the upgrade."
+ },
+ "PAYWALL": {
+ "TITLE": "Upgrade to enable SAML SSO",
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade your plan to get access to SAML single sign-on and other advanced features.",
+ "UPGRADE_NOW": "Upgrade now",
+ "CANCEL_ANYTIME": "You can change or cancel your plan anytime"
+ },
+ "ATTRIBUTE_MAPPING": {
+ "TITLE": "SAML Attribute Setup",
+ "DESCRIPTION": "The following attribute mappings must be configured in your identity provider"
+ },
+ "INFO_SECTION": {
+ "TITLE": "Service Provider Information",
+ "TOOLTIP": "Copy these values and configure them in your Identity Provider to establish the SAML connection"
+ }
+ }
+ },
"CREATE_ACCOUNT": {
"NO_ACCOUNT_WARNING": "אה הו! לא הצלחנו למצוא חשבונות Chatwoot. נא ליצור חשבון חדש כדי להמשיך.",
"NEW_ACCOUNT": "חשבון חדש",
@@ -418,5 +503,255 @@
"SWITCH_TO_REPLY": "עבור לתשובה",
"TOGGLE_SNOOZE_DROPDOWN": "החלפת תפריט נודניק"
}
+ },
+ "ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent assignment",
+ "DESCRIPTION": "Define policies to effectively manage workload and route conversations based on the needs of inboxes and agents. Learn more here"
+ },
+ "ASSIGNMENT_POLICY": {
+ "TITLE": "Assignment policy",
+ "DESCRIPTION": "Manage how conversations get assigned in inboxes.",
+ "FEATURES": [
+ "Assign by conversations evenly or by available capacity",
+ "Add fair distribution rules to avoid overloading any agent",
+ "Add inboxes to a policy - one policy per inbox"
+ ]
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "TITLE": "Agent capacity policy",
+ "DESCRIPTION": "Manage workload for agents.",
+ "FEATURES": [
+ "Define maximum conversations per inbox",
+ "Create exceptions based on labels and time",
+ "Add agents to a policy - one policy per agent"
+ ]
+ }
+ },
+ "AGENT_ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Assignment policy",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "ORDER": "Order",
+ "PRIORITY": "Priority",
+ "ACTIVE": "פעיל",
+ "INACTIVE": "Inactive",
+ "POPOVER": "Added inboxes",
+ "EDIT": "ערוך"
+ },
+ "NO_RECORDS_FOUND": "No assignment policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create assignment policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy created successfully",
+ "ERROR_MESSAGE": "Failed to create assignment policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit assignment policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_INBOX_DIALOG": {
+ "TITLE": "Add inbox",
+ "DESCRIPTION": "{inboxName} inbox is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "ביטול"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update assignment policy"
+ },
+ "INBOX_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Inbox added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Inbox removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove inbox from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "תיאור:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "STATUS": {
+ "LABEL": "מצב:",
+ "PLACEHOLDER": "Select status",
+ "ACTIVE": "Policy is active",
+ "INACTIVE": "Policy is inactive"
+ },
+ "ASSIGNMENT_ORDER": {
+ "LABEL": "Assignment order",
+ "ROUND_ROBIN": {
+ "LABEL": "Round robin",
+ "DESCRIPTION": "Assign conversations evenly among agents."
+ },
+ "BALANCED": {
+ "LABEL": "Balanced",
+ "DESCRIPTION": "Assign conversations based on available capacity."
+ }
+ },
+ "ASSIGNMENT_PRIORITY": {
+ "LABEL": "Assignment priority",
+ "EARLIEST_CREATED": {
+ "LABEL": "Earliest created",
+ "DESCRIPTION": "The conversation that was created first gets assigned first."
+ },
+ "LONGEST_WAITING": {
+ "LABEL": "Longest waiting",
+ "DESCRIPTION": "The conversation waiting the longest gets assigned first."
+ }
+ },
+ "FAIR_DISTRIBUTION": {
+ "LABEL": "Fair distribution policy",
+ "DESCRIPTION": "Set the maximum number of conversations that can be assigned per agent within a time window to avoid overloading any one agent. This required field defaults to 100 conversations per hour.",
+ "INPUT_MAX": "Assign max",
+ "DURATION": "Conversations per agent in every"
+ },
+ "INBOXES": {
+ "LABEL": "Added inboxes",
+ "DESCRIPTION": "Add inboxes for which this policy will be applicable.",
+ "ADD_BUTTON": "Add inbox",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select inboxes to add",
+ "ADD_BUTTON": "הוסף"
+ },
+ "EMPTY_STATE": "No inboxes added to this policy, add an inbox to get started",
+ "API": {
+ "SUCCESS_MESSAGE": "Inbox successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Assignment policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete assignment policy"
+ }
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent capacity",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "POPOVER": "Added agents",
+ "EDIT": "ערוך"
+ },
+ "NO_RECORDS_FOUND": "No agent capacity policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create agent capacity policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy created successfully",
+ "ERROR_MESSAGE": "Failed to create agent capacity policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit agent capacity policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_AGENT_DIALOG": {
+ "TITLE": "Add agent",
+ "DESCRIPTION": "{agentName} is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "ביטול"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update agent capacity policy"
+ },
+ "AGENT_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Agent added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Agent removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove agent from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "תיאור:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "INBOX_CAPACITY_LIMIT": {
+ "LABEL": "Inbox capacity limits",
+ "ADD_BUTTON": "Add inbox",
+ "FIELD": {
+ "SELECT_INBOX": "Select inbox",
+ "MAX_CONVERSATIONS": "Max conversations",
+ "SET_LIMIT": "Set limit"
+ },
+ "EMPTY_STATE": "No inbox limit set"
+ },
+ "EXCLUSION_RULES": {
+ "LABEL": "Exclusion rules",
+ "DESCRIPTION": "Conversations that satisfy the following conditions would not count towards agent capacity",
+ "TAGS": {
+ "LABEL": "Exclude conversations tagged with specific labels",
+ "ADD_TAG": "add tag",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select tags to add"
+ },
+ "EMPTY_STATE": "No tags added to this policy."
+ },
+ "DURATION": {
+ "LABEL": "Exclude conversations older than a specified duration",
+ "PLACEHOLDER": "Set time"
+ }
+ },
+ "USERS": {
+ "LABEL": "Assigned agents",
+ "DESCRIPTION": "Add agents for which this policy will be applicable.",
+ "ADD_BUTTON": "Add agent",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select agents to add",
+ "ADD_BUTTON": "הוסף"
+ },
+ "EMPTY_STATE": "No agents added",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Agent capacity policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete agent capacity policy"
+ }
+ },
+ "DELETE_POLICY": {
+ "TITLE": "Delete policy",
+ "DESCRIPTION": "Are you sure you want to delete this policy? This action cannot be undone.",
+ "CONFIRM_BUTTON_LABEL": "מחק",
+ "CANCEL_BUTTON_LABEL": "ביטול"
+ }
}
}
diff --git a/app/javascript/dashboard/i18n/locale/he/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/he/whatsappTemplates.json
index 67b54f91f..62c127772 100644
--- a/app/javascript/dashboard/i18n/locale/he/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/he/whatsappTemplates.json
@@ -40,6 +40,7 @@
"BUTTON_LABEL": "Button {index}",
"COUPON_CODE": "Enter coupon code (max 15 chars)",
"MEDIA_URL_LABEL": "Enter {type} URL",
+ "DOCUMENT_NAME_PLACEHOLDER": "Enter document filename (e.g., Invoice_2025.pdf)",
"BUTTON_PARAMETER": "Enter button parameter"
}
}
diff --git a/app/javascript/dashboard/i18n/locale/hi/automation.json b/app/javascript/dashboard/i18n/locale/hi/automation.json
index 80274f488..43245a1d5 100644
--- a/app/javascript/dashboard/i18n/locale/hi/automation.json
+++ b/app/javascript/dashboard/i18n/locale/hi/automation.json
@@ -177,7 +177,8 @@
"REFERER_LINK": "Referrer Link",
"ASSIGNEE_NAME": "Assignee",
"TEAM_NAME": "Team",
- "PRIORITY": "Priority"
+ "PRIORITY": "Priority",
+ "LABELS": "Labels"
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/hi/contact.json b/app/javascript/dashboard/i18n/locale/hi/contact.json
index b46989678..89cae2e68 100644
--- a/app/javascript/dashboard/i18n/locale/hi/contact.json
+++ b/app/javascript/dashboard/i18n/locale/hi/contact.json
@@ -610,6 +610,15 @@
"SEND_MESSAGE": "Send message"
}
},
+ "TWILIO_OPTIONS": {
+ "LABEL": "Select template",
+ "SEARCH_PLACEHOLDER": "Search templates",
+ "EMPTY_STATE": "No templates found",
+ "TEMPLATE_PARSER": {
+ "BACK": "Go back",
+ "SEND_MESSAGE": "Send message"
+ }
+ },
"ACTION_BUTTONS": {
"DISCARD": "Discard",
"SEND": "Send ({keyCode})"
diff --git a/app/javascript/dashboard/i18n/locale/hi/contactFilters.json b/app/javascript/dashboard/i18n/locale/hi/contactFilters.json
index bb3221c6e..4c62f0789 100644
--- a/app/javascript/dashboard/i18n/locale/hi/contactFilters.json
+++ b/app/javascript/dashboard/i18n/locale/hi/contactFilters.json
@@ -48,7 +48,8 @@
"CREATED_AT": "Created At",
"LAST_ACTIVITY": "Last Activity",
"REFERER_LINK": "Referrer link",
- "BLOCKED": "Blocked"
+ "BLOCKED": "Blocked",
+ "LABELS": "Labels"
},
"GROUPS": {
"STANDARD_FILTERS": "Standard Filters",
diff --git a/app/javascript/dashboard/i18n/locale/hi/contentTemplates.json b/app/javascript/dashboard/i18n/locale/hi/contentTemplates.json
new file mode 100644
index 000000000..a9b1d54c4
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/hi/contentTemplates.json
@@ -0,0 +1,51 @@
+{
+ "CONTENT_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Twilio Templates",
+ "SUBTITLE": "Select the Twilio template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Search Templates",
+ "NO_TEMPLATES_FOUND": "No templates found for",
+ "NO_CONTENT": "No content",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Category",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No Twilio templates available. Click refresh to sync templates from Twilio.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Language",
+ "TEMPLATE_BODY": "Template Body",
+ "CATEGORY": "Category"
+ },
+ "TYPES": {
+ "MEDIA": "Media",
+ "QUICK_REPLY": "Quick Reply",
+ "TEXT": "Text"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variables",
+ "LANGUAGE": "Language",
+ "CATEGORY": "Category",
+ "VARIABLE_PLACEHOLDER": "Enter {variable} value",
+ "GO_BACK_LABEL": "Go Back",
+ "SEND_MESSAGE_LABEL": "Send Message",
+ "FORM_ERROR_MESSAGE": "Please fill all variables before sending",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "MEDIA_URL_LABEL": "Enter full media URL",
+ "MEDIA_URL_PLACEHOLDER": "https://example.com/image.jpg"
+ },
+ "FORM": {
+ "BACK_BUTTON": "Back",
+ "SEND_MESSAGE_BUTTON": "Send Message"
+ }
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/hi/conversation.json b/app/javascript/dashboard/i18n/locale/hi/conversation.json
index 308f24f51..9fd39b70f 100644
--- a/app/javascript/dashboard/i18n/locale/hi/conversation.json
+++ b/app/javascript/dashboard/i18n/locale/hi/conversation.json
@@ -35,6 +35,11 @@
"API_HOURS_WINDOW": "You can only reply to this conversation within {hours} hours",
"NOT_ASSIGNED_TO_YOU": "This conversation is not assigned to you. Would you like to assign this conversation to yourself?",
"ASSIGN_TO_ME": "Assign to me",
+ "BOT_HANDOFF_MESSAGE": "You are responding to a conversation which is currently handled by an assistant or a bot.",
+ "BOT_HANDOFF_ACTION": "Mark open and assign to you",
+ "BOT_HANDOFF_REOPEN_ACTION": "Mark conversation open",
+ "BOT_HANDOFF_SUCCESS": "Conversation has been handed over to you",
+ "BOT_HANDOFF_ERROR": "Failed to take over the conversation. Please try again.",
"TWILIO_WHATSAPP_CAN_REPLY": "You can only reply to this conversation using a template message due to",
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "24 hour message window restriction",
"OLD_INSTAGRAM_INBOX_REPLY_BANNER": "This Instagram account was migrated to the new Instagram channel inbox. All new messages will show up there. You won’t be able to send messages from this conversation anymore.",
@@ -66,6 +71,17 @@
"SHOW_LABELS": "Show labels",
"HIDE_LABELS": "Hide labels"
},
+ "VOICE_CALL": {
+ "INCOMING_CALL": "Incoming call",
+ "OUTGOING_CALL": "Outgoing call",
+ "CALL_IN_PROGRESS": "Call in progress",
+ "NO_ANSWER": "No answer",
+ "MISSED_CALL": "Missed call",
+ "CALL_ENDED": "Call ended",
+ "NOT_ANSWERED_YET": "Not answered yet",
+ "THEY_ANSWERED": "They answered",
+ "YOU_ANSWERED": "You answered"
+ },
"HEADER": {
"RESOLVE_ACTION": "Resolve",
"REOPEN_ACTION": "Reopen",
diff --git a/app/javascript/dashboard/i18n/locale/hi/helpCenter.json b/app/javascript/dashboard/i18n/locale/hi/helpCenter.json
index 133d87369..328ab88f4 100644
--- a/app/javascript/dashboard/i18n/locale/hi/helpCenter.json
+++ b/app/javascript/dashboard/i18n/locale/hi/helpCenter.json
@@ -741,7 +741,8 @@
"LIVE_CHAT_WIDGET": {
"LABEL": "Live chat widget",
"PLACEHOLDER": "Select live chat widget",
- "HELP_TEXT": "Select a live chat widget that will appear on your help center"
+ "HELP_TEXT": "Select a live chat widget that will appear on your help center",
+ "NONE_OPTION": "No widget"
},
"BRAND_COLOR": {
"LABEL": "Brand color"
@@ -807,6 +808,58 @@
"ERROR_MESSAGE": "Unable to update portal"
}
}
+ },
+ "PDF_UPLOAD": {
+ "TITLE": "Upload PDF Document",
+ "DESCRIPTION": "Upload a PDF document to automatically generate FAQs using AI",
+ "DRAG_DROP_TEXT": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "ADDITIONAL_CONTEXT_LABEL": "Additional Context (Optional)",
+ "ADDITIONAL_CONTEXT_PLACEHOLDER": "Provide any additional context or instructions for FAQ generation...",
+ "UPLOADING": "Uploading...",
+ "UPLOAD": "Upload & Process",
+ "CANCEL": "Cancel",
+ "ERROR_INVALID_TYPE": "Please select a valid PDF file",
+ "ERROR_FILE_TOO_LARGE": "File size must be less than 512MB",
+ "ERROR_UPLOAD_FAILED": "Failed to upload PDF. Please try again."
+ },
+ "PDF_DOCUMENTS": {
+ "TITLE": "PDF Documents",
+ "DESCRIPTION": "Manage uploaded PDF documents and generate FAQs from them",
+ "UPLOAD_PDF": "Upload PDF",
+ "UPLOAD_FIRST_PDF": "Upload your first PDF",
+ "UPLOADED_BY": "Uploaded by",
+ "GENERATE_FAQS": "Generate FAQs",
+ "GENERATING": "Generating...",
+ "CONFIRM_DELETE": "Are you sure you want to delete {filename}?",
+ "EMPTY_STATE": {
+ "TITLE": "No PDF documents yet",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQs using AI"
+ },
+ "STATUS": {
+ "UPLOADED": "Ready",
+ "PROCESSING": "Processing",
+ "PROCESSED": "Completed",
+ "FAILED": "Failed"
+ }
+ },
+ "CONTENT_GENERATION": {
+ "TITLE": "Content Generation",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQ content using AI",
+ "UPLOAD_TITLE": "Upload PDF Document",
+ "DRAG_DROP": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "UPLOADING": "Processing document...",
+ "UPLOAD_SUCCESS": "Document processed successfully!",
+ "UPLOAD_ERROR": "Failed to upload document. Please try again.",
+ "INVALID_FILE_TYPE": "Please select a valid PDF file",
+ "FILE_TOO_LARGE": "File size must be less than 512MB",
+ "GENERATED_CONTENT": "Generated FAQ Content",
+ "PUBLISH_SELECTED": "Publish Selected",
+ "PUBLISHING": "Publishing...",
+ "FROM_DOCUMENT": "From document",
+ "NO_CONTENT": "No generated content available. Upload a PDF document to get started.",
+ "LOADING": "Loading generated content..."
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/hi/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/hi/inboxMgmt.json
index 031fcb57e..cc3b7f015 100644
--- a/app/javascript/dashboard/i18n/locale/hi/inboxMgmt.json
+++ b/app/javascript/dashboard/i18n/locale/hi/inboxMgmt.json
@@ -272,8 +272,8 @@
},
"SUBMIT_BUTTON": "Create WhatsApp Channel",
"EMBEDDED_SIGNUP": {
- "TITLE": "Quick Setup with Meta",
- "DESC": "You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
+ "TITLE": "Quick setup with Meta",
+ "DESC": "Use the WhatsApp Embedded Signup flow to quickly connect new numbers. You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
"BENEFITS": {
"TITLE": "Benefits of Embedded Signup:",
"EASY_SETUP": "No manual configuration required",
@@ -281,9 +281,8 @@
"AUTO_CONFIG": "Automatic webhook and phone number configuration"
},
"LEARN_MORE": {
- "TEXT": "To learn more about integrated signup, pricing, and limitations, visit",
- "LINK_TEXT": "this link.",
- "LINK_URL": "https://developers.facebook.com/docs/whatsapp/embedded-signup/custom-flows/onboarding-business-app-users#limitations"
+ "TEXT": "To learn more about integrated signup, pricing, and limitations, visit {link}.",
+ "LINK_TEXT": "this link"
},
"SUBMIT_BUTTON": "Connect with WhatsApp Business",
"AUTH_PROCESSING": "Authenticating with Meta",
@@ -296,7 +295,9 @@
"INVALID_BUSINESS_DATA": "Invalid business data received from Facebook. Please try again.",
"SIGNUP_ERROR": "Signup error occurred",
"AUTH_NOT_COMPLETED": "Authentication not completed. Please restart the process.",
- "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured"
+ "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured",
+ "MANUAL_FALLBACK": "If your number is already connected to the WhatsApp Business Platform (API), or if you’re a tech provider onboarding your own number, please use the {link} flow",
+ "MANUAL_LINK_TEXT": "manual setup flow"
},
"API": {
"ERROR_MESSAGE": "We were not able to save the WhatsApp channel"
@@ -424,7 +425,51 @@
},
"AUTH": {
"TITLE": "Choose a channel",
- "DESC": "Chatwoot supports live-chat widgets, Facebook Messenger, Twitter profiles, WhatsApp, Emails, etc., as channels. If you want to build a custom channel, you can create it using the API channel. To get started, choose one of the channels below."
+ "DESC": "Chatwoot supports live-chat widgets, Facebook Messenger, Twitter profiles, WhatsApp, Emails, etc., as channels. If you want to build a custom channel, you can create it using the API channel. To get started, choose one of the channels below.",
+ "TITLE_NEXT": "Complete the setup",
+ "TITLE_FINISH": "Voilà!",
+ "CHANNEL": {
+ "WEBSITE": {
+ "TITLE": "Website",
+ "DESCRIPTION": "Create a live-chat widget"
+ },
+ "FACEBOOK": {
+ "TITLE": "Facebook",
+ "DESCRIPTION": "Connect your Facebook page"
+ },
+ "WHATSAPP": {
+ "TITLE": "WhatsApp",
+ "DESCRIPTION": "Support your customers on WhatsApp"
+ },
+ "EMAIL": {
+ "TITLE": "Email",
+ "DESCRIPTION": "Connect with Gmail, Outlook, or other providers"
+ },
+ "SMS": {
+ "TITLE": "SMS",
+ "DESCRIPTION": "Integrate SMS channel with Twilio or bandwidth"
+ },
+ "API": {
+ "TITLE": "API",
+ "DESCRIPTION": "Make a custom channel using our API"
+ },
+ "TELEGRAM": {
+ "TITLE": "Telegram",
+ "DESCRIPTION": "Configure Telegram channel using Bot token"
+ },
+ "LINE": {
+ "TITLE": "Line",
+ "DESCRIPTION": "Integrate your Line channel"
+ },
+ "INSTAGRAM": {
+ "TITLE": "Instagram",
+ "DESCRIPTION": "Connect your instagram account"
+ },
+ "VOICE": {
+ "TITLE": "Voice",
+ "DESCRIPTION": "Integrate with Twilio Voice"
+ }
+ }
},
"AGENTS": {
"TITLE": "Agents",
@@ -478,7 +523,10 @@
"MESSAGE": "You can now engage with your customers through your new Channel. Happy supporting",
"BUTTON_TEXT": "Take me there",
"MORE_SETTINGS": "More settings",
- "WEBSITE_SUCCESS": "You have successfully finished creating a website channel. Copy the code shown below and paste it on your website. Next time a customer use the live chat, the conversation will automatically appear on your inbox."
+ "WEBSITE_SUCCESS": "You have successfully finished creating a website channel. Copy the code shown below and paste it on your website. Next time a customer use the live chat, the conversation will automatically appear on your inbox.",
+ "WHATSAPP_QR_INSTRUCTION": "Scan the QR code above to quickly test your WhatsApp inbox",
+ "MESSENGER_QR_INSTRUCTION": "Scan the QR code above to quickly test your Facebook Messenger inbox",
+ "TELEGRAM_QR_INSTRUCTION": "Scan the QR code above to quickly test your Telegram inbox"
},
"REAUTH": "Reauthorize",
"VIEW": "View",
@@ -868,9 +916,18 @@
"SCRIPT_SETTINGS": "\n window.chatwootSettings = {options};"
},
"EMAIL_PROVIDERS": {
- "MICROSOFT": "Microsoft",
- "GOOGLE": "Google",
- "OTHER_PROVIDERS": "Other Providers"
+ "MICROSOFT": {
+ "TITLE": "Microsoft",
+ "DESCRIPTION": "Connect with Microsoft"
+ },
+ "GOOGLE": {
+ "TITLE": "Google",
+ "DESCRIPTION": "Connect with Google"
+ },
+ "OTHER_PROVIDERS": {
+ "TITLE": "Other Providers",
+ "DESCRIPTION": "Connect with Other Providers"
+ }
},
"CHANNELS": {
"MESSENGER": "Messenger",
diff --git a/app/javascript/dashboard/i18n/locale/hi/integrations.json b/app/javascript/dashboard/i18n/locale/hi/integrations.json
index cab231d7d..f2d79d5d7 100644
--- a/app/javascript/dashboard/i18n/locale/hi/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/hi/integrations.json
@@ -704,11 +704,28 @@
"ERROR_MESSAGE": "There was an error creating the document, please try again."
},
"FORM": {
+ "TYPE": {
+ "LABEL": "Document Type",
+ "URL": "URL",
+ "PDF": "PDF File"
+ },
"URL": {
"LABEL": "URL",
"PLACEHOLDER": "Enter the URL of the document",
"ERROR": "Please provide a valid URL for the document"
},
+ "PDF_FILE": {
+ "LABEL": "PDF File",
+ "CHOOSE_FILE": "Choose PDF file",
+ "ERROR": "Please select a PDF file",
+ "HELP_TEXT": "Maximum file size: 10MB",
+ "INVALID_TYPE": "Please select a valid PDF file",
+ "TOO_LARGE": "File size exceeds 10MB limit"
+ },
+ "NAME": {
+ "LABEL": "Document Name (Optional)",
+ "PLACEHOLDER": "Enter a name for the document"
+ },
"ASSISTANT": {
"LABEL": "Assistant",
"PLACEHOLDER": "Select the assistant",
@@ -744,6 +761,7 @@
"SELECTED": "{count} selected",
"SELECT_ALL": "Select all ({count})",
"UNSELECT_ALL": "Unselect all ({count})",
+ "SEARCH_PLACEHOLDER": "Search FAQs...",
"BULK_APPROVE_BUTTON": "Approve",
"BULK_DELETE_BUTTON": "Delete",
"BULK_APPROVE": {
diff --git a/app/javascript/dashboard/i18n/locale/hi/mfa.json b/app/javascript/dashboard/i18n/locale/hi/mfa.json
new file mode 100644
index 000000000..f7556fdcf
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/hi/mfa.json
@@ -0,0 +1,106 @@
+{
+ "MFA_SETTINGS": {
+ "TITLE": "Two-Factor Authentication",
+ "SUBTITLE": "Secure your account with TOTP-based authentication",
+ "DESCRIPTION": "Add an extra layer of security to your account using a time-based one-time password (TOTP)",
+ "STATUS_TITLE": "Authentication Status",
+ "STATUS_DESCRIPTION": "Manage your two-factor authentication settings and backup recovery codes",
+ "ENABLED": "Enabled",
+ "DISABLED": "Disabled",
+ "STATUS_ENABLED": "Two-factor authentication is active",
+ "STATUS_ENABLED_DESC": "Your account is protected with an additional layer of security",
+ "ENABLE_BUTTON": "Enable Two-Factor Authentication",
+ "ENHANCE_SECURITY": "Enhance Your Account Security",
+ "ENHANCE_SECURITY_DESC": "Two-factor authentication adds an extra layer of security by requiring a verification code from your authenticator app in addition to your password.",
+ "SETUP": {
+ "STEP_NUMBER_1": "1",
+ "STEP_NUMBER_2": "2",
+ "STEP1_TITLE": "Scan QR Code with Your Authenticator App",
+ "STEP1_DESCRIPTION": "Use Google Authenticator, Authy, or any TOTP-compatible app",
+ "LOADING_QR": "Loading...",
+ "MANUAL_ENTRY": "Can't scan? Enter code manually",
+ "SECRET_KEY": "Secret Key",
+ "COPY": "Copy",
+ "ENTER_CODE": "Enter the 6-digit code from your authenticator app",
+ "ENTER_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify & Continue",
+ "CANCEL": "Cancel",
+ "ERROR_STARTING": "MFA not enabled. Please contact administrator.",
+ "INVALID_CODE": "Invalid verification code",
+ "SECRET_COPIED": "Secret key copied to clipboard",
+ "SUCCESS": "Two-factor authentication has been enabled successfully"
+ },
+ "BACKUP": {
+ "TITLE": "Save Your Backup Codes",
+ "DESCRIPTION": "Keep these codes safe. Each can be used once if you lose access to your authenticator",
+ "IMPORTANT": "Important:",
+ "IMPORTANT_NOTE": " Save these codes in a secure location. You won't be able to see them again.",
+ "DOWNLOAD": "Download",
+ "COPY_ALL": "Copy All",
+ "CONFIRM": "I have saved my backup codes in a secure location and understand that I won't be able to see them again",
+ "COMPLETE_SETUP": "Complete Setup",
+ "CODES_COPIED": "Backup codes copied to clipboard"
+ },
+ "MANAGEMENT": {
+ "BACKUP_CODES": "Backup Codes",
+ "BACKUP_CODES_DESC": "Generate new codes if you've lost or used your existing ones",
+ "REGENERATE": "Regenerate Backup Codes",
+ "DISABLE_MFA": "Disable 2FA",
+ "DISABLE_MFA_DESC": "Remove two-factor authentication from your account",
+ "DISABLE_BUTTON": "Disable Two-Factor Authentication"
+ },
+ "DISABLE": {
+ "TITLE": "Disable Two-Factor Authentication",
+ "DESCRIPTION": "You'll need to enter your password and a verification code to disable two-factor authentication.",
+ "PASSWORD": "Password",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Disable 2FA",
+ "CANCEL": "Cancel",
+ "SUCCESS": "Two-factor authentication has been disabled",
+ "ERROR": "Failed to disable MFA. Please check your credentials."
+ },
+ "REGENERATE": {
+ "TITLE": "Regenerate Backup Codes",
+ "DESCRIPTION": "This will invalidate your existing backup codes and generate new ones. Enter your verification code to continue.",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Generate New Codes",
+ "CANCEL": "Cancel",
+ "NEW_CODES_TITLE": "New Backup Codes Generated",
+ "NEW_CODES_DESC": "Your old backup codes have been invalidated. Save these new codes in a secure location.",
+ "CODES_IMPORTANT": "Important:",
+ "CODES_IMPORTANT_NOTE": " Each code can only be used once. Save them before closing this window.",
+ "DOWNLOAD_CODES": "Download Codes",
+ "COPY_ALL_CODES": "Copy All Codes",
+ "CODES_SAVED": "I've Saved My Codes",
+ "SUCCESS": "New backup codes have been generated",
+ "ERROR": "Failed to regenerate backup codes"
+ }
+ },
+ "MFA_VERIFICATION": {
+ "TITLE": "Two-Factor Authentication",
+ "DESCRIPTION": "Enter your verification code to continue",
+ "AUTHENTICATOR_APP": "Authenticator App",
+ "BACKUP_CODE": "Backup Code",
+ "ENTER_OTP_CODE": "Enter 6-digit code from your authenticator app",
+ "ENTER_BACKUP_CODE": "Enter one of your backup codes",
+ "BACKUP_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify",
+ "TRY_ANOTHER_METHOD": "Try another verification method",
+ "CANCEL_LOGIN": "Cancel and return to login",
+ "HELP_TEXT": "Having trouble signing in?",
+ "LEARN_MORE": "Learn more about 2FA",
+ "HELP_MODAL": {
+ "TITLE": "Two-Factor Authentication Help",
+ "AUTHENTICATOR_TITLE": "Using an Authenticator App",
+ "AUTHENTICATOR_DESC": "Open your authenticator app (Google Authenticator, Authy, etc.) and enter the 6-digit code shown for your account.",
+ "BACKUP_TITLE": "Using a Backup Code",
+ "BACKUP_DESC": "If you don't have access to your authenticator app, you can use one of the backup codes you saved when setting up 2FA. Each code can only be used once.",
+ "CONTACT_TITLE": "Need More Help?",
+ "CONTACT_DESC_CLOUD": "If you've lost access to both your authenticator app and backup codes, please reach out to Chatwoot support for assistance.",
+ "CONTACT_DESC_SELF_HOSTED": "If you've lost access to both your authenticator app and backup codes, please contact your administrator for assistance."
+ },
+ "VERIFICATION_FAILED": "Verification failed. Please try again."
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/hi/settings.json b/app/javascript/dashboard/i18n/locale/hi/settings.json
index 98c3f559b..52f28443b 100644
--- a/app/javascript/dashboard/i18n/locale/hi/settings.json
+++ b/app/javascript/dashboard/i18n/locale/hi/settings.json
@@ -51,6 +51,13 @@
"LARGER": "Larger",
"EXTRA_LARGE": "Extra Large"
}
+ },
+ "LANGUAGE": {
+ "TITLE": "Preferred Language",
+ "NOTE": "Choose the language you want to use.",
+ "UPDATE_SUCCESS": "Your Language settings have been updated successfully",
+ "UPDATE_ERROR": "There is an error while updating the language settings, please try again",
+ "USE_ACCOUNT_DEFAULT": "Use account default"
}
},
"MESSAGE_SIGNATURE_SECTION": {
@@ -73,6 +80,11 @@
"NOTE": "Updating your password would reset your logins in multiple devices.",
"BTN_TEXT": "Change password"
},
+ "SECURITY_SECTION": {
+ "TITLE": "Security",
+ "NOTE": "Manage additional security features for your account.",
+ "MFA_BUTTON": "Manage Two-Factor Authentication"
+ },
"ACCESS_TOKEN": {
"TITLE": "Access Token",
"NOTE": "This token can be used if you are building an API based integration",
@@ -330,6 +342,7 @@
"REPORTS_LABEL": "Labels",
"REPORTS_INBOX": "Inbox",
"REPORTS_TEAM": "Team",
+ "AGENT_ASSIGNMENT": "Agent Assignment",
"SET_AVAILABILITY_TITLE": "Set yourself as",
"SET_YOUR_AVAILABILITY": "Set your availability",
"SLA": "SLA",
@@ -350,7 +363,8 @@
"INFO_TEXT": "Let the system automatically mark you offline when you aren't using the app or dashboard.",
"INFO_SHORT": "Automatically mark offline when you aren't using the app."
},
- "DOCS": "Read docs"
+ "DOCS": "Read docs",
+ "SECURITY": "Security"
},
"BILLING_SETTINGS": {
"TITLE": "Billing",
@@ -382,6 +396,77 @@
},
"NO_BILLING_USER": "Your billing account is being configured. Please refresh the page and try again."
},
+ "SECURITY_SETTINGS": {
+ "TITLE": "Security",
+ "DESCRIPTION": "Manage your account security settings.",
+ "LINK_TEXT": "Learn more about SAML SSO",
+ "SAML": {
+ "TITLE": "SAML SSO",
+ "NOTE": "Configure SAML single sign-on for your account. Users will authenticate through your identity provider instead of using email/password.",
+ "ACS_URL": {
+ "LABEL": "ACS URL",
+ "TOOLTIP": "Assertion Consumer Service URL - Configure this URL in your IdP as the destination for SAML responses"
+ },
+ "SSO_URL": {
+ "LABEL": "SSO URL",
+ "HELP": "The URL where SAML authentication requests will be sent",
+ "PLACEHOLDER": "https://your-idp.com/saml/sso"
+ },
+ "CERTIFICATE": {
+ "LABEL": "Signing certificate in PEM format",
+ "HELP": "The public certificate from your identity provider used to verify SAML responses",
+ "PLACEHOLDER": "-----BEGIN CERTIFICATE-----\nMIIC..."
+ },
+ "FINGERPRINT": {
+ "LABEL": "Fingerprint",
+ "TOOLTIP": "SHA-1 fingerprint of the certificate - Use this to verify the certificate in your IdP configuration"
+ },
+ "COPY_SUCCESS": "Code copied to clipboard successfully",
+ "SP_ENTITY_ID": {
+ "LABEL": "SP Entity ID",
+ "HELP": "Unique identifier for this application as a service provider (auto-generated).",
+ "TOOLTIP": "Unique identifier for Chatwoot as the Service Provider - Configure this in your IdP settings"
+ },
+ "IDP_ENTITY_ID": {
+ "LABEL": "Identity Provider Entity ID",
+ "HELP": "Unique identifier for your identity provider (usually found in IdP configuration)",
+ "PLACEHOLDER": "https://your-idp.com/saml"
+ },
+ "UPDATE_BUTTON": "Update SAML Settings",
+ "API": {
+ "SUCCESS": "SAML settings updated successfully",
+ "ERROR": "Failed to update SAML settings",
+ "ERROR_LOADING": "Failed to load SAML settings",
+ "DISABLED": "SAML settings disabled successfully"
+ },
+ "VALIDATION": {
+ "REQUIRED_FIELDS": "SSO URL, Identity Provider Entity ID, and Certificate are required fields",
+ "SSO_URL_ERROR": "Please enter a valid SSO URL",
+ "CERTIFICATE_ERROR": "Certificate is required",
+ "IDP_ENTITY_ID_ERROR": "Identity Provider Entity ID is required"
+ },
+ "ENTERPRISE_PAYWALL": {
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade to an Enterprise plan to access SAML single sign-on and other advanced security features.",
+ "ASK_ADMIN": "Please reach out to your administrator for the upgrade."
+ },
+ "PAYWALL": {
+ "TITLE": "Upgrade to enable SAML SSO",
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade your plan to get access to SAML single sign-on and other advanced features.",
+ "UPGRADE_NOW": "Upgrade now",
+ "CANCEL_ANYTIME": "You can change or cancel your plan anytime"
+ },
+ "ATTRIBUTE_MAPPING": {
+ "TITLE": "SAML Attribute Setup",
+ "DESCRIPTION": "The following attribute mappings must be configured in your identity provider"
+ },
+ "INFO_SECTION": {
+ "TITLE": "Service Provider Information",
+ "TOOLTIP": "Copy these values and configure them in your Identity Provider to establish the SAML connection"
+ }
+ }
+ },
"CREATE_ACCOUNT": {
"NO_ACCOUNT_WARNING": "Uh oh! We could not find any Chatwoot accounts. Please create a new account to continue.",
"NEW_ACCOUNT": "New Account",
@@ -418,5 +503,255 @@
"SWITCH_TO_REPLY": "Switch to Reply",
"TOGGLE_SNOOZE_DROPDOWN": "Toggle snooze dropdown"
}
+ },
+ "ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent assignment",
+ "DESCRIPTION": "Define policies to effectively manage workload and route conversations based on the needs of inboxes and agents. Learn more here"
+ },
+ "ASSIGNMENT_POLICY": {
+ "TITLE": "Assignment policy",
+ "DESCRIPTION": "Manage how conversations get assigned in inboxes.",
+ "FEATURES": [
+ "Assign by conversations evenly or by available capacity",
+ "Add fair distribution rules to avoid overloading any agent",
+ "Add inboxes to a policy - one policy per inbox"
+ ]
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "TITLE": "Agent capacity policy",
+ "DESCRIPTION": "Manage workload for agents.",
+ "FEATURES": [
+ "Define maximum conversations per inbox",
+ "Create exceptions based on labels and time",
+ "Add agents to a policy - one policy per agent"
+ ]
+ }
+ },
+ "AGENT_ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Assignment policy",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "ORDER": "Order",
+ "PRIORITY": "Priority",
+ "ACTIVE": "Active",
+ "INACTIVE": "Inactive",
+ "POPOVER": "Added inboxes",
+ "EDIT": "Edit"
+ },
+ "NO_RECORDS_FOUND": "No assignment policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create assignment policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy created successfully",
+ "ERROR_MESSAGE": "Failed to create assignment policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit assignment policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_INBOX_DIALOG": {
+ "TITLE": "Add inbox",
+ "DESCRIPTION": "{inboxName} inbox is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Cancel"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update assignment policy"
+ },
+ "INBOX_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Inbox added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Inbox removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove inbox from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Description:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "STATUS": {
+ "LABEL": "Status:",
+ "PLACEHOLDER": "Select status",
+ "ACTIVE": "Policy is active",
+ "INACTIVE": "Policy is inactive"
+ },
+ "ASSIGNMENT_ORDER": {
+ "LABEL": "Assignment order",
+ "ROUND_ROBIN": {
+ "LABEL": "Round robin",
+ "DESCRIPTION": "Assign conversations evenly among agents."
+ },
+ "BALANCED": {
+ "LABEL": "Balanced",
+ "DESCRIPTION": "Assign conversations based on available capacity."
+ }
+ },
+ "ASSIGNMENT_PRIORITY": {
+ "LABEL": "Assignment priority",
+ "EARLIEST_CREATED": {
+ "LABEL": "Earliest created",
+ "DESCRIPTION": "The conversation that was created first gets assigned first."
+ },
+ "LONGEST_WAITING": {
+ "LABEL": "Longest waiting",
+ "DESCRIPTION": "The conversation waiting the longest gets assigned first."
+ }
+ },
+ "FAIR_DISTRIBUTION": {
+ "LABEL": "Fair distribution policy",
+ "DESCRIPTION": "Set the maximum number of conversations that can be assigned per agent within a time window to avoid overloading any one agent. This required field defaults to 100 conversations per hour.",
+ "INPUT_MAX": "Assign max",
+ "DURATION": "Conversations per agent in every"
+ },
+ "INBOXES": {
+ "LABEL": "Added inboxes",
+ "DESCRIPTION": "Add inboxes for which this policy will be applicable.",
+ "ADD_BUTTON": "Add inbox",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select inboxes to add",
+ "ADD_BUTTON": "Add"
+ },
+ "EMPTY_STATE": "No inboxes added to this policy, add an inbox to get started",
+ "API": {
+ "SUCCESS_MESSAGE": "Inbox successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Assignment policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete assignment policy"
+ }
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent capacity",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "POPOVER": "Added agents",
+ "EDIT": "Edit"
+ },
+ "NO_RECORDS_FOUND": "No agent capacity policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create agent capacity policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy created successfully",
+ "ERROR_MESSAGE": "Failed to create agent capacity policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit agent capacity policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_AGENT_DIALOG": {
+ "TITLE": "Add agent",
+ "DESCRIPTION": "{agentName} is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Cancel"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update agent capacity policy"
+ },
+ "AGENT_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Agent added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Agent removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove agent from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Description:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "INBOX_CAPACITY_LIMIT": {
+ "LABEL": "Inbox capacity limits",
+ "ADD_BUTTON": "Add inbox",
+ "FIELD": {
+ "SELECT_INBOX": "Select inbox",
+ "MAX_CONVERSATIONS": "Max conversations",
+ "SET_LIMIT": "Set limit"
+ },
+ "EMPTY_STATE": "No inbox limit set"
+ },
+ "EXCLUSION_RULES": {
+ "LABEL": "Exclusion rules",
+ "DESCRIPTION": "Conversations that satisfy the following conditions would not count towards agent capacity",
+ "TAGS": {
+ "LABEL": "Exclude conversations tagged with specific labels",
+ "ADD_TAG": "add tag",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select tags to add"
+ },
+ "EMPTY_STATE": "No tags added to this policy."
+ },
+ "DURATION": {
+ "LABEL": "Exclude conversations older than a specified duration",
+ "PLACEHOLDER": "Set time"
+ }
+ },
+ "USERS": {
+ "LABEL": "Assigned agents",
+ "DESCRIPTION": "Add agents for which this policy will be applicable.",
+ "ADD_BUTTON": "Add agent",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select agents to add",
+ "ADD_BUTTON": "Add"
+ },
+ "EMPTY_STATE": "No agents added",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Agent capacity policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete agent capacity policy"
+ }
+ },
+ "DELETE_POLICY": {
+ "TITLE": "Delete policy",
+ "DESCRIPTION": "Are you sure you want to delete this policy? This action cannot be undone.",
+ "CONFIRM_BUTTON_LABEL": "Delete",
+ "CANCEL_BUTTON_LABEL": "Cancel"
+ }
}
}
diff --git a/app/javascript/dashboard/i18n/locale/hi/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/hi/whatsappTemplates.json
index 5f53faaa8..cf28312dc 100644
--- a/app/javascript/dashboard/i18n/locale/hi/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/hi/whatsappTemplates.json
@@ -40,6 +40,7 @@
"BUTTON_LABEL": "Button {index}",
"COUPON_CODE": "Enter coupon code (max 15 chars)",
"MEDIA_URL_LABEL": "Enter {type} URL",
+ "DOCUMENT_NAME_PLACEHOLDER": "Enter document filename (e.g., Invoice_2025.pdf)",
"BUTTON_PARAMETER": "Enter button parameter"
}
}
diff --git a/app/javascript/dashboard/i18n/locale/hr/automation.json b/app/javascript/dashboard/i18n/locale/hr/automation.json
index 65722bbf2..6172de951 100644
--- a/app/javascript/dashboard/i18n/locale/hr/automation.json
+++ b/app/javascript/dashboard/i18n/locale/hr/automation.json
@@ -177,7 +177,8 @@
"REFERER_LINK": "Referrer Link",
"ASSIGNEE_NAME": "Assignee",
"TEAM_NAME": "Tim",
- "PRIORITY": "Prioritet"
+ "PRIORITY": "Prioritet",
+ "LABELS": "Labels"
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/hr/contact.json b/app/javascript/dashboard/i18n/locale/hr/contact.json
index 86d1592e5..dcd7a88fe 100644
--- a/app/javascript/dashboard/i18n/locale/hr/contact.json
+++ b/app/javascript/dashboard/i18n/locale/hr/contact.json
@@ -610,6 +610,15 @@
"SEND_MESSAGE": "Send message"
}
},
+ "TWILIO_OPTIONS": {
+ "LABEL": "Select template",
+ "SEARCH_PLACEHOLDER": "Search templates",
+ "EMPTY_STATE": "No templates found",
+ "TEMPLATE_PARSER": {
+ "BACK": "Go back",
+ "SEND_MESSAGE": "Send message"
+ }
+ },
"ACTION_BUTTONS": {
"DISCARD": "Discard",
"SEND": "Send ({keyCode})"
diff --git a/app/javascript/dashboard/i18n/locale/hr/contactFilters.json b/app/javascript/dashboard/i18n/locale/hr/contactFilters.json
index cc3ac65e2..7ebd11837 100644
--- a/app/javascript/dashboard/i18n/locale/hr/contactFilters.json
+++ b/app/javascript/dashboard/i18n/locale/hr/contactFilters.json
@@ -48,7 +48,8 @@
"CREATED_AT": "Created At",
"LAST_ACTIVITY": "Last Activity",
"REFERER_LINK": "Referrer link",
- "BLOCKED": "Blocked"
+ "BLOCKED": "Blocked",
+ "LABELS": "Labels"
},
"GROUPS": {
"STANDARD_FILTERS": "Standard Filters",
diff --git a/app/javascript/dashboard/i18n/locale/hr/contentTemplates.json b/app/javascript/dashboard/i18n/locale/hr/contentTemplates.json
new file mode 100644
index 000000000..d923b0254
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/hr/contentTemplates.json
@@ -0,0 +1,51 @@
+{
+ "CONTENT_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Twilio Templates",
+ "SUBTITLE": "Select the Twilio template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Pretraži Predloške",
+ "NO_TEMPLATES_FOUND": "Nije pronađen predložak za",
+ "NO_CONTENT": "No content",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Kategorija",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No Twilio templates available. Click refresh to sync templates from Twilio.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Jezik",
+ "TEMPLATE_BODY": "Tijelo predloška",
+ "CATEGORY": "Kategorija"
+ },
+ "TYPES": {
+ "MEDIA": "Media",
+ "QUICK_REPLY": "Quick Reply",
+ "TEXT": "Tekst"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Varijable",
+ "LANGUAGE": "Jezik",
+ "CATEGORY": "Kategorija",
+ "VARIABLE_PLACEHOLDER": "Unesi {variable} vrijednost",
+ "GO_BACK_LABEL": "Nazad",
+ "SEND_MESSAGE_LABEL": "Šalji poruku",
+ "FORM_ERROR_MESSAGE": "Popuniti sve varijable prije slanja",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "MEDIA_URL_LABEL": "Enter full media URL",
+ "MEDIA_URL_PLACEHOLDER": "https://example.com/image.jpg"
+ },
+ "FORM": {
+ "BACK_BUTTON": "Back",
+ "SEND_MESSAGE_BUTTON": "Šalji poruku"
+ }
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/hr/conversation.json b/app/javascript/dashboard/i18n/locale/hr/conversation.json
index 95bbd6f14..e20527813 100644
--- a/app/javascript/dashboard/i18n/locale/hr/conversation.json
+++ b/app/javascript/dashboard/i18n/locale/hr/conversation.json
@@ -35,6 +35,11 @@
"API_HOURS_WINDOW": "You can only reply to this conversation within {hours} hours",
"NOT_ASSIGNED_TO_YOU": "This conversation is not assigned to you. Would you like to assign this conversation to yourself?",
"ASSIGN_TO_ME": "Assign to me",
+ "BOT_HANDOFF_MESSAGE": "You are responding to a conversation which is currently handled by an assistant or a bot.",
+ "BOT_HANDOFF_ACTION": "Mark open and assign to you",
+ "BOT_HANDOFF_REOPEN_ACTION": "Mark conversation open",
+ "BOT_HANDOFF_SUCCESS": "Conversation has been handed over to you",
+ "BOT_HANDOFF_ERROR": "Failed to take over the conversation. Please try again.",
"TWILIO_WHATSAPP_CAN_REPLY": "You can only reply to this conversation using a template message due to",
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "24 hour message window restriction",
"OLD_INSTAGRAM_INBOX_REPLY_BANNER": "This Instagram account was migrated to the new Instagram channel inbox. All new messages will show up there. You won’t be able to send messages from this conversation anymore.",
@@ -66,6 +71,17 @@
"SHOW_LABELS": "Show labels",
"HIDE_LABELS": "Hide labels"
},
+ "VOICE_CALL": {
+ "INCOMING_CALL": "Incoming call",
+ "OUTGOING_CALL": "Outgoing call",
+ "CALL_IN_PROGRESS": "Call in progress",
+ "NO_ANSWER": "No answer",
+ "MISSED_CALL": "Missed call",
+ "CALL_ENDED": "Call ended",
+ "NOT_ANSWERED_YET": "Not answered yet",
+ "THEY_ANSWERED": "They answered",
+ "YOU_ANSWERED": "You answered"
+ },
"HEADER": {
"RESOLVE_ACTION": "Resolve",
"REOPEN_ACTION": "Reopen",
diff --git a/app/javascript/dashboard/i18n/locale/hr/helpCenter.json b/app/javascript/dashboard/i18n/locale/hr/helpCenter.json
index 68c6c40f1..77200354c 100644
--- a/app/javascript/dashboard/i18n/locale/hr/helpCenter.json
+++ b/app/javascript/dashboard/i18n/locale/hr/helpCenter.json
@@ -741,7 +741,8 @@
"LIVE_CHAT_WIDGET": {
"LABEL": "Live chat widget",
"PLACEHOLDER": "Select live chat widget",
- "HELP_TEXT": "Select a live chat widget that will appear on your help center"
+ "HELP_TEXT": "Select a live chat widget that will appear on your help center",
+ "NONE_OPTION": "No widget"
},
"BRAND_COLOR": {
"LABEL": "Brand color"
@@ -807,6 +808,58 @@
"ERROR_MESSAGE": "Unable to update portal"
}
}
+ },
+ "PDF_UPLOAD": {
+ "TITLE": "Upload PDF Document",
+ "DESCRIPTION": "Upload a PDF document to automatically generate FAQs using AI",
+ "DRAG_DROP_TEXT": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "ADDITIONAL_CONTEXT_LABEL": "Additional Context (Optional)",
+ "ADDITIONAL_CONTEXT_PLACEHOLDER": "Provide any additional context or instructions for FAQ generation...",
+ "UPLOADING": "Prenosim...",
+ "UPLOAD": "Upload & Process",
+ "CANCEL": "Odustani",
+ "ERROR_INVALID_TYPE": "Please select a valid PDF file",
+ "ERROR_FILE_TOO_LARGE": "File size must be less than 512MB",
+ "ERROR_UPLOAD_FAILED": "Failed to upload PDF. Please try again."
+ },
+ "PDF_DOCUMENTS": {
+ "TITLE": "PDF Documents",
+ "DESCRIPTION": "Manage uploaded PDF documents and generate FAQs from them",
+ "UPLOAD_PDF": "Upload PDF",
+ "UPLOAD_FIRST_PDF": "Upload your first PDF",
+ "UPLOADED_BY": "Uploaded by",
+ "GENERATE_FAQS": "Generate FAQs",
+ "GENERATING": "Generiranje...",
+ "CONFIRM_DELETE": "Are you sure you want to delete {filename}?",
+ "EMPTY_STATE": {
+ "TITLE": "No PDF documents yet",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQs using AI"
+ },
+ "STATUS": {
+ "UPLOADED": "Ready",
+ "PROCESSING": "Processing",
+ "PROCESSED": "Completed",
+ "FAILED": "Failed"
+ }
+ },
+ "CONTENT_GENERATION": {
+ "TITLE": "Content Generation",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQ content using AI",
+ "UPLOAD_TITLE": "Upload PDF Document",
+ "DRAG_DROP": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "UPLOADING": "Processing document...",
+ "UPLOAD_SUCCESS": "Document processed successfully!",
+ "UPLOAD_ERROR": "Failed to upload document. Please try again.",
+ "INVALID_FILE_TYPE": "Please select a valid PDF file",
+ "FILE_TOO_LARGE": "File size must be less than 512MB",
+ "GENERATED_CONTENT": "Generated FAQ Content",
+ "PUBLISH_SELECTED": "Publish Selected",
+ "PUBLISHING": "Publishing...",
+ "FROM_DOCUMENT": "From document",
+ "NO_CONTENT": "No generated content available. Upload a PDF document to get started.",
+ "LOADING": "Loading generated content..."
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/hr/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/hr/inboxMgmt.json
index 9bb35d8b6..298158c5b 100644
--- a/app/javascript/dashboard/i18n/locale/hr/inboxMgmt.json
+++ b/app/javascript/dashboard/i18n/locale/hr/inboxMgmt.json
@@ -272,8 +272,8 @@
},
"SUBMIT_BUTTON": "Create WhatsApp Channel",
"EMBEDDED_SIGNUP": {
- "TITLE": "Quick Setup with Meta",
- "DESC": "You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
+ "TITLE": "Quick setup with Meta",
+ "DESC": "Use the WhatsApp Embedded Signup flow to quickly connect new numbers. You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
"BENEFITS": {
"TITLE": "Benefits of Embedded Signup:",
"EASY_SETUP": "No manual configuration required",
@@ -281,9 +281,8 @@
"AUTO_CONFIG": "Automatic webhook and phone number configuration"
},
"LEARN_MORE": {
- "TEXT": "To learn more about integrated signup, pricing, and limitations, visit",
- "LINK_TEXT": "this link.",
- "LINK_URL": "https://developers.facebook.com/docs/whatsapp/embedded-signup/custom-flows/onboarding-business-app-users#limitations"
+ "TEXT": "To learn more about integrated signup, pricing, and limitations, visit {link}.",
+ "LINK_TEXT": "this link"
},
"SUBMIT_BUTTON": "Connect with WhatsApp Business",
"AUTH_PROCESSING": "Authenticating with Meta",
@@ -296,7 +295,9 @@
"INVALID_BUSINESS_DATA": "Invalid business data received from Facebook. Please try again.",
"SIGNUP_ERROR": "Signup error occurred",
"AUTH_NOT_COMPLETED": "Authentication not completed. Please restart the process.",
- "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured"
+ "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured",
+ "MANUAL_FALLBACK": "If your number is already connected to the WhatsApp Business Platform (API), or if you’re a tech provider onboarding your own number, please use the {link} flow",
+ "MANUAL_LINK_TEXT": "manual setup flow"
},
"API": {
"ERROR_MESSAGE": "We were not able to save the WhatsApp channel"
@@ -424,7 +425,51 @@
},
"AUTH": {
"TITLE": "Choose a channel",
- "DESC": "Chatwoot supports live-chat widgets, Facebook Messenger, Twitter profiles, WhatsApp, Emails, etc., as channels. If you want to build a custom channel, you can create it using the API channel. To get started, choose one of the channels below."
+ "DESC": "Chatwoot supports live-chat widgets, Facebook Messenger, Twitter profiles, WhatsApp, Emails, etc., as channels. If you want to build a custom channel, you can create it using the API channel. To get started, choose one of the channels below.",
+ "TITLE_NEXT": "Complete the setup",
+ "TITLE_FINISH": "Voilà!",
+ "CHANNEL": {
+ "WEBSITE": {
+ "TITLE": "Website",
+ "DESCRIPTION": "Create a live-chat widget"
+ },
+ "FACEBOOK": {
+ "TITLE": "Facebook",
+ "DESCRIPTION": "Connect your Facebook page"
+ },
+ "WHATSAPP": {
+ "TITLE": "WhatsApp",
+ "DESCRIPTION": "Support your customers on WhatsApp"
+ },
+ "EMAIL": {
+ "TITLE": "Email",
+ "DESCRIPTION": "Connect with Gmail, Outlook, or other providers"
+ },
+ "SMS": {
+ "TITLE": "SMS",
+ "DESCRIPTION": "Integrate SMS channel with Twilio or bandwidth"
+ },
+ "API": {
+ "TITLE": "API",
+ "DESCRIPTION": "Make a custom channel using our API"
+ },
+ "TELEGRAM": {
+ "TITLE": "Telegram",
+ "DESCRIPTION": "Configure Telegram channel using Bot token"
+ },
+ "LINE": {
+ "TITLE": "Line",
+ "DESCRIPTION": "Integrate your Line channel"
+ },
+ "INSTAGRAM": {
+ "TITLE": "Instagram",
+ "DESCRIPTION": "Connect your instagram account"
+ },
+ "VOICE": {
+ "TITLE": "Voice",
+ "DESCRIPTION": "Integrate with Twilio Voice"
+ }
+ }
},
"AGENTS": {
"TITLE": "Agenti",
@@ -478,7 +523,10 @@
"MESSAGE": "You can now engage with your customers through your new Channel. Happy supporting",
"BUTTON_TEXT": "Take me there",
"MORE_SETTINGS": "More settings",
- "WEBSITE_SUCCESS": "You have successfully finished creating a website channel. Copy the code shown below and paste it on your website. Next time a customer use the live chat, the conversation will automatically appear on your inbox."
+ "WEBSITE_SUCCESS": "You have successfully finished creating a website channel. Copy the code shown below and paste it on your website. Next time a customer use the live chat, the conversation will automatically appear on your inbox.",
+ "WHATSAPP_QR_INSTRUCTION": "Scan the QR code above to quickly test your WhatsApp inbox",
+ "MESSENGER_QR_INSTRUCTION": "Scan the QR code above to quickly test your Facebook Messenger inbox",
+ "TELEGRAM_QR_INSTRUCTION": "Scan the QR code above to quickly test your Telegram inbox"
},
"REAUTH": "Reauthorize",
"VIEW": "View",
@@ -868,9 +916,18 @@
"SCRIPT_SETTINGS": "\n window.chatwootSettings = {options};"
},
"EMAIL_PROVIDERS": {
- "MICROSOFT": "Microsoft",
- "GOOGLE": "Google",
- "OTHER_PROVIDERS": "Other Providers"
+ "MICROSOFT": {
+ "TITLE": "Microsoft",
+ "DESCRIPTION": "Connect with Microsoft"
+ },
+ "GOOGLE": {
+ "TITLE": "Google",
+ "DESCRIPTION": "Connect with Google"
+ },
+ "OTHER_PROVIDERS": {
+ "TITLE": "Other Providers",
+ "DESCRIPTION": "Connect with Other Providers"
+ }
},
"CHANNELS": {
"MESSENGER": "Messenger",
diff --git a/app/javascript/dashboard/i18n/locale/hr/integrations.json b/app/javascript/dashboard/i18n/locale/hr/integrations.json
index 09eb8edaa..d73281dbb 100644
--- a/app/javascript/dashboard/i18n/locale/hr/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/hr/integrations.json
@@ -704,11 +704,28 @@
"ERROR_MESSAGE": "There was an error creating the document, please try again."
},
"FORM": {
+ "TYPE": {
+ "LABEL": "Document Type",
+ "URL": "URL",
+ "PDF": "PDF File"
+ },
"URL": {
"LABEL": "URL",
"PLACEHOLDER": "Enter the URL of the document",
"ERROR": "Please provide a valid URL for the document"
},
+ "PDF_FILE": {
+ "LABEL": "PDF File",
+ "CHOOSE_FILE": "Choose PDF file",
+ "ERROR": "Please select a PDF file",
+ "HELP_TEXT": "Maximum file size: 10MB",
+ "INVALID_TYPE": "Please select a valid PDF file",
+ "TOO_LARGE": "File size exceeds 10MB limit"
+ },
+ "NAME": {
+ "LABEL": "Document Name (Optional)",
+ "PLACEHOLDER": "Enter a name for the document"
+ },
"ASSISTANT": {
"LABEL": "Assistant",
"PLACEHOLDER": "Select the assistant",
@@ -744,6 +761,7 @@
"SELECTED": "{count} selected",
"SELECT_ALL": "Select all ({count})",
"UNSELECT_ALL": "Unselect all ({count})",
+ "SEARCH_PLACEHOLDER": "Search FAQs...",
"BULK_APPROVE_BUTTON": "Approve",
"BULK_DELETE_BUTTON": "Izbriši",
"BULK_APPROVE": {
diff --git a/app/javascript/dashboard/i18n/locale/hr/mfa.json b/app/javascript/dashboard/i18n/locale/hr/mfa.json
new file mode 100644
index 000000000..6ecba6b70
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/hr/mfa.json
@@ -0,0 +1,106 @@
+{
+ "MFA_SETTINGS": {
+ "TITLE": "Two-Factor Authentication",
+ "SUBTITLE": "Secure your account with TOTP-based authentication",
+ "DESCRIPTION": "Add an extra layer of security to your account using a time-based one-time password (TOTP)",
+ "STATUS_TITLE": "Authentication Status",
+ "STATUS_DESCRIPTION": "Manage your two-factor authentication settings and backup recovery codes",
+ "ENABLED": "Enabled",
+ "DISABLED": "Disabled",
+ "STATUS_ENABLED": "Two-factor authentication is active",
+ "STATUS_ENABLED_DESC": "Your account is protected with an additional layer of security",
+ "ENABLE_BUTTON": "Enable Two-Factor Authentication",
+ "ENHANCE_SECURITY": "Enhance Your Account Security",
+ "ENHANCE_SECURITY_DESC": "Two-factor authentication adds an extra layer of security by requiring a verification code from your authenticator app in addition to your password.",
+ "SETUP": {
+ "STEP_NUMBER_1": "1",
+ "STEP_NUMBER_2": "2",
+ "STEP1_TITLE": "Scan QR Code with Your Authenticator App",
+ "STEP1_DESCRIPTION": "Use Google Authenticator, Authy, or any TOTP-compatible app",
+ "LOADING_QR": "Loading...",
+ "MANUAL_ENTRY": "Can't scan? Enter code manually",
+ "SECRET_KEY": "Secret Key",
+ "COPY": "Copy",
+ "ENTER_CODE": "Enter the 6-digit code from your authenticator app",
+ "ENTER_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify & Continue",
+ "CANCEL": "Odustani",
+ "ERROR_STARTING": "MFA not enabled. Please contact administrator.",
+ "INVALID_CODE": "Invalid verification code",
+ "SECRET_COPIED": "Secret key copied to clipboard",
+ "SUCCESS": "Two-factor authentication has been enabled successfully"
+ },
+ "BACKUP": {
+ "TITLE": "Save Your Backup Codes",
+ "DESCRIPTION": "Keep these codes safe. Each can be used once if you lose access to your authenticator",
+ "IMPORTANT": "Important:",
+ "IMPORTANT_NOTE": " Save these codes in a secure location. You won't be able to see them again.",
+ "DOWNLOAD": "Download",
+ "COPY_ALL": "Copy All",
+ "CONFIRM": "I have saved my backup codes in a secure location and understand that I won't be able to see them again",
+ "COMPLETE_SETUP": "Complete Setup",
+ "CODES_COPIED": "Backup codes copied to clipboard"
+ },
+ "MANAGEMENT": {
+ "BACKUP_CODES": "Backup Codes",
+ "BACKUP_CODES_DESC": "Generate new codes if you've lost or used your existing ones",
+ "REGENERATE": "Regenerate Backup Codes",
+ "DISABLE_MFA": "Disable 2FA",
+ "DISABLE_MFA_DESC": "Remove two-factor authentication from your account",
+ "DISABLE_BUTTON": "Disable Two-Factor Authentication"
+ },
+ "DISABLE": {
+ "TITLE": "Disable Two-Factor Authentication",
+ "DESCRIPTION": "You'll need to enter your password and a verification code to disable two-factor authentication.",
+ "PASSWORD": "Password",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Disable 2FA",
+ "CANCEL": "Odustani",
+ "SUCCESS": "Two-factor authentication has been disabled",
+ "ERROR": "Failed to disable MFA. Please check your credentials."
+ },
+ "REGENERATE": {
+ "TITLE": "Regenerate Backup Codes",
+ "DESCRIPTION": "This will invalidate your existing backup codes and generate new ones. Enter your verification code to continue.",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Generate New Codes",
+ "CANCEL": "Odustani",
+ "NEW_CODES_TITLE": "New Backup Codes Generated",
+ "NEW_CODES_DESC": "Your old backup codes have been invalidated. Save these new codes in a secure location.",
+ "CODES_IMPORTANT": "Important:",
+ "CODES_IMPORTANT_NOTE": " Each code can only be used once. Save them before closing this window.",
+ "DOWNLOAD_CODES": "Download Codes",
+ "COPY_ALL_CODES": "Copy All Codes",
+ "CODES_SAVED": "I've Saved My Codes",
+ "SUCCESS": "New backup codes have been generated",
+ "ERROR": "Failed to regenerate backup codes"
+ }
+ },
+ "MFA_VERIFICATION": {
+ "TITLE": "Two-Factor Authentication",
+ "DESCRIPTION": "Enter your verification code to continue",
+ "AUTHENTICATOR_APP": "Authenticator App",
+ "BACKUP_CODE": "Backup Code",
+ "ENTER_OTP_CODE": "Enter 6-digit code from your authenticator app",
+ "ENTER_BACKUP_CODE": "Enter one of your backup codes",
+ "BACKUP_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify",
+ "TRY_ANOTHER_METHOD": "Try another verification method",
+ "CANCEL_LOGIN": "Cancel and return to login",
+ "HELP_TEXT": "Having trouble signing in?",
+ "LEARN_MORE": "Learn more about 2FA",
+ "HELP_MODAL": {
+ "TITLE": "Two-Factor Authentication Help",
+ "AUTHENTICATOR_TITLE": "Using an Authenticator App",
+ "AUTHENTICATOR_DESC": "Open your authenticator app (Google Authenticator, Authy, etc.) and enter the 6-digit code shown for your account.",
+ "BACKUP_TITLE": "Using a Backup Code",
+ "BACKUP_DESC": "If you don't have access to your authenticator app, you can use one of the backup codes you saved when setting up 2FA. Each code can only be used once.",
+ "CONTACT_TITLE": "Need More Help?",
+ "CONTACT_DESC_CLOUD": "If you've lost access to both your authenticator app and backup codes, please reach out to Chatwoot support for assistance.",
+ "CONTACT_DESC_SELF_HOSTED": "If you've lost access to both your authenticator app and backup codes, please contact your administrator for assistance."
+ },
+ "VERIFICATION_FAILED": "Verification failed. Please try again."
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/hr/settings.json b/app/javascript/dashboard/i18n/locale/hr/settings.json
index 03ff834ba..4c9d58c66 100644
--- a/app/javascript/dashboard/i18n/locale/hr/settings.json
+++ b/app/javascript/dashboard/i18n/locale/hr/settings.json
@@ -51,6 +51,13 @@
"LARGER": "Larger",
"EXTRA_LARGE": "Extra Large"
}
+ },
+ "LANGUAGE": {
+ "TITLE": "Preferred Language",
+ "NOTE": "Choose the language you want to use.",
+ "UPDATE_SUCCESS": "Your Language settings have been updated successfully",
+ "UPDATE_ERROR": "There is an error while updating the language settings, please try again",
+ "USE_ACCOUNT_DEFAULT": "Use account default"
}
},
"MESSAGE_SIGNATURE_SECTION": {
@@ -73,6 +80,11 @@
"NOTE": "Updating your password would reset your logins in multiple devices.",
"BTN_TEXT": "Promijeni lozinku"
},
+ "SECURITY_SECTION": {
+ "TITLE": "Security",
+ "NOTE": "Manage additional security features for your account.",
+ "MFA_BUTTON": "Manage Two-Factor Authentication"
+ },
"ACCESS_TOKEN": {
"TITLE": "Pristupni token",
"NOTE": "This token can be used if you are building an API based integration",
@@ -330,6 +342,7 @@
"REPORTS_LABEL": "Labels",
"REPORTS_INBOX": "Inbox",
"REPORTS_TEAM": "Tim",
+ "AGENT_ASSIGNMENT": "Agent Assignment",
"SET_AVAILABILITY_TITLE": "Postavi sebe kao",
"SET_YOUR_AVAILABILITY": "Set your availability",
"SLA": "SLA",
@@ -350,7 +363,8 @@
"INFO_TEXT": "Let the system automatically mark you offline when you aren't using the app or dashboard.",
"INFO_SHORT": "Automatically mark offline when you aren't using the app."
},
- "DOCS": "Pročitaj članke"
+ "DOCS": "Pročitaj članke",
+ "SECURITY": "Security"
},
"BILLING_SETTINGS": {
"TITLE": "Naplata",
@@ -382,6 +396,77 @@
},
"NO_BILLING_USER": "Your billing account is being configured. Please refresh the page and try again."
},
+ "SECURITY_SETTINGS": {
+ "TITLE": "Security",
+ "DESCRIPTION": "Manage your account security settings.",
+ "LINK_TEXT": "Learn more about SAML SSO",
+ "SAML": {
+ "TITLE": "SAML SSO",
+ "NOTE": "Configure SAML single sign-on for your account. Users will authenticate through your identity provider instead of using email/password.",
+ "ACS_URL": {
+ "LABEL": "ACS URL",
+ "TOOLTIP": "Assertion Consumer Service URL - Configure this URL in your IdP as the destination for SAML responses"
+ },
+ "SSO_URL": {
+ "LABEL": "SSO URL",
+ "HELP": "The URL where SAML authentication requests will be sent",
+ "PLACEHOLDER": "https://your-idp.com/saml/sso"
+ },
+ "CERTIFICATE": {
+ "LABEL": "Signing certificate in PEM format",
+ "HELP": "The public certificate from your identity provider used to verify SAML responses",
+ "PLACEHOLDER": "-----BEGIN CERTIFICATE-----\nMIIC..."
+ },
+ "FINGERPRINT": {
+ "LABEL": "Fingerprint",
+ "TOOLTIP": "SHA-1 fingerprint of the certificate - Use this to verify the certificate in your IdP configuration"
+ },
+ "COPY_SUCCESS": "Copied to clipboard",
+ "SP_ENTITY_ID": {
+ "LABEL": "SP Entity ID",
+ "HELP": "Unique identifier for this application as a service provider (auto-generated).",
+ "TOOLTIP": "Unique identifier for Chatwoot as the Service Provider - Configure this in your IdP settings"
+ },
+ "IDP_ENTITY_ID": {
+ "LABEL": "Identity Provider Entity ID",
+ "HELP": "Unique identifier for your identity provider (usually found in IdP configuration)",
+ "PLACEHOLDER": "https://your-idp.com/saml"
+ },
+ "UPDATE_BUTTON": "Update SAML Settings",
+ "API": {
+ "SUCCESS": "SAML settings updated successfully",
+ "ERROR": "Failed to update SAML settings",
+ "ERROR_LOADING": "Failed to load SAML settings",
+ "DISABLED": "SAML settings disabled successfully"
+ },
+ "VALIDATION": {
+ "REQUIRED_FIELDS": "SSO URL, Identity Provider Entity ID, and Certificate are required fields",
+ "SSO_URL_ERROR": "Please enter a valid SSO URL",
+ "CERTIFICATE_ERROR": "Certificate is required",
+ "IDP_ENTITY_ID_ERROR": "Identity Provider Entity ID is required"
+ },
+ "ENTERPRISE_PAYWALL": {
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade to an Enterprise plan to access SAML single sign-on and other advanced security features.",
+ "ASK_ADMIN": "Please reach out to your administrator for the upgrade."
+ },
+ "PAYWALL": {
+ "TITLE": "Upgrade to enable SAML SSO",
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade your plan to get access to SAML single sign-on and other advanced features.",
+ "UPGRADE_NOW": "Upgrade now",
+ "CANCEL_ANYTIME": "You can change or cancel your plan anytime"
+ },
+ "ATTRIBUTE_MAPPING": {
+ "TITLE": "SAML Attribute Setup",
+ "DESCRIPTION": "The following attribute mappings must be configured in your identity provider"
+ },
+ "INFO_SECTION": {
+ "TITLE": "Service Provider Information",
+ "TOOLTIP": "Copy these values and configure them in your Identity Provider to establish the SAML connection"
+ }
+ }
+ },
"CREATE_ACCOUNT": {
"NO_ACCOUNT_WARNING": "Uh oh! We could not find any Chatwoot accounts. Please create a new account to continue.",
"NEW_ACCOUNT": "Novi račun",
@@ -418,5 +503,255 @@
"SWITCH_TO_REPLY": "Switch to Reply",
"TOGGLE_SNOOZE_DROPDOWN": "Toggle snooze dropdown"
}
+ },
+ "ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent assignment",
+ "DESCRIPTION": "Define policies to effectively manage workload and route conversations based on the needs of inboxes and agents. Learn more here"
+ },
+ "ASSIGNMENT_POLICY": {
+ "TITLE": "Assignment policy",
+ "DESCRIPTION": "Manage how conversations get assigned in inboxes.",
+ "FEATURES": [
+ "Assign by conversations evenly or by available capacity",
+ "Add fair distribution rules to avoid overloading any agent",
+ "Add inboxes to a policy - one policy per inbox"
+ ]
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "TITLE": "Agent capacity policy",
+ "DESCRIPTION": "Manage workload for agents.",
+ "FEATURES": [
+ "Define maximum conversations per inbox",
+ "Create exceptions based on labels and time",
+ "Add agents to a policy - one policy per agent"
+ ]
+ }
+ },
+ "AGENT_ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Assignment policy",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "ORDER": "Order",
+ "PRIORITY": "Prioritet",
+ "ACTIVE": "Active",
+ "INACTIVE": "Inactive",
+ "POPOVER": "Added inboxes",
+ "EDIT": "Uredi"
+ },
+ "NO_RECORDS_FOUND": "No assignment policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create assignment policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy created successfully",
+ "ERROR_MESSAGE": "Failed to create assignment policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit assignment policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_INBOX_DIALOG": {
+ "TITLE": "Add inbox",
+ "DESCRIPTION": "{inboxName} inbox is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Odustani"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update assignment policy"
+ },
+ "INBOX_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Inbox added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Inbox removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove inbox from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Description:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "STATUS": {
+ "LABEL": "Status:",
+ "PLACEHOLDER": "Select status",
+ "ACTIVE": "Policy is active",
+ "INACTIVE": "Policy is inactive"
+ },
+ "ASSIGNMENT_ORDER": {
+ "LABEL": "Assignment order",
+ "ROUND_ROBIN": {
+ "LABEL": "Round robin",
+ "DESCRIPTION": "Assign conversations evenly among agents."
+ },
+ "BALANCED": {
+ "LABEL": "Balanced",
+ "DESCRIPTION": "Assign conversations based on available capacity."
+ }
+ },
+ "ASSIGNMENT_PRIORITY": {
+ "LABEL": "Assignment priority",
+ "EARLIEST_CREATED": {
+ "LABEL": "Earliest created",
+ "DESCRIPTION": "The conversation that was created first gets assigned first."
+ },
+ "LONGEST_WAITING": {
+ "LABEL": "Longest waiting",
+ "DESCRIPTION": "The conversation waiting the longest gets assigned first."
+ }
+ },
+ "FAIR_DISTRIBUTION": {
+ "LABEL": "Fair distribution policy",
+ "DESCRIPTION": "Set the maximum number of conversations that can be assigned per agent within a time window to avoid overloading any one agent. This required field defaults to 100 conversations per hour.",
+ "INPUT_MAX": "Assign max",
+ "DURATION": "Conversations per agent in every"
+ },
+ "INBOXES": {
+ "LABEL": "Added inboxes",
+ "DESCRIPTION": "Add inboxes for which this policy will be applicable.",
+ "ADD_BUTTON": "Add inbox",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select inboxes to add",
+ "ADD_BUTTON": "Add"
+ },
+ "EMPTY_STATE": "No inboxes added to this policy, add an inbox to get started",
+ "API": {
+ "SUCCESS_MESSAGE": "Inbox successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Assignment policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete assignment policy"
+ }
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent capacity",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "POPOVER": "Added agents",
+ "EDIT": "Uredi"
+ },
+ "NO_RECORDS_FOUND": "No agent capacity policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create agent capacity policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy created successfully",
+ "ERROR_MESSAGE": "Failed to create agent capacity policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit agent capacity policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_AGENT_DIALOG": {
+ "TITLE": "Add agent",
+ "DESCRIPTION": "{agentName} is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Odustani"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update agent capacity policy"
+ },
+ "AGENT_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Agent added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Agent removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove agent from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Description:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "INBOX_CAPACITY_LIMIT": {
+ "LABEL": "Inbox capacity limits",
+ "ADD_BUTTON": "Add inbox",
+ "FIELD": {
+ "SELECT_INBOX": "Select inbox",
+ "MAX_CONVERSATIONS": "Max conversations",
+ "SET_LIMIT": "Set limit"
+ },
+ "EMPTY_STATE": "No inbox limit set"
+ },
+ "EXCLUSION_RULES": {
+ "LABEL": "Exclusion rules",
+ "DESCRIPTION": "Conversations that satisfy the following conditions would not count towards agent capacity",
+ "TAGS": {
+ "LABEL": "Exclude conversations tagged with specific labels",
+ "ADD_TAG": "add tag",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select tags to add"
+ },
+ "EMPTY_STATE": "No tags added to this policy."
+ },
+ "DURATION": {
+ "LABEL": "Exclude conversations older than a specified duration",
+ "PLACEHOLDER": "Set time"
+ }
+ },
+ "USERS": {
+ "LABEL": "Assigned agents",
+ "DESCRIPTION": "Add agents for which this policy will be applicable.",
+ "ADD_BUTTON": "Add agent",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select agents to add",
+ "ADD_BUTTON": "Add"
+ },
+ "EMPTY_STATE": "No agents added",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Agent capacity policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete agent capacity policy"
+ }
+ },
+ "DELETE_POLICY": {
+ "TITLE": "Delete policy",
+ "DESCRIPTION": "Are you sure you want to delete this policy? This action cannot be undone.",
+ "CONFIRM_BUTTON_LABEL": "Izbriši",
+ "CANCEL_BUTTON_LABEL": "Odustani"
+ }
}
}
diff --git a/app/javascript/dashboard/i18n/locale/hr/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/hr/whatsappTemplates.json
index ce82b426f..39c64adce 100644
--- a/app/javascript/dashboard/i18n/locale/hr/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/hr/whatsappTemplates.json
@@ -40,6 +40,7 @@
"BUTTON_LABEL": "Button {index}",
"COUPON_CODE": "Enter coupon code (max 15 chars)",
"MEDIA_URL_LABEL": "Enter {type} URL",
+ "DOCUMENT_NAME_PLACEHOLDER": "Enter document filename (e.g., Invoice_2025.pdf)",
"BUTTON_PARAMETER": "Enter button parameter"
}
}
diff --git a/app/javascript/dashboard/i18n/locale/hu/automation.json b/app/javascript/dashboard/i18n/locale/hu/automation.json
index 82873e610..a75c77e6b 100644
--- a/app/javascript/dashboard/i18n/locale/hu/automation.json
+++ b/app/javascript/dashboard/i18n/locale/hu/automation.json
@@ -177,7 +177,8 @@
"REFERER_LINK": "Referrer Link",
"ASSIGNEE_NAME": "Assignee",
"TEAM_NAME": "Csapat",
- "PRIORITY": "Prioritás"
+ "PRIORITY": "Prioritás",
+ "LABELS": "Cimkék"
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/hu/contact.json b/app/javascript/dashboard/i18n/locale/hu/contact.json
index aae0bee52..55d5c8271 100644
--- a/app/javascript/dashboard/i18n/locale/hu/contact.json
+++ b/app/javascript/dashboard/i18n/locale/hu/contact.json
@@ -610,6 +610,15 @@
"SEND_MESSAGE": "Üzenet elküldése"
}
},
+ "TWILIO_OPTIONS": {
+ "LABEL": "Select template",
+ "SEARCH_PLACEHOLDER": "Search templates",
+ "EMPTY_STATE": "No templates found",
+ "TEMPLATE_PARSER": {
+ "BACK": "Visszaugrás",
+ "SEND_MESSAGE": "Üzenet elküldése"
+ }
+ },
"ACTION_BUTTONS": {
"DISCARD": "Discard",
"SEND": "Send ({keyCode})"
diff --git a/app/javascript/dashboard/i18n/locale/hu/contactFilters.json b/app/javascript/dashboard/i18n/locale/hu/contactFilters.json
index 6838b2a96..d3f6309a2 100644
--- a/app/javascript/dashboard/i18n/locale/hu/contactFilters.json
+++ b/app/javascript/dashboard/i18n/locale/hu/contactFilters.json
@@ -48,7 +48,8 @@
"CREATED_AT": "Létrehozva",
"LAST_ACTIVITY": "Utolsó aktivitás",
"REFERER_LINK": "Hivatkozás link",
- "BLOCKED": "Blocked"
+ "BLOCKED": "Blocked",
+ "LABELS": "Cimkék"
},
"GROUPS": {
"STANDARD_FILTERS": "Alap szűrők",
diff --git a/app/javascript/dashboard/i18n/locale/hu/contentTemplates.json b/app/javascript/dashboard/i18n/locale/hu/contentTemplates.json
new file mode 100644
index 000000000..6a1956151
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/hu/contentTemplates.json
@@ -0,0 +1,51 @@
+{
+ "CONTENT_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Twilio Templates",
+ "SUBTITLE": "Select the Twilio template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Sablon keresése",
+ "NO_TEMPLATES_FOUND": "Nem található sablon erre:",
+ "NO_CONTENT": "No content",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Kategória",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No Twilio templates available. Click refresh to sync templates from Twilio.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Nyelv",
+ "TEMPLATE_BODY": "Sablon törzse",
+ "CATEGORY": "Kategória"
+ },
+ "TYPES": {
+ "MEDIA": "Media",
+ "QUICK_REPLY": "Quick Reply",
+ "TEXT": "Szöveg"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Változók",
+ "LANGUAGE": "Nyelv",
+ "CATEGORY": "Kategória",
+ "VARIABLE_PLACEHOLDER": "Add meg a {variable} értékét",
+ "GO_BACK_LABEL": "Vissza",
+ "SEND_MESSAGE_LABEL": "Üzenet küldése",
+ "FORM_ERROR_MESSAGE": "Kérlek add meg az összes változó értékét küldés előtt",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "MEDIA_URL_LABEL": "Enter full media URL",
+ "MEDIA_URL_PLACEHOLDER": "https://example.com/image.jpg"
+ },
+ "FORM": {
+ "BACK_BUTTON": "Vissza",
+ "SEND_MESSAGE_BUTTON": "Üzenet küldése"
+ }
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/hu/conversation.json b/app/javascript/dashboard/i18n/locale/hu/conversation.json
index 21336a73e..49220702e 100644
--- a/app/javascript/dashboard/i18n/locale/hu/conversation.json
+++ b/app/javascript/dashboard/i18n/locale/hu/conversation.json
@@ -35,6 +35,11 @@
"API_HOURS_WINDOW": "You can only reply to this conversation within {hours} hours",
"NOT_ASSIGNED_TO_YOU": "Ez a beszélgetés nincs hozzádrendelve. Szeretnéd magadhoz rendelni?",
"ASSIGN_TO_ME": "Hozzárendelés magamhoz",
+ "BOT_HANDOFF_MESSAGE": "You are responding to a conversation which is currently handled by an assistant or a bot.",
+ "BOT_HANDOFF_ACTION": "Mark open and assign to you",
+ "BOT_HANDOFF_REOPEN_ACTION": "Mark conversation open",
+ "BOT_HANDOFF_SUCCESS": "Conversation has been handed over to you",
+ "BOT_HANDOFF_ERROR": "Failed to take over the conversation. Please try again.",
"TWILIO_WHATSAPP_CAN_REPLY": "Erre a beszélgetésre csak konzerv válasszal válaszolhatsz, mert",
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "24 órás üzeneti ablak megkötés",
"OLD_INSTAGRAM_INBOX_REPLY_BANNER": "This Instagram account was migrated to the new Instagram channel inbox. All new messages will show up there. You won’t be able to send messages from this conversation anymore.",
@@ -66,6 +71,17 @@
"SHOW_LABELS": "Cimkék mutatása",
"HIDE_LABELS": "Cimkék elrejtése"
},
+ "VOICE_CALL": {
+ "INCOMING_CALL": "Incoming call",
+ "OUTGOING_CALL": "Outgoing call",
+ "CALL_IN_PROGRESS": "Call in progress",
+ "NO_ANSWER": "No answer",
+ "MISSED_CALL": "Missed call",
+ "CALL_ENDED": "Call ended",
+ "NOT_ANSWERED_YET": "Not answered yet",
+ "THEY_ANSWERED": "They answered",
+ "YOU_ANSWERED": "You answered"
+ },
"HEADER": {
"RESOLVE_ACTION": "Megoldva",
"REOPEN_ACTION": "Újranyitás",
diff --git a/app/javascript/dashboard/i18n/locale/hu/helpCenter.json b/app/javascript/dashboard/i18n/locale/hu/helpCenter.json
index dcd6d853c..13cd495b1 100644
--- a/app/javascript/dashboard/i18n/locale/hu/helpCenter.json
+++ b/app/javascript/dashboard/i18n/locale/hu/helpCenter.json
@@ -741,7 +741,8 @@
"LIVE_CHAT_WIDGET": {
"LABEL": "Live chat widget",
"PLACEHOLDER": "Select live chat widget",
- "HELP_TEXT": "Select a live chat widget that will appear on your help center"
+ "HELP_TEXT": "Select a live chat widget that will appear on your help center",
+ "NONE_OPTION": "No widget"
},
"BRAND_COLOR": {
"LABEL": "Brand color"
@@ -807,6 +808,58 @@
"ERROR_MESSAGE": "Unable to update portal"
}
}
+ },
+ "PDF_UPLOAD": {
+ "TITLE": "Upload PDF Document",
+ "DESCRIPTION": "Upload a PDF document to automatically generate FAQs using AI",
+ "DRAG_DROP_TEXT": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "ADDITIONAL_CONTEXT_LABEL": "Additional Context (Optional)",
+ "ADDITIONAL_CONTEXT_PLACEHOLDER": "Provide any additional context or instructions for FAQ generation...",
+ "UPLOADING": "Frissítés...",
+ "UPLOAD": "Upload & Process",
+ "CANCEL": "Mégse",
+ "ERROR_INVALID_TYPE": "Please select a valid PDF file",
+ "ERROR_FILE_TOO_LARGE": "File size must be less than 512MB",
+ "ERROR_UPLOAD_FAILED": "Failed to upload PDF. Please try again."
+ },
+ "PDF_DOCUMENTS": {
+ "TITLE": "PDF Documents",
+ "DESCRIPTION": "Manage uploaded PDF documents and generate FAQs from them",
+ "UPLOAD_PDF": "Upload PDF",
+ "UPLOAD_FIRST_PDF": "Upload your first PDF",
+ "UPLOADED_BY": "Uploaded by",
+ "GENERATE_FAQS": "Generate FAQs",
+ "GENERATING": "Generálás...",
+ "CONFIRM_DELETE": "Are you sure you want to delete {filename}?",
+ "EMPTY_STATE": {
+ "TITLE": "No PDF documents yet",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQs using AI"
+ },
+ "STATUS": {
+ "UPLOADED": "Ready",
+ "PROCESSING": "Processing",
+ "PROCESSED": "Lezárt",
+ "FAILED": "Failed"
+ }
+ },
+ "CONTENT_GENERATION": {
+ "TITLE": "Content Generation",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQ content using AI",
+ "UPLOAD_TITLE": "Upload PDF Document",
+ "DRAG_DROP": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "UPLOADING": "Processing document...",
+ "UPLOAD_SUCCESS": "Document processed successfully!",
+ "UPLOAD_ERROR": "Failed to upload document. Please try again.",
+ "INVALID_FILE_TYPE": "Please select a valid PDF file",
+ "FILE_TOO_LARGE": "File size must be less than 512MB",
+ "GENERATED_CONTENT": "Generated FAQ Content",
+ "PUBLISH_SELECTED": "Publish Selected",
+ "PUBLISHING": "Publishing...",
+ "FROM_DOCUMENT": "From document",
+ "NO_CONTENT": "No generated content available. Upload a PDF document to get started.",
+ "LOADING": "Loading generated content..."
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/hu/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/hu/inboxMgmt.json
index be250c927..4ae98ce53 100644
--- a/app/javascript/dashboard/i18n/locale/hu/inboxMgmt.json
+++ b/app/javascript/dashboard/i18n/locale/hu/inboxMgmt.json
@@ -272,8 +272,8 @@
},
"SUBMIT_BUTTON": "WhatsApp cstorna létrehozása",
"EMBEDDED_SIGNUP": {
- "TITLE": "Quick Setup with Meta",
- "DESC": "You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
+ "TITLE": "Quick setup with Meta",
+ "DESC": "Use the WhatsApp Embedded Signup flow to quickly connect new numbers. You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
"BENEFITS": {
"TITLE": "Benefits of Embedded Signup:",
"EASY_SETUP": "No manual configuration required",
@@ -281,9 +281,8 @@
"AUTO_CONFIG": "Automatic webhook and phone number configuration"
},
"LEARN_MORE": {
- "TEXT": "To learn more about integrated signup, pricing, and limitations, visit",
- "LINK_TEXT": "this link.",
- "LINK_URL": "https://developers.facebook.com/docs/whatsapp/embedded-signup/custom-flows/onboarding-business-app-users#limitations"
+ "TEXT": "To learn more about integrated signup, pricing, and limitations, visit {link}.",
+ "LINK_TEXT": "this link"
},
"SUBMIT_BUTTON": "Connect with WhatsApp Business",
"AUTH_PROCESSING": "Authenticating with Meta",
@@ -296,7 +295,9 @@
"INVALID_BUSINESS_DATA": "Invalid business data received from Facebook. Please try again.",
"SIGNUP_ERROR": "Signup error occurred",
"AUTH_NOT_COMPLETED": "Authentication not completed. Please restart the process.",
- "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured"
+ "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured",
+ "MANUAL_FALLBACK": "If your number is already connected to the WhatsApp Business Platform (API), or if you’re a tech provider onboarding your own number, please use the {link} flow",
+ "MANUAL_LINK_TEXT": "manual setup flow"
},
"API": {
"ERROR_MESSAGE": "Nem tudtuk elmenteni a WhatsApp csatornát"
@@ -424,7 +425,51 @@
},
"AUTH": {
"TITLE": "Csatorna kiválasztása",
- "DESC": "Chatwoot supports live-chat widgets, Facebook Messenger, Twitter profiles, WhatsApp, Emails, etc., as channels. If you want to build a custom channel, you can create it using the API channel. To get started, choose one of the channels below."
+ "DESC": "Chatwoot supports live-chat widgets, Facebook Messenger, Twitter profiles, WhatsApp, Emails, etc., as channels. If you want to build a custom channel, you can create it using the API channel. To get started, choose one of the channels below.",
+ "TITLE_NEXT": "Complete the setup",
+ "TITLE_FINISH": "Voilà!",
+ "CHANNEL": {
+ "WEBSITE": {
+ "TITLE": "Honlap",
+ "DESCRIPTION": "Create a live-chat widget"
+ },
+ "FACEBOOK": {
+ "TITLE": "Facebook",
+ "DESCRIPTION": "Connect your Facebook page"
+ },
+ "WHATSAPP": {
+ "TITLE": "WhatsApp",
+ "DESCRIPTION": "Support your customers on WhatsApp"
+ },
+ "EMAIL": {
+ "TITLE": "E-mail",
+ "DESCRIPTION": "Connect with Gmail, Outlook, or other providers"
+ },
+ "SMS": {
+ "TITLE": "SMS",
+ "DESCRIPTION": "Integrate SMS channel with Twilio or bandwidth"
+ },
+ "API": {
+ "TITLE": "API",
+ "DESCRIPTION": "Make a custom channel using our API"
+ },
+ "TELEGRAM": {
+ "TITLE": "Telegram",
+ "DESCRIPTION": "Configure Telegram channel using Bot token"
+ },
+ "LINE": {
+ "TITLE": "Line",
+ "DESCRIPTION": "Integrate your Line channel"
+ },
+ "INSTAGRAM": {
+ "TITLE": "Instagram",
+ "DESCRIPTION": "Connect your instagram account"
+ },
+ "VOICE": {
+ "TITLE": "Voice",
+ "DESCRIPTION": "Integrate with Twilio Voice"
+ }
+ }
},
"AGENTS": {
"TITLE": "Ügynökök",
@@ -478,7 +523,10 @@
"MESSAGE": "Most már tudsz egyeztetni az ügyfeleiddel az új csatornán. Boldog támgoatást",
"BUTTON_TEXT": "Vigyél oda",
"MORE_SETTINGS": "További beállítások",
- "WEBSITE_SUCCESS": "Sikeresen létrehoztad a website csatornát. Másold az itt látható kódot és helyezd el a weboldaladon. Legközelebb, mikor egy ügyfél az élő chatben van, a beszélgetés automatikusan megjelenik az inboxodban."
+ "WEBSITE_SUCCESS": "Sikeresen létrehoztad a website csatornát. Másold az itt látható kódot és helyezd el a weboldaladon. Legközelebb, mikor egy ügyfél az élő chatben van, a beszélgetés automatikusan megjelenik az inboxodban.",
+ "WHATSAPP_QR_INSTRUCTION": "Scan the QR code above to quickly test your WhatsApp inbox",
+ "MESSENGER_QR_INSTRUCTION": "Scan the QR code above to quickly test your Facebook Messenger inbox",
+ "TELEGRAM_QR_INSTRUCTION": "Scan the QR code above to quickly test your Telegram inbox"
},
"REAUTH": "Újraengedélyezés",
"VIEW": "Megtekintés",
@@ -868,9 +916,18 @@
"SCRIPT_SETTINGS": "\nwindow.chatwootSettings = {options};"
},
"EMAIL_PROVIDERS": {
- "MICROSOFT": "360Dialog",
- "GOOGLE": "Google",
- "OTHER_PROVIDERS": "Más szolgáltató"
+ "MICROSOFT": {
+ "TITLE": "360Dialog",
+ "DESCRIPTION": "Connect with Microsoft"
+ },
+ "GOOGLE": {
+ "TITLE": "Google",
+ "DESCRIPTION": "Connect with Google"
+ },
+ "OTHER_PROVIDERS": {
+ "TITLE": "Más szolgáltató",
+ "DESCRIPTION": "Connect with Other Providers"
+ }
},
"CHANNELS": {
"MESSENGER": "Messenger",
diff --git a/app/javascript/dashboard/i18n/locale/hu/integrations.json b/app/javascript/dashboard/i18n/locale/hu/integrations.json
index 33904dde3..544358514 100644
--- a/app/javascript/dashboard/i18n/locale/hu/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/hu/integrations.json
@@ -704,11 +704,28 @@
"ERROR_MESSAGE": "There was an error creating the document, please try again."
},
"FORM": {
+ "TYPE": {
+ "LABEL": "Document Type",
+ "URL": "URL",
+ "PDF": "PDF File"
+ },
"URL": {
"LABEL": "URL",
"PLACEHOLDER": "Enter the URL of the document",
"ERROR": "Please provide a valid URL for the document"
},
+ "PDF_FILE": {
+ "LABEL": "PDF File",
+ "CHOOSE_FILE": "Choose PDF file",
+ "ERROR": "Please select a PDF file",
+ "HELP_TEXT": "Maximum file size: 10MB",
+ "INVALID_TYPE": "Please select a valid PDF file",
+ "TOO_LARGE": "File size exceeds 10MB limit"
+ },
+ "NAME": {
+ "LABEL": "Document Name (Optional)",
+ "PLACEHOLDER": "Enter a name for the document"
+ },
"ASSISTANT": {
"LABEL": "Assistant",
"PLACEHOLDER": "Select the assistant",
@@ -744,6 +761,7 @@
"SELECTED": "{count} selected",
"SELECT_ALL": "Select all ({count})",
"UNSELECT_ALL": "Unselect all ({count})",
+ "SEARCH_PLACEHOLDER": "Search FAQs...",
"BULK_APPROVE_BUTTON": "Approve",
"BULK_DELETE_BUTTON": "Törlés",
"BULK_APPROVE": {
diff --git a/app/javascript/dashboard/i18n/locale/hu/mfa.json b/app/javascript/dashboard/i18n/locale/hu/mfa.json
new file mode 100644
index 000000000..479656236
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/hu/mfa.json
@@ -0,0 +1,106 @@
+{
+ "MFA_SETTINGS": {
+ "TITLE": "Two-Factor Authentication",
+ "SUBTITLE": "Secure your account with TOTP-based authentication",
+ "DESCRIPTION": "Add an extra layer of security to your account using a time-based one-time password (TOTP)",
+ "STATUS_TITLE": "Authentication Status",
+ "STATUS_DESCRIPTION": "Manage your two-factor authentication settings and backup recovery codes",
+ "ENABLED": "Engedélyezve",
+ "DISABLED": "Letiltva",
+ "STATUS_ENABLED": "Two-factor authentication is active",
+ "STATUS_ENABLED_DESC": "Your account is protected with an additional layer of security",
+ "ENABLE_BUTTON": "Enable Two-Factor Authentication",
+ "ENHANCE_SECURITY": "Enhance Your Account Security",
+ "ENHANCE_SECURITY_DESC": "Two-factor authentication adds an extra layer of security by requiring a verification code from your authenticator app in addition to your password.",
+ "SETUP": {
+ "STEP_NUMBER_1": "1",
+ "STEP_NUMBER_2": "2",
+ "STEP1_TITLE": "Scan QR Code with Your Authenticator App",
+ "STEP1_DESCRIPTION": "Use Google Authenticator, Authy, or any TOTP-compatible app",
+ "LOADING_QR": "Loading...",
+ "MANUAL_ENTRY": "Can't scan? Enter code manually",
+ "SECRET_KEY": "Secret Key",
+ "COPY": "Másolás",
+ "ENTER_CODE": "Enter the 6-digit code from your authenticator app",
+ "ENTER_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify & Continue",
+ "CANCEL": "Mégse",
+ "ERROR_STARTING": "MFA not enabled. Please contact administrator.",
+ "INVALID_CODE": "Invalid verification code",
+ "SECRET_COPIED": "Secret key copied to clipboard",
+ "SUCCESS": "Two-factor authentication has been enabled successfully"
+ },
+ "BACKUP": {
+ "TITLE": "Save Your Backup Codes",
+ "DESCRIPTION": "Keep these codes safe. Each can be used once if you lose access to your authenticator",
+ "IMPORTANT": "Important:",
+ "IMPORTANT_NOTE": " Save these codes in a secure location. You won't be able to see them again.",
+ "DOWNLOAD": "Letöltés",
+ "COPY_ALL": "Copy All",
+ "CONFIRM": "I have saved my backup codes in a secure location and understand that I won't be able to see them again",
+ "COMPLETE_SETUP": "Complete Setup",
+ "CODES_COPIED": "Backup codes copied to clipboard"
+ },
+ "MANAGEMENT": {
+ "BACKUP_CODES": "Backup Codes",
+ "BACKUP_CODES_DESC": "Generate new codes if you've lost or used your existing ones",
+ "REGENERATE": "Regenerate Backup Codes",
+ "DISABLE_MFA": "Disable 2FA",
+ "DISABLE_MFA_DESC": "Remove two-factor authentication from your account",
+ "DISABLE_BUTTON": "Disable Two-Factor Authentication"
+ },
+ "DISABLE": {
+ "TITLE": "Disable Two-Factor Authentication",
+ "DESCRIPTION": "You'll need to enter your password and a verification code to disable two-factor authentication.",
+ "PASSWORD": "Jelszó",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Disable 2FA",
+ "CANCEL": "Mégse",
+ "SUCCESS": "Two-factor authentication has been disabled",
+ "ERROR": "Failed to disable MFA. Please check your credentials."
+ },
+ "REGENERATE": {
+ "TITLE": "Regenerate Backup Codes",
+ "DESCRIPTION": "This will invalidate your existing backup codes and generate new ones. Enter your verification code to continue.",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Generate New Codes",
+ "CANCEL": "Mégse",
+ "NEW_CODES_TITLE": "New Backup Codes Generated",
+ "NEW_CODES_DESC": "Your old backup codes have been invalidated. Save these new codes in a secure location.",
+ "CODES_IMPORTANT": "Important:",
+ "CODES_IMPORTANT_NOTE": " Each code can only be used once. Save them before closing this window.",
+ "DOWNLOAD_CODES": "Download Codes",
+ "COPY_ALL_CODES": "Copy All Codes",
+ "CODES_SAVED": "I've Saved My Codes",
+ "SUCCESS": "New backup codes have been generated",
+ "ERROR": "Failed to regenerate backup codes"
+ }
+ },
+ "MFA_VERIFICATION": {
+ "TITLE": "Two-Factor Authentication",
+ "DESCRIPTION": "Enter your verification code to continue",
+ "AUTHENTICATOR_APP": "Authenticator App",
+ "BACKUP_CODE": "Backup Code",
+ "ENTER_OTP_CODE": "Enter 6-digit code from your authenticator app",
+ "ENTER_BACKUP_CODE": "Enter one of your backup codes",
+ "BACKUP_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify",
+ "TRY_ANOTHER_METHOD": "Try another verification method",
+ "CANCEL_LOGIN": "Cancel and return to login",
+ "HELP_TEXT": "Having trouble signing in?",
+ "LEARN_MORE": "Learn more about 2FA",
+ "HELP_MODAL": {
+ "TITLE": "Two-Factor Authentication Help",
+ "AUTHENTICATOR_TITLE": "Using an Authenticator App",
+ "AUTHENTICATOR_DESC": "Open your authenticator app (Google Authenticator, Authy, etc.) and enter the 6-digit code shown for your account.",
+ "BACKUP_TITLE": "Using a Backup Code",
+ "BACKUP_DESC": "If you don't have access to your authenticator app, you can use one of the backup codes you saved when setting up 2FA. Each code can only be used once.",
+ "CONTACT_TITLE": "Need More Help?",
+ "CONTACT_DESC_CLOUD": "If you've lost access to both your authenticator app and backup codes, please reach out to Chatwoot support for assistance.",
+ "CONTACT_DESC_SELF_HOSTED": "If you've lost access to both your authenticator app and backup codes, please contact your administrator for assistance."
+ },
+ "VERIFICATION_FAILED": "Verification failed. Please try again."
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/hu/settings.json b/app/javascript/dashboard/i18n/locale/hu/settings.json
index 41dd9b73e..1788fbdcd 100644
--- a/app/javascript/dashboard/i18n/locale/hu/settings.json
+++ b/app/javascript/dashboard/i18n/locale/hu/settings.json
@@ -51,6 +51,13 @@
"LARGER": "Larger",
"EXTRA_LARGE": "Extra Large"
}
+ },
+ "LANGUAGE": {
+ "TITLE": "Preferred Language",
+ "NOTE": "Choose the language you want to use.",
+ "UPDATE_SUCCESS": "Your Language settings have been updated successfully",
+ "UPDATE_ERROR": "There is an error while updating the language settings, please try again",
+ "USE_ACCOUNT_DEFAULT": "Use account default"
}
},
"MESSAGE_SIGNATURE_SECTION": {
@@ -73,6 +80,11 @@
"NOTE": "A jelszó frissítása minden beléptetett eszközt kiléptet.",
"BTN_TEXT": "Jelszó megváltoztatása"
},
+ "SECURITY_SECTION": {
+ "TITLE": "Security",
+ "NOTE": "Manage additional security features for your account.",
+ "MFA_BUTTON": "Manage Two-Factor Authentication"
+ },
"ACCESS_TOKEN": {
"TITLE": "Hozzáférési kulcs",
"NOTE": "Ez a kulcs akkor használható, ha API-alapú integrációt építesz",
@@ -330,6 +342,7 @@
"REPORTS_LABEL": "Cimkék",
"REPORTS_INBOX": "Fiók",
"REPORTS_TEAM": "Csapat",
+ "AGENT_ASSIGNMENT": "Agent Assignment",
"SET_AVAILABILITY_TITLE": "Ugrás a Beszélgetések Irányítópultjához",
"SET_YOUR_AVAILABILITY": "Elérhetőség beállítása",
"SLA": "SLA",
@@ -350,7 +363,8 @@
"INFO_TEXT": "Hagyd, hogy a rendszer automatikusan offline módban jelöljön meg, amikor nem használod az alkalmazást vagy az irányítópultot.",
"INFO_SHORT": "Automatically mark offline when you aren't using the app."
},
- "DOCS": "Dokumentum olvasása"
+ "DOCS": "Dokumentum olvasása",
+ "SECURITY": "Security"
},
"BILLING_SETTINGS": {
"TITLE": "Számlázás",
@@ -382,6 +396,77 @@
},
"NO_BILLING_USER": "Számlázási fiókod konfigurálás alatt áll. Kérjük, frissítsd az oldalt, és próbáld újra."
},
+ "SECURITY_SETTINGS": {
+ "TITLE": "Security",
+ "DESCRIPTION": "Manage your account security settings.",
+ "LINK_TEXT": "Learn more about SAML SSO",
+ "SAML": {
+ "TITLE": "SAML SSO",
+ "NOTE": "Configure SAML single sign-on for your account. Users will authenticate through your identity provider instead of using email/password.",
+ "ACS_URL": {
+ "LABEL": "ACS URL",
+ "TOOLTIP": "Assertion Consumer Service URL - Configure this URL in your IdP as the destination for SAML responses"
+ },
+ "SSO_URL": {
+ "LABEL": "SSO URL",
+ "HELP": "The URL where SAML authentication requests will be sent",
+ "PLACEHOLDER": "https://your-idp.com/saml/sso"
+ },
+ "CERTIFICATE": {
+ "LABEL": "Signing certificate in PEM format",
+ "HELP": "The public certificate from your identity provider used to verify SAML responses",
+ "PLACEHOLDER": "-----BEGIN CERTIFICATE-----\nMIIC..."
+ },
+ "FINGERPRINT": {
+ "LABEL": "Fingerprint",
+ "TOOLTIP": "SHA-1 fingerprint of the certificate - Use this to verify the certificate in your IdP configuration"
+ },
+ "COPY_SUCCESS": "Vágólapra másolva",
+ "SP_ENTITY_ID": {
+ "LABEL": "SP Entity ID",
+ "HELP": "Unique identifier for this application as a service provider (auto-generated).",
+ "TOOLTIP": "Unique identifier for Chatwoot as the Service Provider - Configure this in your IdP settings"
+ },
+ "IDP_ENTITY_ID": {
+ "LABEL": "Identity Provider Entity ID",
+ "HELP": "Unique identifier for your identity provider (usually found in IdP configuration)",
+ "PLACEHOLDER": "https://your-idp.com/saml"
+ },
+ "UPDATE_BUTTON": "Update SAML Settings",
+ "API": {
+ "SUCCESS": "SAML settings updated successfully",
+ "ERROR": "Failed to update SAML settings",
+ "ERROR_LOADING": "Failed to load SAML settings",
+ "DISABLED": "SAML settings disabled successfully"
+ },
+ "VALIDATION": {
+ "REQUIRED_FIELDS": "SSO URL, Identity Provider Entity ID, and Certificate are required fields",
+ "SSO_URL_ERROR": "Please enter a valid SSO URL",
+ "CERTIFICATE_ERROR": "Certificate is required",
+ "IDP_ENTITY_ID_ERROR": "Identity Provider Entity ID is required"
+ },
+ "ENTERPRISE_PAYWALL": {
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade to an Enterprise plan to access SAML single sign-on and other advanced security features.",
+ "ASK_ADMIN": "Please reach out to your administrator for the upgrade."
+ },
+ "PAYWALL": {
+ "TITLE": "Upgrade to enable SAML SSO",
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade your plan to get access to SAML single sign-on and other advanced features.",
+ "UPGRADE_NOW": "Upgrade now",
+ "CANCEL_ANYTIME": "You can change or cancel your plan anytime"
+ },
+ "ATTRIBUTE_MAPPING": {
+ "TITLE": "SAML Attribute Setup",
+ "DESCRIPTION": "The following attribute mappings must be configured in your identity provider"
+ },
+ "INFO_SECTION": {
+ "TITLE": "Service Provider Information",
+ "TOOLTIP": "Copy these values and configure them in your Identity Provider to establish the SAML connection"
+ }
+ }
+ },
"CREATE_ACCOUNT": {
"NO_ACCOUNT_WARNING": "Uppsz! Nem találtunk egyetlen Chatwoot-fiókot sem. A folytatáshoz kérlek hozz létre egy új fiókot.",
"NEW_ACCOUNT": "Új fiók",
@@ -418,5 +503,255 @@
"SWITCH_TO_REPLY": "Válts a válasz fülre",
"TOGGLE_SNOOZE_DROPDOWN": "Alvómód bekapcsolása a legördülő menüben"
}
+ },
+ "ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent assignment",
+ "DESCRIPTION": "Define policies to effectively manage workload and route conversations based on the needs of inboxes and agents. Learn more here"
+ },
+ "ASSIGNMENT_POLICY": {
+ "TITLE": "Assignment policy",
+ "DESCRIPTION": "Manage how conversations get assigned in inboxes.",
+ "FEATURES": [
+ "Assign by conversations evenly or by available capacity",
+ "Add fair distribution rules to avoid overloading any agent",
+ "Add inboxes to a policy - one policy per inbox"
+ ]
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "TITLE": "Agent capacity policy",
+ "DESCRIPTION": "Manage workload for agents.",
+ "FEATURES": [
+ "Define maximum conversations per inbox",
+ "Create exceptions based on labels and time",
+ "Add agents to a policy - one policy per agent"
+ ]
+ }
+ },
+ "AGENT_ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Assignment policy",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "ORDER": "Order",
+ "PRIORITY": "Prioritás",
+ "ACTIVE": "Aktív",
+ "INACTIVE": "Inactive",
+ "POPOVER": "Added inboxes",
+ "EDIT": "Szerkesztés"
+ },
+ "NO_RECORDS_FOUND": "No assignment policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create assignment policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy created successfully",
+ "ERROR_MESSAGE": "Failed to create assignment policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit assignment policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_INBOX_DIALOG": {
+ "TITLE": "Add inbox",
+ "DESCRIPTION": "{inboxName} inbox is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Mégse"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update assignment policy"
+ },
+ "INBOX_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Inbox added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Inbox removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove inbox from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Leírás:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "STATUS": {
+ "LABEL": "Státusz:",
+ "PLACEHOLDER": "Select status",
+ "ACTIVE": "Policy is active",
+ "INACTIVE": "Policy is inactive"
+ },
+ "ASSIGNMENT_ORDER": {
+ "LABEL": "Assignment order",
+ "ROUND_ROBIN": {
+ "LABEL": "Round robin",
+ "DESCRIPTION": "Assign conversations evenly among agents."
+ },
+ "BALANCED": {
+ "LABEL": "Balanced",
+ "DESCRIPTION": "Assign conversations based on available capacity."
+ }
+ },
+ "ASSIGNMENT_PRIORITY": {
+ "LABEL": "Assignment priority",
+ "EARLIEST_CREATED": {
+ "LABEL": "Earliest created",
+ "DESCRIPTION": "The conversation that was created first gets assigned first."
+ },
+ "LONGEST_WAITING": {
+ "LABEL": "Longest waiting",
+ "DESCRIPTION": "The conversation waiting the longest gets assigned first."
+ }
+ },
+ "FAIR_DISTRIBUTION": {
+ "LABEL": "Fair distribution policy",
+ "DESCRIPTION": "Set the maximum number of conversations that can be assigned per agent within a time window to avoid overloading any one agent. This required field defaults to 100 conversations per hour.",
+ "INPUT_MAX": "Assign max",
+ "DURATION": "Conversations per agent in every"
+ },
+ "INBOXES": {
+ "LABEL": "Added inboxes",
+ "DESCRIPTION": "Add inboxes for which this policy will be applicable.",
+ "ADD_BUTTON": "Add inbox",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select inboxes to add",
+ "ADD_BUTTON": "Hozzáadás"
+ },
+ "EMPTY_STATE": "No inboxes added to this policy, add an inbox to get started",
+ "API": {
+ "SUCCESS_MESSAGE": "Inbox successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Assignment policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete assignment policy"
+ }
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent capacity",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "POPOVER": "Added agents",
+ "EDIT": "Szerkesztés"
+ },
+ "NO_RECORDS_FOUND": "No agent capacity policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create agent capacity policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy created successfully",
+ "ERROR_MESSAGE": "Failed to create agent capacity policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit agent capacity policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_AGENT_DIALOG": {
+ "TITLE": "Add agent",
+ "DESCRIPTION": "{agentName} is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Mégse"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update agent capacity policy"
+ },
+ "AGENT_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Agent added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Agent removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove agent from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Leírás:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "INBOX_CAPACITY_LIMIT": {
+ "LABEL": "Inbox capacity limits",
+ "ADD_BUTTON": "Add inbox",
+ "FIELD": {
+ "SELECT_INBOX": "Select inbox",
+ "MAX_CONVERSATIONS": "Max conversations",
+ "SET_LIMIT": "Set limit"
+ },
+ "EMPTY_STATE": "No inbox limit set"
+ },
+ "EXCLUSION_RULES": {
+ "LABEL": "Exclusion rules",
+ "DESCRIPTION": "Conversations that satisfy the following conditions would not count towards agent capacity",
+ "TAGS": {
+ "LABEL": "Exclude conversations tagged with specific labels",
+ "ADD_TAG": "add tag",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select tags to add"
+ },
+ "EMPTY_STATE": "No tags added to this policy."
+ },
+ "DURATION": {
+ "LABEL": "Exclude conversations older than a specified duration",
+ "PLACEHOLDER": "Set time"
+ }
+ },
+ "USERS": {
+ "LABEL": "Assigned agents",
+ "DESCRIPTION": "Add agents for which this policy will be applicable.",
+ "ADD_BUTTON": "Add agent",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select agents to add",
+ "ADD_BUTTON": "Hozzáadás"
+ },
+ "EMPTY_STATE": "No agents added",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Agent capacity policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete agent capacity policy"
+ }
+ },
+ "DELETE_POLICY": {
+ "TITLE": "Delete policy",
+ "DESCRIPTION": "Are you sure you want to delete this policy? This action cannot be undone.",
+ "CONFIRM_BUTTON_LABEL": "Törlés",
+ "CANCEL_BUTTON_LABEL": "Mégse"
+ }
}
}
diff --git a/app/javascript/dashboard/i18n/locale/hu/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/hu/whatsappTemplates.json
index e28c69704..addcfe063 100644
--- a/app/javascript/dashboard/i18n/locale/hu/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/hu/whatsappTemplates.json
@@ -40,6 +40,7 @@
"BUTTON_LABEL": "Button {index}",
"COUPON_CODE": "Enter coupon code (max 15 chars)",
"MEDIA_URL_LABEL": "Enter {type} URL",
+ "DOCUMENT_NAME_PLACEHOLDER": "Enter document filename (e.g., Invoice_2025.pdf)",
"BUTTON_PARAMETER": "Enter button parameter"
}
}
diff --git a/app/javascript/dashboard/i18n/locale/hy/automation.json b/app/javascript/dashboard/i18n/locale/hy/automation.json
index 80274f488..43245a1d5 100644
--- a/app/javascript/dashboard/i18n/locale/hy/automation.json
+++ b/app/javascript/dashboard/i18n/locale/hy/automation.json
@@ -177,7 +177,8 @@
"REFERER_LINK": "Referrer Link",
"ASSIGNEE_NAME": "Assignee",
"TEAM_NAME": "Team",
- "PRIORITY": "Priority"
+ "PRIORITY": "Priority",
+ "LABELS": "Labels"
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/hy/contact.json b/app/javascript/dashboard/i18n/locale/hy/contact.json
index 0ae5ecc38..b147164ec 100644
--- a/app/javascript/dashboard/i18n/locale/hy/contact.json
+++ b/app/javascript/dashboard/i18n/locale/hy/contact.json
@@ -610,6 +610,15 @@
"SEND_MESSAGE": "Send message"
}
},
+ "TWILIO_OPTIONS": {
+ "LABEL": "Select template",
+ "SEARCH_PLACEHOLDER": "Search templates",
+ "EMPTY_STATE": "No templates found",
+ "TEMPLATE_PARSER": {
+ "BACK": "Go back",
+ "SEND_MESSAGE": "Send message"
+ }
+ },
"ACTION_BUTTONS": {
"DISCARD": "Discard",
"SEND": "Send ({keyCode})"
diff --git a/app/javascript/dashboard/i18n/locale/hy/contactFilters.json b/app/javascript/dashboard/i18n/locale/hy/contactFilters.json
index bb3221c6e..4c62f0789 100644
--- a/app/javascript/dashboard/i18n/locale/hy/contactFilters.json
+++ b/app/javascript/dashboard/i18n/locale/hy/contactFilters.json
@@ -48,7 +48,8 @@
"CREATED_AT": "Created At",
"LAST_ACTIVITY": "Last Activity",
"REFERER_LINK": "Referrer link",
- "BLOCKED": "Blocked"
+ "BLOCKED": "Blocked",
+ "LABELS": "Labels"
},
"GROUPS": {
"STANDARD_FILTERS": "Standard Filters",
diff --git a/app/javascript/dashboard/i18n/locale/hy/contentTemplates.json b/app/javascript/dashboard/i18n/locale/hy/contentTemplates.json
new file mode 100644
index 000000000..a9b1d54c4
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/hy/contentTemplates.json
@@ -0,0 +1,51 @@
+{
+ "CONTENT_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Twilio Templates",
+ "SUBTITLE": "Select the Twilio template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Search Templates",
+ "NO_TEMPLATES_FOUND": "No templates found for",
+ "NO_CONTENT": "No content",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Category",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No Twilio templates available. Click refresh to sync templates from Twilio.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Language",
+ "TEMPLATE_BODY": "Template Body",
+ "CATEGORY": "Category"
+ },
+ "TYPES": {
+ "MEDIA": "Media",
+ "QUICK_REPLY": "Quick Reply",
+ "TEXT": "Text"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variables",
+ "LANGUAGE": "Language",
+ "CATEGORY": "Category",
+ "VARIABLE_PLACEHOLDER": "Enter {variable} value",
+ "GO_BACK_LABEL": "Go Back",
+ "SEND_MESSAGE_LABEL": "Send Message",
+ "FORM_ERROR_MESSAGE": "Please fill all variables before sending",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "MEDIA_URL_LABEL": "Enter full media URL",
+ "MEDIA_URL_PLACEHOLDER": "https://example.com/image.jpg"
+ },
+ "FORM": {
+ "BACK_BUTTON": "Back",
+ "SEND_MESSAGE_BUTTON": "Send Message"
+ }
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/hy/conversation.json b/app/javascript/dashboard/i18n/locale/hy/conversation.json
index 308f24f51..9fd39b70f 100644
--- a/app/javascript/dashboard/i18n/locale/hy/conversation.json
+++ b/app/javascript/dashboard/i18n/locale/hy/conversation.json
@@ -35,6 +35,11 @@
"API_HOURS_WINDOW": "You can only reply to this conversation within {hours} hours",
"NOT_ASSIGNED_TO_YOU": "This conversation is not assigned to you. Would you like to assign this conversation to yourself?",
"ASSIGN_TO_ME": "Assign to me",
+ "BOT_HANDOFF_MESSAGE": "You are responding to a conversation which is currently handled by an assistant or a bot.",
+ "BOT_HANDOFF_ACTION": "Mark open and assign to you",
+ "BOT_HANDOFF_REOPEN_ACTION": "Mark conversation open",
+ "BOT_HANDOFF_SUCCESS": "Conversation has been handed over to you",
+ "BOT_HANDOFF_ERROR": "Failed to take over the conversation. Please try again.",
"TWILIO_WHATSAPP_CAN_REPLY": "You can only reply to this conversation using a template message due to",
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "24 hour message window restriction",
"OLD_INSTAGRAM_INBOX_REPLY_BANNER": "This Instagram account was migrated to the new Instagram channel inbox. All new messages will show up there. You won’t be able to send messages from this conversation anymore.",
@@ -66,6 +71,17 @@
"SHOW_LABELS": "Show labels",
"HIDE_LABELS": "Hide labels"
},
+ "VOICE_CALL": {
+ "INCOMING_CALL": "Incoming call",
+ "OUTGOING_CALL": "Outgoing call",
+ "CALL_IN_PROGRESS": "Call in progress",
+ "NO_ANSWER": "No answer",
+ "MISSED_CALL": "Missed call",
+ "CALL_ENDED": "Call ended",
+ "NOT_ANSWERED_YET": "Not answered yet",
+ "THEY_ANSWERED": "They answered",
+ "YOU_ANSWERED": "You answered"
+ },
"HEADER": {
"RESOLVE_ACTION": "Resolve",
"REOPEN_ACTION": "Reopen",
diff --git a/app/javascript/dashboard/i18n/locale/hy/helpCenter.json b/app/javascript/dashboard/i18n/locale/hy/helpCenter.json
index fd2b1a788..0ab8d62ff 100644
--- a/app/javascript/dashboard/i18n/locale/hy/helpCenter.json
+++ b/app/javascript/dashboard/i18n/locale/hy/helpCenter.json
@@ -741,7 +741,8 @@
"LIVE_CHAT_WIDGET": {
"LABEL": "Live chat widget",
"PLACEHOLDER": "Select live chat widget",
- "HELP_TEXT": "Select a live chat widget that will appear on your help center"
+ "HELP_TEXT": "Select a live chat widget that will appear on your help center",
+ "NONE_OPTION": "No widget"
},
"BRAND_COLOR": {
"LABEL": "Brand color"
@@ -807,6 +808,58 @@
"ERROR_MESSAGE": "Unable to update portal"
}
}
+ },
+ "PDF_UPLOAD": {
+ "TITLE": "Upload PDF Document",
+ "DESCRIPTION": "Upload a PDF document to automatically generate FAQs using AI",
+ "DRAG_DROP_TEXT": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "ADDITIONAL_CONTEXT_LABEL": "Additional Context (Optional)",
+ "ADDITIONAL_CONTEXT_PLACEHOLDER": "Provide any additional context or instructions for FAQ generation...",
+ "UPLOADING": "Uploading...",
+ "UPLOAD": "Upload & Process",
+ "CANCEL": "Cancel",
+ "ERROR_INVALID_TYPE": "Please select a valid PDF file",
+ "ERROR_FILE_TOO_LARGE": "File size must be less than 512MB",
+ "ERROR_UPLOAD_FAILED": "Failed to upload PDF. Please try again."
+ },
+ "PDF_DOCUMENTS": {
+ "TITLE": "PDF Documents",
+ "DESCRIPTION": "Manage uploaded PDF documents and generate FAQs from them",
+ "UPLOAD_PDF": "Upload PDF",
+ "UPLOAD_FIRST_PDF": "Upload your first PDF",
+ "UPLOADED_BY": "Uploaded by",
+ "GENERATE_FAQS": "Generate FAQs",
+ "GENERATING": "Generating...",
+ "CONFIRM_DELETE": "Are you sure you want to delete {filename}?",
+ "EMPTY_STATE": {
+ "TITLE": "No PDF documents yet",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQs using AI"
+ },
+ "STATUS": {
+ "UPLOADED": "Ready",
+ "PROCESSING": "Processing",
+ "PROCESSED": "Completed",
+ "FAILED": "Failed"
+ }
+ },
+ "CONTENT_GENERATION": {
+ "TITLE": "Content Generation",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQ content using AI",
+ "UPLOAD_TITLE": "Upload PDF Document",
+ "DRAG_DROP": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "UPLOADING": "Processing document...",
+ "UPLOAD_SUCCESS": "Document processed successfully!",
+ "UPLOAD_ERROR": "Failed to upload document. Please try again.",
+ "INVALID_FILE_TYPE": "Please select a valid PDF file",
+ "FILE_TOO_LARGE": "File size must be less than 512MB",
+ "GENERATED_CONTENT": "Generated FAQ Content",
+ "PUBLISH_SELECTED": "Publish Selected",
+ "PUBLISHING": "Publishing...",
+ "FROM_DOCUMENT": "From document",
+ "NO_CONTENT": "No generated content available. Upload a PDF document to get started.",
+ "LOADING": "Loading generated content..."
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/hy/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/hy/inboxMgmt.json
index ebc03363c..427bb0692 100644
--- a/app/javascript/dashboard/i18n/locale/hy/inboxMgmt.json
+++ b/app/javascript/dashboard/i18n/locale/hy/inboxMgmt.json
@@ -272,8 +272,8 @@
},
"SUBMIT_BUTTON": "Create WhatsApp Channel",
"EMBEDDED_SIGNUP": {
- "TITLE": "Quick Setup with Meta",
- "DESC": "You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
+ "TITLE": "Quick setup with Meta",
+ "DESC": "Use the WhatsApp Embedded Signup flow to quickly connect new numbers. You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
"BENEFITS": {
"TITLE": "Benefits of Embedded Signup:",
"EASY_SETUP": "No manual configuration required",
@@ -281,9 +281,8 @@
"AUTO_CONFIG": "Automatic webhook and phone number configuration"
},
"LEARN_MORE": {
- "TEXT": "To learn more about integrated signup, pricing, and limitations, visit",
- "LINK_TEXT": "this link.",
- "LINK_URL": "https://developers.facebook.com/docs/whatsapp/embedded-signup/custom-flows/onboarding-business-app-users#limitations"
+ "TEXT": "To learn more about integrated signup, pricing, and limitations, visit {link}.",
+ "LINK_TEXT": "this link"
},
"SUBMIT_BUTTON": "Connect with WhatsApp Business",
"AUTH_PROCESSING": "Authenticating with Meta",
@@ -296,7 +295,9 @@
"INVALID_BUSINESS_DATA": "Invalid business data received from Facebook. Please try again.",
"SIGNUP_ERROR": "Signup error occurred",
"AUTH_NOT_COMPLETED": "Authentication not completed. Please restart the process.",
- "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured"
+ "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured",
+ "MANUAL_FALLBACK": "If your number is already connected to the WhatsApp Business Platform (API), or if you’re a tech provider onboarding your own number, please use the {link} flow",
+ "MANUAL_LINK_TEXT": "manual setup flow"
},
"API": {
"ERROR_MESSAGE": "We were not able to save the WhatsApp channel"
@@ -424,7 +425,51 @@
},
"AUTH": {
"TITLE": "Choose a channel",
- "DESC": "Chatwoot supports live-chat widgets, Facebook Messenger, Twitter profiles, WhatsApp, Emails, etc., as channels. If you want to build a custom channel, you can create it using the API channel. To get started, choose one of the channels below."
+ "DESC": "Chatwoot supports live-chat widgets, Facebook Messenger, Twitter profiles, WhatsApp, Emails, etc., as channels. If you want to build a custom channel, you can create it using the API channel. To get started, choose one of the channels below.",
+ "TITLE_NEXT": "Complete the setup",
+ "TITLE_FINISH": "Voilà!",
+ "CHANNEL": {
+ "WEBSITE": {
+ "TITLE": "Website",
+ "DESCRIPTION": "Create a live-chat widget"
+ },
+ "FACEBOOK": {
+ "TITLE": "Facebook",
+ "DESCRIPTION": "Connect your Facebook page"
+ },
+ "WHATSAPP": {
+ "TITLE": "WhatsApp",
+ "DESCRIPTION": "Support your customers on WhatsApp"
+ },
+ "EMAIL": {
+ "TITLE": "Email",
+ "DESCRIPTION": "Connect with Gmail, Outlook, or other providers"
+ },
+ "SMS": {
+ "TITLE": "SMS",
+ "DESCRIPTION": "Integrate SMS channel with Twilio or bandwidth"
+ },
+ "API": {
+ "TITLE": "API",
+ "DESCRIPTION": "Make a custom channel using our API"
+ },
+ "TELEGRAM": {
+ "TITLE": "Telegram",
+ "DESCRIPTION": "Configure Telegram channel using Bot token"
+ },
+ "LINE": {
+ "TITLE": "Line",
+ "DESCRIPTION": "Integrate your Line channel"
+ },
+ "INSTAGRAM": {
+ "TITLE": "Instagram",
+ "DESCRIPTION": "Connect your instagram account"
+ },
+ "VOICE": {
+ "TITLE": "Voice",
+ "DESCRIPTION": "Integrate with Twilio Voice"
+ }
+ }
},
"AGENTS": {
"TITLE": "Agents",
@@ -478,7 +523,10 @@
"MESSAGE": "You can now engage with your customers through your new Channel. Happy supporting",
"BUTTON_TEXT": "Take me there",
"MORE_SETTINGS": "More settings",
- "WEBSITE_SUCCESS": "You have successfully finished creating a website channel. Copy the code shown below and paste it on your website. Next time a customer use the live chat, the conversation will automatically appear on your inbox."
+ "WEBSITE_SUCCESS": "You have successfully finished creating a website channel. Copy the code shown below and paste it on your website. Next time a customer use the live chat, the conversation will automatically appear on your inbox.",
+ "WHATSAPP_QR_INSTRUCTION": "Scan the QR code above to quickly test your WhatsApp inbox",
+ "MESSENGER_QR_INSTRUCTION": "Scan the QR code above to quickly test your Facebook Messenger inbox",
+ "TELEGRAM_QR_INSTRUCTION": "Scan the QR code above to quickly test your Telegram inbox"
},
"REAUTH": "Reauthorize",
"VIEW": "View",
@@ -868,9 +916,18 @@
"SCRIPT_SETTINGS": "\n window.chatwootSettings = {options};"
},
"EMAIL_PROVIDERS": {
- "MICROSOFT": "Microsoft",
- "GOOGLE": "Google",
- "OTHER_PROVIDERS": "Other Providers"
+ "MICROSOFT": {
+ "TITLE": "Microsoft",
+ "DESCRIPTION": "Connect with Microsoft"
+ },
+ "GOOGLE": {
+ "TITLE": "Google",
+ "DESCRIPTION": "Connect with Google"
+ },
+ "OTHER_PROVIDERS": {
+ "TITLE": "Other Providers",
+ "DESCRIPTION": "Connect with Other Providers"
+ }
},
"CHANNELS": {
"MESSENGER": "Messenger",
diff --git a/app/javascript/dashboard/i18n/locale/hy/integrations.json b/app/javascript/dashboard/i18n/locale/hy/integrations.json
index f0c7abbd3..03898d278 100644
--- a/app/javascript/dashboard/i18n/locale/hy/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/hy/integrations.json
@@ -704,11 +704,28 @@
"ERROR_MESSAGE": "There was an error creating the document, please try again."
},
"FORM": {
+ "TYPE": {
+ "LABEL": "Document Type",
+ "URL": "URL",
+ "PDF": "PDF File"
+ },
"URL": {
"LABEL": "URL",
"PLACEHOLDER": "Enter the URL of the document",
"ERROR": "Please provide a valid URL for the document"
},
+ "PDF_FILE": {
+ "LABEL": "PDF File",
+ "CHOOSE_FILE": "Choose PDF file",
+ "ERROR": "Please select a PDF file",
+ "HELP_TEXT": "Maximum file size: 10MB",
+ "INVALID_TYPE": "Please select a valid PDF file",
+ "TOO_LARGE": "File size exceeds 10MB limit"
+ },
+ "NAME": {
+ "LABEL": "Document Name (Optional)",
+ "PLACEHOLDER": "Enter a name for the document"
+ },
"ASSISTANT": {
"LABEL": "Assistant",
"PLACEHOLDER": "Select the assistant",
@@ -744,6 +761,7 @@
"SELECTED": "{count} selected",
"SELECT_ALL": "Select all ({count})",
"UNSELECT_ALL": "Unselect all ({count})",
+ "SEARCH_PLACEHOLDER": "Search FAQs...",
"BULK_APPROVE_BUTTON": "Approve",
"BULK_DELETE_BUTTON": "Delete",
"BULK_APPROVE": {
diff --git a/app/javascript/dashboard/i18n/locale/hy/mfa.json b/app/javascript/dashboard/i18n/locale/hy/mfa.json
new file mode 100644
index 000000000..f7556fdcf
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/hy/mfa.json
@@ -0,0 +1,106 @@
+{
+ "MFA_SETTINGS": {
+ "TITLE": "Two-Factor Authentication",
+ "SUBTITLE": "Secure your account with TOTP-based authentication",
+ "DESCRIPTION": "Add an extra layer of security to your account using a time-based one-time password (TOTP)",
+ "STATUS_TITLE": "Authentication Status",
+ "STATUS_DESCRIPTION": "Manage your two-factor authentication settings and backup recovery codes",
+ "ENABLED": "Enabled",
+ "DISABLED": "Disabled",
+ "STATUS_ENABLED": "Two-factor authentication is active",
+ "STATUS_ENABLED_DESC": "Your account is protected with an additional layer of security",
+ "ENABLE_BUTTON": "Enable Two-Factor Authentication",
+ "ENHANCE_SECURITY": "Enhance Your Account Security",
+ "ENHANCE_SECURITY_DESC": "Two-factor authentication adds an extra layer of security by requiring a verification code from your authenticator app in addition to your password.",
+ "SETUP": {
+ "STEP_NUMBER_1": "1",
+ "STEP_NUMBER_2": "2",
+ "STEP1_TITLE": "Scan QR Code with Your Authenticator App",
+ "STEP1_DESCRIPTION": "Use Google Authenticator, Authy, or any TOTP-compatible app",
+ "LOADING_QR": "Loading...",
+ "MANUAL_ENTRY": "Can't scan? Enter code manually",
+ "SECRET_KEY": "Secret Key",
+ "COPY": "Copy",
+ "ENTER_CODE": "Enter the 6-digit code from your authenticator app",
+ "ENTER_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify & Continue",
+ "CANCEL": "Cancel",
+ "ERROR_STARTING": "MFA not enabled. Please contact administrator.",
+ "INVALID_CODE": "Invalid verification code",
+ "SECRET_COPIED": "Secret key copied to clipboard",
+ "SUCCESS": "Two-factor authentication has been enabled successfully"
+ },
+ "BACKUP": {
+ "TITLE": "Save Your Backup Codes",
+ "DESCRIPTION": "Keep these codes safe. Each can be used once if you lose access to your authenticator",
+ "IMPORTANT": "Important:",
+ "IMPORTANT_NOTE": " Save these codes in a secure location. You won't be able to see them again.",
+ "DOWNLOAD": "Download",
+ "COPY_ALL": "Copy All",
+ "CONFIRM": "I have saved my backup codes in a secure location and understand that I won't be able to see them again",
+ "COMPLETE_SETUP": "Complete Setup",
+ "CODES_COPIED": "Backup codes copied to clipboard"
+ },
+ "MANAGEMENT": {
+ "BACKUP_CODES": "Backup Codes",
+ "BACKUP_CODES_DESC": "Generate new codes if you've lost or used your existing ones",
+ "REGENERATE": "Regenerate Backup Codes",
+ "DISABLE_MFA": "Disable 2FA",
+ "DISABLE_MFA_DESC": "Remove two-factor authentication from your account",
+ "DISABLE_BUTTON": "Disable Two-Factor Authentication"
+ },
+ "DISABLE": {
+ "TITLE": "Disable Two-Factor Authentication",
+ "DESCRIPTION": "You'll need to enter your password and a verification code to disable two-factor authentication.",
+ "PASSWORD": "Password",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Disable 2FA",
+ "CANCEL": "Cancel",
+ "SUCCESS": "Two-factor authentication has been disabled",
+ "ERROR": "Failed to disable MFA. Please check your credentials."
+ },
+ "REGENERATE": {
+ "TITLE": "Regenerate Backup Codes",
+ "DESCRIPTION": "This will invalidate your existing backup codes and generate new ones. Enter your verification code to continue.",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Generate New Codes",
+ "CANCEL": "Cancel",
+ "NEW_CODES_TITLE": "New Backup Codes Generated",
+ "NEW_CODES_DESC": "Your old backup codes have been invalidated. Save these new codes in a secure location.",
+ "CODES_IMPORTANT": "Important:",
+ "CODES_IMPORTANT_NOTE": " Each code can only be used once. Save them before closing this window.",
+ "DOWNLOAD_CODES": "Download Codes",
+ "COPY_ALL_CODES": "Copy All Codes",
+ "CODES_SAVED": "I've Saved My Codes",
+ "SUCCESS": "New backup codes have been generated",
+ "ERROR": "Failed to regenerate backup codes"
+ }
+ },
+ "MFA_VERIFICATION": {
+ "TITLE": "Two-Factor Authentication",
+ "DESCRIPTION": "Enter your verification code to continue",
+ "AUTHENTICATOR_APP": "Authenticator App",
+ "BACKUP_CODE": "Backup Code",
+ "ENTER_OTP_CODE": "Enter 6-digit code from your authenticator app",
+ "ENTER_BACKUP_CODE": "Enter one of your backup codes",
+ "BACKUP_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify",
+ "TRY_ANOTHER_METHOD": "Try another verification method",
+ "CANCEL_LOGIN": "Cancel and return to login",
+ "HELP_TEXT": "Having trouble signing in?",
+ "LEARN_MORE": "Learn more about 2FA",
+ "HELP_MODAL": {
+ "TITLE": "Two-Factor Authentication Help",
+ "AUTHENTICATOR_TITLE": "Using an Authenticator App",
+ "AUTHENTICATOR_DESC": "Open your authenticator app (Google Authenticator, Authy, etc.) and enter the 6-digit code shown for your account.",
+ "BACKUP_TITLE": "Using a Backup Code",
+ "BACKUP_DESC": "If you don't have access to your authenticator app, you can use one of the backup codes you saved when setting up 2FA. Each code can only be used once.",
+ "CONTACT_TITLE": "Need More Help?",
+ "CONTACT_DESC_CLOUD": "If you've lost access to both your authenticator app and backup codes, please reach out to Chatwoot support for assistance.",
+ "CONTACT_DESC_SELF_HOSTED": "If you've lost access to both your authenticator app and backup codes, please contact your administrator for assistance."
+ },
+ "VERIFICATION_FAILED": "Verification failed. Please try again."
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/hy/settings.json b/app/javascript/dashboard/i18n/locale/hy/settings.json
index d547538db..9ddc3b805 100644
--- a/app/javascript/dashboard/i18n/locale/hy/settings.json
+++ b/app/javascript/dashboard/i18n/locale/hy/settings.json
@@ -51,6 +51,13 @@
"LARGER": "Larger",
"EXTRA_LARGE": "Extra Large"
}
+ },
+ "LANGUAGE": {
+ "TITLE": "Preferred Language",
+ "NOTE": "Choose the language you want to use.",
+ "UPDATE_SUCCESS": "Your Language settings have been updated successfully",
+ "UPDATE_ERROR": "There is an error while updating the language settings, please try again",
+ "USE_ACCOUNT_DEFAULT": "Use account default"
}
},
"MESSAGE_SIGNATURE_SECTION": {
@@ -73,6 +80,11 @@
"NOTE": "Updating your password would reset your logins in multiple devices.",
"BTN_TEXT": "Change password"
},
+ "SECURITY_SECTION": {
+ "TITLE": "Security",
+ "NOTE": "Manage additional security features for your account.",
+ "MFA_BUTTON": "Manage Two-Factor Authentication"
+ },
"ACCESS_TOKEN": {
"TITLE": "Access Token",
"NOTE": "This token can be used if you are building an API based integration",
@@ -330,6 +342,7 @@
"REPORTS_LABEL": "Labels",
"REPORTS_INBOX": "Inbox",
"REPORTS_TEAM": "Team",
+ "AGENT_ASSIGNMENT": "Agent Assignment",
"SET_AVAILABILITY_TITLE": "Set yourself as",
"SET_YOUR_AVAILABILITY": "Set your availability",
"SLA": "SLA",
@@ -350,7 +363,8 @@
"INFO_TEXT": "Let the system automatically mark you offline when you aren't using the app or dashboard.",
"INFO_SHORT": "Automatically mark offline when you aren't using the app."
},
- "DOCS": "Read docs"
+ "DOCS": "Read docs",
+ "SECURITY": "Security"
},
"BILLING_SETTINGS": {
"TITLE": "Billing",
@@ -382,6 +396,77 @@
},
"NO_BILLING_USER": "Your billing account is being configured. Please refresh the page and try again."
},
+ "SECURITY_SETTINGS": {
+ "TITLE": "Security",
+ "DESCRIPTION": "Manage your account security settings.",
+ "LINK_TEXT": "Learn more about SAML SSO",
+ "SAML": {
+ "TITLE": "SAML SSO",
+ "NOTE": "Configure SAML single sign-on for your account. Users will authenticate through your identity provider instead of using email/password.",
+ "ACS_URL": {
+ "LABEL": "ACS URL",
+ "TOOLTIP": "Assertion Consumer Service URL - Configure this URL in your IdP as the destination for SAML responses"
+ },
+ "SSO_URL": {
+ "LABEL": "SSO URL",
+ "HELP": "The URL where SAML authentication requests will be sent",
+ "PLACEHOLDER": "https://your-idp.com/saml/sso"
+ },
+ "CERTIFICATE": {
+ "LABEL": "Signing certificate in PEM format",
+ "HELP": "The public certificate from your identity provider used to verify SAML responses",
+ "PLACEHOLDER": "-----BEGIN CERTIFICATE-----\nMIIC..."
+ },
+ "FINGERPRINT": {
+ "LABEL": "Fingerprint",
+ "TOOLTIP": "SHA-1 fingerprint of the certificate - Use this to verify the certificate in your IdP configuration"
+ },
+ "COPY_SUCCESS": "Copied to clipboard",
+ "SP_ENTITY_ID": {
+ "LABEL": "SP Entity ID",
+ "HELP": "Unique identifier for this application as a service provider (auto-generated).",
+ "TOOLTIP": "Unique identifier for Chatwoot as the Service Provider - Configure this in your IdP settings"
+ },
+ "IDP_ENTITY_ID": {
+ "LABEL": "Identity Provider Entity ID",
+ "HELP": "Unique identifier for your identity provider (usually found in IdP configuration)",
+ "PLACEHOLDER": "https://your-idp.com/saml"
+ },
+ "UPDATE_BUTTON": "Update SAML Settings",
+ "API": {
+ "SUCCESS": "SAML settings updated successfully",
+ "ERROR": "Failed to update SAML settings",
+ "ERROR_LOADING": "Failed to load SAML settings",
+ "DISABLED": "SAML settings disabled successfully"
+ },
+ "VALIDATION": {
+ "REQUIRED_FIELDS": "SSO URL, Identity Provider Entity ID, and Certificate are required fields",
+ "SSO_URL_ERROR": "Please enter a valid SSO URL",
+ "CERTIFICATE_ERROR": "Certificate is required",
+ "IDP_ENTITY_ID_ERROR": "Identity Provider Entity ID is required"
+ },
+ "ENTERPRISE_PAYWALL": {
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade to an Enterprise plan to access SAML single sign-on and other advanced security features.",
+ "ASK_ADMIN": "Please reach out to your administrator for the upgrade."
+ },
+ "PAYWALL": {
+ "TITLE": "Upgrade to enable SAML SSO",
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade your plan to get access to SAML single sign-on and other advanced features.",
+ "UPGRADE_NOW": "Upgrade now",
+ "CANCEL_ANYTIME": "You can change or cancel your plan anytime"
+ },
+ "ATTRIBUTE_MAPPING": {
+ "TITLE": "SAML Attribute Setup",
+ "DESCRIPTION": "The following attribute mappings must be configured in your identity provider"
+ },
+ "INFO_SECTION": {
+ "TITLE": "Service Provider Information",
+ "TOOLTIP": "Copy these values and configure them in your Identity Provider to establish the SAML connection"
+ }
+ }
+ },
"CREATE_ACCOUNT": {
"NO_ACCOUNT_WARNING": "Uh oh! We could not find any Chatwoot accounts. Please create a new account to continue.",
"NEW_ACCOUNT": "New Account",
@@ -418,5 +503,255 @@
"SWITCH_TO_REPLY": "Switch to Reply",
"TOGGLE_SNOOZE_DROPDOWN": "Toggle snooze dropdown"
}
+ },
+ "ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent assignment",
+ "DESCRIPTION": "Define policies to effectively manage workload and route conversations based on the needs of inboxes and agents. Learn more here"
+ },
+ "ASSIGNMENT_POLICY": {
+ "TITLE": "Assignment policy",
+ "DESCRIPTION": "Manage how conversations get assigned in inboxes.",
+ "FEATURES": [
+ "Assign by conversations evenly or by available capacity",
+ "Add fair distribution rules to avoid overloading any agent",
+ "Add inboxes to a policy - one policy per inbox"
+ ]
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "TITLE": "Agent capacity policy",
+ "DESCRIPTION": "Manage workload for agents.",
+ "FEATURES": [
+ "Define maximum conversations per inbox",
+ "Create exceptions based on labels and time",
+ "Add agents to a policy - one policy per agent"
+ ]
+ }
+ },
+ "AGENT_ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Assignment policy",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "ORDER": "Order",
+ "PRIORITY": "Priority",
+ "ACTIVE": "Active",
+ "INACTIVE": "Inactive",
+ "POPOVER": "Added inboxes",
+ "EDIT": "Edit"
+ },
+ "NO_RECORDS_FOUND": "No assignment policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create assignment policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy created successfully",
+ "ERROR_MESSAGE": "Failed to create assignment policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit assignment policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_INBOX_DIALOG": {
+ "TITLE": "Add inbox",
+ "DESCRIPTION": "{inboxName} inbox is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Cancel"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update assignment policy"
+ },
+ "INBOX_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Inbox added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Inbox removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove inbox from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Description:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "STATUS": {
+ "LABEL": "Status:",
+ "PLACEHOLDER": "Select status",
+ "ACTIVE": "Policy is active",
+ "INACTIVE": "Policy is inactive"
+ },
+ "ASSIGNMENT_ORDER": {
+ "LABEL": "Assignment order",
+ "ROUND_ROBIN": {
+ "LABEL": "Round robin",
+ "DESCRIPTION": "Assign conversations evenly among agents."
+ },
+ "BALANCED": {
+ "LABEL": "Balanced",
+ "DESCRIPTION": "Assign conversations based on available capacity."
+ }
+ },
+ "ASSIGNMENT_PRIORITY": {
+ "LABEL": "Assignment priority",
+ "EARLIEST_CREATED": {
+ "LABEL": "Earliest created",
+ "DESCRIPTION": "The conversation that was created first gets assigned first."
+ },
+ "LONGEST_WAITING": {
+ "LABEL": "Longest waiting",
+ "DESCRIPTION": "The conversation waiting the longest gets assigned first."
+ }
+ },
+ "FAIR_DISTRIBUTION": {
+ "LABEL": "Fair distribution policy",
+ "DESCRIPTION": "Set the maximum number of conversations that can be assigned per agent within a time window to avoid overloading any one agent. This required field defaults to 100 conversations per hour.",
+ "INPUT_MAX": "Assign max",
+ "DURATION": "Conversations per agent in every"
+ },
+ "INBOXES": {
+ "LABEL": "Added inboxes",
+ "DESCRIPTION": "Add inboxes for which this policy will be applicable.",
+ "ADD_BUTTON": "Add inbox",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select inboxes to add",
+ "ADD_BUTTON": "Add"
+ },
+ "EMPTY_STATE": "No inboxes added to this policy, add an inbox to get started",
+ "API": {
+ "SUCCESS_MESSAGE": "Inbox successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Assignment policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete assignment policy"
+ }
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent capacity",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "POPOVER": "Added agents",
+ "EDIT": "Edit"
+ },
+ "NO_RECORDS_FOUND": "No agent capacity policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create agent capacity policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy created successfully",
+ "ERROR_MESSAGE": "Failed to create agent capacity policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit agent capacity policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_AGENT_DIALOG": {
+ "TITLE": "Add agent",
+ "DESCRIPTION": "{agentName} is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Cancel"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update agent capacity policy"
+ },
+ "AGENT_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Agent added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Agent removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove agent from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Description:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "INBOX_CAPACITY_LIMIT": {
+ "LABEL": "Inbox capacity limits",
+ "ADD_BUTTON": "Add inbox",
+ "FIELD": {
+ "SELECT_INBOX": "Select inbox",
+ "MAX_CONVERSATIONS": "Max conversations",
+ "SET_LIMIT": "Set limit"
+ },
+ "EMPTY_STATE": "No inbox limit set"
+ },
+ "EXCLUSION_RULES": {
+ "LABEL": "Exclusion rules",
+ "DESCRIPTION": "Conversations that satisfy the following conditions would not count towards agent capacity",
+ "TAGS": {
+ "LABEL": "Exclude conversations tagged with specific labels",
+ "ADD_TAG": "add tag",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select tags to add"
+ },
+ "EMPTY_STATE": "No tags added to this policy."
+ },
+ "DURATION": {
+ "LABEL": "Exclude conversations older than a specified duration",
+ "PLACEHOLDER": "Set time"
+ }
+ },
+ "USERS": {
+ "LABEL": "Assigned agents",
+ "DESCRIPTION": "Add agents for which this policy will be applicable.",
+ "ADD_BUTTON": "Add agent",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select agents to add",
+ "ADD_BUTTON": "Add"
+ },
+ "EMPTY_STATE": "No agents added",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Agent capacity policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete agent capacity policy"
+ }
+ },
+ "DELETE_POLICY": {
+ "TITLE": "Delete policy",
+ "DESCRIPTION": "Are you sure you want to delete this policy? This action cannot be undone.",
+ "CONFIRM_BUTTON_LABEL": "Delete",
+ "CANCEL_BUTTON_LABEL": "Cancel"
+ }
}
}
diff --git a/app/javascript/dashboard/i18n/locale/hy/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/hy/whatsappTemplates.json
index 5f53faaa8..cf28312dc 100644
--- a/app/javascript/dashboard/i18n/locale/hy/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/hy/whatsappTemplates.json
@@ -40,6 +40,7 @@
"BUTTON_LABEL": "Button {index}",
"COUPON_CODE": "Enter coupon code (max 15 chars)",
"MEDIA_URL_LABEL": "Enter {type} URL",
+ "DOCUMENT_NAME_PLACEHOLDER": "Enter document filename (e.g., Invoice_2025.pdf)",
"BUTTON_PARAMETER": "Enter button parameter"
}
}
diff --git a/app/javascript/dashboard/i18n/locale/id/automation.json b/app/javascript/dashboard/i18n/locale/id/automation.json
index e8a688248..155d75b84 100644
--- a/app/javascript/dashboard/i18n/locale/id/automation.json
+++ b/app/javascript/dashboard/i18n/locale/id/automation.json
@@ -177,7 +177,8 @@
"REFERER_LINK": "Referrer Link",
"ASSIGNEE_NAME": "Assignee",
"TEAM_NAME": "Tim",
- "PRIORITY": "Prioritas"
+ "PRIORITY": "Prioritas",
+ "LABELS": "Label"
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/id/contact.json b/app/javascript/dashboard/i18n/locale/id/contact.json
index e0887245c..14e25926f 100644
--- a/app/javascript/dashboard/i18n/locale/id/contact.json
+++ b/app/javascript/dashboard/i18n/locale/id/contact.json
@@ -610,6 +610,15 @@
"SEND_MESSAGE": "Kirim Pesan"
}
},
+ "TWILIO_OPTIONS": {
+ "LABEL": "Select template",
+ "SEARCH_PLACEHOLDER": "Search templates",
+ "EMPTY_STATE": "No templates found",
+ "TEMPLATE_PARSER": {
+ "BACK": "Kembali",
+ "SEND_MESSAGE": "Kirim Pesan"
+ }
+ },
"ACTION_BUTTONS": {
"DISCARD": "Discard",
"SEND": "Send ({keyCode})"
diff --git a/app/javascript/dashboard/i18n/locale/id/contactFilters.json b/app/javascript/dashboard/i18n/locale/id/contactFilters.json
index 202e264c6..8360a0639 100644
--- a/app/javascript/dashboard/i18n/locale/id/contactFilters.json
+++ b/app/javascript/dashboard/i18n/locale/id/contactFilters.json
@@ -48,7 +48,8 @@
"CREATED_AT": "Dibuat pada",
"LAST_ACTIVITY": "Aktivitas Terakhir",
"REFERER_LINK": "Tautan Referrer",
- "BLOCKED": "Blocked"
+ "BLOCKED": "Blocked",
+ "LABELS": "Label"
},
"GROUPS": {
"STANDARD_FILTERS": "Filter Standar",
diff --git a/app/javascript/dashboard/i18n/locale/id/contentTemplates.json b/app/javascript/dashboard/i18n/locale/id/contentTemplates.json
new file mode 100644
index 000000000..82385d2de
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/id/contentTemplates.json
@@ -0,0 +1,51 @@
+{
+ "CONTENT_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Twilio Templates",
+ "SUBTITLE": "Select the Twilio template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Cari Templat",
+ "NO_TEMPLATES_FOUND": "Tidak ditemukan templat untuk",
+ "NO_CONTENT": "No content",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Kategori",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No Twilio templates available. Click refresh to sync templates from Twilio.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Bahasa",
+ "TEMPLATE_BODY": "Isi Templat",
+ "CATEGORY": "Kategori"
+ },
+ "TYPES": {
+ "MEDIA": "Media",
+ "QUICK_REPLY": "Quick Reply",
+ "TEXT": "Teks"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variabel",
+ "LANGUAGE": "Bahasa",
+ "CATEGORY": "Kategori",
+ "VARIABLE_PLACEHOLDER": "Masukkan nilai {variable}",
+ "GO_BACK_LABEL": "Kembali",
+ "SEND_MESSAGE_LABEL": "Kirim Pesan",
+ "FORM_ERROR_MESSAGE": "Harap isi semua variabel sebelum mengirim",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "MEDIA_URL_LABEL": "Enter full media URL",
+ "MEDIA_URL_PLACEHOLDER": "https://example.com/image.jpg"
+ },
+ "FORM": {
+ "BACK_BUTTON": "Kembali",
+ "SEND_MESSAGE_BUTTON": "Kirim Pesan"
+ }
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/id/conversation.json b/app/javascript/dashboard/i18n/locale/id/conversation.json
index 3b5b1eaa4..e9197b164 100644
--- a/app/javascript/dashboard/i18n/locale/id/conversation.json
+++ b/app/javascript/dashboard/i18n/locale/id/conversation.json
@@ -35,6 +35,11 @@
"API_HOURS_WINDOW": "You can only reply to this conversation within {hours} hours",
"NOT_ASSIGNED_TO_YOU": "Percakapan ini tidak ditugaskan kepada Anda. Apakah Anda ingin menugaskan percakapan ini kepada diri Anda?",
"ASSIGN_TO_ME": "Tugaskan kepada saya",
+ "BOT_HANDOFF_MESSAGE": "You are responding to a conversation which is currently handled by an assistant or a bot.",
+ "BOT_HANDOFF_ACTION": "Mark open and assign to you",
+ "BOT_HANDOFF_REOPEN_ACTION": "Mark conversation open",
+ "BOT_HANDOFF_SUCCESS": "Conversation has been handed over to you",
+ "BOT_HANDOFF_ERROR": "Failed to take over the conversation. Please try again.",
"TWILIO_WHATSAPP_CAN_REPLY": "Anda hanya dapat membalas percakapan ini menggunakan pesan template karena",
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "Pembatasan jendela pesan 24 jam",
"OLD_INSTAGRAM_INBOX_REPLY_BANNER": "This Instagram account was migrated to the new Instagram channel inbox. All new messages will show up there. You won’t be able to send messages from this conversation anymore.",
@@ -66,6 +71,17 @@
"SHOW_LABELS": "Tampilkan label",
"HIDE_LABELS": "Sembunyikan label"
},
+ "VOICE_CALL": {
+ "INCOMING_CALL": "Incoming call",
+ "OUTGOING_CALL": "Outgoing call",
+ "CALL_IN_PROGRESS": "Call in progress",
+ "NO_ANSWER": "No answer",
+ "MISSED_CALL": "Missed call",
+ "CALL_ENDED": "Call ended",
+ "NOT_ANSWERED_YET": "Not answered yet",
+ "THEY_ANSWERED": "They answered",
+ "YOU_ANSWERED": "You answered"
+ },
"HEADER": {
"RESOLVE_ACTION": "Menyelesaikan",
"REOPEN_ACTION": "Buka Kembali",
diff --git a/app/javascript/dashboard/i18n/locale/id/helpCenter.json b/app/javascript/dashboard/i18n/locale/id/helpCenter.json
index c063f8900..db4b51f74 100644
--- a/app/javascript/dashboard/i18n/locale/id/helpCenter.json
+++ b/app/javascript/dashboard/i18n/locale/id/helpCenter.json
@@ -741,7 +741,8 @@
"LIVE_CHAT_WIDGET": {
"LABEL": "Live chat widget",
"PLACEHOLDER": "Select live chat widget",
- "HELP_TEXT": "Select a live chat widget that will appear on your help center"
+ "HELP_TEXT": "Select a live chat widget that will appear on your help center",
+ "NONE_OPTION": "No widget"
},
"BRAND_COLOR": {
"LABEL": "Brand color"
@@ -807,6 +808,58 @@
"ERROR_MESSAGE": "Unable to update portal"
}
}
+ },
+ "PDF_UPLOAD": {
+ "TITLE": "Upload PDF Document",
+ "DESCRIPTION": "Upload a PDF document to automatically generate FAQs using AI",
+ "DRAG_DROP_TEXT": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "ADDITIONAL_CONTEXT_LABEL": "Additional Context (Optional)",
+ "ADDITIONAL_CONTEXT_PLACEHOLDER": "Provide any additional context or instructions for FAQ generation...",
+ "UPLOADING": "Mengunggah...",
+ "UPLOAD": "Upload & Process",
+ "CANCEL": "Batalkan",
+ "ERROR_INVALID_TYPE": "Please select a valid PDF file",
+ "ERROR_FILE_TOO_LARGE": "File size must be less than 512MB",
+ "ERROR_UPLOAD_FAILED": "Failed to upload PDF. Please try again."
+ },
+ "PDF_DOCUMENTS": {
+ "TITLE": "PDF Documents",
+ "DESCRIPTION": "Manage uploaded PDF documents and generate FAQs from them",
+ "UPLOAD_PDF": "Upload PDF",
+ "UPLOAD_FIRST_PDF": "Upload your first PDF",
+ "UPLOADED_BY": "Uploaded by",
+ "GENERATE_FAQS": "Generate FAQs",
+ "GENERATING": "Menghasilkan...",
+ "CONFIRM_DELETE": "Are you sure you want to delete {filename}?",
+ "EMPTY_STATE": {
+ "TITLE": "No PDF documents yet",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQs using AI"
+ },
+ "STATUS": {
+ "UPLOADED": "Ready",
+ "PROCESSING": "Processing",
+ "PROCESSED": "Selesai",
+ "FAILED": "Failed"
+ }
+ },
+ "CONTENT_GENERATION": {
+ "TITLE": "Content Generation",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQ content using AI",
+ "UPLOAD_TITLE": "Upload PDF Document",
+ "DRAG_DROP": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "UPLOADING": "Processing document...",
+ "UPLOAD_SUCCESS": "Document processed successfully!",
+ "UPLOAD_ERROR": "Failed to upload document. Please try again.",
+ "INVALID_FILE_TYPE": "Please select a valid PDF file",
+ "FILE_TOO_LARGE": "File size must be less than 512MB",
+ "GENERATED_CONTENT": "Generated FAQ Content",
+ "PUBLISH_SELECTED": "Publish Selected",
+ "PUBLISHING": "Publishing...",
+ "FROM_DOCUMENT": "From document",
+ "NO_CONTENT": "No generated content available. Upload a PDF document to get started.",
+ "LOADING": "Loading generated content..."
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/id/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/id/inboxMgmt.json
index 9f19e3d44..94e1bfc77 100644
--- a/app/javascript/dashboard/i18n/locale/id/inboxMgmt.json
+++ b/app/javascript/dashboard/i18n/locale/id/inboxMgmt.json
@@ -272,8 +272,8 @@
},
"SUBMIT_BUTTON": "Buat Saluran WhatsApp",
"EMBEDDED_SIGNUP": {
- "TITLE": "Quick Setup with Meta",
- "DESC": "You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
+ "TITLE": "Quick setup with Meta",
+ "DESC": "Use the WhatsApp Embedded Signup flow to quickly connect new numbers. You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
"BENEFITS": {
"TITLE": "Benefits of Embedded Signup:",
"EASY_SETUP": "No manual configuration required",
@@ -281,9 +281,8 @@
"AUTO_CONFIG": "Automatic webhook and phone number configuration"
},
"LEARN_MORE": {
- "TEXT": "To learn more about integrated signup, pricing, and limitations, visit",
- "LINK_TEXT": "this link.",
- "LINK_URL": "https://developers.facebook.com/docs/whatsapp/embedded-signup/custom-flows/onboarding-business-app-users#limitations"
+ "TEXT": "To learn more about integrated signup, pricing, and limitations, visit {link}.",
+ "LINK_TEXT": "this link"
},
"SUBMIT_BUTTON": "Connect with WhatsApp Business",
"AUTH_PROCESSING": "Authenticating with Meta",
@@ -296,7 +295,9 @@
"INVALID_BUSINESS_DATA": "Invalid business data received from Facebook. Please try again.",
"SIGNUP_ERROR": "Signup error occurred",
"AUTH_NOT_COMPLETED": "Authentication not completed. Please restart the process.",
- "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured"
+ "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured",
+ "MANUAL_FALLBACK": "If your number is already connected to the WhatsApp Business Platform (API), or if you’re a tech provider onboarding your own number, please use the {link} flow",
+ "MANUAL_LINK_TEXT": "manual setup flow"
},
"API": {
"ERROR_MESSAGE": "Kami tidak dapat menyimpan saluran WhatsApp"
@@ -424,7 +425,51 @@
},
"AUTH": {
"TITLE": "Pilih sebuah channel",
- "DESC": "Chatwoot supports live-chat widgets, Facebook Messenger, Twitter profiles, WhatsApp, Emails, etc., as channels. If you want to build a custom channel, you can create it using the API channel. To get started, choose one of the channels below."
+ "DESC": "Chatwoot supports live-chat widgets, Facebook Messenger, Twitter profiles, WhatsApp, Emails, etc., as channels. If you want to build a custom channel, you can create it using the API channel. To get started, choose one of the channels below.",
+ "TITLE_NEXT": "Complete the setup",
+ "TITLE_FINISH": "Voilà!",
+ "CHANNEL": {
+ "WEBSITE": {
+ "TITLE": "Website",
+ "DESCRIPTION": "Create a live-chat widget"
+ },
+ "FACEBOOK": {
+ "TITLE": "Facebook",
+ "DESCRIPTION": "Connect your Facebook page"
+ },
+ "WHATSAPP": {
+ "TITLE": "WhatsApp",
+ "DESCRIPTION": "Support your customers on WhatsApp"
+ },
+ "EMAIL": {
+ "TITLE": "Email",
+ "DESCRIPTION": "Connect with Gmail, Outlook, or other providers"
+ },
+ "SMS": {
+ "TITLE": "SMS",
+ "DESCRIPTION": "Integrate SMS channel with Twilio or bandwidth"
+ },
+ "API": {
+ "TITLE": "API",
+ "DESCRIPTION": "Make a custom channel using our API"
+ },
+ "TELEGRAM": {
+ "TITLE": "Telegram",
+ "DESCRIPTION": "Configure Telegram channel using Bot token"
+ },
+ "LINE": {
+ "TITLE": "Line",
+ "DESCRIPTION": "Integrate your Line channel"
+ },
+ "INSTAGRAM": {
+ "TITLE": "Instagram",
+ "DESCRIPTION": "Connect your instagram account"
+ },
+ "VOICE": {
+ "TITLE": "Voice",
+ "DESCRIPTION": "Integrate with Twilio Voice"
+ }
+ }
},
"AGENTS": {
"TITLE": "Agen",
@@ -478,7 +523,10 @@
"MESSAGE": "Anda sekarang dapat menangani pelanggan Anda melalui Channel baru Anda. Selamat mendukung",
"BUTTON_TEXT": "Pergi ke Kotak Masuk",
"MORE_SETTINGS": "Pengaturan lebih lengkap",
- "WEBSITE_SUCCESS": "Anda telah berhasil menyelesaikan pembuatan channel website. Salin kode yang ditunjukkan di bawah ini dan tempelkan di website Anda. Saat pelanggan menggunakan live chat, percakapan tersebut secara otomatis akan muncul di kotak masuk Anda."
+ "WEBSITE_SUCCESS": "Anda telah berhasil menyelesaikan pembuatan channel website. Salin kode yang ditunjukkan di bawah ini dan tempelkan di website Anda. Saat pelanggan menggunakan live chat, percakapan tersebut secara otomatis akan muncul di kotak masuk Anda.",
+ "WHATSAPP_QR_INSTRUCTION": "Scan the QR code above to quickly test your WhatsApp inbox",
+ "MESSENGER_QR_INSTRUCTION": "Scan the QR code above to quickly test your Facebook Messenger inbox",
+ "TELEGRAM_QR_INSTRUCTION": "Scan the QR code above to quickly test your Telegram inbox"
},
"REAUTH": "Otorisasi ulang",
"VIEW": "Lihat",
@@ -868,9 +916,18 @@
"SCRIPT_SETTINGS": "\n window.chatwootSettings = {options};"
},
"EMAIL_PROVIDERS": {
- "MICROSOFT": "Microsoft",
- "GOOGLE": "Google",
- "OTHER_PROVIDERS": "Penyedia Lain"
+ "MICROSOFT": {
+ "TITLE": "Microsoft",
+ "DESCRIPTION": "Connect with Microsoft"
+ },
+ "GOOGLE": {
+ "TITLE": "Google",
+ "DESCRIPTION": "Connect with Google"
+ },
+ "OTHER_PROVIDERS": {
+ "TITLE": "Penyedia Lain",
+ "DESCRIPTION": "Connect with Other Providers"
+ }
},
"CHANNELS": {
"MESSENGER": "Messenger",
diff --git a/app/javascript/dashboard/i18n/locale/id/integrations.json b/app/javascript/dashboard/i18n/locale/id/integrations.json
index 98e186693..9dd89b13b 100644
--- a/app/javascript/dashboard/i18n/locale/id/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/id/integrations.json
@@ -704,11 +704,28 @@
"ERROR_MESSAGE": "There was an error creating the document, please try again."
},
"FORM": {
+ "TYPE": {
+ "LABEL": "Document Type",
+ "URL": "URL",
+ "PDF": "PDF File"
+ },
"URL": {
"LABEL": "URL",
"PLACEHOLDER": "Enter the URL of the document",
"ERROR": "Please provide a valid URL for the document"
},
+ "PDF_FILE": {
+ "LABEL": "PDF File",
+ "CHOOSE_FILE": "Choose PDF file",
+ "ERROR": "Please select a PDF file",
+ "HELP_TEXT": "Maximum file size: 10MB",
+ "INVALID_TYPE": "Please select a valid PDF file",
+ "TOO_LARGE": "File size exceeds 10MB limit"
+ },
+ "NAME": {
+ "LABEL": "Document Name (Optional)",
+ "PLACEHOLDER": "Enter a name for the document"
+ },
"ASSISTANT": {
"LABEL": "Assistant",
"PLACEHOLDER": "Select the assistant",
@@ -744,6 +761,7 @@
"SELECTED": "{count} selected",
"SELECT_ALL": "Select all ({count})",
"UNSELECT_ALL": "Unselect all ({count})",
+ "SEARCH_PLACEHOLDER": "Search FAQs...",
"BULK_APPROVE_BUTTON": "Approve",
"BULK_DELETE_BUTTON": "Hapus",
"BULK_APPROVE": {
diff --git a/app/javascript/dashboard/i18n/locale/id/mfa.json b/app/javascript/dashboard/i18n/locale/id/mfa.json
new file mode 100644
index 000000000..dee8f80e1
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/id/mfa.json
@@ -0,0 +1,106 @@
+{
+ "MFA_SETTINGS": {
+ "TITLE": "Two-Factor Authentication",
+ "SUBTITLE": "Secure your account with TOTP-based authentication",
+ "DESCRIPTION": "Add an extra layer of security to your account using a time-based one-time password (TOTP)",
+ "STATUS_TITLE": "Authentication Status",
+ "STATUS_DESCRIPTION": "Manage your two-factor authentication settings and backup recovery codes",
+ "ENABLED": "Diaktifkan",
+ "DISABLED": "Nonaktif",
+ "STATUS_ENABLED": "Two-factor authentication is active",
+ "STATUS_ENABLED_DESC": "Your account is protected with an additional layer of security",
+ "ENABLE_BUTTON": "Enable Two-Factor Authentication",
+ "ENHANCE_SECURITY": "Enhance Your Account Security",
+ "ENHANCE_SECURITY_DESC": "Two-factor authentication adds an extra layer of security by requiring a verification code from your authenticator app in addition to your password.",
+ "SETUP": {
+ "STEP_NUMBER_1": "1",
+ "STEP_NUMBER_2": "2",
+ "STEP1_TITLE": "Scan QR Code with Your Authenticator App",
+ "STEP1_DESCRIPTION": "Use Google Authenticator, Authy, or any TOTP-compatible app",
+ "LOADING_QR": "Loading...",
+ "MANUAL_ENTRY": "Can't scan? Enter code manually",
+ "SECRET_KEY": "Secret Key",
+ "COPY": "Salin",
+ "ENTER_CODE": "Enter the 6-digit code from your authenticator app",
+ "ENTER_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify & Continue",
+ "CANCEL": "Batalkan",
+ "ERROR_STARTING": "MFA not enabled. Please contact administrator.",
+ "INVALID_CODE": "Invalid verification code",
+ "SECRET_COPIED": "Secret key copied to clipboard",
+ "SUCCESS": "Two-factor authentication has been enabled successfully"
+ },
+ "BACKUP": {
+ "TITLE": "Save Your Backup Codes",
+ "DESCRIPTION": "Keep these codes safe. Each can be used once if you lose access to your authenticator",
+ "IMPORTANT": "Important:",
+ "IMPORTANT_NOTE": " Save these codes in a secure location. You won't be able to see them again.",
+ "DOWNLOAD": "Unduh",
+ "COPY_ALL": "Copy All",
+ "CONFIRM": "I have saved my backup codes in a secure location and understand that I won't be able to see them again",
+ "COMPLETE_SETUP": "Complete Setup",
+ "CODES_COPIED": "Backup codes copied to clipboard"
+ },
+ "MANAGEMENT": {
+ "BACKUP_CODES": "Backup Codes",
+ "BACKUP_CODES_DESC": "Generate new codes if you've lost or used your existing ones",
+ "REGENERATE": "Regenerate Backup Codes",
+ "DISABLE_MFA": "Disable 2FA",
+ "DISABLE_MFA_DESC": "Remove two-factor authentication from your account",
+ "DISABLE_BUTTON": "Disable Two-Factor Authentication"
+ },
+ "DISABLE": {
+ "TITLE": "Disable Two-Factor Authentication",
+ "DESCRIPTION": "You'll need to enter your password and a verification code to disable two-factor authentication.",
+ "PASSWORD": "Kata Sandi",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Disable 2FA",
+ "CANCEL": "Batalkan",
+ "SUCCESS": "Two-factor authentication has been disabled",
+ "ERROR": "Failed to disable MFA. Please check your credentials."
+ },
+ "REGENERATE": {
+ "TITLE": "Regenerate Backup Codes",
+ "DESCRIPTION": "This will invalidate your existing backup codes and generate new ones. Enter your verification code to continue.",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Generate New Codes",
+ "CANCEL": "Batalkan",
+ "NEW_CODES_TITLE": "New Backup Codes Generated",
+ "NEW_CODES_DESC": "Your old backup codes have been invalidated. Save these new codes in a secure location.",
+ "CODES_IMPORTANT": "Important:",
+ "CODES_IMPORTANT_NOTE": " Each code can only be used once. Save them before closing this window.",
+ "DOWNLOAD_CODES": "Download Codes",
+ "COPY_ALL_CODES": "Copy All Codes",
+ "CODES_SAVED": "I've Saved My Codes",
+ "SUCCESS": "New backup codes have been generated",
+ "ERROR": "Failed to regenerate backup codes"
+ }
+ },
+ "MFA_VERIFICATION": {
+ "TITLE": "Two-Factor Authentication",
+ "DESCRIPTION": "Enter your verification code to continue",
+ "AUTHENTICATOR_APP": "Authenticator App",
+ "BACKUP_CODE": "Backup Code",
+ "ENTER_OTP_CODE": "Enter 6-digit code from your authenticator app",
+ "ENTER_BACKUP_CODE": "Enter one of your backup codes",
+ "BACKUP_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify",
+ "TRY_ANOTHER_METHOD": "Try another verification method",
+ "CANCEL_LOGIN": "Cancel and return to login",
+ "HELP_TEXT": "Having trouble signing in?",
+ "LEARN_MORE": "Learn more about 2FA",
+ "HELP_MODAL": {
+ "TITLE": "Two-Factor Authentication Help",
+ "AUTHENTICATOR_TITLE": "Using an Authenticator App",
+ "AUTHENTICATOR_DESC": "Open your authenticator app (Google Authenticator, Authy, etc.) and enter the 6-digit code shown for your account.",
+ "BACKUP_TITLE": "Using a Backup Code",
+ "BACKUP_DESC": "If you don't have access to your authenticator app, you can use one of the backup codes you saved when setting up 2FA. Each code can only be used once.",
+ "CONTACT_TITLE": "Need More Help?",
+ "CONTACT_DESC_CLOUD": "If you've lost access to both your authenticator app and backup codes, please reach out to Chatwoot support for assistance.",
+ "CONTACT_DESC_SELF_HOSTED": "If you've lost access to both your authenticator app and backup codes, please contact your administrator for assistance."
+ },
+ "VERIFICATION_FAILED": "Verification failed. Please try again."
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/id/settings.json b/app/javascript/dashboard/i18n/locale/id/settings.json
index 916b9cc6e..863f7e562 100644
--- a/app/javascript/dashboard/i18n/locale/id/settings.json
+++ b/app/javascript/dashboard/i18n/locale/id/settings.json
@@ -51,6 +51,13 @@
"LARGER": "Larger",
"EXTRA_LARGE": "Extra Large"
}
+ },
+ "LANGUAGE": {
+ "TITLE": "Preferred Language",
+ "NOTE": "Choose the language you want to use.",
+ "UPDATE_SUCCESS": "Your Language settings have been updated successfully",
+ "UPDATE_ERROR": "There is an error while updating the language settings, please try again",
+ "USE_ACCOUNT_DEFAULT": "Use account default"
}
},
"MESSAGE_SIGNATURE_SECTION": {
@@ -73,6 +80,11 @@
"NOTE": "Memperbarui kata sandi Anda akan mengatur ulang login Anda di beberapa perangkat.",
"BTN_TEXT": "Ubah Kata Sandi"
},
+ "SECURITY_SECTION": {
+ "TITLE": "Security",
+ "NOTE": "Manage additional security features for your account.",
+ "MFA_BUTTON": "Manage Two-Factor Authentication"
+ },
"ACCESS_TOKEN": {
"TITLE": "Token Akses",
"NOTE": "Token ini dapat digunakan jika Anda sedang membangun integrasi berbasis API",
@@ -330,6 +342,7 @@
"REPORTS_LABEL": "Label",
"REPORTS_INBOX": "Kotak Masuk",
"REPORTS_TEAM": "Tim",
+ "AGENT_ASSIGNMENT": "Agent Assignment",
"SET_AVAILABILITY_TITLE": "Atur diri anda sebagai",
"SET_YOUR_AVAILABILITY": "Atur ketersediaan Anda",
"SLA": "SLA",
@@ -350,7 +363,8 @@
"INFO_TEXT": "Biarkan sistem secara otomatis menandai Anda offline saat Anda tidak menggunakan aplikasi atau dasbor.",
"INFO_SHORT": "Automatically mark offline when you aren't using the app."
},
- "DOCS": "Baca dokumen"
+ "DOCS": "Baca dokumen",
+ "SECURITY": "Security"
},
"BILLING_SETTINGS": {
"TITLE": "Pembayaran",
@@ -382,6 +396,77 @@
},
"NO_BILLING_USER": "Akun pembayaran Anda sedang dikonfigurasi. Silakan segarkan halaman dan coba lagi."
},
+ "SECURITY_SETTINGS": {
+ "TITLE": "Security",
+ "DESCRIPTION": "Manage your account security settings.",
+ "LINK_TEXT": "Learn more about SAML SSO",
+ "SAML": {
+ "TITLE": "SAML SSO",
+ "NOTE": "Configure SAML single sign-on for your account. Users will authenticate through your identity provider instead of using email/password.",
+ "ACS_URL": {
+ "LABEL": "ACS URL",
+ "TOOLTIP": "Assertion Consumer Service URL - Configure this URL in your IdP as the destination for SAML responses"
+ },
+ "SSO_URL": {
+ "LABEL": "SSO URL",
+ "HELP": "The URL where SAML authentication requests will be sent",
+ "PLACEHOLDER": "https://your-idp.com/saml/sso"
+ },
+ "CERTIFICATE": {
+ "LABEL": "Signing certificate in PEM format",
+ "HELP": "The public certificate from your identity provider used to verify SAML responses",
+ "PLACEHOLDER": "-----BEGIN CERTIFICATE-----\nMIIC..."
+ },
+ "FINGERPRINT": {
+ "LABEL": "Fingerprint",
+ "TOOLTIP": "SHA-1 fingerprint of the certificate - Use this to verify the certificate in your IdP configuration"
+ },
+ "COPY_SUCCESS": "Kode berhasil disalin ke clipboard",
+ "SP_ENTITY_ID": {
+ "LABEL": "SP Entity ID",
+ "HELP": "Unique identifier for this application as a service provider (auto-generated).",
+ "TOOLTIP": "Unique identifier for Chatwoot as the Service Provider - Configure this in your IdP settings"
+ },
+ "IDP_ENTITY_ID": {
+ "LABEL": "Identity Provider Entity ID",
+ "HELP": "Unique identifier for your identity provider (usually found in IdP configuration)",
+ "PLACEHOLDER": "https://your-idp.com/saml"
+ },
+ "UPDATE_BUTTON": "Update SAML Settings",
+ "API": {
+ "SUCCESS": "SAML settings updated successfully",
+ "ERROR": "Failed to update SAML settings",
+ "ERROR_LOADING": "Failed to load SAML settings",
+ "DISABLED": "SAML settings disabled successfully"
+ },
+ "VALIDATION": {
+ "REQUIRED_FIELDS": "SSO URL, Identity Provider Entity ID, and Certificate are required fields",
+ "SSO_URL_ERROR": "Please enter a valid SSO URL",
+ "CERTIFICATE_ERROR": "Certificate is required",
+ "IDP_ENTITY_ID_ERROR": "Identity Provider Entity ID is required"
+ },
+ "ENTERPRISE_PAYWALL": {
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade to an Enterprise plan to access SAML single sign-on and other advanced security features.",
+ "ASK_ADMIN": "Please reach out to your administrator for the upgrade."
+ },
+ "PAYWALL": {
+ "TITLE": "Upgrade to enable SAML SSO",
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade your plan to get access to SAML single sign-on and other advanced features.",
+ "UPGRADE_NOW": "Upgrade now",
+ "CANCEL_ANYTIME": "You can change or cancel your plan anytime"
+ },
+ "ATTRIBUTE_MAPPING": {
+ "TITLE": "SAML Attribute Setup",
+ "DESCRIPTION": "The following attribute mappings must be configured in your identity provider"
+ },
+ "INFO_SECTION": {
+ "TITLE": "Service Provider Information",
+ "TOOLTIP": "Copy these values and configure them in your Identity Provider to establish the SAML connection"
+ }
+ }
+ },
"CREATE_ACCOUNT": {
"NO_ACCOUNT_WARNING": "Uh oh! Kami tidak dapat menemukan akun Chatwoot apa pun. Harap buat akun baru untuk melanjutkan.",
"NEW_ACCOUNT": "Akun Baru",
@@ -418,5 +503,255 @@
"SWITCH_TO_REPLY": "Beralih ke Balasan",
"TOGGLE_SNOOZE_DROPDOWN": "Buka/Tutup dropdown penundaan"
}
+ },
+ "ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent assignment",
+ "DESCRIPTION": "Define policies to effectively manage workload and route conversations based on the needs of inboxes and agents. Learn more here"
+ },
+ "ASSIGNMENT_POLICY": {
+ "TITLE": "Assignment policy",
+ "DESCRIPTION": "Manage how conversations get assigned in inboxes.",
+ "FEATURES": [
+ "Assign by conversations evenly or by available capacity",
+ "Add fair distribution rules to avoid overloading any agent",
+ "Add inboxes to a policy - one policy per inbox"
+ ]
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "TITLE": "Agent capacity policy",
+ "DESCRIPTION": "Manage workload for agents.",
+ "FEATURES": [
+ "Define maximum conversations per inbox",
+ "Create exceptions based on labels and time",
+ "Add agents to a policy - one policy per agent"
+ ]
+ }
+ },
+ "AGENT_ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Assignment policy",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "ORDER": "Order",
+ "PRIORITY": "Prioritas",
+ "ACTIVE": "Aktif",
+ "INACTIVE": "Inactive",
+ "POPOVER": "Added inboxes",
+ "EDIT": "Edit"
+ },
+ "NO_RECORDS_FOUND": "No assignment policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create assignment policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy created successfully",
+ "ERROR_MESSAGE": "Failed to create assignment policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit assignment policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_INBOX_DIALOG": {
+ "TITLE": "Add inbox",
+ "DESCRIPTION": "{inboxName} inbox is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Batalkan"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update assignment policy"
+ },
+ "INBOX_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Inbox added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Inbox removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove inbox from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Deskripsi:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "STATUS": {
+ "LABEL": "Status:",
+ "PLACEHOLDER": "Select status",
+ "ACTIVE": "Policy is active",
+ "INACTIVE": "Policy is inactive"
+ },
+ "ASSIGNMENT_ORDER": {
+ "LABEL": "Assignment order",
+ "ROUND_ROBIN": {
+ "LABEL": "Round robin",
+ "DESCRIPTION": "Assign conversations evenly among agents."
+ },
+ "BALANCED": {
+ "LABEL": "Balanced",
+ "DESCRIPTION": "Assign conversations based on available capacity."
+ }
+ },
+ "ASSIGNMENT_PRIORITY": {
+ "LABEL": "Assignment priority",
+ "EARLIEST_CREATED": {
+ "LABEL": "Earliest created",
+ "DESCRIPTION": "The conversation that was created first gets assigned first."
+ },
+ "LONGEST_WAITING": {
+ "LABEL": "Longest waiting",
+ "DESCRIPTION": "The conversation waiting the longest gets assigned first."
+ }
+ },
+ "FAIR_DISTRIBUTION": {
+ "LABEL": "Fair distribution policy",
+ "DESCRIPTION": "Set the maximum number of conversations that can be assigned per agent within a time window to avoid overloading any one agent. This required field defaults to 100 conversations per hour.",
+ "INPUT_MAX": "Assign max",
+ "DURATION": "Conversations per agent in every"
+ },
+ "INBOXES": {
+ "LABEL": "Added inboxes",
+ "DESCRIPTION": "Add inboxes for which this policy will be applicable.",
+ "ADD_BUTTON": "Add inbox",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select inboxes to add",
+ "ADD_BUTTON": "Tambahkan"
+ },
+ "EMPTY_STATE": "No inboxes added to this policy, add an inbox to get started",
+ "API": {
+ "SUCCESS_MESSAGE": "Inbox successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Assignment policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete assignment policy"
+ }
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent capacity",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "POPOVER": "Added agents",
+ "EDIT": "Edit"
+ },
+ "NO_RECORDS_FOUND": "No agent capacity policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create agent capacity policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy created successfully",
+ "ERROR_MESSAGE": "Failed to create agent capacity policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit agent capacity policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_AGENT_DIALOG": {
+ "TITLE": "Add agent",
+ "DESCRIPTION": "{agentName} is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Batalkan"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update agent capacity policy"
+ },
+ "AGENT_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Agent added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Agent removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove agent from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Deskripsi:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "INBOX_CAPACITY_LIMIT": {
+ "LABEL": "Inbox capacity limits",
+ "ADD_BUTTON": "Add inbox",
+ "FIELD": {
+ "SELECT_INBOX": "Select inbox",
+ "MAX_CONVERSATIONS": "Max conversations",
+ "SET_LIMIT": "Set limit"
+ },
+ "EMPTY_STATE": "No inbox limit set"
+ },
+ "EXCLUSION_RULES": {
+ "LABEL": "Exclusion rules",
+ "DESCRIPTION": "Conversations that satisfy the following conditions would not count towards agent capacity",
+ "TAGS": {
+ "LABEL": "Exclude conversations tagged with specific labels",
+ "ADD_TAG": "add tag",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select tags to add"
+ },
+ "EMPTY_STATE": "No tags added to this policy."
+ },
+ "DURATION": {
+ "LABEL": "Exclude conversations older than a specified duration",
+ "PLACEHOLDER": "Set time"
+ }
+ },
+ "USERS": {
+ "LABEL": "Assigned agents",
+ "DESCRIPTION": "Add agents for which this policy will be applicable.",
+ "ADD_BUTTON": "Add agent",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select agents to add",
+ "ADD_BUTTON": "Tambahkan"
+ },
+ "EMPTY_STATE": "No agents added",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Agent capacity policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete agent capacity policy"
+ }
+ },
+ "DELETE_POLICY": {
+ "TITLE": "Delete policy",
+ "DESCRIPTION": "Are you sure you want to delete this policy? This action cannot be undone.",
+ "CONFIRM_BUTTON_LABEL": "Hapus",
+ "CANCEL_BUTTON_LABEL": "Batalkan"
+ }
}
}
diff --git a/app/javascript/dashboard/i18n/locale/id/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/id/whatsappTemplates.json
index c9145225c..06a911929 100644
--- a/app/javascript/dashboard/i18n/locale/id/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/id/whatsappTemplates.json
@@ -40,6 +40,7 @@
"BUTTON_LABEL": "Button {index}",
"COUPON_CODE": "Enter coupon code (max 15 chars)",
"MEDIA_URL_LABEL": "Enter {type} URL",
+ "DOCUMENT_NAME_PLACEHOLDER": "Enter document filename (e.g., Invoice_2025.pdf)",
"BUTTON_PARAMETER": "Enter button parameter"
}
}
diff --git a/app/javascript/dashboard/i18n/locale/is/automation.json b/app/javascript/dashboard/i18n/locale/is/automation.json
index 8caeb2344..b183ba597 100644
--- a/app/javascript/dashboard/i18n/locale/is/automation.json
+++ b/app/javascript/dashboard/i18n/locale/is/automation.json
@@ -177,7 +177,8 @@
"REFERER_LINK": "Referrer Link",
"ASSIGNEE_NAME": "Assignee",
"TEAM_NAME": "Team",
- "PRIORITY": "Priority"
+ "PRIORITY": "Priority",
+ "LABELS": "Labels"
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/is/contact.json b/app/javascript/dashboard/i18n/locale/is/contact.json
index 93e674ff5..4317dad6b 100644
--- a/app/javascript/dashboard/i18n/locale/is/contact.json
+++ b/app/javascript/dashboard/i18n/locale/is/contact.json
@@ -610,6 +610,15 @@
"SEND_MESSAGE": "Senda skilaboð"
}
},
+ "TWILIO_OPTIONS": {
+ "LABEL": "Select template",
+ "SEARCH_PLACEHOLDER": "Search templates",
+ "EMPTY_STATE": "No templates found",
+ "TEMPLATE_PARSER": {
+ "BACK": "Go back",
+ "SEND_MESSAGE": "Senda skilaboð"
+ }
+ },
"ACTION_BUTTONS": {
"DISCARD": "Discard",
"SEND": "Send ({keyCode})"
diff --git a/app/javascript/dashboard/i18n/locale/is/contactFilters.json b/app/javascript/dashboard/i18n/locale/is/contactFilters.json
index f78ec4906..3231afa4c 100644
--- a/app/javascript/dashboard/i18n/locale/is/contactFilters.json
+++ b/app/javascript/dashboard/i18n/locale/is/contactFilters.json
@@ -48,7 +48,8 @@
"CREATED_AT": "Created At",
"LAST_ACTIVITY": "Seinasta virkni",
"REFERER_LINK": "Referrer link",
- "BLOCKED": "Blocked"
+ "BLOCKED": "Blocked",
+ "LABELS": "Labels"
},
"GROUPS": {
"STANDARD_FILTERS": "Standard Filters",
diff --git a/app/javascript/dashboard/i18n/locale/is/contentTemplates.json b/app/javascript/dashboard/i18n/locale/is/contentTemplates.json
new file mode 100644
index 000000000..ce5ab5aad
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/is/contentTemplates.json
@@ -0,0 +1,51 @@
+{
+ "CONTENT_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Twilio Templates",
+ "SUBTITLE": "Select the Twilio template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Search Templates",
+ "NO_TEMPLATES_FOUND": "No templates found for",
+ "NO_CONTENT": "No content",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Category",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No Twilio templates available. Click refresh to sync templates from Twilio.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Language",
+ "TEMPLATE_BODY": "Template Body",
+ "CATEGORY": "Category"
+ },
+ "TYPES": {
+ "MEDIA": "Media",
+ "QUICK_REPLY": "Quick Reply",
+ "TEXT": "Text"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variables",
+ "LANGUAGE": "Language",
+ "CATEGORY": "Category",
+ "VARIABLE_PLACEHOLDER": "Enter {variable} value",
+ "GO_BACK_LABEL": "Go Back",
+ "SEND_MESSAGE_LABEL": "Send Message",
+ "FORM_ERROR_MESSAGE": "Please fill all variables before sending",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "MEDIA_URL_LABEL": "Enter full media URL",
+ "MEDIA_URL_PLACEHOLDER": "https://example.com/image.jpg"
+ },
+ "FORM": {
+ "BACK_BUTTON": "Til baka",
+ "SEND_MESSAGE_BUTTON": "Send Message"
+ }
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/is/conversation.json b/app/javascript/dashboard/i18n/locale/is/conversation.json
index ba6ff5c9d..91f0421db 100644
--- a/app/javascript/dashboard/i18n/locale/is/conversation.json
+++ b/app/javascript/dashboard/i18n/locale/is/conversation.json
@@ -35,6 +35,11 @@
"API_HOURS_WINDOW": "You can only reply to this conversation within {hours} hours",
"NOT_ASSIGNED_TO_YOU": "Þetta samtal er ekki úthlutað á þig. Viltu úthluta þessu samtali á þig?",
"ASSIGN_TO_ME": "Úthluta á mig",
+ "BOT_HANDOFF_MESSAGE": "You are responding to a conversation which is currently handled by an assistant or a bot.",
+ "BOT_HANDOFF_ACTION": "Mark open and assign to you",
+ "BOT_HANDOFF_REOPEN_ACTION": "Mark conversation open",
+ "BOT_HANDOFF_SUCCESS": "Conversation has been handed over to you",
+ "BOT_HANDOFF_ERROR": "Failed to take over the conversation. Please try again.",
"TWILIO_WHATSAPP_CAN_REPLY": "Þú getur aðeins svarað þessu samtali með því að nota sniðmátskilaboð vegna þess að",
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "24 hour message window restriction",
"OLD_INSTAGRAM_INBOX_REPLY_BANNER": "This Instagram account was migrated to the new Instagram channel inbox. All new messages will show up there. You won’t be able to send messages from this conversation anymore.",
@@ -66,6 +71,17 @@
"SHOW_LABELS": "Show labels",
"HIDE_LABELS": "Hide labels"
},
+ "VOICE_CALL": {
+ "INCOMING_CALL": "Incoming call",
+ "OUTGOING_CALL": "Outgoing call",
+ "CALL_IN_PROGRESS": "Call in progress",
+ "NO_ANSWER": "No answer",
+ "MISSED_CALL": "Missed call",
+ "CALL_ENDED": "Call ended",
+ "NOT_ANSWERED_YET": "Not answered yet",
+ "THEY_ANSWERED": "They answered",
+ "YOU_ANSWERED": "You answered"
+ },
"HEADER": {
"RESOLVE_ACTION": "Resolve",
"REOPEN_ACTION": "Reopen",
diff --git a/app/javascript/dashboard/i18n/locale/is/helpCenter.json b/app/javascript/dashboard/i18n/locale/is/helpCenter.json
index 02e72d4f9..878352846 100644
--- a/app/javascript/dashboard/i18n/locale/is/helpCenter.json
+++ b/app/javascript/dashboard/i18n/locale/is/helpCenter.json
@@ -741,7 +741,8 @@
"LIVE_CHAT_WIDGET": {
"LABEL": "Live chat widget",
"PLACEHOLDER": "Select live chat widget",
- "HELP_TEXT": "Select a live chat widget that will appear on your help center"
+ "HELP_TEXT": "Select a live chat widget that will appear on your help center",
+ "NONE_OPTION": "No widget"
},
"BRAND_COLOR": {
"LABEL": "Brand color"
@@ -807,6 +808,58 @@
"ERROR_MESSAGE": "Unable to update portal"
}
}
+ },
+ "PDF_UPLOAD": {
+ "TITLE": "Upload PDF Document",
+ "DESCRIPTION": "Upload a PDF document to automatically generate FAQs using AI",
+ "DRAG_DROP_TEXT": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "ADDITIONAL_CONTEXT_LABEL": "Additional Context (Optional)",
+ "ADDITIONAL_CONTEXT_PLACEHOLDER": "Provide any additional context or instructions for FAQ generation...",
+ "UPLOADING": "Hleður upp...",
+ "UPLOAD": "Upload & Process",
+ "CANCEL": "Hætta við",
+ "ERROR_INVALID_TYPE": "Please select a valid PDF file",
+ "ERROR_FILE_TOO_LARGE": "File size must be less than 512MB",
+ "ERROR_UPLOAD_FAILED": "Failed to upload PDF. Please try again."
+ },
+ "PDF_DOCUMENTS": {
+ "TITLE": "PDF Documents",
+ "DESCRIPTION": "Manage uploaded PDF documents and generate FAQs from them",
+ "UPLOAD_PDF": "Upload PDF",
+ "UPLOAD_FIRST_PDF": "Upload your first PDF",
+ "UPLOADED_BY": "Uploaded by",
+ "GENERATE_FAQS": "Generate FAQs",
+ "GENERATING": "Generating...",
+ "CONFIRM_DELETE": "Are you sure you want to delete {filename}?",
+ "EMPTY_STATE": {
+ "TITLE": "No PDF documents yet",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQs using AI"
+ },
+ "STATUS": {
+ "UPLOADED": "Ready",
+ "PROCESSING": "Processing",
+ "PROCESSED": "Completed",
+ "FAILED": "Failed"
+ }
+ },
+ "CONTENT_GENERATION": {
+ "TITLE": "Content Generation",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQ content using AI",
+ "UPLOAD_TITLE": "Upload PDF Document",
+ "DRAG_DROP": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "UPLOADING": "Processing document...",
+ "UPLOAD_SUCCESS": "Document processed successfully!",
+ "UPLOAD_ERROR": "Failed to upload document. Please try again.",
+ "INVALID_FILE_TYPE": "Please select a valid PDF file",
+ "FILE_TOO_LARGE": "File size must be less than 512MB",
+ "GENERATED_CONTENT": "Generated FAQ Content",
+ "PUBLISH_SELECTED": "Publish Selected",
+ "PUBLISHING": "Publishing...",
+ "FROM_DOCUMENT": "From document",
+ "NO_CONTENT": "No generated content available. Upload a PDF document to get started.",
+ "LOADING": "Loading generated content..."
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/is/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/is/inboxMgmt.json
index 4ce98f192..156c86905 100644
--- a/app/javascript/dashboard/i18n/locale/is/inboxMgmt.json
+++ b/app/javascript/dashboard/i18n/locale/is/inboxMgmt.json
@@ -272,8 +272,8 @@
},
"SUBMIT_BUTTON": "Create WhatsApp Channel",
"EMBEDDED_SIGNUP": {
- "TITLE": "Quick Setup with Meta",
- "DESC": "You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
+ "TITLE": "Quick setup with Meta",
+ "DESC": "Use the WhatsApp Embedded Signup flow to quickly connect new numbers. You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
"BENEFITS": {
"TITLE": "Benefits of Embedded Signup:",
"EASY_SETUP": "No manual configuration required",
@@ -281,9 +281,8 @@
"AUTO_CONFIG": "Automatic webhook and phone number configuration"
},
"LEARN_MORE": {
- "TEXT": "To learn more about integrated signup, pricing, and limitations, visit",
- "LINK_TEXT": "this link.",
- "LINK_URL": "https://developers.facebook.com/docs/whatsapp/embedded-signup/custom-flows/onboarding-business-app-users#limitations"
+ "TEXT": "To learn more about integrated signup, pricing, and limitations, visit {link}.",
+ "LINK_TEXT": "this link"
},
"SUBMIT_BUTTON": "Connect with WhatsApp Business",
"AUTH_PROCESSING": "Authenticating with Meta",
@@ -296,7 +295,9 @@
"INVALID_BUSINESS_DATA": "Invalid business data received from Facebook. Please try again.",
"SIGNUP_ERROR": "Signup error occurred",
"AUTH_NOT_COMPLETED": "Authentication not completed. Please restart the process.",
- "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured"
+ "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured",
+ "MANUAL_FALLBACK": "If your number is already connected to the WhatsApp Business Platform (API), or if you’re a tech provider onboarding your own number, please use the {link} flow",
+ "MANUAL_LINK_TEXT": "manual setup flow"
},
"API": {
"ERROR_MESSAGE": "Við gátum ekki vistað WhatsApp rásina"
@@ -424,7 +425,51 @@
},
"AUTH": {
"TITLE": "Veldu rás",
- "DESC": "Chatwoot styður vefspjall, Facebook Messenger, Twitter prófíla, WhatsApp, tölvupóst osfrv., Sem rásir. Ef þú vilt búa til sérsniðna rás geturðu búið hana til með því að nota API rásina. Til að byrja skaltu velja eina af rásunum hér að neðan."
+ "DESC": "Chatwoot styður vefspjall, Facebook Messenger, Twitter prófíla, WhatsApp, tölvupóst osfrv., Sem rásir. Ef þú vilt búa til sérsniðna rás geturðu búið hana til með því að nota API rásina. Til að byrja skaltu velja eina af rásunum hér að neðan.",
+ "TITLE_NEXT": "Complete the setup",
+ "TITLE_FINISH": "Voilà!",
+ "CHANNEL": {
+ "WEBSITE": {
+ "TITLE": "Website",
+ "DESCRIPTION": "Create a live-chat widget"
+ },
+ "FACEBOOK": {
+ "TITLE": "Facebook",
+ "DESCRIPTION": "Connect your Facebook page"
+ },
+ "WHATSAPP": {
+ "TITLE": "WhatsApp",
+ "DESCRIPTION": "Support your customers on WhatsApp"
+ },
+ "EMAIL": {
+ "TITLE": "Tölvupóstfang",
+ "DESCRIPTION": "Connect with Gmail, Outlook, or other providers"
+ },
+ "SMS": {
+ "TITLE": "SMS",
+ "DESCRIPTION": "Integrate SMS channel with Twilio or bandwidth"
+ },
+ "API": {
+ "TITLE": "API",
+ "DESCRIPTION": "Make a custom channel using our API"
+ },
+ "TELEGRAM": {
+ "TITLE": "Telegram",
+ "DESCRIPTION": "Configure Telegram channel using Bot token"
+ },
+ "LINE": {
+ "TITLE": "Line",
+ "DESCRIPTION": "Integrate your Line channel"
+ },
+ "INSTAGRAM": {
+ "TITLE": "Instagram",
+ "DESCRIPTION": "Connect your instagram account"
+ },
+ "VOICE": {
+ "TITLE": "Voice",
+ "DESCRIPTION": "Integrate with Twilio Voice"
+ }
+ }
},
"AGENTS": {
"TITLE": "Þjónustufulltrúar",
@@ -478,7 +523,10 @@
"MESSAGE": "Þú getur nú átt samskipti við viðskiptavini þína í gegnum nýju rásina þína. Gangi þér vel við þjónustuna",
"BUTTON_TEXT": "Taktu mig þangað",
"MORE_SETTINGS": "Fleiri stillingar",
- "WEBSITE_SUCCESS": "Þú hefur lokið við að búa til vefsíðurás. Afritaðu kóðann sem sýndur er hér að neðan og límdu hann á vefsíðuna þína. Næst þegar viðskiptavinur notar spjallið birtist samtalið sjálfkrafa í innhólfinu þínu."
+ "WEBSITE_SUCCESS": "Þú hefur lokið við að búa til vefsíðurás. Afritaðu kóðann sem sýndur er hér að neðan og límdu hann á vefsíðuna þína. Næst þegar viðskiptavinur notar spjallið birtist samtalið sjálfkrafa í innhólfinu þínu.",
+ "WHATSAPP_QR_INSTRUCTION": "Scan the QR code above to quickly test your WhatsApp inbox",
+ "MESSENGER_QR_INSTRUCTION": "Scan the QR code above to quickly test your Facebook Messenger inbox",
+ "TELEGRAM_QR_INSTRUCTION": "Scan the QR code above to quickly test your Telegram inbox"
},
"REAUTH": "Endurauðkenna",
"VIEW": "Skoða",
@@ -868,9 +916,18 @@
"SCRIPT_SETTINGS": "\n window.chatwootSettings = {options};"
},
"EMAIL_PROVIDERS": {
- "MICROSOFT": "Microsoft",
- "GOOGLE": "Google",
- "OTHER_PROVIDERS": "Other Providers"
+ "MICROSOFT": {
+ "TITLE": "Microsoft",
+ "DESCRIPTION": "Connect with Microsoft"
+ },
+ "GOOGLE": {
+ "TITLE": "Google",
+ "DESCRIPTION": "Connect with Google"
+ },
+ "OTHER_PROVIDERS": {
+ "TITLE": "Other Providers",
+ "DESCRIPTION": "Connect with Other Providers"
+ }
},
"CHANNELS": {
"MESSENGER": "Messenger",
diff --git a/app/javascript/dashboard/i18n/locale/is/integrations.json b/app/javascript/dashboard/i18n/locale/is/integrations.json
index 620ce70cf..53a81dc2f 100644
--- a/app/javascript/dashboard/i18n/locale/is/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/is/integrations.json
@@ -704,11 +704,28 @@
"ERROR_MESSAGE": "There was an error creating the document, please try again."
},
"FORM": {
+ "TYPE": {
+ "LABEL": "Document Type",
+ "URL": "URL",
+ "PDF": "PDF File"
+ },
"URL": {
"LABEL": "URL",
"PLACEHOLDER": "Enter the URL of the document",
"ERROR": "Please provide a valid URL for the document"
},
+ "PDF_FILE": {
+ "LABEL": "PDF File",
+ "CHOOSE_FILE": "Choose PDF file",
+ "ERROR": "Please select a PDF file",
+ "HELP_TEXT": "Maximum file size: 10MB",
+ "INVALID_TYPE": "Please select a valid PDF file",
+ "TOO_LARGE": "File size exceeds 10MB limit"
+ },
+ "NAME": {
+ "LABEL": "Document Name (Optional)",
+ "PLACEHOLDER": "Enter a name for the document"
+ },
"ASSISTANT": {
"LABEL": "Assistant",
"PLACEHOLDER": "Select the assistant",
@@ -744,6 +761,7 @@
"SELECTED": "{count} selected",
"SELECT_ALL": "Select all ({count})",
"UNSELECT_ALL": "Unselect all ({count})",
+ "SEARCH_PLACEHOLDER": "Search FAQs...",
"BULK_APPROVE_BUTTON": "Approve",
"BULK_DELETE_BUTTON": "Eyða",
"BULK_APPROVE": {
diff --git a/app/javascript/dashboard/i18n/locale/is/mfa.json b/app/javascript/dashboard/i18n/locale/is/mfa.json
new file mode 100644
index 000000000..aa7da19b6
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/is/mfa.json
@@ -0,0 +1,106 @@
+{
+ "MFA_SETTINGS": {
+ "TITLE": "Two-Factor Authentication",
+ "SUBTITLE": "Secure your account with TOTP-based authentication",
+ "DESCRIPTION": "Add an extra layer of security to your account using a time-based one-time password (TOTP)",
+ "STATUS_TITLE": "Authentication Status",
+ "STATUS_DESCRIPTION": "Manage your two-factor authentication settings and backup recovery codes",
+ "ENABLED": "Virkt",
+ "DISABLED": "Slökkt",
+ "STATUS_ENABLED": "Two-factor authentication is active",
+ "STATUS_ENABLED_DESC": "Your account is protected with an additional layer of security",
+ "ENABLE_BUTTON": "Enable Two-Factor Authentication",
+ "ENHANCE_SECURITY": "Enhance Your Account Security",
+ "ENHANCE_SECURITY_DESC": "Two-factor authentication adds an extra layer of security by requiring a verification code from your authenticator app in addition to your password.",
+ "SETUP": {
+ "STEP_NUMBER_1": "1",
+ "STEP_NUMBER_2": "2",
+ "STEP1_TITLE": "Scan QR Code with Your Authenticator App",
+ "STEP1_DESCRIPTION": "Use Google Authenticator, Authy, or any TOTP-compatible app",
+ "LOADING_QR": "Loading...",
+ "MANUAL_ENTRY": "Can't scan? Enter code manually",
+ "SECRET_KEY": "Secret Key",
+ "COPY": "Copy",
+ "ENTER_CODE": "Enter the 6-digit code from your authenticator app",
+ "ENTER_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify & Continue",
+ "CANCEL": "Hætta við",
+ "ERROR_STARTING": "MFA not enabled. Please contact administrator.",
+ "INVALID_CODE": "Invalid verification code",
+ "SECRET_COPIED": "Secret key copied to clipboard",
+ "SUCCESS": "Two-factor authentication has been enabled successfully"
+ },
+ "BACKUP": {
+ "TITLE": "Save Your Backup Codes",
+ "DESCRIPTION": "Keep these codes safe. Each can be used once if you lose access to your authenticator",
+ "IMPORTANT": "Important:",
+ "IMPORTANT_NOTE": " Save these codes in a secure location. You won't be able to see them again.",
+ "DOWNLOAD": "Sækja",
+ "COPY_ALL": "Copy All",
+ "CONFIRM": "I have saved my backup codes in a secure location and understand that I won't be able to see them again",
+ "COMPLETE_SETUP": "Complete Setup",
+ "CODES_COPIED": "Backup codes copied to clipboard"
+ },
+ "MANAGEMENT": {
+ "BACKUP_CODES": "Backup Codes",
+ "BACKUP_CODES_DESC": "Generate new codes if you've lost or used your existing ones",
+ "REGENERATE": "Regenerate Backup Codes",
+ "DISABLE_MFA": "Disable 2FA",
+ "DISABLE_MFA_DESC": "Remove two-factor authentication from your account",
+ "DISABLE_BUTTON": "Disable Two-Factor Authentication"
+ },
+ "DISABLE": {
+ "TITLE": "Disable Two-Factor Authentication",
+ "DESCRIPTION": "You'll need to enter your password and a verification code to disable two-factor authentication.",
+ "PASSWORD": "Lykilorð",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Disable 2FA",
+ "CANCEL": "Hætta við",
+ "SUCCESS": "Two-factor authentication has been disabled",
+ "ERROR": "Failed to disable MFA. Please check your credentials."
+ },
+ "REGENERATE": {
+ "TITLE": "Regenerate Backup Codes",
+ "DESCRIPTION": "This will invalidate your existing backup codes and generate new ones. Enter your verification code to continue.",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Generate New Codes",
+ "CANCEL": "Hætta við",
+ "NEW_CODES_TITLE": "New Backup Codes Generated",
+ "NEW_CODES_DESC": "Your old backup codes have been invalidated. Save these new codes in a secure location.",
+ "CODES_IMPORTANT": "Important:",
+ "CODES_IMPORTANT_NOTE": " Each code can only be used once. Save them before closing this window.",
+ "DOWNLOAD_CODES": "Download Codes",
+ "COPY_ALL_CODES": "Copy All Codes",
+ "CODES_SAVED": "I've Saved My Codes",
+ "SUCCESS": "New backup codes have been generated",
+ "ERROR": "Failed to regenerate backup codes"
+ }
+ },
+ "MFA_VERIFICATION": {
+ "TITLE": "Two-Factor Authentication",
+ "DESCRIPTION": "Enter your verification code to continue",
+ "AUTHENTICATOR_APP": "Authenticator App",
+ "BACKUP_CODE": "Backup Code",
+ "ENTER_OTP_CODE": "Enter 6-digit code from your authenticator app",
+ "ENTER_BACKUP_CODE": "Enter one of your backup codes",
+ "BACKUP_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify",
+ "TRY_ANOTHER_METHOD": "Try another verification method",
+ "CANCEL_LOGIN": "Cancel and return to login",
+ "HELP_TEXT": "Having trouble signing in?",
+ "LEARN_MORE": "Learn more about 2FA",
+ "HELP_MODAL": {
+ "TITLE": "Two-Factor Authentication Help",
+ "AUTHENTICATOR_TITLE": "Using an Authenticator App",
+ "AUTHENTICATOR_DESC": "Open your authenticator app (Google Authenticator, Authy, etc.) and enter the 6-digit code shown for your account.",
+ "BACKUP_TITLE": "Using a Backup Code",
+ "BACKUP_DESC": "If you don't have access to your authenticator app, you can use one of the backup codes you saved when setting up 2FA. Each code can only be used once.",
+ "CONTACT_TITLE": "Need More Help?",
+ "CONTACT_DESC_CLOUD": "If you've lost access to both your authenticator app and backup codes, please reach out to Chatwoot support for assistance.",
+ "CONTACT_DESC_SELF_HOSTED": "If you've lost access to both your authenticator app and backup codes, please contact your administrator for assistance."
+ },
+ "VERIFICATION_FAILED": "Verification failed. Please try again."
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/is/settings.json b/app/javascript/dashboard/i18n/locale/is/settings.json
index 380695607..776cbf10a 100644
--- a/app/javascript/dashboard/i18n/locale/is/settings.json
+++ b/app/javascript/dashboard/i18n/locale/is/settings.json
@@ -51,6 +51,13 @@
"LARGER": "Larger",
"EXTRA_LARGE": "Extra Large"
}
+ },
+ "LANGUAGE": {
+ "TITLE": "Preferred Language",
+ "NOTE": "Choose the language you want to use.",
+ "UPDATE_SUCCESS": "Your Language settings have been updated successfully",
+ "UPDATE_ERROR": "There is an error while updating the language settings, please try again",
+ "USE_ACCOUNT_DEFAULT": "Use account default"
}
},
"MESSAGE_SIGNATURE_SECTION": {
@@ -73,6 +80,11 @@
"NOTE": "Að uppfæra lykilorðið þitt myndi endurstilla innskráningar þínar í mörgum tækjum.",
"BTN_TEXT": "Breyta lykilorði"
},
+ "SECURITY_SECTION": {
+ "TITLE": "Security",
+ "NOTE": "Manage additional security features for your account.",
+ "MFA_BUTTON": "Manage Two-Factor Authentication"
+ },
"ACCESS_TOKEN": {
"TITLE": "Aðgangslykill",
"NOTE": "Þetta token er hægt að nota ef þú ert að byggja upp API byggða samþættingu",
@@ -330,6 +342,7 @@
"REPORTS_LABEL": "Labels",
"REPORTS_INBOX": "Innhólf",
"REPORTS_TEAM": "Team",
+ "AGENT_ASSIGNMENT": "Agent Assignment",
"SET_AVAILABILITY_TITLE": "Set yourself as",
"SET_YOUR_AVAILABILITY": "Set your availability",
"SLA": "SLA",
@@ -350,7 +363,8 @@
"INFO_TEXT": "Let the system automatically mark you offline when you aren't using the app or dashboard.",
"INFO_SHORT": "Automatically mark offline when you aren't using the app."
},
- "DOCS": "Read docs"
+ "DOCS": "Read docs",
+ "SECURITY": "Security"
},
"BILLING_SETTINGS": {
"TITLE": "Billing",
@@ -382,6 +396,77 @@
},
"NO_BILLING_USER": "Verið er að stilla innheimtureikninginn þinn. Endurnýjaðu síðuna og reyndu aftur."
},
+ "SECURITY_SETTINGS": {
+ "TITLE": "Security",
+ "DESCRIPTION": "Manage your account security settings.",
+ "LINK_TEXT": "Learn more about SAML SSO",
+ "SAML": {
+ "TITLE": "SAML SSO",
+ "NOTE": "Configure SAML single sign-on for your account. Users will authenticate through your identity provider instead of using email/password.",
+ "ACS_URL": {
+ "LABEL": "ACS URL",
+ "TOOLTIP": "Assertion Consumer Service URL - Configure this URL in your IdP as the destination for SAML responses"
+ },
+ "SSO_URL": {
+ "LABEL": "SSO URL",
+ "HELP": "The URL where SAML authentication requests will be sent",
+ "PLACEHOLDER": "https://your-idp.com/saml/sso"
+ },
+ "CERTIFICATE": {
+ "LABEL": "Signing certificate in PEM format",
+ "HELP": "The public certificate from your identity provider used to verify SAML responses",
+ "PLACEHOLDER": "-----BEGIN CERTIFICATE-----\nMIIC..."
+ },
+ "FINGERPRINT": {
+ "LABEL": "Fingerprint",
+ "TOOLTIP": "SHA-1 fingerprint of the certificate - Use this to verify the certificate in your IdP configuration"
+ },
+ "COPY_SUCCESS": "Code copied to clipboard successfully",
+ "SP_ENTITY_ID": {
+ "LABEL": "SP Entity ID",
+ "HELP": "Unique identifier for this application as a service provider (auto-generated).",
+ "TOOLTIP": "Unique identifier for Chatwoot as the Service Provider - Configure this in your IdP settings"
+ },
+ "IDP_ENTITY_ID": {
+ "LABEL": "Identity Provider Entity ID",
+ "HELP": "Unique identifier for your identity provider (usually found in IdP configuration)",
+ "PLACEHOLDER": "https://your-idp.com/saml"
+ },
+ "UPDATE_BUTTON": "Update SAML Settings",
+ "API": {
+ "SUCCESS": "SAML settings updated successfully",
+ "ERROR": "Failed to update SAML settings",
+ "ERROR_LOADING": "Failed to load SAML settings",
+ "DISABLED": "SAML settings disabled successfully"
+ },
+ "VALIDATION": {
+ "REQUIRED_FIELDS": "SSO URL, Identity Provider Entity ID, and Certificate are required fields",
+ "SSO_URL_ERROR": "Please enter a valid SSO URL",
+ "CERTIFICATE_ERROR": "Certificate is required",
+ "IDP_ENTITY_ID_ERROR": "Identity Provider Entity ID is required"
+ },
+ "ENTERPRISE_PAYWALL": {
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade to an Enterprise plan to access SAML single sign-on and other advanced security features.",
+ "ASK_ADMIN": "Please reach out to your administrator for the upgrade."
+ },
+ "PAYWALL": {
+ "TITLE": "Upgrade to enable SAML SSO",
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade your plan to get access to SAML single sign-on and other advanced features.",
+ "UPGRADE_NOW": "Upgrade now",
+ "CANCEL_ANYTIME": "You can change or cancel your plan anytime"
+ },
+ "ATTRIBUTE_MAPPING": {
+ "TITLE": "SAML Attribute Setup",
+ "DESCRIPTION": "The following attribute mappings must be configured in your identity provider"
+ },
+ "INFO_SECTION": {
+ "TITLE": "Service Provider Information",
+ "TOOLTIP": "Copy these values and configure them in your Identity Provider to establish the SAML connection"
+ }
+ }
+ },
"CREATE_ACCOUNT": {
"NO_ACCOUNT_WARNING": "Uh ó! Við fundum enga Chatwoot reikninga. Vinsamlegast búðu til nýjan reikning til að halda áfram.",
"NEW_ACCOUNT": "New Account",
@@ -418,5 +503,255 @@
"SWITCH_TO_REPLY": "Switch to Reply",
"TOGGLE_SNOOZE_DROPDOWN": "Toggle snooze dropdown"
}
+ },
+ "ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent assignment",
+ "DESCRIPTION": "Define policies to effectively manage workload and route conversations based on the needs of inboxes and agents. Learn more here"
+ },
+ "ASSIGNMENT_POLICY": {
+ "TITLE": "Assignment policy",
+ "DESCRIPTION": "Manage how conversations get assigned in inboxes.",
+ "FEATURES": [
+ "Assign by conversations evenly or by available capacity",
+ "Add fair distribution rules to avoid overloading any agent",
+ "Add inboxes to a policy - one policy per inbox"
+ ]
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "TITLE": "Agent capacity policy",
+ "DESCRIPTION": "Manage workload for agents.",
+ "FEATURES": [
+ "Define maximum conversations per inbox",
+ "Create exceptions based on labels and time",
+ "Add agents to a policy - one policy per agent"
+ ]
+ }
+ },
+ "AGENT_ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Assignment policy",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "ORDER": "Order",
+ "PRIORITY": "Priority",
+ "ACTIVE": "Active",
+ "INACTIVE": "Inactive",
+ "POPOVER": "Added inboxes",
+ "EDIT": "Breyta"
+ },
+ "NO_RECORDS_FOUND": "No assignment policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create assignment policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy created successfully",
+ "ERROR_MESSAGE": "Failed to create assignment policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit assignment policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_INBOX_DIALOG": {
+ "TITLE": "Add inbox",
+ "DESCRIPTION": "{inboxName} inbox is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Hætta við"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update assignment policy"
+ },
+ "INBOX_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Inbox added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Inbox removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove inbox from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Description:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "STATUS": {
+ "LABEL": "Staða:",
+ "PLACEHOLDER": "Select status",
+ "ACTIVE": "Policy is active",
+ "INACTIVE": "Policy is inactive"
+ },
+ "ASSIGNMENT_ORDER": {
+ "LABEL": "Assignment order",
+ "ROUND_ROBIN": {
+ "LABEL": "Round robin",
+ "DESCRIPTION": "Assign conversations evenly among agents."
+ },
+ "BALANCED": {
+ "LABEL": "Balanced",
+ "DESCRIPTION": "Assign conversations based on available capacity."
+ }
+ },
+ "ASSIGNMENT_PRIORITY": {
+ "LABEL": "Assignment priority",
+ "EARLIEST_CREATED": {
+ "LABEL": "Earliest created",
+ "DESCRIPTION": "The conversation that was created first gets assigned first."
+ },
+ "LONGEST_WAITING": {
+ "LABEL": "Longest waiting",
+ "DESCRIPTION": "The conversation waiting the longest gets assigned first."
+ }
+ },
+ "FAIR_DISTRIBUTION": {
+ "LABEL": "Fair distribution policy",
+ "DESCRIPTION": "Set the maximum number of conversations that can be assigned per agent within a time window to avoid overloading any one agent. This required field defaults to 100 conversations per hour.",
+ "INPUT_MAX": "Assign max",
+ "DURATION": "Conversations per agent in every"
+ },
+ "INBOXES": {
+ "LABEL": "Added inboxes",
+ "DESCRIPTION": "Add inboxes for which this policy will be applicable.",
+ "ADD_BUTTON": "Add inbox",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select inboxes to add",
+ "ADD_BUTTON": "Bæta við"
+ },
+ "EMPTY_STATE": "No inboxes added to this policy, add an inbox to get started",
+ "API": {
+ "SUCCESS_MESSAGE": "Inbox successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Assignment policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete assignment policy"
+ }
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent capacity",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "POPOVER": "Added agents",
+ "EDIT": "Breyta"
+ },
+ "NO_RECORDS_FOUND": "No agent capacity policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create agent capacity policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy created successfully",
+ "ERROR_MESSAGE": "Failed to create agent capacity policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit agent capacity policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_AGENT_DIALOG": {
+ "TITLE": "Add agent",
+ "DESCRIPTION": "{agentName} is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Hætta við"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update agent capacity policy"
+ },
+ "AGENT_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Agent added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Agent removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove agent from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Description:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "INBOX_CAPACITY_LIMIT": {
+ "LABEL": "Inbox capacity limits",
+ "ADD_BUTTON": "Add inbox",
+ "FIELD": {
+ "SELECT_INBOX": "Select inbox",
+ "MAX_CONVERSATIONS": "Max conversations",
+ "SET_LIMIT": "Set limit"
+ },
+ "EMPTY_STATE": "No inbox limit set"
+ },
+ "EXCLUSION_RULES": {
+ "LABEL": "Exclusion rules",
+ "DESCRIPTION": "Conversations that satisfy the following conditions would not count towards agent capacity",
+ "TAGS": {
+ "LABEL": "Exclude conversations tagged with specific labels",
+ "ADD_TAG": "add tag",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select tags to add"
+ },
+ "EMPTY_STATE": "No tags added to this policy."
+ },
+ "DURATION": {
+ "LABEL": "Exclude conversations older than a specified duration",
+ "PLACEHOLDER": "Set time"
+ }
+ },
+ "USERS": {
+ "LABEL": "Assigned agents",
+ "DESCRIPTION": "Add agents for which this policy will be applicable.",
+ "ADD_BUTTON": "Add agent",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select agents to add",
+ "ADD_BUTTON": "Bæta við"
+ },
+ "EMPTY_STATE": "No agents added",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Agent capacity policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete agent capacity policy"
+ }
+ },
+ "DELETE_POLICY": {
+ "TITLE": "Delete policy",
+ "DESCRIPTION": "Are you sure you want to delete this policy? This action cannot be undone.",
+ "CONFIRM_BUTTON_LABEL": "Eyða",
+ "CANCEL_BUTTON_LABEL": "Hætta við"
+ }
}
}
diff --git a/app/javascript/dashboard/i18n/locale/is/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/is/whatsappTemplates.json
index 55b7680af..1f030beed 100644
--- a/app/javascript/dashboard/i18n/locale/is/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/is/whatsappTemplates.json
@@ -40,6 +40,7 @@
"BUTTON_LABEL": "Button {index}",
"COUPON_CODE": "Enter coupon code (max 15 chars)",
"MEDIA_URL_LABEL": "Enter {type} URL",
+ "DOCUMENT_NAME_PLACEHOLDER": "Enter document filename (e.g., Invoice_2025.pdf)",
"BUTTON_PARAMETER": "Enter button parameter"
}
}
diff --git a/app/javascript/dashboard/i18n/locale/it/automation.json b/app/javascript/dashboard/i18n/locale/it/automation.json
index df3a5c0a8..6945d146a 100644
--- a/app/javascript/dashboard/i18n/locale/it/automation.json
+++ b/app/javascript/dashboard/i18n/locale/it/automation.json
@@ -177,7 +177,8 @@
"REFERER_LINK": "Referrer Link",
"ASSIGNEE_NAME": "Assignee",
"TEAM_NAME": "Team",
- "PRIORITY": "Priorità"
+ "PRIORITY": "Priorità",
+ "LABELS": "Etichette"
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/it/contact.json b/app/javascript/dashboard/i18n/locale/it/contact.json
index d1844f228..e8d127c37 100644
--- a/app/javascript/dashboard/i18n/locale/it/contact.json
+++ b/app/javascript/dashboard/i18n/locale/it/contact.json
@@ -610,6 +610,15 @@
"SEND_MESSAGE": "Invia messaggio"
}
},
+ "TWILIO_OPTIONS": {
+ "LABEL": "Select template",
+ "SEARCH_PLACEHOLDER": "Search templates",
+ "EMPTY_STATE": "No templates found",
+ "TEMPLATE_PARSER": {
+ "BACK": "Torna indietro",
+ "SEND_MESSAGE": "Invia messaggio"
+ }
+ },
"ACTION_BUTTONS": {
"DISCARD": "Discard",
"SEND": "Send ({keyCode})"
diff --git a/app/javascript/dashboard/i18n/locale/it/contactFilters.json b/app/javascript/dashboard/i18n/locale/it/contactFilters.json
index 5ff43bcaa..df937f137 100644
--- a/app/javascript/dashboard/i18n/locale/it/contactFilters.json
+++ b/app/javascript/dashboard/i18n/locale/it/contactFilters.json
@@ -48,7 +48,8 @@
"CREATED_AT": "Creato il",
"LAST_ACTIVITY": "Ultima attività",
"REFERER_LINK": "Link di riferimento",
- "BLOCKED": "Blocked"
+ "BLOCKED": "Blocked",
+ "LABELS": "Etichette"
},
"GROUPS": {
"STANDARD_FILTERS": "Filtri standard",
diff --git a/app/javascript/dashboard/i18n/locale/it/contentTemplates.json b/app/javascript/dashboard/i18n/locale/it/contentTemplates.json
new file mode 100644
index 000000000..f67da5e73
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/it/contentTemplates.json
@@ -0,0 +1,51 @@
+{
+ "CONTENT_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Twilio Templates",
+ "SUBTITLE": "Select the Twilio template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Cerca modelli",
+ "NO_TEMPLATES_FOUND": "Nessun modello trovato per",
+ "NO_CONTENT": "No content",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Categoria",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No Twilio templates available. Click refresh to sync templates from Twilio.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Lingua",
+ "TEMPLATE_BODY": "Corpo modello",
+ "CATEGORY": "Categoria"
+ },
+ "TYPES": {
+ "MEDIA": "Media",
+ "QUICK_REPLY": "Quick Reply",
+ "TEXT": "Testo"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variabili",
+ "LANGUAGE": "Lingua",
+ "CATEGORY": "Categoria",
+ "VARIABLE_PLACEHOLDER": "Inserisci il valore di {variable}",
+ "GO_BACK_LABEL": "Torna indietro",
+ "SEND_MESSAGE_LABEL": "Invia messaggio",
+ "FORM_ERROR_MESSAGE": "Si prega di compilare tutte le variabili prima di inviare",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "MEDIA_URL_LABEL": "Enter full media URL",
+ "MEDIA_URL_PLACEHOLDER": "https://example.com/image.jpg"
+ },
+ "FORM": {
+ "BACK_BUTTON": "Indietro",
+ "SEND_MESSAGE_BUTTON": "Invia messaggio"
+ }
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/it/conversation.json b/app/javascript/dashboard/i18n/locale/it/conversation.json
index 5cf0c76e1..5eccb170e 100644
--- a/app/javascript/dashboard/i18n/locale/it/conversation.json
+++ b/app/javascript/dashboard/i18n/locale/it/conversation.json
@@ -35,6 +35,11 @@
"API_HOURS_WINDOW": "Puoi rispondere a questa conversazione solo entro {hours} ore",
"NOT_ASSIGNED_TO_YOU": "Questa conversazione non è assegnata. Vuoi assegnare questa conversazione a te stesso?",
"ASSIGN_TO_ME": "Assegna a me",
+ "BOT_HANDOFF_MESSAGE": "You are responding to a conversation which is currently handled by an assistant or a bot.",
+ "BOT_HANDOFF_ACTION": "Mark open and assign to you",
+ "BOT_HANDOFF_REOPEN_ACTION": "Mark conversation open",
+ "BOT_HANDOFF_SUCCESS": "Conversation has been handed over to you",
+ "BOT_HANDOFF_ERROR": "Failed to take over the conversation. Please try again.",
"TWILIO_WHATSAPP_CAN_REPLY": "È possibile rispondere a questa conversazione solo utilizzando un messaggio modello a causa di",
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "Restrizione della finestra del messaggio a 24 ore",
"OLD_INSTAGRAM_INBOX_REPLY_BANNER": "Questo account Instagram è stato migrato alla nuova casella di posta del canale Instagram. Tutti i nuovi messaggi verranno visualizzati lì. Non sarà più possibile inviare messaggi da questa conversazione.",
@@ -66,6 +71,17 @@
"SHOW_LABELS": "Show labels",
"HIDE_LABELS": "Hide labels"
},
+ "VOICE_CALL": {
+ "INCOMING_CALL": "Incoming call",
+ "OUTGOING_CALL": "Outgoing call",
+ "CALL_IN_PROGRESS": "Call in progress",
+ "NO_ANSWER": "No answer",
+ "MISSED_CALL": "Missed call",
+ "CALL_ENDED": "Call ended",
+ "NOT_ANSWERED_YET": "Not answered yet",
+ "THEY_ANSWERED": "They answered",
+ "YOU_ANSWERED": "You answered"
+ },
"HEADER": {
"RESOLVE_ACTION": "Risolvi",
"REOPEN_ACTION": "Riapri",
diff --git a/app/javascript/dashboard/i18n/locale/it/helpCenter.json b/app/javascript/dashboard/i18n/locale/it/helpCenter.json
index 9adeee19f..11fa48dba 100644
--- a/app/javascript/dashboard/i18n/locale/it/helpCenter.json
+++ b/app/javascript/dashboard/i18n/locale/it/helpCenter.json
@@ -741,7 +741,8 @@
"LIVE_CHAT_WIDGET": {
"LABEL": "Live chat widget",
"PLACEHOLDER": "Select live chat widget",
- "HELP_TEXT": "Select a live chat widget that will appear on your help center"
+ "HELP_TEXT": "Select a live chat widget that will appear on your help center",
+ "NONE_OPTION": "No widget"
},
"BRAND_COLOR": {
"LABEL": "Brand color"
@@ -807,6 +808,58 @@
"ERROR_MESSAGE": "Unable to update portal"
}
}
+ },
+ "PDF_UPLOAD": {
+ "TITLE": "Upload PDF Document",
+ "DESCRIPTION": "Upload a PDF document to automatically generate FAQs using AI",
+ "DRAG_DROP_TEXT": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "ADDITIONAL_CONTEXT_LABEL": "Additional Context (Optional)",
+ "ADDITIONAL_CONTEXT_PLACEHOLDER": "Provide any additional context or instructions for FAQ generation...",
+ "UPLOADING": "Caricamento...",
+ "UPLOAD": "Upload & Process",
+ "CANCEL": "annulla",
+ "ERROR_INVALID_TYPE": "Please select a valid PDF file",
+ "ERROR_FILE_TOO_LARGE": "File size must be less than 512MB",
+ "ERROR_UPLOAD_FAILED": "Failed to upload PDF. Please try again."
+ },
+ "PDF_DOCUMENTS": {
+ "TITLE": "PDF Documents",
+ "DESCRIPTION": "Manage uploaded PDF documents and generate FAQs from them",
+ "UPLOAD_PDF": "Upload PDF",
+ "UPLOAD_FIRST_PDF": "Upload your first PDF",
+ "UPLOADED_BY": "Uploaded by",
+ "GENERATE_FAQS": "Generate FAQs",
+ "GENERATING": "Generating...",
+ "CONFIRM_DELETE": "Are you sure you want to delete {filename}?",
+ "EMPTY_STATE": {
+ "TITLE": "No PDF documents yet",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQs using AI"
+ },
+ "STATUS": {
+ "UPLOADED": "Ready",
+ "PROCESSING": "Processing",
+ "PROCESSED": "Completato",
+ "FAILED": "Failed"
+ }
+ },
+ "CONTENT_GENERATION": {
+ "TITLE": "Content Generation",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQ content using AI",
+ "UPLOAD_TITLE": "Upload PDF Document",
+ "DRAG_DROP": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "UPLOADING": "Processing document...",
+ "UPLOAD_SUCCESS": "Document processed successfully!",
+ "UPLOAD_ERROR": "Failed to upload document. Please try again.",
+ "INVALID_FILE_TYPE": "Please select a valid PDF file",
+ "FILE_TOO_LARGE": "File size must be less than 512MB",
+ "GENERATED_CONTENT": "Generated FAQ Content",
+ "PUBLISH_SELECTED": "Publish Selected",
+ "PUBLISHING": "Publishing...",
+ "FROM_DOCUMENT": "From document",
+ "NO_CONTENT": "No generated content available. Upload a PDF document to get started.",
+ "LOADING": "Loading generated content..."
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/it/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/it/inboxMgmt.json
index d335618d5..62e5a707b 100644
--- a/app/javascript/dashboard/i18n/locale/it/inboxMgmt.json
+++ b/app/javascript/dashboard/i18n/locale/it/inboxMgmt.json
@@ -272,8 +272,8 @@
},
"SUBMIT_BUTTON": "Crea un canale WhatsApp",
"EMBEDDED_SIGNUP": {
- "TITLE": "Configurazione rapida con Meta",
- "DESC": "Sarai reindirizzato a Meta per accedere al tuo account WhatsApp Business. Avere accesso amministratore aiuterà a rendere la configurazione semplice e facile.",
+ "TITLE": "Quick setup with Meta",
+ "DESC": "Use the WhatsApp Embedded Signup flow to quickly connect new numbers. You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
"BENEFITS": {
"TITLE": "Vantaggi della registrazione integrata:",
"EASY_SETUP": "Nessuna configurazione manuale richiesta",
@@ -281,9 +281,8 @@
"AUTO_CONFIG": "Configurazione automatica del webhook e del numero di telefono"
},
"LEARN_MORE": {
- "TEXT": "To learn more about integrated signup, pricing, and limitations, visit",
- "LINK_TEXT": "this link.",
- "LINK_URL": "https://developers.facebook.com/docs/whatsapp/embedded-signup/custom-flows/onboarding-business-app-users#limitations"
+ "TEXT": "To learn more about integrated signup, pricing, and limitations, visit {link}.",
+ "LINK_TEXT": "this link"
},
"SUBMIT_BUTTON": "Connetti con WhatsApp Business",
"AUTH_PROCESSING": "Autenticazione con Meta",
@@ -296,7 +295,9 @@
"INVALID_BUSINESS_DATA": "Dati aziendali non validi ricevuti da Facebook. Riprova.",
"SIGNUP_ERROR": "Errore di registrazione",
"AUTH_NOT_COMPLETED": "Autenticazione non completata. Riavvia il processo.",
- "SUCCESS_FALLBACK": "Account WhatsApp Business è stato configurato con successo"
+ "SUCCESS_FALLBACK": "Account WhatsApp Business è stato configurato con successo",
+ "MANUAL_FALLBACK": "If your number is already connected to the WhatsApp Business Platform (API), or if you’re a tech provider onboarding your own number, please use the {link} flow",
+ "MANUAL_LINK_TEXT": "manual setup flow"
},
"API": {
"ERROR_MESSAGE": "Non siamo stati in grado di salvare il canale WhatsApp"
@@ -424,7 +425,51 @@
},
"AUTH": {
"TITLE": "Scegli un canale",
- "DESC": "Chatwoot supporta i widget di chat live, Facebook Messenger, profili Twitter, WhatsApp, Email, ecc., come canali. Se vuoi costruire un canale personalizzato, puoi crearlo usando il canale API. Per iniziare, scegli uno dei canali qui sotto."
+ "DESC": "Chatwoot supporta i widget di chat live, Facebook Messenger, profili Twitter, WhatsApp, Email, ecc., come canali. Se vuoi costruire un canale personalizzato, puoi crearlo usando il canale API. Per iniziare, scegli uno dei canali qui sotto.",
+ "TITLE_NEXT": "Complete the setup",
+ "TITLE_FINISH": "Voilà!",
+ "CHANNEL": {
+ "WEBSITE": {
+ "TITLE": "Website",
+ "DESCRIPTION": "Create a live-chat widget"
+ },
+ "FACEBOOK": {
+ "TITLE": "Facebook",
+ "DESCRIPTION": "Connect your Facebook page"
+ },
+ "WHATSAPP": {
+ "TITLE": "WhatsApp",
+ "DESCRIPTION": "Support your customers on WhatsApp"
+ },
+ "EMAIL": {
+ "TITLE": "Email",
+ "DESCRIPTION": "Connect with Gmail, Outlook, or other providers"
+ },
+ "SMS": {
+ "TITLE": "SMS",
+ "DESCRIPTION": "Integrate SMS channel with Twilio or bandwidth"
+ },
+ "API": {
+ "TITLE": "API",
+ "DESCRIPTION": "Make a custom channel using our API"
+ },
+ "TELEGRAM": {
+ "TITLE": "Telegram",
+ "DESCRIPTION": "Configure Telegram channel using Bot token"
+ },
+ "LINE": {
+ "TITLE": "Line",
+ "DESCRIPTION": "Integrate your Line channel"
+ },
+ "INSTAGRAM": {
+ "TITLE": "Instagram",
+ "DESCRIPTION": "Connect your instagram account"
+ },
+ "VOICE": {
+ "TITLE": "Voice",
+ "DESCRIPTION": "Integrate with Twilio Voice"
+ }
+ }
},
"AGENTS": {
"TITLE": "Agenti",
@@ -478,7 +523,10 @@
"MESSAGE": "Ora puoi interagire con i tuoi clienti attraverso il nuovo canale. Buona assistenza",
"BUTTON_TEXT": "Portami lì",
"MORE_SETTINGS": "Altre impostazioni",
- "WEBSITE_SUCCESS": "Hai completato la creazione di un canale sito web. Copia il codice mostrato qui sotto e incollalo sul tuo sito. La prossima volta che un cliente usa la live chat, la conversazione apparirà automaticamente nella tua casella."
+ "WEBSITE_SUCCESS": "Hai completato la creazione di un canale sito web. Copia il codice mostrato qui sotto e incollalo sul tuo sito. La prossima volta che un cliente usa la live chat, la conversazione apparirà automaticamente nella tua casella.",
+ "WHATSAPP_QR_INSTRUCTION": "Scan the QR code above to quickly test your WhatsApp inbox",
+ "MESSENGER_QR_INSTRUCTION": "Scan the QR code above to quickly test your Facebook Messenger inbox",
+ "TELEGRAM_QR_INSTRUCTION": "Scan the QR code above to quickly test your Telegram inbox"
},
"REAUTH": "Riautorizza",
"VIEW": "Visualizza",
@@ -868,9 +916,18 @@
"SCRIPT_SETTINGS": "\n window.chatwootSettings = {options};"
},
"EMAIL_PROVIDERS": {
- "MICROSOFT": "Microsoft",
- "GOOGLE": "Google",
- "OTHER_PROVIDERS": "Other Providers"
+ "MICROSOFT": {
+ "TITLE": "Microsoft",
+ "DESCRIPTION": "Connect with Microsoft"
+ },
+ "GOOGLE": {
+ "TITLE": "Google",
+ "DESCRIPTION": "Connect with Google"
+ },
+ "OTHER_PROVIDERS": {
+ "TITLE": "Other Providers",
+ "DESCRIPTION": "Connect with Other Providers"
+ }
},
"CHANNELS": {
"MESSENGER": "Messenger",
diff --git a/app/javascript/dashboard/i18n/locale/it/integrations.json b/app/javascript/dashboard/i18n/locale/it/integrations.json
index 102539b29..7b6dee7f7 100644
--- a/app/javascript/dashboard/i18n/locale/it/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/it/integrations.json
@@ -704,11 +704,28 @@
"ERROR_MESSAGE": "There was an error creating the document, please try again."
},
"FORM": {
+ "TYPE": {
+ "LABEL": "Document Type",
+ "URL": "URL",
+ "PDF": "PDF File"
+ },
"URL": {
"LABEL": "URL",
"PLACEHOLDER": "Enter the URL of the document",
"ERROR": "Please provide a valid URL for the document"
},
+ "PDF_FILE": {
+ "LABEL": "PDF File",
+ "CHOOSE_FILE": "Choose PDF file",
+ "ERROR": "Please select a PDF file",
+ "HELP_TEXT": "Maximum file size: 10MB",
+ "INVALID_TYPE": "Please select a valid PDF file",
+ "TOO_LARGE": "File size exceeds 10MB limit"
+ },
+ "NAME": {
+ "LABEL": "Document Name (Optional)",
+ "PLACEHOLDER": "Enter a name for the document"
+ },
"ASSISTANT": {
"LABEL": "Assistant",
"PLACEHOLDER": "Select the assistant",
@@ -744,6 +761,7 @@
"SELECTED": "{count} selected",
"SELECT_ALL": "Select all ({count})",
"UNSELECT_ALL": "Unselect all ({count})",
+ "SEARCH_PLACEHOLDER": "Search FAQs...",
"BULK_APPROVE_BUTTON": "Approve",
"BULK_DELETE_BUTTON": "Elimina",
"BULK_APPROVE": {
diff --git a/app/javascript/dashboard/i18n/locale/it/mfa.json b/app/javascript/dashboard/i18n/locale/it/mfa.json
new file mode 100644
index 000000000..62d2eed7d
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/it/mfa.json
@@ -0,0 +1,106 @@
+{
+ "MFA_SETTINGS": {
+ "TITLE": "Two-Factor Authentication",
+ "SUBTITLE": "Secure your account with TOTP-based authentication",
+ "DESCRIPTION": "Add an extra layer of security to your account using a time-based one-time password (TOTP)",
+ "STATUS_TITLE": "Authentication Status",
+ "STATUS_DESCRIPTION": "Manage your two-factor authentication settings and backup recovery codes",
+ "ENABLED": "Abilitato",
+ "DISABLED": "Disabilitato",
+ "STATUS_ENABLED": "Two-factor authentication is active",
+ "STATUS_ENABLED_DESC": "Your account is protected with an additional layer of security",
+ "ENABLE_BUTTON": "Enable Two-Factor Authentication",
+ "ENHANCE_SECURITY": "Enhance Your Account Security",
+ "ENHANCE_SECURITY_DESC": "Two-factor authentication adds an extra layer of security by requiring a verification code from your authenticator app in addition to your password.",
+ "SETUP": {
+ "STEP_NUMBER_1": "1",
+ "STEP_NUMBER_2": "2",
+ "STEP1_TITLE": "Scan QR Code with Your Authenticator App",
+ "STEP1_DESCRIPTION": "Use Google Authenticator, Authy, or any TOTP-compatible app",
+ "LOADING_QR": "Loading...",
+ "MANUAL_ENTRY": "Can't scan? Enter code manually",
+ "SECRET_KEY": "Secret Key",
+ "COPY": "Copia",
+ "ENTER_CODE": "Enter the 6-digit code from your authenticator app",
+ "ENTER_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify & Continue",
+ "CANCEL": "annulla",
+ "ERROR_STARTING": "MFA not enabled. Please contact administrator.",
+ "INVALID_CODE": "Invalid verification code",
+ "SECRET_COPIED": "Secret key copied to clipboard",
+ "SUCCESS": "Two-factor authentication has been enabled successfully"
+ },
+ "BACKUP": {
+ "TITLE": "Save Your Backup Codes",
+ "DESCRIPTION": "Keep these codes safe. Each can be used once if you lose access to your authenticator",
+ "IMPORTANT": "Important:",
+ "IMPORTANT_NOTE": " Save these codes in a secure location. You won't be able to see them again.",
+ "DOWNLOAD": "Scarica",
+ "COPY_ALL": "Copy All",
+ "CONFIRM": "I have saved my backup codes in a secure location and understand that I won't be able to see them again",
+ "COMPLETE_SETUP": "Complete Setup",
+ "CODES_COPIED": "Backup codes copied to clipboard"
+ },
+ "MANAGEMENT": {
+ "BACKUP_CODES": "Backup Codes",
+ "BACKUP_CODES_DESC": "Generate new codes if you've lost or used your existing ones",
+ "REGENERATE": "Regenerate Backup Codes",
+ "DISABLE_MFA": "Disable 2FA",
+ "DISABLE_MFA_DESC": "Remove two-factor authentication from your account",
+ "DISABLE_BUTTON": "Disable Two-Factor Authentication"
+ },
+ "DISABLE": {
+ "TITLE": "Disable Two-Factor Authentication",
+ "DESCRIPTION": "You'll need to enter your password and a verification code to disable two-factor authentication.",
+ "PASSWORD": "Password",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Disable 2FA",
+ "CANCEL": "annulla",
+ "SUCCESS": "Two-factor authentication has been disabled",
+ "ERROR": "Failed to disable MFA. Please check your credentials."
+ },
+ "REGENERATE": {
+ "TITLE": "Regenerate Backup Codes",
+ "DESCRIPTION": "This will invalidate your existing backup codes and generate new ones. Enter your verification code to continue.",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Generate New Codes",
+ "CANCEL": "annulla",
+ "NEW_CODES_TITLE": "New Backup Codes Generated",
+ "NEW_CODES_DESC": "Your old backup codes have been invalidated. Save these new codes in a secure location.",
+ "CODES_IMPORTANT": "Important:",
+ "CODES_IMPORTANT_NOTE": " Each code can only be used once. Save them before closing this window.",
+ "DOWNLOAD_CODES": "Download Codes",
+ "COPY_ALL_CODES": "Copy All Codes",
+ "CODES_SAVED": "I've Saved My Codes",
+ "SUCCESS": "New backup codes have been generated",
+ "ERROR": "Failed to regenerate backup codes"
+ }
+ },
+ "MFA_VERIFICATION": {
+ "TITLE": "Two-Factor Authentication",
+ "DESCRIPTION": "Enter your verification code to continue",
+ "AUTHENTICATOR_APP": "Authenticator App",
+ "BACKUP_CODE": "Backup Code",
+ "ENTER_OTP_CODE": "Enter 6-digit code from your authenticator app",
+ "ENTER_BACKUP_CODE": "Enter one of your backup codes",
+ "BACKUP_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify",
+ "TRY_ANOTHER_METHOD": "Try another verification method",
+ "CANCEL_LOGIN": "Cancel and return to login",
+ "HELP_TEXT": "Having trouble signing in?",
+ "LEARN_MORE": "Learn more about 2FA",
+ "HELP_MODAL": {
+ "TITLE": "Two-Factor Authentication Help",
+ "AUTHENTICATOR_TITLE": "Using an Authenticator App",
+ "AUTHENTICATOR_DESC": "Open your authenticator app (Google Authenticator, Authy, etc.) and enter the 6-digit code shown for your account.",
+ "BACKUP_TITLE": "Using a Backup Code",
+ "BACKUP_DESC": "If you don't have access to your authenticator app, you can use one of the backup codes you saved when setting up 2FA. Each code can only be used once.",
+ "CONTACT_TITLE": "Need More Help?",
+ "CONTACT_DESC_CLOUD": "If you've lost access to both your authenticator app and backup codes, please reach out to Chatwoot support for assistance.",
+ "CONTACT_DESC_SELF_HOSTED": "If you've lost access to both your authenticator app and backup codes, please contact your administrator for assistance."
+ },
+ "VERIFICATION_FAILED": "Verification failed. Please try again."
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/it/settings.json b/app/javascript/dashboard/i18n/locale/it/settings.json
index 443bf1c47..7dc7d5d01 100644
--- a/app/javascript/dashboard/i18n/locale/it/settings.json
+++ b/app/javascript/dashboard/i18n/locale/it/settings.json
@@ -51,6 +51,13 @@
"LARGER": "Larger",
"EXTRA_LARGE": "Extra Large"
}
+ },
+ "LANGUAGE": {
+ "TITLE": "Preferred Language",
+ "NOTE": "Choose the language you want to use.",
+ "UPDATE_SUCCESS": "Your Language settings have been updated successfully",
+ "UPDATE_ERROR": "There is an error while updating the language settings, please try again",
+ "USE_ACCOUNT_DEFAULT": "Use account default"
}
},
"MESSAGE_SIGNATURE_SECTION": {
@@ -73,6 +80,11 @@
"NOTE": "Aggiornare la tua password reimposterà i tuoi accessi in più dispositivi.",
"BTN_TEXT": "Cambia password"
},
+ "SECURITY_SECTION": {
+ "TITLE": "Security",
+ "NOTE": "Manage additional security features for your account.",
+ "MFA_BUTTON": "Manage Two-Factor Authentication"
+ },
"ACCESS_TOKEN": {
"TITLE": "Token di accesso",
"NOTE": "Questo token può essere usato se stai costruendo un'integrazione basata su API",
@@ -330,6 +342,7 @@
"REPORTS_LABEL": "Etichette",
"REPORTS_INBOX": "Posta",
"REPORTS_TEAM": "Team",
+ "AGENT_ASSIGNMENT": "Agent Assignment",
"SET_AVAILABILITY_TITLE": "Imposta te stesso come",
"SET_YOUR_AVAILABILITY": "Imposta la tua disponibilità",
"SLA": "SLA",
@@ -350,7 +363,8 @@
"INFO_TEXT": "Let the system automatically mark you offline when you aren't using the app or dashboard.",
"INFO_SHORT": "Automatically mark offline when you aren't using the app."
},
- "DOCS": "Leggi i documenti"
+ "DOCS": "Leggi i documenti",
+ "SECURITY": "Security"
},
"BILLING_SETTINGS": {
"TITLE": "Fatturazione",
@@ -382,6 +396,77 @@
},
"NO_BILLING_USER": "Il tuo account di fatturazione è in fase di configurazione. Per favore aggiorna la pagina e riprova."
},
+ "SECURITY_SETTINGS": {
+ "TITLE": "Security",
+ "DESCRIPTION": "Manage your account security settings.",
+ "LINK_TEXT": "Learn more about SAML SSO",
+ "SAML": {
+ "TITLE": "SAML SSO",
+ "NOTE": "Configure SAML single sign-on for your account. Users will authenticate through your identity provider instead of using email/password.",
+ "ACS_URL": {
+ "LABEL": "ACS URL",
+ "TOOLTIP": "Assertion Consumer Service URL - Configure this URL in your IdP as the destination for SAML responses"
+ },
+ "SSO_URL": {
+ "LABEL": "SSO URL",
+ "HELP": "The URL where SAML authentication requests will be sent",
+ "PLACEHOLDER": "https://your-idp.com/saml/sso"
+ },
+ "CERTIFICATE": {
+ "LABEL": "Signing certificate in PEM format",
+ "HELP": "The public certificate from your identity provider used to verify SAML responses",
+ "PLACEHOLDER": "-----BEGIN CERTIFICATE-----\nMIIC..."
+ },
+ "FINGERPRINT": {
+ "LABEL": "Fingerprint",
+ "TOOLTIP": "SHA-1 fingerprint of the certificate - Use this to verify the certificate in your IdP configuration"
+ },
+ "COPY_SUCCESS": "Codice copiato negli appunti correttamente",
+ "SP_ENTITY_ID": {
+ "LABEL": "SP Entity ID",
+ "HELP": "Unique identifier for this application as a service provider (auto-generated).",
+ "TOOLTIP": "Unique identifier for Chatwoot as the Service Provider - Configure this in your IdP settings"
+ },
+ "IDP_ENTITY_ID": {
+ "LABEL": "Identity Provider Entity ID",
+ "HELP": "Unique identifier for your identity provider (usually found in IdP configuration)",
+ "PLACEHOLDER": "https://your-idp.com/saml"
+ },
+ "UPDATE_BUTTON": "Update SAML Settings",
+ "API": {
+ "SUCCESS": "SAML settings updated successfully",
+ "ERROR": "Failed to update SAML settings",
+ "ERROR_LOADING": "Failed to load SAML settings",
+ "DISABLED": "SAML settings disabled successfully"
+ },
+ "VALIDATION": {
+ "REQUIRED_FIELDS": "SSO URL, Identity Provider Entity ID, and Certificate are required fields",
+ "SSO_URL_ERROR": "Please enter a valid SSO URL",
+ "CERTIFICATE_ERROR": "Certificate is required",
+ "IDP_ENTITY_ID_ERROR": "Identity Provider Entity ID is required"
+ },
+ "ENTERPRISE_PAYWALL": {
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade to an Enterprise plan to access SAML single sign-on and other advanced security features.",
+ "ASK_ADMIN": "Please reach out to your administrator for the upgrade."
+ },
+ "PAYWALL": {
+ "TITLE": "Upgrade to enable SAML SSO",
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade your plan to get access to SAML single sign-on and other advanced features.",
+ "UPGRADE_NOW": "Upgrade now",
+ "CANCEL_ANYTIME": "You can change or cancel your plan anytime"
+ },
+ "ATTRIBUTE_MAPPING": {
+ "TITLE": "SAML Attribute Setup",
+ "DESCRIPTION": "The following attribute mappings must be configured in your identity provider"
+ },
+ "INFO_SECTION": {
+ "TITLE": "Service Provider Information",
+ "TOOLTIP": "Copy these values and configure them in your Identity Provider to establish the SAML connection"
+ }
+ }
+ },
"CREATE_ACCOUNT": {
"NO_ACCOUNT_WARNING": "Uh oh! Non abbiamo trovato alcun account Chatwoot. Si prega di creare un nuovo account per continuare.",
"NEW_ACCOUNT": "Nuovo account",
@@ -418,5 +503,255 @@
"SWITCH_TO_REPLY": "Passa a Risposta",
"TOGGLE_SNOOZE_DROPDOWN": "Attiva/Disattiva sospensione a discesa"
}
+ },
+ "ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent assignment",
+ "DESCRIPTION": "Define policies to effectively manage workload and route conversations based on the needs of inboxes and agents. Learn more here"
+ },
+ "ASSIGNMENT_POLICY": {
+ "TITLE": "Assignment policy",
+ "DESCRIPTION": "Manage how conversations get assigned in inboxes.",
+ "FEATURES": [
+ "Assign by conversations evenly or by available capacity",
+ "Add fair distribution rules to avoid overloading any agent",
+ "Add inboxes to a policy - one policy per inbox"
+ ]
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "TITLE": "Agent capacity policy",
+ "DESCRIPTION": "Manage workload for agents.",
+ "FEATURES": [
+ "Define maximum conversations per inbox",
+ "Create exceptions based on labels and time",
+ "Add agents to a policy - one policy per agent"
+ ]
+ }
+ },
+ "AGENT_ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Assignment policy",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "ORDER": "Order",
+ "PRIORITY": "Priorità",
+ "ACTIVE": "Attivo",
+ "INACTIVE": "Inactive",
+ "POPOVER": "Added inboxes",
+ "EDIT": "Modifica"
+ },
+ "NO_RECORDS_FOUND": "No assignment policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create assignment policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy created successfully",
+ "ERROR_MESSAGE": "Failed to create assignment policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit assignment policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_INBOX_DIALOG": {
+ "TITLE": "Add inbox",
+ "DESCRIPTION": "{inboxName} inbox is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "annulla"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update assignment policy"
+ },
+ "INBOX_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Inbox added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Inbox removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove inbox from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Descrizione:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "STATUS": {
+ "LABEL": "Stato:",
+ "PLACEHOLDER": "Select status",
+ "ACTIVE": "Policy is active",
+ "INACTIVE": "Policy is inactive"
+ },
+ "ASSIGNMENT_ORDER": {
+ "LABEL": "Assignment order",
+ "ROUND_ROBIN": {
+ "LABEL": "Round robin",
+ "DESCRIPTION": "Assign conversations evenly among agents."
+ },
+ "BALANCED": {
+ "LABEL": "Balanced",
+ "DESCRIPTION": "Assign conversations based on available capacity."
+ }
+ },
+ "ASSIGNMENT_PRIORITY": {
+ "LABEL": "Assignment priority",
+ "EARLIEST_CREATED": {
+ "LABEL": "Earliest created",
+ "DESCRIPTION": "The conversation that was created first gets assigned first."
+ },
+ "LONGEST_WAITING": {
+ "LABEL": "Longest waiting",
+ "DESCRIPTION": "The conversation waiting the longest gets assigned first."
+ }
+ },
+ "FAIR_DISTRIBUTION": {
+ "LABEL": "Fair distribution policy",
+ "DESCRIPTION": "Set the maximum number of conversations that can be assigned per agent within a time window to avoid overloading any one agent. This required field defaults to 100 conversations per hour.",
+ "INPUT_MAX": "Assign max",
+ "DURATION": "Conversations per agent in every"
+ },
+ "INBOXES": {
+ "LABEL": "Added inboxes",
+ "DESCRIPTION": "Add inboxes for which this policy will be applicable.",
+ "ADD_BUTTON": "Add inbox",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select inboxes to add",
+ "ADD_BUTTON": "Aggiungi"
+ },
+ "EMPTY_STATE": "No inboxes added to this policy, add an inbox to get started",
+ "API": {
+ "SUCCESS_MESSAGE": "Inbox successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Assignment policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete assignment policy"
+ }
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent capacity",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "POPOVER": "Added agents",
+ "EDIT": "Modifica"
+ },
+ "NO_RECORDS_FOUND": "No agent capacity policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create agent capacity policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy created successfully",
+ "ERROR_MESSAGE": "Failed to create agent capacity policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit agent capacity policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_AGENT_DIALOG": {
+ "TITLE": "Add agent",
+ "DESCRIPTION": "{agentName} is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "annulla"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update agent capacity policy"
+ },
+ "AGENT_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Agent added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Agent removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove agent from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Descrizione:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "INBOX_CAPACITY_LIMIT": {
+ "LABEL": "Inbox capacity limits",
+ "ADD_BUTTON": "Add inbox",
+ "FIELD": {
+ "SELECT_INBOX": "Select inbox",
+ "MAX_CONVERSATIONS": "Max conversations",
+ "SET_LIMIT": "Set limit"
+ },
+ "EMPTY_STATE": "No inbox limit set"
+ },
+ "EXCLUSION_RULES": {
+ "LABEL": "Exclusion rules",
+ "DESCRIPTION": "Conversations that satisfy the following conditions would not count towards agent capacity",
+ "TAGS": {
+ "LABEL": "Exclude conversations tagged with specific labels",
+ "ADD_TAG": "add tag",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select tags to add"
+ },
+ "EMPTY_STATE": "No tags added to this policy."
+ },
+ "DURATION": {
+ "LABEL": "Exclude conversations older than a specified duration",
+ "PLACEHOLDER": "Set time"
+ }
+ },
+ "USERS": {
+ "LABEL": "Assigned agents",
+ "DESCRIPTION": "Add agents for which this policy will be applicable.",
+ "ADD_BUTTON": "Add agent",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select agents to add",
+ "ADD_BUTTON": "Aggiungi"
+ },
+ "EMPTY_STATE": "No agents added",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Agent capacity policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete agent capacity policy"
+ }
+ },
+ "DELETE_POLICY": {
+ "TITLE": "Delete policy",
+ "DESCRIPTION": "Are you sure you want to delete this policy? This action cannot be undone.",
+ "CONFIRM_BUTTON_LABEL": "Elimina",
+ "CANCEL_BUTTON_LABEL": "annulla"
+ }
}
}
diff --git a/app/javascript/dashboard/i18n/locale/it/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/it/whatsappTemplates.json
index b45435cca..89d7db928 100644
--- a/app/javascript/dashboard/i18n/locale/it/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/it/whatsappTemplates.json
@@ -40,6 +40,7 @@
"BUTTON_LABEL": "Button {index}",
"COUPON_CODE": "Enter coupon code (max 15 chars)",
"MEDIA_URL_LABEL": "Enter {type} URL",
+ "DOCUMENT_NAME_PLACEHOLDER": "Enter document filename (e.g., Invoice_2025.pdf)",
"BUTTON_PARAMETER": "Enter button parameter"
}
}
diff --git a/app/javascript/dashboard/i18n/locale/ja/automation.json b/app/javascript/dashboard/i18n/locale/ja/automation.json
index 7b113c718..e293da3a9 100644
--- a/app/javascript/dashboard/i18n/locale/ja/automation.json
+++ b/app/javascript/dashboard/i18n/locale/ja/automation.json
@@ -177,7 +177,8 @@
"REFERER_LINK": "Referrer Link",
"ASSIGNEE_NAME": "担当者",
"TEAM_NAME": "チーム",
- "PRIORITY": "優先度"
+ "PRIORITY": "優先度",
+ "LABELS": "ラベル"
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/ja/contact.json b/app/javascript/dashboard/i18n/locale/ja/contact.json
index f8f71b337..bbeb2fb65 100644
--- a/app/javascript/dashboard/i18n/locale/ja/contact.json
+++ b/app/javascript/dashboard/i18n/locale/ja/contact.json
@@ -610,6 +610,15 @@
"SEND_MESSAGE": "メッセージを送信"
}
},
+ "TWILIO_OPTIONS": {
+ "LABEL": "テンプレートを選択",
+ "SEARCH_PLACEHOLDER": "テンプレートを検索",
+ "EMPTY_STATE": "テンプレートが見つかりません。",
+ "TEMPLATE_PARSER": {
+ "BACK": "戻る",
+ "SEND_MESSAGE": "メッセージを送信"
+ }
+ },
"ACTION_BUTTONS": {
"DISCARD": "破棄",
"SEND": "送信 ({keyCode})"
diff --git a/app/javascript/dashboard/i18n/locale/ja/contactFilters.json b/app/javascript/dashboard/i18n/locale/ja/contactFilters.json
index 07679ee78..58a0963ff 100644
--- a/app/javascript/dashboard/i18n/locale/ja/contactFilters.json
+++ b/app/javascript/dashboard/i18n/locale/ja/contactFilters.json
@@ -48,7 +48,8 @@
"CREATED_AT": "作成日",
"LAST_ACTIVITY": "最終アクティビティ",
"REFERER_LINK": "リファラーリンク",
- "BLOCKED": "ブロック済み"
+ "BLOCKED": "ブロック済み",
+ "LABELS": "ラベル"
},
"GROUPS": {
"STANDARD_FILTERS": "標準フィルター",
diff --git a/app/javascript/dashboard/i18n/locale/ja/contentTemplates.json b/app/javascript/dashboard/i18n/locale/ja/contentTemplates.json
new file mode 100644
index 000000000..2cbbf06de
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/ja/contentTemplates.json
@@ -0,0 +1,51 @@
+{
+ "CONTENT_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Twilio Templates",
+ "SUBTITLE": "Select the Twilio template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "テンプレートを検索",
+ "NO_TEMPLATES_FOUND": "該当するテンプレートが見つかりません:",
+ "NO_CONTENT": "コンテンツなし",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "カテゴリ",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No Twilio templates available. Click refresh to sync templates from Twilio.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "言語",
+ "TEMPLATE_BODY": "テンプレート本文",
+ "CATEGORY": "カテゴリ"
+ },
+ "TYPES": {
+ "MEDIA": "Media",
+ "QUICK_REPLY": "Quick Reply",
+ "TEXT": "テキスト"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "変数",
+ "LANGUAGE": "言語",
+ "CATEGORY": "カテゴリ",
+ "VARIABLE_PLACEHOLDER": "{variable} の値を入力",
+ "GO_BACK_LABEL": "戻る",
+ "SEND_MESSAGE_LABEL": "メッセージを送信",
+ "FORM_ERROR_MESSAGE": "送信前に全ての変数を入力してください",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "MEDIA_URL_LABEL": "Enter full media URL",
+ "MEDIA_URL_PLACEHOLDER": "https://example.com/image.jpg"
+ },
+ "FORM": {
+ "BACK_BUTTON": "戻る",
+ "SEND_MESSAGE_BUTTON": "メッセージを送信"
+ }
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/ja/conversation.json b/app/javascript/dashboard/i18n/locale/ja/conversation.json
index 7e234c123..99dde2a7a 100644
--- a/app/javascript/dashboard/i18n/locale/ja/conversation.json
+++ b/app/javascript/dashboard/i18n/locale/ja/conversation.json
@@ -35,6 +35,11 @@
"API_HOURS_WINDOW": "You can only reply to this conversation within {hours} hours",
"NOT_ASSIGNED_TO_YOU": "この会話はあなたに割り当てられていません。自分に割り当てますか?",
"ASSIGN_TO_ME": "自分に割り当て",
+ "BOT_HANDOFF_MESSAGE": "You are responding to a conversation which is currently handled by an assistant or a bot.",
+ "BOT_HANDOFF_ACTION": "Mark open and assign to you",
+ "BOT_HANDOFF_REOPEN_ACTION": "Mark conversation open",
+ "BOT_HANDOFF_SUCCESS": "Conversation has been handed over to you",
+ "BOT_HANDOFF_ERROR": "Failed to take over the conversation. Please try again.",
"TWILIO_WHATSAPP_CAN_REPLY": "この会話にはテンプレートメッセージでしか返信できません。",
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "24時間以内のメッセージウィンドウの制限",
"OLD_INSTAGRAM_INBOX_REPLY_BANNER": "This Instagram account was migrated to the new Instagram channel inbox. All new messages will show up there. You won’t be able to send messages from this conversation anymore.",
@@ -66,6 +71,17 @@
"SHOW_LABELS": "ラベルを表示",
"HIDE_LABELS": "ラベルを隠す"
},
+ "VOICE_CALL": {
+ "INCOMING_CALL": "Incoming call",
+ "OUTGOING_CALL": "Outgoing call",
+ "CALL_IN_PROGRESS": "Call in progress",
+ "NO_ANSWER": "No answer",
+ "MISSED_CALL": "Missed call",
+ "CALL_ENDED": "Call ended",
+ "NOT_ANSWERED_YET": "Not answered yet",
+ "THEY_ANSWERED": "They answered",
+ "YOU_ANSWERED": "You answered"
+ },
"HEADER": {
"RESOLVE_ACTION": "解決する",
"REOPEN_ACTION": "再開する",
diff --git a/app/javascript/dashboard/i18n/locale/ja/helpCenter.json b/app/javascript/dashboard/i18n/locale/ja/helpCenter.json
index a9a135289..c82866cfe 100644
--- a/app/javascript/dashboard/i18n/locale/ja/helpCenter.json
+++ b/app/javascript/dashboard/i18n/locale/ja/helpCenter.json
@@ -741,7 +741,8 @@
"LIVE_CHAT_WIDGET": {
"LABEL": "ライブチャットウィジェット",
"PLACEHOLDER": "ライブチャットウィジェットを選択",
- "HELP_TEXT": "ヘルプセンターに表示されるライブチャットウィジェットを選択します"
+ "HELP_TEXT": "ヘルプセンターに表示されるライブチャットウィジェットを選択します",
+ "NONE_OPTION": "No widget"
},
"BRAND_COLOR": {
"LABEL": "ブランドカラー"
@@ -807,6 +808,58 @@
"ERROR_MESSAGE": "ポータルを更新できませんでした"
}
}
+ },
+ "PDF_UPLOAD": {
+ "TITLE": "Upload PDF Document",
+ "DESCRIPTION": "Upload a PDF document to automatically generate FAQs using AI",
+ "DRAG_DROP_TEXT": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "ADDITIONAL_CONTEXT_LABEL": "Additional Context (Optional)",
+ "ADDITIONAL_CONTEXT_PLACEHOLDER": "Provide any additional context or instructions for FAQ generation...",
+ "UPLOADING": "アップロード中...",
+ "UPLOAD": "Upload & Process",
+ "CANCEL": "キャンセル",
+ "ERROR_INVALID_TYPE": "Please select a valid PDF file",
+ "ERROR_FILE_TOO_LARGE": "File size must be less than 512MB",
+ "ERROR_UPLOAD_FAILED": "Failed to upload PDF. Please try again."
+ },
+ "PDF_DOCUMENTS": {
+ "TITLE": "PDF Documents",
+ "DESCRIPTION": "Manage uploaded PDF documents and generate FAQs from them",
+ "UPLOAD_PDF": "Upload PDF",
+ "UPLOAD_FIRST_PDF": "Upload your first PDF",
+ "UPLOADED_BY": "Uploaded by",
+ "GENERATE_FAQS": "Generate FAQs",
+ "GENERATING": "生成中...",
+ "CONFIRM_DELETE": "Are you sure you want to delete {filename}?",
+ "EMPTY_STATE": {
+ "TITLE": "No PDF documents yet",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQs using AI"
+ },
+ "STATUS": {
+ "UPLOADED": "Ready",
+ "PROCESSING": "Processing",
+ "PROCESSED": "完了",
+ "FAILED": "Failed"
+ }
+ },
+ "CONTENT_GENERATION": {
+ "TITLE": "Content Generation",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQ content using AI",
+ "UPLOAD_TITLE": "Upload PDF Document",
+ "DRAG_DROP": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "UPLOADING": "Processing document...",
+ "UPLOAD_SUCCESS": "Document processed successfully!",
+ "UPLOAD_ERROR": "Failed to upload document. Please try again.",
+ "INVALID_FILE_TYPE": "Please select a valid PDF file",
+ "FILE_TOO_LARGE": "File size must be less than 512MB",
+ "GENERATED_CONTENT": "Generated FAQ Content",
+ "PUBLISH_SELECTED": "Publish Selected",
+ "PUBLISHING": "Publishing...",
+ "FROM_DOCUMENT": "From document",
+ "NO_CONTENT": "No generated content available. Upload a PDF document to get started.",
+ "LOADING": "Loading generated content..."
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/ja/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/ja/inboxMgmt.json
index a09b254c8..27d5c9fce 100644
--- a/app/javascript/dashboard/i18n/locale/ja/inboxMgmt.json
+++ b/app/javascript/dashboard/i18n/locale/ja/inboxMgmt.json
@@ -272,8 +272,8 @@
},
"SUBMIT_BUTTON": "WhatsAppチャンネルを作成",
"EMBEDDED_SIGNUP": {
- "TITLE": "Quick Setup with Meta",
- "DESC": "You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
+ "TITLE": "Quick setup with Meta",
+ "DESC": "Use the WhatsApp Embedded Signup flow to quickly connect new numbers. You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
"BENEFITS": {
"TITLE": "Benefits of Embedded Signup:",
"EASY_SETUP": "No manual configuration required",
@@ -281,9 +281,8 @@
"AUTO_CONFIG": "Automatic webhook and phone number configuration"
},
"LEARN_MORE": {
- "TEXT": "To learn more about integrated signup, pricing, and limitations, visit",
- "LINK_TEXT": "this link.",
- "LINK_URL": "https://developers.facebook.com/docs/whatsapp/embedded-signup/custom-flows/onboarding-business-app-users#limitations"
+ "TEXT": "To learn more about integrated signup, pricing, and limitations, visit {link}.",
+ "LINK_TEXT": "this link"
},
"SUBMIT_BUTTON": "Connect with WhatsApp Business",
"AUTH_PROCESSING": "Authenticating with Meta",
@@ -296,7 +295,9 @@
"INVALID_BUSINESS_DATA": "Invalid business data received from Facebook. Please try again.",
"SIGNUP_ERROR": "Signup error occurred",
"AUTH_NOT_COMPLETED": "Authentication not completed. Please restart the process.",
- "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured"
+ "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured",
+ "MANUAL_FALLBACK": "If your number is already connected to the WhatsApp Business Platform (API), or if you’re a tech provider onboarding your own number, please use the {link} flow",
+ "MANUAL_LINK_TEXT": "manual setup flow"
},
"API": {
"ERROR_MESSAGE": "WhatsAppチャンネルを保存できませんでした"
@@ -424,7 +425,51 @@
},
"AUTH": {
"TITLE": "チャンネルを選択",
- "DESC": "Chatwootは、ライブチャットウィジェット、Facebook Messenger、Twitterプロフィール、WhatsApp、Eメールなどのチャンネルをサポートしています。カスタムチャンネルを作成したい場合は、APIチャンネルを使用して作成できます。開始するには、以下のチャンネルのいずれかを選択してください。"
+ "DESC": "Chatwootは、ライブチャットウィジェット、Facebook Messenger、Twitterプロフィール、WhatsApp、Eメールなどのチャンネルをサポートしています。カスタムチャンネルを作成したい場合は、APIチャンネルを使用して作成できます。開始するには、以下のチャンネルのいずれかを選択してください。",
+ "TITLE_NEXT": "Complete the setup",
+ "TITLE_FINISH": "Voilà!",
+ "CHANNEL": {
+ "WEBSITE": {
+ "TITLE": "ウェブサイト",
+ "DESCRIPTION": "Create a live-chat widget"
+ },
+ "FACEBOOK": {
+ "TITLE": "Facebook",
+ "DESCRIPTION": "Connect your Facebook page"
+ },
+ "WHATSAPP": {
+ "TITLE": "WhatsApp",
+ "DESCRIPTION": "Support your customers on WhatsApp"
+ },
+ "EMAIL": {
+ "TITLE": "Eメール",
+ "DESCRIPTION": "Connect with Gmail, Outlook, or other providers"
+ },
+ "SMS": {
+ "TITLE": "SMS",
+ "DESCRIPTION": "Integrate SMS channel with Twilio or bandwidth"
+ },
+ "API": {
+ "TITLE": "API",
+ "DESCRIPTION": "Make a custom channel using our API"
+ },
+ "TELEGRAM": {
+ "TITLE": "Telegram",
+ "DESCRIPTION": "Configure Telegram channel using Bot token"
+ },
+ "LINE": {
+ "TITLE": "Line",
+ "DESCRIPTION": "Integrate your Line channel"
+ },
+ "INSTAGRAM": {
+ "TITLE": "Instagram",
+ "DESCRIPTION": "Connect your instagram account"
+ },
+ "VOICE": {
+ "TITLE": "Voice",
+ "DESCRIPTION": "Integrate with Twilio Voice"
+ }
+ }
},
"AGENTS": {
"TITLE": "担当者",
@@ -478,7 +523,10 @@
"MESSAGE": "新しいチャンネルを通じて顧客と交流できます。サポートを楽しんでください",
"BUTTON_TEXT": "受信トレイに移動",
"MORE_SETTINGS": "その他の設定",
- "WEBSITE_SUCCESS": "ウェブサイトチャンネルの作成が正常に完了しました。以下のコードをコピーしてウェブサイトに貼り付けてください。次回、お客様がライブチャットを使用すると、会話は自動的に受信トレイに表示されます。"
+ "WEBSITE_SUCCESS": "ウェブサイトチャンネルの作成が正常に完了しました。以下のコードをコピーしてウェブサイトに貼り付けてください。次回、お客様がライブチャットを使用すると、会話は自動的に受信トレイに表示されます。",
+ "WHATSAPP_QR_INSTRUCTION": "Scan the QR code above to quickly test your WhatsApp inbox",
+ "MESSENGER_QR_INSTRUCTION": "Scan the QR code above to quickly test your Facebook Messenger inbox",
+ "TELEGRAM_QR_INSTRUCTION": "Scan the QR code above to quickly test your Telegram inbox"
},
"REAUTH": "再認証",
"VIEW": "表示",
@@ -868,9 +916,18 @@
"SCRIPT_SETTINGS": "\n window.chatwootSettings = {options};"
},
"EMAIL_PROVIDERS": {
- "MICROSOFT": "Microsoft",
- "GOOGLE": "Google",
- "OTHER_PROVIDERS": "Other Providers"
+ "MICROSOFT": {
+ "TITLE": "Microsoft",
+ "DESCRIPTION": "Connect with Microsoft"
+ },
+ "GOOGLE": {
+ "TITLE": "Google",
+ "DESCRIPTION": "Connect with Google"
+ },
+ "OTHER_PROVIDERS": {
+ "TITLE": "Other Providers",
+ "DESCRIPTION": "Connect with Other Providers"
+ }
},
"CHANNELS": {
"MESSENGER": "Messenger",
diff --git a/app/javascript/dashboard/i18n/locale/ja/integrations.json b/app/javascript/dashboard/i18n/locale/ja/integrations.json
index 65c471f3e..978ab5172 100644
--- a/app/javascript/dashboard/i18n/locale/ja/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/ja/integrations.json
@@ -704,11 +704,28 @@
"ERROR_MESSAGE": "ドキュメントの作成中にエラーが発生しました。もう一度お試しください。"
},
"FORM": {
+ "TYPE": {
+ "LABEL": "Document Type",
+ "URL": "URL",
+ "PDF": "PDF File"
+ },
"URL": {
"LABEL": "URL",
"PLACEHOLDER": "ドキュメントのURLを入力",
"ERROR": "有効なURLを入力してください"
},
+ "PDF_FILE": {
+ "LABEL": "PDF File",
+ "CHOOSE_FILE": "Choose PDF file",
+ "ERROR": "Please select a PDF file",
+ "HELP_TEXT": "Maximum file size: 10MB",
+ "INVALID_TYPE": "Please select a valid PDF file",
+ "TOO_LARGE": "File size exceeds 10MB limit"
+ },
+ "NAME": {
+ "LABEL": "Document Name (Optional)",
+ "PLACEHOLDER": "Enter a name for the document"
+ },
"ASSISTANT": {
"LABEL": "アシスタント",
"PLACEHOLDER": "アシスタントを選択",
@@ -744,6 +761,7 @@
"SELECTED": "{count} selected",
"SELECT_ALL": "Select all ({count})",
"UNSELECT_ALL": "Unselect all ({count})",
+ "SEARCH_PLACEHOLDER": "Search FAQs...",
"BULK_APPROVE_BUTTON": "Approve",
"BULK_DELETE_BUTTON": "削除",
"BULK_APPROVE": {
diff --git a/app/javascript/dashboard/i18n/locale/ja/mfa.json b/app/javascript/dashboard/i18n/locale/ja/mfa.json
new file mode 100644
index 000000000..dffd3e91a
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/ja/mfa.json
@@ -0,0 +1,106 @@
+{
+ "MFA_SETTINGS": {
+ "TITLE": "Two-Factor Authentication",
+ "SUBTITLE": "Secure your account with TOTP-based authentication",
+ "DESCRIPTION": "Add an extra layer of security to your account using a time-based one-time password (TOTP)",
+ "STATUS_TITLE": "Authentication Status",
+ "STATUS_DESCRIPTION": "Manage your two-factor authentication settings and backup recovery codes",
+ "ENABLED": "有効です",
+ "DISABLED": "無効です",
+ "STATUS_ENABLED": "Two-factor authentication is active",
+ "STATUS_ENABLED_DESC": "Your account is protected with an additional layer of security",
+ "ENABLE_BUTTON": "Enable Two-Factor Authentication",
+ "ENHANCE_SECURITY": "Enhance Your Account Security",
+ "ENHANCE_SECURITY_DESC": "Two-factor authentication adds an extra layer of security by requiring a verification code from your authenticator app in addition to your password.",
+ "SETUP": {
+ "STEP_NUMBER_1": "3",
+ "STEP_NUMBER_2": "4",
+ "STEP1_TITLE": "Scan QR Code with Your Authenticator App",
+ "STEP1_DESCRIPTION": "Use Google Authenticator, Authy, or any TOTP-compatible app",
+ "LOADING_QR": "読み込み中...",
+ "MANUAL_ENTRY": "Can't scan? Enter code manually",
+ "SECRET_KEY": "Secret Key",
+ "COPY": "コピー",
+ "ENTER_CODE": "Enter the 6-digit code from your authenticator app",
+ "ENTER_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify & Continue",
+ "CANCEL": "キャンセル",
+ "ERROR_STARTING": "MFA not enabled. Please contact administrator.",
+ "INVALID_CODE": "Invalid verification code",
+ "SECRET_COPIED": "Secret key copied to clipboard",
+ "SUCCESS": "Two-factor authentication has been enabled successfully"
+ },
+ "BACKUP": {
+ "TITLE": "Save Your Backup Codes",
+ "DESCRIPTION": "Keep these codes safe. Each can be used once if you lose access to your authenticator",
+ "IMPORTANT": "Important:",
+ "IMPORTANT_NOTE": " Save these codes in a secure location. You won't be able to see them again.",
+ "DOWNLOAD": "ダウンロード",
+ "COPY_ALL": "Copy All",
+ "CONFIRM": "I have saved my backup codes in a secure location and understand that I won't be able to see them again",
+ "COMPLETE_SETUP": "Complete Setup",
+ "CODES_COPIED": "Backup codes copied to clipboard"
+ },
+ "MANAGEMENT": {
+ "BACKUP_CODES": "Backup Codes",
+ "BACKUP_CODES_DESC": "Generate new codes if you've lost or used your existing ones",
+ "REGENERATE": "Regenerate Backup Codes",
+ "DISABLE_MFA": "Disable 2FA",
+ "DISABLE_MFA_DESC": "Remove two-factor authentication from your account",
+ "DISABLE_BUTTON": "Disable Two-Factor Authentication"
+ },
+ "DISABLE": {
+ "TITLE": "Disable Two-Factor Authentication",
+ "DESCRIPTION": "You'll need to enter your password and a verification code to disable two-factor authentication.",
+ "PASSWORD": "パスワード",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Disable 2FA",
+ "CANCEL": "キャンセル",
+ "SUCCESS": "Two-factor authentication has been disabled",
+ "ERROR": "Failed to disable MFA. Please check your credentials."
+ },
+ "REGENERATE": {
+ "TITLE": "Regenerate Backup Codes",
+ "DESCRIPTION": "This will invalidate your existing backup codes and generate new ones. Enter your verification code to continue.",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Generate New Codes",
+ "CANCEL": "キャンセル",
+ "NEW_CODES_TITLE": "New Backup Codes Generated",
+ "NEW_CODES_DESC": "Your old backup codes have been invalidated. Save these new codes in a secure location.",
+ "CODES_IMPORTANT": "Important:",
+ "CODES_IMPORTANT_NOTE": " Each code can only be used once. Save them before closing this window.",
+ "DOWNLOAD_CODES": "Download Codes",
+ "COPY_ALL_CODES": "Copy All Codes",
+ "CODES_SAVED": "I've Saved My Codes",
+ "SUCCESS": "New backup codes have been generated",
+ "ERROR": "Failed to regenerate backup codes"
+ }
+ },
+ "MFA_VERIFICATION": {
+ "TITLE": "Two-Factor Authentication",
+ "DESCRIPTION": "Enter your verification code to continue",
+ "AUTHENTICATOR_APP": "Authenticator App",
+ "BACKUP_CODE": "Backup Code",
+ "ENTER_OTP_CODE": "Enter 6-digit code from your authenticator app",
+ "ENTER_BACKUP_CODE": "Enter one of your backup codes",
+ "BACKUP_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify",
+ "TRY_ANOTHER_METHOD": "Try another verification method",
+ "CANCEL_LOGIN": "Cancel and return to login",
+ "HELP_TEXT": "Having trouble signing in?",
+ "LEARN_MORE": "Learn more about 2FA",
+ "HELP_MODAL": {
+ "TITLE": "Two-Factor Authentication Help",
+ "AUTHENTICATOR_TITLE": "Using an Authenticator App",
+ "AUTHENTICATOR_DESC": "Open your authenticator app (Google Authenticator, Authy, etc.) and enter the 6-digit code shown for your account.",
+ "BACKUP_TITLE": "Using a Backup Code",
+ "BACKUP_DESC": "If you don't have access to your authenticator app, you can use one of the backup codes you saved when setting up 2FA. Each code can only be used once.",
+ "CONTACT_TITLE": "Need More Help?",
+ "CONTACT_DESC_CLOUD": "If you've lost access to both your authenticator app and backup codes, please reach out to Chatwoot support for assistance.",
+ "CONTACT_DESC_SELF_HOSTED": "If you've lost access to both your authenticator app and backup codes, please contact your administrator for assistance."
+ },
+ "VERIFICATION_FAILED": "Verification failed. Please try again."
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/ja/settings.json b/app/javascript/dashboard/i18n/locale/ja/settings.json
index c938f7793..7020d5f38 100644
--- a/app/javascript/dashboard/i18n/locale/ja/settings.json
+++ b/app/javascript/dashboard/i18n/locale/ja/settings.json
@@ -51,6 +51,13 @@
"LARGER": "Larger",
"EXTRA_LARGE": "Extra Large"
}
+ },
+ "LANGUAGE": {
+ "TITLE": "Preferred Language",
+ "NOTE": "Choose the language you want to use.",
+ "UPDATE_SUCCESS": "Your Language settings have been updated successfully",
+ "UPDATE_ERROR": "There is an error while updating the language settings, please try again",
+ "USE_ACCOUNT_DEFAULT": "Use account default"
}
},
"MESSAGE_SIGNATURE_SECTION": {
@@ -73,6 +80,11 @@
"NOTE": "パスワードを更新すると、複数のデバイスでログインがリセットされます。",
"BTN_TEXT": "パスワードを変更"
},
+ "SECURITY_SECTION": {
+ "TITLE": "Security",
+ "NOTE": "Manage additional security features for your account.",
+ "MFA_BUTTON": "Manage Two-Factor Authentication"
+ },
"ACCESS_TOKEN": {
"TITLE": "アクセストークン",
"NOTE": "このトークンは、API連携を構築する場合に利用します。",
@@ -330,6 +342,7 @@
"REPORTS_LABEL": "ラベル",
"REPORTS_INBOX": "受信トレイ",
"REPORTS_TEAM": "チーム",
+ "AGENT_ASSIGNMENT": "Agent Assignment",
"SET_AVAILABILITY_TITLE": "ステータスを設定",
"SET_YOUR_AVAILABILITY": "利用可能ステータスを設定",
"SLA": "SLA",
@@ -350,7 +363,8 @@
"INFO_TEXT": "アプリやダッシュボードを使用していない場合に、システムが自動的にオフラインに設定します。",
"INFO_SHORT": "使用していない場合、自動的にオフラインにします。"
},
- "DOCS": "ドキュメントを読む"
+ "DOCS": "ドキュメントを読む",
+ "SECURITY": "Security"
},
"BILLING_SETTINGS": {
"TITLE": "請求設定",
@@ -382,6 +396,77 @@
},
"NO_BILLING_USER": "請求アカウントを設定中です。ページを更新してもう一度お試しください。"
},
+ "SECURITY_SETTINGS": {
+ "TITLE": "Security",
+ "DESCRIPTION": "Manage your account security settings.",
+ "LINK_TEXT": "Learn more about SAML SSO",
+ "SAML": {
+ "TITLE": "SAML SSO",
+ "NOTE": "Configure SAML single sign-on for your account. Users will authenticate through your identity provider instead of using email/password.",
+ "ACS_URL": {
+ "LABEL": "ACS URL",
+ "TOOLTIP": "Assertion Consumer Service URL - Configure this URL in your IdP as the destination for SAML responses"
+ },
+ "SSO_URL": {
+ "LABEL": "SSO URL",
+ "HELP": "The URL where SAML authentication requests will be sent",
+ "PLACEHOLDER": "https://your-idp.com/saml/sso"
+ },
+ "CERTIFICATE": {
+ "LABEL": "Signing certificate in PEM format",
+ "HELP": "The public certificate from your identity provider used to verify SAML responses",
+ "PLACEHOLDER": "-----BEGIN CERTIFICATE-----\nMIIC..."
+ },
+ "FINGERPRINT": {
+ "LABEL": "Fingerprint",
+ "TOOLTIP": "SHA-1 fingerprint of the certificate - Use this to verify the certificate in your IdP configuration"
+ },
+ "COPY_SUCCESS": "コードが正常にクリップボードにコピーされました",
+ "SP_ENTITY_ID": {
+ "LABEL": "SP Entity ID",
+ "HELP": "Unique identifier for this application as a service provider (auto-generated).",
+ "TOOLTIP": "Unique identifier for Chatwoot as the Service Provider - Configure this in your IdP settings"
+ },
+ "IDP_ENTITY_ID": {
+ "LABEL": "Identity Provider Entity ID",
+ "HELP": "Unique identifier for your identity provider (usually found in IdP configuration)",
+ "PLACEHOLDER": "https://your-idp.com/saml"
+ },
+ "UPDATE_BUTTON": "Update SAML Settings",
+ "API": {
+ "SUCCESS": "SAML settings updated successfully",
+ "ERROR": "Failed to update SAML settings",
+ "ERROR_LOADING": "Failed to load SAML settings",
+ "DISABLED": "SAML settings disabled successfully"
+ },
+ "VALIDATION": {
+ "REQUIRED_FIELDS": "SSO URL, Identity Provider Entity ID, and Certificate are required fields",
+ "SSO_URL_ERROR": "Please enter a valid SSO URL",
+ "CERTIFICATE_ERROR": "Certificate is required",
+ "IDP_ENTITY_ID_ERROR": "Identity Provider Entity ID is required"
+ },
+ "ENTERPRISE_PAYWALL": {
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade to an Enterprise plan to access SAML single sign-on and other advanced security features.",
+ "ASK_ADMIN": "管理者にアップグレードを依頼してください。"
+ },
+ "PAYWALL": {
+ "TITLE": "Upgrade to enable SAML SSO",
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade your plan to get access to SAML single sign-on and other advanced features.",
+ "UPGRADE_NOW": "今すぐアップグレード",
+ "CANCEL_ANYTIME": "プランはいつでも変更またはキャンセルできます"
+ },
+ "ATTRIBUTE_MAPPING": {
+ "TITLE": "SAML Attribute Setup",
+ "DESCRIPTION": "The following attribute mappings must be configured in your identity provider"
+ },
+ "INFO_SECTION": {
+ "TITLE": "Service Provider Information",
+ "TOOLTIP": "Copy these values and configure them in your Identity Provider to establish the SAML connection"
+ }
+ }
+ },
"CREATE_ACCOUNT": {
"NO_ACCOUNT_WARNING": "Chatwootアカウントが見つかりませんでした。続行するには新しいアカウントを作成してください。",
"NEW_ACCOUNT": "新規アカウント",
@@ -418,5 +503,255 @@
"SWITCH_TO_REPLY": "返信に切り替え",
"TOGGLE_SNOOZE_DROPDOWN": "スヌーズドロップダウンを切り替え"
}
+ },
+ "ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent assignment",
+ "DESCRIPTION": "Define policies to effectively manage workload and route conversations based on the needs of inboxes and agents. Learn more here"
+ },
+ "ASSIGNMENT_POLICY": {
+ "TITLE": "Assignment policy",
+ "DESCRIPTION": "Manage how conversations get assigned in inboxes.",
+ "FEATURES": [
+ "Assign by conversations evenly or by available capacity",
+ "Add fair distribution rules to avoid overloading any agent",
+ "Add inboxes to a policy - one policy per inbox"
+ ]
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "TITLE": "Agent capacity policy",
+ "DESCRIPTION": "Manage workload for agents.",
+ "FEATURES": [
+ "Define maximum conversations per inbox",
+ "Create exceptions based on labels and time",
+ "Add agents to a policy - one policy per agent"
+ ]
+ }
+ },
+ "AGENT_ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Assignment policy",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "ORDER": "Order",
+ "PRIORITY": "優先度",
+ "ACTIVE": "有効",
+ "INACTIVE": "Inactive",
+ "POPOVER": "Added inboxes",
+ "EDIT": "編集"
+ },
+ "NO_RECORDS_FOUND": "No assignment policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create assignment policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy created successfully",
+ "ERROR_MESSAGE": "Failed to create assignment policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit assignment policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_INBOX_DIALOG": {
+ "TITLE": "Add inbox",
+ "DESCRIPTION": "{inboxName} inbox is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "キャンセル"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update assignment policy"
+ },
+ "INBOX_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Inbox added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Inbox removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove inbox from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "説明:",
+ "PLACEHOLDER": "説明を入力"
+ },
+ "STATUS": {
+ "LABEL": "ステータス:",
+ "PLACEHOLDER": "状況を選択",
+ "ACTIVE": "Policy is active",
+ "INACTIVE": "Policy is inactive"
+ },
+ "ASSIGNMENT_ORDER": {
+ "LABEL": "Assignment order",
+ "ROUND_ROBIN": {
+ "LABEL": "Round robin",
+ "DESCRIPTION": "Assign conversations evenly among agents."
+ },
+ "BALANCED": {
+ "LABEL": "Balanced",
+ "DESCRIPTION": "Assign conversations based on available capacity."
+ }
+ },
+ "ASSIGNMENT_PRIORITY": {
+ "LABEL": "Assignment priority",
+ "EARLIEST_CREATED": {
+ "LABEL": "Earliest created",
+ "DESCRIPTION": "The conversation that was created first gets assigned first."
+ },
+ "LONGEST_WAITING": {
+ "LABEL": "Longest waiting",
+ "DESCRIPTION": "The conversation waiting the longest gets assigned first."
+ }
+ },
+ "FAIR_DISTRIBUTION": {
+ "LABEL": "Fair distribution policy",
+ "DESCRIPTION": "Set the maximum number of conversations that can be assigned per agent within a time window to avoid overloading any one agent. This required field defaults to 100 conversations per hour.",
+ "INPUT_MAX": "Assign max",
+ "DURATION": "Conversations per agent in every"
+ },
+ "INBOXES": {
+ "LABEL": "Added inboxes",
+ "DESCRIPTION": "Add inboxes for which this policy will be applicable.",
+ "ADD_BUTTON": "Add inbox",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select inboxes to add",
+ "ADD_BUTTON": "追加"
+ },
+ "EMPTY_STATE": "No inboxes added to this policy, add an inbox to get started",
+ "API": {
+ "SUCCESS_MESSAGE": "Inbox successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Assignment policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete assignment policy"
+ }
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent capacity",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "POPOVER": "Added agents",
+ "EDIT": "編集"
+ },
+ "NO_RECORDS_FOUND": "No agent capacity policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create agent capacity policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy created successfully",
+ "ERROR_MESSAGE": "Failed to create agent capacity policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit agent capacity policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_AGENT_DIALOG": {
+ "TITLE": "Add agent",
+ "DESCRIPTION": "{agentName} is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "キャンセル"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update agent capacity policy"
+ },
+ "AGENT_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Agent added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Agent removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove agent from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "説明:",
+ "PLACEHOLDER": "説明を入力"
+ },
+ "INBOX_CAPACITY_LIMIT": {
+ "LABEL": "Inbox capacity limits",
+ "ADD_BUTTON": "Add inbox",
+ "FIELD": {
+ "SELECT_INBOX": "Select inbox",
+ "MAX_CONVERSATIONS": "Max conversations",
+ "SET_LIMIT": "Set limit"
+ },
+ "EMPTY_STATE": "No inbox limit set"
+ },
+ "EXCLUSION_RULES": {
+ "LABEL": "Exclusion rules",
+ "DESCRIPTION": "Conversations that satisfy the following conditions would not count towards agent capacity",
+ "TAGS": {
+ "LABEL": "Exclude conversations tagged with specific labels",
+ "ADD_TAG": "add tag",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select tags to add"
+ },
+ "EMPTY_STATE": "No tags added to this policy."
+ },
+ "DURATION": {
+ "LABEL": "Exclude conversations older than a specified duration",
+ "PLACEHOLDER": "Set time"
+ }
+ },
+ "USERS": {
+ "LABEL": "Assigned agents",
+ "DESCRIPTION": "Add agents for which this policy will be applicable.",
+ "ADD_BUTTON": "Add agent",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select agents to add",
+ "ADD_BUTTON": "追加"
+ },
+ "EMPTY_STATE": "No agents added",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Agent capacity policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete agent capacity policy"
+ }
+ },
+ "DELETE_POLICY": {
+ "TITLE": "Delete policy",
+ "DESCRIPTION": "Are you sure you want to delete this policy? This action cannot be undone.",
+ "CONFIRM_BUTTON_LABEL": "削除",
+ "CANCEL_BUTTON_LABEL": "キャンセル"
+ }
}
}
diff --git a/app/javascript/dashboard/i18n/locale/ja/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/ja/whatsappTemplates.json
index 3f1a39d1b..a45896070 100644
--- a/app/javascript/dashboard/i18n/locale/ja/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/ja/whatsappTemplates.json
@@ -40,6 +40,7 @@
"BUTTON_LABEL": "Button {index}",
"COUPON_CODE": "Enter coupon code (max 15 chars)",
"MEDIA_URL_LABEL": "Enter {type} URL",
+ "DOCUMENT_NAME_PLACEHOLDER": "Enter document filename (e.g., Invoice_2025.pdf)",
"BUTTON_PARAMETER": "Enter button parameter"
}
}
diff --git a/app/javascript/dashboard/i18n/locale/ka/automation.json b/app/javascript/dashboard/i18n/locale/ka/automation.json
index 80274f488..43245a1d5 100644
--- a/app/javascript/dashboard/i18n/locale/ka/automation.json
+++ b/app/javascript/dashboard/i18n/locale/ka/automation.json
@@ -177,7 +177,8 @@
"REFERER_LINK": "Referrer Link",
"ASSIGNEE_NAME": "Assignee",
"TEAM_NAME": "Team",
- "PRIORITY": "Priority"
+ "PRIORITY": "Priority",
+ "LABELS": "Labels"
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/ka/contact.json b/app/javascript/dashboard/i18n/locale/ka/contact.json
index 0ae5ecc38..b147164ec 100644
--- a/app/javascript/dashboard/i18n/locale/ka/contact.json
+++ b/app/javascript/dashboard/i18n/locale/ka/contact.json
@@ -610,6 +610,15 @@
"SEND_MESSAGE": "Send message"
}
},
+ "TWILIO_OPTIONS": {
+ "LABEL": "Select template",
+ "SEARCH_PLACEHOLDER": "Search templates",
+ "EMPTY_STATE": "No templates found",
+ "TEMPLATE_PARSER": {
+ "BACK": "Go back",
+ "SEND_MESSAGE": "Send message"
+ }
+ },
"ACTION_BUTTONS": {
"DISCARD": "Discard",
"SEND": "Send ({keyCode})"
diff --git a/app/javascript/dashboard/i18n/locale/ka/contactFilters.json b/app/javascript/dashboard/i18n/locale/ka/contactFilters.json
index bb3221c6e..4c62f0789 100644
--- a/app/javascript/dashboard/i18n/locale/ka/contactFilters.json
+++ b/app/javascript/dashboard/i18n/locale/ka/contactFilters.json
@@ -48,7 +48,8 @@
"CREATED_AT": "Created At",
"LAST_ACTIVITY": "Last Activity",
"REFERER_LINK": "Referrer link",
- "BLOCKED": "Blocked"
+ "BLOCKED": "Blocked",
+ "LABELS": "Labels"
},
"GROUPS": {
"STANDARD_FILTERS": "Standard Filters",
diff --git a/app/javascript/dashboard/i18n/locale/ka/contentTemplates.json b/app/javascript/dashboard/i18n/locale/ka/contentTemplates.json
new file mode 100644
index 000000000..a9b1d54c4
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/ka/contentTemplates.json
@@ -0,0 +1,51 @@
+{
+ "CONTENT_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Twilio Templates",
+ "SUBTITLE": "Select the Twilio template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Search Templates",
+ "NO_TEMPLATES_FOUND": "No templates found for",
+ "NO_CONTENT": "No content",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Category",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No Twilio templates available. Click refresh to sync templates from Twilio.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Language",
+ "TEMPLATE_BODY": "Template Body",
+ "CATEGORY": "Category"
+ },
+ "TYPES": {
+ "MEDIA": "Media",
+ "QUICK_REPLY": "Quick Reply",
+ "TEXT": "Text"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variables",
+ "LANGUAGE": "Language",
+ "CATEGORY": "Category",
+ "VARIABLE_PLACEHOLDER": "Enter {variable} value",
+ "GO_BACK_LABEL": "Go Back",
+ "SEND_MESSAGE_LABEL": "Send Message",
+ "FORM_ERROR_MESSAGE": "Please fill all variables before sending",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "MEDIA_URL_LABEL": "Enter full media URL",
+ "MEDIA_URL_PLACEHOLDER": "https://example.com/image.jpg"
+ },
+ "FORM": {
+ "BACK_BUTTON": "Back",
+ "SEND_MESSAGE_BUTTON": "Send Message"
+ }
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/ka/conversation.json b/app/javascript/dashboard/i18n/locale/ka/conversation.json
index 308f24f51..9fd39b70f 100644
--- a/app/javascript/dashboard/i18n/locale/ka/conversation.json
+++ b/app/javascript/dashboard/i18n/locale/ka/conversation.json
@@ -35,6 +35,11 @@
"API_HOURS_WINDOW": "You can only reply to this conversation within {hours} hours",
"NOT_ASSIGNED_TO_YOU": "This conversation is not assigned to you. Would you like to assign this conversation to yourself?",
"ASSIGN_TO_ME": "Assign to me",
+ "BOT_HANDOFF_MESSAGE": "You are responding to a conversation which is currently handled by an assistant or a bot.",
+ "BOT_HANDOFF_ACTION": "Mark open and assign to you",
+ "BOT_HANDOFF_REOPEN_ACTION": "Mark conversation open",
+ "BOT_HANDOFF_SUCCESS": "Conversation has been handed over to you",
+ "BOT_HANDOFF_ERROR": "Failed to take over the conversation. Please try again.",
"TWILIO_WHATSAPP_CAN_REPLY": "You can only reply to this conversation using a template message due to",
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "24 hour message window restriction",
"OLD_INSTAGRAM_INBOX_REPLY_BANNER": "This Instagram account was migrated to the new Instagram channel inbox. All new messages will show up there. You won’t be able to send messages from this conversation anymore.",
@@ -66,6 +71,17 @@
"SHOW_LABELS": "Show labels",
"HIDE_LABELS": "Hide labels"
},
+ "VOICE_CALL": {
+ "INCOMING_CALL": "Incoming call",
+ "OUTGOING_CALL": "Outgoing call",
+ "CALL_IN_PROGRESS": "Call in progress",
+ "NO_ANSWER": "No answer",
+ "MISSED_CALL": "Missed call",
+ "CALL_ENDED": "Call ended",
+ "NOT_ANSWERED_YET": "Not answered yet",
+ "THEY_ANSWERED": "They answered",
+ "YOU_ANSWERED": "You answered"
+ },
"HEADER": {
"RESOLVE_ACTION": "Resolve",
"REOPEN_ACTION": "Reopen",
diff --git a/app/javascript/dashboard/i18n/locale/ka/helpCenter.json b/app/javascript/dashboard/i18n/locale/ka/helpCenter.json
index fd2b1a788..0ab8d62ff 100644
--- a/app/javascript/dashboard/i18n/locale/ka/helpCenter.json
+++ b/app/javascript/dashboard/i18n/locale/ka/helpCenter.json
@@ -741,7 +741,8 @@
"LIVE_CHAT_WIDGET": {
"LABEL": "Live chat widget",
"PLACEHOLDER": "Select live chat widget",
- "HELP_TEXT": "Select a live chat widget that will appear on your help center"
+ "HELP_TEXT": "Select a live chat widget that will appear on your help center",
+ "NONE_OPTION": "No widget"
},
"BRAND_COLOR": {
"LABEL": "Brand color"
@@ -807,6 +808,58 @@
"ERROR_MESSAGE": "Unable to update portal"
}
}
+ },
+ "PDF_UPLOAD": {
+ "TITLE": "Upload PDF Document",
+ "DESCRIPTION": "Upload a PDF document to automatically generate FAQs using AI",
+ "DRAG_DROP_TEXT": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "ADDITIONAL_CONTEXT_LABEL": "Additional Context (Optional)",
+ "ADDITIONAL_CONTEXT_PLACEHOLDER": "Provide any additional context or instructions for FAQ generation...",
+ "UPLOADING": "Uploading...",
+ "UPLOAD": "Upload & Process",
+ "CANCEL": "Cancel",
+ "ERROR_INVALID_TYPE": "Please select a valid PDF file",
+ "ERROR_FILE_TOO_LARGE": "File size must be less than 512MB",
+ "ERROR_UPLOAD_FAILED": "Failed to upload PDF. Please try again."
+ },
+ "PDF_DOCUMENTS": {
+ "TITLE": "PDF Documents",
+ "DESCRIPTION": "Manage uploaded PDF documents and generate FAQs from them",
+ "UPLOAD_PDF": "Upload PDF",
+ "UPLOAD_FIRST_PDF": "Upload your first PDF",
+ "UPLOADED_BY": "Uploaded by",
+ "GENERATE_FAQS": "Generate FAQs",
+ "GENERATING": "Generating...",
+ "CONFIRM_DELETE": "Are you sure you want to delete {filename}?",
+ "EMPTY_STATE": {
+ "TITLE": "No PDF documents yet",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQs using AI"
+ },
+ "STATUS": {
+ "UPLOADED": "Ready",
+ "PROCESSING": "Processing",
+ "PROCESSED": "Completed",
+ "FAILED": "Failed"
+ }
+ },
+ "CONTENT_GENERATION": {
+ "TITLE": "Content Generation",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQ content using AI",
+ "UPLOAD_TITLE": "Upload PDF Document",
+ "DRAG_DROP": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "UPLOADING": "Processing document...",
+ "UPLOAD_SUCCESS": "Document processed successfully!",
+ "UPLOAD_ERROR": "Failed to upload document. Please try again.",
+ "INVALID_FILE_TYPE": "Please select a valid PDF file",
+ "FILE_TOO_LARGE": "File size must be less than 512MB",
+ "GENERATED_CONTENT": "Generated FAQ Content",
+ "PUBLISH_SELECTED": "Publish Selected",
+ "PUBLISHING": "Publishing...",
+ "FROM_DOCUMENT": "From document",
+ "NO_CONTENT": "No generated content available. Upload a PDF document to get started.",
+ "LOADING": "Loading generated content..."
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/ka/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/ka/inboxMgmt.json
index 031fcb57e..cc3b7f015 100644
--- a/app/javascript/dashboard/i18n/locale/ka/inboxMgmt.json
+++ b/app/javascript/dashboard/i18n/locale/ka/inboxMgmt.json
@@ -272,8 +272,8 @@
},
"SUBMIT_BUTTON": "Create WhatsApp Channel",
"EMBEDDED_SIGNUP": {
- "TITLE": "Quick Setup with Meta",
- "DESC": "You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
+ "TITLE": "Quick setup with Meta",
+ "DESC": "Use the WhatsApp Embedded Signup flow to quickly connect new numbers. You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
"BENEFITS": {
"TITLE": "Benefits of Embedded Signup:",
"EASY_SETUP": "No manual configuration required",
@@ -281,9 +281,8 @@
"AUTO_CONFIG": "Automatic webhook and phone number configuration"
},
"LEARN_MORE": {
- "TEXT": "To learn more about integrated signup, pricing, and limitations, visit",
- "LINK_TEXT": "this link.",
- "LINK_URL": "https://developers.facebook.com/docs/whatsapp/embedded-signup/custom-flows/onboarding-business-app-users#limitations"
+ "TEXT": "To learn more about integrated signup, pricing, and limitations, visit {link}.",
+ "LINK_TEXT": "this link"
},
"SUBMIT_BUTTON": "Connect with WhatsApp Business",
"AUTH_PROCESSING": "Authenticating with Meta",
@@ -296,7 +295,9 @@
"INVALID_BUSINESS_DATA": "Invalid business data received from Facebook. Please try again.",
"SIGNUP_ERROR": "Signup error occurred",
"AUTH_NOT_COMPLETED": "Authentication not completed. Please restart the process.",
- "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured"
+ "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured",
+ "MANUAL_FALLBACK": "If your number is already connected to the WhatsApp Business Platform (API), or if you’re a tech provider onboarding your own number, please use the {link} flow",
+ "MANUAL_LINK_TEXT": "manual setup flow"
},
"API": {
"ERROR_MESSAGE": "We were not able to save the WhatsApp channel"
@@ -424,7 +425,51 @@
},
"AUTH": {
"TITLE": "Choose a channel",
- "DESC": "Chatwoot supports live-chat widgets, Facebook Messenger, Twitter profiles, WhatsApp, Emails, etc., as channels. If you want to build a custom channel, you can create it using the API channel. To get started, choose one of the channels below."
+ "DESC": "Chatwoot supports live-chat widgets, Facebook Messenger, Twitter profiles, WhatsApp, Emails, etc., as channels. If you want to build a custom channel, you can create it using the API channel. To get started, choose one of the channels below.",
+ "TITLE_NEXT": "Complete the setup",
+ "TITLE_FINISH": "Voilà!",
+ "CHANNEL": {
+ "WEBSITE": {
+ "TITLE": "Website",
+ "DESCRIPTION": "Create a live-chat widget"
+ },
+ "FACEBOOK": {
+ "TITLE": "Facebook",
+ "DESCRIPTION": "Connect your Facebook page"
+ },
+ "WHATSAPP": {
+ "TITLE": "WhatsApp",
+ "DESCRIPTION": "Support your customers on WhatsApp"
+ },
+ "EMAIL": {
+ "TITLE": "Email",
+ "DESCRIPTION": "Connect with Gmail, Outlook, or other providers"
+ },
+ "SMS": {
+ "TITLE": "SMS",
+ "DESCRIPTION": "Integrate SMS channel with Twilio or bandwidth"
+ },
+ "API": {
+ "TITLE": "API",
+ "DESCRIPTION": "Make a custom channel using our API"
+ },
+ "TELEGRAM": {
+ "TITLE": "Telegram",
+ "DESCRIPTION": "Configure Telegram channel using Bot token"
+ },
+ "LINE": {
+ "TITLE": "Line",
+ "DESCRIPTION": "Integrate your Line channel"
+ },
+ "INSTAGRAM": {
+ "TITLE": "Instagram",
+ "DESCRIPTION": "Connect your instagram account"
+ },
+ "VOICE": {
+ "TITLE": "Voice",
+ "DESCRIPTION": "Integrate with Twilio Voice"
+ }
+ }
},
"AGENTS": {
"TITLE": "Agents",
@@ -478,7 +523,10 @@
"MESSAGE": "You can now engage with your customers through your new Channel. Happy supporting",
"BUTTON_TEXT": "Take me there",
"MORE_SETTINGS": "More settings",
- "WEBSITE_SUCCESS": "You have successfully finished creating a website channel. Copy the code shown below and paste it on your website. Next time a customer use the live chat, the conversation will automatically appear on your inbox."
+ "WEBSITE_SUCCESS": "You have successfully finished creating a website channel. Copy the code shown below and paste it on your website. Next time a customer use the live chat, the conversation will automatically appear on your inbox.",
+ "WHATSAPP_QR_INSTRUCTION": "Scan the QR code above to quickly test your WhatsApp inbox",
+ "MESSENGER_QR_INSTRUCTION": "Scan the QR code above to quickly test your Facebook Messenger inbox",
+ "TELEGRAM_QR_INSTRUCTION": "Scan the QR code above to quickly test your Telegram inbox"
},
"REAUTH": "Reauthorize",
"VIEW": "View",
@@ -868,9 +916,18 @@
"SCRIPT_SETTINGS": "\n window.chatwootSettings = {options};"
},
"EMAIL_PROVIDERS": {
- "MICROSOFT": "Microsoft",
- "GOOGLE": "Google",
- "OTHER_PROVIDERS": "Other Providers"
+ "MICROSOFT": {
+ "TITLE": "Microsoft",
+ "DESCRIPTION": "Connect with Microsoft"
+ },
+ "GOOGLE": {
+ "TITLE": "Google",
+ "DESCRIPTION": "Connect with Google"
+ },
+ "OTHER_PROVIDERS": {
+ "TITLE": "Other Providers",
+ "DESCRIPTION": "Connect with Other Providers"
+ }
},
"CHANNELS": {
"MESSENGER": "Messenger",
diff --git a/app/javascript/dashboard/i18n/locale/ka/integrations.json b/app/javascript/dashboard/i18n/locale/ka/integrations.json
index f0c7abbd3..03898d278 100644
--- a/app/javascript/dashboard/i18n/locale/ka/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/ka/integrations.json
@@ -704,11 +704,28 @@
"ERROR_MESSAGE": "There was an error creating the document, please try again."
},
"FORM": {
+ "TYPE": {
+ "LABEL": "Document Type",
+ "URL": "URL",
+ "PDF": "PDF File"
+ },
"URL": {
"LABEL": "URL",
"PLACEHOLDER": "Enter the URL of the document",
"ERROR": "Please provide a valid URL for the document"
},
+ "PDF_FILE": {
+ "LABEL": "PDF File",
+ "CHOOSE_FILE": "Choose PDF file",
+ "ERROR": "Please select a PDF file",
+ "HELP_TEXT": "Maximum file size: 10MB",
+ "INVALID_TYPE": "Please select a valid PDF file",
+ "TOO_LARGE": "File size exceeds 10MB limit"
+ },
+ "NAME": {
+ "LABEL": "Document Name (Optional)",
+ "PLACEHOLDER": "Enter a name for the document"
+ },
"ASSISTANT": {
"LABEL": "Assistant",
"PLACEHOLDER": "Select the assistant",
@@ -744,6 +761,7 @@
"SELECTED": "{count} selected",
"SELECT_ALL": "Select all ({count})",
"UNSELECT_ALL": "Unselect all ({count})",
+ "SEARCH_PLACEHOLDER": "Search FAQs...",
"BULK_APPROVE_BUTTON": "Approve",
"BULK_DELETE_BUTTON": "Delete",
"BULK_APPROVE": {
diff --git a/app/javascript/dashboard/i18n/locale/ka/mfa.json b/app/javascript/dashboard/i18n/locale/ka/mfa.json
new file mode 100644
index 000000000..f7556fdcf
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/ka/mfa.json
@@ -0,0 +1,106 @@
+{
+ "MFA_SETTINGS": {
+ "TITLE": "Two-Factor Authentication",
+ "SUBTITLE": "Secure your account with TOTP-based authentication",
+ "DESCRIPTION": "Add an extra layer of security to your account using a time-based one-time password (TOTP)",
+ "STATUS_TITLE": "Authentication Status",
+ "STATUS_DESCRIPTION": "Manage your two-factor authentication settings and backup recovery codes",
+ "ENABLED": "Enabled",
+ "DISABLED": "Disabled",
+ "STATUS_ENABLED": "Two-factor authentication is active",
+ "STATUS_ENABLED_DESC": "Your account is protected with an additional layer of security",
+ "ENABLE_BUTTON": "Enable Two-Factor Authentication",
+ "ENHANCE_SECURITY": "Enhance Your Account Security",
+ "ENHANCE_SECURITY_DESC": "Two-factor authentication adds an extra layer of security by requiring a verification code from your authenticator app in addition to your password.",
+ "SETUP": {
+ "STEP_NUMBER_1": "1",
+ "STEP_NUMBER_2": "2",
+ "STEP1_TITLE": "Scan QR Code with Your Authenticator App",
+ "STEP1_DESCRIPTION": "Use Google Authenticator, Authy, or any TOTP-compatible app",
+ "LOADING_QR": "Loading...",
+ "MANUAL_ENTRY": "Can't scan? Enter code manually",
+ "SECRET_KEY": "Secret Key",
+ "COPY": "Copy",
+ "ENTER_CODE": "Enter the 6-digit code from your authenticator app",
+ "ENTER_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify & Continue",
+ "CANCEL": "Cancel",
+ "ERROR_STARTING": "MFA not enabled. Please contact administrator.",
+ "INVALID_CODE": "Invalid verification code",
+ "SECRET_COPIED": "Secret key copied to clipboard",
+ "SUCCESS": "Two-factor authentication has been enabled successfully"
+ },
+ "BACKUP": {
+ "TITLE": "Save Your Backup Codes",
+ "DESCRIPTION": "Keep these codes safe. Each can be used once if you lose access to your authenticator",
+ "IMPORTANT": "Important:",
+ "IMPORTANT_NOTE": " Save these codes in a secure location. You won't be able to see them again.",
+ "DOWNLOAD": "Download",
+ "COPY_ALL": "Copy All",
+ "CONFIRM": "I have saved my backup codes in a secure location and understand that I won't be able to see them again",
+ "COMPLETE_SETUP": "Complete Setup",
+ "CODES_COPIED": "Backup codes copied to clipboard"
+ },
+ "MANAGEMENT": {
+ "BACKUP_CODES": "Backup Codes",
+ "BACKUP_CODES_DESC": "Generate new codes if you've lost or used your existing ones",
+ "REGENERATE": "Regenerate Backup Codes",
+ "DISABLE_MFA": "Disable 2FA",
+ "DISABLE_MFA_DESC": "Remove two-factor authentication from your account",
+ "DISABLE_BUTTON": "Disable Two-Factor Authentication"
+ },
+ "DISABLE": {
+ "TITLE": "Disable Two-Factor Authentication",
+ "DESCRIPTION": "You'll need to enter your password and a verification code to disable two-factor authentication.",
+ "PASSWORD": "Password",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Disable 2FA",
+ "CANCEL": "Cancel",
+ "SUCCESS": "Two-factor authentication has been disabled",
+ "ERROR": "Failed to disable MFA. Please check your credentials."
+ },
+ "REGENERATE": {
+ "TITLE": "Regenerate Backup Codes",
+ "DESCRIPTION": "This will invalidate your existing backup codes and generate new ones. Enter your verification code to continue.",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Generate New Codes",
+ "CANCEL": "Cancel",
+ "NEW_CODES_TITLE": "New Backup Codes Generated",
+ "NEW_CODES_DESC": "Your old backup codes have been invalidated. Save these new codes in a secure location.",
+ "CODES_IMPORTANT": "Important:",
+ "CODES_IMPORTANT_NOTE": " Each code can only be used once. Save them before closing this window.",
+ "DOWNLOAD_CODES": "Download Codes",
+ "COPY_ALL_CODES": "Copy All Codes",
+ "CODES_SAVED": "I've Saved My Codes",
+ "SUCCESS": "New backup codes have been generated",
+ "ERROR": "Failed to regenerate backup codes"
+ }
+ },
+ "MFA_VERIFICATION": {
+ "TITLE": "Two-Factor Authentication",
+ "DESCRIPTION": "Enter your verification code to continue",
+ "AUTHENTICATOR_APP": "Authenticator App",
+ "BACKUP_CODE": "Backup Code",
+ "ENTER_OTP_CODE": "Enter 6-digit code from your authenticator app",
+ "ENTER_BACKUP_CODE": "Enter one of your backup codes",
+ "BACKUP_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify",
+ "TRY_ANOTHER_METHOD": "Try another verification method",
+ "CANCEL_LOGIN": "Cancel and return to login",
+ "HELP_TEXT": "Having trouble signing in?",
+ "LEARN_MORE": "Learn more about 2FA",
+ "HELP_MODAL": {
+ "TITLE": "Two-Factor Authentication Help",
+ "AUTHENTICATOR_TITLE": "Using an Authenticator App",
+ "AUTHENTICATOR_DESC": "Open your authenticator app (Google Authenticator, Authy, etc.) and enter the 6-digit code shown for your account.",
+ "BACKUP_TITLE": "Using a Backup Code",
+ "BACKUP_DESC": "If you don't have access to your authenticator app, you can use one of the backup codes you saved when setting up 2FA. Each code can only be used once.",
+ "CONTACT_TITLE": "Need More Help?",
+ "CONTACT_DESC_CLOUD": "If you've lost access to both your authenticator app and backup codes, please reach out to Chatwoot support for assistance.",
+ "CONTACT_DESC_SELF_HOSTED": "If you've lost access to both your authenticator app and backup codes, please contact your administrator for assistance."
+ },
+ "VERIFICATION_FAILED": "Verification failed. Please try again."
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/ka/settings.json b/app/javascript/dashboard/i18n/locale/ka/settings.json
index 98c3f559b..52f28443b 100644
--- a/app/javascript/dashboard/i18n/locale/ka/settings.json
+++ b/app/javascript/dashboard/i18n/locale/ka/settings.json
@@ -51,6 +51,13 @@
"LARGER": "Larger",
"EXTRA_LARGE": "Extra Large"
}
+ },
+ "LANGUAGE": {
+ "TITLE": "Preferred Language",
+ "NOTE": "Choose the language you want to use.",
+ "UPDATE_SUCCESS": "Your Language settings have been updated successfully",
+ "UPDATE_ERROR": "There is an error while updating the language settings, please try again",
+ "USE_ACCOUNT_DEFAULT": "Use account default"
}
},
"MESSAGE_SIGNATURE_SECTION": {
@@ -73,6 +80,11 @@
"NOTE": "Updating your password would reset your logins in multiple devices.",
"BTN_TEXT": "Change password"
},
+ "SECURITY_SECTION": {
+ "TITLE": "Security",
+ "NOTE": "Manage additional security features for your account.",
+ "MFA_BUTTON": "Manage Two-Factor Authentication"
+ },
"ACCESS_TOKEN": {
"TITLE": "Access Token",
"NOTE": "This token can be used if you are building an API based integration",
@@ -330,6 +342,7 @@
"REPORTS_LABEL": "Labels",
"REPORTS_INBOX": "Inbox",
"REPORTS_TEAM": "Team",
+ "AGENT_ASSIGNMENT": "Agent Assignment",
"SET_AVAILABILITY_TITLE": "Set yourself as",
"SET_YOUR_AVAILABILITY": "Set your availability",
"SLA": "SLA",
@@ -350,7 +363,8 @@
"INFO_TEXT": "Let the system automatically mark you offline when you aren't using the app or dashboard.",
"INFO_SHORT": "Automatically mark offline when you aren't using the app."
},
- "DOCS": "Read docs"
+ "DOCS": "Read docs",
+ "SECURITY": "Security"
},
"BILLING_SETTINGS": {
"TITLE": "Billing",
@@ -382,6 +396,77 @@
},
"NO_BILLING_USER": "Your billing account is being configured. Please refresh the page and try again."
},
+ "SECURITY_SETTINGS": {
+ "TITLE": "Security",
+ "DESCRIPTION": "Manage your account security settings.",
+ "LINK_TEXT": "Learn more about SAML SSO",
+ "SAML": {
+ "TITLE": "SAML SSO",
+ "NOTE": "Configure SAML single sign-on for your account. Users will authenticate through your identity provider instead of using email/password.",
+ "ACS_URL": {
+ "LABEL": "ACS URL",
+ "TOOLTIP": "Assertion Consumer Service URL - Configure this URL in your IdP as the destination for SAML responses"
+ },
+ "SSO_URL": {
+ "LABEL": "SSO URL",
+ "HELP": "The URL where SAML authentication requests will be sent",
+ "PLACEHOLDER": "https://your-idp.com/saml/sso"
+ },
+ "CERTIFICATE": {
+ "LABEL": "Signing certificate in PEM format",
+ "HELP": "The public certificate from your identity provider used to verify SAML responses",
+ "PLACEHOLDER": "-----BEGIN CERTIFICATE-----\nMIIC..."
+ },
+ "FINGERPRINT": {
+ "LABEL": "Fingerprint",
+ "TOOLTIP": "SHA-1 fingerprint of the certificate - Use this to verify the certificate in your IdP configuration"
+ },
+ "COPY_SUCCESS": "Code copied to clipboard successfully",
+ "SP_ENTITY_ID": {
+ "LABEL": "SP Entity ID",
+ "HELP": "Unique identifier for this application as a service provider (auto-generated).",
+ "TOOLTIP": "Unique identifier for Chatwoot as the Service Provider - Configure this in your IdP settings"
+ },
+ "IDP_ENTITY_ID": {
+ "LABEL": "Identity Provider Entity ID",
+ "HELP": "Unique identifier for your identity provider (usually found in IdP configuration)",
+ "PLACEHOLDER": "https://your-idp.com/saml"
+ },
+ "UPDATE_BUTTON": "Update SAML Settings",
+ "API": {
+ "SUCCESS": "SAML settings updated successfully",
+ "ERROR": "Failed to update SAML settings",
+ "ERROR_LOADING": "Failed to load SAML settings",
+ "DISABLED": "SAML settings disabled successfully"
+ },
+ "VALIDATION": {
+ "REQUIRED_FIELDS": "SSO URL, Identity Provider Entity ID, and Certificate are required fields",
+ "SSO_URL_ERROR": "Please enter a valid SSO URL",
+ "CERTIFICATE_ERROR": "Certificate is required",
+ "IDP_ENTITY_ID_ERROR": "Identity Provider Entity ID is required"
+ },
+ "ENTERPRISE_PAYWALL": {
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade to an Enterprise plan to access SAML single sign-on and other advanced security features.",
+ "ASK_ADMIN": "Please reach out to your administrator for the upgrade."
+ },
+ "PAYWALL": {
+ "TITLE": "Upgrade to enable SAML SSO",
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade your plan to get access to SAML single sign-on and other advanced features.",
+ "UPGRADE_NOW": "Upgrade now",
+ "CANCEL_ANYTIME": "You can change or cancel your plan anytime"
+ },
+ "ATTRIBUTE_MAPPING": {
+ "TITLE": "SAML Attribute Setup",
+ "DESCRIPTION": "The following attribute mappings must be configured in your identity provider"
+ },
+ "INFO_SECTION": {
+ "TITLE": "Service Provider Information",
+ "TOOLTIP": "Copy these values and configure them in your Identity Provider to establish the SAML connection"
+ }
+ }
+ },
"CREATE_ACCOUNT": {
"NO_ACCOUNT_WARNING": "Uh oh! We could not find any Chatwoot accounts. Please create a new account to continue.",
"NEW_ACCOUNT": "New Account",
@@ -418,5 +503,255 @@
"SWITCH_TO_REPLY": "Switch to Reply",
"TOGGLE_SNOOZE_DROPDOWN": "Toggle snooze dropdown"
}
+ },
+ "ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent assignment",
+ "DESCRIPTION": "Define policies to effectively manage workload and route conversations based on the needs of inboxes and agents. Learn more here"
+ },
+ "ASSIGNMENT_POLICY": {
+ "TITLE": "Assignment policy",
+ "DESCRIPTION": "Manage how conversations get assigned in inboxes.",
+ "FEATURES": [
+ "Assign by conversations evenly or by available capacity",
+ "Add fair distribution rules to avoid overloading any agent",
+ "Add inboxes to a policy - one policy per inbox"
+ ]
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "TITLE": "Agent capacity policy",
+ "DESCRIPTION": "Manage workload for agents.",
+ "FEATURES": [
+ "Define maximum conversations per inbox",
+ "Create exceptions based on labels and time",
+ "Add agents to a policy - one policy per agent"
+ ]
+ }
+ },
+ "AGENT_ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Assignment policy",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "ORDER": "Order",
+ "PRIORITY": "Priority",
+ "ACTIVE": "Active",
+ "INACTIVE": "Inactive",
+ "POPOVER": "Added inboxes",
+ "EDIT": "Edit"
+ },
+ "NO_RECORDS_FOUND": "No assignment policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create assignment policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy created successfully",
+ "ERROR_MESSAGE": "Failed to create assignment policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit assignment policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_INBOX_DIALOG": {
+ "TITLE": "Add inbox",
+ "DESCRIPTION": "{inboxName} inbox is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Cancel"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update assignment policy"
+ },
+ "INBOX_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Inbox added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Inbox removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove inbox from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Description:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "STATUS": {
+ "LABEL": "Status:",
+ "PLACEHOLDER": "Select status",
+ "ACTIVE": "Policy is active",
+ "INACTIVE": "Policy is inactive"
+ },
+ "ASSIGNMENT_ORDER": {
+ "LABEL": "Assignment order",
+ "ROUND_ROBIN": {
+ "LABEL": "Round robin",
+ "DESCRIPTION": "Assign conversations evenly among agents."
+ },
+ "BALANCED": {
+ "LABEL": "Balanced",
+ "DESCRIPTION": "Assign conversations based on available capacity."
+ }
+ },
+ "ASSIGNMENT_PRIORITY": {
+ "LABEL": "Assignment priority",
+ "EARLIEST_CREATED": {
+ "LABEL": "Earliest created",
+ "DESCRIPTION": "The conversation that was created first gets assigned first."
+ },
+ "LONGEST_WAITING": {
+ "LABEL": "Longest waiting",
+ "DESCRIPTION": "The conversation waiting the longest gets assigned first."
+ }
+ },
+ "FAIR_DISTRIBUTION": {
+ "LABEL": "Fair distribution policy",
+ "DESCRIPTION": "Set the maximum number of conversations that can be assigned per agent within a time window to avoid overloading any one agent. This required field defaults to 100 conversations per hour.",
+ "INPUT_MAX": "Assign max",
+ "DURATION": "Conversations per agent in every"
+ },
+ "INBOXES": {
+ "LABEL": "Added inboxes",
+ "DESCRIPTION": "Add inboxes for which this policy will be applicable.",
+ "ADD_BUTTON": "Add inbox",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select inboxes to add",
+ "ADD_BUTTON": "Add"
+ },
+ "EMPTY_STATE": "No inboxes added to this policy, add an inbox to get started",
+ "API": {
+ "SUCCESS_MESSAGE": "Inbox successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Assignment policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete assignment policy"
+ }
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent capacity",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "POPOVER": "Added agents",
+ "EDIT": "Edit"
+ },
+ "NO_RECORDS_FOUND": "No agent capacity policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create agent capacity policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy created successfully",
+ "ERROR_MESSAGE": "Failed to create agent capacity policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit agent capacity policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_AGENT_DIALOG": {
+ "TITLE": "Add agent",
+ "DESCRIPTION": "{agentName} is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Cancel"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update agent capacity policy"
+ },
+ "AGENT_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Agent added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Agent removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove agent from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Description:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "INBOX_CAPACITY_LIMIT": {
+ "LABEL": "Inbox capacity limits",
+ "ADD_BUTTON": "Add inbox",
+ "FIELD": {
+ "SELECT_INBOX": "Select inbox",
+ "MAX_CONVERSATIONS": "Max conversations",
+ "SET_LIMIT": "Set limit"
+ },
+ "EMPTY_STATE": "No inbox limit set"
+ },
+ "EXCLUSION_RULES": {
+ "LABEL": "Exclusion rules",
+ "DESCRIPTION": "Conversations that satisfy the following conditions would not count towards agent capacity",
+ "TAGS": {
+ "LABEL": "Exclude conversations tagged with specific labels",
+ "ADD_TAG": "add tag",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select tags to add"
+ },
+ "EMPTY_STATE": "No tags added to this policy."
+ },
+ "DURATION": {
+ "LABEL": "Exclude conversations older than a specified duration",
+ "PLACEHOLDER": "Set time"
+ }
+ },
+ "USERS": {
+ "LABEL": "Assigned agents",
+ "DESCRIPTION": "Add agents for which this policy will be applicable.",
+ "ADD_BUTTON": "Add agent",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select agents to add",
+ "ADD_BUTTON": "Add"
+ },
+ "EMPTY_STATE": "No agents added",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Agent capacity policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete agent capacity policy"
+ }
+ },
+ "DELETE_POLICY": {
+ "TITLE": "Delete policy",
+ "DESCRIPTION": "Are you sure you want to delete this policy? This action cannot be undone.",
+ "CONFIRM_BUTTON_LABEL": "Delete",
+ "CANCEL_BUTTON_LABEL": "Cancel"
+ }
}
}
diff --git a/app/javascript/dashboard/i18n/locale/ka/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/ka/whatsappTemplates.json
index 5f53faaa8..cf28312dc 100644
--- a/app/javascript/dashboard/i18n/locale/ka/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/ka/whatsappTemplates.json
@@ -40,6 +40,7 @@
"BUTTON_LABEL": "Button {index}",
"COUPON_CODE": "Enter coupon code (max 15 chars)",
"MEDIA_URL_LABEL": "Enter {type} URL",
+ "DOCUMENT_NAME_PLACEHOLDER": "Enter document filename (e.g., Invoice_2025.pdf)",
"BUTTON_PARAMETER": "Enter button parameter"
}
}
diff --git a/app/javascript/dashboard/i18n/locale/ko/automation.json b/app/javascript/dashboard/i18n/locale/ko/automation.json
index a5e1b079c..e8e8de6b5 100644
--- a/app/javascript/dashboard/i18n/locale/ko/automation.json
+++ b/app/javascript/dashboard/i18n/locale/ko/automation.json
@@ -177,7 +177,8 @@
"REFERER_LINK": "Referrer Link",
"ASSIGNEE_NAME": "Assignee",
"TEAM_NAME": "Team",
- "PRIORITY": "Priority"
+ "PRIORITY": "Priority",
+ "LABELS": "라벨"
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/ko/contact.json b/app/javascript/dashboard/i18n/locale/ko/contact.json
index 53bac391b..ba15dabee 100644
--- a/app/javascript/dashboard/i18n/locale/ko/contact.json
+++ b/app/javascript/dashboard/i18n/locale/ko/contact.json
@@ -610,6 +610,15 @@
"SEND_MESSAGE": "메시지 보내기"
}
},
+ "TWILIO_OPTIONS": {
+ "LABEL": "Select template",
+ "SEARCH_PLACEHOLDER": "Search templates",
+ "EMPTY_STATE": "No templates found",
+ "TEMPLATE_PARSER": {
+ "BACK": "Go back",
+ "SEND_MESSAGE": "메시지 보내기"
+ }
+ },
"ACTION_BUTTONS": {
"DISCARD": "Discard",
"SEND": "Send ({keyCode})"
diff --git a/app/javascript/dashboard/i18n/locale/ko/contactFilters.json b/app/javascript/dashboard/i18n/locale/ko/contactFilters.json
index e6c452d61..4ba7ee034 100644
--- a/app/javascript/dashboard/i18n/locale/ko/contactFilters.json
+++ b/app/javascript/dashboard/i18n/locale/ko/contactFilters.json
@@ -48,7 +48,8 @@
"CREATED_AT": "에 만들어짐",
"LAST_ACTIVITY": "지난 활동",
"REFERER_LINK": "Referrer link",
- "BLOCKED": "Blocked"
+ "BLOCKED": "Blocked",
+ "LABELS": "라벨"
},
"GROUPS": {
"STANDARD_FILTERS": "기본 필터",
diff --git a/app/javascript/dashboard/i18n/locale/ko/contentTemplates.json b/app/javascript/dashboard/i18n/locale/ko/contentTemplates.json
new file mode 100644
index 000000000..913d8b537
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/ko/contentTemplates.json
@@ -0,0 +1,51 @@
+{
+ "CONTENT_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Twilio Templates",
+ "SUBTITLE": "Select the Twilio template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Search Templates",
+ "NO_TEMPLATES_FOUND": "No templates found for",
+ "NO_CONTENT": "No content",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Category",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No Twilio templates available. Click refresh to sync templates from Twilio.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "언어",
+ "TEMPLATE_BODY": "Template Body",
+ "CATEGORY": "Category"
+ },
+ "TYPES": {
+ "MEDIA": "Media",
+ "QUICK_REPLY": "Quick Reply",
+ "TEXT": "텍스트"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variables",
+ "LANGUAGE": "언어",
+ "CATEGORY": "Category",
+ "VARIABLE_PLACEHOLDER": "Enter {variable} value",
+ "GO_BACK_LABEL": "Go Back",
+ "SEND_MESSAGE_LABEL": "Send Message",
+ "FORM_ERROR_MESSAGE": "Please fill all variables before sending",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "MEDIA_URL_LABEL": "Enter full media URL",
+ "MEDIA_URL_PLACEHOLDER": "https://example.com/image.jpg"
+ },
+ "FORM": {
+ "BACK_BUTTON": "뒤로",
+ "SEND_MESSAGE_BUTTON": "Send Message"
+ }
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/ko/conversation.json b/app/javascript/dashboard/i18n/locale/ko/conversation.json
index aef2e04e1..1278ad79e 100644
--- a/app/javascript/dashboard/i18n/locale/ko/conversation.json
+++ b/app/javascript/dashboard/i18n/locale/ko/conversation.json
@@ -35,6 +35,11 @@
"API_HOURS_WINDOW": "You can only reply to this conversation within {hours} hours",
"NOT_ASSIGNED_TO_YOU": "This conversation is not assigned to you. Would you like to assign this conversation to yourself?",
"ASSIGN_TO_ME": "Assign to me",
+ "BOT_HANDOFF_MESSAGE": "You are responding to a conversation which is currently handled by an assistant or a bot.",
+ "BOT_HANDOFF_ACTION": "Mark open and assign to you",
+ "BOT_HANDOFF_REOPEN_ACTION": "Mark conversation open",
+ "BOT_HANDOFF_SUCCESS": "Conversation has been handed over to you",
+ "BOT_HANDOFF_ERROR": "Failed to take over the conversation. Please try again.",
"TWILIO_WHATSAPP_CAN_REPLY": "You can only reply to this conversation using a template message due to",
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "24시간 메시지 창 제한",
"OLD_INSTAGRAM_INBOX_REPLY_BANNER": "This Instagram account was migrated to the new Instagram channel inbox. All new messages will show up there. You won’t be able to send messages from this conversation anymore.",
@@ -66,6 +71,17 @@
"SHOW_LABELS": "Show labels",
"HIDE_LABELS": "Hide labels"
},
+ "VOICE_CALL": {
+ "INCOMING_CALL": "Incoming call",
+ "OUTGOING_CALL": "Outgoing call",
+ "CALL_IN_PROGRESS": "Call in progress",
+ "NO_ANSWER": "No answer",
+ "MISSED_CALL": "Missed call",
+ "CALL_ENDED": "Call ended",
+ "NOT_ANSWERED_YET": "Not answered yet",
+ "THEY_ANSWERED": "They answered",
+ "YOU_ANSWERED": "You answered"
+ },
"HEADER": {
"RESOLVE_ACTION": "해결함",
"REOPEN_ACTION": "다시 열기",
diff --git a/app/javascript/dashboard/i18n/locale/ko/helpCenter.json b/app/javascript/dashboard/i18n/locale/ko/helpCenter.json
index 6c6057807..2ed7e8812 100644
--- a/app/javascript/dashboard/i18n/locale/ko/helpCenter.json
+++ b/app/javascript/dashboard/i18n/locale/ko/helpCenter.json
@@ -741,7 +741,8 @@
"LIVE_CHAT_WIDGET": {
"LABEL": "Live chat widget",
"PLACEHOLDER": "Select live chat widget",
- "HELP_TEXT": "Select a live chat widget that will appear on your help center"
+ "HELP_TEXT": "Select a live chat widget that will appear on your help center",
+ "NONE_OPTION": "No widget"
},
"BRAND_COLOR": {
"LABEL": "Brand color"
@@ -807,6 +808,58 @@
"ERROR_MESSAGE": "Unable to update portal"
}
}
+ },
+ "PDF_UPLOAD": {
+ "TITLE": "Upload PDF Document",
+ "DESCRIPTION": "Upload a PDF document to automatically generate FAQs using AI",
+ "DRAG_DROP_TEXT": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "ADDITIONAL_CONTEXT_LABEL": "Additional Context (Optional)",
+ "ADDITIONAL_CONTEXT_PLACEHOLDER": "Provide any additional context or instructions for FAQ generation...",
+ "UPLOADING": "업로드 중...",
+ "UPLOAD": "Upload & Process",
+ "CANCEL": "취소",
+ "ERROR_INVALID_TYPE": "Please select a valid PDF file",
+ "ERROR_FILE_TOO_LARGE": "File size must be less than 512MB",
+ "ERROR_UPLOAD_FAILED": "Failed to upload PDF. Please try again."
+ },
+ "PDF_DOCUMENTS": {
+ "TITLE": "PDF Documents",
+ "DESCRIPTION": "Manage uploaded PDF documents and generate FAQs from them",
+ "UPLOAD_PDF": "Upload PDF",
+ "UPLOAD_FIRST_PDF": "Upload your first PDF",
+ "UPLOADED_BY": "Uploaded by",
+ "GENERATE_FAQS": "Generate FAQs",
+ "GENERATING": "Generating...",
+ "CONFIRM_DELETE": "Are you sure you want to delete {filename}?",
+ "EMPTY_STATE": {
+ "TITLE": "No PDF documents yet",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQs using AI"
+ },
+ "STATUS": {
+ "UPLOADED": "Ready",
+ "PROCESSING": "Processing",
+ "PROCESSED": "Completed",
+ "FAILED": "Failed"
+ }
+ },
+ "CONTENT_GENERATION": {
+ "TITLE": "Content Generation",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQ content using AI",
+ "UPLOAD_TITLE": "Upload PDF Document",
+ "DRAG_DROP": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "UPLOADING": "Processing document...",
+ "UPLOAD_SUCCESS": "Document processed successfully!",
+ "UPLOAD_ERROR": "Failed to upload document. Please try again.",
+ "INVALID_FILE_TYPE": "Please select a valid PDF file",
+ "FILE_TOO_LARGE": "File size must be less than 512MB",
+ "GENERATED_CONTENT": "Generated FAQ Content",
+ "PUBLISH_SELECTED": "Publish Selected",
+ "PUBLISHING": "Publishing...",
+ "FROM_DOCUMENT": "From document",
+ "NO_CONTENT": "No generated content available. Upload a PDF document to get started.",
+ "LOADING": "Loading generated content..."
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/ko/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/ko/inboxMgmt.json
index 4a1d5d18d..d2816bb13 100644
--- a/app/javascript/dashboard/i18n/locale/ko/inboxMgmt.json
+++ b/app/javascript/dashboard/i18n/locale/ko/inboxMgmt.json
@@ -272,8 +272,8 @@
},
"SUBMIT_BUTTON": "Create WhatsApp Channel",
"EMBEDDED_SIGNUP": {
- "TITLE": "Quick Setup with Meta",
- "DESC": "You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
+ "TITLE": "Quick setup with Meta",
+ "DESC": "Use the WhatsApp Embedded Signup flow to quickly connect new numbers. You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
"BENEFITS": {
"TITLE": "Benefits of Embedded Signup:",
"EASY_SETUP": "No manual configuration required",
@@ -281,9 +281,8 @@
"AUTO_CONFIG": "Automatic webhook and phone number configuration"
},
"LEARN_MORE": {
- "TEXT": "To learn more about integrated signup, pricing, and limitations, visit",
- "LINK_TEXT": "this link.",
- "LINK_URL": "https://developers.facebook.com/docs/whatsapp/embedded-signup/custom-flows/onboarding-business-app-users#limitations"
+ "TEXT": "To learn more about integrated signup, pricing, and limitations, visit {link}.",
+ "LINK_TEXT": "this link"
},
"SUBMIT_BUTTON": "Connect with WhatsApp Business",
"AUTH_PROCESSING": "Authenticating with Meta",
@@ -296,7 +295,9 @@
"INVALID_BUSINESS_DATA": "Invalid business data received from Facebook. Please try again.",
"SIGNUP_ERROR": "Signup error occurred",
"AUTH_NOT_COMPLETED": "Authentication not completed. Please restart the process.",
- "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured"
+ "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured",
+ "MANUAL_FALLBACK": "If your number is already connected to the WhatsApp Business Platform (API), or if you’re a tech provider onboarding your own number, please use the {link} flow",
+ "MANUAL_LINK_TEXT": "manual setup flow"
},
"API": {
"ERROR_MESSAGE": "We were not able to save the WhatsApp channel"
@@ -424,7 +425,51 @@
},
"AUTH": {
"TITLE": "Choose a channel",
- "DESC": "Chatwoot supports live-chat widgets, Facebook Messenger, Twitter profiles, WhatsApp, Emails, etc., as channels. If you want to build a custom channel, you can create it using the API channel. To get started, choose one of the channels below."
+ "DESC": "Chatwoot supports live-chat widgets, Facebook Messenger, Twitter profiles, WhatsApp, Emails, etc., as channels. If you want to build a custom channel, you can create it using the API channel. To get started, choose one of the channels below.",
+ "TITLE_NEXT": "Complete the setup",
+ "TITLE_FINISH": "Voilà!",
+ "CHANNEL": {
+ "WEBSITE": {
+ "TITLE": "홈페이지",
+ "DESCRIPTION": "Create a live-chat widget"
+ },
+ "FACEBOOK": {
+ "TITLE": "페이스북",
+ "DESCRIPTION": "Connect your Facebook page"
+ },
+ "WHATSAPP": {
+ "TITLE": "WhatsApp",
+ "DESCRIPTION": "Support your customers on WhatsApp"
+ },
+ "EMAIL": {
+ "TITLE": "이메일",
+ "DESCRIPTION": "Connect with Gmail, Outlook, or other providers"
+ },
+ "SMS": {
+ "TITLE": "SMS",
+ "DESCRIPTION": "Integrate SMS channel with Twilio or bandwidth"
+ },
+ "API": {
+ "TITLE": "API",
+ "DESCRIPTION": "Make a custom channel using our API"
+ },
+ "TELEGRAM": {
+ "TITLE": "Telegram",
+ "DESCRIPTION": "Configure Telegram channel using Bot token"
+ },
+ "LINE": {
+ "TITLE": "Line",
+ "DESCRIPTION": "Integrate your Line channel"
+ },
+ "INSTAGRAM": {
+ "TITLE": "Instagram",
+ "DESCRIPTION": "Connect your instagram account"
+ },
+ "VOICE": {
+ "TITLE": "Voice",
+ "DESCRIPTION": "Integrate with Twilio Voice"
+ }
+ }
},
"AGENTS": {
"TITLE": "에이전트",
@@ -478,7 +523,10 @@
"MESSAGE": "이제 새로운 채널을 통해 고객과 대화할 수 있습니다. 행복한 지원",
"BUTTON_TEXT": "나를 그곳으로 데려주세요.",
"MORE_SETTINGS": "More settings",
- "WEBSITE_SUCCESS": "웹사이트 채널 만들기를 완료하셨습니다. 아래 표시된 코드를 복사하여 웹사이트에 붙여 넣으십시오. 다음에 고객이 라이브 채팅을 사용할 때 대화는 받은 편지함에 자동으로 표시됩니다."
+ "WEBSITE_SUCCESS": "웹사이트 채널 만들기를 완료하셨습니다. 아래 표시된 코드를 복사하여 웹사이트에 붙여 넣으십시오. 다음에 고객이 라이브 채팅을 사용할 때 대화는 받은 편지함에 자동으로 표시됩니다.",
+ "WHATSAPP_QR_INSTRUCTION": "Scan the QR code above to quickly test your WhatsApp inbox",
+ "MESSENGER_QR_INSTRUCTION": "Scan the QR code above to quickly test your Facebook Messenger inbox",
+ "TELEGRAM_QR_INSTRUCTION": "Scan the QR code above to quickly test your Telegram inbox"
},
"REAUTH": "재승인",
"VIEW": "보기",
@@ -868,9 +916,18 @@
"SCRIPT_SETTINGS": "\n window.chatwootSettings = {options};"
},
"EMAIL_PROVIDERS": {
- "MICROSOFT": "Microsoft",
- "GOOGLE": "Google",
- "OTHER_PROVIDERS": "Other Providers"
+ "MICROSOFT": {
+ "TITLE": "Microsoft",
+ "DESCRIPTION": "Connect with Microsoft"
+ },
+ "GOOGLE": {
+ "TITLE": "Google",
+ "DESCRIPTION": "Connect with Google"
+ },
+ "OTHER_PROVIDERS": {
+ "TITLE": "Other Providers",
+ "DESCRIPTION": "Connect with Other Providers"
+ }
},
"CHANNELS": {
"MESSENGER": "Messenger",
diff --git a/app/javascript/dashboard/i18n/locale/ko/integrations.json b/app/javascript/dashboard/i18n/locale/ko/integrations.json
index e5935505e..929cecfc0 100644
--- a/app/javascript/dashboard/i18n/locale/ko/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/ko/integrations.json
@@ -704,11 +704,28 @@
"ERROR_MESSAGE": "There was an error creating the document, please try again."
},
"FORM": {
+ "TYPE": {
+ "LABEL": "Document Type",
+ "URL": "URL",
+ "PDF": "PDF File"
+ },
"URL": {
"LABEL": "URL",
"PLACEHOLDER": "Enter the URL of the document",
"ERROR": "Please provide a valid URL for the document"
},
+ "PDF_FILE": {
+ "LABEL": "PDF File",
+ "CHOOSE_FILE": "Choose PDF file",
+ "ERROR": "Please select a PDF file",
+ "HELP_TEXT": "Maximum file size: 10MB",
+ "INVALID_TYPE": "Please select a valid PDF file",
+ "TOO_LARGE": "File size exceeds 10MB limit"
+ },
+ "NAME": {
+ "LABEL": "Document Name (Optional)",
+ "PLACEHOLDER": "Enter a name for the document"
+ },
"ASSISTANT": {
"LABEL": "Assistant",
"PLACEHOLDER": "Select the assistant",
@@ -744,6 +761,7 @@
"SELECTED": "{count} selected",
"SELECT_ALL": "Select all ({count})",
"UNSELECT_ALL": "Unselect all ({count})",
+ "SEARCH_PLACEHOLDER": "Search FAQs...",
"BULK_APPROVE_BUTTON": "Approve",
"BULK_DELETE_BUTTON": "삭제",
"BULK_APPROVE": {
diff --git a/app/javascript/dashboard/i18n/locale/ko/mfa.json b/app/javascript/dashboard/i18n/locale/ko/mfa.json
new file mode 100644
index 000000000..c9ef842a2
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/ko/mfa.json
@@ -0,0 +1,106 @@
+{
+ "MFA_SETTINGS": {
+ "TITLE": "Two-Factor Authentication",
+ "SUBTITLE": "Secure your account with TOTP-based authentication",
+ "DESCRIPTION": "Add an extra layer of security to your account using a time-based one-time password (TOTP)",
+ "STATUS_TITLE": "Authentication Status",
+ "STATUS_DESCRIPTION": "Manage your two-factor authentication settings and backup recovery codes",
+ "ENABLED": "사용함",
+ "DISABLED": "사용 안 함",
+ "STATUS_ENABLED": "Two-factor authentication is active",
+ "STATUS_ENABLED_DESC": "Your account is protected with an additional layer of security",
+ "ENABLE_BUTTON": "Enable Two-Factor Authentication",
+ "ENHANCE_SECURITY": "Enhance Your Account Security",
+ "ENHANCE_SECURITY_DESC": "Two-factor authentication adds an extra layer of security by requiring a verification code from your authenticator app in addition to your password.",
+ "SETUP": {
+ "STEP_NUMBER_1": "1",
+ "STEP_NUMBER_2": "2",
+ "STEP1_TITLE": "Scan QR Code with Your Authenticator App",
+ "STEP1_DESCRIPTION": "Use Google Authenticator, Authy, or any TOTP-compatible app",
+ "LOADING_QR": "Loading...",
+ "MANUAL_ENTRY": "Can't scan? Enter code manually",
+ "SECRET_KEY": "Secret Key",
+ "COPY": "복사",
+ "ENTER_CODE": "Enter the 6-digit code from your authenticator app",
+ "ENTER_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify & Continue",
+ "CANCEL": "취소",
+ "ERROR_STARTING": "MFA not enabled. Please contact administrator.",
+ "INVALID_CODE": "Invalid verification code",
+ "SECRET_COPIED": "Secret key copied to clipboard",
+ "SUCCESS": "Two-factor authentication has been enabled successfully"
+ },
+ "BACKUP": {
+ "TITLE": "Save Your Backup Codes",
+ "DESCRIPTION": "Keep these codes safe. Each can be used once if you lose access to your authenticator",
+ "IMPORTANT": "Important:",
+ "IMPORTANT_NOTE": " Save these codes in a secure location. You won't be able to see them again.",
+ "DOWNLOAD": "다운로드",
+ "COPY_ALL": "Copy All",
+ "CONFIRM": "I have saved my backup codes in a secure location and understand that I won't be able to see them again",
+ "COMPLETE_SETUP": "Complete Setup",
+ "CODES_COPIED": "Backup codes copied to clipboard"
+ },
+ "MANAGEMENT": {
+ "BACKUP_CODES": "Backup Codes",
+ "BACKUP_CODES_DESC": "Generate new codes if you've lost or used your existing ones",
+ "REGENERATE": "Regenerate Backup Codes",
+ "DISABLE_MFA": "Disable 2FA",
+ "DISABLE_MFA_DESC": "Remove two-factor authentication from your account",
+ "DISABLE_BUTTON": "Disable Two-Factor Authentication"
+ },
+ "DISABLE": {
+ "TITLE": "Disable Two-Factor Authentication",
+ "DESCRIPTION": "You'll need to enter your password and a verification code to disable two-factor authentication.",
+ "PASSWORD": "비밀번호",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Disable 2FA",
+ "CANCEL": "취소",
+ "SUCCESS": "Two-factor authentication has been disabled",
+ "ERROR": "Failed to disable MFA. Please check your credentials."
+ },
+ "REGENERATE": {
+ "TITLE": "Regenerate Backup Codes",
+ "DESCRIPTION": "This will invalidate your existing backup codes and generate new ones. Enter your verification code to continue.",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Generate New Codes",
+ "CANCEL": "취소",
+ "NEW_CODES_TITLE": "New Backup Codes Generated",
+ "NEW_CODES_DESC": "Your old backup codes have been invalidated. Save these new codes in a secure location.",
+ "CODES_IMPORTANT": "Important:",
+ "CODES_IMPORTANT_NOTE": " Each code can only be used once. Save them before closing this window.",
+ "DOWNLOAD_CODES": "Download Codes",
+ "COPY_ALL_CODES": "Copy All Codes",
+ "CODES_SAVED": "I've Saved My Codes",
+ "SUCCESS": "New backup codes have been generated",
+ "ERROR": "Failed to regenerate backup codes"
+ }
+ },
+ "MFA_VERIFICATION": {
+ "TITLE": "Two-Factor Authentication",
+ "DESCRIPTION": "Enter your verification code to continue",
+ "AUTHENTICATOR_APP": "Authenticator App",
+ "BACKUP_CODE": "Backup Code",
+ "ENTER_OTP_CODE": "Enter 6-digit code from your authenticator app",
+ "ENTER_BACKUP_CODE": "Enter one of your backup codes",
+ "BACKUP_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify",
+ "TRY_ANOTHER_METHOD": "Try another verification method",
+ "CANCEL_LOGIN": "Cancel and return to login",
+ "HELP_TEXT": "Having trouble signing in?",
+ "LEARN_MORE": "Learn more about 2FA",
+ "HELP_MODAL": {
+ "TITLE": "Two-Factor Authentication Help",
+ "AUTHENTICATOR_TITLE": "Using an Authenticator App",
+ "AUTHENTICATOR_DESC": "Open your authenticator app (Google Authenticator, Authy, etc.) and enter the 6-digit code shown for your account.",
+ "BACKUP_TITLE": "Using a Backup Code",
+ "BACKUP_DESC": "If you don't have access to your authenticator app, you can use one of the backup codes you saved when setting up 2FA. Each code can only be used once.",
+ "CONTACT_TITLE": "Need More Help?",
+ "CONTACT_DESC_CLOUD": "If you've lost access to both your authenticator app and backup codes, please reach out to Chatwoot support for assistance.",
+ "CONTACT_DESC_SELF_HOSTED": "If you've lost access to both your authenticator app and backup codes, please contact your administrator for assistance."
+ },
+ "VERIFICATION_FAILED": "Verification failed. Please try again."
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/ko/settings.json b/app/javascript/dashboard/i18n/locale/ko/settings.json
index 2dcbcd532..c24c08f1b 100644
--- a/app/javascript/dashboard/i18n/locale/ko/settings.json
+++ b/app/javascript/dashboard/i18n/locale/ko/settings.json
@@ -51,6 +51,13 @@
"LARGER": "Larger",
"EXTRA_LARGE": "Extra Large"
}
+ },
+ "LANGUAGE": {
+ "TITLE": "Preferred Language",
+ "NOTE": "Choose the language you want to use.",
+ "UPDATE_SUCCESS": "Your Language settings have been updated successfully",
+ "UPDATE_ERROR": "There is an error while updating the language settings, please try again",
+ "USE_ACCOUNT_DEFAULT": "Use account default"
}
},
"MESSAGE_SIGNATURE_SECTION": {
@@ -73,6 +80,11 @@
"NOTE": "암호를 업데이트하면 여러 장치의 로그인이 재설정됩니다.",
"BTN_TEXT": "Change password"
},
+ "SECURITY_SECTION": {
+ "TITLE": "Security",
+ "NOTE": "Manage additional security features for your account.",
+ "MFA_BUTTON": "Manage Two-Factor Authentication"
+ },
"ACCESS_TOKEN": {
"TITLE": "엑세스 토큰",
"NOTE": "API 기반 통합을 구축하는 경우 이 토큰을 사용할 수 있음",
@@ -330,6 +342,7 @@
"REPORTS_LABEL": "라벨",
"REPORTS_INBOX": "받은 메시지함",
"REPORTS_TEAM": "Team",
+ "AGENT_ASSIGNMENT": "Agent Assignment",
"SET_AVAILABILITY_TITLE": "Set yourself as",
"SET_YOUR_AVAILABILITY": "영업시간 설정",
"SLA": "SLA",
@@ -350,7 +363,8 @@
"INFO_TEXT": "Let the system automatically mark you offline when you aren't using the app or dashboard.",
"INFO_SHORT": "Automatically mark offline when you aren't using the app."
},
- "DOCS": "Read docs"
+ "DOCS": "Read docs",
+ "SECURITY": "Security"
},
"BILLING_SETTINGS": {
"TITLE": "Billing",
@@ -382,6 +396,77 @@
},
"NO_BILLING_USER": "Your billing account is being configured. Please refresh the page and try again."
},
+ "SECURITY_SETTINGS": {
+ "TITLE": "Security",
+ "DESCRIPTION": "Manage your account security settings.",
+ "LINK_TEXT": "Learn more about SAML SSO",
+ "SAML": {
+ "TITLE": "SAML SSO",
+ "NOTE": "Configure SAML single sign-on for your account. Users will authenticate through your identity provider instead of using email/password.",
+ "ACS_URL": {
+ "LABEL": "ACS URL",
+ "TOOLTIP": "Assertion Consumer Service URL - Configure this URL in your IdP as the destination for SAML responses"
+ },
+ "SSO_URL": {
+ "LABEL": "SSO URL",
+ "HELP": "The URL where SAML authentication requests will be sent",
+ "PLACEHOLDER": "https://your-idp.com/saml/sso"
+ },
+ "CERTIFICATE": {
+ "LABEL": "Signing certificate in PEM format",
+ "HELP": "The public certificate from your identity provider used to verify SAML responses",
+ "PLACEHOLDER": "-----BEGIN CERTIFICATE-----\nMIIC..."
+ },
+ "FINGERPRINT": {
+ "LABEL": "Fingerprint",
+ "TOOLTIP": "SHA-1 fingerprint of the certificate - Use this to verify the certificate in your IdP configuration"
+ },
+ "COPY_SUCCESS": "코드가 클립보드에 복사됨",
+ "SP_ENTITY_ID": {
+ "LABEL": "SP Entity ID",
+ "HELP": "Unique identifier for this application as a service provider (auto-generated).",
+ "TOOLTIP": "Unique identifier for Chatwoot as the Service Provider - Configure this in your IdP settings"
+ },
+ "IDP_ENTITY_ID": {
+ "LABEL": "Identity Provider Entity ID",
+ "HELP": "Unique identifier for your identity provider (usually found in IdP configuration)",
+ "PLACEHOLDER": "https://your-idp.com/saml"
+ },
+ "UPDATE_BUTTON": "Update SAML Settings",
+ "API": {
+ "SUCCESS": "SAML settings updated successfully",
+ "ERROR": "Failed to update SAML settings",
+ "ERROR_LOADING": "Failed to load SAML settings",
+ "DISABLED": "SAML settings disabled successfully"
+ },
+ "VALIDATION": {
+ "REQUIRED_FIELDS": "SSO URL, Identity Provider Entity ID, and Certificate are required fields",
+ "SSO_URL_ERROR": "Please enter a valid SSO URL",
+ "CERTIFICATE_ERROR": "Certificate is required",
+ "IDP_ENTITY_ID_ERROR": "Identity Provider Entity ID is required"
+ },
+ "ENTERPRISE_PAYWALL": {
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade to an Enterprise plan to access SAML single sign-on and other advanced security features.",
+ "ASK_ADMIN": "Please reach out to your administrator for the upgrade."
+ },
+ "PAYWALL": {
+ "TITLE": "Upgrade to enable SAML SSO",
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade your plan to get access to SAML single sign-on and other advanced features.",
+ "UPGRADE_NOW": "Upgrade now",
+ "CANCEL_ANYTIME": "You can change or cancel your plan anytime"
+ },
+ "ATTRIBUTE_MAPPING": {
+ "TITLE": "SAML Attribute Setup",
+ "DESCRIPTION": "The following attribute mappings must be configured in your identity provider"
+ },
+ "INFO_SECTION": {
+ "TITLE": "Service Provider Information",
+ "TOOLTIP": "Copy these values and configure them in your Identity Provider to establish the SAML connection"
+ }
+ }
+ },
"CREATE_ACCOUNT": {
"NO_ACCOUNT_WARNING": "Uh oh! We could not find any Chatwoot accounts. Please create a new account to continue.",
"NEW_ACCOUNT": "새 계정",
@@ -418,5 +503,255 @@
"SWITCH_TO_REPLY": "Switch to Reply",
"TOGGLE_SNOOZE_DROPDOWN": "Toggle snooze dropdown"
}
+ },
+ "ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent assignment",
+ "DESCRIPTION": "Define policies to effectively manage workload and route conversations based on the needs of inboxes and agents. Learn more here"
+ },
+ "ASSIGNMENT_POLICY": {
+ "TITLE": "Assignment policy",
+ "DESCRIPTION": "Manage how conversations get assigned in inboxes.",
+ "FEATURES": [
+ "Assign by conversations evenly or by available capacity",
+ "Add fair distribution rules to avoid overloading any agent",
+ "Add inboxes to a policy - one policy per inbox"
+ ]
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "TITLE": "Agent capacity policy",
+ "DESCRIPTION": "Manage workload for agents.",
+ "FEATURES": [
+ "Define maximum conversations per inbox",
+ "Create exceptions based on labels and time",
+ "Add agents to a policy - one policy per agent"
+ ]
+ }
+ },
+ "AGENT_ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Assignment policy",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "ORDER": "Order",
+ "PRIORITY": "Priority",
+ "ACTIVE": "Active",
+ "INACTIVE": "Inactive",
+ "POPOVER": "Added inboxes",
+ "EDIT": "수정"
+ },
+ "NO_RECORDS_FOUND": "No assignment policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create assignment policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy created successfully",
+ "ERROR_MESSAGE": "Failed to create assignment policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit assignment policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_INBOX_DIALOG": {
+ "TITLE": "Add inbox",
+ "DESCRIPTION": "{inboxName} inbox is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "취소"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update assignment policy"
+ },
+ "INBOX_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Inbox added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Inbox removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove inbox from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "내용:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "STATUS": {
+ "LABEL": "상태:",
+ "PLACEHOLDER": "Select status",
+ "ACTIVE": "Policy is active",
+ "INACTIVE": "Policy is inactive"
+ },
+ "ASSIGNMENT_ORDER": {
+ "LABEL": "Assignment order",
+ "ROUND_ROBIN": {
+ "LABEL": "Round robin",
+ "DESCRIPTION": "Assign conversations evenly among agents."
+ },
+ "BALANCED": {
+ "LABEL": "Balanced",
+ "DESCRIPTION": "Assign conversations based on available capacity."
+ }
+ },
+ "ASSIGNMENT_PRIORITY": {
+ "LABEL": "Assignment priority",
+ "EARLIEST_CREATED": {
+ "LABEL": "Earliest created",
+ "DESCRIPTION": "The conversation that was created first gets assigned first."
+ },
+ "LONGEST_WAITING": {
+ "LABEL": "Longest waiting",
+ "DESCRIPTION": "The conversation waiting the longest gets assigned first."
+ }
+ },
+ "FAIR_DISTRIBUTION": {
+ "LABEL": "Fair distribution policy",
+ "DESCRIPTION": "Set the maximum number of conversations that can be assigned per agent within a time window to avoid overloading any one agent. This required field defaults to 100 conversations per hour.",
+ "INPUT_MAX": "Assign max",
+ "DURATION": "Conversations per agent in every"
+ },
+ "INBOXES": {
+ "LABEL": "Added inboxes",
+ "DESCRIPTION": "Add inboxes for which this policy will be applicable.",
+ "ADD_BUTTON": "Add inbox",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select inboxes to add",
+ "ADD_BUTTON": "추가하기"
+ },
+ "EMPTY_STATE": "No inboxes added to this policy, add an inbox to get started",
+ "API": {
+ "SUCCESS_MESSAGE": "Inbox successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Assignment policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete assignment policy"
+ }
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent capacity",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "POPOVER": "Added agents",
+ "EDIT": "수정"
+ },
+ "NO_RECORDS_FOUND": "No agent capacity policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create agent capacity policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy created successfully",
+ "ERROR_MESSAGE": "Failed to create agent capacity policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit agent capacity policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_AGENT_DIALOG": {
+ "TITLE": "Add agent",
+ "DESCRIPTION": "{agentName} is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "취소"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update agent capacity policy"
+ },
+ "AGENT_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Agent added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Agent removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove agent from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "내용:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "INBOX_CAPACITY_LIMIT": {
+ "LABEL": "Inbox capacity limits",
+ "ADD_BUTTON": "Add inbox",
+ "FIELD": {
+ "SELECT_INBOX": "Select inbox",
+ "MAX_CONVERSATIONS": "Max conversations",
+ "SET_LIMIT": "Set limit"
+ },
+ "EMPTY_STATE": "No inbox limit set"
+ },
+ "EXCLUSION_RULES": {
+ "LABEL": "Exclusion rules",
+ "DESCRIPTION": "Conversations that satisfy the following conditions would not count towards agent capacity",
+ "TAGS": {
+ "LABEL": "Exclude conversations tagged with specific labels",
+ "ADD_TAG": "add tag",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select tags to add"
+ },
+ "EMPTY_STATE": "No tags added to this policy."
+ },
+ "DURATION": {
+ "LABEL": "Exclude conversations older than a specified duration",
+ "PLACEHOLDER": "Set time"
+ }
+ },
+ "USERS": {
+ "LABEL": "Assigned agents",
+ "DESCRIPTION": "Add agents for which this policy will be applicable.",
+ "ADD_BUTTON": "Add agent",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select agents to add",
+ "ADD_BUTTON": "추가하기"
+ },
+ "EMPTY_STATE": "No agents added",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Agent capacity policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete agent capacity policy"
+ }
+ },
+ "DELETE_POLICY": {
+ "TITLE": "Delete policy",
+ "DESCRIPTION": "Are you sure you want to delete this policy? This action cannot be undone.",
+ "CONFIRM_BUTTON_LABEL": "삭제",
+ "CANCEL_BUTTON_LABEL": "취소"
+ }
}
}
diff --git a/app/javascript/dashboard/i18n/locale/ko/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/ko/whatsappTemplates.json
index 6f6c098f5..d42a0f1aa 100644
--- a/app/javascript/dashboard/i18n/locale/ko/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/ko/whatsappTemplates.json
@@ -40,6 +40,7 @@
"BUTTON_LABEL": "Button {index}",
"COUPON_CODE": "Enter coupon code (max 15 chars)",
"MEDIA_URL_LABEL": "Enter {type} URL",
+ "DOCUMENT_NAME_PLACEHOLDER": "Enter document filename (e.g., Invoice_2025.pdf)",
"BUTTON_PARAMETER": "Enter button parameter"
}
}
diff --git a/app/javascript/dashboard/i18n/locale/lt/automation.json b/app/javascript/dashboard/i18n/locale/lt/automation.json
index 3485c2355..2f6fbbfbc 100644
--- a/app/javascript/dashboard/i18n/locale/lt/automation.json
+++ b/app/javascript/dashboard/i18n/locale/lt/automation.json
@@ -177,7 +177,8 @@
"REFERER_LINK": "Referrer Link",
"ASSIGNEE_NAME": "Assignee",
"TEAM_NAME": "Komanda",
- "PRIORITY": "Prioritetas"
+ "PRIORITY": "Prioritetas",
+ "LABELS": "Etiketės"
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/lt/contact.json b/app/javascript/dashboard/i18n/locale/lt/contact.json
index ca4e1eeef..f449e741f 100644
--- a/app/javascript/dashboard/i18n/locale/lt/contact.json
+++ b/app/javascript/dashboard/i18n/locale/lt/contact.json
@@ -610,6 +610,15 @@
"SEND_MESSAGE": "Išsiųsti pranešimą"
}
},
+ "TWILIO_OPTIONS": {
+ "LABEL": "Select template",
+ "SEARCH_PLACEHOLDER": "Search templates",
+ "EMPTY_STATE": "No templates found",
+ "TEMPLATE_PARSER": {
+ "BACK": "Grįžti",
+ "SEND_MESSAGE": "Išsiųsti pranešimą"
+ }
+ },
"ACTION_BUTTONS": {
"DISCARD": "Discard",
"SEND": "Send ({keyCode})"
diff --git a/app/javascript/dashboard/i18n/locale/lt/contactFilters.json b/app/javascript/dashboard/i18n/locale/lt/contactFilters.json
index ddd031c73..75f48b586 100644
--- a/app/javascript/dashboard/i18n/locale/lt/contactFilters.json
+++ b/app/javascript/dashboard/i18n/locale/lt/contactFilters.json
@@ -48,7 +48,8 @@
"CREATED_AT": "Sukūrimo data",
"LAST_ACTIVITY": "Paskutiniai veiksmai",
"REFERER_LINK": "Rekomendacijos nuoroda",
- "BLOCKED": "Blocked"
+ "BLOCKED": "Blocked",
+ "LABELS": "Etiketės"
},
"GROUPS": {
"STANDARD_FILTERS": "Standartinis Filtras",
diff --git a/app/javascript/dashboard/i18n/locale/lt/contentTemplates.json b/app/javascript/dashboard/i18n/locale/lt/contentTemplates.json
new file mode 100644
index 000000000..159e33d62
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/lt/contentTemplates.json
@@ -0,0 +1,51 @@
+{
+ "CONTENT_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Twilio Templates",
+ "SUBTITLE": "Select the Twilio template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Ieškoti šablonų",
+ "NO_TEMPLATES_FOUND": "Šablonų nerasta",
+ "NO_CONTENT": "No content",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Kategorija",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No Twilio templates available. Click refresh to sync templates from Twilio.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Kalba",
+ "TEMPLATE_BODY": "Šablono tekstas",
+ "CATEGORY": "Kategorija"
+ },
+ "TYPES": {
+ "MEDIA": "Media",
+ "QUICK_REPLY": "Quick Reply",
+ "TEXT": "Tekstas"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Kintamieji",
+ "LANGUAGE": "Kalba",
+ "CATEGORY": "Kategorija",
+ "VARIABLE_PLACEHOLDER": "Įveskite {variable} reikšmę",
+ "GO_BACK_LABEL": "Grįžti",
+ "SEND_MESSAGE_LABEL": "Išsiųsti pranešimą",
+ "FORM_ERROR_MESSAGE": "Prieš siųsdami užpildykite visus kintamuosius",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "MEDIA_URL_LABEL": "Enter full media URL",
+ "MEDIA_URL_PLACEHOLDER": "https://example.com/image.jpg"
+ },
+ "FORM": {
+ "BACK_BUTTON": "Atgal",
+ "SEND_MESSAGE_BUTTON": "Išsiųsti pranešimą"
+ }
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/lt/conversation.json b/app/javascript/dashboard/i18n/locale/lt/conversation.json
index 79e26b3ad..eb8a0d144 100644
--- a/app/javascript/dashboard/i18n/locale/lt/conversation.json
+++ b/app/javascript/dashboard/i18n/locale/lt/conversation.json
@@ -35,6 +35,11 @@
"API_HOURS_WINDOW": "You can only reply to this conversation within {hours} hours",
"NOT_ASSIGNED_TO_YOU": "Šis pokalbis jums nepriskirtas. Ar norėtumėte priskirti šį pokalbį sau?",
"ASSIGN_TO_ME": "Priskirti man",
+ "BOT_HANDOFF_MESSAGE": "You are responding to a conversation which is currently handled by an assistant or a bot.",
+ "BOT_HANDOFF_ACTION": "Mark open and assign to you",
+ "BOT_HANDOFF_REOPEN_ACTION": "Mark conversation open",
+ "BOT_HANDOFF_SUCCESS": "Conversation has been handed over to you",
+ "BOT_HANDOFF_ERROR": "Failed to take over the conversation. Please try again.",
"TWILIO_WHATSAPP_CAN_REPLY": "Į šį pokalbį galite atsakyti tik naudodami šablono pranešimą, nes",
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "Pranešimų apribojimas 24 valandoms",
"OLD_INSTAGRAM_INBOX_REPLY_BANNER": "This Instagram account was migrated to the new Instagram channel inbox. All new messages will show up there. You won’t be able to send messages from this conversation anymore.",
@@ -66,6 +71,17 @@
"SHOW_LABELS": "Rodyti etiketes",
"HIDE_LABELS": "Slėpti etiketes"
},
+ "VOICE_CALL": {
+ "INCOMING_CALL": "Incoming call",
+ "OUTGOING_CALL": "Outgoing call",
+ "CALL_IN_PROGRESS": "Call in progress",
+ "NO_ANSWER": "No answer",
+ "MISSED_CALL": "Missed call",
+ "CALL_ENDED": "Call ended",
+ "NOT_ANSWERED_YET": "Not answered yet",
+ "THEY_ANSWERED": "They answered",
+ "YOU_ANSWERED": "You answered"
+ },
"HEADER": {
"RESOLVE_ACTION": "Išspręsti",
"REOPEN_ACTION": "Atidarykite iš naujo",
diff --git a/app/javascript/dashboard/i18n/locale/lt/helpCenter.json b/app/javascript/dashboard/i18n/locale/lt/helpCenter.json
index a90cecbdd..25b96ba2f 100644
--- a/app/javascript/dashboard/i18n/locale/lt/helpCenter.json
+++ b/app/javascript/dashboard/i18n/locale/lt/helpCenter.json
@@ -741,7 +741,8 @@
"LIVE_CHAT_WIDGET": {
"LABEL": "Live chat widget",
"PLACEHOLDER": "Select live chat widget",
- "HELP_TEXT": "Select a live chat widget that will appear on your help center"
+ "HELP_TEXT": "Select a live chat widget that will appear on your help center",
+ "NONE_OPTION": "No widget"
},
"BRAND_COLOR": {
"LABEL": "Brand color"
@@ -807,6 +808,58 @@
"ERROR_MESSAGE": "Unable to update portal"
}
}
+ },
+ "PDF_UPLOAD": {
+ "TITLE": "Upload PDF Document",
+ "DESCRIPTION": "Upload a PDF document to automatically generate FAQs using AI",
+ "DRAG_DROP_TEXT": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "ADDITIONAL_CONTEXT_LABEL": "Additional Context (Optional)",
+ "ADDITIONAL_CONTEXT_PLACEHOLDER": "Provide any additional context or instructions for FAQ generation...",
+ "UPLOADING": "Įkeliama...",
+ "UPLOAD": "Upload & Process",
+ "CANCEL": "Atšaukti",
+ "ERROR_INVALID_TYPE": "Please select a valid PDF file",
+ "ERROR_FILE_TOO_LARGE": "File size must be less than 512MB",
+ "ERROR_UPLOAD_FAILED": "Failed to upload PDF. Please try again."
+ },
+ "PDF_DOCUMENTS": {
+ "TITLE": "PDF Documents",
+ "DESCRIPTION": "Manage uploaded PDF documents and generate FAQs from them",
+ "UPLOAD_PDF": "Upload PDF",
+ "UPLOAD_FIRST_PDF": "Upload your first PDF",
+ "UPLOADED_BY": "Uploaded by",
+ "GENERATE_FAQS": "Generate FAQs",
+ "GENERATING": "Sukurti...",
+ "CONFIRM_DELETE": "Are you sure you want to delete {filename}?",
+ "EMPTY_STATE": {
+ "TITLE": "No PDF documents yet",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQs using AI"
+ },
+ "STATUS": {
+ "UPLOADED": "Ready",
+ "PROCESSING": "Processing",
+ "PROCESSED": "Užbaigta",
+ "FAILED": "Failed"
+ }
+ },
+ "CONTENT_GENERATION": {
+ "TITLE": "Content Generation",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQ content using AI",
+ "UPLOAD_TITLE": "Upload PDF Document",
+ "DRAG_DROP": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "UPLOADING": "Processing document...",
+ "UPLOAD_SUCCESS": "Document processed successfully!",
+ "UPLOAD_ERROR": "Failed to upload document. Please try again.",
+ "INVALID_FILE_TYPE": "Please select a valid PDF file",
+ "FILE_TOO_LARGE": "File size must be less than 512MB",
+ "GENERATED_CONTENT": "Generated FAQ Content",
+ "PUBLISH_SELECTED": "Publish Selected",
+ "PUBLISHING": "Publishing...",
+ "FROM_DOCUMENT": "From document",
+ "NO_CONTENT": "No generated content available. Upload a PDF document to get started.",
+ "LOADING": "Loading generated content..."
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/lt/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/lt/inboxMgmt.json
index e35ec10b2..f125f52a0 100644
--- a/app/javascript/dashboard/i18n/locale/lt/inboxMgmt.json
+++ b/app/javascript/dashboard/i18n/locale/lt/inboxMgmt.json
@@ -272,8 +272,8 @@
},
"SUBMIT_BUTTON": "Sukurti WhatsApp Kanalą",
"EMBEDDED_SIGNUP": {
- "TITLE": "Quick Setup with Meta",
- "DESC": "You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
+ "TITLE": "Quick setup with Meta",
+ "DESC": "Use the WhatsApp Embedded Signup flow to quickly connect new numbers. You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
"BENEFITS": {
"TITLE": "Benefits of Embedded Signup:",
"EASY_SETUP": "No manual configuration required",
@@ -281,9 +281,8 @@
"AUTO_CONFIG": "Automatic webhook and phone number configuration"
},
"LEARN_MORE": {
- "TEXT": "To learn more about integrated signup, pricing, and limitations, visit",
- "LINK_TEXT": "this link.",
- "LINK_URL": "https://developers.facebook.com/docs/whatsapp/embedded-signup/custom-flows/onboarding-business-app-users#limitations"
+ "TEXT": "To learn more about integrated signup, pricing, and limitations, visit {link}.",
+ "LINK_TEXT": "this link"
},
"SUBMIT_BUTTON": "Connect with WhatsApp Business",
"AUTH_PROCESSING": "Authenticating with Meta",
@@ -296,7 +295,9 @@
"INVALID_BUSINESS_DATA": "Invalid business data received from Facebook. Please try again.",
"SIGNUP_ERROR": "Signup error occurred",
"AUTH_NOT_COMPLETED": "Authentication not completed. Please restart the process.",
- "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured"
+ "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured",
+ "MANUAL_FALLBACK": "If your number is already connected to the WhatsApp Business Platform (API), or if you’re a tech provider onboarding your own number, please use the {link} flow",
+ "MANUAL_LINK_TEXT": "manual setup flow"
},
"API": {
"ERROR_MESSAGE": "Mes negalėjome išsaugoti WhatsApp kanalo"
@@ -424,7 +425,51 @@
},
"AUTH": {
"TITLE": "Pasirinkti Kanalą",
- "DESC": "Chatwoot palaiko tiesioginio pokalbio valdiklius, Facebook Messenger, Twitter profilius, WhatsApp, el. laiškus ir kt. kaip kanalus. Jei norite sukurti personalizuotą kanalą, galite jį sukurti naudodami API kanalą. Norėdami pradėti, pasirinkite vieną iš toliau pateiktų kanalų."
+ "DESC": "Chatwoot palaiko tiesioginio pokalbio valdiklius, Facebook Messenger, Twitter profilius, WhatsApp, el. laiškus ir kt. kaip kanalus. Jei norite sukurti personalizuotą kanalą, galite jį sukurti naudodami API kanalą. Norėdami pradėti, pasirinkite vieną iš toliau pateiktų kanalų.",
+ "TITLE_NEXT": "Complete the setup",
+ "TITLE_FINISH": "Voilà!",
+ "CHANNEL": {
+ "WEBSITE": {
+ "TITLE": "Internetinis puslapis",
+ "DESCRIPTION": "Create a live-chat widget"
+ },
+ "FACEBOOK": {
+ "TITLE": "Facebook",
+ "DESCRIPTION": "Connect your Facebook page"
+ },
+ "WHATSAPP": {
+ "TITLE": "WhatsApp",
+ "DESCRIPTION": "Support your customers on WhatsApp"
+ },
+ "EMAIL": {
+ "TITLE": "El. paštas",
+ "DESCRIPTION": "Connect with Gmail, Outlook, or other providers"
+ },
+ "SMS": {
+ "TITLE": "SMS",
+ "DESCRIPTION": "Integrate SMS channel with Twilio or bandwidth"
+ },
+ "API": {
+ "TITLE": "API",
+ "DESCRIPTION": "Make a custom channel using our API"
+ },
+ "TELEGRAM": {
+ "TITLE": "Telegram",
+ "DESCRIPTION": "Configure Telegram channel using Bot token"
+ },
+ "LINE": {
+ "TITLE": "Line",
+ "DESCRIPTION": "Integrate your Line channel"
+ },
+ "INSTAGRAM": {
+ "TITLE": "Instagram",
+ "DESCRIPTION": "Connect your instagram account"
+ },
+ "VOICE": {
+ "TITLE": "Voice",
+ "DESCRIPTION": "Integrate with Twilio Voice"
+ }
+ }
},
"AGENTS": {
"TITLE": "Agentai",
@@ -478,7 +523,10 @@
"MESSAGE": "Dabar galite bendrauti su klientais naudodami naująjį kanalą. Gero naudojimo",
"BUTTON_TEXT": "Nuvesk mane ten",
"MORE_SETTINGS": "Daugiau nustatymų",
- "WEBSITE_SUCCESS": "Sėkmingai baigėte kurti svetainės kanalą. Nukopijuokite toliau pateiktą kodą ir įdėkite jį į savo svetainę. Kai kitą kartą klientas naudosis tiesioginiu pokalbiu, pokalbis bus automatiškai rodomas jūsų gautų pranešimų aplanke."
+ "WEBSITE_SUCCESS": "Sėkmingai baigėte kurti svetainės kanalą. Nukopijuokite toliau pateiktą kodą ir įdėkite jį į savo svetainę. Kai kitą kartą klientas naudosis tiesioginiu pokalbiu, pokalbis bus automatiškai rodomas jūsų gautų pranešimų aplanke.",
+ "WHATSAPP_QR_INSTRUCTION": "Scan the QR code above to quickly test your WhatsApp inbox",
+ "MESSENGER_QR_INSTRUCTION": "Scan the QR code above to quickly test your Facebook Messenger inbox",
+ "TELEGRAM_QR_INSTRUCTION": "Scan the QR code above to quickly test your Telegram inbox"
},
"REAUTH": "Pakartotinai autorizuoti",
"VIEW": "Paržiūra",
@@ -868,9 +916,18 @@
"SCRIPT_SETTINGS": "\n window.chatwootSettings = {options};"
},
"EMAIL_PROVIDERS": {
- "MICROSOFT": "Microsoft",
- "GOOGLE": "Google",
- "OTHER_PROVIDERS": "Kiti Tiekėjai"
+ "MICROSOFT": {
+ "TITLE": "Microsoft",
+ "DESCRIPTION": "Connect with Microsoft"
+ },
+ "GOOGLE": {
+ "TITLE": "Google",
+ "DESCRIPTION": "Connect with Google"
+ },
+ "OTHER_PROVIDERS": {
+ "TITLE": "Kiti Tiekėjai",
+ "DESCRIPTION": "Connect with Other Providers"
+ }
},
"CHANNELS": {
"MESSENGER": "Messenger",
diff --git a/app/javascript/dashboard/i18n/locale/lt/integrations.json b/app/javascript/dashboard/i18n/locale/lt/integrations.json
index 81cba5fee..c655e1868 100644
--- a/app/javascript/dashboard/i18n/locale/lt/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/lt/integrations.json
@@ -704,11 +704,28 @@
"ERROR_MESSAGE": "There was an error creating the document, please try again."
},
"FORM": {
+ "TYPE": {
+ "LABEL": "Document Type",
+ "URL": "URL",
+ "PDF": "PDF File"
+ },
"URL": {
"LABEL": "URL",
"PLACEHOLDER": "Enter the URL of the document",
"ERROR": "Please provide a valid URL for the document"
},
+ "PDF_FILE": {
+ "LABEL": "PDF File",
+ "CHOOSE_FILE": "Choose PDF file",
+ "ERROR": "Please select a PDF file",
+ "HELP_TEXT": "Maximum file size: 10MB",
+ "INVALID_TYPE": "Please select a valid PDF file",
+ "TOO_LARGE": "File size exceeds 10MB limit"
+ },
+ "NAME": {
+ "LABEL": "Document Name (Optional)",
+ "PLACEHOLDER": "Enter a name for the document"
+ },
"ASSISTANT": {
"LABEL": "Assistant",
"PLACEHOLDER": "Select the assistant",
@@ -744,6 +761,7 @@
"SELECTED": "{count} selected",
"SELECT_ALL": "Select all ({count})",
"UNSELECT_ALL": "Unselect all ({count})",
+ "SEARCH_PLACEHOLDER": "Search FAQs...",
"BULK_APPROVE_BUTTON": "Approve",
"BULK_DELETE_BUTTON": "Ištrinti",
"BULK_APPROVE": {
diff --git a/app/javascript/dashboard/i18n/locale/lt/mfa.json b/app/javascript/dashboard/i18n/locale/lt/mfa.json
new file mode 100644
index 000000000..08cef978f
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/lt/mfa.json
@@ -0,0 +1,106 @@
+{
+ "MFA_SETTINGS": {
+ "TITLE": "Two-Factor Authentication",
+ "SUBTITLE": "Secure your account with TOTP-based authentication",
+ "DESCRIPTION": "Add an extra layer of security to your account using a time-based one-time password (TOTP)",
+ "STATUS_TITLE": "Authentication Status",
+ "STATUS_DESCRIPTION": "Manage your two-factor authentication settings and backup recovery codes",
+ "ENABLED": "Leisti",
+ "DISABLED": "Išjungta",
+ "STATUS_ENABLED": "Two-factor authentication is active",
+ "STATUS_ENABLED_DESC": "Your account is protected with an additional layer of security",
+ "ENABLE_BUTTON": "Enable Two-Factor Authentication",
+ "ENHANCE_SECURITY": "Enhance Your Account Security",
+ "ENHANCE_SECURITY_DESC": "Two-factor authentication adds an extra layer of security by requiring a verification code from your authenticator app in addition to your password.",
+ "SETUP": {
+ "STEP_NUMBER_1": "1",
+ "STEP_NUMBER_2": "2",
+ "STEP1_TITLE": "Scan QR Code with Your Authenticator App",
+ "STEP1_DESCRIPTION": "Use Google Authenticator, Authy, or any TOTP-compatible app",
+ "LOADING_QR": "Loading...",
+ "MANUAL_ENTRY": "Can't scan? Enter code manually",
+ "SECRET_KEY": "Secret Key",
+ "COPY": "Kopijuoti",
+ "ENTER_CODE": "Enter the 6-digit code from your authenticator app",
+ "ENTER_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify & Continue",
+ "CANCEL": "Atšaukti",
+ "ERROR_STARTING": "MFA not enabled. Please contact administrator.",
+ "INVALID_CODE": "Invalid verification code",
+ "SECRET_COPIED": "Secret key copied to clipboard",
+ "SUCCESS": "Two-factor authentication has been enabled successfully"
+ },
+ "BACKUP": {
+ "TITLE": "Save Your Backup Codes",
+ "DESCRIPTION": "Keep these codes safe. Each can be used once if you lose access to your authenticator",
+ "IMPORTANT": "Important:",
+ "IMPORTANT_NOTE": " Save these codes in a secure location. You won't be able to see them again.",
+ "DOWNLOAD": "Parsisiųsti",
+ "COPY_ALL": "Copy All",
+ "CONFIRM": "I have saved my backup codes in a secure location and understand that I won't be able to see them again",
+ "COMPLETE_SETUP": "Complete Setup",
+ "CODES_COPIED": "Backup codes copied to clipboard"
+ },
+ "MANAGEMENT": {
+ "BACKUP_CODES": "Backup Codes",
+ "BACKUP_CODES_DESC": "Generate new codes if you've lost or used your existing ones",
+ "REGENERATE": "Regenerate Backup Codes",
+ "DISABLE_MFA": "Disable 2FA",
+ "DISABLE_MFA_DESC": "Remove two-factor authentication from your account",
+ "DISABLE_BUTTON": "Disable Two-Factor Authentication"
+ },
+ "DISABLE": {
+ "TITLE": "Disable Two-Factor Authentication",
+ "DESCRIPTION": "You'll need to enter your password and a verification code to disable two-factor authentication.",
+ "PASSWORD": "Slaptažodis",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Disable 2FA",
+ "CANCEL": "Atšaukti",
+ "SUCCESS": "Two-factor authentication has been disabled",
+ "ERROR": "Failed to disable MFA. Please check your credentials."
+ },
+ "REGENERATE": {
+ "TITLE": "Regenerate Backup Codes",
+ "DESCRIPTION": "This will invalidate your existing backup codes and generate new ones. Enter your verification code to continue.",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Generate New Codes",
+ "CANCEL": "Atšaukti",
+ "NEW_CODES_TITLE": "New Backup Codes Generated",
+ "NEW_CODES_DESC": "Your old backup codes have been invalidated. Save these new codes in a secure location.",
+ "CODES_IMPORTANT": "Important:",
+ "CODES_IMPORTANT_NOTE": " Each code can only be used once. Save them before closing this window.",
+ "DOWNLOAD_CODES": "Download Codes",
+ "COPY_ALL_CODES": "Copy All Codes",
+ "CODES_SAVED": "I've Saved My Codes",
+ "SUCCESS": "New backup codes have been generated",
+ "ERROR": "Failed to regenerate backup codes"
+ }
+ },
+ "MFA_VERIFICATION": {
+ "TITLE": "Two-Factor Authentication",
+ "DESCRIPTION": "Enter your verification code to continue",
+ "AUTHENTICATOR_APP": "Authenticator App",
+ "BACKUP_CODE": "Backup Code",
+ "ENTER_OTP_CODE": "Enter 6-digit code from your authenticator app",
+ "ENTER_BACKUP_CODE": "Enter one of your backup codes",
+ "BACKUP_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify",
+ "TRY_ANOTHER_METHOD": "Try another verification method",
+ "CANCEL_LOGIN": "Cancel and return to login",
+ "HELP_TEXT": "Having trouble signing in?",
+ "LEARN_MORE": "Learn more about 2FA",
+ "HELP_MODAL": {
+ "TITLE": "Two-Factor Authentication Help",
+ "AUTHENTICATOR_TITLE": "Using an Authenticator App",
+ "AUTHENTICATOR_DESC": "Open your authenticator app (Google Authenticator, Authy, etc.) and enter the 6-digit code shown for your account.",
+ "BACKUP_TITLE": "Using a Backup Code",
+ "BACKUP_DESC": "If you don't have access to your authenticator app, you can use one of the backup codes you saved when setting up 2FA. Each code can only be used once.",
+ "CONTACT_TITLE": "Need More Help?",
+ "CONTACT_DESC_CLOUD": "If you've lost access to both your authenticator app and backup codes, please reach out to Chatwoot support for assistance.",
+ "CONTACT_DESC_SELF_HOSTED": "If you've lost access to both your authenticator app and backup codes, please contact your administrator for assistance."
+ },
+ "VERIFICATION_FAILED": "Verification failed. Please try again."
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/lt/settings.json b/app/javascript/dashboard/i18n/locale/lt/settings.json
index b80a711de..53fd20b6d 100644
--- a/app/javascript/dashboard/i18n/locale/lt/settings.json
+++ b/app/javascript/dashboard/i18n/locale/lt/settings.json
@@ -51,6 +51,13 @@
"LARGER": "Larger",
"EXTRA_LARGE": "Extra Large"
}
+ },
+ "LANGUAGE": {
+ "TITLE": "Preferred Language",
+ "NOTE": "Choose the language you want to use.",
+ "UPDATE_SUCCESS": "Your Language settings have been updated successfully",
+ "UPDATE_ERROR": "There is an error while updating the language settings, please try again",
+ "USE_ACCOUNT_DEFAULT": "Use account default"
}
},
"MESSAGE_SIGNATURE_SECTION": {
@@ -73,6 +80,11 @@
"NOTE": "Atnaujinus slaptažodį būtų iš naujo nustatyti prisijungimai keliuose įrenginiuose.",
"BTN_TEXT": "Keisti slaptažodį"
},
+ "SECURITY_SECTION": {
+ "TITLE": "Security",
+ "NOTE": "Manage additional security features for your account.",
+ "MFA_BUTTON": "Manage Two-Factor Authentication"
+ },
"ACCESS_TOKEN": {
"TITLE": "Prieeigos raktas",
"NOTE": "Šis prieigos raktas gali būti naudojamas, jei kuriate API pagrįstą integraciją",
@@ -330,6 +342,7 @@
"REPORTS_LABEL": "Etiketės",
"REPORTS_INBOX": "Gautų laiškų aplankas",
"REPORTS_TEAM": "Komanda",
+ "AGENT_ASSIGNMENT": "Agent Assignment",
"SET_AVAILABILITY_TITLE": "Nustatykite save kaip",
"SET_YOUR_AVAILABILITY": "Nustatykite savo pasiekiamumą",
"SLA": "SLA",
@@ -350,7 +363,8 @@
"INFO_TEXT": "Leiskite sistemai automatiškai pažymėti jus \"neprisijungus\", kai nenaudojate programos ar informacinio skydelio.",
"INFO_SHORT": "Automatically mark offline when you aren't using the app."
},
- "DOCS": "Skaityti dokumentus"
+ "DOCS": "Skaityti dokumentus",
+ "SECURITY": "Security"
},
"BILLING_SETTINGS": {
"TITLE": "Atsiskaitymas",
@@ -382,6 +396,77 @@
},
"NO_BILLING_USER": "Jūsų atsiskaitomoji sąskaita konfigūruojama. Atnaujinkite puslapį ir bandykite dar kartą."
},
+ "SECURITY_SETTINGS": {
+ "TITLE": "Security",
+ "DESCRIPTION": "Manage your account security settings.",
+ "LINK_TEXT": "Learn more about SAML SSO",
+ "SAML": {
+ "TITLE": "SAML SSO",
+ "NOTE": "Configure SAML single sign-on for your account. Users will authenticate through your identity provider instead of using email/password.",
+ "ACS_URL": {
+ "LABEL": "ACS URL",
+ "TOOLTIP": "Assertion Consumer Service URL - Configure this URL in your IdP as the destination for SAML responses"
+ },
+ "SSO_URL": {
+ "LABEL": "SSO URL",
+ "HELP": "The URL where SAML authentication requests will be sent",
+ "PLACEHOLDER": "https://your-idp.com/saml/sso"
+ },
+ "CERTIFICATE": {
+ "LABEL": "Signing certificate in PEM format",
+ "HELP": "The public certificate from your identity provider used to verify SAML responses",
+ "PLACEHOLDER": "-----BEGIN CERTIFICATE-----\nMIIC..."
+ },
+ "FINGERPRINT": {
+ "LABEL": "Fingerprint",
+ "TOOLTIP": "SHA-1 fingerprint of the certificate - Use this to verify the certificate in your IdP configuration"
+ },
+ "COPY_SUCCESS": "Nukopijuotas į iškarpinę",
+ "SP_ENTITY_ID": {
+ "LABEL": "SP Entity ID",
+ "HELP": "Unique identifier for this application as a service provider (auto-generated).",
+ "TOOLTIP": "Unique identifier for Chatwoot as the Service Provider - Configure this in your IdP settings"
+ },
+ "IDP_ENTITY_ID": {
+ "LABEL": "Identity Provider Entity ID",
+ "HELP": "Unique identifier for your identity provider (usually found in IdP configuration)",
+ "PLACEHOLDER": "https://your-idp.com/saml"
+ },
+ "UPDATE_BUTTON": "Update SAML Settings",
+ "API": {
+ "SUCCESS": "SAML settings updated successfully",
+ "ERROR": "Failed to update SAML settings",
+ "ERROR_LOADING": "Failed to load SAML settings",
+ "DISABLED": "SAML settings disabled successfully"
+ },
+ "VALIDATION": {
+ "REQUIRED_FIELDS": "SSO URL, Identity Provider Entity ID, and Certificate are required fields",
+ "SSO_URL_ERROR": "Please enter a valid SSO URL",
+ "CERTIFICATE_ERROR": "Certificate is required",
+ "IDP_ENTITY_ID_ERROR": "Identity Provider Entity ID is required"
+ },
+ "ENTERPRISE_PAYWALL": {
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade to an Enterprise plan to access SAML single sign-on and other advanced security features.",
+ "ASK_ADMIN": "Please reach out to your administrator for the upgrade."
+ },
+ "PAYWALL": {
+ "TITLE": "Upgrade to enable SAML SSO",
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade your plan to get access to SAML single sign-on and other advanced features.",
+ "UPGRADE_NOW": "Upgrade now",
+ "CANCEL_ANYTIME": "You can change or cancel your plan anytime"
+ },
+ "ATTRIBUTE_MAPPING": {
+ "TITLE": "SAML Attribute Setup",
+ "DESCRIPTION": "The following attribute mappings must be configured in your identity provider"
+ },
+ "INFO_SECTION": {
+ "TITLE": "Service Provider Information",
+ "TOOLTIP": "Copy these values and configure them in your Identity Provider to establish the SAML connection"
+ }
+ }
+ },
"CREATE_ACCOUNT": {
"NO_ACCOUNT_WARNING": "Oi! Nepavyko rasti jokių Chatwoot paskyrų. Jei norite tęsti, susikurkite naują paskyrą.",
"NEW_ACCOUNT": "Nauja Paskyra",
@@ -418,5 +503,255 @@
"SWITCH_TO_REPLY": "Perjungti į Atsakyti",
"TOGGLE_SNOOZE_DROPDOWN": "Perjungti snooze išskleidžiamąjį meniu"
}
+ },
+ "ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent assignment",
+ "DESCRIPTION": "Define policies to effectively manage workload and route conversations based on the needs of inboxes and agents. Learn more here"
+ },
+ "ASSIGNMENT_POLICY": {
+ "TITLE": "Assignment policy",
+ "DESCRIPTION": "Manage how conversations get assigned in inboxes.",
+ "FEATURES": [
+ "Assign by conversations evenly or by available capacity",
+ "Add fair distribution rules to avoid overloading any agent",
+ "Add inboxes to a policy - one policy per inbox"
+ ]
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "TITLE": "Agent capacity policy",
+ "DESCRIPTION": "Manage workload for agents.",
+ "FEATURES": [
+ "Define maximum conversations per inbox",
+ "Create exceptions based on labels and time",
+ "Add agents to a policy - one policy per agent"
+ ]
+ }
+ },
+ "AGENT_ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Assignment policy",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "ORDER": "Order",
+ "PRIORITY": "Prioritetas",
+ "ACTIVE": "Aktyvus",
+ "INACTIVE": "Inactive",
+ "POPOVER": "Added inboxes",
+ "EDIT": "Redaguoti"
+ },
+ "NO_RECORDS_FOUND": "No assignment policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create assignment policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy created successfully",
+ "ERROR_MESSAGE": "Failed to create assignment policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit assignment policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_INBOX_DIALOG": {
+ "TITLE": "Add inbox",
+ "DESCRIPTION": "{inboxName} inbox is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Atšaukti"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update assignment policy"
+ },
+ "INBOX_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Inbox added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Inbox removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove inbox from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Aprašymas:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "STATUS": {
+ "LABEL": "Būsena:",
+ "PLACEHOLDER": "Select status",
+ "ACTIVE": "Policy is active",
+ "INACTIVE": "Policy is inactive"
+ },
+ "ASSIGNMENT_ORDER": {
+ "LABEL": "Assignment order",
+ "ROUND_ROBIN": {
+ "LABEL": "Round robin",
+ "DESCRIPTION": "Assign conversations evenly among agents."
+ },
+ "BALANCED": {
+ "LABEL": "Balanced",
+ "DESCRIPTION": "Assign conversations based on available capacity."
+ }
+ },
+ "ASSIGNMENT_PRIORITY": {
+ "LABEL": "Assignment priority",
+ "EARLIEST_CREATED": {
+ "LABEL": "Earliest created",
+ "DESCRIPTION": "The conversation that was created first gets assigned first."
+ },
+ "LONGEST_WAITING": {
+ "LABEL": "Longest waiting",
+ "DESCRIPTION": "The conversation waiting the longest gets assigned first."
+ }
+ },
+ "FAIR_DISTRIBUTION": {
+ "LABEL": "Fair distribution policy",
+ "DESCRIPTION": "Set the maximum number of conversations that can be assigned per agent within a time window to avoid overloading any one agent. This required field defaults to 100 conversations per hour.",
+ "INPUT_MAX": "Assign max",
+ "DURATION": "Conversations per agent in every"
+ },
+ "INBOXES": {
+ "LABEL": "Added inboxes",
+ "DESCRIPTION": "Add inboxes for which this policy will be applicable.",
+ "ADD_BUTTON": "Add inbox",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select inboxes to add",
+ "ADD_BUTTON": "Pridėti"
+ },
+ "EMPTY_STATE": "No inboxes added to this policy, add an inbox to get started",
+ "API": {
+ "SUCCESS_MESSAGE": "Inbox successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Assignment policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete assignment policy"
+ }
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent capacity",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "POPOVER": "Added agents",
+ "EDIT": "Redaguoti"
+ },
+ "NO_RECORDS_FOUND": "No agent capacity policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create agent capacity policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy created successfully",
+ "ERROR_MESSAGE": "Failed to create agent capacity policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit agent capacity policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_AGENT_DIALOG": {
+ "TITLE": "Add agent",
+ "DESCRIPTION": "{agentName} is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Atšaukti"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update agent capacity policy"
+ },
+ "AGENT_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Agent added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Agent removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove agent from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Aprašymas:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "INBOX_CAPACITY_LIMIT": {
+ "LABEL": "Inbox capacity limits",
+ "ADD_BUTTON": "Add inbox",
+ "FIELD": {
+ "SELECT_INBOX": "Select inbox",
+ "MAX_CONVERSATIONS": "Max conversations",
+ "SET_LIMIT": "Set limit"
+ },
+ "EMPTY_STATE": "No inbox limit set"
+ },
+ "EXCLUSION_RULES": {
+ "LABEL": "Exclusion rules",
+ "DESCRIPTION": "Conversations that satisfy the following conditions would not count towards agent capacity",
+ "TAGS": {
+ "LABEL": "Exclude conversations tagged with specific labels",
+ "ADD_TAG": "add tag",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select tags to add"
+ },
+ "EMPTY_STATE": "No tags added to this policy."
+ },
+ "DURATION": {
+ "LABEL": "Exclude conversations older than a specified duration",
+ "PLACEHOLDER": "Set time"
+ }
+ },
+ "USERS": {
+ "LABEL": "Assigned agents",
+ "DESCRIPTION": "Add agents for which this policy will be applicable.",
+ "ADD_BUTTON": "Add agent",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select agents to add",
+ "ADD_BUTTON": "Pridėti"
+ },
+ "EMPTY_STATE": "No agents added",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Agent capacity policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete agent capacity policy"
+ }
+ },
+ "DELETE_POLICY": {
+ "TITLE": "Delete policy",
+ "DESCRIPTION": "Are you sure you want to delete this policy? This action cannot be undone.",
+ "CONFIRM_BUTTON_LABEL": "Ištrinti",
+ "CANCEL_BUTTON_LABEL": "Atšaukti"
+ }
}
}
diff --git a/app/javascript/dashboard/i18n/locale/lt/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/lt/whatsappTemplates.json
index a46abc2a0..b84b04b83 100644
--- a/app/javascript/dashboard/i18n/locale/lt/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/lt/whatsappTemplates.json
@@ -40,6 +40,7 @@
"BUTTON_LABEL": "Button {index}",
"COUPON_CODE": "Enter coupon code (max 15 chars)",
"MEDIA_URL_LABEL": "Enter {type} URL",
+ "DOCUMENT_NAME_PLACEHOLDER": "Enter document filename (e.g., Invoice_2025.pdf)",
"BUTTON_PARAMETER": "Enter button parameter"
}
}
diff --git a/app/javascript/dashboard/i18n/locale/lv/automation.json b/app/javascript/dashboard/i18n/locale/lv/automation.json
index 9877bb4e3..4afc3d0b1 100644
--- a/app/javascript/dashboard/i18n/locale/lv/automation.json
+++ b/app/javascript/dashboard/i18n/locale/lv/automation.json
@@ -177,7 +177,8 @@
"REFERER_LINK": "Novirzītāja Saite",
"ASSIGNEE_NAME": "Uzdevuma saņēmējs",
"TEAM_NAME": "Komanda",
- "PRIORITY": "Prioritāte"
+ "PRIORITY": "Prioritāte",
+ "LABELS": "Etiķetes"
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/lv/contact.json b/app/javascript/dashboard/i18n/locale/lv/contact.json
index 2e547cf1e..82c483357 100644
--- a/app/javascript/dashboard/i18n/locale/lv/contact.json
+++ b/app/javascript/dashboard/i18n/locale/lv/contact.json
@@ -610,6 +610,15 @@
"SEND_MESSAGE": "Sūtīt ziņojumu"
}
},
+ "TWILIO_OPTIONS": {
+ "LABEL": "Izvēlieties veidni",
+ "SEARCH_PLACEHOLDER": "Meklēt veidnes",
+ "EMPTY_STATE": "Nav atrasta neviena veidne",
+ "TEMPLATE_PARSER": {
+ "BACK": "Atgriezties",
+ "SEND_MESSAGE": "Sūtīt ziņojumu"
+ }
+ },
"ACTION_BUTTONS": {
"DISCARD": "Izmest",
"SEND": "Sūtīt ({keyCode})"
diff --git a/app/javascript/dashboard/i18n/locale/lv/contactFilters.json b/app/javascript/dashboard/i18n/locale/lv/contactFilters.json
index cbe1d9604..ebfb7d407 100644
--- a/app/javascript/dashboard/i18n/locale/lv/contactFilters.json
+++ b/app/javascript/dashboard/i18n/locale/lv/contactFilters.json
@@ -48,7 +48,8 @@
"CREATED_AT": "Izveidots",
"LAST_ACTIVITY": "Pēdējās Darbības",
"REFERER_LINK": "Atsauces sniedzēja saite",
- "BLOCKED": "Bloķēts"
+ "BLOCKED": "Bloķēts",
+ "LABELS": "Etiķetes"
},
"GROUPS": {
"STANDARD_FILTERS": "Standarta Filtri",
diff --git a/app/javascript/dashboard/i18n/locale/lv/contentTemplates.json b/app/javascript/dashboard/i18n/locale/lv/contentTemplates.json
new file mode 100644
index 000000000..b4a712b09
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/lv/contentTemplates.json
@@ -0,0 +1,51 @@
+{
+ "CONTENT_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Twilio Templates",
+ "SUBTITLE": "Select the Twilio template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Meklēt Veidnes",
+ "NO_TEMPLATES_FOUND": "Veidnes nav atrastas",
+ "NO_CONTENT": "Nav satura",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Kategorija",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No Twilio templates available. Click refresh to sync templates from Twilio.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Valoda",
+ "TEMPLATE_BODY": "Veidnes Pamatteksts",
+ "CATEGORY": "Kategorija"
+ },
+ "TYPES": {
+ "MEDIA": "Media",
+ "QUICK_REPLY": "Quick Reply",
+ "TEXT": "Teksts"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Mainīgie",
+ "LANGUAGE": "Valoda",
+ "CATEGORY": "Kategorija",
+ "VARIABLE_PLACEHOLDER": "Ievadiet {variable} vērtību",
+ "GO_BACK_LABEL": "Atgriezties",
+ "SEND_MESSAGE_LABEL": "Sūtīt Ziņojumu",
+ "FORM_ERROR_MESSAGE": "Lūdzu, pirms nosūtīšanas aizpildiet visus mainīgos",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "MEDIA_URL_LABEL": "Enter full media URL",
+ "MEDIA_URL_PLACEHOLDER": "https://example.com/image.jpg"
+ },
+ "FORM": {
+ "BACK_BUTTON": "Atpakaļ",
+ "SEND_MESSAGE_BUTTON": "Sūtīt Ziņojumu"
+ }
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/lv/conversation.json b/app/javascript/dashboard/i18n/locale/lv/conversation.json
index 634821b17..e8bf5e497 100644
--- a/app/javascript/dashboard/i18n/locale/lv/conversation.json
+++ b/app/javascript/dashboard/i18n/locale/lv/conversation.json
@@ -35,6 +35,11 @@
"API_HOURS_WINDOW": "You can only reply to this conversation within {hours} hours",
"NOT_ASSIGNED_TO_YOU": "Šī saruna nav Jums piešķirta. Vai vēlaties piešķirt šo sarunu sev?",
"ASSIGN_TO_ME": "Piešķirt sev",
+ "BOT_HANDOFF_MESSAGE": "You are responding to a conversation which is currently handled by an assistant or a bot.",
+ "BOT_HANDOFF_ACTION": "Mark open and assign to you",
+ "BOT_HANDOFF_REOPEN_ACTION": "Mark conversation open",
+ "BOT_HANDOFF_SUCCESS": "Conversation has been handed over to you",
+ "BOT_HANDOFF_ERROR": "Failed to take over the conversation. Please try again.",
"TWILIO_WHATSAPP_CAN_REPLY": "Jūs varat atbildēt uz šo sarunu, tikai izmantojot veidnes ziņojumu, jo",
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "24 stundu ziņojuma loga ierobežojums",
"OLD_INSTAGRAM_INBOX_REPLY_BANNER": "This Instagram account was migrated to the new Instagram channel inbox. All new messages will show up there. You won’t be able to send messages from this conversation anymore.",
@@ -66,6 +71,17 @@
"SHOW_LABELS": "Rādīt etiķetes",
"HIDE_LABELS": "Slēpt etiķetes"
},
+ "VOICE_CALL": {
+ "INCOMING_CALL": "Incoming call",
+ "OUTGOING_CALL": "Outgoing call",
+ "CALL_IN_PROGRESS": "Call in progress",
+ "NO_ANSWER": "No answer",
+ "MISSED_CALL": "Missed call",
+ "CALL_ENDED": "Call ended",
+ "NOT_ANSWERED_YET": "Not answered yet",
+ "THEY_ANSWERED": "They answered",
+ "YOU_ANSWERED": "You answered"
+ },
"HEADER": {
"RESOLVE_ACTION": "Atrisināt",
"REOPEN_ACTION": "Atkārtoti atvērt",
diff --git a/app/javascript/dashboard/i18n/locale/lv/helpCenter.json b/app/javascript/dashboard/i18n/locale/lv/helpCenter.json
index 3b76723e4..7a55537c2 100644
--- a/app/javascript/dashboard/i18n/locale/lv/helpCenter.json
+++ b/app/javascript/dashboard/i18n/locale/lv/helpCenter.json
@@ -741,7 +741,8 @@
"LIVE_CHAT_WIDGET": {
"LABEL": "Tiešraides tērzēšanas widget",
"PLACEHOLDER": "Izvēlieties tiešraides tērzēšanas widget",
- "HELP_TEXT": "Izvēlieties tiešsaistes tērzēšanas widget, kas tiks parādīts jūsu palīdzības centrā"
+ "HELP_TEXT": "Izvēlieties tiešsaistes tērzēšanas widget, kas tiks parādīts jūsu palīdzības centrā",
+ "NONE_OPTION": "No widget"
},
"BRAND_COLOR": {
"LABEL": "Zīmola krāsa"
@@ -807,6 +808,58 @@
"ERROR_MESSAGE": "Nevar atjaunināt portālu"
}
}
+ },
+ "PDF_UPLOAD": {
+ "TITLE": "Upload PDF Document",
+ "DESCRIPTION": "Upload a PDF document to automatically generate FAQs using AI",
+ "DRAG_DROP_TEXT": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "ADDITIONAL_CONTEXT_LABEL": "Additional Context (Optional)",
+ "ADDITIONAL_CONTEXT_PLACEHOLDER": "Provide any additional context or instructions for FAQ generation...",
+ "UPLOADING": "Notiek Augšupielāde...",
+ "UPLOAD": "Upload & Process",
+ "CANCEL": "Atcelt",
+ "ERROR_INVALID_TYPE": "Please select a valid PDF file",
+ "ERROR_FILE_TOO_LARGE": "File size must be less than 512MB",
+ "ERROR_UPLOAD_FAILED": "Failed to upload PDF. Please try again."
+ },
+ "PDF_DOCUMENTS": {
+ "TITLE": "PDF Documents",
+ "DESCRIPTION": "Manage uploaded PDF documents and generate FAQs from them",
+ "UPLOAD_PDF": "Upload PDF",
+ "UPLOAD_FIRST_PDF": "Upload your first PDF",
+ "UPLOADED_BY": "Uploaded by",
+ "GENERATE_FAQS": "Generate FAQs",
+ "GENERATING": "Notiek ģenerēšana...",
+ "CONFIRM_DELETE": "Are you sure you want to delete {filename}?",
+ "EMPTY_STATE": {
+ "TITLE": "No PDF documents yet",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQs using AI"
+ },
+ "STATUS": {
+ "UPLOADED": "Ready",
+ "PROCESSING": "Processing",
+ "PROCESSED": "Pabeigts",
+ "FAILED": "Failed"
+ }
+ },
+ "CONTENT_GENERATION": {
+ "TITLE": "Content Generation",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQ content using AI",
+ "UPLOAD_TITLE": "Upload PDF Document",
+ "DRAG_DROP": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "UPLOADING": "Processing document...",
+ "UPLOAD_SUCCESS": "Document processed successfully!",
+ "UPLOAD_ERROR": "Failed to upload document. Please try again.",
+ "INVALID_FILE_TYPE": "Please select a valid PDF file",
+ "FILE_TOO_LARGE": "File size must be less than 512MB",
+ "GENERATED_CONTENT": "Generated FAQ Content",
+ "PUBLISH_SELECTED": "Publish Selected",
+ "PUBLISHING": "Publishing...",
+ "FROM_DOCUMENT": "From document",
+ "NO_CONTENT": "No generated content available. Upload a PDF document to get started.",
+ "LOADING": "Loading generated content..."
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/lv/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/lv/inboxMgmt.json
index 3954c9196..43038c06f 100644
--- a/app/javascript/dashboard/i18n/locale/lv/inboxMgmt.json
+++ b/app/javascript/dashboard/i18n/locale/lv/inboxMgmt.json
@@ -272,8 +272,8 @@
},
"SUBMIT_BUTTON": "Izveidot WhatsApp kanālu",
"EMBEDDED_SIGNUP": {
- "TITLE": "Quick Setup with Meta",
- "DESC": "You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
+ "TITLE": "Quick setup with Meta",
+ "DESC": "Use the WhatsApp Embedded Signup flow to quickly connect new numbers. You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
"BENEFITS": {
"TITLE": "Benefits of Embedded Signup:",
"EASY_SETUP": "No manual configuration required",
@@ -281,9 +281,8 @@
"AUTO_CONFIG": "Automatic webhook and phone number configuration"
},
"LEARN_MORE": {
- "TEXT": "To learn more about integrated signup, pricing, and limitations, visit",
- "LINK_TEXT": "this link.",
- "LINK_URL": "https://developers.facebook.com/docs/whatsapp/embedded-signup/custom-flows/onboarding-business-app-users#limitations"
+ "TEXT": "To learn more about integrated signup, pricing, and limitations, visit {link}.",
+ "LINK_TEXT": "this link"
},
"SUBMIT_BUTTON": "Connect with WhatsApp Business",
"AUTH_PROCESSING": "Authenticating with Meta",
@@ -296,7 +295,9 @@
"INVALID_BUSINESS_DATA": "Invalid business data received from Facebook. Please try again.",
"SIGNUP_ERROR": "Signup error occurred",
"AUTH_NOT_COMPLETED": "Authentication not completed. Please restart the process.",
- "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured"
+ "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured",
+ "MANUAL_FALLBACK": "If your number is already connected to the WhatsApp Business Platform (API), or if you’re a tech provider onboarding your own number, please use the {link} flow",
+ "MANUAL_LINK_TEXT": "manual setup flow"
},
"API": {
"ERROR_MESSAGE": "Mēs nevarējām saglabāt WhatsApp kanālu"
@@ -424,7 +425,51 @@
},
"AUTH": {
"TITLE": "Izvēlieties kanālu",
- "DESC": "Chatwoot atbalsta tiešraides tērzēšanas logrīkus, Facebook Messenger, Twitter profilus, WhatsApp, e-pastus kā kanālus. Ja vēlaties izveidot pielāgotu kanālu, varat to izveidot, izmantojot API kanālu. Lai sāktu, izvēlieties vienu no tālāk norādītajiem kanāliem."
+ "DESC": "Chatwoot atbalsta tiešraides tērzēšanas logrīkus, Facebook Messenger, Twitter profilus, WhatsApp, e-pastus kā kanālus. Ja vēlaties izveidot pielāgotu kanālu, varat to izveidot, izmantojot API kanālu. Lai sāktu, izvēlieties vienu no tālāk norādītajiem kanāliem.",
+ "TITLE_NEXT": "Complete the setup",
+ "TITLE_FINISH": "Gatavs!",
+ "CHANNEL": {
+ "WEBSITE": {
+ "TITLE": "Tīmekļa vietne",
+ "DESCRIPTION": "Create a live-chat widget"
+ },
+ "FACEBOOK": {
+ "TITLE": "Facebook",
+ "DESCRIPTION": "Connect your Facebook page"
+ },
+ "WHATSAPP": {
+ "TITLE": "WhatsApp",
+ "DESCRIPTION": "Support your customers on WhatsApp"
+ },
+ "EMAIL": {
+ "TITLE": "E-pasts",
+ "DESCRIPTION": "Connect with Gmail, Outlook, or other providers"
+ },
+ "SMS": {
+ "TITLE": "SMS",
+ "DESCRIPTION": "Integrate SMS channel with Twilio or bandwidth"
+ },
+ "API": {
+ "TITLE": "API",
+ "DESCRIPTION": "Make a custom channel using our API"
+ },
+ "TELEGRAM": {
+ "TITLE": "Telegram",
+ "DESCRIPTION": "Configure Telegram channel using Bot token"
+ },
+ "LINE": {
+ "TITLE": "Line",
+ "DESCRIPTION": "Integrate your Line channel"
+ },
+ "INSTAGRAM": {
+ "TITLE": "Instagram",
+ "DESCRIPTION": "Connect your instagram account"
+ },
+ "VOICE": {
+ "TITLE": "Voice",
+ "DESCRIPTION": "Integrate with Twilio Voice"
+ }
+ }
},
"AGENTS": {
"TITLE": "Aģenti",
@@ -478,7 +523,10 @@
"MESSAGE": "Tagad Jūs varat izmantot savu jauno Kanālu lai sazinātos ar saviem klientiem. Priecīgu atbalstīšanu",
"BUTTON_TEXT": "Iet uz",
"MORE_SETTINGS": "Papildu iestatījumi",
- "WEBSITE_SUCCESS": "Jūs esat veiksmīgi pabeidzis tīmekļa vietnes kanāla izveidi. Nokopējiet tālāk redzamo kodu un ievietojiet to savā tīmekļa vietnē. Nākamreiz, kad klients izmantos tiešsaistes tērzēšanu, saruna automātiski tiks parādīta Jūsu iesūtnē."
+ "WEBSITE_SUCCESS": "Jūs esat veiksmīgi pabeidzis tīmekļa vietnes kanāla izveidi. Nokopējiet tālāk redzamo kodu un ievietojiet to savā tīmekļa vietnē. Nākamreiz, kad klients izmantos tiešsaistes tērzēšanu, saruna automātiski tiks parādīta Jūsu iesūtnē.",
+ "WHATSAPP_QR_INSTRUCTION": "Scan the QR code above to quickly test your WhatsApp inbox",
+ "MESSENGER_QR_INSTRUCTION": "Scan the QR code above to quickly test your Facebook Messenger inbox",
+ "TELEGRAM_QR_INSTRUCTION": "Scan the QR code above to quickly test your Telegram inbox"
},
"REAUTH": "Atkārtoti autorizēties",
"VIEW": "Apskatīt",
@@ -868,9 +916,18 @@
"SCRIPT_SETTINGS": "\nwindow.chatwootSettings = {options};"
},
"EMAIL_PROVIDERS": {
- "MICROSOFT": "Microsoft",
- "GOOGLE": "Google",
- "OTHER_PROVIDERS": "Citi Pakalpojuma Sniedzēji"
+ "MICROSOFT": {
+ "TITLE": "Microsoft",
+ "DESCRIPTION": "Connect with Microsoft"
+ },
+ "GOOGLE": {
+ "TITLE": "Google",
+ "DESCRIPTION": "Connect with Google"
+ },
+ "OTHER_PROVIDERS": {
+ "TITLE": "Citi Pakalpojuma Sniedzēji",
+ "DESCRIPTION": "Connect with Other Providers"
+ }
},
"CHANNELS": {
"MESSENGER": "Messenger",
diff --git a/app/javascript/dashboard/i18n/locale/lv/integrations.json b/app/javascript/dashboard/i18n/locale/lv/integrations.json
index b8f109f61..a6d4764ee 100644
--- a/app/javascript/dashboard/i18n/locale/lv/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/lv/integrations.json
@@ -704,11 +704,28 @@
"ERROR_MESSAGE": "Veidojot dokumentu, radās kļūda. Lūdzu, mēģiniet vēlreiz."
},
"FORM": {
+ "TYPE": {
+ "LABEL": "Document Type",
+ "URL": "URL",
+ "PDF": "PDF File"
+ },
"URL": {
"LABEL": "URL",
"PLACEHOLDER": "Ievadiet dokumenta URL",
"ERROR": "Lūdzu, norādiet pareizu dokumenta URL"
},
+ "PDF_FILE": {
+ "LABEL": "PDF File",
+ "CHOOSE_FILE": "Choose PDF file",
+ "ERROR": "Please select a PDF file",
+ "HELP_TEXT": "Maximum file size: 10MB",
+ "INVALID_TYPE": "Please select a valid PDF file",
+ "TOO_LARGE": "File size exceeds 10MB limit"
+ },
+ "NAME": {
+ "LABEL": "Document Name (Optional)",
+ "PLACEHOLDER": "Enter a name for the document"
+ },
"ASSISTANT": {
"LABEL": "Asistents",
"PLACEHOLDER": "Izvēlēties asistentu",
@@ -744,6 +761,7 @@
"SELECTED": "Atlasīti {count}",
"SELECT_ALL": "Select all ({count})",
"UNSELECT_ALL": "Unselect all ({count})",
+ "SEARCH_PLACEHOLDER": "Search FAQs...",
"BULK_APPROVE_BUTTON": "Apstiprināt",
"BULK_DELETE_BUTTON": "Dzēst",
"BULK_APPROVE": {
diff --git a/app/javascript/dashboard/i18n/locale/lv/mfa.json b/app/javascript/dashboard/i18n/locale/lv/mfa.json
new file mode 100644
index 000000000..0bf3f0163
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/lv/mfa.json
@@ -0,0 +1,106 @@
+{
+ "MFA_SETTINGS": {
+ "TITLE": "Two-Factor Authentication",
+ "SUBTITLE": "Secure your account with TOTP-based authentication",
+ "DESCRIPTION": "Add an extra layer of security to your account using a time-based one-time password (TOTP)",
+ "STATUS_TITLE": "Authentication Status",
+ "STATUS_DESCRIPTION": "Manage your two-factor authentication settings and backup recovery codes",
+ "ENABLED": "Iespējots",
+ "DISABLED": "Atspējots",
+ "STATUS_ENABLED": "Two-factor authentication is active",
+ "STATUS_ENABLED_DESC": "Your account is protected with an additional layer of security",
+ "ENABLE_BUTTON": "Enable Two-Factor Authentication",
+ "ENHANCE_SECURITY": "Enhance Your Account Security",
+ "ENHANCE_SECURITY_DESC": "Two-factor authentication adds an extra layer of security by requiring a verification code from your authenticator app in addition to your password.",
+ "SETUP": {
+ "STEP_NUMBER_1": "1",
+ "STEP_NUMBER_2": "2",
+ "STEP1_TITLE": "Scan QR Code with Your Authenticator App",
+ "STEP1_DESCRIPTION": "Use Google Authenticator, Authy, or any TOTP-compatible app",
+ "LOADING_QR": "Notiek ielāde...",
+ "MANUAL_ENTRY": "Can't scan? Enter code manually",
+ "SECRET_KEY": "Secret Key",
+ "COPY": "Kopēt",
+ "ENTER_CODE": "Enter the 6-digit code from your authenticator app",
+ "ENTER_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify & Continue",
+ "CANCEL": "Atcelt",
+ "ERROR_STARTING": "MFA not enabled. Please contact administrator.",
+ "INVALID_CODE": "Invalid verification code",
+ "SECRET_COPIED": "Secret key copied to clipboard",
+ "SUCCESS": "Two-factor authentication has been enabled successfully"
+ },
+ "BACKUP": {
+ "TITLE": "Save Your Backup Codes",
+ "DESCRIPTION": "Keep these codes safe. Each can be used once if you lose access to your authenticator",
+ "IMPORTANT": "Important:",
+ "IMPORTANT_NOTE": " Save these codes in a secure location. You won't be able to see them again.",
+ "DOWNLOAD": "Lejupielādēt",
+ "COPY_ALL": "Copy All",
+ "CONFIRM": "I have saved my backup codes in a secure location and understand that I won't be able to see them again",
+ "COMPLETE_SETUP": "Complete Setup",
+ "CODES_COPIED": "Backup codes copied to clipboard"
+ },
+ "MANAGEMENT": {
+ "BACKUP_CODES": "Backup Codes",
+ "BACKUP_CODES_DESC": "Generate new codes if you've lost or used your existing ones",
+ "REGENERATE": "Regenerate Backup Codes",
+ "DISABLE_MFA": "Disable 2FA",
+ "DISABLE_MFA_DESC": "Remove two-factor authentication from your account",
+ "DISABLE_BUTTON": "Disable Two-Factor Authentication"
+ },
+ "DISABLE": {
+ "TITLE": "Disable Two-Factor Authentication",
+ "DESCRIPTION": "You'll need to enter your password and a verification code to disable two-factor authentication.",
+ "PASSWORD": "Parole",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Disable 2FA",
+ "CANCEL": "Atcelt",
+ "SUCCESS": "Two-factor authentication has been disabled",
+ "ERROR": "Failed to disable MFA. Please check your credentials."
+ },
+ "REGENERATE": {
+ "TITLE": "Regenerate Backup Codes",
+ "DESCRIPTION": "This will invalidate your existing backup codes and generate new ones. Enter your verification code to continue.",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Generate New Codes",
+ "CANCEL": "Atcelt",
+ "NEW_CODES_TITLE": "New Backup Codes Generated",
+ "NEW_CODES_DESC": "Your old backup codes have been invalidated. Save these new codes in a secure location.",
+ "CODES_IMPORTANT": "Important:",
+ "CODES_IMPORTANT_NOTE": " Each code can only be used once. Save them before closing this window.",
+ "DOWNLOAD_CODES": "Download Codes",
+ "COPY_ALL_CODES": "Copy All Codes",
+ "CODES_SAVED": "I've Saved My Codes",
+ "SUCCESS": "New backup codes have been generated",
+ "ERROR": "Failed to regenerate backup codes"
+ }
+ },
+ "MFA_VERIFICATION": {
+ "TITLE": "Two-Factor Authentication",
+ "DESCRIPTION": "Enter your verification code to continue",
+ "AUTHENTICATOR_APP": "Authenticator App",
+ "BACKUP_CODE": "Backup Code",
+ "ENTER_OTP_CODE": "Enter 6-digit code from your authenticator app",
+ "ENTER_BACKUP_CODE": "Enter one of your backup codes",
+ "BACKUP_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify",
+ "TRY_ANOTHER_METHOD": "Try another verification method",
+ "CANCEL_LOGIN": "Cancel and return to login",
+ "HELP_TEXT": "Having trouble signing in?",
+ "LEARN_MORE": "Learn more about 2FA",
+ "HELP_MODAL": {
+ "TITLE": "Two-Factor Authentication Help",
+ "AUTHENTICATOR_TITLE": "Using an Authenticator App",
+ "AUTHENTICATOR_DESC": "Open your authenticator app (Google Authenticator, Authy, etc.) and enter the 6-digit code shown for your account.",
+ "BACKUP_TITLE": "Using a Backup Code",
+ "BACKUP_DESC": "If you don't have access to your authenticator app, you can use one of the backup codes you saved when setting up 2FA. Each code can only be used once.",
+ "CONTACT_TITLE": "Need More Help?",
+ "CONTACT_DESC_CLOUD": "If you've lost access to both your authenticator app and backup codes, please reach out to Chatwoot support for assistance.",
+ "CONTACT_DESC_SELF_HOSTED": "If you've lost access to both your authenticator app and backup codes, please contact your administrator for assistance."
+ },
+ "VERIFICATION_FAILED": "Verification failed. Please try again."
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/lv/settings.json b/app/javascript/dashboard/i18n/locale/lv/settings.json
index 9caf8de0b..94c2307e9 100644
--- a/app/javascript/dashboard/i18n/locale/lv/settings.json
+++ b/app/javascript/dashboard/i18n/locale/lv/settings.json
@@ -51,6 +51,13 @@
"LARGER": "Lielāks",
"EXTRA_LARGE": "Īpaši Liels"
}
+ },
+ "LANGUAGE": {
+ "TITLE": "Preferred Language",
+ "NOTE": "Choose the language you want to use.",
+ "UPDATE_SUCCESS": "Your Language settings have been updated successfully",
+ "UPDATE_ERROR": "There is an error while updating the language settings, please try again",
+ "USE_ACCOUNT_DEFAULT": "Use account default"
}
},
"MESSAGE_SIGNATURE_SECTION": {
@@ -73,6 +80,11 @@
"NOTE": "Paroles atjaunināšana varētu atiestatīt jūsu pierakstīšanos vairākās ierīcēs.",
"BTN_TEXT": "Mainīt paroli"
},
+ "SECURITY_SECTION": {
+ "TITLE": "Security",
+ "NOTE": "Manage additional security features for your account.",
+ "MFA_BUTTON": "Manage Two-Factor Authentication"
+ },
"ACCESS_TOKEN": {
"TITLE": "Piekļuves Token",
"NOTE": "Šo token var izmantot, ja veidojat uz API balstītu integrāciju",
@@ -330,6 +342,7 @@
"REPORTS_LABEL": "Etiķetes",
"REPORTS_INBOX": "Iesūtne",
"REPORTS_TEAM": "Komanda",
+ "AGENT_ASSIGNMENT": "Agent Assignment",
"SET_AVAILABILITY_TITLE": "Iestatīt sevi kā",
"SET_YOUR_AVAILABILITY": "Iestatīt savu pieejamību",
"SLA": "SLA",
@@ -350,7 +363,8 @@
"INFO_TEXT": "Ļaut sistēmai, kad neizmantojat lietotni vai informācijas paneli, automātiski atzīmēt Jūs bezsaistē.",
"INFO_SHORT": "Automātiski atzīmēt bezsaistē, kad neizmantojat lietotni."
},
- "DOCS": "Lasīt dokumentus"
+ "DOCS": "Lasīt dokumentus",
+ "SECURITY": "Security"
},
"BILLING_SETTINGS": {
"TITLE": "Norēķini",
@@ -382,6 +396,77 @@
},
"NO_BILLING_USER": "Jūsu norēķinu konts tiek konfigurēts. Lūdzu, atsvaidziniet lapu un mēģiniet vēlreiz."
},
+ "SECURITY_SETTINGS": {
+ "TITLE": "Security",
+ "DESCRIPTION": "Manage your account security settings.",
+ "LINK_TEXT": "Learn more about SAML SSO",
+ "SAML": {
+ "TITLE": "SAML SSO",
+ "NOTE": "Configure SAML single sign-on for your account. Users will authenticate through your identity provider instead of using email/password.",
+ "ACS_URL": {
+ "LABEL": "ACS URL",
+ "TOOLTIP": "Assertion Consumer Service URL - Configure this URL in your IdP as the destination for SAML responses"
+ },
+ "SSO_URL": {
+ "LABEL": "SSO URL",
+ "HELP": "The URL where SAML authentication requests will be sent",
+ "PLACEHOLDER": "https://your-idp.com/saml/sso"
+ },
+ "CERTIFICATE": {
+ "LABEL": "Signing certificate in PEM format",
+ "HELP": "The public certificate from your identity provider used to verify SAML responses",
+ "PLACEHOLDER": "-----BEGIN CERTIFICATE-----\nMIIC..."
+ },
+ "FINGERPRINT": {
+ "LABEL": "Fingerprint",
+ "TOOLTIP": "SHA-1 fingerprint of the certificate - Use this to verify the certificate in your IdP configuration"
+ },
+ "COPY_SUCCESS": "Kopēts starpliktuvē",
+ "SP_ENTITY_ID": {
+ "LABEL": "SP Entity ID",
+ "HELP": "Unique identifier for this application as a service provider (auto-generated).",
+ "TOOLTIP": "Unique identifier for Chatwoot as the Service Provider - Configure this in your IdP settings"
+ },
+ "IDP_ENTITY_ID": {
+ "LABEL": "Identity Provider Entity ID",
+ "HELP": "Unique identifier for your identity provider (usually found in IdP configuration)",
+ "PLACEHOLDER": "https://your-idp.com/saml"
+ },
+ "UPDATE_BUTTON": "Update SAML Settings",
+ "API": {
+ "SUCCESS": "SAML settings updated successfully",
+ "ERROR": "Failed to update SAML settings",
+ "ERROR_LOADING": "Failed to load SAML settings",
+ "DISABLED": "SAML settings disabled successfully"
+ },
+ "VALIDATION": {
+ "REQUIRED_FIELDS": "SSO URL, Identity Provider Entity ID, and Certificate are required fields",
+ "SSO_URL_ERROR": "Please enter a valid SSO URL",
+ "CERTIFICATE_ERROR": "Certificate is required",
+ "IDP_ENTITY_ID_ERROR": "Identity Provider Entity ID is required"
+ },
+ "ENTERPRISE_PAYWALL": {
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade to an Enterprise plan to access SAML single sign-on and other advanced security features.",
+ "ASK_ADMIN": "Lai pārietu uz maksas versiju, lūdzu sazinieties ar savu administratoru."
+ },
+ "PAYWALL": {
+ "TITLE": "Upgrade to enable SAML SSO",
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade your plan to get access to SAML single sign-on and other advanced features.",
+ "UPGRADE_NOW": "Pāriet uz maksas versiju tagad",
+ "CANCEL_ANYTIME": "Jūs varat jebkurā laikā mainīt vai atcelt savu versiju"
+ },
+ "ATTRIBUTE_MAPPING": {
+ "TITLE": "SAML Attribute Setup",
+ "DESCRIPTION": "The following attribute mappings must be configured in your identity provider"
+ },
+ "INFO_SECTION": {
+ "TITLE": "Service Provider Information",
+ "TOOLTIP": "Copy these values and configure them in your Identity Provider to establish the SAML connection"
+ }
+ }
+ },
"CREATE_ACCOUNT": {
"NO_ACCOUNT_WARNING": "Ak, vai! Mēs nevarējām atrast nevienu chatwoot kontu. Lūdzu, izveidojiet jaunu kontu, lai turpinātu.",
"NEW_ACCOUNT": "Jauns Konts",
@@ -418,5 +503,255 @@
"SWITCH_TO_REPLY": "Pārslēgties uz Atbildi",
"TOGGLE_SNOOZE_DROPDOWN": "Pārslēgt atlikšanas nolaižamo izvēlni"
}
+ },
+ "ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent assignment",
+ "DESCRIPTION": "Define policies to effectively manage workload and route conversations based on the needs of inboxes and agents. Learn more here"
+ },
+ "ASSIGNMENT_POLICY": {
+ "TITLE": "Assignment policy",
+ "DESCRIPTION": "Manage how conversations get assigned in inboxes.",
+ "FEATURES": [
+ "Assign by conversations evenly or by available capacity",
+ "Add fair distribution rules to avoid overloading any agent",
+ "Add inboxes to a policy - one policy per inbox"
+ ]
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "TITLE": "Agent capacity policy",
+ "DESCRIPTION": "Manage workload for agents.",
+ "FEATURES": [
+ "Define maximum conversations per inbox",
+ "Create exceptions based on labels and time",
+ "Add agents to a policy - one policy per agent"
+ ]
+ }
+ },
+ "AGENT_ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Assignment policy",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "ORDER": "Order",
+ "PRIORITY": "Prioritāte",
+ "ACTIVE": "Aktīvs",
+ "INACTIVE": "Inactive",
+ "POPOVER": "Added inboxes",
+ "EDIT": "Rediģēt"
+ },
+ "NO_RECORDS_FOUND": "No assignment policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create assignment policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy created successfully",
+ "ERROR_MESSAGE": "Failed to create assignment policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit assignment policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_INBOX_DIALOG": {
+ "TITLE": "Add inbox",
+ "DESCRIPTION": "{inboxName} inbox is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Atcelt"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update assignment policy"
+ },
+ "INBOX_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Inbox added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Inbox removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove inbox from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Apraksts:",
+ "PLACEHOLDER": "Ievadiet aprakstu"
+ },
+ "STATUS": {
+ "LABEL": "Statuss:",
+ "PLACEHOLDER": "Izvēlēties statusu",
+ "ACTIVE": "Policy is active",
+ "INACTIVE": "Policy is inactive"
+ },
+ "ASSIGNMENT_ORDER": {
+ "LABEL": "Assignment order",
+ "ROUND_ROBIN": {
+ "LABEL": "Round robin",
+ "DESCRIPTION": "Assign conversations evenly among agents."
+ },
+ "BALANCED": {
+ "LABEL": "Balanced",
+ "DESCRIPTION": "Assign conversations based on available capacity."
+ }
+ },
+ "ASSIGNMENT_PRIORITY": {
+ "LABEL": "Assignment priority",
+ "EARLIEST_CREATED": {
+ "LABEL": "Earliest created",
+ "DESCRIPTION": "The conversation that was created first gets assigned first."
+ },
+ "LONGEST_WAITING": {
+ "LABEL": "Longest waiting",
+ "DESCRIPTION": "The conversation waiting the longest gets assigned first."
+ }
+ },
+ "FAIR_DISTRIBUTION": {
+ "LABEL": "Fair distribution policy",
+ "DESCRIPTION": "Set the maximum number of conversations that can be assigned per agent within a time window to avoid overloading any one agent. This required field defaults to 100 conversations per hour.",
+ "INPUT_MAX": "Assign max",
+ "DURATION": "Conversations per agent in every"
+ },
+ "INBOXES": {
+ "LABEL": "Added inboxes",
+ "DESCRIPTION": "Add inboxes for which this policy will be applicable.",
+ "ADD_BUTTON": "Add inbox",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select inboxes to add",
+ "ADD_BUTTON": "Pievienot"
+ },
+ "EMPTY_STATE": "No inboxes added to this policy, add an inbox to get started",
+ "API": {
+ "SUCCESS_MESSAGE": "Inbox successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Assignment policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete assignment policy"
+ }
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent capacity",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "POPOVER": "Added agents",
+ "EDIT": "Rediģēt"
+ },
+ "NO_RECORDS_FOUND": "No agent capacity policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create agent capacity policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy created successfully",
+ "ERROR_MESSAGE": "Failed to create agent capacity policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit agent capacity policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_AGENT_DIALOG": {
+ "TITLE": "Add agent",
+ "DESCRIPTION": "{agentName} is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Atcelt"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update agent capacity policy"
+ },
+ "AGENT_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Agent added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Agent removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove agent from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Apraksts:",
+ "PLACEHOLDER": "Ievadiet aprakstu"
+ },
+ "INBOX_CAPACITY_LIMIT": {
+ "LABEL": "Inbox capacity limits",
+ "ADD_BUTTON": "Add inbox",
+ "FIELD": {
+ "SELECT_INBOX": "Select inbox",
+ "MAX_CONVERSATIONS": "Max conversations",
+ "SET_LIMIT": "Set limit"
+ },
+ "EMPTY_STATE": "No inbox limit set"
+ },
+ "EXCLUSION_RULES": {
+ "LABEL": "Exclusion rules",
+ "DESCRIPTION": "Conversations that satisfy the following conditions would not count towards agent capacity",
+ "TAGS": {
+ "LABEL": "Exclude conversations tagged with specific labels",
+ "ADD_TAG": "add tag",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select tags to add"
+ },
+ "EMPTY_STATE": "No tags added to this policy."
+ },
+ "DURATION": {
+ "LABEL": "Exclude conversations older than a specified duration",
+ "PLACEHOLDER": "Set time"
+ }
+ },
+ "USERS": {
+ "LABEL": "Assigned agents",
+ "DESCRIPTION": "Add agents for which this policy will be applicable.",
+ "ADD_BUTTON": "Add agent",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select agents to add",
+ "ADD_BUTTON": "Pievienot"
+ },
+ "EMPTY_STATE": "No agents added",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Agent capacity policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete agent capacity policy"
+ }
+ },
+ "DELETE_POLICY": {
+ "TITLE": "Delete policy",
+ "DESCRIPTION": "Are you sure you want to delete this policy? This action cannot be undone.",
+ "CONFIRM_BUTTON_LABEL": "Dzēst",
+ "CANCEL_BUTTON_LABEL": "Atcelt"
+ }
}
}
diff --git a/app/javascript/dashboard/i18n/locale/lv/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/lv/whatsappTemplates.json
index 0b51f15f3..29a6e3dd1 100644
--- a/app/javascript/dashboard/i18n/locale/lv/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/lv/whatsappTemplates.json
@@ -40,6 +40,7 @@
"BUTTON_LABEL": "Button {index}",
"COUPON_CODE": "Enter coupon code (max 15 chars)",
"MEDIA_URL_LABEL": "Enter {type} URL",
+ "DOCUMENT_NAME_PLACEHOLDER": "Enter document filename (e.g., Invoice_2025.pdf)",
"BUTTON_PARAMETER": "Enter button parameter"
}
}
diff --git a/app/javascript/dashboard/i18n/locale/ml/automation.json b/app/javascript/dashboard/i18n/locale/ml/automation.json
index 6c3cd4517..b6fb7df4a 100644
--- a/app/javascript/dashboard/i18n/locale/ml/automation.json
+++ b/app/javascript/dashboard/i18n/locale/ml/automation.json
@@ -177,7 +177,8 @@
"REFERER_LINK": "Referrer Link",
"ASSIGNEE_NAME": "Assignee",
"TEAM_NAME": "Team",
- "PRIORITY": "Priority"
+ "PRIORITY": "Priority",
+ "LABELS": "ലേബലുകൾ"
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/ml/contact.json b/app/javascript/dashboard/i18n/locale/ml/contact.json
index f8143d8ab..0acae5317 100644
--- a/app/javascript/dashboard/i18n/locale/ml/contact.json
+++ b/app/javascript/dashboard/i18n/locale/ml/contact.json
@@ -610,6 +610,15 @@
"SEND_MESSAGE": "സന്ദേശം അയയ്ക്കുക"
}
},
+ "TWILIO_OPTIONS": {
+ "LABEL": "Select template",
+ "SEARCH_PLACEHOLDER": "Search templates",
+ "EMPTY_STATE": "No templates found",
+ "TEMPLATE_PARSER": {
+ "BACK": "Go back",
+ "SEND_MESSAGE": "സന്ദേശം അയയ്ക്കുക"
+ }
+ },
"ACTION_BUTTONS": {
"DISCARD": "Discard",
"SEND": "Send ({keyCode})"
diff --git a/app/javascript/dashboard/i18n/locale/ml/contactFilters.json b/app/javascript/dashboard/i18n/locale/ml/contactFilters.json
index 2f7f281bf..3b89f3a5d 100644
--- a/app/javascript/dashboard/i18n/locale/ml/contactFilters.json
+++ b/app/javascript/dashboard/i18n/locale/ml/contactFilters.json
@@ -48,7 +48,8 @@
"CREATED_AT": "Created At",
"LAST_ACTIVITY": "അവസാന പ്രവർത്തനം",
"REFERER_LINK": "Referrer link",
- "BLOCKED": "Blocked"
+ "BLOCKED": "Blocked",
+ "LABELS": "ലേബലുകൾ"
},
"GROUPS": {
"STANDARD_FILTERS": "Standard Filters",
diff --git a/app/javascript/dashboard/i18n/locale/ml/contentTemplates.json b/app/javascript/dashboard/i18n/locale/ml/contentTemplates.json
new file mode 100644
index 000000000..5b34d0989
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/ml/contentTemplates.json
@@ -0,0 +1,51 @@
+{
+ "CONTENT_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Twilio Templates",
+ "SUBTITLE": "Select the Twilio template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Search Templates",
+ "NO_TEMPLATES_FOUND": "No templates found for",
+ "NO_CONTENT": "No content",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Category",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No Twilio templates available. Click refresh to sync templates from Twilio.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Language",
+ "TEMPLATE_BODY": "Template Body",
+ "CATEGORY": "Category"
+ },
+ "TYPES": {
+ "MEDIA": "Media",
+ "QUICK_REPLY": "Quick Reply",
+ "TEXT": "Text"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variables",
+ "LANGUAGE": "Language",
+ "CATEGORY": "Category",
+ "VARIABLE_PLACEHOLDER": "Enter {variable} value",
+ "GO_BACK_LABEL": "Go Back",
+ "SEND_MESSAGE_LABEL": "Send Message",
+ "FORM_ERROR_MESSAGE": "Please fill all variables before sending",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "MEDIA_URL_LABEL": "Enter full media URL",
+ "MEDIA_URL_PLACEHOLDER": "https://example.com/image.jpg"
+ },
+ "FORM": {
+ "BACK_BUTTON": "മടങ്ങിപ്പോവുക",
+ "SEND_MESSAGE_BUTTON": "Send Message"
+ }
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/ml/conversation.json b/app/javascript/dashboard/i18n/locale/ml/conversation.json
index 269389e81..0680bf250 100644
--- a/app/javascript/dashboard/i18n/locale/ml/conversation.json
+++ b/app/javascript/dashboard/i18n/locale/ml/conversation.json
@@ -35,6 +35,11 @@
"API_HOURS_WINDOW": "You can only reply to this conversation within {hours} hours",
"NOT_ASSIGNED_TO_YOU": "This conversation is not assigned to you. Would you like to assign this conversation to yourself?",
"ASSIGN_TO_ME": "Assign to me",
+ "BOT_HANDOFF_MESSAGE": "You are responding to a conversation which is currently handled by an assistant or a bot.",
+ "BOT_HANDOFF_ACTION": "Mark open and assign to you",
+ "BOT_HANDOFF_REOPEN_ACTION": "Mark conversation open",
+ "BOT_HANDOFF_SUCCESS": "Conversation has been handed over to you",
+ "BOT_HANDOFF_ERROR": "Failed to take over the conversation. Please try again.",
"TWILIO_WHATSAPP_CAN_REPLY": "You can only reply to this conversation using a template message due to",
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "24 മണിക്കൂർ സന്ദേശ വിൻഡോ നിയന്ത്രണം",
"OLD_INSTAGRAM_INBOX_REPLY_BANNER": "This Instagram account was migrated to the new Instagram channel inbox. All new messages will show up there. You won’t be able to send messages from this conversation anymore.",
@@ -66,6 +71,17 @@
"SHOW_LABELS": "Show labels",
"HIDE_LABELS": "Hide labels"
},
+ "VOICE_CALL": {
+ "INCOMING_CALL": "Incoming call",
+ "OUTGOING_CALL": "Outgoing call",
+ "CALL_IN_PROGRESS": "Call in progress",
+ "NO_ANSWER": "No answer",
+ "MISSED_CALL": "Missed call",
+ "CALL_ENDED": "Call ended",
+ "NOT_ANSWERED_YET": "Not answered yet",
+ "THEY_ANSWERED": "They answered",
+ "YOU_ANSWERED": "You answered"
+ },
"HEADER": {
"RESOLVE_ACTION": "പരിഹരിക്കുക",
"REOPEN_ACTION": "വീണ്ടും തുറക്കുക",
diff --git a/app/javascript/dashboard/i18n/locale/ml/helpCenter.json b/app/javascript/dashboard/i18n/locale/ml/helpCenter.json
index 83e11d51b..b61f61835 100644
--- a/app/javascript/dashboard/i18n/locale/ml/helpCenter.json
+++ b/app/javascript/dashboard/i18n/locale/ml/helpCenter.json
@@ -741,7 +741,8 @@
"LIVE_CHAT_WIDGET": {
"LABEL": "Live chat widget",
"PLACEHOLDER": "Select live chat widget",
- "HELP_TEXT": "Select a live chat widget that will appear on your help center"
+ "HELP_TEXT": "Select a live chat widget that will appear on your help center",
+ "NONE_OPTION": "No widget"
},
"BRAND_COLOR": {
"LABEL": "Brand color"
@@ -807,6 +808,58 @@
"ERROR_MESSAGE": "Unable to update portal"
}
}
+ },
+ "PDF_UPLOAD": {
+ "TITLE": "Upload PDF Document",
+ "DESCRIPTION": "Upload a PDF document to automatically generate FAQs using AI",
+ "DRAG_DROP_TEXT": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "ADDITIONAL_CONTEXT_LABEL": "Additional Context (Optional)",
+ "ADDITIONAL_CONTEXT_PLACEHOLDER": "Provide any additional context or instructions for FAQ generation...",
+ "UPLOADING": "അപ്ലോഡുചെയ്യുന്നു...",
+ "UPLOAD": "Upload & Process",
+ "CANCEL": "റദ്ദാക്കുക",
+ "ERROR_INVALID_TYPE": "Please select a valid PDF file",
+ "ERROR_FILE_TOO_LARGE": "File size must be less than 512MB",
+ "ERROR_UPLOAD_FAILED": "Failed to upload PDF. Please try again."
+ },
+ "PDF_DOCUMENTS": {
+ "TITLE": "PDF Documents",
+ "DESCRIPTION": "Manage uploaded PDF documents and generate FAQs from them",
+ "UPLOAD_PDF": "Upload PDF",
+ "UPLOAD_FIRST_PDF": "Upload your first PDF",
+ "UPLOADED_BY": "Uploaded by",
+ "GENERATE_FAQS": "Generate FAQs",
+ "GENERATING": "Generating...",
+ "CONFIRM_DELETE": "Are you sure you want to delete {filename}?",
+ "EMPTY_STATE": {
+ "TITLE": "No PDF documents yet",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQs using AI"
+ },
+ "STATUS": {
+ "UPLOADED": "Ready",
+ "PROCESSING": "Processing",
+ "PROCESSED": "പൂർത്തിയാക്കി",
+ "FAILED": "Failed"
+ }
+ },
+ "CONTENT_GENERATION": {
+ "TITLE": "Content Generation",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQ content using AI",
+ "UPLOAD_TITLE": "Upload PDF Document",
+ "DRAG_DROP": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "UPLOADING": "Processing document...",
+ "UPLOAD_SUCCESS": "Document processed successfully!",
+ "UPLOAD_ERROR": "Failed to upload document. Please try again.",
+ "INVALID_FILE_TYPE": "Please select a valid PDF file",
+ "FILE_TOO_LARGE": "File size must be less than 512MB",
+ "GENERATED_CONTENT": "Generated FAQ Content",
+ "PUBLISH_SELECTED": "Publish Selected",
+ "PUBLISHING": "Publishing...",
+ "FROM_DOCUMENT": "From document",
+ "NO_CONTENT": "No generated content available. Upload a PDF document to get started.",
+ "LOADING": "Loading generated content..."
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/ml/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/ml/inboxMgmt.json
index 72eee5d91..f4a9f656f 100644
--- a/app/javascript/dashboard/i18n/locale/ml/inboxMgmt.json
+++ b/app/javascript/dashboard/i18n/locale/ml/inboxMgmt.json
@@ -272,8 +272,8 @@
},
"SUBMIT_BUTTON": "Create WhatsApp Channel",
"EMBEDDED_SIGNUP": {
- "TITLE": "Quick Setup with Meta",
- "DESC": "You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
+ "TITLE": "Quick setup with Meta",
+ "DESC": "Use the WhatsApp Embedded Signup flow to quickly connect new numbers. You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
"BENEFITS": {
"TITLE": "Benefits of Embedded Signup:",
"EASY_SETUP": "No manual configuration required",
@@ -281,9 +281,8 @@
"AUTO_CONFIG": "Automatic webhook and phone number configuration"
},
"LEARN_MORE": {
- "TEXT": "To learn more about integrated signup, pricing, and limitations, visit",
- "LINK_TEXT": "this link.",
- "LINK_URL": "https://developers.facebook.com/docs/whatsapp/embedded-signup/custom-flows/onboarding-business-app-users#limitations"
+ "TEXT": "To learn more about integrated signup, pricing, and limitations, visit {link}.",
+ "LINK_TEXT": "this link"
},
"SUBMIT_BUTTON": "Connect with WhatsApp Business",
"AUTH_PROCESSING": "Authenticating with Meta",
@@ -296,7 +295,9 @@
"INVALID_BUSINESS_DATA": "Invalid business data received from Facebook. Please try again.",
"SIGNUP_ERROR": "Signup error occurred",
"AUTH_NOT_COMPLETED": "Authentication not completed. Please restart the process.",
- "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured"
+ "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured",
+ "MANUAL_FALLBACK": "If your number is already connected to the WhatsApp Business Platform (API), or if you’re a tech provider onboarding your own number, please use the {link} flow",
+ "MANUAL_LINK_TEXT": "manual setup flow"
},
"API": {
"ERROR_MESSAGE": "We were not able to save the WhatsApp channel"
@@ -424,7 +425,51 @@
},
"AUTH": {
"TITLE": "Choose a channel",
- "DESC": "Chatwoot supports live-chat widgets, Facebook Messenger, Twitter profiles, WhatsApp, Emails, etc., as channels. If you want to build a custom channel, you can create it using the API channel. To get started, choose one of the channels below."
+ "DESC": "Chatwoot supports live-chat widgets, Facebook Messenger, Twitter profiles, WhatsApp, Emails, etc., as channels. If you want to build a custom channel, you can create it using the API channel. To get started, choose one of the channels below.",
+ "TITLE_NEXT": "Complete the setup",
+ "TITLE_FINISH": "Voilà!",
+ "CHANNEL": {
+ "WEBSITE": {
+ "TITLE": "Website",
+ "DESCRIPTION": "Create a live-chat widget"
+ },
+ "FACEBOOK": {
+ "TITLE": "Facebook",
+ "DESCRIPTION": "Connect your Facebook page"
+ },
+ "WHATSAPP": {
+ "TITLE": "WhatsApp",
+ "DESCRIPTION": "Support your customers on WhatsApp"
+ },
+ "EMAIL": {
+ "TITLE": "ഇമെയിൽ",
+ "DESCRIPTION": "Connect with Gmail, Outlook, or other providers"
+ },
+ "SMS": {
+ "TITLE": "SMS",
+ "DESCRIPTION": "Integrate SMS channel with Twilio or bandwidth"
+ },
+ "API": {
+ "TITLE": "API",
+ "DESCRIPTION": "Make a custom channel using our API"
+ },
+ "TELEGRAM": {
+ "TITLE": "Telegram",
+ "DESCRIPTION": "Configure Telegram channel using Bot token"
+ },
+ "LINE": {
+ "TITLE": "Line",
+ "DESCRIPTION": "Integrate your Line channel"
+ },
+ "INSTAGRAM": {
+ "TITLE": "Instagram",
+ "DESCRIPTION": "Connect your instagram account"
+ },
+ "VOICE": {
+ "TITLE": "Voice",
+ "DESCRIPTION": "Integrate with Twilio Voice"
+ }
+ }
},
"AGENTS": {
"TITLE": "ഏജന്റുമാർ",
@@ -478,7 +523,10 @@
"MESSAGE": "You can now engage with your customers through your new Channel. Happy supporting",
"BUTTON_TEXT": "എന്നെ അവിടേക്ക് കൊണ്ടുപോകുക",
"MORE_SETTINGS": "More settings",
- "WEBSITE_SUCCESS": "നിങ്ങൾ ഒരു വെബ്സൈറ്റ് ചാനൽ സൃഷ്ടിക്കുന്നത് വിജയകരമായി പൂർത്തിയാക്കി. ചുവടെ കാണിച്ചിരിക്കുന്ന കോഡ് പകർത്തി നിങ്ങളുടെ വെബ്സൈറ്റിൽ ചേർക്കുക. അടുത്ത തവണ ഒരു ഉപഭോക്താവ് തത്സമയ ചാറ്റ് ഉപയോഗിക്കുമ്പോൾ, സംഭാഷണം ഓട്ടോമാറ്റിക് ആയി നിങ്ങളുടെ ഇൻബോക്സിൽ ദൃശ്യമാകും."
+ "WEBSITE_SUCCESS": "നിങ്ങൾ ഒരു വെബ്സൈറ്റ് ചാനൽ സൃഷ്ടിക്കുന്നത് വിജയകരമായി പൂർത്തിയാക്കി. ചുവടെ കാണിച്ചിരിക്കുന്ന കോഡ് പകർത്തി നിങ്ങളുടെ വെബ്സൈറ്റിൽ ചേർക്കുക. അടുത്ത തവണ ഒരു ഉപഭോക്താവ് തത്സമയ ചാറ്റ് ഉപയോഗിക്കുമ്പോൾ, സംഭാഷണം ഓട്ടോമാറ്റിക് ആയി നിങ്ങളുടെ ഇൻബോക്സിൽ ദൃശ്യമാകും.",
+ "WHATSAPP_QR_INSTRUCTION": "Scan the QR code above to quickly test your WhatsApp inbox",
+ "MESSENGER_QR_INSTRUCTION": "Scan the QR code above to quickly test your Facebook Messenger inbox",
+ "TELEGRAM_QR_INSTRUCTION": "Scan the QR code above to quickly test your Telegram inbox"
},
"REAUTH": "വീണ്ടും അംഗീകാരം നൽകുക",
"VIEW": "കാണുക",
@@ -868,9 +916,18 @@
"SCRIPT_SETTINGS": "\n window.chatwootSettings = {options};"
},
"EMAIL_PROVIDERS": {
- "MICROSOFT": "Microsoft",
- "GOOGLE": "Google",
- "OTHER_PROVIDERS": "Other Providers"
+ "MICROSOFT": {
+ "TITLE": "Microsoft",
+ "DESCRIPTION": "Connect with Microsoft"
+ },
+ "GOOGLE": {
+ "TITLE": "Google",
+ "DESCRIPTION": "Connect with Google"
+ },
+ "OTHER_PROVIDERS": {
+ "TITLE": "Other Providers",
+ "DESCRIPTION": "Connect with Other Providers"
+ }
},
"CHANNELS": {
"MESSENGER": "Messenger",
diff --git a/app/javascript/dashboard/i18n/locale/ml/integrations.json b/app/javascript/dashboard/i18n/locale/ml/integrations.json
index f375f9ff2..b9011e415 100644
--- a/app/javascript/dashboard/i18n/locale/ml/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/ml/integrations.json
@@ -704,11 +704,28 @@
"ERROR_MESSAGE": "There was an error creating the document, please try again."
},
"FORM": {
+ "TYPE": {
+ "LABEL": "Document Type",
+ "URL": "URL",
+ "PDF": "PDF File"
+ },
"URL": {
"LABEL": "URL",
"PLACEHOLDER": "Enter the URL of the document",
"ERROR": "Please provide a valid URL for the document"
},
+ "PDF_FILE": {
+ "LABEL": "PDF File",
+ "CHOOSE_FILE": "Choose PDF file",
+ "ERROR": "Please select a PDF file",
+ "HELP_TEXT": "Maximum file size: 10MB",
+ "INVALID_TYPE": "Please select a valid PDF file",
+ "TOO_LARGE": "File size exceeds 10MB limit"
+ },
+ "NAME": {
+ "LABEL": "Document Name (Optional)",
+ "PLACEHOLDER": "Enter a name for the document"
+ },
"ASSISTANT": {
"LABEL": "Assistant",
"PLACEHOLDER": "Select the assistant",
@@ -744,6 +761,7 @@
"SELECTED": "{count} selected",
"SELECT_ALL": "Select all ({count})",
"UNSELECT_ALL": "Unselect all ({count})",
+ "SEARCH_PLACEHOLDER": "Search FAQs...",
"BULK_APPROVE_BUTTON": "Approve",
"BULK_DELETE_BUTTON": "ഇല്ലാതാക്കുക",
"BULK_APPROVE": {
diff --git a/app/javascript/dashboard/i18n/locale/ml/mfa.json b/app/javascript/dashboard/i18n/locale/ml/mfa.json
new file mode 100644
index 000000000..7afedc201
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/ml/mfa.json
@@ -0,0 +1,106 @@
+{
+ "MFA_SETTINGS": {
+ "TITLE": "Two-Factor Authentication",
+ "SUBTITLE": "Secure your account with TOTP-based authentication",
+ "DESCRIPTION": "Add an extra layer of security to your account using a time-based one-time password (TOTP)",
+ "STATUS_TITLE": "Authentication Status",
+ "STATUS_DESCRIPTION": "Manage your two-factor authentication settings and backup recovery codes",
+ "ENABLED": "പ്രവർത്തനക്ഷമമാക്കി",
+ "DISABLED": "പ്രവർത്തനരഹിതമാക്കി",
+ "STATUS_ENABLED": "Two-factor authentication is active",
+ "STATUS_ENABLED_DESC": "Your account is protected with an additional layer of security",
+ "ENABLE_BUTTON": "Enable Two-Factor Authentication",
+ "ENHANCE_SECURITY": "Enhance Your Account Security",
+ "ENHANCE_SECURITY_DESC": "Two-factor authentication adds an extra layer of security by requiring a verification code from your authenticator app in addition to your password.",
+ "SETUP": {
+ "STEP_NUMBER_1": "1",
+ "STEP_NUMBER_2": "2",
+ "STEP1_TITLE": "Scan QR Code with Your Authenticator App",
+ "STEP1_DESCRIPTION": "Use Google Authenticator, Authy, or any TOTP-compatible app",
+ "LOADING_QR": "Loading...",
+ "MANUAL_ENTRY": "Can't scan? Enter code manually",
+ "SECRET_KEY": "Secret Key",
+ "COPY": "പകർത്തുക",
+ "ENTER_CODE": "Enter the 6-digit code from your authenticator app",
+ "ENTER_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify & Continue",
+ "CANCEL": "റദ്ദാക്കുക",
+ "ERROR_STARTING": "MFA not enabled. Please contact administrator.",
+ "INVALID_CODE": "Invalid verification code",
+ "SECRET_COPIED": "Secret key copied to clipboard",
+ "SUCCESS": "Two-factor authentication has been enabled successfully"
+ },
+ "BACKUP": {
+ "TITLE": "Save Your Backup Codes",
+ "DESCRIPTION": "Keep these codes safe. Each can be used once if you lose access to your authenticator",
+ "IMPORTANT": "Important:",
+ "IMPORTANT_NOTE": " Save these codes in a secure location. You won't be able to see them again.",
+ "DOWNLOAD": "ഡൗൺലോഡ്",
+ "COPY_ALL": "Copy All",
+ "CONFIRM": "I have saved my backup codes in a secure location and understand that I won't be able to see them again",
+ "COMPLETE_SETUP": "Complete Setup",
+ "CODES_COPIED": "Backup codes copied to clipboard"
+ },
+ "MANAGEMENT": {
+ "BACKUP_CODES": "Backup Codes",
+ "BACKUP_CODES_DESC": "Generate new codes if you've lost or used your existing ones",
+ "REGENERATE": "Regenerate Backup Codes",
+ "DISABLE_MFA": "Disable 2FA",
+ "DISABLE_MFA_DESC": "Remove two-factor authentication from your account",
+ "DISABLE_BUTTON": "Disable Two-Factor Authentication"
+ },
+ "DISABLE": {
+ "TITLE": "Disable Two-Factor Authentication",
+ "DESCRIPTION": "You'll need to enter your password and a verification code to disable two-factor authentication.",
+ "PASSWORD": "പാസ്വേഡ്",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Disable 2FA",
+ "CANCEL": "റദ്ദാക്കുക",
+ "SUCCESS": "Two-factor authentication has been disabled",
+ "ERROR": "Failed to disable MFA. Please check your credentials."
+ },
+ "REGENERATE": {
+ "TITLE": "Regenerate Backup Codes",
+ "DESCRIPTION": "This will invalidate your existing backup codes and generate new ones. Enter your verification code to continue.",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Generate New Codes",
+ "CANCEL": "റദ്ദാക്കുക",
+ "NEW_CODES_TITLE": "New Backup Codes Generated",
+ "NEW_CODES_DESC": "Your old backup codes have been invalidated. Save these new codes in a secure location.",
+ "CODES_IMPORTANT": "Important:",
+ "CODES_IMPORTANT_NOTE": " Each code can only be used once. Save them before closing this window.",
+ "DOWNLOAD_CODES": "Download Codes",
+ "COPY_ALL_CODES": "Copy All Codes",
+ "CODES_SAVED": "I've Saved My Codes",
+ "SUCCESS": "New backup codes have been generated",
+ "ERROR": "Failed to regenerate backup codes"
+ }
+ },
+ "MFA_VERIFICATION": {
+ "TITLE": "Two-Factor Authentication",
+ "DESCRIPTION": "Enter your verification code to continue",
+ "AUTHENTICATOR_APP": "Authenticator App",
+ "BACKUP_CODE": "Backup Code",
+ "ENTER_OTP_CODE": "Enter 6-digit code from your authenticator app",
+ "ENTER_BACKUP_CODE": "Enter one of your backup codes",
+ "BACKUP_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify",
+ "TRY_ANOTHER_METHOD": "Try another verification method",
+ "CANCEL_LOGIN": "Cancel and return to login",
+ "HELP_TEXT": "Having trouble signing in?",
+ "LEARN_MORE": "Learn more about 2FA",
+ "HELP_MODAL": {
+ "TITLE": "Two-Factor Authentication Help",
+ "AUTHENTICATOR_TITLE": "Using an Authenticator App",
+ "AUTHENTICATOR_DESC": "Open your authenticator app (Google Authenticator, Authy, etc.) and enter the 6-digit code shown for your account.",
+ "BACKUP_TITLE": "Using a Backup Code",
+ "BACKUP_DESC": "If you don't have access to your authenticator app, you can use one of the backup codes you saved when setting up 2FA. Each code can only be used once.",
+ "CONTACT_TITLE": "Need More Help?",
+ "CONTACT_DESC_CLOUD": "If you've lost access to both your authenticator app and backup codes, please reach out to Chatwoot support for assistance.",
+ "CONTACT_DESC_SELF_HOSTED": "If you've lost access to both your authenticator app and backup codes, please contact your administrator for assistance."
+ },
+ "VERIFICATION_FAILED": "Verification failed. Please try again."
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/ml/settings.json b/app/javascript/dashboard/i18n/locale/ml/settings.json
index cd7eaaa4b..f698f0dd7 100644
--- a/app/javascript/dashboard/i18n/locale/ml/settings.json
+++ b/app/javascript/dashboard/i18n/locale/ml/settings.json
@@ -51,6 +51,13 @@
"LARGER": "Larger",
"EXTRA_LARGE": "Extra Large"
}
+ },
+ "LANGUAGE": {
+ "TITLE": "Preferred Language",
+ "NOTE": "Choose the language you want to use.",
+ "UPDATE_SUCCESS": "Your Language settings have been updated successfully",
+ "UPDATE_ERROR": "There is an error while updating the language settings, please try again",
+ "USE_ACCOUNT_DEFAULT": "Use account default"
}
},
"MESSAGE_SIGNATURE_SECTION": {
@@ -73,6 +80,11 @@
"NOTE": "നിങ്ങളുടെ പാസ്വേഡ് അപ്ഡേറ്റ് ചെയ്യുന്നത് ഒന്നിലധികം ഉപകരണങ്ങളിൽ നിങ്ങളുടെ ലോഗിനുകൾ പുനഃസജ്ജീകരിക്കും.",
"BTN_TEXT": "Change password"
},
+ "SECURITY_SECTION": {
+ "TITLE": "Security",
+ "NOTE": "Manage additional security features for your account.",
+ "MFA_BUTTON": "Manage Two-Factor Authentication"
+ },
"ACCESS_TOKEN": {
"TITLE": "ആക്സസ് ടോക്കൺ",
"NOTE": "നിങ്ങൾ ഒരു എപിഐ അടിസ്ഥാനമാക്കിയുള്ള സംയോജനം നിർമ്മിക്കുകയാണെങ്കിൽ ഈ ടോക്കൺ ഉപയോഗിക്കാൻ കഴിയും",
@@ -330,6 +342,7 @@
"REPORTS_LABEL": "ലേബലുകൾ",
"REPORTS_INBOX": "ഇൻബോക്സ്",
"REPORTS_TEAM": "Team",
+ "AGENT_ASSIGNMENT": "Agent Assignment",
"SET_AVAILABILITY_TITLE": "Set yourself as",
"SET_YOUR_AVAILABILITY": "Set your availability",
"SLA": "SLA",
@@ -350,7 +363,8 @@
"INFO_TEXT": "Let the system automatically mark you offline when you aren't using the app or dashboard.",
"INFO_SHORT": "Automatically mark offline when you aren't using the app."
},
- "DOCS": "Read docs"
+ "DOCS": "Read docs",
+ "SECURITY": "Security"
},
"BILLING_SETTINGS": {
"TITLE": "Billing",
@@ -382,6 +396,77 @@
},
"NO_BILLING_USER": "Your billing account is being configured. Please refresh the page and try again."
},
+ "SECURITY_SETTINGS": {
+ "TITLE": "Security",
+ "DESCRIPTION": "Manage your account security settings.",
+ "LINK_TEXT": "Learn more about SAML SSO",
+ "SAML": {
+ "TITLE": "SAML SSO",
+ "NOTE": "Configure SAML single sign-on for your account. Users will authenticate through your identity provider instead of using email/password.",
+ "ACS_URL": {
+ "LABEL": "ACS URL",
+ "TOOLTIP": "Assertion Consumer Service URL - Configure this URL in your IdP as the destination for SAML responses"
+ },
+ "SSO_URL": {
+ "LABEL": "SSO URL",
+ "HELP": "The URL where SAML authentication requests will be sent",
+ "PLACEHOLDER": "https://your-idp.com/saml/sso"
+ },
+ "CERTIFICATE": {
+ "LABEL": "Signing certificate in PEM format",
+ "HELP": "The public certificate from your identity provider used to verify SAML responses",
+ "PLACEHOLDER": "-----BEGIN CERTIFICATE-----\nMIIC..."
+ },
+ "FINGERPRINT": {
+ "LABEL": "Fingerprint",
+ "TOOLTIP": "SHA-1 fingerprint of the certificate - Use this to verify the certificate in your IdP configuration"
+ },
+ "COPY_SUCCESS": "കോഡ് ക്ലിപ്പ്ബോർഡിലേക്ക് വിജയകരമായി പകർത്തി",
+ "SP_ENTITY_ID": {
+ "LABEL": "SP Entity ID",
+ "HELP": "Unique identifier for this application as a service provider (auto-generated).",
+ "TOOLTIP": "Unique identifier for Chatwoot as the Service Provider - Configure this in your IdP settings"
+ },
+ "IDP_ENTITY_ID": {
+ "LABEL": "Identity Provider Entity ID",
+ "HELP": "Unique identifier for your identity provider (usually found in IdP configuration)",
+ "PLACEHOLDER": "https://your-idp.com/saml"
+ },
+ "UPDATE_BUTTON": "Update SAML Settings",
+ "API": {
+ "SUCCESS": "SAML settings updated successfully",
+ "ERROR": "Failed to update SAML settings",
+ "ERROR_LOADING": "Failed to load SAML settings",
+ "DISABLED": "SAML settings disabled successfully"
+ },
+ "VALIDATION": {
+ "REQUIRED_FIELDS": "SSO URL, Identity Provider Entity ID, and Certificate are required fields",
+ "SSO_URL_ERROR": "Please enter a valid SSO URL",
+ "CERTIFICATE_ERROR": "Certificate is required",
+ "IDP_ENTITY_ID_ERROR": "Identity Provider Entity ID is required"
+ },
+ "ENTERPRISE_PAYWALL": {
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade to an Enterprise plan to access SAML single sign-on and other advanced security features.",
+ "ASK_ADMIN": "Please reach out to your administrator for the upgrade."
+ },
+ "PAYWALL": {
+ "TITLE": "Upgrade to enable SAML SSO",
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade your plan to get access to SAML single sign-on and other advanced features.",
+ "UPGRADE_NOW": "Upgrade now",
+ "CANCEL_ANYTIME": "You can change or cancel your plan anytime"
+ },
+ "ATTRIBUTE_MAPPING": {
+ "TITLE": "SAML Attribute Setup",
+ "DESCRIPTION": "The following attribute mappings must be configured in your identity provider"
+ },
+ "INFO_SECTION": {
+ "TITLE": "Service Provider Information",
+ "TOOLTIP": "Copy these values and configure them in your Identity Provider to establish the SAML connection"
+ }
+ }
+ },
"CREATE_ACCOUNT": {
"NO_ACCOUNT_WARNING": "Uh oh! We could not find any Chatwoot accounts. Please create a new account to continue.",
"NEW_ACCOUNT": "പുതിയ അക്കൗണ്ട്",
@@ -418,5 +503,255 @@
"SWITCH_TO_REPLY": "Switch to Reply",
"TOGGLE_SNOOZE_DROPDOWN": "Toggle snooze dropdown"
}
+ },
+ "ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent assignment",
+ "DESCRIPTION": "Define policies to effectively manage workload and route conversations based on the needs of inboxes and agents. Learn more here"
+ },
+ "ASSIGNMENT_POLICY": {
+ "TITLE": "Assignment policy",
+ "DESCRIPTION": "Manage how conversations get assigned in inboxes.",
+ "FEATURES": [
+ "Assign by conversations evenly or by available capacity",
+ "Add fair distribution rules to avoid overloading any agent",
+ "Add inboxes to a policy - one policy per inbox"
+ ]
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "TITLE": "Agent capacity policy",
+ "DESCRIPTION": "Manage workload for agents.",
+ "FEATURES": [
+ "Define maximum conversations per inbox",
+ "Create exceptions based on labels and time",
+ "Add agents to a policy - one policy per agent"
+ ]
+ }
+ },
+ "AGENT_ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Assignment policy",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "ORDER": "Order",
+ "PRIORITY": "Priority",
+ "ACTIVE": "സജീവമാണ്",
+ "INACTIVE": "Inactive",
+ "POPOVER": "Added inboxes",
+ "EDIT": "എഡിറ്റുചെയ്യുക"
+ },
+ "NO_RECORDS_FOUND": "No assignment policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create assignment policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy created successfully",
+ "ERROR_MESSAGE": "Failed to create assignment policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit assignment policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_INBOX_DIALOG": {
+ "TITLE": "Add inbox",
+ "DESCRIPTION": "{inboxName} inbox is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "റദ്ദാക്കുക"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update assignment policy"
+ },
+ "INBOX_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Inbox added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Inbox removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove inbox from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Description:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "STATUS": {
+ "LABEL": "Status:",
+ "PLACEHOLDER": "Select status",
+ "ACTIVE": "Policy is active",
+ "INACTIVE": "Policy is inactive"
+ },
+ "ASSIGNMENT_ORDER": {
+ "LABEL": "Assignment order",
+ "ROUND_ROBIN": {
+ "LABEL": "Round robin",
+ "DESCRIPTION": "Assign conversations evenly among agents."
+ },
+ "BALANCED": {
+ "LABEL": "Balanced",
+ "DESCRIPTION": "Assign conversations based on available capacity."
+ }
+ },
+ "ASSIGNMENT_PRIORITY": {
+ "LABEL": "Assignment priority",
+ "EARLIEST_CREATED": {
+ "LABEL": "Earliest created",
+ "DESCRIPTION": "The conversation that was created first gets assigned first."
+ },
+ "LONGEST_WAITING": {
+ "LABEL": "Longest waiting",
+ "DESCRIPTION": "The conversation waiting the longest gets assigned first."
+ }
+ },
+ "FAIR_DISTRIBUTION": {
+ "LABEL": "Fair distribution policy",
+ "DESCRIPTION": "Set the maximum number of conversations that can be assigned per agent within a time window to avoid overloading any one agent. This required field defaults to 100 conversations per hour.",
+ "INPUT_MAX": "Assign max",
+ "DURATION": "Conversations per agent in every"
+ },
+ "INBOXES": {
+ "LABEL": "Added inboxes",
+ "DESCRIPTION": "Add inboxes for which this policy will be applicable.",
+ "ADD_BUTTON": "Add inbox",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select inboxes to add",
+ "ADD_BUTTON": "ചേർക്കുക"
+ },
+ "EMPTY_STATE": "No inboxes added to this policy, add an inbox to get started",
+ "API": {
+ "SUCCESS_MESSAGE": "Inbox successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Assignment policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete assignment policy"
+ }
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent capacity",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "POPOVER": "Added agents",
+ "EDIT": "എഡിറ്റുചെയ്യുക"
+ },
+ "NO_RECORDS_FOUND": "No agent capacity policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create agent capacity policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy created successfully",
+ "ERROR_MESSAGE": "Failed to create agent capacity policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit agent capacity policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_AGENT_DIALOG": {
+ "TITLE": "Add agent",
+ "DESCRIPTION": "{agentName} is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "റദ്ദാക്കുക"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update agent capacity policy"
+ },
+ "AGENT_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Agent added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Agent removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove agent from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Description:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "INBOX_CAPACITY_LIMIT": {
+ "LABEL": "Inbox capacity limits",
+ "ADD_BUTTON": "Add inbox",
+ "FIELD": {
+ "SELECT_INBOX": "Select inbox",
+ "MAX_CONVERSATIONS": "Max conversations",
+ "SET_LIMIT": "Set limit"
+ },
+ "EMPTY_STATE": "No inbox limit set"
+ },
+ "EXCLUSION_RULES": {
+ "LABEL": "Exclusion rules",
+ "DESCRIPTION": "Conversations that satisfy the following conditions would not count towards agent capacity",
+ "TAGS": {
+ "LABEL": "Exclude conversations tagged with specific labels",
+ "ADD_TAG": "add tag",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select tags to add"
+ },
+ "EMPTY_STATE": "No tags added to this policy."
+ },
+ "DURATION": {
+ "LABEL": "Exclude conversations older than a specified duration",
+ "PLACEHOLDER": "Set time"
+ }
+ },
+ "USERS": {
+ "LABEL": "Assigned agents",
+ "DESCRIPTION": "Add agents for which this policy will be applicable.",
+ "ADD_BUTTON": "Add agent",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select agents to add",
+ "ADD_BUTTON": "ചേർക്കുക"
+ },
+ "EMPTY_STATE": "No agents added",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Agent capacity policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete agent capacity policy"
+ }
+ },
+ "DELETE_POLICY": {
+ "TITLE": "Delete policy",
+ "DESCRIPTION": "Are you sure you want to delete this policy? This action cannot be undone.",
+ "CONFIRM_BUTTON_LABEL": "ഇല്ലാതാക്കുക",
+ "CANCEL_BUTTON_LABEL": "റദ്ദാക്കുക"
+ }
}
}
diff --git a/app/javascript/dashboard/i18n/locale/ml/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/ml/whatsappTemplates.json
index 5f53faaa8..cf28312dc 100644
--- a/app/javascript/dashboard/i18n/locale/ml/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/ml/whatsappTemplates.json
@@ -40,6 +40,7 @@
"BUTTON_LABEL": "Button {index}",
"COUPON_CODE": "Enter coupon code (max 15 chars)",
"MEDIA_URL_LABEL": "Enter {type} URL",
+ "DOCUMENT_NAME_PLACEHOLDER": "Enter document filename (e.g., Invoice_2025.pdf)",
"BUTTON_PARAMETER": "Enter button parameter"
}
}
diff --git a/app/javascript/dashboard/i18n/locale/ms/automation.json b/app/javascript/dashboard/i18n/locale/ms/automation.json
index e3990a8ab..3a0c84e2a 100644
--- a/app/javascript/dashboard/i18n/locale/ms/automation.json
+++ b/app/javascript/dashboard/i18n/locale/ms/automation.json
@@ -177,7 +177,8 @@
"REFERER_LINK": "Referrer Link",
"ASSIGNEE_NAME": "Assignee",
"TEAM_NAME": "Team",
- "PRIORITY": "Priority"
+ "PRIORITY": "Priority",
+ "LABELS": "Labels"
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/ms/contact.json b/app/javascript/dashboard/i18n/locale/ms/contact.json
index 0b452f059..ce68540a7 100644
--- a/app/javascript/dashboard/i18n/locale/ms/contact.json
+++ b/app/javascript/dashboard/i18n/locale/ms/contact.json
@@ -610,6 +610,15 @@
"SEND_MESSAGE": "Send message"
}
},
+ "TWILIO_OPTIONS": {
+ "LABEL": "Select template",
+ "SEARCH_PLACEHOLDER": "Search templates",
+ "EMPTY_STATE": "No templates found",
+ "TEMPLATE_PARSER": {
+ "BACK": "Go back",
+ "SEND_MESSAGE": "Send message"
+ }
+ },
"ACTION_BUTTONS": {
"DISCARD": "Discard",
"SEND": "Send ({keyCode})"
diff --git a/app/javascript/dashboard/i18n/locale/ms/contactFilters.json b/app/javascript/dashboard/i18n/locale/ms/contactFilters.json
index 63015071d..8bf0cb5fa 100644
--- a/app/javascript/dashboard/i18n/locale/ms/contactFilters.json
+++ b/app/javascript/dashboard/i18n/locale/ms/contactFilters.json
@@ -48,7 +48,8 @@
"CREATED_AT": "Dicipta Pada",
"LAST_ACTIVITY": "Last Activity",
"REFERER_LINK": "Referrer link",
- "BLOCKED": "Blocked"
+ "BLOCKED": "Blocked",
+ "LABELS": "Labels"
},
"GROUPS": {
"STANDARD_FILTERS": "Penapis Standard",
diff --git a/app/javascript/dashboard/i18n/locale/ms/contentTemplates.json b/app/javascript/dashboard/i18n/locale/ms/contentTemplates.json
new file mode 100644
index 000000000..5368e202c
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/ms/contentTemplates.json
@@ -0,0 +1,51 @@
+{
+ "CONTENT_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Twilio Templates",
+ "SUBTITLE": "Select the Twilio template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Search Templates",
+ "NO_TEMPLATES_FOUND": "No templates found for",
+ "NO_CONTENT": "No content",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Category",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No Twilio templates available. Click refresh to sync templates from Twilio.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Language",
+ "TEMPLATE_BODY": "Template Body",
+ "CATEGORY": "Category"
+ },
+ "TYPES": {
+ "MEDIA": "Media",
+ "QUICK_REPLY": "Quick Reply",
+ "TEXT": "Teks"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variables",
+ "LANGUAGE": "Language",
+ "CATEGORY": "Category",
+ "VARIABLE_PLACEHOLDER": "Enter {variable} value",
+ "GO_BACK_LABEL": "Go Back",
+ "SEND_MESSAGE_LABEL": "Send Message",
+ "FORM_ERROR_MESSAGE": "Please fill all variables before sending",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "MEDIA_URL_LABEL": "Enter full media URL",
+ "MEDIA_URL_PLACEHOLDER": "https://example.com/image.jpg"
+ },
+ "FORM": {
+ "BACK_BUTTON": "Back",
+ "SEND_MESSAGE_BUTTON": "Send Message"
+ }
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/ms/conversation.json b/app/javascript/dashboard/i18n/locale/ms/conversation.json
index b1d271e8a..062782330 100644
--- a/app/javascript/dashboard/i18n/locale/ms/conversation.json
+++ b/app/javascript/dashboard/i18n/locale/ms/conversation.json
@@ -35,6 +35,11 @@
"API_HOURS_WINDOW": "You can only reply to this conversation within {hours} hours",
"NOT_ASSIGNED_TO_YOU": "This conversation is not assigned to you. Would you like to assign this conversation to yourself?",
"ASSIGN_TO_ME": "Assign to me",
+ "BOT_HANDOFF_MESSAGE": "You are responding to a conversation which is currently handled by an assistant or a bot.",
+ "BOT_HANDOFF_ACTION": "Mark open and assign to you",
+ "BOT_HANDOFF_REOPEN_ACTION": "Mark conversation open",
+ "BOT_HANDOFF_SUCCESS": "Conversation has been handed over to you",
+ "BOT_HANDOFF_ERROR": "Failed to take over the conversation. Please try again.",
"TWILIO_WHATSAPP_CAN_REPLY": "You can only reply to this conversation using a template message due to",
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "24 hour message window restriction",
"OLD_INSTAGRAM_INBOX_REPLY_BANNER": "This Instagram account was migrated to the new Instagram channel inbox. All new messages will show up there. You won’t be able to send messages from this conversation anymore.",
@@ -66,6 +71,17 @@
"SHOW_LABELS": "Show labels",
"HIDE_LABELS": "Hide labels"
},
+ "VOICE_CALL": {
+ "INCOMING_CALL": "Incoming call",
+ "OUTGOING_CALL": "Outgoing call",
+ "CALL_IN_PROGRESS": "Call in progress",
+ "NO_ANSWER": "No answer",
+ "MISSED_CALL": "Missed call",
+ "CALL_ENDED": "Call ended",
+ "NOT_ANSWERED_YET": "Not answered yet",
+ "THEY_ANSWERED": "They answered",
+ "YOU_ANSWERED": "You answered"
+ },
"HEADER": {
"RESOLVE_ACTION": "Resolve",
"REOPEN_ACTION": "Reopen",
diff --git a/app/javascript/dashboard/i18n/locale/ms/helpCenter.json b/app/javascript/dashboard/i18n/locale/ms/helpCenter.json
index f6d9bd45e..81674aa07 100644
--- a/app/javascript/dashboard/i18n/locale/ms/helpCenter.json
+++ b/app/javascript/dashboard/i18n/locale/ms/helpCenter.json
@@ -741,7 +741,8 @@
"LIVE_CHAT_WIDGET": {
"LABEL": "Live chat widget",
"PLACEHOLDER": "Select live chat widget",
- "HELP_TEXT": "Select a live chat widget that will appear on your help center"
+ "HELP_TEXT": "Select a live chat widget that will appear on your help center",
+ "NONE_OPTION": "No widget"
},
"BRAND_COLOR": {
"LABEL": "Brand color"
@@ -807,6 +808,58 @@
"ERROR_MESSAGE": "Unable to update portal"
}
}
+ },
+ "PDF_UPLOAD": {
+ "TITLE": "Upload PDF Document",
+ "DESCRIPTION": "Upload a PDF document to automatically generate FAQs using AI",
+ "DRAG_DROP_TEXT": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "ADDITIONAL_CONTEXT_LABEL": "Additional Context (Optional)",
+ "ADDITIONAL_CONTEXT_PLACEHOLDER": "Provide any additional context or instructions for FAQ generation...",
+ "UPLOADING": "Uploading...",
+ "UPLOAD": "Upload & Process",
+ "CANCEL": "Batalkan",
+ "ERROR_INVALID_TYPE": "Please select a valid PDF file",
+ "ERROR_FILE_TOO_LARGE": "File size must be less than 512MB",
+ "ERROR_UPLOAD_FAILED": "Failed to upload PDF. Please try again."
+ },
+ "PDF_DOCUMENTS": {
+ "TITLE": "PDF Documents",
+ "DESCRIPTION": "Manage uploaded PDF documents and generate FAQs from them",
+ "UPLOAD_PDF": "Upload PDF",
+ "UPLOAD_FIRST_PDF": "Upload your first PDF",
+ "UPLOADED_BY": "Uploaded by",
+ "GENERATE_FAQS": "Generate FAQs",
+ "GENERATING": "Generating...",
+ "CONFIRM_DELETE": "Are you sure you want to delete {filename}?",
+ "EMPTY_STATE": {
+ "TITLE": "No PDF documents yet",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQs using AI"
+ },
+ "STATUS": {
+ "UPLOADED": "Ready",
+ "PROCESSING": "Processing",
+ "PROCESSED": "Completed",
+ "FAILED": "Failed"
+ }
+ },
+ "CONTENT_GENERATION": {
+ "TITLE": "Content Generation",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQ content using AI",
+ "UPLOAD_TITLE": "Upload PDF Document",
+ "DRAG_DROP": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "UPLOADING": "Processing document...",
+ "UPLOAD_SUCCESS": "Document processed successfully!",
+ "UPLOAD_ERROR": "Failed to upload document. Please try again.",
+ "INVALID_FILE_TYPE": "Please select a valid PDF file",
+ "FILE_TOO_LARGE": "File size must be less than 512MB",
+ "GENERATED_CONTENT": "Generated FAQ Content",
+ "PUBLISH_SELECTED": "Publish Selected",
+ "PUBLISHING": "Publishing...",
+ "FROM_DOCUMENT": "From document",
+ "NO_CONTENT": "No generated content available. Upload a PDF document to get started.",
+ "LOADING": "Loading generated content..."
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/ms/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/ms/inboxMgmt.json
index 02b707d82..6afcc01a0 100644
--- a/app/javascript/dashboard/i18n/locale/ms/inboxMgmt.json
+++ b/app/javascript/dashboard/i18n/locale/ms/inboxMgmt.json
@@ -272,8 +272,8 @@
},
"SUBMIT_BUTTON": "Create WhatsApp Channel",
"EMBEDDED_SIGNUP": {
- "TITLE": "Quick Setup with Meta",
- "DESC": "You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
+ "TITLE": "Quick setup with Meta",
+ "DESC": "Use the WhatsApp Embedded Signup flow to quickly connect new numbers. You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
"BENEFITS": {
"TITLE": "Benefits of Embedded Signup:",
"EASY_SETUP": "No manual configuration required",
@@ -281,9 +281,8 @@
"AUTO_CONFIG": "Automatic webhook and phone number configuration"
},
"LEARN_MORE": {
- "TEXT": "To learn more about integrated signup, pricing, and limitations, visit",
- "LINK_TEXT": "this link.",
- "LINK_URL": "https://developers.facebook.com/docs/whatsapp/embedded-signup/custom-flows/onboarding-business-app-users#limitations"
+ "TEXT": "To learn more about integrated signup, pricing, and limitations, visit {link}.",
+ "LINK_TEXT": "this link"
},
"SUBMIT_BUTTON": "Connect with WhatsApp Business",
"AUTH_PROCESSING": "Authenticating with Meta",
@@ -296,7 +295,9 @@
"INVALID_BUSINESS_DATA": "Invalid business data received from Facebook. Please try again.",
"SIGNUP_ERROR": "Signup error occurred",
"AUTH_NOT_COMPLETED": "Authentication not completed. Please restart the process.",
- "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured"
+ "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured",
+ "MANUAL_FALLBACK": "If your number is already connected to the WhatsApp Business Platform (API), or if you’re a tech provider onboarding your own number, please use the {link} flow",
+ "MANUAL_LINK_TEXT": "manual setup flow"
},
"API": {
"ERROR_MESSAGE": "We were not able to save the WhatsApp channel"
@@ -424,7 +425,51 @@
},
"AUTH": {
"TITLE": "Choose a channel",
- "DESC": "Chatwoot supports live-chat widgets, Facebook Messenger, Twitter profiles, WhatsApp, Emails, etc., as channels. If you want to build a custom channel, you can create it using the API channel. To get started, choose one of the channels below."
+ "DESC": "Chatwoot supports live-chat widgets, Facebook Messenger, Twitter profiles, WhatsApp, Emails, etc., as channels. If you want to build a custom channel, you can create it using the API channel. To get started, choose one of the channels below.",
+ "TITLE_NEXT": "Complete the setup",
+ "TITLE_FINISH": "Voilà!",
+ "CHANNEL": {
+ "WEBSITE": {
+ "TITLE": "Website",
+ "DESCRIPTION": "Create a live-chat widget"
+ },
+ "FACEBOOK": {
+ "TITLE": "Facebook",
+ "DESCRIPTION": "Connect your Facebook page"
+ },
+ "WHATSAPP": {
+ "TITLE": "WhatsApp",
+ "DESCRIPTION": "Support your customers on WhatsApp"
+ },
+ "EMAIL": {
+ "TITLE": "Email",
+ "DESCRIPTION": "Connect with Gmail, Outlook, or other providers"
+ },
+ "SMS": {
+ "TITLE": "SMS",
+ "DESCRIPTION": "Integrate SMS channel with Twilio or bandwidth"
+ },
+ "API": {
+ "TITLE": "API",
+ "DESCRIPTION": "Make a custom channel using our API"
+ },
+ "TELEGRAM": {
+ "TITLE": "Telegram",
+ "DESCRIPTION": "Configure Telegram channel using Bot token"
+ },
+ "LINE": {
+ "TITLE": "Line",
+ "DESCRIPTION": "Integrate your Line channel"
+ },
+ "INSTAGRAM": {
+ "TITLE": "Instagram",
+ "DESCRIPTION": "Connect your instagram account"
+ },
+ "VOICE": {
+ "TITLE": "Voice",
+ "DESCRIPTION": "Integrate with Twilio Voice"
+ }
+ }
},
"AGENTS": {
"TITLE": "Ejen",
@@ -478,7 +523,10 @@
"MESSAGE": "You can now engage with your customers through your new Channel. Happy supporting",
"BUTTON_TEXT": "Take me there",
"MORE_SETTINGS": "More settings",
- "WEBSITE_SUCCESS": "You have successfully finished creating a website channel. Copy the code shown below and paste it on your website. Next time a customer use the live chat, the conversation will automatically appear on your inbox."
+ "WEBSITE_SUCCESS": "You have successfully finished creating a website channel. Copy the code shown below and paste it on your website. Next time a customer use the live chat, the conversation will automatically appear on your inbox.",
+ "WHATSAPP_QR_INSTRUCTION": "Scan the QR code above to quickly test your WhatsApp inbox",
+ "MESSENGER_QR_INSTRUCTION": "Scan the QR code above to quickly test your Facebook Messenger inbox",
+ "TELEGRAM_QR_INSTRUCTION": "Scan the QR code above to quickly test your Telegram inbox"
},
"REAUTH": "Reauthorize",
"VIEW": "View",
@@ -868,9 +916,18 @@
"SCRIPT_SETTINGS": "\n window.chatwootSettings = {options};"
},
"EMAIL_PROVIDERS": {
- "MICROSOFT": "Microsoft",
- "GOOGLE": "Google",
- "OTHER_PROVIDERS": "Other Providers"
+ "MICROSOFT": {
+ "TITLE": "Microsoft",
+ "DESCRIPTION": "Connect with Microsoft"
+ },
+ "GOOGLE": {
+ "TITLE": "Google",
+ "DESCRIPTION": "Connect with Google"
+ },
+ "OTHER_PROVIDERS": {
+ "TITLE": "Other Providers",
+ "DESCRIPTION": "Connect with Other Providers"
+ }
},
"CHANNELS": {
"MESSENGER": "Messenger",
diff --git a/app/javascript/dashboard/i18n/locale/ms/integrations.json b/app/javascript/dashboard/i18n/locale/ms/integrations.json
index cb2c4a677..572e58b04 100644
--- a/app/javascript/dashboard/i18n/locale/ms/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/ms/integrations.json
@@ -704,11 +704,28 @@
"ERROR_MESSAGE": "There was an error creating the document, please try again."
},
"FORM": {
+ "TYPE": {
+ "LABEL": "Document Type",
+ "URL": "URL",
+ "PDF": "PDF File"
+ },
"URL": {
"LABEL": "URL",
"PLACEHOLDER": "Enter the URL of the document",
"ERROR": "Please provide a valid URL for the document"
},
+ "PDF_FILE": {
+ "LABEL": "PDF File",
+ "CHOOSE_FILE": "Choose PDF file",
+ "ERROR": "Please select a PDF file",
+ "HELP_TEXT": "Maximum file size: 10MB",
+ "INVALID_TYPE": "Please select a valid PDF file",
+ "TOO_LARGE": "File size exceeds 10MB limit"
+ },
+ "NAME": {
+ "LABEL": "Document Name (Optional)",
+ "PLACEHOLDER": "Enter a name for the document"
+ },
"ASSISTANT": {
"LABEL": "Assistant",
"PLACEHOLDER": "Select the assistant",
@@ -744,6 +761,7 @@
"SELECTED": "{count} selected",
"SELECT_ALL": "Select all ({count})",
"UNSELECT_ALL": "Unselect all ({count})",
+ "SEARCH_PLACEHOLDER": "Search FAQs...",
"BULK_APPROVE_BUTTON": "Approve",
"BULK_DELETE_BUTTON": "Padamkan",
"BULK_APPROVE": {
diff --git a/app/javascript/dashboard/i18n/locale/ms/mfa.json b/app/javascript/dashboard/i18n/locale/ms/mfa.json
new file mode 100644
index 000000000..4e0b282be
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/ms/mfa.json
@@ -0,0 +1,106 @@
+{
+ "MFA_SETTINGS": {
+ "TITLE": "Two-Factor Authentication",
+ "SUBTITLE": "Secure your account with TOTP-based authentication",
+ "DESCRIPTION": "Add an extra layer of security to your account using a time-based one-time password (TOTP)",
+ "STATUS_TITLE": "Authentication Status",
+ "STATUS_DESCRIPTION": "Manage your two-factor authentication settings and backup recovery codes",
+ "ENABLED": "Enabled",
+ "DISABLED": "Disabled",
+ "STATUS_ENABLED": "Two-factor authentication is active",
+ "STATUS_ENABLED_DESC": "Your account is protected with an additional layer of security",
+ "ENABLE_BUTTON": "Enable Two-Factor Authentication",
+ "ENHANCE_SECURITY": "Enhance Your Account Security",
+ "ENHANCE_SECURITY_DESC": "Two-factor authentication adds an extra layer of security by requiring a verification code from your authenticator app in addition to your password.",
+ "SETUP": {
+ "STEP_NUMBER_1": "1",
+ "STEP_NUMBER_2": "2",
+ "STEP1_TITLE": "Scan QR Code with Your Authenticator App",
+ "STEP1_DESCRIPTION": "Use Google Authenticator, Authy, or any TOTP-compatible app",
+ "LOADING_QR": "Loading...",
+ "MANUAL_ENTRY": "Can't scan? Enter code manually",
+ "SECRET_KEY": "Secret Key",
+ "COPY": "Copy",
+ "ENTER_CODE": "Enter the 6-digit code from your authenticator app",
+ "ENTER_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify & Continue",
+ "CANCEL": "Batalkan",
+ "ERROR_STARTING": "MFA not enabled. Please contact administrator.",
+ "INVALID_CODE": "Invalid verification code",
+ "SECRET_COPIED": "Secret key copied to clipboard",
+ "SUCCESS": "Two-factor authentication has been enabled successfully"
+ },
+ "BACKUP": {
+ "TITLE": "Save Your Backup Codes",
+ "DESCRIPTION": "Keep these codes safe. Each can be used once if you lose access to your authenticator",
+ "IMPORTANT": "Important:",
+ "IMPORTANT_NOTE": " Save these codes in a secure location. You won't be able to see them again.",
+ "DOWNLOAD": "Download",
+ "COPY_ALL": "Copy All",
+ "CONFIRM": "I have saved my backup codes in a secure location and understand that I won't be able to see them again",
+ "COMPLETE_SETUP": "Complete Setup",
+ "CODES_COPIED": "Backup codes copied to clipboard"
+ },
+ "MANAGEMENT": {
+ "BACKUP_CODES": "Backup Codes",
+ "BACKUP_CODES_DESC": "Generate new codes if you've lost or used your existing ones",
+ "REGENERATE": "Regenerate Backup Codes",
+ "DISABLE_MFA": "Disable 2FA",
+ "DISABLE_MFA_DESC": "Remove two-factor authentication from your account",
+ "DISABLE_BUTTON": "Disable Two-Factor Authentication"
+ },
+ "DISABLE": {
+ "TITLE": "Disable Two-Factor Authentication",
+ "DESCRIPTION": "You'll need to enter your password and a verification code to disable two-factor authentication.",
+ "PASSWORD": "Password",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Disable 2FA",
+ "CANCEL": "Batalkan",
+ "SUCCESS": "Two-factor authentication has been disabled",
+ "ERROR": "Failed to disable MFA. Please check your credentials."
+ },
+ "REGENERATE": {
+ "TITLE": "Regenerate Backup Codes",
+ "DESCRIPTION": "This will invalidate your existing backup codes and generate new ones. Enter your verification code to continue.",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Generate New Codes",
+ "CANCEL": "Batalkan",
+ "NEW_CODES_TITLE": "New Backup Codes Generated",
+ "NEW_CODES_DESC": "Your old backup codes have been invalidated. Save these new codes in a secure location.",
+ "CODES_IMPORTANT": "Important:",
+ "CODES_IMPORTANT_NOTE": " Each code can only be used once. Save them before closing this window.",
+ "DOWNLOAD_CODES": "Download Codes",
+ "COPY_ALL_CODES": "Copy All Codes",
+ "CODES_SAVED": "I've Saved My Codes",
+ "SUCCESS": "New backup codes have been generated",
+ "ERROR": "Failed to regenerate backup codes"
+ }
+ },
+ "MFA_VERIFICATION": {
+ "TITLE": "Two-Factor Authentication",
+ "DESCRIPTION": "Enter your verification code to continue",
+ "AUTHENTICATOR_APP": "Authenticator App",
+ "BACKUP_CODE": "Backup Code",
+ "ENTER_OTP_CODE": "Enter 6-digit code from your authenticator app",
+ "ENTER_BACKUP_CODE": "Enter one of your backup codes",
+ "BACKUP_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify",
+ "TRY_ANOTHER_METHOD": "Try another verification method",
+ "CANCEL_LOGIN": "Cancel and return to login",
+ "HELP_TEXT": "Having trouble signing in?",
+ "LEARN_MORE": "Learn more about 2FA",
+ "HELP_MODAL": {
+ "TITLE": "Two-Factor Authentication Help",
+ "AUTHENTICATOR_TITLE": "Using an Authenticator App",
+ "AUTHENTICATOR_DESC": "Open your authenticator app (Google Authenticator, Authy, etc.) and enter the 6-digit code shown for your account.",
+ "BACKUP_TITLE": "Using a Backup Code",
+ "BACKUP_DESC": "If you don't have access to your authenticator app, you can use one of the backup codes you saved when setting up 2FA. Each code can only be used once.",
+ "CONTACT_TITLE": "Need More Help?",
+ "CONTACT_DESC_CLOUD": "If you've lost access to both your authenticator app and backup codes, please reach out to Chatwoot support for assistance.",
+ "CONTACT_DESC_SELF_HOSTED": "If you've lost access to both your authenticator app and backup codes, please contact your administrator for assistance."
+ },
+ "VERIFICATION_FAILED": "Verification failed. Please try again."
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/ms/settings.json b/app/javascript/dashboard/i18n/locale/ms/settings.json
index 386fddf66..36df89c78 100644
--- a/app/javascript/dashboard/i18n/locale/ms/settings.json
+++ b/app/javascript/dashboard/i18n/locale/ms/settings.json
@@ -51,6 +51,13 @@
"LARGER": "Larger",
"EXTRA_LARGE": "Extra Large"
}
+ },
+ "LANGUAGE": {
+ "TITLE": "Preferred Language",
+ "NOTE": "Choose the language you want to use.",
+ "UPDATE_SUCCESS": "Your Language settings have been updated successfully",
+ "UPDATE_ERROR": "There is an error while updating the language settings, please try again",
+ "USE_ACCOUNT_DEFAULT": "Use account default"
}
},
"MESSAGE_SIGNATURE_SECTION": {
@@ -73,6 +80,11 @@
"NOTE": "Updating your password would reset your logins in multiple devices.",
"BTN_TEXT": "Change password"
},
+ "SECURITY_SECTION": {
+ "TITLE": "Security",
+ "NOTE": "Manage additional security features for your account.",
+ "MFA_BUTTON": "Manage Two-Factor Authentication"
+ },
"ACCESS_TOKEN": {
"TITLE": "Access Token",
"NOTE": "This token can be used if you are building an API based integration",
@@ -330,6 +342,7 @@
"REPORTS_LABEL": "Labels",
"REPORTS_INBOX": "Inbox",
"REPORTS_TEAM": "Team",
+ "AGENT_ASSIGNMENT": "Agent Assignment",
"SET_AVAILABILITY_TITLE": "Set yourself as",
"SET_YOUR_AVAILABILITY": "Set your availability",
"SLA": "SLA",
@@ -350,7 +363,8 @@
"INFO_TEXT": "Let the system automatically mark you offline when you aren't using the app or dashboard.",
"INFO_SHORT": "Automatically mark offline when you aren't using the app."
},
- "DOCS": "Read docs"
+ "DOCS": "Read docs",
+ "SECURITY": "Security"
},
"BILLING_SETTINGS": {
"TITLE": "Billing",
@@ -382,6 +396,77 @@
},
"NO_BILLING_USER": "Your billing account is being configured. Please refresh the page and try again."
},
+ "SECURITY_SETTINGS": {
+ "TITLE": "Security",
+ "DESCRIPTION": "Manage your account security settings.",
+ "LINK_TEXT": "Learn more about SAML SSO",
+ "SAML": {
+ "TITLE": "SAML SSO",
+ "NOTE": "Configure SAML single sign-on for your account. Users will authenticate through your identity provider instead of using email/password.",
+ "ACS_URL": {
+ "LABEL": "ACS URL",
+ "TOOLTIP": "Assertion Consumer Service URL - Configure this URL in your IdP as the destination for SAML responses"
+ },
+ "SSO_URL": {
+ "LABEL": "SSO URL",
+ "HELP": "The URL where SAML authentication requests will be sent",
+ "PLACEHOLDER": "https://your-idp.com/saml/sso"
+ },
+ "CERTIFICATE": {
+ "LABEL": "Signing certificate in PEM format",
+ "HELP": "The public certificate from your identity provider used to verify SAML responses",
+ "PLACEHOLDER": "-----BEGIN CERTIFICATE-----\nMIIC..."
+ },
+ "FINGERPRINT": {
+ "LABEL": "Fingerprint",
+ "TOOLTIP": "SHA-1 fingerprint of the certificate - Use this to verify the certificate in your IdP configuration"
+ },
+ "COPY_SUCCESS": "Code copied to clipboard successfully",
+ "SP_ENTITY_ID": {
+ "LABEL": "SP Entity ID",
+ "HELP": "Unique identifier for this application as a service provider (auto-generated).",
+ "TOOLTIP": "Unique identifier for Chatwoot as the Service Provider - Configure this in your IdP settings"
+ },
+ "IDP_ENTITY_ID": {
+ "LABEL": "Identity Provider Entity ID",
+ "HELP": "Unique identifier for your identity provider (usually found in IdP configuration)",
+ "PLACEHOLDER": "https://your-idp.com/saml"
+ },
+ "UPDATE_BUTTON": "Update SAML Settings",
+ "API": {
+ "SUCCESS": "SAML settings updated successfully",
+ "ERROR": "Failed to update SAML settings",
+ "ERROR_LOADING": "Failed to load SAML settings",
+ "DISABLED": "SAML settings disabled successfully"
+ },
+ "VALIDATION": {
+ "REQUIRED_FIELDS": "SSO URL, Identity Provider Entity ID, and Certificate are required fields",
+ "SSO_URL_ERROR": "Please enter a valid SSO URL",
+ "CERTIFICATE_ERROR": "Certificate is required",
+ "IDP_ENTITY_ID_ERROR": "Identity Provider Entity ID is required"
+ },
+ "ENTERPRISE_PAYWALL": {
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade to an Enterprise plan to access SAML single sign-on and other advanced security features.",
+ "ASK_ADMIN": "Please reach out to your administrator for the upgrade."
+ },
+ "PAYWALL": {
+ "TITLE": "Upgrade to enable SAML SSO",
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade your plan to get access to SAML single sign-on and other advanced features.",
+ "UPGRADE_NOW": "Upgrade now",
+ "CANCEL_ANYTIME": "You can change or cancel your plan anytime"
+ },
+ "ATTRIBUTE_MAPPING": {
+ "TITLE": "SAML Attribute Setup",
+ "DESCRIPTION": "The following attribute mappings must be configured in your identity provider"
+ },
+ "INFO_SECTION": {
+ "TITLE": "Service Provider Information",
+ "TOOLTIP": "Copy these values and configure them in your Identity Provider to establish the SAML connection"
+ }
+ }
+ },
"CREATE_ACCOUNT": {
"NO_ACCOUNT_WARNING": "Uh oh! We could not find any Chatwoot accounts. Please create a new account to continue.",
"NEW_ACCOUNT": "New Account",
@@ -418,5 +503,255 @@
"SWITCH_TO_REPLY": "Switch to Reply",
"TOGGLE_SNOOZE_DROPDOWN": "Toggle snooze dropdown"
}
+ },
+ "ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent assignment",
+ "DESCRIPTION": "Define policies to effectively manage workload and route conversations based on the needs of inboxes and agents. Learn more here"
+ },
+ "ASSIGNMENT_POLICY": {
+ "TITLE": "Assignment policy",
+ "DESCRIPTION": "Manage how conversations get assigned in inboxes.",
+ "FEATURES": [
+ "Assign by conversations evenly or by available capacity",
+ "Add fair distribution rules to avoid overloading any agent",
+ "Add inboxes to a policy - one policy per inbox"
+ ]
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "TITLE": "Agent capacity policy",
+ "DESCRIPTION": "Manage workload for agents.",
+ "FEATURES": [
+ "Define maximum conversations per inbox",
+ "Create exceptions based on labels and time",
+ "Add agents to a policy - one policy per agent"
+ ]
+ }
+ },
+ "AGENT_ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Assignment policy",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "ORDER": "Order",
+ "PRIORITY": "Priority",
+ "ACTIVE": "Active",
+ "INACTIVE": "Inactive",
+ "POPOVER": "Added inboxes",
+ "EDIT": "Edit"
+ },
+ "NO_RECORDS_FOUND": "No assignment policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create assignment policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy created successfully",
+ "ERROR_MESSAGE": "Failed to create assignment policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit assignment policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_INBOX_DIALOG": {
+ "TITLE": "Add inbox",
+ "DESCRIPTION": "{inboxName} inbox is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Batalkan"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update assignment policy"
+ },
+ "INBOX_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Inbox added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Inbox removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove inbox from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Description:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "STATUS": {
+ "LABEL": "Status:",
+ "PLACEHOLDER": "Select status",
+ "ACTIVE": "Policy is active",
+ "INACTIVE": "Policy is inactive"
+ },
+ "ASSIGNMENT_ORDER": {
+ "LABEL": "Assignment order",
+ "ROUND_ROBIN": {
+ "LABEL": "Round robin",
+ "DESCRIPTION": "Assign conversations evenly among agents."
+ },
+ "BALANCED": {
+ "LABEL": "Balanced",
+ "DESCRIPTION": "Assign conversations based on available capacity."
+ }
+ },
+ "ASSIGNMENT_PRIORITY": {
+ "LABEL": "Assignment priority",
+ "EARLIEST_CREATED": {
+ "LABEL": "Earliest created",
+ "DESCRIPTION": "The conversation that was created first gets assigned first."
+ },
+ "LONGEST_WAITING": {
+ "LABEL": "Longest waiting",
+ "DESCRIPTION": "The conversation waiting the longest gets assigned first."
+ }
+ },
+ "FAIR_DISTRIBUTION": {
+ "LABEL": "Fair distribution policy",
+ "DESCRIPTION": "Set the maximum number of conversations that can be assigned per agent within a time window to avoid overloading any one agent. This required field defaults to 100 conversations per hour.",
+ "INPUT_MAX": "Assign max",
+ "DURATION": "Conversations per agent in every"
+ },
+ "INBOXES": {
+ "LABEL": "Added inboxes",
+ "DESCRIPTION": "Add inboxes for which this policy will be applicable.",
+ "ADD_BUTTON": "Add inbox",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select inboxes to add",
+ "ADD_BUTTON": "Add"
+ },
+ "EMPTY_STATE": "No inboxes added to this policy, add an inbox to get started",
+ "API": {
+ "SUCCESS_MESSAGE": "Inbox successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Assignment policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete assignment policy"
+ }
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent capacity",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "POPOVER": "Added agents",
+ "EDIT": "Edit"
+ },
+ "NO_RECORDS_FOUND": "No agent capacity policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create agent capacity policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy created successfully",
+ "ERROR_MESSAGE": "Failed to create agent capacity policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit agent capacity policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_AGENT_DIALOG": {
+ "TITLE": "Add agent",
+ "DESCRIPTION": "{agentName} is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Batalkan"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update agent capacity policy"
+ },
+ "AGENT_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Agent added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Agent removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove agent from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Description:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "INBOX_CAPACITY_LIMIT": {
+ "LABEL": "Inbox capacity limits",
+ "ADD_BUTTON": "Add inbox",
+ "FIELD": {
+ "SELECT_INBOX": "Select inbox",
+ "MAX_CONVERSATIONS": "Max conversations",
+ "SET_LIMIT": "Set limit"
+ },
+ "EMPTY_STATE": "No inbox limit set"
+ },
+ "EXCLUSION_RULES": {
+ "LABEL": "Exclusion rules",
+ "DESCRIPTION": "Conversations that satisfy the following conditions would not count towards agent capacity",
+ "TAGS": {
+ "LABEL": "Exclude conversations tagged with specific labels",
+ "ADD_TAG": "add tag",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select tags to add"
+ },
+ "EMPTY_STATE": "No tags added to this policy."
+ },
+ "DURATION": {
+ "LABEL": "Exclude conversations older than a specified duration",
+ "PLACEHOLDER": "Set time"
+ }
+ },
+ "USERS": {
+ "LABEL": "Assigned agents",
+ "DESCRIPTION": "Add agents for which this policy will be applicable.",
+ "ADD_BUTTON": "Add agent",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select agents to add",
+ "ADD_BUTTON": "Add"
+ },
+ "EMPTY_STATE": "No agents added",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Agent capacity policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete agent capacity policy"
+ }
+ },
+ "DELETE_POLICY": {
+ "TITLE": "Delete policy",
+ "DESCRIPTION": "Are you sure you want to delete this policy? This action cannot be undone.",
+ "CONFIRM_BUTTON_LABEL": "Padamkan",
+ "CANCEL_BUTTON_LABEL": "Batalkan"
+ }
}
}
diff --git a/app/javascript/dashboard/i18n/locale/ms/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/ms/whatsappTemplates.json
index 5f53faaa8..cf28312dc 100644
--- a/app/javascript/dashboard/i18n/locale/ms/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/ms/whatsappTemplates.json
@@ -40,6 +40,7 @@
"BUTTON_LABEL": "Button {index}",
"COUPON_CODE": "Enter coupon code (max 15 chars)",
"MEDIA_URL_LABEL": "Enter {type} URL",
+ "DOCUMENT_NAME_PLACEHOLDER": "Enter document filename (e.g., Invoice_2025.pdf)",
"BUTTON_PARAMETER": "Enter button parameter"
}
}
diff --git a/app/javascript/dashboard/i18n/locale/ne/automation.json b/app/javascript/dashboard/i18n/locale/ne/automation.json
index 4aba66e26..713ae7e82 100644
--- a/app/javascript/dashboard/i18n/locale/ne/automation.json
+++ b/app/javascript/dashboard/i18n/locale/ne/automation.json
@@ -177,7 +177,8 @@
"REFERER_LINK": "Referrer Link",
"ASSIGNEE_NAME": "Assignee",
"TEAM_NAME": "Team",
- "PRIORITY": "Priority"
+ "PRIORITY": "Priority",
+ "LABELS": "Labels"
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/ne/contact.json b/app/javascript/dashboard/i18n/locale/ne/contact.json
index ff40d2c75..793bb8ae2 100644
--- a/app/javascript/dashboard/i18n/locale/ne/contact.json
+++ b/app/javascript/dashboard/i18n/locale/ne/contact.json
@@ -610,6 +610,15 @@
"SEND_MESSAGE": "Send message"
}
},
+ "TWILIO_OPTIONS": {
+ "LABEL": "Select template",
+ "SEARCH_PLACEHOLDER": "Search templates",
+ "EMPTY_STATE": "No templates found",
+ "TEMPLATE_PARSER": {
+ "BACK": "Go back",
+ "SEND_MESSAGE": "Send message"
+ }
+ },
"ACTION_BUTTONS": {
"DISCARD": "Discard",
"SEND": "Send ({keyCode})"
diff --git a/app/javascript/dashboard/i18n/locale/ne/contactFilters.json b/app/javascript/dashboard/i18n/locale/ne/contactFilters.json
index e74d50ff0..4cc1fe59b 100644
--- a/app/javascript/dashboard/i18n/locale/ne/contactFilters.json
+++ b/app/javascript/dashboard/i18n/locale/ne/contactFilters.json
@@ -48,7 +48,8 @@
"CREATED_AT": "Created At",
"LAST_ACTIVITY": "Last Activity",
"REFERER_LINK": "Referrer link",
- "BLOCKED": "Blocked"
+ "BLOCKED": "Blocked",
+ "LABELS": "Labels"
},
"GROUPS": {
"STANDARD_FILTERS": "Standard Filters",
diff --git a/app/javascript/dashboard/i18n/locale/ne/contentTemplates.json b/app/javascript/dashboard/i18n/locale/ne/contentTemplates.json
new file mode 100644
index 000000000..a9b1d54c4
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/ne/contentTemplates.json
@@ -0,0 +1,51 @@
+{
+ "CONTENT_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Twilio Templates",
+ "SUBTITLE": "Select the Twilio template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Search Templates",
+ "NO_TEMPLATES_FOUND": "No templates found for",
+ "NO_CONTENT": "No content",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Category",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No Twilio templates available. Click refresh to sync templates from Twilio.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Language",
+ "TEMPLATE_BODY": "Template Body",
+ "CATEGORY": "Category"
+ },
+ "TYPES": {
+ "MEDIA": "Media",
+ "QUICK_REPLY": "Quick Reply",
+ "TEXT": "Text"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variables",
+ "LANGUAGE": "Language",
+ "CATEGORY": "Category",
+ "VARIABLE_PLACEHOLDER": "Enter {variable} value",
+ "GO_BACK_LABEL": "Go Back",
+ "SEND_MESSAGE_LABEL": "Send Message",
+ "FORM_ERROR_MESSAGE": "Please fill all variables before sending",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "MEDIA_URL_LABEL": "Enter full media URL",
+ "MEDIA_URL_PLACEHOLDER": "https://example.com/image.jpg"
+ },
+ "FORM": {
+ "BACK_BUTTON": "Back",
+ "SEND_MESSAGE_BUTTON": "Send Message"
+ }
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/ne/conversation.json b/app/javascript/dashboard/i18n/locale/ne/conversation.json
index fb4db1116..d4f3d5f66 100644
--- a/app/javascript/dashboard/i18n/locale/ne/conversation.json
+++ b/app/javascript/dashboard/i18n/locale/ne/conversation.json
@@ -35,6 +35,11 @@
"API_HOURS_WINDOW": "You can only reply to this conversation within {hours} hours",
"NOT_ASSIGNED_TO_YOU": "This conversation is not assigned to you. Would you like to assign this conversation to yourself?",
"ASSIGN_TO_ME": "Assign to me",
+ "BOT_HANDOFF_MESSAGE": "You are responding to a conversation which is currently handled by an assistant or a bot.",
+ "BOT_HANDOFF_ACTION": "Mark open and assign to you",
+ "BOT_HANDOFF_REOPEN_ACTION": "Mark conversation open",
+ "BOT_HANDOFF_SUCCESS": "Conversation has been handed over to you",
+ "BOT_HANDOFF_ERROR": "Failed to take over the conversation. Please try again.",
"TWILIO_WHATSAPP_CAN_REPLY": "You can only reply to this conversation using a template message due to",
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "24 hour message window restriction",
"OLD_INSTAGRAM_INBOX_REPLY_BANNER": "This Instagram account was migrated to the new Instagram channel inbox. All new messages will show up there. You won’t be able to send messages from this conversation anymore.",
@@ -66,6 +71,17 @@
"SHOW_LABELS": "Show labels",
"HIDE_LABELS": "Hide labels"
},
+ "VOICE_CALL": {
+ "INCOMING_CALL": "Incoming call",
+ "OUTGOING_CALL": "Outgoing call",
+ "CALL_IN_PROGRESS": "Call in progress",
+ "NO_ANSWER": "No answer",
+ "MISSED_CALL": "Missed call",
+ "CALL_ENDED": "Call ended",
+ "NOT_ANSWERED_YET": "Not answered yet",
+ "THEY_ANSWERED": "They answered",
+ "YOU_ANSWERED": "You answered"
+ },
"HEADER": {
"RESOLVE_ACTION": "Resolve",
"REOPEN_ACTION": "Reopen",
diff --git a/app/javascript/dashboard/i18n/locale/ne/helpCenter.json b/app/javascript/dashboard/i18n/locale/ne/helpCenter.json
index 2e8a9ba7d..760e2b6dd 100644
--- a/app/javascript/dashboard/i18n/locale/ne/helpCenter.json
+++ b/app/javascript/dashboard/i18n/locale/ne/helpCenter.json
@@ -741,7 +741,8 @@
"LIVE_CHAT_WIDGET": {
"LABEL": "Live chat widget",
"PLACEHOLDER": "Select live chat widget",
- "HELP_TEXT": "Select a live chat widget that will appear on your help center"
+ "HELP_TEXT": "Select a live chat widget that will appear on your help center",
+ "NONE_OPTION": "No widget"
},
"BRAND_COLOR": {
"LABEL": "Brand color"
@@ -807,6 +808,58 @@
"ERROR_MESSAGE": "Unable to update portal"
}
}
+ },
+ "PDF_UPLOAD": {
+ "TITLE": "Upload PDF Document",
+ "DESCRIPTION": "Upload a PDF document to automatically generate FAQs using AI",
+ "DRAG_DROP_TEXT": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "ADDITIONAL_CONTEXT_LABEL": "Additional Context (Optional)",
+ "ADDITIONAL_CONTEXT_PLACEHOLDER": "Provide any additional context or instructions for FAQ generation...",
+ "UPLOADING": "अपलोड गर्दै...",
+ "UPLOAD": "Upload & Process",
+ "CANCEL": "Cancel",
+ "ERROR_INVALID_TYPE": "Please select a valid PDF file",
+ "ERROR_FILE_TOO_LARGE": "File size must be less than 512MB",
+ "ERROR_UPLOAD_FAILED": "Failed to upload PDF. Please try again."
+ },
+ "PDF_DOCUMENTS": {
+ "TITLE": "PDF Documents",
+ "DESCRIPTION": "Manage uploaded PDF documents and generate FAQs from them",
+ "UPLOAD_PDF": "Upload PDF",
+ "UPLOAD_FIRST_PDF": "Upload your first PDF",
+ "UPLOADED_BY": "Uploaded by",
+ "GENERATE_FAQS": "Generate FAQs",
+ "GENERATING": "Generating...",
+ "CONFIRM_DELETE": "Are you sure you want to delete {filename}?",
+ "EMPTY_STATE": {
+ "TITLE": "No PDF documents yet",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQs using AI"
+ },
+ "STATUS": {
+ "UPLOADED": "Ready",
+ "PROCESSING": "Processing",
+ "PROCESSED": "Completed",
+ "FAILED": "Failed"
+ }
+ },
+ "CONTENT_GENERATION": {
+ "TITLE": "Content Generation",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQ content using AI",
+ "UPLOAD_TITLE": "Upload PDF Document",
+ "DRAG_DROP": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "UPLOADING": "Processing document...",
+ "UPLOAD_SUCCESS": "Document processed successfully!",
+ "UPLOAD_ERROR": "Failed to upload document. Please try again.",
+ "INVALID_FILE_TYPE": "Please select a valid PDF file",
+ "FILE_TOO_LARGE": "File size must be less than 512MB",
+ "GENERATED_CONTENT": "Generated FAQ Content",
+ "PUBLISH_SELECTED": "Publish Selected",
+ "PUBLISHING": "Publishing...",
+ "FROM_DOCUMENT": "From document",
+ "NO_CONTENT": "No generated content available. Upload a PDF document to get started.",
+ "LOADING": "Loading generated content..."
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/ne/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/ne/inboxMgmt.json
index c8d3e72a0..eb46ff259 100644
--- a/app/javascript/dashboard/i18n/locale/ne/inboxMgmt.json
+++ b/app/javascript/dashboard/i18n/locale/ne/inboxMgmt.json
@@ -272,8 +272,8 @@
},
"SUBMIT_BUTTON": "Create WhatsApp Channel",
"EMBEDDED_SIGNUP": {
- "TITLE": "Quick Setup with Meta",
- "DESC": "You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
+ "TITLE": "Quick setup with Meta",
+ "DESC": "Use the WhatsApp Embedded Signup flow to quickly connect new numbers. You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
"BENEFITS": {
"TITLE": "Benefits of Embedded Signup:",
"EASY_SETUP": "No manual configuration required",
@@ -281,9 +281,8 @@
"AUTO_CONFIG": "Automatic webhook and phone number configuration"
},
"LEARN_MORE": {
- "TEXT": "To learn more about integrated signup, pricing, and limitations, visit",
- "LINK_TEXT": "this link.",
- "LINK_URL": "https://developers.facebook.com/docs/whatsapp/embedded-signup/custom-flows/onboarding-business-app-users#limitations"
+ "TEXT": "To learn more about integrated signup, pricing, and limitations, visit {link}.",
+ "LINK_TEXT": "this link"
},
"SUBMIT_BUTTON": "Connect with WhatsApp Business",
"AUTH_PROCESSING": "Authenticating with Meta",
@@ -296,7 +295,9 @@
"INVALID_BUSINESS_DATA": "Invalid business data received from Facebook. Please try again.",
"SIGNUP_ERROR": "Signup error occurred",
"AUTH_NOT_COMPLETED": "Authentication not completed. Please restart the process.",
- "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured"
+ "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured",
+ "MANUAL_FALLBACK": "If your number is already connected to the WhatsApp Business Platform (API), or if you’re a tech provider onboarding your own number, please use the {link} flow",
+ "MANUAL_LINK_TEXT": "manual setup flow"
},
"API": {
"ERROR_MESSAGE": "We were not able to save the WhatsApp channel"
@@ -424,7 +425,51 @@
},
"AUTH": {
"TITLE": "Choose a channel",
- "DESC": "Chatwoot supports live-chat widgets, Facebook Messenger, Twitter profiles, WhatsApp, Emails, etc., as channels. If you want to build a custom channel, you can create it using the API channel. To get started, choose one of the channels below."
+ "DESC": "Chatwoot supports live-chat widgets, Facebook Messenger, Twitter profiles, WhatsApp, Emails, etc., as channels. If you want to build a custom channel, you can create it using the API channel. To get started, choose one of the channels below.",
+ "TITLE_NEXT": "Complete the setup",
+ "TITLE_FINISH": "Voilà!",
+ "CHANNEL": {
+ "WEBSITE": {
+ "TITLE": "Website",
+ "DESCRIPTION": "Create a live-chat widget"
+ },
+ "FACEBOOK": {
+ "TITLE": "Facebook",
+ "DESCRIPTION": "Connect your Facebook page"
+ },
+ "WHATSAPP": {
+ "TITLE": "WhatsApp",
+ "DESCRIPTION": "Support your customers on WhatsApp"
+ },
+ "EMAIL": {
+ "TITLE": "Email",
+ "DESCRIPTION": "Connect with Gmail, Outlook, or other providers"
+ },
+ "SMS": {
+ "TITLE": "SMS",
+ "DESCRIPTION": "Integrate SMS channel with Twilio or bandwidth"
+ },
+ "API": {
+ "TITLE": "API",
+ "DESCRIPTION": "Make a custom channel using our API"
+ },
+ "TELEGRAM": {
+ "TITLE": "Telegram",
+ "DESCRIPTION": "Configure Telegram channel using Bot token"
+ },
+ "LINE": {
+ "TITLE": "Line",
+ "DESCRIPTION": "Integrate your Line channel"
+ },
+ "INSTAGRAM": {
+ "TITLE": "Instagram",
+ "DESCRIPTION": "Connect your instagram account"
+ },
+ "VOICE": {
+ "TITLE": "Voice",
+ "DESCRIPTION": "Integrate with Twilio Voice"
+ }
+ }
},
"AGENTS": {
"TITLE": "Agents",
@@ -478,7 +523,10 @@
"MESSAGE": "You can now engage with your customers through your new Channel. Happy supporting",
"BUTTON_TEXT": "Take me there",
"MORE_SETTINGS": "More settings",
- "WEBSITE_SUCCESS": "You have successfully finished creating a website channel. Copy the code shown below and paste it on your website. Next time a customer use the live chat, the conversation will automatically appear on your inbox."
+ "WEBSITE_SUCCESS": "You have successfully finished creating a website channel. Copy the code shown below and paste it on your website. Next time a customer use the live chat, the conversation will automatically appear on your inbox.",
+ "WHATSAPP_QR_INSTRUCTION": "Scan the QR code above to quickly test your WhatsApp inbox",
+ "MESSENGER_QR_INSTRUCTION": "Scan the QR code above to quickly test your Facebook Messenger inbox",
+ "TELEGRAM_QR_INSTRUCTION": "Scan the QR code above to quickly test your Telegram inbox"
},
"REAUTH": "Reauthorize",
"VIEW": "View",
@@ -868,9 +916,18 @@
"SCRIPT_SETTINGS": "\n window.chatwootSettings = {options};"
},
"EMAIL_PROVIDERS": {
- "MICROSOFT": "Microsoft",
- "GOOGLE": "Google",
- "OTHER_PROVIDERS": "Other Providers"
+ "MICROSOFT": {
+ "TITLE": "Microsoft",
+ "DESCRIPTION": "Connect with Microsoft"
+ },
+ "GOOGLE": {
+ "TITLE": "Google",
+ "DESCRIPTION": "Connect with Google"
+ },
+ "OTHER_PROVIDERS": {
+ "TITLE": "Other Providers",
+ "DESCRIPTION": "Connect with Other Providers"
+ }
},
"CHANNELS": {
"MESSENGER": "Messenger",
diff --git a/app/javascript/dashboard/i18n/locale/ne/integrations.json b/app/javascript/dashboard/i18n/locale/ne/integrations.json
index 56a6766fc..cfdd76b7c 100644
--- a/app/javascript/dashboard/i18n/locale/ne/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/ne/integrations.json
@@ -704,11 +704,28 @@
"ERROR_MESSAGE": "There was an error creating the document, please try again."
},
"FORM": {
+ "TYPE": {
+ "LABEL": "Document Type",
+ "URL": "URL",
+ "PDF": "PDF File"
+ },
"URL": {
"LABEL": "URL",
"PLACEHOLDER": "Enter the URL of the document",
"ERROR": "Please provide a valid URL for the document"
},
+ "PDF_FILE": {
+ "LABEL": "PDF File",
+ "CHOOSE_FILE": "Choose PDF file",
+ "ERROR": "Please select a PDF file",
+ "HELP_TEXT": "Maximum file size: 10MB",
+ "INVALID_TYPE": "Please select a valid PDF file",
+ "TOO_LARGE": "File size exceeds 10MB limit"
+ },
+ "NAME": {
+ "LABEL": "Document Name (Optional)",
+ "PLACEHOLDER": "Enter a name for the document"
+ },
"ASSISTANT": {
"LABEL": "Assistant",
"PLACEHOLDER": "Select the assistant",
@@ -744,6 +761,7 @@
"SELECTED": "{count} selected",
"SELECT_ALL": "Select all ({count})",
"UNSELECT_ALL": "Unselect all ({count})",
+ "SEARCH_PLACEHOLDER": "Search FAQs...",
"BULK_APPROVE_BUTTON": "Approve",
"BULK_DELETE_BUTTON": "Delete",
"BULK_APPROVE": {
diff --git a/app/javascript/dashboard/i18n/locale/ne/mfa.json b/app/javascript/dashboard/i18n/locale/ne/mfa.json
new file mode 100644
index 000000000..9cef374f8
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/ne/mfa.json
@@ -0,0 +1,106 @@
+{
+ "MFA_SETTINGS": {
+ "TITLE": "Two-Factor Authentication",
+ "SUBTITLE": "Secure your account with TOTP-based authentication",
+ "DESCRIPTION": "Add an extra layer of security to your account using a time-based one-time password (TOTP)",
+ "STATUS_TITLE": "Authentication Status",
+ "STATUS_DESCRIPTION": "Manage your two-factor authentication settings and backup recovery codes",
+ "ENABLED": "Enabled",
+ "DISABLED": "Disabled",
+ "STATUS_ENABLED": "Two-factor authentication is active",
+ "STATUS_ENABLED_DESC": "Your account is protected with an additional layer of security",
+ "ENABLE_BUTTON": "Enable Two-Factor Authentication",
+ "ENHANCE_SECURITY": "Enhance Your Account Security",
+ "ENHANCE_SECURITY_DESC": "Two-factor authentication adds an extra layer of security by requiring a verification code from your authenticator app in addition to your password.",
+ "SETUP": {
+ "STEP_NUMBER_1": "1",
+ "STEP_NUMBER_2": "2",
+ "STEP1_TITLE": "Scan QR Code with Your Authenticator App",
+ "STEP1_DESCRIPTION": "Use Google Authenticator, Authy, or any TOTP-compatible app",
+ "LOADING_QR": "Loading...",
+ "MANUAL_ENTRY": "Can't scan? Enter code manually",
+ "SECRET_KEY": "Secret Key",
+ "COPY": "Copy",
+ "ENTER_CODE": "Enter the 6-digit code from your authenticator app",
+ "ENTER_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify & Continue",
+ "CANCEL": "Cancel",
+ "ERROR_STARTING": "MFA not enabled. Please contact administrator.",
+ "INVALID_CODE": "Invalid verification code",
+ "SECRET_COPIED": "Secret key copied to clipboard",
+ "SUCCESS": "Two-factor authentication has been enabled successfully"
+ },
+ "BACKUP": {
+ "TITLE": "Save Your Backup Codes",
+ "DESCRIPTION": "Keep these codes safe. Each can be used once if you lose access to your authenticator",
+ "IMPORTANT": "Important:",
+ "IMPORTANT_NOTE": " Save these codes in a secure location. You won't be able to see them again.",
+ "DOWNLOAD": "डाउनलोड",
+ "COPY_ALL": "Copy All",
+ "CONFIRM": "I have saved my backup codes in a secure location and understand that I won't be able to see them again",
+ "COMPLETE_SETUP": "Complete Setup",
+ "CODES_COPIED": "Backup codes copied to clipboard"
+ },
+ "MANAGEMENT": {
+ "BACKUP_CODES": "Backup Codes",
+ "BACKUP_CODES_DESC": "Generate new codes if you've lost or used your existing ones",
+ "REGENERATE": "Regenerate Backup Codes",
+ "DISABLE_MFA": "Disable 2FA",
+ "DISABLE_MFA_DESC": "Remove two-factor authentication from your account",
+ "DISABLE_BUTTON": "Disable Two-Factor Authentication"
+ },
+ "DISABLE": {
+ "TITLE": "Disable Two-Factor Authentication",
+ "DESCRIPTION": "You'll need to enter your password and a verification code to disable two-factor authentication.",
+ "PASSWORD": "Password",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Disable 2FA",
+ "CANCEL": "Cancel",
+ "SUCCESS": "Two-factor authentication has been disabled",
+ "ERROR": "Failed to disable MFA. Please check your credentials."
+ },
+ "REGENERATE": {
+ "TITLE": "Regenerate Backup Codes",
+ "DESCRIPTION": "This will invalidate your existing backup codes and generate new ones. Enter your verification code to continue.",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Generate New Codes",
+ "CANCEL": "Cancel",
+ "NEW_CODES_TITLE": "New Backup Codes Generated",
+ "NEW_CODES_DESC": "Your old backup codes have been invalidated. Save these new codes in a secure location.",
+ "CODES_IMPORTANT": "Important:",
+ "CODES_IMPORTANT_NOTE": " Each code can only be used once. Save them before closing this window.",
+ "DOWNLOAD_CODES": "Download Codes",
+ "COPY_ALL_CODES": "Copy All Codes",
+ "CODES_SAVED": "I've Saved My Codes",
+ "SUCCESS": "New backup codes have been generated",
+ "ERROR": "Failed to regenerate backup codes"
+ }
+ },
+ "MFA_VERIFICATION": {
+ "TITLE": "Two-Factor Authentication",
+ "DESCRIPTION": "Enter your verification code to continue",
+ "AUTHENTICATOR_APP": "Authenticator App",
+ "BACKUP_CODE": "Backup Code",
+ "ENTER_OTP_CODE": "Enter 6-digit code from your authenticator app",
+ "ENTER_BACKUP_CODE": "Enter one of your backup codes",
+ "BACKUP_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify",
+ "TRY_ANOTHER_METHOD": "Try another verification method",
+ "CANCEL_LOGIN": "Cancel and return to login",
+ "HELP_TEXT": "Having trouble signing in?",
+ "LEARN_MORE": "Learn more about 2FA",
+ "HELP_MODAL": {
+ "TITLE": "Two-Factor Authentication Help",
+ "AUTHENTICATOR_TITLE": "Using an Authenticator App",
+ "AUTHENTICATOR_DESC": "Open your authenticator app (Google Authenticator, Authy, etc.) and enter the 6-digit code shown for your account.",
+ "BACKUP_TITLE": "Using a Backup Code",
+ "BACKUP_DESC": "If you don't have access to your authenticator app, you can use one of the backup codes you saved when setting up 2FA. Each code can only be used once.",
+ "CONTACT_TITLE": "Need More Help?",
+ "CONTACT_DESC_CLOUD": "If you've lost access to both your authenticator app and backup codes, please reach out to Chatwoot support for assistance.",
+ "CONTACT_DESC_SELF_HOSTED": "If you've lost access to both your authenticator app and backup codes, please contact your administrator for assistance."
+ },
+ "VERIFICATION_FAILED": "Verification failed. Please try again."
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/ne/settings.json b/app/javascript/dashboard/i18n/locale/ne/settings.json
index 22ea8edd8..5779c540b 100644
--- a/app/javascript/dashboard/i18n/locale/ne/settings.json
+++ b/app/javascript/dashboard/i18n/locale/ne/settings.json
@@ -51,6 +51,13 @@
"LARGER": "Larger",
"EXTRA_LARGE": "Extra Large"
}
+ },
+ "LANGUAGE": {
+ "TITLE": "Preferred Language",
+ "NOTE": "Choose the language you want to use.",
+ "UPDATE_SUCCESS": "Your Language settings have been updated successfully",
+ "UPDATE_ERROR": "There is an error while updating the language settings, please try again",
+ "USE_ACCOUNT_DEFAULT": "Use account default"
}
},
"MESSAGE_SIGNATURE_SECTION": {
@@ -73,6 +80,11 @@
"NOTE": "Updating your password would reset your logins in multiple devices.",
"BTN_TEXT": "Change password"
},
+ "SECURITY_SECTION": {
+ "TITLE": "Security",
+ "NOTE": "Manage additional security features for your account.",
+ "MFA_BUTTON": "Manage Two-Factor Authentication"
+ },
"ACCESS_TOKEN": {
"TITLE": "Access Token",
"NOTE": "This token can be used if you are building an API based integration",
@@ -330,6 +342,7 @@
"REPORTS_LABEL": "Labels",
"REPORTS_INBOX": "Inbox",
"REPORTS_TEAM": "Team",
+ "AGENT_ASSIGNMENT": "Agent Assignment",
"SET_AVAILABILITY_TITLE": "Set yourself as",
"SET_YOUR_AVAILABILITY": "Set your availability",
"SLA": "SLA",
@@ -350,7 +363,8 @@
"INFO_TEXT": "Let the system automatically mark you offline when you aren't using the app or dashboard.",
"INFO_SHORT": "Automatically mark offline when you aren't using the app."
},
- "DOCS": "Read docs"
+ "DOCS": "Read docs",
+ "SECURITY": "Security"
},
"BILLING_SETTINGS": {
"TITLE": "Billing",
@@ -382,6 +396,77 @@
},
"NO_BILLING_USER": "Your billing account is being configured. Please refresh the page and try again."
},
+ "SECURITY_SETTINGS": {
+ "TITLE": "Security",
+ "DESCRIPTION": "Manage your account security settings.",
+ "LINK_TEXT": "Learn more about SAML SSO",
+ "SAML": {
+ "TITLE": "SAML SSO",
+ "NOTE": "Configure SAML single sign-on for your account. Users will authenticate through your identity provider instead of using email/password.",
+ "ACS_URL": {
+ "LABEL": "ACS URL",
+ "TOOLTIP": "Assertion Consumer Service URL - Configure this URL in your IdP as the destination for SAML responses"
+ },
+ "SSO_URL": {
+ "LABEL": "SSO URL",
+ "HELP": "The URL where SAML authentication requests will be sent",
+ "PLACEHOLDER": "https://your-idp.com/saml/sso"
+ },
+ "CERTIFICATE": {
+ "LABEL": "Signing certificate in PEM format",
+ "HELP": "The public certificate from your identity provider used to verify SAML responses",
+ "PLACEHOLDER": "-----BEGIN CERTIFICATE-----\nMIIC..."
+ },
+ "FINGERPRINT": {
+ "LABEL": "Fingerprint",
+ "TOOLTIP": "SHA-1 fingerprint of the certificate - Use this to verify the certificate in your IdP configuration"
+ },
+ "COPY_SUCCESS": "Code copied to clipboard successfully",
+ "SP_ENTITY_ID": {
+ "LABEL": "SP Entity ID",
+ "HELP": "Unique identifier for this application as a service provider (auto-generated).",
+ "TOOLTIP": "Unique identifier for Chatwoot as the Service Provider - Configure this in your IdP settings"
+ },
+ "IDP_ENTITY_ID": {
+ "LABEL": "Identity Provider Entity ID",
+ "HELP": "Unique identifier for your identity provider (usually found in IdP configuration)",
+ "PLACEHOLDER": "https://your-idp.com/saml"
+ },
+ "UPDATE_BUTTON": "Update SAML Settings",
+ "API": {
+ "SUCCESS": "SAML settings updated successfully",
+ "ERROR": "Failed to update SAML settings",
+ "ERROR_LOADING": "Failed to load SAML settings",
+ "DISABLED": "SAML settings disabled successfully"
+ },
+ "VALIDATION": {
+ "REQUIRED_FIELDS": "SSO URL, Identity Provider Entity ID, and Certificate are required fields",
+ "SSO_URL_ERROR": "Please enter a valid SSO URL",
+ "CERTIFICATE_ERROR": "Certificate is required",
+ "IDP_ENTITY_ID_ERROR": "Identity Provider Entity ID is required"
+ },
+ "ENTERPRISE_PAYWALL": {
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade to an Enterprise plan to access SAML single sign-on and other advanced security features.",
+ "ASK_ADMIN": "Please reach out to your administrator for the upgrade."
+ },
+ "PAYWALL": {
+ "TITLE": "Upgrade to enable SAML SSO",
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade your plan to get access to SAML single sign-on and other advanced features.",
+ "UPGRADE_NOW": "Upgrade now",
+ "CANCEL_ANYTIME": "You can change or cancel your plan anytime"
+ },
+ "ATTRIBUTE_MAPPING": {
+ "TITLE": "SAML Attribute Setup",
+ "DESCRIPTION": "The following attribute mappings must be configured in your identity provider"
+ },
+ "INFO_SECTION": {
+ "TITLE": "Service Provider Information",
+ "TOOLTIP": "Copy these values and configure them in your Identity Provider to establish the SAML connection"
+ }
+ }
+ },
"CREATE_ACCOUNT": {
"NO_ACCOUNT_WARNING": "Uh oh! We could not find any Chatwoot accounts. Please create a new account to continue.",
"NEW_ACCOUNT": "New Account",
@@ -418,5 +503,255 @@
"SWITCH_TO_REPLY": "Switch to Reply",
"TOGGLE_SNOOZE_DROPDOWN": "Toggle snooze dropdown"
}
+ },
+ "ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent assignment",
+ "DESCRIPTION": "Define policies to effectively manage workload and route conversations based on the needs of inboxes and agents. Learn more here"
+ },
+ "ASSIGNMENT_POLICY": {
+ "TITLE": "Assignment policy",
+ "DESCRIPTION": "Manage how conversations get assigned in inboxes.",
+ "FEATURES": [
+ "Assign by conversations evenly or by available capacity",
+ "Add fair distribution rules to avoid overloading any agent",
+ "Add inboxes to a policy - one policy per inbox"
+ ]
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "TITLE": "Agent capacity policy",
+ "DESCRIPTION": "Manage workload for agents.",
+ "FEATURES": [
+ "Define maximum conversations per inbox",
+ "Create exceptions based on labels and time",
+ "Add agents to a policy - one policy per agent"
+ ]
+ }
+ },
+ "AGENT_ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Assignment policy",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "ORDER": "Order",
+ "PRIORITY": "Priority",
+ "ACTIVE": "Active",
+ "INACTIVE": "Inactive",
+ "POPOVER": "Added inboxes",
+ "EDIT": "Edit"
+ },
+ "NO_RECORDS_FOUND": "No assignment policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create assignment policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy created successfully",
+ "ERROR_MESSAGE": "Failed to create assignment policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit assignment policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_INBOX_DIALOG": {
+ "TITLE": "Add inbox",
+ "DESCRIPTION": "{inboxName} inbox is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Cancel"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update assignment policy"
+ },
+ "INBOX_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Inbox added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Inbox removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove inbox from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Description:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "STATUS": {
+ "LABEL": "Status:",
+ "PLACEHOLDER": "Select status",
+ "ACTIVE": "Policy is active",
+ "INACTIVE": "Policy is inactive"
+ },
+ "ASSIGNMENT_ORDER": {
+ "LABEL": "Assignment order",
+ "ROUND_ROBIN": {
+ "LABEL": "Round robin",
+ "DESCRIPTION": "Assign conversations evenly among agents."
+ },
+ "BALANCED": {
+ "LABEL": "Balanced",
+ "DESCRIPTION": "Assign conversations based on available capacity."
+ }
+ },
+ "ASSIGNMENT_PRIORITY": {
+ "LABEL": "Assignment priority",
+ "EARLIEST_CREATED": {
+ "LABEL": "Earliest created",
+ "DESCRIPTION": "The conversation that was created first gets assigned first."
+ },
+ "LONGEST_WAITING": {
+ "LABEL": "Longest waiting",
+ "DESCRIPTION": "The conversation waiting the longest gets assigned first."
+ }
+ },
+ "FAIR_DISTRIBUTION": {
+ "LABEL": "Fair distribution policy",
+ "DESCRIPTION": "Set the maximum number of conversations that can be assigned per agent within a time window to avoid overloading any one agent. This required field defaults to 100 conversations per hour.",
+ "INPUT_MAX": "Assign max",
+ "DURATION": "Conversations per agent in every"
+ },
+ "INBOXES": {
+ "LABEL": "Added inboxes",
+ "DESCRIPTION": "Add inboxes for which this policy will be applicable.",
+ "ADD_BUTTON": "Add inbox",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select inboxes to add",
+ "ADD_BUTTON": "Add"
+ },
+ "EMPTY_STATE": "No inboxes added to this policy, add an inbox to get started",
+ "API": {
+ "SUCCESS_MESSAGE": "Inbox successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Assignment policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete assignment policy"
+ }
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent capacity",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "POPOVER": "Added agents",
+ "EDIT": "Edit"
+ },
+ "NO_RECORDS_FOUND": "No agent capacity policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create agent capacity policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy created successfully",
+ "ERROR_MESSAGE": "Failed to create agent capacity policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit agent capacity policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_AGENT_DIALOG": {
+ "TITLE": "Add agent",
+ "DESCRIPTION": "{agentName} is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Cancel"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update agent capacity policy"
+ },
+ "AGENT_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Agent added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Agent removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove agent from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Description:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "INBOX_CAPACITY_LIMIT": {
+ "LABEL": "Inbox capacity limits",
+ "ADD_BUTTON": "Add inbox",
+ "FIELD": {
+ "SELECT_INBOX": "Select inbox",
+ "MAX_CONVERSATIONS": "Max conversations",
+ "SET_LIMIT": "Set limit"
+ },
+ "EMPTY_STATE": "No inbox limit set"
+ },
+ "EXCLUSION_RULES": {
+ "LABEL": "Exclusion rules",
+ "DESCRIPTION": "Conversations that satisfy the following conditions would not count towards agent capacity",
+ "TAGS": {
+ "LABEL": "Exclude conversations tagged with specific labels",
+ "ADD_TAG": "add tag",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select tags to add"
+ },
+ "EMPTY_STATE": "No tags added to this policy."
+ },
+ "DURATION": {
+ "LABEL": "Exclude conversations older than a specified duration",
+ "PLACEHOLDER": "Set time"
+ }
+ },
+ "USERS": {
+ "LABEL": "Assigned agents",
+ "DESCRIPTION": "Add agents for which this policy will be applicable.",
+ "ADD_BUTTON": "Add agent",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select agents to add",
+ "ADD_BUTTON": "Add"
+ },
+ "EMPTY_STATE": "No agents added",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Agent capacity policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete agent capacity policy"
+ }
+ },
+ "DELETE_POLICY": {
+ "TITLE": "Delete policy",
+ "DESCRIPTION": "Are you sure you want to delete this policy? This action cannot be undone.",
+ "CONFIRM_BUTTON_LABEL": "Delete",
+ "CANCEL_BUTTON_LABEL": "Cancel"
+ }
}
}
diff --git a/app/javascript/dashboard/i18n/locale/ne/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/ne/whatsappTemplates.json
index 5f53faaa8..cf28312dc 100644
--- a/app/javascript/dashboard/i18n/locale/ne/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/ne/whatsappTemplates.json
@@ -40,6 +40,7 @@
"BUTTON_LABEL": "Button {index}",
"COUPON_CODE": "Enter coupon code (max 15 chars)",
"MEDIA_URL_LABEL": "Enter {type} URL",
+ "DOCUMENT_NAME_PLACEHOLDER": "Enter document filename (e.g., Invoice_2025.pdf)",
"BUTTON_PARAMETER": "Enter button parameter"
}
}
diff --git a/app/javascript/dashboard/i18n/locale/nl/automation.json b/app/javascript/dashboard/i18n/locale/nl/automation.json
index dd45ce36d..fdef5df6f 100644
--- a/app/javascript/dashboard/i18n/locale/nl/automation.json
+++ b/app/javascript/dashboard/i18n/locale/nl/automation.json
@@ -177,7 +177,8 @@
"REFERER_LINK": "Referrer Link",
"ASSIGNEE_NAME": "Assignee",
"TEAM_NAME": "Team",
- "PRIORITY": "Prioriteit"
+ "PRIORITY": "Prioriteit",
+ "LABELS": "Labelen"
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/nl/contact.json b/app/javascript/dashboard/i18n/locale/nl/contact.json
index fe172b93d..6d8c8336a 100644
--- a/app/javascript/dashboard/i18n/locale/nl/contact.json
+++ b/app/javascript/dashboard/i18n/locale/nl/contact.json
@@ -610,6 +610,15 @@
"SEND_MESSAGE": "Verstuur bericht"
}
},
+ "TWILIO_OPTIONS": {
+ "LABEL": "Select template",
+ "SEARCH_PLACEHOLDER": "Search templates",
+ "EMPTY_STATE": "No templates found",
+ "TEMPLATE_PARSER": {
+ "BACK": "Ga terug",
+ "SEND_MESSAGE": "Verstuur bericht"
+ }
+ },
"ACTION_BUTTONS": {
"DISCARD": "Discard",
"SEND": "Send ({keyCode})"
diff --git a/app/javascript/dashboard/i18n/locale/nl/contactFilters.json b/app/javascript/dashboard/i18n/locale/nl/contactFilters.json
index 8c677edf1..e3666f260 100644
--- a/app/javascript/dashboard/i18n/locale/nl/contactFilters.json
+++ b/app/javascript/dashboard/i18n/locale/nl/contactFilters.json
@@ -48,7 +48,8 @@
"CREATED_AT": "Aangemaakt op",
"LAST_ACTIVITY": "Laatste Activiteit",
"REFERER_LINK": "Verwijzer link",
- "BLOCKED": "Blocked"
+ "BLOCKED": "Blocked",
+ "LABELS": "Labelen"
},
"GROUPS": {
"STANDARD_FILTERS": "Standaard Filters",
diff --git a/app/javascript/dashboard/i18n/locale/nl/contentTemplates.json b/app/javascript/dashboard/i18n/locale/nl/contentTemplates.json
new file mode 100644
index 000000000..95c2b64ee
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/nl/contentTemplates.json
@@ -0,0 +1,51 @@
+{
+ "CONTENT_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Twilio Templates",
+ "SUBTITLE": "Select the Twilio template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Templates zoeken",
+ "NO_TEMPLATES_FOUND": "Geen templates gevonden voor",
+ "NO_CONTENT": "No content",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Categorie",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No Twilio templates available. Click refresh to sync templates from Twilio.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Taal",
+ "TEMPLATE_BODY": "Template bericht",
+ "CATEGORY": "Categorie"
+ },
+ "TYPES": {
+ "MEDIA": "Media",
+ "QUICK_REPLY": "Quick Reply",
+ "TEXT": "Tekst"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variabelen",
+ "LANGUAGE": "Taal",
+ "CATEGORY": "Categorie",
+ "VARIABLE_PLACEHOLDER": "Voer {variable} waarde in",
+ "GO_BACK_LABEL": "Ga terug",
+ "SEND_MESSAGE_LABEL": "Verstuur bericht",
+ "FORM_ERROR_MESSAGE": "Vul alstublieft alle variabelen in voordat u deze verzendt",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "MEDIA_URL_LABEL": "Enter full media URL",
+ "MEDIA_URL_PLACEHOLDER": "https://example.com/image.jpg"
+ },
+ "FORM": {
+ "BACK_BUTTON": "Terug",
+ "SEND_MESSAGE_BUTTON": "Verstuur bericht"
+ }
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/nl/conversation.json b/app/javascript/dashboard/i18n/locale/nl/conversation.json
index 42d40749a..aee364321 100644
--- a/app/javascript/dashboard/i18n/locale/nl/conversation.json
+++ b/app/javascript/dashboard/i18n/locale/nl/conversation.json
@@ -35,6 +35,11 @@
"API_HOURS_WINDOW": "You can only reply to this conversation within {hours} hours",
"NOT_ASSIGNED_TO_YOU": "Dit gesprek is niet aan je toegewezen. Wil je dit gesprek aan jezelf toewijzen?",
"ASSIGN_TO_ME": "Aan mij toewijzen",
+ "BOT_HANDOFF_MESSAGE": "You are responding to a conversation which is currently handled by an assistant or a bot.",
+ "BOT_HANDOFF_ACTION": "Mark open and assign to you",
+ "BOT_HANDOFF_REOPEN_ACTION": "Mark conversation open",
+ "BOT_HANDOFF_SUCCESS": "Conversation has been handed over to you",
+ "BOT_HANDOFF_ERROR": "Failed to take over the conversation. Please try again.",
"TWILIO_WHATSAPP_CAN_REPLY": "Je kunt dit gesprek alleen beantwoorden met een sjabloon bericht vanwege",
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "Beperking van 24-uur berichtenvenster",
"OLD_INSTAGRAM_INBOX_REPLY_BANNER": "This Instagram account was migrated to the new Instagram channel inbox. All new messages will show up there. You won’t be able to send messages from this conversation anymore.",
@@ -66,6 +71,17 @@
"SHOW_LABELS": "Labels weergeven",
"HIDE_LABELS": "Labels verbergen"
},
+ "VOICE_CALL": {
+ "INCOMING_CALL": "Incoming call",
+ "OUTGOING_CALL": "Outgoing call",
+ "CALL_IN_PROGRESS": "Call in progress",
+ "NO_ANSWER": "No answer",
+ "MISSED_CALL": "Missed call",
+ "CALL_ENDED": "Call ended",
+ "NOT_ANSWERED_YET": "Not answered yet",
+ "THEY_ANSWERED": "They answered",
+ "YOU_ANSWERED": "You answered"
+ },
"HEADER": {
"RESOLVE_ACTION": "Oplossen",
"REOPEN_ACTION": "Heropenen",
diff --git a/app/javascript/dashboard/i18n/locale/nl/helpCenter.json b/app/javascript/dashboard/i18n/locale/nl/helpCenter.json
index 22b503b2b..9215963f8 100644
--- a/app/javascript/dashboard/i18n/locale/nl/helpCenter.json
+++ b/app/javascript/dashboard/i18n/locale/nl/helpCenter.json
@@ -741,7 +741,8 @@
"LIVE_CHAT_WIDGET": {
"LABEL": "Live chat widget",
"PLACEHOLDER": "Select live chat widget",
- "HELP_TEXT": "Select a live chat widget that will appear on your help center"
+ "HELP_TEXT": "Select a live chat widget that will appear on your help center",
+ "NONE_OPTION": "No widget"
},
"BRAND_COLOR": {
"LABEL": "Brand color"
@@ -807,6 +808,58 @@
"ERROR_MESSAGE": "Unable to update portal"
}
}
+ },
+ "PDF_UPLOAD": {
+ "TITLE": "Upload PDF Document",
+ "DESCRIPTION": "Upload a PDF document to automatically generate FAQs using AI",
+ "DRAG_DROP_TEXT": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "ADDITIONAL_CONTEXT_LABEL": "Additional Context (Optional)",
+ "ADDITIONAL_CONTEXT_PLACEHOLDER": "Provide any additional context or instructions for FAQ generation...",
+ "UPLOADING": "Uploaden...",
+ "UPLOAD": "Upload & Process",
+ "CANCEL": "Annuleren",
+ "ERROR_INVALID_TYPE": "Please select a valid PDF file",
+ "ERROR_FILE_TOO_LARGE": "File size must be less than 512MB",
+ "ERROR_UPLOAD_FAILED": "Failed to upload PDF. Please try again."
+ },
+ "PDF_DOCUMENTS": {
+ "TITLE": "PDF Documents",
+ "DESCRIPTION": "Manage uploaded PDF documents and generate FAQs from them",
+ "UPLOAD_PDF": "Upload PDF",
+ "UPLOAD_FIRST_PDF": "Upload your first PDF",
+ "UPLOADED_BY": "Uploaded by",
+ "GENERATE_FAQS": "Generate FAQs",
+ "GENERATING": "Genereren...",
+ "CONFIRM_DELETE": "Are you sure you want to delete {filename}?",
+ "EMPTY_STATE": {
+ "TITLE": "No PDF documents yet",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQs using AI"
+ },
+ "STATUS": {
+ "UPLOADED": "Ready",
+ "PROCESSING": "Processing",
+ "PROCESSED": "Completed",
+ "FAILED": "Failed"
+ }
+ },
+ "CONTENT_GENERATION": {
+ "TITLE": "Content Generation",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQ content using AI",
+ "UPLOAD_TITLE": "Upload PDF Document",
+ "DRAG_DROP": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "UPLOADING": "Processing document...",
+ "UPLOAD_SUCCESS": "Document processed successfully!",
+ "UPLOAD_ERROR": "Failed to upload document. Please try again.",
+ "INVALID_FILE_TYPE": "Please select a valid PDF file",
+ "FILE_TOO_LARGE": "File size must be less than 512MB",
+ "GENERATED_CONTENT": "Generated FAQ Content",
+ "PUBLISH_SELECTED": "Publish Selected",
+ "PUBLISHING": "Publishing...",
+ "FROM_DOCUMENT": "From document",
+ "NO_CONTENT": "No generated content available. Upload a PDF document to get started.",
+ "LOADING": "Loading generated content..."
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/nl/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/nl/inboxMgmt.json
index dd7433613..e93ea9149 100644
--- a/app/javascript/dashboard/i18n/locale/nl/inboxMgmt.json
+++ b/app/javascript/dashboard/i18n/locale/nl/inboxMgmt.json
@@ -272,8 +272,8 @@
},
"SUBMIT_BUTTON": "Create WhatsApp Channel",
"EMBEDDED_SIGNUP": {
- "TITLE": "Quick Setup with Meta",
- "DESC": "You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
+ "TITLE": "Quick setup with Meta",
+ "DESC": "Use the WhatsApp Embedded Signup flow to quickly connect new numbers. You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
"BENEFITS": {
"TITLE": "Benefits of Embedded Signup:",
"EASY_SETUP": "No manual configuration required",
@@ -281,9 +281,8 @@
"AUTO_CONFIG": "Automatic webhook and phone number configuration"
},
"LEARN_MORE": {
- "TEXT": "To learn more about integrated signup, pricing, and limitations, visit",
- "LINK_TEXT": "this link.",
- "LINK_URL": "https://developers.facebook.com/docs/whatsapp/embedded-signup/custom-flows/onboarding-business-app-users#limitations"
+ "TEXT": "To learn more about integrated signup, pricing, and limitations, visit {link}.",
+ "LINK_TEXT": "this link"
},
"SUBMIT_BUTTON": "Connect with WhatsApp Business",
"AUTH_PROCESSING": "Authenticating with Meta",
@@ -296,7 +295,9 @@
"INVALID_BUSINESS_DATA": "Invalid business data received from Facebook. Please try again.",
"SIGNUP_ERROR": "Signup error occurred",
"AUTH_NOT_COMPLETED": "Authentication not completed. Please restart the process.",
- "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured"
+ "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured",
+ "MANUAL_FALLBACK": "If your number is already connected to the WhatsApp Business Platform (API), or if you’re a tech provider onboarding your own number, please use the {link} flow",
+ "MANUAL_LINK_TEXT": "manual setup flow"
},
"API": {
"ERROR_MESSAGE": "We were not able to save the WhatsApp channel"
@@ -424,7 +425,51 @@
},
"AUTH": {
"TITLE": "Kies een kanaal",
- "DESC": "Chatwoot supports live-chat widgets, Facebook Messenger, Twitter profiles, WhatsApp, Emails, etc., as channels. If you want to build a custom channel, you can create it using the API channel. To get started, choose one of the channels below."
+ "DESC": "Chatwoot supports live-chat widgets, Facebook Messenger, Twitter profiles, WhatsApp, Emails, etc., as channels. If you want to build a custom channel, you can create it using the API channel. To get started, choose one of the channels below.",
+ "TITLE_NEXT": "Complete the setup",
+ "TITLE_FINISH": "Voilà!",
+ "CHANNEL": {
+ "WEBSITE": {
+ "TITLE": "Website",
+ "DESCRIPTION": "Create a live-chat widget"
+ },
+ "FACEBOOK": {
+ "TITLE": "Facebook",
+ "DESCRIPTION": "Connect your Facebook page"
+ },
+ "WHATSAPP": {
+ "TITLE": "WhatsApp",
+ "DESCRIPTION": "Support your customers on WhatsApp"
+ },
+ "EMAIL": {
+ "TITLE": "E-mailadres",
+ "DESCRIPTION": "Connect with Gmail, Outlook, or other providers"
+ },
+ "SMS": {
+ "TITLE": "SMS",
+ "DESCRIPTION": "Integrate SMS channel with Twilio or bandwidth"
+ },
+ "API": {
+ "TITLE": "API",
+ "DESCRIPTION": "Make a custom channel using our API"
+ },
+ "TELEGRAM": {
+ "TITLE": "Telegram",
+ "DESCRIPTION": "Configure Telegram channel using Bot token"
+ },
+ "LINE": {
+ "TITLE": "Line",
+ "DESCRIPTION": "Integrate your Line channel"
+ },
+ "INSTAGRAM": {
+ "TITLE": "Instagram",
+ "DESCRIPTION": "Connect your instagram account"
+ },
+ "VOICE": {
+ "TITLE": "Voice",
+ "DESCRIPTION": "Integrate with Twilio Voice"
+ }
+ }
},
"AGENTS": {
"TITLE": "Agenten",
@@ -478,7 +523,10 @@
"MESSAGE": "Je kunt nu contact opnemen met je klanten via het nieuwe Kanaal. Gelukkige ondersteuning",
"BUTTON_TEXT": "Breng me ernaar toe",
"MORE_SETTINGS": "More settings",
- "WEBSITE_SUCCESS": "Het aanmaken van een website kanaal is gelukt. Kopieer de code hieronder weergegeven en plak deze op uw website. De volgende keer dat een klant de live chat gebruikt, verschijnt het gesprek automatisch op uw inbox."
+ "WEBSITE_SUCCESS": "Het aanmaken van een website kanaal is gelukt. Kopieer de code hieronder weergegeven en plak deze op uw website. De volgende keer dat een klant de live chat gebruikt, verschijnt het gesprek automatisch op uw inbox.",
+ "WHATSAPP_QR_INSTRUCTION": "Scan the QR code above to quickly test your WhatsApp inbox",
+ "MESSENGER_QR_INSTRUCTION": "Scan the QR code above to quickly test your Facebook Messenger inbox",
+ "TELEGRAM_QR_INSTRUCTION": "Scan the QR code above to quickly test your Telegram inbox"
},
"REAUTH": "Autoriseer",
"VIEW": "Bekijken",
@@ -868,9 +916,18 @@
"SCRIPT_SETTINGS": "\n window.chatwootSettings = {options};"
},
"EMAIL_PROVIDERS": {
- "MICROSOFT": "Microsoft",
- "GOOGLE": "Google",
- "OTHER_PROVIDERS": "Other Providers"
+ "MICROSOFT": {
+ "TITLE": "Microsoft",
+ "DESCRIPTION": "Connect with Microsoft"
+ },
+ "GOOGLE": {
+ "TITLE": "Google",
+ "DESCRIPTION": "Connect with Google"
+ },
+ "OTHER_PROVIDERS": {
+ "TITLE": "Other Providers",
+ "DESCRIPTION": "Connect with Other Providers"
+ }
},
"CHANNELS": {
"MESSENGER": "Messenger",
diff --git a/app/javascript/dashboard/i18n/locale/nl/integrations.json b/app/javascript/dashboard/i18n/locale/nl/integrations.json
index cecc6fac0..73fe8761c 100644
--- a/app/javascript/dashboard/i18n/locale/nl/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/nl/integrations.json
@@ -704,11 +704,28 @@
"ERROR_MESSAGE": "There was an error creating the document, please try again."
},
"FORM": {
+ "TYPE": {
+ "LABEL": "Document Type",
+ "URL": "URL",
+ "PDF": "PDF File"
+ },
"URL": {
"LABEL": "URL",
"PLACEHOLDER": "Enter the URL of the document",
"ERROR": "Please provide a valid URL for the document"
},
+ "PDF_FILE": {
+ "LABEL": "PDF File",
+ "CHOOSE_FILE": "Choose PDF file",
+ "ERROR": "Please select a PDF file",
+ "HELP_TEXT": "Maximum file size: 10MB",
+ "INVALID_TYPE": "Please select a valid PDF file",
+ "TOO_LARGE": "File size exceeds 10MB limit"
+ },
+ "NAME": {
+ "LABEL": "Document Name (Optional)",
+ "PLACEHOLDER": "Enter a name for the document"
+ },
"ASSISTANT": {
"LABEL": "Assistant",
"PLACEHOLDER": "Select the assistant",
@@ -744,6 +761,7 @@
"SELECTED": "{count} selected",
"SELECT_ALL": "Select all ({count})",
"UNSELECT_ALL": "Unselect all ({count})",
+ "SEARCH_PLACEHOLDER": "Search FAQs...",
"BULK_APPROVE_BUTTON": "Approve",
"BULK_DELETE_BUTTON": "Verwijderen",
"BULK_APPROVE": {
diff --git a/app/javascript/dashboard/i18n/locale/nl/mfa.json b/app/javascript/dashboard/i18n/locale/nl/mfa.json
new file mode 100644
index 000000000..9caa33f7a
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/nl/mfa.json
@@ -0,0 +1,106 @@
+{
+ "MFA_SETTINGS": {
+ "TITLE": "Two-Factor Authentication",
+ "SUBTITLE": "Secure your account with TOTP-based authentication",
+ "DESCRIPTION": "Add an extra layer of security to your account using a time-based one-time password (TOTP)",
+ "STATUS_TITLE": "Authentication Status",
+ "STATUS_DESCRIPTION": "Manage your two-factor authentication settings and backup recovery codes",
+ "ENABLED": "Ingeschakeld",
+ "DISABLED": "Uitgeschakeld",
+ "STATUS_ENABLED": "Two-factor authentication is active",
+ "STATUS_ENABLED_DESC": "Your account is protected with an additional layer of security",
+ "ENABLE_BUTTON": "Enable Two-Factor Authentication",
+ "ENHANCE_SECURITY": "Enhance Your Account Security",
+ "ENHANCE_SECURITY_DESC": "Two-factor authentication adds an extra layer of security by requiring a verification code from your authenticator app in addition to your password.",
+ "SETUP": {
+ "STEP_NUMBER_1": "1",
+ "STEP_NUMBER_2": "2",
+ "STEP1_TITLE": "Scan QR Code with Your Authenticator App",
+ "STEP1_DESCRIPTION": "Use Google Authenticator, Authy, or any TOTP-compatible app",
+ "LOADING_QR": "Loading...",
+ "MANUAL_ENTRY": "Can't scan? Enter code manually",
+ "SECRET_KEY": "Secret Key",
+ "COPY": "Kopiëren",
+ "ENTER_CODE": "Enter the 6-digit code from your authenticator app",
+ "ENTER_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify & Continue",
+ "CANCEL": "Annuleren",
+ "ERROR_STARTING": "MFA not enabled. Please contact administrator.",
+ "INVALID_CODE": "Invalid verification code",
+ "SECRET_COPIED": "Secret key copied to clipboard",
+ "SUCCESS": "Two-factor authentication has been enabled successfully"
+ },
+ "BACKUP": {
+ "TITLE": "Save Your Backup Codes",
+ "DESCRIPTION": "Keep these codes safe. Each can be used once if you lose access to your authenticator",
+ "IMPORTANT": "Important:",
+ "IMPORTANT_NOTE": " Save these codes in a secure location. You won't be able to see them again.",
+ "DOWNLOAD": "Download",
+ "COPY_ALL": "Copy All",
+ "CONFIRM": "I have saved my backup codes in a secure location and understand that I won't be able to see them again",
+ "COMPLETE_SETUP": "Complete Setup",
+ "CODES_COPIED": "Backup codes copied to clipboard"
+ },
+ "MANAGEMENT": {
+ "BACKUP_CODES": "Backup Codes",
+ "BACKUP_CODES_DESC": "Generate new codes if you've lost or used your existing ones",
+ "REGENERATE": "Regenerate Backup Codes",
+ "DISABLE_MFA": "Disable 2FA",
+ "DISABLE_MFA_DESC": "Remove two-factor authentication from your account",
+ "DISABLE_BUTTON": "Disable Two-Factor Authentication"
+ },
+ "DISABLE": {
+ "TITLE": "Disable Two-Factor Authentication",
+ "DESCRIPTION": "You'll need to enter your password and a verification code to disable two-factor authentication.",
+ "PASSWORD": "Wachtwoord",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Disable 2FA",
+ "CANCEL": "Annuleren",
+ "SUCCESS": "Two-factor authentication has been disabled",
+ "ERROR": "Failed to disable MFA. Please check your credentials."
+ },
+ "REGENERATE": {
+ "TITLE": "Regenerate Backup Codes",
+ "DESCRIPTION": "This will invalidate your existing backup codes and generate new ones. Enter your verification code to continue.",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Generate New Codes",
+ "CANCEL": "Annuleren",
+ "NEW_CODES_TITLE": "New Backup Codes Generated",
+ "NEW_CODES_DESC": "Your old backup codes have been invalidated. Save these new codes in a secure location.",
+ "CODES_IMPORTANT": "Important:",
+ "CODES_IMPORTANT_NOTE": " Each code can only be used once. Save them before closing this window.",
+ "DOWNLOAD_CODES": "Download Codes",
+ "COPY_ALL_CODES": "Copy All Codes",
+ "CODES_SAVED": "I've Saved My Codes",
+ "SUCCESS": "New backup codes have been generated",
+ "ERROR": "Failed to regenerate backup codes"
+ }
+ },
+ "MFA_VERIFICATION": {
+ "TITLE": "Two-Factor Authentication",
+ "DESCRIPTION": "Enter your verification code to continue",
+ "AUTHENTICATOR_APP": "Authenticator App",
+ "BACKUP_CODE": "Backup Code",
+ "ENTER_OTP_CODE": "Enter 6-digit code from your authenticator app",
+ "ENTER_BACKUP_CODE": "Enter one of your backup codes",
+ "BACKUP_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify",
+ "TRY_ANOTHER_METHOD": "Try another verification method",
+ "CANCEL_LOGIN": "Cancel and return to login",
+ "HELP_TEXT": "Having trouble signing in?",
+ "LEARN_MORE": "Learn more about 2FA",
+ "HELP_MODAL": {
+ "TITLE": "Two-Factor Authentication Help",
+ "AUTHENTICATOR_TITLE": "Using an Authenticator App",
+ "AUTHENTICATOR_DESC": "Open your authenticator app (Google Authenticator, Authy, etc.) and enter the 6-digit code shown for your account.",
+ "BACKUP_TITLE": "Using a Backup Code",
+ "BACKUP_DESC": "If you don't have access to your authenticator app, you can use one of the backup codes you saved when setting up 2FA. Each code can only be used once.",
+ "CONTACT_TITLE": "Need More Help?",
+ "CONTACT_DESC_CLOUD": "If you've lost access to both your authenticator app and backup codes, please reach out to Chatwoot support for assistance.",
+ "CONTACT_DESC_SELF_HOSTED": "If you've lost access to both your authenticator app and backup codes, please contact your administrator for assistance."
+ },
+ "VERIFICATION_FAILED": "Verification failed. Please try again."
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/nl/settings.json b/app/javascript/dashboard/i18n/locale/nl/settings.json
index 98ac5535b..0b2d4ac90 100644
--- a/app/javascript/dashboard/i18n/locale/nl/settings.json
+++ b/app/javascript/dashboard/i18n/locale/nl/settings.json
@@ -51,6 +51,13 @@
"LARGER": "Larger",
"EXTRA_LARGE": "Extra Large"
}
+ },
+ "LANGUAGE": {
+ "TITLE": "Preferred Language",
+ "NOTE": "Choose the language you want to use.",
+ "UPDATE_SUCCESS": "Your Language settings have been updated successfully",
+ "UPDATE_ERROR": "There is an error while updating the language settings, please try again",
+ "USE_ACCOUNT_DEFAULT": "Use account default"
}
},
"MESSAGE_SIGNATURE_SECTION": {
@@ -73,6 +80,11 @@
"NOTE": "Het bijwerken van uw wachtwoord zou uw logins op meerdere apparaten opnieuw instellen.",
"BTN_TEXT": "Wachtwoord wijzigen"
},
+ "SECURITY_SECTION": {
+ "TITLE": "Security",
+ "NOTE": "Manage additional security features for your account.",
+ "MFA_BUTTON": "Manage Two-Factor Authentication"
+ },
"ACCESS_TOKEN": {
"TITLE": "Toegangs-token",
"NOTE": "Dit token kan worden gebruikt als u een API gebaseerde integratie bouwt",
@@ -330,6 +342,7 @@
"REPORTS_LABEL": "Labelen",
"REPORTS_INBOX": "Postvak In",
"REPORTS_TEAM": "Team",
+ "AGENT_ASSIGNMENT": "Agent Assignment",
"SET_AVAILABILITY_TITLE": "Set yourself as",
"SET_YOUR_AVAILABILITY": "Set your availability",
"SLA": "SLA",
@@ -350,7 +363,8 @@
"INFO_TEXT": "Let the system automatically mark you offline when you aren't using the app or dashboard.",
"INFO_SHORT": "Automatically mark offline when you aren't using the app."
},
- "DOCS": "Read docs"
+ "DOCS": "Read docs",
+ "SECURITY": "Security"
},
"BILLING_SETTINGS": {
"TITLE": "Billing",
@@ -382,6 +396,77 @@
},
"NO_BILLING_USER": "Your billing account is being configured. Please refresh the page and try again."
},
+ "SECURITY_SETTINGS": {
+ "TITLE": "Security",
+ "DESCRIPTION": "Manage your account security settings.",
+ "LINK_TEXT": "Learn more about SAML SSO",
+ "SAML": {
+ "TITLE": "SAML SSO",
+ "NOTE": "Configure SAML single sign-on for your account. Users will authenticate through your identity provider instead of using email/password.",
+ "ACS_URL": {
+ "LABEL": "ACS URL",
+ "TOOLTIP": "Assertion Consumer Service URL - Configure this URL in your IdP as the destination for SAML responses"
+ },
+ "SSO_URL": {
+ "LABEL": "SSO URL",
+ "HELP": "The URL where SAML authentication requests will be sent",
+ "PLACEHOLDER": "https://your-idp.com/saml/sso"
+ },
+ "CERTIFICATE": {
+ "LABEL": "Signing certificate in PEM format",
+ "HELP": "The public certificate from your identity provider used to verify SAML responses",
+ "PLACEHOLDER": "-----BEGIN CERTIFICATE-----\nMIIC..."
+ },
+ "FINGERPRINT": {
+ "LABEL": "Fingerprint",
+ "TOOLTIP": "SHA-1 fingerprint of the certificate - Use this to verify the certificate in your IdP configuration"
+ },
+ "COPY_SUCCESS": "Code succesvol naar het klembord gekopieerd ",
+ "SP_ENTITY_ID": {
+ "LABEL": "SP Entity ID",
+ "HELP": "Unique identifier for this application as a service provider (auto-generated).",
+ "TOOLTIP": "Unique identifier for Chatwoot as the Service Provider - Configure this in your IdP settings"
+ },
+ "IDP_ENTITY_ID": {
+ "LABEL": "Identity Provider Entity ID",
+ "HELP": "Unique identifier for your identity provider (usually found in IdP configuration)",
+ "PLACEHOLDER": "https://your-idp.com/saml"
+ },
+ "UPDATE_BUTTON": "Update SAML Settings",
+ "API": {
+ "SUCCESS": "SAML settings updated successfully",
+ "ERROR": "Failed to update SAML settings",
+ "ERROR_LOADING": "Failed to load SAML settings",
+ "DISABLED": "SAML settings disabled successfully"
+ },
+ "VALIDATION": {
+ "REQUIRED_FIELDS": "SSO URL, Identity Provider Entity ID, and Certificate are required fields",
+ "SSO_URL_ERROR": "Please enter a valid SSO URL",
+ "CERTIFICATE_ERROR": "Certificate is required",
+ "IDP_ENTITY_ID_ERROR": "Identity Provider Entity ID is required"
+ },
+ "ENTERPRISE_PAYWALL": {
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade to an Enterprise plan to access SAML single sign-on and other advanced security features.",
+ "ASK_ADMIN": "Please reach out to your administrator for the upgrade."
+ },
+ "PAYWALL": {
+ "TITLE": "Upgrade to enable SAML SSO",
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade your plan to get access to SAML single sign-on and other advanced features.",
+ "UPGRADE_NOW": "Upgrade now",
+ "CANCEL_ANYTIME": "You can change or cancel your plan anytime"
+ },
+ "ATTRIBUTE_MAPPING": {
+ "TITLE": "SAML Attribute Setup",
+ "DESCRIPTION": "The following attribute mappings must be configured in your identity provider"
+ },
+ "INFO_SECTION": {
+ "TITLE": "Service Provider Information",
+ "TOOLTIP": "Copy these values and configure them in your Identity Provider to establish the SAML connection"
+ }
+ }
+ },
"CREATE_ACCOUNT": {
"NO_ACCOUNT_WARNING": "Uh oh! We could not find any Chatwoot accounts. Please create a new account to continue.",
"NEW_ACCOUNT": "New Account",
@@ -418,5 +503,255 @@
"SWITCH_TO_REPLY": "Switch to Reply",
"TOGGLE_SNOOZE_DROPDOWN": "Toggle snooze dropdown"
}
+ },
+ "ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent assignment",
+ "DESCRIPTION": "Define policies to effectively manage workload and route conversations based on the needs of inboxes and agents. Learn more here"
+ },
+ "ASSIGNMENT_POLICY": {
+ "TITLE": "Assignment policy",
+ "DESCRIPTION": "Manage how conversations get assigned in inboxes.",
+ "FEATURES": [
+ "Assign by conversations evenly or by available capacity",
+ "Add fair distribution rules to avoid overloading any agent",
+ "Add inboxes to a policy - one policy per inbox"
+ ]
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "TITLE": "Agent capacity policy",
+ "DESCRIPTION": "Manage workload for agents.",
+ "FEATURES": [
+ "Define maximum conversations per inbox",
+ "Create exceptions based on labels and time",
+ "Add agents to a policy - one policy per agent"
+ ]
+ }
+ },
+ "AGENT_ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Assignment policy",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "ORDER": "Order",
+ "PRIORITY": "Prioriteit",
+ "ACTIVE": "Actief",
+ "INACTIVE": "Inactive",
+ "POPOVER": "Added inboxes",
+ "EDIT": "Bewerken"
+ },
+ "NO_RECORDS_FOUND": "No assignment policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create assignment policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy created successfully",
+ "ERROR_MESSAGE": "Failed to create assignment policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit assignment policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_INBOX_DIALOG": {
+ "TITLE": "Add inbox",
+ "DESCRIPTION": "{inboxName} inbox is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Annuleren"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update assignment policy"
+ },
+ "INBOX_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Inbox added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Inbox removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove inbox from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Beschrijving:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "STATUS": {
+ "LABEL": "Status:",
+ "PLACEHOLDER": "Select status",
+ "ACTIVE": "Policy is active",
+ "INACTIVE": "Policy is inactive"
+ },
+ "ASSIGNMENT_ORDER": {
+ "LABEL": "Assignment order",
+ "ROUND_ROBIN": {
+ "LABEL": "Round robin",
+ "DESCRIPTION": "Assign conversations evenly among agents."
+ },
+ "BALANCED": {
+ "LABEL": "Balanced",
+ "DESCRIPTION": "Assign conversations based on available capacity."
+ }
+ },
+ "ASSIGNMENT_PRIORITY": {
+ "LABEL": "Assignment priority",
+ "EARLIEST_CREATED": {
+ "LABEL": "Earliest created",
+ "DESCRIPTION": "The conversation that was created first gets assigned first."
+ },
+ "LONGEST_WAITING": {
+ "LABEL": "Longest waiting",
+ "DESCRIPTION": "The conversation waiting the longest gets assigned first."
+ }
+ },
+ "FAIR_DISTRIBUTION": {
+ "LABEL": "Fair distribution policy",
+ "DESCRIPTION": "Set the maximum number of conversations that can be assigned per agent within a time window to avoid overloading any one agent. This required field defaults to 100 conversations per hour.",
+ "INPUT_MAX": "Assign max",
+ "DURATION": "Conversations per agent in every"
+ },
+ "INBOXES": {
+ "LABEL": "Added inboxes",
+ "DESCRIPTION": "Add inboxes for which this policy will be applicable.",
+ "ADD_BUTTON": "Add inbox",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select inboxes to add",
+ "ADD_BUTTON": "Toevoegen"
+ },
+ "EMPTY_STATE": "No inboxes added to this policy, add an inbox to get started",
+ "API": {
+ "SUCCESS_MESSAGE": "Inbox successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Assignment policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete assignment policy"
+ }
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent capacity",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "POPOVER": "Added agents",
+ "EDIT": "Bewerken"
+ },
+ "NO_RECORDS_FOUND": "No agent capacity policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create agent capacity policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy created successfully",
+ "ERROR_MESSAGE": "Failed to create agent capacity policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit agent capacity policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_AGENT_DIALOG": {
+ "TITLE": "Add agent",
+ "DESCRIPTION": "{agentName} is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Annuleren"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update agent capacity policy"
+ },
+ "AGENT_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Agent added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Agent removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove agent from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Beschrijving:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "INBOX_CAPACITY_LIMIT": {
+ "LABEL": "Inbox capacity limits",
+ "ADD_BUTTON": "Add inbox",
+ "FIELD": {
+ "SELECT_INBOX": "Select inbox",
+ "MAX_CONVERSATIONS": "Max conversations",
+ "SET_LIMIT": "Set limit"
+ },
+ "EMPTY_STATE": "No inbox limit set"
+ },
+ "EXCLUSION_RULES": {
+ "LABEL": "Exclusion rules",
+ "DESCRIPTION": "Conversations that satisfy the following conditions would not count towards agent capacity",
+ "TAGS": {
+ "LABEL": "Exclude conversations tagged with specific labels",
+ "ADD_TAG": "add tag",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select tags to add"
+ },
+ "EMPTY_STATE": "No tags added to this policy."
+ },
+ "DURATION": {
+ "LABEL": "Exclude conversations older than a specified duration",
+ "PLACEHOLDER": "Set time"
+ }
+ },
+ "USERS": {
+ "LABEL": "Assigned agents",
+ "DESCRIPTION": "Add agents for which this policy will be applicable.",
+ "ADD_BUTTON": "Add agent",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select agents to add",
+ "ADD_BUTTON": "Toevoegen"
+ },
+ "EMPTY_STATE": "No agents added",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Agent capacity policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete agent capacity policy"
+ }
+ },
+ "DELETE_POLICY": {
+ "TITLE": "Delete policy",
+ "DESCRIPTION": "Are you sure you want to delete this policy? This action cannot be undone.",
+ "CONFIRM_BUTTON_LABEL": "Verwijderen",
+ "CANCEL_BUTTON_LABEL": "Annuleren"
+ }
}
}
diff --git a/app/javascript/dashboard/i18n/locale/nl/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/nl/whatsappTemplates.json
index 7e4317fb8..5c5862427 100644
--- a/app/javascript/dashboard/i18n/locale/nl/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/nl/whatsappTemplates.json
@@ -40,6 +40,7 @@
"BUTTON_LABEL": "Button {index}",
"COUPON_CODE": "Enter coupon code (max 15 chars)",
"MEDIA_URL_LABEL": "Enter {type} URL",
+ "DOCUMENT_NAME_PLACEHOLDER": "Enter document filename (e.g., Invoice_2025.pdf)",
"BUTTON_PARAMETER": "Enter button parameter"
}
}
diff --git a/app/javascript/dashboard/i18n/locale/no/automation.json b/app/javascript/dashboard/i18n/locale/no/automation.json
index c76e4dc1a..b73eff766 100644
--- a/app/javascript/dashboard/i18n/locale/no/automation.json
+++ b/app/javascript/dashboard/i18n/locale/no/automation.json
@@ -177,7 +177,8 @@
"REFERER_LINK": "Referrer Link",
"ASSIGNEE_NAME": "Agent",
"TEAM_NAME": "Team",
- "PRIORITY": "Priority"
+ "PRIORITY": "Priority",
+ "LABELS": "Etiketter"
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/no/contact.json b/app/javascript/dashboard/i18n/locale/no/contact.json
index 43326595c..d7df11e1a 100644
--- a/app/javascript/dashboard/i18n/locale/no/contact.json
+++ b/app/javascript/dashboard/i18n/locale/no/contact.json
@@ -610,6 +610,15 @@
"SEND_MESSAGE": "Send message"
}
},
+ "TWILIO_OPTIONS": {
+ "LABEL": "Select template",
+ "SEARCH_PLACEHOLDER": "Search templates",
+ "EMPTY_STATE": "No templates found",
+ "TEMPLATE_PARSER": {
+ "BACK": "Go back",
+ "SEND_MESSAGE": "Send message"
+ }
+ },
"ACTION_BUTTONS": {
"DISCARD": "Discard",
"SEND": "Send ({keyCode})"
diff --git a/app/javascript/dashboard/i18n/locale/no/contactFilters.json b/app/javascript/dashboard/i18n/locale/no/contactFilters.json
index ea34b19ec..028a0a71c 100644
--- a/app/javascript/dashboard/i18n/locale/no/contactFilters.json
+++ b/app/javascript/dashboard/i18n/locale/no/contactFilters.json
@@ -48,7 +48,8 @@
"CREATED_AT": "Created At",
"LAST_ACTIVITY": "Last Activity",
"REFERER_LINK": "Referrer link",
- "BLOCKED": "Blocked"
+ "BLOCKED": "Blocked",
+ "LABELS": "Etiketter"
},
"GROUPS": {
"STANDARD_FILTERS": "Standard Filters",
diff --git a/app/javascript/dashboard/i18n/locale/no/contentTemplates.json b/app/javascript/dashboard/i18n/locale/no/contentTemplates.json
new file mode 100644
index 000000000..e754b6a7c
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/no/contentTemplates.json
@@ -0,0 +1,51 @@
+{
+ "CONTENT_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Twilio Templates",
+ "SUBTITLE": "Select the Twilio template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Search Templates",
+ "NO_TEMPLATES_FOUND": "No templates found for",
+ "NO_CONTENT": "Ingen innhold",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Category",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No Twilio templates available. Click refresh to sync templates from Twilio.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Language",
+ "TEMPLATE_BODY": "Template Body",
+ "CATEGORY": "Category"
+ },
+ "TYPES": {
+ "MEDIA": "Media",
+ "QUICK_REPLY": "Quick Reply",
+ "TEXT": "Text"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variables",
+ "LANGUAGE": "Language",
+ "CATEGORY": "Category",
+ "VARIABLE_PLACEHOLDER": "Enter {variable} value",
+ "GO_BACK_LABEL": "Go Back",
+ "SEND_MESSAGE_LABEL": "Send Message",
+ "FORM_ERROR_MESSAGE": "Please fill all variables before sending",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "MEDIA_URL_LABEL": "Enter full media URL",
+ "MEDIA_URL_PLACEHOLDER": "https://example.com/image.jpg"
+ },
+ "FORM": {
+ "BACK_BUTTON": "Tilbake",
+ "SEND_MESSAGE_BUTTON": "Send Message"
+ }
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/no/conversation.json b/app/javascript/dashboard/i18n/locale/no/conversation.json
index f5b59e7ed..094adf7a4 100644
--- a/app/javascript/dashboard/i18n/locale/no/conversation.json
+++ b/app/javascript/dashboard/i18n/locale/no/conversation.json
@@ -35,6 +35,11 @@
"API_HOURS_WINDOW": "You can only reply to this conversation within {hours} hours",
"NOT_ASSIGNED_TO_YOU": "This conversation is not assigned to you. Would you like to assign this conversation to yourself?",
"ASSIGN_TO_ME": "Assign to me",
+ "BOT_HANDOFF_MESSAGE": "You are responding to a conversation which is currently handled by an assistant or a bot.",
+ "BOT_HANDOFF_ACTION": "Mark open and assign to you",
+ "BOT_HANDOFF_REOPEN_ACTION": "Mark conversation open",
+ "BOT_HANDOFF_SUCCESS": "Conversation has been handed over to you",
+ "BOT_HANDOFF_ERROR": "Failed to take over the conversation. Please try again.",
"TWILIO_WHATSAPP_CAN_REPLY": "You can only reply to this conversation using a template message due to",
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "24-timers meldingsrestriksjon",
"OLD_INSTAGRAM_INBOX_REPLY_BANNER": "This Instagram account was migrated to the new Instagram channel inbox. All new messages will show up there. You won’t be able to send messages from this conversation anymore.",
@@ -66,6 +71,17 @@
"SHOW_LABELS": "Show labels",
"HIDE_LABELS": "Hide labels"
},
+ "VOICE_CALL": {
+ "INCOMING_CALL": "Incoming call",
+ "OUTGOING_CALL": "Outgoing call",
+ "CALL_IN_PROGRESS": "Call in progress",
+ "NO_ANSWER": "No answer",
+ "MISSED_CALL": "Missed call",
+ "CALL_ENDED": "Call ended",
+ "NOT_ANSWERED_YET": "Not answered yet",
+ "THEY_ANSWERED": "They answered",
+ "YOU_ANSWERED": "You answered"
+ },
"HEADER": {
"RESOLVE_ACTION": "Løs",
"REOPEN_ACTION": "Gjenåpne",
diff --git a/app/javascript/dashboard/i18n/locale/no/helpCenter.json b/app/javascript/dashboard/i18n/locale/no/helpCenter.json
index 736bb0e23..ac2901adc 100644
--- a/app/javascript/dashboard/i18n/locale/no/helpCenter.json
+++ b/app/javascript/dashboard/i18n/locale/no/helpCenter.json
@@ -741,7 +741,8 @@
"LIVE_CHAT_WIDGET": {
"LABEL": "Live chat widget",
"PLACEHOLDER": "Select live chat widget",
- "HELP_TEXT": "Select a live chat widget that will appear on your help center"
+ "HELP_TEXT": "Select a live chat widget that will appear on your help center",
+ "NONE_OPTION": "No widget"
},
"BRAND_COLOR": {
"LABEL": "Brand color"
@@ -807,6 +808,58 @@
"ERROR_MESSAGE": "Unable to update portal"
}
}
+ },
+ "PDF_UPLOAD": {
+ "TITLE": "Upload PDF Document",
+ "DESCRIPTION": "Upload a PDF document to automatically generate FAQs using AI",
+ "DRAG_DROP_TEXT": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "ADDITIONAL_CONTEXT_LABEL": "Additional Context (Optional)",
+ "ADDITIONAL_CONTEXT_PLACEHOLDER": "Provide any additional context or instructions for FAQ generation...",
+ "UPLOADING": "Laster opp...",
+ "UPLOAD": "Upload & Process",
+ "CANCEL": "Avbryt",
+ "ERROR_INVALID_TYPE": "Please select a valid PDF file",
+ "ERROR_FILE_TOO_LARGE": "File size must be less than 512MB",
+ "ERROR_UPLOAD_FAILED": "Failed to upload PDF. Please try again."
+ },
+ "PDF_DOCUMENTS": {
+ "TITLE": "PDF Documents",
+ "DESCRIPTION": "Manage uploaded PDF documents and generate FAQs from them",
+ "UPLOAD_PDF": "Upload PDF",
+ "UPLOAD_FIRST_PDF": "Upload your first PDF",
+ "UPLOADED_BY": "Uploaded by",
+ "GENERATE_FAQS": "Generate FAQs",
+ "GENERATING": "Generating...",
+ "CONFIRM_DELETE": "Are you sure you want to delete {filename}?",
+ "EMPTY_STATE": {
+ "TITLE": "No PDF documents yet",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQs using AI"
+ },
+ "STATUS": {
+ "UPLOADED": "Ready",
+ "PROCESSING": "Processing",
+ "PROCESSED": "Completed",
+ "FAILED": "Failed"
+ }
+ },
+ "CONTENT_GENERATION": {
+ "TITLE": "Content Generation",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQ content using AI",
+ "UPLOAD_TITLE": "Upload PDF Document",
+ "DRAG_DROP": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "UPLOADING": "Processing document...",
+ "UPLOAD_SUCCESS": "Document processed successfully!",
+ "UPLOAD_ERROR": "Failed to upload document. Please try again.",
+ "INVALID_FILE_TYPE": "Please select a valid PDF file",
+ "FILE_TOO_LARGE": "File size must be less than 512MB",
+ "GENERATED_CONTENT": "Generated FAQ Content",
+ "PUBLISH_SELECTED": "Publish Selected",
+ "PUBLISHING": "Publishing...",
+ "FROM_DOCUMENT": "From document",
+ "NO_CONTENT": "No generated content available. Upload a PDF document to get started.",
+ "LOADING": "Loading generated content..."
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/no/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/no/inboxMgmt.json
index 9bf8edbe4..4a60c22a9 100644
--- a/app/javascript/dashboard/i18n/locale/no/inboxMgmt.json
+++ b/app/javascript/dashboard/i18n/locale/no/inboxMgmt.json
@@ -272,8 +272,8 @@
},
"SUBMIT_BUTTON": "Create WhatsApp Channel",
"EMBEDDED_SIGNUP": {
- "TITLE": "Quick Setup with Meta",
- "DESC": "You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
+ "TITLE": "Quick setup with Meta",
+ "DESC": "Use the WhatsApp Embedded Signup flow to quickly connect new numbers. You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
"BENEFITS": {
"TITLE": "Benefits of Embedded Signup:",
"EASY_SETUP": "No manual configuration required",
@@ -281,9 +281,8 @@
"AUTO_CONFIG": "Automatic webhook and phone number configuration"
},
"LEARN_MORE": {
- "TEXT": "To learn more about integrated signup, pricing, and limitations, visit",
- "LINK_TEXT": "this link.",
- "LINK_URL": "https://developers.facebook.com/docs/whatsapp/embedded-signup/custom-flows/onboarding-business-app-users#limitations"
+ "TEXT": "To learn more about integrated signup, pricing, and limitations, visit {link}.",
+ "LINK_TEXT": "this link"
},
"SUBMIT_BUTTON": "Connect with WhatsApp Business",
"AUTH_PROCESSING": "Authenticating with Meta",
@@ -296,7 +295,9 @@
"INVALID_BUSINESS_DATA": "Invalid business data received from Facebook. Please try again.",
"SIGNUP_ERROR": "Signup error occurred",
"AUTH_NOT_COMPLETED": "Authentication not completed. Please restart the process.",
- "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured"
+ "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured",
+ "MANUAL_FALLBACK": "If your number is already connected to the WhatsApp Business Platform (API), or if you’re a tech provider onboarding your own number, please use the {link} flow",
+ "MANUAL_LINK_TEXT": "manual setup flow"
},
"API": {
"ERROR_MESSAGE": "We were not able to save the WhatsApp channel"
@@ -424,7 +425,51 @@
},
"AUTH": {
"TITLE": "Choose a channel",
- "DESC": "Chatwoot supports live-chat widgets, Facebook Messenger, Twitter profiles, WhatsApp, Emails, etc., as channels. If you want to build a custom channel, you can create it using the API channel. To get started, choose one of the channels below."
+ "DESC": "Chatwoot supports live-chat widgets, Facebook Messenger, Twitter profiles, WhatsApp, Emails, etc., as channels. If you want to build a custom channel, you can create it using the API channel. To get started, choose one of the channels below.",
+ "TITLE_NEXT": "Complete the setup",
+ "TITLE_FINISH": "Voilà!",
+ "CHANNEL": {
+ "WEBSITE": {
+ "TITLE": "Website",
+ "DESCRIPTION": "Create a live-chat widget"
+ },
+ "FACEBOOK": {
+ "TITLE": "Facebook",
+ "DESCRIPTION": "Connect your Facebook page"
+ },
+ "WHATSAPP": {
+ "TITLE": "WhatsApp",
+ "DESCRIPTION": "Support your customers on WhatsApp"
+ },
+ "EMAIL": {
+ "TITLE": "E-post",
+ "DESCRIPTION": "Connect with Gmail, Outlook, or other providers"
+ },
+ "SMS": {
+ "TITLE": "SMS",
+ "DESCRIPTION": "Integrate SMS channel with Twilio or bandwidth"
+ },
+ "API": {
+ "TITLE": "API",
+ "DESCRIPTION": "Make a custom channel using our API"
+ },
+ "TELEGRAM": {
+ "TITLE": "Telegram",
+ "DESCRIPTION": "Configure Telegram channel using Bot token"
+ },
+ "LINE": {
+ "TITLE": "Line",
+ "DESCRIPTION": "Integrate your Line channel"
+ },
+ "INSTAGRAM": {
+ "TITLE": "Instagram",
+ "DESCRIPTION": "Connect your instagram account"
+ },
+ "VOICE": {
+ "TITLE": "Voice",
+ "DESCRIPTION": "Integrate with Twilio Voice"
+ }
+ }
},
"AGENTS": {
"TITLE": "Agenter",
@@ -478,7 +523,10 @@
"MESSAGE": "You can now engage with your customers through your new Channel. Happy supporting",
"BUTTON_TEXT": "Ta meg dit",
"MORE_SETTINGS": "More settings",
- "WEBSITE_SUCCESS": "Du har nå fullført opprettingen av nettstedskanalen. Kopier koden nedenfor og lim den inn på nettstedet. Neste gang en kunde bruker live-chatten vil samtalen vises automatisk i innboksen din."
+ "WEBSITE_SUCCESS": "Du har nå fullført opprettingen av nettstedskanalen. Kopier koden nedenfor og lim den inn på nettstedet. Neste gang en kunde bruker live-chatten vil samtalen vises automatisk i innboksen din.",
+ "WHATSAPP_QR_INSTRUCTION": "Scan the QR code above to quickly test your WhatsApp inbox",
+ "MESSENGER_QR_INSTRUCTION": "Scan the QR code above to quickly test your Facebook Messenger inbox",
+ "TELEGRAM_QR_INSTRUCTION": "Scan the QR code above to quickly test your Telegram inbox"
},
"REAUTH": "Reautoriser",
"VIEW": "Vis",
@@ -868,9 +916,18 @@
"SCRIPT_SETTINGS": "\n window.chatwootSettings = {options};"
},
"EMAIL_PROVIDERS": {
- "MICROSOFT": "Microsoft",
- "GOOGLE": "Google",
- "OTHER_PROVIDERS": "Other Providers"
+ "MICROSOFT": {
+ "TITLE": "Microsoft",
+ "DESCRIPTION": "Connect with Microsoft"
+ },
+ "GOOGLE": {
+ "TITLE": "Google",
+ "DESCRIPTION": "Connect with Google"
+ },
+ "OTHER_PROVIDERS": {
+ "TITLE": "Other Providers",
+ "DESCRIPTION": "Connect with Other Providers"
+ }
},
"CHANNELS": {
"MESSENGER": "Messenger",
diff --git a/app/javascript/dashboard/i18n/locale/no/integrations.json b/app/javascript/dashboard/i18n/locale/no/integrations.json
index 033245b58..62e54852c 100644
--- a/app/javascript/dashboard/i18n/locale/no/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/no/integrations.json
@@ -704,11 +704,28 @@
"ERROR_MESSAGE": "There was an error creating the document, please try again."
},
"FORM": {
+ "TYPE": {
+ "LABEL": "Document Type",
+ "URL": "URL",
+ "PDF": "PDF File"
+ },
"URL": {
"LABEL": "URL",
"PLACEHOLDER": "Enter the URL of the document",
"ERROR": "Please provide a valid URL for the document"
},
+ "PDF_FILE": {
+ "LABEL": "PDF File",
+ "CHOOSE_FILE": "Choose PDF file",
+ "ERROR": "Please select a PDF file",
+ "HELP_TEXT": "Maximum file size: 10MB",
+ "INVALID_TYPE": "Please select a valid PDF file",
+ "TOO_LARGE": "File size exceeds 10MB limit"
+ },
+ "NAME": {
+ "LABEL": "Document Name (Optional)",
+ "PLACEHOLDER": "Enter a name for the document"
+ },
"ASSISTANT": {
"LABEL": "Assistant",
"PLACEHOLDER": "Select the assistant",
@@ -744,6 +761,7 @@
"SELECTED": "{count} selected",
"SELECT_ALL": "Select all ({count})",
"UNSELECT_ALL": "Unselect all ({count})",
+ "SEARCH_PLACEHOLDER": "Search FAQs...",
"BULK_APPROVE_BUTTON": "Approve",
"BULK_DELETE_BUTTON": "Slett",
"BULK_APPROVE": {
diff --git a/app/javascript/dashboard/i18n/locale/no/mfa.json b/app/javascript/dashboard/i18n/locale/no/mfa.json
new file mode 100644
index 000000000..b097a4685
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/no/mfa.json
@@ -0,0 +1,106 @@
+{
+ "MFA_SETTINGS": {
+ "TITLE": "Two-Factor Authentication",
+ "SUBTITLE": "Secure your account with TOTP-based authentication",
+ "DESCRIPTION": "Add an extra layer of security to your account using a time-based one-time password (TOTP)",
+ "STATUS_TITLE": "Authentication Status",
+ "STATUS_DESCRIPTION": "Manage your two-factor authentication settings and backup recovery codes",
+ "ENABLED": "Aktivert",
+ "DISABLED": "Deaktivert",
+ "STATUS_ENABLED": "Two-factor authentication is active",
+ "STATUS_ENABLED_DESC": "Your account is protected with an additional layer of security",
+ "ENABLE_BUTTON": "Enable Two-Factor Authentication",
+ "ENHANCE_SECURITY": "Enhance Your Account Security",
+ "ENHANCE_SECURITY_DESC": "Two-factor authentication adds an extra layer of security by requiring a verification code from your authenticator app in addition to your password.",
+ "SETUP": {
+ "STEP_NUMBER_1": "1",
+ "STEP_NUMBER_2": "2",
+ "STEP1_TITLE": "Scan QR Code with Your Authenticator App",
+ "STEP1_DESCRIPTION": "Use Google Authenticator, Authy, or any TOTP-compatible app",
+ "LOADING_QR": "Loading...",
+ "MANUAL_ENTRY": "Can't scan? Enter code manually",
+ "SECRET_KEY": "Secret Key",
+ "COPY": "Kopier",
+ "ENTER_CODE": "Enter the 6-digit code from your authenticator app",
+ "ENTER_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify & Continue",
+ "CANCEL": "Avbryt",
+ "ERROR_STARTING": "MFA not enabled. Please contact administrator.",
+ "INVALID_CODE": "Invalid verification code",
+ "SECRET_COPIED": "Secret key copied to clipboard",
+ "SUCCESS": "Two-factor authentication has been enabled successfully"
+ },
+ "BACKUP": {
+ "TITLE": "Save Your Backup Codes",
+ "DESCRIPTION": "Keep these codes safe. Each can be used once if you lose access to your authenticator",
+ "IMPORTANT": "Important:",
+ "IMPORTANT_NOTE": " Save these codes in a secure location. You won't be able to see them again.",
+ "DOWNLOAD": "Last ned",
+ "COPY_ALL": "Copy All",
+ "CONFIRM": "I have saved my backup codes in a secure location and understand that I won't be able to see them again",
+ "COMPLETE_SETUP": "Complete Setup",
+ "CODES_COPIED": "Backup codes copied to clipboard"
+ },
+ "MANAGEMENT": {
+ "BACKUP_CODES": "Backup Codes",
+ "BACKUP_CODES_DESC": "Generate new codes if you've lost or used your existing ones",
+ "REGENERATE": "Regenerate Backup Codes",
+ "DISABLE_MFA": "Disable 2FA",
+ "DISABLE_MFA_DESC": "Remove two-factor authentication from your account",
+ "DISABLE_BUTTON": "Disable Two-Factor Authentication"
+ },
+ "DISABLE": {
+ "TITLE": "Disable Two-Factor Authentication",
+ "DESCRIPTION": "You'll need to enter your password and a verification code to disable two-factor authentication.",
+ "PASSWORD": "Passord",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Disable 2FA",
+ "CANCEL": "Avbryt",
+ "SUCCESS": "Two-factor authentication has been disabled",
+ "ERROR": "Failed to disable MFA. Please check your credentials."
+ },
+ "REGENERATE": {
+ "TITLE": "Regenerate Backup Codes",
+ "DESCRIPTION": "This will invalidate your existing backup codes and generate new ones. Enter your verification code to continue.",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Generate New Codes",
+ "CANCEL": "Avbryt",
+ "NEW_CODES_TITLE": "New Backup Codes Generated",
+ "NEW_CODES_DESC": "Your old backup codes have been invalidated. Save these new codes in a secure location.",
+ "CODES_IMPORTANT": "Important:",
+ "CODES_IMPORTANT_NOTE": " Each code can only be used once. Save them before closing this window.",
+ "DOWNLOAD_CODES": "Download Codes",
+ "COPY_ALL_CODES": "Copy All Codes",
+ "CODES_SAVED": "I've Saved My Codes",
+ "SUCCESS": "New backup codes have been generated",
+ "ERROR": "Failed to regenerate backup codes"
+ }
+ },
+ "MFA_VERIFICATION": {
+ "TITLE": "Two-Factor Authentication",
+ "DESCRIPTION": "Enter your verification code to continue",
+ "AUTHENTICATOR_APP": "Authenticator App",
+ "BACKUP_CODE": "Backup Code",
+ "ENTER_OTP_CODE": "Enter 6-digit code from your authenticator app",
+ "ENTER_BACKUP_CODE": "Enter one of your backup codes",
+ "BACKUP_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify",
+ "TRY_ANOTHER_METHOD": "Try another verification method",
+ "CANCEL_LOGIN": "Cancel and return to login",
+ "HELP_TEXT": "Having trouble signing in?",
+ "LEARN_MORE": "Learn more about 2FA",
+ "HELP_MODAL": {
+ "TITLE": "Two-Factor Authentication Help",
+ "AUTHENTICATOR_TITLE": "Using an Authenticator App",
+ "AUTHENTICATOR_DESC": "Open your authenticator app (Google Authenticator, Authy, etc.) and enter the 6-digit code shown for your account.",
+ "BACKUP_TITLE": "Using a Backup Code",
+ "BACKUP_DESC": "If you don't have access to your authenticator app, you can use one of the backup codes you saved when setting up 2FA. Each code can only be used once.",
+ "CONTACT_TITLE": "Need More Help?",
+ "CONTACT_DESC_CLOUD": "If you've lost access to both your authenticator app and backup codes, please reach out to Chatwoot support for assistance.",
+ "CONTACT_DESC_SELF_HOSTED": "If you've lost access to both your authenticator app and backup codes, please contact your administrator for assistance."
+ },
+ "VERIFICATION_FAILED": "Verification failed. Please try again."
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/no/settings.json b/app/javascript/dashboard/i18n/locale/no/settings.json
index e2bffacc6..f4e7d03dd 100644
--- a/app/javascript/dashboard/i18n/locale/no/settings.json
+++ b/app/javascript/dashboard/i18n/locale/no/settings.json
@@ -51,6 +51,13 @@
"LARGER": "Larger",
"EXTRA_LARGE": "Extra Large"
}
+ },
+ "LANGUAGE": {
+ "TITLE": "Preferred Language",
+ "NOTE": "Choose the language you want to use.",
+ "UPDATE_SUCCESS": "Your Language settings have been updated successfully",
+ "UPDATE_ERROR": "There is an error while updating the language settings, please try again",
+ "USE_ACCOUNT_DEFAULT": "Use account default"
}
},
"MESSAGE_SIGNATURE_SECTION": {
@@ -73,6 +80,11 @@
"NOTE": "Oppdatering av passordet ditt nullstiller logger deg ut på andre enheter.",
"BTN_TEXT": "Change password"
},
+ "SECURITY_SECTION": {
+ "TITLE": "Security",
+ "NOTE": "Manage additional security features for your account.",
+ "MFA_BUTTON": "Manage Two-Factor Authentication"
+ },
"ACCESS_TOKEN": {
"TITLE": "Tilgangstoken",
"NOTE": "Dette tokenet kan brukes hvis du lager en API-basert integrasjon",
@@ -330,6 +342,7 @@
"REPORTS_LABEL": "Etiketter",
"REPORTS_INBOX": "Inbox",
"REPORTS_TEAM": "Team",
+ "AGENT_ASSIGNMENT": "Agent Assignment",
"SET_AVAILABILITY_TITLE": "Set yourself as",
"SET_YOUR_AVAILABILITY": "Set your availability",
"SLA": "SLA",
@@ -350,7 +363,8 @@
"INFO_TEXT": "Let the system automatically mark you offline when you aren't using the app or dashboard.",
"INFO_SHORT": "Automatically mark offline when you aren't using the app."
},
- "DOCS": "Read docs"
+ "DOCS": "Read docs",
+ "SECURITY": "Security"
},
"BILLING_SETTINGS": {
"TITLE": "Billing",
@@ -382,6 +396,77 @@
},
"NO_BILLING_USER": "Your billing account is being configured. Please refresh the page and try again."
},
+ "SECURITY_SETTINGS": {
+ "TITLE": "Security",
+ "DESCRIPTION": "Manage your account security settings.",
+ "LINK_TEXT": "Learn more about SAML SSO",
+ "SAML": {
+ "TITLE": "SAML SSO",
+ "NOTE": "Configure SAML single sign-on for your account. Users will authenticate through your identity provider instead of using email/password.",
+ "ACS_URL": {
+ "LABEL": "ACS URL",
+ "TOOLTIP": "Assertion Consumer Service URL - Configure this URL in your IdP as the destination for SAML responses"
+ },
+ "SSO_URL": {
+ "LABEL": "SSO URL",
+ "HELP": "The URL where SAML authentication requests will be sent",
+ "PLACEHOLDER": "https://your-idp.com/saml/sso"
+ },
+ "CERTIFICATE": {
+ "LABEL": "Signing certificate in PEM format",
+ "HELP": "The public certificate from your identity provider used to verify SAML responses",
+ "PLACEHOLDER": "-----BEGIN CERTIFICATE-----\nMIIC..."
+ },
+ "FINGERPRINT": {
+ "LABEL": "Fingerprint",
+ "TOOLTIP": "SHA-1 fingerprint of the certificate - Use this to verify the certificate in your IdP configuration"
+ },
+ "COPY_SUCCESS": "Koden er kopiert til utklippstavlen",
+ "SP_ENTITY_ID": {
+ "LABEL": "SP Entity ID",
+ "HELP": "Unique identifier for this application as a service provider (auto-generated).",
+ "TOOLTIP": "Unique identifier for Chatwoot as the Service Provider - Configure this in your IdP settings"
+ },
+ "IDP_ENTITY_ID": {
+ "LABEL": "Identity Provider Entity ID",
+ "HELP": "Unique identifier for your identity provider (usually found in IdP configuration)",
+ "PLACEHOLDER": "https://your-idp.com/saml"
+ },
+ "UPDATE_BUTTON": "Update SAML Settings",
+ "API": {
+ "SUCCESS": "SAML settings updated successfully",
+ "ERROR": "Failed to update SAML settings",
+ "ERROR_LOADING": "Failed to load SAML settings",
+ "DISABLED": "SAML settings disabled successfully"
+ },
+ "VALIDATION": {
+ "REQUIRED_FIELDS": "SSO URL, Identity Provider Entity ID, and Certificate are required fields",
+ "SSO_URL_ERROR": "Please enter a valid SSO URL",
+ "CERTIFICATE_ERROR": "Certificate is required",
+ "IDP_ENTITY_ID_ERROR": "Identity Provider Entity ID is required"
+ },
+ "ENTERPRISE_PAYWALL": {
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade to an Enterprise plan to access SAML single sign-on and other advanced security features.",
+ "ASK_ADMIN": "Please reach out to your administrator for the upgrade."
+ },
+ "PAYWALL": {
+ "TITLE": "Upgrade to enable SAML SSO",
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade your plan to get access to SAML single sign-on and other advanced features.",
+ "UPGRADE_NOW": "Upgrade now",
+ "CANCEL_ANYTIME": "You can change or cancel your plan anytime"
+ },
+ "ATTRIBUTE_MAPPING": {
+ "TITLE": "SAML Attribute Setup",
+ "DESCRIPTION": "The following attribute mappings must be configured in your identity provider"
+ },
+ "INFO_SECTION": {
+ "TITLE": "Service Provider Information",
+ "TOOLTIP": "Copy these values and configure them in your Identity Provider to establish the SAML connection"
+ }
+ }
+ },
"CREATE_ACCOUNT": {
"NO_ACCOUNT_WARNING": "Uh oh! We could not find any Chatwoot accounts. Please create a new account to continue.",
"NEW_ACCOUNT": "Ny konto",
@@ -418,5 +503,255 @@
"SWITCH_TO_REPLY": "Switch to Reply",
"TOGGLE_SNOOZE_DROPDOWN": "Toggle snooze dropdown"
}
+ },
+ "ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent assignment",
+ "DESCRIPTION": "Define policies to effectively manage workload and route conversations based on the needs of inboxes and agents. Learn more here"
+ },
+ "ASSIGNMENT_POLICY": {
+ "TITLE": "Assignment policy",
+ "DESCRIPTION": "Manage how conversations get assigned in inboxes.",
+ "FEATURES": [
+ "Assign by conversations evenly or by available capacity",
+ "Add fair distribution rules to avoid overloading any agent",
+ "Add inboxes to a policy - one policy per inbox"
+ ]
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "TITLE": "Agent capacity policy",
+ "DESCRIPTION": "Manage workload for agents.",
+ "FEATURES": [
+ "Define maximum conversations per inbox",
+ "Create exceptions based on labels and time",
+ "Add agents to a policy - one policy per agent"
+ ]
+ }
+ },
+ "AGENT_ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Assignment policy",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "ORDER": "Order",
+ "PRIORITY": "Priority",
+ "ACTIVE": "Active",
+ "INACTIVE": "Inactive",
+ "POPOVER": "Added inboxes",
+ "EDIT": "Rediger"
+ },
+ "NO_RECORDS_FOUND": "No assignment policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create assignment policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy created successfully",
+ "ERROR_MESSAGE": "Failed to create assignment policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit assignment policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_INBOX_DIALOG": {
+ "TITLE": "Add inbox",
+ "DESCRIPTION": "{inboxName} inbox is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Avbryt"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update assignment policy"
+ },
+ "INBOX_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Inbox added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Inbox removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove inbox from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Beskrivelse:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "STATUS": {
+ "LABEL": "Satus:",
+ "PLACEHOLDER": "Select status",
+ "ACTIVE": "Policy is active",
+ "INACTIVE": "Policy is inactive"
+ },
+ "ASSIGNMENT_ORDER": {
+ "LABEL": "Assignment order",
+ "ROUND_ROBIN": {
+ "LABEL": "Round robin",
+ "DESCRIPTION": "Assign conversations evenly among agents."
+ },
+ "BALANCED": {
+ "LABEL": "Balanced",
+ "DESCRIPTION": "Assign conversations based on available capacity."
+ }
+ },
+ "ASSIGNMENT_PRIORITY": {
+ "LABEL": "Assignment priority",
+ "EARLIEST_CREATED": {
+ "LABEL": "Earliest created",
+ "DESCRIPTION": "The conversation that was created first gets assigned first."
+ },
+ "LONGEST_WAITING": {
+ "LABEL": "Longest waiting",
+ "DESCRIPTION": "The conversation waiting the longest gets assigned first."
+ }
+ },
+ "FAIR_DISTRIBUTION": {
+ "LABEL": "Fair distribution policy",
+ "DESCRIPTION": "Set the maximum number of conversations that can be assigned per agent within a time window to avoid overloading any one agent. This required field defaults to 100 conversations per hour.",
+ "INPUT_MAX": "Assign max",
+ "DURATION": "Conversations per agent in every"
+ },
+ "INBOXES": {
+ "LABEL": "Added inboxes",
+ "DESCRIPTION": "Add inboxes for which this policy will be applicable.",
+ "ADD_BUTTON": "Add inbox",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select inboxes to add",
+ "ADD_BUTTON": "Add"
+ },
+ "EMPTY_STATE": "No inboxes added to this policy, add an inbox to get started",
+ "API": {
+ "SUCCESS_MESSAGE": "Inbox successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Assignment policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete assignment policy"
+ }
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent capacity",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "POPOVER": "Added agents",
+ "EDIT": "Rediger"
+ },
+ "NO_RECORDS_FOUND": "No agent capacity policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create agent capacity policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy created successfully",
+ "ERROR_MESSAGE": "Failed to create agent capacity policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit agent capacity policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_AGENT_DIALOG": {
+ "TITLE": "Add agent",
+ "DESCRIPTION": "{agentName} is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Avbryt"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update agent capacity policy"
+ },
+ "AGENT_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Agent added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Agent removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove agent from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Beskrivelse:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "INBOX_CAPACITY_LIMIT": {
+ "LABEL": "Inbox capacity limits",
+ "ADD_BUTTON": "Add inbox",
+ "FIELD": {
+ "SELECT_INBOX": "Select inbox",
+ "MAX_CONVERSATIONS": "Max conversations",
+ "SET_LIMIT": "Set limit"
+ },
+ "EMPTY_STATE": "No inbox limit set"
+ },
+ "EXCLUSION_RULES": {
+ "LABEL": "Exclusion rules",
+ "DESCRIPTION": "Conversations that satisfy the following conditions would not count towards agent capacity",
+ "TAGS": {
+ "LABEL": "Exclude conversations tagged with specific labels",
+ "ADD_TAG": "add tag",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select tags to add"
+ },
+ "EMPTY_STATE": "No tags added to this policy."
+ },
+ "DURATION": {
+ "LABEL": "Exclude conversations older than a specified duration",
+ "PLACEHOLDER": "Set time"
+ }
+ },
+ "USERS": {
+ "LABEL": "Assigned agents",
+ "DESCRIPTION": "Add agents for which this policy will be applicable.",
+ "ADD_BUTTON": "Add agent",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select agents to add",
+ "ADD_BUTTON": "Add"
+ },
+ "EMPTY_STATE": "No agents added",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Agent capacity policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete agent capacity policy"
+ }
+ },
+ "DELETE_POLICY": {
+ "TITLE": "Delete policy",
+ "DESCRIPTION": "Are you sure you want to delete this policy? This action cannot be undone.",
+ "CONFIRM_BUTTON_LABEL": "Slett",
+ "CANCEL_BUTTON_LABEL": "Avbryt"
+ }
}
}
diff --git a/app/javascript/dashboard/i18n/locale/no/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/no/whatsappTemplates.json
index 5f53faaa8..cf28312dc 100644
--- a/app/javascript/dashboard/i18n/locale/no/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/no/whatsappTemplates.json
@@ -40,6 +40,7 @@
"BUTTON_LABEL": "Button {index}",
"COUPON_CODE": "Enter coupon code (max 15 chars)",
"MEDIA_URL_LABEL": "Enter {type} URL",
+ "DOCUMENT_NAME_PLACEHOLDER": "Enter document filename (e.g., Invoice_2025.pdf)",
"BUTTON_PARAMETER": "Enter button parameter"
}
}
diff --git a/app/javascript/dashboard/i18n/locale/pl/automation.json b/app/javascript/dashboard/i18n/locale/pl/automation.json
index f4212f70c..63bcba5fd 100644
--- a/app/javascript/dashboard/i18n/locale/pl/automation.json
+++ b/app/javascript/dashboard/i18n/locale/pl/automation.json
@@ -177,7 +177,8 @@
"REFERER_LINK": "Referrer Link",
"ASSIGNEE_NAME": "Assignee",
"TEAM_NAME": "Zespół",
- "PRIORITY": "Priorytet"
+ "PRIORITY": "Priorytet",
+ "LABELS": "Etykiety"
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/pl/contact.json b/app/javascript/dashboard/i18n/locale/pl/contact.json
index 26fddc117..aabcc5ea7 100644
--- a/app/javascript/dashboard/i18n/locale/pl/contact.json
+++ b/app/javascript/dashboard/i18n/locale/pl/contact.json
@@ -610,6 +610,15 @@
"SEND_MESSAGE": "Wyślij wiadomość"
}
},
+ "TWILIO_OPTIONS": {
+ "LABEL": "Select template",
+ "SEARCH_PLACEHOLDER": "Search templates",
+ "EMPTY_STATE": "No templates found",
+ "TEMPLATE_PARSER": {
+ "BACK": "Powróć",
+ "SEND_MESSAGE": "Wyślij wiadomość"
+ }
+ },
"ACTION_BUTTONS": {
"DISCARD": "Discard",
"SEND": "Send ({keyCode})"
diff --git a/app/javascript/dashboard/i18n/locale/pl/contactFilters.json b/app/javascript/dashboard/i18n/locale/pl/contactFilters.json
index a0286bf59..98765d1b3 100644
--- a/app/javascript/dashboard/i18n/locale/pl/contactFilters.json
+++ b/app/javascript/dashboard/i18n/locale/pl/contactFilters.json
@@ -48,7 +48,8 @@
"CREATED_AT": "Utworzono",
"LAST_ACTIVITY": "Ostatnia aktywność",
"REFERER_LINK": "Link referencyjny",
- "BLOCKED": "Blocked"
+ "BLOCKED": "Blocked",
+ "LABELS": "Etykiety"
},
"GROUPS": {
"STANDARD_FILTERS": "Filtry standardowe",
diff --git a/app/javascript/dashboard/i18n/locale/pl/contentTemplates.json b/app/javascript/dashboard/i18n/locale/pl/contentTemplates.json
new file mode 100644
index 000000000..e1b5509b6
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/pl/contentTemplates.json
@@ -0,0 +1,51 @@
+{
+ "CONTENT_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Twilio Templates",
+ "SUBTITLE": "Select the Twilio template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Wyszukaj szablony",
+ "NO_TEMPLATES_FOUND": "Nie znaleziono szablonów dla",
+ "NO_CONTENT": "No content",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Kategoria",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No Twilio templates available. Click refresh to sync templates from Twilio.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Język",
+ "TEMPLATE_BODY": "Treść szablonu",
+ "CATEGORY": "Kategoria"
+ },
+ "TYPES": {
+ "MEDIA": "Media",
+ "QUICK_REPLY": "Quick Reply",
+ "TEXT": "Tekst"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Zmienne",
+ "LANGUAGE": "Język",
+ "CATEGORY": "Kategoria",
+ "VARIABLE_PLACEHOLDER": "Wprowadź wartość {variable}",
+ "GO_BACK_LABEL": "Powrót",
+ "SEND_MESSAGE_LABEL": "Wyślij wiadomość",
+ "FORM_ERROR_MESSAGE": "Proszę wypełnić wszystkie zmienne przed wysłaniem",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "MEDIA_URL_LABEL": "Enter full media URL",
+ "MEDIA_URL_PLACEHOLDER": "https://example.com/image.jpg"
+ },
+ "FORM": {
+ "BACK_BUTTON": "Powrót",
+ "SEND_MESSAGE_BUTTON": "Wyślij wiadomość"
+ }
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/pl/conversation.json b/app/javascript/dashboard/i18n/locale/pl/conversation.json
index 546af5f1d..2ffea2fee 100644
--- a/app/javascript/dashboard/i18n/locale/pl/conversation.json
+++ b/app/javascript/dashboard/i18n/locale/pl/conversation.json
@@ -35,6 +35,11 @@
"API_HOURS_WINDOW": "You can only reply to this conversation within {hours} hours",
"NOT_ASSIGNED_TO_YOU": "Ta konwersacja nie jest Ci przypisana. Czy chcesz przypisać tę konwersację do siebie?",
"ASSIGN_TO_ME": "Przypisz do mnie",
+ "BOT_HANDOFF_MESSAGE": "You are responding to a conversation which is currently handled by an assistant or a bot.",
+ "BOT_HANDOFF_ACTION": "Mark open and assign to you",
+ "BOT_HANDOFF_REOPEN_ACTION": "Mark conversation open",
+ "BOT_HANDOFF_SUCCESS": "Conversation has been handed over to you",
+ "BOT_HANDOFF_ERROR": "Failed to take over the conversation. Please try again.",
"TWILIO_WHATSAPP_CAN_REPLY": "Możesz odpowiedzieć na tę rozmowę tylko za pomocą szablonu wiadomości, ponieważ",
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "Ograniczenie 24-godzinnego okna wiadomości",
"OLD_INSTAGRAM_INBOX_REPLY_BANNER": "This Instagram account was migrated to the new Instagram channel inbox. All new messages will show up there. You won’t be able to send messages from this conversation anymore.",
@@ -66,6 +71,17 @@
"SHOW_LABELS": "Pokaż etykiety",
"HIDE_LABELS": "Ukryj etykiety"
},
+ "VOICE_CALL": {
+ "INCOMING_CALL": "Incoming call",
+ "OUTGOING_CALL": "Outgoing call",
+ "CALL_IN_PROGRESS": "Call in progress",
+ "NO_ANSWER": "No answer",
+ "MISSED_CALL": "Missed call",
+ "CALL_ENDED": "Call ended",
+ "NOT_ANSWERED_YET": "Not answered yet",
+ "THEY_ANSWERED": "They answered",
+ "YOU_ANSWERED": "You answered"
+ },
"HEADER": {
"RESOLVE_ACTION": "Rozwiąż",
"REOPEN_ACTION": "Otwórz ponownie",
diff --git a/app/javascript/dashboard/i18n/locale/pl/helpCenter.json b/app/javascript/dashboard/i18n/locale/pl/helpCenter.json
index 518c73f81..e1f2ce056 100644
--- a/app/javascript/dashboard/i18n/locale/pl/helpCenter.json
+++ b/app/javascript/dashboard/i18n/locale/pl/helpCenter.json
@@ -741,7 +741,8 @@
"LIVE_CHAT_WIDGET": {
"LABEL": "Live chat widget",
"PLACEHOLDER": "Select live chat widget",
- "HELP_TEXT": "Select a live chat widget that will appear on your help center"
+ "HELP_TEXT": "Select a live chat widget that will appear on your help center",
+ "NONE_OPTION": "No widget"
},
"BRAND_COLOR": {
"LABEL": "Brand color"
@@ -807,6 +808,58 @@
"ERROR_MESSAGE": "Unable to update portal"
}
}
+ },
+ "PDF_UPLOAD": {
+ "TITLE": "Upload PDF Document",
+ "DESCRIPTION": "Upload a PDF document to automatically generate FAQs using AI",
+ "DRAG_DROP_TEXT": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "ADDITIONAL_CONTEXT_LABEL": "Additional Context (Optional)",
+ "ADDITIONAL_CONTEXT_PLACEHOLDER": "Provide any additional context or instructions for FAQ generation...",
+ "UPLOADING": "Przesyłanie...",
+ "UPLOAD": "Upload & Process",
+ "CANCEL": "Anuluj",
+ "ERROR_INVALID_TYPE": "Please select a valid PDF file",
+ "ERROR_FILE_TOO_LARGE": "File size must be less than 512MB",
+ "ERROR_UPLOAD_FAILED": "Failed to upload PDF. Please try again."
+ },
+ "PDF_DOCUMENTS": {
+ "TITLE": "PDF Documents",
+ "DESCRIPTION": "Manage uploaded PDF documents and generate FAQs from them",
+ "UPLOAD_PDF": "Upload PDF",
+ "UPLOAD_FIRST_PDF": "Upload your first PDF",
+ "UPLOADED_BY": "Uploaded by",
+ "GENERATE_FAQS": "Generate FAQs",
+ "GENERATING": "Generowanie...",
+ "CONFIRM_DELETE": "Are you sure you want to delete {filename}?",
+ "EMPTY_STATE": {
+ "TITLE": "No PDF documents yet",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQs using AI"
+ },
+ "STATUS": {
+ "UPLOADED": "Ready",
+ "PROCESSING": "Processing",
+ "PROCESSED": "Zakończone",
+ "FAILED": "Failed"
+ }
+ },
+ "CONTENT_GENERATION": {
+ "TITLE": "Content Generation",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQ content using AI",
+ "UPLOAD_TITLE": "Upload PDF Document",
+ "DRAG_DROP": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "UPLOADING": "Processing document...",
+ "UPLOAD_SUCCESS": "Document processed successfully!",
+ "UPLOAD_ERROR": "Failed to upload document. Please try again.",
+ "INVALID_FILE_TYPE": "Please select a valid PDF file",
+ "FILE_TOO_LARGE": "File size must be less than 512MB",
+ "GENERATED_CONTENT": "Generated FAQ Content",
+ "PUBLISH_SELECTED": "Publish Selected",
+ "PUBLISHING": "Publishing...",
+ "FROM_DOCUMENT": "From document",
+ "NO_CONTENT": "No generated content available. Upload a PDF document to get started.",
+ "LOADING": "Loading generated content..."
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/pl/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/pl/inboxMgmt.json
index 772c2afd7..c4aab006b 100644
--- a/app/javascript/dashboard/i18n/locale/pl/inboxMgmt.json
+++ b/app/javascript/dashboard/i18n/locale/pl/inboxMgmt.json
@@ -272,8 +272,8 @@
},
"SUBMIT_BUTTON": "Utwórz kanał WhatsApp",
"EMBEDDED_SIGNUP": {
- "TITLE": "Quick Setup with Meta",
- "DESC": "You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
+ "TITLE": "Quick setup with Meta",
+ "DESC": "Use the WhatsApp Embedded Signup flow to quickly connect new numbers. You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
"BENEFITS": {
"TITLE": "Benefits of Embedded Signup:",
"EASY_SETUP": "No manual configuration required",
@@ -281,9 +281,8 @@
"AUTO_CONFIG": "Automatic webhook and phone number configuration"
},
"LEARN_MORE": {
- "TEXT": "To learn more about integrated signup, pricing, and limitations, visit",
- "LINK_TEXT": "this link.",
- "LINK_URL": "https://developers.facebook.com/docs/whatsapp/embedded-signup/custom-flows/onboarding-business-app-users#limitations"
+ "TEXT": "To learn more about integrated signup, pricing, and limitations, visit {link}.",
+ "LINK_TEXT": "this link"
},
"SUBMIT_BUTTON": "Connect with WhatsApp Business",
"AUTH_PROCESSING": "Authenticating with Meta",
@@ -296,7 +295,9 @@
"INVALID_BUSINESS_DATA": "Invalid business data received from Facebook. Please try again.",
"SIGNUP_ERROR": "Signup error occurred",
"AUTH_NOT_COMPLETED": "Authentication not completed. Please restart the process.",
- "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured"
+ "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured",
+ "MANUAL_FALLBACK": "If your number is already connected to the WhatsApp Business Platform (API), or if you’re a tech provider onboarding your own number, please use the {link} flow",
+ "MANUAL_LINK_TEXT": "manual setup flow"
},
"API": {
"ERROR_MESSAGE": "Nie udało się zapisać kanału WhatsApp"
@@ -424,7 +425,51 @@
},
"AUTH": {
"TITLE": "Wybierz kanał",
- "DESC": "Chatwoot supports live-chat widgets, Facebook Messenger, Twitter profiles, WhatsApp, Emails, etc., as channels. If you want to build a custom channel, you can create it using the API channel. To get started, choose one of the channels below."
+ "DESC": "Chatwoot supports live-chat widgets, Facebook Messenger, Twitter profiles, WhatsApp, Emails, etc., as channels. If you want to build a custom channel, you can create it using the API channel. To get started, choose one of the channels below.",
+ "TITLE_NEXT": "Complete the setup",
+ "TITLE_FINISH": "Voilà!",
+ "CHANNEL": {
+ "WEBSITE": {
+ "TITLE": "Website",
+ "DESCRIPTION": "Create a live-chat widget"
+ },
+ "FACEBOOK": {
+ "TITLE": "Facebook",
+ "DESCRIPTION": "Connect your Facebook page"
+ },
+ "WHATSAPP": {
+ "TITLE": "WhatsApp",
+ "DESCRIPTION": "Support your customers on WhatsApp"
+ },
+ "EMAIL": {
+ "TITLE": "E-mail",
+ "DESCRIPTION": "Connect with Gmail, Outlook, or other providers"
+ },
+ "SMS": {
+ "TITLE": "SMS",
+ "DESCRIPTION": "Integrate SMS channel with Twilio or bandwidth"
+ },
+ "API": {
+ "TITLE": "API",
+ "DESCRIPTION": "Make a custom channel using our API"
+ },
+ "TELEGRAM": {
+ "TITLE": "Telegram",
+ "DESCRIPTION": "Configure Telegram channel using Bot token"
+ },
+ "LINE": {
+ "TITLE": "Line",
+ "DESCRIPTION": "Integrate your Line channel"
+ },
+ "INSTAGRAM": {
+ "TITLE": "Instagram",
+ "DESCRIPTION": "Connect your instagram account"
+ },
+ "VOICE": {
+ "TITLE": "Voice",
+ "DESCRIPTION": "Integrate with Twilio Voice"
+ }
+ }
},
"AGENTS": {
"TITLE": "Agenci",
@@ -478,7 +523,10 @@
"MESSAGE": "Możesz teraz kontaktować się z klientami za pośrednictwem nowego kanału. Szczęśliwy wspierający",
"BUTTON_TEXT": "Zabierz mnie tam",
"MORE_SETTINGS": "Więcej ustawień",
- "WEBSITE_SUCCESS": "Pomyślnie zakończyłeś tworzenie kanału internetowego. Skopiuj poniższy kod i wklej go na swojej stronie. Następnym razem, gdy klient korzysta z czatu na żywo, konwersacja pojawi się automatycznie na twojej skrzynce odbiorczej."
+ "WEBSITE_SUCCESS": "Pomyślnie zakończyłeś tworzenie kanału internetowego. Skopiuj poniższy kod i wklej go na swojej stronie. Następnym razem, gdy klient korzysta z czatu na żywo, konwersacja pojawi się automatycznie na twojej skrzynce odbiorczej.",
+ "WHATSAPP_QR_INSTRUCTION": "Scan the QR code above to quickly test your WhatsApp inbox",
+ "MESSENGER_QR_INSTRUCTION": "Scan the QR code above to quickly test your Facebook Messenger inbox",
+ "TELEGRAM_QR_INSTRUCTION": "Scan the QR code above to quickly test your Telegram inbox"
},
"REAUTH": "Ponowna autoryzacja",
"VIEW": "Widok",
@@ -868,9 +916,18 @@
"SCRIPT_SETTINGS": "\n window.chatwootSettings = {options};"
},
"EMAIL_PROVIDERS": {
- "MICROSOFT": "Microsoft",
- "GOOGLE": "Google",
- "OTHER_PROVIDERS": "Inni dostawcy"
+ "MICROSOFT": {
+ "TITLE": "Microsoft",
+ "DESCRIPTION": "Connect with Microsoft"
+ },
+ "GOOGLE": {
+ "TITLE": "Google",
+ "DESCRIPTION": "Connect with Google"
+ },
+ "OTHER_PROVIDERS": {
+ "TITLE": "Inni dostawcy",
+ "DESCRIPTION": "Connect with Other Providers"
+ }
},
"CHANNELS": {
"MESSENGER": "Messenger",
diff --git a/app/javascript/dashboard/i18n/locale/pl/integrations.json b/app/javascript/dashboard/i18n/locale/pl/integrations.json
index 7787df954..b3f26646a 100644
--- a/app/javascript/dashboard/i18n/locale/pl/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/pl/integrations.json
@@ -704,11 +704,28 @@
"ERROR_MESSAGE": "There was an error creating the document, please try again."
},
"FORM": {
+ "TYPE": {
+ "LABEL": "Document Type",
+ "URL": "Adres URL",
+ "PDF": "PDF File"
+ },
"URL": {
"LABEL": "Adres URL",
"PLACEHOLDER": "Enter the URL of the document",
"ERROR": "Please provide a valid URL for the document"
},
+ "PDF_FILE": {
+ "LABEL": "PDF File",
+ "CHOOSE_FILE": "Choose PDF file",
+ "ERROR": "Please select a PDF file",
+ "HELP_TEXT": "Maximum file size: 10MB",
+ "INVALID_TYPE": "Please select a valid PDF file",
+ "TOO_LARGE": "File size exceeds 10MB limit"
+ },
+ "NAME": {
+ "LABEL": "Document Name (Optional)",
+ "PLACEHOLDER": "Enter a name for the document"
+ },
"ASSISTANT": {
"LABEL": "Assistant",
"PLACEHOLDER": "Select the assistant",
@@ -744,6 +761,7 @@
"SELECTED": "{count} selected",
"SELECT_ALL": "Select all ({count})",
"UNSELECT_ALL": "Unselect all ({count})",
+ "SEARCH_PLACEHOLDER": "Search FAQs...",
"BULK_APPROVE_BUTTON": "Approve",
"BULK_DELETE_BUTTON": "Usuń",
"BULK_APPROVE": {
diff --git a/app/javascript/dashboard/i18n/locale/pl/mfa.json b/app/javascript/dashboard/i18n/locale/pl/mfa.json
new file mode 100644
index 000000000..3c3738b00
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/pl/mfa.json
@@ -0,0 +1,106 @@
+{
+ "MFA_SETTINGS": {
+ "TITLE": "Two-Factor Authentication",
+ "SUBTITLE": "Secure your account with TOTP-based authentication",
+ "DESCRIPTION": "Add an extra layer of security to your account using a time-based one-time password (TOTP)",
+ "STATUS_TITLE": "Authentication Status",
+ "STATUS_DESCRIPTION": "Manage your two-factor authentication settings and backup recovery codes",
+ "ENABLED": "Włączone",
+ "DISABLED": "Wyłączone",
+ "STATUS_ENABLED": "Two-factor authentication is active",
+ "STATUS_ENABLED_DESC": "Your account is protected with an additional layer of security",
+ "ENABLE_BUTTON": "Enable Two-Factor Authentication",
+ "ENHANCE_SECURITY": "Enhance Your Account Security",
+ "ENHANCE_SECURITY_DESC": "Two-factor authentication adds an extra layer of security by requiring a verification code from your authenticator app in addition to your password.",
+ "SETUP": {
+ "STEP_NUMBER_1": "1",
+ "STEP_NUMBER_2": "2",
+ "STEP1_TITLE": "Scan QR Code with Your Authenticator App",
+ "STEP1_DESCRIPTION": "Use Google Authenticator, Authy, or any TOTP-compatible app",
+ "LOADING_QR": "Loading...",
+ "MANUAL_ENTRY": "Can't scan? Enter code manually",
+ "SECRET_KEY": "Secret Key",
+ "COPY": "Kopiuj",
+ "ENTER_CODE": "Enter the 6-digit code from your authenticator app",
+ "ENTER_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify & Continue",
+ "CANCEL": "Anuluj",
+ "ERROR_STARTING": "MFA not enabled. Please contact administrator.",
+ "INVALID_CODE": "Invalid verification code",
+ "SECRET_COPIED": "Secret key copied to clipboard",
+ "SUCCESS": "Two-factor authentication has been enabled successfully"
+ },
+ "BACKUP": {
+ "TITLE": "Save Your Backup Codes",
+ "DESCRIPTION": "Keep these codes safe. Each can be used once if you lose access to your authenticator",
+ "IMPORTANT": "Important:",
+ "IMPORTANT_NOTE": " Save these codes in a secure location. You won't be able to see them again.",
+ "DOWNLOAD": "Pobierz",
+ "COPY_ALL": "Copy All",
+ "CONFIRM": "I have saved my backup codes in a secure location and understand that I won't be able to see them again",
+ "COMPLETE_SETUP": "Complete Setup",
+ "CODES_COPIED": "Backup codes copied to clipboard"
+ },
+ "MANAGEMENT": {
+ "BACKUP_CODES": "Backup Codes",
+ "BACKUP_CODES_DESC": "Generate new codes if you've lost or used your existing ones",
+ "REGENERATE": "Regenerate Backup Codes",
+ "DISABLE_MFA": "Disable 2FA",
+ "DISABLE_MFA_DESC": "Remove two-factor authentication from your account",
+ "DISABLE_BUTTON": "Disable Two-Factor Authentication"
+ },
+ "DISABLE": {
+ "TITLE": "Disable Two-Factor Authentication",
+ "DESCRIPTION": "You'll need to enter your password and a verification code to disable two-factor authentication.",
+ "PASSWORD": "Hasło",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Disable 2FA",
+ "CANCEL": "Anuluj",
+ "SUCCESS": "Two-factor authentication has been disabled",
+ "ERROR": "Failed to disable MFA. Please check your credentials."
+ },
+ "REGENERATE": {
+ "TITLE": "Regenerate Backup Codes",
+ "DESCRIPTION": "This will invalidate your existing backup codes and generate new ones. Enter your verification code to continue.",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Generate New Codes",
+ "CANCEL": "Anuluj",
+ "NEW_CODES_TITLE": "New Backup Codes Generated",
+ "NEW_CODES_DESC": "Your old backup codes have been invalidated. Save these new codes in a secure location.",
+ "CODES_IMPORTANT": "Important:",
+ "CODES_IMPORTANT_NOTE": " Each code can only be used once. Save them before closing this window.",
+ "DOWNLOAD_CODES": "Download Codes",
+ "COPY_ALL_CODES": "Copy All Codes",
+ "CODES_SAVED": "I've Saved My Codes",
+ "SUCCESS": "New backup codes have been generated",
+ "ERROR": "Failed to regenerate backup codes"
+ }
+ },
+ "MFA_VERIFICATION": {
+ "TITLE": "Two-Factor Authentication",
+ "DESCRIPTION": "Enter your verification code to continue",
+ "AUTHENTICATOR_APP": "Authenticator App",
+ "BACKUP_CODE": "Backup Code",
+ "ENTER_OTP_CODE": "Enter 6-digit code from your authenticator app",
+ "ENTER_BACKUP_CODE": "Enter one of your backup codes",
+ "BACKUP_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify",
+ "TRY_ANOTHER_METHOD": "Try another verification method",
+ "CANCEL_LOGIN": "Cancel and return to login",
+ "HELP_TEXT": "Having trouble signing in?",
+ "LEARN_MORE": "Learn more about 2FA",
+ "HELP_MODAL": {
+ "TITLE": "Two-Factor Authentication Help",
+ "AUTHENTICATOR_TITLE": "Using an Authenticator App",
+ "AUTHENTICATOR_DESC": "Open your authenticator app (Google Authenticator, Authy, etc.) and enter the 6-digit code shown for your account.",
+ "BACKUP_TITLE": "Using a Backup Code",
+ "BACKUP_DESC": "If you don't have access to your authenticator app, you can use one of the backup codes you saved when setting up 2FA. Each code can only be used once.",
+ "CONTACT_TITLE": "Need More Help?",
+ "CONTACT_DESC_CLOUD": "If you've lost access to both your authenticator app and backup codes, please reach out to Chatwoot support for assistance.",
+ "CONTACT_DESC_SELF_HOSTED": "If you've lost access to both your authenticator app and backup codes, please contact your administrator for assistance."
+ },
+ "VERIFICATION_FAILED": "Verification failed. Please try again."
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/pl/settings.json b/app/javascript/dashboard/i18n/locale/pl/settings.json
index 5c6d7312b..5fecc7f77 100644
--- a/app/javascript/dashboard/i18n/locale/pl/settings.json
+++ b/app/javascript/dashboard/i18n/locale/pl/settings.json
@@ -51,6 +51,13 @@
"LARGER": "Larger",
"EXTRA_LARGE": "Extra Large"
}
+ },
+ "LANGUAGE": {
+ "TITLE": "Preferred Language",
+ "NOTE": "Choose the language you want to use.",
+ "UPDATE_SUCCESS": "Your Language settings have been updated successfully",
+ "UPDATE_ERROR": "There is an error while updating the language settings, please try again",
+ "USE_ACCOUNT_DEFAULT": "Use account default"
}
},
"MESSAGE_SIGNATURE_SECTION": {
@@ -73,6 +80,11 @@
"NOTE": "Zmiana hasła spowoduje zresetowanie logowania na wielu urządzeniach.",
"BTN_TEXT": "Zmień hasło"
},
+ "SECURITY_SECTION": {
+ "TITLE": "Security",
+ "NOTE": "Manage additional security features for your account.",
+ "MFA_BUTTON": "Manage Two-Factor Authentication"
+ },
"ACCESS_TOKEN": {
"TITLE": "Token dostępu",
"NOTE": "Ten token może być użyty, jeśli budujesz integrację opartą na API",
@@ -330,6 +342,7 @@
"REPORTS_LABEL": "Etykiety",
"REPORTS_INBOX": "Skrzynka odbiorcza",
"REPORTS_TEAM": "Zespół",
+ "AGENT_ASSIGNMENT": "Agent Assignment",
"SET_AVAILABILITY_TITLE": "Ustaw dostępność jako",
"SET_YOUR_AVAILABILITY": "Ustaw swoją dostępność",
"SLA": "SLA",
@@ -350,7 +363,8 @@
"INFO_TEXT": "Pozwól systemowi automatycznie oznaczać Cię jako offline, gdy nie korzystasz z aplikacji lub panelu",
"INFO_SHORT": "Automatically mark offline when you aren't using the app."
},
- "DOCS": "Czytaj dokumentację"
+ "DOCS": "Czytaj dokumentację",
+ "SECURITY": "Security"
},
"BILLING_SETTINGS": {
"TITLE": "Rozliczenia",
@@ -382,6 +396,77 @@
},
"NO_BILLING_USER": "Konfigurowanie konta rozliczeniowego. Odśwież stronę i spróbuj ponownie."
},
+ "SECURITY_SETTINGS": {
+ "TITLE": "Security",
+ "DESCRIPTION": "Manage your account security settings.",
+ "LINK_TEXT": "Learn more about SAML SSO",
+ "SAML": {
+ "TITLE": "SAML SSO",
+ "NOTE": "Configure SAML single sign-on for your account. Users will authenticate through your identity provider instead of using email/password.",
+ "ACS_URL": {
+ "LABEL": "ACS URL",
+ "TOOLTIP": "Assertion Consumer Service URL - Configure this URL in your IdP as the destination for SAML responses"
+ },
+ "SSO_URL": {
+ "LABEL": "SSO URL",
+ "HELP": "The URL where SAML authentication requests will be sent",
+ "PLACEHOLDER": "https://your-idp.com/saml/sso"
+ },
+ "CERTIFICATE": {
+ "LABEL": "Signing certificate in PEM format",
+ "HELP": "The public certificate from your identity provider used to verify SAML responses",
+ "PLACEHOLDER": "-----BEGIN CERTIFICATE-----\nMIIC..."
+ },
+ "FINGERPRINT": {
+ "LABEL": "Fingerprint",
+ "TOOLTIP": "SHA-1 fingerprint of the certificate - Use this to verify the certificate in your IdP configuration"
+ },
+ "COPY_SUCCESS": "Kod został skopiowany do schowka",
+ "SP_ENTITY_ID": {
+ "LABEL": "SP Entity ID",
+ "HELP": "Unique identifier for this application as a service provider (auto-generated).",
+ "TOOLTIP": "Unique identifier for Chatwoot as the Service Provider - Configure this in your IdP settings"
+ },
+ "IDP_ENTITY_ID": {
+ "LABEL": "Identity Provider Entity ID",
+ "HELP": "Unique identifier for your identity provider (usually found in IdP configuration)",
+ "PLACEHOLDER": "https://your-idp.com/saml"
+ },
+ "UPDATE_BUTTON": "Update SAML Settings",
+ "API": {
+ "SUCCESS": "SAML settings updated successfully",
+ "ERROR": "Failed to update SAML settings",
+ "ERROR_LOADING": "Failed to load SAML settings",
+ "DISABLED": "SAML settings disabled successfully"
+ },
+ "VALIDATION": {
+ "REQUIRED_FIELDS": "SSO URL, Identity Provider Entity ID, and Certificate are required fields",
+ "SSO_URL_ERROR": "Please enter a valid SSO URL",
+ "CERTIFICATE_ERROR": "Certificate is required",
+ "IDP_ENTITY_ID_ERROR": "Identity Provider Entity ID is required"
+ },
+ "ENTERPRISE_PAYWALL": {
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade to an Enterprise plan to access SAML single sign-on and other advanced security features.",
+ "ASK_ADMIN": "Please reach out to your administrator for the upgrade."
+ },
+ "PAYWALL": {
+ "TITLE": "Upgrade to enable SAML SSO",
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade your plan to get access to SAML single sign-on and other advanced features.",
+ "UPGRADE_NOW": "Upgrade now",
+ "CANCEL_ANYTIME": "You can change or cancel your plan anytime"
+ },
+ "ATTRIBUTE_MAPPING": {
+ "TITLE": "SAML Attribute Setup",
+ "DESCRIPTION": "The following attribute mappings must be configured in your identity provider"
+ },
+ "INFO_SECTION": {
+ "TITLE": "Service Provider Information",
+ "TOOLTIP": "Copy these values and configure them in your Identity Provider to establish the SAML connection"
+ }
+ }
+ },
"CREATE_ACCOUNT": {
"NO_ACCOUNT_WARNING": "Ups! Nie znaleziono żadnych kont Chatwoot. Aby kontynuować, utwórz nowe konto.",
"NEW_ACCOUNT": "Nowe konto",
@@ -418,5 +503,255 @@
"SWITCH_TO_REPLY": "Przełącz do odpowiedzi",
"TOGGLE_SNOOZE_DROPDOWN": "Przełącz menu rozwijane drzemki"
}
+ },
+ "ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent assignment",
+ "DESCRIPTION": "Define policies to effectively manage workload and route conversations based on the needs of inboxes and agents. Learn more here"
+ },
+ "ASSIGNMENT_POLICY": {
+ "TITLE": "Assignment policy",
+ "DESCRIPTION": "Manage how conversations get assigned in inboxes.",
+ "FEATURES": [
+ "Assign by conversations evenly or by available capacity",
+ "Add fair distribution rules to avoid overloading any agent",
+ "Add inboxes to a policy - one policy per inbox"
+ ]
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "TITLE": "Agent capacity policy",
+ "DESCRIPTION": "Manage workload for agents.",
+ "FEATURES": [
+ "Define maximum conversations per inbox",
+ "Create exceptions based on labels and time",
+ "Add agents to a policy - one policy per agent"
+ ]
+ }
+ },
+ "AGENT_ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Assignment policy",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "ORDER": "Order",
+ "PRIORITY": "Priorytet",
+ "ACTIVE": "Aktywne",
+ "INACTIVE": "Inactive",
+ "POPOVER": "Added inboxes",
+ "EDIT": "Edytuj"
+ },
+ "NO_RECORDS_FOUND": "No assignment policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create assignment policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy created successfully",
+ "ERROR_MESSAGE": "Failed to create assignment policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit assignment policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_INBOX_DIALOG": {
+ "TITLE": "Add inbox",
+ "DESCRIPTION": "{inboxName} inbox is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Anuluj"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update assignment policy"
+ },
+ "INBOX_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Inbox added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Inbox removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove inbox from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Opis:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "STATUS": {
+ "LABEL": "Status:",
+ "PLACEHOLDER": "Select status",
+ "ACTIVE": "Policy is active",
+ "INACTIVE": "Policy is inactive"
+ },
+ "ASSIGNMENT_ORDER": {
+ "LABEL": "Assignment order",
+ "ROUND_ROBIN": {
+ "LABEL": "Round robin",
+ "DESCRIPTION": "Assign conversations evenly among agents."
+ },
+ "BALANCED": {
+ "LABEL": "Balanced",
+ "DESCRIPTION": "Assign conversations based on available capacity."
+ }
+ },
+ "ASSIGNMENT_PRIORITY": {
+ "LABEL": "Assignment priority",
+ "EARLIEST_CREATED": {
+ "LABEL": "Earliest created",
+ "DESCRIPTION": "The conversation that was created first gets assigned first."
+ },
+ "LONGEST_WAITING": {
+ "LABEL": "Longest waiting",
+ "DESCRIPTION": "The conversation waiting the longest gets assigned first."
+ }
+ },
+ "FAIR_DISTRIBUTION": {
+ "LABEL": "Fair distribution policy",
+ "DESCRIPTION": "Set the maximum number of conversations that can be assigned per agent within a time window to avoid overloading any one agent. This required field defaults to 100 conversations per hour.",
+ "INPUT_MAX": "Assign max",
+ "DURATION": "Conversations per agent in every"
+ },
+ "INBOXES": {
+ "LABEL": "Added inboxes",
+ "DESCRIPTION": "Add inboxes for which this policy will be applicable.",
+ "ADD_BUTTON": "Add inbox",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select inboxes to add",
+ "ADD_BUTTON": "Dodaj"
+ },
+ "EMPTY_STATE": "No inboxes added to this policy, add an inbox to get started",
+ "API": {
+ "SUCCESS_MESSAGE": "Inbox successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Assignment policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete assignment policy"
+ }
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent capacity",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "POPOVER": "Added agents",
+ "EDIT": "Edytuj"
+ },
+ "NO_RECORDS_FOUND": "No agent capacity policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create agent capacity policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy created successfully",
+ "ERROR_MESSAGE": "Failed to create agent capacity policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit agent capacity policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_AGENT_DIALOG": {
+ "TITLE": "Add agent",
+ "DESCRIPTION": "{agentName} is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Anuluj"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update agent capacity policy"
+ },
+ "AGENT_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Agent added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Agent removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove agent from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Opis:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "INBOX_CAPACITY_LIMIT": {
+ "LABEL": "Inbox capacity limits",
+ "ADD_BUTTON": "Add inbox",
+ "FIELD": {
+ "SELECT_INBOX": "Select inbox",
+ "MAX_CONVERSATIONS": "Max conversations",
+ "SET_LIMIT": "Set limit"
+ },
+ "EMPTY_STATE": "No inbox limit set"
+ },
+ "EXCLUSION_RULES": {
+ "LABEL": "Exclusion rules",
+ "DESCRIPTION": "Conversations that satisfy the following conditions would not count towards agent capacity",
+ "TAGS": {
+ "LABEL": "Exclude conversations tagged with specific labels",
+ "ADD_TAG": "add tag",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select tags to add"
+ },
+ "EMPTY_STATE": "No tags added to this policy."
+ },
+ "DURATION": {
+ "LABEL": "Exclude conversations older than a specified duration",
+ "PLACEHOLDER": "Set time"
+ }
+ },
+ "USERS": {
+ "LABEL": "Assigned agents",
+ "DESCRIPTION": "Add agents for which this policy will be applicable.",
+ "ADD_BUTTON": "Add agent",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select agents to add",
+ "ADD_BUTTON": "Dodaj"
+ },
+ "EMPTY_STATE": "No agents added",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Agent capacity policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete agent capacity policy"
+ }
+ },
+ "DELETE_POLICY": {
+ "TITLE": "Delete policy",
+ "DESCRIPTION": "Are you sure you want to delete this policy? This action cannot be undone.",
+ "CONFIRM_BUTTON_LABEL": "Usuń",
+ "CANCEL_BUTTON_LABEL": "Anuluj"
+ }
}
}
diff --git a/app/javascript/dashboard/i18n/locale/pl/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/pl/whatsappTemplates.json
index e1946af25..100d7dbff 100644
--- a/app/javascript/dashboard/i18n/locale/pl/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/pl/whatsappTemplates.json
@@ -40,6 +40,7 @@
"BUTTON_LABEL": "Button {index}",
"COUPON_CODE": "Enter coupon code (max 15 chars)",
"MEDIA_URL_LABEL": "Enter {type} URL",
+ "DOCUMENT_NAME_PLACEHOLDER": "Enter document filename (e.g., Invoice_2025.pdf)",
"BUTTON_PARAMETER": "Enter button parameter"
}
}
diff --git a/app/javascript/dashboard/i18n/locale/pt/automation.json b/app/javascript/dashboard/i18n/locale/pt/automation.json
index 0a720f528..b97cf76cc 100644
--- a/app/javascript/dashboard/i18n/locale/pt/automation.json
+++ b/app/javascript/dashboard/i18n/locale/pt/automation.json
@@ -177,7 +177,8 @@
"REFERER_LINK": "Link de referência",
"ASSIGNEE_NAME": "Atribuído",
"TEAM_NAME": "Equipa",
- "PRIORITY": "Prioridade"
+ "PRIORITY": "Prioridade",
+ "LABELS": "Etiquetas"
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/pt/contact.json b/app/javascript/dashboard/i18n/locale/pt/contact.json
index b9f74435c..16ad4872f 100644
--- a/app/javascript/dashboard/i18n/locale/pt/contact.json
+++ b/app/javascript/dashboard/i18n/locale/pt/contact.json
@@ -610,6 +610,15 @@
"SEND_MESSAGE": "Enviar mensagem"
}
},
+ "TWILIO_OPTIONS": {
+ "LABEL": "Select template",
+ "SEARCH_PLACEHOLDER": "Search templates",
+ "EMPTY_STATE": "No templates found",
+ "TEMPLATE_PARSER": {
+ "BACK": "Voltar",
+ "SEND_MESSAGE": "Enviar mensagem"
+ }
+ },
"ACTION_BUTTONS": {
"DISCARD": "Discard",
"SEND": "Send ({keyCode})"
diff --git a/app/javascript/dashboard/i18n/locale/pt/contactFilters.json b/app/javascript/dashboard/i18n/locale/pt/contactFilters.json
index efdf0540d..ca6a363da 100644
--- a/app/javascript/dashboard/i18n/locale/pt/contactFilters.json
+++ b/app/javascript/dashboard/i18n/locale/pt/contactFilters.json
@@ -48,7 +48,8 @@
"CREATED_AT": "Criado em",
"LAST_ACTIVITY": "Última atividade",
"REFERER_LINK": "Link de referência",
- "BLOCKED": "Bloqueado"
+ "BLOCKED": "Bloqueado",
+ "LABELS": "Etiquetas"
},
"GROUPS": {
"STANDARD_FILTERS": "Filtros padrão",
diff --git a/app/javascript/dashboard/i18n/locale/pt/contentTemplates.json b/app/javascript/dashboard/i18n/locale/pt/contentTemplates.json
new file mode 100644
index 000000000..0a24e2378
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/pt/contentTemplates.json
@@ -0,0 +1,51 @@
+{
+ "CONTENT_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Twilio Templates",
+ "SUBTITLE": "Select the Twilio template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Buscar templates",
+ "NO_TEMPLATES_FOUND": "Nenhum template encontrado para",
+ "NO_CONTENT": "Sem conteúdo",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Categoria",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No Twilio templates available. Click refresh to sync templates from Twilio.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Idioma",
+ "TEMPLATE_BODY": "Corpo do Template",
+ "CATEGORY": "Categoria"
+ },
+ "TYPES": {
+ "MEDIA": "Media",
+ "QUICK_REPLY": "Quick Reply",
+ "TEXT": "Texto"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variáveis",
+ "LANGUAGE": "Idioma",
+ "CATEGORY": "Categoria",
+ "VARIABLE_PLACEHOLDER": "Digite o valor {variable}",
+ "GO_BACK_LABEL": "Voltar",
+ "SEND_MESSAGE_LABEL": "Enviar mensagem",
+ "FORM_ERROR_MESSAGE": "Preencha todas as variáveis antes de enviar",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "MEDIA_URL_LABEL": "Enter full media URL",
+ "MEDIA_URL_PLACEHOLDER": "https://example.com/image.jpg"
+ },
+ "FORM": {
+ "BACK_BUTTON": "Voltar",
+ "SEND_MESSAGE_BUTTON": "Enviar mensagem"
+ }
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/pt/conversation.json b/app/javascript/dashboard/i18n/locale/pt/conversation.json
index fba7e5b09..447c0e243 100644
--- a/app/javascript/dashboard/i18n/locale/pt/conversation.json
+++ b/app/javascript/dashboard/i18n/locale/pt/conversation.json
@@ -35,6 +35,11 @@
"API_HOURS_WINDOW": "Só pode responder a esta conversa dentro de {hours} horas",
"NOT_ASSIGNED_TO_YOU": "Esta conversa não está atribuída a si. Gostaria de atribuir esta conversa a si mesmo?",
"ASSIGN_TO_ME": "Atribuir a mim",
+ "BOT_HANDOFF_MESSAGE": "You are responding to a conversation which is currently handled by an assistant or a bot.",
+ "BOT_HANDOFF_ACTION": "Mark open and assign to you",
+ "BOT_HANDOFF_REOPEN_ACTION": "Mark conversation open",
+ "BOT_HANDOFF_SUCCESS": "Conversation has been handed over to you",
+ "BOT_HANDOFF_ERROR": "Failed to take over the conversation. Please try again.",
"TWILIO_WHATSAPP_CAN_REPLY": "Só pode responder utilizando uma mensagem modelo, porque",
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "Mensagens bloqueadas durante 24 horas",
"OLD_INSTAGRAM_INBOX_REPLY_BANNER": "Esta conta do Instagram foi migrada para a nova caixa de entrada do canal Instagram. Todas as novas mensagens aparecerão lá. Já não poderá enviar mensagens a partir desta conversa.",
@@ -66,6 +71,17 @@
"SHOW_LABELS": "Mostrar etiquetas",
"HIDE_LABELS": "Ocultar etiquetas"
},
+ "VOICE_CALL": {
+ "INCOMING_CALL": "Incoming call",
+ "OUTGOING_CALL": "Outgoing call",
+ "CALL_IN_PROGRESS": "Call in progress",
+ "NO_ANSWER": "No answer",
+ "MISSED_CALL": "Missed call",
+ "CALL_ENDED": "Call ended",
+ "NOT_ANSWERED_YET": "Not answered yet",
+ "THEY_ANSWERED": "They answered",
+ "YOU_ANSWERED": "You answered"
+ },
"HEADER": {
"RESOLVE_ACTION": "Resolver",
"REOPEN_ACTION": "Reabrir",
diff --git a/app/javascript/dashboard/i18n/locale/pt/helpCenter.json b/app/javascript/dashboard/i18n/locale/pt/helpCenter.json
index 1f4468d43..1413196f2 100644
--- a/app/javascript/dashboard/i18n/locale/pt/helpCenter.json
+++ b/app/javascript/dashboard/i18n/locale/pt/helpCenter.json
@@ -741,7 +741,8 @@
"LIVE_CHAT_WIDGET": {
"LABEL": "Live chat widget",
"PLACEHOLDER": "Select live chat widget",
- "HELP_TEXT": "Select a live chat widget that will appear on your help center"
+ "HELP_TEXT": "Select a live chat widget that will appear on your help center",
+ "NONE_OPTION": "No widget"
},
"BRAND_COLOR": {
"LABEL": "Brand color"
@@ -807,6 +808,58 @@
"ERROR_MESSAGE": "Unable to update portal"
}
}
+ },
+ "PDF_UPLOAD": {
+ "TITLE": "Upload PDF Document",
+ "DESCRIPTION": "Upload a PDF document to automatically generate FAQs using AI",
+ "DRAG_DROP_TEXT": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "ADDITIONAL_CONTEXT_LABEL": "Additional Context (Optional)",
+ "ADDITIONAL_CONTEXT_PLACEHOLDER": "Provide any additional context or instructions for FAQ generation...",
+ "UPLOADING": "A carregar...",
+ "UPLOAD": "Upload & Process",
+ "CANCEL": "Cancelar",
+ "ERROR_INVALID_TYPE": "Please select a valid PDF file",
+ "ERROR_FILE_TOO_LARGE": "File size must be less than 512MB",
+ "ERROR_UPLOAD_FAILED": "Failed to upload PDF. Please try again."
+ },
+ "PDF_DOCUMENTS": {
+ "TITLE": "PDF Documents",
+ "DESCRIPTION": "Manage uploaded PDF documents and generate FAQs from them",
+ "UPLOAD_PDF": "Upload PDF",
+ "UPLOAD_FIRST_PDF": "Upload your first PDF",
+ "UPLOADED_BY": "Uploaded by",
+ "GENERATE_FAQS": "Generate FAQs",
+ "GENERATING": "A gerar...",
+ "CONFIRM_DELETE": "Tem a certeza que pretende apagar o {filename}?",
+ "EMPTY_STATE": {
+ "TITLE": "No PDF documents yet",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQs using AI"
+ },
+ "STATUS": {
+ "UPLOADED": "Ready",
+ "PROCESSING": "Processing",
+ "PROCESSED": "Concluída",
+ "FAILED": "Failed"
+ }
+ },
+ "CONTENT_GENERATION": {
+ "TITLE": "Content Generation",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQ content using AI",
+ "UPLOAD_TITLE": "Upload PDF Document",
+ "DRAG_DROP": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "UPLOADING": "Processing document...",
+ "UPLOAD_SUCCESS": "Document processed successfully!",
+ "UPLOAD_ERROR": "Failed to upload document. Please try again.",
+ "INVALID_FILE_TYPE": "Please select a valid PDF file",
+ "FILE_TOO_LARGE": "File size must be less than 512MB",
+ "GENERATED_CONTENT": "Generated FAQ Content",
+ "PUBLISH_SELECTED": "Publish Selected",
+ "PUBLISHING": "Publishing...",
+ "FROM_DOCUMENT": "From document",
+ "NO_CONTENT": "No generated content available. Upload a PDF document to get started.",
+ "LOADING": "Loading generated content..."
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/pt/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/pt/inboxMgmt.json
index b7b4ec61d..50e33cb37 100644
--- a/app/javascript/dashboard/i18n/locale/pt/inboxMgmt.json
+++ b/app/javascript/dashboard/i18n/locale/pt/inboxMgmt.json
@@ -272,8 +272,8 @@
},
"SUBMIT_BUTTON": "Criar Canal do WhatsApp",
"EMBEDDED_SIGNUP": {
- "TITLE": "Quick Setup with Meta",
- "DESC": "You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
+ "TITLE": "Quick setup with Meta",
+ "DESC": "Use the WhatsApp Embedded Signup flow to quickly connect new numbers. You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
"BENEFITS": {
"TITLE": "Benefits of Embedded Signup:",
"EASY_SETUP": "No manual configuration required",
@@ -281,9 +281,8 @@
"AUTO_CONFIG": "Automatic webhook and phone number configuration"
},
"LEARN_MORE": {
- "TEXT": "To learn more about integrated signup, pricing, and limitations, visit",
- "LINK_TEXT": "this link.",
- "LINK_URL": "https://developers.facebook.com/docs/whatsapp/embedded-signup/custom-flows/onboarding-business-app-users#limitations"
+ "TEXT": "To learn more about integrated signup, pricing, and limitations, visit {link}.",
+ "LINK_TEXT": "this link"
},
"SUBMIT_BUTTON": "Connect with WhatsApp Business",
"AUTH_PROCESSING": "Authenticating with Meta",
@@ -296,7 +295,9 @@
"INVALID_BUSINESS_DATA": "Invalid business data received from Facebook. Please try again.",
"SIGNUP_ERROR": "Signup error occurred",
"AUTH_NOT_COMPLETED": "Authentication not completed. Please restart the process.",
- "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured"
+ "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured",
+ "MANUAL_FALLBACK": "If your number is already connected to the WhatsApp Business Platform (API), or if you’re a tech provider onboarding your own number, please use the {link} flow",
+ "MANUAL_LINK_TEXT": "manual setup flow"
},
"API": {
"ERROR_MESSAGE": "Não foi possível gravar o canal do WhatsApp"
@@ -424,7 +425,51 @@
},
"AUTH": {
"TITLE": "Escolher um canal",
- "DESC": "O Chatwoot suporta widgets de live-chat, Facebook Messenger, perfis do Twitter, WhatsApp, E-mails, etc., como canais. Se pretende criar um canal personalizado, pode fazê-lo usando o canal API. Para começar, escolha um dos canais abaixo."
+ "DESC": "O Chatwoot suporta widgets de live-chat, Facebook Messenger, perfis do Twitter, WhatsApp, E-mails, etc., como canais. Se pretende criar um canal personalizado, pode fazê-lo usando o canal API. Para começar, escolha um dos canais abaixo.",
+ "TITLE_NEXT": "Complete the setup",
+ "TITLE_FINISH": "Pronto!",
+ "CHANNEL": {
+ "WEBSITE": {
+ "TITLE": "Website",
+ "DESCRIPTION": "Create a live-chat widget"
+ },
+ "FACEBOOK": {
+ "TITLE": "Facebook",
+ "DESCRIPTION": "Connect your Facebook page"
+ },
+ "WHATSAPP": {
+ "TITLE": "WhatsApp",
+ "DESCRIPTION": "Support your customers on WhatsApp"
+ },
+ "EMAIL": {
+ "TITLE": "E-mail",
+ "DESCRIPTION": "Connect with Gmail, Outlook, or other providers"
+ },
+ "SMS": {
+ "TITLE": "SMS",
+ "DESCRIPTION": "Integrate SMS channel with Twilio or bandwidth"
+ },
+ "API": {
+ "TITLE": "API",
+ "DESCRIPTION": "Make a custom channel using our API"
+ },
+ "TELEGRAM": {
+ "TITLE": "Telegram",
+ "DESCRIPTION": "Configure Telegram channel using Bot token"
+ },
+ "LINE": {
+ "TITLE": "Line",
+ "DESCRIPTION": "Integrate your Line channel"
+ },
+ "INSTAGRAM": {
+ "TITLE": "Instagram",
+ "DESCRIPTION": "Connect your instagram account"
+ },
+ "VOICE": {
+ "TITLE": "Voz",
+ "DESCRIPTION": "Integrate with Twilio Voice"
+ }
+ }
},
"AGENTS": {
"TITLE": "Agentes",
@@ -478,7 +523,10 @@
"MESSAGE": "Agora, pode conectar-se com os seus clientes através do seu novo canal.",
"BUTTON_TEXT": "Ir para a caixa de entrada",
"MORE_SETTINGS": "Mais configurações",
- "WEBSITE_SUCCESS": "Acabou de criar um canal de site com sucesso. Copie o código mostrado abaixo e cole-o no seu site. Da próxima vez que um cliente usar o chat em tempo real, a conversa aparecerá automaticamente na sua caixa de entrada."
+ "WEBSITE_SUCCESS": "Acabou de criar um canal de site com sucesso. Copie o código mostrado abaixo e cole-o no seu site. Da próxima vez que um cliente usar o chat em tempo real, a conversa aparecerá automaticamente na sua caixa de entrada.",
+ "WHATSAPP_QR_INSTRUCTION": "Scan the QR code above to quickly test your WhatsApp inbox",
+ "MESSENGER_QR_INSTRUCTION": "Scan the QR code above to quickly test your Facebook Messenger inbox",
+ "TELEGRAM_QR_INSTRUCTION": "Scan the QR code above to quickly test your Telegram inbox"
},
"REAUTH": "Reautorizar",
"VIEW": "Ver",
@@ -868,9 +916,18 @@
"SCRIPT_SETTINGS": "\n window.chatwootSettings = {options};"
},
"EMAIL_PROVIDERS": {
- "MICROSOFT": "Microsoft",
- "GOOGLE": "Google",
- "OTHER_PROVIDERS": "Outros fornecedores"
+ "MICROSOFT": {
+ "TITLE": "Microsoft",
+ "DESCRIPTION": "Connect with Microsoft"
+ },
+ "GOOGLE": {
+ "TITLE": "Google",
+ "DESCRIPTION": "Connect with Google"
+ },
+ "OTHER_PROVIDERS": {
+ "TITLE": "Outros fornecedores",
+ "DESCRIPTION": "Connect with Other Providers"
+ }
},
"CHANNELS": {
"MESSENGER": "Messenger",
diff --git a/app/javascript/dashboard/i18n/locale/pt/integrations.json b/app/javascript/dashboard/i18n/locale/pt/integrations.json
index 2a86bd97f..785036a76 100644
--- a/app/javascript/dashboard/i18n/locale/pt/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/pt/integrations.json
@@ -704,11 +704,28 @@
"ERROR_MESSAGE": "There was an error creating the document, please try again."
},
"FORM": {
+ "TYPE": {
+ "LABEL": "Document Type",
+ "URL": "URL",
+ "PDF": "PDF File"
+ },
"URL": {
"LABEL": "URL",
"PLACEHOLDER": "Enter the URL of the document",
"ERROR": "Please provide a valid URL for the document"
},
+ "PDF_FILE": {
+ "LABEL": "PDF File",
+ "CHOOSE_FILE": "Choose PDF file",
+ "ERROR": "Please select a PDF file",
+ "HELP_TEXT": "Maximum file size: 10MB",
+ "INVALID_TYPE": "Please select a valid PDF file",
+ "TOO_LARGE": "File size exceeds 10MB limit"
+ },
+ "NAME": {
+ "LABEL": "Document Name (Optional)",
+ "PLACEHOLDER": "Enter a name for the document"
+ },
"ASSISTANT": {
"LABEL": "Assistant",
"PLACEHOLDER": "Select the assistant",
@@ -744,6 +761,7 @@
"SELECTED": "{count} selected",
"SELECT_ALL": "Selecionar todas ({count})",
"UNSELECT_ALL": "Desmarcar todas ({count})",
+ "SEARCH_PLACEHOLDER": "Search FAQs...",
"BULK_APPROVE_BUTTON": "Approve",
"BULK_DELETE_BUTTON": "Excluir",
"BULK_APPROVE": {
diff --git a/app/javascript/dashboard/i18n/locale/pt/mfa.json b/app/javascript/dashboard/i18n/locale/pt/mfa.json
new file mode 100644
index 000000000..2fb38bcdf
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/pt/mfa.json
@@ -0,0 +1,106 @@
+{
+ "MFA_SETTINGS": {
+ "TITLE": "Two-Factor Authentication",
+ "SUBTITLE": "Secure your account with TOTP-based authentication",
+ "DESCRIPTION": "Add an extra layer of security to your account using a time-based one-time password (TOTP)",
+ "STATUS_TITLE": "Authentication Status",
+ "STATUS_DESCRIPTION": "Manage your two-factor authentication settings and backup recovery codes",
+ "ENABLED": "Ativado",
+ "DISABLED": "Inativo",
+ "STATUS_ENABLED": "Two-factor authentication is active",
+ "STATUS_ENABLED_DESC": "Your account is protected with an additional layer of security",
+ "ENABLE_BUTTON": "Enable Two-Factor Authentication",
+ "ENHANCE_SECURITY": "Enhance Your Account Security",
+ "ENHANCE_SECURITY_DESC": "Two-factor authentication adds an extra layer of security by requiring a verification code from your authenticator app in addition to your password.",
+ "SETUP": {
+ "STEP_NUMBER_1": "1",
+ "STEP_NUMBER_2": "2",
+ "STEP1_TITLE": "Scan QR Code with Your Authenticator App",
+ "STEP1_DESCRIPTION": "Use Google Authenticator, Authy, or any TOTP-compatible app",
+ "LOADING_QR": "A carregar...",
+ "MANUAL_ENTRY": "Can't scan? Enter code manually",
+ "SECRET_KEY": "Secret Key",
+ "COPY": "Copiar",
+ "ENTER_CODE": "Enter the 6-digit code from your authenticator app",
+ "ENTER_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify & Continue",
+ "CANCEL": "Cancelar",
+ "ERROR_STARTING": "MFA not enabled. Please contact administrator.",
+ "INVALID_CODE": "Invalid verification code",
+ "SECRET_COPIED": "Secret key copied to clipboard",
+ "SUCCESS": "Two-factor authentication has been enabled successfully"
+ },
+ "BACKUP": {
+ "TITLE": "Save Your Backup Codes",
+ "DESCRIPTION": "Keep these codes safe. Each can be used once if you lose access to your authenticator",
+ "IMPORTANT": "Important:",
+ "IMPORTANT_NOTE": " Save these codes in a secure location. You won't be able to see them again.",
+ "DOWNLOAD": "Descarregar",
+ "COPY_ALL": "Copy All",
+ "CONFIRM": "I have saved my backup codes in a secure location and understand that I won't be able to see them again",
+ "COMPLETE_SETUP": "Complete Setup",
+ "CODES_COPIED": "Backup codes copied to clipboard"
+ },
+ "MANAGEMENT": {
+ "BACKUP_CODES": "Backup Codes",
+ "BACKUP_CODES_DESC": "Generate new codes if you've lost or used your existing ones",
+ "REGENERATE": "Regenerate Backup Codes",
+ "DISABLE_MFA": "Disable 2FA",
+ "DISABLE_MFA_DESC": "Remove two-factor authentication from your account",
+ "DISABLE_BUTTON": "Disable Two-Factor Authentication"
+ },
+ "DISABLE": {
+ "TITLE": "Disable Two-Factor Authentication",
+ "DESCRIPTION": "You'll need to enter your password and a verification code to disable two-factor authentication.",
+ "PASSWORD": "Palavra-passe",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Disable 2FA",
+ "CANCEL": "Cancelar",
+ "SUCCESS": "Two-factor authentication has been disabled",
+ "ERROR": "Failed to disable MFA. Please check your credentials."
+ },
+ "REGENERATE": {
+ "TITLE": "Regenerate Backup Codes",
+ "DESCRIPTION": "This will invalidate your existing backup codes and generate new ones. Enter your verification code to continue.",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Generate New Codes",
+ "CANCEL": "Cancelar",
+ "NEW_CODES_TITLE": "New Backup Codes Generated",
+ "NEW_CODES_DESC": "Your old backup codes have been invalidated. Save these new codes in a secure location.",
+ "CODES_IMPORTANT": "Important:",
+ "CODES_IMPORTANT_NOTE": " Each code can only be used once. Save them before closing this window.",
+ "DOWNLOAD_CODES": "Download Codes",
+ "COPY_ALL_CODES": "Copy All Codes",
+ "CODES_SAVED": "I've Saved My Codes",
+ "SUCCESS": "New backup codes have been generated",
+ "ERROR": "Failed to regenerate backup codes"
+ }
+ },
+ "MFA_VERIFICATION": {
+ "TITLE": "Two-Factor Authentication",
+ "DESCRIPTION": "Enter your verification code to continue",
+ "AUTHENTICATOR_APP": "Authenticator App",
+ "BACKUP_CODE": "Backup Code",
+ "ENTER_OTP_CODE": "Enter 6-digit code from your authenticator app",
+ "ENTER_BACKUP_CODE": "Enter one of your backup codes",
+ "BACKUP_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify",
+ "TRY_ANOTHER_METHOD": "Try another verification method",
+ "CANCEL_LOGIN": "Cancel and return to login",
+ "HELP_TEXT": "Having trouble signing in?",
+ "LEARN_MORE": "Learn more about 2FA",
+ "HELP_MODAL": {
+ "TITLE": "Two-Factor Authentication Help",
+ "AUTHENTICATOR_TITLE": "Using an Authenticator App",
+ "AUTHENTICATOR_DESC": "Open your authenticator app (Google Authenticator, Authy, etc.) and enter the 6-digit code shown for your account.",
+ "BACKUP_TITLE": "Using a Backup Code",
+ "BACKUP_DESC": "If you don't have access to your authenticator app, you can use one of the backup codes you saved when setting up 2FA. Each code can only be used once.",
+ "CONTACT_TITLE": "Need More Help?",
+ "CONTACT_DESC_CLOUD": "If you've lost access to both your authenticator app and backup codes, please reach out to Chatwoot support for assistance.",
+ "CONTACT_DESC_SELF_HOSTED": "If you've lost access to both your authenticator app and backup codes, please contact your administrator for assistance."
+ },
+ "VERIFICATION_FAILED": "Verification failed. Please try again."
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/pt/settings.json b/app/javascript/dashboard/i18n/locale/pt/settings.json
index 0f78e6469..72b46a67a 100644
--- a/app/javascript/dashboard/i18n/locale/pt/settings.json
+++ b/app/javascript/dashboard/i18n/locale/pt/settings.json
@@ -51,6 +51,13 @@
"LARGER": "Larger",
"EXTRA_LARGE": "Extra Large"
}
+ },
+ "LANGUAGE": {
+ "TITLE": "Preferred Language",
+ "NOTE": "Choose the language you want to use.",
+ "UPDATE_SUCCESS": "Your Language settings have been updated successfully",
+ "UPDATE_ERROR": "There is an error while updating the language settings, please try again",
+ "USE_ACCOUNT_DEFAULT": "Use account default"
}
},
"MESSAGE_SIGNATURE_SECTION": {
@@ -73,6 +80,11 @@
"NOTE": "Atualizar sua senha irá redefinir seus logins em vários dispositivos.",
"BTN_TEXT": "Alterar password"
},
+ "SECURITY_SECTION": {
+ "TITLE": "Security",
+ "NOTE": "Manage additional security features for your account.",
+ "MFA_BUTTON": "Manage Two-Factor Authentication"
+ },
"ACCESS_TOKEN": {
"TITLE": "Token de acesso",
"NOTE": "Este token pode ser usado se você estiver construindo uma integração baseada em API",
@@ -330,6 +342,7 @@
"REPORTS_LABEL": "Etiquetas",
"REPORTS_INBOX": "Caixa de Entrada",
"REPORTS_TEAM": "Equipa",
+ "AGENT_ASSIGNMENT": "Agent Assignment",
"SET_AVAILABILITY_TITLE": "Defina-se como",
"SET_YOUR_AVAILABILITY": "Definir disponibilidade",
"SLA": "SLA",
@@ -350,7 +363,8 @@
"INFO_TEXT": "Deixar o sistema alterar automaticamente o seu estado para offline quando não estiver a usar a app ou o painel.",
"INFO_SHORT": "Automatically mark offline when you aren't using the app."
},
- "DOCS": "Ler documentos"
+ "DOCS": "Ler documentos",
+ "SECURITY": "Security"
},
"BILLING_SETTINGS": {
"TITLE": "Cobrança",
@@ -382,6 +396,77 @@
},
"NO_BILLING_USER": "Os seus dados de pagamento estão a ser configurados. Atualize a página e tente novamente."
},
+ "SECURITY_SETTINGS": {
+ "TITLE": "Security",
+ "DESCRIPTION": "Manage your account security settings.",
+ "LINK_TEXT": "Learn more about SAML SSO",
+ "SAML": {
+ "TITLE": "SAML SSO",
+ "NOTE": "Configure SAML single sign-on for your account. Users will authenticate through your identity provider instead of using email/password.",
+ "ACS_URL": {
+ "LABEL": "ACS URL",
+ "TOOLTIP": "Assertion Consumer Service URL - Configure this URL in your IdP as the destination for SAML responses"
+ },
+ "SSO_URL": {
+ "LABEL": "SSO URL",
+ "HELP": "The URL where SAML authentication requests will be sent",
+ "PLACEHOLDER": "https://your-idp.com/saml/sso"
+ },
+ "CERTIFICATE": {
+ "LABEL": "Signing certificate in PEM format",
+ "HELP": "The public certificate from your identity provider used to verify SAML responses",
+ "PLACEHOLDER": "-----BEGIN CERTIFICATE-----\nMIIC..."
+ },
+ "FINGERPRINT": {
+ "LABEL": "Fingerprint",
+ "TOOLTIP": "SHA-1 fingerprint of the certificate - Use this to verify the certificate in your IdP configuration"
+ },
+ "COPY_SUCCESS": "Código copiado com sucesso para área de transferência",
+ "SP_ENTITY_ID": {
+ "LABEL": "SP Entity ID",
+ "HELP": "Unique identifier for this application as a service provider (auto-generated).",
+ "TOOLTIP": "Unique identifier for Chatwoot as the Service Provider - Configure this in your IdP settings"
+ },
+ "IDP_ENTITY_ID": {
+ "LABEL": "Identity Provider Entity ID",
+ "HELP": "Unique identifier for your identity provider (usually found in IdP configuration)",
+ "PLACEHOLDER": "https://your-idp.com/saml"
+ },
+ "UPDATE_BUTTON": "Update SAML Settings",
+ "API": {
+ "SUCCESS": "SAML settings updated successfully",
+ "ERROR": "Failed to update SAML settings",
+ "ERROR_LOADING": "Failed to load SAML settings",
+ "DISABLED": "SAML settings disabled successfully"
+ },
+ "VALIDATION": {
+ "REQUIRED_FIELDS": "SSO URL, Identity Provider Entity ID, and Certificate are required fields",
+ "SSO_URL_ERROR": "Please enter a valid SSO URL",
+ "CERTIFICATE_ERROR": "Certificate is required",
+ "IDP_ENTITY_ID_ERROR": "Identity Provider Entity ID is required"
+ },
+ "ENTERPRISE_PAYWALL": {
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade to an Enterprise plan to access SAML single sign-on and other advanced security features.",
+ "ASK_ADMIN": "Por favor, entre em contato com o administrador para atualização."
+ },
+ "PAYWALL": {
+ "TITLE": "Upgrade to enable SAML SSO",
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade your plan to get access to SAML single sign-on and other advanced features.",
+ "UPGRADE_NOW": "Fazer upgrade agora",
+ "CANCEL_ANYTIME": "Pode alterar ou cancelar o plano a qualquer momento"
+ },
+ "ATTRIBUTE_MAPPING": {
+ "TITLE": "SAML Attribute Setup",
+ "DESCRIPTION": "The following attribute mappings must be configured in your identity provider"
+ },
+ "INFO_SECTION": {
+ "TITLE": "Service Provider Information",
+ "TOOLTIP": "Copy these values and configure them in your Identity Provider to establish the SAML connection"
+ }
+ }
+ },
"CREATE_ACCOUNT": {
"NO_ACCOUNT_WARNING": "Não conseguimos encontrar nenhuma conta do Chatwoot. Por favor, crie uma nova conta para continuar.",
"NEW_ACCOUNT": "Nova conta",
@@ -418,5 +503,255 @@
"SWITCH_TO_REPLY": "Mudar para resposta",
"TOGGLE_SNOOZE_DROPDOWN": "Ativar/desativar suspensos"
}
+ },
+ "ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent assignment",
+ "DESCRIPTION": "Define policies to effectively manage workload and route conversations based on the needs of inboxes and agents. Learn more here"
+ },
+ "ASSIGNMENT_POLICY": {
+ "TITLE": "Assignment policy",
+ "DESCRIPTION": "Manage how conversations get assigned in inboxes.",
+ "FEATURES": [
+ "Assign by conversations evenly or by available capacity",
+ "Add fair distribution rules to avoid overloading any agent",
+ "Add inboxes to a policy - one policy per inbox"
+ ]
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "TITLE": "Agent capacity policy",
+ "DESCRIPTION": "Manage workload for agents.",
+ "FEATURES": [
+ "Define maximum conversations per inbox",
+ "Create exceptions based on labels and time",
+ "Add agents to a policy - one policy per agent"
+ ]
+ }
+ },
+ "AGENT_ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Assignment policy",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "ORDER": "Order",
+ "PRIORITY": "Prioridade",
+ "ACTIVE": "Ativa",
+ "INACTIVE": "Inactive",
+ "POPOVER": "Added inboxes",
+ "EDIT": "Editar"
+ },
+ "NO_RECORDS_FOUND": "No assignment policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create assignment policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy created successfully",
+ "ERROR_MESSAGE": "Failed to create assignment policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit assignment policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_INBOX_DIALOG": {
+ "TITLE": "Add inbox",
+ "DESCRIPTION": "{inboxName} inbox is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Cancelar"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update assignment policy"
+ },
+ "INBOX_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Inbox added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Inbox removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove inbox from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Descrição:",
+ "PLACEHOLDER": "Inserir descrição"
+ },
+ "STATUS": {
+ "LABEL": "Estado:",
+ "PLACEHOLDER": "Selecionar estado",
+ "ACTIVE": "Policy is active",
+ "INACTIVE": "Policy is inactive"
+ },
+ "ASSIGNMENT_ORDER": {
+ "LABEL": "Assignment order",
+ "ROUND_ROBIN": {
+ "LABEL": "Round robin",
+ "DESCRIPTION": "Assign conversations evenly among agents."
+ },
+ "BALANCED": {
+ "LABEL": "Balanced",
+ "DESCRIPTION": "Assign conversations based on available capacity."
+ }
+ },
+ "ASSIGNMENT_PRIORITY": {
+ "LABEL": "Assignment priority",
+ "EARLIEST_CREATED": {
+ "LABEL": "Earliest created",
+ "DESCRIPTION": "The conversation that was created first gets assigned first."
+ },
+ "LONGEST_WAITING": {
+ "LABEL": "Longest waiting",
+ "DESCRIPTION": "The conversation waiting the longest gets assigned first."
+ }
+ },
+ "FAIR_DISTRIBUTION": {
+ "LABEL": "Fair distribution policy",
+ "DESCRIPTION": "Set the maximum number of conversations that can be assigned per agent within a time window to avoid overloading any one agent. This required field defaults to 100 conversations per hour.",
+ "INPUT_MAX": "Assign max",
+ "DURATION": "Conversations per agent in every"
+ },
+ "INBOXES": {
+ "LABEL": "Added inboxes",
+ "DESCRIPTION": "Add inboxes for which this policy will be applicable.",
+ "ADD_BUTTON": "Add inbox",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select inboxes to add",
+ "ADD_BUTTON": "Adicionar"
+ },
+ "EMPTY_STATE": "No inboxes added to this policy, add an inbox to get started",
+ "API": {
+ "SUCCESS_MESSAGE": "Inbox successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Assignment policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete assignment policy"
+ }
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent capacity",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "POPOVER": "Added agents",
+ "EDIT": "Editar"
+ },
+ "NO_RECORDS_FOUND": "No agent capacity policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create agent capacity policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy created successfully",
+ "ERROR_MESSAGE": "Failed to create agent capacity policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit agent capacity policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_AGENT_DIALOG": {
+ "TITLE": "Add agent",
+ "DESCRIPTION": "{agentName} is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Cancelar"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update agent capacity policy"
+ },
+ "AGENT_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Agent added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Agent removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove agent from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Descrição:",
+ "PLACEHOLDER": "Inserir descrição"
+ },
+ "INBOX_CAPACITY_LIMIT": {
+ "LABEL": "Inbox capacity limits",
+ "ADD_BUTTON": "Add inbox",
+ "FIELD": {
+ "SELECT_INBOX": "Select inbox",
+ "MAX_CONVERSATIONS": "Max conversations",
+ "SET_LIMIT": "Set limit"
+ },
+ "EMPTY_STATE": "No inbox limit set"
+ },
+ "EXCLUSION_RULES": {
+ "LABEL": "Exclusion rules",
+ "DESCRIPTION": "Conversations that satisfy the following conditions would not count towards agent capacity",
+ "TAGS": {
+ "LABEL": "Exclude conversations tagged with specific labels",
+ "ADD_TAG": "add tag",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select tags to add"
+ },
+ "EMPTY_STATE": "No tags added to this policy."
+ },
+ "DURATION": {
+ "LABEL": "Exclude conversations older than a specified duration",
+ "PLACEHOLDER": "Set time"
+ }
+ },
+ "USERS": {
+ "LABEL": "Assigned agents",
+ "DESCRIPTION": "Add agents for which this policy will be applicable.",
+ "ADD_BUTTON": "Add agent",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select agents to add",
+ "ADD_BUTTON": "Adicionar"
+ },
+ "EMPTY_STATE": "No agents added",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Agent capacity policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete agent capacity policy"
+ }
+ },
+ "DELETE_POLICY": {
+ "TITLE": "Delete policy",
+ "DESCRIPTION": "Are you sure you want to delete this policy? This action cannot be undone.",
+ "CONFIRM_BUTTON_LABEL": "Excluir",
+ "CANCEL_BUTTON_LABEL": "Cancelar"
+ }
}
}
diff --git a/app/javascript/dashboard/i18n/locale/pt/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/pt/whatsappTemplates.json
index 45100216d..ae6878890 100644
--- a/app/javascript/dashboard/i18n/locale/pt/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/pt/whatsappTemplates.json
@@ -40,6 +40,7 @@
"BUTTON_LABEL": "Button {index}",
"COUPON_CODE": "Enter coupon code (max 15 chars)",
"MEDIA_URL_LABEL": "Enter {type} URL",
+ "DOCUMENT_NAME_PLACEHOLDER": "Enter document filename (e.g., Invoice_2025.pdf)",
"BUTTON_PARAMETER": "Enter button parameter"
}
}
diff --git a/app/javascript/dashboard/i18n/locale/pt_BR/attributesMgmt.json b/app/javascript/dashboard/i18n/locale/pt_BR/attributesMgmt.json
index 5011e70b9..799639ebc 100644
--- a/app/javascript/dashboard/i18n/locale/pt_BR/attributesMgmt.json
+++ b/app/javascript/dashboard/i18n/locale/pt_BR/attributesMgmt.json
@@ -15,7 +15,7 @@
"LINK": "Link",
"DATE": "Data",
"LIST": "Lista",
- "CHECKBOX": "Checkbox"
+ "CHECKBOX": "Caixa de seleção"
},
"ADD": {
"TITLE": "Adicionar atributo personalizado",
diff --git a/app/javascript/dashboard/i18n/locale/pt_BR/automation.json b/app/javascript/dashboard/i18n/locale/pt_BR/automation.json
index 294432a4c..f7fd35889 100644
--- a/app/javascript/dashboard/i18n/locale/pt_BR/automation.json
+++ b/app/javascript/dashboard/i18n/locale/pt_BR/automation.json
@@ -131,7 +131,7 @@
"CONVERSATION_CREATED": "Conversa Criada",
"CONVERSATION_UPDATED": "Conversa Atualizada",
"MESSAGE_CREATED": "Mensagem Criada",
- "CONVERSATION_RESOLVED": "Conversa resolvida",
+ "CONVERSATION_RESOLVED": "Conversa Resolvida",
"CONVERSATION_OPENED": "Conversa Aberta"
},
"ACTIONS": {
@@ -177,7 +177,8 @@
"REFERER_LINK": "Link de origem",
"ASSIGNEE_NAME": "Agente atribuído",
"TEAM_NAME": "Time",
- "PRIORITY": "Prioridade"
+ "PRIORITY": "Prioridade",
+ "LABELS": "Etiquetas"
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/pt_BR/contact.json b/app/javascript/dashboard/i18n/locale/pt_BR/contact.json
index 57941ef09..4eaa1efa3 100644
--- a/app/javascript/dashboard/i18n/locale/pt_BR/contact.json
+++ b/app/javascript/dashboard/i18n/locale/pt_BR/contact.json
@@ -610,6 +610,15 @@
"SEND_MESSAGE": "Enviar mensagem"
}
},
+ "TWILIO_OPTIONS": {
+ "LABEL": "Selecione o modelo",
+ "SEARCH_PLACEHOLDER": "Pesquisar modelos",
+ "EMPTY_STATE": "Nenhum modelo encontrado",
+ "TEMPLATE_PARSER": {
+ "BACK": "Voltar atrás",
+ "SEND_MESSAGE": "Enviar mensagem"
+ }
+ },
"ACTION_BUTTONS": {
"DISCARD": "Descartar",
"SEND": "Enviar ({keyCode})"
diff --git a/app/javascript/dashboard/i18n/locale/pt_BR/contactFilters.json b/app/javascript/dashboard/i18n/locale/pt_BR/contactFilters.json
index 9dc1d1b3b..37ad9460e 100644
--- a/app/javascript/dashboard/i18n/locale/pt_BR/contactFilters.json
+++ b/app/javascript/dashboard/i18n/locale/pt_BR/contactFilters.json
@@ -48,7 +48,8 @@
"CREATED_AT": "Criado em",
"LAST_ACTIVITY": "Última atividade",
"REFERER_LINK": "Link de origem",
- "BLOCKED": "Bloqueado"
+ "BLOCKED": "Bloqueado",
+ "LABELS": "Etiquetas"
},
"GROUPS": {
"STANDARD_FILTERS": "Filtros Padrão",
diff --git a/app/javascript/dashboard/i18n/locale/pt_BR/contentTemplates.json b/app/javascript/dashboard/i18n/locale/pt_BR/contentTemplates.json
new file mode 100644
index 000000000..82bd0b244
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/pt_BR/contentTemplates.json
@@ -0,0 +1,51 @@
+{
+ "CONTENT_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Twilio Templates",
+ "SUBTITLE": "Select the Twilio template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configurar modelo: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Pesquisar modelos",
+ "NO_TEMPLATES_FOUND": "Não há templates encontrados para",
+ "NO_CONTENT": "Sem conteúdo",
+ "HEADER": "Cabeçalho",
+ "BODY": "Corpo",
+ "FOOTER": "Rodapé",
+ "BUTTONS": "Botões",
+ "CATEGORY": "Categoria",
+ "MEDIA_CONTENT": "Conteúdo de Mídia",
+ "MEDIA_CONTENT_FALLBACK": "conteúdo de mídia",
+ "NO_TEMPLATES_AVAILABLE": "No Twilio templates available. Click refresh to sync templates from Twilio.",
+ "REFRESH_BUTTON": "Atualizar modelos",
+ "REFRESH_SUCCESS": "Atualização de modelos iniciada. Pode levar alguns minutos para atualizar.",
+ "REFRESH_ERROR": "Falha ao atualizar os modelos. Por favor, tente novamente.",
+ "LABELS": {
+ "LANGUAGE": "Idioma",
+ "TEMPLATE_BODY": "Conteúdo do Template",
+ "CATEGORY": "Categoria"
+ },
+ "TYPES": {
+ "MEDIA": "Media",
+ "QUICK_REPLY": "Quick Reply",
+ "TEXT": "Texto"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variáveis",
+ "LANGUAGE": "Idioma",
+ "CATEGORY": "Categoria",
+ "VARIABLE_PLACEHOLDER": "Insira o valor para {variable}",
+ "GO_BACK_LABEL": "Voltar",
+ "SEND_MESSAGE_LABEL": "Enviar Mensagem",
+ "FORM_ERROR_MESSAGE": "Por favor, preencha todas as variáveis antes de enviar",
+ "MEDIA_HEADER_LABEL": "Cabeçalho {type}",
+ "MEDIA_URL_LABEL": "Enter full media URL",
+ "MEDIA_URL_PLACEHOLDER": "https://example.com/image.jpg"
+ },
+ "FORM": {
+ "BACK_BUTTON": "Anterior",
+ "SEND_MESSAGE_BUTTON": "Enviar Mensagem"
+ }
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/pt_BR/conversation.json b/app/javascript/dashboard/i18n/locale/pt_BR/conversation.json
index 6c681572d..466c962c4 100644
--- a/app/javascript/dashboard/i18n/locale/pt_BR/conversation.json
+++ b/app/javascript/dashboard/i18n/locale/pt_BR/conversation.json
@@ -35,6 +35,11 @@
"API_HOURS_WINDOW": "Você só pode responder a esta conversa em {hours} horas",
"NOT_ASSIGNED_TO_YOU": "Esta conversa não está atribuída a você. Gostaria de atribuir esta conversa a você mesmo?",
"ASSIGN_TO_ME": "Atribuir a mim",
+ "BOT_HANDOFF_MESSAGE": "You are responding to a conversation which is currently handled by an assistant or a bot.",
+ "BOT_HANDOFF_ACTION": "Mark open and assign to you",
+ "BOT_HANDOFF_REOPEN_ACTION": "Mark conversation open",
+ "BOT_HANDOFF_SUCCESS": "Conversation has been handed over to you",
+ "BOT_HANDOFF_ERROR": "Failed to take over the conversation. Please try again.",
"TWILIO_WHATSAPP_CAN_REPLY": "Você só pode responder a esta conversa usando um modelo de mensagem devido a",
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "Restrições de janela de mensagem de 24 horas",
"OLD_INSTAGRAM_INBOX_REPLY_BANNER": "Esta conta do Instagram foi migrada para a nova caixa de entrada do canal do Instagram. Todas as novas mensagens serão mostradas lá. Você não poderá mais enviar mensagens desta conversa.",
@@ -66,6 +71,17 @@
"SHOW_LABELS": "Mostrar etiquetas",
"HIDE_LABELS": "Ocultar as etiquetas"
},
+ "VOICE_CALL": {
+ "INCOMING_CALL": "Incoming call",
+ "OUTGOING_CALL": "Outgoing call",
+ "CALL_IN_PROGRESS": "Call in progress",
+ "NO_ANSWER": "No answer",
+ "MISSED_CALL": "Missed call",
+ "CALL_ENDED": "Call ended",
+ "NOT_ANSWERED_YET": "Not answered yet",
+ "THEY_ANSWERED": "They answered",
+ "YOU_ANSWERED": "You answered"
+ },
"HEADER": {
"RESOLVE_ACTION": "Resolver",
"REOPEN_ACTION": "Reabrir",
diff --git a/app/javascript/dashboard/i18n/locale/pt_BR/helpCenter.json b/app/javascript/dashboard/i18n/locale/pt_BR/helpCenter.json
index a76147ca0..5fa476061 100644
--- a/app/javascript/dashboard/i18n/locale/pt_BR/helpCenter.json
+++ b/app/javascript/dashboard/i18n/locale/pt_BR/helpCenter.json
@@ -741,7 +741,8 @@
"LIVE_CHAT_WIDGET": {
"LABEL": "Widget de chat ao vivo",
"PLACEHOLDER": "Selecionar widget de chat ao vivo",
- "HELP_TEXT": "Selecione um widget de chat ao vivo que aparecerá no centro de ajuda"
+ "HELP_TEXT": "Selecione um widget de chat ao vivo que aparecerá no centro de ajuda",
+ "NONE_OPTION": "No widget"
},
"BRAND_COLOR": {
"LABEL": "Cor da Marca"
@@ -807,6 +808,58 @@
"ERROR_MESSAGE": "Não foi possível atualizar o portal"
}
}
+ },
+ "PDF_UPLOAD": {
+ "TITLE": "Upload PDF Document",
+ "DESCRIPTION": "Upload a PDF document to automatically generate FAQs using AI",
+ "DRAG_DROP_TEXT": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "ADDITIONAL_CONTEXT_LABEL": "Additional Context (Optional)",
+ "ADDITIONAL_CONTEXT_PLACEHOLDER": "Provide any additional context or instructions for FAQ generation...",
+ "UPLOADING": "Enviando...",
+ "UPLOAD": "Upload & Process",
+ "CANCEL": "Cancelar",
+ "ERROR_INVALID_TYPE": "Please select a valid PDF file",
+ "ERROR_FILE_TOO_LARGE": "File size must be less than 512MB",
+ "ERROR_UPLOAD_FAILED": "Failed to upload PDF. Please try again."
+ },
+ "PDF_DOCUMENTS": {
+ "TITLE": "PDF Documents",
+ "DESCRIPTION": "Manage uploaded PDF documents and generate FAQs from them",
+ "UPLOAD_PDF": "Upload PDF",
+ "UPLOAD_FIRST_PDF": "Upload your first PDF",
+ "UPLOADED_BY": "Uploaded by",
+ "GENERATE_FAQS": "Generate FAQs",
+ "GENERATING": "Gerando...",
+ "CONFIRM_DELETE": "Tem certeza que deseja excluir {filename}?",
+ "EMPTY_STATE": {
+ "TITLE": "No PDF documents yet",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQs using AI"
+ },
+ "STATUS": {
+ "UPLOADED": "Ready",
+ "PROCESSING": "Processing",
+ "PROCESSED": "Concluído",
+ "FAILED": "Failed"
+ }
+ },
+ "CONTENT_GENERATION": {
+ "TITLE": "Content Generation",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQ content using AI",
+ "UPLOAD_TITLE": "Upload PDF Document",
+ "DRAG_DROP": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "UPLOADING": "Processing document...",
+ "UPLOAD_SUCCESS": "Document processed successfully!",
+ "UPLOAD_ERROR": "Failed to upload document. Please try again.",
+ "INVALID_FILE_TYPE": "Please select a valid PDF file",
+ "FILE_TOO_LARGE": "File size must be less than 512MB",
+ "GENERATED_CONTENT": "Generated FAQ Content",
+ "PUBLISH_SELECTED": "Publish Selected",
+ "PUBLISHING": "Publishing...",
+ "FROM_DOCUMENT": "From document",
+ "NO_CONTENT": "No generated content available. Upload a PDF document to get started.",
+ "LOADING": "Loading generated content..."
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/pt_BR/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/pt_BR/inboxMgmt.json
index b0b117b51..3b87105ab 100644
--- a/app/javascript/dashboard/i18n/locale/pt_BR/inboxMgmt.json
+++ b/app/javascript/dashboard/i18n/locale/pt_BR/inboxMgmt.json
@@ -272,8 +272,8 @@
},
"SUBMIT_BUTTON": "Criar canal do WhatsApp",
"EMBEDDED_SIGNUP": {
- "TITLE": "Configuração rápida com Meta",
- "DESC": "Você será redirecionado para a Meta para entrar na sua conta do WhatsApp Business. Ter acesso administrativo ajudará a facilitar a instalação.",
+ "TITLE": "Quick setup with Meta",
+ "DESC": "Use the WhatsApp Embedded Signup flow to quickly connect new numbers. You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
"BENEFITS": {
"TITLE": "Benefícios da inscrição incorporada:",
"EASY_SETUP": "Nenhuma configuração manual é necessária",
@@ -281,9 +281,8 @@
"AUTO_CONFIG": "Configuração automática de webhook e número de telefone"
},
"LEARN_MORE": {
- "TEXT": "Para saber mais sobre inscrições integradas, preços e limitações visite",
- "LINK_TEXT": "este link.",
- "LINK_URL": "https://developers.facebook.com/docs/whatsapp/embedded-signup/custom-flows/onboarding-business-app-users#limitations"
+ "TEXT": "To learn more about integrated signup, pricing, and limitations, visit {link}.",
+ "LINK_TEXT": "este link"
},
"SUBMIT_BUTTON": "Conecte-se com WhatsApp Business",
"AUTH_PROCESSING": "Autenticando com Meta",
@@ -296,7 +295,9 @@
"INVALID_BUSINESS_DATA": "Dados de negócio inválidos recebidos do Facebook. Por favor, tente novamente.",
"SIGNUP_ERROR": "Ocorreu um erro no cadastro",
"AUTH_NOT_COMPLETED": "Autenticação não concluída. Por favor, reinicie o processo.",
- "SUCCESS_FALLBACK": "A conta do WhatsApp Business foi configurada com sucesso"
+ "SUCCESS_FALLBACK": "A conta do WhatsApp Business foi configurada com sucesso",
+ "MANUAL_FALLBACK": "If your number is already connected to the WhatsApp Business Platform (API), or if you’re a tech provider onboarding your own number, please use the {link} flow",
+ "MANUAL_LINK_TEXT": "manual setup flow"
},
"API": {
"ERROR_MESSAGE": "Não foi possível salvar o canal do WhatsApp"
@@ -424,7 +425,51 @@
},
"AUTH": {
"TITLE": "Escolha um canal",
- "DESC": "O Chatwoot suporta widgets de chats ao vivo, Facebook Messenger, perfis do Twitter, WhatsApp, E-mails, etc., como canais. Se você quiser criar um canal personalizado, você pode criá-lo usando o canal API. Para começar, escolha um dos canais abaixo."
+ "DESC": "O Chatwoot suporta widgets de chats ao vivo, Facebook Messenger, perfis do Twitter, WhatsApp, E-mails, etc., como canais. Se você quiser criar um canal personalizado, você pode criá-lo usando o canal API. Para começar, escolha um dos canais abaixo.",
+ "TITLE_NEXT": "Complete the setup",
+ "TITLE_FINISH": "Então!",
+ "CHANNEL": {
+ "WEBSITE": {
+ "TITLE": "Site",
+ "DESCRIPTION": "Create a live-chat widget"
+ },
+ "FACEBOOK": {
+ "TITLE": "Facebook",
+ "DESCRIPTION": "Connect your Facebook page"
+ },
+ "WHATSAPP": {
+ "TITLE": "WhatsApp",
+ "DESCRIPTION": "Support your customers on WhatsApp"
+ },
+ "EMAIL": {
+ "TITLE": "e-mail",
+ "DESCRIPTION": "Connect with Gmail, Outlook, or other providers"
+ },
+ "SMS": {
+ "TITLE": "SMS",
+ "DESCRIPTION": "Integrate SMS channel with Twilio or bandwidth"
+ },
+ "API": {
+ "TITLE": "API",
+ "DESCRIPTION": "Make a custom channel using our API"
+ },
+ "TELEGRAM": {
+ "TITLE": "Telegram",
+ "DESCRIPTION": "Configure Telegram channel using Bot token"
+ },
+ "LINE": {
+ "TITLE": "Line",
+ "DESCRIPTION": "Integrate your Line channel"
+ },
+ "INSTAGRAM": {
+ "TITLE": "Instagram",
+ "DESCRIPTION": "Connect your instagram account"
+ },
+ "VOICE": {
+ "TITLE": "Voz",
+ "DESCRIPTION": "Integrate with Twilio Voice"
+ }
+ }
},
"AGENTS": {
"TITLE": "Agentes",
@@ -478,7 +523,10 @@
"MESSAGE": "Agora você ja pode oferecer uma excelente experiência no atendimento de seus clientes através do seu novo Canal",
"BUTTON_TEXT": "Leva-me lá",
"MORE_SETTINGS": "Mais configurações",
- "WEBSITE_SUCCESS": "Você concluiu a criação de um canal de site. Copie o código mostrado abaixo e cole-o no seu site. Na próxima vez que um cliente usar o bate-papo ao vivo, a conversa aparecerá automaticamente na sua caixa de entrada."
+ "WEBSITE_SUCCESS": "Você concluiu a criação de um canal de site. Copie o código mostrado abaixo e cole-o no seu site. Na próxima vez que um cliente usar o bate-papo ao vivo, a conversa aparecerá automaticamente na sua caixa de entrada.",
+ "WHATSAPP_QR_INSTRUCTION": "Scan the QR code above to quickly test your WhatsApp inbox",
+ "MESSENGER_QR_INSTRUCTION": "Scan the QR code above to quickly test your Facebook Messenger inbox",
+ "TELEGRAM_QR_INSTRUCTION": "Scan the QR code above to quickly test your Telegram inbox"
},
"REAUTH": "Reautorizar",
"VIEW": "Visualizar",
@@ -868,9 +916,18 @@
"SCRIPT_SETTINGS": "\n window.chatwootSettings = {options};"
},
"EMAIL_PROVIDERS": {
- "MICROSOFT": "Microsoft",
- "GOOGLE": "Google",
- "OTHER_PROVIDERS": "Outros Provedores"
+ "MICROSOFT": {
+ "TITLE": "Microsoft",
+ "DESCRIPTION": "Connect with Microsoft"
+ },
+ "GOOGLE": {
+ "TITLE": "Google",
+ "DESCRIPTION": "Connect with Google"
+ },
+ "OTHER_PROVIDERS": {
+ "TITLE": "Outros Provedores",
+ "DESCRIPTION": "Connect with Other Providers"
+ }
},
"CHANNELS": {
"MESSENGER": "Messenger",
diff --git a/app/javascript/dashboard/i18n/locale/pt_BR/integrations.json b/app/javascript/dashboard/i18n/locale/pt_BR/integrations.json
index 88624a8d0..585be5a50 100644
--- a/app/javascript/dashboard/i18n/locale/pt_BR/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/pt_BR/integrations.json
@@ -704,11 +704,28 @@
"ERROR_MESSAGE": "Ocorreu um erro ao criar o documento, por favor, tente novamente."
},
"FORM": {
+ "TYPE": {
+ "LABEL": "Document Type",
+ "URL": "URL:",
+ "PDF": "PDF File"
+ },
"URL": {
"LABEL": "URL:",
"PLACEHOLDER": "Insira a URL do documento",
"ERROR": "Por favor forneça uma URL válida para o documento"
},
+ "PDF_FILE": {
+ "LABEL": "PDF File",
+ "CHOOSE_FILE": "Choose PDF file",
+ "ERROR": "Please select a PDF file",
+ "HELP_TEXT": "Maximum file size: 10MB",
+ "INVALID_TYPE": "Please select a valid PDF file",
+ "TOO_LARGE": "File size exceeds 10MB limit"
+ },
+ "NAME": {
+ "LABEL": "Document Name (Optional)",
+ "PLACEHOLDER": "Enter a name for the document"
+ },
"ASSISTANT": {
"LABEL": "Assistente",
"PLACEHOLDER": "Selecione o assistente",
@@ -744,6 +761,7 @@
"SELECTED": "{count} selecionado",
"SELECT_ALL": "Selecionar todos ({count})",
"UNSELECT_ALL": "Desmarcar todos ({count})",
+ "SEARCH_PLACEHOLDER": "Search FAQs...",
"BULK_APPROVE_BUTTON": "Aprovar",
"BULK_DELETE_BUTTON": "Excluir",
"BULK_APPROVE": {
diff --git a/app/javascript/dashboard/i18n/locale/pt_BR/mfa.json b/app/javascript/dashboard/i18n/locale/pt_BR/mfa.json
new file mode 100644
index 000000000..c6fce0c49
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/pt_BR/mfa.json
@@ -0,0 +1,106 @@
+{
+ "MFA_SETTINGS": {
+ "TITLE": "Two-Factor Authentication",
+ "SUBTITLE": "Secure your account with TOTP-based authentication",
+ "DESCRIPTION": "Add an extra layer of security to your account using a time-based one-time password (TOTP)",
+ "STATUS_TITLE": "Authentication Status",
+ "STATUS_DESCRIPTION": "Manage your two-factor authentication settings and backup recovery codes",
+ "ENABLED": "Ativado",
+ "DISABLED": "Desativado",
+ "STATUS_ENABLED": "Two-factor authentication is active",
+ "STATUS_ENABLED_DESC": "Your account is protected with an additional layer of security",
+ "ENABLE_BUTTON": "Enable Two-Factor Authentication",
+ "ENHANCE_SECURITY": "Enhance Your Account Security",
+ "ENHANCE_SECURITY_DESC": "Two-factor authentication adds an extra layer of security by requiring a verification code from your authenticator app in addition to your password.",
+ "SETUP": {
+ "STEP_NUMBER_1": "1",
+ "STEP_NUMBER_2": "2",
+ "STEP1_TITLE": "Scan QR Code with Your Authenticator App",
+ "STEP1_DESCRIPTION": "Use Google Authenticator, Authy, or any TOTP-compatible app",
+ "LOADING_QR": "Carregando...",
+ "MANUAL_ENTRY": "Can't scan? Enter code manually",
+ "SECRET_KEY": "Secret Key",
+ "COPY": "Copiar",
+ "ENTER_CODE": "Enter the 6-digit code from your authenticator app",
+ "ENTER_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify & Continue",
+ "CANCEL": "Cancelar",
+ "ERROR_STARTING": "MFA not enabled. Please contact administrator.",
+ "INVALID_CODE": "Invalid verification code",
+ "SECRET_COPIED": "Secret key copied to clipboard",
+ "SUCCESS": "Two-factor authentication has been enabled successfully"
+ },
+ "BACKUP": {
+ "TITLE": "Save Your Backup Codes",
+ "DESCRIPTION": "Keep these codes safe. Each can be used once if you lose access to your authenticator",
+ "IMPORTANT": "Important:",
+ "IMPORTANT_NOTE": " Save these codes in a secure location. You won't be able to see them again.",
+ "DOWNLOAD": "Baixar",
+ "COPY_ALL": "Copy All",
+ "CONFIRM": "I have saved my backup codes in a secure location and understand that I won't be able to see them again",
+ "COMPLETE_SETUP": "Complete Setup",
+ "CODES_COPIED": "Backup codes copied to clipboard"
+ },
+ "MANAGEMENT": {
+ "BACKUP_CODES": "Backup Codes",
+ "BACKUP_CODES_DESC": "Generate new codes if you've lost or used your existing ones",
+ "REGENERATE": "Regenerate Backup Codes",
+ "DISABLE_MFA": "Disable 2FA",
+ "DISABLE_MFA_DESC": "Remove two-factor authentication from your account",
+ "DISABLE_BUTTON": "Disable Two-Factor Authentication"
+ },
+ "DISABLE": {
+ "TITLE": "Disable Two-Factor Authentication",
+ "DESCRIPTION": "You'll need to enter your password and a verification code to disable two-factor authentication.",
+ "PASSWORD": "Senha",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Disable 2FA",
+ "CANCEL": "Cancelar",
+ "SUCCESS": "Two-factor authentication has been disabled",
+ "ERROR": "Failed to disable MFA. Please check your credentials."
+ },
+ "REGENERATE": {
+ "TITLE": "Regenerate Backup Codes",
+ "DESCRIPTION": "This will invalidate your existing backup codes and generate new ones. Enter your verification code to continue.",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Generate New Codes",
+ "CANCEL": "Cancelar",
+ "NEW_CODES_TITLE": "New Backup Codes Generated",
+ "NEW_CODES_DESC": "Your old backup codes have been invalidated. Save these new codes in a secure location.",
+ "CODES_IMPORTANT": "Important:",
+ "CODES_IMPORTANT_NOTE": " Each code can only be used once. Save them before closing this window.",
+ "DOWNLOAD_CODES": "Download Codes",
+ "COPY_ALL_CODES": "Copy All Codes",
+ "CODES_SAVED": "I've Saved My Codes",
+ "SUCCESS": "New backup codes have been generated",
+ "ERROR": "Failed to regenerate backup codes"
+ }
+ },
+ "MFA_VERIFICATION": {
+ "TITLE": "Two-Factor Authentication",
+ "DESCRIPTION": "Enter your verification code to continue",
+ "AUTHENTICATOR_APP": "Authenticator App",
+ "BACKUP_CODE": "Backup Code",
+ "ENTER_OTP_CODE": "Enter 6-digit code from your authenticator app",
+ "ENTER_BACKUP_CODE": "Enter one of your backup codes",
+ "BACKUP_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify",
+ "TRY_ANOTHER_METHOD": "Try another verification method",
+ "CANCEL_LOGIN": "Cancel and return to login",
+ "HELP_TEXT": "Having trouble signing in?",
+ "LEARN_MORE": "Learn more about 2FA",
+ "HELP_MODAL": {
+ "TITLE": "Two-Factor Authentication Help",
+ "AUTHENTICATOR_TITLE": "Using an Authenticator App",
+ "AUTHENTICATOR_DESC": "Open your authenticator app (Google Authenticator, Authy, etc.) and enter the 6-digit code shown for your account.",
+ "BACKUP_TITLE": "Using a Backup Code",
+ "BACKUP_DESC": "If you don't have access to your authenticator app, you can use one of the backup codes you saved when setting up 2FA. Each code can only be used once.",
+ "CONTACT_TITLE": "Need More Help?",
+ "CONTACT_DESC_CLOUD": "If you've lost access to both your authenticator app and backup codes, please reach out to Chatwoot support for assistance.",
+ "CONTACT_DESC_SELF_HOSTED": "If you've lost access to both your authenticator app and backup codes, please contact your administrator for assistance."
+ },
+ "VERIFICATION_FAILED": "Verification failed. Please try again."
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/pt_BR/settings.json b/app/javascript/dashboard/i18n/locale/pt_BR/settings.json
index 29f75b325..e3db0ecf6 100644
--- a/app/javascript/dashboard/i18n/locale/pt_BR/settings.json
+++ b/app/javascript/dashboard/i18n/locale/pt_BR/settings.json
@@ -51,6 +51,13 @@
"LARGER": "Maior",
"EXTRA_LARGE": "Muito Grande"
}
+ },
+ "LANGUAGE": {
+ "TITLE": "Preferred Language",
+ "NOTE": "Choose the language you want to use.",
+ "UPDATE_SUCCESS": "Your Language settings have been updated successfully",
+ "UPDATE_ERROR": "There is an error while updating the language settings, please try again",
+ "USE_ACCOUNT_DEFAULT": "Use account default"
}
},
"MESSAGE_SIGNATURE_SECTION": {
@@ -73,6 +80,11 @@
"NOTE": "A atualização da sua senha redefiniria o seu login em vários dispositivos.",
"BTN_TEXT": "Mudar Senha"
},
+ "SECURITY_SECTION": {
+ "TITLE": "Security",
+ "NOTE": "Manage additional security features for your account.",
+ "MFA_BUTTON": "Manage Two-Factor Authentication"
+ },
"ACCESS_TOKEN": {
"TITLE": "Token de acesso",
"NOTE": "Esse token pode ser usado se você estiver criando uma integração baseada em API",
@@ -226,7 +238,7 @@
"APPEARANCE": "Alterar Tema",
"SUPER_ADMIN_CONSOLE": "Console de Super Admin",
"DOCS": "Ler documentação",
- "CHANGELOG": "Changelog",
+ "CHANGELOG": "Notas de versão",
"LOGOUT": "Encerrar sessão"
},
"APP_GLOBAL": {
@@ -330,6 +342,7 @@
"REPORTS_LABEL": "Etiquetas",
"REPORTS_INBOX": "Caixa de Entrada",
"REPORTS_TEAM": "Time",
+ "AGENT_ASSIGNMENT": "Agent Assignment",
"SET_AVAILABILITY_TITLE": "Defina como",
"SET_YOUR_AVAILABILITY": "Disponibilidade",
"SLA": "SLA",
@@ -350,7 +363,8 @@
"INFO_TEXT": "Deixe o sistema marcar você automaticamente quando você não estiver usando o app ou o painel de controle.",
"INFO_SHORT": "Marcar off-line automaticamente quando não estiver usando o aplicativo."
},
- "DOCS": "Ler documentos"
+ "DOCS": "Ler documentos",
+ "SECURITY": "Security"
},
"BILLING_SETTINGS": {
"TITLE": "Cobrança",
@@ -382,6 +396,77 @@
},
"NO_BILLING_USER": "A sua conta de cobrança está sendo configurada. Atualize a página e tente novamente."
},
+ "SECURITY_SETTINGS": {
+ "TITLE": "Security",
+ "DESCRIPTION": "Manage your account security settings.",
+ "LINK_TEXT": "Learn more about SAML SSO",
+ "SAML": {
+ "TITLE": "SAML SSO",
+ "NOTE": "Configure SAML single sign-on for your account. Users will authenticate through your identity provider instead of using email/password.",
+ "ACS_URL": {
+ "LABEL": "ACS URL",
+ "TOOLTIP": "Assertion Consumer Service URL - Configure this URL in your IdP as the destination for SAML responses"
+ },
+ "SSO_URL": {
+ "LABEL": "SSO URL",
+ "HELP": "The URL where SAML authentication requests will be sent",
+ "PLACEHOLDER": "https://your-idp.com/saml/sso"
+ },
+ "CERTIFICATE": {
+ "LABEL": "Signing certificate in PEM format",
+ "HELP": "The public certificate from your identity provider used to verify SAML responses",
+ "PLACEHOLDER": "-----BEGIN CERTIFICATE-----\nMIIC..."
+ },
+ "FINGERPRINT": {
+ "LABEL": "Fingerprint",
+ "TOOLTIP": "SHA-1 fingerprint of the certificate - Use this to verify the certificate in your IdP configuration"
+ },
+ "COPY_SUCCESS": "Código copiado para área de transferência com sucesso",
+ "SP_ENTITY_ID": {
+ "LABEL": "SP Entity ID",
+ "HELP": "Unique identifier for this application as a service provider (auto-generated).",
+ "TOOLTIP": "Unique identifier for Chatwoot as the Service Provider - Configure this in your IdP settings"
+ },
+ "IDP_ENTITY_ID": {
+ "LABEL": "Identity Provider Entity ID",
+ "HELP": "Unique identifier for your identity provider (usually found in IdP configuration)",
+ "PLACEHOLDER": "https://your-idp.com/saml"
+ },
+ "UPDATE_BUTTON": "Update SAML Settings",
+ "API": {
+ "SUCCESS": "SAML settings updated successfully",
+ "ERROR": "Failed to update SAML settings",
+ "ERROR_LOADING": "Failed to load SAML settings",
+ "DISABLED": "SAML settings disabled successfully"
+ },
+ "VALIDATION": {
+ "REQUIRED_FIELDS": "SSO URL, Identity Provider Entity ID, and Certificate are required fields",
+ "SSO_URL_ERROR": "Please enter a valid SSO URL",
+ "CERTIFICATE_ERROR": "Certificate is required",
+ "IDP_ENTITY_ID_ERROR": "Identity Provider Entity ID is required"
+ },
+ "ENTERPRISE_PAYWALL": {
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade to an Enterprise plan to access SAML single sign-on and other advanced security features.",
+ "ASK_ADMIN": "Entre em contato com seu administrador para fazer a atualização."
+ },
+ "PAYWALL": {
+ "TITLE": "Upgrade to enable SAML SSO",
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade your plan to get access to SAML single sign-on and other advanced features.",
+ "UPGRADE_NOW": "Atualizar agora",
+ "CANCEL_ANYTIME": "Você pode alterar ou cancelar seu plano a qualquer momento"
+ },
+ "ATTRIBUTE_MAPPING": {
+ "TITLE": "SAML Attribute Setup",
+ "DESCRIPTION": "The following attribute mappings must be configured in your identity provider"
+ },
+ "INFO_SECTION": {
+ "TITLE": "Service Provider Information",
+ "TOOLTIP": "Copy these values and configure them in your Identity Provider to establish the SAML connection"
+ }
+ }
+ },
"CREATE_ACCOUNT": {
"NO_ACCOUNT_WARNING": "Ah oh! Não conseguimos encontrar nenhuma conta. Por favor, crie uma nova conta para continuar.",
"NEW_ACCOUNT": "Nova conta",
@@ -418,5 +503,255 @@
"SWITCH_TO_REPLY": "Mudar para resposta",
"TOGGLE_SNOOZE_DROPDOWN": "Ativar/desativar soneca"
}
+ },
+ "ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent assignment",
+ "DESCRIPTION": "Define policies to effectively manage workload and route conversations based on the needs of inboxes and agents. Learn more here"
+ },
+ "ASSIGNMENT_POLICY": {
+ "TITLE": "Assignment policy",
+ "DESCRIPTION": "Manage how conversations get assigned in inboxes.",
+ "FEATURES": [
+ "Assign by conversations evenly or by available capacity",
+ "Add fair distribution rules to avoid overloading any agent",
+ "Add inboxes to a policy - one policy per inbox"
+ ]
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "TITLE": "Agent capacity policy",
+ "DESCRIPTION": "Manage workload for agents.",
+ "FEATURES": [
+ "Define maximum conversations per inbox",
+ "Create exceptions based on labels and time",
+ "Add agents to a policy - one policy per agent"
+ ]
+ }
+ },
+ "AGENT_ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Assignment policy",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "ORDER": "Order",
+ "PRIORITY": "Prioridade",
+ "ACTIVE": "Ativo",
+ "INACTIVE": "Inactive",
+ "POPOVER": "Added inboxes",
+ "EDIT": "Alterar"
+ },
+ "NO_RECORDS_FOUND": "No assignment policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create assignment policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy created successfully",
+ "ERROR_MESSAGE": "Failed to create assignment policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit assignment policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_INBOX_DIALOG": {
+ "TITLE": "Add inbox",
+ "DESCRIPTION": "{inboxName} inbox is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Cancelar"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update assignment policy"
+ },
+ "INBOX_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Inbox added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Inbox removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove inbox from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Descrição:",
+ "PLACEHOLDER": "Insira a descrição"
+ },
+ "STATUS": {
+ "LABEL": "Status:",
+ "PLACEHOLDER": "Selecione Status",
+ "ACTIVE": "Policy is active",
+ "INACTIVE": "Policy is inactive"
+ },
+ "ASSIGNMENT_ORDER": {
+ "LABEL": "Assignment order",
+ "ROUND_ROBIN": {
+ "LABEL": "Round robin",
+ "DESCRIPTION": "Assign conversations evenly among agents."
+ },
+ "BALANCED": {
+ "LABEL": "Balanced",
+ "DESCRIPTION": "Assign conversations based on available capacity."
+ }
+ },
+ "ASSIGNMENT_PRIORITY": {
+ "LABEL": "Assignment priority",
+ "EARLIEST_CREATED": {
+ "LABEL": "Earliest created",
+ "DESCRIPTION": "The conversation that was created first gets assigned first."
+ },
+ "LONGEST_WAITING": {
+ "LABEL": "Longest waiting",
+ "DESCRIPTION": "The conversation waiting the longest gets assigned first."
+ }
+ },
+ "FAIR_DISTRIBUTION": {
+ "LABEL": "Fair distribution policy",
+ "DESCRIPTION": "Set the maximum number of conversations that can be assigned per agent within a time window to avoid overloading any one agent. This required field defaults to 100 conversations per hour.",
+ "INPUT_MAX": "Assign max",
+ "DURATION": "Conversations per agent in every"
+ },
+ "INBOXES": {
+ "LABEL": "Added inboxes",
+ "DESCRIPTION": "Add inboxes for which this policy will be applicable.",
+ "ADD_BUTTON": "Add inbox",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select inboxes to add",
+ "ADD_BUTTON": "Adicionar"
+ },
+ "EMPTY_STATE": "No inboxes added to this policy, add an inbox to get started",
+ "API": {
+ "SUCCESS_MESSAGE": "Inbox successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Assignment policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete assignment policy"
+ }
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent capacity",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "POPOVER": "Added agents",
+ "EDIT": "Alterar"
+ },
+ "NO_RECORDS_FOUND": "No agent capacity policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create agent capacity policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy created successfully",
+ "ERROR_MESSAGE": "Failed to create agent capacity policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit agent capacity policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_AGENT_DIALOG": {
+ "TITLE": "Add agent",
+ "DESCRIPTION": "{agentName} is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Cancelar"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update agent capacity policy"
+ },
+ "AGENT_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Agent added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Agent removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove agent from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Descrição:",
+ "PLACEHOLDER": "Insira a descrição"
+ },
+ "INBOX_CAPACITY_LIMIT": {
+ "LABEL": "Inbox capacity limits",
+ "ADD_BUTTON": "Add inbox",
+ "FIELD": {
+ "SELECT_INBOX": "Select inbox",
+ "MAX_CONVERSATIONS": "Max conversations",
+ "SET_LIMIT": "Set limit"
+ },
+ "EMPTY_STATE": "No inbox limit set"
+ },
+ "EXCLUSION_RULES": {
+ "LABEL": "Exclusion rules",
+ "DESCRIPTION": "Conversations that satisfy the following conditions would not count towards agent capacity",
+ "TAGS": {
+ "LABEL": "Exclude conversations tagged with specific labels",
+ "ADD_TAG": "add tag",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select tags to add"
+ },
+ "EMPTY_STATE": "No tags added to this policy."
+ },
+ "DURATION": {
+ "LABEL": "Exclude conversations older than a specified duration",
+ "PLACEHOLDER": "Set time"
+ }
+ },
+ "USERS": {
+ "LABEL": "Assigned agents",
+ "DESCRIPTION": "Add agents for which this policy will be applicable.",
+ "ADD_BUTTON": "Add agent",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select agents to add",
+ "ADD_BUTTON": "Adicionar"
+ },
+ "EMPTY_STATE": "No agents added",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Agent capacity policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete agent capacity policy"
+ }
+ },
+ "DELETE_POLICY": {
+ "TITLE": "Delete policy",
+ "DESCRIPTION": "Are you sure you want to delete this policy? This action cannot be undone.",
+ "CONFIRM_BUTTON_LABEL": "Excluir",
+ "CANCEL_BUTTON_LABEL": "Cancelar"
+ }
}
}
diff --git a/app/javascript/dashboard/i18n/locale/pt_BR/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/pt_BR/whatsappTemplates.json
index de844053a..a5587d239 100644
--- a/app/javascript/dashboard/i18n/locale/pt_BR/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/pt_BR/whatsappTemplates.json
@@ -40,6 +40,7 @@
"BUTTON_LABEL": "Botão {index}",
"COUPON_CODE": "Digite o código do cupom (máx. 15 caracteres)",
"MEDIA_URL_LABEL": "Digite a URL {type}",
+ "DOCUMENT_NAME_PLACEHOLDER": "Enter document filename (e.g., Invoice_2025.pdf)",
"BUTTON_PARAMETER": "Insira o parâmetro do botão"
}
}
diff --git a/app/javascript/dashboard/i18n/locale/ro/automation.json b/app/javascript/dashboard/i18n/locale/ro/automation.json
index 8f18d43fe..e222fe9a9 100644
--- a/app/javascript/dashboard/i18n/locale/ro/automation.json
+++ b/app/javascript/dashboard/i18n/locale/ro/automation.json
@@ -177,7 +177,8 @@
"REFERER_LINK": "Referrer Link",
"ASSIGNEE_NAME": "Assignee",
"TEAM_NAME": "Echipa",
- "PRIORITY": "Prioritate"
+ "PRIORITY": "Prioritate",
+ "LABELS": "Etichete"
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/ro/contact.json b/app/javascript/dashboard/i18n/locale/ro/contact.json
index 1e2d05039..13c78338b 100644
--- a/app/javascript/dashboard/i18n/locale/ro/contact.json
+++ b/app/javascript/dashboard/i18n/locale/ro/contact.json
@@ -610,6 +610,15 @@
"SEND_MESSAGE": "Trimite mesaj"
}
},
+ "TWILIO_OPTIONS": {
+ "LABEL": "Select template",
+ "SEARCH_PLACEHOLDER": "Search templates",
+ "EMPTY_STATE": "No templates found",
+ "TEMPLATE_PARSER": {
+ "BACK": "Mergeți înapoi",
+ "SEND_MESSAGE": "Trimite mesaj"
+ }
+ },
"ACTION_BUTTONS": {
"DISCARD": "Discard",
"SEND": "Send ({keyCode})"
diff --git a/app/javascript/dashboard/i18n/locale/ro/contactFilters.json b/app/javascript/dashboard/i18n/locale/ro/contactFilters.json
index 9c2f3706f..b076fd00f 100644
--- a/app/javascript/dashboard/i18n/locale/ro/contactFilters.json
+++ b/app/javascript/dashboard/i18n/locale/ro/contactFilters.json
@@ -48,7 +48,8 @@
"CREATED_AT": "Creat la",
"LAST_ACTIVITY": "Ultima activitate",
"REFERER_LINK": "Link de referință",
- "BLOCKED": "Blocked"
+ "BLOCKED": "Blocked",
+ "LABELS": "Etichete"
},
"GROUPS": {
"STANDARD_FILTERS": "Filtre standard",
diff --git a/app/javascript/dashboard/i18n/locale/ro/contentTemplates.json b/app/javascript/dashboard/i18n/locale/ro/contentTemplates.json
new file mode 100644
index 000000000..0b6c888f3
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/ro/contentTemplates.json
@@ -0,0 +1,51 @@
+{
+ "CONTENT_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Twilio Templates",
+ "SUBTITLE": "Select the Twilio template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Caută Șabloane",
+ "NO_TEMPLATES_FOUND": "Nu s-au găsit șabloane pentru",
+ "NO_CONTENT": "No content",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Categorie",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No Twilio templates available. Click refresh to sync templates from Twilio.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Limbă",
+ "TEMPLATE_BODY": "Corpul șablonului",
+ "CATEGORY": "Categorie"
+ },
+ "TYPES": {
+ "MEDIA": "Media",
+ "QUICK_REPLY": "Quick Reply",
+ "TEXT": "Text"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variabile",
+ "LANGUAGE": "Limbă",
+ "CATEGORY": "Categorie",
+ "VARIABLE_PLACEHOLDER": "Introducerea {variable} valoare",
+ "GO_BACK_LABEL": "Înapoi",
+ "SEND_MESSAGE_LABEL": "Trimite mesaj",
+ "FORM_ERROR_MESSAGE": "Vă rugăm să completați toate variabilele înainte de a trimite",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "MEDIA_URL_LABEL": "Enter full media URL",
+ "MEDIA_URL_PLACEHOLDER": "https://example.com/image.jpg"
+ },
+ "FORM": {
+ "BACK_BUTTON": "Înapoi",
+ "SEND_MESSAGE_BUTTON": "Trimite mesaj"
+ }
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/ro/conversation.json b/app/javascript/dashboard/i18n/locale/ro/conversation.json
index 3e9ddb6b9..c0ae01bb2 100644
--- a/app/javascript/dashboard/i18n/locale/ro/conversation.json
+++ b/app/javascript/dashboard/i18n/locale/ro/conversation.json
@@ -35,6 +35,11 @@
"API_HOURS_WINDOW": "You can only reply to this conversation within {hours} hours",
"NOT_ASSIGNED_TO_YOU": "Această conversație nu vă este atribuită. Doriți să vă atribuiți această conversație?",
"ASSIGN_TO_ME": "Atribuie-mi",
+ "BOT_HANDOFF_MESSAGE": "You are responding to a conversation which is currently handled by an assistant or a bot.",
+ "BOT_HANDOFF_ACTION": "Mark open and assign to you",
+ "BOT_HANDOFF_REOPEN_ACTION": "Mark conversation open",
+ "BOT_HANDOFF_SUCCESS": "Conversation has been handed over to you",
+ "BOT_HANDOFF_ERROR": "Failed to take over the conversation. Please try again.",
"TWILIO_WHATSAPP_CAN_REPLY": "Poți răspunde la această conversație doar folosind un mesaj șablon datorat",
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "Restricţie fereastră mesaj 24 de ore",
"OLD_INSTAGRAM_INBOX_REPLY_BANNER": "This Instagram account was migrated to the new Instagram channel inbox. All new messages will show up there. You won’t be able to send messages from this conversation anymore.",
@@ -66,6 +71,17 @@
"SHOW_LABELS": "Afișare etichete",
"HIDE_LABELS": "Ascunderea etichetelor"
},
+ "VOICE_CALL": {
+ "INCOMING_CALL": "Incoming call",
+ "OUTGOING_CALL": "Outgoing call",
+ "CALL_IN_PROGRESS": "Call in progress",
+ "NO_ANSWER": "No answer",
+ "MISSED_CALL": "Missed call",
+ "CALL_ENDED": "Call ended",
+ "NOT_ANSWERED_YET": "Not answered yet",
+ "THEY_ANSWERED": "They answered",
+ "YOU_ANSWERED": "You answered"
+ },
"HEADER": {
"RESOLVE_ACTION": "Rezolvă",
"REOPEN_ACTION": "Redeschide",
diff --git a/app/javascript/dashboard/i18n/locale/ro/helpCenter.json b/app/javascript/dashboard/i18n/locale/ro/helpCenter.json
index ed326fc89..0eb367788 100644
--- a/app/javascript/dashboard/i18n/locale/ro/helpCenter.json
+++ b/app/javascript/dashboard/i18n/locale/ro/helpCenter.json
@@ -741,7 +741,8 @@
"LIVE_CHAT_WIDGET": {
"LABEL": "Live chat widget",
"PLACEHOLDER": "Select live chat widget",
- "HELP_TEXT": "Select a live chat widget that will appear on your help center"
+ "HELP_TEXT": "Select a live chat widget that will appear on your help center",
+ "NONE_OPTION": "No widget"
},
"BRAND_COLOR": {
"LABEL": "Brand color"
@@ -807,6 +808,58 @@
"ERROR_MESSAGE": "Unable to update portal"
}
}
+ },
+ "PDF_UPLOAD": {
+ "TITLE": "Upload PDF Document",
+ "DESCRIPTION": "Upload a PDF document to automatically generate FAQs using AI",
+ "DRAG_DROP_TEXT": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "ADDITIONAL_CONTEXT_LABEL": "Additional Context (Optional)",
+ "ADDITIONAL_CONTEXT_PLACEHOLDER": "Provide any additional context or instructions for FAQ generation...",
+ "UPLOADING": "Încărcare...",
+ "UPLOAD": "Upload & Process",
+ "CANCEL": "Renunță",
+ "ERROR_INVALID_TYPE": "Please select a valid PDF file",
+ "ERROR_FILE_TOO_LARGE": "File size must be less than 512MB",
+ "ERROR_UPLOAD_FAILED": "Failed to upload PDF. Please try again."
+ },
+ "PDF_DOCUMENTS": {
+ "TITLE": "PDF Documents",
+ "DESCRIPTION": "Manage uploaded PDF documents and generate FAQs from them",
+ "UPLOAD_PDF": "Upload PDF",
+ "UPLOAD_FIRST_PDF": "Upload your first PDF",
+ "UPLOADED_BY": "Uploaded by",
+ "GENERATE_FAQS": "Generate FAQs",
+ "GENERATING": "Generez…",
+ "CONFIRM_DELETE": "Are you sure you want to delete {filename}?",
+ "EMPTY_STATE": {
+ "TITLE": "No PDF documents yet",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQs using AI"
+ },
+ "STATUS": {
+ "UPLOADED": "Ready",
+ "PROCESSING": "Processing",
+ "PROCESSED": "Finalizată",
+ "FAILED": "Failed"
+ }
+ },
+ "CONTENT_GENERATION": {
+ "TITLE": "Content Generation",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQ content using AI",
+ "UPLOAD_TITLE": "Upload PDF Document",
+ "DRAG_DROP": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "UPLOADING": "Processing document...",
+ "UPLOAD_SUCCESS": "Document processed successfully!",
+ "UPLOAD_ERROR": "Failed to upload document. Please try again.",
+ "INVALID_FILE_TYPE": "Please select a valid PDF file",
+ "FILE_TOO_LARGE": "File size must be less than 512MB",
+ "GENERATED_CONTENT": "Generated FAQ Content",
+ "PUBLISH_SELECTED": "Publish Selected",
+ "PUBLISHING": "Publishing...",
+ "FROM_DOCUMENT": "From document",
+ "NO_CONTENT": "No generated content available. Upload a PDF document to get started.",
+ "LOADING": "Loading generated content..."
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/ro/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/ro/inboxMgmt.json
index 3f37e4d67..6e6662c18 100644
--- a/app/javascript/dashboard/i18n/locale/ro/inboxMgmt.json
+++ b/app/javascript/dashboard/i18n/locale/ro/inboxMgmt.json
@@ -272,8 +272,8 @@
},
"SUBMIT_BUTTON": "Creați canalul WhatsApp",
"EMBEDDED_SIGNUP": {
- "TITLE": "Quick Setup with Meta",
- "DESC": "You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
+ "TITLE": "Quick setup with Meta",
+ "DESC": "Use the WhatsApp Embedded Signup flow to quickly connect new numbers. You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
"BENEFITS": {
"TITLE": "Benefits of Embedded Signup:",
"EASY_SETUP": "No manual configuration required",
@@ -281,9 +281,8 @@
"AUTO_CONFIG": "Automatic webhook and phone number configuration"
},
"LEARN_MORE": {
- "TEXT": "To learn more about integrated signup, pricing, and limitations, visit",
- "LINK_TEXT": "this link.",
- "LINK_URL": "https://developers.facebook.com/docs/whatsapp/embedded-signup/custom-flows/onboarding-business-app-users#limitations"
+ "TEXT": "To learn more about integrated signup, pricing, and limitations, visit {link}.",
+ "LINK_TEXT": "this link"
},
"SUBMIT_BUTTON": "Connect with WhatsApp Business",
"AUTH_PROCESSING": "Authenticating with Meta",
@@ -296,7 +295,9 @@
"INVALID_BUSINESS_DATA": "Invalid business data received from Facebook. Please try again.",
"SIGNUP_ERROR": "Signup error occurred",
"AUTH_NOT_COMPLETED": "Authentication not completed. Please restart the process.",
- "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured"
+ "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured",
+ "MANUAL_FALLBACK": "If your number is already connected to the WhatsApp Business Platform (API), or if you’re a tech provider onboarding your own number, please use the {link} flow",
+ "MANUAL_LINK_TEXT": "manual setup flow"
},
"API": {
"ERROR_MESSAGE": "Nu am reușit să salvăm canalul WhatsApp"
@@ -424,7 +425,51 @@
},
"AUTH": {
"TITLE": "Alege un canal",
- "DESC": "Chatwoot acceptă widget-uri live-chat, Facebook Messenger, profiluri Twitter, WhatsApp, e-mailuri etc., ca canale. Dacă doriți să construiți un canal personalizat, îl puteți crea folosind canalul API. Pentru a începe, alegeți unul dintre canalele de mai jos."
+ "DESC": "Chatwoot acceptă widget-uri live-chat, Facebook Messenger, profiluri Twitter, WhatsApp, e-mailuri etc., ca canale. Dacă doriți să construiți un canal personalizat, îl puteți crea folosind canalul API. Pentru a începe, alegeți unul dintre canalele de mai jos.",
+ "TITLE_NEXT": "Complete the setup",
+ "TITLE_FINISH": "Voilà!",
+ "CHANNEL": {
+ "WEBSITE": {
+ "TITLE": "Website",
+ "DESCRIPTION": "Create a live-chat widget"
+ },
+ "FACEBOOK": {
+ "TITLE": "Facebook",
+ "DESCRIPTION": "Connect your Facebook page"
+ },
+ "WHATSAPP": {
+ "TITLE": "WhatsApp",
+ "DESCRIPTION": "Support your customers on WhatsApp"
+ },
+ "EMAIL": {
+ "TITLE": "E-mail",
+ "DESCRIPTION": "Connect with Gmail, Outlook, or other providers"
+ },
+ "SMS": {
+ "TITLE": "SMS",
+ "DESCRIPTION": "Integrate SMS channel with Twilio or bandwidth"
+ },
+ "API": {
+ "TITLE": "API",
+ "DESCRIPTION": "Make a custom channel using our API"
+ },
+ "TELEGRAM": {
+ "TITLE": "Telegram",
+ "DESCRIPTION": "Configure Telegram channel using Bot token"
+ },
+ "LINE": {
+ "TITLE": "Line",
+ "DESCRIPTION": "Integrate your Line channel"
+ },
+ "INSTAGRAM": {
+ "TITLE": "Instagram",
+ "DESCRIPTION": "Connect your instagram account"
+ },
+ "VOICE": {
+ "TITLE": "Voice",
+ "DESCRIPTION": "Integrate with Twilio Voice"
+ }
+ }
},
"AGENTS": {
"TITLE": "Agenți",
@@ -478,7 +523,10 @@
"MESSAGE": "Acum poți intra în contact cu clienții tăi prin noul tău Canal. Suport fericit",
"BUTTON_TEXT": "Du-mă acolo",
"MORE_SETTINGS": "Mai multe setări",
- "WEBSITE_SUCCESS": "Ați finalizat cu succes crearea unui canal web. Copiați codul de mai jos și inserati-l pe site-ul dvs. Data viitoare când un client folosește conversația live, conversația va apărea automat pe căsuța poștală."
+ "WEBSITE_SUCCESS": "Ați finalizat cu succes crearea unui canal web. Copiați codul de mai jos și inserati-l pe site-ul dvs. Data viitoare când un client folosește conversația live, conversația va apărea automat pe căsuța poștală.",
+ "WHATSAPP_QR_INSTRUCTION": "Scan the QR code above to quickly test your WhatsApp inbox",
+ "MESSENGER_QR_INSTRUCTION": "Scan the QR code above to quickly test your Facebook Messenger inbox",
+ "TELEGRAM_QR_INSTRUCTION": "Scan the QR code above to quickly test your Telegram inbox"
},
"REAUTH": "Reautorizează",
"VIEW": "Vizualizare",
@@ -868,9 +916,18 @@
"SCRIPT_SETTINGS": "\nwindow.chatwootSettings = {options};"
},
"EMAIL_PROVIDERS": {
- "MICROSOFT": "Microsoft",
- "GOOGLE": "Google",
- "OTHER_PROVIDERS": "Alți furnizori"
+ "MICROSOFT": {
+ "TITLE": "Microsoft",
+ "DESCRIPTION": "Connect with Microsoft"
+ },
+ "GOOGLE": {
+ "TITLE": "Google",
+ "DESCRIPTION": "Connect with Google"
+ },
+ "OTHER_PROVIDERS": {
+ "TITLE": "Alți furnizori",
+ "DESCRIPTION": "Connect with Other Providers"
+ }
},
"CHANNELS": {
"MESSENGER": "Messenger",
diff --git a/app/javascript/dashboard/i18n/locale/ro/integrations.json b/app/javascript/dashboard/i18n/locale/ro/integrations.json
index dae7e2088..9d192cd77 100644
--- a/app/javascript/dashboard/i18n/locale/ro/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/ro/integrations.json
@@ -704,11 +704,28 @@
"ERROR_MESSAGE": "There was an error creating the document, please try again."
},
"FORM": {
+ "TYPE": {
+ "LABEL": "Document Type",
+ "URL": "URL",
+ "PDF": "PDF File"
+ },
"URL": {
"LABEL": "URL",
"PLACEHOLDER": "Enter the URL of the document",
"ERROR": "Please provide a valid URL for the document"
},
+ "PDF_FILE": {
+ "LABEL": "PDF File",
+ "CHOOSE_FILE": "Choose PDF file",
+ "ERROR": "Please select a PDF file",
+ "HELP_TEXT": "Maximum file size: 10MB",
+ "INVALID_TYPE": "Please select a valid PDF file",
+ "TOO_LARGE": "File size exceeds 10MB limit"
+ },
+ "NAME": {
+ "LABEL": "Document Name (Optional)",
+ "PLACEHOLDER": "Enter a name for the document"
+ },
"ASSISTANT": {
"LABEL": "Assistant",
"PLACEHOLDER": "Select the assistant",
@@ -744,6 +761,7 @@
"SELECTED": "{count} selected",
"SELECT_ALL": "Select all ({count})",
"UNSELECT_ALL": "Unselect all ({count})",
+ "SEARCH_PLACEHOLDER": "Search FAQs...",
"BULK_APPROVE_BUTTON": "Approve",
"BULK_DELETE_BUTTON": "Şterge",
"BULK_APPROVE": {
diff --git a/app/javascript/dashboard/i18n/locale/ro/mfa.json b/app/javascript/dashboard/i18n/locale/ro/mfa.json
new file mode 100644
index 000000000..89f9dacf8
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/ro/mfa.json
@@ -0,0 +1,106 @@
+{
+ "MFA_SETTINGS": {
+ "TITLE": "Two-Factor Authentication",
+ "SUBTITLE": "Secure your account with TOTP-based authentication",
+ "DESCRIPTION": "Add an extra layer of security to your account using a time-based one-time password (TOTP)",
+ "STATUS_TITLE": "Authentication Status",
+ "STATUS_DESCRIPTION": "Manage your two-factor authentication settings and backup recovery codes",
+ "ENABLED": "Activat",
+ "DISABLED": "Dezactivat",
+ "STATUS_ENABLED": "Two-factor authentication is active",
+ "STATUS_ENABLED_DESC": "Your account is protected with an additional layer of security",
+ "ENABLE_BUTTON": "Enable Two-Factor Authentication",
+ "ENHANCE_SECURITY": "Enhance Your Account Security",
+ "ENHANCE_SECURITY_DESC": "Two-factor authentication adds an extra layer of security by requiring a verification code from your authenticator app in addition to your password.",
+ "SETUP": {
+ "STEP_NUMBER_1": "1",
+ "STEP_NUMBER_2": "2",
+ "STEP1_TITLE": "Scan QR Code with Your Authenticator App",
+ "STEP1_DESCRIPTION": "Use Google Authenticator, Authy, or any TOTP-compatible app",
+ "LOADING_QR": "Loading...",
+ "MANUAL_ENTRY": "Can't scan? Enter code manually",
+ "SECRET_KEY": "Secret Key",
+ "COPY": "Copiază",
+ "ENTER_CODE": "Enter the 6-digit code from your authenticator app",
+ "ENTER_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify & Continue",
+ "CANCEL": "Renunță",
+ "ERROR_STARTING": "MFA not enabled. Please contact administrator.",
+ "INVALID_CODE": "Invalid verification code",
+ "SECRET_COPIED": "Secret key copied to clipboard",
+ "SUCCESS": "Two-factor authentication has been enabled successfully"
+ },
+ "BACKUP": {
+ "TITLE": "Save Your Backup Codes",
+ "DESCRIPTION": "Keep these codes safe. Each can be used once if you lose access to your authenticator",
+ "IMPORTANT": "Important:",
+ "IMPORTANT_NOTE": " Save these codes in a secure location. You won't be able to see them again.",
+ "DOWNLOAD": "Descărcare",
+ "COPY_ALL": "Copy All",
+ "CONFIRM": "I have saved my backup codes in a secure location and understand that I won't be able to see them again",
+ "COMPLETE_SETUP": "Complete Setup",
+ "CODES_COPIED": "Backup codes copied to clipboard"
+ },
+ "MANAGEMENT": {
+ "BACKUP_CODES": "Backup Codes",
+ "BACKUP_CODES_DESC": "Generate new codes if you've lost or used your existing ones",
+ "REGENERATE": "Regenerate Backup Codes",
+ "DISABLE_MFA": "Disable 2FA",
+ "DISABLE_MFA_DESC": "Remove two-factor authentication from your account",
+ "DISABLE_BUTTON": "Disable Two-Factor Authentication"
+ },
+ "DISABLE": {
+ "TITLE": "Disable Two-Factor Authentication",
+ "DESCRIPTION": "You'll need to enter your password and a verification code to disable two-factor authentication.",
+ "PASSWORD": "Parola",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Disable 2FA",
+ "CANCEL": "Renunță",
+ "SUCCESS": "Two-factor authentication has been disabled",
+ "ERROR": "Failed to disable MFA. Please check your credentials."
+ },
+ "REGENERATE": {
+ "TITLE": "Regenerate Backup Codes",
+ "DESCRIPTION": "This will invalidate your existing backup codes and generate new ones. Enter your verification code to continue.",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Generate New Codes",
+ "CANCEL": "Renunță",
+ "NEW_CODES_TITLE": "New Backup Codes Generated",
+ "NEW_CODES_DESC": "Your old backup codes have been invalidated. Save these new codes in a secure location.",
+ "CODES_IMPORTANT": "Important:",
+ "CODES_IMPORTANT_NOTE": " Each code can only be used once. Save them before closing this window.",
+ "DOWNLOAD_CODES": "Download Codes",
+ "COPY_ALL_CODES": "Copy All Codes",
+ "CODES_SAVED": "I've Saved My Codes",
+ "SUCCESS": "New backup codes have been generated",
+ "ERROR": "Failed to regenerate backup codes"
+ }
+ },
+ "MFA_VERIFICATION": {
+ "TITLE": "Two-Factor Authentication",
+ "DESCRIPTION": "Enter your verification code to continue",
+ "AUTHENTICATOR_APP": "Authenticator App",
+ "BACKUP_CODE": "Backup Code",
+ "ENTER_OTP_CODE": "Enter 6-digit code from your authenticator app",
+ "ENTER_BACKUP_CODE": "Enter one of your backup codes",
+ "BACKUP_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify",
+ "TRY_ANOTHER_METHOD": "Try another verification method",
+ "CANCEL_LOGIN": "Cancel and return to login",
+ "HELP_TEXT": "Having trouble signing in?",
+ "LEARN_MORE": "Learn more about 2FA",
+ "HELP_MODAL": {
+ "TITLE": "Two-Factor Authentication Help",
+ "AUTHENTICATOR_TITLE": "Using an Authenticator App",
+ "AUTHENTICATOR_DESC": "Open your authenticator app (Google Authenticator, Authy, etc.) and enter the 6-digit code shown for your account.",
+ "BACKUP_TITLE": "Using a Backup Code",
+ "BACKUP_DESC": "If you don't have access to your authenticator app, you can use one of the backup codes you saved when setting up 2FA. Each code can only be used once.",
+ "CONTACT_TITLE": "Need More Help?",
+ "CONTACT_DESC_CLOUD": "If you've lost access to both your authenticator app and backup codes, please reach out to Chatwoot support for assistance.",
+ "CONTACT_DESC_SELF_HOSTED": "If you've lost access to both your authenticator app and backup codes, please contact your administrator for assistance."
+ },
+ "VERIFICATION_FAILED": "Verification failed. Please try again."
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/ro/settings.json b/app/javascript/dashboard/i18n/locale/ro/settings.json
index 570740b7c..2ae1524e6 100644
--- a/app/javascript/dashboard/i18n/locale/ro/settings.json
+++ b/app/javascript/dashboard/i18n/locale/ro/settings.json
@@ -51,6 +51,13 @@
"LARGER": "Larger",
"EXTRA_LARGE": "Extra Large"
}
+ },
+ "LANGUAGE": {
+ "TITLE": "Preferred Language",
+ "NOTE": "Choose the language you want to use.",
+ "UPDATE_SUCCESS": "Your Language settings have been updated successfully",
+ "UPDATE_ERROR": "There is an error while updating the language settings, please try again",
+ "USE_ACCOUNT_DEFAULT": "Use account default"
}
},
"MESSAGE_SIGNATURE_SECTION": {
@@ -73,6 +80,11 @@
"NOTE": "Actualizarea parolei ar reseta autentificările pe mai multe dispozitive.",
"BTN_TEXT": "Schimba parola"
},
+ "SECURITY_SECTION": {
+ "TITLE": "Security",
+ "NOTE": "Manage additional security features for your account.",
+ "MFA_BUTTON": "Manage Two-Factor Authentication"
+ },
"ACCESS_TOKEN": {
"TITLE": "Token acces",
"NOTE": "Acest token poate fi utilizat dacă construiți o integrare bazată pe API",
@@ -330,6 +342,7 @@
"REPORTS_LABEL": "Etichete",
"REPORTS_INBOX": "Inbox",
"REPORTS_TEAM": "Echipa",
+ "AGENT_ASSIGNMENT": "Agent Assignment",
"SET_AVAILABILITY_TITLE": "Setați-vă ca",
"SET_YOUR_AVAILABILITY": "Setați-vă disponibilitatea",
"SLA": "SLA",
@@ -350,7 +363,8 @@
"INFO_TEXT": "Permiteți sistemului să vă marcheze automat offline atunci când nu utilizați aplicația sau tabloul de bord.",
"INFO_SHORT": "Automatically mark offline when you aren't using the app."
},
- "DOCS": "Citiți Documente"
+ "DOCS": "Citiți Documente",
+ "SECURITY": "Security"
},
"BILLING_SETTINGS": {
"TITLE": "Facturare",
@@ -382,6 +396,77 @@
},
"NO_BILLING_USER": "Contul de facturare este configurat. Vă rugăm să reîmprospătați pagina și încercați din nou."
},
+ "SECURITY_SETTINGS": {
+ "TITLE": "Security",
+ "DESCRIPTION": "Manage your account security settings.",
+ "LINK_TEXT": "Learn more about SAML SSO",
+ "SAML": {
+ "TITLE": "SAML SSO",
+ "NOTE": "Configure SAML single sign-on for your account. Users will authenticate through your identity provider instead of using email/password.",
+ "ACS_URL": {
+ "LABEL": "ACS URL",
+ "TOOLTIP": "Assertion Consumer Service URL - Configure this URL in your IdP as the destination for SAML responses"
+ },
+ "SSO_URL": {
+ "LABEL": "SSO URL",
+ "HELP": "The URL where SAML authentication requests will be sent",
+ "PLACEHOLDER": "https://your-idp.com/saml/sso"
+ },
+ "CERTIFICATE": {
+ "LABEL": "Signing certificate in PEM format",
+ "HELP": "The public certificate from your identity provider used to verify SAML responses",
+ "PLACEHOLDER": "-----BEGIN CERTIFICATE-----\nMIIC..."
+ },
+ "FINGERPRINT": {
+ "LABEL": "Fingerprint",
+ "TOOLTIP": "SHA-1 fingerprint of the certificate - Use this to verify the certificate in your IdP configuration"
+ },
+ "COPY_SUCCESS": "Cod copiat în clipboard cu succes",
+ "SP_ENTITY_ID": {
+ "LABEL": "SP Entity ID",
+ "HELP": "Unique identifier for this application as a service provider (auto-generated).",
+ "TOOLTIP": "Unique identifier for Chatwoot as the Service Provider - Configure this in your IdP settings"
+ },
+ "IDP_ENTITY_ID": {
+ "LABEL": "Identity Provider Entity ID",
+ "HELP": "Unique identifier for your identity provider (usually found in IdP configuration)",
+ "PLACEHOLDER": "https://your-idp.com/saml"
+ },
+ "UPDATE_BUTTON": "Update SAML Settings",
+ "API": {
+ "SUCCESS": "SAML settings updated successfully",
+ "ERROR": "Failed to update SAML settings",
+ "ERROR_LOADING": "Failed to load SAML settings",
+ "DISABLED": "SAML settings disabled successfully"
+ },
+ "VALIDATION": {
+ "REQUIRED_FIELDS": "SSO URL, Identity Provider Entity ID, and Certificate are required fields",
+ "SSO_URL_ERROR": "Please enter a valid SSO URL",
+ "CERTIFICATE_ERROR": "Certificate is required",
+ "IDP_ENTITY_ID_ERROR": "Identity Provider Entity ID is required"
+ },
+ "ENTERPRISE_PAYWALL": {
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade to an Enterprise plan to access SAML single sign-on and other advanced security features.",
+ "ASK_ADMIN": "Please reach out to your administrator for the upgrade."
+ },
+ "PAYWALL": {
+ "TITLE": "Upgrade to enable SAML SSO",
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade your plan to get access to SAML single sign-on and other advanced features.",
+ "UPGRADE_NOW": "Upgrade now",
+ "CANCEL_ANYTIME": "You can change or cancel your plan anytime"
+ },
+ "ATTRIBUTE_MAPPING": {
+ "TITLE": "SAML Attribute Setup",
+ "DESCRIPTION": "The following attribute mappings must be configured in your identity provider"
+ },
+ "INFO_SECTION": {
+ "TITLE": "Service Provider Information",
+ "TOOLTIP": "Copy these values and configure them in your Identity Provider to establish the SAML connection"
+ }
+ }
+ },
"CREATE_ACCOUNT": {
"NO_ACCOUNT_WARNING": "Uh oh! Nu am putut găsi niciun cont Chatwoot. Vă rugăm să creați un cont nou pentru a continua.",
"NEW_ACCOUNT": "Cont Nou",
@@ -418,5 +503,255 @@
"SWITCH_TO_REPLY": "Comutarea la Răspuns",
"TOGGLE_SNOOZE_DROPDOWN": "Comutați snooze dropdown"
}
+ },
+ "ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent assignment",
+ "DESCRIPTION": "Define policies to effectively manage workload and route conversations based on the needs of inboxes and agents. Learn more here"
+ },
+ "ASSIGNMENT_POLICY": {
+ "TITLE": "Assignment policy",
+ "DESCRIPTION": "Manage how conversations get assigned in inboxes.",
+ "FEATURES": [
+ "Assign by conversations evenly or by available capacity",
+ "Add fair distribution rules to avoid overloading any agent",
+ "Add inboxes to a policy - one policy per inbox"
+ ]
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "TITLE": "Agent capacity policy",
+ "DESCRIPTION": "Manage workload for agents.",
+ "FEATURES": [
+ "Define maximum conversations per inbox",
+ "Create exceptions based on labels and time",
+ "Add agents to a policy - one policy per agent"
+ ]
+ }
+ },
+ "AGENT_ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Assignment policy",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "ORDER": "Order",
+ "PRIORITY": "Prioritate",
+ "ACTIVE": "Activ",
+ "INACTIVE": "Inactive",
+ "POPOVER": "Added inboxes",
+ "EDIT": "Editare"
+ },
+ "NO_RECORDS_FOUND": "No assignment policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create assignment policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy created successfully",
+ "ERROR_MESSAGE": "Failed to create assignment policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit assignment policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_INBOX_DIALOG": {
+ "TITLE": "Add inbox",
+ "DESCRIPTION": "{inboxName} inbox is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Renunță"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update assignment policy"
+ },
+ "INBOX_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Inbox added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Inbox removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove inbox from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Descriere:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "STATUS": {
+ "LABEL": "Status:",
+ "PLACEHOLDER": "Select status",
+ "ACTIVE": "Policy is active",
+ "INACTIVE": "Policy is inactive"
+ },
+ "ASSIGNMENT_ORDER": {
+ "LABEL": "Assignment order",
+ "ROUND_ROBIN": {
+ "LABEL": "Round robin",
+ "DESCRIPTION": "Assign conversations evenly among agents."
+ },
+ "BALANCED": {
+ "LABEL": "Balanced",
+ "DESCRIPTION": "Assign conversations based on available capacity."
+ }
+ },
+ "ASSIGNMENT_PRIORITY": {
+ "LABEL": "Assignment priority",
+ "EARLIEST_CREATED": {
+ "LABEL": "Earliest created",
+ "DESCRIPTION": "The conversation that was created first gets assigned first."
+ },
+ "LONGEST_WAITING": {
+ "LABEL": "Longest waiting",
+ "DESCRIPTION": "The conversation waiting the longest gets assigned first."
+ }
+ },
+ "FAIR_DISTRIBUTION": {
+ "LABEL": "Fair distribution policy",
+ "DESCRIPTION": "Set the maximum number of conversations that can be assigned per agent within a time window to avoid overloading any one agent. This required field defaults to 100 conversations per hour.",
+ "INPUT_MAX": "Assign max",
+ "DURATION": "Conversations per agent in every"
+ },
+ "INBOXES": {
+ "LABEL": "Added inboxes",
+ "DESCRIPTION": "Add inboxes for which this policy will be applicable.",
+ "ADD_BUTTON": "Add inbox",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select inboxes to add",
+ "ADD_BUTTON": "Adaugă"
+ },
+ "EMPTY_STATE": "No inboxes added to this policy, add an inbox to get started",
+ "API": {
+ "SUCCESS_MESSAGE": "Inbox successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Assignment policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete assignment policy"
+ }
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent capacity",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "POPOVER": "Added agents",
+ "EDIT": "Editare"
+ },
+ "NO_RECORDS_FOUND": "No agent capacity policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create agent capacity policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy created successfully",
+ "ERROR_MESSAGE": "Failed to create agent capacity policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit agent capacity policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_AGENT_DIALOG": {
+ "TITLE": "Add agent",
+ "DESCRIPTION": "{agentName} is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Renunță"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update agent capacity policy"
+ },
+ "AGENT_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Agent added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Agent removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove agent from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Descriere:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "INBOX_CAPACITY_LIMIT": {
+ "LABEL": "Inbox capacity limits",
+ "ADD_BUTTON": "Add inbox",
+ "FIELD": {
+ "SELECT_INBOX": "Select inbox",
+ "MAX_CONVERSATIONS": "Max conversations",
+ "SET_LIMIT": "Set limit"
+ },
+ "EMPTY_STATE": "No inbox limit set"
+ },
+ "EXCLUSION_RULES": {
+ "LABEL": "Exclusion rules",
+ "DESCRIPTION": "Conversations that satisfy the following conditions would not count towards agent capacity",
+ "TAGS": {
+ "LABEL": "Exclude conversations tagged with specific labels",
+ "ADD_TAG": "add tag",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select tags to add"
+ },
+ "EMPTY_STATE": "No tags added to this policy."
+ },
+ "DURATION": {
+ "LABEL": "Exclude conversations older than a specified duration",
+ "PLACEHOLDER": "Set time"
+ }
+ },
+ "USERS": {
+ "LABEL": "Assigned agents",
+ "DESCRIPTION": "Add agents for which this policy will be applicable.",
+ "ADD_BUTTON": "Add agent",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select agents to add",
+ "ADD_BUTTON": "Adaugă"
+ },
+ "EMPTY_STATE": "No agents added",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Agent capacity policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete agent capacity policy"
+ }
+ },
+ "DELETE_POLICY": {
+ "TITLE": "Delete policy",
+ "DESCRIPTION": "Are you sure you want to delete this policy? This action cannot be undone.",
+ "CONFIRM_BUTTON_LABEL": "Şterge",
+ "CANCEL_BUTTON_LABEL": "Renunță"
+ }
}
}
diff --git a/app/javascript/dashboard/i18n/locale/ro/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/ro/whatsappTemplates.json
index e1697e19f..c18eeed20 100644
--- a/app/javascript/dashboard/i18n/locale/ro/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/ro/whatsappTemplates.json
@@ -40,6 +40,7 @@
"BUTTON_LABEL": "Button {index}",
"COUPON_CODE": "Enter coupon code (max 15 chars)",
"MEDIA_URL_LABEL": "Enter {type} URL",
+ "DOCUMENT_NAME_PLACEHOLDER": "Enter document filename (e.g., Invoice_2025.pdf)",
"BUTTON_PARAMETER": "Enter button parameter"
}
}
diff --git a/app/javascript/dashboard/i18n/locale/ru/automation.json b/app/javascript/dashboard/i18n/locale/ru/automation.json
index 08ead85fe..82a74a3b5 100644
--- a/app/javascript/dashboard/i18n/locale/ru/automation.json
+++ b/app/javascript/dashboard/i18n/locale/ru/automation.json
@@ -177,7 +177,8 @@
"REFERER_LINK": "Referrer Link",
"ASSIGNEE_NAME": "Назначено",
"TEAM_NAME": "Команда",
- "PRIORITY": "Приоритет"
+ "PRIORITY": "Приоритет",
+ "LABELS": "Категории"
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/ru/contact.json b/app/javascript/dashboard/i18n/locale/ru/contact.json
index 1898e5a15..4aa2268a3 100644
--- a/app/javascript/dashboard/i18n/locale/ru/contact.json
+++ b/app/javascript/dashboard/i18n/locale/ru/contact.json
@@ -610,6 +610,15 @@
"SEND_MESSAGE": "Отправить сообщение"
}
},
+ "TWILIO_OPTIONS": {
+ "LABEL": "Выбрать шаблон",
+ "SEARCH_PLACEHOLDER": "Поиск шаблонов",
+ "EMPTY_STATE": "Шаблоны не найдены",
+ "TEMPLATE_PARSER": {
+ "BACK": "Вернуться",
+ "SEND_MESSAGE": "Отправить сообщение"
+ }
+ },
"ACTION_BUTTONS": {
"DISCARD": "Сбросить",
"SEND": "Отправить ({keyCode})"
diff --git a/app/javascript/dashboard/i18n/locale/ru/contactFilters.json b/app/javascript/dashboard/i18n/locale/ru/contactFilters.json
index 5878654b7..b7cbeb30f 100644
--- a/app/javascript/dashboard/i18n/locale/ru/contactFilters.json
+++ b/app/javascript/dashboard/i18n/locale/ru/contactFilters.json
@@ -48,7 +48,8 @@
"CREATED_AT": "Дата создания",
"LAST_ACTIVITY": "Последние действия",
"REFERER_LINK": "Реферальная ссылка",
- "BLOCKED": "Заблокирован"
+ "BLOCKED": "Заблокирован",
+ "LABELS": "Категории"
},
"GROUPS": {
"STANDARD_FILTERS": "Стандартные фильтры",
diff --git a/app/javascript/dashboard/i18n/locale/ru/contentTemplates.json b/app/javascript/dashboard/i18n/locale/ru/contentTemplates.json
new file mode 100644
index 000000000..641944ca8
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/ru/contentTemplates.json
@@ -0,0 +1,51 @@
+{
+ "CONTENT_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Twilio Templates",
+ "SUBTITLE": "Select the Twilio template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Найти шаблоны",
+ "NO_TEMPLATES_FOUND": "Не найдено шаблонов для",
+ "NO_CONTENT": "Нет содержимого",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Категория",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No Twilio templates available. Click refresh to sync templates from Twilio.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Язык",
+ "TEMPLATE_BODY": "Тело шаблона",
+ "CATEGORY": "Категория"
+ },
+ "TYPES": {
+ "MEDIA": "Media",
+ "QUICK_REPLY": "Quick Reply",
+ "TEXT": "Текст"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Переменные",
+ "LANGUAGE": "Язык",
+ "CATEGORY": "Категория",
+ "VARIABLE_PLACEHOLDER": "Введите значение {variable}",
+ "GO_BACK_LABEL": "Вернуться",
+ "SEND_MESSAGE_LABEL": "Отправить сообщение",
+ "FORM_ERROR_MESSAGE": "Пожалуйста, заполните все переменные перед отправкой",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "MEDIA_URL_LABEL": "Enter full media URL",
+ "MEDIA_URL_PLACEHOLDER": "https://example.com/image.jpg"
+ },
+ "FORM": {
+ "BACK_BUTTON": "Назад",
+ "SEND_MESSAGE_BUTTON": "Отправить сообщение"
+ }
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/ru/conversation.json b/app/javascript/dashboard/i18n/locale/ru/conversation.json
index a39ab3040..b4b4c1f3f 100644
--- a/app/javascript/dashboard/i18n/locale/ru/conversation.json
+++ b/app/javascript/dashboard/i18n/locale/ru/conversation.json
@@ -35,6 +35,11 @@
"API_HOURS_WINDOW": "You can only reply to this conversation within {hours} hours",
"NOT_ASSIGNED_TO_YOU": "Этот диалог вам не назначен. Вы хотите назначить этот диалог себе?",
"ASSIGN_TO_ME": "Назначить мне",
+ "BOT_HANDOFF_MESSAGE": "You are responding to a conversation which is currently handled by an assistant or a bot.",
+ "BOT_HANDOFF_ACTION": "Mark open and assign to you",
+ "BOT_HANDOFF_REOPEN_ACTION": "Mark conversation open",
+ "BOT_HANDOFF_SUCCESS": "Conversation has been handed over to you",
+ "BOT_HANDOFF_ERROR": "Failed to take over the conversation. Please try again.",
"TWILIO_WHATSAPP_CAN_REPLY": "Вы можете ответить в этой беседе только с помощью шаблона сообщения",
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "Ограничение на 24 часа",
"OLD_INSTAGRAM_INBOX_REPLY_BANNER": "This Instagram account was migrated to the new Instagram channel inbox. All new messages will show up there. You won’t be able to send messages from this conversation anymore.",
@@ -66,6 +71,17 @@
"SHOW_LABELS": "Показать метки",
"HIDE_LABELS": "Скрыть метки"
},
+ "VOICE_CALL": {
+ "INCOMING_CALL": "Incoming call",
+ "OUTGOING_CALL": "Outgoing call",
+ "CALL_IN_PROGRESS": "Call in progress",
+ "NO_ANSWER": "No answer",
+ "MISSED_CALL": "Missed call",
+ "CALL_ENDED": "Call ended",
+ "NOT_ANSWERED_YET": "Not answered yet",
+ "THEY_ANSWERED": "They answered",
+ "YOU_ANSWERED": "You answered"
+ },
"HEADER": {
"RESOLVE_ACTION": "Завершить",
"REOPEN_ACTION": "Открыть заново",
diff --git a/app/javascript/dashboard/i18n/locale/ru/helpCenter.json b/app/javascript/dashboard/i18n/locale/ru/helpCenter.json
index aa59d0944..56d314f62 100644
--- a/app/javascript/dashboard/i18n/locale/ru/helpCenter.json
+++ b/app/javascript/dashboard/i18n/locale/ru/helpCenter.json
@@ -741,7 +741,8 @@
"LIVE_CHAT_WIDGET": {
"LABEL": "Виджет онлайн чата",
"PLACEHOLDER": "Выберите виджет онлайн чата",
- "HELP_TEXT": "Выберите виджет онлайн-чата, который появится в вашем центре помощи"
+ "HELP_TEXT": "Выберите виджет онлайн-чата, который появится в вашем центре помощи",
+ "NONE_OPTION": "No widget"
},
"BRAND_COLOR": {
"LABEL": "Цвет бренда"
@@ -807,6 +808,58 @@
"ERROR_MESSAGE": "Не удалось обновить портал"
}
}
+ },
+ "PDF_UPLOAD": {
+ "TITLE": "Upload PDF Document",
+ "DESCRIPTION": "Upload a PDF document to automatically generate FAQs using AI",
+ "DRAG_DROP_TEXT": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "ADDITIONAL_CONTEXT_LABEL": "Additional Context (Optional)",
+ "ADDITIONAL_CONTEXT_PLACEHOLDER": "Provide any additional context or instructions for FAQ generation...",
+ "UPLOADING": "Загружаем...",
+ "UPLOAD": "Upload & Process",
+ "CANCEL": "Отменить",
+ "ERROR_INVALID_TYPE": "Please select a valid PDF file",
+ "ERROR_FILE_TOO_LARGE": "File size must be less than 512MB",
+ "ERROR_UPLOAD_FAILED": "Failed to upload PDF. Please try again."
+ },
+ "PDF_DOCUMENTS": {
+ "TITLE": "PDF Documents",
+ "DESCRIPTION": "Manage uploaded PDF documents and generate FAQs from them",
+ "UPLOAD_PDF": "Upload PDF",
+ "UPLOAD_FIRST_PDF": "Upload your first PDF",
+ "UPLOADED_BY": "Uploaded by",
+ "GENERATE_FAQS": "Generate FAQs",
+ "GENERATING": "Создание...",
+ "CONFIRM_DELETE": "Are you sure you want to delete {filename}?",
+ "EMPTY_STATE": {
+ "TITLE": "No PDF documents yet",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQs using AI"
+ },
+ "STATUS": {
+ "UPLOADED": "Ready",
+ "PROCESSING": "Processing",
+ "PROCESSED": "Выполнено",
+ "FAILED": "Failed"
+ }
+ },
+ "CONTENT_GENERATION": {
+ "TITLE": "Content Generation",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQ content using AI",
+ "UPLOAD_TITLE": "Upload PDF Document",
+ "DRAG_DROP": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "UPLOADING": "Processing document...",
+ "UPLOAD_SUCCESS": "Document processed successfully!",
+ "UPLOAD_ERROR": "Failed to upload document. Please try again.",
+ "INVALID_FILE_TYPE": "Please select a valid PDF file",
+ "FILE_TOO_LARGE": "File size must be less than 512MB",
+ "GENERATED_CONTENT": "Generated FAQ Content",
+ "PUBLISH_SELECTED": "Publish Selected",
+ "PUBLISHING": "Publishing...",
+ "FROM_DOCUMENT": "From document",
+ "NO_CONTENT": "No generated content available. Upload a PDF document to get started.",
+ "LOADING": "Loading generated content..."
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/ru/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/ru/inboxMgmt.json
index 206a0663b..446bab3d3 100644
--- a/app/javascript/dashboard/i18n/locale/ru/inboxMgmt.json
+++ b/app/javascript/dashboard/i18n/locale/ru/inboxMgmt.json
@@ -272,8 +272,8 @@
},
"SUBMIT_BUTTON": "Создать канал WhatsApp",
"EMBEDDED_SIGNUP": {
- "TITLE": "Quick Setup with Meta",
- "DESC": "You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
+ "TITLE": "Quick setup with Meta",
+ "DESC": "Use the WhatsApp Embedded Signup flow to quickly connect new numbers. You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
"BENEFITS": {
"TITLE": "Benefits of Embedded Signup:",
"EASY_SETUP": "No manual configuration required",
@@ -281,9 +281,8 @@
"AUTO_CONFIG": "Automatic webhook and phone number configuration"
},
"LEARN_MORE": {
- "TEXT": "To learn more about integrated signup, pricing, and limitations, visit",
- "LINK_TEXT": "this link.",
- "LINK_URL": "https://developers.facebook.com/docs/whatsapp/embedded-signup/custom-flows/onboarding-business-app-users#limitations"
+ "TEXT": "To learn more about integrated signup, pricing, and limitations, visit {link}.",
+ "LINK_TEXT": "this link"
},
"SUBMIT_BUTTON": "Connect with WhatsApp Business",
"AUTH_PROCESSING": "Authenticating with Meta",
@@ -296,7 +295,9 @@
"INVALID_BUSINESS_DATA": "Invalid business data received from Facebook. Please try again.",
"SIGNUP_ERROR": "Signup error occurred",
"AUTH_NOT_COMPLETED": "Authentication not completed. Please restart the process.",
- "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured"
+ "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured",
+ "MANUAL_FALLBACK": "If your number is already connected to the WhatsApp Business Platform (API), or if you’re a tech provider onboarding your own number, please use the {link} flow",
+ "MANUAL_LINK_TEXT": "manual setup flow"
},
"API": {
"ERROR_MESSAGE": "Не удалось сохранить канал WhatsApp"
@@ -424,7 +425,51 @@
},
"AUTH": {
"TITLE": "Выберите источник",
- "DESC": "Chatwoot поддерживает виджет чата, страницу Facebook, профиль Twitter, Whatsapp, Email и т. д., как канал связи с пользователями. Если вы хотите настроить пользовательский канал, вы можете создать его с помощью канала API. Выберите один канал из списка ниже, чтобы продолжить."
+ "DESC": "Chatwoot поддерживает виджет чата, страницу Facebook, профиль Twitter, Whatsapp, Email и т. д., как канал связи с пользователями. Если вы хотите настроить пользовательский канал, вы можете создать его с помощью канала API. Выберите один канал из списка ниже, чтобы продолжить.",
+ "TITLE_NEXT": "Complete the setup",
+ "TITLE_FINISH": "Voilà!",
+ "CHANNEL": {
+ "WEBSITE": {
+ "TITLE": "Сайт",
+ "DESCRIPTION": "Create a live-chat widget"
+ },
+ "FACEBOOK": {
+ "TITLE": "Facebook",
+ "DESCRIPTION": "Connect your Facebook page"
+ },
+ "WHATSAPP": {
+ "TITLE": "WhatsApp",
+ "DESCRIPTION": "Support your customers on WhatsApp"
+ },
+ "EMAIL": {
+ "TITLE": "Email",
+ "DESCRIPTION": "Connect with Gmail, Outlook, or other providers"
+ },
+ "SMS": {
+ "TITLE": "SMS",
+ "DESCRIPTION": "Integrate SMS channel with Twilio or bandwidth"
+ },
+ "API": {
+ "TITLE": "API",
+ "DESCRIPTION": "Make a custom channel using our API"
+ },
+ "TELEGRAM": {
+ "TITLE": "Telegram",
+ "DESCRIPTION": "Configure Telegram channel using Bot token"
+ },
+ "LINE": {
+ "TITLE": "Line",
+ "DESCRIPTION": "Integrate your Line channel"
+ },
+ "INSTAGRAM": {
+ "TITLE": "Instagram",
+ "DESCRIPTION": "Connect your instagram account"
+ },
+ "VOICE": {
+ "TITLE": "Voice",
+ "DESCRIPTION": "Integrate with Twilio Voice"
+ }
+ }
},
"AGENTS": {
"TITLE": "Операторы",
@@ -478,7 +523,10 @@
"MESSAGE": "Теперь вы можете взаимодействовать с вашими клиентами через ваш новый канал. Удачной поддержки",
"BUTTON_TEXT": "Перейти",
"MORE_SETTINGS": "Больше параметров",
- "WEBSITE_SUCCESS": "Вы успешно создали источник-сайт. Скопируйте указанный ниже код и вставьте его на ваш сайт. В следующий раз, когда клиент напишет в чат, диалог автоматически появится здесь."
+ "WEBSITE_SUCCESS": "Вы успешно создали источник-сайт. Скопируйте указанный ниже код и вставьте его на ваш сайт. В следующий раз, когда клиент напишет в чат, диалог автоматически появится здесь.",
+ "WHATSAPP_QR_INSTRUCTION": "Scan the QR code above to quickly test your WhatsApp inbox",
+ "MESSENGER_QR_INSTRUCTION": "Scan the QR code above to quickly test your Facebook Messenger inbox",
+ "TELEGRAM_QR_INSTRUCTION": "Scan the QR code above to quickly test your Telegram inbox"
},
"REAUTH": "Войти заново",
"VIEW": "Просмотр",
@@ -868,9 +916,18 @@
"SCRIPT_SETTINGS": "\n window.chatwootSettings = {options};"
},
"EMAIL_PROVIDERS": {
- "MICROSOFT": "Microsoft",
- "GOOGLE": "Google",
- "OTHER_PROVIDERS": "Другие провайдеры"
+ "MICROSOFT": {
+ "TITLE": "Microsoft",
+ "DESCRIPTION": "Connect with Microsoft"
+ },
+ "GOOGLE": {
+ "TITLE": "Google",
+ "DESCRIPTION": "Connect with Google"
+ },
+ "OTHER_PROVIDERS": {
+ "TITLE": "Другие провайдеры",
+ "DESCRIPTION": "Connect with Other Providers"
+ }
},
"CHANNELS": {
"MESSENGER": "Messenger",
diff --git a/app/javascript/dashboard/i18n/locale/ru/integrations.json b/app/javascript/dashboard/i18n/locale/ru/integrations.json
index 6de478fb1..929c76186 100644
--- a/app/javascript/dashboard/i18n/locale/ru/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/ru/integrations.json
@@ -704,11 +704,28 @@
"ERROR_MESSAGE": "Произошла ошибка при создании документа, пожалуйста, попробуйте еще раз."
},
"FORM": {
+ "TYPE": {
+ "LABEL": "Document Type",
+ "URL": "URL",
+ "PDF": "PDF File"
+ },
"URL": {
"LABEL": "URL",
"PLACEHOLDER": "Введите URL документа",
"ERROR": "Пожалуйста, укажите корректный URL для документа"
},
+ "PDF_FILE": {
+ "LABEL": "PDF File",
+ "CHOOSE_FILE": "Choose PDF file",
+ "ERROR": "Please select a PDF file",
+ "HELP_TEXT": "Maximum file size: 10MB",
+ "INVALID_TYPE": "Please select a valid PDF file",
+ "TOO_LARGE": "File size exceeds 10MB limit"
+ },
+ "NAME": {
+ "LABEL": "Document Name (Optional)",
+ "PLACEHOLDER": "Enter a name for the document"
+ },
"ASSISTANT": {
"LABEL": "Ассистент",
"PLACEHOLDER": "Выберите ассистента",
@@ -744,6 +761,7 @@
"SELECTED": "Выбрано {count}",
"SELECT_ALL": "Select all ({count})",
"UNSELECT_ALL": "Unselect all ({count})",
+ "SEARCH_PLACEHOLDER": "Search FAQs...",
"BULK_APPROVE_BUTTON": "Одобрить",
"BULK_DELETE_BUTTON": "Удалить",
"BULK_APPROVE": {
diff --git a/app/javascript/dashboard/i18n/locale/ru/mfa.json b/app/javascript/dashboard/i18n/locale/ru/mfa.json
new file mode 100644
index 000000000..8088b627c
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/ru/mfa.json
@@ -0,0 +1,106 @@
+{
+ "MFA_SETTINGS": {
+ "TITLE": "Two-Factor Authentication",
+ "SUBTITLE": "Secure your account with TOTP-based authentication",
+ "DESCRIPTION": "Add an extra layer of security to your account using a time-based one-time password (TOTP)",
+ "STATUS_TITLE": "Authentication Status",
+ "STATUS_DESCRIPTION": "Manage your two-factor authentication settings and backup recovery codes",
+ "ENABLED": "Включено",
+ "DISABLED": "Выключено",
+ "STATUS_ENABLED": "Two-factor authentication is active",
+ "STATUS_ENABLED_DESC": "Your account is protected with an additional layer of security",
+ "ENABLE_BUTTON": "Enable Two-Factor Authentication",
+ "ENHANCE_SECURITY": "Enhance Your Account Security",
+ "ENHANCE_SECURITY_DESC": "Two-factor authentication adds an extra layer of security by requiring a verification code from your authenticator app in addition to your password.",
+ "SETUP": {
+ "STEP_NUMBER_1": "1",
+ "STEP_NUMBER_2": "2",
+ "STEP1_TITLE": "Scan QR Code with Your Authenticator App",
+ "STEP1_DESCRIPTION": "Use Google Authenticator, Authy, or any TOTP-compatible app",
+ "LOADING_QR": "Загрузка...",
+ "MANUAL_ENTRY": "Can't scan? Enter code manually",
+ "SECRET_KEY": "Secret Key",
+ "COPY": "Копировать",
+ "ENTER_CODE": "Enter the 6-digit code from your authenticator app",
+ "ENTER_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify & Continue",
+ "CANCEL": "Отменить",
+ "ERROR_STARTING": "MFA not enabled. Please contact administrator.",
+ "INVALID_CODE": "Invalid verification code",
+ "SECRET_COPIED": "Secret key copied to clipboard",
+ "SUCCESS": "Two-factor authentication has been enabled successfully"
+ },
+ "BACKUP": {
+ "TITLE": "Save Your Backup Codes",
+ "DESCRIPTION": "Keep these codes safe. Each can be used once if you lose access to your authenticator",
+ "IMPORTANT": "Important:",
+ "IMPORTANT_NOTE": " Save these codes in a secure location. You won't be able to see them again.",
+ "DOWNLOAD": "Скачать",
+ "COPY_ALL": "Copy All",
+ "CONFIRM": "I have saved my backup codes in a secure location and understand that I won't be able to see them again",
+ "COMPLETE_SETUP": "Complete Setup",
+ "CODES_COPIED": "Backup codes copied to clipboard"
+ },
+ "MANAGEMENT": {
+ "BACKUP_CODES": "Backup Codes",
+ "BACKUP_CODES_DESC": "Generate new codes if you've lost or used your existing ones",
+ "REGENERATE": "Regenerate Backup Codes",
+ "DISABLE_MFA": "Disable 2FA",
+ "DISABLE_MFA_DESC": "Remove two-factor authentication from your account",
+ "DISABLE_BUTTON": "Disable Two-Factor Authentication"
+ },
+ "DISABLE": {
+ "TITLE": "Disable Two-Factor Authentication",
+ "DESCRIPTION": "You'll need to enter your password and a verification code to disable two-factor authentication.",
+ "PASSWORD": "Пароль",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Disable 2FA",
+ "CANCEL": "Отменить",
+ "SUCCESS": "Two-factor authentication has been disabled",
+ "ERROR": "Failed to disable MFA. Please check your credentials."
+ },
+ "REGENERATE": {
+ "TITLE": "Regenerate Backup Codes",
+ "DESCRIPTION": "This will invalidate your existing backup codes and generate new ones. Enter your verification code to continue.",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Generate New Codes",
+ "CANCEL": "Отменить",
+ "NEW_CODES_TITLE": "New Backup Codes Generated",
+ "NEW_CODES_DESC": "Your old backup codes have been invalidated. Save these new codes in a secure location.",
+ "CODES_IMPORTANT": "Important:",
+ "CODES_IMPORTANT_NOTE": " Each code can only be used once. Save them before closing this window.",
+ "DOWNLOAD_CODES": "Download Codes",
+ "COPY_ALL_CODES": "Copy All Codes",
+ "CODES_SAVED": "I've Saved My Codes",
+ "SUCCESS": "New backup codes have been generated",
+ "ERROR": "Failed to regenerate backup codes"
+ }
+ },
+ "MFA_VERIFICATION": {
+ "TITLE": "Two-Factor Authentication",
+ "DESCRIPTION": "Enter your verification code to continue",
+ "AUTHENTICATOR_APP": "Authenticator App",
+ "BACKUP_CODE": "Backup Code",
+ "ENTER_OTP_CODE": "Enter 6-digit code from your authenticator app",
+ "ENTER_BACKUP_CODE": "Enter one of your backup codes",
+ "BACKUP_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify",
+ "TRY_ANOTHER_METHOD": "Try another verification method",
+ "CANCEL_LOGIN": "Cancel and return to login",
+ "HELP_TEXT": "Having trouble signing in?",
+ "LEARN_MORE": "Learn more about 2FA",
+ "HELP_MODAL": {
+ "TITLE": "Two-Factor Authentication Help",
+ "AUTHENTICATOR_TITLE": "Using an Authenticator App",
+ "AUTHENTICATOR_DESC": "Open your authenticator app (Google Authenticator, Authy, etc.) and enter the 6-digit code shown for your account.",
+ "BACKUP_TITLE": "Using a Backup Code",
+ "BACKUP_DESC": "If you don't have access to your authenticator app, you can use one of the backup codes you saved when setting up 2FA. Each code can only be used once.",
+ "CONTACT_TITLE": "Need More Help?",
+ "CONTACT_DESC_CLOUD": "If you've lost access to both your authenticator app and backup codes, please reach out to Chatwoot support for assistance.",
+ "CONTACT_DESC_SELF_HOSTED": "If you've lost access to both your authenticator app and backup codes, please contact your administrator for assistance."
+ },
+ "VERIFICATION_FAILED": "Verification failed. Please try again."
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/ru/settings.json b/app/javascript/dashboard/i18n/locale/ru/settings.json
index 749f11492..788f86940 100644
--- a/app/javascript/dashboard/i18n/locale/ru/settings.json
+++ b/app/javascript/dashboard/i18n/locale/ru/settings.json
@@ -51,6 +51,13 @@
"LARGER": "Крупнее",
"EXTRA_LARGE": "Очень большой"
}
+ },
+ "LANGUAGE": {
+ "TITLE": "Preferred Language",
+ "NOTE": "Choose the language you want to use.",
+ "UPDATE_SUCCESS": "Your Language settings have been updated successfully",
+ "UPDATE_ERROR": "There is an error while updating the language settings, please try again",
+ "USE_ACCOUNT_DEFAULT": "Use account default"
}
},
"MESSAGE_SIGNATURE_SECTION": {
@@ -73,6 +80,11 @@
"NOTE": "Обновление пароля сбросит вашу авторизацию на всех устройствах.",
"BTN_TEXT": "Изменить пароль"
},
+ "SECURITY_SECTION": {
+ "TITLE": "Security",
+ "NOTE": "Manage additional security features for your account.",
+ "MFA_BUTTON": "Manage Two-Factor Authentication"
+ },
"ACCESS_TOKEN": {
"TITLE": "Токен доступа",
"NOTE": "Этот токен может быть использован, если вы настраиваете интеграцию на основе API",
@@ -330,6 +342,7 @@
"REPORTS_LABEL": "Категории",
"REPORTS_INBOX": "Электронная почта",
"REPORTS_TEAM": "Команда",
+ "AGENT_ASSIGNMENT": "Agent Assignment",
"SET_AVAILABILITY_TITLE": "Установить себя",
"SET_YOUR_AVAILABILITY": "Настройте ваши рабочие часы",
"SLA": "SLA",
@@ -350,7 +363,8 @@
"INFO_TEXT": "Позволить системе автоматически отмечать вас в автономном режиме, если вы не используете приложение или приборную панель.",
"INFO_SHORT": "Автоматически отмечать оффлайн, когда вы не используете приложение."
},
- "DOCS": "Открыть документацию"
+ "DOCS": "Открыть документацию",
+ "SECURITY": "Security"
},
"BILLING_SETTINGS": {
"TITLE": "Платёж",
@@ -382,6 +396,77 @@
},
"NO_BILLING_USER": "Ваш платёжный счёт настраивается. Пожалуйста, обновите страницу и повторите попытку."
},
+ "SECURITY_SETTINGS": {
+ "TITLE": "Security",
+ "DESCRIPTION": "Manage your account security settings.",
+ "LINK_TEXT": "Learn more about SAML SSO",
+ "SAML": {
+ "TITLE": "SAML SSO",
+ "NOTE": "Configure SAML single sign-on for your account. Users will authenticate through your identity provider instead of using email/password.",
+ "ACS_URL": {
+ "LABEL": "ACS URL",
+ "TOOLTIP": "Assertion Consumer Service URL - Configure this URL in your IdP as the destination for SAML responses"
+ },
+ "SSO_URL": {
+ "LABEL": "SSO URL",
+ "HELP": "The URL where SAML authentication requests will be sent",
+ "PLACEHOLDER": "https://your-idp.com/saml/sso"
+ },
+ "CERTIFICATE": {
+ "LABEL": "Signing certificate in PEM format",
+ "HELP": "The public certificate from your identity provider used to verify SAML responses",
+ "PLACEHOLDER": "-----BEGIN CERTIFICATE-----\nMIIC..."
+ },
+ "FINGERPRINT": {
+ "LABEL": "Fingerprint",
+ "TOOLTIP": "SHA-1 fingerprint of the certificate - Use this to verify the certificate in your IdP configuration"
+ },
+ "COPY_SUCCESS": "Код скопирован в буфер обмена",
+ "SP_ENTITY_ID": {
+ "LABEL": "SP Entity ID",
+ "HELP": "Unique identifier for this application as a service provider (auto-generated).",
+ "TOOLTIP": "Unique identifier for Chatwoot as the Service Provider - Configure this in your IdP settings"
+ },
+ "IDP_ENTITY_ID": {
+ "LABEL": "Identity Provider Entity ID",
+ "HELP": "Unique identifier for your identity provider (usually found in IdP configuration)",
+ "PLACEHOLDER": "https://your-idp.com/saml"
+ },
+ "UPDATE_BUTTON": "Update SAML Settings",
+ "API": {
+ "SUCCESS": "SAML settings updated successfully",
+ "ERROR": "Failed to update SAML settings",
+ "ERROR_LOADING": "Failed to load SAML settings",
+ "DISABLED": "SAML settings disabled successfully"
+ },
+ "VALIDATION": {
+ "REQUIRED_FIELDS": "SSO URL, Identity Provider Entity ID, and Certificate are required fields",
+ "SSO_URL_ERROR": "Please enter a valid SSO URL",
+ "CERTIFICATE_ERROR": "Certificate is required",
+ "IDP_ENTITY_ID_ERROR": "Identity Provider Entity ID is required"
+ },
+ "ENTERPRISE_PAYWALL": {
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade to an Enterprise plan to access SAML single sign-on and other advanced security features.",
+ "ASK_ADMIN": "Пожалуйста, обратитесь к вашему администратору для обновления."
+ },
+ "PAYWALL": {
+ "TITLE": "Upgrade to enable SAML SSO",
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade your plan to get access to SAML single sign-on and other advanced features.",
+ "UPGRADE_NOW": "Обновить сейчас",
+ "CANCEL_ANYTIME": "Вы можете изменить или отменить ваш тарифный план в любое время"
+ },
+ "ATTRIBUTE_MAPPING": {
+ "TITLE": "SAML Attribute Setup",
+ "DESCRIPTION": "The following attribute mappings must be configured in your identity provider"
+ },
+ "INFO_SECTION": {
+ "TITLE": "Service Provider Information",
+ "TOOLTIP": "Copy these values and configure them in your Identity Provider to establish the SAML connection"
+ }
+ }
+ },
"CREATE_ACCOUNT": {
"NO_ACCOUNT_WARNING": "Ой! Мы не смогли найти ни одного аккаунта в Chatwoot. Пожалуйста, создайте новый аккаунт, чтобы продолжить.",
"NEW_ACCOUNT": "Новый аккаунт",
@@ -418,5 +503,255 @@
"SWITCH_TO_REPLY": "Переключиться на ответ",
"TOGGLE_SNOOZE_DROPDOWN": "Вкл/выкл повтор"
}
+ },
+ "ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent assignment",
+ "DESCRIPTION": "Define policies to effectively manage workload and route conversations based on the needs of inboxes and agents. Learn more here"
+ },
+ "ASSIGNMENT_POLICY": {
+ "TITLE": "Assignment policy",
+ "DESCRIPTION": "Manage how conversations get assigned in inboxes.",
+ "FEATURES": [
+ "Assign by conversations evenly or by available capacity",
+ "Add fair distribution rules to avoid overloading any agent",
+ "Add inboxes to a policy - one policy per inbox"
+ ]
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "TITLE": "Agent capacity policy",
+ "DESCRIPTION": "Manage workload for agents.",
+ "FEATURES": [
+ "Define maximum conversations per inbox",
+ "Create exceptions based on labels and time",
+ "Add agents to a policy - one policy per agent"
+ ]
+ }
+ },
+ "AGENT_ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Assignment policy",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "ORDER": "Order",
+ "PRIORITY": "Приоритет",
+ "ACTIVE": "Активно",
+ "INACTIVE": "Inactive",
+ "POPOVER": "Added inboxes",
+ "EDIT": "Редактировать"
+ },
+ "NO_RECORDS_FOUND": "No assignment policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create assignment policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy created successfully",
+ "ERROR_MESSAGE": "Failed to create assignment policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit assignment policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_INBOX_DIALOG": {
+ "TITLE": "Add inbox",
+ "DESCRIPTION": "{inboxName} inbox is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Отменить"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update assignment policy"
+ },
+ "INBOX_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Inbox added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Inbox removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove inbox from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Описание:",
+ "PLACEHOLDER": "Введите описание"
+ },
+ "STATUS": {
+ "LABEL": "Статус:",
+ "PLACEHOLDER": "Выбрать статус",
+ "ACTIVE": "Policy is active",
+ "INACTIVE": "Policy is inactive"
+ },
+ "ASSIGNMENT_ORDER": {
+ "LABEL": "Assignment order",
+ "ROUND_ROBIN": {
+ "LABEL": "Round robin",
+ "DESCRIPTION": "Assign conversations evenly among agents."
+ },
+ "BALANCED": {
+ "LABEL": "Balanced",
+ "DESCRIPTION": "Assign conversations based on available capacity."
+ }
+ },
+ "ASSIGNMENT_PRIORITY": {
+ "LABEL": "Assignment priority",
+ "EARLIEST_CREATED": {
+ "LABEL": "Earliest created",
+ "DESCRIPTION": "The conversation that was created first gets assigned first."
+ },
+ "LONGEST_WAITING": {
+ "LABEL": "Longest waiting",
+ "DESCRIPTION": "The conversation waiting the longest gets assigned first."
+ }
+ },
+ "FAIR_DISTRIBUTION": {
+ "LABEL": "Fair distribution policy",
+ "DESCRIPTION": "Set the maximum number of conversations that can be assigned per agent within a time window to avoid overloading any one agent. This required field defaults to 100 conversations per hour.",
+ "INPUT_MAX": "Assign max",
+ "DURATION": "Conversations per agent in every"
+ },
+ "INBOXES": {
+ "LABEL": "Added inboxes",
+ "DESCRIPTION": "Add inboxes for which this policy will be applicable.",
+ "ADD_BUTTON": "Add inbox",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select inboxes to add",
+ "ADD_BUTTON": "Добавить"
+ },
+ "EMPTY_STATE": "No inboxes added to this policy, add an inbox to get started",
+ "API": {
+ "SUCCESS_MESSAGE": "Inbox successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Assignment policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete assignment policy"
+ }
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent capacity",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "POPOVER": "Added agents",
+ "EDIT": "Редактировать"
+ },
+ "NO_RECORDS_FOUND": "No agent capacity policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create agent capacity policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy created successfully",
+ "ERROR_MESSAGE": "Failed to create agent capacity policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit agent capacity policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_AGENT_DIALOG": {
+ "TITLE": "Add agent",
+ "DESCRIPTION": "{agentName} is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Отменить"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update agent capacity policy"
+ },
+ "AGENT_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Agent added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Agent removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove agent from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Описание:",
+ "PLACEHOLDER": "Введите описание"
+ },
+ "INBOX_CAPACITY_LIMIT": {
+ "LABEL": "Inbox capacity limits",
+ "ADD_BUTTON": "Add inbox",
+ "FIELD": {
+ "SELECT_INBOX": "Select inbox",
+ "MAX_CONVERSATIONS": "Max conversations",
+ "SET_LIMIT": "Set limit"
+ },
+ "EMPTY_STATE": "No inbox limit set"
+ },
+ "EXCLUSION_RULES": {
+ "LABEL": "Exclusion rules",
+ "DESCRIPTION": "Conversations that satisfy the following conditions would not count towards agent capacity",
+ "TAGS": {
+ "LABEL": "Exclude conversations tagged with specific labels",
+ "ADD_TAG": "add tag",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select tags to add"
+ },
+ "EMPTY_STATE": "No tags added to this policy."
+ },
+ "DURATION": {
+ "LABEL": "Exclude conversations older than a specified duration",
+ "PLACEHOLDER": "Set time"
+ }
+ },
+ "USERS": {
+ "LABEL": "Assigned agents",
+ "DESCRIPTION": "Add agents for which this policy will be applicable.",
+ "ADD_BUTTON": "Add agent",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select agents to add",
+ "ADD_BUTTON": "Добавить"
+ },
+ "EMPTY_STATE": "No agents added",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Agent capacity policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete agent capacity policy"
+ }
+ },
+ "DELETE_POLICY": {
+ "TITLE": "Delete policy",
+ "DESCRIPTION": "Are you sure you want to delete this policy? This action cannot be undone.",
+ "CONFIRM_BUTTON_LABEL": "Удалить",
+ "CANCEL_BUTTON_LABEL": "Отменить"
+ }
}
}
diff --git a/app/javascript/dashboard/i18n/locale/ru/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/ru/whatsappTemplates.json
index a8effa8b8..7b6633d88 100644
--- a/app/javascript/dashboard/i18n/locale/ru/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/ru/whatsappTemplates.json
@@ -40,6 +40,7 @@
"BUTTON_LABEL": "Button {index}",
"COUPON_CODE": "Enter coupon code (max 15 chars)",
"MEDIA_URL_LABEL": "Enter {type} URL",
+ "DOCUMENT_NAME_PLACEHOLDER": "Enter document filename (e.g., Invoice_2025.pdf)",
"BUTTON_PARAMETER": "Enter button parameter"
}
}
diff --git a/app/javascript/dashboard/i18n/locale/sh/automation.json b/app/javascript/dashboard/i18n/locale/sh/automation.json
index 80274f488..43245a1d5 100644
--- a/app/javascript/dashboard/i18n/locale/sh/automation.json
+++ b/app/javascript/dashboard/i18n/locale/sh/automation.json
@@ -177,7 +177,8 @@
"REFERER_LINK": "Referrer Link",
"ASSIGNEE_NAME": "Assignee",
"TEAM_NAME": "Team",
- "PRIORITY": "Priority"
+ "PRIORITY": "Priority",
+ "LABELS": "Labels"
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/sh/contact.json b/app/javascript/dashboard/i18n/locale/sh/contact.json
index 735489a08..328e15aaa 100644
--- a/app/javascript/dashboard/i18n/locale/sh/contact.json
+++ b/app/javascript/dashboard/i18n/locale/sh/contact.json
@@ -610,6 +610,15 @@
"SEND_MESSAGE": "Send message"
}
},
+ "TWILIO_OPTIONS": {
+ "LABEL": "Select template",
+ "SEARCH_PLACEHOLDER": "Search templates",
+ "EMPTY_STATE": "No templates found",
+ "TEMPLATE_PARSER": {
+ "BACK": "Go back",
+ "SEND_MESSAGE": "Send message"
+ }
+ },
"ACTION_BUTTONS": {
"DISCARD": "Discard",
"SEND": "Send ({keyCode})"
diff --git a/app/javascript/dashboard/i18n/locale/sh/contactFilters.json b/app/javascript/dashboard/i18n/locale/sh/contactFilters.json
index bb3221c6e..4c62f0789 100644
--- a/app/javascript/dashboard/i18n/locale/sh/contactFilters.json
+++ b/app/javascript/dashboard/i18n/locale/sh/contactFilters.json
@@ -48,7 +48,8 @@
"CREATED_AT": "Created At",
"LAST_ACTIVITY": "Last Activity",
"REFERER_LINK": "Referrer link",
- "BLOCKED": "Blocked"
+ "BLOCKED": "Blocked",
+ "LABELS": "Labels"
},
"GROUPS": {
"STANDARD_FILTERS": "Standard Filters",
diff --git a/app/javascript/dashboard/i18n/locale/sh/contentTemplates.json b/app/javascript/dashboard/i18n/locale/sh/contentTemplates.json
new file mode 100644
index 000000000..a9b1d54c4
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/sh/contentTemplates.json
@@ -0,0 +1,51 @@
+{
+ "CONTENT_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Twilio Templates",
+ "SUBTITLE": "Select the Twilio template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Search Templates",
+ "NO_TEMPLATES_FOUND": "No templates found for",
+ "NO_CONTENT": "No content",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Category",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No Twilio templates available. Click refresh to sync templates from Twilio.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Language",
+ "TEMPLATE_BODY": "Template Body",
+ "CATEGORY": "Category"
+ },
+ "TYPES": {
+ "MEDIA": "Media",
+ "QUICK_REPLY": "Quick Reply",
+ "TEXT": "Text"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variables",
+ "LANGUAGE": "Language",
+ "CATEGORY": "Category",
+ "VARIABLE_PLACEHOLDER": "Enter {variable} value",
+ "GO_BACK_LABEL": "Go Back",
+ "SEND_MESSAGE_LABEL": "Send Message",
+ "FORM_ERROR_MESSAGE": "Please fill all variables before sending",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "MEDIA_URL_LABEL": "Enter full media URL",
+ "MEDIA_URL_PLACEHOLDER": "https://example.com/image.jpg"
+ },
+ "FORM": {
+ "BACK_BUTTON": "Back",
+ "SEND_MESSAGE_BUTTON": "Send Message"
+ }
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/sh/conversation.json b/app/javascript/dashboard/i18n/locale/sh/conversation.json
index 308f24f51..9fd39b70f 100644
--- a/app/javascript/dashboard/i18n/locale/sh/conversation.json
+++ b/app/javascript/dashboard/i18n/locale/sh/conversation.json
@@ -35,6 +35,11 @@
"API_HOURS_WINDOW": "You can only reply to this conversation within {hours} hours",
"NOT_ASSIGNED_TO_YOU": "This conversation is not assigned to you. Would you like to assign this conversation to yourself?",
"ASSIGN_TO_ME": "Assign to me",
+ "BOT_HANDOFF_MESSAGE": "You are responding to a conversation which is currently handled by an assistant or a bot.",
+ "BOT_HANDOFF_ACTION": "Mark open and assign to you",
+ "BOT_HANDOFF_REOPEN_ACTION": "Mark conversation open",
+ "BOT_HANDOFF_SUCCESS": "Conversation has been handed over to you",
+ "BOT_HANDOFF_ERROR": "Failed to take over the conversation. Please try again.",
"TWILIO_WHATSAPP_CAN_REPLY": "You can only reply to this conversation using a template message due to",
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "24 hour message window restriction",
"OLD_INSTAGRAM_INBOX_REPLY_BANNER": "This Instagram account was migrated to the new Instagram channel inbox. All new messages will show up there. You won’t be able to send messages from this conversation anymore.",
@@ -66,6 +71,17 @@
"SHOW_LABELS": "Show labels",
"HIDE_LABELS": "Hide labels"
},
+ "VOICE_CALL": {
+ "INCOMING_CALL": "Incoming call",
+ "OUTGOING_CALL": "Outgoing call",
+ "CALL_IN_PROGRESS": "Call in progress",
+ "NO_ANSWER": "No answer",
+ "MISSED_CALL": "Missed call",
+ "CALL_ENDED": "Call ended",
+ "NOT_ANSWERED_YET": "Not answered yet",
+ "THEY_ANSWERED": "They answered",
+ "YOU_ANSWERED": "You answered"
+ },
"HEADER": {
"RESOLVE_ACTION": "Resolve",
"REOPEN_ACTION": "Reopen",
diff --git a/app/javascript/dashboard/i18n/locale/sh/helpCenter.json b/app/javascript/dashboard/i18n/locale/sh/helpCenter.json
index fd2b1a788..0ab8d62ff 100644
--- a/app/javascript/dashboard/i18n/locale/sh/helpCenter.json
+++ b/app/javascript/dashboard/i18n/locale/sh/helpCenter.json
@@ -741,7 +741,8 @@
"LIVE_CHAT_WIDGET": {
"LABEL": "Live chat widget",
"PLACEHOLDER": "Select live chat widget",
- "HELP_TEXT": "Select a live chat widget that will appear on your help center"
+ "HELP_TEXT": "Select a live chat widget that will appear on your help center",
+ "NONE_OPTION": "No widget"
},
"BRAND_COLOR": {
"LABEL": "Brand color"
@@ -807,6 +808,58 @@
"ERROR_MESSAGE": "Unable to update portal"
}
}
+ },
+ "PDF_UPLOAD": {
+ "TITLE": "Upload PDF Document",
+ "DESCRIPTION": "Upload a PDF document to automatically generate FAQs using AI",
+ "DRAG_DROP_TEXT": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "ADDITIONAL_CONTEXT_LABEL": "Additional Context (Optional)",
+ "ADDITIONAL_CONTEXT_PLACEHOLDER": "Provide any additional context or instructions for FAQ generation...",
+ "UPLOADING": "Uploading...",
+ "UPLOAD": "Upload & Process",
+ "CANCEL": "Cancel",
+ "ERROR_INVALID_TYPE": "Please select a valid PDF file",
+ "ERROR_FILE_TOO_LARGE": "File size must be less than 512MB",
+ "ERROR_UPLOAD_FAILED": "Failed to upload PDF. Please try again."
+ },
+ "PDF_DOCUMENTS": {
+ "TITLE": "PDF Documents",
+ "DESCRIPTION": "Manage uploaded PDF documents and generate FAQs from them",
+ "UPLOAD_PDF": "Upload PDF",
+ "UPLOAD_FIRST_PDF": "Upload your first PDF",
+ "UPLOADED_BY": "Uploaded by",
+ "GENERATE_FAQS": "Generate FAQs",
+ "GENERATING": "Generating...",
+ "CONFIRM_DELETE": "Are you sure you want to delete {filename}?",
+ "EMPTY_STATE": {
+ "TITLE": "No PDF documents yet",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQs using AI"
+ },
+ "STATUS": {
+ "UPLOADED": "Ready",
+ "PROCESSING": "Processing",
+ "PROCESSED": "Completed",
+ "FAILED": "Failed"
+ }
+ },
+ "CONTENT_GENERATION": {
+ "TITLE": "Content Generation",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQ content using AI",
+ "UPLOAD_TITLE": "Upload PDF Document",
+ "DRAG_DROP": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "UPLOADING": "Processing document...",
+ "UPLOAD_SUCCESS": "Document processed successfully!",
+ "UPLOAD_ERROR": "Failed to upload document. Please try again.",
+ "INVALID_FILE_TYPE": "Please select a valid PDF file",
+ "FILE_TOO_LARGE": "File size must be less than 512MB",
+ "GENERATED_CONTENT": "Generated FAQ Content",
+ "PUBLISH_SELECTED": "Publish Selected",
+ "PUBLISHING": "Publishing...",
+ "FROM_DOCUMENT": "From document",
+ "NO_CONTENT": "No generated content available. Upload a PDF document to get started.",
+ "LOADING": "Loading generated content..."
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/sh/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/sh/inboxMgmt.json
index c456411cd..cfa768513 100644
--- a/app/javascript/dashboard/i18n/locale/sh/inboxMgmt.json
+++ b/app/javascript/dashboard/i18n/locale/sh/inboxMgmt.json
@@ -272,8 +272,8 @@
},
"SUBMIT_BUTTON": "Create WhatsApp Channel",
"EMBEDDED_SIGNUP": {
- "TITLE": "Quick Setup with Meta",
- "DESC": "You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
+ "TITLE": "Quick setup with Meta",
+ "DESC": "Use the WhatsApp Embedded Signup flow to quickly connect new numbers. You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
"BENEFITS": {
"TITLE": "Benefits of Embedded Signup:",
"EASY_SETUP": "No manual configuration required",
@@ -281,9 +281,8 @@
"AUTO_CONFIG": "Automatic webhook and phone number configuration"
},
"LEARN_MORE": {
- "TEXT": "To learn more about integrated signup, pricing, and limitations, visit",
- "LINK_TEXT": "this link.",
- "LINK_URL": "https://developers.facebook.com/docs/whatsapp/embedded-signup/custom-flows/onboarding-business-app-users#limitations"
+ "TEXT": "To learn more about integrated signup, pricing, and limitations, visit {link}.",
+ "LINK_TEXT": "this link"
},
"SUBMIT_BUTTON": "Connect with WhatsApp Business",
"AUTH_PROCESSING": "Authenticating with Meta",
@@ -296,7 +295,9 @@
"INVALID_BUSINESS_DATA": "Invalid business data received from Facebook. Please try again.",
"SIGNUP_ERROR": "Signup error occurred",
"AUTH_NOT_COMPLETED": "Authentication not completed. Please restart the process.",
- "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured"
+ "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured",
+ "MANUAL_FALLBACK": "If your number is already connected to the WhatsApp Business Platform (API), or if you’re a tech provider onboarding your own number, please use the {link} flow",
+ "MANUAL_LINK_TEXT": "manual setup flow"
},
"API": {
"ERROR_MESSAGE": "We were not able to save the WhatsApp channel"
@@ -424,7 +425,51 @@
},
"AUTH": {
"TITLE": "Choose a channel",
- "DESC": "Chatwoot supports live-chat widgets, Facebook Messenger, Twitter profiles, WhatsApp, Emails, etc., as channels. If you want to build a custom channel, you can create it using the API channel. To get started, choose one of the channels below."
+ "DESC": "Chatwoot supports live-chat widgets, Facebook Messenger, Twitter profiles, WhatsApp, Emails, etc., as channels. If you want to build a custom channel, you can create it using the API channel. To get started, choose one of the channels below.",
+ "TITLE_NEXT": "Complete the setup",
+ "TITLE_FINISH": "Voilà!",
+ "CHANNEL": {
+ "WEBSITE": {
+ "TITLE": "Website",
+ "DESCRIPTION": "Create a live-chat widget"
+ },
+ "FACEBOOK": {
+ "TITLE": "Facebook",
+ "DESCRIPTION": "Connect your Facebook page"
+ },
+ "WHATSAPP": {
+ "TITLE": "WhatsApp",
+ "DESCRIPTION": "Support your customers on WhatsApp"
+ },
+ "EMAIL": {
+ "TITLE": "Email",
+ "DESCRIPTION": "Connect with Gmail, Outlook, or other providers"
+ },
+ "SMS": {
+ "TITLE": "SMS",
+ "DESCRIPTION": "Integrate SMS channel with Twilio or bandwidth"
+ },
+ "API": {
+ "TITLE": "API",
+ "DESCRIPTION": "Make a custom channel using our API"
+ },
+ "TELEGRAM": {
+ "TITLE": "Telegram",
+ "DESCRIPTION": "Configure Telegram channel using Bot token"
+ },
+ "LINE": {
+ "TITLE": "Line",
+ "DESCRIPTION": "Integrate your Line channel"
+ },
+ "INSTAGRAM": {
+ "TITLE": "Instagram",
+ "DESCRIPTION": "Connect your instagram account"
+ },
+ "VOICE": {
+ "TITLE": "Voice",
+ "DESCRIPTION": "Integrate with Twilio Voice"
+ }
+ }
},
"AGENTS": {
"TITLE": "Agents",
@@ -478,7 +523,10 @@
"MESSAGE": "You can now engage with your customers through your new Channel. Happy supporting",
"BUTTON_TEXT": "Take me there",
"MORE_SETTINGS": "More settings",
- "WEBSITE_SUCCESS": "You have successfully finished creating a website channel. Copy the code shown below and paste it on your website. Next time a customer use the live chat, the conversation will automatically appear on your inbox."
+ "WEBSITE_SUCCESS": "You have successfully finished creating a website channel. Copy the code shown below and paste it on your website. Next time a customer use the live chat, the conversation will automatically appear on your inbox.",
+ "WHATSAPP_QR_INSTRUCTION": "Scan the QR code above to quickly test your WhatsApp inbox",
+ "MESSENGER_QR_INSTRUCTION": "Scan the QR code above to quickly test your Facebook Messenger inbox",
+ "TELEGRAM_QR_INSTRUCTION": "Scan the QR code above to quickly test your Telegram inbox"
},
"REAUTH": "Reauthorize",
"VIEW": "View",
@@ -868,9 +916,18 @@
"SCRIPT_SETTINGS": "\n window.chatwootSettings = {options};"
},
"EMAIL_PROVIDERS": {
- "MICROSOFT": "Microsoft",
- "GOOGLE": "Google",
- "OTHER_PROVIDERS": "Other Providers"
+ "MICROSOFT": {
+ "TITLE": "Microsoft",
+ "DESCRIPTION": "Connect with Microsoft"
+ },
+ "GOOGLE": {
+ "TITLE": "Google",
+ "DESCRIPTION": "Connect with Google"
+ },
+ "OTHER_PROVIDERS": {
+ "TITLE": "Other Providers",
+ "DESCRIPTION": "Connect with Other Providers"
+ }
},
"CHANNELS": {
"MESSENGER": "Messenger",
diff --git a/app/javascript/dashboard/i18n/locale/sh/integrations.json b/app/javascript/dashboard/i18n/locale/sh/integrations.json
index f0c7abbd3..03898d278 100644
--- a/app/javascript/dashboard/i18n/locale/sh/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/sh/integrations.json
@@ -704,11 +704,28 @@
"ERROR_MESSAGE": "There was an error creating the document, please try again."
},
"FORM": {
+ "TYPE": {
+ "LABEL": "Document Type",
+ "URL": "URL",
+ "PDF": "PDF File"
+ },
"URL": {
"LABEL": "URL",
"PLACEHOLDER": "Enter the URL of the document",
"ERROR": "Please provide a valid URL for the document"
},
+ "PDF_FILE": {
+ "LABEL": "PDF File",
+ "CHOOSE_FILE": "Choose PDF file",
+ "ERROR": "Please select a PDF file",
+ "HELP_TEXT": "Maximum file size: 10MB",
+ "INVALID_TYPE": "Please select a valid PDF file",
+ "TOO_LARGE": "File size exceeds 10MB limit"
+ },
+ "NAME": {
+ "LABEL": "Document Name (Optional)",
+ "PLACEHOLDER": "Enter a name for the document"
+ },
"ASSISTANT": {
"LABEL": "Assistant",
"PLACEHOLDER": "Select the assistant",
@@ -744,6 +761,7 @@
"SELECTED": "{count} selected",
"SELECT_ALL": "Select all ({count})",
"UNSELECT_ALL": "Unselect all ({count})",
+ "SEARCH_PLACEHOLDER": "Search FAQs...",
"BULK_APPROVE_BUTTON": "Approve",
"BULK_DELETE_BUTTON": "Delete",
"BULK_APPROVE": {
diff --git a/app/javascript/dashboard/i18n/locale/sh/mfa.json b/app/javascript/dashboard/i18n/locale/sh/mfa.json
new file mode 100644
index 000000000..f7556fdcf
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/sh/mfa.json
@@ -0,0 +1,106 @@
+{
+ "MFA_SETTINGS": {
+ "TITLE": "Two-Factor Authentication",
+ "SUBTITLE": "Secure your account with TOTP-based authentication",
+ "DESCRIPTION": "Add an extra layer of security to your account using a time-based one-time password (TOTP)",
+ "STATUS_TITLE": "Authentication Status",
+ "STATUS_DESCRIPTION": "Manage your two-factor authentication settings and backup recovery codes",
+ "ENABLED": "Enabled",
+ "DISABLED": "Disabled",
+ "STATUS_ENABLED": "Two-factor authentication is active",
+ "STATUS_ENABLED_DESC": "Your account is protected with an additional layer of security",
+ "ENABLE_BUTTON": "Enable Two-Factor Authentication",
+ "ENHANCE_SECURITY": "Enhance Your Account Security",
+ "ENHANCE_SECURITY_DESC": "Two-factor authentication adds an extra layer of security by requiring a verification code from your authenticator app in addition to your password.",
+ "SETUP": {
+ "STEP_NUMBER_1": "1",
+ "STEP_NUMBER_2": "2",
+ "STEP1_TITLE": "Scan QR Code with Your Authenticator App",
+ "STEP1_DESCRIPTION": "Use Google Authenticator, Authy, or any TOTP-compatible app",
+ "LOADING_QR": "Loading...",
+ "MANUAL_ENTRY": "Can't scan? Enter code manually",
+ "SECRET_KEY": "Secret Key",
+ "COPY": "Copy",
+ "ENTER_CODE": "Enter the 6-digit code from your authenticator app",
+ "ENTER_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify & Continue",
+ "CANCEL": "Cancel",
+ "ERROR_STARTING": "MFA not enabled. Please contact administrator.",
+ "INVALID_CODE": "Invalid verification code",
+ "SECRET_COPIED": "Secret key copied to clipboard",
+ "SUCCESS": "Two-factor authentication has been enabled successfully"
+ },
+ "BACKUP": {
+ "TITLE": "Save Your Backup Codes",
+ "DESCRIPTION": "Keep these codes safe. Each can be used once if you lose access to your authenticator",
+ "IMPORTANT": "Important:",
+ "IMPORTANT_NOTE": " Save these codes in a secure location. You won't be able to see them again.",
+ "DOWNLOAD": "Download",
+ "COPY_ALL": "Copy All",
+ "CONFIRM": "I have saved my backup codes in a secure location and understand that I won't be able to see them again",
+ "COMPLETE_SETUP": "Complete Setup",
+ "CODES_COPIED": "Backup codes copied to clipboard"
+ },
+ "MANAGEMENT": {
+ "BACKUP_CODES": "Backup Codes",
+ "BACKUP_CODES_DESC": "Generate new codes if you've lost or used your existing ones",
+ "REGENERATE": "Regenerate Backup Codes",
+ "DISABLE_MFA": "Disable 2FA",
+ "DISABLE_MFA_DESC": "Remove two-factor authentication from your account",
+ "DISABLE_BUTTON": "Disable Two-Factor Authentication"
+ },
+ "DISABLE": {
+ "TITLE": "Disable Two-Factor Authentication",
+ "DESCRIPTION": "You'll need to enter your password and a verification code to disable two-factor authentication.",
+ "PASSWORD": "Password",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Disable 2FA",
+ "CANCEL": "Cancel",
+ "SUCCESS": "Two-factor authentication has been disabled",
+ "ERROR": "Failed to disable MFA. Please check your credentials."
+ },
+ "REGENERATE": {
+ "TITLE": "Regenerate Backup Codes",
+ "DESCRIPTION": "This will invalidate your existing backup codes and generate new ones. Enter your verification code to continue.",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Generate New Codes",
+ "CANCEL": "Cancel",
+ "NEW_CODES_TITLE": "New Backup Codes Generated",
+ "NEW_CODES_DESC": "Your old backup codes have been invalidated. Save these new codes in a secure location.",
+ "CODES_IMPORTANT": "Important:",
+ "CODES_IMPORTANT_NOTE": " Each code can only be used once. Save them before closing this window.",
+ "DOWNLOAD_CODES": "Download Codes",
+ "COPY_ALL_CODES": "Copy All Codes",
+ "CODES_SAVED": "I've Saved My Codes",
+ "SUCCESS": "New backup codes have been generated",
+ "ERROR": "Failed to regenerate backup codes"
+ }
+ },
+ "MFA_VERIFICATION": {
+ "TITLE": "Two-Factor Authentication",
+ "DESCRIPTION": "Enter your verification code to continue",
+ "AUTHENTICATOR_APP": "Authenticator App",
+ "BACKUP_CODE": "Backup Code",
+ "ENTER_OTP_CODE": "Enter 6-digit code from your authenticator app",
+ "ENTER_BACKUP_CODE": "Enter one of your backup codes",
+ "BACKUP_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify",
+ "TRY_ANOTHER_METHOD": "Try another verification method",
+ "CANCEL_LOGIN": "Cancel and return to login",
+ "HELP_TEXT": "Having trouble signing in?",
+ "LEARN_MORE": "Learn more about 2FA",
+ "HELP_MODAL": {
+ "TITLE": "Two-Factor Authentication Help",
+ "AUTHENTICATOR_TITLE": "Using an Authenticator App",
+ "AUTHENTICATOR_DESC": "Open your authenticator app (Google Authenticator, Authy, etc.) and enter the 6-digit code shown for your account.",
+ "BACKUP_TITLE": "Using a Backup Code",
+ "BACKUP_DESC": "If you don't have access to your authenticator app, you can use one of the backup codes you saved when setting up 2FA. Each code can only be used once.",
+ "CONTACT_TITLE": "Need More Help?",
+ "CONTACT_DESC_CLOUD": "If you've lost access to both your authenticator app and backup codes, please reach out to Chatwoot support for assistance.",
+ "CONTACT_DESC_SELF_HOSTED": "If you've lost access to both your authenticator app and backup codes, please contact your administrator for assistance."
+ },
+ "VERIFICATION_FAILED": "Verification failed. Please try again."
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/sh/settings.json b/app/javascript/dashboard/i18n/locale/sh/settings.json
index d547538db..9ddc3b805 100644
--- a/app/javascript/dashboard/i18n/locale/sh/settings.json
+++ b/app/javascript/dashboard/i18n/locale/sh/settings.json
@@ -51,6 +51,13 @@
"LARGER": "Larger",
"EXTRA_LARGE": "Extra Large"
}
+ },
+ "LANGUAGE": {
+ "TITLE": "Preferred Language",
+ "NOTE": "Choose the language you want to use.",
+ "UPDATE_SUCCESS": "Your Language settings have been updated successfully",
+ "UPDATE_ERROR": "There is an error while updating the language settings, please try again",
+ "USE_ACCOUNT_DEFAULT": "Use account default"
}
},
"MESSAGE_SIGNATURE_SECTION": {
@@ -73,6 +80,11 @@
"NOTE": "Updating your password would reset your logins in multiple devices.",
"BTN_TEXT": "Change password"
},
+ "SECURITY_SECTION": {
+ "TITLE": "Security",
+ "NOTE": "Manage additional security features for your account.",
+ "MFA_BUTTON": "Manage Two-Factor Authentication"
+ },
"ACCESS_TOKEN": {
"TITLE": "Access Token",
"NOTE": "This token can be used if you are building an API based integration",
@@ -330,6 +342,7 @@
"REPORTS_LABEL": "Labels",
"REPORTS_INBOX": "Inbox",
"REPORTS_TEAM": "Team",
+ "AGENT_ASSIGNMENT": "Agent Assignment",
"SET_AVAILABILITY_TITLE": "Set yourself as",
"SET_YOUR_AVAILABILITY": "Set your availability",
"SLA": "SLA",
@@ -350,7 +363,8 @@
"INFO_TEXT": "Let the system automatically mark you offline when you aren't using the app or dashboard.",
"INFO_SHORT": "Automatically mark offline when you aren't using the app."
},
- "DOCS": "Read docs"
+ "DOCS": "Read docs",
+ "SECURITY": "Security"
},
"BILLING_SETTINGS": {
"TITLE": "Billing",
@@ -382,6 +396,77 @@
},
"NO_BILLING_USER": "Your billing account is being configured. Please refresh the page and try again."
},
+ "SECURITY_SETTINGS": {
+ "TITLE": "Security",
+ "DESCRIPTION": "Manage your account security settings.",
+ "LINK_TEXT": "Learn more about SAML SSO",
+ "SAML": {
+ "TITLE": "SAML SSO",
+ "NOTE": "Configure SAML single sign-on for your account. Users will authenticate through your identity provider instead of using email/password.",
+ "ACS_URL": {
+ "LABEL": "ACS URL",
+ "TOOLTIP": "Assertion Consumer Service URL - Configure this URL in your IdP as the destination for SAML responses"
+ },
+ "SSO_URL": {
+ "LABEL": "SSO URL",
+ "HELP": "The URL where SAML authentication requests will be sent",
+ "PLACEHOLDER": "https://your-idp.com/saml/sso"
+ },
+ "CERTIFICATE": {
+ "LABEL": "Signing certificate in PEM format",
+ "HELP": "The public certificate from your identity provider used to verify SAML responses",
+ "PLACEHOLDER": "-----BEGIN CERTIFICATE-----\nMIIC..."
+ },
+ "FINGERPRINT": {
+ "LABEL": "Fingerprint",
+ "TOOLTIP": "SHA-1 fingerprint of the certificate - Use this to verify the certificate in your IdP configuration"
+ },
+ "COPY_SUCCESS": "Copied to clipboard",
+ "SP_ENTITY_ID": {
+ "LABEL": "SP Entity ID",
+ "HELP": "Unique identifier for this application as a service provider (auto-generated).",
+ "TOOLTIP": "Unique identifier for Chatwoot as the Service Provider - Configure this in your IdP settings"
+ },
+ "IDP_ENTITY_ID": {
+ "LABEL": "Identity Provider Entity ID",
+ "HELP": "Unique identifier for your identity provider (usually found in IdP configuration)",
+ "PLACEHOLDER": "https://your-idp.com/saml"
+ },
+ "UPDATE_BUTTON": "Update SAML Settings",
+ "API": {
+ "SUCCESS": "SAML settings updated successfully",
+ "ERROR": "Failed to update SAML settings",
+ "ERROR_LOADING": "Failed to load SAML settings",
+ "DISABLED": "SAML settings disabled successfully"
+ },
+ "VALIDATION": {
+ "REQUIRED_FIELDS": "SSO URL, Identity Provider Entity ID, and Certificate are required fields",
+ "SSO_URL_ERROR": "Please enter a valid SSO URL",
+ "CERTIFICATE_ERROR": "Certificate is required",
+ "IDP_ENTITY_ID_ERROR": "Identity Provider Entity ID is required"
+ },
+ "ENTERPRISE_PAYWALL": {
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade to an Enterprise plan to access SAML single sign-on and other advanced security features.",
+ "ASK_ADMIN": "Please reach out to your administrator for the upgrade."
+ },
+ "PAYWALL": {
+ "TITLE": "Upgrade to enable SAML SSO",
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade your plan to get access to SAML single sign-on and other advanced features.",
+ "UPGRADE_NOW": "Upgrade now",
+ "CANCEL_ANYTIME": "You can change or cancel your plan anytime"
+ },
+ "ATTRIBUTE_MAPPING": {
+ "TITLE": "SAML Attribute Setup",
+ "DESCRIPTION": "The following attribute mappings must be configured in your identity provider"
+ },
+ "INFO_SECTION": {
+ "TITLE": "Service Provider Information",
+ "TOOLTIP": "Copy these values and configure them in your Identity Provider to establish the SAML connection"
+ }
+ }
+ },
"CREATE_ACCOUNT": {
"NO_ACCOUNT_WARNING": "Uh oh! We could not find any Chatwoot accounts. Please create a new account to continue.",
"NEW_ACCOUNT": "New Account",
@@ -418,5 +503,255 @@
"SWITCH_TO_REPLY": "Switch to Reply",
"TOGGLE_SNOOZE_DROPDOWN": "Toggle snooze dropdown"
}
+ },
+ "ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent assignment",
+ "DESCRIPTION": "Define policies to effectively manage workload and route conversations based on the needs of inboxes and agents. Learn more here"
+ },
+ "ASSIGNMENT_POLICY": {
+ "TITLE": "Assignment policy",
+ "DESCRIPTION": "Manage how conversations get assigned in inboxes.",
+ "FEATURES": [
+ "Assign by conversations evenly or by available capacity",
+ "Add fair distribution rules to avoid overloading any agent",
+ "Add inboxes to a policy - one policy per inbox"
+ ]
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "TITLE": "Agent capacity policy",
+ "DESCRIPTION": "Manage workload for agents.",
+ "FEATURES": [
+ "Define maximum conversations per inbox",
+ "Create exceptions based on labels and time",
+ "Add agents to a policy - one policy per agent"
+ ]
+ }
+ },
+ "AGENT_ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Assignment policy",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "ORDER": "Order",
+ "PRIORITY": "Priority",
+ "ACTIVE": "Active",
+ "INACTIVE": "Inactive",
+ "POPOVER": "Added inboxes",
+ "EDIT": "Edit"
+ },
+ "NO_RECORDS_FOUND": "No assignment policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create assignment policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy created successfully",
+ "ERROR_MESSAGE": "Failed to create assignment policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit assignment policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_INBOX_DIALOG": {
+ "TITLE": "Add inbox",
+ "DESCRIPTION": "{inboxName} inbox is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Cancel"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update assignment policy"
+ },
+ "INBOX_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Inbox added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Inbox removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove inbox from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Description:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "STATUS": {
+ "LABEL": "Status:",
+ "PLACEHOLDER": "Select status",
+ "ACTIVE": "Policy is active",
+ "INACTIVE": "Policy is inactive"
+ },
+ "ASSIGNMENT_ORDER": {
+ "LABEL": "Assignment order",
+ "ROUND_ROBIN": {
+ "LABEL": "Round robin",
+ "DESCRIPTION": "Assign conversations evenly among agents."
+ },
+ "BALANCED": {
+ "LABEL": "Balanced",
+ "DESCRIPTION": "Assign conversations based on available capacity."
+ }
+ },
+ "ASSIGNMENT_PRIORITY": {
+ "LABEL": "Assignment priority",
+ "EARLIEST_CREATED": {
+ "LABEL": "Earliest created",
+ "DESCRIPTION": "The conversation that was created first gets assigned first."
+ },
+ "LONGEST_WAITING": {
+ "LABEL": "Longest waiting",
+ "DESCRIPTION": "The conversation waiting the longest gets assigned first."
+ }
+ },
+ "FAIR_DISTRIBUTION": {
+ "LABEL": "Fair distribution policy",
+ "DESCRIPTION": "Set the maximum number of conversations that can be assigned per agent within a time window to avoid overloading any one agent. This required field defaults to 100 conversations per hour.",
+ "INPUT_MAX": "Assign max",
+ "DURATION": "Conversations per agent in every"
+ },
+ "INBOXES": {
+ "LABEL": "Added inboxes",
+ "DESCRIPTION": "Add inboxes for which this policy will be applicable.",
+ "ADD_BUTTON": "Add inbox",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select inboxes to add",
+ "ADD_BUTTON": "Add"
+ },
+ "EMPTY_STATE": "No inboxes added to this policy, add an inbox to get started",
+ "API": {
+ "SUCCESS_MESSAGE": "Inbox successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Assignment policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete assignment policy"
+ }
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent capacity",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "POPOVER": "Added agents",
+ "EDIT": "Edit"
+ },
+ "NO_RECORDS_FOUND": "No agent capacity policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create agent capacity policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy created successfully",
+ "ERROR_MESSAGE": "Failed to create agent capacity policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit agent capacity policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_AGENT_DIALOG": {
+ "TITLE": "Add agent",
+ "DESCRIPTION": "{agentName} is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Cancel"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update agent capacity policy"
+ },
+ "AGENT_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Agent added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Agent removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove agent from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Description:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "INBOX_CAPACITY_LIMIT": {
+ "LABEL": "Inbox capacity limits",
+ "ADD_BUTTON": "Add inbox",
+ "FIELD": {
+ "SELECT_INBOX": "Select inbox",
+ "MAX_CONVERSATIONS": "Max conversations",
+ "SET_LIMIT": "Set limit"
+ },
+ "EMPTY_STATE": "No inbox limit set"
+ },
+ "EXCLUSION_RULES": {
+ "LABEL": "Exclusion rules",
+ "DESCRIPTION": "Conversations that satisfy the following conditions would not count towards agent capacity",
+ "TAGS": {
+ "LABEL": "Exclude conversations tagged with specific labels",
+ "ADD_TAG": "add tag",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select tags to add"
+ },
+ "EMPTY_STATE": "No tags added to this policy."
+ },
+ "DURATION": {
+ "LABEL": "Exclude conversations older than a specified duration",
+ "PLACEHOLDER": "Set time"
+ }
+ },
+ "USERS": {
+ "LABEL": "Assigned agents",
+ "DESCRIPTION": "Add agents for which this policy will be applicable.",
+ "ADD_BUTTON": "Add agent",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select agents to add",
+ "ADD_BUTTON": "Add"
+ },
+ "EMPTY_STATE": "No agents added",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Agent capacity policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete agent capacity policy"
+ }
+ },
+ "DELETE_POLICY": {
+ "TITLE": "Delete policy",
+ "DESCRIPTION": "Are you sure you want to delete this policy? This action cannot be undone.",
+ "CONFIRM_BUTTON_LABEL": "Delete",
+ "CANCEL_BUTTON_LABEL": "Cancel"
+ }
}
}
diff --git a/app/javascript/dashboard/i18n/locale/sh/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/sh/whatsappTemplates.json
index 5f53faaa8..cf28312dc 100644
--- a/app/javascript/dashboard/i18n/locale/sh/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/sh/whatsappTemplates.json
@@ -40,6 +40,7 @@
"BUTTON_LABEL": "Button {index}",
"COUPON_CODE": "Enter coupon code (max 15 chars)",
"MEDIA_URL_LABEL": "Enter {type} URL",
+ "DOCUMENT_NAME_PLACEHOLDER": "Enter document filename (e.g., Invoice_2025.pdf)",
"BUTTON_PARAMETER": "Enter button parameter"
}
}
diff --git a/app/javascript/dashboard/i18n/locale/sk/automation.json b/app/javascript/dashboard/i18n/locale/sk/automation.json
index 2ea93e17a..1b945a0c1 100644
--- a/app/javascript/dashboard/i18n/locale/sk/automation.json
+++ b/app/javascript/dashboard/i18n/locale/sk/automation.json
@@ -177,7 +177,8 @@
"REFERER_LINK": "Referrer Link",
"ASSIGNEE_NAME": "Assignee",
"TEAM_NAME": "Team",
- "PRIORITY": "Priority"
+ "PRIORITY": "Priority",
+ "LABELS": "Labels"
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/sk/contact.json b/app/javascript/dashboard/i18n/locale/sk/contact.json
index 9bab7e8fd..c23a221ec 100644
--- a/app/javascript/dashboard/i18n/locale/sk/contact.json
+++ b/app/javascript/dashboard/i18n/locale/sk/contact.json
@@ -610,6 +610,15 @@
"SEND_MESSAGE": "Poslať správu"
}
},
+ "TWILIO_OPTIONS": {
+ "LABEL": "Select template",
+ "SEARCH_PLACEHOLDER": "Search templates",
+ "EMPTY_STATE": "No templates found",
+ "TEMPLATE_PARSER": {
+ "BACK": "Go back",
+ "SEND_MESSAGE": "Poslať správu"
+ }
+ },
"ACTION_BUTTONS": {
"DISCARD": "Discard",
"SEND": "Send ({keyCode})"
diff --git a/app/javascript/dashboard/i18n/locale/sk/contactFilters.json b/app/javascript/dashboard/i18n/locale/sk/contactFilters.json
index a8aa60651..1ecfb2e20 100644
--- a/app/javascript/dashboard/i18n/locale/sk/contactFilters.json
+++ b/app/javascript/dashboard/i18n/locale/sk/contactFilters.json
@@ -48,7 +48,8 @@
"CREATED_AT": "Created At",
"LAST_ACTIVITY": "Posledná aktivita",
"REFERER_LINK": "Referrer link",
- "BLOCKED": "Blocked"
+ "BLOCKED": "Blocked",
+ "LABELS": "Labels"
},
"GROUPS": {
"STANDARD_FILTERS": "Standard Filters",
diff --git a/app/javascript/dashboard/i18n/locale/sk/contentTemplates.json b/app/javascript/dashboard/i18n/locale/sk/contentTemplates.json
new file mode 100644
index 000000000..5c9451243
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/sk/contentTemplates.json
@@ -0,0 +1,51 @@
+{
+ "CONTENT_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Twilio Templates",
+ "SUBTITLE": "Select the Twilio template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Search Templates",
+ "NO_TEMPLATES_FOUND": "No templates found for",
+ "NO_CONTENT": "No content",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Category",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No Twilio templates available. Click refresh to sync templates from Twilio.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Language",
+ "TEMPLATE_BODY": "Template Body",
+ "CATEGORY": "Category"
+ },
+ "TYPES": {
+ "MEDIA": "Media",
+ "QUICK_REPLY": "Quick Reply",
+ "TEXT": "Text"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variables",
+ "LANGUAGE": "Language",
+ "CATEGORY": "Category",
+ "VARIABLE_PLACEHOLDER": "Enter {variable} value",
+ "GO_BACK_LABEL": "Go Back",
+ "SEND_MESSAGE_LABEL": "Send Message",
+ "FORM_ERROR_MESSAGE": "Please fill all variables before sending",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "MEDIA_URL_LABEL": "Enter full media URL",
+ "MEDIA_URL_PLACEHOLDER": "https://example.com/image.jpg"
+ },
+ "FORM": {
+ "BACK_BUTTON": "Späť",
+ "SEND_MESSAGE_BUTTON": "Send Message"
+ }
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/sk/conversation.json b/app/javascript/dashboard/i18n/locale/sk/conversation.json
index 586751810..99dde6c92 100644
--- a/app/javascript/dashboard/i18n/locale/sk/conversation.json
+++ b/app/javascript/dashboard/i18n/locale/sk/conversation.json
@@ -35,6 +35,11 @@
"API_HOURS_WINDOW": "You can only reply to this conversation within {hours} hours",
"NOT_ASSIGNED_TO_YOU": "This conversation is not assigned to you. Would you like to assign this conversation to yourself?",
"ASSIGN_TO_ME": "Prideliť mne",
+ "BOT_HANDOFF_MESSAGE": "You are responding to a conversation which is currently handled by an assistant or a bot.",
+ "BOT_HANDOFF_ACTION": "Mark open and assign to you",
+ "BOT_HANDOFF_REOPEN_ACTION": "Mark conversation open",
+ "BOT_HANDOFF_SUCCESS": "Conversation has been handed over to you",
+ "BOT_HANDOFF_ERROR": "Failed to take over the conversation. Please try again.",
"TWILIO_WHATSAPP_CAN_REPLY": "Na túto konverzáciu môžete odpovedať len pomocou šablóny správy z dôvodu",
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "24-hodinové obmedzenie okna správ",
"OLD_INSTAGRAM_INBOX_REPLY_BANNER": "This Instagram account was migrated to the new Instagram channel inbox. All new messages will show up there. You won’t be able to send messages from this conversation anymore.",
@@ -66,6 +71,17 @@
"SHOW_LABELS": "Show labels",
"HIDE_LABELS": "Hide labels"
},
+ "VOICE_CALL": {
+ "INCOMING_CALL": "Incoming call",
+ "OUTGOING_CALL": "Outgoing call",
+ "CALL_IN_PROGRESS": "Call in progress",
+ "NO_ANSWER": "No answer",
+ "MISSED_CALL": "Missed call",
+ "CALL_ENDED": "Call ended",
+ "NOT_ANSWERED_YET": "Not answered yet",
+ "THEY_ANSWERED": "They answered",
+ "YOU_ANSWERED": "You answered"
+ },
"HEADER": {
"RESOLVE_ACTION": "Vyriešiť",
"REOPEN_ACTION": "Znovu otvoriť",
diff --git a/app/javascript/dashboard/i18n/locale/sk/helpCenter.json b/app/javascript/dashboard/i18n/locale/sk/helpCenter.json
index 9dfb60107..0c9f1c823 100644
--- a/app/javascript/dashboard/i18n/locale/sk/helpCenter.json
+++ b/app/javascript/dashboard/i18n/locale/sk/helpCenter.json
@@ -741,7 +741,8 @@
"LIVE_CHAT_WIDGET": {
"LABEL": "Live chat widget",
"PLACEHOLDER": "Select live chat widget",
- "HELP_TEXT": "Select a live chat widget that will appear on your help center"
+ "HELP_TEXT": "Select a live chat widget that will appear on your help center",
+ "NONE_OPTION": "No widget"
},
"BRAND_COLOR": {
"LABEL": "Brand color"
@@ -807,6 +808,58 @@
"ERROR_MESSAGE": "Unable to update portal"
}
}
+ },
+ "PDF_UPLOAD": {
+ "TITLE": "Upload PDF Document",
+ "DESCRIPTION": "Upload a PDF document to automatically generate FAQs using AI",
+ "DRAG_DROP_TEXT": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "ADDITIONAL_CONTEXT_LABEL": "Additional Context (Optional)",
+ "ADDITIONAL_CONTEXT_PLACEHOLDER": "Provide any additional context or instructions for FAQ generation...",
+ "UPLOADING": "Nahrávanie...",
+ "UPLOAD": "Upload & Process",
+ "CANCEL": "Zrušiť",
+ "ERROR_INVALID_TYPE": "Please select a valid PDF file",
+ "ERROR_FILE_TOO_LARGE": "File size must be less than 512MB",
+ "ERROR_UPLOAD_FAILED": "Failed to upload PDF. Please try again."
+ },
+ "PDF_DOCUMENTS": {
+ "TITLE": "PDF Documents",
+ "DESCRIPTION": "Manage uploaded PDF documents and generate FAQs from them",
+ "UPLOAD_PDF": "Upload PDF",
+ "UPLOAD_FIRST_PDF": "Upload your first PDF",
+ "UPLOADED_BY": "Uploaded by",
+ "GENERATE_FAQS": "Generate FAQs",
+ "GENERATING": "Generating...",
+ "CONFIRM_DELETE": "Are you sure you want to delete {filename}?",
+ "EMPTY_STATE": {
+ "TITLE": "No PDF documents yet",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQs using AI"
+ },
+ "STATUS": {
+ "UPLOADED": "Ready",
+ "PROCESSING": "Processing",
+ "PROCESSED": "Completed",
+ "FAILED": "Failed"
+ }
+ },
+ "CONTENT_GENERATION": {
+ "TITLE": "Content Generation",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQ content using AI",
+ "UPLOAD_TITLE": "Upload PDF Document",
+ "DRAG_DROP": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "UPLOADING": "Processing document...",
+ "UPLOAD_SUCCESS": "Document processed successfully!",
+ "UPLOAD_ERROR": "Failed to upload document. Please try again.",
+ "INVALID_FILE_TYPE": "Please select a valid PDF file",
+ "FILE_TOO_LARGE": "File size must be less than 512MB",
+ "GENERATED_CONTENT": "Generated FAQ Content",
+ "PUBLISH_SELECTED": "Publish Selected",
+ "PUBLISHING": "Publishing...",
+ "FROM_DOCUMENT": "From document",
+ "NO_CONTENT": "No generated content available. Upload a PDF document to get started.",
+ "LOADING": "Loading generated content..."
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/sk/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/sk/inboxMgmt.json
index 884133023..ede5295e9 100644
--- a/app/javascript/dashboard/i18n/locale/sk/inboxMgmt.json
+++ b/app/javascript/dashboard/i18n/locale/sk/inboxMgmt.json
@@ -272,8 +272,8 @@
},
"SUBMIT_BUTTON": "Create WhatsApp Channel",
"EMBEDDED_SIGNUP": {
- "TITLE": "Quick Setup with Meta",
- "DESC": "You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
+ "TITLE": "Quick setup with Meta",
+ "DESC": "Use the WhatsApp Embedded Signup flow to quickly connect new numbers. You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
"BENEFITS": {
"TITLE": "Benefits of Embedded Signup:",
"EASY_SETUP": "No manual configuration required",
@@ -281,9 +281,8 @@
"AUTO_CONFIG": "Automatic webhook and phone number configuration"
},
"LEARN_MORE": {
- "TEXT": "To learn more about integrated signup, pricing, and limitations, visit",
- "LINK_TEXT": "this link.",
- "LINK_URL": "https://developers.facebook.com/docs/whatsapp/embedded-signup/custom-flows/onboarding-business-app-users#limitations"
+ "TEXT": "To learn more about integrated signup, pricing, and limitations, visit {link}.",
+ "LINK_TEXT": "this link"
},
"SUBMIT_BUTTON": "Connect with WhatsApp Business",
"AUTH_PROCESSING": "Authenticating with Meta",
@@ -296,7 +295,9 @@
"INVALID_BUSINESS_DATA": "Invalid business data received from Facebook. Please try again.",
"SIGNUP_ERROR": "Signup error occurred",
"AUTH_NOT_COMPLETED": "Authentication not completed. Please restart the process.",
- "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured"
+ "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured",
+ "MANUAL_FALLBACK": "If your number is already connected to the WhatsApp Business Platform (API), or if you’re a tech provider onboarding your own number, please use the {link} flow",
+ "MANUAL_LINK_TEXT": "manual setup flow"
},
"API": {
"ERROR_MESSAGE": "We were not able to save the WhatsApp channel"
@@ -424,7 +425,51 @@
},
"AUTH": {
"TITLE": "Vybrať kanál",
- "DESC": "Chatwoot supports live-chat widgets, Facebook Messenger, Twitter profiles, WhatsApp, Emails, etc., as channels. If you want to build a custom channel, you can create it using the API channel. To get started, choose one of the channels below."
+ "DESC": "Chatwoot supports live-chat widgets, Facebook Messenger, Twitter profiles, WhatsApp, Emails, etc., as channels. If you want to build a custom channel, you can create it using the API channel. To get started, choose one of the channels below.",
+ "TITLE_NEXT": "Complete the setup",
+ "TITLE_FINISH": "Voilà!",
+ "CHANNEL": {
+ "WEBSITE": {
+ "TITLE": "Website",
+ "DESCRIPTION": "Create a live-chat widget"
+ },
+ "FACEBOOK": {
+ "TITLE": "Facebook",
+ "DESCRIPTION": "Connect your Facebook page"
+ },
+ "WHATSAPP": {
+ "TITLE": "WhatsApp",
+ "DESCRIPTION": "Support your customers on WhatsApp"
+ },
+ "EMAIL": {
+ "TITLE": "Email",
+ "DESCRIPTION": "Connect with Gmail, Outlook, or other providers"
+ },
+ "SMS": {
+ "TITLE": "SMS",
+ "DESCRIPTION": "Integrate SMS channel with Twilio or bandwidth"
+ },
+ "API": {
+ "TITLE": "API",
+ "DESCRIPTION": "Make a custom channel using our API"
+ },
+ "TELEGRAM": {
+ "TITLE": "Telegram",
+ "DESCRIPTION": "Configure Telegram channel using Bot token"
+ },
+ "LINE": {
+ "TITLE": "Line",
+ "DESCRIPTION": "Integrate your Line channel"
+ },
+ "INSTAGRAM": {
+ "TITLE": "Instagram",
+ "DESCRIPTION": "Connect your instagram account"
+ },
+ "VOICE": {
+ "TITLE": "Voice",
+ "DESCRIPTION": "Integrate with Twilio Voice"
+ }
+ }
},
"AGENTS": {
"TITLE": "Agenti",
@@ -478,7 +523,10 @@
"MESSAGE": "You can now engage with your customers through your new Channel. Happy supporting",
"BUTTON_TEXT": "Take me there",
"MORE_SETTINGS": "More settings",
- "WEBSITE_SUCCESS": "You have successfully finished creating a website channel. Copy the code shown below and paste it on your website. Next time a customer use the live chat, the conversation will automatically appear on your inbox."
+ "WEBSITE_SUCCESS": "You have successfully finished creating a website channel. Copy the code shown below and paste it on your website. Next time a customer use the live chat, the conversation will automatically appear on your inbox.",
+ "WHATSAPP_QR_INSTRUCTION": "Scan the QR code above to quickly test your WhatsApp inbox",
+ "MESSENGER_QR_INSTRUCTION": "Scan the QR code above to quickly test your Facebook Messenger inbox",
+ "TELEGRAM_QR_INSTRUCTION": "Scan the QR code above to quickly test your Telegram inbox"
},
"REAUTH": "Reauthorize",
"VIEW": "Zobraziť",
@@ -868,9 +916,18 @@
"SCRIPT_SETTINGS": "\n window.chatwootSettings = {options};"
},
"EMAIL_PROVIDERS": {
- "MICROSOFT": "Microsoft",
- "GOOGLE": "Google",
- "OTHER_PROVIDERS": "Other Providers"
+ "MICROSOFT": {
+ "TITLE": "Microsoft",
+ "DESCRIPTION": "Connect with Microsoft"
+ },
+ "GOOGLE": {
+ "TITLE": "Google",
+ "DESCRIPTION": "Connect with Google"
+ },
+ "OTHER_PROVIDERS": {
+ "TITLE": "Other Providers",
+ "DESCRIPTION": "Connect with Other Providers"
+ }
},
"CHANNELS": {
"MESSENGER": "Messenger",
diff --git a/app/javascript/dashboard/i18n/locale/sk/integrations.json b/app/javascript/dashboard/i18n/locale/sk/integrations.json
index 55cd79768..1bf86ffe2 100644
--- a/app/javascript/dashboard/i18n/locale/sk/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/sk/integrations.json
@@ -704,11 +704,28 @@
"ERROR_MESSAGE": "There was an error creating the document, please try again."
},
"FORM": {
+ "TYPE": {
+ "LABEL": "Document Type",
+ "URL": "URL",
+ "PDF": "PDF File"
+ },
"URL": {
"LABEL": "URL",
"PLACEHOLDER": "Enter the URL of the document",
"ERROR": "Please provide a valid URL for the document"
},
+ "PDF_FILE": {
+ "LABEL": "PDF File",
+ "CHOOSE_FILE": "Choose PDF file",
+ "ERROR": "Please select a PDF file",
+ "HELP_TEXT": "Maximum file size: 10MB",
+ "INVALID_TYPE": "Please select a valid PDF file",
+ "TOO_LARGE": "File size exceeds 10MB limit"
+ },
+ "NAME": {
+ "LABEL": "Document Name (Optional)",
+ "PLACEHOLDER": "Enter a name for the document"
+ },
"ASSISTANT": {
"LABEL": "Assistant",
"PLACEHOLDER": "Select the assistant",
@@ -744,6 +761,7 @@
"SELECTED": "{count} selected",
"SELECT_ALL": "Select all ({count})",
"UNSELECT_ALL": "Unselect all ({count})",
+ "SEARCH_PLACEHOLDER": "Search FAQs...",
"BULK_APPROVE_BUTTON": "Approve",
"BULK_DELETE_BUTTON": "Vymazať",
"BULK_APPROVE": {
diff --git a/app/javascript/dashboard/i18n/locale/sk/mfa.json b/app/javascript/dashboard/i18n/locale/sk/mfa.json
new file mode 100644
index 000000000..134c98d4a
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/sk/mfa.json
@@ -0,0 +1,106 @@
+{
+ "MFA_SETTINGS": {
+ "TITLE": "Two-Factor Authentication",
+ "SUBTITLE": "Secure your account with TOTP-based authentication",
+ "DESCRIPTION": "Add an extra layer of security to your account using a time-based one-time password (TOTP)",
+ "STATUS_TITLE": "Authentication Status",
+ "STATUS_DESCRIPTION": "Manage your two-factor authentication settings and backup recovery codes",
+ "ENABLED": "Zapnuté",
+ "DISABLED": "Vypnuté",
+ "STATUS_ENABLED": "Two-factor authentication is active",
+ "STATUS_ENABLED_DESC": "Your account is protected with an additional layer of security",
+ "ENABLE_BUTTON": "Enable Two-Factor Authentication",
+ "ENHANCE_SECURITY": "Enhance Your Account Security",
+ "ENHANCE_SECURITY_DESC": "Two-factor authentication adds an extra layer of security by requiring a verification code from your authenticator app in addition to your password.",
+ "SETUP": {
+ "STEP_NUMBER_1": "1",
+ "STEP_NUMBER_2": "2",
+ "STEP1_TITLE": "Scan QR Code with Your Authenticator App",
+ "STEP1_DESCRIPTION": "Use Google Authenticator, Authy, or any TOTP-compatible app",
+ "LOADING_QR": "Loading...",
+ "MANUAL_ENTRY": "Can't scan? Enter code manually",
+ "SECRET_KEY": "Secret Key",
+ "COPY": "Copy",
+ "ENTER_CODE": "Enter the 6-digit code from your authenticator app",
+ "ENTER_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify & Continue",
+ "CANCEL": "Zrušiť",
+ "ERROR_STARTING": "MFA not enabled. Please contact administrator.",
+ "INVALID_CODE": "Invalid verification code",
+ "SECRET_COPIED": "Secret key copied to clipboard",
+ "SUCCESS": "Two-factor authentication has been enabled successfully"
+ },
+ "BACKUP": {
+ "TITLE": "Save Your Backup Codes",
+ "DESCRIPTION": "Keep these codes safe. Each can be used once if you lose access to your authenticator",
+ "IMPORTANT": "Important:",
+ "IMPORTANT_NOTE": " Save these codes in a secure location. You won't be able to see them again.",
+ "DOWNLOAD": "Stiahnuť",
+ "COPY_ALL": "Copy All",
+ "CONFIRM": "I have saved my backup codes in a secure location and understand that I won't be able to see them again",
+ "COMPLETE_SETUP": "Complete Setup",
+ "CODES_COPIED": "Backup codes copied to clipboard"
+ },
+ "MANAGEMENT": {
+ "BACKUP_CODES": "Backup Codes",
+ "BACKUP_CODES_DESC": "Generate new codes if you've lost or used your existing ones",
+ "REGENERATE": "Regenerate Backup Codes",
+ "DISABLE_MFA": "Disable 2FA",
+ "DISABLE_MFA_DESC": "Remove two-factor authentication from your account",
+ "DISABLE_BUTTON": "Disable Two-Factor Authentication"
+ },
+ "DISABLE": {
+ "TITLE": "Disable Two-Factor Authentication",
+ "DESCRIPTION": "You'll need to enter your password and a verification code to disable two-factor authentication.",
+ "PASSWORD": "Password",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Disable 2FA",
+ "CANCEL": "Zrušiť",
+ "SUCCESS": "Two-factor authentication has been disabled",
+ "ERROR": "Failed to disable MFA. Please check your credentials."
+ },
+ "REGENERATE": {
+ "TITLE": "Regenerate Backup Codes",
+ "DESCRIPTION": "This will invalidate your existing backup codes and generate new ones. Enter your verification code to continue.",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Generate New Codes",
+ "CANCEL": "Zrušiť",
+ "NEW_CODES_TITLE": "New Backup Codes Generated",
+ "NEW_CODES_DESC": "Your old backup codes have been invalidated. Save these new codes in a secure location.",
+ "CODES_IMPORTANT": "Important:",
+ "CODES_IMPORTANT_NOTE": " Each code can only be used once. Save them before closing this window.",
+ "DOWNLOAD_CODES": "Download Codes",
+ "COPY_ALL_CODES": "Copy All Codes",
+ "CODES_SAVED": "I've Saved My Codes",
+ "SUCCESS": "New backup codes have been generated",
+ "ERROR": "Failed to regenerate backup codes"
+ }
+ },
+ "MFA_VERIFICATION": {
+ "TITLE": "Two-Factor Authentication",
+ "DESCRIPTION": "Enter your verification code to continue",
+ "AUTHENTICATOR_APP": "Authenticator App",
+ "BACKUP_CODE": "Backup Code",
+ "ENTER_OTP_CODE": "Enter 6-digit code from your authenticator app",
+ "ENTER_BACKUP_CODE": "Enter one of your backup codes",
+ "BACKUP_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify",
+ "TRY_ANOTHER_METHOD": "Try another verification method",
+ "CANCEL_LOGIN": "Cancel and return to login",
+ "HELP_TEXT": "Having trouble signing in?",
+ "LEARN_MORE": "Learn more about 2FA",
+ "HELP_MODAL": {
+ "TITLE": "Two-Factor Authentication Help",
+ "AUTHENTICATOR_TITLE": "Using an Authenticator App",
+ "AUTHENTICATOR_DESC": "Open your authenticator app (Google Authenticator, Authy, etc.) and enter the 6-digit code shown for your account.",
+ "BACKUP_TITLE": "Using a Backup Code",
+ "BACKUP_DESC": "If you don't have access to your authenticator app, you can use one of the backup codes you saved when setting up 2FA. Each code can only be used once.",
+ "CONTACT_TITLE": "Need More Help?",
+ "CONTACT_DESC_CLOUD": "If you've lost access to both your authenticator app and backup codes, please reach out to Chatwoot support for assistance.",
+ "CONTACT_DESC_SELF_HOSTED": "If you've lost access to both your authenticator app and backup codes, please contact your administrator for assistance."
+ },
+ "VERIFICATION_FAILED": "Verification failed. Please try again."
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/sk/settings.json b/app/javascript/dashboard/i18n/locale/sk/settings.json
index 9f452890d..44457e288 100644
--- a/app/javascript/dashboard/i18n/locale/sk/settings.json
+++ b/app/javascript/dashboard/i18n/locale/sk/settings.json
@@ -51,6 +51,13 @@
"LARGER": "Larger",
"EXTRA_LARGE": "Extra Large"
}
+ },
+ "LANGUAGE": {
+ "TITLE": "Preferred Language",
+ "NOTE": "Choose the language you want to use.",
+ "UPDATE_SUCCESS": "Your Language settings have been updated successfully",
+ "UPDATE_ERROR": "There is an error while updating the language settings, please try again",
+ "USE_ACCOUNT_DEFAULT": "Use account default"
}
},
"MESSAGE_SIGNATURE_SECTION": {
@@ -73,6 +80,11 @@
"NOTE": "Updating your password would reset your logins in multiple devices.",
"BTN_TEXT": "Change password"
},
+ "SECURITY_SECTION": {
+ "TITLE": "Security",
+ "NOTE": "Manage additional security features for your account.",
+ "MFA_BUTTON": "Manage Two-Factor Authentication"
+ },
"ACCESS_TOKEN": {
"TITLE": "Prístupový token",
"NOTE": "Tento token môžete použiť, ak vytvárate integráciu založenú na rozhraní API",
@@ -330,6 +342,7 @@
"REPORTS_LABEL": "Labels",
"REPORTS_INBOX": "Schránka",
"REPORTS_TEAM": "Team",
+ "AGENT_ASSIGNMENT": "Agent Assignment",
"SET_AVAILABILITY_TITLE": "Set yourself as",
"SET_YOUR_AVAILABILITY": "Nastavte svoju dostupnosť",
"SLA": "SLA",
@@ -350,7 +363,8 @@
"INFO_TEXT": "Let the system automatically mark you offline when you aren't using the app or dashboard.",
"INFO_SHORT": "Automatically mark offline when you aren't using the app."
},
- "DOCS": "Read docs"
+ "DOCS": "Read docs",
+ "SECURITY": "Security"
},
"BILLING_SETTINGS": {
"TITLE": "Billing",
@@ -382,6 +396,77 @@
},
"NO_BILLING_USER": "Your billing account is being configured. Please refresh the page and try again."
},
+ "SECURITY_SETTINGS": {
+ "TITLE": "Security",
+ "DESCRIPTION": "Manage your account security settings.",
+ "LINK_TEXT": "Learn more about SAML SSO",
+ "SAML": {
+ "TITLE": "SAML SSO",
+ "NOTE": "Configure SAML single sign-on for your account. Users will authenticate through your identity provider instead of using email/password.",
+ "ACS_URL": {
+ "LABEL": "ACS URL",
+ "TOOLTIP": "Assertion Consumer Service URL - Configure this URL in your IdP as the destination for SAML responses"
+ },
+ "SSO_URL": {
+ "LABEL": "SSO URL",
+ "HELP": "The URL where SAML authentication requests will be sent",
+ "PLACEHOLDER": "https://your-idp.com/saml/sso"
+ },
+ "CERTIFICATE": {
+ "LABEL": "Signing certificate in PEM format",
+ "HELP": "The public certificate from your identity provider used to verify SAML responses",
+ "PLACEHOLDER": "-----BEGIN CERTIFICATE-----\nMIIC..."
+ },
+ "FINGERPRINT": {
+ "LABEL": "Fingerprint",
+ "TOOLTIP": "SHA-1 fingerprint of the certificate - Use this to verify the certificate in your IdP configuration"
+ },
+ "COPY_SUCCESS": "Code copied to clipboard successfully",
+ "SP_ENTITY_ID": {
+ "LABEL": "SP Entity ID",
+ "HELP": "Unique identifier for this application as a service provider (auto-generated).",
+ "TOOLTIP": "Unique identifier for Chatwoot as the Service Provider - Configure this in your IdP settings"
+ },
+ "IDP_ENTITY_ID": {
+ "LABEL": "Identity Provider Entity ID",
+ "HELP": "Unique identifier for your identity provider (usually found in IdP configuration)",
+ "PLACEHOLDER": "https://your-idp.com/saml"
+ },
+ "UPDATE_BUTTON": "Update SAML Settings",
+ "API": {
+ "SUCCESS": "SAML settings updated successfully",
+ "ERROR": "Failed to update SAML settings",
+ "ERROR_LOADING": "Failed to load SAML settings",
+ "DISABLED": "SAML settings disabled successfully"
+ },
+ "VALIDATION": {
+ "REQUIRED_FIELDS": "SSO URL, Identity Provider Entity ID, and Certificate are required fields",
+ "SSO_URL_ERROR": "Please enter a valid SSO URL",
+ "CERTIFICATE_ERROR": "Certificate is required",
+ "IDP_ENTITY_ID_ERROR": "Identity Provider Entity ID is required"
+ },
+ "ENTERPRISE_PAYWALL": {
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade to an Enterprise plan to access SAML single sign-on and other advanced security features.",
+ "ASK_ADMIN": "Please reach out to your administrator for the upgrade."
+ },
+ "PAYWALL": {
+ "TITLE": "Upgrade to enable SAML SSO",
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade your plan to get access to SAML single sign-on and other advanced features.",
+ "UPGRADE_NOW": "Upgrade now",
+ "CANCEL_ANYTIME": "You can change or cancel your plan anytime"
+ },
+ "ATTRIBUTE_MAPPING": {
+ "TITLE": "SAML Attribute Setup",
+ "DESCRIPTION": "The following attribute mappings must be configured in your identity provider"
+ },
+ "INFO_SECTION": {
+ "TITLE": "Service Provider Information",
+ "TOOLTIP": "Copy these values and configure them in your Identity Provider to establish the SAML connection"
+ }
+ }
+ },
"CREATE_ACCOUNT": {
"NO_ACCOUNT_WARNING": "Uh oh! We could not find any Chatwoot accounts. Please create a new account to continue.",
"NEW_ACCOUNT": "New Account",
@@ -418,5 +503,255 @@
"SWITCH_TO_REPLY": "Prepnúť na odpoveď",
"TOGGLE_SNOOZE_DROPDOWN": "Prepínanie módu \"snooze\""
}
+ },
+ "ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent assignment",
+ "DESCRIPTION": "Define policies to effectively manage workload and route conversations based on the needs of inboxes and agents. Learn more here"
+ },
+ "ASSIGNMENT_POLICY": {
+ "TITLE": "Assignment policy",
+ "DESCRIPTION": "Manage how conversations get assigned in inboxes.",
+ "FEATURES": [
+ "Assign by conversations evenly or by available capacity",
+ "Add fair distribution rules to avoid overloading any agent",
+ "Add inboxes to a policy - one policy per inbox"
+ ]
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "TITLE": "Agent capacity policy",
+ "DESCRIPTION": "Manage workload for agents.",
+ "FEATURES": [
+ "Define maximum conversations per inbox",
+ "Create exceptions based on labels and time",
+ "Add agents to a policy - one policy per agent"
+ ]
+ }
+ },
+ "AGENT_ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Assignment policy",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "ORDER": "Order",
+ "PRIORITY": "Priority",
+ "ACTIVE": "Active",
+ "INACTIVE": "Inactive",
+ "POPOVER": "Added inboxes",
+ "EDIT": "Upraviť"
+ },
+ "NO_RECORDS_FOUND": "No assignment policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create assignment policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy created successfully",
+ "ERROR_MESSAGE": "Failed to create assignment policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit assignment policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_INBOX_DIALOG": {
+ "TITLE": "Add inbox",
+ "DESCRIPTION": "{inboxName} inbox is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Zrušiť"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update assignment policy"
+ },
+ "INBOX_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Inbox added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Inbox removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove inbox from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Description:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "STATUS": {
+ "LABEL": "Status:",
+ "PLACEHOLDER": "Select status",
+ "ACTIVE": "Policy is active",
+ "INACTIVE": "Policy is inactive"
+ },
+ "ASSIGNMENT_ORDER": {
+ "LABEL": "Assignment order",
+ "ROUND_ROBIN": {
+ "LABEL": "Round robin",
+ "DESCRIPTION": "Assign conversations evenly among agents."
+ },
+ "BALANCED": {
+ "LABEL": "Balanced",
+ "DESCRIPTION": "Assign conversations based on available capacity."
+ }
+ },
+ "ASSIGNMENT_PRIORITY": {
+ "LABEL": "Assignment priority",
+ "EARLIEST_CREATED": {
+ "LABEL": "Earliest created",
+ "DESCRIPTION": "The conversation that was created first gets assigned first."
+ },
+ "LONGEST_WAITING": {
+ "LABEL": "Longest waiting",
+ "DESCRIPTION": "The conversation waiting the longest gets assigned first."
+ }
+ },
+ "FAIR_DISTRIBUTION": {
+ "LABEL": "Fair distribution policy",
+ "DESCRIPTION": "Set the maximum number of conversations that can be assigned per agent within a time window to avoid overloading any one agent. This required field defaults to 100 conversations per hour.",
+ "INPUT_MAX": "Assign max",
+ "DURATION": "Conversations per agent in every"
+ },
+ "INBOXES": {
+ "LABEL": "Added inboxes",
+ "DESCRIPTION": "Add inboxes for which this policy will be applicable.",
+ "ADD_BUTTON": "Add inbox",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select inboxes to add",
+ "ADD_BUTTON": "Pridať"
+ },
+ "EMPTY_STATE": "No inboxes added to this policy, add an inbox to get started",
+ "API": {
+ "SUCCESS_MESSAGE": "Inbox successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Assignment policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete assignment policy"
+ }
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent capacity",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "POPOVER": "Added agents",
+ "EDIT": "Upraviť"
+ },
+ "NO_RECORDS_FOUND": "No agent capacity policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create agent capacity policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy created successfully",
+ "ERROR_MESSAGE": "Failed to create agent capacity policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit agent capacity policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_AGENT_DIALOG": {
+ "TITLE": "Add agent",
+ "DESCRIPTION": "{agentName} is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Zrušiť"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update agent capacity policy"
+ },
+ "AGENT_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Agent added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Agent removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove agent from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Description:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "INBOX_CAPACITY_LIMIT": {
+ "LABEL": "Inbox capacity limits",
+ "ADD_BUTTON": "Add inbox",
+ "FIELD": {
+ "SELECT_INBOX": "Select inbox",
+ "MAX_CONVERSATIONS": "Max conversations",
+ "SET_LIMIT": "Set limit"
+ },
+ "EMPTY_STATE": "No inbox limit set"
+ },
+ "EXCLUSION_RULES": {
+ "LABEL": "Exclusion rules",
+ "DESCRIPTION": "Conversations that satisfy the following conditions would not count towards agent capacity",
+ "TAGS": {
+ "LABEL": "Exclude conversations tagged with specific labels",
+ "ADD_TAG": "add tag",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select tags to add"
+ },
+ "EMPTY_STATE": "No tags added to this policy."
+ },
+ "DURATION": {
+ "LABEL": "Exclude conversations older than a specified duration",
+ "PLACEHOLDER": "Set time"
+ }
+ },
+ "USERS": {
+ "LABEL": "Assigned agents",
+ "DESCRIPTION": "Add agents for which this policy will be applicable.",
+ "ADD_BUTTON": "Add agent",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select agents to add",
+ "ADD_BUTTON": "Pridať"
+ },
+ "EMPTY_STATE": "No agents added",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Agent capacity policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete agent capacity policy"
+ }
+ },
+ "DELETE_POLICY": {
+ "TITLE": "Delete policy",
+ "DESCRIPTION": "Are you sure you want to delete this policy? This action cannot be undone.",
+ "CONFIRM_BUTTON_LABEL": "Vymazať",
+ "CANCEL_BUTTON_LABEL": "Zrušiť"
+ }
}
}
diff --git a/app/javascript/dashboard/i18n/locale/sk/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/sk/whatsappTemplates.json
index 5f53faaa8..cf28312dc 100644
--- a/app/javascript/dashboard/i18n/locale/sk/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/sk/whatsappTemplates.json
@@ -40,6 +40,7 @@
"BUTTON_LABEL": "Button {index}",
"COUPON_CODE": "Enter coupon code (max 15 chars)",
"MEDIA_URL_LABEL": "Enter {type} URL",
+ "DOCUMENT_NAME_PLACEHOLDER": "Enter document filename (e.g., Invoice_2025.pdf)",
"BUTTON_PARAMETER": "Enter button parameter"
}
}
diff --git a/app/javascript/dashboard/i18n/locale/sl/automation.json b/app/javascript/dashboard/i18n/locale/sl/automation.json
index 2f97d7257..4ce97216f 100644
--- a/app/javascript/dashboard/i18n/locale/sl/automation.json
+++ b/app/javascript/dashboard/i18n/locale/sl/automation.json
@@ -177,7 +177,8 @@
"REFERER_LINK": "Referrer Link",
"ASSIGNEE_NAME": "Prejemnik",
"TEAM_NAME": "Team",
- "PRIORITY": "Prioriteta"
+ "PRIORITY": "Prioriteta",
+ "LABELS": "Labels"
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/sl/contact.json b/app/javascript/dashboard/i18n/locale/sl/contact.json
index 2b43dfea8..42679f3b3 100644
--- a/app/javascript/dashboard/i18n/locale/sl/contact.json
+++ b/app/javascript/dashboard/i18n/locale/sl/contact.json
@@ -610,6 +610,15 @@
"SEND_MESSAGE": "Send message"
}
},
+ "TWILIO_OPTIONS": {
+ "LABEL": "Select template",
+ "SEARCH_PLACEHOLDER": "Search templates",
+ "EMPTY_STATE": "No templates found",
+ "TEMPLATE_PARSER": {
+ "BACK": "Go back",
+ "SEND_MESSAGE": "Send message"
+ }
+ },
"ACTION_BUTTONS": {
"DISCARD": "Discard",
"SEND": "Send ({keyCode})"
diff --git a/app/javascript/dashboard/i18n/locale/sl/contactFilters.json b/app/javascript/dashboard/i18n/locale/sl/contactFilters.json
index 8134e3447..4a05844ea 100644
--- a/app/javascript/dashboard/i18n/locale/sl/contactFilters.json
+++ b/app/javascript/dashboard/i18n/locale/sl/contactFilters.json
@@ -48,7 +48,8 @@
"CREATED_AT": "Created At",
"LAST_ACTIVITY": "Last Activity",
"REFERER_LINK": "Referrer link",
- "BLOCKED": "Blocked"
+ "BLOCKED": "Blocked",
+ "LABELS": "Labels"
},
"GROUPS": {
"STANDARD_FILTERS": "Standard Filters",
diff --git a/app/javascript/dashboard/i18n/locale/sl/contentTemplates.json b/app/javascript/dashboard/i18n/locale/sl/contentTemplates.json
new file mode 100644
index 000000000..d2491677e
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/sl/contentTemplates.json
@@ -0,0 +1,51 @@
+{
+ "CONTENT_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Twilio Templates",
+ "SUBTITLE": "Select the Twilio template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Išči predloge",
+ "NO_TEMPLATES_FOUND": "Ni najdenih predlog za",
+ "NO_CONTENT": "Ni vsebine",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Kategorija",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No Twilio templates available. Click refresh to sync templates from Twilio.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Jezik",
+ "TEMPLATE_BODY": "Telo predloge",
+ "CATEGORY": "Kategorija"
+ },
+ "TYPES": {
+ "MEDIA": "Media",
+ "QUICK_REPLY": "Quick Reply",
+ "TEXT": "Tekst"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Spremenljivke",
+ "LANGUAGE": "Jezik",
+ "CATEGORY": "Kategorija",
+ "VARIABLE_PLACEHOLDER": "Vnesi vrednost {variable}",
+ "GO_BACK_LABEL": "Pojdi nazaj",
+ "SEND_MESSAGE_LABEL": "Pošlji sporočilo",
+ "FORM_ERROR_MESSAGE": "Prosimo, izpolnite vse spremenljivke pred pošiljanjem",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "MEDIA_URL_LABEL": "Enter full media URL",
+ "MEDIA_URL_PLACEHOLDER": "https://example.com/image.jpg"
+ },
+ "FORM": {
+ "BACK_BUTTON": "Back",
+ "SEND_MESSAGE_BUTTON": "Pošlji sporočilo"
+ }
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/sl/conversation.json b/app/javascript/dashboard/i18n/locale/sl/conversation.json
index b62a8eb60..24bf95f58 100644
--- a/app/javascript/dashboard/i18n/locale/sl/conversation.json
+++ b/app/javascript/dashboard/i18n/locale/sl/conversation.json
@@ -35,6 +35,11 @@
"API_HOURS_WINDOW": "You can only reply to this conversation within {hours} hours",
"NOT_ASSIGNED_TO_YOU": "This conversation is not assigned to you. Would you like to assign this conversation to yourself?",
"ASSIGN_TO_ME": "Assign to me",
+ "BOT_HANDOFF_MESSAGE": "You are responding to a conversation which is currently handled by an assistant or a bot.",
+ "BOT_HANDOFF_ACTION": "Mark open and assign to you",
+ "BOT_HANDOFF_REOPEN_ACTION": "Mark conversation open",
+ "BOT_HANDOFF_SUCCESS": "Conversation has been handed over to you",
+ "BOT_HANDOFF_ERROR": "Failed to take over the conversation. Please try again.",
"TWILIO_WHATSAPP_CAN_REPLY": "You can only reply to this conversation using a template message due to",
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "24 hour message window restriction",
"OLD_INSTAGRAM_INBOX_REPLY_BANNER": "This Instagram account was migrated to the new Instagram channel inbox. All new messages will show up there. You won’t be able to send messages from this conversation anymore.",
@@ -66,6 +71,17 @@
"SHOW_LABELS": "Show labels",
"HIDE_LABELS": "Hide labels"
},
+ "VOICE_CALL": {
+ "INCOMING_CALL": "Incoming call",
+ "OUTGOING_CALL": "Outgoing call",
+ "CALL_IN_PROGRESS": "Call in progress",
+ "NO_ANSWER": "No answer",
+ "MISSED_CALL": "Missed call",
+ "CALL_ENDED": "Call ended",
+ "NOT_ANSWERED_YET": "Not answered yet",
+ "THEY_ANSWERED": "They answered",
+ "YOU_ANSWERED": "You answered"
+ },
"HEADER": {
"RESOLVE_ACTION": "Resolve",
"REOPEN_ACTION": "Reopen",
diff --git a/app/javascript/dashboard/i18n/locale/sl/helpCenter.json b/app/javascript/dashboard/i18n/locale/sl/helpCenter.json
index 5d99482b7..b0bafaf77 100644
--- a/app/javascript/dashboard/i18n/locale/sl/helpCenter.json
+++ b/app/javascript/dashboard/i18n/locale/sl/helpCenter.json
@@ -741,7 +741,8 @@
"LIVE_CHAT_WIDGET": {
"LABEL": "Live chat widget",
"PLACEHOLDER": "Select live chat widget",
- "HELP_TEXT": "Select a live chat widget that will appear on your help center"
+ "HELP_TEXT": "Select a live chat widget that will appear on your help center",
+ "NONE_OPTION": "No widget"
},
"BRAND_COLOR": {
"LABEL": "Brand color"
@@ -807,6 +808,58 @@
"ERROR_MESSAGE": "Unable to update portal"
}
}
+ },
+ "PDF_UPLOAD": {
+ "TITLE": "Upload PDF Document",
+ "DESCRIPTION": "Upload a PDF document to automatically generate FAQs using AI",
+ "DRAG_DROP_TEXT": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "ADDITIONAL_CONTEXT_LABEL": "Additional Context (Optional)",
+ "ADDITIONAL_CONTEXT_PLACEHOLDER": "Provide any additional context or instructions for FAQ generation...",
+ "UPLOADING": "Nalaganje...",
+ "UPLOAD": "Upload & Process",
+ "CANCEL": "Cancel",
+ "ERROR_INVALID_TYPE": "Please select a valid PDF file",
+ "ERROR_FILE_TOO_LARGE": "File size must be less than 512MB",
+ "ERROR_UPLOAD_FAILED": "Failed to upload PDF. Please try again."
+ },
+ "PDF_DOCUMENTS": {
+ "TITLE": "PDF Documents",
+ "DESCRIPTION": "Manage uploaded PDF documents and generate FAQs from them",
+ "UPLOAD_PDF": "Upload PDF",
+ "UPLOAD_FIRST_PDF": "Upload your first PDF",
+ "UPLOADED_BY": "Uploaded by",
+ "GENERATE_FAQS": "Generate FAQs",
+ "GENERATING": "Generating...",
+ "CONFIRM_DELETE": "Are you sure you want to delete {filename}?",
+ "EMPTY_STATE": {
+ "TITLE": "No PDF documents yet",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQs using AI"
+ },
+ "STATUS": {
+ "UPLOADED": "Ready",
+ "PROCESSING": "Processing",
+ "PROCESSED": "Completed",
+ "FAILED": "Failed"
+ }
+ },
+ "CONTENT_GENERATION": {
+ "TITLE": "Content Generation",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQ content using AI",
+ "UPLOAD_TITLE": "Upload PDF Document",
+ "DRAG_DROP": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "UPLOADING": "Processing document...",
+ "UPLOAD_SUCCESS": "Document processed successfully!",
+ "UPLOAD_ERROR": "Failed to upload document. Please try again.",
+ "INVALID_FILE_TYPE": "Please select a valid PDF file",
+ "FILE_TOO_LARGE": "File size must be less than 512MB",
+ "GENERATED_CONTENT": "Generated FAQ Content",
+ "PUBLISH_SELECTED": "Publish Selected",
+ "PUBLISHING": "Publishing...",
+ "FROM_DOCUMENT": "From document",
+ "NO_CONTENT": "No generated content available. Upload a PDF document to get started.",
+ "LOADING": "Loading generated content..."
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/sl/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/sl/inboxMgmt.json
index 40ffb6acb..235e75241 100644
--- a/app/javascript/dashboard/i18n/locale/sl/inboxMgmt.json
+++ b/app/javascript/dashboard/i18n/locale/sl/inboxMgmt.json
@@ -272,8 +272,8 @@
},
"SUBMIT_BUTTON": "Create WhatsApp Channel",
"EMBEDDED_SIGNUP": {
- "TITLE": "Quick Setup with Meta",
- "DESC": "You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
+ "TITLE": "Quick setup with Meta",
+ "DESC": "Use the WhatsApp Embedded Signup flow to quickly connect new numbers. You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
"BENEFITS": {
"TITLE": "Benefits of Embedded Signup:",
"EASY_SETUP": "No manual configuration required",
@@ -281,9 +281,8 @@
"AUTO_CONFIG": "Automatic webhook and phone number configuration"
},
"LEARN_MORE": {
- "TEXT": "To learn more about integrated signup, pricing, and limitations, visit",
- "LINK_TEXT": "this link.",
- "LINK_URL": "https://developers.facebook.com/docs/whatsapp/embedded-signup/custom-flows/onboarding-business-app-users#limitations"
+ "TEXT": "To learn more about integrated signup, pricing, and limitations, visit {link}.",
+ "LINK_TEXT": "this link"
},
"SUBMIT_BUTTON": "Connect with WhatsApp Business",
"AUTH_PROCESSING": "Authenticating with Meta",
@@ -296,7 +295,9 @@
"INVALID_BUSINESS_DATA": "Invalid business data received from Facebook. Please try again.",
"SIGNUP_ERROR": "Signup error occurred",
"AUTH_NOT_COMPLETED": "Authentication not completed. Please restart the process.",
- "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured"
+ "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured",
+ "MANUAL_FALLBACK": "If your number is already connected to the WhatsApp Business Platform (API), or if you’re a tech provider onboarding your own number, please use the {link} flow",
+ "MANUAL_LINK_TEXT": "manual setup flow"
},
"API": {
"ERROR_MESSAGE": "We were not able to save the WhatsApp channel"
@@ -424,7 +425,51 @@
},
"AUTH": {
"TITLE": "Choose a channel",
- "DESC": "Chatwoot supports live-chat widgets, Facebook Messenger, WhatsApp, Emails, etc., as channels. If you want to build a custom channel, you can create it using the API channel. To get started, choose one of the channels below."
+ "DESC": "Chatwoot supports live-chat widgets, Facebook Messenger, WhatsApp, Emails, etc., as channels. If you want to build a custom channel, you can create it using the API channel. To get started, choose one of the channels below.",
+ "TITLE_NEXT": "Complete the setup",
+ "TITLE_FINISH": "Voilà!",
+ "CHANNEL": {
+ "WEBSITE": {
+ "TITLE": "Spletna stran",
+ "DESCRIPTION": "Create a live-chat widget"
+ },
+ "FACEBOOK": {
+ "TITLE": "Facebook",
+ "DESCRIPTION": "Connect your Facebook page"
+ },
+ "WHATSAPP": {
+ "TITLE": "WhatsApp",
+ "DESCRIPTION": "Support your customers on WhatsApp"
+ },
+ "EMAIL": {
+ "TITLE": "E-pošta",
+ "DESCRIPTION": "Connect with Gmail, Outlook, or other providers"
+ },
+ "SMS": {
+ "TITLE": "SMS",
+ "DESCRIPTION": "Integrate SMS channel with Twilio or bandwidth"
+ },
+ "API": {
+ "TITLE": "API",
+ "DESCRIPTION": "Make a custom channel using our API"
+ },
+ "TELEGRAM": {
+ "TITLE": "Telegram",
+ "DESCRIPTION": "Configure Telegram channel using Bot token"
+ },
+ "LINE": {
+ "TITLE": "Line",
+ "DESCRIPTION": "Integrate your Line channel"
+ },
+ "INSTAGRAM": {
+ "TITLE": "Instagram",
+ "DESCRIPTION": "Connect your instagram account"
+ },
+ "VOICE": {
+ "TITLE": "Voice",
+ "DESCRIPTION": "Integrate with Twilio Voice"
+ }
+ }
},
"AGENTS": {
"TITLE": "Agents",
@@ -478,7 +523,10 @@
"MESSAGE": "You can now engage with your customers through your new Channel. Happy supporting",
"BUTTON_TEXT": "Take me there",
"MORE_SETTINGS": "More settings",
- "WEBSITE_SUCCESS": "You have successfully finished creating a website channel. Copy the code shown below and paste it on your website. Next time a customer use the live chat, the conversation will automatically appear on your inbox."
+ "WEBSITE_SUCCESS": "You have successfully finished creating a website channel. Copy the code shown below and paste it on your website. Next time a customer use the live chat, the conversation will automatically appear on your inbox.",
+ "WHATSAPP_QR_INSTRUCTION": "Scan the QR code above to quickly test your WhatsApp inbox",
+ "MESSENGER_QR_INSTRUCTION": "Scan the QR code above to quickly test your Facebook Messenger inbox",
+ "TELEGRAM_QR_INSTRUCTION": "Scan the QR code above to quickly test your Telegram inbox"
},
"REAUTH": "Reauthorize",
"VIEW": "View",
@@ -868,9 +916,18 @@
"SCRIPT_SETTINGS": "\n window.chatwootSettings = {options};"
},
"EMAIL_PROVIDERS": {
- "MICROSOFT": "Microsoft",
- "GOOGLE": "Google",
- "OTHER_PROVIDERS": "Other Providers"
+ "MICROSOFT": {
+ "TITLE": "Microsoft",
+ "DESCRIPTION": "Connect with Microsoft"
+ },
+ "GOOGLE": {
+ "TITLE": "Google",
+ "DESCRIPTION": "Connect with Google"
+ },
+ "OTHER_PROVIDERS": {
+ "TITLE": "Other Providers",
+ "DESCRIPTION": "Connect with Other Providers"
+ }
},
"CHANNELS": {
"MESSENGER": "Messenger",
diff --git a/app/javascript/dashboard/i18n/locale/sl/integrations.json b/app/javascript/dashboard/i18n/locale/sl/integrations.json
index e1f8321c5..e4216f051 100644
--- a/app/javascript/dashboard/i18n/locale/sl/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/sl/integrations.json
@@ -704,11 +704,28 @@
"ERROR_MESSAGE": "There was an error creating the document, please try again."
},
"FORM": {
+ "TYPE": {
+ "LABEL": "Document Type",
+ "URL": "URL",
+ "PDF": "PDF File"
+ },
"URL": {
"LABEL": "URL",
"PLACEHOLDER": "Enter the URL of the document",
"ERROR": "Please provide a valid URL for the document"
},
+ "PDF_FILE": {
+ "LABEL": "PDF File",
+ "CHOOSE_FILE": "Choose PDF file",
+ "ERROR": "Please select a PDF file",
+ "HELP_TEXT": "Maximum file size: 10MB",
+ "INVALID_TYPE": "Please select a valid PDF file",
+ "TOO_LARGE": "File size exceeds 10MB limit"
+ },
+ "NAME": {
+ "LABEL": "Document Name (Optional)",
+ "PLACEHOLDER": "Enter a name for the document"
+ },
"ASSISTANT": {
"LABEL": "Assistant",
"PLACEHOLDER": "Select the assistant",
@@ -744,6 +761,7 @@
"SELECTED": "{count} selected",
"SELECT_ALL": "Select all ({count})",
"UNSELECT_ALL": "Unselect all ({count})",
+ "SEARCH_PLACEHOLDER": "Search FAQs...",
"BULK_APPROVE_BUTTON": "Approve",
"BULK_DELETE_BUTTON": "Izbriši",
"BULK_APPROVE": {
diff --git a/app/javascript/dashboard/i18n/locale/sl/mfa.json b/app/javascript/dashboard/i18n/locale/sl/mfa.json
new file mode 100644
index 000000000..a86db16d0
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/sl/mfa.json
@@ -0,0 +1,106 @@
+{
+ "MFA_SETTINGS": {
+ "TITLE": "Two-Factor Authentication",
+ "SUBTITLE": "Secure your account with TOTP-based authentication",
+ "DESCRIPTION": "Add an extra layer of security to your account using a time-based one-time password (TOTP)",
+ "STATUS_TITLE": "Authentication Status",
+ "STATUS_DESCRIPTION": "Manage your two-factor authentication settings and backup recovery codes",
+ "ENABLED": "Enabled",
+ "DISABLED": "Disabled",
+ "STATUS_ENABLED": "Two-factor authentication is active",
+ "STATUS_ENABLED_DESC": "Your account is protected with an additional layer of security",
+ "ENABLE_BUTTON": "Enable Two-Factor Authentication",
+ "ENHANCE_SECURITY": "Enhance Your Account Security",
+ "ENHANCE_SECURITY_DESC": "Two-factor authentication adds an extra layer of security by requiring a verification code from your authenticator app in addition to your password.",
+ "SETUP": {
+ "STEP_NUMBER_1": "1",
+ "STEP_NUMBER_2": "2",
+ "STEP1_TITLE": "Scan QR Code with Your Authenticator App",
+ "STEP1_DESCRIPTION": "Use Google Authenticator, Authy, or any TOTP-compatible app",
+ "LOADING_QR": "Loading...",
+ "MANUAL_ENTRY": "Can't scan? Enter code manually",
+ "SECRET_KEY": "Secret Key",
+ "COPY": "Copy",
+ "ENTER_CODE": "Enter the 6-digit code from your authenticator app",
+ "ENTER_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify & Continue",
+ "CANCEL": "Cancel",
+ "ERROR_STARTING": "MFA not enabled. Please contact administrator.",
+ "INVALID_CODE": "Invalid verification code",
+ "SECRET_COPIED": "Secret key copied to clipboard",
+ "SUCCESS": "Two-factor authentication has been enabled successfully"
+ },
+ "BACKUP": {
+ "TITLE": "Save Your Backup Codes",
+ "DESCRIPTION": "Keep these codes safe. Each can be used once if you lose access to your authenticator",
+ "IMPORTANT": "Important:",
+ "IMPORTANT_NOTE": " Save these codes in a secure location. You won't be able to see them again.",
+ "DOWNLOAD": "Download",
+ "COPY_ALL": "Copy All",
+ "CONFIRM": "I have saved my backup codes in a secure location and understand that I won't be able to see them again",
+ "COMPLETE_SETUP": "Complete Setup",
+ "CODES_COPIED": "Backup codes copied to clipboard"
+ },
+ "MANAGEMENT": {
+ "BACKUP_CODES": "Backup Codes",
+ "BACKUP_CODES_DESC": "Generate new codes if you've lost or used your existing ones",
+ "REGENERATE": "Regenerate Backup Codes",
+ "DISABLE_MFA": "Disable 2FA",
+ "DISABLE_MFA_DESC": "Remove two-factor authentication from your account",
+ "DISABLE_BUTTON": "Disable Two-Factor Authentication"
+ },
+ "DISABLE": {
+ "TITLE": "Disable Two-Factor Authentication",
+ "DESCRIPTION": "You'll need to enter your password and a verification code to disable two-factor authentication.",
+ "PASSWORD": "Geslo",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Disable 2FA",
+ "CANCEL": "Cancel",
+ "SUCCESS": "Two-factor authentication has been disabled",
+ "ERROR": "Failed to disable MFA. Please check your credentials."
+ },
+ "REGENERATE": {
+ "TITLE": "Regenerate Backup Codes",
+ "DESCRIPTION": "This will invalidate your existing backup codes and generate new ones. Enter your verification code to continue.",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Generate New Codes",
+ "CANCEL": "Cancel",
+ "NEW_CODES_TITLE": "New Backup Codes Generated",
+ "NEW_CODES_DESC": "Your old backup codes have been invalidated. Save these new codes in a secure location.",
+ "CODES_IMPORTANT": "Important:",
+ "CODES_IMPORTANT_NOTE": " Each code can only be used once. Save them before closing this window.",
+ "DOWNLOAD_CODES": "Download Codes",
+ "COPY_ALL_CODES": "Copy All Codes",
+ "CODES_SAVED": "I've Saved My Codes",
+ "SUCCESS": "New backup codes have been generated",
+ "ERROR": "Failed to regenerate backup codes"
+ }
+ },
+ "MFA_VERIFICATION": {
+ "TITLE": "Two-Factor Authentication",
+ "DESCRIPTION": "Enter your verification code to continue",
+ "AUTHENTICATOR_APP": "Authenticator App",
+ "BACKUP_CODE": "Backup Code",
+ "ENTER_OTP_CODE": "Enter 6-digit code from your authenticator app",
+ "ENTER_BACKUP_CODE": "Enter one of your backup codes",
+ "BACKUP_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify",
+ "TRY_ANOTHER_METHOD": "Try another verification method",
+ "CANCEL_LOGIN": "Cancel and return to login",
+ "HELP_TEXT": "Having trouble signing in?",
+ "LEARN_MORE": "Learn more about 2FA",
+ "HELP_MODAL": {
+ "TITLE": "Two-Factor Authentication Help",
+ "AUTHENTICATOR_TITLE": "Using an Authenticator App",
+ "AUTHENTICATOR_DESC": "Open your authenticator app (Google Authenticator, Authy, etc.) and enter the 6-digit code shown for your account.",
+ "BACKUP_TITLE": "Using a Backup Code",
+ "BACKUP_DESC": "If you don't have access to your authenticator app, you can use one of the backup codes you saved when setting up 2FA. Each code can only be used once.",
+ "CONTACT_TITLE": "Need More Help?",
+ "CONTACT_DESC_CLOUD": "If you've lost access to both your authenticator app and backup codes, please reach out to Chatwoot support for assistance.",
+ "CONTACT_DESC_SELF_HOSTED": "If you've lost access to both your authenticator app and backup codes, please contact your administrator for assistance."
+ },
+ "VERIFICATION_FAILED": "Verification failed. Please try again."
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/sl/settings.json b/app/javascript/dashboard/i18n/locale/sl/settings.json
index 4948a016c..f137e56c9 100644
--- a/app/javascript/dashboard/i18n/locale/sl/settings.json
+++ b/app/javascript/dashboard/i18n/locale/sl/settings.json
@@ -51,6 +51,13 @@
"LARGER": "Larger",
"EXTRA_LARGE": "Extra Large"
}
+ },
+ "LANGUAGE": {
+ "TITLE": "Preferred Language",
+ "NOTE": "Choose the language you want to use.",
+ "UPDATE_SUCCESS": "Your Language settings have been updated successfully",
+ "UPDATE_ERROR": "There is an error while updating the language settings, please try again",
+ "USE_ACCOUNT_DEFAULT": "Use account default"
}
},
"MESSAGE_SIGNATURE_SECTION": {
@@ -73,6 +80,11 @@
"NOTE": "Updating your password would reset your logins in multiple devices.",
"BTN_TEXT": "Change password"
},
+ "SECURITY_SECTION": {
+ "TITLE": "Security",
+ "NOTE": "Manage additional security features for your account.",
+ "MFA_BUTTON": "Manage Two-Factor Authentication"
+ },
"ACCESS_TOKEN": {
"TITLE": "Access Token",
"NOTE": "This token can be used if you are building an API based integration",
@@ -330,6 +342,7 @@
"REPORTS_LABEL": "Labels",
"REPORTS_INBOX": "Inbox",
"REPORTS_TEAM": "Team",
+ "AGENT_ASSIGNMENT": "Agent Assignment",
"SET_AVAILABILITY_TITLE": "Set yourself as",
"SET_YOUR_AVAILABILITY": "Set your availability",
"SLA": "SLA",
@@ -350,7 +363,8 @@
"INFO_TEXT": "Let the system automatically mark you offline when you aren't using the app or dashboard.",
"INFO_SHORT": "Automatically mark offline when you aren't using the app."
},
- "DOCS": "Read docs"
+ "DOCS": "Read docs",
+ "SECURITY": "Security"
},
"BILLING_SETTINGS": {
"TITLE": "Billing",
@@ -382,6 +396,77 @@
},
"NO_BILLING_USER": "Your billing account is being configured. Please refresh the page and try again."
},
+ "SECURITY_SETTINGS": {
+ "TITLE": "Security",
+ "DESCRIPTION": "Manage your account security settings.",
+ "LINK_TEXT": "Learn more about SAML SSO",
+ "SAML": {
+ "TITLE": "SAML SSO",
+ "NOTE": "Configure SAML single sign-on for your account. Users will authenticate through your identity provider instead of using email/password.",
+ "ACS_URL": {
+ "LABEL": "ACS URL",
+ "TOOLTIP": "Assertion Consumer Service URL - Configure this URL in your IdP as the destination for SAML responses"
+ },
+ "SSO_URL": {
+ "LABEL": "SSO URL",
+ "HELP": "The URL where SAML authentication requests will be sent",
+ "PLACEHOLDER": "https://your-idp.com/saml/sso"
+ },
+ "CERTIFICATE": {
+ "LABEL": "Signing certificate in PEM format",
+ "HELP": "The public certificate from your identity provider used to verify SAML responses",
+ "PLACEHOLDER": "-----BEGIN CERTIFICATE-----\nMIIC..."
+ },
+ "FINGERPRINT": {
+ "LABEL": "Fingerprint",
+ "TOOLTIP": "SHA-1 fingerprint of the certificate - Use this to verify the certificate in your IdP configuration"
+ },
+ "COPY_SUCCESS": "Copied to clipboard",
+ "SP_ENTITY_ID": {
+ "LABEL": "SP Entity ID",
+ "HELP": "Unique identifier for this application as a service provider (auto-generated).",
+ "TOOLTIP": "Unique identifier for Chatwoot as the Service Provider - Configure this in your IdP settings"
+ },
+ "IDP_ENTITY_ID": {
+ "LABEL": "Identity Provider Entity ID",
+ "HELP": "Unique identifier for your identity provider (usually found in IdP configuration)",
+ "PLACEHOLDER": "https://your-idp.com/saml"
+ },
+ "UPDATE_BUTTON": "Update SAML Settings",
+ "API": {
+ "SUCCESS": "SAML settings updated successfully",
+ "ERROR": "Failed to update SAML settings",
+ "ERROR_LOADING": "Failed to load SAML settings",
+ "DISABLED": "SAML settings disabled successfully"
+ },
+ "VALIDATION": {
+ "REQUIRED_FIELDS": "SSO URL, Identity Provider Entity ID, and Certificate are required fields",
+ "SSO_URL_ERROR": "Please enter a valid SSO URL",
+ "CERTIFICATE_ERROR": "Certificate is required",
+ "IDP_ENTITY_ID_ERROR": "Identity Provider Entity ID is required"
+ },
+ "ENTERPRISE_PAYWALL": {
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade to an Enterprise plan to access SAML single sign-on and other advanced security features.",
+ "ASK_ADMIN": "Please reach out to your administrator for the upgrade."
+ },
+ "PAYWALL": {
+ "TITLE": "Upgrade to enable SAML SSO",
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade your plan to get access to SAML single sign-on and other advanced features.",
+ "UPGRADE_NOW": "Upgrade now",
+ "CANCEL_ANYTIME": "You can change or cancel your plan anytime"
+ },
+ "ATTRIBUTE_MAPPING": {
+ "TITLE": "SAML Attribute Setup",
+ "DESCRIPTION": "The following attribute mappings must be configured in your identity provider"
+ },
+ "INFO_SECTION": {
+ "TITLE": "Service Provider Information",
+ "TOOLTIP": "Copy these values and configure them in your Identity Provider to establish the SAML connection"
+ }
+ }
+ },
"CREATE_ACCOUNT": {
"NO_ACCOUNT_WARNING": "Uh oh! We could not find any Chatwoot accounts. Please create a new account to continue.",
"NEW_ACCOUNT": "New Account",
@@ -418,5 +503,255 @@
"SWITCH_TO_REPLY": "Switch to Reply",
"TOGGLE_SNOOZE_DROPDOWN": "Toggle snooze dropdown"
}
+ },
+ "ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent assignment",
+ "DESCRIPTION": "Define policies to effectively manage workload and route conversations based on the needs of inboxes and agents. Learn more here"
+ },
+ "ASSIGNMENT_POLICY": {
+ "TITLE": "Assignment policy",
+ "DESCRIPTION": "Manage how conversations get assigned in inboxes.",
+ "FEATURES": [
+ "Assign by conversations evenly or by available capacity",
+ "Add fair distribution rules to avoid overloading any agent",
+ "Add inboxes to a policy - one policy per inbox"
+ ]
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "TITLE": "Agent capacity policy",
+ "DESCRIPTION": "Manage workload for agents.",
+ "FEATURES": [
+ "Define maximum conversations per inbox",
+ "Create exceptions based on labels and time",
+ "Add agents to a policy - one policy per agent"
+ ]
+ }
+ },
+ "AGENT_ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Assignment policy",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "ORDER": "Order",
+ "PRIORITY": "Prioriteta",
+ "ACTIVE": "Active",
+ "INACTIVE": "Inactive",
+ "POPOVER": "Added inboxes",
+ "EDIT": "Edit"
+ },
+ "NO_RECORDS_FOUND": "No assignment policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create assignment policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy created successfully",
+ "ERROR_MESSAGE": "Failed to create assignment policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit assignment policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_INBOX_DIALOG": {
+ "TITLE": "Add inbox",
+ "DESCRIPTION": "{inboxName} inbox is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Cancel"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update assignment policy"
+ },
+ "INBOX_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Inbox added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Inbox removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove inbox from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Description:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "STATUS": {
+ "LABEL": "Status:",
+ "PLACEHOLDER": "Select status",
+ "ACTIVE": "Policy is active",
+ "INACTIVE": "Policy is inactive"
+ },
+ "ASSIGNMENT_ORDER": {
+ "LABEL": "Assignment order",
+ "ROUND_ROBIN": {
+ "LABEL": "Round robin",
+ "DESCRIPTION": "Assign conversations evenly among agents."
+ },
+ "BALANCED": {
+ "LABEL": "Balanced",
+ "DESCRIPTION": "Assign conversations based on available capacity."
+ }
+ },
+ "ASSIGNMENT_PRIORITY": {
+ "LABEL": "Assignment priority",
+ "EARLIEST_CREATED": {
+ "LABEL": "Earliest created",
+ "DESCRIPTION": "The conversation that was created first gets assigned first."
+ },
+ "LONGEST_WAITING": {
+ "LABEL": "Longest waiting",
+ "DESCRIPTION": "The conversation waiting the longest gets assigned first."
+ }
+ },
+ "FAIR_DISTRIBUTION": {
+ "LABEL": "Fair distribution policy",
+ "DESCRIPTION": "Set the maximum number of conversations that can be assigned per agent within a time window to avoid overloading any one agent. This required field defaults to 100 conversations per hour.",
+ "INPUT_MAX": "Assign max",
+ "DURATION": "Conversations per agent in every"
+ },
+ "INBOXES": {
+ "LABEL": "Added inboxes",
+ "DESCRIPTION": "Add inboxes for which this policy will be applicable.",
+ "ADD_BUTTON": "Add inbox",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select inboxes to add",
+ "ADD_BUTTON": "Add"
+ },
+ "EMPTY_STATE": "No inboxes added to this policy, add an inbox to get started",
+ "API": {
+ "SUCCESS_MESSAGE": "Inbox successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Assignment policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete assignment policy"
+ }
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent capacity",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "POPOVER": "Added agents",
+ "EDIT": "Edit"
+ },
+ "NO_RECORDS_FOUND": "No agent capacity policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create agent capacity policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy created successfully",
+ "ERROR_MESSAGE": "Failed to create agent capacity policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit agent capacity policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_AGENT_DIALOG": {
+ "TITLE": "Add agent",
+ "DESCRIPTION": "{agentName} is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Cancel"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update agent capacity policy"
+ },
+ "AGENT_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Agent added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Agent removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove agent from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Description:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "INBOX_CAPACITY_LIMIT": {
+ "LABEL": "Inbox capacity limits",
+ "ADD_BUTTON": "Add inbox",
+ "FIELD": {
+ "SELECT_INBOX": "Select inbox",
+ "MAX_CONVERSATIONS": "Max conversations",
+ "SET_LIMIT": "Set limit"
+ },
+ "EMPTY_STATE": "No inbox limit set"
+ },
+ "EXCLUSION_RULES": {
+ "LABEL": "Exclusion rules",
+ "DESCRIPTION": "Conversations that satisfy the following conditions would not count towards agent capacity",
+ "TAGS": {
+ "LABEL": "Exclude conversations tagged with specific labels",
+ "ADD_TAG": "add tag",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select tags to add"
+ },
+ "EMPTY_STATE": "No tags added to this policy."
+ },
+ "DURATION": {
+ "LABEL": "Exclude conversations older than a specified duration",
+ "PLACEHOLDER": "Set time"
+ }
+ },
+ "USERS": {
+ "LABEL": "Assigned agents",
+ "DESCRIPTION": "Add agents for which this policy will be applicable.",
+ "ADD_BUTTON": "Add agent",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select agents to add",
+ "ADD_BUTTON": "Add"
+ },
+ "EMPTY_STATE": "No agents added",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Agent capacity policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete agent capacity policy"
+ }
+ },
+ "DELETE_POLICY": {
+ "TITLE": "Delete policy",
+ "DESCRIPTION": "Are you sure you want to delete this policy? This action cannot be undone.",
+ "CONFIRM_BUTTON_LABEL": "Delete",
+ "CANCEL_BUTTON_LABEL": "Cancel"
+ }
}
}
diff --git a/app/javascript/dashboard/i18n/locale/sl/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/sl/whatsappTemplates.json
index aa6011859..240c5a02e 100644
--- a/app/javascript/dashboard/i18n/locale/sl/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/sl/whatsappTemplates.json
@@ -40,6 +40,7 @@
"BUTTON_LABEL": "Button {index}",
"COUPON_CODE": "Enter coupon code (max 15 chars)",
"MEDIA_URL_LABEL": "Enter {type} URL",
+ "DOCUMENT_NAME_PLACEHOLDER": "Enter document filename (e.g., Invoice_2025.pdf)",
"BUTTON_PARAMETER": "Enter button parameter"
}
}
diff --git a/app/javascript/dashboard/i18n/locale/sq/automation.json b/app/javascript/dashboard/i18n/locale/sq/automation.json
index 020df33f3..cbdf6f00c 100644
--- a/app/javascript/dashboard/i18n/locale/sq/automation.json
+++ b/app/javascript/dashboard/i18n/locale/sq/automation.json
@@ -177,7 +177,8 @@
"REFERER_LINK": "Lidhja e referuesit",
"ASSIGNEE_NAME": "Assignee",
"TEAM_NAME": "Team",
- "PRIORITY": "Priority"
+ "PRIORITY": "Priority",
+ "LABELS": "Labels"
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/sq/contact.json b/app/javascript/dashboard/i18n/locale/sq/contact.json
index d226b001e..50de83beb 100644
--- a/app/javascript/dashboard/i18n/locale/sq/contact.json
+++ b/app/javascript/dashboard/i18n/locale/sq/contact.json
@@ -610,6 +610,15 @@
"SEND_MESSAGE": "Send message"
}
},
+ "TWILIO_OPTIONS": {
+ "LABEL": "Select template",
+ "SEARCH_PLACEHOLDER": "Search templates",
+ "EMPTY_STATE": "No templates found",
+ "TEMPLATE_PARSER": {
+ "BACK": "Go back",
+ "SEND_MESSAGE": "Send message"
+ }
+ },
"ACTION_BUTTONS": {
"DISCARD": "Discard",
"SEND": "Send ({keyCode})"
diff --git a/app/javascript/dashboard/i18n/locale/sq/contactFilters.json b/app/javascript/dashboard/i18n/locale/sq/contactFilters.json
index bb3221c6e..4c62f0789 100644
--- a/app/javascript/dashboard/i18n/locale/sq/contactFilters.json
+++ b/app/javascript/dashboard/i18n/locale/sq/contactFilters.json
@@ -48,7 +48,8 @@
"CREATED_AT": "Created At",
"LAST_ACTIVITY": "Last Activity",
"REFERER_LINK": "Referrer link",
- "BLOCKED": "Blocked"
+ "BLOCKED": "Blocked",
+ "LABELS": "Labels"
},
"GROUPS": {
"STANDARD_FILTERS": "Standard Filters",
diff --git a/app/javascript/dashboard/i18n/locale/sq/contentTemplates.json b/app/javascript/dashboard/i18n/locale/sq/contentTemplates.json
new file mode 100644
index 000000000..5f8b4851c
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/sq/contentTemplates.json
@@ -0,0 +1,51 @@
+{
+ "CONTENT_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Twilio Templates",
+ "SUBTITLE": "Select the Twilio template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Konfiguro shabllonin: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Search Templates",
+ "NO_TEMPLATES_FOUND": "No templates found for",
+ "NO_CONTENT": "No content",
+ "HEADER": "Krye",
+ "BODY": "Trup",
+ "FOOTER": "Fund",
+ "BUTTONS": "Butona",
+ "CATEGORY": "Category",
+ "MEDIA_CONTENT": "Përmbajtje mediatike",
+ "MEDIA_CONTENT_FALLBACK": "përmbajtje mediatike",
+ "NO_TEMPLATES_AVAILABLE": "No Twilio templates available. Click refresh to sync templates from Twilio.",
+ "REFRESH_BUTTON": "Rifresko shabllonet",
+ "REFRESH_SUCCESS": "Rifreskimi i shablloneve u nis. Mund të duhen disa minuta për t'u përditësuar.",
+ "REFRESH_ERROR": "Dështoi rifreskimi i shablloneve. Ju lutemi, provoni përsëri.",
+ "LABELS": {
+ "LANGUAGE": "Language",
+ "TEMPLATE_BODY": "Template Body",
+ "CATEGORY": "Category"
+ },
+ "TYPES": {
+ "MEDIA": "Media",
+ "QUICK_REPLY": "Quick Reply",
+ "TEXT": "Text"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variables",
+ "LANGUAGE": "Language",
+ "CATEGORY": "Category",
+ "VARIABLE_PLACEHOLDER": "Enter {variable} value",
+ "GO_BACK_LABEL": "Go Back",
+ "SEND_MESSAGE_LABEL": "Send Message",
+ "FORM_ERROR_MESSAGE": "Please fill all variables before sending",
+ "MEDIA_HEADER_LABEL": "Krye {type}",
+ "MEDIA_URL_LABEL": "Enter full media URL",
+ "MEDIA_URL_PLACEHOLDER": "https://example.com/image.jpg"
+ },
+ "FORM": {
+ "BACK_BUTTON": "Back",
+ "SEND_MESSAGE_BUTTON": "Send Message"
+ }
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/sq/conversation.json b/app/javascript/dashboard/i18n/locale/sq/conversation.json
index 98da737b8..4089631cf 100644
--- a/app/javascript/dashboard/i18n/locale/sq/conversation.json
+++ b/app/javascript/dashboard/i18n/locale/sq/conversation.json
@@ -35,6 +35,11 @@
"API_HOURS_WINDOW": "Mund t'i përgjigjeni kësaj bisede vetëm brenda {hours} orëve",
"NOT_ASSIGNED_TO_YOU": "This conversation is not assigned to you. Would you like to assign this conversation to yourself?",
"ASSIGN_TO_ME": "Assign to me",
+ "BOT_HANDOFF_MESSAGE": "You are responding to a conversation which is currently handled by an assistant or a bot.",
+ "BOT_HANDOFF_ACTION": "Mark open and assign to you",
+ "BOT_HANDOFF_REOPEN_ACTION": "Mark conversation open",
+ "BOT_HANDOFF_SUCCESS": "Conversation has been handed over to you",
+ "BOT_HANDOFF_ERROR": "Failed to take over the conversation. Please try again.",
"TWILIO_WHATSAPP_CAN_REPLY": "You can only reply to this conversation using a template message due to",
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "24 hour message window restriction",
"OLD_INSTAGRAM_INBOX_REPLY_BANNER": "Kjo llogari Instagram u migrua te kutia hyrëse e re e kanalit të Instagram-it. Të gjitha mesazhet e reja do të shfaqen atje. Nuk do të mund të dërgoni më mesazhe nga kjo bisedë.",
@@ -66,6 +71,17 @@
"SHOW_LABELS": "Show labels",
"HIDE_LABELS": "Hide labels"
},
+ "VOICE_CALL": {
+ "INCOMING_CALL": "Incoming call",
+ "OUTGOING_CALL": "Outgoing call",
+ "CALL_IN_PROGRESS": "Call in progress",
+ "NO_ANSWER": "No answer",
+ "MISSED_CALL": "Missed call",
+ "CALL_ENDED": "Call ended",
+ "NOT_ANSWERED_YET": "Not answered yet",
+ "THEY_ANSWERED": "They answered",
+ "YOU_ANSWERED": "You answered"
+ },
"HEADER": {
"RESOLVE_ACTION": "Resolve",
"REOPEN_ACTION": "Reopen",
diff --git a/app/javascript/dashboard/i18n/locale/sq/helpCenter.json b/app/javascript/dashboard/i18n/locale/sq/helpCenter.json
index 591591e0b..e1991654c 100644
--- a/app/javascript/dashboard/i18n/locale/sq/helpCenter.json
+++ b/app/javascript/dashboard/i18n/locale/sq/helpCenter.json
@@ -741,7 +741,8 @@
"LIVE_CHAT_WIDGET": {
"LABEL": "Live chat widget",
"PLACEHOLDER": "Select live chat widget",
- "HELP_TEXT": "Select a live chat widget that will appear on your help center"
+ "HELP_TEXT": "Select a live chat widget that will appear on your help center",
+ "NONE_OPTION": "No widget"
},
"BRAND_COLOR": {
"LABEL": "Brand color"
@@ -807,6 +808,58 @@
"ERROR_MESSAGE": "Unable to update portal"
}
}
+ },
+ "PDF_UPLOAD": {
+ "TITLE": "Upload PDF Document",
+ "DESCRIPTION": "Upload a PDF document to automatically generate FAQs using AI",
+ "DRAG_DROP_TEXT": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "ADDITIONAL_CONTEXT_LABEL": "Additional Context (Optional)",
+ "ADDITIONAL_CONTEXT_PLACEHOLDER": "Provide any additional context or instructions for FAQ generation...",
+ "UPLOADING": "Po ngarkohet...",
+ "UPLOAD": "Upload & Process",
+ "CANCEL": "Cancel",
+ "ERROR_INVALID_TYPE": "Please select a valid PDF file",
+ "ERROR_FILE_TOO_LARGE": "File size must be less than 512MB",
+ "ERROR_UPLOAD_FAILED": "Failed to upload PDF. Please try again."
+ },
+ "PDF_DOCUMENTS": {
+ "TITLE": "PDF Documents",
+ "DESCRIPTION": "Manage uploaded PDF documents and generate FAQs from them",
+ "UPLOAD_PDF": "Upload PDF",
+ "UPLOAD_FIRST_PDF": "Upload your first PDF",
+ "UPLOADED_BY": "Uploaded by",
+ "GENERATE_FAQS": "Generate FAQs",
+ "GENERATING": "Generating...",
+ "CONFIRM_DELETE": "Are you sure you want to delete {filename}?",
+ "EMPTY_STATE": {
+ "TITLE": "No PDF documents yet",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQs using AI"
+ },
+ "STATUS": {
+ "UPLOADED": "Ready",
+ "PROCESSING": "Processing",
+ "PROCESSED": "Completed",
+ "FAILED": "Failed"
+ }
+ },
+ "CONTENT_GENERATION": {
+ "TITLE": "Content Generation",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQ content using AI",
+ "UPLOAD_TITLE": "Upload PDF Document",
+ "DRAG_DROP": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "UPLOADING": "Processing document...",
+ "UPLOAD_SUCCESS": "Document processed successfully!",
+ "UPLOAD_ERROR": "Failed to upload document. Please try again.",
+ "INVALID_FILE_TYPE": "Please select a valid PDF file",
+ "FILE_TOO_LARGE": "File size must be less than 512MB",
+ "GENERATED_CONTENT": "Generated FAQ Content",
+ "PUBLISH_SELECTED": "Publish Selected",
+ "PUBLISHING": "Publishing...",
+ "FROM_DOCUMENT": "From document",
+ "NO_CONTENT": "No generated content available. Upload a PDF document to get started.",
+ "LOADING": "Loading generated content..."
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/sq/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/sq/inboxMgmt.json
index 255f261b8..13f77cb06 100644
--- a/app/javascript/dashboard/i18n/locale/sq/inboxMgmt.json
+++ b/app/javascript/dashboard/i18n/locale/sq/inboxMgmt.json
@@ -272,8 +272,8 @@
},
"SUBMIT_BUTTON": "Create WhatsApp Channel",
"EMBEDDED_SIGNUP": {
- "TITLE": "Quick Setup with Meta",
- "DESC": "You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
+ "TITLE": "Quick setup with Meta",
+ "DESC": "Use the WhatsApp Embedded Signup flow to quickly connect new numbers. You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
"BENEFITS": {
"TITLE": "Benefits of Embedded Signup:",
"EASY_SETUP": "No manual configuration required",
@@ -281,9 +281,8 @@
"AUTO_CONFIG": "Automatic webhook and phone number configuration"
},
"LEARN_MORE": {
- "TEXT": "To learn more about integrated signup, pricing, and limitations, visit",
- "LINK_TEXT": "this link.",
- "LINK_URL": "https://developers.facebook.com/docs/whatsapp/embedded-signup/custom-flows/onboarding-business-app-users#limitations"
+ "TEXT": "To learn more about integrated signup, pricing, and limitations, visit {link}.",
+ "LINK_TEXT": "this link"
},
"SUBMIT_BUTTON": "Connect with WhatsApp Business",
"AUTH_PROCESSING": "Authenticating with Meta",
@@ -296,7 +295,9 @@
"INVALID_BUSINESS_DATA": "Invalid business data received from Facebook. Please try again.",
"SIGNUP_ERROR": "Signup error occurred",
"AUTH_NOT_COMPLETED": "Authentication not completed. Please restart the process.",
- "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured"
+ "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured",
+ "MANUAL_FALLBACK": "If your number is already connected to the WhatsApp Business Platform (API), or if you’re a tech provider onboarding your own number, please use the {link} flow",
+ "MANUAL_LINK_TEXT": "manual setup flow"
},
"API": {
"ERROR_MESSAGE": "We were not able to save the WhatsApp channel"
@@ -424,7 +425,51 @@
},
"AUTH": {
"TITLE": "Choose a channel",
- "DESC": "Chatwoot supports live-chat widgets, Facebook Messenger, WhatsApp, Emails, etc., as channels. If you want to build a custom channel, you can create it using the API channel. To get started, choose one of the channels below."
+ "DESC": "Chatwoot supports live-chat widgets, Facebook Messenger, WhatsApp, Emails, etc., as channels. If you want to build a custom channel, you can create it using the API channel. To get started, choose one of the channels below.",
+ "TITLE_NEXT": "Complete the setup",
+ "TITLE_FINISH": "Voilà!",
+ "CHANNEL": {
+ "WEBSITE": {
+ "TITLE": "Website",
+ "DESCRIPTION": "Create a live-chat widget"
+ },
+ "FACEBOOK": {
+ "TITLE": "Facebook",
+ "DESCRIPTION": "Connect your Facebook page"
+ },
+ "WHATSAPP": {
+ "TITLE": "WhatsApp",
+ "DESCRIPTION": "Support your customers on WhatsApp"
+ },
+ "EMAIL": {
+ "TITLE": "Email",
+ "DESCRIPTION": "Connect with Gmail, Outlook, or other providers"
+ },
+ "SMS": {
+ "TITLE": "SMS",
+ "DESCRIPTION": "Integrate SMS channel with Twilio or bandwidth"
+ },
+ "API": {
+ "TITLE": "API",
+ "DESCRIPTION": "Make a custom channel using our API"
+ },
+ "TELEGRAM": {
+ "TITLE": "Telegram",
+ "DESCRIPTION": "Configure Telegram channel using Bot token"
+ },
+ "LINE": {
+ "TITLE": "Line",
+ "DESCRIPTION": "Integrate your Line channel"
+ },
+ "INSTAGRAM": {
+ "TITLE": "Instagram",
+ "DESCRIPTION": "Connect your instagram account"
+ },
+ "VOICE": {
+ "TITLE": "Voice",
+ "DESCRIPTION": "Integrate with Twilio Voice"
+ }
+ }
},
"AGENTS": {
"TITLE": "Agents",
@@ -478,7 +523,10 @@
"MESSAGE": "You can now engage with your customers through your new Channel. Happy supporting",
"BUTTON_TEXT": "Take me there",
"MORE_SETTINGS": "More settings",
- "WEBSITE_SUCCESS": "You have successfully finished creating a website channel. Copy the code shown below and paste it on your website. Next time a customer use the live chat, the conversation will automatically appear on your inbox."
+ "WEBSITE_SUCCESS": "You have successfully finished creating a website channel. Copy the code shown below and paste it on your website. Next time a customer use the live chat, the conversation will automatically appear on your inbox.",
+ "WHATSAPP_QR_INSTRUCTION": "Scan the QR code above to quickly test your WhatsApp inbox",
+ "MESSENGER_QR_INSTRUCTION": "Scan the QR code above to quickly test your Facebook Messenger inbox",
+ "TELEGRAM_QR_INSTRUCTION": "Scan the QR code above to quickly test your Telegram inbox"
},
"REAUTH": "Reauthorize",
"VIEW": "View",
@@ -868,9 +916,18 @@
"SCRIPT_SETTINGS": "\n window.chatwootSettings = {options};"
},
"EMAIL_PROVIDERS": {
- "MICROSOFT": "Microsoft",
- "GOOGLE": "Google",
- "OTHER_PROVIDERS": "Other Providers"
+ "MICROSOFT": {
+ "TITLE": "Microsoft",
+ "DESCRIPTION": "Connect with Microsoft"
+ },
+ "GOOGLE": {
+ "TITLE": "Google",
+ "DESCRIPTION": "Connect with Google"
+ },
+ "OTHER_PROVIDERS": {
+ "TITLE": "Other Providers",
+ "DESCRIPTION": "Connect with Other Providers"
+ }
},
"CHANNELS": {
"MESSENGER": "Messenger",
diff --git a/app/javascript/dashboard/i18n/locale/sq/integrations.json b/app/javascript/dashboard/i18n/locale/sq/integrations.json
index 7f988c09f..a4c2bb121 100644
--- a/app/javascript/dashboard/i18n/locale/sq/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/sq/integrations.json
@@ -704,11 +704,28 @@
"ERROR_MESSAGE": "There was an error creating the document, please try again."
},
"FORM": {
+ "TYPE": {
+ "LABEL": "Document Type",
+ "URL": "URL",
+ "PDF": "PDF File"
+ },
"URL": {
"LABEL": "URL",
"PLACEHOLDER": "Enter the URL of the document",
"ERROR": "Please provide a valid URL for the document"
},
+ "PDF_FILE": {
+ "LABEL": "PDF File",
+ "CHOOSE_FILE": "Choose PDF file",
+ "ERROR": "Please select a PDF file",
+ "HELP_TEXT": "Maximum file size: 10MB",
+ "INVALID_TYPE": "Please select a valid PDF file",
+ "TOO_LARGE": "File size exceeds 10MB limit"
+ },
+ "NAME": {
+ "LABEL": "Document Name (Optional)",
+ "PLACEHOLDER": "Enter a name for the document"
+ },
"ASSISTANT": {
"LABEL": "Assistant",
"PLACEHOLDER": "Select the assistant",
@@ -744,6 +761,7 @@
"SELECTED": "{count} selected",
"SELECT_ALL": "Select all ({count})",
"UNSELECT_ALL": "Unselect all ({count})",
+ "SEARCH_PLACEHOLDER": "Search FAQs...",
"BULK_APPROVE_BUTTON": "Approve",
"BULK_DELETE_BUTTON": "Delete",
"BULK_APPROVE": {
diff --git a/app/javascript/dashboard/i18n/locale/sq/mfa.json b/app/javascript/dashboard/i18n/locale/sq/mfa.json
new file mode 100644
index 000000000..f7556fdcf
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/sq/mfa.json
@@ -0,0 +1,106 @@
+{
+ "MFA_SETTINGS": {
+ "TITLE": "Two-Factor Authentication",
+ "SUBTITLE": "Secure your account with TOTP-based authentication",
+ "DESCRIPTION": "Add an extra layer of security to your account using a time-based one-time password (TOTP)",
+ "STATUS_TITLE": "Authentication Status",
+ "STATUS_DESCRIPTION": "Manage your two-factor authentication settings and backup recovery codes",
+ "ENABLED": "Enabled",
+ "DISABLED": "Disabled",
+ "STATUS_ENABLED": "Two-factor authentication is active",
+ "STATUS_ENABLED_DESC": "Your account is protected with an additional layer of security",
+ "ENABLE_BUTTON": "Enable Two-Factor Authentication",
+ "ENHANCE_SECURITY": "Enhance Your Account Security",
+ "ENHANCE_SECURITY_DESC": "Two-factor authentication adds an extra layer of security by requiring a verification code from your authenticator app in addition to your password.",
+ "SETUP": {
+ "STEP_NUMBER_1": "1",
+ "STEP_NUMBER_2": "2",
+ "STEP1_TITLE": "Scan QR Code with Your Authenticator App",
+ "STEP1_DESCRIPTION": "Use Google Authenticator, Authy, or any TOTP-compatible app",
+ "LOADING_QR": "Loading...",
+ "MANUAL_ENTRY": "Can't scan? Enter code manually",
+ "SECRET_KEY": "Secret Key",
+ "COPY": "Copy",
+ "ENTER_CODE": "Enter the 6-digit code from your authenticator app",
+ "ENTER_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify & Continue",
+ "CANCEL": "Cancel",
+ "ERROR_STARTING": "MFA not enabled. Please contact administrator.",
+ "INVALID_CODE": "Invalid verification code",
+ "SECRET_COPIED": "Secret key copied to clipboard",
+ "SUCCESS": "Two-factor authentication has been enabled successfully"
+ },
+ "BACKUP": {
+ "TITLE": "Save Your Backup Codes",
+ "DESCRIPTION": "Keep these codes safe. Each can be used once if you lose access to your authenticator",
+ "IMPORTANT": "Important:",
+ "IMPORTANT_NOTE": " Save these codes in a secure location. You won't be able to see them again.",
+ "DOWNLOAD": "Download",
+ "COPY_ALL": "Copy All",
+ "CONFIRM": "I have saved my backup codes in a secure location and understand that I won't be able to see them again",
+ "COMPLETE_SETUP": "Complete Setup",
+ "CODES_COPIED": "Backup codes copied to clipboard"
+ },
+ "MANAGEMENT": {
+ "BACKUP_CODES": "Backup Codes",
+ "BACKUP_CODES_DESC": "Generate new codes if you've lost or used your existing ones",
+ "REGENERATE": "Regenerate Backup Codes",
+ "DISABLE_MFA": "Disable 2FA",
+ "DISABLE_MFA_DESC": "Remove two-factor authentication from your account",
+ "DISABLE_BUTTON": "Disable Two-Factor Authentication"
+ },
+ "DISABLE": {
+ "TITLE": "Disable Two-Factor Authentication",
+ "DESCRIPTION": "You'll need to enter your password and a verification code to disable two-factor authentication.",
+ "PASSWORD": "Password",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Disable 2FA",
+ "CANCEL": "Cancel",
+ "SUCCESS": "Two-factor authentication has been disabled",
+ "ERROR": "Failed to disable MFA. Please check your credentials."
+ },
+ "REGENERATE": {
+ "TITLE": "Regenerate Backup Codes",
+ "DESCRIPTION": "This will invalidate your existing backup codes and generate new ones. Enter your verification code to continue.",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Generate New Codes",
+ "CANCEL": "Cancel",
+ "NEW_CODES_TITLE": "New Backup Codes Generated",
+ "NEW_CODES_DESC": "Your old backup codes have been invalidated. Save these new codes in a secure location.",
+ "CODES_IMPORTANT": "Important:",
+ "CODES_IMPORTANT_NOTE": " Each code can only be used once. Save them before closing this window.",
+ "DOWNLOAD_CODES": "Download Codes",
+ "COPY_ALL_CODES": "Copy All Codes",
+ "CODES_SAVED": "I've Saved My Codes",
+ "SUCCESS": "New backup codes have been generated",
+ "ERROR": "Failed to regenerate backup codes"
+ }
+ },
+ "MFA_VERIFICATION": {
+ "TITLE": "Two-Factor Authentication",
+ "DESCRIPTION": "Enter your verification code to continue",
+ "AUTHENTICATOR_APP": "Authenticator App",
+ "BACKUP_CODE": "Backup Code",
+ "ENTER_OTP_CODE": "Enter 6-digit code from your authenticator app",
+ "ENTER_BACKUP_CODE": "Enter one of your backup codes",
+ "BACKUP_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify",
+ "TRY_ANOTHER_METHOD": "Try another verification method",
+ "CANCEL_LOGIN": "Cancel and return to login",
+ "HELP_TEXT": "Having trouble signing in?",
+ "LEARN_MORE": "Learn more about 2FA",
+ "HELP_MODAL": {
+ "TITLE": "Two-Factor Authentication Help",
+ "AUTHENTICATOR_TITLE": "Using an Authenticator App",
+ "AUTHENTICATOR_DESC": "Open your authenticator app (Google Authenticator, Authy, etc.) and enter the 6-digit code shown for your account.",
+ "BACKUP_TITLE": "Using a Backup Code",
+ "BACKUP_DESC": "If you don't have access to your authenticator app, you can use one of the backup codes you saved when setting up 2FA. Each code can only be used once.",
+ "CONTACT_TITLE": "Need More Help?",
+ "CONTACT_DESC_CLOUD": "If you've lost access to both your authenticator app and backup codes, please reach out to Chatwoot support for assistance.",
+ "CONTACT_DESC_SELF_HOSTED": "If you've lost access to both your authenticator app and backup codes, please contact your administrator for assistance."
+ },
+ "VERIFICATION_FAILED": "Verification failed. Please try again."
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/sq/settings.json b/app/javascript/dashboard/i18n/locale/sq/settings.json
index 705e75d37..dda91af84 100644
--- a/app/javascript/dashboard/i18n/locale/sq/settings.json
+++ b/app/javascript/dashboard/i18n/locale/sq/settings.json
@@ -51,6 +51,13 @@
"LARGER": "Larger",
"EXTRA_LARGE": "Extra Large"
}
+ },
+ "LANGUAGE": {
+ "TITLE": "Preferred Language",
+ "NOTE": "Choose the language you want to use.",
+ "UPDATE_SUCCESS": "Your Language settings have been updated successfully",
+ "UPDATE_ERROR": "There is an error while updating the language settings, please try again",
+ "USE_ACCOUNT_DEFAULT": "Use account default"
}
},
"MESSAGE_SIGNATURE_SECTION": {
@@ -73,6 +80,11 @@
"NOTE": "Updating your password would reset your logins in multiple devices.",
"BTN_TEXT": "Change password"
},
+ "SECURITY_SECTION": {
+ "TITLE": "Security",
+ "NOTE": "Manage additional security features for your account.",
+ "MFA_BUTTON": "Manage Two-Factor Authentication"
+ },
"ACCESS_TOKEN": {
"TITLE": "Access Token",
"NOTE": "This token can be used if you are building an API based integration",
@@ -330,6 +342,7 @@
"REPORTS_LABEL": "Labels",
"REPORTS_INBOX": "Inbox",
"REPORTS_TEAM": "Team",
+ "AGENT_ASSIGNMENT": "Agent Assignment",
"SET_AVAILABILITY_TITLE": "Set yourself as",
"SET_YOUR_AVAILABILITY": "Set your availability",
"SLA": "SLA",
@@ -350,7 +363,8 @@
"INFO_TEXT": "Let the system automatically mark you offline when you aren't using the app or dashboard.",
"INFO_SHORT": "Automatically mark offline when you aren't using the app."
},
- "DOCS": "Read docs"
+ "DOCS": "Read docs",
+ "SECURITY": "Security"
},
"BILLING_SETTINGS": {
"TITLE": "Billing",
@@ -382,6 +396,77 @@
},
"NO_BILLING_USER": "Your billing account is being configured. Please refresh the page and try again."
},
+ "SECURITY_SETTINGS": {
+ "TITLE": "Security",
+ "DESCRIPTION": "Manage your account security settings.",
+ "LINK_TEXT": "Learn more about SAML SSO",
+ "SAML": {
+ "TITLE": "SAML SSO",
+ "NOTE": "Configure SAML single sign-on for your account. Users will authenticate through your identity provider instead of using email/password.",
+ "ACS_URL": {
+ "LABEL": "ACS URL",
+ "TOOLTIP": "Assertion Consumer Service URL - Configure this URL in your IdP as the destination for SAML responses"
+ },
+ "SSO_URL": {
+ "LABEL": "SSO URL",
+ "HELP": "The URL where SAML authentication requests will be sent",
+ "PLACEHOLDER": "https://your-idp.com/saml/sso"
+ },
+ "CERTIFICATE": {
+ "LABEL": "Signing certificate in PEM format",
+ "HELP": "The public certificate from your identity provider used to verify SAML responses",
+ "PLACEHOLDER": "-----BEGIN CERTIFICATE-----\nMIIC..."
+ },
+ "FINGERPRINT": {
+ "LABEL": "Fingerprint",
+ "TOOLTIP": "SHA-1 fingerprint of the certificate - Use this to verify the certificate in your IdP configuration"
+ },
+ "COPY_SUCCESS": "Copied to clipboard",
+ "SP_ENTITY_ID": {
+ "LABEL": "SP Entity ID",
+ "HELP": "Unique identifier for this application as a service provider (auto-generated).",
+ "TOOLTIP": "Unique identifier for Chatwoot as the Service Provider - Configure this in your IdP settings"
+ },
+ "IDP_ENTITY_ID": {
+ "LABEL": "Identity Provider Entity ID",
+ "HELP": "Unique identifier for your identity provider (usually found in IdP configuration)",
+ "PLACEHOLDER": "https://your-idp.com/saml"
+ },
+ "UPDATE_BUTTON": "Update SAML Settings",
+ "API": {
+ "SUCCESS": "SAML settings updated successfully",
+ "ERROR": "Failed to update SAML settings",
+ "ERROR_LOADING": "Failed to load SAML settings",
+ "DISABLED": "SAML settings disabled successfully"
+ },
+ "VALIDATION": {
+ "REQUIRED_FIELDS": "SSO URL, Identity Provider Entity ID, and Certificate are required fields",
+ "SSO_URL_ERROR": "Please enter a valid SSO URL",
+ "CERTIFICATE_ERROR": "Certificate is required",
+ "IDP_ENTITY_ID_ERROR": "Identity Provider Entity ID is required"
+ },
+ "ENTERPRISE_PAYWALL": {
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade to an Enterprise plan to access SAML single sign-on and other advanced security features.",
+ "ASK_ADMIN": "Please reach out to your administrator for the upgrade."
+ },
+ "PAYWALL": {
+ "TITLE": "Upgrade to enable SAML SSO",
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade your plan to get access to SAML single sign-on and other advanced features.",
+ "UPGRADE_NOW": "Upgrade now",
+ "CANCEL_ANYTIME": "You can change or cancel your plan anytime"
+ },
+ "ATTRIBUTE_MAPPING": {
+ "TITLE": "SAML Attribute Setup",
+ "DESCRIPTION": "The following attribute mappings must be configured in your identity provider"
+ },
+ "INFO_SECTION": {
+ "TITLE": "Service Provider Information",
+ "TOOLTIP": "Copy these values and configure them in your Identity Provider to establish the SAML connection"
+ }
+ }
+ },
"CREATE_ACCOUNT": {
"NO_ACCOUNT_WARNING": "Uh oh! We could not find any Chatwoot accounts. Please create a new account to continue.",
"NEW_ACCOUNT": "New Account",
@@ -418,5 +503,255 @@
"SWITCH_TO_REPLY": "Switch to Reply",
"TOGGLE_SNOOZE_DROPDOWN": "Toggle snooze dropdown"
}
+ },
+ "ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent assignment",
+ "DESCRIPTION": "Define policies to effectively manage workload and route conversations based on the needs of inboxes and agents. Learn more here"
+ },
+ "ASSIGNMENT_POLICY": {
+ "TITLE": "Assignment policy",
+ "DESCRIPTION": "Manage how conversations get assigned in inboxes.",
+ "FEATURES": [
+ "Assign by conversations evenly or by available capacity",
+ "Add fair distribution rules to avoid overloading any agent",
+ "Add inboxes to a policy - one policy per inbox"
+ ]
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "TITLE": "Agent capacity policy",
+ "DESCRIPTION": "Manage workload for agents.",
+ "FEATURES": [
+ "Define maximum conversations per inbox",
+ "Create exceptions based on labels and time",
+ "Add agents to a policy - one policy per agent"
+ ]
+ }
+ },
+ "AGENT_ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Assignment policy",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "ORDER": "Order",
+ "PRIORITY": "Priority",
+ "ACTIVE": "Active",
+ "INACTIVE": "Inactive",
+ "POPOVER": "Added inboxes",
+ "EDIT": "Edit"
+ },
+ "NO_RECORDS_FOUND": "No assignment policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create assignment policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy created successfully",
+ "ERROR_MESSAGE": "Failed to create assignment policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit assignment policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_INBOX_DIALOG": {
+ "TITLE": "Add inbox",
+ "DESCRIPTION": "{inboxName} inbox is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Cancel"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update assignment policy"
+ },
+ "INBOX_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Inbox added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Inbox removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove inbox from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Description:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "STATUS": {
+ "LABEL": "Status:",
+ "PLACEHOLDER": "Select status",
+ "ACTIVE": "Policy is active",
+ "INACTIVE": "Policy is inactive"
+ },
+ "ASSIGNMENT_ORDER": {
+ "LABEL": "Assignment order",
+ "ROUND_ROBIN": {
+ "LABEL": "Round robin",
+ "DESCRIPTION": "Assign conversations evenly among agents."
+ },
+ "BALANCED": {
+ "LABEL": "Balanced",
+ "DESCRIPTION": "Assign conversations based on available capacity."
+ }
+ },
+ "ASSIGNMENT_PRIORITY": {
+ "LABEL": "Assignment priority",
+ "EARLIEST_CREATED": {
+ "LABEL": "Earliest created",
+ "DESCRIPTION": "The conversation that was created first gets assigned first."
+ },
+ "LONGEST_WAITING": {
+ "LABEL": "Longest waiting",
+ "DESCRIPTION": "The conversation waiting the longest gets assigned first."
+ }
+ },
+ "FAIR_DISTRIBUTION": {
+ "LABEL": "Fair distribution policy",
+ "DESCRIPTION": "Set the maximum number of conversations that can be assigned per agent within a time window to avoid overloading any one agent. This required field defaults to 100 conversations per hour.",
+ "INPUT_MAX": "Assign max",
+ "DURATION": "Conversations per agent in every"
+ },
+ "INBOXES": {
+ "LABEL": "Added inboxes",
+ "DESCRIPTION": "Add inboxes for which this policy will be applicable.",
+ "ADD_BUTTON": "Add inbox",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select inboxes to add",
+ "ADD_BUTTON": "Add"
+ },
+ "EMPTY_STATE": "No inboxes added to this policy, add an inbox to get started",
+ "API": {
+ "SUCCESS_MESSAGE": "Inbox successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Assignment policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete assignment policy"
+ }
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent capacity",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "POPOVER": "Added agents",
+ "EDIT": "Edit"
+ },
+ "NO_RECORDS_FOUND": "No agent capacity policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create agent capacity policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy created successfully",
+ "ERROR_MESSAGE": "Failed to create agent capacity policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit agent capacity policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_AGENT_DIALOG": {
+ "TITLE": "Add agent",
+ "DESCRIPTION": "{agentName} is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Cancel"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update agent capacity policy"
+ },
+ "AGENT_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Agent added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Agent removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove agent from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Description:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "INBOX_CAPACITY_LIMIT": {
+ "LABEL": "Inbox capacity limits",
+ "ADD_BUTTON": "Add inbox",
+ "FIELD": {
+ "SELECT_INBOX": "Select inbox",
+ "MAX_CONVERSATIONS": "Max conversations",
+ "SET_LIMIT": "Set limit"
+ },
+ "EMPTY_STATE": "No inbox limit set"
+ },
+ "EXCLUSION_RULES": {
+ "LABEL": "Exclusion rules",
+ "DESCRIPTION": "Conversations that satisfy the following conditions would not count towards agent capacity",
+ "TAGS": {
+ "LABEL": "Exclude conversations tagged with specific labels",
+ "ADD_TAG": "add tag",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select tags to add"
+ },
+ "EMPTY_STATE": "No tags added to this policy."
+ },
+ "DURATION": {
+ "LABEL": "Exclude conversations older than a specified duration",
+ "PLACEHOLDER": "Set time"
+ }
+ },
+ "USERS": {
+ "LABEL": "Assigned agents",
+ "DESCRIPTION": "Add agents for which this policy will be applicable.",
+ "ADD_BUTTON": "Add agent",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select agents to add",
+ "ADD_BUTTON": "Add"
+ },
+ "EMPTY_STATE": "No agents added",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Agent capacity policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete agent capacity policy"
+ }
+ },
+ "DELETE_POLICY": {
+ "TITLE": "Delete policy",
+ "DESCRIPTION": "Are you sure you want to delete this policy? This action cannot be undone.",
+ "CONFIRM_BUTTON_LABEL": "Delete",
+ "CANCEL_BUTTON_LABEL": "Cancel"
+ }
}
}
diff --git a/app/javascript/dashboard/i18n/locale/sq/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/sq/whatsappTemplates.json
index 43cfeea51..2281857e6 100644
--- a/app/javascript/dashboard/i18n/locale/sq/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/sq/whatsappTemplates.json
@@ -40,6 +40,7 @@
"BUTTON_LABEL": "Butoni {index}",
"COUPON_CODE": "Futni kodin e kuponit (maks 15 karaktere)",
"MEDIA_URL_LABEL": "Futni URL-në e {type}",
+ "DOCUMENT_NAME_PLACEHOLDER": "Enter document filename (e.g., Invoice_2025.pdf)",
"BUTTON_PARAMETER": "Futni parametrin e butonit"
}
}
diff --git a/app/javascript/dashboard/i18n/locale/sr/automation.json b/app/javascript/dashboard/i18n/locale/sr/automation.json
index 0f15f8e3d..e2f86f2f9 100644
--- a/app/javascript/dashboard/i18n/locale/sr/automation.json
+++ b/app/javascript/dashboard/i18n/locale/sr/automation.json
@@ -177,7 +177,8 @@
"REFERER_LINK": "Referrer Link",
"ASSIGNEE_NAME": "Assignee",
"TEAM_NAME": "Tim",
- "PRIORITY": "Priority"
+ "PRIORITY": "Priority",
+ "LABELS": "Oznake"
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/sr/contact.json b/app/javascript/dashboard/i18n/locale/sr/contact.json
index 64d2afe4a..15c2151a9 100644
--- a/app/javascript/dashboard/i18n/locale/sr/contact.json
+++ b/app/javascript/dashboard/i18n/locale/sr/contact.json
@@ -610,6 +610,15 @@
"SEND_MESSAGE": "Pošalji poruku"
}
},
+ "TWILIO_OPTIONS": {
+ "LABEL": "Select template",
+ "SEARCH_PLACEHOLDER": "Search templates",
+ "EMPTY_STATE": "No templates found",
+ "TEMPLATE_PARSER": {
+ "BACK": "Povratak",
+ "SEND_MESSAGE": "Pošalji poruku"
+ }
+ },
"ACTION_BUTTONS": {
"DISCARD": "Discard",
"SEND": "Send ({keyCode})"
diff --git a/app/javascript/dashboard/i18n/locale/sr/contactFilters.json b/app/javascript/dashboard/i18n/locale/sr/contactFilters.json
index 51f8dce6f..874e97846 100644
--- a/app/javascript/dashboard/i18n/locale/sr/contactFilters.json
+++ b/app/javascript/dashboard/i18n/locale/sr/contactFilters.json
@@ -48,7 +48,8 @@
"CREATED_AT": "Kreirano",
"LAST_ACTIVITY": "Poslednja aktivnost",
"REFERER_LINK": "Veza preporuke",
- "BLOCKED": "Blocked"
+ "BLOCKED": "Blocked",
+ "LABELS": "Oznake"
},
"GROUPS": {
"STANDARD_FILTERS": "Standardni filteri",
diff --git a/app/javascript/dashboard/i18n/locale/sr/contentTemplates.json b/app/javascript/dashboard/i18n/locale/sr/contentTemplates.json
new file mode 100644
index 000000000..ad67b2ccc
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/sr/contentTemplates.json
@@ -0,0 +1,51 @@
+{
+ "CONTENT_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Twilio Templates",
+ "SUBTITLE": "Select the Twilio template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Pretraži šablone",
+ "NO_TEMPLATES_FOUND": "Nijedan šablon nije pronađen",
+ "NO_CONTENT": "No content",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Kategorija",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No Twilio templates available. Click refresh to sync templates from Twilio.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Jezik",
+ "TEMPLATE_BODY": "Telo šablona",
+ "CATEGORY": "Kategorija"
+ },
+ "TYPES": {
+ "MEDIA": "Media",
+ "QUICK_REPLY": "Quick Reply",
+ "TEXT": "Tekst"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Promenljive",
+ "LANGUAGE": "Jezik",
+ "CATEGORY": "Kategorija",
+ "VARIABLE_PLACEHOLDER": "Unesite vrednost za {variable}",
+ "GO_BACK_LABEL": "Povratak",
+ "SEND_MESSAGE_LABEL": "Pošalji poruku",
+ "FORM_ERROR_MESSAGE": "Molim vas popunite sve promenljive pre slanja",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "MEDIA_URL_LABEL": "Enter full media URL",
+ "MEDIA_URL_PLACEHOLDER": "https://example.com/image.jpg"
+ },
+ "FORM": {
+ "BACK_BUTTON": "Nazad",
+ "SEND_MESSAGE_BUTTON": "Pošalji poruku"
+ }
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/sr/conversation.json b/app/javascript/dashboard/i18n/locale/sr/conversation.json
index cbf7cfe36..1ac190668 100644
--- a/app/javascript/dashboard/i18n/locale/sr/conversation.json
+++ b/app/javascript/dashboard/i18n/locale/sr/conversation.json
@@ -35,6 +35,11 @@
"API_HOURS_WINDOW": "You can only reply to this conversation within {hours} hours",
"NOT_ASSIGNED_TO_YOU": "Ovaj razgovor nije dodeljen vama. Da li želite da dodelite razgovor sebi?",
"ASSIGN_TO_ME": "Dodeli meni",
+ "BOT_HANDOFF_MESSAGE": "You are responding to a conversation which is currently handled by an assistant or a bot.",
+ "BOT_HANDOFF_ACTION": "Mark open and assign to you",
+ "BOT_HANDOFF_REOPEN_ACTION": "Mark conversation open",
+ "BOT_HANDOFF_SUCCESS": "Conversation has been handed over to you",
+ "BOT_HANDOFF_ERROR": "Failed to take over the conversation. Please try again.",
"TWILIO_WHATSAPP_CAN_REPLY": "Možete jedino da odgovarate na ovaj razgovor koristeći šablon poruka zbog",
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "24-časovno ograničenje poruka",
"OLD_INSTAGRAM_INBOX_REPLY_BANNER": "This Instagram account was migrated to the new Instagram channel inbox. All new messages will show up there. You won’t be able to send messages from this conversation anymore.",
@@ -66,6 +71,17 @@
"SHOW_LABELS": "Show labels",
"HIDE_LABELS": "Hide labels"
},
+ "VOICE_CALL": {
+ "INCOMING_CALL": "Incoming call",
+ "OUTGOING_CALL": "Outgoing call",
+ "CALL_IN_PROGRESS": "Call in progress",
+ "NO_ANSWER": "No answer",
+ "MISSED_CALL": "Missed call",
+ "CALL_ENDED": "Call ended",
+ "NOT_ANSWERED_YET": "Not answered yet",
+ "THEY_ANSWERED": "They answered",
+ "YOU_ANSWERED": "You answered"
+ },
"HEADER": {
"RESOLVE_ACTION": "Reši",
"REOPEN_ACTION": "Ponovo otvori",
diff --git a/app/javascript/dashboard/i18n/locale/sr/helpCenter.json b/app/javascript/dashboard/i18n/locale/sr/helpCenter.json
index 892fc24ad..f3461ef73 100644
--- a/app/javascript/dashboard/i18n/locale/sr/helpCenter.json
+++ b/app/javascript/dashboard/i18n/locale/sr/helpCenter.json
@@ -741,7 +741,8 @@
"LIVE_CHAT_WIDGET": {
"LABEL": "Live chat widget",
"PLACEHOLDER": "Select live chat widget",
- "HELP_TEXT": "Select a live chat widget that will appear on your help center"
+ "HELP_TEXT": "Select a live chat widget that will appear on your help center",
+ "NONE_OPTION": "No widget"
},
"BRAND_COLOR": {
"LABEL": "Brand color"
@@ -807,6 +808,58 @@
"ERROR_MESSAGE": "Unable to update portal"
}
}
+ },
+ "PDF_UPLOAD": {
+ "TITLE": "Upload PDF Document",
+ "DESCRIPTION": "Upload a PDF document to automatically generate FAQs using AI",
+ "DRAG_DROP_TEXT": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "ADDITIONAL_CONTEXT_LABEL": "Additional Context (Optional)",
+ "ADDITIONAL_CONTEXT_PLACEHOLDER": "Provide any additional context or instructions for FAQ generation...",
+ "UPLOADING": "Dodavanje...",
+ "UPLOAD": "Upload & Process",
+ "CANCEL": "Otkaži",
+ "ERROR_INVALID_TYPE": "Please select a valid PDF file",
+ "ERROR_FILE_TOO_LARGE": "File size must be less than 512MB",
+ "ERROR_UPLOAD_FAILED": "Failed to upload PDF. Please try again."
+ },
+ "PDF_DOCUMENTS": {
+ "TITLE": "PDF Documents",
+ "DESCRIPTION": "Manage uploaded PDF documents and generate FAQs from them",
+ "UPLOAD_PDF": "Upload PDF",
+ "UPLOAD_FIRST_PDF": "Upload your first PDF",
+ "UPLOADED_BY": "Uploaded by",
+ "GENERATE_FAQS": "Generate FAQs",
+ "GENERATING": "Generating...",
+ "CONFIRM_DELETE": "Are you sure you want to delete {filename}?",
+ "EMPTY_STATE": {
+ "TITLE": "No PDF documents yet",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQs using AI"
+ },
+ "STATUS": {
+ "UPLOADED": "Ready",
+ "PROCESSING": "Processing",
+ "PROCESSED": "Završeno",
+ "FAILED": "Failed"
+ }
+ },
+ "CONTENT_GENERATION": {
+ "TITLE": "Content Generation",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQ content using AI",
+ "UPLOAD_TITLE": "Upload PDF Document",
+ "DRAG_DROP": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "UPLOADING": "Processing document...",
+ "UPLOAD_SUCCESS": "Document processed successfully!",
+ "UPLOAD_ERROR": "Failed to upload document. Please try again.",
+ "INVALID_FILE_TYPE": "Please select a valid PDF file",
+ "FILE_TOO_LARGE": "File size must be less than 512MB",
+ "GENERATED_CONTENT": "Generated FAQ Content",
+ "PUBLISH_SELECTED": "Publish Selected",
+ "PUBLISHING": "Publishing...",
+ "FROM_DOCUMENT": "From document",
+ "NO_CONTENT": "No generated content available. Upload a PDF document to get started.",
+ "LOADING": "Loading generated content..."
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/sr/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/sr/inboxMgmt.json
index 58b7f46e9..912fbbd97 100644
--- a/app/javascript/dashboard/i18n/locale/sr/inboxMgmt.json
+++ b/app/javascript/dashboard/i18n/locale/sr/inboxMgmt.json
@@ -272,8 +272,8 @@
},
"SUBMIT_BUTTON": "Napravite WhatsApp kanal",
"EMBEDDED_SIGNUP": {
- "TITLE": "Quick Setup with Meta",
- "DESC": "You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
+ "TITLE": "Quick setup with Meta",
+ "DESC": "Use the WhatsApp Embedded Signup flow to quickly connect new numbers. You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
"BENEFITS": {
"TITLE": "Benefits of Embedded Signup:",
"EASY_SETUP": "No manual configuration required",
@@ -281,9 +281,8 @@
"AUTO_CONFIG": "Automatic webhook and phone number configuration"
},
"LEARN_MORE": {
- "TEXT": "To learn more about integrated signup, pricing, and limitations, visit",
- "LINK_TEXT": "this link.",
- "LINK_URL": "https://developers.facebook.com/docs/whatsapp/embedded-signup/custom-flows/onboarding-business-app-users#limitations"
+ "TEXT": "To learn more about integrated signup, pricing, and limitations, visit {link}.",
+ "LINK_TEXT": "this link"
},
"SUBMIT_BUTTON": "Connect with WhatsApp Business",
"AUTH_PROCESSING": "Authenticating with Meta",
@@ -296,7 +295,9 @@
"INVALID_BUSINESS_DATA": "Invalid business data received from Facebook. Please try again.",
"SIGNUP_ERROR": "Signup error occurred",
"AUTH_NOT_COMPLETED": "Authentication not completed. Please restart the process.",
- "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured"
+ "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured",
+ "MANUAL_FALLBACK": "If your number is already connected to the WhatsApp Business Platform (API), or if you’re a tech provider onboarding your own number, please use the {link} flow",
+ "MANUAL_LINK_TEXT": "manual setup flow"
},
"API": {
"ERROR_MESSAGE": "Nisamo uspeli da sačuvamo WhatsApp kanal"
@@ -424,7 +425,51 @@
},
"AUTH": {
"TITLE": "Izaberite kanal",
- "DESC": "Chatwoot podržava vidžete ćaskanja, Fejbuk Mesindžer, Tviter profile, WhatsApp, E-poštu, itd., kao kanale. Ako želite da izgradite proizvoljan kanal, možete kreirati ga korišćenjem API kanala. Da bi ste počeli, izaberite jedan od kanala ispod."
+ "DESC": "Chatwoot podržava vidžete ćaskanja, Fejbuk Mesindžer, Tviter profile, WhatsApp, E-poštu, itd., kao kanale. Ako želite da izgradite proizvoljan kanal, možete kreirati ga korišćenjem API kanala. Da bi ste počeli, izaberite jedan od kanala ispod.",
+ "TITLE_NEXT": "Complete the setup",
+ "TITLE_FINISH": "Voilà!",
+ "CHANNEL": {
+ "WEBSITE": {
+ "TITLE": "Website",
+ "DESCRIPTION": "Create a live-chat widget"
+ },
+ "FACEBOOK": {
+ "TITLE": "Facebook",
+ "DESCRIPTION": "Connect your Facebook page"
+ },
+ "WHATSAPP": {
+ "TITLE": "WhatsApp",
+ "DESCRIPTION": "Support your customers on WhatsApp"
+ },
+ "EMAIL": {
+ "TITLE": "E-pošta",
+ "DESCRIPTION": "Connect with Gmail, Outlook, or other providers"
+ },
+ "SMS": {
+ "TITLE": "SMS",
+ "DESCRIPTION": "Integrate SMS channel with Twilio or bandwidth"
+ },
+ "API": {
+ "TITLE": "API",
+ "DESCRIPTION": "Make a custom channel using our API"
+ },
+ "TELEGRAM": {
+ "TITLE": "Telegram",
+ "DESCRIPTION": "Configure Telegram channel using Bot token"
+ },
+ "LINE": {
+ "TITLE": "Line",
+ "DESCRIPTION": "Integrate your Line channel"
+ },
+ "INSTAGRAM": {
+ "TITLE": "Instagram",
+ "DESCRIPTION": "Connect your instagram account"
+ },
+ "VOICE": {
+ "TITLE": "Voice",
+ "DESCRIPTION": "Integrate with Twilio Voice"
+ }
+ }
},
"AGENTS": {
"TITLE": "Agenti",
@@ -478,7 +523,10 @@
"MESSAGE": "Možete se povezati sa vašim potrošačima kroz vaš novi kanal. Srećna podrška",
"BUTTON_TEXT": "Odvedi me tamo",
"MORE_SETTINGS": "Više podešavanja",
- "WEBSITE_SUCCESS": "Uspešno ste završili pravljenje kanala veb sajta. Iskopirajte kod prikazan ispod i nalepite ga u vaš vab sajt. Sledeći put kada potrošač koristi ćaskanje uživo, razgovor će se automatski pojaviti u prijemnom sandučetu."
+ "WEBSITE_SUCCESS": "Uspešno ste završili pravljenje kanala veb sajta. Iskopirajte kod prikazan ispod i nalepite ga u vaš vab sajt. Sledeći put kada potrošač koristi ćaskanje uživo, razgovor će se automatski pojaviti u prijemnom sandučetu.",
+ "WHATSAPP_QR_INSTRUCTION": "Scan the QR code above to quickly test your WhatsApp inbox",
+ "MESSENGER_QR_INSTRUCTION": "Scan the QR code above to quickly test your Facebook Messenger inbox",
+ "TELEGRAM_QR_INSTRUCTION": "Scan the QR code above to quickly test your Telegram inbox"
},
"REAUTH": "Ponovna autorizacija",
"VIEW": "Pregled",
@@ -868,9 +916,18 @@
"SCRIPT_SETTINGS": "\n window.chatwootSettings ={options};"
},
"EMAIL_PROVIDERS": {
- "MICROSOFT": "Microsoft",
- "GOOGLE": "Google",
- "OTHER_PROVIDERS": "Other Providers"
+ "MICROSOFT": {
+ "TITLE": "Microsoft",
+ "DESCRIPTION": "Connect with Microsoft"
+ },
+ "GOOGLE": {
+ "TITLE": "Google",
+ "DESCRIPTION": "Connect with Google"
+ },
+ "OTHER_PROVIDERS": {
+ "TITLE": "Other Providers",
+ "DESCRIPTION": "Connect with Other Providers"
+ }
},
"CHANNELS": {
"MESSENGER": "Messenger",
diff --git a/app/javascript/dashboard/i18n/locale/sr/integrations.json b/app/javascript/dashboard/i18n/locale/sr/integrations.json
index 77c6d0b7b..97bbdcb69 100644
--- a/app/javascript/dashboard/i18n/locale/sr/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/sr/integrations.json
@@ -704,11 +704,28 @@
"ERROR_MESSAGE": "There was an error creating the document, please try again."
},
"FORM": {
+ "TYPE": {
+ "LABEL": "Document Type",
+ "URL": "Adresa",
+ "PDF": "PDF File"
+ },
"URL": {
"LABEL": "Adresa",
"PLACEHOLDER": "Enter the URL of the document",
"ERROR": "Please provide a valid URL for the document"
},
+ "PDF_FILE": {
+ "LABEL": "PDF File",
+ "CHOOSE_FILE": "Choose PDF file",
+ "ERROR": "Please select a PDF file",
+ "HELP_TEXT": "Maximum file size: 10MB",
+ "INVALID_TYPE": "Please select a valid PDF file",
+ "TOO_LARGE": "File size exceeds 10MB limit"
+ },
+ "NAME": {
+ "LABEL": "Document Name (Optional)",
+ "PLACEHOLDER": "Enter a name for the document"
+ },
"ASSISTANT": {
"LABEL": "Assistant",
"PLACEHOLDER": "Select the assistant",
@@ -744,6 +761,7 @@
"SELECTED": "{count} selected",
"SELECT_ALL": "Select all ({count})",
"UNSELECT_ALL": "Unselect all ({count})",
+ "SEARCH_PLACEHOLDER": "Search FAQs...",
"BULK_APPROVE_BUTTON": "Approve",
"BULK_DELETE_BUTTON": "Izbriši",
"BULK_APPROVE": {
diff --git a/app/javascript/dashboard/i18n/locale/sr/mfa.json b/app/javascript/dashboard/i18n/locale/sr/mfa.json
new file mode 100644
index 000000000..00daffda8
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/sr/mfa.json
@@ -0,0 +1,106 @@
+{
+ "MFA_SETTINGS": {
+ "TITLE": "Two-Factor Authentication",
+ "SUBTITLE": "Secure your account with TOTP-based authentication",
+ "DESCRIPTION": "Add an extra layer of security to your account using a time-based one-time password (TOTP)",
+ "STATUS_TITLE": "Authentication Status",
+ "STATUS_DESCRIPTION": "Manage your two-factor authentication settings and backup recovery codes",
+ "ENABLED": "Omogućeno",
+ "DISABLED": "Onemogućeno",
+ "STATUS_ENABLED": "Two-factor authentication is active",
+ "STATUS_ENABLED_DESC": "Your account is protected with an additional layer of security",
+ "ENABLE_BUTTON": "Enable Two-Factor Authentication",
+ "ENHANCE_SECURITY": "Enhance Your Account Security",
+ "ENHANCE_SECURITY_DESC": "Two-factor authentication adds an extra layer of security by requiring a verification code from your authenticator app in addition to your password.",
+ "SETUP": {
+ "STEP_NUMBER_1": "1",
+ "STEP_NUMBER_2": "2",
+ "STEP1_TITLE": "Scan QR Code with Your Authenticator App",
+ "STEP1_DESCRIPTION": "Use Google Authenticator, Authy, or any TOTP-compatible app",
+ "LOADING_QR": "Loading...",
+ "MANUAL_ENTRY": "Can't scan? Enter code manually",
+ "SECRET_KEY": "Secret Key",
+ "COPY": "Kopiraj",
+ "ENTER_CODE": "Enter the 6-digit code from your authenticator app",
+ "ENTER_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify & Continue",
+ "CANCEL": "Otkaži",
+ "ERROR_STARTING": "MFA not enabled. Please contact administrator.",
+ "INVALID_CODE": "Invalid verification code",
+ "SECRET_COPIED": "Secret key copied to clipboard",
+ "SUCCESS": "Two-factor authentication has been enabled successfully"
+ },
+ "BACKUP": {
+ "TITLE": "Save Your Backup Codes",
+ "DESCRIPTION": "Keep these codes safe. Each can be used once if you lose access to your authenticator",
+ "IMPORTANT": "Important:",
+ "IMPORTANT_NOTE": " Save these codes in a secure location. You won't be able to see them again.",
+ "DOWNLOAD": "Preuzmi",
+ "COPY_ALL": "Copy All",
+ "CONFIRM": "I have saved my backup codes in a secure location and understand that I won't be able to see them again",
+ "COMPLETE_SETUP": "Complete Setup",
+ "CODES_COPIED": "Backup codes copied to clipboard"
+ },
+ "MANAGEMENT": {
+ "BACKUP_CODES": "Backup Codes",
+ "BACKUP_CODES_DESC": "Generate new codes if you've lost or used your existing ones",
+ "REGENERATE": "Regenerate Backup Codes",
+ "DISABLE_MFA": "Disable 2FA",
+ "DISABLE_MFA_DESC": "Remove two-factor authentication from your account",
+ "DISABLE_BUTTON": "Disable Two-Factor Authentication"
+ },
+ "DISABLE": {
+ "TITLE": "Disable Two-Factor Authentication",
+ "DESCRIPTION": "You'll need to enter your password and a verification code to disable two-factor authentication.",
+ "PASSWORD": "Lozinka",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Disable 2FA",
+ "CANCEL": "Otkaži",
+ "SUCCESS": "Two-factor authentication has been disabled",
+ "ERROR": "Failed to disable MFA. Please check your credentials."
+ },
+ "REGENERATE": {
+ "TITLE": "Regenerate Backup Codes",
+ "DESCRIPTION": "This will invalidate your existing backup codes and generate new ones. Enter your verification code to continue.",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Generate New Codes",
+ "CANCEL": "Otkaži",
+ "NEW_CODES_TITLE": "New Backup Codes Generated",
+ "NEW_CODES_DESC": "Your old backup codes have been invalidated. Save these new codes in a secure location.",
+ "CODES_IMPORTANT": "Important:",
+ "CODES_IMPORTANT_NOTE": " Each code can only be used once. Save them before closing this window.",
+ "DOWNLOAD_CODES": "Download Codes",
+ "COPY_ALL_CODES": "Copy All Codes",
+ "CODES_SAVED": "I've Saved My Codes",
+ "SUCCESS": "New backup codes have been generated",
+ "ERROR": "Failed to regenerate backup codes"
+ }
+ },
+ "MFA_VERIFICATION": {
+ "TITLE": "Two-Factor Authentication",
+ "DESCRIPTION": "Enter your verification code to continue",
+ "AUTHENTICATOR_APP": "Authenticator App",
+ "BACKUP_CODE": "Backup Code",
+ "ENTER_OTP_CODE": "Enter 6-digit code from your authenticator app",
+ "ENTER_BACKUP_CODE": "Enter one of your backup codes",
+ "BACKUP_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify",
+ "TRY_ANOTHER_METHOD": "Try another verification method",
+ "CANCEL_LOGIN": "Cancel and return to login",
+ "HELP_TEXT": "Having trouble signing in?",
+ "LEARN_MORE": "Learn more about 2FA",
+ "HELP_MODAL": {
+ "TITLE": "Two-Factor Authentication Help",
+ "AUTHENTICATOR_TITLE": "Using an Authenticator App",
+ "AUTHENTICATOR_DESC": "Open your authenticator app (Google Authenticator, Authy, etc.) and enter the 6-digit code shown for your account.",
+ "BACKUP_TITLE": "Using a Backup Code",
+ "BACKUP_DESC": "If you don't have access to your authenticator app, you can use one of the backup codes you saved when setting up 2FA. Each code can only be used once.",
+ "CONTACT_TITLE": "Need More Help?",
+ "CONTACT_DESC_CLOUD": "If you've lost access to both your authenticator app and backup codes, please reach out to Chatwoot support for assistance.",
+ "CONTACT_DESC_SELF_HOSTED": "If you've lost access to both your authenticator app and backup codes, please contact your administrator for assistance."
+ },
+ "VERIFICATION_FAILED": "Verification failed. Please try again."
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/sr/settings.json b/app/javascript/dashboard/i18n/locale/sr/settings.json
index d83baf157..070361cd9 100644
--- a/app/javascript/dashboard/i18n/locale/sr/settings.json
+++ b/app/javascript/dashboard/i18n/locale/sr/settings.json
@@ -51,6 +51,13 @@
"LARGER": "Larger",
"EXTRA_LARGE": "Extra Large"
}
+ },
+ "LANGUAGE": {
+ "TITLE": "Preferred Language",
+ "NOTE": "Choose the language you want to use.",
+ "UPDATE_SUCCESS": "Your Language settings have been updated successfully",
+ "UPDATE_ERROR": "There is an error while updating the language settings, please try again",
+ "USE_ACCOUNT_DEFAULT": "Use account default"
}
},
"MESSAGE_SIGNATURE_SECTION": {
@@ -73,6 +80,11 @@
"NOTE": "Izmena lozinke će resetovati vaše prijave na više uređaja.",
"BTN_TEXT": "Promeni lozinku"
},
+ "SECURITY_SECTION": {
+ "TITLE": "Security",
+ "NOTE": "Manage additional security features for your account.",
+ "MFA_BUTTON": "Manage Two-Factor Authentication"
+ },
"ACCESS_TOKEN": {
"TITLE": "Token za pristup",
"NOTE": "Ovaj token se može koristiti ako izgrađujete integraciju zasnovanu na API-ju",
@@ -330,6 +342,7 @@
"REPORTS_LABEL": "Oznake",
"REPORTS_INBOX": "Prijemno sanduče",
"REPORTS_TEAM": "Tim",
+ "AGENT_ASSIGNMENT": "Agent Assignment",
"SET_AVAILABILITY_TITLE": "Postavite da ste",
"SET_YOUR_AVAILABILITY": "Podesite vašu dostupnost",
"SLA": "SLA",
@@ -350,7 +363,8 @@
"INFO_TEXT": "Let the system automatically mark you offline when you aren't using the app or dashboard.",
"INFO_SHORT": "Automatically mark offline when you aren't using the app."
},
- "DOCS": "Pročitaj dokumentaciju"
+ "DOCS": "Pročitaj dokumentaciju",
+ "SECURITY": "Security"
},
"BILLING_SETTINGS": {
"TITLE": "Plaćanje",
@@ -382,6 +396,77 @@
},
"NO_BILLING_USER": "Vaš nalog za plaćanje se podešava. Molim vas osvežite stranicu i pokušajte ponovo."
},
+ "SECURITY_SETTINGS": {
+ "TITLE": "Security",
+ "DESCRIPTION": "Manage your account security settings.",
+ "LINK_TEXT": "Learn more about SAML SSO",
+ "SAML": {
+ "TITLE": "SAML SSO",
+ "NOTE": "Configure SAML single sign-on for your account. Users will authenticate through your identity provider instead of using email/password.",
+ "ACS_URL": {
+ "LABEL": "ACS URL",
+ "TOOLTIP": "Assertion Consumer Service URL - Configure this URL in your IdP as the destination for SAML responses"
+ },
+ "SSO_URL": {
+ "LABEL": "SSO URL",
+ "HELP": "The URL where SAML authentication requests will be sent",
+ "PLACEHOLDER": "https://your-idp.com/saml/sso"
+ },
+ "CERTIFICATE": {
+ "LABEL": "Signing certificate in PEM format",
+ "HELP": "The public certificate from your identity provider used to verify SAML responses",
+ "PLACEHOLDER": "-----BEGIN CERTIFICATE-----\nMIIC..."
+ },
+ "FINGERPRINT": {
+ "LABEL": "Fingerprint",
+ "TOOLTIP": "SHA-1 fingerprint of the certificate - Use this to verify the certificate in your IdP configuration"
+ },
+ "COPY_SUCCESS": "Kod je uspešno kopiran na beležnicu",
+ "SP_ENTITY_ID": {
+ "LABEL": "SP Entity ID",
+ "HELP": "Unique identifier for this application as a service provider (auto-generated).",
+ "TOOLTIP": "Unique identifier for Chatwoot as the Service Provider - Configure this in your IdP settings"
+ },
+ "IDP_ENTITY_ID": {
+ "LABEL": "Identity Provider Entity ID",
+ "HELP": "Unique identifier for your identity provider (usually found in IdP configuration)",
+ "PLACEHOLDER": "https://your-idp.com/saml"
+ },
+ "UPDATE_BUTTON": "Update SAML Settings",
+ "API": {
+ "SUCCESS": "SAML settings updated successfully",
+ "ERROR": "Failed to update SAML settings",
+ "ERROR_LOADING": "Failed to load SAML settings",
+ "DISABLED": "SAML settings disabled successfully"
+ },
+ "VALIDATION": {
+ "REQUIRED_FIELDS": "SSO URL, Identity Provider Entity ID, and Certificate are required fields",
+ "SSO_URL_ERROR": "Please enter a valid SSO URL",
+ "CERTIFICATE_ERROR": "Certificate is required",
+ "IDP_ENTITY_ID_ERROR": "Identity Provider Entity ID is required"
+ },
+ "ENTERPRISE_PAYWALL": {
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade to an Enterprise plan to access SAML single sign-on and other advanced security features.",
+ "ASK_ADMIN": "Please reach out to your administrator for the upgrade."
+ },
+ "PAYWALL": {
+ "TITLE": "Upgrade to enable SAML SSO",
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade your plan to get access to SAML single sign-on and other advanced features.",
+ "UPGRADE_NOW": "Upgrade now",
+ "CANCEL_ANYTIME": "You can change or cancel your plan anytime"
+ },
+ "ATTRIBUTE_MAPPING": {
+ "TITLE": "SAML Attribute Setup",
+ "DESCRIPTION": "The following attribute mappings must be configured in your identity provider"
+ },
+ "INFO_SECTION": {
+ "TITLE": "Service Provider Information",
+ "TOOLTIP": "Copy these values and configure them in your Identity Provider to establish the SAML connection"
+ }
+ }
+ },
"CREATE_ACCOUNT": {
"NO_ACCOUNT_WARNING": "O ne! Nismo mogli da pronađemo nijedan Chatwoot nalog. Molim vas kreirajte novi da bi ste nastavili.",
"NEW_ACCOUNT": "Novi nalog",
@@ -418,5 +503,255 @@
"SWITCH_TO_REPLY": "Prebaci se na odgovor",
"TOGGLE_SNOOZE_DROPDOWN": "Uključite padajućim menijom odlaganja"
}
+ },
+ "ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent assignment",
+ "DESCRIPTION": "Define policies to effectively manage workload and route conversations based on the needs of inboxes and agents. Learn more here"
+ },
+ "ASSIGNMENT_POLICY": {
+ "TITLE": "Assignment policy",
+ "DESCRIPTION": "Manage how conversations get assigned in inboxes.",
+ "FEATURES": [
+ "Assign by conversations evenly or by available capacity",
+ "Add fair distribution rules to avoid overloading any agent",
+ "Add inboxes to a policy - one policy per inbox"
+ ]
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "TITLE": "Agent capacity policy",
+ "DESCRIPTION": "Manage workload for agents.",
+ "FEATURES": [
+ "Define maximum conversations per inbox",
+ "Create exceptions based on labels and time",
+ "Add agents to a policy - one policy per agent"
+ ]
+ }
+ },
+ "AGENT_ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Assignment policy",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "ORDER": "Order",
+ "PRIORITY": "Priority",
+ "ACTIVE": "Aktivno",
+ "INACTIVE": "Inactive",
+ "POPOVER": "Added inboxes",
+ "EDIT": "Uredi"
+ },
+ "NO_RECORDS_FOUND": "No assignment policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create assignment policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy created successfully",
+ "ERROR_MESSAGE": "Failed to create assignment policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit assignment policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_INBOX_DIALOG": {
+ "TITLE": "Add inbox",
+ "DESCRIPTION": "{inboxName} inbox is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Otkaži"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update assignment policy"
+ },
+ "INBOX_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Inbox added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Inbox removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove inbox from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Opis:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "STATUS": {
+ "LABEL": "Status:",
+ "PLACEHOLDER": "Select status",
+ "ACTIVE": "Policy is active",
+ "INACTIVE": "Policy is inactive"
+ },
+ "ASSIGNMENT_ORDER": {
+ "LABEL": "Assignment order",
+ "ROUND_ROBIN": {
+ "LABEL": "Round robin",
+ "DESCRIPTION": "Assign conversations evenly among agents."
+ },
+ "BALANCED": {
+ "LABEL": "Balanced",
+ "DESCRIPTION": "Assign conversations based on available capacity."
+ }
+ },
+ "ASSIGNMENT_PRIORITY": {
+ "LABEL": "Assignment priority",
+ "EARLIEST_CREATED": {
+ "LABEL": "Earliest created",
+ "DESCRIPTION": "The conversation that was created first gets assigned first."
+ },
+ "LONGEST_WAITING": {
+ "LABEL": "Longest waiting",
+ "DESCRIPTION": "The conversation waiting the longest gets assigned first."
+ }
+ },
+ "FAIR_DISTRIBUTION": {
+ "LABEL": "Fair distribution policy",
+ "DESCRIPTION": "Set the maximum number of conversations that can be assigned per agent within a time window to avoid overloading any one agent. This required field defaults to 100 conversations per hour.",
+ "INPUT_MAX": "Assign max",
+ "DURATION": "Conversations per agent in every"
+ },
+ "INBOXES": {
+ "LABEL": "Added inboxes",
+ "DESCRIPTION": "Add inboxes for which this policy will be applicable.",
+ "ADD_BUTTON": "Add inbox",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select inboxes to add",
+ "ADD_BUTTON": "Dodaj"
+ },
+ "EMPTY_STATE": "No inboxes added to this policy, add an inbox to get started",
+ "API": {
+ "SUCCESS_MESSAGE": "Inbox successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Assignment policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete assignment policy"
+ }
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent capacity",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "POPOVER": "Added agents",
+ "EDIT": "Uredi"
+ },
+ "NO_RECORDS_FOUND": "No agent capacity policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create agent capacity policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy created successfully",
+ "ERROR_MESSAGE": "Failed to create agent capacity policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit agent capacity policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_AGENT_DIALOG": {
+ "TITLE": "Add agent",
+ "DESCRIPTION": "{agentName} is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Otkaži"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update agent capacity policy"
+ },
+ "AGENT_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Agent added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Agent removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove agent from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Opis:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "INBOX_CAPACITY_LIMIT": {
+ "LABEL": "Inbox capacity limits",
+ "ADD_BUTTON": "Add inbox",
+ "FIELD": {
+ "SELECT_INBOX": "Select inbox",
+ "MAX_CONVERSATIONS": "Max conversations",
+ "SET_LIMIT": "Set limit"
+ },
+ "EMPTY_STATE": "No inbox limit set"
+ },
+ "EXCLUSION_RULES": {
+ "LABEL": "Exclusion rules",
+ "DESCRIPTION": "Conversations that satisfy the following conditions would not count towards agent capacity",
+ "TAGS": {
+ "LABEL": "Exclude conversations tagged with specific labels",
+ "ADD_TAG": "add tag",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select tags to add"
+ },
+ "EMPTY_STATE": "No tags added to this policy."
+ },
+ "DURATION": {
+ "LABEL": "Exclude conversations older than a specified duration",
+ "PLACEHOLDER": "Set time"
+ }
+ },
+ "USERS": {
+ "LABEL": "Assigned agents",
+ "DESCRIPTION": "Add agents for which this policy will be applicable.",
+ "ADD_BUTTON": "Add agent",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select agents to add",
+ "ADD_BUTTON": "Dodaj"
+ },
+ "EMPTY_STATE": "No agents added",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Agent capacity policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete agent capacity policy"
+ }
+ },
+ "DELETE_POLICY": {
+ "TITLE": "Delete policy",
+ "DESCRIPTION": "Are you sure you want to delete this policy? This action cannot be undone.",
+ "CONFIRM_BUTTON_LABEL": "Izbriši",
+ "CANCEL_BUTTON_LABEL": "Otkaži"
+ }
}
}
diff --git a/app/javascript/dashboard/i18n/locale/sr/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/sr/whatsappTemplates.json
index a3cf57d23..71c6071f4 100644
--- a/app/javascript/dashboard/i18n/locale/sr/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/sr/whatsappTemplates.json
@@ -40,6 +40,7 @@
"BUTTON_LABEL": "Button {index}",
"COUPON_CODE": "Enter coupon code (max 15 chars)",
"MEDIA_URL_LABEL": "Enter {type} URL",
+ "DOCUMENT_NAME_PLACEHOLDER": "Enter document filename (e.g., Invoice_2025.pdf)",
"BUTTON_PARAMETER": "Enter button parameter"
}
}
diff --git a/app/javascript/dashboard/i18n/locale/sv/automation.json b/app/javascript/dashboard/i18n/locale/sv/automation.json
index b21bfe262..0194a66d2 100644
--- a/app/javascript/dashboard/i18n/locale/sv/automation.json
+++ b/app/javascript/dashboard/i18n/locale/sv/automation.json
@@ -177,7 +177,8 @@
"REFERER_LINK": "Referrer Link",
"ASSIGNEE_NAME": "Assignee",
"TEAM_NAME": "Team",
- "PRIORITY": "Priority"
+ "PRIORITY": "Priority",
+ "LABELS": "Etiketter"
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/sv/contact.json b/app/javascript/dashboard/i18n/locale/sv/contact.json
index 7320ebb32..458efb142 100644
--- a/app/javascript/dashboard/i18n/locale/sv/contact.json
+++ b/app/javascript/dashboard/i18n/locale/sv/contact.json
@@ -610,6 +610,15 @@
"SEND_MESSAGE": "Skicka meddelande"
}
},
+ "TWILIO_OPTIONS": {
+ "LABEL": "Select template",
+ "SEARCH_PLACEHOLDER": "Search templates",
+ "EMPTY_STATE": "No templates found",
+ "TEMPLATE_PARSER": {
+ "BACK": "Go back",
+ "SEND_MESSAGE": "Skicka meddelande"
+ }
+ },
"ACTION_BUTTONS": {
"DISCARD": "Discard",
"SEND": "Send ({keyCode})"
diff --git a/app/javascript/dashboard/i18n/locale/sv/contactFilters.json b/app/javascript/dashboard/i18n/locale/sv/contactFilters.json
index 7c749082e..c864d6e45 100644
--- a/app/javascript/dashboard/i18n/locale/sv/contactFilters.json
+++ b/app/javascript/dashboard/i18n/locale/sv/contactFilters.json
@@ -48,7 +48,8 @@
"CREATED_AT": "Created At",
"LAST_ACTIVITY": "Senaste aktivitet",
"REFERER_LINK": "Referrer link",
- "BLOCKED": "Blockerad"
+ "BLOCKED": "Blockerad",
+ "LABELS": "Etiketter"
},
"GROUPS": {
"STANDARD_FILTERS": "Standard Filters",
diff --git a/app/javascript/dashboard/i18n/locale/sv/contentTemplates.json b/app/javascript/dashboard/i18n/locale/sv/contentTemplates.json
new file mode 100644
index 000000000..6ed3b34f6
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/sv/contentTemplates.json
@@ -0,0 +1,51 @@
+{
+ "CONTENT_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Twilio Templates",
+ "SUBTITLE": "Select the Twilio template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Search Templates",
+ "NO_TEMPLATES_FOUND": "No templates found for",
+ "NO_CONTENT": "Inget innehåll",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Category",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No Twilio templates available. Click refresh to sync templates from Twilio.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Language",
+ "TEMPLATE_BODY": "Template Body",
+ "CATEGORY": "Category"
+ },
+ "TYPES": {
+ "MEDIA": "Media",
+ "QUICK_REPLY": "Quick Reply",
+ "TEXT": "Text"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variables",
+ "LANGUAGE": "Language",
+ "CATEGORY": "Category",
+ "VARIABLE_PLACEHOLDER": "Enter {variable} value",
+ "GO_BACK_LABEL": "Go Back",
+ "SEND_MESSAGE_LABEL": "Send Message",
+ "FORM_ERROR_MESSAGE": "Please fill all variables before sending",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "MEDIA_URL_LABEL": "Enter full media URL",
+ "MEDIA_URL_PLACEHOLDER": "https://example.com/image.jpg"
+ },
+ "FORM": {
+ "BACK_BUTTON": "Tillbaka",
+ "SEND_MESSAGE_BUTTON": "Send Message"
+ }
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/sv/conversation.json b/app/javascript/dashboard/i18n/locale/sv/conversation.json
index e10a0422f..a919575ce 100644
--- a/app/javascript/dashboard/i18n/locale/sv/conversation.json
+++ b/app/javascript/dashboard/i18n/locale/sv/conversation.json
@@ -35,6 +35,11 @@
"API_HOURS_WINDOW": "You can only reply to this conversation within {hours} hours",
"NOT_ASSIGNED_TO_YOU": "Den här konversationen är inte tilldelad dig. Vill du tilldela dig själv den här konversationen?",
"ASSIGN_TO_ME": "Tilldela mig",
+ "BOT_HANDOFF_MESSAGE": "You are responding to a conversation which is currently handled by an assistant or a bot.",
+ "BOT_HANDOFF_ACTION": "Mark open and assign to you",
+ "BOT_HANDOFF_REOPEN_ACTION": "Mark conversation open",
+ "BOT_HANDOFF_SUCCESS": "Conversation has been handed over to you",
+ "BOT_HANDOFF_ERROR": "Failed to take over the conversation. Please try again.",
"TWILIO_WHATSAPP_CAN_REPLY": "Du kan bara svara på denna konversation med ett mallmeddelande på grund av",
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "24 timmars meddelandebegränsning",
"OLD_INSTAGRAM_INBOX_REPLY_BANNER": "This Instagram account was migrated to the new Instagram channel inbox. All new messages will show up there. You won’t be able to send messages from this conversation anymore.",
@@ -66,6 +71,17 @@
"SHOW_LABELS": "Visa etiketter",
"HIDE_LABELS": "Dölj etiketter"
},
+ "VOICE_CALL": {
+ "INCOMING_CALL": "Incoming call",
+ "OUTGOING_CALL": "Outgoing call",
+ "CALL_IN_PROGRESS": "Call in progress",
+ "NO_ANSWER": "No answer",
+ "MISSED_CALL": "Missed call",
+ "CALL_ENDED": "Call ended",
+ "NOT_ANSWERED_YET": "Not answered yet",
+ "THEY_ANSWERED": "They answered",
+ "YOU_ANSWERED": "You answered"
+ },
"HEADER": {
"RESOLVE_ACTION": "Lös",
"REOPEN_ACTION": "Återöppna",
diff --git a/app/javascript/dashboard/i18n/locale/sv/helpCenter.json b/app/javascript/dashboard/i18n/locale/sv/helpCenter.json
index 518ad737e..f3251489c 100644
--- a/app/javascript/dashboard/i18n/locale/sv/helpCenter.json
+++ b/app/javascript/dashboard/i18n/locale/sv/helpCenter.json
@@ -741,7 +741,8 @@
"LIVE_CHAT_WIDGET": {
"LABEL": "Live chat widget",
"PLACEHOLDER": "Select live chat widget",
- "HELP_TEXT": "Select a live chat widget that will appear on your help center"
+ "HELP_TEXT": "Select a live chat widget that will appear on your help center",
+ "NONE_OPTION": "No widget"
},
"BRAND_COLOR": {
"LABEL": "Brand color"
@@ -807,6 +808,58 @@
"ERROR_MESSAGE": "Unable to update portal"
}
}
+ },
+ "PDF_UPLOAD": {
+ "TITLE": "Upload PDF Document",
+ "DESCRIPTION": "Upload a PDF document to automatically generate FAQs using AI",
+ "DRAG_DROP_TEXT": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "ADDITIONAL_CONTEXT_LABEL": "Additional Context (Optional)",
+ "ADDITIONAL_CONTEXT_PLACEHOLDER": "Provide any additional context or instructions for FAQ generation...",
+ "UPLOADING": "Laddar upp...",
+ "UPLOAD": "Upload & Process",
+ "CANCEL": "Avbryt",
+ "ERROR_INVALID_TYPE": "Please select a valid PDF file",
+ "ERROR_FILE_TOO_LARGE": "File size must be less than 512MB",
+ "ERROR_UPLOAD_FAILED": "Failed to upload PDF. Please try again."
+ },
+ "PDF_DOCUMENTS": {
+ "TITLE": "PDF Documents",
+ "DESCRIPTION": "Manage uploaded PDF documents and generate FAQs from them",
+ "UPLOAD_PDF": "Upload PDF",
+ "UPLOAD_FIRST_PDF": "Upload your first PDF",
+ "UPLOADED_BY": "Uploaded by",
+ "GENERATE_FAQS": "Generate FAQs",
+ "GENERATING": "Generating...",
+ "CONFIRM_DELETE": "Are you sure you want to delete {filename}?",
+ "EMPTY_STATE": {
+ "TITLE": "No PDF documents yet",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQs using AI"
+ },
+ "STATUS": {
+ "UPLOADED": "Ready",
+ "PROCESSING": "Processing",
+ "PROCESSED": "Completed",
+ "FAILED": "Failed"
+ }
+ },
+ "CONTENT_GENERATION": {
+ "TITLE": "Content Generation",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQ content using AI",
+ "UPLOAD_TITLE": "Upload PDF Document",
+ "DRAG_DROP": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "UPLOADING": "Processing document...",
+ "UPLOAD_SUCCESS": "Document processed successfully!",
+ "UPLOAD_ERROR": "Failed to upload document. Please try again.",
+ "INVALID_FILE_TYPE": "Please select a valid PDF file",
+ "FILE_TOO_LARGE": "File size must be less than 512MB",
+ "GENERATED_CONTENT": "Generated FAQ Content",
+ "PUBLISH_SELECTED": "Publish Selected",
+ "PUBLISHING": "Publishing...",
+ "FROM_DOCUMENT": "From document",
+ "NO_CONTENT": "No generated content available. Upload a PDF document to get started.",
+ "LOADING": "Loading generated content..."
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/sv/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/sv/inboxMgmt.json
index 7f0708845..ea0d514f5 100644
--- a/app/javascript/dashboard/i18n/locale/sv/inboxMgmt.json
+++ b/app/javascript/dashboard/i18n/locale/sv/inboxMgmt.json
@@ -272,8 +272,8 @@
},
"SUBMIT_BUTTON": "Skapa WhatsApp-kanal",
"EMBEDDED_SIGNUP": {
- "TITLE": "Quick Setup with Meta",
- "DESC": "You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
+ "TITLE": "Quick setup with Meta",
+ "DESC": "Use the WhatsApp Embedded Signup flow to quickly connect new numbers. You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
"BENEFITS": {
"TITLE": "Benefits of Embedded Signup:",
"EASY_SETUP": "No manual configuration required",
@@ -281,9 +281,8 @@
"AUTO_CONFIG": "Automatic webhook and phone number configuration"
},
"LEARN_MORE": {
- "TEXT": "To learn more about integrated signup, pricing, and limitations, visit",
- "LINK_TEXT": "this link.",
- "LINK_URL": "https://developers.facebook.com/docs/whatsapp/embedded-signup/custom-flows/onboarding-business-app-users#limitations"
+ "TEXT": "To learn more about integrated signup, pricing, and limitations, visit {link}.",
+ "LINK_TEXT": "this link"
},
"SUBMIT_BUTTON": "Connect with WhatsApp Business",
"AUTH_PROCESSING": "Authenticating with Meta",
@@ -296,7 +295,9 @@
"INVALID_BUSINESS_DATA": "Invalid business data received from Facebook. Please try again.",
"SIGNUP_ERROR": "Signup error occurred",
"AUTH_NOT_COMPLETED": "Authentication not completed. Please restart the process.",
- "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured"
+ "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured",
+ "MANUAL_FALLBACK": "If your number is already connected to the WhatsApp Business Platform (API), or if you’re a tech provider onboarding your own number, please use the {link} flow",
+ "MANUAL_LINK_TEXT": "manual setup flow"
},
"API": {
"ERROR_MESSAGE": "Vi kunde inte spara WhatsApp-kanalen"
@@ -424,7 +425,51 @@
},
"AUTH": {
"TITLE": "Välj en kanal",
- "DESC": "Chatwoot supports live-chat widgets, Facebook Messenger, Twitter profiles, WhatsApp, Emails, etc., as channels. If you want to build a custom channel, you can create it using the API channel. To get started, choose one of the channels below."
+ "DESC": "Chatwoot supports live-chat widgets, Facebook Messenger, Twitter profiles, WhatsApp, Emails, etc., as channels. If you want to build a custom channel, you can create it using the API channel. To get started, choose one of the channels below.",
+ "TITLE_NEXT": "Complete the setup",
+ "TITLE_FINISH": "Voilà!",
+ "CHANNEL": {
+ "WEBSITE": {
+ "TITLE": "Hemsida",
+ "DESCRIPTION": "Create a live-chat widget"
+ },
+ "FACEBOOK": {
+ "TITLE": "Facebook",
+ "DESCRIPTION": "Connect your Facebook page"
+ },
+ "WHATSAPP": {
+ "TITLE": "WhatsApp",
+ "DESCRIPTION": "Support your customers on WhatsApp"
+ },
+ "EMAIL": {
+ "TITLE": "E-post",
+ "DESCRIPTION": "Connect with Gmail, Outlook, or other providers"
+ },
+ "SMS": {
+ "TITLE": "SMS",
+ "DESCRIPTION": "Integrate SMS channel with Twilio or bandwidth"
+ },
+ "API": {
+ "TITLE": "API",
+ "DESCRIPTION": "Make a custom channel using our API"
+ },
+ "TELEGRAM": {
+ "TITLE": "Telegram",
+ "DESCRIPTION": "Configure Telegram channel using Bot token"
+ },
+ "LINE": {
+ "TITLE": "Line",
+ "DESCRIPTION": "Integrate your Line channel"
+ },
+ "INSTAGRAM": {
+ "TITLE": "Instagram",
+ "DESCRIPTION": "Connect your instagram account"
+ },
+ "VOICE": {
+ "TITLE": "Voice",
+ "DESCRIPTION": "Integrate with Twilio Voice"
+ }
+ }
},
"AGENTS": {
"TITLE": "Agenter",
@@ -478,7 +523,10 @@
"MESSAGE": "Du kan nu interagera med dina kunder genom din nya kanal. Supporta glatt",
"BUTTON_TEXT": "Ta mig dit",
"MORE_SETTINGS": "Fler inställningar",
- "WEBSITE_SUCCESS": "Du har skapat en webbplatskanal. Kopiera koden som visas nedan och klistra in den på din webbplats. Nästa gång en kund använder livechatten visas konversationen automatiskt i din inkorg."
+ "WEBSITE_SUCCESS": "Du har skapat en webbplatskanal. Kopiera koden som visas nedan och klistra in den på din webbplats. Nästa gång en kund använder livechatten visas konversationen automatiskt i din inkorg.",
+ "WHATSAPP_QR_INSTRUCTION": "Scan the QR code above to quickly test your WhatsApp inbox",
+ "MESSENGER_QR_INSTRUCTION": "Scan the QR code above to quickly test your Facebook Messenger inbox",
+ "TELEGRAM_QR_INSTRUCTION": "Scan the QR code above to quickly test your Telegram inbox"
},
"REAUTH": "Återauktorisera",
"VIEW": "Visa",
@@ -868,9 +916,18 @@
"SCRIPT_SETTINGS": "\n window.chatwootSettings = {options};"
},
"EMAIL_PROVIDERS": {
- "MICROSOFT": "Microsoft",
- "GOOGLE": "Google",
- "OTHER_PROVIDERS": "Other Providers"
+ "MICROSOFT": {
+ "TITLE": "Microsoft",
+ "DESCRIPTION": "Connect with Microsoft"
+ },
+ "GOOGLE": {
+ "TITLE": "Google",
+ "DESCRIPTION": "Connect with Google"
+ },
+ "OTHER_PROVIDERS": {
+ "TITLE": "Other Providers",
+ "DESCRIPTION": "Connect with Other Providers"
+ }
},
"CHANNELS": {
"MESSENGER": "Messenger",
diff --git a/app/javascript/dashboard/i18n/locale/sv/integrations.json b/app/javascript/dashboard/i18n/locale/sv/integrations.json
index ce27888e4..c9eef1a7f 100644
--- a/app/javascript/dashboard/i18n/locale/sv/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/sv/integrations.json
@@ -704,11 +704,28 @@
"ERROR_MESSAGE": "There was an error creating the document, please try again."
},
"FORM": {
+ "TYPE": {
+ "LABEL": "Document Type",
+ "URL": "URL",
+ "PDF": "PDF File"
+ },
"URL": {
"LABEL": "URL",
"PLACEHOLDER": "Enter the URL of the document",
"ERROR": "Please provide a valid URL for the document"
},
+ "PDF_FILE": {
+ "LABEL": "PDF File",
+ "CHOOSE_FILE": "Choose PDF file",
+ "ERROR": "Please select a PDF file",
+ "HELP_TEXT": "Maximum file size: 10MB",
+ "INVALID_TYPE": "Please select a valid PDF file",
+ "TOO_LARGE": "File size exceeds 10MB limit"
+ },
+ "NAME": {
+ "LABEL": "Document Name (Optional)",
+ "PLACEHOLDER": "Enter a name for the document"
+ },
"ASSISTANT": {
"LABEL": "Assistant",
"PLACEHOLDER": "Select the assistant",
@@ -744,6 +761,7 @@
"SELECTED": "{count} selected",
"SELECT_ALL": "Select all ({count})",
"UNSELECT_ALL": "Unselect all ({count})",
+ "SEARCH_PLACEHOLDER": "Search FAQs...",
"BULK_APPROVE_BUTTON": "Approve",
"BULK_DELETE_BUTTON": "Radera",
"BULK_APPROVE": {
diff --git a/app/javascript/dashboard/i18n/locale/sv/mfa.json b/app/javascript/dashboard/i18n/locale/sv/mfa.json
new file mode 100644
index 000000000..6e11e3fe2
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/sv/mfa.json
@@ -0,0 +1,106 @@
+{
+ "MFA_SETTINGS": {
+ "TITLE": "Two-Factor Authentication",
+ "SUBTITLE": "Secure your account with TOTP-based authentication",
+ "DESCRIPTION": "Add an extra layer of security to your account using a time-based one-time password (TOTP)",
+ "STATUS_TITLE": "Authentication Status",
+ "STATUS_DESCRIPTION": "Manage your two-factor authentication settings and backup recovery codes",
+ "ENABLED": "Aktiverad",
+ "DISABLED": "Inaktiverad",
+ "STATUS_ENABLED": "Two-factor authentication is active",
+ "STATUS_ENABLED_DESC": "Your account is protected with an additional layer of security",
+ "ENABLE_BUTTON": "Enable Two-Factor Authentication",
+ "ENHANCE_SECURITY": "Enhance Your Account Security",
+ "ENHANCE_SECURITY_DESC": "Two-factor authentication adds an extra layer of security by requiring a verification code from your authenticator app in addition to your password.",
+ "SETUP": {
+ "STEP_NUMBER_1": "1",
+ "STEP_NUMBER_2": "2",
+ "STEP1_TITLE": "Scan QR Code with Your Authenticator App",
+ "STEP1_DESCRIPTION": "Use Google Authenticator, Authy, or any TOTP-compatible app",
+ "LOADING_QR": "Loading...",
+ "MANUAL_ENTRY": "Can't scan? Enter code manually",
+ "SECRET_KEY": "Secret Key",
+ "COPY": "Kopiera",
+ "ENTER_CODE": "Enter the 6-digit code from your authenticator app",
+ "ENTER_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify & Continue",
+ "CANCEL": "Avbryt",
+ "ERROR_STARTING": "MFA not enabled. Please contact administrator.",
+ "INVALID_CODE": "Invalid verification code",
+ "SECRET_COPIED": "Secret key copied to clipboard",
+ "SUCCESS": "Two-factor authentication has been enabled successfully"
+ },
+ "BACKUP": {
+ "TITLE": "Save Your Backup Codes",
+ "DESCRIPTION": "Keep these codes safe. Each can be used once if you lose access to your authenticator",
+ "IMPORTANT": "Important:",
+ "IMPORTANT_NOTE": " Save these codes in a secure location. You won't be able to see them again.",
+ "DOWNLOAD": "Hämta",
+ "COPY_ALL": "Copy All",
+ "CONFIRM": "I have saved my backup codes in a secure location and understand that I won't be able to see them again",
+ "COMPLETE_SETUP": "Complete Setup",
+ "CODES_COPIED": "Backup codes copied to clipboard"
+ },
+ "MANAGEMENT": {
+ "BACKUP_CODES": "Backup Codes",
+ "BACKUP_CODES_DESC": "Generate new codes if you've lost or used your existing ones",
+ "REGENERATE": "Regenerate Backup Codes",
+ "DISABLE_MFA": "Disable 2FA",
+ "DISABLE_MFA_DESC": "Remove two-factor authentication from your account",
+ "DISABLE_BUTTON": "Disable Two-Factor Authentication"
+ },
+ "DISABLE": {
+ "TITLE": "Disable Two-Factor Authentication",
+ "DESCRIPTION": "You'll need to enter your password and a verification code to disable two-factor authentication.",
+ "PASSWORD": "Lösenord",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Disable 2FA",
+ "CANCEL": "Avbryt",
+ "SUCCESS": "Two-factor authentication has been disabled",
+ "ERROR": "Failed to disable MFA. Please check your credentials."
+ },
+ "REGENERATE": {
+ "TITLE": "Regenerate Backup Codes",
+ "DESCRIPTION": "This will invalidate your existing backup codes and generate new ones. Enter your verification code to continue.",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Generate New Codes",
+ "CANCEL": "Avbryt",
+ "NEW_CODES_TITLE": "New Backup Codes Generated",
+ "NEW_CODES_DESC": "Your old backup codes have been invalidated. Save these new codes in a secure location.",
+ "CODES_IMPORTANT": "Important:",
+ "CODES_IMPORTANT_NOTE": " Each code can only be used once. Save them before closing this window.",
+ "DOWNLOAD_CODES": "Download Codes",
+ "COPY_ALL_CODES": "Copy All Codes",
+ "CODES_SAVED": "I've Saved My Codes",
+ "SUCCESS": "New backup codes have been generated",
+ "ERROR": "Failed to regenerate backup codes"
+ }
+ },
+ "MFA_VERIFICATION": {
+ "TITLE": "Two-Factor Authentication",
+ "DESCRIPTION": "Enter your verification code to continue",
+ "AUTHENTICATOR_APP": "Authenticator App",
+ "BACKUP_CODE": "Backup Code",
+ "ENTER_OTP_CODE": "Enter 6-digit code from your authenticator app",
+ "ENTER_BACKUP_CODE": "Enter one of your backup codes",
+ "BACKUP_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify",
+ "TRY_ANOTHER_METHOD": "Try another verification method",
+ "CANCEL_LOGIN": "Cancel and return to login",
+ "HELP_TEXT": "Having trouble signing in?",
+ "LEARN_MORE": "Learn more about 2FA",
+ "HELP_MODAL": {
+ "TITLE": "Two-Factor Authentication Help",
+ "AUTHENTICATOR_TITLE": "Using an Authenticator App",
+ "AUTHENTICATOR_DESC": "Open your authenticator app (Google Authenticator, Authy, etc.) and enter the 6-digit code shown for your account.",
+ "BACKUP_TITLE": "Using a Backup Code",
+ "BACKUP_DESC": "If you don't have access to your authenticator app, you can use one of the backup codes you saved when setting up 2FA. Each code can only be used once.",
+ "CONTACT_TITLE": "Need More Help?",
+ "CONTACT_DESC_CLOUD": "If you've lost access to both your authenticator app and backup codes, please reach out to Chatwoot support for assistance.",
+ "CONTACT_DESC_SELF_HOSTED": "If you've lost access to both your authenticator app and backup codes, please contact your administrator for assistance."
+ },
+ "VERIFICATION_FAILED": "Verification failed. Please try again."
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/sv/settings.json b/app/javascript/dashboard/i18n/locale/sv/settings.json
index e80207056..1041ef3d9 100644
--- a/app/javascript/dashboard/i18n/locale/sv/settings.json
+++ b/app/javascript/dashboard/i18n/locale/sv/settings.json
@@ -51,6 +51,13 @@
"LARGER": "Larger",
"EXTRA_LARGE": "Extra Large"
}
+ },
+ "LANGUAGE": {
+ "TITLE": "Preferred Language",
+ "NOTE": "Choose the language you want to use.",
+ "UPDATE_SUCCESS": "Your Language settings have been updated successfully",
+ "UPDATE_ERROR": "There is an error while updating the language settings, please try again",
+ "USE_ACCOUNT_DEFAULT": "Use account default"
}
},
"MESSAGE_SIGNATURE_SECTION": {
@@ -73,6 +80,11 @@
"NOTE": "Uppdatering av ditt lösenord skulle återställa dina inloggningar på flera enheter.",
"BTN_TEXT": "Change password"
},
+ "SECURITY_SECTION": {
+ "TITLE": "Security",
+ "NOTE": "Manage additional security features for your account.",
+ "MFA_BUTTON": "Manage Two-Factor Authentication"
+ },
"ACCESS_TOKEN": {
"TITLE": "Åtkomsttoken",
"NOTE": "Denna token kan användas om du bygger en API-baserad integration",
@@ -330,6 +342,7 @@
"REPORTS_LABEL": "Etiketter",
"REPORTS_INBOX": "Inkorg",
"REPORTS_TEAM": "Team",
+ "AGENT_ASSIGNMENT": "Agent Assignment",
"SET_AVAILABILITY_TITLE": "Set yourself as",
"SET_YOUR_AVAILABILITY": "Ställ in din tillgänglighet",
"SLA": "SLA",
@@ -350,7 +363,8 @@
"INFO_TEXT": "Let the system automatically mark you offline when you aren't using the app or dashboard.",
"INFO_SHORT": "Automatically mark offline when you aren't using the app."
},
- "DOCS": "Read docs"
+ "DOCS": "Read docs",
+ "SECURITY": "Security"
},
"BILLING_SETTINGS": {
"TITLE": "Billing",
@@ -382,6 +396,77 @@
},
"NO_BILLING_USER": "Your billing account is being configured. Please refresh the page and try again."
},
+ "SECURITY_SETTINGS": {
+ "TITLE": "Security",
+ "DESCRIPTION": "Manage your account security settings.",
+ "LINK_TEXT": "Learn more about SAML SSO",
+ "SAML": {
+ "TITLE": "SAML SSO",
+ "NOTE": "Configure SAML single sign-on for your account. Users will authenticate through your identity provider instead of using email/password.",
+ "ACS_URL": {
+ "LABEL": "ACS URL",
+ "TOOLTIP": "Assertion Consumer Service URL - Configure this URL in your IdP as the destination for SAML responses"
+ },
+ "SSO_URL": {
+ "LABEL": "SSO URL",
+ "HELP": "The URL where SAML authentication requests will be sent",
+ "PLACEHOLDER": "https://your-idp.com/saml/sso"
+ },
+ "CERTIFICATE": {
+ "LABEL": "Signing certificate in PEM format",
+ "HELP": "The public certificate from your identity provider used to verify SAML responses",
+ "PLACEHOLDER": "-----BEGIN CERTIFICATE-----\nMIIC..."
+ },
+ "FINGERPRINT": {
+ "LABEL": "Fingerprint",
+ "TOOLTIP": "SHA-1 fingerprint of the certificate - Use this to verify the certificate in your IdP configuration"
+ },
+ "COPY_SUCCESS": "Koden har kopierats till urklipp",
+ "SP_ENTITY_ID": {
+ "LABEL": "SP Entity ID",
+ "HELP": "Unique identifier for this application as a service provider (auto-generated).",
+ "TOOLTIP": "Unique identifier for Chatwoot as the Service Provider - Configure this in your IdP settings"
+ },
+ "IDP_ENTITY_ID": {
+ "LABEL": "Identity Provider Entity ID",
+ "HELP": "Unique identifier for your identity provider (usually found in IdP configuration)",
+ "PLACEHOLDER": "https://your-idp.com/saml"
+ },
+ "UPDATE_BUTTON": "Update SAML Settings",
+ "API": {
+ "SUCCESS": "SAML settings updated successfully",
+ "ERROR": "Failed to update SAML settings",
+ "ERROR_LOADING": "Failed to load SAML settings",
+ "DISABLED": "SAML settings disabled successfully"
+ },
+ "VALIDATION": {
+ "REQUIRED_FIELDS": "SSO URL, Identity Provider Entity ID, and Certificate are required fields",
+ "SSO_URL_ERROR": "Please enter a valid SSO URL",
+ "CERTIFICATE_ERROR": "Certificate is required",
+ "IDP_ENTITY_ID_ERROR": "Identity Provider Entity ID is required"
+ },
+ "ENTERPRISE_PAYWALL": {
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade to an Enterprise plan to access SAML single sign-on and other advanced security features.",
+ "ASK_ADMIN": "Please reach out to your administrator for the upgrade."
+ },
+ "PAYWALL": {
+ "TITLE": "Upgrade to enable SAML SSO",
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade your plan to get access to SAML single sign-on and other advanced features.",
+ "UPGRADE_NOW": "Upgrade now",
+ "CANCEL_ANYTIME": "You can change or cancel your plan anytime"
+ },
+ "ATTRIBUTE_MAPPING": {
+ "TITLE": "SAML Attribute Setup",
+ "DESCRIPTION": "The following attribute mappings must be configured in your identity provider"
+ },
+ "INFO_SECTION": {
+ "TITLE": "Service Provider Information",
+ "TOOLTIP": "Copy these values and configure them in your Identity Provider to establish the SAML connection"
+ }
+ }
+ },
"CREATE_ACCOUNT": {
"NO_ACCOUNT_WARNING": "Uh oh! We could not find any Chatwoot accounts. Please create a new account to continue.",
"NEW_ACCOUNT": "Nytt konto",
@@ -418,5 +503,255 @@
"SWITCH_TO_REPLY": "Switch to Reply",
"TOGGLE_SNOOZE_DROPDOWN": "Toggle snooze dropdown"
}
+ },
+ "ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent assignment",
+ "DESCRIPTION": "Define policies to effectively manage workload and route conversations based on the needs of inboxes and agents. Learn more here"
+ },
+ "ASSIGNMENT_POLICY": {
+ "TITLE": "Assignment policy",
+ "DESCRIPTION": "Manage how conversations get assigned in inboxes.",
+ "FEATURES": [
+ "Assign by conversations evenly or by available capacity",
+ "Add fair distribution rules to avoid overloading any agent",
+ "Add inboxes to a policy - one policy per inbox"
+ ]
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "TITLE": "Agent capacity policy",
+ "DESCRIPTION": "Manage workload for agents.",
+ "FEATURES": [
+ "Define maximum conversations per inbox",
+ "Create exceptions based on labels and time",
+ "Add agents to a policy - one policy per agent"
+ ]
+ }
+ },
+ "AGENT_ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Assignment policy",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "ORDER": "Order",
+ "PRIORITY": "Priority",
+ "ACTIVE": "Active",
+ "INACTIVE": "Inactive",
+ "POPOVER": "Added inboxes",
+ "EDIT": "Redigera"
+ },
+ "NO_RECORDS_FOUND": "No assignment policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create assignment policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy created successfully",
+ "ERROR_MESSAGE": "Failed to create assignment policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit assignment policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_INBOX_DIALOG": {
+ "TITLE": "Add inbox",
+ "DESCRIPTION": "{inboxName} inbox is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Avbryt"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update assignment policy"
+ },
+ "INBOX_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Inbox added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Inbox removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove inbox from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Beskrivning:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "STATUS": {
+ "LABEL": "Status:",
+ "PLACEHOLDER": "Select status",
+ "ACTIVE": "Policy is active",
+ "INACTIVE": "Policy is inactive"
+ },
+ "ASSIGNMENT_ORDER": {
+ "LABEL": "Assignment order",
+ "ROUND_ROBIN": {
+ "LABEL": "Round robin",
+ "DESCRIPTION": "Assign conversations evenly among agents."
+ },
+ "BALANCED": {
+ "LABEL": "Balanced",
+ "DESCRIPTION": "Assign conversations based on available capacity."
+ }
+ },
+ "ASSIGNMENT_PRIORITY": {
+ "LABEL": "Assignment priority",
+ "EARLIEST_CREATED": {
+ "LABEL": "Earliest created",
+ "DESCRIPTION": "The conversation that was created first gets assigned first."
+ },
+ "LONGEST_WAITING": {
+ "LABEL": "Longest waiting",
+ "DESCRIPTION": "The conversation waiting the longest gets assigned first."
+ }
+ },
+ "FAIR_DISTRIBUTION": {
+ "LABEL": "Fair distribution policy",
+ "DESCRIPTION": "Set the maximum number of conversations that can be assigned per agent within a time window to avoid overloading any one agent. This required field defaults to 100 conversations per hour.",
+ "INPUT_MAX": "Assign max",
+ "DURATION": "Conversations per agent in every"
+ },
+ "INBOXES": {
+ "LABEL": "Added inboxes",
+ "DESCRIPTION": "Add inboxes for which this policy will be applicable.",
+ "ADD_BUTTON": "Add inbox",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select inboxes to add",
+ "ADD_BUTTON": "Add"
+ },
+ "EMPTY_STATE": "No inboxes added to this policy, add an inbox to get started",
+ "API": {
+ "SUCCESS_MESSAGE": "Inbox successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Assignment policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete assignment policy"
+ }
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent capacity",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "POPOVER": "Added agents",
+ "EDIT": "Redigera"
+ },
+ "NO_RECORDS_FOUND": "No agent capacity policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create agent capacity policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy created successfully",
+ "ERROR_MESSAGE": "Failed to create agent capacity policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit agent capacity policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_AGENT_DIALOG": {
+ "TITLE": "Add agent",
+ "DESCRIPTION": "{agentName} is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Avbryt"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update agent capacity policy"
+ },
+ "AGENT_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Agent added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Agent removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove agent from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Beskrivning:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "INBOX_CAPACITY_LIMIT": {
+ "LABEL": "Inbox capacity limits",
+ "ADD_BUTTON": "Add inbox",
+ "FIELD": {
+ "SELECT_INBOX": "Select inbox",
+ "MAX_CONVERSATIONS": "Max conversations",
+ "SET_LIMIT": "Set limit"
+ },
+ "EMPTY_STATE": "No inbox limit set"
+ },
+ "EXCLUSION_RULES": {
+ "LABEL": "Exclusion rules",
+ "DESCRIPTION": "Conversations that satisfy the following conditions would not count towards agent capacity",
+ "TAGS": {
+ "LABEL": "Exclude conversations tagged with specific labels",
+ "ADD_TAG": "add tag",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select tags to add"
+ },
+ "EMPTY_STATE": "No tags added to this policy."
+ },
+ "DURATION": {
+ "LABEL": "Exclude conversations older than a specified duration",
+ "PLACEHOLDER": "Set time"
+ }
+ },
+ "USERS": {
+ "LABEL": "Assigned agents",
+ "DESCRIPTION": "Add agents for which this policy will be applicable.",
+ "ADD_BUTTON": "Add agent",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select agents to add",
+ "ADD_BUTTON": "Add"
+ },
+ "EMPTY_STATE": "No agents added",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Agent capacity policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete agent capacity policy"
+ }
+ },
+ "DELETE_POLICY": {
+ "TITLE": "Delete policy",
+ "DESCRIPTION": "Are you sure you want to delete this policy? This action cannot be undone.",
+ "CONFIRM_BUTTON_LABEL": "Radera",
+ "CANCEL_BUTTON_LABEL": "Avbryt"
+ }
}
}
diff --git a/app/javascript/dashboard/i18n/locale/sv/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/sv/whatsappTemplates.json
index 5f53faaa8..cf28312dc 100644
--- a/app/javascript/dashboard/i18n/locale/sv/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/sv/whatsappTemplates.json
@@ -40,6 +40,7 @@
"BUTTON_LABEL": "Button {index}",
"COUPON_CODE": "Enter coupon code (max 15 chars)",
"MEDIA_URL_LABEL": "Enter {type} URL",
+ "DOCUMENT_NAME_PLACEHOLDER": "Enter document filename (e.g., Invoice_2025.pdf)",
"BUTTON_PARAMETER": "Enter button parameter"
}
}
diff --git a/app/javascript/dashboard/i18n/locale/ta/automation.json b/app/javascript/dashboard/i18n/locale/ta/automation.json
index ba7a725cd..db4270b90 100644
--- a/app/javascript/dashboard/i18n/locale/ta/automation.json
+++ b/app/javascript/dashboard/i18n/locale/ta/automation.json
@@ -177,7 +177,8 @@
"REFERER_LINK": "Referrer Link",
"ASSIGNEE_NAME": "Assignee",
"TEAM_NAME": "Team",
- "PRIORITY": "Priority"
+ "PRIORITY": "Priority",
+ "LABELS": "Labels"
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/ta/contact.json b/app/javascript/dashboard/i18n/locale/ta/contact.json
index bd64d9944..1e90090fc 100644
--- a/app/javascript/dashboard/i18n/locale/ta/contact.json
+++ b/app/javascript/dashboard/i18n/locale/ta/contact.json
@@ -610,6 +610,15 @@
"SEND_MESSAGE": "Send message"
}
},
+ "TWILIO_OPTIONS": {
+ "LABEL": "Select template",
+ "SEARCH_PLACEHOLDER": "Search templates",
+ "EMPTY_STATE": "No templates found",
+ "TEMPLATE_PARSER": {
+ "BACK": "Go back",
+ "SEND_MESSAGE": "Send message"
+ }
+ },
"ACTION_BUTTONS": {
"DISCARD": "Discard",
"SEND": "Send ({keyCode})"
diff --git a/app/javascript/dashboard/i18n/locale/ta/contactFilters.json b/app/javascript/dashboard/i18n/locale/ta/contactFilters.json
index 1cbd8246f..3bfa182a4 100644
--- a/app/javascript/dashboard/i18n/locale/ta/contactFilters.json
+++ b/app/javascript/dashboard/i18n/locale/ta/contactFilters.json
@@ -48,7 +48,8 @@
"CREATED_AT": "Created At",
"LAST_ACTIVITY": "Last Activity",
"REFERER_LINK": "Referrer link",
- "BLOCKED": "Blocked"
+ "BLOCKED": "Blocked",
+ "LABELS": "Labels"
},
"GROUPS": {
"STANDARD_FILTERS": "Standard Filters",
diff --git a/app/javascript/dashboard/i18n/locale/ta/contentTemplates.json b/app/javascript/dashboard/i18n/locale/ta/contentTemplates.json
new file mode 100644
index 000000000..a9b1d54c4
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/ta/contentTemplates.json
@@ -0,0 +1,51 @@
+{
+ "CONTENT_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Twilio Templates",
+ "SUBTITLE": "Select the Twilio template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Search Templates",
+ "NO_TEMPLATES_FOUND": "No templates found for",
+ "NO_CONTENT": "No content",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Category",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No Twilio templates available. Click refresh to sync templates from Twilio.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Language",
+ "TEMPLATE_BODY": "Template Body",
+ "CATEGORY": "Category"
+ },
+ "TYPES": {
+ "MEDIA": "Media",
+ "QUICK_REPLY": "Quick Reply",
+ "TEXT": "Text"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variables",
+ "LANGUAGE": "Language",
+ "CATEGORY": "Category",
+ "VARIABLE_PLACEHOLDER": "Enter {variable} value",
+ "GO_BACK_LABEL": "Go Back",
+ "SEND_MESSAGE_LABEL": "Send Message",
+ "FORM_ERROR_MESSAGE": "Please fill all variables before sending",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "MEDIA_URL_LABEL": "Enter full media URL",
+ "MEDIA_URL_PLACEHOLDER": "https://example.com/image.jpg"
+ },
+ "FORM": {
+ "BACK_BUTTON": "Back",
+ "SEND_MESSAGE_BUTTON": "Send Message"
+ }
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/ta/conversation.json b/app/javascript/dashboard/i18n/locale/ta/conversation.json
index 35fdfe6f3..0771e5063 100644
--- a/app/javascript/dashboard/i18n/locale/ta/conversation.json
+++ b/app/javascript/dashboard/i18n/locale/ta/conversation.json
@@ -35,6 +35,11 @@
"API_HOURS_WINDOW": "You can only reply to this conversation within {hours} hours",
"NOT_ASSIGNED_TO_YOU": "This conversation is not assigned to you. Would you like to assign this conversation to yourself?",
"ASSIGN_TO_ME": "Assign to me",
+ "BOT_HANDOFF_MESSAGE": "You are responding to a conversation which is currently handled by an assistant or a bot.",
+ "BOT_HANDOFF_ACTION": "Mark open and assign to you",
+ "BOT_HANDOFF_REOPEN_ACTION": "Mark conversation open",
+ "BOT_HANDOFF_SUCCESS": "Conversation has been handed over to you",
+ "BOT_HANDOFF_ERROR": "Failed to take over the conversation. Please try again.",
"TWILIO_WHATSAPP_CAN_REPLY": "You can only reply to this conversation using a template message due to",
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "24 hour message window restriction",
"OLD_INSTAGRAM_INBOX_REPLY_BANNER": "This Instagram account was migrated to the new Instagram channel inbox. All new messages will show up there. You won’t be able to send messages from this conversation anymore.",
@@ -66,6 +71,17 @@
"SHOW_LABELS": "Show labels",
"HIDE_LABELS": "Hide labels"
},
+ "VOICE_CALL": {
+ "INCOMING_CALL": "Incoming call",
+ "OUTGOING_CALL": "Outgoing call",
+ "CALL_IN_PROGRESS": "Call in progress",
+ "NO_ANSWER": "No answer",
+ "MISSED_CALL": "Missed call",
+ "CALL_ENDED": "Call ended",
+ "NOT_ANSWERED_YET": "Not answered yet",
+ "THEY_ANSWERED": "They answered",
+ "YOU_ANSWERED": "You answered"
+ },
"HEADER": {
"RESOLVE_ACTION": "தீர்",
"REOPEN_ACTION": "மீண்டும் திற",
diff --git a/app/javascript/dashboard/i18n/locale/ta/helpCenter.json b/app/javascript/dashboard/i18n/locale/ta/helpCenter.json
index c3dfbd38d..d56d7527a 100644
--- a/app/javascript/dashboard/i18n/locale/ta/helpCenter.json
+++ b/app/javascript/dashboard/i18n/locale/ta/helpCenter.json
@@ -741,7 +741,8 @@
"LIVE_CHAT_WIDGET": {
"LABEL": "Live chat widget",
"PLACEHOLDER": "Select live chat widget",
- "HELP_TEXT": "Select a live chat widget that will appear on your help center"
+ "HELP_TEXT": "Select a live chat widget that will appear on your help center",
+ "NONE_OPTION": "No widget"
},
"BRAND_COLOR": {
"LABEL": "Brand color"
@@ -807,6 +808,58 @@
"ERROR_MESSAGE": "Unable to update portal"
}
}
+ },
+ "PDF_UPLOAD": {
+ "TITLE": "Upload PDF Document",
+ "DESCRIPTION": "Upload a PDF document to automatically generate FAQs using AI",
+ "DRAG_DROP_TEXT": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "ADDITIONAL_CONTEXT_LABEL": "Additional Context (Optional)",
+ "ADDITIONAL_CONTEXT_PLACEHOLDER": "Provide any additional context or instructions for FAQ generation...",
+ "UPLOADING": "பதிவேறுகிறது...",
+ "UPLOAD": "Upload & Process",
+ "CANCEL": "ரத்துசெய்",
+ "ERROR_INVALID_TYPE": "Please select a valid PDF file",
+ "ERROR_FILE_TOO_LARGE": "File size must be less than 512MB",
+ "ERROR_UPLOAD_FAILED": "Failed to upload PDF. Please try again."
+ },
+ "PDF_DOCUMENTS": {
+ "TITLE": "PDF Documents",
+ "DESCRIPTION": "Manage uploaded PDF documents and generate FAQs from them",
+ "UPLOAD_PDF": "Upload PDF",
+ "UPLOAD_FIRST_PDF": "Upload your first PDF",
+ "UPLOADED_BY": "Uploaded by",
+ "GENERATE_FAQS": "Generate FAQs",
+ "GENERATING": "Generating...",
+ "CONFIRM_DELETE": "Are you sure you want to delete {filename}?",
+ "EMPTY_STATE": {
+ "TITLE": "No PDF documents yet",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQs using AI"
+ },
+ "STATUS": {
+ "UPLOADED": "Ready",
+ "PROCESSING": "Processing",
+ "PROCESSED": "Completed",
+ "FAILED": "Failed"
+ }
+ },
+ "CONTENT_GENERATION": {
+ "TITLE": "Content Generation",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQ content using AI",
+ "UPLOAD_TITLE": "Upload PDF Document",
+ "DRAG_DROP": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "UPLOADING": "Processing document...",
+ "UPLOAD_SUCCESS": "Document processed successfully!",
+ "UPLOAD_ERROR": "Failed to upload document. Please try again.",
+ "INVALID_FILE_TYPE": "Please select a valid PDF file",
+ "FILE_TOO_LARGE": "File size must be less than 512MB",
+ "GENERATED_CONTENT": "Generated FAQ Content",
+ "PUBLISH_SELECTED": "Publish Selected",
+ "PUBLISHING": "Publishing...",
+ "FROM_DOCUMENT": "From document",
+ "NO_CONTENT": "No generated content available. Upload a PDF document to get started.",
+ "LOADING": "Loading generated content..."
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/ta/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/ta/inboxMgmt.json
index 43a5c0f8f..3b6c4713b 100644
--- a/app/javascript/dashboard/i18n/locale/ta/inboxMgmt.json
+++ b/app/javascript/dashboard/i18n/locale/ta/inboxMgmt.json
@@ -272,8 +272,8 @@
},
"SUBMIT_BUTTON": "Create WhatsApp Channel",
"EMBEDDED_SIGNUP": {
- "TITLE": "Quick Setup with Meta",
- "DESC": "You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
+ "TITLE": "Quick setup with Meta",
+ "DESC": "Use the WhatsApp Embedded Signup flow to quickly connect new numbers. You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
"BENEFITS": {
"TITLE": "Benefits of Embedded Signup:",
"EASY_SETUP": "No manual configuration required",
@@ -281,9 +281,8 @@
"AUTO_CONFIG": "Automatic webhook and phone number configuration"
},
"LEARN_MORE": {
- "TEXT": "To learn more about integrated signup, pricing, and limitations, visit",
- "LINK_TEXT": "this link.",
- "LINK_URL": "https://developers.facebook.com/docs/whatsapp/embedded-signup/custom-flows/onboarding-business-app-users#limitations"
+ "TEXT": "To learn more about integrated signup, pricing, and limitations, visit {link}.",
+ "LINK_TEXT": "this link"
},
"SUBMIT_BUTTON": "Connect with WhatsApp Business",
"AUTH_PROCESSING": "Authenticating with Meta",
@@ -296,7 +295,9 @@
"INVALID_BUSINESS_DATA": "Invalid business data received from Facebook. Please try again.",
"SIGNUP_ERROR": "Signup error occurred",
"AUTH_NOT_COMPLETED": "Authentication not completed. Please restart the process.",
- "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured"
+ "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured",
+ "MANUAL_FALLBACK": "If your number is already connected to the WhatsApp Business Platform (API), or if you’re a tech provider onboarding your own number, please use the {link} flow",
+ "MANUAL_LINK_TEXT": "manual setup flow"
},
"API": {
"ERROR_MESSAGE": "We were not able to save the WhatsApp channel"
@@ -424,7 +425,51 @@
},
"AUTH": {
"TITLE": "Choose a channel",
- "DESC": "Chatwoot supports live-chat widgets, Facebook Messenger, Twitter profiles, WhatsApp, Emails, etc., as channels. If you want to build a custom channel, you can create it using the API channel. To get started, choose one of the channels below."
+ "DESC": "Chatwoot supports live-chat widgets, Facebook Messenger, Twitter profiles, WhatsApp, Emails, etc., as channels. If you want to build a custom channel, you can create it using the API channel. To get started, choose one of the channels below.",
+ "TITLE_NEXT": "Complete the setup",
+ "TITLE_FINISH": "Voilà!",
+ "CHANNEL": {
+ "WEBSITE": {
+ "TITLE": "Website",
+ "DESCRIPTION": "Create a live-chat widget"
+ },
+ "FACEBOOK": {
+ "TITLE": "Facebook",
+ "DESCRIPTION": "Connect your Facebook page"
+ },
+ "WHATSAPP": {
+ "TITLE": "WhatsApp",
+ "DESCRIPTION": "Support your customers on WhatsApp"
+ },
+ "EMAIL": {
+ "TITLE": "இமெயில்",
+ "DESCRIPTION": "Connect with Gmail, Outlook, or other providers"
+ },
+ "SMS": {
+ "TITLE": "SMS",
+ "DESCRIPTION": "Integrate SMS channel with Twilio or bandwidth"
+ },
+ "API": {
+ "TITLE": "API",
+ "DESCRIPTION": "Make a custom channel using our API"
+ },
+ "TELEGRAM": {
+ "TITLE": "Telegram",
+ "DESCRIPTION": "Configure Telegram channel using Bot token"
+ },
+ "LINE": {
+ "TITLE": "Line",
+ "DESCRIPTION": "Integrate your Line channel"
+ },
+ "INSTAGRAM": {
+ "TITLE": "Instagram",
+ "DESCRIPTION": "Connect your instagram account"
+ },
+ "VOICE": {
+ "TITLE": "Voice",
+ "DESCRIPTION": "Integrate with Twilio Voice"
+ }
+ }
},
"AGENTS": {
"TITLE": "ஏஜென்ட்கள்",
@@ -478,7 +523,10 @@
"MESSAGE": "You can now engage with your customers through your new Channel. Happy supporting",
"BUTTON_TEXT": "என்னை அங்கே அழைத்துச் செல்லுங்கள்",
"MORE_SETTINGS": "More settings",
- "WEBSITE_SUCCESS": "வலைத்தள சேனலை உருவாக்குவதை வெற்றிகரமாக முடித்துவிட்டீர்கள். கீழே காட்டப்பட்டுள்ள உங்கள் இணையதளத்தில் ஒட்டவும். அடுத்த முறை வாடிக்கையாளர் நேரடி சாட்டை பயன்படுத்தும்போது, உரையாடல் தானாகவே உங்கள் இன்பாக்ஸில் தோன்றும்."
+ "WEBSITE_SUCCESS": "வலைத்தள சேனலை உருவாக்குவதை வெற்றிகரமாக முடித்துவிட்டீர்கள். கீழே காட்டப்பட்டுள்ள உங்கள் இணையதளத்தில் ஒட்டவும். அடுத்த முறை வாடிக்கையாளர் நேரடி சாட்டை பயன்படுத்தும்போது, உரையாடல் தானாகவே உங்கள் இன்பாக்ஸில் தோன்றும்.",
+ "WHATSAPP_QR_INSTRUCTION": "Scan the QR code above to quickly test your WhatsApp inbox",
+ "MESSENGER_QR_INSTRUCTION": "Scan the QR code above to quickly test your Facebook Messenger inbox",
+ "TELEGRAM_QR_INSTRUCTION": "Scan the QR code above to quickly test your Telegram inbox"
},
"REAUTH": "மறு அங்கீகாரம்",
"VIEW": "காண்க",
@@ -868,9 +916,18 @@
"SCRIPT_SETTINGS": "\n window.chatwootSettings = {options};"
},
"EMAIL_PROVIDERS": {
- "MICROSOFT": "Microsoft",
- "GOOGLE": "Google",
- "OTHER_PROVIDERS": "Other Providers"
+ "MICROSOFT": {
+ "TITLE": "Microsoft",
+ "DESCRIPTION": "Connect with Microsoft"
+ },
+ "GOOGLE": {
+ "TITLE": "Google",
+ "DESCRIPTION": "Connect with Google"
+ },
+ "OTHER_PROVIDERS": {
+ "TITLE": "Other Providers",
+ "DESCRIPTION": "Connect with Other Providers"
+ }
},
"CHANNELS": {
"MESSENGER": "Messenger",
diff --git a/app/javascript/dashboard/i18n/locale/ta/integrations.json b/app/javascript/dashboard/i18n/locale/ta/integrations.json
index 30244bd5a..ada1a1f10 100644
--- a/app/javascript/dashboard/i18n/locale/ta/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/ta/integrations.json
@@ -704,11 +704,28 @@
"ERROR_MESSAGE": "There was an error creating the document, please try again."
},
"FORM": {
+ "TYPE": {
+ "LABEL": "Document Type",
+ "URL": "URL",
+ "PDF": "PDF File"
+ },
"URL": {
"LABEL": "URL",
"PLACEHOLDER": "Enter the URL of the document",
"ERROR": "Please provide a valid URL for the document"
},
+ "PDF_FILE": {
+ "LABEL": "PDF File",
+ "CHOOSE_FILE": "Choose PDF file",
+ "ERROR": "Please select a PDF file",
+ "HELP_TEXT": "Maximum file size: 10MB",
+ "INVALID_TYPE": "Please select a valid PDF file",
+ "TOO_LARGE": "File size exceeds 10MB limit"
+ },
+ "NAME": {
+ "LABEL": "Document Name (Optional)",
+ "PLACEHOLDER": "Enter a name for the document"
+ },
"ASSISTANT": {
"LABEL": "Assistant",
"PLACEHOLDER": "Select the assistant",
@@ -744,6 +761,7 @@
"SELECTED": "{count} selected",
"SELECT_ALL": "Select all ({count})",
"UNSELECT_ALL": "Unselect all ({count})",
+ "SEARCH_PLACEHOLDER": "Search FAQs...",
"BULK_APPROVE_BUTTON": "Approve",
"BULK_DELETE_BUTTON": "Delete",
"BULK_APPROVE": {
diff --git a/app/javascript/dashboard/i18n/locale/ta/mfa.json b/app/javascript/dashboard/i18n/locale/ta/mfa.json
new file mode 100644
index 000000000..04ef1ee0b
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/ta/mfa.json
@@ -0,0 +1,106 @@
+{
+ "MFA_SETTINGS": {
+ "TITLE": "Two-Factor Authentication",
+ "SUBTITLE": "Secure your account with TOTP-based authentication",
+ "DESCRIPTION": "Add an extra layer of security to your account using a time-based one-time password (TOTP)",
+ "STATUS_TITLE": "Authentication Status",
+ "STATUS_DESCRIPTION": "Manage your two-factor authentication settings and backup recovery codes",
+ "ENABLED": "இயக்கப்பட்டது",
+ "DISABLED": "முடக்கப்பட்டது",
+ "STATUS_ENABLED": "Two-factor authentication is active",
+ "STATUS_ENABLED_DESC": "Your account is protected with an additional layer of security",
+ "ENABLE_BUTTON": "Enable Two-Factor Authentication",
+ "ENHANCE_SECURITY": "Enhance Your Account Security",
+ "ENHANCE_SECURITY_DESC": "Two-factor authentication adds an extra layer of security by requiring a verification code from your authenticator app in addition to your password.",
+ "SETUP": {
+ "STEP_NUMBER_1": "1",
+ "STEP_NUMBER_2": "2",
+ "STEP1_TITLE": "Scan QR Code with Your Authenticator App",
+ "STEP1_DESCRIPTION": "Use Google Authenticator, Authy, or any TOTP-compatible app",
+ "LOADING_QR": "Loading...",
+ "MANUAL_ENTRY": "Can't scan? Enter code manually",
+ "SECRET_KEY": "Secret Key",
+ "COPY": "நகல்",
+ "ENTER_CODE": "Enter the 6-digit code from your authenticator app",
+ "ENTER_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify & Continue",
+ "CANCEL": "ரத்துசெய்",
+ "ERROR_STARTING": "MFA not enabled. Please contact administrator.",
+ "INVALID_CODE": "Invalid verification code",
+ "SECRET_COPIED": "Secret key copied to clipboard",
+ "SUCCESS": "Two-factor authentication has been enabled successfully"
+ },
+ "BACKUP": {
+ "TITLE": "Save Your Backup Codes",
+ "DESCRIPTION": "Keep these codes safe. Each can be used once if you lose access to your authenticator",
+ "IMPORTANT": "Important:",
+ "IMPORTANT_NOTE": " Save these codes in a secure location. You won't be able to see them again.",
+ "DOWNLOAD": "பதிவிறக்கம்",
+ "COPY_ALL": "Copy All",
+ "CONFIRM": "I have saved my backup codes in a secure location and understand that I won't be able to see them again",
+ "COMPLETE_SETUP": "Complete Setup",
+ "CODES_COPIED": "Backup codes copied to clipboard"
+ },
+ "MANAGEMENT": {
+ "BACKUP_CODES": "Backup Codes",
+ "BACKUP_CODES_DESC": "Generate new codes if you've lost or used your existing ones",
+ "REGENERATE": "Regenerate Backup Codes",
+ "DISABLE_MFA": "Disable 2FA",
+ "DISABLE_MFA_DESC": "Remove two-factor authentication from your account",
+ "DISABLE_BUTTON": "Disable Two-Factor Authentication"
+ },
+ "DISABLE": {
+ "TITLE": "Disable Two-Factor Authentication",
+ "DESCRIPTION": "You'll need to enter your password and a verification code to disable two-factor authentication.",
+ "PASSWORD": "பாஸ்வேர்ட்",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Disable 2FA",
+ "CANCEL": "ரத்துசெய்",
+ "SUCCESS": "Two-factor authentication has been disabled",
+ "ERROR": "Failed to disable MFA. Please check your credentials."
+ },
+ "REGENERATE": {
+ "TITLE": "Regenerate Backup Codes",
+ "DESCRIPTION": "This will invalidate your existing backup codes and generate new ones. Enter your verification code to continue.",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Generate New Codes",
+ "CANCEL": "ரத்துசெய்",
+ "NEW_CODES_TITLE": "New Backup Codes Generated",
+ "NEW_CODES_DESC": "Your old backup codes have been invalidated. Save these new codes in a secure location.",
+ "CODES_IMPORTANT": "Important:",
+ "CODES_IMPORTANT_NOTE": " Each code can only be used once. Save them before closing this window.",
+ "DOWNLOAD_CODES": "Download Codes",
+ "COPY_ALL_CODES": "Copy All Codes",
+ "CODES_SAVED": "I've Saved My Codes",
+ "SUCCESS": "New backup codes have been generated",
+ "ERROR": "Failed to regenerate backup codes"
+ }
+ },
+ "MFA_VERIFICATION": {
+ "TITLE": "Two-Factor Authentication",
+ "DESCRIPTION": "Enter your verification code to continue",
+ "AUTHENTICATOR_APP": "Authenticator App",
+ "BACKUP_CODE": "Backup Code",
+ "ENTER_OTP_CODE": "Enter 6-digit code from your authenticator app",
+ "ENTER_BACKUP_CODE": "Enter one of your backup codes",
+ "BACKUP_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify",
+ "TRY_ANOTHER_METHOD": "Try another verification method",
+ "CANCEL_LOGIN": "Cancel and return to login",
+ "HELP_TEXT": "Having trouble signing in?",
+ "LEARN_MORE": "Learn more about 2FA",
+ "HELP_MODAL": {
+ "TITLE": "Two-Factor Authentication Help",
+ "AUTHENTICATOR_TITLE": "Using an Authenticator App",
+ "AUTHENTICATOR_DESC": "Open your authenticator app (Google Authenticator, Authy, etc.) and enter the 6-digit code shown for your account.",
+ "BACKUP_TITLE": "Using a Backup Code",
+ "BACKUP_DESC": "If you don't have access to your authenticator app, you can use one of the backup codes you saved when setting up 2FA. Each code can only be used once.",
+ "CONTACT_TITLE": "Need More Help?",
+ "CONTACT_DESC_CLOUD": "If you've lost access to both your authenticator app and backup codes, please reach out to Chatwoot support for assistance.",
+ "CONTACT_DESC_SELF_HOSTED": "If you've lost access to both your authenticator app and backup codes, please contact your administrator for assistance."
+ },
+ "VERIFICATION_FAILED": "Verification failed. Please try again."
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/ta/settings.json b/app/javascript/dashboard/i18n/locale/ta/settings.json
index fa1caa4e3..9f3df3486 100644
--- a/app/javascript/dashboard/i18n/locale/ta/settings.json
+++ b/app/javascript/dashboard/i18n/locale/ta/settings.json
@@ -51,6 +51,13 @@
"LARGER": "Larger",
"EXTRA_LARGE": "Extra Large"
}
+ },
+ "LANGUAGE": {
+ "TITLE": "Preferred Language",
+ "NOTE": "Choose the language you want to use.",
+ "UPDATE_SUCCESS": "Your Language settings have been updated successfully",
+ "UPDATE_ERROR": "There is an error while updating the language settings, please try again",
+ "USE_ACCOUNT_DEFAULT": "Use account default"
}
},
"MESSAGE_SIGNATURE_SECTION": {
@@ -73,6 +80,11 @@
"NOTE": "உங்கள் பாஸ்வேர்டைப் புதுப்பிப்பது உங்கள் உள்நுழைவுகளை பல சாதனங்களில் மீட்டமைக்கும்.",
"BTN_TEXT": "Change password"
},
+ "SECURITY_SECTION": {
+ "TITLE": "Security",
+ "NOTE": "Manage additional security features for your account.",
+ "MFA_BUTTON": "Manage Two-Factor Authentication"
+ },
"ACCESS_TOKEN": {
"TITLE": "அணுகுவதற்கான டோக்கன்",
"NOTE": "நீங்கள் API அடிப்படையிலான ஒருங்கிணைப்பை உருவாக்கினால் இந்த டோக்கனைப் பயன்படுத்தலாம்",
@@ -330,6 +342,7 @@
"REPORTS_LABEL": "Labels",
"REPORTS_INBOX": "Inbox",
"REPORTS_TEAM": "Team",
+ "AGENT_ASSIGNMENT": "Agent Assignment",
"SET_AVAILABILITY_TITLE": "Set yourself as",
"SET_YOUR_AVAILABILITY": "Set your availability",
"SLA": "SLA",
@@ -350,7 +363,8 @@
"INFO_TEXT": "Let the system automatically mark you offline when you aren't using the app or dashboard.",
"INFO_SHORT": "Automatically mark offline when you aren't using the app."
},
- "DOCS": "Read docs"
+ "DOCS": "Read docs",
+ "SECURITY": "Security"
},
"BILLING_SETTINGS": {
"TITLE": "Billing",
@@ -382,6 +396,77 @@
},
"NO_BILLING_USER": "Your billing account is being configured. Please refresh the page and try again."
},
+ "SECURITY_SETTINGS": {
+ "TITLE": "Security",
+ "DESCRIPTION": "Manage your account security settings.",
+ "LINK_TEXT": "Learn more about SAML SSO",
+ "SAML": {
+ "TITLE": "SAML SSO",
+ "NOTE": "Configure SAML single sign-on for your account. Users will authenticate through your identity provider instead of using email/password.",
+ "ACS_URL": {
+ "LABEL": "ACS URL",
+ "TOOLTIP": "Assertion Consumer Service URL - Configure this URL in your IdP as the destination for SAML responses"
+ },
+ "SSO_URL": {
+ "LABEL": "SSO URL",
+ "HELP": "The URL where SAML authentication requests will be sent",
+ "PLACEHOLDER": "https://your-idp.com/saml/sso"
+ },
+ "CERTIFICATE": {
+ "LABEL": "Signing certificate in PEM format",
+ "HELP": "The public certificate from your identity provider used to verify SAML responses",
+ "PLACEHOLDER": "-----BEGIN CERTIFICATE-----\nMIIC..."
+ },
+ "FINGERPRINT": {
+ "LABEL": "Fingerprint",
+ "TOOLTIP": "SHA-1 fingerprint of the certificate - Use this to verify the certificate in your IdP configuration"
+ },
+ "COPY_SUCCESS": "குறியீடு கிளிப்போர்டில் வெற்றிகரமாக காப்பி செய்யப்பட்டது",
+ "SP_ENTITY_ID": {
+ "LABEL": "SP Entity ID",
+ "HELP": "Unique identifier for this application as a service provider (auto-generated).",
+ "TOOLTIP": "Unique identifier for Chatwoot as the Service Provider - Configure this in your IdP settings"
+ },
+ "IDP_ENTITY_ID": {
+ "LABEL": "Identity Provider Entity ID",
+ "HELP": "Unique identifier for your identity provider (usually found in IdP configuration)",
+ "PLACEHOLDER": "https://your-idp.com/saml"
+ },
+ "UPDATE_BUTTON": "Update SAML Settings",
+ "API": {
+ "SUCCESS": "SAML settings updated successfully",
+ "ERROR": "Failed to update SAML settings",
+ "ERROR_LOADING": "Failed to load SAML settings",
+ "DISABLED": "SAML settings disabled successfully"
+ },
+ "VALIDATION": {
+ "REQUIRED_FIELDS": "SSO URL, Identity Provider Entity ID, and Certificate are required fields",
+ "SSO_URL_ERROR": "Please enter a valid SSO URL",
+ "CERTIFICATE_ERROR": "Certificate is required",
+ "IDP_ENTITY_ID_ERROR": "Identity Provider Entity ID is required"
+ },
+ "ENTERPRISE_PAYWALL": {
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade to an Enterprise plan to access SAML single sign-on and other advanced security features.",
+ "ASK_ADMIN": "Please reach out to your administrator for the upgrade."
+ },
+ "PAYWALL": {
+ "TITLE": "Upgrade to enable SAML SSO",
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade your plan to get access to SAML single sign-on and other advanced features.",
+ "UPGRADE_NOW": "Upgrade now",
+ "CANCEL_ANYTIME": "You can change or cancel your plan anytime"
+ },
+ "ATTRIBUTE_MAPPING": {
+ "TITLE": "SAML Attribute Setup",
+ "DESCRIPTION": "The following attribute mappings must be configured in your identity provider"
+ },
+ "INFO_SECTION": {
+ "TITLE": "Service Provider Information",
+ "TOOLTIP": "Copy these values and configure them in your Identity Provider to establish the SAML connection"
+ }
+ }
+ },
"CREATE_ACCOUNT": {
"NO_ACCOUNT_WARNING": "Uh oh! We could not find any Chatwoot accounts. Please create a new account to continue.",
"NEW_ACCOUNT": "New Account",
@@ -418,5 +503,255 @@
"SWITCH_TO_REPLY": "Switch to Reply",
"TOGGLE_SNOOZE_DROPDOWN": "Toggle snooze dropdown"
}
+ },
+ "ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent assignment",
+ "DESCRIPTION": "Define policies to effectively manage workload and route conversations based on the needs of inboxes and agents. Learn more here"
+ },
+ "ASSIGNMENT_POLICY": {
+ "TITLE": "Assignment policy",
+ "DESCRIPTION": "Manage how conversations get assigned in inboxes.",
+ "FEATURES": [
+ "Assign by conversations evenly or by available capacity",
+ "Add fair distribution rules to avoid overloading any agent",
+ "Add inboxes to a policy - one policy per inbox"
+ ]
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "TITLE": "Agent capacity policy",
+ "DESCRIPTION": "Manage workload for agents.",
+ "FEATURES": [
+ "Define maximum conversations per inbox",
+ "Create exceptions based on labels and time",
+ "Add agents to a policy - one policy per agent"
+ ]
+ }
+ },
+ "AGENT_ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Assignment policy",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "ORDER": "Order",
+ "PRIORITY": "Priority",
+ "ACTIVE": "Active",
+ "INACTIVE": "Inactive",
+ "POPOVER": "Added inboxes",
+ "EDIT": "திருத்து"
+ },
+ "NO_RECORDS_FOUND": "No assignment policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create assignment policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy created successfully",
+ "ERROR_MESSAGE": "Failed to create assignment policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit assignment policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_INBOX_DIALOG": {
+ "TITLE": "Add inbox",
+ "DESCRIPTION": "{inboxName} inbox is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "ரத்துசெய்"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update assignment policy"
+ },
+ "INBOX_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Inbox added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Inbox removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove inbox from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Description:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "STATUS": {
+ "LABEL": "Status:",
+ "PLACEHOLDER": "Select status",
+ "ACTIVE": "Policy is active",
+ "INACTIVE": "Policy is inactive"
+ },
+ "ASSIGNMENT_ORDER": {
+ "LABEL": "Assignment order",
+ "ROUND_ROBIN": {
+ "LABEL": "Round robin",
+ "DESCRIPTION": "Assign conversations evenly among agents."
+ },
+ "BALANCED": {
+ "LABEL": "Balanced",
+ "DESCRIPTION": "Assign conversations based on available capacity."
+ }
+ },
+ "ASSIGNMENT_PRIORITY": {
+ "LABEL": "Assignment priority",
+ "EARLIEST_CREATED": {
+ "LABEL": "Earliest created",
+ "DESCRIPTION": "The conversation that was created first gets assigned first."
+ },
+ "LONGEST_WAITING": {
+ "LABEL": "Longest waiting",
+ "DESCRIPTION": "The conversation waiting the longest gets assigned first."
+ }
+ },
+ "FAIR_DISTRIBUTION": {
+ "LABEL": "Fair distribution policy",
+ "DESCRIPTION": "Set the maximum number of conversations that can be assigned per agent within a time window to avoid overloading any one agent. This required field defaults to 100 conversations per hour.",
+ "INPUT_MAX": "Assign max",
+ "DURATION": "Conversations per agent in every"
+ },
+ "INBOXES": {
+ "LABEL": "Added inboxes",
+ "DESCRIPTION": "Add inboxes for which this policy will be applicable.",
+ "ADD_BUTTON": "Add inbox",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select inboxes to add",
+ "ADD_BUTTON": "Add"
+ },
+ "EMPTY_STATE": "No inboxes added to this policy, add an inbox to get started",
+ "API": {
+ "SUCCESS_MESSAGE": "Inbox successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Assignment policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete assignment policy"
+ }
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent capacity",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "POPOVER": "Added agents",
+ "EDIT": "திருத்து"
+ },
+ "NO_RECORDS_FOUND": "No agent capacity policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create agent capacity policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy created successfully",
+ "ERROR_MESSAGE": "Failed to create agent capacity policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit agent capacity policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_AGENT_DIALOG": {
+ "TITLE": "Add agent",
+ "DESCRIPTION": "{agentName} is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "ரத்துசெய்"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update agent capacity policy"
+ },
+ "AGENT_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Agent added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Agent removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove agent from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Description:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "INBOX_CAPACITY_LIMIT": {
+ "LABEL": "Inbox capacity limits",
+ "ADD_BUTTON": "Add inbox",
+ "FIELD": {
+ "SELECT_INBOX": "Select inbox",
+ "MAX_CONVERSATIONS": "Max conversations",
+ "SET_LIMIT": "Set limit"
+ },
+ "EMPTY_STATE": "No inbox limit set"
+ },
+ "EXCLUSION_RULES": {
+ "LABEL": "Exclusion rules",
+ "DESCRIPTION": "Conversations that satisfy the following conditions would not count towards agent capacity",
+ "TAGS": {
+ "LABEL": "Exclude conversations tagged with specific labels",
+ "ADD_TAG": "add tag",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select tags to add"
+ },
+ "EMPTY_STATE": "No tags added to this policy."
+ },
+ "DURATION": {
+ "LABEL": "Exclude conversations older than a specified duration",
+ "PLACEHOLDER": "Set time"
+ }
+ },
+ "USERS": {
+ "LABEL": "Assigned agents",
+ "DESCRIPTION": "Add agents for which this policy will be applicable.",
+ "ADD_BUTTON": "Add agent",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select agents to add",
+ "ADD_BUTTON": "Add"
+ },
+ "EMPTY_STATE": "No agents added",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Agent capacity policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete agent capacity policy"
+ }
+ },
+ "DELETE_POLICY": {
+ "TITLE": "Delete policy",
+ "DESCRIPTION": "Are you sure you want to delete this policy? This action cannot be undone.",
+ "CONFIRM_BUTTON_LABEL": "Delete",
+ "CANCEL_BUTTON_LABEL": "ரத்துசெய்"
+ }
}
}
diff --git a/app/javascript/dashboard/i18n/locale/ta/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/ta/whatsappTemplates.json
index 5f53faaa8..cf28312dc 100644
--- a/app/javascript/dashboard/i18n/locale/ta/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/ta/whatsappTemplates.json
@@ -40,6 +40,7 @@
"BUTTON_LABEL": "Button {index}",
"COUPON_CODE": "Enter coupon code (max 15 chars)",
"MEDIA_URL_LABEL": "Enter {type} URL",
+ "DOCUMENT_NAME_PLACEHOLDER": "Enter document filename (e.g., Invoice_2025.pdf)",
"BUTTON_PARAMETER": "Enter button parameter"
}
}
diff --git a/app/javascript/dashboard/i18n/locale/th/automation.json b/app/javascript/dashboard/i18n/locale/th/automation.json
index 8df9e274f..0518a4ed1 100644
--- a/app/javascript/dashboard/i18n/locale/th/automation.json
+++ b/app/javascript/dashboard/i18n/locale/th/automation.json
@@ -177,7 +177,8 @@
"REFERER_LINK": "Referrer Link",
"ASSIGNEE_NAME": "Assignee",
"TEAM_NAME": "ทีม",
- "PRIORITY": "Priority"
+ "PRIORITY": "Priority",
+ "LABELS": "ป้ายกำกับ"
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/th/contact.json b/app/javascript/dashboard/i18n/locale/th/contact.json
index 9a9dd4520..aefcea237 100644
--- a/app/javascript/dashboard/i18n/locale/th/contact.json
+++ b/app/javascript/dashboard/i18n/locale/th/contact.json
@@ -610,6 +610,15 @@
"SEND_MESSAGE": "ส่วข้อความ"
}
},
+ "TWILIO_OPTIONS": {
+ "LABEL": "Select template",
+ "SEARCH_PLACEHOLDER": "Search templates",
+ "EMPTY_STATE": "No templates found",
+ "TEMPLATE_PARSER": {
+ "BACK": "Go back",
+ "SEND_MESSAGE": "ส่วข้อความ"
+ }
+ },
"ACTION_BUTTONS": {
"DISCARD": "Discard",
"SEND": "Send ({keyCode})"
diff --git a/app/javascript/dashboard/i18n/locale/th/contactFilters.json b/app/javascript/dashboard/i18n/locale/th/contactFilters.json
index 897905fe2..f96aee686 100644
--- a/app/javascript/dashboard/i18n/locale/th/contactFilters.json
+++ b/app/javascript/dashboard/i18n/locale/th/contactFilters.json
@@ -48,7 +48,8 @@
"CREATED_AT": "สร้างเมื่อ",
"LAST_ACTIVITY": "ล่าสุดเมื่อ",
"REFERER_LINK": "Referrer link",
- "BLOCKED": "Blocked"
+ "BLOCKED": "Blocked",
+ "LABELS": "ป้ายกำกับ"
},
"GROUPS": {
"STANDARD_FILTERS": "ตัวกรองมาตรฐาน",
diff --git a/app/javascript/dashboard/i18n/locale/th/contentTemplates.json b/app/javascript/dashboard/i18n/locale/th/contentTemplates.json
new file mode 100644
index 000000000..2f26bad2f
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/th/contentTemplates.json
@@ -0,0 +1,51 @@
+{
+ "CONTENT_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Twilio Templates",
+ "SUBTITLE": "Select the Twilio template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Search Templates",
+ "NO_TEMPLATES_FOUND": "No templates found for",
+ "NO_CONTENT": "No content",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Category",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No Twilio templates available. Click refresh to sync templates from Twilio.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Language",
+ "TEMPLATE_BODY": "Template Body",
+ "CATEGORY": "Category"
+ },
+ "TYPES": {
+ "MEDIA": "Media",
+ "QUICK_REPLY": "Quick Reply",
+ "TEXT": "ข้อความ"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variables",
+ "LANGUAGE": "Language",
+ "CATEGORY": "Category",
+ "VARIABLE_PLACEHOLDER": "Enter {variable} value",
+ "GO_BACK_LABEL": "Go Back",
+ "SEND_MESSAGE_LABEL": "Send Message",
+ "FORM_ERROR_MESSAGE": "Please fill all variables before sending",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "MEDIA_URL_LABEL": "Enter full media URL",
+ "MEDIA_URL_PLACEHOLDER": "https://example.com/image.jpg"
+ },
+ "FORM": {
+ "BACK_BUTTON": "ย้อนกลับ",
+ "SEND_MESSAGE_BUTTON": "Send Message"
+ }
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/th/conversation.json b/app/javascript/dashboard/i18n/locale/th/conversation.json
index fd7422578..9b3cc2aba 100644
--- a/app/javascript/dashboard/i18n/locale/th/conversation.json
+++ b/app/javascript/dashboard/i18n/locale/th/conversation.json
@@ -35,6 +35,11 @@
"API_HOURS_WINDOW": "You can only reply to this conversation within {hours} hours",
"NOT_ASSIGNED_TO_YOU": "การสนทนานี้ไม่ได้ถูกมอบหมายให้คุณ ต้องการที่จะจัดการด้วยตัวเองหรือไม่?",
"ASSIGN_TO_ME": "มอบหมายให้ฉัน",
+ "BOT_HANDOFF_MESSAGE": "You are responding to a conversation which is currently handled by an assistant or a bot.",
+ "BOT_HANDOFF_ACTION": "Mark open and assign to you",
+ "BOT_HANDOFF_REOPEN_ACTION": "Mark conversation open",
+ "BOT_HANDOFF_SUCCESS": "Conversation has been handed over to you",
+ "BOT_HANDOFF_ERROR": "Failed to take over the conversation. Please try again.",
"TWILIO_WHATSAPP_CAN_REPLY": "คุณสามารถตอบกลับการสนทนานี้ได้โดยใช้รูปแบบข้อความที่กำหนดเท่านั้น",
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "การจำกัดหน้าต่างข้อความ 24 ชั่วโมง",
"OLD_INSTAGRAM_INBOX_REPLY_BANNER": "This Instagram account was migrated to the new Instagram channel inbox. All new messages will show up there. You won’t be able to send messages from this conversation anymore.",
@@ -66,6 +71,17 @@
"SHOW_LABELS": "แสดงป้ายกำกับ",
"HIDE_LABELS": "ซ่อนป้ายกำกับ"
},
+ "VOICE_CALL": {
+ "INCOMING_CALL": "Incoming call",
+ "OUTGOING_CALL": "Outgoing call",
+ "CALL_IN_PROGRESS": "Call in progress",
+ "NO_ANSWER": "No answer",
+ "MISSED_CALL": "Missed call",
+ "CALL_ENDED": "Call ended",
+ "NOT_ANSWERED_YET": "Not answered yet",
+ "THEY_ANSWERED": "They answered",
+ "YOU_ANSWERED": "You answered"
+ },
"HEADER": {
"RESOLVE_ACTION": "เสร็จสิ้น",
"REOPEN_ACTION": "เปิดใหม่อีกครั้ง",
diff --git a/app/javascript/dashboard/i18n/locale/th/helpCenter.json b/app/javascript/dashboard/i18n/locale/th/helpCenter.json
index a9bb3fda5..b4a2bd2b7 100644
--- a/app/javascript/dashboard/i18n/locale/th/helpCenter.json
+++ b/app/javascript/dashboard/i18n/locale/th/helpCenter.json
@@ -741,7 +741,8 @@
"LIVE_CHAT_WIDGET": {
"LABEL": "Live chat widget",
"PLACEHOLDER": "Select live chat widget",
- "HELP_TEXT": "Select a live chat widget that will appear on your help center"
+ "HELP_TEXT": "Select a live chat widget that will appear on your help center",
+ "NONE_OPTION": "No widget"
},
"BRAND_COLOR": {
"LABEL": "Brand color"
@@ -807,6 +808,58 @@
"ERROR_MESSAGE": "Unable to update portal"
}
}
+ },
+ "PDF_UPLOAD": {
+ "TITLE": "Upload PDF Document",
+ "DESCRIPTION": "Upload a PDF document to automatically generate FAQs using AI",
+ "DRAG_DROP_TEXT": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "ADDITIONAL_CONTEXT_LABEL": "Additional Context (Optional)",
+ "ADDITIONAL_CONTEXT_PLACEHOLDER": "Provide any additional context or instructions for FAQ generation...",
+ "UPLOADING": "กำลังอัพโหลด",
+ "UPLOAD": "Upload & Process",
+ "CANCEL": "ยกเลิก",
+ "ERROR_INVALID_TYPE": "Please select a valid PDF file",
+ "ERROR_FILE_TOO_LARGE": "File size must be less than 512MB",
+ "ERROR_UPLOAD_FAILED": "Failed to upload PDF. Please try again."
+ },
+ "PDF_DOCUMENTS": {
+ "TITLE": "PDF Documents",
+ "DESCRIPTION": "Manage uploaded PDF documents and generate FAQs from them",
+ "UPLOAD_PDF": "Upload PDF",
+ "UPLOAD_FIRST_PDF": "Upload your first PDF",
+ "UPLOADED_BY": "Uploaded by",
+ "GENERATE_FAQS": "Generate FAQs",
+ "GENERATING": "Generating...",
+ "CONFIRM_DELETE": "Are you sure you want to delete {filename}?",
+ "EMPTY_STATE": {
+ "TITLE": "No PDF documents yet",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQs using AI"
+ },
+ "STATUS": {
+ "UPLOADED": "Ready",
+ "PROCESSING": "Processing",
+ "PROCESSED": "เสร็จสิ้น",
+ "FAILED": "Failed"
+ }
+ },
+ "CONTENT_GENERATION": {
+ "TITLE": "Content Generation",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQ content using AI",
+ "UPLOAD_TITLE": "Upload PDF Document",
+ "DRAG_DROP": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "UPLOADING": "Processing document...",
+ "UPLOAD_SUCCESS": "Document processed successfully!",
+ "UPLOAD_ERROR": "Failed to upload document. Please try again.",
+ "INVALID_FILE_TYPE": "Please select a valid PDF file",
+ "FILE_TOO_LARGE": "File size must be less than 512MB",
+ "GENERATED_CONTENT": "Generated FAQ Content",
+ "PUBLISH_SELECTED": "Publish Selected",
+ "PUBLISHING": "Publishing...",
+ "FROM_DOCUMENT": "From document",
+ "NO_CONTENT": "No generated content available. Upload a PDF document to get started.",
+ "LOADING": "Loading generated content..."
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/th/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/th/inboxMgmt.json
index 260dca517..65445c95b 100644
--- a/app/javascript/dashboard/i18n/locale/th/inboxMgmt.json
+++ b/app/javascript/dashboard/i18n/locale/th/inboxMgmt.json
@@ -272,8 +272,8 @@
},
"SUBMIT_BUTTON": "สร้างช่องทาง WhatsApp",
"EMBEDDED_SIGNUP": {
- "TITLE": "Quick Setup with Meta",
- "DESC": "You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
+ "TITLE": "Quick setup with Meta",
+ "DESC": "Use the WhatsApp Embedded Signup flow to quickly connect new numbers. You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
"BENEFITS": {
"TITLE": "Benefits of Embedded Signup:",
"EASY_SETUP": "No manual configuration required",
@@ -281,9 +281,8 @@
"AUTO_CONFIG": "Automatic webhook and phone number configuration"
},
"LEARN_MORE": {
- "TEXT": "To learn more about integrated signup, pricing, and limitations, visit",
- "LINK_TEXT": "this link.",
- "LINK_URL": "https://developers.facebook.com/docs/whatsapp/embedded-signup/custom-flows/onboarding-business-app-users#limitations"
+ "TEXT": "To learn more about integrated signup, pricing, and limitations, visit {link}.",
+ "LINK_TEXT": "this link"
},
"SUBMIT_BUTTON": "Connect with WhatsApp Business",
"AUTH_PROCESSING": "Authenticating with Meta",
@@ -296,7 +295,9 @@
"INVALID_BUSINESS_DATA": "Invalid business data received from Facebook. Please try again.",
"SIGNUP_ERROR": "Signup error occurred",
"AUTH_NOT_COMPLETED": "Authentication not completed. Please restart the process.",
- "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured"
+ "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured",
+ "MANUAL_FALLBACK": "If your number is already connected to the WhatsApp Business Platform (API), or if you’re a tech provider onboarding your own number, please use the {link} flow",
+ "MANUAL_LINK_TEXT": "manual setup flow"
},
"API": {
"ERROR_MESSAGE": "เราไม่สามารถบันทึกช่องทาง WhatsApp ได้"
@@ -424,7 +425,51 @@
},
"AUTH": {
"TITLE": "เลือกช่องทาง",
- "DESC": "Chatwoot รองรับ live-chat widget, Facebook, Twitter, WhatsApp, Email และช่องทางอื่น ๆ ถ้าคุณต้องการสร้างช่องทางพิเศษ คุณสามารถสร้างได้โดยใช้ API channel เลือกช่องทางด้านล่างนี้เพื่อดำเนินการต่อ"
+ "DESC": "Chatwoot รองรับ live-chat widget, Facebook, Twitter, WhatsApp, Email และช่องทางอื่น ๆ ถ้าคุณต้องการสร้างช่องทางพิเศษ คุณสามารถสร้างได้โดยใช้ API channel เลือกช่องทางด้านล่างนี้เพื่อดำเนินการต่อ",
+ "TITLE_NEXT": "Complete the setup",
+ "TITLE_FINISH": "Voilà!",
+ "CHANNEL": {
+ "WEBSITE": {
+ "TITLE": "Website",
+ "DESCRIPTION": "Create a live-chat widget"
+ },
+ "FACEBOOK": {
+ "TITLE": "เฟสบุ๊ค",
+ "DESCRIPTION": "Connect your Facebook page"
+ },
+ "WHATSAPP": {
+ "TITLE": "WhatsApp",
+ "DESCRIPTION": "Support your customers on WhatsApp"
+ },
+ "EMAIL": {
+ "TITLE": "อีเมล์",
+ "DESCRIPTION": "Connect with Gmail, Outlook, or other providers"
+ },
+ "SMS": {
+ "TITLE": "SMS",
+ "DESCRIPTION": "Integrate SMS channel with Twilio or bandwidth"
+ },
+ "API": {
+ "TITLE": "API",
+ "DESCRIPTION": "Make a custom channel using our API"
+ },
+ "TELEGRAM": {
+ "TITLE": "Telegram",
+ "DESCRIPTION": "Configure Telegram channel using Bot token"
+ },
+ "LINE": {
+ "TITLE": "Line",
+ "DESCRIPTION": "Integrate your Line channel"
+ },
+ "INSTAGRAM": {
+ "TITLE": "Instagram",
+ "DESCRIPTION": "Connect your instagram account"
+ },
+ "VOICE": {
+ "TITLE": "Voice",
+ "DESCRIPTION": "Integrate with Twilio Voice"
+ }
+ }
},
"AGENTS": {
"TITLE": "พนักงาน",
@@ -478,7 +523,10 @@
"MESSAGE": "ตอนนี้คุณสามารถมีส่วนร่วมกับลูกค้าของคุณผ่านช่องทางใหม่ได้แล้ว ขอให้มีความสุขกับการคุย",
"BUTTON_TEXT": "พาฉันไปที่นั่น",
"MORE_SETTINGS": "ตั้งค่าเพิ่มเติม",
- "WEBSITE_SUCCESS": "คุณได้สร้างช่องทางเว็บไซต์สำเร็จแล้ว คัดลอกโค้ดข้างล่างแล้วนำไปแปะที่เว็บไซต์ของคุณ ครั้งถัดไปที่ลูกค้าใช้ live chat การสนทนาจะปรากฎที่กล่องข้อความของคุณโดยอัตโนมัติ"
+ "WEBSITE_SUCCESS": "คุณได้สร้างช่องทางเว็บไซต์สำเร็จแล้ว คัดลอกโค้ดข้างล่างแล้วนำไปแปะที่เว็บไซต์ของคุณ ครั้งถัดไปที่ลูกค้าใช้ live chat การสนทนาจะปรากฎที่กล่องข้อความของคุณโดยอัตโนมัติ",
+ "WHATSAPP_QR_INSTRUCTION": "Scan the QR code above to quickly test your WhatsApp inbox",
+ "MESSENGER_QR_INSTRUCTION": "Scan the QR code above to quickly test your Facebook Messenger inbox",
+ "TELEGRAM_QR_INSTRUCTION": "Scan the QR code above to quickly test your Telegram inbox"
},
"REAUTH": "ขอสิทธิ์อีกครั้ง",
"VIEW": "ดู",
@@ -868,9 +916,18 @@
"SCRIPT_SETTINGS": "\n window.chatwootSettings = {options};"
},
"EMAIL_PROVIDERS": {
- "MICROSOFT": "Microsoft",
- "GOOGLE": "Google",
- "OTHER_PROVIDERS": "Other Providers"
+ "MICROSOFT": {
+ "TITLE": "Microsoft",
+ "DESCRIPTION": "Connect with Microsoft"
+ },
+ "GOOGLE": {
+ "TITLE": "Google",
+ "DESCRIPTION": "Connect with Google"
+ },
+ "OTHER_PROVIDERS": {
+ "TITLE": "Other Providers",
+ "DESCRIPTION": "Connect with Other Providers"
+ }
},
"CHANNELS": {
"MESSENGER": "Messenger",
diff --git a/app/javascript/dashboard/i18n/locale/th/integrations.json b/app/javascript/dashboard/i18n/locale/th/integrations.json
index c9ab15529..295e55571 100644
--- a/app/javascript/dashboard/i18n/locale/th/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/th/integrations.json
@@ -704,11 +704,28 @@
"ERROR_MESSAGE": "There was an error creating the document, please try again."
},
"FORM": {
+ "TYPE": {
+ "LABEL": "Document Type",
+ "URL": "ลิ้ง",
+ "PDF": "PDF File"
+ },
"URL": {
"LABEL": "ลิ้ง",
"PLACEHOLDER": "Enter the URL of the document",
"ERROR": "Please provide a valid URL for the document"
},
+ "PDF_FILE": {
+ "LABEL": "PDF File",
+ "CHOOSE_FILE": "Choose PDF file",
+ "ERROR": "Please select a PDF file",
+ "HELP_TEXT": "Maximum file size: 10MB",
+ "INVALID_TYPE": "Please select a valid PDF file",
+ "TOO_LARGE": "File size exceeds 10MB limit"
+ },
+ "NAME": {
+ "LABEL": "Document Name (Optional)",
+ "PLACEHOLDER": "Enter a name for the document"
+ },
"ASSISTANT": {
"LABEL": "Assistant",
"PLACEHOLDER": "Select the assistant",
@@ -744,6 +761,7 @@
"SELECTED": "{count} selected",
"SELECT_ALL": "Select all ({count})",
"UNSELECT_ALL": "Unselect all ({count})",
+ "SEARCH_PLACEHOLDER": "Search FAQs...",
"BULK_APPROVE_BUTTON": "Approve",
"BULK_DELETE_BUTTON": "ลบ",
"BULK_APPROVE": {
diff --git a/app/javascript/dashboard/i18n/locale/th/mfa.json b/app/javascript/dashboard/i18n/locale/th/mfa.json
new file mode 100644
index 000000000..5e24f73a5
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/th/mfa.json
@@ -0,0 +1,106 @@
+{
+ "MFA_SETTINGS": {
+ "TITLE": "Two-Factor Authentication",
+ "SUBTITLE": "Secure your account with TOTP-based authentication",
+ "DESCRIPTION": "Add an extra layer of security to your account using a time-based one-time password (TOTP)",
+ "STATUS_TITLE": "Authentication Status",
+ "STATUS_DESCRIPTION": "Manage your two-factor authentication settings and backup recovery codes",
+ "ENABLED": "เปิด",
+ "DISABLED": "ปิด",
+ "STATUS_ENABLED": "Two-factor authentication is active",
+ "STATUS_ENABLED_DESC": "Your account is protected with an additional layer of security",
+ "ENABLE_BUTTON": "Enable Two-Factor Authentication",
+ "ENHANCE_SECURITY": "Enhance Your Account Security",
+ "ENHANCE_SECURITY_DESC": "Two-factor authentication adds an extra layer of security by requiring a verification code from your authenticator app in addition to your password.",
+ "SETUP": {
+ "STEP_NUMBER_1": "1",
+ "STEP_NUMBER_2": "2",
+ "STEP1_TITLE": "Scan QR Code with Your Authenticator App",
+ "STEP1_DESCRIPTION": "Use Google Authenticator, Authy, or any TOTP-compatible app",
+ "LOADING_QR": "Loading...",
+ "MANUAL_ENTRY": "Can't scan? Enter code manually",
+ "SECRET_KEY": "Secret Key",
+ "COPY": "คัดลอก",
+ "ENTER_CODE": "Enter the 6-digit code from your authenticator app",
+ "ENTER_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify & Continue",
+ "CANCEL": "ยกเลิก",
+ "ERROR_STARTING": "MFA not enabled. Please contact administrator.",
+ "INVALID_CODE": "Invalid verification code",
+ "SECRET_COPIED": "Secret key copied to clipboard",
+ "SUCCESS": "Two-factor authentication has been enabled successfully"
+ },
+ "BACKUP": {
+ "TITLE": "Save Your Backup Codes",
+ "DESCRIPTION": "Keep these codes safe. Each can be used once if you lose access to your authenticator",
+ "IMPORTANT": "Important:",
+ "IMPORTANT_NOTE": " Save these codes in a secure location. You won't be able to see them again.",
+ "DOWNLOAD": "ดาวโหลด",
+ "COPY_ALL": "Copy All",
+ "CONFIRM": "I have saved my backup codes in a secure location and understand that I won't be able to see them again",
+ "COMPLETE_SETUP": "Complete Setup",
+ "CODES_COPIED": "Backup codes copied to clipboard"
+ },
+ "MANAGEMENT": {
+ "BACKUP_CODES": "Backup Codes",
+ "BACKUP_CODES_DESC": "Generate new codes if you've lost or used your existing ones",
+ "REGENERATE": "Regenerate Backup Codes",
+ "DISABLE_MFA": "Disable 2FA",
+ "DISABLE_MFA_DESC": "Remove two-factor authentication from your account",
+ "DISABLE_BUTTON": "Disable Two-Factor Authentication"
+ },
+ "DISABLE": {
+ "TITLE": "Disable Two-Factor Authentication",
+ "DESCRIPTION": "You'll need to enter your password and a verification code to disable two-factor authentication.",
+ "PASSWORD": "หรัสผ่าน",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Disable 2FA",
+ "CANCEL": "ยกเลิก",
+ "SUCCESS": "Two-factor authentication has been disabled",
+ "ERROR": "Failed to disable MFA. Please check your credentials."
+ },
+ "REGENERATE": {
+ "TITLE": "Regenerate Backup Codes",
+ "DESCRIPTION": "This will invalidate your existing backup codes and generate new ones. Enter your verification code to continue.",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Generate New Codes",
+ "CANCEL": "ยกเลิก",
+ "NEW_CODES_TITLE": "New Backup Codes Generated",
+ "NEW_CODES_DESC": "Your old backup codes have been invalidated. Save these new codes in a secure location.",
+ "CODES_IMPORTANT": "Important:",
+ "CODES_IMPORTANT_NOTE": " Each code can only be used once. Save them before closing this window.",
+ "DOWNLOAD_CODES": "Download Codes",
+ "COPY_ALL_CODES": "Copy All Codes",
+ "CODES_SAVED": "I've Saved My Codes",
+ "SUCCESS": "New backup codes have been generated",
+ "ERROR": "Failed to regenerate backup codes"
+ }
+ },
+ "MFA_VERIFICATION": {
+ "TITLE": "Two-Factor Authentication",
+ "DESCRIPTION": "Enter your verification code to continue",
+ "AUTHENTICATOR_APP": "Authenticator App",
+ "BACKUP_CODE": "Backup Code",
+ "ENTER_OTP_CODE": "Enter 6-digit code from your authenticator app",
+ "ENTER_BACKUP_CODE": "Enter one of your backup codes",
+ "BACKUP_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify",
+ "TRY_ANOTHER_METHOD": "Try another verification method",
+ "CANCEL_LOGIN": "Cancel and return to login",
+ "HELP_TEXT": "Having trouble signing in?",
+ "LEARN_MORE": "Learn more about 2FA",
+ "HELP_MODAL": {
+ "TITLE": "Two-Factor Authentication Help",
+ "AUTHENTICATOR_TITLE": "Using an Authenticator App",
+ "AUTHENTICATOR_DESC": "Open your authenticator app (Google Authenticator, Authy, etc.) and enter the 6-digit code shown for your account.",
+ "BACKUP_TITLE": "Using a Backup Code",
+ "BACKUP_DESC": "If you don't have access to your authenticator app, you can use one of the backup codes you saved when setting up 2FA. Each code can only be used once.",
+ "CONTACT_TITLE": "Need More Help?",
+ "CONTACT_DESC_CLOUD": "If you've lost access to both your authenticator app and backup codes, please reach out to Chatwoot support for assistance.",
+ "CONTACT_DESC_SELF_HOSTED": "If you've lost access to both your authenticator app and backup codes, please contact your administrator for assistance."
+ },
+ "VERIFICATION_FAILED": "Verification failed. Please try again."
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/th/settings.json b/app/javascript/dashboard/i18n/locale/th/settings.json
index 67004d623..49cb4e087 100644
--- a/app/javascript/dashboard/i18n/locale/th/settings.json
+++ b/app/javascript/dashboard/i18n/locale/th/settings.json
@@ -51,6 +51,13 @@
"LARGER": "Larger",
"EXTRA_LARGE": "Extra Large"
}
+ },
+ "LANGUAGE": {
+ "TITLE": "Preferred Language",
+ "NOTE": "Choose the language you want to use.",
+ "UPDATE_SUCCESS": "Your Language settings have been updated successfully",
+ "UPDATE_ERROR": "There is an error while updating the language settings, please try again",
+ "USE_ACCOUNT_DEFAULT": "Use account default"
}
},
"MESSAGE_SIGNATURE_SECTION": {
@@ -73,6 +80,11 @@
"NOTE": "การอัปเดตรหัสผ่านจะรีเซ็ทการเข้าสู่ระบบของบัญชีนี้ในอุปกรณ์อื่นๆ",
"BTN_TEXT": "เปลี่ยนรหัสผ่าน"
},
+ "SECURITY_SECTION": {
+ "TITLE": "Security",
+ "NOTE": "Manage additional security features for your account.",
+ "MFA_BUTTON": "Manage Two-Factor Authentication"
+ },
"ACCESS_TOKEN": {
"TITLE": "Access Token",
"NOTE": "คุณสามารถใช้ token นี้เชื่อมต่อกับ API ได้",
@@ -330,6 +342,7 @@
"REPORTS_LABEL": "ป้ายกำกับ",
"REPORTS_INBOX": "กล่องข้อความ",
"REPORTS_TEAM": "ทีม",
+ "AGENT_ASSIGNMENT": "Agent Assignment",
"SET_AVAILABILITY_TITLE": "ตั้งสถานะเป็น...",
"SET_YOUR_AVAILABILITY": "ตั้งค่าความพร้อมในการให้บริการ",
"SLA": "SLA",
@@ -350,7 +363,8 @@
"INFO_TEXT": "Let the system automatically mark you offline when you aren't using the app or dashboard.",
"INFO_SHORT": "Automatically mark offline when you aren't using the app."
},
- "DOCS": "Read docs"
+ "DOCS": "Read docs",
+ "SECURITY": "Security"
},
"BILLING_SETTINGS": {
"TITLE": "Billing",
@@ -382,6 +396,77 @@
},
"NO_BILLING_USER": "Your billing account is being configured. Please refresh the page and try again."
},
+ "SECURITY_SETTINGS": {
+ "TITLE": "Security",
+ "DESCRIPTION": "Manage your account security settings.",
+ "LINK_TEXT": "Learn more about SAML SSO",
+ "SAML": {
+ "TITLE": "SAML SSO",
+ "NOTE": "Configure SAML single sign-on for your account. Users will authenticate through your identity provider instead of using email/password.",
+ "ACS_URL": {
+ "LABEL": "ACS URL",
+ "TOOLTIP": "Assertion Consumer Service URL - Configure this URL in your IdP as the destination for SAML responses"
+ },
+ "SSO_URL": {
+ "LABEL": "SSO URL",
+ "HELP": "The URL where SAML authentication requests will be sent",
+ "PLACEHOLDER": "https://your-idp.com/saml/sso"
+ },
+ "CERTIFICATE": {
+ "LABEL": "Signing certificate in PEM format",
+ "HELP": "The public certificate from your identity provider used to verify SAML responses",
+ "PLACEHOLDER": "-----BEGIN CERTIFICATE-----\nMIIC..."
+ },
+ "FINGERPRINT": {
+ "LABEL": "Fingerprint",
+ "TOOLTIP": "SHA-1 fingerprint of the certificate - Use this to verify the certificate in your IdP configuration"
+ },
+ "COPY_SUCCESS": "คัดลอกไปยังคลิปบอร์ดเเล้ว",
+ "SP_ENTITY_ID": {
+ "LABEL": "SP Entity ID",
+ "HELP": "Unique identifier for this application as a service provider (auto-generated).",
+ "TOOLTIP": "Unique identifier for Chatwoot as the Service Provider - Configure this in your IdP settings"
+ },
+ "IDP_ENTITY_ID": {
+ "LABEL": "Identity Provider Entity ID",
+ "HELP": "Unique identifier for your identity provider (usually found in IdP configuration)",
+ "PLACEHOLDER": "https://your-idp.com/saml"
+ },
+ "UPDATE_BUTTON": "Update SAML Settings",
+ "API": {
+ "SUCCESS": "SAML settings updated successfully",
+ "ERROR": "Failed to update SAML settings",
+ "ERROR_LOADING": "Failed to load SAML settings",
+ "DISABLED": "SAML settings disabled successfully"
+ },
+ "VALIDATION": {
+ "REQUIRED_FIELDS": "SSO URL, Identity Provider Entity ID, and Certificate are required fields",
+ "SSO_URL_ERROR": "Please enter a valid SSO URL",
+ "CERTIFICATE_ERROR": "Certificate is required",
+ "IDP_ENTITY_ID_ERROR": "Identity Provider Entity ID is required"
+ },
+ "ENTERPRISE_PAYWALL": {
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade to an Enterprise plan to access SAML single sign-on and other advanced security features.",
+ "ASK_ADMIN": "Please reach out to your administrator for the upgrade."
+ },
+ "PAYWALL": {
+ "TITLE": "Upgrade to enable SAML SSO",
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade your plan to get access to SAML single sign-on and other advanced features.",
+ "UPGRADE_NOW": "Upgrade now",
+ "CANCEL_ANYTIME": "You can change or cancel your plan anytime"
+ },
+ "ATTRIBUTE_MAPPING": {
+ "TITLE": "SAML Attribute Setup",
+ "DESCRIPTION": "The following attribute mappings must be configured in your identity provider"
+ },
+ "INFO_SECTION": {
+ "TITLE": "Service Provider Information",
+ "TOOLTIP": "Copy these values and configure them in your Identity Provider to establish the SAML connection"
+ }
+ }
+ },
"CREATE_ACCOUNT": {
"NO_ACCOUNT_WARNING": "ไม่นะ! ดูเหมือนว่าเราจะไม่เจอบัญชี Chatwoot ของคุณ โปรดสร้างบัญชีใหม่เพื่อดำเนินการต่อ",
"NEW_ACCOUNT": "สร้างบัญชีใหม่",
@@ -418,5 +503,255 @@
"SWITCH_TO_REPLY": "สลับเป็นการตอบกลับ",
"TOGGLE_SNOOZE_DROPDOWN": "เปิดหรือปิดเมนูพักการสนทนา"
}
+ },
+ "ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent assignment",
+ "DESCRIPTION": "Define policies to effectively manage workload and route conversations based on the needs of inboxes and agents. Learn more here"
+ },
+ "ASSIGNMENT_POLICY": {
+ "TITLE": "Assignment policy",
+ "DESCRIPTION": "Manage how conversations get assigned in inboxes.",
+ "FEATURES": [
+ "Assign by conversations evenly or by available capacity",
+ "Add fair distribution rules to avoid overloading any agent",
+ "Add inboxes to a policy - one policy per inbox"
+ ]
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "TITLE": "Agent capacity policy",
+ "DESCRIPTION": "Manage workload for agents.",
+ "FEATURES": [
+ "Define maximum conversations per inbox",
+ "Create exceptions based on labels and time",
+ "Add agents to a policy - one policy per agent"
+ ]
+ }
+ },
+ "AGENT_ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Assignment policy",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "ORDER": "Order",
+ "PRIORITY": "Priority",
+ "ACTIVE": "ใช้งานอยู่",
+ "INACTIVE": "Inactive",
+ "POPOVER": "Added inboxes",
+ "EDIT": "เเก้ไข"
+ },
+ "NO_RECORDS_FOUND": "No assignment policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create assignment policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy created successfully",
+ "ERROR_MESSAGE": "Failed to create assignment policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit assignment policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_INBOX_DIALOG": {
+ "TITLE": "Add inbox",
+ "DESCRIPTION": "{inboxName} inbox is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "ยกเลิก"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update assignment policy"
+ },
+ "INBOX_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Inbox added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Inbox removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove inbox from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "คำอธิบาย:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "STATUS": {
+ "LABEL": "สถานะ:",
+ "PLACEHOLDER": "Select status",
+ "ACTIVE": "Policy is active",
+ "INACTIVE": "Policy is inactive"
+ },
+ "ASSIGNMENT_ORDER": {
+ "LABEL": "Assignment order",
+ "ROUND_ROBIN": {
+ "LABEL": "Round robin",
+ "DESCRIPTION": "Assign conversations evenly among agents."
+ },
+ "BALANCED": {
+ "LABEL": "Balanced",
+ "DESCRIPTION": "Assign conversations based on available capacity."
+ }
+ },
+ "ASSIGNMENT_PRIORITY": {
+ "LABEL": "Assignment priority",
+ "EARLIEST_CREATED": {
+ "LABEL": "Earliest created",
+ "DESCRIPTION": "The conversation that was created first gets assigned first."
+ },
+ "LONGEST_WAITING": {
+ "LABEL": "Longest waiting",
+ "DESCRIPTION": "The conversation waiting the longest gets assigned first."
+ }
+ },
+ "FAIR_DISTRIBUTION": {
+ "LABEL": "Fair distribution policy",
+ "DESCRIPTION": "Set the maximum number of conversations that can be assigned per agent within a time window to avoid overloading any one agent. This required field defaults to 100 conversations per hour.",
+ "INPUT_MAX": "Assign max",
+ "DURATION": "Conversations per agent in every"
+ },
+ "INBOXES": {
+ "LABEL": "Added inboxes",
+ "DESCRIPTION": "Add inboxes for which this policy will be applicable.",
+ "ADD_BUTTON": "Add inbox",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select inboxes to add",
+ "ADD_BUTTON": "เพิ่ม"
+ },
+ "EMPTY_STATE": "No inboxes added to this policy, add an inbox to get started",
+ "API": {
+ "SUCCESS_MESSAGE": "Inbox successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Assignment policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete assignment policy"
+ }
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent capacity",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "POPOVER": "Added agents",
+ "EDIT": "เเก้ไข"
+ },
+ "NO_RECORDS_FOUND": "No agent capacity policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create agent capacity policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy created successfully",
+ "ERROR_MESSAGE": "Failed to create agent capacity policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit agent capacity policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_AGENT_DIALOG": {
+ "TITLE": "Add agent",
+ "DESCRIPTION": "{agentName} is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "ยกเลิก"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update agent capacity policy"
+ },
+ "AGENT_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Agent added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Agent removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove agent from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "คำอธิบาย:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "INBOX_CAPACITY_LIMIT": {
+ "LABEL": "Inbox capacity limits",
+ "ADD_BUTTON": "Add inbox",
+ "FIELD": {
+ "SELECT_INBOX": "Select inbox",
+ "MAX_CONVERSATIONS": "Max conversations",
+ "SET_LIMIT": "Set limit"
+ },
+ "EMPTY_STATE": "No inbox limit set"
+ },
+ "EXCLUSION_RULES": {
+ "LABEL": "Exclusion rules",
+ "DESCRIPTION": "Conversations that satisfy the following conditions would not count towards agent capacity",
+ "TAGS": {
+ "LABEL": "Exclude conversations tagged with specific labels",
+ "ADD_TAG": "add tag",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select tags to add"
+ },
+ "EMPTY_STATE": "No tags added to this policy."
+ },
+ "DURATION": {
+ "LABEL": "Exclude conversations older than a specified duration",
+ "PLACEHOLDER": "Set time"
+ }
+ },
+ "USERS": {
+ "LABEL": "Assigned agents",
+ "DESCRIPTION": "Add agents for which this policy will be applicable.",
+ "ADD_BUTTON": "Add agent",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select agents to add",
+ "ADD_BUTTON": "เพิ่ม"
+ },
+ "EMPTY_STATE": "No agents added",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Agent capacity policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete agent capacity policy"
+ }
+ },
+ "DELETE_POLICY": {
+ "TITLE": "Delete policy",
+ "DESCRIPTION": "Are you sure you want to delete this policy? This action cannot be undone.",
+ "CONFIRM_BUTTON_LABEL": "ลบ",
+ "CANCEL_BUTTON_LABEL": "ยกเลิก"
+ }
}
}
diff --git a/app/javascript/dashboard/i18n/locale/th/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/th/whatsappTemplates.json
index 5f53faaa8..cf28312dc 100644
--- a/app/javascript/dashboard/i18n/locale/th/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/th/whatsappTemplates.json
@@ -40,6 +40,7 @@
"BUTTON_LABEL": "Button {index}",
"COUPON_CODE": "Enter coupon code (max 15 chars)",
"MEDIA_URL_LABEL": "Enter {type} URL",
+ "DOCUMENT_NAME_PLACEHOLDER": "Enter document filename (e.g., Invoice_2025.pdf)",
"BUTTON_PARAMETER": "Enter button parameter"
}
}
diff --git a/app/javascript/dashboard/i18n/locale/tl/automation.json b/app/javascript/dashboard/i18n/locale/tl/automation.json
index 80274f488..43245a1d5 100644
--- a/app/javascript/dashboard/i18n/locale/tl/automation.json
+++ b/app/javascript/dashboard/i18n/locale/tl/automation.json
@@ -177,7 +177,8 @@
"REFERER_LINK": "Referrer Link",
"ASSIGNEE_NAME": "Assignee",
"TEAM_NAME": "Team",
- "PRIORITY": "Priority"
+ "PRIORITY": "Priority",
+ "LABELS": "Labels"
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/tl/contact.json b/app/javascript/dashboard/i18n/locale/tl/contact.json
index 4dd081bd4..12b2d097e 100644
--- a/app/javascript/dashboard/i18n/locale/tl/contact.json
+++ b/app/javascript/dashboard/i18n/locale/tl/contact.json
@@ -610,6 +610,15 @@
"SEND_MESSAGE": "Send message"
}
},
+ "TWILIO_OPTIONS": {
+ "LABEL": "Select template",
+ "SEARCH_PLACEHOLDER": "Search templates",
+ "EMPTY_STATE": "No templates found",
+ "TEMPLATE_PARSER": {
+ "BACK": "Go back",
+ "SEND_MESSAGE": "Send message"
+ }
+ },
"ACTION_BUTTONS": {
"DISCARD": "Discard",
"SEND": "Send ({keyCode})"
diff --git a/app/javascript/dashboard/i18n/locale/tl/contactFilters.json b/app/javascript/dashboard/i18n/locale/tl/contactFilters.json
index bb3221c6e..4c62f0789 100644
--- a/app/javascript/dashboard/i18n/locale/tl/contactFilters.json
+++ b/app/javascript/dashboard/i18n/locale/tl/contactFilters.json
@@ -48,7 +48,8 @@
"CREATED_AT": "Created At",
"LAST_ACTIVITY": "Last Activity",
"REFERER_LINK": "Referrer link",
- "BLOCKED": "Blocked"
+ "BLOCKED": "Blocked",
+ "LABELS": "Labels"
},
"GROUPS": {
"STANDARD_FILTERS": "Standard Filters",
diff --git a/app/javascript/dashboard/i18n/locale/tl/contentTemplates.json b/app/javascript/dashboard/i18n/locale/tl/contentTemplates.json
new file mode 100644
index 000000000..a9b1d54c4
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/tl/contentTemplates.json
@@ -0,0 +1,51 @@
+{
+ "CONTENT_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Twilio Templates",
+ "SUBTITLE": "Select the Twilio template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Search Templates",
+ "NO_TEMPLATES_FOUND": "No templates found for",
+ "NO_CONTENT": "No content",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Category",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No Twilio templates available. Click refresh to sync templates from Twilio.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Language",
+ "TEMPLATE_BODY": "Template Body",
+ "CATEGORY": "Category"
+ },
+ "TYPES": {
+ "MEDIA": "Media",
+ "QUICK_REPLY": "Quick Reply",
+ "TEXT": "Text"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variables",
+ "LANGUAGE": "Language",
+ "CATEGORY": "Category",
+ "VARIABLE_PLACEHOLDER": "Enter {variable} value",
+ "GO_BACK_LABEL": "Go Back",
+ "SEND_MESSAGE_LABEL": "Send Message",
+ "FORM_ERROR_MESSAGE": "Please fill all variables before sending",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "MEDIA_URL_LABEL": "Enter full media URL",
+ "MEDIA_URL_PLACEHOLDER": "https://example.com/image.jpg"
+ },
+ "FORM": {
+ "BACK_BUTTON": "Back",
+ "SEND_MESSAGE_BUTTON": "Send Message"
+ }
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/tl/conversation.json b/app/javascript/dashboard/i18n/locale/tl/conversation.json
index 308f24f51..9fd39b70f 100644
--- a/app/javascript/dashboard/i18n/locale/tl/conversation.json
+++ b/app/javascript/dashboard/i18n/locale/tl/conversation.json
@@ -35,6 +35,11 @@
"API_HOURS_WINDOW": "You can only reply to this conversation within {hours} hours",
"NOT_ASSIGNED_TO_YOU": "This conversation is not assigned to you. Would you like to assign this conversation to yourself?",
"ASSIGN_TO_ME": "Assign to me",
+ "BOT_HANDOFF_MESSAGE": "You are responding to a conversation which is currently handled by an assistant or a bot.",
+ "BOT_HANDOFF_ACTION": "Mark open and assign to you",
+ "BOT_HANDOFF_REOPEN_ACTION": "Mark conversation open",
+ "BOT_HANDOFF_SUCCESS": "Conversation has been handed over to you",
+ "BOT_HANDOFF_ERROR": "Failed to take over the conversation. Please try again.",
"TWILIO_WHATSAPP_CAN_REPLY": "You can only reply to this conversation using a template message due to",
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "24 hour message window restriction",
"OLD_INSTAGRAM_INBOX_REPLY_BANNER": "This Instagram account was migrated to the new Instagram channel inbox. All new messages will show up there. You won’t be able to send messages from this conversation anymore.",
@@ -66,6 +71,17 @@
"SHOW_LABELS": "Show labels",
"HIDE_LABELS": "Hide labels"
},
+ "VOICE_CALL": {
+ "INCOMING_CALL": "Incoming call",
+ "OUTGOING_CALL": "Outgoing call",
+ "CALL_IN_PROGRESS": "Call in progress",
+ "NO_ANSWER": "No answer",
+ "MISSED_CALL": "Missed call",
+ "CALL_ENDED": "Call ended",
+ "NOT_ANSWERED_YET": "Not answered yet",
+ "THEY_ANSWERED": "They answered",
+ "YOU_ANSWERED": "You answered"
+ },
"HEADER": {
"RESOLVE_ACTION": "Resolve",
"REOPEN_ACTION": "Reopen",
diff --git a/app/javascript/dashboard/i18n/locale/tl/helpCenter.json b/app/javascript/dashboard/i18n/locale/tl/helpCenter.json
index fd2b1a788..0ab8d62ff 100644
--- a/app/javascript/dashboard/i18n/locale/tl/helpCenter.json
+++ b/app/javascript/dashboard/i18n/locale/tl/helpCenter.json
@@ -741,7 +741,8 @@
"LIVE_CHAT_WIDGET": {
"LABEL": "Live chat widget",
"PLACEHOLDER": "Select live chat widget",
- "HELP_TEXT": "Select a live chat widget that will appear on your help center"
+ "HELP_TEXT": "Select a live chat widget that will appear on your help center",
+ "NONE_OPTION": "No widget"
},
"BRAND_COLOR": {
"LABEL": "Brand color"
@@ -807,6 +808,58 @@
"ERROR_MESSAGE": "Unable to update portal"
}
}
+ },
+ "PDF_UPLOAD": {
+ "TITLE": "Upload PDF Document",
+ "DESCRIPTION": "Upload a PDF document to automatically generate FAQs using AI",
+ "DRAG_DROP_TEXT": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "ADDITIONAL_CONTEXT_LABEL": "Additional Context (Optional)",
+ "ADDITIONAL_CONTEXT_PLACEHOLDER": "Provide any additional context or instructions for FAQ generation...",
+ "UPLOADING": "Uploading...",
+ "UPLOAD": "Upload & Process",
+ "CANCEL": "Cancel",
+ "ERROR_INVALID_TYPE": "Please select a valid PDF file",
+ "ERROR_FILE_TOO_LARGE": "File size must be less than 512MB",
+ "ERROR_UPLOAD_FAILED": "Failed to upload PDF. Please try again."
+ },
+ "PDF_DOCUMENTS": {
+ "TITLE": "PDF Documents",
+ "DESCRIPTION": "Manage uploaded PDF documents and generate FAQs from them",
+ "UPLOAD_PDF": "Upload PDF",
+ "UPLOAD_FIRST_PDF": "Upload your first PDF",
+ "UPLOADED_BY": "Uploaded by",
+ "GENERATE_FAQS": "Generate FAQs",
+ "GENERATING": "Generating...",
+ "CONFIRM_DELETE": "Are you sure you want to delete {filename}?",
+ "EMPTY_STATE": {
+ "TITLE": "No PDF documents yet",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQs using AI"
+ },
+ "STATUS": {
+ "UPLOADED": "Ready",
+ "PROCESSING": "Processing",
+ "PROCESSED": "Completed",
+ "FAILED": "Failed"
+ }
+ },
+ "CONTENT_GENERATION": {
+ "TITLE": "Content Generation",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQ content using AI",
+ "UPLOAD_TITLE": "Upload PDF Document",
+ "DRAG_DROP": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "UPLOADING": "Processing document...",
+ "UPLOAD_SUCCESS": "Document processed successfully!",
+ "UPLOAD_ERROR": "Failed to upload document. Please try again.",
+ "INVALID_FILE_TYPE": "Please select a valid PDF file",
+ "FILE_TOO_LARGE": "File size must be less than 512MB",
+ "GENERATED_CONTENT": "Generated FAQ Content",
+ "PUBLISH_SELECTED": "Publish Selected",
+ "PUBLISHING": "Publishing...",
+ "FROM_DOCUMENT": "From document",
+ "NO_CONTENT": "No generated content available. Upload a PDF document to get started.",
+ "LOADING": "Loading generated content..."
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/tl/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/tl/inboxMgmt.json
index 6f44ec046..a525921db 100644
--- a/app/javascript/dashboard/i18n/locale/tl/inboxMgmt.json
+++ b/app/javascript/dashboard/i18n/locale/tl/inboxMgmt.json
@@ -272,8 +272,8 @@
},
"SUBMIT_BUTTON": "Create WhatsApp Channel",
"EMBEDDED_SIGNUP": {
- "TITLE": "Quick Setup with Meta",
- "DESC": "You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
+ "TITLE": "Quick setup with Meta",
+ "DESC": "Use the WhatsApp Embedded Signup flow to quickly connect new numbers. You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
"BENEFITS": {
"TITLE": "Benefits of Embedded Signup:",
"EASY_SETUP": "No manual configuration required",
@@ -281,9 +281,8 @@
"AUTO_CONFIG": "Automatic webhook and phone number configuration"
},
"LEARN_MORE": {
- "TEXT": "To learn more about integrated signup, pricing, and limitations, visit",
- "LINK_TEXT": "this link.",
- "LINK_URL": "https://developers.facebook.com/docs/whatsapp/embedded-signup/custom-flows/onboarding-business-app-users#limitations"
+ "TEXT": "To learn more about integrated signup, pricing, and limitations, visit {link}.",
+ "LINK_TEXT": "this link"
},
"SUBMIT_BUTTON": "Connect with WhatsApp Business",
"AUTH_PROCESSING": "Authenticating with Meta",
@@ -296,7 +295,9 @@
"INVALID_BUSINESS_DATA": "Invalid business data received from Facebook. Please try again.",
"SIGNUP_ERROR": "Signup error occurred",
"AUTH_NOT_COMPLETED": "Authentication not completed. Please restart the process.",
- "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured"
+ "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured",
+ "MANUAL_FALLBACK": "If your number is already connected to the WhatsApp Business Platform (API), or if you’re a tech provider onboarding your own number, please use the {link} flow",
+ "MANUAL_LINK_TEXT": "manual setup flow"
},
"API": {
"ERROR_MESSAGE": "We were not able to save the WhatsApp channel"
@@ -424,7 +425,51 @@
},
"AUTH": {
"TITLE": "Choose a channel",
- "DESC": "Chatwoot supports live-chat widgets, Facebook Messenger, WhatsApp, Emails, etc., as channels. If you want to build a custom channel, you can create it using the API channel. To get started, choose one of the channels below."
+ "DESC": "Chatwoot supports live-chat widgets, Facebook Messenger, WhatsApp, Emails, etc., as channels. If you want to build a custom channel, you can create it using the API channel. To get started, choose one of the channels below.",
+ "TITLE_NEXT": "Complete the setup",
+ "TITLE_FINISH": "Voilà!",
+ "CHANNEL": {
+ "WEBSITE": {
+ "TITLE": "Website",
+ "DESCRIPTION": "Create a live-chat widget"
+ },
+ "FACEBOOK": {
+ "TITLE": "Facebook",
+ "DESCRIPTION": "Connect your Facebook page"
+ },
+ "WHATSAPP": {
+ "TITLE": "WhatsApp",
+ "DESCRIPTION": "Support your customers on WhatsApp"
+ },
+ "EMAIL": {
+ "TITLE": "Email",
+ "DESCRIPTION": "Connect with Gmail, Outlook, or other providers"
+ },
+ "SMS": {
+ "TITLE": "SMS",
+ "DESCRIPTION": "Integrate SMS channel with Twilio or bandwidth"
+ },
+ "API": {
+ "TITLE": "API",
+ "DESCRIPTION": "Make a custom channel using our API"
+ },
+ "TELEGRAM": {
+ "TITLE": "Telegram",
+ "DESCRIPTION": "Configure Telegram channel using Bot token"
+ },
+ "LINE": {
+ "TITLE": "Line",
+ "DESCRIPTION": "Integrate your Line channel"
+ },
+ "INSTAGRAM": {
+ "TITLE": "Instagram",
+ "DESCRIPTION": "Connect your instagram account"
+ },
+ "VOICE": {
+ "TITLE": "Voice",
+ "DESCRIPTION": "Integrate with Twilio Voice"
+ }
+ }
},
"AGENTS": {
"TITLE": "Agents",
@@ -478,7 +523,10 @@
"MESSAGE": "You can now engage with your customers through your new Channel. Happy supporting",
"BUTTON_TEXT": "Take me there",
"MORE_SETTINGS": "More settings",
- "WEBSITE_SUCCESS": "You have successfully finished creating a website channel. Copy the code shown below and paste it on your website. Next time a customer use the live chat, the conversation will automatically appear on your inbox."
+ "WEBSITE_SUCCESS": "You have successfully finished creating a website channel. Copy the code shown below and paste it on your website. Next time a customer use the live chat, the conversation will automatically appear on your inbox.",
+ "WHATSAPP_QR_INSTRUCTION": "Scan the QR code above to quickly test your WhatsApp inbox",
+ "MESSENGER_QR_INSTRUCTION": "Scan the QR code above to quickly test your Facebook Messenger inbox",
+ "TELEGRAM_QR_INSTRUCTION": "Scan the QR code above to quickly test your Telegram inbox"
},
"REAUTH": "Reauthorize",
"VIEW": "View",
@@ -868,9 +916,18 @@
"SCRIPT_SETTINGS": "\n window.chatwootSettings = {options};"
},
"EMAIL_PROVIDERS": {
- "MICROSOFT": "Microsoft",
- "GOOGLE": "Google",
- "OTHER_PROVIDERS": "Other Providers"
+ "MICROSOFT": {
+ "TITLE": "Microsoft",
+ "DESCRIPTION": "Connect with Microsoft"
+ },
+ "GOOGLE": {
+ "TITLE": "Google",
+ "DESCRIPTION": "Connect with Google"
+ },
+ "OTHER_PROVIDERS": {
+ "TITLE": "Other Providers",
+ "DESCRIPTION": "Connect with Other Providers"
+ }
},
"CHANNELS": {
"MESSENGER": "Messenger",
diff --git a/app/javascript/dashboard/i18n/locale/tl/integrations.json b/app/javascript/dashboard/i18n/locale/tl/integrations.json
index be9281284..c59ec66df 100644
--- a/app/javascript/dashboard/i18n/locale/tl/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/tl/integrations.json
@@ -704,11 +704,28 @@
"ERROR_MESSAGE": "There was an error creating the document, please try again."
},
"FORM": {
+ "TYPE": {
+ "LABEL": "Document Type",
+ "URL": "URL",
+ "PDF": "PDF File"
+ },
"URL": {
"LABEL": "URL",
"PLACEHOLDER": "Enter the URL of the document",
"ERROR": "Please provide a valid URL for the document"
},
+ "PDF_FILE": {
+ "LABEL": "PDF File",
+ "CHOOSE_FILE": "Choose PDF file",
+ "ERROR": "Please select a PDF file",
+ "HELP_TEXT": "Maximum file size: 10MB",
+ "INVALID_TYPE": "Please select a valid PDF file",
+ "TOO_LARGE": "File size exceeds 10MB limit"
+ },
+ "NAME": {
+ "LABEL": "Document Name (Optional)",
+ "PLACEHOLDER": "Enter a name for the document"
+ },
"ASSISTANT": {
"LABEL": "Assistant",
"PLACEHOLDER": "Select the assistant",
@@ -744,6 +761,7 @@
"SELECTED": "{count} selected",
"SELECT_ALL": "Select all ({count})",
"UNSELECT_ALL": "Unselect all ({count})",
+ "SEARCH_PLACEHOLDER": "Search FAQs...",
"BULK_APPROVE_BUTTON": "Approve",
"BULK_DELETE_BUTTON": "Delete",
"BULK_APPROVE": {
diff --git a/app/javascript/dashboard/i18n/locale/tl/mfa.json b/app/javascript/dashboard/i18n/locale/tl/mfa.json
new file mode 100644
index 000000000..f7556fdcf
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/tl/mfa.json
@@ -0,0 +1,106 @@
+{
+ "MFA_SETTINGS": {
+ "TITLE": "Two-Factor Authentication",
+ "SUBTITLE": "Secure your account with TOTP-based authentication",
+ "DESCRIPTION": "Add an extra layer of security to your account using a time-based one-time password (TOTP)",
+ "STATUS_TITLE": "Authentication Status",
+ "STATUS_DESCRIPTION": "Manage your two-factor authentication settings and backup recovery codes",
+ "ENABLED": "Enabled",
+ "DISABLED": "Disabled",
+ "STATUS_ENABLED": "Two-factor authentication is active",
+ "STATUS_ENABLED_DESC": "Your account is protected with an additional layer of security",
+ "ENABLE_BUTTON": "Enable Two-Factor Authentication",
+ "ENHANCE_SECURITY": "Enhance Your Account Security",
+ "ENHANCE_SECURITY_DESC": "Two-factor authentication adds an extra layer of security by requiring a verification code from your authenticator app in addition to your password.",
+ "SETUP": {
+ "STEP_NUMBER_1": "1",
+ "STEP_NUMBER_2": "2",
+ "STEP1_TITLE": "Scan QR Code with Your Authenticator App",
+ "STEP1_DESCRIPTION": "Use Google Authenticator, Authy, or any TOTP-compatible app",
+ "LOADING_QR": "Loading...",
+ "MANUAL_ENTRY": "Can't scan? Enter code manually",
+ "SECRET_KEY": "Secret Key",
+ "COPY": "Copy",
+ "ENTER_CODE": "Enter the 6-digit code from your authenticator app",
+ "ENTER_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify & Continue",
+ "CANCEL": "Cancel",
+ "ERROR_STARTING": "MFA not enabled. Please contact administrator.",
+ "INVALID_CODE": "Invalid verification code",
+ "SECRET_COPIED": "Secret key copied to clipboard",
+ "SUCCESS": "Two-factor authentication has been enabled successfully"
+ },
+ "BACKUP": {
+ "TITLE": "Save Your Backup Codes",
+ "DESCRIPTION": "Keep these codes safe. Each can be used once if you lose access to your authenticator",
+ "IMPORTANT": "Important:",
+ "IMPORTANT_NOTE": " Save these codes in a secure location. You won't be able to see them again.",
+ "DOWNLOAD": "Download",
+ "COPY_ALL": "Copy All",
+ "CONFIRM": "I have saved my backup codes in a secure location and understand that I won't be able to see them again",
+ "COMPLETE_SETUP": "Complete Setup",
+ "CODES_COPIED": "Backup codes copied to clipboard"
+ },
+ "MANAGEMENT": {
+ "BACKUP_CODES": "Backup Codes",
+ "BACKUP_CODES_DESC": "Generate new codes if you've lost or used your existing ones",
+ "REGENERATE": "Regenerate Backup Codes",
+ "DISABLE_MFA": "Disable 2FA",
+ "DISABLE_MFA_DESC": "Remove two-factor authentication from your account",
+ "DISABLE_BUTTON": "Disable Two-Factor Authentication"
+ },
+ "DISABLE": {
+ "TITLE": "Disable Two-Factor Authentication",
+ "DESCRIPTION": "You'll need to enter your password and a verification code to disable two-factor authentication.",
+ "PASSWORD": "Password",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Disable 2FA",
+ "CANCEL": "Cancel",
+ "SUCCESS": "Two-factor authentication has been disabled",
+ "ERROR": "Failed to disable MFA. Please check your credentials."
+ },
+ "REGENERATE": {
+ "TITLE": "Regenerate Backup Codes",
+ "DESCRIPTION": "This will invalidate your existing backup codes and generate new ones. Enter your verification code to continue.",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Generate New Codes",
+ "CANCEL": "Cancel",
+ "NEW_CODES_TITLE": "New Backup Codes Generated",
+ "NEW_CODES_DESC": "Your old backup codes have been invalidated. Save these new codes in a secure location.",
+ "CODES_IMPORTANT": "Important:",
+ "CODES_IMPORTANT_NOTE": " Each code can only be used once. Save them before closing this window.",
+ "DOWNLOAD_CODES": "Download Codes",
+ "COPY_ALL_CODES": "Copy All Codes",
+ "CODES_SAVED": "I've Saved My Codes",
+ "SUCCESS": "New backup codes have been generated",
+ "ERROR": "Failed to regenerate backup codes"
+ }
+ },
+ "MFA_VERIFICATION": {
+ "TITLE": "Two-Factor Authentication",
+ "DESCRIPTION": "Enter your verification code to continue",
+ "AUTHENTICATOR_APP": "Authenticator App",
+ "BACKUP_CODE": "Backup Code",
+ "ENTER_OTP_CODE": "Enter 6-digit code from your authenticator app",
+ "ENTER_BACKUP_CODE": "Enter one of your backup codes",
+ "BACKUP_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify",
+ "TRY_ANOTHER_METHOD": "Try another verification method",
+ "CANCEL_LOGIN": "Cancel and return to login",
+ "HELP_TEXT": "Having trouble signing in?",
+ "LEARN_MORE": "Learn more about 2FA",
+ "HELP_MODAL": {
+ "TITLE": "Two-Factor Authentication Help",
+ "AUTHENTICATOR_TITLE": "Using an Authenticator App",
+ "AUTHENTICATOR_DESC": "Open your authenticator app (Google Authenticator, Authy, etc.) and enter the 6-digit code shown for your account.",
+ "BACKUP_TITLE": "Using a Backup Code",
+ "BACKUP_DESC": "If you don't have access to your authenticator app, you can use one of the backup codes you saved when setting up 2FA. Each code can only be used once.",
+ "CONTACT_TITLE": "Need More Help?",
+ "CONTACT_DESC_CLOUD": "If you've lost access to both your authenticator app and backup codes, please reach out to Chatwoot support for assistance.",
+ "CONTACT_DESC_SELF_HOSTED": "If you've lost access to both your authenticator app and backup codes, please contact your administrator for assistance."
+ },
+ "VERIFICATION_FAILED": "Verification failed. Please try again."
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/tl/settings.json b/app/javascript/dashboard/i18n/locale/tl/settings.json
index d547538db..9ddc3b805 100644
--- a/app/javascript/dashboard/i18n/locale/tl/settings.json
+++ b/app/javascript/dashboard/i18n/locale/tl/settings.json
@@ -51,6 +51,13 @@
"LARGER": "Larger",
"EXTRA_LARGE": "Extra Large"
}
+ },
+ "LANGUAGE": {
+ "TITLE": "Preferred Language",
+ "NOTE": "Choose the language you want to use.",
+ "UPDATE_SUCCESS": "Your Language settings have been updated successfully",
+ "UPDATE_ERROR": "There is an error while updating the language settings, please try again",
+ "USE_ACCOUNT_DEFAULT": "Use account default"
}
},
"MESSAGE_SIGNATURE_SECTION": {
@@ -73,6 +80,11 @@
"NOTE": "Updating your password would reset your logins in multiple devices.",
"BTN_TEXT": "Change password"
},
+ "SECURITY_SECTION": {
+ "TITLE": "Security",
+ "NOTE": "Manage additional security features for your account.",
+ "MFA_BUTTON": "Manage Two-Factor Authentication"
+ },
"ACCESS_TOKEN": {
"TITLE": "Access Token",
"NOTE": "This token can be used if you are building an API based integration",
@@ -330,6 +342,7 @@
"REPORTS_LABEL": "Labels",
"REPORTS_INBOX": "Inbox",
"REPORTS_TEAM": "Team",
+ "AGENT_ASSIGNMENT": "Agent Assignment",
"SET_AVAILABILITY_TITLE": "Set yourself as",
"SET_YOUR_AVAILABILITY": "Set your availability",
"SLA": "SLA",
@@ -350,7 +363,8 @@
"INFO_TEXT": "Let the system automatically mark you offline when you aren't using the app or dashboard.",
"INFO_SHORT": "Automatically mark offline when you aren't using the app."
},
- "DOCS": "Read docs"
+ "DOCS": "Read docs",
+ "SECURITY": "Security"
},
"BILLING_SETTINGS": {
"TITLE": "Billing",
@@ -382,6 +396,77 @@
},
"NO_BILLING_USER": "Your billing account is being configured. Please refresh the page and try again."
},
+ "SECURITY_SETTINGS": {
+ "TITLE": "Security",
+ "DESCRIPTION": "Manage your account security settings.",
+ "LINK_TEXT": "Learn more about SAML SSO",
+ "SAML": {
+ "TITLE": "SAML SSO",
+ "NOTE": "Configure SAML single sign-on for your account. Users will authenticate through your identity provider instead of using email/password.",
+ "ACS_URL": {
+ "LABEL": "ACS URL",
+ "TOOLTIP": "Assertion Consumer Service URL - Configure this URL in your IdP as the destination for SAML responses"
+ },
+ "SSO_URL": {
+ "LABEL": "SSO URL",
+ "HELP": "The URL where SAML authentication requests will be sent",
+ "PLACEHOLDER": "https://your-idp.com/saml/sso"
+ },
+ "CERTIFICATE": {
+ "LABEL": "Signing certificate in PEM format",
+ "HELP": "The public certificate from your identity provider used to verify SAML responses",
+ "PLACEHOLDER": "-----BEGIN CERTIFICATE-----\nMIIC..."
+ },
+ "FINGERPRINT": {
+ "LABEL": "Fingerprint",
+ "TOOLTIP": "SHA-1 fingerprint of the certificate - Use this to verify the certificate in your IdP configuration"
+ },
+ "COPY_SUCCESS": "Copied to clipboard",
+ "SP_ENTITY_ID": {
+ "LABEL": "SP Entity ID",
+ "HELP": "Unique identifier for this application as a service provider (auto-generated).",
+ "TOOLTIP": "Unique identifier for Chatwoot as the Service Provider - Configure this in your IdP settings"
+ },
+ "IDP_ENTITY_ID": {
+ "LABEL": "Identity Provider Entity ID",
+ "HELP": "Unique identifier for your identity provider (usually found in IdP configuration)",
+ "PLACEHOLDER": "https://your-idp.com/saml"
+ },
+ "UPDATE_BUTTON": "Update SAML Settings",
+ "API": {
+ "SUCCESS": "SAML settings updated successfully",
+ "ERROR": "Failed to update SAML settings",
+ "ERROR_LOADING": "Failed to load SAML settings",
+ "DISABLED": "SAML settings disabled successfully"
+ },
+ "VALIDATION": {
+ "REQUIRED_FIELDS": "SSO URL, Identity Provider Entity ID, and Certificate are required fields",
+ "SSO_URL_ERROR": "Please enter a valid SSO URL",
+ "CERTIFICATE_ERROR": "Certificate is required",
+ "IDP_ENTITY_ID_ERROR": "Identity Provider Entity ID is required"
+ },
+ "ENTERPRISE_PAYWALL": {
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade to an Enterprise plan to access SAML single sign-on and other advanced security features.",
+ "ASK_ADMIN": "Please reach out to your administrator for the upgrade."
+ },
+ "PAYWALL": {
+ "TITLE": "Upgrade to enable SAML SSO",
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade your plan to get access to SAML single sign-on and other advanced features.",
+ "UPGRADE_NOW": "Upgrade now",
+ "CANCEL_ANYTIME": "You can change or cancel your plan anytime"
+ },
+ "ATTRIBUTE_MAPPING": {
+ "TITLE": "SAML Attribute Setup",
+ "DESCRIPTION": "The following attribute mappings must be configured in your identity provider"
+ },
+ "INFO_SECTION": {
+ "TITLE": "Service Provider Information",
+ "TOOLTIP": "Copy these values and configure them in your Identity Provider to establish the SAML connection"
+ }
+ }
+ },
"CREATE_ACCOUNT": {
"NO_ACCOUNT_WARNING": "Uh oh! We could not find any Chatwoot accounts. Please create a new account to continue.",
"NEW_ACCOUNT": "New Account",
@@ -418,5 +503,255 @@
"SWITCH_TO_REPLY": "Switch to Reply",
"TOGGLE_SNOOZE_DROPDOWN": "Toggle snooze dropdown"
}
+ },
+ "ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent assignment",
+ "DESCRIPTION": "Define policies to effectively manage workload and route conversations based on the needs of inboxes and agents. Learn more here"
+ },
+ "ASSIGNMENT_POLICY": {
+ "TITLE": "Assignment policy",
+ "DESCRIPTION": "Manage how conversations get assigned in inboxes.",
+ "FEATURES": [
+ "Assign by conversations evenly or by available capacity",
+ "Add fair distribution rules to avoid overloading any agent",
+ "Add inboxes to a policy - one policy per inbox"
+ ]
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "TITLE": "Agent capacity policy",
+ "DESCRIPTION": "Manage workload for agents.",
+ "FEATURES": [
+ "Define maximum conversations per inbox",
+ "Create exceptions based on labels and time",
+ "Add agents to a policy - one policy per agent"
+ ]
+ }
+ },
+ "AGENT_ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Assignment policy",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "ORDER": "Order",
+ "PRIORITY": "Priority",
+ "ACTIVE": "Active",
+ "INACTIVE": "Inactive",
+ "POPOVER": "Added inboxes",
+ "EDIT": "Edit"
+ },
+ "NO_RECORDS_FOUND": "No assignment policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create assignment policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy created successfully",
+ "ERROR_MESSAGE": "Failed to create assignment policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit assignment policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_INBOX_DIALOG": {
+ "TITLE": "Add inbox",
+ "DESCRIPTION": "{inboxName} inbox is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Cancel"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update assignment policy"
+ },
+ "INBOX_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Inbox added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Inbox removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove inbox from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Description:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "STATUS": {
+ "LABEL": "Status:",
+ "PLACEHOLDER": "Select status",
+ "ACTIVE": "Policy is active",
+ "INACTIVE": "Policy is inactive"
+ },
+ "ASSIGNMENT_ORDER": {
+ "LABEL": "Assignment order",
+ "ROUND_ROBIN": {
+ "LABEL": "Round robin",
+ "DESCRIPTION": "Assign conversations evenly among agents."
+ },
+ "BALANCED": {
+ "LABEL": "Balanced",
+ "DESCRIPTION": "Assign conversations based on available capacity."
+ }
+ },
+ "ASSIGNMENT_PRIORITY": {
+ "LABEL": "Assignment priority",
+ "EARLIEST_CREATED": {
+ "LABEL": "Earliest created",
+ "DESCRIPTION": "The conversation that was created first gets assigned first."
+ },
+ "LONGEST_WAITING": {
+ "LABEL": "Longest waiting",
+ "DESCRIPTION": "The conversation waiting the longest gets assigned first."
+ }
+ },
+ "FAIR_DISTRIBUTION": {
+ "LABEL": "Fair distribution policy",
+ "DESCRIPTION": "Set the maximum number of conversations that can be assigned per agent within a time window to avoid overloading any one agent. This required field defaults to 100 conversations per hour.",
+ "INPUT_MAX": "Assign max",
+ "DURATION": "Conversations per agent in every"
+ },
+ "INBOXES": {
+ "LABEL": "Added inboxes",
+ "DESCRIPTION": "Add inboxes for which this policy will be applicable.",
+ "ADD_BUTTON": "Add inbox",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select inboxes to add",
+ "ADD_BUTTON": "Add"
+ },
+ "EMPTY_STATE": "No inboxes added to this policy, add an inbox to get started",
+ "API": {
+ "SUCCESS_MESSAGE": "Inbox successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Assignment policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete assignment policy"
+ }
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent capacity",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "POPOVER": "Added agents",
+ "EDIT": "Edit"
+ },
+ "NO_RECORDS_FOUND": "No agent capacity policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create agent capacity policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy created successfully",
+ "ERROR_MESSAGE": "Failed to create agent capacity policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit agent capacity policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_AGENT_DIALOG": {
+ "TITLE": "Add agent",
+ "DESCRIPTION": "{agentName} is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Cancel"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update agent capacity policy"
+ },
+ "AGENT_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Agent added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Agent removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove agent from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Description:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "INBOX_CAPACITY_LIMIT": {
+ "LABEL": "Inbox capacity limits",
+ "ADD_BUTTON": "Add inbox",
+ "FIELD": {
+ "SELECT_INBOX": "Select inbox",
+ "MAX_CONVERSATIONS": "Max conversations",
+ "SET_LIMIT": "Set limit"
+ },
+ "EMPTY_STATE": "No inbox limit set"
+ },
+ "EXCLUSION_RULES": {
+ "LABEL": "Exclusion rules",
+ "DESCRIPTION": "Conversations that satisfy the following conditions would not count towards agent capacity",
+ "TAGS": {
+ "LABEL": "Exclude conversations tagged with specific labels",
+ "ADD_TAG": "add tag",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select tags to add"
+ },
+ "EMPTY_STATE": "No tags added to this policy."
+ },
+ "DURATION": {
+ "LABEL": "Exclude conversations older than a specified duration",
+ "PLACEHOLDER": "Set time"
+ }
+ },
+ "USERS": {
+ "LABEL": "Assigned agents",
+ "DESCRIPTION": "Add agents for which this policy will be applicable.",
+ "ADD_BUTTON": "Add agent",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select agents to add",
+ "ADD_BUTTON": "Add"
+ },
+ "EMPTY_STATE": "No agents added",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Agent capacity policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete agent capacity policy"
+ }
+ },
+ "DELETE_POLICY": {
+ "TITLE": "Delete policy",
+ "DESCRIPTION": "Are you sure you want to delete this policy? This action cannot be undone.",
+ "CONFIRM_BUTTON_LABEL": "Delete",
+ "CANCEL_BUTTON_LABEL": "Cancel"
+ }
}
}
diff --git a/app/javascript/dashboard/i18n/locale/tl/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/tl/whatsappTemplates.json
index 5f53faaa8..cf28312dc 100644
--- a/app/javascript/dashboard/i18n/locale/tl/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/tl/whatsappTemplates.json
@@ -40,6 +40,7 @@
"BUTTON_LABEL": "Button {index}",
"COUPON_CODE": "Enter coupon code (max 15 chars)",
"MEDIA_URL_LABEL": "Enter {type} URL",
+ "DOCUMENT_NAME_PLACEHOLDER": "Enter document filename (e.g., Invoice_2025.pdf)",
"BUTTON_PARAMETER": "Enter button parameter"
}
}
diff --git a/app/javascript/dashboard/i18n/locale/tr/automation.json b/app/javascript/dashboard/i18n/locale/tr/automation.json
index 3bac1c990..7d5464304 100644
--- a/app/javascript/dashboard/i18n/locale/tr/automation.json
+++ b/app/javascript/dashboard/i18n/locale/tr/automation.json
@@ -177,7 +177,8 @@
"REFERER_LINK": "Yönlendiren Bağlantı",
"ASSIGNEE_NAME": "Assignee",
"TEAM_NAME": "Ekip",
- "PRIORITY": "Öncelik"
+ "PRIORITY": "Öncelik",
+ "LABELS": "Etiketler"
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/tr/contact.json b/app/javascript/dashboard/i18n/locale/tr/contact.json
index 59d0d9d66..982575725 100644
--- a/app/javascript/dashboard/i18n/locale/tr/contact.json
+++ b/app/javascript/dashboard/i18n/locale/tr/contact.json
@@ -610,6 +610,15 @@
"SEND_MESSAGE": "Mesajı Gönder"
}
},
+ "TWILIO_OPTIONS": {
+ "LABEL": "Select template",
+ "SEARCH_PLACEHOLDER": "Search templates",
+ "EMPTY_STATE": "No templates found",
+ "TEMPLATE_PARSER": {
+ "BACK": "Geri git",
+ "SEND_MESSAGE": "Mesajı Gönder"
+ }
+ },
"ACTION_BUTTONS": {
"DISCARD": "Discard",
"SEND": "Send ({keyCode})"
diff --git a/app/javascript/dashboard/i18n/locale/tr/contactFilters.json b/app/javascript/dashboard/i18n/locale/tr/contactFilters.json
index 9c2a1d280..1b022a3e3 100644
--- a/app/javascript/dashboard/i18n/locale/tr/contactFilters.json
+++ b/app/javascript/dashboard/i18n/locale/tr/contactFilters.json
@@ -48,7 +48,8 @@
"CREATED_AT": "Oluşturma",
"LAST_ACTIVITY": "Son aktivite",
"REFERER_LINK": "Yönlendiren bağlantısı",
- "BLOCKED": "Blocked"
+ "BLOCKED": "Blocked",
+ "LABELS": "Etiketler"
},
"GROUPS": {
"STANDARD_FILTERS": "Standart Filtreler",
diff --git a/app/javascript/dashboard/i18n/locale/tr/contentTemplates.json b/app/javascript/dashboard/i18n/locale/tr/contentTemplates.json
new file mode 100644
index 000000000..f0218d6d4
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/tr/contentTemplates.json
@@ -0,0 +1,51 @@
+{
+ "CONTENT_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Twilio Templates",
+ "SUBTITLE": "Select the Twilio template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Şablon Ara",
+ "NO_TEMPLATES_FOUND": "İçin hiç şablon bulunamadı",
+ "NO_CONTENT": "No content",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Kategori",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No Twilio templates available. Click refresh to sync templates from Twilio.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Dil",
+ "TEMPLATE_BODY": "Şablon İçeriği",
+ "CATEGORY": "Kategori"
+ },
+ "TYPES": {
+ "MEDIA": "Media",
+ "QUICK_REPLY": "Quick Reply",
+ "TEXT": "Metin"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Değişkenler",
+ "LANGUAGE": "Dil",
+ "CATEGORY": "Kategori",
+ "VARIABLE_PLACEHOLDER": "{variable} değerini girin",
+ "GO_BACK_LABEL": "Geri Git",
+ "SEND_MESSAGE_LABEL": "Mesaj Gönder",
+ "FORM_ERROR_MESSAGE": "Lütfen göndermeden önce tüm değişkenleri doldurun",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "MEDIA_URL_LABEL": "Enter full media URL",
+ "MEDIA_URL_PLACEHOLDER": "https://example.com/image.jpg"
+ },
+ "FORM": {
+ "BACK_BUTTON": "Geri",
+ "SEND_MESSAGE_BUTTON": "Mesaj Gönder"
+ }
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/tr/conversation.json b/app/javascript/dashboard/i18n/locale/tr/conversation.json
index 4fd308d44..f80e4c33c 100644
--- a/app/javascript/dashboard/i18n/locale/tr/conversation.json
+++ b/app/javascript/dashboard/i18n/locale/tr/conversation.json
@@ -35,6 +35,11 @@
"API_HOURS_WINDOW": "Bu sohbete yalnızca {hours} saat içinde yanıt verebilirsiniz",
"NOT_ASSIGNED_TO_YOU": "Bu görüşme size atanmamış. Bu konuşmayı kendinize atamak ister misiniz?",
"ASSIGN_TO_ME": "Bana ata",
+ "BOT_HANDOFF_MESSAGE": "You are responding to a conversation which is currently handled by an assistant or a bot.",
+ "BOT_HANDOFF_ACTION": "Mark open and assign to you",
+ "BOT_HANDOFF_REOPEN_ACTION": "Mark conversation open",
+ "BOT_HANDOFF_SUCCESS": "Conversation has been handed over to you",
+ "BOT_HANDOFF_ERROR": "Failed to take over the conversation. Please try again.",
"TWILIO_WHATSAPP_CAN_REPLY": "Bu konuşmaya yalnızca şablon mesaj kullanarak yanıt verebilirsiniz, çünkü",
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "24 saat mesaj penceresi kısıtlaması",
"OLD_INSTAGRAM_INBOX_REPLY_BANNER": "Bu Instagram hesabı yeni Instagram kanal gelen kutusuna taşındı. Tüm yeni mesajlar orada görünecek. Bu sohbetten artık mesaj gönderemezsiniz.",
@@ -66,6 +71,17 @@
"SHOW_LABELS": "Etiketleri Göster",
"HIDE_LABELS": "Etiketleri Gizle"
},
+ "VOICE_CALL": {
+ "INCOMING_CALL": "Incoming call",
+ "OUTGOING_CALL": "Outgoing call",
+ "CALL_IN_PROGRESS": "Call in progress",
+ "NO_ANSWER": "No answer",
+ "MISSED_CALL": "Missed call",
+ "CALL_ENDED": "Call ended",
+ "NOT_ANSWERED_YET": "Not answered yet",
+ "THEY_ANSWERED": "They answered",
+ "YOU_ANSWERED": "You answered"
+ },
"HEADER": {
"RESOLVE_ACTION": "Çözüldü",
"REOPEN_ACTION": "Yeniden aç",
diff --git a/app/javascript/dashboard/i18n/locale/tr/helpCenter.json b/app/javascript/dashboard/i18n/locale/tr/helpCenter.json
index 59555ecec..c07a20870 100644
--- a/app/javascript/dashboard/i18n/locale/tr/helpCenter.json
+++ b/app/javascript/dashboard/i18n/locale/tr/helpCenter.json
@@ -741,7 +741,8 @@
"LIVE_CHAT_WIDGET": {
"LABEL": "Live chat widget",
"PLACEHOLDER": "Select live chat widget",
- "HELP_TEXT": "Select a live chat widget that will appear on your help center"
+ "HELP_TEXT": "Select a live chat widget that will appear on your help center",
+ "NONE_OPTION": "No widget"
},
"BRAND_COLOR": {
"LABEL": "Brand color"
@@ -807,6 +808,58 @@
"ERROR_MESSAGE": "Unable to update portal"
}
}
+ },
+ "PDF_UPLOAD": {
+ "TITLE": "Upload PDF Document",
+ "DESCRIPTION": "Upload a PDF document to automatically generate FAQs using AI",
+ "DRAG_DROP_TEXT": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "ADDITIONAL_CONTEXT_LABEL": "Additional Context (Optional)",
+ "ADDITIONAL_CONTEXT_PLACEHOLDER": "Provide any additional context or instructions for FAQ generation...",
+ "UPLOADING": "Yükleniyor ...",
+ "UPLOAD": "Upload & Process",
+ "CANCEL": "İptal Et",
+ "ERROR_INVALID_TYPE": "Please select a valid PDF file",
+ "ERROR_FILE_TOO_LARGE": "File size must be less than 512MB",
+ "ERROR_UPLOAD_FAILED": "Failed to upload PDF. Please try again."
+ },
+ "PDF_DOCUMENTS": {
+ "TITLE": "PDF Documents",
+ "DESCRIPTION": "Manage uploaded PDF documents and generate FAQs from them",
+ "UPLOAD_PDF": "Upload PDF",
+ "UPLOAD_FIRST_PDF": "Upload your first PDF",
+ "UPLOADED_BY": "Uploaded by",
+ "GENERATE_FAQS": "Generate FAQs",
+ "GENERATING": "Oluşturuluyor...",
+ "CONFIRM_DELETE": "{filename} silmek istediğinizden emin misiniz?",
+ "EMPTY_STATE": {
+ "TITLE": "No PDF documents yet",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQs using AI"
+ },
+ "STATUS": {
+ "UPLOADED": "Ready",
+ "PROCESSING": "Processing",
+ "PROCESSED": "Tamamlandı",
+ "FAILED": "Failed"
+ }
+ },
+ "CONTENT_GENERATION": {
+ "TITLE": "Content Generation",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQ content using AI",
+ "UPLOAD_TITLE": "Upload PDF Document",
+ "DRAG_DROP": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "UPLOADING": "Processing document...",
+ "UPLOAD_SUCCESS": "Document processed successfully!",
+ "UPLOAD_ERROR": "Failed to upload document. Please try again.",
+ "INVALID_FILE_TYPE": "Please select a valid PDF file",
+ "FILE_TOO_LARGE": "File size must be less than 512MB",
+ "GENERATED_CONTENT": "Generated FAQ Content",
+ "PUBLISH_SELECTED": "Publish Selected",
+ "PUBLISHING": "Publishing...",
+ "FROM_DOCUMENT": "From document",
+ "NO_CONTENT": "No generated content available. Upload a PDF document to get started.",
+ "LOADING": "Loading generated content..."
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/tr/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/tr/inboxMgmt.json
index c29f24673..0b89db391 100644
--- a/app/javascript/dashboard/i18n/locale/tr/inboxMgmt.json
+++ b/app/javascript/dashboard/i18n/locale/tr/inboxMgmt.json
@@ -272,8 +272,8 @@
},
"SUBMIT_BUTTON": "WhatsApp Kanalı Oluştur",
"EMBEDDED_SIGNUP": {
- "TITLE": "Quick Setup with Meta",
- "DESC": "You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
+ "TITLE": "Quick setup with Meta",
+ "DESC": "Use the WhatsApp Embedded Signup flow to quickly connect new numbers. You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
"BENEFITS": {
"TITLE": "Benefits of Embedded Signup:",
"EASY_SETUP": "No manual configuration required",
@@ -281,9 +281,8 @@
"AUTO_CONFIG": "Automatic webhook and phone number configuration"
},
"LEARN_MORE": {
- "TEXT": "To learn more about integrated signup, pricing, and limitations, visit",
- "LINK_TEXT": "this link.",
- "LINK_URL": "https://developers.facebook.com/docs/whatsapp/embedded-signup/custom-flows/onboarding-business-app-users#limitations"
+ "TEXT": "To learn more about integrated signup, pricing, and limitations, visit {link}.",
+ "LINK_TEXT": "this link"
},
"SUBMIT_BUTTON": "Connect with WhatsApp Business",
"AUTH_PROCESSING": "Authenticating with Meta",
@@ -296,7 +295,9 @@
"INVALID_BUSINESS_DATA": "Invalid business data received from Facebook. Please try again.",
"SIGNUP_ERROR": "Signup error occurred",
"AUTH_NOT_COMPLETED": "Authentication not completed. Please restart the process.",
- "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured"
+ "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured",
+ "MANUAL_FALLBACK": "If your number is already connected to the WhatsApp Business Platform (API), or if you’re a tech provider onboarding your own number, please use the {link} flow",
+ "MANUAL_LINK_TEXT": "manual setup flow"
},
"API": {
"ERROR_MESSAGE": "WhatsApp kanalını kaydedemedik"
@@ -424,7 +425,51 @@
},
"AUTH": {
"TITLE": "Bir Kanal Seçin",
- "DESC": "Chatwoot, canlı sohbet widget'ları, Facebook Messenger, Twitter profilleri, WhatsApp, E-postalar vb. olarak kanalları destekler. Özel bir kanal oluşturmak istiyorsanız, API kanalını kullanarak bunu oluşturabilirsiniz. Başlamak için aşağıdaki kanallardan birini seçin."
+ "DESC": "Chatwoot, canlı sohbet widget'ları, Facebook Messenger, Twitter profilleri, WhatsApp, E-postalar vb. olarak kanalları destekler. Özel bir kanal oluşturmak istiyorsanız, API kanalını kullanarak bunu oluşturabilirsiniz. Başlamak için aşağıdaki kanallardan birini seçin.",
+ "TITLE_NEXT": "Complete the setup",
+ "TITLE_FINISH": "Voilà!",
+ "CHANNEL": {
+ "WEBSITE": {
+ "TITLE": "Website",
+ "DESCRIPTION": "Create a live-chat widget"
+ },
+ "FACEBOOK": {
+ "TITLE": "Facebook\n",
+ "DESCRIPTION": "Connect your Facebook page"
+ },
+ "WHATSAPP": {
+ "TITLE": "WhatsApp",
+ "DESCRIPTION": "Support your customers on WhatsApp"
+ },
+ "EMAIL": {
+ "TITLE": "E-Posta",
+ "DESCRIPTION": "Connect with Gmail, Outlook, or other providers"
+ },
+ "SMS": {
+ "TITLE": "SMS",
+ "DESCRIPTION": "Integrate SMS channel with Twilio or bandwidth"
+ },
+ "API": {
+ "TITLE": "API",
+ "DESCRIPTION": "Make a custom channel using our API"
+ },
+ "TELEGRAM": {
+ "TITLE": "Telegram",
+ "DESCRIPTION": "Configure Telegram channel using Bot token"
+ },
+ "LINE": {
+ "TITLE": "Line",
+ "DESCRIPTION": "Integrate your Line channel"
+ },
+ "INSTAGRAM": {
+ "TITLE": "Instagram",
+ "DESCRIPTION": "Connect your instagram account"
+ },
+ "VOICE": {
+ "TITLE": "Voice",
+ "DESCRIPTION": "Integrate with Twilio Voice"
+ }
+ }
},
"AGENTS": {
"TITLE": "Kullanıcılar",
@@ -478,7 +523,10 @@
"MESSAGE": "Artık yeni kanalınız aracılığıyla müşterilerinizle iletişim kurabilirsiniz. Mutlu destekleme!",
"BUTTON_TEXT": "Beni oraya götür",
"MORE_SETTINGS": "Daha fazla ayar",
- "WEBSITE_SUCCESS": "Bir web sitesi kanalı oluşturmayı başarıyla tamamladınız. Aşağıda gösterilen kodu kopyalayın ve web sitenize yapıştırın. Bir müşteri canlı sohbeti bir dahaki sefere kullandığında, konuşma otomatik olarak gelen kutunuzda görünecektir."
+ "WEBSITE_SUCCESS": "Bir web sitesi kanalı oluşturmayı başarıyla tamamladınız. Aşağıda gösterilen kodu kopyalayın ve web sitenize yapıştırın. Bir müşteri canlı sohbeti bir dahaki sefere kullandığında, konuşma otomatik olarak gelen kutunuzda görünecektir.",
+ "WHATSAPP_QR_INSTRUCTION": "Scan the QR code above to quickly test your WhatsApp inbox",
+ "MESSENGER_QR_INSTRUCTION": "Scan the QR code above to quickly test your Facebook Messenger inbox",
+ "TELEGRAM_QR_INSTRUCTION": "Scan the QR code above to quickly test your Telegram inbox"
},
"REAUTH": "Yeniden yetkilendir",
"VIEW": "Görünüm",
@@ -868,9 +916,18 @@
"SCRIPT_SETTINGS": "\nwindow.chatwootSettings = {options};"
},
"EMAIL_PROVIDERS": {
- "MICROSOFT": "Microsoft",
- "GOOGLE": "Google",
- "OTHER_PROVIDERS": "Diğer Sağlayıcılar"
+ "MICROSOFT": {
+ "TITLE": "Microsoft",
+ "DESCRIPTION": "Connect with Microsoft"
+ },
+ "GOOGLE": {
+ "TITLE": "Google",
+ "DESCRIPTION": "Connect with Google"
+ },
+ "OTHER_PROVIDERS": {
+ "TITLE": "Diğer Sağlayıcılar",
+ "DESCRIPTION": "Connect with Other Providers"
+ }
},
"CHANNELS": {
"MESSENGER": "Messenger",
diff --git a/app/javascript/dashboard/i18n/locale/tr/integrations.json b/app/javascript/dashboard/i18n/locale/tr/integrations.json
index d414ea777..bcc877100 100644
--- a/app/javascript/dashboard/i18n/locale/tr/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/tr/integrations.json
@@ -704,11 +704,28 @@
"ERROR_MESSAGE": "There was an error creating the document, please try again."
},
"FORM": {
+ "TYPE": {
+ "LABEL": "Document Type",
+ "URL": "URL",
+ "PDF": "PDF File"
+ },
"URL": {
"LABEL": "URL",
"PLACEHOLDER": "Enter the URL of the document",
"ERROR": "Please provide a valid URL for the document"
},
+ "PDF_FILE": {
+ "LABEL": "PDF File",
+ "CHOOSE_FILE": "Choose PDF file",
+ "ERROR": "Please select a PDF file",
+ "HELP_TEXT": "Maximum file size: 10MB",
+ "INVALID_TYPE": "Please select a valid PDF file",
+ "TOO_LARGE": "File size exceeds 10MB limit"
+ },
+ "NAME": {
+ "LABEL": "Document Name (Optional)",
+ "PLACEHOLDER": "Enter a name for the document"
+ },
"ASSISTANT": {
"LABEL": "Assistant",
"PLACEHOLDER": "Select the assistant",
@@ -744,6 +761,7 @@
"SELECTED": "{count} selected",
"SELECT_ALL": "Select all ({count})",
"UNSELECT_ALL": "Unselect all ({count})",
+ "SEARCH_PLACEHOLDER": "Search FAQs...",
"BULK_APPROVE_BUTTON": "Approve",
"BULK_DELETE_BUTTON": "Sil",
"BULK_APPROVE": {
diff --git a/app/javascript/dashboard/i18n/locale/tr/mfa.json b/app/javascript/dashboard/i18n/locale/tr/mfa.json
new file mode 100644
index 000000000..bdbc3b62a
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/tr/mfa.json
@@ -0,0 +1,106 @@
+{
+ "MFA_SETTINGS": {
+ "TITLE": "Two-Factor Authentication",
+ "SUBTITLE": "Secure your account with TOTP-based authentication",
+ "DESCRIPTION": "Add an extra layer of security to your account using a time-based one-time password (TOTP)",
+ "STATUS_TITLE": "Authentication Status",
+ "STATUS_DESCRIPTION": "Manage your two-factor authentication settings and backup recovery codes",
+ "ENABLED": "Etkin",
+ "DISABLED": "Devre dışı",
+ "STATUS_ENABLED": "Two-factor authentication is active",
+ "STATUS_ENABLED_DESC": "Your account is protected with an additional layer of security",
+ "ENABLE_BUTTON": "Enable Two-Factor Authentication",
+ "ENHANCE_SECURITY": "Enhance Your Account Security",
+ "ENHANCE_SECURITY_DESC": "Two-factor authentication adds an extra layer of security by requiring a verification code from your authenticator app in addition to your password.",
+ "SETUP": {
+ "STEP_NUMBER_1": "1",
+ "STEP_NUMBER_2": "2",
+ "STEP1_TITLE": "Scan QR Code with Your Authenticator App",
+ "STEP1_DESCRIPTION": "Use Google Authenticator, Authy, or any TOTP-compatible app",
+ "LOADING_QR": "Loading...",
+ "MANUAL_ENTRY": "Can't scan? Enter code manually",
+ "SECRET_KEY": "Secret Key",
+ "COPY": "Kopyala",
+ "ENTER_CODE": "Enter the 6-digit code from your authenticator app",
+ "ENTER_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify & Continue",
+ "CANCEL": "İptal Et",
+ "ERROR_STARTING": "MFA not enabled. Please contact administrator.",
+ "INVALID_CODE": "Invalid verification code",
+ "SECRET_COPIED": "Secret key copied to clipboard",
+ "SUCCESS": "Two-factor authentication has been enabled successfully"
+ },
+ "BACKUP": {
+ "TITLE": "Save Your Backup Codes",
+ "DESCRIPTION": "Keep these codes safe. Each can be used once if you lose access to your authenticator",
+ "IMPORTANT": "Important:",
+ "IMPORTANT_NOTE": " Save these codes in a secure location. You won't be able to see them again.",
+ "DOWNLOAD": "İndir",
+ "COPY_ALL": "Copy All",
+ "CONFIRM": "I have saved my backup codes in a secure location and understand that I won't be able to see them again",
+ "COMPLETE_SETUP": "Complete Setup",
+ "CODES_COPIED": "Backup codes copied to clipboard"
+ },
+ "MANAGEMENT": {
+ "BACKUP_CODES": "Backup Codes",
+ "BACKUP_CODES_DESC": "Generate new codes if you've lost or used your existing ones",
+ "REGENERATE": "Regenerate Backup Codes",
+ "DISABLE_MFA": "Disable 2FA",
+ "DISABLE_MFA_DESC": "Remove two-factor authentication from your account",
+ "DISABLE_BUTTON": "Disable Two-Factor Authentication"
+ },
+ "DISABLE": {
+ "TITLE": "Disable Two-Factor Authentication",
+ "DESCRIPTION": "You'll need to enter your password and a verification code to disable two-factor authentication.",
+ "PASSWORD": "Parola",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Disable 2FA",
+ "CANCEL": "İptal Et",
+ "SUCCESS": "Two-factor authentication has been disabled",
+ "ERROR": "Failed to disable MFA. Please check your credentials."
+ },
+ "REGENERATE": {
+ "TITLE": "Regenerate Backup Codes",
+ "DESCRIPTION": "This will invalidate your existing backup codes and generate new ones. Enter your verification code to continue.",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Generate New Codes",
+ "CANCEL": "İptal Et",
+ "NEW_CODES_TITLE": "New Backup Codes Generated",
+ "NEW_CODES_DESC": "Your old backup codes have been invalidated. Save these new codes in a secure location.",
+ "CODES_IMPORTANT": "Important:",
+ "CODES_IMPORTANT_NOTE": " Each code can only be used once. Save them before closing this window.",
+ "DOWNLOAD_CODES": "Download Codes",
+ "COPY_ALL_CODES": "Copy All Codes",
+ "CODES_SAVED": "I've Saved My Codes",
+ "SUCCESS": "New backup codes have been generated",
+ "ERROR": "Failed to regenerate backup codes"
+ }
+ },
+ "MFA_VERIFICATION": {
+ "TITLE": "Two-Factor Authentication",
+ "DESCRIPTION": "Enter your verification code to continue",
+ "AUTHENTICATOR_APP": "Authenticator App",
+ "BACKUP_CODE": "Backup Code",
+ "ENTER_OTP_CODE": "Enter 6-digit code from your authenticator app",
+ "ENTER_BACKUP_CODE": "Enter one of your backup codes",
+ "BACKUP_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify",
+ "TRY_ANOTHER_METHOD": "Try another verification method",
+ "CANCEL_LOGIN": "Cancel and return to login",
+ "HELP_TEXT": "Having trouble signing in?",
+ "LEARN_MORE": "Learn more about 2FA",
+ "HELP_MODAL": {
+ "TITLE": "Two-Factor Authentication Help",
+ "AUTHENTICATOR_TITLE": "Using an Authenticator App",
+ "AUTHENTICATOR_DESC": "Open your authenticator app (Google Authenticator, Authy, etc.) and enter the 6-digit code shown for your account.",
+ "BACKUP_TITLE": "Using a Backup Code",
+ "BACKUP_DESC": "If you don't have access to your authenticator app, you can use one of the backup codes you saved when setting up 2FA. Each code can only be used once.",
+ "CONTACT_TITLE": "Need More Help?",
+ "CONTACT_DESC_CLOUD": "If you've lost access to both your authenticator app and backup codes, please reach out to Chatwoot support for assistance.",
+ "CONTACT_DESC_SELF_HOSTED": "If you've lost access to both your authenticator app and backup codes, please contact your administrator for assistance."
+ },
+ "VERIFICATION_FAILED": "Verification failed. Please try again."
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/tr/settings.json b/app/javascript/dashboard/i18n/locale/tr/settings.json
index 535a59849..e5913ff5c 100644
--- a/app/javascript/dashboard/i18n/locale/tr/settings.json
+++ b/app/javascript/dashboard/i18n/locale/tr/settings.json
@@ -51,6 +51,13 @@
"LARGER": "Larger",
"EXTRA_LARGE": "Extra Large"
}
+ },
+ "LANGUAGE": {
+ "TITLE": "Preferred Language",
+ "NOTE": "Choose the language you want to use.",
+ "UPDATE_SUCCESS": "Your Language settings have been updated successfully",
+ "UPDATE_ERROR": "There is an error while updating the language settings, please try again",
+ "USE_ACCOUNT_DEFAULT": "Use account default"
}
},
"MESSAGE_SIGNATURE_SECTION": {
@@ -73,6 +80,11 @@
"NOTE": "Parolanızı güncellemek, giriş bilgilerinizi birden fazla cihazda sıfırlar.",
"BTN_TEXT": "Şifre değiştir"
},
+ "SECURITY_SECTION": {
+ "TITLE": "Security",
+ "NOTE": "Manage additional security features for your account.",
+ "MFA_BUTTON": "Manage Two-Factor Authentication"
+ },
"ACCESS_TOKEN": {
"TITLE": "Erişim Jetonu",
"NOTE": "Bu simge, API tabanlı bir entegrasyon oluşturuyorsanız kullanılabilir",
@@ -330,6 +342,7 @@
"REPORTS_LABEL": "Etiketler",
"REPORTS_INBOX": "Gelen kutusu",
"REPORTS_TEAM": "Ekip",
+ "AGENT_ASSIGNMENT": "Agent Assignment",
"SET_AVAILABILITY_TITLE": "Kendini şu şekilde ayarla",
"SET_YOUR_AVAILABILITY": "Uygunluk Durumunuzu Ayarlayın",
"SLA": "SLA",
@@ -350,7 +363,8 @@
"INFO_TEXT": "Sistem sizi otomatik olarak çevrimdışı işaretlesin, uygulamayı veya gösterge tablosunu kullanmıyorsanız.",
"INFO_SHORT": "Automatically mark offline when you aren't using the app."
},
- "DOCS": "Dokümantasyonu oku"
+ "DOCS": "Dokümantasyonu oku",
+ "SECURITY": "Security"
},
"BILLING_SETTINGS": {
"TITLE": "Fatura",
@@ -382,6 +396,77 @@
},
"NO_BILLING_USER": "Fatura hesabınız yapılandırılıyor. Lütfen sayfayı yenileyip tekrar deneyin."
},
+ "SECURITY_SETTINGS": {
+ "TITLE": "Security",
+ "DESCRIPTION": "Manage your account security settings.",
+ "LINK_TEXT": "Learn more about SAML SSO",
+ "SAML": {
+ "TITLE": "SAML SSO",
+ "NOTE": "Configure SAML single sign-on for your account. Users will authenticate through your identity provider instead of using email/password.",
+ "ACS_URL": {
+ "LABEL": "ACS URL",
+ "TOOLTIP": "Assertion Consumer Service URL - Configure this URL in your IdP as the destination for SAML responses"
+ },
+ "SSO_URL": {
+ "LABEL": "SSO URL",
+ "HELP": "The URL where SAML authentication requests will be sent",
+ "PLACEHOLDER": "https://your-idp.com/saml/sso"
+ },
+ "CERTIFICATE": {
+ "LABEL": "Signing certificate in PEM format",
+ "HELP": "The public certificate from your identity provider used to verify SAML responses",
+ "PLACEHOLDER": "-----BEGIN CERTIFICATE-----\nMIIC..."
+ },
+ "FINGERPRINT": {
+ "LABEL": "Fingerprint",
+ "TOOLTIP": "SHA-1 fingerprint of the certificate - Use this to verify the certificate in your IdP configuration"
+ },
+ "COPY_SUCCESS": "Kod başarıyla panoya kopyalandı",
+ "SP_ENTITY_ID": {
+ "LABEL": "SP Entity ID",
+ "HELP": "Unique identifier for this application as a service provider (auto-generated).",
+ "TOOLTIP": "Unique identifier for Chatwoot as the Service Provider - Configure this in your IdP settings"
+ },
+ "IDP_ENTITY_ID": {
+ "LABEL": "Identity Provider Entity ID",
+ "HELP": "Unique identifier for your identity provider (usually found in IdP configuration)",
+ "PLACEHOLDER": "https://your-idp.com/saml"
+ },
+ "UPDATE_BUTTON": "Update SAML Settings",
+ "API": {
+ "SUCCESS": "SAML settings updated successfully",
+ "ERROR": "Failed to update SAML settings",
+ "ERROR_LOADING": "Failed to load SAML settings",
+ "DISABLED": "SAML settings disabled successfully"
+ },
+ "VALIDATION": {
+ "REQUIRED_FIELDS": "SSO URL, Identity Provider Entity ID, and Certificate are required fields",
+ "SSO_URL_ERROR": "Please enter a valid SSO URL",
+ "CERTIFICATE_ERROR": "Certificate is required",
+ "IDP_ENTITY_ID_ERROR": "Identity Provider Entity ID is required"
+ },
+ "ENTERPRISE_PAYWALL": {
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade to an Enterprise plan to access SAML single sign-on and other advanced security features.",
+ "ASK_ADMIN": "Please reach out to your administrator for the upgrade."
+ },
+ "PAYWALL": {
+ "TITLE": "Upgrade to enable SAML SSO",
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade your plan to get access to SAML single sign-on and other advanced features.",
+ "UPGRADE_NOW": "Upgrade now",
+ "CANCEL_ANYTIME": "You can change or cancel your plan anytime"
+ },
+ "ATTRIBUTE_MAPPING": {
+ "TITLE": "SAML Attribute Setup",
+ "DESCRIPTION": "The following attribute mappings must be configured in your identity provider"
+ },
+ "INFO_SECTION": {
+ "TITLE": "Service Provider Information",
+ "TOOLTIP": "Copy these values and configure them in your Identity Provider to establish the SAML connection"
+ }
+ }
+ },
"CREATE_ACCOUNT": {
"NO_ACCOUNT_WARNING": "Eyvah! Hiçbir Chatwoot hesabı bulunamadı. Devam etmek için yeni bir hesap oluşturun.",
"NEW_ACCOUNT": "Yeni hesap",
@@ -418,5 +503,255 @@
"SWITCH_TO_REPLY": "Yanıt'a geç",
"TOGGLE_SNOOZE_DROPDOWN": "Snooze açılır menüsünü aç/kapat"
}
+ },
+ "ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent assignment",
+ "DESCRIPTION": "Define policies to effectively manage workload and route conversations based on the needs of inboxes and agents. Learn more here"
+ },
+ "ASSIGNMENT_POLICY": {
+ "TITLE": "Assignment policy",
+ "DESCRIPTION": "Manage how conversations get assigned in inboxes.",
+ "FEATURES": [
+ "Assign by conversations evenly or by available capacity",
+ "Add fair distribution rules to avoid overloading any agent",
+ "Add inboxes to a policy - one policy per inbox"
+ ]
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "TITLE": "Agent capacity policy",
+ "DESCRIPTION": "Manage workload for agents.",
+ "FEATURES": [
+ "Define maximum conversations per inbox",
+ "Create exceptions based on labels and time",
+ "Add agents to a policy - one policy per agent"
+ ]
+ }
+ },
+ "AGENT_ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Assignment policy",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "ORDER": "Order",
+ "PRIORITY": "Öncelik",
+ "ACTIVE": "Aktif",
+ "INACTIVE": "Inactive",
+ "POPOVER": "Added inboxes",
+ "EDIT": "Düzenle"
+ },
+ "NO_RECORDS_FOUND": "No assignment policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create assignment policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy created successfully",
+ "ERROR_MESSAGE": "Failed to create assignment policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit assignment policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_INBOX_DIALOG": {
+ "TITLE": "Add inbox",
+ "DESCRIPTION": "{inboxName} inbox is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "İptal Et"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update assignment policy"
+ },
+ "INBOX_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Inbox added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Inbox removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove inbox from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Açıklama:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "STATUS": {
+ "LABEL": "Durum:",
+ "PLACEHOLDER": "Select status",
+ "ACTIVE": "Policy is active",
+ "INACTIVE": "Policy is inactive"
+ },
+ "ASSIGNMENT_ORDER": {
+ "LABEL": "Assignment order",
+ "ROUND_ROBIN": {
+ "LABEL": "Round robin",
+ "DESCRIPTION": "Assign conversations evenly among agents."
+ },
+ "BALANCED": {
+ "LABEL": "Balanced",
+ "DESCRIPTION": "Assign conversations based on available capacity."
+ }
+ },
+ "ASSIGNMENT_PRIORITY": {
+ "LABEL": "Assignment priority",
+ "EARLIEST_CREATED": {
+ "LABEL": "Earliest created",
+ "DESCRIPTION": "The conversation that was created first gets assigned first."
+ },
+ "LONGEST_WAITING": {
+ "LABEL": "Longest waiting",
+ "DESCRIPTION": "The conversation waiting the longest gets assigned first."
+ }
+ },
+ "FAIR_DISTRIBUTION": {
+ "LABEL": "Fair distribution policy",
+ "DESCRIPTION": "Set the maximum number of conversations that can be assigned per agent within a time window to avoid overloading any one agent. This required field defaults to 100 conversations per hour.",
+ "INPUT_MAX": "Assign max",
+ "DURATION": "Conversations per agent in every"
+ },
+ "INBOXES": {
+ "LABEL": "Added inboxes",
+ "DESCRIPTION": "Add inboxes for which this policy will be applicable.",
+ "ADD_BUTTON": "Add inbox",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select inboxes to add",
+ "ADD_BUTTON": "Ekle"
+ },
+ "EMPTY_STATE": "No inboxes added to this policy, add an inbox to get started",
+ "API": {
+ "SUCCESS_MESSAGE": "Inbox successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Assignment policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete assignment policy"
+ }
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent capacity",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "POPOVER": "Added agents",
+ "EDIT": "Düzenle"
+ },
+ "NO_RECORDS_FOUND": "No agent capacity policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create agent capacity policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy created successfully",
+ "ERROR_MESSAGE": "Failed to create agent capacity policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit agent capacity policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_AGENT_DIALOG": {
+ "TITLE": "Add agent",
+ "DESCRIPTION": "{agentName} is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "İptal Et"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update agent capacity policy"
+ },
+ "AGENT_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Agent added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Agent removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove agent from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Açıklama:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "INBOX_CAPACITY_LIMIT": {
+ "LABEL": "Inbox capacity limits",
+ "ADD_BUTTON": "Add inbox",
+ "FIELD": {
+ "SELECT_INBOX": "Select inbox",
+ "MAX_CONVERSATIONS": "Max conversations",
+ "SET_LIMIT": "Set limit"
+ },
+ "EMPTY_STATE": "No inbox limit set"
+ },
+ "EXCLUSION_RULES": {
+ "LABEL": "Exclusion rules",
+ "DESCRIPTION": "Conversations that satisfy the following conditions would not count towards agent capacity",
+ "TAGS": {
+ "LABEL": "Exclude conversations tagged with specific labels",
+ "ADD_TAG": "add tag",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select tags to add"
+ },
+ "EMPTY_STATE": "No tags added to this policy."
+ },
+ "DURATION": {
+ "LABEL": "Exclude conversations older than a specified duration",
+ "PLACEHOLDER": "Set time"
+ }
+ },
+ "USERS": {
+ "LABEL": "Assigned agents",
+ "DESCRIPTION": "Add agents for which this policy will be applicable.",
+ "ADD_BUTTON": "Add agent",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select agents to add",
+ "ADD_BUTTON": "Ekle"
+ },
+ "EMPTY_STATE": "No agents added",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Agent capacity policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete agent capacity policy"
+ }
+ },
+ "DELETE_POLICY": {
+ "TITLE": "Delete policy",
+ "DESCRIPTION": "Are you sure you want to delete this policy? This action cannot be undone.",
+ "CONFIRM_BUTTON_LABEL": "Sil",
+ "CANCEL_BUTTON_LABEL": "İptal Et"
+ }
}
}
diff --git a/app/javascript/dashboard/i18n/locale/tr/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/tr/whatsappTemplates.json
index 47dfd1c55..86bde2059 100644
--- a/app/javascript/dashboard/i18n/locale/tr/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/tr/whatsappTemplates.json
@@ -40,6 +40,7 @@
"BUTTON_LABEL": "Button {index}",
"COUPON_CODE": "Enter coupon code (max 15 chars)",
"MEDIA_URL_LABEL": "Enter {type} URL",
+ "DOCUMENT_NAME_PLACEHOLDER": "Enter document filename (e.g., Invoice_2025.pdf)",
"BUTTON_PARAMETER": "Enter button parameter"
}
}
diff --git a/app/javascript/dashboard/i18n/locale/uk/attributesMgmt.json b/app/javascript/dashboard/i18n/locale/uk/attributesMgmt.json
index a89b19361..946c661ff 100644
--- a/app/javascript/dashboard/i18n/locale/uk/attributesMgmt.json
+++ b/app/javascript/dashboard/i18n/locale/uk/attributesMgmt.json
@@ -6,7 +6,7 @@
"DESCRIPTION": "A custom attribute tracks additional details about your contacts or conversations—such as the subscription plan or the date of their first purchase. You can add different types of custom attributes, such as text, lists, or numbers, to capture the specific information you need.",
"LEARN_MORE": "Learn more about custom attributes",
"ATTRIBUTE_MODELS": {
- "CONVERSATION": "Діалог",
+ "CONVERSATION": "Розмови",
"CONTACT": "Контакт"
},
"ATTRIBUTE_TYPES": {
diff --git a/app/javascript/dashboard/i18n/locale/uk/automation.json b/app/javascript/dashboard/i18n/locale/uk/automation.json
index 84617e9f0..d26cef431 100644
--- a/app/javascript/dashboard/i18n/locale/uk/automation.json
+++ b/app/javascript/dashboard/i18n/locale/uk/automation.json
@@ -177,7 +177,8 @@
"REFERER_LINK": "Referrer Link",
"ASSIGNEE_NAME": "Assignee",
"TEAM_NAME": "Команда",
- "PRIORITY": "Пріоритет"
+ "PRIORITY": "Пріоритет",
+ "LABELS": "Мітки"
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/uk/contact.json b/app/javascript/dashboard/i18n/locale/uk/contact.json
index 54bc682c6..6f673d76a 100644
--- a/app/javascript/dashboard/i18n/locale/uk/contact.json
+++ b/app/javascript/dashboard/i18n/locale/uk/contact.json
@@ -17,10 +17,10 @@
"IP_ADDRESS": "IP-адреса",
"CREATED_AT_LABEL": "Створено",
"NEW_MESSAGE": "Нове повідомлення",
- "CALL": "Call",
- "CALL_UNDER_DEVELOPMENT": "Calling is under development",
+ "CALL": "Дзвінок",
+ "CALL_UNDER_DEVELOPMENT": "Дзвінки знаходяться на стадії розробки",
"VOICE_INBOX_PICKER": {
- "TITLE": "Choose a voice inbox"
+ "TITLE": "Оберіть голосову теку"
},
"CONVERSATIONS": {
"NO_RECORDS_FOUND": "Не було попередніх бесід, пов'язаних з цим контактом.",
@@ -556,7 +556,7 @@
"SAVE": "Save note",
"EXPAND": "Розширити",
"COLLAPSE": "Collapse",
- "NO_NOTES": "No notes, you can add notes from the contact details page.",
+ "NO_NOTES": "Немає нотаток, ви можете додати їх на сторінці контакту.",
"EMPTY_STATE": "There are no notes associated to this contact. You can add a note by typing in the box above."
}
},
@@ -610,6 +610,15 @@
"SEND_MESSAGE": "Надіслати"
}
},
+ "TWILIO_OPTIONS": {
+ "LABEL": "Select template",
+ "SEARCH_PLACEHOLDER": "Search templates",
+ "EMPTY_STATE": "No templates found",
+ "TEMPLATE_PARSER": {
+ "BACK": "Повернутися назад",
+ "SEND_MESSAGE": "Надіслати"
+ }
+ },
"ACTION_BUTTONS": {
"DISCARD": "Discard",
"SEND": "Send ({keyCode})"
diff --git a/app/javascript/dashboard/i18n/locale/uk/contactFilters.json b/app/javascript/dashboard/i18n/locale/uk/contactFilters.json
index 3377fd177..1b5d347a9 100644
--- a/app/javascript/dashboard/i18n/locale/uk/contactFilters.json
+++ b/app/javascript/dashboard/i18n/locale/uk/contactFilters.json
@@ -48,7 +48,8 @@
"CREATED_AT": "Створений в",
"LAST_ACTIVITY": "Остання активність",
"REFERER_LINK": "Реферальне посилання",
- "BLOCKED": "Заблоковано"
+ "BLOCKED": "Заблоковано",
+ "LABELS": "Мітки"
},
"GROUPS": {
"STANDARD_FILTERS": "Стандартні фільтри",
diff --git a/app/javascript/dashboard/i18n/locale/uk/contentTemplates.json b/app/javascript/dashboard/i18n/locale/uk/contentTemplates.json
new file mode 100644
index 000000000..24b6195af
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/uk/contentTemplates.json
@@ -0,0 +1,51 @@
+{
+ "CONTENT_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Twilio Templates",
+ "SUBTITLE": "Select the Twilio template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Знайти шаблони",
+ "NO_TEMPLATES_FOUND": "Шаблонів не знайдено для",
+ "NO_CONTENT": "Немає вмісту",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Категорія",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No Twilio templates available. Click refresh to sync templates from Twilio.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Мова",
+ "TEMPLATE_BODY": "Тіло шаблона",
+ "CATEGORY": "Категорія"
+ },
+ "TYPES": {
+ "MEDIA": "Media",
+ "QUICK_REPLY": "Quick Reply",
+ "TEXT": "Текст"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Змінні",
+ "LANGUAGE": "Мова",
+ "CATEGORY": "Категорія",
+ "VARIABLE_PLACEHOLDER": "Введіть значення {variable}",
+ "GO_BACK_LABEL": "Повернутися",
+ "SEND_MESSAGE_LABEL": "Надіслати повідомлення",
+ "FORM_ERROR_MESSAGE": "Будь ласка, заповніть всі змінні перед надсиланням",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "MEDIA_URL_LABEL": "Enter full media URL",
+ "MEDIA_URL_PLACEHOLDER": "https://example.com/image.jpg"
+ },
+ "FORM": {
+ "BACK_BUTTON": "Назад",
+ "SEND_MESSAGE_BUTTON": "Надіслати повідомлення"
+ }
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/uk/conversation.json b/app/javascript/dashboard/i18n/locale/uk/conversation.json
index dad14a889..5aca1094e 100644
--- a/app/javascript/dashboard/i18n/locale/uk/conversation.json
+++ b/app/javascript/dashboard/i18n/locale/uk/conversation.json
@@ -32,9 +32,14 @@
"LOADING_CONVERSATIONS": "Завантаження бесід",
"CANNOT_REPLY": "Ви не можете відповісти через",
"24_HOURS_WINDOW": "24-годинне обмеження на повідомлення",
- "API_HOURS_WINDOW": "You can only reply to this conversation within {hours} hours",
+ "API_HOURS_WINDOW": "Ви можете відповідати лише в межах {hours} годин",
"NOT_ASSIGNED_TO_YOU": "Ця розмова не призначена на вас. Ви бажаєте призначити цю розмову на себе?",
"ASSIGN_TO_ME": "Призначити мені",
+ "BOT_HANDOFF_MESSAGE": "You are responding to a conversation which is currently handled by an assistant or a bot.",
+ "BOT_HANDOFF_ACTION": "Відзначити відкритим та призначити на себе",
+ "BOT_HANDOFF_REOPEN_ACTION": "Позначити розмову відкритою",
+ "BOT_HANDOFF_SUCCESS": "Розмова була призначена на вас",
+ "BOT_HANDOFF_ERROR": "Failed to take over the conversation. Please try again.",
"TWILIO_WHATSAPP_CAN_REPLY": "Ви можете відповісти на цю розмову тільки за допомогою шаблонного повідомлення через",
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "24-годинне обмеження на повідомлення",
"OLD_INSTAGRAM_INBOX_REPLY_BANNER": "This Instagram account was migrated to the new Instagram channel inbox. All new messages will show up there. You won’t be able to send messages from this conversation anymore.",
@@ -66,6 +71,17 @@
"SHOW_LABELS": "Показати мітки",
"HIDE_LABELS": "Сховати мітки"
},
+ "VOICE_CALL": {
+ "INCOMING_CALL": "Incoming call",
+ "OUTGOING_CALL": "Outgoing call",
+ "CALL_IN_PROGRESS": "Call in progress",
+ "NO_ANSWER": "No answer",
+ "MISSED_CALL": "Missed call",
+ "CALL_ENDED": "Call ended",
+ "NOT_ANSWERED_YET": "Not answered yet",
+ "THEY_ANSWERED": "They answered",
+ "YOU_ANSWERED": "You answered"
+ },
"HEADER": {
"RESOLVE_ACTION": "Вирішити",
"REOPEN_ACTION": "Відкрити знову",
@@ -311,7 +327,7 @@
"CONVERSATION_ACTIONS": "Дії при бесіді",
"CONVERSATION_LABELS": "Мітки бесіди",
"CONVERSATION_INFO": "Інформація про бесіду",
- "CONTACT_NOTES": "Contact Notes",
+ "CONTACT_NOTES": "Нотатки контакту",
"CONTACT_ATTRIBUTES": "Атрибути контакту",
"PREVIOUS_CONVERSATION": "Попередні бесіди",
"MACROS": "Макрос",
diff --git a/app/javascript/dashboard/i18n/locale/uk/helpCenter.json b/app/javascript/dashboard/i18n/locale/uk/helpCenter.json
index 8d651d366..9e36d17d9 100644
--- a/app/javascript/dashboard/i18n/locale/uk/helpCenter.json
+++ b/app/javascript/dashboard/i18n/locale/uk/helpCenter.json
@@ -741,7 +741,8 @@
"LIVE_CHAT_WIDGET": {
"LABEL": "Live chat widget",
"PLACEHOLDER": "Select live chat widget",
- "HELP_TEXT": "Select a live chat widget that will appear on your help center"
+ "HELP_TEXT": "Select a live chat widget that will appear on your help center",
+ "NONE_OPTION": "No widget"
},
"BRAND_COLOR": {
"LABEL": "Brand color"
@@ -807,6 +808,58 @@
"ERROR_MESSAGE": "Unable to update portal"
}
}
+ },
+ "PDF_UPLOAD": {
+ "TITLE": "Upload PDF Document",
+ "DESCRIPTION": "Upload a PDF document to automatically generate FAQs using AI",
+ "DRAG_DROP_TEXT": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "ADDITIONAL_CONTEXT_LABEL": "Additional Context (Optional)",
+ "ADDITIONAL_CONTEXT_PLACEHOLDER": "Provide any additional context or instructions for FAQ generation...",
+ "UPLOADING": "Завантажується...",
+ "UPLOAD": "Upload & Process",
+ "CANCEL": "Скасувати",
+ "ERROR_INVALID_TYPE": "Please select a valid PDF file",
+ "ERROR_FILE_TOO_LARGE": "File size must be less than 512MB",
+ "ERROR_UPLOAD_FAILED": "Failed to upload PDF. Please try again."
+ },
+ "PDF_DOCUMENTS": {
+ "TITLE": "PDF Documents",
+ "DESCRIPTION": "Manage uploaded PDF documents and generate FAQs from them",
+ "UPLOAD_PDF": "Upload PDF",
+ "UPLOAD_FIRST_PDF": "Upload your first PDF",
+ "UPLOADED_BY": "Uploaded by",
+ "GENERATE_FAQS": "Generate FAQs",
+ "GENERATING": "Генерація...",
+ "CONFIRM_DELETE": "Are you sure you want to delete {filename}?",
+ "EMPTY_STATE": {
+ "TITLE": "No PDF documents yet",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQs using AI"
+ },
+ "STATUS": {
+ "UPLOADED": "Ready",
+ "PROCESSING": "Processing",
+ "PROCESSED": "Завершено",
+ "FAILED": "Failed"
+ }
+ },
+ "CONTENT_GENERATION": {
+ "TITLE": "Content Generation",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQ content using AI",
+ "UPLOAD_TITLE": "Upload PDF Document",
+ "DRAG_DROP": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "UPLOADING": "Processing document...",
+ "UPLOAD_SUCCESS": "Document processed successfully!",
+ "UPLOAD_ERROR": "Failed to upload document. Please try again.",
+ "INVALID_FILE_TYPE": "Please select a valid PDF file",
+ "FILE_TOO_LARGE": "File size must be less than 512MB",
+ "GENERATED_CONTENT": "Generated FAQ Content",
+ "PUBLISH_SELECTED": "Publish Selected",
+ "PUBLISHING": "Publishing...",
+ "FROM_DOCUMENT": "From document",
+ "NO_CONTENT": "No generated content available. Upload a PDF document to get started.",
+ "LOADING": "Loading generated content..."
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/uk/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/uk/inboxMgmt.json
index aa7d8f5a5..239c767ec 100644
--- a/app/javascript/dashboard/i18n/locale/uk/inboxMgmt.json
+++ b/app/javascript/dashboard/i18n/locale/uk/inboxMgmt.json
@@ -272,8 +272,8 @@
},
"SUBMIT_BUTTON": "Створити канал WhatsApp",
"EMBEDDED_SIGNUP": {
- "TITLE": "Quick Setup with Meta",
- "DESC": "You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
+ "TITLE": "Quick setup with Meta",
+ "DESC": "Use the WhatsApp Embedded Signup flow to quickly connect new numbers. You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
"BENEFITS": {
"TITLE": "Benefits of Embedded Signup:",
"EASY_SETUP": "No manual configuration required",
@@ -281,9 +281,8 @@
"AUTO_CONFIG": "Automatic webhook and phone number configuration"
},
"LEARN_MORE": {
- "TEXT": "To learn more about integrated signup, pricing, and limitations, visit",
- "LINK_TEXT": "this link.",
- "LINK_URL": "https://developers.facebook.com/docs/whatsapp/embedded-signup/custom-flows/onboarding-business-app-users#limitations"
+ "TEXT": "To learn more about integrated signup, pricing, and limitations, visit {link}.",
+ "LINK_TEXT": "this link"
},
"SUBMIT_BUTTON": "Connect with WhatsApp Business",
"AUTH_PROCESSING": "Authenticating with Meta",
@@ -296,7 +295,9 @@
"INVALID_BUSINESS_DATA": "Invalid business data received from Facebook. Please try again.",
"SIGNUP_ERROR": "Signup error occurred",
"AUTH_NOT_COMPLETED": "Authentication not completed. Please restart the process.",
- "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured"
+ "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured",
+ "MANUAL_FALLBACK": "If your number is already connected to the WhatsApp Business Platform (API), or if you’re a tech provider onboarding your own number, please use the {link} flow",
+ "MANUAL_LINK_TEXT": "manual setup flow"
},
"API": {
"ERROR_MESSAGE": "Ми не змогли зберегти канал WhatsApp"
@@ -424,7 +425,51 @@
},
"AUTH": {
"TITLE": "Оберіть канал",
- "DESC": "Chatwoot підтримує live-chat віджети, Facebook Messenger, профілі Twitter, WhatsApp, електронну пошту і т.д. Якщо ви хочете створити користувальницький канал, ви можете створити його за допомогою каналу API. Щоб почати, виберіть один з каналів нижче."
+ "DESC": "Chatwoot підтримує live-chat віджети, Facebook Messenger, профілі Twitter, WhatsApp, електронну пошту і т.д. Якщо ви хочете створити користувальницький канал, ви можете створити його за допомогою каналу API. Щоб почати, виберіть один з каналів нижче.",
+ "TITLE_NEXT": "Complete the setup",
+ "TITLE_FINISH": "Voilà!",
+ "CHANNEL": {
+ "WEBSITE": {
+ "TITLE": "Вебсайт",
+ "DESCRIPTION": "Create a live-chat widget"
+ },
+ "FACEBOOK": {
+ "TITLE": "Facebook",
+ "DESCRIPTION": "Connect your Facebook page"
+ },
+ "WHATSAPP": {
+ "TITLE": "WhatsApp",
+ "DESCRIPTION": "Support your customers on WhatsApp"
+ },
+ "EMAIL": {
+ "TITLE": "Email",
+ "DESCRIPTION": "Connect with Gmail, Outlook, or other providers"
+ },
+ "SMS": {
+ "TITLE": "SMS",
+ "DESCRIPTION": "Integrate SMS channel with Twilio or bandwidth"
+ },
+ "API": {
+ "TITLE": "API",
+ "DESCRIPTION": "Make a custom channel using our API"
+ },
+ "TELEGRAM": {
+ "TITLE": "Telegram",
+ "DESCRIPTION": "Configure Telegram channel using Bot token"
+ },
+ "LINE": {
+ "TITLE": "Line",
+ "DESCRIPTION": "Integrate your Line channel"
+ },
+ "INSTAGRAM": {
+ "TITLE": "Instagram",
+ "DESCRIPTION": "Connect your instagram account"
+ },
+ "VOICE": {
+ "TITLE": "Voice",
+ "DESCRIPTION": "Integrate with Twilio Voice"
+ }
+ }
},
"AGENTS": {
"TITLE": "Агенти",
@@ -478,7 +523,10 @@
"MESSAGE": "Ви тепер можете взаємодіяти з вашими клієнтами через ваш новий канал. Щасливої підтримки",
"BUTTON_TEXT": "Давай туди",
"MORE_SETTINGS": "Додаткові налаштування",
- "WEBSITE_SUCCESS": "Ви успішно завершили створення каналу Веб-сайт. Скопіюйте наведений нижче код і вставте його на ваш сайт. Наступного разу коли клієнт скористається онлайн чатом, розмова автоматично з'явиться в вашій скриньці Вхідні."
+ "WEBSITE_SUCCESS": "Ви успішно завершили створення каналу Веб-сайт. Скопіюйте наведений нижче код і вставте його на ваш сайт. Наступного разу коли клієнт скористається онлайн чатом, розмова автоматично з'явиться в вашій скриньці Вхідні.",
+ "WHATSAPP_QR_INSTRUCTION": "Scan the QR code above to quickly test your WhatsApp inbox",
+ "MESSENGER_QR_INSTRUCTION": "Scan the QR code above to quickly test your Facebook Messenger inbox",
+ "TELEGRAM_QR_INSTRUCTION": "Scan the QR code above to quickly test your Telegram inbox"
},
"REAUTH": "Повторна авторизація",
"VIEW": "Вигляд",
@@ -868,9 +916,18 @@
"SCRIPT_SETTINGS": "\n window.chatwootSettings = {options};"
},
"EMAIL_PROVIDERS": {
- "MICROSOFT": "Microsoft",
- "GOOGLE": "Google",
- "OTHER_PROVIDERS": "Інші постачальники"
+ "MICROSOFT": {
+ "TITLE": "Microsoft",
+ "DESCRIPTION": "Connect with Microsoft"
+ },
+ "GOOGLE": {
+ "TITLE": "Google",
+ "DESCRIPTION": "Connect with Google"
+ },
+ "OTHER_PROVIDERS": {
+ "TITLE": "Інші постачальники",
+ "DESCRIPTION": "Connect with Other Providers"
+ }
},
"CHANNELS": {
"MESSENGER": "Messenger",
diff --git a/app/javascript/dashboard/i18n/locale/uk/integrations.json b/app/javascript/dashboard/i18n/locale/uk/integrations.json
index fd1c820fc..52d74e197 100644
--- a/app/javascript/dashboard/i18n/locale/uk/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/uk/integrations.json
@@ -555,7 +555,7 @@
}
},
"LIST": {
- "SEARCH_PLACEHOLDER": "Search..."
+ "SEARCH_PLACEHOLDER": "Пошук..."
},
"EMPTY_MESSAGE": "No guardrails found. Create or add examples to begin.",
"SEARCH_EMPTY_MESSAGE": "No guardrails found for this search.",
@@ -603,7 +603,7 @@
}
},
"LIST": {
- "SEARCH_PLACEHOLDER": "Search..."
+ "SEARCH_PLACEHOLDER": "Пошук..."
},
"EMPTY_MESSAGE": "No response guidelines found. Create or add examples to begin.",
"SEARCH_EMPTY_MESSAGE": "No response guidelines found for this search.",
@@ -670,7 +670,7 @@
"UPDATE": "Update changes"
},
"LIST": {
- "SEARCH_PLACEHOLDER": "Search..."
+ "SEARCH_PLACEHOLDER": "Пошук..."
},
"EMPTY_MESSAGE": "No scenarios found. Create or add examples to begin.",
"SEARCH_EMPTY_MESSAGE": "No scenarios found for this search.",
@@ -704,11 +704,28 @@
"ERROR_MESSAGE": "There was an error creating the document, please try again."
},
"FORM": {
+ "TYPE": {
+ "LABEL": "Document Type",
+ "URL": "URL",
+ "PDF": "PDF File"
+ },
"URL": {
"LABEL": "URL",
"PLACEHOLDER": "Enter the URL of the document",
"ERROR": "Please provide a valid URL for the document"
},
+ "PDF_FILE": {
+ "LABEL": "PDF File",
+ "CHOOSE_FILE": "Choose PDF file",
+ "ERROR": "Please select a PDF file",
+ "HELP_TEXT": "Maximum file size: 10MB",
+ "INVALID_TYPE": "Please select a valid PDF file",
+ "TOO_LARGE": "File size exceeds 10MB limit"
+ },
+ "NAME": {
+ "LABEL": "Document Name (Optional)",
+ "PLACEHOLDER": "Enter a name for the document"
+ },
"ASSISTANT": {
"LABEL": "Assistant",
"PLACEHOLDER": "Select the assistant",
@@ -744,6 +761,7 @@
"SELECTED": "{count} selected",
"SELECT_ALL": "Select all ({count})",
"UNSELECT_ALL": "Unselect all ({count})",
+ "SEARCH_PLACEHOLDER": "Search FAQs...",
"BULK_APPROVE_BUTTON": "Approve",
"BULK_DELETE_BUTTON": "Видалити",
"BULK_APPROVE": {
diff --git a/app/javascript/dashboard/i18n/locale/uk/mfa.json b/app/javascript/dashboard/i18n/locale/uk/mfa.json
new file mode 100644
index 000000000..496335c5d
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/uk/mfa.json
@@ -0,0 +1,106 @@
+{
+ "MFA_SETTINGS": {
+ "TITLE": "Two-Factor Authentication",
+ "SUBTITLE": "Secure your account with TOTP-based authentication",
+ "DESCRIPTION": "Add an extra layer of security to your account using a time-based one-time password (TOTP)",
+ "STATUS_TITLE": "Authentication Status",
+ "STATUS_DESCRIPTION": "Manage your two-factor authentication settings and backup recovery codes",
+ "ENABLED": "Увімкнено",
+ "DISABLED": "Вимкнено",
+ "STATUS_ENABLED": "Two-factor authentication is active",
+ "STATUS_ENABLED_DESC": "Your account is protected with an additional layer of security",
+ "ENABLE_BUTTON": "Enable Two-Factor Authentication",
+ "ENHANCE_SECURITY": "Enhance Your Account Security",
+ "ENHANCE_SECURITY_DESC": "Two-factor authentication adds an extra layer of security by requiring a verification code from your authenticator app in addition to your password.",
+ "SETUP": {
+ "STEP_NUMBER_1": "1",
+ "STEP_NUMBER_2": "2",
+ "STEP1_TITLE": "Scan QR Code with Your Authenticator App",
+ "STEP1_DESCRIPTION": "Use Google Authenticator, Authy, or any TOTP-compatible app",
+ "LOADING_QR": "Loading...",
+ "MANUAL_ENTRY": "Can't scan? Enter code manually",
+ "SECRET_KEY": "Secret Key",
+ "COPY": "Копіювати",
+ "ENTER_CODE": "Enter the 6-digit code from your authenticator app",
+ "ENTER_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify & Continue",
+ "CANCEL": "Скасувати",
+ "ERROR_STARTING": "MFA not enabled. Please contact administrator.",
+ "INVALID_CODE": "Invalid verification code",
+ "SECRET_COPIED": "Secret key copied to clipboard",
+ "SUCCESS": "Two-factor authentication has been enabled successfully"
+ },
+ "BACKUP": {
+ "TITLE": "Save Your Backup Codes",
+ "DESCRIPTION": "Keep these codes safe. Each can be used once if you lose access to your authenticator",
+ "IMPORTANT": "Important:",
+ "IMPORTANT_NOTE": " Save these codes in a secure location. You won't be able to see them again.",
+ "DOWNLOAD": "Звантажити",
+ "COPY_ALL": "Copy All",
+ "CONFIRM": "I have saved my backup codes in a secure location and understand that I won't be able to see them again",
+ "COMPLETE_SETUP": "Complete Setup",
+ "CODES_COPIED": "Backup codes copied to clipboard"
+ },
+ "MANAGEMENT": {
+ "BACKUP_CODES": "Backup Codes",
+ "BACKUP_CODES_DESC": "Generate new codes if you've lost or used your existing ones",
+ "REGENERATE": "Regenerate Backup Codes",
+ "DISABLE_MFA": "Disable 2FA",
+ "DISABLE_MFA_DESC": "Remove two-factor authentication from your account",
+ "DISABLE_BUTTON": "Disable Two-Factor Authentication"
+ },
+ "DISABLE": {
+ "TITLE": "Disable Two-Factor Authentication",
+ "DESCRIPTION": "You'll need to enter your password and a verification code to disable two-factor authentication.",
+ "PASSWORD": "Пароль",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Disable 2FA",
+ "CANCEL": "Скасувати",
+ "SUCCESS": "Two-factor authentication has been disabled",
+ "ERROR": "Failed to disable MFA. Please check your credentials."
+ },
+ "REGENERATE": {
+ "TITLE": "Regenerate Backup Codes",
+ "DESCRIPTION": "This will invalidate your existing backup codes and generate new ones. Enter your verification code to continue.",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Generate New Codes",
+ "CANCEL": "Скасувати",
+ "NEW_CODES_TITLE": "New Backup Codes Generated",
+ "NEW_CODES_DESC": "Your old backup codes have been invalidated. Save these new codes in a secure location.",
+ "CODES_IMPORTANT": "Important:",
+ "CODES_IMPORTANT_NOTE": " Each code can only be used once. Save them before closing this window.",
+ "DOWNLOAD_CODES": "Download Codes",
+ "COPY_ALL_CODES": "Copy All Codes",
+ "CODES_SAVED": "I've Saved My Codes",
+ "SUCCESS": "New backup codes have been generated",
+ "ERROR": "Failed to regenerate backup codes"
+ }
+ },
+ "MFA_VERIFICATION": {
+ "TITLE": "Two-Factor Authentication",
+ "DESCRIPTION": "Enter your verification code to continue",
+ "AUTHENTICATOR_APP": "Authenticator App",
+ "BACKUP_CODE": "Backup Code",
+ "ENTER_OTP_CODE": "Enter 6-digit code from your authenticator app",
+ "ENTER_BACKUP_CODE": "Enter one of your backup codes",
+ "BACKUP_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify",
+ "TRY_ANOTHER_METHOD": "Try another verification method",
+ "CANCEL_LOGIN": "Cancel and return to login",
+ "HELP_TEXT": "Having trouble signing in?",
+ "LEARN_MORE": "Learn more about 2FA",
+ "HELP_MODAL": {
+ "TITLE": "Two-Factor Authentication Help",
+ "AUTHENTICATOR_TITLE": "Using an Authenticator App",
+ "AUTHENTICATOR_DESC": "Open your authenticator app (Google Authenticator, Authy, etc.) and enter the 6-digit code shown for your account.",
+ "BACKUP_TITLE": "Using a Backup Code",
+ "BACKUP_DESC": "If you don't have access to your authenticator app, you can use one of the backup codes you saved when setting up 2FA. Each code can only be used once.",
+ "CONTACT_TITLE": "Need More Help?",
+ "CONTACT_DESC_CLOUD": "If you've lost access to both your authenticator app and backup codes, please reach out to Chatwoot support for assistance.",
+ "CONTACT_DESC_SELF_HOSTED": "If you've lost access to both your authenticator app and backup codes, please contact your administrator for assistance."
+ },
+ "VERIFICATION_FAILED": "Verification failed. Please try again."
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/uk/settings.json b/app/javascript/dashboard/i18n/locale/uk/settings.json
index 6d311258a..d973668e9 100644
--- a/app/javascript/dashboard/i18n/locale/uk/settings.json
+++ b/app/javascript/dashboard/i18n/locale/uk/settings.json
@@ -51,6 +51,13 @@
"LARGER": "Larger",
"EXTRA_LARGE": "Extra Large"
}
+ },
+ "LANGUAGE": {
+ "TITLE": "Preferred Language",
+ "NOTE": "Choose the language you want to use.",
+ "UPDATE_SUCCESS": "Your Language settings have been updated successfully",
+ "UPDATE_ERROR": "There is an error while updating the language settings, please try again",
+ "USE_ACCOUNT_DEFAULT": "Use account default"
}
},
"MESSAGE_SIGNATURE_SECTION": {
@@ -73,6 +80,11 @@
"NOTE": "Оновлення вашого пароля призведе до скидання ваших записів про вхід на інших пристроях.",
"BTN_TEXT": "Змінити пароль"
},
+ "SECURITY_SECTION": {
+ "TITLE": "Security",
+ "NOTE": "Manage additional security features for your account.",
+ "MFA_BUTTON": "Manage Two-Factor Authentication"
+ },
"ACCESS_TOKEN": {
"TITLE": "Ключ доступу",
"NOTE": "Цей ключ можна використовувати, якщо ви створюєте API-інтеграцію",
@@ -330,6 +342,7 @@
"REPORTS_LABEL": "Мітки",
"REPORTS_INBOX": "Канал",
"REPORTS_TEAM": "Команда",
+ "AGENT_ASSIGNMENT": "Agent Assignment",
"SET_AVAILABILITY_TITLE": "Позначити себе як",
"SET_YOUR_AVAILABILITY": "Встановіть доступність",
"SLA": "SLA",
@@ -350,7 +363,8 @@
"INFO_TEXT": "Дозволити системі автоматично позначати як не в мережі коли ви не використовуєте програму або панель управління.",
"INFO_SHORT": "Automatically mark offline when you aren't using the app."
},
- "DOCS": "Читати документи"
+ "DOCS": "Читати документи",
+ "SECURITY": "Security"
},
"BILLING_SETTINGS": {
"TITLE": "Біллінг",
@@ -382,6 +396,77 @@
},
"NO_BILLING_USER": "Ваш платіжний обліковий запис налаштовується. Будь ласка, оновіть сторінку та повторіть спробу."
},
+ "SECURITY_SETTINGS": {
+ "TITLE": "Security",
+ "DESCRIPTION": "Manage your account security settings.",
+ "LINK_TEXT": "Learn more about SAML SSO",
+ "SAML": {
+ "TITLE": "SAML SSO",
+ "NOTE": "Configure SAML single sign-on for your account. Users will authenticate through your identity provider instead of using email/password.",
+ "ACS_URL": {
+ "LABEL": "ACS URL",
+ "TOOLTIP": "Assertion Consumer Service URL - Configure this URL in your IdP as the destination for SAML responses"
+ },
+ "SSO_URL": {
+ "LABEL": "SSO URL",
+ "HELP": "The URL where SAML authentication requests will be sent",
+ "PLACEHOLDER": "https://your-idp.com/saml/sso"
+ },
+ "CERTIFICATE": {
+ "LABEL": "Signing certificate in PEM format",
+ "HELP": "The public certificate from your identity provider used to verify SAML responses",
+ "PLACEHOLDER": "-----BEGIN CERTIFICATE-----\nMIIC..."
+ },
+ "FINGERPRINT": {
+ "LABEL": "Fingerprint",
+ "TOOLTIP": "SHA-1 fingerprint of the certificate - Use this to verify the certificate in your IdP configuration"
+ },
+ "COPY_SUCCESS": "Код скопійований в буфер обміну",
+ "SP_ENTITY_ID": {
+ "LABEL": "SP Entity ID",
+ "HELP": "Unique identifier for this application as a service provider (auto-generated).",
+ "TOOLTIP": "Unique identifier for Chatwoot as the Service Provider - Configure this in your IdP settings"
+ },
+ "IDP_ENTITY_ID": {
+ "LABEL": "Identity Provider Entity ID",
+ "HELP": "Unique identifier for your identity provider (usually found in IdP configuration)",
+ "PLACEHOLDER": "https://your-idp.com/saml"
+ },
+ "UPDATE_BUTTON": "Update SAML Settings",
+ "API": {
+ "SUCCESS": "SAML settings updated successfully",
+ "ERROR": "Failed to update SAML settings",
+ "ERROR_LOADING": "Failed to load SAML settings",
+ "DISABLED": "SAML settings disabled successfully"
+ },
+ "VALIDATION": {
+ "REQUIRED_FIELDS": "SSO URL, Identity Provider Entity ID, and Certificate are required fields",
+ "SSO_URL_ERROR": "Please enter a valid SSO URL",
+ "CERTIFICATE_ERROR": "Certificate is required",
+ "IDP_ENTITY_ID_ERROR": "Identity Provider Entity ID is required"
+ },
+ "ENTERPRISE_PAYWALL": {
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade to an Enterprise plan to access SAML single sign-on and other advanced security features.",
+ "ASK_ADMIN": "Будь ласка, зверніться до адміністратора для оновлення."
+ },
+ "PAYWALL": {
+ "TITLE": "Upgrade to enable SAML SSO",
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade your plan to get access to SAML single sign-on and other advanced features.",
+ "UPGRADE_NOW": "Оновити зараз",
+ "CANCEL_ANYTIME": "Ви можете змінити або скасувати план у будь-який час"
+ },
+ "ATTRIBUTE_MAPPING": {
+ "TITLE": "SAML Attribute Setup",
+ "DESCRIPTION": "The following attribute mappings must be configured in your identity provider"
+ },
+ "INFO_SECTION": {
+ "TITLE": "Service Provider Information",
+ "TOOLTIP": "Copy these values and configure them in your Identity Provider to establish the SAML connection"
+ }
+ }
+ },
"CREATE_ACCOUNT": {
"NO_ACCOUNT_WARNING": "Ой! Ми не змогли знайти жодного облікового запису Chatwoot. Будь ласка, створіть новий обліковий запис, щоб продовжити.",
"NEW_ACCOUNT": "Новий акаунт",
@@ -418,5 +503,255 @@
"SWITCH_TO_REPLY": "Перейти до відповіді",
"TOGGLE_SNOOZE_DROPDOWN": "Перемкнути випадаючий список відкладення"
}
+ },
+ "ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent assignment",
+ "DESCRIPTION": "Define policies to effectively manage workload and route conversations based on the needs of inboxes and agents. Learn more here"
+ },
+ "ASSIGNMENT_POLICY": {
+ "TITLE": "Assignment policy",
+ "DESCRIPTION": "Manage how conversations get assigned in inboxes.",
+ "FEATURES": [
+ "Assign by conversations evenly or by available capacity",
+ "Add fair distribution rules to avoid overloading any agent",
+ "Add inboxes to a policy - one policy per inbox"
+ ]
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "TITLE": "Agent capacity policy",
+ "DESCRIPTION": "Manage workload for agents.",
+ "FEATURES": [
+ "Define maximum conversations per inbox",
+ "Create exceptions based on labels and time",
+ "Add agents to a policy - one policy per agent"
+ ]
+ }
+ },
+ "AGENT_ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Assignment policy",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "ORDER": "Order",
+ "PRIORITY": "Пріоритет",
+ "ACTIVE": "Активний",
+ "INACTIVE": "Inactive",
+ "POPOVER": "Added inboxes",
+ "EDIT": "Редагувати"
+ },
+ "NO_RECORDS_FOUND": "No assignment policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create assignment policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy created successfully",
+ "ERROR_MESSAGE": "Failed to create assignment policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit assignment policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_INBOX_DIALOG": {
+ "TITLE": "Add inbox",
+ "DESCRIPTION": "{inboxName} inbox is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Скасувати"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update assignment policy"
+ },
+ "INBOX_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Inbox added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Inbox removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove inbox from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Опис:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "STATUS": {
+ "LABEL": "Статус:",
+ "PLACEHOLDER": "Select status",
+ "ACTIVE": "Policy is active",
+ "INACTIVE": "Policy is inactive"
+ },
+ "ASSIGNMENT_ORDER": {
+ "LABEL": "Assignment order",
+ "ROUND_ROBIN": {
+ "LABEL": "Round robin",
+ "DESCRIPTION": "Assign conversations evenly among agents."
+ },
+ "BALANCED": {
+ "LABEL": "Balanced",
+ "DESCRIPTION": "Assign conversations based on available capacity."
+ }
+ },
+ "ASSIGNMENT_PRIORITY": {
+ "LABEL": "Assignment priority",
+ "EARLIEST_CREATED": {
+ "LABEL": "Earliest created",
+ "DESCRIPTION": "The conversation that was created first gets assigned first."
+ },
+ "LONGEST_WAITING": {
+ "LABEL": "Longest waiting",
+ "DESCRIPTION": "The conversation waiting the longest gets assigned first."
+ }
+ },
+ "FAIR_DISTRIBUTION": {
+ "LABEL": "Fair distribution policy",
+ "DESCRIPTION": "Set the maximum number of conversations that can be assigned per agent within a time window to avoid overloading any one agent. This required field defaults to 100 conversations per hour.",
+ "INPUT_MAX": "Assign max",
+ "DURATION": "Conversations per agent in every"
+ },
+ "INBOXES": {
+ "LABEL": "Added inboxes",
+ "DESCRIPTION": "Add inboxes for which this policy will be applicable.",
+ "ADD_BUTTON": "Add inbox",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Виберіть канали для додавання",
+ "ADD_BUTTON": "Додати"
+ },
+ "EMPTY_STATE": "No inboxes added to this policy, add an inbox to get started",
+ "API": {
+ "SUCCESS_MESSAGE": "Inbox successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Assignment policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete assignment policy"
+ }
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent capacity",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "POPOVER": "Added agents",
+ "EDIT": "Редагувати"
+ },
+ "NO_RECORDS_FOUND": "No agent capacity policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create agent capacity policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy created successfully",
+ "ERROR_MESSAGE": "Failed to create agent capacity policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit agent capacity policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_AGENT_DIALOG": {
+ "TITLE": "Add agent",
+ "DESCRIPTION": "{agentName} is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Скасувати"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update agent capacity policy"
+ },
+ "AGENT_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Agent added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Agent removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove agent from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Опис:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "INBOX_CAPACITY_LIMIT": {
+ "LABEL": "Inbox capacity limits",
+ "ADD_BUTTON": "Add inbox",
+ "FIELD": {
+ "SELECT_INBOX": "Select inbox",
+ "MAX_CONVERSATIONS": "Max conversations",
+ "SET_LIMIT": "Set limit"
+ },
+ "EMPTY_STATE": "No inbox limit set"
+ },
+ "EXCLUSION_RULES": {
+ "LABEL": "Exclusion rules",
+ "DESCRIPTION": "Conversations that satisfy the following conditions would not count towards agent capacity",
+ "TAGS": {
+ "LABEL": "Exclude conversations tagged with specific labels",
+ "ADD_TAG": "add tag",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Виберіть мітки для додавання"
+ },
+ "EMPTY_STATE": "No tags added to this policy."
+ },
+ "DURATION": {
+ "LABEL": "Exclude conversations older than a specified duration",
+ "PLACEHOLDER": "Set time"
+ }
+ },
+ "USERS": {
+ "LABEL": "Assigned agents",
+ "DESCRIPTION": "Add agents for which this policy will be applicable.",
+ "ADD_BUTTON": "Add agent",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Виберіть агентів для додавання",
+ "ADD_BUTTON": "Додати"
+ },
+ "EMPTY_STATE": "No agents added",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Agent capacity policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete agent capacity policy"
+ }
+ },
+ "DELETE_POLICY": {
+ "TITLE": "Delete policy",
+ "DESCRIPTION": "Are you sure you want to delete this policy? This action cannot be undone.",
+ "CONFIRM_BUTTON_LABEL": "Видалити",
+ "CANCEL_BUTTON_LABEL": "Скасувати"
+ }
}
}
diff --git a/app/javascript/dashboard/i18n/locale/uk/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/uk/whatsappTemplates.json
index d181cd3f1..2a583f8a3 100644
--- a/app/javascript/dashboard/i18n/locale/uk/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/uk/whatsappTemplates.json
@@ -40,6 +40,7 @@
"BUTTON_LABEL": "Button {index}",
"COUPON_CODE": "Enter coupon code (max 15 chars)",
"MEDIA_URL_LABEL": "Enter {type} URL",
+ "DOCUMENT_NAME_PLACEHOLDER": "Enter document filename (e.g., Invoice_2025.pdf)",
"BUTTON_PARAMETER": "Enter button parameter"
}
}
diff --git a/app/javascript/dashboard/i18n/locale/ur/automation.json b/app/javascript/dashboard/i18n/locale/ur/automation.json
index 3ef2ccc5e..0c1f76d5b 100644
--- a/app/javascript/dashboard/i18n/locale/ur/automation.json
+++ b/app/javascript/dashboard/i18n/locale/ur/automation.json
@@ -177,7 +177,8 @@
"REFERER_LINK": "Referrer Link",
"ASSIGNEE_NAME": "Assignee",
"TEAM_NAME": "Team",
- "PRIORITY": "Priority"
+ "PRIORITY": "Priority",
+ "LABELS": "Labels"
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/ur/contact.json b/app/javascript/dashboard/i18n/locale/ur/contact.json
index b87865ca0..1456ed8ae 100644
--- a/app/javascript/dashboard/i18n/locale/ur/contact.json
+++ b/app/javascript/dashboard/i18n/locale/ur/contact.json
@@ -610,6 +610,15 @@
"SEND_MESSAGE": "پیغام بھیجیں"
}
},
+ "TWILIO_OPTIONS": {
+ "LABEL": "Select template",
+ "SEARCH_PLACEHOLDER": "Search templates",
+ "EMPTY_STATE": "No templates found",
+ "TEMPLATE_PARSER": {
+ "BACK": "Go back",
+ "SEND_MESSAGE": "پیغام بھیجیں"
+ }
+ },
"ACTION_BUTTONS": {
"DISCARD": "Discard",
"SEND": "Send ({keyCode})"
diff --git a/app/javascript/dashboard/i18n/locale/ur/contactFilters.json b/app/javascript/dashboard/i18n/locale/ur/contactFilters.json
index 258a072ea..5d065730c 100644
--- a/app/javascript/dashboard/i18n/locale/ur/contactFilters.json
+++ b/app/javascript/dashboard/i18n/locale/ur/contactFilters.json
@@ -48,7 +48,8 @@
"CREATED_AT": "Created At",
"LAST_ACTIVITY": "آخری سرگرمی",
"REFERER_LINK": "Referrer link",
- "BLOCKED": "Blocked"
+ "BLOCKED": "Blocked",
+ "LABELS": "Labels"
},
"GROUPS": {
"STANDARD_FILTERS": "Standard Filters",
diff --git a/app/javascript/dashboard/i18n/locale/ur/contentTemplates.json b/app/javascript/dashboard/i18n/locale/ur/contentTemplates.json
new file mode 100644
index 000000000..a9b1d54c4
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/ur/contentTemplates.json
@@ -0,0 +1,51 @@
+{
+ "CONTENT_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Twilio Templates",
+ "SUBTITLE": "Select the Twilio template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Search Templates",
+ "NO_TEMPLATES_FOUND": "No templates found for",
+ "NO_CONTENT": "No content",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Category",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No Twilio templates available. Click refresh to sync templates from Twilio.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Language",
+ "TEMPLATE_BODY": "Template Body",
+ "CATEGORY": "Category"
+ },
+ "TYPES": {
+ "MEDIA": "Media",
+ "QUICK_REPLY": "Quick Reply",
+ "TEXT": "Text"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variables",
+ "LANGUAGE": "Language",
+ "CATEGORY": "Category",
+ "VARIABLE_PLACEHOLDER": "Enter {variable} value",
+ "GO_BACK_LABEL": "Go Back",
+ "SEND_MESSAGE_LABEL": "Send Message",
+ "FORM_ERROR_MESSAGE": "Please fill all variables before sending",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "MEDIA_URL_LABEL": "Enter full media URL",
+ "MEDIA_URL_PLACEHOLDER": "https://example.com/image.jpg"
+ },
+ "FORM": {
+ "BACK_BUTTON": "Back",
+ "SEND_MESSAGE_BUTTON": "Send Message"
+ }
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/ur/conversation.json b/app/javascript/dashboard/i18n/locale/ur/conversation.json
index 67c016b4f..fc03fbc7c 100644
--- a/app/javascript/dashboard/i18n/locale/ur/conversation.json
+++ b/app/javascript/dashboard/i18n/locale/ur/conversation.json
@@ -35,6 +35,11 @@
"API_HOURS_WINDOW": "You can only reply to this conversation within {hours} hours",
"NOT_ASSIGNED_TO_YOU": "یہ گفتگو آپ کو تفویض نہیں کی گئی ہے۔ کیا آپ یہ گفتگو اپنے آپ کو تفویض کرنا چاہیں گے?",
"ASSIGN_TO_ME": "مجھے تفویض کریں۔",
+ "BOT_HANDOFF_MESSAGE": "You are responding to a conversation which is currently handled by an assistant or a bot.",
+ "BOT_HANDOFF_ACTION": "Mark open and assign to you",
+ "BOT_HANDOFF_REOPEN_ACTION": "Mark conversation open",
+ "BOT_HANDOFF_SUCCESS": "Conversation has been handed over to you",
+ "BOT_HANDOFF_ERROR": "Failed to take over the conversation. Please try again.",
"TWILIO_WHATSAPP_CAN_REPLY": "آپ اس بات چیت کا جواب صرف ایک ٹیمپلیٹ پیغام کا استعمال کرتے ہوئے دے سکتے ہیں, کيونکہ",
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "24 گھنٹے میسج ونڈو کی پابندی",
"OLD_INSTAGRAM_INBOX_REPLY_BANNER": "This Instagram account was migrated to the new Instagram channel inbox. All new messages will show up there. You won’t be able to send messages from this conversation anymore.",
@@ -66,6 +71,17 @@
"SHOW_LABELS": "Show labels",
"HIDE_LABELS": "Hide labels"
},
+ "VOICE_CALL": {
+ "INCOMING_CALL": "Incoming call",
+ "OUTGOING_CALL": "Outgoing call",
+ "CALL_IN_PROGRESS": "Call in progress",
+ "NO_ANSWER": "No answer",
+ "MISSED_CALL": "Missed call",
+ "CALL_ENDED": "Call ended",
+ "NOT_ANSWERED_YET": "Not answered yet",
+ "THEY_ANSWERED": "They answered",
+ "YOU_ANSWERED": "You answered"
+ },
"HEADER": {
"RESOLVE_ACTION": "حل کریں۔",
"REOPEN_ACTION": "دوبارہ کھولیں۔",
diff --git a/app/javascript/dashboard/i18n/locale/ur/helpCenter.json b/app/javascript/dashboard/i18n/locale/ur/helpCenter.json
index f09b765c0..962efbf1d 100644
--- a/app/javascript/dashboard/i18n/locale/ur/helpCenter.json
+++ b/app/javascript/dashboard/i18n/locale/ur/helpCenter.json
@@ -741,7 +741,8 @@
"LIVE_CHAT_WIDGET": {
"LABEL": "Live chat widget",
"PLACEHOLDER": "Select live chat widget",
- "HELP_TEXT": "Select a live chat widget that will appear on your help center"
+ "HELP_TEXT": "Select a live chat widget that will appear on your help center",
+ "NONE_OPTION": "No widget"
},
"BRAND_COLOR": {
"LABEL": "Brand color"
@@ -807,6 +808,58 @@
"ERROR_MESSAGE": "Unable to update portal"
}
}
+ },
+ "PDF_UPLOAD": {
+ "TITLE": "Upload PDF Document",
+ "DESCRIPTION": "Upload a PDF document to automatically generate FAQs using AI",
+ "DRAG_DROP_TEXT": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "ADDITIONAL_CONTEXT_LABEL": "Additional Context (Optional)",
+ "ADDITIONAL_CONTEXT_PLACEHOLDER": "Provide any additional context or instructions for FAQ generation...",
+ "UPLOADING": "Uploading...",
+ "UPLOAD": "Upload & Process",
+ "CANCEL": "منسوخ کریں۔",
+ "ERROR_INVALID_TYPE": "Please select a valid PDF file",
+ "ERROR_FILE_TOO_LARGE": "File size must be less than 512MB",
+ "ERROR_UPLOAD_FAILED": "Failed to upload PDF. Please try again."
+ },
+ "PDF_DOCUMENTS": {
+ "TITLE": "PDF Documents",
+ "DESCRIPTION": "Manage uploaded PDF documents and generate FAQs from them",
+ "UPLOAD_PDF": "Upload PDF",
+ "UPLOAD_FIRST_PDF": "Upload your first PDF",
+ "UPLOADED_BY": "Uploaded by",
+ "GENERATE_FAQS": "Generate FAQs",
+ "GENERATING": "Generating...",
+ "CONFIRM_DELETE": "Are you sure you want to delete {filename}?",
+ "EMPTY_STATE": {
+ "TITLE": "No PDF documents yet",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQs using AI"
+ },
+ "STATUS": {
+ "UPLOADED": "Ready",
+ "PROCESSING": "Processing",
+ "PROCESSED": "Completed",
+ "FAILED": "Failed"
+ }
+ },
+ "CONTENT_GENERATION": {
+ "TITLE": "Content Generation",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQ content using AI",
+ "UPLOAD_TITLE": "Upload PDF Document",
+ "DRAG_DROP": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "UPLOADING": "Processing document...",
+ "UPLOAD_SUCCESS": "Document processed successfully!",
+ "UPLOAD_ERROR": "Failed to upload document. Please try again.",
+ "INVALID_FILE_TYPE": "Please select a valid PDF file",
+ "FILE_TOO_LARGE": "File size must be less than 512MB",
+ "GENERATED_CONTENT": "Generated FAQ Content",
+ "PUBLISH_SELECTED": "Publish Selected",
+ "PUBLISHING": "Publishing...",
+ "FROM_DOCUMENT": "From document",
+ "NO_CONTENT": "No generated content available. Upload a PDF document to get started.",
+ "LOADING": "Loading generated content..."
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/ur/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/ur/inboxMgmt.json
index 1e0d7a922..124684241 100644
--- a/app/javascript/dashboard/i18n/locale/ur/inboxMgmt.json
+++ b/app/javascript/dashboard/i18n/locale/ur/inboxMgmt.json
@@ -272,8 +272,8 @@
},
"SUBMIT_BUTTON": "Create WhatsApp Channel",
"EMBEDDED_SIGNUP": {
- "TITLE": "Quick Setup with Meta",
- "DESC": "You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
+ "TITLE": "Quick setup with Meta",
+ "DESC": "Use the WhatsApp Embedded Signup flow to quickly connect new numbers. You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
"BENEFITS": {
"TITLE": "Benefits of Embedded Signup:",
"EASY_SETUP": "No manual configuration required",
@@ -281,9 +281,8 @@
"AUTO_CONFIG": "Automatic webhook and phone number configuration"
},
"LEARN_MORE": {
- "TEXT": "To learn more about integrated signup, pricing, and limitations, visit",
- "LINK_TEXT": "this link.",
- "LINK_URL": "https://developers.facebook.com/docs/whatsapp/embedded-signup/custom-flows/onboarding-business-app-users#limitations"
+ "TEXT": "To learn more about integrated signup, pricing, and limitations, visit {link}.",
+ "LINK_TEXT": "this link"
},
"SUBMIT_BUTTON": "Connect with WhatsApp Business",
"AUTH_PROCESSING": "Authenticating with Meta",
@@ -296,7 +295,9 @@
"INVALID_BUSINESS_DATA": "Invalid business data received from Facebook. Please try again.",
"SIGNUP_ERROR": "Signup error occurred",
"AUTH_NOT_COMPLETED": "Authentication not completed. Please restart the process.",
- "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured"
+ "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured",
+ "MANUAL_FALLBACK": "If your number is already connected to the WhatsApp Business Platform (API), or if you’re a tech provider onboarding your own number, please use the {link} flow",
+ "MANUAL_LINK_TEXT": "manual setup flow"
},
"API": {
"ERROR_MESSAGE": "We were not able to save the WhatsApp channel"
@@ -424,7 +425,51 @@
},
"AUTH": {
"TITLE": "Choose a channel",
- "DESC": "Chatwoot supports live-chat widgets, Facebook Messenger, Twitter profiles, WhatsApp, Emails, etc., as channels. If you want to build a custom channel, you can create it using the API channel. To get started, choose one of the channels below."
+ "DESC": "Chatwoot supports live-chat widgets, Facebook Messenger, Twitter profiles, WhatsApp, Emails, etc., as channels. If you want to build a custom channel, you can create it using the API channel. To get started, choose one of the channels below.",
+ "TITLE_NEXT": "Complete the setup",
+ "TITLE_FINISH": "Voilà!",
+ "CHANNEL": {
+ "WEBSITE": {
+ "TITLE": "Website",
+ "DESCRIPTION": "Create a live-chat widget"
+ },
+ "FACEBOOK": {
+ "TITLE": "فیس بک",
+ "DESCRIPTION": "Connect your Facebook page"
+ },
+ "WHATSAPP": {
+ "TITLE": "WhatsApp",
+ "DESCRIPTION": "Support your customers on WhatsApp"
+ },
+ "EMAIL": {
+ "TITLE": "Email",
+ "DESCRIPTION": "Connect with Gmail, Outlook, or other providers"
+ },
+ "SMS": {
+ "TITLE": "SMS",
+ "DESCRIPTION": "Integrate SMS channel with Twilio or bandwidth"
+ },
+ "API": {
+ "TITLE": "API",
+ "DESCRIPTION": "Make a custom channel using our API"
+ },
+ "TELEGRAM": {
+ "TITLE": "Telegram",
+ "DESCRIPTION": "Configure Telegram channel using Bot token"
+ },
+ "LINE": {
+ "TITLE": "Line",
+ "DESCRIPTION": "Integrate your Line channel"
+ },
+ "INSTAGRAM": {
+ "TITLE": "Instagram",
+ "DESCRIPTION": "Connect your instagram account"
+ },
+ "VOICE": {
+ "TITLE": "Voice",
+ "DESCRIPTION": "Integrate with Twilio Voice"
+ }
+ }
},
"AGENTS": {
"TITLE": "ایجنٹ",
@@ -478,7 +523,10 @@
"MESSAGE": "You can now engage with your customers through your new Channel. Happy supporting",
"BUTTON_TEXT": "Take me there",
"MORE_SETTINGS": "More settings",
- "WEBSITE_SUCCESS": "You have successfully finished creating a website channel. Copy the code shown below and paste it on your website. Next time a customer use the live chat, the conversation will automatically appear on your inbox."
+ "WEBSITE_SUCCESS": "You have successfully finished creating a website channel. Copy the code shown below and paste it on your website. Next time a customer use the live chat, the conversation will automatically appear on your inbox.",
+ "WHATSAPP_QR_INSTRUCTION": "Scan the QR code above to quickly test your WhatsApp inbox",
+ "MESSENGER_QR_INSTRUCTION": "Scan the QR code above to quickly test your Facebook Messenger inbox",
+ "TELEGRAM_QR_INSTRUCTION": "Scan the QR code above to quickly test your Telegram inbox"
},
"REAUTH": "Reauthorize",
"VIEW": "View",
@@ -868,9 +916,18 @@
"SCRIPT_SETTINGS": "\n window.chatwootSettings = {options};"
},
"EMAIL_PROVIDERS": {
- "MICROSOFT": "Microsoft",
- "GOOGLE": "Google",
- "OTHER_PROVIDERS": "Other Providers"
+ "MICROSOFT": {
+ "TITLE": "Microsoft",
+ "DESCRIPTION": "Connect with Microsoft"
+ },
+ "GOOGLE": {
+ "TITLE": "Google",
+ "DESCRIPTION": "Connect with Google"
+ },
+ "OTHER_PROVIDERS": {
+ "TITLE": "Other Providers",
+ "DESCRIPTION": "Connect with Other Providers"
+ }
},
"CHANNELS": {
"MESSENGER": "Messenger",
diff --git a/app/javascript/dashboard/i18n/locale/ur/integrations.json b/app/javascript/dashboard/i18n/locale/ur/integrations.json
index eaab517a2..5b33237af 100644
--- a/app/javascript/dashboard/i18n/locale/ur/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/ur/integrations.json
@@ -704,11 +704,28 @@
"ERROR_MESSAGE": "There was an error creating the document, please try again."
},
"FORM": {
+ "TYPE": {
+ "LABEL": "Document Type",
+ "URL": "URL",
+ "PDF": "PDF File"
+ },
"URL": {
"LABEL": "URL",
"PLACEHOLDER": "Enter the URL of the document",
"ERROR": "Please provide a valid URL for the document"
},
+ "PDF_FILE": {
+ "LABEL": "PDF File",
+ "CHOOSE_FILE": "Choose PDF file",
+ "ERROR": "Please select a PDF file",
+ "HELP_TEXT": "Maximum file size: 10MB",
+ "INVALID_TYPE": "Please select a valid PDF file",
+ "TOO_LARGE": "File size exceeds 10MB limit"
+ },
+ "NAME": {
+ "LABEL": "Document Name (Optional)",
+ "PLACEHOLDER": "Enter a name for the document"
+ },
"ASSISTANT": {
"LABEL": "Assistant",
"PLACEHOLDER": "Select the assistant",
@@ -744,6 +761,7 @@
"SELECTED": "{count} selected",
"SELECT_ALL": "Select all ({count})",
"UNSELECT_ALL": "Unselect all ({count})",
+ "SEARCH_PLACEHOLDER": "Search FAQs...",
"BULK_APPROVE_BUTTON": "Approve",
"BULK_DELETE_BUTTON": "حذف کریں۔",
"BULK_APPROVE": {
diff --git a/app/javascript/dashboard/i18n/locale/ur/mfa.json b/app/javascript/dashboard/i18n/locale/ur/mfa.json
new file mode 100644
index 000000000..c50b628aa
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/ur/mfa.json
@@ -0,0 +1,106 @@
+{
+ "MFA_SETTINGS": {
+ "TITLE": "Two-Factor Authentication",
+ "SUBTITLE": "Secure your account with TOTP-based authentication",
+ "DESCRIPTION": "Add an extra layer of security to your account using a time-based one-time password (TOTP)",
+ "STATUS_TITLE": "Authentication Status",
+ "STATUS_DESCRIPTION": "Manage your two-factor authentication settings and backup recovery codes",
+ "ENABLED": "Enabled",
+ "DISABLED": "Disabled",
+ "STATUS_ENABLED": "Two-factor authentication is active",
+ "STATUS_ENABLED_DESC": "Your account is protected with an additional layer of security",
+ "ENABLE_BUTTON": "Enable Two-Factor Authentication",
+ "ENHANCE_SECURITY": "Enhance Your Account Security",
+ "ENHANCE_SECURITY_DESC": "Two-factor authentication adds an extra layer of security by requiring a verification code from your authenticator app in addition to your password.",
+ "SETUP": {
+ "STEP_NUMBER_1": "1",
+ "STEP_NUMBER_2": "2",
+ "STEP1_TITLE": "Scan QR Code with Your Authenticator App",
+ "STEP1_DESCRIPTION": "Use Google Authenticator, Authy, or any TOTP-compatible app",
+ "LOADING_QR": "Loading...",
+ "MANUAL_ENTRY": "Can't scan? Enter code manually",
+ "SECRET_KEY": "Secret Key",
+ "COPY": "Copy",
+ "ENTER_CODE": "Enter the 6-digit code from your authenticator app",
+ "ENTER_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify & Continue",
+ "CANCEL": "منسوخ کریں۔",
+ "ERROR_STARTING": "MFA not enabled. Please contact administrator.",
+ "INVALID_CODE": "Invalid verification code",
+ "SECRET_COPIED": "Secret key copied to clipboard",
+ "SUCCESS": "Two-factor authentication has been enabled successfully"
+ },
+ "BACKUP": {
+ "TITLE": "Save Your Backup Codes",
+ "DESCRIPTION": "Keep these codes safe. Each can be used once if you lose access to your authenticator",
+ "IMPORTANT": "Important:",
+ "IMPORTANT_NOTE": " Save these codes in a secure location. You won't be able to see them again.",
+ "DOWNLOAD": "ڈاؤن لوڈ کریں",
+ "COPY_ALL": "Copy All",
+ "CONFIRM": "I have saved my backup codes in a secure location and understand that I won't be able to see them again",
+ "COMPLETE_SETUP": "Complete Setup",
+ "CODES_COPIED": "Backup codes copied to clipboard"
+ },
+ "MANAGEMENT": {
+ "BACKUP_CODES": "Backup Codes",
+ "BACKUP_CODES_DESC": "Generate new codes if you've lost or used your existing ones",
+ "REGENERATE": "Regenerate Backup Codes",
+ "DISABLE_MFA": "Disable 2FA",
+ "DISABLE_MFA_DESC": "Remove two-factor authentication from your account",
+ "DISABLE_BUTTON": "Disable Two-Factor Authentication"
+ },
+ "DISABLE": {
+ "TITLE": "Disable Two-Factor Authentication",
+ "DESCRIPTION": "You'll need to enter your password and a verification code to disable two-factor authentication.",
+ "PASSWORD": "Password",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Disable 2FA",
+ "CANCEL": "منسوخ کریں۔",
+ "SUCCESS": "Two-factor authentication has been disabled",
+ "ERROR": "Failed to disable MFA. Please check your credentials."
+ },
+ "REGENERATE": {
+ "TITLE": "Regenerate Backup Codes",
+ "DESCRIPTION": "This will invalidate your existing backup codes and generate new ones. Enter your verification code to continue.",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Generate New Codes",
+ "CANCEL": "منسوخ کریں۔",
+ "NEW_CODES_TITLE": "New Backup Codes Generated",
+ "NEW_CODES_DESC": "Your old backup codes have been invalidated. Save these new codes in a secure location.",
+ "CODES_IMPORTANT": "Important:",
+ "CODES_IMPORTANT_NOTE": " Each code can only be used once. Save them before closing this window.",
+ "DOWNLOAD_CODES": "Download Codes",
+ "COPY_ALL_CODES": "Copy All Codes",
+ "CODES_SAVED": "I've Saved My Codes",
+ "SUCCESS": "New backup codes have been generated",
+ "ERROR": "Failed to regenerate backup codes"
+ }
+ },
+ "MFA_VERIFICATION": {
+ "TITLE": "Two-Factor Authentication",
+ "DESCRIPTION": "Enter your verification code to continue",
+ "AUTHENTICATOR_APP": "Authenticator App",
+ "BACKUP_CODE": "Backup Code",
+ "ENTER_OTP_CODE": "Enter 6-digit code from your authenticator app",
+ "ENTER_BACKUP_CODE": "Enter one of your backup codes",
+ "BACKUP_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify",
+ "TRY_ANOTHER_METHOD": "Try another verification method",
+ "CANCEL_LOGIN": "Cancel and return to login",
+ "HELP_TEXT": "Having trouble signing in?",
+ "LEARN_MORE": "Learn more about 2FA",
+ "HELP_MODAL": {
+ "TITLE": "Two-Factor Authentication Help",
+ "AUTHENTICATOR_TITLE": "Using an Authenticator App",
+ "AUTHENTICATOR_DESC": "Open your authenticator app (Google Authenticator, Authy, etc.) and enter the 6-digit code shown for your account.",
+ "BACKUP_TITLE": "Using a Backup Code",
+ "BACKUP_DESC": "If you don't have access to your authenticator app, you can use one of the backup codes you saved when setting up 2FA. Each code can only be used once.",
+ "CONTACT_TITLE": "Need More Help?",
+ "CONTACT_DESC_CLOUD": "If you've lost access to both your authenticator app and backup codes, please reach out to Chatwoot support for assistance.",
+ "CONTACT_DESC_SELF_HOSTED": "If you've lost access to both your authenticator app and backup codes, please contact your administrator for assistance."
+ },
+ "VERIFICATION_FAILED": "Verification failed. Please try again."
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/ur/settings.json b/app/javascript/dashboard/i18n/locale/ur/settings.json
index 326f39c98..a91b5c96e 100644
--- a/app/javascript/dashboard/i18n/locale/ur/settings.json
+++ b/app/javascript/dashboard/i18n/locale/ur/settings.json
@@ -51,6 +51,13 @@
"LARGER": "Larger",
"EXTRA_LARGE": "Extra Large"
}
+ },
+ "LANGUAGE": {
+ "TITLE": "Preferred Language",
+ "NOTE": "Choose the language you want to use.",
+ "UPDATE_SUCCESS": "Your Language settings have been updated successfully",
+ "UPDATE_ERROR": "There is an error while updating the language settings, please try again",
+ "USE_ACCOUNT_DEFAULT": "Use account default"
}
},
"MESSAGE_SIGNATURE_SECTION": {
@@ -73,6 +80,11 @@
"NOTE": "Updating your password would reset your logins in multiple devices.",
"BTN_TEXT": "Change password"
},
+ "SECURITY_SECTION": {
+ "TITLE": "Security",
+ "NOTE": "Manage additional security features for your account.",
+ "MFA_BUTTON": "Manage Two-Factor Authentication"
+ },
"ACCESS_TOKEN": {
"TITLE": "Access Token",
"NOTE": "This token can be used if you are building an API based integration",
@@ -330,6 +342,7 @@
"REPORTS_LABEL": "Labels",
"REPORTS_INBOX": "ان باکس",
"REPORTS_TEAM": "Team",
+ "AGENT_ASSIGNMENT": "Agent Assignment",
"SET_AVAILABILITY_TITLE": "Set yourself as",
"SET_YOUR_AVAILABILITY": "Set your availability",
"SLA": "SLA",
@@ -350,7 +363,8 @@
"INFO_TEXT": "Let the system automatically mark you offline when you aren't using the app or dashboard.",
"INFO_SHORT": "Automatically mark offline when you aren't using the app."
},
- "DOCS": "Read docs"
+ "DOCS": "Read docs",
+ "SECURITY": "Security"
},
"BILLING_SETTINGS": {
"TITLE": "Billing",
@@ -382,6 +396,77 @@
},
"NO_BILLING_USER": "Your billing account is being configured. Please refresh the page and try again."
},
+ "SECURITY_SETTINGS": {
+ "TITLE": "Security",
+ "DESCRIPTION": "Manage your account security settings.",
+ "LINK_TEXT": "Learn more about SAML SSO",
+ "SAML": {
+ "TITLE": "SAML SSO",
+ "NOTE": "Configure SAML single sign-on for your account. Users will authenticate through your identity provider instead of using email/password.",
+ "ACS_URL": {
+ "LABEL": "ACS URL",
+ "TOOLTIP": "Assertion Consumer Service URL - Configure this URL in your IdP as the destination for SAML responses"
+ },
+ "SSO_URL": {
+ "LABEL": "SSO URL",
+ "HELP": "The URL where SAML authentication requests will be sent",
+ "PLACEHOLDER": "https://your-idp.com/saml/sso"
+ },
+ "CERTIFICATE": {
+ "LABEL": "Signing certificate in PEM format",
+ "HELP": "The public certificate from your identity provider used to verify SAML responses",
+ "PLACEHOLDER": "-----BEGIN CERTIFICATE-----\nMIIC..."
+ },
+ "FINGERPRINT": {
+ "LABEL": "Fingerprint",
+ "TOOLTIP": "SHA-1 fingerprint of the certificate - Use this to verify the certificate in your IdP configuration"
+ },
+ "COPY_SUCCESS": "Code copied to clipboard successfully",
+ "SP_ENTITY_ID": {
+ "LABEL": "SP Entity ID",
+ "HELP": "Unique identifier for this application as a service provider (auto-generated).",
+ "TOOLTIP": "Unique identifier for Chatwoot as the Service Provider - Configure this in your IdP settings"
+ },
+ "IDP_ENTITY_ID": {
+ "LABEL": "Identity Provider Entity ID",
+ "HELP": "Unique identifier for your identity provider (usually found in IdP configuration)",
+ "PLACEHOLDER": "https://your-idp.com/saml"
+ },
+ "UPDATE_BUTTON": "Update SAML Settings",
+ "API": {
+ "SUCCESS": "SAML settings updated successfully",
+ "ERROR": "Failed to update SAML settings",
+ "ERROR_LOADING": "Failed to load SAML settings",
+ "DISABLED": "SAML settings disabled successfully"
+ },
+ "VALIDATION": {
+ "REQUIRED_FIELDS": "SSO URL, Identity Provider Entity ID, and Certificate are required fields",
+ "SSO_URL_ERROR": "Please enter a valid SSO URL",
+ "CERTIFICATE_ERROR": "Certificate is required",
+ "IDP_ENTITY_ID_ERROR": "Identity Provider Entity ID is required"
+ },
+ "ENTERPRISE_PAYWALL": {
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade to an Enterprise plan to access SAML single sign-on and other advanced security features.",
+ "ASK_ADMIN": "Please reach out to your administrator for the upgrade."
+ },
+ "PAYWALL": {
+ "TITLE": "Upgrade to enable SAML SSO",
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade your plan to get access to SAML single sign-on and other advanced features.",
+ "UPGRADE_NOW": "Upgrade now",
+ "CANCEL_ANYTIME": "You can change or cancel your plan anytime"
+ },
+ "ATTRIBUTE_MAPPING": {
+ "TITLE": "SAML Attribute Setup",
+ "DESCRIPTION": "The following attribute mappings must be configured in your identity provider"
+ },
+ "INFO_SECTION": {
+ "TITLE": "Service Provider Information",
+ "TOOLTIP": "Copy these values and configure them in your Identity Provider to establish the SAML connection"
+ }
+ }
+ },
"CREATE_ACCOUNT": {
"NO_ACCOUNT_WARNING": "Uh oh! We could not find any Chatwoot accounts. Please create a new account to continue.",
"NEW_ACCOUNT": "New Account",
@@ -418,5 +503,255 @@
"SWITCH_TO_REPLY": "Switch to Reply",
"TOGGLE_SNOOZE_DROPDOWN": "Toggle snooze dropdown"
}
+ },
+ "ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent assignment",
+ "DESCRIPTION": "Define policies to effectively manage workload and route conversations based on the needs of inboxes and agents. Learn more here"
+ },
+ "ASSIGNMENT_POLICY": {
+ "TITLE": "Assignment policy",
+ "DESCRIPTION": "Manage how conversations get assigned in inboxes.",
+ "FEATURES": [
+ "Assign by conversations evenly or by available capacity",
+ "Add fair distribution rules to avoid overloading any agent",
+ "Add inboxes to a policy - one policy per inbox"
+ ]
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "TITLE": "Agent capacity policy",
+ "DESCRIPTION": "Manage workload for agents.",
+ "FEATURES": [
+ "Define maximum conversations per inbox",
+ "Create exceptions based on labels and time",
+ "Add agents to a policy - one policy per agent"
+ ]
+ }
+ },
+ "AGENT_ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Assignment policy",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "ORDER": "Order",
+ "PRIORITY": "Priority",
+ "ACTIVE": "Active",
+ "INACTIVE": "Inactive",
+ "POPOVER": "Added inboxes",
+ "EDIT": "ترمیم"
+ },
+ "NO_RECORDS_FOUND": "No assignment policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create assignment policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy created successfully",
+ "ERROR_MESSAGE": "Failed to create assignment policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit assignment policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_INBOX_DIALOG": {
+ "TITLE": "Add inbox",
+ "DESCRIPTION": "{inboxName} inbox is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "منسوخ کریں۔"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update assignment policy"
+ },
+ "INBOX_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Inbox added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Inbox removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove inbox from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Description:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "STATUS": {
+ "LABEL": "اسٹیٹس:",
+ "PLACEHOLDER": "Select status",
+ "ACTIVE": "Policy is active",
+ "INACTIVE": "Policy is inactive"
+ },
+ "ASSIGNMENT_ORDER": {
+ "LABEL": "Assignment order",
+ "ROUND_ROBIN": {
+ "LABEL": "Round robin",
+ "DESCRIPTION": "Assign conversations evenly among agents."
+ },
+ "BALANCED": {
+ "LABEL": "Balanced",
+ "DESCRIPTION": "Assign conversations based on available capacity."
+ }
+ },
+ "ASSIGNMENT_PRIORITY": {
+ "LABEL": "Assignment priority",
+ "EARLIEST_CREATED": {
+ "LABEL": "Earliest created",
+ "DESCRIPTION": "The conversation that was created first gets assigned first."
+ },
+ "LONGEST_WAITING": {
+ "LABEL": "Longest waiting",
+ "DESCRIPTION": "The conversation waiting the longest gets assigned first."
+ }
+ },
+ "FAIR_DISTRIBUTION": {
+ "LABEL": "Fair distribution policy",
+ "DESCRIPTION": "Set the maximum number of conversations that can be assigned per agent within a time window to avoid overloading any one agent. This required field defaults to 100 conversations per hour.",
+ "INPUT_MAX": "Assign max",
+ "DURATION": "Conversations per agent in every"
+ },
+ "INBOXES": {
+ "LABEL": "Added inboxes",
+ "DESCRIPTION": "Add inboxes for which this policy will be applicable.",
+ "ADD_BUTTON": "Add inbox",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select inboxes to add",
+ "ADD_BUTTON": "شامل کریں۔"
+ },
+ "EMPTY_STATE": "No inboxes added to this policy, add an inbox to get started",
+ "API": {
+ "SUCCESS_MESSAGE": "Inbox successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Assignment policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete assignment policy"
+ }
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent capacity",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "POPOVER": "Added agents",
+ "EDIT": "ترمیم"
+ },
+ "NO_RECORDS_FOUND": "No agent capacity policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create agent capacity policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy created successfully",
+ "ERROR_MESSAGE": "Failed to create agent capacity policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit agent capacity policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_AGENT_DIALOG": {
+ "TITLE": "Add agent",
+ "DESCRIPTION": "{agentName} is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "منسوخ کریں۔"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update agent capacity policy"
+ },
+ "AGENT_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Agent added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Agent removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove agent from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Description:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "INBOX_CAPACITY_LIMIT": {
+ "LABEL": "Inbox capacity limits",
+ "ADD_BUTTON": "Add inbox",
+ "FIELD": {
+ "SELECT_INBOX": "Select inbox",
+ "MAX_CONVERSATIONS": "Max conversations",
+ "SET_LIMIT": "Set limit"
+ },
+ "EMPTY_STATE": "No inbox limit set"
+ },
+ "EXCLUSION_RULES": {
+ "LABEL": "Exclusion rules",
+ "DESCRIPTION": "Conversations that satisfy the following conditions would not count towards agent capacity",
+ "TAGS": {
+ "LABEL": "Exclude conversations tagged with specific labels",
+ "ADD_TAG": "add tag",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select tags to add"
+ },
+ "EMPTY_STATE": "No tags added to this policy."
+ },
+ "DURATION": {
+ "LABEL": "Exclude conversations older than a specified duration",
+ "PLACEHOLDER": "Set time"
+ }
+ },
+ "USERS": {
+ "LABEL": "Assigned agents",
+ "DESCRIPTION": "Add agents for which this policy will be applicable.",
+ "ADD_BUTTON": "Add agent",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select agents to add",
+ "ADD_BUTTON": "شامل کریں۔"
+ },
+ "EMPTY_STATE": "No agents added",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Agent capacity policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete agent capacity policy"
+ }
+ },
+ "DELETE_POLICY": {
+ "TITLE": "Delete policy",
+ "DESCRIPTION": "Are you sure you want to delete this policy? This action cannot be undone.",
+ "CONFIRM_BUTTON_LABEL": "حذف کریں۔",
+ "CANCEL_BUTTON_LABEL": "منسوخ کریں۔"
+ }
}
}
diff --git a/app/javascript/dashboard/i18n/locale/ur/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/ur/whatsappTemplates.json
index 5f53faaa8..cf28312dc 100644
--- a/app/javascript/dashboard/i18n/locale/ur/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/ur/whatsappTemplates.json
@@ -40,6 +40,7 @@
"BUTTON_LABEL": "Button {index}",
"COUPON_CODE": "Enter coupon code (max 15 chars)",
"MEDIA_URL_LABEL": "Enter {type} URL",
+ "DOCUMENT_NAME_PLACEHOLDER": "Enter document filename (e.g., Invoice_2025.pdf)",
"BUTTON_PARAMETER": "Enter button parameter"
}
}
diff --git a/app/javascript/dashboard/i18n/locale/ur_IN/automation.json b/app/javascript/dashboard/i18n/locale/ur_IN/automation.json
index 80274f488..43245a1d5 100644
--- a/app/javascript/dashboard/i18n/locale/ur_IN/automation.json
+++ b/app/javascript/dashboard/i18n/locale/ur_IN/automation.json
@@ -177,7 +177,8 @@
"REFERER_LINK": "Referrer Link",
"ASSIGNEE_NAME": "Assignee",
"TEAM_NAME": "Team",
- "PRIORITY": "Priority"
+ "PRIORITY": "Priority",
+ "LABELS": "Labels"
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/ur_IN/contact.json b/app/javascript/dashboard/i18n/locale/ur_IN/contact.json
index 735489a08..328e15aaa 100644
--- a/app/javascript/dashboard/i18n/locale/ur_IN/contact.json
+++ b/app/javascript/dashboard/i18n/locale/ur_IN/contact.json
@@ -610,6 +610,15 @@
"SEND_MESSAGE": "Send message"
}
},
+ "TWILIO_OPTIONS": {
+ "LABEL": "Select template",
+ "SEARCH_PLACEHOLDER": "Search templates",
+ "EMPTY_STATE": "No templates found",
+ "TEMPLATE_PARSER": {
+ "BACK": "Go back",
+ "SEND_MESSAGE": "Send message"
+ }
+ },
"ACTION_BUTTONS": {
"DISCARD": "Discard",
"SEND": "Send ({keyCode})"
diff --git a/app/javascript/dashboard/i18n/locale/ur_IN/contactFilters.json b/app/javascript/dashboard/i18n/locale/ur_IN/contactFilters.json
index bb3221c6e..4c62f0789 100644
--- a/app/javascript/dashboard/i18n/locale/ur_IN/contactFilters.json
+++ b/app/javascript/dashboard/i18n/locale/ur_IN/contactFilters.json
@@ -48,7 +48,8 @@
"CREATED_AT": "Created At",
"LAST_ACTIVITY": "Last Activity",
"REFERER_LINK": "Referrer link",
- "BLOCKED": "Blocked"
+ "BLOCKED": "Blocked",
+ "LABELS": "Labels"
},
"GROUPS": {
"STANDARD_FILTERS": "Standard Filters",
diff --git a/app/javascript/dashboard/i18n/locale/ur_IN/contentTemplates.json b/app/javascript/dashboard/i18n/locale/ur_IN/contentTemplates.json
new file mode 100644
index 000000000..a9b1d54c4
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/ur_IN/contentTemplates.json
@@ -0,0 +1,51 @@
+{
+ "CONTENT_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Twilio Templates",
+ "SUBTITLE": "Select the Twilio template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Search Templates",
+ "NO_TEMPLATES_FOUND": "No templates found for",
+ "NO_CONTENT": "No content",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Category",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No Twilio templates available. Click refresh to sync templates from Twilio.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Language",
+ "TEMPLATE_BODY": "Template Body",
+ "CATEGORY": "Category"
+ },
+ "TYPES": {
+ "MEDIA": "Media",
+ "QUICK_REPLY": "Quick Reply",
+ "TEXT": "Text"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variables",
+ "LANGUAGE": "Language",
+ "CATEGORY": "Category",
+ "VARIABLE_PLACEHOLDER": "Enter {variable} value",
+ "GO_BACK_LABEL": "Go Back",
+ "SEND_MESSAGE_LABEL": "Send Message",
+ "FORM_ERROR_MESSAGE": "Please fill all variables before sending",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "MEDIA_URL_LABEL": "Enter full media URL",
+ "MEDIA_URL_PLACEHOLDER": "https://example.com/image.jpg"
+ },
+ "FORM": {
+ "BACK_BUTTON": "Back",
+ "SEND_MESSAGE_BUTTON": "Send Message"
+ }
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/ur_IN/conversation.json b/app/javascript/dashboard/i18n/locale/ur_IN/conversation.json
index 308f24f51..9fd39b70f 100644
--- a/app/javascript/dashboard/i18n/locale/ur_IN/conversation.json
+++ b/app/javascript/dashboard/i18n/locale/ur_IN/conversation.json
@@ -35,6 +35,11 @@
"API_HOURS_WINDOW": "You can only reply to this conversation within {hours} hours",
"NOT_ASSIGNED_TO_YOU": "This conversation is not assigned to you. Would you like to assign this conversation to yourself?",
"ASSIGN_TO_ME": "Assign to me",
+ "BOT_HANDOFF_MESSAGE": "You are responding to a conversation which is currently handled by an assistant or a bot.",
+ "BOT_HANDOFF_ACTION": "Mark open and assign to you",
+ "BOT_HANDOFF_REOPEN_ACTION": "Mark conversation open",
+ "BOT_HANDOFF_SUCCESS": "Conversation has been handed over to you",
+ "BOT_HANDOFF_ERROR": "Failed to take over the conversation. Please try again.",
"TWILIO_WHATSAPP_CAN_REPLY": "You can only reply to this conversation using a template message due to",
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "24 hour message window restriction",
"OLD_INSTAGRAM_INBOX_REPLY_BANNER": "This Instagram account was migrated to the new Instagram channel inbox. All new messages will show up there. You won’t be able to send messages from this conversation anymore.",
@@ -66,6 +71,17 @@
"SHOW_LABELS": "Show labels",
"HIDE_LABELS": "Hide labels"
},
+ "VOICE_CALL": {
+ "INCOMING_CALL": "Incoming call",
+ "OUTGOING_CALL": "Outgoing call",
+ "CALL_IN_PROGRESS": "Call in progress",
+ "NO_ANSWER": "No answer",
+ "MISSED_CALL": "Missed call",
+ "CALL_ENDED": "Call ended",
+ "NOT_ANSWERED_YET": "Not answered yet",
+ "THEY_ANSWERED": "They answered",
+ "YOU_ANSWERED": "You answered"
+ },
"HEADER": {
"RESOLVE_ACTION": "Resolve",
"REOPEN_ACTION": "Reopen",
diff --git a/app/javascript/dashboard/i18n/locale/ur_IN/helpCenter.json b/app/javascript/dashboard/i18n/locale/ur_IN/helpCenter.json
index fd2b1a788..0ab8d62ff 100644
--- a/app/javascript/dashboard/i18n/locale/ur_IN/helpCenter.json
+++ b/app/javascript/dashboard/i18n/locale/ur_IN/helpCenter.json
@@ -741,7 +741,8 @@
"LIVE_CHAT_WIDGET": {
"LABEL": "Live chat widget",
"PLACEHOLDER": "Select live chat widget",
- "HELP_TEXT": "Select a live chat widget that will appear on your help center"
+ "HELP_TEXT": "Select a live chat widget that will appear on your help center",
+ "NONE_OPTION": "No widget"
},
"BRAND_COLOR": {
"LABEL": "Brand color"
@@ -807,6 +808,58 @@
"ERROR_MESSAGE": "Unable to update portal"
}
}
+ },
+ "PDF_UPLOAD": {
+ "TITLE": "Upload PDF Document",
+ "DESCRIPTION": "Upload a PDF document to automatically generate FAQs using AI",
+ "DRAG_DROP_TEXT": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "ADDITIONAL_CONTEXT_LABEL": "Additional Context (Optional)",
+ "ADDITIONAL_CONTEXT_PLACEHOLDER": "Provide any additional context or instructions for FAQ generation...",
+ "UPLOADING": "Uploading...",
+ "UPLOAD": "Upload & Process",
+ "CANCEL": "Cancel",
+ "ERROR_INVALID_TYPE": "Please select a valid PDF file",
+ "ERROR_FILE_TOO_LARGE": "File size must be less than 512MB",
+ "ERROR_UPLOAD_FAILED": "Failed to upload PDF. Please try again."
+ },
+ "PDF_DOCUMENTS": {
+ "TITLE": "PDF Documents",
+ "DESCRIPTION": "Manage uploaded PDF documents and generate FAQs from them",
+ "UPLOAD_PDF": "Upload PDF",
+ "UPLOAD_FIRST_PDF": "Upload your first PDF",
+ "UPLOADED_BY": "Uploaded by",
+ "GENERATE_FAQS": "Generate FAQs",
+ "GENERATING": "Generating...",
+ "CONFIRM_DELETE": "Are you sure you want to delete {filename}?",
+ "EMPTY_STATE": {
+ "TITLE": "No PDF documents yet",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQs using AI"
+ },
+ "STATUS": {
+ "UPLOADED": "Ready",
+ "PROCESSING": "Processing",
+ "PROCESSED": "Completed",
+ "FAILED": "Failed"
+ }
+ },
+ "CONTENT_GENERATION": {
+ "TITLE": "Content Generation",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQ content using AI",
+ "UPLOAD_TITLE": "Upload PDF Document",
+ "DRAG_DROP": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "UPLOADING": "Processing document...",
+ "UPLOAD_SUCCESS": "Document processed successfully!",
+ "UPLOAD_ERROR": "Failed to upload document. Please try again.",
+ "INVALID_FILE_TYPE": "Please select a valid PDF file",
+ "FILE_TOO_LARGE": "File size must be less than 512MB",
+ "GENERATED_CONTENT": "Generated FAQ Content",
+ "PUBLISH_SELECTED": "Publish Selected",
+ "PUBLISHING": "Publishing...",
+ "FROM_DOCUMENT": "From document",
+ "NO_CONTENT": "No generated content available. Upload a PDF document to get started.",
+ "LOADING": "Loading generated content..."
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/ur_IN/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/ur_IN/inboxMgmt.json
index 43dbe3e65..1006fabc6 100644
--- a/app/javascript/dashboard/i18n/locale/ur_IN/inboxMgmt.json
+++ b/app/javascript/dashboard/i18n/locale/ur_IN/inboxMgmt.json
@@ -272,8 +272,8 @@
},
"SUBMIT_BUTTON": "Create WhatsApp Channel",
"EMBEDDED_SIGNUP": {
- "TITLE": "Quick Setup with Meta",
- "DESC": "You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
+ "TITLE": "Quick setup with Meta",
+ "DESC": "Use the WhatsApp Embedded Signup flow to quickly connect new numbers. You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
"BENEFITS": {
"TITLE": "Benefits of Embedded Signup:",
"EASY_SETUP": "No manual configuration required",
@@ -281,9 +281,8 @@
"AUTO_CONFIG": "Automatic webhook and phone number configuration"
},
"LEARN_MORE": {
- "TEXT": "To learn more about integrated signup, pricing, and limitations, visit",
- "LINK_TEXT": "this link.",
- "LINK_URL": "https://developers.facebook.com/docs/whatsapp/embedded-signup/custom-flows/onboarding-business-app-users#limitations"
+ "TEXT": "To learn more about integrated signup, pricing, and limitations, visit {link}.",
+ "LINK_TEXT": "this link"
},
"SUBMIT_BUTTON": "Connect with WhatsApp Business",
"AUTH_PROCESSING": "Authenticating with Meta",
@@ -296,7 +295,9 @@
"INVALID_BUSINESS_DATA": "Invalid business data received from Facebook. Please try again.",
"SIGNUP_ERROR": "Signup error occurred",
"AUTH_NOT_COMPLETED": "Authentication not completed. Please restart the process.",
- "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured"
+ "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured",
+ "MANUAL_FALLBACK": "If your number is already connected to the WhatsApp Business Platform (API), or if you’re a tech provider onboarding your own number, please use the {link} flow",
+ "MANUAL_LINK_TEXT": "manual setup flow"
},
"API": {
"ERROR_MESSAGE": "We were not able to save the WhatsApp channel"
@@ -424,7 +425,51 @@
},
"AUTH": {
"TITLE": "Choose a channel",
- "DESC": "Chatwoot supports live-chat widgets, Facebook Messenger, Twitter profiles, WhatsApp, Emails, etc., as channels. If you want to build a custom channel, you can create it using the API channel. To get started, choose one of the channels below."
+ "DESC": "Chatwoot supports live-chat widgets, Facebook Messenger, Twitter profiles, WhatsApp, Emails, etc., as channels. If you want to build a custom channel, you can create it using the API channel. To get started, choose one of the channels below.",
+ "TITLE_NEXT": "Complete the setup",
+ "TITLE_FINISH": "Voilà!",
+ "CHANNEL": {
+ "WEBSITE": {
+ "TITLE": "Website",
+ "DESCRIPTION": "Create a live-chat widget"
+ },
+ "FACEBOOK": {
+ "TITLE": "Facebook",
+ "DESCRIPTION": "Connect your Facebook page"
+ },
+ "WHATSAPP": {
+ "TITLE": "WhatsApp",
+ "DESCRIPTION": "Support your customers on WhatsApp"
+ },
+ "EMAIL": {
+ "TITLE": "Email",
+ "DESCRIPTION": "Connect with Gmail, Outlook, or other providers"
+ },
+ "SMS": {
+ "TITLE": "SMS",
+ "DESCRIPTION": "Integrate SMS channel with Twilio or bandwidth"
+ },
+ "API": {
+ "TITLE": "API",
+ "DESCRIPTION": "Make a custom channel using our API"
+ },
+ "TELEGRAM": {
+ "TITLE": "Telegram",
+ "DESCRIPTION": "Configure Telegram channel using Bot token"
+ },
+ "LINE": {
+ "TITLE": "Line",
+ "DESCRIPTION": "Integrate your Line channel"
+ },
+ "INSTAGRAM": {
+ "TITLE": "Instagram",
+ "DESCRIPTION": "Connect your instagram account"
+ },
+ "VOICE": {
+ "TITLE": "Voice",
+ "DESCRIPTION": "Integrate with Twilio Voice"
+ }
+ }
},
"AGENTS": {
"TITLE": "Agents",
@@ -478,7 +523,10 @@
"MESSAGE": "You can now engage with your customers through your new Channel. Happy supporting",
"BUTTON_TEXT": "Take me there",
"MORE_SETTINGS": "More settings",
- "WEBSITE_SUCCESS": "You have successfully finished creating a website channel. Copy the code shown below and paste it on your website. Next time a customer use the live chat, the conversation will automatically appear on your inbox."
+ "WEBSITE_SUCCESS": "You have successfully finished creating a website channel. Copy the code shown below and paste it on your website. Next time a customer use the live chat, the conversation will automatically appear on your inbox.",
+ "WHATSAPP_QR_INSTRUCTION": "Scan the QR code above to quickly test your WhatsApp inbox",
+ "MESSENGER_QR_INSTRUCTION": "Scan the QR code above to quickly test your Facebook Messenger inbox",
+ "TELEGRAM_QR_INSTRUCTION": "Scan the QR code above to quickly test your Telegram inbox"
},
"REAUTH": "Reauthorize",
"VIEW": "View",
@@ -868,9 +916,18 @@
"SCRIPT_SETTINGS": "\n window.chatwootSettings = {options};"
},
"EMAIL_PROVIDERS": {
- "MICROSOFT": "Microsoft",
- "GOOGLE": "Google",
- "OTHER_PROVIDERS": "Other Providers"
+ "MICROSOFT": {
+ "TITLE": "Microsoft",
+ "DESCRIPTION": "Connect with Microsoft"
+ },
+ "GOOGLE": {
+ "TITLE": "Google",
+ "DESCRIPTION": "Connect with Google"
+ },
+ "OTHER_PROVIDERS": {
+ "TITLE": "Other Providers",
+ "DESCRIPTION": "Connect with Other Providers"
+ }
},
"CHANNELS": {
"MESSENGER": "Messenger",
diff --git a/app/javascript/dashboard/i18n/locale/ur_IN/integrations.json b/app/javascript/dashboard/i18n/locale/ur_IN/integrations.json
index f0c7abbd3..03898d278 100644
--- a/app/javascript/dashboard/i18n/locale/ur_IN/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/ur_IN/integrations.json
@@ -704,11 +704,28 @@
"ERROR_MESSAGE": "There was an error creating the document, please try again."
},
"FORM": {
+ "TYPE": {
+ "LABEL": "Document Type",
+ "URL": "URL",
+ "PDF": "PDF File"
+ },
"URL": {
"LABEL": "URL",
"PLACEHOLDER": "Enter the URL of the document",
"ERROR": "Please provide a valid URL for the document"
},
+ "PDF_FILE": {
+ "LABEL": "PDF File",
+ "CHOOSE_FILE": "Choose PDF file",
+ "ERROR": "Please select a PDF file",
+ "HELP_TEXT": "Maximum file size: 10MB",
+ "INVALID_TYPE": "Please select a valid PDF file",
+ "TOO_LARGE": "File size exceeds 10MB limit"
+ },
+ "NAME": {
+ "LABEL": "Document Name (Optional)",
+ "PLACEHOLDER": "Enter a name for the document"
+ },
"ASSISTANT": {
"LABEL": "Assistant",
"PLACEHOLDER": "Select the assistant",
@@ -744,6 +761,7 @@
"SELECTED": "{count} selected",
"SELECT_ALL": "Select all ({count})",
"UNSELECT_ALL": "Unselect all ({count})",
+ "SEARCH_PLACEHOLDER": "Search FAQs...",
"BULK_APPROVE_BUTTON": "Approve",
"BULK_DELETE_BUTTON": "Delete",
"BULK_APPROVE": {
diff --git a/app/javascript/dashboard/i18n/locale/ur_IN/mfa.json b/app/javascript/dashboard/i18n/locale/ur_IN/mfa.json
new file mode 100644
index 000000000..f7556fdcf
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/ur_IN/mfa.json
@@ -0,0 +1,106 @@
+{
+ "MFA_SETTINGS": {
+ "TITLE": "Two-Factor Authentication",
+ "SUBTITLE": "Secure your account with TOTP-based authentication",
+ "DESCRIPTION": "Add an extra layer of security to your account using a time-based one-time password (TOTP)",
+ "STATUS_TITLE": "Authentication Status",
+ "STATUS_DESCRIPTION": "Manage your two-factor authentication settings and backup recovery codes",
+ "ENABLED": "Enabled",
+ "DISABLED": "Disabled",
+ "STATUS_ENABLED": "Two-factor authentication is active",
+ "STATUS_ENABLED_DESC": "Your account is protected with an additional layer of security",
+ "ENABLE_BUTTON": "Enable Two-Factor Authentication",
+ "ENHANCE_SECURITY": "Enhance Your Account Security",
+ "ENHANCE_SECURITY_DESC": "Two-factor authentication adds an extra layer of security by requiring a verification code from your authenticator app in addition to your password.",
+ "SETUP": {
+ "STEP_NUMBER_1": "1",
+ "STEP_NUMBER_2": "2",
+ "STEP1_TITLE": "Scan QR Code with Your Authenticator App",
+ "STEP1_DESCRIPTION": "Use Google Authenticator, Authy, or any TOTP-compatible app",
+ "LOADING_QR": "Loading...",
+ "MANUAL_ENTRY": "Can't scan? Enter code manually",
+ "SECRET_KEY": "Secret Key",
+ "COPY": "Copy",
+ "ENTER_CODE": "Enter the 6-digit code from your authenticator app",
+ "ENTER_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify & Continue",
+ "CANCEL": "Cancel",
+ "ERROR_STARTING": "MFA not enabled. Please contact administrator.",
+ "INVALID_CODE": "Invalid verification code",
+ "SECRET_COPIED": "Secret key copied to clipboard",
+ "SUCCESS": "Two-factor authentication has been enabled successfully"
+ },
+ "BACKUP": {
+ "TITLE": "Save Your Backup Codes",
+ "DESCRIPTION": "Keep these codes safe. Each can be used once if you lose access to your authenticator",
+ "IMPORTANT": "Important:",
+ "IMPORTANT_NOTE": " Save these codes in a secure location. You won't be able to see them again.",
+ "DOWNLOAD": "Download",
+ "COPY_ALL": "Copy All",
+ "CONFIRM": "I have saved my backup codes in a secure location and understand that I won't be able to see them again",
+ "COMPLETE_SETUP": "Complete Setup",
+ "CODES_COPIED": "Backup codes copied to clipboard"
+ },
+ "MANAGEMENT": {
+ "BACKUP_CODES": "Backup Codes",
+ "BACKUP_CODES_DESC": "Generate new codes if you've lost or used your existing ones",
+ "REGENERATE": "Regenerate Backup Codes",
+ "DISABLE_MFA": "Disable 2FA",
+ "DISABLE_MFA_DESC": "Remove two-factor authentication from your account",
+ "DISABLE_BUTTON": "Disable Two-Factor Authentication"
+ },
+ "DISABLE": {
+ "TITLE": "Disable Two-Factor Authentication",
+ "DESCRIPTION": "You'll need to enter your password and a verification code to disable two-factor authentication.",
+ "PASSWORD": "Password",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Disable 2FA",
+ "CANCEL": "Cancel",
+ "SUCCESS": "Two-factor authentication has been disabled",
+ "ERROR": "Failed to disable MFA. Please check your credentials."
+ },
+ "REGENERATE": {
+ "TITLE": "Regenerate Backup Codes",
+ "DESCRIPTION": "This will invalidate your existing backup codes and generate new ones. Enter your verification code to continue.",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Generate New Codes",
+ "CANCEL": "Cancel",
+ "NEW_CODES_TITLE": "New Backup Codes Generated",
+ "NEW_CODES_DESC": "Your old backup codes have been invalidated. Save these new codes in a secure location.",
+ "CODES_IMPORTANT": "Important:",
+ "CODES_IMPORTANT_NOTE": " Each code can only be used once. Save them before closing this window.",
+ "DOWNLOAD_CODES": "Download Codes",
+ "COPY_ALL_CODES": "Copy All Codes",
+ "CODES_SAVED": "I've Saved My Codes",
+ "SUCCESS": "New backup codes have been generated",
+ "ERROR": "Failed to regenerate backup codes"
+ }
+ },
+ "MFA_VERIFICATION": {
+ "TITLE": "Two-Factor Authentication",
+ "DESCRIPTION": "Enter your verification code to continue",
+ "AUTHENTICATOR_APP": "Authenticator App",
+ "BACKUP_CODE": "Backup Code",
+ "ENTER_OTP_CODE": "Enter 6-digit code from your authenticator app",
+ "ENTER_BACKUP_CODE": "Enter one of your backup codes",
+ "BACKUP_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify",
+ "TRY_ANOTHER_METHOD": "Try another verification method",
+ "CANCEL_LOGIN": "Cancel and return to login",
+ "HELP_TEXT": "Having trouble signing in?",
+ "LEARN_MORE": "Learn more about 2FA",
+ "HELP_MODAL": {
+ "TITLE": "Two-Factor Authentication Help",
+ "AUTHENTICATOR_TITLE": "Using an Authenticator App",
+ "AUTHENTICATOR_DESC": "Open your authenticator app (Google Authenticator, Authy, etc.) and enter the 6-digit code shown for your account.",
+ "BACKUP_TITLE": "Using a Backup Code",
+ "BACKUP_DESC": "If you don't have access to your authenticator app, you can use one of the backup codes you saved when setting up 2FA. Each code can only be used once.",
+ "CONTACT_TITLE": "Need More Help?",
+ "CONTACT_DESC_CLOUD": "If you've lost access to both your authenticator app and backup codes, please reach out to Chatwoot support for assistance.",
+ "CONTACT_DESC_SELF_HOSTED": "If you've lost access to both your authenticator app and backup codes, please contact your administrator for assistance."
+ },
+ "VERIFICATION_FAILED": "Verification failed. Please try again."
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/ur_IN/settings.json b/app/javascript/dashboard/i18n/locale/ur_IN/settings.json
index 98c3f559b..52f28443b 100644
--- a/app/javascript/dashboard/i18n/locale/ur_IN/settings.json
+++ b/app/javascript/dashboard/i18n/locale/ur_IN/settings.json
@@ -51,6 +51,13 @@
"LARGER": "Larger",
"EXTRA_LARGE": "Extra Large"
}
+ },
+ "LANGUAGE": {
+ "TITLE": "Preferred Language",
+ "NOTE": "Choose the language you want to use.",
+ "UPDATE_SUCCESS": "Your Language settings have been updated successfully",
+ "UPDATE_ERROR": "There is an error while updating the language settings, please try again",
+ "USE_ACCOUNT_DEFAULT": "Use account default"
}
},
"MESSAGE_SIGNATURE_SECTION": {
@@ -73,6 +80,11 @@
"NOTE": "Updating your password would reset your logins in multiple devices.",
"BTN_TEXT": "Change password"
},
+ "SECURITY_SECTION": {
+ "TITLE": "Security",
+ "NOTE": "Manage additional security features for your account.",
+ "MFA_BUTTON": "Manage Two-Factor Authentication"
+ },
"ACCESS_TOKEN": {
"TITLE": "Access Token",
"NOTE": "This token can be used if you are building an API based integration",
@@ -330,6 +342,7 @@
"REPORTS_LABEL": "Labels",
"REPORTS_INBOX": "Inbox",
"REPORTS_TEAM": "Team",
+ "AGENT_ASSIGNMENT": "Agent Assignment",
"SET_AVAILABILITY_TITLE": "Set yourself as",
"SET_YOUR_AVAILABILITY": "Set your availability",
"SLA": "SLA",
@@ -350,7 +363,8 @@
"INFO_TEXT": "Let the system automatically mark you offline when you aren't using the app or dashboard.",
"INFO_SHORT": "Automatically mark offline when you aren't using the app."
},
- "DOCS": "Read docs"
+ "DOCS": "Read docs",
+ "SECURITY": "Security"
},
"BILLING_SETTINGS": {
"TITLE": "Billing",
@@ -382,6 +396,77 @@
},
"NO_BILLING_USER": "Your billing account is being configured. Please refresh the page and try again."
},
+ "SECURITY_SETTINGS": {
+ "TITLE": "Security",
+ "DESCRIPTION": "Manage your account security settings.",
+ "LINK_TEXT": "Learn more about SAML SSO",
+ "SAML": {
+ "TITLE": "SAML SSO",
+ "NOTE": "Configure SAML single sign-on for your account. Users will authenticate through your identity provider instead of using email/password.",
+ "ACS_URL": {
+ "LABEL": "ACS URL",
+ "TOOLTIP": "Assertion Consumer Service URL - Configure this URL in your IdP as the destination for SAML responses"
+ },
+ "SSO_URL": {
+ "LABEL": "SSO URL",
+ "HELP": "The URL where SAML authentication requests will be sent",
+ "PLACEHOLDER": "https://your-idp.com/saml/sso"
+ },
+ "CERTIFICATE": {
+ "LABEL": "Signing certificate in PEM format",
+ "HELP": "The public certificate from your identity provider used to verify SAML responses",
+ "PLACEHOLDER": "-----BEGIN CERTIFICATE-----\nMIIC..."
+ },
+ "FINGERPRINT": {
+ "LABEL": "Fingerprint",
+ "TOOLTIP": "SHA-1 fingerprint of the certificate - Use this to verify the certificate in your IdP configuration"
+ },
+ "COPY_SUCCESS": "Code copied to clipboard successfully",
+ "SP_ENTITY_ID": {
+ "LABEL": "SP Entity ID",
+ "HELP": "Unique identifier for this application as a service provider (auto-generated).",
+ "TOOLTIP": "Unique identifier for Chatwoot as the Service Provider - Configure this in your IdP settings"
+ },
+ "IDP_ENTITY_ID": {
+ "LABEL": "Identity Provider Entity ID",
+ "HELP": "Unique identifier for your identity provider (usually found in IdP configuration)",
+ "PLACEHOLDER": "https://your-idp.com/saml"
+ },
+ "UPDATE_BUTTON": "Update SAML Settings",
+ "API": {
+ "SUCCESS": "SAML settings updated successfully",
+ "ERROR": "Failed to update SAML settings",
+ "ERROR_LOADING": "Failed to load SAML settings",
+ "DISABLED": "SAML settings disabled successfully"
+ },
+ "VALIDATION": {
+ "REQUIRED_FIELDS": "SSO URL, Identity Provider Entity ID, and Certificate are required fields",
+ "SSO_URL_ERROR": "Please enter a valid SSO URL",
+ "CERTIFICATE_ERROR": "Certificate is required",
+ "IDP_ENTITY_ID_ERROR": "Identity Provider Entity ID is required"
+ },
+ "ENTERPRISE_PAYWALL": {
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade to an Enterprise plan to access SAML single sign-on and other advanced security features.",
+ "ASK_ADMIN": "Please reach out to your administrator for the upgrade."
+ },
+ "PAYWALL": {
+ "TITLE": "Upgrade to enable SAML SSO",
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade your plan to get access to SAML single sign-on and other advanced features.",
+ "UPGRADE_NOW": "Upgrade now",
+ "CANCEL_ANYTIME": "You can change or cancel your plan anytime"
+ },
+ "ATTRIBUTE_MAPPING": {
+ "TITLE": "SAML Attribute Setup",
+ "DESCRIPTION": "The following attribute mappings must be configured in your identity provider"
+ },
+ "INFO_SECTION": {
+ "TITLE": "Service Provider Information",
+ "TOOLTIP": "Copy these values and configure them in your Identity Provider to establish the SAML connection"
+ }
+ }
+ },
"CREATE_ACCOUNT": {
"NO_ACCOUNT_WARNING": "Uh oh! We could not find any Chatwoot accounts. Please create a new account to continue.",
"NEW_ACCOUNT": "New Account",
@@ -418,5 +503,255 @@
"SWITCH_TO_REPLY": "Switch to Reply",
"TOGGLE_SNOOZE_DROPDOWN": "Toggle snooze dropdown"
}
+ },
+ "ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent assignment",
+ "DESCRIPTION": "Define policies to effectively manage workload and route conversations based on the needs of inboxes and agents. Learn more here"
+ },
+ "ASSIGNMENT_POLICY": {
+ "TITLE": "Assignment policy",
+ "DESCRIPTION": "Manage how conversations get assigned in inboxes.",
+ "FEATURES": [
+ "Assign by conversations evenly or by available capacity",
+ "Add fair distribution rules to avoid overloading any agent",
+ "Add inboxes to a policy - one policy per inbox"
+ ]
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "TITLE": "Agent capacity policy",
+ "DESCRIPTION": "Manage workload for agents.",
+ "FEATURES": [
+ "Define maximum conversations per inbox",
+ "Create exceptions based on labels and time",
+ "Add agents to a policy - one policy per agent"
+ ]
+ }
+ },
+ "AGENT_ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Assignment policy",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "ORDER": "Order",
+ "PRIORITY": "Priority",
+ "ACTIVE": "Active",
+ "INACTIVE": "Inactive",
+ "POPOVER": "Added inboxes",
+ "EDIT": "Edit"
+ },
+ "NO_RECORDS_FOUND": "No assignment policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create assignment policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy created successfully",
+ "ERROR_MESSAGE": "Failed to create assignment policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit assignment policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_INBOX_DIALOG": {
+ "TITLE": "Add inbox",
+ "DESCRIPTION": "{inboxName} inbox is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Cancel"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update assignment policy"
+ },
+ "INBOX_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Inbox added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Inbox removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove inbox from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Description:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "STATUS": {
+ "LABEL": "Status:",
+ "PLACEHOLDER": "Select status",
+ "ACTIVE": "Policy is active",
+ "INACTIVE": "Policy is inactive"
+ },
+ "ASSIGNMENT_ORDER": {
+ "LABEL": "Assignment order",
+ "ROUND_ROBIN": {
+ "LABEL": "Round robin",
+ "DESCRIPTION": "Assign conversations evenly among agents."
+ },
+ "BALANCED": {
+ "LABEL": "Balanced",
+ "DESCRIPTION": "Assign conversations based on available capacity."
+ }
+ },
+ "ASSIGNMENT_PRIORITY": {
+ "LABEL": "Assignment priority",
+ "EARLIEST_CREATED": {
+ "LABEL": "Earliest created",
+ "DESCRIPTION": "The conversation that was created first gets assigned first."
+ },
+ "LONGEST_WAITING": {
+ "LABEL": "Longest waiting",
+ "DESCRIPTION": "The conversation waiting the longest gets assigned first."
+ }
+ },
+ "FAIR_DISTRIBUTION": {
+ "LABEL": "Fair distribution policy",
+ "DESCRIPTION": "Set the maximum number of conversations that can be assigned per agent within a time window to avoid overloading any one agent. This required field defaults to 100 conversations per hour.",
+ "INPUT_MAX": "Assign max",
+ "DURATION": "Conversations per agent in every"
+ },
+ "INBOXES": {
+ "LABEL": "Added inboxes",
+ "DESCRIPTION": "Add inboxes for which this policy will be applicable.",
+ "ADD_BUTTON": "Add inbox",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select inboxes to add",
+ "ADD_BUTTON": "Add"
+ },
+ "EMPTY_STATE": "No inboxes added to this policy, add an inbox to get started",
+ "API": {
+ "SUCCESS_MESSAGE": "Inbox successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Assignment policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete assignment policy"
+ }
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent capacity",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "POPOVER": "Added agents",
+ "EDIT": "Edit"
+ },
+ "NO_RECORDS_FOUND": "No agent capacity policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create agent capacity policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy created successfully",
+ "ERROR_MESSAGE": "Failed to create agent capacity policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit agent capacity policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_AGENT_DIALOG": {
+ "TITLE": "Add agent",
+ "DESCRIPTION": "{agentName} is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Cancel"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update agent capacity policy"
+ },
+ "AGENT_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Agent added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Agent removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove agent from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Description:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "INBOX_CAPACITY_LIMIT": {
+ "LABEL": "Inbox capacity limits",
+ "ADD_BUTTON": "Add inbox",
+ "FIELD": {
+ "SELECT_INBOX": "Select inbox",
+ "MAX_CONVERSATIONS": "Max conversations",
+ "SET_LIMIT": "Set limit"
+ },
+ "EMPTY_STATE": "No inbox limit set"
+ },
+ "EXCLUSION_RULES": {
+ "LABEL": "Exclusion rules",
+ "DESCRIPTION": "Conversations that satisfy the following conditions would not count towards agent capacity",
+ "TAGS": {
+ "LABEL": "Exclude conversations tagged with specific labels",
+ "ADD_TAG": "add tag",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select tags to add"
+ },
+ "EMPTY_STATE": "No tags added to this policy."
+ },
+ "DURATION": {
+ "LABEL": "Exclude conversations older than a specified duration",
+ "PLACEHOLDER": "Set time"
+ }
+ },
+ "USERS": {
+ "LABEL": "Assigned agents",
+ "DESCRIPTION": "Add agents for which this policy will be applicable.",
+ "ADD_BUTTON": "Add agent",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select agents to add",
+ "ADD_BUTTON": "Add"
+ },
+ "EMPTY_STATE": "No agents added",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Agent capacity policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete agent capacity policy"
+ }
+ },
+ "DELETE_POLICY": {
+ "TITLE": "Delete policy",
+ "DESCRIPTION": "Are you sure you want to delete this policy? This action cannot be undone.",
+ "CONFIRM_BUTTON_LABEL": "Delete",
+ "CANCEL_BUTTON_LABEL": "Cancel"
+ }
}
}
diff --git a/app/javascript/dashboard/i18n/locale/ur_IN/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/ur_IN/whatsappTemplates.json
index 5f53faaa8..cf28312dc 100644
--- a/app/javascript/dashboard/i18n/locale/ur_IN/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/ur_IN/whatsappTemplates.json
@@ -40,6 +40,7 @@
"BUTTON_LABEL": "Button {index}",
"COUPON_CODE": "Enter coupon code (max 15 chars)",
"MEDIA_URL_LABEL": "Enter {type} URL",
+ "DOCUMENT_NAME_PLACEHOLDER": "Enter document filename (e.g., Invoice_2025.pdf)",
"BUTTON_PARAMETER": "Enter button parameter"
}
}
diff --git a/app/javascript/dashboard/i18n/locale/vi/automation.json b/app/javascript/dashboard/i18n/locale/vi/automation.json
index 1f8d5ea79..765ab9ede 100644
--- a/app/javascript/dashboard/i18n/locale/vi/automation.json
+++ b/app/javascript/dashboard/i18n/locale/vi/automation.json
@@ -177,7 +177,8 @@
"REFERER_LINK": "Referrer Link",
"ASSIGNEE_NAME": "Assignee",
"TEAM_NAME": "Nhóm",
- "PRIORITY": "Mức độ ưu tiên"
+ "PRIORITY": "Mức độ ưu tiên",
+ "LABELS": "Nhãn"
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/vi/contact.json b/app/javascript/dashboard/i18n/locale/vi/contact.json
index 621f31671..f04ae7e53 100644
--- a/app/javascript/dashboard/i18n/locale/vi/contact.json
+++ b/app/javascript/dashboard/i18n/locale/vi/contact.json
@@ -610,6 +610,15 @@
"SEND_MESSAGE": "Gửi tin nhắn"
}
},
+ "TWILIO_OPTIONS": {
+ "LABEL": "Select template",
+ "SEARCH_PLACEHOLDER": "Search templates",
+ "EMPTY_STATE": "No templates found",
+ "TEMPLATE_PARSER": {
+ "BACK": "Trở về",
+ "SEND_MESSAGE": "Gửi tin nhắn"
+ }
+ },
"ACTION_BUTTONS": {
"DISCARD": "Discard",
"SEND": "Send ({keyCode})"
diff --git a/app/javascript/dashboard/i18n/locale/vi/contactFilters.json b/app/javascript/dashboard/i18n/locale/vi/contactFilters.json
index f235daffa..e4b72381b 100644
--- a/app/javascript/dashboard/i18n/locale/vi/contactFilters.json
+++ b/app/javascript/dashboard/i18n/locale/vi/contactFilters.json
@@ -48,7 +48,8 @@
"CREATED_AT": "Tạo vào lúc",
"LAST_ACTIVITY": "Hành động cuối cùng",
"REFERER_LINK": "Liên kết người gới thiệu",
- "BLOCKED": "Blocked"
+ "BLOCKED": "Blocked",
+ "LABELS": "Nhãn"
},
"GROUPS": {
"STANDARD_FILTERS": "Bộ lọc tiêu chuẩn",
diff --git a/app/javascript/dashboard/i18n/locale/vi/contentTemplates.json b/app/javascript/dashboard/i18n/locale/vi/contentTemplates.json
new file mode 100644
index 000000000..b15490394
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/vi/contentTemplates.json
@@ -0,0 +1,51 @@
+{
+ "CONTENT_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Twilio Templates",
+ "SUBTITLE": "Select the Twilio template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Tìm kiếm Mẫu",
+ "NO_TEMPLATES_FOUND": "Không tìm thấy mẫu nào cho",
+ "NO_CONTENT": "No content",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Loại",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No Twilio templates available. Click refresh to sync templates from Twilio.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Ngôn ngữ",
+ "TEMPLATE_BODY": "Nội dung của Mẫu",
+ "CATEGORY": "Loại"
+ },
+ "TYPES": {
+ "MEDIA": "Media",
+ "QUICK_REPLY": "Quick Reply",
+ "TEXT": "Văn bản"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Biến",
+ "LANGUAGE": "Ngôn ngữ",
+ "CATEGORY": "Loại",
+ "VARIABLE_PLACEHOLDER": "Nhập giá trị {variable}",
+ "GO_BACK_LABEL": "Quay lại",
+ "SEND_MESSAGE_LABEL": "Gửi tin nhắn",
+ "FORM_ERROR_MESSAGE": "Vui lòng điền vào tất cả các biến trước khi gửi",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "MEDIA_URL_LABEL": "Enter full media URL",
+ "MEDIA_URL_PLACEHOLDER": "https://example.com/image.jpg"
+ },
+ "FORM": {
+ "BACK_BUTTON": "Trờ về",
+ "SEND_MESSAGE_BUTTON": "Gửi tin nhắn"
+ }
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/vi/conversation.json b/app/javascript/dashboard/i18n/locale/vi/conversation.json
index 2803b1748..58eadd9e0 100644
--- a/app/javascript/dashboard/i18n/locale/vi/conversation.json
+++ b/app/javascript/dashboard/i18n/locale/vi/conversation.json
@@ -35,6 +35,11 @@
"API_HOURS_WINDOW": "You can only reply to this conversation within {hours} hours",
"NOT_ASSIGNED_TO_YOU": "Hội thoại này không được phân công cho bạn. Bạn có muốn phân công hội thoại này cho chính mình?",
"ASSIGN_TO_ME": "Phân công cho tôi",
+ "BOT_HANDOFF_MESSAGE": "You are responding to a conversation which is currently handled by an assistant or a bot.",
+ "BOT_HANDOFF_ACTION": "Mark open and assign to you",
+ "BOT_HANDOFF_REOPEN_ACTION": "Mark conversation open",
+ "BOT_HANDOFF_SUCCESS": "Conversation has been handed over to you",
+ "BOT_HANDOFF_ERROR": "Failed to take over the conversation. Please try again.",
"TWILIO_WHATSAPP_CAN_REPLY": "Bạn chỉ có thể phản hồi hội thoại này bằng tin nhắn mẫu vì",
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "Giới hạn thời lượng tin nhắn 24 giờ",
"OLD_INSTAGRAM_INBOX_REPLY_BANNER": "This Instagram account was migrated to the new Instagram channel inbox. All new messages will show up there. You won’t be able to send messages from this conversation anymore.",
@@ -66,6 +71,17 @@
"SHOW_LABELS": "Hiển thị nhãn",
"HIDE_LABELS": "Ẩn nhãn"
},
+ "VOICE_CALL": {
+ "INCOMING_CALL": "Incoming call",
+ "OUTGOING_CALL": "Outgoing call",
+ "CALL_IN_PROGRESS": "Call in progress",
+ "NO_ANSWER": "No answer",
+ "MISSED_CALL": "Missed call",
+ "CALL_ENDED": "Call ended",
+ "NOT_ANSWERED_YET": "Not answered yet",
+ "THEY_ANSWERED": "They answered",
+ "YOU_ANSWERED": "You answered"
+ },
"HEADER": {
"RESOLVE_ACTION": "Giải quyết",
"REOPEN_ACTION": "Mở lại",
diff --git a/app/javascript/dashboard/i18n/locale/vi/helpCenter.json b/app/javascript/dashboard/i18n/locale/vi/helpCenter.json
index c7cb438b4..c9cf12333 100644
--- a/app/javascript/dashboard/i18n/locale/vi/helpCenter.json
+++ b/app/javascript/dashboard/i18n/locale/vi/helpCenter.json
@@ -741,7 +741,8 @@
"LIVE_CHAT_WIDGET": {
"LABEL": "Live chat widget",
"PLACEHOLDER": "Select live chat widget",
- "HELP_TEXT": "Select a live chat widget that will appear on your help center"
+ "HELP_TEXT": "Select a live chat widget that will appear on your help center",
+ "NONE_OPTION": "No widget"
},
"BRAND_COLOR": {
"LABEL": "Brand color"
@@ -807,6 +808,58 @@
"ERROR_MESSAGE": "Unable to update portal"
}
}
+ },
+ "PDF_UPLOAD": {
+ "TITLE": "Upload PDF Document",
+ "DESCRIPTION": "Upload a PDF document to automatically generate FAQs using AI",
+ "DRAG_DROP_TEXT": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "ADDITIONAL_CONTEXT_LABEL": "Additional Context (Optional)",
+ "ADDITIONAL_CONTEXT_PLACEHOLDER": "Provide any additional context or instructions for FAQ generation...",
+ "UPLOADING": "Đang tải lên...",
+ "UPLOAD": "Upload & Process",
+ "CANCEL": "Huỷ",
+ "ERROR_INVALID_TYPE": "Please select a valid PDF file",
+ "ERROR_FILE_TOO_LARGE": "File size must be less than 512MB",
+ "ERROR_UPLOAD_FAILED": "Failed to upload PDF. Please try again."
+ },
+ "PDF_DOCUMENTS": {
+ "TITLE": "PDF Documents",
+ "DESCRIPTION": "Manage uploaded PDF documents and generate FAQs from them",
+ "UPLOAD_PDF": "Upload PDF",
+ "UPLOAD_FIRST_PDF": "Upload your first PDF",
+ "UPLOADED_BY": "Uploaded by",
+ "GENERATE_FAQS": "Generate FAQs",
+ "GENERATING": "Generating...",
+ "CONFIRM_DELETE": "Are you sure you want to delete {filename}?",
+ "EMPTY_STATE": {
+ "TITLE": "No PDF documents yet",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQs using AI"
+ },
+ "STATUS": {
+ "UPLOADED": "Ready",
+ "PROCESSING": "Processing",
+ "PROCESSED": "Hoàn tất",
+ "FAILED": "Failed"
+ }
+ },
+ "CONTENT_GENERATION": {
+ "TITLE": "Content Generation",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQ content using AI",
+ "UPLOAD_TITLE": "Upload PDF Document",
+ "DRAG_DROP": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "UPLOADING": "Processing document...",
+ "UPLOAD_SUCCESS": "Document processed successfully!",
+ "UPLOAD_ERROR": "Failed to upload document. Please try again.",
+ "INVALID_FILE_TYPE": "Please select a valid PDF file",
+ "FILE_TOO_LARGE": "File size must be less than 512MB",
+ "GENERATED_CONTENT": "Generated FAQ Content",
+ "PUBLISH_SELECTED": "Publish Selected",
+ "PUBLISHING": "Publishing...",
+ "FROM_DOCUMENT": "From document",
+ "NO_CONTENT": "No generated content available. Upload a PDF document to get started.",
+ "LOADING": "Loading generated content..."
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/vi/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/vi/inboxMgmt.json
index 3d660c2b6..f8012008c 100644
--- a/app/javascript/dashboard/i18n/locale/vi/inboxMgmt.json
+++ b/app/javascript/dashboard/i18n/locale/vi/inboxMgmt.json
@@ -272,8 +272,8 @@
},
"SUBMIT_BUTTON": "Tạo kênh WhatsApp",
"EMBEDDED_SIGNUP": {
- "TITLE": "Quick Setup with Meta",
- "DESC": "You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
+ "TITLE": "Quick setup with Meta",
+ "DESC": "Use the WhatsApp Embedded Signup flow to quickly connect new numbers. You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
"BENEFITS": {
"TITLE": "Benefits of Embedded Signup:",
"EASY_SETUP": "No manual configuration required",
@@ -281,9 +281,8 @@
"AUTO_CONFIG": "Automatic webhook and phone number configuration"
},
"LEARN_MORE": {
- "TEXT": "To learn more about integrated signup, pricing, and limitations, visit",
- "LINK_TEXT": "this link.",
- "LINK_URL": "https://developers.facebook.com/docs/whatsapp/embedded-signup/custom-flows/onboarding-business-app-users#limitations"
+ "TEXT": "To learn more about integrated signup, pricing, and limitations, visit {link}.",
+ "LINK_TEXT": "this link"
},
"SUBMIT_BUTTON": "Connect with WhatsApp Business",
"AUTH_PROCESSING": "Authenticating with Meta",
@@ -296,7 +295,9 @@
"INVALID_BUSINESS_DATA": "Invalid business data received from Facebook. Please try again.",
"SIGNUP_ERROR": "Signup error occurred",
"AUTH_NOT_COMPLETED": "Authentication not completed. Please restart the process.",
- "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured"
+ "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured",
+ "MANUAL_FALLBACK": "If your number is already connected to the WhatsApp Business Platform (API), or if you’re a tech provider onboarding your own number, please use the {link} flow",
+ "MANUAL_LINK_TEXT": "manual setup flow"
},
"API": {
"ERROR_MESSAGE": "Chúng tôi không thể lưu kênh WhatsApp"
@@ -424,7 +425,51 @@
},
"AUTH": {
"TITLE": "Chọn kênh",
- "DESC": "Chatwoot hỗ trợ các tiện ích trò chuyện trực tiếp, Facebook Messenger, hồ sơ Twitter, WhatsApp, Email, v. v., dưới dạng các kênh. Nếu bạn muốn xây dựng một kênh tùy chỉnh, bạn có thể tạo kênh này bằng cách sử dụng kênh API. Để bắt đầu, hãy chọn một trong các kênh bên dưới."
+ "DESC": "Chatwoot hỗ trợ các tiện ích trò chuyện trực tiếp, Facebook Messenger, hồ sơ Twitter, WhatsApp, Email, v. v., dưới dạng các kênh. Nếu bạn muốn xây dựng một kênh tùy chỉnh, bạn có thể tạo kênh này bằng cách sử dụng kênh API. Để bắt đầu, hãy chọn một trong các kênh bên dưới.",
+ "TITLE_NEXT": "Complete the setup",
+ "TITLE_FINISH": "Voilà!",
+ "CHANNEL": {
+ "WEBSITE": {
+ "TITLE": "Website",
+ "DESCRIPTION": "Create a live-chat widget"
+ },
+ "FACEBOOK": {
+ "TITLE": "Facebook",
+ "DESCRIPTION": "Connect your Facebook page"
+ },
+ "WHATSAPP": {
+ "TITLE": "WhatsApp",
+ "DESCRIPTION": "Support your customers on WhatsApp"
+ },
+ "EMAIL": {
+ "TITLE": "Email",
+ "DESCRIPTION": "Connect with Gmail, Outlook, or other providers"
+ },
+ "SMS": {
+ "TITLE": "SMS",
+ "DESCRIPTION": "Integrate SMS channel with Twilio or bandwidth"
+ },
+ "API": {
+ "TITLE": "API",
+ "DESCRIPTION": "Make a custom channel using our API"
+ },
+ "TELEGRAM": {
+ "TITLE": "Telegram",
+ "DESCRIPTION": "Configure Telegram channel using Bot token"
+ },
+ "LINE": {
+ "TITLE": "Line",
+ "DESCRIPTION": "Integrate your Line channel"
+ },
+ "INSTAGRAM": {
+ "TITLE": "Instagram",
+ "DESCRIPTION": "Connect your instagram account"
+ },
+ "VOICE": {
+ "TITLE": "Voice",
+ "DESCRIPTION": "Integrate with Twilio Voice"
+ }
+ }
},
"AGENTS": {
"TITLE": "Nhà Cung Cấp",
@@ -478,7 +523,10 @@
"MESSAGE": "Giờ đây, bạn có thể tương tác với khách hàng thông qua Kênh mới của mình. Chúc vui vẻ ủng hộ",
"BUTTON_TEXT": "Đưa cho tôi",
"MORE_SETTINGS": "Nhiều tuỳ chọn hơn",
- "WEBSITE_SUCCESS": "Bạn đã hoàn thành việc tạo kênh trang web thành công. Sao chép mã được hiển thị bên dưới và dán vào trang web của bạn. Lần tới khi khách hàng sử dụng cuộc trò chuyện trực tiếp, cuộc trò chuyện sẽ tự động xuất hiện trong hộp thư đến của bạn."
+ "WEBSITE_SUCCESS": "Bạn đã hoàn thành việc tạo kênh trang web thành công. Sao chép mã được hiển thị bên dưới và dán vào trang web của bạn. Lần tới khi khách hàng sử dụng cuộc trò chuyện trực tiếp, cuộc trò chuyện sẽ tự động xuất hiện trong hộp thư đến của bạn.",
+ "WHATSAPP_QR_INSTRUCTION": "Scan the QR code above to quickly test your WhatsApp inbox",
+ "MESSENGER_QR_INSTRUCTION": "Scan the QR code above to quickly test your Facebook Messenger inbox",
+ "TELEGRAM_QR_INSTRUCTION": "Scan the QR code above to quickly test your Telegram inbox"
},
"REAUTH": "Ủy quyền lại",
"VIEW": "Xem",
@@ -868,9 +916,18 @@
"SCRIPT_SETTINGS": "\nwindow.chatwootSettings = {options};"
},
"EMAIL_PROVIDERS": {
- "MICROSOFT": "Microsoft",
- "GOOGLE": "Google",
- "OTHER_PROVIDERS": "Other Providers"
+ "MICROSOFT": {
+ "TITLE": "Microsoft",
+ "DESCRIPTION": "Connect with Microsoft"
+ },
+ "GOOGLE": {
+ "TITLE": "Google",
+ "DESCRIPTION": "Connect with Google"
+ },
+ "OTHER_PROVIDERS": {
+ "TITLE": "Other Providers",
+ "DESCRIPTION": "Connect with Other Providers"
+ }
},
"CHANNELS": {
"MESSENGER": "Messenger",
diff --git a/app/javascript/dashboard/i18n/locale/vi/integrations.json b/app/javascript/dashboard/i18n/locale/vi/integrations.json
index 10a6df6b9..4968ebfc6 100644
--- a/app/javascript/dashboard/i18n/locale/vi/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/vi/integrations.json
@@ -704,11 +704,28 @@
"ERROR_MESSAGE": "There was an error creating the document, please try again."
},
"FORM": {
+ "TYPE": {
+ "LABEL": "Document Type",
+ "URL": "URL",
+ "PDF": "PDF File"
+ },
"URL": {
"LABEL": "URL",
"PLACEHOLDER": "Enter the URL of the document",
"ERROR": "Please provide a valid URL for the document"
},
+ "PDF_FILE": {
+ "LABEL": "PDF File",
+ "CHOOSE_FILE": "Choose PDF file",
+ "ERROR": "Please select a PDF file",
+ "HELP_TEXT": "Maximum file size: 10MB",
+ "INVALID_TYPE": "Please select a valid PDF file",
+ "TOO_LARGE": "File size exceeds 10MB limit"
+ },
+ "NAME": {
+ "LABEL": "Document Name (Optional)",
+ "PLACEHOLDER": "Enter a name for the document"
+ },
"ASSISTANT": {
"LABEL": "Assistant",
"PLACEHOLDER": "Select the assistant",
@@ -744,6 +761,7 @@
"SELECTED": "{count} selected",
"SELECT_ALL": "Select all ({count})",
"UNSELECT_ALL": "Unselect all ({count})",
+ "SEARCH_PLACEHOLDER": "Search FAQs...",
"BULK_APPROVE_BUTTON": "Approve",
"BULK_DELETE_BUTTON": "Xoá",
"BULK_APPROVE": {
diff --git a/app/javascript/dashboard/i18n/locale/vi/mfa.json b/app/javascript/dashboard/i18n/locale/vi/mfa.json
new file mode 100644
index 000000000..31e4b8c52
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/vi/mfa.json
@@ -0,0 +1,106 @@
+{
+ "MFA_SETTINGS": {
+ "TITLE": "Two-Factor Authentication",
+ "SUBTITLE": "Secure your account with TOTP-based authentication",
+ "DESCRIPTION": "Add an extra layer of security to your account using a time-based one-time password (TOTP)",
+ "STATUS_TITLE": "Authentication Status",
+ "STATUS_DESCRIPTION": "Manage your two-factor authentication settings and backup recovery codes",
+ "ENABLED": "Bật",
+ "DISABLED": "Không bật",
+ "STATUS_ENABLED": "Two-factor authentication is active",
+ "STATUS_ENABLED_DESC": "Your account is protected with an additional layer of security",
+ "ENABLE_BUTTON": "Enable Two-Factor Authentication",
+ "ENHANCE_SECURITY": "Enhance Your Account Security",
+ "ENHANCE_SECURITY_DESC": "Two-factor authentication adds an extra layer of security by requiring a verification code from your authenticator app in addition to your password.",
+ "SETUP": {
+ "STEP_NUMBER_1": "1",
+ "STEP_NUMBER_2": "2",
+ "STEP1_TITLE": "Scan QR Code with Your Authenticator App",
+ "STEP1_DESCRIPTION": "Use Google Authenticator, Authy, or any TOTP-compatible app",
+ "LOADING_QR": "Loading...",
+ "MANUAL_ENTRY": "Can't scan? Enter code manually",
+ "SECRET_KEY": "Secret Key",
+ "COPY": "Sao Chép",
+ "ENTER_CODE": "Enter the 6-digit code from your authenticator app",
+ "ENTER_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify & Continue",
+ "CANCEL": "Huỷ",
+ "ERROR_STARTING": "MFA not enabled. Please contact administrator.",
+ "INVALID_CODE": "Invalid verification code",
+ "SECRET_COPIED": "Secret key copied to clipboard",
+ "SUCCESS": "Two-factor authentication has been enabled successfully"
+ },
+ "BACKUP": {
+ "TITLE": "Save Your Backup Codes",
+ "DESCRIPTION": "Keep these codes safe. Each can be used once if you lose access to your authenticator",
+ "IMPORTANT": "Important:",
+ "IMPORTANT_NOTE": " Save these codes in a secure location. You won't be able to see them again.",
+ "DOWNLOAD": "Tải xuống",
+ "COPY_ALL": "Copy All",
+ "CONFIRM": "I have saved my backup codes in a secure location and understand that I won't be able to see them again",
+ "COMPLETE_SETUP": "Complete Setup",
+ "CODES_COPIED": "Backup codes copied to clipboard"
+ },
+ "MANAGEMENT": {
+ "BACKUP_CODES": "Backup Codes",
+ "BACKUP_CODES_DESC": "Generate new codes if you've lost or used your existing ones",
+ "REGENERATE": "Regenerate Backup Codes",
+ "DISABLE_MFA": "Disable 2FA",
+ "DISABLE_MFA_DESC": "Remove two-factor authentication from your account",
+ "DISABLE_BUTTON": "Disable Two-Factor Authentication"
+ },
+ "DISABLE": {
+ "TITLE": "Disable Two-Factor Authentication",
+ "DESCRIPTION": "You'll need to enter your password and a verification code to disable two-factor authentication.",
+ "PASSWORD": "Mật khẩu",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Disable 2FA",
+ "CANCEL": "Huỷ",
+ "SUCCESS": "Two-factor authentication has been disabled",
+ "ERROR": "Failed to disable MFA. Please check your credentials."
+ },
+ "REGENERATE": {
+ "TITLE": "Regenerate Backup Codes",
+ "DESCRIPTION": "This will invalidate your existing backup codes and generate new ones. Enter your verification code to continue.",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Generate New Codes",
+ "CANCEL": "Huỷ",
+ "NEW_CODES_TITLE": "New Backup Codes Generated",
+ "NEW_CODES_DESC": "Your old backup codes have been invalidated. Save these new codes in a secure location.",
+ "CODES_IMPORTANT": "Important:",
+ "CODES_IMPORTANT_NOTE": " Each code can only be used once. Save them before closing this window.",
+ "DOWNLOAD_CODES": "Download Codes",
+ "COPY_ALL_CODES": "Copy All Codes",
+ "CODES_SAVED": "I've Saved My Codes",
+ "SUCCESS": "New backup codes have been generated",
+ "ERROR": "Failed to regenerate backup codes"
+ }
+ },
+ "MFA_VERIFICATION": {
+ "TITLE": "Two-Factor Authentication",
+ "DESCRIPTION": "Enter your verification code to continue",
+ "AUTHENTICATOR_APP": "Authenticator App",
+ "BACKUP_CODE": "Backup Code",
+ "ENTER_OTP_CODE": "Enter 6-digit code from your authenticator app",
+ "ENTER_BACKUP_CODE": "Enter one of your backup codes",
+ "BACKUP_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify",
+ "TRY_ANOTHER_METHOD": "Try another verification method",
+ "CANCEL_LOGIN": "Cancel and return to login",
+ "HELP_TEXT": "Having trouble signing in?",
+ "LEARN_MORE": "Learn more about 2FA",
+ "HELP_MODAL": {
+ "TITLE": "Two-Factor Authentication Help",
+ "AUTHENTICATOR_TITLE": "Using an Authenticator App",
+ "AUTHENTICATOR_DESC": "Open your authenticator app (Google Authenticator, Authy, etc.) and enter the 6-digit code shown for your account.",
+ "BACKUP_TITLE": "Using a Backup Code",
+ "BACKUP_DESC": "If you don't have access to your authenticator app, you can use one of the backup codes you saved when setting up 2FA. Each code can only be used once.",
+ "CONTACT_TITLE": "Need More Help?",
+ "CONTACT_DESC_CLOUD": "If you've lost access to both your authenticator app and backup codes, please reach out to Chatwoot support for assistance.",
+ "CONTACT_DESC_SELF_HOSTED": "If you've lost access to both your authenticator app and backup codes, please contact your administrator for assistance."
+ },
+ "VERIFICATION_FAILED": "Verification failed. Please try again."
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/vi/settings.json b/app/javascript/dashboard/i18n/locale/vi/settings.json
index 6a3f631c7..6196b02bf 100644
--- a/app/javascript/dashboard/i18n/locale/vi/settings.json
+++ b/app/javascript/dashboard/i18n/locale/vi/settings.json
@@ -51,6 +51,13 @@
"LARGER": "Larger",
"EXTRA_LARGE": "Extra Large"
}
+ },
+ "LANGUAGE": {
+ "TITLE": "Preferred Language",
+ "NOTE": "Choose the language you want to use.",
+ "UPDATE_SUCCESS": "Your Language settings have been updated successfully",
+ "UPDATE_ERROR": "There is an error while updating the language settings, please try again",
+ "USE_ACCOUNT_DEFAULT": "Use account default"
}
},
"MESSAGE_SIGNATURE_SECTION": {
@@ -73,6 +80,11 @@
"NOTE": "Cập nhật mật khẩu của bạn sẽ đặt lại thông tin đăng nhập của bạn trên nhiều thiết bị.",
"BTN_TEXT": "Thay đổi mật khẩu"
},
+ "SECURITY_SECTION": {
+ "TITLE": "Security",
+ "NOTE": "Manage additional security features for your account.",
+ "MFA_BUTTON": "Manage Two-Factor Authentication"
+ },
"ACCESS_TOKEN": {
"TITLE": "Token truy cập",
"NOTE": "Có thể sử dụng Token này nếu bạn đang xây dựng tích hợp dựa trên API",
@@ -330,6 +342,7 @@
"REPORTS_LABEL": "Nhãn",
"REPORTS_INBOX": "Kênh",
"REPORTS_TEAM": "Nhóm",
+ "AGENT_ASSIGNMENT": "Agent Assignment",
"SET_AVAILABILITY_TITLE": "Đặt chính mình như",
"SET_YOUR_AVAILABILITY": "Đặt tính khả dụng của bạn",
"SLA": "SLA",
@@ -350,7 +363,8 @@
"INFO_TEXT": "Hãy để hệ thống tự động đánh dấu bạn ngoại tuyến khi bạn không sử dụng ứng dụng hoặc trang tổng quan.",
"INFO_SHORT": "Automatically mark offline when you aren't using the app."
},
- "DOCS": "Đọc tài liệu"
+ "DOCS": "Đọc tài liệu",
+ "SECURITY": "Security"
},
"BILLING_SETTINGS": {
"TITLE": "Phí",
@@ -382,6 +396,77 @@
},
"NO_BILLING_USER": "Tài khoản thanh toán của bạn đang được định cấu hình. Hãy làm mới trang và thử lại."
},
+ "SECURITY_SETTINGS": {
+ "TITLE": "Security",
+ "DESCRIPTION": "Manage your account security settings.",
+ "LINK_TEXT": "Learn more about SAML SSO",
+ "SAML": {
+ "TITLE": "SAML SSO",
+ "NOTE": "Configure SAML single sign-on for your account. Users will authenticate through your identity provider instead of using email/password.",
+ "ACS_URL": {
+ "LABEL": "ACS URL",
+ "TOOLTIP": "Assertion Consumer Service URL - Configure this URL in your IdP as the destination for SAML responses"
+ },
+ "SSO_URL": {
+ "LABEL": "SSO URL",
+ "HELP": "The URL where SAML authentication requests will be sent",
+ "PLACEHOLDER": "https://your-idp.com/saml/sso"
+ },
+ "CERTIFICATE": {
+ "LABEL": "Signing certificate in PEM format",
+ "HELP": "The public certificate from your identity provider used to verify SAML responses",
+ "PLACEHOLDER": "-----BEGIN CERTIFICATE-----\nMIIC..."
+ },
+ "FINGERPRINT": {
+ "LABEL": "Fingerprint",
+ "TOOLTIP": "SHA-1 fingerprint of the certificate - Use this to verify the certificate in your IdP configuration"
+ },
+ "COPY_SUCCESS": "Copied to clipboard",
+ "SP_ENTITY_ID": {
+ "LABEL": "SP Entity ID",
+ "HELP": "Unique identifier for this application as a service provider (auto-generated).",
+ "TOOLTIP": "Unique identifier for Chatwoot as the Service Provider - Configure this in your IdP settings"
+ },
+ "IDP_ENTITY_ID": {
+ "LABEL": "Identity Provider Entity ID",
+ "HELP": "Unique identifier for your identity provider (usually found in IdP configuration)",
+ "PLACEHOLDER": "https://your-idp.com/saml"
+ },
+ "UPDATE_BUTTON": "Update SAML Settings",
+ "API": {
+ "SUCCESS": "SAML settings updated successfully",
+ "ERROR": "Failed to update SAML settings",
+ "ERROR_LOADING": "Failed to load SAML settings",
+ "DISABLED": "SAML settings disabled successfully"
+ },
+ "VALIDATION": {
+ "REQUIRED_FIELDS": "SSO URL, Identity Provider Entity ID, and Certificate are required fields",
+ "SSO_URL_ERROR": "Please enter a valid SSO URL",
+ "CERTIFICATE_ERROR": "Certificate is required",
+ "IDP_ENTITY_ID_ERROR": "Identity Provider Entity ID is required"
+ },
+ "ENTERPRISE_PAYWALL": {
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade to an Enterprise plan to access SAML single sign-on and other advanced security features.",
+ "ASK_ADMIN": "Please reach out to your administrator for the upgrade."
+ },
+ "PAYWALL": {
+ "TITLE": "Upgrade to enable SAML SSO",
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade your plan to get access to SAML single sign-on and other advanced features.",
+ "UPGRADE_NOW": "Upgrade now",
+ "CANCEL_ANYTIME": "You can change or cancel your plan anytime"
+ },
+ "ATTRIBUTE_MAPPING": {
+ "TITLE": "SAML Attribute Setup",
+ "DESCRIPTION": "The following attribute mappings must be configured in your identity provider"
+ },
+ "INFO_SECTION": {
+ "TITLE": "Service Provider Information",
+ "TOOLTIP": "Copy these values and configure them in your Identity Provider to establish the SAML connection"
+ }
+ }
+ },
"CREATE_ACCOUNT": {
"NO_ACCOUNT_WARNING": "Ồ ồ! Chúng tôi không thể tìm thấy bất kỳ tài khoản Chatwoot nào. Vui lòng tạo một tài khoản mới để tiếp tục.",
"NEW_ACCOUNT": "Tạo mới tài khoản",
@@ -418,5 +503,255 @@
"SWITCH_TO_REPLY": "Chuyển sang phản hồi",
"TOGGLE_SNOOZE_DROPDOWN": "Chuyển đổi thả xuống tạm dừng"
}
+ },
+ "ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent assignment",
+ "DESCRIPTION": "Define policies to effectively manage workload and route conversations based on the needs of inboxes and agents. Learn more here"
+ },
+ "ASSIGNMENT_POLICY": {
+ "TITLE": "Assignment policy",
+ "DESCRIPTION": "Manage how conversations get assigned in inboxes.",
+ "FEATURES": [
+ "Assign by conversations evenly or by available capacity",
+ "Add fair distribution rules to avoid overloading any agent",
+ "Add inboxes to a policy - one policy per inbox"
+ ]
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "TITLE": "Agent capacity policy",
+ "DESCRIPTION": "Manage workload for agents.",
+ "FEATURES": [
+ "Define maximum conversations per inbox",
+ "Create exceptions based on labels and time",
+ "Add agents to a policy - one policy per agent"
+ ]
+ }
+ },
+ "AGENT_ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Assignment policy",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "ORDER": "Order",
+ "PRIORITY": "Mức độ ưu tiên",
+ "ACTIVE": "Có hiệu lực",
+ "INACTIVE": "Inactive",
+ "POPOVER": "Added inboxes",
+ "EDIT": "Chỉnh sửa"
+ },
+ "NO_RECORDS_FOUND": "No assignment policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create assignment policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy created successfully",
+ "ERROR_MESSAGE": "Failed to create assignment policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit assignment policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_INBOX_DIALOG": {
+ "TITLE": "Add inbox",
+ "DESCRIPTION": "{inboxName} inbox is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Huỷ"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update assignment policy"
+ },
+ "INBOX_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Inbox added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Inbox removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove inbox from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Mô tả:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "STATUS": {
+ "LABEL": "Trạng thái:",
+ "PLACEHOLDER": "Select status",
+ "ACTIVE": "Policy is active",
+ "INACTIVE": "Policy is inactive"
+ },
+ "ASSIGNMENT_ORDER": {
+ "LABEL": "Assignment order",
+ "ROUND_ROBIN": {
+ "LABEL": "Round robin",
+ "DESCRIPTION": "Assign conversations evenly among agents."
+ },
+ "BALANCED": {
+ "LABEL": "Balanced",
+ "DESCRIPTION": "Assign conversations based on available capacity."
+ }
+ },
+ "ASSIGNMENT_PRIORITY": {
+ "LABEL": "Assignment priority",
+ "EARLIEST_CREATED": {
+ "LABEL": "Earliest created",
+ "DESCRIPTION": "The conversation that was created first gets assigned first."
+ },
+ "LONGEST_WAITING": {
+ "LABEL": "Longest waiting",
+ "DESCRIPTION": "The conversation waiting the longest gets assigned first."
+ }
+ },
+ "FAIR_DISTRIBUTION": {
+ "LABEL": "Fair distribution policy",
+ "DESCRIPTION": "Set the maximum number of conversations that can be assigned per agent within a time window to avoid overloading any one agent. This required field defaults to 100 conversations per hour.",
+ "INPUT_MAX": "Assign max",
+ "DURATION": "Conversations per agent in every"
+ },
+ "INBOXES": {
+ "LABEL": "Added inboxes",
+ "DESCRIPTION": "Add inboxes for which this policy will be applicable.",
+ "ADD_BUTTON": "Add inbox",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select inboxes to add",
+ "ADD_BUTTON": "Thêm"
+ },
+ "EMPTY_STATE": "No inboxes added to this policy, add an inbox to get started",
+ "API": {
+ "SUCCESS_MESSAGE": "Inbox successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Assignment policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete assignment policy"
+ }
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent capacity",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "POPOVER": "Added agents",
+ "EDIT": "Chỉnh sửa"
+ },
+ "NO_RECORDS_FOUND": "No agent capacity policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create agent capacity policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy created successfully",
+ "ERROR_MESSAGE": "Failed to create agent capacity policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit agent capacity policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_AGENT_DIALOG": {
+ "TITLE": "Add agent",
+ "DESCRIPTION": "{agentName} is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "Huỷ"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update agent capacity policy"
+ },
+ "AGENT_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Agent added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Agent removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove agent from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "Mô tả:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "INBOX_CAPACITY_LIMIT": {
+ "LABEL": "Inbox capacity limits",
+ "ADD_BUTTON": "Add inbox",
+ "FIELD": {
+ "SELECT_INBOX": "Select inbox",
+ "MAX_CONVERSATIONS": "Max conversations",
+ "SET_LIMIT": "Set limit"
+ },
+ "EMPTY_STATE": "No inbox limit set"
+ },
+ "EXCLUSION_RULES": {
+ "LABEL": "Exclusion rules",
+ "DESCRIPTION": "Conversations that satisfy the following conditions would not count towards agent capacity",
+ "TAGS": {
+ "LABEL": "Exclude conversations tagged with specific labels",
+ "ADD_TAG": "add tag",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select tags to add"
+ },
+ "EMPTY_STATE": "No tags added to this policy."
+ },
+ "DURATION": {
+ "LABEL": "Exclude conversations older than a specified duration",
+ "PLACEHOLDER": "Set time"
+ }
+ },
+ "USERS": {
+ "LABEL": "Assigned agents",
+ "DESCRIPTION": "Add agents for which this policy will be applicable.",
+ "ADD_BUTTON": "Add agent",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select agents to add",
+ "ADD_BUTTON": "Thêm"
+ },
+ "EMPTY_STATE": "No agents added",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Agent capacity policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete agent capacity policy"
+ }
+ },
+ "DELETE_POLICY": {
+ "TITLE": "Delete policy",
+ "DESCRIPTION": "Are you sure you want to delete this policy? This action cannot be undone.",
+ "CONFIRM_BUTTON_LABEL": "Xoá",
+ "CANCEL_BUTTON_LABEL": "Huỷ"
+ }
}
}
diff --git a/app/javascript/dashboard/i18n/locale/vi/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/vi/whatsappTemplates.json
index fefd680ec..c5753a6fa 100644
--- a/app/javascript/dashboard/i18n/locale/vi/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/vi/whatsappTemplates.json
@@ -40,6 +40,7 @@
"BUTTON_LABEL": "Button {index}",
"COUPON_CODE": "Enter coupon code (max 15 chars)",
"MEDIA_URL_LABEL": "Enter {type} URL",
+ "DOCUMENT_NAME_PLACEHOLDER": "Enter document filename (e.g., Invoice_2025.pdf)",
"BUTTON_PARAMETER": "Enter button parameter"
}
}
diff --git a/app/javascript/dashboard/i18n/locale/zh_CN/automation.json b/app/javascript/dashboard/i18n/locale/zh_CN/automation.json
index 206b50103..182e598a0 100644
--- a/app/javascript/dashboard/i18n/locale/zh_CN/automation.json
+++ b/app/javascript/dashboard/i18n/locale/zh_CN/automation.json
@@ -177,7 +177,8 @@
"REFERER_LINK": "引荐链接",
"ASSIGNEE_NAME": "负责人",
"TEAM_NAME": "团队",
- "PRIORITY": "优先级"
+ "PRIORITY": "优先级",
+ "LABELS": "标签"
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/zh_CN/components.json b/app/javascript/dashboard/i18n/locale/zh_CN/components.json
index dbd4630d6..ba1a35d76 100644
--- a/app/javascript/dashboard/i18n/locale/zh_CN/components.json
+++ b/app/javascript/dashboard/i18n/locale/zh_CN/components.json
@@ -51,6 +51,6 @@
"PLACEHOLDER": "输入耗时"
},
"CHANNEL_SELECTOR": {
- "COMING_SOON": "Coming Soon!"
+ "COMING_SOON": "即将到来!"
}
}
diff --git a/app/javascript/dashboard/i18n/locale/zh_CN/contact.json b/app/javascript/dashboard/i18n/locale/zh_CN/contact.json
index 70834c650..043947747 100644
--- a/app/javascript/dashboard/i18n/locale/zh_CN/contact.json
+++ b/app/javascript/dashboard/i18n/locale/zh_CN/contact.json
@@ -18,9 +18,9 @@
"CREATED_AT_LABEL": "创建时间",
"NEW_MESSAGE": "新消息",
"CALL": "呼叫",
- "CALL_UNDER_DEVELOPMENT": "Calling is under development",
+ "CALL_UNDER_DEVELOPMENT": "呼叫功能正在开发中",
"VOICE_INBOX_PICKER": {
- "TITLE": "Choose a voice inbox"
+ "TITLE": "选择一个语音收件箱"
},
"CONVERSATIONS": {
"NO_RECORDS_FOUND": "此联系人没有关联到以前的会话。",
@@ -610,6 +610,15 @@
"SEND_MESSAGE": "发送消息"
}
},
+ "TWILIO_OPTIONS": {
+ "LABEL": "选择模板",
+ "SEARCH_PLACEHOLDER": "搜索模板",
+ "EMPTY_STATE": "未找到模板",
+ "TEMPLATE_PARSER": {
+ "BACK": "返回",
+ "SEND_MESSAGE": "发送消息"
+ }
+ },
"ACTION_BUTTONS": {
"DISCARD": "丢弃",
"SEND": "发送 ({keyCode})"
diff --git a/app/javascript/dashboard/i18n/locale/zh_CN/contactFilters.json b/app/javascript/dashboard/i18n/locale/zh_CN/contactFilters.json
index 239546d58..10037155b 100644
--- a/app/javascript/dashboard/i18n/locale/zh_CN/contactFilters.json
+++ b/app/javascript/dashboard/i18n/locale/zh_CN/contactFilters.json
@@ -48,7 +48,8 @@
"CREATED_AT": "创建于",
"LAST_ACTIVITY": "最后一次活动",
"REFERER_LINK": "引用链接",
- "BLOCKED": "已阻止"
+ "BLOCKED": "已阻止",
+ "LABELS": "标签"
},
"GROUPS": {
"STANDARD_FILTERS": "标准过滤器",
diff --git a/app/javascript/dashboard/i18n/locale/zh_CN/contentTemplates.json b/app/javascript/dashboard/i18n/locale/zh_CN/contentTemplates.json
new file mode 100644
index 000000000..92c9d6a49
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/zh_CN/contentTemplates.json
@@ -0,0 +1,51 @@
+{
+ "CONTENT_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Twilio 模板",
+ "SUBTITLE": "选择您想要发送的 Twilio 模板",
+ "TEMPLATE_SELECTED_SUBTITLE": "配置模板: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "查找模板",
+ "NO_TEMPLATES_FOUND": "没有找到对应的模版",
+ "NO_CONTENT": "无内容",
+ "HEADER": "页头",
+ "BODY": "正文内容",
+ "FOOTER": "页脚",
+ "BUTTONS": "按钮",
+ "CATEGORY": "类别",
+ "MEDIA_CONTENT": "媒体内容",
+ "MEDIA_CONTENT_FALLBACK": "媒体内容",
+ "NO_TEMPLATES_AVAILABLE": "没有可用的 Twilio 模板。单击刷新以同步Twilio 的模板。",
+ "REFRESH_BUTTON": "刷新模板",
+ "REFRESH_SUCCESS": "模板刷新已启动。更新可能需要几分钟时间。",
+ "REFRESH_ERROR": "刷新模板失败。请重试。",
+ "LABELS": {
+ "LANGUAGE": "语言",
+ "TEMPLATE_BODY": "模板内容",
+ "CATEGORY": "类别"
+ },
+ "TYPES": {
+ "MEDIA": "媒体",
+ "QUICK_REPLY": "快速回复",
+ "TEXT": "文本"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "参数",
+ "LANGUAGE": "语言",
+ "CATEGORY": "类别",
+ "VARIABLE_PLACEHOLDER": "请填写 {variable}",
+ "GO_BACK_LABEL": "返回",
+ "SEND_MESSAGE_LABEL": "发送消息",
+ "FORM_ERROR_MESSAGE": "你必须填写所有参数才能发送",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "MEDIA_URL_LABEL": "输入完整媒体 URL",
+ "MEDIA_URL_PLACEHOLDER": "https://example.com/image.jpg"
+ },
+ "FORM": {
+ "BACK_BUTTON": "后退",
+ "SEND_MESSAGE_BUTTON": "发送消息"
+ }
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/zh_CN/conversation.json b/app/javascript/dashboard/i18n/locale/zh_CN/conversation.json
index c2bdd2aaa..5eaff3f80 100644
--- a/app/javascript/dashboard/i18n/locale/zh_CN/conversation.json
+++ b/app/javascript/dashboard/i18n/locale/zh_CN/conversation.json
@@ -35,6 +35,11 @@
"API_HOURS_WINDOW": "您只能在 {hours} 小时内回复此对话",
"NOT_ASSIGNED_TO_YOU": "此对话未分配给您。您想要将此对话分配给自己吗?",
"ASSIGN_TO_ME": "分配给我",
+ "BOT_HANDOFF_MESSAGE": "您正在回复目前由助手或机器人处理的对话。",
+ "BOT_HANDOFF_ACTION": "标记打开并分配给自己",
+ "BOT_HANDOFF_REOPEN_ACTION": "标记对话已打开",
+ "BOT_HANDOFF_SUCCESS": "对话已分配给您",
+ "BOT_HANDOFF_ERROR": "接管对话失败,请再试一次。",
"TWILIO_WHATSAPP_CAN_REPLY": "您只能使用模板信息回复此会话,原因是",
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "24 小时消息窗口限制",
"OLD_INSTAGRAM_INBOX_REPLY_BANNER": "此 Instagram 帐户已迁移到新的 Instagram 通道收件箱。 所有新消息都将在这里显示。您将无法从这个对话中发送消息。",
@@ -66,6 +71,17 @@
"SHOW_LABELS": "显示标签",
"HIDE_LABELS": "隐藏标签"
},
+ "VOICE_CALL": {
+ "INCOMING_CALL": "来电",
+ "OUTGOING_CALL": "已拨出电话",
+ "CALL_IN_PROGRESS": "呼叫进行中",
+ "NO_ANSWER": "无应答",
+ "MISSED_CALL": "未接来电",
+ "CALL_ENDED": "通话结束",
+ "NOT_ANSWERED_YET": "尚未回复",
+ "THEY_ANSWERED": "对方已回复",
+ "YOU_ANSWERED": "你已回复"
+ },
"HEADER": {
"RESOLVE_ACTION": "已解决",
"REOPEN_ACTION": "重新打开",
@@ -144,9 +160,9 @@
"AGENTS_LOADING": "正在加载客服代表...",
"ASSIGN_TEAM": "分配一个团队",
"DELETE": "删除对话",
- "OPEN_IN_NEW_TAB": "Open in new tab",
- "COPY_LINK": "Copy conversation link",
- "COPY_LINK_SUCCESS": "Conversation link copied to clipboard",
+ "OPEN_IN_NEW_TAB": "在新标签页中打开",
+ "COPY_LINK": "复制对话链接",
+ "COPY_LINK_SUCCESS": "对话链接已复制到剪贴板",
"API": {
"AGENT_ASSIGNMENT": {
"SUCCESFUL": "对话 ID {conversationId} 已分配给 \"{agentName}\"",
@@ -315,7 +331,7 @@
"CONTACT_ATTRIBUTES": "联系人属性",
"PREVIOUS_CONVERSATION": "上一次对话",
"MACROS": "宏",
- "LINEAR_ISSUES": "Linked Linear Issues",
+ "LINEAR_ISSUES": "已链接的 Linear 问题",
"SHOPIFY_ORDERS": "Shopify Orders"
},
"SHOPIFY": {
diff --git a/app/javascript/dashboard/i18n/locale/zh_CN/generalSettings.json b/app/javascript/dashboard/i18n/locale/zh_CN/generalSettings.json
index beb501125..3181e1d0b 100644
--- a/app/javascript/dashboard/i18n/locale/zh_CN/generalSettings.json
+++ b/app/javascript/dashboard/i18n/locale/zh_CN/generalSettings.json
@@ -3,7 +3,7 @@
"LIMIT_MESSAGES": {
"CONVERSATION": "您已经超过对话限制。黑客计划只允许500次对话。",
"INBOXES": "您已超过收件箱限制。Hacker 计划只支持网站在线聊天。其他收件箱如电子邮件、WhatsApp 等需要付费计划。",
- "AGENTS": "You have exceeded the agent limit. Your plan only allows {allowedAgents} agents.",
+ "AGENTS": "您已超过席位限制。当前计划只允许 {allowedAgents} 个席位。",
"NON_ADMIN": "请联系您的管理员升级计划并继续使用所有功能。"
},
"TITLE": "帐户设置",
@@ -134,7 +134,7 @@
"MULTISELECT": {
"ENTER_TO_SELECT": "按回车键选择",
"ENTER_TO_REMOVE": "按回车键删除",
- "NO_OPTIONS": "List is empty",
+ "NO_OPTIONS": "列表为空",
"SELECT_ONE": "请选择一个",
"SELECT": "选择"
}
diff --git a/app/javascript/dashboard/i18n/locale/zh_CN/helpCenter.json b/app/javascript/dashboard/i18n/locale/zh_CN/helpCenter.json
index ede897b38..2b9dcb49f 100644
--- a/app/javascript/dashboard/i18n/locale/zh_CN/helpCenter.json
+++ b/app/javascript/dashboard/i18n/locale/zh_CN/helpCenter.json
@@ -741,7 +741,8 @@
"LIVE_CHAT_WIDGET": {
"LABEL": "在线聊天小部件",
"PLACEHOLDER": "选择在线聊天小部件",
- "HELP_TEXT": "选择将显示在您的帮助中心上的在线聊天小部件"
+ "HELP_TEXT": "选择将显示在您的帮助中心上的在线聊天小部件",
+ "NONE_OPTION": "没有小部件"
},
"BRAND_COLOR": {
"LABEL": "品牌颜色"
@@ -807,6 +808,58 @@
"ERROR_MESSAGE": "无法更新门户"
}
}
+ },
+ "PDF_UPLOAD": {
+ "TITLE": "Upload PDF Document",
+ "DESCRIPTION": "Upload a PDF document to automatically generate FAQs using AI",
+ "DRAG_DROP_TEXT": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "选择 PDF 文件",
+ "ADDITIONAL_CONTEXT_LABEL": "Additional Context (Optional)",
+ "ADDITIONAL_CONTEXT_PLACEHOLDER": "Provide any additional context or instructions for FAQ generation...",
+ "UPLOADING": "上传中...",
+ "UPLOAD": "上传和进度",
+ "CANCEL": "取消",
+ "ERROR_INVALID_TYPE": "请选择一个有效的 PDF 文件",
+ "ERROR_FILE_TOO_LARGE": "文件大小必须小于 512MB",
+ "ERROR_UPLOAD_FAILED": "上传 PDF 失败。请重试。"
+ },
+ "PDF_DOCUMENTS": {
+ "TITLE": "PDF 文档",
+ "DESCRIPTION": "管理上传的 PDF 文档并从它们生成常见问题",
+ "UPLOAD_PDF": "上传 PDF",
+ "UPLOAD_FIRST_PDF": "上传您的第一个PDF",
+ "UPLOADED_BY": "上传者",
+ "GENERATE_FAQS": "生成常见问题",
+ "GENERATING": "生成中...",
+ "CONFIRM_DELETE": "您确定要删除 {filename}?",
+ "EMPTY_STATE": {
+ "TITLE": "尚无PDF文档",
+ "DESCRIPTION": "上传 PDF 文档以使用 AI 自动生成常见问题内容"
+ },
+ "STATUS": {
+ "UPLOADED": "已就绪",
+ "PROCESSING": "处理中",
+ "PROCESSED": "已完成",
+ "FAILED": "失败"
+ }
+ },
+ "CONTENT_GENERATION": {
+ "TITLE": "内容生成",
+ "DESCRIPTION": "上传 PDF 文档以使用 AI 自动生成常见问题内容",
+ "UPLOAD_TITLE": "上传 PDF 文档",
+ "DRAG_DROP": "拖放您的 PDF 文件到此处,或单击以选择",
+ "SELECT_FILE": "选择 PDF 文件",
+ "UPLOADING": "正在处理文档...",
+ "UPLOAD_SUCCESS": "文档处理成功!",
+ "UPLOAD_ERROR": "上传文档失败。请重试。",
+ "INVALID_FILE_TYPE": "请选择一个有效的 PDF 文件",
+ "FILE_TOO_LARGE": "文件大小必须小于 512MB",
+ "GENERATED_CONTENT": "生成常见问题",
+ "PUBLISH_SELECTED": "发布所选内容",
+ "PUBLISHING": "发布中...",
+ "FROM_DOCUMENT": "来自文档",
+ "NO_CONTENT": "No generated content available. Upload a PDF document to get started.",
+ "LOADING": "Loading generated content..."
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/zh_CN/inbox.json b/app/javascript/dashboard/i18n/locale/zh_CN/inbox.json
index 4b6be6b7f..bd5197767 100644
--- a/app/javascript/dashboard/i18n/locale/zh_CN/inbox.json
+++ b/app/javascript/dashboard/i18n/locale/zh_CN/inbox.json
@@ -76,19 +76,19 @@
"REAUTHORIZE": {
"TITLE": "Reauthorization Required",
"DESCRIPTION": "Your WhatsApp connection has expired. Please reconnect to continue receiving and sending messages.",
- "BUTTON_TEXT": "Reconnect WhatsApp",
- "LOADING_FACEBOOK": "Loading Facebook SDK...",
- "SUCCESS": "WhatsApp reconnected successfully",
- "ERROR": "Failed to reconnect WhatsApp. Please try again.",
- "WHATSAPP_APP_ID_MISSING": "WhatsApp App ID is not configured. Please contact your administrator.",
+ "BUTTON_TEXT": "重新连接 WhatsApp",
+ "LOADING_FACEBOOK": "加载 Facebook SDK...",
+ "SUCCESS": "WhatsApp 重新连接成功",
+ "ERROR": "无法重新连接 WhatsApp。请再试一次。",
+ "WHATSAPP_APP_ID_MISSING": "WhatsApp ID未配置。请联系您的管理员。",
"WHATSAPP_CONFIG_ID_MISSING": "WhatsApp Configuration ID is not configured. Please contact your administrator.",
- "CONFIGURATION_ERROR": "Configuration error occurred during reauthorization.",
- "FACEBOOK_LOAD_ERROR": "Failed to load Facebook SDK. Please try again.",
+ "CONFIGURATION_ERROR": "重新授权时发生配置错误。",
+ "FACEBOOK_LOAD_ERROR": "无法加载 Facebook SDK。请重试。",
"TROUBLESHOOTING": {
- "TITLE": "Troubleshooting",
- "POPUP_BLOCKED": "Ensure pop-ups are allowed for this site",
- "COOKIES": "Third-party cookies must be enabled",
- "ADMIN_ACCESS": "You need admin access to the WhatsApp Business Account"
+ "TITLE": "疑难解答",
+ "POPUP_BLOCKED": "确保此站点允许弹出窗口",
+ "COOKIES": "必须启用第三方cookie",
+ "ADMIN_ACCESS": "您需要管理员权限才能访问 WhatsApp Business 账户"
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/zh_CN/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/zh_CN/inboxMgmt.json
index 8abeeb053..6900d1433 100644
--- a/app/javascript/dashboard/i18n/locale/zh_CN/inboxMgmt.json
+++ b/app/javascript/dashboard/i18n/locale/zh_CN/inboxMgmt.json
@@ -222,15 +222,15 @@
"DESC": "开始通过WhatsApp支持您的客户",
"PROVIDERS": {
"LABEL": "API提供商",
- "WHATSAPP_EMBEDDED": "WhatsApp Business",
+ "WHATSAPP_EMBEDDED": "WhatsApp 商务版",
"TWILIO": "Twilio",
"WHATSAPP_CLOUD": "WhatsApp 云服务",
- "WHATSAPP_CLOUD_DESC": "Quick setup through Meta",
- "TWILIO_DESC": "Connect via Twilio credentials",
+ "WHATSAPP_CLOUD_DESC": "通过 Meta 快速完成设置",
+ "TWILIO_DESC": "通过 Twilio 凭据连接",
"360_DIALOG": "360Dialog"
},
"SELECT_PROVIDER": {
- "TITLE": "Select your API provider",
+ "TITLE": "选择您的 API 提供商",
"DESCRIPTION": "Choose your WhatsApp provider. You can connect directly through Meta which requires no setup, or connect through Twilio using your account credentials."
},
"INBOX_NAME": {
@@ -272,8 +272,8 @@
},
"SUBMIT_BUTTON": "创建WhatsApp频道",
"EMBEDDED_SIGNUP": {
- "TITLE": "Quick Setup with Meta",
- "DESC": "You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
+ "TITLE": "Quick setup with Meta",
+ "DESC": "Use the WhatsApp Embedded Signup flow to quickly connect new numbers. You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
"BENEFITS": {
"TITLE": "Benefits of Embedded Signup:",
"EASY_SETUP": "No manual configuration required",
@@ -281,29 +281,30 @@
"AUTO_CONFIG": "Automatic webhook and phone number configuration"
},
"LEARN_MORE": {
- "TEXT": "To learn more about integrated signup, pricing, and limitations, visit",
- "LINK_TEXT": "this link.",
- "LINK_URL": "https://developers.facebook.com/docs/whatsapp/embedded-signup/custom-flows/onboarding-business-app-users#limitations"
+ "TEXT": "To learn more about integrated signup, pricing, and limitations, visit {link}.",
+ "LINK_TEXT": "this link"
},
"SUBMIT_BUTTON": "Connect with WhatsApp Business",
"AUTH_PROCESSING": "Authenticating with Meta",
"WAITING_FOR_BUSINESS_INFO": "Please complete business setup in the Meta window...",
"PROCESSING": "Setting up your WhatsApp Business Account",
- "LOADING_SDK": "Loading Facebook SDK...",
- "CANCELLED": "WhatsApp Signup was cancelled",
+ "LOADING_SDK": "加载 Facebook SDK...",
+ "CANCELLED": "WhatsApp 注册已取消",
"SUCCESS_TITLE": "WhatsApp Business Account Connected!",
- "WAITING_FOR_AUTH": "Waiting for authentication...",
+ "WAITING_FOR_AUTH": "正在等待认证...",
"INVALID_BUSINESS_DATA": "Invalid business data received from Facebook. Please try again.",
- "SIGNUP_ERROR": "Signup error occurred",
+ "SIGNUP_ERROR": "注册时发生错误",
"AUTH_NOT_COMPLETED": "Authentication not completed. Please restart the process.",
- "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured"
+ "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured",
+ "MANUAL_FALLBACK": "If your number is already connected to the WhatsApp Business Platform (API), or if you’re a tech provider onboarding your own number, please use the {link} flow",
+ "MANUAL_LINK_TEXT": "手动设置流程"
},
"API": {
"ERROR_MESSAGE": "我们无法保存 WhatsApp 通道"
}
},
"VOICE": {
- "TITLE": "Voice Channel",
+ "TITLE": "语音频道",
"DESC": "Integrate Twilio Voice and start supporting your customers via phone calls.",
"PHONE_NUMBER": {
"LABEL": "电话号码",
@@ -323,8 +324,8 @@
},
"API_KEY_SID": {
"LABEL": "API 密钥 SID",
- "PLACEHOLDER": "Enter your Twilio API Key SID",
- "REQUIRED": "API Key SID is required"
+ "PLACEHOLDER": "输入您的 Twilio API 密钥的 SID",
+ "REQUIRED": "API 密钥 SID 未填写"
},
"API_KEY_SECRET": {
"LABEL": "API 密钥密码",
@@ -338,9 +339,9 @@
"TWILIO_STATUS_URL_TITLE": "Twilio Status Callback URL",
"TWILIO_STATUS_URL_SUBTITLE": "Configure this URL as the Status Callback URL on your Twilio phone number."
},
- "SUBMIT_BUTTON": "Create Voice Channel",
+ "SUBMIT_BUTTON": "创建语音频道",
"API": {
- "ERROR_MESSAGE": "We were not able to create the voice channel"
+ "ERROR_MESSAGE": "我们无法创建语音频道"
}
},
"API_CHANNEL": {
@@ -424,7 +425,51 @@
},
"AUTH": {
"TITLE": "选择一个通道",
- "DESC": "Chatwoot支持实时聊天小部件、Facebook Messenger、Twitter个人资料、WhatsApp、电子邮件等作为通道。如果您想构建自定义通道,可以使用API通道创建。要开始,请从下面的通道中选择一个。"
+ "DESC": "Chatwoot支持实时聊天小部件、Facebook Messenger、Twitter个人资料、WhatsApp、电子邮件等作为通道。如果您想构建自定义通道,可以使用API通道创建。要开始,请从下面的通道中选择一个。",
+ "TITLE_NEXT": "完成设置",
+ "TITLE_FINISH": "搞定!",
+ "CHANNEL": {
+ "WEBSITE": {
+ "TITLE": "网站",
+ "DESCRIPTION": "创建在线聊天小部件"
+ },
+ "FACEBOOK": {
+ "TITLE": "Facebook",
+ "DESCRIPTION": "连接您的 Facebook 页面"
+ },
+ "WHATSAPP": {
+ "TITLE": "WhatsApp",
+ "DESCRIPTION": "在 WhatsApp 上回应您的客户"
+ },
+ "EMAIL": {
+ "TITLE": "Email",
+ "DESCRIPTION": "连接到 Gmail、Outlook,或其他提供商"
+ },
+ "SMS": {
+ "TITLE": "短信",
+ "DESCRIPTION": "将短信频道与Twilio 或 Bandwidth 集成"
+ },
+ "API": {
+ "TITLE": "API",
+ "DESCRIPTION": "使用我们的 API 创建一个自定义频道"
+ },
+ "TELEGRAM": {
+ "TITLE": "Telegram",
+ "DESCRIPTION": "使用 Bot 令牌配置 Telegram 频道"
+ },
+ "LINE": {
+ "TITLE": "Line",
+ "DESCRIPTION": "集成 Line"
+ },
+ "INSTAGRAM": {
+ "TITLE": "Instagram",
+ "DESCRIPTION": "连接您的 instagram 帐户"
+ },
+ "VOICE": {
+ "TITLE": "语音",
+ "DESCRIPTION": "与 Twilio 语音集成"
+ }
+ }
},
"AGENTS": {
"TITLE": "客服代理们",
@@ -478,7 +523,10 @@
"MESSAGE": "您现在可以通过您的新频道与您的客户联系。快乐支持",
"BUTTON_TEXT": "带我到这里",
"MORE_SETTINGS": "更多设置",
- "WEBSITE_SUCCESS": "您已成功完成创建网站频道。复制下面显示的代码并将其粘贴在您的网站上。下次客户使用实时聊天时,对话将自动出现在您的收件箱中。"
+ "WEBSITE_SUCCESS": "您已成功完成创建网站频道。复制下面显示的代码并将其粘贴在您的网站上。下次客户使用实时聊天时,对话将自动出现在您的收件箱中。",
+ "WHATSAPP_QR_INSTRUCTION": "扫描上面的二维码以快速测试您的 WhatsApp 收件箱",
+ "MESSENGER_QR_INSTRUCTION": "扫描上面的二维码以快速测试您的 Facebook Messenger 收件箱",
+ "TELEGRAM_QR_INSTRUCTION": "扫描以上二维码以快速测试您的 Telegram 收件箱"
},
"REAUTH": "重新授权",
"VIEW": "查看",
@@ -604,11 +652,11 @@
"WHATSAPP_SECTION_UPDATE_TITLE": "更新API密钥",
"WHATSAPP_SECTION_UPDATE_PLACEHOLDER": "在此处输入新的API密钥",
"WHATSAPP_SECTION_UPDATE_BUTTON": "更新",
- "WHATSAPP_EMBEDDED_SIGNUP_TITLE": "WhatsApp Embedded Signup",
- "WHATSAPP_EMBEDDED_SIGNUP_SUBHEADER": "This inbox is connected through WhatsApp embedded signup.",
- "WHATSAPP_EMBEDDED_SIGNUP_DESCRIPTION": "You can reconfigure this inbox to update your WhatsApp Business settings.",
- "WHATSAPP_RECONFIGURE_BUTTON": "Reconfigure",
- "WHATSAPP_CONNECT_TITLE": "Connect to WhatsApp Business",
+ "WHATSAPP_EMBEDDED_SIGNUP_TITLE": "WhatsApp 嵌入注册",
+ "WHATSAPP_EMBEDDED_SIGNUP_SUBHEADER": "此收件箱已通过嵌入注册的 WhatsApp 连接。",
+ "WHATSAPP_EMBEDDED_SIGNUP_DESCRIPTION": "您可以重新配置此收件箱来更新您的 WhatsApp Business 设置。",
+ "WHATSAPP_RECONFIGURE_BUTTON": "重新配置",
+ "WHATSAPP_CONNECT_TITLE": "连接到 WhatsApp Business",
"WHATSAPP_CONNECT_SUBHEADER": "Upgrade to WhatsApp embedded signup for easier management.",
"WHATSAPP_CONNECT_DESCRIPTION": "Connect this inbox to WhatsApp Business for enhanced features and easier management.",
"WHATSAPP_CONNECT_BUTTON": "连接",
@@ -616,14 +664,14 @@
"WHATSAPP_CONNECT_ERROR": "Failed to connect to WhatsApp Business. Please try again.",
"WHATSAPP_RECONFIGURE_SUCCESS": "Successfully reconfigured WhatsApp Business!",
"WHATSAPP_RECONFIGURE_ERROR": "Failed to reconfigure WhatsApp Business. Please try again.",
- "WHATSAPP_APP_ID_MISSING": "WhatsApp App ID is not configured. Please contact your administrator.",
+ "WHATSAPP_APP_ID_MISSING": "WhatsApp ID未配置。请联系您的管理员。",
"WHATSAPP_CONFIG_ID_MISSING": "WhatsApp Configuration ID is not configured. Please contact your administrator.",
"WHATSAPP_LOGIN_CANCELLED": "WhatsApp login was cancelled. Please try again.",
"WHATSAPP_WEBHOOK_TITLE": "Webhook 验证令牌",
"WHATSAPP_WEBHOOK_SUBHEADER": "此令牌用于验证webhook端点的真实性。",
"WHATSAPP_TEMPLATES_SYNC_TITLE": "Sync Templates",
"WHATSAPP_TEMPLATES_SYNC_SUBHEADER": "Manually sync message templates from WhatsApp to update your available templates.",
- "WHATSAPP_TEMPLATES_SYNC_BUTTON": "Sync Templates",
+ "WHATSAPP_TEMPLATES_SYNC_BUTTON": "同步模板",
"WHATSAPP_TEMPLATES_SYNC_SUCCESS": "Templates sync initiated successfully. It may take a couple of minutes to update.",
"UPDATE_PRE_CHAT_FORM_SETTINGS": "更新预聊天表单设置"
},
@@ -868,9 +916,18 @@
"SCRIPT_SETTINGS": "\n window.chatwootSettings = {options};"
},
"EMAIL_PROVIDERS": {
- "MICROSOFT": "Microsoft",
- "GOOGLE": "Google",
- "OTHER_PROVIDERS": "其他提供商"
+ "MICROSOFT": {
+ "TITLE": "Microsoft",
+ "DESCRIPTION": "与 Microsoft 关联"
+ },
+ "GOOGLE": {
+ "TITLE": "Google",
+ "DESCRIPTION": "与 Google 关联"
+ },
+ "OTHER_PROVIDERS": {
+ "TITLE": "其他提供商",
+ "DESCRIPTION": "与其他提供商关联"
+ }
},
"CHANNELS": {
"MESSENGER": "Messenger",
@@ -884,7 +941,7 @@
"LINE": "Line",
"API": "API 频道",
"INSTAGRAM": "Instagram",
- "VOICE": "Voice"
+ "VOICE": "语音"
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/zh_CN/integrations.json b/app/javascript/dashboard/i18n/locale/zh_CN/integrations.json
index 24de2da6d..6558e5a4b 100644
--- a/app/javascript/dashboard/i18n/locale/zh_CN/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/zh_CN/integrations.json
@@ -326,10 +326,10 @@
"CANCEL": "取消"
},
"CTA": {
- "TITLE": "Connect to Linear",
- "AGENT_DESCRIPTION": "Linear workspace is not connected. Request your administrator to connect a workspace to use this integration.",
+ "TITLE": "连接到 Linear",
+ "AGENT_DESCRIPTION": "Linear 工作区未集成。请通知您的管理员连接一个工作区来使用这个集成。",
"DESCRIPTION": "Linear workspace is not connected. Click the button below to connect your workspace to use this integration.",
- "BUTTON_TEXT": "Connect Linear workspace"
+ "BUTTON_TEXT": "链接 Linear 工作空间"
}
},
"NOTION": {
@@ -487,11 +487,11 @@
"ASSISTANT": "助手"
},
"BASIC_SETTINGS": {
- "TITLE": "Basic settings",
+ "TITLE": "基本设置",
"DESCRIPTION": "Customize what the assistant says when ending a conversation or transferring to a human."
},
"SYSTEM_SETTINGS": {
- "TITLE": "System settings",
+ "TITLE": "系统设置",
"DESCRIPTION": "Customize what the assistant says when ending a conversation or transferring to a human."
},
"CONTROL_ITEMS": {
@@ -534,16 +534,16 @@
},
"BULK_ACTION": {
"SELECTED": "{count} item selected | {count} items selected",
- "SELECT_ALL": "Select all ({count})",
- "UNSELECT_ALL": "Unselect all ({count})",
+ "SELECT_ALL": "全选 ({count})",
+ "UNSELECT_ALL": "取消全选({count})",
"BULK_DELETE_BUTTON": "删除"
},
"ADD": {
"SUGGESTED": {
"TITLE": "Example guardrails",
- "ADD": "Add all",
+ "ADD": "添加全部",
"ADD_SINGLE": "Add this",
- "SAVE": "Add and save (↵)",
+ "SAVE": "添加并保存",
"PLACEHOLDER": "Type in another guardrail..."
},
"NEW": {
@@ -551,7 +551,7 @@
"CREATE": "创建",
"CANCEL": "取消",
"PLACEHOLDER": "Type in another guardrail...",
- "TEST_ALL": "Test all"
+ "TEST_ALL": "测试全部"
}
},
"LIST": {
@@ -582,16 +582,16 @@
},
"BULK_ACTION": {
"SELECTED": "{count} item selected | {count} items selected",
- "SELECT_ALL": "Select all ({count})",
- "UNSELECT_ALL": "Unselect all ({count})",
+ "SELECT_ALL": "全选 ({count})",
+ "UNSELECT_ALL": "取消全选({count})",
"BULK_DELETE_BUTTON": "删除"
},
"ADD": {
"SUGGESTED": {
"TITLE": "Example response guidelines",
- "ADD": "Add all",
+ "ADD": "添加全部",
"ADD_SINGLE": "Add this",
- "SAVE": "Add and save (↵)",
+ "SAVE": "添加并保存",
"PLACEHOLDER": "Type in another response guideline..."
},
"NEW": {
@@ -599,7 +599,7 @@
"CREATE": "创建",
"CANCEL": "取消",
"PLACEHOLDER": "Type in another response guideline...",
- "TEST_ALL": "Test all"
+ "TEST_ALL": "测试全部"
}
},
"LIST": {
@@ -630,14 +630,14 @@
},
"BULK_ACTION": {
"SELECTED": "{count} item selected | {count} items selected",
- "SELECT_ALL": "Select all ({count})",
- "UNSELECT_ALL": "Unselect all ({count})",
+ "SELECT_ALL": "全选 ({count})",
+ "UNSELECT_ALL": "取消全选({count})",
"BULK_DELETE_BUTTON": "删除"
},
"ADD": {
"SUGGESTED": {
"TITLE": "Example scenarios",
- "ADD": "Add all",
+ "ADD": "添加全部",
"ADD_SINGLE": "Add this",
"TOOLS_USED": "Tools used :"
},
@@ -704,11 +704,28 @@
"ERROR_MESSAGE": "创建文档时出错,请重试"
},
"FORM": {
+ "TYPE": {
+ "LABEL": "Document Type",
+ "URL": "网址",
+ "PDF": "PDF 文件"
+ },
"URL": {
"LABEL": "网址",
"PLACEHOLDER": "输入文档的 URL",
"ERROR": "请提供有效的文档 URL"
},
+ "PDF_FILE": {
+ "LABEL": "PDF 文件",
+ "CHOOSE_FILE": "选择 PDF 文件",
+ "ERROR": "请选择一个 PDF 文件",
+ "HELP_TEXT": "最大文件大小: 10MB",
+ "INVALID_TYPE": "请选择一个有效的 PDF 文件",
+ "TOO_LARGE": "文件大小超过 10MB 限制"
+ },
+ "NAME": {
+ "LABEL": "文档名称(可选)",
+ "PLACEHOLDER": "输入文档的名称"
+ },
"ASSISTANT": {
"LABEL": "助手",
"PLACEHOLDER": "选择助手",
@@ -742,8 +759,9 @@
"CONVERSATION": "对话 #{id}"
},
"SELECTED": "{count} 已选择",
- "SELECT_ALL": "Select all ({count})",
- "UNSELECT_ALL": "Unselect all ({count})",
+ "SELECT_ALL": "全选 ({count})",
+ "UNSELECT_ALL": "取消全选({count})",
+ "SEARCH_PLACEHOLDER": "搜索常见问题...",
"BULK_APPROVE_BUTTON": "批准",
"BULK_DELETE_BUTTON": "删除",
"BULK_APPROVE": {
diff --git a/app/javascript/dashboard/i18n/locale/zh_CN/mfa.json b/app/javascript/dashboard/i18n/locale/zh_CN/mfa.json
new file mode 100644
index 000000000..3a76a9a9e
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/zh_CN/mfa.json
@@ -0,0 +1,106 @@
+{
+ "MFA_SETTINGS": {
+ "TITLE": "两步验证",
+ "SUBTITLE": "使用TOTP身份验证来保护您的帐户",
+ "DESCRIPTION": "使用基于时间的一次性密码(TOTP)为您的帐户添加额外的一层安全保护",
+ "STATUS_TITLE": "验证状态",
+ "STATUS_DESCRIPTION": "管理您的二步验证设置和备份码",
+ "ENABLED": "已启用",
+ "DISABLED": "已禁用",
+ "STATUS_ENABLED": "两步验证已启用",
+ "STATUS_ENABLED_DESC": "您的帐户受到额外的安全层保护",
+ "ENABLE_BUTTON": "启用两步验证",
+ "ENHANCE_SECURITY": "增强您的帐户安全",
+ "ENHANCE_SECURITY_DESC": "两步验证除了您的密码外还需要额外的身份验证程序的验证码,从而增加了额外的安全层次。",
+ "SETUP": {
+ "STEP_NUMBER_1": "1",
+ "STEP_NUMBER_2": "2",
+ "STEP1_TITLE": "使用您的身份验证器应用程序扫描二维码",
+ "STEP1_DESCRIPTION": "使用 Google 身份验证器、Authy 或者任何 TOTP 兼容应用程序",
+ "LOADING_QR": "加载中...",
+ "MANUAL_ENTRY": "无法扫描?手动输入代码",
+ "SECRET_KEY": "密钥",
+ "COPY": "复制",
+ "ENTER_CODE": "从您的身份验证程序中输入6位数字代码",
+ "ENTER_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "验证并继续",
+ "CANCEL": "取消",
+ "ERROR_STARTING": "MFA 未启用。请与管理员联系。",
+ "INVALID_CODE": "无效的验证码",
+ "SECRET_COPIED": "密钥已复制到剪贴板",
+ "SUCCESS": "已成功启用两步验证"
+ },
+ "BACKUP": {
+ "TITLE": "保存您的备份代码",
+ "DESCRIPTION": "妥善保管这些备份代码,如果您无法访问身份验证器,每个代码可以使用一次",
+ "IMPORTANT": "重要:",
+ "IMPORTANT_NOTE": " 将这些代码保存到一个安全的位置。您将无法再次看到它们。",
+ "DOWNLOAD": "下载",
+ "COPY_ALL": "复制全部",
+ "CONFIRM": "我已经将我的备份代码保存在一个安全的位置,并且知道我将无法再次看到它们。",
+ "COMPLETE_SETUP": "完成设置",
+ "CODES_COPIED": "备份代码已复制到剪贴板"
+ },
+ "MANAGEMENT": {
+ "BACKUP_CODES": "备份代码",
+ "BACKUP_CODES_DESC": "如果您丢失或使用了您现有的代码,则生成新代码",
+ "REGENERATE": "重新生成备份代码",
+ "DISABLE_MFA": "禁用两步验证",
+ "DISABLE_MFA_DESC": "从您的帐户中删除两步验证",
+ "DISABLE_BUTTON": "禁用两步验证"
+ },
+ "DISABLE": {
+ "TITLE": "禁用两步验证",
+ "DESCRIPTION": "您需要输入您的密码和验证码来禁用两步验证。",
+ "PASSWORD": "密码",
+ "OTP_CODE": "验证码",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "禁用两步验证",
+ "CANCEL": "取消",
+ "SUCCESS": "两步验证已禁用",
+ "ERROR": "禁用MFA失败。请检查您的凭据。"
+ },
+ "REGENERATE": {
+ "TITLE": "重新生成备份代码",
+ "DESCRIPTION": "这将作废您现有的备份代码并生成新的替代。输入您的验证码以继续。",
+ "OTP_CODE": "验证码",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "生成新代码",
+ "CANCEL": "取消",
+ "NEW_CODES_TITLE": "新的备份码已生成",
+ "NEW_CODES_DESC": "您旧的备份代码已失效。将这些新代码保存到一个安全位置。",
+ "CODES_IMPORTANT": "重要:",
+ "CODES_IMPORTANT_NOTE": " 每个代码只能使用一次。在关闭此窗口前保存它们。",
+ "DOWNLOAD_CODES": "下载代码",
+ "COPY_ALL_CODES": "复制全部代码",
+ "CODES_SAVED": "我已保存我的代码",
+ "SUCCESS": "已生成新的备份代码",
+ "ERROR": "重新生成备份代码失败"
+ }
+ },
+ "MFA_VERIFICATION": {
+ "TITLE": "两步验证",
+ "DESCRIPTION": "输入您的验证码以继续",
+ "AUTHENTICATOR_APP": "身份验证器应用",
+ "BACKUP_CODE": "备份代码",
+ "ENTER_OTP_CODE": "从您的身份验证程序中输入6位数字代码",
+ "ENTER_BACKUP_CODE": "输入您的备份代码",
+ "BACKUP_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "验证",
+ "TRY_ANOTHER_METHOD": "尝试另一种验证方法",
+ "CANCEL_LOGIN": "取消并返回登录",
+ "HELP_TEXT": "登录遇到困难吗?",
+ "LEARN_MORE": "了解更多关于两步验证的信息",
+ "HELP_MODAL": {
+ "TITLE": "两步验证帮助",
+ "AUTHENTICATOR_TITLE": "使用身份验证器应用程序",
+ "AUTHENTICATOR_DESC": "打开你的身份验证器应用(Google Autenticator,Authy等),然后输入应用显示的6位数字",
+ "BACKUP_TITLE": "使用备份代码",
+ "BACKUP_DESC": "如果您无法访问身份验证器应用程序,你可以使用此前保存的备份代码替代,每个代码只能使用一次。",
+ "CONTACT_TITLE": "需要更多帮助吗?",
+ "CONTACT_DESC_CLOUD": "如果您无法访问身份验证器应用程序和备份代码,请联系Chatwoot 支持寻求帮助。",
+ "CONTACT_DESC_SELF_HOSTED": "如果您无法访问身份验证器应用程序和备份代码,请联系您的管理员寻求帮助。"
+ },
+ "VERIFICATION_FAILED": "验证失败。请重试。"
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/zh_CN/settings.json b/app/javascript/dashboard/i18n/locale/zh_CN/settings.json
index 843f2f7a2..25c7e3f0d 100644
--- a/app/javascript/dashboard/i18n/locale/zh_CN/settings.json
+++ b/app/javascript/dashboard/i18n/locale/zh_CN/settings.json
@@ -51,6 +51,13 @@
"LARGER": "较大",
"EXTRA_LARGE": "特大"
}
+ },
+ "LANGUAGE": {
+ "TITLE": "首选语言",
+ "NOTE": "选择您想要使用的语言。",
+ "UPDATE_SUCCESS": "您的语言设置已成功更新",
+ "UPDATE_ERROR": "更新语言设置时出错,请重试",
+ "USE_ACCOUNT_DEFAULT": "使用账户默认值"
}
},
"MESSAGE_SIGNATURE_SECTION": {
@@ -73,6 +80,11 @@
"NOTE": "更新您的密码会在多个设备中重置您的登录信息。",
"BTN_TEXT": "更改密码"
},
+ "SECURITY_SECTION": {
+ "TITLE": "安全",
+ "NOTE": "Manage additional security features for your account.",
+ "MFA_BUTTON": "Manage Two-Factor Authentication"
+ },
"ACCESS_TOKEN": {
"TITLE": "访问令牌",
"NOTE": "如果您正在构建基于 API 的集成,这个令牌可以被使用",
@@ -226,7 +238,7 @@
"APPEARANCE": "更改外观",
"SUPER_ADMIN_CONSOLE": "超级管理员控制台",
"DOCS": "阅读文档",
- "CHANGELOG": "Changelog",
+ "CHANGELOG": "更新日志",
"LOGOUT": "注销"
},
"APP_GLOBAL": {
@@ -330,6 +342,7 @@
"REPORTS_LABEL": "标签",
"REPORTS_INBOX": "收件箱",
"REPORTS_TEAM": "团队",
+ "AGENT_ASSIGNMENT": "Agent Assignment",
"SET_AVAILABILITY_TITLE": "将自己设置为",
"SET_YOUR_AVAILABILITY": "设置您的可用性",
"SLA": "SLA",
@@ -350,7 +363,8 @@
"INFO_TEXT": "当您不使用应用程序或仪表板时,让系统自动标记您离线。",
"INFO_SHORT": "当您不使用应用程序时自动标记离线。"
},
- "DOCS": "阅读文档"
+ "DOCS": "阅读文档",
+ "SECURITY": "安全"
},
"BILLING_SETTINGS": {
"TITLE": "计费方式",
@@ -382,6 +396,77 @@
},
"NO_BILLING_USER": "您的计费账户正在配置中。请刷新页面并重试。"
},
+ "SECURITY_SETTINGS": {
+ "TITLE": "安全",
+ "DESCRIPTION": "管理您的账户安全设置。",
+ "LINK_TEXT": "了解更多关于 SAML SSO 的信息",
+ "SAML": {
+ "TITLE": "SAML SSO",
+ "NOTE": "Configure SAML single sign-on for your account. Users will authenticate through your identity provider instead of using email/password.",
+ "ACS_URL": {
+ "LABEL": "ACS URL",
+ "TOOLTIP": "Assertion Consumer Service URL - Configure this URL in your IdP as the destination for SAML responses"
+ },
+ "SSO_URL": {
+ "LABEL": "SSO URL",
+ "HELP": "The URL where SAML authentication requests will be sent",
+ "PLACEHOLDER": "https://your-idp.com/saml/sso"
+ },
+ "CERTIFICATE": {
+ "LABEL": "Signing certificate in PEM format",
+ "HELP": "The public certificate from your identity provider used to verify SAML responses",
+ "PLACEHOLDER": "-----BEGIN CERTIFICATE-----\nMIIC..."
+ },
+ "FINGERPRINT": {
+ "LABEL": "Fingerprint",
+ "TOOLTIP": "SHA-1 fingerprint of the certificate - Use this to verify the certificate in your IdP configuration"
+ },
+ "COPY_SUCCESS": "已复制到剪贴板",
+ "SP_ENTITY_ID": {
+ "LABEL": "SP Entity ID",
+ "HELP": "Unique identifier for this application as a service provider (auto-generated).",
+ "TOOLTIP": "Unique identifier for Chatwoot as the Service Provider - Configure this in your IdP settings"
+ },
+ "IDP_ENTITY_ID": {
+ "LABEL": "Identity Provider Entity ID",
+ "HELP": "Unique identifier for your identity provider (usually found in IdP configuration)",
+ "PLACEHOLDER": "https://your-idp.com/saml"
+ },
+ "UPDATE_BUTTON": "更新 SAML 设置",
+ "API": {
+ "SUCCESS": "SAML settings updated successfully",
+ "ERROR": "Failed to update SAML settings",
+ "ERROR_LOADING": "Failed to load SAML settings",
+ "DISABLED": "SAML settings disabled successfully"
+ },
+ "VALIDATION": {
+ "REQUIRED_FIELDS": "SSO URL, Identity Provider Entity ID, and Certificate are required fields",
+ "SSO_URL_ERROR": "Please enter a valid SSO URL",
+ "CERTIFICATE_ERROR": "Certificate is required",
+ "IDP_ENTITY_ID_ERROR": "Identity Provider Entity ID is required"
+ },
+ "ENTERPRISE_PAYWALL": {
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade to an Enterprise plan to access SAML single sign-on and other advanced security features.",
+ "ASK_ADMIN": "请联系您的管理员进行升级。"
+ },
+ "PAYWALL": {
+ "TITLE": "Upgrade to enable SAML SSO",
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade your plan to get access to SAML single sign-on and other advanced features.",
+ "UPGRADE_NOW": "立即升级",
+ "CANCEL_ANYTIME": "您可以随时更改或取消您的计划"
+ },
+ "ATTRIBUTE_MAPPING": {
+ "TITLE": "SAML Attribute Setup",
+ "DESCRIPTION": "The following attribute mappings must be configured in your identity provider"
+ },
+ "INFO_SECTION": {
+ "TITLE": "Service Provider Information",
+ "TOOLTIP": "Copy these values and configure them in your Identity Provider to establish the SAML connection"
+ }
+ }
+ },
"CREATE_ACCOUNT": {
"NO_ACCOUNT_WARNING": "哦,糟糕!我们找不到任何 Chatwoot 账户。请创建一个新账户以继续。",
"NEW_ACCOUNT": "新账户",
@@ -418,5 +503,255 @@
"SWITCH_TO_REPLY": "切换到回复",
"TOGGLE_SNOOZE_DROPDOWN": "切换暂停下拉菜单"
}
+ },
+ "ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent assignment",
+ "DESCRIPTION": "Define policies to effectively manage workload and route conversations based on the needs of inboxes and agents. Learn more here"
+ },
+ "ASSIGNMENT_POLICY": {
+ "TITLE": "Assignment policy",
+ "DESCRIPTION": "Manage how conversations get assigned in inboxes.",
+ "FEATURES": [
+ "Assign by conversations evenly or by available capacity",
+ "Add fair distribution rules to avoid overloading any agent",
+ "Add inboxes to a policy - one policy per inbox"
+ ]
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "TITLE": "Agent capacity policy",
+ "DESCRIPTION": "Manage workload for agents.",
+ "FEATURES": [
+ "Define maximum conversations per inbox",
+ "Create exceptions based on labels and time",
+ "Add agents to a policy - one policy per agent"
+ ]
+ }
+ },
+ "AGENT_ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Assignment policy",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "ORDER": "Order",
+ "PRIORITY": "优先级",
+ "ACTIVE": "状态",
+ "INACTIVE": "Inactive",
+ "POPOVER": "Added inboxes",
+ "EDIT": "编辑"
+ },
+ "NO_RECORDS_FOUND": "No assignment policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create assignment policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy created successfully",
+ "ERROR_MESSAGE": "Failed to create assignment policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit assignment policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_INBOX_DIALOG": {
+ "TITLE": "Add inbox",
+ "DESCRIPTION": "{inboxName} inbox is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "取消"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update assignment policy"
+ },
+ "INBOX_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Inbox added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Inbox removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove inbox from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "描述:",
+ "PLACEHOLDER": "输入描述"
+ },
+ "STATUS": {
+ "LABEL": "状态:",
+ "PLACEHOLDER": "选择状态",
+ "ACTIVE": "Policy is active",
+ "INACTIVE": "Policy is inactive"
+ },
+ "ASSIGNMENT_ORDER": {
+ "LABEL": "Assignment order",
+ "ROUND_ROBIN": {
+ "LABEL": "Round robin",
+ "DESCRIPTION": "Assign conversations evenly among agents."
+ },
+ "BALANCED": {
+ "LABEL": "Balanced",
+ "DESCRIPTION": "Assign conversations based on available capacity."
+ }
+ },
+ "ASSIGNMENT_PRIORITY": {
+ "LABEL": "Assignment priority",
+ "EARLIEST_CREATED": {
+ "LABEL": "Earliest created",
+ "DESCRIPTION": "The conversation that was created first gets assigned first."
+ },
+ "LONGEST_WAITING": {
+ "LABEL": "Longest waiting",
+ "DESCRIPTION": "The conversation waiting the longest gets assigned first."
+ }
+ },
+ "FAIR_DISTRIBUTION": {
+ "LABEL": "Fair distribution policy",
+ "DESCRIPTION": "Set the maximum number of conversations that can be assigned per agent within a time window to avoid overloading any one agent. This required field defaults to 100 conversations per hour.",
+ "INPUT_MAX": "Assign max",
+ "DURATION": "Conversations per agent in every"
+ },
+ "INBOXES": {
+ "LABEL": "Added inboxes",
+ "DESCRIPTION": "Add inboxes for which this policy will be applicable.",
+ "ADD_BUTTON": "Add inbox",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select inboxes to add",
+ "ADD_BUTTON": "添加"
+ },
+ "EMPTY_STATE": "No inboxes added to this policy, add an inbox to get started",
+ "API": {
+ "SUCCESS_MESSAGE": "Inbox successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Assignment policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete assignment policy"
+ }
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent capacity",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "POPOVER": "Added agents",
+ "EDIT": "编辑"
+ },
+ "NO_RECORDS_FOUND": "No agent capacity policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create agent capacity policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy created successfully",
+ "ERROR_MESSAGE": "Failed to create agent capacity policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit agent capacity policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_AGENT_DIALOG": {
+ "TITLE": "Add agent",
+ "DESCRIPTION": "{agentName} is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "取消"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update agent capacity policy"
+ },
+ "AGENT_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Agent added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Agent removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove agent from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "描述:",
+ "PLACEHOLDER": "输入描述"
+ },
+ "INBOX_CAPACITY_LIMIT": {
+ "LABEL": "Inbox capacity limits",
+ "ADD_BUTTON": "Add inbox",
+ "FIELD": {
+ "SELECT_INBOX": "Select inbox",
+ "MAX_CONVERSATIONS": "Max conversations",
+ "SET_LIMIT": "Set limit"
+ },
+ "EMPTY_STATE": "没有设置收件箱限制"
+ },
+ "EXCLUSION_RULES": {
+ "LABEL": "Exclusion rules",
+ "DESCRIPTION": "Conversations that satisfy the following conditions would not count towards agent capacity",
+ "TAGS": {
+ "LABEL": "Exclude conversations tagged with specific labels",
+ "ADD_TAG": "添加标签",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select tags to add"
+ },
+ "EMPTY_STATE": "No tags added to this policy."
+ },
+ "DURATION": {
+ "LABEL": "Exclude conversations older than a specified duration",
+ "PLACEHOLDER": "Set time"
+ }
+ },
+ "USERS": {
+ "LABEL": "Assigned agents",
+ "DESCRIPTION": "Add agents for which this policy will be applicable.",
+ "ADD_BUTTON": "Add agent",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select agents to add",
+ "ADD_BUTTON": "添加"
+ },
+ "EMPTY_STATE": "No agents added",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Agent capacity policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete agent capacity policy"
+ }
+ },
+ "DELETE_POLICY": {
+ "TITLE": "Delete policy",
+ "DESCRIPTION": "Are you sure you want to delete this policy? This action cannot be undone.",
+ "CONFIRM_BUTTON_LABEL": "删除",
+ "CANCEL_BUTTON_LABEL": "取消"
+ }
}
}
diff --git a/app/javascript/dashboard/i18n/locale/zh_CN/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/zh_CN/whatsappTemplates.json
index 9ca1ae234..c7db1963d 100644
--- a/app/javascript/dashboard/i18n/locale/zh_CN/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/zh_CN/whatsappTemplates.json
@@ -40,6 +40,7 @@
"BUTTON_LABEL": "Button {index}",
"COUPON_CODE": "Enter coupon code (max 15 chars)",
"MEDIA_URL_LABEL": "Enter {type} URL",
+ "DOCUMENT_NAME_PLACEHOLDER": "Enter document filename (e.g., Invoice_2025.pdf)",
"BUTTON_PARAMETER": "Enter button parameter"
}
}
diff --git a/app/javascript/dashboard/i18n/locale/zh_TW/automation.json b/app/javascript/dashboard/i18n/locale/zh_TW/automation.json
index 416683ea6..d9dc73351 100644
--- a/app/javascript/dashboard/i18n/locale/zh_TW/automation.json
+++ b/app/javascript/dashboard/i18n/locale/zh_TW/automation.json
@@ -177,7 +177,8 @@
"REFERER_LINK": "Referrer Link",
"ASSIGNEE_NAME": "Assignee",
"TEAM_NAME": "Team",
- "PRIORITY": "優先程度"
+ "PRIORITY": "優先程度",
+ "LABELS": "標籤"
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/zh_TW/contact.json b/app/javascript/dashboard/i18n/locale/zh_TW/contact.json
index 9a0bbc05d..8c1e26894 100644
--- a/app/javascript/dashboard/i18n/locale/zh_TW/contact.json
+++ b/app/javascript/dashboard/i18n/locale/zh_TW/contact.json
@@ -610,6 +610,15 @@
"SEND_MESSAGE": "傳送訊息"
}
},
+ "TWILIO_OPTIONS": {
+ "LABEL": "Select template",
+ "SEARCH_PLACEHOLDER": "Search templates",
+ "EMPTY_STATE": "No templates found",
+ "TEMPLATE_PARSER": {
+ "BACK": "返回",
+ "SEND_MESSAGE": "傳送訊息"
+ }
+ },
"ACTION_BUTTONS": {
"DISCARD": "Discard",
"SEND": "Send ({keyCode})"
diff --git a/app/javascript/dashboard/i18n/locale/zh_TW/contactFilters.json b/app/javascript/dashboard/i18n/locale/zh_TW/contactFilters.json
index 197785619..5d5de1c42 100644
--- a/app/javascript/dashboard/i18n/locale/zh_TW/contactFilters.json
+++ b/app/javascript/dashboard/i18n/locale/zh_TW/contactFilters.json
@@ -48,7 +48,8 @@
"CREATED_AT": "建立於",
"LAST_ACTIVITY": "最後活動",
"REFERER_LINK": "Referrer link",
- "BLOCKED": "Blocked"
+ "BLOCKED": "Blocked",
+ "LABELS": "標籤"
},
"GROUPS": {
"STANDARD_FILTERS": "一般篩選條件",
diff --git a/app/javascript/dashboard/i18n/locale/zh_TW/contentTemplates.json b/app/javascript/dashboard/i18n/locale/zh_TW/contentTemplates.json
new file mode 100644
index 000000000..f490c40d1
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/zh_TW/contentTemplates.json
@@ -0,0 +1,51 @@
+{
+ "CONTENT_TEMPLATES": {
+ "MODAL": {
+ "TITLE": "Twilio Templates",
+ "SUBTITLE": "Select the Twilio template you want to send",
+ "TEMPLATE_SELECTED_SUBTITLE": "Configure template: {templateName}"
+ },
+ "PICKER": {
+ "SEARCH_PLACEHOLDER": "Search Templates",
+ "NO_TEMPLATES_FOUND": "No templates found for",
+ "NO_CONTENT": "No content",
+ "HEADER": "Header",
+ "BODY": "Body",
+ "FOOTER": "Footer",
+ "BUTTONS": "Buttons",
+ "CATEGORY": "Category",
+ "MEDIA_CONTENT": "Media Content",
+ "MEDIA_CONTENT_FALLBACK": "media content",
+ "NO_TEMPLATES_AVAILABLE": "No Twilio templates available. Click refresh to sync templates from Twilio.",
+ "REFRESH_BUTTON": "Refresh templates",
+ "REFRESH_SUCCESS": "Templates refresh initiated. It may take a couple of minutes to update.",
+ "REFRESH_ERROR": "Failed to refresh templates. Please try again.",
+ "LABELS": {
+ "LANGUAGE": "Language",
+ "TEMPLATE_BODY": "Template Body",
+ "CATEGORY": "Category"
+ },
+ "TYPES": {
+ "MEDIA": "Media",
+ "QUICK_REPLY": "Quick Reply",
+ "TEXT": "文字"
+ }
+ },
+ "PARSER": {
+ "VARIABLES_LABEL": "Variables",
+ "LANGUAGE": "Language",
+ "CATEGORY": "Category",
+ "VARIABLE_PLACEHOLDER": "Enter {variable} value",
+ "GO_BACK_LABEL": "Go Back",
+ "SEND_MESSAGE_LABEL": "Send Message",
+ "FORM_ERROR_MESSAGE": "Please fill all variables before sending",
+ "MEDIA_HEADER_LABEL": "{type} Header",
+ "MEDIA_URL_LABEL": "Enter full media URL",
+ "MEDIA_URL_PLACEHOLDER": "https://example.com/image.jpg"
+ },
+ "FORM": {
+ "BACK_BUTTON": "返回",
+ "SEND_MESSAGE_BUTTON": "Send Message"
+ }
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/zh_TW/conversation.json b/app/javascript/dashboard/i18n/locale/zh_TW/conversation.json
index 7b15031ca..333a13a0f 100644
--- a/app/javascript/dashboard/i18n/locale/zh_TW/conversation.json
+++ b/app/javascript/dashboard/i18n/locale/zh_TW/conversation.json
@@ -35,6 +35,11 @@
"API_HOURS_WINDOW": "You can only reply to this conversation within {hours} hours",
"NOT_ASSIGNED_TO_YOU": "This conversation is not assigned to you. Would you like to assign this conversation to yourself?",
"ASSIGN_TO_ME": "指定給我",
+ "BOT_HANDOFF_MESSAGE": "You are responding to a conversation which is currently handled by an assistant or a bot.",
+ "BOT_HANDOFF_ACTION": "Mark open and assign to you",
+ "BOT_HANDOFF_REOPEN_ACTION": "Mark conversation open",
+ "BOT_HANDOFF_SUCCESS": "Conversation has been handed over to you",
+ "BOT_HANDOFF_ERROR": "Failed to take over the conversation. Please try again.",
"TWILIO_WHATSAPP_CAN_REPLY": "You can only reply to this conversation using a template message due to",
"TWILIO_WHATSAPP_24_HOURS_WINDOW": "24 小時消息視窗限制",
"OLD_INSTAGRAM_INBOX_REPLY_BANNER": "This Instagram account was migrated to the new Instagram channel inbox. All new messages will show up there. You won’t be able to send messages from this conversation anymore.",
@@ -66,6 +71,17 @@
"SHOW_LABELS": "Show labels",
"HIDE_LABELS": "Hide labels"
},
+ "VOICE_CALL": {
+ "INCOMING_CALL": "Incoming call",
+ "OUTGOING_CALL": "Outgoing call",
+ "CALL_IN_PROGRESS": "Call in progress",
+ "NO_ANSWER": "No answer",
+ "MISSED_CALL": "Missed call",
+ "CALL_ENDED": "Call ended",
+ "NOT_ANSWERED_YET": "Not answered yet",
+ "THEY_ANSWERED": "They answered",
+ "YOU_ANSWERED": "You answered"
+ },
"HEADER": {
"RESOLVE_ACTION": "已解決",
"REOPEN_ACTION": "重新打開",
diff --git a/app/javascript/dashboard/i18n/locale/zh_TW/helpCenter.json b/app/javascript/dashboard/i18n/locale/zh_TW/helpCenter.json
index 0fff5245e..f3390be76 100644
--- a/app/javascript/dashboard/i18n/locale/zh_TW/helpCenter.json
+++ b/app/javascript/dashboard/i18n/locale/zh_TW/helpCenter.json
@@ -741,7 +741,8 @@
"LIVE_CHAT_WIDGET": {
"LABEL": "Live chat widget",
"PLACEHOLDER": "Select live chat widget",
- "HELP_TEXT": "Select a live chat widget that will appear on your help center"
+ "HELP_TEXT": "Select a live chat widget that will appear on your help center",
+ "NONE_OPTION": "No widget"
},
"BRAND_COLOR": {
"LABEL": "Brand color"
@@ -807,6 +808,58 @@
"ERROR_MESSAGE": "Unable to update portal"
}
}
+ },
+ "PDF_UPLOAD": {
+ "TITLE": "Upload PDF Document",
+ "DESCRIPTION": "Upload a PDF document to automatically generate FAQs using AI",
+ "DRAG_DROP_TEXT": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "ADDITIONAL_CONTEXT_LABEL": "Additional Context (Optional)",
+ "ADDITIONAL_CONTEXT_PLACEHOLDER": "Provide any additional context or instructions for FAQ generation...",
+ "UPLOADING": "上傳中",
+ "UPLOAD": "Upload & Process",
+ "CANCEL": "取消",
+ "ERROR_INVALID_TYPE": "Please select a valid PDF file",
+ "ERROR_FILE_TOO_LARGE": "File size must be less than 512MB",
+ "ERROR_UPLOAD_FAILED": "Failed to upload PDF. Please try again."
+ },
+ "PDF_DOCUMENTS": {
+ "TITLE": "PDF Documents",
+ "DESCRIPTION": "Manage uploaded PDF documents and generate FAQs from them",
+ "UPLOAD_PDF": "Upload PDF",
+ "UPLOAD_FIRST_PDF": "Upload your first PDF",
+ "UPLOADED_BY": "Uploaded by",
+ "GENERATE_FAQS": "Generate FAQs",
+ "GENERATING": "Generating...",
+ "CONFIRM_DELETE": "Are you sure you want to delete {filename}?",
+ "EMPTY_STATE": {
+ "TITLE": "No PDF documents yet",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQs using AI"
+ },
+ "STATUS": {
+ "UPLOADED": "Ready",
+ "PROCESSING": "Processing",
+ "PROCESSED": "已完成",
+ "FAILED": "Failed"
+ }
+ },
+ "CONTENT_GENERATION": {
+ "TITLE": "Content Generation",
+ "DESCRIPTION": "Upload PDF documents to automatically generate FAQ content using AI",
+ "UPLOAD_TITLE": "Upload PDF Document",
+ "DRAG_DROP": "Drag and drop your PDF file here, or click to select",
+ "SELECT_FILE": "Select PDF File",
+ "UPLOADING": "Processing document...",
+ "UPLOAD_SUCCESS": "Document processed successfully!",
+ "UPLOAD_ERROR": "Failed to upload document. Please try again.",
+ "INVALID_FILE_TYPE": "Please select a valid PDF file",
+ "FILE_TOO_LARGE": "File size must be less than 512MB",
+ "GENERATED_CONTENT": "Generated FAQ Content",
+ "PUBLISH_SELECTED": "Publish Selected",
+ "PUBLISHING": "Publishing...",
+ "FROM_DOCUMENT": "From document",
+ "NO_CONTENT": "No generated content available. Upload a PDF document to get started.",
+ "LOADING": "Loading generated content..."
}
}
}
diff --git a/app/javascript/dashboard/i18n/locale/zh_TW/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/zh_TW/inboxMgmt.json
index 759e3639d..be582fe4b 100644
--- a/app/javascript/dashboard/i18n/locale/zh_TW/inboxMgmt.json
+++ b/app/javascript/dashboard/i18n/locale/zh_TW/inboxMgmt.json
@@ -272,8 +272,8 @@
},
"SUBMIT_BUTTON": "Create WhatsApp Channel",
"EMBEDDED_SIGNUP": {
- "TITLE": "Quick Setup with Meta",
- "DESC": "You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
+ "TITLE": "Quick setup with Meta",
+ "DESC": "Use the WhatsApp Embedded Signup flow to quickly connect new numbers. You will be redirected to Meta to log into your WhatsApp Business account. Having admin access will help make the setup smooth and easy.",
"BENEFITS": {
"TITLE": "Benefits of Embedded Signup:",
"EASY_SETUP": "No manual configuration required",
@@ -281,9 +281,8 @@
"AUTO_CONFIG": "Automatic webhook and phone number configuration"
},
"LEARN_MORE": {
- "TEXT": "To learn more about integrated signup, pricing, and limitations, visit",
- "LINK_TEXT": "this link.",
- "LINK_URL": "https://developers.facebook.com/docs/whatsapp/embedded-signup/custom-flows/onboarding-business-app-users#limitations"
+ "TEXT": "To learn more about integrated signup, pricing, and limitations, visit {link}.",
+ "LINK_TEXT": "this link"
},
"SUBMIT_BUTTON": "Connect with WhatsApp Business",
"AUTH_PROCESSING": "Authenticating with Meta",
@@ -296,7 +295,9 @@
"INVALID_BUSINESS_DATA": "Invalid business data received from Facebook. Please try again.",
"SIGNUP_ERROR": "Signup error occurred",
"AUTH_NOT_COMPLETED": "Authentication not completed. Please restart the process.",
- "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured"
+ "SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured",
+ "MANUAL_FALLBACK": "If your number is already connected to the WhatsApp Business Platform (API), or if you’re a tech provider onboarding your own number, please use the {link} flow",
+ "MANUAL_LINK_TEXT": "manual setup flow"
},
"API": {
"ERROR_MESSAGE": "We were not able to save the WhatsApp channel"
@@ -424,7 +425,51 @@
},
"AUTH": {
"TITLE": "選擇一個頻道",
- "DESC": "Chatwoot supports live-chat widgets, Facebook Messenger, Twitter profiles, WhatsApp, Emails, etc., as channels. If you want to build a custom channel, you can create it using the API channel. To get started, choose one of the channels below."
+ "DESC": "Chatwoot supports live-chat widgets, Facebook Messenger, Twitter profiles, WhatsApp, Emails, etc., as channels. If you want to build a custom channel, you can create it using the API channel. To get started, choose one of the channels below.",
+ "TITLE_NEXT": "Complete the setup",
+ "TITLE_FINISH": "Voilà!",
+ "CHANNEL": {
+ "WEBSITE": {
+ "TITLE": "Website",
+ "DESCRIPTION": "Create a live-chat widget"
+ },
+ "FACEBOOK": {
+ "TITLE": "Facebook",
+ "DESCRIPTION": "Connect your Facebook page"
+ },
+ "WHATSAPP": {
+ "TITLE": "WhatsApp",
+ "DESCRIPTION": "Support your customers on WhatsApp"
+ },
+ "EMAIL": {
+ "TITLE": "Email",
+ "DESCRIPTION": "Connect with Gmail, Outlook, or other providers"
+ },
+ "SMS": {
+ "TITLE": "SMS",
+ "DESCRIPTION": "Integrate SMS channel with Twilio or bandwidth"
+ },
+ "API": {
+ "TITLE": "API",
+ "DESCRIPTION": "Make a custom channel using our API"
+ },
+ "TELEGRAM": {
+ "TITLE": "Telegram",
+ "DESCRIPTION": "Configure Telegram channel using Bot token"
+ },
+ "LINE": {
+ "TITLE": "Line",
+ "DESCRIPTION": "Integrate your Line channel"
+ },
+ "INSTAGRAM": {
+ "TITLE": "Instagram",
+ "DESCRIPTION": "Connect your instagram account"
+ },
+ "VOICE": {
+ "TITLE": "Voice",
+ "DESCRIPTION": "Integrate with Twilio Voice"
+ }
+ }
},
"AGENTS": {
"TITLE": "客服",
@@ -478,7 +523,10 @@
"MESSAGE": "您現在可以通過您的新頻道與您的客户聯繫。開心的支援客戶吧",
"BUTTON_TEXT": "带我到這裡",
"MORE_SETTINGS": "更多設定",
- "WEBSITE_SUCCESS": "您已成功完成建立網站頻道。複製下面顯示的代碼並將其黏貼在您的網站上。 下次客户使用即時聊天時,對話將自動出現在您的收件匣中。"
+ "WEBSITE_SUCCESS": "您已成功完成建立網站頻道。複製下面顯示的代碼並將其黏貼在您的網站上。 下次客户使用即時聊天時,對話將自動出現在您的收件匣中。",
+ "WHATSAPP_QR_INSTRUCTION": "Scan the QR code above to quickly test your WhatsApp inbox",
+ "MESSENGER_QR_INSTRUCTION": "Scan the QR code above to quickly test your Facebook Messenger inbox",
+ "TELEGRAM_QR_INSTRUCTION": "Scan the QR code above to quickly test your Telegram inbox"
},
"REAUTH": "重新授權",
"VIEW": "查看",
@@ -868,9 +916,18 @@
"SCRIPT_SETTINGS": "\n window.chatwootSettings = {options};"
},
"EMAIL_PROVIDERS": {
- "MICROSOFT": "Microsoft",
- "GOOGLE": "Google",
- "OTHER_PROVIDERS": "Other Providers"
+ "MICROSOFT": {
+ "TITLE": "Microsoft",
+ "DESCRIPTION": "Connect with Microsoft"
+ },
+ "GOOGLE": {
+ "TITLE": "Google",
+ "DESCRIPTION": "Connect with Google"
+ },
+ "OTHER_PROVIDERS": {
+ "TITLE": "Other Providers",
+ "DESCRIPTION": "Connect with Other Providers"
+ }
},
"CHANNELS": {
"MESSENGER": "Messenger",
diff --git a/app/javascript/dashboard/i18n/locale/zh_TW/integrations.json b/app/javascript/dashboard/i18n/locale/zh_TW/integrations.json
index 598fc185b..cf00f291a 100644
--- a/app/javascript/dashboard/i18n/locale/zh_TW/integrations.json
+++ b/app/javascript/dashboard/i18n/locale/zh_TW/integrations.json
@@ -704,11 +704,28 @@
"ERROR_MESSAGE": "There was an error creating the document, please try again."
},
"FORM": {
+ "TYPE": {
+ "LABEL": "Document Type",
+ "URL": "URL",
+ "PDF": "PDF File"
+ },
"URL": {
"LABEL": "URL",
"PLACEHOLDER": "Enter the URL of the document",
"ERROR": "Please provide a valid URL for the document"
},
+ "PDF_FILE": {
+ "LABEL": "PDF File",
+ "CHOOSE_FILE": "Choose PDF file",
+ "ERROR": "Please select a PDF file",
+ "HELP_TEXT": "Maximum file size: 10MB",
+ "INVALID_TYPE": "Please select a valid PDF file",
+ "TOO_LARGE": "File size exceeds 10MB limit"
+ },
+ "NAME": {
+ "LABEL": "Document Name (Optional)",
+ "PLACEHOLDER": "Enter a name for the document"
+ },
"ASSISTANT": {
"LABEL": "Assistant",
"PLACEHOLDER": "Select the assistant",
@@ -744,6 +761,7 @@
"SELECTED": "{count} selected",
"SELECT_ALL": "Select all ({count})",
"UNSELECT_ALL": "Unselect all ({count})",
+ "SEARCH_PLACEHOLDER": "Search FAQs...",
"BULK_APPROVE_BUTTON": "Approve",
"BULK_DELETE_BUTTON": "刪除",
"BULK_APPROVE": {
diff --git a/app/javascript/dashboard/i18n/locale/zh_TW/mfa.json b/app/javascript/dashboard/i18n/locale/zh_TW/mfa.json
new file mode 100644
index 000000000..f584d7110
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/zh_TW/mfa.json
@@ -0,0 +1,106 @@
+{
+ "MFA_SETTINGS": {
+ "TITLE": "Two-Factor Authentication",
+ "SUBTITLE": "Secure your account with TOTP-based authentication",
+ "DESCRIPTION": "Add an extra layer of security to your account using a time-based one-time password (TOTP)",
+ "STATUS_TITLE": "Authentication Status",
+ "STATUS_DESCRIPTION": "Manage your two-factor authentication settings and backup recovery codes",
+ "ENABLED": "已啟用",
+ "DISABLED": "已停用",
+ "STATUS_ENABLED": "Two-factor authentication is active",
+ "STATUS_ENABLED_DESC": "Your account is protected with an additional layer of security",
+ "ENABLE_BUTTON": "Enable Two-Factor Authentication",
+ "ENHANCE_SECURITY": "Enhance Your Account Security",
+ "ENHANCE_SECURITY_DESC": "Two-factor authentication adds an extra layer of security by requiring a verification code from your authenticator app in addition to your password.",
+ "SETUP": {
+ "STEP_NUMBER_1": "1",
+ "STEP_NUMBER_2": "2",
+ "STEP1_TITLE": "Scan QR Code with Your Authenticator App",
+ "STEP1_DESCRIPTION": "Use Google Authenticator, Authy, or any TOTP-compatible app",
+ "LOADING_QR": "Loading...",
+ "MANUAL_ENTRY": "Can't scan? Enter code manually",
+ "SECRET_KEY": "Secret Key",
+ "COPY": "複製",
+ "ENTER_CODE": "Enter the 6-digit code from your authenticator app",
+ "ENTER_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify & Continue",
+ "CANCEL": "取消",
+ "ERROR_STARTING": "MFA not enabled. Please contact administrator.",
+ "INVALID_CODE": "Invalid verification code",
+ "SECRET_COPIED": "Secret key copied to clipboard",
+ "SUCCESS": "Two-factor authentication has been enabled successfully"
+ },
+ "BACKUP": {
+ "TITLE": "Save Your Backup Codes",
+ "DESCRIPTION": "Keep these codes safe. Each can be used once if you lose access to your authenticator",
+ "IMPORTANT": "Important:",
+ "IMPORTANT_NOTE": " Save these codes in a secure location. You won't be able to see them again.",
+ "DOWNLOAD": "下載",
+ "COPY_ALL": "Copy All",
+ "CONFIRM": "I have saved my backup codes in a secure location and understand that I won't be able to see them again",
+ "COMPLETE_SETUP": "Complete Setup",
+ "CODES_COPIED": "Backup codes copied to clipboard"
+ },
+ "MANAGEMENT": {
+ "BACKUP_CODES": "Backup Codes",
+ "BACKUP_CODES_DESC": "Generate new codes if you've lost or used your existing ones",
+ "REGENERATE": "Regenerate Backup Codes",
+ "DISABLE_MFA": "Disable 2FA",
+ "DISABLE_MFA_DESC": "Remove two-factor authentication from your account",
+ "DISABLE_BUTTON": "Disable Two-Factor Authentication"
+ },
+ "DISABLE": {
+ "TITLE": "Disable Two-Factor Authentication",
+ "DESCRIPTION": "You'll need to enter your password and a verification code to disable two-factor authentication.",
+ "PASSWORD": "密碼",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Disable 2FA",
+ "CANCEL": "取消",
+ "SUCCESS": "Two-factor authentication has been disabled",
+ "ERROR": "Failed to disable MFA. Please check your credentials."
+ },
+ "REGENERATE": {
+ "TITLE": "Regenerate Backup Codes",
+ "DESCRIPTION": "This will invalidate your existing backup codes and generate new ones. Enter your verification code to continue.",
+ "OTP_CODE": "Verification Code",
+ "OTP_CODE_PLACEHOLDER": "000000",
+ "CONFIRM": "Generate New Codes",
+ "CANCEL": "取消",
+ "NEW_CODES_TITLE": "New Backup Codes Generated",
+ "NEW_CODES_DESC": "Your old backup codes have been invalidated. Save these new codes in a secure location.",
+ "CODES_IMPORTANT": "Important:",
+ "CODES_IMPORTANT_NOTE": " Each code can only be used once. Save them before closing this window.",
+ "DOWNLOAD_CODES": "Download Codes",
+ "COPY_ALL_CODES": "Copy All Codes",
+ "CODES_SAVED": "I've Saved My Codes",
+ "SUCCESS": "New backup codes have been generated",
+ "ERROR": "Failed to regenerate backup codes"
+ }
+ },
+ "MFA_VERIFICATION": {
+ "TITLE": "Two-Factor Authentication",
+ "DESCRIPTION": "Enter your verification code to continue",
+ "AUTHENTICATOR_APP": "Authenticator App",
+ "BACKUP_CODE": "Backup Code",
+ "ENTER_OTP_CODE": "Enter 6-digit code from your authenticator app",
+ "ENTER_BACKUP_CODE": "Enter one of your backup codes",
+ "BACKUP_CODE_PLACEHOLDER": "000000",
+ "VERIFY_BUTTON": "Verify",
+ "TRY_ANOTHER_METHOD": "Try another verification method",
+ "CANCEL_LOGIN": "Cancel and return to login",
+ "HELP_TEXT": "Having trouble signing in?",
+ "LEARN_MORE": "Learn more about 2FA",
+ "HELP_MODAL": {
+ "TITLE": "Two-Factor Authentication Help",
+ "AUTHENTICATOR_TITLE": "Using an Authenticator App",
+ "AUTHENTICATOR_DESC": "Open your authenticator app (Google Authenticator, Authy, etc.) and enter the 6-digit code shown for your account.",
+ "BACKUP_TITLE": "Using a Backup Code",
+ "BACKUP_DESC": "If you don't have access to your authenticator app, you can use one of the backup codes you saved when setting up 2FA. Each code can only be used once.",
+ "CONTACT_TITLE": "Need More Help?",
+ "CONTACT_DESC_CLOUD": "If you've lost access to both your authenticator app and backup codes, please reach out to Chatwoot support for assistance.",
+ "CONTACT_DESC_SELF_HOSTED": "If you've lost access to both your authenticator app and backup codes, please contact your administrator for assistance."
+ },
+ "VERIFICATION_FAILED": "Verification failed. Please try again."
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/zh_TW/settings.json b/app/javascript/dashboard/i18n/locale/zh_TW/settings.json
index 094af1d7c..3a544d72f 100644
--- a/app/javascript/dashboard/i18n/locale/zh_TW/settings.json
+++ b/app/javascript/dashboard/i18n/locale/zh_TW/settings.json
@@ -51,6 +51,13 @@
"LARGER": "Larger",
"EXTRA_LARGE": "Extra Large"
}
+ },
+ "LANGUAGE": {
+ "TITLE": "Preferred Language",
+ "NOTE": "Choose the language you want to use.",
+ "UPDATE_SUCCESS": "Your Language settings have been updated successfully",
+ "UPDATE_ERROR": "There is an error while updating the language settings, please try again",
+ "USE_ACCOUNT_DEFAULT": "Use account default"
}
},
"MESSAGE_SIGNATURE_SECTION": {
@@ -73,6 +80,11 @@
"NOTE": "更新您的密碼會在多個設備中重置您的登入資訊。",
"BTN_TEXT": "變更密碼"
},
+ "SECURITY_SECTION": {
+ "TITLE": "Security",
+ "NOTE": "Manage additional security features for your account.",
+ "MFA_BUTTON": "Manage Two-Factor Authentication"
+ },
"ACCESS_TOKEN": {
"TITLE": "訪問 token",
"NOTE": "如果要構建基於 API 的整合,則可以使用此 token",
@@ -330,6 +342,7 @@
"REPORTS_LABEL": "標籤",
"REPORTS_INBOX": "收件匣",
"REPORTS_TEAM": "Team",
+ "AGENT_ASSIGNMENT": "Agent Assignment",
"SET_AVAILABILITY_TITLE": "我的狀態",
"SET_YOUR_AVAILABILITY": "設定你的服務時間",
"SLA": "服務水準協議(SLA)",
@@ -350,7 +363,8 @@
"INFO_TEXT": "當您未使用應用程式或儀表板時,讓系統自動將您標記為離線",
"INFO_SHORT": "Automatically mark offline when you aren't using the app."
},
- "DOCS": "Read docs"
+ "DOCS": "Read docs",
+ "SECURITY": "Security"
},
"BILLING_SETTINGS": {
"TITLE": "帳單",
@@ -382,6 +396,77 @@
},
"NO_BILLING_USER": "Your billing account is being configured. Please refresh the page and try again."
},
+ "SECURITY_SETTINGS": {
+ "TITLE": "Security",
+ "DESCRIPTION": "Manage your account security settings.",
+ "LINK_TEXT": "Learn more about SAML SSO",
+ "SAML": {
+ "TITLE": "SAML SSO",
+ "NOTE": "Configure SAML single sign-on for your account. Users will authenticate through your identity provider instead of using email/password.",
+ "ACS_URL": {
+ "LABEL": "ACS URL",
+ "TOOLTIP": "Assertion Consumer Service URL - Configure this URL in your IdP as the destination for SAML responses"
+ },
+ "SSO_URL": {
+ "LABEL": "SSO URL",
+ "HELP": "The URL where SAML authentication requests will be sent",
+ "PLACEHOLDER": "https://your-idp.com/saml/sso"
+ },
+ "CERTIFICATE": {
+ "LABEL": "Signing certificate in PEM format",
+ "HELP": "The public certificate from your identity provider used to verify SAML responses",
+ "PLACEHOLDER": "-----BEGIN CERTIFICATE-----\nMIIC..."
+ },
+ "FINGERPRINT": {
+ "LABEL": "Fingerprint",
+ "TOOLTIP": "SHA-1 fingerprint of the certificate - Use this to verify the certificate in your IdP configuration"
+ },
+ "COPY_SUCCESS": "Copied to clipboard",
+ "SP_ENTITY_ID": {
+ "LABEL": "SP Entity ID",
+ "HELP": "Unique identifier for this application as a service provider (auto-generated).",
+ "TOOLTIP": "Unique identifier for Chatwoot as the Service Provider - Configure this in your IdP settings"
+ },
+ "IDP_ENTITY_ID": {
+ "LABEL": "Identity Provider Entity ID",
+ "HELP": "Unique identifier for your identity provider (usually found in IdP configuration)",
+ "PLACEHOLDER": "https://your-idp.com/saml"
+ },
+ "UPDATE_BUTTON": "Update SAML Settings",
+ "API": {
+ "SUCCESS": "SAML settings updated successfully",
+ "ERROR": "Failed to update SAML settings",
+ "ERROR_LOADING": "Failed to load SAML settings",
+ "DISABLED": "SAML settings disabled successfully"
+ },
+ "VALIDATION": {
+ "REQUIRED_FIELDS": "SSO URL, Identity Provider Entity ID, and Certificate are required fields",
+ "SSO_URL_ERROR": "Please enter a valid SSO URL",
+ "CERTIFICATE_ERROR": "Certificate is required",
+ "IDP_ENTITY_ID_ERROR": "Identity Provider Entity ID is required"
+ },
+ "ENTERPRISE_PAYWALL": {
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade to an Enterprise plan to access SAML single sign-on and other advanced security features.",
+ "ASK_ADMIN": "Please reach out to your administrator for the upgrade."
+ },
+ "PAYWALL": {
+ "TITLE": "Upgrade to enable SAML SSO",
+ "AVAILABLE_ON": "The SAML SSO feature is only available in the Enterprise plans.",
+ "UPGRADE_PROMPT": "Upgrade your plan to get access to SAML single sign-on and other advanced features.",
+ "UPGRADE_NOW": "Upgrade now",
+ "CANCEL_ANYTIME": "You can change or cancel your plan anytime"
+ },
+ "ATTRIBUTE_MAPPING": {
+ "TITLE": "SAML Attribute Setup",
+ "DESCRIPTION": "The following attribute mappings must be configured in your identity provider"
+ },
+ "INFO_SECTION": {
+ "TITLE": "Service Provider Information",
+ "TOOLTIP": "Copy these values and configure them in your Identity Provider to establish the SAML connection"
+ }
+ }
+ },
"CREATE_ACCOUNT": {
"NO_ACCOUNT_WARNING": "Uh oh! We could not find any Chatwoot accounts. Please create a new account to continue.",
"NEW_ACCOUNT": "新帳戶",
@@ -418,5 +503,255 @@
"SWITCH_TO_REPLY": "Switch to Reply",
"TOGGLE_SNOOZE_DROPDOWN": "Toggle snooze dropdown"
}
+ },
+ "ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent assignment",
+ "DESCRIPTION": "Define policies to effectively manage workload and route conversations based on the needs of inboxes and agents. Learn more here"
+ },
+ "ASSIGNMENT_POLICY": {
+ "TITLE": "Assignment policy",
+ "DESCRIPTION": "Manage how conversations get assigned in inboxes.",
+ "FEATURES": [
+ "Assign by conversations evenly or by available capacity",
+ "Add fair distribution rules to avoid overloading any agent",
+ "Add inboxes to a policy - one policy per inbox"
+ ]
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "TITLE": "Agent capacity policy",
+ "DESCRIPTION": "Manage workload for agents.",
+ "FEATURES": [
+ "Define maximum conversations per inbox",
+ "Create exceptions based on labels and time",
+ "Add agents to a policy - one policy per agent"
+ ]
+ }
+ },
+ "AGENT_ASSIGNMENT_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Assignment policy",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "ORDER": "Order",
+ "PRIORITY": "優先程度",
+ "ACTIVE": "Active",
+ "INACTIVE": "Inactive",
+ "POPOVER": "Added inboxes",
+ "EDIT": "編輯"
+ },
+ "NO_RECORDS_FOUND": "No assignment policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create assignment policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy created successfully",
+ "ERROR_MESSAGE": "Failed to create assignment policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit assignment policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_INBOX_DIALOG": {
+ "TITLE": "Add inbox",
+ "DESCRIPTION": "{inboxName} inbox is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "取消"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Assignment policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update assignment policy"
+ },
+ "INBOX_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Inbox added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Inbox removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove inbox from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "描述資訊:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "STATUS": {
+ "LABEL": "狀態:",
+ "PLACEHOLDER": "Select status",
+ "ACTIVE": "Policy is active",
+ "INACTIVE": "Policy is inactive"
+ },
+ "ASSIGNMENT_ORDER": {
+ "LABEL": "Assignment order",
+ "ROUND_ROBIN": {
+ "LABEL": "Round robin",
+ "DESCRIPTION": "Assign conversations evenly among agents."
+ },
+ "BALANCED": {
+ "LABEL": "Balanced",
+ "DESCRIPTION": "Assign conversations based on available capacity."
+ }
+ },
+ "ASSIGNMENT_PRIORITY": {
+ "LABEL": "Assignment priority",
+ "EARLIEST_CREATED": {
+ "LABEL": "Earliest created",
+ "DESCRIPTION": "The conversation that was created first gets assigned first."
+ },
+ "LONGEST_WAITING": {
+ "LABEL": "Longest waiting",
+ "DESCRIPTION": "The conversation waiting the longest gets assigned first."
+ }
+ },
+ "FAIR_DISTRIBUTION": {
+ "LABEL": "Fair distribution policy",
+ "DESCRIPTION": "Set the maximum number of conversations that can be assigned per agent within a time window to avoid overloading any one agent. This required field defaults to 100 conversations per hour.",
+ "INPUT_MAX": "Assign max",
+ "DURATION": "Conversations per agent in every"
+ },
+ "INBOXES": {
+ "LABEL": "Added inboxes",
+ "DESCRIPTION": "Add inboxes for which this policy will be applicable.",
+ "ADD_BUTTON": "Add inbox",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select inboxes to add",
+ "ADD_BUTTON": "新增"
+ },
+ "EMPTY_STATE": "No inboxes added to this policy, add an inbox to get started",
+ "API": {
+ "SUCCESS_MESSAGE": "Inbox successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add inbox to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Assignment policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete assignment policy"
+ }
+ },
+ "AGENT_CAPACITY_POLICY": {
+ "INDEX": {
+ "HEADER": {
+ "TITLE": "Agent capacity",
+ "CREATE_POLICY": "New policy"
+ },
+ "CARD": {
+ "POPOVER": "Added agents",
+ "EDIT": "編輯"
+ },
+ "NO_RECORDS_FOUND": "No agent capacity policies found"
+ },
+ "CREATE": {
+ "HEADER": {
+ "TITLE": "Create agent capacity policy"
+ },
+ "CREATE_BUTTON": "Create policy",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy created successfully",
+ "ERROR_MESSAGE": "Failed to create agent capacity policy"
+ }
+ },
+ "EDIT": {
+ "HEADER": {
+ "TITLE": "Edit agent capacity policy"
+ },
+ "EDIT_BUTTON": "Update policy",
+ "CONFIRM_ADD_AGENT_DIALOG": {
+ "TITLE": "Add agent",
+ "DESCRIPTION": "{agentName} is already linked to another policy. Are you sure you want to link it to this policy? It will be unlinked from the other policy.",
+ "CONFIRM_BUTTON_LABEL": "Continue",
+ "CANCEL_BUTTON_LABEL": "取消"
+ },
+ "API": {
+ "SUCCESS_MESSAGE": "Agent capacity policy updated successfully",
+ "ERROR_MESSAGE": "Failed to update agent capacity policy"
+ },
+ "AGENT_API": {
+ "ADD": {
+ "SUCCESS_MESSAGE": "Agent added to policy successfully",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ },
+ "REMOVE": {
+ "SUCCESS_MESSAGE": "Agent removed from policy successfully",
+ "ERROR_MESSAGE": "Failed to remove agent from policy"
+ }
+ }
+ },
+ "FORM": {
+ "NAME": {
+ "LABEL": "Policy name:",
+ "PLACEHOLDER": "Enter policy name"
+ },
+ "DESCRIPTION": {
+ "LABEL": "描述資訊:",
+ "PLACEHOLDER": "Enter description"
+ },
+ "INBOX_CAPACITY_LIMIT": {
+ "LABEL": "Inbox capacity limits",
+ "ADD_BUTTON": "Add inbox",
+ "FIELD": {
+ "SELECT_INBOX": "Select inbox",
+ "MAX_CONVERSATIONS": "Max conversations",
+ "SET_LIMIT": "Set limit"
+ },
+ "EMPTY_STATE": "No inbox limit set"
+ },
+ "EXCLUSION_RULES": {
+ "LABEL": "Exclusion rules",
+ "DESCRIPTION": "Conversations that satisfy the following conditions would not count towards agent capacity",
+ "TAGS": {
+ "LABEL": "Exclude conversations tagged with specific labels",
+ "ADD_TAG": "add tag",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select tags to add"
+ },
+ "EMPTY_STATE": "No tags added to this policy."
+ },
+ "DURATION": {
+ "LABEL": "Exclude conversations older than a specified duration",
+ "PLACEHOLDER": "Set time"
+ }
+ },
+ "USERS": {
+ "LABEL": "Assigned agents",
+ "DESCRIPTION": "Add agents for which this policy will be applicable.",
+ "ADD_BUTTON": "Add agent",
+ "DROPDOWN": {
+ "SEARCH_PLACEHOLDER": "Search and select agents to add",
+ "ADD_BUTTON": "新增"
+ },
+ "EMPTY_STATE": "No agents added",
+ "API": {
+ "SUCCESS_MESSAGE": "Agent successfully added to policy",
+ "ERROR_MESSAGE": "Failed to add agent to policy"
+ }
+ }
+ },
+ "DELETE_POLICY": {
+ "SUCCESS_MESSAGE": "Agent capacity policy deleted successfully",
+ "ERROR_MESSAGE": "Failed to delete agent capacity policy"
+ }
+ },
+ "DELETE_POLICY": {
+ "TITLE": "Delete policy",
+ "DESCRIPTION": "Are you sure you want to delete this policy? This action cannot be undone.",
+ "CONFIRM_BUTTON_LABEL": "刪除",
+ "CANCEL_BUTTON_LABEL": "取消"
+ }
}
}
diff --git a/app/javascript/dashboard/i18n/locale/zh_TW/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/zh_TW/whatsappTemplates.json
index 5f53faaa8..cf28312dc 100644
--- a/app/javascript/dashboard/i18n/locale/zh_TW/whatsappTemplates.json
+++ b/app/javascript/dashboard/i18n/locale/zh_TW/whatsappTemplates.json
@@ -40,6 +40,7 @@
"BUTTON_LABEL": "Button {index}",
"COUPON_CODE": "Enter coupon code (max 15 chars)",
"MEDIA_URL_LABEL": "Enter {type} URL",
+ "DOCUMENT_NAME_PLACEHOLDER": "Enter document filename (e.g., Invoice_2025.pdf)",
"BUTTON_PARAMETER": "Enter button parameter"
}
}
diff --git a/app/javascript/dashboard/routes/dashboard/captain/responses/Index.vue b/app/javascript/dashboard/routes/dashboard/captain/responses/Index.vue
index 85e7f1ed0..86d3fff69 100644
--- a/app/javascript/dashboard/routes/dashboard/captain/responses/Index.vue
+++ b/app/javascript/dashboard/routes/dashboard/captain/responses/Index.vue
@@ -6,10 +6,12 @@ import { useI18n } from 'vue-i18n';
import { OnClickOutside } from '@vueuse/components';
import { useRouter } from 'vue-router';
import { FEATURE_FLAGS } from 'dashboard/featureFlags';
+import { debounce } from '@chatwoot/utils';
import Button from 'dashboard/components-next/button/Button.vue';
import Checkbox from 'dashboard/components-next/checkbox/Checkbox.vue';
import DropdownMenu from 'dashboard/components-next/dropdown-menu/DropdownMenu.vue';
+import Input from 'dashboard/components-next/input/Input.vue';
import DeleteDialog from 'dashboard/components-next/captain/pageComponents/DeleteDialog.vue';
import BulkDeleteDialog from 'dashboard/components-next/captain/pageComponents/BulkDeleteDialog.vue';
import PageLayout from 'dashboard/components-next/captain/PageLayout.vue';
@@ -36,6 +38,7 @@ const bulkDeleteDialog = ref(null);
const selectedStatus = ref('all');
const selectedAssistant = ref('all');
const dialogType = ref('');
+const searchQuery = ref('');
const { t } = useI18n();
const createDialog = ref(null);
@@ -138,6 +141,9 @@ const fetchResponses = (page = 1) => {
if (selectedAssistant.value !== 'all') {
filterParams.assistantId = selectedAssistant.value;
}
+ if (searchQuery.value) {
+ filterParams.search = searchQuery.value;
+ }
store.dispatch('captainResponses/get', filterParams);
};
@@ -250,6 +256,10 @@ const handleAssistantFilterChange = assistant => {
fetchResponses();
};
+const debouncedSearch = debounce(async () => {
+ fetchResponses();
+}, 500);
+
onMounted(() => {
store.dispatch('captainAssistants/get');
fetchResponses();
@@ -292,34 +302,47 @@ onMounted(() => {
-
-
-
+
+
diff --git a/app/javascript/dashboard/routes/dashboard/conversation/contact/ContactNotes.vue b/app/javascript/dashboard/routes/dashboard/conversation/contact/ContactNotes.vue
index 66b6876b1..c828c60ef 100644
--- a/app/javascript/dashboard/routes/dashboard/conversation/contact/ContactNotes.vue
+++ b/app/javascript/dashboard/routes/dashboard/conversation/contact/ContactNotes.vue
@@ -1,21 +1,34 @@
-
-
-
-
-
{{ t('CONTACTS_LAYOUT.SIDEBAR.NOTES.NO_NOTES') }}
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ t('CONTACTS_LAYOUT.SIDEBAR.NOTES.CONVERSATION_EMPTY_STATE') }}
+
+
+
+
+
+ {{ t('CONTACTS_LAYOUT.SIDEBAR.NOTES.ADD_NOTE') }}
+
+
+
+
+
+
+
diff --git a/app/javascript/dashboard/routes/dashboard/settings/SettingsLayout.vue b/app/javascript/dashboard/routes/dashboard/settings/SettingsLayout.vue
index 44bad28c1..9e34cb384 100644
--- a/app/javascript/dashboard/routes/dashboard/settings/SettingsLayout.vue
+++ b/app/javascript/dashboard/routes/dashboard/settings/SettingsLayout.vue
@@ -23,7 +23,7 @@ defineProps({
-
+
@@ -37,6 +37,6 @@ defineProps({
-
+
diff --git a/app/javascript/dashboard/routes/dashboard/settings/account/Index.vue b/app/javascript/dashboard/routes/dashboard/settings/account/Index.vue
index 69342858d..c6e9a1ebb 100644
--- a/app/javascript/dashboard/routes/dashboard/settings/account/Index.vue
+++ b/app/javascript/dashboard/routes/dashboard/settings/account/Index.vue
@@ -7,7 +7,6 @@ import { useUISettings } from 'dashboard/composables/useUISettings';
import { useConfig } from 'dashboard/composables/useConfig';
import { useAccount } from 'dashboard/composables/useAccount';
import { FEATURE_FLAGS } from '../../../../featureFlags';
-import { getLanguageDirection } from 'dashboard/components/widgets/conversation/advancedFilterItems/languages';
import WithLabel from 'v3/components/Form/WithLabel.vue';
import NextInput from 'next/input/Input.vue';
import BaseSettingsHeader from '../components/BaseSettingsHeader.vue';
@@ -33,12 +32,12 @@ export default {
NextInput,
},
setup() {
- const { updateUISettings } = useUISettings();
+ const { updateUISettings, uiSettings } = useUISettings();
const { enabledLanguages } = useConfig();
const { accountId } = useAccount();
const v$ = useVuelidate();
- return { updateUISettings, v$, enabledLanguages, accountId };
+ return { updateUISettings, uiSettings, v$, enabledLanguages, accountId };
},
data() {
return {
@@ -112,7 +111,7 @@ export default {
const { name, locale, id, domain, support_email, features } =
this.getAccount(this.accountId);
- this.$root.$i18n.locale = locale;
+ this.$root.$i18n.locale = this.uiSettings?.locale || locale;
this.name = name;
this.locale = locale;
this.id = id;
@@ -137,21 +136,19 @@ export default {
domain: this.domain,
support_email: this.supportEmail,
});
- this.$root.$i18n.locale = this.locale;
+ // If user locale is set, update the locale with user locale
+ if (this.uiSettings?.locale) {
+ this.$root.$i18n.locale = this.uiSettings?.locale;
+ } else {
+ // If user locale is not set, update the locale with account locale
+ this.$root.$i18n.locale = this.locale;
+ }
this.getAccount(this.id).locale = this.locale;
- this.updateDirectionView(this.locale);
useAlert(this.$t('GENERAL_SETTINGS.UPDATE.SUCCESS'));
} catch (error) {
useAlert(this.$t('GENERAL_SETTINGS.UPDATE.ERROR'));
}
},
-
- updateDirectionView(locale) {
- const isRTLSupported = getLanguageDirection(locale);
- this.updateUISettings({
- rtl_view: isRTLSupported,
- });
- },
},
};
diff --git a/app/javascript/dashboard/routes/dashboard/settings/assignmentPolicy/Index.vue b/app/javascript/dashboard/routes/dashboard/settings/assignmentPolicy/Index.vue
new file mode 100644
index 000000000..ab9fcf17e
--- /dev/null
+++ b/app/javascript/dashboard/routes/dashboard/settings/assignmentPolicy/Index.vue
@@ -0,0 +1,81 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/javascript/dashboard/routes/dashboard/settings/assignmentPolicy/assignmentPolicy.routes.js b/app/javascript/dashboard/routes/dashboard/settings/assignmentPolicy/assignmentPolicy.routes.js
new file mode 100644
index 000000000..d09871674
--- /dev/null
+++ b/app/javascript/dashboard/routes/dashboard/settings/assignmentPolicy/assignmentPolicy.routes.js
@@ -0,0 +1,90 @@
+import { FEATURE_FLAGS } from '../../../../featureFlags';
+import { frontendURL } from '../../../../helper/URLHelper';
+import SettingsWrapper from '../SettingsWrapper.vue';
+import AssignmentPolicyIndex from './Index.vue';
+import AgentAssignmentIndex from './pages/AgentAssignmentIndexPage.vue';
+import AgentAssignmentCreate from './pages/AgentAssignmentCreatePage.vue';
+import AgentAssignmentEdit from './pages/AgentAssignmentEditPage.vue';
+import AgentCapacityIndex from './pages/AgentCapacityIndexPage.vue';
+import AgentCapacityCreate from './pages/AgentCapacityCreatePage.vue';
+import AgentCapacityEdit from './pages/AgentCapacityEditPage.vue';
+
+export default {
+ routes: [
+ {
+ path: frontendURL('accounts/:accountId/settings/assignment-policy'),
+ component: SettingsWrapper,
+ children: [
+ {
+ path: '',
+ redirect: to => {
+ return { name: 'assignment_policy_index', params: to.params };
+ },
+ },
+ {
+ path: 'index',
+ name: 'assignment_policy_index',
+ component: AssignmentPolicyIndex,
+ meta: {
+ featureFlag: FEATURE_FLAGS.ASSIGNMENT_V2,
+ permissions: ['administrator'],
+ },
+ },
+ {
+ path: 'assignment',
+ name: 'agent_assignment_policy_index',
+ component: AgentAssignmentIndex,
+ meta: {
+ featureFlag: FEATURE_FLAGS.ASSIGNMENT_V2,
+ permissions: ['administrator'],
+ },
+ },
+ {
+ path: 'assignment/create',
+ name: 'agent_assignment_policy_create',
+ component: AgentAssignmentCreate,
+ meta: {
+ featureFlag: FEATURE_FLAGS.ASSIGNMENT_V2,
+ permissions: ['administrator'],
+ },
+ },
+ {
+ path: 'assignment/edit/:id',
+ name: 'agent_assignment_policy_edit',
+ component: AgentAssignmentEdit,
+ meta: {
+ featureFlag: FEATURE_FLAGS.ASSIGNMENT_V2,
+ permissions: ['administrator'],
+ },
+ },
+ {
+ path: 'capacity',
+ name: 'agent_capacity_policy_index',
+ component: AgentCapacityIndex,
+ meta: {
+ featureFlag: FEATURE_FLAGS.ASSIGNMENT_V2,
+ permissions: ['administrator'],
+ },
+ },
+ {
+ path: 'capacity/create',
+ name: 'agent_capacity_policy_create',
+ component: AgentCapacityCreate,
+ meta: {
+ featureFlag: FEATURE_FLAGS.ASSIGNMENT_V2,
+ permissions: ['administrator'],
+ },
+ },
+ {
+ path: 'capacity/edit/:id',
+ name: 'agent_capacity_policy_edit',
+ component: AgentCapacityEdit,
+ meta: {
+ featureFlag: FEATURE_FLAGS.ASSIGNMENT_V2,
+ permissions: ['administrator'],
+ },
+ },
+ ],
+ },
+ ],
+};
diff --git a/app/javascript/dashboard/routes/dashboard/settings/assignmentPolicy/constants.js b/app/javascript/dashboard/routes/dashboard/settings/assignmentPolicy/constants.js
new file mode 100644
index 000000000..350faa60c
--- /dev/null
+++ b/app/javascript/dashboard/routes/dashboard/settings/assignmentPolicy/constants.js
@@ -0,0 +1,17 @@
+// Assignment order types
+export const ROUND_ROBIN = 'round_robin';
+export const BALANCED = 'balanced';
+
+// Assignment priority types
+export const EARLIEST_CREATED = 'earliest_created';
+export const LONGEST_WAITING = 'longest_waiting';
+
+// Default values for fair distribution
+export const DEFAULT_FAIR_DISTRIBUTION_LIMIT = 100;
+export const DEFAULT_FAIR_DISTRIBUTION_WINDOW = 3600;
+
+// Options groupings
+export const OPTIONS = {
+ ORDER: [ROUND_ROBIN, BALANCED],
+ PRIORITY: [EARLIEST_CREATED, LONGEST_WAITING],
+};
diff --git a/app/javascript/dashboard/routes/dashboard/settings/assignmentPolicy/pages/AgentAssignmentCreatePage.vue b/app/javascript/dashboard/routes/dashboard/settings/assignmentPolicy/pages/AgentAssignmentCreatePage.vue
new file mode 100644
index 000000000..3c68d9585
--- /dev/null
+++ b/app/javascript/dashboard/routes/dashboard/settings/assignmentPolicy/pages/AgentAssignmentCreatePage.vue
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/javascript/dashboard/routes/dashboard/settings/assignmentPolicy/pages/AgentAssignmentEditPage.vue b/app/javascript/dashboard/routes/dashboard/settings/assignmentPolicy/pages/AgentAssignmentEditPage.vue
new file mode 100644
index 000000000..c54f912d4
--- /dev/null
+++ b/app/javascript/dashboard/routes/dashboard/settings/assignmentPolicy/pages/AgentAssignmentEditPage.vue
@@ -0,0 +1,197 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/javascript/dashboard/routes/dashboard/settings/assignmentPolicy/pages/AgentAssignmentIndexPage.vue b/app/javascript/dashboard/routes/dashboard/settings/assignmentPolicy/pages/AgentAssignmentIndexPage.vue
new file mode 100644
index 000000000..be5297a16
--- /dev/null
+++ b/app/javascript/dashboard/routes/dashboard/settings/assignmentPolicy/pages/AgentAssignmentIndexPage.vue
@@ -0,0 +1,128 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/javascript/dashboard/routes/dashboard/settings/assignmentPolicy/pages/AgentCapacityCreatePage.vue b/app/javascript/dashboard/routes/dashboard/settings/assignmentPolicy/pages/AgentCapacityCreatePage.vue
new file mode 100644
index 000000000..db184b4ee
--- /dev/null
+++ b/app/javascript/dashboard/routes/dashboard/settings/assignmentPolicy/pages/AgentCapacityCreatePage.vue
@@ -0,0 +1,87 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/javascript/dashboard/routes/dashboard/settings/assignmentPolicy/pages/AgentCapacityEditPage.vue b/app/javascript/dashboard/routes/dashboard/settings/assignmentPolicy/pages/AgentCapacityEditPage.vue
new file mode 100644
index 000000000..41e277c2d
--- /dev/null
+++ b/app/javascript/dashboard/routes/dashboard/settings/assignmentPolicy/pages/AgentCapacityEditPage.vue
@@ -0,0 +1,192 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/javascript/dashboard/routes/dashboard/settings/assignmentPolicy/pages/AgentCapacityIndexPage.vue b/app/javascript/dashboard/routes/dashboard/settings/assignmentPolicy/pages/AgentCapacityIndexPage.vue
new file mode 100644
index 000000000..fb94e5fb4
--- /dev/null
+++ b/app/javascript/dashboard/routes/dashboard/settings/assignmentPolicy/pages/AgentCapacityIndexPage.vue
@@ -0,0 +1,126 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/javascript/dashboard/routes/dashboard/settings/assignmentPolicy/pages/components/AgentAssignmentPolicyForm.vue b/app/javascript/dashboard/routes/dashboard/settings/assignmentPolicy/pages/components/AgentAssignmentPolicyForm.vue
new file mode 100644
index 000000000..84a19f749
--- /dev/null
+++ b/app/javascript/dashboard/routes/dashboard/settings/assignmentPolicy/pages/components/AgentAssignmentPolicyForm.vue
@@ -0,0 +1,257 @@
+
+
+
+
+
diff --git a/app/javascript/dashboard/routes/dashboard/settings/assignmentPolicy/pages/components/AgentCapacityPolicyForm.vue b/app/javascript/dashboard/routes/dashboard/settings/assignmentPolicy/pages/components/AgentCapacityPolicyForm.vue
new file mode 100644
index 000000000..24c4f0a38
--- /dev/null
+++ b/app/javascript/dashboard/routes/dashboard/settings/assignmentPolicy/pages/components/AgentCapacityPolicyForm.vue
@@ -0,0 +1,214 @@
+
+
+
+
+
diff --git a/app/javascript/dashboard/routes/dashboard/settings/assignmentPolicy/pages/components/ConfirmDeletePolicyDialog.vue b/app/javascript/dashboard/routes/dashboard/settings/assignmentPolicy/pages/components/ConfirmDeletePolicyDialog.vue
new file mode 100644
index 000000000..2dd57f713
--- /dev/null
+++ b/app/javascript/dashboard/routes/dashboard/settings/assignmentPolicy/pages/components/ConfirmDeletePolicyDialog.vue
@@ -0,0 +1,44 @@
+
+
+
+
+
diff --git a/app/javascript/dashboard/routes/dashboard/settings/assignmentPolicy/pages/components/ConfirmInboxDialog.vue b/app/javascript/dashboard/routes/dashboard/settings/assignmentPolicy/pages/components/ConfirmInboxDialog.vue
new file mode 100644
index 000000000..af49a29af
--- /dev/null
+++ b/app/javascript/dashboard/routes/dashboard/settings/assignmentPolicy/pages/components/ConfirmInboxDialog.vue
@@ -0,0 +1,59 @@
+
+
+
+
+
diff --git a/app/javascript/dashboard/routes/dashboard/settings/automation/constants.js b/app/javascript/dashboard/routes/dashboard/settings/automation/constants.js
index 0a6905039..bc767040b 100644
--- a/app/javascript/dashboard/routes/dashboard/settings/automation/constants.js
+++ b/app/javascript/dashboard/routes/dashboard/settings/automation/constants.js
@@ -68,6 +68,12 @@ export const AUTOMATIONS = {
inputType: 'plain_text',
filterOperators: OPERATOR_TYPES_6,
},
+ {
+ key: 'labels',
+ name: 'LABELS',
+ inputType: 'multi_select',
+ filterOperators: OPERATOR_TYPES_3,
+ },
],
actions: [
{
@@ -186,6 +192,12 @@ export const AUTOMATIONS = {
inputType: 'multi_select',
filterOperators: OPERATOR_TYPES_1,
},
+ {
+ key: 'labels',
+ name: 'LABELS',
+ inputType: 'multi_select',
+ filterOperators: OPERATOR_TYPES_3,
+ },
],
actions: [
{
@@ -308,6 +320,12 @@ export const AUTOMATIONS = {
inputType: 'multi_select',
filterOperators: OPERATOR_TYPES_1,
},
+ {
+ key: 'labels',
+ name: 'LABELS',
+ inputType: 'multi_select',
+ filterOperators: OPERATOR_TYPES_3,
+ },
],
actions: [
{
@@ -424,6 +442,12 @@ export const AUTOMATIONS = {
inputType: 'multi_select',
filterOperators: OPERATOR_TYPES_1,
},
+ {
+ key: 'labels',
+ name: 'LABELS',
+ inputType: 'multi_select',
+ filterOperators: OPERATOR_TYPES_3,
+ },
],
actions: [
{
diff --git a/app/javascript/dashboard/routes/dashboard/settings/inbox/FinishSetup.vue b/app/javascript/dashboard/routes/dashboard/settings/inbox/FinishSetup.vue
index 13985784d..b19416137 100644
--- a/app/javascript/dashboard/routes/dashboard/settings/inbox/FinishSetup.vue
+++ b/app/javascript/dashboard/routes/dashboard/settings/inbox/FinishSetup.vue
@@ -34,6 +34,7 @@ const {
isAWhatsAppChannel,
isAFacebookInbox,
isATelegramChannel,
+ isATwilioWhatsAppChannel,
} = useInbox(route.params.inbox_id);
const hasDuplicateInstagramInbox = computed(() => {
@@ -168,7 +169,7 @@ onMounted(() => {
-
+
{
diff --git a/app/javascript/dashboard/routes/dashboard/settings/inbox/Settings.vue b/app/javascript/dashboard/routes/dashboard/settings/inbox/Settings.vue
index 609fd3fd4..1f455f112 100644
--- a/app/javascript/dashboard/routes/dashboard/settings/inbox/Settings.vue
+++ b/app/javascript/dashboard/routes/dashboard/settings/inbox/Settings.vue
@@ -328,7 +328,7 @@ export default {
this.continuityViaEmail = this.inbox.continuity_via_email;
this.channelWebsiteUrl = this.inbox.website_url;
this.channelWelcomeTitle = this.inbox.welcome_title;
- this.channelWelcomeTagline = this.inbox.welcome_tagline;
+ this.channelWelcomeTagline = this.inbox.welcome_tagline || '';
this.selectedFeatureFlags = this.inbox.selected_feature_flags || [];
this.replyTime = this.inbox.reply_time;
this.locktoSingleConversation = this.inbox.lock_to_single_conversation;
diff --git a/app/javascript/dashboard/routes/dashboard/settings/inbox/channels/Whatsapp.vue b/app/javascript/dashboard/routes/dashboard/settings/inbox/channels/Whatsapp.vue
index 0691c4b4f..c6c021a45 100644
--- a/app/javascript/dashboard/routes/dashboard/settings/inbox/channels/Whatsapp.vue
+++ b/app/javascript/dashboard/routes/dashboard/settings/inbox/channels/Whatsapp.vue
@@ -1,25 +1,23 @@
@@ -117,18 +94,52 @@ const shouldShowCloudWhatsapp = provider => {
diff --git a/app/javascript/dashboard/routes/dashboard/settings/inbox/channels/WhatsappEmbeddedSignup.vue b/app/javascript/dashboard/routes/dashboard/settings/inbox/channels/WhatsappEmbeddedSignup.vue
index 583ca2413..cf5c1310e 100644
--- a/app/javascript/dashboard/routes/dashboard/settings/inbox/channels/WhatsappEmbeddedSignup.vue
+++ b/app/javascript/dashboard/routes/dashboard/settings/inbox/channels/WhatsappEmbeddedSignup.vue
@@ -2,12 +2,13 @@
import { ref, computed, onMounted, onBeforeUnmount } from 'vue';
import { useStore } from 'vuex';
import { useRouter } from 'vue-router';
-import { useI18n } from 'vue-i18n';
+import { useI18n, I18nT } from 'vue-i18n';
import { useAlert } from 'dashboard/composables';
import Icon from 'next/icon/Icon.vue';
import NextButton from 'next/button/Button.vue';
import LoadingState from 'dashboard/components/widgets/LoadingState.vue';
import { parseAPIErrorResponse } from 'dashboard/store/utils/api';
+import globalConstants from 'dashboard/constants/globals.js';
import {
setupFacebookSdk,
initWhatsAppEmbeddedSignup,
@@ -28,9 +29,6 @@ const authCode = ref(null);
const businessData = ref(null);
const isAuthenticating = ref(false);
-// Computed
-const whatsappIconPath = '/assets/images/dashboard/channels/whatsapp.png';
-
const benefits = computed(() => [
{
key: 'EASY_SETUP',
@@ -235,14 +233,9 @@ onBeforeUnmount(() => {
-
![]()
+
@@ -266,22 +259,26 @@ onBeforeUnmount(() => {
diff --git a/app/javascript/dashboard/routes/dashboard/settings/inbox/helpers/timezones.json b/app/javascript/dashboard/routes/dashboard/settings/inbox/helpers/timezones.json
index b238add21..810415ffb 100644
--- a/app/javascript/dashboard/routes/dashboard/settings/inbox/helpers/timezones.json
+++ b/app/javascript/dashboard/routes/dashboard/settings/inbox/helpers/timezones.json
@@ -86,7 +86,7 @@
"Riyadh (GMT+03:00)": "Asia/Riyadh",
"Nairobi (GMT+03:00)": "Africa/Nairobi",
"Baghdad (GMT+03:00)": "Asia/Baghdad",
- "Tehran (GMT+04:30)": "Asia/Tehran",
+ "Tehran (GMT+03:30)": "Asia/Tehran",
"Abu Dhabi (GMT+04:00)": "Asia/Muscat",
"Muscat (GMT+04:00)": "Asia/Muscat",
"Baku (GMT+04:00)": "Asia/Baku",
diff --git a/app/javascript/dashboard/routes/dashboard/settings/profile/Index.vue b/app/javascript/dashboard/routes/dashboard/settings/profile/Index.vue
index bbde7a1c0..305a6d3ef 100644
--- a/app/javascript/dashboard/routes/dashboard/settings/profile/Index.vue
+++ b/app/javascript/dashboard/routes/dashboard/settings/profile/Index.vue
@@ -7,16 +7,19 @@ import { useBranding } from 'shared/composables/useBranding';
import { clearCookiesOnLogout } from 'dashboard/store/utils/api.js';
import { copyTextToClipboard } from 'shared/helpers/clipboard';
import { parseAPIErrorResponse } from 'dashboard/store/utils/api';
+import { parseBoolean } from '@chatwoot/utils';
import UserProfilePicture from './UserProfilePicture.vue';
import UserBasicDetails from './UserBasicDetails.vue';
import MessageSignature from './MessageSignature.vue';
import FontSize from './FontSize.vue';
+import UserLanguageSelect from './UserLanguageSelect.vue';
import HotKeyCard from './HotKeyCard.vue';
import ChangePassword from './ChangePassword.vue';
import NotificationPreferences from './NotificationPreferences.vue';
import AudioNotifications from './AudioNotifications.vue';
import FormSection from 'dashboard/components/FormSection.vue';
import AccessToken from './AccessToken.vue';
+import MfaSettingsCard from './MfaSettingsCard.vue';
import Policy from 'dashboard/components/policy.vue';
import {
ROLES,
@@ -28,6 +31,7 @@ export default {
MessageSignature,
FormSection,
FontSize,
+ UserLanguageSelect,
UserProfilePicture,
Policy,
UserBasicDetails,
@@ -36,6 +40,7 @@ export default {
NotificationPreferences,
AudioNotifications,
AccessToken,
+ MfaSettingsCard,
},
setup() {
const { isEditorHotKeyEnabled, updateUISettings } = useUISettings();
@@ -93,6 +98,9 @@ export default {
currentUserId: 'getCurrentUserID',
globalConfig: 'globalConfig/get',
}),
+ isMfaEnabled() {
+ return parseBoolean(window.chatwootConfig?.isMfaEnabled);
+ },
},
mounted() {
if (this.currentUserId) {
@@ -230,6 +238,12 @@ export default {
"
@change="updateFontSize"
/>
+
+
+
+
+import { ref } from 'vue';
+import { useI18n } from 'vue-i18n';
+import { copyTextToClipboard } from 'shared/helpers/clipboard';
+import { useAlert } from 'dashboard/composables';
+import Button from 'dashboard/components-next/button/Button.vue';
+import Input from 'dashboard/components-next/input/Input.vue';
+import Icon from 'dashboard/components-next/icon/Icon.vue';
+import Dialog from 'dashboard/components-next/dialog/Dialog.vue';
+
+const props = defineProps({
+ mfaEnabled: {
+ type: Boolean,
+ required: true,
+ },
+ backupCodes: {
+ type: Array,
+ default: () => [],
+ },
+});
+
+const emit = defineEmits(['disableMfa', 'regenerateBackupCodes']);
+
+const { t } = useI18n();
+
+// Dialog refs
+const disableDialogRef = ref(null);
+const regenerateDialogRef = ref(null);
+const backupCodesDialogRef = ref(null);
+
+// Form values
+const disablePassword = ref('');
+const disableOtpCode = ref('');
+const regenerateOtpCode = ref('');
+
+// Utility functions
+const copyBackupCodes = async () => {
+ const codesText = props.backupCodes.join('\n');
+ await copyTextToClipboard(codesText);
+ useAlert(t('MFA_SETTINGS.BACKUP.CODES_COPIED'));
+};
+
+const downloadBackupCodes = () => {
+ const codesText = `Chatwoot Two-Factor Authentication Backup Codes\n\n${props.backupCodes.join('\n')}\n\nKeep these codes in a safe place.`;
+ const blob = new Blob([codesText], { type: 'text/plain' });
+ const url = URL.createObjectURL(blob);
+ const a = document.createElement('a');
+ a.href = url;
+ a.download = 'chatwoot-backup-codes.txt';
+ a.click();
+ URL.revokeObjectURL(url);
+};
+
+const handleDisableMfa = async () => {
+ emit('disableMfa', {
+ password: disablePassword.value,
+ otpCode: disableOtpCode.value,
+ });
+};
+
+const handleRegenerateBackupCodes = async () => {
+ emit('regenerateBackupCodes', {
+ otpCode: regenerateOtpCode.value,
+ });
+};
+
+// Methods exposed for parent component
+const resetDisableForm = () => {
+ disablePassword.value = '';
+ disableOtpCode.value = '';
+ disableDialogRef.value?.close();
+};
+
+const resetRegenerateForm = () => {
+ regenerateOtpCode.value = '';
+ regenerateDialogRef.value?.close();
+};
+
+const showBackupCodesDialog = () => {
+ backupCodesDialogRef.value?.open();
+};
+
+defineExpose({
+ resetDisableForm,
+ resetRegenerateForm,
+ showBackupCodesDialog,
+});
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t('MFA_SETTINGS.MANAGEMENT.BACKUP_CODES') }}
+
+
+
+ {{ $t('MFA_SETTINGS.MANAGEMENT.BACKUP_CODES_DESC') }}
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t('MFA_SETTINGS.MANAGEMENT.DISABLE_MFA') }}
+
+
+
+ {{ $t('MFA_SETTINGS.MANAGEMENT.DISABLE_MFA_DESC') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/javascript/dashboard/routes/dashboard/settings/profile/MfaSettings.vue b/app/javascript/dashboard/routes/dashboard/settings/profile/MfaSettings.vue
new file mode 100644
index 000000000..a647bd0da
--- /dev/null
+++ b/app/javascript/dashboard/routes/dashboard/settings/profile/MfaSettings.vue
@@ -0,0 +1,178 @@
+
+
+
+
+
+
+
+ {{ $t('MFA_SETTINGS.TITLE') }}
+
+
+ {{ $t('MFA_SETTINGS.SUBTITLE') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/javascript/dashboard/routes/dashboard/settings/profile/MfaSettingsCard.vue b/app/javascript/dashboard/routes/dashboard/settings/profile/MfaSettingsCard.vue
new file mode 100644
index 000000000..673f97143
--- /dev/null
+++ b/app/javascript/dashboard/routes/dashboard/settings/profile/MfaSettingsCard.vue
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+ {{ $t('MFA_SETTINGS.TITLE') }}
+
+
+
+ {{ $t('MFA_SETTINGS.DESCRIPTION') }}
+
+
+
+
+
+
diff --git a/app/javascript/dashboard/routes/dashboard/settings/profile/MfaSetupWizard.vue b/app/javascript/dashboard/routes/dashboard/settings/profile/MfaSetupWizard.vue
new file mode 100644
index 000000000..528df7658
--- /dev/null
+++ b/app/javascript/dashboard/routes/dashboard/settings/profile/MfaSetupWizard.vue
@@ -0,0 +1,323 @@
+
+
+
+
+
+
+
+
+
+ {{ $t('MFA_SETTINGS.SETUP.STEP1_TITLE') }}
+
+
+ {{ $t('MFA_SETTINGS.SETUP.STEP1_DESCRIPTION') }}
+
+
+
+
+
![MFA QR Code]()
+
+
+ {{ $t('MFA_SETTINGS.SETUP.LOADING_QR') }}
+
+
+
+
+
+
+
+ {{ $t('MFA_SETTINGS.SETUP.MANUAL_ENTRY') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t('MFA_SETTINGS.BACKUP.TITLE') }}
+
+
+ {{ $t('MFA_SETTINGS.BACKUP.DESCRIPTION') }}
+
+
+
+
+
+
+
+ {{ $t('MFA_SETTINGS.BACKUP.IMPORTANT') }}
+ {{ $t('MFA_SETTINGS.BACKUP.IMPORTANT_NOTE') }}
+
+
+
+
+
+
+
+ {{ code }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/javascript/dashboard/routes/dashboard/settings/profile/MfaStatusCard.vue b/app/javascript/dashboard/routes/dashboard/settings/profile/MfaStatusCard.vue
new file mode 100644
index 000000000..3dc22eb4a
--- /dev/null
+++ b/app/javascript/dashboard/routes/dashboard/settings/profile/MfaStatusCard.vue
@@ -0,0 +1,63 @@
+
+
+
+
+
+
+
+ {{ $t('MFA_SETTINGS.ENHANCE_SECURITY') }}
+
+
+ {{ $t('MFA_SETTINGS.ENHANCE_SECURITY_DESC') }}
+
+
+
+
+
+
+
+
+
+ {{ $t('MFA_SETTINGS.STATUS_ENABLED') }}
+
+
+
+ {{ $t('MFA_SETTINGS.STATUS_ENABLED_DESC') }}
+
+
+
+
diff --git a/app/javascript/dashboard/routes/dashboard/settings/profile/UserLanguageSelect.vue b/app/javascript/dashboard/routes/dashboard/settings/profile/UserLanguageSelect.vue
new file mode 100644
index 000000000..e9e72b518
--- /dev/null
+++ b/app/javascript/dashboard/routes/dashboard/settings/profile/UserLanguageSelect.vue
@@ -0,0 +1,103 @@
+
+
+
+
+
+
+
+ {{ description }}
+
+
+
+
+
+
+
diff --git a/app/javascript/dashboard/routes/dashboard/settings/profile/profile.routes.js b/app/javascript/dashboard/routes/dashboard/settings/profile/profile.routes.js
index b99031047..ef208eae4 100644
--- a/app/javascript/dashboard/routes/dashboard/settings/profile/profile.routes.js
+++ b/app/javascript/dashboard/routes/dashboard/settings/profile/profile.routes.js
@@ -1,7 +1,9 @@
import { frontendURL } from '../../../../helper/URLHelper';
+import { parseBoolean } from '@chatwoot/utils';
import SettingsContent from './Wrapper.vue';
import Index from './Index.vue';
+import MfaSettings from './MfaSettings.vue';
export default {
routes: [
@@ -21,6 +23,23 @@ export default {
permissions: ['administrator', 'agent', 'custom_role'],
},
},
+ {
+ path: 'mfa',
+ name: 'profile_settings_mfa',
+ component: MfaSettings,
+ meta: {
+ permissions: ['administrator', 'agent', 'custom_role'],
+ },
+ beforeEnter: (to, from, next) => {
+ // Check if MFA is enabled globally
+ if (!parseBoolean(window.chatwootConfig?.isMfaEnabled)) {
+ // Redirect to profile settings if MFA is disabled
+ next({ name: 'profile_settings_index' });
+ } else {
+ next();
+ }
+ },
+ },
],
},
],
diff --git a/app/javascript/dashboard/routes/dashboard/settings/security/Index.vue b/app/javascript/dashboard/routes/dashboard/settings/security/Index.vue
new file mode 100644
index 000000000..0ac35c9e2
--- /dev/null
+++ b/app/javascript/dashboard/routes/dashboard/settings/security/Index.vue
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/javascript/dashboard/routes/dashboard/settings/security/components/SamlAttributeMap.vue b/app/javascript/dashboard/routes/dashboard/settings/security/components/SamlAttributeMap.vue
new file mode 100644
index 000000000..442b426df
--- /dev/null
+++ b/app/javascript/dashboard/routes/dashboard/settings/security/components/SamlAttributeMap.vue
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
+
+ {{ t('SECURITY_SETTINGS.SAML.ATTRIBUTE_MAPPING.DESCRIPTION') }}
+
+
+
+ email
+ first_name
+ last_name
+
+
+
+
+
diff --git a/app/javascript/dashboard/routes/dashboard/settings/security/components/SamlInfoSection.vue b/app/javascript/dashboard/routes/dashboard/settings/security/components/SamlInfoSection.vue
new file mode 100644
index 000000000..66820cafa
--- /dev/null
+++ b/app/javascript/dashboard/routes/dashboard/settings/security/components/SamlInfoSection.vue
@@ -0,0 +1,102 @@
+
+
+
+
+
+
+ {{ t('SECURITY_SETTINGS.SAML.INFO_SECTION.TITLE') }}
+
+
+
+
+
+
+
+ {{ item.label }}
+
+
+ {{ item.value }}
+
+
+
+
+
+
diff --git a/app/javascript/dashboard/routes/dashboard/settings/security/components/SamlPaywall.vue b/app/javascript/dashboard/routes/dashboard/settings/security/components/SamlPaywall.vue
new file mode 100644
index 000000000..a08cce9e0
--- /dev/null
+++ b/app/javascript/dashboard/routes/dashboard/settings/security/components/SamlPaywall.vue
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
diff --git a/app/javascript/dashboard/routes/dashboard/settings/security/components/SamlSettings.vue b/app/javascript/dashboard/routes/dashboard/settings/security/components/SamlSettings.vue
new file mode 100644
index 000000000..dca12200b
--- /dev/null
+++ b/app/javascript/dashboard/routes/dashboard/settings/security/components/SamlSettings.vue
@@ -0,0 +1,251 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/javascript/dashboard/routes/dashboard/settings/security/security.routes.js b/app/javascript/dashboard/routes/dashboard/settings/security/security.routes.js
new file mode 100644
index 000000000..f9058d6bc
--- /dev/null
+++ b/app/javascript/dashboard/routes/dashboard/settings/security/security.routes.js
@@ -0,0 +1,41 @@
+import { frontendURL } from '../../../../helper/URLHelper';
+import { INSTALLATION_TYPES } from 'dashboard/constants/installationTypes';
+import { FEATURE_FLAGS } from 'dashboard/featureFlags';
+import SettingsWrapper from '../SettingsWrapper.vue';
+import Index from './Index.vue';
+
+export default {
+ routes: [
+ {
+ path: frontendURL('accounts/:accountId/settings/security'),
+ meta: {
+ permissions: ['administrator'],
+ installationTypes: [
+ INSTALLATION_TYPES.CLOUD,
+ INSTALLATION_TYPES.ENTERPRISE,
+ ],
+ },
+ component: SettingsWrapper,
+ props: {
+ headerTitle: 'SECURITY_SETTINGS.TITLE',
+ icon: 'i-lucide-shield',
+ showNewButton: false,
+ },
+ children: [
+ {
+ path: '',
+ name: 'security_settings_index',
+ component: Index,
+ meta: {
+ permissions: ['administrator'],
+ featureFlag: FEATURE_FLAGS.SAML,
+ installationTypes: [
+ INSTALLATION_TYPES.CLOUD,
+ INSTALLATION_TYPES.ENTERPRISE,
+ ],
+ },
+ },
+ ],
+ },
+ ],
+};
diff --git a/app/javascript/dashboard/routes/dashboard/settings/settings.routes.js b/app/javascript/dashboard/routes/dashboard/settings/settings.routes.js
index ef39688a5..22173f66d 100644
--- a/app/javascript/dashboard/routes/dashboard/settings/settings.routes.js
+++ b/app/javascript/dashboard/routes/dashboard/settings/settings.routes.js
@@ -6,6 +6,7 @@ import {
import account from './account/account.routes';
import agent from './agents/agent.routes';
+import assignmentPolicy from './assignmentPolicy/assignmentPolicy.routes';
import agentBot from './agentBots/agentBot.routes';
import attributes from './attributes/attributes.routes';
import automation from './automation/automation.routes';
@@ -22,6 +23,7 @@ import sla from './sla/sla.routes';
import teams from './teams/teams.routes';
import customRoles from './customRoles/customRole.routes';
import profile from './profile/profile.routes';
+import security from './security/security.routes';
export default {
routes: [
@@ -44,6 +46,7 @@ export default {
},
...account.routes,
...agent.routes,
+ ...assignmentPolicy.routes,
...agentBot.routes,
...attributes.routes,
...automation.routes,
@@ -59,5 +62,6 @@ export default {
...teams.routes,
...customRoles.routes,
...profile.routes,
+ ...security.routes,
],
};
diff --git a/app/javascript/dashboard/store/index.js b/app/javascript/dashboard/store/index.js
index 5a020dda6..16bcab3f9 100755
--- a/app/javascript/dashboard/store/index.js
+++ b/app/javascript/dashboard/store/index.js
@@ -2,7 +2,9 @@ import { createStore } from 'vuex';
import accounts from './modules/accounts';
import agentBots from './modules/agentBots';
+import agentCapacityPolicies from './modules/agentCapacityPolicies';
import agents from './modules/agents';
+import assignmentPolicies from './modules/assignmentPolicies';
import articles from './modules/helpCenterArticles';
import attributes from './modules/attributes';
import auditlogs from './modules/auditlogs';
@@ -62,7 +64,9 @@ export default createStore({
modules: {
accounts,
agentBots,
+ agentCapacityPolicies,
agents,
+ assignmentPolicies,
articles,
attributes,
auditlogs,
diff --git a/app/javascript/dashboard/store/modules/accounts.js b/app/javascript/dashboard/store/modules/accounts.js
index 0d5fdc748..1eef59cfe 100644
--- a/app/javascript/dashboard/store/modules/accounts.js
+++ b/app/javascript/dashboard/store/modules/accounts.js
@@ -28,12 +28,16 @@ export const getters = {
getUIFlags($state) {
return $state.uiFlags;
},
- isRTL: ($state, _, rootState) => {
- const accountId = rootState.route?.params?.accountId;
- if (!accountId) return false;
+ isRTL: ($state, _getters, rootState, rootGetters) => {
+ const accountId = Number(rootState.route?.params?.accountId);
+ const userLocale = rootGetters?.getUISettings?.locale;
+ const accountLocale =
+ accountId && findRecordById($state, accountId)?.locale;
- const { locale } = findRecordById($state, Number(accountId));
- return locale ? getLanguageDirection(locale) : false;
+ // Prefer user locale; fallback to account locale
+ const effectiveLocale = userLocale ?? accountLocale;
+
+ return effectiveLocale ? getLanguageDirection(effectiveLocale) : false;
},
isTrialAccount: $state => id => {
const account = findRecordById($state, id);
diff --git a/app/javascript/dashboard/store/modules/agentCapacityPolicies.js b/app/javascript/dashboard/store/modules/agentCapacityPolicies.js
new file mode 100644
index 000000000..ea554448f
--- /dev/null
+++ b/app/javascript/dashboard/store/modules/agentCapacityPolicies.js
@@ -0,0 +1,316 @@
+import * as MutationHelpers from 'shared/helpers/vuex/mutationHelpers';
+import types from '../mutation-types';
+import AgentCapacityPoliciesAPI from '../../api/agentCapacityPolicies';
+import { throwErrorMessage } from '../utils/api';
+import camelcaseKeys from 'camelcase-keys';
+import snakecaseKeys from 'snakecase-keys';
+
+export const state = {
+ records: [],
+ uiFlags: {
+ isFetching: false,
+ isFetchingItem: false,
+ isCreating: false,
+ isUpdating: false,
+ isDeleting: false,
+ },
+ usersUiFlags: {
+ isFetching: false,
+ isDeleting: false,
+ },
+};
+
+export const getters = {
+ getAgentCapacityPolicies(_state) {
+ return _state.records;
+ },
+ getUIFlags(_state) {
+ return _state.uiFlags;
+ },
+ getUsersUIFlags(_state) {
+ return _state.usersUiFlags;
+ },
+ getAgentCapacityPolicyById: _state => id => {
+ return _state.records.find(record => record.id === Number(id)) || {};
+ },
+};
+
+export const actions = {
+ get: async function get({ commit }) {
+ commit(types.SET_AGENT_CAPACITY_POLICIES_UI_FLAG, { isFetching: true });
+ try {
+ const response = await AgentCapacityPoliciesAPI.get();
+ commit(
+ types.SET_AGENT_CAPACITY_POLICIES,
+ camelcaseKeys(response.data, { deep: true })
+ );
+ } catch (error) {
+ throwErrorMessage(error);
+ } finally {
+ commit(types.SET_AGENT_CAPACITY_POLICIES_UI_FLAG, { isFetching: false });
+ }
+ },
+
+ show: async function show({ commit }, policyId) {
+ commit(types.SET_AGENT_CAPACITY_POLICIES_UI_FLAG, { isFetchingItem: true });
+ try {
+ const response = await AgentCapacityPoliciesAPI.show(policyId);
+ const policy = camelcaseKeys(response.data, { deep: true });
+ commit(types.SET_AGENT_CAPACITY_POLICY, policy);
+ } catch (error) {
+ throwErrorMessage(error);
+ } finally {
+ commit(types.SET_AGENT_CAPACITY_POLICIES_UI_FLAG, {
+ isFetchingItem: false,
+ });
+ }
+ },
+
+ create: async function create({ commit }, policyObj) {
+ commit(types.SET_AGENT_CAPACITY_POLICIES_UI_FLAG, { isCreating: true });
+ try {
+ const response = await AgentCapacityPoliciesAPI.create(
+ snakecaseKeys(policyObj)
+ );
+ commit(
+ types.ADD_AGENT_CAPACITY_POLICY,
+ camelcaseKeys(response.data, { deep: true })
+ );
+ return response.data;
+ } catch (error) {
+ throwErrorMessage(error);
+ throw error;
+ } finally {
+ commit(types.SET_AGENT_CAPACITY_POLICIES_UI_FLAG, { isCreating: false });
+ }
+ },
+
+ update: async function update({ commit }, { id, ...policyParams }) {
+ commit(types.SET_AGENT_CAPACITY_POLICIES_UI_FLAG, { isUpdating: true });
+ try {
+ const response = await AgentCapacityPoliciesAPI.update(
+ id,
+ snakecaseKeys(policyParams)
+ );
+ commit(
+ types.EDIT_AGENT_CAPACITY_POLICY,
+ camelcaseKeys(response.data, { deep: true })
+ );
+ return response.data;
+ } catch (error) {
+ throwErrorMessage(error);
+ throw error;
+ } finally {
+ commit(types.SET_AGENT_CAPACITY_POLICIES_UI_FLAG, { isUpdating: false });
+ }
+ },
+
+ delete: async function deletePolicy({ commit }, policyId) {
+ commit(types.SET_AGENT_CAPACITY_POLICIES_UI_FLAG, { isDeleting: true });
+ try {
+ await AgentCapacityPoliciesAPI.delete(policyId);
+ commit(types.DELETE_AGENT_CAPACITY_POLICY, policyId);
+ } catch (error) {
+ throwErrorMessage(error);
+ throw error;
+ } finally {
+ commit(types.SET_AGENT_CAPACITY_POLICIES_UI_FLAG, { isDeleting: false });
+ }
+ },
+
+ getUsers: async function getUsers({ commit }, policyId) {
+ commit(types.SET_AGENT_CAPACITY_POLICIES_USERS_UI_FLAG, {
+ isFetching: true,
+ });
+ try {
+ const response = await AgentCapacityPoliciesAPI.getUsers(policyId);
+ commit(types.SET_AGENT_CAPACITY_POLICIES_USERS, {
+ policyId,
+ users: camelcaseKeys(response.data),
+ });
+ return response.data;
+ } catch (error) {
+ throwErrorMessage(error);
+ throw error;
+ } finally {
+ commit(types.SET_AGENT_CAPACITY_POLICIES_USERS_UI_FLAG, {
+ isFetching: false,
+ });
+ }
+ },
+
+ addUser: async function addUser({ commit }, { policyId, userData }) {
+ try {
+ const response = await AgentCapacityPoliciesAPI.addUser(
+ policyId,
+ userData
+ );
+ commit(types.ADD_AGENT_CAPACITY_POLICIES_USERS, {
+ policyId,
+ user: camelcaseKeys(response.data),
+ });
+ return response.data;
+ } catch (error) {
+ throwErrorMessage(error);
+ throw error;
+ }
+ },
+
+ removeUser: async function removeUser({ commit }, { policyId, userId }) {
+ commit(types.SET_AGENT_CAPACITY_POLICIES_USERS_UI_FLAG, {
+ isDeleting: true,
+ });
+ try {
+ await AgentCapacityPoliciesAPI.removeUser(policyId, userId);
+ commit(types.DELETE_AGENT_CAPACITY_POLICIES_USERS, { policyId, userId });
+ } catch (error) {
+ throwErrorMessage(error);
+ throw error;
+ } finally {
+ commit(types.SET_AGENT_CAPACITY_POLICIES_USERS_UI_FLAG, {
+ isDeleting: false,
+ });
+ }
+ },
+
+ createInboxLimit: async function createInboxLimit(
+ { commit },
+ { policyId, limitData }
+ ) {
+ try {
+ const response = await AgentCapacityPoliciesAPI.createInboxLimit(
+ policyId,
+ limitData
+ );
+ commit(
+ types.SET_AGENT_CAPACITY_POLICIES_INBOXES,
+ camelcaseKeys(response.data)
+ );
+ return response.data;
+ } catch (error) {
+ throwErrorMessage(error);
+ throw error;
+ }
+ },
+
+ updateInboxLimit: async function updateInboxLimit(
+ { commit },
+ { policyId, limitId, limitData }
+ ) {
+ try {
+ const response = await AgentCapacityPoliciesAPI.updateInboxLimit(
+ policyId,
+ limitId,
+ limitData
+ );
+ commit(
+ types.EDIT_AGENT_CAPACITY_POLICIES_INBOXES,
+ camelcaseKeys(response.data)
+ );
+ return response.data;
+ } catch (error) {
+ throwErrorMessage(error);
+ throw error;
+ }
+ },
+
+ deleteInboxLimit: async function deleteInboxLimit(
+ { commit },
+ { policyId, limitId }
+ ) {
+ try {
+ await AgentCapacityPoliciesAPI.deleteInboxLimit(policyId, limitId);
+ commit(types.DELETE_AGENT_CAPACITY_POLICIES_INBOXES, {
+ policyId,
+ limitId,
+ });
+ } catch (error) {
+ throwErrorMessage(error);
+ throw error;
+ }
+ },
+};
+
+export const mutations = {
+ [types.SET_AGENT_CAPACITY_POLICIES_UI_FLAG](_state, data) {
+ _state.uiFlags = {
+ ..._state.uiFlags,
+ ...data,
+ };
+ },
+
+ [types.SET_AGENT_CAPACITY_POLICIES]: MutationHelpers.set,
+ [types.SET_AGENT_CAPACITY_POLICY]: MutationHelpers.setSingleRecord,
+ [types.ADD_AGENT_CAPACITY_POLICY]: MutationHelpers.create,
+ [types.EDIT_AGENT_CAPACITY_POLICY]: MutationHelpers.updateAttributes,
+ [types.DELETE_AGENT_CAPACITY_POLICY]: MutationHelpers.destroy,
+
+ [types.SET_AGENT_CAPACITY_POLICIES_USERS_UI_FLAG](_state, data) {
+ _state.usersUiFlags = {
+ ..._state.usersUiFlags,
+ ...data,
+ };
+ },
+ [types.SET_AGENT_CAPACITY_POLICIES_USERS](_state, { policyId, users }) {
+ const policy = _state.records.find(p => p.id === policyId);
+ if (policy) {
+ policy.users = users;
+ }
+ },
+ [types.ADD_AGENT_CAPACITY_POLICIES_USERS](_state, { policyId, user }) {
+ const policy = _state.records.find(p => p.id === policyId);
+ if (policy) {
+ policy.users = policy.users || [];
+ policy.users.push(user);
+ policy.assignedAgentCount = policy.users.length;
+ }
+ },
+ [types.DELETE_AGENT_CAPACITY_POLICIES_USERS](_state, { policyId, userId }) {
+ const policy = _state.records.find(p => p.id === policyId);
+ if (policy) {
+ policy.users = (policy.users || []).filter(user => user.id !== userId);
+ policy.assignedAgentCount = policy.users.length;
+ }
+ },
+
+ [types.SET_AGENT_CAPACITY_POLICIES_INBOXES](_state, data) {
+ const policy = _state.records.find(
+ p => p.id === data.agentCapacityPolicyId
+ );
+ policy?.inboxCapacityLimits.push({
+ id: data.id,
+ inboxId: data.inboxId,
+ conversationLimit: data.conversationLimit,
+ });
+ },
+ [types.EDIT_AGENT_CAPACITY_POLICIES_INBOXES](_state, data) {
+ const policy = _state.records.find(
+ p => p.id === data.agentCapacityPolicyId
+ );
+ const limit = policy?.inboxCapacityLimits.find(l => l.id === data.id);
+ if (limit) {
+ Object.assign(limit, {
+ conversationLimit: data.conversationLimit,
+ });
+ }
+ },
+ [types.DELETE_AGENT_CAPACITY_POLICIES_INBOXES](
+ _state,
+ { policyId, limitId }
+ ) {
+ const policy = _state.records.find(p => p.id === policyId);
+ if (policy) {
+ policy.inboxCapacityLimits = policy.inboxCapacityLimits.filter(
+ limit => limit.id !== limitId
+ );
+ }
+ },
+};
+
+export default {
+ namespaced: true,
+ state,
+ getters,
+ actions,
+ mutations,
+};
diff --git a/app/javascript/dashboard/store/modules/assignmentPolicies.js b/app/javascript/dashboard/store/modules/assignmentPolicies.js
new file mode 100644
index 000000000..9ea2f49c3
--- /dev/null
+++ b/app/javascript/dashboard/store/modules/assignmentPolicies.js
@@ -0,0 +1,232 @@
+import * as MutationHelpers from 'shared/helpers/vuex/mutationHelpers';
+import types from '../mutation-types';
+import AssignmentPoliciesAPI from '../../api/assignmentPolicies';
+import { throwErrorMessage } from '../utils/api';
+import camelcaseKeys from 'camelcase-keys';
+import snakecaseKeys from 'snakecase-keys';
+
+export const state = {
+ records: [],
+ uiFlags: {
+ isFetching: false,
+ isFetchingItem: false,
+ isCreating: false,
+ isUpdating: false,
+ isDeleting: false,
+ },
+ inboxUiFlags: {
+ isFetching: false,
+ isDeleting: false,
+ },
+};
+
+export const getters = {
+ getAssignmentPolicies(_state) {
+ return _state.records;
+ },
+ getUIFlags(_state) {
+ return _state.uiFlags;
+ },
+ getInboxUiFlags(_state) {
+ return _state.inboxUiFlags;
+ },
+ getAssignmentPolicyById: _state => id => {
+ return _state.records.find(record => record.id === Number(id)) || {};
+ },
+};
+
+export const actions = {
+ get: async function get({ commit }) {
+ commit(types.SET_ASSIGNMENT_POLICIES_UI_FLAG, { isFetching: true });
+ try {
+ const response = await AssignmentPoliciesAPI.get();
+ commit(types.SET_ASSIGNMENT_POLICIES, camelcaseKeys(response.data));
+ } catch (error) {
+ throwErrorMessage(error);
+ } finally {
+ commit(types.SET_ASSIGNMENT_POLICIES_UI_FLAG, { isFetching: false });
+ }
+ },
+
+ show: async function show({ commit }, policyId) {
+ commit(types.SET_ASSIGNMENT_POLICIES_UI_FLAG, { isFetchingItem: true });
+ try {
+ const response = await AssignmentPoliciesAPI.show(policyId);
+ const policy = camelcaseKeys(response.data);
+ commit(types.SET_ASSIGNMENT_POLICY, policy);
+ } catch (error) {
+ throwErrorMessage(error);
+ } finally {
+ commit(types.SET_ASSIGNMENT_POLICIES_UI_FLAG, { isFetchingItem: false });
+ }
+ },
+
+ create: async function create({ commit }, policyObj) {
+ commit(types.SET_ASSIGNMENT_POLICIES_UI_FLAG, { isCreating: true });
+ try {
+ const response = await AssignmentPoliciesAPI.create(
+ snakecaseKeys(policyObj)
+ );
+ commit(types.ADD_ASSIGNMENT_POLICY, camelcaseKeys(response.data));
+ return response.data;
+ } catch (error) {
+ throwErrorMessage(error);
+ throw error;
+ } finally {
+ commit(types.SET_ASSIGNMENT_POLICIES_UI_FLAG, { isCreating: false });
+ }
+ },
+
+ update: async function update({ commit }, { id, ...policyParams }) {
+ commit(types.SET_ASSIGNMENT_POLICIES_UI_FLAG, { isUpdating: true });
+ try {
+ const response = await AssignmentPoliciesAPI.update(
+ id,
+ snakecaseKeys(policyParams)
+ );
+ commit(types.EDIT_ASSIGNMENT_POLICY, camelcaseKeys(response.data));
+ return response.data;
+ } catch (error) {
+ throwErrorMessage(error);
+ throw error;
+ } finally {
+ commit(types.SET_ASSIGNMENT_POLICIES_UI_FLAG, { isUpdating: false });
+ }
+ },
+
+ delete: async function deletePolicy({ commit }, policyId) {
+ commit(types.SET_ASSIGNMENT_POLICIES_UI_FLAG, { isDeleting: true });
+ try {
+ await AssignmentPoliciesAPI.delete(policyId);
+ commit(types.DELETE_ASSIGNMENT_POLICY, policyId);
+ } catch (error) {
+ throwErrorMessage(error);
+ throw error;
+ } finally {
+ commit(types.SET_ASSIGNMENT_POLICIES_UI_FLAG, { isDeleting: false });
+ }
+ },
+
+ getInboxes: async function getInboxes({ commit }, policyId) {
+ commit(types.SET_ASSIGNMENT_POLICIES_INBOXES_UI_FLAG, { isFetching: true });
+ try {
+ const response = await AssignmentPoliciesAPI.getInboxes(policyId);
+ commit(types.SET_ASSIGNMENT_POLICIES_INBOXES, {
+ policyId,
+ inboxes: camelcaseKeys(response.data.inboxes),
+ });
+ } catch (error) {
+ throwErrorMessage(error);
+ throw error;
+ } finally {
+ commit(types.SET_ASSIGNMENT_POLICIES_INBOXES_UI_FLAG, {
+ isFetching: false,
+ });
+ }
+ },
+
+ setInboxPolicy: async function setInboxPolicy(
+ { commit },
+ { inboxId, policyId }
+ ) {
+ try {
+ const response = await AssignmentPoliciesAPI.setInboxPolicy(
+ inboxId,
+ policyId
+ );
+ commit(
+ types.ADD_ASSIGNMENT_POLICIES_INBOXES,
+ camelcaseKeys(response.data)
+ );
+ return response.data;
+ } catch (error) {
+ throwErrorMessage(error);
+ throw error;
+ }
+ },
+
+ getInboxPolicy: async function getInboxPolicy(_, { inboxId }) {
+ try {
+ const response = await AssignmentPoliciesAPI.getInboxPolicy(inboxId);
+ return camelcaseKeys(response.data);
+ } catch (error) {
+ throwErrorMessage(error);
+ throw error;
+ }
+ },
+
+ updateInboxPolicy: async function updateInboxPolicy({ commit }, { policy }) {
+ try {
+ commit(types.EDIT_ASSIGNMENT_POLICY, policy);
+ } catch (error) {
+ throwErrorMessage(error);
+ throw error;
+ }
+ },
+
+ removeInboxPolicy: async function removeInboxPolicy(
+ { commit },
+ { policyId, inboxId }
+ ) {
+ commit(types.SET_ASSIGNMENT_POLICIES_INBOXES_UI_FLAG, {
+ isDeleting: true,
+ });
+ try {
+ await AssignmentPoliciesAPI.removeInboxPolicy(inboxId);
+ commit(types.DELETE_ASSIGNMENT_POLICIES_INBOXES, {
+ policyId,
+ inboxId,
+ });
+ } catch (error) {
+ throwErrorMessage(error);
+ throw error;
+ } finally {
+ commit(types.SET_ASSIGNMENT_POLICIES_INBOXES_UI_FLAG, {
+ isDeleting: false,
+ });
+ }
+ },
+};
+
+export const mutations = {
+ [types.SET_ASSIGNMENT_POLICIES_UI_FLAG](_state, data) {
+ _state.uiFlags = {
+ ..._state.uiFlags,
+ ...data,
+ };
+ },
+
+ [types.SET_ASSIGNMENT_POLICIES]: MutationHelpers.set,
+ [types.SET_ASSIGNMENT_POLICY]: MutationHelpers.setSingleRecord,
+ [types.ADD_ASSIGNMENT_POLICY]: MutationHelpers.create,
+ [types.EDIT_ASSIGNMENT_POLICY]: MutationHelpers.updateAttributes,
+ [types.DELETE_ASSIGNMENT_POLICY]: MutationHelpers.destroy,
+
+ [types.SET_ASSIGNMENT_POLICIES_INBOXES_UI_FLAG](_state, data) {
+ _state.inboxUiFlags = {
+ ..._state.inboxUiFlags,
+ ...data,
+ };
+ },
+ [types.SET_ASSIGNMENT_POLICIES_INBOXES](_state, { policyId, inboxes }) {
+ const policy = _state.records.find(p => p.id === policyId);
+ if (policy) {
+ policy.inboxes = inboxes;
+ }
+ },
+ [types.DELETE_ASSIGNMENT_POLICIES_INBOXES](_state, { policyId, inboxId }) {
+ const policy = _state.records.find(p => p.id === policyId);
+ if (policy) {
+ policy.inboxes = policy?.inboxes?.filter(inbox => inbox.id !== inboxId);
+ }
+ },
+ [types.ADD_ASSIGNMENT_POLICIES_INBOXES]: MutationHelpers.updateAttributes,
+};
+
+export default {
+ namespaced: true,
+ state,
+ getters,
+ actions,
+ mutations,
+};
diff --git a/app/javascript/dashboard/store/modules/conversationStats.js b/app/javascript/dashboard/store/modules/conversationStats.js
index bae365f30..917781bbf 100644
--- a/app/javascript/dashboard/store/modules/conversationStats.js
+++ b/app/javascript/dashboard/store/modules/conversationStats.js
@@ -27,10 +27,18 @@ const fetchMetaData = async (commit, params) => {
const debouncedFetchMetaData = debounce(fetchMetaData, 500, false, 1000);
const longDebouncedFetchMetaData = debounce(fetchMetaData, 500, false, 5000);
+const superLongDebouncedFetchMetaData = debounce(
+ fetchMetaData,
+ 1500,
+ false,
+ 10000
+);
export const actions = {
get: async ({ commit, state: $state }, params) => {
- if ($state.allCount > 100) {
+ if ($state.allCount > 10000) {
+ superLongDebouncedFetchMetaData(commit, params);
+ } else if ($state.allCount > 100) {
longDebouncedFetchMetaData(commit, params);
} else {
debouncedFetchMetaData(commit, params);
diff --git a/app/javascript/dashboard/store/modules/conversations/helpers/filterHelpers.js b/app/javascript/dashboard/store/modules/conversations/helpers/filterHelpers.js
index 3f1c32059..3d627e3ef 100644
--- a/app/javascript/dashboard/store/modules/conversations/helpers/filterHelpers.js
+++ b/app/javascript/dashboard/store/modules/conversations/helpers/filterHelpers.js
@@ -64,11 +64,13 @@ const getValueFromConversation = (conversation, attributeKey) => {
switch (attributeKey) {
case 'status':
case 'priority':
- case 'display_id':
case 'labels':
case 'created_at':
case 'last_activity_at':
return conversation[attributeKey];
+ case 'display_id':
+ // Frontend uses 'id' but backend expects 'display_id'
+ return conversation.display_id || conversation.id;
case 'assignee_id':
return conversation.meta?.assignee?.id;
case 'inbox_id':
diff --git a/app/javascript/dashboard/store/modules/conversations/helpers/specs/filterHelpers.spec.js b/app/javascript/dashboard/store/modules/conversations/helpers/specs/filterHelpers.spec.js
index b36128819..096481c69 100644
--- a/app/javascript/dashboard/store/modules/conversations/helpers/specs/filterHelpers.spec.js
+++ b/app/javascript/dashboard/store/modules/conversations/helpers/specs/filterHelpers.spec.js
@@ -247,7 +247,7 @@ describe('filterHelpers', () => {
// Text search tests - display_id
it('should match conversation with equal_to operator for display_id', () => {
- const conversation = { display_id: '12345' };
+ const conversation = { id: '12345' };
const filters = [
{
attribute_key: 'display_id',
@@ -260,7 +260,7 @@ describe('filterHelpers', () => {
});
it('should match conversation with contains operator for display_id', () => {
- const conversation = { display_id: '12345' };
+ const conversation = { id: '12345' };
const filters = [
{
attribute_key: 'display_id',
@@ -273,7 +273,7 @@ describe('filterHelpers', () => {
});
it('should not match conversation with does_not_contain operator for display_id', () => {
- const conversation = { display_id: '12345' };
+ const conversation = { id: '12345' };
const filters = [
{
attribute_key: 'display_id',
@@ -286,7 +286,7 @@ describe('filterHelpers', () => {
});
it('should match conversation with does_not_contain operator when value is not present', () => {
- const conversation = { display_id: '12345' };
+ const conversation = { id: '12345' };
const filters = [
{
attribute_key: 'display_id',
@@ -989,7 +989,7 @@ describe('filterHelpers', () => {
it('should handle empty string values in conversation', () => {
const conversation = {
- display_id: '',
+ id: '',
};
const filters = [
{
diff --git a/app/javascript/dashboard/store/modules/inboxes.js b/app/javascript/dashboard/store/modules/inboxes.js
index c4789a7a9..9886be679 100644
--- a/app/javascript/dashboard/store/modules/inboxes.js
+++ b/app/javascript/dashboard/store/modules/inboxes.js
@@ -29,6 +29,9 @@ export const getters = {
getInboxes($state) {
return $state.records;
},
+ getAllInboxes($state) {
+ return camelcaseKeys($state.records, { deep: true });
+ },
getWhatsAppTemplates: $state => inboxId => {
const [inbox] = $state.records.filter(
record => record.id === Number(inboxId)
diff --git a/app/javascript/dashboard/store/modules/specs/account/getters.spec.js b/app/javascript/dashboard/store/modules/specs/account/getters.spec.js
index 77cc9b357..f354faca2 100644
--- a/app/javascript/dashboard/store/modules/specs/account/getters.spec.js
+++ b/app/javascript/dashboard/store/modules/specs/account/getters.spec.js
@@ -49,35 +49,74 @@ describe('#getters', () => {
});
describe('isRTL', () => {
- it('returns false when accountId is not present', () => {
+ afterEach(() => {
+ vi.restoreAllMocks();
+ });
+
+ it('returns false when accountId is not present and userLocale is not set', () => {
+ const state = { records: [accountData] };
const rootState = { route: { params: {} } };
- expect(getters.isRTL({}, null, rootState)).toBe(false);
+ const rootGetters = {};
+
+ expect(getters.isRTL(state, null, rootState, rootGetters)).toBe(false);
});
- it('returns true for RTL language', () => {
- const state = {
- records: [{ id: 1, locale: 'ar' }],
- };
- const rootState = { route: { params: { accountId: '1' } } };
- vi.spyOn(languageHelpers, 'getLanguageDirection').mockReturnValue(true);
- expect(getters.isRTL(state, null, rootState)).toBe(true);
+ it('uses userLocale when present (no accountId)', () => {
+ const state = { records: [accountData] };
+ const rootState = { route: { params: {} } };
+ const rootGetters = { getUISettings: { locale: 'ar' } };
+ const spy = vi
+ .spyOn(languageHelpers, 'getLanguageDirection')
+ .mockReturnValue(true);
+
+ expect(getters.isRTL(state, null, rootState, rootGetters)).toBe(true);
+ expect(spy).toHaveBeenCalledWith('ar');
});
- it('returns false for LTR language', () => {
- const state = {
- records: [{ id: 1, locale: 'en' }],
- };
+ it('prefers userLocale over account locale when both are present', () => {
+ const state = { records: [{ id: 1, locale: 'en' }] };
const rootState = { route: { params: { accountId: '1' } } };
- vi.spyOn(languageHelpers, 'getLanguageDirection').mockReturnValue(false);
- expect(getters.isRTL(state, null, rootState)).toBe(false);
+ const rootGetters = { getUISettings: { locale: 'ar' } };
+ const spy = vi
+ .spyOn(languageHelpers, 'getLanguageDirection')
+ .mockReturnValue(true);
+
+ expect(getters.isRTL(state, null, rootState, rootGetters)).toBe(true);
+ expect(spy).toHaveBeenCalledWith('ar');
});
- it('returns false when account is not found', () => {
- const state = {
- records: [],
- };
+ it('falls back to account locale when userLocale is not provided', () => {
+ const state = { records: [{ id: 1, locale: 'ar' }] };
const rootState = { route: { params: { accountId: '1' } } };
- expect(getters.isRTL(state, null, rootState)).toBe(false);
+ const rootGetters = {};
+ const spy = vi
+ .spyOn(languageHelpers, 'getLanguageDirection')
+ .mockReturnValue(true);
+
+ expect(getters.isRTL(state, null, rootState, rootGetters)).toBe(true);
+ expect(spy).toHaveBeenCalledWith('ar');
+ });
+
+ it('returns false for LTR language when userLocale is provided', () => {
+ const state = { records: [{ id: 1, locale: 'en' }] };
+ const rootState = { route: { params: { accountId: '1' } } };
+ const rootGetters = { getUISettings: { locale: 'en' } };
+ const spy = vi
+ .spyOn(languageHelpers, 'getLanguageDirection')
+ .mockReturnValue(false);
+
+ expect(getters.isRTL(state, null, rootState, rootGetters)).toBe(false);
+ expect(spy).toHaveBeenCalledWith('en');
+ });
+
+ it('returns false when accountId present but user locale is null', () => {
+ const state = { records: [{ id: 1, locale: 'en' }] };
+ const rootState = { route: { params: { accountId: '1' } } };
+ const rootGetters = { getUISettings: { locale: null } };
+ const spy = vi.spyOn(languageHelpers, 'getLanguageDirection');
+
+ expect(getters.isRTL(state, null, rootState, rootGetters)).toBe(false);
+ expect(spy).toHaveBeenCalledWith('en');
});
});
});
diff --git a/app/javascript/dashboard/store/modules/specs/agentCapacityPolicies/actions.spec.js b/app/javascript/dashboard/store/modules/specs/agentCapacityPolicies/actions.spec.js
new file mode 100644
index 000000000..3414a2086
--- /dev/null
+++ b/app/javascript/dashboard/store/modules/specs/agentCapacityPolicies/actions.spec.js
@@ -0,0 +1,408 @@
+import axios from 'axios';
+import { actions } from '../../agentCapacityPolicies';
+import types from '../../../mutation-types';
+import agentCapacityPoliciesList, {
+ camelCaseFixtures,
+ mockUsers,
+ mockInboxLimits,
+ camelCaseMockInboxLimits,
+} from './fixtures';
+import camelcaseKeys from 'camelcase-keys';
+import snakecaseKeys from 'snakecase-keys';
+
+const commit = vi.fn();
+
+global.axios = axios;
+vi.mock('axios');
+vi.mock('camelcase-keys');
+vi.mock('snakecase-keys');
+vi.mock('../../../utils/api');
+
+describe('#actions', () => {
+ beforeEach(() => {
+ vi.clearAllMocks();
+ });
+
+ describe('#get', () => {
+ it('sends correct actions if API is success', async () => {
+ axios.get.mockResolvedValue({ data: agentCapacityPoliciesList });
+ camelcaseKeys.mockReturnValue(camelCaseFixtures);
+
+ await actions.get({ commit });
+
+ expect(camelcaseKeys).toHaveBeenCalledWith(agentCapacityPoliciesList, {
+ deep: true,
+ });
+ expect(commit.mock.calls).toEqual([
+ [types.SET_AGENT_CAPACITY_POLICIES_UI_FLAG, { isFetching: true }],
+ [types.SET_AGENT_CAPACITY_POLICIES, camelCaseFixtures],
+ [types.SET_AGENT_CAPACITY_POLICIES_UI_FLAG, { isFetching: false }],
+ ]);
+ });
+
+ it('sends correct actions if API is error', async () => {
+ axios.get.mockRejectedValue({ message: 'Incorrect header' });
+
+ await actions.get({ commit });
+
+ expect(commit.mock.calls).toEqual([
+ [types.SET_AGENT_CAPACITY_POLICIES_UI_FLAG, { isFetching: true }],
+ [types.SET_AGENT_CAPACITY_POLICIES_UI_FLAG, { isFetching: false }],
+ ]);
+ });
+ });
+
+ describe('#show', () => {
+ it('sends correct actions if API is success', async () => {
+ const policyData = agentCapacityPoliciesList[0];
+ const camelCasedPolicy = camelCaseFixtures[0];
+
+ axios.get.mockResolvedValue({ data: policyData });
+ camelcaseKeys.mockReturnValue(camelCasedPolicy);
+
+ await actions.show({ commit }, 1);
+
+ expect(camelcaseKeys).toHaveBeenCalledWith(policyData, {
+ deep: true,
+ });
+ expect(commit.mock.calls).toEqual([
+ [types.SET_AGENT_CAPACITY_POLICIES_UI_FLAG, { isFetchingItem: true }],
+ [types.SET_AGENT_CAPACITY_POLICY, camelCasedPolicy],
+ [types.SET_AGENT_CAPACITY_POLICIES_UI_FLAG, { isFetchingItem: false }],
+ ]);
+ });
+
+ it('sends correct actions if API is error', async () => {
+ axios.get.mockRejectedValue({ message: 'Not found' });
+
+ await actions.show({ commit }, 1);
+
+ expect(commit.mock.calls).toEqual([
+ [types.SET_AGENT_CAPACITY_POLICIES_UI_FLAG, { isFetchingItem: true }],
+ [types.SET_AGENT_CAPACITY_POLICIES_UI_FLAG, { isFetchingItem: false }],
+ ]);
+ });
+ });
+
+ describe('#create', () => {
+ it('sends correct actions if API is success', async () => {
+ const newPolicy = agentCapacityPoliciesList[0];
+ const camelCasedData = camelCaseFixtures[0];
+ const snakeCasedPolicy = { default_capacity: 10 };
+
+ axios.post.mockResolvedValue({ data: newPolicy });
+ camelcaseKeys.mockReturnValue(camelCasedData);
+ snakecaseKeys.mockReturnValue(snakeCasedPolicy);
+
+ const result = await actions.create({ commit }, newPolicy);
+
+ expect(snakecaseKeys).toHaveBeenCalledWith(newPolicy);
+ expect(camelcaseKeys).toHaveBeenCalledWith(newPolicy, {
+ deep: true,
+ });
+ expect(commit.mock.calls).toEqual([
+ [types.SET_AGENT_CAPACITY_POLICIES_UI_FLAG, { isCreating: true }],
+ [types.ADD_AGENT_CAPACITY_POLICY, camelCasedData],
+ [types.SET_AGENT_CAPACITY_POLICIES_UI_FLAG, { isCreating: false }],
+ ]);
+ expect(result).toEqual(newPolicy);
+ });
+
+ it('sends correct actions if API is error', async () => {
+ axios.post.mockRejectedValue(new Error('Validation error'));
+
+ await expect(actions.create({ commit }, {})).rejects.toThrow(Error);
+
+ expect(commit.mock.calls).toEqual([
+ [types.SET_AGENT_CAPACITY_POLICIES_UI_FLAG, { isCreating: true }],
+ [types.SET_AGENT_CAPACITY_POLICIES_UI_FLAG, { isCreating: false }],
+ ]);
+ });
+ });
+
+ describe('#update', () => {
+ it('sends correct actions if API is success', async () => {
+ const updateParams = { id: 1, name: 'Updated Policy' };
+ const responseData = {
+ ...agentCapacityPoliciesList[0],
+ name: 'Updated Policy',
+ };
+ const camelCasedData = {
+ ...camelCaseFixtures[0],
+ name: 'Updated Policy',
+ };
+ const snakeCasedParams = { name: 'Updated Policy' };
+
+ axios.patch.mockResolvedValue({ data: responseData });
+ camelcaseKeys.mockReturnValue(camelCasedData);
+ snakecaseKeys.mockReturnValue(snakeCasedParams);
+
+ const result = await actions.update({ commit }, updateParams);
+
+ expect(snakecaseKeys).toHaveBeenCalledWith({ name: 'Updated Policy' });
+ expect(camelcaseKeys).toHaveBeenCalledWith(responseData, {
+ deep: true,
+ });
+ expect(commit.mock.calls).toEqual([
+ [types.SET_AGENT_CAPACITY_POLICIES_UI_FLAG, { isUpdating: true }],
+ [types.EDIT_AGENT_CAPACITY_POLICY, camelCasedData],
+ [types.SET_AGENT_CAPACITY_POLICIES_UI_FLAG, { isUpdating: false }],
+ ]);
+ expect(result).toEqual(responseData);
+ });
+
+ it('sends correct actions if API is error', async () => {
+ axios.patch.mockRejectedValue(new Error('Validation error'));
+
+ await expect(
+ actions.update({ commit }, { id: 1, name: 'Test' })
+ ).rejects.toThrow(Error);
+
+ expect(commit.mock.calls).toEqual([
+ [types.SET_AGENT_CAPACITY_POLICIES_UI_FLAG, { isUpdating: true }],
+ [types.SET_AGENT_CAPACITY_POLICIES_UI_FLAG, { isUpdating: false }],
+ ]);
+ });
+ });
+
+ describe('#delete', () => {
+ it('sends correct actions if API is success', async () => {
+ const policyId = 1;
+ axios.delete.mockResolvedValue({});
+
+ await actions.delete({ commit }, policyId);
+
+ expect(commit.mock.calls).toEqual([
+ [types.SET_AGENT_CAPACITY_POLICIES_UI_FLAG, { isDeleting: true }],
+ [types.DELETE_AGENT_CAPACITY_POLICY, policyId],
+ [types.SET_AGENT_CAPACITY_POLICIES_UI_FLAG, { isDeleting: false }],
+ ]);
+ });
+
+ it('sends correct actions if API is error', async () => {
+ axios.delete.mockRejectedValue(new Error('Not found'));
+
+ await expect(actions.delete({ commit }, 1)).rejects.toThrow(Error);
+
+ expect(commit.mock.calls).toEqual([
+ [types.SET_AGENT_CAPACITY_POLICIES_UI_FLAG, { isDeleting: true }],
+ [types.SET_AGENT_CAPACITY_POLICIES_UI_FLAG, { isDeleting: false }],
+ ]);
+ });
+ });
+
+ describe('#getUsers', () => {
+ it('sends correct actions if API is success', async () => {
+ const policyId = 1;
+ const userData = [
+ { id: 1, name: 'Agent 1', email: 'agent1@example.com', capacity: 15 },
+ { id: 2, name: 'Agent 2', email: 'agent2@example.com', capacity: 20 },
+ ];
+ const camelCasedUsers = [
+ { id: 1, name: 'Agent 1', email: 'agent1@example.com', capacity: 15 },
+ { id: 2, name: 'Agent 2', email: 'agent2@example.com', capacity: 20 },
+ ];
+
+ axios.get.mockResolvedValue({ data: userData });
+ camelcaseKeys.mockReturnValue(camelCasedUsers);
+
+ const result = await actions.getUsers({ commit }, policyId);
+
+ expect(camelcaseKeys).toHaveBeenCalledWith(userData);
+ expect(commit.mock.calls).toEqual([
+ [types.SET_AGENT_CAPACITY_POLICIES_USERS_UI_FLAG, { isFetching: true }],
+ [
+ types.SET_AGENT_CAPACITY_POLICIES_USERS,
+ { policyId, users: camelCasedUsers },
+ ],
+ [
+ types.SET_AGENT_CAPACITY_POLICIES_USERS_UI_FLAG,
+ { isFetching: false },
+ ],
+ ]);
+ expect(result).toEqual(userData);
+ });
+
+ it('sends correct actions if API fails', async () => {
+ axios.get.mockRejectedValue(new Error('API Error'));
+
+ await expect(actions.getUsers({ commit }, 1)).rejects.toThrow(Error);
+
+ expect(commit.mock.calls).toEqual([
+ [types.SET_AGENT_CAPACITY_POLICIES_USERS_UI_FLAG, { isFetching: true }],
+ [
+ types.SET_AGENT_CAPACITY_POLICIES_USERS_UI_FLAG,
+ { isFetching: false },
+ ],
+ ]);
+ });
+ });
+
+ describe('#addUser', () => {
+ it('sends correct actions if API is success', async () => {
+ const policyId = 1;
+ const userData = { user_id: 3, capacity: 12 };
+ const responseData = mockUsers[2];
+ const camelCasedUser = mockUsers[2];
+
+ axios.post.mockResolvedValue({ data: responseData });
+ camelcaseKeys.mockReturnValue(camelCasedUser);
+
+ const result = await actions.addUser({ commit }, { policyId, userData });
+
+ expect(camelcaseKeys).toHaveBeenCalledWith(responseData);
+ expect(commit.mock.calls).toEqual([
+ [
+ types.ADD_AGENT_CAPACITY_POLICIES_USERS,
+ { policyId, user: camelCasedUser },
+ ],
+ ]);
+ expect(result).toEqual(responseData);
+ });
+
+ it('sends correct actions if API is error', async () => {
+ axios.post.mockRejectedValue(new Error('Validation error'));
+
+ await expect(
+ actions.addUser({ commit }, { policyId: 1, userData: {} })
+ ).rejects.toThrow(Error);
+
+ expect(commit).not.toHaveBeenCalled();
+ });
+ });
+
+ describe('#removeUser', () => {
+ it('sends correct actions if API is success', async () => {
+ const policyId = 1;
+ const userId = 2;
+ axios.delete.mockResolvedValue({});
+
+ await actions.removeUser({ commit }, { policyId, userId });
+
+ expect(commit.mock.calls).toEqual([
+ [types.SET_AGENT_CAPACITY_POLICIES_USERS_UI_FLAG, { isDeleting: true }],
+ [types.DELETE_AGENT_CAPACITY_POLICIES_USERS, { policyId, userId }],
+ [
+ types.SET_AGENT_CAPACITY_POLICIES_USERS_UI_FLAG,
+ { isDeleting: false },
+ ],
+ ]);
+ });
+
+ it('sends correct actions if API is error', async () => {
+ axios.delete.mockRejectedValue(new Error('Not found'));
+
+ await expect(
+ actions.removeUser({ commit }, { policyId: 1, userId: 2 })
+ ).rejects.toThrow(Error);
+
+ expect(commit.mock.calls).toEqual([
+ [types.SET_AGENT_CAPACITY_POLICIES_USERS_UI_FLAG, { isDeleting: true }],
+ [
+ types.SET_AGENT_CAPACITY_POLICIES_USERS_UI_FLAG,
+ { isDeleting: false },
+ ],
+ ]);
+ });
+ });
+
+ describe('#createInboxLimit', () => {
+ it('sends correct actions if API is success', async () => {
+ const policyId = 1;
+ const limitData = { inbox_id: 3, conversation_limit: 20 };
+ const responseData = mockInboxLimits[2];
+ const camelCasedData = camelCaseMockInboxLimits[2];
+
+ axios.post.mockResolvedValue({ data: responseData });
+ camelcaseKeys.mockReturnValue(camelCasedData);
+
+ const result = await actions.createInboxLimit(
+ { commit },
+ { policyId, limitData }
+ );
+
+ expect(camelcaseKeys).toHaveBeenCalledWith(responseData);
+ expect(commit.mock.calls).toEqual([
+ [types.SET_AGENT_CAPACITY_POLICIES_INBOXES, camelCasedData],
+ ]);
+ expect(result).toEqual(responseData);
+ });
+
+ it('sends correct actions if API is error', async () => {
+ axios.post.mockRejectedValue(new Error('Validation error'));
+
+ await expect(
+ actions.createInboxLimit({ commit }, { policyId: 1, limitData: {} })
+ ).rejects.toThrow(Error);
+
+ expect(commit).not.toHaveBeenCalled();
+ });
+ });
+
+ describe('#updateInboxLimit', () => {
+ it('sends correct actions if API is success', async () => {
+ const policyId = 1;
+ const limitId = 1;
+ const limitData = { conversation_limit: 25 };
+ const responseData = {
+ ...mockInboxLimits[0],
+ conversation_limit: 25,
+ };
+ const camelCasedData = {
+ ...camelCaseMockInboxLimits[0],
+ conversationLimit: 25,
+ };
+
+ axios.put.mockResolvedValue({ data: responseData });
+ camelcaseKeys.mockReturnValue(camelCasedData);
+
+ const result = await actions.updateInboxLimit(
+ { commit },
+ { policyId, limitId, limitData }
+ );
+
+ expect(camelcaseKeys).toHaveBeenCalledWith(responseData);
+ expect(commit.mock.calls).toEqual([
+ [types.EDIT_AGENT_CAPACITY_POLICIES_INBOXES, camelCasedData],
+ ]);
+ expect(result).toEqual(responseData);
+ });
+
+ it('sends correct actions if API is error', async () => {
+ axios.put.mockRejectedValue(new Error('Validation error'));
+
+ await expect(
+ actions.updateInboxLimit(
+ { commit },
+ { policyId: 1, limitId: 1, limitData: {} }
+ )
+ ).rejects.toThrow(Error);
+
+ expect(commit).not.toHaveBeenCalled();
+ });
+ });
+
+ describe('#deleteInboxLimit', () => {
+ it('sends correct actions if API is success', async () => {
+ const policyId = 1;
+ const limitId = 1;
+ axios.delete.mockResolvedValue({});
+
+ await actions.deleteInboxLimit({ commit }, { policyId, limitId });
+
+ expect(commit.mock.calls).toEqual([
+ [types.DELETE_AGENT_CAPACITY_POLICIES_INBOXES, { policyId, limitId }],
+ ]);
+ });
+
+ it('sends correct actions if API is error', async () => {
+ axios.delete.mockRejectedValue(new Error('Not found'));
+
+ await expect(
+ actions.deleteInboxLimit({ commit }, { policyId: 1, limitId: 1 })
+ ).rejects.toThrow(Error);
+
+ expect(commit).not.toHaveBeenCalled();
+ });
+ });
+});
diff --git a/app/javascript/dashboard/store/modules/specs/agentCapacityPolicies/fixtures.js b/app/javascript/dashboard/store/modules/specs/agentCapacityPolicies/fixtures.js
new file mode 100644
index 000000000..c79597919
--- /dev/null
+++ b/app/javascript/dashboard/store/modules/specs/agentCapacityPolicies/fixtures.js
@@ -0,0 +1,199 @@
+export default [
+ {
+ id: 1,
+ name: 'Standard Capacity Policy',
+ description: 'Default capacity policy for agents',
+ default_capacity: 10,
+ enabled: true,
+ account_id: 1,
+ assigned_agent_count: 3,
+ created_at: '2024-01-01T10:00:00.000Z',
+ updated_at: '2024-01-01T10:00:00.000Z',
+ users: [],
+ inbox_capacity_limits: [
+ {
+ id: 1,
+ inbox_id: 1,
+ conversation_limit: 15,
+ agent_capacity_policy_id: 1,
+ },
+ {
+ id: 2,
+ inbox_id: 2,
+ conversation_limit: 8,
+ agent_capacity_policy_id: 1,
+ },
+ ],
+ },
+ {
+ id: 2,
+ name: 'High Capacity Policy',
+ description: 'High capacity policy for senior agents',
+ default_capacity: 20,
+ enabled: true,
+ account_id: 1,
+ assigned_agent_count: 5,
+ created_at: '2024-01-01T11:00:00.000Z',
+ updated_at: '2024-01-01T11:00:00.000Z',
+ users: [
+ {
+ id: 1,
+ name: 'Agent Smith',
+ email: 'agent.smith@example.com',
+ capacity: 25,
+ },
+ {
+ id: 2,
+ name: 'Agent Johnson',
+ email: 'agent.johnson@example.com',
+ capacity: 18,
+ },
+ ],
+ inbox_capacity_limits: [],
+ },
+ {
+ id: 3,
+ name: 'Disabled Policy',
+ description: 'Disabled capacity policy',
+ default_capacity: 5,
+ enabled: false,
+ account_id: 1,
+ assigned_agent_count: 0,
+ created_at: '2024-01-01T12:00:00.000Z',
+ updated_at: '2024-01-01T12:00:00.000Z',
+ users: [],
+ inbox_capacity_limits: [],
+ },
+];
+
+export const camelCaseFixtures = [
+ {
+ id: 1,
+ name: 'Standard Capacity Policy',
+ description: 'Default capacity policy for agents',
+ defaultCapacity: 10,
+ enabled: true,
+ accountId: 1,
+ assignedAgentCount: 3,
+ createdAt: '2024-01-01T10:00:00.000Z',
+ updatedAt: '2024-01-01T10:00:00.000Z',
+ users: [],
+ inboxCapacityLimits: [
+ {
+ id: 1,
+ inboxId: 1,
+ conversationLimit: 15,
+ agentCapacityPolicyId: 1,
+ },
+ {
+ id: 2,
+ inboxId: 2,
+ conversationLimit: 8,
+ agentCapacityPolicyId: 1,
+ },
+ ],
+ },
+ {
+ id: 2,
+ name: 'High Capacity Policy',
+ description: 'High capacity policy for senior agents',
+ defaultCapacity: 20,
+ enabled: true,
+ accountId: 1,
+ assignedAgentCount: 5,
+ createdAt: '2024-01-01T11:00:00.000Z',
+ updatedAt: '2024-01-01T11:00:00.000Z',
+ users: [
+ {
+ id: 1,
+ name: 'Agent Smith',
+ email: 'agent.smith@example.com',
+ capacity: 25,
+ },
+ {
+ id: 2,
+ name: 'Agent Johnson',
+ email: 'agent.johnson@example.com',
+ capacity: 18,
+ },
+ ],
+ inboxCapacityLimits: [],
+ },
+ {
+ id: 3,
+ name: 'Disabled Policy',
+ description: 'Disabled capacity policy',
+ defaultCapacity: 5,
+ enabled: false,
+ accountId: 1,
+ assignedAgentCount: 0,
+ createdAt: '2024-01-01T12:00:00.000Z',
+ updatedAt: '2024-01-01T12:00:00.000Z',
+ users: [],
+ inboxCapacityLimits: [],
+ },
+];
+
+// Additional test data for user and inbox limit operations
+export const mockUsers = [
+ {
+ id: 1,
+ name: 'Agent Smith',
+ email: 'agent.smith@example.com',
+ capacity: 25,
+ },
+ {
+ id: 2,
+ name: 'Agent Johnson',
+ email: 'agent.johnson@example.com',
+ capacity: 18,
+ },
+ {
+ id: 3,
+ name: 'Agent Brown',
+ email: 'agent.brown@example.com',
+ capacity: 12,
+ },
+];
+
+export const mockInboxLimits = [
+ {
+ id: 1,
+ inbox_id: 1,
+ conversation_limit: 15,
+ agent_capacity_policy_id: 1,
+ },
+ {
+ id: 2,
+ inbox_id: 2,
+ conversation_limit: 8,
+ agent_capacity_policy_id: 1,
+ },
+ {
+ id: 3,
+ inbox_id: 3,
+ conversation_limit: 20,
+ agent_capacity_policy_id: 2,
+ },
+];
+
+export const camelCaseMockInboxLimits = [
+ {
+ id: 1,
+ inboxId: 1,
+ conversationLimit: 15,
+ agentCapacityPolicyId: 1,
+ },
+ {
+ id: 2,
+ inboxId: 2,
+ conversationLimit: 8,
+ agentCapacityPolicyId: 1,
+ },
+ {
+ id: 3,
+ inboxId: 3,
+ conversationLimit: 20,
+ agentCapacityPolicyId: 2,
+ },
+];
diff --git a/app/javascript/dashboard/store/modules/specs/agentCapacityPolicies/getters.spec.js b/app/javascript/dashboard/store/modules/specs/agentCapacityPolicies/getters.spec.js
new file mode 100644
index 000000000..2acd00ad4
--- /dev/null
+++ b/app/javascript/dashboard/store/modules/specs/agentCapacityPolicies/getters.spec.js
@@ -0,0 +1,51 @@
+import { getters } from '../../agentCapacityPolicies';
+import agentCapacityPoliciesList from './fixtures';
+
+describe('#getters', () => {
+ it('getAgentCapacityPolicies', () => {
+ const state = { records: agentCapacityPoliciesList };
+ expect(getters.getAgentCapacityPolicies(state)).toEqual(
+ agentCapacityPoliciesList
+ );
+ });
+
+ it('getUIFlags', () => {
+ const state = {
+ uiFlags: {
+ isFetching: true,
+ isFetchingItem: false,
+ isCreating: false,
+ isUpdating: false,
+ isDeleting: false,
+ },
+ };
+ expect(getters.getUIFlags(state)).toEqual({
+ isFetching: true,
+ isFetchingItem: false,
+ isCreating: false,
+ isUpdating: false,
+ isDeleting: false,
+ });
+ });
+
+ it('getUsersUIFlags', () => {
+ const state = {
+ usersUiFlags: {
+ isFetching: false,
+ isDeleting: false,
+ },
+ };
+ expect(getters.getUsersUIFlags(state)).toEqual({
+ isFetching: false,
+ isDeleting: false,
+ });
+ });
+
+ it('getAgentCapacityPolicyById', () => {
+ const state = { records: agentCapacityPoliciesList };
+ expect(getters.getAgentCapacityPolicyById(state)(1)).toEqual(
+ agentCapacityPoliciesList[0]
+ );
+ expect(getters.getAgentCapacityPolicyById(state)(4)).toEqual({});
+ });
+});
diff --git a/app/javascript/dashboard/store/modules/specs/agentCapacityPolicies/mutations.spec.js b/app/javascript/dashboard/store/modules/specs/agentCapacityPolicies/mutations.spec.js
new file mode 100644
index 000000000..f6cfba80d
--- /dev/null
+++ b/app/javascript/dashboard/store/modules/specs/agentCapacityPolicies/mutations.spec.js
@@ -0,0 +1,619 @@
+import { mutations } from '../../agentCapacityPolicies';
+import types from '../../../mutation-types';
+import agentCapacityPoliciesList, { mockUsers } from './fixtures';
+
+describe('#mutations', () => {
+ describe('#SET_AGENT_CAPACITY_POLICIES_UI_FLAG', () => {
+ it('sets single ui flag', () => {
+ const state = {
+ uiFlags: {
+ isFetching: false,
+ isCreating: false,
+ },
+ };
+
+ mutations[types.SET_AGENT_CAPACITY_POLICIES_UI_FLAG](state, {
+ isFetching: true,
+ });
+
+ expect(state.uiFlags).toEqual({
+ isFetching: true,
+ isCreating: false,
+ });
+ });
+
+ it('sets multiple ui flags', () => {
+ const state = {
+ uiFlags: {
+ isFetching: false,
+ isCreating: false,
+ isUpdating: false,
+ },
+ };
+
+ mutations[types.SET_AGENT_CAPACITY_POLICIES_UI_FLAG](state, {
+ isFetching: true,
+ isCreating: true,
+ });
+
+ expect(state.uiFlags).toEqual({
+ isFetching: true,
+ isCreating: true,
+ isUpdating: false,
+ });
+ });
+ });
+
+ describe('#SET_AGENT_CAPACITY_POLICIES', () => {
+ it('sets agent capacity policies records', () => {
+ const state = { records: [] };
+
+ mutations[types.SET_AGENT_CAPACITY_POLICIES](
+ state,
+ agentCapacityPoliciesList
+ );
+
+ expect(state.records).toEqual(agentCapacityPoliciesList);
+ });
+
+ it('replaces existing records', () => {
+ const state = { records: [{ id: 999, name: 'Old Policy' }] };
+
+ mutations[types.SET_AGENT_CAPACITY_POLICIES](
+ state,
+ agentCapacityPoliciesList
+ );
+
+ expect(state.records).toEqual(agentCapacityPoliciesList);
+ });
+ });
+
+ describe('#SET_AGENT_CAPACITY_POLICY', () => {
+ it('sets single agent capacity policy record', () => {
+ const state = { records: [] };
+
+ mutations[types.SET_AGENT_CAPACITY_POLICY](
+ state,
+ agentCapacityPoliciesList[0]
+ );
+
+ expect(state.records).toEqual([agentCapacityPoliciesList[0]]);
+ });
+
+ it('replaces existing record', () => {
+ const state = { records: [{ id: 1, name: 'Old Policy' }] };
+
+ mutations[types.SET_AGENT_CAPACITY_POLICY](
+ state,
+ agentCapacityPoliciesList[0]
+ );
+
+ expect(state.records).toEqual([agentCapacityPoliciesList[0]]);
+ });
+ });
+
+ describe('#ADD_AGENT_CAPACITY_POLICY', () => {
+ it('adds new policy to empty records', () => {
+ const state = { records: [] };
+
+ mutations[types.ADD_AGENT_CAPACITY_POLICY](
+ state,
+ agentCapacityPoliciesList[0]
+ );
+
+ expect(state.records).toEqual([agentCapacityPoliciesList[0]]);
+ });
+
+ it('adds new policy to existing records', () => {
+ const state = { records: [agentCapacityPoliciesList[0]] };
+
+ mutations[types.ADD_AGENT_CAPACITY_POLICY](
+ state,
+ agentCapacityPoliciesList[1]
+ );
+
+ expect(state.records).toEqual([
+ agentCapacityPoliciesList[0],
+ agentCapacityPoliciesList[1],
+ ]);
+ });
+ });
+
+ describe('#EDIT_AGENT_CAPACITY_POLICY', () => {
+ it('updates existing policy by id', () => {
+ const state = {
+ records: [
+ { ...agentCapacityPoliciesList[0] },
+ { ...agentCapacityPoliciesList[1] },
+ ],
+ };
+
+ const updatedPolicy = {
+ ...agentCapacityPoliciesList[0],
+ name: 'Updated Policy Name',
+ description: 'Updated Description',
+ };
+
+ mutations[types.EDIT_AGENT_CAPACITY_POLICY](state, updatedPolicy);
+
+ expect(state.records[0]).toEqual(updatedPolicy);
+ expect(state.records[1]).toEqual(agentCapacityPoliciesList[1]);
+ });
+
+ it('updates policy with camelCase properties', () => {
+ const camelCasePolicy = {
+ id: 1,
+ name: 'Camel Case Policy',
+ defaultCapacity: 15,
+ enabled: true,
+ };
+
+ const state = {
+ records: [camelCasePolicy],
+ };
+
+ const updatedPolicy = {
+ ...camelCasePolicy,
+ name: 'Updated Camel Case',
+ defaultCapacity: 25,
+ };
+
+ mutations[types.EDIT_AGENT_CAPACITY_POLICY](state, updatedPolicy);
+
+ expect(state.records[0]).toEqual(updatedPolicy);
+ });
+
+ it('does nothing if policy id not found', () => {
+ const state = {
+ records: [agentCapacityPoliciesList[0]],
+ };
+
+ const nonExistentPolicy = {
+ id: 999,
+ name: 'Non-existent',
+ };
+
+ const originalRecords = [...state.records];
+ mutations[types.EDIT_AGENT_CAPACITY_POLICY](state, nonExistentPolicy);
+
+ expect(state.records).toEqual(originalRecords);
+ });
+ });
+
+ describe('#DELETE_AGENT_CAPACITY_POLICY', () => {
+ it('deletes policy by id', () => {
+ const state = {
+ records: [agentCapacityPoliciesList[0], agentCapacityPoliciesList[1]],
+ };
+
+ mutations[types.DELETE_AGENT_CAPACITY_POLICY](state, 1);
+
+ expect(state.records).toEqual([agentCapacityPoliciesList[1]]);
+ });
+
+ it('does nothing if id not found', () => {
+ const state = {
+ records: [agentCapacityPoliciesList[0]],
+ };
+
+ mutations[types.DELETE_AGENT_CAPACITY_POLICY](state, 999);
+
+ expect(state.records).toEqual([agentCapacityPoliciesList[0]]);
+ });
+
+ it('handles empty records', () => {
+ const state = { records: [] };
+
+ mutations[types.DELETE_AGENT_CAPACITY_POLICY](state, 1);
+
+ expect(state.records).toEqual([]);
+ });
+ });
+
+ describe('#SET_AGENT_CAPACITY_POLICIES_USERS_UI_FLAG', () => {
+ it('sets users ui flags', () => {
+ const state = {
+ usersUiFlags: {
+ isFetching: false,
+ },
+ };
+
+ mutations[types.SET_AGENT_CAPACITY_POLICIES_USERS_UI_FLAG](state, {
+ isFetching: true,
+ });
+
+ expect(state.usersUiFlags).toEqual({
+ isFetching: true,
+ });
+ });
+
+ it('merges with existing flags', () => {
+ const state = {
+ usersUiFlags: {
+ isFetching: false,
+ isDeleting: true,
+ },
+ };
+
+ mutations[types.SET_AGENT_CAPACITY_POLICIES_USERS_UI_FLAG](state, {
+ isFetching: true,
+ });
+
+ expect(state.usersUiFlags).toEqual({
+ isFetching: true,
+ isDeleting: true,
+ });
+ });
+ });
+
+ describe('#SET_AGENT_CAPACITY_POLICIES_USERS', () => {
+ it('sets users for existing policy', () => {
+ const testUsers = [
+ { id: 1, name: 'Agent 1', email: 'agent1@example.com', capacity: 15 },
+ { id: 2, name: 'Agent 2', email: 'agent2@example.com', capacity: 20 },
+ ];
+
+ const state = {
+ records: [
+ { id: 1, name: 'Policy 1', users: [] },
+ { id: 2, name: 'Policy 2', users: [] },
+ ],
+ };
+
+ mutations[types.SET_AGENT_CAPACITY_POLICIES_USERS](state, {
+ policyId: 1,
+ users: testUsers,
+ });
+
+ expect(state.records[0].users).toEqual(testUsers);
+ expect(state.records[1].users).toEqual([]);
+ });
+
+ it('replaces existing users', () => {
+ const oldUsers = [{ id: 99, name: 'Old Agent', capacity: 5 }];
+ const newUsers = [{ id: 1, name: 'New Agent', capacity: 15 }];
+
+ const state = {
+ records: [{ id: 1, name: 'Policy 1', users: oldUsers }],
+ };
+
+ mutations[types.SET_AGENT_CAPACITY_POLICIES_USERS](state, {
+ policyId: 1,
+ users: newUsers,
+ });
+
+ expect(state.records[0].users).toEqual(newUsers);
+ });
+
+ it('does nothing if policy not found', () => {
+ const state = {
+ records: [{ id: 1, name: 'Policy 1', users: [] }],
+ };
+
+ const originalState = JSON.parse(JSON.stringify(state));
+
+ mutations[types.SET_AGENT_CAPACITY_POLICIES_USERS](state, {
+ policyId: 999,
+ users: [{ id: 1, name: 'Test' }],
+ });
+
+ expect(state).toEqual(originalState);
+ });
+ });
+
+ describe('#ADD_AGENT_CAPACITY_POLICIES_USERS', () => {
+ it('adds user to existing policy', () => {
+ const state = {
+ records: [
+ { id: 1, name: 'Policy 1', users: [] },
+ { id: 2, name: 'Policy 2', users: [] },
+ ],
+ };
+
+ mutations[types.ADD_AGENT_CAPACITY_POLICIES_USERS](state, {
+ policyId: 1,
+ user: mockUsers[0],
+ });
+
+ expect(state.records[0].users).toEqual([mockUsers[0]]);
+ expect(state.records[1].users).toEqual([]);
+ });
+
+ it('adds user to policy with existing users', () => {
+ const state = {
+ records: [{ id: 1, name: 'Policy 1', users: [mockUsers[0]] }],
+ };
+
+ mutations[types.ADD_AGENT_CAPACITY_POLICIES_USERS](state, {
+ policyId: 1,
+ user: mockUsers[1],
+ });
+
+ expect(state.records[0].users).toEqual([mockUsers[0], mockUsers[1]]);
+ });
+
+ it('initializes users array if undefined', () => {
+ const state = {
+ records: [{ id: 1, name: 'Policy 1' }],
+ };
+
+ mutations[types.ADD_AGENT_CAPACITY_POLICIES_USERS](state, {
+ policyId: 1,
+ user: mockUsers[0],
+ });
+
+ expect(state.records[0].users).toEqual([mockUsers[0]]);
+ });
+
+ it('updates assigned agent count', () => {
+ const state = {
+ records: [{ id: 1, name: 'Policy 1', users: [] }],
+ };
+
+ mutations[types.ADD_AGENT_CAPACITY_POLICIES_USERS](state, {
+ policyId: 1,
+ user: mockUsers[0],
+ });
+
+ expect(state.records[0].assignedAgentCount).toEqual(1);
+ });
+ });
+
+ describe('#DELETE_AGENT_CAPACITY_POLICIES_USERS', () => {
+ it('removes user from policy', () => {
+ const state = {
+ records: [
+ {
+ id: 1,
+ name: 'Policy 1',
+ users: [mockUsers[0], mockUsers[1], mockUsers[2]],
+ },
+ ],
+ };
+
+ mutations[types.DELETE_AGENT_CAPACITY_POLICIES_USERS](state, {
+ policyId: 1,
+ userId: 2,
+ });
+
+ expect(state.records[0].users).toEqual([mockUsers[0], mockUsers[2]]);
+ });
+
+ it('handles removing non-existent user', () => {
+ const state = {
+ records: [
+ {
+ id: 1,
+ name: 'Policy 1',
+ users: [mockUsers[0]],
+ },
+ ],
+ };
+
+ mutations[types.DELETE_AGENT_CAPACITY_POLICIES_USERS](state, {
+ policyId: 1,
+ userId: 999,
+ });
+
+ expect(state.records[0].users).toEqual([mockUsers[0]]);
+ });
+
+ it('updates assigned agent count', () => {
+ const state = {
+ records: [{ id: 1, name: 'Policy 1', users: [mockUsers[0]] }],
+ };
+
+ mutations[types.DELETE_AGENT_CAPACITY_POLICIES_USERS](state, {
+ policyId: 1,
+ userId: 1,
+ });
+
+ expect(state.records[0].assignedAgentCount).toEqual(0);
+ });
+ });
+
+ describe('#SET_AGENT_CAPACITY_POLICIES_INBOXES', () => {
+ it('adds inbox limit to policy', () => {
+ const state = {
+ records: [
+ {
+ id: 1,
+ name: 'Policy 1',
+ inboxCapacityLimits: [],
+ },
+ ],
+ };
+
+ const inboxLimitData = {
+ id: 1,
+ inboxId: 1,
+ conversationLimit: 15,
+ agentCapacityPolicyId: 1,
+ };
+
+ mutations[types.SET_AGENT_CAPACITY_POLICIES_INBOXES](
+ state,
+ inboxLimitData
+ );
+
+ expect(state.records[0].inboxCapacityLimits).toEqual([
+ {
+ id: 1,
+ inboxId: 1,
+ conversationLimit: 15,
+ },
+ ]);
+ });
+
+ it('does nothing if policy not found', () => {
+ const state = {
+ records: [{ id: 1, name: 'Policy 1', inboxCapacityLimits: [] }],
+ };
+
+ const originalState = JSON.parse(JSON.stringify(state));
+
+ mutations[types.SET_AGENT_CAPACITY_POLICIES_INBOXES](state, {
+ id: 1,
+ inboxId: 1,
+ conversationLimit: 15,
+ agentCapacityPolicyId: 999,
+ });
+
+ expect(state).toEqual(originalState);
+ });
+ });
+
+ describe('#EDIT_AGENT_CAPACITY_POLICIES_INBOXES', () => {
+ it('updates existing inbox limit', () => {
+ const state = {
+ records: [
+ {
+ id: 1,
+ name: 'Policy 1',
+ inboxCapacityLimits: [
+ {
+ id: 1,
+ inboxId: 1,
+ conversationLimit: 15,
+ },
+ {
+ id: 2,
+ inboxId: 2,
+ conversationLimit: 8,
+ },
+ ],
+ },
+ ],
+ };
+
+ mutations[types.EDIT_AGENT_CAPACITY_POLICIES_INBOXES](state, {
+ id: 1,
+ inboxId: 1,
+ conversationLimit: 25,
+ agentCapacityPolicyId: 1,
+ });
+
+ expect(state.records[0].inboxCapacityLimits[0]).toEqual({
+ id: 1,
+ inboxId: 1,
+ conversationLimit: 25,
+ });
+ expect(state.records[0].inboxCapacityLimits[1]).toEqual({
+ id: 2,
+ inboxId: 2,
+ conversationLimit: 8,
+ });
+ });
+
+ it('does nothing if limit not found', () => {
+ const state = {
+ records: [
+ {
+ id: 1,
+ name: 'Policy 1',
+ inboxCapacityLimits: [
+ {
+ id: 1,
+ inboxId: 1,
+ conversationLimit: 15,
+ },
+ ],
+ },
+ ],
+ };
+
+ const originalLimits = [...state.records[0].inboxCapacityLimits];
+
+ mutations[types.EDIT_AGENT_CAPACITY_POLICIES_INBOXES](state, {
+ id: 999,
+ inboxId: 1,
+ conversationLimit: 25,
+ agentCapacityPolicyId: 1,
+ });
+
+ expect(state.records[0].inboxCapacityLimits).toEqual(originalLimits);
+ });
+
+ it('does nothing if policy not found', () => {
+ const state = {
+ records: [{ id: 1, name: 'Policy 1', inboxCapacityLimits: [] }],
+ };
+
+ const originalState = JSON.parse(JSON.stringify(state));
+
+ mutations[types.EDIT_AGENT_CAPACITY_POLICIES_INBOXES](state, {
+ id: 1,
+ inboxId: 1,
+ conversationLimit: 25,
+ agentCapacityPolicyId: 999,
+ });
+
+ expect(state).toEqual(originalState);
+ });
+ });
+
+ describe('#DELETE_AGENT_CAPACITY_POLICIES_INBOXES', () => {
+ it('removes inbox limit from policy', () => {
+ const state = {
+ records: [
+ {
+ id: 1,
+ name: 'Policy 1',
+ inboxCapacityLimits: [
+ {
+ id: 1,
+ inboxId: 1,
+ conversationLimit: 15,
+ },
+ {
+ id: 2,
+ inboxId: 2,
+ conversationLimit: 8,
+ },
+ ],
+ },
+ ],
+ };
+
+ mutations[types.DELETE_AGENT_CAPACITY_POLICIES_INBOXES](state, {
+ policyId: 1,
+ limitId: 1,
+ });
+
+ expect(state.records[0].inboxCapacityLimits).toEqual([
+ {
+ id: 2,
+ inboxId: 2,
+ conversationLimit: 8,
+ },
+ ]);
+ });
+
+ it('handles removing non-existent limit', () => {
+ const state = {
+ records: [
+ {
+ id: 1,
+ name: 'Policy 1',
+ inboxCapacityLimits: [
+ {
+ id: 1,
+ inboxId: 1,
+ conversationLimit: 15,
+ },
+ ],
+ },
+ ],
+ };
+
+ const originalLimits = [...state.records[0].inboxCapacityLimits];
+
+ mutations[types.DELETE_AGENT_CAPACITY_POLICIES_INBOXES](state, {
+ policyId: 1,
+ limitId: 999,
+ });
+
+ expect(state.records[0].inboxCapacityLimits).toEqual(originalLimits);
+ });
+ });
+});
diff --git a/app/javascript/dashboard/store/modules/specs/assignmentPolicies/actions.spec.js b/app/javascript/dashboard/store/modules/specs/assignmentPolicies/actions.spec.js
new file mode 100644
index 000000000..1398f5959
--- /dev/null
+++ b/app/javascript/dashboard/store/modules/specs/assignmentPolicies/actions.spec.js
@@ -0,0 +1,326 @@
+import axios from 'axios';
+import { actions } from '../../assignmentPolicies';
+import types from '../../../mutation-types';
+import assignmentPoliciesList, { camelCaseFixtures } from './fixtures';
+import camelcaseKeys from 'camelcase-keys';
+import snakecaseKeys from 'snakecase-keys';
+
+const commit = vi.fn();
+
+global.axios = axios;
+vi.mock('axios');
+vi.mock('camelcase-keys');
+vi.mock('snakecase-keys');
+vi.mock('../../../utils/api');
+
+describe('#actions', () => {
+ beforeEach(() => {
+ vi.clearAllMocks();
+ });
+
+ describe('#get', () => {
+ it('sends correct actions if API is success', async () => {
+ axios.get.mockResolvedValue({ data: assignmentPoliciesList });
+ camelcaseKeys.mockReturnValue(camelCaseFixtures);
+
+ await actions.get({ commit });
+
+ expect(camelcaseKeys).toHaveBeenCalledWith(assignmentPoliciesList);
+ expect(commit.mock.calls).toEqual([
+ [types.SET_ASSIGNMENT_POLICIES_UI_FLAG, { isFetching: true }],
+ [types.SET_ASSIGNMENT_POLICIES, camelCaseFixtures],
+ [types.SET_ASSIGNMENT_POLICIES_UI_FLAG, { isFetching: false }],
+ ]);
+ });
+
+ it('sends correct actions if API is error', async () => {
+ axios.get.mockRejectedValue({ message: 'Incorrect header' });
+
+ await actions.get({ commit });
+
+ expect(commit.mock.calls).toEqual([
+ [types.SET_ASSIGNMENT_POLICIES_UI_FLAG, { isFetching: true }],
+ [types.SET_ASSIGNMENT_POLICIES_UI_FLAG, { isFetching: false }],
+ ]);
+ });
+ });
+
+ describe('#show', () => {
+ it('sends correct actions if API is success', async () => {
+ const policyData = assignmentPoliciesList[0];
+ const camelCasedPolicy = camelCaseFixtures[0];
+
+ axios.get.mockResolvedValue({ data: policyData });
+ camelcaseKeys.mockReturnValue(camelCasedPolicy);
+
+ await actions.show({ commit }, 1);
+
+ expect(camelcaseKeys).toHaveBeenCalledWith(policyData);
+ expect(commit.mock.calls).toEqual([
+ [types.SET_ASSIGNMENT_POLICIES_UI_FLAG, { isFetchingItem: true }],
+ [types.SET_ASSIGNMENT_POLICY, camelCasedPolicy],
+ [types.SET_ASSIGNMENT_POLICIES_UI_FLAG, { isFetchingItem: false }],
+ ]);
+ });
+
+ it('sends correct actions if API is error', async () => {
+ axios.get.mockRejectedValue({ message: 'Not found' });
+
+ await actions.show({ commit }, 1);
+
+ expect(commit.mock.calls).toEqual([
+ [types.SET_ASSIGNMENT_POLICIES_UI_FLAG, { isFetchingItem: true }],
+ [types.SET_ASSIGNMENT_POLICIES_UI_FLAG, { isFetchingItem: false }],
+ ]);
+ });
+ });
+
+ describe('#create', () => {
+ it('sends correct actions if API is success', async () => {
+ const newPolicy = assignmentPoliciesList[0];
+ const camelCasedData = camelCaseFixtures[0];
+ const snakeCasedPolicy = { assignment_order: 'round_robin' };
+
+ axios.post.mockResolvedValue({ data: newPolicy });
+ camelcaseKeys.mockReturnValue(camelCasedData);
+ snakecaseKeys.mockReturnValue(snakeCasedPolicy);
+
+ const result = await actions.create({ commit }, newPolicy);
+
+ expect(snakecaseKeys).toHaveBeenCalledWith(newPolicy);
+ expect(camelcaseKeys).toHaveBeenCalledWith(newPolicy);
+ expect(commit.mock.calls).toEqual([
+ [types.SET_ASSIGNMENT_POLICIES_UI_FLAG, { isCreating: true }],
+ [types.ADD_ASSIGNMENT_POLICY, camelCasedData],
+ [types.SET_ASSIGNMENT_POLICIES_UI_FLAG, { isCreating: false }],
+ ]);
+ expect(result).toEqual(newPolicy);
+ });
+
+ it('sends correct actions if API is error', async () => {
+ axios.post.mockRejectedValue(new Error('Validation error'));
+
+ await expect(actions.create({ commit }, {})).rejects.toThrow(Error);
+
+ expect(commit.mock.calls).toEqual([
+ [types.SET_ASSIGNMENT_POLICIES_UI_FLAG, { isCreating: true }],
+ [types.SET_ASSIGNMENT_POLICIES_UI_FLAG, { isCreating: false }],
+ ]);
+ });
+ });
+
+ describe('#update', () => {
+ it('sends correct actions if API is success', async () => {
+ const updateParams = { id: 1, name: 'Updated Policy' };
+ const responseData = {
+ ...assignmentPoliciesList[0],
+ name: 'Updated Policy',
+ };
+ const camelCasedData = {
+ ...camelCaseFixtures[0],
+ name: 'Updated Policy',
+ };
+ const snakeCasedParams = { name: 'Updated Policy' };
+
+ axios.patch.mockResolvedValue({ data: responseData });
+ camelcaseKeys.mockReturnValue(camelCasedData);
+ snakecaseKeys.mockReturnValue(snakeCasedParams);
+
+ const result = await actions.update({ commit }, updateParams);
+
+ expect(snakecaseKeys).toHaveBeenCalledWith({ name: 'Updated Policy' });
+ expect(camelcaseKeys).toHaveBeenCalledWith(responseData);
+ expect(commit.mock.calls).toEqual([
+ [types.SET_ASSIGNMENT_POLICIES_UI_FLAG, { isUpdating: true }],
+ [types.EDIT_ASSIGNMENT_POLICY, camelCasedData],
+ [types.SET_ASSIGNMENT_POLICIES_UI_FLAG, { isUpdating: false }],
+ ]);
+ expect(result).toEqual(responseData);
+ });
+
+ it('sends correct actions if API is error', async () => {
+ axios.patch.mockRejectedValue(new Error('Validation error'));
+
+ await expect(
+ actions.update({ commit }, { id: 1, name: 'Test' })
+ ).rejects.toThrow(Error);
+
+ expect(commit.mock.calls).toEqual([
+ [types.SET_ASSIGNMENT_POLICIES_UI_FLAG, { isUpdating: true }],
+ [types.SET_ASSIGNMENT_POLICIES_UI_FLAG, { isUpdating: false }],
+ ]);
+ });
+ });
+
+ describe('#delete', () => {
+ it('sends correct actions if API is success', async () => {
+ const policyId = 1;
+ axios.delete.mockResolvedValue({});
+
+ await actions.delete({ commit }, policyId);
+
+ expect(commit.mock.calls).toEqual([
+ [types.SET_ASSIGNMENT_POLICIES_UI_FLAG, { isDeleting: true }],
+ [types.DELETE_ASSIGNMENT_POLICY, policyId],
+ [types.SET_ASSIGNMENT_POLICIES_UI_FLAG, { isDeleting: false }],
+ ]);
+ });
+
+ it('sends correct actions if API is error', async () => {
+ axios.delete.mockRejectedValue(new Error('Not found'));
+
+ await expect(actions.delete({ commit }, 1)).rejects.toThrow(Error);
+
+ expect(commit.mock.calls).toEqual([
+ [types.SET_ASSIGNMENT_POLICIES_UI_FLAG, { isDeleting: true }],
+ [types.SET_ASSIGNMENT_POLICIES_UI_FLAG, { isDeleting: false }],
+ ]);
+ });
+ });
+
+ describe('#getInboxes', () => {
+ it('sends correct actions if API is success', async () => {
+ const policyId = 1;
+ const inboxData = {
+ inboxes: [
+ { id: 1, name: 'Support' },
+ { id: 2, name: 'Sales' },
+ ],
+ };
+ const camelCasedInboxes = [
+ { id: 1, name: 'Support' },
+ { id: 2, name: 'Sales' },
+ ];
+
+ axios.get.mockResolvedValue({ data: inboxData });
+ camelcaseKeys.mockReturnValue(camelCasedInboxes);
+
+ await actions.getInboxes({ commit }, policyId);
+
+ expect(camelcaseKeys).toHaveBeenCalledWith(inboxData.inboxes);
+ expect(commit.mock.calls).toEqual([
+ [types.SET_ASSIGNMENT_POLICIES_INBOXES_UI_FLAG, { isFetching: true }],
+ [
+ types.SET_ASSIGNMENT_POLICIES_INBOXES,
+ { policyId, inboxes: camelCasedInboxes },
+ ],
+ [types.SET_ASSIGNMENT_POLICIES_INBOXES_UI_FLAG, { isFetching: false }],
+ ]);
+ });
+
+ it('sends correct actions if API fails', async () => {
+ axios.get.mockRejectedValue(new Error('API Error'));
+
+ await expect(actions.getInboxes({ commit }, 1)).rejects.toThrow(Error);
+
+ expect(commit.mock.calls).toEqual([
+ [types.SET_ASSIGNMENT_POLICIES_INBOXES_UI_FLAG, { isFetching: true }],
+ [types.SET_ASSIGNMENT_POLICIES_INBOXES_UI_FLAG, { isFetching: false }],
+ ]);
+ });
+ });
+
+ describe('#setInboxPolicy', () => {
+ it('sends correct actions if API is success', async () => {
+ const responseData = { success: true, policy_id: 2 };
+ const camelCasedData = { success: true, policyId: 2 };
+
+ axios.post.mockResolvedValue({ data: responseData });
+ camelcaseKeys.mockReturnValue(camelCasedData);
+
+ const result = await actions.setInboxPolicy(
+ { commit },
+ { inboxId: 1, policyId: 2 }
+ );
+
+ expect(camelcaseKeys).toHaveBeenCalledWith(responseData);
+ expect(commit.mock.calls).toEqual([
+ [types.ADD_ASSIGNMENT_POLICIES_INBOXES, camelCasedData],
+ ]);
+ expect(result).toEqual(responseData);
+ });
+
+ it('throws error if API fails', async () => {
+ axios.post.mockRejectedValue(new Error('API Error'));
+
+ await expect(
+ actions.setInboxPolicy({ commit }, { inboxId: 1, policyId: 2 })
+ ).rejects.toThrow(Error);
+ });
+ });
+
+ describe('#getInboxPolicy', () => {
+ it('returns camelCased response data if API is success', async () => {
+ const responseData = { policy_id: 1, name: 'Round Robin' };
+ const camelCasedData = { policyId: 1, name: 'Round Robin' };
+
+ axios.get.mockResolvedValue({ data: responseData });
+ camelcaseKeys.mockReturnValue(camelCasedData);
+
+ const result = await actions.getInboxPolicy({}, { inboxId: 1 });
+
+ expect(camelcaseKeys).toHaveBeenCalledWith(responseData);
+ expect(result).toEqual(camelCasedData);
+ });
+
+ it('throws error if API fails', async () => {
+ axios.get.mockRejectedValue(new Error('Not found'));
+
+ await expect(
+ actions.getInboxPolicy({}, { inboxId: 999 })
+ ).rejects.toThrow(Error);
+ });
+ });
+
+ describe('#updateInboxPolicy', () => {
+ it('commits EDIT_ASSIGNMENT_POLICY mutation', async () => {
+ const policy = { id: 1, name: 'Updated Policy' };
+
+ await actions.updateInboxPolicy({ commit }, { policy });
+
+ expect(commit.mock.calls).toEqual([
+ [types.EDIT_ASSIGNMENT_POLICY, policy],
+ ]);
+ });
+
+ it('throws error if commit fails', async () => {
+ commit.mockImplementation(() => {
+ throw new Error('Commit failed');
+ });
+
+ await expect(
+ actions.updateInboxPolicy({ commit }, { policy: {} })
+ ).rejects.toThrow(Error);
+ });
+ });
+
+ describe('#removeInboxPolicy', () => {
+ it('sends correct actions if API is success', async () => {
+ const policyId = 1;
+ const inboxId = 2;
+
+ axios.delete.mockResolvedValue({});
+
+ await actions.removeInboxPolicy({ commit }, { policyId, inboxId });
+
+ expect(commit.mock.calls).toEqual([
+ [types.SET_ASSIGNMENT_POLICIES_INBOXES_UI_FLAG, { isDeleting: true }],
+ [types.DELETE_ASSIGNMENT_POLICIES_INBOXES, { policyId, inboxId }],
+ [types.SET_ASSIGNMENT_POLICIES_INBOXES_UI_FLAG, { isDeleting: false }],
+ ]);
+ });
+
+ it('sends correct actions if API fails', async () => {
+ axios.delete.mockRejectedValue(new Error('Not found'));
+
+ await expect(
+ actions.removeInboxPolicy({ commit }, { policyId: 1, inboxId: 999 })
+ ).rejects.toThrow(Error);
+
+ expect(commit.mock.calls).toEqual([
+ [types.SET_ASSIGNMENT_POLICIES_INBOXES_UI_FLAG, { isDeleting: true }],
+ [types.SET_ASSIGNMENT_POLICIES_INBOXES_UI_FLAG, { isDeleting: false }],
+ ]);
+ });
+ });
+});
diff --git a/app/javascript/dashboard/store/modules/specs/assignmentPolicies/fixtures.js b/app/javascript/dashboard/store/modules/specs/assignmentPolicies/fixtures.js
new file mode 100644
index 000000000..1b5ed25af
--- /dev/null
+++ b/app/javascript/dashboard/store/modules/specs/assignmentPolicies/fixtures.js
@@ -0,0 +1,57 @@
+export default [
+ {
+ id: 1,
+ name: 'Round Robin Policy',
+ description: 'Distributes conversations evenly among agents',
+ assignment_order: 'round_robin',
+ conversation_priority: 'earliest_created',
+ fair_distribution_limit: 100,
+ fair_distribution_window: 3600,
+ enabled: true,
+ assigned_inbox_count: 3,
+ created_at: 1704110400,
+ updated_at: 1704110400,
+ },
+ {
+ id: 2,
+ name: 'Balanced Policy',
+ description: 'Assigns conversations based on agent capacity',
+ assignment_order: 'balanced',
+ conversation_priority: 'longest_waiting',
+ fair_distribution_limit: 50,
+ fair_distribution_window: 1800,
+ enabled: false,
+ assigned_inbox_count: 1,
+ created_at: 1704114000,
+ updated_at: 1704114000,
+ },
+];
+
+export const camelCaseFixtures = [
+ {
+ id: 1,
+ name: 'Round Robin Policy',
+ description: 'Distributes conversations evenly among agents',
+ assignmentOrder: 'round_robin',
+ conversationPriority: 'earliest_created',
+ fairDistributionLimit: 100,
+ fairDistributionWindow: 3600,
+ enabled: true,
+ assignedInboxCount: 3,
+ createdAt: 1704110400,
+ updatedAt: 1704110400,
+ },
+ {
+ id: 2,
+ name: 'Balanced Policy',
+ description: 'Assigns conversations based on agent capacity',
+ assignmentOrder: 'balanced',
+ conversationPriority: 'longest_waiting',
+ fairDistributionLimit: 50,
+ fairDistributionWindow: 1800,
+ enabled: false,
+ assignedInboxCount: 1,
+ createdAt: 1704114000,
+ updatedAt: 1704114000,
+ },
+];
diff --git a/app/javascript/dashboard/store/modules/specs/assignmentPolicies/getters.spec.js b/app/javascript/dashboard/store/modules/specs/assignmentPolicies/getters.spec.js
new file mode 100644
index 000000000..4fd1e7ad7
--- /dev/null
+++ b/app/javascript/dashboard/store/modules/specs/assignmentPolicies/getters.spec.js
@@ -0,0 +1,51 @@
+import { getters } from '../../assignmentPolicies';
+import assignmentPoliciesList from './fixtures';
+
+describe('#getters', () => {
+ it('getAssignmentPolicies', () => {
+ const state = { records: assignmentPoliciesList };
+ expect(getters.getAssignmentPolicies(state)).toEqual(
+ assignmentPoliciesList
+ );
+ });
+
+ it('getUIFlags', () => {
+ const state = {
+ uiFlags: {
+ isFetching: true,
+ isFetchingItem: false,
+ isCreating: false,
+ isUpdating: false,
+ isDeleting: false,
+ },
+ };
+ expect(getters.getUIFlags(state)).toEqual({
+ isFetching: true,
+ isFetchingItem: false,
+ isCreating: false,
+ isUpdating: false,
+ isDeleting: false,
+ });
+ });
+
+ it('getInboxUiFlags', () => {
+ const state = {
+ inboxUiFlags: {
+ isFetching: false,
+ isDeleting: false,
+ },
+ };
+ expect(getters.getInboxUiFlags(state)).toEqual({
+ isFetching: false,
+ isDeleting: false,
+ });
+ });
+
+ it('getAssignmentPolicyById', () => {
+ const state = { records: assignmentPoliciesList };
+ expect(getters.getAssignmentPolicyById(state)(1)).toEqual(
+ assignmentPoliciesList[0]
+ );
+ expect(getters.getAssignmentPolicyById(state)(3)).toEqual({});
+ });
+});
diff --git a/app/javascript/dashboard/store/modules/specs/assignmentPolicies/mutations.spec.js b/app/javascript/dashboard/store/modules/specs/assignmentPolicies/mutations.spec.js
new file mode 100644
index 000000000..b3c029c57
--- /dev/null
+++ b/app/javascript/dashboard/store/modules/specs/assignmentPolicies/mutations.spec.js
@@ -0,0 +1,385 @@
+import { mutations } from '../../assignmentPolicies';
+import types from '../../../mutation-types';
+import assignmentPoliciesList from './fixtures';
+
+describe('#mutations', () => {
+ describe('#SET_ASSIGNMENT_POLICIES_UI_FLAG', () => {
+ it('sets single ui flag', () => {
+ const state = {
+ uiFlags: {
+ isFetching: false,
+ isCreating: false,
+ },
+ };
+
+ mutations[types.SET_ASSIGNMENT_POLICIES_UI_FLAG](state, {
+ isFetching: true,
+ });
+
+ expect(state.uiFlags).toEqual({
+ isFetching: true,
+ isCreating: false,
+ });
+ });
+
+ it('sets multiple ui flags', () => {
+ const state = {
+ uiFlags: {
+ isFetching: false,
+ isCreating: false,
+ isUpdating: false,
+ },
+ };
+
+ mutations[types.SET_ASSIGNMENT_POLICIES_UI_FLAG](state, {
+ isFetching: true,
+ isCreating: true,
+ });
+
+ expect(state.uiFlags).toEqual({
+ isFetching: true,
+ isCreating: true,
+ isUpdating: false,
+ });
+ });
+ });
+
+ describe('#SET_ASSIGNMENT_POLICIES', () => {
+ it('sets assignment policies records', () => {
+ const state = { records: [] };
+
+ mutations[types.SET_ASSIGNMENT_POLICIES](state, assignmentPoliciesList);
+
+ expect(state.records).toEqual(assignmentPoliciesList);
+ });
+
+ it('replaces existing records', () => {
+ const state = { records: [{ id: 999, name: 'Old Policy' }] };
+
+ mutations[types.SET_ASSIGNMENT_POLICIES](state, assignmentPoliciesList);
+
+ expect(state.records).toEqual(assignmentPoliciesList);
+ });
+ });
+
+ describe('#SET_ASSIGNMENT_POLICY', () => {
+ it('sets single assignment policy record', () => {
+ const state = { records: [] };
+
+ mutations[types.SET_ASSIGNMENT_POLICY](state, assignmentPoliciesList[0]);
+
+ expect(state.records).toEqual([assignmentPoliciesList[0]]);
+ });
+
+ it('replaces existing record', () => {
+ const state = { records: [{ id: 1, name: 'Old Policy' }] };
+
+ mutations[types.SET_ASSIGNMENT_POLICY](state, assignmentPoliciesList[0]);
+
+ expect(state.records).toEqual([assignmentPoliciesList[0]]);
+ });
+ });
+
+ describe('#ADD_ASSIGNMENT_POLICY', () => {
+ it('adds new policy to empty records', () => {
+ const state = { records: [] };
+
+ mutations[types.ADD_ASSIGNMENT_POLICY](state, assignmentPoliciesList[0]);
+
+ expect(state.records).toEqual([assignmentPoliciesList[0]]);
+ });
+
+ it('adds new policy to existing records', () => {
+ const state = { records: [assignmentPoliciesList[0]] };
+
+ mutations[types.ADD_ASSIGNMENT_POLICY](state, assignmentPoliciesList[1]);
+
+ expect(state.records).toEqual([
+ assignmentPoliciesList[0],
+ assignmentPoliciesList[1],
+ ]);
+ });
+ });
+
+ describe('#EDIT_ASSIGNMENT_POLICY', () => {
+ it('updates existing policy by id', () => {
+ const state = {
+ records: [
+ { ...assignmentPoliciesList[0] },
+ { ...assignmentPoliciesList[1] },
+ ],
+ };
+
+ const updatedPolicy = {
+ ...assignmentPoliciesList[0],
+ name: 'Updated Policy Name',
+ description: 'Updated Description',
+ };
+
+ mutations[types.EDIT_ASSIGNMENT_POLICY](state, updatedPolicy);
+
+ expect(state.records[0]).toEqual(updatedPolicy);
+ expect(state.records[1]).toEqual(assignmentPoliciesList[1]);
+ });
+
+ it('updates policy with camelCase properties', () => {
+ const camelCasePolicy = {
+ id: 1,
+ name: 'Camel Case Policy',
+ assignmentOrder: 'round_robin',
+ conversationPriority: 'earliest_created',
+ };
+
+ const state = {
+ records: [camelCasePolicy],
+ };
+
+ const updatedPolicy = {
+ ...camelCasePolicy,
+ name: 'Updated Camel Case',
+ assignmentOrder: 'balanced',
+ };
+
+ mutations[types.EDIT_ASSIGNMENT_POLICY](state, updatedPolicy);
+
+ expect(state.records[0]).toEqual(updatedPolicy);
+ });
+
+ it('does nothing if policy id not found', () => {
+ const state = {
+ records: [assignmentPoliciesList[0]],
+ };
+
+ const nonExistentPolicy = {
+ id: 999,
+ name: 'Non-existent',
+ };
+
+ const originalRecords = [...state.records];
+ mutations[types.EDIT_ASSIGNMENT_POLICY](state, nonExistentPolicy);
+
+ expect(state.records).toEqual(originalRecords);
+ });
+ });
+
+ describe('#DELETE_ASSIGNMENT_POLICY', () => {
+ it('deletes policy by id', () => {
+ const state = {
+ records: [assignmentPoliciesList[0], assignmentPoliciesList[1]],
+ };
+
+ mutations[types.DELETE_ASSIGNMENT_POLICY](state, 1);
+
+ expect(state.records).toEqual([assignmentPoliciesList[1]]);
+ });
+
+ it('does nothing if id not found', () => {
+ const state = {
+ records: [assignmentPoliciesList[0]],
+ };
+
+ mutations[types.DELETE_ASSIGNMENT_POLICY](state, 999);
+
+ expect(state.records).toEqual([assignmentPoliciesList[0]]);
+ });
+
+ it('handles empty records', () => {
+ const state = { records: [] };
+
+ mutations[types.DELETE_ASSIGNMENT_POLICY](state, 1);
+
+ expect(state.records).toEqual([]);
+ });
+ });
+
+ describe('#SET_ASSIGNMENT_POLICIES_INBOXES_UI_FLAG', () => {
+ it('sets inbox ui flags', () => {
+ const state = {
+ inboxUiFlags: {
+ isFetching: false,
+ },
+ };
+
+ mutations[types.SET_ASSIGNMENT_POLICIES_INBOXES_UI_FLAG](state, {
+ isFetching: true,
+ });
+
+ expect(state.inboxUiFlags).toEqual({
+ isFetching: true,
+ });
+ });
+
+ it('merges with existing flags', () => {
+ const state = {
+ inboxUiFlags: {
+ isFetching: false,
+ isLoading: true,
+ },
+ };
+
+ mutations[types.SET_ASSIGNMENT_POLICIES_INBOXES_UI_FLAG](state, {
+ isFetching: true,
+ });
+
+ expect(state.inboxUiFlags).toEqual({
+ isFetching: true,
+ isLoading: true,
+ });
+ });
+ });
+
+ describe('#SET_ASSIGNMENT_POLICIES_INBOXES', () => {
+ it('sets inboxes for existing policy', () => {
+ const mockInboxes = [
+ { id: 1, name: 'Support Inbox' },
+ { id: 2, name: 'Sales Inbox' },
+ ];
+
+ const state = {
+ records: [
+ { id: 1, name: 'Policy 1', inboxes: [] },
+ { id: 2, name: 'Policy 2', inboxes: [] },
+ ],
+ };
+
+ mutations[types.SET_ASSIGNMENT_POLICIES_INBOXES](state, {
+ policyId: 1,
+ inboxes: mockInboxes,
+ });
+
+ expect(state.records[0].inboxes).toEqual(mockInboxes);
+ expect(state.records[1].inboxes).toEqual([]);
+ });
+
+ it('replaces existing inboxes', () => {
+ const oldInboxes = [{ id: 99, name: 'Old Inbox' }];
+ const newInboxes = [{ id: 1, name: 'New Inbox' }];
+
+ const state = {
+ records: [{ id: 1, name: 'Policy 1', inboxes: oldInboxes }],
+ };
+
+ mutations[types.SET_ASSIGNMENT_POLICIES_INBOXES](state, {
+ policyId: 1,
+ inboxes: newInboxes,
+ });
+
+ expect(state.records[0].inboxes).toEqual(newInboxes);
+ });
+
+ it('does nothing if policy not found', () => {
+ const state = {
+ records: [{ id: 1, name: 'Policy 1', inboxes: [] }],
+ };
+
+ const originalState = JSON.parse(JSON.stringify(state));
+
+ mutations[types.SET_ASSIGNMENT_POLICIES_INBOXES](state, {
+ policyId: 999,
+ inboxes: [{ id: 1, name: 'Test' }],
+ });
+
+ expect(state).toEqual(originalState);
+ });
+ });
+
+ describe('#DELETE_ASSIGNMENT_POLICIES_INBOXES', () => {
+ it('removes inbox from policy', () => {
+ const mockInboxes = [
+ { id: 1, name: 'Support Inbox' },
+ { id: 2, name: 'Sales Inbox' },
+ { id: 3, name: 'Marketing Inbox' },
+ ];
+
+ const state = {
+ records: [
+ { id: 1, name: 'Policy 1', inboxes: mockInboxes },
+ { id: 2, name: 'Policy 2', inboxes: [] },
+ ],
+ };
+
+ mutations[types.DELETE_ASSIGNMENT_POLICIES_INBOXES](state, {
+ policyId: 1,
+ inboxId: 2,
+ });
+
+ expect(state.records[0].inboxes).toEqual([
+ { id: 1, name: 'Support Inbox' },
+ { id: 3, name: 'Marketing Inbox' },
+ ]);
+ expect(state.records[1].inboxes).toEqual([]);
+ });
+
+ it('does nothing if policy not found', () => {
+ const state = {
+ records: [
+ { id: 1, name: 'Policy 1', inboxes: [{ id: 1, name: 'Test' }] },
+ ],
+ };
+
+ const originalState = JSON.parse(JSON.stringify(state));
+
+ mutations[types.DELETE_ASSIGNMENT_POLICIES_INBOXES](state, {
+ policyId: 999,
+ inboxId: 1,
+ });
+
+ expect(state).toEqual(originalState);
+ });
+
+ it('does nothing if inbox not found in policy', () => {
+ const mockInboxes = [{ id: 1, name: 'Support Inbox' }];
+
+ const state = {
+ records: [{ id: 1, name: 'Policy 1', inboxes: mockInboxes }],
+ };
+
+ mutations[types.DELETE_ASSIGNMENT_POLICIES_INBOXES](state, {
+ policyId: 1,
+ inboxId: 999,
+ });
+
+ expect(state.records[0].inboxes).toEqual(mockInboxes);
+ });
+
+ it('handles policy with no inboxes', () => {
+ const state = {
+ records: [{ id: 1, name: 'Policy 1' }],
+ };
+
+ mutations[types.DELETE_ASSIGNMENT_POLICIES_INBOXES](state, {
+ policyId: 1,
+ inboxId: 1,
+ });
+
+ expect(state.records[0]).toEqual({ id: 1, name: 'Policy 1' });
+ });
+ });
+
+ describe('#ADD_ASSIGNMENT_POLICIES_INBOXES', () => {
+ it('updates policy attributes using MutationHelpers.updateAttributes', () => {
+ const state = {
+ records: [
+ { id: 1, name: 'Policy 1', assignedInboxCount: 2 },
+ { id: 2, name: 'Policy 2', assignedInboxCount: 1 },
+ ],
+ };
+
+ const updatedPolicy = {
+ id: 1,
+ name: 'Policy 1',
+ assignedInboxCount: 3,
+ inboxes: [{ id: 1, name: 'New Inbox' }],
+ };
+
+ mutations[types.ADD_ASSIGNMENT_POLICIES_INBOXES](state, updatedPolicy);
+
+ expect(state.records[0]).toEqual(updatedPolicy);
+ expect(state.records[1]).toEqual({
+ id: 2,
+ name: 'Policy 2',
+ assignedInboxCount: 1,
+ });
+ });
+ });
+});
diff --git a/app/javascript/dashboard/store/mutation-types.js b/app/javascript/dashboard/store/mutation-types.js
index a63fec2d1..4f361e140 100644
--- a/app/javascript/dashboard/store/mutation-types.js
+++ b/app/javascript/dashboard/store/mutation-types.js
@@ -348,4 +348,34 @@ export default {
SET_TEAM_CONVERSATION_METRIC: 'SET_TEAM_CONVERSATION_METRIC',
TOGGLE_TEAM_CONVERSATION_METRIC_LOADING:
'TOGGLE_TEAM_CONVERSATION_METRIC_LOADING',
+
+ // Assignment Policies
+ SET_ASSIGNMENT_POLICIES_UI_FLAG: 'SET_ASSIGNMENT_POLICIES_UI_FLAG',
+ SET_ASSIGNMENT_POLICIES: 'SET_ASSIGNMENT_POLICIES',
+ SET_ASSIGNMENT_POLICY: 'SET_ASSIGNMENT_POLICY',
+ ADD_ASSIGNMENT_POLICY: 'ADD_ASSIGNMENT_POLICY',
+ EDIT_ASSIGNMENT_POLICY: 'EDIT_ASSIGNMENT_POLICY',
+ DELETE_ASSIGNMENT_POLICY: 'DELETE_ASSIGNMENT_POLICY',
+ SET_ASSIGNMENT_POLICIES_INBOXES: 'SET_ASSIGNMENT_POLICIES_INBOXES',
+ SET_ASSIGNMENT_POLICIES_INBOXES_UI_FLAG:
+ 'SET_ASSIGNMENT_POLICIES_INBOXES_UI_FLAG',
+ DELETE_ASSIGNMENT_POLICIES_INBOXES: 'DELETE_ASSIGNMENT_POLICIES_INBOXES',
+ ADD_ASSIGNMENT_POLICIES_INBOXES: 'ADD_ASSIGNMENT_POLICIES_INBOXES',
+
+ // Agent Capacity Policies
+ SET_AGENT_CAPACITY_POLICIES_UI_FLAG: 'SET_AGENT_CAPACITY_POLICIES_UI_FLAG',
+ SET_AGENT_CAPACITY_POLICIES: 'SET_AGENT_CAPACITY_POLICIES',
+ SET_AGENT_CAPACITY_POLICY: 'SET_AGENT_CAPACITY_POLICY',
+ ADD_AGENT_CAPACITY_POLICY: 'ADD_AGENT_CAPACITY_POLICY',
+ EDIT_AGENT_CAPACITY_POLICY: 'EDIT_AGENT_CAPACITY_POLICY',
+ DELETE_AGENT_CAPACITY_POLICY: 'DELETE_AGENT_CAPACITY_POLICY',
+ SET_AGENT_CAPACITY_POLICIES_USERS: 'SET_AGENT_CAPACITY_POLICIES_USERS',
+ SET_AGENT_CAPACITY_POLICIES_USERS_UI_FLAG:
+ 'SET_AGENT_CAPACITY_POLICIES_USERS_UI_FLAG',
+ ADD_AGENT_CAPACITY_POLICIES_USERS: 'ADD_AGENT_CAPACITY_POLICIES_USERS',
+ DELETE_AGENT_CAPACITY_POLICIES_USERS: 'DELETE_AGENT_CAPACITY_POLICIES_USERS',
+ SET_AGENT_CAPACITY_POLICIES_INBOXES: 'SET_AGENT_CAPACITY_POLICIES_INBOXES',
+ EDIT_AGENT_CAPACITY_POLICIES_INBOXES: 'EDIT_AGENT_CAPACITY_POLICIES_INBOXES',
+ DELETE_AGENT_CAPACITY_POLICIES_INBOXES:
+ 'DELETE_AGENT_CAPACITY_POLICIES_INBOXES',
};
diff --git a/app/javascript/shared/helpers/clipboard.js b/app/javascript/shared/helpers/clipboard.js
index dcd25afae..3b66c39ac 100644
--- a/app/javascript/shared/helpers/clipboard.js
+++ b/app/javascript/shared/helpers/clipboard.js
@@ -17,3 +17,22 @@ export const copyTextToClipboard = async data => {
throw new Error(`Unable to copy text to clipboard: ${error.message}`);
}
};
+
+/**
+ * Handles OTP paste events by extracting numeric digits from clipboard data.
+ *
+ * @param {ClipboardEvent} event - The paste event from the clipboard
+ * @param {number} maxLength - Maximum number of digits to extract (default: 6)
+ * @returns {string|null} - Extracted numeric string or null if invalid
+ */
+export const handleOtpPaste = (event, maxLength = 6) => {
+ if (!event?.clipboardData) return null;
+
+ const pastedData = event.clipboardData
+ .getData('text')
+ .replace(/\D/g, '') // Remove all non-digit characters
+ .slice(0, maxLength); // Limit to maxLength digits
+
+ // Only return if we have the exact expected length
+ return pastedData.length === maxLength ? pastedData : null;
+};
diff --git a/app/javascript/shared/helpers/specs/clipboard.spec.js b/app/javascript/shared/helpers/specs/clipboard.spec.js
index c675edd35..a169e0939 100644
--- a/app/javascript/shared/helpers/specs/clipboard.spec.js
+++ b/app/javascript/shared/helpers/specs/clipboard.spec.js
@@ -1,4 +1,4 @@
-import { copyTextToClipboard } from '../clipboard';
+import { copyTextToClipboard, handleOtpPaste } from '../clipboard';
const mockWriteText = vi.fn();
Object.assign(navigator, {
@@ -172,3 +172,113 @@ describe('copyTextToClipboard', () => {
});
});
});
+
+describe('handleOtpPaste', () => {
+ // Helper function to create mock clipboard event
+ const createMockPasteEvent = text => ({
+ clipboardData: {
+ getData: vi.fn().mockReturnValue(text),
+ },
+ });
+
+ describe('valid OTP paste scenarios', () => {
+ it('extracts 6-digit OTP from clean numeric string', () => {
+ const event = createMockPasteEvent('123456');
+ const result = handleOtpPaste(event);
+
+ expect(result).toBe('123456');
+ expect(event.clipboardData.getData).toHaveBeenCalledWith('text');
+ });
+
+ it('extracts 6-digit OTP from string with spaces', () => {
+ const event = createMockPasteEvent('1 2 3 4 5 6');
+ const result = handleOtpPaste(event);
+
+ expect(result).toBe('123456');
+ });
+
+ it('extracts 6-digit OTP from string with dashes', () => {
+ const event = createMockPasteEvent('123-456');
+ const result = handleOtpPaste(event);
+
+ expect(result).toBe('123456');
+ });
+
+ it('handles negative numbers by extracting digits only', () => {
+ const event = createMockPasteEvent('-123456');
+ const result = handleOtpPaste(event);
+
+ expect(result).toBe('123456');
+ });
+
+ it('handles decimal numbers by extracting digits only', () => {
+ const event = createMockPasteEvent('123.456');
+ const result = handleOtpPaste(event);
+
+ expect(result).toBe('123456');
+ });
+
+ it('extracts 6-digit OTP from mixed alphanumeric string', () => {
+ const event = createMockPasteEvent('Your code is: 987654');
+ const result = handleOtpPaste(event);
+
+ expect(result).toBe('987654');
+ });
+
+ it('extracts first 6 digits when more than 6 digits present', () => {
+ const event = createMockPasteEvent('12345678901234');
+ const result = handleOtpPaste(event);
+
+ expect(result).toBe('123456');
+ });
+
+ it('handles custom maxLength parameter', () => {
+ const event = createMockPasteEvent('12345678');
+ const result = handleOtpPaste(event, 8);
+
+ expect(result).toBe('12345678');
+ });
+
+ it('extracts 4-digit OTP with custom maxLength', () => {
+ const event = createMockPasteEvent('Your PIN: 9876');
+ const result = handleOtpPaste(event, 4);
+
+ expect(result).toBe('9876');
+ });
+ });
+
+ describe('invalid OTP paste scenarios', () => {
+ it('returns null for insufficient digits', () => {
+ const event = createMockPasteEvent('12345');
+ const result = handleOtpPaste(event);
+
+ expect(result).toBeNull();
+ });
+
+ it('returns null for text with no digits', () => {
+ const event = createMockPasteEvent('Hello World');
+ const result = handleOtpPaste(event);
+
+ expect(result).toBeNull();
+ });
+
+ it('returns null for empty string', () => {
+ const event = createMockPasteEvent('');
+ const result = handleOtpPaste(event);
+
+ expect(result).toBeNull();
+ });
+
+ it('returns null when event is null', () => {
+ const result = handleOtpPaste(null);
+
+ expect(result).toBeNull();
+ });
+
+ it('returns null when event is undefined', () => {
+ const result = handleOtpPaste(undefined);
+
+ expect(result).toBeNull();
+ });
+ });
+});
diff --git a/app/javascript/shared/helpers/specs/timeHelper.spec.js b/app/javascript/shared/helpers/specs/timeHelper.spec.js
index 13d04568f..e7a4e025f 100644
--- a/app/javascript/shared/helpers/specs/timeHelper.spec.js
+++ b/app/javascript/shared/helpers/specs/timeHelper.spec.js
@@ -4,6 +4,8 @@ import {
dynamicTime,
dateFormat,
shortTimestamp,
+ getDayDifferenceFromNow,
+ hasOneDayPassed,
} from 'shared/helpers/timeHelper';
beforeEach(() => {
@@ -90,3 +92,148 @@ describe('#shortTimestamp', () => {
expect(shortTimestamp('4 years ago', true)).toEqual('4y ago');
});
});
+
+describe('#getDayDifferenceFromNow', () => {
+ it('returns 0 for timestamps from today', () => {
+ // Mock current date: May 5, 2023
+ const now = new Date(Date.UTC(2023, 4, 5, 12, 0, 0)); // 12:00 PM
+ const todayTimestamp = Math.floor(now.getTime() / 1000); // Same day
+
+ expect(getDayDifferenceFromNow(now, todayTimestamp)).toEqual(0);
+ });
+
+ it('returns 2 for timestamps from 2 days ago', () => {
+ const now = new Date(Date.UTC(2023, 4, 5, 12, 0, 0)); // May 5, 2023
+ const twoDaysAgoTimestamp = Math.floor(
+ new Date(Date.UTC(2023, 4, 3, 10, 0, 0)).getTime() / 1000
+ ); // May 3, 2023
+
+ expect(getDayDifferenceFromNow(now, twoDaysAgoTimestamp)).toEqual(2);
+ });
+
+ it('returns 7 for timestamps from a week ago', () => {
+ const now = new Date(Date.UTC(2023, 4, 5, 12, 0, 0)); // May 5, 2023
+ const weekAgoTimestamp = Math.floor(
+ new Date(Date.UTC(2023, 3, 28, 8, 0, 0)).getTime() / 1000
+ ); // April 28, 2023
+
+ expect(getDayDifferenceFromNow(now, weekAgoTimestamp)).toEqual(7);
+ });
+
+ it('returns 30 for timestamps from a month ago', () => {
+ const now = new Date(Date.UTC(2023, 4, 5, 12, 0, 0)); // May 5, 2023
+ const monthAgoTimestamp = Math.floor(
+ new Date(Date.UTC(2023, 3, 5, 12, 0, 0)).getTime() / 1000
+ ); // April 5, 2023
+
+ expect(getDayDifferenceFromNow(now, monthAgoTimestamp)).toEqual(30);
+ });
+
+ it('handles edge case with different times on same day', () => {
+ const now = new Date(Date.UTC(2023, 4, 5, 23, 59, 59)); // May 5, 2023 11:59:59 PM
+ const morningTimestamp = Math.floor(
+ new Date(Date.UTC(2023, 4, 5, 0, 0, 1)).getTime() / 1000
+ ); // May 5, 2023 12:00:01 AM
+
+ expect(getDayDifferenceFromNow(now, morningTimestamp)).toEqual(0);
+ });
+
+ it('handles cross-month boundaries correctly', () => {
+ const now = new Date(Date.UTC(2023, 4, 1, 12, 0, 0)); // May 1, 2023
+ const lastMonthTimestamp = Math.floor(
+ new Date(Date.UTC(2023, 3, 30, 12, 0, 0)).getTime() / 1000
+ ); // April 30, 2023
+
+ expect(getDayDifferenceFromNow(now, lastMonthTimestamp)).toEqual(1);
+ });
+
+ it('handles cross-year boundaries correctly', () => {
+ const now = new Date(Date.UTC(2023, 0, 2, 12, 0, 0)); // January 2, 2023
+ const lastYearTimestamp = Math.floor(
+ new Date(Date.UTC(2022, 11, 31, 12, 0, 0)).getTime() / 1000
+ ); // December 31, 2022
+
+ expect(getDayDifferenceFromNow(now, lastYearTimestamp)).toEqual(2);
+ });
+});
+
+describe('#hasOneDayPassed', () => {
+ beforeEach(() => {
+ // Mock current date: May 5, 2023, 12:00 PM UTC (1683288000)
+ const mockDate = new Date(1683288000 * 1000);
+ vi.setSystemTime(mockDate);
+ });
+
+ it('returns false for timestamps from today', () => {
+ // Same day, different time - May 5, 2023 8:00 AM UTC
+ const todayTimestamp = 1683273600;
+
+ expect(hasOneDayPassed(todayTimestamp)).toBe(false);
+ });
+
+ it('returns false for timestamps from yesterday (less than 24 hours)', () => {
+ // Yesterday but less than 24 hours ago - May 4, 2023 6:00 PM UTC (18 hours ago)
+ const yesterdayTimestamp = 1683230400;
+
+ expect(hasOneDayPassed(yesterdayTimestamp)).toBe(false);
+ });
+
+ it('returns true for timestamps from exactly 1 day ago', () => {
+ // Exactly 24 hours ago - May 4, 2023 12:00 PM UTC
+ const oneDayAgoTimestamp = 1683201600;
+
+ expect(hasOneDayPassed(oneDayAgoTimestamp)).toBe(true);
+ });
+
+ it('returns true for timestamps from more than 1 day ago', () => {
+ // 2 days ago - May 3, 2023 10:00 AM UTC
+ const twoDaysAgoTimestamp = 1683108000;
+
+ expect(hasOneDayPassed(twoDaysAgoTimestamp)).toBe(true);
+ });
+
+ it('returns true for timestamps from a week ago', () => {
+ // 7 days ago - April 28, 2023 8:00 AM UTC
+ const weekAgoTimestamp = 1682668800;
+
+ expect(hasOneDayPassed(weekAgoTimestamp)).toBe(true);
+ });
+
+ it('returns true for null timestamp (defensive check)', () => {
+ expect(hasOneDayPassed(null)).toBe(true);
+ });
+
+ it('returns true for undefined timestamp (defensive check)', () => {
+ expect(hasOneDayPassed(undefined)).toBe(true);
+ });
+
+ it('returns true for zero timestamp (defensive check)', () => {
+ expect(hasOneDayPassed(0)).toBe(true);
+ });
+
+ it('returns true for empty string timestamp (defensive check)', () => {
+ expect(hasOneDayPassed('')).toBe(true);
+ });
+
+ it('handles cross-month boundaries correctly', () => {
+ // Set current time to May 1, 2023 12:00 PM UTC (1682942400)
+ const mayFirst = new Date(1682942400 * 1000);
+ vi.setSystemTime(mayFirst);
+
+ // April 29, 2023 12:00 PM UTC (1682769600) - 2 days ago, crossing month boundary
+ const crossMonthTimestamp = 1682769600;
+
+ expect(hasOneDayPassed(crossMonthTimestamp)).toBe(true);
+ });
+
+ it('handles cross-year boundaries correctly', () => {
+ // Set current time to January 2, 2023 12:00 PM UTC (1672660800)
+ const newYear = new Date(1672660800 * 1000);
+ vi.setSystemTime(newYear);
+
+ // December 30, 2022 12:00 PM UTC (1672401600) - 3 days ago, crossing year boundary
+ const crossYearTimestamp = 1672401600;
+
+ expect(hasOneDayPassed(crossYearTimestamp)).toBe(true);
+ });
+});
diff --git a/app/javascript/shared/helpers/timeHelper.js b/app/javascript/shared/helpers/timeHelper.js
index 6e041c7ec..5347d2410 100644
--- a/app/javascript/shared/helpers/timeHelper.js
+++ b/app/javascript/shared/helpers/timeHelper.js
@@ -3,6 +3,7 @@ import {
isSameYear,
fromUnixTime,
formatDistanceToNow,
+ differenceInDays,
} from 'date-fns';
/**
@@ -91,3 +92,25 @@ export const shortTimestamp = (time, withAgo = false) => {
.replace(' years ago', `y${suffix}`);
return convertToShortTime;
};
+
+/**
+ * Calculates the difference in days between now and a given timestamp.
+ * @param {Date} now - Current date/time.
+ * @param {number} timestampInSeconds - Unix timestamp in seconds.
+ * @returns {number} Number of days difference.
+ */
+export const getDayDifferenceFromNow = (now, timestampInSeconds) => {
+ const date = new Date(timestampInSeconds * 1000);
+ return differenceInDays(now, date);
+};
+
+/**
+ * Checks if more than 24 hours have passed since a given timestamp.
+ * Useful for determining if retry/refresh actions should be disabled.
+ * @param {number} timestamp - Unix timestamp.
+ * @returns {boolean} True if more than 24 hours have passed.
+ */
+export const hasOneDayPassed = timestamp => {
+ if (!timestamp) return true; // Defensive check
+ return getDayDifferenceFromNow(new Date(), timestamp) >= 1;
+};
diff --git a/app/javascript/shared/store/globalConfig.js b/app/javascript/shared/store/globalConfig.js
index 608a31ec1..8abeba123 100644
--- a/app/javascript/shared/store/globalConfig.js
+++ b/app/javascript/shared/store/globalConfig.js
@@ -1,3 +1,5 @@
+import { parseBoolean } from '@chatwoot/utils';
+
const {
API_CHANNEL_NAME: apiChannelName,
API_CHANNEL_THUMBNAIL: apiChannelThumbnail,
@@ -15,6 +17,7 @@ const {
LOGO: logo,
LOGO_DARK: logoDark,
PRIVACY_URL: privacyURL,
+ IS_ENTERPRISE: isEnterprise,
TERMS_URL: termsURL,
WIDGET_BRAND_URL: widgetBrandURL,
DISABLE_USER_PROFILE_UPDATE: disableUserProfileUpdate,
@@ -30,8 +33,8 @@ const state = {
chatwootInboxToken,
deploymentEnv,
createNewAccountFromDashboard,
- directUploadsEnabled: directUploadsEnabled === 'true',
- disableUserProfileUpdate: disableUserProfileUpdate === 'true',
+ directUploadsEnabled: parseBoolean(directUploadsEnabled),
+ disableUserProfileUpdate: parseBoolean(disableUserProfileUpdate),
displayManifest,
gitSha,
hCaptchaSiteKey,
@@ -42,6 +45,7 @@ const state = {
privacyURL,
termsURL,
widgetBrandURL,
+ isEnterprise: parseBoolean(isEnterprise),
};
export const getters = {
diff --git a/app/javascript/v3/App.vue b/app/javascript/v3/App.vue
index 992c0b381..ef7107beb 100644
--- a/app/javascript/v3/App.vue
+++ b/app/javascript/v3/App.vue
@@ -15,8 +15,10 @@ export default {
setColorTheme() {
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
this.theme = 'dark';
+ document.documentElement.classList.add('dark');
} else {
- this.theme = 'light ';
+ this.theme = 'light';
+ document.documentElement.classList.remove('dark');
}
},
listenToThemeChanges() {
@@ -25,8 +27,10 @@ export default {
mql.onchange = e => {
if (e.matches) {
this.theme = 'dark';
+ document.documentElement.classList.add('dark');
} else {
this.theme = 'light';
+ document.documentElement.classList.remove('dark');
}
};
},
diff --git a/app/javascript/v3/api/auth.js b/app/javascript/v3/api/auth.js
index a4793d3d0..146db4e07 100644
--- a/app/javascript/v3/api/auth.js
+++ b/app/javascript/v3/api/auth.js
@@ -13,6 +13,16 @@ export const login = async ({
}) => {
try {
const response = await wootAPI.post('auth/sign_in', credentials);
+
+ // Check if MFA is required
+ if (response.status === 206 && response.data.mfa_required) {
+ // Return MFA data instead of throwing error
+ return {
+ mfaRequired: true,
+ mfaToken: response.data.mfa_token,
+ };
+ }
+
setAuthCredentials(response);
clearLocalStorageOnLogout();
window.location = getLoginRedirectURL({
@@ -20,8 +30,17 @@ export const login = async ({
ssoConversationId,
user: response.data.data,
});
+ return null;
} catch (error) {
+ // Check if it's an MFA required response
+ if (error.response?.status === 206 && error.response?.data?.mfa_required) {
+ return {
+ mfaRequired: true,
+ mfaToken: error.response.data.mfa_token,
+ };
+ }
throwErrorMessage(error);
+ return null;
}
};
diff --git a/app/javascript/v3/components/Form/Input.vue b/app/javascript/v3/components/Form/Input.vue
index 674b1a59a..4a0b0dc63 100644
--- a/app/javascript/v3/components/Form/Input.vue
+++ b/app/javascript/v3/components/Form/Input.vue
@@ -55,6 +55,7 @@ const model = defineModel({
- {{ $t('REGISTER.HAVE_AN_ACCOUNT') }}
+ {{ $t('REGISTER.HAVE_AN_ACCOUNT') }}
{{ replaceInstallationName($t('LOGIN.TITLE')) }}
diff --git a/app/javascript/v3/views/login/Index.vue b/app/javascript/v3/views/login/Index.vue
index 4a51b3059..926424cba 100644
--- a/app/javascript/v3/views/login/Index.vue
+++ b/app/javascript/v3/views/login/Index.vue
@@ -15,6 +15,7 @@ import FormInput from '../../components/Form/Input.vue';
import GoogleOAuthButton from '../../components/GoogleOauth/Button.vue';
import Spinner from 'shared/components/Spinner.vue';
import NextButton from 'dashboard/components-next/button/Button.vue';
+import MfaVerification from 'dashboard/components/auth/MfaVerification.vue';
const ERROR_MESSAGES = {
'no-account-found': 'LOGIN.OAUTH.NO_ACCOUNT_FOUND',
@@ -29,6 +30,7 @@ export default {
GoogleOAuthButton,
Spinner,
NextButton,
+ MfaVerification,
},
props: {
ssoAuthToken: { type: String, default: '' },
@@ -58,6 +60,8 @@ export default {
hasErrored: false,
},
error: '',
+ mfaRequired: false,
+ mfaToken: null,
};
},
validations() {
@@ -81,14 +85,19 @@ export default {
showSignupLink() {
return parseBoolean(window.chatwootConfig.signupEnabled);
},
+ showSamlLogin() {
+ return this.globalConfig.isEnterprise;
+ },
},
created() {
if (this.ssoAuthToken) {
this.submitLogin();
}
if (this.authError) {
- const message = ERROR_MESSAGES[this.authError] ?? 'LOGIN.API.UNAUTH';
- useAlert(this.$t(message));
+ const messageKey = ERROR_MESSAGES[this.authError] ?? 'LOGIN.API.UNAUTH';
+ // Use a method to get the translated text to avoid dynamic key warning
+ const translatedMessage = this.getTranslatedMessage(messageKey);
+ useAlert(translatedMessage);
// wait for idle state
this.requestIdleCallbackPolyfill(() => {
// Remove the error query param from the url
@@ -98,6 +107,18 @@ export default {
}
},
methods: {
+ getTranslatedMessage(key) {
+ // Avoid dynamic key warning by handling each case explicitly
+ switch (key) {
+ case 'LOGIN.OAUTH.NO_ACCOUNT_FOUND':
+ return this.$t('LOGIN.OAUTH.NO_ACCOUNT_FOUND');
+ case 'LOGIN.OAUTH.BUSINESS_ACCOUNTS_ONLY':
+ return this.$t('LOGIN.OAUTH.BUSINESS_ACCOUNTS_ONLY');
+ case 'LOGIN.API.UNAUTH':
+ default:
+ return this.$t('LOGIN.API.UNAUTH');
+ }
+ },
// TODO: Remove this when Safari gets wider support
// Ref: https://caniuse.com/requestidlecallback
//
@@ -140,7 +161,15 @@ export default {
};
login(credentials)
- .then(() => {
+ .then(result => {
+ // Check if MFA is required
+ if (result?.mfaRequired) {
+ this.loginApi.showLoading = false;
+ this.mfaRequired = true;
+ this.mfaToken = result.mfaToken;
+ return;
+ }
+
this.handleImpersonation();
this.showAlertMessage(this.$t('LOGIN.API.SUCCESS_MESSAGE'));
})
@@ -163,6 +192,17 @@ export default {
this.submitLogin();
},
+ handleMfaVerified() {
+ // MFA verification successful, continue with login
+ this.handleImpersonation();
+ window.location = '/app';
+ },
+ handleMfaCancel() {
+ // User cancelled MFA, reset state
+ this.mfaRequired = false;
+ this.mfaToken = null;
+ this.credentials.password = '';
+ },
},
};
@@ -193,7 +233,19 @@ export default {
+
+
+
+
+
+
+
+ {{ $t('LOGIN.SAML.LABEL') }}
+
+
diff --git a/app/javascript/v3/views/login/Saml.vue b/app/javascript/v3/views/login/Saml.vue
new file mode 100644
index 000000000..fc4d75494
--- /dev/null
+++ b/app/javascript/v3/views/login/Saml.vue
@@ -0,0 +1,102 @@
+
+
+
+
+
+
+
+
+ {{ t('LOGIN.SAML.TITLE') }}
+
+
+
+
+
+ {{ t('LOGIN.SAML.BACK_TO_LOGIN') }}
+
+
+
+
diff --git a/app/javascript/v3/views/routes.js b/app/javascript/v3/views/routes.js
index e1f14c78e..6b975e09b 100644
--- a/app/javascript/v3/views/routes.js
+++ b/app/javascript/v3/views/routes.js
@@ -1,6 +1,7 @@
import { frontendURL } from 'dashboard/helper/URLHelper';
import Login from './login/Index.vue';
+import SamlLogin from './login/Saml.vue';
import Signup from './auth/signup/Index.vue';
import ResetPassword from './auth/reset/password/Index.vue';
import Confirmation from './auth/confirmation/Index.vue';
@@ -20,6 +21,12 @@ export default [
authError: route.query.error,
}),
},
+ {
+ path: frontendURL('login/sso'),
+ name: 'sso_login',
+ component: SamlLogin,
+ meta: { requireEnterprise: true },
+ },
{
path: frontendURL('auth/signup'),
name: 'auth_signup',
diff --git a/app/javascript/widget/components/pageComponents/Home/Article/ArticleContainer.vue b/app/javascript/widget/components/pageComponents/Home/Article/ArticleContainer.vue
index 07dcd2220..6cfaf8fac 100644
--- a/app/javascript/widget/components/pageComponents/Home/Article/ArticleContainer.vue
+++ b/app/javascript/widget/components/pageComponents/Home/Article/ArticleContainer.vue
@@ -21,6 +21,9 @@ const articleUiFlags = useMapGetter('article/uiFlags');
const locale = computed(() => {
const { locale: selectedLocale } = i18n;
+
+ if (!portal.value || !portal.value.config) return null;
+
const { allowed_locales: allowedLocales } = portal.value.config;
return getMatchingLocale(selectedLocale.value, allowedLocales);
});
diff --git a/app/javascript/widget/i18n/locale/ar.json b/app/javascript/widget/i18n/locale/ar.json
index 110fadcd1..1424b7418 100644
--- a/app/javascript/widget/i18n/locale/ar.json
+++ b/app/javascript/widget/i18n/locale/ar.json
@@ -14,13 +14,13 @@
},
"THUMBNAIL": {
"AUTHOR": {
- "NOT_AVAILABLE": "Not available"
+ "NOT_AVAILABLE": "غير متاح"
}
},
"TEAM_AVAILABILITY": {
"ONLINE": "متواجدون لخدمتك",
"OFFLINE": "نحن بعيدون في الوقت الحالي",
- "BACK_AS_SOON_AS_POSSIBLE": "We will be back as soon as possible"
+ "BACK_AS_SOON_AS_POSSIBLE": "سوف نعود في أقرب وقت ممكن"
},
"REPLY_TIME": {
"IN_A_FEW_MINUTES": "عادة نقوم بالرد خلال بضع دقائق",
@@ -30,7 +30,7 @@
"BACK_IN_MINUTES": "We will be back online in {time} minutes",
"BACK_AT_TIME": "We will be back online at {time}",
"BACK_ON_DAY": "We will be back online on {day}",
- "BACK_TOMORROW": "We will be back online tomorrow",
+ "BACK_TOMORROW": "سنكون متاحين غدًا للرد على استفساراتك",
"BACK_IN_SOME_TIME": "We will be back online in some time"
},
"DAY_NAMES": {
diff --git a/app/javascript/widget/i18n/locale/fa.json b/app/javascript/widget/i18n/locale/fa.json
index 9fdbd0a38..c4d1bcc1c 100644
--- a/app/javascript/widget/i18n/locale/fa.json
+++ b/app/javascript/widget/i18n/locale/fa.json
@@ -14,24 +14,24 @@
},
"THUMBNAIL": {
"AUTHOR": {
- "NOT_AVAILABLE": "Not available"
+ "NOT_AVAILABLE": "خارج از دسترس"
}
},
"TEAM_AVAILABILITY": {
"ONLINE": "ما آنلاین هستیم",
"OFFLINE": "در حال حاضر دردسترس نیستیم",
- "BACK_AS_SOON_AS_POSSIBLE": "We will be back as soon as possible"
+ "BACK_AS_SOON_AS_POSSIBLE": "در سریعترین زمان ممکن باز خواهیم گشت"
},
"REPLY_TIME": {
"IN_A_FEW_MINUTES": "معمولاً در عرض چند دقیقه پاسخ می دهند",
"IN_A_FEW_HOURS": "معمولاً در عرض چند ساعت پاسخ می دهند",
"IN_A_DAY": "به طور معمول در یک روز پاسخ می دهند",
- "BACK_IN_HOURS": "We will be back online in {n} hour | We will be back online in {n} hours",
- "BACK_IN_MINUTES": "We will be back online in {time} minutes",
- "BACK_AT_TIME": "We will be back online at {time}",
- "BACK_ON_DAY": "We will be back online on {day}",
- "BACK_TOMORROW": "We will be back online tomorrow",
- "BACK_IN_SOME_TIME": "We will be back online in some time"
+ "BACK_IN_HOURS": "تا {n} ساعت دیگر باز خواهیم گشت | تا {n} ساعت دیگر باز خواهیم گشت",
+ "BACK_IN_MINUTES": "تا {time} دقیقه دیگر باز خواهیم گشت",
+ "BACK_AT_TIME": "در {time} باز خواهیم گشت",
+ "BACK_ON_DAY": "در روز {day} باز خواهیم گشت",
+ "BACK_TOMORROW": "فردا باز خواهیم گشت",
+ "BACK_IN_SOME_TIME": "مدتی دیگر باز خواهیم گشت"
},
"DAY_NAMES": {
"SUNDAY": "یکشنبه",
diff --git a/app/javascript/widget/i18n/locale/fr.json b/app/javascript/widget/i18n/locale/fr.json
index 578d0217f..90275e02a 100644
--- a/app/javascript/widget/i18n/locale/fr.json
+++ b/app/javascript/widget/i18n/locale/fr.json
@@ -14,24 +14,24 @@
},
"THUMBNAIL": {
"AUTHOR": {
- "NOT_AVAILABLE": "Not available"
+ "NOT_AVAILABLE": "Non disponible"
}
},
"TEAM_AVAILABILITY": {
"ONLINE": "Nous sommes en ligne",
"OFFLINE": "Nous sommes absents pour le moment",
- "BACK_AS_SOON_AS_POSSIBLE": "We will be back as soon as possible"
+ "BACK_AS_SOON_AS_POSSIBLE": "Nous serons de retour dès que possible"
},
"REPLY_TIME": {
"IN_A_FEW_MINUTES": "Répond généralement en quelques minutes",
"IN_A_FEW_HOURS": "Répond généralement en quelques heures",
"IN_A_DAY": "Répond généralement dans la journée",
- "BACK_IN_HOURS": "We will be back online in {n} hour | We will be back online in {n} hours",
- "BACK_IN_MINUTES": "We will be back online in {time} minutes",
- "BACK_AT_TIME": "We will be back online at {time}",
- "BACK_ON_DAY": "We will be back online on {day}",
- "BACK_TOMORROW": "We will be back online tomorrow",
- "BACK_IN_SOME_TIME": "We will be back online in some time"
+ "BACK_IN_HOURS": "Nous serons de retour en ligne dans {n} heure | Nous serons de retour en ligne dans {n} heures",
+ "BACK_IN_MINUTES": "Nous serons de retour en ligne dans {time} minutes",
+ "BACK_AT_TIME": "Nous serons de retour en ligne à {time}",
+ "BACK_ON_DAY": "Nous serons de retour en ligne {day}",
+ "BACK_TOMORROW": "Nous serons de retour en ligne demain",
+ "BACK_IN_SOME_TIME": "Nous serons de retours en ligne dans quelques instants"
},
"DAY_NAMES": {
"SUNDAY": "Dimanche",
diff --git a/app/javascript/widget/i18n/locale/pt_BR.json b/app/javascript/widget/i18n/locale/pt_BR.json
index 7a2678962..124e9f8a9 100644
--- a/app/javascript/widget/i18n/locale/pt_BR.json
+++ b/app/javascript/widget/i18n/locale/pt_BR.json
@@ -20,18 +20,18 @@
"TEAM_AVAILABILITY": {
"ONLINE": "Estamos conectados",
"OFFLINE": "Estamos ausentes no momento",
- "BACK_AS_SOON_AS_POSSIBLE": "We will be back as soon as possible"
+ "BACK_AS_SOON_AS_POSSIBLE": "Estaremos de volta em breve"
},
"REPLY_TIME": {
"IN_A_FEW_MINUTES": "Normalmente responde em alguns minutos",
"IN_A_FEW_HOURS": "Normalmente responde em algumas horas",
"IN_A_DAY": "Normalmente responde em um dia",
- "BACK_IN_HOURS": "We will be back online in {n} hour | We will be back online in {n} hours",
+ "BACK_IN_HOURS": "Estaremos de volta em {n} hora | Estaremos de volta em {n} horas",
"BACK_IN_MINUTES": "We will be back online in {time} minutes",
"BACK_AT_TIME": "We will be back online at {time}",
"BACK_ON_DAY": "We will be back online on {day}",
"BACK_TOMORROW": "We will be back online tomorrow",
- "BACK_IN_SOME_TIME": "We will be back online in some time"
+ "BACK_IN_SOME_TIME": "Estaremos disponíveis novamente em breve"
},
"DAY_NAMES": {
"SUNDAY": "Domingo",
diff --git a/app/jobs/avatar/avatar_from_url_job.rb b/app/jobs/avatar/avatar_from_url_job.rb
index 9996cf3eb..0ab7ebea8 100644
--- a/app/jobs/avatar/avatar_from_url_job.rb
+++ b/app/jobs/avatar/avatar_from_url_job.rb
@@ -1,27 +1,83 @@
+# Downloads and attaches avatar images from a URL.
+# Notes:
+# - For contact objects, we use `additional_attributes` to rate limit the
+# job and track state.
+# - We save the hash of the synced URL to retrigger downloads only when
+# there is a change in the underlying asset.
+# - A 1 minute rate limit window is enforced via `last_avatar_sync_at`.
class Avatar::AvatarFromUrlJob < ApplicationJob
+ include UrlHelper
queue_as :purgable
+ MAX_DOWNLOAD_SIZE = 15 * 1024 * 1024
+ RATE_LIMIT_WINDOW = 1.minute
+
def perform(avatarable, avatar_url)
return unless avatarable.respond_to?(:avatar)
+ return unless url_valid?(avatar_url)
- avatar_file = Down.download(
- avatar_url,
- max_size: 15 * 1024 * 1024
+ return unless should_sync_avatar?(avatarable, avatar_url)
+
+ avatar_file = Down.download(avatar_url, max_size: MAX_DOWNLOAD_SIZE)
+ raise Down::Error, 'Invalid file' unless valid_file?(avatar_file)
+
+ avatarable.avatar.attach(
+ io: avatar_file,
+ filename: avatar_file.original_filename,
+ content_type: avatar_file.content_type
)
- if valid_image?(avatar_file)
- avatarable.avatar.attach(io: avatar_file, filename: avatar_file.original_filename,
- content_type: avatar_file.content_type)
- end
+
rescue Down::NotFound, Down::Error => e
- Rails.logger.error "Exception: invalid avatar url #{avatar_url} : #{e.message}"
+ Rails.logger.error "AvatarFromUrlJob error for #{avatar_url}: #{e.class} - #{e.message}"
+ ensure
+ update_avatar_sync_attributes(avatarable, avatar_url)
end
private
- def valid_image?(file)
- return false if file.original_filename.blank?
+ def should_sync_avatar?(avatarable, avatar_url)
+ # Only Contacts are rate-limited and hash-gated.
+ return true unless avatarable.is_a?(Contact)
- # TODO: check if the file is an actual image
+ attrs = avatarable.additional_attributes || {}
+
+ return false if within_rate_limit?(attrs)
+ return false if duplicate_url?(attrs, avatar_url)
+
+ true
+ end
+
+ def within_rate_limit?(attrs)
+ ts = attrs['last_avatar_sync_at']
+ return false if ts.blank?
+
+ Time.zone.parse(ts) > RATE_LIMIT_WINDOW.ago
+ end
+
+ def duplicate_url?(attrs, avatar_url)
+ stored_hash = attrs['avatar_url_hash']
+ stored_hash.present? && stored_hash == generate_url_hash(avatar_url)
+ end
+
+ def generate_url_hash(url)
+ Digest::SHA256.hexdigest(url)
+ end
+
+ def update_avatar_sync_attributes(avatarable, avatar_url)
+ # Only Contacts have sync attributes persisted
+ return unless avatarable.is_a?(Contact)
+ return if avatar_url.blank?
+
+ additional_attributes = avatarable.additional_attributes || {}
+ additional_attributes['last_avatar_sync_at'] = Time.current.iso8601
+ additional_attributes['avatar_url_hash'] = generate_url_hash(avatar_url)
+
+ # Persist without triggering validations that may fail due to avatar file checks
+ avatarable.update_columns(additional_attributes: additional_attributes) # rubocop:disable Rails/SkipsModelValidations
+ end
+
+ def valid_file?(file)
+ return false if file.original_filename.blank?
true
end
diff --git a/app/jobs/delete_object_job.rb b/app/jobs/delete_object_job.rb
index 49a7e4752..756d0feb1 100644
--- a/app/jobs/delete_object_job.rb
+++ b/app/jobs/delete_object_job.rb
@@ -1,12 +1,40 @@
class DeleteObjectJob < ApplicationJob
queue_as :low
+ BATCH_SIZE = 5_000
+ HEAVY_ASSOCIATIONS = {
+ Account => %i[conversations contacts inboxes reporting_events],
+ Inbox => %i[conversations contact_inboxes reporting_events]
+ }.freeze
+
def perform(object, user = nil, ip = nil)
+ # Pre-purge heavy associations for large objects to avoid
+ # timeouts & race conditions due to destroy_async fan-out.
+ purge_heavy_associations(object)
object.destroy!
process_post_deletion_tasks(object, user, ip)
end
def process_post_deletion_tasks(object, user, ip); end
+
+ private
+
+ def purge_heavy_associations(object)
+ klass = HEAVY_ASSOCIATIONS.keys.find { |k| object.is_a?(k) }
+ return unless klass
+
+ HEAVY_ASSOCIATIONS[klass].each do |assoc|
+ next unless object.respond_to?(assoc)
+
+ batch_destroy(object.public_send(assoc))
+ end
+ end
+
+ def batch_destroy(relation)
+ relation.find_in_batches(batch_size: BATCH_SIZE) do |batch|
+ batch.each(&:destroy!)
+ end
+ end
end
DeleteObjectJob.prepend_mod_with('DeleteObjectJob')
diff --git a/app/mailboxes/imap/imap_mailbox.rb b/app/mailboxes/imap/imap_mailbox.rb
index bd591b05a..5fea49722 100644
--- a/app/mailboxes/imap/imap_mailbox.rb
+++ b/app/mailboxes/imap/imap_mailbox.rb
@@ -42,7 +42,7 @@ class Imap::ImapMailbox
message = @inbox.messages.find_by(source_id: in_reply_to)
if message.nil?
- @inbox.conversations.where("additional_attributes->>'in_reply_to' = ?", in_reply_to).first
+ @inbox.conversations.find_by("additional_attributes->>'in_reply_to' = ?", in_reply_to)
else
@inbox.conversations.find(message.conversation_id)
end
diff --git a/app/mailers/conversation_reply_mailer.rb b/app/mailers/conversation_reply_mailer.rb
index 360b227cb..a82c65440 100644
--- a/app/mailers/conversation_reply_mailer.rb
+++ b/app/mailers/conversation_reply_mailer.rb
@@ -101,7 +101,7 @@ class ConversationReplyMailer < ApplicationMailer
end
def custom_sender_name
- current_message&.sender&.available_name || @agent&.available_name || 'Notifications'
+ current_message&.sender&.available_name || @agent&.available_name || I18n.t('conversations.reply.email.header.notifications')
end
def business_name
diff --git a/app/models/account.rb b/app/models/account.rb
index b84d7f526..7efb13bb5 100644
--- a/app/models/account.rb
+++ b/app/models/account.rb
@@ -89,7 +89,6 @@ class Account < ApplicationRecord
has_many :portals, dependent: :destroy_async, class_name: '::Portal'
has_many :sms_channels, dependent: :destroy_async, class_name: '::Channel::Sms'
has_many :teams, dependent: :destroy_async
- has_many :telegram_bots, dependent: :destroy_async
has_many :telegram_channels, dependent: :destroy_async, class_name: '::Channel::Telegram'
has_many :twilio_sms, dependent: :destroy_async, class_name: '::Channel::TwilioSms'
has_many :twitter_profiles, dependent: :destroy_async, class_name: '::Channel::TwitterProfile'
diff --git a/app/models/automation_rule.rb b/app/models/automation_rule.rb
index 6f3f47d9c..9dc4d97eb 100644
--- a/app/models/automation_rule.rb
+++ b/app/models/automation_rule.rb
@@ -36,7 +36,7 @@ class AutomationRule < ApplicationRecord
def conditions_attributes
%w[content email country_code status message_type browser_language assignee_id team_id referer city company inbox_id
- mail_subject phone_number priority conversation_language]
+ mail_subject phone_number priority conversation_language labels]
end
def actions_attributes
diff --git a/app/models/channel/web_widget.rb b/app/models/channel/web_widget.rb
index 9e3016eac..d4e9989c1 100644
--- a/app/models/channel/web_widget.rb
+++ b/app/models/channel/web_widget.rb
@@ -3,6 +3,7 @@
# Table name: channel_web_widgets
#
# id :integer not null, primary key
+# allowed_domains :text default("")
# continuity_via_email :boolean default(TRUE), not null
# feature_flags :integer default(7), not null
# hmac_mandatory :boolean default(FALSE)
@@ -31,7 +32,7 @@ class Channel::WebWidget < ApplicationRecord
self.table_name = 'channel_web_widgets'
EDITABLE_ATTRS = [:website_url, :widget_color, :welcome_title, :welcome_tagline, :reply_time, :pre_chat_form_enabled,
- :continuity_via_email, :hmac_mandatory,
+ :continuity_via_email, :hmac_mandatory, :allowed_domains,
{ pre_chat_form_options: [:pre_chat_message, :require_email,
{ pre_chat_fields:
[:field_type, :label, :placeholder, :name, :enabled, :type, :enabled, :required,
diff --git a/app/models/concerns/labelable.rb b/app/models/concerns/labelable.rb
index e710e97e9..bf8778921 100644
--- a/app/models/concerns/labelable.rb
+++ b/app/models/concerns/labelable.rb
@@ -10,6 +10,8 @@ module Labelable
end
def add_labels(new_labels = nil)
+ return if new_labels.blank?
+
new_labels = Array(new_labels) # Make sure new_labels is an array
combined_labels = labels + new_labels
update!(label_list: combined_labels)
diff --git a/app/models/concerns/user_attribute_helpers.rb b/app/models/concerns/user_attribute_helpers.rb
index 32ff026ac..442d166ef 100644
--- a/app/models/concerns/user_attribute_helpers.rb
+++ b/app/models/concerns/user_attribute_helpers.rb
@@ -18,7 +18,7 @@ module UserAttributeHelpers
end
def active_account_user
- account_users.order(active_at: :desc)&.first
+ account_users.order(Arel.sql('active_at DESC NULLS LAST'))&.first
end
def current_account_user
diff --git a/app/models/conversation.rb b/app/models/conversation.rb
index d6c5d0e4a..4ec63acc2 100644
--- a/app/models/conversation.rb
+++ b/app/models/conversation.rb
@@ -297,8 +297,6 @@ class Conversation < ApplicationRecord
previous_labels, current_labels = previous_changes[:label_list]
return unless (previous_labels.is_a? Array) && (current_labels.is_a? Array)
- dispatcher_dispatch(CONVERSATION_UPDATED, previous_changes)
-
create_label_added(user_name, current_labels - previous_labels)
create_label_removed(user_name, previous_labels - current_labels)
end
diff --git a/app/models/message.rb b/app/models/message.rb
index 12b12b205..06be665d8 100644
--- a/app/models/message.rb
+++ b/app/models/message.rb
@@ -95,7 +95,8 @@ class Message < ApplicationRecord
incoming_email: 8,
input_csat: 9,
integrations: 10,
- sticker: 11
+ sticker: 11,
+ voice_call: 12
}
enum status: { sent: 0, delivered: 1, read: 2, failed: 3 }
# [:submitted_email, :items, :submitted_values] : Used for bot message types
@@ -104,9 +105,10 @@ class Message < ApplicationRecord
# [:deleted] : Used to denote whether the message was deleted by the agent
# [:external_created_at] : Can specify if the message was created at a different timestamp externally
# [:external_error : Can specify if the message creation failed due to an error at external API
+ # [:data] : Used for structured content types such as voice_call
store :content_attributes, accessors: [:submitted_email, :items, :submitted_values, :email, :in_reply_to, :deleted,
:external_created_at, :story_sender, :story_id, :external_error,
- :translations, :in_reply_to_external_id, :is_unsupported], coder: JSON
+ :translations, :in_reply_to_external_id, :is_unsupported, :data], coder: JSON
store :external_source_ids, accessors: [:slack], coder: JSON, prefix: :external_source_id
@@ -114,6 +116,7 @@ class Message < ApplicationRecord
scope :chat, -> { where.not(message_type: :activity).where(private: false) }
scope :non_activity_messages, -> { where.not(message_type: :activity).reorder('id desc') }
scope :today, -> { where("date_trunc('day', created_at) = ?", Date.current) }
+ scope :voice_calls, -> { where(content_type: :voice_call) }
# TODO: Get rid of default scope
# https://stackoverflow.com/a/1834250/939299
diff --git a/app/models/super_admin.rb b/app/models/super_admin.rb
index f41610ee4..316d60c7b 100644
--- a/app/models/super_admin.rb
+++ b/app/models/super_admin.rb
@@ -7,6 +7,7 @@
# confirmation_sent_at :datetime
# confirmation_token :string
# confirmed_at :datetime
+# consumed_timestep :integer
# current_sign_in_at :datetime
# current_sign_in_ip :string
# custom_attributes :jsonb
@@ -17,6 +18,9 @@
# last_sign_in_ip :string
# message_signature :text
# name :string not null
+# otp_backup_codes :text
+# otp_required_for_login :boolean default(FALSE), not null
+# otp_secret :string
# provider :string default("email"), not null
# pubsub_token :string
# remember_created_at :datetime
@@ -33,10 +37,12 @@
#
# Indexes
#
-# index_users_on_email (email)
-# index_users_on_pubsub_token (pubsub_token) UNIQUE
-# index_users_on_reset_password_token (reset_password_token) UNIQUE
-# index_users_on_uid_and_provider (uid,provider) UNIQUE
+# index_users_on_email (email)
+# index_users_on_otp_required_for_login (otp_required_for_login)
+# index_users_on_otp_secret (otp_secret) UNIQUE
+# index_users_on_pubsub_token (pubsub_token) UNIQUE
+# index_users_on_reset_password_token (reset_password_token) UNIQUE
+# index_users_on_uid_and_provider (uid,provider) UNIQUE
#
class SuperAdmin < User
end
diff --git a/app/models/telegram_bot.rb b/app/models/telegram_bot.rb
deleted file mode 100644
index 725250053..000000000
--- a/app/models/telegram_bot.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-# == Schema Information
-#
-# Table name: telegram_bots
-#
-# id :integer not null, primary key
-# auth_key :string
-# name :string
-# created_at :datetime not null
-# updated_at :datetime not null
-# account_id :integer
-#
-
-class TelegramBot < ApplicationRecord
- belongs_to :account
- has_one :inbox, as: :channel, dependent: :destroy_async
- validates :auth_key, uniqueness: { scope: :account_id }
-end
diff --git a/app/models/user.rb b/app/models/user.rb
index d1907362a..4923d0a35 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -7,6 +7,7 @@
# confirmation_sent_at :datetime
# confirmation_token :string
# confirmed_at :datetime
+# consumed_timestep :integer
# current_sign_in_at :datetime
# current_sign_in_ip :string
# custom_attributes :jsonb
@@ -17,6 +18,9 @@
# last_sign_in_ip :string
# message_signature :text
# name :string not null
+# otp_backup_codes :text
+# otp_required_for_login :boolean default(FALSE), not null
+# otp_secret :string
# provider :string default("email"), not null
# pubsub_token :string
# remember_created_at :datetime
@@ -33,10 +37,12 @@
#
# Indexes
#
-# index_users_on_email (email)
-# index_users_on_pubsub_token (pubsub_token) UNIQUE
-# index_users_on_reset_password_token (reset_password_token) UNIQUE
-# index_users_on_uid_and_provider (uid,provider) UNIQUE
+# index_users_on_email (email)
+# index_users_on_otp_required_for_login (otp_required_for_login)
+# index_users_on_otp_secret (otp_secret) UNIQUE
+# index_users_on_pubsub_token (pubsub_token) UNIQUE
+# index_users_on_reset_password_token (reset_password_token) UNIQUE
+# index_users_on_uid_and_provider (uid,provider) UNIQUE
#
class User < ApplicationRecord
@@ -58,7 +64,8 @@ class User < ApplicationRecord
:validatable,
:confirmable,
:password_has_required_content,
- :omniauthable, omniauth_providers: [:google_oauth2]
+ :two_factor_authenticatable,
+ :omniauthable, omniauth_providers: [:google_oauth2, :saml]
# TODO: remove in a future version once online status is moved to account users
# remove the column availability from users
@@ -70,6 +77,12 @@ class User < ApplicationRecord
validates :email, presence: true
+ serialize :otp_backup_codes, type: Array
+
+ # Encrypt sensitive MFA fields
+ encrypts :otp_secret, deterministic: true
+ encrypts :otp_backup_codes
+
has_many :account_users, dependent: :destroy_async
has_many :accounts, through: :account_users
accepts_nested_attributes_for :account_users
@@ -156,6 +169,27 @@ class User < ApplicationRecord
find_by(email: email&.downcase)
end
+ # 2FA/MFA Methods
+ # Delegated to Mfa::ManagementService for better separation of concerns
+ def mfa_service
+ @mfa_service ||= Mfa::ManagementService.new(user: self)
+ end
+
+ delegate :two_factor_provisioning_uri, to: :mfa_service
+ delegate :backup_codes_generated?, to: :mfa_service
+ delegate :enable_two_factor!, to: :mfa_service
+ delegate :disable_two_factor!, to: :mfa_service
+ delegate :generate_backup_codes!, to: :mfa_service
+ delegate :validate_backup_code!, to: :mfa_service
+
+ def mfa_enabled?
+ otp_required_for_login?
+ end
+
+ def mfa_feature_available?
+ Chatwoot.mfa_enabled?
+ end
+
private
def remove_macros
diff --git a/app/services/automation_rules/conditions_filter_service.rb b/app/services/automation_rules/conditions_filter_service.rb
index 23873371d..993ed21c9 100644
--- a/app/services/automation_rules/conditions_filter_service.rb
+++ b/app/services/automation_rules/conditions_filter_service.rb
@@ -151,13 +151,36 @@ class AutomationRules::ConditionsFilterService < FilterService
" #{table_name}.additional_attributes ->> '#{attribute_key}' #{filter_operator_value} #{query_operator} "
when 'standard'
if attribute_key == 'labels'
- " tags.id #{filter_operator_value} #{query_operator} "
+ build_label_query_string(query_hash, current_index, query_operator)
else
" #{table_name}.#{attribute_key} #{filter_operator_value} #{query_operator} "
end
end
end
+ def build_label_query_string(query_hash, current_index, query_operator)
+ case query_hash['filter_operator']
+ when 'equal_to'
+ return " 1=0 #{query_operator} " if query_hash['values'].blank?
+
+ value_placeholder = "value_#{current_index}"
+ @filter_values[value_placeholder] = query_hash['values'].first
+ " tags.name = :#{value_placeholder} #{query_operator} "
+ when 'not_equal_to'
+ return " 1=0 #{query_operator} " if query_hash['values'].blank?
+
+ value_placeholder = "value_#{current_index}"
+ @filter_values[value_placeholder] = query_hash['values'].first
+ " tags.name != :#{value_placeholder} #{query_operator} "
+ when 'is_present'
+ " tags.id IS NOT NULL #{query_operator} "
+ when 'is_not_present'
+ " tags.id IS NULL #{query_operator} "
+ else
+ " tags.id #{filter_operation(query_hash, current_index)} #{query_operator} "
+ end
+ end
+
private
def base_relation
@@ -166,7 +189,21 @@ class AutomationRules::ConditionsFilterService < FilterService
).joins(
'LEFT OUTER JOIN messages on messages.conversation_id = conversations.id'
)
+
+ # Only add label joins when label conditions exist
+ if label_conditions?
+ records = records.joins(
+ 'LEFT OUTER JOIN taggings ON taggings.taggable_id = conversations.id AND taggings.taggable_type = \'Conversation\''
+ ).joins(
+ 'LEFT OUTER JOIN tags ON taggings.tag_id = tags.id'
+ )
+ end
+
records = records.where(messages: { id: @options[:message].id }) if @options[:message].present?
records
end
+
+ def label_conditions?
+ @rule.conditions.any? { |condition| condition['attribute_key'] == 'labels' }
+ end
end
diff --git a/app/services/base_token_service.rb b/app/services/base_token_service.rb
new file mode 100644
index 000000000..966404108
--- /dev/null
+++ b/app/services/base_token_service.rb
@@ -0,0 +1,27 @@
+class BaseTokenService
+ pattr_initialize [:payload, :token]
+
+ def generate_token
+ JWT.encode(token_payload, secret_key, algorithm)
+ end
+
+ def decode_token
+ JWT.decode(token, secret_key, true, algorithm: algorithm).first.symbolize_keys
+ rescue JWT::ExpiredSignature, JWT::DecodeError
+ {}
+ end
+
+ private
+
+ def token_payload
+ payload || {}
+ end
+
+ def secret_key
+ Rails.application.secret_key_base
+ end
+
+ def algorithm
+ 'HS256'
+ end
+end
diff --git a/app/services/contacts/filter_service.rb b/app/services/contacts/filter_service.rb
index 7f2d6a0b8..9d017ea75 100644
--- a/app/services/contacts/filter_service.rb
+++ b/app/services/contacts/filter_service.rb
@@ -21,7 +21,7 @@ class Contacts::FilterService < FilterService
def filter_values(query_hash)
current_val = query_hash['values'][0]
if query_hash['attribute_key'] == 'phone_number'
- "+#{current_val}"
+ "+#{current_val&.delete('+')}"
elsif query_hash['attribute_key'] == 'country_code'
current_val.downcase
else
diff --git a/app/services/line/incoming_message_service.rb b/app/services/line/incoming_message_service.rb
index 4761292d0..6a1192d02 100644
--- a/app/services/line/incoming_message_service.rb
+++ b/app/services/line/incoming_message_service.rb
@@ -10,11 +10,6 @@ class Line::IncomingMessageService
# probably test events
return if params[:events].blank?
- line_contact_info
- return if line_contact_info['userId'].blank?
-
- set_contact
- set_conversation
parse_events
end
@@ -22,6 +17,14 @@ class Line::IncomingMessageService
def parse_events
params[:events].each do |event|
+ next unless event_type_message?(event)
+
+ get_line_contact_info(event)
+ next if @line_contact_info['userId'].blank?
+
+ set_contact
+ set_conversation
+
next unless message_created? event
attach_files event['message']
@@ -30,8 +33,6 @@ class Line::IncomingMessageService
end
def message_created?(event)
- return unless event_type_message?(event)
-
@message = @conversation.messages.build(
content: message_content(event),
account_id: @inbox.account_id,
@@ -76,7 +77,8 @@ class Line::IncomingMessageService
response = inbox.channel.client.get_message_content(message['id'])
- file_name = "media-#{message['id']}.#{response.content_type.split('/')[1]}"
+ extension = get_file_extension(response)
+ file_name = message['fileName'] || "media-#{message['id']}.#{extension}"
temp_file = Tempfile.new(file_name)
temp_file.binmode
temp_file << response.body
@@ -93,25 +95,38 @@ class Line::IncomingMessageService
)
end
+ def get_file_extension(response)
+ if response.content_type&.include?('/')
+ response.content_type.split('/')[1]
+ else
+ 'bin'
+ end
+ end
+
def event_type_message?(event)
event['type'] == 'message' || event['type'] == 'sticker'
end
def message_type_non_text?(type)
- [Line::Bot::Event::MessageType::Video, Line::Bot::Event::MessageType::Audio, Line::Bot::Event::MessageType::Image].include?(type)
+ [
+ Line::Bot::Event::MessageType::Video,
+ Line::Bot::Event::MessageType::Audio,
+ Line::Bot::Event::MessageType::Image,
+ Line::Bot::Event::MessageType::File
+ ].include?(type)
end
def account
@account ||= inbox.account
end
- def line_contact_info
- @line_contact_info ||= JSON.parse(inbox.channel.client.get_profile(params[:events].first['source']['userId']).body)
+ def get_line_contact_info(event)
+ @line_contact_info = JSON.parse(inbox.channel.client.get_profile(event['source']['userId']).body)
end
def set_contact
contact_inbox = ::ContactInboxWithContactBuilder.new(
- source_id: line_contact_info['userId'],
+ source_id: @line_contact_info['userId'],
inbox: inbox,
contact_attributes: contact_attributes
).perform
@@ -138,15 +153,15 @@ class Line::IncomingMessageService
def contact_attributes
{
- name: line_contact_info['displayName'],
- avatar_url: line_contact_info['pictureUrl'],
+ name: @line_contact_info['displayName'],
+ avatar_url: @line_contact_info['pictureUrl'],
additional_attributes: additional_attributes
}
end
def additional_attributes
{
- social_line_user_id: line_contact_info['userId']
+ social_line_user_id: @line_contact_info['userId']
}
end
diff --git a/app/services/mfa/authentication_service.rb b/app/services/mfa/authentication_service.rb
new file mode 100644
index 000000000..caad66cf7
--- /dev/null
+++ b/app/services/mfa/authentication_service.rb
@@ -0,0 +1,23 @@
+class Mfa::AuthenticationService
+ pattr_initialize [:user!, :otp_code, :backup_code]
+
+ def authenticate
+ return false unless user
+
+ return authenticate_with_otp if otp_code.present?
+ return authenticate_with_backup_code if backup_code.present?
+
+ false
+ end
+
+ private
+
+ def authenticate_with_otp
+ user.validate_and_consume_otp!(otp_code)
+ end
+
+ def authenticate_with_backup_code
+ mfa_service = Mfa::ManagementService.new(user: user)
+ mfa_service.validate_backup_code!(backup_code)
+ end
+end
diff --git a/app/services/mfa/management_service.rb b/app/services/mfa/management_service.rb
new file mode 100644
index 000000000..d4c01ec1f
--- /dev/null
+++ b/app/services/mfa/management_service.rb
@@ -0,0 +1,88 @@
+class Mfa::ManagementService
+ pattr_initialize [:user!]
+
+ def enable_two_factor!
+ user.otp_secret = User.generate_otp_secret
+ user.save!
+ end
+
+ def disable_two_factor!
+ user.otp_secret = nil
+ user.otp_required_for_login = false
+ user.otp_backup_codes = nil
+ user.save!
+ end
+
+ def verify_and_activate!
+ ActiveRecord::Base.transaction do
+ user.update!(otp_required_for_login: true)
+ backup_codes_generated? ? nil : generate_backup_codes!
+ end
+ end
+
+ def two_factor_provisioning_uri
+ return nil if user.otp_secret.blank?
+
+ issuer = 'Chatwoot'
+ label = user.email
+ user.otp_provisioning_uri(label, issuer: issuer)
+ end
+
+ def generate_backup_codes!
+ codes = Array.new(10) { SecureRandom.hex(4).upcase }
+ user.otp_backup_codes = codes
+ user.save!
+ codes
+ end
+
+ def validate_backup_code!(code)
+ return false unless valid_backup_code_input?(code)
+
+ codes = user.otp_backup_codes
+ found_index = find_matching_code_index(codes, code)
+
+ return false if found_index.nil?
+
+ mark_code_as_used(codes, found_index)
+ end
+
+ private
+
+ def valid_backup_code_input?(code)
+ user.otp_backup_codes.present? && code.present?
+ end
+
+ def find_matching_code_index(codes, code)
+ found_index = nil
+
+ # Constant-time comparison to prevent timing attacks
+ codes.each_with_index do |stored_code, idx|
+ is_match = ActiveSupport::SecurityUtils.secure_compare(stored_code, code)
+ is_unused = stored_code != 'XXXXXXXX'
+ found_index = idx if is_match && is_unused
+ end
+
+ found_index
+ end
+
+ def mark_code_as_used(codes, index)
+ codes[index] = 'XXXXXXXX'
+ user.otp_backup_codes = codes
+ user.save!
+ true
+ end
+
+ public
+
+ def backup_codes_generated?
+ user.otp_backup_codes.present?
+ end
+
+ def mfa_enabled?
+ user.otp_required_for_login?
+ end
+
+ def two_factor_setup_pending?
+ user.otp_secret.present? && !user.otp_required_for_login?
+ end
+end
diff --git a/app/services/mfa/token_service.rb b/app/services/mfa/token_service.rb
new file mode 100644
index 000000000..a7994b60c
--- /dev/null
+++ b/app/services/mfa/token_service.rb
@@ -0,0 +1,28 @@
+class Mfa::TokenService < BaseTokenService
+ pattr_initialize [:user, :token]
+
+ MFA_TOKEN_EXPIRY = 5.minutes
+
+ def generate_token
+ @payload = build_payload
+ super
+ end
+
+ def verify_token
+ decoded = decode_token
+ return nil if decoded.blank?
+
+ User.find(decoded[:user_id])
+ rescue ActiveRecord::RecordNotFound
+ nil
+ end
+
+ private
+
+ def build_payload
+ {
+ user_id: user.id,
+ exp: MFA_TOKEN_EXPIRY.from_now.to_i
+ }
+ end
+end
diff --git a/app/services/whatsapp/incoming_message_whatsapp_cloud_service.rb b/app/services/whatsapp/incoming_message_whatsapp_cloud_service.rb
index 97051a2cf..f8ac8c85a 100644
--- a/app/services/whatsapp/incoming_message_whatsapp_cloud_service.rb
+++ b/app/services/whatsapp/incoming_message_whatsapp_cloud_service.rb
@@ -9,7 +9,13 @@ class Whatsapp::IncomingMessageWhatsappCloudService < Whatsapp::IncomingMessageB
end
def download_attachment_file(attachment_payload)
- url_response = HTTParty.get(inbox.channel.media_url(attachment_payload[:id]), headers: inbox.channel.api_headers)
+ url_response = HTTParty.get(
+ inbox.channel.media_url(
+ attachment_payload[:id],
+ inbox.channel.provider_config['phone_number_id']
+ ),
+ headers: inbox.channel.api_headers
+ )
# This url response will be failure if the access token has expired.
inbox.channel.authorization_error! if url_response.unauthorized?
Down.download(url_response.parsed_response['url'], headers: inbox.channel.api_headers) if url_response.success?
diff --git a/app/services/whatsapp/populate_template_parameters_service.rb b/app/services/whatsapp/populate_template_parameters_service.rb
index 278e52f64..3f9f64b91 100644
--- a/app/services/whatsapp/populate_template_parameters_service.rb
+++ b/app/services/whatsapp/populate_template_parameters_service.rb
@@ -30,12 +30,12 @@ class Whatsapp::PopulateTemplateParametersService
end
end
- def build_media_parameter(url, media_type)
+ def build_media_parameter(url, media_type, media_name = nil)
return nil if url.blank?
sanitized_url = sanitize_parameter(url)
validate_url(sanitized_url)
- build_media_type_parameter(sanitized_url, media_type.downcase)
+ build_media_type_parameter(sanitized_url, media_type.downcase, media_name)
end
def build_named_parameter(parameter_name, value)
@@ -89,14 +89,14 @@ class Whatsapp::PopulateTemplateParametersService
}
end
- def build_media_type_parameter(sanitized_url, media_type)
+ def build_media_type_parameter(sanitized_url, media_type, media_name = nil)
case media_type
when 'image'
build_image_parameter(sanitized_url)
when 'video'
build_video_parameter(sanitized_url)
when 'document'
- build_document_parameter(sanitized_url)
+ build_document_parameter(sanitized_url, media_name)
else
raise ArgumentError, "Unsupported media type: #{media_type}"
end
@@ -110,8 +110,11 @@ class Whatsapp::PopulateTemplateParametersService
{ type: 'video', video: { link: url } }
end
- def build_document_parameter(url)
- { type: 'document', document: { link: url } }
+ def build_document_parameter(url, media_name = nil)
+ document_params = { link: url }
+ document_params[:filename] = media_name if media_name.present?
+
+ { type: 'document', document: document_params }
end
def rich_formatting?(text)
diff --git a/app/services/whatsapp/providers/whatsapp_cloud_service.rb b/app/services/whatsapp/providers/whatsapp_cloud_service.rb
index 68e965595..1968693ff 100644
--- a/app/services/whatsapp/providers/whatsapp_cloud_service.rb
+++ b/app/services/whatsapp/providers/whatsapp_cloud_service.rb
@@ -62,8 +62,10 @@ class Whatsapp::Providers::WhatsappCloudService < Whatsapp::Providers::BaseServi
{ 'Authorization' => "Bearer #{whatsapp_channel.provider_config['api_key']}", 'Content-Type' => 'application/json' }
end
- def media_url(media_id)
- "#{api_base_path}/v13.0/#{media_id}"
+ def media_url(media_id, phone_number_id = nil)
+ url = "#{api_base_path}/v13.0/#{media_id}"
+ url += "?phone_number_id=#{phone_number_id}" if phone_number_id
+ url
end
def api_base_path
@@ -141,7 +143,11 @@ class Whatsapp::Providers::WhatsappCloudService < Whatsapp::Providers::BaseServi
# {
# processed_params: {
# body: { '1': 'John', '2': '123 Main St' },
- # header: { media_url: 'https://...', media_type: 'image' },
+ # header: {
+ # media_url: 'https://...',
+ # media_type: 'image',
+ # media_name: 'filename.pdf' # Optional, for document templates only
+ # },
# buttons: [{ type: 'url', parameter: 'otp123456' }]
# }
# }
diff --git a/app/services/whatsapp/template_processor_service.rb b/app/services/whatsapp/template_processor_service.rb
index 3b12bf58b..4a89d684a 100644
--- a/app/services/whatsapp/template_processor_service.rb
+++ b/app/services/whatsapp/template_processor_service.rb
@@ -60,9 +60,10 @@ class Whatsapp::TemplateProcessorService
next if value.blank?
if media_url_with_type?(key, header_data)
- media_param = parameter_builder.build_media_parameter(value, header_data['media_type'])
+ media_name = header_data['media_name']
+ media_param = parameter_builder.build_media_parameter(value, header_data['media_type'], media_name)
header_params << media_param if media_param
- elsif key != 'media_type'
+ elsif key != 'media_type' && key != 'media_name'
header_params << parameter_builder.build_parameter(value)
end
end
diff --git a/app/services/widget/token_service.rb b/app/services/widget/token_service.rb
index 5fb719e7b..b50119eb1 100644
--- a/app/services/widget/token_service.rb
+++ b/app/services/widget/token_service.rb
@@ -1,21 +1,27 @@
-class Widget::TokenService
- pattr_initialize [:payload, :token]
+class Widget::TokenService < BaseTokenService
+ DEFAULT_EXPIRY_DAYS = 180
def generate_token
- JWT.encode payload, secret_key, 'HS256'
- end
-
- def decode_token
- JWT.decode(
- token, secret_key, true, algorithm: 'HS256'
- ).first.symbolize_keys
- rescue StandardError
- {}
+ JWT.encode(token_payload, secret_key, algorithm)
end
private
- def secret_key
- Rails.application.secret_key_base
+ def token_payload
+ (payload || {}).merge(exp: exp, iat: iat)
+ end
+
+ def iat
+ Time.zone.now.to_i
+ end
+
+ def exp
+ iat + expire_in.days.to_i
+ end
+
+ def expire_in
+ # Value is stored in days, defaulting to 6 months (180 days)
+ token_expiry_value = InstallationConfig.find_by(name: 'WIDGET_TOKEN_EXPIRY')&.value
+ (token_expiry_value.presence || DEFAULT_EXPIRY_DAYS).to_i
end
end
diff --git a/app/views/api/v1/accounts/assignment_policies/_assignment_policy.json.jbuilder b/app/views/api/v1/accounts/assignment_policies/_assignment_policy.json.jbuilder
index b48307a94..cf09a2949 100644
--- a/app/views/api/v1/accounts/assignment_policies/_assignment_policy.json.jbuilder
+++ b/app/views/api/v1/accounts/assignment_policies/_assignment_policy.json.jbuilder
@@ -6,5 +6,6 @@ json.conversation_priority assignment_policy.conversation_priority
json.fair_distribution_limit assignment_policy.fair_distribution_limit
json.fair_distribution_window assignment_policy.fair_distribution_window
json.enabled assignment_policy.enabled
+json.assigned_inbox_count assignment_policy.inboxes.count
json.created_at assignment_policy.created_at.to_i
json.updated_at assignment_policy.updated_at.to_i
diff --git a/app/views/api/v1/models/_inbox.json.jbuilder b/app/views/api/v1/models/_inbox.json.jbuilder
index 1b2ab177b..d9134d563 100644
--- a/app/views/api/v1/models/_inbox.json.jbuilder
+++ b/app/views/api/v1/models/_inbox.json.jbuilder
@@ -33,6 +33,7 @@ end
json.tweets_enabled resource.channel.try(:tweets_enabled) if resource.twitter?
## WebWidget Attributes
+json.allowed_domains resource.channel.try(:allowed_domains)
json.widget_color resource.channel.try(:widget_color)
json.website_url resource.channel.try(:website_url)
json.hmac_mandatory resource.channel.try(:hmac_mandatory)
diff --git a/app/views/api/v1/profile/mfa/backup_codes.json.jbuilder b/app/views/api/v1/profile/mfa/backup_codes.json.jbuilder
new file mode 100644
index 000000000..2aafdd3cd
--- /dev/null
+++ b/app/views/api/v1/profile/mfa/backup_codes.json.jbuilder
@@ -0,0 +1 @@
+json.backup_codes @backup_codes
diff --git a/app/views/api/v1/profile/mfa/create.json.jbuilder b/app/views/api/v1/profile/mfa/create.json.jbuilder
new file mode 100644
index 000000000..52072ccfb
--- /dev/null
+++ b/app/views/api/v1/profile/mfa/create.json.jbuilder
@@ -0,0 +1,2 @@
+json.provisioning_url @user.mfa_service.two_factor_provisioning_uri
+json.secret @user.otp_secret
diff --git a/app/views/api/v1/profile/mfa/destroy.json.jbuilder b/app/views/api/v1/profile/mfa/destroy.json.jbuilder
new file mode 100644
index 000000000..a5bf2ea84
--- /dev/null
+++ b/app/views/api/v1/profile/mfa/destroy.json.jbuilder
@@ -0,0 +1 @@
+json.enabled @user.mfa_enabled?
diff --git a/app/views/api/v1/profile/mfa/show.json.jbuilder b/app/views/api/v1/profile/mfa/show.json.jbuilder
new file mode 100644
index 000000000..4568f48f9
--- /dev/null
+++ b/app/views/api/v1/profile/mfa/show.json.jbuilder
@@ -0,0 +1,3 @@
+json.feature_available Chatwoot.mfa_enabled?
+json.enabled @user.mfa_enabled?
+json.backup_codes_generated @user.mfa_service.backup_codes_generated? if Chatwoot.mfa_enabled?
diff --git a/app/views/api/v1/profile/mfa/verify.json.jbuilder b/app/views/api/v1/profile/mfa/verify.json.jbuilder
new file mode 100644
index 000000000..54be3fc35
--- /dev/null
+++ b/app/views/api/v1/profile/mfa/verify.json.jbuilder
@@ -0,0 +1,2 @@
+json.enabled @user.mfa_enabled?
+json.backup_codes @backup_codes if @backup_codes.present?
diff --git a/app/views/layouts/vueapp.html.erb b/app/views/layouts/vueapp.html.erb
index 55fba871c..4d71a547d 100644
--- a/app/views/layouts/vueapp.html.erb
+++ b/app/views/layouts/vueapp.html.erb
@@ -44,6 +44,7 @@
whatsappApiVersion: '<%= @global_config['WHATSAPP_API_VERSION'] %>',
signupEnabled: '<%= @global_config['ENABLE_ACCOUNT_SIGNUP'] %>',
isEnterprise: '<%= @global_config['IS_ENTERPRISE'] %>',
+ isMfaEnabled: '<%= Chatwoot.mfa_enabled? %>',
<% if @global_config['IS_ENTERPRISE'] %>
enterprisePlanName: '<%= @global_config['INSTALLATION_PRICING_PLAN'] %>',
<% end %>
diff --git a/app/views/public/api/v1/portals/_footer.html.erb b/app/views/public/api/v1/portals/_footer.html.erb
index bfba6235b..10894d9c7 100644
--- a/app/views/public/api/v1/portals/_footer.html.erb
+++ b/app/views/public/api/v1/portals/_footer.html.erb
@@ -11,7 +11,7 @@
<%= I18n.t('public_portal.footer.made_with') %>
- <%= @global_config['BRAND_NAME'] %>
+ <%= @global_config['INSTALLATION_NAME'] %>
diff --git a/config/app.yml b/config/app.yml
index d11111fbc..34d044656 100644
--- a/config/app.yml
+++ b/config/app.yml
@@ -1,5 +1,5 @@
shared: &shared
- version: '4.5.2'
+ version: '4.6.0'
development:
<<: *shared
diff --git a/config/application.rb b/config/application.rb
index 3eca267f0..d644dd28f 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -47,6 +47,10 @@ module Chatwoot
# Add enterprise views to the view paths
config.paths['app/views'].unshift('enterprise/app/views')
+ # Load enterprise initializers alongside standard initializers
+ enterprise_initializers = Rails.root.join('enterprise/config/initializers')
+ Dir[enterprise_initializers.join('**/*.rb')].each { |f| require f } if enterprise_initializers.exist?
+
# Settings in config/environments/* take precedence over those specified here.
# Application configuration can go into files in config/initializers
# -- all .rb files in that directory are automatically loaded after loading
@@ -64,6 +68,16 @@ module Chatwoot
# Disable PDF/video preview generation as we don't use them
config.active_storage.previewers = []
+
+ # Active Record Encryption configuration
+ # Required for MFA/2FA features - skip if not using encryption
+ if ENV['ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY'].present?
+ config.active_record.encryption.primary_key = ENV['ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY']
+ config.active_record.encryption.deterministic_key = ENV.fetch('ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY', nil)
+ config.active_record.encryption.key_derivation_salt = ENV.fetch('ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT', nil)
+ config.active_record.encryption.support_unencrypted_data = true
+ config.active_record.encryption.store_key_references = true
+ end
end
def self.config
@@ -78,4 +92,16 @@ module Chatwoot
# ref: https://www.rubydoc.info/stdlib/openssl/OpenSSL/SSL/SSLContext#DEFAULT_PARAMS-constant
ENV['REDIS_OPENSSL_VERIFY_MODE'] == 'none' ? OpenSSL::SSL::VERIFY_NONE : OpenSSL::SSL::VERIFY_PEER
end
+
+ def self.encryption_configured?
+ # Check if proper encryption keys are configured
+ # MFA/2FA features should only be enabled when proper keys are set
+ ENV['ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY'].present? &&
+ ENV['ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY'].present? &&
+ ENV['ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT'].present?
+ end
+
+ def self.mfa_enabled?
+ encryption_configured?
+ end
end
diff --git a/config/features.yml b/config/features.yml
index c8ed2e189..181284784 100644
--- a/config/features.yml
+++ b/config/features.yml
@@ -184,6 +184,7 @@
- name: whatsapp_embedded_signup
display_name: WhatsApp Embedded Signup
enabled: false
+ deprecated: true
- name: whatsapp_campaign
display_name: WhatsApp Campaign
enabled: false
@@ -204,3 +205,7 @@
enabled: false
premium: true
chatwoot_internal: true
+- name: saml
+ display_name: SAML
+ enabled: false
+ premium: true
diff --git a/config/initializers/filter_parameter_logging.rb b/config/initializers/filter_parameter_logging.rb
index 4c9ecc032..18855c95f 100644
--- a/config/initializers/filter_parameter_logging.rb
+++ b/config/initializers/filter_parameter_logging.rb
@@ -2,7 +2,8 @@
# Configure sensitive parameters which will be filtered from the log file.
Rails.application.config.filter_parameters += [
- :password, :secret, :_key, :auth, :crypt, :salt, :certificate, :otp, :access, :private, :protected, :ssn
+ :password, :secret, :_key, :auth, :crypt, :salt, :certificate, :otp, :access, :private, :protected, :ssn,
+ :otp_secret, :otp_code, :backup_code, :mfa_token, :otp_backup_codes
]
# Regex to filter all occurrences of 'token' in keys except for 'website_token'
diff --git a/config/initializers/omniauth.rb b/config/initializers/omniauth.rb
index d92d9b040..54aa6ded8 100644
--- a/config/initializers/omniauth.rb
+++ b/config/initializers/omniauth.rb
@@ -1,3 +1,7 @@
+# OmniAuth configuration
+# Sets the full host URL for callbacks and proper redirect handling
+OmniAuth.config.full_host = ENV.fetch('FRONTEND_URL', 'http://localhost:3000')
+
Rails.application.config.middleware.use OmniAuth::Builder do
provider :google_oauth2, ENV.fetch('GOOGLE_OAUTH_CLIENT_ID', nil), ENV.fetch('GOOGLE_OAUTH_CLIENT_SECRET', nil), {
provider_ignores_state: true
diff --git a/config/initializers/rack_attack.rb b/config/initializers/rack_attack.rb
index fe3f6c554..fe40974f2 100644
--- a/config/initializers/rack_attack.rb
+++ b/config/initializers/rack_attack.rb
@@ -83,12 +83,17 @@ class Rack::Attack
end
# ### Prevent Brute-Force Login Attacks ###
+ # Exclude MFA verification attempts from regular login throttling
throttle('login/ip', limit: 5, period: 5.minutes) do |req|
- req.ip if req.path_without_extentions == '/auth/sign_in' && req.post?
+ if req.path_without_extentions == '/auth/sign_in' && req.post? && req.params['mfa_token'].blank?
+ # Skip if this is an MFA verification request
+ req.ip
+ end
end
throttle('login/email', limit: 10, period: 15.minutes) do |req|
- if req.path_without_extentions == '/auth/sign_in' && req.post?
+ # Skip if this is an MFA verification request
+ if req.path_without_extentions == '/auth/sign_in' && req.post? && req.params['mfa_token'].blank?
# ref: https://github.com/rack/rack-attack/issues/399
# NOTE: This line used to throw ArgumentError /rails/action_mailbox/sendgrid/inbound_emails : invalid byte sequence in UTF-8
# Hence placed in the if block
@@ -114,6 +119,28 @@ class Rack::Attack
req.ip if req.path_without_extentions == '/api/v1/profile/resend_confirmation' && req.post?
end
+ ## MFA throttling - prevent brute force attacks
+ throttle('mfa_verification/ip', limit: 5, period: 1.minute) do |req|
+ if req.path_without_extentions == '/api/v1/profile/mfa'
+ req.ip if req.delete? # Throttle disable attempts
+ elsif req.path_without_extentions.match?(%r{/api/v1/profile/mfa/(verify|backup_codes)})
+ req.ip if req.post? # Throttle verify and backup_codes attempts
+ end
+ end
+
+ # Separate rate limiting for MFA verification attempts
+ throttle('mfa_login/ip', limit: 10, period: 1.minute) do |req|
+ req.ip if req.path_without_extentions == '/auth/sign_in' && req.post? && req.params['mfa_token'].present?
+ end
+
+ throttle('mfa_login/token', limit: 10, period: 1.minute) do |req|
+ if req.path_without_extentions == '/auth/sign_in' && req.post?
+ # Track by MFA token to prevent brute force on a specific token
+ mfa_token = req.params['mfa_token'].presence
+ (mfa_token.presence)
+ end
+ end
+
## Prevent Brute-Force Signup Attacks ###
throttle('accounts/ip', limit: 5, period: 30.minutes) do |req|
req.ip if req.path_without_extentions == '/api/v1/accounts' && req.post?
diff --git a/config/installation_config.yml b/config/installation_config.yml
index 056c0baa2..9eb6af14f 100644
--- a/config/installation_config.yml
+++ b/config/installation_config.yml
@@ -120,7 +120,7 @@
- name: FACEBOOK_API_VERSION
display_title: 'Facebook API Version'
description: 'Configure this if you want to use a different Facebook API version. Make sure its prefixed with `v`'
- value: 'v17.0'
+ value: 'v18.0'
locked: false
- name: ENABLE_MESSENGER_CHANNEL_HUMAN_AGENT
display_title: 'Enable human agent'
@@ -236,6 +236,10 @@
display_title: 'Blocked Email Domains'
description: 'Add a domain per line to block them from signing up, accepts Regex'
type: code
+- name: SKIP_INCOMING_BCC_PROCESSING
+ value:
+ display_title: 'Skip BCC Processing For'
+ description: 'Comma-separated list of account IDs that should be skipped from incoming BCC processing'
- name: INACTIVE_WHATSAPP_NUMBERS
value: ''
display_title: 'Inactive WhatsApp Numbers'
@@ -439,3 +443,11 @@
locked: false
description: 'Zone ID for the Cloudflare domain'
## ------ End of Configs added for Cloudflare ------ ##
+
+## ------ Customizations for Customers ------ ##
+- name: WIDGET_TOKEN_EXPIRY
+ display_title: 'Widget Token Expiry'
+ value: 180
+ locked: false
+ description: 'Token expiry in days'
+## ------ End of Customizations for Customers ------ ##
diff --git a/config/locales/am.yml b/config/locales/am.yml
index 2466c6060..12a3bd314 100644
--- a/config/locales/am.yml
+++ b/config/locales/am.yml
@@ -26,6 +26,9 @@ am:
messages:
reset_password_success: Woot! Request for password reset is successful. Check your mail for instructions.
reset_password_failure: Uh ho! We could not find any user with the specified email.
+ reset_password_saml_user: This account uses SAML authentication. Password reset is not available. Please contact your administrator.
+ login_saml_user: This account uses SAML authentication. Please sign in through your organization's SAML provider.
+ saml_not_available: SAML authentication is not available in this installation.
inbox_deletetion_response: Your inbox deletion request will be processed in some time.
errors:
validations:
@@ -41,6 +44,8 @@ am:
failed: Signup failed
assignment_policy:
not_found: Assignment policy not found
+ saml:
+ feature_not_enabled: SAML feature not enabled for this account
data_import:
data_type:
invalid: Invalid data type
@@ -84,6 +89,20 @@ am:
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
+ mfa:
+ already_enabled: MFA is already enabled
+ not_enabled: MFA is not enabled
+ invalid_code: Invalid verification code
+ invalid_backup_code: Invalid backup code
+ invalid_token: Invalid or expired MFA token
+ invalid_credentials: Invalid credentials or verification code
+ feature_unavailable: MFA feature is not available. Please configure encryption keys.
+ profile:
+ mfa:
+ enabled: MFA enabled successfully
+ disabled: MFA disabled successfully
+ account_saml_settings:
+ invalid_certificate: must be a valid X.509 certificate in PEM format
reports:
period: Reporting period %{since} to %{until}
utc_warning: The report generated is in UTC timezone
@@ -209,6 +228,7 @@ am:
reply:
email:
header:
+ notifications: 'Notifications'
from_with_name: '%{assignee_name} from %{inbox_name} <%{from_email}>'
reply_with_name: '%{assignee_name} from %{inbox_name} '
friendly_name: '%{sender_name} from %{business_name} <%{from_email}>'
@@ -278,6 +298,26 @@ am:
completed_tool_call: 'Completed %{function_name} tool call'
invalid_tool_call: 'Invalid tool call'
tool_not_available: 'Tool not available'
+ documents:
+ limit_exceeded: 'Document limit exceeded'
+ pdf_format_error: 'must be a PDF file'
+ pdf_size_error: 'must be less than 10MB'
+ pdf_upload_failed: 'Failed to upload PDF to OpenAI'
+ pdf_upload_success: 'PDF uploaded successfully with file_id: %{file_id}'
+ pdf_processing_failed: 'Failed to process PDF document %{document_id}: %{error}'
+ pdf_processing_success: 'Successfully processed PDF document %{document_id}'
+ faq_generation_complete: 'FAQ generation complete. Total FAQs created: %{count}'
+ using_paginated_faq: 'Using paginated FAQ generation for document %{document_id}'
+ using_standard_faq: 'Using standard FAQ generation for document %{document_id}'
+ response_creation_error: 'Error in creating response document: %{error}'
+ missing_openai_file_id: 'Document must have openai_file_id for paginated processing'
+ openai_api_error: 'OpenAI API Error: %{error}'
+ starting_paginated_faq: 'Starting paginated FAQ generation (%{pages_per_chunk} pages per chunk)'
+ stopping_faq_generation: 'Stopping processing. Reason: %{reason}'
+ paginated_faq_complete: 'Paginated generation complete. Total FAQs: %{total_faqs}, Pages processed: %{pages_processed}'
+ processing_pages: 'Processing pages %{start}-%{end} (iteration %{iteration})'
+ chunk_generated: 'Chunk generated %{chunk_faqs} FAQs. Total so far: %{total_faqs}'
+ page_processing_error: 'Error processing pages %{start}-%{end}: %{error}'
public_portal:
search:
search_placeholder: Search for article by title or body...
@@ -358,6 +398,8 @@ am:
Transcript:
%{format_messages}
+ agent_capacity_policy:
+ inbox_already_assigned: 'Inbox has already been assigned to this policy'
portals:
send_instructions:
email_required: 'Email is required'
diff --git a/config/locales/ar.yml b/config/locales/ar.yml
index 46212cf2e..7fc73b030 100644
--- a/config/locales/ar.yml
+++ b/config/locales/ar.yml
@@ -26,6 +26,9 @@ ar:
messages:
reset_password_success: تم إرسال طلب إعادة تعيين كلمة المرور. يرجى مراجعة بريدك الإلكتروني للحصول على التعليمات.
reset_password_failure: المعذرة! لم نتمكن من العثور على أي مستخدم بعنوان البريد الإلكتروني المحدد.
+ reset_password_saml_user: This account uses SAML authentication. Password reset is not available. Please contact your administrator.
+ login_saml_user: This account uses SAML authentication. Please sign in through your organization's SAML provider.
+ saml_not_available: SAML authentication is not available in this installation.
inbox_deletetion_response: سيتم معالجة طلب حذف صندوق الوارد الخاص بك في بعض الوقت.
errors:
validations:
@@ -41,6 +44,8 @@ ar:
failed: فشلت عملية التسجيل
assignment_policy:
not_found: Assignment policy not found
+ saml:
+ feature_not_enabled: SAML feature not enabled for this account
data_import:
data_type:
invalid: نوع البيانات غير صالح
@@ -84,6 +89,20 @@ ar:
invalid_value: قيمة غير صالحة. القيم المقدمة ل %{attribute_name} غير صالحة
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
+ mfa:
+ already_enabled: MFA is already enabled
+ not_enabled: MFA is not enabled
+ invalid_code: Invalid verification code
+ invalid_backup_code: Invalid backup code
+ invalid_token: Invalid or expired MFA token
+ invalid_credentials: Invalid credentials or verification code
+ feature_unavailable: MFA feature is not available. Please configure encryption keys.
+ profile:
+ mfa:
+ enabled: MFA enabled successfully
+ disabled: MFA disabled successfully
+ account_saml_settings:
+ invalid_certificate: must be a valid X.509 certificate in PEM format
reports:
period: فترة التبليغ %{since} إلى %{until}
utc_warning: التقرير الذي تم إنشاؤه في التوقيت العالمي الموحّد
@@ -209,6 +228,7 @@ ar:
reply:
email:
header:
+ notifications: 'الإشعارات'
from_with_name: '%{assignee_name} من %{inbox_name} <%{from_email}>'
reply_with_name: '%{assignee_name} من %{inbox_name} <%{reply_email}>'
friendly_name: '%{sender_name} من %{business_name} <%{from_email}>'
@@ -278,6 +298,26 @@ ar:
completed_tool_call: 'Completed %{function_name} tool call'
invalid_tool_call: 'Invalid tool call'
tool_not_available: 'Tool not available'
+ documents:
+ limit_exceeded: 'Document limit exceeded'
+ pdf_format_error: 'must be a PDF file'
+ pdf_size_error: 'must be less than 10MB'
+ pdf_upload_failed: 'Failed to upload PDF to OpenAI'
+ pdf_upload_success: 'PDF uploaded successfully with file_id: %{file_id}'
+ pdf_processing_failed: 'Failed to process PDF document %{document_id}: %{error}'
+ pdf_processing_success: 'Successfully processed PDF document %{document_id}'
+ faq_generation_complete: 'FAQ generation complete. Total FAQs created: %{count}'
+ using_paginated_faq: 'Using paginated FAQ generation for document %{document_id}'
+ using_standard_faq: 'Using standard FAQ generation for document %{document_id}'
+ response_creation_error: 'Error in creating response document: %{error}'
+ missing_openai_file_id: 'Document must have openai_file_id for paginated processing'
+ openai_api_error: 'OpenAI API Error: %{error}'
+ starting_paginated_faq: 'Starting paginated FAQ generation (%{pages_per_chunk} pages per chunk)'
+ stopping_faq_generation: 'Stopping processing. Reason: %{reason}'
+ paginated_faq_complete: 'Paginated generation complete. Total FAQs: %{total_faqs}, Pages processed: %{pages_processed}'
+ processing_pages: 'Processing pages %{start}-%{end} (iteration %{iteration})'
+ chunk_generated: 'Chunk generated %{chunk_faqs} FAQs. Total so far: %{total_faqs}'
+ page_processing_error: 'Error processing pages %{start}-%{end}: %{error}'
public_portal:
search:
search_placeholder: البحث عن مقالة حسب العنوان أو الجسم...
@@ -374,6 +414,8 @@ ar:
Transcript:
%{format_messages}
+ agent_capacity_policy:
+ inbox_already_assigned: 'Inbox has already been assigned to this policy'
portals:
send_instructions:
email_required: 'البريد الإلكتروني مطلوب'
diff --git a/config/locales/az.yml b/config/locales/az.yml
index 79682f873..265eb92d1 100644
--- a/config/locales/az.yml
+++ b/config/locales/az.yml
@@ -26,6 +26,9 @@ az:
messages:
reset_password_success: Woot! Request for password reset is successful. Check your mail for instructions.
reset_password_failure: Uh ho! We could not find any user with the specified email.
+ reset_password_saml_user: This account uses SAML authentication. Password reset is not available. Please contact your administrator.
+ login_saml_user: This account uses SAML authentication. Please sign in through your organization's SAML provider.
+ saml_not_available: SAML authentication is not available in this installation.
inbox_deletetion_response: Your inbox deletion request will be processed in some time.
errors:
validations:
@@ -41,6 +44,8 @@ az:
failed: Signup failed
assignment_policy:
not_found: Assignment policy not found
+ saml:
+ feature_not_enabled: SAML feature not enabled for this account
data_import:
data_type:
invalid: Invalid data type
@@ -84,6 +89,20 @@ az:
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
+ mfa:
+ already_enabled: MFA is already enabled
+ not_enabled: MFA is not enabled
+ invalid_code: Invalid verification code
+ invalid_backup_code: Invalid backup code
+ invalid_token: Invalid or expired MFA token
+ invalid_credentials: Invalid credentials or verification code
+ feature_unavailable: MFA feature is not available. Please configure encryption keys.
+ profile:
+ mfa:
+ enabled: MFA enabled successfully
+ disabled: MFA disabled successfully
+ account_saml_settings:
+ invalid_certificate: must be a valid X.509 certificate in PEM format
reports:
period: Reporting period %{since} to %{until}
utc_warning: The report generated is in UTC timezone
@@ -209,6 +228,7 @@ az:
reply:
email:
header:
+ notifications: 'Notifications'
from_with_name: '%{assignee_name} from %{inbox_name} <%{from_email}>'
reply_with_name: '%{assignee_name} from %{inbox_name} '
friendly_name: '%{sender_name} from %{business_name} <%{from_email}>'
@@ -278,6 +298,26 @@ az:
completed_tool_call: 'Completed %{function_name} tool call'
invalid_tool_call: 'Invalid tool call'
tool_not_available: 'Tool not available'
+ documents:
+ limit_exceeded: 'Document limit exceeded'
+ pdf_format_error: 'must be a PDF file'
+ pdf_size_error: 'must be less than 10MB'
+ pdf_upload_failed: 'Failed to upload PDF to OpenAI'
+ pdf_upload_success: 'PDF uploaded successfully with file_id: %{file_id}'
+ pdf_processing_failed: 'Failed to process PDF document %{document_id}: %{error}'
+ pdf_processing_success: 'Successfully processed PDF document %{document_id}'
+ faq_generation_complete: 'FAQ generation complete. Total FAQs created: %{count}'
+ using_paginated_faq: 'Using paginated FAQ generation for document %{document_id}'
+ using_standard_faq: 'Using standard FAQ generation for document %{document_id}'
+ response_creation_error: 'Error in creating response document: %{error}'
+ missing_openai_file_id: 'Document must have openai_file_id for paginated processing'
+ openai_api_error: 'OpenAI API Error: %{error}'
+ starting_paginated_faq: 'Starting paginated FAQ generation (%{pages_per_chunk} pages per chunk)'
+ stopping_faq_generation: 'Stopping processing. Reason: %{reason}'
+ paginated_faq_complete: 'Paginated generation complete. Total FAQs: %{total_faqs}, Pages processed: %{pages_processed}'
+ processing_pages: 'Processing pages %{start}-%{end} (iteration %{iteration})'
+ chunk_generated: 'Chunk generated %{chunk_faqs} FAQs. Total so far: %{total_faqs}'
+ page_processing_error: 'Error processing pages %{start}-%{end}: %{error}'
public_portal:
search:
search_placeholder: Search for article by title or body...
@@ -358,6 +398,8 @@ az:
Transcript:
%{format_messages}
+ agent_capacity_policy:
+ inbox_already_assigned: 'Inbox has already been assigned to this policy'
portals:
send_instructions:
email_required: 'Email is required'
diff --git a/config/locales/bg.yml b/config/locales/bg.yml
index 5ca4765a8..6cef681dd 100644
--- a/config/locales/bg.yml
+++ b/config/locales/bg.yml
@@ -26,6 +26,9 @@ bg:
messages:
reset_password_success: Woot! Request for password reset is successful. Check your mail for instructions.
reset_password_failure: Uh ho! We could not find any user with the specified email.
+ reset_password_saml_user: This account uses SAML authentication. Password reset is not available. Please contact your administrator.
+ login_saml_user: This account uses SAML authentication. Please sign in through your organization's SAML provider.
+ saml_not_available: SAML authentication is not available in this installation.
inbox_deletetion_response: Your inbox deletion request will be processed in some time.
errors:
validations:
@@ -41,6 +44,8 @@ bg:
failed: Signup failed
assignment_policy:
not_found: Assignment policy not found
+ saml:
+ feature_not_enabled: SAML feature not enabled for this account
data_import:
data_type:
invalid: Invalid data type
@@ -84,6 +89,20 @@ bg:
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
+ mfa:
+ already_enabled: MFA is already enabled
+ not_enabled: MFA is not enabled
+ invalid_code: Invalid verification code
+ invalid_backup_code: Invalid backup code
+ invalid_token: Invalid or expired MFA token
+ invalid_credentials: Invalid credentials or verification code
+ feature_unavailable: MFA feature is not available. Please configure encryption keys.
+ profile:
+ mfa:
+ enabled: MFA enabled successfully
+ disabled: MFA disabled successfully
+ account_saml_settings:
+ invalid_certificate: must be a valid X.509 certificate in PEM format
reports:
period: Reporting period %{since} to %{until}
utc_warning: The report generated is in UTC timezone
@@ -209,6 +228,7 @@ bg:
reply:
email:
header:
+ notifications: 'Notifications'
from_with_name: '%{assignee_name} from %{inbox_name} <%{from_email}>'
reply_with_name: '%{assignee_name} from %{inbox_name} '
friendly_name: '%{sender_name} from %{business_name} <%{from_email}>'
@@ -278,6 +298,26 @@ bg:
completed_tool_call: 'Completed %{function_name} tool call'
invalid_tool_call: 'Invalid tool call'
tool_not_available: 'Tool not available'
+ documents:
+ limit_exceeded: 'Document limit exceeded'
+ pdf_format_error: 'must be a PDF file'
+ pdf_size_error: 'must be less than 10MB'
+ pdf_upload_failed: 'Failed to upload PDF to OpenAI'
+ pdf_upload_success: 'PDF uploaded successfully with file_id: %{file_id}'
+ pdf_processing_failed: 'Failed to process PDF document %{document_id}: %{error}'
+ pdf_processing_success: 'Successfully processed PDF document %{document_id}'
+ faq_generation_complete: 'FAQ generation complete. Total FAQs created: %{count}'
+ using_paginated_faq: 'Using paginated FAQ generation for document %{document_id}'
+ using_standard_faq: 'Using standard FAQ generation for document %{document_id}'
+ response_creation_error: 'Error in creating response document: %{error}'
+ missing_openai_file_id: 'Document must have openai_file_id for paginated processing'
+ openai_api_error: 'OpenAI API Error: %{error}'
+ starting_paginated_faq: 'Starting paginated FAQ generation (%{pages_per_chunk} pages per chunk)'
+ stopping_faq_generation: 'Stopping processing. Reason: %{reason}'
+ paginated_faq_complete: 'Paginated generation complete. Total FAQs: %{total_faqs}, Pages processed: %{pages_processed}'
+ processing_pages: 'Processing pages %{start}-%{end} (iteration %{iteration})'
+ chunk_generated: 'Chunk generated %{chunk_faqs} FAQs. Total so far: %{total_faqs}'
+ page_processing_error: 'Error processing pages %{start}-%{end}: %{error}'
public_portal:
search:
search_placeholder: Search for article by title or body...
@@ -358,6 +398,8 @@ bg:
Transcript:
%{format_messages}
+ agent_capacity_policy:
+ inbox_already_assigned: 'Inbox has already been assigned to this policy'
portals:
send_instructions:
email_required: 'Email is required'
diff --git a/config/locales/ca.yml b/config/locales/ca.yml
index e74015a14..2861b5a12 100644
--- a/config/locales/ca.yml
+++ b/config/locales/ca.yml
@@ -26,6 +26,9 @@ ca:
messages:
reset_password_success: Woot! S'ha restablert la contrasenya amb èxit. Revisa el correu per més instruccions.
reset_password_failure: Uh ho! No s'ha trobat cap compte amb aquest correu electrònic.
+ reset_password_saml_user: This account uses SAML authentication. Password reset is not available. Please contact your administrator.
+ login_saml_user: This account uses SAML authentication. Please sign in through your organization's SAML provider.
+ saml_not_available: SAML authentication is not available in this installation.
inbox_deletetion_response: La teva sol·licitud d'eliminació de la safata d'entrada es processarà d'aquí a un temps.
errors:
validations:
@@ -41,6 +44,8 @@ ca:
failed: El registre ha fallat
assignment_policy:
not_found: Assignment policy not found
+ saml:
+ feature_not_enabled: SAML feature not enabled for this account
data_import:
data_type:
invalid: Tipus de dades no vàlid
@@ -84,6 +89,20 @@ ca:
invalid_value: Valor no vàlid. Els valors proporcionats per a %{attribute_name} no són vàlids
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
+ mfa:
+ already_enabled: MFA is already enabled
+ not_enabled: MFA is not enabled
+ invalid_code: Invalid verification code
+ invalid_backup_code: Invalid backup code
+ invalid_token: Invalid or expired MFA token
+ invalid_credentials: Invalid credentials or verification code
+ feature_unavailable: MFA feature is not available. Please configure encryption keys.
+ profile:
+ mfa:
+ enabled: MFA enabled successfully
+ disabled: MFA disabled successfully
+ account_saml_settings:
+ invalid_certificate: must be a valid X.509 certificate in PEM format
reports:
period: Període d'informes %{since} a %{until}
utc_warning: L'informe generat es troba a la zona horària UTC
@@ -209,6 +228,7 @@ ca:
reply:
email:
header:
+ notifications: 'Notificacions'
from_with_name: '%{assignee_name} des de %{inbox_name} <%{from_email}>'
reply_with_name: '%{assignee_name} des de %{inbox_name} '
friendly_name: '%{sender_name} des de %{business_name} <%{from_email}>'
@@ -278,6 +298,26 @@ ca:
completed_tool_call: 'Completed %{function_name} tool call'
invalid_tool_call: 'Invalid tool call'
tool_not_available: 'Tool not available'
+ documents:
+ limit_exceeded: 'Document limit exceeded'
+ pdf_format_error: 'must be a PDF file'
+ pdf_size_error: 'must be less than 10MB'
+ pdf_upload_failed: 'Failed to upload PDF to OpenAI'
+ pdf_upload_success: 'PDF uploaded successfully with file_id: %{file_id}'
+ pdf_processing_failed: 'Failed to process PDF document %{document_id}: %{error}'
+ pdf_processing_success: 'Successfully processed PDF document %{document_id}'
+ faq_generation_complete: 'FAQ generation complete. Total FAQs created: %{count}'
+ using_paginated_faq: 'Using paginated FAQ generation for document %{document_id}'
+ using_standard_faq: 'Using standard FAQ generation for document %{document_id}'
+ response_creation_error: 'Error in creating response document: %{error}'
+ missing_openai_file_id: 'Document must have openai_file_id for paginated processing'
+ openai_api_error: 'OpenAI API Error: %{error}'
+ starting_paginated_faq: 'Starting paginated FAQ generation (%{pages_per_chunk} pages per chunk)'
+ stopping_faq_generation: 'Stopping processing. Reason: %{reason}'
+ paginated_faq_complete: 'Paginated generation complete. Total FAQs: %{total_faqs}, Pages processed: %{pages_processed}'
+ processing_pages: 'Processing pages %{start}-%{end} (iteration %{iteration})'
+ chunk_generated: 'Chunk generated %{chunk_faqs} FAQs. Total so far: %{total_faqs}'
+ page_processing_error: 'Error processing pages %{start}-%{end}: %{error}'
public_portal:
search:
search_placeholder: Cerca l'article per títol o cos...
@@ -358,6 +398,8 @@ ca:
Transcript:
%{format_messages}
+ agent_capacity_policy:
+ inbox_already_assigned: 'Inbox has already been assigned to this policy'
portals:
send_instructions:
email_required: 'El correu electrònic és obligatori'
diff --git a/config/locales/cs.yml b/config/locales/cs.yml
index f58991eae..1432220ff 100644
--- a/config/locales/cs.yml
+++ b/config/locales/cs.yml
@@ -26,6 +26,9 @@ cs:
messages:
reset_password_success: Woot! Žádost o obnovení hesla byla úspěšná. Zkontrolujte svůj e-mail pro pokyny.
reset_password_failure: Jejda! Nenašli jsme žádného uživatele se zadaným e-mailem.
+ reset_password_saml_user: This account uses SAML authentication. Password reset is not available. Please contact your administrator.
+ login_saml_user: This account uses SAML authentication. Please sign in through your organization's SAML provider.
+ saml_not_available: SAML authentication is not available in this installation.
inbox_deletetion_response: Your inbox deletion request will be processed in some time.
errors:
validations:
@@ -41,6 +44,8 @@ cs:
failed: Registrace se nezdařila
assignment_policy:
not_found: Assignment policy not found
+ saml:
+ feature_not_enabled: SAML feature not enabled for this account
data_import:
data_type:
invalid: Invalid data type
@@ -84,6 +89,20 @@ cs:
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
+ mfa:
+ already_enabled: MFA is already enabled
+ not_enabled: MFA is not enabled
+ invalid_code: Invalid verification code
+ invalid_backup_code: Invalid backup code
+ invalid_token: Invalid or expired MFA token
+ invalid_credentials: Invalid credentials or verification code
+ feature_unavailable: MFA feature is not available. Please configure encryption keys.
+ profile:
+ mfa:
+ enabled: MFA enabled successfully
+ disabled: MFA disabled successfully
+ account_saml_settings:
+ invalid_certificate: must be a valid X.509 certificate in PEM format
reports:
period: Reporting period %{since} to %{until}
utc_warning: The report generated is in UTC timezone
@@ -209,6 +228,7 @@ cs:
reply:
email:
header:
+ notifications: 'Oznámení'
from_with_name: '%{assignee_name} from %{inbox_name} <%{from_email}>'
reply_with_name: '%{assignee_name} from %{inbox_name} '
friendly_name: '%{sender_name} from %{business_name} <%{from_email}>'
@@ -278,6 +298,26 @@ cs:
completed_tool_call: 'Completed %{function_name} tool call'
invalid_tool_call: 'Invalid tool call'
tool_not_available: 'Tool not available'
+ documents:
+ limit_exceeded: 'Document limit exceeded'
+ pdf_format_error: 'must be a PDF file'
+ pdf_size_error: 'must be less than 10MB'
+ pdf_upload_failed: 'Failed to upload PDF to OpenAI'
+ pdf_upload_success: 'PDF uploaded successfully with file_id: %{file_id}'
+ pdf_processing_failed: 'Failed to process PDF document %{document_id}: %{error}'
+ pdf_processing_success: 'Successfully processed PDF document %{document_id}'
+ faq_generation_complete: 'FAQ generation complete. Total FAQs created: %{count}'
+ using_paginated_faq: 'Using paginated FAQ generation for document %{document_id}'
+ using_standard_faq: 'Using standard FAQ generation for document %{document_id}'
+ response_creation_error: 'Error in creating response document: %{error}'
+ missing_openai_file_id: 'Document must have openai_file_id for paginated processing'
+ openai_api_error: 'OpenAI API Error: %{error}'
+ starting_paginated_faq: 'Starting paginated FAQ generation (%{pages_per_chunk} pages per chunk)'
+ stopping_faq_generation: 'Stopping processing. Reason: %{reason}'
+ paginated_faq_complete: 'Paginated generation complete. Total FAQs: %{total_faqs}, Pages processed: %{pages_processed}'
+ processing_pages: 'Processing pages %{start}-%{end} (iteration %{iteration})'
+ chunk_generated: 'Chunk generated %{chunk_faqs} FAQs. Total so far: %{total_faqs}'
+ page_processing_error: 'Error processing pages %{start}-%{end}: %{error}'
public_portal:
search:
search_placeholder: Search for article by title or body...
@@ -366,6 +406,8 @@ cs:
Transcript:
%{format_messages}
+ agent_capacity_policy:
+ inbox_already_assigned: 'Inbox has already been assigned to this policy'
portals:
send_instructions:
email_required: 'Email is required'
diff --git a/config/locales/da.yml b/config/locales/da.yml
index fecd42250..08d090b41 100644
--- a/config/locales/da.yml
+++ b/config/locales/da.yml
@@ -26,6 +26,9 @@ da:
messages:
reset_password_success: Woot! Anmodning om nulstilling af adgangskode er vellykket. Tjek din mail for instruktioner.
reset_password_failure: Åh nej! Vi kunne ikke finde nogen bruger med den angivne e-mail.
+ reset_password_saml_user: This account uses SAML authentication. Password reset is not available. Please contact your administrator.
+ login_saml_user: This account uses SAML authentication. Please sign in through your organization's SAML provider.
+ saml_not_available: SAML authentication is not available in this installation.
inbox_deletetion_response: Your inbox deletion request will be processed in some time.
errors:
validations:
@@ -41,6 +44,8 @@ da:
failed: Tilmelding mislykkedes
assignment_policy:
not_found: Assignment policy not found
+ saml:
+ feature_not_enabled: SAML feature not enabled for this account
data_import:
data_type:
invalid: Ugyldig datatype
@@ -84,6 +89,20 @@ da:
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
+ mfa:
+ already_enabled: MFA is already enabled
+ not_enabled: MFA is not enabled
+ invalid_code: Invalid verification code
+ invalid_backup_code: Invalid backup code
+ invalid_token: Invalid or expired MFA token
+ invalid_credentials: Invalid credentials or verification code
+ feature_unavailable: MFA feature is not available. Please configure encryption keys.
+ profile:
+ mfa:
+ enabled: MFA enabled successfully
+ disabled: MFA disabled successfully
+ account_saml_settings:
+ invalid_certificate: must be a valid X.509 certificate in PEM format
reports:
period: Rapporteringsperiode %{since} til %{until}
utc_warning: The report generated is in UTC timezone
@@ -209,6 +228,7 @@ da:
reply:
email:
header:
+ notifications: 'Notifikationer'
from_with_name: '%{assignee_name} fra %{inbox_name} <%{from_email}>'
reply_with_name: '%{assignee_name} fra %{inbox_name} '
friendly_name: '%{sender_name} fra %{business_name} <%{from_email}>'
@@ -278,6 +298,26 @@ da:
completed_tool_call: 'Completed %{function_name} tool call'
invalid_tool_call: 'Invalid tool call'
tool_not_available: 'Tool not available'
+ documents:
+ limit_exceeded: 'Document limit exceeded'
+ pdf_format_error: 'must be a PDF file'
+ pdf_size_error: 'must be less than 10MB'
+ pdf_upload_failed: 'Failed to upload PDF to OpenAI'
+ pdf_upload_success: 'PDF uploaded successfully with file_id: %{file_id}'
+ pdf_processing_failed: 'Failed to process PDF document %{document_id}: %{error}'
+ pdf_processing_success: 'Successfully processed PDF document %{document_id}'
+ faq_generation_complete: 'FAQ generation complete. Total FAQs created: %{count}'
+ using_paginated_faq: 'Using paginated FAQ generation for document %{document_id}'
+ using_standard_faq: 'Using standard FAQ generation for document %{document_id}'
+ response_creation_error: 'Error in creating response document: %{error}'
+ missing_openai_file_id: 'Document must have openai_file_id for paginated processing'
+ openai_api_error: 'OpenAI API Error: %{error}'
+ starting_paginated_faq: 'Starting paginated FAQ generation (%{pages_per_chunk} pages per chunk)'
+ stopping_faq_generation: 'Stopping processing. Reason: %{reason}'
+ paginated_faq_complete: 'Paginated generation complete. Total FAQs: %{total_faqs}, Pages processed: %{pages_processed}'
+ processing_pages: 'Processing pages %{start}-%{end} (iteration %{iteration})'
+ chunk_generated: 'Chunk generated %{chunk_faqs} FAQs. Total so far: %{total_faqs}'
+ page_processing_error: 'Error processing pages %{start}-%{end}: %{error}'
public_portal:
search:
search_placeholder: Search for article by title or body...
@@ -358,6 +398,8 @@ da:
Transcript:
%{format_messages}
+ agent_capacity_policy:
+ inbox_already_assigned: 'Inbox has already been assigned to this policy'
portals:
send_instructions:
email_required: 'Email is required'
diff --git a/config/locales/de.yml b/config/locales/de.yml
index c7a81c971..4e37949a0 100644
--- a/config/locales/de.yml
+++ b/config/locales/de.yml
@@ -26,6 +26,9 @@ de:
messages:
reset_password_success: Woot! Die Anforderung zum Zurücksetzen des Passworts ist erfolgreich. Überprüfen Sie Ihre E-Mails auf Anweisungen.
reset_password_failure: Uh ho! Wir konnten keinen Benutzer mit der angegebenen E-Mail-Adresse finden.
+ reset_password_saml_user: This account uses SAML authentication. Password reset is not available. Please contact your administrator.
+ login_saml_user: This account uses SAML authentication. Please sign in through your organization's SAML provider.
+ saml_not_available: SAML authentication is not available in this installation.
inbox_deletetion_response: Die Löschanfrage Ihres Posteingangs wird in Kürze bearbeitet.
errors:
validations:
@@ -41,6 +44,8 @@ de:
failed: Anmeldung gescheitert
assignment_policy:
not_found: Assignment policy not found
+ saml:
+ feature_not_enabled: SAML feature not enabled for this account
data_import:
data_type:
invalid: Ungültiger Datentyp
@@ -84,6 +89,20 @@ de:
invalid_value: Ungültiger Wert. Die Werte für %{attribute_name} sind ungültig
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
+ mfa:
+ already_enabled: MFA is already enabled
+ not_enabled: MFA is not enabled
+ invalid_code: Invalid verification code
+ invalid_backup_code: Invalid backup code
+ invalid_token: Invalid or expired MFA token
+ invalid_credentials: Invalid credentials or verification code
+ feature_unavailable: MFA feature is not available. Please configure encryption keys.
+ profile:
+ mfa:
+ enabled: MFA enabled successfully
+ disabled: MFA disabled successfully
+ account_saml_settings:
+ invalid_certificate: must be a valid X.509 certificate in PEM format
reports:
period: Berichtszeitraum von %{since} bis %{until}
utc_warning: Der generierte Bericht ist in UTC-Zeitzone
@@ -209,6 +228,7 @@ de:
reply:
email:
header:
+ notifications: 'Push-Benachrichtigungen'
from_with_name: '%{assignee_name} von %{inbox_name} <%{from_email}>'
reply_with_name: '%{assignee_name} von %{inbox_name} '
friendly_name: '%{sender_name} von %{business_name} <%{from_email}>'
@@ -278,6 +298,26 @@ de:
completed_tool_call: 'Completed %{function_name} tool call'
invalid_tool_call: 'Invalid tool call'
tool_not_available: 'Tool not available'
+ documents:
+ limit_exceeded: 'Document limit exceeded'
+ pdf_format_error: 'must be a PDF file'
+ pdf_size_error: 'must be less than 10MB'
+ pdf_upload_failed: 'Failed to upload PDF to OpenAI'
+ pdf_upload_success: 'PDF uploaded successfully with file_id: %{file_id}'
+ pdf_processing_failed: 'Failed to process PDF document %{document_id}: %{error}'
+ pdf_processing_success: 'Successfully processed PDF document %{document_id}'
+ faq_generation_complete: 'FAQ generation complete. Total FAQs created: %{count}'
+ using_paginated_faq: 'Using paginated FAQ generation for document %{document_id}'
+ using_standard_faq: 'Using standard FAQ generation for document %{document_id}'
+ response_creation_error: 'Error in creating response document: %{error}'
+ missing_openai_file_id: 'Document must have openai_file_id for paginated processing'
+ openai_api_error: 'OpenAI API Error: %{error}'
+ starting_paginated_faq: 'Starting paginated FAQ generation (%{pages_per_chunk} pages per chunk)'
+ stopping_faq_generation: 'Stopping processing. Reason: %{reason}'
+ paginated_faq_complete: 'Paginated generation complete. Total FAQs: %{total_faqs}, Pages processed: %{pages_processed}'
+ processing_pages: 'Processing pages %{start}-%{end} (iteration %{iteration})'
+ chunk_generated: 'Chunk generated %{chunk_faqs} FAQs. Total so far: %{total_faqs}'
+ page_processing_error: 'Error processing pages %{start}-%{end}: %{error}'
public_portal:
search:
search_placeholder: Artikel nach Titel oder Text suchen...
@@ -358,6 +398,8 @@ de:
Transcript:
%{format_messages}
+ agent_capacity_policy:
+ inbox_already_assigned: 'Inbox has already been assigned to this policy'
portals:
send_instructions:
email_required: 'E-Mail ist erforderlich'
diff --git a/config/locales/el.yml b/config/locales/el.yml
index 211782256..830e4da8d 100644
--- a/config/locales/el.yml
+++ b/config/locales/el.yml
@@ -26,6 +26,9 @@ el:
messages:
reset_password_success: Woot! Το αίτημά σας για επαναφορά κωδικού ενεργοποιήθηκε. Ελέξτε το email σας για οδηγίες.
reset_password_failure: Ωχ όχι! Δεν υπάρχει κάποιος χρήστης με το συγκεκριμένο email.
+ reset_password_saml_user: This account uses SAML authentication. Password reset is not available. Please contact your administrator.
+ login_saml_user: This account uses SAML authentication. Please sign in through your organization's SAML provider.
+ saml_not_available: SAML authentication is not available in this installation.
inbox_deletetion_response: Your inbox deletion request will be processed in some time.
errors:
validations:
@@ -41,6 +44,8 @@ el:
failed: Η εγγραφή απέτυχε
assignment_policy:
not_found: Assignment policy not found
+ saml:
+ feature_not_enabled: SAML feature not enabled for this account
data_import:
data_type:
invalid: Μη έγκυρος τύπος δεδομένων
@@ -84,6 +89,20 @@ el:
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
+ mfa:
+ already_enabled: MFA is already enabled
+ not_enabled: MFA is not enabled
+ invalid_code: Invalid verification code
+ invalid_backup_code: Invalid backup code
+ invalid_token: Invalid or expired MFA token
+ invalid_credentials: Invalid credentials or verification code
+ feature_unavailable: MFA feature is not available. Please configure encryption keys.
+ profile:
+ mfa:
+ enabled: MFA enabled successfully
+ disabled: MFA disabled successfully
+ account_saml_settings:
+ invalid_certificate: must be a valid X.509 certificate in PEM format
reports:
period: Περίοδος αναφοράς %{since} έως %{until}
utc_warning: The report generated is in UTC timezone
@@ -209,6 +228,7 @@ el:
reply:
email:
header:
+ notifications: 'Ειδοποιήσεις'
from_with_name: '%{assignee_name} από %{inbox_name} <%{from_email}>'
reply_with_name: '%{assignee_name} από %{inbox_name} '
friendly_name: '%{sender_name} από %{business_name} <%{from_email}>'
@@ -278,6 +298,26 @@ el:
completed_tool_call: 'Completed %{function_name} tool call'
invalid_tool_call: 'Invalid tool call'
tool_not_available: 'Tool not available'
+ documents:
+ limit_exceeded: 'Document limit exceeded'
+ pdf_format_error: 'must be a PDF file'
+ pdf_size_error: 'must be less than 10MB'
+ pdf_upload_failed: 'Failed to upload PDF to OpenAI'
+ pdf_upload_success: 'PDF uploaded successfully with file_id: %{file_id}'
+ pdf_processing_failed: 'Failed to process PDF document %{document_id}: %{error}'
+ pdf_processing_success: 'Successfully processed PDF document %{document_id}'
+ faq_generation_complete: 'FAQ generation complete. Total FAQs created: %{count}'
+ using_paginated_faq: 'Using paginated FAQ generation for document %{document_id}'
+ using_standard_faq: 'Using standard FAQ generation for document %{document_id}'
+ response_creation_error: 'Error in creating response document: %{error}'
+ missing_openai_file_id: 'Document must have openai_file_id for paginated processing'
+ openai_api_error: 'OpenAI API Error: %{error}'
+ starting_paginated_faq: 'Starting paginated FAQ generation (%{pages_per_chunk} pages per chunk)'
+ stopping_faq_generation: 'Stopping processing. Reason: %{reason}'
+ paginated_faq_complete: 'Paginated generation complete. Total FAQs: %{total_faqs}, Pages processed: %{pages_processed}'
+ processing_pages: 'Processing pages %{start}-%{end} (iteration %{iteration})'
+ chunk_generated: 'Chunk generated %{chunk_faqs} FAQs. Total so far: %{total_faqs}'
+ page_processing_error: 'Error processing pages %{start}-%{end}: %{error}'
public_portal:
search:
search_placeholder: Αναζήτηση άρθρου με τίτλο ή περιεχόμενο...
@@ -358,6 +398,8 @@ el:
Transcript:
%{format_messages}
+ agent_capacity_policy:
+ inbox_already_assigned: 'Inbox has already been assigned to this policy'
portals:
send_instructions:
email_required: 'Email is required'
diff --git a/config/locales/en.yml b/config/locales/en.yml
index cb72bb699..ca3a9e950 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -36,9 +36,16 @@ en:
success: 'Channel reauthorized successfully'
not_required: 'Reauthorization is not required for this inbox'
invalid_channel: 'Invalid channel type for reauthorization'
+ auth:
+ saml:
+ invalid_email: 'Please enter a valid email address'
+ authentication_failed: 'Authentication failed. Please check your credentials and try again.'
messages:
reset_password_success: Woot! Request for password reset is successful. Check your mail for instructions.
reset_password_failure: Uh ho! We could not find any user with the specified email.
+ reset_password_saml_user: This account uses SAML authentication. Password reset is not available. Please contact your administrator.
+ login_saml_user: This account uses SAML authentication. Please sign in through your organization's SAML provider.
+ saml_not_available: SAML authentication is not available in this installation.
inbox_deletetion_response: Your inbox deletion request will be processed in some time.
errors:
@@ -55,6 +62,8 @@ en:
failed: Signup failed
assignment_policy:
not_found: Assignment policy not found
+ saml:
+ feature_not_enabled: SAML feature not enabled for this account
data_import:
data_type:
invalid: Invalid data type
@@ -98,6 +107,20 @@ en:
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
+ mfa:
+ already_enabled: MFA is already enabled
+ not_enabled: MFA is not enabled
+ invalid_code: Invalid verification code
+ invalid_backup_code: Invalid backup code
+ invalid_token: Invalid or expired MFA token
+ invalid_credentials: Invalid credentials or verification code
+ feature_unavailable: MFA feature is not available. Please configure encryption keys.
+ profile:
+ mfa:
+ enabled: MFA enabled successfully
+ disabled: MFA disabled successfully
+ account_saml_settings:
+ invalid_certificate: must be a valid X.509 certificate in PEM format
reports:
period: Reporting period %{since} to %{until}
utc_warning: The report generated is in UTC timezone
@@ -223,6 +246,7 @@ en:
reply:
email:
header:
+ notifications: 'Notifications'
from_with_name: '%{assignee_name} from %{inbox_name} <%{from_email}>'
reply_with_name: '%{assignee_name} from %{inbox_name} '
friendly_name: '%{sender_name} from %{business_name} <%{from_email}>'
diff --git a/config/locales/es.yml b/config/locales/es.yml
index 06da2c054..3aec3acbd 100644
--- a/config/locales/es.yml
+++ b/config/locales/es.yml
@@ -26,6 +26,9 @@ es:
messages:
reset_password_success: '¡Genial! La solicitud de restablecimiento de contraseña ha sido exitosa. Revisa tu correo para ver las instrucciones.'
reset_password_failure: '¡Uh ho! No hemos podido encontrar ningún usuario con el correo electrónico especificado.'
+ reset_password_saml_user: This account uses SAML authentication. Password reset is not available. Please contact your administrator.
+ login_saml_user: This account uses SAML authentication. Please sign in through your organization's SAML provider.
+ saml_not_available: SAML authentication is not available in this installation.
inbox_deletetion_response: Su solicitud de eliminación de la bandeja de entrada será procesada en algún tiempo.
errors:
validations:
@@ -41,6 +44,8 @@ es:
failed: Registro fallido
assignment_policy:
not_found: Assignment policy not found
+ saml:
+ feature_not_enabled: SAML feature not enabled for this account
data_import:
data_type:
invalid: Tipo de datos no válido
@@ -84,6 +89,20 @@ es:
invalid_value: Valor no válido. Los valores proporcionados para %{attribute_name} no son válidos
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
+ mfa:
+ already_enabled: MFA is already enabled
+ not_enabled: MFA is not enabled
+ invalid_code: Invalid verification code
+ invalid_backup_code: Invalid backup code
+ invalid_token: Invalid or expired MFA token
+ invalid_credentials: Invalid credentials or verification code
+ feature_unavailable: MFA feature is not available. Please configure encryption keys.
+ profile:
+ mfa:
+ enabled: MFA enabled successfully
+ disabled: MFA disabled successfully
+ account_saml_settings:
+ invalid_certificate: must be a valid X.509 certificate in PEM format
reports:
period: Reportando el periodo desde %{since} hasta %{until}
utc_warning: El informe generado está en zona horaria UTC
@@ -209,6 +228,7 @@ es:
reply:
email:
header:
+ notifications: 'Notificaciones'
from_with_name: '%{assignee_name} de %{inbox_name} <%{from_email}>'
reply_with_name: '%{assignee_name} de %{inbox_name} '
friendly_name: '%{sender_name} de %{business_name} <%{from_email}>'
@@ -278,6 +298,26 @@ es:
completed_tool_call: 'Completed %{function_name} tool call'
invalid_tool_call: 'Invalid tool call'
tool_not_available: 'Tool not available'
+ documents:
+ limit_exceeded: 'Document limit exceeded'
+ pdf_format_error: 'must be a PDF file'
+ pdf_size_error: 'must be less than 10MB'
+ pdf_upload_failed: 'Failed to upload PDF to OpenAI'
+ pdf_upload_success: 'PDF uploaded successfully with file_id: %{file_id}'
+ pdf_processing_failed: 'Failed to process PDF document %{document_id}: %{error}'
+ pdf_processing_success: 'Successfully processed PDF document %{document_id}'
+ faq_generation_complete: 'FAQ generation complete. Total FAQs created: %{count}'
+ using_paginated_faq: 'Using paginated FAQ generation for document %{document_id}'
+ using_standard_faq: 'Using standard FAQ generation for document %{document_id}'
+ response_creation_error: 'Error in creating response document: %{error}'
+ missing_openai_file_id: 'Document must have openai_file_id for paginated processing'
+ openai_api_error: 'OpenAI API Error: %{error}'
+ starting_paginated_faq: 'Starting paginated FAQ generation (%{pages_per_chunk} pages per chunk)'
+ stopping_faq_generation: 'Stopping processing. Reason: %{reason}'
+ paginated_faq_complete: 'Paginated generation complete. Total FAQs: %{total_faqs}, Pages processed: %{pages_processed}'
+ processing_pages: 'Processing pages %{start}-%{end} (iteration %{iteration})'
+ chunk_generated: 'Chunk generated %{chunk_faqs} FAQs. Total so far: %{total_faqs}'
+ page_processing_error: 'Error processing pages %{start}-%{end}: %{error}'
public_portal:
search:
search_placeholder: Buscar artículo por título o cuerpo...
@@ -358,6 +398,8 @@ es:
Transcript:
%{format_messages}
+ agent_capacity_policy:
+ inbox_already_assigned: 'Inbox has already been assigned to this policy'
portals:
send_instructions:
email_required: 'El email es requerido'
diff --git a/config/locales/fa.yml b/config/locales/fa.yml
index 703916b83..78137eca1 100644
--- a/config/locales/fa.yml
+++ b/config/locales/fa.yml
@@ -26,6 +26,9 @@ fa:
messages:
reset_password_success: سوت! درخواست ریست شدن رمز عبور با موفقیت ارسال شد. ایمیل خود را چک کنید
reset_password_failure: اوه نه! کاربری با چنین ایمیلی وجود ندارد
+ reset_password_saml_user: This account uses SAML authentication. Password reset is not available. Please contact your administrator.
+ login_saml_user: This account uses SAML authentication. Please sign in through your organization's SAML provider.
+ saml_not_available: SAML authentication is not available in this installation.
inbox_deletetion_response: درخواست حذف صندوق ورودی شما پس از مدتی پردازش خواهد شد.
errors:
validations:
@@ -41,6 +44,8 @@ fa:
failed: ثبت نام ناموفق بود
assignment_policy:
not_found: Assignment policy not found
+ saml:
+ feature_not_enabled: SAML feature not enabled for this account
data_import:
data_type:
invalid: نوع داده نامعتبر است
@@ -84,6 +89,20 @@ fa:
invalid_value: مقدار معتبر نیست. مقادیر ارائه شده برای %{attribute_name} معتبر نیست
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
+ mfa:
+ already_enabled: MFA is already enabled
+ not_enabled: MFA is not enabled
+ invalid_code: Invalid verification code
+ invalid_backup_code: Invalid backup code
+ invalid_token: Invalid or expired MFA token
+ invalid_credentials: Invalid credentials or verification code
+ feature_unavailable: MFA feature is not available. Please configure encryption keys.
+ profile:
+ mfa:
+ enabled: MFA enabled successfully
+ disabled: MFA disabled successfully
+ account_saml_settings:
+ invalid_certificate: must be a valid X.509 certificate in PEM format
reports:
period: زمان گزارش از %{since} تا %{until}
utc_warning: گزارش تولید شده در منطقه زمانی UTC است
@@ -209,6 +228,7 @@ fa:
reply:
email:
header:
+ notifications: 'اعلان ها'
from_with_name: '%{assignee_name} از %{inbox_name} «%{from_email}»'
reply_with_name: '%{assignee_name} از %{inbox_name} '
friendly_name: '%{sender_name} از %{business_name} «%{from_email}»'
@@ -278,6 +298,26 @@ fa:
completed_tool_call: 'Completed %{function_name} tool call'
invalid_tool_call: 'Invalid tool call'
tool_not_available: 'Tool not available'
+ documents:
+ limit_exceeded: 'Document limit exceeded'
+ pdf_format_error: 'must be a PDF file'
+ pdf_size_error: 'must be less than 10MB'
+ pdf_upload_failed: 'Failed to upload PDF to OpenAI'
+ pdf_upload_success: 'PDF uploaded successfully with file_id: %{file_id}'
+ pdf_processing_failed: 'Failed to process PDF document %{document_id}: %{error}'
+ pdf_processing_success: 'Successfully processed PDF document %{document_id}'
+ faq_generation_complete: 'FAQ generation complete. Total FAQs created: %{count}'
+ using_paginated_faq: 'Using paginated FAQ generation for document %{document_id}'
+ using_standard_faq: 'Using standard FAQ generation for document %{document_id}'
+ response_creation_error: 'Error in creating response document: %{error}'
+ missing_openai_file_id: 'Document must have openai_file_id for paginated processing'
+ openai_api_error: 'OpenAI API Error: %{error}'
+ starting_paginated_faq: 'Starting paginated FAQ generation (%{pages_per_chunk} pages per chunk)'
+ stopping_faq_generation: 'Stopping processing. Reason: %{reason}'
+ paginated_faq_complete: 'Paginated generation complete. Total FAQs: %{total_faqs}, Pages processed: %{pages_processed}'
+ processing_pages: 'Processing pages %{start}-%{end} (iteration %{iteration})'
+ chunk_generated: 'Chunk generated %{chunk_faqs} FAQs. Total so far: %{total_faqs}'
+ page_processing_error: 'Error processing pages %{start}-%{end}: %{error}'
public_portal:
search:
search_placeholder: جستجوی مقاله براساس عنوان یا متن...
@@ -358,6 +398,8 @@ fa:
Transcript:
%{format_messages}
+ agent_capacity_policy:
+ inbox_already_assigned: 'Inbox has already been assigned to this policy'
portals:
send_instructions:
email_required: 'ایمیل الزامی است'
diff --git a/config/locales/fi.yml b/config/locales/fi.yml
index 9c8e4e988..7a2c675ee 100644
--- a/config/locales/fi.yml
+++ b/config/locales/fi.yml
@@ -26,6 +26,9 @@ fi:
messages:
reset_password_success: Woot! Salasanan nollauspyyntö onnistui. Tarkista sähköpostisi saadaksesi ohjeita.
reset_password_failure: Hö! Emme löytäneet yhtään käyttäjää määritellyllä sähköpostilla.
+ reset_password_saml_user: This account uses SAML authentication. Password reset is not available. Please contact your administrator.
+ login_saml_user: This account uses SAML authentication. Please sign in through your organization's SAML provider.
+ saml_not_available: SAML authentication is not available in this installation.
inbox_deletetion_response: Your inbox deletion request will be processed in some time.
errors:
validations:
@@ -41,6 +44,8 @@ fi:
failed: Rekisteröityminen epäonnistui
assignment_policy:
not_found: Assignment policy not found
+ saml:
+ feature_not_enabled: SAML feature not enabled for this account
data_import:
data_type:
invalid: Invalid data type
@@ -84,6 +89,20 @@ fi:
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
+ mfa:
+ already_enabled: MFA is already enabled
+ not_enabled: MFA is not enabled
+ invalid_code: Invalid verification code
+ invalid_backup_code: Invalid backup code
+ invalid_token: Invalid or expired MFA token
+ invalid_credentials: Invalid credentials or verification code
+ feature_unavailable: MFA feature is not available. Please configure encryption keys.
+ profile:
+ mfa:
+ enabled: MFA enabled successfully
+ disabled: MFA disabled successfully
+ account_saml_settings:
+ invalid_certificate: must be a valid X.509 certificate in PEM format
reports:
period: Raportointijakso %{since} – %{until}
utc_warning: The report generated is in UTC timezone
@@ -209,6 +228,7 @@ fi:
reply:
email:
header:
+ notifications: 'Ilmoitukset'
from_with_name: '%{assignee_name} from %{inbox_name} <%{from_email}>'
reply_with_name: '%{assignee_name} from %{inbox_name} '
friendly_name: '%{sender_name} from %{business_name} <%{from_email}>'
@@ -278,6 +298,26 @@ fi:
completed_tool_call: 'Completed %{function_name} tool call'
invalid_tool_call: 'Invalid tool call'
tool_not_available: 'Tool not available'
+ documents:
+ limit_exceeded: 'Document limit exceeded'
+ pdf_format_error: 'must be a PDF file'
+ pdf_size_error: 'must be less than 10MB'
+ pdf_upload_failed: 'Failed to upload PDF to OpenAI'
+ pdf_upload_success: 'PDF uploaded successfully with file_id: %{file_id}'
+ pdf_processing_failed: 'Failed to process PDF document %{document_id}: %{error}'
+ pdf_processing_success: 'Successfully processed PDF document %{document_id}'
+ faq_generation_complete: 'FAQ generation complete. Total FAQs created: %{count}'
+ using_paginated_faq: 'Using paginated FAQ generation for document %{document_id}'
+ using_standard_faq: 'Using standard FAQ generation for document %{document_id}'
+ response_creation_error: 'Error in creating response document: %{error}'
+ missing_openai_file_id: 'Document must have openai_file_id for paginated processing'
+ openai_api_error: 'OpenAI API Error: %{error}'
+ starting_paginated_faq: 'Starting paginated FAQ generation (%{pages_per_chunk} pages per chunk)'
+ stopping_faq_generation: 'Stopping processing. Reason: %{reason}'
+ paginated_faq_complete: 'Paginated generation complete. Total FAQs: %{total_faqs}, Pages processed: %{pages_processed}'
+ processing_pages: 'Processing pages %{start}-%{end} (iteration %{iteration})'
+ chunk_generated: 'Chunk generated %{chunk_faqs} FAQs. Total so far: %{total_faqs}'
+ page_processing_error: 'Error processing pages %{start}-%{end}: %{error}'
public_portal:
search:
search_placeholder: Search for article by title or body...
@@ -358,6 +398,8 @@ fi:
Transcript:
%{format_messages}
+ agent_capacity_policy:
+ inbox_already_assigned: 'Inbox has already been assigned to this policy'
portals:
send_instructions:
email_required: 'Sähköpostiosoite vaaditaan'
diff --git a/config/locales/fr.yml b/config/locales/fr.yml
index 0645826f5..b9552db4a 100644
--- a/config/locales/fr.yml
+++ b/config/locales/fr.yml
@@ -26,6 +26,9 @@ fr:
messages:
reset_password_success: Super ! La demande de réinitialisation du mot de passe a réussi. Consultez vos e-mails pour obtenir des instructions.
reset_password_failure: Oh oh ! Nous n'avons trouvé aucun utilisateur avec le courriel spécifié.
+ reset_password_saml_user: This account uses SAML authentication. Password reset is not available. Please contact your administrator.
+ login_saml_user: This account uses SAML authentication. Please sign in through your organization's SAML provider.
+ saml_not_available: SAML authentication is not available in this installation.
inbox_deletetion_response: Votre demande de suppression de la boîte de réception sera traitée dans un certain délai.
errors:
validations:
@@ -41,6 +44,8 @@ fr:
failed: L'inscription a échoué
assignment_policy:
not_found: Assignment policy not found
+ saml:
+ feature_not_enabled: SAML feature not enabled for this account
data_import:
data_type:
invalid: Type de données incorrect
@@ -84,6 +89,20 @@ fr:
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
+ mfa:
+ already_enabled: MFA is already enabled
+ not_enabled: MFA is not enabled
+ invalid_code: Invalid verification code
+ invalid_backup_code: Invalid backup code
+ invalid_token: Invalid or expired MFA token
+ invalid_credentials: Invalid credentials or verification code
+ feature_unavailable: MFA feature is not available. Please configure encryption keys.
+ profile:
+ mfa:
+ enabled: MFA enabled successfully
+ disabled: MFA disabled successfully
+ account_saml_settings:
+ invalid_certificate: must be a valid X.509 certificate in PEM format
reports:
period: Période de rapport %{since} à %{until}
utc_warning: Le rapport généré est dans le fuseau horaire UTC
@@ -209,6 +228,7 @@ fr:
reply:
email:
header:
+ notifications: 'Notifications'
from_with_name: '%{assignee_name} de %{inbox_name} <%{from_email}>'
reply_with_name: '%{assignee_name} de %{inbox_name} '
friendly_name: '%{sender_name} de %{business_name} <%{from_email}>'
@@ -278,6 +298,26 @@ fr:
completed_tool_call: 'Completed %{function_name} tool call'
invalid_tool_call: 'Invalid tool call'
tool_not_available: 'Tool not available'
+ documents:
+ limit_exceeded: 'Document limit exceeded'
+ pdf_format_error: 'must be a PDF file'
+ pdf_size_error: 'must be less than 10MB'
+ pdf_upload_failed: 'Failed to upload PDF to OpenAI'
+ pdf_upload_success: 'PDF uploaded successfully with file_id: %{file_id}'
+ pdf_processing_failed: 'Failed to process PDF document %{document_id}: %{error}'
+ pdf_processing_success: 'Successfully processed PDF document %{document_id}'
+ faq_generation_complete: 'FAQ generation complete. Total FAQs created: %{count}'
+ using_paginated_faq: 'Using paginated FAQ generation for document %{document_id}'
+ using_standard_faq: 'Using standard FAQ generation for document %{document_id}'
+ response_creation_error: 'Error in creating response document: %{error}'
+ missing_openai_file_id: 'Document must have openai_file_id for paginated processing'
+ openai_api_error: 'OpenAI API Error: %{error}'
+ starting_paginated_faq: 'Starting paginated FAQ generation (%{pages_per_chunk} pages per chunk)'
+ stopping_faq_generation: 'Stopping processing. Reason: %{reason}'
+ paginated_faq_complete: 'Paginated generation complete. Total FAQs: %{total_faqs}, Pages processed: %{pages_processed}'
+ processing_pages: 'Processing pages %{start}-%{end} (iteration %{iteration})'
+ chunk_generated: 'Chunk generated %{chunk_faqs} FAQs. Total so far: %{total_faqs}'
+ page_processing_error: 'Error processing pages %{start}-%{end}: %{error}'
public_portal:
search:
search_placeholder: Rechercher un article par titre ou contenu...
@@ -358,6 +398,8 @@ fr:
Transcription :
%{format_messages}
+ agent_capacity_policy:
+ inbox_already_assigned: 'Inbox has already been assigned to this policy'
portals:
send_instructions:
email_required: 'L''e-mail est requis'
diff --git a/config/locales/he.yml b/config/locales/he.yml
index a00832de7..ee144312a 100644
--- a/config/locales/he.yml
+++ b/config/locales/he.yml
@@ -26,6 +26,9 @@ he:
messages:
reset_password_success: יאס! בקשה לאיפוס ססמה נשלחה בהצלחה. בדוק תיבת מייל להוראות.
reset_password_failure: אופס! לא מצאנו משתמש עם המייל שצוין.
+ reset_password_saml_user: This account uses SAML authentication. Password reset is not available. Please contact your administrator.
+ login_saml_user: This account uses SAML authentication. Please sign in through your organization's SAML provider.
+ saml_not_available: SAML authentication is not available in this installation.
inbox_deletetion_response: Your inbox deletion request will be processed in some time.
errors:
validations:
@@ -41,6 +44,8 @@ he:
failed: הרשמה נכשלה
assignment_policy:
not_found: Assignment policy not found
+ saml:
+ feature_not_enabled: SAML feature not enabled for this account
data_import:
data_type:
invalid: Invalid data type
@@ -84,6 +89,20 @@ he:
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
+ mfa:
+ already_enabled: MFA is already enabled
+ not_enabled: MFA is not enabled
+ invalid_code: Invalid verification code
+ invalid_backup_code: Invalid backup code
+ invalid_token: Invalid or expired MFA token
+ invalid_credentials: Invalid credentials or verification code
+ feature_unavailable: MFA feature is not available. Please configure encryption keys.
+ profile:
+ mfa:
+ enabled: MFA enabled successfully
+ disabled: MFA disabled successfully
+ account_saml_settings:
+ invalid_certificate: must be a valid X.509 certificate in PEM format
reports:
period: Reporting period %{since} to %{until}
utc_warning: The report generated is in UTC timezone
@@ -209,6 +228,7 @@ he:
reply:
email:
header:
+ notifications: 'התראות'
from_with_name: '%{assignee_name} from %{inbox_name} <%{from_email}>'
reply_with_name: '%{assignee_name} from %{inbox_name} '
friendly_name: '%{sender_name} from %{business_name} <%{from_email}>'
@@ -278,6 +298,26 @@ he:
completed_tool_call: 'Completed %{function_name} tool call'
invalid_tool_call: 'Invalid tool call'
tool_not_available: 'Tool not available'
+ documents:
+ limit_exceeded: 'Document limit exceeded'
+ pdf_format_error: 'must be a PDF file'
+ pdf_size_error: 'must be less than 10MB'
+ pdf_upload_failed: 'Failed to upload PDF to OpenAI'
+ pdf_upload_success: 'PDF uploaded successfully with file_id: %{file_id}'
+ pdf_processing_failed: 'Failed to process PDF document %{document_id}: %{error}'
+ pdf_processing_success: 'Successfully processed PDF document %{document_id}'
+ faq_generation_complete: 'FAQ generation complete. Total FAQs created: %{count}'
+ using_paginated_faq: 'Using paginated FAQ generation for document %{document_id}'
+ using_standard_faq: 'Using standard FAQ generation for document %{document_id}'
+ response_creation_error: 'Error in creating response document: %{error}'
+ missing_openai_file_id: 'Document must have openai_file_id for paginated processing'
+ openai_api_error: 'OpenAI API Error: %{error}'
+ starting_paginated_faq: 'Starting paginated FAQ generation (%{pages_per_chunk} pages per chunk)'
+ stopping_faq_generation: 'Stopping processing. Reason: %{reason}'
+ paginated_faq_complete: 'Paginated generation complete. Total FAQs: %{total_faqs}, Pages processed: %{pages_processed}'
+ processing_pages: 'Processing pages %{start}-%{end} (iteration %{iteration})'
+ chunk_generated: 'Chunk generated %{chunk_faqs} FAQs. Total so far: %{total_faqs}'
+ page_processing_error: 'Error processing pages %{start}-%{end}: %{error}'
public_portal:
search:
search_placeholder: Search for article by title or body...
@@ -366,6 +406,8 @@ he:
Transcript:
%{format_messages}
+ agent_capacity_policy:
+ inbox_already_assigned: 'Inbox has already been assigned to this policy'
portals:
send_instructions:
email_required: 'Email is required'
diff --git a/config/locales/hi.yml b/config/locales/hi.yml
index 0368f1e12..afdd4db20 100644
--- a/config/locales/hi.yml
+++ b/config/locales/hi.yml
@@ -26,6 +26,9 @@ hi:
messages:
reset_password_success: Woot! Request for password reset is successful. Check your mail for instructions.
reset_password_failure: Uh ho! We could not find any user with the specified email.
+ reset_password_saml_user: This account uses SAML authentication. Password reset is not available. Please contact your administrator.
+ login_saml_user: This account uses SAML authentication. Please sign in through your organization's SAML provider.
+ saml_not_available: SAML authentication is not available in this installation.
inbox_deletetion_response: Your inbox deletion request will be processed in some time.
errors:
validations:
@@ -41,6 +44,8 @@ hi:
failed: Signup failed
assignment_policy:
not_found: Assignment policy not found
+ saml:
+ feature_not_enabled: SAML feature not enabled for this account
data_import:
data_type:
invalid: Invalid data type
@@ -84,6 +89,20 @@ hi:
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
+ mfa:
+ already_enabled: MFA is already enabled
+ not_enabled: MFA is not enabled
+ invalid_code: Invalid verification code
+ invalid_backup_code: Invalid backup code
+ invalid_token: Invalid or expired MFA token
+ invalid_credentials: Invalid credentials or verification code
+ feature_unavailable: MFA feature is not available. Please configure encryption keys.
+ profile:
+ mfa:
+ enabled: MFA enabled successfully
+ disabled: MFA disabled successfully
+ account_saml_settings:
+ invalid_certificate: must be a valid X.509 certificate in PEM format
reports:
period: Reporting period %{since} to %{until}
utc_warning: The report generated is in UTC timezone
@@ -209,6 +228,7 @@ hi:
reply:
email:
header:
+ notifications: 'Notifications'
from_with_name: '%{assignee_name} from %{inbox_name} <%{from_email}>'
reply_with_name: '%{assignee_name} from %{inbox_name} '
friendly_name: '%{sender_name} from %{business_name} <%{from_email}>'
@@ -278,6 +298,26 @@ hi:
completed_tool_call: 'Completed %{function_name} tool call'
invalid_tool_call: 'Invalid tool call'
tool_not_available: 'Tool not available'
+ documents:
+ limit_exceeded: 'Document limit exceeded'
+ pdf_format_error: 'must be a PDF file'
+ pdf_size_error: 'must be less than 10MB'
+ pdf_upload_failed: 'Failed to upload PDF to OpenAI'
+ pdf_upload_success: 'PDF uploaded successfully with file_id: %{file_id}'
+ pdf_processing_failed: 'Failed to process PDF document %{document_id}: %{error}'
+ pdf_processing_success: 'Successfully processed PDF document %{document_id}'
+ faq_generation_complete: 'FAQ generation complete. Total FAQs created: %{count}'
+ using_paginated_faq: 'Using paginated FAQ generation for document %{document_id}'
+ using_standard_faq: 'Using standard FAQ generation for document %{document_id}'
+ response_creation_error: 'Error in creating response document: %{error}'
+ missing_openai_file_id: 'Document must have openai_file_id for paginated processing'
+ openai_api_error: 'OpenAI API Error: %{error}'
+ starting_paginated_faq: 'Starting paginated FAQ generation (%{pages_per_chunk} pages per chunk)'
+ stopping_faq_generation: 'Stopping processing. Reason: %{reason}'
+ paginated_faq_complete: 'Paginated generation complete. Total FAQs: %{total_faqs}, Pages processed: %{pages_processed}'
+ processing_pages: 'Processing pages %{start}-%{end} (iteration %{iteration})'
+ chunk_generated: 'Chunk generated %{chunk_faqs} FAQs. Total so far: %{total_faqs}'
+ page_processing_error: 'Error processing pages %{start}-%{end}: %{error}'
public_portal:
search:
search_placeholder: Search for article by title or body...
@@ -358,6 +398,8 @@ hi:
Transcript:
%{format_messages}
+ agent_capacity_policy:
+ inbox_already_assigned: 'Inbox has already been assigned to this policy'
portals:
send_instructions:
email_required: 'Email is required'
diff --git a/config/locales/hr.yml b/config/locales/hr.yml
index dcbb3152d..1b4098e53 100644
--- a/config/locales/hr.yml
+++ b/config/locales/hr.yml
@@ -26,6 +26,9 @@ hr:
messages:
reset_password_success: Woot! Request for password reset is successful. Check your mail for instructions.
reset_password_failure: Uh ho! We could not find any user with the specified email.
+ reset_password_saml_user: This account uses SAML authentication. Password reset is not available. Please contact your administrator.
+ login_saml_user: This account uses SAML authentication. Please sign in through your organization's SAML provider.
+ saml_not_available: SAML authentication is not available in this installation.
inbox_deletetion_response: Your inbox deletion request will be processed in some time.
errors:
validations:
@@ -41,6 +44,8 @@ hr:
failed: Signup failed
assignment_policy:
not_found: Assignment policy not found
+ saml:
+ feature_not_enabled: SAML feature not enabled for this account
data_import:
data_type:
invalid: Invalid data type
@@ -84,6 +89,20 @@ hr:
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
+ mfa:
+ already_enabled: MFA is already enabled
+ not_enabled: MFA is not enabled
+ invalid_code: Invalid verification code
+ invalid_backup_code: Invalid backup code
+ invalid_token: Invalid or expired MFA token
+ invalid_credentials: Invalid credentials or verification code
+ feature_unavailable: MFA feature is not available. Please configure encryption keys.
+ profile:
+ mfa:
+ enabled: MFA enabled successfully
+ disabled: MFA disabled successfully
+ account_saml_settings:
+ invalid_certificate: must be a valid X.509 certificate in PEM format
reports:
period: Reporting period %{since} to %{until}
utc_warning: The report generated is in UTC timezone
@@ -209,6 +228,7 @@ hr:
reply:
email:
header:
+ notifications: 'Notifications'
from_with_name: '%{assignee_name} from %{inbox_name} <%{from_email}>'
reply_with_name: '%{assignee_name} from %{inbox_name} '
friendly_name: '%{sender_name} iz %{business_name} <%{from_email}>'
@@ -278,6 +298,26 @@ hr:
completed_tool_call: 'Completed %{function_name} tool call'
invalid_tool_call: 'Invalid tool call'
tool_not_available: 'Tool not available'
+ documents:
+ limit_exceeded: 'Document limit exceeded'
+ pdf_format_error: 'must be a PDF file'
+ pdf_size_error: 'must be less than 10MB'
+ pdf_upload_failed: 'Failed to upload PDF to OpenAI'
+ pdf_upload_success: 'PDF uploaded successfully with file_id: %{file_id}'
+ pdf_processing_failed: 'Failed to process PDF document %{document_id}: %{error}'
+ pdf_processing_success: 'Successfully processed PDF document %{document_id}'
+ faq_generation_complete: 'FAQ generation complete. Total FAQs created: %{count}'
+ using_paginated_faq: 'Using paginated FAQ generation for document %{document_id}'
+ using_standard_faq: 'Using standard FAQ generation for document %{document_id}'
+ response_creation_error: 'Error in creating response document: %{error}'
+ missing_openai_file_id: 'Document must have openai_file_id for paginated processing'
+ openai_api_error: 'OpenAI API Error: %{error}'
+ starting_paginated_faq: 'Starting paginated FAQ generation (%{pages_per_chunk} pages per chunk)'
+ stopping_faq_generation: 'Stopping processing. Reason: %{reason}'
+ paginated_faq_complete: 'Paginated generation complete. Total FAQs: %{total_faqs}, Pages processed: %{pages_processed}'
+ processing_pages: 'Processing pages %{start}-%{end} (iteration %{iteration})'
+ chunk_generated: 'Chunk generated %{chunk_faqs} FAQs. Total so far: %{total_faqs}'
+ page_processing_error: 'Error processing pages %{start}-%{end}: %{error}'
public_portal:
search:
search_placeholder: Search for article by title or body...
@@ -362,6 +402,8 @@ hr:
Transkript:
%{format_messages}
+ agent_capacity_policy:
+ inbox_already_assigned: 'Inbox has already been assigned to this policy'
portals:
send_instructions:
email_required: 'Email is required'
diff --git a/config/locales/hu.yml b/config/locales/hu.yml
index 8244f32ad..2fbec7417 100644
--- a/config/locales/hu.yml
+++ b/config/locales/hu.yml
@@ -26,6 +26,9 @@ hu:
messages:
reset_password_success: Mi?! A jelszóvisszaállítási kérésed sikeres volt. Nézd meg az e-mailed a részletekért.
reset_password_failure: Jajj ne! Nem találtunk felhasználót ezzel az e-mailcímmel.
+ reset_password_saml_user: This account uses SAML authentication. Password reset is not available. Please contact your administrator.
+ login_saml_user: This account uses SAML authentication. Please sign in through your organization's SAML provider.
+ saml_not_available: SAML authentication is not available in this installation.
inbox_deletetion_response: A beérkező üzeneteid törlésére vonatkozó kérésed nem sokára feldolgozásra kerül.
errors:
validations:
@@ -41,6 +44,8 @@ hu:
failed: Feliratkozás sikertelen
assignment_policy:
not_found: Assignment policy not found
+ saml:
+ feature_not_enabled: SAML feature not enabled for this account
data_import:
data_type:
invalid: Hibás adattípus
@@ -84,6 +89,20 @@ hu:
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
+ mfa:
+ already_enabled: MFA is already enabled
+ not_enabled: MFA is not enabled
+ invalid_code: Invalid verification code
+ invalid_backup_code: Invalid backup code
+ invalid_token: Invalid or expired MFA token
+ invalid_credentials: Invalid credentials or verification code
+ feature_unavailable: MFA feature is not available. Please configure encryption keys.
+ profile:
+ mfa:
+ enabled: MFA enabled successfully
+ disabled: MFA disabled successfully
+ account_saml_settings:
+ invalid_certificate: must be a valid X.509 certificate in PEM format
reports:
period: Jelentési időszak %{since}-tól %{until}-ig
utc_warning: A generált riport UTC időzónát használ
@@ -209,6 +228,7 @@ hu:
reply:
email:
header:
+ notifications: 'Értesítések'
from_with_name: '%{assignee_name} innen %{inbox_name} <%{from_email}>'
reply_with_name: '%{assignee_name} innen %{inbox_name} '
friendly_name: '%{sender_name} innen %{business_name} <%{from_email}>'
@@ -278,6 +298,26 @@ hu:
completed_tool_call: 'Completed %{function_name} tool call'
invalid_tool_call: 'Invalid tool call'
tool_not_available: 'Tool not available'
+ documents:
+ limit_exceeded: 'Document limit exceeded'
+ pdf_format_error: 'must be a PDF file'
+ pdf_size_error: 'must be less than 10MB'
+ pdf_upload_failed: 'Failed to upload PDF to OpenAI'
+ pdf_upload_success: 'PDF uploaded successfully with file_id: %{file_id}'
+ pdf_processing_failed: 'Failed to process PDF document %{document_id}: %{error}'
+ pdf_processing_success: 'Successfully processed PDF document %{document_id}'
+ faq_generation_complete: 'FAQ generation complete. Total FAQs created: %{count}'
+ using_paginated_faq: 'Using paginated FAQ generation for document %{document_id}'
+ using_standard_faq: 'Using standard FAQ generation for document %{document_id}'
+ response_creation_error: 'Error in creating response document: %{error}'
+ missing_openai_file_id: 'Document must have openai_file_id for paginated processing'
+ openai_api_error: 'OpenAI API Error: %{error}'
+ starting_paginated_faq: 'Starting paginated FAQ generation (%{pages_per_chunk} pages per chunk)'
+ stopping_faq_generation: 'Stopping processing. Reason: %{reason}'
+ paginated_faq_complete: 'Paginated generation complete. Total FAQs: %{total_faqs}, Pages processed: %{pages_processed}'
+ processing_pages: 'Processing pages %{start}-%{end} (iteration %{iteration})'
+ chunk_generated: 'Chunk generated %{chunk_faqs} FAQs. Total so far: %{total_faqs}'
+ page_processing_error: 'Error processing pages %{start}-%{end}: %{error}'
public_portal:
search:
search_placeholder: Keress a bejegyzések címében és tartalmában...
@@ -358,6 +398,8 @@ hu:
Transcript:
%{format_messages}
+ agent_capacity_policy:
+ inbox_already_assigned: 'Inbox has already been assigned to this policy'
portals:
send_instructions:
email_required: 'Email is required'
diff --git a/config/locales/hy.yml b/config/locales/hy.yml
index 7e76b6177..29387a457 100644
--- a/config/locales/hy.yml
+++ b/config/locales/hy.yml
@@ -26,6 +26,9 @@ hy:
messages:
reset_password_success: Woot! Request for password reset is successful. Check your mail for instructions.
reset_password_failure: Uh ho! We could not find any user with the specified email.
+ reset_password_saml_user: This account uses SAML authentication. Password reset is not available. Please contact your administrator.
+ login_saml_user: This account uses SAML authentication. Please sign in through your organization's SAML provider.
+ saml_not_available: SAML authentication is not available in this installation.
inbox_deletetion_response: Your inbox deletion request will be processed in some time.
errors:
validations:
@@ -41,6 +44,8 @@ hy:
failed: Signup failed
assignment_policy:
not_found: Assignment policy not found
+ saml:
+ feature_not_enabled: SAML feature not enabled for this account
data_import:
data_type:
invalid: Invalid data type
@@ -84,6 +89,20 @@ hy:
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
+ mfa:
+ already_enabled: MFA is already enabled
+ not_enabled: MFA is not enabled
+ invalid_code: Invalid verification code
+ invalid_backup_code: Invalid backup code
+ invalid_token: Invalid or expired MFA token
+ invalid_credentials: Invalid credentials or verification code
+ feature_unavailable: MFA feature is not available. Please configure encryption keys.
+ profile:
+ mfa:
+ enabled: MFA enabled successfully
+ disabled: MFA disabled successfully
+ account_saml_settings:
+ invalid_certificate: must be a valid X.509 certificate in PEM format
reports:
period: Reporting period %{since} to %{until}
utc_warning: The report generated is in UTC timezone
@@ -209,6 +228,7 @@ hy:
reply:
email:
header:
+ notifications: 'Notifications'
from_with_name: '%{assignee_name} from %{inbox_name} <%{from_email}>'
reply_with_name: '%{assignee_name} from %{inbox_name} '
friendly_name: '%{sender_name} from %{business_name} <%{from_email}>'
@@ -278,6 +298,26 @@ hy:
completed_tool_call: 'Completed %{function_name} tool call'
invalid_tool_call: 'Invalid tool call'
tool_not_available: 'Tool not available'
+ documents:
+ limit_exceeded: 'Document limit exceeded'
+ pdf_format_error: 'must be a PDF file'
+ pdf_size_error: 'must be less than 10MB'
+ pdf_upload_failed: 'Failed to upload PDF to OpenAI'
+ pdf_upload_success: 'PDF uploaded successfully with file_id: %{file_id}'
+ pdf_processing_failed: 'Failed to process PDF document %{document_id}: %{error}'
+ pdf_processing_success: 'Successfully processed PDF document %{document_id}'
+ faq_generation_complete: 'FAQ generation complete. Total FAQs created: %{count}'
+ using_paginated_faq: 'Using paginated FAQ generation for document %{document_id}'
+ using_standard_faq: 'Using standard FAQ generation for document %{document_id}'
+ response_creation_error: 'Error in creating response document: %{error}'
+ missing_openai_file_id: 'Document must have openai_file_id for paginated processing'
+ openai_api_error: 'OpenAI API Error: %{error}'
+ starting_paginated_faq: 'Starting paginated FAQ generation (%{pages_per_chunk} pages per chunk)'
+ stopping_faq_generation: 'Stopping processing. Reason: %{reason}'
+ paginated_faq_complete: 'Paginated generation complete. Total FAQs: %{total_faqs}, Pages processed: %{pages_processed}'
+ processing_pages: 'Processing pages %{start}-%{end} (iteration %{iteration})'
+ chunk_generated: 'Chunk generated %{chunk_faqs} FAQs. Total so far: %{total_faqs}'
+ page_processing_error: 'Error processing pages %{start}-%{end}: %{error}'
public_portal:
search:
search_placeholder: Search for article by title or body...
@@ -358,6 +398,8 @@ hy:
Transcript:
%{format_messages}
+ agent_capacity_policy:
+ inbox_already_assigned: 'Inbox has already been assigned to this policy'
portals:
send_instructions:
email_required: 'Email is required'
diff --git a/config/locales/id.yml b/config/locales/id.yml
index 50109acbb..48d20c784 100644
--- a/config/locales/id.yml
+++ b/config/locales/id.yml
@@ -26,6 +26,9 @@ id:
messages:
reset_password_success: Woot! Permintaan pengaturan ulang kata sandi berhasil. Periksa email Anda untuk mendapatkan petunjuk.
reset_password_failure: Aduh! Kami tidak dapat menemukan pengguna dengan email yang dimasukkan.
+ reset_password_saml_user: This account uses SAML authentication. Password reset is not available. Please contact your administrator.
+ login_saml_user: This account uses SAML authentication. Please sign in through your organization's SAML provider.
+ saml_not_available: SAML authentication is not available in this installation.
inbox_deletetion_response: Permintaan penghapusan kotak masuk Anda akan diproses dalam beberapa waktu.
errors:
validations:
@@ -41,6 +44,8 @@ id:
failed: Pendaftaran gagal
assignment_policy:
not_found: Assignment policy not found
+ saml:
+ feature_not_enabled: SAML feature not enabled for this account
data_import:
data_type:
invalid: Jenis data tidak valid
@@ -84,6 +89,20 @@ id:
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
+ mfa:
+ already_enabled: MFA is already enabled
+ not_enabled: MFA is not enabled
+ invalid_code: Invalid verification code
+ invalid_backup_code: Invalid backup code
+ invalid_token: Invalid or expired MFA token
+ invalid_credentials: Invalid credentials or verification code
+ feature_unavailable: MFA feature is not available. Please configure encryption keys.
+ profile:
+ mfa:
+ enabled: MFA enabled successfully
+ disabled: MFA disabled successfully
+ account_saml_settings:
+ invalid_certificate: must be a valid X.509 certificate in PEM format
reports:
period: Periode pelaporan %{since} hingga %{until}
utc_warning: Laporan yang dihasilkan berada dalam zona waktu UTC
@@ -209,6 +228,7 @@ id:
reply:
email:
header:
+ notifications: 'Notifikasi'
from_with_name: '%{assignee_name} dari %{inbox_name} <%{from_email}>'
reply_with_name: '%{assignee_name} dari %{inbox_name} '
friendly_name: '%{sender_name} dari %{business_name} <%{from_email}>'
@@ -278,6 +298,26 @@ id:
completed_tool_call: 'Completed %{function_name} tool call'
invalid_tool_call: 'Invalid tool call'
tool_not_available: 'Tool not available'
+ documents:
+ limit_exceeded: 'Document limit exceeded'
+ pdf_format_error: 'must be a PDF file'
+ pdf_size_error: 'must be less than 10MB'
+ pdf_upload_failed: 'Failed to upload PDF to OpenAI'
+ pdf_upload_success: 'PDF uploaded successfully with file_id: %{file_id}'
+ pdf_processing_failed: 'Failed to process PDF document %{document_id}: %{error}'
+ pdf_processing_success: 'Successfully processed PDF document %{document_id}'
+ faq_generation_complete: 'FAQ generation complete. Total FAQs created: %{count}'
+ using_paginated_faq: 'Using paginated FAQ generation for document %{document_id}'
+ using_standard_faq: 'Using standard FAQ generation for document %{document_id}'
+ response_creation_error: 'Error in creating response document: %{error}'
+ missing_openai_file_id: 'Document must have openai_file_id for paginated processing'
+ openai_api_error: 'OpenAI API Error: %{error}'
+ starting_paginated_faq: 'Starting paginated FAQ generation (%{pages_per_chunk} pages per chunk)'
+ stopping_faq_generation: 'Stopping processing. Reason: %{reason}'
+ paginated_faq_complete: 'Paginated generation complete. Total FAQs: %{total_faqs}, Pages processed: %{pages_processed}'
+ processing_pages: 'Processing pages %{start}-%{end} (iteration %{iteration})'
+ chunk_generated: 'Chunk generated %{chunk_faqs} FAQs. Total so far: %{total_faqs}'
+ page_processing_error: 'Error processing pages %{start}-%{end}: %{error}'
public_portal:
search:
search_placeholder: Telusuri artikel menurut judul atau isi...
@@ -354,6 +394,8 @@ id:
Transcript:
%{format_messages}
+ agent_capacity_policy:
+ inbox_already_assigned: 'Inbox has already been assigned to this policy'
portals:
send_instructions:
email_required: 'Email is required'
diff --git a/config/locales/is.yml b/config/locales/is.yml
index 465e86ff2..ae06bca66 100644
--- a/config/locales/is.yml
+++ b/config/locales/is.yml
@@ -26,6 +26,9 @@ is:
messages:
reset_password_success: Woot! Beiðni um endurstillingu lykilorðs tókst. Skoðaðu póstinn þinn til að fá leiðbeiningar.
reset_password_failure: Uh ó! Við fundum engan notanda með tilgreint netfang.
+ reset_password_saml_user: This account uses SAML authentication. Password reset is not available. Please contact your administrator.
+ login_saml_user: This account uses SAML authentication. Please sign in through your organization's SAML provider.
+ saml_not_available: SAML authentication is not available in this installation.
inbox_deletetion_response: Your inbox deletion request will be processed in some time.
errors:
validations:
@@ -41,6 +44,8 @@ is:
failed: Signup failed
assignment_policy:
not_found: Assignment policy not found
+ saml:
+ feature_not_enabled: SAML feature not enabled for this account
data_import:
data_type:
invalid: Invalid data type
@@ -84,6 +89,20 @@ is:
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
+ mfa:
+ already_enabled: MFA is already enabled
+ not_enabled: MFA is not enabled
+ invalid_code: Invalid verification code
+ invalid_backup_code: Invalid backup code
+ invalid_token: Invalid or expired MFA token
+ invalid_credentials: Invalid credentials or verification code
+ feature_unavailable: MFA feature is not available. Please configure encryption keys.
+ profile:
+ mfa:
+ enabled: MFA enabled successfully
+ disabled: MFA disabled successfully
+ account_saml_settings:
+ invalid_certificate: must be a valid X.509 certificate in PEM format
reports:
period: Reporting period %{since} to %{until}
utc_warning: The report generated is in UTC timezone
@@ -209,6 +228,7 @@ is:
reply:
email:
header:
+ notifications: 'Skilaboð'
from_with_name: '%{assignee_name} frá %{inbox_name} <%{from_email}>'
reply_with_name: '%{assignee_name} frá %{inbox_name} '
friendly_name: '%{sender_name} frá %{business_name} <%{from_email}>'
@@ -278,6 +298,26 @@ is:
completed_tool_call: 'Completed %{function_name} tool call'
invalid_tool_call: 'Invalid tool call'
tool_not_available: 'Tool not available'
+ documents:
+ limit_exceeded: 'Document limit exceeded'
+ pdf_format_error: 'must be a PDF file'
+ pdf_size_error: 'must be less than 10MB'
+ pdf_upload_failed: 'Failed to upload PDF to OpenAI'
+ pdf_upload_success: 'PDF uploaded successfully with file_id: %{file_id}'
+ pdf_processing_failed: 'Failed to process PDF document %{document_id}: %{error}'
+ pdf_processing_success: 'Successfully processed PDF document %{document_id}'
+ faq_generation_complete: 'FAQ generation complete. Total FAQs created: %{count}'
+ using_paginated_faq: 'Using paginated FAQ generation for document %{document_id}'
+ using_standard_faq: 'Using standard FAQ generation for document %{document_id}'
+ response_creation_error: 'Error in creating response document: %{error}'
+ missing_openai_file_id: 'Document must have openai_file_id for paginated processing'
+ openai_api_error: 'OpenAI API Error: %{error}'
+ starting_paginated_faq: 'Starting paginated FAQ generation (%{pages_per_chunk} pages per chunk)'
+ stopping_faq_generation: 'Stopping processing. Reason: %{reason}'
+ paginated_faq_complete: 'Paginated generation complete. Total FAQs: %{total_faqs}, Pages processed: %{pages_processed}'
+ processing_pages: 'Processing pages %{start}-%{end} (iteration %{iteration})'
+ chunk_generated: 'Chunk generated %{chunk_faqs} FAQs. Total so far: %{total_faqs}'
+ page_processing_error: 'Error processing pages %{start}-%{end}: %{error}'
public_portal:
search:
search_placeholder: Search for article by title or body...
@@ -358,6 +398,8 @@ is:
Transcript:
%{format_messages}
+ agent_capacity_policy:
+ inbox_already_assigned: 'Inbox has already been assigned to this policy'
portals:
send_instructions:
email_required: 'Email is required'
diff --git a/config/locales/it.yml b/config/locales/it.yml
index 5347657c6..aabb5f597 100644
--- a/config/locales/it.yml
+++ b/config/locales/it.yml
@@ -26,6 +26,9 @@ it:
messages:
reset_password_success: Woot! Richiesta di reimpostazione della password riuscita. Controlla la tua mail per le istruzioni.
reset_password_failure: Uh ho! Non siamo riusciti a trovare alcun utente con l'email specificata.
+ reset_password_saml_user: This account uses SAML authentication. Password reset is not available. Please contact your administrator.
+ login_saml_user: This account uses SAML authentication. Please sign in through your organization's SAML provider.
+ saml_not_available: SAML authentication is not available in this installation.
inbox_deletetion_response: Your inbox deletion request will be processed in some time.
errors:
validations:
@@ -41,6 +44,8 @@ it:
failed: Registrazione non riuscita
assignment_policy:
not_found: Assignment policy not found
+ saml:
+ feature_not_enabled: SAML feature not enabled for this account
data_import:
data_type:
invalid: Tipo di dato non valido
@@ -84,6 +89,20 @@ it:
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
+ mfa:
+ already_enabled: MFA is already enabled
+ not_enabled: MFA is not enabled
+ invalid_code: Invalid verification code
+ invalid_backup_code: Invalid backup code
+ invalid_token: Invalid or expired MFA token
+ invalid_credentials: Invalid credentials or verification code
+ feature_unavailable: MFA feature is not available. Please configure encryption keys.
+ profile:
+ mfa:
+ enabled: MFA enabled successfully
+ disabled: MFA disabled successfully
+ account_saml_settings:
+ invalid_certificate: must be a valid X.509 certificate in PEM format
reports:
period: Periodo di segnalazione da %{since} a %{until}
utc_warning: The report generated is in UTC timezone
@@ -209,6 +228,7 @@ it:
reply:
email:
header:
+ notifications: 'Notifiche'
from_with_name: '%{assignee_name} da %{inbox_name} <%{from_email}>'
reply_with_name: '%{assignee_name} da %{inbox_name} '
friendly_name: '%{sender_name} da %{business_name} <%{from_email}>'
@@ -278,6 +298,26 @@ it:
completed_tool_call: 'Completed %{function_name} tool call'
invalid_tool_call: 'Chiamata strumento non valida'
tool_not_available: 'Strumento non disponibile'
+ documents:
+ limit_exceeded: 'Document limit exceeded'
+ pdf_format_error: 'must be a PDF file'
+ pdf_size_error: 'must be less than 10MB'
+ pdf_upload_failed: 'Failed to upload PDF to OpenAI'
+ pdf_upload_success: 'PDF uploaded successfully with file_id: %{file_id}'
+ pdf_processing_failed: 'Failed to process PDF document %{document_id}: %{error}'
+ pdf_processing_success: 'Successfully processed PDF document %{document_id}'
+ faq_generation_complete: 'FAQ generation complete. Total FAQs created: %{count}'
+ using_paginated_faq: 'Using paginated FAQ generation for document %{document_id}'
+ using_standard_faq: 'Using standard FAQ generation for document %{document_id}'
+ response_creation_error: 'Error in creating response document: %{error}'
+ missing_openai_file_id: 'Document must have openai_file_id for paginated processing'
+ openai_api_error: 'OpenAI API Error: %{error}'
+ starting_paginated_faq: 'Starting paginated FAQ generation (%{pages_per_chunk} pages per chunk)'
+ stopping_faq_generation: 'Stopping processing. Reason: %{reason}'
+ paginated_faq_complete: 'Paginated generation complete. Total FAQs: %{total_faqs}, Pages processed: %{pages_processed}'
+ processing_pages: 'Processing pages %{start}-%{end} (iteration %{iteration})'
+ chunk_generated: 'Chunk generated %{chunk_faqs} FAQs. Total so far: %{total_faqs}'
+ page_processing_error: 'Error processing pages %{start}-%{end}: %{error}'
public_portal:
search:
search_placeholder: Search for article by title or body...
@@ -358,6 +398,8 @@ it:
Transcript:
%{format_messages}
+ agent_capacity_policy:
+ inbox_already_assigned: 'Inbox has already been assigned to this policy'
portals:
send_instructions:
email_required: 'L''email è obbligatoria'
diff --git a/config/locales/ja.yml b/config/locales/ja.yml
index d2e7f1c6f..c56fdf32d 100644
--- a/config/locales/ja.yml
+++ b/config/locales/ja.yml
@@ -26,6 +26,9 @@ ja:
messages:
reset_password_success: やりましたね! パスワードのリセットリクエストが成功しました。手順についてはメールを確認してください。
reset_password_failure: メールアドレスが見つかりませんでした。
+ reset_password_saml_user: This account uses SAML authentication. Password reset is not available. Please contact your administrator.
+ login_saml_user: This account uses SAML authentication. Please sign in through your organization's SAML provider.
+ saml_not_available: SAML authentication is not available in this installation.
inbox_deletetion_response: 受信トレイの削除リクエストは、しばらくしてから処理されます。
errors:
validations:
@@ -41,6 +44,8 @@ ja:
failed: サインアップに失敗しました
assignment_policy:
not_found: Assignment policy not found
+ saml:
+ feature_not_enabled: SAML feature not enabled for this account
data_import:
data_type:
invalid: 無効なデータ型。
@@ -84,6 +89,20 @@ ja:
invalid_value: 無効な値です。%{attribute_name} に提供された値は無効です。
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
+ mfa:
+ already_enabled: MFA is already enabled
+ not_enabled: MFA is not enabled
+ invalid_code: Invalid verification code
+ invalid_backup_code: Invalid backup code
+ invalid_token: Invalid or expired MFA token
+ invalid_credentials: Invalid credentials or verification code
+ feature_unavailable: MFA feature is not available. Please configure encryption keys.
+ profile:
+ mfa:
+ enabled: MFA enabled successfully
+ disabled: MFA disabled successfully
+ account_saml_settings:
+ invalid_certificate: must be a valid X.509 certificate in PEM format
reports:
period: レポート期間 %{since} から %{until} まで
utc_warning: 生成されたレポートはUTCタイムゾーンです
@@ -209,6 +228,7 @@ ja:
reply:
email:
header:
+ notifications: '通知'
from_with_name: '%{assignee_name} from %{inbox_name} <%{from_email}>'
reply_with_name: '%{assignee_name} from %{inbox_name} '
friendly_name: '%{sender_name} from %{business_name} <%{from_email}>'
@@ -278,6 +298,26 @@ ja:
completed_tool_call: 'Completed %{function_name} tool call'
invalid_tool_call: 'Invalid tool call'
tool_not_available: 'Tool not available'
+ documents:
+ limit_exceeded: 'Document limit exceeded'
+ pdf_format_error: 'must be a PDF file'
+ pdf_size_error: 'must be less than 10MB'
+ pdf_upload_failed: 'Failed to upload PDF to OpenAI'
+ pdf_upload_success: 'PDF uploaded successfully with file_id: %{file_id}'
+ pdf_processing_failed: 'Failed to process PDF document %{document_id}: %{error}'
+ pdf_processing_success: 'Successfully processed PDF document %{document_id}'
+ faq_generation_complete: 'FAQ generation complete. Total FAQs created: %{count}'
+ using_paginated_faq: 'Using paginated FAQ generation for document %{document_id}'
+ using_standard_faq: 'Using standard FAQ generation for document %{document_id}'
+ response_creation_error: 'Error in creating response document: %{error}'
+ missing_openai_file_id: 'Document must have openai_file_id for paginated processing'
+ openai_api_error: 'OpenAI API Error: %{error}'
+ starting_paginated_faq: 'Starting paginated FAQ generation (%{pages_per_chunk} pages per chunk)'
+ stopping_faq_generation: 'Stopping processing. Reason: %{reason}'
+ paginated_faq_complete: 'Paginated generation complete. Total FAQs: %{total_faqs}, Pages processed: %{pages_processed}'
+ processing_pages: 'Processing pages %{start}-%{end} (iteration %{iteration})'
+ chunk_generated: 'Chunk generated %{chunk_faqs} FAQs. Total so far: %{total_faqs}'
+ page_processing_error: 'Error processing pages %{start}-%{end}: %{error}'
public_portal:
search:
search_placeholder: タイトルまたは本文で記事を検索...
@@ -354,6 +394,8 @@ ja:
Transcript:
%{format_messages}
+ agent_capacity_policy:
+ inbox_already_assigned: 'Inbox has already been assigned to this policy'
portals:
send_instructions:
email_required: 'Email is required'
diff --git a/config/locales/ka.yml b/config/locales/ka.yml
index d8d49a9a9..e4d6987b9 100644
--- a/config/locales/ka.yml
+++ b/config/locales/ka.yml
@@ -26,6 +26,9 @@ ka:
messages:
reset_password_success: Woot! Request for password reset is successful. Check your mail for instructions.
reset_password_failure: Uh ho! We could not find any user with the specified email.
+ reset_password_saml_user: This account uses SAML authentication. Password reset is not available. Please contact your administrator.
+ login_saml_user: This account uses SAML authentication. Please sign in through your organization's SAML provider.
+ saml_not_available: SAML authentication is not available in this installation.
inbox_deletetion_response: Your inbox deletion request will be processed in some time.
errors:
validations:
@@ -41,6 +44,8 @@ ka:
failed: Signup failed
assignment_policy:
not_found: Assignment policy not found
+ saml:
+ feature_not_enabled: SAML feature not enabled for this account
data_import:
data_type:
invalid: Invalid data type
@@ -84,6 +89,20 @@ ka:
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
+ mfa:
+ already_enabled: MFA is already enabled
+ not_enabled: MFA is not enabled
+ invalid_code: Invalid verification code
+ invalid_backup_code: Invalid backup code
+ invalid_token: Invalid or expired MFA token
+ invalid_credentials: Invalid credentials or verification code
+ feature_unavailable: MFA feature is not available. Please configure encryption keys.
+ profile:
+ mfa:
+ enabled: MFA enabled successfully
+ disabled: MFA disabled successfully
+ account_saml_settings:
+ invalid_certificate: must be a valid X.509 certificate in PEM format
reports:
period: Reporting period %{since} to %{until}
utc_warning: The report generated is in UTC timezone
@@ -209,6 +228,7 @@ ka:
reply:
email:
header:
+ notifications: 'Notifications'
from_with_name: '%{assignee_name} from %{inbox_name} <%{from_email}>'
reply_with_name: '%{assignee_name} from %{inbox_name} '
friendly_name: '%{sender_name} from %{business_name} <%{from_email}>'
@@ -278,6 +298,26 @@ ka:
completed_tool_call: 'Completed %{function_name} tool call'
invalid_tool_call: 'Invalid tool call'
tool_not_available: 'Tool not available'
+ documents:
+ limit_exceeded: 'Document limit exceeded'
+ pdf_format_error: 'must be a PDF file'
+ pdf_size_error: 'must be less than 10MB'
+ pdf_upload_failed: 'Failed to upload PDF to OpenAI'
+ pdf_upload_success: 'PDF uploaded successfully with file_id: %{file_id}'
+ pdf_processing_failed: 'Failed to process PDF document %{document_id}: %{error}'
+ pdf_processing_success: 'Successfully processed PDF document %{document_id}'
+ faq_generation_complete: 'FAQ generation complete. Total FAQs created: %{count}'
+ using_paginated_faq: 'Using paginated FAQ generation for document %{document_id}'
+ using_standard_faq: 'Using standard FAQ generation for document %{document_id}'
+ response_creation_error: 'Error in creating response document: %{error}'
+ missing_openai_file_id: 'Document must have openai_file_id for paginated processing'
+ openai_api_error: 'OpenAI API Error: %{error}'
+ starting_paginated_faq: 'Starting paginated FAQ generation (%{pages_per_chunk} pages per chunk)'
+ stopping_faq_generation: 'Stopping processing. Reason: %{reason}'
+ paginated_faq_complete: 'Paginated generation complete. Total FAQs: %{total_faqs}, Pages processed: %{pages_processed}'
+ processing_pages: 'Processing pages %{start}-%{end} (iteration %{iteration})'
+ chunk_generated: 'Chunk generated %{chunk_faqs} FAQs. Total so far: %{total_faqs}'
+ page_processing_error: 'Error processing pages %{start}-%{end}: %{error}'
public_portal:
search:
search_placeholder: Search for article by title or body...
@@ -358,6 +398,8 @@ ka:
Transcript:
%{format_messages}
+ agent_capacity_policy:
+ inbox_already_assigned: 'Inbox has already been assigned to this policy'
portals:
send_instructions:
email_required: 'Email is required'
diff --git a/config/locales/ko.yml b/config/locales/ko.yml
index a6a58d94f..e80b1ce62 100644
--- a/config/locales/ko.yml
+++ b/config/locales/ko.yml
@@ -26,6 +26,9 @@ ko:
messages:
reset_password_success: Woot! Request for password reset is successful. Check your mail for instructions.
reset_password_failure: Uh ho! We could not find any user with the specified email.
+ reset_password_saml_user: This account uses SAML authentication. Password reset is not available. Please contact your administrator.
+ login_saml_user: This account uses SAML authentication. Please sign in through your organization's SAML provider.
+ saml_not_available: SAML authentication is not available in this installation.
inbox_deletetion_response: Your inbox deletion request will be processed in some time.
errors:
validations:
@@ -41,6 +44,8 @@ ko:
failed: Signup failed
assignment_policy:
not_found: Assignment policy not found
+ saml:
+ feature_not_enabled: SAML feature not enabled for this account
data_import:
data_type:
invalid: Invalid data type
@@ -84,6 +89,20 @@ ko:
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
+ mfa:
+ already_enabled: MFA is already enabled
+ not_enabled: MFA is not enabled
+ invalid_code: Invalid verification code
+ invalid_backup_code: Invalid backup code
+ invalid_token: Invalid or expired MFA token
+ invalid_credentials: Invalid credentials or verification code
+ feature_unavailable: MFA feature is not available. Please configure encryption keys.
+ profile:
+ mfa:
+ enabled: MFA enabled successfully
+ disabled: MFA disabled successfully
+ account_saml_settings:
+ invalid_certificate: must be a valid X.509 certificate in PEM format
reports:
period: 보고 기간 %{since} - %{until}
utc_warning: The report generated is in UTC timezone
@@ -209,6 +228,7 @@ ko:
reply:
email:
header:
+ notifications: '알림'
from_with_name: '%{assignee_name} from %{inbox_name} <%{from_email}>'
reply_with_name: '%{assignee_name} from %{inbox_name} '
friendly_name: '%{sender_name} from %{business_name} <%{from_email}>'
@@ -278,6 +298,26 @@ ko:
completed_tool_call: 'Completed %{function_name} tool call'
invalid_tool_call: 'Invalid tool call'
tool_not_available: 'Tool not available'
+ documents:
+ limit_exceeded: 'Document limit exceeded'
+ pdf_format_error: 'must be a PDF file'
+ pdf_size_error: 'must be less than 10MB'
+ pdf_upload_failed: 'Failed to upload PDF to OpenAI'
+ pdf_upload_success: 'PDF uploaded successfully with file_id: %{file_id}'
+ pdf_processing_failed: 'Failed to process PDF document %{document_id}: %{error}'
+ pdf_processing_success: 'Successfully processed PDF document %{document_id}'
+ faq_generation_complete: 'FAQ generation complete. Total FAQs created: %{count}'
+ using_paginated_faq: 'Using paginated FAQ generation for document %{document_id}'
+ using_standard_faq: 'Using standard FAQ generation for document %{document_id}'
+ response_creation_error: 'Error in creating response document: %{error}'
+ missing_openai_file_id: 'Document must have openai_file_id for paginated processing'
+ openai_api_error: 'OpenAI API Error: %{error}'
+ starting_paginated_faq: 'Starting paginated FAQ generation (%{pages_per_chunk} pages per chunk)'
+ stopping_faq_generation: 'Stopping processing. Reason: %{reason}'
+ paginated_faq_complete: 'Paginated generation complete. Total FAQs: %{total_faqs}, Pages processed: %{pages_processed}'
+ processing_pages: 'Processing pages %{start}-%{end} (iteration %{iteration})'
+ chunk_generated: 'Chunk generated %{chunk_faqs} FAQs. Total so far: %{total_faqs}'
+ page_processing_error: 'Error processing pages %{start}-%{end}: %{error}'
public_portal:
search:
search_placeholder: 게시물을 제목이나 내용으로 검색하세요...
@@ -354,6 +394,8 @@ ko:
Transcript:
%{format_messages}
+ agent_capacity_policy:
+ inbox_already_assigned: 'Inbox has already been assigned to this policy'
portals:
send_instructions:
email_required: '이메일이 필요합니다'
diff --git a/config/locales/lt.yml b/config/locales/lt.yml
index 46baa4120..da5b1247b 100644
--- a/config/locales/lt.yml
+++ b/config/locales/lt.yml
@@ -26,6 +26,9 @@ lt:
messages:
reset_password_success: Kietai! Slaptažodžio nustatymo iš naujo užklausa įvykdyta. Instrukcijų ieškokite savo pašte.
reset_password_failure: Oho! Nepavyko rasti vartotojo su nurodytu el. pašto adresu.
+ reset_password_saml_user: This account uses SAML authentication. Password reset is not available. Please contact your administrator.
+ login_saml_user: This account uses SAML authentication. Please sign in through your organization's SAML provider.
+ saml_not_available: SAML authentication is not available in this installation.
inbox_deletetion_response: Jūsų gautųj laiškų aplanko ištrynimo užklausa bus apdorota po kurio laiko.
errors:
validations:
@@ -41,6 +44,8 @@ lt:
failed: Prisijungimas nesėkmingas
assignment_policy:
not_found: Assignment policy not found
+ saml:
+ feature_not_enabled: SAML feature not enabled for this account
data_import:
data_type:
invalid: Neteisingas duomenų tipas
@@ -84,6 +89,20 @@ lt:
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
+ mfa:
+ already_enabled: MFA is already enabled
+ not_enabled: MFA is not enabled
+ invalid_code: Invalid verification code
+ invalid_backup_code: Invalid backup code
+ invalid_token: Invalid or expired MFA token
+ invalid_credentials: Invalid credentials or verification code
+ feature_unavailable: MFA feature is not available. Please configure encryption keys.
+ profile:
+ mfa:
+ enabled: MFA enabled successfully
+ disabled: MFA disabled successfully
+ account_saml_settings:
+ invalid_certificate: must be a valid X.509 certificate in PEM format
reports:
period: Ataskaitinis laikotarpis nuo %{since} iki %{until}
utc_warning: Sugeneruota ataskaita yra UTC laiko juostoje
@@ -209,6 +228,7 @@ lt:
reply:
email:
header:
+ notifications: 'Perspėjimai'
from_with_name: '%{assignee_name} nuo %{inbox_name} <%{from_email}>'
reply_with_name: '%{assignee_name} nuo %{inbox_name} '
friendly_name: '%{sender_name} nuo %{business_name} <%{from_email}>'
@@ -278,6 +298,26 @@ lt:
completed_tool_call: 'Completed %{function_name} tool call'
invalid_tool_call: 'Invalid tool call'
tool_not_available: 'Tool not available'
+ documents:
+ limit_exceeded: 'Document limit exceeded'
+ pdf_format_error: 'must be a PDF file'
+ pdf_size_error: 'must be less than 10MB'
+ pdf_upload_failed: 'Failed to upload PDF to OpenAI'
+ pdf_upload_success: 'PDF uploaded successfully with file_id: %{file_id}'
+ pdf_processing_failed: 'Failed to process PDF document %{document_id}: %{error}'
+ pdf_processing_success: 'Successfully processed PDF document %{document_id}'
+ faq_generation_complete: 'FAQ generation complete. Total FAQs created: %{count}'
+ using_paginated_faq: 'Using paginated FAQ generation for document %{document_id}'
+ using_standard_faq: 'Using standard FAQ generation for document %{document_id}'
+ response_creation_error: 'Error in creating response document: %{error}'
+ missing_openai_file_id: 'Document must have openai_file_id for paginated processing'
+ openai_api_error: 'OpenAI API Error: %{error}'
+ starting_paginated_faq: 'Starting paginated FAQ generation (%{pages_per_chunk} pages per chunk)'
+ stopping_faq_generation: 'Stopping processing. Reason: %{reason}'
+ paginated_faq_complete: 'Paginated generation complete. Total FAQs: %{total_faqs}, Pages processed: %{pages_processed}'
+ processing_pages: 'Processing pages %{start}-%{end} (iteration %{iteration})'
+ chunk_generated: 'Chunk generated %{chunk_faqs} FAQs. Total so far: %{total_faqs}'
+ page_processing_error: 'Error processing pages %{start}-%{end}: %{error}'
public_portal:
search:
search_placeholder: Ieškokite straipsnio pagal pavadinimą arba turinį...
@@ -366,6 +406,8 @@ lt:
Transcript:
%{format_messages}
+ agent_capacity_policy:
+ inbox_already_assigned: 'Inbox has already been assigned to this policy'
portals:
send_instructions:
email_required: 'Email is required'
diff --git a/config/locales/lv.yml b/config/locales/lv.yml
index cf7d5f42d..6c66ccbd5 100644
--- a/config/locales/lv.yml
+++ b/config/locales/lv.yml
@@ -26,6 +26,9 @@ lv:
messages:
reset_password_success: Urā! Paroles atiestatīšanas pieprasījums ir veiksmīgs. Pārbaudiet savu e-pastu, lai iegūtu norādījumus.
reset_password_failure: Ak, vai! Mēs nevarējām atrast nevienu lietotāju ar norādīto e -pastu.
+ reset_password_saml_user: This account uses SAML authentication. Password reset is not available. Please contact your administrator.
+ login_saml_user: This account uses SAML authentication. Please sign in through your organization's SAML provider.
+ saml_not_available: SAML authentication is not available in this installation.
inbox_deletetion_response: Jūsu iesūtnes dzēšanas pieprasījums pēc kāda laika tiks apstrādāts.
errors:
validations:
@@ -41,6 +44,8 @@ lv:
failed: Reģistrēšanās neizdevās
assignment_policy:
not_found: Assignment policy not found
+ saml:
+ feature_not_enabled: SAML feature not enabled for this account
data_import:
data_type:
invalid: Nederīgs datu tips
@@ -84,6 +89,20 @@ lv:
invalid_value: Nederīga vērtība. Norādītās vērtības priekš %{attribute_name} nav derīgas
custom_attribute_definition:
key_conflict: Norādītā atslēga nav atļauta, jo tā var būt pretrunā ar noklusējuma atribūtiem.
+ mfa:
+ already_enabled: MFA is already enabled
+ not_enabled: MFA is not enabled
+ invalid_code: Invalid verification code
+ invalid_backup_code: Invalid backup code
+ invalid_token: Invalid or expired MFA token
+ invalid_credentials: Invalid credentials or verification code
+ feature_unavailable: MFA feature is not available. Please configure encryption keys.
+ profile:
+ mfa:
+ enabled: MFA enabled successfully
+ disabled: MFA disabled successfully
+ account_saml_settings:
+ invalid_certificate: must be a valid X.509 certificate in PEM format
reports:
period: Ziņošanas periods %{since} līdz %{until}
utc_warning: Izveidotais pārskats atbilst UTC laika joslai
@@ -209,6 +228,7 @@ lv:
reply:
email:
header:
+ notifications: 'Paziņojumi'
from_with_name: '%{assignee_name} no %{inbox_name} <%{from_email}>'
reply_with_name: '%{assignee_name} no %{inbox_name} '
friendly_name: '%{sender_name} no %{business_name} <%{from_email}>'
@@ -278,6 +298,26 @@ lv:
completed_tool_call: 'Completed %{function_name} tool call'
invalid_tool_call: 'Invalid tool call'
tool_not_available: 'Tool not available'
+ documents:
+ limit_exceeded: 'Document limit exceeded'
+ pdf_format_error: 'must be a PDF file'
+ pdf_size_error: 'must be less than 10MB'
+ pdf_upload_failed: 'Failed to upload PDF to OpenAI'
+ pdf_upload_success: 'PDF uploaded successfully with file_id: %{file_id}'
+ pdf_processing_failed: 'Failed to process PDF document %{document_id}: %{error}'
+ pdf_processing_success: 'Successfully processed PDF document %{document_id}'
+ faq_generation_complete: 'FAQ generation complete. Total FAQs created: %{count}'
+ using_paginated_faq: 'Using paginated FAQ generation for document %{document_id}'
+ using_standard_faq: 'Using standard FAQ generation for document %{document_id}'
+ response_creation_error: 'Error in creating response document: %{error}'
+ missing_openai_file_id: 'Document must have openai_file_id for paginated processing'
+ openai_api_error: 'OpenAI API Error: %{error}'
+ starting_paginated_faq: 'Starting paginated FAQ generation (%{pages_per_chunk} pages per chunk)'
+ stopping_faq_generation: 'Stopping processing. Reason: %{reason}'
+ paginated_faq_complete: 'Paginated generation complete. Total FAQs: %{total_faqs}, Pages processed: %{pages_processed}'
+ processing_pages: 'Processing pages %{start}-%{end} (iteration %{iteration})'
+ chunk_generated: 'Chunk generated %{chunk_faqs} FAQs. Total so far: %{total_faqs}'
+ page_processing_error: 'Error processing pages %{start}-%{end}: %{error}'
public_portal:
search:
search_placeholder: Meklēt rakstu pēc nosaukuma vai pamatteksta...
@@ -362,6 +402,8 @@ lv:
Transcript:
%{format_messages}
+ agent_capacity_policy:
+ inbox_already_assigned: 'Inbox has already been assigned to this policy'
portals:
send_instructions:
email_required: 'Email is required'
diff --git a/config/locales/ml.yml b/config/locales/ml.yml
index c82ac2ae8..ab3837cd6 100644
--- a/config/locales/ml.yml
+++ b/config/locales/ml.yml
@@ -26,6 +26,9 @@ ml:
messages:
reset_password_success: Woot! പാസ്വേഡ് പുനസജ്ജീകരണത്തിനുള്ള അഭ്യർത്ഥന വിജയകരമാണ്. നിർദ്ദേശങ്ങൾക്കായി നിങ്ങളുടെ മെയിൽ പരിശോധിക്കുക.
reset_password_failure: ക്ഷമിക്കണം! നിർദ്ദിഷ്ട ഇമെയിൽ ഉള്ള ഒരു ഉപയോക്താവിനെയും ഞങ്ങൾക്ക് കണ്ടെത്താൻ കഴിഞ്ഞില്ല.
+ reset_password_saml_user: This account uses SAML authentication. Password reset is not available. Please contact your administrator.
+ login_saml_user: This account uses SAML authentication. Please sign in through your organization's SAML provider.
+ saml_not_available: SAML authentication is not available in this installation.
inbox_deletetion_response: Your inbox deletion request will be processed in some time.
errors:
validations:
@@ -41,6 +44,8 @@ ml:
failed: സൈനപ്പ് പരാജയപ്പെട്ടു
assignment_policy:
not_found: Assignment policy not found
+ saml:
+ feature_not_enabled: SAML feature not enabled for this account
data_import:
data_type:
invalid: Invalid data type
@@ -84,6 +89,20 @@ ml:
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
+ mfa:
+ already_enabled: MFA is already enabled
+ not_enabled: MFA is not enabled
+ invalid_code: Invalid verification code
+ invalid_backup_code: Invalid backup code
+ invalid_token: Invalid or expired MFA token
+ invalid_credentials: Invalid credentials or verification code
+ feature_unavailable: MFA feature is not available. Please configure encryption keys.
+ profile:
+ mfa:
+ enabled: MFA enabled successfully
+ disabled: MFA disabled successfully
+ account_saml_settings:
+ invalid_certificate: must be a valid X.509 certificate in PEM format
reports:
period: Reporting period %{since} to %{until}
utc_warning: The report generated is in UTC timezone
@@ -209,6 +228,7 @@ ml:
reply:
email:
header:
+ notifications: 'Notifications'
from_with_name: '%{assignee_name} from %{inbox_name} <%{from_email}>'
reply_with_name: '%{assignee_name} from %{inbox_name} '
friendly_name: '%{sender_name} from %{business_name} <%{from_email}>'
@@ -278,6 +298,26 @@ ml:
completed_tool_call: 'Completed %{function_name} tool call'
invalid_tool_call: 'Invalid tool call'
tool_not_available: 'Tool not available'
+ documents:
+ limit_exceeded: 'Document limit exceeded'
+ pdf_format_error: 'must be a PDF file'
+ pdf_size_error: 'must be less than 10MB'
+ pdf_upload_failed: 'Failed to upload PDF to OpenAI'
+ pdf_upload_success: 'PDF uploaded successfully with file_id: %{file_id}'
+ pdf_processing_failed: 'Failed to process PDF document %{document_id}: %{error}'
+ pdf_processing_success: 'Successfully processed PDF document %{document_id}'
+ faq_generation_complete: 'FAQ generation complete. Total FAQs created: %{count}'
+ using_paginated_faq: 'Using paginated FAQ generation for document %{document_id}'
+ using_standard_faq: 'Using standard FAQ generation for document %{document_id}'
+ response_creation_error: 'Error in creating response document: %{error}'
+ missing_openai_file_id: 'Document must have openai_file_id for paginated processing'
+ openai_api_error: 'OpenAI API Error: %{error}'
+ starting_paginated_faq: 'Starting paginated FAQ generation (%{pages_per_chunk} pages per chunk)'
+ stopping_faq_generation: 'Stopping processing. Reason: %{reason}'
+ paginated_faq_complete: 'Paginated generation complete. Total FAQs: %{total_faqs}, Pages processed: %{pages_processed}'
+ processing_pages: 'Processing pages %{start}-%{end} (iteration %{iteration})'
+ chunk_generated: 'Chunk generated %{chunk_faqs} FAQs. Total so far: %{total_faqs}'
+ page_processing_error: 'Error processing pages %{start}-%{end}: %{error}'
public_portal:
search:
search_placeholder: Search for article by title or body...
@@ -358,6 +398,8 @@ ml:
Transcript:
%{format_messages}
+ agent_capacity_policy:
+ inbox_already_assigned: 'Inbox has already been assigned to this policy'
portals:
send_instructions:
email_required: 'ഇമെയിൽ ആവശ്യമാണ്'
diff --git a/config/locales/ms.yml b/config/locales/ms.yml
index 3460b45ca..e623fe083 100644
--- a/config/locales/ms.yml
+++ b/config/locales/ms.yml
@@ -26,6 +26,9 @@ ms:
messages:
reset_password_success: Woot! Request for password reset is successful. Check your mail for instructions.
reset_password_failure: Uh ho! We could not find any user with the specified email.
+ reset_password_saml_user: This account uses SAML authentication. Password reset is not available. Please contact your administrator.
+ login_saml_user: This account uses SAML authentication. Please sign in through your organization's SAML provider.
+ saml_not_available: SAML authentication is not available in this installation.
inbox_deletetion_response: Your inbox deletion request will be processed in some time.
errors:
validations:
@@ -41,6 +44,8 @@ ms:
failed: Signup failed
assignment_policy:
not_found: Assignment policy not found
+ saml:
+ feature_not_enabled: SAML feature not enabled for this account
data_import:
data_type:
invalid: Invalid data type
@@ -84,6 +89,20 @@ ms:
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
+ mfa:
+ already_enabled: MFA is already enabled
+ not_enabled: MFA is not enabled
+ invalid_code: Invalid verification code
+ invalid_backup_code: Invalid backup code
+ invalid_token: Invalid or expired MFA token
+ invalid_credentials: Invalid credentials or verification code
+ feature_unavailable: MFA feature is not available. Please configure encryption keys.
+ profile:
+ mfa:
+ enabled: MFA enabled successfully
+ disabled: MFA disabled successfully
+ account_saml_settings:
+ invalid_certificate: must be a valid X.509 certificate in PEM format
reports:
period: Reporting period %{since} to %{until}
utc_warning: The report generated is in UTC timezone
@@ -209,6 +228,7 @@ ms:
reply:
email:
header:
+ notifications: 'Notifications'
from_with_name: '%{assignee_name} from %{inbox_name} <%{from_email}>'
reply_with_name: '%{assignee_name} from %{inbox_name} '
friendly_name: '%{sender_name} from %{business_name} <%{from_email}>'
@@ -278,6 +298,26 @@ ms:
completed_tool_call: 'Completed %{function_name} tool call'
invalid_tool_call: 'Invalid tool call'
tool_not_available: 'Tool not available'
+ documents:
+ limit_exceeded: 'Document limit exceeded'
+ pdf_format_error: 'must be a PDF file'
+ pdf_size_error: 'must be less than 10MB'
+ pdf_upload_failed: 'Failed to upload PDF to OpenAI'
+ pdf_upload_success: 'PDF uploaded successfully with file_id: %{file_id}'
+ pdf_processing_failed: 'Failed to process PDF document %{document_id}: %{error}'
+ pdf_processing_success: 'Successfully processed PDF document %{document_id}'
+ faq_generation_complete: 'FAQ generation complete. Total FAQs created: %{count}'
+ using_paginated_faq: 'Using paginated FAQ generation for document %{document_id}'
+ using_standard_faq: 'Using standard FAQ generation for document %{document_id}'
+ response_creation_error: 'Error in creating response document: %{error}'
+ missing_openai_file_id: 'Document must have openai_file_id for paginated processing'
+ openai_api_error: 'OpenAI API Error: %{error}'
+ starting_paginated_faq: 'Starting paginated FAQ generation (%{pages_per_chunk} pages per chunk)'
+ stopping_faq_generation: 'Stopping processing. Reason: %{reason}'
+ paginated_faq_complete: 'Paginated generation complete. Total FAQs: %{total_faqs}, Pages processed: %{pages_processed}'
+ processing_pages: 'Processing pages %{start}-%{end} (iteration %{iteration})'
+ chunk_generated: 'Chunk generated %{chunk_faqs} FAQs. Total so far: %{total_faqs}'
+ page_processing_error: 'Error processing pages %{start}-%{end}: %{error}'
public_portal:
search:
search_placeholder: Search for article by title or body...
@@ -354,6 +394,8 @@ ms:
Transcript:
%{format_messages}
+ agent_capacity_policy:
+ inbox_already_assigned: 'Inbox has already been assigned to this policy'
portals:
send_instructions:
email_required: 'Email is required'
diff --git a/config/locales/ne.yml b/config/locales/ne.yml
index 920cb9531..57dfae6b1 100644
--- a/config/locales/ne.yml
+++ b/config/locales/ne.yml
@@ -26,6 +26,9 @@ ne:
messages:
reset_password_success: Woot! Request for password reset is successful. Check your mail for instructions.
reset_password_failure: Uh ho! We could not find any user with the specified email.
+ reset_password_saml_user: This account uses SAML authentication. Password reset is not available. Please contact your administrator.
+ login_saml_user: This account uses SAML authentication. Please sign in through your organization's SAML provider.
+ saml_not_available: SAML authentication is not available in this installation.
inbox_deletetion_response: Your inbox deletion request will be processed in some time.
errors:
validations:
@@ -41,6 +44,8 @@ ne:
failed: Signup failed
assignment_policy:
not_found: Assignment policy not found
+ saml:
+ feature_not_enabled: SAML feature not enabled for this account
data_import:
data_type:
invalid: Invalid data type
@@ -84,6 +89,20 @@ ne:
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
+ mfa:
+ already_enabled: MFA is already enabled
+ not_enabled: MFA is not enabled
+ invalid_code: Invalid verification code
+ invalid_backup_code: Invalid backup code
+ invalid_token: Invalid or expired MFA token
+ invalid_credentials: Invalid credentials or verification code
+ feature_unavailable: MFA feature is not available. Please configure encryption keys.
+ profile:
+ mfa:
+ enabled: MFA enabled successfully
+ disabled: MFA disabled successfully
+ account_saml_settings:
+ invalid_certificate: must be a valid X.509 certificate in PEM format
reports:
period: Reporting period %{since} to %{until}
utc_warning: The report generated is in UTC timezone
@@ -209,6 +228,7 @@ ne:
reply:
email:
header:
+ notifications: 'Notifications'
from_with_name: '%{assignee_name} from %{inbox_name} <%{from_email}>'
reply_with_name: '%{assignee_name} from %{inbox_name} '
friendly_name: '%{sender_name} from %{business_name} <%{from_email}>'
@@ -278,6 +298,26 @@ ne:
completed_tool_call: 'Completed %{function_name} tool call'
invalid_tool_call: 'Invalid tool call'
tool_not_available: 'Tool not available'
+ documents:
+ limit_exceeded: 'Document limit exceeded'
+ pdf_format_error: 'must be a PDF file'
+ pdf_size_error: 'must be less than 10MB'
+ pdf_upload_failed: 'Failed to upload PDF to OpenAI'
+ pdf_upload_success: 'PDF uploaded successfully with file_id: %{file_id}'
+ pdf_processing_failed: 'Failed to process PDF document %{document_id}: %{error}'
+ pdf_processing_success: 'Successfully processed PDF document %{document_id}'
+ faq_generation_complete: 'FAQ generation complete. Total FAQs created: %{count}'
+ using_paginated_faq: 'Using paginated FAQ generation for document %{document_id}'
+ using_standard_faq: 'Using standard FAQ generation for document %{document_id}'
+ response_creation_error: 'Error in creating response document: %{error}'
+ missing_openai_file_id: 'Document must have openai_file_id for paginated processing'
+ openai_api_error: 'OpenAI API Error: %{error}'
+ starting_paginated_faq: 'Starting paginated FAQ generation (%{pages_per_chunk} pages per chunk)'
+ stopping_faq_generation: 'Stopping processing. Reason: %{reason}'
+ paginated_faq_complete: 'Paginated generation complete. Total FAQs: %{total_faqs}, Pages processed: %{pages_processed}'
+ processing_pages: 'Processing pages %{start}-%{end} (iteration %{iteration})'
+ chunk_generated: 'Chunk generated %{chunk_faqs} FAQs. Total so far: %{total_faqs}'
+ page_processing_error: 'Error processing pages %{start}-%{end}: %{error}'
public_portal:
search:
search_placeholder: Search for article by title or body...
@@ -358,6 +398,8 @@ ne:
Transcript:
%{format_messages}
+ agent_capacity_policy:
+ inbox_already_assigned: 'Inbox has already been assigned to this policy'
portals:
send_instructions:
email_required: 'Email is required'
diff --git a/config/locales/nl.yml b/config/locales/nl.yml
index 8e4c95604..0fd23d830 100644
--- a/config/locales/nl.yml
+++ b/config/locales/nl.yml
@@ -26,6 +26,9 @@ nl:
messages:
reset_password_success: Woot! Verzoek om wachtwoord te resetten is gelukt. Controleer je e-mail voor instructies.
reset_password_failure: Oh ho! We konden geen gebruiker vinden met het opgegeven e-mailadres.
+ reset_password_saml_user: This account uses SAML authentication. Password reset is not available. Please contact your administrator.
+ login_saml_user: This account uses SAML authentication. Please sign in through your organization's SAML provider.
+ saml_not_available: SAML authentication is not available in this installation.
inbox_deletetion_response: Uw verzoek tot verwijdering binnen de inbox zal binnen enige tijd worden verwerkt.
errors:
validations:
@@ -41,6 +44,8 @@ nl:
failed: Aanmelden mislukt
assignment_policy:
not_found: Assignment policy not found
+ saml:
+ feature_not_enabled: SAML feature not enabled for this account
data_import:
data_type:
invalid: Ongeldig datatype
@@ -84,6 +89,20 @@ nl:
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
+ mfa:
+ already_enabled: MFA is already enabled
+ not_enabled: MFA is not enabled
+ invalid_code: Invalid verification code
+ invalid_backup_code: Invalid backup code
+ invalid_token: Invalid or expired MFA token
+ invalid_credentials: Invalid credentials or verification code
+ feature_unavailable: MFA feature is not available. Please configure encryption keys.
+ profile:
+ mfa:
+ enabled: MFA enabled successfully
+ disabled: MFA disabled successfully
+ account_saml_settings:
+ invalid_certificate: must be a valid X.509 certificate in PEM format
reports:
period: Rapportering van %{since} tot %{until}
utc_warning: The report generated is in UTC timezone
@@ -209,6 +228,7 @@ nl:
reply:
email:
header:
+ notifications: 'Notifications'
from_with_name: '%{assignee_name} from %{inbox_name} <%{from_email}>'
reply_with_name: '%{assignee_name} from %{inbox_name} '
friendly_name: '%{sender_name} from %{business_name} <%{from_email}>'
@@ -278,6 +298,26 @@ nl:
completed_tool_call: 'Completed %{function_name} tool call'
invalid_tool_call: 'Invalid tool call'
tool_not_available: 'Tool not available'
+ documents:
+ limit_exceeded: 'Document limit exceeded'
+ pdf_format_error: 'must be a PDF file'
+ pdf_size_error: 'must be less than 10MB'
+ pdf_upload_failed: 'Failed to upload PDF to OpenAI'
+ pdf_upload_success: 'PDF uploaded successfully with file_id: %{file_id}'
+ pdf_processing_failed: 'Failed to process PDF document %{document_id}: %{error}'
+ pdf_processing_success: 'Successfully processed PDF document %{document_id}'
+ faq_generation_complete: 'FAQ generation complete. Total FAQs created: %{count}'
+ using_paginated_faq: 'Using paginated FAQ generation for document %{document_id}'
+ using_standard_faq: 'Using standard FAQ generation for document %{document_id}'
+ response_creation_error: 'Error in creating response document: %{error}'
+ missing_openai_file_id: 'Document must have openai_file_id for paginated processing'
+ openai_api_error: 'OpenAI API Error: %{error}'
+ starting_paginated_faq: 'Starting paginated FAQ generation (%{pages_per_chunk} pages per chunk)'
+ stopping_faq_generation: 'Stopping processing. Reason: %{reason}'
+ paginated_faq_complete: 'Paginated generation complete. Total FAQs: %{total_faqs}, Pages processed: %{pages_processed}'
+ processing_pages: 'Processing pages %{start}-%{end} (iteration %{iteration})'
+ chunk_generated: 'Chunk generated %{chunk_faqs} FAQs. Total so far: %{total_faqs}'
+ page_processing_error: 'Error processing pages %{start}-%{end}: %{error}'
public_portal:
search:
search_placeholder: Search for article by title or body...
@@ -358,6 +398,8 @@ nl:
Transcript:
%{format_messages}
+ agent_capacity_policy:
+ inbox_already_assigned: 'Inbox has already been assigned to this policy'
portals:
send_instructions:
email_required: 'E-mail is vereist'
diff --git a/config/locales/no.yml b/config/locales/no.yml
index 3f3fa5490..aa84fed1b 100644
--- a/config/locales/no.yml
+++ b/config/locales/no.yml
@@ -26,6 +26,9 @@
messages:
reset_password_success: Woot! Forespørsel om tilbakestilling av passord er vellykket. Sjekk innboksen for instruksjoner.
reset_password_failure: Uff da! Vi fant ingen bruker med den angitte eposten.
+ reset_password_saml_user: This account uses SAML authentication. Password reset is not available. Please contact your administrator.
+ login_saml_user: This account uses SAML authentication. Please sign in through your organization's SAML provider.
+ saml_not_available: SAML authentication is not available in this installation.
inbox_deletetion_response: Innboksen din slettingsforespørsel vil bli behandlet i løpet av en periode.
errors:
validations:
@@ -41,6 +44,8 @@
failed: Registrering mislyktes
assignment_policy:
not_found: Assignment policy not found
+ saml:
+ feature_not_enabled: SAML feature not enabled for this account
data_import:
data_type:
invalid: Ugyldig datatype
@@ -84,6 +89,20 @@
invalid_value: Ugyldig verdi. Verdiene angitt for %{attribute_name} er ugyldige
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
+ mfa:
+ already_enabled: MFA is already enabled
+ not_enabled: MFA is not enabled
+ invalid_code: Invalid verification code
+ invalid_backup_code: Invalid backup code
+ invalid_token: Invalid or expired MFA token
+ invalid_credentials: Invalid credentials or verification code
+ feature_unavailable: MFA feature is not available. Please configure encryption keys.
+ profile:
+ mfa:
+ enabled: MFA enabled successfully
+ disabled: MFA disabled successfully
+ account_saml_settings:
+ invalid_certificate: must be a valid X.509 certificate in PEM format
reports:
period: Rapporteringsperiode %{since} til %{until}
utc_warning: Rapporten generert er i UTC tidssone
@@ -209,6 +228,7 @@
reply:
email:
header:
+ notifications: 'Varsler'
from_with_name: '%{assignee_name} from %{inbox_name} <%{from_email}>'
reply_with_name: '%{assignee_name} from %{inbox_name} '
friendly_name: '%{sender_name} from %{business_name} <%{from_email}>'
@@ -278,6 +298,26 @@
completed_tool_call: 'Completed %{function_name} tool call'
invalid_tool_call: 'Invalid tool call'
tool_not_available: 'Tool not available'
+ documents:
+ limit_exceeded: 'Document limit exceeded'
+ pdf_format_error: 'must be a PDF file'
+ pdf_size_error: 'must be less than 10MB'
+ pdf_upload_failed: 'Failed to upload PDF to OpenAI'
+ pdf_upload_success: 'PDF uploaded successfully with file_id: %{file_id}'
+ pdf_processing_failed: 'Failed to process PDF document %{document_id}: %{error}'
+ pdf_processing_success: 'Successfully processed PDF document %{document_id}'
+ faq_generation_complete: 'FAQ generation complete. Total FAQs created: %{count}'
+ using_paginated_faq: 'Using paginated FAQ generation for document %{document_id}'
+ using_standard_faq: 'Using standard FAQ generation for document %{document_id}'
+ response_creation_error: 'Error in creating response document: %{error}'
+ missing_openai_file_id: 'Document must have openai_file_id for paginated processing'
+ openai_api_error: 'OpenAI API Error: %{error}'
+ starting_paginated_faq: 'Starting paginated FAQ generation (%{pages_per_chunk} pages per chunk)'
+ stopping_faq_generation: 'Stopping processing. Reason: %{reason}'
+ paginated_faq_complete: 'Paginated generation complete. Total FAQs: %{total_faqs}, Pages processed: %{pages_processed}'
+ processing_pages: 'Processing pages %{start}-%{end} (iteration %{iteration})'
+ chunk_generated: 'Chunk generated %{chunk_faqs} FAQs. Total so far: %{total_faqs}'
+ page_processing_error: 'Error processing pages %{start}-%{end}: %{error}'
public_portal:
search:
search_placeholder: Search for article by title or body...
@@ -358,6 +398,8 @@
Transcript:
%{format_messages}
+ agent_capacity_policy:
+ inbox_already_assigned: 'Inbox has already been assigned to this policy'
portals:
send_instructions:
email_required: 'Email is required'
diff --git a/config/locales/pl.yml b/config/locales/pl.yml
index b16e709ec..358d48202 100644
--- a/config/locales/pl.yml
+++ b/config/locales/pl.yml
@@ -26,6 +26,9 @@ pl:
messages:
reset_password_success: Woot! Prośba o zresetowanie hasła zakończona pomyślnie. Sprawdź swoją pocztę, aby uzyskać instrukcje.
reset_password_failure: Ups! Nie mogliśmy znaleźć żadnego użytkownika z podanym adresem e-mail.
+ reset_password_saml_user: This account uses SAML authentication. Password reset is not available. Please contact your administrator.
+ login_saml_user: This account uses SAML authentication. Please sign in through your organization's SAML provider.
+ saml_not_available: SAML authentication is not available in this installation.
inbox_deletetion_response: Żądanie usunięcia skrzynki odbiorczej zostanie rozpatrzone za jakiś czas.
errors:
validations:
@@ -41,6 +44,8 @@ pl:
failed: Rejestracja nie powiodła się
assignment_policy:
not_found: Assignment policy not found
+ saml:
+ feature_not_enabled: SAML feature not enabled for this account
data_import:
data_type:
invalid: Nieprawidłowy typ danych
@@ -84,6 +89,20 @@ pl:
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
+ mfa:
+ already_enabled: MFA is already enabled
+ not_enabled: MFA is not enabled
+ invalid_code: Invalid verification code
+ invalid_backup_code: Invalid backup code
+ invalid_token: Invalid or expired MFA token
+ invalid_credentials: Invalid credentials or verification code
+ feature_unavailable: MFA feature is not available. Please configure encryption keys.
+ profile:
+ mfa:
+ enabled: MFA enabled successfully
+ disabled: MFA disabled successfully
+ account_saml_settings:
+ invalid_certificate: must be a valid X.509 certificate in PEM format
reports:
period: Okres raportowania od %{since} do %{until}
utc_warning: Generowany raport jest w strefie czasowej UTC
@@ -209,6 +228,7 @@ pl:
reply:
email:
header:
+ notifications: 'Powiadomienia'
from_with_name: '%{assignee_name} z %{inbox_name} <%{from_email}>'
reply_with_name: '%{assignee_name} z %{inbox_name} '
friendly_name: '%{sender_name} z %{business_name} <%{from_email}>'
@@ -278,6 +298,26 @@ pl:
completed_tool_call: 'Completed %{function_name} tool call'
invalid_tool_call: 'Invalid tool call'
tool_not_available: 'Tool not available'
+ documents:
+ limit_exceeded: 'Document limit exceeded'
+ pdf_format_error: 'must be a PDF file'
+ pdf_size_error: 'must be less than 10MB'
+ pdf_upload_failed: 'Failed to upload PDF to OpenAI'
+ pdf_upload_success: 'PDF uploaded successfully with file_id: %{file_id}'
+ pdf_processing_failed: 'Failed to process PDF document %{document_id}: %{error}'
+ pdf_processing_success: 'Successfully processed PDF document %{document_id}'
+ faq_generation_complete: 'FAQ generation complete. Total FAQs created: %{count}'
+ using_paginated_faq: 'Using paginated FAQ generation for document %{document_id}'
+ using_standard_faq: 'Using standard FAQ generation for document %{document_id}'
+ response_creation_error: 'Error in creating response document: %{error}'
+ missing_openai_file_id: 'Document must have openai_file_id for paginated processing'
+ openai_api_error: 'OpenAI API Error: %{error}'
+ starting_paginated_faq: 'Starting paginated FAQ generation (%{pages_per_chunk} pages per chunk)'
+ stopping_faq_generation: 'Stopping processing. Reason: %{reason}'
+ paginated_faq_complete: 'Paginated generation complete. Total FAQs: %{total_faqs}, Pages processed: %{pages_processed}'
+ processing_pages: 'Processing pages %{start}-%{end} (iteration %{iteration})'
+ chunk_generated: 'Chunk generated %{chunk_faqs} FAQs. Total so far: %{total_faqs}'
+ page_processing_error: 'Error processing pages %{start}-%{end}: %{error}'
public_portal:
search:
search_placeholder: Wyszukaj artykuł według tytułu lub treści...
@@ -366,6 +406,8 @@ pl:
Transcript:
%{format_messages}
+ agent_capacity_policy:
+ inbox_already_assigned: 'Inbox has already been assigned to this policy'
portals:
send_instructions:
email_required: 'E-mail jest wymagany'
diff --git a/config/locales/pt.yml b/config/locales/pt.yml
index cb4379ce5..fb40e2e7f 100644
--- a/config/locales/pt.yml
+++ b/config/locales/pt.yml
@@ -26,6 +26,9 @@ pt:
messages:
reset_password_success: Legal! Pedido de redefinição de senha bem sucedido. Verifique o seu e-mail para obter instruções.
reset_password_failure: Uh ho! Não conseguimos encontrar nenhum uutilizador com o e-mail especificado.
+ reset_password_saml_user: This account uses SAML authentication. Password reset is not available. Please contact your administrator.
+ login_saml_user: This account uses SAML authentication. Please sign in through your organization's SAML provider.
+ saml_not_available: SAML authentication is not available in this installation.
inbox_deletetion_response: O seu pedido de eliminação de caixa de entrada será processado mais tarde.
errors:
validations:
@@ -41,6 +44,8 @@ pt:
failed: Falha na inscrição
assignment_policy:
not_found: Assignment policy not found
+ saml:
+ feature_not_enabled: SAML feature not enabled for this account
data_import:
data_type:
invalid: Tipo de dados inválido
@@ -84,6 +89,20 @@ pt:
invalid_value: Valor inválido. Os valores fornecidos para %{attribute_name} são inválidos
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
+ mfa:
+ already_enabled: MFA is already enabled
+ not_enabled: MFA is not enabled
+ invalid_code: Invalid verification code
+ invalid_backup_code: Invalid backup code
+ invalid_token: Invalid or expired MFA token
+ invalid_credentials: Invalid credentials or verification code
+ feature_unavailable: MFA feature is not available. Please configure encryption keys.
+ profile:
+ mfa:
+ enabled: MFA enabled successfully
+ disabled: MFA disabled successfully
+ account_saml_settings:
+ invalid_certificate: must be a valid X.509 certificate in PEM format
reports:
period: Período do relatório de %{since} a %{until}
utc_warning: O relatório gerado está no fuso horário UTC
@@ -209,6 +228,7 @@ pt:
reply:
email:
header:
+ notifications: 'Notificações'
from_with_name: '%{assignee_name} de %{inbox_name} <%{from_email}>'
reply_with_name: '%{assignee_name} de %{inbox_name} '
friendly_name: '%{sender_name} de %{business_name} <%{from_email}>'
@@ -278,6 +298,26 @@ pt:
completed_tool_call: 'Chamada da ferramenta %{function_name} concluída'
invalid_tool_call: 'Chamada de ferramenta incorreta'
tool_not_available: 'Ferramenta não disponível'
+ documents:
+ limit_exceeded: 'Document limit exceeded'
+ pdf_format_error: 'must be a PDF file'
+ pdf_size_error: 'must be less than 10MB'
+ pdf_upload_failed: 'Failed to upload PDF to OpenAI'
+ pdf_upload_success: 'PDF uploaded successfully with file_id: %{file_id}'
+ pdf_processing_failed: 'Failed to process PDF document %{document_id}: %{error}'
+ pdf_processing_success: 'Successfully processed PDF document %{document_id}'
+ faq_generation_complete: 'FAQ generation complete. Total FAQs created: %{count}'
+ using_paginated_faq: 'Using paginated FAQ generation for document %{document_id}'
+ using_standard_faq: 'Using standard FAQ generation for document %{document_id}'
+ response_creation_error: 'Error in creating response document: %{error}'
+ missing_openai_file_id: 'Document must have openai_file_id for paginated processing'
+ openai_api_error: 'OpenAI API Error: %{error}'
+ starting_paginated_faq: 'Starting paginated FAQ generation (%{pages_per_chunk} pages per chunk)'
+ stopping_faq_generation: 'Stopping processing. Reason: %{reason}'
+ paginated_faq_complete: 'Paginated generation complete. Total FAQs: %{total_faqs}, Pages processed: %{pages_processed}'
+ processing_pages: 'Processing pages %{start}-%{end} (iteration %{iteration})'
+ chunk_generated: 'Chunk generated %{chunk_faqs} FAQs. Total so far: %{total_faqs}'
+ page_processing_error: 'Error processing pages %{start}-%{end}: %{error}'
public_portal:
search:
search_placeholder: Pesquisar artigo por título ou corpo...
@@ -346,6 +386,8 @@ pt:
Nova conversa iniciada em %{brand_name}\n\nCanal: %{channel_info}\nCriado: %{formatted_creation_time}\nID da conversa: %{display_id}\nVer em %{brand_name}: %{url}
transcript_activity: |
Transcrição da conversa de %{brand_name}\n\nCanal: %{channel_info}\nID da conversa: %{display_id}\nVer em %{brand_name}: %{url}\n\nTranscrição:\n%{format_messages}
+ agent_capacity_policy:
+ inbox_already_assigned: 'Inbox has already been assigned to this policy'
portals:
send_instructions:
email_required: 'E-mail é necessário'
diff --git a/config/locales/pt_BR.yml b/config/locales/pt_BR.yml
index 4dcfbad6c..bdffa0b63 100644
--- a/config/locales/pt_BR.yml
+++ b/config/locales/pt_BR.yml
@@ -26,6 +26,9 @@ pt_BR:
messages:
reset_password_success: Legal! A solicitação de alteração de senha foi bem sucedida. Verifique seu e-mail para obter instruções.
reset_password_failure: Uh ho! Não conseguimos encontrar nenhum usuário com o e-mail especificado.
+ reset_password_saml_user: This account uses SAML authentication. Password reset is not available. Please contact your administrator.
+ login_saml_user: This account uses SAML authentication. Please sign in through your organization's SAML provider.
+ saml_not_available: SAML authentication is not available in this installation.
inbox_deletetion_response: Seu pedido de exclusão da caixa de entrada será processado dentro de algum tempo.
errors:
validations:
@@ -41,6 +44,8 @@ pt_BR:
failed: Registro falhou
assignment_policy:
not_found: Assignment policy not found
+ saml:
+ feature_not_enabled: SAML feature not enabled for this account
data_import:
data_type:
invalid: Tipo de dado inválido
@@ -84,6 +89,20 @@ pt_BR:
invalid_value: Valor inválido. Os valores fornecidos para %{attribute_name} são inválidos
custom_attribute_definition:
key_conflict: A chave fornecida não é permitida pois pode entrar em conflito com os atributos padrão.
+ mfa:
+ already_enabled: MFA is already enabled
+ not_enabled: MFA is not enabled
+ invalid_code: Invalid verification code
+ invalid_backup_code: Invalid backup code
+ invalid_token: Invalid or expired MFA token
+ invalid_credentials: Invalid credentials or verification code
+ feature_unavailable: MFA feature is not available. Please configure encryption keys.
+ profile:
+ mfa:
+ enabled: MFA enabled successfully
+ disabled: MFA disabled successfully
+ account_saml_settings:
+ invalid_certificate: must be a valid X.509 certificate in PEM format
reports:
period: Reportando o período %{since} a %{until}
utc_warning: O relatório gerado está em fuso horário UTC
@@ -209,6 +228,7 @@ pt_BR:
reply:
email:
header:
+ notifications: 'Notificações'
from_with_name: '%{assignee_name} de %{inbox_name} <%{from_email}>'
reply_with_name: '%{assignee_name} de %{inbox_name} '
friendly_name: '%{sender_name} de %{business_name} <%{from_email}>'
@@ -278,6 +298,26 @@ pt_BR:
completed_tool_call: 'Uso da ferramenta %{function_name} concluída'
invalid_tool_call: 'Ferramenta inválida'
tool_not_available: 'Ferramenta indisponível'
+ documents:
+ limit_exceeded: 'Document limit exceeded'
+ pdf_format_error: 'must be a PDF file'
+ pdf_size_error: 'must be less than 10MB'
+ pdf_upload_failed: 'Failed to upload PDF to OpenAI'
+ pdf_upload_success: 'PDF uploaded successfully with file_id: %{file_id}'
+ pdf_processing_failed: 'Failed to process PDF document %{document_id}: %{error}'
+ pdf_processing_success: 'Successfully processed PDF document %{document_id}'
+ faq_generation_complete: 'FAQ generation complete. Total FAQs created: %{count}'
+ using_paginated_faq: 'Using paginated FAQ generation for document %{document_id}'
+ using_standard_faq: 'Using standard FAQ generation for document %{document_id}'
+ response_creation_error: 'Error in creating response document: %{error}'
+ missing_openai_file_id: 'Document must have openai_file_id for paginated processing'
+ openai_api_error: 'OpenAI API Error: %{error}'
+ starting_paginated_faq: 'Starting paginated FAQ generation (%{pages_per_chunk} pages per chunk)'
+ stopping_faq_generation: 'Stopping processing. Reason: %{reason}'
+ paginated_faq_complete: 'Paginated generation complete. Total FAQs: %{total_faqs}, Pages processed: %{pages_processed}'
+ processing_pages: 'Processing pages %{start}-%{end} (iteration %{iteration})'
+ chunk_generated: 'Chunk generated %{chunk_faqs} FAQs. Total so far: %{total_faqs}'
+ page_processing_error: 'Error processing pages %{start}-%{end}: %{error}'
public_portal:
search:
search_placeholder: Pesquisar por artigo por título ou corpo...
@@ -358,6 +398,8 @@ pt_BR:
Transcrição:
%{format_messages}
+ agent_capacity_policy:
+ inbox_already_assigned: 'Inbox has already been assigned to this policy'
portals:
send_instructions:
email_required: 'E-mail é obrigatório'
diff --git a/config/locales/ro.yml b/config/locales/ro.yml
index 8ea544ff3..df979d147 100644
--- a/config/locales/ro.yml
+++ b/config/locales/ro.yml
@@ -26,6 +26,9 @@ ro:
messages:
reset_password_success: Woot! Cererea de resetare a parolei a reusit. Verifica emailul pentru instructiuni.
reset_password_failure: Nu am putut găsi niciun utilizator cu e-mailul specificat.
+ reset_password_saml_user: This account uses SAML authentication. Password reset is not available. Please contact your administrator.
+ login_saml_user: This account uses SAML authentication. Please sign in through your organization's SAML provider.
+ saml_not_available: SAML authentication is not available in this installation.
inbox_deletetion_response: Solicitarea de ștergere a inboxului va fi procesată într-un anumit timp.
errors:
validations:
@@ -41,6 +44,8 @@ ro:
failed: Înregistrare eșuată
assignment_policy:
not_found: Assignment policy not found
+ saml:
+ feature_not_enabled: SAML feature not enabled for this account
data_import:
data_type:
invalid: Tip de date nevalid
@@ -84,6 +89,20 @@ ro:
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
+ mfa:
+ already_enabled: MFA is already enabled
+ not_enabled: MFA is not enabled
+ invalid_code: Invalid verification code
+ invalid_backup_code: Invalid backup code
+ invalid_token: Invalid or expired MFA token
+ invalid_credentials: Invalid credentials or verification code
+ feature_unavailable: MFA feature is not available. Please configure encryption keys.
+ profile:
+ mfa:
+ enabled: MFA enabled successfully
+ disabled: MFA disabled successfully
+ account_saml_settings:
+ invalid_certificate: must be a valid X.509 certificate in PEM format
reports:
period: Perioada de raportare %{since}-%{until}
utc_warning: Raportul generat este în fusul orar UTC
@@ -209,6 +228,7 @@ ro:
reply:
email:
header:
+ notifications: 'Notificări'
from_with_name: '%{assignee_name} din %{inbox_name} <%{from_email}>'
reply_with_name: '%{assignee_name} din %{inbox_name} '
friendly_name: '%{sender_name} din %{business_name} <%{from_email}>'
@@ -278,6 +298,26 @@ ro:
completed_tool_call: 'Completed %{function_name} tool call'
invalid_tool_call: 'Invalid tool call'
tool_not_available: 'Tool not available'
+ documents:
+ limit_exceeded: 'Document limit exceeded'
+ pdf_format_error: 'must be a PDF file'
+ pdf_size_error: 'must be less than 10MB'
+ pdf_upload_failed: 'Failed to upload PDF to OpenAI'
+ pdf_upload_success: 'PDF uploaded successfully with file_id: %{file_id}'
+ pdf_processing_failed: 'Failed to process PDF document %{document_id}: %{error}'
+ pdf_processing_success: 'Successfully processed PDF document %{document_id}'
+ faq_generation_complete: 'FAQ generation complete. Total FAQs created: %{count}'
+ using_paginated_faq: 'Using paginated FAQ generation for document %{document_id}'
+ using_standard_faq: 'Using standard FAQ generation for document %{document_id}'
+ response_creation_error: 'Error in creating response document: %{error}'
+ missing_openai_file_id: 'Document must have openai_file_id for paginated processing'
+ openai_api_error: 'OpenAI API Error: %{error}'
+ starting_paginated_faq: 'Starting paginated FAQ generation (%{pages_per_chunk} pages per chunk)'
+ stopping_faq_generation: 'Stopping processing. Reason: %{reason}'
+ paginated_faq_complete: 'Paginated generation complete. Total FAQs: %{total_faqs}, Pages processed: %{pages_processed}'
+ processing_pages: 'Processing pages %{start}-%{end} (iteration %{iteration})'
+ chunk_generated: 'Chunk generated %{chunk_faqs} FAQs. Total so far: %{total_faqs}'
+ page_processing_error: 'Error processing pages %{start}-%{end}: %{error}'
public_portal:
search:
search_placeholder: Căutați articol după titlu sau corp...
@@ -362,6 +402,8 @@ ro:
Transcript:
%{format_messages}
+ agent_capacity_policy:
+ inbox_already_assigned: 'Inbox has already been assigned to this policy'
portals:
send_instructions:
email_required: 'E-mailul este necesar'
diff --git a/config/locales/ru.yml b/config/locales/ru.yml
index 8ded63703..ba8c90652 100644
--- a/config/locales/ru.yml
+++ b/config/locales/ru.yml
@@ -26,6 +26,9 @@ ru:
messages:
reset_password_success: Круто! Запрос на сброс пароля удался. Проверьте почту для получения инструкций.
reset_password_failure: Ой! Мы не смогли найти пользователя с указанным email.
+ reset_password_saml_user: This account uses SAML authentication. Password reset is not available. Please contact your administrator.
+ login_saml_user: This account uses SAML authentication. Please sign in through your organization's SAML provider.
+ saml_not_available: SAML authentication is not available in this installation.
inbox_deletetion_response: Ваш запрос на удаление входящих сообщений будет обработан через некоторое время.
errors:
validations:
@@ -41,6 +44,8 @@ ru:
failed: Ошибка регистрации
assignment_policy:
not_found: Assignment policy not found
+ saml:
+ feature_not_enabled: SAML feature not enabled for this account
data_import:
data_type:
invalid: Недопустимый тип данных
@@ -84,6 +89,20 @@ ru:
invalid_value: Недопустимое значение. Значения, предоставленные для %{attribute_name} являются недопустимыми
custom_attribute_definition:
key_conflict: Предоставленный ключ не разрешён, так как он может конфликтовать со стандартными атрибутами.
+ mfa:
+ already_enabled: MFA is already enabled
+ not_enabled: MFA is not enabled
+ invalid_code: Invalid verification code
+ invalid_backup_code: Invalid backup code
+ invalid_token: Invalid or expired MFA token
+ invalid_credentials: Invalid credentials or verification code
+ feature_unavailable: MFA feature is not available. Please configure encryption keys.
+ profile:
+ mfa:
+ enabled: MFA enabled successfully
+ disabled: MFA disabled successfully
+ account_saml_settings:
+ invalid_certificate: must be a valid X.509 certificate in PEM format
reports:
period: Отчётный период с %{since} по %{until}
utc_warning: Отчёт создан в часовом поясе UTC
@@ -209,6 +228,7 @@ ru:
reply:
email:
header:
+ notifications: 'Уведомления'
from_with_name: '%{assignee_name} от %{inbox_name} <%{from_email}>'
reply_with_name: '%{assignee_name} от %{inbox_name} <%{reply_email}>'
friendly_name: '%{sender_name} из %{business_name} <%{from_email}>'
@@ -278,6 +298,26 @@ ru:
completed_tool_call: 'Completed %{function_name} tool call'
invalid_tool_call: 'Invalid tool call'
tool_not_available: 'Tool not available'
+ documents:
+ limit_exceeded: 'Document limit exceeded'
+ pdf_format_error: 'must be a PDF file'
+ pdf_size_error: 'must be less than 10MB'
+ pdf_upload_failed: 'Failed to upload PDF to OpenAI'
+ pdf_upload_success: 'PDF uploaded successfully with file_id: %{file_id}'
+ pdf_processing_failed: 'Failed to process PDF document %{document_id}: %{error}'
+ pdf_processing_success: 'Successfully processed PDF document %{document_id}'
+ faq_generation_complete: 'FAQ generation complete. Total FAQs created: %{count}'
+ using_paginated_faq: 'Using paginated FAQ generation for document %{document_id}'
+ using_standard_faq: 'Using standard FAQ generation for document %{document_id}'
+ response_creation_error: 'Error in creating response document: %{error}'
+ missing_openai_file_id: 'Document must have openai_file_id for paginated processing'
+ openai_api_error: 'OpenAI API Error: %{error}'
+ starting_paginated_faq: 'Starting paginated FAQ generation (%{pages_per_chunk} pages per chunk)'
+ stopping_faq_generation: 'Stopping processing. Reason: %{reason}'
+ paginated_faq_complete: 'Paginated generation complete. Total FAQs: %{total_faqs}, Pages processed: %{pages_processed}'
+ processing_pages: 'Processing pages %{start}-%{end} (iteration %{iteration})'
+ chunk_generated: 'Chunk generated %{chunk_faqs} FAQs. Total so far: %{total_faqs}'
+ page_processing_error: 'Error processing pages %{start}-%{end}: %{error}'
public_portal:
search:
search_placeholder: Поиск статьи по названию или содержанию...
@@ -366,6 +406,8 @@ ru:
Transcript:
%{format_messages}
+ agent_capacity_policy:
+ inbox_already_assigned: 'Inbox has already been assigned to this policy'
portals:
send_instructions:
email_required: 'Необходимо указать Email'
diff --git a/config/locales/sh.yml b/config/locales/sh.yml
index 0121d7b84..7e0fa8abf 100644
--- a/config/locales/sh.yml
+++ b/config/locales/sh.yml
@@ -26,6 +26,9 @@ sh:
messages:
reset_password_success: Woot! Request for password reset is successful. Check your mail for instructions.
reset_password_failure: Uh ho! We could not find any user with the specified email.
+ reset_password_saml_user: This account uses SAML authentication. Password reset is not available. Please contact your administrator.
+ login_saml_user: This account uses SAML authentication. Please sign in through your organization's SAML provider.
+ saml_not_available: SAML authentication is not available in this installation.
inbox_deletetion_response: Your inbox deletion request will be processed in some time.
errors:
validations:
@@ -41,6 +44,8 @@ sh:
failed: Signup failed
assignment_policy:
not_found: Assignment policy not found
+ saml:
+ feature_not_enabled: SAML feature not enabled for this account
data_import:
data_type:
invalid: Invalid data type
@@ -84,6 +89,20 @@ sh:
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
+ mfa:
+ already_enabled: MFA is already enabled
+ not_enabled: MFA is not enabled
+ invalid_code: Invalid verification code
+ invalid_backup_code: Invalid backup code
+ invalid_token: Invalid or expired MFA token
+ invalid_credentials: Invalid credentials or verification code
+ feature_unavailable: MFA feature is not available. Please configure encryption keys.
+ profile:
+ mfa:
+ enabled: MFA enabled successfully
+ disabled: MFA disabled successfully
+ account_saml_settings:
+ invalid_certificate: must be a valid X.509 certificate in PEM format
reports:
period: Reporting period %{since} to %{until}
utc_warning: The report generated is in UTC timezone
@@ -209,6 +228,7 @@ sh:
reply:
email:
header:
+ notifications: 'Notifications'
from_with_name: '%{assignee_name} from %{inbox_name} <%{from_email}>'
reply_with_name: '%{assignee_name} from %{inbox_name} '
friendly_name: '%{sender_name} from %{business_name} <%{from_email}>'
@@ -278,6 +298,26 @@ sh:
completed_tool_call: 'Completed %{function_name} tool call'
invalid_tool_call: 'Invalid tool call'
tool_not_available: 'Tool not available'
+ documents:
+ limit_exceeded: 'Document limit exceeded'
+ pdf_format_error: 'must be a PDF file'
+ pdf_size_error: 'must be less than 10MB'
+ pdf_upload_failed: 'Failed to upload PDF to OpenAI'
+ pdf_upload_success: 'PDF uploaded successfully with file_id: %{file_id}'
+ pdf_processing_failed: 'Failed to process PDF document %{document_id}: %{error}'
+ pdf_processing_success: 'Successfully processed PDF document %{document_id}'
+ faq_generation_complete: 'FAQ generation complete. Total FAQs created: %{count}'
+ using_paginated_faq: 'Using paginated FAQ generation for document %{document_id}'
+ using_standard_faq: 'Using standard FAQ generation for document %{document_id}'
+ response_creation_error: 'Error in creating response document: %{error}'
+ missing_openai_file_id: 'Document must have openai_file_id for paginated processing'
+ openai_api_error: 'OpenAI API Error: %{error}'
+ starting_paginated_faq: 'Starting paginated FAQ generation (%{pages_per_chunk} pages per chunk)'
+ stopping_faq_generation: 'Stopping processing. Reason: %{reason}'
+ paginated_faq_complete: 'Paginated generation complete. Total FAQs: %{total_faqs}, Pages processed: %{pages_processed}'
+ processing_pages: 'Processing pages %{start}-%{end} (iteration %{iteration})'
+ chunk_generated: 'Chunk generated %{chunk_faqs} FAQs. Total so far: %{total_faqs}'
+ page_processing_error: 'Error processing pages %{start}-%{end}: %{error}'
public_portal:
search:
search_placeholder: Search for article by title or body...
@@ -366,6 +406,8 @@ sh:
Transcript:
%{format_messages}
+ agent_capacity_policy:
+ inbox_already_assigned: 'Inbox has already been assigned to this policy'
portals:
send_instructions:
email_required: 'Email is required'
diff --git a/config/locales/sk.yml b/config/locales/sk.yml
index e445b7495..d59526132 100644
--- a/config/locales/sk.yml
+++ b/config/locales/sk.yml
@@ -26,6 +26,9 @@ sk:
messages:
reset_password_success: Woot! Request for password reset is successful. Check your mail for instructions.
reset_password_failure: Uh ho! We could not find any user with the specified email.
+ reset_password_saml_user: This account uses SAML authentication. Password reset is not available. Please contact your administrator.
+ login_saml_user: This account uses SAML authentication. Please sign in through your organization's SAML provider.
+ saml_not_available: SAML authentication is not available in this installation.
inbox_deletetion_response: Your inbox deletion request will be processed in some time.
errors:
validations:
@@ -41,6 +44,8 @@ sk:
failed: Signup failed
assignment_policy:
not_found: Assignment policy not found
+ saml:
+ feature_not_enabled: SAML feature not enabled for this account
data_import:
data_type:
invalid: Invalid data type
@@ -84,6 +89,20 @@ sk:
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
+ mfa:
+ already_enabled: MFA is already enabled
+ not_enabled: MFA is not enabled
+ invalid_code: Invalid verification code
+ invalid_backup_code: Invalid backup code
+ invalid_token: Invalid or expired MFA token
+ invalid_credentials: Invalid credentials or verification code
+ feature_unavailable: MFA feature is not available. Please configure encryption keys.
+ profile:
+ mfa:
+ enabled: MFA enabled successfully
+ disabled: MFA disabled successfully
+ account_saml_settings:
+ invalid_certificate: must be a valid X.509 certificate in PEM format
reports:
period: Reporting period %{since} to %{until}
utc_warning: The report generated is in UTC timezone
@@ -209,6 +228,7 @@ sk:
reply:
email:
header:
+ notifications: 'Upozornenia'
from_with_name: '%{assignee_name} z %{inbox_name} '
reply_with_name: '%{assignee_name} z %{inbox_name} '
friendly_name: '%{sender_name} z %{business_name} '
@@ -278,6 +298,26 @@ sk:
completed_tool_call: 'Completed %{function_name} tool call'
invalid_tool_call: 'Invalid tool call'
tool_not_available: 'Tool not available'
+ documents:
+ limit_exceeded: 'Document limit exceeded'
+ pdf_format_error: 'must be a PDF file'
+ pdf_size_error: 'must be less than 10MB'
+ pdf_upload_failed: 'Failed to upload PDF to OpenAI'
+ pdf_upload_success: 'PDF uploaded successfully with file_id: %{file_id}'
+ pdf_processing_failed: 'Failed to process PDF document %{document_id}: %{error}'
+ pdf_processing_success: 'Successfully processed PDF document %{document_id}'
+ faq_generation_complete: 'FAQ generation complete. Total FAQs created: %{count}'
+ using_paginated_faq: 'Using paginated FAQ generation for document %{document_id}'
+ using_standard_faq: 'Using standard FAQ generation for document %{document_id}'
+ response_creation_error: 'Error in creating response document: %{error}'
+ missing_openai_file_id: 'Document must have openai_file_id for paginated processing'
+ openai_api_error: 'OpenAI API Error: %{error}'
+ starting_paginated_faq: 'Starting paginated FAQ generation (%{pages_per_chunk} pages per chunk)'
+ stopping_faq_generation: 'Stopping processing. Reason: %{reason}'
+ paginated_faq_complete: 'Paginated generation complete. Total FAQs: %{total_faqs}, Pages processed: %{pages_processed}'
+ processing_pages: 'Processing pages %{start}-%{end} (iteration %{iteration})'
+ chunk_generated: 'Chunk generated %{chunk_faqs} FAQs. Total so far: %{total_faqs}'
+ page_processing_error: 'Error processing pages %{start}-%{end}: %{error}'
public_portal:
search:
search_placeholder: Search for article by title or body...
@@ -366,6 +406,8 @@ sk:
Transcript:
%{format_messages}
+ agent_capacity_policy:
+ inbox_already_assigned: 'Inbox has already been assigned to this policy'
portals:
send_instructions:
email_required: 'Email is required'
diff --git a/config/locales/sl.yml b/config/locales/sl.yml
index 079083bc9..a35d95be7 100644
--- a/config/locales/sl.yml
+++ b/config/locales/sl.yml
@@ -26,6 +26,9 @@ sl:
messages:
reset_password_success: Juhu! Zahteva za ponastavitev gesla je bila uspešna. Preverite svojo e-pošto za navodila.
reset_password_failure: O ne! Nismo mogli najti nobenega uporabnika z navedenim e-poštnim naslovom.
+ reset_password_saml_user: This account uses SAML authentication. Password reset is not available. Please contact your administrator.
+ login_saml_user: This account uses SAML authentication. Please sign in through your organization's SAML provider.
+ saml_not_available: SAML authentication is not available in this installation.
inbox_deletetion_response: Vaša zahteva za izbris predala bo obdelana čez nekaj časa.
errors:
validations:
@@ -41,6 +44,8 @@ sl:
failed: Registracija neuspešna
assignment_policy:
not_found: Assignment policy not found
+ saml:
+ feature_not_enabled: SAML feature not enabled for this account
data_import:
data_type:
invalid: Nepravilen podatkovni tip
@@ -84,6 +89,20 @@ sl:
invalid_value: Neveljavna vrednost. Podane vrednosti za %{attribute_name} so neveljavne
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
+ mfa:
+ already_enabled: MFA is already enabled
+ not_enabled: MFA is not enabled
+ invalid_code: Invalid verification code
+ invalid_backup_code: Invalid backup code
+ invalid_token: Invalid or expired MFA token
+ invalid_credentials: Invalid credentials or verification code
+ feature_unavailable: MFA feature is not available. Please configure encryption keys.
+ profile:
+ mfa:
+ enabled: MFA enabled successfully
+ disabled: MFA disabled successfully
+ account_saml_settings:
+ invalid_certificate: must be a valid X.509 certificate in PEM format
reports:
period: Obdobje poročanja %{since} do %{until}
utc_warning: Ustvarjeno poročilo je v časovnem pasu UTC
@@ -209,6 +228,7 @@ sl:
reply:
email:
header:
+ notifications: 'Notifications'
from_with_name: '%{assignee_name} iz %{inbox_name} <%{from_email}>'
reply_with_name: '%{assignee_name} iz %{inbox_name} '
friendly_name: '%{sender_name} iz %{business_name} <%{from_email}>'
@@ -278,6 +298,26 @@ sl:
completed_tool_call: 'Completed %{function_name} tool call'
invalid_tool_call: 'Invalid tool call'
tool_not_available: 'Tool not available'
+ documents:
+ limit_exceeded: 'Document limit exceeded'
+ pdf_format_error: 'must be a PDF file'
+ pdf_size_error: 'must be less than 10MB'
+ pdf_upload_failed: 'Failed to upload PDF to OpenAI'
+ pdf_upload_success: 'PDF uploaded successfully with file_id: %{file_id}'
+ pdf_processing_failed: 'Failed to process PDF document %{document_id}: %{error}'
+ pdf_processing_success: 'Successfully processed PDF document %{document_id}'
+ faq_generation_complete: 'FAQ generation complete. Total FAQs created: %{count}'
+ using_paginated_faq: 'Using paginated FAQ generation for document %{document_id}'
+ using_standard_faq: 'Using standard FAQ generation for document %{document_id}'
+ response_creation_error: 'Error in creating response document: %{error}'
+ missing_openai_file_id: 'Document must have openai_file_id for paginated processing'
+ openai_api_error: 'OpenAI API Error: %{error}'
+ starting_paginated_faq: 'Starting paginated FAQ generation (%{pages_per_chunk} pages per chunk)'
+ stopping_faq_generation: 'Stopping processing. Reason: %{reason}'
+ paginated_faq_complete: 'Paginated generation complete. Total FAQs: %{total_faqs}, Pages processed: %{pages_processed}'
+ processing_pages: 'Processing pages %{start}-%{end} (iteration %{iteration})'
+ chunk_generated: 'Chunk generated %{chunk_faqs} FAQs. Total so far: %{total_faqs}'
+ page_processing_error: 'Error processing pages %{start}-%{end}: %{error}'
public_portal:
search:
search_placeholder: Iskanje članka po naslovu ali telesu ...
@@ -366,6 +406,8 @@ sl:
Transcript:
%{format_messages}
+ agent_capacity_policy:
+ inbox_already_assigned: 'Inbox has already been assigned to this policy'
portals:
send_instructions:
email_required: 'Email is required'
diff --git a/config/locales/sq.yml b/config/locales/sq.yml
index f9922e678..c2da0e9f8 100644
--- a/config/locales/sq.yml
+++ b/config/locales/sq.yml
@@ -26,6 +26,9 @@ sq:
messages:
reset_password_success: Woot! Request for password reset is successful. Check your mail for instructions.
reset_password_failure: Uh ho! We could not find any user with the specified email.
+ reset_password_saml_user: This account uses SAML authentication. Password reset is not available. Please contact your administrator.
+ login_saml_user: This account uses SAML authentication. Please sign in through your organization's SAML provider.
+ saml_not_available: SAML authentication is not available in this installation.
inbox_deletetion_response: Your inbox deletion request will be processed in some time.
errors:
validations:
@@ -41,6 +44,8 @@ sq:
failed: Signup failed
assignment_policy:
not_found: Nuk u gjet politika e caktimit
+ saml:
+ feature_not_enabled: SAML feature not enabled for this account
data_import:
data_type:
invalid: Invalid data type
@@ -84,6 +89,20 @@ sq:
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
+ mfa:
+ already_enabled: MFA is already enabled
+ not_enabled: MFA is not enabled
+ invalid_code: Invalid verification code
+ invalid_backup_code: Invalid backup code
+ invalid_token: Invalid or expired MFA token
+ invalid_credentials: Invalid credentials or verification code
+ feature_unavailable: MFA feature is not available. Please configure encryption keys.
+ profile:
+ mfa:
+ enabled: MFA enabled successfully
+ disabled: MFA disabled successfully
+ account_saml_settings:
+ invalid_certificate: must be a valid X.509 certificate in PEM format
reports:
period: Reporting period %{since} to %{until}
utc_warning: The report generated is in UTC timezone
@@ -209,6 +228,7 @@ sq:
reply:
email:
header:
+ notifications: 'Notifications'
from_with_name: '%{assignee_name} from %{inbox_name} <%{from_email}>'
reply_with_name: '%{assignee_name} from %{inbox_name} '
friendly_name: '%{sender_name} from %{business_name} <%{from_email}>'
@@ -278,6 +298,26 @@ sq:
completed_tool_call: 'Thirrja e mjetit %{function_name} u përfundua'
invalid_tool_call: 'Thirrje e pavlefshme e mjetit'
tool_not_available: 'Mjeti nuk është i disponueshëm'
+ documents:
+ limit_exceeded: 'Document limit exceeded'
+ pdf_format_error: 'must be a PDF file'
+ pdf_size_error: 'must be less than 10MB'
+ pdf_upload_failed: 'Failed to upload PDF to OpenAI'
+ pdf_upload_success: 'PDF uploaded successfully with file_id: %{file_id}'
+ pdf_processing_failed: 'Failed to process PDF document %{document_id}: %{error}'
+ pdf_processing_success: 'Successfully processed PDF document %{document_id}'
+ faq_generation_complete: 'FAQ generation complete. Total FAQs created: %{count}'
+ using_paginated_faq: 'Using paginated FAQ generation for document %{document_id}'
+ using_standard_faq: 'Using standard FAQ generation for document %{document_id}'
+ response_creation_error: 'Error in creating response document: %{error}'
+ missing_openai_file_id: 'Document must have openai_file_id for paginated processing'
+ openai_api_error: 'OpenAI API Error: %{error}'
+ starting_paginated_faq: 'Starting paginated FAQ generation (%{pages_per_chunk} pages per chunk)'
+ stopping_faq_generation: 'Stopping processing. Reason: %{reason}'
+ paginated_faq_complete: 'Paginated generation complete. Total FAQs: %{total_faqs}, Pages processed: %{pages_processed}'
+ processing_pages: 'Processing pages %{start}-%{end} (iteration %{iteration})'
+ chunk_generated: 'Chunk generated %{chunk_faqs} FAQs. Total so far: %{total_faqs}'
+ page_processing_error: 'Error processing pages %{start}-%{end}: %{error}'
public_portal:
search:
search_placeholder: Search for article by title or body...
@@ -358,6 +398,8 @@ sq:
Transkript:
%{format_messages}
+ agent_capacity_policy:
+ inbox_already_assigned: 'Inbox has already been assigned to this policy'
portals:
send_instructions:
email_required: 'Kërkohet emaili'
diff --git a/config/locales/sr.yml b/config/locales/sr.yml
index 243e190ea..d949944c6 100644
--- a/config/locales/sr.yml
+++ b/config/locales/sr.yml
@@ -26,6 +26,9 @@ sr-Latn:
messages:
reset_password_success: Opa! Zahtev za resetovanjem lozinke je uspešan. Proverite vašu e-poštu za uputstvo.
reset_password_failure: O ne! Nismo mogli da pronađemo nijednog korisnika sa navedenom e-poštom.
+ reset_password_saml_user: This account uses SAML authentication. Password reset is not available. Please contact your administrator.
+ login_saml_user: This account uses SAML authentication. Please sign in through your organization's SAML provider.
+ saml_not_available: SAML authentication is not available in this installation.
inbox_deletetion_response: Your inbox deletion request will be processed in some time.
errors:
validations:
@@ -41,6 +44,8 @@ sr-Latn:
failed: Registracija nije uspela
assignment_policy:
not_found: Assignment policy not found
+ saml:
+ feature_not_enabled: SAML feature not enabled for this account
data_import:
data_type:
invalid: Neispravan tip podatka
@@ -84,6 +89,20 @@ sr-Latn:
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
+ mfa:
+ already_enabled: MFA is already enabled
+ not_enabled: MFA is not enabled
+ invalid_code: Invalid verification code
+ invalid_backup_code: Invalid backup code
+ invalid_token: Invalid or expired MFA token
+ invalid_credentials: Invalid credentials or verification code
+ feature_unavailable: MFA feature is not available. Please configure encryption keys.
+ profile:
+ mfa:
+ enabled: MFA enabled successfully
+ disabled: MFA disabled successfully
+ account_saml_settings:
+ invalid_certificate: must be a valid X.509 certificate in PEM format
reports:
period: Period izveštaja %{since} do %{until}
utc_warning: Generisani izveštaj je u UTC vremenskoj zoni
@@ -209,6 +228,7 @@ sr-Latn:
reply:
email:
header:
+ notifications: 'Obaveštenja'
from_with_name: '%{assignee_name} iz %{inbox_name} <%{from_email}>'
reply_with_name: '%{assignee_name} iz %{inbox_name} '
friendly_name: '%{sender_name} iz %{business_name} <%{from_email}>'
@@ -278,6 +298,26 @@ sr-Latn:
completed_tool_call: 'Completed %{function_name} tool call'
invalid_tool_call: 'Invalid tool call'
tool_not_available: 'Tool not available'
+ documents:
+ limit_exceeded: 'Document limit exceeded'
+ pdf_format_error: 'must be a PDF file'
+ pdf_size_error: 'must be less than 10MB'
+ pdf_upload_failed: 'Failed to upload PDF to OpenAI'
+ pdf_upload_success: 'PDF uploaded successfully with file_id: %{file_id}'
+ pdf_processing_failed: 'Failed to process PDF document %{document_id}: %{error}'
+ pdf_processing_success: 'Successfully processed PDF document %{document_id}'
+ faq_generation_complete: 'FAQ generation complete. Total FAQs created: %{count}'
+ using_paginated_faq: 'Using paginated FAQ generation for document %{document_id}'
+ using_standard_faq: 'Using standard FAQ generation for document %{document_id}'
+ response_creation_error: 'Error in creating response document: %{error}'
+ missing_openai_file_id: 'Document must have openai_file_id for paginated processing'
+ openai_api_error: 'OpenAI API Error: %{error}'
+ starting_paginated_faq: 'Starting paginated FAQ generation (%{pages_per_chunk} pages per chunk)'
+ stopping_faq_generation: 'Stopping processing. Reason: %{reason}'
+ paginated_faq_complete: 'Paginated generation complete. Total FAQs: %{total_faqs}, Pages processed: %{pages_processed}'
+ processing_pages: 'Processing pages %{start}-%{end} (iteration %{iteration})'
+ chunk_generated: 'Chunk generated %{chunk_faqs} FAQs. Total so far: %{total_faqs}'
+ page_processing_error: 'Error processing pages %{start}-%{end}: %{error}'
public_portal:
search:
search_placeholder: Search for article by title or body...
@@ -362,6 +402,8 @@ sr-Latn:
Transcript:
%{format_messages}
+ agent_capacity_policy:
+ inbox_already_assigned: 'Inbox has already been assigned to this policy'
portals:
send_instructions:
email_required: 'Email is required'
diff --git a/config/locales/sv.yml b/config/locales/sv.yml
index bf441e57c..87eb69f7d 100644
--- a/config/locales/sv.yml
+++ b/config/locales/sv.yml
@@ -26,6 +26,9 @@ sv:
messages:
reset_password_success: Woot! Begäran om återställning av lösenord lyckades. Kontrollera din e-post för instruktioner.
reset_password_failure: Oj då! Vi kunde inte hitta någon användare med den angivna e-postadressen.
+ reset_password_saml_user: This account uses SAML authentication. Password reset is not available. Please contact your administrator.
+ login_saml_user: This account uses SAML authentication. Please sign in through your organization's SAML provider.
+ saml_not_available: SAML authentication is not available in this installation.
inbox_deletetion_response: Your inbox deletion request will be processed in some time.
errors:
validations:
@@ -41,6 +44,8 @@ sv:
failed: Registrering misslyckades
assignment_policy:
not_found: Assignment policy not found
+ saml:
+ feature_not_enabled: SAML feature not enabled for this account
data_import:
data_type:
invalid: Invalid data type
@@ -84,6 +89,20 @@ sv:
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
+ mfa:
+ already_enabled: MFA is already enabled
+ not_enabled: MFA is not enabled
+ invalid_code: Invalid verification code
+ invalid_backup_code: Invalid backup code
+ invalid_token: Invalid or expired MFA token
+ invalid_credentials: Invalid credentials or verification code
+ feature_unavailable: MFA feature is not available. Please configure encryption keys.
+ profile:
+ mfa:
+ enabled: MFA enabled successfully
+ disabled: MFA disabled successfully
+ account_saml_settings:
+ invalid_certificate: must be a valid X.509 certificate in PEM format
reports:
period: Rapporteringsperiod %{since} till %{until}
utc_warning: The report generated is in UTC timezone
@@ -209,6 +228,7 @@ sv:
reply:
email:
header:
+ notifications: 'Aviseringar'
from_with_name: '%{assignee_name} from %{inbox_name} <%{from_email}>'
reply_with_name: '%{assignee_name} from %{inbox_name} '
friendly_name: '%{sender_name} from %{business_name} <%{from_email}>'
@@ -278,6 +298,26 @@ sv:
completed_tool_call: 'Completed %{function_name} tool call'
invalid_tool_call: 'Invalid tool call'
tool_not_available: 'Tool not available'
+ documents:
+ limit_exceeded: 'Document limit exceeded'
+ pdf_format_error: 'must be a PDF file'
+ pdf_size_error: 'must be less than 10MB'
+ pdf_upload_failed: 'Failed to upload PDF to OpenAI'
+ pdf_upload_success: 'PDF uploaded successfully with file_id: %{file_id}'
+ pdf_processing_failed: 'Failed to process PDF document %{document_id}: %{error}'
+ pdf_processing_success: 'Successfully processed PDF document %{document_id}'
+ faq_generation_complete: 'FAQ generation complete. Total FAQs created: %{count}'
+ using_paginated_faq: 'Using paginated FAQ generation for document %{document_id}'
+ using_standard_faq: 'Using standard FAQ generation for document %{document_id}'
+ response_creation_error: 'Error in creating response document: %{error}'
+ missing_openai_file_id: 'Document must have openai_file_id for paginated processing'
+ openai_api_error: 'OpenAI API Error: %{error}'
+ starting_paginated_faq: 'Starting paginated FAQ generation (%{pages_per_chunk} pages per chunk)'
+ stopping_faq_generation: 'Stopping processing. Reason: %{reason}'
+ paginated_faq_complete: 'Paginated generation complete. Total FAQs: %{total_faqs}, Pages processed: %{pages_processed}'
+ processing_pages: 'Processing pages %{start}-%{end} (iteration %{iteration})'
+ chunk_generated: 'Chunk generated %{chunk_faqs} FAQs. Total so far: %{total_faqs}'
+ page_processing_error: 'Error processing pages %{start}-%{end}: %{error}'
public_portal:
search:
search_placeholder: Sök efter artikel baserat på rubrik eller brödtext...
@@ -358,6 +398,8 @@ sv:
Transcript:
%{format_messages}
+ agent_capacity_policy:
+ inbox_already_assigned: 'Inbox has already been assigned to this policy'
portals:
send_instructions:
email_required: 'Email is required'
diff --git a/config/locales/ta.yml b/config/locales/ta.yml
index dbb280d06..2e63ab3e9 100644
--- a/config/locales/ta.yml
+++ b/config/locales/ta.yml
@@ -26,6 +26,9 @@ ta:
messages:
reset_password_success: வூட்! பாஸ்வேர்டை மீட்டமைப்பிற்கான கோரிக்கை வெற்றிகரமாக அனுப்பப்பட்டுள்ளது. வழிமுறைகளுக்கு உங்கள் ஈ-மெயிலைப் பார்க்கவும்.
reset_password_failure: மன்னிக்கவும்! குறிப்பிட்ட ஈ-மெயிலுடன் எந்த பயனரையும் எங்களால் கண்டுபிடிக்க முடியவில்லை.
+ reset_password_saml_user: This account uses SAML authentication. Password reset is not available. Please contact your administrator.
+ login_saml_user: This account uses SAML authentication. Please sign in through your organization's SAML provider.
+ saml_not_available: SAML authentication is not available in this installation.
inbox_deletetion_response: Your inbox deletion request will be processed in some time.
errors:
validations:
@@ -41,6 +44,8 @@ ta:
failed: உள்நுழையும் முயறிசி தோல்வி அடைந்துள்ளது
assignment_policy:
not_found: Assignment policy not found
+ saml:
+ feature_not_enabled: SAML feature not enabled for this account
data_import:
data_type:
invalid: Invalid data type
@@ -84,6 +89,20 @@ ta:
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
+ mfa:
+ already_enabled: MFA is already enabled
+ not_enabled: MFA is not enabled
+ invalid_code: Invalid verification code
+ invalid_backup_code: Invalid backup code
+ invalid_token: Invalid or expired MFA token
+ invalid_credentials: Invalid credentials or verification code
+ feature_unavailable: MFA feature is not available. Please configure encryption keys.
+ profile:
+ mfa:
+ enabled: MFA enabled successfully
+ disabled: MFA disabled successfully
+ account_saml_settings:
+ invalid_certificate: must be a valid X.509 certificate in PEM format
reports:
period: Reporting period %{since} to %{until}
utc_warning: The report generated is in UTC timezone
@@ -209,6 +228,7 @@ ta:
reply:
email:
header:
+ notifications: 'Notifications'
from_with_name: '%{assignee_name} from %{inbox_name} <%{from_email}>'
reply_with_name: '%{assignee_name} from %{inbox_name} '
friendly_name: '%{sender_name} from %{business_name} <%{from_email}>'
@@ -278,6 +298,26 @@ ta:
completed_tool_call: 'Completed %{function_name} tool call'
invalid_tool_call: 'Invalid tool call'
tool_not_available: 'Tool not available'
+ documents:
+ limit_exceeded: 'Document limit exceeded'
+ pdf_format_error: 'must be a PDF file'
+ pdf_size_error: 'must be less than 10MB'
+ pdf_upload_failed: 'Failed to upload PDF to OpenAI'
+ pdf_upload_success: 'PDF uploaded successfully with file_id: %{file_id}'
+ pdf_processing_failed: 'Failed to process PDF document %{document_id}: %{error}'
+ pdf_processing_success: 'Successfully processed PDF document %{document_id}'
+ faq_generation_complete: 'FAQ generation complete. Total FAQs created: %{count}'
+ using_paginated_faq: 'Using paginated FAQ generation for document %{document_id}'
+ using_standard_faq: 'Using standard FAQ generation for document %{document_id}'
+ response_creation_error: 'Error in creating response document: %{error}'
+ missing_openai_file_id: 'Document must have openai_file_id for paginated processing'
+ openai_api_error: 'OpenAI API Error: %{error}'
+ starting_paginated_faq: 'Starting paginated FAQ generation (%{pages_per_chunk} pages per chunk)'
+ stopping_faq_generation: 'Stopping processing. Reason: %{reason}'
+ paginated_faq_complete: 'Paginated generation complete. Total FAQs: %{total_faqs}, Pages processed: %{pages_processed}'
+ processing_pages: 'Processing pages %{start}-%{end} (iteration %{iteration})'
+ chunk_generated: 'Chunk generated %{chunk_faqs} FAQs. Total so far: %{total_faqs}'
+ page_processing_error: 'Error processing pages %{start}-%{end}: %{error}'
public_portal:
search:
search_placeholder: Search for article by title or body...
@@ -358,6 +398,8 @@ ta:
Transcript:
%{format_messages}
+ agent_capacity_policy:
+ inbox_already_assigned: 'Inbox has already been assigned to this policy'
portals:
send_instructions:
email_required: 'Email is required'
diff --git a/config/locales/th.yml b/config/locales/th.yml
index 61c99c44e..eb21fb6ea 100644
--- a/config/locales/th.yml
+++ b/config/locales/th.yml
@@ -26,6 +26,9 @@ th:
messages:
reset_password_success: Woot! Request for password reset is successful. Check your mail for instructions.
reset_password_failure: Uh ho! We could not find any user with the specified email.
+ reset_password_saml_user: This account uses SAML authentication. Password reset is not available. Please contact your administrator.
+ login_saml_user: This account uses SAML authentication. Please sign in through your organization's SAML provider.
+ saml_not_available: SAML authentication is not available in this installation.
inbox_deletetion_response: Your inbox deletion request will be processed in some time.
errors:
validations:
@@ -41,6 +44,8 @@ th:
failed: Signup failed
assignment_policy:
not_found: Assignment policy not found
+ saml:
+ feature_not_enabled: SAML feature not enabled for this account
data_import:
data_type:
invalid: Invalid data type
@@ -84,6 +89,20 @@ th:
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
+ mfa:
+ already_enabled: MFA is already enabled
+ not_enabled: MFA is not enabled
+ invalid_code: Invalid verification code
+ invalid_backup_code: Invalid backup code
+ invalid_token: Invalid or expired MFA token
+ invalid_credentials: Invalid credentials or verification code
+ feature_unavailable: MFA feature is not available. Please configure encryption keys.
+ profile:
+ mfa:
+ enabled: MFA enabled successfully
+ disabled: MFA disabled successfully
+ account_saml_settings:
+ invalid_certificate: must be a valid X.509 certificate in PEM format
reports:
period: Reporting period %{since} to %{until}
utc_warning: The report generated is in UTC timezone
@@ -209,6 +228,7 @@ th:
reply:
email:
header:
+ notifications: 'การแจ้งเตือน'
from_with_name: '%{assignee_name} from %{inbox_name} <%{from_email}>'
reply_with_name: '%{assignee_name} from %{inbox_name} '
friendly_name: '%{sender_name} from %{business_name} <%{from_email}>'
@@ -278,6 +298,26 @@ th:
completed_tool_call: 'Completed %{function_name} tool call'
invalid_tool_call: 'Invalid tool call'
tool_not_available: 'Tool not available'
+ documents:
+ limit_exceeded: 'Document limit exceeded'
+ pdf_format_error: 'must be a PDF file'
+ pdf_size_error: 'must be less than 10MB'
+ pdf_upload_failed: 'Failed to upload PDF to OpenAI'
+ pdf_upload_success: 'PDF uploaded successfully with file_id: %{file_id}'
+ pdf_processing_failed: 'Failed to process PDF document %{document_id}: %{error}'
+ pdf_processing_success: 'Successfully processed PDF document %{document_id}'
+ faq_generation_complete: 'FAQ generation complete. Total FAQs created: %{count}'
+ using_paginated_faq: 'Using paginated FAQ generation for document %{document_id}'
+ using_standard_faq: 'Using standard FAQ generation for document %{document_id}'
+ response_creation_error: 'Error in creating response document: %{error}'
+ missing_openai_file_id: 'Document must have openai_file_id for paginated processing'
+ openai_api_error: 'OpenAI API Error: %{error}'
+ starting_paginated_faq: 'Starting paginated FAQ generation (%{pages_per_chunk} pages per chunk)'
+ stopping_faq_generation: 'Stopping processing. Reason: %{reason}'
+ paginated_faq_complete: 'Paginated generation complete. Total FAQs: %{total_faqs}, Pages processed: %{pages_processed}'
+ processing_pages: 'Processing pages %{start}-%{end} (iteration %{iteration})'
+ chunk_generated: 'Chunk generated %{chunk_faqs} FAQs. Total so far: %{total_faqs}'
+ page_processing_error: 'Error processing pages %{start}-%{end}: %{error}'
public_portal:
search:
search_placeholder: Search for article by title or body...
@@ -354,6 +394,8 @@ th:
Transcript:
%{format_messages}
+ agent_capacity_policy:
+ inbox_already_assigned: 'Inbox has already been assigned to this policy'
portals:
send_instructions:
email_required: 'Email is required'
diff --git a/config/locales/tl.yml b/config/locales/tl.yml
index 4a7ac228d..e50977905 100644
--- a/config/locales/tl.yml
+++ b/config/locales/tl.yml
@@ -26,6 +26,9 @@ tl:
messages:
reset_password_success: Woot! Request for password reset is successful. Check your mail for instructions.
reset_password_failure: Uh ho! We could not find any user with the specified email.
+ reset_password_saml_user: This account uses SAML authentication. Password reset is not available. Please contact your administrator.
+ login_saml_user: This account uses SAML authentication. Please sign in through your organization's SAML provider.
+ saml_not_available: SAML authentication is not available in this installation.
inbox_deletetion_response: Your inbox deletion request will be processed in some time.
errors:
validations:
@@ -41,6 +44,8 @@ tl:
failed: Signup failed
assignment_policy:
not_found: Assignment policy not found
+ saml:
+ feature_not_enabled: SAML feature not enabled for this account
data_import:
data_type:
invalid: Invalid data type
@@ -84,6 +89,20 @@ tl:
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
+ mfa:
+ already_enabled: MFA is already enabled
+ not_enabled: MFA is not enabled
+ invalid_code: Invalid verification code
+ invalid_backup_code: Invalid backup code
+ invalid_token: Invalid or expired MFA token
+ invalid_credentials: Invalid credentials or verification code
+ feature_unavailable: MFA feature is not available. Please configure encryption keys.
+ profile:
+ mfa:
+ enabled: MFA enabled successfully
+ disabled: MFA disabled successfully
+ account_saml_settings:
+ invalid_certificate: must be a valid X.509 certificate in PEM format
reports:
period: Reporting period %{since} to %{until}
utc_warning: The report generated is in UTC timezone
@@ -209,6 +228,7 @@ tl:
reply:
email:
header:
+ notifications: 'Notifications'
from_with_name: '%{assignee_name} from %{inbox_name} <%{from_email}>'
reply_with_name: '%{assignee_name} from %{inbox_name} '
friendly_name: '%{sender_name} from %{business_name} <%{from_email}>'
@@ -278,6 +298,26 @@ tl:
completed_tool_call: 'Completed %{function_name} tool call'
invalid_tool_call: 'Invalid tool call'
tool_not_available: 'Tool not available'
+ documents:
+ limit_exceeded: 'Document limit exceeded'
+ pdf_format_error: 'must be a PDF file'
+ pdf_size_error: 'must be less than 10MB'
+ pdf_upload_failed: 'Failed to upload PDF to OpenAI'
+ pdf_upload_success: 'PDF uploaded successfully with file_id: %{file_id}'
+ pdf_processing_failed: 'Failed to process PDF document %{document_id}: %{error}'
+ pdf_processing_success: 'Successfully processed PDF document %{document_id}'
+ faq_generation_complete: 'FAQ generation complete. Total FAQs created: %{count}'
+ using_paginated_faq: 'Using paginated FAQ generation for document %{document_id}'
+ using_standard_faq: 'Using standard FAQ generation for document %{document_id}'
+ response_creation_error: 'Error in creating response document: %{error}'
+ missing_openai_file_id: 'Document must have openai_file_id for paginated processing'
+ openai_api_error: 'OpenAI API Error: %{error}'
+ starting_paginated_faq: 'Starting paginated FAQ generation (%{pages_per_chunk} pages per chunk)'
+ stopping_faq_generation: 'Stopping processing. Reason: %{reason}'
+ paginated_faq_complete: 'Paginated generation complete. Total FAQs: %{total_faqs}, Pages processed: %{pages_processed}'
+ processing_pages: 'Processing pages %{start}-%{end} (iteration %{iteration})'
+ chunk_generated: 'Chunk generated %{chunk_faqs} FAQs. Total so far: %{total_faqs}'
+ page_processing_error: 'Error processing pages %{start}-%{end}: %{error}'
public_portal:
search:
search_placeholder: Search for article by title or body...
@@ -358,6 +398,8 @@ tl:
Transcript:
%{format_messages}
+ agent_capacity_policy:
+ inbox_already_assigned: 'Inbox has already been assigned to this policy'
portals:
send_instructions:
email_required: 'Email is required'
diff --git a/config/locales/tr.yml b/config/locales/tr.yml
index fb0c26e89..f638246bc 100644
--- a/config/locales/tr.yml
+++ b/config/locales/tr.yml
@@ -26,6 +26,9 @@ tr:
messages:
reset_password_success: Parola sıfırlama isteği başarılı. Talimatlar için postanızı kontrol edin.
reset_password_failure: Belirtilen e-postaya sahip herhangi bir kullanıcı bulamadık.
+ reset_password_saml_user: This account uses SAML authentication. Password reset is not available. Please contact your administrator.
+ login_saml_user: This account uses SAML authentication. Please sign in through your organization's SAML provider.
+ saml_not_available: SAML authentication is not available in this installation.
inbox_deletetion_response: Gelen kutusu silme isteğiniz bir süre sonra işleme alınacaktır.
errors:
validations:
@@ -41,6 +44,8 @@ tr:
failed: Kayıt başarısız oldu
assignment_policy:
not_found: Assignment policy not found
+ saml:
+ feature_not_enabled: SAML feature not enabled for this account
data_import:
data_type:
invalid: Hatalı veri türü
@@ -84,6 +89,20 @@ tr:
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
+ mfa:
+ already_enabled: MFA is already enabled
+ not_enabled: MFA is not enabled
+ invalid_code: Invalid verification code
+ invalid_backup_code: Invalid backup code
+ invalid_token: Invalid or expired MFA token
+ invalid_credentials: Invalid credentials or verification code
+ feature_unavailable: MFA feature is not available. Please configure encryption keys.
+ profile:
+ mfa:
+ enabled: MFA enabled successfully
+ disabled: MFA disabled successfully
+ account_saml_settings:
+ invalid_certificate: must be a valid X.509 certificate in PEM format
reports:
period: Raporlama aralığı %{since}'dan %{until}'a
utc_warning: Oluşturulan rapor UTC zaman dilimindedir.
@@ -209,6 +228,7 @@ tr:
reply:
email:
header:
+ notifications: 'Bildirimler'
from_with_name: '"%{inbox_name} <%{from_email}> adresinden %{assignee_name}''e gönderildi,'
reply_with_name: '%{assignee_name} tarafından %{inbox_name} '
friendly_name: '%{sender_name} tarafından %{business_name} <%{from_email}>'
@@ -278,6 +298,26 @@ tr:
completed_tool_call: '%{function_name} aracı çağrısı tamamlandı'
invalid_tool_call: 'Geçersiz araç çağrısı'
tool_not_available: 'Araç mevcut değil'
+ documents:
+ limit_exceeded: 'Document limit exceeded'
+ pdf_format_error: 'must be a PDF file'
+ pdf_size_error: 'must be less than 10MB'
+ pdf_upload_failed: 'Failed to upload PDF to OpenAI'
+ pdf_upload_success: 'PDF uploaded successfully with file_id: %{file_id}'
+ pdf_processing_failed: 'Failed to process PDF document %{document_id}: %{error}'
+ pdf_processing_success: 'Successfully processed PDF document %{document_id}'
+ faq_generation_complete: 'FAQ generation complete. Total FAQs created: %{count}'
+ using_paginated_faq: 'Using paginated FAQ generation for document %{document_id}'
+ using_standard_faq: 'Using standard FAQ generation for document %{document_id}'
+ response_creation_error: 'Error in creating response document: %{error}'
+ missing_openai_file_id: 'Document must have openai_file_id for paginated processing'
+ openai_api_error: 'OpenAI API Error: %{error}'
+ starting_paginated_faq: 'Starting paginated FAQ generation (%{pages_per_chunk} pages per chunk)'
+ stopping_faq_generation: 'Stopping processing. Reason: %{reason}'
+ paginated_faq_complete: 'Paginated generation complete. Total FAQs: %{total_faqs}, Pages processed: %{pages_processed}'
+ processing_pages: 'Processing pages %{start}-%{end} (iteration %{iteration})'
+ chunk_generated: 'Chunk generated %{chunk_faqs} FAQs. Total so far: %{total_faqs}'
+ page_processing_error: 'Error processing pages %{start}-%{end}: %{error}'
public_portal:
search:
search_placeholder: Başlık veya içerikle makale arayın...
@@ -358,6 +398,8 @@ tr:
Döküm:
%{format_messages}
+ agent_capacity_policy:
+ inbox_already_assigned: 'Inbox has already been assigned to this policy'
portals:
send_instructions:
email_required: 'E-posta gereklidir'
diff --git a/config/locales/uk.yml b/config/locales/uk.yml
index 4d9a08fc3..c0001cfae 100644
--- a/config/locales/uk.yml
+++ b/config/locales/uk.yml
@@ -26,6 +26,9 @@ uk:
messages:
reset_password_success: Круто! Запит на скидання пароля виконано успішно. Перевірте вашу пошту за подальшими інструкціями.
reset_password_failure: Ой-ой! Ми не змогли знайти жодного користувача з цією адресою електронної пошти.
+ reset_password_saml_user: This account uses SAML authentication. Password reset is not available. Please contact your administrator.
+ login_saml_user: This account uses SAML authentication. Please sign in through your organization's SAML provider.
+ saml_not_available: SAML authentication is not available in this installation.
inbox_deletetion_response: Ваш запит на видалення буде оброблений протягом деякого часу.
errors:
validations:
@@ -41,6 +44,8 @@ uk:
failed: Помилка реєстрації
assignment_policy:
not_found: Assignment policy not found
+ saml:
+ feature_not_enabled: SAML feature not enabled for this account
data_import:
data_type:
invalid: Некоректний тип даних
@@ -84,6 +89,20 @@ uk:
invalid_value: Невірне значення. Надані значення для %{attribute_name} є неприпустимі
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
+ mfa:
+ already_enabled: MFA is already enabled
+ not_enabled: MFA is not enabled
+ invalid_code: Invalid verification code
+ invalid_backup_code: Invalid backup code
+ invalid_token: Invalid or expired MFA token
+ invalid_credentials: Invalid credentials or verification code
+ feature_unavailable: MFA feature is not available. Please configure encryption keys.
+ profile:
+ mfa:
+ enabled: MFA enabled successfully
+ disabled: MFA disabled successfully
+ account_saml_settings:
+ invalid_certificate: must be a valid X.509 certificate in PEM format
reports:
period: Період звіту %{since} до %{until}
utc_warning: Звіт створено в часовій зоні UTC
@@ -209,6 +228,7 @@ uk:
reply:
email:
header:
+ notifications: 'Сповіщення'
from_with_name: '%{assignee_name} з %{inbox_name} <%{from_email}>'
reply_with_name: '%{assignee_name} від %{inbox_name} '
friendly_name: '%{sender_name} з %{business_name} <%{from_email}>'
@@ -278,6 +298,26 @@ uk:
completed_tool_call: 'Completed %{function_name} tool call'
invalid_tool_call: 'Invalid tool call'
tool_not_available: 'Tool not available'
+ documents:
+ limit_exceeded: 'Document limit exceeded'
+ pdf_format_error: 'must be a PDF file'
+ pdf_size_error: 'must be less than 10MB'
+ pdf_upload_failed: 'Failed to upload PDF to OpenAI'
+ pdf_upload_success: 'PDF uploaded successfully with file_id: %{file_id}'
+ pdf_processing_failed: 'Failed to process PDF document %{document_id}: %{error}'
+ pdf_processing_success: 'Successfully processed PDF document %{document_id}'
+ faq_generation_complete: 'FAQ generation complete. Total FAQs created: %{count}'
+ using_paginated_faq: 'Using paginated FAQ generation for document %{document_id}'
+ using_standard_faq: 'Using standard FAQ generation for document %{document_id}'
+ response_creation_error: 'Error in creating response document: %{error}'
+ missing_openai_file_id: 'Document must have openai_file_id for paginated processing'
+ openai_api_error: 'OpenAI API Error: %{error}'
+ starting_paginated_faq: 'Starting paginated FAQ generation (%{pages_per_chunk} pages per chunk)'
+ stopping_faq_generation: 'Stopping processing. Reason: %{reason}'
+ paginated_faq_complete: 'Paginated generation complete. Total FAQs: %{total_faqs}, Pages processed: %{pages_processed}'
+ processing_pages: 'Processing pages %{start}-%{end} (iteration %{iteration})'
+ chunk_generated: 'Chunk generated %{chunk_faqs} FAQs. Total so far: %{total_faqs}'
+ page_processing_error: 'Error processing pages %{start}-%{end}: %{error}'
public_portal:
search:
search_placeholder: Пошук статті за заголовком або змістом...
@@ -366,6 +406,8 @@ uk:
Transcript:
%{format_messages}
+ agent_capacity_policy:
+ inbox_already_assigned: 'Inbox has already been assigned to this policy'
portals:
send_instructions:
email_required: 'Необхідно вказати електронну адресу'
diff --git a/config/locales/ur.yml b/config/locales/ur.yml
index 366c187de..ccac5d054 100644
--- a/config/locales/ur.yml
+++ b/config/locales/ur.yml
@@ -26,6 +26,9 @@ ur:
messages:
reset_password_success: Woot! Request for password reset is successful. Check your mail for instructions.
reset_password_failure: Uh ho! We could not find any user with the specified email.
+ reset_password_saml_user: This account uses SAML authentication. Password reset is not available. Please contact your administrator.
+ login_saml_user: This account uses SAML authentication. Please sign in through your organization's SAML provider.
+ saml_not_available: SAML authentication is not available in this installation.
inbox_deletetion_response: Your inbox deletion request will be processed in some time.
errors:
validations:
@@ -41,6 +44,8 @@ ur:
failed: Signup failed
assignment_policy:
not_found: Assignment policy not found
+ saml:
+ feature_not_enabled: SAML feature not enabled for this account
data_import:
data_type:
invalid: Invalid data type
@@ -84,6 +89,20 @@ ur:
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
+ mfa:
+ already_enabled: MFA is already enabled
+ not_enabled: MFA is not enabled
+ invalid_code: Invalid verification code
+ invalid_backup_code: Invalid backup code
+ invalid_token: Invalid or expired MFA token
+ invalid_credentials: Invalid credentials or verification code
+ feature_unavailable: MFA feature is not available. Please configure encryption keys.
+ profile:
+ mfa:
+ enabled: MFA enabled successfully
+ disabled: MFA disabled successfully
+ account_saml_settings:
+ invalid_certificate: must be a valid X.509 certificate in PEM format
reports:
period: Reporting period %{since} to %{until}
utc_warning: The report generated is in UTC timezone
@@ -209,6 +228,7 @@ ur:
reply:
email:
header:
+ notifications: 'Notifications'
from_with_name: '%{assignee_name} from %{inbox_name} <%{from_email}>'
reply_with_name: '%{assignee_name} from %{inbox_name} '
friendly_name: '%{sender_name} from %{business_name} <%{from_email}>'
@@ -278,6 +298,26 @@ ur:
completed_tool_call: 'Completed %{function_name} tool call'
invalid_tool_call: 'Invalid tool call'
tool_not_available: 'Tool not available'
+ documents:
+ limit_exceeded: 'Document limit exceeded'
+ pdf_format_error: 'must be a PDF file'
+ pdf_size_error: 'must be less than 10MB'
+ pdf_upload_failed: 'Failed to upload PDF to OpenAI'
+ pdf_upload_success: 'PDF uploaded successfully with file_id: %{file_id}'
+ pdf_processing_failed: 'Failed to process PDF document %{document_id}: %{error}'
+ pdf_processing_success: 'Successfully processed PDF document %{document_id}'
+ faq_generation_complete: 'FAQ generation complete. Total FAQs created: %{count}'
+ using_paginated_faq: 'Using paginated FAQ generation for document %{document_id}'
+ using_standard_faq: 'Using standard FAQ generation for document %{document_id}'
+ response_creation_error: 'Error in creating response document: %{error}'
+ missing_openai_file_id: 'Document must have openai_file_id for paginated processing'
+ openai_api_error: 'OpenAI API Error: %{error}'
+ starting_paginated_faq: 'Starting paginated FAQ generation (%{pages_per_chunk} pages per chunk)'
+ stopping_faq_generation: 'Stopping processing. Reason: %{reason}'
+ paginated_faq_complete: 'Paginated generation complete. Total FAQs: %{total_faqs}, Pages processed: %{pages_processed}'
+ processing_pages: 'Processing pages %{start}-%{end} (iteration %{iteration})'
+ chunk_generated: 'Chunk generated %{chunk_faqs} FAQs. Total so far: %{total_faqs}'
+ page_processing_error: 'Error processing pages %{start}-%{end}: %{error}'
public_portal:
search:
search_placeholder: Search for article by title or body...
@@ -358,6 +398,8 @@ ur:
Transcript:
%{format_messages}
+ agent_capacity_policy:
+ inbox_already_assigned: 'Inbox has already been assigned to this policy'
portals:
send_instructions:
email_required: 'Email is required'
diff --git a/config/locales/ur_IN.yml b/config/locales/ur_IN.yml
index b6cc72296..7ab544044 100644
--- a/config/locales/ur_IN.yml
+++ b/config/locales/ur_IN.yml
@@ -26,6 +26,9 @@ ur:
messages:
reset_password_success: Woot! Request for password reset is successful. Check your mail for instructions.
reset_password_failure: Uh ho! We could not find any user with the specified email.
+ reset_password_saml_user: This account uses SAML authentication. Password reset is not available. Please contact your administrator.
+ login_saml_user: This account uses SAML authentication. Please sign in through your organization's SAML provider.
+ saml_not_available: SAML authentication is not available in this installation.
inbox_deletetion_response: Your inbox deletion request will be processed in some time.
errors:
validations:
@@ -41,6 +44,8 @@ ur:
failed: Signup failed
assignment_policy:
not_found: Assignment policy not found
+ saml:
+ feature_not_enabled: SAML feature not enabled for this account
data_import:
data_type:
invalid: Invalid data type
@@ -84,6 +89,20 @@ ur:
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
+ mfa:
+ already_enabled: MFA is already enabled
+ not_enabled: MFA is not enabled
+ invalid_code: Invalid verification code
+ invalid_backup_code: Invalid backup code
+ invalid_token: Invalid or expired MFA token
+ invalid_credentials: Invalid credentials or verification code
+ feature_unavailable: MFA feature is not available. Please configure encryption keys.
+ profile:
+ mfa:
+ enabled: MFA enabled successfully
+ disabled: MFA disabled successfully
+ account_saml_settings:
+ invalid_certificate: must be a valid X.509 certificate in PEM format
reports:
period: Reporting period %{since} to %{until}
utc_warning: The report generated is in UTC timezone
@@ -209,6 +228,7 @@ ur:
reply:
email:
header:
+ notifications: 'Notifications'
from_with_name: '%{assignee_name} from %{inbox_name} <%{from_email}>'
reply_with_name: '%{assignee_name} from %{inbox_name} '
friendly_name: '%{sender_name} from %{business_name} <%{from_email}>'
@@ -278,6 +298,26 @@ ur:
completed_tool_call: 'Completed %{function_name} tool call'
invalid_tool_call: 'Invalid tool call'
tool_not_available: 'Tool not available'
+ documents:
+ limit_exceeded: 'Document limit exceeded'
+ pdf_format_error: 'must be a PDF file'
+ pdf_size_error: 'must be less than 10MB'
+ pdf_upload_failed: 'Failed to upload PDF to OpenAI'
+ pdf_upload_success: 'PDF uploaded successfully with file_id: %{file_id}'
+ pdf_processing_failed: 'Failed to process PDF document %{document_id}: %{error}'
+ pdf_processing_success: 'Successfully processed PDF document %{document_id}'
+ faq_generation_complete: 'FAQ generation complete. Total FAQs created: %{count}'
+ using_paginated_faq: 'Using paginated FAQ generation for document %{document_id}'
+ using_standard_faq: 'Using standard FAQ generation for document %{document_id}'
+ response_creation_error: 'Error in creating response document: %{error}'
+ missing_openai_file_id: 'Document must have openai_file_id for paginated processing'
+ openai_api_error: 'OpenAI API Error: %{error}'
+ starting_paginated_faq: 'Starting paginated FAQ generation (%{pages_per_chunk} pages per chunk)'
+ stopping_faq_generation: 'Stopping processing. Reason: %{reason}'
+ paginated_faq_complete: 'Paginated generation complete. Total FAQs: %{total_faqs}, Pages processed: %{pages_processed}'
+ processing_pages: 'Processing pages %{start}-%{end} (iteration %{iteration})'
+ chunk_generated: 'Chunk generated %{chunk_faqs} FAQs. Total so far: %{total_faqs}'
+ page_processing_error: 'Error processing pages %{start}-%{end}: %{error}'
public_portal:
search:
search_placeholder: Search for article by title or body...
@@ -358,6 +398,8 @@ ur:
Transcript:
%{format_messages}
+ agent_capacity_policy:
+ inbox_already_assigned: 'Inbox has already been assigned to this policy'
portals:
send_instructions:
email_required: 'Email is required'
diff --git a/config/locales/vi.yml b/config/locales/vi.yml
index 31524c6d2..f4f23de4d 100644
--- a/config/locales/vi.yml
+++ b/config/locales/vi.yml
@@ -26,6 +26,9 @@ vi:
messages:
reset_password_success: Chà! Yêu cầu đặt lại mật khẩu thành công. Kiểm tra thư của bạn để biết hướng dẫn.
reset_password_failure: Uh ho! Chúng tôi không thể tìm thấy bất kỳ người dùng nào có email được chỉ định.
+ reset_password_saml_user: This account uses SAML authentication. Password reset is not available. Please contact your administrator.
+ login_saml_user: This account uses SAML authentication. Please sign in through your organization's SAML provider.
+ saml_not_available: SAML authentication is not available in this installation.
inbox_deletetion_response: Yêu cầu xoá hộp thư của bạn sẽ được xử lý.
errors:
validations:
@@ -41,6 +44,8 @@ vi:
failed: Đăng ký thât bại
assignment_policy:
not_found: Assignment policy not found
+ saml:
+ feature_not_enabled: SAML feature not enabled for this account
data_import:
data_type:
invalid: Kiểu dữ liệu không hợp lệ
@@ -84,6 +89,20 @@ vi:
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
+ mfa:
+ already_enabled: MFA is already enabled
+ not_enabled: MFA is not enabled
+ invalid_code: Invalid verification code
+ invalid_backup_code: Invalid backup code
+ invalid_token: Invalid or expired MFA token
+ invalid_credentials: Invalid credentials or verification code
+ feature_unavailable: MFA feature is not available. Please configure encryption keys.
+ profile:
+ mfa:
+ enabled: MFA enabled successfully
+ disabled: MFA disabled successfully
+ account_saml_settings:
+ invalid_certificate: must be a valid X.509 certificate in PEM format
reports:
period: Thời gian báo cáo từ %{since} đến %{until}
utc_warning: Báo cáo đã được tạo với múi giờ UTC
@@ -209,6 +228,7 @@ vi:
reply:
email:
header:
+ notifications: 'Thông báo'
from_with_name: '%{assignee_name} từ %{inbox_name} <%{from_email}>'
reply_with_name: '%{assignee_name} từ %{inbox_name} '
friendly_name: '%{sender_name} từ %{business_name} <%{from_email}>'
@@ -278,6 +298,26 @@ vi:
completed_tool_call: 'Completed %{function_name} tool call'
invalid_tool_call: 'Invalid tool call'
tool_not_available: 'Tool not available'
+ documents:
+ limit_exceeded: 'Document limit exceeded'
+ pdf_format_error: 'must be a PDF file'
+ pdf_size_error: 'must be less than 10MB'
+ pdf_upload_failed: 'Failed to upload PDF to OpenAI'
+ pdf_upload_success: 'PDF uploaded successfully with file_id: %{file_id}'
+ pdf_processing_failed: 'Failed to process PDF document %{document_id}: %{error}'
+ pdf_processing_success: 'Successfully processed PDF document %{document_id}'
+ faq_generation_complete: 'FAQ generation complete. Total FAQs created: %{count}'
+ using_paginated_faq: 'Using paginated FAQ generation for document %{document_id}'
+ using_standard_faq: 'Using standard FAQ generation for document %{document_id}'
+ response_creation_error: 'Error in creating response document: %{error}'
+ missing_openai_file_id: 'Document must have openai_file_id for paginated processing'
+ openai_api_error: 'OpenAI API Error: %{error}'
+ starting_paginated_faq: 'Starting paginated FAQ generation (%{pages_per_chunk} pages per chunk)'
+ stopping_faq_generation: 'Stopping processing. Reason: %{reason}'
+ paginated_faq_complete: 'Paginated generation complete. Total FAQs: %{total_faqs}, Pages processed: %{pages_processed}'
+ processing_pages: 'Processing pages %{start}-%{end} (iteration %{iteration})'
+ chunk_generated: 'Chunk generated %{chunk_faqs} FAQs. Total so far: %{total_faqs}'
+ page_processing_error: 'Error processing pages %{start}-%{end}: %{error}'
public_portal:
search:
search_placeholder: Tìm bài viết theo tiêu đề hoặc nội dung...
@@ -354,6 +394,8 @@ vi:
Transcript:
%{format_messages}
+ agent_capacity_policy:
+ inbox_already_assigned: 'Inbox has already been assigned to this policy'
portals:
send_instructions:
email_required: 'Email bắt buộc có'
diff --git a/config/locales/zh_CN.yml b/config/locales/zh_CN.yml
index 6cf6e7aea..ee3ce7d46 100644
--- a/config/locales/zh_CN.yml
+++ b/config/locales/zh_CN.yml
@@ -26,6 +26,9 @@ zh_CN:
messages:
reset_password_success: 哇!密码重置请求成功。请检查您的邮件获取说明。
reset_password_failure: 哎呀!我们找不到指定电子邮件的任何用户。
+ reset_password_saml_user: This account uses SAML authentication. Password reset is not available. Please contact your administrator.
+ login_saml_user: This account uses SAML authentication. Please sign in through your organization's SAML provider.
+ saml_not_available: SAML authentication is not available in this installation.
inbox_deletetion_response: 您的收件箱删除请求将在一段时间内处理。
errors:
validations:
@@ -41,6 +44,8 @@ zh_CN:
failed: 注册失败
assignment_policy:
not_found: Assignment policy not found
+ saml:
+ feature_not_enabled: SAML feature not enabled for this account
data_import:
data_type:
invalid: 错误的数据类型
@@ -84,6 +89,20 @@ zh_CN:
invalid_value: 无效的值。为 %{attribute_name} 提供的值无效
custom_attribute_definition:
key_conflict: 提供的键不允许使用,因为它可能与默认属性冲突。
+ mfa:
+ already_enabled: MFA 已启用
+ not_enabled: MFA is not enabled
+ invalid_code: Invalid verification code
+ invalid_backup_code: Invalid backup code
+ invalid_token: Invalid or expired MFA token
+ invalid_credentials: Invalid credentials or verification code
+ feature_unavailable: MFA feature is not available. Please configure encryption keys.
+ profile:
+ mfa:
+ enabled: MFA enabled successfully
+ disabled: MFA disabled successfully
+ account_saml_settings:
+ invalid_certificate: must be a valid X.509 certificate in PEM format
reports:
period: 报告周期 %{since} 至 %{until}
utc_warning: 生成的报表在 UTC 时区
@@ -209,6 +228,7 @@ zh_CN:
reply:
email:
header:
+ notifications: '消息通知'
from_with_name: '%{assignee_name} 来自 %{inbox_name} <%{from_email}>'
reply_with_name: '%{assignee_name} 来自 %{inbox_name} '
friendly_name: '%{sender_name} 来自 %{business_name} <%{from_email}>'
@@ -278,6 +298,26 @@ zh_CN:
completed_tool_call: '%{function_name} 工具调用完成'
invalid_tool_call: '无效的工具调用'
tool_not_available: '工具不可用'
+ documents:
+ limit_exceeded: 'Document limit exceeded'
+ pdf_format_error: 'must be a PDF file'
+ pdf_size_error: 'must be less than 10MB'
+ pdf_upload_failed: 'Failed to upload PDF to OpenAI'
+ pdf_upload_success: 'PDF uploaded successfully with file_id: %{file_id}'
+ pdf_processing_failed: 'Failed to process PDF document %{document_id}: %{error}'
+ pdf_processing_success: 'Successfully processed PDF document %{document_id}'
+ faq_generation_complete: 'FAQ generation complete. Total FAQs created: %{count}'
+ using_paginated_faq: 'Using paginated FAQ generation for document %{document_id}'
+ using_standard_faq: 'Using standard FAQ generation for document %{document_id}'
+ response_creation_error: 'Error in creating response document: %{error}'
+ missing_openai_file_id: 'Document must have openai_file_id for paginated processing'
+ openai_api_error: 'OpenAI API Error: %{error}'
+ starting_paginated_faq: 'Starting paginated FAQ generation (%{pages_per_chunk} pages per chunk)'
+ stopping_faq_generation: 'Stopping processing. Reason: %{reason}'
+ paginated_faq_complete: 'Paginated generation complete. Total FAQs: %{total_faqs}, Pages processed: %{pages_processed}'
+ processing_pages: 'Processing pages %{start}-%{end} (iteration %{iteration})'
+ chunk_generated: 'Chunk generated %{chunk_faqs} FAQs. Total so far: %{total_faqs}'
+ page_processing_error: 'Error processing pages %{start}-%{end}: %{error}'
public_portal:
search:
search_placeholder: 搜索文章的标题或正文...
@@ -354,6 +394,8 @@ zh_CN:
副本:
%{format_messages}
+ agent_capacity_policy:
+ inbox_already_assigned: 'Inbox has already been assigned to this policy'
portals:
send_instructions:
email_required: 'Email 是必填项'
diff --git a/config/locales/zh_TW.yml b/config/locales/zh_TW.yml
index 636b5231f..a75d8e6ba 100644
--- a/config/locales/zh_TW.yml
+++ b/config/locales/zh_TW.yml
@@ -26,6 +26,9 @@ zh_TW:
messages:
reset_password_success: 密碼重設成功,請確認您的信箱有收到重設信件。
reset_password_failure: 我們找不到用戶指定的電子郵件。
+ reset_password_saml_user: This account uses SAML authentication. Password reset is not available. Please contact your administrator.
+ login_saml_user: This account uses SAML authentication. Please sign in through your organization's SAML provider.
+ saml_not_available: SAML authentication is not available in this installation.
inbox_deletetion_response: 您的收件匣刪除請求將在一段時間後處理。
errors:
validations:
@@ -41,6 +44,8 @@ zh_TW:
failed: 註冊失敗。
assignment_policy:
not_found: Assignment policy not found
+ saml:
+ feature_not_enabled: SAML feature not enabled for this account
data_import:
data_type:
invalid: Invalid data type
@@ -84,6 +89,20 @@ zh_TW:
invalid_value: Invalid value. The values provided for %{attribute_name} are invalid
custom_attribute_definition:
key_conflict: The provided key is not allowed as it might conflict with default attributes.
+ mfa:
+ already_enabled: MFA is already enabled
+ not_enabled: MFA is not enabled
+ invalid_code: Invalid verification code
+ invalid_backup_code: Invalid backup code
+ invalid_token: Invalid or expired MFA token
+ invalid_credentials: Invalid credentials or verification code
+ feature_unavailable: MFA feature is not available. Please configure encryption keys.
+ profile:
+ mfa:
+ enabled: MFA enabled successfully
+ disabled: MFA disabled successfully
+ account_saml_settings:
+ invalid_certificate: must be a valid X.509 certificate in PEM format
reports:
period: Reporting period %{since} to %{until}
utc_warning: The report generated is in UTC timezone
@@ -209,6 +228,7 @@ zh_TW:
reply:
email:
header:
+ notifications: '通知'
from_with_name: '%{assignee_name} from %{inbox_name} <%{from_email}>'
reply_with_name: '%{assignee_name} from %{inbox_name} '
friendly_name: '%{sender_name} from %{business_name} <%{from_email}>'
@@ -278,6 +298,26 @@ zh_TW:
completed_tool_call: 'Completed %{function_name} tool call'
invalid_tool_call: 'Invalid tool call'
tool_not_available: 'Tool not available'
+ documents:
+ limit_exceeded: 'Document limit exceeded'
+ pdf_format_error: 'must be a PDF file'
+ pdf_size_error: 'must be less than 10MB'
+ pdf_upload_failed: 'Failed to upload PDF to OpenAI'
+ pdf_upload_success: 'PDF uploaded successfully with file_id: %{file_id}'
+ pdf_processing_failed: 'Failed to process PDF document %{document_id}: %{error}'
+ pdf_processing_success: 'Successfully processed PDF document %{document_id}'
+ faq_generation_complete: 'FAQ generation complete. Total FAQs created: %{count}'
+ using_paginated_faq: 'Using paginated FAQ generation for document %{document_id}'
+ using_standard_faq: 'Using standard FAQ generation for document %{document_id}'
+ response_creation_error: 'Error in creating response document: %{error}'
+ missing_openai_file_id: 'Document must have openai_file_id for paginated processing'
+ openai_api_error: 'OpenAI API Error: %{error}'
+ starting_paginated_faq: 'Starting paginated FAQ generation (%{pages_per_chunk} pages per chunk)'
+ stopping_faq_generation: 'Stopping processing. Reason: %{reason}'
+ paginated_faq_complete: 'Paginated generation complete. Total FAQs: %{total_faqs}, Pages processed: %{pages_processed}'
+ processing_pages: 'Processing pages %{start}-%{end} (iteration %{iteration})'
+ chunk_generated: 'Chunk generated %{chunk_faqs} FAQs. Total so far: %{total_faqs}'
+ page_processing_error: 'Error processing pages %{start}-%{end}: %{error}'
public_portal:
search:
search_placeholder: Search for article by title or body...
@@ -354,6 +394,8 @@ zh_TW:
Transcript:
%{format_messages}
+ agent_capacity_policy:
+ inbox_already_assigned: 'Inbox has already been assigned to this policy'
portals:
send_instructions:
email_required: 'Email is required'
diff --git a/config/routes.rb b/config/routes.rb
index 67ded50cb..6a484b380 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -69,6 +69,7 @@ Rails.application.routes.draw do
end
resources :documents, only: [:index, :show, :create, :destroy]
end
+ resource :saml_settings, only: [:show, :create, :update, :destroy]
resources :agent_bots, only: [:index, :create, :show, :update, :destroy] do
delete :avatar, on: :member
post :reset_access_token, on: :member
@@ -324,6 +325,9 @@ Rails.application.routes.draw do
resources :webhooks, only: [:create]
end
+ # Frontend API endpoint to trigger SAML authentication flow
+ post 'auth/saml_login', to: 'auth#saml_login'
+
resource :profile, only: [:show, :update] do
delete :avatar, on: :collection
member do
@@ -333,6 +337,14 @@ Rails.application.routes.draw do
post :resend_confirmation
post :reset_access_token
end
+
+ # MFA routes
+ scope module: 'profile' do
+ resource :mfa, controller: 'mfa', only: [:show, :create, :destroy] do
+ post :verify
+ post :backup_codes
+ end
+ end
end
resource :notification_subscriptions, only: [:create, :destroy]
@@ -523,6 +535,15 @@ Rails.application.routes.draw do
namespace :twilio do
resources :callback, only: [:create]
resources :delivery_status, only: [:create]
+
+ if ChatwootApp.enterprise?
+ resource :voice, only: [], controller: 'voice' do
+ collection do
+ post 'call/:phone', action: :call_twiml
+ post 'status/:phone', action: :status
+ end
+ end
+ end
end
get 'microsoft/callback', to: 'microsoft/callbacks#show'
diff --git a/db/migrate/20250820130619_add_two_factor_to_users.rb b/db/migrate/20250820130619_add_two_factor_to_users.rb
new file mode 100644
index 000000000..3178aae05
--- /dev/null
+++ b/db/migrate/20250820130619_add_two_factor_to_users.rb
@@ -0,0 +1,11 @@
+class AddTwoFactorToUsers < ActiveRecord::Migration[7.1]
+ def change
+ add_column :users, :otp_secret, :string
+ add_column :users, :consumed_timestep, :integer
+ add_column :users, :otp_required_for_login, :boolean, default: false, null: false
+ add_column :users, :otp_backup_codes, :text
+
+ add_index :users, :otp_secret, unique: true
+ add_index :users, :otp_required_for_login
+ end
+end
diff --git a/db/migrate/20250825070005_create_account_saml_settings.rb b/db/migrate/20250825070005_create_account_saml_settings.rb
new file mode 100644
index 000000000..7a0937fbf
--- /dev/null
+++ b/db/migrate/20250825070005_create_account_saml_settings.rb
@@ -0,0 +1,14 @@
+class CreateAccountSamlSettings < ActiveRecord::Migration[7.1]
+ def change
+ create_table :account_saml_settings do |t|
+ t.references :account, null: false
+ t.string :sso_url
+ t.text :certificate
+ t.string :sp_entity_id
+ t.string :idp_entity_id
+ t.json :role_mappings, default: {}
+
+ t.timestamps
+ end
+ end
+end
diff --git a/db/migrate/20250826000000_drop_telegram_bots.rb b/db/migrate/20250826000000_drop_telegram_bots.rb
new file mode 100644
index 000000000..5625e41e9
--- /dev/null
+++ b/db/migrate/20250826000000_drop_telegram_bots.rb
@@ -0,0 +1,10 @@
+class DropTelegramBots < ActiveRecord::Migration[7.1]
+ def change
+ drop_table :telegram_bots do |t|
+ t.string :name
+ t.string :auth_key
+ t.integer :account_id
+ t.timestamps
+ end
+ end
+end
diff --git a/db/migrate/20250916024703_add_allowed_domains_to_channel_widgets.rb b/db/migrate/20250916024703_add_allowed_domains_to_channel_widgets.rb
new file mode 100644
index 000000000..b0df78df6
--- /dev/null
+++ b/db/migrate/20250916024703_add_allowed_domains_to_channel_widgets.rb
@@ -0,0 +1,5 @@
+class AddAllowedDomainsToChannelWidgets < ActiveRecord::Migration[7.1]
+ def change
+ add_column :channel_web_widgets, :allowed_domains, :text, default: ''
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index f48ff6707..00b6f9109 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema[7.1].define(version: 2025_08_22_061042) do
+ActiveRecord::Schema[7.1].define(version: 2025_09_16_024703) do
# These extensions should be enabled to support this database
enable_extension "pg_stat_statements"
enable_extension "pg_trgm"
@@ -28,6 +28,18 @@ ActiveRecord::Schema[7.1].define(version: 2025_08_22_061042) do
t.index ["token"], name: "index_access_tokens_on_token", unique: true
end
+ create_table "account_saml_settings", force: :cascade do |t|
+ t.bigint "account_id", null: false
+ t.string "sso_url"
+ t.text "certificate"
+ t.string "sp_entity_id"
+ t.string "idp_entity_id"
+ t.json "role_mappings", default: {}
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ t.index ["account_id"], name: "index_account_saml_settings_on_account_id"
+ end
+
create_table "account_users", force: :cascade do |t|
t.bigint "account_id"
t.bigint "user_id"
@@ -521,6 +533,7 @@ ActiveRecord::Schema[7.1].define(version: 2025_08_22_061042) do
t.jsonb "pre_chat_form_options", default: {}
t.boolean "hmac_mandatory", default: false
t.boolean "continuity_via_email", default: true, null: false
+ t.text "allowed_domains", default: ""
t.index ["hmac_token"], name: "index_channel_web_widgets_on_hmac_token", unique: true
t.index ["website_token"], name: "index_channel_web_widgets_on_website_token", unique: true
end
@@ -1134,14 +1147,6 @@ ActiveRecord::Schema[7.1].define(version: 2025_08_22_061042) do
t.index ["name", "account_id"], name: "index_teams_on_name_and_account_id", unique: true
end
- create_table "telegram_bots", id: :serial, force: :cascade do |t|
- t.string "name"
- t.string "auth_key"
- t.integer "account_id"
- t.datetime "created_at", precision: nil, null: false
- t.datetime "updated_at", precision: nil, null: false
- end
-
create_table "users", id: :serial, force: :cascade do |t|
t.string "provider", default: "email", null: false
t.string "uid", default: "", null: false
@@ -1170,7 +1175,13 @@ ActiveRecord::Schema[7.1].define(version: 2025_08_22_061042) do
t.jsonb "custom_attributes", default: {}
t.string "type"
t.text "message_signature"
+ t.string "otp_secret"
+ t.integer "consumed_timestep"
+ t.boolean "otp_required_for_login", default: false
+ t.text "otp_backup_codes"
t.index ["email"], name: "index_users_on_email"
+ t.index ["otp_required_for_login"], name: "index_users_on_otp_required_for_login"
+ t.index ["otp_secret"], name: "index_users_on_otp_secret", unique: true
t.index ["pubsub_token"], name: "index_users_on_pubsub_token", unique: true
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
t.index ["uid", "provider"], name: "index_users_on_uid_and_provider", unique: true
diff --git a/enterprise/app/builders/enterprise/agent_builder.rb b/enterprise/app/builders/enterprise/agent_builder.rb
new file mode 100644
index 000000000..3007dbb61
--- /dev/null
+++ b/enterprise/app/builders/enterprise/agent_builder.rb
@@ -0,0 +1,13 @@
+module Enterprise::AgentBuilder
+ def perform
+ super.tap do |user|
+ convert_to_saml_provider(user) if user.persisted? && account.saml_enabled?
+ end
+ end
+
+ private
+
+ def convert_to_saml_provider(user)
+ user.update!(provider: 'saml') unless user.provider == 'saml'
+ end
+end
diff --git a/enterprise/app/builders/saml_user_builder.rb b/enterprise/app/builders/saml_user_builder.rb
new file mode 100644
index 000000000..97a287462
--- /dev/null
+++ b/enterprise/app/builders/saml_user_builder.rb
@@ -0,0 +1,109 @@
+class SamlUserBuilder
+ def initialize(auth_hash, account_id)
+ @auth_hash = auth_hash
+ @account_id = account_id
+ @saml_settings = AccountSamlSettings.find_by(account_id: account_id)
+ end
+
+ def perform
+ @user = find_or_create_user
+ add_user_to_account if @user.persisted?
+ @user
+ end
+
+ private
+
+ def find_or_create_user
+ user = User.from_email(auth_attribute('email'))
+
+ if user
+ confirm_user_if_required(user)
+ convert_existing_user_to_saml(user)
+ return user
+ end
+
+ create_user
+ end
+
+ def confirm_user_if_required(user)
+ return if user.confirmed?
+
+ user.skip_confirmation!
+ user.save!
+ end
+
+ def convert_existing_user_to_saml(user)
+ return if user.provider == 'saml'
+
+ user.update!(provider: 'saml')
+ end
+
+ def create_user
+ full_name = [auth_attribute('first_name'), auth_attribute('last_name')].compact.join(' ')
+ fallback_name = auth_attribute('name') || auth_attribute('email').split('@').first
+
+ User.create(
+ email: auth_attribute('email'),
+ name: (full_name.presence || fallback_name),
+ display_name: auth_attribute('first_name'),
+ provider: 'saml',
+ uid: uid,
+ password: SecureRandom.hex(32),
+ confirmed_at: Time.current
+ )
+ end
+
+ def add_user_to_account
+ account = Account.find_by(id: @account_id)
+ return unless account
+
+ # Create account_user if not exists
+ account_user = AccountUser.find_or_create_by(
+ user: @user,
+ account: account
+ )
+
+ # Set default role as agent if not set
+ account_user.update(role: 'agent') if account_user.role.blank?
+
+ # Handle role mappings if configured
+ apply_role_mappings(account_user, account)
+ end
+
+ def apply_role_mappings(account_user, account)
+ matching_mapping = find_matching_role_mapping(account)
+ return unless matching_mapping
+
+ if matching_mapping['role']
+ account_user.update(role: matching_mapping['role'])
+ elsif matching_mapping['custom_role_id']
+ account_user.update(custom_role_id: matching_mapping['custom_role_id'])
+ end
+ end
+
+ def find_matching_role_mapping(_account)
+ return if @saml_settings&.role_mappings.blank?
+
+ saml_groups.each do |group|
+ mapping = @saml_settings.role_mappings[group]
+ return mapping if mapping.present?
+ end
+ nil
+ end
+
+ def auth_attribute(key, fallback = nil)
+ @auth_hash.dig('info', key) || fallback
+ end
+
+ def uid
+ @auth_hash['uid']
+ end
+
+ def saml_groups
+ # Groups can come from different attributes depending on IdP
+ @auth_hash.dig('extra', 'raw_info', 'groups') ||
+ @auth_hash.dig('extra', 'raw_info', 'Group') ||
+ @auth_hash.dig('extra', 'raw_info', 'memberOf') ||
+ []
+ end
+end
diff --git a/enterprise/app/controllers/api/v1/accounts/agent_capacity_policies/users_controller.rb b/enterprise/app/controllers/api/v1/accounts/agent_capacity_policies/users_controller.rb
index a49b4f00f..f8b085732 100644
--- a/enterprise/app/controllers/api/v1/accounts/agent_capacity_policies/users_controller.rb
+++ b/enterprise/app/controllers/api/v1/accounts/agent_capacity_policies/users_controller.rb
@@ -4,8 +4,8 @@ class Api::V1::Accounts::AgentCapacityPolicies::UsersController < Api::V1::Accou
before_action :fetch_user, only: [:destroy]
def index
- @users = Current.account.users.joins(:account_users)
- .where(account_users: { agent_capacity_policy_id: @agent_capacity_policy.id })
+ @users = User.joins(:account_users)
+ .where(account_users: { account_id: Current.account.id, agent_capacity_policy_id: @agent_capacity_policy.id })
end
def create
diff --git a/enterprise/app/controllers/api/v1/accounts/agent_capacity_policies_controller.rb b/enterprise/app/controllers/api/v1/accounts/agent_capacity_policies_controller.rb
index d6d166ee5..9ece78762 100644
--- a/enterprise/app/controllers/api/v1/accounts/agent_capacity_policies_controller.rb
+++ b/enterprise/app/controllers/api/v1/accounts/agent_capacity_policies_controller.rb
@@ -27,7 +27,7 @@ class Api::V1::Accounts::AgentCapacityPoliciesController < Api::V1::Accounts::En
params.require(:agent_capacity_policy).permit(
:name,
:description,
- exclusion_rules: [:overall_capacity, { hours: [], days: [] }]
+ exclusion_rules: [:exclude_older_than_hours, { excluded_labels: [] }]
)
end
diff --git a/enterprise/app/controllers/api/v1/accounts/captain/assistant_responses_controller.rb b/enterprise/app/controllers/api/v1/accounts/captain/assistant_responses_controller.rb
index c7b0366dd..151cf279c 100644
--- a/enterprise/app/controllers/api/v1/accounts/captain/assistant_responses_controller.rb
+++ b/enterprise/app/controllers/api/v1/accounts/captain/assistant_responses_controller.rb
@@ -10,21 +10,9 @@ class Api::V1::Accounts::Captain::AssistantResponsesController < Api::V1::Accoun
RESULTS_PER_PAGE = 25
def index
- base_query = @responses
- base_query = base_query.where(assistant_id: permitted_params[:assistant_id]) if permitted_params[:assistant_id].present?
-
- if permitted_params[:document_id].present?
- base_query = base_query.where(
- documentable_id: permitted_params[:document_id],
- documentable_type: 'Captain::Document'
- )
- end
-
- base_query = base_query.where(status: permitted_params[:status]) if permitted_params[:status].present?
-
- @responses_count = base_query.count
-
- @responses = base_query.page(@current_page).per(RESULTS_PER_PAGE)
+ filtered_query = apply_filters(@responses)
+ @responses_count = filtered_query.count
+ @responses = filtered_query.page(@current_page).per(RESULTS_PER_PAGE)
end
def show; end
@@ -46,6 +34,29 @@ class Api::V1::Accounts::Captain::AssistantResponsesController < Api::V1::Accoun
private
+ def apply_filters(base_query)
+ base_query = base_query.where(assistant_id: permitted_params[:assistant_id]) if permitted_params[:assistant_id].present?
+
+ if permitted_params[:document_id].present?
+ base_query = base_query.where(
+ documentable_id: permitted_params[:document_id],
+ documentable_type: 'Captain::Document'
+ )
+ end
+
+ base_query = base_query.where(status: permitted_params[:status]) if permitted_params[:status].present?
+
+ if permitted_params[:search].present?
+ search_term = "%#{permitted_params[:search]}%"
+ base_query = base_query.where(
+ 'question ILIKE :search OR answer ILIKE :search',
+ search: search_term
+ )
+ end
+
+ base_query
+ end
+
def set_assistant
@assistant = Current.account.captain_assistants.find_by(id: params[:assistant_id])
end
@@ -63,7 +74,7 @@ class Api::V1::Accounts::Captain::AssistantResponsesController < Api::V1::Accoun
end
def permitted_params
- params.permit(:id, :assistant_id, :page, :document_id, :account_id, :status)
+ params.permit(:id, :assistant_id, :page, :document_id, :account_id, :status, :search)
end
def response_params
diff --git a/enterprise/app/controllers/api/v1/accounts/saml_settings_controller.rb b/enterprise/app/controllers/api/v1/accounts/saml_settings_controller.rb
new file mode 100644
index 000000000..9f00138cb
--- /dev/null
+++ b/enterprise/app/controllers/api/v1/accounts/saml_settings_controller.rb
@@ -0,0 +1,56 @@
+class Api::V1::Accounts::SamlSettingsController < Api::V1::Accounts::BaseController
+ before_action :check_saml_feature_enabled
+ before_action :check_authorization
+ before_action :set_saml_settings
+
+ def show; end
+
+ def create
+ @saml_settings = Current.account.build_saml_settings(saml_settings_params)
+ if @saml_settings.save
+ render :show
+ else
+ render json: { errors: @saml_settings.errors.full_messages }, status: :unprocessable_entity
+ end
+ end
+
+ def update
+ if @saml_settings.update(saml_settings_params)
+ render :show
+ else
+ render json: { errors: @saml_settings.errors.full_messages }, status: :unprocessable_entity
+ end
+ end
+
+ def destroy
+ @saml_settings.destroy!
+ head :no_content
+ end
+
+ private
+
+ def set_saml_settings
+ @saml_settings = Current.account.saml_settings ||
+ Current.account.build_saml_settings
+ end
+
+ def saml_settings_params
+ params.require(:saml_settings).permit(
+ :sso_url,
+ :certificate,
+ :idp_entity_id,
+ :sp_entity_id,
+ role_mappings: {}
+ )
+ end
+
+ def check_authorization
+ authorize(AccountSamlSettings)
+ end
+
+ def check_saml_feature_enabled
+ return if Current.account.feature_enabled?('saml')
+
+ render json: { error: I18n.t('errors.saml.feature_not_enabled') }, status: :forbidden
+ end
+end
diff --git a/enterprise/app/controllers/api/v1/auth_controller.rb b/enterprise/app/controllers/api/v1/auth_controller.rb
new file mode 100644
index 000000000..a8eb7ad9d
--- /dev/null
+++ b/enterprise/app/controllers/api/v1/auth_controller.rb
@@ -0,0 +1,49 @@
+class Api::V1::AuthController < Api::BaseController
+ skip_before_action :authenticate_user!, only: [:saml_login]
+ before_action :find_user_and_account, only: [:saml_login]
+
+ def saml_login
+ return if @account.nil?
+
+ saml_initiation_url = "/auth/saml?account_id=#{@account.id}"
+ redirect_to saml_initiation_url, status: :temporary_redirect
+ end
+
+ private
+
+ def find_user_and_account
+ return unless validate_email_presence
+
+ find_saml_enabled_account
+ end
+
+ def validate_email_presence
+ @email = params[:email]&.downcase&.strip
+ return true if @email.present?
+
+ render json: { error: I18n.t('auth.saml.invalid_email') }, status: :bad_request
+ false
+ end
+
+ def find_saml_enabled_account
+ user = User.from_email(@email)
+ return render_saml_error unless user
+
+ account_user = find_account_with_saml(user)
+ return render_saml_error unless account_user
+
+ @account = account_user.account
+ end
+
+ def find_account_with_saml(user)
+ user.account_users
+ .joins(account: :saml_settings)
+ .where.not(saml_settings: { sso_url: [nil, ''] })
+ .where.not(saml_settings: { certificate: [nil, ''] })
+ .find { |account_user| account_user.account.feature_enabled?('saml') }
+ end
+
+ def render_saml_error
+ render json: { error: I18n.t('auth.saml.authentication_failed') }, status: :unauthorized
+ end
+end
diff --git a/enterprise/app/controllers/enterprise/devise_overrides/omniauth_callbacks_controller.rb b/enterprise/app/controllers/enterprise/devise_overrides/omniauth_callbacks_controller.rb
new file mode 100644
index 000000000..973f26650
--- /dev/null
+++ b/enterprise/app/controllers/enterprise/devise_overrides/omniauth_callbacks_controller.rb
@@ -0,0 +1,64 @@
+module Enterprise::DeviseOverrides::OmniauthCallbacksController
+ def saml
+ # Call parent's omniauth_success which handles the auth
+ omniauth_success
+ end
+
+ def redirect_callbacks
+ # derive target redirect route from 'resource_class' param, which was set
+ # before authentication.
+ devise_mapping = get_devise_mapping
+ redirect_route = get_redirect_route(devise_mapping)
+
+ # preserve omniauth info for success route. ignore 'extra' in twitter
+ # auth response to avoid CookieOverflow.
+ session['dta.omniauth.auth'] = request.env['omniauth.auth'].except('extra')
+ session['dta.omniauth.params'] = request.env['omniauth.params']
+
+ # For SAML, use 303 See Other to convert POST to GET and preserve session
+ if params[:provider] == 'saml'
+ redirect_to redirect_route, { status: 303 }.merge(redirect_options)
+ else
+ super
+ end
+ end
+
+ def omniauth_success
+ case auth_hash&.dig('provider')
+ when 'saml'
+ handle_saml_auth
+ else
+ super
+ end
+ end
+
+ private
+
+ def handle_saml_auth
+ account_id = extract_saml_account_id
+ return redirect_to login_page_url(error: 'saml-not-enabled') unless saml_enabled_for_account?(account_id)
+
+ @resource = SamlUserBuilder.new(auth_hash, account_id).perform
+
+ if @resource.persisted?
+ sign_in_user
+ else
+ redirect_to login_page_url(error: 'saml-authentication-failed')
+ end
+ end
+
+ def extract_saml_account_id
+ params[:account_id] || session[:saml_account_id] || request.env['omniauth.params']&.dig('account_id')
+ end
+
+ def saml_enabled_for_account?(account_id)
+ return false if account_id.blank?
+
+ account = Account.find_by(id: account_id)
+
+ return false if account.nil?
+ return false unless account.feature_enabled?('saml')
+
+ AccountSamlSettings.find_by(account_id: account_id).present?
+ end
+end
diff --git a/enterprise/app/controllers/enterprise/devise_overrides/passwords_controller.rb b/enterprise/app/controllers/enterprise/devise_overrides/passwords_controller.rb
new file mode 100644
index 000000000..3a20e0d71
--- /dev/null
+++ b/enterprise/app/controllers/enterprise/devise_overrides/passwords_controller.rb
@@ -0,0 +1,16 @@
+module Enterprise::DeviseOverrides::PasswordsController
+ include SamlAuthenticationHelper
+
+ def create
+ if saml_user_attempting_password_auth?(params[:email])
+ render json: {
+ success: false,
+ message: I18n.t('messages.reset_password_saml_user'),
+ errors: [I18n.t('messages.reset_password_saml_user')]
+ }, status: :forbidden
+ return
+ end
+
+ super
+ end
+end
diff --git a/enterprise/app/controllers/enterprise/devise_overrides/sessions_controller.rb b/enterprise/app/controllers/enterprise/devise_overrides/sessions_controller.rb
index e11e3fff9..adfc0413e 100644
--- a/enterprise/app/controllers/enterprise/devise_overrides/sessions_controller.rb
+++ b/enterprise/app/controllers/enterprise/devise_overrides/sessions_controller.rb
@@ -1,4 +1,19 @@
module Enterprise::DeviseOverrides::SessionsController
+ include SamlAuthenticationHelper
+
+ def create
+ if saml_user_attempting_password_auth?(params[:email], sso_auth_token: params[:sso_auth_token])
+ render json: {
+ success: false,
+ message: I18n.t('messages.login_saml_user'),
+ errors: [I18n.t('messages.login_saml_user')]
+ }, status: :unauthorized
+ return
+ end
+
+ super
+ end
+
def render_create_success
create_audit_event('sign_in')
super
diff --git a/enterprise/app/controllers/enterprise/super_admin/app_configs_controller.rb b/enterprise/app/controllers/enterprise/super_admin/app_configs_controller.rb
index 5466c86a4..934462b93 100644
--- a/enterprise/app/controllers/enterprise/super_admin/app_configs_controller.rb
+++ b/enterprise/app/controllers/enterprise/super_admin/app_configs_controller.rb
@@ -10,7 +10,7 @@ module Enterprise::SuperAdmin::AppConfigsController
when 'internal'
@allowed_configs = internal_config_options
when 'captain'
- @allowed_configs = %w[CAPTAIN_OPEN_AI_API_KEY CAPTAIN_OPEN_AI_MODEL CAPTAIN_OPEN_AI_ENDPOINT CAPTAIN_FIRECRAWL_API_KEY]
+ @allowed_configs = captain_config_options
else
super
end
@@ -33,7 +33,17 @@ module Enterprise::SuperAdmin::AppConfigsController
def internal_config_options
%w[CHATWOOT_INBOX_TOKEN CHATWOOT_INBOX_HMAC_KEY ANALYTICS_TOKEN CLEARBIT_API_KEY DASHBOARD_SCRIPTS INACTIVE_WHATSAPP_NUMBERS BLOCKED_EMAIL_DOMAINS
- CAPTAIN_CLOUD_PLAN_LIMITS ACCOUNT_SECURITY_NOTIFICATION_WEBHOOK_URL CHATWOOT_INSTANCE_ADMIN_EMAIL
+ SKIP_INCOMING_BCC_PROCESSING CAPTAIN_CLOUD_PLAN_LIMITS ACCOUNT_SECURITY_NOTIFICATION_WEBHOOK_URL CHATWOOT_INSTANCE_ADMIN_EMAIL
OG_IMAGE_CDN_URL OG_IMAGE_CLIENT_REF CLOUDFLARE_API_KEY CLOUDFLARE_ZONE_ID]
end
+
+ def captain_config_options
+ %w[
+ CAPTAIN_OPEN_AI_API_KEY
+ CAPTAIN_OPEN_AI_MODEL
+ CAPTAIN_OPEN_AI_ENDPOINT
+ CAPTAIN_EMBEDDING_MODEL
+ CAPTAIN_FIRECRAWL_API_KEY
+ ]
+ end
end
diff --git a/enterprise/app/controllers/twilio/voice_controller.rb b/enterprise/app/controllers/twilio/voice_controller.rb
new file mode 100644
index 000000000..436083f14
--- /dev/null
+++ b/enterprise/app/controllers/twilio/voice_controller.rb
@@ -0,0 +1,38 @@
+class Twilio::VoiceController < ApplicationController
+ before_action :set_inbox!
+
+ def status
+ Voice::StatusUpdateService.new(
+ account: @inbox.account,
+ call_sid: params[:CallSid],
+ call_status: params[:CallStatus]
+ ).perform
+ head :no_content
+ end
+
+ def call_twiml
+ account = @inbox.account
+ call_sid = params[:CallSid]
+ from_number = params[:From].to_s
+ to_number = params[:To].to_s
+
+ builder = Voice::InboundCallBuilder.new(
+ account: account,
+ inbox: @inbox,
+ from_number: from_number,
+ to_number: to_number,
+ call_sid: call_sid
+ ).perform
+ render xml: builder.twiml_response
+ end
+
+ private
+
+ def set_inbox!
+ # Resolve from the digits in the route param and look up exact E.164 match
+ digits = params[:phone].to_s.gsub(/\D/, '')
+ e164 = "+#{digits}"
+ channel = Channel::Voice.find_by!(phone_number: e164)
+ @inbox = channel.inbox
+ end
+end
diff --git a/enterprise/app/helpers/saml_authentication_helper.rb b/enterprise/app/helpers/saml_authentication_helper.rb
new file mode 100644
index 000000000..9adcb22c3
--- /dev/null
+++ b/enterprise/app/helpers/saml_authentication_helper.rb
@@ -0,0 +1,12 @@
+module SamlAuthenticationHelper
+ def saml_user_attempting_password_auth?(email, sso_auth_token: nil)
+ return false if email.blank?
+
+ user = User.from_email(email)
+ return false unless user&.provider == 'saml'
+
+ return false if sso_auth_token.present? && user.valid_sso_auth_token?(sso_auth_token)
+
+ true
+ end
+end
diff --git a/enterprise/app/jobs/captain/documents/response_builder_job.rb b/enterprise/app/jobs/captain/documents/response_builder_job.rb
index 5dacb416f..b22fa5bc1 100644
--- a/enterprise/app/jobs/captain/documents/response_builder_job.rb
+++ b/enterprise/app/jobs/captain/documents/response_builder_job.rb
@@ -33,7 +33,8 @@ class Captain::Documents::ResponseBuilderJob < ApplicationJob
Captain::Llm::PaginatedFaqGeneratorService.new(
document,
pages_per_chunk: options[:pages_per_chunk],
- max_pages: options[:max_pages]
+ max_pages: options[:max_pages],
+ language: document.account.locale_english_name
)
end
diff --git a/enterprise/app/jobs/saml/update_account_users_provider_job.rb b/enterprise/app/jobs/saml/update_account_users_provider_job.rb
new file mode 100644
index 000000000..46f47829d
--- /dev/null
+++ b/enterprise/app/jobs/saml/update_account_users_provider_job.rb
@@ -0,0 +1,33 @@
+class Saml::UpdateAccountUsersProviderJob < ApplicationJob
+ queue_as :default
+
+ # Updates the authentication provider for users in an account
+ # This job is triggered when SAML settings are created or destroyed
+ def perform(account_id, provider)
+ account = Account.find(account_id)
+ account.users.find_each(batch_size: 1000) do |user|
+ next unless should_update_user_provider?(user, provider)
+
+ # rubocop:disable Rails/SkipsModelValidations
+ user.update_column(:provider, provider)
+ # rubocop:enable Rails/SkipsModelValidations
+ end
+ end
+
+ private
+
+ # Determines if a user's provider should be updated based on their multi-account status
+ # When resetting to 'email', only update users who don't have SAML enabled on other accounts
+ # This prevents breaking SAML authentication for users who belong to multiple accounts
+ def should_update_user_provider?(user, provider)
+ return !user_has_other_saml_accounts?(user) if provider == 'email'
+
+ true
+ end
+
+ # Checks if the user belongs to any other accounts that have SAML configured
+ # Used to preserve SAML authentication when one account disables SAML but others still use it
+ def user_has_other_saml_accounts?(user)
+ user.accounts.joins(:saml_settings).exists?
+ end
+end
diff --git a/enterprise/app/models/account_saml_settings.rb b/enterprise/app/models/account_saml_settings.rb
new file mode 100644
index 000000000..9e37f1c82
--- /dev/null
+++ b/enterprise/app/models/account_saml_settings.rb
@@ -0,0 +1,79 @@
+# == Schema Information
+#
+# Table name: account_saml_settings
+#
+# id :bigint not null, primary key
+# certificate :text
+# role_mappings :json
+# sso_url :string
+# created_at :datetime not null
+# updated_at :datetime not null
+# account_id :bigint not null
+# idp_entity_id :string
+# sp_entity_id :string
+#
+# Indexes
+#
+# index_account_saml_settings_on_account_id (account_id)
+#
+class AccountSamlSettings < ApplicationRecord
+ belongs_to :account
+
+ validates :account_id, presence: true
+ validates :sso_url, presence: true
+ validates :certificate, presence: true
+ validates :idp_entity_id, presence: true
+ validate :certificate_must_be_valid_x509
+
+ before_validation :set_sp_entity_id, if: :sp_entity_id_needs_generation?
+
+ after_create_commit :update_account_users_provider
+ after_destroy_commit :reset_account_users_provider
+
+ def saml_enabled?
+ sso_url.present? && certificate.present?
+ end
+
+ def certificate_fingerprint
+ return nil if certificate.blank?
+
+ begin
+ cert = OpenSSL::X509::Certificate.new(certificate)
+ OpenSSL::Digest::SHA1.new(cert.to_der).hexdigest
+ .upcase.gsub(/(.{2})(?=.)/, '\1:')
+ rescue OpenSSL::X509::CertificateError
+ nil
+ end
+ end
+
+ private
+
+ def set_sp_entity_id
+ base_url = GlobalConfigService.load('FRONTEND_URL', 'http://localhost:3000')
+ self.sp_entity_id = "#{base_url}/saml/sp/#{account_id}"
+ end
+
+ def sp_entity_id_needs_generation?
+ sp_entity_id.blank?
+ end
+
+ def installation_name
+ GlobalConfigService.load('INSTALLATION_NAME', 'Chatwoot')
+ end
+
+ def update_account_users_provider
+ Saml::UpdateAccountUsersProviderJob.perform_later(account_id, 'saml')
+ end
+
+ def reset_account_users_provider
+ Saml::UpdateAccountUsersProviderJob.perform_later(account_id, 'email')
+ end
+
+ def certificate_must_be_valid_x509
+ return if certificate.blank?
+
+ OpenSSL::X509::Certificate.new(certificate)
+ rescue OpenSSL::X509::CertificateError
+ errors.add(:certificate, I18n.t('errors.account_saml_settings.invalid_certificate'))
+ end
+end
diff --git a/enterprise/app/models/channel/voice.rb b/enterprise/app/models/channel/voice.rb
index 40f9070be..2662b7284 100644
--- a/enterprise/app/models/channel/voice.rb
+++ b/enterprise/app/models/channel/voice.rb
@@ -44,13 +44,13 @@ class Channel::Voice < ApplicationRecord
# Public URLs used to configure Twilio webhooks
def voice_call_webhook_url
- base = ENV.fetch('FRONTEND_URL', '').to_s.sub(%r{/*$}, '')
- "#{base}/twilio/voice/call/#{phone_number}"
+ digits = phone_number.delete_prefix('+')
+ "#{ENV.fetch('FRONTEND_URL', nil)}/twilio/voice/call/#{digits}"
end
def voice_status_webhook_url
- base = ENV.fetch('FRONTEND_URL', '').to_s.sub(%r{/*$}, '')
- "#{base}/twilio/voice/status/#{phone_number}"
+ digits = phone_number.delete_prefix('+')
+ "#{ENV.fetch('FRONTEND_URL', nil)}/twilio/voice/status/#{digits}"
end
private
diff --git a/enterprise/app/models/enterprise/account.rb b/enterprise/app/models/enterprise/account.rb
index dff97c0ee..0bf93c98b 100644
--- a/enterprise/app/models/enterprise/account.rb
+++ b/enterprise/app/models/enterprise/account.rb
@@ -27,4 +27,8 @@ module Enterprise::Account
def unmark_for_deletion
custom_attributes.delete('marked_for_deletion_at') && custom_attributes.delete('marked_for_deletion_reason') && save
end
+
+ def saml_enabled?
+ saml_settings&.saml_enabled? || false
+ end
end
diff --git a/enterprise/app/models/enterprise/concerns/account.rb b/enterprise/app/models/enterprise/concerns/account.rb
index e1136fd07..b52ac4b3e 100644
--- a/enterprise/app/models/enterprise/concerns/account.rb
+++ b/enterprise/app/models/enterprise/concerns/account.rb
@@ -13,5 +13,7 @@ module Enterprise::Concerns::Account
has_many :copilot_threads, dependent: :destroy_async
has_many :voice_channels, dependent: :destroy_async, class_name: '::Channel::Voice'
+
+ has_one :saml_settings, dependent: :destroy_async, class_name: 'AccountSamlSettings'
end
end
diff --git a/enterprise/app/models/enterprise/conversation.rb b/enterprise/app/models/enterprise/conversation.rb
index e137c0929..e653ad0ab 100644
--- a/enterprise/app/models/enterprise/conversation.rb
+++ b/enterprise/app/models/enterprise/conversation.rb
@@ -2,4 +2,15 @@ module Enterprise::Conversation
def list_of_keys
super + %w[sla_policy_id]
end
+
+ # Include select additional_attributes keys (call related) for update events
+ def allowed_keys?
+ return true if super
+
+ attrs_change = previous_changes['additional_attributes']
+ return false unless attrs_change.is_a?(Array) && attrs_change[1].is_a?(Hash)
+
+ changed_attr_keys = attrs_change[1].keys
+ changed_attr_keys.intersect?(%w[call_status])
+ end
end
diff --git a/enterprise/app/models/enterprise/inbox.rb b/enterprise/app/models/enterprise/inbox.rb
index 0ae21ce00..2462122f7 100644
--- a/enterprise/app/models/enterprise/inbox.rb
+++ b/enterprise/app/models/enterprise/inbox.rb
@@ -22,7 +22,13 @@ module Enterprise::Inbox
end
def get_agent_ids_over_assignment_limit(limit)
- conversations.open.select(:assignee_id).group(:assignee_id).having("count(*) >= #{limit.to_i}").filter_map(&:assignee_id)
+ conversations
+ .open
+ .where(account_id: account_id)
+ .select(:assignee_id)
+ .group(:assignee_id)
+ .having("count(*) >= #{limit.to_i}")
+ .filter_map(&:assignee_id)
end
def ensure_valid_max_assignment_limit
diff --git a/enterprise/app/policies/account_saml_settings_policy.rb b/enterprise/app/policies/account_saml_settings_policy.rb
new file mode 100644
index 000000000..fc1418935
--- /dev/null
+++ b/enterprise/app/policies/account_saml_settings_policy.rb
@@ -0,0 +1,17 @@
+class AccountSamlSettingsPolicy < ApplicationPolicy
+ def show?
+ @account_user.administrator?
+ end
+
+ def create?
+ @account_user.administrator?
+ end
+
+ def update?
+ @account_user.administrator?
+ end
+
+ def destroy?
+ @account_user.administrator?
+ end
+end
diff --git a/enterprise/app/services/captain/llm/paginated_faq_generator_service.rb b/enterprise/app/services/captain/llm/paginated_faq_generator_service.rb
index 18f9813ef..2d326f081 100644
--- a/enterprise/app/services/captain/llm/paginated_faq_generator_service.rb
+++ b/enterprise/app/services/captain/llm/paginated_faq_generator_service.rb
@@ -8,6 +8,7 @@ class Captain::Llm::PaginatedFaqGeneratorService < Llm::BaseOpenAiService
def initialize(document, options = {})
super()
@document = document
+ @language = options[:language] || 'english'
@pages_per_chunk = options[:pages_per_chunk] || DEFAULT_PAGES_PER_CHUNK
@max_pages = options[:max_pages] # Optional limit from UI
@total_pages_processed = 0
@@ -118,7 +119,7 @@ class Captain::Llm::PaginatedFaqGeneratorService < Llm::BaseOpenAiService
end
def page_chunk_prompt(start_page, end_page)
- Captain::Llm::SystemPromptsService.paginated_faq_generator(start_page, end_page)
+ Captain::Llm::SystemPromptsService.paginated_faq_generator(start_page, end_page, @language)
end
def standard_chat_parameters
@@ -128,7 +129,7 @@ class Captain::Llm::PaginatedFaqGeneratorService < Llm::BaseOpenAiService
messages: [
{
role: 'system',
- content: Captain::Llm::SystemPromptsService.faq_generator
+ content: Captain::Llm::SystemPromptsService.faq_generator(@language)
},
{
role: 'user',
diff --git a/enterprise/app/services/captain/llm/system_prompts_service.rb b/enterprise/app/services/captain/llm/system_prompts_service.rb
index b8282beb1..ba8834c1f 100644
--- a/enterprise/app/services/captain/llm/system_prompts_service.rb
+++ b/enterprise/app/services/captain/llm/system_prompts_service.rb
@@ -206,7 +206,7 @@ class Captain::Llm::SystemPromptsService
SYSTEM_PROMPT_MESSAGE
end
- def paginated_faq_generator(start_page, end_page)
+ def paginated_faq_generator(start_page, end_page, language = 'english')
<<~PROMPT
You are an expert technical documentation specialist tasked with creating comprehensive FAQs from a SPECIFIC SECTION of a document.
@@ -226,6 +226,8 @@ class Captain::Llm::SystemPromptsService
FAQ GENERATION GUIDELINES
════════════════════════════════════════════════════════
+ **Language**: Generate the FAQs only in #{language}, use no other language
+
1. **Comprehensive Extraction**
• Extract ALL information that could generate FAQs from this section
• Target 5-10 FAQs per page equivalent of rich content
diff --git a/enterprise/app/services/voice/inbound_call_builder.rb b/enterprise/app/services/voice/inbound_call_builder.rb
new file mode 100644
index 000000000..f5ae18801
--- /dev/null
+++ b/enterprise/app/services/voice/inbound_call_builder.rb
@@ -0,0 +1,82 @@
+class Voice::InboundCallBuilder
+ pattr_initialize [:account!, :inbox!, :from_number!, :to_number, :call_sid!]
+
+ attr_reader :conversation
+
+ def perform
+ contact = find_or_create_contact!
+ contact_inbox = find_or_create_contact_inbox!(contact)
+ @conversation = find_or_create_conversation!(contact, contact_inbox)
+ create_call_message_if_needed!
+ self
+ end
+
+ def twiml_response
+ response = Twilio::TwiML::VoiceResponse.new
+ response.say(message: 'Please wait while we connect you to an agent')
+ response.to_s
+ end
+
+ private
+
+ def find_or_create_conversation!(contact, contact_inbox)
+ account.conversations.find_or_create_by!(
+ account_id: account.id,
+ inbox_id: inbox.id,
+ identifier: call_sid
+ ) do |conv|
+ conv.contact_id = contact.id
+ conv.contact_inbox_id = contact_inbox.id
+ conv.additional_attributes = {
+ 'call_direction' => 'inbound',
+ 'call_status' => 'ringing'
+ }
+ end
+ end
+
+ def create_call_message!
+ content_attrs = call_message_content_attributes
+
+ @conversation.messages.create!(
+ account_id: account.id,
+ inbox_id: inbox.id,
+ message_type: :incoming,
+ sender: @conversation.contact,
+ content: 'Voice Call',
+ content_type: 'voice_call',
+ content_attributes: content_attrs
+ )
+ end
+
+ def create_call_message_if_needed!
+ return if @conversation.messages.voice_calls.exists?
+
+ create_call_message!
+ end
+
+ def call_message_content_attributes
+ {
+ data: {
+ call_sid: call_sid,
+ status: 'ringing',
+ conversation_id: @conversation.display_id,
+ call_direction: 'inbound',
+ from_number: from_number,
+ to_number: to_number,
+ meta: {
+ created_at: Time.current.to_i,
+ ringing_at: Time.current.to_i
+ }
+ }
+ }
+ end
+
+ def find_or_create_contact!
+ account.contacts.find_by(phone_number: from_number) ||
+ account.contacts.create!(phone_number: from_number, name: 'Unknown Caller')
+ end
+
+ def find_or_create_contact_inbox!(contact)
+ ContactInbox.where(contact_id: contact.id, inbox_id: inbox.id, source_id: from_number).first_or_create!
+ end
+end
diff --git a/enterprise/app/services/voice/status_update_service.rb b/enterprise/app/services/voice/status_update_service.rb
new file mode 100644
index 000000000..18152c549
--- /dev/null
+++ b/enterprise/app/services/voice/status_update_service.rb
@@ -0,0 +1,29 @@
+class Voice::StatusUpdateService
+ pattr_initialize [:account!, :call_sid!, :call_status]
+
+ def perform
+ conversation = account.conversations.find_by(identifier: call_sid)
+ return unless conversation
+ return if call_status.to_s.strip.empty?
+
+ update_conversation!(conversation)
+ update_last_call_message!(conversation)
+ end
+
+ private
+
+ def update_conversation!(conversation)
+ attrs = (conversation.additional_attributes || {}).merge('call_status' => call_status)
+ conversation.update!(additional_attributes: attrs)
+ end
+
+ def update_last_call_message!(conversation)
+ msg = conversation.messages.voice_calls.order(created_at: :desc).first
+ return unless msg
+
+ data = msg.content_attributes.is_a?(Hash) ? msg.content_attributes : {}
+ data['data'] ||= {}
+ data['data']['status'] = call_status
+ msg.update!(content_attributes: data)
+ end
+end
diff --git a/enterprise/app/views/api/v1/accounts/saml_settings/create.json.jbuilder b/enterprise/app/views/api/v1/accounts/saml_settings/create.json.jbuilder
new file mode 100644
index 000000000..fa0c761c9
--- /dev/null
+++ b/enterprise/app/views/api/v1/accounts/saml_settings/create.json.jbuilder
@@ -0,0 +1 @@
+json.partial! 'api/v1/models/account_saml_settings', account_saml_settings: @saml_settings
diff --git a/enterprise/app/views/api/v1/accounts/saml_settings/show.json.jbuilder b/enterprise/app/views/api/v1/accounts/saml_settings/show.json.jbuilder
new file mode 100644
index 000000000..fa0c761c9
--- /dev/null
+++ b/enterprise/app/views/api/v1/accounts/saml_settings/show.json.jbuilder
@@ -0,0 +1 @@
+json.partial! 'api/v1/models/account_saml_settings', account_saml_settings: @saml_settings
diff --git a/enterprise/app/views/api/v1/accounts/saml_settings/update.json.jbuilder b/enterprise/app/views/api/v1/accounts/saml_settings/update.json.jbuilder
new file mode 100644
index 000000000..fa0c761c9
--- /dev/null
+++ b/enterprise/app/views/api/v1/accounts/saml_settings/update.json.jbuilder
@@ -0,0 +1 @@
+json.partial! 'api/v1/models/account_saml_settings', account_saml_settings: @saml_settings
diff --git a/enterprise/app/views/api/v1/models/_account_saml_settings.json.jbuilder b/enterprise/app/views/api/v1/models/_account_saml_settings.json.jbuilder
new file mode 100644
index 000000000..b17944605
--- /dev/null
+++ b/enterprise/app/views/api/v1/models/_account_saml_settings.json.jbuilder
@@ -0,0 +1,10 @@
+json.id account_saml_settings.id
+json.account_id account_saml_settings.account_id
+json.sso_url account_saml_settings.sso_url
+json.certificate account_saml_settings.certificate
+json.fingerprint account_saml_settings.certificate_fingerprint
+json.idp_entity_id account_saml_settings.idp_entity_id
+json.sp_entity_id account_saml_settings.sp_entity_id
+json.role_mappings account_saml_settings.role_mappings || {}
+json.created_at account_saml_settings.created_at
+json.updated_at account_saml_settings.updated_at
diff --git a/enterprise/app/views/api/v1/models/_agent_capacity_policy.json.jbuilder b/enterprise/app/views/api/v1/models/_agent_capacity_policy.json.jbuilder
index 8f7a41aa1..2051cc1c1 100644
--- a/enterprise/app/views/api/v1/models/_agent_capacity_policy.json.jbuilder
+++ b/enterprise/app/views/api/v1/models/_agent_capacity_policy.json.jbuilder
@@ -5,6 +5,7 @@ json.exclusion_rules agent_capacity_policy.exclusion_rules
json.created_at agent_capacity_policy.created_at.to_i
json.updated_at agent_capacity_policy.updated_at.to_i
json.account_id agent_capacity_policy.account_id
+json.assigned_agent_count agent_capacity_policy.account_users.count
json.inbox_capacity_limits agent_capacity_policy.inbox_capacity_limits do |limit|
json.id limit.id
diff --git a/enterprise/app/views/devise/mailer/confirmation_instructions.html.erb b/enterprise/app/views/devise/mailer/confirmation_instructions.html.erb
new file mode 100644
index 000000000..91837f980
--- /dev/null
+++ b/enterprise/app/views/devise/mailer/confirmation_instructions.html.erb
@@ -0,0 +1,45 @@
+Hi <%= @resource.name %>,
+
+<% account_user = @resource&.account_users&.first %>
+<% is_saml_account = account_user&.account&.saml_enabled? %>
+
+<% if account_user&.inviter.present? && @resource.unconfirmed_email.blank? %>
+ <% if is_saml_account %>
+ <%= account_user.inviter.name %>, with <%= account_user.account.name %>, has invited you to access <%= global_config['BRAND_NAME'] || 'Chatwoot' %> via Single Sign-On (SSO).
+ Your organization uses SSO for secure authentication. You will not need a password to access your account.
+ <% else %>
+ <%= account_user.inviter.name %>, with <%= account_user.account.name %>, has invited you to try out <%= global_config['BRAND_NAME'] || 'Chatwoot' %>.
+ <% end %>
+<% end %>
+
+<% if @resource.confirmed? %>
+ You can login to your <%= global_config['BRAND_NAME'] || 'Chatwoot' %> account through the link below:
+<% else %>
+ <% if account_user&.inviter.blank? %>
+
+ Welcome to <%= global_config['BRAND_NAME'] || 'Chatwoot' %>! We have a suite of powerful tools ready for you to explore. Before that we quickly need to verify your email address to know it's really you.
+
+ <% end %>
+ <% unless is_saml_account %>
+ Please take a moment and click the link below and activate your account.
+ <% end %>
+<% end %>
+
+
+<% if @resource.unconfirmed_email.present? %>
+ <%= link_to 'Confirm my account', frontend_url('auth/confirmation', confirmation_token: @token) %>
+<% elsif @resource.confirmed? %>
+ <% if is_saml_account %>
+ You can now access your account by logging in through your organization's SSO portal.
+ <% else %>
+ <%= link_to 'Login to my account', frontend_url('auth/sign_in') %>
+ <% end %>
+<% elsif account_user&.inviter.present? %>
+ <% if is_saml_account %>
+ You can access your account by logging in through your organization's SSO portal.
+ <% else %>
+ <%= link_to 'Confirm my account', frontend_url('auth/password/edit', reset_password_token: @resource.send(:set_reset_password_token)) %>
+ <% end %>
+<% else %>
+ <%= link_to 'Confirm my account', frontend_url('auth/confirmation', confirmation_token: @token) %>
+<% end %>
diff --git a/enterprise/config/initializers/omniauth_saml.rb b/enterprise/config/initializers/omniauth_saml.rb
new file mode 100644
index 000000000..f73e3a109
--- /dev/null
+++ b/enterprise/config/initializers/omniauth_saml.rb
@@ -0,0 +1,43 @@
+# Enterprise Edition SAML SSO Provider
+# This initializer adds SAML authentication support for Enterprise customers
+
+# SAML setup proc for multi-tenant configuration
+SAML_SETUP_PROC = proc do |env|
+ request = ActionDispatch::Request.new(env)
+
+ # Extract account_id from various sources
+ account_id = request.params['account_id'] ||
+ request.session[:saml_account_id] ||
+ env['omniauth.params']&.dig('account_id')
+
+ if account_id
+ # Store in session and omniauth params for callback
+ request.session[:saml_account_id] = account_id
+ env['omniauth.params'] ||= {}
+ env['omniauth.params']['account_id'] = account_id
+
+ # Find SAML settings for this account
+ settings = AccountSamlSettings.find_by(account_id: account_id)
+
+ if settings
+ # Configure the strategy options dynamically
+ env['omniauth.strategy'].options[:assertion_consumer_service_url] = "#{ENV.fetch('FRONTEND_URL', 'http://localhost:3000')}/omniauth/saml/callback?account_id=#{account_id}"
+ env['omniauth.strategy'].options[:sp_entity_id] = settings.sp_entity_id
+ env['omniauth.strategy'].options[:idp_entity_id] = settings.idp_entity_id
+ env['omniauth.strategy'].options[:idp_sso_service_url] = settings.sso_url
+ env['omniauth.strategy'].options[:idp_cert] = settings.certificate
+ env['omniauth.strategy'].options[:name_identifier_format] = 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress'
+ else
+ # Set a dummy certificate to avoid the error
+ env['omniauth.strategy'].options[:idp_cert] = 'DUMMY'
+ end
+ else
+ # Set a dummy certificate to avoid the error
+ env['omniauth.strategy'].options[:idp_cert] = 'DUMMY'
+ end
+end
+
+Rails.application.config.middleware.use OmniAuth::Builder do
+ # SAML provider with setup phase for multi-tenant configuration
+ provider :saml, setup: SAML_SETUP_PROC
+end
diff --git a/enterprise/lib/tasks.rb b/enterprise/lib/tasks.rb
deleted file mode 100644
index 139819bbb..000000000
--- a/enterprise/lib/tasks.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-# Load all rake tasks from the enterprise/lib/tasks directory
-module Tasks
- Dir.glob(File.join(File.dirname(__FILE__), 'tasks', '*.rake')).each { |r| load r }
-end
diff --git a/enterprise/lib/tasks/search.rake b/enterprise/lib/tasks/search.rake
index 03bad2c1b..4ec137434 100644
--- a/enterprise/lib/tasks/search.rake
+++ b/enterprise/lib/tasks/search.rake
@@ -1,22 +1,4 @@
-module Tasks::SearchTaskHelpers
- def check_opensearch_config
- if ENV['OPENSEARCH_URL'].blank?
- puts 'Skipping reindex as OPENSEARCH_URL is not configured'
- return false
- end
- true
- end
-
- def reindex_account(account)
- Messages::ReindexService.new(account: account).perform
- puts "Reindex task queued for account #{account.id}"
- end
-end
-
namespace :search do
- desc 'Reindex messages using searchkick'
- include Tasks::SearchTaskHelpers
-
desc 'Reindex messages for all accounts'
task all: :environment do
next unless check_opensearch_config
@@ -47,3 +29,16 @@ namespace :search do
reindex_account(account)
end
end
+
+def check_opensearch_config
+ if ENV['OPENSEARCH_URL'].blank?
+ puts 'Skipping reindex as OPENSEARCH_URL is not configured'
+ return false
+ end
+ true
+end
+
+def reindex_account(account)
+ Messages::ReindexService.new(account: account).perform
+ puts "Reindex task queued for account #{account.id}"
+end
diff --git a/enterprise/tasks_railtie.rb b/enterprise/tasks_railtie.rb
new file mode 100644
index 000000000..9a7d09360
--- /dev/null
+++ b/enterprise/tasks_railtie.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+class TasksRailtie < Rails::Railtie
+ rake_tasks do
+ # Load all rake tasks from enterprise/lib/tasks
+ Dir.glob(Rails.root.join('enterprise/lib/tasks/**/*.rake')).each { |f| load f }
+ end
+end
diff --git a/histoire.config.ts b/histoire.config.ts
index 15c5e7b64..41b6f3c13 100644
--- a/histoire.config.ts
+++ b/histoire.config.ts
@@ -4,6 +4,7 @@ import { HstVue } from '@histoire/plugin-vue';
export default defineConfig({
setupFile: './histoire.setup.ts',
plugins: [HstVue()],
+ collectMaxThreads: 4,
vite: {
server: {
port: 6179,
diff --git a/lib/filters/filter_keys.yml b/lib/filters/filter_keys.yml
index 902b5f219..626a0cbd5 100644
--- a/lib/filters/filter_keys.yml
+++ b/lib/filters/filter_keys.yml
@@ -4,7 +4,7 @@
# 3. Automation Filters (app/services/automation_rules/conditions_filter_service.rb), (app/services/automation_rules/condition_validation_service.rb)
-# Format
+# Format
# - Parent Key (conversation, contact, messages)
# - Key (attribute_name)
# - attribute_type: "standard" : supported ["standard", "additional_attributes (only for conversations and messages)"]
@@ -138,7 +138,7 @@ contacts:
- "does_not_contain"
phone_number:
attribute_type: "standard"
- data_type: "text_case_insensitive"
+ data_type: "text" # Text is not explicity defined in filters, default filter will be used
filter_operators:
- "equal_to"
- "not_equal_to"
diff --git a/lib/integrations/openai_base_service.rb b/lib/integrations/openai_base_service.rb
index f06baf5b5..7b4a93f95 100644
--- a/lib/integrations/openai_base_service.rb
+++ b/lib/integrations/openai_base_service.rb
@@ -81,7 +81,7 @@ class Integrations::OpenaiBaseService
end
def api_url
- endpoint = InstallationConfig.find_by(name: 'CAPTAIN_OPEN_AI_ENDPOINT')&.value || 'https://api.openai.com/'
+ endpoint = InstallationConfig.find_by(name: 'CAPTAIN_OPEN_AI_ENDPOINT')&.value.presence || 'https://api.openai.com/'
endpoint = endpoint.chomp('/')
"#{endpoint}/v1/chat/completions"
end
diff --git a/lib/tasks/mfa.rake b/lib/tasks/mfa.rake
new file mode 100644
index 000000000..df2e8c425
--- /dev/null
+++ b/lib/tasks/mfa.rake
@@ -0,0 +1,65 @@
+module MfaTasks
+ def self.find_user_or_exit(email)
+ abort 'Error: Please provide an email address' if email.blank?
+ user = User.from_email(email)
+ abort "Error: User with email '#{email}' not found" unless user
+ user
+ end
+
+ def self.reset_user_mfa(user)
+ user.update!(
+ otp_required_for_login: false,
+ otp_secret: nil,
+ otp_backup_codes: nil
+ )
+ end
+
+ def self.reset_single(args)
+ user = find_user_or_exit(args[:email])
+ abort "MFA is already disabled for #{args[:email]}" if !user.otp_required_for_login? && user.otp_secret.nil?
+ reset_user_mfa(user)
+ puts "✓ MFA has been successfully reset for #{args[:email]}"
+ rescue StandardError => e
+ abort "Error resetting MFA: #{e.message}"
+ end
+
+ def self.reset_all
+ print 'Are you sure you want to reset MFA for ALL users? This cannot be undone! (yes/no): '
+ abort 'Operation cancelled' unless $stdin.gets.chomp.downcase == 'yes'
+
+ affected_users = User.where(otp_required_for_login: true).or(User.where.not(otp_secret: nil))
+ count = affected_users.count
+ abort 'No users have MFA enabled' if count.zero?
+
+ puts "\nResetting MFA for #{count} user(s)..."
+ affected_users.find_each { |user| reset_user_mfa(user) }
+ puts "✓ MFA has been reset for #{count} user(s)"
+ end
+
+ def self.generate_backup_codes(args)
+ user = find_user_or_exit(args[:email])
+ abort "Error: MFA is not enabled for #{args[:email]}" unless user.otp_required_for_login?
+
+ service = Mfa::ManagementService.new(user: user)
+ codes = service.generate_backup_codes!
+ puts "\nNew backup codes generated for #{args[:email]}:"
+ codes.each { |code| puts code }
+ end
+end
+
+namespace :mfa do
+ desc 'Reset MFA for a specific user by email'
+ task :reset, [:email] => :environment do |_task, args|
+ MfaTasks.reset_single(args)
+ end
+
+ desc 'Reset MFA for all users in the system'
+ task reset_all: :environment do
+ MfaTasks.reset_all
+ end
+
+ desc 'Generate new backup codes for a user'
+ task :generate_backup_codes, [:email] => :environment do |_task, args|
+ MfaTasks.generate_backup_codes(args)
+ end
+end
diff --git a/package.json b/package.json
index d087e6b03..66dd84aa2 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@chatwoot/chatwoot",
- "version": "4.5.2",
+ "version": "4.6.0",
"license": "MIT",
"scripts": {
"eslint": "eslint app/**/*.{js,vue}",
@@ -34,7 +34,7 @@
"@breezystack/lamejs": "^1.2.7",
"@chatwoot/ninja-keys": "1.2.3",
"@chatwoot/prosemirror-schema": "1.2.1",
- "@chatwoot/utils": "^0.0.50",
+ "@chatwoot/utils": "^0.0.51",
"@formkit/core": "^1.6.7",
"@formkit/vue": "^1.6.7",
"@hcaptcha/vue3-hcaptcha": "^1.3.0",
@@ -139,7 +139,7 @@
"prosemirror-model": "^1.22.3",
"size-limit": "^8.2.4",
"tailwindcss": "^3.4.13",
- "vite": "^5.4.19",
+ "vite": "^5.4.20",
"vite-plugin-ruby": "^5.0.0",
"vitest": "3.0.5"
},
@@ -155,7 +155,7 @@
"pnpm": {
"overrides": {
"vite-node": "2.0.1",
- "vite": "5.4.19",
+ "vite": "5.4.20",
"vitest": "3.0.5"
}
},
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 5089f1761..71279b7e5 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -6,7 +6,7 @@ settings:
overrides:
vite-node: 2.0.1
- vite: 5.4.19
+ vite: 5.4.20
vitest: 3.0.5
importers:
@@ -23,8 +23,8 @@ importers:
specifier: 1.2.1
version: 1.2.1
'@chatwoot/utils':
- specifier: ^0.0.50
- version: 0.0.50
+ specifier: ^0.0.51
+ version: 0.0.51
'@formkit/core':
specifier: ^1.6.7
version: 1.6.7
@@ -69,7 +69,7 @@ importers:
version: 8.20.5(vue@3.5.12(typescript@5.6.2))
'@vitejs/plugin-vue':
specifier: ^5.1.4
- version: 5.1.4(vite@5.4.19(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0))(vue@3.5.12(typescript@5.6.2))
+ version: 5.1.4(vite@5.4.20(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0))(vue@3.5.12(typescript@5.6.2))
'@vue/compiler-sfc':
specifier: ^3.5.8
version: 3.5.8
@@ -241,7 +241,7 @@ importers:
version: 1.8.1(tailwindcss@3.4.13)
'@histoire/plugin-vue':
specifier: 0.17.15
- version: 0.17.15(histoire@0.17.15(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0)(vite@5.4.19(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0)))(vite@5.4.19(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0))(vue@3.5.12(typescript@5.6.2))
+ version: 0.17.15(histoire@0.17.15(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0)(vite@5.4.20(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0)))(vite@5.4.20(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0))(vue@3.5.12(typescript@5.6.2))
'@iconify-json/logos':
specifier: ^1.2.3
version: 1.2.3
@@ -304,7 +304,7 @@ importers:
version: 6.0.0
histoire:
specifier: 0.17.15
- version: 0.17.15(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0)(vite@5.4.19(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0))
+ version: 0.17.15(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0)(vite@5.4.20(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0))
husky:
specifier: ^7.0.0
version: 7.0.4
@@ -333,11 +333,11 @@ importers:
specifier: ^3.4.13
version: 3.4.13
vite:
- specifier: 5.4.19
- version: 5.4.19(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0)
+ specifier: 5.4.20
+ version: 5.4.20(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0)
vite-plugin-ruby:
specifier: ^5.0.0
- version: 5.0.0(vite@5.4.19(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0))
+ version: 5.0.0(vite@5.4.20(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0))
vitest:
specifier: 3.0.5
version: 3.0.5(@types/node@22.7.0)(jsdom@24.1.3)(sass@1.79.3)(terser@5.33.0)
@@ -409,8 +409,8 @@ packages:
'@chatwoot/prosemirror-schema@1.2.1':
resolution: {integrity: sha512-UbiEvG5tgi1d0lMbkaqxgTh7vHfywEYKLQo1sxqp4Q7aLZh4QFtbLzJ2zyBtu4Nhipe+guFfEJdic7i43MP/XQ==}
- '@chatwoot/utils@0.0.50':
- resolution: {integrity: sha512-GGvB+ujt+8qnV6KKEM2IH9/JmbMpMMfrJ4C+SdPvd/WbhUEFvRof0D9fsU+444G8BUh2om7GM7mXOa3pEH+Vtw==}
+ '@chatwoot/utils@0.0.51':
+ resolution: {integrity: sha512-WlEmWfOTzR7YZRUWzn5Wpm15/BRudpwqoNckph8TohyDbiim1CP4UZGa+qjajxTbNGLLhtKlm0Xl+X16+5Wceg==}
engines: {node: '>=10'}
'@codemirror/commands@6.7.0':
@@ -863,7 +863,7 @@ packages:
'@histoire/shared@0.17.17':
resolution: {integrity: sha512-ueGtURysonT0MujCObPCR57+mgZluMEXCrbc2FBgKAD/DoAt38tNwSGsmLldk2O6nTr7lr6ClbVSgWrLwgY6Xw==}
peerDependencies:
- vite: 5.4.19
+ vite: 5.4.20
'@histoire/vendors@0.17.17':
resolution: {integrity: sha512-QZvmffdoJlLuYftPIkOU5Q2FPAdG2JjMuQ5jF7NmEl0n1XnmbMqtRkdYTZ4eF6CO1KLZ0Zyf6gBQvoT1uWNcjA==}
@@ -943,8 +943,8 @@ packages:
resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==}
engines: {node: '>=8'}
- '@jridgewell/gen-mapping@0.3.12':
- resolution: {integrity: sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==}
+ '@jridgewell/gen-mapping@0.3.13':
+ resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==}
'@jridgewell/gen-mapping@0.3.5':
resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==}
@@ -966,20 +966,20 @@ packages:
resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==}
engines: {node: '>=6.0.0'}
- '@jridgewell/source-map@0.3.10':
- resolution: {integrity: sha512-0pPkgz9dY+bijgistcTTJ5mR+ocqRXLuhXHYdzoMmmoJ2C9S46RCm2GMUbatPEUK9Yjy26IrAy8D/M00lLkv+Q==}
+ '@jridgewell/source-map@0.3.11':
+ resolution: {integrity: sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==}
'@jridgewell/sourcemap-codec@1.5.0':
resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==}
- '@jridgewell/sourcemap-codec@1.5.4':
- resolution: {integrity: sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==}
+ '@jridgewell/sourcemap-codec@1.5.5':
+ resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
'@jridgewell/trace-mapping@0.3.25':
resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
- '@jridgewell/trace-mapping@0.3.29':
- resolution: {integrity: sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==}
+ '@jridgewell/trace-mapping@0.3.30':
+ resolution: {integrity: sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==}
'@kurkle/color@0.3.2':
resolution: {integrity: sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==}
@@ -1047,103 +1047,108 @@ packages:
'@rails/ujs@7.1.400':
resolution: {integrity: sha512-YwvXm3BR5tn+VCAKYGycLejMRVZE3Ionj5gFjEeGXCZnI0Rpi+7dKpmyu90kdUY7dRUFpHTdu9zZceEzFLl38w==}
- '@rollup/rollup-android-arm-eabi@4.40.2':
- resolution: {integrity: sha512-JkdNEq+DFxZfUwxvB58tHMHBHVgX23ew41g1OQinthJ+ryhdRk67O31S7sYw8u2lTjHUPFxwar07BBt1KHp/hg==}
+ '@rollup/rollup-android-arm-eabi@4.50.1':
+ resolution: {integrity: sha512-HJXwzoZN4eYTdD8bVV22DN8gsPCAj3V20NHKOs8ezfXanGpmVPR7kalUHd+Y31IJp9stdB87VKPFbsGY3H/2ag==}
cpu: [arm]
os: [android]
- '@rollup/rollup-android-arm64@4.40.2':
- resolution: {integrity: sha512-13unNoZ8NzUmnndhPTkWPWbX3vtHodYmy+I9kuLxN+F+l+x3LdVF7UCu8TWVMt1POHLh6oDHhnOA04n8oJZhBw==}
+ '@rollup/rollup-android-arm64@4.50.1':
+ resolution: {integrity: sha512-PZlsJVcjHfcH53mOImyt3bc97Ep3FJDXRpk9sMdGX0qgLmY0EIWxCag6EigerGhLVuL8lDVYNnSo8qnTElO4xw==}
cpu: [arm64]
os: [android]
- '@rollup/rollup-darwin-arm64@4.40.2':
- resolution: {integrity: sha512-Gzf1Hn2Aoe8VZzevHostPX23U7N5+4D36WJNHK88NZHCJr7aVMG4fadqkIf72eqVPGjGc0HJHNuUaUcxiR+N/w==}
+ '@rollup/rollup-darwin-arm64@4.50.1':
+ resolution: {integrity: sha512-xc6i2AuWh++oGi4ylOFPmzJOEeAa2lJeGUGb4MudOtgfyyjr4UPNK+eEWTPLvmPJIY/pgw6ssFIox23SyrkkJw==}
cpu: [arm64]
os: [darwin]
- '@rollup/rollup-darwin-x64@4.40.2':
- resolution: {integrity: sha512-47N4hxa01a4x6XnJoskMKTS8XZ0CZMd8YTbINbi+w03A2w4j1RTlnGHOz/P0+Bg1LaVL6ufZyNprSg+fW5nYQQ==}
+ '@rollup/rollup-darwin-x64@4.50.1':
+ resolution: {integrity: sha512-2ofU89lEpDYhdLAbRdeyz/kX3Y2lpYc6ShRnDjY35bZhd2ipuDMDi6ZTQ9NIag94K28nFMofdnKeHR7BT0CATw==}
cpu: [x64]
os: [darwin]
- '@rollup/rollup-freebsd-arm64@4.40.2':
- resolution: {integrity: sha512-8t6aL4MD+rXSHHZUR1z19+9OFJ2rl1wGKvckN47XFRVO+QL/dUSpKA2SLRo4vMg7ELA8pzGpC+W9OEd1Z/ZqoQ==}
+ '@rollup/rollup-freebsd-arm64@4.50.1':
+ resolution: {integrity: sha512-wOsE6H2u6PxsHY/BeFHA4VGQN3KUJFZp7QJBmDYI983fgxq5Th8FDkVuERb2l9vDMs1D5XhOrhBrnqcEY6l8ZA==}
cpu: [arm64]
os: [freebsd]
- '@rollup/rollup-freebsd-x64@4.40.2':
- resolution: {integrity: sha512-C+AyHBzfpsOEYRFjztcYUFsH4S7UsE9cDtHCtma5BK8+ydOZYgMmWg1d/4KBytQspJCld8ZIujFMAdKG1xyr4Q==}
+ '@rollup/rollup-freebsd-x64@4.50.1':
+ resolution: {integrity: sha512-A/xeqaHTlKbQggxCqispFAcNjycpUEHP52mwMQZUNqDUJFFYtPHCXS1VAG29uMlDzIVr+i00tSFWFLivMcoIBQ==}
cpu: [x64]
os: [freebsd]
- '@rollup/rollup-linux-arm-gnueabihf@4.40.2':
- resolution: {integrity: sha512-de6TFZYIvJwRNjmW3+gaXiZ2DaWL5D5yGmSYzkdzjBDS3W+B9JQ48oZEsmMvemqjtAFzE16DIBLqd6IQQRuG9Q==}
+ '@rollup/rollup-linux-arm-gnueabihf@4.50.1':
+ resolution: {integrity: sha512-54v4okehwl5TaSIkpp97rAHGp7t3ghinRd/vyC1iXqXMfjYUTm7TfYmCzXDoHUPTTf36L8pr0E7YsD3CfB3ZDg==}
cpu: [arm]
os: [linux]
- '@rollup/rollup-linux-arm-musleabihf@4.40.2':
- resolution: {integrity: sha512-urjaEZubdIkacKc930hUDOfQPysezKla/O9qV+O89enqsqUmQm8Xj8O/vh0gHg4LYfv7Y7UsE3QjzLQzDYN1qg==}
+ '@rollup/rollup-linux-arm-musleabihf@4.50.1':
+ resolution: {integrity: sha512-p/LaFyajPN/0PUHjv8TNyxLiA7RwmDoVY3flXHPSzqrGcIp/c2FjwPPP5++u87DGHtw+5kSH5bCJz0mvXngYxw==}
cpu: [arm]
os: [linux]
- '@rollup/rollup-linux-arm64-gnu@4.40.2':
- resolution: {integrity: sha512-KlE8IC0HFOC33taNt1zR8qNlBYHj31qGT1UqWqtvR/+NuCVhfufAq9fxO8BMFC22Wu0rxOwGVWxtCMvZVLmhQg==}
+ '@rollup/rollup-linux-arm64-gnu@4.50.1':
+ resolution: {integrity: sha512-2AbMhFFkTo6Ptna1zO7kAXXDLi7H9fGTbVaIq2AAYO7yzcAsuTNWPHhb2aTA6GPiP+JXh85Y8CiS54iZoj4opw==}
cpu: [arm64]
os: [linux]
- '@rollup/rollup-linux-arm64-musl@4.40.2':
- resolution: {integrity: sha512-j8CgxvfM0kbnhu4XgjnCWJQyyBOeBI1Zq91Z850aUddUmPeQvuAy6OiMdPS46gNFgy8gN1xkYyLgwLYZG3rBOg==}
+ '@rollup/rollup-linux-arm64-musl@4.50.1':
+ resolution: {integrity: sha512-Cgef+5aZwuvesQNw9eX7g19FfKX5/pQRIyhoXLCiBOrWopjo7ycfB292TX9MDcDijiuIJlx1IzJz3IoCPfqs9w==}
cpu: [arm64]
os: [linux]
- '@rollup/rollup-linux-loongarch64-gnu@4.40.2':
- resolution: {integrity: sha512-Ybc/1qUampKuRF4tQXc7G7QY9YRyeVSykfK36Y5Qc5dmrIxwFhrOzqaVTNoZygqZ1ZieSWTibfFhQ5qK8jpWxw==}
+ '@rollup/rollup-linux-loongarch64-gnu@4.50.1':
+ resolution: {integrity: sha512-RPhTwWMzpYYrHrJAS7CmpdtHNKtt2Ueo+BlLBjfZEhYBhK00OsEqM08/7f+eohiF6poe0YRDDd8nAvwtE/Y62Q==}
cpu: [loong64]
os: [linux]
- '@rollup/rollup-linux-powerpc64le-gnu@4.40.2':
- resolution: {integrity: sha512-3FCIrnrt03CCsZqSYAOW/k9n625pjpuMzVfeI+ZBUSDT3MVIFDSPfSUgIl9FqUftxcUXInvFah79hE1c9abD+Q==}
+ '@rollup/rollup-linux-ppc64-gnu@4.50.1':
+ resolution: {integrity: sha512-eSGMVQw9iekut62O7eBdbiccRguuDgiPMsw++BVUg+1K7WjZXHOg/YOT9SWMzPZA+w98G+Fa1VqJgHZOHHnY0Q==}
cpu: [ppc64]
os: [linux]
- '@rollup/rollup-linux-riscv64-gnu@4.40.2':
- resolution: {integrity: sha512-QNU7BFHEvHMp2ESSY3SozIkBPaPBDTsfVNGx3Xhv+TdvWXFGOSH2NJvhD1zKAT6AyuuErJgbdvaJhYVhVqrWTg==}
+ '@rollup/rollup-linux-riscv64-gnu@4.50.1':
+ resolution: {integrity: sha512-S208ojx8a4ciIPrLgazF6AgdcNJzQE4+S9rsmOmDJkusvctii+ZvEuIC4v/xFqzbuP8yDjn73oBlNDgF6YGSXQ==}
cpu: [riscv64]
os: [linux]
- '@rollup/rollup-linux-riscv64-musl@4.40.2':
- resolution: {integrity: sha512-5W6vNYkhgfh7URiXTO1E9a0cy4fSgfE4+Hl5agb/U1sa0kjOLMLC1wObxwKxecE17j0URxuTrYZZME4/VH57Hg==}
+ '@rollup/rollup-linux-riscv64-musl@4.50.1':
+ resolution: {integrity: sha512-3Ag8Ls1ggqkGUvSZWYcdgFwriy2lWo+0QlYgEFra/5JGtAd6C5Hw59oojx1DeqcA2Wds2ayRgvJ4qxVTzCHgzg==}
cpu: [riscv64]
os: [linux]
- '@rollup/rollup-linux-s390x-gnu@4.40.2':
- resolution: {integrity: sha512-B7LKIz+0+p348JoAL4X/YxGx9zOx3sR+o6Hj15Y3aaApNfAshK8+mWZEf759DXfRLeL2vg5LYJBB7DdcleYCoQ==}
+ '@rollup/rollup-linux-s390x-gnu@4.50.1':
+ resolution: {integrity: sha512-t9YrKfaxCYe7l7ldFERE1BRg/4TATxIg+YieHQ966jwvo7ddHJxPj9cNFWLAzhkVsbBvNA4qTbPVNsZKBO4NSg==}
cpu: [s390x]
os: [linux]
- '@rollup/rollup-linux-x64-gnu@4.40.2':
- resolution: {integrity: sha512-lG7Xa+BmBNwpjmVUbmyKxdQJ3Q6whHjMjzQplOs5Z+Gj7mxPtWakGHqzMqNER68G67kmCX9qX57aRsW5V0VOng==}
+ '@rollup/rollup-linux-x64-gnu@4.50.1':
+ resolution: {integrity: sha512-MCgtFB2+SVNuQmmjHf+wfI4CMxy3Tk8XjA5Z//A0AKD7QXUYFMQcns91K6dEHBvZPCnhJSyDWLApk40Iq/H3tA==}
cpu: [x64]
os: [linux]
- '@rollup/rollup-linux-x64-musl@4.40.2':
- resolution: {integrity: sha512-tD46wKHd+KJvsmije4bUskNuvWKFcTOIM9tZ/RrmIvcXnbi0YK/cKS9FzFtAm7Oxi2EhV5N2OpfFB348vSQRXA==}
+ '@rollup/rollup-linux-x64-musl@4.50.1':
+ resolution: {integrity: sha512-nEvqG+0jeRmqaUMuwzlfMKwcIVffy/9KGbAGyoa26iu6eSngAYQ512bMXuqqPrlTyfqdlB9FVINs93j534UJrg==}
cpu: [x64]
os: [linux]
- '@rollup/rollup-win32-arm64-msvc@4.40.2':
- resolution: {integrity: sha512-Bjv/HG8RRWLNkXwQQemdsWw4Mg+IJ29LK+bJPW2SCzPKOUaMmPEppQlu/Fqk1d7+DX3V7JbFdbkh/NMmurT6Pg==}
+ '@rollup/rollup-openharmony-arm64@4.50.1':
+ resolution: {integrity: sha512-RDsLm+phmT3MJd9SNxA9MNuEAO/J2fhW8GXk62G/B4G7sLVumNFbRwDL6v5NrESb48k+QMqdGbHgEtfU0LCpbA==}
+ cpu: [arm64]
+ os: [openharmony]
+
+ '@rollup/rollup-win32-arm64-msvc@4.50.1':
+ resolution: {integrity: sha512-hpZB/TImk2FlAFAIsoElM3tLzq57uxnGYwplg6WDyAxbYczSi8O2eQ+H2Lx74504rwKtZ3N2g4bCUkiamzS6TQ==}
cpu: [arm64]
os: [win32]
- '@rollup/rollup-win32-ia32-msvc@4.40.2':
- resolution: {integrity: sha512-dt1llVSGEsGKvzeIO76HToiYPNPYPkmjhMHhP00T9S4rDern8P2ZWvWAQUEJ+R1UdMWJ/42i/QqJ2WV765GZcA==}
+ '@rollup/rollup-win32-ia32-msvc@4.50.1':
+ resolution: {integrity: sha512-SXjv8JlbzKM0fTJidX4eVsH+Wmnp0/WcD8gJxIZyR6Gay5Qcsmdbi9zVtnbkGPG8v2vMR1AD06lGWy5FLMcG7A==}
cpu: [ia32]
os: [win32]
- '@rollup/rollup-win32-x64-msvc@4.40.2':
- resolution: {integrity: sha512-bwspbWB04XJpeElvsp+DCylKfF4trJDa2Y9Go8O6A7YLX2LIKGcNK/CYImJN6ZP4DcuOHB4Utl3iCbnR62DudA==}
+ '@rollup/rollup-win32-x64-msvc@4.50.1':
+ resolution: {integrity: sha512-StxAO/8ts62KZVRAm4JZYq9+NqNsV7RvimNK+YM7ry//zebEH6meuugqW/P5OFUCjyQgui+9fUxT6d5NShvMvA==}
cpu: [x64]
os: [win32]
@@ -1224,8 +1229,8 @@ packages:
resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==}
engines: {node: '>= 10'}
- '@types/estree@1.0.7':
- resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==}
+ '@types/estree@1.0.8':
+ resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
'@types/flexsearch@0.7.6':
resolution: {integrity: sha512-H5IXcRn96/gaDmo+rDl2aJuIJsob8dgOXDqf8K0t8rWZd1AFNaaspmRsElESiU+EWE33qfbFPgI0OC/B1g9FCA==}
@@ -1278,7 +1283,7 @@ packages:
resolution: {integrity: sha512-N2XSI2n3sQqp5w7Y/AN/L2XDjBIRGqXko+eDp42sydYSBeJuSm5a1sLf8zakmo8u7tA8NmBgoDLA1HeOESjp9A==}
engines: {node: ^18.0.0 || >=20.0.0}
peerDependencies:
- vite: 5.4.19
+ vite: 5.4.20
vue: ^3.2.25
'@vitest/coverage-v8@3.0.5':
@@ -1297,7 +1302,7 @@ packages:
resolution: {integrity: sha512-CLPNBFBIE7x6aEGbIjaQAX03ZZlBMaWwAjBdMkIf/cAn6xzLTiM3zYqO/WAbieEjsAZir6tO71mzeHZoodThvw==}
peerDependencies:
msw: ^2.4.9
- vite: 5.4.19
+ vite: 5.4.20
peerDependenciesMeta:
msw:
optional: true
@@ -2595,7 +2600,7 @@ packages:
resolution: {integrity: sha512-DiRMSIgj340z+zikqf0f3Pj0CTv2/xtdBMBIAO1EARat+QXxMwumbfK41Gi7f9IIBr+UVmomNcwFxVY2EM/vrw==}
hasBin: true
peerDependencies:
- vite: 5.4.19
+ vite: 5.4.20
hotkeys-js@3.8.7:
resolution: {integrity: sha512-ckAx3EkUr5XjDwjEHDorHxRO2Kb7z6Z2Sxul4MbBkN8Nho7XDslQsgMJT+CiJ5Z4TgRxxvKHEpuLE3imzqy4Lg==}
@@ -3214,6 +3219,11 @@ packages:
mz@2.7.0:
resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==}
+ nanoid@3.3.11:
+ resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
+ engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+ hasBin: true
+
nanoid@3.3.8:
resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
@@ -3680,8 +3690,8 @@ packages:
resolution: {integrity: sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==}
engines: {node: ^10 || ^12 || >=14}
- postcss@8.5.3:
- resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==}
+ postcss@8.5.6:
+ resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==}
engines: {node: ^10 || ^12 || >=14}
posthog-js@1.260.3:
@@ -3862,8 +3872,8 @@ packages:
deprecated: Rimraf versions prior to v4 are no longer supported
hasBin: true
- rollup@4.40.2:
- resolution: {integrity: sha512-tfUOg6DTP4rhQ3VjOO6B4wyrJnGOX85requAXvqYTHsOgb2TFJdZ3aWpT8W2kPoypSGP7dZUyzxJ9ee4buM5Fg==}
+ rollup@4.50.1:
+ resolution: {integrity: sha512-78E9voJHwnXQMiQdiqswVLZwJIzdBKJ1GdI5Zx6XwoFKUIk09/sSrr+05QFzvYb8q6Y9pPV45zzDuYa3907TZA==}
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
hasBin: true
@@ -4364,10 +4374,10 @@ packages:
vite-plugin-ruby@5.0.0:
resolution: {integrity: sha512-c8PjTp21Ah/ttgnNUyu0qvCXZI08Jr9I24oUKg3TRIRhF5GcOZ++6wtlTCrNFd9COEQbpXHxlRIXd/MEg0iZJw==}
peerDependencies:
- vite: 5.4.19
+ vite: 5.4.20
- vite@5.4.19:
- resolution: {integrity: sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==}
+ vite@5.4.20:
+ resolution: {integrity: sha512-j3lYzGC3P+B5Yfy/pfKNgVEg4+UtcIJcVRt2cDjIOmhLourAqPqf8P7acgxeiSgUB7E3p2P8/3gNIgDLpwzs4g==}
engines: {node: ^18.0.0 || >=20.0.0}
hasBin: true
peerDependencies:
@@ -4771,7 +4781,7 @@ snapshots:
prosemirror-utils: 1.2.2(prosemirror-model@1.22.3)(prosemirror-state@1.4.3)
prosemirror-view: 1.34.1
- '@chatwoot/utils@0.0.50':
+ '@chatwoot/utils@0.0.51':
dependencies:
date-fns: 2.30.0
@@ -5188,10 +5198,10 @@ snapshots:
highlight.js: 11.10.0
vue: 3.5.12(typescript@5.6.2)
- '@histoire/app@0.17.17(vite@5.4.19(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0))':
+ '@histoire/app@0.17.17(vite@5.4.20(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0))':
dependencies:
- '@histoire/controls': 0.17.17(vite@5.4.19(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0))
- '@histoire/shared': 0.17.17(vite@5.4.19(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0))
+ '@histoire/controls': 0.17.17(vite@5.4.20(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0))
+ '@histoire/shared': 0.17.17(vite@5.4.20(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0))
'@histoire/vendors': 0.17.17
'@types/flexsearch': 0.7.6
flexsearch: 0.7.21
@@ -5199,7 +5209,7 @@ snapshots:
transitivePeerDependencies:
- vite
- '@histoire/controls@0.17.17(vite@5.4.19(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0))':
+ '@histoire/controls@0.17.17(vite@5.4.20(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0))':
dependencies:
'@codemirror/commands': 6.7.0
'@codemirror/lang-json': 6.0.1
@@ -5208,26 +5218,26 @@ snapshots:
'@codemirror/state': 6.4.1
'@codemirror/theme-one-dark': 6.1.2
'@codemirror/view': 6.34.1
- '@histoire/shared': 0.17.17(vite@5.4.19(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0))
+ '@histoire/shared': 0.17.17(vite@5.4.20(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0))
'@histoire/vendors': 0.17.17
transitivePeerDependencies:
- vite
- '@histoire/plugin-vue@0.17.15(histoire@0.17.15(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0)(vite@5.4.19(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0)))(vite@5.4.19(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0))(vue@3.5.12(typescript@5.6.2))':
+ '@histoire/plugin-vue@0.17.15(histoire@0.17.15(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0)(vite@5.4.20(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0)))(vite@5.4.20(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0))(vue@3.5.12(typescript@5.6.2))':
dependencies:
- '@histoire/controls': 0.17.17(vite@5.4.19(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0))
- '@histoire/shared': 0.17.17(vite@5.4.19(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0))
+ '@histoire/controls': 0.17.17(vite@5.4.20(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0))
+ '@histoire/shared': 0.17.17(vite@5.4.20(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0))
'@histoire/vendors': 0.17.17
change-case: 4.1.2
globby: 13.2.2
- histoire: 0.17.15(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0)(vite@5.4.19(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0))
+ histoire: 0.17.15(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0)(vite@5.4.20(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0))
launch-editor: 2.9.1
pathe: 1.1.2
vue: 3.5.12(typescript@5.6.2)
transitivePeerDependencies:
- vite
- '@histoire/shared@0.17.17(vite@5.4.19(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0))':
+ '@histoire/shared@0.17.17(vite@5.4.20(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0))':
dependencies:
'@histoire/vendors': 0.17.17
'@types/fs-extra': 9.0.13
@@ -5235,7 +5245,7 @@ snapshots:
chokidar: 3.6.0
pathe: 1.1.2
picocolors: 1.1.0
- vite: 5.4.19(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0)
+ vite: 5.4.20(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0)
'@histoire/vendors@0.17.17': {}
@@ -5349,10 +5359,10 @@ snapshots:
'@istanbuljs/schema@0.1.3': {}
- '@jridgewell/gen-mapping@0.3.12':
+ '@jridgewell/gen-mapping@0.3.13':
dependencies:
- '@jridgewell/sourcemap-codec': 1.5.4
- '@jridgewell/trace-mapping': 0.3.29
+ '@jridgewell/sourcemap-codec': 1.5.5
+ '@jridgewell/trace-mapping': 0.3.30
optional: true
'@jridgewell/gen-mapping@0.3.5':
@@ -5374,15 +5384,15 @@ snapshots:
'@jridgewell/set-array@1.2.1': {}
- '@jridgewell/source-map@0.3.10':
+ '@jridgewell/source-map@0.3.11':
dependencies:
- '@jridgewell/gen-mapping': 0.3.12
- '@jridgewell/trace-mapping': 0.3.29
+ '@jridgewell/gen-mapping': 0.3.13
+ '@jridgewell/trace-mapping': 0.3.30
optional: true
'@jridgewell/sourcemap-codec@1.5.0': {}
- '@jridgewell/sourcemap-codec@1.5.4':
+ '@jridgewell/sourcemap-codec@1.5.5':
optional: true
'@jridgewell/trace-mapping@0.3.25':
@@ -5390,10 +5400,10 @@ snapshots:
'@jridgewell/resolve-uri': 3.1.1
'@jridgewell/sourcemap-codec': 1.5.0
- '@jridgewell/trace-mapping@0.3.29':
+ '@jridgewell/trace-mapping@0.3.30':
dependencies:
'@jridgewell/resolve-uri': 3.1.2
- '@jridgewell/sourcemap-codec': 1.5.4
+ '@jridgewell/sourcemap-codec': 1.5.5
optional: true
'@kurkle/color@0.3.2': {}
@@ -5456,64 +5466,67 @@ snapshots:
'@rails/ujs@7.1.400': {}
- '@rollup/rollup-android-arm-eabi@4.40.2':
+ '@rollup/rollup-android-arm-eabi@4.50.1':
optional: true
- '@rollup/rollup-android-arm64@4.40.2':
+ '@rollup/rollup-android-arm64@4.50.1':
optional: true
- '@rollup/rollup-darwin-arm64@4.40.2':
+ '@rollup/rollup-darwin-arm64@4.50.1':
optional: true
- '@rollup/rollup-darwin-x64@4.40.2':
+ '@rollup/rollup-darwin-x64@4.50.1':
optional: true
- '@rollup/rollup-freebsd-arm64@4.40.2':
+ '@rollup/rollup-freebsd-arm64@4.50.1':
optional: true
- '@rollup/rollup-freebsd-x64@4.40.2':
+ '@rollup/rollup-freebsd-x64@4.50.1':
optional: true
- '@rollup/rollup-linux-arm-gnueabihf@4.40.2':
+ '@rollup/rollup-linux-arm-gnueabihf@4.50.1':
optional: true
- '@rollup/rollup-linux-arm-musleabihf@4.40.2':
+ '@rollup/rollup-linux-arm-musleabihf@4.50.1':
optional: true
- '@rollup/rollup-linux-arm64-gnu@4.40.2':
+ '@rollup/rollup-linux-arm64-gnu@4.50.1':
optional: true
- '@rollup/rollup-linux-arm64-musl@4.40.2':
+ '@rollup/rollup-linux-arm64-musl@4.50.1':
optional: true
- '@rollup/rollup-linux-loongarch64-gnu@4.40.2':
+ '@rollup/rollup-linux-loongarch64-gnu@4.50.1':
optional: true
- '@rollup/rollup-linux-powerpc64le-gnu@4.40.2':
+ '@rollup/rollup-linux-ppc64-gnu@4.50.1':
optional: true
- '@rollup/rollup-linux-riscv64-gnu@4.40.2':
+ '@rollup/rollup-linux-riscv64-gnu@4.50.1':
optional: true
- '@rollup/rollup-linux-riscv64-musl@4.40.2':
+ '@rollup/rollup-linux-riscv64-musl@4.50.1':
optional: true
- '@rollup/rollup-linux-s390x-gnu@4.40.2':
+ '@rollup/rollup-linux-s390x-gnu@4.50.1':
optional: true
- '@rollup/rollup-linux-x64-gnu@4.40.2':
+ '@rollup/rollup-linux-x64-gnu@4.50.1':
optional: true
- '@rollup/rollup-linux-x64-musl@4.40.2':
+ '@rollup/rollup-linux-x64-musl@4.50.1':
optional: true
- '@rollup/rollup-win32-arm64-msvc@4.40.2':
+ '@rollup/rollup-openharmony-arm64@4.50.1':
optional: true
- '@rollup/rollup-win32-ia32-msvc@4.40.2':
+ '@rollup/rollup-win32-arm64-msvc@4.50.1':
optional: true
- '@rollup/rollup-win32-x64-msvc@4.40.2':
+ '@rollup/rollup-win32-ia32-msvc@4.50.1':
+ optional: true
+
+ '@rollup/rollup-win32-x64-msvc@4.50.1':
optional: true
'@rtsao/scc@1.1.0': {}
@@ -5606,7 +5619,7 @@ snapshots:
'@tootallnate/once@2.0.0': {}
- '@types/estree@1.0.7': {}
+ '@types/estree@1.0.8': {}
'@types/flexsearch@0.7.6': {}
@@ -5664,9 +5677,9 @@ snapshots:
global: 4.4.0
is-function: 1.0.2
- '@vitejs/plugin-vue@5.1.4(vite@5.4.19(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0))(vue@3.5.12(typescript@5.6.2))':
+ '@vitejs/plugin-vue@5.1.4(vite@5.4.20(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0))(vue@3.5.12(typescript@5.6.2))':
dependencies:
- vite: 5.4.19(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0)
+ vite: 5.4.20(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0)
vue: 3.5.12(typescript@5.6.2)
'@vitest/coverage-v8@3.0.5(vitest@3.0.5(@types/node@22.7.0)(jsdom@24.1.3)(sass@1.79.3)(terser@5.33.0))':
@@ -5694,13 +5707,13 @@ snapshots:
chai: 5.1.2
tinyrainbow: 2.0.0
- '@vitest/mocker@3.0.5(vite@5.4.19(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0))':
+ '@vitest/mocker@3.0.5(vite@5.4.20(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0))':
dependencies:
'@vitest/spy': 3.0.5
estree-walker: 3.0.3
magic-string: 0.30.17
optionalDependencies:
- vite: 5.4.19(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0)
+ vite: 5.4.20(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0)
'@vitest/pretty-format@3.0.5':
dependencies:
@@ -5775,7 +5788,7 @@ snapshots:
'@vue/shared': 3.5.12
estree-walker: 2.0.2
magic-string: 0.30.17
- postcss: 8.5.3
+ postcss: 8.5.6
source-map-js: 1.2.1
'@vue/compiler-sfc@3.5.13':
@@ -5787,7 +5800,7 @@ snapshots:
'@vue/shared': 3.5.13
estree-walker: 2.0.2
magic-string: 0.30.17
- postcss: 8.5.3
+ postcss: 8.5.6
source-map-js: 1.2.1
'@vue/compiler-sfc@3.5.8':
@@ -6948,7 +6961,7 @@ snapshots:
estree-walker@3.0.3:
dependencies:
- '@types/estree': 1.0.7
+ '@types/estree': 1.0.8
esutils@2.0.3: {}
@@ -7272,12 +7285,12 @@ snapshots:
highlight.js@11.10.0: {}
- histoire@0.17.15(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0)(vite@5.4.19(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0)):
+ histoire@0.17.15(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0)(vite@5.4.20(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0)):
dependencies:
'@akryum/tinypool': 0.3.1
- '@histoire/app': 0.17.17(vite@5.4.19(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0))
- '@histoire/controls': 0.17.17(vite@5.4.19(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0))
- '@histoire/shared': 0.17.17(vite@5.4.19(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0))
+ '@histoire/app': 0.17.17(vite@5.4.20(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0))
+ '@histoire/controls': 0.17.17(vite@5.4.20(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0))
+ '@histoire/shared': 0.17.17(vite@5.4.20(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0))
'@histoire/vendors': 0.17.17
'@types/flexsearch': 0.7.6
'@types/markdown-it': 12.2.3
@@ -7304,7 +7317,7 @@ snapshots:
sade: 1.8.1
shiki-es: 0.2.0
sirv: 2.0.4
- vite: 5.4.19(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0)
+ vite: 5.4.20(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0)
vite-node: 2.0.1(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0)
transitivePeerDependencies:
- '@types/node'
@@ -7983,6 +7996,8 @@ snapshots:
object-assign: 4.1.1
thenify-all: 1.6.0
+ nanoid@3.3.11: {}
+
nanoid@3.3.8: {}
nanospinner@1.1.0:
@@ -8470,9 +8485,9 @@ snapshots:
picocolors: 1.1.0
source-map-js: 1.2.1
- postcss@8.5.3:
+ postcss@8.5.6:
dependencies:
- nanoid: 3.3.8
+ nanoid: 3.3.11
picocolors: 1.1.1
source-map-js: 1.2.1
@@ -8667,30 +8682,31 @@ snapshots:
dependencies:
glob: 7.2.3
- rollup@4.40.2:
+ rollup@4.50.1:
dependencies:
- '@types/estree': 1.0.7
+ '@types/estree': 1.0.8
optionalDependencies:
- '@rollup/rollup-android-arm-eabi': 4.40.2
- '@rollup/rollup-android-arm64': 4.40.2
- '@rollup/rollup-darwin-arm64': 4.40.2
- '@rollup/rollup-darwin-x64': 4.40.2
- '@rollup/rollup-freebsd-arm64': 4.40.2
- '@rollup/rollup-freebsd-x64': 4.40.2
- '@rollup/rollup-linux-arm-gnueabihf': 4.40.2
- '@rollup/rollup-linux-arm-musleabihf': 4.40.2
- '@rollup/rollup-linux-arm64-gnu': 4.40.2
- '@rollup/rollup-linux-arm64-musl': 4.40.2
- '@rollup/rollup-linux-loongarch64-gnu': 4.40.2
- '@rollup/rollup-linux-powerpc64le-gnu': 4.40.2
- '@rollup/rollup-linux-riscv64-gnu': 4.40.2
- '@rollup/rollup-linux-riscv64-musl': 4.40.2
- '@rollup/rollup-linux-s390x-gnu': 4.40.2
- '@rollup/rollup-linux-x64-gnu': 4.40.2
- '@rollup/rollup-linux-x64-musl': 4.40.2
- '@rollup/rollup-win32-arm64-msvc': 4.40.2
- '@rollup/rollup-win32-ia32-msvc': 4.40.2
- '@rollup/rollup-win32-x64-msvc': 4.40.2
+ '@rollup/rollup-android-arm-eabi': 4.50.1
+ '@rollup/rollup-android-arm64': 4.50.1
+ '@rollup/rollup-darwin-arm64': 4.50.1
+ '@rollup/rollup-darwin-x64': 4.50.1
+ '@rollup/rollup-freebsd-arm64': 4.50.1
+ '@rollup/rollup-freebsd-x64': 4.50.1
+ '@rollup/rollup-linux-arm-gnueabihf': 4.50.1
+ '@rollup/rollup-linux-arm-musleabihf': 4.50.1
+ '@rollup/rollup-linux-arm64-gnu': 4.50.1
+ '@rollup/rollup-linux-arm64-musl': 4.50.1
+ '@rollup/rollup-linux-loongarch64-gnu': 4.50.1
+ '@rollup/rollup-linux-ppc64-gnu': 4.50.1
+ '@rollup/rollup-linux-riscv64-gnu': 4.50.1
+ '@rollup/rollup-linux-riscv64-musl': 4.50.1
+ '@rollup/rollup-linux-s390x-gnu': 4.50.1
+ '@rollup/rollup-linux-x64-gnu': 4.50.1
+ '@rollup/rollup-linux-x64-musl': 4.50.1
+ '@rollup/rollup-openharmony-arm64': 4.50.1
+ '@rollup/rollup-win32-arm64-msvc': 4.50.1
+ '@rollup/rollup-win32-ia32-msvc': 4.50.1
+ '@rollup/rollup-win32-x64-msvc': 4.50.1
fsevents: 2.3.3
rope-sequence@1.3.2: {}
@@ -9027,7 +9043,7 @@ snapshots:
terser@5.33.0:
dependencies:
- '@jridgewell/source-map': 0.3.10
+ '@jridgewell/source-map': 0.3.11
acorn: 8.15.0
commander: 2.20.3
source-map-support: 0.5.21
@@ -9269,7 +9285,7 @@ snapshots:
debug: 4.4.0
pathe: 1.1.2
picocolors: 1.1.1
- vite: 5.4.19(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0)
+ vite: 5.4.20(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0)
transitivePeerDependencies:
- '@types/node'
- less
@@ -9281,19 +9297,19 @@ snapshots:
- supports-color
- terser
- vite-plugin-ruby@5.0.0(vite@5.4.19(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0)):
+ vite-plugin-ruby@5.0.0(vite@5.4.20(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0)):
dependencies:
debug: 4.3.5
fast-glob: 3.3.2
- vite: 5.4.19(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0)
+ vite: 5.4.20(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0)
transitivePeerDependencies:
- supports-color
- vite@5.4.19(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0):
+ vite@5.4.20(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0):
dependencies:
esbuild: 0.21.5
- postcss: 8.5.3
- rollup: 4.40.2
+ postcss: 8.5.6
+ rollup: 4.50.1
optionalDependencies:
'@types/node': 22.7.0
fsevents: 2.3.3
@@ -9303,7 +9319,7 @@ snapshots:
vitest@3.0.5(@types/node@22.7.0)(jsdom@24.1.3)(sass@1.79.3)(terser@5.33.0):
dependencies:
'@vitest/expect': 3.0.5
- '@vitest/mocker': 3.0.5(vite@5.4.19(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0))
+ '@vitest/mocker': 3.0.5(vite@5.4.20(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0))
'@vitest/pretty-format': 3.0.5
'@vitest/runner': 3.0.5
'@vitest/snapshot': 3.0.5
@@ -9319,7 +9335,7 @@ snapshots:
tinyexec: 0.3.2
tinypool: 1.0.2
tinyrainbow: 2.0.0
- vite: 5.4.19(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0)
+ vite: 5.4.20(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0)
vite-node: 2.0.1(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0)
why-is-node-running: 2.3.0
optionalDependencies:
diff --git a/spec/builders/v2/report_builder_spec.rb b/spec/builders/v2/report_builder_spec.rb
index 2ad62e2d6..7498ce3ac 100644
--- a/spec/builders/v2/report_builder_spec.rb
+++ b/spec/builders/v2/report_builder_spec.rb
@@ -120,8 +120,38 @@ describe V2::ReportBuilder do
builder = described_class.new(account, params)
metrics = builder.timeseries
- # 4 conversations are resolved
- expect(metrics[Time.zone.today]).to be 4
+ # 5 resolution events occurred (even though 1 was later reopened)
+ expect(metrics[Time.zone.today]).to be 5
+ expect(metrics[Time.zone.today - 2.days]).to be 0
+ end
+ end
+
+ it 'return resolutions count with multiple resolutions of same conversation' do
+ travel_to(Time.zone.today) do
+ params = {
+ metric: 'resolutions_count',
+ type: :account,
+ since: (Time.zone.today - 3.days).to_time.to_i.to_s,
+ until: Time.zone.today.end_of_day.to_time.to_i.to_s
+ }
+
+ conversations = account.conversations.where('created_at < ?', 1.day.ago)
+ perform_enqueued_jobs do
+ # Resolve all 5 conversations (first round)
+ conversations.each(&:resolved!)
+
+ # Reopen 2 conversations and resolve them again
+ conversations.first(2).each do |conversation|
+ conversation.open!
+ conversation.resolved!
+ end
+ end
+
+ builder = described_class.new(account, params)
+ metrics = builder.timeseries
+
+ # 7 total resolution events: 5 initial + 2 re-resolutions
+ expect(metrics[Time.zone.today]).to be 7
expect(metrics[Time.zone.today - 2.days]).to be 0
end
end
@@ -153,10 +183,10 @@ describe V2::ReportBuilder do
metrics = builder.timeseries
summary = builder.bot_summary
- # 4 conversations are resolved
- expect(metrics[Time.zone.today]).to be 4
+ # 5 bot resolution events occurred (even though 1 was later reopened)
+ expect(metrics[Time.zone.today]).to be 5
expect(metrics[Time.zone.today - 2.days]).to be 0
- expect(summary[:bot_resolutions_count]).to be 4
+ expect(summary[:bot_resolutions_count]).to be 5
end
end
@@ -339,8 +369,40 @@ describe V2::ReportBuilder do
builder = described_class.new(account, params)
metrics = builder.timeseries
- # this should count only 4 since the last conversation was reopened
- expect(metrics[Time.zone.today]).to be 4
+ # this should count all 5 resolution events (even though 1 was later reopened)
+ expect(metrics[Time.zone.today]).to be 5
+ expect(metrics[Time.zone.today - 2.days]).to be 0
+ end
+ end
+
+ it 'return resolutions count with multiple resolutions of same conversation' do
+ travel_to(Time.zone.today) do
+ params = {
+ metric: 'resolutions_count',
+ type: :label,
+ id: label_2.id,
+ since: (Time.zone.today - 3.days).to_time.to_i.to_s,
+ until: (Time.zone.today + 1.day).to_time.to_i.to_s
+ }
+
+ conversations = account.conversations.where('created_at < ?', 1.day.ago)
+
+ perform_enqueued_jobs do
+ # Resolve all 5 conversations (first round)
+ conversations.each(&:resolved!)
+
+ # Reopen 3 conversations and resolve them again
+ conversations.first(3).each do |conversation|
+ conversation.open!
+ conversation.resolved!
+ end
+ end
+
+ builder = described_class.new(account, params)
+ metrics = builder.timeseries
+
+ # 8 total resolution events: 5 initial + 3 re-resolutions
+ expect(metrics[Time.zone.today]).to be 8
expect(metrics[Time.zone.today - 2.days]).to be 0
end
end
diff --git a/spec/builders/v2/reports/label_summary_builder_spec.rb b/spec/builders/v2/reports/label_summary_builder_spec.rb
index 1560008a1..f0eb6cefd 100644
--- a/spec/builders/v2/reports/label_summary_builder_spec.rb
+++ b/spec/builders/v2/reports/label_summary_builder_spec.rb
@@ -313,5 +313,61 @@ RSpec.describe V2::Reports::LabelSummaryBuilder do
expect(label_1_report[:avg_first_response_time]).to eq(1800.0)
end
end
+
+ context 'with resolution count with multiple resolutions of same conversation' do
+ let(:business_hours) { false }
+ let(:account2) { create(:account) }
+ let(:unique_label_name) { SecureRandom.uuid }
+ let(:test_label) { create(:label, title: unique_label_name, account: account2) }
+ let(:test_date) { Date.new(2025, 6, 15) }
+ let(:account2_builder) do
+ described_class.new(account: account2, params: {
+ business_hours: false,
+ since: test_date.to_time.to_i.to_s,
+ until: test_date.end_of_day.to_time.to_i.to_s,
+ timezone_offset: 0
+ })
+ end
+
+ before do
+ # Ensure test_label is created
+ test_label
+
+ travel_to(test_date) do
+ user = create(:user, account: account2)
+ inbox = create(:inbox, account: account2)
+ create(:inbox_member, user: user, inbox: inbox)
+
+ gravatar_url = 'https://www.gravatar.com'
+ stub_request(:get, /#{gravatar_url}.*/).to_return(status: 404)
+
+ perform_enqueued_jobs do
+ conversation = create(:conversation, account: account2,
+ inbox: inbox, assignee: user,
+ created_at: test_date)
+ conversation.update_labels(unique_label_name)
+ conversation.label_list
+ conversation.save!
+
+ # First resolution
+ conversation.resolved!
+
+ # Reopen conversation
+ conversation.open!
+
+ # Second resolution
+ conversation.resolved!
+ end
+ end
+ end
+
+ it 'counts multiple resolution events for same conversation' do
+ report = account2_builder.build
+
+ test_label_report = report.find { |r| r[:name] == unique_label_name }
+ expect(test_label_report).not_to be_nil
+ expect(test_label_report[:resolved_conversations_count]).to eq(2)
+ end
+ end
end
end
diff --git a/spec/builders/v2/reports/timeseries/count_report_builder_spec.rb b/spec/builders/v2/reports/timeseries/count_report_builder_spec.rb
new file mode 100644
index 000000000..038bd61c2
--- /dev/null
+++ b/spec/builders/v2/reports/timeseries/count_report_builder_spec.rb
@@ -0,0 +1,113 @@
+require 'rails_helper'
+
+describe V2::Reports::Timeseries::CountReportBuilder do
+ subject { described_class.new(account, params) }
+
+ let(:account) { create(:account) }
+ let(:account2) { create(:account) }
+ let(:user) { create(:user, email: 'agent1@example.com') }
+ let(:inbox) { create(:inbox, account: account) }
+ let(:inbox2) { create(:inbox, account: account2) }
+ let(:current_time) { Time.current }
+
+ let(:params) do
+ {
+ type: 'agent',
+ metric: 'resolutions_count',
+ since: (current_time - 1.day).beginning_of_day.to_i.to_s,
+ until: current_time.end_of_day.to_i.to_s,
+ id: user.id.to_s
+ }
+ end
+
+ before do
+ travel_to current_time
+
+ # Add the same user to both accounts
+ create(:account_user, account: account, user: user)
+ create(:account_user, account: account2, user: user)
+
+ # Create conversations in account1
+ conversation1 = create(:conversation, account: account, inbox: inbox, assignee: user)
+ conversation2 = create(:conversation, account: account, inbox: inbox, assignee: user)
+
+ # Create conversations in account2
+ conversation3 = create(:conversation, account: account2, inbox: inbox2, assignee: user)
+ conversation4 = create(:conversation, account: account2, inbox: inbox2, assignee: user)
+
+ # User resolves 2 conversations in account1
+ create(:reporting_event,
+ name: 'conversation_resolved',
+ account: account,
+ user: user,
+ conversation: conversation1,
+ created_at: current_time - 12.hours)
+
+ create(:reporting_event,
+ name: 'conversation_resolved',
+ account: account,
+ user: user,
+ conversation: conversation2,
+ created_at: current_time - 6.hours)
+
+ # Same user resolves 3 conversations in account2 - these should NOT be counted for account1
+ create(:reporting_event,
+ name: 'conversation_resolved',
+ account: account2,
+ user: user,
+ conversation: conversation3,
+ created_at: current_time - 8.hours)
+
+ create(:reporting_event,
+ name: 'conversation_resolved',
+ account: account2,
+ user: user,
+ conversation: conversation4,
+ created_at: current_time - 4.hours)
+
+ # Create another conversation in account2 for testing
+ conversation5 = create(:conversation, account: account2, inbox: inbox2, assignee: user)
+ create(:reporting_event,
+ name: 'conversation_resolved',
+ account: account2,
+ user: user,
+ conversation: conversation5,
+ created_at: current_time - 2.hours)
+ end
+
+ describe '#aggregate_value' do
+ it 'returns only resolutions performed by the user in the specified account' do
+ # User should have 2 resolutions in account1, not 5 (total across both accounts)
+ expect(subject.aggregate_value).to eq(2)
+ end
+
+ context 'when querying account2' do
+ subject { described_class.new(account2, params) }
+
+ it 'returns only resolutions for account2' do
+ # User should have 3 resolutions in account2
+ expect(subject.aggregate_value).to eq(3)
+ end
+ end
+ end
+
+ describe '#timeseries' do
+ it 'filters resolutions by account' do
+ result = subject.timeseries
+ # Should only count the 2 resolutions from account1
+ total_count = result.sum { |r| r[:value] }
+ expect(total_count).to eq(2)
+ end
+ end
+
+ describe 'account isolation' do
+ it 'does not leak data between accounts' do
+ # If account isolation works correctly, the counts should be different
+ account1_count = described_class.new(account, params).aggregate_value
+ account2_count = described_class.new(account2, params).aggregate_value
+
+ expect(account1_count).to eq(2)
+ expect(account2_count).to eq(3)
+ end
+ end
+end
diff --git a/spec/controllers/api/v1/accounts/portals_controller_spec.rb b/spec/controllers/api/v1/accounts/portals_controller_spec.rb
index d0ea13e2b..f38660706 100644
--- a/spec/controllers/api/v1/accounts/portals_controller_spec.rb
+++ b/spec/controllers/api/v1/accounts/portals_controller_spec.rb
@@ -154,6 +154,25 @@ RSpec.describe 'Api::V1::Accounts::Portals', type: :request do
portal.reload
expect(portal.archived).to be_truthy
end
+
+ it 'clears associated web widget when inbox selection is blank' do
+ web_widget_inbox = create(:inbox, account: account)
+ portal.update!(channel_web_widget: web_widget_inbox.channel)
+
+ expect(portal.channel_web_widget_id).to eq(web_widget_inbox.channel.id)
+
+ put "/api/v1/accounts/#{account.id}/portals/#{portal.slug}",
+ params: {
+ portal: { name: portal.name },
+ inbox_id: ''
+ },
+ headers: admin.create_new_auth_token
+
+ expect(response).to have_http_status(:success)
+ portal.reload
+ expect(portal.channel_web_widget_id).to be_nil
+ expect(response.parsed_body['inbox']).to be_nil
+ end
end
end
diff --git a/spec/controllers/api/v1/accounts/whatsapp/authorizations_controller_spec.rb b/spec/controllers/api/v1/accounts/whatsapp/authorizations_controller_spec.rb
index 2e74817a7..7beafb47b 100644
--- a/spec/controllers/api/v1/accounts/whatsapp/authorizations_controller_spec.rb
+++ b/spec/controllers/api/v1/accounts/whatsapp/authorizations_controller_spec.rb
@@ -16,31 +16,7 @@ RSpec.describe 'WhatsApp Authorization API', type: :request do
let(:agent) { create(:user, account: account, role: :agent) }
let(:administrator) { create(:user, account: account, role: :administrator) }
- context 'when feature is not enabled' do
- before do
- account.disable_features!(:whatsapp_embedded_signup)
- end
-
- it 'returns forbidden' do
- post "/api/v1/accounts/#{account.id}/whatsapp/authorization",
- params: {
- code: 'test_code',
- business_id: 'test_business_id',
- waba_id: 'test_waba_id'
- },
- headers: agent.create_new_auth_token,
- as: :json
-
- expect(response).to have_http_status(:forbidden)
- expect(response.parsed_body['error']).to eq('WhatsApp embedded signup is not enabled for this account')
- end
- end
-
- context 'when feature is enabled' do
- before do
- account.enable_features!(:whatsapp_embedded_signup)
- end
-
+ context 'when authenticated user makes request' do
it 'returns unprocessable entity when code is missing' do
post "/api/v1/accounts/#{account.id}/whatsapp/authorization",
params: {
@@ -246,10 +222,6 @@ RSpec.describe 'WhatsApp Authorization API', type: :request do
context 'when user is not authorized for the account' do
let(:other_account) { create(:account) }
- before do
- account.enable_features!(:whatsapp_embedded_signup)
- end
-
it 'returns unauthorized' do
post "/api/v1/accounts/#{other_account.id}/whatsapp/authorization",
params: {
@@ -265,10 +237,6 @@ RSpec.describe 'WhatsApp Authorization API', type: :request do
end
context 'when user is an administrator' do
- before do
- account.enable_features!(:whatsapp_embedded_signup)
- end
-
it 'allows channel creation' do
embedded_signup_service = instance_double(Whatsapp::EmbeddedSignupService)
whatsapp_channel = create(:channel_whatsapp, account: account, validate_provider_config: false, sync_templates: false)
@@ -321,10 +289,6 @@ RSpec.describe 'WhatsApp Authorization API', type: :request do
context 'when user is an administrator' do
let(:administrator) { create(:user, account: account, role: :administrator) }
- before do
- account.enable_features!(:whatsapp_embedded_signup)
- end
-
context 'with valid parameters' do
let(:valid_params) do
{
@@ -489,7 +453,6 @@ RSpec.describe 'WhatsApp Authorization API', type: :request do
let(:agent) { create(:user, account: account, role: :agent) }
before do
- account.enable_features!(:whatsapp_embedded_signup)
create(:inbox_member, inbox: whatsapp_inbox, user: agent)
end
diff --git a/spec/controllers/api/v2/accounts/reports_controller_spec.rb b/spec/controllers/api/v2/accounts/reports_controller_spec.rb
new file mode 100644
index 000000000..4dbbcf406
--- /dev/null
+++ b/spec/controllers/api/v2/accounts/reports_controller_spec.rb
@@ -0,0 +1,197 @@
+require 'rails_helper'
+
+RSpec.describe Api::V2::Accounts::ReportsController, type: :request do
+ let(:account) { create(:account) }
+ let(:admin) { create(:user, account: account, role: :administrator) }
+ let(:agent) { create(:user, account: account, role: :agent) }
+ let(:inbox) { create(:inbox, account: account) }
+
+ describe 'GET /api/v2/accounts/{account.id}/reports' do
+ context 'when authenticated and authorized' do
+ before do
+ # Create conversations across 24 hours at different times
+ base_time = Time.utc(2024, 1, 15, 0, 0) # Start at midnight UTC
+
+ # Create conversations every 4 hours across 24 hours
+ 6.times do |i|
+ time = base_time + (i * 4).hours
+ travel_to time do
+ conversation = create(:conversation, account: account, inbox: inbox, assignee: agent)
+ create(:message, account: account, conversation: conversation, message_type: :outgoing)
+ end
+ end
+ end
+
+ it 'timezone_offset affects data grouping and timestamps correctly' do
+ Time.use_zone('UTC') do
+ base_time = Time.utc(2024, 1, 15, 0, 0)
+ base_params = {
+ metric: 'conversations_count',
+ type: 'account',
+ since: (base_time - 1.day).to_i.to_s,
+ until: (base_time + 2.days).to_i.to_s,
+ group_by: 'day'
+ }
+
+ responses = [0, -8, 9].map do |offset|
+ get "/api/v2/accounts/#{account.id}/reports",
+ params: base_params.merge(timezone_offset: offset),
+ headers: admin.create_new_auth_token, as: :json
+ response.parsed_body
+ end
+
+ data_entries = responses.map { |r| r.select { |e| e['value'] > 0 } }
+ totals = responses.map { |r| r.sum { |e| e['value'] } }
+ timestamps = responses.map { |r| r.map { |e| e['timestamp'] } }
+
+ # Data conservation and redistribution
+ expect(totals.uniq).to eq([6])
+ expect(data_entries[0].map { |e| e['value'] }).to eq([1, 5])
+ expect(data_entries[1].map { |e| e['value'] }).to eq([3, 3])
+ expect(data_entries[2].map { |e| e['value'] }).to eq([4, 2])
+
+ # Timestamp differences
+ expect(timestamps.uniq.size).to eq(3)
+ timestamps[0].zip(timestamps[1]).each { |utc, pst| expect(utc - pst).to eq(-28_800) }
+ end
+ end
+
+ describe 'timezone_offset does not affect summary report totals' do
+ let(:base_time) { Time.utc(2024, 1, 15, 12, 0) }
+ let(:summary_params) do
+ {
+ type: 'account',
+ since: (base_time - 1.day).to_i.to_s,
+ until: (base_time + 1.day).to_i.to_s
+ }
+ end
+
+ let(:jst_params) do
+ # For JST: User wants "Jan 15 JST" which translates to:
+ # Jan 14 15:00 UTC to Jan 15 15:00 UTC (event NOT included)
+ {
+ type: 'account',
+ since: (Time.utc(2024, 1, 15, 0, 0) - 9.hours).to_i.to_s, # Jan 14 15:00 UTC
+ until: (Time.utc(2024, 1, 16, 0, 0) - 9.hours).to_i.to_s # Jan 15 15:00 UTC
+ }
+ end
+ let(:utc_params) do
+ # For UTC: Jan 15 00:00 UTC to Jan 16 00:00 UTC (event included)
+ {
+ type: 'account',
+ since: Time.utc(2024, 1, 15, 0, 0).to_i.to_s,
+ until: Time.utc(2024, 1, 16, 0, 0).to_i.to_s
+ }
+ end
+
+ it 'returns identical conversation counts across timezones' do
+ Time.use_zone('UTC') do
+ summaries = [-8, 0, 9].map do |offset|
+ get "/api/v2/accounts/#{account.id}/reports/summary",
+ params: summary_params.merge(timezone_offset: offset),
+ headers: admin.create_new_auth_token, as: :json
+ response.parsed_body
+ end
+
+ conversation_counts = summaries.map { |s| s['conversations_count'] }
+ expect(conversation_counts.uniq).to eq([6])
+ end
+ end
+
+ it 'returns identical message counts across timezones' do
+ Time.use_zone('UTC') do
+ get "/api/v2/accounts/#{account.id}/reports/summary",
+ params: summary_params.merge(timezone_offset: 0),
+ headers: admin.create_new_auth_token, as: :json
+ utc_summary = response.parsed_body
+
+ get "/api/v2/accounts/#{account.id}/reports/summary",
+ params: summary_params.merge(timezone_offset: -8),
+ headers: admin.create_new_auth_token, as: :json
+ pst_summary = response.parsed_body
+
+ expect(utc_summary['incoming_messages_count']).to eq(pst_summary['incoming_messages_count'])
+ expect(utc_summary['outgoing_messages_count']).to eq(pst_summary['outgoing_messages_count'])
+ end
+ end
+
+ it 'returns consistent resolution counts across timezones' do
+ Time.use_zone('UTC') do
+ get "/api/v2/accounts/#{account.id}/reports/summary",
+ params: summary_params.merge(timezone_offset: 0),
+ headers: admin.create_new_auth_token, as: :json
+ utc_summary = response.parsed_body
+
+ get "/api/v2/accounts/#{account.id}/reports/summary",
+ params: summary_params.merge(timezone_offset: 9),
+ headers: admin.create_new_auth_token, as: :json
+ jst_summary = response.parsed_body
+
+ expect(utc_summary['resolutions_count']).to eq(jst_summary['resolutions_count'])
+ end
+ end
+
+ it 'returns consistent previous period data across timezones' do
+ Time.use_zone('UTC') do
+ get "/api/v2/accounts/#{account.id}/reports/summary",
+ params: summary_params.merge(timezone_offset: 0),
+ headers: admin.create_new_auth_token, as: :json
+ utc_summary = response.parsed_body
+
+ get "/api/v2/accounts/#{account.id}/reports/summary",
+ params: summary_params.merge(timezone_offset: -8),
+ headers: admin.create_new_auth_token, as: :json
+ pst_summary = response.parsed_body
+
+ expect(utc_summary['previous']['conversations_count']).to eq(pst_summary['previous']['conversations_count']) if utc_summary['previous']
+ end
+ end
+
+ it 'summary reports work when frontend sends correct timezone boundaries' do
+ Time.use_zone('UTC') do
+ # Create a resolution event right at timezone boundary
+ boundary_time = Time.utc(2024, 1, 15, 23, 30) # 11:30 PM UTC on Jan 15
+ gravatar_url = 'https://www.gravatar.com'
+ stub_request(:get, /#{gravatar_url}.*/).to_return(status: 404)
+
+ travel_to boundary_time do
+ perform_enqueued_jobs do
+ conversation = create(:conversation, account: account, inbox: inbox, assignee: agent)
+ conversation.resolved!
+ end
+ end
+
+ get "/api/v2/accounts/#{account.id}/reports/summary",
+ params: jst_params.merge(timezone_offset: 9),
+ headers: admin.create_new_auth_token, as: :json
+ jst_summary = response.parsed_body
+
+ get "/api/v2/accounts/#{account.id}/reports/summary",
+ params: utc_params.merge(timezone_offset: 0),
+ headers: admin.create_new_auth_token, as: :json
+ utc_summary = response.parsed_body
+
+ expect(jst_summary['resolutions_count']).to eq(0)
+ expect(utc_summary['resolutions_count']).to eq(1)
+ end
+ end
+ end
+ end
+
+ context 'when unauthenticated' do
+ it 'returns unauthorized' do
+ get "/api/v2/accounts/#{account.id}/reports"
+ expect(response).to have_http_status(:unauthorized)
+ end
+ end
+
+ context 'when authenticated but not authorized' do
+ it 'returns forbidden' do
+ get "/api/v2/accounts/#{account.id}/reports",
+ headers: agent.create_new_auth_token,
+ as: :json
+ expect(response).to have_http_status(:unauthorized)
+ end
+ end
+ end
+end
diff --git a/spec/controllers/devise_overrides/sessions_controller_spec.rb b/spec/controllers/devise_overrides/sessions_controller_spec.rb
new file mode 100644
index 000000000..8ee012670
--- /dev/null
+++ b/spec/controllers/devise_overrides/sessions_controller_spec.rb
@@ -0,0 +1,166 @@
+require 'rails_helper'
+
+RSpec.describe DeviseOverrides::SessionsController, type: :controller do
+ include Devise::Test::ControllerHelpers
+
+ before do
+ request.env['devise.mapping'] = Devise.mappings[:user]
+ end
+
+ describe 'POST #create' do
+ let(:user) { create(:user, password: 'Test@123456') }
+
+ context 'with standard authentication' do
+ it 'authenticates with valid credentials' do
+ post :create, params: { email: user.email, password: 'Test@123456' }
+
+ expect(response).to have_http_status(:success)
+ end
+
+ it 'rejects invalid credentials' do
+ post :create, params: { email: user.email, password: 'wrong' }
+
+ expect(response).to have_http_status(:unauthorized)
+ end
+ end
+
+ context 'with MFA authentication' do
+ before do
+ skip('Skipping since MFA is not configured in this environment') unless Chatwoot.encryption_configured?
+ user.enable_two_factor!
+ user.update!(otp_required_for_login: true)
+ end
+
+ it 'requires MFA verification after successful password authentication' do
+ post :create, params: { email: user.email, password: 'Test@123456' }
+
+ expect(response).to have_http_status(:partial_content)
+ json_response = response.parsed_body
+ expect(json_response['mfa_required']).to be(true)
+ expect(json_response['mfa_token']).to be_present
+ end
+
+ it 'does not return authentication tokens before MFA verification' do
+ post :create, params: { email: user.email, password: 'Test@123456' }
+
+ expect(response).to have_http_status(:partial_content)
+
+ # Check that no authentication headers are present
+ expect(response.headers['access-token']).to be_nil
+ expect(response.headers['uid']).to be_nil
+ expect(response.headers['client']).to be_nil
+ expect(response.headers['Authorization']).to be_nil
+
+ # Check that no bearer token is present in any form
+ response.headers.each do |key, value|
+ expect(value.to_s).not_to include('Bearer') if key.downcase.include?('auth')
+ end
+
+ json_response = response.parsed_body
+ expect(json_response['data']).to be_nil
+ end
+
+ context 'when verifying MFA' do
+ let(:mfa_token) { Mfa::TokenService.new(user: user).generate_token }
+
+ it 'authenticates with valid OTP' do
+ post :create, params: {
+ mfa_token: mfa_token,
+ otp_code: user.current_otp
+ }
+
+ expect(response).to have_http_status(:success)
+ end
+
+ it 'authenticates with valid backup code' do
+ backup_codes = user.generate_backup_codes!
+
+ post :create, params: {
+ mfa_token: mfa_token,
+ backup_code: backup_codes.first
+ }
+
+ expect(response).to have_http_status(:success)
+ end
+
+ it 'rejects invalid OTP' do
+ post :create, params: {
+ mfa_token: mfa_token,
+ otp_code: '000000'
+ }
+
+ expect(response).to have_http_status(:bad_request)
+ expect(response.parsed_body['error']).to eq(I18n.t('errors.mfa.invalid_code'))
+ end
+
+ it 'rejects invalid backup code' do
+ user.generate_backup_codes!
+
+ post :create, params: {
+ mfa_token: mfa_token,
+ backup_code: 'invalid'
+ }
+
+ expect(response).to have_http_status(:bad_request)
+ expect(response.parsed_body['error']).to eq(I18n.t('errors.mfa.invalid_code'))
+ end
+
+ it 'rejects expired MFA token' do
+ expired_token = JWT.encode(
+ { user_id: user.id, exp: 1.minute.ago.to_i },
+ Rails.application.secret_key_base,
+ 'HS256'
+ )
+
+ post :create, params: {
+ mfa_token: expired_token,
+ otp_code: user.current_otp
+ }
+
+ expect(response).to have_http_status(:unauthorized)
+ expect(response.parsed_body['error']).to eq(I18n.t('errors.mfa.invalid_token'))
+ end
+
+ it 'requires either OTP or backup code' do
+ post :create, params: { mfa_token: mfa_token }
+
+ expect(response).to have_http_status(:bad_request)
+ expect(response.parsed_body['error']).to eq(I18n.t('errors.mfa.invalid_code'))
+ end
+ end
+ end
+
+ context 'with SSO authentication' do
+ it 'authenticates with valid SSO token' do
+ sso_token = user.generate_sso_auth_token
+
+ post :create, params: {
+ email: user.email,
+ sso_auth_token: sso_token
+ }
+
+ expect(response).to have_http_status(:success)
+ end
+
+ it 'rejects invalid SSO token' do
+ post :create, params: {
+ email: user.email,
+ sso_auth_token: 'invalid'
+ }
+
+ expect(response).to have_http_status(:unauthorized)
+ end
+ end
+ end
+
+ describe 'GET #new' do
+ it 'redirects to frontend login page' do
+ allow(ENV).to receive(:fetch).and_call_original
+ allow(ENV).to receive(:fetch).with('FRONTEND_URL', nil).and_return('/frontend')
+
+ get :new
+
+ expect(response).to redirect_to('/frontend/app/login?error=access-denied')
+ end
+ end
+end
diff --git a/spec/controllers/public/api/v1/inbox/conversations_controller_spec.rb b/spec/controllers/public/api/v1/inbox/conversations_controller_spec.rb
index 2b2bdefc3..6684d18cf 100644
--- a/spec/controllers/public/api/v1/inbox/conversations_controller_spec.rb
+++ b/spec/controllers/public/api/v1/inbox/conversations_controller_spec.rb
@@ -16,6 +16,17 @@ RSpec.describe 'Public Inbox Contact Conversations API', type: :request do
expect(data.first['uuid']).to eq contact_inbox.conversations.first.uuid
end
+ it 'return the conversations when hmac_verified is true' do
+ contact_inbox.update(hmac_verified: true)
+ create(:conversation, contact: contact)
+ get "/public/api/v1/inboxes/#{api_channel.identifier}/contacts/#{contact_inbox.source_id}/conversations"
+
+ expect(response).to have_http_status(:success)
+ data = response.parsed_body
+ expect(data.length).to eq 1
+ expect(data.first['uuid']).to eq contact.conversations.first.uuid
+ end
+
it 'does not return any private or activity message' do
conversation = create(:conversation, contact_inbox: contact_inbox)
create(:message, account: conversation.account, inbox: conversation.inbox, conversation: conversation, content: 'message-1')
diff --git a/spec/enterprise/builders/agent_builder_spec.rb b/spec/enterprise/builders/agent_builder_spec.rb
new file mode 100644
index 000000000..8f0ae4c17
--- /dev/null
+++ b/spec/enterprise/builders/agent_builder_spec.rb
@@ -0,0 +1,139 @@
+require 'rails_helper'
+
+RSpec.describe AgentBuilder do
+ let(:email) { 'agent@example.com' }
+ let(:name) { 'Test Agent' }
+ let(:account) { create(:account) }
+ let!(:inviter) { create(:user, account: account, role: 'administrator') }
+ let(:builder) do
+ described_class.new(
+ email: email,
+ name: name,
+ account: account,
+ inviter: inviter
+ )
+ end
+
+ describe '#perform with SAML enabled' do
+ let(:saml_settings) do
+ create(:account_saml_settings, account: account)
+ end
+
+ before { saml_settings }
+
+ context 'when user does not exist' do
+ it 'creates a new user with SAML provider' do
+ expect { builder.perform }.to change(User, :count).by(1)
+
+ user = User.from_email(email)
+ expect(user.provider).to eq('saml')
+ end
+
+ it 'creates user with correct attributes' do
+ user = builder.perform
+
+ expect(user.email).to eq(email)
+ expect(user.name).to eq(name)
+ expect(user.provider).to eq('saml')
+ expect(user.encrypted_password).to be_present
+ end
+
+ it 'adds user to the account with correct role' do
+ user = builder.perform
+ account_user = AccountUser.find_by(user: user, account: account)
+
+ expect(account_user).to be_present
+ expect(account_user.role).to eq('agent')
+ expect(account_user.inviter).to eq(inviter)
+ end
+ end
+
+ context 'when user already exists with email provider' do
+ let!(:existing_user) { create(:user, email: email, provider: 'email') }
+
+ it 'does not create a new user' do
+ expect { builder.perform }.not_to change(User, :count)
+ end
+
+ it 'converts existing user to SAML provider' do
+ expect(existing_user.provider).to eq('email')
+
+ builder.perform
+
+ expect(existing_user.reload.provider).to eq('saml')
+ end
+
+ it 'adds existing user to the account' do
+ user = builder.perform
+ account_user = AccountUser.find_by(user: user, account: account)
+
+ expect(account_user).to be_present
+ expect(account_user.inviter).to eq(inviter)
+ end
+ end
+
+ context 'when user already exists with SAML provider' do
+ let!(:existing_user) { create(:user, email: email, provider: 'saml') }
+
+ it 'does not change the provider' do
+ expect { builder.perform }.not_to(change { existing_user.reload.provider })
+ end
+
+ it 'still adds user to the account' do
+ user = builder.perform
+ account_user = AccountUser.find_by(user: user, account: account)
+
+ expect(account_user).to be_present
+ end
+ end
+ end
+
+ describe '#perform without SAML' do
+ context 'when user does not exist' do
+ it 'creates a new user with email provider (default behavior)' do
+ expect { builder.perform }.to change(User, :count).by(1)
+
+ user = User.from_email(email)
+ expect(user.provider).to eq('email')
+ end
+ end
+
+ context 'when user already exists' do
+ let!(:existing_user) { create(:user, email: email, provider: 'email') }
+
+ it 'does not change the existing user provider' do
+ expect { builder.perform }.not_to(change { existing_user.reload.provider })
+ end
+ end
+ end
+
+ describe '#perform with different account configurations' do
+ context 'when account has no SAML settings' do
+ # No saml_settings created for this account
+
+ it 'treats account as non-SAML enabled' do
+ user = builder.perform
+ expect(user.provider).to eq('email')
+ end
+ end
+
+ context 'when SAML settings are deleted after user creation' do
+ let(:saml_settings) do
+ create(:account_saml_settings, account: account)
+ end
+ let(:existing_user) { create(:user, email: email, provider: 'saml') }
+
+ before do
+ saml_settings
+ existing_user
+ end
+
+ it 'does not affect existing SAML users when adding to account' do
+ saml_settings.destroy!
+
+ user = builder.perform
+ expect(user.provider).to eq('saml') # Unchanged
+ end
+ end
+ end
+end
diff --git a/spec/enterprise/builders/saml_user_builder_spec.rb b/spec/enterprise/builders/saml_user_builder_spec.rb
new file mode 100644
index 000000000..434beda16
--- /dev/null
+++ b/spec/enterprise/builders/saml_user_builder_spec.rb
@@ -0,0 +1,264 @@
+require 'rails_helper'
+
+RSpec.describe SamlUserBuilder do
+ let(:email) { 'saml.user@example.com' }
+ let(:auth_hash) do
+ {
+ 'provider' => 'saml',
+ 'uid' => 'saml-uid-123',
+ 'info' => {
+ 'email' => email,
+ 'name' => 'SAML User',
+ 'first_name' => 'SAML',
+ 'last_name' => 'User'
+ },
+ 'extra' => {
+ 'raw_info' => {
+ 'groups' => %w[Administrators Users]
+ }
+ }
+ }
+ end
+ let(:account) { create(:account) }
+ let(:builder) { described_class.new(auth_hash, account.id) }
+
+ describe '#perform' do
+ context 'when user does not exist' do
+ it 'creates a new user' do
+ expect { builder.perform }.to change(User, :count).by(1)
+ end
+
+ it 'creates user with correct attributes' do
+ user = builder.perform
+
+ expect(user.email).to eq(email)
+ expect(user.name).to eq('SAML User')
+ expect(user.display_name).to eq('SAML')
+ expect(user.provider).to eq('saml')
+ expect(user.uid).to eq(email) # User model sets uid to email in before_validation callback
+ expect(user.confirmed_at).to be_present
+ end
+
+ it 'creates user with a random password' do
+ user = builder.perform
+ expect(user.encrypted_password).to be_present
+ end
+
+ it 'adds user to the account' do
+ user = builder.perform
+ expect(user.accounts).to include(account)
+ end
+
+ it 'sets default role as agent' do
+ user = builder.perform
+ account_user = AccountUser.find_by(user: user, account: account)
+ expect(account_user.role).to eq('agent')
+ end
+
+ context 'when name is not provided' do
+ let(:auth_hash) do
+ {
+ 'provider' => 'saml',
+ 'uid' => 'saml-uid-123',
+ 'info' => {
+ 'email' => email
+ }
+ }
+ end
+
+ it 'derives name from email' do
+ user = builder.perform
+ expect(user.name).to eq('saml.user')
+ end
+ end
+ end
+
+ context 'when user already exists' do
+ let!(:existing_user) { create(:user, email: email) }
+
+ it 'does not create a new user' do
+ expect { builder.perform }.not_to change(User, :count)
+ end
+
+ it 'returns the existing user' do
+ user = builder.perform
+ expect(user).to eq(existing_user)
+ end
+
+ it 'adds existing user to the account if not already added' do
+ user = builder.perform
+ expect(user.accounts).to include(account)
+ end
+
+ it 'converts existing user to SAML' do
+ expect(existing_user.provider).not_to eq('saml')
+
+ builder.perform
+
+ expect(existing_user.reload.provider).to eq('saml')
+ end
+
+ it 'does not change provider if user is already SAML' do
+ existing_user.update!(provider: 'saml')
+
+ expect { builder.perform }.not_to(change { existing_user.reload.provider })
+ end
+
+ it 'does not duplicate account association' do
+ existing_user.account_users.create!(account: account, role: 'agent')
+
+ expect { builder.perform }.not_to change(AccountUser, :count)
+ end
+
+ context 'when user is not confirmed' do
+ let(:unconfirmed_email) { 'unconfirmed_saml_user@example.com' }
+ let(:unconfirmed_auth_hash) do
+ {
+ 'provider' => 'saml',
+ 'uid' => 'saml-uid-123',
+ 'info' => {
+ 'email' => unconfirmed_email,
+ 'name' => 'SAML User',
+ 'first_name' => 'SAML',
+ 'last_name' => 'User'
+ },
+ 'extra' => {
+ 'raw_info' => {
+ 'groups' => %w[Administrators Users]
+ }
+ }
+ }
+ end
+ let(:unconfirmed_builder) { described_class.new(unconfirmed_auth_hash, account.id) }
+ let!(:existing_user) do
+ user = build(:user, email: unconfirmed_email)
+ user.confirmed_at = nil
+ user.save!(validate: false)
+ user
+ end
+
+ it 'confirms unconfirmed user after SAML authentication' do
+ expect(existing_user.confirmed?).to be false
+
+ unconfirmed_builder.perform
+
+ expect(existing_user.reload.confirmed?).to be true
+ end
+ end
+
+ context 'when user is already confirmed' do
+ let!(:existing_user) { create(:user, email: email, confirmed_at: Time.current) }
+
+ it 'keeps already confirmed user confirmed' do
+ expect(existing_user.confirmed?).to be true
+ original_confirmed_at = existing_user.confirmed_at
+
+ builder.perform
+
+ expect(existing_user.reload.confirmed?).to be true
+ expect(existing_user.reload.confirmed_at).to be_within(2.seconds).of(original_confirmed_at)
+ end
+ end
+ end
+
+ context 'with role mappings' do
+ let(:saml_settings) do
+ create(:account_saml_settings,
+ account: account,
+ role_mappings: {
+ 'Administrators' => { 'role' => 'administrator' },
+ 'Agents' => { 'role' => 'agent' }
+ })
+ end
+
+ before { saml_settings }
+
+ it 'applies administrator role based on SAML groups' do
+ user = builder.perform
+ account_user = AccountUser.find_by(user: user, account: account)
+ expect(account_user.role).to eq('administrator')
+ end
+
+ context 'with custom role mapping' do
+ let!(:custom_role) { create(:custom_role, account: account) }
+ let(:saml_settings) do
+ create(:account_saml_settings,
+ account: account,
+ role_mappings: {
+ 'Administrators' => { 'custom_role_id' => custom_role.id }
+ })
+ end
+
+ before { saml_settings }
+
+ it 'applies custom role based on SAML groups' do
+ user = builder.perform
+ account_user = AccountUser.find_by(user: user, account: account)
+ expect(account_user.custom_role_id).to eq(custom_role.id)
+ end
+ end
+
+ context 'when user is not in any mapped groups' do
+ let(:auth_hash) do
+ {
+ 'provider' => 'saml',
+ 'uid' => 'saml-uid-123',
+ 'info' => {
+ 'email' => email,
+ 'name' => 'SAML User'
+ },
+ 'extra' => {
+ 'raw_info' => {
+ 'groups' => ['UnmappedGroup']
+ }
+ }
+ }
+ end
+
+ it 'keeps default agent role' do
+ user = builder.perform
+ account_user = AccountUser.find_by(user: user, account: account)
+ expect(account_user.role).to eq('agent')
+ end
+ end
+ end
+
+ context 'with different group attribute names' do
+ let(:auth_hash) do
+ {
+ 'provider' => 'saml',
+ 'uid' => 'saml-uid-123',
+ 'info' => {
+ 'email' => email,
+ 'name' => 'SAML User'
+ },
+ 'extra' => {
+ 'raw_info' => {
+ 'memberOf' => ['CN=Administrators,OU=Groups,DC=example,DC=com']
+ }
+ }
+ }
+ end
+
+ it 'reads groups from memberOf attribute' do
+ builder_instance = described_class.new(auth_hash, account_id: account.id)
+ allow(builder_instance).to receive(:saml_groups).and_return(['CN=Administrators,OU=Groups,DC=example,DC=com'])
+ user = builder_instance.perform
+ expect(user).to be_persisted
+ end
+ end
+
+ context 'when there are errors' do
+ it 'returns unsaved user object when user creation fails' do
+ allow(User).to receive(:create).and_return(User.new(email: email))
+ user = builder.perform
+ expect(user.persisted?).to be false
+ end
+
+ it 'does not create account association for failed user' do
+ allow(User).to receive(:create).and_return(User.new(email: email))
+ expect { builder.perform }.not_to change(AccountUser, :count)
+ end
+ end
+ end
+end
diff --git a/spec/enterprise/controllers/api/v1/accounts/agent_capacity_policies/users_controller_spec.rb b/spec/enterprise/controllers/api/v1/accounts/agent_capacity_policies/users_controller_spec.rb
index be25151ae..9ed837107 100644
--- a/spec/enterprise/controllers/api/v1/accounts/agent_capacity_policies/users_controller_spec.rb
+++ b/spec/enterprise/controllers/api/v1/accounts/agent_capacity_policies/users_controller_spec.rb
@@ -19,6 +19,26 @@ RSpec.describe 'Agent Capacity Policy Users API', type: :request do
expect(response).to have_http_status(:success)
expect(response.parsed_body.first['id']).to eq(user.id)
end
+
+ it 'returns each user only once without duplicates' do
+ # Assign multiple users to the same policy
+ user.account_users.first.update!(agent_capacity_policy: agent_capacity_policy)
+ agent.account_users.first.update!(agent_capacity_policy: agent_capacity_policy)
+
+ get "/api/v1/accounts/#{account.id}/agent_capacity_policies/#{agent_capacity_policy.id}/users",
+ headers: administrator.create_new_auth_token,
+ as: :json
+
+ expect(response).to have_http_status(:success)
+
+ # Check that we have exactly 2 users
+ expect(response.parsed_body.length).to eq(2)
+
+ # Check that each user appears only once
+ user_ids = response.parsed_body.map { |u| u['id'] }
+ expect(user_ids).to contain_exactly(user.id, agent.id)
+ expect(user_ids.uniq).to eq(user_ids) # No duplicates
+ end
end
end
diff --git a/spec/enterprise/controllers/api/v1/accounts/agent_capacity_policies_controller_spec.rb b/spec/enterprise/controllers/api/v1/accounts/agent_capacity_policies_controller_spec.rb
index d6b171fe8..691891027 100644
--- a/spec/enterprise/controllers/api/v1/accounts/agent_capacity_policies_controller_spec.rb
+++ b/spec/enterprise/controllers/api/v1/accounts/agent_capacity_policies_controller_spec.rb
@@ -103,7 +103,10 @@ RSpec.describe 'Agent Capacity Policies API', type: :request do
agent_capacity_policy: {
name: 'Test Policy',
description: 'Test Description',
- exclusion_rules: { overall_capacity: 10 }
+ exclusion_rules: {
+ excluded_labels: %w[urgent spam],
+ exclude_older_than_hours: 24
+ }
}
}
@@ -115,6 +118,10 @@ RSpec.describe 'Agent Capacity Policies API', type: :request do
expect(response).to have_http_status(:success)
expect(response.parsed_body['name']).to eq('Test Policy')
expect(response.parsed_body['description']).to eq('Test Description')
+ expect(response.parsed_body['exclusion_rules']).to eq({
+ 'excluded_labels' => %w[urgent spam],
+ 'exclude_older_than_hours' => 24
+ })
end
it 'returns validation errors for invalid data' do
@@ -165,6 +172,28 @@ RSpec.describe 'Agent Capacity Policies API', type: :request do
expect(response).to have_http_status(:success)
expect(response.parsed_body['name']).to eq('Updated Policy')
end
+
+ it 'updates exclusion rules when administrator' do
+ params = {
+ agent_capacity_policy: {
+ exclusion_rules: {
+ excluded_labels: %w[vip priority],
+ exclude_older_than_hours: 48
+ }
+ }
+ }
+
+ put "/api/v1/accounts/#{account.id}/agent_capacity_policies/#{agent_capacity_policy.id}",
+ params: params,
+ headers: administrator.create_new_auth_token,
+ as: :json
+
+ expect(response).to have_http_status(:success)
+ expect(response.parsed_body['exclusion_rules']).to eq({
+ 'excluded_labels' => %w[vip priority],
+ 'exclude_older_than_hours' => 48
+ })
+ end
end
end
diff --git a/spec/enterprise/controllers/api/v1/accounts/captain/assistant_responses_controller_spec.rb b/spec/enterprise/controllers/api/v1/accounts/captain/assistant_responses_controller_spec.rb
index 58c23f897..038ee5e8d 100644
--- a/spec/enterprise/controllers/api/v1/accounts/captain/assistant_responses_controller_spec.rb
+++ b/spec/enterprise/controllers/api/v1/accounts/captain/assistant_responses_controller_spec.rb
@@ -90,6 +90,53 @@ RSpec.describe 'Api::V1::Accounts::Captain::AssistantResponses', type: :request
expect(json_response[:payload][0][:documentable][:id]).to eq(document.id)
end
end
+
+ context 'when searching' do
+ before do
+ create(:captain_assistant_response,
+ account: account,
+ assistant: assistant,
+ question: 'How to reset password?',
+ answer: 'Click forgot password')
+ create(:captain_assistant_response,
+ account: account,
+ assistant: assistant,
+ question: 'How to change email?',
+ answer: 'Go to settings')
+ end
+
+ it 'finds responses by question text' do
+ get "/api/v1/accounts/#{account.id}/captain/assistant_responses",
+ params: { search: 'password' },
+ headers: agent.create_new_auth_token,
+ as: :json
+
+ expect(response).to have_http_status(:ok)
+ expect(json_response[:payload].length).to eq(1)
+ expect(json_response[:payload][0][:question]).to include('password')
+ end
+
+ it 'finds responses by answer text' do
+ get "/api/v1/accounts/#{account.id}/captain/assistant_responses",
+ params: { search: 'settings' },
+ headers: agent.create_new_auth_token,
+ as: :json
+
+ expect(response).to have_http_status(:ok)
+ expect(json_response[:payload].length).to eq(1)
+ expect(json_response[:payload][0][:answer]).to include('settings')
+ end
+
+ it 'returns empty when no matches' do
+ get "/api/v1/accounts/#{account.id}/captain/assistant_responses",
+ params: { search: 'nonexistent' },
+ headers: agent.create_new_auth_token,
+ as: :json
+
+ expect(response).to have_http_status(:ok)
+ expect(json_response[:payload].length).to eq(0)
+ end
+ end
end
describe 'GET /api/v1/accounts/:account_id/captain/assistant_responses/:id' do
diff --git a/spec/enterprise/controllers/api/v1/accounts/saml_settings_controller_spec.rb b/spec/enterprise/controllers/api/v1/accounts/saml_settings_controller_spec.rb
new file mode 100644
index 000000000..14bf0ebb0
--- /dev/null
+++ b/spec/enterprise/controllers/api/v1/accounts/saml_settings_controller_spec.rb
@@ -0,0 +1,265 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+RSpec.describe 'Api::V1::Accounts::SamlSettings', type: :request do
+ let(:account) { create(:account) }
+ let(:agent) { create(:user, account: account, role: :agent) }
+ let(:administrator) { create(:user, account: account, role: :administrator) }
+
+ before do
+ account.enable_features('saml')
+ account.save!
+ end
+
+ def json_response
+ JSON.parse(response.body, symbolize_names: true)
+ end
+
+ describe 'GET /api/v1/accounts/{account.id}/saml_settings' do
+ context 'when unauthenticated' do
+ it 'returns unauthorized' do
+ get "/api/v1/accounts/#{account.id}/saml_settings"
+ expect(response).to have_http_status(:unauthorized)
+ end
+ end
+
+ context 'when authenticated as administrator' do
+ context 'when SAML settings exist' do
+ let(:saml_settings) do
+ create(:account_saml_settings,
+ account: account,
+ sso_url: 'https://idp.example.com/saml/sso',
+ role_mappings: { 'Admins' => { 'role' => 1 } })
+ end
+
+ before do
+ saml_settings # Ensure the record exists
+ end
+
+ it 'returns the SAML settings' do
+ get "/api/v1/accounts/#{account.id}/saml_settings",
+ headers: administrator.create_new_auth_token,
+ as: :json
+
+ expect(response).to have_http_status(:success)
+ expect(json_response[:sso_url]).to eq('https://idp.example.com/saml/sso')
+ expect(json_response[:role_mappings]).to eq({ Admins: { role: 1 } })
+ end
+ end
+
+ context 'when SAML settings do not exist' do
+ it 'returns default SAML settings' do
+ get "/api/v1/accounts/#{account.id}/saml_settings",
+ headers: administrator.create_new_auth_token,
+ as: :json
+
+ expect(response).to have_http_status(:success)
+ expect(json_response[:role_mappings]).to eq({})
+ end
+ end
+ end
+
+ context 'when authenticated as agent' do
+ it 'returns unauthorized' do
+ get "/api/v1/accounts/#{account.id}/saml_settings",
+ headers: agent.create_new_auth_token
+
+ expect(response).to have_http_status(:unauthorized)
+ end
+ end
+
+ context 'when SAML feature is not enabled' do
+ before do
+ account.disable_features('saml')
+ account.save!
+ end
+
+ it 'returns forbidden with feature not enabled message' do
+ get "/api/v1/accounts/#{account.id}/saml_settings",
+ headers: administrator.create_new_auth_token
+
+ expect(response).to have_http_status(:forbidden)
+ end
+ end
+ end
+
+ describe 'POST /api/v1/accounts/{account.id}/saml_settings' do
+ let(:valid_params) do
+ key = OpenSSL::PKey::RSA.new(2048)
+ cert = OpenSSL::X509::Certificate.new
+ cert.version = 2
+ cert.serial = 1
+ cert.subject = OpenSSL::X509::Name.parse('/C=US/ST=Test/L=Test/O=Test/CN=test.example.com')
+ cert.issuer = cert.subject
+ cert.public_key = key.public_key
+ cert.not_before = Time.zone.now
+ cert.not_after = cert.not_before + (365 * 24 * 60 * 60)
+ cert.sign(key, OpenSSL::Digest.new('SHA256'))
+
+ {
+ saml_settings: {
+ sso_url: 'https://idp.example.com/saml/sso',
+ certificate: cert.to_pem,
+ idp_entity_id: 'https://idp.example.com/saml/metadata',
+ role_mappings: { 'Admins' => { 'role' => 1 }, 'Users' => { 'role' => 0 } }
+ }
+ }
+ end
+
+ context 'when unauthenticated' do
+ it 'returns unauthorized' do
+ post "/api/v1/accounts/#{account.id}/saml_settings", params: valid_params
+ expect(response).to have_http_status(:unauthorized)
+ end
+ end
+
+ context 'when authenticated as administrator' do
+ context 'with valid parameters' do
+ it 'creates SAML settings' do
+ expect do
+ post "/api/v1/accounts/#{account.id}/saml_settings",
+ params: valid_params,
+ headers: administrator.create_new_auth_token,
+ as: :json
+ end.to change(AccountSamlSettings, :count).by(1)
+
+ expect(response).to have_http_status(:success)
+
+ saml_settings = AccountSamlSettings.find_by(account: account)
+ expect(saml_settings.sso_url).to eq('https://idp.example.com/saml/sso')
+ expect(saml_settings.role_mappings).to eq({ 'Admins' => { 'role' => 1 }, 'Users' => { 'role' => 0 } })
+ end
+ end
+
+ context 'with invalid parameters' do
+ let(:invalid_params) do
+ valid_params.tap do |params|
+ params[:saml_settings][:sso_url] = nil
+ end
+ end
+
+ it 'returns unprocessable entity' do
+ post "/api/v1/accounts/#{account.id}/saml_settings",
+ params: invalid_params,
+ headers: administrator.create_new_auth_token,
+ as: :json
+
+ expect(response).to have_http_status(:unprocessable_entity)
+ expect(AccountSamlSettings.count).to eq(0)
+ end
+ end
+ end
+
+ context 'when authenticated as agent' do
+ it 'returns unauthorized' do
+ post "/api/v1/accounts/#{account.id}/saml_settings",
+ params: valid_params,
+ headers: agent.create_new_auth_token
+
+ expect(response).to have_http_status(:unauthorized)
+ expect(AccountSamlSettings.count).to eq(0)
+ end
+ end
+ end
+
+ describe 'PUT /api/v1/accounts/{account.id}/saml_settings' do
+ let(:saml_settings) do
+ create(:account_saml_settings,
+ account: account,
+ sso_url: 'https://old.example.com/saml')
+ end
+ let(:update_params) do
+ key = OpenSSL::PKey::RSA.new(2048)
+ cert = OpenSSL::X509::Certificate.new
+ cert.version = 2
+ cert.serial = 3
+ cert.subject = OpenSSL::X509::Name.parse('/C=US/ST=Test/L=Test/O=Test/CN=update.example.com')
+ cert.issuer = cert.subject
+ cert.public_key = key.public_key
+ cert.not_before = Time.zone.now
+ cert.not_after = cert.not_before + (365 * 24 * 60 * 60)
+ cert.sign(key, OpenSSL::Digest.new('SHA256'))
+
+ {
+ saml_settings: {
+ sso_url: 'https://new.example.com/saml/sso',
+ certificate: cert.to_pem,
+ role_mappings: { 'NewGroup' => { 'custom_role_id' => 5 } }
+ }
+ }
+ end
+
+ before do
+ saml_settings # Ensure the record exists
+ end
+
+ context 'when unauthenticated' do
+ it 'returns unauthorized' do
+ put "/api/v1/accounts/#{account.id}/saml_settings", params: update_params
+ expect(response).to have_http_status(:unauthorized)
+ end
+ end
+
+ context 'when authenticated as administrator' do
+ it 'updates SAML settings' do
+ put "/api/v1/accounts/#{account.id}/saml_settings",
+ params: update_params,
+ headers: administrator.create_new_auth_token,
+ as: :json
+
+ expect(response).to have_http_status(:success)
+
+ saml_settings.reload
+ expect(saml_settings.sso_url).to eq('https://new.example.com/saml/sso')
+ expect(saml_settings.role_mappings).to eq({ 'NewGroup' => { 'custom_role_id' => 5 } })
+ end
+ end
+
+ context 'when authenticated as agent' do
+ it 'returns unauthorized' do
+ put "/api/v1/accounts/#{account.id}/saml_settings",
+ params: update_params,
+ headers: agent.create_new_auth_token
+
+ expect(response).to have_http_status(:unauthorized)
+ end
+ end
+ end
+
+ describe 'DELETE /api/v1/accounts/{account.id}/saml_settings' do
+ let(:saml_settings) { create(:account_saml_settings, account: account) }
+
+ before do
+ saml_settings # Ensure the record exists
+ end
+
+ context 'when unauthenticated' do
+ it 'returns unauthorized' do
+ delete "/api/v1/accounts/#{account.id}/saml_settings"
+ expect(response).to have_http_status(:unauthorized)
+ end
+ end
+
+ context 'when authenticated as administrator' do
+ it 'destroys SAML settings' do
+ expect do
+ delete "/api/v1/accounts/#{account.id}/saml_settings",
+ headers: administrator.create_new_auth_token
+ end.to change(AccountSamlSettings, :count).by(-1)
+
+ expect(response).to have_http_status(:no_content)
+ end
+ end
+
+ context 'when authenticated as agent' do
+ it 'returns unauthorized' do
+ delete "/api/v1/accounts/#{account.id}/saml_settings",
+ headers: agent.create_new_auth_token
+
+ expect(response).to have_http_status(:unauthorized)
+ expect(AccountSamlSettings.count).to eq(1)
+ end
+ end
+ end
+end
diff --git a/spec/enterprise/controllers/api/v1/auth_controller_spec.rb b/spec/enterprise/controllers/api/v1/auth_controller_spec.rb
new file mode 100644
index 000000000..30367ab17
--- /dev/null
+++ b/spec/enterprise/controllers/api/v1/auth_controller_spec.rb
@@ -0,0 +1,131 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+RSpec.describe 'Api::V1::Auth', type: :request do
+ let(:account) { create(:account) }
+ let(:user) { create(:user, email: 'user@example.com') }
+
+ before do
+ account.enable_features('saml')
+ account.save!
+ end
+
+ def json_response
+ JSON.parse(response.body, symbolize_names: true)
+ end
+
+ describe 'POST /api/v1/auth/saml_login' do
+ context 'when email is blank' do
+ it 'returns bad request' do
+ post '/api/v1/auth/saml_login', params: { email: '' }
+
+ expect(response).to have_http_status(:bad_request)
+ end
+ end
+
+ context 'when email is nil' do
+ it 'returns bad request' do
+ post '/api/v1/auth/saml_login', params: {}
+
+ expect(response).to have_http_status(:bad_request)
+ end
+ end
+
+ context 'when user does not exist' do
+ it 'returns unauthorized with generic message' do
+ post '/api/v1/auth/saml_login', params: { email: 'nonexistent@example.com' }
+
+ expect(response).to have_http_status(:unauthorized)
+ end
+ end
+
+ context 'when user exists but has no SAML enabled accounts' do
+ before do
+ create(:account_user, user: user, account: account)
+ end
+
+ it 'returns unauthorized' do
+ post '/api/v1/auth/saml_login', params: { email: user.email }
+
+ expect(response).to have_http_status(:unauthorized)
+ end
+ end
+
+ context 'when user has account without SAML feature enabled' do
+ let(:saml_settings) { create(:account_saml_settings, account: account) }
+
+ before do
+ saml_settings
+ create(:account_user, user: user, account: account)
+ account.disable_features('saml')
+ account.save!
+ end
+
+ it 'returns unauthorized' do
+ post '/api/v1/auth/saml_login', params: { email: user.email }
+
+ expect(response).to have_http_status(:unauthorized)
+ end
+ end
+
+ context 'when user has valid SAML configuration' do
+ let(:saml_settings) do
+ create(:account_saml_settings, account: account)
+ end
+
+ before do
+ saml_settings
+ create(:account_user, user: user, account: account)
+ end
+
+ it 'redirects to SAML initiation URL' do
+ post '/api/v1/auth/saml_login', params: { email: user.email }
+
+ expect(response).to have_http_status(:temporary_redirect)
+ expect(response.location).to include("/auth/saml?account_id=#{account.id}")
+ end
+
+ it 'handles email case insensitivity' do
+ post '/api/v1/auth/saml_login', params: { email: user.email.upcase }
+
+ expect(response).to have_http_status(:temporary_redirect)
+ expect(response.location).to include("/auth/saml?account_id=#{account.id}")
+ end
+
+ it 'strips whitespace from email' do
+ post '/api/v1/auth/saml_login', params: { email: " #{user.email} " }
+
+ expect(response).to have_http_status(:temporary_redirect)
+ expect(response.location).to include("/auth/saml?account_id=#{account.id}")
+ end
+ end
+
+ context 'when user has multiple accounts with SAML' do
+ let(:account2) { create(:account) }
+ let(:saml_settings1) do
+ create(:account_saml_settings, account: account)
+ end
+ let(:saml_settings2) do
+ create(:account_saml_settings, account: account2)
+ end
+
+ before do
+ account2.enable_features('saml')
+ account2.save!
+ saml_settings1
+ saml_settings2
+ create(:account_user, user: user, account: account)
+ create(:account_user, user: user, account: account2)
+ end
+
+ it 'redirects to the first SAML enabled account' do
+ post '/api/v1/auth/saml_login', params: { email: user.email }
+
+ expect(response).to have_http_status(:temporary_redirect)
+ returned_account_id = response.location.match(/account_id=(\d+)/)[1].to_i
+ expect([account.id, account2.id]).to include(returned_account_id)
+ end
+ end
+ end
+end
diff --git a/spec/enterprise/controllers/enterprise/devise_overrides/omniauth_callbacks_controller_spec.rb b/spec/enterprise/controllers/enterprise/devise_overrides/omniauth_callbacks_controller_spec.rb
new file mode 100644
index 000000000..af67e3009
--- /dev/null
+++ b/spec/enterprise/controllers/enterprise/devise_overrides/omniauth_callbacks_controller_spec.rb
@@ -0,0 +1,61 @@
+require 'rails_helper'
+
+RSpec.describe 'Enterprise SAML OmniAuth Callbacks', type: :request do
+ let!(:account) { create(:account) }
+ let(:saml_settings) { create(:account_saml_settings, account: account) }
+
+ def set_saml_config(email = 'test@example.com')
+ OmniAuth.config.test_mode = true
+ OmniAuth.config.mock_auth[:saml] = OmniAuth::AuthHash.new(
+ provider: 'saml',
+ uid: '123545',
+ info: {
+ name: 'Test User',
+ email: email
+ }
+ )
+ end
+
+ before do
+ allow(ChatwootApp).to receive(:enterprise?).and_return(true)
+ account.enable_features!('saml')
+ saml_settings
+ end
+
+ describe '#saml callback' do
+ it 'creates new user and logs them in' do
+ with_modified_env FRONTEND_URL: 'http://www.example.com' do
+ set_saml_config('new_user@example.com')
+
+ get "/omniauth/saml/callback?account_id=#{account.id}"
+
+ # expect a 302 redirect to auth/saml/callback
+ expect(response).to redirect_to('http://www.example.com/auth/saml/callback')
+ follow_redirect!
+
+ # expect redirect to login with SSO token
+ expect(response).to redirect_to(%r{/app/login\?email=.+&sso_auth_token=.+$})
+
+ # verify user was created
+ user = User.from_email('new_user@example.com')
+ expect(user).to be_present
+ expect(user.provider).to eq('saml')
+ end
+ end
+
+ it 'logs in existing user' do
+ with_modified_env FRONTEND_URL: 'http://www.example.com' do
+ create(:user, email: 'existing@example.com', account: account)
+ set_saml_config('existing@example.com')
+
+ get "/omniauth/saml/callback?account_id=#{account.id}"
+
+ # expect a 302 redirect to auth/saml/callback
+ expect(response).to redirect_to('http://www.example.com/auth/saml/callback')
+ follow_redirect!
+
+ expect(response).to redirect_to(%r{/app/login\?email=.+&sso_auth_token=.+$})
+ end
+ end
+ end
+end
diff --git a/spec/enterprise/controllers/enterprise/devise_overrides/passwords_controller_spec.rb b/spec/enterprise/controllers/enterprise/devise_overrides/passwords_controller_spec.rb
new file mode 100644
index 000000000..23b498be8
--- /dev/null
+++ b/spec/enterprise/controllers/enterprise/devise_overrides/passwords_controller_spec.rb
@@ -0,0 +1,36 @@
+require 'rails_helper'
+
+RSpec.describe 'Enterprise Passwords Controller', type: :request do
+ let!(:account) { create(:account) }
+
+ describe 'POST /auth/password' do
+ context 'with SAML user email' do
+ let!(:saml_user) { create(:user, email: 'saml@example.com', provider: 'saml', account: account) }
+
+ it 'prevents password reset and returns forbidden with custom error message' do
+ params = { email: saml_user.email, redirect_url: 'http://test.host' }
+
+ post user_password_path, params: params, as: :json
+
+ expect(response).to have_http_status(:forbidden)
+ json_response = JSON.parse(response.body)
+ expect(json_response['success']).to be(false)
+ expect(json_response['errors']).to include(I18n.t('messages.reset_password_saml_user'))
+ end
+ end
+
+ context 'with non-SAML user email' do
+ let!(:regular_user) { create(:user, email: 'regular@example.com', provider: 'email', account: account) }
+
+ it 'allows password reset for non-SAML users' do
+ params = { email: regular_user.email, redirect_url: 'http://test.host' }
+
+ post user_password_path, params: params, as: :json
+
+ expect(response).to have_http_status(:ok)
+ json_response = JSON.parse(response.body)
+ expect(json_response['message']).to be_present
+ end
+ end
+ end
+end
diff --git a/spec/enterprise/controllers/enterprise/devise_overrides/session_controller_spec.rb b/spec/enterprise/controllers/enterprise/devise_overrides/session_controller_spec.rb
index 89f8794ee..08c69c840 100644
--- a/spec/enterprise/controllers/enterprise/devise_overrides/session_controller_spec.rb
+++ b/spec/enterprise/controllers/enterprise/devise_overrides/session_controller_spec.rb
@@ -5,32 +5,75 @@ RSpec.describe 'Enterprise Audit API', type: :request do
let!(:user) { create(:user, password: 'Password1!', account: account) }
describe 'POST /sign_in' do
- it 'creates a sign_in audit event wwith valid credentials' do
- params = { email: user.email, password: 'Password1!' }
+ context 'with SAML user attempting password login' do
+ let(:saml_settings) { create(:account_saml_settings, account: account) }
+ let(:saml_user) { create(:user, email: 'saml@example.com', provider: 'saml', account: account) }
- expect do
- post new_user_session_url,
- params: params,
- as: :json
- end.to change(Enterprise::AuditLog, :count).by(1)
+ before do
+ saml_settings
+ saml_user
+ end
- expect(response).to have_http_status(:success)
- expect(response.body).to include(user.email)
+ it 'prevents login and returns SAML authentication error' do
+ params = { email: saml_user.email, password: 'Password1!' }
- # Check if the sign_in event is created
- user.reload
- expect(user.audits.last.action).to eq('sign_in')
- expect(user.audits.last.associated_id).to eq(account.id)
- expect(user.audits.last.associated_type).to eq('Account')
+ post new_user_session_url, params: params, as: :json
+
+ expect(response).to have_http_status(:unauthorized)
+ json_response = JSON.parse(response.body)
+ expect(json_response['success']).to be(false)
+ expect(json_response['errors']).to include(I18n.t('messages.login_saml_user'))
+ end
+
+ it 'allows login with valid SSO token' do
+ valid_token = saml_user.generate_sso_auth_token
+ params = { email: saml_user.email, sso_auth_token: valid_token, password: 'Password1!' }
+
+ expect do
+ post new_user_session_url, params: params, as: :json
+ end.to change(Enterprise::AuditLog, :count).by(1)
+
+ expect(response).to have_http_status(:success)
+ expect(response.body).to include(saml_user.email)
+ end
end
- it 'will not create a sign_in audit event with invalid credentials' do
- params = { email: user.email, password: 'invalid' }
- expect do
- post new_user_session_url,
- params: params,
- as: :json
- end.not_to change(Enterprise::AuditLog, :count)
+ context 'with regular user credentials' do
+ it 'creates a sign_in audit event wwith valid credentials' do
+ params = { email: user.email, password: 'Password1!' }
+
+ expect do
+ post new_user_session_url,
+ params: params,
+ as: :json
+ end.to change(Enterprise::AuditLog, :count).by(1)
+
+ expect(response).to have_http_status(:success)
+ expect(response.body).to include(user.email)
+
+ # Check if the sign_in event is created
+ user.reload
+ expect(user.audits.last.action).to eq('sign_in')
+ expect(user.audits.last.associated_id).to eq(account.id)
+ expect(user.audits.last.associated_type).to eq('Account')
+ end
+
+ it 'will not create a sign_in audit event with invalid credentials' do
+ params = { email: user.email, password: 'invalid' }
+ expect do
+ post new_user_session_url,
+ params: params,
+ as: :json
+ end.not_to change(Enterprise::AuditLog, :count)
+ end
+ end
+
+ context 'with blank email' do
+ it 'skips SAML check and processes normally' do
+ params = { email: '', password: 'Password1!' }
+ post new_user_session_url, params: params, as: :json
+ expect(response).to have_http_status(:unauthorized)
+ end
end
end
diff --git a/spec/enterprise/controllers/twilio/voice_controller_spec.rb b/spec/enterprise/controllers/twilio/voice_controller_spec.rb
new file mode 100644
index 000000000..0f5e4d00a
--- /dev/null
+++ b/spec/enterprise/controllers/twilio/voice_controller_spec.rb
@@ -0,0 +1,87 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+RSpec.describe 'Twilio::VoiceController', type: :request do
+ let(:account) { create(:account) }
+ let(:channel) { create(:channel_voice, account: account, phone_number: '+15551230003') }
+ let(:inbox) { channel.inbox }
+ let(:digits) { channel.phone_number.delete_prefix('+') }
+
+ before do
+ allow(Twilio::VoiceWebhookSetupService).to receive(:new)
+ .and_return(instance_double(Twilio::VoiceWebhookSetupService, perform: "AP#{SecureRandom.hex(16)}"))
+ end
+
+ describe 'POST /twilio/voice/call/:phone' do
+ let(:call_sid) { 'CA_test_call_sid_123' }
+ let(:from_number) { '+15550003333' }
+ let(:to_number) { channel.phone_number }
+
+ it 'invokes Voice::InboundCallBuilder with expected params and renders its TwiML' do
+ builder_double = instance_double(Voice::InboundCallBuilder)
+ expect(Voice::InboundCallBuilder).to receive(:new).with(
+ hash_including(
+ account: account,
+ inbox: inbox,
+ from_number: from_number,
+ to_number: to_number,
+ call_sid: call_sid
+ )
+ ).and_return(builder_double)
+ expect(builder_double).to receive(:perform).and_return(builder_double)
+ expect(builder_double).to receive(:twiml_response).and_return('')
+
+ post "/twilio/voice/call/#{digits}", params: {
+ 'CallSid' => call_sid,
+ 'From' => from_number,
+ 'To' => to_number
+ }
+
+ expect(response).to have_http_status(:ok)
+ expect(response.body).to eq('')
+ end
+
+ it 'raises not found when inbox is not present' do
+ expect(Voice::InboundCallBuilder).not_to receive(:new)
+ post '/twilio/voice/call/19998887777', params: {
+ 'CallSid' => call_sid,
+ 'From' => from_number,
+ 'To' => to_number
+ }
+ expect(response).to have_http_status(:not_found)
+ end
+ end
+
+ describe 'POST /twilio/voice/status/:phone' do
+ let(:call_sid) { 'CA_status_sid_456' }
+
+ it 'invokes Voice::StatusUpdateService with expected params' do
+ service_double = instance_double(Voice::StatusUpdateService, perform: nil)
+ expect(Voice::StatusUpdateService).to receive(:new).with(
+ hash_including(
+ account: account,
+ call_sid: call_sid,
+ call_status: 'completed'
+ )
+ ).and_return(service_double)
+ expect(service_double).to receive(:perform)
+
+ post "/twilio/voice/status/#{digits}", params: {
+ 'CallSid' => call_sid,
+ 'CallStatus' => 'completed'
+ }
+
+ expect(response).to have_http_status(:no_content)
+ end
+
+ it 'raises not found when inbox is not present' do
+ expect(Voice::StatusUpdateService).not_to receive(:new)
+ post '/twilio/voice/status/18005550101', params: {
+ 'CallSid' => call_sid,
+ 'CallStatus' => 'busy'
+ }
+ expect(response).to have_http_status(:not_found)
+ end
+ end
+end
diff --git a/spec/enterprise/jobs/saml/update_account_users_provider_job_spec.rb b/spec/enterprise/jobs/saml/update_account_users_provider_job_spec.rb
new file mode 100644
index 000000000..82bb6b015
--- /dev/null
+++ b/spec/enterprise/jobs/saml/update_account_users_provider_job_spec.rb
@@ -0,0 +1,65 @@
+require 'rails_helper'
+
+RSpec.describe Saml::UpdateAccountUsersProviderJob, type: :job do
+ let(:account) { create(:account) }
+ let!(:user1) { create(:user, accounts: [account], provider: 'email') }
+ let!(:user2) { create(:user, accounts: [account], provider: 'email') }
+ let!(:user3) { create(:user, accounts: [account], provider: 'google') }
+
+ describe '#perform' do
+ context 'when setting provider to saml' do
+ it 'updates all account users to saml provider' do
+ described_class.new.perform(account.id, 'saml')
+
+ expect(user1.reload.provider).to eq('saml')
+ expect(user2.reload.provider).to eq('saml')
+ expect(user3.reload.provider).to eq('saml')
+ end
+ end
+
+ context 'when resetting provider to email' do
+ before do
+ # rubocop:disable Rails/SkipsModelValidations
+ user1.update_column(:provider, 'saml')
+ user2.update_column(:provider, 'saml')
+ user3.update_column(:provider, 'saml')
+ # rubocop:enable Rails/SkipsModelValidations
+ end
+
+ context 'when users have no other SAML accounts' do
+ it 'updates all account users to email provider' do
+ described_class.new.perform(account.id, 'email')
+
+ expect(user1.reload.provider).to eq('email')
+ expect(user2.reload.provider).to eq('email')
+ expect(user3.reload.provider).to eq('email')
+ end
+ end
+
+ context 'when users belong to other accounts with SAML enabled' do
+ let(:other_account) { create(:account) }
+
+ before do
+ create(:account_saml_settings, account: other_account)
+ user1.account_users.create!(account: other_account, role: :agent)
+ end
+
+ it 'preserves SAML provider for users with other SAML accounts' do
+ described_class.new.perform(account.id, 'email')
+
+ expect(user1.reload.provider).to eq('saml')
+ expect(user2.reload.provider).to eq('email')
+ expect(user3.reload.provider).to eq('email')
+ end
+ end
+ end
+
+ context 'when account does not exist' do
+ it 'raises ActiveRecord::RecordNotFound' do
+ expect do
+ described_class.new.perform(999_999, 'saml')
+ end.to raise_error(ActiveRecord::RecordNotFound)
+ end
+ end
+ end
+end
diff --git a/spec/enterprise/mailers/devise_mailer_spec.rb b/spec/enterprise/mailers/devise_mailer_spec.rb
new file mode 100644
index 000000000..286e863f7
--- /dev/null
+++ b/spec/enterprise/mailers/devise_mailer_spec.rb
@@ -0,0 +1,150 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+RSpec.describe 'Devise::Mailer' do
+ describe 'confirmation_instructions with Enterprise features' do
+ let(:account) { create(:account) }
+ let!(:confirmable_user) { create(:user, inviter: inviter_val, account: account) }
+ let(:inviter_val) { nil }
+ let(:mail) { Devise::Mailer.confirmation_instructions(confirmable_user.reload, nil, {}) }
+
+ before do
+ confirmable_user.update!(confirmed_at: nil)
+ confirmable_user.send(:generate_confirmation_token)
+ end
+
+ context 'with SAML enabled account' do
+ let(:saml_settings) { create(:account_saml_settings, account: account) }
+
+ before { saml_settings }
+
+ context 'when user has no inviter' do
+ it 'shows standard welcome message without SSO references' do
+ expect(mail.body).to match('We have a suite of powerful tools ready for you to explore.')
+ expect(mail.body).not_to match('via Single Sign-On')
+ end
+
+ it 'does not show activation instructions for SAML accounts' do
+ expect(mail.body).not_to match('Please take a moment and click the link below and activate your account')
+ end
+
+ it 'shows confirmation link' do
+ expect(mail.body).to include("app/auth/confirmation?confirmation_token=#{confirmable_user.confirmation_token}")
+ end
+ end
+
+ context 'when user has inviter and SAML is enabled' do
+ let(:inviter_val) { create(:user, :administrator, skip_confirmation: true, account: account) }
+
+ it 'mentions SSO invitation' do
+ expect(mail.body).to match(
+ "#{CGI.escapeHTML(inviter_val.name)}, with #{CGI.escapeHTML(account.name)}, has invited you to access.*via Single Sign-On \\(SSO\\)"
+ )
+ end
+
+ it 'explains SSO authentication' do
+ expect(mail.body).to match('Your organization uses SSO for secure authentication')
+ expect(mail.body).to match('You will not need a password to access your account')
+ end
+
+ it 'does not show standard invitation message' do
+ expect(mail.body).not_to match('has invited you to try out')
+ end
+
+ it 'directs to SSO portal instead of password reset' do
+ expect(mail.body).to match('You can access your account by logging in through your organization\'s SSO portal')
+ expect(mail.body).not_to include('app/auth/password/edit')
+ end
+ end
+
+ context 'when user is already confirmed and has inviter' do
+ let(:inviter_val) { create(:user, :administrator, skip_confirmation: true, account: account) }
+
+ before do
+ confirmable_user.confirm
+ end
+
+ it 'shows SSO login instructions' do
+ expect(mail.body).to match('You can now access your account by logging in through your organization\'s SSO portal')
+ expect(mail.body).not_to include('/auth/sign_in')
+ end
+ end
+
+ context 'when user updates email on SAML account' do
+ let(:inviter_val) { create(:user, :administrator, skip_confirmation: true, account: account) }
+
+ before do
+ confirmable_user.update!(email: 'updated@example.com')
+ end
+
+ it 'still shows confirmation link for email verification' do
+ expect(mail.body).to include('app/auth/confirmation?confirmation_token')
+ expect(confirmable_user.unconfirmed_email.blank?).to be false
+ end
+ end
+
+ context 'when user is already confirmed with no inviter' do
+ before do
+ confirmable_user.confirm
+ end
+
+ it 'shows SSO login instructions instead of regular login' do
+ expect(mail.body).to match('You can now access your account by logging in through your organization\'s SSO portal')
+ expect(mail.body).not_to include('/auth/sign_in')
+ end
+ end
+ end
+
+ context 'when account does not have SAML enabled' do
+ context 'when user has inviter' do
+ let(:inviter_val) { create(:user, :administrator, skip_confirmation: true, account: account) }
+
+ it 'shows standard invitation without SSO references' do
+ expect(mail.body).to match('has invited you to try out Chatwoot')
+ expect(mail.body).not_to match('via Single Sign-On')
+ expect(mail.body).not_to match('SSO portal')
+ end
+
+ it 'shows password reset link' do
+ expect(mail.body).to include('app/auth/password/edit')
+ end
+ end
+
+ context 'when user has no inviter' do
+ it 'shows standard welcome message and activation instructions' do
+ expect(mail.body).to match('We have a suite of powerful tools ready for you to explore')
+ expect(mail.body).to match('Please take a moment and click the link below and activate your account')
+ end
+
+ it 'shows confirmation link' do
+ expect(mail.body).to include("app/auth/confirmation?confirmation_token=#{confirmable_user.confirmation_token}")
+ end
+ end
+
+ context 'when user is already confirmed' do
+ let(:inviter_val) { create(:user, :administrator, skip_confirmation: true, account: account) }
+
+ before do
+ confirmable_user.confirm
+ end
+
+ it 'shows regular login link' do
+ expect(mail.body).to include('/auth/sign_in')
+ expect(mail.body).not_to match('SSO portal')
+ end
+ end
+
+ context 'when user updates email' do
+ before do
+ confirmable_user.update!(email: 'updated@example.com')
+ end
+
+ it 'shows confirmation link for email verification' do
+ expect(mail.body).to include('app/auth/confirmation?confirmation_token')
+ expect(confirmable_user.unconfirmed_email.blank?).to be false
+ end
+ end
+ end
+ end
+end
diff --git a/spec/enterprise/models/account_saml_settings_spec.rb b/spec/enterprise/models/account_saml_settings_spec.rb
new file mode 100644
index 000000000..ac35eb486
--- /dev/null
+++ b/spec/enterprise/models/account_saml_settings_spec.rb
@@ -0,0 +1,134 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+RSpec.describe AccountSamlSettings, type: :model do
+ let(:account) { create(:account) }
+ let(:saml_settings) { build(:account_saml_settings, account: account) }
+
+ describe 'associations' do
+ it { is_expected.to belong_to(:account) }
+ end
+
+ describe 'validations' do
+ it 'requires sso_url' do
+ settings = build(:account_saml_settings, account: account, sso_url: nil)
+ expect(settings).not_to be_valid
+ expect(settings.errors[:sso_url]).to include("can't be blank")
+ end
+
+ it 'requires certificate' do
+ settings = build(:account_saml_settings, account: account, certificate: nil)
+ expect(settings).not_to be_valid
+ expect(settings.errors[:certificate]).to include("can't be blank")
+ end
+
+ it 'requires idp_entity_id' do
+ settings = build(:account_saml_settings, account: account, idp_entity_id: nil)
+ expect(settings).not_to be_valid
+ expect(settings.errors[:idp_entity_id]).to include("can't be blank")
+ end
+ end
+
+ describe '#saml_enabled?' do
+ it 'returns true when required fields are present' do
+ settings = build(:account_saml_settings,
+ account: account,
+ sso_url: 'https://example.com/sso',
+ certificate: 'valid-certificate')
+ expect(settings.saml_enabled?).to be true
+ end
+
+ it 'returns false when sso_url is missing' do
+ settings = build(:account_saml_settings,
+ account: account,
+ sso_url: nil,
+ certificate: 'valid-certificate')
+ expect(settings.saml_enabled?).to be false
+ end
+
+ it 'returns false when certificate is missing' do
+ settings = build(:account_saml_settings,
+ account: account,
+ sso_url: 'https://example.com/sso',
+ certificate: nil)
+ expect(settings.saml_enabled?).to be false
+ end
+ end
+
+ describe 'sp_entity_id auto-generation' do
+ it 'automatically generates sp_entity_id when creating' do
+ settings = build(:account_saml_settings, account: account, sp_entity_id: nil)
+ expect(settings).to be_valid
+ settings.save!
+ expect(settings.sp_entity_id).to eq("http://localhost:3000/saml/sp/#{account.id}")
+ end
+
+ it 'does not override existing sp_entity_id' do
+ custom_id = 'https://custom.example.com/saml/sp/123'
+ settings = build(:account_saml_settings, account: account, sp_entity_id: custom_id)
+ settings.save!
+ expect(settings.sp_entity_id).to eq(custom_id)
+ end
+ end
+
+ describe '#certificate_fingerprint' do
+ let(:valid_cert_pem) do
+ key = OpenSSL::PKey::RSA.new(2048)
+ cert = OpenSSL::X509::Certificate.new
+ cert.version = 2
+ cert.serial = 1
+ cert.subject = OpenSSL::X509::Name.parse('/C=US/ST=Test/L=Test/O=Test/CN=test.example.com')
+ cert.issuer = cert.subject
+ cert.public_key = key.public_key
+ cert.not_before = Time.zone.now
+ cert.not_after = cert.not_before + (365 * 24 * 60 * 60)
+ cert.sign(key, OpenSSL::Digest.new('SHA256'))
+ cert.to_pem
+ end
+
+ it 'returns fingerprint for valid certificate' do
+ settings = build(:account_saml_settings, account: account, certificate: valid_cert_pem)
+ fingerprint = settings.certificate_fingerprint
+
+ expect(fingerprint).to be_present
+ expect(fingerprint).to match(/^[A-F0-9]{2}(:[A-F0-9]{2}){19}$/) # SHA1 fingerprint format
+ end
+
+ it 'returns nil for blank certificate' do
+ settings = build(:account_saml_settings, account: account, certificate: '')
+ expect(settings.certificate_fingerprint).to be_nil
+ end
+
+ it 'returns nil for invalid certificate' do
+ settings = build(:account_saml_settings, account: account, certificate: 'invalid-cert-data')
+ expect(settings.certificate_fingerprint).to be_nil
+ end
+
+ it 'formats fingerprint correctly' do
+ settings = build(:account_saml_settings, account: account, certificate: valid_cert_pem)
+ fingerprint = settings.certificate_fingerprint
+
+ # Should be uppercase with colons separating each byte
+ expect(fingerprint).to match(/^[A-F0-9:]+$/)
+ expect(fingerprint.count(':')).to eq(19) # 20 bytes = 19 colons
+ end
+ end
+
+ describe 'callbacks' do
+ describe 'after_create_commit' do
+ it 'queues job to set account users to saml provider' do
+ expect(Saml::UpdateAccountUsersProviderJob).to receive(:perform_later).with(account.id, 'saml')
+ create(:account_saml_settings, account: account)
+ end
+ end
+
+ describe 'after_destroy_commit' do
+ it 'queues job to reset account users provider' do
+ settings = create(:account_saml_settings, account: account)
+ expect(Saml::UpdateAccountUsersProviderJob).to receive(:perform_later).with(account.id, 'email')
+ settings.destroy
+ end
+ end
+ end
+end
diff --git a/spec/enterprise/models/inbox_spec.rb b/spec/enterprise/models/inbox_spec.rb
index 3e3e060d8..4ba1a7021 100644
--- a/spec/enterprise/models/inbox_spec.rb
+++ b/spec/enterprise/models/inbox_spec.rb
@@ -15,8 +15,8 @@ RSpec.describe Inbox do
create(:conversation, inbox: inbox, assignee: inbox_member_1.user)
# to test conversations in other inboxes won't impact
create_list(:conversation, 3, assignee: inbox_member_1.user)
- create_list(:conversation, 2, inbox: inbox, assignee: inbox_member_2.user)
- create_list(:conversation, 3, inbox: inbox, assignee: inbox_member_3.user)
+ create_list(:conversation, 2, inbox: inbox, account: inbox.account, assignee: inbox_member_2.user)
+ create_list(:conversation, 3, inbox: inbox, account: inbox.account, assignee: inbox_member_3.user)
end
it 'validated max_assignment_limit' do
@@ -33,7 +33,7 @@ RSpec.describe Inbox do
end
it 'returns all member ids when inbox max_assignment_limit is not configured' do
- expect(inbox.member_ids_with_assignment_capacity).to eq(inbox.members.ids)
+ expect(inbox.member_ids_with_assignment_capacity).to match_array(inbox.members.ids)
end
end
diff --git a/spec/enterprise/services/voice/inbound_call_builder_spec.rb b/spec/enterprise/services/voice/inbound_call_builder_spec.rb
new file mode 100644
index 000000000..12e2d7235
--- /dev/null
+++ b/spec/enterprise/services/voice/inbound_call_builder_spec.rb
@@ -0,0 +1,57 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+RSpec.describe Voice::InboundCallBuilder do
+ let(:account) { create(:account) }
+ let(:channel) { create(:channel_voice, account: account, phone_number: '+15551230001') }
+ let(:inbox) { channel.inbox }
+
+ let(:from_number) { '+15550001111' }
+ let(:to_number) { channel.phone_number }
+ let(:call_sid) { 'CA1234567890abcdef' }
+
+ before do
+ allow(Twilio::VoiceWebhookSetupService).to receive(:new)
+ .and_return(instance_double(Twilio::VoiceWebhookSetupService, perform: "AP#{SecureRandom.hex(16)}"))
+ end
+
+ def build_and_perform
+ described_class.new(
+ account: account,
+ inbox: inbox,
+ from_number: from_number,
+ to_number: to_number,
+ call_sid: call_sid
+ ).perform
+ end
+
+ it 'creates a new conversation with inbound ringing attributes' do
+ builder = build_and_perform
+ conversation = builder.conversation
+ expect(conversation).to be_present
+ expect(conversation.account_id).to eq(account.id)
+ expect(conversation.inbox_id).to eq(inbox.id)
+ expect(conversation.identifier).to eq(call_sid)
+ expect(conversation.additional_attributes['call_direction']).to eq('inbound')
+ expect(conversation.additional_attributes['call_status']).to eq('ringing')
+ end
+
+ it 'creates a voice_call message with ringing status' do
+ builder = build_and_perform
+ conversation = builder.conversation
+ msg = conversation.messages.voice_calls.last
+ expect(msg).to be_present
+ expect(msg.message_type).to eq('incoming')
+ expect(msg.content_type).to eq('voice_call')
+ expect(msg.content_attributes.dig('data', 'call_sid')).to eq(call_sid)
+ expect(msg.content_attributes.dig('data', 'status')).to eq('ringing')
+ end
+
+ it 'returns TwiML that informs the caller we are connecting' do
+ builder = build_and_perform
+ xml = builder.twiml_response
+ expect(xml).to include('Please wait while we connect you to an agent')
+ expect(xml).to include(' 'inbound', 'call_status' => 'ringing' }
+ )
+ end
+ let(:message) do
+ conversation.messages.create!(
+ account_id: account.id,
+ inbox_id: inbox.id,
+ message_type: :incoming,
+ sender: contact,
+ content: 'Voice Call',
+ content_type: 'voice_call',
+ content_attributes: { data: { call_sid: call_sid, status: 'ringing' } }
+ )
+ end
+ let(:channel) { create(:channel_voice, account: account, phone_number: '+15551230002') }
+ let(:inbox) { channel.inbox }
+ let(:from_number) { '+15550002222' }
+ let(:call_sid) { 'CATESTSTATUS123' }
+
+ before do
+ allow(Twilio::VoiceWebhookSetupService).to receive(:new)
+ .and_return(instance_double(Twilio::VoiceWebhookSetupService, perform: "AP#{SecureRandom.hex(16)}"))
+ end
+
+ it 'updates conversation and last voice message with call status' do
+ # Ensure records are created after stub setup
+ conversation
+ message
+
+ described_class.new(
+ account: account,
+ call_sid: call_sid,
+ call_status: 'completed'
+ ).perform
+
+ conversation.reload
+ message.reload
+
+ expect(conversation.additional_attributes['call_status']).to eq('completed')
+ expect(message.content_attributes.dig('data', 'status')).to eq('completed')
+ end
+
+ it 'no-ops when conversation not found' do
+ expect do
+ described_class.new(account: account, call_sid: 'UNKNOWN', call_status: 'busy').perform
+ end.not_to raise_error
+ end
+end
diff --git a/spec/factories/account_saml_settings.rb b/spec/factories/account_saml_settings.rb
new file mode 100644
index 000000000..4262daf7b
--- /dev/null
+++ b/spec/factories/account_saml_settings.rb
@@ -0,0 +1,31 @@
+FactoryBot.define do
+ factory :account_saml_settings do
+ account
+ sso_url { 'https://idp.example.com/saml/sso' }
+ certificate do
+ key = OpenSSL::PKey::RSA.new(2048)
+ cert = OpenSSL::X509::Certificate.new
+ cert.version = 2
+ cert.serial = 1
+ cert.subject = OpenSSL::X509::Name.parse('/C=US/ST=Test/L=Test/O=Test/CN=test.example.com')
+ cert.issuer = cert.subject
+ cert.public_key = key.public_key
+ cert.not_before = Time.zone.now
+ cert.not_after = cert.not_before + (365 * 24 * 60 * 60)
+ cert.sign(key, OpenSSL::Digest.new('SHA256'))
+ cert.to_pem
+ end
+ idp_entity_id { 'https://idp.example.com/saml/metadata' }
+ role_mappings { {} }
+
+ trait :with_role_mappings do
+ role_mappings do
+ {
+ 'Administrators' => { 'role' => 1 },
+ 'Agents' => { 'role' => 0 },
+ 'Custom-Team' => { 'custom_role_id' => 5 }
+ }
+ end
+ end
+ end
+end
diff --git a/spec/finders/email_channel_finder_spec.rb b/spec/finders/email_channel_finder_spec.rb
index fe57dec0b..d56d97008 100644
--- a/spec/finders/email_channel_finder_spec.rb
+++ b/spec/finders/email_channel_finder_spec.rb
@@ -2,6 +2,7 @@ require 'rails_helper'
describe EmailChannelFinder do
include ActionMailbox::TestHelper
+
let!(:channel_email) { create(:channel_email) }
describe '#perform' do
@@ -48,6 +49,75 @@ describe EmailChannelFinder do
expect(channel).to eq(channel_email)
end
+ it 'skip bcc email when account is configured to skip BCC processing' do
+ channel_email.update(email: 'test@example.com')
+ reply_mail.mail['to'] = nil
+ reply_mail.mail['bcc'] = 'test@example.com'
+
+ allow(GlobalConfigService).to receive(:load)
+ .with('SKIP_INCOMING_BCC_PROCESSING', '')
+ .and_return(channel_email.account_id.to_s)
+
+ channel = described_class.new(reply_mail.mail).perform
+ expect(channel).to be_nil
+ end
+
+ it 'skip bcc email when account is in multiple account ids config' do
+ channel_email.update(email: 'test@example.com')
+ reply_mail.mail['to'] = nil
+ reply_mail.mail['bcc'] = 'test@example.com'
+
+ # Include this account along with other account IDs
+ other_account_ids = [123, 456, channel_email.account_id, 789]
+ allow(GlobalConfigService).to receive(:load)
+ .with('SKIP_INCOMING_BCC_PROCESSING', '')
+ .and_return(other_account_ids.join(','))
+
+ channel = described_class.new(reply_mail.mail).perform
+ expect(channel).to be_nil
+ end
+
+ it 'process bcc email when account is not in skip config' do
+ channel_email.update(email: 'test@example.com')
+ reply_mail.mail['to'] = nil
+ reply_mail.mail['bcc'] = 'test@example.com'
+
+ # Configure other account IDs but not this one
+ other_account_ids = [123, 456, 789]
+ allow(GlobalConfigService).to receive(:load)
+ .with('SKIP_INCOMING_BCC_PROCESSING', '')
+ .and_return(other_account_ids.join(','))
+
+ channel = described_class.new(reply_mail.mail).perform
+ expect(channel).to eq(channel_email)
+ end
+
+ it 'process bcc email when skip config is empty' do
+ channel_email.update(email: 'test@example.com')
+ reply_mail.mail['to'] = nil
+ reply_mail.mail['bcc'] = 'test@example.com'
+
+ allow(GlobalConfigService).to receive(:load)
+ .with('SKIP_INCOMING_BCC_PROCESSING', '')
+ .and_return('')
+
+ channel = described_class.new(reply_mail.mail).perform
+ expect(channel).to eq(channel_email)
+ end
+
+ it 'process bcc email when skip config is nil' do
+ channel_email.update(email: 'test@example.com')
+ reply_mail.mail['to'] = nil
+ reply_mail.mail['bcc'] = 'test@example.com'
+
+ allow(GlobalConfigService).to receive(:load)
+ .with('SKIP_INCOMING_BCC_PROCESSING', '')
+ .and_return(nil)
+
+ channel = described_class.new(reply_mail.mail).perform
+ expect(channel).to eq(channel_email)
+ end
+
it 'return channel with X-Original-To email' do
channel_email.update(email: 'test@example.com')
reply_mail.mail['to'] = nil
@@ -55,6 +125,19 @@ describe EmailChannelFinder do
channel = described_class.new(reply_mail.mail).perform
expect(channel).to eq(channel_email)
end
+
+ it 'process X-Original-To email even when account is configured to skip BCC processing' do
+ channel_email.update(email: 'test@example.com')
+ reply_mail.mail['to'] = nil
+ reply_mail.mail['X-Original-To'] = 'test@example.com'
+
+ allow(GlobalConfigService).to receive(:load)
+ .with('SKIP_INCOMING_BCC_PROCESSING', '')
+ .and_return(channel_email.account_id.to_s)
+
+ channel = described_class.new(reply_mail.mail).perform
+ expect(channel).to eq(channel_email)
+ end
end
end
end
diff --git a/spec/jobs/avatar/avatar_from_url_job_spec.rb b/spec/jobs/avatar/avatar_from_url_job_spec.rb
index 2e6d89804..8db3769ad 100644
--- a/spec/jobs/avatar/avatar_from_url_job_spec.rb
+++ b/spec/jobs/avatar/avatar_from_url_job_spec.rb
@@ -1,36 +1,119 @@
require 'rails_helper'
RSpec.describe Avatar::AvatarFromUrlJob do
- let(:avatarable) { create(:contact) }
- let(:avatar_url) { 'https://example.com/avatar.png' }
+ let(:file) { fixture_file_upload(Rails.root.join('spec/assets/avatar.png'), 'image/png') }
+ let(:valid_url) { 'https://example.com/avatar.png' }
it 'enqueues the job' do
- expect { described_class.perform_later(avatarable, avatar_url) }.to have_enqueued_job(described_class)
- .on_queue('purgable')
+ contact = create(:contact)
+ expect { described_class.perform_later(contact, 'https://example.com/avatar.png') }
+ .to have_enqueued_job(described_class).on_queue('purgable')
end
- it 'will attach avatar from url' do
- expect(avatarable.avatar).not_to be_attached
- expect(Down).to receive(:download).with(avatar_url,
- max_size: 15 * 1024 * 1024).and_return(fixture_file_upload(Rails.root.join('spec/assets/avatar.png'),
- 'image/png'))
- described_class.perform_now(avatarable, avatar_url)
- expect(avatarable.avatar).to be_attached
+ context 'with rate-limited avatarable (Contact)' do
+ let(:avatarable) { create(:contact) }
+
+ it 'attaches and updates sync attributes' do
+ expect(Down).to receive(:download).with(valid_url, max_size: Avatar::AvatarFromUrlJob::MAX_DOWNLOAD_SIZE).and_return(file)
+ described_class.perform_now(avatarable, valid_url)
+ avatarable.reload
+ expect(avatarable.avatar).to be_attached
+ expect(avatarable.additional_attributes['avatar_url_hash']).to eq(Digest::SHA256.hexdigest(valid_url))
+ expect(avatarable.additional_attributes['last_avatar_sync_at']).to be_present
+ end
+
+ it 'returns early when rate limited' do
+ ts = 30.seconds.ago.iso8601
+ avatarable.update(additional_attributes: { 'last_avatar_sync_at' => ts })
+ expect(Down).not_to receive(:download)
+ described_class.perform_now(avatarable, valid_url)
+ avatarable.reload
+ expect(avatarable.avatar).not_to be_attached
+ expect(avatarable.additional_attributes['last_avatar_sync_at']).to be_present
+ expect(Time.zone.parse(avatarable.additional_attributes['last_avatar_sync_at']))
+ .to be > Time.zone.parse(ts)
+ expect(avatarable.additional_attributes['avatar_url_hash']).to eq(Digest::SHA256.hexdigest(valid_url))
+ end
+
+ it 'returns early when hash unchanged' do
+ avatarable.update(additional_attributes: { 'avatar_url_hash' => Digest::SHA256.hexdigest(valid_url) })
+ expect(Down).not_to receive(:download)
+ described_class.perform_now(avatarable, valid_url)
+ expect(avatarable.avatar).not_to be_attached
+ avatarable.reload
+ expect(avatarable.additional_attributes['last_avatar_sync_at']).to be_present
+ expect(avatarable.additional_attributes['avatar_url_hash']).to eq(Digest::SHA256.hexdigest(valid_url))
+ end
+
+ it 'updates sync attributes even when URL is invalid' do
+ invalid_url = 'invalid_url'
+ expect(Down).not_to receive(:download)
+ described_class.perform_now(avatarable, invalid_url)
+ avatarable.reload
+ expect(avatarable.avatar).not_to be_attached
+ expect(avatarable.additional_attributes['last_avatar_sync_at']).to be_present
+ expect(avatarable.additional_attributes['avatar_url_hash']).to eq(Digest::SHA256.hexdigest(invalid_url))
+ end
+
+ it 'updates sync attributes when file download is valid but content type is unsupported' do
+ temp_file = Tempfile.new(['invalid', '.xml'])
+ temp_file.write('content')
+ temp_file.rewind
+
+ uploaded = ActionDispatch::Http::UploadedFile.new(
+ tempfile: temp_file,
+ filename: 'invalid.xml',
+ type: 'application/xml'
+ )
+
+ expect(Down).to receive(:download).with(valid_url, max_size: Avatar::AvatarFromUrlJob::MAX_DOWNLOAD_SIZE).and_return(uploaded)
+
+ described_class.perform_now(avatarable, valid_url)
+ avatarable.reload
+
+ expect(avatarable.avatar).not_to be_attached
+ expect(avatarable.additional_attributes['last_avatar_sync_at']).to be_present
+ expect(avatarable.additional_attributes['avatar_url_hash']).to eq(Digest::SHA256.hexdigest(valid_url))
+
+ temp_file.close
+ temp_file.unlink
+ end
+ end
+
+ context 'with regular avatarable' do
+ let(:avatarable) { create(:agent_bot) }
+
+ it 'downloads and attaches avatar' do
+ expect(Down).to receive(:download).with(valid_url, max_size: Avatar::AvatarFromUrlJob::MAX_DOWNLOAD_SIZE).and_return(file)
+ described_class.perform_now(avatarable, valid_url)
+ expect(avatarable.avatar).to be_attached
+ end
end
# ref: https://github.com/chatwoot/chatwoot/issues/10449
- it 'will not throw error if the avatar url is not valid and the file does not have a filename' do
- # Create a temporary file with no filename and content type application/xml
+ it 'does not raise error when downloaded file has no filename (invalid content)' do
+ contact = create(:contact)
temp_file = Tempfile.new(['invalid', '.xml'])
temp_file.write('content')
temp_file.rewind
- expect(Down).to receive(:download).with(avatar_url, max_size: 15 * 1024 * 1024)
+ expect(Down).to receive(:download).with(valid_url, max_size: Avatar::AvatarFromUrlJob::MAX_DOWNLOAD_SIZE)
.and_return(ActionDispatch::Http::UploadedFile.new(tempfile: temp_file, type: 'application/xml'))
- expect { described_class.perform_now(avatarable, avatar_url) }.not_to raise_error
+ expect { described_class.perform_now(contact, valid_url) }.not_to raise_error
temp_file.close
- temp_file.unlink # deletes the temp file
+ temp_file.unlink
+ end
+
+ it 'skips sync attribute updates when URL is nil' do
+ contact = create(:contact)
+ expect(Down).not_to receive(:download)
+
+ expect { described_class.perform_now(contact, nil) }.not_to raise_error
+
+ contact.reload
+ expect(contact.additional_attributes['last_avatar_sync_at']).to be_nil
+ expect(contact.additional_attributes['avatar_url_hash']).to be_nil
end
end
diff --git a/spec/jobs/delete_object_job_spec.rb b/spec/jobs/delete_object_job_spec.rb
index 854ff1da2..cb01ceb34 100644
--- a/spec/jobs/delete_object_job_spec.rb
+++ b/spec/jobs/delete_object_job_spec.rb
@@ -1,20 +1,74 @@
require 'rails_helper'
-RSpec.describe DeleteObjectJob do
- subject(:job) { described_class.perform_later(account) }
+RSpec.describe DeleteObjectJob, type: :job do
+ describe '#perform' do
+ context 'when object is heavy (Inbox)' do
+ let!(:account) { create(:account) }
+ let!(:inbox) { create(:inbox, account: account) }
- let(:account) { create(:account) }
+ before do
+ create_list(:conversation, 3, account: account, inbox: inbox)
+ ReportingEvent.create!(account: account, inbox: inbox, name: 'inbox_metric', value: 1.0)
+ end
- it 'enqueues the job' do
- expect { job }.to have_enqueued_job(described_class)
- .with(account)
- .on_queue('low')
- end
+ it 'enqueues on the low queue' do
+ expect { described_class.perform_later(inbox) }
+ .to have_enqueued_job(described_class).with(inbox).on_queue('low')
+ end
- context 'when an object is passed to the job' do
- it 'is deleted' do
- described_class.perform_now(account)
- expect { account.reload }.to raise_error(ActiveRecord::RecordNotFound)
+ it 'pre-deletes heavy associations and then destroys the object' do
+ conv_ids = inbox.conversations.pluck(:id)
+ ci_ids = inbox.contact_inboxes.pluck(:id)
+ contact_ids = inbox.contacts.pluck(:id)
+ re_ids = inbox.reporting_events.pluck(:id)
+
+ described_class.perform_now(inbox)
+
+ expect(Conversation.where(id: conv_ids)).to be_empty
+ expect(ContactInbox.where(id: ci_ids)).to be_empty
+ expect(ReportingEvent.where(id: re_ids)).to be_empty
+ # Contacts should not be deleted for inbox destroy
+ expect(Contact.where(id: contact_ids)).not_to be_empty
+ expect { inbox.reload }.to raise_error(ActiveRecord::RecordNotFound)
+ end
+ end
+
+ context 'when object is heavy (Account)' do
+ let!(:account) { create(:account) }
+ let!(:inbox1) { create(:inbox, account: account) }
+ let!(:inbox2) { create(:inbox, account: account) }
+
+ before do
+ create_list(:conversation, 2, account: account, inbox: inbox1)
+ create_list(:conversation, 1, account: account, inbox: inbox2)
+ ReportingEvent.create!(account: account, name: 'acct_metric', value: 2.5)
+ ReportingEvent.create!(account: account, inbox: inbox1, name: 'acct_inbox_metric', value: 3.5)
+ end
+
+ it 'pre-deletes conversations, contacts, inboxes and reporting events and then destroys the account' do
+ conv_ids = account.conversations.pluck(:id)
+ contact_ids = account.contacts.pluck(:id)
+ inbox_ids = account.inboxes.pluck(:id)
+ re_ids = account.reporting_events.pluck(:id)
+
+ described_class.perform_now(account)
+
+ expect(Conversation.where(id: conv_ids)).to be_empty
+ expect(Contact.where(id: contact_ids)).to be_empty
+ expect(Inbox.where(id: inbox_ids)).to be_empty
+ expect(ReportingEvent.where(id: re_ids)).to be_empty
+ expect { account.reload }.to raise_error(ActiveRecord::RecordNotFound)
+ end
+ end
+
+ context 'when object is regular (Label)' do
+ it 'just destroys the object' do
+ label = create(:label)
+
+ described_class.perform_now(label)
+
+ expect { label.reload }.to raise_error(ActiveRecord::RecordNotFound)
+ end
end
end
end
diff --git a/spec/listeners/automation_rule_listener_labels_spec.rb b/spec/listeners/automation_rule_listener_labels_spec.rb
new file mode 100644
index 000000000..36002f7f3
--- /dev/null
+++ b/spec/listeners/automation_rule_listener_labels_spec.rb
@@ -0,0 +1,244 @@
+require 'rails_helper'
+
+describe AutomationRuleListener do
+ let(:listener) { described_class.instance }
+ let!(:account) { create(:account) }
+ let!(:user) { create(:user, account: account) }
+ let!(:inbox) { create(:inbox, account: account) }
+ let!(:contact) { create(:contact, account: account) }
+ let!(:conversation) { create(:conversation, account: account, inbox: inbox, contact: contact) }
+ let(:label1) { create(:label, account: account, title: 'bug') }
+ let(:label2) { create(:label, account: account, title: 'feature') }
+ let(:label3) { create(:label, account: account, title: 'urgent') }
+
+ before do
+ Current.user = user
+ end
+
+ describe 'conversation_updated with label conditions and actions' do
+ context 'when label is added and automation rule has label condition' do
+ let(:automation_rule) do
+ create(:automation_rule,
+ event_name: 'conversation_updated',
+ account: account,
+ conditions: [
+ {
+ attribute_key: 'labels',
+ filter_operator: 'equal_to',
+ values: ['bug'],
+ query_operator: nil
+ }
+ ],
+ actions: [
+ {
+ action_name: 'add_label',
+ action_params: ['urgent']
+ },
+ {
+ action_name: 'send_message',
+ action_params: ['Bug report received. We will investigate this issue.']
+ }
+ ])
+ end
+
+ it 'triggers automation when the specified label is added' do
+ automation_rule # Create the automation rule
+ expect(Messages::MessageBuilder).to receive(:new).and_call_original
+
+ # Add the 'bug' label to trigger the automation
+ conversation.add_labels(['bug'])
+
+ # Dispatch the event
+ event = Events::Base.new('conversation_updated', Time.zone.now, {
+ conversation: conversation,
+ changed_attributes: { label_list: [[], ['bug']] }
+ })
+
+ listener.conversation_updated(event)
+
+ # Verify the label was added by automation
+ expect(conversation.reload.label_list).to include('urgent')
+
+ # Verify a message was sent
+ expect(conversation.messages.last.content).to eq('Bug report received. We will investigate this issue.')
+ end
+
+ it 'does not trigger automation when a different label is added' do
+ automation_rule # Create the automation rule
+ expect(Messages::MessageBuilder).not_to receive(:new)
+
+ # Add a different label
+ conversation.add_labels(['feature'])
+
+ event = Events::Base.new('conversation_updated', Time.zone.now, {
+ conversation: conversation,
+ changed_attributes: { label_list: [[], ['feature']] }
+ })
+
+ listener.conversation_updated(event)
+
+ # Verify the automation did not run
+ expect(conversation.reload.label_list).not_to include('urgent')
+ end
+ end
+
+ context 'when automation rule has is_present label condition' do
+ let(:automation_rule) do
+ create(:automation_rule,
+ event_name: 'conversation_updated',
+ account: account,
+ conditions: [
+ {
+ attribute_key: 'labels',
+ filter_operator: 'is_present',
+ values: [],
+ query_operator: nil
+ }
+ ],
+ actions: [
+ {
+ action_name: 'send_message',
+ action_params: ['Thank you for adding a label to categorize this conversation.']
+ }
+ ])
+ end
+
+ it 'triggers automation when any label is added to an unlabeled conversation' do
+ automation_rule # Create the automation rule
+ expect(Messages::MessageBuilder).to receive(:new).and_call_original
+
+ # Add any label to trigger the automation
+ conversation.add_labels(['feature'])
+
+ event = Events::Base.new('conversation_updated', Time.zone.now, {
+ conversation: conversation,
+ changed_attributes: { label_list: [[], ['feature']] }
+ })
+
+ listener.conversation_updated(event)
+
+ # Verify a message was sent
+ expect(conversation.messages.last.content).to eq('Thank you for adding a label to categorize this conversation.')
+ end
+
+ it 'still triggers when labels are removed but conversation still has labels' do
+ automation_rule # Create the automation rule
+ # Start with multiple labels
+ conversation.add_labels(%w[bug feature])
+ conversation.reload
+
+ expect(Messages::MessageBuilder).to receive(:new).and_call_original
+
+ # Remove one label but conversation still has labels
+ conversation.update_labels(['bug'])
+
+ event = Events::Base.new('conversation_updated', Time.zone.now, {
+ conversation: conversation,
+ changed_attributes: { label_list: [%w[bug feature], ['bug']] }
+ })
+
+ listener.conversation_updated(event)
+
+ # Should still trigger because conversation has labels (is_present condition)
+ expect(conversation.messages.last.content).to eq('Thank you for adding a label to categorize this conversation.')
+ end
+
+ it 'does not trigger when all labels are removed' do
+ automation_rule # Create the automation rule
+ # Start with labels
+ conversation.add_labels(['bug'])
+ conversation.reload
+
+ expect(Messages::MessageBuilder).not_to receive(:new)
+
+ # Remove all labels
+ conversation.update_labels([])
+
+ event = Events::Base.new('conversation_updated', Time.zone.now, {
+ conversation: conversation,
+ changed_attributes: { label_list: [['bug'], []] }
+ })
+
+ listener.conversation_updated(event)
+ end
+ end
+
+ context 'when automation rule has remove_label action' do
+ let!(:automation_rule) do
+ create(:automation_rule,
+ event_name: 'conversation_updated',
+ account: account,
+ conditions: [
+ {
+ attribute_key: 'labels',
+ filter_operator: 'equal_to',
+ values: ['urgent'],
+ query_operator: nil
+ }
+ ],
+ actions: [
+ {
+ action_name: 'remove_label',
+ action_params: ['bug']
+ }
+ ])
+ end
+
+ it 'removes specified labels when condition is met' do
+ automation_rule # Create the automation rule
+ # Start with both labels
+ conversation.add_labels(%w[bug urgent])
+
+ event = Events::Base.new('conversation_updated', Time.zone.now, {
+ conversation: conversation,
+ changed_attributes: { label_list: [['bug'], %w[bug urgent]] }
+ })
+
+ listener.conversation_updated(event)
+
+ # Verify the bug label was removed but urgent remains
+ expect(conversation.reload.label_list).to include('urgent')
+ expect(conversation.reload.label_list).not_to include('bug')
+ end
+ end
+ end
+
+ describe 'preventing infinite loops' do
+ let!(:automation_rule) do
+ create(:automation_rule,
+ event_name: 'conversation_updated',
+ account: account,
+ conditions: [
+ {
+ attribute_key: 'labels',
+ filter_operator: 'equal_to',
+ values: ['bug'],
+ query_operator: nil
+ }
+ ],
+ actions: [
+ {
+ action_name: 'add_label',
+ action_params: ['processed']
+ }
+ ])
+ end
+
+ it 'does not trigger automation when performed by automation rule' do
+ automation_rule # Create the automation rule
+ conversation.add_labels(['bug'])
+
+ # Simulate event performed by automation rule
+ event = Events::Base.new('conversation_updated', Time.zone.now, {
+ conversation: conversation,
+ changed_attributes: { label_list: [[], ['bug']] },
+ performed_by: automation_rule
+ })
+
+ # Should not process the event since it was performed by automation
+ expect(AutomationRules::ActionService).not_to receive(:new)
+
+ listener.conversation_updated(event)
+ end
+ end
+end
diff --git a/spec/mailboxes/application_mailbox_spec.rb b/spec/mailboxes/application_mailbox_spec.rb
index f4f28d811..33bbf9de8 100644
--- a/spec/mailboxes/application_mailbox_spec.rb
+++ b/spec/mailboxes/application_mailbox_spec.rb
@@ -66,6 +66,20 @@ RSpec.describe ApplicationMailbox do
expect(dbl).to receive(:perform_processing).and_return(true)
described_class.route reply_cc_mail
end
+
+ it 'skips routing when BCC processing is disabled for account' do
+ allow(GlobalConfigService).to receive(:load).with('SKIP_INCOMING_BCC_PROCESSING', '').and_return(channel_email.account_id.to_s)
+
+ # Create a BCC-only email scenario
+ bcc_mail = create_inbound_email_from_fixture('support.eml')
+ bcc_mail.mail['to'] = nil
+ bcc_mail.mail['bcc'] = 'care@example.com'
+
+ channel_email.update(email: 'care@example.com')
+
+ expect(DefaultMailbox).to receive(:new).and_return(double.tap { |d| expect(d).to receive(:perform_processing) })
+ described_class.route bcc_mail
+ end
end
describe 'Invalid Mail To Address' do
diff --git a/spec/mailboxes/support_mailbox_spec.rb b/spec/mailboxes/support_mailbox_spec.rb
index f6964285d..0dbfbbe3b 100644
--- a/spec/mailboxes/support_mailbox_spec.rb
+++ b/spec/mailboxes/support_mailbox_spec.rb
@@ -334,5 +334,19 @@ RSpec.describe SupportMailbox do
expect(conversation.messages.last.content_attributes['email']['subject']).to eq('attachment with html')
end
end
+
+ describe 'when BCC processing is disabled for account' do
+ before do
+ allow(GlobalConfigService).to receive(:load).with('SKIP_INCOMING_BCC_PROCESSING', '').and_return(account.id.to_s)
+ end
+
+ it 'does not process BCC-only emails' do
+ bcc_mail = create_inbound_email_from_fixture('support.eml')
+ bcc_mail.mail['to'] = nil
+ bcc_mail.mail['bcc'] = 'care@example.com'
+
+ expect { described_class.receive bcc_mail }.to raise_error('Email channel/inbox not found')
+ end
+ end
end
end
diff --git a/spec/mailers/administrator_notifications/base_mailer_spec.rb b/spec/mailers/administrator_notifications/base_mailer_spec.rb
index 619fef0a7..1524a46cc 100644
--- a/spec/mailers/administrator_notifications/base_mailer_spec.rb
+++ b/spec/mailers/administrator_notifications/base_mailer_spec.rb
@@ -17,8 +17,7 @@ RSpec.describe AdministratorNotifications::BaseMailer do
# Call the private method
admin_emails = mailer.send(:admin_emails)
- expect(admin_emails).to include(admin1.email)
- expect(admin_emails).to include(admin2.email)
+ expect(admin_emails).to contain_exactly(admin1.email, admin2.email)
expect(admin_emails).not_to include(agent.email)
end
end
@@ -49,7 +48,7 @@ RSpec.describe AdministratorNotifications::BaseMailer do
# Mock the send_mail_with_liquid method
expect(mailer).to receive(:send_mail_with_liquid).with(
- to: [admin1.email, admin2.email],
+ to: contain_exactly(admin1.email, admin2.email),
subject: subject
).and_return(true)
diff --git a/spec/mailers/administrator_notifications/channel_notifications_mailer_spec.rb b/spec/mailers/administrator_notifications/channel_notifications_mailer_spec.rb
index e5cd7327b..39bc4ee1a 100644
--- a/spec/mailers/administrator_notifications/channel_notifications_mailer_spec.rb
+++ b/spec/mailers/administrator_notifications/channel_notifications_mailer_spec.rb
@@ -9,6 +9,7 @@ RSpec.describe AdministratorNotifications::ChannelNotificationsMailer do
let(:class_instance) { described_class.new }
let!(:account) { create(:account) }
let!(:administrator) { create(:user, :administrator, email: 'agent1@example.com', account: account) }
+ let!(:another_administrator) { create(:user, :administrator, email: 'agent2@example.com', account: account) }
describe 'facebook_disconnect' do
before do
@@ -26,7 +27,7 @@ RSpec.describe AdministratorNotifications::ChannelNotificationsMailer do
end
it 'renders the receiver email' do
- expect(mail.to).to eq([administrator.email])
+ expect(mail.to).to contain_exactly(administrator.email, another_administrator.email)
end
end
end
@@ -41,7 +42,7 @@ RSpec.describe AdministratorNotifications::ChannelNotificationsMailer do
end
it 'renders the receiver email' do
- expect(mail.to).to eq([administrator.email])
+ expect(mail.to).to contain_exactly(administrator.email, another_administrator.email)
end
end
@@ -55,7 +56,7 @@ RSpec.describe AdministratorNotifications::ChannelNotificationsMailer do
end
it 'renders the receiver email' do
- expect(mail.to).to eq([administrator.email])
+ expect(mail.to).to contain_exactly(administrator.email, another_administrator.email)
end
end
end
diff --git a/spec/mailers/administrator_notifications/integrations_notification_mailer_spec.rb b/spec/mailers/administrator_notifications/integrations_notification_mailer_spec.rb
index 331d33d06..04bc405f4 100644
--- a/spec/mailers/administrator_notifications/integrations_notification_mailer_spec.rb
+++ b/spec/mailers/administrator_notifications/integrations_notification_mailer_spec.rb
@@ -6,6 +6,7 @@ RSpec.describe AdministratorNotifications::IntegrationsNotificationMailer do
let!(:account) { create(:account) }
let!(:administrator) { create(:user, :administrator, email: 'admin@example.com', account: account) }
+ let!(:another_administrator) { create(:user, :administrator, email: 'owner@example.com', account: account) }
describe 'slack_disconnect' do
let(:mail) { described_class.with(account: account).slack_disconnect.deliver_now }
@@ -15,7 +16,7 @@ RSpec.describe AdministratorNotifications::IntegrationsNotificationMailer do
end
it 'renders the receiver email' do
- expect(mail.to).to eq([administrator.email])
+ expect(mail.to).to contain_exactly(administrator.email, another_administrator.email)
end
it 'includes reconnect instructions in the body' do
@@ -35,7 +36,7 @@ RSpec.describe AdministratorNotifications::IntegrationsNotificationMailer do
end
it 'renders the receiver email' do
- expect(mail.to).to eq([administrator.email])
+ expect(mail.to).to contain_exactly(administrator.email, another_administrator.email)
end
end
end
diff --git a/spec/models/account_spec.rb b/spec/models/account_spec.rb
index 51ab792cf..842aaf732 100644
--- a/spec/models/account_spec.rb
+++ b/spec/models/account_spec.rb
@@ -8,7 +8,6 @@ RSpec.describe Account do
it { is_expected.to have_many(:inboxes).dependent(:destroy_async) }
it { is_expected.to have_many(:conversations).dependent(:destroy_async) }
it { is_expected.to have_many(:contacts).dependent(:destroy_async) }
- it { is_expected.to have_many(:telegram_bots).dependent(:destroy_async) }
it { is_expected.to have_many(:canned_responses).dependent(:destroy_async) }
it { is_expected.to have_many(:facebook_pages).class_name('::Channel::FacebookPage').dependent(:destroy_async) }
it { is_expected.to have_many(:web_widgets).class_name('::Channel::WebWidget').dependent(:destroy_async) }
diff --git a/spec/models/automation_rule_spec.rb b/spec/models/automation_rule_spec.rb
index 53ebfa0c7..91452b8a4 100644
--- a/spec/models/automation_rule_spec.rb
+++ b/spec/models/automation_rule_spec.rb
@@ -60,6 +60,32 @@ RSpec.describe AutomationRule do
expect(rule.valid?).to be false
expect(rule.errors.messages[:conditions]).to eq(['Automation conditions should have query operator.'])
end
+
+ it 'allows labels as a valid condition attribute' do
+ params[:conditions] = [
+ {
+ attribute_key: 'labels',
+ filter_operator: 'equal_to',
+ values: ['bug'],
+ query_operator: nil
+ }
+ ]
+ rule = FactoryBot.build(:automation_rule, params)
+ expect(rule.valid?).to be true
+ end
+
+ it 'validates label condition operators' do
+ params[:conditions] = [
+ {
+ attribute_key: 'labels',
+ filter_operator: 'is_present',
+ values: [],
+ query_operator: nil
+ }
+ ]
+ rule = FactoryBot.build(:automation_rule, params)
+ expect(rule.valid?).to be true
+ end
end
describe 'reauthorizable' do
diff --git a/spec/models/concerns/featurable_spec.rb b/spec/models/concerns/featurable_spec.rb
deleted file mode 100644
index 1cf0b87f2..000000000
--- a/spec/models/concerns/featurable_spec.rb
+++ /dev/null
@@ -1,57 +0,0 @@
-require 'rails_helper'
-
-RSpec.describe Featurable do
- let(:account) { create(:account) }
-
- describe 'WhatsApp embedded signup feature' do
- it 'is disabled by default' do
- expect(account.feature_whatsapp_embedded_signup?).to be false
- expect(account.feature_enabled?('whatsapp_embedded_signup')).to be false
- end
-
- describe '#enable_features!' do
- it 'enables the whatsapp embedded signup feature' do
- account.enable_features!(:whatsapp_embedded_signup)
- expect(account.feature_whatsapp_embedded_signup?).to be true
- expect(account.feature_enabled?('whatsapp_embedded_signup')).to be true
- end
-
- it 'enables multiple features at once' do
- account.enable_features!(:whatsapp_embedded_signup, :help_center)
- expect(account.feature_whatsapp_embedded_signup?).to be true
- expect(account.feature_help_center?).to be true
- end
- end
-
- describe '#disable_features!' do
- before do
- account.enable_features!(:whatsapp_embedded_signup)
- end
-
- it 'disables the whatsapp embedded signup feature' do
- expect(account.feature_whatsapp_embedded_signup?).to be true
-
- account.disable_features!(:whatsapp_embedded_signup)
- expect(account.feature_whatsapp_embedded_signup?).to be false
- end
- end
-
- describe '#enabled_features' do
- it 'includes whatsapp_embedded_signup when enabled' do
- account.enable_features!(:whatsapp_embedded_signup)
- expect(account.enabled_features).to include('whatsapp_embedded_signup' => true)
- end
-
- it 'does not include whatsapp_embedded_signup when disabled' do
- account.disable_features!(:whatsapp_embedded_signup)
- expect(account.enabled_features).not_to include('whatsapp_embedded_signup' => true)
- end
- end
-
- describe '#all_features' do
- it 'includes whatsapp_embedded_signup in all features list' do
- expect(account.all_features).to have_key('whatsapp_embedded_signup')
- end
- end
- end
-end
diff --git a/spec/models/concerns/switch_locale_spec.rb b/spec/models/concerns/switch_locale_spec.rb
index 98628b92c..421f254a0 100644
--- a/spec/models/concerns/switch_locale_spec.rb
+++ b/spec/models/concerns/switch_locale_spec.rb
@@ -29,6 +29,26 @@ RSpec.describe 'SwitchLocale Concern', type: :controller do
end
end
+ context 'when user has a locale set in ui_settings' do
+ let(:user) { create(:user, ui_settings: { 'locale' => 'es' }) }
+
+ before { controller.instance_variable_set(:@user, user) }
+
+ it 'returns the user locale' do
+ expect(controller.send(:locale_from_user)).to eq('es')
+ end
+ end
+
+ context 'when user does not have a locale set' do
+ let(:user) { create(:user, ui_settings: {}) }
+
+ before { controller.instance_variable_set(:@user, user) }
+
+ it 'returns nil' do
+ expect(controller.send(:locale_from_user)).to be_nil
+ end
+ end
+
context 'when request is from custom domain' do
before { request.host = portal.custom_domain }
diff --git a/spec/models/conversation_spec.rb b/spec/models/conversation_spec.rb
index a29359528..007ce987f 100644
--- a/spec/models/conversation_spec.rb
+++ b/spec/models/conversation_spec.rb
@@ -136,7 +136,7 @@ RSpec.describe Conversation do
notifiable_assignee_change: false,
changed_attributes: changed_attributes,
performed_by: nil
- ).exactly(2).times
+ )
end
it 'runs after_update callbacks' do
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index c740c67fb..213988a0d 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -110,4 +110,148 @@ RSpec.describe User do
expect(new_user.email).to eq('test123@test.com')
end
end
+
+ describe '2FA/MFA functionality' do
+ before do
+ skip('Skipping since MFA is not configured in this environment') unless Chatwoot.encryption_configured?
+ end
+
+ let(:user) { create(:user, password: 'Test@123456') }
+
+ describe '#enable_two_factor!' do
+ it 'generates OTP secret for 2FA setup' do
+ expect(user.otp_secret).to be_nil
+ expect(user.otp_required_for_login).to be_falsey
+
+ user.enable_two_factor!
+
+ expect(user.otp_secret).not_to be_nil
+ # otp_required_for_login is false until verification is complete
+ expect(user.otp_required_for_login).to be_falsey
+ end
+ end
+
+ describe '#disable_two_factor!' do
+ before do
+ user.enable_two_factor!
+ user.update!(otp_required_for_login: true) # Simulate verified 2FA
+ user.generate_backup_codes!
+ end
+
+ it 'disables 2FA and clears OTP secret' do
+ user.disable_two_factor!
+
+ expect(user.otp_secret).to be_nil
+ expect(user.otp_required_for_login).to be_falsey
+ expect(user.otp_backup_codes).to be_blank # Can be nil or empty array
+ end
+ end
+
+ describe '#generate_backup_codes!' do
+ before do
+ user.enable_two_factor!
+ end
+
+ it 'generates 10 backup codes' do
+ codes = user.generate_backup_codes!
+
+ expect(codes).to be_an(Array)
+ expect(codes.length).to eq(10)
+ expect(codes.first).to match(/\A[A-F0-9]{8}\z/) # 8-character hex codes
+ expect(user.otp_backup_codes).not_to be_nil
+ end
+ end
+
+ describe '#two_factor_provisioning_uri' do
+ before do
+ user.enable_two_factor!
+ end
+
+ it 'generates a valid provisioning URI for QR code' do
+ uri = user.two_factor_provisioning_uri
+
+ expect(uri).to include('otpauth://totp/')
+ expect(uri).to include(CGI.escape(user.email))
+ expect(uri).to include('Chatwoot')
+ end
+ end
+
+ describe '#validate_backup_code!' do
+ let(:backup_codes) { user.generate_backup_codes! }
+
+ before do
+ user.enable_two_factor!
+ backup_codes
+ end
+
+ it 'validates and invalidates correct backup code' do
+ code = backup_codes.first
+ result = user.validate_backup_code!(code)
+ expect(result).to be_truthy
+
+ # Verify it's marked as used
+ user.reload
+ expect(user.otp_backup_codes).to include('XXXXXXXX')
+ end
+
+ it 'rejects invalid backup code' do
+ result = user.validate_backup_code!('invalid')
+ expect(result).to be_falsey
+ end
+
+ it 'rejects already used backup code' do
+ code = backup_codes.first
+ user.validate_backup_code!(code)
+
+ # Try to use the same code again
+ result = user.validate_backup_code!(code)
+ expect(result).to be_falsey
+ end
+
+ it 'handles blank code' do
+ result = user.validate_backup_code!(nil)
+ expect(result).to be_falsey
+
+ result = user.validate_backup_code!('')
+ expect(result).to be_falsey
+ end
+ end
+ end
+
+ describe '#active_account_user' do
+ let(:user) { create(:user) }
+ let(:account1) { create(:account) }
+ let(:account2) { create(:account) }
+ let(:account3) { create(:account) }
+
+ before do
+ # Create account_users with different active_at values
+ create(:account_user, user: user, account: account1, active_at: 2.days.ago)
+ create(:account_user, user: user, account: account2, active_at: 1.day.ago)
+ create(:account_user, user: user, account: account3, active_at: nil) # New account with NULL active_at
+ end
+
+ it 'returns the account_user with the most recent active_at, prioritizing timestamps over NULL values' do
+ # Should return account2 (most recent timestamp) even though account3 was created last with NULL active_at
+ expect(user.active_account_user.account_id).to eq(account2.id)
+ end
+
+ it 'returns NULL active_at account only when no other accounts have active_at' do
+ # Remove active_at from all accounts
+ user.account_users.each { |au| au.update!(active_at: nil) }
+
+ # Should return one of the accounts (behavior is undefined but consistent)
+ expect(user.active_account_user).to be_present
+ end
+
+ context 'when multiple accounts have NULL active_at' do
+ before do
+ create(:account_user, user: user, account: create(:account), active_at: nil)
+ end
+
+ it 'still prioritizes accounts with timestamps' do
+ expect(user.active_account_user.account_id).to eq(account2.id)
+ end
+ end
+ end
end
diff --git a/spec/requests/api/v1/profile/mfa_controller_spec.rb b/spec/requests/api/v1/profile/mfa_controller_spec.rb
new file mode 100644
index 000000000..97a2e206f
--- /dev/null
+++ b/spec/requests/api/v1/profile/mfa_controller_spec.rb
@@ -0,0 +1,274 @@
+require 'rails_helper'
+
+RSpec.describe 'MFA API', type: :request do
+ before do
+ skip('Skipping since MFA is not configured in this environment') unless Chatwoot.encryption_configured?
+ allow(Chatwoot).to receive(:mfa_enabled?).and_return(true)
+ end
+
+ let(:account) { create(:account) }
+ let(:user) { create(:user, account: account, password: 'Test@123456') }
+
+ describe 'GET /api/v1/profile/mfa' do
+ context 'when 2FA is disabled' do
+ it 'returns MFA disabled status' do
+ get '/api/v1/profile/mfa',
+ headers: user.create_new_auth_token,
+ as: :json
+
+ expect(response).to have_http_status(:success)
+ json_response = response.parsed_body
+ expect(json_response['enabled']).to be_falsey
+ expect(json_response['backup_codes_generated']).to be_falsey
+ end
+ end
+
+ context 'when 2FA is enabled' do
+ before do
+ user.enable_two_factor!
+ user.update!(otp_required_for_login: true)
+ end
+
+ it 'returns MFA enabled status' do
+ get '/api/v1/profile/mfa',
+ headers: user.create_new_auth_token,
+ as: :json
+
+ expect(response).to have_http_status(:success)
+ json_response = response.parsed_body
+ expect(json_response['enabled']).to be_truthy
+ end
+
+ context 'with backup codes generated' do
+ before do
+ user.generate_backup_codes!
+ end
+
+ it 'indicates backup codes are generated' do
+ get '/api/v1/profile/mfa',
+ headers: user.create_new_auth_token,
+ as: :json
+
+ expect(response).to have_http_status(:success)
+ json_response = response.parsed_body
+ expect(json_response['backup_codes_generated']).to be_truthy
+ end
+ end
+ end
+ end
+
+ describe 'POST /api/v1/profile/mfa' do
+ context 'when 2FA is not enabled' do
+ it 'enables 2FA and returns QR code URL' do
+ post '/api/v1/profile/mfa',
+ headers: user.create_new_auth_token,
+ as: :json
+
+ expect(response).to have_http_status(:success)
+ json_response = response.parsed_body
+ expect(json_response['provisioning_url']).not_to be_nil
+ expect(json_response['provisioning_url']).to include('otpauth://totp')
+ expect(json_response['secret']).not_to be_nil
+
+ user.reload
+ expect(user.otp_secret).not_to be_nil
+ end
+ end
+
+ context 'when 2FA is already enabled' do
+ before do
+ user.enable_two_factor!
+ user.update!(otp_required_for_login: true)
+ end
+
+ it 'returns error message' do
+ post '/api/v1/profile/mfa',
+ headers: user.create_new_auth_token,
+ as: :json
+
+ expect(response).to have_http_status(:unprocessable_entity)
+ json_response = response.parsed_body
+ expect(json_response['error']).to eq(I18n.t('errors.mfa.already_enabled'))
+ end
+ end
+ end
+
+ describe 'POST /api/v1/profile/mfa/verify' do
+ before do
+ user.enable_two_factor!
+ end
+
+ context 'with valid OTP code' do
+ it 'verifies and confirms 2FA setup with backup codes' do
+ otp_code = user.current_otp
+
+ post '/api/v1/profile/mfa/verify',
+ params: { otp_code: otp_code },
+ headers: user.create_new_auth_token,
+ as: :json
+
+ expect(response).to have_http_status(:success)
+ json_response = response.parsed_body
+ expect(json_response['enabled']).to be_truthy
+ expect(json_response['backup_codes']).to be_an(Array)
+ expect(json_response['backup_codes'].length).to eq(10)
+
+ user.reload
+ expect(user.otp_required_for_login).to be_truthy
+ expect(user.otp_backup_codes).not_to be_nil
+ end
+ end
+
+ context 'with invalid OTP code' do
+ it 'returns error message' do
+ post '/api/v1/profile/mfa/verify',
+ params: { otp_code: '000000' },
+ headers: user.create_new_auth_token,
+ as: :json
+
+ expect(response).to have_http_status(:unprocessable_entity)
+ json_response = response.parsed_body
+ expect(json_response['error']).to eq(I18n.t('errors.mfa.invalid_code'))
+ end
+ end
+
+ context 'when 2FA is already verified' do
+ before do
+ user.update!(otp_required_for_login: true)
+ end
+
+ it 'returns already enabled error' do
+ post '/api/v1/profile/mfa/verify',
+ params: { otp_code: user.current_otp },
+ headers: user.create_new_auth_token,
+ as: :json
+
+ expect(response).to have_http_status(:unprocessable_entity)
+ json_response = response.parsed_body
+ expect(json_response['error']).to eq(I18n.t('errors.mfa.already_enabled'))
+ end
+ end
+ end
+
+ describe 'DELETE /api/v1/profile/mfa' do
+ context 'when 2FA is enabled' do
+ before do
+ user.enable_two_factor!
+ user.update!(otp_required_for_login: true)
+ user.generate_backup_codes!
+ end
+
+ context 'with valid password and OTP' do
+ it 'disables 2FA successfully' do
+ otp_code = user.current_otp
+
+ delete '/api/v1/profile/mfa',
+ params: { password: 'Test@123456', otp_code: otp_code },
+ headers: user.create_new_auth_token,
+ as: :json
+
+ expect(response).to have_http_status(:success)
+ json_response = response.parsed_body
+ expect(json_response['enabled']).to be_falsey
+
+ user.reload
+ expect(user.otp_required_for_login).to be_falsey
+ expect(user.otp_secret).to be_nil
+ expect(user.otp_backup_codes).to be_blank
+ end
+ end
+
+ context 'with invalid password' do
+ it 'returns error message' do
+ otp_code = user.current_otp
+
+ delete '/api/v1/profile/mfa',
+ params: { password: 'wrong_password', otp_code: otp_code },
+ headers: user.create_new_auth_token,
+ as: :json
+
+ expect(response).to have_http_status(:unprocessable_entity)
+ json_response = response.parsed_body
+ expect(json_response['error']).to include('Invalid')
+ end
+ end
+
+ context 'with invalid OTP' do
+ it 'returns error message' do
+ delete '/api/v1/profile/mfa',
+ params: { password: 'Test@123456', otp_code: '000000' },
+ headers: user.create_new_auth_token,
+ as: :json
+
+ expect(response).to have_http_status(:unprocessable_entity)
+ json_response = response.parsed_body
+ expect(json_response['error']).to include('Invalid')
+ end
+ end
+ end
+
+ context 'when 2FA is not enabled' do
+ it 'returns not enabled error' do
+ delete '/api/v1/profile/mfa',
+ params: { password: 'Test@123456', otp_code: '123456' },
+ headers: user.create_new_auth_token,
+ as: :json
+
+ expect(response).to have_http_status(:unprocessable_entity)
+ json_response = response.parsed_body
+ expect(json_response['error']).to eq(I18n.t('errors.mfa.not_enabled'))
+ end
+ end
+ end
+
+ describe 'POST /api/v1/profile/mfa/backup_codes' do
+ context 'when 2FA is enabled' do
+ before do
+ user.enable_two_factor!
+ user.update!(otp_required_for_login: true)
+ end
+
+ context 'with valid OTP' do
+ it 'generates new backup codes' do
+ otp_code = user.current_otp
+
+ post '/api/v1/profile/mfa/backup_codes',
+ params: { otp_code: otp_code },
+ headers: user.create_new_auth_token,
+ as: :json
+
+ expect(response).to have_http_status(:success)
+ json_response = response.parsed_body
+ expect(json_response['backup_codes']).to be_an(Array)
+ expect(json_response['backup_codes'].length).to eq(10)
+ end
+ end
+
+ context 'with invalid OTP' do
+ it 'returns error message' do
+ post '/api/v1/profile/mfa/backup_codes',
+ params: { otp_code: '000000' },
+ headers: user.create_new_auth_token,
+ as: :json
+
+ expect(response).to have_http_status(:unprocessable_entity)
+ json_response = response.parsed_body
+ expect(json_response['error']).to eq(I18n.t('errors.mfa.invalid_code'))
+ end
+ end
+ end
+
+ context 'when 2FA is not enabled' do
+ it 'returns not enabled error' do
+ post '/api/v1/profile/mfa/backup_codes',
+ params: { otp_code: '123456' },
+ headers: user.create_new_auth_token,
+ as: :json
+
+ expect(response).to have_http_status(:unprocessable_entity)
+ json_response = response.parsed_body
+ expect(json_response['error']).to eq(I18n.t('errors.mfa.not_enabled'))
+ end
+ end
+ end
+end
diff --git a/spec/services/automation_rules/action_service_spec.rb b/spec/services/automation_rules/action_service_spec.rb
index e63fd7545..b4eaa5dd0 100644
--- a/spec/services/automation_rules/action_service_spec.rb
+++ b/spec/services/automation_rules/action_service_spec.rb
@@ -118,6 +118,45 @@ RSpec.describe AutomationRules::ActionService do
end
end
+ describe '#perform with add_label action' do
+ before do
+ rule.actions << { action_name: 'add_label', action_params: %w[bug feature] }
+ rule.save
+ end
+
+ it 'will add labels to conversation' do
+ described_class.new(rule, account, conversation).perform
+ expect(conversation.reload.label_list).to include('bug', 'feature')
+ end
+
+ it 'will not duplicate existing labels' do
+ conversation.add_labels(['bug'])
+ described_class.new(rule, account, conversation).perform
+ expect(conversation.reload.label_list.count('bug')).to eq(1)
+ expect(conversation.reload.label_list).to include('feature')
+ end
+ end
+
+ describe '#perform with remove_label action' do
+ before do
+ conversation.add_labels(%w[bug feature support])
+ rule.actions << { action_name: 'remove_label', action_params: %w[bug feature] }
+ rule.save
+ end
+
+ it 'will remove specified labels from conversation' do
+ described_class.new(rule, account, conversation).perform
+ expect(conversation.reload.label_list).not_to include('bug', 'feature')
+ expect(conversation.reload.label_list).to include('support')
+ end
+
+ it 'will not fail if labels do not exist on conversation' do
+ conversation.update_labels(['support']) # Remove bug and feature first
+ expect { described_class.new(rule, account, conversation).perform }.not_to raise_error
+ expect(conversation.reload.label_list).to include('support')
+ end
+ end
+
describe '#perform with add_private_note action' do
let(:message_builder) { double }
diff --git a/spec/services/automation_rules/conditions_filter_service_spec.rb b/spec/services/automation_rules/conditions_filter_service_spec.rb
index 7082d31b1..5efd26341 100644
--- a/spec/services/automation_rules/conditions_filter_service_spec.rb
+++ b/spec/services/automation_rules/conditions_filter_service_spec.rb
@@ -134,5 +134,86 @@ RSpec.describe AutomationRules::ConditionsFilterService do
end
end
end
+
+ context 'when conditions based on labels' do
+ before do
+ conversation.add_labels(['bug'])
+ end
+
+ context 'when filter_operator is equal_to' do
+ before do
+ rule.conditions = [
+ { 'values': ['bug'], 'attribute_key': 'labels', 'query_operator': nil, 'filter_operator': 'equal_to' }
+ ]
+ rule.save
+ end
+
+ it 'will return true when conversation has the label' do
+ expect(described_class.new(rule, conversation, { changed_attributes: {} }).perform).to be(true)
+ end
+
+ it 'will return false when conversation does not have the label' do
+ rule.conditions = [
+ { 'values': ['feature'], 'attribute_key': 'labels', 'query_operator': nil, 'filter_operator': 'equal_to' }
+ ]
+ rule.save
+ expect(described_class.new(rule, conversation, { changed_attributes: {} }).perform).to be(false)
+ end
+ end
+
+ context 'when filter_operator is not_equal_to' do
+ before do
+ rule.conditions = [
+ { 'values': ['feature'], 'attribute_key': 'labels', 'query_operator': nil, 'filter_operator': 'not_equal_to' }
+ ]
+ rule.save
+ end
+
+ it 'will return true when conversation does not have the label' do
+ expect(described_class.new(rule, conversation, { changed_attributes: {} }).perform).to be(true)
+ end
+
+ it 'will return false when conversation has the label' do
+ conversation.add_labels(['feature'])
+ expect(described_class.new(rule, conversation, { changed_attributes: {} }).perform).to be(false)
+ end
+ end
+
+ context 'when filter_operator is is_present' do
+ before do
+ rule.conditions = [
+ { 'values': [], 'attribute_key': 'labels', 'query_operator': nil, 'filter_operator': 'is_present' }
+ ]
+ rule.save
+ end
+
+ it 'will return true when conversation has any labels' do
+ expect(described_class.new(rule, conversation, { changed_attributes: {} }).perform).to be(true)
+ end
+
+ it 'will return false when conversation has no labels' do
+ conversation.update_labels([])
+ expect(described_class.new(rule, conversation, { changed_attributes: {} }).perform).to be(false)
+ end
+ end
+
+ context 'when filter_operator is is_not_present' do
+ before do
+ rule.conditions = [
+ { 'values': [], 'attribute_key': 'labels', 'query_operator': nil, 'filter_operator': 'is_not_present' }
+ ]
+ rule.save
+ end
+
+ it 'will return false when conversation has any labels' do
+ expect(described_class.new(rule, conversation, { changed_attributes: {} }).perform).to be(false)
+ end
+
+ it 'will return true when conversation has no labels' do
+ conversation.update_labels([])
+ expect(described_class.new(rule, conversation, { changed_attributes: {} }).perform).to be(true)
+ end
+ end
+ end
end
end
diff --git a/spec/services/base_token_service_spec.rb b/spec/services/base_token_service_spec.rb
new file mode 100644
index 000000000..1b34aedf2
--- /dev/null
+++ b/spec/services/base_token_service_spec.rb
@@ -0,0 +1,42 @@
+require 'rails_helper'
+
+describe BaseTokenService do
+ let(:payload) { { user_id: 1, exp: 5.minutes.from_now.to_i } }
+ let(:token_service) { described_class.new(payload: payload) }
+
+ describe '#generate_token' do
+ it 'generates a JWT token with the provided payload' do
+ token = token_service.generate_token
+ expect(token).to be_present
+ expect(token).to be_a(String)
+ end
+
+ it 'encodes the payload correctly' do
+ token = token_service.generate_token
+ decoded = JWT.decode(token, Rails.application.secret_key_base, true, algorithm: 'HS256').first
+ expect(decoded['user_id']).to eq(1)
+ end
+ end
+
+ describe '#decode_token' do
+ let(:token) { token_service.generate_token }
+ let(:decoder_service) { described_class.new(token: token) }
+
+ it 'decodes a valid JWT token' do
+ decoded = decoder_service.decode_token
+ expect(decoded[:user_id]).to eq(1)
+ end
+
+ it 'returns empty hash for invalid token' do
+ invalid_service = described_class.new(token: 'invalid_token')
+ expect(invalid_service.decode_token).to eq({})
+ end
+
+ it 'returns empty hash for expired token' do
+ expired_payload = { user_id: 1, exp: 1.minute.ago.to_i }
+ expired_token = JWT.encode(expired_payload, Rails.application.secret_key_base, 'HS256')
+ expired_service = described_class.new(token: expired_token)
+ expect(expired_service.decode_token).to eq({})
+ end
+ end
+end
diff --git a/spec/services/contacts/filter_service_spec.rb b/spec/services/contacts/filter_service_spec.rb
index 22882ae81..77d3a49f3 100644
--- a/spec/services/contacts/filter_service_spec.rb
+++ b/spec/services/contacts/filter_service_spec.rb
@@ -9,7 +9,7 @@ describe Contacts::FilterService do
let!(:inbox) { create(:inbox, account: account, enable_auto_assignment: false) }
let!(:en_contact) { create(:contact, account: account, additional_attributes: { 'country_code': 'uk' }) }
let!(:el_contact) { create(:contact, account: account, additional_attributes: { 'country_code': 'gr' }) }
- let!(:cs_contact) { create(:contact, account: account, additional_attributes: { 'country_code': 'cz' }) }
+ let!(:cs_contact) { create(:contact, :with_phone_number, account: account, additional_attributes: { 'country_code': 'cz' }) }
before do
create(:inbox_member, user: first_user, inbox: inbox)
@@ -65,6 +65,42 @@ describe Contacts::FilterService do
end
end
+ context 'with standard attributes - phone' do
+ it 'filter contacts by name' do
+ params[:payload] = [
+ {
+ attribute_key: 'phone_number',
+ filter_operator: 'equal_to',
+ values: [cs_contact.phone_number],
+ query_operator: nil
+ }.with_indifferent_access
+ ]
+
+ result = filter_service.new(account, first_user, params).perform
+ expect(result[:count]).to be 1
+ expect(result[:contacts].length).to be 1
+ expect(result[:contacts].first.name).to eq(cs_contact.name)
+ end
+ end
+
+ context 'with standard attributes - phone (without +)' do
+ it 'filter contacts by name' do
+ params[:payload] = [
+ {
+ attribute_key: 'phone_number',
+ filter_operator: 'equal_to',
+ values: [cs_contact.phone_number[1..]],
+ query_operator: nil
+ }.with_indifferent_access
+ ]
+
+ result = filter_service.new(account, first_user, params).perform
+ expect(result[:count]).to be 1
+ expect(result[:contacts].length).to be 1
+ expect(result[:contacts].first.name).to eq(cs_contact.name)
+ end
+ end
+
context 'with standard attributes - blocked' do
it 'filter contacts by blocked' do
blocked_contact = create(:contact, account: account, blocked: true)
diff --git a/spec/services/line/incoming_message_service_spec.rb b/spec/services/line/incoming_message_service_spec.rb
index 3bcdf8b04..a7805ce9b 100644
--- a/spec/services/line/incoming_message_service_spec.rb
+++ b/spec/services/line/incoming_message_service_spec.rb
@@ -35,6 +35,62 @@ describe Line::IncomingMessageService do
}.with_indifferent_access
end
+ let(:follow_params) do
+ {
+ 'destination': '2342234234',
+ 'events': [
+ {
+ 'replyToken': '8cf9239d56244f4197887e939187e19e',
+ 'type': 'follow',
+ 'mode': 'active',
+ 'timestamp': 1_462_629_479_859,
+ 'source': {
+ 'type': 'user',
+ 'userId': 'U4af4980629'
+ }
+ }
+ ]
+ }.with_indifferent_access
+ end
+
+ let(:multi_user_params) do
+ {
+ 'destination': '2342234234',
+ 'events': [
+ {
+ 'replyToken': '0f3779fba3b349968c5d07db31eab56f1',
+ 'type': 'message',
+ 'mode': 'active',
+ 'timestamp': 1_462_629_479_859,
+ 'source': {
+ 'type': 'user',
+ 'userId': 'U4af4980629'
+ },
+ 'message': {
+ 'id': '3257081',
+ 'type': 'text',
+ 'text': 'Hello, world 1'
+ }
+ },
+ {
+ 'replyToken': '0f3779fba3b349968c5d07db31eab56f2',
+ 'type': 'message',
+ 'mode': 'active',
+ 'timestamp': 1_462_629_479_859,
+ 'source': {
+ 'type': 'user',
+ 'userId': 'U4af49806292'
+ },
+ 'message': {
+ 'id': '3257082',
+ 'type': 'text',
+ 'text': 'Hello, world 2'
+ }
+ }
+ ]
+ }.with_indifferent_access
+ end
+
let(:image_params) do
{
'destination': '2342234234',
@@ -105,6 +161,40 @@ describe Line::IncomingMessageService do
}.with_indifferent_access
end
+ let(:file_params) do
+ {
+ 'destination': '2342234234',
+ 'events': [
+ {
+ 'replyToken': '0f3779fba3b349968c5d07db31eab56f',
+ 'type': 'message',
+ 'mode': 'active',
+ 'timestamp': 1_462_629_479_859,
+ 'source': {
+ 'type': 'user',
+ 'userId': 'U4af4980629'
+ },
+ 'message': {
+ 'type': 'file',
+ 'id': '354718',
+ 'fileName': 'contacts.csv',
+ 'fileSize': 2978
+ }
+ },
+ {
+ 'replyToken': '8cf9239d56244f4197887e939187e19e',
+ 'type': 'follow',
+ 'mode': 'active',
+ 'timestamp': 1_462_629_479_859,
+ 'source': {
+ 'type': 'user',
+ 'userId': 'U4af4980629'
+ }
+ }
+ ]
+ }.with_indifferent_access
+ end
+
let(:sticker_params) do
{
'destination': '2342234234',
@@ -141,8 +231,8 @@ describe Line::IncomingMessageService do
end
describe '#perform' do
- context 'when valid text message params' do
- it 'creates appropriate conversations, message and contacts' do
+ context 'when non-text message params' do
+ it 'does not create conversations, messages and contacts' do
line_bot = double
line_user_profile = double
allow(Line::Bot::Client).to receive(:new).and_return(line_bot)
@@ -154,12 +244,56 @@ describe Line::IncomingMessageService do
'pictureUrl': 'https://test.com'
}.to_json
)
+ described_class.new(inbox: line_channel.inbox, params: follow_params).perform
+ expect(line_channel.inbox.conversations.size).to eq(0)
+ expect(Contact.all.size).to eq(0)
+ expect(line_channel.inbox.messages.size).to eq(0)
+ end
+ end
+
+ context 'when valid text message params' do
+ let(:line_bot) { double }
+ let(:line_user_profile) { double }
+
+ before do
+ allow(Line::Bot::Client).to receive(:new).and_return(line_bot)
+ allow(line_bot).to receive(:get_profile).with('U4af4980629').and_return(line_user_profile)
+ allow(line_user_profile).to receive(:body).and_return(
+ {
+ 'displayName': 'LINE Test',
+ 'userId': 'U4af4980629',
+ 'pictureUrl': 'https://test.com'
+ }.to_json
+ )
+ end
+
+ it 'creates appropriate conversations, message and contacts' do
described_class.new(inbox: line_channel.inbox, params: params).perform
expect(line_channel.inbox.conversations).not_to eq(0)
expect(Contact.all.first.name).to eq('LINE Test')
expect(Contact.all.first.additional_attributes['social_line_user_id']).to eq('U4af4980629')
expect(line_channel.inbox.messages.first.content).to eq('Hello, world')
end
+
+ it 'creates appropriate conversations, message and contacts for multi user' do
+ line_user_profile2 = double
+ allow(line_bot).to receive(:get_profile).with('U4af49806292').and_return(line_user_profile2)
+ allow(line_user_profile2).to receive(:body).and_return(
+ {
+ 'displayName': 'LINE Test 2',
+ 'userId': 'U4af49806292',
+ 'pictureUrl': 'https://test.com'
+ }.to_json
+ )
+ described_class.new(inbox: line_channel.inbox, params: multi_user_params).perform
+ expect(line_channel.inbox.conversations.size).to eq(2)
+ expect(Contact.all.first.name).to eq('LINE Test')
+ expect(Contact.all.first.additional_attributes['social_line_user_id']).to eq('U4af4980629')
+ expect(Contact.all.last.name).to eq('LINE Test 2')
+ expect(Contact.all.last.additional_attributes['social_line_user_id']).to eq('U4af49806292')
+ expect(line_channel.inbox.messages.first.content).to eq('Hello, world 1')
+ expect(line_channel.inbox.messages.last.content).to eq('Hello, world 2')
+ end
end
context 'when valid sticker message params' do
@@ -241,5 +375,35 @@ describe Line::IncomingMessageService do
expect(line_channel.inbox.messages.first.attachments.first.file.blob.filename.to_s).to eq('media-354718.mp4')
end
end
+
+ context 'when valid file message params' do
+ it 'creates appropriate conversations, message and contacts' do
+ line_bot = double
+ line_user_profile = double
+ allow(Line::Bot::Client).to receive(:new).and_return(line_bot)
+ allow(line_bot).to receive(:get_profile).and_return(line_user_profile)
+ file = fixture_file_upload(Rails.root.join('spec/assets/contacts.csv'), 'text/csv')
+ allow(line_bot).to receive(:get_message_content).and_return(
+ OpenStruct.new({
+ body: Base64.encode64(file.read),
+ content_type: 'text/csv'
+ })
+ )
+ allow(line_user_profile).to receive(:body).and_return(
+ {
+ 'displayName': 'LINE Test',
+ 'userId': 'U4af4980629',
+ 'pictureUrl': 'https://test.com'
+ }.to_json
+ )
+ described_class.new(inbox: line_channel.inbox, params: file_params).perform
+ expect(line_channel.inbox.conversations).not_to eq(0)
+ expect(Contact.all.first.name).to eq('LINE Test')
+ expect(Contact.all.first.additional_attributes['social_line_user_id']).to eq('U4af4980629')
+ expect(line_channel.inbox.messages.first.content).to be_nil
+ expect(line_channel.inbox.messages.first.attachments.first.file_type).to eq('file')
+ expect(line_channel.inbox.messages.first.attachments.first.file.blob.filename.to_s).to eq('contacts.csv')
+ end
+ end
end
end
diff --git a/spec/services/mfa/authentication_service_spec.rb b/spec/services/mfa/authentication_service_spec.rb
new file mode 100644
index 000000000..c4cc5ef5e
--- /dev/null
+++ b/spec/services/mfa/authentication_service_spec.rb
@@ -0,0 +1,106 @@
+require 'rails_helper'
+
+describe Mfa::AuthenticationService do
+ before do
+ skip('Skipping since MFA is not configured in this environment') unless Chatwoot.encryption_configured?
+ user.enable_two_factor!
+ user.update!(otp_required_for_login: true)
+ end
+
+ let(:user) { create(:user) }
+
+ describe '#authenticate' do
+ context 'with OTP code' do
+ context 'when OTP is valid' do
+ it 'returns true' do
+ valid_otp = user.current_otp
+ service = described_class.new(user: user, otp_code: valid_otp)
+ expect(service.authenticate).to be_truthy
+ end
+ end
+
+ context 'when OTP is invalid' do
+ it 'returns false' do
+ service = described_class.new(user: user, otp_code: '000000')
+ expect(service.authenticate).to be_falsey
+ end
+ end
+
+ context 'when OTP is nil' do
+ it 'returns false' do
+ service = described_class.new(user: user, otp_code: nil)
+ expect(service.authenticate).to be_falsey
+ end
+ end
+ end
+
+ context 'with backup code' do
+ let(:backup_codes) { user.generate_backup_codes! }
+
+ context 'when backup code is valid' do
+ it 'returns true and invalidates the code' do
+ valid_code = backup_codes.first
+ service = described_class.new(user: user, backup_code: valid_code)
+
+ expect(service.authenticate).to be_truthy
+
+ # Code should be invalidated after use
+ user.reload
+ expect(user.otp_backup_codes).to include('XXXXXXXX')
+ end
+ end
+
+ context 'when backup code is invalid' do
+ it 'returns false' do
+ service = described_class.new(user: user, backup_code: 'invalid')
+ expect(service.authenticate).to be_falsey
+ end
+ end
+
+ context 'when backup code has already been used' do
+ it 'returns false' do
+ valid_code = backup_codes.first
+ # Use the code once
+ service = described_class.new(user: user, backup_code: valid_code)
+ service.authenticate
+
+ # Try to use it again
+ service2 = described_class.new(user: user.reload, backup_code: valid_code)
+ expect(service2.authenticate).to be_falsey
+ end
+ end
+ end
+
+ context 'with neither OTP nor backup code' do
+ it 'returns false' do
+ service = described_class.new(user: user)
+ expect(service.authenticate).to be_falsey
+ end
+ end
+
+ context 'when user is nil' do
+ it 'returns false' do
+ service = described_class.new(user: nil, otp_code: '123456')
+ expect(service.authenticate).to be_falsey
+ end
+ end
+
+ context 'when both OTP and backup code are provided' do
+ it 'uses OTP authentication first' do
+ valid_otp = user.current_otp
+ backup_codes = user.generate_backup_codes!
+
+ service = described_class.new(
+ user: user,
+ otp_code: valid_otp,
+ backup_code: backup_codes.first
+ )
+
+ expect(service.authenticate).to be_truthy
+ # Backup code should not be consumed
+ user.reload
+ expect(user.otp_backup_codes).not_to include('XXXXXXXX')
+ end
+ end
+ end
+end
diff --git a/spec/services/mfa/token_service_spec.rb b/spec/services/mfa/token_service_spec.rb
new file mode 100644
index 000000000..7d4fe55b6
--- /dev/null
+++ b/spec/services/mfa/token_service_spec.rb
@@ -0,0 +1,72 @@
+require 'rails_helper'
+
+describe Mfa::TokenService do
+ before do
+ skip('Skipping since MFA is not configured in this environment') unless Chatwoot.encryption_configured?
+ end
+
+ let(:user) { create(:user) }
+ let(:token_service) { described_class.new(user: user) }
+
+ describe '#generate_token' do
+ it 'generates a JWT token with user_id' do
+ token = token_service.generate_token
+ expect(token).to be_present
+ expect(token).to be_a(String)
+ end
+
+ it 'includes user_id in the payload' do
+ token = token_service.generate_token
+ decoded = JWT.decode(token, Rails.application.secret_key_base, true, algorithm: 'HS256').first
+ expect(decoded['user_id']).to eq(user.id)
+ end
+
+ it 'sets expiration to 5 minutes from now' do
+ allow(Time).to receive(:now).and_return(Time.zone.parse('2024-01-01 12:00:00'))
+ token = token_service.generate_token
+ decoded = JWT.decode(token, Rails.application.secret_key_base, true, algorithm: 'HS256').first
+ expected_exp = Time.zone.parse('2024-01-01 12:05:00').to_i
+ expect(decoded['exp']).to eq(expected_exp)
+ end
+ end
+
+ describe '#verify_token' do
+ let(:valid_token) { token_service.generate_token }
+
+ context 'with valid token' do
+ it 'returns the user' do
+ verifier = described_class.new(token: valid_token)
+ verified_user = verifier.verify_token
+ expect(verified_user).to eq(user)
+ end
+ end
+
+ context 'with invalid token' do
+ it 'returns nil for malformed token' do
+ verifier = described_class.new(token: 'invalid_token')
+ expect(verifier.verify_token).to be_nil
+ end
+
+ it 'returns nil for expired token' do
+ expired_payload = { user_id: user.id, exp: 1.minute.ago.to_i }
+ expired_token = JWT.encode(expired_payload, Rails.application.secret_key_base, 'HS256')
+ verifier = described_class.new(token: expired_token)
+ expect(verifier.verify_token).to be_nil
+ end
+
+ it 'returns nil for non-existent user' do
+ payload = { user_id: 999_999, exp: 5.minutes.from_now.to_i }
+ token = JWT.encode(payload, Rails.application.secret_key_base, 'HS256')
+ verifier = described_class.new(token: token)
+ expect(verifier.verify_token).to be_nil
+ end
+ end
+
+ context 'with blank token' do
+ it 'returns nil' do
+ verifier = described_class.new(token: nil)
+ expect(verifier.verify_token).to be_nil
+ end
+ end
+ end
+end
diff --git a/spec/services/whatsapp/incoming_message_whatsapp_cloud_service_spec.rb b/spec/services/whatsapp/incoming_message_whatsapp_cloud_service_spec.rb
index b222354a4..b162250bf 100644
--- a/spec/services/whatsapp/incoming_message_whatsapp_cloud_service_spec.rb
+++ b/spec/services/whatsapp/incoming_message_whatsapp_cloud_service_spec.rb
@@ -29,32 +29,23 @@ describe Whatsapp::IncomingMessageWhatsappCloudService do
context 'when valid attachment message params' do
it 'creates appropriate conversations, message and contacts' do
- stub_request(:get, whatsapp_channel.media_url('b1c68f38-8734-4ad3-b4a1-ef0c10d683')).to_return(
- status: 200,
- body: {
- messaging_product: 'whatsapp',
- url: 'https://chatwoot-assets.local/sample.png',
- mime_type: 'image/jpeg',
- sha256: 'sha256',
- file_size: 'SIZE',
- id: 'b1c68f38-8734-4ad3-b4a1-ef0c10d683'
- }.to_json,
- headers: { 'content-type' => 'application/json' }
- )
- stub_request(:get, 'https://chatwoot-assets.local/sample.png').to_return(
- status: 200,
- body: File.read('spec/assets/sample.png')
- )
-
+ stub_media_url_request
+ stub_sample_png_request
described_class.new(inbox: whatsapp_channel.inbox, params: params).perform
- expect(whatsapp_channel.inbox.conversations.count).not_to eq(0)
- expect(Contact.all.first.name).to eq('Sojan Jose')
- expect(whatsapp_channel.inbox.messages.first.content).to eq('Check out my product!')
- expect(whatsapp_channel.inbox.messages.first.attachments.present?).to be true
+ expect_conversation_created
+ expect_contact_name
+ expect_message_content
+ expect_message_has_attachment
end
it 'increments reauthorization count if fetching attachment fails' do
- stub_request(:get, whatsapp_channel.media_url('b1c68f38-8734-4ad3-b4a1-ef0c10d683')).to_return(
+ stub_request(
+ :get,
+ whatsapp_channel.media_url(
+ 'b1c68f38-8734-4ad3-b4a1-ef0c10d683',
+ whatsapp_channel.provider_config['phone_number_id']
+ )
+ ).to_return(
status: 401
)
@@ -115,4 +106,50 @@ describe Whatsapp::IncomingMessageWhatsappCloudService do
end
end
end
+
+ # Métodos auxiliares para reduzir o tamanho do exemplo
+
+ def stub_media_url_request
+ stub_request(
+ :get,
+ whatsapp_channel.media_url(
+ 'b1c68f38-8734-4ad3-b4a1-ef0c10d683',
+ whatsapp_channel.provider_config['phone_number_id']
+ )
+ ).to_return(
+ status: 200,
+ body: {
+ messaging_product: 'whatsapp',
+ url: 'https://chatwoot-assets.local/sample.png',
+ mime_type: 'image/jpeg',
+ sha256: 'sha256',
+ file_size: 'SIZE',
+ id: 'b1c68f38-8734-4ad3-b4a1-ef0c10d683'
+ }.to_json,
+ headers: { 'content-type' => 'application/json' }
+ )
+ end
+
+ def stub_sample_png_request
+ stub_request(:get, 'https://chatwoot-assets.local/sample.png').to_return(
+ status: 200,
+ body: File.read('spec/assets/sample.png')
+ )
+ end
+
+ def expect_conversation_created
+ expect(whatsapp_channel.inbox.conversations.count).not_to eq(0)
+ end
+
+ def expect_contact_name
+ expect(Contact.all.first.name).to eq('Sojan Jose')
+ end
+
+ def expect_message_content
+ expect(whatsapp_channel.inbox.messages.first.content).to eq('Check out my product!')
+ end
+
+ def expect_message_has_attachment
+ expect(whatsapp_channel.inbox.messages.first.attachments.present?).to be true
+ end
end
diff --git a/spec/services/widget/token_service_expiry_spec.rb b/spec/services/widget/token_service_expiry_spec.rb
new file mode 100644
index 000000000..051a757a5
--- /dev/null
+++ b/spec/services/widget/token_service_expiry_spec.rb
@@ -0,0 +1,42 @@
+require 'rails_helper'
+
+RSpec.describe Widget::TokenService, type: :service do
+ describe 'token expiry configuration' do
+ let(:service) { described_class.new(payload: {}) }
+
+ before do
+ # Clear any existing configs to ensure test isolation
+ InstallationConfig.where(name: 'WIDGET_TOKEN_EXPIRY').destroy_all
+ end
+
+ context 'with valid configuration' do
+ before do
+ create(:installation_config, name: 'WIDGET_TOKEN_EXPIRY', value: '30')
+ end
+
+ it 'uses the configured value for token expiry' do
+ freeze_time do
+ token = service.generate_token
+ decoded = JWT.decode(token, Rails.application.secret_key_base, true, algorithm: 'HS256').first
+ expect(decoded['iat']).to eq(Time.now.to_i)
+ expect(decoded['exp']).to eq(30.days.from_now.to_i)
+ end
+ end
+ end
+
+ context 'with empty configuration' do
+ before do
+ create(:installation_config, name: 'WIDGET_TOKEN_EXPIRY', value: '')
+ end
+
+ it 'uses the default expiry' do
+ freeze_time do
+ token = service.generate_token
+ decoded = JWT.decode(token, Rails.application.secret_key_base, true, algorithm: 'HS256').first
+ expect(decoded['iat']).to eq(Time.now.to_i)
+ expect(decoded['exp']).to eq(180.days.from_now.to_i)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/services/widget/token_service_spec.rb b/spec/services/widget/token_service_spec.rb
new file mode 100644
index 000000000..724728b51
--- /dev/null
+++ b/spec/services/widget/token_service_spec.rb
@@ -0,0 +1,43 @@
+require 'rails_helper'
+
+describe Widget::TokenService do
+ let(:payload) { { source_id: 'contact_123', inbox_id: 1 } }
+ let(:token_service) { described_class.new(payload: payload) }
+
+ describe 'inheritance' do
+ it 'inherits from BaseTokenService' do
+ expect(described_class.superclass).to eq(BaseTokenService)
+ end
+ end
+
+ describe '#generate_token' do
+ it 'generates a JWT token with the provided payload' do
+ token = token_service.generate_token
+ expect(token).to be_present
+ expect(token).to be_a(String)
+ end
+
+ it 'encodes the payload correctly' do
+ token = token_service.generate_token
+ decoded = JWT.decode(token, Rails.application.secret_key_base, true, algorithm: 'HS256').first
+ expect(decoded['source_id']).to eq('contact_123')
+ expect(decoded['inbox_id']).to eq(1)
+ end
+ end
+
+ describe '#decode_token' do
+ let(:token) { token_service.generate_token }
+ let(:decoder_service) { described_class.new(token: token) }
+
+ it 'decodes a valid JWT token' do
+ decoded = decoder_service.decode_token
+ expect(decoded[:source_id]).to eq('contact_123')
+ expect(decoded[:inbox_id]).to eq(1)
+ end
+
+ it 'returns empty hash for invalid token' do
+ invalid_service = described_class.new(token: 'invalid_token')
+ expect(invalid_service.decode_token).to eq({})
+ end
+ end
+end
diff --git a/swagger/paths/application/conversation/toggle_status.yml b/swagger/paths/application/conversation/toggle_status.yml
index 9c6f9ee6a..80cf47c65 100644
--- a/swagger/paths/application/conversation/toggle_status.yml
+++ b/swagger/paths/application/conversation/toggle_status.yml
@@ -2,7 +2,13 @@ tags:
- Conversations
operationId: toggle-status-of-a-conversation
summary: Toggle Status
-description: Toggles the status of the conversation between open and resolved
+description: |-
+ Toggle the status of a conversation. Pass `status` to explicitly set the
+ conversation state. Use `snoozed` along with `snoozed_until` to snooze a
+ conversation until a specific time. If `snoozed_until` is omitted, the
+ conversation is snoozed until the next reply from the contact. Regardless
+ of the value provided, snoozed conversations always reopen on the next
+ reply from the contact.
security:
- userApiKey: []
- agentBotApiKey: []
@@ -17,16 +23,36 @@ requestBody:
properties:
status:
type: string
- enum: ['open', 'resolved', 'pending']
+ enum: ['open', 'resolved', 'pending', 'snoozed']
description: The status of the conversation
example: open
+ snoozed_until:
+ type: number
+ description: When status is `snoozed`, schedule the reopen time as a Unix timestamp in seconds.
+ If not provided, the conversation is snoozed until the next
+ customer reply. The conversation always reopens when the
+ customer replies.
+ example: 1757506877
responses:
'200':
description: Success
content:
application/json:
schema:
- $ref: '#/components/schemas/conversation_status_toggle'
+ type: object
+ properties:
+ meta:
+ type: object
+ payload:
+ type: object
+ properties:
+ success:
+ type: boolean
+ current_status:
+ type: string
+ enum: ['open', 'resolved', 'pending', 'snoozed']
+ conversation_id:
+ type: number
'404':
description: Conversation not found
content:
diff --git a/swagger/swagger.json b/swagger/swagger.json
index aa7455e7f..39022bf84 100644
--- a/swagger/swagger.json
+++ b/swagger/swagger.json
@@ -4673,7 +4673,7 @@
],
"operationId": "toggle-status-of-a-conversation",
"summary": "Toggle Status",
- "description": "Toggles the status of the conversation between open and resolved",
+ "description": "Toggle the status of a conversation. Pass `status` to explicitly set the\nconversation state. Use `snoozed` along with `snoozed_until` to snooze a\nconversation until a specific time. If `snoozed_until` is omitted, the\nconversation is snoozed until the next reply from the contact. Regardless\nof the value provided, snoozed conversations always reopen on the next\nreply from the contact.",
"security": [
{
"userApiKey": []
@@ -4697,10 +4697,16 @@
"enum": [
"open",
"resolved",
- "pending"
+ "pending",
+ "snoozed"
],
"description": "The status of the conversation",
"example": "open"
+ },
+ "snoozed_until": {
+ "type": "number",
+ "description": "When status is `snoozed`, schedule the reopen time as a Unix timestamp in seconds. If not provided, the conversation is snoozed until the next customer reply. The conversation always reopens when the customer replies.",
+ "example": 1757506877
}
}
}
@@ -4713,7 +4719,32 @@
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/conversation_status_toggle"
+ "type": "object",
+ "properties": {
+ "meta": {
+ "type": "object"
+ },
+ "payload": {
+ "type": "object",
+ "properties": {
+ "success": {
+ "type": "boolean"
+ },
+ "current_status": {
+ "type": "string",
+ "enum": [
+ "open",
+ "resolved",
+ "pending",
+ "snoozed"
+ ]
+ },
+ "conversation_id": {
+ "type": "number"
+ }
+ }
+ }
+ }
}
}
}
diff --git a/swagger/tag_groups/application_swagger.json b/swagger/tag_groups/application_swagger.json
index a36443f81..b26b913bc 100644
--- a/swagger/tag_groups/application_swagger.json
+++ b/swagger/tag_groups/application_swagger.json
@@ -3070,7 +3070,7 @@
],
"operationId": "toggle-status-of-a-conversation",
"summary": "Toggle Status",
- "description": "Toggles the status of the conversation between open and resolved",
+ "description": "Toggle the status of a conversation. Pass `status` to explicitly set the\nconversation state. Use `snoozed` along with `snoozed_until` to snooze a\nconversation until a specific time. If `snoozed_until` is omitted, the\nconversation is snoozed until the next reply from the contact. Regardless\nof the value provided, snoozed conversations always reopen on the next\nreply from the contact.",
"security": [
{
"userApiKey": []
@@ -3094,10 +3094,16 @@
"enum": [
"open",
"resolved",
- "pending"
+ "pending",
+ "snoozed"
],
"description": "The status of the conversation",
"example": "open"
+ },
+ "snoozed_until": {
+ "type": "number",
+ "description": "When status is `snoozed`, schedule the reopen time as a Unix timestamp in seconds. If not provided, the conversation is snoozed until the next customer reply. The conversation always reopens when the customer replies.",
+ "example": 1757506877
}
}
}
@@ -3110,7 +3116,32 @@
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/conversation_status_toggle"
+ "type": "object",
+ "properties": {
+ "meta": {
+ "type": "object"
+ },
+ "payload": {
+ "type": "object",
+ "properties": {
+ "success": {
+ "type": "boolean"
+ },
+ "current_status": {
+ "type": "string",
+ "enum": [
+ "open",
+ "resolved",
+ "pending",
+ "snoozed"
+ ]
+ },
+ "conversation_id": {
+ "type": "number"
+ }
+ }
+ }
+ }
}
}
}