diff --git a/lang/es.json b/lang/es.json
index 89f0476c..0813cbe6 100644
--- a/lang/es.json
+++ b/lang/es.json
@@ -61,6 +61,7 @@
"Account name": "Nombre de cuenta",
"Accounts": "Cuentas",
"Accounts Created": "Cuentas Creadas",
+ "Accounts need to be mapped before syncing can begin.": "Las cuentas deben mapearse antes de que pueda comenzar la sincronización.",
"Actions": "Acciones",
"Add Another Account": "Agregar Otra Cuenta",
"Add Condition": "Agregar Condición",
@@ -97,6 +98,7 @@
"Any errors or omissions in content or any loss or damage incurred from using content": "Cualquier error u omisión en el contenido o cualquier pérdida o daño incurrido por usar el contenido",
"Any interruption or cessation of transmission to or from the service": "Cualquier interrupción o cese de transmisión hacia o desde el servicio",
"Any unauthorized access to or use of our servers and/or personal information": "Cualquier acceso no autorizado o uso de nuestros servidores y/o información personal",
+ "An unexpected error occurred during sync.": "Se produjo un error inesperado durante la sincronización.",
"Appearance": "Apariencia",
"Appearance settings": "Configuración de apariencia",
"Are you overspending? Know exactly where you stand.": "¿Estás gastando de más? Conoce exactamente dónde estás parado.",
@@ -125,6 +127,7 @@
"Bi-weekly": "Quincenal",
"Bank": "Banco",
"Bank accounts": "Cuentas bancarias",
+ "Bank Connections": "Conexiones Bancarias",
"Bank logo preview": "Vista previa del logo del banco",
"Bank name": "Nombre del banco",
"Beautiful charts and graphs help you understand your spending patterns.": "Gráficos hermosos te ayudan a entender tus patrones de gasto.",
@@ -173,6 +176,8 @@
"Company Name:": "Nombre de la Empresa:",
"Conditions": "Condiciones",
"Conditions joined by:": "Condiciones unidas por:",
+ "Connect Bank": "Conectar Banco",
+ "Connections": "Conexiones",
"Confirm": "Confirmar",
"Confirm Password": "Confirmar Contraseña",
"Confirm password": "Confirmar contraseña",
@@ -184,6 +189,7 @@
"Continue Setup": "Continuar Configuración",
"Continue to Import": "Continuar con Importación",
"Create \"": "Crear \"",
+ "Create": "Crear",
"Create Account": "Crear Cuenta",
"Create Automation Rule": "Crear Regla de Automatización",
"Create Budget": "Crear Presupuesto",
@@ -222,6 +228,7 @@
"Dark": "Oscuro",
"Dashboard": "Panel",
"Data Accuracy:": "Precisión de Datos:",
+ "Daily": "Diario",
"Daily spending and transactions": "Gastos diarios y transacciones",
"Data Imported": "Datos Importados",
"Data is Encrypted": "Los Datos Están Encriptados",
@@ -249,6 +256,7 @@
"Disable 2FA": "Desactivar 2FA",
"Disable privacy mode": "Desactivar modo privado",
"Disabled": "Desactivado",
+ "Disconnect": "Desconectar",
"Discord for 80% off": "Discord con 80% de descuento",
"Don't have an account?": "¿No tienes una cuenta?",
"Download as CSV or Excel format": "Descargar en formato CSV o Excel",
@@ -312,6 +320,7 @@
"Export": "Exportar",
"Export Data": "Exportar Datos",
"Export from any bank": "Exporta desde cualquier banco",
+ "Expires": "Vence",
"Failed to create transaction": "Error al crear la transacción",
"Failed to decrypt message. Please check your password and try again.": "Error al descifrar el mensaje. Por favor verifica tu contraseña e inténtalo de nuevo.",
"Failed to update transaction": "Error al actualizar la transacción",
@@ -392,6 +401,7 @@
"Language": "Idioma",
"lime": "lima",
"Last updated:": "Última actualización:",
+ "Last synced": "Última sincronización",
"Latest transactions in this account": "Últimas transacciones en esta cuenta",
"Let's Get Started": "Comencemos",
"Lifetime": "De por vida",
@@ -421,6 +431,7 @@
"Manage Plan": "Gestionar Plan",
"Manage Subscription": "Gestionar Suscripción",
"Manage your bank accounts": "Gestiona tus cuentas bancarias",
+ "Manage your connected bank accounts for automatic transaction syncing.": "Gestiona tus cuentas bancarias conectadas para sincronización automática de transacciones.",
"Manage your profile and account settings": "Gestiona tu perfil y configuración de cuenta",
"Manage your subscription, update payment methods, or view invoices through the Stripe billing portal.": "Gestiona tu suscripción, actualiza métodos de pago o consulta facturas a través del portal de facturación de Stripe.",
"Manage your transaction automation rules": "Gestiona tus reglas de automatización de transacciones",
@@ -468,12 +479,14 @@
"No expenses this period": "Sin gastos en este período",
"No income this period": "Sin ingresos en este período",
"No labels found.": "No se encontraron etiquetas.",
+ "No bank connections yet. Connect a bank to automatically sync your transactions.": "Aún no hay conexiones bancarias. Conecta un banco para sincronizar automáticamente tus transacciones.",
"No results found": "No se encontraron resultados",
"No spending data this month": "Sin datos de gasto este mes",
"No transactions found.": "No se encontraron transacciones.",
"No tracking": "Sin seguimiento",
"None": "Ninguno",
"None (Remove)": "Ninguno (Eliminar)",
+ "Never": "Nunca",
"Notes": "Notas",
"Nothing in these Terms excludes or limits our liability for death or personal injury caused by negligence, fraud, or any liability that cannot be excluded or limited under applicable law.": "Nada en estos Términos excluye o limita nuestra responsabilidad por muerte o lesiones personales causadas por negligencia, fraude, o cualquier responsabilidad que no pueda ser excluida o limitada bajo la ley aplicable.",
"Notify us immediately of any unauthorized access or security breach": "Notifícanos inmediatamente de cualquier acceso no autorizado o violación de seguridad",
@@ -639,7 +652,12 @@
"Supported formats": "Formatos soportados",
"Supports CSV, XLS, and XLSX files": "Soporta archivos CSV, XLS y XLSX",
"Sync now": "Sincronizar ahora",
+ "Sync Now": "Sincronizar Ahora",
+ "Switch to daily view": "Cambiar a vista diaria",
+ "Switch to monthly view": "Cambiar a vista mensual",
"Sync your encrypted financial data across multiple devices via cloud storage": "Sincroniza tus datos financieros encriptados en múltiples dispositivos a través de almacenamiento en la nube",
+ "Syncing balances\u2026": "Sincronizando balances\u2026",
+ "Syncing transactions and balances\u2026": "Sincronizando transacciones y balances\u2026",
"System": "Sistema",
"Tap": "Toca",
"Tap the": "Toca el botón",
@@ -890,6 +908,7 @@
"Drop your CSV or Excel file here, or click to browse": "Arrastra tu archivo CSV o Excel aquí, o haz clic para buscar",
"Import transactions from CSV or Excel files": "Importar transacciones desde archivos CSV o Excel",
"Importing Transactions": "Importando Transacciones",
+ "Map Accounts": "Mapear Cuentas",
"Map Columns": "Mapear Columnas",
"Match your file columns to transaction fields": "Asocia las columnas de tu archivo con los campos de transacción",
"No bank found.": "No se encontró ningún banco.",
@@ -1046,5 +1065,8 @@
"Reactivate Your Subscription": "Reactiva Tu Suscripción",
"Your data and settings will be preserved, so you can pick up right where you left off.": "Tus datos y configuración se conservarán, así que puedes retomar donde lo dejaste.",
"If you have any questions or just want to chat, simply reply to this email. I read and respond to every message personally.": "Si tienes alguna pregunta o simplemente quieres charlar, responde a este correo. Leo y respondo personalmente cada mensaje.",
- "Thanks again for being part of this journey!": "¡Gracias de nuevo por ser parte de este viaje!"
+ "Thanks again for being part of this journey!": "¡Gracias de nuevo por ser parte de este viaje!",
+ "Need help? Join our Discord": "\u00bfNecesitas ayuda? \u00danete a nuestro Discord",
+ "Retry": "Reintentar",
+ "Update Credentials": "Actualizar Credenciales"
}
diff --git a/resources/js/components/accounts/account-balance-chart.tsx b/resources/js/components/accounts/account-balance-chart.tsx
index b3ad5c7f..98b6d7fd 100644
--- a/resources/js/components/accounts/account-balance-chart.tsx
+++ b/resources/js/components/accounts/account-balance-chart.tsx
@@ -29,6 +29,7 @@ import {
import { useLocale } from '@/hooks/use-locale';
import { useIsMobile } from '@/hooks/use-mobile';
import { Account, supportsInvestedAmount } from '@/types/account';
+import { formatCurrency } from '@/utils/currency';
import { formatDayFromDate, formatMonthFromYearMonth } from '@/utils/date';
import { __ } from '@/utils/i18n';
import { format, subDays, subMonths } from 'date-fns';
@@ -106,7 +107,7 @@ function InvestmentTooltipContent({
{__('Gain/loss')}
= 0 ? 'text-green-600 dark:text-green-400' : 'text-red-600 dark:text-red-400'}`}
+ className={`font-mono font-medium whitespace-nowrap tabular-nums ${gain >= 0 ? 'text-green-600 dark:text-green-400' : 'text-red-600 dark:text-red-400'}`}
>
{gain >= 0 ? '+' : ''}
{valueFormatter(gain)}
@@ -178,12 +179,7 @@ function formatChartCurrency(
currencyCode: string,
locale: string,
): string {
- return new Intl.NumberFormat(locale, {
- style: 'currency',
- currency: currencyCode,
- minimumFractionDigits: 0,
- maximumFractionDigits: 0,
- }).format(value / 100);
+ return formatCurrency(value, currencyCode, locale, 0, 0);
}
function calculateTrend(
diff --git a/resources/js/components/accounts/balances-modal.tsx b/resources/js/components/accounts/balances-modal.tsx
index 3931252b..13482f37 100644
--- a/resources/js/components/accounts/balances-modal.tsx
+++ b/resources/js/components/accounts/balances-modal.tsx
@@ -33,8 +33,10 @@ import {
TableHeader,
TableRow,
} from '@/components/ui/table';
+import { useLocale } from '@/hooks/use-locale';
import type { Account, AccountBalance } from '@/types/account';
import { supportsInvestedAmount } from '@/types/account';
+import { formatCurrency } from '@/utils/currency';
import { __ } from '@/utils/i18n';
import { Pencil, Trash2 } from 'lucide-react';
import { useCallback, useEffect, useState } from 'react';
@@ -60,6 +62,7 @@ export function BalancesModal({
onOpenChange,
onBalanceChange,
}: BalancesModalProps) {
+ const locale = useLocale();
const [balances, setBalances] = useState([]);
const [isLoading, setIsLoading] = useState(false);
const [currentPage, setCurrentPage] = useState(1);
@@ -80,10 +83,8 @@ export function BalancesModal({
useState(null);
const [isDeleting, setIsDeleting] = useState(false);
- const formatter = new Intl.NumberFormat(undefined, {
- style: 'currency',
- currency: account.currency_code,
- });
+ const formatBalance = (valueInCents: number) =>
+ formatCurrency(valueInCents, account.currency_code, locale);
const showInvestedAmount = supportsInvestedAmount(account);
@@ -217,7 +218,7 @@ export function BalancesModal({
function formatDate(dateString: string): string {
const date = new Date(dateString);
- return date.toLocaleDateString(undefined, {
+ return date.toLocaleDateString(locale, {
year: 'numeric',
month: 'short',
day: 'numeric',
@@ -302,17 +303,16 @@ export function BalancesModal({
)}
- {formatter.format(
- balance.balance / 100,
+ {formatBalance(
+ balance.balance,
)}
{showInvestedAmount && (
{balance.invested_amount !==
null
- ? formatter.format(
- balance.invested_amount /
- 100,
+ ? formatBalance(
+ balance.invested_amount,
)
: '—'}
diff --git a/resources/js/components/accounts/import-balances/import-balance-step-mapping.tsx b/resources/js/components/accounts/import-balances/import-balance-step-mapping.tsx
index 8e3c657d..c7bea6e8 100644
--- a/resources/js/components/accounts/import-balances/import-balance-step-mapping.tsx
+++ b/resources/js/components/accounts/import-balances/import-balance-step-mapping.tsx
@@ -8,6 +8,7 @@ import {
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
+import { useLocale } from '@/hooks/use-locale';
import { parseAmount, parseDate } from '@/lib/file-parser';
import type {
BalanceColumnMapping,
@@ -45,11 +46,15 @@ export function ImportBalanceStepMapping({
onBack,
}: ImportBalanceStepMappingProps) {
const isValid = columnMapping.balance_date && columnMapping.balance;
+ const locale = useLocale();
- const currencyFormatter = new Intl.NumberFormat('en-US', {
- style: 'currency',
- currency: currencyCode,
- });
+ const formatRawAmount = (value: number) =>
+ new Intl.NumberFormat(locale, {
+ style: 'currency',
+ currency: currencyCode,
+ })
+ .format(value)
+ .replace(/\s/g, '\u202F');
const previewBalances = parsedData.slice(0, 3).map((row) => {
const date = columnMapping.balance_date
@@ -69,22 +74,20 @@ export function ImportBalanceStepMapping({
);
investedAmount =
invested !== null
- ? currencyFormatter.format(invested)
+ ? formatRawAmount(invested)
: 'Invalid amount';
}
return {
date: date
- ? date.toLocaleDateString('en-US', {
+ ? date.toLocaleDateString(locale, {
year: 'numeric',
month: 'short',
day: 'numeric',
})
: 'Invalid date',
balance:
- balance !== null
- ? currencyFormatter.format(balance)
- : 'Invalid amount',
+ balance !== null ? formatRawAmount(balance) : 'Invalid amount',
investedAmount,
};
});
diff --git a/resources/js/components/accounts/import-balances/import-balance-step-preview.tsx b/resources/js/components/accounts/import-balances/import-balance-step-preview.tsx
index f0ad637a..e39e0072 100644
--- a/resources/js/components/accounts/import-balances/import-balance-step-preview.tsx
+++ b/resources/js/components/accounts/import-balances/import-balance-step-preview.tsx
@@ -9,6 +9,7 @@ import {
} from '@/components/ui/table';
import { useLocale } from '@/hooks/use-locale';
import { type ParsedBalance } from '@/types/balance-import';
+import { formatCurrency } from '@/utils/currency';
import { formatDateMedium } from '@/utils/date';
import { __ } from '@/utils/i18n';
@@ -32,12 +33,8 @@ export function ImportBalanceStepPreview({
const locale = useLocale();
const total = balances.length;
- const formatBalance = (balance: number): string => {
- return new Intl.NumberFormat(locale, {
- style: 'currency',
- currency: currencyCode,
- }).format(balance / 100);
- };
+ const formatBalance = (valueInCents: number): string =>
+ formatCurrency(valueInCents, currencyCode, locale);
const hasInvestedData =
showInvestedAmount && balances.some((b) => b.invested_amount !== null);
diff --git a/resources/js/components/automation-rules/create-automation-rule-dialog.tsx b/resources/js/components/automation-rules/create-automation-rule-dialog.tsx
index 4bc68778..b3ed8644 100644
--- a/resources/js/components/automation-rules/create-automation-rule-dialog.tsx
+++ b/resources/js/components/automation-rules/create-automation-rule-dialog.tsx
@@ -126,7 +126,7 @@ export function CreateAutomationRuleDialog({
{__('Create Automation Rule')}
{__(
- 'Create a rule to automatically categorize transactions.',
+ 'Create a rule to automatically categorize transactions and add labels.',
)}
@@ -194,7 +194,7 @@ export function CreateAutomationRuleDialog({
disabled={isSubmitting}
data-testid="submit-automation-rule"
>
- {isSubmitting ? 'Creating...' : 'Create'}
+ {isSubmitting ? __('Creating...') : __('Create')}
diff --git a/resources/js/components/budgets/budget-list-card.tsx b/resources/js/components/budgets/budget-list-card.tsx
index 22f9d404..84d8789d 100644
--- a/resources/js/components/budgets/budget-list-card.tsx
+++ b/resources/js/components/budgets/budget-list-card.tsx
@@ -102,9 +102,17 @@ export function BudgetListCard({ budget, currencyCode }: Props) {
{__('Spent')}
- {formatCurrency(stats.totalSpent, currencyCode)}{' '}
+ {formatCurrency(
+ stats.totalSpent,
+ currencyCode,
+ locale,
+ )}{' '}
{__('of')}{' '}
- {formatCurrency(stats.totalAllocated, currencyCode)}
+ {formatCurrency(
+ stats.totalAllocated,
+ currencyCode,
+ locale,
+ )}