@jjlmoya/utils-diy 1.3.0 → 1.5.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 +2 -1
- package/src/category/i18n/de.ts +12 -0
- package/src/category/i18n/id.ts +12 -0
- package/src/category/i18n/it.ts +12 -0
- package/src/category/i18n/ja.ts +12 -0
- package/src/category/i18n/ko.ts +12 -0
- package/src/category/i18n/nl.ts +12 -0
- package/src/category/i18n/pl.ts +12 -0
- package/src/category/i18n/pt.ts +12 -0
- package/src/category/i18n/ru.ts +12 -0
- package/src/category/i18n/sv.ts +12 -0
- package/src/category/i18n/tr.ts +12 -0
- package/src/category/i18n/zh.ts +12 -0
- package/src/category/index.ts +13 -1
- package/src/index.ts +1 -1
- package/src/tests/category_validation.test.ts +18 -0
- package/src/tests/faq_count.test.ts +1 -1
- package/src/tests/i18n_coverage.test.ts +36 -0
- package/src/tests/locale_completeness.test.ts +2 -2
- package/src/tests/seo_length.test.ts +3 -0
- package/src/tests/slug_language_code_format.test.ts +23 -0
- package/src/tests/slug_uniqueness.test.ts +81 -0
- package/src/tool/balusterCalculator/i18n/de.ts +264 -0
- package/src/tool/balusterCalculator/i18n/id.ts +264 -0
- package/src/tool/balusterCalculator/i18n/it.ts +264 -0
- package/src/tool/balusterCalculator/i18n/ja.ts +264 -0
- package/src/tool/balusterCalculator/i18n/ko.ts +264 -0
- package/src/tool/balusterCalculator/i18n/nl.ts +264 -0
- package/src/tool/balusterCalculator/i18n/pl.ts +264 -0
- package/src/tool/balusterCalculator/i18n/pt.ts +264 -0
- package/src/tool/balusterCalculator/i18n/ru.ts +264 -0
- package/src/tool/balusterCalculator/i18n/sv.ts +264 -0
- package/src/tool/balusterCalculator/i18n/tr.ts +264 -0
- package/src/tool/balusterCalculator/i18n/zh.ts +264 -0
- package/src/tool/balusterCalculator/index.ts +15 -7
- package/src/tool/clayCalculator/i18n/de.ts +183 -0
- package/src/tool/clayCalculator/i18n/id.ts +183 -0
- package/src/tool/clayCalculator/i18n/it.ts +183 -0
- package/src/tool/clayCalculator/i18n/ja.ts +182 -0
- package/src/tool/clayCalculator/i18n/ko.ts +183 -0
- package/src/tool/clayCalculator/i18n/nl.ts +183 -0
- package/src/tool/clayCalculator/i18n/pl.ts +183 -0
- package/src/tool/clayCalculator/i18n/pt.ts +183 -0
- package/src/tool/clayCalculator/i18n/ru.ts +183 -0
- package/src/tool/clayCalculator/i18n/sv.ts +183 -0
- package/src/tool/clayCalculator/i18n/tr.ts +183 -0
- package/src/tool/clayCalculator/i18n/zh.ts +183 -0
- package/src/tool/clayCalculator/index.ts +15 -7
- package/src/tool/concreteCalculator/i18n/de.ts +201 -0
- package/src/tool/concreteCalculator/i18n/id.ts +201 -0
- package/src/tool/concreteCalculator/i18n/it.ts +201 -0
- package/src/tool/concreteCalculator/i18n/ja.ts +201 -0
- package/src/tool/concreteCalculator/i18n/ko.ts +201 -0
- package/src/tool/concreteCalculator/i18n/nl.ts +201 -0
- package/src/tool/concreteCalculator/i18n/pl.ts +201 -0
- package/src/tool/concreteCalculator/i18n/pt.ts +201 -0
- package/src/tool/concreteCalculator/i18n/ru.ts +201 -0
- package/src/tool/concreteCalculator/i18n/sv.ts +201 -0
- package/src/tool/concreteCalculator/i18n/tr.ts +201 -0
- package/src/tool/concreteCalculator/i18n/zh.ts +201 -0
- package/src/tool/concreteCalculator/index.ts +17 -5
- package/src/tool/cutOptimizer/i18n/de.ts +178 -0
- package/src/tool/cutOptimizer/i18n/id.ts +178 -0
- package/src/tool/cutOptimizer/i18n/it.ts +178 -0
- package/src/tool/cutOptimizer/i18n/ja.ts +178 -0
- package/src/tool/cutOptimizer/i18n/ko.ts +178 -0
- package/src/tool/cutOptimizer/i18n/nl.ts +178 -0
- package/src/tool/cutOptimizer/i18n/pl.ts +178 -0
- package/src/tool/cutOptimizer/i18n/pt.ts +178 -0
- package/src/tool/cutOptimizer/i18n/ru.ts +178 -0
- package/src/tool/cutOptimizer/i18n/sv.ts +178 -0
- package/src/tool/cutOptimizer/i18n/tr.ts +178 -0
- package/src/tool/cutOptimizer/i18n/zh.ts +178 -0
- package/src/tool/cutOptimizer/index.ts +17 -5
- package/src/tool/drillCalculator/i18n/de.ts +137 -0
- package/src/tool/drillCalculator/i18n/id.ts +137 -0
- package/src/tool/drillCalculator/i18n/it.ts +137 -0
- package/src/tool/drillCalculator/i18n/ja.ts +137 -0
- package/src/tool/drillCalculator/i18n/ko.ts +137 -0
- package/src/tool/drillCalculator/i18n/nl.ts +137 -0
- package/src/tool/drillCalculator/i18n/pl.ts +137 -0
- package/src/tool/drillCalculator/i18n/pt.ts +137 -0
- package/src/tool/drillCalculator/i18n/ru.ts +137 -0
- package/src/tool/drillCalculator/i18n/sv.ts +137 -0
- package/src/tool/drillCalculator/i18n/tr.ts +137 -0
- package/src/tool/drillCalculator/i18n/zh.ts +137 -0
- package/src/tool/drillCalculator/index.ts +17 -5
- package/src/tool/drillSharpener/i18n/de.ts +120 -0
- package/src/tool/drillSharpener/i18n/id.ts +120 -0
- package/src/tool/drillSharpener/i18n/it.ts +120 -0
- package/src/tool/drillSharpener/i18n/ja.ts +120 -0
- package/src/tool/drillSharpener/i18n/ko.ts +120 -0
- package/src/tool/drillSharpener/i18n/nl.ts +120 -0
- package/src/tool/drillSharpener/i18n/pl.ts +120 -0
- package/src/tool/drillSharpener/i18n/pt.ts +120 -0
- package/src/tool/drillSharpener/i18n/ru.ts +120 -0
- package/src/tool/drillSharpener/i18n/sv.ts +120 -0
- package/src/tool/drillSharpener/i18n/tr.ts +120 -0
- package/src/tool/drillSharpener/i18n/zh.ts +120 -0
- package/src/tool/drillSharpener/index.ts +17 -5
- package/src/tool/epoxyCalculator/i18n/de.ts +118 -0
- package/src/tool/epoxyCalculator/i18n/id.ts +118 -0
- package/src/tool/epoxyCalculator/i18n/it.ts +118 -0
- package/src/tool/epoxyCalculator/i18n/ja.ts +118 -0
- package/src/tool/epoxyCalculator/i18n/ko.ts +118 -0
- package/src/tool/epoxyCalculator/i18n/nl.ts +118 -0
- package/src/tool/epoxyCalculator/i18n/pl.ts +118 -0
- package/src/tool/epoxyCalculator/i18n/pt.ts +118 -0
- package/src/tool/epoxyCalculator/i18n/ru.ts +118 -0
- package/src/tool/epoxyCalculator/i18n/sv.ts +118 -0
- package/src/tool/epoxyCalculator/i18n/tr.ts +118 -0
- package/src/tool/epoxyCalculator/i18n/zh.ts +118 -0
- package/src/tool/epoxyCalculator/index.ts +15 -7
- package/src/tool/furnitureFit/i18n/de.ts +113 -0
- package/src/tool/furnitureFit/i18n/id.ts +113 -0
- package/src/tool/furnitureFit/i18n/it.ts +113 -0
- package/src/tool/furnitureFit/i18n/ja.ts +113 -0
- package/src/tool/furnitureFit/i18n/ko.ts +113 -0
- package/src/tool/furnitureFit/i18n/nl.ts +113 -0
- package/src/tool/furnitureFit/i18n/pl.ts +113 -0
- package/src/tool/furnitureFit/i18n/pt.ts +113 -0
- package/src/tool/furnitureFit/i18n/ru.ts +113 -0
- package/src/tool/furnitureFit/i18n/sv.ts +113 -0
- package/src/tool/furnitureFit/i18n/tr.ts +113 -0
- package/src/tool/furnitureFit/i18n/zh.ts +113 -0
- package/src/tool/furnitureFit/index.ts +17 -5
- package/src/tool/mortarCalculator/i18n/de.ts +129 -0
- package/src/tool/mortarCalculator/i18n/id.ts +129 -0
- package/src/tool/mortarCalculator/i18n/it.ts +129 -0
- package/src/tool/mortarCalculator/i18n/ja.ts +129 -0
- package/src/tool/mortarCalculator/i18n/ko.ts +129 -0
- package/src/tool/mortarCalculator/i18n/nl.ts +129 -0
- package/src/tool/mortarCalculator/i18n/pl.ts +129 -0
- package/src/tool/mortarCalculator/i18n/pt.ts +129 -0
- package/src/tool/mortarCalculator/i18n/ru.ts +129 -0
- package/src/tool/mortarCalculator/i18n/sv.ts +129 -0
- package/src/tool/mortarCalculator/i18n/tr.ts +129 -0
- package/src/tool/mortarCalculator/i18n/zh.ts +129 -0
- package/src/tool/mortarCalculator/index.ts +17 -5
- package/src/tool/passepartoutCalculator/i18n/de.ts +108 -0
- package/src/tool/passepartoutCalculator/i18n/id.ts +108 -0
- package/src/tool/passepartoutCalculator/i18n/it.ts +108 -0
- package/src/tool/passepartoutCalculator/i18n/ja.ts +108 -0
- package/src/tool/passepartoutCalculator/i18n/ko.ts +108 -0
- package/src/tool/passepartoutCalculator/i18n/nl.ts +108 -0
- package/src/tool/passepartoutCalculator/i18n/pl.ts +108 -0
- package/src/tool/passepartoutCalculator/i18n/pt.ts +108 -0
- package/src/tool/passepartoutCalculator/i18n/ru.ts +108 -0
- package/src/tool/passepartoutCalculator/i18n/sv.ts +108 -0
- package/src/tool/passepartoutCalculator/i18n/tr.ts +108 -0
- package/src/tool/passepartoutCalculator/i18n/zh.ts +108 -0
- package/src/tool/passepartoutCalculator/index.ts +17 -5
- package/src/tool/stairCalculator/i18n/de.ts +144 -0
- package/src/tool/stairCalculator/i18n/id.ts +144 -0
- package/src/tool/stairCalculator/i18n/it.ts +144 -0
- package/src/tool/stairCalculator/i18n/ja.ts +144 -0
- package/src/tool/stairCalculator/i18n/ko.ts +144 -0
- package/src/tool/stairCalculator/i18n/nl.ts +144 -0
- package/src/tool/stairCalculator/i18n/pl.ts +144 -0
- package/src/tool/stairCalculator/i18n/pt.ts +144 -0
- package/src/tool/stairCalculator/i18n/ru.ts +144 -0
- package/src/tool/stairCalculator/i18n/sv.ts +144 -0
- package/src/tool/stairCalculator/i18n/tr.ts +144 -0
- package/src/tool/stairCalculator/i18n/zh.ts +144 -0
- package/src/tool/stairCalculator/index.ts +17 -5
- package/src/tool/thermalExpansionCalculator/i18n/de.ts +132 -0
- package/src/tool/thermalExpansionCalculator/i18n/id.ts +130 -0
- package/src/tool/thermalExpansionCalculator/i18n/it.ts +130 -0
- package/src/tool/thermalExpansionCalculator/i18n/ja.ts +130 -0
- package/src/tool/thermalExpansionCalculator/i18n/ko.ts +130 -0
- package/src/tool/thermalExpansionCalculator/i18n/nl.ts +130 -0
- package/src/tool/thermalExpansionCalculator/i18n/pl.ts +130 -0
- package/src/tool/thermalExpansionCalculator/i18n/pt.ts +130 -0
- package/src/tool/thermalExpansionCalculator/i18n/ru.ts +130 -0
- package/src/tool/thermalExpansionCalculator/i18n/sv.ts +130 -0
- package/src/tool/thermalExpansionCalculator/i18n/tr.ts +130 -0
- package/src/tool/thermalExpansionCalculator/i18n/zh.ts +130 -0
- package/src/tool/thermalExpansionCalculator/index.ts +17 -5
- package/src/tool/voltageDropCalculator/i18n/de.ts +112 -0
- package/src/tool/voltageDropCalculator/i18n/id.ts +110 -0
- package/src/tool/voltageDropCalculator/i18n/it.ts +110 -0
- package/src/tool/voltageDropCalculator/i18n/ja.ts +110 -0
- package/src/tool/voltageDropCalculator/i18n/ko.ts +110 -0
- package/src/tool/voltageDropCalculator/i18n/nl.ts +110 -0
- package/src/tool/voltageDropCalculator/i18n/pl.ts +110 -0
- package/src/tool/voltageDropCalculator/i18n/pt.ts +110 -0
- package/src/tool/voltageDropCalculator/i18n/ru.ts +110 -0
- package/src/tool/voltageDropCalculator/i18n/sv.ts +110 -0
- package/src/tool/voltageDropCalculator/i18n/tr.ts +110 -0
- package/src/tool/voltageDropCalculator/i18n/zh.ts +110 -0
- package/src/tool/voltageDropCalculator/index.ts +17 -5
- package/src/tools.ts +2 -0
- package/src/types.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jjlmoya/utils-diy",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./src/index.ts",
|
|
@@ -37,6 +37,7 @@
|
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@iconify-json/mdi": "^1.2.3",
|
|
40
|
+
"@jjlmoya/prompagate": "^1.1.0",
|
|
40
41
|
"@jjlmoya/utils-shared": "1.2.0",
|
|
41
42
|
"astro": "^6.1.2",
|
|
42
43
|
"astro-icon": "^1.1.0"
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { CategoryLocaleContent } from '../../types';
|
|
2
|
+
|
|
3
|
+
export const content: CategoryLocaleContent = {
|
|
4
|
+
slug: 'diy',
|
|
5
|
+
title: 'Werkzeuge und Rechner für Heimwerker und Bauprojekte',
|
|
6
|
+
description: 'Planen Sie Ihre Heimwerkerprojekte mit kostenlosen Online-Tools. Rechner für Tonschrumpfung, Harzmischungen, Beton, Schnittoptimierer und Treppenlayout.',
|
|
7
|
+
seo: [
|
|
8
|
+
{ type: 'title', text: 'Heimtechnik und DIY Projekte: Vom Entwurf zur Realität', level: 2 },
|
|
9
|
+
{ type: 'paragraph', html: 'Die <strong>Do-It-Yourself (DIY)</strong>-Bewegung ist kein bloßes Hobby mehr, sondern eine Form der technischen und kreativen Souveränität. In diesem Bereich bieten wir eine Reihe von <strong>kostenlosen Online-Tools</strong> an, die für Maker, Handwerker und Bauinteressierte entwickelt wurden, die professionelle Ergebnisse anstreben.' },
|
|
10
|
+
{ type: 'stats', columns: 2, items: [{ label: 'Optimierung', value: 'Material 95%+', icon: 'mdi:saw-blade' }, { label: 'Sicherheit', value: 'Normgerecht', icon: 'mdi:ruler-square' }, { label: 'Materialien', value: 'Wiederverwertung', icon: 'mdi:tools' }, { label: 'Präzision', value: 'Millimetergenau', icon: 'mdi:calculator' }] },
|
|
11
|
+
],
|
|
12
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { CategoryLocaleContent } from '../../types';
|
|
2
|
+
|
|
3
|
+
export const content: CategoryLocaleContent = {
|
|
4
|
+
slug: 'diy',
|
|
5
|
+
title: 'Alat dan Kalkulator untuk Proyek DIY dan Konstruksi',
|
|
6
|
+
description: 'Rencanakan proyek DIY Anda dengan alat online gratis. Kalkulator penyusutan tanah liat, campuran resin, beton, pengoptimal pemotongan, dan tata letak tangga.',
|
|
7
|
+
seo: [
|
|
8
|
+
{ type: 'title', text: 'Teknik Rumahan dan Proyek DIY: Dari Desain ke Realitas', level: 2 },
|
|
9
|
+
{ type: 'paragraph', html: 'Gerakan <strong>Do It Yourself (DIY)</strong> bukan lagi sekadar hobi, melainkan bentuk kedaulatan teknis dan kreatif. Di bagian ini, kami menyediakan serangkaian <strong>alat online gratis</strong> yang dirancang untuk maker, pengrajin, dan penggemar konstruksi yang mencari hasil profesional.' },
|
|
10
|
+
{ type: 'stats', columns: 2, items: [{ label: 'Optimasi', value: 'Material 95%+', icon: 'mdi:saw-blade' }, { label: 'Keamanan', value: 'Sesuai Standar', icon: 'mdi:ruler-square' }, { label: 'Material', value: 'Pemulihan', icon: 'mdi:tools' }, { label: 'Presisi', value: 'Milimetrik', icon: 'mdi:calculator' }] },
|
|
11
|
+
],
|
|
12
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { CategoryLocaleContent } from '../../types';
|
|
2
|
+
|
|
3
|
+
export const content: CategoryLocaleContent = {
|
|
4
|
+
slug: 'diy',
|
|
5
|
+
title: 'Strumenti e Calcolatori per Progetti DIY e Costruzione',
|
|
6
|
+
description: 'Pianifica i tuoi progetti fai da te con strumenti online gratuiti. Calcolatori per il ritiro dell\'argilla, miscele di resina, cemento, ottimizzatori di taglio e layout scale.',
|
|
7
|
+
seo: [
|
|
8
|
+
{ type: 'title', text: 'Ingegneria Domestica e Progetti DIY: Dal Design alla Realtà', level: 2 },
|
|
9
|
+
{ type: 'paragraph', html: 'Il movimento <strong>Do It Yourself (DIY)</strong> non è più un semplice hobby ma una forma di sovranità tecnica e creativa. In questa sezione, forniamo una suite di <strong>strumenti online gratuiti</strong> progettati per maker, artigiani e appassionati di costruzioni che cercano risultati professionali.' },
|
|
10
|
+
{ type: 'stats', columns: 2, items: [{ label: 'Ottimizzazione', value: 'Materiale 95%+', icon: 'mdi:saw-blade' }, { label: 'Sicurezza', value: 'Normativa', icon: 'mdi:ruler-square' }, { label: 'Materiali', value: 'Recupero', icon: 'mdi:tools' }, { label: 'Precisione', value: 'Millimetrica', icon: 'mdi:calculator' }] },
|
|
11
|
+
],
|
|
12
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { CategoryLocaleContent } from '../../types';
|
|
2
|
+
|
|
3
|
+
export const content: CategoryLocaleContent = {
|
|
4
|
+
slug: 'diy',
|
|
5
|
+
title: 'DIY・建設プロジェクト向けツールと計算機',
|
|
6
|
+
description: '無料のオンラインツールでDIYプロジェクトを計画しましょう。粘土の収縮、レジン混合、コンクリート、カット最適化、階段の設計などの計算機。',
|
|
7
|
+
seo: [
|
|
8
|
+
{ type: 'title', text: 'ホームエンジニアリングとDIYプロジェクト:設計から実現まで', level: 2 },
|
|
9
|
+
{ type: 'paragraph', html: '<strong>DIY (Do It Yourself)</strong> ムーブメントは、単なる趣味ではなく、技術的および創造的な自立の形へと進化しました。このセクションでは、プロフェッショナルな結果を求めるメーカー、職人、建設愛好家向けに設計された一連の<strong>無料オンラインツール</strong>を提供しています。' },
|
|
10
|
+
{ type: 'stats', columns: 2, items: [{ label: '最適化', value: '材料利用率 95%+', icon: 'mdi:saw-blade' }, { label: '安全性', value: '規格準拠', icon: 'mdi:ruler-square' }, { label: '材料', value: '再利用', icon: 'mdi:tools' }, { label: '精度', value: 'ミリ単位', icon: 'mdi:calculator' }] },
|
|
11
|
+
],
|
|
12
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { CategoryLocaleContent } from '../../types';
|
|
2
|
+
|
|
3
|
+
export const content: CategoryLocaleContent = {
|
|
4
|
+
slug: 'diy',
|
|
5
|
+
title: 'DIY 및 건설 프로젝트를 위한 도구와 계산기',
|
|
6
|
+
description: '무료 온라인 도구로 DIY 프로젝트를 계획하세요. 점토 수축, 레진 혼합, 콘크리트, 절단 최적화 및 계단 레이아웃 계산기.',
|
|
7
|
+
seo: [
|
|
8
|
+
{ type: 'title', text: '홈 엔지니어링 및 DIY 프로젝트: 설계에서 현실까지', level: 2 },
|
|
9
|
+
{ type: 'paragraph', html: '<strong>DIY (Do It Yourself)</strong> 운동은 더 이상 단순한 취미가 아니라 기술적, 창의적 자립의 형태가 되었습니다. 이 섹션에서는 전문적인 결과를 원하는 메이커, 장인 및 건설 애호가들을 위해 설계된 일련의 <strong>무료 온라인 도구</strong>를 제공합니다.' },
|
|
10
|
+
{ type: 'stats', columns: 2, items: [{ label: '최적화', value: '자재 95%+', icon: 'mdi:saw-blade' }, { label: '안전', value: '규격 준수', icon: 'mdi:ruler-square' }, { label: '자재', value: '재활용', icon: 'mdi:tools' }, { label: '정밀도', value: '밀리미터 단위', icon: 'mdi:calculator' }] },
|
|
11
|
+
],
|
|
12
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { CategoryLocaleContent } from '../../types';
|
|
2
|
+
|
|
3
|
+
export const content: CategoryLocaleContent = {
|
|
4
|
+
slug: 'diy',
|
|
5
|
+
title: 'Tools en Calculators voor DIY en Bouwprojecten',
|
|
6
|
+
description: 'Plan je DIY projecten met gratis online tools. Calculators voor kleikrimp, harsmengsels, beton, zaagoptimalisatie en trappenlay-out.',
|
|
7
|
+
seo: [
|
|
8
|
+
{ type: 'title', text: 'Thuisengineering en DIY projecten: Van Ontwerp tot Realiteit', level: 2 },
|
|
9
|
+
{ type: 'paragraph', html: 'De <strong>Do It Yourself (DIY)</strong> beweging is niet langer een simpele hobby, maar een vorm van technische en creatieve soevereiniteit. In deze sectie bieden we een reeks <strong>gratis online tools</strong> aan, ontworpen voor makers, ambachtslieden en bouwliefhebbers die op zoek zijn naar professionele resultaten.' },
|
|
10
|
+
{ type: 'stats', columns: 2, items: [{ label: 'Optimalisatie', value: 'Materiaal 95%+', icon: 'mdi:saw-blade' }, { label: 'Veiligheid', value: 'Volgens Normen', icon: 'mdi:ruler-square' }, { label: 'Materialen', value: 'Hergebruik', icon: 'mdi:tools' }, { label: 'Precisie', value: 'Millimeternauwkeurig', icon: 'mdi:calculator' }] },
|
|
11
|
+
],
|
|
12
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { CategoryLocaleContent } from '../../types';
|
|
2
|
+
|
|
3
|
+
export const content: CategoryLocaleContent = {
|
|
4
|
+
slug: 'diy',
|
|
5
|
+
title: 'Narzędzia i Kalkulatory dla Projektów DIY i Budowlanych',
|
|
6
|
+
description: 'Zaplanuj swoje projekty DIY dzięki darmowym narzędziom online. Kalkulatory skurczu gliny, mieszanek żywicy, betonu, optymalizatory cięcia i tyczenia schodów.',
|
|
7
|
+
seo: [
|
|
8
|
+
{ type: 'title', text: 'Inżynieria domowa i projekty DIY: Od projektu do rzeczywistości', level: 2 },
|
|
9
|
+
{ type: 'paragraph', html: 'Ruch <strong>Do It Yourself (DIY)</strong> przestał być zwykłym hobby, stając się formą technicznej i kreatywnej suwerenności. W tej sekcji udostępniamy zestaw <strong>darmowych narzędzi online</strong> zaprojektowanych dla twórców, rzemieślników i entuzjastów budownictwa szukających profesjonalnych efektów.' },
|
|
10
|
+
{ type: 'stats', columns: 2, items: [{ label: 'Optymalizacja', value: 'Materiał 95%+', icon: 'mdi:saw-blade' }, { label: 'Bezpieczeństwo', value: 'Zgodne z Normatywami', icon: 'mdi:ruler-square' }, { label: 'Materiały', value: 'Odzysk', icon: 'mdi:tools' }, { label: 'Precyzja', value: 'Milimetrowa', icon: 'mdi:calculator' }] },
|
|
11
|
+
],
|
|
12
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { CategoryLocaleContent } from '../../types';
|
|
2
|
+
|
|
3
|
+
export const content: CategoryLocaleContent = {
|
|
4
|
+
slug: 'diy',
|
|
5
|
+
title: 'Ferramentas e Calculadoras para Projetos DIY e Construção',
|
|
6
|
+
description: 'Planeie os seus projetos de bricolage com ferramentas online gratuitas. Calculadoras de contração de argila, misturas de resina, betão, otimizadores de corte e traçado de escadas.',
|
|
7
|
+
seo: [
|
|
8
|
+
{ type: 'title', text: 'Engenharia Doméstica e Projetos DIY: Do Design à Realidade', level: 2 },
|
|
9
|
+
{ type: 'paragraph', html: 'O movimento <strong>Do It Yourself (DIY)</strong> deixou de ser um simples passatempo para se tornar uma forma de soberania técnica e criativa. Nesta secção, fornecemos uma suite de <strong>ferramentas online gratuitas</strong> concebidas para makers, artesãos e entusiastas da construção que procuram resultados profissionais.' },
|
|
10
|
+
{ type: 'stats', columns: 2, items: [{ label: 'Otimização', value: 'Material 95%+', icon: 'mdi:saw-blade' }, { label: 'Segurança', value: 'Normativa', icon: 'mdi:ruler-square' }, { label: 'Materiais', value: 'Recuperação', icon: 'mdi:tools' }, { label: 'Precisão', value: 'Milimétrica', icon: 'mdi:calculator' }] },
|
|
11
|
+
],
|
|
12
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { CategoryLocaleContent } from '../../types';
|
|
2
|
+
|
|
3
|
+
export const content: CategoryLocaleContent = {
|
|
4
|
+
slug: 'diy',
|
|
5
|
+
title: 'Инструменты и калькуляторы для DIY и строительных проектов',
|
|
6
|
+
description: 'Планируйте свои DIY-проекты с помощью бесплатных онлайн-инструментов. Калькуляторы усадки глины, смесей смол, бетона, оптимизаторы резки и планировка лестниц.',
|
|
7
|
+
seo: [
|
|
8
|
+
{ type: 'title', text: 'Домашняя инженерия и DIY-проекты: от идеи к реальности', level: 2 },
|
|
9
|
+
{ type: 'paragraph', html: 'Движение <strong>Do It Yourself (DIY)</strong> перестало быть просто хобби, превратившись в форму технического и творческого суверенитета. В этом разделе мы предоставляем набор <strong>бесплатных онлайн-инструментов</strong>, разработанных для мейкеров, мастеров и любителей строительства, стремящихся к профессиональным результатам.' },
|
|
10
|
+
{ type: 'stats', columns: 2, items: [{ label: 'Оптимизация', value: 'Материал 95%+', icon: 'mdi:saw-blade' }, { label: 'Безопасность', value: 'Нормативы', icon: 'mdi:ruler-square' }, { label: 'Материалы', value: 'Восстановление', icon: 'mdi:tools' }, { label: 'Точность', value: 'Миллиметровая', icon: 'mdi:calculator' }] },
|
|
11
|
+
],
|
|
12
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { CategoryLocaleContent } from '../../types';
|
|
2
|
+
|
|
3
|
+
export const content: CategoryLocaleContent = {
|
|
4
|
+
slug: 'diy',
|
|
5
|
+
title: 'Verktyg och kalkylatorer för DIY och byggprojekt',
|
|
6
|
+
description: 'Planera dina DIY projekt med kostnadsfria onlineverktyg. Kalkylatorer för lerkrympning, hartsmixer, betong, skäroptimerare och trapplayout.',
|
|
7
|
+
seo: [
|
|
8
|
+
{ type: 'title', text: 'Hemteknik och DIY projekt: Från design till verklighet', level: 2 },
|
|
9
|
+
{ type: 'paragraph', html: '<strong>Do It Yourself (DIY)</strong>-rörelsen är inte längre bara en hobby utan en form av teknisk och kreativ självständighet. I den här sektionen tillhandahåller vi en uppsättning <strong>kostnadsfria onlineverktyg</strong> designade för makare, hantverkare och byggentusiaster som söker professionella resultat.' },
|
|
10
|
+
{ type: 'stats', columns: 2, items: [{ label: 'Optimering', value: 'Material 95%+', icon: 'mdi:saw-blade' }, { label: 'Säkerhet', value: 'Enligt Standard', icon: 'mdi:ruler-square' }, { label: 'Material', value: 'Återvinning', icon: 'mdi:tools' }, { label: 'Precision', value: 'Millimeterprecision', icon: 'mdi:calculator' }] },
|
|
11
|
+
],
|
|
12
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { CategoryLocaleContent } from '../../types';
|
|
2
|
+
|
|
3
|
+
export const content: CategoryLocaleContent = {
|
|
4
|
+
slug: 'diy',
|
|
5
|
+
title: 'Kendin Yap (DIY) ve İnşaat Projeleri için Araçlar ve Hesaplayıcılar',
|
|
6
|
+
description: 'Ücretsiz çevrimiçi araçlarla DIY projelerinizi planlayın. Kil çekmesi, reçine karışımları, beton, kesim optimizasyonu ve merdiven düzeni hesaplayıcıları.',
|
|
7
|
+
seo: [
|
|
8
|
+
{ type: 'title', text: 'Ev Mühendisliği ve DIY Projeleri: Tasarımdan Gerçeğe', level: 2 },
|
|
9
|
+
{ type: 'paragraph', html: '<strong>Do It Yourself (DIY)</strong> hareketi artık basit bir hobiden öte, teknik ve yaratıcı bir egemenlik biçimi haline geldi. Bu bölümde, profesyonel sonuçlar arayan yapımcılar, zanaatkarlar ve inşaat meraklıları için tasarlanmış bir dizi <strong>ücretsiz çevrimiçi araç</strong> sunuyoruz.' },
|
|
10
|
+
{ type: 'stats', columns: 2, items: [{ label: 'Optimizasyon', value: 'Malzeme %95+', icon: 'mdi:saw-blade' }, { label: 'Güvenlik', value: 'Standartlara Uygun', icon: 'mdi:ruler-square' }, { label: 'Malzeme', value: 'Geri Kazanım', icon: 'mdi:tools' }, { label: 'Hassasiyet', value: 'Milimetrik', icon: 'mdi:calculator' }] },
|
|
11
|
+
],
|
|
12
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { CategoryLocaleContent } from '../../types';
|
|
2
|
+
|
|
3
|
+
export const content: CategoryLocaleContent = {
|
|
4
|
+
slug: 'diy',
|
|
5
|
+
title: '针对 DIY 和建筑项目的工具与计算器',
|
|
6
|
+
description: '使用免费在线工具规划您的 DIY 项目。包含粘土收缩、树脂混合、混凝土、切割优化和楼梯布局等计算器。',
|
|
7
|
+
seo: [
|
|
8
|
+
{ type: 'title', text: '居家工程与 DIY 项目:从设计到现实', level: 2 },
|
|
9
|
+
{ type: 'paragraph', html: '<strong>DIY (Do It Yourself)</strong> 运动已不再仅仅是一种简单的爱好,而是一种技术和创造力的自主形式。在本节中,我们为寻求专业效果的高手、工匠和建筑爱好者提供了一系列<strong>免费在线工具</strong>。' },
|
|
10
|
+
{ type: 'stats', columns: 2, items: [{ label: '优化', value: '材料利用 95%+', icon: 'mdi:saw-blade' }, { label: '安全', value: '符合标准', icon: 'mdi:ruler-square' }, { label: '材料', value: '回收利用', icon: 'mdi:tools' }, { label: '精度', value: '毫米级', icon: 'mdi:calculator' }] },
|
|
11
|
+
],
|
|
12
|
+
};
|
package/src/category/index.ts
CHANGED
|
@@ -17,9 +17,21 @@ export const diyCategory: DiyCategoryEntry = {
|
|
|
17
17
|
icon: 'mdi:hand',
|
|
18
18
|
tools: [clayCalculator, epoxyCalculator, balusterCalculator, mortarCalculator, passepartoutCalculator, concreteCalculator, cutOptimizer, voltageDropCalculator, furnitureFit, thermalExpansionCalculator, drillCalculator, stairCalculator, drillSharpener],
|
|
19
19
|
i18n: {
|
|
20
|
-
|
|
20
|
+
de: () => import('./i18n/de').then((m) => m.content),
|
|
21
21
|
en: () => import('./i18n/en').then((m) => m.content),
|
|
22
|
+
es: () => import('./i18n/es').then((m) => m.content),
|
|
22
23
|
fr: () => import('./i18n/fr').then((m) => m.content),
|
|
24
|
+
id: () => import('./i18n/id').then((m) => m.content),
|
|
25
|
+
it: () => import('./i18n/it').then((m) => m.content),
|
|
26
|
+
ja: () => import('./i18n/ja').then((m) => m.content),
|
|
27
|
+
ko: () => import('./i18n/ko').then((m) => m.content),
|
|
28
|
+
nl: () => import('./i18n/nl').then((m) => m.content),
|
|
29
|
+
pl: () => import('./i18n/pl').then((m) => m.content),
|
|
30
|
+
pt: () => import('./i18n/pt').then((m) => m.content),
|
|
31
|
+
ru: () => import('./i18n/ru').then((m) => m.content),
|
|
32
|
+
sv: () => import('./i18n/sv').then((m) => m.content),
|
|
33
|
+
tr: () => import('./i18n/tr').then((m) => m.content),
|
|
34
|
+
zh: () => import('./i18n/zh').then((m) => m.content),
|
|
23
35
|
},
|
|
24
36
|
};
|
|
25
37
|
|
package/src/index.ts
CHANGED
|
@@ -15,7 +15,7 @@ export type {
|
|
|
15
15
|
ToolDefinition,
|
|
16
16
|
} from './types';
|
|
17
17
|
|
|
18
|
-
export { ALL_TOOLS } from './tools';
|
|
18
|
+
export { ALL_ENTRIES, ALL_TOOLS } from './tools';
|
|
19
19
|
|
|
20
20
|
export { ClayCalculatorComponent, ClayCalculatorSEO, ClayCalculatorBibliography } from './tool/clayCalculator';
|
|
21
21
|
export { CLAY_CALCULATOR_TOOL } from './tool/clayCalculator/index';
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { diyCategory as toolsCategory } from '../category/index';
|
|
3
|
+
|
|
4
|
+
const EXPECTED_LOCALES = [
|
|
5
|
+
'de', 'en', 'es', 'fr', 'id', 'it', 'ja', 'ko', 'nl', 'pl', 'pt', 'ru', 'sv', 'tr', 'zh'
|
|
6
|
+
];
|
|
7
|
+
|
|
8
|
+
describe('Category Validation', () => {
|
|
9
|
+
it('should have all 15 required locales', () => {
|
|
10
|
+
const registeredLocales = Object.keys(toolsCategory.i18n);
|
|
11
|
+
EXPECTED_LOCALES.forEach((locale) => {
|
|
12
|
+
expect(
|
|
13
|
+
registeredLocales,
|
|
14
|
+
`Category is missing locale "${locale}"`,
|
|
15
|
+
).toContain(locale);
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { describe, it, expect } from 'vitest';
|
|
2
2
|
import type * as DATA from '../data';
|
|
3
3
|
|
|
4
|
-
const TOOLS: typeof DATA.
|
|
4
|
+
const TOOLS: typeof DATA.audiovisualCategory[] = [];
|
|
5
5
|
|
|
6
6
|
describe('FAQ Content Validation', () => {
|
|
7
7
|
TOOLS.forEach((entry) => {
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { ALL_TOOLS } from '../tools';
|
|
3
|
+
|
|
4
|
+
const EXPECTED_LOCALES = [
|
|
5
|
+
'de', 'en', 'es', 'fr', 'id', 'it', 'ja', 'ko', 'nl', 'pl', 'pt', 'ru', 'sv', 'tr', 'zh'
|
|
6
|
+
];
|
|
7
|
+
|
|
8
|
+
describe('I18n Coverage Validation', () => {
|
|
9
|
+
it('all tools should be registered', () => {
|
|
10
|
+
expect(ALL_TOOLS.length).toBeGreaterThan(0);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
ALL_TOOLS.forEach(({ entry }: { entry: any }) => {
|
|
14
|
+
describe(`Tool: ${entry.id}`, () => {
|
|
15
|
+
it('should have all 15 required locales', () => {
|
|
16
|
+
const registeredLocales = Object.keys(entry.i18n);
|
|
17
|
+
EXPECTED_LOCALES.forEach((locale) => {
|
|
18
|
+
expect(
|
|
19
|
+
registeredLocales,
|
|
20
|
+
`Tool "${entry.id}" is missing locale "${locale}"`,
|
|
21
|
+
).toContain(locale);
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('all locale loaders should be functions', () => {
|
|
26
|
+
EXPECTED_LOCALES.forEach((locale) => {
|
|
27
|
+
const loader = entry.i18n[locale as keyof typeof entry.i18n];
|
|
28
|
+
expect(
|
|
29
|
+
typeof loader,
|
|
30
|
+
`Tool "${entry.id}" locale "${locale}" loader is not a function`,
|
|
31
|
+
).toBe('function');
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
});
|
|
@@ -35,8 +35,8 @@ describe('Locale Completeness Validation', () => {
|
|
|
35
35
|
});
|
|
36
36
|
});
|
|
37
37
|
|
|
38
|
-
it('
|
|
39
|
-
expect(ALL_TOOLS.length).
|
|
38
|
+
it('all 13 tools registered', () => {
|
|
39
|
+
expect(ALL_TOOLS.length).toBe(13);
|
|
40
40
|
});
|
|
41
41
|
});
|
|
42
42
|
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { describe, it, expect } from 'vitest';
|
|
2
2
|
import * as DATA from '../data';
|
|
3
3
|
|
|
4
|
+
import { ALL_TOOLS } from '../tools';
|
|
5
|
+
|
|
4
6
|
const ENTRIES = [
|
|
5
7
|
{ id: 'diyCategory', i18n: DATA.diyCategory.i18n },
|
|
8
|
+
...ALL_TOOLS.map(tool => ({ id: tool.entry.id, i18n: tool.entry.i18n }))
|
|
6
9
|
];
|
|
7
10
|
|
|
8
11
|
describe('SEO Content Length Validation', () => {
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { ALL_TOOLS } from '../tools';
|
|
3
|
+
import type { ToolLocaleContent } from '../types';
|
|
4
|
+
|
|
5
|
+
describe('Slug Language Code Format Validation', () => {
|
|
6
|
+
ALL_TOOLS.forEach((tool) => {
|
|
7
|
+
describe(`Tool: ${tool.entry.id}`, () => {
|
|
8
|
+
it('slug should not end with 2-letter language codes like -ja, -ru, -ko', async () => {
|
|
9
|
+
const locales = Object.keys(tool.entry.i18n);
|
|
10
|
+
|
|
11
|
+
for (const locale of locales) {
|
|
12
|
+
const loader = tool.entry.i18n[locale as keyof typeof tool.entry.i18n];
|
|
13
|
+
const content = (await loader?.()) as ToolLocaleContent;
|
|
14
|
+
|
|
15
|
+
expect(
|
|
16
|
+
content.slug,
|
|
17
|
+
`Tool "${tool.entry.id}" locale "${locale}" slug ("${content.slug}") cannot end with a 2-letter language code (e.g., -ja, -ru, -ko).`,
|
|
18
|
+
).not.toMatch(/-[a-z]{2}$/);
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
});
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { ALL_TOOLS } from '../tools';
|
|
3
|
+
import type { ToolLocaleContent } from '../types';
|
|
4
|
+
|
|
5
|
+
const sharingLocales = ['ja', 'ko', 'zh'];
|
|
6
|
+
|
|
7
|
+
interface ValidateParams {
|
|
8
|
+
toolId: string;
|
|
9
|
+
locale: string;
|
|
10
|
+
content: ToolLocaleContent;
|
|
11
|
+
enSlug: string;
|
|
12
|
+
slugs: Map<string, string>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const validateLocaleSlug = ({
|
|
16
|
+
toolId,
|
|
17
|
+
locale,
|
|
18
|
+
content,
|
|
19
|
+
enSlug,
|
|
20
|
+
slugs,
|
|
21
|
+
}: ValidateParams) => {
|
|
22
|
+
expect(
|
|
23
|
+
content.slug,
|
|
24
|
+
`Tool "${toolId}" locale "${locale}" has an invalid slug ("${content.slug}"). Slugs must be transliterated (only a-z, 0-9, and -).`,
|
|
25
|
+
).toMatch(/^[a-z0-9-]+$/);
|
|
26
|
+
|
|
27
|
+
if (locale === 'en') {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (sharingLocales.includes(locale)) {
|
|
32
|
+
expect(
|
|
33
|
+
content.slug,
|
|
34
|
+
`Tool "${toolId}" locale "${locale}" must use the same slug as "en" ("${enSlug}").`,
|
|
35
|
+
).toBe(enSlug);
|
|
36
|
+
} else {
|
|
37
|
+
expect(
|
|
38
|
+
content.slug,
|
|
39
|
+
`Tool "${toolId}" locale "${locale}" has the same slug as "en" ("${enSlug}"). Cada slug tiene que estar en su propia idioma`,
|
|
40
|
+
).not.toBe(enSlug);
|
|
41
|
+
|
|
42
|
+
if (slugs.has(content.slug)) {
|
|
43
|
+
const previousLocale = slugs.get(content.slug);
|
|
44
|
+
expect(
|
|
45
|
+
false,
|
|
46
|
+
`Tool "${toolId}" locales "${locale}" and "${previousLocale}" share the same slug ("${content.slug}"). Cada slug tiene que estar en su propia idioma`,
|
|
47
|
+
).toBe(true);
|
|
48
|
+
}
|
|
49
|
+
slugs.set(content.slug, locale);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
describe('Slug Localization and Uniqueness Validation', () => {
|
|
54
|
+
ALL_TOOLS.forEach((tool) => {
|
|
55
|
+
describe(`Tool: ${tool.entry.id}`, () => {
|
|
56
|
+
it('every locale should have a unique, translated slug', async () => {
|
|
57
|
+
const slugs = new Map<string, string>();
|
|
58
|
+
const locales = Object.keys(tool.entry.i18n);
|
|
59
|
+
|
|
60
|
+
let enSlug = '';
|
|
61
|
+
if (locales.includes('en')) {
|
|
62
|
+
const enLoader = tool.entry.i18n['en' as keyof typeof tool.entry.i18n];
|
|
63
|
+
const enContent = (await enLoader?.()) as ToolLocaleContent;
|
|
64
|
+
enSlug = enContent.slug;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
for (const locale of locales) {
|
|
68
|
+
const loader = tool.entry.i18n[locale as keyof typeof tool.entry.i18n];
|
|
69
|
+
const content = (await loader?.()) as ToolLocaleContent;
|
|
70
|
+
validateLocaleSlug({
|
|
71
|
+
toolId: tool.entry.id,
|
|
72
|
+
locale,
|
|
73
|
+
content,
|
|
74
|
+
enSlug,
|
|
75
|
+
slugs,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
});
|