wacrm/messages/en.json
Arnas Donauskas 78b42dfba9 feat: WhatsApp interactive messages (reply buttons + lists)
Add full support for WhatsApp Cloud API interactive messages across the
inbox, automations, and a new quick-replies feature. Extends the
interactive plumbing that previously existed only for the Flows engine.

Sending
- send-message core handles `interactive` (reply buttons ≤3, list ≤10
  rows) via the existing Meta senders; validated server-side before send.
- Inbox composer gains a "+" menu → builder dialog with live WhatsApp-style
  preview + Meta char limits (button 20, list row 24, body 1024).
- Automation "Send Buttons" / "Send List" steps in the builder.
- Public v1 /messages accepts type=interactive.

Storage / round-trip
- migration 035: messages.interactive_payload JSONB + quick_replies table.
- Outbound interactive persists the structured payload (composer,
  automations, and Flows) so the thread re-renders the buttons/rows.

Receiving
- Bubble renders outbound interactive via the shared preview; inbound taps
  (button_reply/list_reply) keep the "chosen option" affordance.

Automations
- New `interactive_reply` trigger matches on tapped button/list IDs
  (exact), enabling chained multi-step menus.

Quick replies
- New account-scoped quick_replies (text or interactive snippets), managed
  in Settings and insertable from the composer; "Save as quick reply" in
  the builder.

Shared core in src/lib/whatsapp/interactive.ts + src/components/interactive/.
Includes unit tests for the validator, send-param validation, the
interactive_reply trigger match, and the new automation step/trigger
validation. typecheck + lint clean; full suite green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 19:03:15 +03:00

1697 lines
72 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{
"LoginPage": {
"titleAccept": "Sign in to accept",
"titleWelcome": "Welcome back",
"descAccept": "Sign in and we'll take you to the invitation.",
"descWelcome": "Sign in to your account",
"emailLabel": "Email",
"emailPlaceholder": "you@example.com",
"passwordLabel": "Password",
"forgotPassword": "Forgot password?",
"passwordPlaceholder": "Enter your password",
"signingIn": "Signing in...",
"signIn": "Sign in",
"noAccount": "Don't have an account?",
"createAccount": "Create account"
},
"Sidebar": {
"title": "CRM Template for WhatsApp",
"dashboard": "Dashboard",
"inbox": "Inbox",
"notifications": "Notifications",
"contacts": "Contacts",
"pipelines": "Pipelines",
"broadcasts": "Broadcasts",
"automations": "Automations",
"flows": "Flows",
"aiAgents": "AI Agents",
"settings": "Settings",
"beta": "Beta",
"unreadConversations": "{count} unread {count, plural, =1 {conversation} other {conversations}}",
"unreadNotifications": "{count} unread {count, plural, =1 {notification} other {notifications}}",
"roleOwner": "Owner",
"roleAdmin": "Admin",
"roleAgent": "Agent",
"roleViewer": "Viewer",
"closeMenu": "Close menu",
"defaultUser": "User",
"defaultAvatar": "Avatar",
"menuProfile": "Profile",
"menuSettings": "Settings",
"menuSignOut": "Sign out"
},
"Header": {
"dashboard": "Dashboard",
"inbox": "Inbox",
"notifications": "Notifications",
"contacts": "Contacts",
"pipelines": "Pipelines",
"broadcasts": "Broadcasts",
"automations": "Automations",
"settings": "Settings",
"openMenu": "Open menu",
"openAccountMenu": "Open account menu",
"defaultUser": "User",
"defaultAvatar": "Avatar",
"menuProfile": "Profile",
"menuSettings": "Settings",
"menuSignOut": "Sign out"
},
"ModeToggle": {
"switchMode": "Switch to {mode} mode"
},
"Dashboard": {
"page": {
"title": "Dashboard",
"description": "Live analytics across conversations, contacts, deals, broadcasts, and automations.",
"activeConversations": "Active Conversations",
"newContactsToday": "New Contacts Today",
"openDealsValue": "Open Deals Value",
"messagesSentToday": "Messages Sent Today",
"newTodayVsYesterday": "new today vs yesterday",
"vsYesterday": "vs yesterday",
"openDeals": "{count} open {count, plural, =1 {deal} other {deals}}",
"noChange": "No change {suffix}"
},
"quickActions": {
"newContact": "New Contact",
"newDeal": "New Deal",
"newBroadcast": "New Broadcast",
"newAutomation": "New Automation"
},
"activityFeed": {
"title": "Recent Activity",
"viewAll": "View all →",
"noActivity": "No activity yet",
"noActivityHint": "Activity from messages, deals, broadcasts, and automations will appear here.",
"showingOf": "Showing {visible} of {totalLoaded}{plus}",
"show": "Show",
"timeS": "{sec}s ago",
"timeM": "{min}m ago",
"timeH": "{hr}h ago",
"timeD": "{day}d ago"
},
"conversationsChart": {
"title": "Conversations Over Time",
"description": "Daily message volume by direction",
"days": "{count} days",
"noActivity": "No message activity in this range",
"noActivityHint": "Send or receive messages to start populating this chart.",
"incoming": "Incoming",
"outgoing": "Outgoing",
"tooltipIncoming": "{count} incoming",
"tooltipOutgoing": "{count} outgoing",
"ariaLabel": "Conversations per day"
},
"pipelineDonut": {
"title": "Pipeline Value",
"description": "Open deals by stage",
"noOpenDeals": "No open deals yet",
"noOpenDealsHint": "Create deals in Pipelines to see stage breakdowns here.",
"dealCount": "{count} {count, plural, =1 {deal} other {deals}}",
"total": "Total",
"ariaLabel": "Pipeline value by stage"
},
"responseTimeChart": {
"title": "Average First Response Time",
"description": "Minutes to reply to a customer's first unreplied message, by weekday",
"target": "target {minutes}m",
"thisWeek": "This week:",
"lastWeek": "Last week:",
"noReplies": "No replies recorded yet",
"noRepliesHint": "This chart fills in as you reply to customer messages."
},
"emptyState": {
"title": "Not enough data yet"
}
},
"Inbox": {
"page": {
"whatsappNotConnected": "WhatsApp® is not connected. Go to Settings to connect your account."
},
"conversationList": {
"searchPlaceholder": "Search conversations...",
"filterAll": "All",
"filterUnread": "Unread",
"filterOpen": "Open",
"filterPending": "Pending",
"filterClosed": "Closed",
"tags": "Tags",
"company": "Company",
"allCompanies": "All companies",
"clearAll": "Clear all",
"noConversations": "No conversations found",
"noMessagesYet": "No messages yet",
"unknown": "Unknown"
},
"messageThread": {
"backToConversations": "Back to conversations",
"hideContactPanel": "Hide contact panel",
"showContactPanel": "Show contact panel",
"hideContact": "Hide contact",
"showContact": "Show contact",
"refresh": "Refresh",
"refreshConversation": "Refresh conversation",
"status": "Status",
"statusOpen": "Open",
"statusPending": "Pending",
"statusClosed": "Closed",
"assign": "Assign",
"assigned": "Assigned",
"unassign": "Unassign",
"noTeammates": "No teammates available",
"me": " (me)",
"noMessagesYet": "No messages yet",
"sendTemplateHint": "Send a template to start the conversation",
"selectConversation": "Select a conversation",
"selectConversationHint": "Choose a conversation from the left to start messaging",
"today": "Today",
"yesterday": "Yesterday"
},
"sessionTimer": {
"expired": "Expired",
"xhRemaining": "{hours}h remaining",
"xmRemaining": "{minutes}m remaining",
"noCustomerMessages": "No customer messages"
},
"composer": {
"draftHint": "Tap the ✨ to draft a reply with AI — you can edit it before sending",
"sessionExpiredHint": "24-hour session expired. Use a template to re-engage.",
"templates": "Templates",
"readOnlyPlaceholder": "Read-only — viewers can browse but not reply",
"sessionExpiredPlaceholder": "Session expired - use a template",
"typeMessagePlaceholder": "Type a message... (Shift+Enter for new line)",
"readOnlyTitle": "Read-only — your role can't send messages",
"recording": "Recording… {current} / {max}",
"cancel": "Cancel",
"stopAndAttach": "Stop and attach",
"attachMedia": "Attach media",
"photo": "Photo",
"video": "Video",
"document": "Document",
"voiceNote": "Voice note",
"sendTemplate": "Send template",
"draftWithAI": "Draft a reply with AI",
"addCaption": "Add a caption…",
"removeAttachment": "Remove attachment",
"moreActions": "More",
"interactiveMessage": "Interactive message",
"quickReplies": "Quick replies",
"quickRepliesEmpty": "No quick replies yet. Add some in Settings → Quick replies.",
"saveAsQuickReply": "Save as quick reply",
"send": "Send",
"quickReplyNamePrompt": "Name this quick reply:",
"quickReplySaved": "Saved as a quick reply.",
"quickReplySaveError": "Couldn't save the quick reply."
},
"bubble": {
"photo": "Photo",
"video": "Video",
"audio": "Audio",
"unavailable": "{label} unavailable",
"document": "Document",
"locationShared": "Location shared",
"template": "Template",
"buttonReply": "Button reply",
"interactiveReply": "[Interactive reply]",
"unsupported": "[Unsupported message type]",
"aiBadge": "AI",
"aiBadgeTitle": "Sent automatically by the AI assistant"
},
"actions": {
"reply": "Reply",
"copyText": "Copy text",
"delete": "Delete",
"react": "React",
"reactWith": "React with {emoji}",
"nothingToCopy": "Nothing to copy",
"copied": "Copied",
"copyFailed": "Copy failed"
},
"replyQuote": {
"photo": "[Photo]",
"video": "[Video]",
"document": "[Document]",
"audio": "[Audio]",
"voiceMessage": "[Voice message]",
"location": "[Location]",
"template": "[Template]",
"message": "[Message]",
"cancelReply": "Cancel reply"
},
"templatePicker": {
"sendTemplate": "Send template",
"fillPlaceholders": "Fill in the placeholders to render this template. Meta requires every variable to be set.",
"pickTemplate": "Pick an approved WhatsApp template to send to this contact.",
"noApprovedTemplates": "No approved templates",
"noApprovedTemplatesHint": "Approve a template in Meta WhatsApp Manager, then sync it from Settings → Templates.",
"preview": "Preview",
"headerValuePlaceholder": "Value for the header variable",
"bodyValuePlaceholder": "Value for {val}",
"urlSuffixValuePlaceholder": "URL suffix value",
"finalUrl": "Final URL: {url}",
"back": "Back",
"cancel": "Cancel",
"send": "Send template"
},
"sidebar": {
"contactInfo": "Contact info",
"tags": "Tags",
"notes": "Notes",
"deals": "Deals",
"noTags": "No tags",
"noDeals": "No deals",
"addNotePlaceholder": "Add a note..."
},
"aiBanner": {
"pausedTitle": "AI assistant is paused here",
"activeText": "AI assistant is replying automatically",
"takeOver": "Take over",
"resume": "Resume AI",
"tookOver": "You’ve taken over this chat.",
"resumed": "AI resumed.",
"updateError": "Couldn’t update the AI assistant.",
"networkError": "Couldn’t reach the server."
}
},
"Contacts": {
"page": {
"title": "Contacts",
"subtitle": "Manage your contact list. {count} total contacts.",
"subtitleZero": "Manage your contact list.",
"customFieldsBtn": "Custom fields",
"importBtn": "Import",
"addContactBtn": "Add Contact",
"searchPlaceholder": "Search by name, phone, or email...",
"filterByTags": "Filter by tags",
"clearAll": "Clear all",
"noTagsYet": "No tags yet.",
"selectedCount": "{count} selected",
"clearSelection": "Clear",
"deleteSelected": "Delete selected",
"tableColumns": {
"name": "Name",
"phone": "Phone",
"email": "Email",
"company": "Company",
"tags": "Tags",
"createdAt": "Created"
},
"loading": "Loading contacts...",
"noContactsMatch": "No contacts match your filters.",
"noContactsYet": "No contacts yet.",
"addFirstContact": "Add your first contact",
"unnamed": "Unnamed",
"editAction": "Edit",
"deleteAction": "Delete",
"showingPagination": "Showing {start}-{end} of {total}",
"pageCount": "Page {page} of {total}",
"deleteContactTitle": "Delete Contact",
"deleteContactDesc": "Are you sure you want to delete {name}? This action cannot be undone.",
"cancel": "Cancel",
"deleteBtn": "Delete",
"deleteBulkTitle": "Delete Contacts",
"deleteBulkDesc": "Are you sure you want to delete {count} selected contacts? This action cannot be undone.",
"toastFailedLoad": "Failed to load contacts",
"toastFailedDelete": "Failed to delete contact",
"toastDeleted": "Contact deleted",
"toastBulkFailedDelete": "Failed to delete contacts",
"toastBulkDeleted": "{count} contacts deleted"
},
"form": {
"addTitle": "Add Contact",
"addDesc": "Create a new contact. Name is required.",
"editTitle": "Edit Contact",
"editDesc": "Update contact details and tags.",
"nameLabel": "Name",
"namePlaceholder": "Jane Doe",
"phoneLabel": "Phone Number",
"phonePlaceholder": "+1234567890",
"phoneHint": "Include country code, e.g. +1 for US",
"emailLabel": "Email",
"emailPlaceholder": "jane@example.com",
"companyLabel": "Company",
"companyPlaceholder": "Acme Corp",
"tagsLabel": "Tags",
"tagsPlaceholder": "Select tags...",
"noTagsFound": "No tags found.",
"createTagAction": "Create tag \"{value}\"",
"cancel": "Cancel",
"save": "Save Contact",
"update": "Update",
"create": "Create",
"toastSuccessAdd": "Contact created successfully",
"toastSuccessEdit": "Contact updated successfully",
"toastError": "Failed to save contact",
"toastConflict": "A contact with this phone number already exists",
"viewExisting": "View {name}",
"dupExact": "A contact with this phone number already exists.",
"dupSimilar": "A contact with a very similar number already exists.",
"loadingTags": "Loading tags...",
"noTagsAvailable": "No tags available. Create tags in Settings.",
"phoneRequired": "Phone number is required"
},
"detailView": {
"tabs": {
"details": "Details",
"activity": "Activity",
"notes": "Notes",
"custom": "Custom Fields",
"deals": "Deals",
"noTags": "No tags",
"noDeals": "No deals",
"addNotePlaceholder": "Add a note..."
},
"actions": {
"edit": "Edit",
"message": "Message",
"delete": "Delete"
},
"contactInfo": "Contact Information",
"unnamed": "Unknown",
"contactDetailsDesc": "Contact details",
"phone": "Phone",
"email": "Email",
"company": "Company",
"createdAt": "Created At",
"customFields": "Custom Fields",
"noCustomFields": "No custom fields defined. Create them in Settings.",
"manageFields": "Manage fields",
"enterCustomField": "Enter {name}...",
"activityTab": {
"title": "Activity History",
"noActivity": "No activity yet."
},
"notesTab": {
"title": "Internal Notes",
"placeholder": "Write a note...",
"save": "Add Note",
"noNotes": "No notes yet."
},
"dealsTab": {
"noDeals": "No deals yet"
},
"tagsTab": {
"clickTagDesc": "Click a tag to add or remove it from this contact.",
"noTagsAvailable": "No tags available. Create tags in Settings."
},
"sendTemplateBtn": "Send template",
"saveChangesBtn": "Save Changes",
"saveCustomFieldsBtn": "Save Custom Fields",
"toastPhoneRequired": "Phone number is required",
"toastUpdateFailed": "Failed to update contact",
"toastUpdated": "Contact updated",
"toastNotAuthenticated": "Not authenticated",
"toastNoteAddFailed": "Failed to add note",
"toastNoteAdded": "Note added",
"toastNoteDeleteFailed": "Failed to delete note",
"toastNoteDeleted": "Note deleted",
"toastCustomFieldsSaved": "Custom fields saved",
"toastCustomFieldsFailed": "Failed to save custom fields",
"toastTemplateSent": "Template \"{name}\" sent",
"toastTemplateFailed": "Failed to send template: {reason}"
},
"importModal": {
"title": "Import Contacts",
"desc": "Upload a CSV with a required <phoneCode>phone</phoneCode> column. Optional: <nameCode>name</nameCode>, <emailCode>email</emailCode>, <companyCode>company</companyCode>, <tagsCode>tags</tagsCode> (comma-separated; quote multi-tag cells).",
"uploadDropzone": "Click to choose a CSV file",
"uploadHint": ".csv up to your browser limit",
"rowsReady": "{count} row ready",
"rowsReady_plural": "{count} rows ready",
"preview": "Preview · first {count}",
"previewTags": "{tags} tag · {contacts} contact",
"previewTags_plural": "{tags} tags · {contacts} contacts",
"moreRows": "+ {count} more row not shown",
"moreRows_plural": "+ {count} more rows not shown",
"importComplete": "Import complete",
"resultImported": "{count} imported",
"resultTags": "{count} tag assigned",
"resultTags_plural": "{count} tags assigned",
"resultSkipped": "{count} skipped",
"resultFailed": "{count} failed",
"willBeCreated": "{name} (will be created on import)",
"columns": {
"phone": "Phone",
"name": "Name",
"email": "Email",
"company": "Company",
"tags": "Tags"
},
"cancel": "Cancel",
"close": "Close",
"importBtn": "Import {count} contact",
"importBtn_plural": "Import {count} contacts",
"toastNoValidRows": "No valid rows found. Ensure CSV has a \"phone\" column header.",
"toastTagsWarning": "Contacts imported, but some tag assignments failed.",
"toastImported": "{count} contact imported",
"toastImported_plural": "{count} contacts imported",
"toastTagsAssigned": "{count} tag assignment applied",
"toastTagsAssigned_plural": "{count} tag assignments applied",
"toastTagsSkipped": "Unknown tags skipped (create them in Settings first): {sample}{more}",
"toastSkipped": "{count} duplicate skipped",
"toastSkipped_plural": "{count} duplicates skipped",
"toastFailed": "{count} contact failed to import",
"toastFailed_plural": "{count} contacts failed to import",
"toastError": "Import failed"
},
"customFields": {
"title": "Custom fields",
"desc": "Define extra contact fields (e.g. ZIP code, lead source). They appear on every contact and in the “Update Contact Field” automation action.",
"addField": "Add",
"fieldName": "New field name…",
"loading": "Loading…",
"empty": "No custom fields yet.",
"renameAria": "Rename {name}",
"deleteTitle": "Delete field",
"deleteConfirm": "Delete \"{name}\"? This also removes its stored value on every contact. This cannot be undone.",
"toastNoAccount": "Your profile is not linked to an account.",
"toastDuplicate": "A field named \"{name}\" already exists.",
"toastCreateFailed": "Could not create field. You may not have permission.",
"toastCreated": "Created \"{name}\".",
"toastRenameFailed": "Could not rename field.",
"toastDeleteFailed": "Could not delete field.",
"toastDeleted": "Deleted \"{name}\"."
}
},
"Pipelines": {
"page": {
"selectPipeline": "Select Pipeline",
"noPipelinesYet": "No pipelines yet",
"managePipelines": "Manage Pipelines",
"addPipeline": "Add Pipeline",
"addDeal": "Add Deal",
"createToStartTracking": "Create a pipeline to start tracking deals",
"createPipeline": "Create Pipeline",
"newPipeline": "New Pipeline",
"pipelineName": "Pipeline Name",
"pipelineNamePlaceholder": "e.g., Enterprise Sales",
"defaultStagesDesc": "Default stages (New Lead → Won) will be created automatically.",
"cancel": "Cancel",
"creating": "Creating...",
"createPipelineBtn": "Create Pipeline",
"toastFailedLoadPipelines": "Failed to load pipelines",
"toastFailedMoveDeal": "Failed to move deal",
"toastFailedCreatePipeline": "Failed to create pipeline",
"toastPipelineCreated": "Pipeline created",
"toastNotLinkedToAccount": "Your profile is not linked to an account."
},
"board": {
"dropDealHere": "Drop a deal here",
"addDeal": "Add Deal"
},
"card": {
"noContact": "No contact",
"won": "Won",
"lost": "Lost"
},
"form": {
"editDeal": "Edit Deal",
"newDeal": "New Deal",
"title": "Title",
"titlePlaceholder": "Deal title",
"contact": "Contact",
"selectContact": "Select a contact",
"linkToConversation": "Link to Conversation",
"value": "Value",
"currency": "Currency",
"expectedCloseDate": "Expected Close Date",
"stage": "Stage",
"assignedTo": "Assigned To",
"unassigned": "Unassigned",
"notes": "Notes",
"notesPlaceholder": "Add notes...",
"status": "Status",
"markAsWon": "Mark as Won",
"markAsLost": "Mark as Lost",
"reopenDeal": "Reopen deal",
"cancel": "Cancel",
"saving": "Saving...",
"saveChanges": "Save Changes",
"createDeal": "Create Deal",
"deletePrompt": "Delete this deal?",
"confirm": "Confirm",
"deleting": "Deleting...",
"deleteDeal": "Delete Deal",
"toastRequired": "Title, contact, and stage are required",
"toastFailedSave": "Failed to save deal",
"toastNotSignedIn": "Not signed in",
"toastNotLinked": "Your profile is not linked to an account.",
"toastFailedCreate": "Failed to create deal",
"toastUpdated": "Deal updated",
"toastCreated": "Deal created",
"toastFailedStatus": "Failed to update deal status",
"toastMarkedWon": "Marked as won",
"toastMarkedLost": "Marked as lost",
"toastReopened": "Deal reopened",
"toastFailedDelete": "Failed to delete deal",
"toastDeleted": "Deal deleted"
},
"settings": {
"managePipeline": "Manage Pipeline",
"deletePipeline": "Delete Pipeline",
"deletePipelineDesc": "This will archive all deals in this pipeline. This cannot be undone.",
"cancel": "Cancel",
"deleting": "Deleting...",
"deletePipelineBtn": "Delete Pipeline",
"pipelineName": "Pipeline Name",
"stages": "Stages",
"newStageNamePlaceholder": "New stage name",
"add": "Add",
"createNewPipeline": "Create a new pipeline",
"saving": "Saving...",
"saveChanges": "Save Changes",
"dragToReorder": "Drag to reorder",
"changeColor": "Change color",
"toastFailedSave": "Failed to save pipeline",
"toastSaved": "Pipeline saved",
"toastFailedAddStage": "Failed to add stage",
"toastMoveOrDeleteDeals": "Move or delete deals in this stage first",
"toastFailedDeleteStage": "Failed to delete stage",
"toastFailedDeletePipeline": "Failed to delete pipeline",
"toastDeleted": "Pipeline deleted"
},
"analytics": {
"totalDeals": "Total Deals",
"totalDealsTooltip": "Count of every deal in this pipeline that isn't marked as Lost. Won deals are still included.",
"pipelineValue": "Pipeline Value",
"pipelineValueTooltip": "Sum of the dollar values of all deals in this pipeline, excluding deals marked as Lost.",
"avgDealSize": "Avg Deal Size",
"avgDealSizeTooltip": "Pipeline Value divided by Total Deals — the average value of a single non-lost deal.",
"weightedValue": "Weighted Value",
"weightedValueTooltip": "Expected revenue: each open deal's value × its stage probability. First stage ≈ 10%, stages progress up to 90%, Won = 100%. Lost deals are excluded.",
"wonThisMonth": "Won This Month",
"wonThisMonthTooltip": "Deals marked as Won since the first day of the current month.",
"lostThisMonth": "Lost This Month",
"lostThisMonthTooltip": "Deals marked as Lost since the first day of the current month.",
"howCalculated": "How {label} is calculated"
}
},
"Broadcasts": {
"page": {
"title": "Broadcasts",
"subtitle": "Send bulk messages to your contacts using approved templates.",
"newBroadcast": "New Broadcast",
"noBroadcastsYet": "No broadcasts yet",
"createFirst": "Create your first broadcast to reach your contacts at scale.",
"table": {
"name": "Name",
"template": "Template",
"recipients": "Recipients",
"delivery": "Delivery",
"read": "Read",
"status": "Status",
"date": "Date"
},
"errorLoad": "Failed to load broadcasts",
"retry": "Retry"
},
"status": {
"draft": "Draft",
"scheduled": "Scheduled",
"sending": "Sending",
"sent": "Sent",
"failed": "Failed",
"pending": "Pending",
"delivered": "Delivered",
"read": "Read",
"replied": "Replied",
"unknown": "Unknown"
},
"detail": {
"backToBroadcasts": "Back to Broadcasts",
"notFound": "Broadcast not found",
"template": "Template: {name}",
"createdAt": "Created {date}",
"deletePrompt": "Delete this broadcast?",
"cancel": "Cancel",
"confirm": "Confirm",
"deleting": "Deleting…",
"delete": "Delete",
"cannotDeleteSending": "Cannot delete while a broadcast is actively sending",
"deleteHover": "Delete this broadcast",
"funnel": "Funnel",
"recipientsHeader": "Recipients ({filtered} of {total})",
"recipientsHeaderAll": "Recipients ({total})",
"allStatuses": "All statuses",
"exportCsv": "Export CSV",
"noRecipients": "No recipients found.",
"noRecipientsFilter": "No recipients match this filter.",
"table": {
"contact": "Contact",
"phone": "Phone",
"status": "Status",
"sent": "Sent",
"delivered": "Delivered",
"read": "Read",
"error": "Error"
},
"stats": {
"totalRecipients": "Total Recipients",
"sent": "Sent",
"delivered": "Delivered",
"read": "Read",
"replied": "Replied",
"failed": "Failed"
},
"toastFailedDelete": "Failed to delete: {error}",
"toastDeleted": "Broadcast deleted"
},
"new": {
"title": "New Broadcast",
"subtitle": "Create and send a broadcast message to your contacts.",
"steps": {
"template": "Template",
"audience": "Audience",
"personalize": "Personalize",
"send": "Send"
},
"toastGiveName": "Give the broadcast a name before saving a draft.",
"toastNotSignedIn": "Not signed in.",
"toastNotLinked": "Your profile is not linked to an account.",
"toastFailedDraft": "Failed to save draft: {error}",
"toastDraftSaved": "Draft saved"
},
"wizard": {
"next": "Next",
"back": "Back",
"cancel": "Cancel",
"chooseTemplate": {
"title": "Choose a Template",
"subtitle": "Select an approved message template for your broadcast.",
"noTemplates": "No templates available.",
"createFirst": "Create a template in Settings first.",
"errorLoad": "Failed to load templates"
},
"selectAudience": {
"title": "Select Audience",
"subtitle": "Choose who will receive this broadcast.",
"method": {
"all": "All Contacts",
"tags": "Filter by Tags",
"customField": "Custom Field",
"csv": "Upload CSV"
},
"allDesc": "Send to all {count} contacts in your database.",
"allDescLoading": "Send to all contacts in your database.",
"tagDesc": "Send to contacts matching specific tags.",
"customFieldDesc": "Send to contacts matching a custom field rule.",
"csvDesc": "Upload a CSV with phone numbers (and optional names).",
"selectTags": "Select Tags",
"searchTags": "Search tags...",
"noTagsFound": "No tags found.",
"excludeTags": "Exclude Tags (Optional)",
"searchExcludeTags": "Search tags to exclude...",
"field": "Field",
"selectField": "Select a field...",
"operator": "Operator",
"operatorIs": "Is",
"operatorIsNot": "Is not",
"operatorContains": "Contains",
"value": "Value",
"valuePlaceholder": "Value to match",
"uploadCsv": "Upload CSV File",
"csvFormatDesc": "Must contain a 'phone' column (E.164 format). 'name' column is optional.",
"csvContactsFound": "{count} valid contact(s) found in CSV.",
"errorLoadTags": "Failed to load tags",
"errorLoadFields": "Failed to load custom fields",
"errorCsvParse": "Could not parse CSV file.",
"errorCsvMissingPhone": "CSV must have a 'phone' column."
},
"personalize": {
"title": "Personalize Variables",
"subtitle": "Map your template variables to contact fields or provide static values.",
"headerImage": "Header Image",
"headerImageDesc": "This template requires a header image.",
"imageUrl": "Image URL",
"imageUrlPlaceholder": "https://example.com/image.jpg",
"variables": "Variables",
"varDesc": "Set values for {{name}}",
"type": "Type",
"typeStatic": "Static Value",
"typeContact": "Contact Field",
"typeCustom": "Custom Field",
"staticValue": "Static Value",
"contactField": "Contact Field",
"selectContactField": "Select field...",
"customField": "Custom Field",
"selectCustomField": "Select custom field...",
"preview": "Preview",
"previewSample": "Showing preview with sample data.",
"noPreview": "Preview not available.",
"fieldMap": {
"name": "Name",
"phone": "Phone",
"email": "Email"
}
},
"scheduleSend": {
"title": "Review & Send",
"subtitle": "Give your broadcast a name and confirm sending.",
"broadcastName": "Broadcast Name",
"broadcastNamePlaceholder": "e.g., Summer Promo",
"summary": "Summary",
"template": "Template:",
"audience": "Audience:",
"audienceAll": "All contacts",
"audienceTags": "Filtered by tags",
"audienceField": "Filtered by custom field",
"audienceCsv": "CSV upload",
"variables": "Variables:",
"variablesMapped": "{count} mapped",
"saveDraft": "Save as Draft",
"sendNow": "Send Broadcast",
"sending": "Sending...",
"preparing": "Preparing...",
"progressDesc": "Processing {current} of {total} recipients...",
"toastFailedPreview": "Failed to load preview data"
}
}
},
"Automations": {
"list": {
"title": "Automations",
"subtitle": "Build workflows that react to WhatsApp® events automatically.",
"create": "Create Automation",
"retry": "Retry",
"templatesTitle": "Quick-start templates",
"emptyTitle": "No automations yet",
"emptyDesc": "Pick a template above or create one from scratch.",
"runs": "{count} run",
"runsPlural": "{count} runs",
"lastRun": "last {time}",
"activate": "Activate",
"deactivate": "Deactivate",
"edit": "Edit",
"duplicate": "Duplicate",
"viewLogs": "View Logs",
"delete": "Delete",
"cancel": "Cancel",
"deleteTitle": "Delete automation",
"deleteDesc": "This permanently removes {name} and its execution history. This cannot be undone.",
"toasts": {
"activated": "Automation activated",
"paused": "Automation paused",
"duplicated": "Automation duplicated",
"deleted": "Automation deleted",
"updateError": "Failed to update",
"duplicateError": "Failed to duplicate",
"deleteError": "Failed to delete"
}
},
"edit": {
"loading": "Loading...",
"loadError": "Failed to load ({status})",
"back": "Back to Automations"
},
"logs": {
"title": "Execution logs",
"loadError": "Failed to load logs",
"emptyTitle": "No executions yet",
"emptyDesc": "Trigger this automation to see runs here.",
"unknownContact": "Unknown contact",
"step": "{count} step",
"stepPlural": "{count} steps",
"noSteps": "No steps recorded.",
"status": {
"success": "success",
"partial": "partial",
"failed": "failed"
},
"back": "Back",
"backAria": "Back"
},
"builder": {
"steps": {
"send_message": "Send Message",
"send_buttons": "Send Buttons",
"send_list": "Send List",
"send_template": "Send Template",
"add_tag": "Add Tag",
"remove_tag": "Remove Tag",
"assign_conversation": "Assign Conversation",
"update_contact_field": "Update Contact Field",
"create_deal": "Create Deal",
"wait": "Wait",
"condition": "Condition (If/Else)",
"send_webhook": "Send Webhook",
"close_conversation": "Close Conversation"
},
"triggers": {
"new_message_received": {
"label": "New Message Received",
"hint": "Any incoming message"
},
"first_inbound_message": {
"label": "First Message from Contact",
"hint": "First time this contact ever messages you (works for manually-added contacts too)"
},
"keyword_match": {
"label": "Keyword Match",
"hint": "Message contains specific keyword(s)"
},
"interactive_reply": {
"label": "Button / List Reply",
"hint": "Customer taps a button or list row whose id matches — chain menus across automations"
},
"new_contact_created": {
"label": "New Contact Created",
"hint": "When a contact is auto-created from an incoming message"
},
"conversation_assigned": {
"label": "Conversation Assigned",
"hint": "When assigned to an agent"
},
"tag_added": {
"label": "Tag Added",
"hint": "When a tag is added to a contact"
},
"time_based": {
"label": "Time-Based",
"hint": "On a recurring schedule"
}
},
"trigger": "Trigger",
"triggerType": "Trigger type",
"keywords": "Keywords",
"keywordsHint": "Comma-separated list (e.g. hello, help, buy)",
"replyIds": "Reply IDs",
"replyIdsHint": "yes, no, more_info",
"replyIdsHelp": "Comma-separated button/list-row ids to match, exact. Use the same ids you set on a Send Buttons / Send List step.",
"schedule": "Schedule",
"scheduleHint": "Cron expression (e.g. 0 9 * * 1-5)",
"active": "Active",
"activeAria": "Active",
"save": "Save",
"saveDraft": "Save Draft",
"untitled": "Untitled automation",
"backToAutomations": "Back to automations",
"toasts": {
"saved": "Automation saved",
"created": "Automation created",
"saveFailed": "Save failed"
},
"tags": {
"select": "Select a tag…",
"placeholder": "Tag id",
"unknown": "{id} (unknown tag)"
},
"fields": {
"name": "Name",
"email": "Email",
"company": "Company",
"customFields": "Custom fields",
"unknown": "{id} (unknown field)"
},
"agents": {
"select": "Select an agent…",
"placeholder": "Agent id",
"unknown": "{id} (unknown agent)"
},
"pipelines": {
"pipelineLabel": "Pipeline",
"pipelineIdLabel": "Pipeline id",
"stageLabel": "Stage",
"stageIdLabel": "Stage id",
"selectPipeline": "Select a pipeline…",
"selectStage": "Select a stage…",
"selectPipelineFirst": "Select a pipeline first…",
"unknownPipeline": "{id} (unknown pipeline)",
"unknownStage": "{id} (unknown stage)"
},
"templates": {
"templateLabel": "Template",
"templateNameLabel": "Template name",
"languageLabel": "Language",
"select": "Select a template…",
"unknown": "{name} ({lang}) — not in approved list",
"unknownLang": "unknown"
},
"branches": {
"yes": "Yes",
"no": "No"
},
"addStep": "Add step",
"config": {
"messageText": "Message text",
"placeholderMessageText": "Hi! Thanks for reaching out…",
"tagLabel": "Tag",
"modeLabel": "Mode",
"modes": {
"round_robin": "Round-robin",
"specific": "Specific agent"
},
"agentLabel": "Agent",
"fieldLabel": "Field",
"valueLabel": "Value",
"placeholderValue": "Text or {{ vars.x }} / {{ message.text }}",
"titleLabel": "Title",
"amountLabel": "Amount",
"unitLabel": "Unit",
"units": {
"minutes": "Minutes",
"hours": "Hours",
"days": "Days"
},
"subjectLabel": "Subject",
"subjects": {
"tag_presence": "Tag presence",
"contact_field": "Contact field",
"message_content": "Message content",
"time_of_day": "Time of day"
},
"operandLabel": "Operand",
"placeholderTime": "HH:mm-HH:mm",
"placeholderContact": "name / email / company",
"placeholderTag": "tag id",
"urlLabel": "URL",
"headersLabel": "Headers",
"placeholderHeaders": "{\"Authorization\": \"Bearer ...\"}",
"bodyTemplateLabel": "Body template",
"placeholderBody": "{\"id\": \"{{ contact.id }}\"}",
"matchType": "Match type",
"matchContains": "Contains",
"matchExact": "Exact",
"closeConversationHint": "Sets the conversation status to \"closed\". No configuration needed."
}
}
},
"Flows": {
"list": {
"title": "Flows",
"beta": "Beta",
"description": "Build branching, button-driven WhatsApp conversations. Useful for menus, FAQs, and triage before a human steps in.",
"newFlow": "New flow",
"statusDraft": "Draft",
"statusActive": "Active",
"statusArchived": "Archived",
"loadError": "Couldn't load flows.",
"createError": "Couldn't create flow.",
"cloneError": "Clone failed",
"deleteConfirm": "Delete \"{name}\"? Any active runs will end immediately.",
"deleteSuccess": "Flow deleted.",
"deleteError": "Couldn't delete flow.",
"emptyTitle": "No flows yet",
"emptyDesc": "Build your first conversation — a welcome menu, an order lookup, an FAQ bot. Customers tap buttons; the bot routes them to the right answer (or the right agent).",
"createFirst": "Create your first flow",
"createTitle": "Create a new flow",
"createDesc": "Start from a template or build from scratch.",
"startTemplate": "Start from a template",
"nodeCount": "{count} {count, plural, =1 {node} other {nodes}}",
"startBlank": "Or start blank",
"placeholderName": "e.g. Welcome menu",
"cancel": "Cancel",
"createBlank": "Create blank flow",
"runCount": "{count} {count, plural, =1 {run} other {runs}}",
"edit": "Edit",
"delete": "Delete",
"triggerKeywordNone": "Triggers on keyword (none set)",
"triggerKeyword": "Triggers on: {keywords}",
"triggerFirstInbound": "Triggers on a contact's first-ever inbound message",
"triggerManual": "Manual trigger"
},
"edit": {
"notFound": "Flow not found.",
"backToFlows": "← Back to flows",
"loadError": "Couldn't load flow."
},
"logs": {
"title": "Runs",
"description": "The 50 most recent times this flow ran. Expand a row to see the engine's per-step log.",
"emptyState": "No runs yet. Trigger the flow from a personal WhatsApp number to see it appear here.",
"loadError": "Couldn't load runs.",
"unknownContact": "Unknown contact",
"atNode": "at {node}",
"started": "Started {time}",
"reprompts": "{count} re-prompts",
"ranFor": "ran for {duration}",
"capturedVars": "Captured vars ({count})",
"noEvents": "No events recorded for this run.",
"statusActive": "Active",
"statusCompleted": "Completed",
"statusHandedOff": "Handed off",
"statusTimedOut": "Timed out",
"statusPaused": "Paused by agent",
"statusFailed": "Failed"
},
"builder": {
"triggerTitle": "Trigger",
"whenLabel": "When…",
"triggerKeywordTitle": "A message contains a keyword",
"triggerFirstInboundTitle": "Customer's first ever inbound message",
"triggerManualTitle": "Manual only (no auto-trigger)",
"keywordsLabel": "Keywords (comma-separated)",
"keywordsPlaceholder": "support, help, hi",
"entryNodeTitle": "Entry node:",
"entryNodePlaceholder": "Pick the first node…",
"nodesTitle": "Nodes ({count})",
"nodesEmpty": "Add a <strong>Start</strong> node, then a <strong>Send buttons</strong> node, then a <strong>Handoff</strong> — that's the welcome-menu shape from the brief.",
"addNode": "Add node",
"setAsEntry": "Set as entry",
"removeNode": "Remove node",
"showAdvanced": "Show advanced",
"hideAdvanced": "Hide advanced",
"nodeKeyLabel": "Node key (internal identifier — keep stable for analytics)",
"replyIdsHint": "Reply IDs for each option are shown inline above. They're returned by WhatsApp when a customer taps; you usually don't need to touch them.",
"badgeEntry": "Entry",
"noNodesYet": "No nodes yet.",
"deleteNode": "Delete node",
"canvasView": "Canvas",
"listView": "List",
"noNodeSelected": "No node selected",
"noNodeDesc": "Select a node on the canvas to edit its configuration.",
"form": {
"advancesTo": "Advances to",
"textToCustomer": "Text sent to the customer",
"promptToCustomer": "Prompt sent to the customer",
"varKeyLabel": "Variable key (stored in flow_runs.vars; alphanumeric + underscore)",
"varKeyPlaceholder": "e.g. name, email, company",
"varKeyHelp": "Interpolate in downstream prompts and handoff notes with",
"advanceAfterCapture": "After capturing, advance to",
"internalNote": "Internal note (for the agent picking up)",
"endNodeHelp": "Terminal node. When the runner reaches this node the run is marked complete. No config needed.",
"bodyText": "Body text",
"footerText": "Footer (optional, 60 chars)",
"buttonsHelp": "Buttons (1–3) — each one routes to a different next node",
"optionTitlePlaceholder": "Visible title (≤20 chars)",
"nextNodePlaceholder": "Next node…",
"addButton": "Add button",
"buttonLabel": "Tap-to-expand button label (≤20 chars)",
"rowsHelp": "Rows (1–10 total across all sections)",
"sectionTitlePlaceholder": "Section {count} title (optional)",
"rowTitlePlaceholder": "Row title (≤24)",
"addRow": "Add row",
"addSection": "Add section",
"ifLabel": "If",
"capturedVariable": "Captured variable",
"contactHasTag": "Contact has tag",
"contactField": "Contact field",
"varName": "var name",
"tagLabel": "Tag",
"fieldLabel": "Field",
"pickTag": "Pick a tag…",
"pickField": "Pick a field…",
"operatorLabel": "Operator",
"isPresent": "is present",
"isAbsent": "is absent",
"equals": "equals",
"contains": "contains",
"valueLabel": "Value",
"ifTrueAdvance": "If true → advance to",
"ifFalseAdvance": "If false → advance to",
"actionLabel": "Action",
"addTag": "Add tag",
"removeTag": "Remove tag",
"tagUuidPlaceholder": "Tag UUID",
"thenAdvanceTo": "Then advance to",
"mediaTypeLabel": "Media type",
"imageLabel": "Image (PNG, JPEG, WebP)",
"videoLabel": "Video (MP4, 3GP)",
"documentLabel": "Document (PDF, Word, Excel, PowerPoint, TXT)",
"fileLabel": "File",
"removeFile": "Remove file",
"uploading": "Uploading…",
"clickToUpload": "Click to upload (max 16 MB)",
"captionLabel": "Caption (optional, shown under the media)",
"filenameLabel": "Filename shown to the customer (documents only)",
"filenamePlaceholder": "invoice.pdf",
"advanceAfterSending": "After sending, advance to",
"pickNextNode": "Pick a next node…",
"none": "— None —"
},
"categories": {
"messaging": "Messaging",
"logic": "Logic & data",
"flow": "Flow control"
},
"nodes": {
"start": {
"label": "Start",
"blurb": "Entry point of the flow"
},
"send_message": {
"label": "Send message",
"blurb": "Sends a WhatsApp text message"
},
"send_buttons": {
"label": "Send buttons",
"blurb": "Sends quick-reply buttons"
},
"send_list": {
"label": "Send list",
"blurb": "Sends a tappable list of options"
},
"send_media": {
"label": "Send media",
"blurb": "Sends an image, video, or document"
},
"collect_input": {
"label": "Collect input",
"blurb": "Asks a question, saves the reply"
},
"condition": {
"label": "If / else",
"blurb": "Branches on a rule"
},
"set_tag": {
"label": "Tag contact",
"blurb": "Adds or removes a contact tag"
},
"handoff": {
"label": "Handoff to agent",
"blurb": "Hands the conversation to a human"
},
"end": {
"label": "End",
"blurb": "Ends the flow"
}
}
},
"validation": {
"noIssues": "No issues. Ready to activate.",
"summary": "{errorCount} {errorCount, plural, =1 {error} other {errors}}, {warningCount} {warningCount, plural, =1 {warning} other {warnings}}",
"jumpToNode": "Jump to node {key}"
},
"editorState": {
"saved": "Saved.",
"fixIssues": "Fix the issues below before activating.",
"statusActivated": "Flow activated.",
"statusArchived": "Archived.",
"statusDraft": "Saved as draft."
},
"summary": {
"options": "{count} {count, plural, =1 {option} other {options}}",
"optionsAcrossSections": "{rowCount} {rowCount, plural, =1 {option} other {options}} across {sectionCount} {sectionCount, plural, =1 {section} other {sections}}",
"noFile": "{label} (no file uploaded)",
"media": "Media",
"image": "Image",
"video": "Video",
"document": "Document",
"audio": "Audio",
"hasTag": "has tag {tag}",
"opContains": "contains",
"opExists": "exists",
"opMissing": "missing",
"modeAdd": "Add",
"modeRemove": "Remove",
"tagPicked": "{mode} tag {tag}…",
"tagNone": "{mode} tag (none picked)"
}
},
"Settings": {
"pageTitle": "Settings",
"pageDesc": "Everything in one place — your account and your workspace. Pick a section to manage it.",
"overview": {
"notSetup": "Not set up yet",
"connected": "Connected",
"needsReconnecting": "Needs reconnecting",
"viewTeamMembers": "View team members",
"membersCount": "{count} {count, plural, =1 {member} other {members}}",
"pendingInvites": "{count} pending {count, plural, =1 {invite} other {invites}}",
"manageTemplates": "Manage message templates",
"templatesCount": "{count} {count, plural, =1 {template} other {templates}}",
"pendingReview": "{count} pending review",
"tagsAndFields": "Tags and custom fields",
"tagsCount": "{count} {count, plural, =1 {tag} other {tags}}",
"fieldsCount": "{count} custom {count, plural, =1 {field} other {fields}}",
"appearance": "{mode} mode · {theme} accent",
"yourAccount": "Your account",
"loading": "Loading…"
},
"members": {
"title": "Team members",
"description": "People with access to this account. Roles control what each teammate can do.",
"inviteMember": "Invite member",
"online": "online",
"away": "away",
"offline": "offline",
"memberCount": "{count} {count, plural, =1 {member} other {members}}",
"you": "You",
"unnamed": "Unnamed",
"joined": "Joined {date}",
"remove": "Remove",
"removedToast": "Removed {name}",
"updatedToast": "Updated {name} to {role}",
"pendingInvitations": "Pending invitations",
"inviteHint": "The plaintext invite URL is only shown once at creation for security — to re-share, revoke the invite below and create a new one.",
"noPendingTitle": "No pending invitations.",
"noPendingDesc": "Click <bold>Invite member</bold> above to generate a shareable link.",
"untitledInvite": "Untitled invite",
"created": "Created {date}",
"expiresInDays": "expires in {days} {days, plural, =1 {day} other {days}}",
"expiresInHours": "expires in {hours} {hours, plural, =1 {hour} other {hours}}",
"expired": "expired",
"revoke": "Revoke",
"revokedToast": "Invitation revoked",
"removeDialogTitle": "Remove member",
"removeDialogDesc": "Remove <bold>{name}</bold> from the account? They'll be signed out of this account and given a fresh personal account on their next sign-in. Their login isn't deleted.",
"cancel": "Cancel",
"removing": "Removing...",
"removeBtn": "Remove member"
},
"invite": {
"inviteCreated": "Invite created",
"inviteCreatedDesc": "Share this link with your new teammate. They'll be able to sign up (or sign in) and join the account as <bold>{role}</bold>. The link is valid for <bold>{days} {days, plural, =1 {day} other {days}}</bold>.",
"inviteLink": "Invite link",
"copy": "Copy",
"saveLinkNow": "Save this link now.",
"saveLinkHint": "We never store the plaintext — once you close this dialog the URL is gone. To re-share, revoke this invite and create a new one.",
"sendViaWhatsApp": "Send via WhatsApp",
"done": "Done",
"dialogTitle": "Invite a teammate",
"dialogDesc": "Generate a one-time invite link. Share it via WhatsApp, Slack, or any channel you like — no email service required.",
"roleLabel": "Role",
"validForLabel": "Link valid for",
"days1": "1 day",
"days7": "7 days",
"days30": "30 days",
"labelTitle": "Label",
"optional": "(optional)",
"labelPlaceholder": "e.g. Sara — support team",
"labelHint": "Helps you remember who you sent the link to in the pending list below.",
"cancel": "Cancel",
"creating": "Creating...",
"generateLink": "Generate link",
"labelTooLong": "Label must be {max} characters or fewer",
"copied": "Invite link copied",
"clipboardBlocked": "Clipboard blocked — copy the link manually",
"whatsappMessage": "Join {accountName} on wacrm using this link (valid for {expiresInDays} days): {url}"
},
"tagsAndFields": {
"title": "Fields & tags",
"description": "Two ways to organize contacts: colour-coded tags for quick grouping, and custom fields for structured data.",
"tagsTitle": "Tags",
"tagsDesc": "Colour-coded labels for grouping and filtering contacts.",
"fieldsTitle": "Custom fields",
"fieldsDesc": "Extra contact fields (e.g. ZIP code, lead source). They appear on every contact and in the “Update Contact Field” automation action.",
"adminRole": "Admin",
"failedToLoadTags": "Failed to load tags",
"nameRequired": "Tag name is required",
"notAuthenticated": "Not authenticated",
"tagCreated": "Tag created",
"failedToCreateTag": "Failed to create tag",
"tagDeleted": "Tag deleted",
"failedToDeleteTag": "Failed to delete tag",
"deleteAria": "Delete {name}",
"noTags": "No tags yet — create your first one below.",
"placeholder": "e.g. Newsletter",
"useColor": "Use {color}",
"addTag": "Add tag",
"deleteTag": "Delete tag",
"deleteConfirm": "Delete the tag \"{name}\"? This removes it from all contacts and cannot be undone.",
"cancel": "Cancel",
"deleting": "Deleting...",
"colors": {
"red": "Red",
"orange": "Orange",
"amber": "Amber",
"emerald": "Emerald",
"cyan": "Cyan",
"blue": "Blue",
"violet": "Violet",
"pink": "Pink"
}
},
"templates": {
"title": "Message templates",
"description": "Create templates and submit them to Meta for approval. Use \"Sync from Meta\" to pull templates approved elsewhere.",
"syncFromMeta": "Sync from Meta",
"syncing": "Syncing…",
"syncTitle": "Pull approved templates from your Meta WhatsApp Business Account",
"newTemplate": "New Template",
"noTemplates": "No templates yet.",
"createFirst": "Create your first message template to get started.",
"editTitle": "Editing triggers Meta re-review — status flips to PENDING.",
"editLabel": "Edit template",
"edit": "Edit",
"resubmitTitle": "Edit the template and resubmit to Meta for review.",
"resubmitLabel": "Edit and resubmit template",
"resubmit": "Resubmit",
"deleteMetaLocallyAria": "Delete template from Meta and locally",
"deleteLocallyAria": "Delete template locally",
"deleteMetaLocallyTitle": "Delete from Meta and locally",
"deleteLocallyTitle": "Delete locally",
"dialogEditTitle": "Edit Message Template",
"dialogNewTitle": "New Message Template",
"dialogEditDesc": "Save your changes to re-submit to Meta. Status will flip back to PENDING during review.",
"dialogNewDesc": "Build a template and submit it to Meta for approval. Once approved, you can use it in broadcasts and the inbox.",
"authWarning": "AUTHENTICATION templates have a fixed body + OTP button shape that needs a different builder. Create them in Meta WhatsApp Manager for now and use <bold>Sync from Meta</bold> to bring them in.",
"templateName": "Template Name",
"namePlaceholder": "e.g. order_confirmation",
"nameFixed": "Name is fixed once a template exists on Meta — create a new template to change it.",
"nameHint": "Lowercase letters, digits, and underscores only.",
"category": "Category",
"language": "Language",
"langFixed": "Language is fixed once a template exists on Meta.",
"langHint": "Must match the exact code on Meta — <code>en_US</code> and <code>en</code> are distinct.",
"header": "Header",
"headerNone": "None",
"headerText": "Text",
"headerImage": "Image",
"headerVideo": "Video",
"headerDocument": "Document",
"headerTextPlaceholder": "Header text (max 60 chars, optional {{1}})",
"headerSampleAria": "Sample value for header variable",
"headerSamplePlaceholder": "Sample value for {{1}} (required for Meta review)",
"uploadImage": "Upload image",
"uploadHint": "JPEG or PNG, ≤5 MB",
"mediaUrlPlaceholder": "https://… (or paste a public {format} link)",
"imageHint": "Upload a JPEG/PNG (≤5 MB, ≥800×418 px recommended) or paste a public HTTPS link — we upload it to Meta for review automatically.",
"mediaHint": "Must be a publicly accessible HTTPS link. Meta fetches it once during review, so it needs to stay live for ~24 hrs.",
"videoHint": " Recommended: MP4 / 3GPP, ≤16 MB, ≤60 seconds.",
"documentHint": " Recommended: PDF, ≤100 MB.",
"bodyText": "Body Text",
"bodyPlaceholder": "Hello {{1}}, your order {{2}} is confirmed.",
"bodyHint": "Use {{1}}, {{2}} for variables (must be contiguous starting at {{1}}).",
"sampleValues": "Sample values (Meta uses these to review your template)",
"sampleAria": "Sample value for body variable {var}",
"samplePlaceholder": "Sample for {var}",
"footer": "Footer (optional)",
"footerPlaceholder": "Optional footer text (max 60 chars)",
"buttons": "Buttons (optional)",
"addButton": "Add Button",
"buttonsLimit": "Up to {max} buttons. QUICK_REPLY buttons must come before URL / phone / copy-code buttons.",
"btnQuickReply": "Quick Reply",
"btnUrl": "URL",
"btnPhone": "Phone",
"btnCopyCode": "Copy Code",
"btnLabelPlaceholder": "Button label",
"urlPlaceholder": "https://example.com/path or with {{1}} suffix",
"urlSamplePlaceholder": "Example value for {{1}} (required when URL has a variable)",
"phonePlaceholder": "+15551234567",
"codePlaceholder": "Example code (e.g. SUMMER20)",
"cancel": "Cancel",
"saving": "Saving…",
"submitting": "Submitting…",
"saveResubmit": "Save & Resubmit",
"submitApproval": "Submit for Approval",
"deleteDialogTitle": "Delete template?",
"deleteMetaDesc": "\"{name}\" will be deleted from Meta and from wacrm. Active broadcasts using this template will start failing on their next send. This can't be undone.",
"deleteLocalDesc": "\"{name}\" will be deleted from wacrm. It was never submitted to Meta, so no remote cleanup is needed.",
"deleting": "Deleting…",
"delete": "Delete",
"toastLoadFailed": "Failed to load templates",
"toastSubmitFailed": "Failed to submit",
"toastSubmitEditSuccess": "Edit submitted — Meta typically reviews within 24 hours.",
"toastSubmitNewSuccess": "Submitted to Meta — typical review time is 24 hours. Status updates automatically.",
"toastSaveEditDry": "Template updated (dry-run — no Meta call)",
"toastSaveNewDry": "Template saved (dry-run — no Meta call)",
"toastSyncCount": "Synced {total} {total, plural, =1 {template} other {templates}} from Meta",
"toastSyncDetails": " ({inserted} new, {updated} updated)",
"toastSyncFailed": "Failed to sync: {preview}",
"toastSyncTruncated": "Synced the first 2000 templates only — your account has more. Sync again to continue, or contact support if this persists.",
"toastSyncError": "Failed to sync templates",
"toastDeleteSuccess": "Template deleted",
"toastDeleteError": "Failed to delete template",
"toastInvalidImage": "Header image must be a JPEG or PNG.",
"toastImageTooLarge": "Image is {size} MB — Meta's limit is 5 MB.",
"toastUploadSuccess": "Image uploaded.",
"toastUploadFailed": "Upload failed."
},
"roles": {
"admin": "Admin",
"adminHint": "Manage members + everything",
"agent": "Agent",
"agentHint": "Use features; no settings",
"viewer": "Viewer",
"viewerHint": "Read-only across the app",
"owner": "Owner",
"ownerHint": "Full control over account and billing"
},
"whatsapp": {
"title": "WhatsApp connection",
"description": "Connect your Meta WhatsApp Business API. Credentials, webhook, and setup steps all live here.",
"tokenCorrupted": "Stored token can't be decrypted",
"resetting": "Resetting...",
"resetConfig": "Reset Configuration",
"credentialsValid": "Credentials valid",
"notConnected": "Not Connected",
"connectedDesc": "Your access token authenticates with Meta. See Registration status below for whether webhooks are actually wired.",
"notConnectedDesc": "Configure your Meta API credentials below to connect your WhatsApp Business account.",
"registered": "Registered — Meta will deliver events to wacrm",
"notRegistered": "Not registered — Meta will not deliver events",
"verifyWithMeta": "Verify with Meta",
"subscribedSince": "Subscribed since {date}. Click Verify with Meta if events stop arriving.",
"unknownDate": "unknown",
"lastAttemptFailed": "Last attempt failed with: ",
"retryHint": "Enter (or correct) the 2-step PIN below and click Save Configuration to retry.",
"noRegistrationHint": "This number was saved before registration tracking existed, or registration was skipped. Enter the 2-step PIN below and click Save Configuration to subscribe it.",
"diagnosticLastRun": "Diagnostic — last run: ",
"live": "live",
"notLive": "not live",
"apiCredentialsTitle": "API Credentials",
"apiCredentialsDesc": "Enter your Meta WhatsApp Business API credentials.",
"phoneNumberId": "Phone Number ID",
"wabaId": "WhatsApp Business Account ID",
"accessToken": "Permanent Access Token",
"accessTokenPlaceholder": "Enter your access token",
"tokenHidden": "Token is hidden for security. Re-enter it to update configuration.",
"webhookVerifyToken": "Webhook Verify Token",
"webhookVerifyTokenPlaceholder": "Create a custom verify token",
"webhookVerifyTokenHint": "A custom string you create. Must match the token you set in Meta webhook settings.",
"twoStepPin": "Two-step verification PIN",
"optional": "(optional)",
"pinPlaceholder": "6-digit PIN from Meta WhatsApp Manager",
"pinHint": "Needed only to wire inbound messages for a production number. Set it in Meta Business Manager → WhatsApp Accounts → Phone Numbers → Two-step verification, then paste it here so wacrm can subscribe the number — otherwise Meta routes inbound events to whichever app last claimed it (the symptom that hits second numbers under a shared WABA). Meta test numbers have no PIN and are pre-registered — leave this blank for them. Leaving it blank also keeps an existing registration untouched.",
"webhookTitle": "Webhook Configuration",
"webhookDesc": "Use this URL as your webhook callback in the Meta App Dashboard.",
"webhookUrl": "Webhook Callback URL",
"saving": "Saving...",
"saveConfig": "Save Configuration",
"testing": "Testing...",
"testConnection": "Test API Connection",
"setupInstructions": "Setup Instructions",
"setupInstructionsDesc": "Follow these steps to connect your WhatsApp Business API.",
"step1": "Create a Meta App",
"step1_1": "Go to developers.facebook.com",
"step1_2": "Click \"My Apps\" and then \"Create App\"",
"step1_3": "Select \"Business\" as the app type",
"step1_4": "Fill in app details and create",
"step2": "Add WhatsApp Product",
"step2_1": "In your app dashboard, click \"Add Product\"",
"step2_2": "Find \"WhatsApp\" and click \"Set Up\"",
"step2_3": "Follow the setup wizard to link your business",
"step3": "Get API Credentials",
"step3_1": "Go to WhatsApp > API Setup",
"step3_2": "Copy your <strong class=\"text-foreground\">Phone Number ID</strong>",
"step3_3": "Copy your <strong class=\"text-foreground\">WhatsApp Business Account ID</strong>",
"step3_4": "Generate a <strong class=\"text-foreground\">Permanent Access Token</strong> from Business Settings > System Users",
"step4": "Configure Webhooks",
"step4_1": "Go to WhatsApp > Configuration",
"step4_2": "Click \"Edit\" on the Webhook section",
"step4_3": "Paste the <strong class=\"text-foreground\">Webhook Callback URL</strong> from above",
"step4_4": "Enter the same <strong class=\"text-foreground\">Verify Token</strong> you set here",
"step4_5": "Subscribe to \"messages\" webhook field",
"metaDocs": "Meta WhatsApp API Documentation"
},
"sections": {
"overview": "Overview",
"profile": "Your profile",
"security": "Login & security",
"appearance": "Appearance",
"whatsapp": "WhatsApp",
"templates": "Templates",
"fields": "Fields & tags",
"deals": "Deals & currency",
"members": "Team members",
"api": "API keys"
},
"groups": {
"account": "Account",
"workspace": "Workspace"
},
"profile": {
"title": "Your profile",
"description": "How you show up across the app. Your avatar and name appear in the header, sidebar, and anywhere your teammates see you.",
"changePhoto": "Change photo",
"uploadPhoto": "Upload photo",
"remove": "Remove",
"photoHint": "PNG, JPG, WebP, or GIF. Up to 2 MB.",
"displayName": "Display name",
"email": "Email",
"emailChangeHint": "Check the inbox for <bold>{oldEmail}</bold> and <bold>{newEmail}</bold> — both need to confirm before the change takes effect.",
"accountDetails": "Account details",
"role": "Role",
"joined": "Joined",
"userId": "User ID",
"loading": "Loading your profile…",
"saving": "Saving…",
"saveChanges": "Save changes",
"unsupportedImage": "Unsupported image type",
"unsupportedImageDesc": "Use PNG, JPG, WebP, or GIF.",
"imageTooLarge": "Image is too large",
"imageTooLargeDesc": "Maximum 2 MB.",
"nameRequired": "Display name is required",
"invalidEmail": "Enter a valid email address",
"uploadFailed": "Upload failed: {message}",
"saveFailed": "Save failed: {message}",
"profileSaved": "Profile saved",
"emailChangeFailed": "Email change failed: {message}",
"profileSavedEmailCheck": "Profile saved — check your email to confirm the address change",
"passwordTitle": "Password",
"passwordDesc": "Use at least {min} characters. You will stay signed in on this device after changing it.",
"currentPassword": "Current password",
"newPassword": "New password",
"confirmPassword": "Confirm new password",
"updating": "Updating…",
"updatePassword": "Update password",
"cannotChangeNoEmail": "Cannot change password without a current email",
"passwordTooShort": "Password must be at least {min} characters",
"passwordMismatch": "New password and confirmation do not match",
"currentPasswordIncorrect": "Current password is incorrect",
"passwordUpdateFailed": "Password update failed: {message}",
"passwordUpdated": "Password updated",
"sessionsTitle": "Active sessions",
"sessionsDesc": "Sign out of every device where you're logged in — including this one. Useful if you lost a laptop or shared your password.",
"signOutAll": "Sign out of all devices",
"signOutConfirmTitle": "Sign out everywhere?",
"signOutConfirmDesc": "Every device logged into this account will be signed out and will need to log in again. You will be redirected to the login page.",
"cancel": "Cancel",
"signingOut": "Signing out…",
"signOutEverywhere": "Sign out everywhere",
"signOutFailed": "Sign-out failed: {message}"
},
"appearance": {
"title": "Appearance",
"description": "Set the mode and accent colour used across the app. Saved to this device — try it, it changes live.",
"mode": "Mode",
"useMode": "Use {mode} mode",
"active": "Active",
"accentColor": "Accent color",
"useTheme": "Use {name} theme"
},
"security": {
"title": "Login & security",
"description": "Change your password and sign out of your devices. These keep your account safe."
},
"apiKeys": {
"title": "API keys",
"description": "Keys authenticate the public REST API (<apiCode>/api/v1</apiCode>) so you can build your own automations. Send them as <headerCode>Authorization: Bearer &lt;key&gt;</headerCode>.",
"newApiKey": "New API key",
"noApiKeys": "No API keys yet.",
"createOneHint": "Click <bold>New API key</bold> to create one.",
"askAdminHint": "Ask an admin to create one.",
"revoked": "Revoked",
"expired": "Expired",
"noScopes": "No scopes",
"created": "Created {date}",
"lastUsed": "last used {date}",
"neverUsed": "never used",
"expires": "expires {date}",
"revoke": "Revoke",
"revokeSuccess": "Revoked \"{name}\"",
"revokeFailed": "Failed to revoke key",
"loadFailed": "Failed to load API keys",
"createError": "Failed to create key",
"nameRequired": "Give the key a name",
"nameLabel": "Name",
"namePlaceholder": "e.g. Zapier automation",
"scopesLabel": "Scopes",
"scopesHint": "A key with no scopes can still call <code className=\"text-[11px]\">GET /api/v1/me</code> to verify it works.",
"cancel": "Cancel",
"creating": "Creating…",
"createKey": "Create key",
"copyTitle": "Copy your API key",
"copyDesc": "This is the only time the full key is shown. Store it somewhere safe — if you lose it, revoke it and create a new one.",
"apiKeyLabel": "API key",
"copy": "Copy",
"copySuccess": "API key copied",
"copyFailed": "Copy failed — select and copy manually",
"newKeyTitle": "New API key",
"newKeyDesc": "Name it after the integration that will use it, and grant only the scopes it needs.",
"done": "Done",
"networkError": "Could not reach the server"
},
"deals": {
"title": "Deals & currency",
"description": "The currency used for new deals and for pipeline and dashboard totals.",
"defaultCurrency": "Default currency",
"defaultCurrencyDesc": "New deals default to this currency, and pipeline and dashboard totals are shown in it. Existing deals keep the currency they were saved with.",
"currencyLabel": "Currency",
"adminOnlyHint": "Only account admins can change the default currency.",
"save": "Save",
"saving": "Saving...",
"saveFailed": "Failed to save default currency",
"saveSuccess": "Default currency updated"
},
"aiConfig": {
"title": "Agent setup",
"description": "Bring your own OpenAI or Anthropic key. wacrm calls the provider directly with your key — no per-seat AI fees, and your data stays yours. This powers AI-drafted replies in the inbox, the auto-reply bot, and the Playground.",
"adminOnlyConfig": "Only admins and owners can change the AI configuration.",
"loadFailed": "Failed to load AI configuration",
"providerAndKey": "Provider & key",
"encryptionNotice": "Your key is encrypted at rest (AES-256-GCM) and never shown again after saving.",
"provider": "Provider",
"model": "Model",
"apiKey": "API key",
"testKey": "Test key",
"testSuccess": "Key works — the provider responded.",
"testRejected": "The provider rejected the request.",
"testNetworkError": "Could not reach the provider.",
"embeddingsKey": "Embeddings key",
"optionalSemanticSearch": "(optional — enables semantic knowledge-base search)",
"embeddingsHint": "An OpenAI key used only to embed your knowledge base (text-embedding-3-small){sameKeyText}. Leave blank to use keyword search instead. Clear it to turn semantic search off.",
"sameKeyText": " — can be the same key as above",
"behaviour": "Behaviour",
"behaviourDesc": "Tell the assistant about your business — products, tone, what it may and may not promise. This context feeds both drafts and auto-replies.",
"businessContext": "Business context & instructions",
"promptPlaceholder": "e.g. We are Acme, a coffee-equipment store. Be warm and concise. Never quote prices or delivery dates — hand off to a human for those.",
"enableAssistant": "Enable AI assistant",
"enableAssistantDesc": "Master switch. Turns on the “Draft with AI” button in the inbox.",
"autoReply": "Auto-reply to inbound messages",
"autoReplyDesc": "The bot answers new inbound messages automatically (only when no flow handles them and no agent is assigned). Hands off to a human when it can’t help.",
"maxAutoReplies": "Max auto-replies per conversation",
"maxAutoRepliesDesc": "After this many bot replies in one thread, the bot goes quiet.",
"handoffTo": "Hand off to",
"handoffToDesc": "When the bot can’t help — or hits the reply cap — it pauses and routes the chat here, with a short note on what happened.",
"handoffQueue": "Unassigned queue (any agent can pick it up)",
"remove": "Remove",
"save": "Save",
"missingModel": "Enter a model name.",
"missingApiKey": "Enter your API key.",
"saveSuccess": "AI assistant saved.",
"saveFailed": "Failed to save.",
"removeSuccess": "AI configuration removed.",
"removeFailed": "Failed to remove."
},
"aiKnowledge": {
"loadFailed": "Failed to load knowledge base",
"openFailed": "Failed to open document",
"titleContentRequired": "Title and content are required.",
"saveSuccessNew": "Document added.",
"saveSuccessUpdate": "Document updated.",
"saveFailed": "Failed to save.",
"removeSuccess": "Document removed.",
"removeFailed": "Failed to remove.",
"reindexSuccess": "Reindexed {count} document(s).",
"reindexFailed": "Reindex failed.",
"title": "Knowledge base",
"description": "Add FAQs, policies, or product details. The assistant retrieves the relevant pieces when drafting and auto-replying, so it can answer instead of handing off.{searchType}",
"semanticSearchOn": " Semantic search is on (embeddings key set).",
"keywordSearchOn": " Using keyword search — add an embeddings key above for semantic search.",
"noDocs": "No documents yet.",
"editDocTitle": "Title",
"editDocTitlePlaceholder": "e.g. Returns & refunds policy",
"editDocContent": "Content",
"editDocContentPlaceholder": "Paste the FAQ answer, policy text, or product details…",
"cancel": "Cancel",
"saveDoc": "Save document",
"addDoc": "Add document",
"reindex": "Reindex",
"reindexTooltip": "Re-embed all documents (e.g. after adding an embeddings key)",
"loading": "Loading…"
}
}
}