fix(i18n): translate Unknown Income/Expense and other missing ES strings (#331)

## Summary

Fixes untranslated strings surfaced in the Spanish UI (e.g. `Unknown
Income`, `Unknown Expense` on the cashflow breakdown).

## Root cause

`CashflowAnalyticsController` pushed literal English names into the API
response for uncategorized totals, bypassing `__()`. Additional `__()`
calls across the sync flow and validation had no matching keys in
`lang/es.json`. Several frontend toasts/errors were also hardcoded.

## Changes

**Controller (P0)**
- `app/Http/Controllers/Api/CashflowAnalyticsController.php` — wrap
`Unknown Income`/`Unknown Expense` with `__()` (lines 250, 323).

**Translations (P0 + P1 + P2)**
- `lang/es.json` — added 23 keys (1730 → 1753):
  - `Unknown Income`, `Unknown Expense`, `Unknown Bank`, `Unknown error`
- Sync/banking messages: `Invalid credentials…`, `Rate limit exceeded…`,
`Failed to sync with the provider…`, `The provider is experiencing
issues…`, `An unexpected error occurred during sync…`, `Credentials
updated. Sync started.`, `Action required: :provider connection needs
attention`, `:count new transactions synced on Whisper Money`
- Validation: `The selected property cannot be linked.`, `The selected
property is already linked to a loan.`, `The verification link is
invalid.`, `This field is required.`
- Misc: `Confirm your waitlist spot - Whisper Money`, `Log in to your
bank`
- Frontend toasts: `Failed to re-evaluate rules. Please try again.`,
`Failed to load import data`, `All transactions failed to import`,
`Failed to update transactions`, `Failed to update transactions with
labels`

**Frontend hardcoded strings wrapped in `__()` (P2)**
- `resources/js/components/transactions/import-transactions-drawer.tsx`
- `resources/js/components/accounts/import-balances-drawer.tsx`
- `resources/js/lib/sync-manager.ts` (added `@/utils/i18n` import)
- `resources/js/components/transactions/transaction-list.tsx`
- `resources/js/components/transactions/import-transactions-button.tsx`
- `resources/js/pages/transactions/index.tsx`

## Verification

- `vendor/bin/pint --dirty` → pass
- `php artisan test --filter=CashflowAnalytics` → 24 passed
- `php artisan test --filter=Sync` → 166 passed
- `npm run build` → OK
- Post-fix audit: 0 real missing `__()` keys remaining
This commit is contained in:
Víctor Falcón 2026-04-24 17:58:26 +01:00 committed by GitHub
parent 7028400050
commit 79075dbcdf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 1765 additions and 1741 deletions

View File

@ -247,7 +247,7 @@ class CashflowAnalyticsController extends Controller
'category_id' => null,
'category' => (new Category)->forceFill([
'id' => null,
'name' => $isIncome ? 'Unknown Income' : 'Unknown Expense',
'name' => $isIncome ? __('Unknown Income') : __('Unknown Expense'),
'type' => $isIncome ? CategoryType::Income : CategoryType::Expense,
'color' => 'gray',
'icon' => 'HelpCircle',
@ -320,7 +320,7 @@ class CashflowAnalyticsController extends Controller
'category_id' => null,
'category' => (new Category)->forceFill([
'id' => null,
'name' => $type === CategoryType::Income ? 'Unknown Income' : 'Unknown Expense',
'name' => $type === CategoryType::Income ? __('Unknown Income') : __('Unknown Expense'),
'type' => $type,
'color' => 'gray',
'icon' => 'HelpCircle',

File diff suppressed because it is too large Load Diff

View File

@ -487,7 +487,7 @@ export function ImportBalancesDrawer({
const errorMessage =
result.reason instanceof Error
? result.reason.message
: 'Unknown error';
: __('Unknown error');
errors.push({
rowNumber,

View File

@ -47,7 +47,7 @@ export function ImportTransactionsButton() {
setImportData(data);
setDrawerOpen(true);
} catch (error) {
toast.error('Failed to load import data');
toast.error(__('Failed to load import data'));
console.error(error);
} finally {
setLoading(false);

View File

@ -430,7 +430,7 @@ export function ImportTransactionsDrawer({
const errorMessage =
result.reason instanceof Error
? result.reason.message
: 'Unknown error';
: __('Unknown error');
console.error(`Transaction ${rowNumber} failed:`, {
transaction,
@ -557,7 +557,7 @@ export function ImportTransactionsDrawer({
});
onOpenChange(false);
} else {
toast.error('All transactions failed to import');
toast.error(__('All transactions failed to import'));
}
transactionSyncService

View File

@ -932,7 +932,7 @@ export function TransactionList({
} catch (error) {
consoleDebug('❌ Error during bulk re-evaluation:', error);
console.error('Failed to re-evaluate rules:', error);
toast.error('Failed to re-evaluate rules. Please try again.', {
toast.error(__('Failed to re-evaluate rules. Please try again.'), {
id: toastId,
});
} finally {

View File

@ -1,3 +1,4 @@
import { __ } from '@/utils/i18n';
import type { Transaction } from '@/types/transaction';
import type { UUID } from '@/types/uuid';
import axios from 'axios';
@ -64,7 +65,7 @@ export class TransactionSyncManager {
} catch (error) {
result.success = false;
result.errors.push(
error instanceof Error ? error.message : 'Unknown error',
error instanceof Error ? error.message : __('Unknown error'),
);
} finally {
this.syncInProgress = false;

View File

@ -719,7 +719,7 @@ export default function Transactions({
refreshTransactions();
} catch (error) {
console.error('Failed to re-evaluate rules:', error);
toast.error('Failed to re-evaluate rules. Please try again.', {
toast.error(__('Failed to re-evaluate rules. Please try again.'), {
id: toastId,
});
} finally {
@ -851,7 +851,7 @@ export default function Transactions({
}
} catch (error) {
console.error('Failed to update transactions:', error);
toast.error('Failed to update transactions');
toast.error(__('Failed to update transactions'));
} finally {
setIsBulkUpdating(false);
}
@ -977,7 +977,7 @@ export default function Transactions({
}
} catch (error) {
console.error('Failed to update transactions with labels:', error);
toast.error('Failed to update transactions with labels');
toast.error(__('Failed to update transactions with labels'));
} finally {
setIsBulkUpdating(false);
}