@jjlmoya/utils-aquarium 1.0.0 → 1.53.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.
Files changed (29) hide show
  1. package/package.json +13 -4
  2. package/src/category/i18n/zh.ts +1 -1
  3. package/src/tests/bibliography_wellformed_export.test.ts +46 -0
  4. package/src/tests/i18n_coverage.test.ts +1 -1
  5. package/src/tests/seo_wellformed_export.test.ts +65 -0
  6. package/src/tool/aquariumTankVolumeWaterChangeCalculator/aquarium-tank-volume-water-change-calculator.css +107 -56
  7. package/src/tool/aquariumTankVolumeWaterChangeCalculator/bibliography.ts +6 -6
  8. package/src/tool/aquariumTankVolumeWaterChangeCalculator/component.astro +24 -39
  9. package/src/tool/aquariumTankVolumeWaterChangeCalculator/controller.ts +90 -39
  10. package/src/tool/aquariumTankVolumeWaterChangeCalculator/dom-views.ts +19 -11
  11. package/src/tool/aquariumTankVolumeWaterChangeCalculator/entry.ts +14 -0
  12. package/src/tool/aquariumTankVolumeWaterChangeCalculator/i18n/de.ts +34 -0
  13. package/src/tool/aquariumTankVolumeWaterChangeCalculator/i18n/en.ts +8 -8
  14. package/src/tool/aquariumTankVolumeWaterChangeCalculator/i18n/es.ts +34 -0
  15. package/src/tool/aquariumTankVolumeWaterChangeCalculator/i18n/fr.ts +34 -0
  16. package/src/tool/aquariumTankVolumeWaterChangeCalculator/i18n/id.ts +34 -0
  17. package/src/tool/aquariumTankVolumeWaterChangeCalculator/i18n/it.ts +34 -0
  18. package/src/tool/aquariumTankVolumeWaterChangeCalculator/i18n/ja.ts +34 -0
  19. package/src/tool/aquariumTankVolumeWaterChangeCalculator/i18n/ko.ts +34 -0
  20. package/src/tool/aquariumTankVolumeWaterChangeCalculator/i18n/nl.ts +34 -0
  21. package/src/tool/aquariumTankVolumeWaterChangeCalculator/i18n/pl.ts +34 -0
  22. package/src/tool/aquariumTankVolumeWaterChangeCalculator/i18n/pt.ts +34 -0
  23. package/src/tool/aquariumTankVolumeWaterChangeCalculator/i18n/ru.ts +34 -0
  24. package/src/tool/aquariumTankVolumeWaterChangeCalculator/i18n/sv.ts +34 -0
  25. package/src/tool/aquariumTankVolumeWaterChangeCalculator/i18n/tr.ts +34 -0
  26. package/src/tool/aquariumTankVolumeWaterChangeCalculator/i18n/zh.ts +34 -0
  27. package/src/tool/aquariumTankVolumeWaterChangeCalculator/localized-content.ts +60 -0
  28. package/src/tool/aquariumTankVolumeWaterChangeCalculator/logic.test.ts +10 -0
  29. package/src/tool/aquariumTankVolumeWaterChangeCalculator/ui.ts +3 -3
@@ -3,10 +3,11 @@ import {
3
3
  centimetresToInches,
4
4
  inchesToCentimetres,
5
5
  type TankInput,
6
+ type TankShape,
6
7
  type UnitSystem,
7
8
  } from './logic';
8
9
  import { defaultAquariumState, loadAquariumState, saveAquariumState, type SavedAquariumState } from './storage';
9
- import { renderEmpty, renderError, renderResults } from './dom-views';
10
+ import { renderError, renderResults } from './dom-views';
10
11
  import type { AquariumTankVolumeWaterChangeCalculatorUI } from './ui';
11
12
 
12
13
  interface ControllerContext {
@@ -14,14 +15,29 @@ interface ControllerContext {
14
15
  ui: AquariumTankVolumeWaterChangeCalculatorUI;
15
16
  state: SavedAquariumState;
16
17
  locale: string;
18
+ shapeInputs: Partial<Record<TankShape, Partial<TankInput>>>;
17
19
  }
18
20
 
19
- const DIMENSION_FIELDS = ['lengthCm', 'widthCm', 'heightCm', 'diameterCm', 'bowDepthCm', 'substrateDepthCm'] as const;
21
+ type ShapeField = 'lengthCm' | 'widthCm' | 'heightCm' | 'diameterCm' | 'bowDepthCm';
22
+
23
+ const SHAPE_FIELDS: Record<TankShape, ShapeField[]> = {
24
+ rectangular: ['lengthCm', 'widthCm', 'heightCm'],
25
+ cylindrical: ['diameterCm', 'heightCm'],
26
+ bow: ['lengthCm', 'widthCm', 'heightCm', 'bowDepthCm'],
27
+ };
28
+
29
+ const FIELD_LABELS: Record<ShapeField, keyof AquariumTankVolumeWaterChangeCalculatorUI> = {
30
+ lengthCm: 'lengthLabel',
31
+ widthCm: 'widthLabel',
32
+ heightCm: 'heightLabel',
33
+ diameterCm: 'diameterLabel',
34
+ bowDepthCm: 'bowDepthLabel',
35
+ };
36
+
37
+ const COMMON_FIELDS = ['substrateDepthCm'] as const;
20
38
 
21
39
  function getInput(root: HTMLElement, field: string): HTMLInputElement | null {
22
- const selector = '[data-input="' + field + '"]';
23
- const inputs = Array.from(root.querySelectorAll<HTMLInputElement>(selector));
24
- return inputs.find((input) => input.closest('[hidden]') === null) ?? inputs[0] ?? null;
40
+ return root.querySelector<HTMLInputElement>('[data-input="' + field + '"]');
25
41
  }
26
42
 
27
43
  function readNumber(root: HTMLElement, field: string, unit: UnitSystem): number {
@@ -33,18 +49,63 @@ function readNumber(root: HTMLElement, field: string, unit: UnitSystem): number
33
49
 
34
50
  function readInput(context: ControllerContext): TankInput {
35
51
  const input = { ...context.state.input };
36
- const activeShape = context.root.querySelector<HTMLElement>('[data-shape].is-active')?.dataset.shape;
37
- if (activeShape === 'rectangular' || activeShape === 'cylindrical' || activeShape === 'bow') input.shape = activeShape;
38
- for (const field of DIMENSION_FIELDS) input[field] = readNumber(context.root, field, context.state.unit);
52
+ const shape = context.root.querySelector<HTMLElement>('[data-shape].is-active')?.dataset.shape;
53
+ if (shape === 'rectangular' || shape === 'cylindrical' || shape === 'bow') input.shape = shape;
54
+ for (const field of SHAPE_FIELDS[input.shape]) input[field] = readNumber(context.root, field, context.state.unit);
55
+ for (const field of COMMON_FIELDS) input[field] = readNumber(context.root, field, context.state.unit);
39
56
  input.changePercent = readNumber(context.root, 'changePercent', 'metric');
40
57
  return input;
41
58
  }
42
59
 
43
- function writeNumber(root: HTMLElement, field: string, valueCm: number, unit: UnitSystem): void {
44
- const element = getInput(root, field);
45
- if (element === null) return;
60
+ function displayFieldValue(valueCm: number, unit: UnitSystem): string {
46
61
  const value = unit === 'metric' ? valueCm : centimetresToInches(valueCm);
47
- element.value = String(Math.round(value * 10) / 10);
62
+ return String(Math.round(value * 10) / 10);
63
+ }
64
+
65
+ function createShapeField(context: ControllerContext, field: ShapeField): HTMLLabelElement {
66
+ const label = document.createElement('label');
67
+ label.className = 'atvwc-field';
68
+ label.dataset.field = field;
69
+ const title = document.createElement('span');
70
+ title.textContent = context.ui[FIELD_LABELS[field]] ?? '';
71
+ const unit = document.createElement('span');
72
+ unit.dataset.lengthUnit = '';
73
+ unit.textContent = context.state.unit === 'metric' ? context.ui.lengthUnitMetric : context.ui.lengthUnitImperial;
74
+ title.append(' ', unit);
75
+ const input = document.createElement('input');
76
+ input.dataset.input = field;
77
+ input.type = 'number';
78
+ input.min = field === 'bowDepthCm' ? '0' : '0.1';
79
+ input.max = '500';
80
+ input.step = '0.1';
81
+ input.inputMode = 'decimal';
82
+ input.value = displayFieldValue(context.state.input[field], context.state.unit);
83
+ label.append(title, input);
84
+ return label;
85
+ }
86
+
87
+ function renderShapeFields(context: ControllerContext): void {
88
+ const container = context.root.querySelector<HTMLElement>('[data-dynamic-fields]');
89
+ if (container === null) return;
90
+ const fragment = document.createDocumentFragment();
91
+ for (const field of SHAPE_FIELDS[context.state.input.shape]) fragment.append(createShapeField(context, field));
92
+ container.replaceChildren(fragment);
93
+ }
94
+
95
+ function rememberShapeInput(context: ControllerContext): void {
96
+ const shape = context.state.input.shape;
97
+ const remembered: Partial<TankInput> = {};
98
+ for (const field of SHAPE_FIELDS[shape]) remembered[field] = context.state.input[field];
99
+ context.shapeInputs[shape] = remembered;
100
+ }
101
+
102
+ function changeShape(context: ControllerContext, shape: TankShape): void {
103
+ context.state.input = readInput(context);
104
+ rememberShapeInput(context);
105
+ const remembered = context.shapeInputs[shape] ?? {};
106
+ context.state.input = { ...context.state.input, ...remembered, shape };
107
+ writeInput(context);
108
+ calculate(context);
48
109
  }
49
110
 
50
111
  function setUnitButtons(context: ControllerContext): void {
@@ -54,29 +115,32 @@ function setUnitButtons(context: ControllerContext): void {
54
115
  button.setAttribute('aria-pressed', String(active));
55
116
  });
56
117
  const unitText = context.state.unit === 'metric' ? context.ui.lengthUnitMetric : context.ui.lengthUnitImperial;
57
- context.root.querySelectorAll<HTMLElement>('[data-length-unit]').forEach((label) => {
58
- label.textContent = unitText;
59
- });
118
+ context.root.querySelectorAll<HTMLElement>('[data-length-unit]').forEach((label) => { label.textContent = unitText; });
60
119
  }
61
120
 
62
121
  function setShapeButtons(context: ControllerContext): void {
122
+ context.root.dataset.shape = context.state.input.shape;
63
123
  context.root.querySelectorAll<HTMLElement>('[data-shape]').forEach((button) => {
64
124
  const active = button.dataset.shape === context.state.input.shape;
65
125
  button.classList.toggle('is-active', active);
66
126
  button.setAttribute('aria-pressed', String(active));
67
127
  });
68
- context.root.querySelectorAll<HTMLElement>('[data-shape-fields]').forEach((group) => {
69
- group.toggleAttribute('hidden', group.getAttribute('data-shape-fields') !== context.state.input.shape);
70
- });
128
+ }
129
+
130
+ function writeNumber(root: HTMLElement, field: string, valueCm: number, unit: UnitSystem): void {
131
+ const element = getInput(root, field);
132
+ if (element !== null) element.value = displayFieldValue(valueCm, unit);
71
133
  }
72
134
 
73
135
  function writeInput(context: ControllerContext): void {
74
- for (const field of DIMENSION_FIELDS) writeNumber(context.root, field, context.state.input[field], context.state.unit);
136
+ renderShapeFields(context);
137
+ setUnitButtons(context);
138
+ setShapeButtons(context);
139
+ for (const field of SHAPE_FIELDS[context.state.input.shape]) writeNumber(context.root, field, context.state.input[field], context.state.unit);
140
+ for (const field of COMMON_FIELDS) writeNumber(context.root, field, context.state.input[field], context.state.unit);
75
141
  const change = getInput(context.root, 'changePercent');
76
142
  if (change !== null) change.value = String(context.state.input.changePercent);
77
143
  context.root.dataset.unit = context.state.unit;
78
- setUnitButtons(context);
79
- setShapeButtons(context);
80
144
  }
81
145
 
82
146
  function calculate(context: ControllerContext): void {
@@ -87,18 +151,12 @@ function calculate(context: ControllerContext): void {
87
151
  renderError(context.root, context.ui.invalidState);
88
152
  return;
89
153
  }
90
- renderResults(context.root, {
91
- result: result.value,
92
- unit: context.state.unit,
93
- locale: context.locale,
94
- ui: context.ui,
95
- });
154
+ renderResults(context.root, { result: result.value, unit: context.state.unit, locale: context.locale, ui: context.ui });
96
155
  }
97
156
 
98
157
  function reset(context: ControllerContext): void {
99
158
  context.state = defaultAquariumState();
100
159
  writeInput(context);
101
- renderEmpty(context.root, context.ui.emptyState);
102
160
  calculate(context);
103
161
  }
104
162
 
@@ -107,9 +165,7 @@ function bind(context: ControllerContext): void {
107
165
  button.addEventListener('click', () => {
108
166
  const shape = button.dataset.shape;
109
167
  if (shape !== 'rectangular' && shape !== 'cylindrical' && shape !== 'bow') return;
110
- context.state.input.shape = shape;
111
- setShapeButtons(context);
112
- calculate(context);
168
+ changeShape(context, shape);
113
169
  });
114
170
  });
115
171
  context.root.querySelectorAll<HTMLElement>('[data-unit-choice]').forEach((button) => {
@@ -122,9 +178,8 @@ function bind(context: ControllerContext): void {
122
178
  calculate(context);
123
179
  });
124
180
  });
125
- context.root.querySelectorAll('input').forEach((input) => input.addEventListener('input', () => calculate(context)));
181
+ context.root.addEventListener('input', () => calculate(context));
126
182
  context.root.querySelector('[data-action="reset"]')?.addEventListener('click', () => reset(context));
127
- context.root.querySelector('[data-action="calculate"]')?.addEventListener('click', () => calculate(context));
128
183
  }
129
184
 
130
185
  function readUi(root: HTMLElement): AquariumTankVolumeWaterChangeCalculatorUI {
@@ -135,12 +190,8 @@ export function initAquariumTankVolumeWaterChangeCalculator(): void {
135
190
  const root = document.querySelector<HTMLElement>('#aquarium-tank-volume-water-change-calculator');
136
191
  if (root === null) return;
137
192
  const stored = loadAquariumState();
138
- const context: ControllerContext = {
139
- root,
140
- ui: readUi(root),
141
- state: stored ?? defaultAquariumState(),
142
- locale: document.documentElement.lang || 'en',
143
- };
193
+ const state = stored ?? defaultAquariumState();
194
+ const context: ControllerContext = { root, ui: readUi(root), state, locale: document.documentElement.lang || 'en', shapeInputs: {} };
144
195
  writeInput(context);
145
196
  bind(context);
146
197
  calculate(context);
@@ -16,15 +16,21 @@ function formatNumber(value: number, locale: string): string {
16
16
  return new Intl.NumberFormat(locale, { maximumFractionDigits: 1 }).format(value);
17
17
  }
18
18
 
19
- function displayVolume(value: number, unit: UnitSystem, locale: string): string {
19
+ function displayVolume(value: number, unit: UnitSystem, locale: string, ui: AquariumTankVolumeWaterChangeCalculatorUI): string {
20
20
  const amount = unit === 'metric' ? value : litresToGallons(value);
21
- const label = unit === 'metric' ? 'L' : 'US gal';
21
+ const label = unit === 'metric' ? ui.litreUnit : ui.gallonUnit;
22
22
  return formatNumber(amount, locale) + ' ' + label;
23
23
  }
24
24
 
25
- function displayMass(value: number, unit: UnitSystem, locale: string): string {
25
+ function displayMass(value: number, unit: UnitSystem, locale: string, ui: AquariumTankVolumeWaterChangeCalculatorUI): string {
26
26
  const amount = unit === 'metric' ? value : kilogramsToPounds(value);
27
- const label = unit === 'metric' ? 'kg' : 'lb';
27
+ const label = unit === 'metric' ? ui.kilogramUnit : ui.poundUnit;
28
+ return formatNumber(amount, locale) + ' ' + label;
29
+ }
30
+
31
+ function displayArea(valueCm2: number, unit: UnitSystem, locale: string, ui: AquariumTankVolumeWaterChangeCalculatorUI): string {
32
+ const amount = unit === 'metric' ? valueCm2 / 10000 : valueCm2 / 929.0304;
33
+ const label = unit === 'metric' ? ui.squareMetreUnit : ui.squareFootUnit;
28
34
  return formatNumber(amount, locale) + ' ' + label;
29
35
  }
30
36
 
@@ -37,16 +43,18 @@ interface RenderOptions {
37
43
 
38
44
  export function renderResults(root: HTMLElement, options: RenderOptions): void {
39
45
  const { result, unit, locale, ui } = options;
40
- setText(root, '[data-result="gross"]', displayVolume(result.grossLitres, unit, locale));
41
- setText(root, '[data-result="substrate"]', displayVolume(result.substrateLitres, unit, locale));
42
- setText(root, '[data-result="net"]', displayVolume(result.netLitres, unit, locale));
43
- setText(root, '[data-result="change"]', displayVolume(result.changeLitres, unit, locale));
44
- setText(root, '[data-result="mass"]', displayMass(result.waterMassKg, unit, locale));
45
- setText(root, '[data-result="footprint"]', formatNumber(result.footprintCm2 / 10000, locale) + ' m²');
46
+ setText(root, '[data-result="gross"]', displayVolume(result.grossLitres, unit, locale, ui));
47
+ setText(root, '[data-result="substrate"]', displayVolume(result.substrateLitres, unit, locale, ui));
48
+ setText(root, '[data-result="net"]', displayVolume(result.netLitres, unit, locale, ui));
49
+ setText(root, '[data-result="change"]', displayVolume(result.changeLitres, unit, locale, ui));
50
+ setText(root, '[data-result="change-summary"]', ui.changeLabel + ': ' + displayVolume(result.changeLitres, unit, locale, ui));
51
+ setText(root, '[data-result="stage-volume"]', displayVolume(result.netLitres, unit, locale, ui));
52
+ setText(root, '[data-result="mass"]', displayMass(result.waterMassKg, unit, locale, ui));
53
+ setText(root, '[data-result="footprint"]', displayArea(result.footprintCm2, unit, locale, ui));
46
54
  setText(root, '[data-result="share"]', formatNumber(getSubstrateShare(result), locale) + '%');
47
55
  setText(root, '[data-result="scale"]', ui[getTankScale(result.netLitres)] ?? '');
48
56
  const water = getElement<HTMLElement>(root, '[data-water]');
49
- if (water !== null) water.style.setProperty('--water-level', String(Math.min(92, Math.max(20, result.netLitres / 4))));
57
+ if (water !== null) water.style.setProperty('--water-level', Math.round((result.netLitres / result.grossLitres) * 100) + '%');
50
58
  root.classList.add('has-results');
51
59
  root.classList.remove('has-error');
52
60
  }
@@ -8,6 +8,20 @@ export const aquariumTankVolumeWaterChangeCalculator: AquariumToolEntry<Aquarium
8
8
  id: 'aquarium-tank-volume-water-change-calculator',
9
9
  icons: { bg: 'mdi:fishbowl-outline', fg: 'mdi:water-opacity' },
10
10
  i18n: {
11
+ de: () => import('./i18n/de').then((m) => m.content),
11
12
  en: () => import('./i18n/en').then((m) => m.content),
13
+ es: () => import('./i18n/es').then((m) => m.content),
14
+ fr: () => import('./i18n/fr').then((m) => m.content),
15
+ id: () => import('./i18n/id').then((m) => m.content),
16
+ it: () => import('./i18n/it').then((m) => m.content),
17
+ ja: () => import('./i18n/ja').then((m) => m.content),
18
+ ko: () => import('./i18n/ko').then((m) => m.content),
19
+ nl: () => import('./i18n/nl').then((m) => m.content),
20
+ pl: () => import('./i18n/pl').then((m) => m.content),
21
+ pt: () => import('./i18n/pt').then((m) => m.content),
22
+ ru: () => import('./i18n/ru').then((m) => m.content),
23
+ sv: () => import('./i18n/sv').then((m) => m.content),
24
+ tr: () => import('./i18n/tr').then((m) => m.content),
25
+ zh: () => import('./i18n/zh').then((m) => m.content),
12
26
  },
13
27
  };
@@ -0,0 +1,34 @@
1
+ import { createAquariumContent } from '../localized-content';
2
+
3
+ export const content = createAquariumContent({
4
+ locale: 'de',
5
+ slug: 'aquarium-volumen-wasserwechsel-rechner',
6
+ title: 'Aquarium Volumen und Wasserwechsel Rechner',
7
+ description: 'Berechne das nutzbare Aquariumvolumen, den Verdrängungsraum des Bodengrunds und die Wassermenge für einen geplanten Wasserwechsel aus den Innenmaßen.',
8
+ faq: [
9
+ { question: 'Verwendet der Rechner die angegebene Beckengröße?', answer: 'Nein. Er zieht die Höhe des Bodengrunds vom geometrischen Volumen ab und liefert damit eine Schätzung des nutzbaren Wasservolumens.' },
10
+ { question: 'Wie wird ein gebogenes Frontglas berechnet?', answer: 'Der Rechner ergänzt die rechteckige Grundfläche um ein angenähertes Kreissegment. Das Ergebnis bleibt wegen unterschiedlicher Glasformen eine Näherung.' },
11
+ { question: 'Welche Einheiten zeigt der Rechner?', answer: 'Im metrischen Modus erscheinen Zentimeter, Liter und Kilogramm. Im imperialen Modus erscheinen Zoll, Gallonen und Pfund.' },
12
+ { question: 'Empfiehlt der Rechner einen Wasserwechselplan?', answer: 'Nein. Er berechnet nur die Wassermenge für den von dir eingegebenen Prozentsatz und beurteilt weder Wasserqualität noch Besatz.' },
13
+ { question: 'Werden meine Eingaben übertragen?', answer: 'Nein. Die Berechnung läuft im Browser. Die letzten Einstellungen können lokal in diesem Browser gespeichert werden.' },
14
+ ],
15
+ howTo: [
16
+ { name: 'Beckenform wählen', text: 'Wähle ein rechteckiges, zylindrisches oder gebogenes Becken. Danach erscheinen nur die Maße für diese Form.' },
17
+ { name: 'Innenmaße eingeben', text: 'Gib Länge, Breite und Höhe oder bei einem Zylinder Durchmesser und Höhe ein. Innenmaße sind meist die bessere Grundlage.' },
18
+ { name: 'Bodengrund abziehen', text: 'Miss die durchschnittliche Höhe des Bodengrunds und trage sie ein. Diese Schicht wird vom Bruttovolumen abgezogen.' },
19
+ { name: 'Wasserwechsel festlegen', text: 'Gib den geplanten Prozentsatz ein. Der Rechner zeigt die entsprechende Wassermenge und eine ungefähre Wassermasse.' },
20
+ ],
21
+ seo: [
22
+ { type: 'title', text: 'Wie viel Wasser ist wirklich im Becken?', level: 2 },
23
+ { type: 'paragraph', html: 'Die Nennangabe eines Aquariums beschreibt oft das randvolle Becken. Für Dosierung, Heizung und einen Wasserwechsel ist das nutzbare Volumen wichtiger. Der Rechner arbeitet deshalb mit Innenmaßen und zieht den Bodengrund ab.' },
24
+ { type: 'title', text: 'Die passende Grundfläche für jede Form', level: 2 },
25
+ { type: 'list', items: ['<strong>Rechteckig:</strong> Länge mal Breite mal Höhe.', '<strong>Zylindrisch:</strong> Grundfläche mit pi mal Radius zum Quadrat mal Höhe.', '<strong>Gebogene Front:</strong> Rechteckige Grundfläche plus angenähertes Kreissegment.'] },
26
+ { type: 'title', text: 'Messen statt die Verpackung schätzen', level: 2 },
27
+ { type: 'paragraph', html: 'Miss im Inneren des Beckens. Wenn der Bodengrund ansteigt, miss ihn an mehreren Stellen und trage den Mittelwert ein. Bei stark unregelmäßigen Formen ist das tatsächliche Füllvolumen mit einem bekannten Eimer genauer als eine geometrische Näherung.' },
28
+ { type: 'tip', title: 'Wasserwechsel sicher planen', html: '<p>Nutze das Ergebnis als Arbeitswert für den eingegebenen Prozentsatz. Salz, Medikamente und andere Mittel können zusätzliche Anweisungen des jeweiligen Produkts benötigen.</p>' },
29
+ { type: 'table', headers: ['Ergebnis', 'Bedeutung'], rows: [['Bruttovolumen', 'Geometrisches Volumen vor dem Bodengrund'], ['Bodengrund', 'Geschätzter Raum, den die Substratschicht einnimmt'], ['Nutzbares Wasservolumen', 'Arbeitswert nach Abzug des Bodengrunds'], ['Wasserwechsel', 'Der eingegebene Anteil des nutzbaren Volumens']] },
30
+ ],
31
+ ui: {
32
+ configureLabel: 'Becken konfigurieren', dimensionsTitle: 'Beckenmaße', shapeLabel: 'Beckenform', rectangularShape: 'Rechteckig', cylindricalShape: 'Zylindrisch', bowShape: 'Gebogene Front', lengthLabel: 'Innenlänge', widthLabel: 'Innenbreite', heightLabel: 'Innenhöhe', diameterLabel: 'Innendurchmesser', bowDepthLabel: 'Tiefe der Frontkurve', substrateLabel: 'Bodengrundhöhe', changeLabel: 'Wasserwechsel', unitLabel: 'Einheiten', metricLabel: 'Metrisch', imperialLabel: 'Imperial', resetLabel: 'Zurücksetzen', grossVolumeLabel: 'Bruttovolumen', substrateVolumeLabel: 'Bodengrundverdrängung', netVolumeLabel: 'Nutzbares Wasservolumen', changeVolumeLabel: 'Zu wechselndes Wasser', waterWeightLabel: 'Wassermasse', emptyState: 'Maße anpassen, um das Becken zu sehen.', invalidState: 'Prüfe die Maße: Werte müssen positiv sein, der Bodengrund muss niedriger als das Becken sein und der Wechsel muss zwischen 0 und 100 Prozent liegen.', litreUnit: 'L', gallonUnit: 'gal', kilogramUnit: 'kg', poundUnit: 'lb', lengthUnitMetric: 'cm', lengthUnitImperial: 'Zoll', shapeHint: 'Verwende Innenmaße für eine bessere Schätzung.', bowHint: 'Die gebogene Front ist eine transparente Näherung.', substrateHint: 'Diese Schicht wird vom Bruttovolumen abgezogen.', resultHint: 'Das Ergebnis aktualisiert sich beim Eingeben.', approximationLabel: 'Bodengrundanteil', footprintLabel: 'Grundfläche', squareMetreUnit: 'm²', squareFootUnit: 'ft²', nano: 'Nano', compact: 'Kompakt', roomy: 'Groß', showcase: 'Sehr groß',
33
+ },
34
+ });
@@ -18,7 +18,7 @@ const faq = [
18
18
  },
19
19
  {
20
20
  question: 'Which imperial units are shown?',
21
- answer: 'Imperial mode shows US gallons for volume, pounds for water mass and inches for dimensions.',
21
+ answer: 'Imperial mode shows gallons for volume, pounds for water mass and inches for dimensions.',
22
22
  },
23
23
  {
24
24
  question: 'Does this recommend a water change schedule?',
@@ -41,7 +41,7 @@ const howTo = [
41
41
  },
42
42
  {
43
43
  name: 'Account for substrate',
44
- text: 'Enter the average substrate depth. The calculator subtracts that layer from the gross geometric volume.',
44
+ text: 'Enter the substrate depth. The calculator subtracts that layer from the gross geometric volume.',
45
45
  },
46
46
  {
47
47
  name: 'Set the water change percentage',
@@ -126,7 +126,7 @@ export const content: ToolLocaleContent<AquariumTankVolumeWaterChangeCalculatorU
126
126
  {
127
127
  type: 'tip',
128
128
  title: 'Measure inside the tank',
129
- html: '<p>Use internal dimensions and the average substrate depth. A ruler and a quick three-point average will usually be more useful than relying on a product box capacity.</p>',
129
+ html: '<p>Use internal dimensions. If the substrate slopes, measure its depth at a few points and enter the average instead of relying on a product box capacity.</p>',
130
130
  },
131
131
  {
132
132
  type: 'table',
@@ -140,8 +140,7 @@ export const content: ToolLocaleContent<AquariumTankVolumeWaterChangeCalculatorU
140
140
  },
141
141
  ],
142
142
  ui: {
143
- toolTitle: 'Tank volume lab',
144
- toolSubtitle: 'Shape, substrate and water change in one calm view.',
143
+ configureLabel: 'Configure the tank',
145
144
  dimensionsTitle: 'Tank dimensions',
146
145
  shapeLabel: 'Tank shape',
147
146
  rectangularShape: 'Rectangular',
@@ -152,12 +151,11 @@ export const content: ToolLocaleContent<AquariumTankVolumeWaterChangeCalculatorU
152
151
  heightLabel: 'Inside height',
153
152
  diameterLabel: 'Inside diameter',
154
153
  bowDepthLabel: 'Front curve depth',
155
- substrateLabel: 'Average substrate depth',
154
+ substrateLabel: 'Substrate depth',
156
155
  changeLabel: 'Water change',
157
156
  unitLabel: 'Units',
158
157
  metricLabel: 'Metric',
159
158
  imperialLabel: 'Imperial',
160
- calculateLabel: 'Calculate volume',
161
159
  resetLabel: 'Reset',
162
160
  grossVolumeLabel: 'Gross volume',
163
161
  substrateVolumeLabel: 'Substrate displacement',
@@ -167,7 +165,7 @@ export const content: ToolLocaleContent<AquariumTankVolumeWaterChangeCalculatorU
167
165
  emptyState: 'Adjust the dimensions to see the tank fill.',
168
166
  invalidState: 'Check the dimensions: use positive values, keep substrate below the tank height and keep the change between 0 and 100 percent.',
169
167
  litreUnit: 'L',
170
- gallonUnit: 'US gal',
168
+ gallonUnit: 'gal',
171
169
  kilogramUnit: 'kg',
172
170
  poundUnit: 'lb',
173
171
  lengthUnitMetric: 'cm',
@@ -178,6 +176,8 @@ export const content: ToolLocaleContent<AquariumTankVolumeWaterChangeCalculatorU
178
176
  resultHint: 'Your result updates locally as you type.',
179
177
  approximationLabel: 'Substrate share',
180
178
  footprintLabel: 'Footprint',
179
+ squareMetreUnit: 'm²',
180
+ squareFootUnit: 'ft²',
181
181
  nano: 'Nano',
182
182
  compact: 'Compact',
183
183
  roomy: 'Roomy',
@@ -0,0 +1,34 @@
1
+ import { createAquariumContent } from '../localized-content';
2
+
3
+ export const content = createAquariumContent({
4
+ locale: 'es',
5
+ slug: 'calculadora-volumen-acuario-cambio-agua',
6
+ title: 'Calculadora de volumen del acuario y cambio de agua',
7
+ description: 'Calcula el volumen útil del acuario, el espacio que ocupa el sustrato y los litros de un cambio de agua usando las medidas interiores del tanque.',
8
+ faq: [
9
+ { question: '¿Usa la capacidad comercial del acuario?', answer: 'No. Resta la altura del sustrato al volumen geométrico y ofrece una estimación del agua útil, no una capacidad oficial del fabricante.' },
10
+ { question: '¿Cómo calcula el frente curvo?', answer: 'Suma un segmento circular aproximado a la base rectangular. El resultado es orientativo porque cada perfil de cristal curvo puede ser distinto.' },
11
+ { question: '¿Qué unidades puedo usar?', answer: 'El modo métrico muestra centímetros, litros y kilogramos. El modo imperial muestra pulgadas, galones y libras.' },
12
+ { question: '¿Me recomienda cuándo hacer un cambio de agua?', answer: 'No. Solo calcula la cantidad correspondiente al porcentaje que introduces y no valora la calidad del agua ni la población del acuario.' },
13
+ { question: '¿Salen mis datos del navegador?', answer: 'No. El cálculo se ejecuta en el navegador y los últimos ajustes pueden guardarse localmente para recuperar el formulario.' },
14
+ ],
15
+ howTo: [
16
+ { name: 'Elige la forma del acuario', text: 'Selecciona rectangular, cilíndrico o frente curvo. Solo aparecerán las medidas necesarias para esa forma.' },
17
+ { name: 'Introduce las medidas interiores', text: 'Escribe largo, ancho y alto, o diámetro y alto para un cilindro. Las medidas interiores dan una estimación más útil.' },
18
+ { name: 'Indica la altura del sustrato', text: 'Mide el sustrato en varios puntos si está inclinado y escribe su altura media. Se resta del volumen bruto.' },
19
+ { name: 'Escribe el porcentaje del cambio', text: 'Introduce el porcentaje que quieres reemplazar. El resultado muestra los litros correspondientes y su peso aproximado.' },
20
+ ],
21
+ seo: [
22
+ { type: 'title', text: 'Calcula el agua que realmente tienes', level: 2 },
23
+ { type: 'paragraph', html: 'La capacidad que aparece en una caja suele representar el tanque lleno hasta arriba. Para dosificar, calentar o preparar un cambio de agua necesitas una cifra de trabajo: medidas interiores menos el espacio ocupado por el sustrato.' },
24
+ { type: 'title', text: 'Una fórmula para cada geometría', level: 2 },
25
+ { type: 'list', items: ['<strong>Rectangular:</strong> largo por ancho por alto.', '<strong>Cilíndrico:</strong> área de la base, pi por radio al cuadrado, por alto.', '<strong>Frente curvo:</strong> base rectangular más un segmento circular aproximado.'] },
26
+ { type: 'title', text: 'Mide el interior y revisa el sustrato', level: 2 },
27
+ { type: 'paragraph', html: 'Mide desde dentro del cristal. Si el sustrato forma una pendiente, toma varias medidas y usa la media. Para un acuario muy irregular, llenar con un recipiente de volumen conocido puede ser más fiable que una aproximación geométrica.' },
28
+ { type: 'tip', title: 'Usa el resultado como volumen de trabajo', html: '<p>El valor de cambio corresponde al porcentaje introducido. La temperatura, la salinidad y los objetos dentro del acuario pueden cambiar ligeramente la masa real del agua.</p>' },
29
+ { type: 'table', headers: ['Resultado', 'Qué significa'], rows: [['Volumen bruto', 'Volumen geométrico antes de restar el sustrato'], ['Desplazamiento del sustrato', 'Espacio estimado que ocupa la capa de fondo'], ['Volumen de agua útil', 'Estimación de trabajo después del sustrato'], ['Cambio de agua', 'Porcentaje introducido aplicado al volumen útil']] },
30
+ ],
31
+ ui: {
32
+ configureLabel: 'Configura el acuario', dimensionsTitle: 'Medidas del acuario', shapeLabel: 'Forma del acuario', rectangularShape: 'Rectangular', cylindricalShape: 'Cilíndrico', bowShape: 'Frente curvo', lengthLabel: 'Largo interior', widthLabel: 'Ancho interior', heightLabel: 'Alto interior', diameterLabel: 'Diámetro interior', bowDepthLabel: 'Profundidad de la curva frontal', substrateLabel: 'Altura del sustrato', changeLabel: 'Cambio de agua', unitLabel: 'Unidades', metricLabel: 'Métrico', imperialLabel: 'Imperial', resetLabel: 'Restablecer', grossVolumeLabel: 'Volumen bruto', substrateVolumeLabel: 'Desplazamiento del sustrato', netVolumeLabel: 'Volumen de agua útil', changeVolumeLabel: 'Agua a reemplazar', waterWeightLabel: 'Peso del agua', emptyState: 'Ajusta las medidas para ver el nivel.', invalidState: 'Revisa las medidas: usa valores positivos, mantén el sustrato por debajo del alto y el cambio entre 0 y 100 por ciento.', litreUnit: 'L', gallonUnit: 'gal', kilogramUnit: 'kg', poundUnit: 'lb', lengthUnitMetric: 'cm', lengthUnitImperial: 'pulg', shapeHint: 'Usa medidas interiores para obtener una estimación más útil.', bowHint: 'El frente curvo usa una aproximación transparente del segmento.', substrateHint: 'Esta capa se resta del volumen bruto.', resultHint: 'El resultado se actualiza mientras escribes.', approximationLabel: 'Parte de sustrato', footprintLabel: 'Base', squareMetreUnit: 'm²', squareFootUnit: 'ft²', nano: 'Nano', compact: 'Compacto', roomy: 'Amplio', showcase: 'Expositor',
33
+ },
34
+ });
@@ -0,0 +1,34 @@
1
+ import { createAquariumContent } from '../localized-content';
2
+
3
+ export const content = createAquariumContent({
4
+ locale: 'fr',
5
+ slug: 'calculateur-volume-aquarium-changement-eau',
6
+ title: "Calculateur du volume d'aquarium et du changement d'eau",
7
+ description: "Calculez le volume d'eau utile, le déplacement du substrat et la quantité d'eau à remplacer à partir des dimensions intérieures de votre aquarium.",
8
+ faq: [
9
+ { question: "Le calculateur utilise-t-il la capacité indiquée sur l'aquarium ?", answer: "Non. Il retire la hauteur du substrat du volume géométrique et fournit une estimation de l'eau réellement disponible." },
10
+ { question: "Comment fonctionne l'option façade bombée ?", answer: "Elle ajoute un segment circulaire approximatif à une base rectangulaire. Le résultat reste indicatif, car les profils de verre varient selon les aquariums." },
11
+ { question: 'Quelles unités sont disponibles ?', answer: "Le mode métrique affiche les centimètres, les litres et les kilogrammes. Le mode impérial affiche les pouces, les gallons et les livres." },
12
+ { question: 'Le calculateur conseille-t-il un calendrier de changement ?', answer: "Non. Il applique seulement le pourcentage saisi et n'évalue ni la qualité de l'eau ni la population du bac." },
13
+ { question: 'Mes données quittent-elles le navigateur ?', answer: "Non. Le calcul se fait dans le navigateur et les derniers réglages peuvent être conservés localement." },
14
+ ],
15
+ howTo: [
16
+ { name: "Choisir la forme de l'aquarium", text: "Sélectionnez une forme rectangulaire, cylindrique ou bombée. Seules les dimensions utiles à cette forme apparaîtront." },
17
+ { name: 'Saisir les dimensions intérieures', text: "Indiquez la longueur, la largeur et la hauteur, ou le diamètre et la hauteur pour un cylindre. Les mesures intérieures sont préférables." },
18
+ { name: 'Déduire le substrat', text: "Mesurez l'épaisseur moyenne du substrat et saisissez-la. Cette couche est retirée du volume brut." },
19
+ { name: "Définir le changement d'eau", text: "Saisissez le pourcentage à remplacer. Le résultat donne le volume correspondant et une masse d'eau approximative." },
20
+ ],
21
+ seo: [
22
+ { type: 'title', text: "Quel volume d'eau reste vraiment dans le bac ?", level: 2 },
23
+ { type: 'paragraph', html: "La capacité annoncée correspond souvent à un aquarium rempli à ras bord. Pour doser, chauffer ou préparer un changement, il faut plutôt connaître le volume utile: les mesures intérieures moins l'espace occupé par le substrat." },
24
+ { type: 'title', text: 'Une base adaptée à chaque forme', level: 2 },
25
+ { type: 'list', items: ['<strong>Rectangulaire :</strong> longueur multipliée par largeur et hauteur.', '<strong>Cylindrique :</strong> aire de la base, pi fois le rayon au carré, multipliée par la hauteur.', '<strong>Façade bombée :</strong> base rectangulaire plus un segment circulaire approximatif.'] },
26
+ { type: 'title', text: "Mesurer l'intérieur plutôt que l'emballage", level: 2 },
27
+ { type: 'paragraph', html: "Mesurez depuis l'intérieur du verre. Si le substrat est incliné, mesurez plusieurs points puis saisissez la moyenne. Pour une forme très irrégulière, remplir le bac avec un récipient de volume connu peut être plus précis qu'une approximation géométrique." },
28
+ { type: 'tip', title: "Interpréter le volume du changement", html: "<p>Le résultat correspond au pourcentage saisi du volume utile. La température, la salinité et les éléments présents dans le bac peuvent modifier légèrement la masse réelle de l'eau.</p>" },
29
+ { type: 'table', headers: ['Résultat', 'Signification'], rows: [['Volume brut', 'Volume géométrique avant le substrat'], ['Déplacement du substrat', 'Espace estimé occupé par la couche de fond'], ['Volume utile', "Estimation de l'eau après déduction du substrat"], ["Changement d'eau", "Pourcentage saisi appliqué au volume utile"]] },
30
+ ],
31
+ ui: {
32
+ configureLabel: "Configurer l'aquarium", dimensionsTitle: "Dimensions de l'aquarium", shapeLabel: "Forme de l'aquarium", rectangularShape: 'Rectangulaire', cylindricalShape: 'Cylindrique', bowShape: 'Façade bombée', lengthLabel: 'Longueur intérieure', widthLabel: 'Largeur intérieure', heightLabel: 'Hauteur intérieure', diameterLabel: 'Diamètre intérieur', bowDepthLabel: 'Profondeur de la courbe', substrateLabel: 'Épaisseur du substrat', changeLabel: "Changement d'eau", unitLabel: 'Unités', metricLabel: 'Métrique', imperialLabel: 'Impérial', resetLabel: 'Réinitialiser', grossVolumeLabel: 'Volume brut', substrateVolumeLabel: 'Déplacement du substrat', netVolumeLabel: 'Volume utile', changeVolumeLabel: 'Eau à remplacer', waterWeightLabel: "Masse de l'eau", emptyState: 'Ajustez les dimensions pour voir le niveau.', invalidState: 'Vérifiez les dimensions: utilisez des valeurs positives, gardez le substrat sous la hauteur du bac et choisissez un changement entre 0 et 100 pour cent.', litreUnit: 'L', gallonUnit: 'gal', kilogramUnit: 'kg', poundUnit: 'lb', lengthUnitMetric: 'cm', lengthUnitImperial: 'po', shapeHint: "Utilisez les dimensions intérieures pour une estimation plus utile.", bowHint: 'La façade bombée utilise une approximation transparente.', substrateHint: 'Cette couche est retirée del volume brut.', resultHint: "Le résultat se met à jour pendant la saisie.", approximationLabel: 'Part du substrat', footprintLabel: 'Base', squareMetreUnit: 'm²', squareFootUnit: 'pi²', nano: 'Nano', compact: 'Compact', roomy: 'Spacieux', showcase: 'Vitrine',
33
+ },
34
+ });
@@ -0,0 +1,34 @@
1
+ import { createAquariumContent } from '../localized-content';
2
+
3
+ export const content = createAquariumContent({
4
+ locale: 'id',
5
+ slug: 'kalkulator-volume-akuarium-pergantian-air',
6
+ title: 'Kalkulator Volume Akuarium dan Pergantian Air',
7
+ description: 'Hitung perkiraan volume air yang dapat digunakan, ruang yang ditempati substrat, dan jumlah air untuk pergantian berdasarkan ukuran bagian dalam akuarium.',
8
+ faq: [
9
+ { question: 'Apakah kalkulator memakai kapasitas yang tertulis di akuarium?', answer: 'Tidak. Kalkulator mengurangi tinggi substrat dari volume geometris sehingga hasilnya menjadi perkiraan volume air yang dapat digunakan.' },
10
+ { question: 'Bagaimana bentuk bagian depan melengkung dihitung?', answer: 'Kalkulator menambahkan segmen lingkaran perkiraan pada alas persegi panjang. Hasilnya bersifat perkiraan karena profil kaca dapat berbeda.' },
11
+ { question: 'Satuan apa yang tersedia?', answer: 'Mode metrik menampilkan sentimeter, liter, dan kilogram. Mode imperial menampilkan inci, galon, dan pon.' },
12
+ { question: 'Apakah kalkulator menentukan jadwal pergantian air?', answer: 'Tidak. Kalkulator hanya menerapkan persentase yang Anda masukkan dan tidak menilai kualitas air atau jumlah ikan.' },
13
+ { question: 'Apakah data saya dikirim keluar dari peramban?', answer: 'Tidak. Perhitungan berjalan di peramban dan pengaturan terakhir dapat disimpan secara lokal.' },
14
+ ],
15
+ howTo: [
16
+ { name: 'Pilih bentuk akuarium', text: 'Pilih bentuk persegi panjang, silinder, atau bagian depan melengkung. Hanya ukuran yang diperlukan untuk bentuk itu yang akan ditampilkan.' },
17
+ { name: 'Masukkan ukuran bagian dalam', text: 'Masukkan panjang, lebar, dan tinggi, atau diameter dan tinggi untuk silinder. Ukuran bagian dalam memberikan perkiraan yang lebih berguna.' },
18
+ { name: 'Masukkan tinggi substrat', text: 'Ukur tinggi substrat di beberapa titik bila permukaannya miring, lalu masukkan nilai rata-ratanya. Lapisan ini dikurangi dari volume kotor.' },
19
+ { name: 'Masukkan persentase pergantian', text: 'Masukkan persentase air yang ingin diganti. Hasilnya menunjukkan volume dan perkiraan massa air.' },
20
+ ],
21
+ seo: [
22
+ { type: 'title', text: 'Hitung air yang benar-benar tersedia', level: 2 },
23
+ { type: 'paragraph', html: 'Kapasitas pada kotak biasanya menggambarkan akuarium yang diisi sampai penuh. Untuk menentukan dosis, pemanas, atau jumlah air yang harus diganti, gunakan volume kerja: ukuran bagian dalam dikurangi ruang yang ditempati substrat.' },
24
+ { type: 'title', text: 'Gunakan rumus sesuai bentuknya', level: 2 },
25
+ { type: 'list', items: ['<strong>Persegi panjang:</strong> panjang dikalikan lebar dan tinggi.', '<strong>Silinder:</strong> luas alas, pi dikalikan kuadrat jari-jari, dikalikan tinggi.', '<strong>Bagian depan melengkung:</strong> alas persegi panjang ditambah segmen lingkaran perkiraan.'] },
26
+ { type: 'title', text: 'Ukur bagian dalam dan substrat', level: 2 },
27
+ { type: 'paragraph', html: 'Ukur dari sisi dalam kaca. Jika substrat memiliki kemiringan, ambil beberapa pengukuran dan masukkan nilai rata-rata. Untuk akuarium dengan bentuk sangat tidak beraturan, mengisinya memakai wadah bervolume diketahui dapat lebih akurat daripada pendekatan geometris.' },
28
+ { type: 'tip', title: 'Gunakan hasil sebagai volume kerja', html: '<p>Volume pergantian mengikuti persentase yang Anda masukkan. Suhu, kadar garam, dan benda di dalam akuarium dapat mengubah massa air sebenarnya sedikit.</p>' },
29
+ { type: 'table', headers: ['Hasil', 'Artinya'], rows: [['Volume kotor', 'Volume geometris sebelum substrat dikurangi'], ['Perpindahan substrat', 'Perkiraan ruang yang ditempati lapisan dasar'], ['Volume air bersih', 'Perkiraan volume kerja setelah substrat dikurangi'], ['Pergantian air', 'Persentase yang dimasukkan dari volume air bersih']] },
30
+ ],
31
+ ui: {
32
+ configureLabel: 'Atur akuarium', dimensionsTitle: 'Ukuran akuarium', shapeLabel: 'Bentuk akuarium', rectangularShape: 'Persegi panjang', cylindricalShape: 'Silinder', bowShape: 'Depan melengkung', lengthLabel: 'Panjang bagian dalam', widthLabel: 'Lebar bagian dalam', heightLabel: 'Tinggi bagian dalam', diameterLabel: 'Diameter bagian dalam', bowDepthLabel: 'Kedalaman lengkungan depan', substrateLabel: 'Tinggi substrat', changeLabel: 'Pergantian air', unitLabel: 'Satuan', metricLabel: 'Metrik', imperialLabel: 'Imperial', resetLabel: 'Atur ulang', grossVolumeLabel: 'Volume kotor', substrateVolumeLabel: 'Perpindahan substrat', netVolumeLabel: 'Volume air bersih', changeVolumeLabel: 'Air yang diganti', waterWeightLabel: 'Massa air', emptyState: 'Sesuaikan ukuran untuk melihat permukaan air.', invalidState: 'Periksa ukuran: gunakan nilai positif, jaga substrat di bawah tinggi akuarium, dan masukkan pergantian antara 0 dan 100 persen.', litreUnit: 'L', gallonUnit: 'gal', kilogramUnit: 'kg', poundUnit: 'lb', lengthUnitMetric: 'cm', lengthUnitImperial: 'inci', shapeHint: 'Gunakan ukuran bagian dalam untuk perkiraan yang lebih berguna.', bowHint: 'Bagian depan melengkung memakai pendekatan segmen transparan.', substrateHint: 'Lapisan ini dikurangi dari volume kotor.', resultHint: 'Hasil diperbarui saat Anda mengetik.', approximationLabel: 'Bagian substrat', footprintLabel: 'Alas', squareMetreUnit: 'm²', squareFootUnit: 'ft²', nano: 'Nano', compact: 'Kompak', roomy: 'Luas', showcase: 'Pajangan',
33
+ },
34
+ });
@@ -0,0 +1,34 @@
1
+ import { createAquariumContent } from '../localized-content';
2
+
3
+ export const content = createAquariumContent({
4
+ locale: 'it',
5
+ slug: 'calcolatore-volume-acquario-cambio-acqua',
6
+ title: "Calcolatore del volume dell'acquario e del cambio d'acqua",
7
+ description: "Calcola il volume d'acqua utile, lo spazio occupato dal substrato e la quantità da sostituire usando le misure interne del tuo acquario.",
8
+ faq: [
9
+ { question: "Il calcolatore usa la capacità indicata sulla confezione?", answer: "No. Sottrae l'altezza del substrato dal volume geometrico e fornisce una stima dell'acqua effettivamente disponibile." },
10
+ { question: "Come funziona l'opzione con fronte curvo?", answer: "Aggiunge un segmento circolare approssimato alla base rettangolare. Il risultato è indicativo perché i profili del vetro curvo cambiano da una vasca all'altra." },
11
+ { question: 'Quali unità sono disponibili?', answer: "La modalità metrica mostra centimetri, litri e chilogrammi. La modalità imperiale mostra pollici, galloni e libbre." },
12
+ { question: 'Il calcolatore consiglia un programma per il cambio?', answer: "No. Applica solo la percentuale inserita e non valuta la qualità dell'acqua o la popolazione della vasca." },
13
+ { question: 'I miei dati lasciano il browser?', answer: "No. Il calcolo avviene nel browser e le ultime impostazioni possono essere conservate localmente." },
14
+ ],
15
+ howTo: [
16
+ { name: "Scegliere la forma dell'acquario", text: "Seleziona una vasca rettangolare, cilindrica o con fronte curvo. Compariranno solo le misure necessarie per quella forma." },
17
+ { name: 'Inserire le misure interne', text: "Inserisci lunghezza, larghezza e altezza, oppure diametro e altezza per un cilindro. Le misure interne sono la base più utile." },
18
+ { name: 'Considerare il substrato', text: "Misura l'altezza media del substrato e inseriscila. Questo strato viene sottratto dal volume lordo." },
19
+ { name: "Impostare la percentuale d'acqua", text: "Inserisci la percentuale che vuoi sostituire. Il risultato mostra il volume corrispondente e la massa approssimativa dell'acqua." },
20
+ ],
21
+ seo: [
22
+ { type: 'title', text: "Quanta acqua c'è davvero nella vasca?", level: 2 },
23
+ { type: 'paragraph', html: "La capacità commerciale descrive spesso la vasca piena fino all'orlo. Per dosare, riscaldare o preparare un cambio d'acqua serve il volume utile: le misure interne meno lo spazio occupato dal substrato." },
24
+ { type: 'title', text: 'La base giusta per ogni forma', level: 2 },
25
+ { type: 'list', items: ['<strong>Rettangolare:</strong> lunghezza per larghezza per altezza.', '<strong>Cilindrica:</strong> area della base, pi greco per il raggio al quadrato, per altezza.', '<strong>Fronte curvo:</strong> base rettangolare più un segmento circolare approssimato.'] },
26
+ { type: 'title', text: "Misurare l'interno, non la scatola", level: 2 },
27
+ { type: 'paragraph', html: "Misura dall'interno del vetro. Se il substrato è inclinato, misura più punti e inserisci la media. Per forme molto irregolari, riempire la vasca con un recipiente di volume noto può essere più preciso di un'approssimazione geometrica." },
28
+ { type: 'tip', title: 'Leggere il volume del cambio', html: "<p>Il risultato applica la percentuale scelta al volume utile. Temperatura, salinità e oggetti presenti nella vasca possono modificare leggermente la massa reale dell'acqua.</p>" },
29
+ { type: 'table', headers: ['Risultato', 'Significato'], rows: [['Volume lordo', 'Volume geometrico prima del substrato'], ['Spostamento del substrato', 'Spazio stimato occupato dallo strato di fondo'], ['Volume utile', "Stima dell'acqua dopo aver sottratto il substrato"], ["Cambio d'acqua", 'Percentuale inserita applicata al volume utile']] },
30
+ ],
31
+ ui: {
32
+ configureLabel: "Configura l'acquario", dimensionsTitle: "Misure dell'acquario", shapeLabel: "Forma dell'acquario", rectangularShape: 'Rettangolare', cylindricalShape: 'Cilindrica', bowShape: 'Fronte curvo', lengthLabel: 'Lunghezza interna', widthLabel: 'Larghezza interna', heightLabel: 'Altezza interna', diameterLabel: 'Diametro interno', bowDepthLabel: 'Profondità della curva', substrateLabel: 'Altezza del substrato', changeLabel: "Cambio d'acqua", unitLabel: 'Unità', metricLabel: 'Metrico', imperialLabel: 'Imperiale', resetLabel: 'Ripristina', grossVolumeLabel: 'Volume lordo', substrateVolumeLabel: 'Spostamento del substrato', netVolumeLabel: 'Volume utile', changeVolumeLabel: "Acqua da sostituire", waterWeightLabel: "Massa dell'acqua", emptyState: 'Modifica le misure per vedere il livello.', invalidState: "Controlla le misure: usa valori positivi, mantieni il substrato sotto l'altezza della vasca e scegli un cambio tra 0 e 100 percento.", litreUnit: 'L', gallonUnit: 'gal', kilogramUnit: 'kg', poundUnit: 'lb', lengthUnitMetric: 'cm', lengthUnitImperial: 'pol', shapeHint: "Usa le misure interne per una stima più utile.", bowHint: 'Il fronte curvo usa un segmento circolare approssimato.', substrateHint: 'Questo strato viene sottratto dal volume lordo.', resultHint: "Il risultato si aggiorna mentre scrivi.", approximationLabel: 'Quota del substrato', footprintLabel: 'Base', squareMetreUnit: 'm²', squareFootUnit: 'ft²', nano: 'Nano', compact: 'Compatto', roomy: 'Spazioso', showcase: 'Vetrina',
33
+ },
34
+ });
@@ -0,0 +1,34 @@
1
+ import { createAquariumContent } from '../localized-content';
2
+
3
+ export const content = createAquariumContent({
4
+ locale: 'ja',
5
+ slug: 'aquarium-tank-volume-water-change-calculator',
6
+ title: '水槽の容量と水換え量計算機',
7
+ description: '水槽の内寸と底床の厚さから、実際に使える水量、水換え量、水の重さを計算します。メートル法とヤード・ポンド法に対応しています。',
8
+ faq: [
9
+ { question: '市販の水槽容量をそのまま使いますか?', answer: 'いいえ。水槽の幾何学的な容量から底床が占める分を引き、実際に使える水量の目安を表示します。' },
10
+ { question: '前面が曲面の水槽はどう計算しますか?', answer: '長方形の底面に円形部分の近似を加えます。ガラスの曲線は製品ごとに異なるため、結果は目安です。' },
11
+ { question: 'どの単位を使えますか?', answer: 'メートル法ではセンチメートル、リットル、キログラムを表示し、ヤード・ポンド法ではインチ、ガロン、ポンドを表示します。' },
12
+ { question: '水換えの頻度も提案しますか?', answer: 'いいえ。入力した割合に対応する量だけを計算し、水質や生体数を判断するものではありません。' },
13
+ { question: '入力データは外部へ送信されますか?', answer: 'いいえ。計算はブラウザ内で行われ、最後の設定はこのブラウザにだけ保存できます。' },
14
+ ],
15
+ howTo: [
16
+ { name: '水槽の形を選ぶ', text: '長方形、円筒形、前面曲面から選びます。選んだ形に必要な入力だけが表示されます。' },
17
+ { name: '内寸を入力する', text: '長さ、幅、高さ、または円筒形の直径と高さを入力します。できるだけガラスの内側を測ってください。' },
18
+ { name: '底床の厚さを入力する', text: '傾斜した底床は数か所で測り、平均の厚さを入力します。その分が総容量から差し引かれます。' },
19
+ { name: '水換え割合を入力する', text: '交換したい割合を入力します。対応する水量と水の重さの目安が表示されます。' },
20
+ ],
21
+ seo: [
22
+ { type: 'title', text: '水槽に実際に入る水量を知る', level: 2 },
23
+ { type: 'paragraph', html: '商品に表示された容量は、水槽を満杯にしたときの値であることが多くあります。水換えや添加剤の量を決めるときは、内寸から底床が占める空間を引いた実用的な水量が役立ちます。' },
24
+ { type: 'title', text: '形状に合わせた計算', level: 2 },
25
+ { type: 'list', items: ['<strong>長方形:</strong> 長さ×幅×高さで計算します。', '<strong>円筒形:</strong> 半径の二乗に円周率と高さを掛けます。', '<strong>前面曲面:</strong> 長方形の底面に曲面部分の近似を加えます。'] },
26
+ { type: 'title', text: '外寸や箱の容量に頼らない', level: 2 },
27
+ { type: 'paragraph', html: 'ガラスの内側を測定してください。底床が傾斜している場合は複数箇所を測って平均を使います。形が大きく不規則な場合は、既知の容量の容器で実際に満たす方法が幾何学的な近似より正確なことがあります。' },
28
+ { type: 'tip', title: '水換え量の読み方', html: '<p>表示される交換量は、入力した割合を実用水量に適用した値です。水温や塩分、流木などの内部物は実際の水の重さを少し変えることがあります。</p>' },
29
+ { type: 'table', headers: ['表示', '意味'], rows: [['総容量', '底床を差し引く前の幾何学的な容量'], ['底床の排除量', '底床が占めると見積もった空間'], ['実用水量', '底床を差し引いた作業用の水量'], ['水換え量', '実用水量に入力割合を掛けた量']] },
30
+ ],
31
+ ui: {
32
+ configureLabel: '水槽を設定', dimensionsTitle: '水槽の寸法', shapeLabel: '水槽の形', rectangularShape: '長方形', cylindricalShape: '円筒形', bowShape: '前面曲面', lengthLabel: '内側の長さ', widthLabel: '内側の幅', heightLabel: '内側の高さ', diameterLabel: '内側の直径', bowDepthLabel: '曲面の奥行き', substrateLabel: '底床の厚さ', changeLabel: '水換え', unitLabel: '単位', metricLabel: 'メートル法', imperialLabel: 'ヤード・ポンド法', resetLabel: 'リセット', grossVolumeLabel: '総容量', substrateVolumeLabel: '底床の排除量', netVolumeLabel: '実用水量', changeVolumeLabel: '交換する水量', waterWeightLabel: '水の重さ', emptyState: '寸法を調整すると水位が表示されます。', invalidState: '寸法を確認してください。正の値を使い、底床を水槽の高さ未満にし、水換え割合を0から100パーセントにしてください。', litreUnit: 'L', gallonUnit: 'gal', kilogramUnit: 'kg', poundUnit: 'lb', lengthUnitMetric: 'cm', lengthUnitImperial: 'in', shapeHint: '内寸を使うと、より実用的な目安になります。', bowHint: '前面曲面は透明な近似計算です。', substrateHint: 'この厚さを総容量から差し引きます。', resultHint: '入力すると結果が更新されます。', approximationLabel: '底床の割合', footprintLabel: '底面積', squareMetreUnit: 'm²', squareFootUnit: 'ft²', nano: '小型', compact: '標準', roomy: '大型', showcase: '特大',
33
+ },
34
+ });