Give a new account a first run, and drop to three tabs

Registering used to land you on a bare form asking for a child, with no hint
what came next. A short wizard now walks through the child, whether you are
planning alone or together, and where the day balances come from: fetched from
Försäkringskassan, entered by hand, or skipped — and if skipped, the figures
carry a badge saying they are estimates until real ones arrive.

Fetching is the first option rather than a hidden one, so the scraper is on by
default and the script that used to force it on no longer needs to.

Planning and Tips lose their tabs. Authoring moved to the calendar and the
figures moved to the overview, so both pages were only a second way to reach
what is already on screen; their routes now redirect rather than dead-end. The
dead translations went with them.

Dates also follow the chosen language now instead of always formatting as
Swedish.
This commit is contained in:
2026-09-17 22:05:54 +02:00
parent 064bcc2b3a
commit a4fce16dd1
40 changed files with 1060 additions and 4986 deletions
-2
View File
@@ -16,8 +16,6 @@ export const common = {
'nav.logout': 'Log out',
'nav.logoutAria': 'Log out',
'nav.overview': 'Overview',
'nav.plan': 'Plan',
'nav.selectChildAria': 'Select child',
'nav.settings': 'Settings',
'nav.tips': 'Tips',
} as const
+2 -9
View File
@@ -2,25 +2,18 @@ export const dashboard = {
'age4Bars.expiringStat': '{expiring} days expiring',
'age4Bars.savableStat': '{left} of {cap} days can be saved',
'age4Bars.title': 'Days left at age 4, per parent',
'dashboard.addChildButton': 'Add child',
'dashboard.addChildIntro': 'You have no children registered yet. Add your first child to get started.',
'dashboard.addChildTitle': 'Add child',
'dashboard.addingChild': 'Adding...',
'dashboard.birthdayLabel': 'Birthday',
'dashboard.childNameLabel': 'Child\'s name',
'dashboard.createChildError': 'Could not create child',
'dashboard.daysLeft': 'Days left',
'dashboard.enterManually': 'Enter details manually',
'dashboard.estimatedBadge': 'Estimated',
'dashboard.estimatedBadgeCaption': 'No real balances have been fetched yet — these figures use the statutory assumption (480 days, none used). Fetch from Försäkringskassan or enter details manually for exact figures.',
'dashboard.expiringAtB4': 'Expiring at age 4 if the current plan is followed (all parents)',
'dashboard.expiringAtB4ForParent': 'Expiring at age 4 if the current plan is followed – {name}',
'dashboard.expiringAtB4ParentFallback': 'Parent {number}',
'dashboard.fetchFromFk': 'Fetch from Försäkringskassan',
'dashboard.fillAllFields': 'Fill in all fields',
'dashboard.hero.hasPlanMessage': '{atRisk} days are at risk of disappearing when {childName} turns 4 ({date}).',
'dashboard.hero.noPlanMessage': 'You have {days} days to plan. Without a plan, {atRisk} of them will disappear when {childName} turns 4.',
'dashboard.loading': 'Loading...',
'dashboard.moreDetailsSummary': 'More details',
'dashboard.namePlaceholder': 'Name',
'dashboard.overviewTitle': 'Overview',
'dashboard.planFold.noWishesHint': 'Add wishes under Planning to see a proposal here.',
'dashboard.planFold.summary': 'Plan proposal for the years ahead',
+2
View File
@@ -3,6 +3,7 @@ import { common } from './common'
import { auth } from './auth'
import { calendar } from './calendar'
import { dashboard } from './dashboard'
import { onboarding } from './onboarding'
import { settings } from './settings'
import { plan } from './plan'
import { wishEditor } from './wishEditor'
@@ -26,6 +27,7 @@ export const en = {
...auth,
...calendar,
...dashboard,
...onboarding,
...settings,
...plan,
...wishEditor,
+29
View File
@@ -0,0 +1,29 @@
export const onboarding = {
'onboarding.addChildButton': 'Add child',
'onboarding.addingChild': 'Adding...',
'onboarding.balancesLegend': 'Parental-leave days',
'onboarding.birthdayLabel': 'Birthday',
'onboarding.childNameLabel': 'Child\'s name',
'onboarding.continueButton': 'Continue',
'onboarding.createChildError': 'Could not create child',
'onboarding.familyIntro': 'This controls how the planner splits parental-leave days.',
'onboarding.familyTitle': 'Are you planning alone or together with a partner?',
'onboarding.fillAllFields': 'Fill in all fields',
'onboarding.finishError': 'Something went wrong. Please try again.',
'onboarding.manualFetchDescription': 'Enter your balances manually if you already know them.',
'onboarding.manualFetchTitle': 'Enter manually',
'onboarding.namePlaceholder': 'Name',
'onboarding.partnerAddedStatus': 'Partner added: {name}',
'onboarding.partnerIntro': 'Add your partner now, or do it later under Settings.',
'onboarding.partnerNameLabel': 'Partner\'s name',
'onboarding.savingFamilyError': 'Could not save the planning mode',
'onboarding.scrapeFetchDescription': 'Fastest and most accurate — fetches your exact balances automatically via BankID.',
'onboarding.scrapeFetchTitle': 'Fetch from Försäkringskassan',
'onboarding.skipButton': 'Skip for now',
'onboarding.skipHint': 'We\'ll use the statutory assumption (480 days, none used) until you enter your own figures.',
'onboarding.stepBalancesIntro': 'This tells us how many days are left to plan. You can always update this later.',
'onboarding.stepBalancesTitle': 'Fetch your parental-leave days',
'onboarding.stepChildIntro': 'Add your first child to start planning your parental leave.',
'onboarding.stepChildTitle': 'Welcome! Let\'s add your child',
'onboarding.stepIndicator': 'Step {step} of {total}',
} as const
+8 -74
View File
@@ -1,56 +1,18 @@
// `plan.*` (the standalone Plan tab) and most `planResults.*` keys were
// retired with the Plan/Tips tabs (leave is now authored on the calendar and
// Översikt carries the headline figures — see PLANNING_ENGINE_SPEC.md's
// superseding note). What's left here is the subset `planResults.*`
// `PlanFold.tsx` (the read-only plan-proposal fold on Översikt) and
// `frontend/src/utils/rejectionMessages.ts` still render, plus
// `planningModeSelector.*` (`PlanningModeSelector.tsx`, used by both
// Settings and Onboarding).
export const plan = {
'plan.addAtLeastOneWish': 'Add at least one wish above to suggest a plan.',
'plan.addWishError': 'Could not add wish',
'plan.calculating': 'Calculating...',
'plan.intro': 'Describe how you want to be on leave — for example 6 weeks every summer and 4 weeks every Christmas — and get a suggested plan showing exactly which days are already free, which cost a vacation day, and which cost a parental-benefit day.',
'plan.loading': 'Loading planning...',
'plan.proposeButton': 'Suggest plan',
'plan.removeWishError': 'Could not remove wish',
'plan.title': 'Planning',
'plan.updateWishError': 'Could not save wish',
'plan.wishesTitle': 'Your wishes',
'planResults.age4ProjectionsIntro': 'Here\'s what the 4th birthday looks like if this proposal is applied, including future recurring wishes. Your saved plan (under Overview) may show a different result.',
'planResults.age4ProjectionsTitle': 'At the child\'s 4th birthday, if this proposal is applied',
'planResults.applyButton': 'Use plan',
'planResults.applySavedPrefix': 'The plan has been saved!',
'planResults.applyingButton': 'Saving...',
'planResults.balancesTitle': 'Remaining balances after the plan',
'planResults.cancelButton': 'Cancel',
'planResults.collisionDateHeader': 'Date',
'planResults.collisionExistingHeader': 'Existing type',
'planResults.collisionProposedHeader': 'Proposed type',
'planResults.confirmOverwriteButton': 'Yes, replace the existing days',
'planResults.confirmOverwriteIntro': 'The following days are already planned and will be replaced:',
'planResults.confirmOverwriteTitle': 'Conflict with existing days',
'planResults.createdClause': '{count} new {dayWord} created',
'planResults.daySingular': 'day',
'planResults.dayTypeFree': 'free days (weekend/public holiday)',
'planResults.dayTypeLagsta': 'parental-benefit days (lägstanivå, minimum rate)',
'planResults.dayTypeSjukpenning': 'parental-benefit days (sjukpenningnivå, income-based)',
'planResults.dayTypeVacation': 'vacation days',
'planResults.daysPlural': 'days',
'planResults.detailTitle': 'Leave periods in detail',
'planResults.dubbeldagarLeftHeader': 'Double days left',
'planResults.expiringAtB4Header': 'Expiring',
'planResults.explanationDetailFree': '{free_days} fully free days (weekend/public holiday)',
'planResults.explanationDetailLagsta': '{lagsta_days} parental-benefit days (lägstanivå, minimum rate)',
'planResults.explanationDetailSjukpenning': '{sjukpenning_days} parental-benefit days (sjukpenningnivå, income-based)',
'planResults.explanationDetailVacationAndFp': 'you keep your salary for {vacation_days} days and save {fp_days} parental-benefit days',
'planResults.explanationDetailVacationOnly': '{vacation_days} vacation days',
'planResults.explanationShortfallSuffix': ' ({shortfall_days} days short)',
'planResults.explanationSummary': '{wish_name} {year} — {weeks_wished} weeks wished, {weeks_funded} weeks funded',
'planResults.fourthBirthdayHeader': '4th birthday',
'planResults.freeDaySingular': 'free day',
'planResults.freeDaysPlural': 'free days',
'planResults.fullyFunded': 'Fully funded',
'planResults.funded': 'funded',
'planResults.lagstaLeftHeader': 'Lägstanivå days left',
'planResults.leavesHeader': 'Leave periods',
'planResults.noWindowsInHorizon': 'No windows within the planning horizon.',
'planResults.parentHeader': 'Parent',
'planResults.parentalDaysTitle': 'Parental-benefit days',
'planResults.previewBanner': 'This is a suggestion — nothing is saved automatically. You can adjust your wishes above and suggest a new plan as many times as you like.',
'planResults.projectedLeftHeader': 'Projected left',
'planResults.rejectionCollisionsNeedReplace': '{count} day(s) already planned differently; pass replace=true to overwrite',
'planResults.rejectionConflictingDuplicate': 'Conflicting duplicate entries for {date} (child {child_id}, parent {parent_name}) in the same batch',
'planResults.rejectionDubbeldagBatchQuotaExceeded': 'This batch would result in {projected_total} double days, the max is {max_dubbeldagar}',
@@ -61,23 +23,7 @@ export const plan = {
'planResults.rejectionInsufficientVacationBalance': '{parent_name} would need {needed} more vacation day(s) in {year}, but only {remaining} remain',
'planResults.rejectionInvalidDayType': 'Day {index} ({date}): invalid day type "{day_type}"',
'planResults.rejectionOutsideEligibleWindow': '{date}: outside the child\'s eligible window ({birth_date} – {hard_expiry})',
'planResults.rejectionsClause': '. Rejected: {rejections}',
'planResults.remedyCarryVacationForward': '{spare_total} vacation days from previous years can be carried forward',
'planResults.remedyLowerPriorityWish': 'Lowering {wish_name} from {from_weeks} to {to_weeks} weeks frees up {freed_days} days',
'planResults.remedyReducePreschoolReserve': 'Reducing the preschool planning-day reserve ({reserved} reserved) frees up {reserved} days',
'planResults.remedyShiftWindowIntoFreeDays': 'Moving {wish_name} closer to a weekend frees up {shift_gain} days',
'planResults.replacedClause': ', {count} {dayWord} replaced',
'planResults.savableAfterB4Header': 'Savable',
'planResults.shortfallCouldNotBeFunded': '{days} {dayWord} could not be funded.',
'planResults.shortfallLabel': 'Shortfall:',
'planResults.sjukpenningLeftHeader': 'Sjukpenningnivå days left',
'planResults.skippedFreeClause': ', {count} {dayWord} skipped',
'planResults.skippedIdenticalClause': ', {count} {dayWord} skipped (already identical)',
'planResults.strategyDubbeldag': 'Double days — both parents home at the same time',
'planResults.strategyParentSwap': 'Parents alternate',
'planResults.strategyPrefix': 'Strategy:',
'planResults.strategySolo': 'One parent home',
'planResults.strategyUnplaced': 'Could not be placed — no free slot was found',
'planResults.surplusAdviceBodyFrontLoadEarlyYears': '{parent_name} has a large surplus ({days_involved} parental-benefit days) — take more days now while the child is still small and you are home anyway, rather than spreading them thinly until the 4th birthday.',
'planResults.surplusAdviceBodyShiftMixToFp': '{parent_name} could swap {days_involved} vacation day(s) for parental-benefit days in this plan — the parental-benefit days would otherwise be forfeited at the 4th birthday, while the vacation days would not.',
'planResults.surplusAdviceBodyUseLessVacation': '{parent_name} could take {days_involved} fewer vacation day(s) ({years_text}) without going below the statutory minimum ({min_vacation_days_per_year} days/year) — save them instead to cover parental-benefit days that would otherwise be forfeited.',
@@ -91,19 +37,7 @@ export const plan = {
'planResults.totalAvailableHeader': 'Total available',
'planResults.unknownParent': 'Unknown parent',
'planResults.usedHeader': 'Used',
'planResults.vacationAssumedBadge': 'Assumed',
'planResults.vacationAssumedPrompt': 'No value is recorded for this year — the statutory 25 days has been assumed. Confirm or edit it in Settings.',
'planResults.vacationDaysTitle': 'Vacation days',
'planResults.warningBodyDaysLostAtAge4': '{parent_name} risks losing {expiring_days} days at the child\'s 4th birthday ({fourth_birthday}) unless more days are used before then.',
'planResults.warningBodySgiRisk': '{parent_name} risks their SGI protection for the week starting {week_start} ({days_planned} day(s) planned, at least {min_days_per_week} required).',
'planResults.warningBodyVacationExpiring': '{parent_name} has {saved_days_in} saved vacation day(s) from {year} at risk of being forfeited.',
'planResults.warningDaysLostAtAge4': 'Days at risk of being lost at age 4',
'planResults.warningSgiRisk': 'Risk to SGI protection',
'planResults.warningVacationExpiring': 'Vacation days at risk of being lost',
'planResults.warningsTitle': 'Warnings',
'planResults.weekSingular': 'week',
'planResults.weeksUnit': 'weeks',
'planResults.wished': 'wished',
'planResults.yearHeader': 'Year',
'planResults.yearOverviewTitle': 'Overview by year',
'planningModeSelector.couple': 'Me and my partner',
-2
View File
@@ -23,7 +23,5 @@ export const tips = {
'tip.wrapRedDaysWithVacation.title': 'Make the most of public holidays',
'tip.wrapWeekendWithVacation.body': '{parent_name} has parental-benefit leave planned on Friday {friday} and Monday {monday}. A vacation day in between makes for a longer break.',
'tip.wrapWeekendWithVacation.title': 'Make the most of the weekend',
'tips.empty': 'No tips yet. Great job planning your parental leave!',
'tips.loading': 'Loading tips...',
'tips.title': 'Optimization Tips',
} as const
+5 -44
View File
@@ -1,57 +1,18 @@
// The `WishEditor.tsx`/`Plan.tsx` flow it was written for is gone (leave is
// now authored directly on the calendar). What's left here is the subset of
// keys `PeriodPopover.tsx` and `PresetRail.tsx` (both `components/calendar/*`,
// owned by another task) still render for the calendar's own flexible/season
// wish authoring — see those files before removing anything further.
export const wishEditor = {
'wishEditor.adjustBound': 'Adjust search range',
'wishEditor.adjustDates': 'Adjust dates',
'wishEditor.bothParents': 'Both parents',
'wishEditor.boundEndsLabel': 'to',
'wishEditor.boundStartsLabel': 'Search range from',
'wishEditor.boundTooShortError': 'The search range is shorter than the {weeks} weeks wished — choose a longer range or fewer weeks.',
'wishEditor.customFlexButton': '+ Custom search range',
'wishEditor.customFlexName': 'Custom search range',
'wishEditor.customLeaveButton': '+ Custom leave',
'wishEditor.customLeaveName': 'Custom leave',
'wishEditor.eitherParent': 'Either parent',
'wishEditor.emptyState': 'No leave wishes yet. Choose a suggestion above to get started.',
'wishEditor.endsLabel': 'Ends',
'wishEditor.firstYearLabel': 'First year',
'wishEditor.flexBadge': 'whenever it fits best',
'wishEditor.flexPresetsLabel': 'Quick picks — search within a season ("whenever it fits best"):',
'wishEditor.hideDates': 'Hide dates',
'wishEditor.lastYearLabel': 'Last year',
'wishEditor.modeFixed': 'Fixed dates',
'wishEditor.modeFlexible': 'Whenever it fits best',
'wishEditor.modeFlexibleExplainer': 'The engine picks the best week inside the search range below by itself — it looks for the period with the most röda dagar (public holidays) and klämdagar (bridge days), since that costs the fewest semesterdagar (vacation days). A recurring wish is re-picked every year (Easter moves, for example), and the suggestion always needs confirming in the Plan tab before it is saved, just like any other plan.',
'wishEditor.modeSectionLabel': 'How should this wish be placed?',
'wishEditor.month1': 'January',
'wishEditor.month10': 'October',
'wishEditor.month11': 'November',
'wishEditor.month12': 'December',
'wishEditor.month2': 'February',
'wishEditor.month3': 'March',
'wishEditor.month4': 'April',
'wishEditor.month5': 'May',
'wishEditor.month6': 'June',
'wishEditor.month7': 'July',
'wishEditor.month8': 'August',
'wishEditor.month9': 'September',
'wishEditor.nameAria': 'Name for {name}',
'wishEditor.parentLabel': 'Parent',
'wishEditor.presetHostlov': 'Autumn break (höstlov)',
'wishEditor.presetJullov': 'Christmas break',
'wishEditor.presetSommar': 'Summer',
'wishEditor.priorityLabel': 'Priority',
'wishEditor.recurEveryYear': 'Repeats every year',
'wishEditor.removeButton': 'Remove',
'wishEditor.retryButton': 'Try again',
'wishEditor.saveFailedStatus': 'Could not save — your edit is kept, try again.',
'wishEditor.savedStatus': 'Saved',
'wishEditor.seasonHost': 'Autumn',
'wishEditor.seasonJul': 'Christmas',
'wishEditor.seasonSommar': 'Summer',
'wishEditor.seasonVar': 'Spring',
'wishEditor.startsLabel': 'Starts',
'wishEditor.untilAge4': 'Until the child turns 4',
'wishEditor.weekUnit': 'week',
'wishEditor.weeksLabel': 'Weeks',
'wishEditor.weeksUnit': 'weeks',
'wishEditor.wrapsNote': 'Extends into the next year.',
} as const