@jjlmoya/utils-home 1.16.0 → 1.23.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 (174) hide show
  1. package/package.json +1 -1
  2. package/src/category/i18n/de.ts +10 -10
  3. package/src/category/i18n/en.ts +8 -8
  4. package/src/category/i18n/es.ts +2 -2
  5. package/src/category/i18n/fr.ts +15 -15
  6. package/src/category/i18n/id.ts +8 -8
  7. package/src/category/i18n/it.ts +7 -7
  8. package/src/category/i18n/nl.ts +8 -8
  9. package/src/category/i18n/pl.ts +10 -10
  10. package/src/category/i18n/pt.ts +8 -8
  11. package/src/category/i18n/ru.ts +10 -10
  12. package/src/category/i18n/sv.ts +8 -8
  13. package/src/category/i18n/tr.ts +4 -4
  14. package/src/category/i18n/zh.ts +8 -8
  15. package/src/entries.ts +4 -1
  16. package/src/pages/[locale]/[slug].astro +28 -12
  17. package/src/tests/locale_completeness.test.ts +4 -22
  18. package/src/tests/no_en_dash.test.ts +70 -0
  19. package/src/tests/shared-test-helpers.ts +56 -0
  20. package/src/tests/tool_exports.test.ts +34 -0
  21. package/src/tests/tool_validation.test.ts +2 -2
  22. package/src/tool/dewPointCalculator/bibliography.ts +10 -0
  23. package/src/tool/dewPointCalculator/i18n/de.ts +7 -17
  24. package/src/tool/dewPointCalculator/i18n/en.ts +8 -18
  25. package/src/tool/dewPointCalculator/i18n/es.ts +7 -17
  26. package/src/tool/dewPointCalculator/i18n/fr.ts +8 -18
  27. package/src/tool/dewPointCalculator/i18n/id.ts +7 -17
  28. package/src/tool/dewPointCalculator/i18n/it.ts +7 -17
  29. package/src/tool/dewPointCalculator/i18n/ja.ts +6 -16
  30. package/src/tool/dewPointCalculator/i18n/ko.ts +6 -16
  31. package/src/tool/dewPointCalculator/i18n/nl.ts +7 -17
  32. package/src/tool/dewPointCalculator/i18n/pl.ts +7 -17
  33. package/src/tool/dewPointCalculator/i18n/pt.ts +7 -17
  34. package/src/tool/dewPointCalculator/i18n/ru.ts +13 -23
  35. package/src/tool/dewPointCalculator/i18n/sv.ts +7 -17
  36. package/src/tool/dewPointCalculator/i18n/tr.ts +6 -16
  37. package/src/tool/dewPointCalculator/i18n/zh.ts +7 -17
  38. package/src/tool/dewPointCalculator/seo.astro +2 -1
  39. package/src/tool/heatingComparator/bibliography.ts +14 -0
  40. package/src/tool/heatingComparator/i18n/de.ts +10 -24
  41. package/src/tool/heatingComparator/i18n/en.ts +3 -13
  42. package/src/tool/heatingComparator/i18n/es.ts +3 -17
  43. package/src/tool/heatingComparator/i18n/fr.ts +9 -19
  44. package/src/tool/heatingComparator/i18n/id.ts +3 -17
  45. package/src/tool/heatingComparator/i18n/it.ts +3 -17
  46. package/src/tool/heatingComparator/i18n/ja.ts +296 -310
  47. package/src/tool/heatingComparator/i18n/ko.ts +296 -306
  48. package/src/tool/heatingComparator/i18n/nl.ts +3 -17
  49. package/src/tool/heatingComparator/i18n/pl.ts +3 -17
  50. package/src/tool/heatingComparator/i18n/pt.ts +3 -17
  51. package/src/tool/heatingComparator/i18n/ru.ts +14 -24
  52. package/src/tool/heatingComparator/i18n/sv.ts +6 -20
  53. package/src/tool/heatingComparator/i18n/tr.ts +2 -16
  54. package/src/tool/heatingComparator/i18n/zh.ts +296 -306
  55. package/src/tool/heatingComparator/seo.astro +3 -3
  56. package/src/tool/ledSavingCalculator/bibliography.ts +14 -0
  57. package/src/tool/ledSavingCalculator/i18n/de.ts +6 -16
  58. package/src/tool/ledSavingCalculator/i18n/en.ts +6 -20
  59. package/src/tool/ledSavingCalculator/i18n/es.ts +6 -20
  60. package/src/tool/ledSavingCalculator/i18n/fr.ts +10 -24
  61. package/src/tool/ledSavingCalculator/i18n/id.ts +5 -15
  62. package/src/tool/ledSavingCalculator/i18n/it.ts +6 -16
  63. package/src/tool/ledSavingCalculator/i18n/ja.ts +5 -15
  64. package/src/tool/ledSavingCalculator/i18n/ko.ts +4 -14
  65. package/src/tool/ledSavingCalculator/i18n/nl.ts +5 -15
  66. package/src/tool/ledSavingCalculator/i18n/pl.ts +5 -15
  67. package/src/tool/ledSavingCalculator/i18n/pt.ts +5 -15
  68. package/src/tool/ledSavingCalculator/i18n/ru.ts +8 -18
  69. package/src/tool/ledSavingCalculator/i18n/sv.ts +5 -15
  70. package/src/tool/ledSavingCalculator/i18n/tr.ts +5 -15
  71. package/src/tool/ledSavingCalculator/i18n/zh.ts +6 -16
  72. package/src/tool/ledSavingCalculator/seo.astro +2 -1
  73. package/src/tool/projectorCalculator/bibliography.ts +5 -0
  74. package/src/tool/projectorCalculator/i18n/de.ts +4 -8
  75. package/src/tool/projectorCalculator/i18n/en.ts +3 -8
  76. package/src/tool/projectorCalculator/i18n/es.ts +4 -9
  77. package/src/tool/projectorCalculator/i18n/fr.ts +6 -11
  78. package/src/tool/projectorCalculator/i18n/id.ts +4 -9
  79. package/src/tool/projectorCalculator/i18n/it.ts +4 -8
  80. package/src/tool/projectorCalculator/i18n/ja.ts +175 -179
  81. package/src/tool/projectorCalculator/i18n/ko.ts +175 -179
  82. package/src/tool/projectorCalculator/i18n/nl.ts +4 -8
  83. package/src/tool/projectorCalculator/i18n/pl.ts +5 -9
  84. package/src/tool/projectorCalculator/i18n/pt.ts +4 -8
  85. package/src/tool/projectorCalculator/i18n/ru.ts +7 -11
  86. package/src/tool/projectorCalculator/i18n/sv.ts +4 -8
  87. package/src/tool/projectorCalculator/i18n/tr.ts +4 -8
  88. package/src/tool/projectorCalculator/i18n/zh.ts +175 -179
  89. package/src/tool/projectorCalculator/seo.astro +2 -1
  90. package/src/tool/qrGenerator/bibliography.ts +14 -0
  91. package/src/tool/qrGenerator/i18n/de.ts +192 -202
  92. package/src/tool/qrGenerator/i18n/en.ts +3 -17
  93. package/src/tool/qrGenerator/i18n/es.ts +2 -16
  94. package/src/tool/qrGenerator/i18n/fr.ts +3 -17
  95. package/src/tool/qrGenerator/i18n/id.ts +146 -150
  96. package/src/tool/qrGenerator/i18n/it.ts +169 -173
  97. package/src/tool/qrGenerator/i18n/ja.ts +146 -150
  98. package/src/tool/qrGenerator/i18n/ko.ts +146 -150
  99. package/src/tool/qrGenerator/i18n/nl.ts +146 -150
  100. package/src/tool/qrGenerator/i18n/pl.ts +146 -150
  101. package/src/tool/qrGenerator/i18n/pt.ts +146 -150
  102. package/src/tool/qrGenerator/i18n/ru.ts +146 -150
  103. package/src/tool/qrGenerator/i18n/sv.ts +146 -150
  104. package/src/tool/qrGenerator/i18n/tr.ts +146 -150
  105. package/src/tool/qrGenerator/i18n/zh.ts +146 -150
  106. package/src/tool/qrGenerator/seo.astro +2 -1
  107. package/src/tool/solarCalculator/bibliography.ts +5 -0
  108. package/src/tool/solarCalculator/i18n/de.ts +141 -145
  109. package/src/tool/solarCalculator/i18n/en.ts +7 -12
  110. package/src/tool/solarCalculator/i18n/es.ts +5 -10
  111. package/src/tool/solarCalculator/i18n/fr.ts +8 -13
  112. package/src/tool/solarCalculator/i18n/id.ts +4 -8
  113. package/src/tool/solarCalculator/i18n/it.ts +4 -8
  114. package/src/tool/solarCalculator/i18n/ja.ts +121 -125
  115. package/src/tool/solarCalculator/i18n/ko.ts +116 -120
  116. package/src/tool/solarCalculator/i18n/nl.ts +4 -7
  117. package/src/tool/solarCalculator/i18n/pl.ts +5 -9
  118. package/src/tool/solarCalculator/i18n/pt.ts +4 -8
  119. package/src/tool/solarCalculator/i18n/ru.ts +7 -10
  120. package/src/tool/solarCalculator/i18n/sv.ts +4 -7
  121. package/src/tool/solarCalculator/i18n/tr.ts +4 -7
  122. package/src/tool/solarCalculator/i18n/zh.ts +116 -120
  123. package/src/tool/solarCalculator/seo.astro +2 -1
  124. package/src/tool/tariffComparator/bibliography.ts +7 -0
  125. package/src/tool/tariffComparator/i18n/de.ts +129 -132
  126. package/src/tool/tariffComparator/i18n/en.ts +5 -12
  127. package/src/tool/tariffComparator/i18n/es.ts +5 -12
  128. package/src/tool/tariffComparator/i18n/fr.ts +8 -15
  129. package/src/tool/tariffComparator/i18n/id.ts +2 -5
  130. package/src/tool/tariffComparator/i18n/it.ts +2 -5
  131. package/src/tool/tariffComparator/i18n/ja.ts +129 -132
  132. package/src/tool/tariffComparator/i18n/ko.ts +129 -132
  133. package/src/tool/tariffComparator/i18n/nl.ts +2 -5
  134. package/src/tool/tariffComparator/i18n/pl.ts +3 -6
  135. package/src/tool/tariffComparator/i18n/pt.ts +2 -5
  136. package/src/tool/tariffComparator/i18n/ru.ts +2 -5
  137. package/src/tool/tariffComparator/i18n/sv.ts +2 -5
  138. package/src/tool/tariffComparator/i18n/tr.ts +2 -5
  139. package/src/tool/tariffComparator/i18n/zh.ts +129 -132
  140. package/src/tool/tariffComparator/seo.astro +2 -1
  141. package/src/tool/wifiRangeSimulator/bibliography.astro +14 -0
  142. package/src/tool/wifiRangeSimulator/bibliography.ts +14 -0
  143. package/src/tool/wifiRangeSimulator/component.astro +170 -0
  144. package/src/tool/wifiRangeSimulator/entry.ts +29 -0
  145. package/src/tool/wifiRangeSimulator/i18n/de.ts +477 -0
  146. package/src/tool/wifiRangeSimulator/i18n/en.ts +477 -0
  147. package/src/tool/wifiRangeSimulator/i18n/es.ts +477 -0
  148. package/src/tool/wifiRangeSimulator/i18n/fr.ts +477 -0
  149. package/src/tool/wifiRangeSimulator/i18n/id.ts +477 -0
  150. package/src/tool/wifiRangeSimulator/i18n/it.ts +477 -0
  151. package/src/tool/wifiRangeSimulator/i18n/ja.ts +477 -0
  152. package/src/tool/wifiRangeSimulator/i18n/ko.ts +477 -0
  153. package/src/tool/wifiRangeSimulator/i18n/nl.ts +477 -0
  154. package/src/tool/wifiRangeSimulator/i18n/pl.ts +477 -0
  155. package/src/tool/wifiRangeSimulator/i18n/pt.ts +477 -0
  156. package/src/tool/wifiRangeSimulator/i18n/ru.ts +477 -0
  157. package/src/tool/wifiRangeSimulator/i18n/sv.ts +477 -0
  158. package/src/tool/wifiRangeSimulator/i18n/tr.ts +477 -0
  159. package/src/tool/wifiRangeSimulator/i18n/zh.ts +477 -0
  160. package/src/tool/wifiRangeSimulator/i18n-utils.ts +14 -0
  161. package/src/tool/wifiRangeSimulator/index.ts +8 -0
  162. package/src/tool/wifiRangeSimulator/logic.ts +220 -0
  163. package/src/tool/wifiRangeSimulator/seo.astro +15 -0
  164. package/src/tool/wifiRangeSimulator/sketch-actions.ts +168 -0
  165. package/src/tool/wifiRangeSimulator/sketch-events.ts +138 -0
  166. package/src/tool/wifiRangeSimulator/sketch-render-dash.ts +170 -0
  167. package/src/tool/wifiRangeSimulator/sketch-render-device.ts +42 -0
  168. package/src/tool/wifiRangeSimulator/sketch-render.ts +155 -0
  169. package/src/tool/wifiRangeSimulator/sketch-state.ts +186 -0
  170. package/src/tool/wifiRangeSimulator/sketch.ts +100 -0
  171. package/src/tool/wifiRangeSimulator/ui.ts +69 -0
  172. package/src/tool/wifiRangeSimulator/wifi-range-simulator.css +583 -0
  173. package/src/tools.ts +2 -0
  174. package/src/types.ts +0 -2
@@ -1,6 +1,7 @@
1
1
  import type { WithContext, FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
2
2
  import type { ToolLocaleContent } from '../../../types';
3
3
  import type { TariffComparatorUI } from '../ui';
4
+ import { bibliography } from '../bibliography';
4
5
 
5
6
  const slug = 'energietarief-vergelijker';
6
7
  const title = 'Energietarief Vergelijker: Vast vs. Variabel';
@@ -77,12 +78,8 @@ export const content: ToolLocaleContent<TariffComparatorUI> = {
77
78
  slug,
78
79
  title,
79
80
  description,
80
- faqTitle: 'Veelgestelde Vragen',
81
81
  faq: faqData,
82
- bibliographyTitle: 'Referenties',
83
- bibliography: [
84
- { name: 'ACM ConsuWijzer', url: 'https://www.consuwijzer.nl' },
85
- ],
82
+ bibliography,
86
83
  howTo: howToData,
87
84
  schemas: [faqSchema, howToSchema, appSchema],
88
85
  seo: [
@@ -1,6 +1,7 @@
1
1
  import type { WithContext, FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
2
2
  import type { ToolLocaleContent } from '../../../types';
3
3
  import type { TariffComparatorUI } from '../ui';
4
+ import { bibliography } from '../bibliography';
4
5
 
5
6
  const slug = 'porownanie-taryf-pradu';
6
7
  const title = 'Porównywarka Taryf Prądu: Rynek Wolny vs Regulowany';
@@ -21,7 +22,7 @@ const faqData = [
21
22
  {
22
23
  question: 'Co to jest moc umowna?',
23
24
  answer:
24
- 'To maksymalna ilość energii (kW), jaką możesz pobierać jednocześnie. W domach standard to 12.516 kW.',
25
+ 'To maksymalna ilość energii (kW), jaką możesz pobierać jednocześnie. W domach standard to 12.5-16 kW.',
25
26
  },
26
27
  ];
27
28
 
@@ -77,12 +78,8 @@ export const content: ToolLocaleContent<TariffComparatorUI> = {
77
78
  slug,
78
79
  title,
79
80
  description,
80
- faqTitle: 'Często Zadawane Pytania',
81
81
  faq: faqData,
82
- bibliographyTitle: 'Bibliografia',
83
- bibliography: [
84
- { name: 'URE — Urząd Regulacji Energetyki', url: 'https://www.ure.gov.pl' },
85
- ],
82
+ bibliography,
86
83
  howTo: howToData,
87
84
  schemas: [faqSchema, howToSchema, appSchema],
88
85
  seo: [
@@ -1,6 +1,7 @@
1
1
  import type { WithContext, FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
2
2
  import type { ToolLocaleContent } from '../../../types';
3
3
  import type { TariffComparatorUI } from '../ui';
4
+ import { bibliography } from '../bibliography';
4
5
 
5
6
  const slug = 'comparador-tarifas-eletricidade';
6
7
  const title = 'Comparador de Tarifas de Eletricidade: Mercado Livre vs Regulado';
@@ -77,12 +78,8 @@ export const content: ToolLocaleContent<TariffComparatorUI> = {
77
78
  slug,
78
79
  title,
79
80
  description,
80
- faqTitle: 'Perguntas Frequentes',
81
81
  faq: faqData,
82
- bibliographyTitle: 'Bibliografia',
83
- bibliography: [
84
- { name: 'ERSE — Simulador de Preços', url: 'https://www.erse.pt' },
85
- ],
82
+ bibliography,
86
83
  howTo: howToData,
87
84
  schemas: [faqSchema, howToSchema, appSchema],
88
85
  seo: [
@@ -1,6 +1,7 @@
1
1
  import type { WithContext, FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
2
2
  import type { ToolLocaleContent } from '../../../types';
3
3
  import type { TariffComparatorUI } from '../ui';
4
+ import { bibliography } from '../bibliography';
4
5
 
5
6
  const slug = 'sravnenie-tarifov-elektrichestva';
6
7
  const title = 'Сравнение тарифов ЖКХ: Свободный vs Регулируемый';
@@ -59,12 +60,8 @@ export const content: ToolLocaleContent<TariffComparatorUI> = {
59
60
  slug,
60
61
  title,
61
62
  description,
62
- faqTitle: 'Часто задаваемые вопросы',
63
63
  faq: faqData,
64
- bibliographyTitle: 'Библиография',
65
- bibliography: [
66
- { name: 'Федеральная антимонопольная служба — Тарифы', url: 'https://fas.gov.ru' },
67
- ],
64
+ bibliography,
68
65
  howTo: howToData,
69
66
  schemas: [faqSchema, howToSchema, appSchema],
70
67
  seo: [
@@ -1,6 +1,7 @@
1
1
  import type { WithContext, FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
2
2
  import type { ToolLocaleContent } from '../../../types';
3
3
  import type { TariffComparatorUI } from '../ui';
4
+ import { bibliography } from '../bibliography';
4
5
 
5
6
  const slug = 'jamfor-deltra-priser';
6
7
  const title = 'Jämför Elavtal: Rörligt vs Fast Pris';
@@ -64,12 +65,8 @@ export const content: ToolLocaleContent<TariffComparatorUI> = {
64
65
  slug,
65
66
  title,
66
67
  description,
67
- faqTitle: 'Vanliga Frågor',
68
68
  faq: faqData,
69
- bibliographyTitle: 'Referenser',
70
- bibliography: [
71
- { name: 'Energimarknadsinspektionen', url: 'https://www.ei.se' },
72
- ],
69
+ bibliography,
73
70
  howTo: howToData,
74
71
  schemas: [faqSchema, howToSchema, appSchema],
75
72
  seo: [
@@ -1,6 +1,7 @@
1
1
  import type { WithContext, FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
2
2
  import type { ToolLocaleContent } from '../../../types';
3
3
  import type { TariffComparatorUI } from '../ui';
4
+ import { bibliography } from '../bibliography';
4
5
 
5
6
  const slug = 'elektrik-tarifesi-karsilastirma';
6
7
  const title = 'Elektrik Tarifesi Karşılaştırıcı: Sabit vs Değişken';
@@ -77,12 +78,8 @@ export const content: ToolLocaleContent<TariffComparatorUI> = {
77
78
  slug,
78
79
  title,
79
80
  description,
80
- faqTitle: 'Sıkça Sorulan Sorular',
81
81
  faq: faqData,
82
- bibliographyTitle: 'Kaynakça',
83
- bibliography: [
84
- { name: 'EPDK — Enerji Piyasası Düzenleme Kurumu', url: 'https://www.epdk.gov.tr' },
85
- ],
82
+ bibliography,
86
83
  howTo: howToData,
87
84
  schemas: [faqSchema, howToSchema, appSchema],
88
85
  seo: [
@@ -1,133 +1,130 @@
1
- import type { WithContext, FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
2
- import type { ToolLocaleContent } from '../../../types';
3
- import type { TariffComparatorUI } from '../ui';
4
-
5
- const slug = 'electricity-tariff-comparator';
6
- const title = '电费套餐对比:自由市场 vs 监管市场';
7
- const description =
8
- '了解哪种电费套餐最适合您。根据您的实际用电量对比自由市场套餐与监管市场套餐。';
9
-
10
- const faqData = [
11
- {
12
- question: '固定价格和动态价格哪个更好?',
13
- answer:
14
- '固定价格提供可预测性。动态价格(如监管市场)在发电量充沛时可能极低,但存在波动风险。本计算器可帮您做出选择。',
15
- },
16
- {
17
- question: '如何获取我的年用电量?',
18
- answer:
19
- '您可以在最新的电费账单中找到“年用电量”(kWh) 这一数值。',
20
- },
21
- {
22
- question: '什么是合同功率?',
23
- answer:
24
- '指您可以同时使用的最大电力负荷 (kW)。',
25
- },
26
- ];
27
-
28
- const howToData = [
29
- {
30
- name: '查看账单',
31
- text: '查找年用电量 (kWh) 和合同功率 (kW)。',
32
- },
33
- {
34
- name: '调整数值',
35
- text: '根据您的实际数据调节滑块。',
36
- },
37
- {
38
- name: '对比结果',
39
- text: '查看两种模式下的预估年支出差异。',
40
- },
41
- ];
42
-
43
- const faqSchema: WithContext<FAQPage> = {
44
- '@context': 'https://schema.org',
45
- '@type': 'FAQPage',
46
- mainEntity: faqData.map((item) => ({
47
- '@type': 'Question',
48
- name: item.question,
49
- acceptedAnswer: { '@type': 'Answer', text: item.answer },
50
- })),
51
- };
52
-
53
- const howToSchema: WithContext<HowTo> = {
54
- '@context': 'https://schema.org',
55
- '@type': 'HowTo',
56
- name: title,
1
+ import type { WithContext, FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
2
+ import type { ToolLocaleContent } from '../../../types';
3
+ import type { TariffComparatorUI } from '../ui';
4
+ import { bibliography } from '../bibliography';
5
+
6
+ const slug = 'electricity-tariff-comparator';
7
+ const title = '电费套餐对比:自由市场 vs 监管市场';
8
+ const description =
9
+ '了解哪种电费套餐最适合您。根据您的实际用电量对比自由市场套餐与监管市场套餐。';
10
+
11
+ const faqData = [
12
+ {
13
+ question: '固定价格和动态价格哪个更好?',
14
+ answer:
15
+ '固定价格提供可预测性。动态价格(如监管市场)在发电量充沛时可能极低,但存在波动风险。本计算器可帮您做出选择。',
16
+ },
17
+ {
18
+ question: '如何获取我的年用电量?',
19
+ answer:
20
+ '您可以在最新的电费账单中找到"年用电量"(kWh) 这一数值。',
21
+ },
22
+ {
23
+ question: '什么是合同功率?',
24
+ answer:
25
+ '指您可以同时使用的最大电力负荷 (kW)。',
26
+ },
27
+ ];
28
+
29
+ const howToData = [
30
+ {
31
+ name: '查看账单',
32
+ text: '查找年用电量 (kWh) 和合同功率 (kW)。',
33
+ },
34
+ {
35
+ name: '调整数值',
36
+ text: '根据您的实际数据调节滑块。',
37
+ },
38
+ {
39
+ name: '对比结果',
40
+ text: '查看两种模式下的预估年支出差异。',
41
+ },
42
+ ];
43
+
44
+ const faqSchema: WithContext<FAQPage> = {
45
+ '@context': 'https://schema.org',
46
+ '@type': 'FAQPage',
47
+ mainEntity: faqData.map((item) => ({
48
+ '@type': 'Question',
49
+ name: item.question,
50
+ acceptedAnswer: { '@type': 'Answer', text: item.answer },
51
+ })),
52
+ };
53
+
54
+ const howToSchema: WithContext<HowTo> = {
55
+ '@context': 'https://schema.org',
56
+ '@type': 'HowTo',
57
+ name: title,
58
+ description,
59
+ step: howToData.map((step) => ({
60
+ '@type': 'HowToStep',
61
+ name: step.name,
62
+ text: step.text,
63
+ })),
64
+ };
65
+
66
+ const appSchema: WithContext<SoftwareApplication> = {
67
+ '@context': 'https://schema.org',
68
+ '@type': 'SoftwareApplication',
69
+ name: title,
70
+ description,
71
+ applicationCategory: 'UtilityApplication',
72
+ operatingSystem: 'All',
73
+ offers: { '@type': 'Offer', price: '0', priceCurrency: 'CNY' },
74
+ inLanguage: 'zh',
75
+ };
76
+
77
+ export const content: ToolLocaleContent<TariffComparatorUI> = {
78
+ slug,
79
+ title,
57
80
  description,
58
- step: howToData.map((step) => ({
59
- '@type': 'HowToStep',
60
- name: step.name,
61
- text: step.text,
62
- })),
63
- };
64
-
65
- const appSchema: WithContext<SoftwareApplication> = {
66
- '@context': 'https://schema.org',
67
- '@type': 'SoftwareApplication',
68
- name: title,
69
- description,
70
- applicationCategory: 'UtilityApplication',
71
- operatingSystem: 'All',
72
- offers: { '@type': 'Offer', price: '0', priceCurrency: 'CNY' },
73
- inLanguage: 'zh',
74
- };
75
-
76
- export const content: ToolLocaleContent<TariffComparatorUI> = {
77
- slug,
78
- title,
79
- description,
80
- faqTitle: '常见问题',
81
- faq: faqData,
82
- bibliographyTitle: '参考资料',
83
- bibliography: [
84
- { name: '能源监管部门官方指南', url: 'https://www.nea.gov.cn' },
85
- ],
86
- howTo: howToData,
87
- schemas: [faqSchema, howToSchema, appSchema],
88
- seo: [
89
- {
90
- type: 'title',
91
- text: '节省您的电力开支',
92
- level: 2,
93
- },
94
- {
95
- type: 'paragraph',
96
- html: '选择合适的套餐每年可节省数百元。利用我们的对比工具做出决定。',
97
- },
98
- {
99
- type: 'summary',
100
- title: '省钱策略',
101
- items: [
102
- '定期对比市场上的新套餐。',
103
- '合理设定合同功率,避免不必要的月租费。',
104
- '尽量将大功率用电安排在低谷时段。',
105
- ],
106
- },
107
- ],
108
- ui: {
109
- labelConsumption: '年用电量',
110
- unitKwhYear: 'kWh/年',
111
- labelPower: '合同功率',
112
- unitKw: 'kW',
113
- labelMarket: '监管市场 (动态)',
114
- labelFree: '自由市场 (固定)',
115
- labelAnnualEst: '预估年支出',
116
- labelPowerTerm: '功率费 (固定)',
117
- labelEnergyTerm: '电费 (变量)',
118
- labelDashboard: '能源面板',
119
- labelMaxPower: '峰值功率',
120
- labelMaxPowerEst: '预估峰值',
121
- labelCo2: '碳足迹',
122
- labelCo2Est: '预估年排放量',
123
- labelSimulator: '方案模拟器',
124
- labelSolar: '太阳能自用',
125
- labelSolarDesc: '模拟安装电池板后的节省(默认减少 30% 用电)',
126
- labelShift: '低谷时段转移',
127
- labelShiftDesc: '转移至低谷时段的比例:',
128
- badgeBetter: '更佳性价比',
129
- badgeWorseYear: '年',
130
- currencySign: '¥',
131
- monthLabels: '1月,2月,3月,4月,5月,6月,7月,8月,9月,10月,11月,12月',
132
- },
133
- };
81
+ faq: faqData,
82
+ bibliography,
83
+ howTo: howToData,
84
+ schemas: [faqSchema, howToSchema, appSchema],
85
+ seo: [
86
+ {
87
+ type: 'title',
88
+ text: '节省您的电力开支',
89
+ level: 2,
90
+ },
91
+ {
92
+ type: 'paragraph',
93
+ html: '选择合适的套餐每年可节省数百元。利用我们的对比工具做出决定。',
94
+ },
95
+ {
96
+ type: 'summary',
97
+ title: '省钱策略',
98
+ items: [
99
+ '定期对比市场上的新套餐。',
100
+ '合理设定合同功率,避免不必要的月租费。',
101
+ '尽量将大功率用电安排在低谷时段。',
102
+ ],
103
+ },
104
+ ],
105
+ ui: {
106
+ labelConsumption: '年用电量',
107
+ unitKwhYear: 'kWh/年',
108
+ labelPower: '合同功率',
109
+ unitKw: 'kW',
110
+ labelMarket: '监管市场 (动态)',
111
+ labelFree: '自由市场 (固定)',
112
+ labelAnnualEst: '预估年支出',
113
+ labelPowerTerm: '功率费 (固定)',
114
+ labelEnergyTerm: '电费 (变量)',
115
+ labelDashboard: '能源面板',
116
+ labelMaxPower: '峰值功率',
117
+ labelMaxPowerEst: '预估峰值',
118
+ labelCo2: '碳足迹',
119
+ labelCo2Est: '预估年排放量',
120
+ labelSimulator: '方案模拟器',
121
+ labelSolar: '太阳能自用',
122
+ labelSolarDesc: '模拟安装电池板后的节省(默认减少 30% 用电)',
123
+ labelShift: '低谷时段转移',
124
+ labelShiftDesc: '转移至低谷时段的比例:',
125
+ badgeBetter: '更佳性价比',
126
+ badgeWorseYear: '',
127
+ currencySign: '¥',
128
+ monthLabels: '1月,2月,3月,4月,5月,6月,7月,8月,9月,10月,11月,12月',
129
+ },
130
+ };
@@ -11,4 +11,5 @@ const { locale = 'es' } = Astro.props;
11
11
  const content = await tariffComparator.i18n[locale]?.();
12
12
  if (!content) return null;
13
13
  ---
14
- <SEORenderer content={{ locale: locale as string, sections: content.seo }} />
14
+
15
+ {content.seo?.length > 0 && <SEORenderer content={{ locale, sections: content.seo }} />}
@@ -0,0 +1,14 @@
1
+ ---
2
+ import { Bibliography as SharedBibliography } from '@jjlmoya/utils-shared';
3
+ import { wifiRangeSimulator } from './index';
4
+ import type { KnownLocale } from '../../types';
5
+
6
+ interface Props {
7
+ locale?: KnownLocale;
8
+ }
9
+
10
+ const { locale = 'en' } = Astro.props;
11
+ const content = await wifiRangeSimulator.i18n[locale]?.();
12
+ ---
13
+
14
+ {content && <SharedBibliography links={content.bibliography} />}
@@ -0,0 +1,14 @@
1
+ export const bibliography = [
2
+ {
3
+ name: 'Cisco: Wi-Fi RF Principles and Signal Attenuation (2023)',
4
+ url: 'https://learningnetwork.cisco.com/s/article/wi-fi-7-rf-planning-principles-and-deployment-best-practices',
5
+ },
6
+ {
7
+ name: 'University of Kansas: Building Material Impact on Wi-Fi Signal (2022)',
8
+ url: 'https://www.researchgate.net/publication/380627539_Experimental_Assessment_of_the_Effects_of_Building_Materials_on_Wi-Fi_Signal_24_GHz_and_5_GHz',
9
+ },
10
+ {
11
+ name: 'IEEE 802.11 Wireless Standard: Physical Layer Specifications (2021)',
12
+ url: 'https://standards.ieee.org/ieee/802.11ax/7180/',
13
+ },
14
+ ];
@@ -0,0 +1,170 @@
1
+ ---
2
+ import type { WifiRangeSimulatorUI } from './ui';
3
+ import type { Point } from './logic';
4
+ import { calculateSignalFromSketch, getVerdictColor } from './logic';
5
+
6
+ interface Props {
7
+ ui?: Record<string, unknown>;
8
+ }
9
+
10
+ const { ui = {} } = Astro.props;
11
+ const wUI = ui as WifiRangeSimulatorUI;
12
+
13
+ const circumference = 2 * Math.PI * 70;
14
+ const initialRouter: Point = { x: 150, y: 250 };
15
+ const initialDevice: Point = { x: 400, y: 250 };
16
+ const initialResult = calculateSignalFromSketch(initialRouter, initialDevice, [], []);
17
+ ---
18
+
19
+ <div class="sketch-wrapper">
20
+ <div class="sketch-card">
21
+ <div class="sketch-toolbar" id="sketch-toolbar">
22
+ <div class="sketch-tool-group">
23
+ <button class="sketch-tool active" data-tool="select" type="button">
24
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 3l7.07 16.97 2.51-7.39 7.39-2.51L3 3z"/></svg>
25
+ <span>{wUI.labelMove}</span>
26
+ </button>
27
+ </div>
28
+ <div class="sketch-tool-sep" />
29
+ <div class="sketch-tool-group">
30
+ <span class="sketch-tool-section-label">{wUI.labelWalls}</span>
31
+ <button class="sketch-tool" data-tool="drywall" type="button"><span class="sketch-tool-swatch" style="background:#e7e5e4" /><span>{wUI.labelDrywall}</span></button>
32
+ <button class="sketch-tool" data-tool="brick" type="button"><span class="sketch-tool-swatch" style="background:#a16207" /><span>{wUI.labelBrick}</span></button>
33
+ <button class="sketch-tool" data-tool="concrete" type="button"><span class="sketch-tool-swatch" style="background:#57534e" /><span>{wUI.labelConcrete}</span></button>
34
+ <button class="sketch-tool" data-tool="stoneWall" type="button"><span class="sketch-tool-swatch" style="background:#44403c" /><span>{wUI.labelStone}</span></button>
35
+ </div>
36
+ <div class="sketch-tool-sep" />
37
+ <div class="sketch-tool-group">
38
+ <span class="sketch-tool-section-label">{wUI.labelObjects}</span>
39
+ <button class="sketch-tool" data-tool="woodDoor" type="button"><span class="sketch-tool-swatch" style="background:#92400e" /><span>{wUI.labelWood}</span></button>
40
+ <button class="sketch-tool" data-tool="metalDoor" type="button"><span class="sketch-tool-swatch" style="background:#334155" /><span>{wUI.labelMetal}</span></button>
41
+ <button class="sketch-tool" data-tool="window" type="button"><span class="sketch-tool-swatch" style="background:#bfdbfe" /><span>{wUI.labelWindow}</span></button>
42
+ <button class="sketch-tool" data-tool="fridge" type="button"><span class="sketch-tool-swatch" style="background:#94a3b8" /><span>{wUI.labelFridge}</span></button>
43
+ <button class="sketch-tool" data-tool="aquarium" type="button"><span class="sketch-tool-swatch" style="background:#0ea5e9" /><span>{wUI.labelFish}</span></button>
44
+ <button class="sketch-tool" data-tool="microwave" type="button"><span class="sketch-tool-swatch" style="background:#475569" /><span>{wUI.labelMicro}</span></button>
45
+ <button class="sketch-tool" data-tool="mirror" type="button"><span class="sketch-tool-swatch" style="background:#c0c0c0" /><span>{wUI.labelMirror}</span></button>
46
+ <button class="sketch-tool" data-tool="furniture" type="button"><span class="sketch-tool-swatch" style="background:#78350f" /><span>{wUI.labelFurn}</span></button>
47
+ </div>
48
+ <div class="sketch-tool-sep" />
49
+ <div class="sketch-tool-group">
50
+ <span class="sketch-tool-section-label">{wUI.labelActions}</span>
51
+ <button class="sketch-tool" data-tool="undo" type="button">
52
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 7v6h6"/><path d="M21 17a9 9 0 0 0-9-9 9 9 0 0 0-6 2.3L3 13"/></svg>
53
+ <span>{wUI.labelUndo}</span>
54
+ </button>
55
+ <button class="sketch-tool" data-tool="addDevice" type="button">
56
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="4" width="20" height="14" rx="2"/><line x1="12" y1="8" x2="12" y2="14"/><line x1="9" y1="11" x2="15" y2="11"/></svg>
57
+ <span>{wUI.labelAddDevice}</span>
58
+ </button>
59
+ <button class="sketch-tool sketch-tool-danger" data-tool="clearWalls" type="button">
60
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18"/><path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"/><path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"/><line x1="10" y1="11" x2="10" y2="17"/><line x1="14" y1="11" x2="14" y2="17"/></svg>
61
+ <span>{wUI.labelClearWalls}</span>
62
+ </button>
63
+ <button class="sketch-tool sketch-tool-danger" data-tool="clearObjects" type="button">
64
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18"/><path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"/><path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"/><line x1="10" y1="11" x2="10" y2="17"/><line x1="14" y1="11" x2="14" y2="17"/></svg>
65
+ <span>{wUI.labelClearObjects}</span>
66
+ </button>
67
+ <button class="sketch-tool sketch-tool-danger" data-tool="clear" type="button">
68
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18"/><path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"/><path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"/><line x1="10" y1="11" x2="10" y2="17"/><line x1="14" y1="11" x2="14" y2="17"/></svg>
69
+ <span>{wUI.labelClearAll}</span>
70
+ </button>
71
+ </div>
72
+ </div>
73
+
74
+ <div class="sketch-body">
75
+ <div class="sketch-canvas-wrap" id="sketch-canvas-wrap">
76
+ <div class="sketch-world" id="sketch-world">
77
+ <svg class="sketch-svg" id="sketch-svg" viewBox="0 0 800 500" width="800" height="500">
78
+ <defs>
79
+ <pattern id="sketch-grid" width="20" height="20" patternUnits="userSpaceOnUse">
80
+ <path d="M 20 0 L 0 0 0 20" fill="none" stroke="var(--sketch-grid-color)" stroke-width="0.5" />
81
+ </pattern>
82
+ </defs>
83
+ <rect width="800" height="500" fill="url(#sketch-grid)" />
84
+ <g id="sketch-walls-layer" />
85
+ <g id="sketch-objs-layer" />
86
+ <g id="sketch-rays-layer" style="pointer-events:none" />
87
+ <g id="sketch-router-group" transform={`translate(${initialRouter.x}, ${initialRouter.y})`} style="cursor:grab">
88
+ <circle id="sketch-router-glow" cx="0" cy="0" r="28" fill="none" stroke={getVerdictColor(initialResult.verdict)} stroke-width="2" opacity="0.3" />
89
+ <rect x="-16" y="-16" width="32" height="32" rx="8" fill="var(--bg-surface)" stroke="var(--border-color)" stroke-width="1" />
90
+ <svg x="-10" y="-10" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke={getVerdictColor(initialResult.verdict)} stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
91
+ <path d="M12 20h.01" /><path d="M2 8.82a15 15 0 0 1 20 0" /><path d="M5 12.859a10 10 0 0 1 14 0" /><path d="M8.5 16.429a5 5 0 0 1 7 0" />
92
+ </svg>
93
+ <text x="0" y="28" text-anchor="middle" fill="var(--text-muted)" font-size="8" font-weight="700">{wUI.labelRouter}</text>
94
+ </g>
95
+ <line id="sketch-draw-line" x1="0" y1="0" x2="0" y2="0" stroke="#0ea5e9" stroke-width="3" stroke-dasharray="6 4" opacity="0" pointer-events="none" />
96
+ </svg>
97
+ </div>
98
+ <div class="sketch-zoom-controls">
99
+ <button class="sketch-zoom-btn" data-tool="zoomIn" type="button" title={wUI.labelZoomIn}>+</button>
100
+ <button class="sketch-zoom-btn" data-tool="zoomOut" type="button" title={wUI.labelZoomOut}>−</button>
101
+ </div>
102
+ <div class="sketch-scale-bar" id="sketch-scale-bar">
103
+ <div class="sketch-scale-track" id="sketch-scale-track">
104
+ <div class="sketch-scale-segment dark" />
105
+ <div class="sketch-scale-segment light" />
106
+ <div class="sketch-scale-segment dark" />
107
+ <div class="sketch-scale-segment light" />
108
+ <div class="sketch-scale-segment dark" />
109
+ </div>
110
+ <div class="sketch-scale-labels">
111
+ <span class="sketch-scale-label">0</span>
112
+ <span class="sketch-scale-label end">{wUI.labelScaleMeters}</span>
113
+ </div>
114
+ </div>
115
+ </div>
116
+
117
+ <div class="sketch-panel">
118
+ <div class="sketch-avg-label">{wUI.labelAverageSignal}</div>
119
+ <div class="sketch-ring" id="sketch-ring">
120
+ <svg viewBox="0 0 160 160" width="120" height="120">
121
+ <circle class="sketch-ring-bg" cx="80" cy="80" r="70" />
122
+ <circle
123
+ class="sketch-ring-fill"
124
+ id="sketch-ring-fill"
125
+ cx="80"
126
+ cy="80"
127
+ r="70"
128
+ stroke-dasharray={circumference}
129
+ stroke-dashoffset={circumference - (circumference * initialResult.strengthPercent) / 100}
130
+ stroke={getVerdictColor(initialResult.verdict)}
131
+ />
132
+ </svg>
133
+ <div class="sketch-ring-inner">
134
+ <div class="sketch-ring-pct" id="sketch-ring-pct" style={`color:${getVerdictColor(initialResult.verdict)}`}>{initialResult.strengthPercent}%</div>
135
+ <div class="sketch-ring-label">{wUI.labelSignalStrength}</div>
136
+ </div>
137
+ </div>
138
+ <div class="sketch-range"><span class="sketch-range-val" id="sketch-range-val">{initialResult.effectiveRange} {wUI.labelMeters}</span></div>
139
+ <div class="sketch-streams">
140
+ <span class="sketch-streams-title">{wUI.labelStreamingVerdict}</span>
141
+ {[
142
+ { key: '4kStreaming', label: wUI.label4kStreaming },
143
+ { key: 'onlineGaming', label: wUI.labelOnlineGaming },
144
+ { key: 'videoCalls', label: wUI.labelVideoCalls },
145
+ { key: 'basicBrowsing', label: wUI.labelBasicBrowsing },
146
+ ].map((item) => (
147
+ <div class="sketch-stream-row">
148
+ <span class="sketch-stream-name">{item.label}</span>
149
+ <span class="sketch-stream-badge" id={`sketch-badge-${item.key}`}>{initialResult.streamingVerdict[item.key]}</span>
150
+ </div>
151
+ ))}
152
+ </div>
153
+ <div class="sketch-device-panel" id="sketch-device-panel" />
154
+ <div class="sketch-tip-area" id="sketch-tip-area" />
155
+ </div>
156
+ </div>
157
+
158
+ <div class="sketch-hint" id="sketch-hint">{wUI.hintText}</div>
159
+ </div>
160
+ </div>
161
+
162
+ <script define:vars={{ ui }} is:inline>
163
+ window.__wifiUI = ui;
164
+ </script>
165
+
166
+ <script>
167
+ import { initSketch } from './sketch';
168
+ document.addEventListener('astro:page-load', initSketch);
169
+ initSketch();
170
+ </script>