@jjlmoya/utils-tools 1.11.0 → 1.13.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 (234) hide show
  1. package/package.json +1 -1
  2. package/src/pages/[locale]/[slug].astro +30 -13
  3. package/src/tests/locale_completeness.test.ts +3 -20
  4. package/src/tests/shared-test-helpers.ts +56 -0
  5. package/src/tests/tool_exports.test.ts +34 -0
  6. package/src/tool/date-diff-calculator/bibliography.astro +2 -10
  7. package/src/tool/date-diff-calculator/bibliography.ts +7 -0
  8. package/src/tool/date-diff-calculator/date-difference-calculator.css +1 -1
  9. package/src/tool/date-diff-calculator/i18n/de.ts +4 -7
  10. package/src/tool/date-diff-calculator/i18n/en.ts +4 -7
  11. package/src/tool/date-diff-calculator/i18n/es.ts +4 -7
  12. package/src/tool/date-diff-calculator/i18n/fr.ts +4 -7
  13. package/src/tool/date-diff-calculator/i18n/id.ts +4 -7
  14. package/src/tool/date-diff-calculator/i18n/it.ts +4 -7
  15. package/src/tool/date-diff-calculator/i18n/ja.ts +4 -7
  16. package/src/tool/date-diff-calculator/i18n/ko.ts +4 -7
  17. package/src/tool/date-diff-calculator/i18n/nl.ts +4 -7
  18. package/src/tool/date-diff-calculator/i18n/pl.ts +4 -7
  19. package/src/tool/date-diff-calculator/i18n/pt.ts +4 -7
  20. package/src/tool/date-diff-calculator/i18n/ru.ts +4 -7
  21. package/src/tool/date-diff-calculator/i18n/sv.ts +4 -7
  22. package/src/tool/date-diff-calculator/i18n/tr.ts +4 -7
  23. package/src/tool/date-diff-calculator/i18n/zh.ts +4 -7
  24. package/src/tool/date-diff-calculator/seo.astro +2 -1
  25. package/src/tool/drive-direct-link/bibliography.astro +2 -10
  26. package/src/tool/drive-direct-link/bibliography.ts +6 -0
  27. package/src/tool/drive-direct-link/google-drive-direct-download-link.css +1 -1
  28. package/src/tool/drive-direct-link/i18n/de.ts +4 -6
  29. package/src/tool/drive-direct-link/i18n/en.ts +4 -6
  30. package/src/tool/drive-direct-link/i18n/es.ts +4 -6
  31. package/src/tool/drive-direct-link/i18n/fr.ts +4 -6
  32. package/src/tool/drive-direct-link/i18n/id.ts +4 -6
  33. package/src/tool/drive-direct-link/i18n/it.ts +4 -6
  34. package/src/tool/drive-direct-link/i18n/ja.ts +4 -6
  35. package/src/tool/drive-direct-link/i18n/ko.ts +4 -6
  36. package/src/tool/drive-direct-link/i18n/nl.ts +4 -6
  37. package/src/tool/drive-direct-link/i18n/pl.ts +4 -6
  38. package/src/tool/drive-direct-link/i18n/pt.ts +4 -6
  39. package/src/tool/drive-direct-link/i18n/ru.ts +4 -6
  40. package/src/tool/drive-direct-link/i18n/sv.ts +4 -6
  41. package/src/tool/drive-direct-link/i18n/tr.ts +4 -6
  42. package/src/tool/drive-direct-link/i18n/zh.ts +4 -6
  43. package/src/tool/drive-direct-link/seo.astro +2 -1
  44. package/src/tool/email-list-cleaner/bibliography.astro +2 -10
  45. package/src/tool/email-list-cleaner/bibliography.ts +7 -0
  46. package/src/tool/email-list-cleaner/email-list-cleaner.css +1 -1
  47. package/src/tool/email-list-cleaner/i18n/de.ts +4 -7
  48. package/src/tool/email-list-cleaner/i18n/en.ts +4 -7
  49. package/src/tool/email-list-cleaner/i18n/es.ts +4 -7
  50. package/src/tool/email-list-cleaner/i18n/fr.ts +4 -7
  51. package/src/tool/email-list-cleaner/i18n/id.ts +4 -7
  52. package/src/tool/email-list-cleaner/i18n/it.ts +4 -7
  53. package/src/tool/email-list-cleaner/i18n/ja.ts +4 -7
  54. package/src/tool/email-list-cleaner/i18n/ko.ts +4 -7
  55. package/src/tool/email-list-cleaner/i18n/nl.ts +4 -7
  56. package/src/tool/email-list-cleaner/i18n/pl.ts +4 -7
  57. package/src/tool/email-list-cleaner/i18n/pt.ts +4 -7
  58. package/src/tool/email-list-cleaner/i18n/ru.ts +4 -7
  59. package/src/tool/email-list-cleaner/i18n/sv.ts +4 -7
  60. package/src/tool/email-list-cleaner/i18n/tr.ts +4 -7
  61. package/src/tool/email-list-cleaner/i18n/zh.ts +4 -7
  62. package/src/tool/email-list-cleaner/seo.astro +2 -1
  63. package/src/tool/env-badge-spain/bibliography.astro +2 -10
  64. package/src/tool/env-badge-spain/bibliography.ts +6 -0
  65. package/src/tool/env-badge-spain/environmental-badge-simulator-spain.css +17 -17
  66. package/src/tool/env-badge-spain/i18n/de.ts +4 -6
  67. package/src/tool/env-badge-spain/i18n/en.ts +4 -6
  68. package/src/tool/env-badge-spain/i18n/es.ts +4 -6
  69. package/src/tool/env-badge-spain/i18n/fr.ts +4 -6
  70. package/src/tool/env-badge-spain/i18n/id.ts +4 -6
  71. package/src/tool/env-badge-spain/i18n/it.ts +4 -6
  72. package/src/tool/env-badge-spain/i18n/ja.ts +4 -6
  73. package/src/tool/env-badge-spain/i18n/ko.ts +4 -6
  74. package/src/tool/env-badge-spain/i18n/nl.ts +4 -6
  75. package/src/tool/env-badge-spain/i18n/pl.ts +4 -6
  76. package/src/tool/env-badge-spain/i18n/pt.ts +4 -6
  77. package/src/tool/env-badge-spain/i18n/ru.ts +4 -6
  78. package/src/tool/env-badge-spain/i18n/sv.ts +4 -6
  79. package/src/tool/env-badge-spain/i18n/tr.ts +4 -6
  80. package/src/tool/env-badge-spain/i18n/zh.ts +4 -6
  81. package/src/tool/env-badge-spain/seo.astro +2 -1
  82. package/src/tool/morse-beacon/bibliography.astro +2 -10
  83. package/src/tool/morse-beacon/bibliography.ts +7 -0
  84. package/src/tool/morse-beacon/i18n/de.ts +4 -7
  85. package/src/tool/morse-beacon/i18n/en.ts +4 -7
  86. package/src/tool/morse-beacon/i18n/es.ts +4 -7
  87. package/src/tool/morse-beacon/i18n/fr.ts +4 -7
  88. package/src/tool/morse-beacon/i18n/id.ts +4 -7
  89. package/src/tool/morse-beacon/i18n/it.ts +4 -7
  90. package/src/tool/morse-beacon/i18n/ja.ts +4 -7
  91. package/src/tool/morse-beacon/i18n/ko.ts +4 -7
  92. package/src/tool/morse-beacon/i18n/nl.ts +4 -7
  93. package/src/tool/morse-beacon/i18n/pl.ts +4 -7
  94. package/src/tool/morse-beacon/i18n/pt.ts +4 -7
  95. package/src/tool/morse-beacon/i18n/ru.ts +4 -7
  96. package/src/tool/morse-beacon/i18n/sv.ts +4 -7
  97. package/src/tool/morse-beacon/i18n/tr.ts +4 -7
  98. package/src/tool/morse-beacon/i18n/zh.ts +4 -7
  99. package/src/tool/morse-beacon/morse-beacon.css +1 -1
  100. package/src/tool/morse-beacon/seo.astro +2 -1
  101. package/src/tool/password-generator/bibliography.astro +2 -10
  102. package/src/tool/password-generator/bibliography.ts +8 -0
  103. package/src/tool/password-generator/i18n/de.ts +4 -8
  104. package/src/tool/password-generator/i18n/en.ts +4 -8
  105. package/src/tool/password-generator/i18n/es.ts +4 -8
  106. package/src/tool/password-generator/i18n/fr.ts +4 -8
  107. package/src/tool/password-generator/i18n/id.ts +4 -8
  108. package/src/tool/password-generator/i18n/it.ts +4 -8
  109. package/src/tool/password-generator/i18n/ja.ts +4 -8
  110. package/src/tool/password-generator/i18n/ko.ts +4 -8
  111. package/src/tool/password-generator/i18n/nl.ts +4 -8
  112. package/src/tool/password-generator/i18n/pl.ts +4 -8
  113. package/src/tool/password-generator/i18n/pt.ts +4 -8
  114. package/src/tool/password-generator/i18n/ru.ts +4 -8
  115. package/src/tool/password-generator/i18n/sv.ts +4 -8
  116. package/src/tool/password-generator/i18n/tr.ts +4 -8
  117. package/src/tool/password-generator/i18n/zh.ts +4 -8
  118. package/src/tool/password-generator/password-generator.css +4 -4
  119. package/src/tool/password-generator/seo.astro +2 -1
  120. package/src/tool/routes/bibliography.astro +2 -10
  121. package/src/tool/routes/bibliography.ts +9 -0
  122. package/src/tool/routes/i18n/de.ts +4 -9
  123. package/src/tool/routes/i18n/en.ts +4 -9
  124. package/src/tool/routes/i18n/es.ts +4 -9
  125. package/src/tool/routes/i18n/fr.ts +4 -9
  126. package/src/tool/routes/i18n/id.ts +4 -9
  127. package/src/tool/routes/i18n/it.ts +4 -9
  128. package/src/tool/routes/i18n/ja.ts +4 -9
  129. package/src/tool/routes/i18n/ko.ts +4 -9
  130. package/src/tool/routes/i18n/nl.ts +4 -9
  131. package/src/tool/routes/i18n/pl.ts +4 -9
  132. package/src/tool/routes/i18n/pt.ts +4 -9
  133. package/src/tool/routes/i18n/ru.ts +4 -9
  134. package/src/tool/routes/i18n/sv.ts +4 -9
  135. package/src/tool/routes/i18n/tr.ts +4 -9
  136. package/src/tool/routes/i18n/zh.ts +4 -9
  137. package/src/tool/routes/optimal-routes.css +2 -2
  138. package/src/tool/routes/seo.astro +2 -1
  139. package/src/tool/rule-of-three/bibliography.astro +2 -10
  140. package/src/tool/rule-of-three/bibliography.ts +6 -0
  141. package/src/tool/rule-of-three/i18n/de.ts +4 -6
  142. package/src/tool/rule-of-three/i18n/en.ts +4 -6
  143. package/src/tool/rule-of-three/i18n/es.ts +4 -6
  144. package/src/tool/rule-of-three/i18n/fr.ts +4 -6
  145. package/src/tool/rule-of-three/i18n/id.ts +4 -6
  146. package/src/tool/rule-of-three/i18n/it.ts +4 -6
  147. package/src/tool/rule-of-three/i18n/ja.ts +4 -6
  148. package/src/tool/rule-of-three/i18n/ko.ts +4 -6
  149. package/src/tool/rule-of-three/i18n/nl.ts +4 -6
  150. package/src/tool/rule-of-three/i18n/pl.ts +4 -6
  151. package/src/tool/rule-of-three/i18n/pt.ts +4 -6
  152. package/src/tool/rule-of-three/i18n/ru.ts +4 -6
  153. package/src/tool/rule-of-three/i18n/sv.ts +4 -6
  154. package/src/tool/rule-of-three/i18n/tr.ts +4 -6
  155. package/src/tool/rule-of-three/i18n/zh.ts +4 -6
  156. package/src/tool/rule-of-three/rule-of-three.css +2 -2
  157. package/src/tool/rule-of-three/seo.astro +2 -1
  158. package/src/tool/seo-content-optimizer/bibliography.astro +2 -10
  159. package/src/tool/seo-content-optimizer/bibliography.ts +6 -0
  160. package/src/tool/seo-content-optimizer/i18n/de.ts +4 -6
  161. package/src/tool/seo-content-optimizer/i18n/en.ts +4 -6
  162. package/src/tool/seo-content-optimizer/i18n/es.ts +4 -6
  163. package/src/tool/seo-content-optimizer/i18n/fr.ts +4 -6
  164. package/src/tool/seo-content-optimizer/i18n/id.ts +4 -6
  165. package/src/tool/seo-content-optimizer/i18n/it.ts +4 -6
  166. package/src/tool/seo-content-optimizer/i18n/ja.ts +4 -6
  167. package/src/tool/seo-content-optimizer/i18n/ko.ts +4 -6
  168. package/src/tool/seo-content-optimizer/i18n/nl.ts +4 -6
  169. package/src/tool/seo-content-optimizer/i18n/pl.ts +4 -6
  170. package/src/tool/seo-content-optimizer/i18n/pt.ts +4 -6
  171. package/src/tool/seo-content-optimizer/i18n/ru.ts +4 -6
  172. package/src/tool/seo-content-optimizer/i18n/sv.ts +4 -6
  173. package/src/tool/seo-content-optimizer/i18n/tr.ts +4 -6
  174. package/src/tool/seo-content-optimizer/i18n/zh.ts +4 -6
  175. package/src/tool/seo-content-optimizer/seo-content-optimizer.css +10 -10
  176. package/src/tool/seo-content-optimizer/seo.astro +2 -1
  177. package/src/tool/speed-reader/bibliography.astro +2 -10
  178. package/src/tool/speed-reader/bibliography.ts +10 -0
  179. package/src/tool/speed-reader/i18n/de.ts +4 -10
  180. package/src/tool/speed-reader/i18n/en.ts +4 -10
  181. package/src/tool/speed-reader/i18n/es.ts +4 -10
  182. package/src/tool/speed-reader/i18n/fr.ts +4 -10
  183. package/src/tool/speed-reader/i18n/id.ts +4 -10
  184. package/src/tool/speed-reader/i18n/it.ts +4 -10
  185. package/src/tool/speed-reader/i18n/ja.ts +4 -10
  186. package/src/tool/speed-reader/i18n/ko.ts +4 -10
  187. package/src/tool/speed-reader/i18n/nl.ts +4 -10
  188. package/src/tool/speed-reader/i18n/pl.ts +4 -10
  189. package/src/tool/speed-reader/i18n/pt.ts +4 -10
  190. package/src/tool/speed-reader/i18n/ru.ts +4 -10
  191. package/src/tool/speed-reader/i18n/sv.ts +4 -10
  192. package/src/tool/speed-reader/i18n/tr.ts +4 -10
  193. package/src/tool/speed-reader/i18n/zh.ts +4 -10
  194. package/src/tool/speed-reader/seo.astro +2 -1
  195. package/src/tool/speed-reader/speed-reader.css +5 -5
  196. package/src/tool/text-pixel-calculator/bibliography.astro +2 -10
  197. package/src/tool/text-pixel-calculator/bibliography.ts +6 -0
  198. package/src/tool/text-pixel-calculator/i18n/de.ts +4 -6
  199. package/src/tool/text-pixel-calculator/i18n/en.ts +4 -6
  200. package/src/tool/text-pixel-calculator/i18n/es.ts +4 -6
  201. package/src/tool/text-pixel-calculator/i18n/fr.ts +4 -6
  202. package/src/tool/text-pixel-calculator/i18n/id.ts +4 -6
  203. package/src/tool/text-pixel-calculator/i18n/it.ts +4 -6
  204. package/src/tool/text-pixel-calculator/i18n/ja.ts +4 -6
  205. package/src/tool/text-pixel-calculator/i18n/ko.ts +4 -6
  206. package/src/tool/text-pixel-calculator/i18n/nl.ts +4 -6
  207. package/src/tool/text-pixel-calculator/i18n/pl.ts +4 -6
  208. package/src/tool/text-pixel-calculator/i18n/pt.ts +4 -6
  209. package/src/tool/text-pixel-calculator/i18n/ru.ts +4 -6
  210. package/src/tool/text-pixel-calculator/i18n/sv.ts +4 -6
  211. package/src/tool/text-pixel-calculator/i18n/tr.ts +4 -6
  212. package/src/tool/text-pixel-calculator/i18n/zh.ts +4 -6
  213. package/src/tool/text-pixel-calculator/seo.astro +2 -1
  214. package/src/tool/text-pixel-calculator/text-pixel-width-calculator.css +1 -1
  215. package/src/tool/whatsapp-link/bibliography.astro +2 -10
  216. package/src/tool/whatsapp-link/bibliography.ts +6 -0
  217. package/src/tool/whatsapp-link/i18n/de.ts +4 -6
  218. package/src/tool/whatsapp-link/i18n/en.ts +4 -6
  219. package/src/tool/whatsapp-link/i18n/es.ts +4 -6
  220. package/src/tool/whatsapp-link/i18n/fr.ts +4 -6
  221. package/src/tool/whatsapp-link/i18n/id.ts +4 -6
  222. package/src/tool/whatsapp-link/i18n/it.ts +4 -6
  223. package/src/tool/whatsapp-link/i18n/ja.ts +4 -6
  224. package/src/tool/whatsapp-link/i18n/ko.ts +4 -6
  225. package/src/tool/whatsapp-link/i18n/nl.ts +4 -6
  226. package/src/tool/whatsapp-link/i18n/pl.ts +4 -6
  227. package/src/tool/whatsapp-link/i18n/pt.ts +4 -6
  228. package/src/tool/whatsapp-link/i18n/ru.ts +4 -6
  229. package/src/tool/whatsapp-link/i18n/sv.ts +4 -6
  230. package/src/tool/whatsapp-link/i18n/tr.ts +4 -6
  231. package/src/tool/whatsapp-link/i18n/zh.ts +4 -6
  232. package/src/tool/whatsapp-link/seo.astro +2 -1
  233. package/src/tool/whatsapp-link/whatsapp-link-generator.css +2 -2
  234. package/src/types.ts +0 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jjlmoya/utils-tools",
3
- "version": "1.11.0",
3
+ "version": "1.13.0",
4
4
  "type": "module",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
@@ -34,18 +34,28 @@ export async function getStaticPaths() {
34
34
  ]),
35
35
  ) as Partial<Record<KnownLocale, string>>;
36
36
 
37
+ const firstLoader = entry.i18n.en ?? Object.values(entry.i18n)[0];
38
+ const englishSlug = firstLoader ? (await firstLoader()).slug : entry.id;
39
+
37
40
  for (const { locale, content } of localeContents) {
38
- const allToolsNav = await Promise.all(
39
- ALL_TOOLS.map(async ({ entry: navEntry }) => ({
40
- id: navEntry.id,
41
- title: (await navEntry.i18n[locale]!()).title,
42
- href: `/${locale}/${(await navEntry.i18n[locale]!()).slug}`,
43
- isActive: navEntry.id === entry.id,
44
- })),
45
- );
41
+ const allToolsNav = (
42
+ await Promise.all(
43
+ ALL_TOOLS.map(async ({ entry: navEntry }) => {
44
+ const loader = navEntry.i18n[locale] ?? navEntry.i18n.en;
45
+ if (!loader) return null;
46
+ const navContent = await loader();
47
+ return {
48
+ id: navEntry.id,
49
+ title: navContent.title,
50
+ href: `/${locale}/${navContent.slug}`,
51
+ isActive: navEntry.id === entry.id,
52
+ };
53
+ }),
54
+ )
55
+ ).filter(Boolean) as NavItem[];
46
56
  paths.push({
47
57
  params: { locale, slug: content.slug },
48
- props: { Component, locale, content, localeUrls, allToolsNav },
58
+ props: { Component, locale, content, localeUrls, allToolsNav, englishSlug },
49
59
  });
50
60
  }
51
61
  }
@@ -66,11 +76,17 @@ interface Props {
66
76
  content: ToolLocaleContent;
67
77
  localeUrls: Partial<Record<KnownLocale, string>>;
68
78
  allToolsNav: NavItem[];
79
+ englishSlug: string;
69
80
  }
70
81
 
71
- const { Component, locale, content, localeUrls, allToolsNav } = Astro.props;
82
+ const { Component, locale, content, localeUrls, allToolsNav, englishSlug } = Astro.props;
83
+
84
+ // eslint-disable-next-line custom/no-css-comments
85
+ const cssFiles = import.meta.glob("../../tool/**/*.css", { query: "?raw", import: "default" });
86
+ const cssKey = Object.keys(cssFiles).find((k) => k.endsWith(`/${englishSlug}.css`));
87
+ const toolCss = cssKey ? await cssFiles[cssKey]() as string : "";
72
88
 
73
- const seoContent: UtilitySEOContent = { locale, sections: content.seo };
89
+ const seoContent: UtilitySEOContent = { locale, sections: content.seo ?? [] };
74
90
 
75
91
  const words = content.title.split(" ");
76
92
  const titleHighlight = words[0] || "";
@@ -89,8 +105,9 @@ const titleBase = words.slice(1).join(" ") || "";
89
105
  tools={allToolsNav}
90
106
  />
91
107
  <Fragment slot="head">
108
+ {toolCss && <Fragment set:html={`<style is:inline>${toolCss}</style>`} />}
92
109
  {
93
- content.schemas.map((schema: unknown) => (
110
+ ( content.schemas ?? []).map((schema: unknown) => (
94
111
  <script
95
112
  is:inline
96
113
  type="application/ld+json"
@@ -116,7 +133,7 @@ const titleBase = words.slice(1).join(" ") || "";
116
133
  </section>
117
134
 
118
135
  <section class="section-faq">
119
- <FAQSection items={content.faq} />
136
+ <FAQSection items={content.faq} inLanguage={locale} />
120
137
  </section>
121
138
 
122
139
  <section class="section-bibliography">
@@ -7,28 +7,11 @@ describe('Locale Completeness Validation', () => {
7
7
  describe(`Tool: ${tool.entry.id}`, () => {
8
8
  Object.keys(tool.entry.i18n).forEach((locale) => {
9
9
  describe(`Locale: ${locale}`, () => {
10
- it('faqTitle should be defined when faq items exist', async () => {
10
+ it('locale should load correctly', async () => {
11
11
  const loader = tool.entry.i18n[locale as keyof typeof tool.entry.i18n];
12
12
  const content = (await loader?.()) as ToolLocaleContent;
13
-
14
- if (content.faq.length > 0) {
15
- expect(
16
- content.faqTitle,
17
- `Tool "${tool.entry.id}" locale "${locale}" has ${content.faq.length} FAQ items but is missing faqTitle`,
18
- ).toBeTruthy();
19
- }
20
- });
21
-
22
- it('bibliographyTitle should be defined when bibliography items exist', async () => {
23
- const loader = tool.entry.i18n[locale as keyof typeof tool.entry.i18n];
24
- const content = (await loader?.()) as ToolLocaleContent;
25
-
26
- if (content.bibliography.length > 0) {
27
- expect(
28
- content.bibliographyTitle,
29
- `Tool "${tool.entry.id}" locale "${locale}" has ${content.bibliography.length} bibliography items but is missing bibliographyTitle`,
30
- ).toBeTruthy();
31
- }
13
+ expect(content).toBeDefined();
14
+ expect(content.slug).toBeTruthy();
32
15
  });
33
16
  });
34
17
  });
@@ -0,0 +1,56 @@
1
+ import type { ToolDefinition } from '../types';
2
+
3
+ export interface ToolExportValidationResult {
4
+ passed: boolean;
5
+ failures: string[];
6
+ }
7
+
8
+ function validateComponentType(
9
+ toolId: string,
10
+ componentName: string,
11
+ component: unknown,
12
+ failures: string[],
13
+ ): void {
14
+ if (typeof component !== 'function') {
15
+ failures.push(`${toolId}: ${componentName} is not a function (${typeof component})`);
16
+ }
17
+ }
18
+
19
+ async function validateComponentExecution(
20
+ toolId: string,
21
+ componentName: string,
22
+ fn: () => Promise<unknown>,
23
+ failures: string[],
24
+ ): Promise<void> {
25
+ try {
26
+ const result = await fn();
27
+ if (!result || typeof result !== 'object') {
28
+ failures.push(`${toolId}: ${componentName} import returned invalid result`);
29
+ }
30
+ } catch (error) {
31
+ failures.push(`${toolId}: ${componentName} execution error - ${error instanceof Error ? error.message : 'unknown'}`);
32
+ }
33
+ }
34
+
35
+ export async function validateToolExports(tools: ToolDefinition[]): Promise<ToolExportValidationResult> {
36
+ const failures: string[] = [];
37
+
38
+ for (const tool of tools) {
39
+ validateComponentType(tool.entry.id, 'Component', tool.Component, failures);
40
+ validateComponentType(tool.entry.id, 'SEOComponent', tool.SEOComponent, failures);
41
+ validateComponentType(tool.entry.id, 'BibliographyComponent', tool.BibliographyComponent, failures);
42
+
43
+ const componentFn = tool.Component as () => Promise<unknown>;
44
+ const seoFn = tool.SEOComponent as () => Promise<unknown>;
45
+ const bibFn = tool.BibliographyComponent as () => Promise<unknown>;
46
+
47
+ await validateComponentExecution(tool.entry.id, 'Component', componentFn, failures);
48
+ await validateComponentExecution(tool.entry.id, 'SEOComponent', seoFn, failures);
49
+ await validateComponentExecution(tool.entry.id, 'BibliographyComponent', bibFn, failures);
50
+ }
51
+
52
+ return {
53
+ passed: failures.length === 0,
54
+ failures,
55
+ };
56
+ }
@@ -0,0 +1,34 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { ALL_TOOLS } from '../tools';
3
+ import { validateToolExports } from './shared-test-helpers';
4
+
5
+ describe('Tool Exports Pattern Validation', () => {
6
+ describe('Component Exports Format', () => {
7
+ ALL_TOOLS.forEach((tool) => {
8
+ it(`${tool.entry.id}: Component should be a lazy-loaded function`, () => {
9
+ expect(typeof tool.Component).toBe('function');
10
+ expect(tool.Component).toBeInstanceOf(Function);
11
+ });
12
+
13
+ it(`${tool.entry.id}: SEOComponent should be a lazy-loaded function`, () => {
14
+ expect(typeof tool.SEOComponent).toBe('function');
15
+ expect(tool.SEOComponent).toBeInstanceOf(Function);
16
+ });
17
+
18
+ it(`${tool.entry.id}: BibliographyComponent should be a lazy-loaded function`, () => {
19
+ expect(typeof tool.BibliographyComponent).toBe('function');
20
+ expect(tool.BibliographyComponent).toBeInstanceOf(Function);
21
+ });
22
+ });
23
+ });
24
+
25
+ describe('Dynamic Import Validation', () => {
26
+ it('all tools must have functional dynamic imports', async () => {
27
+ const result = await validateToolExports(ALL_TOOLS);
28
+ if (!result.passed) {
29
+ throw new Error(`Tool export validation failed:\n${result.failures.join('\n')}`);
30
+ }
31
+ expect(result.passed).toBe(true);
32
+ });
33
+ });
34
+ });
@@ -1,14 +1,6 @@
1
1
  ---
2
2
  import { Bibliography as SharedBibliography } from '@jjlmoya/utils-shared';
3
- import { dateDiffCalculator } 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 dateDiffCalculator.i18n[locale]?.();
3
+ import { bibliography } from './bibliography';
12
4
  ---
13
5
 
14
- {content && <SharedBibliography links={content.bibliography} />}
6
+ <SharedBibliography links={bibliography} />
@@ -0,0 +1,7 @@
1
+ import type { BibliographyEntry } from '../../types';
2
+
3
+ export const bibliography: BibliographyEntry[] = [
4
+ { name: 'Time and Date: World Clock and Time Zone Converter', url: 'https://www.timeanddate.com/worldclock/' },
5
+ { name: 'Wikipedia: History of the Gregorian Calendar', url: 'https://en.wikipedia.org/wiki/Gregorian_calendar' },
6
+ { name: 'Wikipedia: Eisenhower Matrix and Time Management', url: 'https://en.wikipedia.org/wiki/Time_management#The_Eisenhower_Method' },
7
+ ];
@@ -18,7 +18,7 @@
18
18
  margin: 0 auto;
19
19
  }
20
20
 
21
- :global(.theme-dark) .dd-root {
21
+ .theme-dark .dd-root {
22
22
  --dd-card-bg: #1e293b;
23
23
  --dd-card-border: rgba(255, 255, 255, 0.05);
24
24
  --dd-field-bg: #0f172a;
@@ -1,3 +1,4 @@
1
+ import { bibliography } from '../bibliography';
1
2
  import type { ToolLocaleContent } from '../../../types';
2
3
  import type { WithContext, FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
3
4
  import type { DateDiffCalculatorUI } from '../ui';
@@ -77,15 +78,11 @@ export const content: ToolLocaleContent<DateDiffCalculatorUI> = {
77
78
  title: 'Datumsdifferenz Rechner',
78
79
  description: 'Berechnen Sie genau, wie viel Zeit zwischen zwei Daten vergangen ist oder wie lange es noch bis zu einem Ereignis dauert. Kostenloses Tool mit Ergebnissen in Tagen, Stunden und Minuten.',
79
80
  ui,
80
- faqTitle: 'Häufig gestellte Fragen',
81
+
81
82
  faq: faqData,
82
83
  howTo: howToData,
83
- bibliographyTitle: 'Referenzen',
84
- bibliography: [
85
- { name: 'Time and Date: Weltuhr und Zeitzonen-Konverter', url: 'https://www.timeanddate.com/worldclock/' },
86
- { name: 'Wikipedia: Geschichte des gregorianischen Kalenders', url: 'https://de.wikipedia.org/wiki/Gregorianischer_Kalender' },
87
- { name: 'Wikipedia: Eisenhower-Matrix und Zeitmanagement', url: 'https://de.wikipedia.org/wiki/Zeitmanagement#Die_ Eisenhower-Methode' },
88
- ],
84
+
85
+ bibliography,
89
86
  schemas: [faqSchema, howToSchema, appSchema],
90
87
  seo: [
91
88
  { type: 'title', level: 2, text: 'Datumsdifferenzrechner mit Tagen, Stunden und Minuten' },
@@ -1,3 +1,4 @@
1
+ import { bibliography } from '../bibliography';
1
2
  import type { ToolLocaleContent } from '../../../types';
2
3
  import type { WithContext, FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
3
4
  import type { DateDiffCalculatorUI } from '../ui';
@@ -77,15 +78,11 @@ export const content: ToolLocaleContent<DateDiffCalculatorUI> = {
77
78
  title: 'Date Difference Calculator',
78
79
  description: 'Calculate exactly how much time has passed between two dates or how long until an event. Free tool with results in days, hours and minutes.',
79
80
  ui,
80
- faqTitle: 'Frequently Asked Questions',
81
+
81
82
  faq: faqData,
82
83
  howTo: howToData,
83
- bibliographyTitle: 'References',
84
- bibliography: [
85
- { name: 'Time and Date: World Clock and Time Zone Converter', url: 'https://www.timeanddate.com/worldclock/' },
86
- { name: 'Wikipedia: History of the Gregorian Calendar', url: 'https://en.wikipedia.org/wiki/Gregorian_calendar' },
87
- { name: 'Wikipedia: Eisenhower Matrix and Time Management', url: 'https://en.wikipedia.org/wiki/Time_management#The_Eisenhower_Method' },
88
- ],
84
+
85
+ bibliography,
89
86
  schemas: [faqSchema, howToSchema, appSchema],
90
87
  seo: [
91
88
  { type: 'title', level: 2, text: 'Date difference calculator with days, hours and minutes' },
@@ -1,3 +1,4 @@
1
+ import { bibliography } from '../bibliography';
1
2
  import type { ToolLocaleContent } from '../../../types';
2
3
  import type { WithContext, FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
3
4
  import type { DateDiffCalculatorUI } from '../ui';
@@ -77,15 +78,11 @@ export const content: ToolLocaleContent<DateDiffCalculatorUI> = {
77
78
  title: 'Calculadora de Diferencia entre Fechas',
78
79
  description: 'Calcula exactamente cuánto tiempo ha pasado entre dos fechas o cuánto falta para un evento. Resultados en días, horas y minutos de forma gratuita y segura.',
79
80
  ui,
80
- faqTitle: 'Preguntas Frecuentes',
81
+
81
82
  faq: faqData,
82
83
  howTo: howToData,
83
- bibliographyTitle: 'Referencias',
84
- bibliography: [
85
- { name: 'Time and Date: Reloj Mundial y Conversor de Husos Horarios', url: 'https://www.timeanddate.com/worldclock/' },
86
- { name: 'Wikipedia: Historia del Calendario Gregoriano', url: 'https://es.wikipedia.org/wiki/Calendario_gregoriano' },
87
- { name: 'Wikipedia: Matriz de Eisenhower y Gestión del Tiempo', url: 'https://es.wikipedia.org/wiki/Matriz_de_Eisenhower' },
88
- ],
84
+
85
+ bibliography,
89
86
  schemas: [faqSchema, howToSchema, appSchema],
90
87
  seo: [
91
88
  { type: 'title', level: 2, text: 'Calculadora de diferencia entre fechas con días, horas y minutos' },
@@ -1,3 +1,4 @@
1
+ import { bibliography } from '../bibliography';
1
2
  import type { ToolLocaleContent } from '../../../types';
2
3
  import type { WithContext, FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
3
4
  import type { DateDiffCalculatorUI } from '../ui';
@@ -77,15 +78,11 @@ export const content: ToolLocaleContent<DateDiffCalculatorUI> = {
77
78
  title: 'Calculateur de Différence de Dates',
78
79
  description: 'Calculez exactement combien de temps s\'est écoulé entre deux dates ou combien il reste avant un événement. Outil gratuit avec résultats en jours, heures et minutes.',
79
80
  ui,
80
- faqTitle: 'Questions Fréquentes',
81
+
81
82
  faq: faqData,
82
83
  howTo: howToData,
83
- bibliographyTitle: 'Références',
84
- bibliography: [
85
- { name: 'Time and Date : Horloge Mondiale et Convertisseur de Fuseaux Horaires', url: 'https://www.timeanddate.com/worldclock/' },
86
- { name: 'Wikipedia : Histoire du Calendrier Grégorien', url: 'https://fr.wikipedia.org/wiki/Calendrier_gr%C3%A9gorien' },
87
- { name: 'Wikipedia : Matrice d\'Eisenhower et Gestion du Temps', url: 'https://fr.wikipedia.org/wiki/Matrice_d%27Eisenhower' },
88
- ],
84
+
85
+ bibliography,
89
86
  schemas: [faqSchema, howToSchema, appSchema],
90
87
  seo: [
91
88
  { type: 'title', level: 2, text: 'Calculateur de différence de dates en jours, heures et minutes' },
@@ -1,3 +1,4 @@
1
+ import { bibliography } from '../bibliography';
1
2
  import type { ToolLocaleContent } from '../../../types';
2
3
  import type { WithContext, FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
3
4
  import type { DateDiffCalculatorUI } from '../ui';
@@ -77,15 +78,11 @@ export const content: ToolLocaleContent<DateDiffCalculatorUI> = {
77
78
  title: 'Kalkulator Selisih Tanggal',
78
79
  description: 'Hitung secara tepat berapa lama waktu yang telah berlalu antara dua tanggal atau berapa lama hingga suatu acara terjadi. Alat gratis dengan hasil dalam hari, jam, dan menit.',
79
80
  ui,
80
- faqTitle: 'Pertanyaan yang Sering Diajukan',
81
+
81
82
  faq: faqData,
82
83
  howTo: howToData,
83
- bibliographyTitle: 'Referensi',
84
- bibliography: [
85
- { name: 'Time and Date: World Clock and Time Zone Converter', url: 'https://www.timeanddate.com/worldclock/' },
86
- { name: 'Wikipedia: Sejarah Kalender Gregorian', url: 'https://id.wikipedia.org/wiki/Kalender_Gregorius' },
87
- { name: 'Wikipedia: Manajemen Waktu — Matriks Eisenhower', url: 'https://id.wikipedia.org/wiki/Manajemen_waktu' },
88
- ],
84
+
85
+ bibliography,
89
86
  schemas: [faqSchema, howToSchema, appSchema],
90
87
  seo: [
91
88
  { type: 'title', level: 2, text: 'Kalkulator selisih tanggal dengan hari, jam, dan menit' },
@@ -1,3 +1,4 @@
1
+ import { bibliography } from '../bibliography';
1
2
  import type { ToolLocaleContent } from '../../../types';
2
3
  import type { WithContext, FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
3
4
  import type { DateDiffCalculatorUI } from '../ui';
@@ -77,15 +78,11 @@ export const content: ToolLocaleContent<DateDiffCalculatorUI> = {
77
78
  title: 'Calcolatore di Differenza tra Date',
78
79
  description: 'Calcola esattamente quanto tempo è passato tra due date o quanto manca a un evento. Strumento gratuito con risultati in giorni, ore e minuti.',
79
80
  ui,
80
- faqTitle: 'Domande Frequenti',
81
+
81
82
  faq: faqData,
82
83
  howTo: howToData,
83
- bibliographyTitle: 'Riferimenti',
84
- bibliography: [
85
- { name: 'Time and Date: World Clock and Time Zone Converter', url: 'https://www.timeanddate.com/worldclock/' },
86
- { name: 'Wikipedia: History of the Gregorian Calendar', url: 'https://it.wikipedia.org/wiki/Calendario_gregoriano' },
87
- { name: 'Wikipedia: Eisenhower Matrix and Time Management', url: 'https://it.wikipedia.org/wiki/Gestione_del_tempo#Metodo_di_Eisenhower' },
88
- ],
84
+
85
+ bibliography,
89
86
  schemas: [faqSchema, howToSchema, appSchema],
90
87
  seo: [
91
88
  { type: 'title', level: 2, text: 'Calcolatore di differenza tra date con giorni, ore e minuti' },
@@ -1,3 +1,4 @@
1
+ import { bibliography } from '../bibliography';
1
2
  import type { ToolLocaleContent } from '../../../types';
2
3
  import type { WithContext, FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
3
4
  import type { DateDiffCalculatorUI } from '../ui';
@@ -77,15 +78,11 @@ export const content: ToolLocaleContent<DateDiffCalculatorUI> = {
77
78
  title: '日付差計算機',
78
79
  description: '2つの日付の間にどれだけの時間が経過したか、あるいはイベントまであとどれくらいかを正確に計算します。日、時、分で結果を表示する無料ツール。',
79
80
  ui,
80
- faqTitle: 'よくある質問',
81
+
81
82
  faq: faqData,
82
83
  howTo: howToData,
83
- bibliographyTitle: '参考文献',
84
- bibliography: [
85
- { name: 'Time and Date: 世界時計とタイムゾーン変換器', url: 'https://www.timeanddate.com/worldclock/' },
86
- { name: 'Wikipedia: グレゴリオ暦の歴史', url: 'https://ja.wikipedia.org/wiki/グレゴリオ暦' },
87
- { name: 'Wikipedia: アイゼンハワー・マトリクスと時間管理', url: 'https://ja.wikipedia.org/wiki/時間管理' },
88
- ],
84
+
85
+ bibliography,
89
86
  schemas: [faqSchema, howToSchema, appSchema],
90
87
  seo: [
91
88
  { type: 'title', level: 2, text: '日、時、分でわかる日付差計算機' },
@@ -1,3 +1,4 @@
1
+ import { bibliography } from '../bibliography';
1
2
  import type { ToolLocaleContent } from '../../../types';
2
3
  import type { WithContext, FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
3
4
  import type { DateDiffCalculatorUI } from '../ui';
@@ -77,15 +78,11 @@ export const content: ToolLocaleContent<DateDiffCalculatorUI> = {
77
78
  title: '날짜 차이 계산기',
78
79
  description: '두 날짜 사이에 정확히 얼마의 시간이 지났는지 또는 이벤트까지 얼마나 남았는지 계산합니다. 일, 시, 분 단위 결과를 제공하는 무료 도구.',
79
80
  ui,
80
- faqTitle: '자주 묻는 질문',
81
+
81
82
  faq: faqData,
82
83
  howTo: howToData,
83
- bibliographyTitle: '참고 문헌',
84
- bibliography: [
85
- { name: 'Time and Date: World Clock and Time Zone Converter', url: 'https://www.timeanddate.com/worldclock/' },
86
- { name: 'Wikipedia: 그레고리력의 역사', url: 'https://ko.wikipedia.org/wiki/그레고리력' },
87
- { name: 'Wikipedia: 아이젠하워 매트릭스와 시간 관리', url: 'https://ko.wikipedia.org/wiki/시간_관리' },
88
- ],
84
+
85
+ bibliography,
89
86
  schemas: [faqSchema, howToSchema, appSchema],
90
87
  seo: [
91
88
  { type: 'title', level: 2, text: '일, 시, 분 단위로 계산하는 날짜 차이 계산기' },
@@ -1,3 +1,4 @@
1
+ import { bibliography } from '../bibliography';
1
2
  import type { ToolLocaleContent } from '../../../types';
2
3
  import type { WithContext, FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
3
4
  import type { DateDiffCalculatorUI } from '../ui';
@@ -77,15 +78,11 @@ export const content: ToolLocaleContent<DateDiffCalculatorUI> = {
77
78
  title: 'Datumverschil Calculator',
78
79
  description: 'Bereken precies hoeveel tijd er is verstreken tussen twee datums of hoelang het nog duurt tot een evenement. Gratis tool met resultaten in dagen, uren en minuten.',
79
80
  ui,
80
- faqTitle: 'Veelgestelde Vragen',
81
+
81
82
  faq: faqData,
82
83
  howTo: howToData,
83
- bibliographyTitle: 'Referenties',
84
- bibliography: [
85
- { name: 'Time and Date: World Clock and Time Zone Converter', url: 'https://www.timeanddate.com/worldclock/' },
86
- { name: 'Wikipedia: History of the Gregorian Calendar', url: 'https://nl.wikipedia.org/wiki/Gregoriaanse_kalender' },
87
- { name: 'Wikipedia: Eisenhower Matrix and Time Management', url: 'https://nl.wikipedia.org/wiki/Eisenhower-methode' },
88
- ],
84
+
85
+ bibliography,
89
86
  schemas: [faqSchema, howToSchema, appSchema],
90
87
  seo: [
91
88
  { type: 'title', level: 2, text: 'Datumverschil-calculator met dagen, uren en minuten' },
@@ -1,3 +1,4 @@
1
+ import { bibliography } from '../bibliography';
1
2
  import type { ToolLocaleContent } from '../../../types';
2
3
  import type { WithContext, FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
3
4
  import type { DateDiffCalculatorUI } from '../ui';
@@ -77,15 +78,11 @@ export const content: ToolLocaleContent<DateDiffCalculatorUI> = {
77
78
  title: 'Kalkulator Różnicy Dat',
78
79
  description: 'Oblicz dokładnie, ile czasu upłynęło między dwiema datami lub ile czasu pozostało do wydarzenia. Darmowe narzędzie z wynikami w dniach, godzinach i minutach.',
79
80
  ui,
80
- faqTitle: 'Często zadawane pytania',
81
+
81
82
  faq: faqData,
82
83
  howTo: howToData,
83
- bibliographyTitle: 'Źródła',
84
- bibliography: [
85
- { name: 'Time and Date: World Clock and Time Zone Converter', url: 'https://www.timeanddate.com/worldclock/' },
86
- { name: 'Wikipedia: Historia kalendarza gregoriańskiego', url: 'https://pl.wikipedia.org/wiki/Kalendarz_gregoriański' },
87
- { name: 'Wikipedia: Macierz Eisenhowera i zarządzanie czasem', url: 'https://pl.wikipedia.org/wiki/Zarządzanie_czasem' },
88
- ],
84
+
85
+ bibliography,
89
86
  schemas: [faqSchema, howToSchema, appSchema],
90
87
  seo: [
91
88
  { type: 'title', level: 2, text: 'Kalkulator różnicy dat z dniami, godzinami i minutami' },
@@ -1,3 +1,4 @@
1
+ import { bibliography } from '../bibliography';
1
2
  import type { ToolLocaleContent } from '../../../types';
2
3
  import type { WithContext, FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
3
4
  import type { DateDiffCalculatorUI } from '../ui';
@@ -77,15 +78,11 @@ export const content: ToolLocaleContent<DateDiffCalculatorUI> = {
77
78
  title: 'Calculadora de Diferença de Datas',
78
79
  description: 'Calcule exatamente quanto tempo passou entre duas datas ou quanto falta para um evento. Ferramenta gratuita com resultados em dias, horas e minutos.',
79
80
  ui,
80
- faqTitle: 'Perguntas Frequentes',
81
+
81
82
  faq: faqData,
82
83
  howTo: howToData,
83
- bibliographyTitle: 'Referências',
84
- bibliography: [
85
- { name: 'Time and Date: World Clock and Time Zone Converter', url: 'https://www.timeanddate.com/worldclock/' },
86
- { name: 'Wikipedia: History of the Gregorian Calendar', url: 'https://pt.wikipedia.org/wiki/Calendário_gregoriano' },
87
- { name: 'Wikipedia: Eisenhower Matrix and Time Management', url: 'https://pt.wikipedia.org/wiki/Gestão_do_tempo#Matriz_de_Eisenhower' },
88
- ],
84
+
85
+ bibliography,
89
86
  schemas: [faqSchema, howToSchema, appSchema],
90
87
  seo: [
91
88
  { type: 'title', level: 2, text: 'Calculadora de diferença de datas com dias, horas e minutos' },
@@ -1,3 +1,4 @@
1
+ import { bibliography } from '../bibliography';
1
2
  import type { ToolLocaleContent } from '../../../types';
2
3
  import type { WithContext, FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
3
4
  import type { DateDiffCalculatorUI } from '../ui';
@@ -77,15 +78,11 @@ export const content: ToolLocaleContent<DateDiffCalculatorUI> = {
77
78
  title: 'Калькулятор разницы дат',
78
79
  description: 'Точно рассчитайте, сколько времени прошло между двумя датами или сколько осталось до события. Бесплатный инструмент с результатами в днях, часах и минутах.',
79
80
  ui,
80
- faqTitle: 'Часто задаваемые вопросы',
81
+
81
82
  faq: faqData,
82
83
  howTo: howToData,
83
- bibliographyTitle: 'Источники',
84
- bibliography: [
85
- { name: 'Time and Date: World Clock and Time Zone Converter', url: 'https://www.timeanddate.com/worldclock/' },
86
- { name: 'Википедия: История григорианского календаря', url: 'https://ru.wikipedia.org/wiki/Григорианский_календарь' },
87
- { name: 'Википедия: Матрица Эйзенхауэра и тайм-менеджмент', url: 'https://ru.wikipedia.org/wiki/Управление_временем' },
88
- ],
84
+
85
+ bibliography,
89
86
  schemas: [faqSchema, howToSchema, appSchema],
90
87
  seo: [
91
88
  { type: 'title', level: 2, text: 'Калькулятор разницы дат с днями, часами и минутами' },
@@ -1,3 +1,4 @@
1
+ import { bibliography } from '../bibliography';
1
2
  import type { ToolLocaleContent } from '../../../types';
2
3
  import type { WithContext, FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
3
4
  import type { DateDiffCalculatorUI } from '../ui';
@@ -77,15 +78,11 @@ export const content: ToolLocaleContent<DateDiffCalculatorUI> = {
77
78
  title: 'Datumskillnadskalkylator',
78
79
  description: 'Beräkna exakt hur mycket tid som har förflutit mellan två datum eller hur länge det är kvar till en händelse. Gratis verktyg med resultat i dagar, timmar och minuter.',
79
80
  ui,
80
- faqTitle: 'Vanliga frågor',
81
+
81
82
  faq: faqData,
82
83
  howTo: howToData,
83
- bibliographyTitle: 'Referenser',
84
- bibliography: [
85
- { name: 'Time and Date: World Clock and Time Zone Converter', url: 'https://www.timeanddate.com/worldclock/' },
86
- { name: 'Wikipedia: History of the Gregorian Calendar', url: 'https://sv.wikipedia.org/wiki/Gregorianska_kalendern' },
87
- { name: 'Wikipedia: Eisenhower Matrix and Time Management', url: 'https://sv.wikipedia.org/wiki/Tidshantering' },
88
- ],
84
+
85
+ bibliography,
89
86
  schemas: [faqSchema, howToSchema, appSchema],
90
87
  seo: [
91
88
  { type: 'title', level: 2, text: 'Datumskillnadskalkylator med dagar, timmar och minuter' },
@@ -1,3 +1,4 @@
1
+ import { bibliography } from '../bibliography';
1
2
  import type { ToolLocaleContent } from '../../../types';
2
3
  import type { WithContext, FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
3
4
  import type { DateDiffCalculatorUI } from '../ui';
@@ -77,15 +78,11 @@ export const content: ToolLocaleContent<DateDiffCalculatorUI> = {
77
78
  title: 'Tarih Farkı Hesaplayıcı',
78
79
  description: 'İki tarih arasında tam olarak ne kadar zaman geçtiğini veya bir etkinliğe ne kadar kaldığını hesaplayın. Gün, saat ve dakika cinsinden sonuçlar veren ücretsiz araç.',
79
80
  ui,
80
- faqTitle: 'Sıkça Sorulan Sorular',
81
+
81
82
  faq: faqData,
82
83
  howTo: howToData,
83
- bibliographyTitle: 'Kaynaklar',
84
- bibliography: [
85
- { name: 'Time and Date: World Clock and Time Zone Converter', url: 'https://www.timeanddate.com/worldclock/' },
86
- { name: 'Vikipedi: Miladi Takvim Tarihi', url: 'https://tr.wikipedia.org/wiki/Miladi_takvim' },
87
- { name: 'Vikipedi: Eisenhower Matrisi ve Zaman Yönetimi', url: 'https://tr.wikipedia.org/wiki/Zaman_yönetimi' },
88
- ],
84
+
85
+ bibliography,
89
86
  schemas: [faqSchema, howToSchema, appSchema],
90
87
  seo: [
91
88
  { type: 'title', level: 2, text: 'Gün, saat ve dakika ile tarih farkı hesaplayıcı' },
@@ -1,3 +1,4 @@
1
+ import { bibliography } from '../bibliography';
1
2
  import type { ToolLocaleContent } from '../../../types';
2
3
  import type { WithContext, FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
3
4
  import type { DateDiffCalculatorUI } from '../ui';
@@ -77,15 +78,11 @@ export const content: ToolLocaleContent<DateDiffCalculatorUI> = {
77
78
  title: '日期差计算器',
78
79
  description: '精确计算两个日期之间经过了多少时间,或者距离某个事件还有多久。提供天、小时和分钟结果的免费工具。',
79
80
  ui,
80
- faqTitle: '常见问题',
81
+
81
82
  faq: faqData,
82
83
  howTo: howToData,
83
- bibliographyTitle: '参考文献',
84
- bibliography: [
85
- { name: 'Time and Date: 世界时钟和时区转换器', url: 'https://www.timeanddate.com/worldclock/' },
86
- { name: '维基百科:格里历的历史', url: 'https://zh.wikipedia.org/wiki/格里历' },
87
- { name: '维基百科:艾森豪威尔矩阵与时间管理', url: 'https://zh.wikipedia.org/wiki/时间管理' },
88
- ],
84
+
85
+ bibliography,
89
86
  schemas: [faqSchema, howToSchema, appSchema],
90
87
  seo: [
91
88
  { type: 'title', level: 2, text: '带有天、小时和分钟显示的日期差计算器' },
@@ -9,6 +9,7 @@ interface Props {
9
9
 
10
10
  const { locale = 'es' } = Astro.props;
11
11
  const content = await dateDiffCalculator.i18n[locale]?.();
12
+ if (!content) return null;
12
13
  ---
13
14
 
14
- {content && <SEORenderer content={{ locale, sections: content.seo }} />}
15
+ {content.seo?.length > 0 && <SEORenderer content={{ locale, sections: content.seo }} />}