@jjlmoya/utils-travel 1.1.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.
- package/package.json +60 -0
- package/src/category/i18n/en.ts +185 -0
- package/src/category/i18n/es.ts +187 -0
- package/src/category/i18n/fr.ts +100 -0
- package/src/category/index.ts +12 -0
- package/src/category/seo.astro +15 -0
- package/src/components/PreviewNavSidebar.astro +116 -0
- package/src/components/PreviewToolbar.astro +143 -0
- package/src/data.ts +15 -0
- package/src/env.d.ts +5 -0
- package/src/index.ts +22 -0
- package/src/layouts/PreviewLayout.astro +117 -0
- package/src/pages/[locale]/[slug].astro +146 -0
- package/src/pages/[locale].astro +278 -0
- package/src/pages/index.astro +4 -0
- package/src/tests/faq_count.test.ts +8 -0
- package/src/tests/locale_completeness.test.ts +21 -0
- package/src/tests/mocks/astro_mock.js +2 -0
- package/src/tests/no_h1_in_components.test.ts +8 -0
- package/src/tests/seo_length.test.ts +8 -0
- package/src/tests/tool_validation.test.ts +17 -0
- package/src/tool/luggage-calculator/bibliography.astro +14 -0
- package/src/tool/luggage-calculator/component.astro +560 -0
- package/src/tool/luggage-calculator/i18n/en.ts +617 -0
- package/src/tool/luggage-calculator/i18n/es.ts +617 -0
- package/src/tool/luggage-calculator/i18n/fr.ts +549 -0
- package/src/tool/luggage-calculator/index.ts +53 -0
- package/src/tool/luggage-calculator/seo.astro +14 -0
- package/src/tool/mini-adventures/bibliography.astro +14 -0
- package/src/tool/mini-adventures/component.astro +665 -0
- package/src/tool/mini-adventures/i18n/en.ts +161 -0
- package/src/tool/mini-adventures/i18n/es.ts +286 -0
- package/src/tool/mini-adventures/i18n/fr.ts +144 -0
- package/src/tool/mini-adventures/index.ts +70 -0
- package/src/tool/mini-adventures/seo.astro +14 -0
- package/src/tool/optimal-routes/bibliography.astro +14 -0
- package/src/tool/optimal-routes/component.astro +439 -0
- package/src/tool/optimal-routes/i18n/en.ts +42 -0
- package/src/tool/optimal-routes/i18n/es.ts +52 -0
- package/src/tool/optimal-routes/index.ts +63 -0
- package/src/tool/optimal-routes/lib/RouteManager.ts +181 -0
- package/src/tool/optimal-routes/seo.astro +14 -0
- package/src/tool/suitcase-checklist/bibliography.astro +14 -0
- package/src/tool/suitcase-checklist/component.astro +710 -0
- package/src/tool/suitcase-checklist/i18n/en.ts +261 -0
- package/src/tool/suitcase-checklist/i18n/es.ts +261 -0
- package/src/tool/suitcase-checklist/i18n/fr.ts +259 -0
- package/src/tool/suitcase-checklist/index.ts +75 -0
- package/src/tool/suitcase-checklist/seo.astro +14 -0
- package/src/tool/tip-calculator/bibliography.astro +14 -0
- package/src/tool/tip-calculator/component.astro +683 -0
- package/src/tool/tip-calculator/i18n/en.ts +264 -0
- package/src/tool/tip-calculator/i18n/es.ts +264 -0
- package/src/tool/tip-calculator/i18n/fr.ts +264 -0
- package/src/tool/tip-calculator/index.ts +53 -0
- package/src/tool/tip-calculator/seo.astro +14 -0
- package/src/tools.ts +13 -0
- package/src/types.ts +73 -0
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import type { WithContext, FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
|
|
2
|
+
import type { ToolLocaleContent } from '../../../types';
|
|
3
|
+
import type { SuitcaseChecklistUI } from '../index';
|
|
4
|
+
|
|
5
|
+
const slug = 'generateur-checklist-valise';
|
|
6
|
+
const title = 'Générateur de Checklist de Valise | Préparez vos bagages';
|
|
7
|
+
const description = 'L\'outil en ligne ultime pour organiser vos bagages en fonction de votre type de voyage, destination et durée. N\'oubliez plus jamais vos essentiels de voyage.';
|
|
8
|
+
|
|
9
|
+
const ui: SuitcaseChecklistUI = {
|
|
10
|
+
title: "Planificateur de Bagages",
|
|
11
|
+
form: {
|
|
12
|
+
destTitle: "Destination et Climat",
|
|
13
|
+
destTypes: [
|
|
14
|
+
{ id: "playa", name: "Plage / Côte", icon: "mdi:beach" },
|
|
15
|
+
{ id: "ciudad", name: "Ville / Urbain", icon: "mdi:city" },
|
|
16
|
+
{ id: "montana", name: "Montagne / Rural", icon: "mdi:pine-tree" },
|
|
17
|
+
{ id: "frio", name: "Climat Froid / Neige", icon: "mdi:snowflake" }
|
|
18
|
+
],
|
|
19
|
+
daysTitle: "Combien de jours ?",
|
|
20
|
+
intlTitle: "Voyage International ?",
|
|
21
|
+
intlLabel: "Oui, je traverse des frontières",
|
|
22
|
+
reasonTitle: "Motif du Voyage",
|
|
23
|
+
reasons: [
|
|
24
|
+
{ id: "ocio", name: "Loisir / Plaisir", icon: "mdi:party-popper" },
|
|
25
|
+
{ id: "negocios", name: "Affaires", icon: "mdi:tie" }
|
|
26
|
+
],
|
|
27
|
+
submitBtn: "Configurer ma Valise Idéale"
|
|
28
|
+
},
|
|
29
|
+
results: {
|
|
30
|
+
title: "Votre Liste de Voyage",
|
|
31
|
+
editBtn: "Modifier",
|
|
32
|
+
printBtn: "Enregistrer PDF",
|
|
33
|
+
tipsTitle: "Conseils pour votre Voyage",
|
|
34
|
+
progressText: "Complété"
|
|
35
|
+
},
|
|
36
|
+
itemsDb: {
|
|
37
|
+
essentials: {
|
|
38
|
+
id: "essentials",
|
|
39
|
+
title: "Documents et Base",
|
|
40
|
+
icon: "mdi:passport",
|
|
41
|
+
items: [
|
|
42
|
+
{ label: "CNI / Passeport" },
|
|
43
|
+
{ label: "Cartes bancaires" },
|
|
44
|
+
{ label: "Espèces" },
|
|
45
|
+
{ label: "Billets (Avion/Train)" },
|
|
46
|
+
{ label: "Réservations d'hébergement" },
|
|
47
|
+
{ label: "Clés de maison" },
|
|
48
|
+
{ label: "Documents internationaux", dynamicId: "item-internacional" }
|
|
49
|
+
]
|
|
50
|
+
},
|
|
51
|
+
technology: {
|
|
52
|
+
id: "technology",
|
|
53
|
+
title: "Technologie",
|
|
54
|
+
icon: "mdi:laptop",
|
|
55
|
+
items: [
|
|
56
|
+
{ label: "Téléphone portable" },
|
|
57
|
+
{ label: "Chargeur de téléphone" },
|
|
58
|
+
{ label: "Écouteurs / Casque" },
|
|
59
|
+
{ label: "Batterie externe" },
|
|
60
|
+
{ label: "Adaptateur de prise", dynamicId: "item-adaptador" }
|
|
61
|
+
]
|
|
62
|
+
},
|
|
63
|
+
toiletries: {
|
|
64
|
+
id: "toiletries",
|
|
65
|
+
title: "Trousse de Toilette",
|
|
66
|
+
icon: "mdi:toothbrush",
|
|
67
|
+
items: [
|
|
68
|
+
{ label: "Brosse à dents et dentifrice" },
|
|
69
|
+
{ label: "Déodorant" },
|
|
70
|
+
{ label: "Gel douche et shampoing" },
|
|
71
|
+
{ label: "Peigne / Brosse" },
|
|
72
|
+
{ label: "Mini trousse de secours" },
|
|
73
|
+
{ label: "Médicaments personnels" }
|
|
74
|
+
]
|
|
75
|
+
},
|
|
76
|
+
clothing_base: {
|
|
77
|
+
id: "clothing_base",
|
|
78
|
+
title: "Vêtements Quotidiens",
|
|
79
|
+
icon: "mdi:tshirt-crew",
|
|
80
|
+
items: [
|
|
81
|
+
{ label: "Sous-vêtements", dynamicId: "qty-ropa-interior" },
|
|
82
|
+
{ label: "T-shirts / Hauts", dynamicId: "qty-camisetas" },
|
|
83
|
+
{ label: "Pantalons / Bas", dynamicId: "qty-pantalones" },
|
|
84
|
+
{ label: "Chaussettes", dynamicId: "qty-calcetines" },
|
|
85
|
+
{ label: "Pyjama" },
|
|
86
|
+
{ label: "Veste légère" },
|
|
87
|
+
{ label: "Chaussures confortables" }
|
|
88
|
+
]
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
specificItems: {
|
|
92
|
+
destType: {
|
|
93
|
+
playa: {
|
|
94
|
+
id: "dest-playa",
|
|
95
|
+
title: "Pour la Côte",
|
|
96
|
+
icon: "mdi:beach",
|
|
97
|
+
items: [
|
|
98
|
+
{ label: "Maillots de bain" },
|
|
99
|
+
{ label: "Serviette de plage" },
|
|
100
|
+
{ label: "Crème solaire" },
|
|
101
|
+
{ label: "Lunettes de soleil" },
|
|
102
|
+
{ label: "Tongs" },
|
|
103
|
+
{ label: "Chapeau ou Casquette" }
|
|
104
|
+
]
|
|
105
|
+
},
|
|
106
|
+
ciudad: {
|
|
107
|
+
id: "dest-ciudad",
|
|
108
|
+
title: "Urbain",
|
|
109
|
+
icon: "mdi:city",
|
|
110
|
+
items: [
|
|
111
|
+
{ label: "Baskets pour marcher" },
|
|
112
|
+
{ label: "Petit sac à dos sécurisé" },
|
|
113
|
+
{ label: "Parapluie compact" },
|
|
114
|
+
{ label: "Guide de la ville" }
|
|
115
|
+
]
|
|
116
|
+
},
|
|
117
|
+
montana: {
|
|
118
|
+
id: "dest-montana",
|
|
119
|
+
title: "Montagne",
|
|
120
|
+
icon: "mdi:pine-tree",
|
|
121
|
+
items: [
|
|
122
|
+
{ label: "Chaussures de randonnée" },
|
|
123
|
+
{ label: "Coupe-vent déperlant" },
|
|
124
|
+
{ label: "Pantalons de trekking" },
|
|
125
|
+
{ label: "Gourde" },
|
|
126
|
+
{ label: "Sac à dos de montagne" }
|
|
127
|
+
]
|
|
128
|
+
},
|
|
129
|
+
frio: {
|
|
130
|
+
id: "dest-frio",
|
|
131
|
+
title: "Clima Froid",
|
|
132
|
+
icon: "mdi:snowflake",
|
|
133
|
+
items: [
|
|
134
|
+
{ label: "Manteau épais" },
|
|
135
|
+
{ label: "Bonnet et gants" },
|
|
136
|
+
{ label: "Chaussures d'hiver" },
|
|
137
|
+
{ label: "T-shirts thermiques" },
|
|
138
|
+
{ label: "Écharpe" }
|
|
139
|
+
]
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
travelType: {
|
|
143
|
+
negocios: {
|
|
144
|
+
id: "travel-negocios",
|
|
145
|
+
title: "Affaires",
|
|
146
|
+
icon: "mdi:tie",
|
|
147
|
+
items: [
|
|
148
|
+
{ label: "Costume / Tenue formelle" },
|
|
149
|
+
{ label: "Chemises habillées" },
|
|
150
|
+
{ label: "Chaussures de ville" },
|
|
151
|
+
{ label: "Ordinateur portable" },
|
|
152
|
+
{ label: "Chargeur d'ordinateur" },
|
|
153
|
+
{ label: "Documents de travail" }
|
|
154
|
+
]
|
|
155
|
+
},
|
|
156
|
+
ocio: {
|
|
157
|
+
id: "travel-ocio",
|
|
158
|
+
title: "Loisir",
|
|
159
|
+
icon: "mdi:camera",
|
|
160
|
+
items: [
|
|
161
|
+
{ label: "Vêtements pour sortir" },
|
|
162
|
+
{ label: "Appareil photo (Optionnel)" },
|
|
163
|
+
{ label: "Livre / Divertissement" }
|
|
164
|
+
]
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
tips: {
|
|
169
|
+
military: "Rouler ses vêtements style 'militaire' aide énormément à éviter les plis et gagne de la place.",
|
|
170
|
+
longTravel: "Vous partez plus d'une semaine. Au lieu d'emporter des kilos de linge, prévoyez de laver vos basiques à mi-parcours.",
|
|
171
|
+
intlPassport: "Voyage International : Vérifiez dès aujourd'hui que votre passeport est valide au moins 6 mois, car de nombreux pays l'exigent.",
|
|
172
|
+
intlBank: "Consultez si des visas sont nécessaires ou si votre banque applique des frais à l'étranger.",
|
|
173
|
+
intlAdapter: "N'oubliez pas votre adaptateur universel si les prises du pays de destination sont différentes !",
|
|
174
|
+
coldLayers: "Pour le froid, la clé est de s'habiller en couches : une base thermique, une couche isolante et un bon manteau imperméable.",
|
|
175
|
+
coldHeavyOn: "Portez les vêtements les plus volumineux et lourds sur vous dans l'avion pour gagner de la place en cabine.",
|
|
176
|
+
beachWet: "Prévoyez plusieurs maillots de bain ; l'humidité empêche parfois le séchage pour le lendemain."
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
const seo: ToolLocaleContent<SuitcaseChecklistUI>['seo'] = [
|
|
181
|
+
{ type: "title", text: "Générateur de Checklist de Valise : Votre Compagnon de Voyage Idéal", level: 2 },
|
|
182
|
+
{ type: "paragraph", html: "Préparer sa valise est souvent l'un des moments les plus stressants avant un voyage. Le peur d'oublier quelque chose d'important, le doute sur la météo ou l'incertitude sur la quantité de bagages peuvent gâcher l'excitation du départ. C'est ici qu'un <strong>générateur de checklist de valise</strong> devient votre meilleur allié." },
|
|
183
|
+
{
|
|
184
|
+
type: "summary",
|
|
185
|
+
title: "Avantages de notre créateur de listes",
|
|
186
|
+
items: [
|
|
187
|
+
"Personnalisation absolue adaptée au climat (plage, montagne, ville ou neige).",
|
|
188
|
+
"Calcul automatique de la quantité de vêtements selon la durée de votre séjour.",
|
|
189
|
+
"Réduction drastique du stress logistique avec une checklist interactive.",
|
|
190
|
+
"Optimisation de l'espace pour éviter le syndrome du 'au cas où'."
|
|
191
|
+
]
|
|
192
|
+
}
|
|
193
|
+
];
|
|
194
|
+
|
|
195
|
+
const faq: ToolLocaleContent<SuitcaseChecklistUI>['faq'] = [
|
|
196
|
+
{ question: "Puis-je enregistrer la liste générée ?", answer: "Vous pouvez cocher les éléments de votre liste et utiliser le bouton pour l'imprimer ou l'enregistrer en format PDF sur votre appareil." },
|
|
197
|
+
{ question: "Comment sont calculées les quantités de vêtements ?", answer: "Notre outil estime le nombre de rechanges selon la durée du voyage sélectionnée, favorisant un voyage léger." },
|
|
198
|
+
{ question: "Cela fonctionne-t-il pour les climats froids ?", answer: "Oui, vous pouvez sélectionner la destination de climat froid pour que la checklist inclue automatiquement les articles indispensables pour ces températures." },
|
|
199
|
+
{ question: "L'outil est-il payant ?", answer: "Non, notre générateur de listes est 100% gratuit et ne nécessite aucune inscription ou téléchargement." }
|
|
200
|
+
];
|
|
201
|
+
|
|
202
|
+
const bibliography: ToolLocaleContent<SuitcaseChecklistUI>['bibliography'] = [
|
|
203
|
+
{ name: "KonMari - How to KonMari a Suitcase", url: "https://konmari.com/how-to-konmari-a-suitcase/" },
|
|
204
|
+
{ name: "Eagle Creek - Ultimate Travel Packing Checklist", url: "https://eaglecreek.com/blogs/articles/what-pack-ultimate-travel-packing-checklist" }
|
|
205
|
+
];
|
|
206
|
+
|
|
207
|
+
const howTo: ToolLocaleContent<SuitcaseChecklistUI>['howTo'] = [
|
|
208
|
+
{ name: "Définissez votre voyage", text: "Sélectionnez le climat, le motif et la durée de votre escapade." },
|
|
209
|
+
{ name: "Générez la liste", text: "Cliquez sur le bouton et obtenez instantanément tout le nécessaire." },
|
|
210
|
+
{ name: "Préparez votre valise", text: "Cochez les articles au fur et à mesure pour ne rien oublier d'essentiel." }
|
|
211
|
+
];
|
|
212
|
+
|
|
213
|
+
const faqSchema: WithContext<FAQPage> = {
|
|
214
|
+
'@context': 'https://schema.org',
|
|
215
|
+
'@type': 'FAQPage',
|
|
216
|
+
mainEntity: faq.map((item) => ({
|
|
217
|
+
'@type': 'Question',
|
|
218
|
+
name: item.question,
|
|
219
|
+
acceptedAnswer: { '@type': 'Answer', text: item.answer },
|
|
220
|
+
})),
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
const howToSchema: WithContext<HowTo> = {
|
|
224
|
+
'@context': 'https://schema.org',
|
|
225
|
+
'@type': 'HowTo',
|
|
226
|
+
name: title,
|
|
227
|
+
description: description,
|
|
228
|
+
step: howTo.map((step) => ({
|
|
229
|
+
'@type': 'HowToStep',
|
|
230
|
+
name: step.name,
|
|
231
|
+
text: step.text,
|
|
232
|
+
})),
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
const appSchema: WithContext<SoftwareApplication> = {
|
|
236
|
+
'@context': 'https://schema.org',
|
|
237
|
+
'@type': 'SoftwareApplication',
|
|
238
|
+
name: title,
|
|
239
|
+
description: description,
|
|
240
|
+
applicationCategory: 'UtilitiesApplication',
|
|
241
|
+
operatingSystem: 'All',
|
|
242
|
+
offers: { '@type': 'Offer', price: '0', priceCurrency: 'EUR' },
|
|
243
|
+
inLanguage: 'fr',
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
export const content: ToolLocaleContent<SuitcaseChecklistUI> = {
|
|
247
|
+
slug,
|
|
248
|
+
title,
|
|
249
|
+
description,
|
|
250
|
+
ui,
|
|
251
|
+
seo,
|
|
252
|
+
faq,
|
|
253
|
+
faqTitle: "Questions Fréquentes",
|
|
254
|
+
bibliography,
|
|
255
|
+
bibliographyTitle: "Sources et Références",
|
|
256
|
+
howTo,
|
|
257
|
+
howToTitle: "Comment planifier sa valise",
|
|
258
|
+
schemas: [faqSchema, howToSchema, appSchema],
|
|
259
|
+
};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { TravelToolEntry, ToolDefinition } from '../../types';
|
|
2
|
+
import SuitcaseChecklist from './component.astro';
|
|
3
|
+
import SuitcaseChecklistSEO from './seo.astro';
|
|
4
|
+
import SuitcaseChecklistBibliography from './bibliography.astro';
|
|
5
|
+
|
|
6
|
+
export interface ChecklistItem {
|
|
7
|
+
label: string;
|
|
8
|
+
dynamicId?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface ChecklistCategory {
|
|
12
|
+
id: string;
|
|
13
|
+
title: string;
|
|
14
|
+
icon: string;
|
|
15
|
+
items: ChecklistItem[];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface SuitcaseChecklistUI {
|
|
19
|
+
[key: string]: unknown;
|
|
20
|
+
title: string;
|
|
21
|
+
form: {
|
|
22
|
+
destTitle: string;
|
|
23
|
+
destTypes: Array<{ id: string; name: string; icon: string }>;
|
|
24
|
+
daysTitle: string;
|
|
25
|
+
intlTitle: string;
|
|
26
|
+
intlLabel: string;
|
|
27
|
+
reasonTitle: string;
|
|
28
|
+
reasons: Array<{ id: string; name: string; icon: string }>;
|
|
29
|
+
submitBtn: string;
|
|
30
|
+
};
|
|
31
|
+
results: {
|
|
32
|
+
title: string;
|
|
33
|
+
editBtn: string;
|
|
34
|
+
printBtn: string;
|
|
35
|
+
tipsTitle: string;
|
|
36
|
+
progressText: string;
|
|
37
|
+
};
|
|
38
|
+
itemsDb: Record<string, ChecklistCategory>;
|
|
39
|
+
specificItems: {
|
|
40
|
+
destType: Record<string, ChecklistCategory>;
|
|
41
|
+
travelType: Record<string, ChecklistCategory>;
|
|
42
|
+
};
|
|
43
|
+
tips: {
|
|
44
|
+
military: string;
|
|
45
|
+
longTravel: string;
|
|
46
|
+
intlPassport: string;
|
|
47
|
+
intlBank: string;
|
|
48
|
+
intlAdapter: string;
|
|
49
|
+
coldLayers: string;
|
|
50
|
+
coldHeavyOn: string;
|
|
51
|
+
beachWet: string;
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export const suitcaseChecklist: TravelToolEntry<SuitcaseChecklistUI> = {
|
|
56
|
+
id: 'suitcase-checklist',
|
|
57
|
+
icons: {
|
|
58
|
+
bg: 'mdi:bag-checked',
|
|
59
|
+
fg: 'mdi:format-list-checks',
|
|
60
|
+
},
|
|
61
|
+
i18n: {
|
|
62
|
+
es: () => import('./i18n/es').then((m) => m.content),
|
|
63
|
+
en: () => import('./i18n/en').then((m) => m.content),
|
|
64
|
+
fr: () => import('./i18n/fr').then((m) => m.content),
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export { SuitcaseChecklist, SuitcaseChecklistSEO, SuitcaseChecklistBibliography };
|
|
69
|
+
|
|
70
|
+
export const SUITCASE_CHECKLIST_TOOL: ToolDefinition = {
|
|
71
|
+
entry: suitcaseChecklist,
|
|
72
|
+
Component: SuitcaseChecklist,
|
|
73
|
+
SEOComponent: SuitcaseChecklistSEO,
|
|
74
|
+
BibliographyComponent: SuitcaseChecklistBibliography,
|
|
75
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { SEORenderer } from "@jjlmoya/utils-shared";
|
|
3
|
+
import { suitcaseChecklist } from "./index";
|
|
4
|
+
import type { KnownLocale } from "../../types";
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
locale?: KnownLocale;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const { locale = "es" } = Astro.props;
|
|
11
|
+
const content = await suitcaseChecklist.i18n[locale]?.();
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
{content && <SEORenderer content={{ locale, sections: content.seo }} />}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { Bibliography as SharedBibliography } from "@jjlmoya/utils-shared";
|
|
3
|
+
import { tipCalculator } from "./index";
|
|
4
|
+
import type { KnownLocale } from "../../types";
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
locale?: KnownLocale;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const { locale = "es" } = Astro.props;
|
|
11
|
+
const content = await tipCalculator.i18n[locale]?.();
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
{content && <SharedBibliography links={content.bibliography} />}
|