🐛 fix(i18n): 收口 dev 合并后的业务提示多语言遗漏

This commit is contained in:
tianqijiuyun-latiao
2026-06-23 18:26:20 +08:00
parent 09ae3d74c4
commit 71989af586
33 changed files with 1556 additions and 150 deletions

View File

@@ -1,4 +1,5 @@
import { describe, expect, it } from 'vitest';
import { afterEach, describe, expect, it } from 'vitest';
import { setCurrentLanguage, t } from '../i18n';
import {
formatExportElapsed,
formatExportProgressRows,
@@ -9,6 +10,10 @@ import {
} from './exportProgress';
describe('exportProgress', () => {
afterEach(() => {
setCurrentLanguage('zh-CN');
});
it('uses actual percent when total row count is known', () => {
expect(resolveExportProgressPercent('running', 25, 100, true)).toBe(25);
});
@@ -21,15 +26,26 @@ describe('exportProgress', () => {
});
it('falls back to indeterminate progress when total row hint is zero', () => {
setCurrentLanguage('en-US');
expect(resolveExportProgressPercent('running', 754000, 0, true)).toBe(0);
expect(shouldUseExactExportProgress('running', 0, true)).toBe(false);
expect(shouldUseIndeterminateExportProgress('running', 0, true)).toBe(true);
expect(formatExportProgressRows(754000, 0, true)).toBe('已写入 754,000 行');
expect(formatExportProgressRows(754000, 0, true)).toBe(
t('data_export.progress.rows_written', { current: '754,000' }),
);
});
it('formats row summary for known and unknown totals', () => {
expect(formatExportProgressRows(12345, 0, false)).toBe('已写入 12,345 行');
expect(formatExportProgressRows(12345, 880000, true)).toBe('已写入 12,345 / 880,000 行');
it('formats row summary with localized text and number separators', () => {
setCurrentLanguage('de-DE');
expect(formatExportProgressRows(12345, 0, false)).toBe(
t('data_export.progress.rows_written', { current: '12.345' }),
);
expect(formatExportProgressRows(12345, 880000, true)).toBe(
t('data_export.progress.rows_written_with_total', {
current: '12.345',
total: '880.000',
}),
);
});
it('resolves and formats elapsed export duration', () => {
@@ -38,4 +54,12 @@ describe('exportProgress', () => {
expect(formatExportElapsed(30_500)).toBe('00:30');
expect(formatExportElapsed(3_723_000)).toBe('01:02:03');
});
it('keeps export progress source free of hard-coded Chinese row summaries', async () => {
const { readFileSync } = await import('node:fs');
const source = readFileSync(new URL('./exportProgress.ts', import.meta.url), 'utf8');
expect(source).not.toContain('已写入 ');
expect(source).not.toContain("Intl.NumberFormat('zh-CN')");
});
});

View File

@@ -1,3 +1,5 @@
import { getCurrentLanguage, t } from '../i18n';
export type ExportProgressStatus = 'idle' | 'start' | 'running' | 'finalizing' | 'done' | 'error';
const hasUsableExportTotal = (total: number, totalRowsKnown: boolean): boolean => {
@@ -54,13 +56,16 @@ export const formatExportProgressRows = (
total: number,
totalRowsKnown: boolean,
): string => {
const formatter = new Intl.NumberFormat('zh-CN');
const formatter = new Intl.NumberFormat(getCurrentLanguage());
const safeCurrent = formatter.format(Math.max(0, Math.trunc(Number(current) || 0)));
if (!hasUsableExportTotal(total, totalRowsKnown)) {
return `已写入 ${safeCurrent}`;
return t('data_export.progress.rows_written', { current: safeCurrent });
}
const safeTotal = formatter.format(Math.max(0, Math.trunc(Number(total) || 0)));
return `已写入 ${safeCurrent} / ${safeTotal}`;
return t('data_export.progress.rows_written_with_total', {
current: safeCurrent,
total: safeTotal,
});
};
export const resolveExportElapsedMs = (

View File

@@ -1,7 +1,12 @@
import { describe, expect, it } from 'vitest'
import { afterEach, describe, expect, it } from 'vitest'
import { setCurrentLanguage, t } from '../i18n'
import { buildSqlAnalysisWorkbenchTab, resolveSqlAnalysisWorkbenchTabId } from './sqlAnalysisTab'
describe('sqlAnalysisTab', () => {
afterEach(() => {
setCurrentLanguage('zh-CN')
})
it('builds a stable workbench tab per connection and database', () => {
expect(resolveSqlAnalysisWorkbenchTabId('conn-1', 'analytics')).toBe('sql-analysis-conn-1-analytics')
expect(resolveSqlAnalysisWorkbenchTabId('conn-1')).toBe('sql-analysis-conn-1-default')
@@ -18,7 +23,7 @@ describe('sqlAnalysisTab', () => {
expect(tab).toMatchObject({
id: 'sql-analysis-conn-1-analytics',
title: 'SQL 分析 · analytics',
title: t('sql_analysis.workbench.tab_title_with_database', { database: 'analytics' }),
type: 'sql-analysis',
connectionId: 'conn-1',
dbName: 'analytics',
@@ -39,4 +44,28 @@ describe('sqlAnalysisTab', () => {
expect(tab.sqlAnalysisView).toBe('slow-query')
expect(tab.sqlAnalysisRequestKey).toBe('slow-1')
})
it('localizes default sql analysis tab titles', () => {
setCurrentLanguage('en-US')
expect(buildSqlAnalysisWorkbenchTab({
connectionId: 'conn-1',
dbName: 'analytics',
}).title).toBe(
t('sql_analysis.workbench.tab_title_with_database', { database: 'analytics' }),
)
expect(buildSqlAnalysisWorkbenchTab({
connectionId: 'conn-1',
}).title).toBe(
t('sql_analysis.workbench.tab_title'),
)
})
it('keeps sql analysis tab source free of hard-coded Chinese titles', async () => {
const { readFileSync } = await import('node:fs')
const source = readFileSync(new URL('./sqlAnalysisTab.ts', import.meta.url), 'utf8')
expect(source).not.toContain('SQL 分析 ·')
expect(source).not.toContain("'SQL 分析'")
})
})

View File

@@ -1,4 +1,5 @@
import type { TabData } from '../types'
import { t } from '../i18n'
export type SqlAnalysisView = 'diagnose' | 'slow-query'
@@ -26,12 +27,15 @@ export const buildSqlAnalysisWorkbenchTab = (
const connectionId = String(input.connectionId || '').trim()
const dbName = String(input.dbName || '').trim()
const view = input.view === 'slow-query' ? 'slow-query' : 'diagnose'
const title = String(input.title || (dbName ? `SQL 分析 · ${dbName}` : 'SQL 分析')).trim()
const defaultTitle = dbName
? t('sql_analysis.workbench.tab_title_with_database', { database: dbName })
: t('sql_analysis.workbench.tab_title')
const title = String(input.title || defaultTitle).trim()
const query = typeof input.query === 'string' ? input.query : ''
return {
id: resolveSqlAnalysisWorkbenchTabId(connectionId, dbName || undefined),
title: title || (dbName ? `SQL 分析 · ${dbName}` : 'SQL 分析'),
title: title || defaultTitle,
type: 'sql-analysis',
connectionId,
...(dbName ? { dbName } : {}),