mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-10-24 09:25:21 +08:00
feat(i18n): localize backend messages by workspace language
This commit is contained in:
@@ -750,6 +750,22 @@ async function handleBackendApi(route: Route, state: LangBotApiMockState) {
|
||||
return fulfillJson(route, { workspaces: state.workspaces });
|
||||
}
|
||||
|
||||
if (path === '/api/v1/workspaces/current/language') {
|
||||
return fulfillJson(route, {
|
||||
language: 'en_US',
|
||||
supported_languages: [
|
||||
'en_US',
|
||||
'zh_Hans',
|
||||
'zh_Hant',
|
||||
'ja_JP',
|
||||
'es_ES',
|
||||
'ru_RU',
|
||||
'th_TH',
|
||||
'vi_VN',
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
if (path === '/api/v1/workspaces/current') {
|
||||
const selectedWorkspaceUuid = request.headers()['x-workspace-id'];
|
||||
const entry = state.workspaces.find(
|
||||
|
||||
@@ -5765,12 +5765,110 @@
|
||||
"zh_Hans": "失败提示文本"
|
||||
},
|
||||
"description": {
|
||||
"en_US": "The text to display when a request fails. Only effective when Exception Handling Strategy is set to \"Show Failure Hint\"",
|
||||
"zh_Hans": "请求失败时显示的提示文本,仅在异常处理策略设置为\"仅文字提示\"时生效"
|
||||
"en_US": "Leave empty to use the Workspace language. Used for request and runner execution failures; custom text is not translated.",
|
||||
"zh_Hans": "留空时使用工作区语言的默认提示。用于请求和运行器执行失败;自定义文本不会自动翻译。"
|
||||
},
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "Request failed."
|
||||
"default": ""
|
||||
},
|
||||
{
|
||||
"name": "rate-limit-hint",
|
||||
"label": {
|
||||
"en_US": "Rate Limit Hint",
|
||||
"zh_Hans": "限速提示"
|
||||
},
|
||||
"description": {
|
||||
"en_US": "Leave empty to use the Workspace language.",
|
||||
"zh_Hans": "留空时使用工作区语言的默认提示。"
|
||||
},
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": ""
|
||||
},
|
||||
{
|
||||
"name": "content-filter-hint",
|
||||
"label": {
|
||||
"en_US": "Content Filter Hint",
|
||||
"zh_Hans": "内容过滤提示"
|
||||
},
|
||||
"description": {
|
||||
"en_US": "Leave empty to use the Workspace language.",
|
||||
"zh_Hans": "留空时使用工作区语言的默认提示。"
|
||||
},
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": ""
|
||||
},
|
||||
{
|
||||
"name": "content-filter-error-hint",
|
||||
"label": {
|
||||
"en_US": "Content Check Failure Hint",
|
||||
"zh_Hans": "内容检查失败提示"
|
||||
},
|
||||
"description": {
|
||||
"en_US": "Leave empty to use the Workspace language.",
|
||||
"zh_Hans": "留空时使用工作区语言的默认提示。"
|
||||
},
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": ""
|
||||
},
|
||||
{
|
||||
"name": "tool-call-hint",
|
||||
"label": {
|
||||
"en_US": "Tool Call Hint",
|
||||
"zh_Hans": "工具调用提示"
|
||||
},
|
||||
"description": {
|
||||
"en_US": "Leave empty to use the Workspace language. Use {tools} for tool names. Requires Track Function Calls.",
|
||||
"zh_Hans": "留空时使用工作区语言的默认提示。 可用 {tools} 插入工具名称,需开启跟踪函数调用。"
|
||||
},
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": ""
|
||||
},
|
||||
{
|
||||
"name": "runner-not-found-hint",
|
||||
"label": {
|
||||
"en_US": "Runner Missing Hint",
|
||||
"zh_Hans": "运行器不存在提示"
|
||||
},
|
||||
"description": {
|
||||
"en_US": "Leave empty to use the Workspace language. Use {runner} for the runner ID.",
|
||||
"zh_Hans": "留空时使用工作区语言的默认提示。 可用 {runner} 插入运行器 ID。"
|
||||
},
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": ""
|
||||
},
|
||||
{
|
||||
"name": "runner-not-authorized-hint",
|
||||
"label": {
|
||||
"en_US": "Runner Authorization Hint",
|
||||
"zh_Hans": "运行器未授权提示"
|
||||
},
|
||||
"description": {
|
||||
"en_US": "Leave empty to use the Workspace language.",
|
||||
"zh_Hans": "留空时使用工作区语言的默认提示。"
|
||||
},
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": ""
|
||||
},
|
||||
{
|
||||
"name": "runner-unavailable-hint",
|
||||
"label": {
|
||||
"en_US": "Runner Unavailable Hint",
|
||||
"zh_Hans": "运行器不可用提示"
|
||||
},
|
||||
"description": {
|
||||
"en_US": "Leave empty to use the Workspace language.",
|
||||
"zh_Hans": "留空时使用工作区语言的默认提示。"
|
||||
},
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": ""
|
||||
},
|
||||
{
|
||||
"name": "at-sender",
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
import { expect, test } from '@playwright/test';
|
||||
import {
|
||||
installLangBotApiMocks,
|
||||
makeWorkspaceEntry,
|
||||
} from './fixtures/langbot-api';
|
||||
|
||||
test('workspace language persists independently of personal UI language', async ({
|
||||
page,
|
||||
}, testInfo) => {
|
||||
const workspace = makeWorkspaceEntry(
|
||||
'workspace-language',
|
||||
'Language Workspace',
|
||||
'local',
|
||||
);
|
||||
workspace.permissions = ['workspace.view', 'workspace.update'];
|
||||
await installLangBotApiMocks(page, {
|
||||
authenticated: true,
|
||||
workspaces: [workspace],
|
||||
});
|
||||
let language = 'en_US';
|
||||
await page.route('**/api/v1/workspaces/current/language', async (route) => {
|
||||
if (route.request().method() === 'PUT') {
|
||||
expect(route.request().headers()['x-workspace-id']).toBe(
|
||||
'workspace-language',
|
||||
);
|
||||
language = route.request().postDataJSON().language;
|
||||
}
|
||||
await route.fulfill({
|
||||
json: {
|
||||
code: 0,
|
||||
msg: 'ok',
|
||||
data: {
|
||||
language,
|
||||
supported_languages: ['en_US', 'zh_Hans', 'ja_JP'],
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
await page.goto('/home?action=showWorkspaceSettings');
|
||||
const select = page.getByRole('combobox', {
|
||||
name: 'Workspace language',
|
||||
exact: true,
|
||||
});
|
||||
await expect(select).toHaveText('English');
|
||||
await select.click();
|
||||
await page.getByRole('option', { name: '日本語', exact: true }).click();
|
||||
await expect(select).toHaveText('日本語');
|
||||
await expect(page.getByText('Language saved', { exact: true })).toBeVisible();
|
||||
await page.reload();
|
||||
await page.goto('/home?action=showWorkspaceSettings');
|
||||
await expect(select).toHaveText('日本語');
|
||||
await expect(
|
||||
page.getByText('Workspace language', { exact: true }),
|
||||
).toBeVisible();
|
||||
await page.screenshot({
|
||||
path: testInfo.outputPath('workspace-language.png'),
|
||||
fullPage: true,
|
||||
});
|
||||
});
|
||||
|
||||
test('viewer can read but cannot edit workspace language', async ({ page }) => {
|
||||
const workspace = makeWorkspaceEntry(
|
||||
'workspace-language',
|
||||
'Language Workspace',
|
||||
'local',
|
||||
);
|
||||
workspace.membership.role = 'viewer';
|
||||
workspace.permissions = ['workspace.view'];
|
||||
await installLangBotApiMocks(page, {
|
||||
authenticated: true,
|
||||
workspaces: [workspace],
|
||||
});
|
||||
await page.goto('/home?action=showWorkspaceSettings');
|
||||
const select = page.getByRole('combobox', {
|
||||
name: 'Workspace language',
|
||||
exact: true,
|
||||
});
|
||||
await expect(select).toHaveText('English');
|
||||
await expect(select).toBeDisabled();
|
||||
});
|
||||
Reference in New Issue
Block a user