Files
OnBudget/lib/l10n/app_en.arb
T
SandersandClaude Opus 4.8 eed9164150 Add CompanionDevice watch pairing and store raw AI responses
Two fixes for the notification-parsing pipeline:

- CompanionDeviceManager pairing (DEVICE_PROFILE_WATCH -> COMPANION_DEVICE_WATCH
  role -> RECEIVE_SENSITIVE_NOTIFICATIONS) via a new platform channel in
  MainActivity.kt, companion_device_channel.dart and companion_access_controller,
  surfaced as a card in parsing settings. Lifts the system "Confidential"
  redaction that hid VTB/T-Bank notification text from the listener.

- raw_messages.ai_response: the model's raw completion content is now persisted
  (schema v4 + migration) and shown in the parsing log detail panel. Unlike
  draftJson it survives inbox sweeps and is filled even for partial/ignored
  outcomes.

flutter analyze: no issues.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 22:04:39 +03:00

473 lines
18 KiB
Plaintext

{
"@@locale": "en",
"appTitle": "Kitty finances",
"navHome": "Home",
"navInbox": "Inbox",
"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",
"gateCheckRuleAutoApplyEnabled": "auto-apply is off for the rule",
"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",
"parsingCompanionTitle": "Full notification access",
"parsingCompanionBound": "Watch linked — notification text arrives unredacted",
"parsingCompanionNotBound": "Android hides sensitive notification text. Link a watch to make the app trusted.",
"parsingCompanionBind": "Link watch",
"parsingCompanionBindFailed": "Failed to link: {error}",
"@parsingCompanionBindFailed": { "placeholders": { "error": { "type": "String" } } },
"parsingCompanionPickerTitle": "Select your watch",
"parsingCompanionPickerScan": "Other device — system scan",
"parsingCompanionBtDenied": "Bluetooth permission denied — allow it in app 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",
"parsingDetailAiResponse": "AI response",
"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",
"ruleEditorAccountPick": "Select account",
"ruleEditorAccountAuto": "Auto — app account",
"ruleEditorIgnoreLabel": "Ignore message",
"ruleEditorIgnoreHint": "Matching notifications are skipped without a transaction",
"ruleEditorAutoApplyLabel": "Apply automatically",
"ruleEditorAutoApplyHint": "Off: matches go to the Inbox pre-filled for manual review",
"ruleEditorTxType": "Type",
"ruleEditorTxTypeAny": "Any",
"ruleManualBadge": "manual"
}