@jjlmoya/utils-tools 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 +63 -0
  2. package/src/category/i18n/en.ts +172 -0
  3. package/src/category/i18n/es.ts +172 -0
  4. package/src/category/i18n/fr.ts +172 -0
  5. package/src/category/index.ts +23 -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 +90 -0
  12. package/src/layouts/PreviewLayout.astro +117 -0
  13. package/src/pages/[locale]/[slug].astro +146 -0
  14. package/src/pages/[locale].astro +251 -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/schemas_fulfillment.test.ts +23 -0
  21. package/src/tests/seo_length.test.ts +23 -0
  22. package/src/tests/title_quality.test.ts +56 -0
  23. package/src/tests/tool_validation.test.ts +17 -0
  24. package/src/tool/date-diff-calculator/bibliography.astro +14 -0
  25. package/src/tool/date-diff-calculator/component.astro +370 -0
  26. package/src/tool/date-diff-calculator/i18n/en.ts +132 -0
  27. package/src/tool/date-diff-calculator/i18n/es.ts +132 -0
  28. package/src/tool/date-diff-calculator/i18n/fr.ts +132 -0
  29. package/src/tool/date-diff-calculator/index.ts +22 -0
  30. package/src/tool/date-diff-calculator/seo.astro +14 -0
  31. package/src/tool/date-diff-calculator/ui.ts +17 -0
  32. package/src/tool/drive-direct-link/bibliography.astro +14 -0
  33. package/src/tool/drive-direct-link/component.astro +280 -0
  34. package/src/tool/drive-direct-link/i18n/en.ts +118 -0
  35. package/src/tool/drive-direct-link/i18n/es.ts +118 -0
  36. package/src/tool/drive-direct-link/i18n/fr.ts +118 -0
  37. package/src/tool/drive-direct-link/index.ts +22 -0
  38. package/src/tool/drive-direct-link/seo.astro +14 -0
  39. package/src/tool/drive-direct-link/ui.ts +10 -0
  40. package/src/tool/email-list-cleaner/bibliography.astro +14 -0
  41. package/src/tool/email-list-cleaner/component.astro +375 -0
  42. package/src/tool/email-list-cleaner/i18n/en.ts +140 -0
  43. package/src/tool/email-list-cleaner/i18n/es.ts +140 -0
  44. package/src/tool/email-list-cleaner/i18n/fr.ts +140 -0
  45. package/src/tool/email-list-cleaner/index.ts +22 -0
  46. package/src/tool/email-list-cleaner/seo.astro +14 -0
  47. package/src/tool/email-list-cleaner/ui.ts +15 -0
  48. package/src/tool/env-badge-spain/bibliography.astro +14 -0
  49. package/src/tool/env-badge-spain/component.astro +303 -0
  50. package/src/tool/env-badge-spain/components/BadgeForm.astro +243 -0
  51. package/src/tool/env-badge-spain/components/BadgeResult.astro +151 -0
  52. package/src/tool/env-badge-spain/i18n/en.ts +153 -0
  53. package/src/tool/env-badge-spain/i18n/es.ts +153 -0
  54. package/src/tool/env-badge-spain/i18n/fr.ts +153 -0
  55. package/src/tool/env-badge-spain/index.ts +22 -0
  56. package/src/tool/env-badge-spain/seo.astro +14 -0
  57. package/src/tool/env-badge-spain/ui.ts +53 -0
  58. package/src/tool/morse-beacon/bibliography.astro +14 -0
  59. package/src/tool/morse-beacon/component.astro +534 -0
  60. package/src/tool/morse-beacon/i18n/en.ts +157 -0
  61. package/src/tool/morse-beacon/i18n/es.ts +157 -0
  62. package/src/tool/morse-beacon/i18n/fr.ts +157 -0
  63. package/src/tool/morse-beacon/index.ts +22 -0
  64. package/src/tool/morse-beacon/logic/MorseEngine.ts +124 -0
  65. package/src/tool/morse-beacon/seo.astro +14 -0
  66. package/src/tool/morse-beacon/ui.ts +18 -0
  67. package/src/tool/password-generator/bibliography.astro +14 -0
  68. package/src/tool/password-generator/component.astro +259 -0
  69. package/src/tool/password-generator/components/Config.astro +227 -0
  70. package/src/tool/password-generator/components/Display.astro +147 -0
  71. package/src/tool/password-generator/components/Strength.astro +70 -0
  72. package/src/tool/password-generator/i18n/en.ts +166 -0
  73. package/src/tool/password-generator/i18n/es.ts +166 -0
  74. package/src/tool/password-generator/i18n/fr.ts +166 -0
  75. package/src/tool/password-generator/index.ts +22 -0
  76. package/src/tool/password-generator/seo.astro +14 -0
  77. package/src/tool/password-generator/ui.ts +16 -0
  78. package/src/tool/routes/bibliography.astro +14 -0
  79. package/src/tool/routes/component.astro +543 -0
  80. package/src/tool/routes/i18n/en.ts +157 -0
  81. package/src/tool/routes/i18n/es.ts +157 -0
  82. package/src/tool/routes/i18n/fr.ts +157 -0
  83. package/src/tool/routes/index.ts +22 -0
  84. package/src/tool/routes/logic/GeocodingService.ts +60 -0
  85. package/src/tool/routes/logic/RouteManager.ts +192 -0
  86. package/src/tool/routes/logic/RouteService.ts +66 -0
  87. package/src/tool/routes/seo.astro +14 -0
  88. package/src/tool/routes/ui.ts +16 -0
  89. package/src/tool/rule-of-three/bibliography.astro +14 -0
  90. package/src/tool/rule-of-three/component.astro +369 -0
  91. package/src/tool/rule-of-three/i18n/en.ts +171 -0
  92. package/src/tool/rule-of-three/i18n/es.ts +171 -0
  93. package/src/tool/rule-of-three/i18n/fr.ts +171 -0
  94. package/src/tool/rule-of-three/index.ts +22 -0
  95. package/src/tool/rule-of-three/seo.astro +14 -0
  96. package/src/tool/rule-of-three/ui.ts +13 -0
  97. package/src/tool/seo-content-optimizer/bibliography.astro +14 -0
  98. package/src/tool/seo-content-optimizer/component.astro +552 -0
  99. package/src/tool/seo-content-optimizer/i18n/en.ts +136 -0
  100. package/src/tool/seo-content-optimizer/i18n/es.ts +136 -0
  101. package/src/tool/seo-content-optimizer/i18n/fr.ts +136 -0
  102. package/src/tool/seo-content-optimizer/index.ts +22 -0
  103. package/src/tool/seo-content-optimizer/seo.astro +14 -0
  104. package/src/tool/seo-content-optimizer/ui.ts +29 -0
  105. package/src/tool/speed-reader/bibliography.astro +14 -0
  106. package/src/tool/speed-reader/component.astro +586 -0
  107. package/src/tool/speed-reader/i18n/en.ts +152 -0
  108. package/src/tool/speed-reader/i18n/es.ts +152 -0
  109. package/src/tool/speed-reader/i18n/fr.ts +152 -0
  110. package/src/tool/speed-reader/index.ts +22 -0
  111. package/src/tool/speed-reader/logic/RSVPEngine.ts +106 -0
  112. package/src/tool/speed-reader/seo.astro +14 -0
  113. package/src/tool/speed-reader/ui.ts +14 -0
  114. package/src/tool/text-pixel-calculator/bibliography.astro +14 -0
  115. package/src/tool/text-pixel-calculator/component.astro +315 -0
  116. package/src/tool/text-pixel-calculator/components/Editor.astro +240 -0
  117. package/src/tool/text-pixel-calculator/components/Preview.astro +155 -0
  118. package/src/tool/text-pixel-calculator/components/Stats.astro +87 -0
  119. package/src/tool/text-pixel-calculator/i18n/en.ts +133 -0
  120. package/src/tool/text-pixel-calculator/i18n/es.ts +133 -0
  121. package/src/tool/text-pixel-calculator/i18n/fr.ts +133 -0
  122. package/src/tool/text-pixel-calculator/index.ts +22 -0
  123. package/src/tool/text-pixel-calculator/seo.astro +14 -0
  124. package/src/tool/text-pixel-calculator/ui.ts +15 -0
  125. package/src/tool/whatsapp-link/bibliography.astro +14 -0
  126. package/src/tool/whatsapp-link/component.astro +455 -0
  127. package/src/tool/whatsapp-link/i18n/en.ts +128 -0
  128. package/src/tool/whatsapp-link/i18n/es.ts +128 -0
  129. package/src/tool/whatsapp-link/i18n/fr.ts +128 -0
  130. package/src/tool/whatsapp-link/index.ts +22 -0
  131. package/src/tool/whatsapp-link/seo.astro +14 -0
  132. package/src/tool/whatsapp-link/ui.ts +15 -0
  133. package/src/tools.ts +15 -0
  134. package/src/types.ts +72 -0
@@ -0,0 +1,128 @@
1
+ import type { ToolLocaleContent } from '../../../types';
2
+ import type { WithContext, FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
3
+ import type { WhatsappLinkUI } from '../ui';
4
+
5
+ const faqData = [
6
+ {
7
+ question: '¿Cómo genero un link de WhatsApp?',
8
+ answer: 'Para crear tu enlace escribe tu número móvil incluyendo el indicativo de país. Por ejemplo, para España pon primero el 34, para México el 52, seguido de tu número normal. Puedes añadir un mensaje opcional y la herramienta generará el enlace final (wa.me) que podrás copiar.',
9
+ },
10
+ {
11
+ question: '¿Puedo añadir un mensaje predefinido largo?',
12
+ answer: 'Sí. Puedes adjuntar un texto que aparecerá automáticamente en la caja de mensaje del usuario al presionar el enlace. Se usa la codificación de URLs del navegador para soportar espacios, tildes y emoticonos sin corromper la URL.',
13
+ },
14
+ {
15
+ question: '¿Cómo uso el código QR generado?',
16
+ answer: 'Al generar un link válido, aparecerá el botón "Mostrar QR". El código QR generado contiene la URL. Puedes hacer clic derecho y guardar la imagen para usarla en tarjetas de visita, carteles o redes sociales.',
17
+ },
18
+ {
19
+ question: '¿A dónde van mis datos?',
20
+ answer: 'El generador realiza toda la recolección, limpieza y codificación puramente del lado cliente, en tu navegador. Tu número de teléfono y mensaje predefinido no se envían a ningún servidor ni se almacenan en ninguna base de datos.',
21
+ },
22
+ ];
23
+
24
+ const howToData = [
25
+ { name: 'Seleccionar el prefijo', text: 'Elige el indicativo de tu país en el selector y escribe tu número de teléfono sin el prefijo.' },
26
+ { name: 'Añadir mensaje opcional', text: 'Escribe un texto predefinido que aparecerá cuando alguien abra el enlace y pulse Enviar.' },
27
+ { name: 'Generar el enlace', text: 'Pulsa el botón verde para obtener tu URL directa de wa.me lista para compartir.' },
28
+ { name: 'Compartir o imprimir QR', text: 'Copia el enlace, prueba el chat o descarga el código QR para tarjetas e impresión.' },
29
+ ];
30
+
31
+ const faqSchema: WithContext<FAQPage> = {
32
+ '@context': 'https://schema.org',
33
+ '@type': 'FAQPage',
34
+ mainEntity: faqData.map((item) => ({
35
+ '@type': 'Question',
36
+ name: item.question,
37
+ acceptedAnswer: { '@type': 'Answer', text: item.answer },
38
+ })),
39
+ };
40
+
41
+ const howToSchema: WithContext<HowTo> = {
42
+ '@context': 'https://schema.org',
43
+ '@type': 'HowTo',
44
+ name: 'Cómo crear un enlace directo de WhatsApp',
45
+ step: howToData.map((s) => ({ '@type': 'HowToStep', name: s.name, text: s.text })),
46
+ };
47
+
48
+ const appSchema: WithContext<SoftwareApplication> = {
49
+ '@context': 'https://schema.org',
50
+ '@type': 'SoftwareApplication',
51
+ name: 'Generador de Enlaces de WhatsApp',
52
+ applicationCategory: 'UtilitiesApplication',
53
+ operatingSystem: 'Web',
54
+ offers: { '@type': 'Offer', price: '0', priceCurrency: 'EUR' },
55
+ description: 'Crea enlaces directos a tu chat de WhatsApp con mensaje predefinido y código QR. Herramienta gratuita y 100% privada.',
56
+ };
57
+
58
+ const ui: WhatsappLinkUI = {
59
+ phoneLabel: 'Teléfono de WhatsApp',
60
+ phonePlaceholder: '600 00 00 00',
61
+ messageLabel: 'Mensaje inicial (opcional)',
62
+ messagePlaceholder: '¡Hola! Me gustaría más información sobre el servicio...',
63
+ generateBtn: 'Generar Enlace',
64
+ resultLabel: 'Link Directo Generado',
65
+ copyTitle: 'Copiar al portapapeles',
66
+ copyDone: '¡Copiado!',
67
+ testBtn: 'Probar Chat',
68
+ qrShow: 'Mostrar QR',
69
+ qrHide: 'Ocultar QR',
70
+ errorPhone: 'Por favor, introduce un número válido.',
71
+ defaultPrefix: '34',
72
+ };
73
+
74
+ export const content: ToolLocaleContent<WhatsappLinkUI> = {
75
+ slug: 'generador-enlaces-whatsapp',
76
+ title: 'Generador de Enlace WhatsApp con QR',
77
+ description: 'Crea enlaces directos a tu chat de WhatsApp con mensaje predefinido y código QR. Herramienta gratuita, sin registro y 100% privada.',
78
+ ui,
79
+ faqTitle: 'Preguntas Frecuentes',
80
+ faq: faqData,
81
+ howTo: howToData,
82
+ bibliographyTitle: 'Referencias',
83
+ bibliography: [
84
+ { name: 'Cómo usar la función clic para chatear — Centro de ayuda de WhatsApp', url: 'https://faq.whatsapp.com/591339899867293' },
85
+ { name: 'API Click to chat: parámetros y formatos — WhatsApp', url: 'https://faq.whatsapp.com/425559092497645' },
86
+ ],
87
+ schemas: [faqSchema, howToSchema, appSchema],
88
+ seo: [
89
+ { type: 'title', level: 2, text: 'Genera enlaces cortos y directos para WhatsApp' },
90
+ {
91
+ type: 'paragraph',
92
+ html: '¿Necesitas que clientes o seguidores te contacten por WhatsApp sin tener que guardar tu número? Nuestro <strong>creador de enlaces wa.me</strong> soluciona este problema generando un link que abre un chat directo al instante, sin pasos previos.',
93
+ },
94
+ { type: 'title', level: 3, text: '¿Para qué sirve el acortador wa.me de WhatsApp?' },
95
+ {
96
+ type: 'paragraph',
97
+ html: 'WhatsApp dispone de una API llamada "Clic para chatear". Mediante una URL especial, cualquier usuario puede abrir un chat nuevo contigo sin necesidad de añadirte como contacto previamente, tanto desde móvil como desde WhatsApp Web.',
98
+ },
99
+ {
100
+ type: 'list',
101
+ items: [
102
+ '<strong>Más conversiones:</strong> Un botón de "Contactar por WhatsApp" en tu tienda reduce la fricción y aumenta las ventas.',
103
+ '<strong>Mensaje predefinido:</strong> El usuario solo pulsa "Enviar". Ejemplo: "¡Hola! Vengo desde Instagram y quiero pedir la oferta."',
104
+ '<strong>Código QR automático:</strong> Descarga el QR para tarjetas de visita, carteles o publicaciones en redes.',
105
+ ],
106
+ },
107
+ { type: 'title', level: 3, text: '¿Cómo funciona la generación del enlace?' },
108
+ {
109
+ type: 'paragraph',
110
+ html: 'La herramienta concatena el prefijo internacional y el número limpio, y lo adhiere a la API oficial de WhatsApp junto al parámetro de texto convertido mediante <em>url-encoding</em>.',
111
+ },
112
+ {
113
+ type: 'code',
114
+ ariaLabel: 'Formato de URL de WhatsApp',
115
+ code: 'https://wa.me/34XXXXXXXXX\nhttps://wa.me/34XXXXXXXXX?text=%C2%A1Hola!%20Me%20gustar%C3%ADa...',
116
+ },
117
+ { type: 'title', level: 3, text: 'Privacidad garantizada y procesamiento local' },
118
+ {
119
+ type: 'paragraph',
120
+ html: 'Toda la construcción del enlace ocurre en tu navegador mediante JavaScript. Ningún servidor registra, guarda ni lee los números de teléfono ni los mensajes que introduces. Tu privacidad está completamente protegida.',
121
+ },
122
+ {
123
+ type: 'tip',
124
+ title: 'Importancia del prefijo internacional',
125
+ html: 'Para que WhatsApp dirija el mensaje correctamente, es obligatorio incluir el código de país. Para España es el <strong>34</strong>, seguido del número de 9 cifras, sin espacios ni el símbolo +. El resultado final sería, por ejemplo, <code>346XXXXXXXX</code>.',
126
+ },
127
+ ],
128
+ };
@@ -0,0 +1,128 @@
1
+ import type { ToolLocaleContent } from '../../../types';
2
+ import type { WithContext, FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
3
+ import type { WhatsappLinkUI } from '../ui';
4
+
5
+ const faqData = [
6
+ {
7
+ question: 'Comment générer un lien WhatsApp ?',
8
+ answer: "Pour créer votre lien, saisissez votre numéro de mobile en incluant l'indicatif de pays. Par exemple, pour l'Espagne mettez d'abord le 34, pour la France le 33, suivi de votre numéro local. Vous pouvez ajouter un message optionnel et l'outil générera le lien wa.me final prêt à copier.",
9
+ },
10
+ {
11
+ question: 'Puis-je ajouter un long message prédéfini ?',
12
+ answer: "Oui. Vous pouvez joindre un texte qui apparaîtra automatiquement dans la zone de message de l'utilisateur lorsqu'il clique sur le lien. L'outil utilise l'encodage URL pour prendre en charge les espaces, les accents et les emojis sans corrompre l'URL.",
13
+ },
14
+ {
15
+ question: 'Comment utiliser le QR code généré ?',
16
+ answer: 'Une fois un lien valide généré, le bouton "Afficher QR" apparaît. Le code QR contient votre URL. Faites un clic droit et sélectionnez "Enregistrer l\'image" pour télécharger un fichier haute résolution pour cartes de visite, affiches ou réseaux sociaux.',
17
+ },
18
+ {
19
+ question: 'Où vont mes données ?',
20
+ answer: "Le générateur traite tout côté client, directement dans votre navigateur. Votre numéro de téléphone et votre message prédéfini ne sont jamais envoyés à un serveur ni stockés dans une base de données.",
21
+ },
22
+ ];
23
+
24
+ const howToData = [
25
+ { name: "Sélectionner l'indicatif", text: "Choisissez l'indicatif de votre pays dans le menu déroulant et saisissez votre numéro sans le préfixe." },
26
+ { name: 'Ajouter un message optionnel', text: "Rédigez un texte prédéfini qui apparaîtra quand quelqu'un ouvrira le lien et appuiera sur Envoyer." },
27
+ { name: 'Générer le lien', text: "Appuyez sur le bouton vert pour obtenir votre URL directe wa.me prête à partager." },
28
+ { name: 'Partager ou imprimer le QR', text: "Copiez le lien, testez le chat ou téléchargez le code QR pour les cartes et l'impression." },
29
+ ];
30
+
31
+ const faqSchema: WithContext<FAQPage> = {
32
+ '@context': 'https://schema.org',
33
+ '@type': 'FAQPage',
34
+ mainEntity: faqData.map((item) => ({
35
+ '@type': 'Question',
36
+ name: item.question,
37
+ acceptedAnswer: { '@type': 'Answer', text: item.answer },
38
+ })),
39
+ };
40
+
41
+ const howToSchema: WithContext<HowTo> = {
42
+ '@context': 'https://schema.org',
43
+ '@type': 'HowTo',
44
+ name: 'Comment créer un lien WhatsApp direct',
45
+ step: howToData.map((s) => ({ '@type': 'HowToStep', name: s.name, text: s.text })),
46
+ };
47
+
48
+ const appSchema: WithContext<SoftwareApplication> = {
49
+ '@context': 'https://schema.org',
50
+ '@type': 'SoftwareApplication',
51
+ name: 'Générateur de Liens WhatsApp',
52
+ applicationCategory: 'UtilitiesApplication',
53
+ operatingSystem: 'Web',
54
+ offers: { '@type': 'Offer', price: '0', priceCurrency: 'EUR' },
55
+ description: "Créez des liens directs vers votre chat WhatsApp avec message prédéfini et code QR. Outil gratuit, sans inscription, 100% privé.",
56
+ };
57
+
58
+ const ui: WhatsappLinkUI = {
59
+ phoneLabel: 'Numéro WhatsApp',
60
+ phonePlaceholder: '06 00 00 00 00',
61
+ messageLabel: 'Message initial (optionnel)',
62
+ messagePlaceholder: "Bonjour ! Je souhaiterais plus d'informations sur votre service...",
63
+ generateBtn: 'Générer le lien',
64
+ resultLabel: 'Lien direct généré',
65
+ copyTitle: 'Copier dans le presse-papiers',
66
+ copyDone: 'Copié !',
67
+ testBtn: 'Tester le chat',
68
+ qrShow: 'Afficher QR',
69
+ qrHide: 'Masquer QR',
70
+ errorPhone: 'Veuillez saisir un numéro valide.',
71
+ defaultPrefix: '33',
72
+ };
73
+
74
+ export const content: ToolLocaleContent<WhatsappLinkUI> = {
75
+ slug: 'generateur-lien-whatsapp',
76
+ title: 'Générateur de Lien WhatsApp avec QR',
77
+ description: "Créez des liens directs vers votre chat WhatsApp avec message prédéfini et code QR. Outil gratuit, sans inscription, 100% privé.",
78
+ ui,
79
+ faqTitle: 'Questions Fréquentes',
80
+ faq: faqData,
81
+ howTo: howToData,
82
+ bibliographyTitle: 'Références',
83
+ bibliography: [
84
+ { name: "Comment utiliser la fonction clic pour chatter — Aide WhatsApp", url: 'https://faq.whatsapp.com/591339899867293' },
85
+ { name: "API Click to chat : paramètres et formats — WhatsApp", url: 'https://faq.whatsapp.com/425559092497645' },
86
+ ],
87
+ schemas: [faqSchema, howToSchema, appSchema],
88
+ seo: [
89
+ { type: 'title', level: 2, text: 'Générez des liens courts et directs pour WhatsApp' },
90
+ {
91
+ type: 'paragraph',
92
+ html: 'Vous avez besoin que des clients ou abonnés vous contactent sur WhatsApp sans avoir à enregistrer votre numéro ? Notre <strong>créateur de liens wa.me</strong> résout ce problème en générant un lien qui ouvre un chat direct instantanément, sans étapes préalables.',
93
+ },
94
+ { type: 'title', level: 3, text: "À quoi sert le raccourcisseur wa.me de WhatsApp ?" },
95
+ {
96
+ type: 'paragraph',
97
+ html: 'WhatsApp propose une API appelée "Clic pour chatter". Grâce à une URL spéciale, tout utilisateur peut ouvrir une nouvelle conversation avec vous sans avoir besoin de vous ajouter comme contact au préalable, depuis mobile comme depuis WhatsApp Web.',
98
+ },
99
+ {
100
+ type: 'list',
101
+ items: [
102
+ "<strong>Plus de conversions :</strong> Un bouton « Contacter via WhatsApp » sur votre boutique réduit la friction et augmente les ventes.",
103
+ "<strong>Message prédéfini :</strong> L'utilisateur appuie juste sur « Envoyer ». Exemple : « Bonjour ! Je viens d'Instagram et veux profiter de l'offre. »",
104
+ "<strong>Code QR automatique :</strong> Téléchargez le QR pour cartes de visite, affiches ou publications sur les réseaux.",
105
+ ],
106
+ },
107
+ { type: 'title', level: 3, text: 'Comment fonctionne la génération du lien ?' },
108
+ {
109
+ type: 'paragraph',
110
+ html: "L'outil concatène le préfixe international et le numéro nettoyé, puis l'ajoute à l'API officielle de WhatsApp avec le paramètre de message converti via <em>url-encoding</em>.",
111
+ },
112
+ {
113
+ type: 'code',
114
+ ariaLabel: 'Format URL WhatsApp',
115
+ code: 'https://wa.me/33XXXXXXXXX\nhttps://wa.me/33XXXXXXXXX?text=Bonjour%20%21%20Je%20souhaite...',
116
+ },
117
+ { type: 'title', level: 3, text: 'Confidentialité garantie et traitement local' },
118
+ {
119
+ type: 'paragraph',
120
+ html: 'Toute la construction du lien se fait dans votre navigateur via JavaScript. Aucun serveur n\'enregistre, ne sauvegarde ni ne lit les numéros de téléphone ou les messages que vous saisissez. Votre confidentialité est totalement protégée.',
121
+ },
122
+ {
123
+ type: 'tip',
124
+ title: 'Importance du préfixe international',
125
+ html: 'Pour que WhatsApp achemine correctement le message, l\'indicatif de pays est obligatoire. Pour la France c\'est le <strong>33</strong>, suivi du numéro à 9 chiffres (sans le 0 initial), sans espace ni symbole +. Le résultat final serait par exemple <code>336XXXXXXXX</code>.',
126
+ },
127
+ ],
128
+ };
@@ -0,0 +1,22 @@
1
+ import type { ToolDefinition, ToolsToolEntry } from '../../types';
2
+ import type { WhatsappLinkUI } from './ui';
3
+ import WhatsappLinkComponent from './component.astro';
4
+ import WhatsappLinkSEO from './seo.astro';
5
+ import WhatsappLinkBibliography from './bibliography.astro';
6
+
7
+ export const whatsappLink: ToolsToolEntry<WhatsappLinkUI> = {
8
+ id: 'whatsapp-link',
9
+ icons: { bg: 'mdi:whatsapp', fg: 'mdi:link-variant' },
10
+ i18n: {
11
+ es: () => import('./i18n/es').then((m) => m.content),
12
+ en: () => import('./i18n/en').then((m) => m.content),
13
+ fr: () => import('./i18n/fr').then((m) => m.content),
14
+ },
15
+ };
16
+
17
+ export const WHATSAPP_LINK_TOOL: ToolDefinition = {
18
+ entry: whatsappLink,
19
+ Component: WhatsappLinkComponent,
20
+ SEOComponent: WhatsappLinkSEO,
21
+ BibliographyComponent: WhatsappLinkBibliography,
22
+ };
@@ -0,0 +1,14 @@
1
+ ---
2
+ import { SEORenderer } from '@jjlmoya/utils-shared';
3
+ import { whatsappLink } 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 whatsappLink.i18n[locale]?.();
12
+ ---
13
+
14
+ {content && <SEORenderer content={{ locale, sections: content.seo }} />}
@@ -0,0 +1,15 @@
1
+ export interface WhatsappLinkUI extends Record<string, string> {
2
+ phoneLabel: string;
3
+ phonePlaceholder: string;
4
+ messageLabel: string;
5
+ messagePlaceholder: string;
6
+ generateBtn: string;
7
+ resultLabel: string;
8
+ copyTitle: string;
9
+ copyDone: string;
10
+ testBtn: string;
11
+ qrShow: string;
12
+ qrHide: string;
13
+ errorPhone: string;
14
+ defaultPrefix: string;
15
+ }
package/src/tools.ts ADDED
@@ -0,0 +1,15 @@
1
+ import type { ToolDefinition } from './types';
2
+ import { ROUTES_TOOL } from './tool/routes/index';
3
+ import { RULE_OF_THREE_TOOL } from './tool/rule-of-three/index';
4
+ import { PASSWORD_GENERATOR_TOOL } from './tool/password-generator/index';
5
+ import { MORSE_BEACON_TOOL } from './tool/morse-beacon/index';
6
+ import { SPEED_READER_TOOL } from './tool/speed-reader/index';
7
+ import { WHATSAPP_LINK_TOOL } from './tool/whatsapp-link/index';
8
+ import { TEXT_PIXEL_CALCULATOR_TOOL } from './tool/text-pixel-calculator/index';
9
+ import { DATE_DIFF_CALCULATOR_TOOL } from './tool/date-diff-calculator/index';
10
+ import { EMAIL_LIST_CLEANER_TOOL } from './tool/email-list-cleaner/index';
11
+ import { ENV_BADGE_SPAIN_TOOL } from './tool/env-badge-spain/index';
12
+ import { DRIVE_DIRECT_LINK_TOOL } from './tool/drive-direct-link/index';
13
+ import { SEO_CONTENT_OPTIMIZER_TOOL } from './tool/seo-content-optimizer/index';
14
+
15
+ export const ALL_TOOLS: ToolDefinition[] = [ROUTES_TOOL, RULE_OF_THREE_TOOL, PASSWORD_GENERATOR_TOOL, MORSE_BEACON_TOOL, SPEED_READER_TOOL, WHATSAPP_LINK_TOOL, TEXT_PIXEL_CALCULATOR_TOOL, DATE_DIFF_CALCULATOR_TOOL, EMAIL_LIST_CLEANER_TOOL, ENV_BADGE_SPAIN_TOOL, DRIVE_DIRECT_LINK_TOOL, SEO_CONTENT_OPTIMIZER_TOOL];
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, string> = Record<string, string>> {
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 ToolsToolEntry<TUI extends Record<string, string> = Record<string, string>> {
52
+ id: string;
53
+ icons: {
54
+ bg: string;
55
+ fg: string;
56
+ };
57
+ i18n: LocaleMap<ToolLocaleContent<TUI>>;
58
+ }
59
+
60
+ export interface ToolsCategoryEntry {
61
+ icon: string;
62
+ tools: ToolsToolEntry[];
63
+ i18n: LocaleMap<CategoryLocaleContent>;
64
+ }
65
+
66
+ export interface ToolDefinition {
67
+ entry: ToolsToolEntry;
68
+ Component: unknown;
69
+ SEOComponent: unknown;
70
+ BibliographyComponent: unknown;
71
+ }
72
+