Compare commits
246
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
30e4585af2 | ||
|
|
3dba5f55a3 | ||
|
|
42d14ae4d0 | ||
|
|
2ed7b53ca3 | ||
|
|
e82ba57464 | ||
|
|
adc260ab27 | ||
|
|
ad65bace3b | ||
|
|
1b39d151f4 | ||
|
|
8e1fa6e03d | ||
|
|
ba0b526009 | ||
|
|
0fa5a75302 | ||
|
|
7c8bd0ca91 | ||
|
|
c05cd06d96 | ||
|
|
07438acad5 | ||
|
|
5e591b9ceb | ||
|
|
260d16afe1 | ||
|
|
d22c1fc4b7 | ||
|
|
1f69cfbb5c | ||
|
|
8badb131b8 | ||
|
|
0e4c352571 | ||
|
|
bb4a7cf2f2 | ||
|
|
04e667b0a4 | ||
|
|
b1c97d4846 | ||
|
|
88ac480da9 | ||
|
|
7694ca0cc5 | ||
|
|
7ea9c402a1 | ||
|
|
8df10a9397 | ||
|
|
65ba5c2eae | ||
|
|
d999e955d6 | ||
|
|
6143d9dea7 | ||
|
|
854c9bcf55 | ||
|
|
19702eda8b | ||
|
|
75a51786ec | ||
|
|
3d7cfd4c85 | ||
|
|
a3bf14955c | ||
|
|
1980fdf278 | ||
|
|
0ac2be508d | ||
|
|
2fe2d04208 | ||
|
|
ccc0357fee | ||
|
|
b934297a99 | ||
|
|
63bec71927 | ||
|
|
902d268ba7 | ||
|
|
c610e288cb | ||
|
|
5cb7978997 | ||
|
|
c87cfbafbc | ||
|
|
f6e6c027cd | ||
|
|
21bc4167fa | ||
|
|
abffd343f1 | ||
|
|
b362ee3e3c | ||
|
|
b95e1b94a7 | ||
|
|
be7469f342 | ||
|
|
602af113a1 | ||
|
|
313222c52a | ||
|
|
1e80e88bb7 | ||
|
|
d3c4e95edf | ||
|
|
429fba6ddd | ||
|
|
a9d80705cb | ||
|
|
01ad16dab8 | ||
|
|
7877d6a080 | ||
|
|
734b1f5a3d | ||
|
|
38cc568bb0 | ||
|
|
ed8c24c043 | ||
|
|
9855995480 | ||
|
|
1c6e64b2f0 | ||
|
|
f8ad752c86 | ||
|
|
ef31d99431 | ||
|
|
44fc22ab5c | ||
|
|
3f05d8972e | ||
|
|
25a731eb6c | ||
|
|
063460b198 | ||
|
|
670545b317 | ||
|
|
3da8ae601d | ||
|
|
919154e26c | ||
|
|
8859cdff07 | ||
|
|
e8e4d04073 | ||
|
|
f2dd1e7806 | ||
|
|
597158c221 | ||
|
|
b276344f0c | ||
|
|
e9a882e7bc | ||
|
|
f6f757c358 | ||
|
|
92723b0d23 | ||
|
|
e1b1e1f286 | ||
|
|
6b2a4d70e8 | ||
|
|
a9767234f9 | ||
|
|
88cb9c22f0 | ||
|
|
913547c3b4 | ||
|
|
714ba2d823 | ||
|
|
a08c286871 | ||
|
|
436a63f584 | ||
|
|
e87ccfac50 | ||
|
|
c5044fce96 | ||
|
|
07e0b35529 | ||
|
|
a614a8e327 | ||
|
|
d96b79855c | ||
|
|
194bb7b317 | ||
|
|
3ed140ef80 | ||
|
|
06f2db89af | ||
|
|
582a984ea3 | ||
|
|
ced1532a31 | ||
|
|
8b7a0528aa | ||
|
|
c86a9e1236 | ||
|
|
8292d611cd | ||
|
|
7d09ab7086 | ||
|
|
956ed80cbd | ||
|
|
5a3d38b854 | ||
|
|
088f48f401 | ||
|
|
37bbde5600 | ||
|
|
d31e72254f | ||
|
|
558b1c9787 | ||
|
|
293bdbdb2e | ||
|
|
aca0e1ae6d | ||
|
|
bdc6d15e62 | ||
|
|
a640c127b2 | ||
|
|
3ee3dd0df6 | ||
|
|
75260980c5 | ||
|
|
7d46f51669 | ||
|
|
f28e1bd016 | ||
|
|
619edb5eb0 | ||
|
|
153b5675ca | ||
|
|
60c623121d | ||
|
|
eb49cbec51 | ||
|
|
e80510632c | ||
|
|
5f66f4b0a7 | ||
|
|
0fa18cddda | ||
|
|
fd6ad2b1e1 | ||
|
|
04d5279411 | ||
|
|
f7058a8af4 | ||
|
|
eecfd8132b | ||
|
|
70ed790098 | ||
|
|
6c3e667fac | ||
|
|
7494baef3c | ||
|
|
f2034c752f | ||
|
|
2d489c1c0a | ||
|
|
53ede69a73 | ||
|
|
c2e2b320ef | ||
|
|
cb05a318a7 | ||
|
|
fd9beedb68 | ||
|
|
e2061fb624 | ||
|
|
ce5803d312 | ||
|
|
cba2647b5e | ||
|
|
c3e438b365 | ||
|
|
a48fea0a7f | ||
|
|
80daea6054 | ||
|
|
cd93459320 | ||
|
|
9801137296 | ||
|
|
d4890a6e52 | ||
|
|
79c8df7c4f | ||
|
|
e204ed95ce | ||
|
|
34a3da97b2 | ||
|
|
5da34f916e | ||
|
|
4a31dd2120 | ||
|
|
6ca32faeab | ||
|
|
186d8c9462 | ||
|
|
50a46d123a | ||
|
|
ec5dee5c46 | ||
|
|
93cc1f339a | ||
|
|
3468ca48c3 | ||
|
|
47e2e4371f | ||
|
|
9f3d2d545b | ||
|
|
4e04fd3d08 | ||
|
|
e064899267 | ||
|
|
3594ce9bb5 | ||
|
|
03777d7cc1 | ||
|
|
e3e238bce8 | ||
|
|
e0e8a70720 | ||
|
|
e2d1ce7554 | ||
|
|
1dd5eb64cf | ||
|
|
0c6d2cc0b7 | ||
|
|
b6ff4f54be | ||
|
|
07c92c6097 | ||
|
|
cafa486c18 | ||
|
|
baea194f6b | ||
|
|
583ba7c2a0 | ||
|
|
1a54cc4990 | ||
|
|
60099a4d5b | ||
|
|
dcb2ab7c99 | ||
|
|
aa5735df39 | ||
|
|
81bd22d0dd | ||
|
|
f8ff3b8d74 | ||
|
|
20ccca72ae | ||
|
|
60fafc65ca | ||
|
|
f53ae6457c | ||
|
|
2bb9886c6d | ||
|
|
9b9875b847 | ||
|
|
7e5f19e2b0 | ||
|
|
0d73760dac | ||
|
|
8527170d82 | ||
|
|
044282730f | ||
|
|
d5bf7f81cb | ||
|
|
8b125efac2 | ||
|
|
69ea2dc58d | ||
|
|
5cc2004a71 | ||
|
|
92ac1c3825 | ||
|
|
bc8f528f90 | ||
|
|
bef1a6047a | ||
|
|
ab4eb0e7d7 | ||
|
|
e5b64287ac | ||
|
|
34a480e799 | ||
|
|
58b067c08f | ||
|
|
78faf125bd | ||
|
|
0d9662477d | ||
|
|
3e31f63f09 | ||
|
|
b84d98b90b | ||
|
|
3a75ec02f0 | ||
|
|
9cbe749994 | ||
|
|
e9ea5cbe45 | ||
|
|
665549d52d | ||
|
|
17db86c2fe | ||
|
|
0ace7f38a6 | ||
|
|
4a2250065c | ||
|
|
31ba25b12b | ||
|
|
bc5bd51003 | ||
|
|
6d933112e7 | ||
|
|
45d7e570b7 | ||
|
|
bb3ad70f0e | ||
|
|
c983c87537 | ||
|
|
c5def0522f | ||
|
|
b95e59ee0d | ||
|
|
f49d825e8b | ||
|
|
75a82d9ab7 | ||
|
|
0eb121df75 | ||
|
|
18e45a0da6 | ||
|
|
df14e97ff6 | ||
|
|
63c9cf3ad1 | ||
|
|
ec36442f57 | ||
|
|
b81c43a5a2 | ||
|
|
23cad33d96 | ||
|
|
36a05097fa | ||
|
|
0a181b0cea | ||
|
|
87df43bdd0 | ||
|
|
28f87d2fca | ||
|
|
37eed5de1e | ||
|
|
170b64d1f1 | ||
|
|
95cb3a7ad8 | ||
|
|
ecd9c26c8c | ||
|
|
88e2661ca6 | ||
|
|
33dea83716 | ||
|
|
4c6a345d60 | ||
|
|
04ac9d3780 | ||
|
|
1f6203d558 | ||
|
|
3eed8905cc | ||
|
|
f27bbef73b | ||
|
|
ed3059c1aa | ||
|
|
9d591b8f46 | ||
|
|
1afcd36dee | ||
|
|
a3ffb48a47 |
+5
-3
@@ -996,11 +996,13 @@ GEM
|
||||
activemodel (>= 3.2)
|
||||
mail (~> 2.5)
|
||||
version_gem (1.1.4)
|
||||
vite_rails (3.0.17)
|
||||
railties (>= 5.1, < 8)
|
||||
vite_rails (3.10.0)
|
||||
railties (>= 5.1, < 9)
|
||||
vite_ruby (~> 3.0, >= 3.2.2)
|
||||
vite_ruby (3.8.0)
|
||||
vite_ruby (3.10.2)
|
||||
dry-cli (>= 0.7, < 2)
|
||||
logger (~> 1.6)
|
||||
mutex_m
|
||||
rack-proxy (~> 0.6, >= 0.6.1)
|
||||
zeitwerk (~> 2.2)
|
||||
warden (1.2.9)
|
||||
|
||||
@@ -92,10 +92,18 @@ class Messages::Facebook::MessageBuilder < Messages::Messenger::MessageBuilder
|
||||
def fallback_params(attachment)
|
||||
{
|
||||
fallback_title: attachment['title'],
|
||||
external_url: attachment['url']
|
||||
external_url: attachment['url'] || attachment.dig('payload', 'url')
|
||||
}
|
||||
end
|
||||
|
||||
# Facebook shared posts point to page URLs, not downloadable media URLs.
|
||||
# Keep this Facebook-only so Messenger/Instagram share attachments still use the parent media handling.
|
||||
def normalize_file_type(type)
|
||||
return :fallback if type.to_sym == :share
|
||||
|
||||
super
|
||||
end
|
||||
|
||||
def conversation_params
|
||||
{
|
||||
account_id: @inbox.account_id,
|
||||
|
||||
@@ -13,6 +13,7 @@ class Messages::MessageBuilder
|
||||
@account = conversation.account
|
||||
@message_type = params[:message_type] || 'outgoing'
|
||||
@attachments = params[:attachments]
|
||||
@is_voice_message = ActiveModel::Type::Boolean.new.cast(params[:is_voice_message])
|
||||
@automation_rule = content_attributes&.dig(:automation_rule_id)
|
||||
return unless params.instance_of?(ActionController::Parameters)
|
||||
|
||||
@@ -56,16 +57,25 @@ class Messages::MessageBuilder
|
||||
file: uploaded_attachment
|
||||
)
|
||||
|
||||
attachment.file_type = if uploaded_attachment.is_a?(String)
|
||||
file_type_by_signed_id(
|
||||
uploaded_attachment
|
||||
)
|
||||
else
|
||||
file_type(uploaded_attachment&.content_type)
|
||||
end
|
||||
attachment.file_type = attachment_file_type(uploaded_attachment)
|
||||
tag_voice_message(attachment)
|
||||
end
|
||||
end
|
||||
|
||||
def attachment_file_type(uploaded_attachment)
|
||||
if uploaded_attachment.is_a?(String)
|
||||
file_type_by_signed_id(uploaded_attachment)
|
||||
else
|
||||
file_type(uploaded_attachment&.content_type)
|
||||
end
|
||||
end
|
||||
|
||||
def tag_voice_message(attachment)
|
||||
return unless @is_voice_message && attachment.file_type == 'audio'
|
||||
|
||||
attachment.meta = (attachment.meta || {}).merge('is_voice_message' => true)
|
||||
end
|
||||
|
||||
def process_emails
|
||||
return unless @conversation.inbox&.inbox_type == 'Email'
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ class Api::V1::Accounts::Instagram::AuthorizationsController < Api::V1::Accounts
|
||||
enable_fb_login: '0',
|
||||
force_authentication: '1',
|
||||
response_type: 'code',
|
||||
state: generate_instagram_token(Current.account.id)
|
||||
state: generate_instagram_token(Current.account.id, params[:return_to])
|
||||
}
|
||||
)
|
||||
if redirect_url
|
||||
|
||||
@@ -8,7 +8,15 @@ class Api::V1::Accounts::OauthAuthorizationController < Api::V1::Accounts::BaseC
|
||||
end
|
||||
|
||||
def state
|
||||
Current.account.to_sgid(expires_in: 15.minutes).to_s
|
||||
# The sgid purpose doubles as a return hint: onboarding tags it so the callback
|
||||
# can route the user back to inbox setup. The purpose is part of the signed
|
||||
# payload (tamper-proof), and a non-onboarding request keeps the default
|
||||
# purpose, leaving callers like Notion byte-identical.
|
||||
Current.account.to_sgid(expires_in: 15.minutes, for: state_purpose).to_s
|
||||
end
|
||||
|
||||
def state_purpose
|
||||
params[:return_to] == 'onboarding' ? 'onboarding' : 'default'
|
||||
end
|
||||
|
||||
def base_url
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
class Api::V1::Accounts::OnboardingsController < Api::V1::Accounts::BaseController
|
||||
before_action :check_admin_authorization?
|
||||
|
||||
def update
|
||||
@account = Current.account
|
||||
finalize = finalizing_account_details?
|
||||
|
||||
@account.assign_attributes(account_params)
|
||||
@account.custom_attributes.merge!(custom_attributes_params)
|
||||
@account.custom_attributes.delete('onboarding_step') if finalize
|
||||
@account.save!
|
||||
|
||||
# TODO: re-enable when the help center generation UI is ready to surface progress
|
||||
# Onboarding::HelpCenterCreationService.new(@account, Current.user).perform if finalize && website.present?
|
||||
|
||||
render 'api/v1/accounts/update', format: :json
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def finalizing_account_details?
|
||||
@account.custom_attributes['onboarding_step'] == 'account_details'
|
||||
end
|
||||
|
||||
def website
|
||||
custom_attributes_params[:website]
|
||||
end
|
||||
|
||||
def account_params
|
||||
params.permit(:name, :locale)
|
||||
end
|
||||
|
||||
def custom_attributes_params
|
||||
params.permit(:industry, :company_size, :timezone, :referral_source, :user_role, :website)
|
||||
end
|
||||
end
|
||||
@@ -3,7 +3,7 @@ class Api::V1::Accounts::Tiktok::AuthorizationsController < Api::V1::Accounts::O
|
||||
|
||||
def create
|
||||
redirect_url = Tiktok::AuthClient.authorize_url(
|
||||
state: generate_tiktok_token(Current.account.id)
|
||||
state: generate_tiktok_token(Current.account.id, params[:return_to])
|
||||
)
|
||||
|
||||
if redirect_url
|
||||
|
||||
@@ -58,7 +58,6 @@ class Api::V1::AccountsController < Api::BaseController
|
||||
@account.assign_attributes(account_params.slice(:name, :locale, :domain, :support_email))
|
||||
@account.custom_attributes.merge!(custom_attributes_params)
|
||||
@account.settings.merge!(settings_params)
|
||||
@account.custom_attributes.delete('onboarding_step') if @account.custom_attributes['onboarding_step'] == 'account_details'
|
||||
@account.custom_attributes['onboarding_step'] = 'invite_team' if @account.custom_attributes['onboarding_step'] == 'account_update'
|
||||
@account.save!
|
||||
end
|
||||
|
||||
@@ -28,6 +28,8 @@ class Instagram::CallbacksController < ApplicationController
|
||||
@long_lived_token_response = exchange_for_long_lived_token(@response.token)
|
||||
inbox, already_exists = find_or_create_inbox
|
||||
|
||||
return redirect_to app_onboarding_inbox_setup_url(account_id: account_id) if return_to == 'onboarding'
|
||||
|
||||
if already_exists
|
||||
redirect_to app_instagram_inbox_settings_url(account_id: account_id, inbox_id: inbox.id)
|
||||
else
|
||||
@@ -149,6 +151,10 @@ class Instagram::CallbacksController < ApplicationController
|
||||
verify_instagram_token(params[:state])
|
||||
end
|
||||
|
||||
def return_to
|
||||
instagram_token_return_to(params[:state])
|
||||
end
|
||||
|
||||
def oauth_code
|
||||
params[:code]
|
||||
end
|
||||
|
||||
@@ -14,4 +14,11 @@ class Microsoft::CallbacksController < OauthCallbackController
|
||||
def imap_address
|
||||
'outlook.office365.com'
|
||||
end
|
||||
|
||||
# Exchange Online's SMTP AUTH (XOAUTH2) rejects proxy addresses in the SASL `user=` field;
|
||||
# it must match the token's UPN. `preferred_username` is the documented v2.0 claim;
|
||||
# `upn` is the v1.0 fallback.
|
||||
def imap_login_identity
|
||||
users_data['preferred_username'] || users_data['upn'] || super
|
||||
end
|
||||
end
|
||||
|
||||
@@ -16,6 +16,8 @@ class OauthCallbackController < ApplicationController
|
||||
def handle_response
|
||||
inbox, already_exists = find_or_create_inbox
|
||||
|
||||
return redirect_to app_onboarding_inbox_setup_url(account_id: account.id) if return_to == 'onboarding'
|
||||
|
||||
if already_exists
|
||||
redirect_to app_email_inbox_settings_url(account_id: account.id, inbox_id: inbox.id)
|
||||
else
|
||||
@@ -44,7 +46,7 @@ class OauthCallbackController < ApplicationController
|
||||
|
||||
def update_channel(channel_email)
|
||||
channel_email.update!({
|
||||
imap_login: users_data['email'], imap_address: imap_address,
|
||||
imap_login: imap_login_identity, imap_address: imap_address,
|
||||
imap_port: '993', imap_enabled: true,
|
||||
provider: provider_name,
|
||||
provider_config: {
|
||||
@@ -55,6 +57,13 @@ class OauthCallbackController < ApplicationController
|
||||
})
|
||||
end
|
||||
|
||||
# Identity used as the IMAP/SMTP login (SASL XOAUTH2 `user=` field). Defaults to the
|
||||
# id_token's email claim; providers override when their server requires a different
|
||||
# claim (e.g. Microsoft SMTP requires UPN).
|
||||
def imap_login_identity
|
||||
users_data['email']
|
||||
end
|
||||
|
||||
def provider_name
|
||||
raise NotImplementedError
|
||||
end
|
||||
@@ -81,10 +90,19 @@ class OauthCallbackController < ApplicationController
|
||||
decoded_token[0]
|
||||
end
|
||||
|
||||
# The sgid purpose carries the onboarding return hint (see
|
||||
# OauthAuthorizationController#state). Try the onboarding purpose first — a match
|
||||
# both resolves the account and records the return target — then fall back to the
|
||||
# default purpose used by every other caller.
|
||||
def account_from_signed_id
|
||||
raise ActionController::BadRequest, 'Missing state variable' if params[:state].blank?
|
||||
|
||||
account = GlobalID::Locator.locate_signed(params[:state])
|
||||
if (account = GlobalID::Locator.locate_signed(params[:state], for: 'onboarding'))
|
||||
@return_to = 'onboarding'
|
||||
else
|
||||
account = GlobalID::Locator.locate_signed(params[:state])
|
||||
end
|
||||
|
||||
raise 'Invalid or expired state' if account.nil?
|
||||
|
||||
account
|
||||
@@ -94,6 +112,11 @@ class OauthCallbackController < ApplicationController
|
||||
@account ||= account_from_signed_id
|
||||
end
|
||||
|
||||
def return_to
|
||||
account # resolving the sgid records which purpose matched
|
||||
@return_to
|
||||
end
|
||||
|
||||
# Fallback name, for when name field is missing from users_data
|
||||
def fallback_name
|
||||
users_data['email'].split('@').first.parameterize.titleize
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
class Public::Api::V1::Portals::SearchController < Public::Api::V1::Portals::BaseController
|
||||
before_action :ensure_custom_domain_request, only: [:index]
|
||||
before_action :portal
|
||||
before_action :set_portal_layout
|
||||
before_action :set_view_variant
|
||||
before_action :ensure_portal_feature_enabled
|
||||
layout 'portal'
|
||||
|
||||
def index
|
||||
@query = params[:query].to_s.strip
|
||||
@articles = @portal.articles.published.includes(:category).where(locale: params[:locale])
|
||||
|
||||
search_articles
|
||||
|
||||
@articles = @articles.page(params[:page]).per(10)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def search_articles
|
||||
@articles = @query.present? ? @articles.search(search_params) : @articles.none
|
||||
end
|
||||
|
||||
def search_params
|
||||
params.permit(:query, :locale, :sort, :status, :page).tap do |permitted|
|
||||
permitted[:query] = @query
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Public::Api::V1::Portals::SearchController.prepend_mod_with('Public::Api::V1::Portals::SearchController')
|
||||
@@ -20,6 +20,8 @@ class Tiktok::CallbacksController < ApplicationController
|
||||
def process_successful_authorization
|
||||
inbox, already_exists = find_or_create_inbox
|
||||
|
||||
return redirect_to app_onboarding_inbox_setup_url(account_id: account_id) if return_to == 'onboarding'
|
||||
|
||||
if already_exists
|
||||
redirect_to app_tiktok_inbox_settings_url(account_id: account_id, inbox_id: inbox.id)
|
||||
else
|
||||
@@ -127,6 +129,10 @@ class Tiktok::CallbacksController < ApplicationController
|
||||
@account_id ||= verify_tiktok_token(params[:state])
|
||||
end
|
||||
|
||||
def return_to
|
||||
tiktok_token_return_to(params[:state])
|
||||
end
|
||||
|
||||
def account
|
||||
@account ||= Account.find(account_id)
|
||||
end
|
||||
|
||||
@@ -4,21 +4,21 @@ module Instagram::IntegrationHelper
|
||||
# Generates a signed JWT token for Instagram integration
|
||||
#
|
||||
# @param account_id [Integer] The account ID to encode in the token
|
||||
# @param return_to [String, nil] Optional onboarding return hint
|
||||
# @return [String, nil] The encoded JWT token or nil if client secret is missing
|
||||
def generate_instagram_token(account_id)
|
||||
def generate_instagram_token(account_id, return_to = nil)
|
||||
return if client_secret.blank?
|
||||
|
||||
JWT.encode(token_payload(account_id), client_secret, 'HS256')
|
||||
JWT.encode(token_payload(account_id, return_to), client_secret, 'HS256')
|
||||
rescue StandardError => e
|
||||
Rails.logger.error("Failed to generate Instagram token: #{e.message}")
|
||||
nil
|
||||
end
|
||||
|
||||
def token_payload(account_id)
|
||||
{
|
||||
sub: account_id,
|
||||
iat: Time.current.to_i
|
||||
}
|
||||
def token_payload(account_id, return_to = nil)
|
||||
payload = { sub: account_id, iat: Time.current.to_i }
|
||||
payload[:return_to] = return_to if return_to.present?
|
||||
payload
|
||||
end
|
||||
|
||||
# Verifies and decodes a Instagram JWT token
|
||||
@@ -28,7 +28,14 @@ module Instagram::IntegrationHelper
|
||||
def verify_instagram_token(token)
|
||||
return if token.blank? || client_secret.blank?
|
||||
|
||||
decode_token(token, client_secret)
|
||||
decode_token(token, client_secret)&.dig('sub')
|
||||
end
|
||||
|
||||
# Reads the onboarding return hint from a Instagram JWT token, if present.
|
||||
def instagram_token_return_to(token)
|
||||
return if token.blank? || client_secret.blank?
|
||||
|
||||
decode_token(token, client_secret)&.dig('return_to')
|
||||
end
|
||||
|
||||
private
|
||||
@@ -41,7 +48,7 @@ module Instagram::IntegrationHelper
|
||||
JWT.decode(token, secret, true, {
|
||||
algorithm: 'HS256',
|
||||
verify_expiration: true
|
||||
}).first['sub']
|
||||
}).first
|
||||
rescue StandardError => e
|
||||
Rails.logger.error("Unexpected error verifying Instagram token: #{e.message}")
|
||||
nil
|
||||
|
||||
@@ -45,9 +45,16 @@ module PortalHelper
|
||||
theme.present? && theme != 'system' ? "?theme=#{theme}" : ''
|
||||
end
|
||||
|
||||
def portal_query_string(theme, is_plain_layout_enabled)
|
||||
query_params = {}
|
||||
query_params[:theme] = theme if theme.present? && theme != 'system'
|
||||
query_params[:show_plain_layout] = true if is_plain_layout_enabled
|
||||
query_params.present? ? "?#{query_params.to_query}" : ''
|
||||
end
|
||||
|
||||
def generate_home_link(portal_slug, portal_locale, theme, is_plain_layout_enabled)
|
||||
if is_plain_layout_enabled
|
||||
"/hc/#{portal_slug}/#{portal_locale}#{theme_query_string(theme)}"
|
||||
"/hc/#{portal_slug}/#{portal_locale}#{portal_query_string(theme, is_plain_layout_enabled)}"
|
||||
else
|
||||
"/hc/#{portal_slug}/#{portal_locale}"
|
||||
end
|
||||
@@ -61,7 +68,7 @@ module PortalHelper
|
||||
is_plain_layout_enabled = params[:is_plain_layout_enabled]
|
||||
|
||||
if is_plain_layout_enabled
|
||||
"/hc/#{portal_slug}/#{category_locale}/categories/#{category_slug}#{theme_query_string(theme)}"
|
||||
"/hc/#{portal_slug}/#{category_locale}/categories/#{category_slug}#{portal_query_string(theme, is_plain_layout_enabled)}"
|
||||
else
|
||||
"/hc/#{portal_slug}/#{category_locale}/categories/#{category_slug}"
|
||||
end
|
||||
@@ -69,7 +76,7 @@ module PortalHelper
|
||||
|
||||
def generate_article_link(portal_slug, article_slug, theme, is_plain_layout_enabled)
|
||||
if is_plain_layout_enabled
|
||||
"/hc/#{portal_slug}/articles/#{article_slug}#{theme_query_string(theme)}"
|
||||
"/hc/#{portal_slug}/articles/#{article_slug}#{portal_query_string(theme, is_plain_layout_enabled)}"
|
||||
else
|
||||
"/hc/#{portal_slug}/articles/#{article_slug}"
|
||||
end
|
||||
|
||||
@@ -2,11 +2,12 @@ module Tiktok::IntegrationHelper
|
||||
# Generates a signed JWT token for Tiktok integration
|
||||
#
|
||||
# @param account_id [Integer] The account ID to encode in the token
|
||||
# @param return_to [String, nil] Optional onboarding return hint
|
||||
# @return [String, nil] The encoded JWT token or nil if client secret is missing
|
||||
def generate_tiktok_token(account_id)
|
||||
def generate_tiktok_token(account_id, return_to = nil)
|
||||
return if client_secret.blank?
|
||||
|
||||
JWT.encode(token_payload(account_id), client_secret, 'HS256')
|
||||
JWT.encode(token_payload(account_id, return_to), client_secret, 'HS256')
|
||||
rescue StandardError => e
|
||||
Rails.logger.error("Failed to generate TikTok token: #{e.message}")
|
||||
nil
|
||||
@@ -19,7 +20,14 @@ module Tiktok::IntegrationHelper
|
||||
def verify_tiktok_token(token)
|
||||
return if token.blank? || client_secret.blank?
|
||||
|
||||
decode_token(token, client_secret)
|
||||
decode_token(token, client_secret)&.dig('sub')
|
||||
end
|
||||
|
||||
# Reads the onboarding return hint from a Tiktok JWT token, if present.
|
||||
def tiktok_token_return_to(token)
|
||||
return if token.blank? || client_secret.blank?
|
||||
|
||||
decode_token(token, client_secret)&.dig('return_to')
|
||||
end
|
||||
|
||||
private
|
||||
@@ -28,18 +36,17 @@ module Tiktok::IntegrationHelper
|
||||
@client_secret ||= GlobalConfigService.load('TIKTOK_APP_SECRET', nil)
|
||||
end
|
||||
|
||||
def token_payload(account_id)
|
||||
{
|
||||
sub: account_id,
|
||||
iat: Time.current.to_i
|
||||
}
|
||||
def token_payload(account_id, return_to = nil)
|
||||
payload = { sub: account_id, iat: Time.current.to_i }
|
||||
payload[:return_to] = return_to if return_to.present?
|
||||
payload
|
||||
end
|
||||
|
||||
def decode_token(token, secret)
|
||||
JWT.decode(token, secret, true, {
|
||||
algorithm: 'HS256',
|
||||
verify_expiration: true
|
||||
}).first['sub']
|
||||
}).first
|
||||
rescue StandardError => e
|
||||
Rails.logger.error("Unexpected error verifying Tiktok token: #{e.message}")
|
||||
nil
|
||||
|
||||
@@ -12,6 +12,7 @@ export const buildCreatePayload = ({
|
||||
bccEmails = '',
|
||||
toEmails = '',
|
||||
templateParams,
|
||||
isVoiceMessage = false,
|
||||
}) => {
|
||||
let payload;
|
||||
if (files && files.length !== 0) {
|
||||
@@ -33,6 +34,9 @@ export const buildCreatePayload = ({
|
||||
if (contentAttributes) {
|
||||
payload.append('content_attributes', JSON.stringify(contentAttributes));
|
||||
}
|
||||
if (isVoiceMessage) {
|
||||
payload.append('is_voice_message', true);
|
||||
}
|
||||
} else {
|
||||
payload = {
|
||||
content: message,
|
||||
@@ -64,6 +68,7 @@ class MessageApi extends ApiClient {
|
||||
bccEmails = '',
|
||||
toEmails = '',
|
||||
templateParams,
|
||||
isVoiceMessage = false,
|
||||
}) {
|
||||
return axios({
|
||||
method: 'post',
|
||||
@@ -78,6 +83,7 @@ class MessageApi extends ApiClient {
|
||||
bccEmails,
|
||||
toEmails,
|
||||
templateParams,
|
||||
isVoiceMessage,
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
/* global axios */
|
||||
import ApiClient from './ApiClient';
|
||||
|
||||
class OnboardingAPI extends ApiClient {
|
||||
constructor() {
|
||||
super('onboarding', { accountScoped: true });
|
||||
}
|
||||
|
||||
update(data) {
|
||||
return axios.patch(this.url, data);
|
||||
}
|
||||
}
|
||||
|
||||
export default new OnboardingAPI();
|
||||
@@ -83,5 +83,29 @@ describe('#ConversationAPI', () => {
|
||||
template_params: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
it('appends is_voice_message when isVoiceMessage is true', () => {
|
||||
const formPayload = buildCreatePayload({
|
||||
message: 'voice message',
|
||||
echoId: 42,
|
||||
isPrivate: false,
|
||||
files: [new Blob(['audio-data'], { type: 'audio/ogg' })],
|
||||
isVoiceMessage: true,
|
||||
});
|
||||
expect(formPayload).toBeInstanceOf(FormData);
|
||||
expect(formPayload.get('is_voice_message')).toEqual('true');
|
||||
});
|
||||
|
||||
it('does not append is_voice_message when isVoiceMessage is false', () => {
|
||||
const formPayload = buildCreatePayload({
|
||||
message: 'regular audio',
|
||||
echoId: 43,
|
||||
isPrivate: false,
|
||||
files: [new Blob(['audio-data'], { type: 'audio/ogg' })],
|
||||
isVoiceMessage: false,
|
||||
});
|
||||
expect(formPayload).toBeInstanceOf(FormData);
|
||||
expect(formPayload.get('is_voice_message')).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -49,6 +49,7 @@ const emit = defineEmits(['edit', 'delete']);
|
||||
const { t } = useI18n();
|
||||
|
||||
const STATUS_COMPLETED = 'completed';
|
||||
const STATUS_PROCESSING = 'processing';
|
||||
|
||||
const { formatMessage } = useMessageFormatter();
|
||||
|
||||
@@ -68,9 +69,15 @@ const campaignStatus = computed(() => {
|
||||
: t('CAMPAIGN.LIVE_CHAT.CARD.STATUS.DISABLED');
|
||||
}
|
||||
|
||||
return props.status === STATUS_COMPLETED
|
||||
? t('CAMPAIGN.SMS.CARD.STATUS.COMPLETED')
|
||||
: t('CAMPAIGN.SMS.CARD.STATUS.SCHEDULED');
|
||||
if (props.status === STATUS_COMPLETED) {
|
||||
return t('CAMPAIGN.SMS.CARD.STATUS.COMPLETED');
|
||||
}
|
||||
|
||||
if (props.status === STATUS_PROCESSING) {
|
||||
return t('CAMPAIGN.SMS.CARD.STATUS.PROCESSING');
|
||||
}
|
||||
|
||||
return t('CAMPAIGN.SMS.CARD.STATUS.SCHEDULED');
|
||||
});
|
||||
|
||||
const inboxName = computed(() => props.inbox?.name || '');
|
||||
|
||||
+29
-15
@@ -1,34 +1,48 @@
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { computed, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import Button from 'dashboard/components-next/button/Button.vue';
|
||||
import DropdownMenu from 'dashboard/components-next/dropdown-menu/DropdownMenu.vue';
|
||||
import { usePolicy } from 'dashboard/composables/usePolicy';
|
||||
|
||||
const emit = defineEmits(['add', 'import', 'export']);
|
||||
|
||||
const { t } = useI18n();
|
||||
const { checkPermissions } = usePolicy();
|
||||
|
||||
const contactMenuItems = [
|
||||
const contactMenuItems = computed(() => [
|
||||
{
|
||||
label: t('CONTACTS_LAYOUT.HEADER.ACTIONS.CONTACT_CREATION.ADD_CONTACT'),
|
||||
action: 'add',
|
||||
value: 'add',
|
||||
icon: 'i-lucide-plus',
|
||||
},
|
||||
{
|
||||
label: t('CONTACTS_LAYOUT.HEADER.ACTIONS.CONTACT_CREATION.EXPORT_CONTACT'),
|
||||
action: 'export',
|
||||
value: 'export',
|
||||
icon: 'i-lucide-upload',
|
||||
},
|
||||
{
|
||||
label: t('CONTACTS_LAYOUT.HEADER.ACTIONS.CONTACT_CREATION.IMPORT_CONTACT'),
|
||||
action: 'import',
|
||||
value: 'import',
|
||||
icon: 'i-lucide-download',
|
||||
},
|
||||
];
|
||||
...(checkPermissions(['administrator', 'contact_manage'])
|
||||
? [
|
||||
{
|
||||
label: t(
|
||||
'CONTACTS_LAYOUT.HEADER.ACTIONS.CONTACT_CREATION.EXPORT_CONTACT'
|
||||
),
|
||||
action: 'export',
|
||||
value: 'export',
|
||||
icon: 'i-lucide-upload',
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(checkPermissions(['administrator', 'contact_manage'])
|
||||
? [
|
||||
{
|
||||
label: t(
|
||||
'CONTACTS_LAYOUT.HEADER.ACTIONS.CONTACT_CREATION.IMPORT_CONTACT'
|
||||
),
|
||||
action: 'import',
|
||||
value: 'import',
|
||||
icon: 'i-lucide-download',
|
||||
},
|
||||
]
|
||||
: []),
|
||||
]);
|
||||
const showActionsDropdown = ref(false);
|
||||
|
||||
const handleContactAction = ({ action }) => {
|
||||
|
||||
@@ -31,6 +31,7 @@ import FileBubble from './bubbles/File.vue';
|
||||
import AudioBubble from './bubbles/Audio.vue';
|
||||
import VideoBubble from './bubbles/Video.vue';
|
||||
import EmbedBubble from './bubbles/Embed.vue';
|
||||
import FallbackBubble from './bubbles/Fallback.vue';
|
||||
import InstagramStoryBubble from './bubbles/InstagramStory.vue';
|
||||
import EmailBubble from './bubbles/Email/Index.vue';
|
||||
import UnsupportedBubble from './bubbles/Unsupported.vue';
|
||||
@@ -328,6 +329,8 @@ const componentToRender = computed(() => {
|
||||
if (Array.isArray(props.attachments) && props.attachments.length === 1) {
|
||||
const fileType = props.attachments[0].fileType;
|
||||
|
||||
if (fileType === ATTACHMENT_TYPES.FALLBACK) return FallbackBubble;
|
||||
|
||||
if (!props.content) {
|
||||
if (fileType === ATTACHMENT_TYPES.IMAGE) return ImageBubble;
|
||||
if (fileType === ATTACHMENT_TYPES.FILE) return FileBubble;
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
<script setup>
|
||||
import { computed } from 'vue';
|
||||
import BaseBubble from './Base.vue';
|
||||
import FormattedContent from './Text/FormattedContent.vue';
|
||||
import { useMessageContext } from '../provider.js';
|
||||
|
||||
const { attachments, content } = useMessageContext();
|
||||
|
||||
const attachment = computed(() => attachments.value?.[0] || {});
|
||||
const url = computed(
|
||||
() => attachment.value.dataUrl || attachment.value.data_url
|
||||
);
|
||||
const title = computed(
|
||||
() =>
|
||||
attachment.value.fallbackTitle ||
|
||||
attachment.value.fallback_title ||
|
||||
url.value
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<BaseBubble class="p-3" data-bubble-name="fallback">
|
||||
<FormattedContent v-if="content" :content="content" class="mb-2" />
|
||||
<a
|
||||
v-if="url"
|
||||
:href="url"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="block max-w-[320px] truncate text-sm text-n-brand underline"
|
||||
>
|
||||
{{ title }}
|
||||
</a>
|
||||
<span v-else class="text-sm text-n-slate-11">
|
||||
{{ title }}
|
||||
</span>
|
||||
</BaseBubble>
|
||||
</template>
|
||||
@@ -14,11 +14,11 @@ const props = defineProps({
|
||||
const emit = defineEmits(['removeAttachment']);
|
||||
|
||||
const nonRecordedAudioAttachments = computed(() => {
|
||||
return props.attachments.filter(attachment => !attachment?.isRecordedAudio);
|
||||
return props.attachments.filter(attachment => !attachment?.isVoiceMessage);
|
||||
});
|
||||
|
||||
const recordedAudioAttachments = computed(() =>
|
||||
props.attachments.filter(attachment => attachment.isRecordedAudio)
|
||||
props.attachments.filter(attachment => attachment.isVoiceMessage)
|
||||
);
|
||||
|
||||
const onRemoveAttachment = itemIndex => {
|
||||
|
||||
@@ -4,7 +4,7 @@ import { ref, onMounted, onUnmounted } from 'vue';
|
||||
import WaveSurfer from 'wavesurfer.js';
|
||||
import RecordPlugin from 'wavesurfer.js/dist/plugins/record.js';
|
||||
import { format, intervalToDuration } from 'date-fns';
|
||||
import { convertAudio } from './utils/mp3ConversionUtils';
|
||||
import { convertAudio } from './utils/audioConversionUtils';
|
||||
|
||||
const props = defineProps({
|
||||
audioRecordFormat: {
|
||||
@@ -18,6 +18,7 @@ const emit = defineEmits([
|
||||
'finishRecord',
|
||||
'pause',
|
||||
'play',
|
||||
'recordError',
|
||||
]);
|
||||
|
||||
const waveformContainer = ref(null);
|
||||
@@ -26,6 +27,7 @@ const record = ref(null);
|
||||
const isRecording = ref(false);
|
||||
const isPlaying = ref(false);
|
||||
const hasRecording = ref(false);
|
||||
const recordedAudioUrl = ref(null);
|
||||
|
||||
const formatTimeProgress = time => {
|
||||
const duration = intervalToDuration({ start: 0, end: time });
|
||||
@@ -35,6 +37,28 @@ const formatTimeProgress = time => {
|
||||
);
|
||||
};
|
||||
|
||||
const AUDIO_EXTENSION_MAP = {
|
||||
'audio/ogg': 'ogg',
|
||||
'audio/mp3': 'mp3',
|
||||
'audio/mpeg': 'mp3',
|
||||
'audio/wav': 'wav',
|
||||
'audio/webm': 'webm',
|
||||
};
|
||||
|
||||
const getRecordPluginOptions = audioFormat => {
|
||||
const options = {
|
||||
scrollingWaveform: true,
|
||||
renderRecordedAudio: false,
|
||||
};
|
||||
if (
|
||||
audioFormat === 'audio/ogg' &&
|
||||
MediaRecorder.isTypeSupported('audio/ogg;codecs=opus')
|
||||
) {
|
||||
options.mimeType = 'audio/ogg;codecs=opus';
|
||||
}
|
||||
return options;
|
||||
};
|
||||
|
||||
const initWaveSurfer = () => {
|
||||
wavesurfer.value = WaveSurfer.create({
|
||||
container: waveformContainer.value,
|
||||
@@ -45,10 +69,7 @@ const initWaveSurfer = () => {
|
||||
barGap: 1,
|
||||
barRadius: 2,
|
||||
plugins: [
|
||||
RecordPlugin.create({
|
||||
scrollingWaveform: true,
|
||||
renderRecordedAudio: false,
|
||||
}),
|
||||
RecordPlugin.create(getRecordPluginOptions(props.audioRecordFormat)),
|
||||
],
|
||||
});
|
||||
|
||||
@@ -62,21 +83,34 @@ const initWaveSurfer = () => {
|
||||
});
|
||||
|
||||
record.value.on('record-end', async blob => {
|
||||
const audioUrl = URL.createObjectURL(blob);
|
||||
const audioBlob = await convertAudio(blob, props.audioRecordFormat);
|
||||
const fileName = `${getUuid()}.mp3`;
|
||||
const file = new File([audioBlob], fileName, {
|
||||
type: props.audioRecordFormat,
|
||||
});
|
||||
wavesurfer.value.load(audioUrl);
|
||||
emit('finishRecord', {
|
||||
name: file.name,
|
||||
type: file.type,
|
||||
size: file.size,
|
||||
file,
|
||||
});
|
||||
hasRecording.value = true;
|
||||
isRecording.value = false;
|
||||
try {
|
||||
const audioBlob = await convertAudio(blob, props.audioRecordFormat);
|
||||
// Use the converted blob's actual type, which may differ from the
|
||||
// requested format when the browser can't produce it (e.g. Safari falls
|
||||
// back to MP3 instead of OGG). This keeps the filename, content type, and
|
||||
// voice-note flag consistent with the real bytes.
|
||||
const audioType = audioBlob.type || props.audioRecordFormat;
|
||||
const ext = AUDIO_EXTENSION_MAP[audioType] || 'mp3';
|
||||
const fileName = `${getUuid()}.${ext}`;
|
||||
const file = new File([audioBlob], fileName, {
|
||||
type: audioType,
|
||||
});
|
||||
if (recordedAudioUrl.value) URL.revokeObjectURL(recordedAudioUrl.value);
|
||||
recordedAudioUrl.value = URL.createObjectURL(audioBlob);
|
||||
wavesurfer.value.load(recordedAudioUrl.value);
|
||||
emit('finishRecord', {
|
||||
name: file.name,
|
||||
type: file.type,
|
||||
size: file.size,
|
||||
file,
|
||||
});
|
||||
hasRecording.value = true;
|
||||
isRecording.value = false;
|
||||
} catch (error) {
|
||||
isRecording.value = false;
|
||||
hasRecording.value = false;
|
||||
emit('recordError', { error });
|
||||
}
|
||||
});
|
||||
|
||||
record.value.on('record-progress', time => {
|
||||
@@ -109,6 +143,10 @@ onMounted(() => {
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
if (recordedAudioUrl.value) {
|
||||
URL.revokeObjectURL(recordedAudioUrl.value);
|
||||
recordedAudioUrl.value = null;
|
||||
}
|
||||
if (wavesurfer.value) {
|
||||
wavesurfer.value.destroy();
|
||||
}
|
||||
|
||||
+16
@@ -1,5 +1,7 @@
|
||||
import lamejs from '@breezystack/lamejs';
|
||||
|
||||
import { remuxWebmToOgg } from './webmOpusToOgg';
|
||||
|
||||
const writeString = (view, offset, string) => {
|
||||
// eslint-disable-next-line no-plusplus
|
||||
for (let i = 0; i < string.length; i++) {
|
||||
@@ -139,6 +141,20 @@ export const convertAudio = async (inputBlob, outputFormat, bitrate = 128) => {
|
||||
audio = await convertToWav(inputBlob);
|
||||
} else if (outputFormat === 'audio/mp3') {
|
||||
audio = await convertToMp3(inputBlob, bitrate);
|
||||
} else if (outputFormat === 'audio/ogg') {
|
||||
const inputType = inputBlob.type.split(';')[0].trim();
|
||||
if (inputType === 'audio/webm' || inputType === 'video/webm') {
|
||||
audio = await remuxWebmToOgg(inputBlob);
|
||||
} else if (inputType === 'audio/ogg') {
|
||||
audio = inputBlob;
|
||||
} else {
|
||||
// Browsers that record neither WebM nor OGG (e.g. Safari records
|
||||
// audio/mp4) cannot produce OGG/Opus. Fall back to MP3 so the recording
|
||||
// still sends as a regular audio message instead of failing. The caller
|
||||
// keys the voice-note flag off the returned blob type, so an MP3 result
|
||||
// is never mislabeled as an OGG/Opus voice note.
|
||||
audio = await convertToMp3(inputBlob, bitrate);
|
||||
}
|
||||
} else {
|
||||
throw new Error('Unsupported output format');
|
||||
}
|
||||
@@ -0,0 +1,457 @@
|
||||
/* eslint-disable no-bitwise */
|
||||
/**
|
||||
* WebM/Opus → OGG/Opus remuxer
|
||||
*
|
||||
* Chrome's MediaRecorder produces WebM containers even when
|
||||
* `audio/ogg;codecs=opus` is requested. WhatsApp Cloud API requires
|
||||
* proper OGG/Opus files for voice messages.
|
||||
*
|
||||
* This module extracts raw Opus packets from the WebM (EBML) container
|
||||
* and repackages them into a valid OGG bitstream. The audio data itself
|
||||
* is never re-encoded — only the container format changes.
|
||||
*
|
||||
* References:
|
||||
* EBML (container for WebM): RFC 8794 — https://www.rfc-editor.org/rfc/rfc8794
|
||||
* Matroska/WebM elements: https://www.matroska.org/technical/elements.html
|
||||
* OGG bitstream framing: RFC 3533 — https://www.rfc-editor.org/rfc/rfc3533
|
||||
* Opus codec: RFC 6716 — https://www.rfc-editor.org/rfc/rfc6716
|
||||
* Opus in OGG (OpusHead/Tags): RFC 7845 — https://www.rfc-editor.org/rfc/rfc7845
|
||||
*/
|
||||
|
||||
// ======================== EBML / WebM parser ========================
|
||||
|
||||
const EBML_IDS = {
|
||||
Segment: 0x18538067,
|
||||
SegmentInfo: 0x1549a966,
|
||||
Tracks: 0x1654ae6b,
|
||||
TrackEntry: 0xae,
|
||||
CodecPrivate: 0x63a2,
|
||||
Audio: 0xe1,
|
||||
SamplingFrequency: 0xb5,
|
||||
Channels: 0x9f,
|
||||
Cluster: 0x1f43b675,
|
||||
Timecode: 0xe7,
|
||||
SimpleBlock: 0xa3,
|
||||
BlockGroup: 0xa0,
|
||||
Block: 0xa1,
|
||||
};
|
||||
|
||||
const MASTER_ELEMENTS = new Set([
|
||||
0x1a45dfa3, // EBML header
|
||||
EBML_IDS.Segment,
|
||||
EBML_IDS.SegmentInfo,
|
||||
EBML_IDS.Tracks,
|
||||
EBML_IDS.TrackEntry,
|
||||
EBML_IDS.Audio,
|
||||
EBML_IDS.Cluster,
|
||||
EBML_IDS.BlockGroup,
|
||||
]);
|
||||
|
||||
/** Read an EBML variable-length integer (data size). */
|
||||
function readVint(data, pos) {
|
||||
if (pos >= data.length) return null;
|
||||
const first = data[pos];
|
||||
if (first === 0) return null;
|
||||
|
||||
let len = 1;
|
||||
let mask = 0x80;
|
||||
while (len <= 8 && !(first & mask)) {
|
||||
len += 1;
|
||||
mask >>= 1;
|
||||
}
|
||||
if (len > 8 || pos + len > data.length) return null;
|
||||
|
||||
let value = first & (mask - 1);
|
||||
for (let i = 1; i < len; i += 1) {
|
||||
value = value * 256 + data[pos + i];
|
||||
}
|
||||
return { value, length: len };
|
||||
}
|
||||
|
||||
/** Read an EBML element ID (leading marker bits are kept). */
|
||||
function readElementId(data, pos) {
|
||||
if (pos >= data.length) return null;
|
||||
const first = data[pos];
|
||||
if (first === 0) return null;
|
||||
|
||||
let len = 1;
|
||||
let mask = 0x80;
|
||||
while (len <= 4 && !(first & mask)) {
|
||||
len += 1;
|
||||
mask >>= 1;
|
||||
}
|
||||
if (len > 4 || pos + len > data.length) return null;
|
||||
|
||||
let id = first;
|
||||
for (let i = 1; i < len; i += 1) {
|
||||
id = id * 256 + data[pos + i];
|
||||
}
|
||||
return { id, length: len };
|
||||
}
|
||||
|
||||
function readUintBE(data, offset, length) {
|
||||
let v = 0;
|
||||
for (let i = 0; i < length; i += 1) v = v * 256 + data[offset + i];
|
||||
return v;
|
||||
}
|
||||
|
||||
function readFloatBE(data, offset, length) {
|
||||
if (length !== 4 && length !== 8) return NaN;
|
||||
const buf = new ArrayBuffer(length);
|
||||
const u8 = new Uint8Array(buf);
|
||||
for (let i = 0; i < length; i += 1) u8[i] = data[offset + i];
|
||||
const view = new DataView(buf);
|
||||
return length === 4 ? view.getFloat32(0) : view.getFloat64(0);
|
||||
}
|
||||
|
||||
/** Extract the raw Opus frame from a SimpleBlock / Block element. */
|
||||
function extractFrameFromBlock(data, offset, end) {
|
||||
const trackVint = readVint(data, offset);
|
||||
if (!trackVint) return null;
|
||||
let pos = offset + trackVint.length;
|
||||
|
||||
// int16 relative timecode (big-endian, signed) – skip
|
||||
pos += 2;
|
||||
// Flags byte – skip. Lacing (Xiph/EBML/fixed-size) is NOT supported;
|
||||
// this assumes single-frame blocks as produced by MediaRecorder.
|
||||
const flags = data[pos];
|
||||
const lacingBits = (flags >> 1) & 0x03;
|
||||
if (lacingBits !== 0) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(
|
||||
'webmOpusToOgg: laced SimpleBlock detected (unsupported), frame may be invalid'
|
||||
);
|
||||
}
|
||||
pos += 1;
|
||||
|
||||
if (pos >= end) return null;
|
||||
return data.slice(pos, end);
|
||||
}
|
||||
|
||||
/**
|
||||
* Walk the EBML tree and collect metadata + Opus frames.
|
||||
* We only descend into master elements and only extract the fields we need.
|
||||
*/
|
||||
function parseWebM(buffer) {
|
||||
const data = new Uint8Array(buffer);
|
||||
const result = {
|
||||
channels: 1,
|
||||
sampleRate: 48000,
|
||||
codecPrivate: null,
|
||||
frames: [],
|
||||
};
|
||||
|
||||
function walk(start, end) {
|
||||
let pos = start;
|
||||
while (pos < end) {
|
||||
const idRes = readElementId(data, pos);
|
||||
if (!idRes) break;
|
||||
pos += idRes.length;
|
||||
|
||||
const sizeRes = readVint(data, pos);
|
||||
if (!sizeRes) break;
|
||||
pos += sizeRes.length;
|
||||
|
||||
// Handle "unknown size" (all-ones VINT) by treating it as the rest of the parent
|
||||
// Use Math.pow instead of bit-shift to avoid 32-bit overflow for 5+ byte VINTs
|
||||
const maxVint = 2 ** (7 * sizeRes.length) - 1;
|
||||
const elEnd =
|
||||
sizeRes.value === maxVint ? end : Math.min(pos + sizeRes.value, end);
|
||||
|
||||
if (MASTER_ELEMENTS.has(idRes.id)) {
|
||||
walk(pos, elEnd);
|
||||
} else {
|
||||
switch (idRes.id) {
|
||||
case EBML_IDS.Channels:
|
||||
result.channels = readUintBE(data, pos, sizeRes.value);
|
||||
break;
|
||||
case EBML_IDS.SamplingFrequency:
|
||||
result.sampleRate = readFloatBE(data, pos, sizeRes.value);
|
||||
break;
|
||||
case EBML_IDS.CodecPrivate:
|
||||
result.codecPrivate = data.slice(pos, elEnd);
|
||||
break;
|
||||
case EBML_IDS.SimpleBlock:
|
||||
case EBML_IDS.Block: {
|
||||
const frame = extractFrameFromBlock(data, pos, elEnd);
|
||||
if (frame && frame.length > 0) result.frames.push(frame);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
pos = elEnd;
|
||||
}
|
||||
}
|
||||
|
||||
walk(0, data.length);
|
||||
return result;
|
||||
}
|
||||
|
||||
// ======================== OGG writer ========================
|
||||
|
||||
/** OGG CRC-32 table (polynomial 0x04C11DB7). */
|
||||
const CRC_TABLE = (() => {
|
||||
const t = new Uint32Array(256);
|
||||
for (let i = 0; i < 256; i += 1) {
|
||||
let c = i << 24;
|
||||
for (let j = 0; j < 8; j += 1) {
|
||||
c = ((c << 1) ^ (c & 0x80000000 ? 0x04c11db7 : 0)) >>> 0;
|
||||
}
|
||||
t[i] = c;
|
||||
}
|
||||
return t;
|
||||
})();
|
||||
|
||||
function oggCrc32(bytes) {
|
||||
let crc = 0;
|
||||
for (let i = 0; i < bytes.length; i += 1) {
|
||||
crc = (CRC_TABLE[((crc >>> 24) ^ bytes[i]) & 0xff] ^ (crc << 8)) >>> 0;
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build one OGG page.
|
||||
*
|
||||
* @param {number} headerType 0x02 = BOS, 0x04 = EOS, 0x00 = normal
|
||||
* @param {number} granulePosition 48 kHz sample count
|
||||
* @param {number} serialNumber logical stream id
|
||||
* @param {number} pageSeq page sequence counter
|
||||
* @param {Uint8Array[]} packets one or more complete Opus packets
|
||||
*/
|
||||
function createOggPage(
|
||||
headerType,
|
||||
granulePosition,
|
||||
serialNumber,
|
||||
pageSeq,
|
||||
packets
|
||||
) {
|
||||
// Build the lacing / segment table
|
||||
const segTable = [];
|
||||
let dataLen = 0;
|
||||
packets.forEach(pkt => {
|
||||
let rem = pkt.length;
|
||||
while (rem >= 255) {
|
||||
segTable.push(255);
|
||||
rem -= 255;
|
||||
}
|
||||
segTable.push(rem); // final segment (0 when pkt.length is a multiple of 255)
|
||||
dataLen += pkt.length;
|
||||
});
|
||||
|
||||
const hdrLen = 27 + segTable.length;
|
||||
const page = new Uint8Array(hdrLen + dataLen);
|
||||
const dv = new DataView(page.buffer);
|
||||
|
||||
// Capture pattern
|
||||
page.set([0x4f, 0x67, 0x67, 0x53]); // "OggS"
|
||||
page[4] = 0; // version
|
||||
page[5] = headerType;
|
||||
|
||||
// Granule position (int64 LE)
|
||||
dv.setUint32(6, granulePosition & 0xffffffff, true);
|
||||
dv.setUint32(
|
||||
10,
|
||||
Math.floor(granulePosition / 0x100000000) & 0xffffffff,
|
||||
true
|
||||
);
|
||||
|
||||
dv.setUint32(14, serialNumber, true); // serial
|
||||
dv.setUint32(18, pageSeq, true); // page sequence
|
||||
dv.setUint32(22, 0, true); // CRC placeholder
|
||||
|
||||
page[26] = segTable.length;
|
||||
for (let i = 0; i < segTable.length; i += 1) page[27 + i] = segTable[i];
|
||||
|
||||
let off = hdrLen;
|
||||
packets.forEach(pkt => {
|
||||
page.set(pkt, off);
|
||||
off += pkt.length;
|
||||
});
|
||||
|
||||
// Fill in the CRC
|
||||
dv.setUint32(22, oggCrc32(page), true);
|
||||
return page;
|
||||
}
|
||||
|
||||
// ======================== Opus helpers ========================
|
||||
|
||||
/** Lookup table: frame duration in ms for each Opus TOC config index (0-31). */
|
||||
const OPUS_FRAME_MS = [
|
||||
10,
|
||||
20,
|
||||
40,
|
||||
60, // 0-3 SILK NB
|
||||
10,
|
||||
20,
|
||||
40,
|
||||
60, // 4-7 SILK MB
|
||||
10,
|
||||
20,
|
||||
40,
|
||||
60, // 8-11 SILK WB
|
||||
10,
|
||||
20, // 12-13 Hybrid SWB
|
||||
10,
|
||||
20, // 14-15 Hybrid FB
|
||||
2.5,
|
||||
5,
|
||||
10,
|
||||
20, // 16-19 CELT NB
|
||||
2.5,
|
||||
5,
|
||||
10,
|
||||
20, // 20-23 CELT WB
|
||||
2.5,
|
||||
5,
|
||||
10,
|
||||
20, // 24-27 CELT SWB
|
||||
2.5,
|
||||
5,
|
||||
10,
|
||||
20, // 28-31 CELT FB
|
||||
];
|
||||
|
||||
/** Return the total number of 48 kHz PCM samples represented by an Opus packet. */
|
||||
function opusPacketSamples(pkt) {
|
||||
if (!pkt || pkt.length === 0) return 960; // default 20 ms
|
||||
const toc = pkt[0];
|
||||
const config = (toc >> 3) & 0x1f;
|
||||
const code = toc & 0x03;
|
||||
|
||||
const samplesPerFrame = ((OPUS_FRAME_MS[config] || 20) * 48000) / 1000;
|
||||
let frameCount;
|
||||
if (code <= 1) frameCount = code + 1;
|
||||
else if (code === 2) frameCount = 2;
|
||||
else frameCount = pkt.length >= 2 ? pkt[1] & 0x3f : 1;
|
||||
|
||||
return samplesPerFrame * frameCount;
|
||||
}
|
||||
|
||||
function buildOpusHead(channels, sampleRate, preSkip) {
|
||||
const buf = new Uint8Array(19);
|
||||
const dv = new DataView(buf.buffer);
|
||||
buf.set(new TextEncoder().encode('OpusHead'));
|
||||
buf[8] = 1; // version
|
||||
buf[9] = channels;
|
||||
dv.setUint16(10, preSkip, true);
|
||||
dv.setUint32(12, sampleRate, true);
|
||||
dv.setInt16(16, 0, true); // output gain
|
||||
buf[18] = 0; // channel mapping family
|
||||
return buf;
|
||||
}
|
||||
|
||||
function buildOpusTags() {
|
||||
const vendor = new TextEncoder().encode('chatwoot');
|
||||
const buf = new Uint8Array(8 + 4 + vendor.length + 4);
|
||||
const dv = new DataView(buf.buffer);
|
||||
buf.set(new TextEncoder().encode('OpusTags'));
|
||||
dv.setUint32(8, vendor.length, true);
|
||||
buf.set(vendor, 12);
|
||||
dv.setUint32(12 + vendor.length, 0, true); // 0 user comments
|
||||
return buf;
|
||||
}
|
||||
|
||||
// ======================== Public API ========================
|
||||
|
||||
const MAX_FRAMES_PER_PAGE = 50; // ~1 s at 20 ms/frame
|
||||
const MAX_SEGMENTS_PER_PAGE = 255;
|
||||
|
||||
/**
|
||||
* Remux a WebM/Opus blob into an OGG/Opus blob.
|
||||
* If the input is already OGG (starts with "OggS"), it is returned as-is.
|
||||
*
|
||||
* @param {Blob} webmBlob
|
||||
* @returns {Promise<Blob>} OGG/Opus blob
|
||||
*/
|
||||
export async function remuxWebmToOgg(webmBlob) {
|
||||
const buffer = await webmBlob.arrayBuffer();
|
||||
const bytes = new Uint8Array(buffer);
|
||||
|
||||
// Already OGG? Return unchanged.
|
||||
if (
|
||||
bytes.length >= 4 &&
|
||||
bytes[0] === 0x4f &&
|
||||
bytes[1] === 0x67 &&
|
||||
bytes[2] === 0x67 &&
|
||||
bytes[3] === 0x53
|
||||
) {
|
||||
return webmBlob;
|
||||
}
|
||||
|
||||
const { channels, sampleRate, codecPrivate, frames } = parseWebM(buffer);
|
||||
if (frames.length === 0) {
|
||||
throw new Error('No Opus frames found in WebM input');
|
||||
}
|
||||
|
||||
// Extract pre-skip from the WebM CodecPrivate (which IS the OpusHead)
|
||||
let preSkip = 312;
|
||||
if (codecPrivate && codecPrivate.length >= 12) {
|
||||
const magic = new TextDecoder().decode(codecPrivate.slice(0, 8));
|
||||
if (magic === 'OpusHead') {
|
||||
preSkip = new DataView(
|
||||
codecPrivate.buffer,
|
||||
codecPrivate.byteOffset,
|
||||
codecPrivate.length
|
||||
).getUint16(10, true);
|
||||
}
|
||||
}
|
||||
|
||||
const serial = (Math.random() * 0x100000000) >>> 0;
|
||||
let pageSeq = 0;
|
||||
const pages = [];
|
||||
|
||||
// Page 0 – OpusHead (BOS)
|
||||
pages.push(
|
||||
createOggPage(0x02, 0, serial, pageSeq, [
|
||||
buildOpusHead(channels, sampleRate, preSkip),
|
||||
])
|
||||
);
|
||||
pageSeq += 1;
|
||||
|
||||
// Page 1 – OpusTags
|
||||
pages.push(createOggPage(0x00, 0, serial, pageSeq, [buildOpusTags()]));
|
||||
pageSeq += 1;
|
||||
|
||||
// Audio pages
|
||||
let granule = 0;
|
||||
let idx = 0;
|
||||
|
||||
while (idx < frames.length) {
|
||||
const packets = [];
|
||||
let segs = 0;
|
||||
|
||||
while (idx < frames.length && packets.length < MAX_FRAMES_PER_PAGE) {
|
||||
const pkt = frames[idx];
|
||||
// createOggPage always appends a terminating lacing value, so a packet
|
||||
// spans floor(len/255)+1 segments (including the extra 0 when len is an
|
||||
// exact multiple of 255). Math.ceil would undercount those cases.
|
||||
const pktSegs = Math.floor(pkt.length / 255) + 1;
|
||||
if (segs + pktSegs > MAX_SEGMENTS_PER_PAGE && packets.length > 0) break;
|
||||
|
||||
packets.push(pkt);
|
||||
segs += pktSegs;
|
||||
granule += opusPacketSamples(pkt);
|
||||
idx += 1;
|
||||
}
|
||||
|
||||
const isLast = idx >= frames.length;
|
||||
pages.push(
|
||||
createOggPage(isLast ? 0x04 : 0x00, granule, serial, pageSeq, packets)
|
||||
);
|
||||
pageSeq += 1;
|
||||
}
|
||||
|
||||
// Concatenate pages into a single buffer
|
||||
const total = pages.reduce((s, p) => s + p.length, 0);
|
||||
const out = new Uint8Array(total);
|
||||
let off = 0;
|
||||
pages.forEach(p => {
|
||||
out.set(p, off);
|
||||
off += p.length;
|
||||
});
|
||||
|
||||
return new Blob([out], { type: 'audio/ogg' });
|
||||
}
|
||||
@@ -375,7 +375,10 @@ export default {
|
||||
return `draft-${this.conversationIdByRoute}-${this.replyType}`;
|
||||
},
|
||||
audioRecordFormat() {
|
||||
if (this.isAWhatsAppChannel || this.isATelegramChannel) {
|
||||
if (this.isAWhatsAppChannel) {
|
||||
return AUDIO_FORMATS.OGG;
|
||||
}
|
||||
if (this.isATelegramChannel) {
|
||||
return AUDIO_FORMATS.MP3;
|
||||
}
|
||||
if (this.isAPIInbox) {
|
||||
@@ -1008,14 +1011,18 @@ export default {
|
||||
onFinishRecorder(file) {
|
||||
this.recordingAudioState = 'stopped';
|
||||
this.hasRecordedAudio = true;
|
||||
// Added a new key isRecordedAudio to the file to find it's and recorded audio
|
||||
// Added a new key isVoiceMessage to the file to identify recorded audio
|
||||
// Because to filter and show only non recorded audio and other attachments
|
||||
const autoRecordedFile = {
|
||||
...file,
|
||||
isRecordedAudio: true,
|
||||
isVoiceMessage: true,
|
||||
};
|
||||
return file && this.onFileUpload(autoRecordedFile);
|
||||
},
|
||||
onRecordError() {
|
||||
this.toggleAudioRecorder();
|
||||
useAlert(this.$t('CONVERSATION.REPLYBOX.AUDIO_CONVERSION_FAILED'));
|
||||
},
|
||||
toggleTyping(status) {
|
||||
const conversationId = this.currentChat.id;
|
||||
const isPrivate = this.isPrivate;
|
||||
@@ -1042,7 +1049,7 @@ export default {
|
||||
isPrivate: this.isPrivate,
|
||||
thumb: reader.result,
|
||||
blobSignedId: blob ? blob.signed_id : undefined,
|
||||
isRecordedAudio: file?.isRecordedAudio || false,
|
||||
isVoiceMessage: file?.isVoiceMessage || false,
|
||||
});
|
||||
};
|
||||
},
|
||||
@@ -1078,6 +1085,7 @@ export default {
|
||||
private: false,
|
||||
message: caption,
|
||||
sender: this.sender,
|
||||
isVoiceMessage: attachment.isVoiceMessage || false,
|
||||
};
|
||||
|
||||
attachmentPayload = this.setReplyToInPayload(attachmentPayload);
|
||||
@@ -1127,6 +1135,9 @@ export default {
|
||||
this.attachedFiles.forEach(attachment => {
|
||||
if (this.globalConfig.directUploadsEnabled) {
|
||||
messagePayload.files.push(attachment.blobSignedId);
|
||||
if (attachment.isVoiceMessage) {
|
||||
messagePayload.isVoiceMessage = true;
|
||||
}
|
||||
} else {
|
||||
messagePayload.files.push(attachment.resource.file);
|
||||
}
|
||||
@@ -1215,7 +1226,7 @@ export default {
|
||||
this.hasRecordedAudio = false;
|
||||
// Only clear the recorded audio when we click toggle button.
|
||||
this.attachedFiles = this.attachedFiles.filter(
|
||||
file => !file?.isRecordedAudio
|
||||
file => !file?.isVoiceMessage
|
||||
);
|
||||
},
|
||||
toggleEditorSize() {
|
||||
@@ -1293,6 +1304,7 @@ export default {
|
||||
:audio-record-format="audioRecordFormat"
|
||||
@recorder-progress-changed="onRecordProgressChanged"
|
||||
@finish-record="onFinishRecorder"
|
||||
@record-error="onRecordError"
|
||||
@play="recordingAudioState = 'playing'"
|
||||
@pause="recordingAudioState = 'paused'"
|
||||
/>
|
||||
|
||||
@@ -10,10 +10,12 @@ vi.mock('shared/helpers/mitt', () => ({
|
||||
|
||||
vi.mock('dashboard/helper/AnalyticsHelper/index', async importOriginal => {
|
||||
const actual = await importOriginal();
|
||||
actual.default = {
|
||||
track: vi.fn(),
|
||||
return {
|
||||
...actual,
|
||||
default: {
|
||||
track: vi.fn(),
|
||||
},
|
||||
};
|
||||
return actual;
|
||||
});
|
||||
|
||||
describe('useTrack', () => {
|
||||
|
||||
@@ -16,10 +16,12 @@ vi.mock('vue-i18n');
|
||||
vi.mock('dashboard/api/captain/tasks');
|
||||
vi.mock('dashboard/helper/AnalyticsHelper/index', async importOriginal => {
|
||||
const actual = await importOriginal();
|
||||
actual.default = {
|
||||
track: vi.fn(),
|
||||
return {
|
||||
...actual,
|
||||
default: {
|
||||
track: vi.fn(),
|
||||
},
|
||||
};
|
||||
return actual;
|
||||
});
|
||||
vi.mock('dashboard/helper/AnalyticsHelper/events', () => ({
|
||||
CAPTAIN_EVENTS: {
|
||||
|
||||
@@ -52,6 +52,10 @@ export function useAccount() {
|
||||
});
|
||||
};
|
||||
|
||||
const finishOnboarding = async data => {
|
||||
await store.dispatch('accounts/finishOnboarding', data);
|
||||
};
|
||||
|
||||
return {
|
||||
accountId,
|
||||
route,
|
||||
@@ -61,5 +65,6 @@ export function useAccount() {
|
||||
isCloudFeatureEnabled,
|
||||
isOnChatwootCloud,
|
||||
updateAccount,
|
||||
finishOnboarding,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
"OR": "OR"
|
||||
},
|
||||
"INPUT_PLACEHOLDER": "Enter value",
|
||||
"CONTACT_SEARCH_PLACEHOLDER": "እውቂያዎችን ይፈልጉ",
|
||||
"CONTACT_FALLBACK": "Contact #{id}",
|
||||
"OPERATOR_LABELS": {
|
||||
"equal_to": "Equal to",
|
||||
"not_equal_to": "Not equal to",
|
||||
@@ -49,6 +51,7 @@
|
||||
"ASSIGNEE_NAME": "Assignee name",
|
||||
"INBOX_NAME": "Inbox name",
|
||||
"TEAM_NAME": "Team name",
|
||||
"CONTACT": "እውቂያ",
|
||||
"CONVERSATION_IDENTIFIER": "Conversation identifier",
|
||||
"CAMPAIGN_NAME": "Campaign name",
|
||||
"LABELS": "Labels",
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
{
|
||||
"BULK_ACTION": {
|
||||
"CONVERSATIONS_SELECTED": "{conversationCount} conversations selected",
|
||||
"AGENT_SELECT_LABEL": "Select agent",
|
||||
"ASSIGN_CONFIRMATION_LABEL": "Are you sure to assign {conversationCount} {conversationLabel} to",
|
||||
"UNASSIGN_CONFIRMATION_LABEL": "Are you sure to unassign {conversationCount} {conversationLabel}?",
|
||||
"GO_BACK_LABEL": "Go back",
|
||||
"ASSIGN_LABEL": "Assign",
|
||||
"NONE": "None",
|
||||
"CLEAR_SELECTION": "Clear",
|
||||
"ASSIGN_AGENT_CONFIRMATION_LABEL": "Are you sure you want to assign {n} conversation to {agentName}? | Are you sure you want to assign {n} conversations to {agentName}?",
|
||||
@@ -20,7 +15,6 @@
|
||||
"RESOLVE_SUCCESFUL": "Conversations resolved successfully.",
|
||||
"RESOLVE_FAILED": "Failed to resolve conversations. Please try again.",
|
||||
"ALL_CONVERSATIONS_SELECTED_ALERT": "Conversations visible on this page are only selected.",
|
||||
"AGENT_LIST_LOADING": "Loading agents",
|
||||
"UPDATE": {
|
||||
"CHANGE_STATUS": "Change status",
|
||||
"SNOOZE_UNTIL": "Snooze",
|
||||
@@ -33,16 +27,16 @@
|
||||
},
|
||||
"LABELS": {
|
||||
"ASSIGN_LABELS": "Assign labels",
|
||||
"NO_LABELS_FOUND": "No labels found",
|
||||
"REMOVE_LABELS": "Remove labels",
|
||||
"ASSIGN_SELECTED_LABELS": "Assign selected labels",
|
||||
"REMOVE_SELECTED_LABELS": "Remove selected labels",
|
||||
"ASSIGN_SUCCESFUL": "Labels assigned successfully.",
|
||||
"ASSIGN_FAILED": "Failed to assign labels. Please try again."
|
||||
"ASSIGN_FAILED": "Failed to assign labels. Please try again.",
|
||||
"REMOVE_SUCCESFUL": "Labels removed successfully.",
|
||||
"REMOVE_FAILED": "Failed to remove labels. Please try again."
|
||||
},
|
||||
"TEAMS": {
|
||||
"TEAM_SELECT_LABEL": "Select team",
|
||||
"NONE": "None",
|
||||
"NO_TEAMS_AVAILABLE": "There are no teams added to this account yet.",
|
||||
"ASSIGN_SELECTED_TEAMS": "Assign selected team.",
|
||||
"ASSIGN_TEAM_CONFIRMATION_LABEL": "Are you sure you want to assign {n} conversation to {teamName}? | Are you sure you want to assign {n} conversations to {teamName}?",
|
||||
"UNASSIGN_TEAM_CONFIRMATION_LABEL": "Are you sure you want to unassign {n} conversation? | Are you sure you want to unassign {n} conversations?",
|
||||
"ASSIGN_SUCCESFUL": "Teams assigned successfully.",
|
||||
|
||||
@@ -88,6 +88,7 @@
|
||||
},
|
||||
"CARD": {
|
||||
"STATUS": {
|
||||
"PROCESSING": "በሂደት ላይ",
|
||||
"COMPLETED": "Completed",
|
||||
"SCHEDULED": "Scheduled"
|
||||
},
|
||||
@@ -146,6 +147,7 @@
|
||||
},
|
||||
"CARD": {
|
||||
"STATUS": {
|
||||
"PROCESSING": "በሂደት ላይ",
|
||||
"COMPLETED": "Completed",
|
||||
"SCHEDULED": "Scheduled"
|
||||
},
|
||||
|
||||
@@ -63,6 +63,7 @@
|
||||
"CODE": "Code",
|
||||
"BULLET_LIST": "Bullet List",
|
||||
"ORDERED_LIST": "Ordered List",
|
||||
"TABLE": "Table"
|
||||
"TABLE": "Table",
|
||||
"IMAGE": "Image"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
"CALL": "ደውል",
|
||||
"CALL_INITIATED": "ለእውነተኛው እውቀት መደወል እየተከናወነ ነው…",
|
||||
"CALL_FAILED": "ጥሪውን መጀመር አልቻልንም። እባክዎ ደግመው ይሞክሩ።.",
|
||||
"WHATSAPP_CALL_PERMISSION_REQUESTED": "Sent a call permission request to the contact. Try again once they accept.",
|
||||
"WHATSAPP_CALL_PERMISSION_PENDING": "Call permission request already sent recently. Try again once the contact accepts.",
|
||||
"CLICK_TO_EDIT": "Click to edit",
|
||||
"VOICE_INBOX_PICKER": {
|
||||
"TITLE": "የድምፅ ኢንቦክስ ይምረጡ"
|
||||
@@ -583,8 +585,11 @@
|
||||
},
|
||||
"CONTACTS_BULK_ACTIONS": {
|
||||
"ASSIGN_LABELS": "መለያዎችን መሰጠት",
|
||||
"REMOVE_LABELS": "Remove Labels",
|
||||
"ASSIGN_LABELS_SUCCESS": "መለያዎች በተሳካ ሁኔታ ተመዝግበዋል።.",
|
||||
"ASSIGN_LABELS_FAILED": "መለያዎችን ማስመዝገብ አልተሳካም",
|
||||
"REMOVE_LABELS_SUCCESS": "Labels removed successfully.",
|
||||
"REMOVE_LABELS_FAILED": "Failed to remove labels",
|
||||
"DESCRIPTION": "ለተመረጡት እውነተኛዎች የሚያክሉትን መለያዎች ይምረጡ።.",
|
||||
"NO_LABELS_FOUND": "እስካሁን መለያዎች አልተገኙም።.",
|
||||
"SELECTED_COUNT": "{count} ተመርጧል",
|
||||
|
||||
@@ -62,6 +62,7 @@
|
||||
"UNSUPPORTED_MESSAGE_FACEBOOK": "This message is unsupported. You can view this message on the Facebook Messenger app.",
|
||||
"UNSUPPORTED_MESSAGE_INSTAGRAM": "This message is unsupported. You can view this message on the Instagram app.",
|
||||
"UNSUPPORTED_MESSAGE_TIKTOK": "This message is unsupported. You can view this message on the TikTok app.",
|
||||
"UNSUPPORTED_MESSAGE_WHATSAPP": "This message is unsupported. You can view this message on the WhatsApp app.",
|
||||
"SUCCESS_DELETE_MESSAGE": "Message deleted successfully",
|
||||
"FAIL_DELETE_MESSSAGE": "Couldn't delete message! Try again",
|
||||
"NO_RESPONSE": "No response",
|
||||
@@ -79,13 +80,22 @@
|
||||
"OUTGOING_CALL": "Outgoing call",
|
||||
"CALL_IN_PROGRESS": "Call in progress",
|
||||
"NO_ANSWER": "No answer",
|
||||
"NO_ANSWER_OUTBOUND_LABEL": "No answer",
|
||||
"NO_ANSWER_OUTBOUND_SUBTEXT": "Contact didn't pick up",
|
||||
"MISSED_CALL": "Missed call",
|
||||
"MISSED_CALL_INBOUND_SUBTEXT": "No agent picked up",
|
||||
"MISSED_CALL_DECLINED_BY": "Declined by {agentName}",
|
||||
"CALL_ENDED": "Call ended",
|
||||
"HANDLED_BY": "Handled by {agentName}",
|
||||
"NOT_ANSWERED_YET": "Not answered yet",
|
||||
"CALLING": "Calling…",
|
||||
"THEY_ANSWERED": "They answered",
|
||||
"YOU_ANSWERED": "You answered",
|
||||
"AGENT_ANSWERED": "{agentName} answered",
|
||||
"JOIN_CALL": "Join call"
|
||||
"JOIN_CALL": "Join call",
|
||||
"CALL_BACK": "Call back",
|
||||
"TRANSCRIPT_SHOW_MORE": "Show more",
|
||||
"TRANSCRIPT_SHOW_LESS": "Show less"
|
||||
},
|
||||
"HEADER": {
|
||||
"RESOLVE_ACTION": "ተፈትኗል",
|
||||
@@ -100,6 +110,12 @@
|
||||
"SNOOZED_UNTIL_TOMORROW": "Snoozed until tomorrow",
|
||||
"SNOOZED_UNTIL_NEXT_WEEK": "Snoozed until next week",
|
||||
"SNOOZED_UNTIL_NEXT_REPLY": "Snoozed until next reply",
|
||||
"WHATSAPP_CALL": "Start WhatsApp call",
|
||||
"WHATSAPP_CALL_FAILED": "Could not start the WhatsApp call.",
|
||||
"VOICE_CALL": "Start call",
|
||||
"VOICE_CALL_FAILED": "Could not start the call.",
|
||||
"WHATSAPP_CALL_PERMISSION_REQUESTED": "Sent a call permission request to the contact. Try again once they accept.",
|
||||
"WHATSAPP_CALL_PERMISSION_PENDING": "Call permission request already sent recently. Try again once the contact accepts.",
|
||||
"SLA_STATUS": {
|
||||
"FRT": "FRT {status}",
|
||||
"NRT": "NRT {status}",
|
||||
@@ -215,7 +231,9 @@
|
||||
"TIP_AUDIORECORDER_ICON": "Record audio",
|
||||
"TIP_AUDIORECORDER_PERMISSION": "Allow access to audio",
|
||||
"TIP_AUDIORECORDER_ERROR": "Could not open the audio",
|
||||
"AUDIO_CONVERSION_FAILED": "Audio conversion failed. Please try again.",
|
||||
"DRAG_DROP": "Drag and drop here to attach",
|
||||
"IMAGE_UPLOAD_SUCCESS": "ምስል በተሳካ ሁኔታ ተሰብስቧል",
|
||||
"START_AUDIO_RECORDING": "Start audio recording",
|
||||
"STOP_AUDIO_RECORDING": "Stop audio recording",
|
||||
"COPILOT_THINKING": "ኮፒሎት እየሰማራ ነው",
|
||||
@@ -299,8 +317,13 @@
|
||||
"NOT_ANSWERED_YET": "Not answered yet",
|
||||
"HANDLED_IN_ANOTHER_TAB": "Being handled in another tab",
|
||||
"REJECT_CALL": "Reject",
|
||||
"DISMISS_CALL": "Dismiss",
|
||||
"JOIN_CALL": "Join call",
|
||||
"END_CALL": "End call"
|
||||
"END_CALL": "End call",
|
||||
"MUTE": "Mute mic",
|
||||
"UNMUTE": "Unmute mic",
|
||||
"VIEW_CHAT_HISTORY": "View chat history",
|
||||
"GO_TO_CONVERSATION": "Go to conversation thread"
|
||||
}
|
||||
},
|
||||
"EMAIL_TRANSCRIPT": {
|
||||
|
||||
@@ -607,9 +607,12 @@
|
||||
"DRAFT": "እቅድ",
|
||||
"ARCHIVE": "አርክቭ",
|
||||
"TRANSLATE": "Translate",
|
||||
"MOVE_TO_CATEGORY": "Category",
|
||||
"DELETE": "Delete",
|
||||
"STATUS_SUCCESS": "Articles updated successfully",
|
||||
"STATUS_ERROR": "Failed to update articles",
|
||||
"CATEGORY_SUCCESS": "Articles moved successfully",
|
||||
"CATEGORY_ERROR": "Failed to move articles",
|
||||
"DELETE_CONFIRM_TITLE": "Delete article | Delete {count} articles",
|
||||
"DELETE_CONFIRM_DESCRIPTION": "This will permanently delete the selected article. This action cannot be undone. | This will permanently delete {count} selected articles. This action cannot be undone.",
|
||||
"DELETE_CONFIRM": "Delete",
|
||||
@@ -697,9 +700,27 @@
|
||||
"MAKE_DEFAULT": "እንደ ነባሪ አድርግ",
|
||||
"MOVE_TO_DRAFT": "Move to draft",
|
||||
"PUBLISH_LOCALE": "Publish locale",
|
||||
"CUSTOMIZE_CONTENT": "Localize content",
|
||||
"DELETE": "ሰርዝ"
|
||||
}
|
||||
},
|
||||
"CONTENT_DIALOG": {
|
||||
"TITLE": "Localize content",
|
||||
"DESCRIPTION": "Set values specific to this locale. Anything left blank falls back to the default locale.",
|
||||
"NAME": {
|
||||
"LABEL": "Name"
|
||||
},
|
||||
"PAGE_TITLE": {
|
||||
"LABEL": "የገፅ ርዕስ"
|
||||
},
|
||||
"HEADER_TEXT": {
|
||||
"LABEL": "የራስጌ ጽሑፍ"
|
||||
},
|
||||
"API": {
|
||||
"SUCCESS_MESSAGE": "Locale content updated successfully",
|
||||
"ERROR_MESSAGE": "Unable to update locale content. Try again."
|
||||
}
|
||||
},
|
||||
"ADD_LOCALE_DIALOG": {
|
||||
"TITLE": "አዲስ ቋንቋ አክል",
|
||||
"DESCRIPTION": "ይህ ሰነድ በሚጻፍበት ቋንቋ ይምረጡ። ይህ ወደ የትርጉም ዝርዝርዎ ይጨምራል እና በኋላ ተጨማሪ ማክሰኞ ይችላሉ።.",
|
||||
@@ -866,6 +887,28 @@
|
||||
},
|
||||
"EDIT_CONFIGURATION": "ቅንብር አርትዕ"
|
||||
},
|
||||
"LAYOUT_CONTENT": {
|
||||
"HEADER": "Appearance",
|
||||
"DESCRIPTION": "Pick the layout that fits how your visitors read.",
|
||||
"LAYOUT": {
|
||||
"CLASSIC": {
|
||||
"TITLE": "Classic",
|
||||
"DESCRIPTION": "A welcoming home page with search and featured topics."
|
||||
},
|
||||
"SIDEBAR": {
|
||||
"TITLE": "Documentation",
|
||||
"DESCRIPTION": "Side-by-side navigation that keeps every guide a click away."
|
||||
}
|
||||
},
|
||||
"SOCIAL_LINKS": {
|
||||
"HEADER": "Social links",
|
||||
"DESCRIPTION": "Add the handle for each network and your help center builds the full link. Shown in the documentation layout footer.",
|
||||
"PLACEHOLDER": "handle",
|
||||
"ADD": "Add social link",
|
||||
"REMOVE": "አስወግድ"
|
||||
},
|
||||
"SAVE": "ለውጦች አስቀምጥ"
|
||||
},
|
||||
"API": {
|
||||
"CREATE_PORTAL": {
|
||||
"SUCCESS_MESSAGE": "ፖርታል በተሳካ ሁኔታ ተፈጥሯል",
|
||||
|
||||
@@ -308,6 +308,7 @@
|
||||
"AUTH_PROCESSING": "ከMeta ጋር በመረጋገጥ ላይ ነው",
|
||||
"WAITING_FOR_BUSINESS_INFO": "እባክዎ በMeta መስኮት ውስጥ የንግድ ቅንብር ያርኩ...",
|
||||
"PROCESSING": "የWhatsApp ቢዝነስ መለያዎን እየተዘጋጀ ነው",
|
||||
"ENABLING_CALLING": "Enabling WhatsApp Calling on your number…",
|
||||
"LOADING_SDK": "Facebook SDK እየተጫነ ነው...",
|
||||
"CANCELLED": "የWhatsApp ምዝገባ ተሰርዟል",
|
||||
"SUCCESS_TITLE": "የWhatsApp ቢዝነስ መለያ ተገናኝቷል!",
|
||||
@@ -317,7 +318,8 @@
|
||||
"AUTH_NOT_COMPLETED": "ማረጋገጫ አልተጠናቀቀም። ሂደቱን እባክዎ ዳግም ይጀምሩ።.",
|
||||
"SUCCESS_FALLBACK": "የWhatsApp የንግድ መለያ በተሳካ ሁኔታ ተቋቋመ",
|
||||
"MANUAL_FALLBACK": "ቁጥርዎ ከWhatsApp Business Platform (API) ጋር ከተገናኘ እና ወይም እርስዎ የቴክኖሎጂ አቅራቢ ከሆኑ እና የእርስዎን ቁጥር በራስዎ ሲያስገቡ፣ እባክዎ የ{link} ሂደትን ይጠቀሙ",
|
||||
"MANUAL_LINK_TEXT": "የእጅ ማቀናበሪያ ሂደት"
|
||||
"MANUAL_LINK_TEXT": "የእጅ ማቀናበሪያ ሂደት",
|
||||
"CALLING_ENABLE_FAILED": "Your WhatsApp inbox is ready, but voice calling couldn't be turned on — this number isn't enrolled in the WhatsApp Business Calling API yet. Reach out to Meta or your WhatsApp Business Solution Provider to onboard it, then turn calling on from the inbox's Calls settings."
|
||||
},
|
||||
"API": {
|
||||
"ERROR_MESSAGE": "WhatsApp ቻናሉን ማስቀመጥ አልተቻለም"
|
||||
@@ -465,6 +467,10 @@
|
||||
"TITLE": "ዋትስአፕ",
|
||||
"DESCRIPTION": "በWhatsApp ላይ ደንበኞችዎን ድጋፍ ያድርጉ"
|
||||
},
|
||||
"WHATSAPP_CALL": {
|
||||
"TITLE": "WhatsApp Call",
|
||||
"DESCRIPTION": "Take voice calls on your WhatsApp number"
|
||||
},
|
||||
"EMAIL": {
|
||||
"TITLE": "ኢሜይል",
|
||||
"DESCRIPTION": "ከGmail, Outlook ወይም ከሌሎች አቅራቢዎች ጋር ያገናኙ"
|
||||
@@ -637,7 +643,8 @@
|
||||
"BOT_CONFIGURATION": "የቦት ቅንብሮች",
|
||||
"ACCOUNT_HEALTH": "የመለያ ጤና",
|
||||
"CSAT": "የደንበኞች ደህንነት ግምገማ (CSAT)",
|
||||
"VOICE": "ድምጽ"
|
||||
"VOICE": "ድምጽ",
|
||||
"CALLS": "Calls"
|
||||
},
|
||||
"VOICE_CONFIGURATION": {
|
||||
"ENABLE_VOICE": {
|
||||
@@ -648,6 +655,26 @@
|
||||
"DESCRIPTION": "Voice calling requires Twilio API Key credentials. These are used to generate tokens for agent voice connections."
|
||||
}
|
||||
},
|
||||
"WHATSAPP_CALLING": {
|
||||
"ENABLE": {
|
||||
"LABEL": "Enable WhatsApp Calling",
|
||||
"DESCRIPTION": "Allow agents to receive and place WhatsApp Cloud calls on this inbox. Customers can call this business number directly from WhatsApp."
|
||||
},
|
||||
"ENABLE_FAILED": "Voice calling couldn't be turned on for this number — it isn't enrolled in the WhatsApp Business Calling API yet. Reach out to Meta or your WhatsApp Business Solution Provider to onboard it, then try again.",
|
||||
"PHONE_NUMBER": {
|
||||
"LABEL": "Business phone number",
|
||||
"HELP_TEXT": "WhatsApp number that customers will call."
|
||||
},
|
||||
"HOW_IT_WORKS": {
|
||||
"LABEL": "How it works",
|
||||
"DESCRIPTION": "Calls are placed peer-to-peer between the agent's browser and Meta — no extra credentials are required. Make sure the agent's browser has microphone permission for this site."
|
||||
},
|
||||
"PERMISSION_REQUEST_BODY": {
|
||||
"LABEL": "Call permission request message",
|
||||
"HELP_TEXT": "Shown to the contact when they haven't yet consented to receive calls. Leave blank to use the default.",
|
||||
"PLACEHOLDER": "We would like to call you regarding your conversation."
|
||||
}
|
||||
},
|
||||
"CHANNEL_PREFERENCES": "የቻናል ቅድሚያዎች",
|
||||
"WIDGET_FEATURES": "የዊጅት ባህሪያት",
|
||||
"ACCOUNT_HEALTH": {
|
||||
@@ -800,6 +827,10 @@
|
||||
"WHATSAPP_TEMPLATES_SYNC_SUBHEADER": "ከWhatsApp መልእክት አብነቶች እጅግ በእጅ ማስተካከል ለእንደገና የሚገኙ አብነቶችን ያዘምኑ።.",
|
||||
"WHATSAPP_TEMPLATES_SYNC_BUTTON": "አብነቶችን ያዘምኑ",
|
||||
"WHATSAPP_TEMPLATES_SYNC_SUCCESS": "የአብነት ማስተካከያ በተሳካ ሁኔታ ተጀምሯል። ለማዘመን ጥቂት ደቂቃዎች ሊወስድ ይችላል።.",
|
||||
"WHATSAPP_CALLING_ENABLED": {
|
||||
"LABEL": "Enable voice calling",
|
||||
"DESCRIPTION": "Allow agents to start and receive WhatsApp voice calls on this inbox. Available only on embedded-signup WhatsApp Cloud channels with calling permission granted by Meta."
|
||||
},
|
||||
"UPDATE_PRE_CHAT_FORM_SETTINGS": "የቀደም ቻት ቅጥያ ቅንብሮችን አዘምን"
|
||||
},
|
||||
"HELP_CENTER": {
|
||||
|
||||
@@ -57,7 +57,8 @@
|
||||
"CONTACT_CREATED": "እውቂያ ተፈጥሯል",
|
||||
"CONTACT_UPDATED": "እውቂያ ተሻሽሏል",
|
||||
"CONVERSATION_TYPING_ON": "ውይይት ማስተካከያ በተጠቃሚ ላይ ነው",
|
||||
"CONVERSATION_TYPING_OFF": "ውይይት ማስተካከያ ከተጠቃሚ ላይ አልተጠቀሰም"
|
||||
"CONVERSATION_TYPING_OFF": "ውይይት ማስተካከያ ከተጠቃሚ ላይ አልተጠቀሰም",
|
||||
"INBOX_UPDATED": "Inbox updated"
|
||||
}
|
||||
},
|
||||
"NAME": {
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
"OR": "أو"
|
||||
},
|
||||
"INPUT_PLACEHOLDER": "أدخل القيمة",
|
||||
"CONTACT_SEARCH_PLACEHOLDER": "Search contacts",
|
||||
"CONTACT_FALLBACK": "Contact #{id}",
|
||||
"OPERATOR_LABELS": {
|
||||
"equal_to": "يساوي",
|
||||
"not_equal_to": "لا يساوي",
|
||||
@@ -49,6 +51,7 @@
|
||||
"ASSIGNEE_NAME": "اسم المكلَّف",
|
||||
"INBOX_NAME": "اسم صندوق الوارد",
|
||||
"TEAM_NAME": "اسم الفريق",
|
||||
"CONTACT": "جهات الاتصال",
|
||||
"CONVERSATION_IDENTIFIER": "معرف المحادثة",
|
||||
"CAMPAIGN_NAME": "اسم الحملة",
|
||||
"LABELS": "الوسوم",
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
{
|
||||
"BULK_ACTION": {
|
||||
"CONVERSATIONS_SELECTED": "{conversationCount} المحادثات المحددة",
|
||||
"AGENT_SELECT_LABEL": "اختر وكيل",
|
||||
"ASSIGN_CONFIRMATION_LABEL": "هل أنت متأكد من أنك تريد تعيين {conversationCount} {conversationLabel} إلى",
|
||||
"UNASSIGN_CONFIRMATION_LABEL": "هل أنت متأكد من إلغاء تعيين {conversationCount} {conversationLabel}؟",
|
||||
"GO_BACK_LABEL": "العودة للخلف",
|
||||
"ASSIGN_LABEL": "تكليف",
|
||||
"NONE": "لا شيء",
|
||||
"CLEAR_SELECTION": "مسح",
|
||||
"ASSIGN_AGENT_CONFIRMATION_LABEL": "Are you sure you want to assign {n} conversation to {agentName}? | Are you sure you want to assign {n} conversations to {agentName}?",
|
||||
@@ -20,7 +15,6 @@
|
||||
"RESOLVE_SUCCESFUL": "تم تسوية المحادثات بنجاح.",
|
||||
"RESOLVE_FAILED": "فشل في حل المحادثات، يرجى المحاولة مرة أخرى.",
|
||||
"ALL_CONVERSATIONS_SELECTED_ALERT": "المحادثات المرئية في هذه الصفحة هي المحددة فقط.",
|
||||
"AGENT_LIST_LOADING": "جاري جلب الوكلاء",
|
||||
"UPDATE": {
|
||||
"CHANGE_STATUS": "تغيير الحالة",
|
||||
"SNOOZE_UNTIL": "تأجيل",
|
||||
@@ -33,16 +27,16 @@
|
||||
},
|
||||
"LABELS": {
|
||||
"ASSIGN_LABELS": "إضافة وسم",
|
||||
"NO_LABELS_FOUND": "لم يتم العثور على تصنيفات",
|
||||
"REMOVE_LABELS": "Remove labels",
|
||||
"ASSIGN_SELECTED_LABELS": "تعيين التسميات المحددة",
|
||||
"REMOVE_SELECTED_LABELS": "Remove selected labels",
|
||||
"ASSIGN_SUCCESFUL": "تم تعيين التسميات بنجاح.",
|
||||
"ASSIGN_FAILED": "فشل في تعيين التسميات ، الرجاء المحاولة مرة أخرى."
|
||||
"ASSIGN_FAILED": "فشل في تعيين التسميات ، الرجاء المحاولة مرة أخرى.",
|
||||
"REMOVE_SUCCESFUL": "Labels removed successfully.",
|
||||
"REMOVE_FAILED": "Failed to remove labels. Please try again."
|
||||
},
|
||||
"TEAMS": {
|
||||
"TEAM_SELECT_LABEL": "اختر فريق",
|
||||
"NONE": "لا شيء",
|
||||
"NO_TEAMS_AVAILABLE": "لا توجد فرق مضافة إلى هذا الحساب حتى الآن.",
|
||||
"ASSIGN_SELECTED_TEAMS": "تعيين فريق محدد.",
|
||||
"ASSIGN_TEAM_CONFIRMATION_LABEL": "Are you sure you want to assign {n} conversation to {teamName}? | Are you sure you want to assign {n} conversations to {teamName}?",
|
||||
"UNASSIGN_TEAM_CONFIRMATION_LABEL": "Are you sure you want to unassign {n} conversation? | Are you sure you want to unassign {n} conversations?",
|
||||
"ASSIGN_SUCCESFUL": "تم تعيين الفرق بنجاح.",
|
||||
|
||||
@@ -88,6 +88,7 @@
|
||||
},
|
||||
"CARD": {
|
||||
"STATUS": {
|
||||
"PROCESSING": "Processing",
|
||||
"COMPLETED": "مكتمل",
|
||||
"SCHEDULED": "Scheduled"
|
||||
},
|
||||
@@ -146,6 +147,7 @@
|
||||
},
|
||||
"CARD": {
|
||||
"STATUS": {
|
||||
"PROCESSING": "Processing",
|
||||
"COMPLETED": "مكتمل",
|
||||
"SCHEDULED": "Scheduled"
|
||||
},
|
||||
|
||||
@@ -63,6 +63,7 @@
|
||||
"CODE": "Code",
|
||||
"BULLET_LIST": "Bullet List",
|
||||
"ORDERED_LIST": "Ordered List",
|
||||
"TABLE": "Table"
|
||||
"TABLE": "Table",
|
||||
"IMAGE": "Image"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
"CALL": "Call",
|
||||
"CALL_INITIATED": "جار الاتصال بجهة الاتصال…",
|
||||
"CALL_FAILED": "تعذر بدء المكالمة. الرجاء المحاولة مرة أخرى.",
|
||||
"WHATSAPP_CALL_PERMISSION_REQUESTED": "Sent a call permission request to the contact. Try again once they accept.",
|
||||
"WHATSAPP_CALL_PERMISSION_PENDING": "Call permission request already sent recently. Try again once the contact accepts.",
|
||||
"CLICK_TO_EDIT": "Click to edit",
|
||||
"VOICE_INBOX_PICKER": {
|
||||
"TITLE": "Choose a voice inbox"
|
||||
@@ -583,8 +585,11 @@
|
||||
},
|
||||
"CONTACTS_BULK_ACTIONS": {
|
||||
"ASSIGN_LABELS": "تعيين التسميات",
|
||||
"REMOVE_LABELS": "Remove Labels",
|
||||
"ASSIGN_LABELS_SUCCESS": "تم تعيين التسميات بنجاح.",
|
||||
"ASSIGN_LABELS_FAILED": "Failed to assign labels",
|
||||
"REMOVE_LABELS_SUCCESS": "Labels removed successfully.",
|
||||
"REMOVE_LABELS_FAILED": "Failed to remove labels",
|
||||
"DESCRIPTION": "Select the labels you want to add to the selected contacts.",
|
||||
"NO_LABELS_FOUND": "No labels available yet.",
|
||||
"SELECTED_COUNT": "{count} selected",
|
||||
|
||||
@@ -62,6 +62,7 @@
|
||||
"UNSUPPORTED_MESSAGE_FACEBOOK": "هذه الرسالة غير مدعومة، يمكنك مشاهدة هذه الرسالة على تطبيق فيسبوك (Messenger).",
|
||||
"UNSUPPORTED_MESSAGE_INSTAGRAM": "هذه الرسالة غير مدعومة، يمكنك عرض هذه الرسالة على تطبيق Instagram.",
|
||||
"UNSUPPORTED_MESSAGE_TIKTOK": "هذه الرسالة غير مدعومة. يمكنك مشاهدة هذه الرسالة على تطبيق TikTok.",
|
||||
"UNSUPPORTED_MESSAGE_WHATSAPP": "This message is unsupported. You can view this message on the WhatsApp app.",
|
||||
"SUCCESS_DELETE_MESSAGE": "تم حذف الرسالة بنجاح",
|
||||
"FAIL_DELETE_MESSSAGE": "تعذر حذف الرسالة! حاول مرة أخرى",
|
||||
"NO_RESPONSE": "لا توجد استجابة",
|
||||
@@ -79,13 +80,22 @@
|
||||
"OUTGOING_CALL": "Outgoing call",
|
||||
"CALL_IN_PROGRESS": "Call in progress",
|
||||
"NO_ANSWER": "No answer",
|
||||
"NO_ANSWER_OUTBOUND_LABEL": "No answer",
|
||||
"NO_ANSWER_OUTBOUND_SUBTEXT": "Contact didn't pick up",
|
||||
"MISSED_CALL": "Missed call",
|
||||
"MISSED_CALL_INBOUND_SUBTEXT": "No agent picked up",
|
||||
"MISSED_CALL_DECLINED_BY": "Declined by {agentName}",
|
||||
"CALL_ENDED": "Call ended",
|
||||
"HANDLED_BY": "Handled by {agentName}",
|
||||
"NOT_ANSWERED_YET": "Not answered yet",
|
||||
"CALLING": "Calling…",
|
||||
"THEY_ANSWERED": "They answered",
|
||||
"YOU_ANSWERED": "You answered",
|
||||
"AGENT_ANSWERED": "{agentName} answered",
|
||||
"JOIN_CALL": "انضم إلى المكالمة"
|
||||
"JOIN_CALL": "انضم إلى المكالمة",
|
||||
"CALL_BACK": "Call back",
|
||||
"TRANSCRIPT_SHOW_MORE": "Show more",
|
||||
"TRANSCRIPT_SHOW_LESS": "Show less"
|
||||
},
|
||||
"HEADER": {
|
||||
"RESOLVE_ACTION": "حل المحادثة",
|
||||
@@ -100,6 +110,12 @@
|
||||
"SNOOZED_UNTIL_TOMORROW": "تأجيل حتى الغد",
|
||||
"SNOOZED_UNTIL_NEXT_WEEK": "تأجيل حتى الأسبوع القادم",
|
||||
"SNOOZED_UNTIL_NEXT_REPLY": "تأجيل حتى الرد التالي",
|
||||
"WHATSAPP_CALL": "Start WhatsApp call",
|
||||
"WHATSAPP_CALL_FAILED": "Could not start the WhatsApp call.",
|
||||
"VOICE_CALL": "Start call",
|
||||
"VOICE_CALL_FAILED": "Could not start the call.",
|
||||
"WHATSAPP_CALL_PERMISSION_REQUESTED": "Sent a call permission request to the contact. Try again once they accept.",
|
||||
"WHATSAPP_CALL_PERMISSION_PENDING": "Call permission request already sent recently. Try again once the contact accepts.",
|
||||
"SLA_STATUS": {
|
||||
"FRT": "FRT {status}",
|
||||
"NRT": "NRT {status}",
|
||||
@@ -215,7 +231,9 @@
|
||||
"TIP_AUDIORECORDER_ICON": "تسجيل الصوت",
|
||||
"TIP_AUDIORECORDER_PERMISSION": "السماح بالوصول إلى الصوت",
|
||||
"TIP_AUDIORECORDER_ERROR": "تعذر فتح الصوت",
|
||||
"AUDIO_CONVERSION_FAILED": "Audio conversion failed. Please try again.",
|
||||
"DRAG_DROP": "اسحب و أسقط هنا للإرفاق",
|
||||
"IMAGE_UPLOAD_SUCCESS": "تم رفع الصورة بنجاح",
|
||||
"START_AUDIO_RECORDING": "بدء التسجيل الصوتي",
|
||||
"STOP_AUDIO_RECORDING": "إيقاف التسجيل الصوتي",
|
||||
"COPILOT_THINKING": "Copilot يفكر",
|
||||
@@ -299,8 +317,13 @@
|
||||
"NOT_ANSWERED_YET": "لم يتم الرد بعد",
|
||||
"HANDLED_IN_ANOTHER_TAB": "يتم التعامل معها في علامة تبويب أخرى",
|
||||
"REJECT_CALL": "رفض",
|
||||
"DISMISS_CALL": "تجاهل",
|
||||
"JOIN_CALL": "انضم إلى المكالمة",
|
||||
"END_CALL": "إنهاء المكالمة"
|
||||
"END_CALL": "إنهاء المكالمة",
|
||||
"MUTE": "Mute mic",
|
||||
"UNMUTE": "Unmute mic",
|
||||
"VIEW_CHAT_HISTORY": "View chat history",
|
||||
"GO_TO_CONVERSATION": "Go to conversation thread"
|
||||
}
|
||||
},
|
||||
"EMAIL_TRANSCRIPT": {
|
||||
|
||||
@@ -607,9 +607,12 @@
|
||||
"DRAFT": "مسودة",
|
||||
"ARCHIVE": "Archive",
|
||||
"TRANSLATE": "ترجم",
|
||||
"MOVE_TO_CATEGORY": "الفئة",
|
||||
"DELETE": "حذف",
|
||||
"STATUS_SUCCESS": "Articles updated successfully",
|
||||
"STATUS_ERROR": "Failed to update articles",
|
||||
"CATEGORY_SUCCESS": "Articles moved successfully",
|
||||
"CATEGORY_ERROR": "Failed to move articles",
|
||||
"DELETE_CONFIRM_TITLE": "Delete article | Delete {count} articles",
|
||||
"DELETE_CONFIRM_DESCRIPTION": "This will permanently delete the selected article. This action cannot be undone. | This will permanently delete {count} selected articles. This action cannot be undone.",
|
||||
"DELETE_CONFIRM": "حذف",
|
||||
@@ -697,9 +700,27 @@
|
||||
"MAKE_DEFAULT": "Make default",
|
||||
"MOVE_TO_DRAFT": "Move to draft",
|
||||
"PUBLISH_LOCALE": "Publish locale",
|
||||
"CUSTOMIZE_CONTENT": "Localize content",
|
||||
"DELETE": "حذف"
|
||||
}
|
||||
},
|
||||
"CONTENT_DIALOG": {
|
||||
"TITLE": "Localize content",
|
||||
"DESCRIPTION": "Set values specific to this locale. Anything left blank falls back to the default locale.",
|
||||
"NAME": {
|
||||
"LABEL": "الاسم"
|
||||
},
|
||||
"PAGE_TITLE": {
|
||||
"LABEL": "Page title"
|
||||
},
|
||||
"HEADER_TEXT": {
|
||||
"LABEL": "Header text"
|
||||
},
|
||||
"API": {
|
||||
"SUCCESS_MESSAGE": "Locale content updated successfully",
|
||||
"ERROR_MESSAGE": "Unable to update locale content. Try again."
|
||||
}
|
||||
},
|
||||
"ADD_LOCALE_DIALOG": {
|
||||
"TITLE": "إضافة لغة جديدة",
|
||||
"DESCRIPTION": "Select the language in which this article will be written. This will be added to your list of translations, and you can add more later.",
|
||||
@@ -866,6 +887,28 @@
|
||||
},
|
||||
"EDIT_CONFIGURATION": "Edit configuration"
|
||||
},
|
||||
"LAYOUT_CONTENT": {
|
||||
"HEADER": "المظهر",
|
||||
"DESCRIPTION": "Pick the layout that fits how your visitors read.",
|
||||
"LAYOUT": {
|
||||
"CLASSIC": {
|
||||
"TITLE": "Classic",
|
||||
"DESCRIPTION": "A welcoming home page with search and featured topics."
|
||||
},
|
||||
"SIDEBAR": {
|
||||
"TITLE": "Documentation",
|
||||
"DESCRIPTION": "Side-by-side navigation that keeps every guide a click away."
|
||||
}
|
||||
},
|
||||
"SOCIAL_LINKS": {
|
||||
"HEADER": "Social links",
|
||||
"DESCRIPTION": "Add the handle for each network and your help center builds the full link. Shown in the documentation layout footer.",
|
||||
"PLACEHOLDER": "handle",
|
||||
"ADD": "Add social link",
|
||||
"REMOVE": "حذف"
|
||||
},
|
||||
"SAVE": "Save changes"
|
||||
},
|
||||
"API": {
|
||||
"CREATE_PORTAL": {
|
||||
"SUCCESS_MESSAGE": "تم إنشاء البوابة بنجاح",
|
||||
|
||||
@@ -308,6 +308,7 @@
|
||||
"AUTH_PROCESSING": "Authenticating with Meta",
|
||||
"WAITING_FOR_BUSINESS_INFO": "Please complete business setup in the Meta window...",
|
||||
"PROCESSING": "Setting up your WhatsApp Business Account",
|
||||
"ENABLING_CALLING": "Enabling WhatsApp Calling on your number…",
|
||||
"LOADING_SDK": "Loading Facebook SDK...",
|
||||
"CANCELLED": "WhatsApp Signup was cancelled",
|
||||
"SUCCESS_TITLE": "WhatsApp Business Account Connected!",
|
||||
@@ -317,7 +318,8 @@
|
||||
"AUTH_NOT_COMPLETED": "Authentication not completed. Please restart the process.",
|
||||
"SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured",
|
||||
"MANUAL_FALLBACK": "If your number is already connected to the WhatsApp Business Platform (API), or if you’re a tech provider onboarding your own number, please use the {link} flow",
|
||||
"MANUAL_LINK_TEXT": "manual setup flow"
|
||||
"MANUAL_LINK_TEXT": "manual setup flow",
|
||||
"CALLING_ENABLE_FAILED": "Your WhatsApp inbox is ready, but voice calling couldn't be turned on — this number isn't enrolled in the WhatsApp Business Calling API yet. Reach out to Meta or your WhatsApp Business Solution Provider to onboard it, then turn calling on from the inbox's Calls settings."
|
||||
},
|
||||
"API": {
|
||||
"ERROR_MESSAGE": "لم نتمكن من حفظ قناة واتساب"
|
||||
@@ -465,6 +467,10 @@
|
||||
"TITLE": "واتساب",
|
||||
"DESCRIPTION": "Support your customers on WhatsApp"
|
||||
},
|
||||
"WHATSAPP_CALL": {
|
||||
"TITLE": "WhatsApp Call",
|
||||
"DESCRIPTION": "Take voice calls on your WhatsApp number"
|
||||
},
|
||||
"EMAIL": {
|
||||
"TITLE": "البريد الإلكتروني",
|
||||
"DESCRIPTION": "Connect with Gmail, Outlook, or other providers"
|
||||
@@ -637,7 +643,8 @@
|
||||
"BOT_CONFIGURATION": "اعدادات البوت",
|
||||
"ACCOUNT_HEALTH": "Account Health",
|
||||
"CSAT": "تقييم رضاء العملاء",
|
||||
"VOICE": "Voice"
|
||||
"VOICE": "Voice",
|
||||
"CALLS": "Calls"
|
||||
},
|
||||
"VOICE_CONFIGURATION": {
|
||||
"ENABLE_VOICE": {
|
||||
@@ -648,6 +655,26 @@
|
||||
"DESCRIPTION": "Voice calling requires Twilio API Key credentials. These are used to generate tokens for agent voice connections."
|
||||
}
|
||||
},
|
||||
"WHATSAPP_CALLING": {
|
||||
"ENABLE": {
|
||||
"LABEL": "Enable WhatsApp Calling",
|
||||
"DESCRIPTION": "Allow agents to receive and place WhatsApp Cloud calls on this inbox. Customers can call this business number directly from WhatsApp."
|
||||
},
|
||||
"ENABLE_FAILED": "Voice calling couldn't be turned on for this number — it isn't enrolled in the WhatsApp Business Calling API yet. Reach out to Meta or your WhatsApp Business Solution Provider to onboard it, then try again.",
|
||||
"PHONE_NUMBER": {
|
||||
"LABEL": "Business phone number",
|
||||
"HELP_TEXT": "WhatsApp number that customers will call."
|
||||
},
|
||||
"HOW_IT_WORKS": {
|
||||
"LABEL": "How it works",
|
||||
"DESCRIPTION": "Calls are placed peer-to-peer between the agent's browser and Meta — no extra credentials are required. Make sure the agent's browser has microphone permission for this site."
|
||||
},
|
||||
"PERMISSION_REQUEST_BODY": {
|
||||
"LABEL": "Call permission request message",
|
||||
"HELP_TEXT": "Shown to the contact when they haven't yet consented to receive calls. Leave blank to use the default.",
|
||||
"PLACEHOLDER": "We would like to call you regarding your conversation."
|
||||
}
|
||||
},
|
||||
"CHANNEL_PREFERENCES": "Channel Preferences",
|
||||
"WIDGET_FEATURES": "Widget features",
|
||||
"ACCOUNT_HEALTH": {
|
||||
@@ -800,6 +827,10 @@
|
||||
"WHATSAPP_TEMPLATES_SYNC_SUBHEADER": "Manually sync message templates from WhatsApp to update your available templates.",
|
||||
"WHATSAPP_TEMPLATES_SYNC_BUTTON": "Sync Templates",
|
||||
"WHATSAPP_TEMPLATES_SYNC_SUCCESS": "Templates sync initiated successfully. It may take a couple of minutes to update.",
|
||||
"WHATSAPP_CALLING_ENABLED": {
|
||||
"LABEL": "Enable voice calling",
|
||||
"DESCRIPTION": "Allow agents to start and receive WhatsApp voice calls on this inbox. Available only on embedded-signup WhatsApp Cloud channels with calling permission granted by Meta."
|
||||
},
|
||||
"UPDATE_PRE_CHAT_FORM_SETTINGS": "Update Pre Chat Form Settings"
|
||||
},
|
||||
"HELP_CENTER": {
|
||||
|
||||
@@ -57,7 +57,8 @@
|
||||
"CONTACT_CREATED": "Contact created",
|
||||
"CONTACT_UPDATED": "Contact updated",
|
||||
"CONVERSATION_TYPING_ON": "Conversation Typing On",
|
||||
"CONVERSATION_TYPING_OFF": "Conversation Typing Off"
|
||||
"CONVERSATION_TYPING_OFF": "Conversation Typing Off",
|
||||
"INBOX_UPDATED": "Inbox updated"
|
||||
}
|
||||
},
|
||||
"NAME": {
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
"OR": "OR"
|
||||
},
|
||||
"INPUT_PLACEHOLDER": "Enter value",
|
||||
"CONTACT_SEARCH_PLACEHOLDER": "Əlaqələrdə axtarış",
|
||||
"CONTACT_FALLBACK": "Contact #{id}",
|
||||
"OPERATOR_LABELS": {
|
||||
"equal_to": "Equal to",
|
||||
"not_equal_to": "Not equal to",
|
||||
@@ -49,6 +51,7 @@
|
||||
"ASSIGNEE_NAME": "Assignee name",
|
||||
"INBOX_NAME": "Inbox name",
|
||||
"TEAM_NAME": "Team name",
|
||||
"CONTACT": "Əlaqə",
|
||||
"CONVERSATION_IDENTIFIER": "Conversation identifier",
|
||||
"CAMPAIGN_NAME": "Campaign name",
|
||||
"LABELS": "Labels",
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
{
|
||||
"BULK_ACTION": {
|
||||
"CONVERSATIONS_SELECTED": "{conversationCount} conversations selected",
|
||||
"AGENT_SELECT_LABEL": "Select agent",
|
||||
"ASSIGN_CONFIRMATION_LABEL": "Are you sure to assign {conversationCount} {conversationLabel} to",
|
||||
"UNASSIGN_CONFIRMATION_LABEL": "Are you sure to unassign {conversationCount} {conversationLabel}?",
|
||||
"GO_BACK_LABEL": "Go back",
|
||||
"ASSIGN_LABEL": "Assign",
|
||||
"NONE": "Heç biri",
|
||||
"CLEAR_SELECTION": "Clear",
|
||||
"ASSIGN_AGENT_CONFIRMATION_LABEL": "Are you sure you want to assign {n} conversation to {agentName}? | Are you sure you want to assign {n} conversations to {agentName}?",
|
||||
@@ -20,7 +15,6 @@
|
||||
"RESOLVE_SUCCESFUL": "Conversations resolved successfully.",
|
||||
"RESOLVE_FAILED": "Failed to resolve conversations. Please try again.",
|
||||
"ALL_CONVERSATIONS_SELECTED_ALERT": "Conversations visible on this page are only selected.",
|
||||
"AGENT_LIST_LOADING": "Loading agents",
|
||||
"UPDATE": {
|
||||
"CHANGE_STATUS": "Change status",
|
||||
"SNOOZE_UNTIL": "Snooze",
|
||||
@@ -33,16 +27,16 @@
|
||||
},
|
||||
"LABELS": {
|
||||
"ASSIGN_LABELS": "Assign labels",
|
||||
"NO_LABELS_FOUND": "No labels found",
|
||||
"REMOVE_LABELS": "Remove labels",
|
||||
"ASSIGN_SELECTED_LABELS": "Assign selected labels",
|
||||
"REMOVE_SELECTED_LABELS": "Remove selected labels",
|
||||
"ASSIGN_SUCCESFUL": "Labels assigned successfully.",
|
||||
"ASSIGN_FAILED": "Failed to assign labels. Please try again."
|
||||
"ASSIGN_FAILED": "Failed to assign labels. Please try again.",
|
||||
"REMOVE_SUCCESFUL": "Labels removed successfully.",
|
||||
"REMOVE_FAILED": "Failed to remove labels. Please try again."
|
||||
},
|
||||
"TEAMS": {
|
||||
"TEAM_SELECT_LABEL": "Select team",
|
||||
"NONE": "None",
|
||||
"NO_TEAMS_AVAILABLE": "There are no teams added to this account yet.",
|
||||
"ASSIGN_SELECTED_TEAMS": "Assign selected team.",
|
||||
"ASSIGN_TEAM_CONFIRMATION_LABEL": "Are you sure you want to assign {n} conversation to {teamName}? | Are you sure you want to assign {n} conversations to {teamName}?",
|
||||
"UNASSIGN_TEAM_CONFIRMATION_LABEL": "Are you sure you want to unassign {n} conversation? | Are you sure you want to unassign {n} conversations?",
|
||||
"ASSIGN_SUCCESFUL": "Teams assigned successfully.",
|
||||
|
||||
@@ -88,6 +88,7 @@
|
||||
},
|
||||
"CARD": {
|
||||
"STATUS": {
|
||||
"PROCESSING": "Processing",
|
||||
"COMPLETED": "Completed",
|
||||
"SCHEDULED": "Scheduled"
|
||||
},
|
||||
@@ -146,6 +147,7 @@
|
||||
},
|
||||
"CARD": {
|
||||
"STATUS": {
|
||||
"PROCESSING": "Processing",
|
||||
"COMPLETED": "Completed",
|
||||
"SCHEDULED": "Scheduled"
|
||||
},
|
||||
|
||||
@@ -63,6 +63,7 @@
|
||||
"CODE": "Code",
|
||||
"BULLET_LIST": "Bullet List",
|
||||
"ORDERED_LIST": "Ordered List",
|
||||
"TABLE": "Table"
|
||||
"TABLE": "Table",
|
||||
"IMAGE": "Image"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
"CALL": "Zəng et",
|
||||
"CALL_INITIATED": "Calling the contact…",
|
||||
"CALL_FAILED": "Unable to start the call. Please try again.",
|
||||
"WHATSAPP_CALL_PERMISSION_REQUESTED": "Sent a call permission request to the contact. Try again once they accept.",
|
||||
"WHATSAPP_CALL_PERMISSION_PENDING": "Call permission request already sent recently. Try again once the contact accepts.",
|
||||
"CLICK_TO_EDIT": "Click to edit",
|
||||
"VOICE_INBOX_PICKER": {
|
||||
"TITLE": "Choose a voice inbox"
|
||||
@@ -583,8 +585,11 @@
|
||||
},
|
||||
"CONTACTS_BULK_ACTIONS": {
|
||||
"ASSIGN_LABELS": "Assign Labels",
|
||||
"REMOVE_LABELS": "Remove Labels",
|
||||
"ASSIGN_LABELS_SUCCESS": "Labels assigned successfully.",
|
||||
"ASSIGN_LABELS_FAILED": "Failed to assign labels",
|
||||
"REMOVE_LABELS_SUCCESS": "Labels removed successfully.",
|
||||
"REMOVE_LABELS_FAILED": "Failed to remove labels",
|
||||
"DESCRIPTION": "Select the labels you want to add to the selected contacts.",
|
||||
"NO_LABELS_FOUND": "No labels available yet.",
|
||||
"SELECTED_COUNT": "{count} seçildi",
|
||||
|
||||
@@ -62,6 +62,7 @@
|
||||
"UNSUPPORTED_MESSAGE_FACEBOOK": "Bu mesaj dəstəklənmir. Bu mesajı Facebook Messenger tətbiqində görə bilərsiniz.",
|
||||
"UNSUPPORTED_MESSAGE_INSTAGRAM": "Bu mesaj dəstəklənmir. Bu mesajı Instagram tətbiqində görə bilərsiniz.",
|
||||
"UNSUPPORTED_MESSAGE_TIKTOK": "Bu mesaj dəstəklənmir. Bu mesajı TikTok tətbiqində görə bilərsiniz.",
|
||||
"UNSUPPORTED_MESSAGE_WHATSAPP": "This message is unsupported. You can view this message on the WhatsApp app.",
|
||||
"SUCCESS_DELETE_MESSAGE": "Mesaj uğurla silindi",
|
||||
"FAIL_DELETE_MESSSAGE": "Mesajı silmək mümkün olmadı! Yenidən cəhd edin",
|
||||
"NO_RESPONSE": "Cavab yoxdur",
|
||||
@@ -79,13 +80,22 @@
|
||||
"OUTGOING_CALL": "Gedən zəng",
|
||||
"CALL_IN_PROGRESS": "Zəng davam edir",
|
||||
"NO_ANSWER": "Cavab yoxdur",
|
||||
"NO_ANSWER_OUTBOUND_LABEL": "Cavab yoxdur",
|
||||
"NO_ANSWER_OUTBOUND_SUBTEXT": "Contact didn't pick up",
|
||||
"MISSED_CALL": "Qeyri-işlək zəng",
|
||||
"MISSED_CALL_INBOUND_SUBTEXT": "No agent picked up",
|
||||
"MISSED_CALL_DECLINED_BY": "Declined by {agentName}",
|
||||
"CALL_ENDED": "Zəng bitdi",
|
||||
"HANDLED_BY": "Handled by {agentName}",
|
||||
"NOT_ANSWERED_YET": "Hələ cavab verilməyib",
|
||||
"CALLING": "Calling…",
|
||||
"THEY_ANSWERED": "Onlar cavab verdi",
|
||||
"YOU_ANSWERED": "Siz cavab verdiniz",
|
||||
"AGENT_ANSWERED": "{agentName} answered",
|
||||
"JOIN_CALL": "Zəngə qoşul"
|
||||
"JOIN_CALL": "Zəngə qoşul",
|
||||
"CALL_BACK": "Call back",
|
||||
"TRANSCRIPT_SHOW_MORE": "Show more",
|
||||
"TRANSCRIPT_SHOW_LESS": "Show less"
|
||||
},
|
||||
"HEADER": {
|
||||
"RESOLVE_ACTION": "Həll et",
|
||||
@@ -100,6 +110,12 @@
|
||||
"SNOOZED_UNTIL_TOMORROW": "Sabaha qədər təxirə salındı",
|
||||
"SNOOZED_UNTIL_NEXT_WEEK": "Gələn həftəyə qədər təxirə salındı",
|
||||
"SNOOZED_UNTIL_NEXT_REPLY": "Növbəti cavaba qədər təxirə salındı",
|
||||
"WHATSAPP_CALL": "Start WhatsApp call",
|
||||
"WHATSAPP_CALL_FAILED": "Could not start the WhatsApp call.",
|
||||
"VOICE_CALL": "Start call",
|
||||
"VOICE_CALL_FAILED": "Could not start the call.",
|
||||
"WHATSAPP_CALL_PERMISSION_REQUESTED": "Sent a call permission request to the contact. Try again once they accept.",
|
||||
"WHATSAPP_CALL_PERMISSION_PENDING": "Call permission request already sent recently. Try again once the contact accepts.",
|
||||
"SLA_STATUS": {
|
||||
"FRT": "FRT {status}",
|
||||
"NRT": "NRT {status}",
|
||||
@@ -215,7 +231,9 @@
|
||||
"TIP_AUDIORECORDER_ICON": "Səs yaz",
|
||||
"TIP_AUDIORECORDER_PERMISSION": "Səsə girişə icazə ver",
|
||||
"TIP_AUDIORECORDER_ERROR": "Səsi açmaq mümkün olmadı",
|
||||
"AUDIO_CONVERSION_FAILED": "Audio conversion failed. Please try again.",
|
||||
"DRAG_DROP": "Qoşmaq üçün buraya sürükləyin və buraxın",
|
||||
"IMAGE_UPLOAD_SUCCESS": "Şəkil uğurla yükləndi",
|
||||
"START_AUDIO_RECORDING": "Səs yazısını başla",
|
||||
"STOP_AUDIO_RECORDING": "Səs yazısını dayandırın",
|
||||
"COPILOT_THINKING": "Copilot düşünür",
|
||||
@@ -299,8 +317,13 @@
|
||||
"NOT_ANSWERED_YET": "Hələ cavab verilməyib",
|
||||
"HANDLED_IN_ANOTHER_TAB": "Başqa sekmədə işlənir",
|
||||
"REJECT_CALL": "İmtina et",
|
||||
"DISMISS_CALL": "Dismiss",
|
||||
"JOIN_CALL": "Zəngə qoşul",
|
||||
"END_CALL": "Zəngi bitir"
|
||||
"END_CALL": "Zəngi bitir",
|
||||
"MUTE": "Mute mic",
|
||||
"UNMUTE": "Unmute mic",
|
||||
"VIEW_CHAT_HISTORY": "View chat history",
|
||||
"GO_TO_CONVERSATION": "Go to conversation thread"
|
||||
}
|
||||
},
|
||||
"EMAIL_TRANSCRIPT": {
|
||||
|
||||
@@ -607,9 +607,12 @@
|
||||
"DRAFT": "Qaralama",
|
||||
"ARCHIVE": "Archive",
|
||||
"TRANSLATE": "Tərcümə et",
|
||||
"MOVE_TO_CATEGORY": "Category",
|
||||
"DELETE": "Delete",
|
||||
"STATUS_SUCCESS": "Articles updated successfully",
|
||||
"STATUS_ERROR": "Failed to update articles",
|
||||
"CATEGORY_SUCCESS": "Articles moved successfully",
|
||||
"CATEGORY_ERROR": "Failed to move articles",
|
||||
"DELETE_CONFIRM_TITLE": "Delete article | Delete {count} articles",
|
||||
"DELETE_CONFIRM_DESCRIPTION": "This will permanently delete the selected article. This action cannot be undone. | This will permanently delete {count} selected articles. This action cannot be undone.",
|
||||
"DELETE_CONFIRM": "Delete",
|
||||
@@ -697,9 +700,27 @@
|
||||
"MAKE_DEFAULT": "Make default",
|
||||
"MOVE_TO_DRAFT": "Move to draft",
|
||||
"PUBLISH_LOCALE": "Publish locale",
|
||||
"CUSTOMIZE_CONTENT": "Localize content",
|
||||
"DELETE": "Delete"
|
||||
}
|
||||
},
|
||||
"CONTENT_DIALOG": {
|
||||
"TITLE": "Localize content",
|
||||
"DESCRIPTION": "Set values specific to this locale. Anything left blank falls back to the default locale.",
|
||||
"NAME": {
|
||||
"LABEL": "Name"
|
||||
},
|
||||
"PAGE_TITLE": {
|
||||
"LABEL": "Page title"
|
||||
},
|
||||
"HEADER_TEXT": {
|
||||
"LABEL": "Header text"
|
||||
},
|
||||
"API": {
|
||||
"SUCCESS_MESSAGE": "Locale content updated successfully",
|
||||
"ERROR_MESSAGE": "Unable to update locale content. Try again."
|
||||
}
|
||||
},
|
||||
"ADD_LOCALE_DIALOG": {
|
||||
"TITLE": "Add a new locale",
|
||||
"DESCRIPTION": "Select the language in which this article will be written. This will be added to your list of translations, and you can add more later.",
|
||||
@@ -866,6 +887,28 @@
|
||||
},
|
||||
"EDIT_CONFIGURATION": "Edit configuration"
|
||||
},
|
||||
"LAYOUT_CONTENT": {
|
||||
"HEADER": "Appearance",
|
||||
"DESCRIPTION": "Pick the layout that fits how your visitors read.",
|
||||
"LAYOUT": {
|
||||
"CLASSIC": {
|
||||
"TITLE": "Classic",
|
||||
"DESCRIPTION": "A welcoming home page with search and featured topics."
|
||||
},
|
||||
"SIDEBAR": {
|
||||
"TITLE": "Documentation",
|
||||
"DESCRIPTION": "Side-by-side navigation that keeps every guide a click away."
|
||||
}
|
||||
},
|
||||
"SOCIAL_LINKS": {
|
||||
"HEADER": "Social links",
|
||||
"DESCRIPTION": "Add the handle for each network and your help center builds the full link. Shown in the documentation layout footer.",
|
||||
"PLACEHOLDER": "handle",
|
||||
"ADD": "Add social link",
|
||||
"REMOVE": "Sil"
|
||||
},
|
||||
"SAVE": "Save changes"
|
||||
},
|
||||
"API": {
|
||||
"CREATE_PORTAL": {
|
||||
"SUCCESS_MESSAGE": "Portal created successfully",
|
||||
|
||||
@@ -308,6 +308,7 @@
|
||||
"AUTH_PROCESSING": "Meta ilə autentifikasiya olunur",
|
||||
"WAITING_FOR_BUSINESS_INFO": "Zəhmət olmasa Meta pəncərəsində biznes quraşdırmasını tamamlayın...",
|
||||
"PROCESSING": "WhatsApp Biznes Hesabınızı quraşdırırıq",
|
||||
"ENABLING_CALLING": "Enabling WhatsApp Calling on your number…",
|
||||
"LOADING_SDK": "Facebook SDK yüklənir...",
|
||||
"CANCELLED": "WhatsApp Qeydiyyatı ləğv edildi",
|
||||
"SUCCESS_TITLE": "WhatsApp Biznes Hesabı Qoşuldu!",
|
||||
@@ -317,7 +318,8 @@
|
||||
"AUTH_NOT_COMPLETED": "Autentifikasiya tamamlanmadı. Zəhmət olmasa prosesi yenidən başladın.",
|
||||
"SUCCESS_FALLBACK": "WhatsApp Biznes Hesabı uğurla konfiqurasiya edildi",
|
||||
"MANUAL_FALLBACK": "Əgər nömrəniz artıq WhatsApp Business Platformasına (API) qoşulubsa və ya texnoloji təminatçı olaraq öz nömrənizi əlavə edirsinizsə, zəhmət olmasa {link} prosesindən istifadə edin",
|
||||
"MANUAL_LINK_TEXT": "əl ilə qurma prosesi"
|
||||
"MANUAL_LINK_TEXT": "əl ilə qurma prosesi",
|
||||
"CALLING_ENABLE_FAILED": "Your WhatsApp inbox is ready, but voice calling couldn't be turned on — this number isn't enrolled in the WhatsApp Business Calling API yet. Reach out to Meta or your WhatsApp Business Solution Provider to onboard it, then turn calling on from the inbox's Calls settings."
|
||||
},
|
||||
"API": {
|
||||
"ERROR_MESSAGE": "WhatsApp kanalını yadda saxlaya bilmədik"
|
||||
@@ -465,6 +467,10 @@
|
||||
"TITLE": "WhatsApp",
|
||||
"DESCRIPTION": "Müştərilərinizi WhatsApp-da dəstəkləyin"
|
||||
},
|
||||
"WHATSAPP_CALL": {
|
||||
"TITLE": "WhatsApp Call",
|
||||
"DESCRIPTION": "Take voice calls on your WhatsApp number"
|
||||
},
|
||||
"EMAIL": {
|
||||
"TITLE": "Elektron Poçt",
|
||||
"DESCRIPTION": "Gmail, Outlook və ya digər təminatçılarla qoşulun"
|
||||
@@ -637,7 +643,8 @@
|
||||
"BOT_CONFIGURATION": "Bot Konfiqurasiyası",
|
||||
"ACCOUNT_HEALTH": "Hesabın sağlamlığı",
|
||||
"CSAT": "CSAT",
|
||||
"VOICE": "Səs"
|
||||
"VOICE": "Səs",
|
||||
"CALLS": "Calls"
|
||||
},
|
||||
"VOICE_CONFIGURATION": {
|
||||
"ENABLE_VOICE": {
|
||||
@@ -648,6 +655,26 @@
|
||||
"DESCRIPTION": "Voice calling requires Twilio API Key credentials. These are used to generate tokens for agent voice connections."
|
||||
}
|
||||
},
|
||||
"WHATSAPP_CALLING": {
|
||||
"ENABLE": {
|
||||
"LABEL": "Enable WhatsApp Calling",
|
||||
"DESCRIPTION": "Allow agents to receive and place WhatsApp Cloud calls on this inbox. Customers can call this business number directly from WhatsApp."
|
||||
},
|
||||
"ENABLE_FAILED": "Voice calling couldn't be turned on for this number — it isn't enrolled in the WhatsApp Business Calling API yet. Reach out to Meta or your WhatsApp Business Solution Provider to onboard it, then try again.",
|
||||
"PHONE_NUMBER": {
|
||||
"LABEL": "Business phone number",
|
||||
"HELP_TEXT": "WhatsApp number that customers will call."
|
||||
},
|
||||
"HOW_IT_WORKS": {
|
||||
"LABEL": "How it works",
|
||||
"DESCRIPTION": "Calls are placed peer-to-peer between the agent's browser and Meta — no extra credentials are required. Make sure the agent's browser has microphone permission for this site."
|
||||
},
|
||||
"PERMISSION_REQUEST_BODY": {
|
||||
"LABEL": "Call permission request message",
|
||||
"HELP_TEXT": "Shown to the contact when they haven't yet consented to receive calls. Leave blank to use the default.",
|
||||
"PLACEHOLDER": "We would like to call you regarding your conversation."
|
||||
}
|
||||
},
|
||||
"CHANNEL_PREFERENCES": "Kanal üstünlükləri",
|
||||
"WIDGET_FEATURES": "Widget xüsusiyyətləri",
|
||||
"ACCOUNT_HEALTH": {
|
||||
@@ -800,6 +827,10 @@
|
||||
"WHATSAPP_TEMPLATES_SYNC_SUBHEADER": "Mövcud şablonlarınızı yeniləmək üçün WhatsApp-dan mesaj şablonlarını əl ilə sinxronlaşdırın.",
|
||||
"WHATSAPP_TEMPLATES_SYNC_BUTTON": "Şablonları sinxronlaşdır",
|
||||
"WHATSAPP_TEMPLATES_SYNC_SUCCESS": "Şablonların sinxronizasiyası uğurla başlandı. Yenilənməsi bir neçə dəqiqə çəkə bilər.",
|
||||
"WHATSAPP_CALLING_ENABLED": {
|
||||
"LABEL": "Enable voice calling",
|
||||
"DESCRIPTION": "Allow agents to start and receive WhatsApp voice calls on this inbox. Available only on embedded-signup WhatsApp Cloud channels with calling permission granted by Meta."
|
||||
},
|
||||
"UPDATE_PRE_CHAT_FORM_SETTINGS": "Pre Chat Form Parametrlərini Yeniləyin"
|
||||
},
|
||||
"HELP_CENTER": {
|
||||
|
||||
@@ -57,7 +57,8 @@
|
||||
"CONTACT_CREATED": "Əlaqə yaradıldı",
|
||||
"CONTACT_UPDATED": "Əlaqə yeniləndi",
|
||||
"CONVERSATION_TYPING_ON": "Söhbət Yazılır",
|
||||
"CONVERSATION_TYPING_OFF": "Söhbət Yazması Söndürülüb"
|
||||
"CONVERSATION_TYPING_OFF": "Söhbət Yazması Söndürülüb",
|
||||
"INBOX_UPDATED": "Inbox updated"
|
||||
}
|
||||
},
|
||||
"NAME": {
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
"OR": "ИЛИ"
|
||||
},
|
||||
"INPUT_PLACEHOLDER": "Enter value",
|
||||
"CONTACT_SEARCH_PLACEHOLDER": "Search contacts",
|
||||
"CONTACT_FALLBACK": "Contact #{id}",
|
||||
"OPERATOR_LABELS": {
|
||||
"equal_to": "Равно на",
|
||||
"not_equal_to": "Различно от",
|
||||
@@ -49,6 +51,7 @@
|
||||
"ASSIGNEE_NAME": "Assignee name",
|
||||
"INBOX_NAME": "Inbox name",
|
||||
"TEAM_NAME": "Team name",
|
||||
"CONTACT": "Contact",
|
||||
"CONVERSATION_IDENTIFIER": "Conversation identifier",
|
||||
"CAMPAIGN_NAME": "Campaign name",
|
||||
"LABELS": "Етикети",
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
{
|
||||
"BULK_ACTION": {
|
||||
"CONVERSATIONS_SELECTED": "{conversationCount} conversations selected",
|
||||
"AGENT_SELECT_LABEL": "Изберете агент",
|
||||
"ASSIGN_CONFIRMATION_LABEL": "Are you sure to assign {conversationCount} {conversationLabel} to",
|
||||
"UNASSIGN_CONFIRMATION_LABEL": "Are you sure to unassign {conversationCount} {conversationLabel}?",
|
||||
"GO_BACK_LABEL": "Go back",
|
||||
"ASSIGN_LABEL": "Assign",
|
||||
"NONE": "None",
|
||||
"CLEAR_SELECTION": "Clear",
|
||||
"ASSIGN_AGENT_CONFIRMATION_LABEL": "Are you sure you want to assign {n} conversation to {agentName}? | Are you sure you want to assign {n} conversations to {agentName}?",
|
||||
@@ -20,7 +15,6 @@
|
||||
"RESOLVE_SUCCESFUL": "Conversations resolved successfully.",
|
||||
"RESOLVE_FAILED": "Failed to resolve conversations. Please try again.",
|
||||
"ALL_CONVERSATIONS_SELECTED_ALERT": "Conversations visible on this page are only selected.",
|
||||
"AGENT_LIST_LOADING": "Loading agents",
|
||||
"UPDATE": {
|
||||
"CHANGE_STATUS": "Change status",
|
||||
"SNOOZE_UNTIL": "Snooze",
|
||||
@@ -33,16 +27,16 @@
|
||||
},
|
||||
"LABELS": {
|
||||
"ASSIGN_LABELS": "Assign labels",
|
||||
"NO_LABELS_FOUND": "Няма намерени етикети",
|
||||
"REMOVE_LABELS": "Remove labels",
|
||||
"ASSIGN_SELECTED_LABELS": "Assign selected labels",
|
||||
"REMOVE_SELECTED_LABELS": "Remove selected labels",
|
||||
"ASSIGN_SUCCESFUL": "Labels assigned successfully.",
|
||||
"ASSIGN_FAILED": "Failed to assign labels. Please try again."
|
||||
"ASSIGN_FAILED": "Failed to assign labels. Please try again.",
|
||||
"REMOVE_SUCCESFUL": "Labels removed successfully.",
|
||||
"REMOVE_FAILED": "Failed to remove labels. Please try again."
|
||||
},
|
||||
"TEAMS": {
|
||||
"TEAM_SELECT_LABEL": "Изберете екип",
|
||||
"NONE": "Нито един",
|
||||
"NO_TEAMS_AVAILABLE": "There are no teams added to this account yet.",
|
||||
"ASSIGN_SELECTED_TEAMS": "Assign selected team.",
|
||||
"ASSIGN_TEAM_CONFIRMATION_LABEL": "Are you sure you want to assign {n} conversation to {teamName}? | Are you sure you want to assign {n} conversations to {teamName}?",
|
||||
"UNASSIGN_TEAM_CONFIRMATION_LABEL": "Are you sure you want to unassign {n} conversation? | Are you sure you want to unassign {n} conversations?",
|
||||
"ASSIGN_SUCCESFUL": "Teams assigned successfully.",
|
||||
|
||||
@@ -88,6 +88,7 @@
|
||||
},
|
||||
"CARD": {
|
||||
"STATUS": {
|
||||
"PROCESSING": "Processing",
|
||||
"COMPLETED": "Завършено",
|
||||
"SCHEDULED": "Scheduled"
|
||||
},
|
||||
@@ -146,6 +147,7 @@
|
||||
},
|
||||
"CARD": {
|
||||
"STATUS": {
|
||||
"PROCESSING": "Processing",
|
||||
"COMPLETED": "Завършено",
|
||||
"SCHEDULED": "Scheduled"
|
||||
},
|
||||
|
||||
@@ -63,6 +63,7 @@
|
||||
"CODE": "Code",
|
||||
"BULLET_LIST": "Bullet List",
|
||||
"ORDERED_LIST": "Ordered List",
|
||||
"TABLE": "Table"
|
||||
"TABLE": "Table",
|
||||
"IMAGE": "Image"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
"CALL": "Call",
|
||||
"CALL_INITIATED": "Calling the contact…",
|
||||
"CALL_FAILED": "Unable to start the call. Please try again.",
|
||||
"WHATSAPP_CALL_PERMISSION_REQUESTED": "Sent a call permission request to the contact. Try again once they accept.",
|
||||
"WHATSAPP_CALL_PERMISSION_PENDING": "Call permission request already sent recently. Try again once the contact accepts.",
|
||||
"CLICK_TO_EDIT": "Click to edit",
|
||||
"VOICE_INBOX_PICKER": {
|
||||
"TITLE": "Choose a voice inbox"
|
||||
@@ -583,8 +585,11 @@
|
||||
},
|
||||
"CONTACTS_BULK_ACTIONS": {
|
||||
"ASSIGN_LABELS": "Assign Labels",
|
||||
"REMOVE_LABELS": "Remove Labels",
|
||||
"ASSIGN_LABELS_SUCCESS": "Labels assigned successfully.",
|
||||
"ASSIGN_LABELS_FAILED": "Failed to assign labels",
|
||||
"REMOVE_LABELS_SUCCESS": "Labels removed successfully.",
|
||||
"REMOVE_LABELS_FAILED": "Failed to remove labels",
|
||||
"DESCRIPTION": "Select the labels you want to add to the selected contacts.",
|
||||
"NO_LABELS_FOUND": "No labels available yet.",
|
||||
"SELECTED_COUNT": "{count} selected",
|
||||
|
||||
@@ -62,6 +62,7 @@
|
||||
"UNSUPPORTED_MESSAGE_FACEBOOK": "This message is unsupported. You can view this message on the Facebook Messenger app.",
|
||||
"UNSUPPORTED_MESSAGE_INSTAGRAM": "This message is unsupported. You can view this message on the Instagram app.",
|
||||
"UNSUPPORTED_MESSAGE_TIKTOK": "This message is unsupported. You can view this message on the TikTok app.",
|
||||
"UNSUPPORTED_MESSAGE_WHATSAPP": "This message is unsupported. You can view this message on the WhatsApp app.",
|
||||
"SUCCESS_DELETE_MESSAGE": "Message deleted successfully",
|
||||
"FAIL_DELETE_MESSSAGE": "Couldn't delete message! Try again",
|
||||
"NO_RESPONSE": "No response",
|
||||
@@ -79,13 +80,22 @@
|
||||
"OUTGOING_CALL": "Outgoing call",
|
||||
"CALL_IN_PROGRESS": "Call in progress",
|
||||
"NO_ANSWER": "No answer",
|
||||
"NO_ANSWER_OUTBOUND_LABEL": "No answer",
|
||||
"NO_ANSWER_OUTBOUND_SUBTEXT": "Contact didn't pick up",
|
||||
"MISSED_CALL": "Missed call",
|
||||
"MISSED_CALL_INBOUND_SUBTEXT": "No agent picked up",
|
||||
"MISSED_CALL_DECLINED_BY": "Declined by {agentName}",
|
||||
"CALL_ENDED": "Call ended",
|
||||
"HANDLED_BY": "Handled by {agentName}",
|
||||
"NOT_ANSWERED_YET": "Not answered yet",
|
||||
"CALLING": "Calling…",
|
||||
"THEY_ANSWERED": "They answered",
|
||||
"YOU_ANSWERED": "You answered",
|
||||
"AGENT_ANSWERED": "{agentName} answered",
|
||||
"JOIN_CALL": "Join call"
|
||||
"JOIN_CALL": "Join call",
|
||||
"CALL_BACK": "Call back",
|
||||
"TRANSCRIPT_SHOW_MORE": "Show more",
|
||||
"TRANSCRIPT_SHOW_LESS": "Show less"
|
||||
},
|
||||
"HEADER": {
|
||||
"RESOLVE_ACTION": "Resolve",
|
||||
@@ -100,6 +110,12 @@
|
||||
"SNOOZED_UNTIL_TOMORROW": "Snoozed until tomorrow",
|
||||
"SNOOZED_UNTIL_NEXT_WEEK": "Snoozed until next week",
|
||||
"SNOOZED_UNTIL_NEXT_REPLY": "Snoozed until next reply",
|
||||
"WHATSAPP_CALL": "Start WhatsApp call",
|
||||
"WHATSAPP_CALL_FAILED": "Could not start the WhatsApp call.",
|
||||
"VOICE_CALL": "Start call",
|
||||
"VOICE_CALL_FAILED": "Could not start the call.",
|
||||
"WHATSAPP_CALL_PERMISSION_REQUESTED": "Sent a call permission request to the contact. Try again once they accept.",
|
||||
"WHATSAPP_CALL_PERMISSION_PENDING": "Call permission request already sent recently. Try again once the contact accepts.",
|
||||
"SLA_STATUS": {
|
||||
"FRT": "FRT {status}",
|
||||
"NRT": "NRT {status}",
|
||||
@@ -215,7 +231,9 @@
|
||||
"TIP_AUDIORECORDER_ICON": "Record audio",
|
||||
"TIP_AUDIORECORDER_PERMISSION": "Allow access to audio",
|
||||
"TIP_AUDIORECORDER_ERROR": "Could not open the audio",
|
||||
"AUDIO_CONVERSION_FAILED": "Audio conversion failed. Please try again.",
|
||||
"DRAG_DROP": "Drag and drop here to attach",
|
||||
"IMAGE_UPLOAD_SUCCESS": "Image uploaded successfully",
|
||||
"START_AUDIO_RECORDING": "Start audio recording",
|
||||
"STOP_AUDIO_RECORDING": "Stop audio recording",
|
||||
"COPILOT_THINKING": "Copilot мисли",
|
||||
@@ -299,8 +317,13 @@
|
||||
"NOT_ANSWERED_YET": "Not answered yet",
|
||||
"HANDLED_IN_ANOTHER_TAB": "Being handled in another tab",
|
||||
"REJECT_CALL": "Reject",
|
||||
"DISMISS_CALL": "Dismiss",
|
||||
"JOIN_CALL": "Join call",
|
||||
"END_CALL": "End call"
|
||||
"END_CALL": "End call",
|
||||
"MUTE": "Mute mic",
|
||||
"UNMUTE": "Unmute mic",
|
||||
"VIEW_CHAT_HISTORY": "View chat history",
|
||||
"GO_TO_CONVERSATION": "Go to conversation thread"
|
||||
}
|
||||
},
|
||||
"EMAIL_TRANSCRIPT": {
|
||||
|
||||
@@ -607,9 +607,12 @@
|
||||
"DRAFT": "Draft",
|
||||
"ARCHIVE": "Archive",
|
||||
"TRANSLATE": "Translate",
|
||||
"MOVE_TO_CATEGORY": "Category",
|
||||
"DELETE": "Изтрий",
|
||||
"STATUS_SUCCESS": "Articles updated successfully",
|
||||
"STATUS_ERROR": "Failed to update articles",
|
||||
"CATEGORY_SUCCESS": "Articles moved successfully",
|
||||
"CATEGORY_ERROR": "Failed to move articles",
|
||||
"DELETE_CONFIRM_TITLE": "Delete article | Delete {count} articles",
|
||||
"DELETE_CONFIRM_DESCRIPTION": "This will permanently delete the selected article. This action cannot be undone. | This will permanently delete {count} selected articles. This action cannot be undone.",
|
||||
"DELETE_CONFIRM": "Изтрий",
|
||||
@@ -697,9 +700,27 @@
|
||||
"MAKE_DEFAULT": "Make default",
|
||||
"MOVE_TO_DRAFT": "Move to draft",
|
||||
"PUBLISH_LOCALE": "Publish locale",
|
||||
"CUSTOMIZE_CONTENT": "Localize content",
|
||||
"DELETE": "Изтрий"
|
||||
}
|
||||
},
|
||||
"CONTENT_DIALOG": {
|
||||
"TITLE": "Localize content",
|
||||
"DESCRIPTION": "Set values specific to this locale. Anything left blank falls back to the default locale.",
|
||||
"NAME": {
|
||||
"LABEL": "Име"
|
||||
},
|
||||
"PAGE_TITLE": {
|
||||
"LABEL": "Page title"
|
||||
},
|
||||
"HEADER_TEXT": {
|
||||
"LABEL": "Header text"
|
||||
},
|
||||
"API": {
|
||||
"SUCCESS_MESSAGE": "Locale content updated successfully",
|
||||
"ERROR_MESSAGE": "Unable to update locale content. Try again."
|
||||
}
|
||||
},
|
||||
"ADD_LOCALE_DIALOG": {
|
||||
"TITLE": "Add a new locale",
|
||||
"DESCRIPTION": "Select the language in which this article will be written. This will be added to your list of translations, and you can add more later.",
|
||||
@@ -866,6 +887,28 @@
|
||||
},
|
||||
"EDIT_CONFIGURATION": "Edit configuration"
|
||||
},
|
||||
"LAYOUT_CONTENT": {
|
||||
"HEADER": "Appearance",
|
||||
"DESCRIPTION": "Pick the layout that fits how your visitors read.",
|
||||
"LAYOUT": {
|
||||
"CLASSIC": {
|
||||
"TITLE": "Classic",
|
||||
"DESCRIPTION": "A welcoming home page with search and featured topics."
|
||||
},
|
||||
"SIDEBAR": {
|
||||
"TITLE": "Documentation",
|
||||
"DESCRIPTION": "Side-by-side navigation that keeps every guide a click away."
|
||||
}
|
||||
},
|
||||
"SOCIAL_LINKS": {
|
||||
"HEADER": "Social links",
|
||||
"DESCRIPTION": "Add the handle for each network and your help center builds the full link. Shown in the documentation layout footer.",
|
||||
"PLACEHOLDER": "handle",
|
||||
"ADD": "Add social link",
|
||||
"REMOVE": "Remove"
|
||||
},
|
||||
"SAVE": "Save changes"
|
||||
},
|
||||
"API": {
|
||||
"CREATE_PORTAL": {
|
||||
"SUCCESS_MESSAGE": "Portal created successfully",
|
||||
|
||||
@@ -308,6 +308,7 @@
|
||||
"AUTH_PROCESSING": "Authenticating with Meta",
|
||||
"WAITING_FOR_BUSINESS_INFO": "Please complete business setup in the Meta window...",
|
||||
"PROCESSING": "Setting up your WhatsApp Business Account",
|
||||
"ENABLING_CALLING": "Enabling WhatsApp Calling on your number…",
|
||||
"LOADING_SDK": "Loading Facebook SDK...",
|
||||
"CANCELLED": "WhatsApp Signup was cancelled",
|
||||
"SUCCESS_TITLE": "WhatsApp Business Account Connected!",
|
||||
@@ -317,7 +318,8 @@
|
||||
"AUTH_NOT_COMPLETED": "Authentication not completed. Please restart the process.",
|
||||
"SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured",
|
||||
"MANUAL_FALLBACK": "If your number is already connected to the WhatsApp Business Platform (API), or if you’re a tech provider onboarding your own number, please use the {link} flow",
|
||||
"MANUAL_LINK_TEXT": "manual setup flow"
|
||||
"MANUAL_LINK_TEXT": "manual setup flow",
|
||||
"CALLING_ENABLE_FAILED": "Your WhatsApp inbox is ready, but voice calling couldn't be turned on — this number isn't enrolled in the WhatsApp Business Calling API yet. Reach out to Meta or your WhatsApp Business Solution Provider to onboard it, then turn calling on from the inbox's Calls settings."
|
||||
},
|
||||
"API": {
|
||||
"ERROR_MESSAGE": "We were not able to save the WhatsApp channel"
|
||||
@@ -465,6 +467,10 @@
|
||||
"TITLE": "WhatsApp",
|
||||
"DESCRIPTION": "Support your customers on WhatsApp"
|
||||
},
|
||||
"WHATSAPP_CALL": {
|
||||
"TITLE": "WhatsApp Call",
|
||||
"DESCRIPTION": "Take voice calls on your WhatsApp number"
|
||||
},
|
||||
"EMAIL": {
|
||||
"TITLE": "Email",
|
||||
"DESCRIPTION": "Connect with Gmail, Outlook, or other providers"
|
||||
@@ -637,7 +643,8 @@
|
||||
"BOT_CONFIGURATION": "Bot Configuration",
|
||||
"ACCOUNT_HEALTH": "Account Health",
|
||||
"CSAT": "CSAT",
|
||||
"VOICE": "Voice"
|
||||
"VOICE": "Voice",
|
||||
"CALLS": "Calls"
|
||||
},
|
||||
"VOICE_CONFIGURATION": {
|
||||
"ENABLE_VOICE": {
|
||||
@@ -648,6 +655,26 @@
|
||||
"DESCRIPTION": "Voice calling requires Twilio API Key credentials. These are used to generate tokens for agent voice connections."
|
||||
}
|
||||
},
|
||||
"WHATSAPP_CALLING": {
|
||||
"ENABLE": {
|
||||
"LABEL": "Enable WhatsApp Calling",
|
||||
"DESCRIPTION": "Allow agents to receive and place WhatsApp Cloud calls on this inbox. Customers can call this business number directly from WhatsApp."
|
||||
},
|
||||
"ENABLE_FAILED": "Voice calling couldn't be turned on for this number — it isn't enrolled in the WhatsApp Business Calling API yet. Reach out to Meta or your WhatsApp Business Solution Provider to onboard it, then try again.",
|
||||
"PHONE_NUMBER": {
|
||||
"LABEL": "Business phone number",
|
||||
"HELP_TEXT": "WhatsApp number that customers will call."
|
||||
},
|
||||
"HOW_IT_WORKS": {
|
||||
"LABEL": "How it works",
|
||||
"DESCRIPTION": "Calls are placed peer-to-peer between the agent's browser and Meta — no extra credentials are required. Make sure the agent's browser has microphone permission for this site."
|
||||
},
|
||||
"PERMISSION_REQUEST_BODY": {
|
||||
"LABEL": "Call permission request message",
|
||||
"HELP_TEXT": "Shown to the contact when they haven't yet consented to receive calls. Leave blank to use the default.",
|
||||
"PLACEHOLDER": "We would like to call you regarding your conversation."
|
||||
}
|
||||
},
|
||||
"CHANNEL_PREFERENCES": "Channel Preferences",
|
||||
"WIDGET_FEATURES": "Widget features",
|
||||
"ACCOUNT_HEALTH": {
|
||||
@@ -800,6 +827,10 @@
|
||||
"WHATSAPP_TEMPLATES_SYNC_SUBHEADER": "Manually sync message templates from WhatsApp to update your available templates.",
|
||||
"WHATSAPP_TEMPLATES_SYNC_BUTTON": "Sync Templates",
|
||||
"WHATSAPP_TEMPLATES_SYNC_SUCCESS": "Templates sync initiated successfully. It may take a couple of minutes to update.",
|
||||
"WHATSAPP_CALLING_ENABLED": {
|
||||
"LABEL": "Enable voice calling",
|
||||
"DESCRIPTION": "Allow agents to start and receive WhatsApp voice calls on this inbox. Available only on embedded-signup WhatsApp Cloud channels with calling permission granted by Meta."
|
||||
},
|
||||
"UPDATE_PRE_CHAT_FORM_SETTINGS": "Update Pre Chat Form Settings"
|
||||
},
|
||||
"HELP_CENTER": {
|
||||
|
||||
@@ -57,7 +57,8 @@
|
||||
"CONTACT_CREATED": "Contact created",
|
||||
"CONTACT_UPDATED": "Contact updated",
|
||||
"CONVERSATION_TYPING_ON": "Conversation Typing On",
|
||||
"CONVERSATION_TYPING_OFF": "Conversation Typing Off"
|
||||
"CONVERSATION_TYPING_OFF": "Conversation Typing Off",
|
||||
"INBOX_UPDATED": "Inbox updated"
|
||||
}
|
||||
},
|
||||
"NAME": {
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
"OR": "OR"
|
||||
},
|
||||
"INPUT_PLACEHOLDER": "Enter value",
|
||||
"CONTACT_SEARCH_PLACEHOLDER": "যোগাযোগ অনুসন্ধান করুন",
|
||||
"CONTACT_FALLBACK": "Contact #{id}",
|
||||
"OPERATOR_LABELS": {
|
||||
"equal_to": "Equal to",
|
||||
"not_equal_to": "Not equal to",
|
||||
@@ -49,6 +51,7 @@
|
||||
"ASSIGNEE_NAME": "Assignee name",
|
||||
"INBOX_NAME": "Inbox name",
|
||||
"TEAM_NAME": "Team name",
|
||||
"CONTACT": "যোগাযোগ",
|
||||
"CONVERSATION_IDENTIFIER": "Conversation identifier",
|
||||
"CAMPAIGN_NAME": "Campaign name",
|
||||
"LABELS": "Labels",
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
{
|
||||
"BULK_ACTION": {
|
||||
"CONVERSATIONS_SELECTED": "{conversationCount} conversations selected",
|
||||
"AGENT_SELECT_LABEL": "Select agent",
|
||||
"ASSIGN_CONFIRMATION_LABEL": "Are you sure to assign {conversationCount} {conversationLabel} to",
|
||||
"UNASSIGN_CONFIRMATION_LABEL": "Are you sure to unassign {conversationCount} {conversationLabel}?",
|
||||
"GO_BACK_LABEL": "Go back",
|
||||
"ASSIGN_LABEL": "Assign",
|
||||
"NONE": "কিছুই না",
|
||||
"CLEAR_SELECTION": "Clear",
|
||||
"ASSIGN_AGENT_CONFIRMATION_LABEL": "Are you sure you want to assign {n} conversation to {agentName}? | Are you sure you want to assign {n} conversations to {agentName}?",
|
||||
@@ -20,7 +15,6 @@
|
||||
"RESOLVE_SUCCESFUL": "Conversations resolved successfully.",
|
||||
"RESOLVE_FAILED": "Failed to resolve conversations. Please try again.",
|
||||
"ALL_CONVERSATIONS_SELECTED_ALERT": "Conversations visible on this page are only selected.",
|
||||
"AGENT_LIST_LOADING": "Loading agents",
|
||||
"UPDATE": {
|
||||
"CHANGE_STATUS": "Change status",
|
||||
"SNOOZE_UNTIL": "Snooze",
|
||||
@@ -33,16 +27,16 @@
|
||||
},
|
||||
"LABELS": {
|
||||
"ASSIGN_LABELS": "Assign labels",
|
||||
"NO_LABELS_FOUND": "No labels found",
|
||||
"REMOVE_LABELS": "Remove labels",
|
||||
"ASSIGN_SELECTED_LABELS": "Assign selected labels",
|
||||
"REMOVE_SELECTED_LABELS": "Remove selected labels",
|
||||
"ASSIGN_SUCCESFUL": "Labels assigned successfully.",
|
||||
"ASSIGN_FAILED": "Failed to assign labels. Please try again."
|
||||
"ASSIGN_FAILED": "Failed to assign labels. Please try again.",
|
||||
"REMOVE_SUCCESFUL": "Labels removed successfully.",
|
||||
"REMOVE_FAILED": "Failed to remove labels. Please try again."
|
||||
},
|
||||
"TEAMS": {
|
||||
"TEAM_SELECT_LABEL": "Select team",
|
||||
"NONE": "None",
|
||||
"NO_TEAMS_AVAILABLE": "There are no teams added to this account yet.",
|
||||
"ASSIGN_SELECTED_TEAMS": "Assign selected team.",
|
||||
"ASSIGN_TEAM_CONFIRMATION_LABEL": "Are you sure you want to assign {n} conversation to {teamName}? | Are you sure you want to assign {n} conversations to {teamName}?",
|
||||
"UNASSIGN_TEAM_CONFIRMATION_LABEL": "Are you sure you want to unassign {n} conversation? | Are you sure you want to unassign {n} conversations?",
|
||||
"ASSIGN_SUCCESFUL": "Teams assigned successfully.",
|
||||
|
||||
@@ -88,6 +88,7 @@
|
||||
},
|
||||
"CARD": {
|
||||
"STATUS": {
|
||||
"PROCESSING": "প্রক্রিয়াধীন",
|
||||
"COMPLETED": "Completed",
|
||||
"SCHEDULED": "Scheduled"
|
||||
},
|
||||
@@ -146,6 +147,7 @@
|
||||
},
|
||||
"CARD": {
|
||||
"STATUS": {
|
||||
"PROCESSING": "প্রক্রিয়াধীন",
|
||||
"COMPLETED": "Completed",
|
||||
"SCHEDULED": "Scheduled"
|
||||
},
|
||||
|
||||
@@ -63,6 +63,7 @@
|
||||
"CODE": "Code",
|
||||
"BULLET_LIST": "Bullet List",
|
||||
"ORDERED_LIST": "Ordered List",
|
||||
"TABLE": "Table"
|
||||
"TABLE": "Table",
|
||||
"IMAGE": "Image"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
"CALL": "Call",
|
||||
"CALL_INITIATED": "Calling the contact…",
|
||||
"CALL_FAILED": "Unable to start the call. Please try again.",
|
||||
"WHATSAPP_CALL_PERMISSION_REQUESTED": "Sent a call permission request to the contact. Try again once they accept.",
|
||||
"WHATSAPP_CALL_PERMISSION_PENDING": "Call permission request already sent recently. Try again once the contact accepts.",
|
||||
"CLICK_TO_EDIT": "Click to edit",
|
||||
"VOICE_INBOX_PICKER": {
|
||||
"TITLE": "Choose a voice inbox"
|
||||
@@ -583,8 +585,11 @@
|
||||
},
|
||||
"CONTACTS_BULK_ACTIONS": {
|
||||
"ASSIGN_LABELS": "Assign Labels",
|
||||
"REMOVE_LABELS": "Remove Labels",
|
||||
"ASSIGN_LABELS_SUCCESS": "Labels assigned successfully.",
|
||||
"ASSIGN_LABELS_FAILED": "Failed to assign labels",
|
||||
"REMOVE_LABELS_SUCCESS": "Labels removed successfully.",
|
||||
"REMOVE_LABELS_FAILED": "Failed to remove labels",
|
||||
"DESCRIPTION": "Select the labels you want to add to the selected contacts.",
|
||||
"NO_LABELS_FOUND": "No labels available yet.",
|
||||
"SELECTED_COUNT": "{count} selected",
|
||||
|
||||
@@ -62,6 +62,7 @@
|
||||
"UNSUPPORTED_MESSAGE_FACEBOOK": "এই বার্তাটি সমর্থিত নয়। আপনি Facebook Messenger অ্যাপে এই বার্তাটি দেখতে পারেন।",
|
||||
"UNSUPPORTED_MESSAGE_INSTAGRAM": "এই বার্তাটি সমর্থিত নয়। আপনি Instagram অ্যাপে এই বার্তাটি দেখতে পারেন।",
|
||||
"UNSUPPORTED_MESSAGE_TIKTOK": "এই বার্তাটি সমর্থিত নয়। TikTok অ্যাপে এই বার্তাটি দেখতে পারেন।",
|
||||
"UNSUPPORTED_MESSAGE_WHATSAPP": "This message is unsupported. You can view this message on the WhatsApp app.",
|
||||
"SUCCESS_DELETE_MESSAGE": "বার্তা সফলভাবে মুছে ফেলা হয়েছে",
|
||||
"FAIL_DELETE_MESSSAGE": "বার্তা মুছে ফেলা যায়নি! আবার চেষ্টা করুন",
|
||||
"NO_RESPONSE": "কোনো উত্তর নেই",
|
||||
@@ -79,13 +80,22 @@
|
||||
"OUTGOING_CALL": "আউটগোয়িং কল",
|
||||
"CALL_IN_PROGRESS": "কল চলছে",
|
||||
"NO_ANSWER": "উত্তর নেই",
|
||||
"NO_ANSWER_OUTBOUND_LABEL": "উত্তর নেই",
|
||||
"NO_ANSWER_OUTBOUND_SUBTEXT": "Contact didn't pick up",
|
||||
"MISSED_CALL": "মিসড কল",
|
||||
"MISSED_CALL_INBOUND_SUBTEXT": "No agent picked up",
|
||||
"MISSED_CALL_DECLINED_BY": "Declined by {agentName}",
|
||||
"CALL_ENDED": "কল শেষ হয়েছে",
|
||||
"HANDLED_BY": "Handled by {agentName}",
|
||||
"NOT_ANSWERED_YET": "এখনও উত্তর পাওয়া যায়নি",
|
||||
"CALLING": "Calling…",
|
||||
"THEY_ANSWERED": "তারা উত্তর দিয়েছে",
|
||||
"YOU_ANSWERED": "আপনি উত্তর দিয়েছেন",
|
||||
"AGENT_ANSWERED": "{agentName} answered",
|
||||
"JOIN_CALL": "কলে যোগ দিন"
|
||||
"JOIN_CALL": "কলে যোগ দিন",
|
||||
"CALL_BACK": "Call back",
|
||||
"TRANSCRIPT_SHOW_MORE": "Show more",
|
||||
"TRANSCRIPT_SHOW_LESS": "Show less"
|
||||
},
|
||||
"HEADER": {
|
||||
"RESOLVE_ACTION": "সমাধান করুন",
|
||||
@@ -100,6 +110,12 @@
|
||||
"SNOOZED_UNTIL_TOMORROW": "আগামীকাল পর্যন্ত স্থগিত",
|
||||
"SNOOZED_UNTIL_NEXT_WEEK": "পরবর্তী সপ্তাহ পর্যন্ত স্থগিত",
|
||||
"SNOOZED_UNTIL_NEXT_REPLY": "পরবর্তী উত্তর পর্যন্ত স্থগিত",
|
||||
"WHATSAPP_CALL": "Start WhatsApp call",
|
||||
"WHATSAPP_CALL_FAILED": "Could not start the WhatsApp call.",
|
||||
"VOICE_CALL": "Start call",
|
||||
"VOICE_CALL_FAILED": "Could not start the call.",
|
||||
"WHATSAPP_CALL_PERMISSION_REQUESTED": "Sent a call permission request to the contact. Try again once they accept.",
|
||||
"WHATSAPP_CALL_PERMISSION_PENDING": "Call permission request already sent recently. Try again once the contact accepts.",
|
||||
"SLA_STATUS": {
|
||||
"FRT": "FRT {status}",
|
||||
"NRT": "NRT {status}",
|
||||
@@ -215,7 +231,9 @@
|
||||
"TIP_AUDIORECORDER_ICON": "অডিও রেকর্ড করুন",
|
||||
"TIP_AUDIORECORDER_PERMISSION": "অডিও অ্যাক্সেসের অনুমতি দিন",
|
||||
"TIP_AUDIORECORDER_ERROR": "অডিও খোলা যায়নি",
|
||||
"AUDIO_CONVERSION_FAILED": "Audio conversion failed. Please try again.",
|
||||
"DRAG_DROP": "সংযুক্ত করতে এখানে ড্র্যাগ ও ড্রপ করুন",
|
||||
"IMAGE_UPLOAD_SUCCESS": "ছবি সফলভাবে আপলোড হয়েছে",
|
||||
"START_AUDIO_RECORDING": "অডিও রেকর্ডিং শুরু করুন",
|
||||
"STOP_AUDIO_RECORDING": "অডিও রেকর্ডিং বন্ধ করুন",
|
||||
"COPILOT_THINKING": "Copilot ভাবছে",
|
||||
@@ -299,8 +317,13 @@
|
||||
"NOT_ANSWERED_YET": "এখনও উত্তর পাওয়া যায়নি",
|
||||
"HANDLED_IN_ANOTHER_TAB": "অন্য ট্যাবে পরিচালিত হচ্ছে",
|
||||
"REJECT_CALL": "প্রত্যাখ্যান করুন",
|
||||
"DISMISS_CALL": "Dismiss",
|
||||
"JOIN_CALL": "কলে যোগ দিন",
|
||||
"END_CALL": "কল শেষ করুন"
|
||||
"END_CALL": "কল শেষ করুন",
|
||||
"MUTE": "Mute mic",
|
||||
"UNMUTE": "Unmute mic",
|
||||
"VIEW_CHAT_HISTORY": "View chat history",
|
||||
"GO_TO_CONVERSATION": "Go to conversation thread"
|
||||
}
|
||||
},
|
||||
"EMAIL_TRANSCRIPT": {
|
||||
|
||||
@@ -607,9 +607,12 @@
|
||||
"DRAFT": "খসড়া",
|
||||
"ARCHIVE": "আর্কাইভ",
|
||||
"TRANSLATE": "অনুবাদ করুন",
|
||||
"MOVE_TO_CATEGORY": "Category",
|
||||
"DELETE": "মুছে ফেলুন",
|
||||
"STATUS_SUCCESS": "Articles updated successfully",
|
||||
"STATUS_ERROR": "Failed to update articles",
|
||||
"CATEGORY_SUCCESS": "Articles moved successfully",
|
||||
"CATEGORY_ERROR": "Failed to move articles",
|
||||
"DELETE_CONFIRM_TITLE": "Delete article | Delete {count} articles",
|
||||
"DELETE_CONFIRM_DESCRIPTION": "This will permanently delete the selected article. This action cannot be undone. | This will permanently delete {count} selected articles. This action cannot be undone.",
|
||||
"DELETE_CONFIRM": "মুছে ফেলুন",
|
||||
@@ -697,9 +700,27 @@
|
||||
"MAKE_DEFAULT": "ডিফল্ট করুন",
|
||||
"MOVE_TO_DRAFT": "Move to draft",
|
||||
"PUBLISH_LOCALE": "Publish locale",
|
||||
"CUSTOMIZE_CONTENT": "Localize content",
|
||||
"DELETE": "মুছে ফেলুন"
|
||||
}
|
||||
},
|
||||
"CONTENT_DIALOG": {
|
||||
"TITLE": "Localize content",
|
||||
"DESCRIPTION": "Set values specific to this locale. Anything left blank falls back to the default locale.",
|
||||
"NAME": {
|
||||
"LABEL": "নাম"
|
||||
},
|
||||
"PAGE_TITLE": {
|
||||
"LABEL": "পৃষ্ঠার শিরোনাম"
|
||||
},
|
||||
"HEADER_TEXT": {
|
||||
"LABEL": "হেডার টেক্সট"
|
||||
},
|
||||
"API": {
|
||||
"SUCCESS_MESSAGE": "Locale content updated successfully",
|
||||
"ERROR_MESSAGE": "Unable to update locale content. Try again."
|
||||
}
|
||||
},
|
||||
"ADD_LOCALE_DIALOG": {
|
||||
"TITLE": "নতুন লোকেল যোগ করুন",
|
||||
"DESCRIPTION": "এই নিবন্ধটি যে ভাষায় লেখা হবে তা নির্বাচন করুন. এটি আপনার অনুবাদ তালিকায় যোগ হবে, এবং পরে আপনি আরও ভাষা যোগ করতে পারবেন.",
|
||||
@@ -866,6 +887,28 @@
|
||||
},
|
||||
"EDIT_CONFIGURATION": "কনফিগারেশন সম্পাদনা করুন"
|
||||
},
|
||||
"LAYOUT_CONTENT": {
|
||||
"HEADER": "Appearance",
|
||||
"DESCRIPTION": "Pick the layout that fits how your visitors read.",
|
||||
"LAYOUT": {
|
||||
"CLASSIC": {
|
||||
"TITLE": "Classic",
|
||||
"DESCRIPTION": "A welcoming home page with search and featured topics."
|
||||
},
|
||||
"SIDEBAR": {
|
||||
"TITLE": "Documentation",
|
||||
"DESCRIPTION": "Side-by-side navigation that keeps every guide a click away."
|
||||
}
|
||||
},
|
||||
"SOCIAL_LINKS": {
|
||||
"HEADER": "Social links",
|
||||
"DESCRIPTION": "Add the handle for each network and your help center builds the full link. Shown in the documentation layout footer.",
|
||||
"PLACEHOLDER": "handle",
|
||||
"ADD": "Add social link",
|
||||
"REMOVE": "সরান"
|
||||
},
|
||||
"SAVE": "পরিবর্তন সংরক্ষণ করুন"
|
||||
},
|
||||
"API": {
|
||||
"CREATE_PORTAL": {
|
||||
"SUCCESS_MESSAGE": "পোর্টাল সফলভাবে তৈরি হয়েছে",
|
||||
|
||||
@@ -308,6 +308,7 @@
|
||||
"AUTH_PROCESSING": "Meta-র সাথে প্রমাণীকরণ চলছে",
|
||||
"WAITING_FOR_BUSINESS_INFO": "অনুগ্রহ করে Meta উইন্ডোতে ব্যবসার সেটআপ সম্পন্ন করুন...",
|
||||
"PROCESSING": "আপনার WhatsApp Business Account সেটআপ করা হচ্ছে",
|
||||
"ENABLING_CALLING": "Enabling WhatsApp Calling on your number…",
|
||||
"LOADING_SDK": "Facebook SDK লোড হচ্ছে...",
|
||||
"CANCELLED": "WhatsApp সাইনআপ বাতিল করা হয়েছে",
|
||||
"SUCCESS_TITLE": "WhatsApp Business Account সংযুক্ত হয়েছে!",
|
||||
@@ -317,7 +318,8 @@
|
||||
"AUTH_NOT_COMPLETED": "প্রমাণীকরণ সম্পন্ন হয়নি। অনুগ্রহ করে প্রক্রিয়াটি পুনরায় শুরু করুন।.",
|
||||
"SUCCESS_FALLBACK": "WhatsApp Business Account সফলভাবে কনফিগার করা হয়েছে",
|
||||
"MANUAL_FALLBACK": "আপনার নম্বর যদি ইতিমধ্যে WhatsApp Business Platform (API)-এর সাথে সংযুক্ত থাকে, অথবা আপনি যদি নিজস্ব নম্বর অনবোর্ড করতে টেক প্রোভাইডার হন, তাহলে অনুগ্রহ করে {link} ফ্লো ব্যবহার করুন",
|
||||
"MANUAL_LINK_TEXT": "ম্যানুয়াল সেটআপ ফ্লো"
|
||||
"MANUAL_LINK_TEXT": "ম্যানুয়াল সেটআপ ফ্লো",
|
||||
"CALLING_ENABLE_FAILED": "Your WhatsApp inbox is ready, but voice calling couldn't be turned on — this number isn't enrolled in the WhatsApp Business Calling API yet. Reach out to Meta or your WhatsApp Business Solution Provider to onboard it, then turn calling on from the inbox's Calls settings."
|
||||
},
|
||||
"API": {
|
||||
"ERROR_MESSAGE": "আমরা WhatsApp চ্যানেল সংরক্ষণ করতে পারিনি"
|
||||
@@ -465,6 +467,10 @@
|
||||
"TITLE": "WhatsApp",
|
||||
"DESCRIPTION": "WhatsApp-এ আপনার গ্রাহকদের সহায়তা দিন"
|
||||
},
|
||||
"WHATSAPP_CALL": {
|
||||
"TITLE": "WhatsApp Call",
|
||||
"DESCRIPTION": "Take voice calls on your WhatsApp number"
|
||||
},
|
||||
"EMAIL": {
|
||||
"TITLE": "ইমেইল",
|
||||
"DESCRIPTION": "Gmail, Outlook, অথবা অন্যান্য প্রদানকারীর সাথে সংযোগ করুন"
|
||||
@@ -637,7 +643,8 @@
|
||||
"BOT_CONFIGURATION": "বট কনফিগারেশন",
|
||||
"ACCOUNT_HEALTH": "অ্যাকাউন্টের স্বাস্থ্য",
|
||||
"CSAT": "CSAT",
|
||||
"VOICE": "ভয়েস"
|
||||
"VOICE": "ভয়েস",
|
||||
"CALLS": "Calls"
|
||||
},
|
||||
"VOICE_CONFIGURATION": {
|
||||
"ENABLE_VOICE": {
|
||||
@@ -648,6 +655,26 @@
|
||||
"DESCRIPTION": "Voice calling requires Twilio API Key credentials. These are used to generate tokens for agent voice connections."
|
||||
}
|
||||
},
|
||||
"WHATSAPP_CALLING": {
|
||||
"ENABLE": {
|
||||
"LABEL": "Enable WhatsApp Calling",
|
||||
"DESCRIPTION": "Allow agents to receive and place WhatsApp Cloud calls on this inbox. Customers can call this business number directly from WhatsApp."
|
||||
},
|
||||
"ENABLE_FAILED": "Voice calling couldn't be turned on for this number — it isn't enrolled in the WhatsApp Business Calling API yet. Reach out to Meta or your WhatsApp Business Solution Provider to onboard it, then try again.",
|
||||
"PHONE_NUMBER": {
|
||||
"LABEL": "Business phone number",
|
||||
"HELP_TEXT": "WhatsApp number that customers will call."
|
||||
},
|
||||
"HOW_IT_WORKS": {
|
||||
"LABEL": "How it works",
|
||||
"DESCRIPTION": "Calls are placed peer-to-peer between the agent's browser and Meta — no extra credentials are required. Make sure the agent's browser has microphone permission for this site."
|
||||
},
|
||||
"PERMISSION_REQUEST_BODY": {
|
||||
"LABEL": "Call permission request message",
|
||||
"HELP_TEXT": "Shown to the contact when they haven't yet consented to receive calls. Leave blank to use the default.",
|
||||
"PLACEHOLDER": "We would like to call you regarding your conversation."
|
||||
}
|
||||
},
|
||||
"CHANNEL_PREFERENCES": "চ্যানেল পছন্দসমূহ",
|
||||
"WIDGET_FEATURES": "উইজেট ফিচারসমূহ",
|
||||
"ACCOUNT_HEALTH": {
|
||||
@@ -800,6 +827,10 @@
|
||||
"WHATSAPP_TEMPLATES_SYNC_SUBHEADER": "আপনার উপলব্ধ টেমপ্লেট আপডেট করতে WhatsApp থেকে মেসেজ টেমপ্লেটগুলো ম্যানুয়ালি সিঙ্ক করুন।.",
|
||||
"WHATSAPP_TEMPLATES_SYNC_BUTTON": "টেমপ্লেট সিঙ্ক করুন",
|
||||
"WHATSAPP_TEMPLATES_SYNC_SUCCESS": "টেমপ্লেট সিঙ্ক সফলভাবে শুরু হয়েছে। আপডেট হতে কয়েক মিনিট সময় লাগতে পারে।.",
|
||||
"WHATSAPP_CALLING_ENABLED": {
|
||||
"LABEL": "Enable voice calling",
|
||||
"DESCRIPTION": "Allow agents to start and receive WhatsApp voice calls on this inbox. Available only on embedded-signup WhatsApp Cloud channels with calling permission granted by Meta."
|
||||
},
|
||||
"UPDATE_PRE_CHAT_FORM_SETTINGS": "প্রি-চ্যাট ফর্ম সেটিংস আপডেট করুন"
|
||||
},
|
||||
"HELP_CENTER": {
|
||||
|
||||
@@ -57,7 +57,8 @@
|
||||
"CONTACT_CREATED": "যোগাযোগ তৈরি হয়েছে",
|
||||
"CONTACT_UPDATED": "যোগাযোগ আপডেট হয়েছে",
|
||||
"CONVERSATION_TYPING_ON": "কথোপকথন টাইপিং চালু আছে",
|
||||
"CONVERSATION_TYPING_OFF": "কথোপকথন টাইপিং বন্ধ আছে"
|
||||
"CONVERSATION_TYPING_OFF": "কথোপকথন টাইপিং বন্ধ আছে",
|
||||
"INBOX_UPDATED": "Inbox updated"
|
||||
}
|
||||
},
|
||||
"NAME": {
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
"OR": "O"
|
||||
},
|
||||
"INPUT_PLACEHOLDER": "Enter value",
|
||||
"CONTACT_SEARCH_PLACEHOLDER": "Search contacts",
|
||||
"CONTACT_FALLBACK": "Contact #{id}",
|
||||
"OPERATOR_LABELS": {
|
||||
"equal_to": "Igual a",
|
||||
"not_equal_to": "No és igual a",
|
||||
@@ -49,6 +51,7 @@
|
||||
"ASSIGNEE_NAME": "Nom assignat",
|
||||
"INBOX_NAME": "Nom de la safata d'entrada",
|
||||
"TEAM_NAME": "Nom de l'equip",
|
||||
"CONTACT": "Contacte",
|
||||
"CONVERSATION_IDENTIFIER": "Identificador de conversa",
|
||||
"CAMPAIGN_NAME": "Nom de la campanya",
|
||||
"LABELS": "Etiquetes",
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
{
|
||||
"BULK_ACTION": {
|
||||
"CONVERSATIONS_SELECTED": "{conversationCount} converses seleccionades",
|
||||
"AGENT_SELECT_LABEL": "Seleccionar Agent",
|
||||
"ASSIGN_CONFIRMATION_LABEL": "Estas segur d'assignar {conversationCount} {conversationLabel} a",
|
||||
"UNASSIGN_CONFIRMATION_LABEL": "Estàs segur de desassignar {conversationCount} {conversationLabel}?",
|
||||
"GO_BACK_LABEL": "Torna",
|
||||
"ASSIGN_LABEL": "Assignar",
|
||||
"NONE": "Ningú",
|
||||
"CLEAR_SELECTION": "Neteja",
|
||||
"ASSIGN_AGENT_CONFIRMATION_LABEL": "Are you sure you want to assign {n} conversation to {agentName}? | Are you sure you want to assign {n} conversations to {agentName}?",
|
||||
@@ -20,7 +15,6 @@
|
||||
"RESOLVE_SUCCESFUL": "Les converses s'han resolt correctament.",
|
||||
"RESOLVE_FAILED": "No s'han pogut resoldre les converses. Torna-ho a provar.",
|
||||
"ALL_CONVERSATIONS_SELECTED_ALERT": "Les converses visibles en aquesta pàgina només estan seleccionades.",
|
||||
"AGENT_LIST_LOADING": "S'estan carregant els agents",
|
||||
"UPDATE": {
|
||||
"CHANGE_STATUS": "Canvia l'estat",
|
||||
"SNOOZE_UNTIL": "Posposat",
|
||||
@@ -33,16 +27,16 @@
|
||||
},
|
||||
"LABELS": {
|
||||
"ASSIGN_LABELS": "Assignar etiquetes",
|
||||
"NO_LABELS_FOUND": "No s'han trobat etiquetes",
|
||||
"REMOVE_LABELS": "Remove labels",
|
||||
"ASSIGN_SELECTED_LABELS": "Assigna les etiquetes seleccionades",
|
||||
"REMOVE_SELECTED_LABELS": "Remove selected labels",
|
||||
"ASSIGN_SUCCESFUL": "L'etiqueta s'ha assignat correctament.",
|
||||
"ASSIGN_FAILED": "No s'han pogut assignar les etiquetes. Torna-ho a provar."
|
||||
"ASSIGN_FAILED": "No s'han pogut assignar les etiquetes. Torna-ho a provar.",
|
||||
"REMOVE_SUCCESFUL": "Labels removed successfully.",
|
||||
"REMOVE_FAILED": "Failed to remove labels. Please try again."
|
||||
},
|
||||
"TEAMS": {
|
||||
"TEAM_SELECT_LABEL": "Selecciona equip",
|
||||
"NONE": "Ningú",
|
||||
"NO_TEAMS_AVAILABLE": "Encara no hi ha cap equip afegit a aquest compte.",
|
||||
"ASSIGN_SELECTED_TEAMS": "Assigna l'equip seleccionat.",
|
||||
"ASSIGN_TEAM_CONFIRMATION_LABEL": "Are you sure you want to assign {n} conversation to {teamName}? | Are you sure you want to assign {n} conversations to {teamName}?",
|
||||
"UNASSIGN_TEAM_CONFIRMATION_LABEL": "Are you sure you want to unassign {n} conversation? | Are you sure you want to unassign {n} conversations?",
|
||||
"ASSIGN_SUCCESFUL": "Equips assignats correctament.",
|
||||
|
||||
@@ -88,6 +88,7 @@
|
||||
},
|
||||
"CARD": {
|
||||
"STATUS": {
|
||||
"PROCESSING": "Processing",
|
||||
"COMPLETED": "Completat",
|
||||
"SCHEDULED": "Scheduled"
|
||||
},
|
||||
@@ -146,6 +147,7 @@
|
||||
},
|
||||
"CARD": {
|
||||
"STATUS": {
|
||||
"PROCESSING": "Processing",
|
||||
"COMPLETED": "Completat",
|
||||
"SCHEDULED": "Scheduled"
|
||||
},
|
||||
|
||||
@@ -63,6 +63,7 @@
|
||||
"CODE": "Code",
|
||||
"BULLET_LIST": "Bullet List",
|
||||
"ORDERED_LIST": "Ordered List",
|
||||
"TABLE": "Table"
|
||||
"TABLE": "Table",
|
||||
"IMAGE": "Image"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
"CALL": "Call",
|
||||
"CALL_INITIATED": "Calling the contact…",
|
||||
"CALL_FAILED": "Unable to start the call. Please try again.",
|
||||
"WHATSAPP_CALL_PERMISSION_REQUESTED": "Sent a call permission request to the contact. Try again once they accept.",
|
||||
"WHATSAPP_CALL_PERMISSION_PENDING": "Call permission request already sent recently. Try again once the contact accepts.",
|
||||
"CLICK_TO_EDIT": "Click to edit",
|
||||
"VOICE_INBOX_PICKER": {
|
||||
"TITLE": "Choose a voice inbox"
|
||||
@@ -583,8 +585,11 @@
|
||||
},
|
||||
"CONTACTS_BULK_ACTIONS": {
|
||||
"ASSIGN_LABELS": "Assign Labels",
|
||||
"REMOVE_LABELS": "Remove Labels",
|
||||
"ASSIGN_LABELS_SUCCESS": "L'etiqueta s'ha assignat correctament.",
|
||||
"ASSIGN_LABELS_FAILED": "Failed to assign labels",
|
||||
"REMOVE_LABELS_SUCCESS": "Labels removed successfully.",
|
||||
"REMOVE_LABELS_FAILED": "Failed to remove labels",
|
||||
"DESCRIPTION": "Select the labels you want to add to the selected contacts.",
|
||||
"NO_LABELS_FOUND": "No labels available yet.",
|
||||
"SELECTED_COUNT": "{count} selected",
|
||||
|
||||
@@ -62,6 +62,7 @@
|
||||
"UNSUPPORTED_MESSAGE_FACEBOOK": "Aquest missatge no està suportat. Pots veure aquest missatge a l'aplicació Facebook Messenger.",
|
||||
"UNSUPPORTED_MESSAGE_INSTAGRAM": "Aquest missatge no està suportat. Pots veure aquest missatge a l'aplicació d'Instagram.",
|
||||
"UNSUPPORTED_MESSAGE_TIKTOK": "This message is unsupported. You can view this message on the TikTok app.",
|
||||
"UNSUPPORTED_MESSAGE_WHATSAPP": "This message is unsupported. You can view this message on the WhatsApp app.",
|
||||
"SUCCESS_DELETE_MESSAGE": "El missatge s'ha suprimit correctament",
|
||||
"FAIL_DELETE_MESSSAGE": "No s'ha pogut suprimir el missatge! Torna-ho a provar",
|
||||
"NO_RESPONSE": "Sense resposta",
|
||||
@@ -79,13 +80,22 @@
|
||||
"OUTGOING_CALL": "Outgoing call",
|
||||
"CALL_IN_PROGRESS": "Call in progress",
|
||||
"NO_ANSWER": "No answer",
|
||||
"NO_ANSWER_OUTBOUND_LABEL": "No answer",
|
||||
"NO_ANSWER_OUTBOUND_SUBTEXT": "Contact didn't pick up",
|
||||
"MISSED_CALL": "Missed call",
|
||||
"MISSED_CALL_INBOUND_SUBTEXT": "No agent picked up",
|
||||
"MISSED_CALL_DECLINED_BY": "Declined by {agentName}",
|
||||
"CALL_ENDED": "Call ended",
|
||||
"HANDLED_BY": "Handled by {agentName}",
|
||||
"NOT_ANSWERED_YET": "Not answered yet",
|
||||
"CALLING": "Calling…",
|
||||
"THEY_ANSWERED": "They answered",
|
||||
"YOU_ANSWERED": "You answered",
|
||||
"AGENT_ANSWERED": "{agentName} answered",
|
||||
"JOIN_CALL": "Join call"
|
||||
"JOIN_CALL": "Join call",
|
||||
"CALL_BACK": "Call back",
|
||||
"TRANSCRIPT_SHOW_MORE": "Show more",
|
||||
"TRANSCRIPT_SHOW_LESS": "Show less"
|
||||
},
|
||||
"HEADER": {
|
||||
"RESOLVE_ACTION": "Resoldre",
|
||||
@@ -100,6 +110,12 @@
|
||||
"SNOOZED_UNTIL_TOMORROW": "Posposat fins demà",
|
||||
"SNOOZED_UNTIL_NEXT_WEEK": "Posposat fins a la setmana vinent",
|
||||
"SNOOZED_UNTIL_NEXT_REPLY": "Posposat fins a la següent resposta",
|
||||
"WHATSAPP_CALL": "Start WhatsApp call",
|
||||
"WHATSAPP_CALL_FAILED": "Could not start the WhatsApp call.",
|
||||
"VOICE_CALL": "Start call",
|
||||
"VOICE_CALL_FAILED": "Could not start the call.",
|
||||
"WHATSAPP_CALL_PERMISSION_REQUESTED": "Sent a call permission request to the contact. Try again once they accept.",
|
||||
"WHATSAPP_CALL_PERMISSION_PENDING": "Call permission request already sent recently. Try again once the contact accepts.",
|
||||
"SLA_STATUS": {
|
||||
"FRT": "FRT {status}",
|
||||
"NRT": "NRT {status}",
|
||||
@@ -215,7 +231,9 @@
|
||||
"TIP_AUDIORECORDER_ICON": "Gravar àudio",
|
||||
"TIP_AUDIORECORDER_PERMISSION": "Permet l'accés a l'àudio",
|
||||
"TIP_AUDIORECORDER_ERROR": "No s'ha pogut obrir l'àudio",
|
||||
"AUDIO_CONVERSION_FAILED": "Audio conversion failed. Please try again.",
|
||||
"DRAG_DROP": "Arrossega i deixa anar aquí per adjuntar-lo",
|
||||
"IMAGE_UPLOAD_SUCCESS": "La imatge s'ha carregat correctament",
|
||||
"START_AUDIO_RECORDING": "Inicia la gravació d'àudio",
|
||||
"STOP_AUDIO_RECORDING": "Atura la gravació d'àudio",
|
||||
"COPILOT_THINKING": "Copilot està pensant",
|
||||
@@ -299,8 +317,13 @@
|
||||
"NOT_ANSWERED_YET": "Not answered yet",
|
||||
"HANDLED_IN_ANOTHER_TAB": "Being handled in another tab",
|
||||
"REJECT_CALL": "Reject",
|
||||
"DISMISS_CALL": "Descartar",
|
||||
"JOIN_CALL": "Join call",
|
||||
"END_CALL": "End call"
|
||||
"END_CALL": "End call",
|
||||
"MUTE": "Mute mic",
|
||||
"UNMUTE": "Unmute mic",
|
||||
"VIEW_CHAT_HISTORY": "View chat history",
|
||||
"GO_TO_CONVERSATION": "Go to conversation thread"
|
||||
}
|
||||
},
|
||||
"EMAIL_TRANSCRIPT": {
|
||||
|
||||
@@ -607,9 +607,12 @@
|
||||
"DRAFT": "Esborrany",
|
||||
"ARCHIVE": "Archive",
|
||||
"TRANSLATE": "Tradueix",
|
||||
"MOVE_TO_CATEGORY": "Categoria",
|
||||
"DELETE": "Esborrar",
|
||||
"STATUS_SUCCESS": "Articles updated successfully",
|
||||
"STATUS_ERROR": "Failed to update articles",
|
||||
"CATEGORY_SUCCESS": "Articles moved successfully",
|
||||
"CATEGORY_ERROR": "Failed to move articles",
|
||||
"DELETE_CONFIRM_TITLE": "Delete article | Delete {count} articles",
|
||||
"DELETE_CONFIRM_DESCRIPTION": "This will permanently delete the selected article. This action cannot be undone. | This will permanently delete {count} selected articles. This action cannot be undone.",
|
||||
"DELETE_CONFIRM": "Esborrar",
|
||||
@@ -697,9 +700,27 @@
|
||||
"MAKE_DEFAULT": "Make default",
|
||||
"MOVE_TO_DRAFT": "Move to draft",
|
||||
"PUBLISH_LOCALE": "Publish locale",
|
||||
"CUSTOMIZE_CONTENT": "Localize content",
|
||||
"DELETE": "Esborrar"
|
||||
}
|
||||
},
|
||||
"CONTENT_DIALOG": {
|
||||
"TITLE": "Localize content",
|
||||
"DESCRIPTION": "Set values specific to this locale. Anything left blank falls back to the default locale.",
|
||||
"NAME": {
|
||||
"LABEL": "Nom"
|
||||
},
|
||||
"PAGE_TITLE": {
|
||||
"LABEL": "Page title"
|
||||
},
|
||||
"HEADER_TEXT": {
|
||||
"LABEL": "Header text"
|
||||
},
|
||||
"API": {
|
||||
"SUCCESS_MESSAGE": "Locale content updated successfully",
|
||||
"ERROR_MESSAGE": "Unable to update locale content. Try again."
|
||||
}
|
||||
},
|
||||
"ADD_LOCALE_DIALOG": {
|
||||
"TITLE": "Afegeix una nova localització",
|
||||
"DESCRIPTION": "Select the language in which this article will be written. This will be added to your list of translations, and you can add more later.",
|
||||
@@ -866,6 +887,28 @@
|
||||
},
|
||||
"EDIT_CONFIGURATION": "Edit configuration"
|
||||
},
|
||||
"LAYOUT_CONTENT": {
|
||||
"HEADER": "Aparença",
|
||||
"DESCRIPTION": "Pick the layout that fits how your visitors read.",
|
||||
"LAYOUT": {
|
||||
"CLASSIC": {
|
||||
"TITLE": "Classic",
|
||||
"DESCRIPTION": "A welcoming home page with search and featured topics."
|
||||
},
|
||||
"SIDEBAR": {
|
||||
"TITLE": "Documentation",
|
||||
"DESCRIPTION": "Side-by-side navigation that keeps every guide a click away."
|
||||
}
|
||||
},
|
||||
"SOCIAL_LINKS": {
|
||||
"HEADER": "Social links",
|
||||
"DESCRIPTION": "Add the handle for each network and your help center builds the full link. Shown in the documentation layout footer.",
|
||||
"PLACEHOLDER": "handle",
|
||||
"ADD": "Add social link",
|
||||
"REMOVE": "Suprimeix"
|
||||
},
|
||||
"SAVE": "Save changes"
|
||||
},
|
||||
"API": {
|
||||
"CREATE_PORTAL": {
|
||||
"SUCCESS_MESSAGE": "Portal creat correctament",
|
||||
|
||||
@@ -308,6 +308,7 @@
|
||||
"AUTH_PROCESSING": "Authenticating with Meta",
|
||||
"WAITING_FOR_BUSINESS_INFO": "Please complete business setup in the Meta window...",
|
||||
"PROCESSING": "Setting up your WhatsApp Business Account",
|
||||
"ENABLING_CALLING": "Enabling WhatsApp Calling on your number…",
|
||||
"LOADING_SDK": "Loading Facebook SDK...",
|
||||
"CANCELLED": "WhatsApp Signup was cancelled",
|
||||
"SUCCESS_TITLE": "WhatsApp Business Account Connected!",
|
||||
@@ -317,7 +318,8 @@
|
||||
"AUTH_NOT_COMPLETED": "Authentication not completed. Please restart the process.",
|
||||
"SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured",
|
||||
"MANUAL_FALLBACK": "If your number is already connected to the WhatsApp Business Platform (API), or if you’re a tech provider onboarding your own number, please use the {link} flow",
|
||||
"MANUAL_LINK_TEXT": "manual setup flow"
|
||||
"MANUAL_LINK_TEXT": "manual setup flow",
|
||||
"CALLING_ENABLE_FAILED": "Your WhatsApp inbox is ready, but voice calling couldn't be turned on — this number isn't enrolled in the WhatsApp Business Calling API yet. Reach out to Meta or your WhatsApp Business Solution Provider to onboard it, then turn calling on from the inbox's Calls settings."
|
||||
},
|
||||
"API": {
|
||||
"ERROR_MESSAGE": "No hem pogut desar el canal WhatsApp"
|
||||
@@ -465,6 +467,10 @@
|
||||
"TITLE": "WhatsApp",
|
||||
"DESCRIPTION": "Support your customers on WhatsApp"
|
||||
},
|
||||
"WHATSAPP_CALL": {
|
||||
"TITLE": "WhatsApp Call",
|
||||
"DESCRIPTION": "Take voice calls on your WhatsApp number"
|
||||
},
|
||||
"EMAIL": {
|
||||
"TITLE": "Correu electrònic",
|
||||
"DESCRIPTION": "Connect with Gmail, Outlook, or other providers"
|
||||
@@ -637,7 +643,8 @@
|
||||
"BOT_CONFIGURATION": "Configuracions del bot",
|
||||
"ACCOUNT_HEALTH": "Account Health",
|
||||
"CSAT": "CSAT",
|
||||
"VOICE": "Voice"
|
||||
"VOICE": "Voice",
|
||||
"CALLS": "Calls"
|
||||
},
|
||||
"VOICE_CONFIGURATION": {
|
||||
"ENABLE_VOICE": {
|
||||
@@ -648,6 +655,26 @@
|
||||
"DESCRIPTION": "Voice calling requires Twilio API Key credentials. These are used to generate tokens for agent voice connections."
|
||||
}
|
||||
},
|
||||
"WHATSAPP_CALLING": {
|
||||
"ENABLE": {
|
||||
"LABEL": "Enable WhatsApp Calling",
|
||||
"DESCRIPTION": "Allow agents to receive and place WhatsApp Cloud calls on this inbox. Customers can call this business number directly from WhatsApp."
|
||||
},
|
||||
"ENABLE_FAILED": "Voice calling couldn't be turned on for this number — it isn't enrolled in the WhatsApp Business Calling API yet. Reach out to Meta or your WhatsApp Business Solution Provider to onboard it, then try again.",
|
||||
"PHONE_NUMBER": {
|
||||
"LABEL": "Business phone number",
|
||||
"HELP_TEXT": "WhatsApp number that customers will call."
|
||||
},
|
||||
"HOW_IT_WORKS": {
|
||||
"LABEL": "How it works",
|
||||
"DESCRIPTION": "Calls are placed peer-to-peer between the agent's browser and Meta — no extra credentials are required. Make sure the agent's browser has microphone permission for this site."
|
||||
},
|
||||
"PERMISSION_REQUEST_BODY": {
|
||||
"LABEL": "Call permission request message",
|
||||
"HELP_TEXT": "Shown to the contact when they haven't yet consented to receive calls. Leave blank to use the default.",
|
||||
"PLACEHOLDER": "We would like to call you regarding your conversation."
|
||||
}
|
||||
},
|
||||
"CHANNEL_PREFERENCES": "Channel Preferences",
|
||||
"WIDGET_FEATURES": "Widget features",
|
||||
"ACCOUNT_HEALTH": {
|
||||
@@ -800,6 +827,10 @@
|
||||
"WHATSAPP_TEMPLATES_SYNC_SUBHEADER": "Manually sync message templates from WhatsApp to update your available templates.",
|
||||
"WHATSAPP_TEMPLATES_SYNC_BUTTON": "Sync Templates",
|
||||
"WHATSAPP_TEMPLATES_SYNC_SUCCESS": "Templates sync initiated successfully. It may take a couple of minutes to update.",
|
||||
"WHATSAPP_CALLING_ENABLED": {
|
||||
"LABEL": "Enable voice calling",
|
||||
"DESCRIPTION": "Allow agents to start and receive WhatsApp voice calls on this inbox. Available only on embedded-signup WhatsApp Cloud channels with calling permission granted by Meta."
|
||||
},
|
||||
"UPDATE_PRE_CHAT_FORM_SETTINGS": "Actualitza la configuració del formulari de xat prèvia"
|
||||
},
|
||||
"HELP_CENTER": {
|
||||
|
||||
@@ -57,7 +57,8 @@
|
||||
"CONTACT_CREATED": "Contacte creat",
|
||||
"CONTACT_UPDATED": "Contacte actualitzat",
|
||||
"CONVERSATION_TYPING_ON": "Conversation Typing On",
|
||||
"CONVERSATION_TYPING_OFF": "Conversation Typing Off"
|
||||
"CONVERSATION_TYPING_OFF": "Conversation Typing Off",
|
||||
"INBOX_UPDATED": "Inbox updated"
|
||||
}
|
||||
},
|
||||
"NAME": {
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
"OR": "OR"
|
||||
},
|
||||
"INPUT_PLACEHOLDER": "Enter value",
|
||||
"CONTACT_SEARCH_PLACEHOLDER": "Search contacts",
|
||||
"CONTACT_FALLBACK": "Contact #{id}",
|
||||
"OPERATOR_LABELS": {
|
||||
"equal_to": "Equal to",
|
||||
"not_equal_to": "Not equal to",
|
||||
@@ -49,6 +51,7 @@
|
||||
"ASSIGNEE_NAME": "Assignee name",
|
||||
"INBOX_NAME": "Inbox name",
|
||||
"TEAM_NAME": "Team name",
|
||||
"CONTACT": "Contact",
|
||||
"CONVERSATION_IDENTIFIER": "Conversation identifier",
|
||||
"CAMPAIGN_NAME": "Campaign name",
|
||||
"LABELS": "Štítky",
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
{
|
||||
"BULK_ACTION": {
|
||||
"CONVERSATIONS_SELECTED": "{conversationCount} conversations selected",
|
||||
"AGENT_SELECT_LABEL": "Vybrat agenta",
|
||||
"ASSIGN_CONFIRMATION_LABEL": "Are you sure to assign {conversationCount} {conversationLabel} to",
|
||||
"UNASSIGN_CONFIRMATION_LABEL": "Are you sure to unassign {conversationCount} {conversationLabel}?",
|
||||
"GO_BACK_LABEL": "Go back",
|
||||
"ASSIGN_LABEL": "Přiřadit",
|
||||
"NONE": "Nic",
|
||||
"CLEAR_SELECTION": "Clear",
|
||||
"ASSIGN_AGENT_CONFIRMATION_LABEL": "Are you sure you want to assign {n} conversation to {agentName}? | Are you sure you want to assign {n} conversations to {agentName}?",
|
||||
@@ -20,7 +15,6 @@
|
||||
"RESOLVE_SUCCESFUL": "Conversations resolved successfully.",
|
||||
"RESOLVE_FAILED": "Failed to resolve conversations. Please try again.",
|
||||
"ALL_CONVERSATIONS_SELECTED_ALERT": "Conversations visible on this page are only selected.",
|
||||
"AGENT_LIST_LOADING": "Načítání agentů",
|
||||
"UPDATE": {
|
||||
"CHANGE_STATUS": "Change status",
|
||||
"SNOOZE_UNTIL": "Odložit",
|
||||
@@ -33,16 +27,16 @@
|
||||
},
|
||||
"LABELS": {
|
||||
"ASSIGN_LABELS": "Assign labels",
|
||||
"NO_LABELS_FOUND": "No labels found",
|
||||
"REMOVE_LABELS": "Remove labels",
|
||||
"ASSIGN_SELECTED_LABELS": "Assign selected labels",
|
||||
"REMOVE_SELECTED_LABELS": "Remove selected labels",
|
||||
"ASSIGN_SUCCESFUL": "Labels assigned successfully.",
|
||||
"ASSIGN_FAILED": "Failed to assign labels. Please try again."
|
||||
"ASSIGN_FAILED": "Failed to assign labels. Please try again.",
|
||||
"REMOVE_SUCCESFUL": "Labels removed successfully.",
|
||||
"REMOVE_FAILED": "Failed to remove labels. Please try again."
|
||||
},
|
||||
"TEAMS": {
|
||||
"TEAM_SELECT_LABEL": "Vybrat tým",
|
||||
"NONE": "Nic",
|
||||
"NO_TEAMS_AVAILABLE": "There are no teams added to this account yet.",
|
||||
"ASSIGN_SELECTED_TEAMS": "Assign selected team.",
|
||||
"ASSIGN_TEAM_CONFIRMATION_LABEL": "Are you sure you want to assign {n} conversation to {teamName}? | Are you sure you want to assign {n} conversations to {teamName}?",
|
||||
"UNASSIGN_TEAM_CONFIRMATION_LABEL": "Are you sure you want to unassign {n} conversation? | Are you sure you want to unassign {n} conversations?",
|
||||
"ASSIGN_SUCCESFUL": "Teams assigned successfully.",
|
||||
|
||||
@@ -88,6 +88,7 @@
|
||||
},
|
||||
"CARD": {
|
||||
"STATUS": {
|
||||
"PROCESSING": "Processing",
|
||||
"COMPLETED": "Completed",
|
||||
"SCHEDULED": "Scheduled"
|
||||
},
|
||||
@@ -146,6 +147,7 @@
|
||||
},
|
||||
"CARD": {
|
||||
"STATUS": {
|
||||
"PROCESSING": "Processing",
|
||||
"COMPLETED": "Completed",
|
||||
"SCHEDULED": "Scheduled"
|
||||
},
|
||||
|
||||
@@ -63,6 +63,7 @@
|
||||
"CODE": "Code",
|
||||
"BULLET_LIST": "Bullet List",
|
||||
"ORDERED_LIST": "Ordered List",
|
||||
"TABLE": "Table"
|
||||
"TABLE": "Table",
|
||||
"IMAGE": "Image"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
"CALL": "Call",
|
||||
"CALL_INITIATED": "Calling the contact…",
|
||||
"CALL_FAILED": "Unable to start the call. Please try again.",
|
||||
"WHATSAPP_CALL_PERMISSION_REQUESTED": "Sent a call permission request to the contact. Try again once they accept.",
|
||||
"WHATSAPP_CALL_PERMISSION_PENDING": "Call permission request already sent recently. Try again once the contact accepts.",
|
||||
"CLICK_TO_EDIT": "Click to edit",
|
||||
"VOICE_INBOX_PICKER": {
|
||||
"TITLE": "Choose a voice inbox"
|
||||
@@ -583,8 +585,11 @@
|
||||
},
|
||||
"CONTACTS_BULK_ACTIONS": {
|
||||
"ASSIGN_LABELS": "Assign Labels",
|
||||
"REMOVE_LABELS": "Remove Labels",
|
||||
"ASSIGN_LABELS_SUCCESS": "Labels assigned successfully.",
|
||||
"ASSIGN_LABELS_FAILED": "Failed to assign labels",
|
||||
"REMOVE_LABELS_SUCCESS": "Labels removed successfully.",
|
||||
"REMOVE_LABELS_FAILED": "Failed to remove labels",
|
||||
"DESCRIPTION": "Select the labels you want to add to the selected contacts.",
|
||||
"NO_LABELS_FOUND": "No labels available yet.",
|
||||
"SELECTED_COUNT": "{count} selected",
|
||||
|
||||
@@ -62,6 +62,7 @@
|
||||
"UNSUPPORTED_MESSAGE_FACEBOOK": "This message is unsupported. You can view this message on the Facebook Messenger app.",
|
||||
"UNSUPPORTED_MESSAGE_INSTAGRAM": "This message is unsupported. You can view this message on the Instagram app.",
|
||||
"UNSUPPORTED_MESSAGE_TIKTOK": "This message is unsupported. You can view this message on the TikTok app.",
|
||||
"UNSUPPORTED_MESSAGE_WHATSAPP": "This message is unsupported. You can view this message on the WhatsApp app.",
|
||||
"SUCCESS_DELETE_MESSAGE": "Zpráva byla úspěšně smazána",
|
||||
"FAIL_DELETE_MESSSAGE": "Zpráva se nepodařilo odstranit! Zkuste to znovu",
|
||||
"NO_RESPONSE": "Bez odpovědi",
|
||||
@@ -79,13 +80,22 @@
|
||||
"OUTGOING_CALL": "Outgoing call",
|
||||
"CALL_IN_PROGRESS": "Call in progress",
|
||||
"NO_ANSWER": "No answer",
|
||||
"NO_ANSWER_OUTBOUND_LABEL": "No answer",
|
||||
"NO_ANSWER_OUTBOUND_SUBTEXT": "Contact didn't pick up",
|
||||
"MISSED_CALL": "Missed call",
|
||||
"MISSED_CALL_INBOUND_SUBTEXT": "No agent picked up",
|
||||
"MISSED_CALL_DECLINED_BY": "Declined by {agentName}",
|
||||
"CALL_ENDED": "Call ended",
|
||||
"HANDLED_BY": "Handled by {agentName}",
|
||||
"NOT_ANSWERED_YET": "Not answered yet",
|
||||
"CALLING": "Calling…",
|
||||
"THEY_ANSWERED": "They answered",
|
||||
"YOU_ANSWERED": "You answered",
|
||||
"AGENT_ANSWERED": "{agentName} answered",
|
||||
"JOIN_CALL": "Join call"
|
||||
"JOIN_CALL": "Join call",
|
||||
"CALL_BACK": "Call back",
|
||||
"TRANSCRIPT_SHOW_MORE": "Show more",
|
||||
"TRANSCRIPT_SHOW_LESS": "Show less"
|
||||
},
|
||||
"HEADER": {
|
||||
"RESOLVE_ACTION": "Vyřešit",
|
||||
@@ -100,6 +110,12 @@
|
||||
"SNOOZED_UNTIL_TOMORROW": "Odloženo do zítřka",
|
||||
"SNOOZED_UNTIL_NEXT_WEEK": "Odloženo do příštího týdne",
|
||||
"SNOOZED_UNTIL_NEXT_REPLY": "Odloženo do další odpovědi",
|
||||
"WHATSAPP_CALL": "Start WhatsApp call",
|
||||
"WHATSAPP_CALL_FAILED": "Could not start the WhatsApp call.",
|
||||
"VOICE_CALL": "Start call",
|
||||
"VOICE_CALL_FAILED": "Could not start the call.",
|
||||
"WHATSAPP_CALL_PERMISSION_REQUESTED": "Sent a call permission request to the contact. Try again once they accept.",
|
||||
"WHATSAPP_CALL_PERMISSION_PENDING": "Call permission request already sent recently. Try again once the contact accepts.",
|
||||
"SLA_STATUS": {
|
||||
"FRT": "FRT {status}",
|
||||
"NRT": "NRT {status}",
|
||||
@@ -215,7 +231,9 @@
|
||||
"TIP_AUDIORECORDER_ICON": "Nahrát zvuk",
|
||||
"TIP_AUDIORECORDER_PERMISSION": "Povolit přístup ke zvuku",
|
||||
"TIP_AUDIORECORDER_ERROR": "Zvuk se nepodařilo otevřít",
|
||||
"AUDIO_CONVERSION_FAILED": "Audio conversion failed. Please try again.",
|
||||
"DRAG_DROP": "Přetažením sem připojíte",
|
||||
"IMAGE_UPLOAD_SUCCESS": "Image uploaded successfully",
|
||||
"START_AUDIO_RECORDING": "Spustit nahrávání zvuku",
|
||||
"STOP_AUDIO_RECORDING": "Zastavit nahrávání zvuku",
|
||||
"COPILOT_THINKING": "Copilot přemýšlí",
|
||||
@@ -299,8 +317,13 @@
|
||||
"NOT_ANSWERED_YET": "Not answered yet",
|
||||
"HANDLED_IN_ANOTHER_TAB": "Being handled in another tab",
|
||||
"REJECT_CALL": "Reject",
|
||||
"DISMISS_CALL": "Dismiss",
|
||||
"JOIN_CALL": "Join call",
|
||||
"END_CALL": "End call"
|
||||
"END_CALL": "End call",
|
||||
"MUTE": "Mute mic",
|
||||
"UNMUTE": "Unmute mic",
|
||||
"VIEW_CHAT_HISTORY": "View chat history",
|
||||
"GO_TO_CONVERSATION": "Go to conversation thread"
|
||||
}
|
||||
},
|
||||
"EMAIL_TRANSCRIPT": {
|
||||
|
||||
@@ -607,9 +607,12 @@
|
||||
"DRAFT": "Koncept",
|
||||
"ARCHIVE": "Archive",
|
||||
"TRANSLATE": "Translate",
|
||||
"MOVE_TO_CATEGORY": "Category",
|
||||
"DELETE": "Vymazat",
|
||||
"STATUS_SUCCESS": "Articles updated successfully",
|
||||
"STATUS_ERROR": "Failed to update articles",
|
||||
"CATEGORY_SUCCESS": "Articles moved successfully",
|
||||
"CATEGORY_ERROR": "Failed to move articles",
|
||||
"DELETE_CONFIRM_TITLE": "Delete article | Delete {count} articles",
|
||||
"DELETE_CONFIRM_DESCRIPTION": "This will permanently delete the selected article. This action cannot be undone. | This will permanently delete {count} selected articles. This action cannot be undone.",
|
||||
"DELETE_CONFIRM": "Vymazat",
|
||||
@@ -697,9 +700,27 @@
|
||||
"MAKE_DEFAULT": "Make default",
|
||||
"MOVE_TO_DRAFT": "Move to draft",
|
||||
"PUBLISH_LOCALE": "Publish locale",
|
||||
"CUSTOMIZE_CONTENT": "Localize content",
|
||||
"DELETE": "Vymazat"
|
||||
}
|
||||
},
|
||||
"CONTENT_DIALOG": {
|
||||
"TITLE": "Localize content",
|
||||
"DESCRIPTION": "Set values specific to this locale. Anything left blank falls back to the default locale.",
|
||||
"NAME": {
|
||||
"LABEL": "Název"
|
||||
},
|
||||
"PAGE_TITLE": {
|
||||
"LABEL": "Page title"
|
||||
},
|
||||
"HEADER_TEXT": {
|
||||
"LABEL": "Header text"
|
||||
},
|
||||
"API": {
|
||||
"SUCCESS_MESSAGE": "Locale content updated successfully",
|
||||
"ERROR_MESSAGE": "Unable to update locale content. Try again."
|
||||
}
|
||||
},
|
||||
"ADD_LOCALE_DIALOG": {
|
||||
"TITLE": "Add a new locale",
|
||||
"DESCRIPTION": "Select the language in which this article will be written. This will be added to your list of translations, and you can add more later.",
|
||||
@@ -866,6 +887,28 @@
|
||||
},
|
||||
"EDIT_CONFIGURATION": "Edit configuration"
|
||||
},
|
||||
"LAYOUT_CONTENT": {
|
||||
"HEADER": "Appearance",
|
||||
"DESCRIPTION": "Pick the layout that fits how your visitors read.",
|
||||
"LAYOUT": {
|
||||
"CLASSIC": {
|
||||
"TITLE": "Classic",
|
||||
"DESCRIPTION": "A welcoming home page with search and featured topics."
|
||||
},
|
||||
"SIDEBAR": {
|
||||
"TITLE": "Documentation",
|
||||
"DESCRIPTION": "Side-by-side navigation that keeps every guide a click away."
|
||||
}
|
||||
},
|
||||
"SOCIAL_LINKS": {
|
||||
"HEADER": "Social links",
|
||||
"DESCRIPTION": "Add the handle for each network and your help center builds the full link. Shown in the documentation layout footer.",
|
||||
"PLACEHOLDER": "handle",
|
||||
"ADD": "Add social link",
|
||||
"REMOVE": "Odebrat"
|
||||
},
|
||||
"SAVE": "Save changes"
|
||||
},
|
||||
"API": {
|
||||
"CREATE_PORTAL": {
|
||||
"SUCCESS_MESSAGE": "Portal created successfully",
|
||||
|
||||
@@ -308,6 +308,7 @@
|
||||
"AUTH_PROCESSING": "Authenticating with Meta",
|
||||
"WAITING_FOR_BUSINESS_INFO": "Please complete business setup in the Meta window...",
|
||||
"PROCESSING": "Setting up your WhatsApp Business Account",
|
||||
"ENABLING_CALLING": "Enabling WhatsApp Calling on your number…",
|
||||
"LOADING_SDK": "Loading Facebook SDK...",
|
||||
"CANCELLED": "WhatsApp Signup was cancelled",
|
||||
"SUCCESS_TITLE": "WhatsApp Business Account Connected!",
|
||||
@@ -317,7 +318,8 @@
|
||||
"AUTH_NOT_COMPLETED": "Authentication not completed. Please restart the process.",
|
||||
"SUCCESS_FALLBACK": "WhatsApp Business Account has been successfully configured",
|
||||
"MANUAL_FALLBACK": "If your number is already connected to the WhatsApp Business Platform (API), or if you’re a tech provider onboarding your own number, please use the {link} flow",
|
||||
"MANUAL_LINK_TEXT": "manual setup flow"
|
||||
"MANUAL_LINK_TEXT": "manual setup flow",
|
||||
"CALLING_ENABLE_FAILED": "Your WhatsApp inbox is ready, but voice calling couldn't be turned on — this number isn't enrolled in the WhatsApp Business Calling API yet. Reach out to Meta or your WhatsApp Business Solution Provider to onboard it, then turn calling on from the inbox's Calls settings."
|
||||
},
|
||||
"API": {
|
||||
"ERROR_MESSAGE": "We were not able to save the WhatsApp channel"
|
||||
@@ -465,6 +467,10 @@
|
||||
"TITLE": "WhatsApp",
|
||||
"DESCRIPTION": "Support your customers on WhatsApp"
|
||||
},
|
||||
"WHATSAPP_CALL": {
|
||||
"TITLE": "WhatsApp Call",
|
||||
"DESCRIPTION": "Take voice calls on your WhatsApp number"
|
||||
},
|
||||
"EMAIL": {
|
||||
"TITLE": "E-mailová adresa",
|
||||
"DESCRIPTION": "Connect with Gmail, Outlook, or other providers"
|
||||
@@ -637,7 +643,8 @@
|
||||
"BOT_CONFIGURATION": "Bot Configuration",
|
||||
"ACCOUNT_HEALTH": "Account Health",
|
||||
"CSAT": "CSAT",
|
||||
"VOICE": "Voice"
|
||||
"VOICE": "Voice",
|
||||
"CALLS": "Calls"
|
||||
},
|
||||
"VOICE_CONFIGURATION": {
|
||||
"ENABLE_VOICE": {
|
||||
@@ -648,6 +655,26 @@
|
||||
"DESCRIPTION": "Voice calling requires Twilio API Key credentials. These are used to generate tokens for agent voice connections."
|
||||
}
|
||||
},
|
||||
"WHATSAPP_CALLING": {
|
||||
"ENABLE": {
|
||||
"LABEL": "Enable WhatsApp Calling",
|
||||
"DESCRIPTION": "Allow agents to receive and place WhatsApp Cloud calls on this inbox. Customers can call this business number directly from WhatsApp."
|
||||
},
|
||||
"ENABLE_FAILED": "Voice calling couldn't be turned on for this number — it isn't enrolled in the WhatsApp Business Calling API yet. Reach out to Meta or your WhatsApp Business Solution Provider to onboard it, then try again.",
|
||||
"PHONE_NUMBER": {
|
||||
"LABEL": "Business phone number",
|
||||
"HELP_TEXT": "WhatsApp number that customers will call."
|
||||
},
|
||||
"HOW_IT_WORKS": {
|
||||
"LABEL": "How it works",
|
||||
"DESCRIPTION": "Calls are placed peer-to-peer between the agent's browser and Meta — no extra credentials are required. Make sure the agent's browser has microphone permission for this site."
|
||||
},
|
||||
"PERMISSION_REQUEST_BODY": {
|
||||
"LABEL": "Call permission request message",
|
||||
"HELP_TEXT": "Shown to the contact when they haven't yet consented to receive calls. Leave blank to use the default.",
|
||||
"PLACEHOLDER": "We would like to call you regarding your conversation."
|
||||
}
|
||||
},
|
||||
"CHANNEL_PREFERENCES": "Channel Preferences",
|
||||
"WIDGET_FEATURES": "Widget features",
|
||||
"ACCOUNT_HEALTH": {
|
||||
@@ -800,6 +827,10 @@
|
||||
"WHATSAPP_TEMPLATES_SYNC_SUBHEADER": "Manually sync message templates from WhatsApp to update your available templates.",
|
||||
"WHATSAPP_TEMPLATES_SYNC_BUTTON": "Sync Templates",
|
||||
"WHATSAPP_TEMPLATES_SYNC_SUCCESS": "Templates sync initiated successfully. It may take a couple of minutes to update.",
|
||||
"WHATSAPP_CALLING_ENABLED": {
|
||||
"LABEL": "Enable voice calling",
|
||||
"DESCRIPTION": "Allow agents to start and receive WhatsApp voice calls on this inbox. Available only on embedded-signup WhatsApp Cloud channels with calling permission granted by Meta."
|
||||
},
|
||||
"UPDATE_PRE_CHAT_FORM_SETTINGS": "Update Pre Chat Form Settings"
|
||||
},
|
||||
"HELP_CENTER": {
|
||||
|
||||
@@ -57,7 +57,8 @@
|
||||
"CONTACT_CREATED": "Contact created",
|
||||
"CONTACT_UPDATED": "Contact updated",
|
||||
"CONVERSATION_TYPING_ON": "Conversation Typing On",
|
||||
"CONVERSATION_TYPING_OFF": "Conversation Typing Off"
|
||||
"CONVERSATION_TYPING_OFF": "Conversation Typing Off",
|
||||
"INBOX_UPDATED": "Inbox updated"
|
||||
}
|
||||
},
|
||||
"NAME": {
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
"OR": "ELLER"
|
||||
},
|
||||
"INPUT_PLACEHOLDER": "Enter value",
|
||||
"CONTACT_SEARCH_PLACEHOLDER": "Search contacts",
|
||||
"CONTACT_FALLBACK": "Contact #{id}",
|
||||
"OPERATOR_LABELS": {
|
||||
"equal_to": "Lig med",
|
||||
"not_equal_to": "Ikke lig med",
|
||||
@@ -49,6 +51,7 @@
|
||||
"ASSIGNEE_NAME": "Assignee name",
|
||||
"INBOX_NAME": "Indbakkens navn",
|
||||
"TEAM_NAME": "Team navn",
|
||||
"CONTACT": "Kontakt",
|
||||
"CONVERSATION_IDENTIFIER": "Conversation identifier",
|
||||
"CAMPAIGN_NAME": "Campaign name",
|
||||
"LABELS": "Etiketter",
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
{
|
||||
"BULK_ACTION": {
|
||||
"CONVERSATIONS_SELECTED": "{conversationCount} samtaler valgt",
|
||||
"AGENT_SELECT_LABEL": "Vælg agent",
|
||||
"ASSIGN_CONFIRMATION_LABEL": "Are you sure to assign {conversationCount} {conversationLabel} to",
|
||||
"UNASSIGN_CONFIRMATION_LABEL": "Are you sure to unassign {conversationCount} {conversationLabel}?",
|
||||
"GO_BACK_LABEL": "Gå tilbage",
|
||||
"ASSIGN_LABEL": "Tildel",
|
||||
"NONE": "Ingen",
|
||||
"CLEAR_SELECTION": "Clear",
|
||||
"ASSIGN_AGENT_CONFIRMATION_LABEL": "Are you sure you want to assign {n} conversation to {agentName}? | Are you sure you want to assign {n} conversations to {agentName}?",
|
||||
@@ -20,7 +15,6 @@
|
||||
"RESOLVE_SUCCESFUL": "Samtaler løst med succes.",
|
||||
"RESOLVE_FAILED": "Failed to resolve conversations. Please try again.",
|
||||
"ALL_CONVERSATIONS_SELECTED_ALERT": "Samtaler synlig på denne side er kun valgt.",
|
||||
"AGENT_LIST_LOADING": "Indlæser agenter",
|
||||
"UPDATE": {
|
||||
"CHANGE_STATUS": "Skift status",
|
||||
"SNOOZE_UNTIL": "Udsæt",
|
||||
@@ -33,16 +27,16 @@
|
||||
},
|
||||
"LABELS": {
|
||||
"ASSIGN_LABELS": "Assign labels",
|
||||
"NO_LABELS_FOUND": "Ingen labels fundet",
|
||||
"REMOVE_LABELS": "Remove labels",
|
||||
"ASSIGN_SELECTED_LABELS": "Tildel valgte etiketter",
|
||||
"REMOVE_SELECTED_LABELS": "Remove selected labels",
|
||||
"ASSIGN_SUCCESFUL": "Etiketter tildelt med succes.",
|
||||
"ASSIGN_FAILED": "Failed to assign labels. Please try again."
|
||||
"ASSIGN_FAILED": "Failed to assign labels. Please try again.",
|
||||
"REMOVE_SUCCESFUL": "Labels removed successfully.",
|
||||
"REMOVE_FAILED": "Failed to remove labels. Please try again."
|
||||
},
|
||||
"TEAMS": {
|
||||
"TEAM_SELECT_LABEL": "Vælg hold",
|
||||
"NONE": "Ingen",
|
||||
"NO_TEAMS_AVAILABLE": "There are no teams added to this account yet.",
|
||||
"ASSIGN_SELECTED_TEAMS": "Tildel det valgte team.",
|
||||
"ASSIGN_TEAM_CONFIRMATION_LABEL": "Are you sure you want to assign {n} conversation to {teamName}? | Are you sure you want to assign {n} conversations to {teamName}?",
|
||||
"UNASSIGN_TEAM_CONFIRMATION_LABEL": "Are you sure you want to unassign {n} conversation? | Are you sure you want to unassign {n} conversations?",
|
||||
"ASSIGN_SUCCESFUL": "Teams assigned successfully.",
|
||||
|
||||
@@ -88,6 +88,7 @@
|
||||
},
|
||||
"CARD": {
|
||||
"STATUS": {
|
||||
"PROCESSING": "Processing",
|
||||
"COMPLETED": "Afsluttet",
|
||||
"SCHEDULED": "Scheduled"
|
||||
},
|
||||
@@ -146,6 +147,7 @@
|
||||
},
|
||||
"CARD": {
|
||||
"STATUS": {
|
||||
"PROCESSING": "Processing",
|
||||
"COMPLETED": "Afsluttet",
|
||||
"SCHEDULED": "Scheduled"
|
||||
},
|
||||
|
||||
@@ -63,6 +63,7 @@
|
||||
"CODE": "Code",
|
||||
"BULLET_LIST": "Bullet List",
|
||||
"ORDERED_LIST": "Ordered List",
|
||||
"TABLE": "Table"
|
||||
"TABLE": "Table",
|
||||
"IMAGE": "Image"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
"CALL": "Call",
|
||||
"CALL_INITIATED": "Calling the contact…",
|
||||
"CALL_FAILED": "Unable to start the call. Please try again.",
|
||||
"WHATSAPP_CALL_PERMISSION_REQUESTED": "Sent a call permission request to the contact. Try again once they accept.",
|
||||
"WHATSAPP_CALL_PERMISSION_PENDING": "Call permission request already sent recently. Try again once the contact accepts.",
|
||||
"CLICK_TO_EDIT": "Click to edit",
|
||||
"VOICE_INBOX_PICKER": {
|
||||
"TITLE": "Choose a voice inbox"
|
||||
@@ -583,8 +585,11 @@
|
||||
},
|
||||
"CONTACTS_BULK_ACTIONS": {
|
||||
"ASSIGN_LABELS": "Tildel Etiketter",
|
||||
"REMOVE_LABELS": "Remove Labels",
|
||||
"ASSIGN_LABELS_SUCCESS": "Etiketter tildelt med succes.",
|
||||
"ASSIGN_LABELS_FAILED": "Failed to assign labels",
|
||||
"REMOVE_LABELS_SUCCESS": "Labels removed successfully.",
|
||||
"REMOVE_LABELS_FAILED": "Failed to remove labels",
|
||||
"DESCRIPTION": "Select the labels you want to add to the selected contacts.",
|
||||
"NO_LABELS_FOUND": "No labels available yet.",
|
||||
"SELECTED_COUNT": "{count} selected",
|
||||
|
||||
@@ -62,6 +62,7 @@
|
||||
"UNSUPPORTED_MESSAGE_FACEBOOK": "This message is unsupported. You can view this message on the Facebook Messenger app.",
|
||||
"UNSUPPORTED_MESSAGE_INSTAGRAM": "This message is unsupported. You can view this message on the Instagram app.",
|
||||
"UNSUPPORTED_MESSAGE_TIKTOK": "This message is unsupported. You can view this message on the TikTok app.",
|
||||
"UNSUPPORTED_MESSAGE_WHATSAPP": "This message is unsupported. You can view this message on the WhatsApp app.",
|
||||
"SUCCESS_DELETE_MESSAGE": "Besked slettet",
|
||||
"FAIL_DELETE_MESSSAGE": "Kunne ikke slette beskeden! Prøv igen",
|
||||
"NO_RESPONSE": "Intet svar",
|
||||
@@ -79,13 +80,22 @@
|
||||
"OUTGOING_CALL": "Outgoing call",
|
||||
"CALL_IN_PROGRESS": "Call in progress",
|
||||
"NO_ANSWER": "No answer",
|
||||
"NO_ANSWER_OUTBOUND_LABEL": "No answer",
|
||||
"NO_ANSWER_OUTBOUND_SUBTEXT": "Contact didn't pick up",
|
||||
"MISSED_CALL": "Missed call",
|
||||
"MISSED_CALL_INBOUND_SUBTEXT": "No agent picked up",
|
||||
"MISSED_CALL_DECLINED_BY": "Declined by {agentName}",
|
||||
"CALL_ENDED": "Call ended",
|
||||
"HANDLED_BY": "Handled by {agentName}",
|
||||
"NOT_ANSWERED_YET": "Not answered yet",
|
||||
"CALLING": "Calling…",
|
||||
"THEY_ANSWERED": "They answered",
|
||||
"YOU_ANSWERED": "You answered",
|
||||
"AGENT_ANSWERED": "{agentName} answered",
|
||||
"JOIN_CALL": "Join call"
|
||||
"JOIN_CALL": "Join call",
|
||||
"CALL_BACK": "Call back",
|
||||
"TRANSCRIPT_SHOW_MORE": "Show more",
|
||||
"TRANSCRIPT_SHOW_LESS": "Show less"
|
||||
},
|
||||
"HEADER": {
|
||||
"RESOLVE_ACTION": "Løs",
|
||||
@@ -100,6 +110,12 @@
|
||||
"SNOOZED_UNTIL_TOMORROW": "Udsat til i morgen",
|
||||
"SNOOZED_UNTIL_NEXT_WEEK": "Udsat indtil næste uge",
|
||||
"SNOOZED_UNTIL_NEXT_REPLY": "Udsat indtil næste svar",
|
||||
"WHATSAPP_CALL": "Start WhatsApp call",
|
||||
"WHATSAPP_CALL_FAILED": "Could not start the WhatsApp call.",
|
||||
"VOICE_CALL": "Start call",
|
||||
"VOICE_CALL_FAILED": "Could not start the call.",
|
||||
"WHATSAPP_CALL_PERMISSION_REQUESTED": "Sent a call permission request to the contact. Try again once they accept.",
|
||||
"WHATSAPP_CALL_PERMISSION_PENDING": "Call permission request already sent recently. Try again once the contact accepts.",
|
||||
"SLA_STATUS": {
|
||||
"FRT": "FRT {status}",
|
||||
"NRT": "NRT {status}",
|
||||
@@ -215,7 +231,9 @@
|
||||
"TIP_AUDIORECORDER_ICON": "Optag lyd",
|
||||
"TIP_AUDIORECORDER_PERMISSION": "Tillad adgang til lyd",
|
||||
"TIP_AUDIORECORDER_ERROR": "Kunne ikke åbne lyden",
|
||||
"AUDIO_CONVERSION_FAILED": "Audio conversion failed. Please try again.",
|
||||
"DRAG_DROP": "Træk og slip her for at vedhæfte",
|
||||
"IMAGE_UPLOAD_SUCCESS": "Billede blev uploadet",
|
||||
"START_AUDIO_RECORDING": "Start lydoptagelse",
|
||||
"STOP_AUDIO_RECORDING": "Stop lydoptagelse",
|
||||
"COPILOT_THINKING": "Copilot tænker",
|
||||
@@ -299,8 +317,13 @@
|
||||
"NOT_ANSWERED_YET": "Not answered yet",
|
||||
"HANDLED_IN_ANOTHER_TAB": "Being handled in another tab",
|
||||
"REJECT_CALL": "Reject",
|
||||
"DISMISS_CALL": "Dismiss",
|
||||
"JOIN_CALL": "Join call",
|
||||
"END_CALL": "End call"
|
||||
"END_CALL": "End call",
|
||||
"MUTE": "Mute mic",
|
||||
"UNMUTE": "Unmute mic",
|
||||
"VIEW_CHAT_HISTORY": "View chat history",
|
||||
"GO_TO_CONVERSATION": "Go to conversation thread"
|
||||
}
|
||||
},
|
||||
"EMAIL_TRANSCRIPT": {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user