- 切换到 expo-router 文件路由,删除 App.tsx - 新增 5 个 Expo 原生插件:ppocr (OCR), accessibility (账单抓取), notification-listener, screenshot-monitor, sms-receiver - 实现核心领域逻辑:billPipeline (账单流水), dedup (去重), transferRecognizer (转账识别), ruleEngine + categories (双轨制分类), budgets, creditCards, recurring, sync, ocrProcessor - 增强 ledger.ts:支持 balance assertion, option, pad/note 指令, posting 级 metadata, cost/price 解析 - 新增完整 UI:tabs (首页/报表/设置), 交易详情, 预算, 日历热力图, 分类管理, 信用卡, 定期交易, 规则管理 - 实现 Zustand 状态管理:ledgerStore, importStore, settingsStore, metadataStore, automationStore + 持久化 - 新增 AI 功能:chatAssistant, monthlySummary, voiceInput - 实现多端同步:gitSync, webdavSync, icloudSync - 新增主题系统 (tokens/presets) 和 i18n (zh/en) - 添加 30+ 单元测试覆盖核心逻辑
63 lines
2.0 KiB
TypeScript
63 lines
2.0 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
||
import i18n, { enKeys, setLocale, t, zhKeys } from '../src/i18n';
|
||
import zh from '../src/i18n/zh';
|
||
import en from '../src/i18n/en';
|
||
|
||
describe('i18n key 完整性', () => {
|
||
it('zh 与 en 的 key 集合完全一致', () => {
|
||
const missingInEn = [...zhKeys].filter(k => !enKeys.has(k));
|
||
const missingInZh = [...enKeys].filter(k => !zhKeys.has(k));
|
||
expect(missingInEn).toEqual([]);
|
||
expect(missingInZh).toEqual([]);
|
||
});
|
||
|
||
it('关键 key 存在', () => {
|
||
expect(zhKeys.has('app.name')).toBe(true);
|
||
expect(zhKeys.has('tab.home')).toBe(true);
|
||
expect(zhKeys.has('home.title')).toBe(true);
|
||
expect(zhKeys.has('settings.themeMode')).toBe(true);
|
||
expect(zhKeys.has('errors.ledgerNotLoaded')).toBe(true);
|
||
});
|
||
});
|
||
|
||
describe('i18n 翻译', () => {
|
||
it('默认 locale 为 zh', () => {
|
||
setLocale('zh');
|
||
expect(i18n.locale).toBe('zh');
|
||
expect(t('app.name')).toBe('Bean Mobile');
|
||
expect(t('tab.home')).toBe('首页');
|
||
});
|
||
|
||
it('切换到 en 后返回英文', () => {
|
||
setLocale('en');
|
||
expect(t('tab.home')).toBe('Home');
|
||
expect(t('settings.themeLight')).toBe('Light');
|
||
setLocale('zh'); // 还原
|
||
});
|
||
|
||
it('占位符替换(zh)', () => {
|
||
setLocale('zh');
|
||
expect(t('home.accounts', { count: 4, total: 10 })).toBe('4 个已开户账户 · 10 条桌面交易');
|
||
});
|
||
|
||
it('占位符替换(en)', () => {
|
||
setLocale('en');
|
||
expect(t('home.accounts', { count: 4, total: 10 })).toBe('4 open accounts · 10 desktop transactions');
|
||
setLocale('zh');
|
||
});
|
||
|
||
it('未知 key 返回 missing 标记(fallback)', () => {
|
||
setLocale('zh');
|
||
expect(t('nonexistent.key')).toContain('missing');
|
||
expect(t('nonexistent.key')).toContain('nonexistent.key');
|
||
});
|
||
});
|
||
|
||
describe('i18n 结构一致性', () => {
|
||
it('zh 与 en 顶层模块一致', () => {
|
||
const zhModules = Object.keys(zh).sort();
|
||
const enModules = Object.keys(en).sort();
|
||
expect(zhModules).toEqual(enModules);
|
||
});
|
||
});
|