@jjlmoya/utils-cooking 1.26.0 → 1.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/category/i18n/fr.ts +16 -16
- package/src/category/i18n/ru.ts +7 -7
- package/src/category/i18n/zh.ts +1 -1
- package/src/category/index.ts +2 -0
- package/src/entries.ts +3 -1
- package/src/index.ts +1 -0
- package/src/tests/i18n-titles.test.ts +3 -2
- package/src/tests/lacto-fermentation-salt-calculator.test.ts +64 -0
- package/src/tests/locale_completeness.test.ts +2 -2
- package/src/tests/no_en_dash.test.ts +70 -0
- package/src/tests/tool_validation.test.ts +2 -2
- package/src/tool/american-kitchen-converter/i18n/en.ts +2 -2
- package/src/tool/american-kitchen-converter/i18n/fr.ts +2 -2
- package/src/tool/american-kitchen-converter/i18n/pl.ts +2 -2
- package/src/tool/american-kitchen-converter/i18n/ru.ts +13 -13
- package/src/tool/american-kitchen-converter/i18n/sv.ts +2 -2
- package/src/tool/american-kitchen-converter/i18n/zh.ts +3 -3
- package/src/tool/banana-ripeness/banana-ripeness.css +56 -0
- package/src/tool/banana-ripeness/i18n/fr.ts +5 -5
- package/src/tool/banana-ripeness/i18n/ru.ts +5 -5
- package/src/tool/brine/i18n/fr.ts +4 -4
- package/src/tool/brine/i18n/ru.ts +3 -3
- package/src/tool/brine/i18n/sv.ts +9 -9
- package/src/tool/cookware-guide/i18n/fr.ts +9 -9
- package/src/tool/cookware-guide/i18n/ru.ts +6 -6
- package/src/tool/cookware-guide/i18n/zh.ts +3 -3
- package/src/tool/egg-timer/i18n/ru.ts +4 -4
- package/src/tool/ingredient-rescaler/i18n/en.ts +1 -1
- package/src/tool/ingredient-rescaler/i18n/fr.ts +3 -3
- package/src/tool/ingredient-rescaler/i18n/pl.ts +1 -1
- package/src/tool/ingredient-rescaler/i18n/ru.ts +5 -5
- package/src/tool/ingredient-rescaler/i18n/zh.ts +5 -5
- package/src/tool/kitchen-timer/i18n/de.ts +1 -1
- package/src/tool/kitchen-timer/i18n/fr.ts +8 -8
- package/src/tool/kitchen-timer/i18n/id.ts +1 -1
- package/src/tool/kitchen-timer/i18n/nl.ts +1 -1
- package/src/tool/kitchen-timer/i18n/pl.ts +1 -1
- package/src/tool/kitchen-timer/i18n/ru.ts +4 -4
- package/src/tool/kitchen-timer/i18n/sv.ts +1 -1
- package/src/tool/kitchen-timer/i18n/zh.ts +5 -5
- package/src/tool/lacto-fermentation-salt-calculator/bibliography.astro +6 -0
- package/src/tool/lacto-fermentation-salt-calculator/bibliography.ts +10 -0
- package/src/tool/lacto-fermentation-salt-calculator/component.astro +163 -0
- package/src/tool/lacto-fermentation-salt-calculator/components/DigitalScale.astro +43 -0
- package/src/tool/lacto-fermentation-salt-calculator/components/PetriDish.astro +55 -0
- package/src/tool/lacto-fermentation-salt-calculator/components/WizardMode.astro +83 -0
- package/src/tool/lacto-fermentation-salt-calculator/entry.ts +26 -0
- package/src/tool/lacto-fermentation-salt-calculator/i18n/de.ts +195 -0
- package/src/tool/lacto-fermentation-salt-calculator/i18n/en.ts +195 -0
- package/src/tool/lacto-fermentation-salt-calculator/i18n/es.ts +195 -0
- package/src/tool/lacto-fermentation-salt-calculator/i18n/fr.ts +195 -0
- package/src/tool/lacto-fermentation-salt-calculator/i18n/id.ts +195 -0
- package/src/tool/lacto-fermentation-salt-calculator/i18n/it.ts +195 -0
- package/src/tool/lacto-fermentation-salt-calculator/i18n/ja.ts +195 -0
- package/src/tool/lacto-fermentation-salt-calculator/i18n/ko.ts +195 -0
- package/src/tool/lacto-fermentation-salt-calculator/i18n/nl.ts +195 -0
- package/src/tool/lacto-fermentation-salt-calculator/i18n/pl.ts +195 -0
- package/src/tool/lacto-fermentation-salt-calculator/i18n/pt.ts +195 -0
- package/src/tool/lacto-fermentation-salt-calculator/i18n/ru.ts +195 -0
- package/src/tool/lacto-fermentation-salt-calculator/i18n/sv.ts +195 -0
- package/src/tool/lacto-fermentation-salt-calculator/i18n/tr.ts +195 -0
- package/src/tool/lacto-fermentation-salt-calculator/i18n/zh.ts +195 -0
- package/src/tool/lacto-fermentation-salt-calculator/index.ts +11 -0
- package/src/tool/lacto-fermentation-salt-calculator/lacto-fermentation-salt-calculator.css +692 -0
- package/src/tool/lacto-fermentation-salt-calculator/logic.ts +37 -0
- package/src/tool/lacto-fermentation-salt-calculator/seo.astro +15 -0
- package/src/tool/meringue-peak/i18n/en.ts +3 -3
- package/src/tool/meringue-peak/i18n/ru.ts +3 -3
- package/src/tool/meringue-peak/i18n/zh.ts +2 -2
- package/src/tool/pizza/i18n/de.ts +1 -1
- package/src/tool/pizza/i18n/fr.ts +4 -4
- package/src/tool/pizza/i18n/ru.ts +6 -6
- package/src/tool/roux-guide/i18n/fr.ts +3 -3
- package/src/tool/roux-guide/i18n/ru.ts +4 -4
- package/src/tool/roux-guide/i18n/zh.ts +1 -1
- package/src/tool/sourdough-calculator/i18n/ru.ts +2 -2
- package/src/tool/sourdough-calculator/i18n/zh.ts +2 -2
- package/src/tool/yeast-converter/i18n/de.ts +1 -1
- package/src/tool/yeast-converter/i18n/en.ts +1 -1
- package/src/tool/yeast-converter/i18n/fr.ts +19 -19
- package/src/tool/yeast-converter/i18n/id.ts +1 -1
- package/src/tool/yeast-converter/i18n/nl.ts +1 -1
- package/src/tool/yeast-converter/i18n/pl.ts +1 -1
- package/src/tool/yeast-converter/i18n/pt.ts +1 -1
- package/src/tool/yeast-converter/i18n/ru.ts +4 -4
- package/src/tool/yeast-converter/i18n/sv.ts +1 -1
- package/src/tools.ts +2 -2
|
@@ -7,12 +7,12 @@ const faq = [
|
|
|
7
7
|
{
|
|
8
8
|
question: 'Combien de minuteurs puis-je créer ?',
|
|
9
9
|
answer:
|
|
10
|
-
'Illimité. Ajoutez-en autant que nécessaire avec le bouton "+". Parfait pour cuisiner plusieurs plats simultanément
|
|
10
|
+
'Illimité. Ajoutez-en autant que nécessaire avec le bouton "+". Parfait pour cuisiner plusieurs plats simultanément: bouillir des pâtes, réduire une sauce, reposer la viande et cuire au four, tout en même temps.',
|
|
11
11
|
},
|
|
12
12
|
{
|
|
13
13
|
question: 'Pourquoi le temps de repos est-il important pour la viande ?',
|
|
14
14
|
answer:
|
|
15
|
-
'Lorsque vous coupez la viande juste après la cuisson, les jus s\'échappent sur l\'assiette. Si vous la laissez reposer 5 à 10 minutes, les fibres se détendent et les jus se redistribuent. Résultat
|
|
15
|
+
'Lorsque vous coupez la viande juste après la cuisson, les jus s\'échappent sur l\'assiette. Si vous la laissez reposer 5 à 10 minutes, les fibres se détendent et les jus se redistribuent. Résultat: une viande juteuse au lieu d\'être sèche. Le repos est une cuisson passive.',
|
|
16
16
|
},
|
|
17
17
|
{
|
|
18
18
|
question: 'Fonctionne-t-il avec l\'écran verrouillé ?',
|
|
@@ -32,7 +32,7 @@ const howTo = [
|
|
|
32
32
|
},
|
|
33
33
|
{
|
|
34
34
|
name: 'Personnaliser chaque minuteur',
|
|
35
|
-
text: 'Changez le nom de chaque minuteur pour identifier ce qui cuit
|
|
35
|
+
text: 'Changez le nom de chaque minuteur pour identifier ce qui cuit: "Four", "Riz", "Sauce", etc.',
|
|
36
36
|
},
|
|
37
37
|
{
|
|
38
38
|
name: 'Contrôler depuis le dock mobile',
|
|
@@ -110,12 +110,12 @@ export const content: ToolLocaleContent = {
|
|
|
110
110
|
{
|
|
111
111
|
question: 'Combien de minuteurs puis-je créer ?',
|
|
112
112
|
answer:
|
|
113
|
-
'Illimité. Ajoutez-en autant que nécessaire avec le bouton "+". Parfait pour cuisiner plusieurs plats simultanément
|
|
113
|
+
'Illimité. Ajoutez-en autant que nécessaire avec le bouton "+". Parfait pour cuisiner plusieurs plats simultanément: bouillir des pâtes, réduire une sauce, reposer la viande et cuire au four, tout en même temps.',
|
|
114
114
|
},
|
|
115
115
|
{
|
|
116
116
|
question: 'Pourquoi le temps de repos est-il important pour la viande ?',
|
|
117
117
|
answer:
|
|
118
|
-
'Lorsque vous coupez la viande juste après la cuisson, les jus s\'échappent sur l\'assiette. Si vous la laissez reposer 5 à 10 minutes, les fibres se détendent et les jus se redistribuent. Résultat
|
|
118
|
+
'Lorsque vous coupez la viande juste après la cuisson, les jus s\'échappent sur l\'assiette. Si vous la laissez reposer 5 à 10 minutes, les fibres se détendent et les jus se redistribuent. Résultat: une viande juteuse au lieu d\'être sèche. Le repos est une cuisson passive.',
|
|
119
119
|
},
|
|
120
120
|
{
|
|
121
121
|
question: 'Fonctionne-t-il avec l\'écran verrouillé ?',
|
|
@@ -134,7 +134,7 @@ export const content: ToolLocaleContent = {
|
|
|
134
134
|
},
|
|
135
135
|
{
|
|
136
136
|
name: 'Personnaliser chaque minuteur',
|
|
137
|
-
text: 'Changez le nom de chaque minuteur pour identifier ce qui cuit
|
|
137
|
+
text: 'Changez le nom de chaque minuteur pour identifier ce qui cuit: "Four", "Riz", "Sauce", etc.',
|
|
138
138
|
},
|
|
139
139
|
{
|
|
140
140
|
name: 'Contrôler depuis le dock mobile',
|
|
@@ -157,7 +157,7 @@ export const content: ToolLocaleContent = {
|
|
|
157
157
|
},
|
|
158
158
|
{
|
|
159
159
|
type: 'paragraph',
|
|
160
|
-
html: 'Cet outil de <strong>Minuteur Multiple</strong> a été conçu pour les chefs à domicile et les professionnels qui ont besoin d\'orchestrer une symphonie de plats
|
|
160
|
+
html: 'Cet outil de <strong>Minuteur Multiple</strong> a été conçu pour les chefs à domicile et les professionnels qui ont besoin d\'orchestrer une symphonie de plats: bouillir des pâtes, rôti au four et réduction de sauce, tout cela se passant simultanément.',
|
|
161
161
|
},
|
|
162
162
|
{
|
|
163
163
|
type: 'title',
|
|
@@ -182,7 +182,7 @@ export const content: ToolLocaleContent = {
|
|
|
182
182
|
},
|
|
183
183
|
{
|
|
184
184
|
type: 'title',
|
|
185
|
-
text: 'Sécurité Alimentaire
|
|
185
|
+
text: 'Sécurité Alimentaire: La Zone de Danger',
|
|
186
186
|
level: 3,
|
|
187
187
|
},
|
|
188
188
|
{
|
|
@@ -6,7 +6,7 @@ const description = "Kelola beberapa waktu memasak secara bersamaan. Alarm indep
|
|
|
6
6
|
const faq = [
|
|
7
7
|
{
|
|
8
8
|
question: 'Berapa banyak timer yang bisa saya buat?',
|
|
9
|
-
answer: 'Tak terbatas. Tambahkan sebanyak yang Anda butuhkan dengan tombol "+". Ideal untuk memasak beberapa hidangan sekaligus: merebus pasta, mereduksi saus, mendiamkan daging, dan oven yang menyala
|
|
9
|
+
answer: 'Tak terbatas. Tambahkan sebanyak yang Anda butuhkan dengan tombol "+". Ideal untuk memasak beberapa hidangan sekaligus: merebus pasta, mereduksi saus, mendiamkan daging, dan oven yang menyala - semuanya sekaligus.',
|
|
10
10
|
},
|
|
11
11
|
{
|
|
12
12
|
question: 'Mengapa waktu istirahat penting untuk daging?',
|
|
@@ -6,7 +6,7 @@ const description = "Beheer meerdere kooktijden gelijktijdig. Onafhankelijke ala
|
|
|
6
6
|
const faq = [
|
|
7
7
|
{
|
|
8
8
|
question: 'Hoeveel timers kan ik maken?',
|
|
9
|
-
answer: 'Onbeperkt. Voeg er zoveel toe als je nodig hebt met de "+"-knop. Ideaal voor het gelijktijdig koken van meerdere gerechten: pasta koken, saus inkoken, vlees laten rusten en de oven aan
|
|
9
|
+
answer: 'Onbeperkt. Voeg er zoveel toe als je nodig hebt met de "+"-knop. Ideaal voor het gelijktijdig koken van meerdere gerechten: pasta koken, saus inkoken, vlees laten rusten en de oven aan - alles tegelijk.',
|
|
10
10
|
},
|
|
11
11
|
{
|
|
12
12
|
question: 'Waarom is rusttijd belangrijk voor vlees?',
|
|
@@ -6,7 +6,7 @@ const description = "Zarządzaj wieloma czasami gotowania jednocześnie. Niezale
|
|
|
6
6
|
const faq = [
|
|
7
7
|
{
|
|
8
8
|
question: 'Ile minutników mogę stworzyć?',
|
|
9
|
-
answer: 'Nieograniczoną liczbę. Dodaj tyle, ile potrzebujesz przyciskiem "+". Idealne do gotowania kilku potraw naraz: gotujący się makaron, redukujący się sos, odpoczywające mięso i pracujący piekarnik
|
|
9
|
+
answer: 'Nieograniczoną liczbę. Dodaj tyle, ile potrzebujesz przyciskiem "+". Idealne do gotowania kilku potraw naraz: gotujący się makaron, redukujący się sos, odpoczywające mięso i pracujący piekarnik - wszystko jednocześnie.',
|
|
10
10
|
},
|
|
11
11
|
{
|
|
12
12
|
question: 'Dlaczego odpoczynek mięsa jest ważny?',
|
|
@@ -6,11 +6,11 @@ const description = "Управляйте несколькими процесс
|
|
|
6
6
|
const faq = [
|
|
7
7
|
{
|
|
8
8
|
question: 'Сколько таймеров я могу создать?',
|
|
9
|
-
answer: 'Неограниченно. Добавляйте столько, сколько нужно, кнопкой "+". Идеально для одновременного приготовления нескольких блюд: варка пасты, выпаривание соуса, отдых мяса и работа духовки
|
|
9
|
+
answer: 'Неограниченно. Добавляйте столько, сколько нужно, кнопкой "+". Идеально для одновременного приготовления нескольких блюд: варка пасты, выпаривание соуса, отдых мяса и работа духовки - всё сразу.',
|
|
10
10
|
},
|
|
11
11
|
{
|
|
12
12
|
question: 'Почему важен отдых мяса?',
|
|
13
|
-
answer: 'Если разрезать мясо сразу после огня, соки вытекут на тарелку. При отдыхе в течение 5
|
|
13
|
+
answer: 'Если разрезать мясо сразу после огня, соки вытекут на тарелку. При отдыхе в течение 5-10 минут волокна расслабляются, и соки распределяются равномерно. Результат: сочное мясо вместо сухого.',
|
|
14
14
|
},
|
|
15
15
|
{
|
|
16
16
|
question: 'Работает ли таймер при заблокированном экране?',
|
|
@@ -110,7 +110,7 @@ export const content: ToolLocaleContent = {
|
|
|
110
110
|
},
|
|
111
111
|
{
|
|
112
112
|
type: 'paragraph',
|
|
113
|
-
html: 'Профессиональная кухня
|
|
113
|
+
html: 'Профессиональная кухня - это не только рецепты, но и <strong>точный тайминг</strong>. Концепция <em>"Mise en Place"</em> (всё на своих местах) включает время как один из важнейших ингредиентов.',
|
|
114
114
|
},
|
|
115
115
|
{
|
|
116
116
|
type: 'title',
|
|
@@ -135,7 +135,7 @@ export const content: ToolLocaleContent = {
|
|
|
135
135
|
},
|
|
136
136
|
{
|
|
137
137
|
type: 'paragraph',
|
|
138
|
-
html: 'Время влияет и на безопасность. "Опасная зона" размножения бактерий
|
|
138
|
+
html: 'Время влияет и на безопасность. "Опасная зона" размножения бактерий - от 5°C до 65°C. Блюда не должны находиться в этом диапазоне более <strong>2 часов</strong>.',
|
|
139
139
|
},
|
|
140
140
|
{
|
|
141
141
|
type: 'title',
|
|
@@ -6,7 +6,7 @@ const description = "Hantera flera tillagningstider samtidigt. Oberoende larm, i
|
|
|
6
6
|
const faq = [
|
|
7
7
|
{
|
|
8
8
|
question: 'Hur många timers kan jag skapa?',
|
|
9
|
-
answer: 'Obegränsat antal. Lägg till så många du behöver med "+"-knappen. Perfekt för att laga flera rätter samtidigt: pasta som kokar, sås som reduceras, kött som vilar och ugnen igång
|
|
9
|
+
answer: 'Obegränsat antal. Lägg till så många du behöver med "+"-knappen. Perfekt för att laga flera rätter samtidigt: pasta som kokar, sås som reduceras, kött som vilar och ugnen igång - allt på en gång.',
|
|
10
10
|
},
|
|
11
11
|
{
|
|
12
12
|
question: 'Varför är vilotiden viktig för kött?',
|
|
@@ -6,7 +6,7 @@ const description = "同时管理多个烹饪时间。独立闹钟设计,专
|
|
|
6
6
|
const faq = [
|
|
7
7
|
{
|
|
8
8
|
question: '我可以创建多少个计时器?',
|
|
9
|
-
answer: '
|
|
9
|
+
answer: '无限制。通过"+"按钮,你可以为每道菜添加一个计时器。非常适合同时管控多项任务:在煮面条的同时,收浓酱汁、让肉类静置、并监控烤箱。',
|
|
10
10
|
},
|
|
11
11
|
{
|
|
12
12
|
question: '为什么肉类静置过程很重要?',
|
|
@@ -17,18 +17,18 @@ const faq = [
|
|
|
17
17
|
answer: '是的,但你需要开启浏览器的通知权限。计时器会在后台持续运行,即使你切换标签页或锁屏手机,它也会通过声音和系统通知提醒你。',
|
|
18
18
|
},
|
|
19
19
|
{
|
|
20
|
-
question: '
|
|
20
|
+
question: '什么是食品"危险温度带"?',
|
|
21
21
|
answer: '细菌在 5°C 到 65°C 之间繁殖最快。烹饪后的食物在该温度区间内不应停留超过 2 小时(如果环境温度超过 30°C,则限制为 1 小时)。使用计时器来控制降温冷却的时间。',
|
|
22
22
|
},
|
|
23
23
|
];
|
|
24
24
|
const howTo = [
|
|
25
25
|
{
|
|
26
26
|
name: '创建多个计时器',
|
|
27
|
-
text: '
|
|
27
|
+
text: '点击"+"按钮,根据你的需要添加多个计时器。适合协调多道菜肴同步进行。',
|
|
28
28
|
},
|
|
29
29
|
{
|
|
30
30
|
name: '个性化计时器名称',
|
|
31
|
-
text: '
|
|
31
|
+
text: '修改每个计时器的标签以便识别正在烹饪的内容:"烤箱"、"米饭"、"酱汁"等。',
|
|
32
32
|
},
|
|
33
33
|
{
|
|
34
34
|
name: '移动端便捷控制',
|
|
@@ -147,7 +147,7 @@ export const content: ToolLocaleContent = {
|
|
|
147
147
|
items: [
|
|
148
148
|
'<strong>错开完成时间:</strong> 如果所有菜同时完成,装盘时你会手忙脚乱。尽量规划让配菜早于主菜 5 分钟准备好。',
|
|
149
149
|
'<strong>利用余热:</strong> 在计时器响铃前 1-2 分钟关闭火源。余热会温柔地完成最后的烹制。',
|
|
150
|
-
'<strong>重命名你的计时器:</strong>
|
|
150
|
+
'<strong>重命名你的计时器:</strong> 忙碌时容易忘记闹钟是哪道菜。使用本工具的重命名功能,标注"烤箱"、"米饭"或"酱汁"。',
|
|
151
151
|
],
|
|
152
152
|
},
|
|
153
153
|
{
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export const bibliography = [
|
|
2
|
+
{
|
|
3
|
+
name: 'Microbiology and Technology of Fermented Foods',
|
|
4
|
+
url: 'https://onlinelibrary.wiley.com/doi/book/10.1002/9780470277515',
|
|
5
|
+
},
|
|
6
|
+
{
|
|
7
|
+
name: 'Lactic Acid Bacteria: Microbiological and Functional Aspects',
|
|
8
|
+
url: 'https://www.taylorfrancis.com/books/edit/10.1201/9780429057465/lactic-acid-bacteria-gabriel-vinderola-arthur-ouwehand-seppo-salminen-atte-von-wright',
|
|
9
|
+
},
|
|
10
|
+
];
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
---
|
|
2
|
+
import WizardMode from './components/WizardMode.astro';
|
|
3
|
+
import DigitalScale from './components/DigitalScale.astro';
|
|
4
|
+
import PetriDish from './components/PetriDish.astro';
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
ui: Record<string, string>;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const { ui } = Astro.props;
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
<div class="fermentation-container">
|
|
14
|
+
<div class="fermentation-card">
|
|
15
|
+
<div class="fermentation-glow-1"></div>
|
|
16
|
+
<div class="fermentation-glow-2"></div>
|
|
17
|
+
|
|
18
|
+
<div class="fermentation-grid">
|
|
19
|
+
<div class="fermentation-controls-section">
|
|
20
|
+
<WizardMode ui={ui} />
|
|
21
|
+
<DigitalScale ui={ui} />
|
|
22
|
+
</div>
|
|
23
|
+
|
|
24
|
+
<div class="fermentation-visuals-section">
|
|
25
|
+
<PetriDish ui={ui} />
|
|
26
|
+
</div>
|
|
27
|
+
</div>
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
|
|
31
|
+
<script is:inline define:vars={{ ui }}>
|
|
32
|
+
const metricBtn = document.getElementById('toggle-metric-btn');
|
|
33
|
+
const imperialBtn = document.getElementById('toggle-imperial-btn');
|
|
34
|
+
const dryBtn = document.getElementById('toggle-dry-btn');
|
|
35
|
+
const wetBtn = document.getElementById('toggle-wet-btn');
|
|
36
|
+
const vegInput = document.getElementById('veg-weight-input');
|
|
37
|
+
const waterInput = document.getElementById('water-weight-input');
|
|
38
|
+
const waterGroup = document.getElementById('water-weight-group');
|
|
39
|
+
const salinitySlider = document.getElementById('salinity-slider');
|
|
40
|
+
|
|
41
|
+
const salinityDisplay = document.getElementById('salinity-val-display');
|
|
42
|
+
const scaleDisplay = document.getElementById('scale-main-value');
|
|
43
|
+
const scaleUnitDisplay = document.getElementById('scale-unit-label');
|
|
44
|
+
const fineDisplay = document.getElementById('fine-teaspoons-val');
|
|
45
|
+
const kosherDisplay = document.getElementById('kosher-teaspoons-val');
|
|
46
|
+
|
|
47
|
+
const petriBac = document.getElementById('petri-bacteria');
|
|
48
|
+
const petriMold = document.getElementById('petri-mold');
|
|
49
|
+
const petriCrys = document.getElementById('petri-crystals');
|
|
50
|
+
|
|
51
|
+
const statusBadge = document.getElementById('status-badge');
|
|
52
|
+
const statusBadgeText = document.getElementById('status-badge-text');
|
|
53
|
+
const statusDescText = document.getElementById('status-description-text');
|
|
54
|
+
|
|
55
|
+
let activeMode = 'dry';
|
|
56
|
+
let activeUnit = 'metric';
|
|
57
|
+
|
|
58
|
+
function updatePetriAndStatus(salinity) {
|
|
59
|
+
if (salinity < 2.0) {
|
|
60
|
+
petriMold.style.opacity = '1';
|
|
61
|
+
petriBac.style.opacity = '0';
|
|
62
|
+
petriCrys.style.opacity = '0';
|
|
63
|
+
|
|
64
|
+
statusBadge.className = 'status-badge danger';
|
|
65
|
+
statusBadgeText.textContent = ui.statusDanger;
|
|
66
|
+
statusDescText.textContent = ui.statusDangerDesc;
|
|
67
|
+
} else if (salinity > 3.5) {
|
|
68
|
+
petriMold.style.opacity = '0';
|
|
69
|
+
petriBac.style.opacity = '0';
|
|
70
|
+
petriCrys.style.opacity = '1';
|
|
71
|
+
|
|
72
|
+
statusBadge.className = 'status-badge inhibited';
|
|
73
|
+
statusBadgeText.textContent = ui.statusInhibited;
|
|
74
|
+
statusDescText.textContent = ui.statusInhibitedDesc;
|
|
75
|
+
} else {
|
|
76
|
+
petriMold.style.opacity = '0';
|
|
77
|
+
petriBac.style.opacity = '1';
|
|
78
|
+
petriCrys.style.opacity = '0';
|
|
79
|
+
|
|
80
|
+
statusBadge.className = 'status-badge optimal';
|
|
81
|
+
statusBadgeText.textContent = ui.statusOptimal;
|
|
82
|
+
statusDescText.textContent = ui.statusOptimalDesc;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function updateView() {
|
|
87
|
+
const vegWeight = Math.max(0, parseFloat(vegInput.value) || 0);
|
|
88
|
+
const waterWeight = Math.max(0, parseFloat(waterInput.value) || 0);
|
|
89
|
+
const salinity = Math.max(0, parseFloat(salinitySlider.value) || 0);
|
|
90
|
+
|
|
91
|
+
salinityDisplay.textContent = `${salinity.toFixed(1)}%`;
|
|
92
|
+
|
|
93
|
+
const totalWeight = activeMode === 'dry' ? vegWeight : (vegWeight + waterWeight);
|
|
94
|
+
const saltResult = Math.max(0, (totalWeight * salinity) / 100);
|
|
95
|
+
|
|
96
|
+
scaleDisplay.textContent = saltResult.toFixed(2);
|
|
97
|
+
scaleUnitDisplay.textContent = activeUnit === 'metric' ? 'g' : 'oz';
|
|
98
|
+
|
|
99
|
+
const saltGrams = activeUnit === 'imperial' ? saltResult * 28.3495 : saltResult;
|
|
100
|
+
|
|
101
|
+
fineDisplay.textContent = Math.max(0, saltGrams / 5.7).toFixed(1);
|
|
102
|
+
kosherDisplay.textContent = Math.max(0, saltGrams / 4.3).toFixed(1);
|
|
103
|
+
|
|
104
|
+
updatePetriAndStatus(salinity);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function convertInputsTo(targetUnit) {
|
|
108
|
+
const currentVeg = parseFloat(vegInput.value) || 0;
|
|
109
|
+
const currentWater = parseFloat(waterInput.value) || 0;
|
|
110
|
+
|
|
111
|
+
const labels = document.querySelectorAll('.weight-unit-label');
|
|
112
|
+
|
|
113
|
+
if (targetUnit === 'imperial') {
|
|
114
|
+
vegInput.value = (currentVeg * 0.035274).toFixed(1);
|
|
115
|
+
waterInput.value = (currentWater * 0.035274).toFixed(1);
|
|
116
|
+
labels.forEach(label => label.textContent = 'oz');
|
|
117
|
+
} else {
|
|
118
|
+
vegInput.value = Math.round(currentVeg / 0.035274).toString();
|
|
119
|
+
waterInput.value = Math.round(currentWater / 0.035274).toString();
|
|
120
|
+
labels.forEach(label => label.textContent = 'g');
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
metricBtn.addEventListener('click', () => {
|
|
125
|
+
if (activeUnit === 'metric') return;
|
|
126
|
+
activeUnit = 'metric';
|
|
127
|
+
metricBtn.classList.add('active');
|
|
128
|
+
imperialBtn.classList.remove('active');
|
|
129
|
+
convertInputsTo('metric');
|
|
130
|
+
updateView();
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
imperialBtn.addEventListener('click', () => {
|
|
134
|
+
if (activeUnit === 'imperial') return;
|
|
135
|
+
activeUnit = 'imperial';
|
|
136
|
+
imperialBtn.classList.add('active');
|
|
137
|
+
metricBtn.classList.remove('active');
|
|
138
|
+
convertInputsTo('imperial');
|
|
139
|
+
updateView();
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
dryBtn.addEventListener('click', () => {
|
|
143
|
+
activeMode = 'dry';
|
|
144
|
+
dryBtn.classList.add('active');
|
|
145
|
+
wetBtn.classList.remove('active');
|
|
146
|
+
waterGroup.classList.add('hidden');
|
|
147
|
+
updateView();
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
wetBtn.addEventListener('click', () => {
|
|
151
|
+
activeMode = 'wet';
|
|
152
|
+
wetBtn.classList.add('active');
|
|
153
|
+
dryBtn.classList.remove('active');
|
|
154
|
+
waterGroup.classList.remove('hidden');
|
|
155
|
+
updateView();
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
vegInput.addEventListener('input', updateView);
|
|
159
|
+
waterInput.addEventListener('input', updateView);
|
|
160
|
+
salinitySlider.addEventListener('input', updateView);
|
|
161
|
+
|
|
162
|
+
updateView();
|
|
163
|
+
</script>
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
---
|
|
2
|
+
interface Props {
|
|
3
|
+
ui: Record<string, string>;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
const { ui } = Astro.props;
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
<div class="digital-scale-container">
|
|
10
|
+
<div class="scale-header">
|
|
11
|
+
<span class="scale-title">{ui.scaleTitle}</span>
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
<div class="scale-plate">
|
|
15
|
+
<div class="scale-display">
|
|
16
|
+
<div class="scale-digits">
|
|
17
|
+
<span id="scale-main-value">25.00</span>
|
|
18
|
+
<span id="scale-unit-label" class="scale-unit">g</span>
|
|
19
|
+
</div>
|
|
20
|
+
<div class="scale-label">{ui.saltGramsLabel}</div>
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
23
|
+
|
|
24
|
+
<div class="scale-conversions">
|
|
25
|
+
<div class="conversion-item">
|
|
26
|
+
<span class="conversion-label">{ui.fineLabel}</span>
|
|
27
|
+
<div class="conversion-val">
|
|
28
|
+
<span id="fine-teaspoons-val">4.4</span>
|
|
29
|
+
<span class="conversion-unit">tsp</span>
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
<div class="conversion-item">
|
|
34
|
+
<span class="conversion-label">{ui.kosherLabel}</span>
|
|
35
|
+
<div class="conversion-val">
|
|
36
|
+
<span id="kosher-teaspoons-val">5.8</span>
|
|
37
|
+
<span class="conversion-unit">tsp</span>
|
|
38
|
+
</div>
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
41
|
+
|
|
42
|
+
<p class="scale-disclaimer">{ui.disclaimer}</p>
|
|
43
|
+
</div>
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
---
|
|
2
|
+
interface Props {
|
|
3
|
+
ui: Record<string, string>;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
const { ui } = Astro.props;
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
<div class="petri-dish-section">
|
|
10
|
+
<div class="petri-header">
|
|
11
|
+
<span class="petri-title">{ui.petriTitle}</span>
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
<div class="petri-container">
|
|
15
|
+
<div class="petri-dish">
|
|
16
|
+
<div class="petri-agar">
|
|
17
|
+
<svg viewBox="0 0 200 200" class="petri-svg" xmlns="http://www.w3.org/2000/svg">
|
|
18
|
+
<g id="petri-bacteria" class="bacteria-layer">
|
|
19
|
+
<ellipse cx="60" cy="80" rx="6" ry="14" transform="rotate(25 60 80)" class="bac-cell"></ellipse>
|
|
20
|
+
<ellipse cx="140" cy="70" rx="5" ry="12" transform="rotate(-35 140 70)" class="bac-cell"></ellipse>
|
|
21
|
+
<ellipse cx="100" cy="130" rx="7" ry="15" transform="rotate(45 100 130)" class="bac-cell"></ellipse>
|
|
22
|
+
<circle cx="80" cy="50" r="5" class="bac-coccus"></circle>
|
|
23
|
+
<circle cx="120" cy="110" r="4.5" class="bac-coccus"></circle>
|
|
24
|
+
<circle cx="70" cy="150" r="6" class="bac-coccus"></circle>
|
|
25
|
+
</g>
|
|
26
|
+
|
|
27
|
+
<g id="petri-mold" class="mold-layer">
|
|
28
|
+
<path d="M 40,100 C 50,80 70,80 80,100 C 90,80 110,80 120,100 C 110,120 90,120 80,100 C 70,120 50,120 40,100 Z" class="mold-fungus" fill="none" stroke-width="3"></path>
|
|
29
|
+
<path d="M 110,60 C 120,45 135,45 140,60 C 145,45 160,45 160,60" class="mold-fungus" fill="none" stroke-width="2.5"></path>
|
|
30
|
+
<path d="M 70,140 C 80,125 95,125 100,140 C 105,125 120,125 120,140" class="mold-fungus" fill="none" stroke-width="2.5"></path>
|
|
31
|
+
<circle cx="80" cy="100" r="3" class="mold-spore"></circle>
|
|
32
|
+
<circle cx="140" cy="60" r="2" class="mold-spore"></circle>
|
|
33
|
+
<circle cx="100" cy="140" r="2.5" class="mold-spore"></circle>
|
|
34
|
+
</g>
|
|
35
|
+
|
|
36
|
+
<g id="petri-crystals" class="crystals-layer">
|
|
37
|
+
<rect x="50" y="50" width="12" height="12" transform="rotate(15 56 56)" class="crystal-cube"></rect>
|
|
38
|
+
<rect x="130" y="120" width="16" height="16" transform="rotate(45 138 128)" class="crystal-cube"></rect>
|
|
39
|
+
<rect x="60" y="120" width="10" height="10" transform="rotate(5 65 125)" class="crystal-cube"></rect>
|
|
40
|
+
<rect x="140" y="45" width="14" height="14" transform="rotate(30 147 52)" class="crystal-cube"></rect>
|
|
41
|
+
</g>
|
|
42
|
+
</svg>
|
|
43
|
+
</div>
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
|
|
47
|
+
<div class="status-box">
|
|
48
|
+
<div id="status-badge" class="status-badge optimal">
|
|
49
|
+
<span id="status-badge-text">{ui.statusOptimal}</span>
|
|
50
|
+
</div>
|
|
51
|
+
<p id="status-description-text" class="status-desc-text">
|
|
52
|
+
{ui.statusOptimalDesc}
|
|
53
|
+
</p>
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
---
|
|
2
|
+
interface Props {
|
|
3
|
+
ui: Record<string, string>;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
const { ui } = Astro.props;
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
<div class="fermentation-wizard">
|
|
10
|
+
<div class="wizard-control-group">
|
|
11
|
+
<label class="wizard-label">{ui.unitLabel}</label>
|
|
12
|
+
<div class="unit-toggle-container">
|
|
13
|
+
<button type="button" id="toggle-metric-btn" class="mode-btn active" data-unit="metric">
|
|
14
|
+
{ui.metricUnit}
|
|
15
|
+
</button>
|
|
16
|
+
<button type="button" id="toggle-imperial-btn" class="mode-btn" data-unit="imperial">
|
|
17
|
+
{ui.imperialUnit}
|
|
18
|
+
</button>
|
|
19
|
+
</div>
|
|
20
|
+
</div>
|
|
21
|
+
|
|
22
|
+
<div class="wizard-control-group">
|
|
23
|
+
<label class="wizard-label">
|
|
24
|
+
{ui.modeLabel}
|
|
25
|
+
</label>
|
|
26
|
+
<div class="mode-toggle-container">
|
|
27
|
+
<button type="button" id="toggle-dry-btn" class="mode-btn active" data-mode="dry">
|
|
28
|
+
{ui.dryMode}
|
|
29
|
+
</button>
|
|
30
|
+
<button type="button" id="toggle-wet-btn" class="mode-btn" data-mode="wet">
|
|
31
|
+
{ui.wetMode}
|
|
32
|
+
</button>
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
|
|
36
|
+
<div class="wizard-control-group">
|
|
37
|
+
<label for="veg-weight-input" class="wizard-label">
|
|
38
|
+
{ui.vegWeightLabel} (<span class="weight-unit-label">g</span>)
|
|
39
|
+
</label>
|
|
40
|
+
<input
|
|
41
|
+
type="number"
|
|
42
|
+
id="veg-weight-input"
|
|
43
|
+
min="0"
|
|
44
|
+
max="100000"
|
|
45
|
+
step="any"
|
|
46
|
+
value="1000"
|
|
47
|
+
class="wizard-number-input"
|
|
48
|
+
inputmode="decimal"
|
|
49
|
+
/>
|
|
50
|
+
</div>
|
|
51
|
+
|
|
52
|
+
<div id="water-weight-group" class="wizard-control-group hidden">
|
|
53
|
+
<label for="water-weight-input" class="wizard-label">
|
|
54
|
+
{ui.waterWeightLabel} (<span class="weight-unit-label">g</span>)
|
|
55
|
+
</label>
|
|
56
|
+
<input
|
|
57
|
+
type="number"
|
|
58
|
+
id="water-weight-input"
|
|
59
|
+
min="0"
|
|
60
|
+
max="100000"
|
|
61
|
+
step="any"
|
|
62
|
+
value="500"
|
|
63
|
+
class="wizard-number-input"
|
|
64
|
+
inputmode="decimal"
|
|
65
|
+
/>
|
|
66
|
+
</div>
|
|
67
|
+
|
|
68
|
+
<div class="wizard-control-group">
|
|
69
|
+
<div class="slider-header">
|
|
70
|
+
<label for="salinity-slider" class="wizard-label">{ui.salinityLabel}</label>
|
|
71
|
+
<span id="salinity-val-display" class="slider-val">2.5%</span>
|
|
72
|
+
</div>
|
|
73
|
+
<input
|
|
74
|
+
type="range"
|
|
75
|
+
id="salinity-slider"
|
|
76
|
+
min="1.0"
|
|
77
|
+
max="6.0"
|
|
78
|
+
step="0.1"
|
|
79
|
+
value="2.5"
|
|
80
|
+
class="wizard-slider"
|
|
81
|
+
/>
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { CookingToolEntry } from '../../types';
|
|
2
|
+
|
|
3
|
+
export const lactoFermentationSalt: CookingToolEntry = {
|
|
4
|
+
id: 'lacto-fermentation-salt-calculator',
|
|
5
|
+
icons: {
|
|
6
|
+
bg: 'mdi:leaf',
|
|
7
|
+
fg: 'mdi:flask-outline',
|
|
8
|
+
},
|
|
9
|
+
i18n: {
|
|
10
|
+
en: () => import('./i18n/en').then((m) => m.content),
|
|
11
|
+
de: () => import('./i18n/de').then((m) => m.content),
|
|
12
|
+
es: () => import('./i18n/es').then((m) => m.content),
|
|
13
|
+
fr: () => import('./i18n/fr').then((m) => m.content),
|
|
14
|
+
id: () => import('./i18n/id').then((m) => m.content),
|
|
15
|
+
it: () => import('./i18n/it').then((m) => m.content),
|
|
16
|
+
ja: () => import('./i18n/ja').then((m) => m.content),
|
|
17
|
+
ko: () => import('./i18n/ko').then((m) => m.content),
|
|
18
|
+
nl: () => import('./i18n/nl').then((m) => m.content),
|
|
19
|
+
pl: () => import('./i18n/pl').then((m) => m.content),
|
|
20
|
+
pt: () => import('./i18n/pt').then((m) => m.content),
|
|
21
|
+
ru: () => import('./i18n/ru').then((m) => m.content),
|
|
22
|
+
sv: () => import('./i18n/sv').then((m) => m.content),
|
|
23
|
+
tr: () => import('./i18n/tr').then((m) => m.content),
|
|
24
|
+
zh: () => import('./i18n/zh').then((m) => m.content),
|
|
25
|
+
},
|
|
26
|
+
};
|