@jjlmoya/utils-creative 1.6.0 → 1.8.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 +63 -62
- package/src/category/i18n/de.ts +9 -0
- package/src/category/i18n/id.ts +9 -0
- package/src/category/i18n/it.ts +9 -0
- package/src/category/i18n/ja.ts +9 -0
- package/src/category/i18n/ko.ts +9 -0
- package/src/category/i18n/nl.ts +9 -0
- package/src/category/i18n/pl.ts +9 -0
- package/src/category/i18n/pt.ts +9 -0
- package/src/category/i18n/ru.ts +9 -0
- package/src/category/i18n/sv.ts +9 -0
- package/src/category/i18n/tr.ts +9 -0
- package/src/category/i18n/zh.ts +9 -0
- package/src/category/index.ts +12 -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/schemas_fulfillment.test.ts +23 -0
- package/src/tests/title_quality.test.ts +55 -0
- package/src/tests/tool_validation.test.ts +2 -2
- package/src/tool/bead-pattern-generator/i18n/de.ts +115 -0
- package/src/tool/bead-pattern-generator/i18n/en.ts +59 -17
- package/src/tool/bead-pattern-generator/i18n/es.ts +59 -17
- package/src/tool/bead-pattern-generator/i18n/fr.ts +59 -17
- package/src/tool/bead-pattern-generator/i18n/id.ts +115 -0
- package/src/tool/bead-pattern-generator/i18n/it.ts +115 -0
- package/src/tool/bead-pattern-generator/i18n/ja.ts +115 -0
- package/src/tool/bead-pattern-generator/i18n/ko.ts +115 -0
- package/src/tool/bead-pattern-generator/i18n/nl.ts +115 -0
- package/src/tool/bead-pattern-generator/i18n/pl.ts +115 -0
- package/src/tool/bead-pattern-generator/i18n/pt.ts +115 -0
- package/src/tool/bead-pattern-generator/i18n/ru.ts +115 -0
- package/src/tool/bead-pattern-generator/i18n/sv.ts +115 -0
- package/src/tool/bead-pattern-generator/i18n/tr.ts +115 -0
- package/src/tool/bead-pattern-generator/i18n/zh.ts +115 -0
- package/src/tool/bead-pattern-generator/index.ts +12 -0
- package/src/tool/dice-roller/i18n/de.ts +138 -0
- package/src/tool/dice-roller/i18n/en.ts +74 -32
- package/src/tool/dice-roller/i18n/es.ts +74 -32
- package/src/tool/dice-roller/i18n/fr.ts +74 -32
- package/src/tool/dice-roller/i18n/id.ts +138 -0
- package/src/tool/dice-roller/i18n/it.ts +138 -0
- package/src/tool/dice-roller/i18n/ja.ts +138 -0
- package/src/tool/dice-roller/i18n/ko.ts +138 -0
- package/src/tool/dice-roller/i18n/nl.ts +138 -0
- package/src/tool/dice-roller/i18n/pl.ts +138 -0
- package/src/tool/dice-roller/i18n/pt.ts +138 -0
- package/src/tool/dice-roller/i18n/ru.ts +138 -0
- package/src/tool/dice-roller/i18n/sv.ts +138 -0
- package/src/tool/dice-roller/i18n/tr.ts +138 -0
- package/src/tool/dice-roller/i18n/zh.ts +138 -0
- package/src/tool/dice-roller/index.ts +12 -0
- package/src/tool/excuse-generator/i18n/de.ts +122 -0
- package/src/tool/excuse-generator/i18n/en.ts +60 -18
- package/src/tool/excuse-generator/i18n/es.ts +60 -18
- package/src/tool/excuse-generator/i18n/fr.ts +60 -18
- package/src/tool/excuse-generator/i18n/id.ts +122 -0
- package/src/tool/excuse-generator/i18n/it.ts +122 -0
- package/src/tool/excuse-generator/i18n/ja.ts +122 -0
- package/src/tool/excuse-generator/i18n/ko.ts +122 -0
- package/src/tool/excuse-generator/i18n/nl.ts +122 -0
- package/src/tool/excuse-generator/i18n/pl.ts +122 -0
- package/src/tool/excuse-generator/i18n/pt.ts +122 -0
- package/src/tool/excuse-generator/i18n/ru.ts +122 -0
- package/src/tool/excuse-generator/i18n/sv.ts +122 -0
- package/src/tool/excuse-generator/i18n/tr.ts +122 -0
- package/src/tool/excuse-generator/i18n/zh.ts +122 -0
- package/src/tool/excuse-generator/index.ts +12 -0
- package/src/tool/fortune-cookie/i18n/de.ts +127 -0
- package/src/tool/fortune-cookie/i18n/en.ts +60 -18
- package/src/tool/fortune-cookie/i18n/es.ts +60 -18
- package/src/tool/fortune-cookie/i18n/fr.ts +60 -18
- package/src/tool/fortune-cookie/i18n/id.ts +127 -0
- package/src/tool/fortune-cookie/i18n/it.ts +127 -0
- package/src/tool/fortune-cookie/i18n/ja.ts +127 -0
- package/src/tool/fortune-cookie/i18n/ko.ts +127 -0
- package/src/tool/fortune-cookie/i18n/nl.ts +127 -0
- package/src/tool/fortune-cookie/i18n/pl.ts +127 -0
- package/src/tool/fortune-cookie/i18n/pt.ts +127 -0
- package/src/tool/fortune-cookie/i18n/ru.ts +127 -0
- package/src/tool/fortune-cookie/i18n/sv.ts +127 -0
- package/src/tool/fortune-cookie/i18n/tr.ts +127 -0
- package/src/tool/fortune-cookie/i18n/zh.ts +127 -0
- package/src/tool/fortune-cookie/index.ts +12 -0
- package/src/tool/synesthesia-painter/i18n/de.ts +122 -0
- package/src/tool/synesthesia-painter/i18n/en.ts +74 -32
- package/src/tool/synesthesia-painter/i18n/es.ts +74 -32
- package/src/tool/synesthesia-painter/i18n/fr.ts +75 -33
- package/src/tool/synesthesia-painter/i18n/id.ts +122 -0
- package/src/tool/synesthesia-painter/i18n/it.ts +122 -0
- package/src/tool/synesthesia-painter/i18n/ja.ts +122 -0
- package/src/tool/synesthesia-painter/i18n/ko.ts +122 -0
- package/src/tool/synesthesia-painter/i18n/nl.ts +122 -0
- package/src/tool/synesthesia-painter/i18n/pl.ts +122 -0
- package/src/tool/synesthesia-painter/i18n/pt.ts +122 -0
- package/src/tool/synesthesia-painter/i18n/ru.ts +122 -0
- package/src/tool/synesthesia-painter/i18n/sv.ts +122 -0
- package/src/tool/synesthesia-painter/i18n/tr.ts +122 -0
- package/src/tool/synesthesia-painter/i18n/zh.ts +122 -0
- package/src/tool/synesthesia-painter/index.ts +12 -0
- package/src/tool/zalgo-generator/i18n/de.ts +113 -0
- package/src/tool/zalgo-generator/i18n/en.ts +58 -16
- package/src/tool/zalgo-generator/i18n/es.ts +58 -16
- package/src/tool/zalgo-generator/i18n/fr.ts +58 -16
- package/src/tool/zalgo-generator/i18n/id.ts +113 -0
- package/src/tool/zalgo-generator/i18n/it.ts +113 -0
- package/src/tool/zalgo-generator/i18n/ja.ts +113 -0
- package/src/tool/zalgo-generator/i18n/ko.ts +113 -0
- package/src/tool/zalgo-generator/i18n/nl.ts +113 -0
- package/src/tool/zalgo-generator/i18n/pl.ts +113 -0
- package/src/tool/zalgo-generator/i18n/pt.ts +113 -0
- package/src/tool/zalgo-generator/i18n/ru.ts +113 -0
- package/src/tool/zalgo-generator/i18n/sv.ts +113 -0
- package/src/tool/zalgo-generator/i18n/tr.ts +113 -0
- package/src/tool/zalgo-generator/i18n/zh.ts +113 -0
- package/src/tool/zalgo-generator/index.ts +12 -0
- package/src/tools.ts +14 -1
package/package.json
CHANGED
|
@@ -1,64 +1,65 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
"
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
"
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
2
|
+
"name": "@jjlmoya/utils-creative",
|
|
3
|
+
"version": "1.8.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./src/index.ts",
|
|
6
|
+
"types": "./src/index.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./src/index.ts",
|
|
9
|
+
"./data": "./src/data.ts"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"src"
|
|
13
|
+
],
|
|
14
|
+
"publishConfig": {
|
|
15
|
+
"access": "public"
|
|
16
|
+
},
|
|
17
|
+
"scripts": {
|
|
18
|
+
"dev": "astro dev",
|
|
19
|
+
"start": "astro dev",
|
|
20
|
+
"build": "astro build",
|
|
21
|
+
"preview": "astro preview",
|
|
22
|
+
"astro": "astro",
|
|
23
|
+
"lint": "eslint src/ --max-warnings 0 && stylelint \"src/**/*.{css,astro}\"",
|
|
24
|
+
"check": "astro check",
|
|
25
|
+
"type-check": "astro check",
|
|
26
|
+
"test": "vitest run",
|
|
27
|
+
"preversion": "npm run lint && npm run test",
|
|
28
|
+
"postversion": "git push && git push --tags",
|
|
29
|
+
"patch": "npm version patch",
|
|
30
|
+
"minor": "npm version minor",
|
|
31
|
+
"major": "npm version major"
|
|
32
|
+
},
|
|
33
|
+
"lint-staged": {
|
|
34
|
+
"*.{ts,tsx,astro}": [
|
|
35
|
+
"eslint --fix"
|
|
36
|
+
]
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@iconify-json/mdi": "^1.2.3",
|
|
40
|
+
"@jjlmoya/prompagate": "^1.1.0",
|
|
41
|
+
"@jjlmoya/utils-shared": "1.2.0",
|
|
42
|
+
"astro": "^6.1.2",
|
|
43
|
+
"astro-icon": "^1.1.0"
|
|
44
|
+
},
|
|
45
|
+
"peerDependencies": {
|
|
46
|
+
"jszip": "^3.10.1"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@astrojs/check": "^0.9.8",
|
|
50
|
+
"eslint": "^9.39.4",
|
|
51
|
+
"eslint-plugin-astro": "^1.6.0",
|
|
52
|
+
"eslint-plugin-no-comments": "^1.1.10",
|
|
53
|
+
"husky": "^9.1.7",
|
|
54
|
+
"jszip": "^3.10.1",
|
|
55
|
+
"lint-staged": "^16.4.0",
|
|
56
|
+
"postcss-html": "^1.8.1",
|
|
57
|
+
"schema-dts": "^1.1.2",
|
|
58
|
+
"stylelint": "^17.6.0",
|
|
59
|
+
"stylelint-config-standard": "^40.0.0",
|
|
60
|
+
"stylelint-declaration-strict-value": "^1.11.1",
|
|
61
|
+
"typescript": "^5.4.0",
|
|
62
|
+
"typescript-eslint": "^8.58.0",
|
|
63
|
+
"vitest": "^4.1.2"
|
|
64
|
+
}
|
|
64
65
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export const content = {
|
|
2
|
+
slug: 'kreativitaet-freizeit',
|
|
3
|
+
title: 'Kreativität und Freizeit',
|
|
4
|
+
description: 'Werkzeuge, die inspirieren, amüsieren und kreativ machen.',
|
|
5
|
+
seo: [
|
|
6
|
+
{ type: 'title', text: 'Tägliche kreative Hilfsmittel', level: 2 },
|
|
7
|
+
{ type: 'paragraph', html: 'Von Ausredengeneratoren bis hin zu Bastelmustern – unsere Sammlung kreativer Werkzeuge ist für Unterhaltung und praktischen Gebrauch konzipiert.' }
|
|
8
|
+
]
|
|
9
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export const content = {
|
|
2
|
+
slug: 'kreativitas-hiburan',
|
|
3
|
+
title: 'Kreativitas dan Hiburan',
|
|
4
|
+
description: 'Alat yang dirancang untuk menginspirasi, menghibur, dan berkreasi.',
|
|
5
|
+
seo: [
|
|
6
|
+
{ type: 'title', text: 'Utilitas Kreatif Harian', level: 2 },
|
|
7
|
+
{ type: 'paragraph', html: 'Dari pembuat alasan hingga pola kerajinan, koleksi alat kreatif kami dirancang untuk hiburan dan penggunaan praktis.' }
|
|
8
|
+
]
|
|
9
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export const content = {
|
|
2
|
+
slug: 'creativita-tempo-libero',
|
|
3
|
+
title: 'Creatività e Tempo Libero',
|
|
4
|
+
description: 'Strumenti progettati per ispirare, divertire e creare.',
|
|
5
|
+
seo: [
|
|
6
|
+
{ type: 'title', text: 'Utility Creative Quotidiane', level: 2 },
|
|
7
|
+
{ type: 'paragraph', html: 'Dai generatori di scuse ai modelli per il fai-da-te, la nostra collezione di strumenti creativi è pensata per l\'intrattenimento e l\'uso pratico.' }
|
|
8
|
+
]
|
|
9
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export const content = {
|
|
2
|
+
slug: 'creativity-leisure-ja',
|
|
3
|
+
title: 'クリエイティビティとレジャー',
|
|
4
|
+
description: 'インスピレーションを与え、楽しみ、創造するためのツール。',
|
|
5
|
+
seo: [
|
|
6
|
+
{ type: 'title', text: '毎日のクリエイティブ・ユーティリティ', level: 2 },
|
|
7
|
+
{ type: 'paragraph', html: '言訳ジェネレーターから手芸パターンまで、私たちのクリエイティブツールコレクションは、エンターテインメントと実用性のために設計されています。' }
|
|
8
|
+
]
|
|
9
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export const content = {
|
|
2
|
+
slug: 'creativity-leisure-ko',
|
|
3
|
+
title: '창의성과 여가',
|
|
4
|
+
description: '영감을 주고, 즐거움을 선사하며, 창작을 돕기 위해 설계된 도구들입니다.',
|
|
5
|
+
seo: [
|
|
6
|
+
{ type: 'title', text: '일상의 창의적인 유틸리티', level: 2 },
|
|
7
|
+
{ type: 'paragraph', html: '변명 생성기부터 공예 패턴까지, 당사의 창의적인 도구 컬렉션은 즐거움과 실용적인 사용을 위해 제작되었습니다.' }
|
|
8
|
+
]
|
|
9
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export const content = {
|
|
2
|
+
slug: 'creativiteit-vrije-tijd',
|
|
3
|
+
title: 'Creativiteit en Vrije Tijd',
|
|
4
|
+
description: 'Hulpmiddelen ontworpen om te inspireren, te vermaken en te creëren.',
|
|
5
|
+
seo: [
|
|
6
|
+
{ type: 'title', text: 'Dagelijkse Creatieve Hulpmiddelen', level: 2 },
|
|
7
|
+
{ type: 'paragraph', html: 'Van smoesjesgeneratoren tot knutselpatronen, onze collectie creatieve hulpmiddelen is ontworpen voor entertainment en praktisch gebruik.' }
|
|
8
|
+
]
|
|
9
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export const content = {
|
|
2
|
+
slug: 'kreatywnosc-i-rozrywka',
|
|
3
|
+
title: 'Kreatywność i Rozrywka',
|
|
4
|
+
description: 'Narzędzia zaprojektowane, aby inspirować, bawić i tworzyć.',
|
|
5
|
+
seo: [
|
|
6
|
+
{ type: 'title', text: 'Codzienne Narzędzia Kreatywne', level: 2 },
|
|
7
|
+
{ type: 'paragraph', html: 'Od generatorów wymówek po wzory do rękodzieła, nasza kolekcja narzędzi kreatywnych została stworzona z myślą o rozrywce i praktycznym zastosowaniu.' }
|
|
8
|
+
]
|
|
9
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export const content = {
|
|
2
|
+
slug: 'criatividade-lazer',
|
|
3
|
+
title: 'Criatividade e Lazer',
|
|
4
|
+
description: 'Ferramentas concebidas para inspirar, divertir e criar.',
|
|
5
|
+
seo: [
|
|
6
|
+
{ type: 'title', text: 'Utilitários Criativos Diários', level: 2 },
|
|
7
|
+
{ type: 'paragraph', html: 'Desde geradores de desculpas a padrões de artesanato, a nossa coleção de ferramentas criativas foi desenhada para entretenimento e uso prático.' }
|
|
8
|
+
]
|
|
9
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export const content = {
|
|
2
|
+
slug: 'creativity-leisure-ru',
|
|
3
|
+
title: 'Творчество и досуг',
|
|
4
|
+
description: 'Инструменты, созданные для того, чтобы вдохновлять, развлекать и создавать.',
|
|
5
|
+
seo: [
|
|
6
|
+
{ type: 'title', text: 'Ежедневные творческие утилиты', level: 2 },
|
|
7
|
+
{ type: 'paragraph', html: 'От генераторов оправданий до шаблонов для рукоделия — наша коллекция творческих инструментов создана для развлечения и практического использования.' }
|
|
8
|
+
]
|
|
9
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export const content = {
|
|
2
|
+
slug: 'kreativitet-fritid',
|
|
3
|
+
title: 'Kreativitet och Fritid',
|
|
4
|
+
description: 'Verktyg designade för att inspirera, roa och skapa.',
|
|
5
|
+
seo: [
|
|
6
|
+
{ type: 'title', text: 'Dagliga Kreativa Verktyg', level: 2 },
|
|
7
|
+
{ type: 'paragraph', html: 'Från ursäktsgeneratorer till hantverksmönster, vår samling av kreativa verktyg är designad för underhållning och praktisk användning.' }
|
|
8
|
+
]
|
|
9
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export const content = {
|
|
2
|
+
slug: 'yaraticilik-bos-zaman',
|
|
3
|
+
title: 'Yaratıcılık ve Boş Zaman',
|
|
4
|
+
description: 'İlham vermek, eğlendirmek ve yaratmak için tasarlanmış araçlar.',
|
|
5
|
+
seo: [
|
|
6
|
+
{ type: 'title', text: 'Günlük Yaratıcı Araçlar', level: 2 },
|
|
7
|
+
{ type: 'paragraph', html: 'Mazeret üreteçlerinden el işi desenlerine kadar yaratıcı araç koleksiyonumuz eğlence ve pratik kullanım için tasarlanmıştır.' }
|
|
8
|
+
]
|
|
9
|
+
};
|
package/src/category/index.ts
CHANGED
|
@@ -30,5 +30,17 @@ export const creativeCategory: {
|
|
|
30
30
|
es: () => import('./i18n/es').then((m) => m.content),
|
|
31
31
|
en: () => import('./i18n/en').then((m) => m.content),
|
|
32
32
|
fr: () => import('./i18n/fr').then((m) => m.content),
|
|
33
|
+
de: () => import('./i18n/de').then((m) => m.content),
|
|
34
|
+
id: () => import('./i18n/id').then((m) => m.content),
|
|
35
|
+
it: () => import('./i18n/it').then((m) => m.content),
|
|
36
|
+
ja: () => import('./i18n/ja').then((m) => m.content),
|
|
37
|
+
ko: () => import('./i18n/ko').then((m) => m.content),
|
|
38
|
+
nl: () => import('./i18n/nl').then((m) => m.content),
|
|
39
|
+
pl: () => import('./i18n/pl').then((m) => m.content),
|
|
40
|
+
pt: () => import('./i18n/pt').then((m) => m.content),
|
|
41
|
+
ru: () => import('./i18n/ru').then((m) => m.content),
|
|
42
|
+
sv: () => import('./i18n/sv').then((m) => m.content),
|
|
43
|
+
tr: () => import('./i18n/tr').then((m) => m.content),
|
|
44
|
+
zh: () => import('./i18n/zh').then((m) => m.content),
|
|
33
45
|
},
|
|
34
46
|
};
|
|
@@ -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
|
+
});
|
|
@@ -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('Schemas Fulfillment Validation', () => {
|
|
6
|
+
ALL_TOOLS.forEach((tool) => {
|
|
7
|
+
describe(`Tool: ${tool.entry.id}`, () => {
|
|
8
|
+
Object.keys(tool.entry.i18n).forEach((locale) => {
|
|
9
|
+
it(`Locale: ${locale} should have faqSchema, appSchema and howToSchema`, async () => {
|
|
10
|
+
const loader = tool.entry.i18n[locale as keyof typeof tool.entry.i18n];
|
|
11
|
+
if (!loader) return;
|
|
12
|
+
const content = (await loader()) as ToolLocaleContent;
|
|
13
|
+
|
|
14
|
+
const schemaTypes = content.schemas.map((s: any) => s['@type']);
|
|
15
|
+
|
|
16
|
+
expect(schemaTypes, `Tool "${tool.entry.id}" locale "${locale}" is missing FAQPage schema`).toContain('FAQPage');
|
|
17
|
+
expect(schemaTypes, `Tool "${tool.entry.id}" locale "${locale}" is missing SoftwareApplication schema`).toContain('SoftwareApplication');
|
|
18
|
+
expect(schemaTypes, `Tool "${tool.entry.id}" locale "${locale}" is missing HowTo schema`).toContain('HowTo');
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
});
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { describe, it } from 'vitest';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
|
|
5
|
+
function getFiles(dir: string, ext: string[]): string[] {
|
|
6
|
+
const results: string[] = [];
|
|
7
|
+
if (!fs.existsSync(dir)) return results;
|
|
8
|
+
const list = fs.readdirSync(dir);
|
|
9
|
+
for (const file of list) {
|
|
10
|
+
const fullPath = path.join(dir, file);
|
|
11
|
+
const stat = fs.statSync(fullPath);
|
|
12
|
+
if (stat && stat.isDirectory()) {
|
|
13
|
+
results.push(...getFiles(fullPath, ext));
|
|
14
|
+
} else if (ext.some((e) => file.endsWith(e))) {
|
|
15
|
+
results.push(fullPath);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return results;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const SRC_DIR = path.join(process.cwd(), 'src');
|
|
22
|
+
|
|
23
|
+
describe('Project Titles - Separator Validation', () => {
|
|
24
|
+
const files = [
|
|
25
|
+
...getFiles(path.join(SRC_DIR, 'tool'), ['.ts']),
|
|
26
|
+
...getFiles(path.join(SRC_DIR, 'category'), ['.ts']),
|
|
27
|
+
].filter(f => f.includes('i18n'));
|
|
28
|
+
|
|
29
|
+
it.each(files)('Verify that titles in %s do not contain | or -', (filePath) => {
|
|
30
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
31
|
+
const relativePath = path.relative(process.cwd(), filePath);
|
|
32
|
+
|
|
33
|
+
const titlePatterns = [
|
|
34
|
+
/const\s+title\s*=\s*['"]([^'"]+)['"]/g,
|
|
35
|
+
/title\s*:\s*['"]([^'"]+)['"]/g,
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
const findings: string[] = [];
|
|
39
|
+
|
|
40
|
+
for (const pattern of titlePatterns) {
|
|
41
|
+
let match;
|
|
42
|
+
while ((match = pattern.exec(content)) !== null) {
|
|
43
|
+
const title = match[1];
|
|
44
|
+
if (title.includes('|') || title.includes('-')) {
|
|
45
|
+
findings.push(title);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (findings.length > 0) {
|
|
51
|
+
const list = findings.map((f) => ` - "${f}"`).join('\n');
|
|
52
|
+
throw new Error(`Forbidden separators (| or -) found in titles in ${relativePath}:\n${list}`);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
});
|
|
@@ -4,8 +4,8 @@ import { creativeCategory } 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 6 tools in ALL_TOOLS', () => {
|
|
8
|
+
expect(ALL_TOOLS.length).toBe(6);
|
|
9
9
|
});
|
|
10
10
|
|
|
11
11
|
it('creativeCategory should be defined', () => {
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import type { WithContext, FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
|
|
2
|
+
import type { BeadPatternGeneratorLocaleContent } from '../index';
|
|
3
|
+
|
|
4
|
+
const slug = 'perlenmuster-generator';
|
|
5
|
+
const title = 'Muster Generator';
|
|
6
|
+
const description = 'Erstellen Sie Pixelkunst und Perlenschemata für Miyuki oder Hama aus Ihren Fotos. Farbauantierungsalgorithmus, Tunnelblick-Modus und ZIP-Export.';
|
|
7
|
+
|
|
8
|
+
const faq: BeadPatternGeneratorLocaleContent['faq'] = [
|
|
9
|
+
{ question: 'Was ist Farbauantierung bei Mustern?', answer: 'Dabei werden die Tausenden von Farben in einem Foto auf einige wenige reduziert, die den tatsächlich verfügbaren Perlenfarben entsprechen (z. B. Miyuki oder Hama). Wir verwenden intelligente Algorithmen, um die visuelle Ähnlichkeit mit einer minimalen Palette beizubehalten.' },
|
|
10
|
+
{ question: 'Kann ich dieses Muster für Kreuzstich verwenden?', answer: 'Ja, der Generator erstellt ein Rasterdiagramm, das perfekt für Kreuzstich geeignet ist. Sie müssen lediglich eine Rastergröße wählen, die zu Ihrem Stoff passt (Aida 14, 18 usw.).' },
|
|
11
|
+
{ question: 'Was ist der Unterschied zwischen Miyuki- und Hama-Perlen?', answer: 'Miyuki Delica-Perlen sind sehr kleine, präzise Glasperlen für Schmuck. Hama-Perlen sind aus Kunststoff und werden mit einem Bügeleisen verschmolzen. Unser Werkzeug ermöglicht es Ihnen, das Seitenverhältnis anzupassen, damit sich das Muster je nach verwendetem Material nicht verzerrt.' },
|
|
12
|
+
{ question: 'Wie funktioniert der Dithering-Algorithmus?', answer: 'Dithering erzeugt die Illusion von mehr Farben, indem Pixel verschiedener Farben in Abständen gemischt werden. Dies hilft dabei, Farbübergänge auch mit einer begrenzten Perlenpalette glatter erscheinen zu lassen.' },
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
const howTo: BeadPatternGeneratorLocaleContent['howTo'] = [
|
|
16
|
+
{ name: 'Ein klares Bild hochladen', text: 'Wählen Sie ein Foto mit gutem Kontrast und wenigen kleinen Details, damit das Muster einfacher zu befolgen ist.' },
|
|
17
|
+
{ name: 'Die Rastergröße anpassen', text: 'Legen Sie fest, wie viele Perlen breit und hoch Ihr fertiges Stück sein soll. Mehr Perlen = mehr Details, aber auch höherer Schwierigkeitsgrad.' },
|
|
18
|
+
{ name: 'Die Farbpalette optimieren', text: 'Reduzieren Sie die Anzahl der Farben, bis sie mit den Perlen übereinstimmen, die Sie in Ihrem Bastelset zur Verfügung haben.' },
|
|
19
|
+
{ name: 'Das Musterschema exportieren', text: 'Generieren Sie das endgültige Muster mit Farbcodes, um es als Referenz beim Zusammensetzen Ihrer Perlen auf der Platte oder dem Faden zu verwenden.' },
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
const faqSchema: WithContext<FAQPage> = {
|
|
23
|
+
'@context': 'https://schema.org',
|
|
24
|
+
'@type': 'FAQPage',
|
|
25
|
+
mainEntity: faq.map((item) => ({
|
|
26
|
+
'@type': 'Question',
|
|
27
|
+
name: item.question,
|
|
28
|
+
acceptedAnswer: { '@type': 'Answer', text: item.answer },
|
|
29
|
+
})),
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const howToSchema: WithContext<HowTo> = {
|
|
33
|
+
'@context': 'https://schema.org',
|
|
34
|
+
'@type': 'HowTo',
|
|
35
|
+
name: title,
|
|
36
|
+
description,
|
|
37
|
+
step: howTo.map((step) => ({
|
|
38
|
+
'@type': 'HowToStep',
|
|
39
|
+
name: step.name,
|
|
40
|
+
text: step.text,
|
|
41
|
+
})),
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const appSchema: WithContext<SoftwareApplication> = {
|
|
45
|
+
'@context': 'https://schema.org',
|
|
46
|
+
'@type': 'SoftwareApplication',
|
|
47
|
+
name: title,
|
|
48
|
+
description,
|
|
49
|
+
applicationCategory: 'UtilitiesApplication',
|
|
50
|
+
operatingSystem: 'Web',
|
|
51
|
+
offers: { '@type': 'Offer', price: '0', priceCurrency: 'EUR' },
|
|
52
|
+
inLanguage: 'de',
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export const content: BeadPatternGeneratorLocaleContent = {
|
|
56
|
+
slug,
|
|
57
|
+
title,
|
|
58
|
+
description,
|
|
59
|
+
faqTitle: 'Häufig gestellte Fragen',
|
|
60
|
+
bibliographyTitle: 'Handwerks-Bibliographie',
|
|
61
|
+
ui: {
|
|
62
|
+
title: 'Muster Labor',
|
|
63
|
+
subtitle: 'Farbtechnik für Ihre Hände',
|
|
64
|
+
description: 'Vom Foto zum Perlenschema.',
|
|
65
|
+
gridSizeLabel: 'Größe (Breite)',
|
|
66
|
+
colorCountLabel: 'Farben',
|
|
67
|
+
optionsLabel: 'Optionen',
|
|
68
|
+
rulersLabel: 'Lineale',
|
|
69
|
+
symbolsTooltip: 'Überraschungsmuster',
|
|
70
|
+
symbolsLabel: 'Symbole',
|
|
71
|
+
downloadBtn: 'Herunterladen',
|
|
72
|
+
uploadTitle: 'Laden Sie Ihre Vision hoch',
|
|
73
|
+
uploadSubtitle: 'und lassen Sie die digitale Magie wirken',
|
|
74
|
+
paletteTitle: 'Chromatische DNA',
|
|
75
|
+
reuploadBtn: 'Bild ändern',
|
|
76
|
+
tunnelVisionTitle: 'Tunnelblick',
|
|
77
|
+
tunnelVisionSubtitle: 'Ihr Präzisionsassistent Reihe für Reihe.',
|
|
78
|
+
uploadLabel: 'Laden Sie Ihr Foto hoch',
|
|
79
|
+
pixelateBtn: 'Muster generieren',
|
|
80
|
+
faqTitle: 'FAQ',
|
|
81
|
+
bibliographyTitle: 'Referenzen'
|
|
82
|
+
},
|
|
83
|
+
seo: [
|
|
84
|
+
{ type: 'title', text: 'Digitale Alchemie: Pixel in greifbare Kunst verwandeln', level: 2 },
|
|
85
|
+
{ type: 'paragraph', html: 'Willkommen im <strong>Ultimativen Musterstudio</strong>. Ein <em>chromatisches Intelligenzsystem</em>, entwickelt für Pixel-Art-Architekten, Miyuki-Meister und Kreuzstich-Visionäre. Ihre Brücke zwischen Digitalem und Handwerk.' },
|
|
86
|
+
{ type: 'card', icon: 'mdi:grid', title: 'Das intelligente Raster', html: 'Unser Algorithmus für <strong>räumliches Subsampling</strong> "schrumpft" Ihr Bild nicht einfach nur. Er analysiert die visuelle Struktur, um komplexe Pixelgruppen auf einzelne Zellen abzubilden und so die Integrität von Silhouetten und Kanten wie ein erfahrener Illustrator zu bewahren.' },
|
|
87
|
+
{ type: 'card', icon: 'mdi:palette', title: 'K Means Farbquantisierung', html: 'Wir implementieren eine Variante des <strong>K-Means-Clustering</strong>-Algorithmus, der mathematisch die "Zentroid"-Töne Ihres Bildes findet. Erstaunliche visuelle Treue mit einer minimalistischen Palette von 12, 24 oder 32 Farben.' },
|
|
88
|
+
{ type: 'title', text: 'Meisterschaft in 3 Schritten', level: 3 },
|
|
89
|
+
{ type: 'card', icon: 'mdi:image-search', title: 'Die perfekte Auswahl', html: 'Suchen Sie nach <strong>hohem Kontrast</strong>, dramatischer Beleuchtung und ikonischen Formen. Porträts mit klarem Hintergrund, Logos und Vektorgrafiken lassen sich wunderbar umsetzen.' },
|
|
90
|
+
{ type: 'card', icon: 'mdi:ruler', title: 'Dimensionale Kalibrierung', html: '<strong>Miyuki Delica:</strong> 50 Perlen ≈ 8 cm · <strong>Hama Midi:</strong> 50 Perlen ≈ 25 cm · <strong>Kreuzstich:</strong> 1 Zelle = 1 Stich.' },
|
|
91
|
+
{ type: 'card', icon: 'mdi:eye-check', title: 'Zen Ausführung (Tunnelblick Modus)', html: 'Unser <strong>Tunnelblick</strong>-System fungiert als Ihr persönlicher Assistent, der visuelles Rauschen ausblendet und chirurgisch nur die aktive Reihe hervorhebt. Absolute Konzentration.' },
|
|
92
|
+
{ type: 'title', text: 'Unendliche Leinwände', level: 3 },
|
|
93
|
+
{ type: 'list', items: [
|
|
94
|
+
'<strong>Textiler Schmuck:</strong> Komplizierte Muster für Webstühle und geometrische Armbänder.',
|
|
95
|
+
'<strong>Kreuzstich:</strong> Moderne Schemata, bereit, Pixel für Pixel gestickt zu werden.',
|
|
96
|
+
'<strong>Mosaike:</strong> Großformatige Wandbilder aus vereinfachten Keramikmosaiksteinen.',
|
|
97
|
+
'<strong>Retro-Gaming:</strong> Authentische 8-Bit-Assets und Sprites in Sekunden.',
|
|
98
|
+
]},
|
|
99
|
+
{ type: 'stats', items: [
|
|
100
|
+
{ value: '10–100', label: 'Perlenbreite', icon: 'mdi:arrow-expand-horizontal' },
|
|
101
|
+
{ value: '2–32', label: 'Farbpalettenplätze', icon: 'mdi:palette-swatch' },
|
|
102
|
+
{ value: 'K-Means', label: 'Quantisierungsalgorithmus', icon: 'mdi:function-variant' },
|
|
103
|
+
{ value: 'ZIP', label: 'Exportformat (Muster + Anleitung)', icon: 'mdi:zip-box' },
|
|
104
|
+
], columns: 4 },
|
|
105
|
+
{ type: 'paragraph', html: 'In einer Ära flüchtiger Bildschirme ist das Erschaffen von etwas Physischem ein revolutionärer Akt. Dieses Werkzeug versucht nicht, Kunst zu automatisieren, sondern den <strong>Handwerker zu stärken</strong>. Wir geben Ihnen rechnerische Präzision, damit Ihre Hände bleibende Vermächtnisse schaffen können.' },
|
|
106
|
+
],
|
|
107
|
+
faq,
|
|
108
|
+
bibliography: [
|
|
109
|
+
{ name: 'Scikit-Image: Color Quantization using K-Means', url: 'https://scikit-learn.org/0.23/auto_examples/cluster/plot_color_quantization.html' },
|
|
110
|
+
{ name: 'Miyuki Delica Beads Specification', url: 'https://www.miyuki-beads.co.jp/english/seedbeads/delica.html' },
|
|
111
|
+
{ name: 'Visgraf Lab: Dithering Algorithms', url: 'https://www.visgraf.impa.br/Courses/ip00/proj/Dithering1/floyd_steinberg_dithering.html' },
|
|
112
|
+
],
|
|
113
|
+
howTo,
|
|
114
|
+
schemas: [faqSchema as any, howToSchema as any, appSchema],
|
|
115
|
+
};
|