Files
OnBudget/lib/l10n/app_localizations_en.dart
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

1188 lines
26 KiB
Dart

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