@jjlmoya/utils-alcohol 1.13.0 → 1.14.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 +58 -58
- package/src/tests/schemas_fulfillment.test.ts +23 -0
- package/src/tests/title_quality.test.ts +55 -0
- package/src/tool/alcoholClearance/i18n/en.ts +22 -1
- package/src/tool/alcoholClearance/i18n/es.ts +22 -1
- package/src/tool/alcoholClearance/i18n/fr.ts +22 -1
- package/src/tool/beerCooler/i18n/en.ts +25 -4
- package/src/tool/beerCooler/i18n/es.ts +25 -4
- package/src/tool/beerCooler/i18n/fr.ts +25 -4
- package/src/tool/carbonationCalculator/i18n/en.ts +22 -1
- package/src/tool/carbonationCalculator/i18n/es.ts +22 -1
- package/src/tool/carbonationCalculator/i18n/fr.ts +22 -1
- package/src/tool/cocktailBalancer/i18n/en.ts +23 -2
- package/src/tool/cocktailBalancer/i18n/es.ts +23 -2
- package/src/tool/cocktailBalancer/i18n/fr.ts +23 -2
- package/src/tool/partyKeg/i18n/en.ts +22 -1
- package/src/tool/partyKeg/i18n/es.ts +22 -1
- package/src/tool/partyKeg/i18n/fr.ts +23 -2
package/package.json
CHANGED
|
@@ -1,60 +1,60 @@
|
|
|
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
|
-
|
|
2
|
+
"name": "@jjlmoya/utils-alcohol",
|
|
3
|
+
"version": "1.14.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/utils-shared": "1.2.0",
|
|
41
|
+
"astro": "^6.1.2",
|
|
42
|
+
"astro-icon": "^1.1.0"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@astrojs/check": "^0.9.8",
|
|
46
|
+
"eslint": "^9.39.4",
|
|
47
|
+
"eslint-plugin-astro": "^1.6.0",
|
|
48
|
+
"eslint-plugin-no-comments": "^1.1.10",
|
|
49
|
+
"husky": "^9.1.7",
|
|
50
|
+
"lint-staged": "^16.4.0",
|
|
51
|
+
"postcss-html": "^1.8.1",
|
|
52
|
+
"schema-dts": "^1.1.2",
|
|
53
|
+
"stylelint": "^17.6.0",
|
|
54
|
+
"stylelint-config-standard": "^40.0.0",
|
|
55
|
+
"stylelint-declaration-strict-value": "^1.11.1",
|
|
56
|
+
"typescript": "^5.4.0",
|
|
57
|
+
"typescript-eslint": "^8.58.0",
|
|
58
|
+
"vitest": "^4.1.2"
|
|
59
|
+
}
|
|
60
60
|
}
|
|
@@ -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
|
+
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { WithContext, SoftwareApplication } from 'schema-dts';
|
|
1
|
+
import type { WithContext, SoftwareApplication, FAQPage, HowTo } from 'schema-dts';
|
|
2
2
|
import type { AlcoholClearanceUI, AlcoholClearanceLocaleContent } from '../index';
|
|
3
3
|
|
|
4
4
|
const slug = 'alcohol-clearance-calculator';
|
|
@@ -158,6 +158,27 @@ const seo: AlcoholClearanceLocaleContent['seo'] = [
|
|
|
158
158
|
];
|
|
159
159
|
|
|
160
160
|
const schemas: AlcoholClearanceLocaleContent['schemas'] = [
|
|
161
|
+
{
|
|
162
|
+
'@context': 'https://schema.org',
|
|
163
|
+
'@type': 'FAQPage',
|
|
164
|
+
mainEntity: faq.map((item) => ({
|
|
165
|
+
'@type': 'Question',
|
|
166
|
+
name: item.question,
|
|
167
|
+
acceptedAnswer: { '@type': 'Answer', text: item.answer },
|
|
168
|
+
})),
|
|
169
|
+
} as WithContext<FAQPage>,
|
|
170
|
+
{
|
|
171
|
+
'@context': 'https://schema.org',
|
|
172
|
+
'@type': 'HowTo',
|
|
173
|
+
name: title,
|
|
174
|
+
description: description,
|
|
175
|
+
step: howTo.map((step, i) => ({
|
|
176
|
+
'@type': 'HowToStep',
|
|
177
|
+
position: i + 1,
|
|
178
|
+
name: step.name,
|
|
179
|
+
text: step.text,
|
|
180
|
+
})),
|
|
181
|
+
} as WithContext<HowTo>,
|
|
161
182
|
{
|
|
162
183
|
'@context': 'https://schema.org',
|
|
163
184
|
'@type': 'SoftwareApplication',
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { WithContext, SoftwareApplication } from 'schema-dts';
|
|
1
|
+
import type { WithContext, SoftwareApplication, FAQPage, HowTo } from 'schema-dts';
|
|
2
2
|
import type { AlcoholClearanceUI, AlcoholClearanceLocaleContent } from '../index';
|
|
3
3
|
|
|
4
4
|
const slug = 'calculadora-alcohol-resaca';
|
|
@@ -167,6 +167,27 @@ const seo: AlcoholClearanceLocaleContent['seo'] = [
|
|
|
167
167
|
];
|
|
168
168
|
|
|
169
169
|
const schemas: AlcoholClearanceLocaleContent['schemas'] = [
|
|
170
|
+
{
|
|
171
|
+
'@context': 'https://schema.org',
|
|
172
|
+
'@type': 'FAQPage',
|
|
173
|
+
mainEntity: faq.map((item) => ({
|
|
174
|
+
'@type': 'Question',
|
|
175
|
+
name: item.question,
|
|
176
|
+
acceptedAnswer: { '@type': 'Answer', text: item.answer },
|
|
177
|
+
})),
|
|
178
|
+
} as WithContext<FAQPage>,
|
|
179
|
+
{
|
|
180
|
+
'@context': 'https://schema.org',
|
|
181
|
+
'@type': 'HowTo',
|
|
182
|
+
name: title,
|
|
183
|
+
description: description,
|
|
184
|
+
step: howTo.map((step, i) => ({
|
|
185
|
+
'@type': 'HowToStep',
|
|
186
|
+
position: i + 1,
|
|
187
|
+
name: step.name,
|
|
188
|
+
text: step.text,
|
|
189
|
+
})),
|
|
190
|
+
} as WithContext<HowTo>,
|
|
170
191
|
{
|
|
171
192
|
'@context': 'https://schema.org',
|
|
172
193
|
'@type': 'SoftwareApplication',
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { WithContext, SoftwareApplication } from 'schema-dts';
|
|
1
|
+
import type { WithContext, SoftwareApplication, FAQPage, HowTo } from 'schema-dts';
|
|
2
2
|
import type { AlcoholClearanceUI, AlcoholClearanceLocaleContent } from '../index';
|
|
3
3
|
|
|
4
4
|
const slug = 'calculateur-elimination-alcohol';
|
|
@@ -149,6 +149,27 @@ const seo: AlcoholClearanceLocaleContent['seo'] = [
|
|
|
149
149
|
];
|
|
150
150
|
|
|
151
151
|
const schemas: AlcoholClearanceLocaleContent['schemas'] = [
|
|
152
|
+
{
|
|
153
|
+
'@context': 'https://schema.org',
|
|
154
|
+
'@type': 'FAQPage',
|
|
155
|
+
mainEntity: faq.map((item) => ({
|
|
156
|
+
'@type': 'Question',
|
|
157
|
+
name: item.question,
|
|
158
|
+
acceptedAnswer: { '@type': 'Answer', text: item.answer },
|
|
159
|
+
})),
|
|
160
|
+
} as WithContext<FAQPage>,
|
|
161
|
+
{
|
|
162
|
+
'@context': 'https://schema.org',
|
|
163
|
+
'@type': 'HowTo',
|
|
164
|
+
name: title,
|
|
165
|
+
description: description,
|
|
166
|
+
step: howTo.map((step, i) => ({
|
|
167
|
+
'@type': 'HowToStep',
|
|
168
|
+
position: i + 1,
|
|
169
|
+
name: step.name,
|
|
170
|
+
text: step.text,
|
|
171
|
+
})),
|
|
172
|
+
} as WithContext<HowTo>,
|
|
152
173
|
{
|
|
153
174
|
'@context': 'https://schema.org',
|
|
154
175
|
'@type': 'SoftwareApplication',
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { WithContext, SoftwareApplication } from 'schema-dts';
|
|
1
|
+
import type { WithContext, SoftwareApplication, FAQPage, HowTo } from 'schema-dts';
|
|
2
2
|
import type { BeerCoolerUI, BeerCoolerLocaleContent } from '../index';
|
|
3
3
|
|
|
4
4
|
const slug = 'beer-cooler';
|
|
@@ -135,7 +135,7 @@ const seo: BeerCoolerLocaleContent['seo'] = [
|
|
|
135
135
|
},
|
|
136
136
|
{
|
|
137
137
|
type: 'tip',
|
|
138
|
-
title: 'Pro Tip:
|
|
138
|
+
title: 'Pro Tip: Prechill the Glass',
|
|
139
139
|
html: 'The glass you serve beer into has a significant thermal mass. A warm glass can raise the temperature of a perfectly cold beer by 2-3°C instantly on contact. Place your serving glass in the freezer for 5 minutes before pouring, or rinse it with cold water. This simple step extends the window where your beer tastes exactly as intended.'
|
|
140
140
|
},
|
|
141
141
|
{
|
|
@@ -150,11 +150,32 @@ const seo: BeerCoolerLocaleContent['seo'] = [
|
|
|
150
150
|
];
|
|
151
151
|
|
|
152
152
|
const schemas: BeerCoolerLocaleContent['schemas'] = [
|
|
153
|
+
{
|
|
154
|
+
'@context': 'https://schema.org',
|
|
155
|
+
'@type': 'FAQPage',
|
|
156
|
+
mainEntity: faq.map((item) => ({
|
|
157
|
+
'@type': 'Question',
|
|
158
|
+
name: item.question,
|
|
159
|
+
acceptedAnswer: { '@type': 'Answer', text: item.answer },
|
|
160
|
+
})),
|
|
161
|
+
} as WithContext<FAQPage>,
|
|
162
|
+
{
|
|
163
|
+
'@context': 'https://schema.org',
|
|
164
|
+
'@type': 'HowTo',
|
|
165
|
+
name: title,
|
|
166
|
+
description: description,
|
|
167
|
+
step: howTo.map((step, i) => ({
|
|
168
|
+
'@type': 'HowToStep',
|
|
169
|
+
position: i + 1,
|
|
170
|
+
name: step.name,
|
|
171
|
+
text: step.text,
|
|
172
|
+
})),
|
|
173
|
+
} as WithContext<HowTo>,
|
|
153
174
|
{
|
|
154
175
|
'@context': 'https://schema.org',
|
|
155
176
|
'@type': 'SoftwareApplication',
|
|
156
|
-
name:
|
|
157
|
-
description:
|
|
177
|
+
name: title,
|
|
178
|
+
description: description,
|
|
158
179
|
applicationCategory: 'UtilityApplication',
|
|
159
180
|
operatingSystem: 'Web',
|
|
160
181
|
offers: { '@type': 'Offer', price: '0', priceCurrency: 'EUR' },
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { WithContext, SoftwareApplication } from 'schema-dts';
|
|
1
|
+
import type { WithContext, SoftwareApplication, FAQPage, HowTo } from 'schema-dts';
|
|
2
2
|
import type { BeerCoolerUI, BeerCoolerLocaleContent } from '../index';
|
|
3
3
|
|
|
4
4
|
const slug = 'calculadora-enfriamiento-cerveza';
|
|
@@ -135,7 +135,7 @@ const seo: BeerCoolerLocaleContent['seo'] = [
|
|
|
135
135
|
},
|
|
136
136
|
{
|
|
137
137
|
type: 'tip',
|
|
138
|
-
title: 'Consejo Pro:
|
|
138
|
+
title: 'Consejo Pro: Preenfría el Vaso',
|
|
139
139
|
html: 'El vaso en el que sirves la cerveza tiene una masa térmica significativa. Un vaso caliente puede subir la temperatura de una cerveza perfectamente fría en 2-3°C al instante. Mete el vaso de servicio en el congelador 5 minutos antes de servir, o enjuágalo con agua fría. Este simple paso prolonga la ventana en que tu cerveza sabe exactamente como debe.'
|
|
140
140
|
},
|
|
141
141
|
{
|
|
@@ -163,11 +163,32 @@ const seo: BeerCoolerLocaleContent['seo'] = [
|
|
|
163
163
|
];
|
|
164
164
|
|
|
165
165
|
const schemas: BeerCoolerLocaleContent['schemas'] = [
|
|
166
|
+
{
|
|
167
|
+
'@context': 'https://schema.org',
|
|
168
|
+
'@type': 'FAQPage',
|
|
169
|
+
mainEntity: faq.map((item) => ({
|
|
170
|
+
'@type': 'Question',
|
|
171
|
+
name: item.question,
|
|
172
|
+
acceptedAnswer: { '@type': 'Answer', text: item.answer },
|
|
173
|
+
})),
|
|
174
|
+
} as WithContext<FAQPage>,
|
|
175
|
+
{
|
|
176
|
+
'@context': 'https://schema.org',
|
|
177
|
+
'@type': 'HowTo',
|
|
178
|
+
name: title,
|
|
179
|
+
description: description,
|
|
180
|
+
step: howTo.map((step, i) => ({
|
|
181
|
+
'@type': 'HowToStep',
|
|
182
|
+
position: i + 1,
|
|
183
|
+
name: step.name,
|
|
184
|
+
text: step.text,
|
|
185
|
+
})),
|
|
186
|
+
} as WithContext<HowTo>,
|
|
166
187
|
{
|
|
167
188
|
'@context': 'https://schema.org',
|
|
168
189
|
'@type': 'SoftwareApplication',
|
|
169
|
-
name:
|
|
170
|
-
description:
|
|
190
|
+
name: title,
|
|
191
|
+
description: description,
|
|
171
192
|
applicationCategory: 'UtilityApplication',
|
|
172
193
|
operatingSystem: 'Web',
|
|
173
194
|
offers: { '@type': 'Offer', price: '0', priceCurrency: 'EUR' },
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { WithContext, SoftwareApplication } from 'schema-dts';
|
|
1
|
+
import type { WithContext, SoftwareApplication, FAQPage, HowTo } from 'schema-dts';
|
|
2
2
|
import type { BeerCoolerUI, BeerCoolerLocaleContent } from '../index';
|
|
3
3
|
|
|
4
4
|
const slug = 'refroidisseur-biere';
|
|
@@ -135,7 +135,7 @@ const seo: BeerCoolerLocaleContent['seo'] = [
|
|
|
135
135
|
},
|
|
136
136
|
{
|
|
137
137
|
type: 'tip',
|
|
138
|
-
title: 'Conseil Pro :
|
|
138
|
+
title: 'Conseil Pro : Prérefroidir le Verre',
|
|
139
139
|
html: 'Le verre dans lequel vous servez la bière possède une masse thermique significative. Un verre chaud peut augmenter la température d\'une bière parfaitement froide de 2 à 3°C instantanément au contact. Placez votre verre de service au congélateur 5 minutes avant de verser, ou rincez-le à l\'eau froide. Cette simple étape prolonge la fenêtre où votre bière a exactement le goût voulu.'
|
|
140
140
|
},
|
|
141
141
|
{
|
|
@@ -150,11 +150,32 @@ const seo: BeerCoolerLocaleContent['seo'] = [
|
|
|
150
150
|
];
|
|
151
151
|
|
|
152
152
|
const schemas: BeerCoolerLocaleContent['schemas'] = [
|
|
153
|
+
{
|
|
154
|
+
'@context': 'https://schema.org',
|
|
155
|
+
'@type': 'FAQPage',
|
|
156
|
+
mainEntity: faq.map((item) => ({
|
|
157
|
+
'@type': 'Question',
|
|
158
|
+
name: item.question,
|
|
159
|
+
acceptedAnswer: { '@type': 'Answer', text: item.answer },
|
|
160
|
+
})),
|
|
161
|
+
} as WithContext<FAQPage>,
|
|
162
|
+
{
|
|
163
|
+
'@context': 'https://schema.org',
|
|
164
|
+
'@type': 'HowTo',
|
|
165
|
+
name: title,
|
|
166
|
+
description: description,
|
|
167
|
+
step: howTo.map((step, i) => ({
|
|
168
|
+
'@type': 'HowToStep',
|
|
169
|
+
position: i + 1,
|
|
170
|
+
name: step.name,
|
|
171
|
+
text: step.text,
|
|
172
|
+
})),
|
|
173
|
+
} as WithContext<HowTo>,
|
|
153
174
|
{
|
|
154
175
|
'@context': 'https://schema.org',
|
|
155
176
|
'@type': 'SoftwareApplication',
|
|
156
|
-
name:
|
|
157
|
-
description:
|
|
177
|
+
name: title,
|
|
178
|
+
description: description,
|
|
158
179
|
applicationCategory: 'UtilityApplication',
|
|
159
180
|
operatingSystem: 'Web',
|
|
160
181
|
offers: { '@type': 'Offer', price: '0', priceCurrency: 'EUR' },
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { WithContext, SoftwareApplication } from 'schema-dts';
|
|
1
|
+
import type { WithContext, SoftwareApplication, FAQPage, HowTo } from 'schema-dts';
|
|
2
2
|
import type { CarbonationUI, CarbonationLocaleContent } from '../index';
|
|
3
3
|
|
|
4
4
|
const slug = 'beer-carbonation-calculator';
|
|
@@ -153,6 +153,27 @@ const seo: CarbonationLocaleContent['seo'] = [
|
|
|
153
153
|
];
|
|
154
154
|
|
|
155
155
|
const schemas: CarbonationLocaleContent['schemas'] = [
|
|
156
|
+
{
|
|
157
|
+
'@context': 'https://schema.org',
|
|
158
|
+
'@type': 'FAQPage',
|
|
159
|
+
mainEntity: faq.map((item) => ({
|
|
160
|
+
'@type': 'Question',
|
|
161
|
+
name: item.question,
|
|
162
|
+
acceptedAnswer: { '@type': 'Answer', text: item.answer },
|
|
163
|
+
})),
|
|
164
|
+
} as WithContext<FAQPage>,
|
|
165
|
+
{
|
|
166
|
+
'@context': 'https://schema.org',
|
|
167
|
+
'@type': 'HowTo',
|
|
168
|
+
name: title,
|
|
169
|
+
description: description,
|
|
170
|
+
step: howTo.map((step, i) => ({
|
|
171
|
+
'@type': 'HowToStep',
|
|
172
|
+
position: i + 1,
|
|
173
|
+
name: step.name,
|
|
174
|
+
text: step.text,
|
|
175
|
+
})),
|
|
176
|
+
} as WithContext<HowTo>,
|
|
156
177
|
{
|
|
157
178
|
'@context': 'https://schema.org',
|
|
158
179
|
'@type': 'SoftwareApplication',
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { WithContext, SoftwareApplication } from 'schema-dts';
|
|
1
|
+
import type { WithContext, SoftwareApplication, FAQPage, HowTo } from 'schema-dts';
|
|
2
2
|
import type { CarbonationUI, CarbonationLocaleContent } from '../index';
|
|
3
3
|
|
|
4
4
|
const slug = 'calculadora-carbonatacion';
|
|
@@ -157,6 +157,27 @@ const seo: CarbonationLocaleContent['seo'] = [
|
|
|
157
157
|
];
|
|
158
158
|
|
|
159
159
|
const schemas: CarbonationLocaleContent['schemas'] = [
|
|
160
|
+
{
|
|
161
|
+
'@context': 'https://schema.org',
|
|
162
|
+
'@type': 'FAQPage',
|
|
163
|
+
mainEntity: faq.map((item) => ({
|
|
164
|
+
'@type': 'Question',
|
|
165
|
+
name: item.question,
|
|
166
|
+
acceptedAnswer: { '@type': 'Answer', text: item.answer },
|
|
167
|
+
})),
|
|
168
|
+
} as WithContext<FAQPage>,
|
|
169
|
+
{
|
|
170
|
+
'@context': 'https://schema.org',
|
|
171
|
+
'@type': 'HowTo',
|
|
172
|
+
name: title,
|
|
173
|
+
description: description,
|
|
174
|
+
step: howTo.map((step, i) => ({
|
|
175
|
+
'@type': 'HowToStep',
|
|
176
|
+
position: i + 1,
|
|
177
|
+
name: step.name,
|
|
178
|
+
text: step.text,
|
|
179
|
+
})),
|
|
180
|
+
} as WithContext<HowTo>,
|
|
160
181
|
{
|
|
161
182
|
'@context': 'https://schema.org',
|
|
162
183
|
'@type': 'SoftwareApplication',
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { WithContext, SoftwareApplication } from 'schema-dts';
|
|
1
|
+
import type { WithContext, SoftwareApplication, FAQPage, HowTo } from 'schema-dts';
|
|
2
2
|
import type { CarbonationUI, CarbonationLocaleContent } from '../index';
|
|
3
3
|
|
|
4
4
|
const slug = 'calculateur-carbonatation-biere';
|
|
@@ -153,6 +153,27 @@ const seo: CarbonationLocaleContent['seo'] = [
|
|
|
153
153
|
];
|
|
154
154
|
|
|
155
155
|
const schemas: CarbonationLocaleContent['schemas'] = [
|
|
156
|
+
{
|
|
157
|
+
'@context': 'https://schema.org',
|
|
158
|
+
'@type': 'FAQPage',
|
|
159
|
+
mainEntity: faq.map((item) => ({
|
|
160
|
+
'@type': 'Question',
|
|
161
|
+
name: item.question,
|
|
162
|
+
acceptedAnswer: { '@type': 'Answer', text: item.answer },
|
|
163
|
+
})),
|
|
164
|
+
} as WithContext<FAQPage>,
|
|
165
|
+
{
|
|
166
|
+
'@context': 'https://schema.org',
|
|
167
|
+
'@type': 'HowTo',
|
|
168
|
+
name: title,
|
|
169
|
+
description: description,
|
|
170
|
+
step: howTo.map((step, i) => ({
|
|
171
|
+
'@type': 'HowToStep',
|
|
172
|
+
position: i + 1,
|
|
173
|
+
name: step.name,
|
|
174
|
+
text: step.text,
|
|
175
|
+
})),
|
|
176
|
+
} as WithContext<HowTo>,
|
|
156
177
|
{
|
|
157
178
|
'@context': 'https://schema.org',
|
|
158
179
|
'@type': 'SoftwareApplication',
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type { WithContext, SoftwareApplication } from 'schema-dts';
|
|
1
|
+
import type { WithContext, SoftwareApplication, FAQPage, HowTo } from 'schema-dts';
|
|
2
2
|
import type { CocktailBalancerUI, CocktailBalancerLocaleContent } from '../index';
|
|
3
3
|
|
|
4
4
|
const slug = 'cocktail-balancer';
|
|
5
|
-
const title = 'Cocktail Balancer
|
|
5
|
+
const title = 'Cocktail Balancer: The Sour Law';
|
|
6
6
|
const description = 'Calculate the perfect balance between sweet and sour for your cocktails. Master the golden ratio of mixology.';
|
|
7
7
|
|
|
8
8
|
const ui: CocktailBalancerUI = {
|
|
@@ -175,6 +175,27 @@ const seo: CocktailBalancerLocaleContent['seo'] = [
|
|
|
175
175
|
];
|
|
176
176
|
|
|
177
177
|
const schemas: CocktailBalancerLocaleContent['schemas'] = [
|
|
178
|
+
{
|
|
179
|
+
'@context': 'https://schema.org',
|
|
180
|
+
'@type': 'FAQPage',
|
|
181
|
+
mainEntity: faq.map((item) => ({
|
|
182
|
+
'@type': 'Question',
|
|
183
|
+
name: item.question,
|
|
184
|
+
acceptedAnswer: { '@type': 'Answer', text: item.answer },
|
|
185
|
+
})),
|
|
186
|
+
} as WithContext<FAQPage>,
|
|
187
|
+
{
|
|
188
|
+
'@context': 'https://schema.org',
|
|
189
|
+
'@type': 'HowTo',
|
|
190
|
+
name: title,
|
|
191
|
+
description: description,
|
|
192
|
+
step: howTo.map((step, i) => ({
|
|
193
|
+
'@type': 'HowToStep',
|
|
194
|
+
position: i + 1,
|
|
195
|
+
name: step.name,
|
|
196
|
+
text: step.text,
|
|
197
|
+
})),
|
|
198
|
+
} as WithContext<HowTo>,
|
|
178
199
|
{
|
|
179
200
|
'@context': 'https://schema.org',
|
|
180
201
|
'@type': 'SoftwareApplication',
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type { WithContext, SoftwareApplication } from 'schema-dts';
|
|
1
|
+
import type { WithContext, SoftwareApplication, FAQPage, HowTo } from 'schema-dts';
|
|
2
2
|
import type { CocktailBalancerUI, CocktailBalancerLocaleContent } from '../index';
|
|
3
3
|
|
|
4
4
|
const slug = 'equilibrador-cocteles';
|
|
5
|
-
const title = 'Equilibrador de Cócteles
|
|
5
|
+
const title = 'Equilibrador de Cócteles: La Ley del Sour';
|
|
6
6
|
const description = 'Calcula el equilibrio perfecto entre dulce y ácido para tus cócteles. Domina la proporción áurea de la mixología.';
|
|
7
7
|
|
|
8
8
|
const ui: CocktailBalancerUI = {
|
|
@@ -175,6 +175,27 @@ const seo: CocktailBalancerLocaleContent['seo'] = [
|
|
|
175
175
|
];
|
|
176
176
|
|
|
177
177
|
const schemas: CocktailBalancerLocaleContent['schemas'] = [
|
|
178
|
+
{
|
|
179
|
+
'@context': 'https://schema.org',
|
|
180
|
+
'@type': 'FAQPage',
|
|
181
|
+
mainEntity: faq.map((item) => ({
|
|
182
|
+
'@type': 'Question',
|
|
183
|
+
name: item.question,
|
|
184
|
+
acceptedAnswer: { '@type': 'Answer', text: item.answer },
|
|
185
|
+
})),
|
|
186
|
+
} as WithContext<FAQPage>,
|
|
187
|
+
{
|
|
188
|
+
'@context': 'https://schema.org',
|
|
189
|
+
'@type': 'HowTo',
|
|
190
|
+
name: title,
|
|
191
|
+
description: description,
|
|
192
|
+
step: howTo.map((step, i) => ({
|
|
193
|
+
'@type': 'HowToStep',
|
|
194
|
+
position: i + 1,
|
|
195
|
+
name: step.name,
|
|
196
|
+
text: step.text,
|
|
197
|
+
})),
|
|
198
|
+
} as WithContext<HowTo>,
|
|
178
199
|
{
|
|
179
200
|
'@context': 'https://schema.org',
|
|
180
201
|
'@type': 'SoftwareApplication',
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type { WithContext, SoftwareApplication } from 'schema-dts';
|
|
1
|
+
import type { WithContext, SoftwareApplication, FAQPage, HowTo } from 'schema-dts';
|
|
2
2
|
import type { CocktailBalancerUI, CocktailBalancerLocaleContent } from '../index';
|
|
3
3
|
|
|
4
4
|
const slug = 'equilibre-cocktail';
|
|
5
|
-
const title = 'Équilibreur de Cocktails
|
|
5
|
+
const title = 'Équilibreur de Cocktails: La Loi du Sour';
|
|
6
6
|
const description = 'Calculez l\'équilibre parfait entre le sucré et l\'acide pour vos cocktails. Maîtrisez le nombre d\'or de la mixologie.';
|
|
7
7
|
|
|
8
8
|
const ui: CocktailBalancerUI = {
|
|
@@ -175,6 +175,27 @@ const seo: CocktailBalancerLocaleContent['seo'] = [
|
|
|
175
175
|
];
|
|
176
176
|
|
|
177
177
|
const schemas: CocktailBalancerLocaleContent['schemas'] = [
|
|
178
|
+
{
|
|
179
|
+
'@context': 'https://schema.org',
|
|
180
|
+
'@type': 'FAQPage',
|
|
181
|
+
mainEntity: faq.map((item) => ({
|
|
182
|
+
'@type': 'Question',
|
|
183
|
+
name: item.question,
|
|
184
|
+
acceptedAnswer: { '@type': 'Answer', text: item.answer },
|
|
185
|
+
})),
|
|
186
|
+
} as WithContext<FAQPage>,
|
|
187
|
+
{
|
|
188
|
+
'@context': 'https://schema.org',
|
|
189
|
+
'@type': 'HowTo',
|
|
190
|
+
name: title,
|
|
191
|
+
description: description,
|
|
192
|
+
step: howTo.map((step, i) => ({
|
|
193
|
+
'@type': 'HowToStep',
|
|
194
|
+
position: i + 1,
|
|
195
|
+
name: step.name,
|
|
196
|
+
text: step.text,
|
|
197
|
+
})),
|
|
198
|
+
} as WithContext<HowTo>,
|
|
178
199
|
{
|
|
179
200
|
'@context': 'https://schema.org',
|
|
180
201
|
'@type': 'SoftwareApplication',
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { WithContext, SoftwareApplication } from 'schema-dts';
|
|
1
|
+
import type { WithContext, SoftwareApplication, FAQPage, HowTo } from 'schema-dts';
|
|
2
2
|
import type { PartyKegUI, PartyKegLocaleContent } from '../index';
|
|
3
3
|
|
|
4
4
|
const slug = 'party-stock-calculator';
|
|
@@ -140,6 +140,27 @@ const seo: PartyKegLocaleContent['seo'] = [
|
|
|
140
140
|
];
|
|
141
141
|
|
|
142
142
|
const schemas: PartyKegLocaleContent['schemas'] = [
|
|
143
|
+
{
|
|
144
|
+
'@context': 'https://schema.org',
|
|
145
|
+
'@type': 'FAQPage',
|
|
146
|
+
mainEntity: faq.map((item) => ({
|
|
147
|
+
'@type': 'Question',
|
|
148
|
+
name: item.question,
|
|
149
|
+
acceptedAnswer: { '@type': 'Answer', text: item.answer },
|
|
150
|
+
})),
|
|
151
|
+
} as WithContext<FAQPage>,
|
|
152
|
+
{
|
|
153
|
+
'@context': 'https://schema.org',
|
|
154
|
+
'@type': 'HowTo',
|
|
155
|
+
name: title,
|
|
156
|
+
description: description,
|
|
157
|
+
step: howTo.map((step, i) => ({
|
|
158
|
+
'@type': 'HowToStep',
|
|
159
|
+
position: i + 1,
|
|
160
|
+
name: step.name,
|
|
161
|
+
text: step.text,
|
|
162
|
+
})),
|
|
163
|
+
} as WithContext<HowTo>,
|
|
143
164
|
{
|
|
144
165
|
'@context': 'https://schema.org',
|
|
145
166
|
'@type': 'SoftwareApplication',
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { WithContext, SoftwareApplication } from 'schema-dts';
|
|
1
|
+
import type { WithContext, SoftwareApplication, FAQPage, HowTo } from 'schema-dts';
|
|
2
2
|
import type { PartyKegUI, PartyKegLocaleContent } from '../index';
|
|
3
3
|
|
|
4
4
|
const slug = 'calculadora-barriles-fiesta';
|
|
@@ -144,6 +144,27 @@ const seo: PartyKegLocaleContent['seo'] = [
|
|
|
144
144
|
];
|
|
145
145
|
|
|
146
146
|
const schemas: PartyKegLocaleContent['schemas'] = [
|
|
147
|
+
{
|
|
148
|
+
'@context': 'https://schema.org',
|
|
149
|
+
'@type': 'FAQPage',
|
|
150
|
+
mainEntity: faq.map((item) => ({
|
|
151
|
+
'@type': 'Question',
|
|
152
|
+
name: item.question,
|
|
153
|
+
acceptedAnswer: { '@type': 'Answer', text: item.answer },
|
|
154
|
+
})),
|
|
155
|
+
} as WithContext<FAQPage>,
|
|
156
|
+
{
|
|
157
|
+
'@context': 'https://schema.org',
|
|
158
|
+
'@type': 'HowTo',
|
|
159
|
+
name: title,
|
|
160
|
+
description: description,
|
|
161
|
+
step: howTo.map((step, i) => ({
|
|
162
|
+
'@type': 'HowToStep',
|
|
163
|
+
position: i + 1,
|
|
164
|
+
name: step.name,
|
|
165
|
+
text: step.text,
|
|
166
|
+
})),
|
|
167
|
+
} as WithContext<HowTo>,
|
|
147
168
|
{
|
|
148
169
|
'@context': 'https://schema.org',
|
|
149
170
|
'@type': 'SoftwareApplication',
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { WithContext, SoftwareApplication } from 'schema-dts';
|
|
1
|
+
import type { WithContext, SoftwareApplication, FAQPage, HowTo } from 'schema-dts';
|
|
2
2
|
import type { PartyKegUI, PartyKegLocaleContent } from '../index';
|
|
3
3
|
|
|
4
|
-
const slug = 'calculateur-fete-
|
|
4
|
+
const slug = 'calculateur-fete-fut';
|
|
5
5
|
const title = 'Calculateur de Bière pour Fêtes : Quantité par Personne, Mariage ou Anniversaire';
|
|
6
6
|
const description = 'Outil gratuit pour calculer la quantité de bière et de glace selon les invités, la durée et la température. Idéal pour les mariages, anniversaires et événements en plein air.';
|
|
7
7
|
|
|
@@ -140,6 +140,27 @@ const seo: PartyKegLocaleContent['seo'] = [
|
|
|
140
140
|
];
|
|
141
141
|
|
|
142
142
|
const schemas: PartyKegLocaleContent['schemas'] = [
|
|
143
|
+
{
|
|
144
|
+
'@context': 'https://schema.org',
|
|
145
|
+
'@type': 'FAQPage',
|
|
146
|
+
mainEntity: faq.map((item) => ({
|
|
147
|
+
'@type': 'Question',
|
|
148
|
+
name: item.question,
|
|
149
|
+
acceptedAnswer: { '@type': 'Answer', text: item.answer },
|
|
150
|
+
})),
|
|
151
|
+
} as WithContext<FAQPage>,
|
|
152
|
+
{
|
|
153
|
+
'@context': 'https://schema.org',
|
|
154
|
+
'@type': 'HowTo',
|
|
155
|
+
name: title,
|
|
156
|
+
description: description,
|
|
157
|
+
step: howTo.map((step, i) => ({
|
|
158
|
+
'@type': 'HowToStep',
|
|
159
|
+
position: i + 1,
|
|
160
|
+
name: step.name,
|
|
161
|
+
text: step.text,
|
|
162
|
+
})),
|
|
163
|
+
} as WithContext<HowTo>,
|
|
143
164
|
{
|
|
144
165
|
'@context': 'https://schema.org',
|
|
145
166
|
'@type': 'SoftwareApplication',
|