Notification parsing: - Drop the account_bindings table/DAO/repo/entity/controller/screen; account resolution now goes senderToAccount rule -> source_apps.defaultAccountId (trusted) -> global default (untrusted -> Inbox), via v1->v2 migration. - Add defaultAccountId to source_apps; per-app settings consolidated into source_app_detail_screen (/settings/parsing/apps/:pkg). - Inbox auto-learns an app default on first Confirm/CreateRule; account picker on the card instead of a disabled button; parse_error_labels extracted. Analytics: - Replace placeholder screen with fl_chart cards (chart_card, chart_theme, month_stepper, month_math domain helper); slim down habit_analysis_screen. Android: add launcher icon (adaptive foreground + colors.xml) and app_name. Tests: migration_v2, analytics (screen/month_math), AI retry, inbox visibility; update resolver/gate/inbox suites for the new resolution path. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
455 lines
17 KiB
Plaintext
455 lines
17 KiB
Plaintext
{
|
|
"@@locale": "en",
|
|
|
|
"appTitle": "Kitty finances",
|
|
|
|
"navHome": "Home",
|
|
"navAnalytics": "Analytics",
|
|
"navAccounts": "Accounts",
|
|
"navProfile": "Profile",
|
|
|
|
"allAccounts": "All",
|
|
|
|
"comingSoon": "Coming soon",
|
|
|
|
"budget": "BUDGET",
|
|
"balance": "BALANCE",
|
|
"income": "INCOME",
|
|
"expenses": "EXPENSES",
|
|
|
|
"transactions": "Transactions",
|
|
"transactionCount": "{count, plural, one{{count} transaction} other{{count} transactions}}",
|
|
"@transactionCount": {
|
|
"placeholders": {
|
|
"count": {
|
|
"type": "int"
|
|
}
|
|
}
|
|
},
|
|
|
|
"allCategoriesFilter": "All categories · all types",
|
|
"filterByCategories": "Filter by category",
|
|
"clearFilter": "Clear",
|
|
"selectAll": "Select all",
|
|
"categoriesSelectedCount": "{count, plural, one{{count} category} other{{count} categories}}",
|
|
"@categoriesSelectedCount": {
|
|
"placeholders": {
|
|
"count": {
|
|
"type": "int"
|
|
}
|
|
}
|
|
},
|
|
|
|
"moreCategories": "{count, plural, one{+ {count} more →} other{+ {count} more →}}",
|
|
"@moreCategories": {
|
|
"placeholders": {
|
|
"count": {
|
|
"type": "int"
|
|
}
|
|
}
|
|
},
|
|
|
|
"todayWithDate": "Today · {date}",
|
|
"@todayWithDate": {
|
|
"placeholders": {
|
|
"date": {
|
|
"type": "String"
|
|
}
|
|
}
|
|
},
|
|
|
|
"yesterdayWithDate": "Yesterday · {date}",
|
|
"@yesterdayWithDate": {
|
|
"placeholders": {
|
|
"date": {
|
|
"type": "String"
|
|
}
|
|
}
|
|
},
|
|
|
|
"analyticsTitle": "Reports",
|
|
"analyticsSubtitle": "Analytics",
|
|
"analyticsNoData": "No data for this month",
|
|
|
|
"accountsSubtitle": "Management",
|
|
|
|
"profileSubtitle": "Settings",
|
|
"darkTheme": "Dark theme",
|
|
"profileLanguageTile": "Language",
|
|
"langEnglish": "English",
|
|
"langRussian": "Russian",
|
|
"profileHint": "User profile, currency, locale and other settings will appear here.",
|
|
|
|
"onboardingTitle": "Welcome",
|
|
"onboardingSubtitle": "Tell us your name to get started.",
|
|
"onboardingNameLabel": "Your name",
|
|
"onboardingNameHint": "How should we address you?",
|
|
"onboardingContinue": "Continue",
|
|
"onboardingAccountTitle": "Your first account",
|
|
"onboardingAccountSubtitle": "Add an account to track your money. You can add more later.",
|
|
|
|
"txNewTitle": "New transaction",
|
|
"txEditTitle": "Edit transaction",
|
|
"txTypeExpense": "Expense",
|
|
"txTypeIncome": "Income",
|
|
"txTypeTransfer": "Transfer",
|
|
"txAmountHint": "0",
|
|
"txCategoryLabel": "Category",
|
|
"txAccountLabel": "Account",
|
|
"txAccountFromLabel": "From",
|
|
"txAccountToLabel": "To",
|
|
"txDateTimeLabel": "Date & time",
|
|
"txBalanceAfter": "Balance after",
|
|
"txTransferAfter": "After transfer",
|
|
"txNoteLabel": "Merchant",
|
|
"txNoteHint": "e.g. Amazon…",
|
|
"txExtraInfoLabel": "Additional info",
|
|
"txExtraInfoHint": "e.g. Groceries for the week…",
|
|
"txSaveButton": "Add transaction",
|
|
"txSaveEditButton": "Save changes",
|
|
"txTransferButton": "Transfer {amount}",
|
|
"@txTransferButton": {
|
|
"placeholders": {
|
|
"amount": { "type": "String" }
|
|
}
|
|
},
|
|
"txDeleteButton": "Delete",
|
|
"txDeleteConfirmTitle": "Delete transaction?",
|
|
"txDeleteConfirmMessage": "This action cannot be undone.",
|
|
"txDeleteConfirmYes": "Delete",
|
|
"txDeleteConfirmNo": "Cancel",
|
|
"txPickCategory": "Pick a category",
|
|
"txPickAccount": "Pick an account",
|
|
"pickerAccountTitle": "Account",
|
|
"pickerAccountSubtitle": "{count, plural, one{{count} account} other{{count} accounts}}",
|
|
"@pickerAccountSubtitle": {
|
|
"placeholders": {
|
|
"count": { "type": "int" }
|
|
}
|
|
},
|
|
"pickerCategoryTitle": "Category",
|
|
"pickerCategorySubtitle": "{count, plural, one{{count} category} other{{count} categories}}",
|
|
"@pickerCategorySubtitle": {
|
|
"placeholders": {
|
|
"count": { "type": "int" }
|
|
}
|
|
},
|
|
"pickerAddAccount": "Add account",
|
|
"pickerCreateCategory": "Create category",
|
|
"comingSoonShort": "Coming soon",
|
|
"txValidationAmountRequired": "Enter an amount",
|
|
"txValidationAccountRequired": "Pick an account",
|
|
"txValidationCategoryRequired": "Pick a category",
|
|
"txValidationTransferSameAccount": "Source and destination must differ",
|
|
"txValidationTransferDestRequired": "Pick a destination account",
|
|
|
|
"categoriesScreenTitle": "Categories",
|
|
"categoriesEmpty": "No categories yet. Tap + to create your first one.",
|
|
"categoryNewTitle": "New category",
|
|
"categoryEditTitle": "Edit category",
|
|
"categoryNameLabel": "Name",
|
|
"categoryNameHint": "e.g. Groceries",
|
|
"categoryIconLabel": "Icon",
|
|
"categoryColorLabel": "Color",
|
|
"categorySaveButton": "Create category",
|
|
"categoryArchiveButton": "Archive",
|
|
"categoryArchiveConfirmTitle": "Archive category?",
|
|
"categoryArchiveConfirmMessage": "Archived categories disappear from pickers and lists. Existing transactions keep their reference.",
|
|
"categoryValidationNameRequired": "Enter a name",
|
|
"categoryValidationNameTooLong": "Name is too long (max 50 chars)",
|
|
|
|
"iconPickerTitle": "Pick an icon",
|
|
"colorPickerTitle": "Pick a color",
|
|
"currencyPickerTitle": "Pick a currency",
|
|
"currencyPickerSearchHint": "Search…",
|
|
"colorPickerAllUsed": "All palette colors are already in use by other categories.",
|
|
"iconGroupFood": "Food",
|
|
"iconGroupTransport": "Transport",
|
|
"iconGroupShopping": "Shopping",
|
|
"iconGroupHome": "Home",
|
|
"iconGroupHealth": "Health",
|
|
"iconGroupEntertainment": "Leisure",
|
|
"iconGroupWork": "Work",
|
|
"iconGroupTravel": "Travel",
|
|
"iconGroupFinance": "Finance",
|
|
"iconGroupOther": "Other",
|
|
|
|
"profileCategoriesTile": "Categories",
|
|
"profileAccountsTile": "Accounts",
|
|
|
|
"accountsEmpty": "No accounts yet. Tap + to add one.",
|
|
"accountNewTitle": "New account",
|
|
"accountEditTitle": "Edit account",
|
|
"accountNameLabel": "Name",
|
|
"accountNameHint": "e.g. Cash wallet",
|
|
"accountTypeLabel": "Type",
|
|
"accountTypeCash": "Cash",
|
|
"accountTypeCard": "Card",
|
|
"accountTypeBank": "Bank",
|
|
"accountTypeSavings": "Savings",
|
|
"accountCurrencyLabel": "Currency",
|
|
"accountInitialBalanceLabel": "Initial balance",
|
|
"accountIconLabel": "Icon",
|
|
"accountColorLabel": "Color",
|
|
"accountIsDefaultLabel": "Default account",
|
|
"accountIsDefaultHint": "Used when creating new transactions",
|
|
"accountSaveButton": "Create account",
|
|
"accountSaveEditButton": "Save changes",
|
|
"accountArchiveButton": "Archive",
|
|
"accountArchiveConfirmTitle": "Archive account?",
|
|
"accountArchiveConfirmMessage": "Archived accounts disappear from pickers and lists. Existing transactions keep their reference.",
|
|
"accountValidationNameRequired": "Enter a name",
|
|
"accountValidationNameTooLong": "Name is too long (max 50 chars)",
|
|
"accountSaveError": "Failed to save the account. Please try again.",
|
|
"accountArchiveError": "Failed to archive the account. Please try again.",
|
|
"accountSetDefaultError": "Account saved, but couldn't mark it as default.",
|
|
|
|
"profileParsingTile": "Notification parsing",
|
|
|
|
"parsingSettingsTitle": "Notification parsing",
|
|
"parsingSettingsSubtitle": "Settings",
|
|
"parsingEnableLabel": "Recognize notifications",
|
|
"parsingAutoApplyLabel": "Auto-add transactions",
|
|
"parsingAutoApplyHint": "Add without confirmation when a rule exists, the amount is found in the message text and the account is trusted.",
|
|
"parsingTransferPairingLabel": "Merge transfers between accounts",
|
|
"parsingTransferPairingHint": "Two notifications about one transfer (debit + credit) are merged into a single transfer transaction pending confirmation.",
|
|
"parsingDiagnosticModeLabel": "Diagnostic mode",
|
|
"parsingDiagnosticModeHint": "Capture every notification from monitored apps (incl. empty/duplicate) with a full dump of its fields. Temporary — for debugging, adds noise to the parsing log.",
|
|
"inboxWhyNotAuto": "Why not automatic: {reasons}",
|
|
"@inboxWhyNotAuto": { "placeholders": { "reasons": { "type": "String" } } },
|
|
"gateCheckRuleMatched": "no rule for this merchant",
|
|
"gateCheckAmountVerifiedInBody": "amount not found in the message text",
|
|
"gateCheckCurrencyKnown": "currency not recognized",
|
|
"gateCheckTypeMatchesRule": "transaction type differs from the rule",
|
|
"gateCheckAccountResolved": "account not resolved",
|
|
"gateCheckAccountTrusted": "account not confirmed for this app",
|
|
"gateCheckAmountUnderCap": "amount is too large",
|
|
"parsingRulesTile": "Parsing rules",
|
|
"parsingRulesCreatedCount": "Rules created: {count}",
|
|
"@parsingRulesCreatedCount": { "placeholders": { "count": { "type": "int" } } },
|
|
"parsingAiSectionTitle": "AI (DeepSeek)",
|
|
"parsingAiComingSoon": "AI recognition is coming in a future version.",
|
|
"parsingNotifAccessTitle": "Notification access",
|
|
"parsingNotifAccessGranted": "Granted — notifications are being read",
|
|
"parsingNotifAccessDenied": "Not granted — tap to open settings",
|
|
"parsingNotifAccessOpenSettings": "Open settings",
|
|
"parsingDebugSectionTitle": "Debug",
|
|
"parsingDebugInjectTile": "Inject a test notification",
|
|
|
|
"parsingLogTile": "Parsing log",
|
|
"parsingLogTitle": "Parsing log",
|
|
"parsingLogEmpty": "No messages yet. Incoming notifications will appear here.",
|
|
"parsingLogError": "Failed to load the log.",
|
|
"parsingLogFilterAll": "All",
|
|
"parsingLogFilterInbox": "Inbox",
|
|
"parsingLogFilterApplied": "Applied",
|
|
"parsingLogFilterIgnored": "Ignored",
|
|
"parsingLogFilterFailed": "Failed",
|
|
"parsingStatusPending": "Pending",
|
|
"parsingStatusInbox": "Inbox",
|
|
"parsingStatusApplied": "Applied",
|
|
"parsingStatusIgnored": "Ignored",
|
|
"parsingStatusFailed": "Failed",
|
|
"parsingStatusPendingAi": "Waiting for AI",
|
|
"parsingStatusParsedPartial": "Partial",
|
|
"parsingStatusWaitingPair": "Waiting for pair",
|
|
"parsingStatusPaired": "Merged into transfer",
|
|
"parsingUnmergeTransfer": "Split transfer",
|
|
|
|
"parsingLogOnline": "Online",
|
|
"parsingLogOffline": "Offline",
|
|
"parsingLogFilterWaiting": "Waiting",
|
|
|
|
"parsingDetailConfidence": "Confidence",
|
|
"parsingDetailPipeline": "Pipeline",
|
|
"parsingDetailMessage": "Message",
|
|
"parsingDetailDraft": "Draft",
|
|
"parsingDetailRule": "Rule suggestion",
|
|
"parsingDetailMeta": "Meta",
|
|
"parsingDetailDiagnostics": "Diagnostics",
|
|
"parsingDetailAttempt": "Attempt {count}/{max}",
|
|
"@parsingDetailAttempt": { "placeholders": { "count": { "type": "int" }, "max": { "type": "int" } } },
|
|
"parsingDetailSource": "Source",
|
|
"parsingDetailFailedChecks": "Why not automatic",
|
|
"parsingDetailAmountScore": "Amount",
|
|
"parsingDetailAccountScore": "Account",
|
|
"parsingDetailTypeScore": "Type",
|
|
"parsingDetailMerchantScore": "Merchant",
|
|
"parsingDetailCategoryScore": "Category",
|
|
"parsingDetailTitle": "Title",
|
|
"parsingDetailBody": "Body",
|
|
"parsingDetailCard": "Card",
|
|
"parsingDetailCurrency": "Currency",
|
|
"parsingDetailKind": "Kind",
|
|
"parsingDetailDate": "Date",
|
|
"parsingDetailMerchantRaw": "Merchant (raw)",
|
|
"parsingDetailMerchantCanonical": "Merchant (canonical)",
|
|
"parsingDetailCounterparty": "Counterparty",
|
|
"parsingDetailCategorySuggestion": "Category hint",
|
|
"parsingDetailAccountId": "Account ID",
|
|
"parsingDetailCategoryId": "Category ID",
|
|
"parsingDetailTransferTo": "Transfer to",
|
|
"parsingDetailRuleCategory": "Category",
|
|
"parsingDetailTransactionId": "Transaction ID",
|
|
"parsingDetailDedupHash": "Dedup hash",
|
|
"parsingDetailReceivedAt": "Received at",
|
|
"parsingDetailCreatedAt": "Created at",
|
|
|
|
"inboxTitle": "From notifications",
|
|
"inboxSubtitle": "A rule is learned on the first tap: similar messages will be confirmed automatically.",
|
|
"inboxEmpty": "Inbox is empty. New notifications will appear here.",
|
|
"inboxCreateRule": "Create rule “{merchant} → {category}”",
|
|
"@inboxCreateRule": { "placeholders": { "merchant": { "type": "String" }, "category": { "type": "String" } } },
|
|
"inboxCreateRuleNoCategory": "Create rule for “{merchant}”",
|
|
"@inboxCreateRuleNoCategory": { "placeholders": { "merchant": { "type": "String" } } },
|
|
"inboxConfirm": "Confirm",
|
|
"inboxCategoryRequiredHint": "Pick a category to confirm",
|
|
"inboxIgnore": "Ignore",
|
|
"inboxApplyAll": "Apply all",
|
|
"inboxTransferPairTitle": "Transfer between accounts",
|
|
"inboxTransferFrom": "From account",
|
|
"inboxTransferTo": "To account",
|
|
"inboxTransferPickAccount": "Choose…",
|
|
"inboxUnpair": "Split",
|
|
"inboxUnrecognized": "Not recognized",
|
|
"inboxParseErrorTitle": "Recognition error",
|
|
"inboxRetry": "Try again",
|
|
"inboxAddManually": "Add manually",
|
|
"inboxNoCategory": "No category",
|
|
"inboxAccountUnknown": "Account unknown",
|
|
"inboxEditRuleTooltip": "Edit before saving",
|
|
"inboxWaitingNetworkTitle": "Waiting for network",
|
|
"inboxStuckOffline": "No internet connection. Parsing will resume once you're back online.",
|
|
"inboxStuckNetwork": "Network is up, but the AI request failed. Will retry automatically.",
|
|
"parseErrorOffline": "No internet connection",
|
|
"parseErrorNetwork": "AI request failed (network error)",
|
|
"parseErrorRetryLimit": "AI retry limit reached",
|
|
|
|
"rulesTitle": "Parsing rules",
|
|
"rulesSubtitle": "Settings",
|
|
"rulesFilterAll": "All",
|
|
"rulesFilterMerchants": "Merchants",
|
|
"rulesFilterAccounts": "Accounts",
|
|
"rulesFilterIgnore": "Ignore",
|
|
"rulesEmpty": "No rules yet. Create them from the Inbox in one tap.",
|
|
"ruleMatchesCount": "{count, plural, one{{count} match} other{{count} matches}}",
|
|
"@ruleMatchesCount": { "placeholders": { "count": { "type": "int" } } },
|
|
"ruleDisabled": "Disabled",
|
|
|
|
"ruleEditorNewTitle": "New rule",
|
|
"ruleEditorEditTitle": "Rule",
|
|
"ruleEditorIfContains": "If the notification contains",
|
|
"ruleEditorPatternHint": "e.g. WBSPB",
|
|
"ruleEditorModeContains": "Contains",
|
|
"ruleEditorModeExact": "Exact",
|
|
"ruleEditorModeRegex": "Regex",
|
|
"ruleEditorThen": "Then it is:",
|
|
"ruleEditorMerchant": "Merchant",
|
|
"ruleEditorMerchantHint": "e.g. Wildberries",
|
|
"ruleEditorCategory": "Category",
|
|
"ruleEditorAccount": "Account",
|
|
"ruleEditorAccountUnchanged": "— keep unchanged —",
|
|
"ruleEditorApp": "App",
|
|
"ruleEditorAppPick": "Select app",
|
|
"ruleEditorNoApps": "Add a source app first",
|
|
"rulesUnknownApp": "Unknown app",
|
|
"ruleEditorAdvanced": "Advanced",
|
|
"ruleEditorPriority": "Priority",
|
|
"ruleEditorMatchesTitle": "Matches (last 30 days)",
|
|
"ruleEditorNoMatches": "No matches in the last 30 days",
|
|
"ruleEditorSave": "Save",
|
|
"ruleEditorDelete": "Delete",
|
|
"ruleEditorEnabledLabel": "Enabled",
|
|
|
|
"debugInjectTitle": "Test notification",
|
|
"debugInjectSubtitle": "Debug",
|
|
"debugInjectPackageLabel": "App package",
|
|
"debugInjectTitleLabel": "Title (optional)",
|
|
"debugInjectBodyLabel": "Notification text",
|
|
"debugInjectSend": "Send to pipeline",
|
|
"debugInjectSent": "Sent",
|
|
"debugInjectFillSample": "Sample: purchase",
|
|
|
|
"aiConsentKeyMissing": "API Key is missing",
|
|
"aiConsentSaved": "Settings saved",
|
|
"aiConsentTitle": "AI Parsing",
|
|
"aiConsentBody": "We use the DeepSeek API to parse notifications with AI. Your data is sent to the selected model. You need your own API key. Tokens are used.",
|
|
"aiConsentKeyLabel": "DeepSeek API Key",
|
|
"aiConsentKeyHint": "sk-...",
|
|
"aiConsentModelLabel": "AI Model",
|
|
"aiConsentAllow": "Allow AI Parsing",
|
|
"aiConsentRegexOnly": "Use Regex Only (Local)",
|
|
|
|
"parsingAiConfigure": "Configure AI",
|
|
"parsingAiTokensToday": "Tokens used today: {tokens}",
|
|
"@parsingAiTokensToday": {
|
|
"placeholders": {
|
|
"tokens": {
|
|
"type": "int"
|
|
}
|
|
}
|
|
},
|
|
"parsingAiTokensTodayLimited": "Tokens today: {tokens} / {limit}",
|
|
"@parsingAiTokensTodayLimited": {
|
|
"placeholders": {
|
|
"tokens": {
|
|
"type": "int"
|
|
},
|
|
"limit": {
|
|
"type": "int"
|
|
}
|
|
}
|
|
},
|
|
|
|
"habitTrackingTile": "Spending habit analysis",
|
|
"habitObligationRequired": "Required",
|
|
"habitObligationOptional": "Optional",
|
|
"habitObligationUnnecessary": "Unnecessary",
|
|
"habitObligationOptionalShort": "Optional",
|
|
"habitImpulseImpulsive": "Impulse",
|
|
"habitImpulseConsidered": "Thoughtful",
|
|
"habitNotMarked": "not marked",
|
|
"habitFilterAll": "All",
|
|
"habitScaleImpulseCaps": "IMPULSIVENESS",
|
|
"habitScaleObligationCaps": "NECESSITY",
|
|
"habitAnalysisTitle": "Transactions",
|
|
"habitAnalysisTile": "Habit analysis",
|
|
|
|
"profileSeedDemoTile": "Fill with demo transactions",
|
|
"profileSeedDemoSuccess": "Demo transactions added",
|
|
|
|
"commonAdd": "Add",
|
|
"commonCancel": "Cancel",
|
|
"commonDelete": "Delete",
|
|
|
|
"parsingAppsTile": "Source apps",
|
|
|
|
"sourceAppsTitle": "Source apps",
|
|
"sourceAppsAddedSection": "Monitored",
|
|
"sourceAppsAvailableSection": "Available",
|
|
"sourceAppsEmpty": "No apps added yet. Pick a bank below or add one manually.",
|
|
"sourceAppsAddManual": "Add manually",
|
|
"sourceAppsPackageLabel": "Package name",
|
|
"sourceAppsNameLabel": "Display name (optional)",
|
|
"sourceAppsPickFromInstalled": "Choose from installed apps",
|
|
"sourceAppsPickerTitle": "Installed apps",
|
|
"sourceAppsSearchHint": "Search",
|
|
"sourceAppsPickerEmpty": "No apps found",
|
|
"sourceAppsAlreadyAdded": "Already added",
|
|
"sourceAppsSelfMerchantLabel": "Merchant is the app itself",
|
|
"sourceAppsSelfMerchantHint": "Notifications never name the seller: pick the category manually, no rules are suggested",
|
|
|
|
"appDetailEnabled": "Monitor notifications",
|
|
"appDetailDefaultAccount": "Default account",
|
|
"appDetailDefaultAccountNone": "Not set",
|
|
"appDetailDefaultAccountHint": "New transactions from this app go to this account. It is remembered automatically the first time you confirm a message in the Inbox.",
|
|
"appDetailRoutingRules": "Account routing rules",
|
|
"appDetailRoutingRulesEmpty": "If the app reports several accounts (card, deposit), add a rule: text pattern → account. Rules override the default account.",
|
|
"appDetailAddRule": "Add",
|
|
"inboxAppDefaultSet": "Account saved as the app's default",
|
|
|
|
"ruleKindMerchant": "Merchant → category",
|
|
"ruleKindAccount": "Sender → account",
|
|
"ruleEditorAccountPick": "Select account"
|
|
}
|