diff --git a/packages/loot-core/src/server/budget/actions.ts b/packages/loot-core/src/server/budget/actions.ts index e0bfbf05c14..1443c112e58 100644 --- a/packages/loot-core/src/server/budget/actions.ts +++ b/packages/loot-core/src/server/budget/actions.ts @@ -5,6 +5,8 @@ import * as db from '../db'; import * as sheet from '../sheet'; import { batchMessages } from '../sync'; +import { useTranslation } from 'react-i18next'; + export async function getSheetValue( sheetName: string, cell: string, @@ -512,17 +514,23 @@ async function addMovementNotes({ const fromCategoryName = from === 'to-be-budgeted' - ? 'To Budget' + ? t('To Budget') : categories.find(c => c.id === from)?.name; const toCategoryName = to === 'to-be-budgeted' - ? 'To Budget' + ? t('To Budget') : to === 'overbudgeted' - ? 'Overbudgeted' + ? t('Overbudgeted') : categories.find(c => c.id === to)?.name; - const note = `Reassigned ${displayAmount} from ${fromCategoryName} → ${toCategoryName} on ${displayDay}`; + const note = t('Reassigned {{displayAmount}} from {{fromCategory}} → ' + + '{{toCategory}} on {{displayDay}}', { + displayAmount: displayAmount, + fromCategory: fromCategoryName, + toCategory: toCategoryName, + displayDay: displayDay + }); await db.update('notes', { id: monthBudgetNotesId, diff --git a/packages/loot-core/src/server/budget/categoryTemplate.ts b/packages/loot-core/src/server/budget/categoryTemplate.ts index a930fc601ff..e9c7fc4059a 100644 --- a/packages/loot-core/src/server/budget/categoryTemplate.ts +++ b/packages/loot-core/src/server/budget/categoryTemplate.ts @@ -9,6 +9,8 @@ import { goalsSchedule } from './goalsSchedule'; import { getActiveSchedules } from './statements'; import { Template } from './types/templates'; +import { useTranslation } from 'react-i18next'; + export class CategoryTemplate { /*---------------------------------------------------------------------------- * Using This Class: @@ -282,7 +284,9 @@ export class CategoryTemplate { .filter(t => t.type === 'schedule') .forEach(t => { if (!scheduleNames.includes(t.name.trim())) { - throw new Error(`Schedule ${t.name.trim()} does not exist`); + throw new Error(t('Schedule {{name}} does not exist', { + name: t.name.trim() + })); } }); //find lowest priority @@ -296,8 +300,9 @@ export class CategoryTemplate { .filter(t => t.type === 'schedule' || t.type === 'by') .forEach(t => { if (t.priority !== lowestPriority) { - throw new Error( - `Schedule and By templates must be the same priority level. Fix by setting all Schedule and By templates to priority level ${lowestPriority}`, + throw new Error(t( + 'Schedule and By templates must be the same priority level. Fix by setting all Schedule and By templates to priority level {{lowestPriority}}', + { lowestPriority: lowestPriority}), ); //t.priority = lowestPriority; } @@ -312,8 +317,8 @@ export class CategoryTemplate { ); if (range < 0 && !(t.repeat || t.annual)) { throw new Error( - `Target month has passed, remove or update the target month`, - ); + t('Target month has passed, remove or update the target month'), + ); } }); } @@ -333,7 +338,9 @@ export class CategoryTemplate { //skip the name check since these are special } else if (!availNames.includes(n)) { throw new Error( - `Category \x22${n}\x22 is not found in available income categories`, + t('Category \x22{{name}}\x22 is not found in available income categories', { + name: n + }), ); } }); @@ -343,7 +350,7 @@ export class CategoryTemplate { for (const t of this.templates) { if (!t.limit) continue; if (this.limitCheck) { - throw new Error('Only one `up to` allowed per category'); + throw new Error(t('Only one `up to` allowed per category')); } else if (t.limit) { if (t.limit.period === 'daily') { const numDays = monthUtils.differenceInCalendarDays( @@ -364,7 +371,7 @@ export class CategoryTemplate { } else if (t.limit.period === 'monthly') { this.limitAmount = amountToInteger(t.limit.amount); } else { - throw new Error('Invalid limit period. Check template syntax'); + throw new Error(t('Invalid limit period. Check template syntax')); } //amount is good save the rest this.limitCheck = true; @@ -376,13 +383,13 @@ export class CategoryTemplate { private checkSpend() { const st = this.templates.filter(t => t.type === 'spend'); if (st.length > 1) { - throw new Error('Only one spend template is allowed per category'); + throw new Error(t('Only one spend template is allowed per category')); } } private checkGoal() { if (this.goals.length > 1) { - throw new Error(`Only one #goal is allowed per category`); + throw new Error(t('Only one #goal is allowed per category')); } } diff --git a/packages/loot-core/src/server/budget/cleanup-template.ts b/packages/loot-core/src/server/budget/cleanup-template.ts index 8c1b109212c..e1dcf90877f 100644 --- a/packages/loot-core/src/server/budget/cleanup-template.ts +++ b/packages/loot-core/src/server/budget/cleanup-template.ts @@ -5,6 +5,7 @@ import * as db from '../db'; import { setBudget, getSheetValue, setGoal } from './actions'; import { parse } from './cleanup-template.pegjs'; +import { useTranslation } from 'react-i18next'; export function cleanupTemplate({ month }: { month: string }) { return processCleanup(month); @@ -113,7 +114,8 @@ async function applyGroupCleanups( }); } } else { - warnings.push(groupName + ' has no matching sink categories.'); + warnings.push(t('{{groupname}} has no matching sink categories.', + { groupname: groupName })); } sourceGroups = sourceGroups.filter(c => c.group !== groupName); groupLength = sourceGroups.length; @@ -218,7 +220,8 @@ async function processCleanup(month: string): Promise { }); num_sources += 1; } else { - warnings.push(category.name + ' does not have available funds.'); + warnings.push(t('{{name}} does not have available funds.', + { name: category.name })); } const carryover = await db.first( `SELECT carryover FROM zero_budgets WHERE month = ? and category = ?`, @@ -285,7 +288,7 @@ async function processCleanup(month: string): Promise { const budgetAvailable = await getSheetValue(sheetName, `to-budget`); if (budgetAvailable <= 0) { - warnings.push('Global: No funds are available to reallocate.'); + warnings.push(t('Global: No funds are available to reallocate.')); } //fill sinking categories @@ -320,35 +323,36 @@ async function processCleanup(month: string): Promise { return { type: 'error', sticky: true, - message: 'There were errors interpreting some templates:', + message: t('There were errors interpreting some templates:'), pre: errors.join('\n\n'), }; } else if (warnings.length) { return { type: 'warning', - message: 'Global: Funds not available:', + message: t('Global: Funds not available:'), pre: warnings.join('\n\n'), }; } else { return { type: 'message', - message: 'All categories were up to date.', + message: t('All categories were up to date.'), }; } } else { - const applied = `Successfully returned funds from ${num_sources} ${ - num_sources === 1 ? 'source' : 'sources' - } and funded ${num_sinks} sinking ${num_sinks === 1 ? 'fund' : 'funds'}.`; + const applied = t('Successfully returned funds from {{count}} sources ', + { count: num_sources }) + + t(' and funded {{count}} sinking funds', { count: num_sinks }); if (errors.length) { return { sticky: true, - message: `${applied} There were errors interpreting some templates:`, + message: applied + ' ' + + t('There were errors interpreting some templates:'), pre: errors.join('\n\n'), }; } else if (warnings.length) { return { type: 'warning', - message: 'Global: Funds not available:', + message: t('Global: Funds not available:'), pre: warnings.join('\n\n'), }; } else { diff --git a/packages/loot-core/src/server/budget/goalsSchedule.ts b/packages/loot-core/src/server/budget/goalsSchedule.ts index 87a84485c7e..bce2ae21ff8 100644 --- a/packages/loot-core/src/server/budget/goalsSchedule.ts +++ b/packages/loot-core/src/server/budget/goalsSchedule.ts @@ -10,6 +10,8 @@ import { import { isReflectBudget } from './actions'; +import { useTranslation } from 'react-i18next'; + async function createScheduleList(template, current_month, category) { const t = []; const errors = []; @@ -62,7 +64,8 @@ async function createScheduleList(template, current_month, category) { ); if (num_months < 0) { //non-repeating schedules could be negative - errors.push(`Schedule ${template[ll].name} is in the Past.`); + errors.push(t('Schedule {{name}} is in the past.', { + name: template[ll].name })); } else { t.push({ target, @@ -126,7 +129,8 @@ async function createScheduleList(template, current_month, category) { } } else { errors.push( - `Schedule ${t[ll].name} is not active during the month in question.`, + t('Schedule {{name}} is not active during the month in question.', { + name: t[ll].name }), ); } } diff --git a/packages/loot-core/src/server/budget/goaltemplates.ts b/packages/loot-core/src/server/budget/goaltemplates.ts index f0509ccbb2a..3cafa2c5ed7 100644 --- a/packages/loot-core/src/server/budget/goaltemplates.ts +++ b/packages/loot-core/src/server/budget/goaltemplates.ts @@ -8,6 +8,8 @@ import { isReflectBudget, getSheetValue, setGoal, setBudget } from './actions'; import { CategoryTemplate } from './categoryTemplate'; import { checkTemplates, storeTemplates } from './template-notes'; +import { useTranslation } from 'react-i18next'; + export async function applyTemplate({ month }): Promise { await storeTemplates(); const categoryTemplates = await getTemplates(null); @@ -188,13 +190,13 @@ async function processTemplate( if (catObjects.length === 0 && errors.length === 0) { return { type: 'message', - message: 'Everything is up to date', + message: t('Everything is up to date'), }; } if (errors.length > 0) { return { sticky: true, - message: 'There were errors interpreting some templates:', + message: t('There were errors interpreting some templates:'), pre: errors.join(`\n\n`), }; } @@ -245,6 +247,7 @@ async function processTemplate( return { type: 'message', - message: `Successfully applied templates to ${catObjects.length} categories`, + message: t('Successfully applied templates to {{count}} categories', { + count: catObjects.length }), }; } diff --git a/packages/loot-core/src/server/budget/template-notes.ts b/packages/loot-core/src/server/budget/template-notes.ts index 4141ec3dc07..76019c5473d 100644 --- a/packages/loot-core/src/server/budget/template-notes.ts +++ b/packages/loot-core/src/server/budget/template-notes.ts @@ -10,6 +10,8 @@ import { } from './statements'; import { Template } from './types/templates'; +import { useTranslation } from 'react-i18next'; + export const TEMPLATE_PREFIX = '#template'; export const GOAL_PREFIX = '#goal'; @@ -56,14 +58,14 @@ export async function checkTemplates(): Promise { if (errors.length) { return { sticky: true, - message: 'There were errors interpreting some templates:', + message: t('There were errors interpreting some templates:'), pre: errors.join('\n\n'), }; } return { type: 'message', - message: 'All templates passed! 🎉', + message: t('All templates passed!') + ' 🎉', }; }