@jjlmoya/utils-hardware 1.15.0 → 1.17.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.
- package/package.json +1 -1
- package/src/category/i18n/ru.ts +5 -5
- package/src/category/index.ts +3 -1
- package/src/entries.ts +7 -1
- package/src/index.ts +2 -0
- package/src/pages/[locale]/[slug].astro +28 -12
- package/src/tests/locale_completeness.test.ts +7 -21
- package/src/tests/no_h1_in_components.test.ts +1 -1
- package/src/tests/shared-test-helpers.ts +56 -0
- package/src/tests/tool_exports.test.ts +34 -0
- package/src/tests/tool_validation.test.ts +2 -2
- package/src/tool/batteryHealthEstimator/bibliography.ts +13 -0
- package/src/tool/batteryHealthEstimator/i18n/de.ts +3 -13
- package/src/tool/batteryHealthEstimator/i18n/en.ts +3 -13
- package/src/tool/batteryHealthEstimator/i18n/es.ts +2 -12
- package/src/tool/batteryHealthEstimator/i18n/fr.ts +3 -13
- package/src/tool/batteryHealthEstimator/i18n/id.ts +3 -13
- package/src/tool/batteryHealthEstimator/i18n/it.ts +2 -12
- package/src/tool/batteryHealthEstimator/i18n/ja.ts +2 -12
- package/src/tool/batteryHealthEstimator/i18n/ko.ts +2 -12
- package/src/tool/batteryHealthEstimator/i18n/nl.ts +3 -13
- package/src/tool/batteryHealthEstimator/i18n/pl.ts +3 -13
- package/src/tool/batteryHealthEstimator/i18n/pt.ts +3 -13
- package/src/tool/batteryHealthEstimator/i18n/ru.ts +7 -17
- package/src/tool/batteryHealthEstimator/i18n/sv.ts +3 -13
- package/src/tool/batteryHealthEstimator/i18n/tr.ts +3 -13
- package/src/tool/batteryHealthEstimator/i18n/zh.ts +3 -13
- package/src/tool/batteryHealthEstimator/seo.astro +3 -2
- package/src/tool/colorAccuracyTest/bibliography.astro +14 -0
- package/src/tool/colorAccuracyTest/bibliography.ts +16 -0
- package/src/tool/colorAccuracyTest/color-accuracy-test.css +728 -0
- package/src/tool/colorAccuracyTest/component.astro +157 -0
- package/src/tool/colorAccuracyTest/entry.ts +29 -0
- package/src/tool/colorAccuracyTest/i18n/de.ts +284 -0
- package/src/tool/colorAccuracyTest/i18n/en.ts +284 -0
- package/src/tool/colorAccuracyTest/i18n/es.ts +284 -0
- package/src/tool/colorAccuracyTest/i18n/fr.ts +284 -0
- package/src/tool/colorAccuracyTest/i18n/id.ts +284 -0
- package/src/tool/colorAccuracyTest/i18n/it.ts +284 -0
- package/src/tool/colorAccuracyTest/i18n/ja.ts +284 -0
- package/src/tool/colorAccuracyTest/i18n/ko.ts +284 -0
- package/src/tool/colorAccuracyTest/i18n/nl.ts +284 -0
- package/src/tool/colorAccuracyTest/i18n/pl.ts +284 -0
- package/src/tool/colorAccuracyTest/i18n/pt.ts +284 -0
- package/src/tool/colorAccuracyTest/i18n/ru.ts +284 -0
- package/src/tool/colorAccuracyTest/i18n/sv.ts +284 -0
- package/src/tool/colorAccuracyTest/i18n/tr.ts +284 -0
- package/src/tool/colorAccuracyTest/i18n/zh.ts +284 -0
- package/src/tool/colorAccuracyTest/index.ts +9 -0
- package/src/tool/colorAccuracyTest/logic.ts +226 -0
- package/src/tool/colorAccuracyTest/seo.astro +15 -0
- package/src/tool/colorAccuracyTest/ui.ts +27 -0
- package/src/tool/deadPixelTest/bibliography.ts +13 -0
- package/src/tool/deadPixelTest/i18n/de.ts +2 -12
- package/src/tool/deadPixelTest/i18n/en.ts +2 -12
- package/src/tool/deadPixelTest/i18n/es.ts +2 -12
- package/src/tool/deadPixelTest/i18n/fr.ts +2 -12
- package/src/tool/deadPixelTest/i18n/id.ts +2 -12
- package/src/tool/deadPixelTest/i18n/it.ts +2 -12
- package/src/tool/deadPixelTest/i18n/ja.ts +2 -12
- package/src/tool/deadPixelTest/i18n/ko.ts +2 -12
- package/src/tool/deadPixelTest/i18n/nl.ts +2 -12
- package/src/tool/deadPixelTest/i18n/pl.ts +2 -12
- package/src/tool/deadPixelTest/i18n/pt.ts +2 -12
- package/src/tool/deadPixelTest/i18n/ru.ts +2 -12
- package/src/tool/deadPixelTest/i18n/sv.ts +2 -12
- package/src/tool/deadPixelTest/i18n/tr.ts +2 -12
- package/src/tool/deadPixelTest/i18n/zh.ts +2 -12
- package/src/tool/deadPixelTest/seo.astro +3 -2
- package/src/tool/gamepadTest/bibliography.ts +12 -0
- package/src/tool/gamepadTest/i18n/de.ts +2 -12
- package/src/tool/gamepadTest/i18n/en.ts +2 -12
- package/src/tool/gamepadTest/i18n/es.ts +2 -12
- package/src/tool/gamepadTest/i18n/fr.ts +2 -12
- package/src/tool/gamepadTest/i18n/id.ts +2 -12
- package/src/tool/gamepadTest/i18n/it.ts +2 -12
- package/src/tool/gamepadTest/i18n/ja.ts +2 -12
- package/src/tool/gamepadTest/i18n/ko.ts +2 -12
- package/src/tool/gamepadTest/i18n/nl.ts +2 -12
- package/src/tool/gamepadTest/i18n/pl.ts +2 -12
- package/src/tool/gamepadTest/i18n/pt.ts +2 -12
- package/src/tool/gamepadTest/i18n/ru.ts +3 -13
- package/src/tool/gamepadTest/i18n/sv.ts +2 -12
- package/src/tool/gamepadTest/i18n/tr.ts +2 -12
- package/src/tool/gamepadTest/i18n/zh.ts +2 -12
- package/src/tool/gamepadTest/seo.astro +3 -2
- package/src/tool/gamepadVibrationTester/bibliography.ts +13 -0
- package/src/tool/gamepadVibrationTester/i18n/de.ts +2 -12
- package/src/tool/gamepadVibrationTester/i18n/en.ts +2 -12
- package/src/tool/gamepadVibrationTester/i18n/es.ts +2 -12
- package/src/tool/gamepadVibrationTester/i18n/fr.ts +2 -12
- package/src/tool/gamepadVibrationTester/i18n/id.ts +2 -12
- package/src/tool/gamepadVibrationTester/i18n/it.ts +2 -12
- package/src/tool/gamepadVibrationTester/i18n/ja.ts +2 -12
- package/src/tool/gamepadVibrationTester/i18n/ko.ts +2 -12
- package/src/tool/gamepadVibrationTester/i18n/nl.ts +2 -12
- package/src/tool/gamepadVibrationTester/i18n/pl.ts +2 -12
- package/src/tool/gamepadVibrationTester/i18n/pt.ts +2 -12
- package/src/tool/gamepadVibrationTester/i18n/ru.ts +5 -15
- package/src/tool/gamepadVibrationTester/i18n/sv.ts +2 -12
- package/src/tool/gamepadVibrationTester/i18n/tr.ts +2 -12
- package/src/tool/gamepadVibrationTester/i18n/zh.ts +2 -12
- package/src/tool/gamepadVibrationTester/seo.astro +3 -2
- package/src/tool/keyboardTest/bibliography.ts +13 -0
- package/src/tool/keyboardTest/i18n/de.ts +2 -12
- package/src/tool/keyboardTest/i18n/en.ts +2 -12
- package/src/tool/keyboardTest/i18n/es.ts +2 -12
- package/src/tool/keyboardTest/i18n/fr.ts +2 -12
- package/src/tool/keyboardTest/i18n/id.ts +2 -12
- package/src/tool/keyboardTest/i18n/it.ts +2 -12
- package/src/tool/keyboardTest/i18n/ja.ts +2 -12
- package/src/tool/keyboardTest/i18n/ko.ts +2 -12
- package/src/tool/keyboardTest/i18n/nl.ts +2 -12
- package/src/tool/keyboardTest/i18n/pl.ts +2 -12
- package/src/tool/keyboardTest/i18n/pt.ts +2 -12
- package/src/tool/keyboardTest/i18n/ru.ts +2 -12
- package/src/tool/keyboardTest/i18n/sv.ts +2 -12
- package/src/tool/keyboardTest/i18n/tr.ts +2 -12
- package/src/tool/keyboardTest/i18n/zh.ts +2 -12
- package/src/tool/keyboardTest/seo.astro +3 -2
- package/src/tool/mousePollingTest/bibliography.ts +13 -0
- package/src/tool/mousePollingTest/i18n/de.ts +3 -13
- package/src/tool/mousePollingTest/i18n/en.ts +3 -13
- package/src/tool/mousePollingTest/i18n/es.ts +3 -13
- package/src/tool/mousePollingTest/i18n/fr.ts +3 -13
- package/src/tool/mousePollingTest/i18n/id.ts +3 -13
- package/src/tool/mousePollingTest/i18n/it.ts +3 -13
- package/src/tool/mousePollingTest/i18n/ja.ts +3 -13
- package/src/tool/mousePollingTest/i18n/ko.ts +3 -13
- package/src/tool/mousePollingTest/i18n/nl.ts +3 -13
- package/src/tool/mousePollingTest/i18n/pl.ts +3 -13
- package/src/tool/mousePollingTest/i18n/pt.ts +3 -13
- package/src/tool/mousePollingTest/i18n/ru.ts +5 -15
- package/src/tool/mousePollingTest/i18n/sv.ts +3 -13
- package/src/tool/mousePollingTest/i18n/tr.ts +3 -13
- package/src/tool/mousePollingTest/i18n/zh.ts +3 -13
- package/src/tool/mousePollingTest/seo.astro +3 -2
- package/src/tool/refreshRateDetector/bibliography.astro +14 -0
- package/src/tool/refreshRateDetector/bibliography.ts +12 -0
- package/src/tool/refreshRateDetector/component.astro +206 -0
- package/src/tool/refreshRateDetector/entry.ts +29 -0
- package/src/tool/refreshRateDetector/i18n/de.ts +196 -0
- package/src/tool/refreshRateDetector/i18n/en.ts +196 -0
- package/src/tool/refreshRateDetector/i18n/es.ts +196 -0
- package/src/tool/refreshRateDetector/i18n/fr.ts +196 -0
- package/src/tool/refreshRateDetector/i18n/id.ts +196 -0
- package/src/tool/refreshRateDetector/i18n/it.ts +196 -0
- package/src/tool/refreshRateDetector/i18n/ja.ts +196 -0
- package/src/tool/refreshRateDetector/i18n/ko.ts +196 -0
- package/src/tool/refreshRateDetector/i18n/nl.ts +196 -0
- package/src/tool/refreshRateDetector/i18n/pl.ts +196 -0
- package/src/tool/refreshRateDetector/i18n/pt.ts +196 -0
- package/src/tool/refreshRateDetector/i18n/ru.ts +196 -0
- package/src/tool/refreshRateDetector/i18n/sv.ts +196 -0
- package/src/tool/refreshRateDetector/i18n/tr.ts +196 -0
- package/src/tool/refreshRateDetector/i18n/zh.ts +196 -0
- package/src/tool/refreshRateDetector/index.ts +11 -0
- package/src/tool/refreshRateDetector/monitor-refresh-rate-detector.css +342 -0
- package/src/tool/refreshRateDetector/seo.astro +15 -0
- package/src/tool/refreshRateDetector/ui.ts +24 -0
- package/src/tool/toneGenerator/bibliography.ts +13 -0
- package/src/tool/toneGenerator/i18n/de.ts +3 -13
- package/src/tool/toneGenerator/i18n/en.ts +3 -13
- package/src/tool/toneGenerator/i18n/es.ts +2 -12
- package/src/tool/toneGenerator/i18n/fr.ts +3 -13
- package/src/tool/toneGenerator/i18n/id.ts +3 -13
- package/src/tool/toneGenerator/i18n/it.ts +3 -13
- package/src/tool/toneGenerator/i18n/ja.ts +2 -12
- package/src/tool/toneGenerator/i18n/ko.ts +2 -12
- package/src/tool/toneGenerator/i18n/nl.ts +3 -13
- package/src/tool/toneGenerator/i18n/pl.ts +3 -13
- package/src/tool/toneGenerator/i18n/pt.ts +3 -13
- package/src/tool/toneGenerator/i18n/ru.ts +6 -16
- package/src/tool/toneGenerator/i18n/sv.ts +3 -13
- package/src/tool/toneGenerator/i18n/tr.ts +2 -12
- package/src/tool/toneGenerator/i18n/zh.ts +2 -12
- package/src/tool/toneGenerator/seo.astro +3 -2
- package/src/tools.ts +3 -1
package/package.json
CHANGED
package/src/category/i18n/ru.ts
CHANGED
|
@@ -12,7 +12,7 @@ export const content: CategoryLocaleContent = {
|
|
|
12
12
|
},
|
|
13
13
|
{
|
|
14
14
|
type: 'paragraph',
|
|
15
|
-
html: 'Обслуживание оборудования больше не требует установки тяжёлых диагностических приложений. В этом разделе мы предлагаем набор <strong>бесплатных онлайн-инструментов</strong>, предназначенных для тестирования ваших компонентов с использованием нативных веб-API. От обнаружения усталости LED-панели до измерения частоты опроса игровых периферийных устройств
|
|
15
|
+
html: 'Обслуживание оборудования больше не требует установки тяжёлых диагностических приложений. В этом разделе мы предлагаем набор <strong>бесплатных онлайн-инструментов</strong>, предназначенных для тестирования ваших компонентов с использованием нативных веб-API. От обнаружения усталости LED-панели до измерения частоты опроса игровых периферийных устройств -мы применяем стандартные протоколы тестирования, чтобы вы знали реальное состояние вашего оборудования.',
|
|
16
16
|
},
|
|
17
17
|
{
|
|
18
18
|
type: 'paragraph',
|
|
@@ -43,7 +43,7 @@ export const content: CategoryLocaleContent = {
|
|
|
43
43
|
},
|
|
44
44
|
{
|
|
45
45
|
type: 'paragraph',
|
|
46
|
-
html: 'Износ джойстика (известный как дрейф)
|
|
46
|
+
html: 'Износ джойстика (известный как дрейф) -наиболее распространённая неисправность в современных контроллерах PS5, Xbox и Switch. Наш <strong>симулятор контроллера</strong> анализирует оси X/Y с точностью числа с плавающей запятой, позволяя визуализировать любое нежелательное движение или отсутствие мёртвой зоны в стиках. Также позволяет тестировать тактильный отклик и вибрацию мотора Dual-Rumble через браузер.',
|
|
47
47
|
},
|
|
48
48
|
{
|
|
49
49
|
type: 'title',
|
|
@@ -65,7 +65,7 @@ export const content: CategoryLocaleContent = {
|
|
|
65
65
|
},
|
|
66
66
|
{
|
|
67
67
|
type: 'paragraph',
|
|
68
|
-
html: 'Понимание sRGB vs. Adobe RGB vs. P3 также критично. sRGB
|
|
68
|
+
html: 'Понимание sRGB vs. Adobe RGB vs. P3 также критично. sRGB -веб-стандарт (достаточен для 99% использования), но Adobe RGB охватывает на 40% больше цветов (необходим для профессионалов). Монитор, охватывающий только 70% sRGB, будет производить недостаточную цветовую насыщенность. Наши инструменты позволяют проводить проверку без вложений в профессиональное оборудование за €1000.',
|
|
69
69
|
},
|
|
70
70
|
{
|
|
71
71
|
type: 'title',
|
|
@@ -78,7 +78,7 @@ export const content: CategoryLocaleContent = {
|
|
|
78
78
|
},
|
|
79
79
|
{
|
|
80
80
|
type: 'paragraph',
|
|
81
|
-
html: 'Тесты задержки также критичны для соревновательного игрового процесса. Задержка ввод-дисплей (от нажатия кнопки до отображения на экране) может составлять 20–50 мс на приличном оборудовании, но 200+ мс на медленном. В соревновательных играх (FPS, файтинги) 50 мс
|
|
81
|
+
html: 'Тесты задержки также критичны для соревновательного игрового процесса. Задержка ввод-дисплей (от нажатия кнопки до отображения на экране) может составлять 20–50 мс на приличном оборудовании, но 200+ мс на медленном. В соревновательных играх (FPS, файтинги) 50 мс -разница между победой и поражением. Поэтому профессиональные геймеры инвестируют в мониторы 240 Гц, мыши 8000 Гц и оптимизированные ПК.',
|
|
82
82
|
},
|
|
83
83
|
{
|
|
84
84
|
type: 'title',
|
|
@@ -87,7 +87,7 @@ export const content: CategoryLocaleContent = {
|
|
|
87
87
|
},
|
|
88
88
|
{
|
|
89
89
|
type: 'paragraph',
|
|
90
|
-
html: '"Ремонтопригодность" становится всё важнее в 2026 году. "Ремонтируемый" телефон служит 6 лет; "запечатанный" истекает через 2–3 года. Ноутбук с обновляемой ОЗУ
|
|
90
|
+
html: '"Ремонтопригодность" становится всё важнее в 2026 году. "Ремонтируемый" телефон служит 6 лет; "запечатанный" истекает через 2–3 года. Ноутбук с обновляемой ОЗУ -инвестиция; с припаянной ОЗУ -одноразовый. Диагностические инструменты, которые мы внедряем, согласуются с движением за право на ремонт: мы хотим, чтобы вы знали точно, что сломалось, прежде чем выбрасывать устройства.',
|
|
91
91
|
},
|
|
92
92
|
{
|
|
93
93
|
type: 'paragraph',
|
package/src/category/index.ts
CHANGED
|
@@ -6,10 +6,12 @@ import { probadorVibracionMando } from '../tool/gamepadVibrationTester/index';
|
|
|
6
6
|
import { testRaton } from '../tool/mousePollingTest/index';
|
|
7
7
|
import { estimadorSaludBateria } from '../tool/batteryHealthEstimator/index';
|
|
8
8
|
import { toneGenerator } from '../tool/toneGenerator/index';
|
|
9
|
+
import { refreshRateDetector } from '../tool/refreshRateDetector/index';
|
|
10
|
+
import { spectrumCanvas } from '../tool/colorAccuracyTest/index';
|
|
9
11
|
|
|
10
12
|
export const hardwareCategory: HardwareCategoryEntry = {
|
|
11
13
|
icon: 'mdi:memory',
|
|
12
|
-
tools: [pixelesPantalla, testTeclado, testMando, probadorVibracionMando, testRaton, estimadorSaludBateria, toneGenerator],
|
|
14
|
+
tools: [pixelesPantalla, testTeclado, testMando, probadorVibracionMando, testRaton, estimadorSaludBateria, toneGenerator, refreshRateDetector, spectrumCanvas],
|
|
13
15
|
i18n: {
|
|
14
16
|
en: () => import('./i18n/en').then((m) => m.content),
|
|
15
17
|
es: () => import('./i18n/es').then((m) => m.content),
|
package/src/entries.ts
CHANGED
|
@@ -12,6 +12,10 @@ export { testRaton } from './tool/mousePollingTest/entry';
|
|
|
12
12
|
export type { TestRatonLocaleContent } from './tool/mousePollingTest/entry';
|
|
13
13
|
export { toneGenerator } from './tool/toneGenerator/entry';
|
|
14
14
|
export type { ToneGeneratorLocaleContent } from './tool/toneGenerator/entry';
|
|
15
|
+
export { refreshRateDetector } from './tool/refreshRateDetector/entry';
|
|
16
|
+
export type { RefreshRateDetectorLocaleContent } from './tool/refreshRateDetector/entry';
|
|
17
|
+
export { spectrumCanvas } from './tool/colorAccuracyTest/entry';
|
|
18
|
+
export type { SpectrumCanvasLocaleContent } from './tool/colorAccuracyTest/entry';
|
|
15
19
|
export { hardwareCategory } from './category';
|
|
16
20
|
import { estimadorSaludBateria } from './tool/batteryHealthEstimator/entry';
|
|
17
21
|
import { pixelesPantalla } from './tool/deadPixelTest/entry';
|
|
@@ -20,4 +24,6 @@ import { probadorVibracionMando } from './tool/gamepadVibrationTester/entry';
|
|
|
20
24
|
import { testTeclado } from './tool/keyboardTest/entry';
|
|
21
25
|
import { testRaton } from './tool/mousePollingTest/entry';
|
|
22
26
|
import { toneGenerator } from './tool/toneGenerator/entry';
|
|
23
|
-
|
|
27
|
+
import { refreshRateDetector } from './tool/refreshRateDetector/entry';
|
|
28
|
+
import { spectrumCanvas } from './tool/colorAccuracyTest/entry';
|
|
29
|
+
export const ALL_ENTRIES = [estimadorSaludBateria, pixelesPantalla, testMando, probadorVibracionMando, testTeclado, testRaton, toneGenerator, refreshRateDetector, spectrumCanvas];
|
package/src/index.ts
CHANGED
|
@@ -24,3 +24,5 @@ export { PROBADOR_VIBRACION_MANDO_TOOL } from './tool/gamepadVibrationTester/ind
|
|
|
24
24
|
export { TEST_RATON_TOOL } from './tool/mousePollingTest/index';
|
|
25
25
|
export { ESTIMADOR_SALUD_BATERIA_TOOL } from './tool/batteryHealthEstimator/index';
|
|
26
26
|
export { TONE_GENERATOR_TOOL } from './tool/toneGenerator/index';
|
|
27
|
+
export { REFRESH_RATE_DETECTOR_TOOL } from './tool/refreshRateDetector/index';
|
|
28
|
+
export { SPECTRUM_CANVAS_TOOL } from './tool/colorAccuracyTest/index';
|
|
@@ -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 =
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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,16 @@ 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
|
+
const cssFiles = import.meta.glob('../../tool/*/*.css', { query: "?raw", import: "default" });
|
|
85
|
+
const cssKey = Object.keys(cssFiles).find((k) => k.endsWith(`/${englishSlug}.css`));
|
|
86
|
+
const toolCss = cssKey ? await cssFiles[cssKey]() as string : "";
|
|
72
87
|
|
|
73
|
-
const seoContent: UtilitySEOContent = { locale, sections: content.seo };
|
|
88
|
+
const seoContent: UtilitySEOContent = { locale, sections: content.seo ?? [] };
|
|
74
89
|
|
|
75
90
|
const words = content.title.split(" ");
|
|
76
91
|
const titleHighlight = words[0] || "";
|
|
@@ -89,8 +104,9 @@ const titleBase = words.slice(1).join(" ") || "";
|
|
|
89
104
|
tools={allToolsNav}
|
|
90
105
|
/>
|
|
91
106
|
<Fragment slot="head">
|
|
107
|
+
{toolCss ? <Fragment set:html={`<style is:inline>${toolCss}</style>`} /> : null}
|
|
92
108
|
{
|
|
93
|
-
content.schemas.map((schema: unknown) => (
|
|
109
|
+
( content.schemas ?? []).map((schema: unknown) => (
|
|
94
110
|
<script
|
|
95
111
|
is:inline
|
|
96
112
|
type="application/ld+json"
|
|
@@ -7,36 +7,22 @@ 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('
|
|
10
|
+
it('bibliography should be defined', async () => {
|
|
11
11
|
const loader = tool.entry.i18n[locale as keyof typeof tool.entry.i18n];
|
|
12
12
|
const content = (await loader?.()) as ToolLocaleContent;
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
-
}
|
|
14
|
+
expect(
|
|
15
|
+
content.bibliography,
|
|
16
|
+
`Tool "${tool.entry.id}" locale "${locale}" is missing bibliography`,
|
|
17
|
+
).toBeDefined();
|
|
32
18
|
});
|
|
33
19
|
});
|
|
34
20
|
});
|
|
35
21
|
});
|
|
36
22
|
});
|
|
37
23
|
|
|
38
|
-
it('all
|
|
39
|
-
expect(ALL_TOOLS.length).toBe(
|
|
24
|
+
it('all 9 tools registered', () => {
|
|
25
|
+
expect(ALL_TOOLS.length).toBe(9);
|
|
40
26
|
});
|
|
41
27
|
});
|
|
42
28
|
|
|
@@ -40,7 +40,7 @@ describe('No H1 in Components', () => {
|
|
|
40
40
|
const content = readFileSync(file, 'utf-8');
|
|
41
41
|
expect(
|
|
42
42
|
hasH1(content),
|
|
43
|
-
`File "${relativePath}" contains a <h1> element. Use <h2> or lower inside components
|
|
43
|
+
`File "${relativePath}" contains a <h1> element. Use <h2> or lower inside components -h1 belongs to the page layout.`,
|
|
44
44
|
).toBe(false);
|
|
45
45
|
});
|
|
46
46
|
});
|
|
@@ -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
|
+
});
|
|
@@ -4,8 +4,8 @@ import { hardwareCategory } from '../data';
|
|
|
4
4
|
|
|
5
5
|
describe('Tool Validation Suite', () => {
|
|
6
6
|
describe('Library Registration', () => {
|
|
7
|
-
it('should have
|
|
8
|
-
expect(ALL_TOOLS.length).toBe(
|
|
7
|
+
it('should have 9 tools in ALL_TOOLS', () => {
|
|
8
|
+
expect(ALL_TOOLS.length).toBe(9);
|
|
9
9
|
});
|
|
10
10
|
|
|
11
11
|
it('hardwareCategory should be defined', () => {
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { BibliographyEntry } from '../../../types';
|
|
2
|
+
|
|
3
|
+
export const bibliography: BibliographyEntry[] = [
|
|
4
|
+
|
|
5
|
+
{
|
|
6
|
+
name: 'Journal of Power Sources',
|
|
7
|
+
url: 'https://www.sciencedirect.com/journal/journal-of-power-sources',
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
name: 'IEEE Xplore -Lithium-Ion Battery Life Prediction',
|
|
11
|
+
url: 'https://ieeexplore.ieee.org/abstract/document/11090151',
|
|
12
|
+
},
|
|
13
|
+
];
|
|
@@ -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 { EstimadorSaludBateriaUI } from '../ui';
|
|
4
|
+
import { bibliography } from '../bibliography';
|
|
4
5
|
|
|
5
6
|
const slug = 'lithium-batterie-gesundheitsrechner';
|
|
6
7
|
const title = 'Rechner für den Zustand von Lithium Batterien';
|
|
@@ -87,21 +88,10 @@ export const content: ToolLocaleContent<EstimadorSaludBateriaUI> = {
|
|
|
87
88
|
slug,
|
|
88
89
|
title,
|
|
89
90
|
description,
|
|
90
|
-
faqTitle: 'Häufig gestellte Fragen',
|
|
91
91
|
faq: faqData,
|
|
92
|
-
bibliographyTitle: 'Referenzen',
|
|
93
|
-
bibliography: [
|
|
94
|
-
{
|
|
95
|
-
name: 'Journal of Power Sources',
|
|
96
|
-
url: 'https://www.sciencedirect.com/journal/journal-of-power-sources',
|
|
97
|
-
},
|
|
98
|
-
{
|
|
99
|
-
name: 'IEEE Xplore — Lithium-Ion Battery Life Prediction',
|
|
100
|
-
url: 'https://ieeexplore.ieee.org/abstract/document/11090151',
|
|
101
|
-
},
|
|
102
|
-
],
|
|
103
92
|
howTo: howToData,
|
|
104
93
|
schemas: [faqSchema, howToSchema, appSchema],
|
|
94
|
+
bibliography,
|
|
105
95
|
seo: [
|
|
106
96
|
{ type: 'title', text: 'Die Chemie der Zeit: Warum Lithium Batterien sterben', level: 2 },
|
|
107
97
|
{
|
|
@@ -153,6 +143,6 @@ export const content: ToolLocaleContent<EstimadorSaludBateriaUI> = {
|
|
|
153
143
|
recVoltageHigh: 'Vermeiden Sie es, die Batterie über längere Zeit bei 100 % Ladung (4,2 V) zu halten.',
|
|
154
144
|
recVoltageLow: 'Tiefe Entladungen vermeiden; Zyklen zwischen 20 % und 80 % verdoppeln die Lebensdauer.',
|
|
155
145
|
recSohLow: 'Kapazität ist unter den optimalen Standard gefallen. Erwägen Sie einen Austausch, wenn die Autonomie nicht ausreicht.',
|
|
156
|
-
recDefault: 'Behalten Sie Ihre aktuellen Gewohnheiten bei
|
|
146
|
+
recDefault: 'Behalten Sie Ihre aktuellen Gewohnheiten bei -Ihre Batterie befindet sich in einem idealen Betriebsbereich.',
|
|
157
147
|
},
|
|
158
148
|
};
|
|
@@ -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 { EstimadorSaludBateriaUI } from '../ui';
|
|
4
|
+
import { bibliography } from '../bibliography';
|
|
4
5
|
|
|
5
6
|
const slug = 'lithium-battery-health-calculator';
|
|
6
7
|
const title = 'Lithium Battery Health Calculator';
|
|
@@ -87,21 +88,10 @@ export const content: ToolLocaleContent<EstimadorSaludBateriaUI> = {
|
|
|
87
88
|
slug,
|
|
88
89
|
title,
|
|
89
90
|
description,
|
|
90
|
-
faqTitle: 'Frequently Asked Questions',
|
|
91
91
|
faq: faqData,
|
|
92
|
-
bibliographyTitle: 'References',
|
|
93
|
-
bibliography: [
|
|
94
|
-
{
|
|
95
|
-
name: 'Journal of Power Sources',
|
|
96
|
-
url: 'https://www.sciencedirect.com/journal/journal-of-power-sources',
|
|
97
|
-
},
|
|
98
|
-
{
|
|
99
|
-
name: 'IEEE Xplore — Lithium-Ion Battery Life Prediction',
|
|
100
|
-
url: 'https://ieeexplore.ieee.org/abstract/document/11090151',
|
|
101
|
-
},
|
|
102
|
-
],
|
|
103
92
|
howTo: howToData,
|
|
104
93
|
schemas: [faqSchema, howToSchema, appSchema],
|
|
94
|
+
bibliography,
|
|
105
95
|
seo: [
|
|
106
96
|
{ type: 'title', text: 'The chemistry of time: why lithium batteries die', level: 2 },
|
|
107
97
|
{
|
|
@@ -153,6 +143,6 @@ export const content: ToolLocaleContent<EstimadorSaludBateriaUI> = {
|
|
|
153
143
|
recVoltageHigh: 'Avoid keeping the battery at 100% charge (4.2V) for extended periods.',
|
|
154
144
|
recVoltageLow: 'Avoid deep discharges; cycles between 20% and 80% double battery life.',
|
|
155
145
|
recSohLow: 'Capacity has dropped below the optimal standard. Consider a replacement if autonomy is insufficient.',
|
|
156
|
-
recDefault: 'Keep your current habits
|
|
146
|
+
recDefault: 'Keep your current habits -your battery is in an ideal operating range.',
|
|
157
147
|
},
|
|
158
148
|
};
|
|
@@ -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 { EstimadorSaludBateriaUI } from '../ui';
|
|
4
|
+
import { bibliography } from '../bibliography';
|
|
4
5
|
|
|
5
6
|
const slug = 'estimador-salud-bateria';
|
|
6
7
|
const title = 'Calculadora de Salud de Batería de Litio';
|
|
@@ -87,21 +88,10 @@ export const content: ToolLocaleContent<EstimadorSaludBateriaUI> = {
|
|
|
87
88
|
slug,
|
|
88
89
|
title,
|
|
89
90
|
description,
|
|
90
|
-
faqTitle: 'Preguntas Frecuentes',
|
|
91
91
|
faq: faqData,
|
|
92
|
-
bibliographyTitle: 'Referencias',
|
|
93
|
-
bibliography: [
|
|
94
|
-
{
|
|
95
|
-
name: 'Journal of Power Sources',
|
|
96
|
-
url: 'https://www.sciencedirect.com/journal/journal-of-power-sources',
|
|
97
|
-
},
|
|
98
|
-
{
|
|
99
|
-
name: 'IEEE Xplore — Lithium-Ion Battery Life Prediction',
|
|
100
|
-
url: 'https://ieeexplore.ieee.org/abstract/document/11090151',
|
|
101
|
-
},
|
|
102
|
-
],
|
|
103
92
|
howTo: howToData,
|
|
104
93
|
schemas: [faqSchema, howToSchema, appSchema],
|
|
94
|
+
bibliography,
|
|
105
95
|
seo: [
|
|
106
96
|
{ type: 'title', text: 'La química del tiempo: por qué mueren las baterías de litio', level: 2 },
|
|
107
97
|
{
|
|
@@ -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 { EstimadorSaludBateriaUI } from '../ui';
|
|
4
|
+
import { bibliography } from '../bibliography';
|
|
4
5
|
|
|
5
6
|
const slug = 'calculateur-sante-batterie-lithium';
|
|
6
7
|
const title = 'Calculateur de Santé de Batterie Lithium';
|
|
@@ -87,21 +88,10 @@ export const content: ToolLocaleContent<EstimadorSaludBateriaUI> = {
|
|
|
87
88
|
slug,
|
|
88
89
|
title,
|
|
89
90
|
description,
|
|
90
|
-
faqTitle: 'Questions Fréquentes',
|
|
91
91
|
faq: faqData,
|
|
92
|
-
bibliographyTitle: 'Références',
|
|
93
|
-
bibliography: [
|
|
94
|
-
{
|
|
95
|
-
name: 'Journal of Power Sources',
|
|
96
|
-
url: 'https://www.sciencedirect.com/journal/journal-of-power-sources',
|
|
97
|
-
},
|
|
98
|
-
{
|
|
99
|
-
name: 'IEEE Xplore — Lithium-Ion Battery Life Prediction',
|
|
100
|
-
url: 'https://ieeexplore.ieee.org/abstract/document/11090151',
|
|
101
|
-
},
|
|
102
|
-
],
|
|
103
92
|
howTo: howToData,
|
|
104
93
|
schemas: [faqSchema, howToSchema, appSchema],
|
|
94
|
+
bibliography,
|
|
105
95
|
seo: [
|
|
106
96
|
{ type: 'title', text: 'La chimie du temps : pourquoi les batteries lithium meurent', level: 2 },
|
|
107
97
|
{
|
|
@@ -153,6 +143,6 @@ export const content: ToolLocaleContent<EstimadorSaludBateriaUI> = {
|
|
|
153
143
|
recVoltageHigh: "Évitez de maintenir la batterie à 100% de charge (4,2V) pendant de longues périodes.",
|
|
154
144
|
recVoltageLow: "Évitez les décharges profondes ; les cycles entre 20% et 80% doublent la durée de vie.",
|
|
155
145
|
recSohLow: "La capacité est tombée en dessous du standard optimal. Envisagez un remplacement si l'autonomie est insuffisante.",
|
|
156
|
-
recDefault: "Maintenez vos habitudes actuelles
|
|
146
|
+
recDefault: "Maintenez vos habitudes actuelles -votre batterie est dans une plage de fonctionnement idéale.",
|
|
157
147
|
},
|
|
158
148
|
};
|
|
@@ -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 { EstimadorSaludBateriaUI } from '../ui';
|
|
4
|
+
import { bibliography } from '../bibliography';
|
|
4
5
|
|
|
5
6
|
const slug = 'kalkulator-kesehatan-baterai-lithium';
|
|
6
7
|
const title = 'Kalkulator Kesehatan Baterai Lithium';
|
|
@@ -87,21 +88,10 @@ export const content: ToolLocaleContent<EstimadorSaludBateriaUI> = {
|
|
|
87
88
|
slug,
|
|
88
89
|
title,
|
|
89
90
|
description,
|
|
90
|
-
faqTitle: 'Pertanyaan yang Sering Diajukan',
|
|
91
91
|
faq: faqData,
|
|
92
|
-
bibliographyTitle: 'Referensi',
|
|
93
|
-
bibliography: [
|
|
94
|
-
{
|
|
95
|
-
name: 'Journal of Power Sources',
|
|
96
|
-
url: 'https://www.sciencedirect.com/journal/journal-of-power-sources',
|
|
97
|
-
},
|
|
98
|
-
{
|
|
99
|
-
name: 'IEEE Xplore — Lithium-Ion Battery Life Prediction',
|
|
100
|
-
url: 'https://ieeexplore.ieee.org/abstract/document/11090151',
|
|
101
|
-
},
|
|
102
|
-
],
|
|
103
92
|
howTo: howToData,
|
|
104
93
|
schemas: [faqSchema, howToSchema, appSchema],
|
|
94
|
+
bibliography,
|
|
105
95
|
seo: [
|
|
106
96
|
{ type: 'title', text: 'Kimia waktu: mengapa baterai lithium mati', level: 2 },
|
|
107
97
|
{
|
|
@@ -153,6 +143,6 @@ export const content: ToolLocaleContent<EstimadorSaludBateriaUI> = {
|
|
|
153
143
|
recVoltageHigh: 'Hindari menjaga baterai pada daya 100% (4.2V) untuk waktu yang lama.',
|
|
154
144
|
recVoltageLow: 'Hindari pengosongan daya yang dalam; siklus antara 20% dan 80% menggandakan masa pakai baterai.',
|
|
155
145
|
recSohLow: 'Kapasitas telah turun di bawah standar optimal. Pertimbangkan penggantian jika otonomi tidak mencukupi.',
|
|
156
|
-
recDefault: 'Pertahankan kebiasaan Anda saat ini
|
|
146
|
+
recDefault: 'Pertahankan kebiasaan Anda saat ini -baterai Anda berada dalam rentang operasi yang ideal.',
|
|
157
147
|
},
|
|
158
148
|
};
|
|
@@ -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 { EstimadorSaludBateriaUI } from '../ui';
|
|
4
|
+
import { bibliography } from '../bibliography';
|
|
4
5
|
|
|
5
6
|
const slug = 'calcolatore-salute-batteria-litio';
|
|
6
7
|
const title = 'Calcolatore Salute della Batteria al Litio';
|
|
@@ -87,21 +88,10 @@ export const content: ToolLocaleContent<EstimadorSaludBateriaUI> = {
|
|
|
87
88
|
slug,
|
|
88
89
|
title,
|
|
89
90
|
description,
|
|
90
|
-
faqTitle: 'Domande Frequenti',
|
|
91
91
|
faq: faqData,
|
|
92
|
-
bibliographyTitle: 'Riferimenti',
|
|
93
|
-
bibliography: [
|
|
94
|
-
{
|
|
95
|
-
name: 'Journal of Power Sources',
|
|
96
|
-
url: 'https://www.sciencedirect.com/journal/journal-of-power-sources',
|
|
97
|
-
},
|
|
98
|
-
{
|
|
99
|
-
name: 'IEEE Xplore — Lithium-Ion Battery Life Prediction',
|
|
100
|
-
url: 'https://ieeexplore.ieee.org/abstract/document/11090151',
|
|
101
|
-
},
|
|
102
|
-
],
|
|
103
92
|
howTo: howToData,
|
|
104
93
|
schemas: [faqSchema, howToSchema, appSchema],
|
|
94
|
+
bibliography,
|
|
105
95
|
seo: [
|
|
106
96
|
{ type: 'title', text: 'La chimica del tempo: perché le batterie al litio muoiono', level: 2 },
|
|
107
97
|
{
|
|
@@ -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 { EstimadorSaludBateriaUI } from '../ui';
|
|
4
|
+
import { bibliography } from '../bibliography';
|
|
4
5
|
|
|
5
6
|
const slug = 'lithium-battery-health-calculator';
|
|
6
7
|
const title = 'リチウムイオン電池寿命診断ツール';
|
|
@@ -87,21 +88,10 @@ export const content: ToolLocaleContent<EstimadorSaludBateriaUI> = {
|
|
|
87
88
|
slug,
|
|
88
89
|
title,
|
|
89
90
|
description,
|
|
90
|
-
faqTitle: 'よくある質問',
|
|
91
91
|
faq: faqData,
|
|
92
|
-
bibliographyTitle: '参考文献',
|
|
93
|
-
bibliography: [
|
|
94
|
-
{
|
|
95
|
-
name: 'Journal of Power Sources',
|
|
96
|
-
url: 'https://www.sciencedirect.com/journal/journal-of-power-sources',
|
|
97
|
-
},
|
|
98
|
-
{
|
|
99
|
-
name: 'IEEE Xplore — Lithium-Ion Battery Life Prediction',
|
|
100
|
-
url: 'https://ieeexplore.ieee.org/abstract/document/11090151',
|
|
101
|
-
},
|
|
102
|
-
],
|
|
103
92
|
howTo: howToData,
|
|
104
93
|
schemas: [faqSchema, howToSchema, appSchema],
|
|
94
|
+
bibliography,
|
|
105
95
|
seo: [
|
|
106
96
|
{ type: 'title', text: '時間の化学:リチウム電池が寿命を迎える理由', level: 2 },
|
|
107
97
|
{
|
|
@@ -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 { EstimadorSaludBateriaUI } from '../ui';
|
|
4
|
+
import { bibliography } from '../bibliography';
|
|
4
5
|
|
|
5
6
|
const slug = 'lithium-battery-health-calculator';
|
|
6
7
|
const title = '리튬 배터리 수명 진단 계산기';
|
|
@@ -87,21 +88,10 @@ export const content: ToolLocaleContent<EstimadorSaludBateriaUI> = {
|
|
|
87
88
|
slug,
|
|
88
89
|
title,
|
|
89
90
|
description,
|
|
90
|
-
faqTitle: '자주 묻는 질문',
|
|
91
91
|
faq: faqData,
|
|
92
|
-
bibliographyTitle: '참고 문헌',
|
|
93
|
-
bibliography: [
|
|
94
|
-
{
|
|
95
|
-
name: 'Journal of Power Sources',
|
|
96
|
-
url: 'https://www.sciencedirect.com/journal/journal-of-power-sources',
|
|
97
|
-
},
|
|
98
|
-
{
|
|
99
|
-
name: 'IEEE Xplore — Lithium-Ion Battery Life Prediction',
|
|
100
|
-
url: 'https://ieeexplore.ieee.org/abstract/document/11090151',
|
|
101
|
-
},
|
|
102
|
-
],
|
|
103
92
|
howTo: howToData,
|
|
104
93
|
schemas: [faqSchema, howToSchema, appSchema],
|
|
94
|
+
bibliography,
|
|
105
95
|
seo: [
|
|
106
96
|
{ type: 'title', text: '시간의 화학: 리튬 배터리가 노화되는 이유', level: 2 },
|
|
107
97
|
{
|