diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index b6473cf13..924a4e451 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -29,7 +29,11 @@ class ApplicationController < ActionController::Base end def switch_locale(account) - I18n.locale = (I18n.available_locales.map(&:to_s).include?(account.locale) ? account.locale : nil) || I18n.default_locale + # priority is for locale set in query string (mostly for widget/from js sdk) + locale ||= (I18n.available_locales.map(&:to_s).include?(params[:locale]) ? params[:locale] : nil) + # if local is not set in param, lets try account + locale ||= (I18n.available_locales.map(&:to_s).include?(account.locale) ? account.locale : nil) + I18n.locale = locale || I18n.default_locale end def account_accessible_for_user?(account) diff --git a/app/javascript/packs/sdk.js b/app/javascript/packs/sdk.js index a32b9dca4..ba0187d52 100755 --- a/app/javascript/packs/sdk.js +++ b/app/javascript/packs/sdk.js @@ -10,6 +10,7 @@ const runSDK = ({ baseUrl, websiteToken }) => { isOpen: false, position: chatwootSettings.position === 'left' ? 'left' : 'right', websiteToken, + locale: chatwootSettings.locale || 'en', toggle() { IFrameHelper.events.toggleBubble(); @@ -36,6 +37,10 @@ const runSDK = ({ baseUrl, websiteToken }) => { IFrameHelper.sendMessage('remove-label', { label }); }, + setLocale(localeToBeUsed = 'en') { + IFrameHelper.sendMessage('set-locale', { locale: localeToBeUsed }); + }, + reset() { if (window.$chatwoot.isOpen) { IFrameHelper.events.toggleBubble(); diff --git a/app/javascript/sdk/IFrameHelper.js b/app/javascript/sdk/IFrameHelper.js index eb7a32d25..e5fadfbd4 100644 --- a/app/javascript/sdk/IFrameHelper.js +++ b/app/javascript/sdk/IFrameHelper.js @@ -76,7 +76,9 @@ export const IFrameHelper = { expires: 365, }); window.$chatwoot.hasLoaded = true; - IFrameHelper.sendMessage('config-set', {}); + IFrameHelper.sendMessage('config-set', { + locale: window.$chatwoot.locale, + }); IFrameHelper.onLoad(message.config.channelConfig); IFrameHelper.setCurrentUrl(); IFrameHelper.toggleCloseButton(); diff --git a/app/javascript/widget/App.vue b/app/javascript/widget/App.vue index 11dd1b9c5..e5414a1ac 100755 --- a/app/javascript/widget/App.vue +++ b/app/javascript/widget/App.vue @@ -19,7 +19,7 @@ export default { }, mounted() { const { websiteToken, locale } = window.chatwootWebChannel; - Vue.config.lang = locale; + this.setLocale(locale); if (IFrameHelper.isIFrame()) { IFrameHelper.sendMessage({ @@ -44,6 +44,7 @@ export default { if (message.event === 'config-set') { this.fetchOldConversations(); this.fetchAvailableAgents(websiteToken); + this.setLocale(message.locale); } else if (message.event === 'widget-visible') { this.scrollConversationToBottom(); } else if (message.event === 'set-current-url') { @@ -58,6 +59,8 @@ export default { this.$store.dispatch('conversationLabels/destroy', message.label); } else if (message.event === 'set-user') { this.$store.dispatch('contacts/update', message); + } else if (message.event === 'set-locale') { + this.setLocale(message.locale); } }); @@ -71,6 +74,12 @@ export default { const container = this.$el.querySelector('.conversation-wrap'); container.scrollTop = container.scrollHeight; }, + setLocale(locale) { + const { enabledLanguages } = window.chatwootWebChannel; + if (enabledLanguages.some(lang => lang.iso_639_1_code === locale)) { + Vue.config.lang = locale; + } + }, }, }; diff --git a/app/javascript/widget/api/endPoints.js b/app/javascript/widget/api/endPoints.js index c86f915e4..ef51af907 100755 --- a/app/javascript/widget/api/endPoints.js +++ b/app/javascript/widget/api/endPoints.js @@ -1,13 +1,26 @@ -const sendMessage = content => ({ - url: `/api/v1/widget/messages${window.location.search}`, - params: { - message: { - content, - timestamp: new Date().toString(), - referer_url: window.refererURL || '', +import Vue from 'vue'; + +const sendMessage = content => { + const locale = Vue.config.lang; + const refererURL = window.refererURL || ''; + let search = window.location.search; + if (search) { + search = `${search}&locale=${locale}`; + } else { + search = `?locale=${locale}`; + } + + return { + url: `/api/v1/widget/messages${search}`, + params: { + message: { + content, + timestamp: new Date().toString(), + referer_url: refererURL, + }, }, - }, -}); + }; +}; const sendAttachment = ({ attachment }) => { const { refererURL = '' } = window; diff --git a/app/javascript/widget/api/specs/endPoints.spec.js b/app/javascript/widget/api/specs/endPoints.spec.js index d17b315ba..396df2c71 100644 --- a/app/javascript/widget/api/specs/endPoints.spec.js +++ b/app/javascript/widget/api/specs/endPoints.spec.js @@ -1,12 +1,14 @@ import endPoints from '../endPoints'; +jest.mock('vue', () => ({ config: { lang: 'ar' } })); + describe('#sendMessage', () => { it('returns correct payload', () => { const spy = jest.spyOn(global, 'Date').mockImplementation(() => ({ toString: () => 'mock date', })); expect(endPoints.sendMessage('hello')).toEqual({ - url: `/api/v1/widget/messages`, + url: `/api/v1/widget/messages?locale=ar`, params: { message: { content: 'hello', diff --git a/app/javascript/widget/i18n/index.js b/app/javascript/widget/i18n/index.js index 524ae1480..5cf0282f8 100644 --- a/app/javascript/widget/i18n/index.js +++ b/app/javascript/widget/i18n/index.js @@ -1,15 +1,23 @@ +import { default as ar } from './locale/ar.json'; import { default as ca } from './locale/ca.json'; import { default as de } from './locale/de.json'; import { default as el } from './locale/el.json'; import { default as en } from './locale/en.json'; +import { default as fr } from './locale/fr.json'; import { default as ml } from './locale/ml.json'; +import { default as nl } from './locale/nl.json'; import { default as pt } from './locale/pt.json'; +import { default as pt_BR } from './locale/pt_BR.json'; export default { + ar, ca, de, el, en, + fr, ml, + nl, pt, + pt_BR, }; diff --git a/app/views/widget_tests/index.html.erb b/app/views/widget_tests/index.html.erb index 8af7dd5a0..4642d88bf 100644 --- a/app/views/widget_tests/index.html.erb +++ b/app/views/widget_tests/index.html.erb @@ -5,6 +5,7 @@ window.chatwootSettings = { hideMessageBubble: false, position: 'left', + locale: 'fr', }; (function(d,t) { diff --git a/app/views/widgets/show.html.erb b/app/views/widgets/show.html.erb index 3bf98cf99..765151101 100644 --- a/app/views/widgets/show.html.erb +++ b/app/views/widgets/show.html.erb @@ -17,6 +17,7 @@ welcomeTagline: '<%= @web_widget.welcome_tagline %>', welcomeTitle: '<%= @web_widget.welcome_title %>', widgetColor: '<%= @web_widget.widget_color %>', + enabledLanguages: <%= available_locales_with_name.to_json.html_safe %>, } window.chatwootWidgetDefaults = { useInboxAvatarForBot: <%= ActiveModel::Type::Boolean.new.cast(ENV.fetch('USE_INBOX_AVATAR_FOR_BOT', false)) %>, diff --git a/docs/channels/website-sdk.md b/docs/channels/website-sdk.md index 42c83829c..be59f8f69 100644 --- a/docs/channels/website-sdk.md +++ b/docs/channels/website-sdk.md @@ -14,6 +14,7 @@ To hide the bubble, you can use the following setting. Please not if you use thi window.chatwootSettings = { hideMessageBubble: false, position: 'left', // This can be left or right + locale: 'en', // Language to be set } ``` @@ -37,6 +38,14 @@ window.$chatwoot.setUser('identifier_key', { Make sure that you reset the session when the user logouts of your app. +### To set language manually + +```js +window.$chatwoot.setLocale('en') +``` + +To set the language manually use the setLocale function. + ### To set labels on the conversation Please note that the labels will be set on a conversation, if the user has not started a conversation, then the following items will not have any effect.