@jjlmoya/utils-cooking 1.13.0 → 1.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/index.ts +1 -1
- package/src/tests/category_validation.test.ts +73 -0
- package/src/tests/slug_language_code_format.test.ts +23 -0
- package/src/tests/slug_uniqueness.test.ts +81 -0
- package/src/tool/american-kitchen-converter/i18n/ja.ts +1 -1
- package/src/tool/american-kitchen-converter/i18n/ko.ts +1 -1
- package/src/tool/american-kitchen-converter/i18n/zh.ts +1 -1
- package/src/tool/banana-ripeness/i18n/fr.ts +1 -1
- package/src/tool/banana-ripeness/i18n/ja.ts +1 -1
- package/src/tool/banana-ripeness/i18n/ko.ts +1 -1
- package/src/tool/banana-ripeness/i18n/nl.ts +1 -1
- package/src/tool/banana-ripeness/i18n/zh.ts +1 -1
- package/src/tool/brine/i18n/ja.ts +1 -1
- package/src/tool/brine/i18n/ko.ts +1 -1
- package/src/tool/brine/i18n/zh.ts +1 -1
- package/src/tool/cookware-guide/i18n/fr.ts +1 -1
- package/src/tool/cookware-guide/i18n/ja.ts +1 -1
- package/src/tool/cookware-guide/i18n/ko.ts +1 -1
- package/src/tool/egg-timer/i18n/ja.ts +1 -1
- package/src/tool/egg-timer/i18n/ko.ts +1 -1
- package/src/tool/egg-timer/i18n/zh.ts +1 -1
- package/src/tool/ingredient-rescaler/i18n/es.ts +95 -95
- package/src/tool/ingredient-rescaler/i18n/ja.ts +1 -1
- package/src/tool/ingredient-rescaler/i18n/ko.ts +1 -1
- package/src/tool/ingredient-rescaler/i18n/pt.ts +1 -1
- package/src/tool/ingredient-rescaler/i18n/zh.ts +1 -1
- package/src/tool/meringue-peak/i18n/ja.ts +1 -1
- package/src/tool/meringue-peak/i18n/ko.ts +1 -1
- package/src/tool/meringue-peak/i18n/zh.ts +1 -1
- package/src/tool/mold-scaler/i18n/ja.ts +1 -1
- package/src/tool/mold-scaler/i18n/ko.ts +1 -1
- package/src/tool/mold-scaler/i18n/zh.ts +1 -1
- package/src/tool/pizza/i18n/de.ts +1 -1
- package/src/tool/pizza/i18n/es.ts +228 -228
- package/src/tool/pizza/i18n/fr.ts +1 -1
- package/src/tool/pizza/i18n/id.ts +1 -1
- package/src/tool/pizza/i18n/it.ts +1 -1
- package/src/tool/pizza/i18n/ja.ts +1 -1
- package/src/tool/pizza/i18n/ko.ts +1 -1
- package/src/tool/pizza/i18n/nl.ts +1 -1
- package/src/tool/pizza/i18n/pl.ts +1 -1
- package/src/tool/pizza/i18n/pt.ts +1 -1
- package/src/tool/pizza/i18n/ru.ts +1 -1
- package/src/tool/pizza/i18n/sv.ts +1 -1
- package/src/tool/pizza/i18n/tr.ts +1 -1
- package/src/tool/pizza/i18n/zh.ts +1 -1
- package/src/tool/roux-guide/i18n/es.ts +172 -172
- package/src/tool/roux-guide/i18n/ja.ts +1 -1
- package/src/tool/roux-guide/i18n/ko.ts +1 -1
- package/src/tool/roux-guide/i18n/pt.ts +1 -1
- package/src/tool/roux-guide/i18n/ru.ts +1 -1
- package/src/tool/roux-guide/i18n/sv.ts +1 -1
- package/src/tool/roux-guide/i18n/zh.ts +1 -1
- package/src/tool/sourdough-calculator/i18n/ja.ts +1 -1
- package/src/tool/sourdough-calculator/i18n/ko.ts +1 -1
- package/src/tool/sourdough-calculator/i18n/zh.ts +1 -1
- package/src/tools.ts +2 -0
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { cookingCategory as toolsCategory } from '../category/index';
|
|
3
|
+
import type { CategoryLocaleContent } from '../types';
|
|
4
|
+
|
|
5
|
+
const EXPECTED_LOCALES = [
|
|
6
|
+
'de', 'en', 'es', 'fr', 'id', 'it', 'ja', 'ko', 'nl', 'pl', 'pt', 'ru', 'sv', 'tr', 'zh'
|
|
7
|
+
];
|
|
8
|
+
|
|
9
|
+
const sharingLocales = ['ja', 'ko', 'zh'];
|
|
10
|
+
|
|
11
|
+
describe('Category Validation', () => {
|
|
12
|
+
it('should have all 15 required locales', () => {
|
|
13
|
+
const registeredLocales = Object.keys(toolsCategory.i18n);
|
|
14
|
+
EXPECTED_LOCALES.forEach((locale) => {
|
|
15
|
+
expect(
|
|
16
|
+
registeredLocales,
|
|
17
|
+
`Category is missing locale "${locale}"`,
|
|
18
|
+
).toContain(locale);
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
describe('Category Slug Validation', () => {
|
|
23
|
+
it('every locale should have a unique, translated slug and follow format rules', async () => {
|
|
24
|
+
const slugs = new Map<string, string>();
|
|
25
|
+
const locales = Object.keys(toolsCategory.i18n);
|
|
26
|
+
|
|
27
|
+
let enSlug = '';
|
|
28
|
+
if (locales.includes('en')) {
|
|
29
|
+
const enLoader = toolsCategory.i18n['en' as keyof typeof toolsCategory.i18n];
|
|
30
|
+
const enContent = (await enLoader?.()) as CategoryLocaleContent;
|
|
31
|
+
enSlug = enContent.slug;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
for (const locale of locales) {
|
|
35
|
+
const loader = toolsCategory.i18n[locale as keyof typeof toolsCategory.i18n];
|
|
36
|
+
const content = (await loader?.()) as CategoryLocaleContent;
|
|
37
|
+
|
|
38
|
+
expect(
|
|
39
|
+
content.slug,
|
|
40
|
+
`Category locale "${locale}" has an invalid slug ("${content.slug}"). Slugs must be transliterated (only a-z, 0-9, and -).`,
|
|
41
|
+
).toMatch(/^[a-z0-9-]+$/);
|
|
42
|
+
|
|
43
|
+
expect(
|
|
44
|
+
content.slug,
|
|
45
|
+
`Category locale "${locale}" slug ("${content.slug}") cannot end with a 2-letter language code (e.g., -ja, -ru, -ko).`,
|
|
46
|
+
).not.toMatch(/-[a-z]{2}$/);
|
|
47
|
+
|
|
48
|
+
if (locale !== 'en') {
|
|
49
|
+
if (sharingLocales.includes(locale)) {
|
|
50
|
+
expect(
|
|
51
|
+
content.slug,
|
|
52
|
+
`Category locale "${locale}" must use the same slug as "en" ("${enSlug}").`,
|
|
53
|
+
).toBe(enSlug);
|
|
54
|
+
} else {
|
|
55
|
+
expect(
|
|
56
|
+
content.slug,
|
|
57
|
+
`Category locale "${locale}" has the same slug as "en" ("${enSlug}"). Cada slug tiene que estar en su propio idioma`,
|
|
58
|
+
).not.toBe(enSlug);
|
|
59
|
+
|
|
60
|
+
if (slugs.has(content.slug)) {
|
|
61
|
+
const previousLocale = slugs.get(content.slug);
|
|
62
|
+
expect(
|
|
63
|
+
false,
|
|
64
|
+
`Category locales "${locale}" and "${previousLocale}" share the same slug ("${content.slug}"). Cada slug tiene que estar en su propia idioma`,
|
|
65
|
+
).toBe(true);
|
|
66
|
+
}
|
|
67
|
+
slugs.set(content.slug, locale);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
});
|
|
@@ -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
|
+
});
|
|
@@ -76,7 +76,7 @@ const appSchema = {
|
|
|
76
76
|
};
|
|
77
77
|
|
|
78
78
|
export const content: ToolLocaleContent = {
|
|
79
|
-
slug: '
|
|
79
|
+
slug: 'us-cooking-conversion-calculator-cups-to-grams-fahrenheit',
|
|
80
80
|
title: "アメリカン・キッチン・コンバーター:カップからグラム、温度への換算",
|
|
81
81
|
description: "アメリカのカップ、大さじ、ファーレンハイト(華氏)をメートル法に換算します。アメリカのレシピをプロの精度で読み解くためのツールです。",
|
|
82
82
|
faqTitle: "よくある質問",
|
|
@@ -76,7 +76,7 @@ const appSchema = {
|
|
|
76
76
|
};
|
|
77
77
|
|
|
78
78
|
export const content: ToolLocaleContent = {
|
|
79
|
-
slug: '
|
|
79
|
+
slug: 'us-cooking-conversion-calculator-cups-to-grams-fahrenheit',
|
|
80
80
|
title: "미국식 주방 단위 변환기: 컵을 그람 및 온도로 변환",
|
|
81
81
|
description: "컵(cups), 큰술(tablespoons), 화씨(Fahrenheit)를 미터법으로 변환합니다. 미국 레시피를 전문가의 정밀함으로 해석하기 위한 나침반입니다.",
|
|
82
82
|
faqTitle: "자주 묻는 질문",
|
|
@@ -76,7 +76,7 @@ const appSchema = {
|
|
|
76
76
|
};
|
|
77
77
|
|
|
78
78
|
export const content: ToolLocaleContent = {
|
|
79
|
-
slug: '
|
|
79
|
+
slug: 'us-cooking-conversion-calculator-cups-to-grams-fahrenheit',
|
|
80
80
|
title: "美式厨房单位转换器:杯转克与温度换算",
|
|
81
81
|
description: "将美式量杯(cups)、大勺(tablespoons)及华氏度(Fahrenheit)转换为公制单位。为您精准解读美式食谱的专业指南。",
|
|
82
82
|
faqTitle: "常问问题",
|
|
@@ -72,7 +72,7 @@ const appSchema = {
|
|
|
72
72
|
};
|
|
73
73
|
|
|
74
74
|
export const content: ToolLocaleContent = {
|
|
75
|
-
slug: '
|
|
75
|
+
slug: 'maturite-banane',
|
|
76
76
|
title: 'Diagnostic et Conservation des Bananes : Guide Scientifique',
|
|
77
77
|
description: 'Analysez le stade de maturité de vos bananes avec une précision scientifique. Apprenez les techniques de conservation, la biochimie de l\'éthylène et l\'optimisation nutritionnelle.',
|
|
78
78
|
faqTitle: 'Questions Fréquemment Posées',
|
|
@@ -72,7 +72,7 @@ const appSchema = {
|
|
|
72
72
|
};
|
|
73
73
|
|
|
74
74
|
export const content: ToolLocaleContent = {
|
|
75
|
-
slug: 'banana-
|
|
75
|
+
slug: 'banana-ripeness',
|
|
76
76
|
title: "バナナの診断と保存:科学的ガイド",
|
|
77
77
|
description: "バナナの熟成状態を科学的な精度で分析します。保存方法、エチレンの生化学、そして栄養の最適化について学びましょう。",
|
|
78
78
|
faqTitle: "よくある質問",
|
|
@@ -72,7 +72,7 @@ const appSchema = {
|
|
|
72
72
|
};
|
|
73
73
|
|
|
74
74
|
export const content: ToolLocaleContent = {
|
|
75
|
-
slug: 'banana-
|
|
75
|
+
slug: 'banana-ripeness',
|
|
76
76
|
title: "바나나 숙도 진단 및 보관: 과학적 가이드",
|
|
77
77
|
description: "바나나의 숙성 상태를 과학적인 정밀함으로 분석합니다. 보관 기술, 에틸렌의 생화학, 그리고 영양 최적화 방법을 배워보세요.",
|
|
78
78
|
faqTitle: "자주 묻는 질문",
|
|
@@ -72,7 +72,7 @@ const appSchema = {
|
|
|
72
72
|
};
|
|
73
73
|
|
|
74
74
|
export const content: ToolLocaleContent = {
|
|
75
|
-
slug: '
|
|
75
|
+
slug: 'bananenrijpheid-conservering',
|
|
76
76
|
title: 'Bananen Diagnose en Conservering: Wetenschappelijke Gids',
|
|
77
77
|
description: 'Analyseer de rijpheidsstatus van je bananen met wetenschappelijke precisie. Leer conserveringstechnieken, de biochemie van ethyleen en voedingsoptimalisatie.',
|
|
78
78
|
faqTitle: 'Veelgestelde Vragen',
|
|
@@ -76,7 +76,7 @@ const appSchema = {
|
|
|
76
76
|
};
|
|
77
77
|
|
|
78
78
|
export const content: ToolLocaleContent = {
|
|
79
|
-
slug:
|
|
79
|
+
slug: 'equilibrium-brining-calculator-meat-fermentation-ratios',
|
|
80
80
|
title: "平衡塩水計算機",
|
|
81
81
|
description: "ジューシーな肉と完璧な発酵食品のための、バランスの科学。食材と水の総重量に基づいた正確な塩分濃度を計算します。",
|
|
82
82
|
ui: {
|
|
@@ -76,7 +76,7 @@ const appSchema = {
|
|
|
76
76
|
};
|
|
77
77
|
|
|
78
78
|
export const content: ToolLocaleContent = {
|
|
79
|
-
slug:
|
|
79
|
+
slug: 'equilibrium-brining-calculator-meat-fermentation-ratios',
|
|
80
80
|
title: "평형 염수 계산기",
|
|
81
81
|
description: "촉촉한 고기와 완벽한 발효를 위한 평형의 과학. 식재료와 물의 총 중량에 기초한 정확한 염도를 계산하세요.",
|
|
82
82
|
ui: {
|
|
@@ -72,7 +72,7 @@ const appSchema = {
|
|
|
72
72
|
};
|
|
73
73
|
|
|
74
74
|
export const content: ToolLocaleContent = {
|
|
75
|
-
slug: '
|
|
75
|
+
slug: 'selecteur-ustensiles',
|
|
76
76
|
title: 'Sélecteur d\'Ustensiles Connecté Guide de Cuisine',
|
|
77
77
|
description: 'Guide interactif pour choisir la meilleure poêle ou casserole selon votre style de cuisine. Fonte vs Acier Inoxydable vs Téflon. Trouvez votre outil parfait.',
|
|
78
78
|
faqTitle: 'Questions Fréquemment Posées',
|
|
@@ -72,7 +72,7 @@ const appSchema = {
|
|
|
72
72
|
};
|
|
73
73
|
|
|
74
74
|
export const content: ToolLocaleContent = {
|
|
75
|
-
slug: 'cookware-
|
|
75
|
+
slug: 'cookware-selector',
|
|
76
76
|
title: 'スマート・フライパン・セレクター:調理器具ガイド',
|
|
77
77
|
description: 'あなたの料理スタイルに合わせて最適なフライパンや鍋を選ぶためのインタラクティブガイド。鋳鉄 vs ステンレス vs テフロン。理想の道具を見つけましょう。',
|
|
78
78
|
faqTitle: 'よくある質問',
|
|
@@ -72,7 +72,7 @@ const appSchema = {
|
|
|
72
72
|
};
|
|
73
73
|
|
|
74
74
|
export const content: ToolLocaleContent = {
|
|
75
|
-
slug: 'cookware-
|
|
75
|
+
slug: 'cookware-selector',
|
|
76
76
|
title: '스마트 프라이팬 선택기: 조리 도구 가이드',
|
|
77
77
|
description: '요리 스타일에 맞춰 가장 적합한 팬이나 냄비를 선택하기 위한 인터랙티브 가이드. 주물 vs 스테인리스 vs 테플론. 나에게 완벽한 도구를 찾아보세요.',
|
|
78
78
|
faqTitle: '자주 묻는 질문',
|
|
@@ -72,7 +72,7 @@ const appSchema = {
|
|
|
72
72
|
};
|
|
73
73
|
|
|
74
74
|
export const content: ToolLocaleContent = {
|
|
75
|
-
slug:
|
|
75
|
+
slug: 'perfect-boiled-egg-timer-altitude-calculator',
|
|
76
76
|
title: "과학적인 계란 타이머",
|
|
77
77
|
description: "고도, 온도, 크기에 따른 완벽한 계란 삶기를 위한 열력학 계산기입니다.",
|
|
78
78
|
faqTitle: "자주 묻는 질문",
|
|
@@ -4,115 +4,115 @@ const title = 'Escalador de Ingredientes Ajuste de Recetas';
|
|
|
4
4
|
const description = 'Escala recetas automáticamente según el número de raciones. Calcula las cantidades exactas de ingredientes multiplicando o reduciendo tu receta sin complicaciones.';
|
|
5
5
|
|
|
6
6
|
const faq = [
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
7
|
+
{
|
|
8
|
+
question: '¿Por qué mi factor de conversión incluye decimales?',
|
|
9
|
+
answer: 'Porque las proporciones culinarias no siempre son redondas. Si escalas una receta para 4 personas a 7, el factor es 1.75 exactamente. Los ingredientes se multiplican por ese número, aunque encuentres fracciones. Es más exacto que redondear arbitrariamente.',
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
question: '¿Qué pasa si ingreso "1/2 cucharadita de sal"?',
|
|
13
|
+
answer: 'El parser busca el número primero. Reconocerá "1" como cantidad, luego "/2" como parte de la unidad. El resultado será "0.5", y al escalar se multiplicará correctamente. Para fracciones como "1/2", ingresa "0.5" directamente (más claro) o la herramienta las interpreta como división.',
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
question: '¿Debo redondear los resultados finales?',
|
|
17
|
+
answer: 'Depende del ingrediente. Para harinas, sí. Para levaduras o especias, la precisión importa menos (escala al 75% de lo indicado). Para huevos: si obtienes 2.3, usa 2 completos + parte de un tercero (pesada), o redondea a 2 si el plato lo permite.',
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
question: '¿Por qué no cambia el resultado cuando cambio las raciones?',
|
|
21
|
+
answer: 'Asegúrate de que el campo de ingredientes tenga contenido. Si está vacío, no hay nada que escalar. También verifica que los números en tus ingredientes sean reconocibles (ej: "500g", "1/2 cucharadita").',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
question: '¿Funciona con medidas imperiales (onzas, tazas)?',
|
|
25
|
+
answer: 'Técnicamente sí, la herramienta lee números y escala. Pero la precisión es limitada con tazas (volumen inconsistente). Se recomienda convertir a gramos antes de escalar.',
|
|
26
|
+
},
|
|
27
27
|
];
|
|
28
28
|
|
|
29
29
|
const howTo = [
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
30
|
+
{
|
|
31
|
+
name: 'Ingresa tus raciones',
|
|
32
|
+
text: 'En el campo "Original", coloca el número de personas para el que la receta está diseñada. En "Deseado", coloca el número de personas que vas a cocinar.',
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: 'Pega tu lista de ingredientes',
|
|
36
|
+
text: 'Copia y pega tu lista tal cual. Cada ingrediente en una línea. La herramienta reconoce números al inicio (500g, 1/2, 2.5) y escala automáticamente.',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: 'Ajusta según contexto',
|
|
40
|
+
text: 'Los resultados son matemáticamente exactos, pero la cocina es arte. Especias: escala al 75%. Levaduras: menos de lo teórico en grandes cantidades. Tiempos: no se escalan nunca.',
|
|
41
|
+
},
|
|
42
42
|
];
|
|
43
43
|
|
|
44
44
|
const faqSchema = {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
45
|
+
'@context': 'https://schema.org',
|
|
46
|
+
'@type': 'FAQPage',
|
|
47
|
+
mainEntity: faq.map((item) => ({
|
|
48
|
+
'@type': 'Question',
|
|
49
|
+
name: item.question,
|
|
50
|
+
acceptedAnswer: { '@type': 'Answer', text: item.answer },
|
|
51
|
+
})),
|
|
52
52
|
};
|
|
53
53
|
|
|
54
54
|
const howToSchema = {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
55
|
+
'@context': 'https://schema.org',
|
|
56
|
+
'@type': 'HowTo',
|
|
57
|
+
name: title,
|
|
58
|
+
description,
|
|
59
|
+
step: howTo.map((step) => ({
|
|
60
|
+
'@type': 'HowToStep',
|
|
61
|
+
name: step.name,
|
|
62
|
+
text: step.text,
|
|
63
|
+
})),
|
|
64
64
|
};
|
|
65
65
|
|
|
66
66
|
const appSchema = {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
67
|
+
'@context': 'https://schema.org',
|
|
68
|
+
'@type': 'SoftwareApplication',
|
|
69
|
+
name: title,
|
|
70
|
+
description,
|
|
71
|
+
applicationCategory: 'UtilitiesApplication',
|
|
72
|
+
operatingSystem: 'All',
|
|
73
73
|
};
|
|
74
74
|
|
|
75
75
|
export const content: ToolLocaleContent = {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
76
|
+
slug: 'reescalador-ingredientes',
|
|
77
|
+
title,
|
|
78
|
+
description,
|
|
79
|
+
ui: {
|
|
80
|
+
servings: 'Raciones',
|
|
81
|
+
original: 'Original',
|
|
82
|
+
desired: 'Deseado',
|
|
83
|
+
multiplyingFactor: 'Factor Multiplicador',
|
|
84
|
+
ingredientsList: 'Lista de Ingredientes',
|
|
85
|
+
pasteHere: 'Pega aquí tu lista.',
|
|
86
|
+
exampleLine1: 'Ej:',
|
|
87
|
+
exampleLine2: '500g Harina',
|
|
88
|
+
exampleLine3: '300ml Agua',
|
|
89
|
+
exampleLine4: '10g Sal',
|
|
90
|
+
result: 'Resultado',
|
|
91
|
+
copy: 'Copiar',
|
|
92
|
+
copied: 'Copiado!',
|
|
93
|
+
emptyState: 'Tus ingredientes ajustados aparecerán aquí...',
|
|
94
|
+
defaultIngredient1: '200g Harina',
|
|
95
|
+
defaultIngredient2: '100ml Leche',
|
|
96
|
+
defaultIngredient3: '2 Huevos',
|
|
97
|
+
},
|
|
98
|
+
faqTitle: 'Preguntas Frecuentes',
|
|
99
|
+
faq,
|
|
100
|
+
bibliographyTitle: 'Bibliografía',
|
|
101
|
+
bibliography: [
|
|
102
|
+
{
|
|
103
|
+
name: 'Harold McGee - On Food and Cooking: The Science and Lore of the Kitchen',
|
|
104
|
+
url: 'https://en.wikipedia.org/wiki/Harold_McGee',
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
name: 'The Flavor Bible by Karen Page and Andrew Dornenburg',
|
|
108
|
+
url: 'https://www.flavorprints.com/',
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
name: 'Modernist Cuisine - Técnicas de escalado científico',
|
|
112
|
+
url: 'https://www.modernistcuisine.com/',
|
|
113
|
+
},
|
|
114
|
+
],
|
|
115
|
+
howTo,
|
|
116
116
|
seo: [
|
|
117
117
|
{
|
|
118
118
|
type: 'title',
|
|
@@ -241,5 +241,5 @@ export const content: ToolLocaleContent = {
|
|
|
241
241
|
},
|
|
242
242
|
],
|
|
243
243
|
|
|
244
|
-
|
|
244
|
+
schemas: [appSchema, faqSchema, howToSchema],
|
|
245
245
|
};
|