Skip to content

Commit c4110f6

Browse files
feat: Add language options to the embed
1 parent c8b5346 commit c4110f6

5 files changed

Lines changed: 147 additions & 8 deletions

File tree

frontend/public/assistant.js

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
; (function () {
1+
;(function () {
22
window.sqlbot_assistant_handler = window.sqlbot_assistant_handler || {}
33
const defaultData = {
44
id: '1',
@@ -74,6 +74,9 @@
7474
if (data.history) {
7575
srcUrl += `&history=${data.history}`
7676
}
77+
if (data.lang) {
78+
srcUrl += `&lang=${data.lang}`
79+
}
7780
return `
7881
<div id="sqlbot-assistant-chat-container">
7982
<iframe id="sqlbot-assistant-chat-iframe-${data.id}" allow="microphone;clipboard-read 'src'; clipboard-write 'src'" src="${srcUrl}"></iframe>
@@ -171,12 +174,12 @@
171174
}
172175
chat_button_img.style.display = 'block'
173176
function resizeImg() {
174-
const rate = window.outerWidth / window.innerWidth;
175-
chat_button_img.style.width = `${30 * (1 / rate)}px`;
176-
chat_button_img.style.height = `${30 * (1 / rate)}px`;
177+
const rate = window.outerWidth / window.innerWidth
178+
chat_button_img.style.width = `${30 * (1 / rate)}px`
179+
chat_button_img.style.height = `${30 * (1 / rate)}px`
177180
}
178181
resizeImg()
179-
window.addEventListener('resize', resizeImg);
182+
window.addEventListener('resize', resizeImg)
180183
// 对话框元素
181184
const chat_container = root.querySelector('#sqlbot-assistant-chat-container')
182185
// 引导层
@@ -567,6 +570,7 @@
567570
const online = getParam(src, 'online')
568571
const userFlag = getParam(src, 'userFlag')
569572
const history = getParam(src, 'history')
573+
const lang = getParam(src, 'lang')
570574
let url = `${domain_url}/api/v1/system/assistant/info/${id}`
571575
if (domain_url.includes('5173')) {
572576
url = url.replace('5173', '8000')
@@ -605,6 +609,7 @@
605609
tempData['online'] = online && online.toString().toLowerCase() == 'true'
606610
tempData['userFlag'] = userFlag
607611
tempData['history'] = history
612+
tempData['lang'] = lang
608613
initsqlbot_assistant(tempData)
609614
registerMessageEvent(id, tempData)
610615
})
@@ -776,6 +781,24 @@
776781
contentWindow.postMessage(params, url)
777782
}
778783
}
784+
window.sqlbot_assistant_handler[id]['setLang'] = (lang) => {
785+
if (lang != null && typeof lang != 'string') {
786+
throw new Error('The parameter can only be of type string')
787+
}
788+
const iframe = document.getElementById(`sqlbot-assistant-chat-iframe-${id}`)
789+
if (iframe) {
790+
const url = iframe.src
791+
const eventName = 'sqlbot_assistant_event'
792+
const params = {
793+
busi: 'setLang',
794+
lang,
795+
eventName,
796+
messageId: id,
797+
}
798+
const contentWindow = iframe.contentWindow
799+
contentWindow.postMessage(params, url)
800+
}
801+
}
779802
window.sqlbot_assistant_handler[id]['refresh'] = (online, userFlag) => {
780803
if (online != null && typeof online != 'boolean') {
781804
throw new Error('The parameter can only be of type boolean')

frontend/src/i18n/index.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,31 @@ import { getBrowserLocale } from '@/utils/utils'
1212
const elementKoLocale = elementEnLocale
1313
const { wsCache } = useCache()
1414

15+
const isEmbeddedRoute = () => {
16+
const hash = window.location.hash
17+
if (!hash) return false
18+
const hashPath = hash.substring(1).split('?')[0]
19+
return ['/assistant', '/embeddedPage', '/embeddedCommon'].includes(hashPath)
20+
}
21+
22+
const getUrlLang = () => {
23+
try {
24+
const hash = window.location.hash
25+
if (!hash) return null
26+
const hashQuery = hash.substring(1).split('?')[1]
27+
if (!hashQuery) return null
28+
return new URLSearchParams(hashQuery).get('lang')
29+
} catch {
30+
return null
31+
}
32+
}
33+
1534
const getDefaultLocale = () => {
35+
// 嵌入式页面的 URL lang 参数(第三种国际化渠道),优先级最高
36+
const urlLang = getUrlLang()
37+
if (urlLang && isEmbeddedRoute()) {
38+
return urlLang
39+
}
1640
return wsCache.get('user.language') || getBrowserLocale() || 'zh-CN'
1741
}
1842

frontend/src/views/embedded/common.vue

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,13 @@ const divLoading = ref(true)
3030
const eventName = 'sqlbot_embedded_event'
3131
const busiFlag = ref('ds')
3232
33+
const setParamLanguage = () => {
34+
const lang = route.query.lang
35+
if (lang) {
36+
userStore.setLanguage(lang as string)
37+
}
38+
}
39+
3340
const isWsAdmin = computed(() => {
3441
return userStore.isAdmin || userStore.isSpaceAdmin
3542
})
@@ -49,13 +56,21 @@ const communicationCb = async (event: any) => {
4956
assistantStore.setType(type)
5057
assistantStore.setToken(certificate)
5158
assistantStore.setAssistant(true)
52-
await userStore.info()
59+
try {
60+
await userStore.info()
61+
} catch (e) {
62+
console.error('Failed to fetch user info in common embedded:', e)
63+
}
64+
setParamLanguage()
5365
loading.value = false
5466
return
5567
}
5668
if (event.data?.hostOrigin) {
5769
assistantStore.setHostOrigin(event.data?.hostOrigin)
5870
}
71+
if (event.data?.busi == 'setLang') {
72+
userStore.setLanguage(event.data.lang)
73+
}
5974
}
6075
}
6176
@@ -82,6 +97,7 @@ const registerReady = (assistantId: any) => {
8297
}
8398
8499
onBeforeMount(async () => {
100+
setParamLanguage()
85101
const assistantId = route.query.id
86102
if (!assistantId) {
87103
ElMessage.error('Miss embedded id, please check embedded url')

frontend/src/views/embedded/index.vue

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,16 @@
3131
</div>
3232
</template>
3333
<script setup lang="ts">
34-
import { onBeforeMount, nextTick, onBeforeUnmount, ref, onMounted, reactive, computed } from 'vue'
34+
import {
35+
onBeforeMount,
36+
nextTick,
37+
onBeforeUnmount,
38+
ref,
39+
onMounted,
40+
reactive,
41+
computed,
42+
watch,
43+
} from 'vue'
3544
import ChatComponent from '@/views/chat/index.vue'
3645
import { request } from '@/utils/request'
3746
import LOGO from '@/assets/svg/logo-custom_small.svg'
@@ -42,6 +51,9 @@ import { assistantApi } from '@/api/assistant'
4251
import { useAssistantStore } from '@/stores/assistant'
4352
import { setCurrentColor } from '@/utils/utils'
4453
import { useI18n } from 'vue-i18n'
54+
import { i18n } from '@/i18n'
55+
import { useUserStore } from '@/stores/user'
56+
const userStore = useUserStore()
4557
4658
const { t } = useI18n()
4759
const assistantStore = useAssistantStore()
@@ -92,6 +104,9 @@ const communicationCb = async (event: any) => {
92104
if (event.data?.busi == 'createConversation') {
93105
createChat()
94106
}
107+
if (event.data?.busi == 'setLang') {
108+
userStore.setLanguage(event.data.lang)
109+
}
95110
}
96111
}
97112
const setFormatOnline = (text?: any) => {
@@ -128,6 +143,24 @@ const customSet = reactive({
128143
theme: '#1CBA90',
129144
header_font_color: '#1F2329',
130145
}) as { [key: string]: any }
146+
147+
// 记录哪些字段被服务端 API 配置覆盖过,避免被 locale watcher 覆盖
148+
const configuredKeys = new Set<string>()
149+
150+
// 监听 locale 变化,动态更新未被子定义覆盖的翻译字段
151+
watch(
152+
() => i18n.global.locale.value,
153+
() => {
154+
if (!configuredKeys.has('welcome')) {
155+
customSet.welcome = t('embedded.i_am_sqlbot')
156+
}
157+
if (!configuredKeys.has('welcome_desc')) {
158+
customSet.welcome_desc = t('embedded.data_analysis_now')
159+
}
160+
},
161+
{ immediate: true }
162+
)
163+
131164
const logo = ref()
132165
const basePath = import.meta.env.VITE_API_BASE_URL
133166
const baseUrl = basePath + '/system/assistant/picture/'
@@ -140,7 +173,14 @@ const setPageHeaderFontColor = (val: any) => {
140173
const ele = document.querySelector('body') as HTMLElement
141174
ele.style.setProperty('--ed-text-color-primary', val)
142175
}
176+
const setParamLanguage = () => {
177+
const lang = route.query.lang
178+
if (lang) {
179+
userStore.setLanguage(lang as string)
180+
}
181+
}
143182
onBeforeMount(async () => {
183+
setParamLanguage()
144184
const assistantId = route.query.id
145185
if (!assistantId) {
146186
ElMessage.error('Miss assistant id, please check assistant url')
@@ -197,6 +237,7 @@ onBeforeMount(async () => {
197237
![null, undefined].includes(rawData[key])
198238
) {
199239
customSet[key] = rawData[key]
240+
configuredKeys.add(key)
200241
}
201242
}
202243

frontend/src/views/embedded/page.vue

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { assistantApi } from '@/api/assistant'
2222
import { useAssistantStore } from '@/stores/assistant'
2323
import { useAppearanceStoreWithOut } from '@/stores/appearance'
2424
import { useI18n } from 'vue-i18n'
25+
import { i18n } from '@/i18n'
2526
import { request } from '@/utils/request'
2627
import { setCurrentColor } from '@/utils/utils'
2728
import { useUserStore } from '@/stores/user'
@@ -41,6 +42,24 @@ const customSet = reactive({
4142
theme: '#1CBA90',
4243
header_font_color: '#1F2329',
4344
}) as { [key: string]: any }
45+
46+
// 记录哪些字段被服务端 API 配置覆盖过,避免被 locale watcher 覆盖
47+
const configuredKeys = new Set<string>()
48+
49+
// 监听 locale 变化,动态更新未被子定义覆盖的翻译字段
50+
watch(
51+
() => i18n.global.locale.value,
52+
() => {
53+
if (!configuredKeys.has('welcome')) {
54+
customSet.welcome = t('embedded.i_am_sqlbot')
55+
}
56+
if (!configuredKeys.has('welcome_desc')) {
57+
customSet.welcome_desc = t('embedded.data_analysis_now')
58+
}
59+
},
60+
{ immediate: true }
61+
)
62+
4463
const logo = ref()
4564
const basePath = import.meta.env.VITE_API_BASE_URL
4665
const baseUrl = basePath + '/system/assistant/picture/'
@@ -65,7 +84,12 @@ const communicationCb = async (event: any) => {
6584
if (type === 4) {
6685
assistantStore.setToken(certificate)
6786
assistantStore.setAssistant(true)
68-
await userStore.info()
87+
try {
88+
await userStore.info()
89+
} catch (e) {
90+
console.error('Failed to fetch user info in embedded page:', e)
91+
}
92+
setParamLanguage()
6993
loading.value = false
7094
return
7195
}
@@ -84,6 +108,9 @@ const communicationCb = async (event: any) => {
84108
if (event.data?.busi == 'createConversation') {
85109
createChat()
86110
}
111+
if (event.data?.busi == 'setLang') {
112+
userStore.setLanguage(event.data.lang)
113+
}
87114
}
88115
}
89116
@@ -132,8 +159,15 @@ const setPageCustomColor = (val: any) => {
132159
setCurrentColor(val, ele)
133160
}
134161
162+
const setParamLanguage = () => {
163+
const lang = route.query.lang
164+
if (lang) {
165+
userStore.setLanguage(lang as string)
166+
}
167+
}
135168
onBeforeMount(async () => {
136169
const assistantId = route.query.id
170+
setParamLanguage()
137171
if (!assistantId) {
138172
ElMessage.error('Miss embedded id, please check embedded url')
139173
return
@@ -193,6 +227,7 @@ onBeforeMount(async () => {
193227
![null, undefined].includes(rawData[key])
194228
) {
195229
customSet[key] = rawData[key]
230+
configuredKeys.add(key)
196231
}
197232
}
198233

0 commit comments

Comments
 (0)