核心重构 — 去除 channel 字段,引入 sourceAccount 模型: - 从 ImportedEvent、Rule、EnhancedRule、OcrRule 等接口中彻底移除 channel 字段 - rules.ts 中 resolveChannelAccount → resolveSourceAccount,规则匹配与账户解析不再依赖渠道概念 - dedup.ts 重写去重逻辑:从基于渠道匹配改为基于交易对手(counterparty)匹配,支持相同金额/交易对手/时间窗口的多级置信度判断 - transferRecognizer.ts 增加资产负债表账户校验,确保转账双方均为 Assets/Liabilities 类账户 - 全局替换影响:types、rules、ocr、adapters、adapters-migrations、所有服务层和测试 新增基础设施: - domain/constants.ts — 统一常量定义(支付包名、截图关键词、去重参数、方向检测函数 detectDirection()),消除 OCR/SMS/截图等模块的重复定义 - domain/channelConfig.ts — 渠道配置系统(支付宝/微信/银行),支持按包名和名称查找 - domain/pipelineSingleton.ts — 共享 BillPipeline 单例,解决 importStore/automationStore 的互斥锁共享问题 - domain/transactionBuilder.ts — 统一交易构建入口 buildAndSaveTransaction(),同时服务手动录入和无障碍监听 OCR 增强: - 新增账单详情页解析(parseDetailPageBill),支持支付宝/微信详情页结构化提取 - checkIsDetailPage() 识别详情页特征词,防止误提取(如"消费1次"被误读为金额) - 金额正则支持千分位逗号分隔,商户名正则改用 lookahead 边界匹配 - 时间解析支持中文格式(年月日)和跨年推断 - OcrProcessor 新增详情页路由,跳过 Layer 1 规则匹配 UI 全面升级: - 主题重设计:accent 色从绿色改为靛蓝(#4F46E5),深色模式适配 OLED 纯黑,引入 Quicksand/Caveat 字体 - 新增 commonStyles.ts 统一 chip/input/modal 等通用样式 - 首页 Bento 网格布局:净资产英雄卡片 + 定期账单/月度统计并排展示 - 报表新增周报标签页,月报整合日历视图(支持点击查看当日交易明细) - TrendLine 图表从 View 条形图重写为 SVG 贝塞尔曲线 - CategoryPicker 从水平滚动改为 4 列网格 + emoji 图标 - Button/Card 增加 press 缩放动画 管道与自动化改进: - automationPipeline.ts 新增 handleIncomingBillEvent() 实时账单处理(悬浮账单卡片 + 前台 Alert 确认) - 新增无障碍文本直解析 parseAndProcessAccessibilityTexts(),微信/支付宝详情页绕过 OCR - rules.ts 新增智能还款检测(花呗/信用卡还款自动路由)和退款视为收入处理 - metadataStore 默认规则精简为 6 条通用规则,移除约 20 条个人化硬编码规则 存储与同步: - storePersistence.ts 原子写入 + 崩溃恢复 + 重试机制 - 备份升级到 v2 格式,包含 settings 和 metadata - 同步路径统一从 mobile.bean 改为 main.bean - _layout.tsx 启动时自动迁移旧 mobile.bean 到 main.bean 其他: - 删除独立日历页面,功能合并到报表月报标签 - i18n 清理:移除渠道相关翻译,新增 50+ 翻译键 - docs/android-build-guide.md 重写为 APK 体积优化指南 - 新增 design-system/beancount-mobile/MASTER.md 设计系统文档 - 测试全面更新覆盖以上所有变更
643 lines
24 KiB
TypeScript
643 lines
24 KiB
TypeScript
/**
|
|
* English translations. Keys must stay in sync with zh.ts.
|
|
*/
|
|
export default {
|
|
app: {
|
|
name: 'Bean Mobile',
|
|
tagline: 'Simple, offline mobile bookkeeping. Your privacy is protected, and all data is securely stored locally.',
|
|
statusParsed: 'Ledger loaded. Your transactions are safely stored locally.',
|
|
},
|
|
common: {
|
|
cancel: 'Cancel',
|
|
delete: 'Delete',
|
|
confirm: 'OK',
|
|
save: 'Save',
|
|
edit: 'Edit',
|
|
add: 'Add',
|
|
back: 'Back',
|
|
notFound: 'Not Found',
|
|
untitled: 'Untitled',
|
|
clickEditLongDelete: 'Tap to edit · Long-press to delete',
|
|
noData: 'No data',
|
|
discard: 'Discard',
|
|
},
|
|
tab: {
|
|
home: 'Home',
|
|
transactions: 'Txns',
|
|
report: 'Report',
|
|
import: 'Import',
|
|
rules: 'Rules',
|
|
settings: 'Settings',
|
|
},
|
|
home: {
|
|
title: 'Ledger Status',
|
|
accounts: '%{count} open accounts · %{total} transactions',
|
|
readonlyHint: 'Ledger is ready. New transactions will be saved automatically.',
|
|
mobileBean: 'Local Records',
|
|
mobileBeanEmpty: 'No transactions yet',
|
|
newTransaction: 'New Transaction',
|
|
netWorthTitle: 'Net Worth',
|
|
assets: 'Assets',
|
|
liabilities: 'Liabilities',
|
|
netWorth: 'Net Worth',
|
|
income: 'Income',
|
|
expense: 'Expense',
|
|
balance: 'Balance',
|
|
monthlyTrend: 'Monthly Trend',
|
|
recentTransactions: 'Recent',
|
|
recentEmpty: 'No transactions yet. Tap + to add one.',
|
|
ocrScan: 'Scan Receipt',
|
|
dueRecurring: 'Due Recurring Bills',
|
|
dueAmount: 'Amount',
|
|
confirmRecurring: 'Confirm',
|
|
recurringSuccess: 'Success',
|
|
recurringSuccessDesc: 'Recurring bill "%{name}" has been recorded and next due date updated.',
|
|
recurringFail: 'Recurring recording failed',
|
|
accountTree: 'Account Balance Tree',
|
|
balanced: '✓ Balanced',
|
|
unbalanced: '✗ Unbalanced',
|
|
},
|
|
transaction: {
|
|
newTitle: 'Manual Entry',
|
|
amountPlaceholder: 'Amount, e.g. 24.50',
|
|
accountPlaceholder: 'Expense account',
|
|
submit: 'Save Transaction',
|
|
appended: 'Transaction saved successfully.',
|
|
detailTitle: 'Transaction Detail',
|
|
notFoundTitle: 'Transaction Not Found',
|
|
notFoundDesc: 'Transaction with ID %{id} could not be found. It may have been deleted or not yet loaded.',
|
|
summary: 'Summary',
|
|
noSummary: '(no summary)',
|
|
postings: 'Postings',
|
|
tags: 'Tags',
|
|
links: 'Links',
|
|
metadata: 'Additional Info',
|
|
rawBean: 'Underlying Text',
|
|
source: 'Source',
|
|
directionExpense: 'Expense',
|
|
directionIncome: 'Income',
|
|
directionTransfer: 'Transfer',
|
|
formDate: 'Date',
|
|
formNarration: 'Narration',
|
|
formPayee: 'Payee',
|
|
formPayeePlaceholder: 'Counterparty/merchant',
|
|
formSourceAccount: 'Source Account',
|
|
formAvailable: 'Available',
|
|
formCategoryExpense: 'Expense Category',
|
|
formCategoryIncome: 'Income Category',
|
|
formTags: 'Tags (optional)',
|
|
formTagsWillWrite: 'Will write',
|
|
amountRequired: 'Please enter an amount',
|
|
sameAccountError: 'Source and target accounts cannot be the same',
|
|
narrationDefaultExpense: 'Expense',
|
|
narrationDefaultIncome: 'Income',
|
|
narrationDefaultTransfer: 'Transfer',
|
|
advancedMode: 'Advanced Double-Entry Mode',
|
|
atLeast2Postings: 'Double-entry requires at least 2 valid posting lines',
|
|
unbalanced: 'Unbalanced! Sum of postings per currency must equal 0',
|
|
defaultAdvancedNarration: 'Double-entry transaction',
|
|
postingsList: 'Details',
|
|
addPosting: 'Add Detail',
|
|
deleteLastPosting: 'Delete Last',
|
|
targetAccount: 'Target Account',
|
|
unsavedTitle: 'Discard changes?',
|
|
unsavedMessage: 'You have unsaved changes. Are you sure you want to leave?',
|
|
hideDetails: 'Hide details',
|
|
addDetails: 'Add transaction details (date, payee, tags, etc.)',
|
|
extraDetails: 'Extra details',
|
|
invalidAmount: 'Please enter a valid amount value',
|
|
linkTransaction: 'Link Another Transaction',
|
|
linkSuccess: 'Linked',
|
|
linkSuccessDesc: 'Two transactions linked with tag ^%{tag}.',
|
|
linkFail: 'Link failed',
|
|
unlinkSuccess: 'Unlinked',
|
|
unlinkSuccessDesc: 'Removed link tag ^%{tag} from this transaction.',
|
|
unlinkFail: 'Unlink failed',
|
|
relatedTransactions: 'Related Transactions',
|
|
readOnlyLinks: 'Read-only transaction, links cannot be edited.',
|
|
linkMobileTx: 'Link Mobile Transaction',
|
|
searchTxPlaceholder: 'Search narration or account...',
|
|
noLinkableTx: 'No linkable mobile transactions',
|
|
edit: 'Edit',
|
|
delete: 'Delete',
|
|
deleteConfirm: 'Delete this transaction? This cannot be undone.',
|
|
deleteTitle: 'Delete Transaction',
|
|
editSuccess: 'Updated',
|
|
deleteSuccess: 'Deleted',
|
|
},
|
|
transactions: {
|
|
searchPlaceholder: 'Search narration / payee',
|
|
filterAll: 'All',
|
|
filterExpense: 'Expense',
|
|
filterIncome: 'Income',
|
|
filterTransfer: 'Transfer',
|
|
countTotal: '%{count} total',
|
|
countFiltered: '%{shown} / %{total}',
|
|
noMatchTitle: 'No Matching Transactions',
|
|
noMatchEmpty: 'No transactions yet.',
|
|
noMatchFiltered: 'No transactions match your search. Try adjusting the filters.',
|
|
advancedFilter: 'Advanced',
|
|
accountFilterPlaceholder: 'Account contains...',
|
|
dateFrom: 'From date',
|
|
dateTo: 'To date',
|
|
},
|
|
report: {
|
|
title: 'Report',
|
|
empty: 'No transaction data yet. Add transactions to see reports.',
|
|
monthly: 'Monthly Income/Expense',
|
|
categoryPie: 'Expense by Category',
|
|
heatmap: 'This Month Heatmap',
|
|
netWorthTrend: 'Net Worth Trend',
|
|
annual: '%{year} Annual Report',
|
|
shareTitle: 'Share Report',
|
|
exportSuccess: 'Exported',
|
|
exportFail: 'Export failed',
|
|
aiSummary: 'Report Summary',
|
|
trendTitle: 'Income/Expense Trend',
|
|
netWorthTitle: 'Net Worth Trend',
|
|
netWorthCurrent: 'Current Net Worth: %{amount}',
|
|
monthlyTitle: 'Monthly Report',
|
|
categoryTitle: 'Expense by Category',
|
|
annualTitle: '%{year} Annual Report',
|
|
annualIncome: 'Income',
|
|
annualExpense: 'Expense',
|
|
annualNet: 'Net',
|
|
annualStats: '%{count} transactions · Daily avg %{avg}',
|
|
topCategory: 'Top Categories',
|
|
tabWeekly: 'Weekly',
|
|
tabMonthly: 'Monthly',
|
|
tabAnnual: 'Annual',
|
|
weeklyTitle: 'Weekly Report',
|
|
weeklyIncome: 'Weekly Income',
|
|
weeklyExpense: 'Weekly Expense',
|
|
weeklyNet: 'Weekly Net',
|
|
weeklyStats: '%{count} transactions',
|
|
monthlyIncome: 'Monthly Income',
|
|
monthlyExpense: 'Monthly Expense',
|
|
monthlyNet: 'Monthly Net',
|
|
monthlyStats: '%{count} transactions',
|
|
},
|
|
calendar: {
|
|
title: 'Calendar',
|
|
noDayTx: 'No transactions on this day',
|
|
},
|
|
category: {
|
|
title: 'Categories',
|
|
expense: 'Expense Categories',
|
|
income: 'Income Categories',
|
|
addExpense: 'Add Expense Category',
|
|
addIncome: 'Add Income Category',
|
|
editTitle: 'Edit Category',
|
|
addTitle: 'Add %{type} Category',
|
|
fieldName: 'Category Name',
|
|
fieldLinkedAccount: 'Linked Beancount Account',
|
|
fieldKeywords: 'Keywords (comma-separated)',
|
|
keywordsLabel: 'Keywords',
|
|
deleteTitle: 'Delete Category',
|
|
deleteConfirm: 'Delete "%{name}"?',
|
|
namePlaceholder: 'e.g. Food',
|
|
accountPlaceholder: 'e.g. Expenses:Food',
|
|
keywordsPlaceholder: 'Match keywords, comma-separated, e.g. food, dinner, coffee',
|
|
},
|
|
tag: {
|
|
title: 'Tags',
|
|
count: 'Tags (%{count})',
|
|
add: 'Add Tag',
|
|
editTitle: 'Edit Tag',
|
|
invalidName: 'Invalid tag name',
|
|
invalidDesc: 'Only letters, digits, underscore, and hyphen are allowed (1-32 chars)',
|
|
selectColor: 'Select Color',
|
|
fieldName: 'Tag name (letters/digits/_/-)',
|
|
empty: 'No tags yet',
|
|
deleteTitle: 'Delete Tag',
|
|
deleteConfirm: 'Delete "#%{name}"?',
|
|
},
|
|
budget: {
|
|
title: 'Budgets',
|
|
add: 'Add Budget',
|
|
editTitle: 'Edit Budget',
|
|
fieldName: 'Budget Name',
|
|
fieldAmount: 'Amount',
|
|
fieldPeriod: 'Period (monthly/weekly/yearly)',
|
|
fieldCategoryAccount: 'Linked Account (blank = total)',
|
|
fieldStartDate: 'Start Date',
|
|
periodMonthly: 'mo',
|
|
periodWeekly: 'wk',
|
|
periodYearly: 'yr',
|
|
yuanPer: 'CNY / %{period}',
|
|
used: 'Used %{spent} (%{pct}%)',
|
|
unnamed: 'Untitled Budget',
|
|
empty: 'No budgets yet. Tap above to add one.',
|
|
deleteTitle: 'Delete Budget',
|
|
deleteConfirm: 'Delete "%{name}"?',
|
|
invalidAmountTitle: 'Invalid Amount',
|
|
invalidAmountDesc: 'Please enter a budget amount greater than 0',
|
|
fieldNamePlaceholder: 'e.g. Food Budget',
|
|
fieldAmountPlaceholder: 'e.g. 2000',
|
|
fieldCategoryPlaceholder: 'e.g. Expenses:Food',
|
|
},
|
|
creditCard: {
|
|
title: 'Credit Cards',
|
|
add: 'Add Credit Card',
|
|
editTitle: 'Edit Credit Card',
|
|
fieldName: 'Card Name',
|
|
fieldBank: 'Bank',
|
|
fieldLastFour: 'Last 4 Digits',
|
|
fieldBillingDay: 'Billing Day (1-28)',
|
|
fieldPaymentDay: 'Payment Day (1-28)',
|
|
fieldLimit: 'Credit Limit',
|
|
fieldCurrency: 'Currency',
|
|
fieldLinkedAccount: 'Linked Beancount Account',
|
|
labelBank: 'Bank',
|
|
labelBillingDay: 'Billing Day',
|
|
labelPaymentDay: 'Payment Day',
|
|
labelLimit: 'Limit',
|
|
daySuffix: '',
|
|
unnamed: 'Untitled Card',
|
|
empty: 'No credit cards yet. Tap above to add one.',
|
|
deleteTitle: 'Delete Credit Card',
|
|
deleteConfirm: 'Delete "%{name}"?',
|
|
},
|
|
rules: {
|
|
title: 'Auto-categorization Rules',
|
|
add: 'Add Rule',
|
|
editTitle: 'Edit Rule',
|
|
empty: 'No rules yet',
|
|
fieldPriority: 'Priority (higher = matched first)',
|
|
fieldCounterparty: 'Counterparty contains',
|
|
fieldMemo: 'Memo contains (optional)',
|
|
fieldSourceAccount: 'Source Account',
|
|
fieldCategoryAccount: 'Category Account',
|
|
fieldNarration: 'Narration (optional)',
|
|
fieldTags: 'Tags (comma-separated)',
|
|
condCounterparty: 'payee contains "%{val}"',
|
|
condMemo: 'memo contains "%{val}"',
|
|
condNone: 'no conditions',
|
|
hitsSuffix: '%{count} hits',
|
|
deleteTitle: 'Delete Rule',
|
|
deleteConfirm: 'Delete "%{name}"?',
|
|
},
|
|
importFlow: {
|
|
title: 'Bill Import',
|
|
hint: 'Flow: Import CSV → Process (transfer→dedup→categorize) → Confirm each.',
|
|
importButton: 'Import demo Alipay CSV',
|
|
processButton: 'Re-analyze (%{count} bills)',
|
|
result: 'Last run: %{drafts} drafts / %{duplicates} dups / %{transfers} transfers',
|
|
processed: 'Done: %{drafts} drafts, %{duplicates} duplicates, %{transfers} transfers.',
|
|
confirm: 'Confirm',
|
|
confirmed: 'Saved locally.',
|
|
transfer: 'Transfer',
|
|
trade: 'Trade',
|
|
fallbackWarn: '⚠ %{reason}',
|
|
selectFile: 'Select local CSV or Excel bill',
|
|
confirmAll: 'Commit all (%{count})',
|
|
duplicatesTitle: 'Suspected duplicates (%{count})',
|
|
collapseList: 'Collapse',
|
|
expandDuplicates: 'Expand suspected duplicates',
|
|
duplicateReason: 'Similar time and matching amount (%{date} · payee: %{payee})',
|
|
noDesc: 'No description',
|
|
duplicateAmount: 'Amount: %{amount} %{currency}',
|
|
unknownPayee: 'Unknown',
|
|
ignored: 'Ignored this duplicate.',
|
|
forcedImport: 'Bill forcibly imported!',
|
|
decodeFail: 'Bill decode failed: %{error}',
|
|
importSuccess: 'Imported %{format} bill: %{name}.',
|
|
formatExcel: 'Excel',
|
|
formatCsv: 'CSV',
|
|
pipelineRunning: 'Automatically categorizing and finding duplicates...',
|
|
pipelineDone: 'Parsed! Converted %{drafts} transactions, detected %{duplicates} suspected duplicates.',
|
|
pipelineFail: 'Analysis failed: %{error}',
|
|
pickFail: 'File selection failed: %{error}',
|
|
importFailTitle: 'Import failed',
|
|
commitFail: 'Commit failed: %{error}',
|
|
commitFailTitle: 'Recording failed',
|
|
batchPreparing: 'Validating and preparing batch commit...',
|
|
autoOpenFail: 'Auto-open failed: %{error}',
|
|
autoOpenFailTitle: 'Open failed',
|
|
batchAllSuccess: 'All committed! Successfully recorded %{count} transactions.',
|
|
batchPartial: 'Partial failure: %{success} succeeded, %{fail} failed.',
|
|
batchResultTitle: 'Batch result',
|
|
batchResultBody: 'Succeeded: %{success}\nFailed: %{fail}\n\nReasons:\n%{reasons}',
|
|
batchNoValid: 'No valid transactions. Reasons:\n%{reasons}',
|
|
batchFail: 'Batch commit failed: %{error}',
|
|
batchErrorTitle: 'Recording error',
|
|
suspectedDuplicate: 'Suspected duplicate',
|
|
skip: 'Skip',
|
|
forceImport: 'Import anyway',
|
|
},
|
|
onboarding: {
|
|
welcome: 'Welcome',
|
|
language: 'Choose Language',
|
|
theme: 'Choose Theme',
|
|
ledger: 'Ledger Import',
|
|
ledgerDesc: 'The app safely stores all new transactions in a separate local file without altering your existing ledger.',
|
|
permissions: 'Feature Setup',
|
|
permissionsDesc: 'OCR, notification listening, and SMS monitoring require manual permission grants in system settings (Accessibility/Notification access/SMS).',
|
|
security: 'Security',
|
|
securityDesc: 'Enable app lock (biometric) and blur private content when switching apps.',
|
|
prev: 'Previous',
|
|
next: 'Next',
|
|
start: 'Get Started',
|
|
enableLock: 'Enable App Lock',
|
|
lockEnabled: '✓ App Lock Enabled',
|
|
},
|
|
diagnostics: {
|
|
title: 'Read-only Compatibility & Diagnostics',
|
|
syntaxErrors: 'Syntax issues: %{count}',
|
|
unsupported: 'Preserved advanced directives: %{count}',
|
|
balanceAssertions: 'Balance assertions: %{count}',
|
|
options: 'Options: %{count}',
|
|
hint: 'Unrecognized directives are preserved in read-only mode.',
|
|
},
|
|
settings: {
|
|
title: 'Settings',
|
|
appearance: 'Appearance',
|
|
themeMode: 'Theme',
|
|
themeLight: 'Light',
|
|
themeDark: 'Dark',
|
|
themeSystem: 'System',
|
|
language: 'Language',
|
|
langZh: '中文',
|
|
langEn: 'English',
|
|
dataManagement: 'Data Management',
|
|
categories: 'Categories',
|
|
tags: 'Tags',
|
|
budgets: 'Budgets',
|
|
creditCards: 'Credit Cards',
|
|
calendar: 'Calendar View',
|
|
security: 'Security Settings',
|
|
appLock: 'Enable App Lock',
|
|
setPinDesc: 'Set a 4-digit PIN to unlock the app',
|
|
reminderTitle: 'Reminders',
|
|
reminderToggle: 'Daily bookkeeping reminder',
|
|
reminderTime: 'Reminder time',
|
|
syncTitle: 'Sync & Backup',
|
|
webdavConfig: 'WebDAV Sync Settings',
|
|
gitConfig: 'Git Sync Settings',
|
|
iCloudSync: 'Sync with iCloud Now',
|
|
syncNow: 'Sync Now',
|
|
backupNow: 'Database Backup & Maintenance',
|
|
backupBtn: 'Backup Data',
|
|
restoreBtn: 'Restore Data',
|
|
optimizeDb: 'Optimize & Maintain Database',
|
|
dedupLabel: 'Multi-channel De-duplication',
|
|
transferLabel: 'Smart Transfer Recognition',
|
|
syncSuccess: 'Sync successful',
|
|
syncFail: 'Sync failed: %{error}',
|
|
backupSuccess: 'Backup successful: %{path}',
|
|
restoreSuccess: 'Restore successful, ledger refreshed',
|
|
maintenanceSuccess: 'Database maintenance completed',
|
|
algorithmConfig: 'Auto-Classification Rules Settings',
|
|
selectLang: 'Select language',
|
|
preferencesTitle: 'Preferences & Security',
|
|
recurringTitle: 'Recurring Transactions',
|
|
systemFeaturesTitle: 'Features & System',
|
|
aiSettingsTitle: 'AI & Smart Entry',
|
|
syncSettingsTitle: 'Cloud Sync & Backup',
|
|
aboutTitle: 'About',
|
|
aboutVersion: 'v0.1.0 (Hermes)',
|
|
enableAiHint: 'Enable AI bill classification hints',
|
|
aiAssistantTitle: 'AI Bill Recognition Assistant',
|
|
aiProvider: 'AI Provider',
|
|
aiConfigBtn: 'Configure API connection',
|
|
aiCurrentModel: 'Current model: %{model} · URL: %{url}',
|
|
aiConfigTitle: 'Configure AI API',
|
|
aiFieldApiKey: 'API Key',
|
|
aiFieldBaseUrl: 'API Base URL',
|
|
aiFieldModel: 'Model name',
|
|
},
|
|
account: {
|
|
title: 'Accounts',
|
|
tabAssets: 'Assets',
|
|
tabLiabilities: 'Liabilities',
|
|
tabExpenses: 'Expense Categories',
|
|
tabIncome: 'Income Categories',
|
|
addNew: 'New account',
|
|
fullName: 'Full path',
|
|
currentBalance: 'Current balance',
|
|
adjustBalance: 'Calibrate balance',
|
|
closeAccount: 'Close account',
|
|
empty: 'No accounts in this category. They are auto-created when you record or import transactions.',
|
|
addModalTitle: 'New Account',
|
|
adjustModalTitle: 'Calibrate Account Balance',
|
|
fieldType: 'Account type',
|
|
fieldName: 'Account name (English/Chinese)',
|
|
fieldNamePlaceholder: 'Digits, hyphens, Chinese supported',
|
|
fieldCurrency: 'Currency',
|
|
fieldBalance: 'Target balance',
|
|
fieldBalancePlaceholder: 'e.g. 10000.00',
|
|
fieldDate: 'Effective date',
|
|
confirmClose: 'Confirm Close',
|
|
confirmCloseDesc: 'Close this account?\n\n%{name}\n\nThis account will be closed in your local ledger.',
|
|
btnConfirmClose: 'Close',
|
|
closeSuccess: 'Account closed',
|
|
closeSuccessDesc: 'Account has been closed in the local ledger.',
|
|
closeFail: 'Close failed',
|
|
addFail: 'Add failed',
|
|
addFailEmpty: 'Account type and name are required.',
|
|
invalidRootAccount: 'Account must start with one of the five root account types (Assets, Liabilities, Income, Expenses, Equity)',
|
|
openSuccess: 'Account opened',
|
|
openSuccessDesc: 'Account opened successfully:\n\n%{name}',
|
|
openFail: 'Open failed',
|
|
adjustFail: 'Calibrate failed',
|
|
adjustFailEmpty: 'Balance value cannot be empty.',
|
|
adjustSuccess: 'Balance adjusted',
|
|
adjustSuccessDesc: 'Account %{name} balance calibrated to %{balance} %{currency}.',
|
|
billingPeriod: 'Billing Period',
|
|
dueDate: 'Due Date',
|
|
statementAmount: 'Statement Amount',
|
|
availableCredit: 'Available Credit',
|
|
},
|
|
recurring: {
|
|
title: 'Recurring Transactions',
|
|
add: 'Add recurring bill',
|
|
frequency: 'Frequency',
|
|
freqMonthly: 'Monthly',
|
|
freqWeekly: 'Weekly',
|
|
freqYearly: 'Yearly',
|
|
freqDaily: 'Daily',
|
|
nextDue: 'Next due',
|
|
flow: 'Flow',
|
|
perAmount: 'Per period',
|
|
edit: 'Edit',
|
|
delete: 'Delete',
|
|
empty: 'No recurring bills yet. Tap above to add one.',
|
|
editTitle: 'Edit Recurring Bill',
|
|
addTitle: 'New Recurring Bill',
|
|
fieldName: 'Bill name',
|
|
fieldNamePlaceholder: 'e.g. iCloud subscription',
|
|
fieldFromAccount: 'Source account (Beancount path)',
|
|
fieldToAccount: 'Category account (Beancount path)',
|
|
fieldAmount: 'Amount',
|
|
fieldFrequency: 'Frequency (daily/weekly/monthly/yearly)',
|
|
fieldNextDue: 'Next due date',
|
|
deleteTitle: 'Delete Recurring Bill',
|
|
deleteConfirm: 'Delete "%{name}"?',
|
|
inputError: 'Input error',
|
|
inputErrorDesc: 'All fields are required.',
|
|
addSuccess: 'Added',
|
|
addSuccessDesc: 'Recurring bill "%{name}" created.',
|
|
editSuccess: 'Updated',
|
|
editSuccessDesc: 'Recurring bill "%{name}" updated.',
|
|
},
|
|
sync: {
|
|
title: 'Cloud Sync & Backup',
|
|
cloudTitle: 'Cloud Sync Configuration',
|
|
backupTitle: 'Backup & Maintenance',
|
|
configWebdav: 'Configure WebDAV',
|
|
configGit: 'Configure Git',
|
|
webdavSyncNow: 'WebDAV Sync Now',
|
|
gitSyncNow: 'Git Sync Now',
|
|
iCloudSync: 'iCloud Sync',
|
|
iCloudUnavailable: 'iCloud sync is iOS-only and unavailable on this platform',
|
|
backupNow: 'Backup Now',
|
|
restoreNow: 'Restore from Backup',
|
|
loadLocalLedger: 'Load local .bean ledger',
|
|
maintenance: 'Run maintenance & cleanup',
|
|
webdavUrl: 'WebDAV URL',
|
|
username: 'Username',
|
|
password: 'Password',
|
|
remotePath: 'Remote Path',
|
|
branch: 'Branch',
|
|
passwordToken: 'Password/Token',
|
|
webdavConfigTitle: 'WebDAV Configuration',
|
|
gitConfigTitle: 'Git Repository Configuration',
|
|
hint: 'Hint',
|
|
webdavNotConfigured: 'Please configure WebDAV first',
|
|
syncSuccess: 'Sync successful',
|
|
syncFail: 'Sync failed: %{error}',
|
|
webdvResult: 'Cloud sync complete (WebDAV: %{action})',
|
|
gitResult: 'Cloud sync complete (Git: %{action})',
|
|
gitConflict: 'Sync complete, %{count} conflicts detected — please review manually',
|
|
gitNotConfigured: 'Please configure Git remote URL first',
|
|
maintenanceTitle: 'Deep Maintenance',
|
|
maintenanceConfirm: 'Will scan and clean cache and orphan files. Continue?',
|
|
iCloudResult: 'iCloud sync complete: %{action}',
|
|
demoMode: 'Demo Mode',
|
|
gitDemoDesc: 'Git sync currently uses simulated data. Real implementation requires isomorphic-git (planned). Run demo?',
|
|
iCloudDemoDesc: 'iCloud sync currently uses simulated data. Run demo?',
|
|
backupSuccessFolder: 'Backup file saved successfully to selected folder!',
|
|
restoreSuccessParts: 'Restore successful! Restored: %{parts}',
|
|
restorePartsTx: 'Transactions',
|
|
restorePartsSettings: 'User settings',
|
|
restorePartsMeta: 'Categories/Rules/Budgets',
|
|
maintenanceDemoDesc: 'Maintenance cleanup currently uses simulated data. Run demo?',
|
|
runDemo: 'Run Demo',
|
|
demoSyncSuccess: 'Demo complete',
|
|
gitDemoContent: '2026-07-14 * "Git pull merge"\n Expenses:Food 15.00 CNY\n Assets:Alipay -15.00 CNY',
|
|
iCloudDemoContent: '2026-07-14 * "iCloud pull merge"\n Expenses:Shopping 88.00 CNY\n Assets:Alipay -88.00 CNY',
|
|
backupShareTitle: 'Share ledger backup',
|
|
backupSuccess: 'Backup successful',
|
|
backupSuccessDesc: 'Backup written to: %{path}',
|
|
backupFail: 'Backup failed',
|
|
restoreSuccess: 'Restore successful',
|
|
restoreDesc: 'Ledger restored from %{name}',
|
|
restoreFail: 'Restore failed',
|
|
loadSuccess: 'Loaded successfully',
|
|
loadDesc: 'Loaded local ledger: %{name}',
|
|
loadFail: 'Load failed',
|
|
maintenanceResult: 'Cleaned %{count} orphan files\nFreed %{size} bytes',
|
|
optimizeFail: 'Optimization failed',
|
|
exportTitle: 'Export & Share',
|
|
exportExcel: 'Export to Excel',
|
|
exportRules: 'Export rules',
|
|
importRules: 'Import rules',
|
|
exportSuccess: 'Exported',
|
|
exportFail: 'Export failed',
|
|
importRulesSuccess: 'Imported %{count} new rules',
|
|
},
|
|
errors: {
|
|
ledgerNotLoaded: 'Ledger not loaded, call loadLedger first',
|
|
},
|
|
ocr: {
|
|
scanning: 'Scanning receipt...',
|
|
notReady: 'OCR engine not ready, please try again later',
|
|
success: 'Recognized! Please verify the details',
|
|
failed: 'Could not recognize the receipt, please enter manually',
|
|
},
|
|
ai: {
|
|
chatTitle: 'AI Assistant',
|
|
welcome: 'Hi! I am your AI bookkeeping assistant. Try "spent 35 at Starbucks yesterday" to record a transaction, or just chat with me.',
|
|
inputPlaceholder: 'Type a message or expense description...',
|
|
thinking: 'AI thinking...',
|
|
notConfigured: 'AI is not configured. Go to Settings → AI Settings to enable and configure your API key.',
|
|
error: 'Sorry, something went wrong. Please try again.',
|
|
billDetected: 'Bill detected:',
|
|
tapToRecord: 'Tap to record',
|
|
},
|
|
remark: {
|
|
title: 'Remark Templates',
|
|
add: 'Add Template',
|
|
editTitle: 'Edit Template',
|
|
fieldName: 'Template name',
|
|
fieldTemplate: 'Template (${counterparty} ${time} placeholders)',
|
|
empty: 'No templates',
|
|
deleteTitle: 'Delete Template',
|
|
deleteConfirm: 'Delete "%{name}"?',
|
|
},
|
|
automation: {
|
|
title: 'Auto Bookkeeping',
|
|
detectedEvents: 'Detected Events (%{count})',
|
|
drafts: 'Pending Drafts (%{count})',
|
|
process: 'Process All',
|
|
noEvents: 'No detected events',
|
|
noDrafts: 'No pending drafts',
|
|
confirmDraft: 'Confirm',
|
|
rejectDraft: 'Reject',
|
|
sourceNotification: 'Notification',
|
|
sourceSms: 'SMS',
|
|
sourceScreenshot: 'Screenshot',
|
|
sourceOcr: 'OCR',
|
|
sourceManual: 'Manual',
|
|
channelStats: 'Source Stats',
|
|
confirmed: 'Saved successfully',
|
|
screenshotStart: 'Start Screenshot Monitor',
|
|
screenshotStop: 'Stop Screenshot Monitor',
|
|
screenshotUnavailable: 'Screenshot monitor module unavailable',
|
|
accessibilityTitle: 'Accessibility Bill Recognition',
|
|
serviceRunning: 'Service Enabled',
|
|
serviceStopped: 'Service Not Enabled',
|
|
openSettings: 'Open System Settings',
|
|
openSettingsFail: 'Cannot open system settings',
|
|
rememberPage: 'Remember Current Page',
|
|
rememberPageSuccess: 'Page Remembered',
|
|
rememberPageDesc: 'Will auto-capture and recognize %{pkg} (%{activity}) on content change',
|
|
rememberPageFail: 'Failed to remember page',
|
|
manualOcr: 'Manual OCR',
|
|
manualOcrTriggered: 'Recognition triggered, please wait',
|
|
manualOcrFail: 'Failed to trigger recognition',
|
|
whitelistTitle: 'Payment App Whitelist (auto-recognition only for these apps)',
|
|
whitelistEmpty: 'No whitelist',
|
|
rememberedPages: 'Remembered Pages (%{count})',
|
|
clearAll: 'Clear All',
|
|
clearPagesTitle: 'Clear Remembered Pages',
|
|
clearPagesConfirm: 'Clear all remembered pages? Auto-recognition will stop.',
|
|
bridgeUnavailable: 'Accessibility bridge unavailable (rebuild APK required)',
|
|
currentApp: 'Current App',
|
|
},
|
|
lockScreen: {
|
|
locked: 'App Locked',
|
|
bioPrompt: 'Please authenticate with biometrics to unlock',
|
|
authFail: 'Authentication failed, please enter passcode',
|
|
authenticating: 'Authenticating...',
|
|
clickUnlock: 'Unlock',
|
|
usePin: 'Use PIN Code',
|
|
enterPin: 'Enter Passcode',
|
|
pinFail: 'Incorrect passcode, try again',
|
|
useBio: 'Use Biometrics',
|
|
clear: 'Clear',
|
|
},
|
|
error: {
|
|
retry: 'Retry',
|
|
clearCache: 'Clear Cache & Restart',
|
|
exportLogs: 'Export Logs',
|
|
subTitle: 'If the issue persists, please export logs and submit to GitHub Issues',
|
|
noLogs: 'Notice',
|
|
noLogsMsg: 'No crash logs recorded',
|
|
exportFail: 'Export failed',
|
|
},
|
|
speedDial: {
|
|
expandHint: 'Expand quick actions',
|
|
},
|
|
};
|