@jjlmoya/utils-developer 1.2.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 (228) hide show
  1. package/package.json +64 -0
  2. package/src/category/i18n/en.ts +34 -0
  3. package/src/category/i18n/es.ts +34 -0
  4. package/src/category/i18n/fr.ts +34 -0
  5. package/src/category/index.ts +31 -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 +78 -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/css_specificity_logic.test.ts +39 -0
  17. package/src/tests/faq_count.test.ts +19 -0
  18. package/src/tests/locale_completeness.test.ts +42 -0
  19. package/src/tests/mocks/astro_mock.js +2 -0
  20. package/src/tests/no_h1_in_components.test.ts +48 -0
  21. package/src/tests/schemas_fulfillment.test.ts +23 -0
  22. package/src/tests/seo_length.test.ts +22 -0
  23. package/src/tests/title_quality.test.ts +55 -0
  24. package/src/tests/tool_validation.test.ts +17 -0
  25. package/src/tool/aspectRatio/bibliography.astro +14 -0
  26. package/src/tool/aspectRatio/component.astro +468 -0
  27. package/src/tool/aspectRatio/i18n/en.ts +177 -0
  28. package/src/tool/aspectRatio/i18n/es.ts +177 -0
  29. package/src/tool/aspectRatio/i18n/fr.ts +177 -0
  30. package/src/tool/aspectRatio/index.ts +34 -0
  31. package/src/tool/aspectRatio/seo.astro +14 -0
  32. package/src/tool/aspectRatio/ui.ts +12 -0
  33. package/src/tool/calculadoraTiempoDatos/bibliography.astro +14 -0
  34. package/src/tool/calculadoraTiempoDatos/component.astro +829 -0
  35. package/src/tool/calculadoraTiempoDatos/i18n/en.ts +150 -0
  36. package/src/tool/calculadoraTiempoDatos/i18n/es.ts +163 -0
  37. package/src/tool/calculadoraTiempoDatos/i18n/fr.ts +150 -0
  38. package/src/tool/calculadoraTiempoDatos/index.ts +33 -0
  39. package/src/tool/calculadoraTiempoDatos/seo.astro +14 -0
  40. package/src/tool/calculadoraTiempoDatos/ui.ts +24 -0
  41. package/src/tool/colorConverter/bibliography.astro +14 -0
  42. package/src/tool/colorConverter/component.astro +504 -0
  43. package/src/tool/colorConverter/i18n/en.ts +126 -0
  44. package/src/tool/colorConverter/i18n/es.ts +126 -0
  45. package/src/tool/colorConverter/i18n/fr.ts +126 -0
  46. package/src/tool/colorConverter/index.ts +33 -0
  47. package/src/tool/colorConverter/seo.astro +14 -0
  48. package/src/tool/colorConverter/ui.ts +16 -0
  49. package/src/tool/conversorExcelCsvHtml/bibliography.astro +14 -0
  50. package/src/tool/conversorExcelCsvHtml/component.astro +595 -0
  51. package/src/tool/conversorExcelCsvHtml/i18n/en.ts +129 -0
  52. package/src/tool/conversorExcelCsvHtml/i18n/es.ts +129 -0
  53. package/src/tool/conversorExcelCsvHtml/i18n/fr.ts +129 -0
  54. package/src/tool/conversorExcelCsvHtml/index.ts +33 -0
  55. package/src/tool/conversorExcelCsvHtml/seo.astro +14 -0
  56. package/src/tool/conversorExcelCsvHtml/ui.ts +14 -0
  57. package/src/tool/cronGenerator/bibliography.astro +14 -0
  58. package/src/tool/cronGenerator/component.astro +667 -0
  59. package/src/tool/cronGenerator/i18n/en.ts +191 -0
  60. package/src/tool/cronGenerator/i18n/es.ts +191 -0
  61. package/src/tool/cronGenerator/i18n/fr.ts +191 -0
  62. package/src/tool/cronGenerator/index.ts +34 -0
  63. package/src/tool/cronGenerator/seo.astro +14 -0
  64. package/src/tool/cronGenerator/ui.ts +45 -0
  65. package/src/tool/cssSpecificityCalculator/bibliography.astro +14 -0
  66. package/src/tool/cssSpecificityCalculator/component.astro +292 -0
  67. package/src/tool/cssSpecificityCalculator/i18n/en.ts +168 -0
  68. package/src/tool/cssSpecificityCalculator/i18n/es.ts +168 -0
  69. package/src/tool/cssSpecificityCalculator/i18n/fr.ts +168 -0
  70. package/src/tool/cssSpecificityCalculator/index.ts +34 -0
  71. package/src/tool/cssSpecificityCalculator/logic.ts +100 -0
  72. package/src/tool/cssSpecificityCalculator/seo.astro +14 -0
  73. package/src/tool/cssSpecificityCalculator/ui-controller.ts +57 -0
  74. package/src/tool/cssSpecificityCalculator/ui.ts +11 -0
  75. package/src/tool/cssToInlineConverter/bibliography.astro +14 -0
  76. package/src/tool/cssToInlineConverter/component.astro +308 -0
  77. package/src/tool/cssToInlineConverter/i18n/en.ts +160 -0
  78. package/src/tool/cssToInlineConverter/i18n/es.ts +160 -0
  79. package/src/tool/cssToInlineConverter/i18n/fr.ts +160 -0
  80. package/src/tool/cssToInlineConverter/index.ts +34 -0
  81. package/src/tool/cssToInlineConverter/seo.astro +14 -0
  82. package/src/tool/cssToInlineConverter/ui.ts +12 -0
  83. package/src/tool/duplicateCssRemover/bibliography.astro +14 -0
  84. package/src/tool/duplicateCssRemover/component.astro +353 -0
  85. package/src/tool/duplicateCssRemover/i18n/en.ts +167 -0
  86. package/src/tool/duplicateCssRemover/i18n/es.ts +167 -0
  87. package/src/tool/duplicateCssRemover/i18n/fr.ts +167 -0
  88. package/src/tool/duplicateCssRemover/index.ts +34 -0
  89. package/src/tool/duplicateCssRemover/seo.astro +14 -0
  90. package/src/tool/duplicateCssRemover/ui.ts +12 -0
  91. package/src/tool/generadorSecurityTxt/bibliography.astro +14 -0
  92. package/src/tool/generadorSecurityTxt/component.astro +473 -0
  93. package/src/tool/generadorSecurityTxt/i18n/en.ts +173 -0
  94. package/src/tool/generadorSecurityTxt/i18n/es.ts +173 -0
  95. package/src/tool/generadorSecurityTxt/i18n/fr.ts +173 -0
  96. package/src/tool/generadorSecurityTxt/index.ts +33 -0
  97. package/src/tool/generadorSecurityTxt/seo.astro +14 -0
  98. package/src/tool/generadorSecurityTxt/ui.ts +27 -0
  99. package/src/tool/hashGenerator/bibliography.astro +14 -0
  100. package/src/tool/hashGenerator/component.astro +367 -0
  101. package/src/tool/hashGenerator/i18n/en.ts +118 -0
  102. package/src/tool/hashGenerator/i18n/es.ts +118 -0
  103. package/src/tool/hashGenerator/i18n/fr.ts +118 -0
  104. package/src/tool/hashGenerator/index.ts +33 -0
  105. package/src/tool/hashGenerator/md5.ts +138 -0
  106. package/src/tool/hashGenerator/seo.astro +14 -0
  107. package/src/tool/hashGenerator/ui.ts +11 -0
  108. package/src/tool/inspectorCertificadosSsl/bibliography.astro +14 -0
  109. package/src/tool/inspectorCertificadosSsl/component.astro +580 -0
  110. package/src/tool/inspectorCertificadosSsl/i18n/en.ts +214 -0
  111. package/src/tool/inspectorCertificadosSsl/i18n/es.ts +214 -0
  112. package/src/tool/inspectorCertificadosSsl/i18n/fr.ts +214 -0
  113. package/src/tool/inspectorCertificadosSsl/index.ts +33 -0
  114. package/src/tool/inspectorCertificadosSsl/seo.astro +14 -0
  115. package/src/tool/inspectorCertificadosSsl/ui.ts +29 -0
  116. package/src/tool/jsonFormatter/bibliography.astro +14 -0
  117. package/src/tool/jsonFormatter/component.astro +590 -0
  118. package/src/tool/jsonFormatter/i18n/en.ts +156 -0
  119. package/src/tool/jsonFormatter/i18n/es.ts +156 -0
  120. package/src/tool/jsonFormatter/i18n/fr.ts +156 -0
  121. package/src/tool/jsonFormatter/index.ts +34 -0
  122. package/src/tool/jsonFormatter/seo.astro +14 -0
  123. package/src/tool/jsonFormatter/ui.ts +15 -0
  124. package/src/tool/keycode/bibliography.astro +14 -0
  125. package/src/tool/keycode/component.astro +540 -0
  126. package/src/tool/keycode/i18n/en.ts +160 -0
  127. package/src/tool/keycode/i18n/es.ts +160 -0
  128. package/src/tool/keycode/i18n/fr.ts +160 -0
  129. package/src/tool/keycode/index.ts +34 -0
  130. package/src/tool/keycode/seo.astro +14 -0
  131. package/src/tool/keycode/ui.ts +10 -0
  132. package/src/tool/llmCostCalculator/bibliography.astro +14 -0
  133. package/src/tool/llmCostCalculator/component.astro +488 -0
  134. package/src/tool/llmCostCalculator/data.ts +99 -0
  135. package/src/tool/llmCostCalculator/i18n/en.ts +165 -0
  136. package/src/tool/llmCostCalculator/i18n/es.ts +165 -0
  137. package/src/tool/llmCostCalculator/i18n/fr.ts +165 -0
  138. package/src/tool/llmCostCalculator/index.ts +34 -0
  139. package/src/tool/llmCostCalculator/seo.astro +14 -0
  140. package/src/tool/llmCostCalculator/ui.ts +11 -0
  141. package/src/tool/mobileMockupGenerator/app.ts +614 -0
  142. package/src/tool/mobileMockupGenerator/bibliography.astro +14 -0
  143. package/src/tool/mobileMockupGenerator/component.astro +1044 -0
  144. package/src/tool/mobileMockupGenerator/constants.ts +48 -0
  145. package/src/tool/mobileMockupGenerator/i18n/en.ts +121 -0
  146. package/src/tool/mobileMockupGenerator/i18n/es.ts +121 -0
  147. package/src/tool/mobileMockupGenerator/i18n/fr.ts +121 -0
  148. package/src/tool/mobileMockupGenerator/index.ts +33 -0
  149. package/src/tool/mobileMockupGenerator/rendering.ts +189 -0
  150. package/src/tool/mobileMockupGenerator/seo.astro +14 -0
  151. package/src/tool/mobileMockupGenerator/store.ts +145 -0
  152. package/src/tool/mobileMockupGenerator/types.ts +41 -0
  153. package/src/tool/mobileMockupGenerator/ui.ts +48 -0
  154. package/src/tool/musicalTypography/bibliography.astro +14 -0
  155. package/src/tool/musicalTypography/component.astro +480 -0
  156. package/src/tool/musicalTypography/i18n/en.ts +179 -0
  157. package/src/tool/musicalTypography/i18n/es.ts +179 -0
  158. package/src/tool/musicalTypography/i18n/fr.ts +179 -0
  159. package/src/tool/musicalTypography/index.ts +33 -0
  160. package/src/tool/musicalTypography/seo.astro +14 -0
  161. package/src/tool/musicalTypography/ui.ts +25 -0
  162. package/src/tool/placeholderGenerator/bibliography.astro +14 -0
  163. package/src/tool/placeholderGenerator/component.astro +499 -0
  164. package/src/tool/placeholderGenerator/i18n/en.ts +176 -0
  165. package/src/tool/placeholderGenerator/i18n/es.ts +176 -0
  166. package/src/tool/placeholderGenerator/i18n/fr.ts +176 -0
  167. package/src/tool/placeholderGenerator/index.ts +34 -0
  168. package/src/tool/placeholderGenerator/seo.astro +14 -0
  169. package/src/tool/placeholderGenerator/ui.ts +14 -0
  170. package/src/tool/promptLibrary/bibliography.astro +14 -0
  171. package/src/tool/promptLibrary/component.astro +1131 -0
  172. package/src/tool/promptLibrary/controller.ts +49 -0
  173. package/src/tool/promptLibrary/i18n/en.ts +131 -0
  174. package/src/tool/promptLibrary/i18n/es.ts +131 -0
  175. package/src/tool/promptLibrary/i18n/fr.ts +126 -0
  176. package/src/tool/promptLibrary/index.ts +33 -0
  177. package/src/tool/promptLibrary/seo.astro +14 -0
  178. package/src/tool/promptLibrary/ui.ts +29 -0
  179. package/src/tool/readabilityCalculator/bibliography.astro +14 -0
  180. package/src/tool/readabilityCalculator/component.astro +534 -0
  181. package/src/tool/readabilityCalculator/i18n/en.ts +133 -0
  182. package/src/tool/readabilityCalculator/i18n/es.ts +133 -0
  183. package/src/tool/readabilityCalculator/i18n/fr.ts +133 -0
  184. package/src/tool/readabilityCalculator/index.ts +33 -0
  185. package/src/tool/readabilityCalculator/seo.astro +14 -0
  186. package/src/tool/readabilityCalculator/ui.ts +27 -0
  187. package/src/tool/svgSanitizer/bibliography.astro +14 -0
  188. package/src/tool/svgSanitizer/component.astro +554 -0
  189. package/src/tool/svgSanitizer/i18n/en.ts +125 -0
  190. package/src/tool/svgSanitizer/i18n/es.ts +125 -0
  191. package/src/tool/svgSanitizer/i18n/fr.ts +125 -0
  192. package/src/tool/svgSanitizer/index.ts +33 -0
  193. package/src/tool/svgSanitizer/seo.astro +14 -0
  194. package/src/tool/svgSanitizer/ui.ts +18 -0
  195. package/src/tool/svgToCss/bibliography.astro +14 -0
  196. package/src/tool/svgToCss/component.astro +381 -0
  197. package/src/tool/svgToCss/i18n/en.ts +174 -0
  198. package/src/tool/svgToCss/i18n/es.ts +174 -0
  199. package/src/tool/svgToCss/i18n/fr.ts +174 -0
  200. package/src/tool/svgToCss/index.ts +34 -0
  201. package/src/tool/svgToCss/seo.astro +14 -0
  202. package/src/tool/svgToCss/ui.ts +13 -0
  203. package/src/tool/urlCleaner/bibliography.astro +14 -0
  204. package/src/tool/urlCleaner/component.astro +458 -0
  205. package/src/tool/urlCleaner/i18n/en.ts +116 -0
  206. package/src/tool/urlCleaner/i18n/es.ts +116 -0
  207. package/src/tool/urlCleaner/i18n/fr.ts +116 -0
  208. package/src/tool/urlCleaner/index.ts +33 -0
  209. package/src/tool/urlCleaner/seo.astro +14 -0
  210. package/src/tool/urlCleaner/ui.ts +12 -0
  211. package/src/tool/urlEncoderDecoder/bibliography.astro +14 -0
  212. package/src/tool/urlEncoderDecoder/component.astro +291 -0
  213. package/src/tool/urlEncoderDecoder/i18n/en.ts +162 -0
  214. package/src/tool/urlEncoderDecoder/i18n/es.ts +162 -0
  215. package/src/tool/urlEncoderDecoder/i18n/fr.ts +162 -0
  216. package/src/tool/urlEncoderDecoder/index.ts +34 -0
  217. package/src/tool/urlEncoderDecoder/seo.astro +14 -0
  218. package/src/tool/urlEncoderDecoder/ui.ts +11 -0
  219. package/src/tool/utmGenerator/bibliography.astro +14 -0
  220. package/src/tool/utmGenerator/component.astro +270 -0
  221. package/src/tool/utmGenerator/i18n/en.ts +117 -0
  222. package/src/tool/utmGenerator/i18n/es.ts +117 -0
  223. package/src/tool/utmGenerator/i18n/fr.ts +117 -0
  224. package/src/tool/utmGenerator/index.ts +33 -0
  225. package/src/tool/utmGenerator/seo.astro +14 -0
  226. package/src/tool/utmGenerator/ui.ts +13 -0
  227. package/src/tools.ts +27 -0
  228. package/src/types.ts +72 -0
@@ -0,0 +1,116 @@
1
+ import type { WithContext, FAQPage, HowToThing, SoftwareApplication } from 'schema-dts';
2
+ import type { ToolLocaleContent } from '../../../types';
3
+ import type { UrlCleanerUI } from '../ui';
4
+
5
+ const slug = 'nettoyeur-suivi-url';
6
+ const title = 'Nettoyeur de Suivi d\'URL: Supprimer UTM, FBCLID et GCLID';
7
+ const description = 'Supprimez automatiquement les paramètres de suivi et de publicité de vos URLs. Partagez des liens propres et protégez votre vie privée numérique instantanément.';
8
+
9
+ const faqData = [
10
+ {
11
+ question: 'Quels types de paramètres de suivi cet outil supprime-t-il?',
12
+ answer: 'Notre outil détecte et supprime automatiquement les paramètres de suivi les plus courants, notamment UTM (utm_source, utm_medium, etc.), les identifiants de clics publicitaires (fbclid, gclid, msclkid) et les identifiants de campagne de marketing par email (mc_cid, _hsenc).',
13
+ },
14
+ {
15
+ question: 'Cela affecte-t-il la fonctionnalité du site Web?',
16
+ answer: 'Généralement non. Ces paramètres sont utilisés presque exclusivement pour l\'analyse et le marketing. Lorsque vous les supprimez, la page se charge normalement, mais le propriétaire du site ne pourra pas suivre la source exacte de votre clic.',
17
+ },
18
+ {
19
+ question: 'Est-il sûr de traiter mes liens ici?',
20
+ answer: 'Absolument. Comme tous nos outils, le processus est 100% côté client. Vos liens ne sont jamais envoyés à nos serveurs; tout se passe en privé dans votre propre navigateur.',
21
+ },
22
+ {
23
+ question: 'Pourquoi mes liens Facebook sont-ils si longs?',
24
+ answer: 'Facebook ajoute un paramètre appelé "fbclid" à tous les liens qui quittent sa plate-forme. Cela leur permet de suivre votre activité sur d\'autres sites Web même si vous avez bloqué la publicité tierce.',
25
+ },
26
+ ];
27
+
28
+ const howToData = [
29
+ { name: 'Collez votre URL', text: 'Entrez l\'URL complète contenant des paramètres de suivi' },
30
+ { name: 'Cliquez sur Nettoyer', text: 'L\'outil analysera automatiquement l\'URL' },
31
+ { name: 'Examinez les résultats', text: 'Vous verrez l\'URL propre et une liste des paramètres supprimés' },
32
+ { name: 'Copiez et partagez', text: 'Utilisez l\'URL propre dans vos e-mails, réseaux sociaux ou messages' },
33
+ ];
34
+
35
+ const faqSchema: WithContext<FAQPage> = {
36
+ '@context': 'https://schema.org',
37
+ '@type': 'FAQPage',
38
+ mainEntity: faqData.map((item) => ({
39
+ '@type': 'Question',
40
+ name: item.question,
41
+ acceptedAnswer: { '@type': 'Answer', text: item.answer },
42
+ })),
43
+ };
44
+
45
+ const howToSchema: WithContext<HowToThing> = {
46
+ '@context': 'https://schema.org',
47
+ '@type': 'HowTo',
48
+ name: title,
49
+ description,
50
+ step: howToData.map((s, i) => ({ '@type': 'HowToStep', position: i + 1, name: s.name, text: s.text })),
51
+ };
52
+
53
+ const appSchema: WithContext<SoftwareApplication> = {
54
+ '@context': 'https://schema.org',
55
+ '@type': 'SoftwareApplication',
56
+ name: title,
57
+ description,
58
+ applicationCategory: 'UtilityApplication',
59
+ operatingSystem: 'All',
60
+ offers: { '@type': 'Offer', price: '0', priceCurrency: 'EUR' },
61
+ inLanguage: 'fr',
62
+ };
63
+
64
+ const ui: UrlCleanerUI = {
65
+ labelInput: 'Collez l\'URL avec les paramètres de suivi',
66
+ btnClean: 'Nettoyer',
67
+ labelCleaned: 'URL Propre',
68
+ labelRemoved: 'Traceurs Supprimés',
69
+ countLabel: 'Paramètres supprimés',
70
+ reductionLabel: 'Réduction de longueur',
71
+ noneDetected: 'Aucun paramètre de suivi courant détecté.',
72
+ errorInvalid: 'Veuillez entrer une URL valide.',
73
+ btnCopy: 'Copier',
74
+ btnCopied: 'Copié',
75
+ };
76
+
77
+ export const content: ToolLocaleContent<UrlCleanerUI> = {
78
+ slug,
79
+ title,
80
+ description,
81
+ ui,
82
+ faqTitle: 'Questions Fréquemment Posées',
83
+ faq: faqData,
84
+ bibliographyTitle: 'Ressources sur la Confidentialité et le Suivi Web',
85
+ bibliography: [
86
+ { name: 'Electronic Frontier Foundation (EFF): Guide sur le Suivi en Ligne', url: 'https://www.eff.org/issues/online-behavioral-tracking' },
87
+ { name: 'Google Analytics: Documentation des Paramètres UTM', url: 'https://support.google.com/analytics/answer/1033863' },
88
+ { name: 'Confidentialité sur le Web: L\'impact des Click IDs', url: 'https://www.w3.org/community/web-advertising/wiki/Click_Identifiers' },
89
+ ],
90
+ howTo: howToData,
91
+ schemas: [faqSchema, howToSchema, appSchema],
92
+ seo: [
93
+ { type: 'title', text: 'Nettoyeur de Suivi d\'URL: Supprimer UTM, FBCLID et GCLID', level: 2 },
94
+ {
95
+ type: 'paragraph',
96
+ html: 'Supprimez les traceurs invisibles de vos liens pour les partager de manière propre, privée et professionnelle. Découvrez ce que signifient ces étranges codes dans vos URLs.',
97
+ },
98
+ { type: 'title', text: 'Qu\'est-ce qu\'un nettoyeur de suivi d\'URL et pourquoi l\'avez-vous besoin?', level: 3 },
99
+ {
100
+ type: 'paragraph',
101
+ html: 'Avez-vous déjà copié un lien pour l\'envoyer à un ami et réalisé qu\'il était trois fois plus long qu\'il ne devrait l\'être? Ce "bruit" supplémentaire provient des paramètres de suivi. Un <strong>nettoyeur de suivi</strong> est un outil conçu pour "dénuder" l\'URL de toutes les informations publicitaires et de suivi que les grandes plates-formes injectent dans chaque clic que vous effectuez.',
102
+ },
103
+ { type: 'title', text: 'Paramètres de suivi les plus courants', level: 3 },
104
+ {
105
+ type: 'list',
106
+ items: [
107
+ '<strong>UTM (Google Analytics):</strong> utm_source, utm_medium, utm_campaign pour suivre les campagnes',
108
+ '<strong>FBCLID:</strong> Identifiant de clic Facebook pour contourner les restrictions de cookies',
109
+ '<strong>GCLID / DCLID:</strong> Google Click ID pour lier les visites aux campagnes Google Ads',
110
+ '<strong>MSCLKID:</strong> Identifiant de clic Microsoft Advertising et Bing',
111
+ '<strong>HubSpot & Mailchimp:</strong> Paramètres d\'automatisation marketing comme _hsenc, mc_cid',
112
+ '<strong>LinkedIn & Autres:</strong> li_fat_id et autres traceurs de réseaux sociaux',
113
+ ],
114
+ },
115
+ ],
116
+ };
@@ -0,0 +1,33 @@
1
+ import type { DeveloperToolEntry, ToolLocaleContent, ToolDefinition } from '../../types';
2
+ import UrlCleanerComponent from './component.astro';
3
+ import UrlCleanerSEO from './seo.astro';
4
+ import UrlCleanerBibliography from './bibliography.astro';
5
+ import type { UrlCleanerUI } from './ui';
6
+
7
+ export type UrlCleanerLocaleContent = ToolLocaleContent<UrlCleanerUI>;
8
+
9
+ import { content as es } from './i18n/es';
10
+ import { content as en } from './i18n/en';
11
+ import { content as fr } from './i18n/fr';
12
+
13
+ export const urlCleaner: DeveloperToolEntry<UrlCleanerUI> = {
14
+ id: 'url-cleaner',
15
+ icons: {
16
+ bg: 'mdi:link-variant-off',
17
+ fg: 'mdi:broom',
18
+ },
19
+ i18n: {
20
+ es: async () => es,
21
+ en: async () => en,
22
+ fr: async () => fr,
23
+ },
24
+ };
25
+
26
+ export { UrlCleanerComponent, UrlCleanerSEO, UrlCleanerBibliography };
27
+
28
+ export const URL_CLEANER_TOOL: ToolDefinition = {
29
+ entry: urlCleaner,
30
+ Component: UrlCleanerComponent,
31
+ SEOComponent: UrlCleanerSEO,
32
+ BibliographyComponent: UrlCleanerBibliography,
33
+ };
@@ -0,0 +1,14 @@
1
+ ---
2
+ import { SEORenderer } from '@jjlmoya/utils-shared';
3
+ import { urlCleaner } 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 urlCleaner.i18n[locale]?.();
12
+ ---
13
+
14
+ {content && <SEORenderer content={{ locale, sections: content.seo }} />}
@@ -0,0 +1,12 @@
1
+ export interface UrlCleanerUI extends Record<string, string> {
2
+ labelInput: string;
3
+ btnClean: string;
4
+ labelCleaned: string;
5
+ labelRemoved: string;
6
+ countLabel: string;
7
+ reductionLabel: string;
8
+ noneDetected: string;
9
+ errorInvalid: string;
10
+ btnCopy: string;
11
+ btnCopied: string;
12
+ }
@@ -0,0 +1,14 @@
1
+ ---
2
+ import { Bibliography as SharedBibliography } from '@jjlmoya/utils-shared';
3
+ import { urlEncoderDecoder } 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 urlEncoderDecoder.i18n[locale]?.();
12
+ ---
13
+
14
+ {content && <SharedBibliography links={content.bibliography} />}
@@ -0,0 +1,291 @@
1
+ ---
2
+ import type { KnownLocale } from '../../types';
3
+ import type { UrlEncoderDecoderUI } from './ui';
4
+
5
+ interface Props {
6
+ locale?: KnownLocale;
7
+ ui?: Record<string, unknown>;
8
+ }
9
+
10
+ const { ui } = Astro.props;
11
+ const t = (ui ?? {}) as UrlEncoderDecoderUI;
12
+ ---
13
+
14
+ <div
15
+ class="urlc-root"
16
+ data-btn-copy={t.btnCopy}
17
+ data-btn-copied={t.btnCopied}
18
+ >
19
+ <div class="urlc-card">
20
+ <div class="urlc-field">
21
+ <div class="urlc-field-header">
22
+ <span class="urlc-field-label">{t.labelRaw}</span>
23
+ <div class="urlc-toolbar">
24
+ <button id="urlc-clear-raw" class="urlc-toolbar-btn">{t.btnClear}</button>
25
+ <button id="urlc-copy-raw" class="urlc-toolbar-btn">{t.btnCopy}</button>
26
+ </div>
27
+ </div>
28
+ <textarea
29
+ id="urlc-input-raw"
30
+ class="urlc-textarea"
31
+ placeholder={t.placeholderRaw}
32
+ spellcheck="false"
33
+ ></textarea>
34
+ </div>
35
+
36
+ <div class="urlc-actions">
37
+ <button id="urlc-encode-btn" class="urlc-btn urlc-btn-encode">
38
+ <svg class="urlc-btn-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
39
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
40
+ </svg>
41
+ {t.btnEncode}
42
+ </button>
43
+ <button id="urlc-decode-btn" class="urlc-btn urlc-btn-decode">
44
+ <svg class="urlc-btn-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
45
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 15l7-7 7 7"></path>
46
+ </svg>
47
+ {t.btnDecode}
48
+ </button>
49
+ </div>
50
+
51
+ <div class="urlc-field">
52
+ <div class="urlc-field-header">
53
+ <span class="urlc-field-label">{t.labelEncoded}</span>
54
+ <div class="urlc-toolbar">
55
+ <button id="urlc-clear-enc" class="urlc-toolbar-btn">{t.btnClear}</button>
56
+ <button id="urlc-copy-enc" class="urlc-toolbar-btn">{t.btnCopy}</button>
57
+ </div>
58
+ </div>
59
+ <textarea
60
+ id="urlc-input-encoded"
61
+ class="urlc-textarea urlc-textarea-encoded"
62
+ placeholder={t.placeholderEncoded}
63
+ spellcheck="false"
64
+ ></textarea>
65
+ </div>
66
+ </div>
67
+ </div>
68
+
69
+ <style>
70
+ .urlc-root {
71
+ --urlc-primary: #f59e0b;
72
+ --urlc-primary-dark: #d97706;
73
+ --urlc-secondary: #3b82f6;
74
+ --urlc-secondary-dark: #2563eb;
75
+ --urlc-card-bg: #fff;
76
+ --urlc-bg: #f8fafc;
77
+ --urlc-text: #0f172a;
78
+ --urlc-text-muted: #64748b;
79
+ --urlc-border: #e2e8f0;
80
+ --urlc-encoded-bg: rgba(245, 158, 11, 0.03);
81
+
82
+ max-width: 1000px;
83
+ margin: 2rem auto;
84
+ color: var(--urlc-text);
85
+ }
86
+
87
+ :global(.theme-dark) .urlc-root {
88
+ --urlc-card-bg: #1e293b;
89
+ --urlc-bg: #0f172a;
90
+ --urlc-text: #f1f5f9;
91
+ --urlc-text-muted: #94a3b8;
92
+ --urlc-border: #334155;
93
+ --urlc-encoded-bg: rgba(245, 158, 11, 0.06);
94
+ }
95
+
96
+ .urlc-card {
97
+ background: var(--urlc-card-bg);
98
+ border: 1px solid var(--urlc-border);
99
+ border-radius: 1.25rem;
100
+ box-shadow: 0 4px 24px rgba(0, 0, 0, 0.06);
101
+ padding: 1.75rem;
102
+ display: flex;
103
+ flex-direction: column;
104
+ gap: 1.25rem;
105
+ }
106
+
107
+ .urlc-field {
108
+ display: flex;
109
+ flex-direction: column;
110
+ gap: 0.5rem;
111
+ }
112
+
113
+ .urlc-field-header {
114
+ display: flex;
115
+ justify-content: space-between;
116
+ align-items: center;
117
+ }
118
+
119
+ .urlc-field-label {
120
+ font-size: 0.8rem;
121
+ font-weight: 700;
122
+ color: var(--urlc-text-muted);
123
+ text-transform: uppercase;
124
+ letter-spacing: 0.06em;
125
+ }
126
+
127
+ .urlc-toolbar {
128
+ display: flex;
129
+ gap: 0.375rem;
130
+ }
131
+
132
+ .urlc-toolbar-btn {
133
+ background: transparent;
134
+ border: 1px solid var(--urlc-border);
135
+ color: var(--urlc-text-muted);
136
+ padding: 0.2rem 0.6rem;
137
+ border-radius: 0.4rem;
138
+ font-size: 0.75rem;
139
+ font-weight: 600;
140
+ cursor: pointer;
141
+ transition: all 0.15s;
142
+ }
143
+
144
+ .urlc-toolbar-btn:hover {
145
+ background: var(--urlc-bg);
146
+ color: var(--urlc-text);
147
+ border-color: var(--urlc-text-muted);
148
+ }
149
+
150
+ .urlc-textarea {
151
+ width: 100%;
152
+ height: 160px;
153
+ padding: 1rem 1.25rem;
154
+ font-size: 0.9rem;
155
+ color: var(--urlc-text);
156
+ background: var(--urlc-bg);
157
+ border: 1px solid var(--urlc-border);
158
+ border-radius: 0.875rem;
159
+ resize: vertical;
160
+ transition: border-color 0.2s, box-shadow 0.2s;
161
+ line-height: 1.6;
162
+ outline: none;
163
+ }
164
+
165
+ .urlc-textarea:focus {
166
+ border-color: var(--urlc-secondary);
167
+ box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.12);
168
+ }
169
+
170
+ .urlc-textarea-encoded {
171
+ border-color: var(--urlc-primary);
172
+ background: var(--urlc-encoded-bg);
173
+ }
174
+
175
+ .urlc-textarea-encoded:focus {
176
+ border-color: var(--urlc-primary);
177
+ box-shadow: 0 0 0 3px rgba(245, 158, 11, 0.12);
178
+ }
179
+
180
+ .urlc-actions {
181
+ display: flex;
182
+ justify-content: center;
183
+ gap: 0.875rem;
184
+ }
185
+
186
+ @media (max-width: 480px) {
187
+ .urlc-actions {
188
+ flex-direction: column;
189
+ }
190
+ }
191
+
192
+ .urlc-btn {
193
+ display: flex;
194
+ align-items: center;
195
+ justify-content: center;
196
+ gap: 0.5rem;
197
+ font-weight: 700;
198
+ font-size: 0.95rem;
199
+ padding: 0.75rem 1.75rem;
200
+ border-radius: 0.75rem;
201
+ border: none;
202
+ cursor: pointer;
203
+ transition: transform 0.15s, box-shadow 0.15s, background 0.15s;
204
+ }
205
+
206
+ .urlc-btn:hover {
207
+ transform: translateY(-2px);
208
+ }
209
+
210
+ .urlc-btn-encode {
211
+ background: var(--urlc-primary);
212
+ color: #fff;
213
+ box-shadow: 0 4px 12px rgba(245, 158, 11, 0.3);
214
+ }
215
+
216
+ .urlc-btn-encode:hover {
217
+ background: var(--urlc-primary-dark);
218
+ box-shadow: 0 6px 16px rgba(245, 158, 11, 0.4);
219
+ }
220
+
221
+ .urlc-btn-decode {
222
+ background: var(--urlc-secondary);
223
+ color: #fff;
224
+ box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3);
225
+ }
226
+
227
+ .urlc-btn-decode:hover {
228
+ background: var(--urlc-secondary-dark);
229
+ box-shadow: 0 6px 16px rgba(59, 130, 246, 0.4);
230
+ }
231
+
232
+ .urlc-btn-icon {
233
+ width: 18px;
234
+ height: 18px;
235
+ }
236
+ </style>
237
+
238
+ <script>
239
+ const root = document.querySelector('.urlc-root') as HTMLElement | null;
240
+ const inputRaw = document.getElementById('urlc-input-raw') as HTMLTextAreaElement;
241
+ const inputEnc = document.getElementById('urlc-input-encoded') as HTMLTextAreaElement;
242
+ const btnEncode = document.getElementById('urlc-encode-btn');
243
+ const btnDecode = document.getElementById('urlc-decode-btn');
244
+ const copyRaw = document.getElementById('urlc-copy-raw');
245
+ const copyEnc = document.getElementById('urlc-copy-enc');
246
+ const clearRaw = document.getElementById('urlc-clear-raw');
247
+ const clearEnc = document.getElementById('urlc-clear-enc');
248
+
249
+ const txtCopy = root?.dataset.btnCopy ?? 'Copy';
250
+ const txtCopied = root?.dataset.btnCopied ?? 'Copied!';
251
+
252
+ const encode = () => {
253
+ if (!inputRaw.value) return;
254
+ try {
255
+ inputEnc.value = encodeURIComponent(inputRaw.value);
256
+ } catch {
257
+ inputEnc.value = '';
258
+ }
259
+ };
260
+
261
+ const decode = () => {
262
+ if (!inputEnc.value) return;
263
+ try {
264
+ inputRaw.value = decodeURIComponent(inputEnc.value);
265
+ } catch {
266
+ inputRaw.value = '';
267
+ }
268
+ };
269
+
270
+ const setupCopy = (btn: HTMLElement | null, input: HTMLTextAreaElement) => {
271
+ if (!btn) return;
272
+ btn.addEventListener('click', async () => {
273
+ if (!input.value) return;
274
+ await navigator.clipboard.writeText(input.value);
275
+ btn.textContent = txtCopied;
276
+ setTimeout(() => { btn.textContent = txtCopy; }, 1500);
277
+ });
278
+ };
279
+
280
+ const setupClear = (btn: HTMLElement | null, input: HTMLTextAreaElement) => {
281
+ if (!btn) return;
282
+ btn.addEventListener('click', () => { input.value = ''; input.focus(); });
283
+ };
284
+
285
+ btnEncode?.addEventListener('click', encode);
286
+ btnDecode?.addEventListener('click', decode);
287
+ setupCopy(copyRaw, inputRaw);
288
+ setupCopy(copyEnc, inputEnc);
289
+ setupClear(clearRaw, inputRaw);
290
+ setupClear(clearEnc, inputEnc);
291
+ </script>
@@ -0,0 +1,162 @@
1
+ import type { WithContext, FAQPage, HowToThing, SoftwareApplication } from 'schema-dts';
2
+ import type { ToolLocaleContent } from '../../../types';
3
+ import type { UrlEncoderDecoderUI } from '../ui';
4
+
5
+ const slug = 'url-encoder-decoder';
6
+ const title = 'URL Encoder and Decoder Online';
7
+ const description =
8
+ 'Convert special characters from any link to safe format (Percent-Encoding) or return them to their original readable state instantly and locally.';
9
+
10
+ const faqData = [
11
+ {
12
+ question: 'Which characters get encoded in a URL?',
13
+ answer:
14
+ 'All characters not permitted in the ASCII standard for URLs are encoded: spaces, accented letters, symbols such as &, =, +, #, ?, / and others. For example, a space becomes %20 and ñ becomes %C3%B1.',
15
+ },
16
+ {
17
+ question: 'What is the difference between encodeURI and encodeURIComponent?',
18
+ answer:
19
+ 'encodeURI encodes a complete URL and leaves reserved characters such as / and ? intact. encodeURIComponent encodes all special characters including reserved ones, making it ideal for encoding individual query parameter values.',
20
+ },
21
+ {
22
+ question: 'Why does my URL have %20 instead of spaces?',
23
+ answer:
24
+ 'The HTTP protocol does not allow spaces in URLs. %20 is the Percent-Encoding representation of a space according to the ASCII standard. Some systems use the + sign as an alternative, but %20 is the most universal and safe.',
25
+ },
26
+ {
27
+ question: 'Is it safe to use this tool with private URLs?',
28
+ answer:
29
+ 'Yes, completely safe. All processing happens in your browser using native JavaScript (encodeURIComponent/decodeURIComponent). None of your URLs or parameters are sent to any external server.',
30
+ },
31
+ ];
32
+
33
+ const howToData = [
34
+ {
35
+ name: 'Paste the raw text',
36
+ text: 'Type or paste your URL or text string into the upper field "Raw Text (Readable)".',
37
+ },
38
+ {
39
+ name: 'Encode or decode',
40
+ text: 'Click "Encode URL" to convert the text to safe Percent-Encoding format, or "Decode" to revert an encoded URL to its readable form.',
41
+ },
42
+ {
43
+ name: 'Copy the result',
44
+ text: 'Use the "Copy" button of the corresponding field to take the result to your clipboard.',
45
+ },
46
+ ];
47
+
48
+ const faqSchema: WithContext<FAQPage> = {
49
+ '@context': 'https://schema.org',
50
+ '@type': 'FAQPage',
51
+ mainEntity: faqData.map((item) => ({
52
+ '@type': 'Question',
53
+ name: item.question,
54
+ acceptedAnswer: { '@type': 'Answer', text: item.answer },
55
+ })),
56
+ };
57
+
58
+ const howToSchema: WithContext<HowToThing> = {
59
+ '@context': 'https://schema.org',
60
+ '@type': 'HowTo',
61
+ name: title,
62
+ description,
63
+ step: howToData.map((step, i) => ({
64
+ '@type': 'HowToStep',
65
+ position: i + 1,
66
+ name: step.name,
67
+ text: step.text,
68
+ })),
69
+ };
70
+
71
+ const appSchema: WithContext<SoftwareApplication> = {
72
+ '@context': 'https://schema.org',
73
+ '@type': 'SoftwareApplication',
74
+ name: title,
75
+ description,
76
+ applicationCategory: 'UtilityApplication',
77
+ operatingSystem: 'All',
78
+ offers: { '@type': 'Offer', price: '0', priceCurrency: 'EUR' },
79
+ inLanguage: 'en',
80
+ };
81
+
82
+ const ui: UrlEncoderDecoderUI = {
83
+ labelRaw: 'Raw Text (Readable)',
84
+ labelEncoded: 'Formatted URL (Encoded)',
85
+ btnClear: 'Clear',
86
+ btnCopy: 'Copy',
87
+ btnCopied: 'Copied!',
88
+ btnEncode: 'Encode URL',
89
+ btnDecode: 'Decode',
90
+ placeholderRaw: 'https://example.com/search?q=red high heels&size=38',
91
+ placeholderEncoded: 'https%3A%2F%2Fexample.com%2Fsearch%3Fq%3Dred%20high%20heels%26size%3D38',
92
+ };
93
+
94
+ export const content: ToolLocaleContent<UrlEncoderDecoderUI> = {
95
+ slug,
96
+ title,
97
+ description,
98
+ ui,
99
+ faqTitle: 'Frequently Asked Questions',
100
+ faq: faqData,
101
+ bibliographyTitle: 'References and Documentation',
102
+ bibliography: [
103
+ {
104
+ name: 'MDN Web Docs: encodeURIComponent()',
105
+ url: 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent',
106
+ },
107
+ {
108
+ name: 'IETF RFC 3986: URI Generic Syntax',
109
+ url: 'https://datatracker.ietf.org/doc/html/rfc3986',
110
+ },
111
+ {
112
+ name: 'W3C: URL Living Standard',
113
+ url: 'https://url.spec.whatwg.org/',
114
+ },
115
+ ],
116
+ howTo: howToData,
117
+ schemas: [faqSchema, howToSchema, appSchema],
118
+ seo: [
119
+ {
120
+ type: 'title',
121
+ text: 'What is URL Encoding?',
122
+ level: 2,
123
+ },
124
+ {
125
+ type: 'paragraph',
126
+ html: 'When browsing the internet or sending requests to servers, it is common to think of a URL (Uniform Resource Locator) as simply a "web address". However, internet protocol dictates that URLs can only be transmitted using a very restricted set of <strong>standard ASCII</strong> characters.',
127
+ },
128
+ {
129
+ type: 'paragraph',
130
+ html: 'What happens if the URL contains a space, accented letters, or special parameters such as plus (<code>+</code>) or equals (<code>=</code>) signs? To prevent systems from collapsing when trying to read illegal characters, these must be translated to their safe compatible form using <strong>Percent-Encoding</strong>.',
131
+ },
132
+ {
133
+ type: 'title',
134
+ text: 'How Percent-Encoding Works',
135
+ level: 3,
136
+ },
137
+ {
138
+ type: 'paragraph',
139
+ html: 'When you use this tool, the algorithm takes any "unsafe" character (such as a space or an accented letter like ñ) and replaces it with a percent sign <code>%</code> followed by two hexadecimal digits corresponding to its value in the UTF-8 standard.',
140
+ },
141
+ {
142
+ type: 'list',
143
+ items: [
144
+ '<strong>Basic Example:</strong> A simple space will be replaced by its safe equivalent: <code>%20</code>.',
145
+ '<strong>Extended Support:</strong> The letter <code>á</code> becomes <code>%C3%A1</code>, and <code>ñ</code> becomes <code>%C3%B1</code>.',
146
+ ],
147
+ },
148
+ {
149
+ type: 'title',
150
+ text: 'Importance in APIs and GET Requests',
151
+ level: 3,
152
+ },
153
+ {
154
+ type: 'paragraph',
155
+ html: 'When developing integrations, a typical mistake is passing a raw string to URL parameters. If you insert <code>shirt&blue</code> raw into your backend (<code>/search?q=shirt&blue</code>), the server will interpret <code>blue</code> as a new parameter, breaking all the logic.',
156
+ },
157
+ {
158
+ type: 'paragraph',
159
+ html: 'This tool guarantees clean, automatic calculations with 100% execution in your local browser. None of your URL strings are transmitted to third-party servers, ensuring the privacy of your tokens and analytics parameters.',
160
+ },
161
+ ],
162
+ };