* feat(scheduled-messages): add recurring scheduled messages Implements the recurring scheduled messages feature allowing agents to configure recurrence rules when scheduling messages, with automatic creation of subsequent scheduled messages after each send. Backend: - RecurringScheduledMessage model with JSONB recurrence_rule validation - RecurrenceCalculatorService for next occurrence date calculation - RecurrenceDescriptionService for human-readable rule descriptions - CreateNextOccurrenceService for auto-creating child ScheduledMessages - RecurringScheduledMessagesController with CRUD operations - RecurringScheduledMessagePolicy for authorization - Modified SendScheduledMessageJob to handle recurrence after send - Updated due_for_sending scope to exclude resolved conversations - ActionCable events for real-time updates - Activity message i18n (en + pt-BR) Frontend: - RecurrenceDropdown.vue with contextual shortcut options - RecurrenceCustomModal.vue for custom recurrence configuration - RecurringScheduledMessageItem.vue for sidebar display - Integration into ScheduledMessageModal.vue - Updated ScheduledMessages.vue with recurrence section and filtering - Vuex store module for recurring scheduled messages - API client for CRUD operations - WebSocket handlers in actionCable.js - recurrenceHelpers.js utility functions - i18n keys for en and pt-BR Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(recurring-messages): fix creation and edit visibility - Fix API payload key mismatch (snake_case vs camelCase) in modal submit - Add status: 'active' to recurring creation payload - Fix strong params to permit recurrence_rule array fields (week_days) - Cast string values from strong params to integers for JSONB validation - Show RecurrenceDropdown when editing (remove isEditing gate) - Populate recurrenceRule from scheduled message's recurring parent - Include recurring_scheduled_message_id and recurrence_rule in scheduled message jbuilder response Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(recurring-messages): fix dropdown toggle and locale tag errors - Use DropdownItem :click prop instead of @click to use the injected closeMenu from DropdownContainer context (default slot doesn't expose toggle) - Normalize locale from pt_BR to pt-BR for Intl.DateTimeFormat compatibility in RecurringScheduledMessageItem Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat(recurring-messages): add separator line and expandable history - Add border separator between recurring messages section and pending/draft messages, matching the history section separator - Replace static 'N enviadas' counter with clickable toggle that expands to show individual sent/failed child messages with status badges and formatted timestamps Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat(recurring-messages): add click-to-navigate on sent children Make sent child messages in recurring message history clickable. Clicking navigates to the actual message in the conversation using the messageId query param, same pattern as ScheduledMessageItem. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat(recurring-messages): allow editing recurring messages - Add edit button to RecurringScheduledMessageItem (active only) - Transform recurring message into scheduledMessage-compatible shape with recurring_scheduled_message_id set, so the modal reuses the existing update path - Handle edge case of removing recurrence from a recurring message (cancels series without trying to update a non-existent standalone) - Sent history is preserved by the backend update action Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(recurring-messages): show recurrence field without date selection Remove v-if="scheduledDate" gate so the recurrence dropdown is always visible in the modal. Falls back to today's date for contextual shortcut labels when no date is selected yet. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(recurring-messages): ensure recurrence visible on edit and add pending_scheduled_message to API - Add pending_scheduled_message to recurring_scheduled_message jbuilder so REST API data matches WebSocket push_event_data - Add fallback in openEditRecurringModal to find pending child from scheduled_messages array when pending_scheduled_message is absent - Add same fallback in nextSendLabel computed Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(scheduled-messages): add sections for Drafts and Pending messages in the UI * refactor(scheduled-messages): merge RecurringScheduledMessageItem into ScheduledMessageItem Consolidate the recurring message card into the existing ScheduledMessageItem component instead of maintaining a separate component. The unified component detects recurring messages via recurrence_rule and conditionally shows: - Recurrence description header with repeat icon - Next send time label - Expandable sent/failed children history with click-to-navigate - Stop button (replaces delete) with confirmation modal - Active/completed/cancelled status badges Delete the now-unused RecurringScheduledMessageItem.vue. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(scheduled-messages): use blue badge for active, keep green for sent Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(scheduled-messages): remove draft and pending sections from UI and update recurrence title * fix(recurring-messages): use Teleport for recurrence dropdown Replace DropdownContainer with Teleport-based floating dropdown so options render outside the modal. Fixes: - Dropdown no longer enlarges the modal or causes scrolling - Dropdown closes before Custom modal opens (no overlap) - Auto-detects available space and opens above/below trigger Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(recurring-messages): recalculate next date when recurrence rule changes When editing a recurring message and changing the recurrence rule, the pending occurrence date is now validated against the new rule. If the user-provided date doesn't match (e.g. Thursday removed from weekly days), the system computes the next valid date using RecurrenceCalculatorService instead of blindly using the old date. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(scheduled-messages): resolve deprecated onClose and disabled type warnings - Replace :on-close prop with @close event on woot-modal components - Cast hasTemplate computed to boolean to fix disabled prop type check Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(scheduled-messages): replace remaining deprecated on-close props - ScheduledMessages.vue delete confirm modal - RecurrenceCustomModal.vue modal Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(recurring-messages): address review feedback on PR #240 - Fix v-if/v-else bug hiding message list behind resolved warning - Fix occurrences_sent incrementing on failed sends (skip_increment flag) - Fix compute_next_valid_date using .min instead of .max - Fix Vuex delete action to update state on cancel (not remove) - Use atomic update_counters for occurrences_sent increment - Add safe Date.iso8601 parsing with rescue in should_complete? - Add null: false to occurrences_sent migration column - Fix pt-BR accent: Recorrencia → Recorrências - Use I18n.with_locale(account.locale) for all activity messages - Fix N+1 in jbuilder partials (Ruby filtering + eager loading) - Add interval >= 1 validation to RecurrenceCustomModal isValid - Validate recurrence_rule presence when status is active - Add ISO8601 date format validation for end_date - Add unknown_agent i18n key for fallback Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(recurring-messages): wrap create/update in transactions, clean pending on deactivation - Wrap create and update flows in ActiveRecord transactions - Move attachment purge after save! to prevent data loss on validation failure - Destroy pending children when status transitions to non-active - Fixes critical bug where stopping recurrence could leave armed pending messages Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(recurring-messages): prevent monthly/yearly day-of-month drift Store the original intended day in recurrence_rule JSONB as month_day (for monthly) and year_day/year_month (for yearly). The calculator now uses these stored values instead of @last_date.day, preventing drift after short months cap the day (e.g., Jan 31 → Feb 28 → all subsequent months stuck on 28). Backend: - RecurrenceCalculatorService: use rule[:month_day] for monthly and rule[:year_day]/rule[:year_month] for yearly calculations - Controller: permit and cast the new integer keys Frontend: - recurrenceHelpers: yearly shortcuts include year_day/year_month - RecurrenceCustomModal: emit month_day for monthly day_of_month rules and year_day/year_month for yearly rules Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(recurring-messages): i18n description services, align due_for_sending scope Backend: - RecurrenceDescriptionService: replace hardcoded English with I18n.t() calls; accept locale parameter and use I18n.with_locale - RecurringScheduledMessage model: pass account locale to description service - ScheduledMessage: align due_for_sending? instance method with scope by checking conversation status (open/pending) Frontend: - buildRecurrenceDescription: use t() i18n function instead of manual isPt locale branching - Add DESCRIPTION i18n keys to en/conversation.json and pt_BR/conversation.json - Update RecurrenceDropdown and ScheduledMessageItem callers to pass t i18n: - Add recurring_scheduled_messages.description.* keys to en.yml and pt_BR.yml Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(recurring-messages): keep pending child when deactivating recurrence When editing a recurring message to disable recurrence while setting a future send date, the pending child message is now preserved instead of being destroyed. This allows a 'final send' without creating new recurrences (the send job already guards with recurring&.active?). Backend: - Add update_pending_on_deactivation: updates pending child's scheduled_at or creates a final pending occurrence - Replace destroy_all in update's non-active branch Frontend: - activeRecurringMessages now includes non-active recurring messages that still have a pending child (pending_scheduled_message) - Stop button hidden for already-cancelled recurring messages - inactiveRecurringMessages excludes messages with pending children Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(recurring-messages): prevent removing recurrence from existing recurring message Once a scheduled message has recurrence, the 'Does not repeat' option is hidden from the RecurrenceDropdown when editing. This avoids edge cases where deactivating recurrence leaves the message in an ambiguous display state. - RecurrenceDropdown: add hideNoRepeat prop, filter NO_REPEAT from shortcuts - ScheduledMessageModal: pass hideNoRepeat when isEditingRecurring - Revert update_pending_on_deactivation (no longer reachable from UI) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(recurring-messages): address CodeRabbit review feedback - ScheduledMessage#push_event_data: expose recurring_scheduled_message_id in ActionCable payloads so frontend correctly classifies children - RecurringScheduledMessagePolicy: add agent_bot? check for parity with ScheduledMessagePolicy - RecurrenceCalculatorService: guard against nil/empty week_days - Factory: bind inbox and account to conversation to prevent cross-account flakiness in specs Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(recurring-messages): update schema to enforce non-null constraint on occurrences_sent --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: gabrieljablonski <contact@gabrieljablonski.com>
544 lines
26 KiB
YAML
544 lines
26 KiB
YAML
# Files in the config/locales directory are used for internationalization
|
|
# and are automatically loaded by Rails. If you want to use locales other
|
|
# than English, add the necessary files in this directory.
|
|
#
|
|
# To use the locales, use `I18n.t`:
|
|
#
|
|
# I18n.t 'hello'
|
|
#
|
|
# In views, this is aliased to just `t`:
|
|
#
|
|
# <%= t('hello') %>
|
|
#
|
|
# To use a different locale, set it with `I18n.locale`:
|
|
#
|
|
# I18n.locale = :es
|
|
#
|
|
# This would use the information in config/locales/es.yml.
|
|
#
|
|
# The following keys must be escaped otherwise they will not be retrieved by
|
|
# the default I18n backend:
|
|
#
|
|
# true, false, on, off, yes, no
|
|
#
|
|
# Instead, surround them with single quotes.
|
|
#
|
|
# en:
|
|
# 'true': 'foo'
|
|
#
|
|
# To learn more, please read the Rails Internationalization guide
|
|
# available at https://guides.rubyonrails.org/i18n.html.
|
|
|
|
en:
|
|
hello: 'Hello world'
|
|
inbox:
|
|
reauthorization:
|
|
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: Request for password reset is successful. A email with instructions will be sent to your email if it exists.
|
|
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:
|
|
presence: must not be blank
|
|
webhook:
|
|
invalid: Invalid events
|
|
signup:
|
|
disposable_email: We do not allow disposable emails
|
|
blocked_domain: This domain is not allowed. If you believe this is a mistake, please contact support.
|
|
invalid_email: You have entered an invalid email
|
|
email_already_exists: 'You have already signed up for an account with %{email}'
|
|
invalid_params: 'Invalid, please check the signup paramters and try again'
|
|
failed: Signup failed
|
|
assignment_policy:
|
|
not_found: Assignment policy not found
|
|
attachments:
|
|
invalid: Invalid attachment
|
|
automation:
|
|
scheduled_message:
|
|
delay_out_of_range: Delay must be between 1 minute and 999 days
|
|
content_attachment_or_template_required: Scheduled message must have content, attachment, or template
|
|
scheduled_messages:
|
|
cannot_delete_processed: Cannot delete a scheduled message that has already been sent or failed
|
|
saml:
|
|
feature_not_enabled: SAML feature not enabled for this account
|
|
sso_not_enabled: SAML SSO is not enabled for this installation
|
|
data_import:
|
|
data_type:
|
|
invalid: Invalid data type
|
|
contacts:
|
|
import:
|
|
failed: File is blank
|
|
export:
|
|
success: We will notify you once contacts export file is ready to view.
|
|
email:
|
|
invalid: Invalid email
|
|
phone_number:
|
|
invalid: should be in e164 format
|
|
companies:
|
|
domain:
|
|
invalid: must be a valid domain name
|
|
search:
|
|
query_missing: Specify search string with parameter q
|
|
messages:
|
|
search:
|
|
time_range_limit_exceeded: 'Search is limited to the last %{days} days'
|
|
categories:
|
|
locale:
|
|
unique: should be unique in the category and portal
|
|
dyte:
|
|
invalid_message_type: 'Invalid message type. Action not permitted'
|
|
slack:
|
|
invalid_channel_id: 'Invalid slack channel. Please try again'
|
|
whatsapp:
|
|
token_exchange_failed: 'Failed to exchange code for access token. Please try again.'
|
|
invalid_token_permissions: 'The access token does not have the required permissions for WhatsApp.'
|
|
phone_info_fetch_failed: 'Failed to fetch phone number information. Please try again.'
|
|
phone_number_already_exists: 'Channel already exists for this phone number: %{phone_number}, please contact support if the error persists'
|
|
reauthorization:
|
|
generic: 'Failed to reauthorize WhatsApp. Please try again.'
|
|
not_supported: 'Reauthorization is not supported for this type of WhatsApp channel.'
|
|
inboxes:
|
|
imap:
|
|
socket_error: Please check the network connection, IMAP address and try again.
|
|
no_response_error: Please check the IMAP credentials and try again.
|
|
host_unreachable_error: Host unreachable, Please check the IMAP address, IMAP port and try again.
|
|
connection_timed_out_error: Connection timed out for %{address}:%{port}
|
|
connection_closed_error: Connection closed.
|
|
validations:
|
|
name: should not start or end with symbols, and it should not have < > / \ @ characters.
|
|
channel:
|
|
provider_connection:
|
|
wrong_phone_number: The phone number you are trying to link is not the same as the one you have configured. Please make sure the phone number is correct before scanning the QR code.
|
|
custom_filters:
|
|
number_of_records: Limit reached. The maximum number of allowed custom filters for a user per account is 1000.
|
|
invalid_attribute: Invalid attribute key - [%{key}]. The key should be one of [%{allowed_keys}] or a custom attribute defined in the account.
|
|
invalid_operator: Invalid operator. The allowed operators for %{attribute_name} are [%{allowed_keys}].
|
|
invalid_query_operator: Query operator must be either "AND" or "OR".
|
|
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.
|
|
topup:
|
|
credits_required: Credits amount is required
|
|
invalid_credits: Invalid credits amount
|
|
invalid_option: Invalid topup option
|
|
plan_not_eligible: Top-ups are only available for paid plans. Please upgrade your plan first.
|
|
stripe_customer_not_configured: Stripe customer not configured
|
|
no_payment_method: No payment methods found. Please add a payment method before making a purchase.
|
|
reports:
|
|
date_range_too_long: Date range cannot exceed 6 months
|
|
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
|
|
agent_csv:
|
|
agent_name: Agent name
|
|
conversations_count: Assigned conversations
|
|
avg_first_response_time: Avg first response time
|
|
avg_resolution_time: Avg resolution time
|
|
resolution_count: Resolution Count
|
|
avg_customer_waiting_time: Avg customer waiting time
|
|
inbox_csv:
|
|
inbox_name: Inbox name
|
|
inbox_type: Inbox type
|
|
conversations_count: No. of conversations
|
|
avg_first_response_time: Avg first response time
|
|
avg_resolution_time: Avg resolution time
|
|
label_csv:
|
|
label_title: Label
|
|
conversations_count: No. of conversations
|
|
avg_first_response_time: Avg first response time
|
|
avg_resolution_time: Avg resolution time
|
|
avg_reply_time: Avg reply time
|
|
resolution_count: Resolution Count
|
|
team_csv:
|
|
team_name: Team name
|
|
conversations_count: Conversations count
|
|
avg_first_response_time: Avg first response time
|
|
avg_resolution_time: Avg resolution time
|
|
resolution_count: Resolution Count
|
|
avg_customer_waiting_time: Avg customer waiting time
|
|
conversation_csv:
|
|
conversations_count: Conversations
|
|
incoming_messages_count: Messages received
|
|
outgoing_messages_count: Messages sent
|
|
avg_first_response_time: Avg first response time
|
|
avg_resolution_time: Avg resolution time
|
|
resolution_count: Resolution count
|
|
avg_customer_waiting_time: Avg customer waiting time
|
|
conversation_traffic_csv:
|
|
timezone: Timezone
|
|
sla_csv:
|
|
conversation_id: Conversation ID
|
|
sla_policy_breached: SLA Policy
|
|
assignee: Assignee
|
|
team: Team
|
|
inbox: Inbox
|
|
labels: Labels
|
|
conversation_link: Link to the Conversation
|
|
breached_events: Breached Events
|
|
default_group_by: day
|
|
csat:
|
|
headers:
|
|
contact_name: Contact Name
|
|
contact_email_address: Contact Email Address
|
|
contact_phone_number: Contact Phone Number
|
|
link_to_the_conversation: Link to the conversation
|
|
agent_name: Agent Name
|
|
rating: Rating
|
|
feedback: Feedback Comment
|
|
recorded_at: Recorded date
|
|
review_notes: Review Notes
|
|
notifications:
|
|
notification_title:
|
|
conversation_creation: 'A conversation (#%{display_id}) has been created in %{inbox_name}'
|
|
conversation_assignment: 'A conversation (#%{display_id}) has been assigned to you'
|
|
assigned_conversation_new_message: 'A new message is created in conversation (#%{display_id})'
|
|
conversation_mention: 'You have been mentioned in conversation (#%{display_id})'
|
|
sla_missed_first_response: 'SLA target first response missed for conversation (#%{display_id})'
|
|
sla_missed_next_response: 'SLA target next response missed for conversation (#%{display_id})'
|
|
sla_missed_resolution: 'SLA target resolution missed for conversation (#%{display_id})'
|
|
attachment: 'Attachment'
|
|
no_content: 'No content'
|
|
conversations:
|
|
captain:
|
|
handoff: 'Transferring to another agent for further assistance.'
|
|
messages:
|
|
instagram_story_content: '%{story_sender} mentioned you in the story: '
|
|
instagram_deleted_story_content: This story is no longer available.
|
|
instagram_shared_story_content: 'Shared story'
|
|
instagram_shared_post_content: 'Shared post'
|
|
deleted: This message was deleted
|
|
whatsapp:
|
|
list_button_label: 'Choose an item'
|
|
delivery_status:
|
|
error_code: 'Error code: %{error_code}'
|
|
activity:
|
|
captain:
|
|
resolved: 'Conversation was marked resolved by %{user_name} due to inactivity'
|
|
open: 'Conversation was marked open by %{user_name}'
|
|
agent_bot:
|
|
error_moved_to_open: 'Conversation was marked open by system due to an error with the agent bot.'
|
|
status:
|
|
resolved: 'Conversation was marked resolved by %{user_name}'
|
|
contact_resolved: 'Conversation was resolved by %{contact_name}'
|
|
open: 'Conversation was reopened by %{user_name}'
|
|
pending: 'Conversation was marked as pending by %{user_name}'
|
|
snoozed: 'Conversation was snoozed by %{user_name}'
|
|
auto_resolved_days: 'Conversation was marked resolved by system due to %{count} days of inactivity'
|
|
auto_resolved_hours: 'Conversation was marked resolved by system due to %{count} hours of inactivity'
|
|
auto_resolved_minutes: 'Conversation was marked resolved by system due to %{count} minutes of inactivity'
|
|
system_auto_open: System reopened the conversation due to a new incoming message.
|
|
priority:
|
|
added: '%{user_name} set the priority to %{new_priority}'
|
|
updated: '%{user_name} changed the priority from %{old_priority} to %{new_priority}'
|
|
removed: '%{user_name} removed the priority'
|
|
assignee:
|
|
self_assigned: '%{user_name} self-assigned this conversation'
|
|
assigned: 'Assigned to %{assignee_name} by %{user_name}'
|
|
removed: 'Conversation unassigned by %{user_name}'
|
|
team:
|
|
assigned: 'Assigned to %{team_name} by %{user_name}'
|
|
assigned_with_assignee: 'Assigned to %{assignee_name} via %{team_name} by %{user_name}'
|
|
removed: 'Unassigned from %{team_name} by %{user_name}'
|
|
labels:
|
|
added: '%{user_name} added %{labels}'
|
|
removed: '%{user_name} removed %{labels}'
|
|
sla:
|
|
added: '%{user_name} added SLA policy %{sla_name}'
|
|
removed: '%{user_name} removed SLA policy %{sla_name}'
|
|
linear:
|
|
issue_created: 'Linear issue %{issue_id} was created by %{user_name}'
|
|
issue_linked: 'Linear issue %{issue_id} was linked by %{user_name}'
|
|
issue_unlinked: 'Linear issue %{issue_id} was unlinked by %{user_name}'
|
|
group_participants:
|
|
add:
|
|
single: '%{author_name} added %{contact_name} to the group'
|
|
multiple: '%{author_name} added %{contact_names} and %{last_contact_name} to the group'
|
|
join: '%{contact_name} joined using the group invite link'
|
|
leave: '%{contact_name} left the group'
|
|
remove:
|
|
single: '%{author_name} removed %{contact_name} from the group'
|
|
multiple: '%{author_name} removed %{contact_names} and %{last_contact_name} from the group'
|
|
promote:
|
|
single: '%{author_name} promoted %{contact_name} to admin'
|
|
multiple: '%{author_name} promoted %{contact_names} and %{last_contact_name} to admin'
|
|
demote:
|
|
single: '%{author_name} demoted %{contact_name} to member'
|
|
multiple: '%{author_name} demoted %{contact_names} and %{last_contact_name} to member'
|
|
groups_update:
|
|
subject_changed: '%{author_name} changed the group name to "%{value}"'
|
|
description_changed: '%{author_name} changed the group description'
|
|
description_removed: '%{author_name} removed the group description'
|
|
restrict_enabled: '%{author_name} changed the settings so only admins can edit the group settings'
|
|
restrict_disabled: '%{author_name} changed the settings so all members can edit the group settings'
|
|
announce_enabled: "%{author_name} changed this group's settings to allow only admins to send messages to this group"
|
|
announce_disabled: "%{author_name} changed this group's settings to allow all members to send messages to this group"
|
|
member_add_mode_enabled: "%{author_name} changed this group's setting to allow all members to add others to this group"
|
|
member_add_mode_disabled: "%{author_name} changed this group's setting to allow only admins to add others to this group"
|
|
join_approval_mode_enabled: '%{author_name} turned on admin approval to join this group'
|
|
join_approval_mode_disabled: '%{author_name} turned off admin approval to join this group'
|
|
invite_link_reset: "%{author_name} reset this group's invite link"
|
|
membership_request_created: '%{contact_name} wants to join the group'
|
|
membership_request_revoked: '%{contact_name} no longer wants to join the group'
|
|
icon_changed: '%{author_name} changed the group image'
|
|
csat:
|
|
not_sent_due_to_messaging_window: 'CSAT survey not sent due to outgoing message restrictions'
|
|
auto_resolve:
|
|
not_sent_due_to_messaging_window: 'Auto-resolve message not sent due to outgoing message restrictions'
|
|
muted: '%{user_name} has muted the conversation'
|
|
unmuted: '%{user_name} has unmuted the conversation'
|
|
recurring_message_failed: 'Recurring scheduled message failed to send. Next occurrence scheduled for %{next_date}.'
|
|
recurring_message_completed: 'Recurring scheduled message series completed after %{count} occurrences.'
|
|
recurring_message_cancelled: 'Recurring scheduled message has been stopped by %{agent}.'
|
|
unknown_agent: 'Unknown'
|
|
auto_resolution_message: 'Resolving the conversation as it has been inactive for a while. Please start a new conversation if you need further assistance.'
|
|
templates:
|
|
greeting_message_body: '%{account_name} typically replies in a few hours.'
|
|
ways_to_reach_you_message_body: 'Give the team a way to reach you.'
|
|
email_input_box_message_body: 'Get notified by email'
|
|
csat_input_message_body: 'Please rate the conversation'
|
|
reply:
|
|
email:
|
|
header:
|
|
notifications: 'Notifications'
|
|
from_with_name: '%{assignee_name} from %{inbox_name} <%{from_email}>'
|
|
reply_with_name: '%{assignee_name} from %{inbox_name} <reply+%{reply_email}>'
|
|
friendly_name: '%{sender_name} from %{business_name} <%{from_email}>'
|
|
professional_name: '%{business_name} <%{from_email}>'
|
|
channel_email:
|
|
header:
|
|
reply_with_name: '%{assignee_name} from %{inbox_name} <%{from_email}>'
|
|
reply_with_inbox_name: '%{inbox_name} <%{from_email}>'
|
|
email_subject: 'New messages on this conversation'
|
|
transcript_subject: 'Conversation Transcript'
|
|
survey:
|
|
response: 'Please rate this conversation, %{link}'
|
|
contacts:
|
|
online:
|
|
delete: '%{contact_name} is Online, please try again later'
|
|
sync_group:
|
|
not_a_group: 'Contact is not a group'
|
|
no_identifier: 'Contact does not have a group identifier'
|
|
no_supported_inbox: 'No inbox with group sync support found for this contact'
|
|
metadata_unavailable: 'Could not retrieve group metadata from the provider'
|
|
integration_apps:
|
|
# Note: webhooks and dashboard_apps don't need short_description as they use different modal components
|
|
dashboard_apps:
|
|
name: 'Dashboard Apps'
|
|
description: 'Dashboard Apps allow you to create and embed applications that display user information, orders, or payment history, providing more context to your customer support agents.'
|
|
dyte:
|
|
name: 'Dyte'
|
|
short_description: 'Start video/voice calls with customers directly from Chatwoot.'
|
|
description: 'Dyte is a product that integrates audio and video functionalities into your application. With this integration, your agents can start video/voice calls with your customers directly from Chatwoot.'
|
|
meeting_name: '%{agent_name} has started a meeting'
|
|
slack:
|
|
name: 'Slack'
|
|
short_description: 'Receive notifications and respond to conversations directly in Slack.'
|
|
description: "Integrate Chatwoot with Slack to keep your team in sync. This integration allows you to receive notifications for new conversations and respond to them directly within Slack's interface."
|
|
webhooks:
|
|
name: 'Webhooks'
|
|
description: 'Webhook events provide real-time updates about activities in your Chatwoot account. You can subscribe to your preferred events, and Chatwoot will send you HTTP callbacks with the updates.'
|
|
dialogflow:
|
|
name: 'Dialogflow'
|
|
short_description: 'Build chatbots to handle initial queries before transferring to agents.'
|
|
description: 'Build chatbots with Dialogflow and easily integrate them into your inbox. These bots can handle initial queries before transferring them to a customer service agent.'
|
|
google_translate:
|
|
name: 'Google Translate'
|
|
short_description: 'Automatically translate customer messages for agents.'
|
|
description: "Integrate Google Translate to help agents easily translate customer messages. This integration automatically detects the language and converts it to the agent's or admin's preferred language."
|
|
openai:
|
|
name: 'OpenAI'
|
|
short_description: 'AI-powered reply suggestions, summarization, and message enhancement.'
|
|
description: 'Leverage the power of large language models from OpenAI with the features such as reply suggestions, summarization, message rephrasing, spell-checking, and label classification.'
|
|
linear:
|
|
name: 'Linear'
|
|
short_description: 'Create and link Linear issues directly from conversations.'
|
|
description: 'Create issues in Linear directly from your conversation window. Alternatively, link existing Linear issues for a more streamlined and efficient issue tracking process.'
|
|
notion:
|
|
name: 'Notion'
|
|
short_description: 'Integrate databases, documents and pages directly with Captain.'
|
|
description: 'Connect your Notion workspace to enable Captain to access and generate intelligent responses using content from your databases, documents, and pages to provide more contextual customer support.'
|
|
shopify:
|
|
name: 'Shopify'
|
|
short_description: 'Access order details and customer data from your Shopify store.'
|
|
description: 'Connect your Shopify store to access order details, customer information, and product data directly within your conversations and helps your support team provide faster, more contextual assistance to your customers.'
|
|
leadsquared:
|
|
name: 'LeadSquared'
|
|
short_description: 'Sync your contacts and conversations with LeadSquared CRM.'
|
|
description: 'Sync your contacts and conversations with LeadSquared CRM. This integration automatically creates leads in LeadSquared when new contacts are added, and logs conversation activity to provide your sales team with complete context.'
|
|
captain:
|
|
copilot_message_required: Message is required
|
|
copilot_error: 'Please connect an assistant to this inbox to use Copilot'
|
|
copilot_limit: 'You are out of Copilot credits. You can buy more credits from the billing section.'
|
|
upgrade: 'Upgrade your plan to enable Captain AI'
|
|
disabled: 'Captain AI is disabled for this account.'
|
|
api_key_missing: 'Captain AI API key is not configured.'
|
|
copilot:
|
|
using_tool: 'Using tool %{function_name}'
|
|
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}'
|
|
custom_tool:
|
|
slug_generation_failed: 'Unable to generate unique slug after 5 attempts'
|
|
public_portal:
|
|
search:
|
|
search_placeholder: Search for article by title or body...
|
|
empty_placeholder: No results found.
|
|
loading_placeholder: Searching...
|
|
results_title: Search results
|
|
toc_header: 'On this page'
|
|
hero:
|
|
sub_title: Search for the articles here or browse the categories below.
|
|
common:
|
|
home: Home
|
|
last_updated_on: Last updated on %{last_updated_on}
|
|
view_all_articles: View all
|
|
article: article
|
|
articles: articles
|
|
author: author
|
|
authors: authors
|
|
other: other
|
|
others: others
|
|
by: By
|
|
no_articles: There are no articles here
|
|
footer:
|
|
made_with: Made with
|
|
header:
|
|
go_to_homepage: Website
|
|
visit_website: Visit website
|
|
appearance:
|
|
system: System
|
|
light: Light
|
|
dark: Dark
|
|
featured_articles: Featured Articles
|
|
uncategorized: Uncategorized
|
|
404:
|
|
title: Page not found
|
|
description: We couldn't find the page you were looking for.
|
|
back_to_home: Go to home page
|
|
slack_unfurl:
|
|
fields:
|
|
name: Name
|
|
email: Email
|
|
phone_number: Phone
|
|
company_name: Company
|
|
inbox_name: Inbox
|
|
inbox_type: Inbox Type
|
|
button: Open conversation
|
|
time_units:
|
|
days:
|
|
one: '%{count} day'
|
|
other: '%{count} days'
|
|
hours:
|
|
one: '%{count} hour'
|
|
other: '%{count} hours'
|
|
minutes:
|
|
one: '%{count} minute'
|
|
other: '%{count} minutes'
|
|
seconds:
|
|
one: '%{count} second'
|
|
other: '%{count} seconds'
|
|
automation:
|
|
system_name: 'Automation System'
|
|
crm:
|
|
no_message: 'No messages in conversation'
|
|
attachment: '[Attachment: %{type}]'
|
|
no_content: '[No content]'
|
|
created_activity: |
|
|
New conversation started on %{brand_name}
|
|
|
|
Channel: %{channel_info}
|
|
Created: %{formatted_creation_time}
|
|
Conversation ID: %{display_id}
|
|
View in %{brand_name}: %{url}
|
|
transcript_activity: |
|
|
Conversation Transcript from %{brand_name}
|
|
|
|
Channel: %{channel_info}
|
|
Conversation ID: %{display_id}
|
|
View in %{brand_name}: %{url}
|
|
|
|
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'
|
|
invalid_email_format: 'Invalid email format'
|
|
custom_domain_not_configured: 'Custom domain is not configured'
|
|
instructions_sent_successfully: 'Instructions sent successfully'
|
|
subject: 'Finish setting up %{custom_domain}'
|
|
ssl_status:
|
|
custom_domain_not_configured: 'Custom domain is not configured'
|
|
recurring_scheduled_messages:
|
|
description:
|
|
daily:
|
|
one: "Every day"
|
|
other: "Every %{count} days"
|
|
weekly:
|
|
one: "Every week"
|
|
other: "Every %{count} weeks"
|
|
weekly_on: "%{prefix} on %{days}"
|
|
monthly:
|
|
one: "Monthly"
|
|
other: "Every %{count} months"
|
|
monthly_on_weekday: "%{prefix} on the %{ordinal} %{weekday}"
|
|
yearly:
|
|
one: "Every year"
|
|
other: "Every %{count} years"
|
|
until_date: "until %{date}"
|
|
after_count:
|
|
one: "%{count} occurrence"
|
|
other: "%{count} occurrences"
|
|
ordinals:
|
|
first: "first"
|
|
second: "second"
|
|
third: "third"
|
|
fourth: "fourth"
|
|
fifth: "fifth"
|
|
last: "last"
|