HH-551: localize activity messages (#132)
* fix(HH-551): localize activity messages * fix(HH-551): handle null activity content --------- Co-authored-by: Rogee <rogee@ipao.vip>
This commit is contained in:
@@ -8,7 +8,49 @@ import { useMessageContext } from '../provider.js';
|
||||
|
||||
const { content, createdAt } = useMessageContext();
|
||||
|
||||
const { locale } = useI18n();
|
||||
const { locale, t } = useI18n();
|
||||
|
||||
const localizedContent = computed(() => {
|
||||
const normalizedContent = String(content.value ?? '');
|
||||
const resolvedBy = normalizedContent.match(
|
||||
/^Conversation was resolved by (.+)$/
|
||||
);
|
||||
if (resolvedBy) {
|
||||
const name = ['Anonymous Visitor', 'visitor'].includes(resolvedBy[1])
|
||||
? t('CONVERSATION.ACTIVITY.ANONYMOUS_VISITOR')
|
||||
: resolvedBy[1];
|
||||
return t('CONVERSATION.ACTIVITY.RESOLVED_BY', { name });
|
||||
}
|
||||
if (
|
||||
normalizedContent ===
|
||||
'Conversation was reopened due to agent bot webhook failure'
|
||||
) {
|
||||
return t('CONVERSATION.ACTIVITY.BOT_WEBHOOK_FAILURE');
|
||||
}
|
||||
if (
|
||||
normalizedContent ===
|
||||
'CSAT survey not sent due to outgoing message restrictions'
|
||||
) {
|
||||
return t('CONVERSATION.ACTIVITY.CSAT_RESTRICTED');
|
||||
}
|
||||
const linear = normalizedContent.match(
|
||||
/^Linear issue (\S+) was (created|linked|unlinked) by (.+)$/
|
||||
);
|
||||
if (linear) {
|
||||
const params = {
|
||||
issueId: linear[1],
|
||||
name: linear[3],
|
||||
};
|
||||
if (linear[2] === 'created') {
|
||||
return t('CONVERSATION.ACTIVITY.LINEAR_CREATED', params);
|
||||
}
|
||||
if (linear[2] === 'linked') {
|
||||
return t('CONVERSATION.ACTIVITY.LINEAR_LINKED', params);
|
||||
}
|
||||
return t('CONVERSATION.ACTIVITY.LINEAR_UNLINKED', params);
|
||||
}
|
||||
return normalizedContent;
|
||||
});
|
||||
|
||||
const readableTime = computed(() => {
|
||||
if (locale.value === 'zh_CN') {
|
||||
@@ -28,8 +70,8 @@ const readableTime = computed(() => {
|
||||
data-bubble-name="activity"
|
||||
>
|
||||
<span
|
||||
v-dompurify-html="content"
|
||||
:title="content"
|
||||
v-dompurify-html="localizedContent"
|
||||
:title="localizedContent"
|
||||
class="min-w-0 max-w-full [overflow-wrap:anywhere]"
|
||||
/>
|
||||
</BaseBubble>
|
||||
|
||||
+93
-38
@@ -1,53 +1,108 @@
|
||||
import { mount } from '@vue/test-utils';
|
||||
import { ref } from 'vue';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import { config, mount } from '@vue/test-utils';
|
||||
import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import Activity from '../Activity.vue';
|
||||
|
||||
vi.mock('vue-i18n', () => ({
|
||||
useI18n: () => ({ locale: ref('zh_CN') }),
|
||||
const mocks = vi.hoisted(() => ({
|
||||
content: '',
|
||||
}));
|
||||
|
||||
vi.mock('shared/helpers/timeHelper', () => ({
|
||||
messageTimestamp: () => '8月6日 下午 3:12',
|
||||
messageTimestamp: () => '8月6日 下午 3:12',
|
||||
}));
|
||||
|
||||
const longURL =
|
||||
'https://ada.baidu.com/site/wjzt6ah8/agent?imid=e159aa97cd2c403f2fba76899e47615f&trinfo=XzFWIaclcWbzcMY&word=%E4%B8%89%E9%98%B3%E7%97%85%E6%AF%92%E6%90%BA%E5%B8%A6';
|
||||
'https://ada.baidu.com/site/wjzt6ah8/agent?imid=e159aa97cd2c403f2fba76899e47615f&trinfo=XzFWIaclcWbzcMY&word=%E4%B8%89%E9%98%B3%E7%97%85%E6%AF%92%E6%90%BA%E5%B8%A6';
|
||||
|
||||
vi.mock('../../provider.js', () => ({
|
||||
useMessageContext: () => ({
|
||||
content: ref(`来源页面:<a href="${longURL}">${longURL}</a>`),
|
||||
createdAt: ref(1786018000),
|
||||
}),
|
||||
}));
|
||||
const i18n = config.global.plugins.find(plugin => plugin.global?.locale);
|
||||
const initialLocale = i18n.global.locale.value;
|
||||
|
||||
beforeAll(() => {
|
||||
i18n.global.locale.value = 'zh_CN';
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
i18n.global.locale.value = initialLocale;
|
||||
});
|
||||
|
||||
vi.mock('../../provider.js', async () => {
|
||||
const { ref } = await vi.importActual('vue');
|
||||
return {
|
||||
useMessageContext: () => ({
|
||||
content: ref(mocks.content),
|
||||
createdAt: ref(1786018000),
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
const mountActivity = () =>
|
||||
mount(Activity, {
|
||||
global: {
|
||||
directives: {
|
||||
dompurifyHtml: (element, binding) => {
|
||||
element.innerHTML = binding.value;
|
||||
},
|
||||
},
|
||||
stubs: {
|
||||
BaseBubble: {
|
||||
template: '<div><slot /></div>',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
describe('Activity bubble', () => {
|
||||
it('allows long links in system messages to wrap inside the bubble', () => {
|
||||
const wrapper = mount(Activity, {
|
||||
global: {
|
||||
directives: {
|
||||
dompurifyHtml: (element, binding) => {
|
||||
element.innerHTML = binding.value;
|
||||
},
|
||||
tooltip: () => {},
|
||||
},
|
||||
stubs: {
|
||||
BaseBubble: {
|
||||
template: '<div><slot /></div>',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
it('allows long links in system messages to wrap inside the bubble', () => {
|
||||
mocks.content = `来源页面:<a href="${longURL}">${longURL}</a>`;
|
||||
const wrapper = mountActivity();
|
||||
|
||||
expect(
|
||||
wrapper.get('[data-bubble-name="activity"]').classes()
|
||||
).toContain('max-w-full');
|
||||
expect(wrapper.get('[data-bubble-name="activity"]').classes()).toContain(
|
||||
'max-w-full'
|
||||
);
|
||||
|
||||
const content = wrapper.get('span');
|
||||
expect(content.classes()).toContain('min-w-0');
|
||||
expect(content.classes()).toContain('max-w-full');
|
||||
expect(content.classes()).toContain('[overflow-wrap:anywhere]');
|
||||
expect(content.get('a').text()).toBe(longURL);
|
||||
});
|
||||
const content = wrapper.get('span');
|
||||
expect(content.classes()).toContain('min-w-0');
|
||||
expect(content.classes()).toContain('max-w-full');
|
||||
expect(content.classes()).toContain('[overflow-wrap:anywhere]');
|
||||
expect(content.get('a').text()).toBe(longURL);
|
||||
});
|
||||
|
||||
it.each([
|
||||
['Conversation was resolved by Anonymous Visitor', '会话已由匿名访客解决'],
|
||||
['Conversation was resolved by Alice', '会话已由Alice解决'],
|
||||
[
|
||||
'Conversation was reopened due to agent bot webhook failure',
|
||||
'由于客服机器人 Webhook 失败,会话已重新打开',
|
||||
],
|
||||
[
|
||||
'CSAT survey not sent due to outgoing message restrictions',
|
||||
'由于发送消息受限,未发送 CSAT 调查',
|
||||
],
|
||||
[
|
||||
'Linear issue ENG-1 was created by Alice',
|
||||
'Linear 问题 ENG-1 已由 Alice 创建',
|
||||
],
|
||||
[
|
||||
'Linear issue ENG-1 was linked by Alice',
|
||||
'Linear 问题 ENG-1 已由 Alice 关联',
|
||||
],
|
||||
[
|
||||
'Linear issue ENG-1 was unlinked by Alice',
|
||||
'Linear 问题 ENG-1 已由 Alice 取消关联',
|
||||
],
|
||||
])('localizes known backend activity content: %s', (content, expected) => {
|
||||
mocks.content = content;
|
||||
const wrapper = mountActivity();
|
||||
|
||||
expect(wrapper.get('[data-bubble-name="activity"] span').text()).toBe(
|
||||
expected
|
||||
);
|
||||
});
|
||||
|
||||
it('renders an empty activity when content is null', () => {
|
||||
mocks.content = null;
|
||||
const wrapper = mountActivity();
|
||||
|
||||
expect(wrapper.get('[data-bubble-name="activity"] span').text()).toBe('');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,6 +2,15 @@
|
||||
"CONVERSATION": {
|
||||
"SELECT_A_CONVERSATION": "Please select a conversation from left pane",
|
||||
"CSAT_REPLY_MESSAGE": "Please rate the conversation",
|
||||
"ACTIVITY": {
|
||||
"ANONYMOUS_VISITOR": "Anonymous visitor",
|
||||
"RESOLVED_BY": "Conversation was resolved by {name}",
|
||||
"BOT_WEBHOOK_FAILURE": "Conversation was reopened because the agent bot webhook failed",
|
||||
"CSAT_RESTRICTED": "CSAT survey was not sent due to outgoing message restrictions",
|
||||
"LINEAR_CREATED": "Linear issue {issueId} was created by {name}",
|
||||
"LINEAR_LINKED": "Linear issue {issueId} was linked by {name}",
|
||||
"LINEAR_UNLINKED": "Linear issue {issueId} was unlinked by {name}"
|
||||
},
|
||||
"404": "Sorry, we cannot find the conversation. Please try again",
|
||||
"SWITCH_VIEW_LAYOUT": "Switch the layout",
|
||||
"DASHBOARD_APP_TAB_MESSAGES": "Messages",
|
||||
|
||||
@@ -2,6 +2,15 @@
|
||||
"CONVERSATION": {
|
||||
"SELECT_A_CONVERSATION": "请从左侧窗格选择一个对话",
|
||||
"CSAT_REPLY_MESSAGE": "请为会话评分",
|
||||
"ACTIVITY": {
|
||||
"ANONYMOUS_VISITOR": "匿名访客",
|
||||
"RESOLVED_BY": "会话已由{name}解决",
|
||||
"BOT_WEBHOOK_FAILURE": "由于客服机器人 Webhook 失败,会话已重新打开",
|
||||
"CSAT_RESTRICTED": "由于发送消息受限,未发送 CSAT 调查",
|
||||
"LINEAR_CREATED": "Linear 问题 {issueId} 已由 {name} 创建",
|
||||
"LINEAR_LINKED": "Linear 问题 {issueId} 已由 {name} 关联",
|
||||
"LINEAR_UNLINKED": "Linear 问题 {issueId} 已由 {name} 取消关联"
|
||||
},
|
||||
"404": "抱歉,我们找不到对话。请重试",
|
||||
"SWITCH_VIEW_LAYOUT": "切换布局",
|
||||
"DASHBOARD_APP_TAB_MESSAGES": "消息",
|
||||
|
||||
Reference in New Issue
Block a user