@jjlmoya/utils-textiles 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.
Files changed (134) hide show
  1. package/package.json +60 -0
  2. package/src/category/i18n/en.ts +76 -0
  3. package/src/category/i18n/es.ts +76 -0
  4. package/src/category/i18n/fr.ts +73 -0
  5. package/src/category/index.ts +17 -0
  6. package/src/category/seo.astro +15 -0
  7. package/src/components/PreviewNavSidebar.astro +116 -0
  8. package/src/components/PreviewToolbar.astro +143 -0
  9. package/src/data.ts +11 -0
  10. package/src/env.d.ts +5 -0
  11. package/src/index.ts +32 -0
  12. package/src/layouts/PreviewLayout.astro +117 -0
  13. package/src/pages/[locale]/[slug].astro +146 -0
  14. package/src/pages/[locale].astro +253 -0
  15. package/src/pages/index.astro +4 -0
  16. package/src/tests/faq_count.test.ts +19 -0
  17. package/src/tests/locale_completeness.test.ts +42 -0
  18. package/src/tests/mocks/astro_mock.js +2 -0
  19. package/src/tests/no_h1_in_components.test.ts +48 -0
  20. package/src/tests/seo_length.test.ts +22 -0
  21. package/src/tests/title_separators.test.ts +40 -0
  22. package/src/tests/tool_validation.test.ts +17 -0
  23. package/src/tool/burnTest/bibliography.astro +34 -0
  24. package/src/tool/burnTest/component.astro +602 -0
  25. package/src/tool/burnTest/i18n/en.ts +225 -0
  26. package/src/tool/burnTest/i18n/es.ts +225 -0
  27. package/src/tool/burnTest/i18n/fr.ts +225 -0
  28. package/src/tool/burnTest/index.ts +34 -0
  29. package/src/tool/burnTest/logic.ts +3 -0
  30. package/src/tool/burnTest/seo.astro +40 -0
  31. package/src/tool/burnTest/ui.ts +36 -0
  32. package/src/tool/clothingSizeConverter/bibliography.astro +34 -0
  33. package/src/tool/clothingSizeConverter/component.astro +1226 -0
  34. package/src/tool/clothingSizeConverter/i18n/en.ts +356 -0
  35. package/src/tool/clothingSizeConverter/i18n/es.ts +356 -0
  36. package/src/tool/clothingSizeConverter/i18n/fr.ts +356 -0
  37. package/src/tool/clothingSizeConverter/index.ts +34 -0
  38. package/src/tool/clothingSizeConverter/logic.ts +30 -0
  39. package/src/tool/clothingSizeConverter/seo.astro +40 -0
  40. package/src/tool/clothingSizeConverter/ui.ts +70 -0
  41. package/src/tool/fabricProjectCalculator/bibliography.astro +19 -0
  42. package/src/tool/fabricProjectCalculator/component.astro +914 -0
  43. package/src/tool/fabricProjectCalculator/i18n/en.ts +250 -0
  44. package/src/tool/fabricProjectCalculator/i18n/es.ts +250 -0
  45. package/src/tool/fabricProjectCalculator/i18n/fr.ts +250 -0
  46. package/src/tool/fabricProjectCalculator/index.ts +34 -0
  47. package/src/tool/fabricProjectCalculator/logic.ts +89 -0
  48. package/src/tool/fabricProjectCalculator/seo.astro +40 -0
  49. package/src/tool/fabricProjectCalculator/ui.ts +57 -0
  50. package/src/tool/fabricTruth/bibliography.astro +40 -0
  51. package/src/tool/fabricTruth/component.astro +708 -0
  52. package/src/tool/fabricTruth/i18n/en.ts +282 -0
  53. package/src/tool/fabricTruth/i18n/es.ts +316 -0
  54. package/src/tool/fabricTruth/i18n/fr.ts +282 -0
  55. package/src/tool/fabricTruth/index.ts +34 -0
  56. package/src/tool/fabricTruth/logic.ts +91 -0
  57. package/src/tool/fabricTruth/seo.astro +40 -0
  58. package/src/tool/fabricTruth/ui.ts +28 -0
  59. package/src/tool/fiberPrep/bibliography.astro +33 -0
  60. package/src/tool/fiberPrep/component.astro +987 -0
  61. package/src/tool/fiberPrep/i18n/en.ts +225 -0
  62. package/src/tool/fiberPrep/i18n/es.ts +225 -0
  63. package/src/tool/fiberPrep/i18n/fr.ts +225 -0
  64. package/src/tool/fiberPrep/index.ts +34 -0
  65. package/src/tool/fiberPrep/logic.ts +36 -0
  66. package/src/tool/fiberPrep/seo.astro +40 -0
  67. package/src/tool/fiberPrep/ui.ts +38 -0
  68. package/src/tool/knittingGauge/bibliography.astro +14 -0
  69. package/src/tool/knittingGauge/component.astro +828 -0
  70. package/src/tool/knittingGauge/i18n/en.ts +234 -0
  71. package/src/tool/knittingGauge/i18n/es.ts +234 -0
  72. package/src/tool/knittingGauge/i18n/fr.ts +234 -0
  73. package/src/tool/knittingGauge/index.ts +34 -0
  74. package/src/tool/knittingGauge/logic.ts +28 -0
  75. package/src/tool/knittingGauge/seo.astro +40 -0
  76. package/src/tool/knittingGauge/ui.ts +41 -0
  77. package/src/tool/laundryGuide/bibliography.astro +33 -0
  78. package/src/tool/laundryGuide/component.astro +486 -0
  79. package/src/tool/laundryGuide/data-en.ts +166 -0
  80. package/src/tool/laundryGuide/data-es.ts +166 -0
  81. package/src/tool/laundryGuide/data-fr.ts +103 -0
  82. package/src/tool/laundryGuide/i18n/en.ts +275 -0
  83. package/src/tool/laundryGuide/i18n/es.ts +275 -0
  84. package/src/tool/laundryGuide/i18n/fr.ts +291 -0
  85. package/src/tool/laundryGuide/index.ts +29 -0
  86. package/src/tool/laundryGuide/logic.ts +19 -0
  87. package/src/tool/laundryGuide/seo.astro +40 -0
  88. package/src/tool/needleConverter/bibliography.astro +14 -0
  89. package/src/tool/needleConverter/component.astro +518 -0
  90. package/src/tool/needleConverter/i18n/en.ts +217 -0
  91. package/src/tool/needleConverter/i18n/es.ts +217 -0
  92. package/src/tool/needleConverter/i18n/fr.ts +217 -0
  93. package/src/tool/needleConverter/index.ts +34 -0
  94. package/src/tool/needleConverter/logic.ts +31 -0
  95. package/src/tool/needleConverter/seo.astro +15 -0
  96. package/src/tool/needleConverter/ui.ts +17 -0
  97. package/src/tool/sewingPatternScaler/bibliography.astro +14 -0
  98. package/src/tool/sewingPatternScaler/component.astro +550 -0
  99. package/src/tool/sewingPatternScaler/i18n/en.ts +196 -0
  100. package/src/tool/sewingPatternScaler/i18n/es.ts +231 -0
  101. package/src/tool/sewingPatternScaler/i18n/fr.ts +187 -0
  102. package/src/tool/sewingPatternScaler/index.ts +34 -0
  103. package/src/tool/sewingPatternScaler/logic.ts +35 -0
  104. package/src/tool/sewingPatternScaler/seo.astro +49 -0
  105. package/src/tool/sewingPatternScaler/ui.ts +29 -0
  106. package/src/tool/shoeSizeConverter/bibliography.astro +34 -0
  107. package/src/tool/shoeSizeConverter/component.astro +437 -0
  108. package/src/tool/shoeSizeConverter/i18n/en.ts +188 -0
  109. package/src/tool/shoeSizeConverter/i18n/es.ts +188 -0
  110. package/src/tool/shoeSizeConverter/i18n/fr.ts +188 -0
  111. package/src/tool/shoeSizeConverter/index.ts +34 -0
  112. package/src/tool/shoeSizeConverter/logic.ts +8 -0
  113. package/src/tool/shoeSizeConverter/seo.astro +40 -0
  114. package/src/tool/shoeSizeConverter/ui.ts +21 -0
  115. package/src/tool/stainChemistry/bibliography.astro +40 -0
  116. package/src/tool/stainChemistry/component.astro +717 -0
  117. package/src/tool/stainChemistry/i18n/en.ts +303 -0
  118. package/src/tool/stainChemistry/i18n/es.ts +300 -0
  119. package/src/tool/stainChemistry/i18n/fr.ts +310 -0
  120. package/src/tool/stainChemistry/index.ts +34 -0
  121. package/src/tool/stainChemistry/logic.ts +11 -0
  122. package/src/tool/stainChemistry/seo.astro +40 -0
  123. package/src/tool/stainChemistry/ui.ts +51 -0
  124. package/src/tool/yarnCalculator/bibliography.astro +19 -0
  125. package/src/tool/yarnCalculator/component.astro +792 -0
  126. package/src/tool/yarnCalculator/i18n/en.ts +310 -0
  127. package/src/tool/yarnCalculator/i18n/es.ts +310 -0
  128. package/src/tool/yarnCalculator/i18n/fr.ts +310 -0
  129. package/src/tool/yarnCalculator/index.ts +34 -0
  130. package/src/tool/yarnCalculator/logic.ts +84 -0
  131. package/src/tool/yarnCalculator/seo.astro +14 -0
  132. package/src/tool/yarnCalculator/ui.ts +42 -0
  133. package/src/tools.ts +16 -0
  134. package/src/types.ts +72 -0
@@ -0,0 +1,310 @@
1
+ import type { WithContext, FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
2
+ import type { ToolLocaleContent } from '../../../types';
3
+ import type { YarnCalculatorUI } from '../ui';
4
+
5
+ const slug = 'calculateur-laine';
6
+ const title = 'Calculateur de Laine : Combien de mètres et de pelotes ?';
7
+ const description =
8
+ 'Calculez avec précision la quantité de laine nécessaire pour vos projets de tricot et crochet en fonction du patron et de la taille.';
9
+
10
+ const faqData = [
11
+ {
12
+ question: 'Pourquoi le crochet utilise-t-il plus de laine que le tricot ?',
13
+ answer:
14
+ 'Le crochet crée des nœuds et des boucles superposés, ce qui produit un tissu plus dense et épais par centimètre carré. En moyenne, un projet au crochet consomme 25 à 35% de fil de plus que le même projet tricoté à plat.',
15
+ },
16
+ {
17
+ question: 'Que se passe-t-il si j\'achète de la laine de différents lots de teinture ?',
18
+ answer:
19
+ 'Même si la couleur semble identique en magasin, les variations chimiques minimes lors de la teinture peuvent créer des "rayures" visibles dans le vêtement terminé. Il est fortement recommandé d\'acheter toute la laine en une fois en vérifiant que le code de lot est identique.',
20
+ },
21
+ {
22
+ question: 'Comment la tension affecte-t-elle la consommation finale ?',
23
+ answer:
24
+ 'Si vous tricotez de façon lâche, chaque point utilise plus de mètres de fil. Si votre échantillon de tension est plus grand que ce qu\'indique le patron, votre consommation de laine augmentera considérablement. Il est essentiel de réaliser un échantillon de tension au préalable.',
25
+ },
26
+ {
27
+ question: 'Comment calculer les mètres si je n\'ai que le poids de la pelote ?',
28
+ answer:
29
+ 'Sans la donnée de métrage, c\'est difficile. La plupart des pelotes indiquent les mètres sur leur étiquette. Si ce n\'est pas le cas, recherchez le nom de la laine dans des bases de données comme Ravelry pour trouver la longueur exacte par 50g ou 100g.',
30
+ },
31
+ ];
32
+
33
+ const howToData = [
34
+ {
35
+ name: 'Sélectionnez le projet',
36
+ text: 'Choisissez parmi un pull, une écharpe, un bonnet, des chaussettes ou une couverture pour charger les bases de consommation spécifiques.',
37
+ },
38
+ {
39
+ name: 'Définissez la taille et l\'épaisseur',
40
+ text: 'Indiquez les dimensions finales (S, M, L, XL) et l\'épaisseur du fil que vous utiliserez (Fingering, DK, Worsted, Bulky).',
41
+ },
42
+ {
43
+ name: 'Activez les multiplicateurs',
44
+ text: 'Si votre patron comporte des torsades ou des textures complexes, activez le commutateur de textures pour ajouter le supplément nécessaire.',
45
+ },
46
+ {
47
+ name: 'Obtenez la liste d\'achats',
48
+ text: 'Entrez les mètres par pelote de votre laine choisie pour voir exactement combien d\'unités vous devez acheter.',
49
+ },
50
+ ];
51
+
52
+ const faqSchema: WithContext<FAQPage> = {
53
+ '@context': 'https://schema.org',
54
+ '@type': 'FAQPage',
55
+ mainEntity: faqData.map((item) => ({
56
+ '@type': 'Question',
57
+ name: item.question,
58
+ acceptedAnswer: { '@type': 'Answer', text: item.answer },
59
+ })),
60
+ };
61
+
62
+ const howToSchema: WithContext<HowTo> = {
63
+ '@context': 'https://schema.org',
64
+ '@type': 'HowTo',
65
+ name: title,
66
+ description,
67
+ step: howToData.map((step) => ({
68
+ '@type': 'HowToStep',
69
+ name: step.name,
70
+ text: step.text,
71
+ })),
72
+ };
73
+
74
+ const appSchema: WithContext<SoftwareApplication> = {
75
+ '@context': 'https://schema.org',
76
+ '@type': 'SoftwareApplication',
77
+ name: title,
78
+ description,
79
+ applicationCategory: 'UtilityApplication',
80
+ operatingSystem: 'All',
81
+ offers: { '@type': 'Offer', price: '0', priceCurrency: 'EUR' },
82
+ inLanguage: 'fr',
83
+ };
84
+
85
+ export const content: ToolLocaleContent<YarnCalculatorUI> = {
86
+ slug,
87
+ title,
88
+ description,
89
+ faqTitle: 'Questions Fréquentes',
90
+ faq: faqData,
91
+ bibliographyTitle: 'Bibliographie',
92
+ bibliography: [
93
+ {
94
+ name: 'Craft Yarn Council: Standard Yarn Weights & Yardages',
95
+ url: 'https://www.craftyarncouncil.com/standards/yarn-weight-system',
96
+ },
97
+ {
98
+ name: 'Interweave Essentials: Yarn Substitution and Estimation',
99
+ url: 'https://www.interweave.com/article/knitting/yarn-substitution-angelina-take-two/',
100
+ },
101
+ {
102
+ name: 'The Woolery: Fibers and Calculation of Yarn Usage',
103
+ url: 'https://woolery.com/blogs/the-woolery-blog/yarn-weight-charts-helpful-guide/',
104
+ },
105
+ ],
106
+ howTo: howToData,
107
+ schemas: [faqSchema, howToSchema, appSchema],
108
+ seo: [
109
+ {
110
+ type: 'title',
111
+ text: 'Guide de Consommation de Laine : Comment Calculer Mètres et Pelotes',
112
+ level: 2,
113
+ },
114
+ {
115
+ type: 'paragraph',
116
+ html: 'Maîtrisez la logistique du tricot et évitez de manquer de fil grâce à notre calculateur technique de précision pour le tricot et le crochet. L\'une des plus grandes craintes des tricoteurs est le <strong>"Yarn Chicken"</strong> : ce moment critique en fin de projet où le fil semble vouloir se terminer avant de fermer les dernières mailles. Ce calculateur a été développé pour vous offrir des estimations basées sur les standards industriels de métrage et de grammage.',
117
+ },
118
+ {
119
+ type: 'stats',
120
+ items: [
121
+ { value: '+10%', label: 'Marge de Sécurité', icon: 'mdi:security' },
122
+ { value: 'Métrique', label: 'Calcul en Mètres', icon: 'mdi:ruler' },
123
+ { value: 'Hors ligne', label: 'Base de Données Locale', icon: 'mdi:wifi-off' },
124
+ ],
125
+ columns: 3,
126
+ },
127
+ {
128
+ type: 'title',
129
+ text: 'Pourquoi le Poids des Pelotes est Trompeur',
130
+ level: 3,
131
+ },
132
+ {
133
+ type: 'paragraph',
134
+ html: 'Beaucoup de débutants font l\'erreur d\'acheter de la laine en se basant exclusivement sur le poids (ex. "6 pelotes de 100g"). Cependant, le poids est une unité de masse, pas de longueur. Deux laines de 100g peuvent avoir des longueurs radicalement différentes : une laine <strong>Bulky</strong> peut n\'avoir que 80 mètres, tandis qu\'une <strong>Fingering / Lace</strong> peut dépasser 450 mètres pour le même poids.',
135
+ },
136
+ {
137
+ type: 'paragraph',
138
+ html: 'C\'est pourquoi notre calculateur priorise toujours les <strong>mètres totaux</strong>. Une fois que vous connaissez la longueur nécessaire pour votre taille et projet, divisez-la par le métrage indiqué sur l\'étiquette de votre pelote pour obtenir le nombre exact d\'unités à acheter.',
139
+ },
140
+ {
141
+ type: 'title',
142
+ text: 'Tableau de Métrages Standard par Épaisseur',
143
+ level: 3,
144
+ },
145
+ {
146
+ type: 'paragraph',
147
+ html: 'Si vous n\'avez pas l\'étiquette sous la main, utilisez ces métrages de référence (pour 100g de fibre) pour vos estimations :',
148
+ },
149
+ {
150
+ type: 'table',
151
+ headers: ['Épaisseur (Weight)', 'Mètres approx. / 100g', 'Utilisation Recommandée', 'Aiguilles Suggérées'],
152
+ rows: [
153
+ ['<strong>Lace / 0</strong>', '600 - 900m', 'Châles dentelle, ajourés', '1.5 - 2.5mm'],
154
+ ['<strong>Fingering / 1</strong>', '360 - 450m', 'Chaussettes, layette', '2.25 - 3.25mm'],
155
+ ['<strong>Sport / 2</strong>', '270 - 320m', 'Vestes légères', '3.25 - 3.75mm'],
156
+ ['<strong>DK / 3</strong>', '200 - 250m', 'Pulls, bonnets', '3.75 - 4.5mm'],
157
+ ['<strong>Worsted / 4</strong>', '180 - 220m', 'Couvertures, manteaux', '4.5 - 5.5mm'],
158
+ ['<strong>Bulky / 5</strong>', '100 - 140m', 'Écharpes XXL, couvertures', '6.0 - 8.0mm'],
159
+ ],
160
+ },
161
+ {
162
+ type: 'tip',
163
+ title: 'Le Facteur Tension (Gauge)',
164
+ html: '<p>Rappellez-vous que si vous tricotez très lâchement, vos mailles seront plus grandes et consommeront plus de fil. Notre calculateur applique une marge de sécurité, mais si vous savez que votre tension est extrêmement lâche, ajoutez 5-10% supplémentaires pour éviter les surprises.</p>',
165
+ },
166
+ {
167
+ type: 'title',
168
+ text: 'Variables qui Augmentent la Consommation de Fil',
169
+ level: 3,
170
+ },
171
+ {
172
+ type: 'paragraph',
173
+ html: 'Tous les points ne consomment pas la même quantité de fibre. Lors du choix de votre design, tenez compte de ces facteurs multiplicateurs :',
174
+ },
175
+ {
176
+ type: 'comparative',
177
+ items: [
178
+ {
179
+ title: 'Jersey / Point Endroit',
180
+ description: 'Représente la consommation de base et l\'efficacité maximale de fil pour tout vêtement.',
181
+ icon: 'mdi:minus-circle-outline',
182
+ points: ['Idéal pour les débutants', 'Tombé naturel du tissu'],
183
+ },
184
+ {
185
+ title: 'Torsades / Câbles',
186
+ description: 'Le croisement des fibres "raccourcit" la pièce, augmentant la consommation de 25%.',
187
+ icon: 'mdi:texture-box',
188
+ points: ['Nécessite plus de mètres', 'Tissu plus dense et lourd'],
189
+ pointIcon: 'mdi:alert-circle-outline',
190
+ },
191
+ {
192
+ title: 'Crochet',
193
+ description: 'Les nœuds du crochet utilisent 30% de fil de plus que le tricot plat.',
194
+ icon: 'mdi:hook',
195
+ points: ['Consommation rapide des pelotes', 'Texture tridimensionnelle'],
196
+ pointIcon: 'mdi:alert-circle-outline',
197
+ },
198
+ {
199
+ title: 'Côtes',
200
+ description: 'La structure endroit/envers accumule plus de laine par cm carré.',
201
+ icon: 'mdi:unfold-more-horizontal',
202
+ points: ['Consommation intermédiaire (+15%)', 'Haute récupération élastique'],
203
+ },
204
+ ],
205
+ columns: 2,
206
+ },
207
+ {
208
+ type: 'title',
209
+ text: 'Glossaire du Tricoteur Prévoyant',
210
+ level: 3,
211
+ },
212
+ {
213
+ type: 'glossary',
214
+ items: [
215
+ {
216
+ term: 'Dye Lot (Lot de teinture)',
217
+ definition:
218
+ 'Numéro identifiant la cuve de teinture dans laquelle la laine a été teinte. Différents lots peuvent présenter des variations de couleur imperceptibles dans la pelote mais très visibles dans le vêtement terminé.',
219
+ },
220
+ {
221
+ term: 'Stash (Réserve)',
222
+ definition:
223
+ 'La collection personnelle de fils d\'un tricoteur. Identifier le poids des restes de fils sans étiquette est essentiel pour calculer les petits projets.',
224
+ },
225
+ {
226
+ term: 'Put-up (Conditionnement)',
227
+ definition:
228
+ 'La forme sous laquelle le fil est présenté (écheveau, pelote ou cône), ce qui détermine s\'il doit être mis en pelote avant utilisation.',
229
+ },
230
+ {
231
+ term: 'Yarn Chicken',
232
+ definition:
233
+ 'Le défi de tenter de terminer une section de tricot avec une quantité de fil apparemment insuffisante.',
234
+ },
235
+ ],
236
+ },
237
+ {
238
+ type: 'title',
239
+ text: 'Comment Utiliser les Résultats de ce Calculateur',
240
+ level: 3,
241
+ },
242
+ {
243
+ type: 'paragraph',
244
+ html: 'Une fois que l\'outil vous donne le nombre de mètres et de pelotes, nous recommandons ces étapes de vérification manuelle :',
245
+ },
246
+ {
247
+ type: 'list',
248
+ items: [
249
+ '<strong>Vérifiez le métrage réel de votre pelote :</strong> Toutes les pelotes de 100g n\'ont pas 200m. Vérifiez sur l\'étiquette pour ajuster le calcul final.',
250
+ '<strong>Ajoutez le métrage des détails :</strong> Si vous ajoutez des franges à une écharpe ou des pompons à un bonnet, ajoutez 50-80 mètres supplémentaires au total.',
251
+ '<strong>Échantillon de tension :</strong> Tricotez un carré de 10x10 cm. Pesez-le sur une balance de précision. S\'il pèse 20g et votre couverture fait 100x100cm (100 carrés), vous aurez besoin exactement de 2kg de laine.',
252
+ '<strong>Achetez toujours du même lot :</strong> Si le calcul indique 7 pelotes, achetez-en 8 si elles sont de lots différents pour avoir une marge ou pour les pièces cachées comme les coutures.',
253
+ ],
254
+ },
255
+ {
256
+ type: 'summary',
257
+ title: 'Conseils Stratégiques pour l\'Achat de Laine',
258
+ items: [
259
+ 'Priorisez les mètres sur les grammes pour assurer la longueur nécessaire.',
260
+ 'Le crochet consomme beaucoup plus de laine que le tricot ; ajustez le sélecteur de type.',
261
+ 'Les textures denses (torsades) nécessitent une augmentation de 25% du budget de fil.',
262
+ 'Conservez toujours une étiquette de chaque lot au cas où vous auriez besoin de commander la même couleur.',
263
+ 'Utilisez la marge de sécurité de 10% pour tricoter l\'échantillon de tension obligatoire.',
264
+ ],
265
+ },
266
+ ],
267
+ ui: {
268
+ sectionProject: '1. TYPE DE PROJET',
269
+ sectionSize: '2. DIMENSIONS / TAILLE',
270
+ sectionWeight: '3. ÉPAISSEUR DU FIL',
271
+ sectionPattern: '4. CARACTÉRISTIQUES DU PATRON',
272
+ sectionBall: '5. DONNÉES DE LA PELOTE',
273
+ labelCables: 'Torsades ou textures denses ?',
274
+ labelGrams: 'GRAMMES / PELOTE',
275
+ labelMeters: 'MÈTRES / PELOTE',
276
+ weightFine: 'Fin',
277
+ weightMedium: 'Moyen',
278
+ weightThick: 'Épais',
279
+ weightXXL: 'XXL',
280
+ projectSweater: 'PULL',
281
+ projectScarf: 'ÉCHARPE',
282
+ projectHat: 'BONNET',
283
+ projectSocks: 'CHAUSSETTES',
284
+ projectBlanket: 'COUVERTURE',
285
+ projectCardigan: 'GILET',
286
+ statPrefix: 'CONSOMMATION TOTALE ESTIMÉE',
287
+ statSuffix: 'M',
288
+ statNote: 'Métrage final calculé avec une réserve de sécurité de 10%.',
289
+ ballsFormatSingular: 'Achetez {n} pelote',
290
+ ballsFormatPlural: 'Achetez {n} pelotes',
291
+ ballsDetailFormat: 'Pelotes de {w}g / {m}m chacune.',
292
+ alertTitle: 'Sécurité stratégique :',
293
+ alertText:
294
+ 'Ce calcul garantit déjà que vous ne manquerez pas de fil. N\'oubliez pas de vérifier le numéro de lot lors de l\'achat de vos pelotes pour éviter les sauts de couleur.',
295
+ schemeSleeveLabel: 'Manches',
296
+ schemeSleeveEach: 'ch.',
297
+ schemeBodyLabel: 'Corps',
298
+ schemeTotalHat: 'Total Bonnet',
299
+ schemeTotalFlat: 'Total Pièce',
300
+ schemeTotalSocks: 'Total Paire',
301
+ sizeLabels: {
302
+ sweater: ['S', 'M', 'L', 'XL'],
303
+ cardigan: ['S', 'M', 'L', 'XL'],
304
+ hat: ['Enfant', 'S', 'M', 'L'],
305
+ scarf: ['Mini', 'M', 'Grand', 'Maxi'],
306
+ socks: ['Enfant', 'Ad. S', 'Ad. M', 'Ad. L'],
307
+ blanket: ['Bébé', 'Canapé', '1 pers.', '2 pers.'],
308
+ },
309
+ },
310
+ };
@@ -0,0 +1,34 @@
1
+ import type { TextilesToolEntry, ToolLocaleContent, ToolDefinition } from '../../types';
2
+ import YarnCalculatorComponent from './component.astro';
3
+ import YarnCalculatorSEO from './seo.astro';
4
+ import YarnCalculatorBibliography from './bibliography.astro';
5
+
6
+ import type { YarnCalculatorUI } from './ui';
7
+
8
+ export type YarnCalculatorLocaleContent = ToolLocaleContent<YarnCalculatorUI>;
9
+
10
+ import { content as es } from './i18n/es';
11
+ import { content as en } from './i18n/en';
12
+ import { content as fr } from './i18n/fr';
13
+
14
+ export const yarnCalculator: TextilesToolEntry<YarnCalculatorUI> = {
15
+ id: 'yarn-calculator',
16
+ icons: {
17
+ bg: 'mdi:comma-circle',
18
+ fg: 'mdi:counter',
19
+ },
20
+ i18n: {
21
+ es: async () => es,
22
+ en: async () => en,
23
+ fr: async () => fr,
24
+ },
25
+ };
26
+
27
+ export { YarnCalculatorComponent, YarnCalculatorSEO, YarnCalculatorBibliography };
28
+
29
+ export const YARN_CALCULATOR_TOOL: ToolDefinition = {
30
+ entry: yarnCalculator,
31
+ Component: YarnCalculatorComponent,
32
+ SEOComponent: YarnCalculatorSEO,
33
+ BibliographyComponent: YarnCalculatorBibliography,
34
+ };
@@ -0,0 +1,84 @@
1
+ export type SizeKey = 's' | 'm' | 'l' | 'xl';
2
+ export type WeightKey = 'fingering' | 'dk' | 'worsted' | 'bulky';
3
+
4
+ export interface ProjectSpec {
5
+ s: number[];
6
+ m: number[];
7
+ l: number[];
8
+ xl: number[];
9
+ parts: { body: number; sleeves: number };
10
+ }
11
+
12
+ export const PROJECTS: Record<string, ProjectSpec> = {
13
+ sweater: {
14
+ s: [1100, 1000, 900, 800],
15
+ m: [1250, 1150, 1050, 950],
16
+ l: [1450, 1350, 1250, 1150],
17
+ xl: [1700, 1600, 1500, 1400],
18
+ parts: { body: 0.7, sleeves: 0.3 },
19
+ },
20
+ cardigan: {
21
+ s: [1200, 1100, 1000, 900],
22
+ m: [1350, 1250, 1150, 1050],
23
+ l: [1550, 1450, 1350, 1250],
24
+ xl: [1850, 1750, 1650, 1550],
25
+ parts: { body: 0.65, sleeves: 0.35 },
26
+ },
27
+ hat: {
28
+ s: [180, 160, 140, 120],
29
+ m: [210, 190, 170, 150],
30
+ l: [250, 230, 210, 190],
31
+ xl: [300, 280, 260, 240],
32
+ parts: { body: 1, sleeves: 0 },
33
+ },
34
+ scarf: {
35
+ s: [350, 300, 250, 200],
36
+ m: [500, 450, 400, 350],
37
+ l: [700, 650, 600, 550],
38
+ xl: [950, 900, 850, 800],
39
+ parts: { body: 1, sleeves: 0 },
40
+ },
41
+ socks: {
42
+ s: [300, 250, 200, 150],
43
+ m: [400, 350, 300, 250],
44
+ l: [500, 450, 400, 350],
45
+ xl: [650, 600, 550, 500],
46
+ parts: { body: 1, sleeves: 0 },
47
+ },
48
+ blanket: {
49
+ s: [800, 750, 700, 650],
50
+ m: [3500, 3200, 3000, 2800],
51
+ l: [5500, 5000, 4500, 4000],
52
+ xl: [8000, 7500, 7000, 6500],
53
+ parts: { body: 1, sleeves: 0 },
54
+ },
55
+ };
56
+
57
+ export const WEIGHT_IDX: Record<WeightKey, number> = {
58
+ fingering: 0,
59
+ dk: 1,
60
+ worsted: 2,
61
+ bulky: 3,
62
+ };
63
+
64
+ export function getBaseMeters(project: string, size: SizeKey, weight: WeightKey): number {
65
+ const proj = PROJECTS[project];
66
+ if (!proj) return 0;
67
+ return proj[size][WEIGHT_IDX[weight]];
68
+ }
69
+
70
+ export function computeTotal(baseM: number, cables: boolean): number {
71
+ return Math.ceil(baseM * (cables ? 1.25 : 1) * 1.1);
72
+ }
73
+
74
+ export function computeBalls(totalM: number, ballM: number): number {
75
+ return Math.ceil(totalM / ballM);
76
+ }
77
+
78
+ export function getProjectParts(project: string): { body: number; sleeves: number } {
79
+ return PROJECTS[project]?.parts ?? { body: 1, sleeves: 0 };
80
+ }
81
+
82
+ export function parseParam(p: URLSearchParams, key: string, def: number): number {
83
+ return parseInt(p.get(key) ?? '') || def;
84
+ }
@@ -0,0 +1,14 @@
1
+ ---
2
+ import { SEORenderer } from '@jjlmoya/utils-shared';
3
+ import { yarnCalculator } 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 yarnCalculator.i18n[locale]?.();
12
+ if (!content) return null;
13
+ ---
14
+ <SEORenderer content={{ locale: locale as string, sections: content.seo }} />
@@ -0,0 +1,42 @@
1
+ export interface YarnCalculatorUI {
2
+ sectionProject: string;
3
+ sectionSize: string;
4
+ sectionWeight: string;
5
+ sectionPattern: string;
6
+ sectionBall: string;
7
+ labelCables: string;
8
+ labelGrams: string;
9
+ labelMeters: string;
10
+ weightFine: string;
11
+ weightMedium: string;
12
+ weightThick: string;
13
+ weightXXL: string;
14
+ projectSweater: string;
15
+ projectScarf: string;
16
+ projectHat: string;
17
+ projectSocks: string;
18
+ projectBlanket: string;
19
+ projectCardigan: string;
20
+ statPrefix: string;
21
+ statSuffix: string;
22
+ statNote: string;
23
+ ballsFormatSingular: string;
24
+ ballsFormatPlural: string;
25
+ ballsDetailFormat: string;
26
+ alertTitle: string;
27
+ alertText: string;
28
+ schemeSleeveLabel: string;
29
+ schemeSleeveEach: string;
30
+ schemeBodyLabel: string;
31
+ schemeTotalHat: string;
32
+ schemeTotalFlat: string;
33
+ schemeTotalSocks: string;
34
+ sizeLabels: {
35
+ sweater: string[];
36
+ cardigan: string[];
37
+ hat: string[];
38
+ scarf: string[];
39
+ socks: string[];
40
+ blanket: string[];
41
+ };
42
+ }
package/src/tools.ts ADDED
@@ -0,0 +1,16 @@
1
+ import type { ToolDefinition } from './types';
2
+ import { FABRIC_TRUTH_TOOL } from './tool/fabricTruth/index';
3
+ import { LAUNDRY_GUIDE_TOOL } from './tool/laundryGuide/index';
4
+ import { STAIN_CHEMISTRY_TOOL } from './tool/stainChemistry/index';
5
+ import { BURN_TEST_TOOL } from './tool/burnTest/index';
6
+ import { FIBER_PREP_TOOL } from './tool/fiberPrep/index';
7
+ import { SHOE_SIZE_CONVERTER_TOOL } from './tool/shoeSizeConverter/index';
8
+ import { CLOTHING_SIZE_CONVERTER_TOOL } from './tool/clothingSizeConverter/index';
9
+ import { KNITTING_GAUGE_TOOL } from './tool/knittingGauge/index';
10
+ import { FABRIC_PROJECT_CALCULATOR_TOOL } from './tool/fabricProjectCalculator/index';
11
+ import { SEWING_PATTERN_SCALER_TOOL } from './tool/sewingPatternScaler/index';
12
+ import { NEEDLE_CONVERTER_TOOL } from './tool/needleConverter/index';
13
+ import { YARN_CALCULATOR_TOOL } from './tool/yarnCalculator/index';
14
+
15
+ export const ALL_TOOLS: ToolDefinition[] = [FABRIC_TRUTH_TOOL, LAUNDRY_GUIDE_TOOL, STAIN_CHEMISTRY_TOOL, BURN_TEST_TOOL, FIBER_PREP_TOOL, SHOE_SIZE_CONVERTER_TOOL, CLOTHING_SIZE_CONVERTER_TOOL, KNITTING_GAUGE_TOOL, FABRIC_PROJECT_CALCULATOR_TOOL, SEWING_PATTERN_SCALER_TOOL, NEEDLE_CONVERTER_TOOL, YARN_CALCULATOR_TOOL];
16
+
package/src/types.ts ADDED
@@ -0,0 +1,72 @@
1
+ import type { SEOSection } from '@jjlmoya/utils-shared';
2
+ import type { WithContext, Thing } from 'schema-dts';
3
+
4
+ export type { SEOSection };
5
+
6
+ export type KnownLocale =
7
+ | 'ar' | 'da' | 'de' | 'en' | 'es' | 'fi'
8
+ | 'fr' | 'it' | 'ja' | 'ko' | 'nb' | 'nl'
9
+ | 'pl' | 'pt' | 'ru' | 'sv' | 'tr' | 'zh';
10
+
11
+ export interface FAQItem {
12
+ question: string;
13
+ answer: string;
14
+ }
15
+
16
+ export interface BibliographyEntry {
17
+ name: string;
18
+ url: string;
19
+ }
20
+
21
+ export interface HowToStep {
22
+ name: string;
23
+ text: string;
24
+ }
25
+
26
+ export interface ToolLocaleContent<TUI extends Record<string, unknown> = Record<string, unknown>> {
27
+ slug: string;
28
+ title: string;
29
+ description: string;
30
+ ui: TUI;
31
+ seo: SEOSection[];
32
+ faqTitle?: string;
33
+ faq: FAQItem[];
34
+ bibliographyTitle?: string;
35
+ bibliography: BibliographyEntry[];
36
+ howTo: HowToStep[];
37
+ schemas: WithContext<Thing>[];
38
+ }
39
+
40
+ export interface CategoryLocaleContent {
41
+ slug: string;
42
+ title: string;
43
+ description: string;
44
+ seo: SEOSection[];
45
+ }
46
+
47
+ export type LocaleLoader<T> = () => Promise<T>;
48
+
49
+ export type LocaleMap<T> = Partial<Record<KnownLocale, LocaleLoader<T>>>;
50
+
51
+ export interface TextilesToolEntry<TUI extends Record<string, unknown> = Record<string, unknown>> {
52
+ id: string;
53
+ icons: {
54
+ bg: string;
55
+ fg: string;
56
+ };
57
+ i18n: LocaleMap<ToolLocaleContent<TUI>>;
58
+ }
59
+
60
+ export interface TextilesCategoryEntry {
61
+ icon: string;
62
+ tools: TextilesToolEntry[];
63
+ i18n: LocaleMap<CategoryLocaleContent>;
64
+ }
65
+
66
+ export interface ToolDefinition {
67
+ entry: TextilesToolEntry;
68
+ Component: unknown;
69
+ SEOComponent: unknown;
70
+ BibliographyComponent: unknown;
71
+ }
72
+