mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-10-23 17:05:28 +08:00
feat(i18n): localize backend messages by workspace language
This commit is contained in:
@@ -51,6 +51,17 @@ const ASSIGNABLE_ROLES: Exclude<WorkspaceRole, 'owner'>[] = [
|
||||
'viewer',
|
||||
];
|
||||
|
||||
const MESSAGE_LANGUAGES: Record<string, string> = {
|
||||
en_US: 'English',
|
||||
zh_Hans: '简体中文',
|
||||
zh_Hant: '繁體中文',
|
||||
ja_JP: '日本語',
|
||||
es_ES: 'Español',
|
||||
ru_RU: 'Русский',
|
||||
th_TH: 'ไทย',
|
||||
vi_VN: 'Tiếng Việt',
|
||||
};
|
||||
|
||||
export default function WorkspaceSettingsPanel({
|
||||
active,
|
||||
}: WorkspaceSettingsPanelProps) {
|
||||
@@ -61,6 +72,9 @@ export default function WorkspaceSettingsPanel({
|
||||
const [members, setMembers] = useState<WorkspaceMembership[]>([]);
|
||||
const [invitations, setInvitations] = useState<WorkspaceInvitation[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [messageLanguage, setMessageLanguage] = useState('en_US');
|
||||
const [supportedLanguages, setSupportedLanguages] = useState<string[]>([]);
|
||||
const [savingLanguage, setSavingLanguage] = useState(false);
|
||||
const [inviteEmail, setInviteEmail] = useState('');
|
||||
const [inviteRole, setInviteRole] =
|
||||
useState<Exclude<WorkspaceRole, 'owner'>>('viewer');
|
||||
@@ -86,8 +100,13 @@ export default function WorkspaceSettingsPanel({
|
||||
const loadWorkspace = useCallback(async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const current = await backendClient.getCurrentWorkspace();
|
||||
const [current, languageSettings] = await Promise.all([
|
||||
backendClient.getCurrentWorkspace(),
|
||||
backendClient.getWorkspaceLanguage(),
|
||||
]);
|
||||
setWorkspaceInfo(current);
|
||||
setMessageLanguage(languageSettings.language);
|
||||
setSupportedLanguages(languageSettings.supported_languages);
|
||||
|
||||
const [memberResponse, invitationResponse] = await Promise.all([
|
||||
current.permissions.includes('member.view')
|
||||
@@ -106,6 +125,19 @@ export default function WorkspaceSettingsPanel({
|
||||
}
|
||||
}, [t]);
|
||||
|
||||
async function saveLanguage(locale: string) {
|
||||
setSavingLanguage(true);
|
||||
try {
|
||||
const result = await backendClient.setWorkspaceLanguage(locale);
|
||||
setMessageLanguage(result.language);
|
||||
toast.success(t('workspaceLanguage.saved'));
|
||||
} catch {
|
||||
toast.error(t('workspaceLanguage.saveFailed'));
|
||||
} finally {
|
||||
setSavingLanguage(false);
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (active) void loadWorkspace();
|
||||
}, [active, loadWorkspace]);
|
||||
@@ -222,6 +254,41 @@ export default function WorkspaceSettingsPanel({
|
||||
</PanelToolbar>
|
||||
|
||||
<PanelBody className="space-y-6">
|
||||
<section className="space-y-3">
|
||||
<div>
|
||||
<h3 id="workspace-language-label" className="text-sm font-semibold">
|
||||
{t('workspaceLanguage.label')}
|
||||
</h3>
|
||||
<p
|
||||
id="workspace-language-description"
|
||||
className="text-xs text-muted-foreground"
|
||||
>
|
||||
{t('workspaceLanguage.description')}
|
||||
</p>
|
||||
</div>
|
||||
<Select
|
||||
value={messageLanguage}
|
||||
onValueChange={saveLanguage}
|
||||
disabled={
|
||||
loading || savingLanguage || !permissions.has('workspace.update')
|
||||
}
|
||||
>
|
||||
<SelectTrigger
|
||||
className="w-full sm:w-64"
|
||||
aria-labelledby="workspace-language-label"
|
||||
aria-describedby="workspace-language-description"
|
||||
>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{supportedLanguages.map((locale) => (
|
||||
<SelectItem key={locale} value={locale}>
|
||||
{MESSAGE_LANGUAGES[locale] || locale}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</section>
|
||||
{canInvite && (
|
||||
<section className="space-y-3">
|
||||
<div>
|
||||
|
||||
@@ -111,6 +111,17 @@ import type {
|
||||
* 负责与后端 API 的所有交互
|
||||
*/
|
||||
export class BackendClient extends BaseHttpClient {
|
||||
public getWorkspaceLanguage(): Promise<{
|
||||
language: string;
|
||||
supported_languages: string[];
|
||||
}> {
|
||||
return this.get('/api/v1/workspaces/current/language');
|
||||
}
|
||||
|
||||
public setWorkspaceLanguage(language: string): Promise<{ language: string }> {
|
||||
return this.put('/api/v1/workspaces/current/language', { language });
|
||||
}
|
||||
|
||||
constructor(baseURL: string) {
|
||||
super(baseURL, false);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
} from './workspaceContext';
|
||||
import { setCurrentWorkspaceSnapshot } from './currentWorkspaceStore';
|
||||
import { clearWorkspaceBootstrapSnapshot } from './workspaceBootstrapStore';
|
||||
import i18n from 'i18next';
|
||||
|
||||
type JSONValue = string | number | boolean | JSONObject | JSONArray | null;
|
||||
interface JSONObject {
|
||||
|
||||
Reference in New Issue
Block a user