@jjlmoya/utils-babies 1.14.0 → 1.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/package.json +2 -2
  2. package/src/category/index.ts +3 -0
  3. package/src/entries.ts +4 -1
  4. package/src/index.ts +1 -0
  5. package/src/pages/[locale]/[slug].astro +31 -39
  6. package/src/tests/locale_completeness.test.ts +2 -2
  7. package/src/tests/shared-test-helpers.ts +56 -0
  8. package/src/tests/tool_exports.test.ts +34 -0
  9. package/src/tests/tool_validation.test.ts +2 -2
  10. package/src/tool/baby-budget-planner/baby-budget-planner.css +405 -0
  11. package/src/tool/baby-budget-planner/bibliography.astro +14 -0
  12. package/src/tool/baby-budget-planner/bibliography.ts +14 -0
  13. package/src/tool/baby-budget-planner/component.astro +406 -0
  14. package/src/tool/baby-budget-planner/entry.ts +77 -0
  15. package/src/tool/baby-budget-planner/i18n/de.ts +182 -0
  16. package/src/tool/baby-budget-planner/i18n/en.ts +184 -0
  17. package/src/tool/baby-budget-planner/i18n/es.ts +183 -0
  18. package/src/tool/baby-budget-planner/i18n/fr.ts +183 -0
  19. package/src/tool/baby-budget-planner/i18n/id.ts +183 -0
  20. package/src/tool/baby-budget-planner/i18n/it.ts +183 -0
  21. package/src/tool/baby-budget-planner/i18n/ja.ts +183 -0
  22. package/src/tool/baby-budget-planner/i18n/ko.ts +183 -0
  23. package/src/tool/baby-budget-planner/i18n/nl.ts +183 -0
  24. package/src/tool/baby-budget-planner/i18n/pl.ts +183 -0
  25. package/src/tool/baby-budget-planner/i18n/pt.ts +183 -0
  26. package/src/tool/baby-budget-planner/i18n/ru.ts +183 -0
  27. package/src/tool/baby-budget-planner/i18n/sv.ts +183 -0
  28. package/src/tool/baby-budget-planner/i18n/tr.ts +183 -0
  29. package/src/tool/baby-budget-planner/i18n/zh.ts +183 -0
  30. package/src/tool/baby-budget-planner/index.ts +10 -0
  31. package/src/tool/baby-budget-planner/logic.ts +68 -0
  32. package/src/tool/baby-budget-planner/seo.astro +15 -0
  33. package/src/tool/baby-feeding-calculator/bibliography.astro +9 -6
  34. package/src/tool/baby-feeding-calculator/seo.astro +2 -45
  35. package/src/tool/baby-percentile-calculator/seo.astro +7 -28
  36. package/src/tool/baby-size-converter/seo.astro +7 -28
  37. package/src/tool/fertile-days-estimator/seo.astro +7 -28
  38. package/src/tool/pregnancy-calculator/seo.astro +7 -28
  39. package/src/tool/vaccination-calendar/seo.astro +7 -28
  40. package/src/tools.ts +2 -0
@@ -0,0 +1,14 @@
1
+ ---
2
+ import { Bibliography as BibliographyUI } from "@jjlmoya/utils-shared";
3
+ import { babyBudgetPlanner } from "./index";
4
+ import type { KnownLocale } from "../../types";
5
+
6
+ interface Props {
7
+ locale?: KnownLocale;
8
+ }
9
+ const { locale = "es" } = Astro.props;
10
+ const content = await babyBudgetPlanner.i18n[locale]?.();
11
+ if (!content) return null;
12
+ ---
13
+
14
+ <BibliographyUI links={content.bibliography} />
@@ -0,0 +1,14 @@
1
+ export const bibliography = [
2
+ {
3
+ name: 'Financial Planning for New Parents - Investopedia',
4
+ url: 'https://www.investopedia.com/articles/pf/08/financial-planning-baby.asp',
5
+ },
6
+ {
7
+ name: 'Average Cost of Raising a Child - USDA Report',
8
+ url: 'https://www.usda.gov/media/blog/2017/01/13/cost-raising-child',
9
+ },
10
+ {
11
+ name: 'Baby Cost Calculator - BabyCenter',
12
+ url: 'https://www.babycenter.com/baby-cost-calculator',
13
+ },
14
+ ];
@@ -0,0 +1,406 @@
1
+ ---
2
+ import type { BabyBudgetPlannerUI } from './entry';
3
+ import { DEFAULT_ITEMS } from './logic';
4
+ interface Props { ui: BabyBudgetPlannerUI; }
5
+ const { ui } = Astro.props;
6
+ ---
7
+ <div id="bbp-root" class="bbp-card" data-ui={JSON.stringify(ui)} data-defaults={JSON.stringify(DEFAULT_ITEMS)}>
8
+ <div class="bbp-main">
9
+ <div class="bbp-left">
10
+ <div id="bbp-onboarding-view">
11
+ <span class="bbp-section-marker">{ui.onboardingTitle}</span>
12
+
13
+ <div class="bbp-group">
14
+ <label class="bbp-label">{ui.lifestyleLabel}</label>
15
+ <div class="bbp-options-grid" data-key="lifestyle">
16
+ <button class="bbp-choice-btn active" data-value="saving">{ui.lifestyleSaving}</button>
17
+ <button class="bbp-choice-btn" data-value="balanced">{ui.lifestyleBalanced}</button>
18
+ <button class="bbp-choice-btn" data-value="premium">{ui.lifestylePremium}</button>
19
+ </div>
20
+ </div>
21
+
22
+ <div class="bbp-group">
23
+ <label class="bbp-label">{ui.feedingLabel}</label>
24
+ <div class="bbp-options-grid" data-key="feeding">
25
+ <button class="bbp-choice-btn active" data-value="breast">{ui.feedingBreast}</button>
26
+ <button class="bbp-choice-btn" data-value="formula">{ui.feedingFormula}</button>
27
+ <button class="bbp-choice-btn" data-value="mixed">{ui.feedingMixed}</button>
28
+ </div>
29
+ </div>
30
+
31
+ <div class="bbp-group">
32
+ <label class="bbp-label">{ui.childcareLabel}</label>
33
+ <div class="bbp-options-grid" data-key="childcare">
34
+ <button class="bbp-choice-btn active" data-value="home">{ui.childcareHome}</button>
35
+ <button class="bbp-choice-btn" data-value="public">{ui.childcarePublic}</button>
36
+ <button class="bbp-choice-btn" data-value="private">{ui.childcarePrivate}</button>
37
+ </div>
38
+ </div>
39
+
40
+ <button id="bbp-start-btn" class="bbp-cta-btn">{ui.startBtn}</button>
41
+ </div>
42
+
43
+ <div id="bbp-dashboard-view" class="bbp-hidden">
44
+ <div class="bbp-dashboard-section">
45
+ <span class="bbp-section-marker">{ui.categoryBeforeBirth}</span>
46
+ <div id="bbp-items-startup"></div>
47
+ </div>
48
+ <div class="bbp-dashboard-section">
49
+ <span class="bbp-section-marker">{ui.categoryMonthByMonth}</span>
50
+ <div id="bbp-items-recurring"></div>
51
+ </div>
52
+ </div>
53
+ </div>
54
+
55
+ <div class="bbp-right">
56
+ <div id="bbp-import-callout" class="bbp-import-promo">
57
+ <div class="bbp-promo-icon">
58
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4M7 10l5 5 5-5M12 15V3"/></svg>
59
+ </div>
60
+ <button id="bbp-import-cta" class="bbp-tool-btn primary-btn">{ui.importBtn}</button>
61
+ </div>
62
+
63
+ <div class="bbp-dashboard-only bbp-hidden bbp-right-col">
64
+ <div class="bbp-panel bbp-total-box">
65
+ <span class="bbp-stat-label">{ui.totalNeededLabel}</span>
66
+ <div class="bbp-total-val" id="bbp-total-needed">0 {ui.currencySymbol}</div>
67
+ <div class="bbp-progress-rail">
68
+ <div class="bbp-progress-thumb" id="bbp-progress-fill" style="width: 0%"></div>
69
+ </div>
70
+ <span class="bbp-stat-label" id="bbp-progress-text">0% {ui.savingsProgressLabel}</span>
71
+ </div>
72
+
73
+ <div class="bbp-panel">
74
+ <span class="bbp-section-marker">{ui.chartTitle}</span>
75
+ <div class="bbp-curve-wrapper">
76
+ <div class="bbp-curve-chart" id="bbp-curve-viz"></div>
77
+ <div class="bbp-curve-labels" id="bbp-curve-labels"></div>
78
+ </div>
79
+ </div>
80
+
81
+ <div class="bbp-panel bbp-ghost-list">
82
+ <span class="bbp-section-marker">{ui.ghostAlertsTitle}</span>
83
+ <div class="bbp-ghost-item">{ui.ghostInsurance}</div>
84
+ <div class="bbp-ghost-item">{ui.ghostHeating}</div>
85
+ <div class="bbp-ghost-item">{ui.ghostVaccines}</div>
86
+ </div>
87
+
88
+ <div class="bbp-panel bbp-diaper-tool">
89
+ <span class="bbp-section-marker">{ui.diaperCalcTitle}</span>
90
+ <div class="bbp-diaper-grid">
91
+ <div class="bbp-group">
92
+ <label class="bbp-label">{ui.diaperPriceLabel}</label>
93
+ <input type="number" id="bbp-diaper-price" class="bbp-input-fancy" />
94
+ </div>
95
+ <div class="bbp-group">
96
+ <label class="bbp-label">{ui.diaperUnitsLabel}</label>
97
+ <input type="number" id="bbp-diaper-units" class="bbp-input-fancy" />
98
+ </div>
99
+ </div>
100
+ <div id="bbp-diaper-res" class="bbp-diaper-result"></div>
101
+ </div>
102
+
103
+ <div class="bbp-action-grid">
104
+ <button id="bbp-export-btn" class="bbp-tool-btn">{ui.exportBtn}</button>
105
+ <button id="bbp-reset-btn" class="bbp-tool-btn">{ui.resetBtn}</button>
106
+ </div>
107
+ </div>
108
+ </div>
109
+ </div>
110
+
111
+ <input type="file" id="bbp-import-input" class="bbp-hidden" accept=".json" />
112
+ </div>
113
+
114
+ <script is:inline>
115
+ (function() {
116
+ const root = document.getElementById('bbp-root');
117
+ if (!root) return;
118
+ const ui = JSON.parse(root.dataset.ui);
119
+ const defaults = JSON.parse(root.dataset.defaults);
120
+
121
+ let state = {
122
+ lifestyle: 'saving',
123
+ feeding: 'breast',
124
+ childcare: 'home',
125
+ items: JSON.parse(JSON.stringify(defaults)),
126
+ diaperCalc: { price: 20, units: 80 },
127
+ isStarted: false
128
+ };
129
+
130
+ const storageKey = 'bbp_user_data';
131
+
132
+ function save() {
133
+ localStorage.setItem(storageKey, JSON.stringify(state));
134
+ render();
135
+ }
136
+
137
+ function load() {
138
+ const saved = localStorage.getItem(storageKey);
139
+ if (saved) {
140
+ state = JSON.parse(saved);
141
+ if (state.isStarted) {
142
+ root.classList.add('is-started');
143
+ document.getElementById('bbp-onboarding-view').classList.add('bbp-hidden');
144
+ document.getElementById('bbp-dashboard-view').classList.remove('bbp-hidden');
145
+ document.getElementById('bbp-import-callout').classList.add('bbp-hidden');
146
+ root.querySelectorAll('.bbp-dashboard-only').forEach(el => el.classList.remove('bbp-hidden'));
147
+
148
+ root.querySelectorAll('.bbp-options-grid').forEach(group => {
149
+ const val = state[group.dataset.key];
150
+ group.querySelectorAll('.bbp-choice-btn').forEach(btn => {
151
+ btn.classList.toggle('active', btn.dataset.value === val);
152
+ });
153
+ });
154
+ document.getElementById('bbp-diaper-price').value = state.diaperCalc.price;
155
+ document.getElementById('bbp-diaper-units').value = state.diaperCalc.units;
156
+ }
157
+ }
158
+ }
159
+
160
+ function render() {
161
+ const startupList = document.getElementById('bbp-items-startup');
162
+ const recurringList = document.getElementById('bbp-items-recurring');
163
+ if (!startupList || !recurringList) return;
164
+ startupList.innerHTML = '';
165
+ recurringList.innerHTML = '';
166
+
167
+ state.items.forEach((item, index) => {
168
+ const basePrice = item.basePrice[state.lifestyle] * ui.currencyMultiplier;
169
+ const price = item.userPrice !== undefined ? item.userPrice : basePrice;
170
+ const finalPrice = (item.isSecondHand || item.isWishlist) ? 0 : price;
171
+
172
+ const row = document.createElement('div');
173
+ row.className = 'bbp-item-row';
174
+ row.innerHTML = `
175
+ <div class="bbp-item-meta">
176
+ <span class="bbp-item-name">${ui[item.labelKey]}</span>
177
+ <div class="bbp-toggles">
178
+ <span class="bbp-toggle-pill sh ${item.isSecondHand ? 'checked' : ''}" data-idx="${index}" data-prop="isSecondHand">${ui.secondHandLabel}</span>
179
+ <span class="bbp-toggle-pill wl ${item.isWishlist ? 'checked' : ''}" data-idx="${index}" data-prop="isWishlist">${ui.wishlistLabel}</span>
180
+ </div>
181
+ </div>
182
+ <div class="bbp-price-area">
183
+ <span class="bbp-price-bubble ${finalPrice === 0 ? 'is-zero' : ''}" data-idx="${index}">${finalPrice} ${ui.currencySymbol}</span>
184
+ <input type="number" class="bbp-input-fancy bbp-hidden bbp-price-input" data-idx="${index}" value="${price}" />
185
+ </div>
186
+ `;
187
+
188
+ const bubble = row.querySelector('.bbp-price-bubble');
189
+ const input = row.querySelector('.bbp-price-input');
190
+
191
+ bubble.addEventListener('click', () => {
192
+ bubble.classList.add('bbp-hidden');
193
+ input.classList.remove('bbp-hidden');
194
+ input.focus();
195
+ });
196
+
197
+ input.addEventListener('blur', () => {
198
+ const val = parseFloat(input.value);
199
+ if (!isNaN(val)) {
200
+ state.items[index].userPrice = val;
201
+ save();
202
+ }
203
+ bubble.classList.remove('bbp-hidden');
204
+ input.classList.add('bbp-hidden');
205
+ });
206
+
207
+ input.addEventListener('keydown', (e) => {
208
+ if (e.key === 'Enter') input.blur();
209
+ });
210
+
211
+ row.querySelectorAll('.bbp-toggle-pill').forEach(pill => {
212
+ pill.addEventListener('click', () => {
213
+ const prop = pill.dataset.prop;
214
+ state.items[index][prop] = !state.items[index][prop];
215
+ save();
216
+ });
217
+ });
218
+
219
+ if (item.category === 'monthly') {
220
+ recurringList.appendChild(row);
221
+ } else {
222
+ startupList.appendChild(row);
223
+ }
224
+ });
225
+
226
+ let totalFirstYear = 0;
227
+ const monthExpenses = [];
228
+ for (let m = 0; m <= 12; m++) {
229
+ let mTotal = 0;
230
+ if (m === 0) {
231
+ state.items.filter(i => i.category === 'before').forEach(i => {
232
+ const basePrice = i.basePrice[state.lifestyle] * ui.currencyMultiplier;
233
+ if (!i.isSecondHand && !i.isWishlist) mTotal += (i.userPrice ?? basePrice);
234
+ });
235
+ }
236
+ if (m === 6) {
237
+ state.items.filter(i => i.category === 'milestones').forEach(i => {
238
+ const basePrice = i.basePrice[state.lifestyle] * ui.currencyMultiplier;
239
+ if (!i.isSecondHand && !i.isWishlist) mTotal += (i.userPrice ?? basePrice);
240
+ });
241
+ }
242
+ state.items.filter(i => i.category === 'monthly').forEach(i => {
243
+ if (i.id === 'daycare' && state.childcare === 'home') return;
244
+ const basePrice = i.basePrice[state.lifestyle] * ui.currencyMultiplier;
245
+ if (!i.isSecondHand && !i.isWishlist) mTotal += (i.userPrice ?? basePrice);
246
+ });
247
+
248
+ if (state.feeding === 'formula') mTotal += (state.lifestyle === 'premium' ? 100 : 60) * ui.currencyMultiplier;
249
+ if (state.feeding === 'mixed') mTotal += (state.lifestyle === 'premium' ? 50 : 30) * ui.currencyMultiplier;
250
+
251
+ monthExpenses.push(mTotal);
252
+ totalFirstYear += mTotal;
253
+ }
254
+
255
+ document.getElementById('bbp-total-needed').textContent = Math.round(totalFirstYear) + ' ' + ui.currencySymbol;
256
+
257
+ const itemsHandled = state.items.filter(i => i.isSecondHand || i.isWishlist).length;
258
+ const progress = Math.round((itemsHandled / state.items.length) * 100);
259
+ document.getElementById('bbp-progress-fill').style.width = progress + '%';
260
+ document.getElementById('bbp-progress-text').textContent = progress + '% ' + ui.savingsProgressLabel;
261
+
262
+ const curveViz = document.getElementById('bbp-curve-viz');
263
+ const curveLabels = document.getElementById('bbp-curve-labels');
264
+ if (curveViz && curveLabels) {
265
+ curveViz.innerHTML = '';
266
+ curveLabels.innerHTML = '';
267
+ const maxExp = Math.max(...monthExpenses, 1);
268
+ monthExpenses.forEach((exp, i) => {
269
+ const barWrap = document.createElement('div');
270
+ barWrap.className = 'bbp-bar-wrap';
271
+
272
+ const bar = document.createElement('div');
273
+ bar.className = 'bbp-bar';
274
+ bar.style.height = (exp / maxExp * 100) + '%';
275
+
276
+ const val = document.createElement('span');
277
+ val.className = 'bbp-bar-val';
278
+ val.textContent = Math.round(exp);
279
+
280
+ barWrap.appendChild(val);
281
+ barWrap.appendChild(bar);
282
+ curveViz.appendChild(barWrap);
283
+
284
+ const lbl = document.createElement('span');
285
+ lbl.className = 'bbp-bar-lbl';
286
+ lbl.textContent = i === 0 ? ui.initialMonthLabel : ui.monthPrefix + i;
287
+ curveLabels.appendChild(lbl);
288
+ });
289
+ }
290
+
291
+ const dp = state.diaperCalc.price;
292
+ const du = state.diaperCalc.units;
293
+ if (du > 0) {
294
+ const cpu = (dp / du).toFixed(2);
295
+ const monthly = (cpu * 200).toFixed(0);
296
+ document.getElementById('bbp-diaper-res').textContent = cpu + ' ' + ui.currencySymbol + '/u ~ ' + monthly + ' ' + ui.currencySymbol + ui.perMonthLabel;
297
+ }
298
+ }
299
+
300
+ root.querySelectorAll('.bbp-choice-btn').forEach(btn => {
301
+ btn.addEventListener('click', () => {
302
+ const group = btn.closest('[data-key]');
303
+ state[group.dataset.key] = btn.dataset.value;
304
+ group.querySelectorAll('.bbp-choice-btn').forEach(b => b.classList.remove('active'));
305
+ btn.classList.add('active');
306
+ if (state.isStarted) save();
307
+ });
308
+ });
309
+
310
+ document.getElementById('bbp-start-btn').addEventListener('click', () => {
311
+ state.isStarted = true;
312
+ root.classList.add('is-started');
313
+ document.getElementById('bbp-onboarding-view').classList.add('bbp-hidden');
314
+ document.getElementById('bbp-dashboard-view').classList.remove('bbp-hidden');
315
+ document.getElementById('bbp-import-callout').classList.add('bbp-hidden');
316
+ root.querySelectorAll('.bbp-dashboard-only').forEach(el => el.classList.remove('bbp-hidden'));
317
+ save();
318
+ });
319
+
320
+ let resetTimeout;
321
+ document.getElementById('bbp-reset-btn').addEventListener('click', (e) => {
322
+ const btn = e.target;
323
+ if (btn.dataset.confirm === 'true') {
324
+ localStorage.removeItem(storageKey);
325
+ state = {
326
+ lifestyle: 'saving',
327
+ feeding: 'breast',
328
+ childcare: 'home',
329
+ items: JSON.parse(JSON.stringify(defaults)),
330
+ diaperCalc: { price: 20, units: 80 },
331
+ isStarted: false
332
+ };
333
+ root.classList.remove('is-started');
334
+ document.getElementById('bbp-dashboard-view').classList.add('bbp-hidden');
335
+ document.getElementById('bbp-onboarding-view').classList.remove('bbp-hidden');
336
+ document.getElementById('bbp-import-callout').classList.remove('bbp-hidden');
337
+ root.querySelectorAll('.bbp-dashboard-only').forEach(el => el.classList.add('bbp-hidden'));
338
+
339
+ btn.textContent = ui.resetBtn;
340
+ delete btn.dataset.confirm;
341
+ render();
342
+ } else {
343
+ btn.dataset.confirm = 'true';
344
+ btn.textContent = ui.confirmReset;
345
+ clearTimeout(resetTimeout);
346
+ resetTimeout = setTimeout(() => {
347
+ btn.textContent = ui.resetBtn;
348
+ delete btn.dataset.confirm;
349
+ }, 3000);
350
+ }
351
+ });
352
+
353
+ document.getElementById('bbp-export-btn').addEventListener('click', () => {
354
+ const blob = new Blob([JSON.stringify(state)], { type: 'application/json' });
355
+ const url = URL.createObjectURL(blob);
356
+ const a = document.createElement('a');
357
+ a.href = url;
358
+ a.download = 'budget.json';
359
+ a.click();
360
+ });
361
+
362
+ const triggerImport = () => document.getElementById('bbp-import-input').click();
363
+ document.getElementById('bbp-import-cta').addEventListener('click', triggerImport);
364
+
365
+ document.getElementById('bbp-import-input').addEventListener('change', (e) => {
366
+ const file = e.target.files[0];
367
+ if (!file) return;
368
+ const reader = new FileReader();
369
+ reader.onload = (event) => {
370
+ try {
371
+ state = JSON.parse(event.target.result);
372
+ save();
373
+ if (state.isStarted) {
374
+ root.classList.add('is-started');
375
+ document.getElementById('bbp-onboarding-view').classList.add('bbp-hidden');
376
+ document.getElementById('bbp-dashboard-view').classList.remove('bbp-hidden');
377
+ document.getElementById('bbp-import-callout').classList.add('bbp-hidden');
378
+ root.querySelectorAll('.bbp-dashboard-only').forEach(el => el.classList.remove('bbp-hidden'));
379
+ } else {
380
+ root.classList.remove('is-started');
381
+ document.getElementById('bbp-onboarding-view').classList.remove('bbp-hidden');
382
+ document.getElementById('bbp-dashboard-view').classList.add('bbp-hidden');
383
+ document.getElementById('bbp-import-callout').classList.remove('bbp-hidden');
384
+ root.querySelectorAll('.bbp-dashboard-only').forEach(el => el.classList.add('bbp-hidden'));
385
+ }
386
+ render();
387
+ } catch {
388
+ alert('Error');
389
+ }
390
+ };
391
+ reader.readAsText(file);
392
+ });
393
+
394
+ document.getElementById('bbp-diaper-price').addEventListener('input', (e) => {
395
+ state.diaperCalc.price = parseFloat(e.target.value) || 0;
396
+ save();
397
+ });
398
+ document.getElementById('bbp-diaper-units').addEventListener('input', (e) => {
399
+ state.diaperCalc.units = parseFloat(e.target.value) || 0;
400
+ save();
401
+ });
402
+
403
+ load();
404
+ if (state.isStarted) render();
405
+ })();
406
+ </script>
@@ -0,0 +1,77 @@
1
+ export interface BabyBudgetPlannerUI {
2
+ title: string;
3
+ onboardingTitle: string;
4
+ lifestyleLabel: string;
5
+ lifestyleSaving: string;
6
+ lifestyleBalanced: string;
7
+ lifestylePremium: string;
8
+ feedingLabel: string;
9
+ feedingBreast: string;
10
+ feedingFormula: string;
11
+ feedingMixed: string;
12
+ childcareLabel: string;
13
+ childcareHome: string;
14
+ childcarePublic: string;
15
+ childcarePrivate: string;
16
+ startBtn: string;
17
+ categoryBeforeBirth: string;
18
+ categoryMonthByMonth: string;
19
+ categoryMilestones: string;
20
+ totalNeededLabel: string;
21
+ savingsProgressLabel: string;
22
+ chartTitle: string;
23
+ ghostAlertsTitle: string;
24
+ ghostInsurance: string;
25
+ ghostHeating: string;
26
+ ghostVaccines: string;
27
+ diaperCalcTitle: string;
28
+ diaperPriceLabel: string;
29
+ diaperUnitsLabel: string;
30
+ secondHandLabel: string;
31
+ wishlistLabel: string;
32
+ itemNursery: string;
33
+ itemStroller: string;
34
+ itemHospitalBag: string;
35
+ itemDiapers: string;
36
+ itemPharmacy: string;
37
+ itemDaycare: string;
38
+ itemSolidFood: string;
39
+ itemClothes: string;
40
+ itemHighChair: string;
41
+ editLabel: string;
42
+ saveBtn: string;
43
+ exportBtn: string;
44
+ importBtn: string;
45
+ resetBtn: string;
46
+ currencySymbol: string;
47
+ monthPrefix: string;
48
+ initialMonthLabel: string;
49
+ perMonthLabel: string;
50
+ confirmReset: string;
51
+ currencyMultiplier: number;
52
+ }
53
+
54
+ export const babyBudgetPlanner = {
55
+ id: 'baby-budget-planner',
56
+ icons: {
57
+ bg: 'mdi:finance',
58
+ fg: 'mdi:baby-face-outline',
59
+ },
60
+ i18n: {
61
+ de: async () => (await import('./i18n/de')).content,
62
+ en: async () => (await import('./i18n/en')).content,
63
+ es: async () => (await import('./i18n/es')).content,
64
+ fr: async () => (await import('./i18n/fr')).content,
65
+ id: async () => (await import('./i18n/id')).content,
66
+ it: async () => (await import('./i18n/it')).content,
67
+ ja: async () => (await import('./i18n/ja')).content,
68
+ ko: async () => (await import('./i18n/ko')).content,
69
+ nl: async () => (await import('./i18n/nl')).content,
70
+ pl: async () => (await import('./i18n/pl')).content,
71
+ pt: async () => (await import('./i18n/pt')).content,
72
+ ru: async () => (await import('./i18n/ru')).content,
73
+ sv: async () => (await import('./i18n/sv')).content,
74
+ tr: async () => (await import('./i18n/tr')).content,
75
+ zh: async () => (await import('./i18n/zh')).content,
76
+ },
77
+ };