refactor(i18n): support per-provider empty state and OAuth messages

This commit is contained in:
Supra4E8C
2025-12-28 17:41:25 +08:00
parent 762db81252
commit 981f7ac9b2
2 changed files with 27 additions and 11 deletions

View File

@@ -1216,6 +1216,8 @@ export function AiProvidersPage() {
onEdit: (index: number) => void, onEdit: (index: number) => void,
onDelete: (item: T) => void, onDelete: (item: T) => void,
addLabel: string, addLabel: string,
emptyTitle: string,
emptyDescription: string,
deleteLabel?: string, deleteLabel?: string,
options?: { options?: {
getRowDisabled?: (item: T, index: number) => boolean; getRowDisabled?: (item: T, index: number) => boolean;
@@ -1229,8 +1231,8 @@ export function AiProvidersPage() {
if (!items.length) { if (!items.length) {
return ( return (
<EmptyState <EmptyState
title={t('common.info')} title={emptyTitle}
description={t('ai_providers.gemini_empty_desc')} description={emptyDescription}
action={ action={
<Button onClick={() => onEdit(-1)} disabled={disableControls}> <Button onClick={() => onEdit(-1)} disabled={disableControls}>
{addLabel} {addLabel}
@@ -1381,6 +1383,8 @@ export function AiProvidersPage() {
(index) => openGeminiModal(index), (index) => openGeminiModal(index),
(item) => deleteGemini(item.apiKey), (item) => deleteGemini(item.apiKey),
t('ai_providers.gemini_add_button'), t('ai_providers.gemini_add_button'),
t('ai_providers.gemini_empty_title'),
t('ai_providers.gemini_empty_desc'),
undefined, undefined,
{ {
getRowDisabled: (item) => hasDisableAllModelsRule(item.excludedModels), getRowDisabled: (item) => hasDisableAllModelsRule(item.excludedModels),
@@ -1499,6 +1503,8 @@ export function AiProvidersPage() {
(index) => openProviderModal('codex', index), (index) => openProviderModal('codex', index),
(item) => deleteProviderEntry('codex', item.apiKey), (item) => deleteProviderEntry('codex', item.apiKey),
t('ai_providers.codex_add_button'), t('ai_providers.codex_add_button'),
t('ai_providers.codex_empty_title'),
t('ai_providers.codex_empty_desc'),
undefined, undefined,
{ {
getRowDisabled: (item) => hasDisableAllModelsRule(item.excludedModels), getRowDisabled: (item) => hasDisableAllModelsRule(item.excludedModels),
@@ -1633,6 +1639,8 @@ export function AiProvidersPage() {
(index) => openProviderModal('claude', index), (index) => openProviderModal('claude', index),
(item) => deleteProviderEntry('claude', item.apiKey), (item) => deleteProviderEntry('claude', item.apiKey),
t('ai_providers.claude_add_button'), t('ai_providers.claude_add_button'),
t('ai_providers.claude_empty_title'),
t('ai_providers.claude_empty_desc'),
undefined, undefined,
{ {
getRowDisabled: (item) => hasDisableAllModelsRule(item.excludedModels), getRowDisabled: (item) => hasDisableAllModelsRule(item.excludedModels),
@@ -1853,7 +1861,9 @@ export function AiProvidersPage() {
}, },
(index) => openOpenaiModal(index), (index) => openOpenaiModal(index),
(item) => deleteOpenai(item.name), (item) => deleteOpenai(item.name),
t('ai_providers.openai_add_button') t('ai_providers.openai_add_button'),
t('ai_providers.openai_empty_title'),
t('ai_providers.openai_empty_desc')
)} )}
</Card> </Card>

View File

@@ -64,6 +64,9 @@ const PROVIDERS: { id: OAuthProvider; titleKey: string; hintKey: string; urlLabe
]; ];
const CALLBACK_SUPPORTED: OAuthProvider[] = ['codex', 'anthropic', 'antigravity', 'gemini-cli', 'iflow']; const CALLBACK_SUPPORTED: OAuthProvider[] = ['codex', 'anthropic', 'antigravity', 'gemini-cli', 'iflow'];
const getProviderI18nPrefix = (provider: OAuthProvider) => provider.replace('-', '_');
const getAuthKey = (provider: OAuthProvider, suffix: string) =>
`auth_login.${getProviderI18nPrefix(provider)}_${suffix}`;
const getIcon = (icon: string | { light: string; dark: string }, theme: 'light' | 'dark') => { const getIcon = (icon: string | { light: string; dark: string }, theme: 'light' | 'dark') => {
return typeof icon === 'string' ? icon : icon[theme]; return typeof icon === 'string' ? icon : icon[theme];
@@ -105,12 +108,15 @@ export function OAuthPage() {
const res = await oauthApi.getAuthStatus(state); const res = await oauthApi.getAuthStatus(state);
if (res.status === 'ok') { if (res.status === 'ok') {
updateProviderState(provider, { status: 'success', polling: false }); updateProviderState(provider, { status: 'success', polling: false });
showNotification(t('auth_login.codex_oauth_status_success'), 'success'); showNotification(t(getAuthKey(provider, 'oauth_status_success')), 'success');
window.clearInterval(timer); window.clearInterval(timer);
delete timers.current[provider]; delete timers.current[provider];
} else if (res.status === 'error') { } else if (res.status === 'error') {
updateProviderState(provider, { status: 'error', error: res.error, polling: false }); updateProviderState(provider, { status: 'error', error: res.error, polling: false });
showNotification(`${t('auth_login.codex_oauth_status_error')} ${res.error || ''}`, 'error'); showNotification(
`${t(getAuthKey(provider, 'oauth_status_error'))} ${res.error || ''}`,
'error'
);
window.clearInterval(timer); window.clearInterval(timer);
delete timers.current[provider]; delete timers.current[provider];
} }
@@ -153,7 +159,7 @@ export function OAuthPage() {
} }
} catch (err: any) { } catch (err: any) {
updateProviderState(provider, { status: 'error', error: err?.message, polling: false }); updateProviderState(provider, { status: 'error', error: err?.message, polling: false });
showNotification(`${t('auth_login.codex_oauth_start_error')} ${err?.message || ''}`, 'error'); showNotification(`${t(getAuthKey(provider, 'oauth_start_error'))} ${err?.message || ''}`, 'error');
} }
}; };
@@ -347,14 +353,14 @@ export function OAuthPage() {
<div className={styles.authUrlValue}>{state.url}</div> <div className={styles.authUrlValue}>{state.url}</div>
<div className={styles.authUrlActions}> <div className={styles.authUrlActions}>
<Button variant="secondary" size="sm" onClick={() => copyLink(state.url!)}> <Button variant="secondary" size="sm" onClick={() => copyLink(state.url!)}>
{t('auth_login.codex_copy_link')} {t(getAuthKey(provider.id, 'copy_link'))}
</Button> </Button>
<Button <Button
variant="secondary" variant="secondary"
size="sm" size="sm"
onClick={() => window.open(state.url, '_blank', 'noopener,noreferrer')} onClick={() => window.open(state.url, '_blank', 'noopener,noreferrer')}
> >
{t('auth_login.codex_open_link')} {t(getAuthKey(provider.id, 'open_link'))}
</Button> </Button>
</div> </div>
</div> </div>
@@ -399,10 +405,10 @@ export function OAuthPage() {
{state.status && state.status !== 'idle' && ( {state.status && state.status !== 'idle' && (
<div className="status-badge" style={{ marginTop: 8 }}> <div className="status-badge" style={{ marginTop: 8 }}>
{state.status === 'success' {state.status === 'success'
? t('auth_login.codex_oauth_status_success') ? t(getAuthKey(provider.id, 'oauth_status_success'))
: state.status === 'error' : state.status === 'error'
? `${t('auth_login.codex_oauth_status_error')} ${state.error || ''}` ? `${t(getAuthKey(provider.id, 'oauth_status_error'))} ${state.error || ''}`
: t('auth_login.codex_oauth_status_waiting')} : t(getAuthKey(provider.id, 'oauth_status_waiting'))}
</div> </div>
)} )}
</Card> </Card>