@jjlmoya/utils-aquarium 1.65.0 → 1.67.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 (72) hide show
  1. package/package.json +1 -1
  2. package/src/category/index.ts +3 -1
  3. package/src/entries.ts +3 -1
  4. package/src/index.ts +2 -0
  5. package/src/layouts/ProductionCategoryPage.astro +184 -184
  6. package/src/mfe/category-ui.ts +34 -34
  7. package/src/tests/qa-test-helpers.ts +32 -0
  8. package/src/tests/qa_bibliography_links.test.ts +48 -0
  9. package/src/tests/qa_claim_evidence.test.ts +69 -0
  10. package/src/tests/qa_logic_reference_coverage.test.ts +46 -0
  11. package/src/tests/qa_runtime_i18n.test.ts +138 -0
  12. package/src/tests/tool_validation.test.ts +5 -3
  13. package/src/tool/aquariumCalciumAlkalinityDosingCalculator/aquarium-calcium-alkalinity-dosing-calculator.css +611 -0
  14. package/src/tool/aquariumCalciumAlkalinityDosingCalculator/bibliography.astro +7 -0
  15. package/src/tool/aquariumCalciumAlkalinityDosingCalculator/bibliography.ts +5 -0
  16. package/src/tool/aquariumCalciumAlkalinityDosingCalculator/component.astro +93 -0
  17. package/src/tool/aquariumCalciumAlkalinityDosingCalculator/controller.ts +140 -0
  18. package/src/tool/aquariumCalciumAlkalinityDosingCalculator/dom-views.ts +246 -0
  19. package/src/tool/aquariumCalciumAlkalinityDosingCalculator/entry.ts +27 -0
  20. package/src/tool/aquariumCalciumAlkalinityDosingCalculator/i18n/de.ts +2 -0
  21. package/src/tool/aquariumCalciumAlkalinityDosingCalculator/i18n/en.ts +3 -0
  22. package/src/tool/aquariumCalciumAlkalinityDosingCalculator/i18n/es.ts +2 -0
  23. package/src/tool/aquariumCalciumAlkalinityDosingCalculator/i18n/fr.ts +2 -0
  24. package/src/tool/aquariumCalciumAlkalinityDosingCalculator/i18n/id.ts +2 -0
  25. package/src/tool/aquariumCalciumAlkalinityDosingCalculator/i18n/it.ts +2 -0
  26. package/src/tool/aquariumCalciumAlkalinityDosingCalculator/i18n/ja.ts +2 -0
  27. package/src/tool/aquariumCalciumAlkalinityDosingCalculator/i18n/ko.ts +2 -0
  28. package/src/tool/aquariumCalciumAlkalinityDosingCalculator/i18n/nl.ts +2 -0
  29. package/src/tool/aquariumCalciumAlkalinityDosingCalculator/i18n/pl.ts +2 -0
  30. package/src/tool/aquariumCalciumAlkalinityDosingCalculator/i18n/pt.ts +2 -0
  31. package/src/tool/aquariumCalciumAlkalinityDosingCalculator/i18n/ru.ts +2 -0
  32. package/src/tool/aquariumCalciumAlkalinityDosingCalculator/i18n/shared.ts +63 -0
  33. package/src/tool/aquariumCalciumAlkalinityDosingCalculator/i18n/sv.ts +2 -0
  34. package/src/tool/aquariumCalciumAlkalinityDosingCalculator/i18n/tr.ts +2 -0
  35. package/src/tool/aquariumCalciumAlkalinityDosingCalculator/i18n/translations.ts +155 -0
  36. package/src/tool/aquariumCalciumAlkalinityDosingCalculator/i18n/zh.ts +2 -0
  37. package/src/tool/aquariumCalciumAlkalinityDosingCalculator/index.ts +11 -0
  38. package/src/tool/aquariumCalciumAlkalinityDosingCalculator/logic.test.ts +33 -0
  39. package/src/tool/aquariumCalciumAlkalinityDosingCalculator/logic.ts +112 -0
  40. package/src/tool/aquariumCalciumAlkalinityDosingCalculator/seo.astro +11 -0
  41. package/src/tool/aquariumCalciumAlkalinityDosingCalculator/ui.ts +44 -0
  42. package/src/tool/aquariumCalciumAlkalinityDosingCalculator/view-helpers.ts +68 -0
  43. package/src/tool/aquariumSaltDosingCalculator/aquarium-salt-dosing-calculator.css +503 -0
  44. package/src/tool/aquariumSaltDosingCalculator/bibliography.astro +6 -0
  45. package/src/tool/aquariumSaltDosingCalculator/bibliography.ts +5 -0
  46. package/src/tool/aquariumSaltDosingCalculator/component.astro +36 -0
  47. package/src/tool/aquariumSaltDosingCalculator/controller.ts +104 -0
  48. package/src/tool/aquariumSaltDosingCalculator/dom-views.ts +74 -0
  49. package/src/tool/aquariumSaltDosingCalculator/entry.ts +13 -0
  50. package/src/tool/aquariumSaltDosingCalculator/i18n/de.ts +2 -0
  51. package/src/tool/aquariumSaltDosingCalculator/i18n/en.ts +2 -0
  52. package/src/tool/aquariumSaltDosingCalculator/i18n/es.ts +2 -0
  53. package/src/tool/aquariumSaltDosingCalculator/i18n/fr.ts +2 -0
  54. package/src/tool/aquariumSaltDosingCalculator/i18n/id.ts +2 -0
  55. package/src/tool/aquariumSaltDosingCalculator/i18n/it.ts +2 -0
  56. package/src/tool/aquariumSaltDosingCalculator/i18n/ja.ts +2 -0
  57. package/src/tool/aquariumSaltDosingCalculator/i18n/ko.ts +2 -0
  58. package/src/tool/aquariumSaltDosingCalculator/i18n/nl.ts +2 -0
  59. package/src/tool/aquariumSaltDosingCalculator/i18n/pl.ts +2 -0
  60. package/src/tool/aquariumSaltDosingCalculator/i18n/pt.ts +2 -0
  61. package/src/tool/aquariumSaltDosingCalculator/i18n/ru.ts +2 -0
  62. package/src/tool/aquariumSaltDosingCalculator/i18n/sv.ts +2 -0
  63. package/src/tool/aquariumSaltDosingCalculator/i18n/tr.ts +2 -0
  64. package/src/tool/aquariumSaltDosingCalculator/i18n/zh.ts +2 -0
  65. package/src/tool/aquariumSaltDosingCalculator/index.ts +11 -0
  66. package/src/tool/aquariumSaltDosingCalculator/localized-content.ts +151 -0
  67. package/src/tool/aquariumSaltDosingCalculator/logic.test.ts +46 -0
  68. package/src/tool/aquariumSaltDosingCalculator/logic.ts +85 -0
  69. package/src/tool/aquariumSaltDosingCalculator/seo.astro +11 -0
  70. package/src/tool/aquariumSaltDosingCalculator/storage.ts +19 -0
  71. package/src/tool/aquariumSaltDosingCalculator/ui.ts +47 -0
  72. package/src/tools.ts +12 -0
@@ -0,0 +1,155 @@
1
+ import type { SEOSection } from '../../../types';
2
+ import { buildContent, type LocalizedCopy } from './shared';
3
+ import type { AquariumCalciumAlkalinityDosingCalculatorUI } from '../ui';
4
+
5
+ const englishUi: AquariumCalciumAlkalinityDosingCalculatorUI = {
6
+ toolKicker: '01 / Add supplement', resultTitle: 'Add supplement', configureTitle: '02 / Calcium or alkalinity', parameterLabel: 'Calcium / alkalinity',
7
+ calciumLabel: 'Calcium', alkalinityLabel: 'Alkalinity', calciumShortLabel: 'Ca', alkalinityShortLabel: 'Alk',
8
+ volumeLabel: 'Working water volume', currentLabel: 'Current test result', targetLabel: 'Target result', productStrengthLabel: 'Product strength on label', productStrengthHint: 'How much one dose unit raises the selected parameter in 100 L.',
9
+ doseUnitLabel: 'Dose unit', millilitresLabel: 'mL', gramsLabel: 'g', volumeUnitLabel: 'Volume unit', litresLabel: 'Litres', gallonsLabel: 'US gallons',
10
+ resultActionLabel: 'Action', addAction: 'Add supplement gradually', reduceAction: 'Plan a reduction', targetAction: 'No correction needed', emptyState: 'Enter your own aquarium values to see the action.', doseLabel: 'Estimated supplement', waterChangeLabel: 'Estimated water change', currentTargetLabel: 'Current → target', differenceLabel: 'Difference', unitCalcium: 'ppm Ca', unitAlkalinity: 'dKH', unitPercent: '%',
11
+ addSummary: 'This is the theoretical amount from your label strength. Split the correction when your method or product instructions call for it.', reduceSummary: 'The target is lower than the test result. The percentage uses a simple dilution assumption; choose the water-change method and retest yourself.', targetSummary: 'Your current result matches the target entered. Keep testing on your normal schedule.', verificationNote: 'Estimate only. Product labels, test accuracy, water volume and aquarium demand matter. Retest before repeating a correction.', sceneAdd: 'Measured dose entering the aquarium', sceneReduce: 'Water-change decision', sceneTarget: 'Target reached', invalidState: 'Check the values and keep them within the displayed ranges.', resetLabel: 'Reset example', localOnlyLabel: 'Runs in your browser',
12
+ };
13
+
14
+ function ui(changes: Partial<AquariumCalciumAlkalinityDosingCalculatorUI>): AquariumCalciumAlkalinityDosingCalculatorUI {
15
+ return { ...englishUi, ...changes } as AquariumCalciumAlkalinityDosingCalculatorUI;
16
+ }
17
+
18
+ function seo(firstTitle: string, firstParagraph: string, secondTitle: string, secondParagraph: string, thirdTitle: string, thirdParagraph: string, tipTitle: string, tip: string, list: string[], rows: string[][]): SEOSection[] {
19
+ return [
20
+ { type: 'title', text: firstTitle, level: 2 }, { type: 'paragraph', html: firstParagraph },
21
+ { type: 'title', text: secondTitle, level: 2 }, { type: 'paragraph', html: secondParagraph },
22
+ { type: 'list', items: list }, { type: 'title', text: thirdTitle, level: 2 }, { type: 'paragraph', html: thirdParagraph },
23
+ { type: 'tip', title: tipTitle, html: `<p>${tip}</p>` }, { type: 'table', headers: ['Output', 'Meaning'], rows },
24
+ ];
25
+ }
26
+
27
+ const bibliography = (names: string[]) => [
28
+ { name: names[0]!, url: 'https://oceanservice.noaa.gov/education/tutorial_chemistry/02oceanchemistry.html' },
29
+ { name: names[1]!, url: 'https://www.usgs.gov/special-topics/water-science-school/science/hardness-water' },
30
+ { name: names[2]!, url: 'https://www.fao.org/4/x5685s/x5685s00.htm' },
31
+ ];
32
+
33
+ const en: LocalizedCopy = {
34
+ language: 'en', slug: 'aquarium-calcium-alkalinity-dosing-calculator', title: 'Aquarium Calcium and Alkalinity Dosing Calculator', description: 'Calculate a theoretical calcium or alkalinity supplement dose from your aquarium volume, test result, target and the strength printed on your own product label.',
35
+ ui: englishUi,
36
+ seo: seo('A dose that starts with your own label', 'Calcium and alkalinity supplements are not interchangeable. This calculator uses your aquarium test result, working water volume and the response printed on your own product label.', 'Calcium and alkalinity use different scales', 'Choose calcium for ppm or alkalinity for dKH, then enter the increase caused by one dose unit in 100 litres.', 'When the target is lower', 'A supplement cannot lower a test result. In that case, the tool switches to a reduction decision and a simple water-change estimate.', 'Measure before you repeat', 'Follow the product label, split large corrections when appropriate, and retest before another dose.', ['<strong>Use working volume:</strong> count the water actually in the aquarium.', '<strong>Copy the exact label rate:</strong> use the product you will dose.', '<strong>Keep units consistent:</strong> ppm for calcium and dKH for alkalinity.', '<strong>Retest after a correction:</strong> do not chain estimates.'], [['Estimated supplement', 'Theoretical amount when the target is higher'], ['Estimated water change', 'Simple reduction estimate when the target is lower'], ['Current → target', 'The gap the calculation is trying to close'], ['Difference', 'The selected change in ppm or dKH']]),
37
+ faq: [{ question: 'What product strength should I enter?', answer: 'Enter the response stated on your exact product label: how many ppm of calcium or dKH of alkalinity one dose unit raises in 100 litres.' }, { question: 'Can this calculator choose a universal dose?', answer: 'No. Supplements differ in concentration, so the calculator requires the rate for the product you actually use.' }, { question: 'What if my test result is already above the target?', answer: 'The result changes to a reduction decision and estimates a water-change percentage using a simple dilution model.' }, { question: 'Should I add the full amount at once?', answer: 'Treat the result as a starting estimate, follow the product instructions and split large corrections when appropriate.' }],
38
+ howTo: [{ name: 'Choose the parameter', text: 'Select calcium for a ppm test or alkalinity for a dKH test.' }, { name: 'Enter your measured values', text: 'Add the working water volume, current test result and target.' }, { name: 'Read the product label', text: 'Enter how much one millilitre or gram raises the parameter in 100 litres.' }, { name: 'Use the action and retest', text: 'Use the estimate as a starting point, follow the label and retest before another correction.' }], bibliography: bibliography(['NOAA Ocean Service, Ocean acidification and seawater chemistry', 'USGS Water Science School, Hardness of water', 'FAO, Manual de acuicultura en español']),
39
+ };
40
+
41
+ const de: LocalizedCopy = {
42
+ language: 'de', slug: 'aquarium-calcium-alkalinitats-dosierrechner', title: 'Aquarium Rechner für Calcium und Alkalinitätsdosierung', description: 'Berechne eine theoretische Dosierung für Calcium oder Alkalinität aus Wasservolumen, Messwert, Zielwert und dem Etikett deines Produkts.',
43
+ ui: ui({ toolKicker: '01 / Supplement dosieren', resultTitle: 'Supplement dosieren', configureTitle: '02 / Calcium oder Alkalinität', parameterLabel: 'Calcium / Alkalinität', calciumLabel: 'Calcium', alkalinityLabel: 'Alkalinität', volumeLabel: 'Arbeitsvolumen Wasser', currentLabel: 'Aktueller Messwert', targetLabel: 'Zielwert', productStrengthLabel: 'Produktstärke laut Etikett', productStrengthHint: 'Wie stark eine Dosiereinheit den gewählten Wert in 100 L erhöht.', doseUnitLabel: 'Dosiereinheit', volumeUnitLabel: 'Volumeneinheit', litresLabel: 'Liter', gallonsLabel: 'US-Gallonen', resultActionLabel: 'Aktion', addAction: 'Supplement schrittweise zugeben', reduceAction: 'Reduktion planen', targetAction: 'Keine Korrektur nötig', emptyState: 'Gib deine Aquarienwerte ein, um die Aktion zu sehen.', doseLabel: 'Geschätztes Supplement', waterChangeLabel: 'Geschätzter Wasserwechsel', currentTargetLabel: 'Aktuell → Ziel', differenceLabel: 'Differenz', unitCalcium: 'ppm Ca', unitAlkalinity: 'dKH', addSummary: 'Dies ist die theoretische Menge aus deiner Etikettangabe. Teile die Korrektur nach Methode oder Produktanleitung auf.', reduceSummary: 'Der Zielwert liegt unter dem Messwert. Der Prozentsatz nutzt eine einfache Verdünnungsannahme; wähle die Methode selbst und miss erneut.', targetSummary: 'Der aktuelle Wert entspricht dem eingegebenen Ziel. Teste nach deinem normalen Rhythmus weiter.', verificationNote: 'Nur eine Schätzung. Etikett, Messgenauigkeit, Wasservolumen und Aquarienverbrauch sind entscheidend. Vor einer Wiederholung erneut messen.', sceneAdd: 'Gemessene Dosis gelangt ins Aquarium', sceneReduce: 'Entscheidung zum Wasserwechsel', sceneTarget: 'Ziel erreicht', invalidState: 'Prüfe die Werte und halte die angezeigten Grenzen ein.', resetLabel: 'Beispiel zurücksetzen', localOnlyLabel: 'Läuft im Browser' }),
44
+ seo: seo('Plane die nächste Korrektur mit deinem Produktetikett', 'Calcium- und Alkalinitätspräparate sind nicht austauschbar. Dieser Rechner nutzt deinen Messwert, das Arbeitsvolumen und die Reaktion auf dem Etikett deines eigenen Produkts.', 'Calcium und Alkalinität haben unterschiedliche Skalen', 'Wähle Calcium für ppm oder Alkalinität für dKH. Trage ein, wie stark eine Dosiereinheit den Wert in 100 Litern erhöht.', 'Wenn der Zielwert niedriger ist', 'Ein Supplement kann einen Messwert nicht senken. Dann zeigt der Rechner eine einfache Wasserwechsel-Schätzung, die vom Wasser ohne zusätzlichen Parameter ausgeht.', 'Vor der nächsten Korrektur messen', 'Folge der Produktanleitung, korrigiere große Abstände schrittweise und miss vor einer weiteren Korrektur erneut.', ['<strong>Arbeitsvolumen verwenden:</strong> Zähle das tatsächlich vorhandene Wasser.', '<strong>Etikett exakt übernehmen:</strong> Nutze die Rate für dein konkretes Produkt.', '<strong>Einheiten beibehalten:</strong> ppm für Calcium und dKH für Alkalinität.', '<strong>Nach jeder Korrektur messen:</strong> Verkette keine Schätzungen.'], [['Geschätztes Supplement', 'Theoretische Menge bei höherem Zielwert'], ['Geschätzter Wasserwechsel', 'Einfache Reduktionsschätzung bei niedrigerem Zielwert'], ['Aktuell → Ziel', 'Die zu schließende Lücke'], ['Differenz', 'Die Änderung in ppm oder dKH']]),
45
+ faq: [{ question: 'Welche Produktstärke trage ich ein?', answer: 'Übernimm die Reaktion vom Etikett deines konkreten Produkts: wie viel ppm Calcium oder dKH eine Dosiereinheit in 100 Litern erhöht.' }, { question: 'Kann der Rechner eine allgemeine Dosis empfehlen?', answer: 'Nein. Produkte unterscheiden sich. Der Rechner verlangt absichtlich deine eigene Etikettangabe.' }, { question: 'Was passiert, wenn mein Wert über dem Ziel liegt?', answer: 'Der Rechner wechselt zu einer Reduktionsentscheidung und schätzt einen Wasserwechsel mit einer einfachen Verdünnungsannahme.' }, { question: 'Soll ich die gesamte Menge sofort zugeben?', answer: 'Behandle das Ergebnis als Startschätzung, folge der Anleitung und korrigiere größere Abstände schrittweise.' }],
46
+ howTo: [{ name: 'Parameter wählen', text: 'Wähle Calcium für einen ppm-Test oder Alkalinität für einen dKH-Test.' }, { name: 'Messwerte eingeben', text: 'Trage Arbeitsvolumen, aktuellen Messwert und Zielwert ein.' }, { name: 'Etikett ablesen', text: 'Gib an, wie stark eine Milliliter- oder Grammdosis den Wert in 100 Litern erhöht.' }, { name: 'Aktion ausführen und messen', text: 'Nutze die Schätzung als Startpunkt, folge der Produktanleitung und teste vor der nächsten Korrektur erneut.' }], bibliography: bibliography(['NOAA Ocean Service, Versauerung der Meere und Meerwasserchemie', 'USGS Water Science School, Wasserhärte', 'FAO, Handbuch der Aquakultur']),
47
+ };
48
+
49
+ const es: LocalizedCopy = {
50
+ language: 'es', slug: 'calculadora-dosificacion-calcio-alcalinidad-acuario', title: 'Calculadora de dosificación de calcio y alcalinidad para acuarios', description: 'Calcula una dosis teórica de calcio o alcalinidad a partir del volumen, la medición, el objetivo y la concentración de tu producto.',
51
+ ui: ui({ toolKicker: '01 / Añadir suplemento', resultTitle: 'Añadir suplemento', configureTitle: '02 / Calcio o alcalinidad', parameterLabel: 'Calcio / alcalinidad', volumeLabel: 'Volumen útil de agua', currentLabel: 'Medición actual', targetLabel: 'Valor objetivo', productStrengthLabel: 'Concentración de la etiqueta', productStrengthHint: 'Cuánto sube el parámetro elegido una unidad de dosis en 100 L.', doseUnitLabel: 'Unidad de dosis', volumeUnitLabel: 'Unidad de volumen', litresLabel: 'Litros', gallonsLabel: 'Galones US', resultActionLabel: 'Acción', addAction: 'Añade el suplemento poco a poco', reduceAction: 'Planifica una reducción', targetAction: 'No hace falta corregir', emptyState: 'Introduce los datos de tu acuario para ver la acción.', doseLabel: 'Suplemento estimado', waterChangeLabel: 'Cambio de agua estimado', currentTargetLabel: 'Actual → objetivo', differenceLabel: 'Diferencia', addSummary: 'Es la cantidad teórica según la concentración de tu etiqueta. Divide la corrección cuando lo indique tu método o el producto.', reduceSummary: 'El objetivo está por debajo de la medición. El porcentaje usa una dilución sencilla; elige el método de cambio y vuelve a medir.', targetSummary: 'La medición actual coincide con el objetivo. Continúa con tu rutina normal de pruebas.', verificationNote: 'Solo es una estimación. Importan la etiqueta, la precisión del test, el volumen real y el consumo del acuario. Mide antes de repetir.', sceneAdd: 'Dosis medida entrando en el acuario', sceneReduce: 'Decisión de cambio de agua', sceneTarget: 'Objetivo alcanzado', invalidState: 'Revisa los valores y respeta los límites mostrados.', resetLabel: 'Restablecer ejemplo', localOnlyLabel: 'Funciona en tu navegador' }),
52
+ seo: seo('Calcula la próxima corrección con tu propia etiqueta', 'Los suplementos de calcio y alcalinidad no son intercambiables. Esta calculadora usa tu medición, el volumen útil y la respuesta impresa en el producto concreto que utilizas.', 'Calcio y alcalinidad usan escalas distintas', 'Elige calcio para ppm o alcalinidad para dKH. Después introduce cuánto sube el valor una dosis en 100 litros.', 'Cuando el objetivo es menor', 'Un suplemento no puede bajar una medición. En ese caso la herramienta cambia a una decisión de reducción y estima un cambio de agua sencillo.', 'Mide antes de repetir', 'Sigue la etiqueta, divide las correcciones grandes y vuelve a medir antes de aplicar otra dosis.', ['<strong>Usa el volumen real:</strong> cuenta el agua que hay, no la capacidad anunciada.', '<strong>Copia la etiqueta exacta:</strong> usa la respuesta de tu producto.', '<strong>Mantén la unidad:</strong> ppm para calcio y dKH para alcalinidad.', '<strong>Vuelve a medir:</strong> no encadenes estimaciones.'], [['Suplemento estimado', 'Cantidad teórica cuando el objetivo es mayor'], ['Cambio de agua estimado', 'Reducción sencilla cuando el objetivo es menor'], ['Actual → objetivo', 'La diferencia que se intenta cerrar'], ['Diferencia', 'El cambio seleccionado en ppm o dKH']]),
53
+ faq: [{ question: '¿Qué concentración del producto debo introducir?', answer: 'Copia la respuesta de la etiqueta de tu producto: cuánto sube el calcio en ppm o la alcalinidad en dKH una dosis en 100 litros.' }, { question: '¿Puede recomendar una dosis universal?', answer: 'No. Los suplementos cambian de concentración. La calculadora necesita la etiqueta del producto que realmente vas a usar.' }, { question: '¿Qué ocurre si la medición ya está por encima del objetivo?', answer: 'La herramienta cambia a una decisión de reducción y estima el porcentaje de cambio de agua con una dilución sencilla.' }, { question: '¿Añado toda la cantidad de una vez?', answer: 'Usa el resultado como punto de partida, sigue las instrucciones y divide las correcciones grandes cuando sea apropiado.' }],
54
+ howTo: [{ name: 'Elige el parámetro', text: 'Selecciona calcio para una medición en ppm o alcalinidad para una medición en dKH.' }, { name: 'Introduce tus mediciones', text: 'Añade el volumen útil, la medición actual y el objetivo.' }, { name: 'Lee la etiqueta', text: 'Introduce cuánto sube el parámetro una dosis de mililitros o gramos en 100 litros.' }, { name: 'Aplica y vuelve a medir', text: 'Usa la estimación como punto de partida, sigue la etiqueta y mide antes de corregir de nuevo.' }], bibliography: bibliography(['NOAA Ocean Service, Acidificación oceánica y química del agua marina', 'USGS Water Science School, Dureza del agua', 'FAO, Manual de acuicultura en español']),
55
+ };
56
+
57
+ const fr: LocalizedCopy = {
58
+ language: 'fr', slug: 'calculateur-dosage-calcium-alcalinite-aquarium', title: 'Calculateur de dosage du calcium et de l alcalinité pour aquarium', description: 'Calculez une dose théorique de calcium ou d alcalinité à partir du volume, de la mesure, de la cible et de la concentration indiquée sur votre produit.',
59
+ ui: ui({ toolKicker: '01 / Ajouter le complément', resultTitle: 'Ajouter le complément', configureTitle: '02 / Calcium ou alcalinité', parameterLabel: 'Calcium / alcalinité', volumeLabel: 'Volume d eau utile', currentLabel: 'Mesure actuelle', targetLabel: 'Valeur cible', productStrengthLabel: 'Concentration sur l étiquette', productStrengthHint: 'Hausse du paramètre choisi produite par une dose dans 100 L.', doseUnitLabel: 'Unité de dose', volumeUnitLabel: 'Unité de volume', litresLabel: 'Litres', gallonsLabel: 'Gallons US', resultActionLabel: 'Action', addAction: 'Ajouter progressivement', reduceAction: 'Planifier une réduction', targetAction: 'Aucune correction nécessaire', emptyState: 'Saisissez les données de votre aquarium pour voir l action.', doseLabel: 'Complément estimé', waterChangeLabel: 'Changement d eau estimé', currentTargetLabel: 'Actuel → cible', differenceLabel: 'Écart', addSummary: 'C est la quantité théorique selon votre étiquette. Fractionnez la correction lorsque votre méthode ou le produit le demande.', reduceSummary: 'La cible est inférieure à la mesure. Le pourcentage repose sur une dilution simple ; choisissez la méthode puis mesurez à nouveau.', targetSummary: 'La mesure actuelle correspond à la cible saisie. Continuez vos tests habituels.', verificationNote: 'Estimation uniquement. L étiquette, la précision du test, le volume réel et la consommation de l aquarium comptent. Mesurez avant de répéter.', sceneAdd: 'Dose mesurée ajoutée à l aquarium', sceneReduce: 'Décision de changement d eau', sceneTarget: 'Cible atteinte', invalidState: 'Vérifiez les valeurs et respectez les limites affichées.', resetLabel: 'Réinitialiser l exemple', localOnlyLabel: 'Fonctionne dans le navigateur' }),
60
+ seo: seo('Préparez la prochaine correction avec votre étiquette', 'Les compléments de calcium et d alcalinité ne sont pas interchangeables. Ce calculateur utilise votre mesure, le volume utile et la réponse inscrite sur votre produit.', 'Le calcium et l alcalinité ont des échelles différentes', 'Choisissez le calcium pour les ppm ou l alcalinité pour les dKH, puis saisissez la hausse produite par une dose dans 100 litres.', 'Lorsque la cible est plus basse', 'Un complément ne peut pas faire baisser une mesure. L outil propose alors une décision de réduction et une estimation simple de changement d eau.', 'Mesurez avant de recommencer', 'Suivez l étiquette, fractionnez les grandes corrections et mesurez à nouveau avant toute dose supplémentaire.', ['<strong>Utilisez le volume réel :</strong> comptez l eau présente.', '<strong>Recopiez la bonne étiquette :</strong> utilisez la réponse de votre produit.', '<strong>Gardez les unités :</strong> ppm pour le calcium et dKH pour l alcalinité.', '<strong>Mesurez après correction :</strong> ne cumulez pas les estimations.'], [['Complément estimé', 'Quantité théorique lorsque la cible est supérieure'], ['Changement d eau estimé', 'Réduction simple lorsque la cible est inférieure'], ['Actuel → cible', 'Écart à combler'], ['Écart', 'Variation en ppm ou dKH']]),
61
+ faq: [{ question: 'Quelle concentration dois-je saisir ?', answer: 'Recopiez la réponse de l étiquette de votre produit: la hausse en ppm de calcium ou en dKH d alcalinité produite par une dose dans 100 litres.' }, { question: 'Le calculateur peut-il proposer une dose universelle ?', answer: 'Non. Les produits ont des concentrations différentes ; utilisez toujours l étiquette du produit choisi.' }, { question: 'Que faire si la mesure dépasse déjà la cible ?', answer: 'Le résultat devient une décision de réduction et estime un changement d eau selon un modèle de dilution simple.' }, { question: 'Puis-je ajouter toute la quantité d un coup ?', answer: 'Utilisez le résultat comme estimation de départ, suivez les instructions et fractionnez les grandes corrections.' }],
62
+ howTo: [{ name: 'Choisissez le paramètre', text: 'Sélectionnez calcium pour une mesure en ppm ou alcalinité pour une mesure en dKH.' }, { name: 'Saisissez vos mesures', text: 'Ajoutez le volume utile, la mesure actuelle et la cible.' }, { name: 'Lisez l étiquette', text: 'Indiquez la hausse créée par une dose en millilitres ou grammes dans 100 litres.' }, { name: 'Agissez puis mesurez', text: 'Utilisez l estimation, suivez l étiquette et mesurez avant toute nouvelle correction.' }], bibliography: bibliography(['NOAA Ocean Service, Acidification des océans et chimie de l eau de mer', 'USGS Water Science School, Dureté de l eau', 'FAO, Manuel d aquaculture']),
63
+ };
64
+
65
+ const id: LocalizedCopy = {
66
+ language: 'id', slug: 'kalkulator-dosis-kalsium-alkalinitas-aquarium', title: 'Kalkulator dosis kalsium dan alkalinitas akuarium', description: 'Hitung perkiraan dosis suplemen kalsium atau alkalinitas dari volume air, hasil tes, target, dan kekuatan pada label produk Anda.',
67
+ ui: ui({ toolKicker: '01 / Tambahkan suplemen', resultTitle: 'Tambahkan suplemen', configureTitle: '02 / Kalsium atau alkalinitas', parameterLabel: 'Kalsium / alkalinitas', volumeLabel: 'Volume air kerja', currentLabel: 'Hasil tes saat ini', targetLabel: 'Hasil target', productStrengthLabel: 'Kekuatan pada label', productStrengthHint: 'Kenaikan parameter yang dipilih dari satu unit dosis dalam 100 L.', doseUnitLabel: 'Satuan dosis', volumeUnitLabel: 'Satuan volume', litresLabel: 'Liter', gallonsLabel: 'Galon AS', resultActionLabel: 'Tindakan', addAction: 'Tambahkan bertahap', reduceAction: 'Rencanakan pengurangan', targetAction: 'Tidak perlu koreksi', emptyState: 'Masukkan data akuarium Anda untuk melihat tindakan.', doseLabel: 'Suplemen perkiraan', waterChangeLabel: 'Pergantian air perkiraan', currentTargetLabel: 'Saat ini → target', differenceLabel: 'Selisih', addSummary: 'Ini adalah jumlah teoretis dari kekuatan label. Bagi koreksi jika metode atau petunjuk produk Anda memintanya.', reduceSummary: 'Target lebih rendah daripada hasil tes. Persentase memakai asumsi pengenceran sederhana; pilih metode pergantian air dan tes ulang.', targetSummary: 'Hasil saat ini sama dengan target. Lanjutkan pengujian sesuai jadwal normal.', verificationNote: 'Hanya perkiraan. Label, ketelitian tes, volume air, dan kebutuhan akuarium berpengaruh. Tes ulang sebelum mengulangi koreksi.', sceneAdd: 'Dosis terukur masuk ke akuarium', sceneReduce: 'Keputusan pergantian air', sceneTarget: 'Target tercapai', invalidState: 'Periksa nilai dan gunakan rentang yang ditampilkan.', resetLabel: 'Atur ulang contoh', localOnlyLabel: 'Berjalan di browser Anda' }),
68
+ seo: seo('Mulai koreksi berikutnya dari label produk Anda', 'Suplemen kalsium dan alkalinitas tidak dapat dipertukarkan. Alat ini memakai hasil tes, volume kerja, dan respons pada label produk yang Anda gunakan.', 'Kalsium dan alkalinitas memakai skala berbeda', 'Pilih kalsium untuk ppm atau alkalinitas untuk dKH, lalu masukkan kenaikan dari satu dosis dalam 100 liter.', 'Saat target lebih rendah', 'Suplemen tidak dapat menurunkan hasil tes. Alat ini beralih ke keputusan pengurangan dan perkiraan pergantian air sederhana.', 'Tes ulang sebelum mengulangi', 'Ikuti label produk, bagi koreksi besar, lalu tes ulang sebelum menambahkan dosis berikutnya.', ['<strong>Gunakan volume nyata:</strong> hitung air yang benar-benar ada.', '<strong>Salin label yang tepat:</strong> gunakan respons produk Anda.', '<strong>Pertahankan satuan:</strong> ppm untuk kalsium dan dKH untuk alkalinitas.', '<strong>Tes ulang:</strong> jangan menumpuk perkiraan.'], [['Suplemen perkiraan', 'Jumlah teoretis saat target lebih tinggi'], ['Pergantian air perkiraan', 'Perkiraan pengurangan saat target lebih rendah'], ['Saat ini → target', 'Selisih yang hendak ditutup'], ['Selisih', 'Perubahan dalam ppm atau dKH']]),
69
+ faq: [{ question: 'Kekuatan produk apa yang harus saya masukkan?', answer: 'Masukkan respons pada label produk Anda: kenaikan ppm kalsium atau dKH alkalinitas dari satu dosis dalam 100 liter.' }, { question: 'Bisakah kalkulator memilih dosis universal?', answer: 'Tidak. Konsentrasi produk berbeda, jadi gunakan label produk yang benar-benar Anda pakai.' }, { question: 'Bagaimana jika hasil tes sudah di atas target?', answer: 'Hasil berubah menjadi keputusan pengurangan dan memperkirakan persentase pergantian air dengan pengenceran sederhana.' }, { question: 'Haruskah seluruh dosis ditambahkan sekaligus?', answer: 'Gunakan hasil sebagai perkiraan awal, ikuti petunjuk produk, dan bagi koreksi besar bila diperlukan.' }],
70
+ howTo: [{ name: 'Pilih parameter', text: 'Pilih kalsium untuk tes ppm atau alkalinitas untuk tes dKH.' }, { name: 'Masukkan hasil tes', text: 'Masukkan volume kerja, hasil saat ini, dan target.' }, { name: 'Baca label produk', text: 'Masukkan kenaikan dari satu dosis mililiter atau gram dalam 100 liter.' }, { name: 'Lakukan dan tes ulang', text: 'Gunakan perkiraan sebagai awal, ikuti label, lalu tes ulang sebelum koreksi berikutnya.' }], bibliography: bibliography(['NOAA Ocean Service, Pengasaman laut dan kimia air laut', 'USGS Water Science School, Kesadahan air', 'FAO, Manual akuakultur']),
71
+ };
72
+
73
+ const it: LocalizedCopy = {
74
+ language: 'it', slug: 'calcolatore-dosaggio-calcio-alcalinita-acquario', title: 'Calcolatore del dosaggio di calcio e alcalinità per acquario', description: 'Calcola una dose teorica di calcio o alcalinità usando volume, misurazione, obiettivo e concentrazione indicata sull etichetta del prodotto.',
75
+ ui: ui({ toolKicker: '01 / Aggiungi integratore', resultTitle: 'Aggiungi integratore', configureTitle: '02 / Calcio o alcalinità', parameterLabel: 'Calcio / alcalinità', volumeLabel: 'Volume d acqua utile', currentLabel: 'Misurazione attuale', targetLabel: 'Valore obiettivo', productStrengthLabel: 'Concentrazione in etichetta', productStrengthHint: 'Aumento del parametro scelto prodotto da una dose in 100 L.', doseUnitLabel: 'Unità di dose', volumeUnitLabel: 'Unità di volume', litresLabel: 'Litri', gallonsLabel: 'Galloni USA', resultActionLabel: 'Azione', addAction: 'Aggiungi gradualmente', reduceAction: 'Pianifica una riduzione', targetAction: 'Nessuna correzione necessaria', emptyState: 'Inserisci i dati del tuo acquario per vedere l azione.', doseLabel: 'Integratore stimato', waterChangeLabel: 'Cambio d acqua stimato', currentTargetLabel: 'Attuale → obiettivo', differenceLabel: 'Differenza', addSummary: 'È la quantità teorica ricavata dall etichetta. Dividi la correzione quando lo richiedono il metodo o le istruzioni del prodotto.', reduceSummary: 'L obiettivo è inferiore alla misurazione. La percentuale usa una semplice ipotesi di diluizione; scegli il metodo e misura di nuovo.', targetSummary: 'La misurazione attuale coincide con l obiettivo. Continua con il normale programma di test.', verificationNote: 'Solo una stima. Etichetta, precisione del test, volume reale e consumo dell acquario sono importanti. Misura prima di ripetere.', sceneAdd: 'Dose misurata che entra nell acquario', sceneReduce: 'Decisione sul cambio d acqua', sceneTarget: 'Obiettivo raggiunto', invalidState: 'Controlla i valori e rispetta gli intervalli mostrati.', resetLabel: 'Ripristina esempio', localOnlyLabel: 'Funziona nel browser' }),
76
+ seo: seo('Prepara la prossima correzione dalla tua etichetta', 'Gli integratori di calcio e alcalinità non sono intercambiabili. Questo calcolatore usa la tua misurazione, il volume utile e la risposta indicata sul prodotto che usi.', 'Calcio e alcalinità hanno scale diverse', 'Scegli calcio per i ppm o alcalinità per i dKH, poi inserisci l aumento prodotto da una dose in 100 litri.', 'Quando l obiettivo è più basso', 'Un integratore non può ridurre una misurazione. In quel caso lo strumento propone una decisione di riduzione e una semplice stima del cambio d acqua.', 'Misura prima di ripetere', 'Segui l etichetta, dividi le correzioni grandi e misura di nuovo prima di una dose successiva.', ['<strong>Usa il volume reale:</strong> conta l acqua presente.', '<strong>Usa l etichetta giusta:</strong> inserisci la risposta del tuo prodotto.', '<strong>Mantieni le unità:</strong> ppm per il calcio e dKH per l alcalinità.', '<strong>Misura dopo la correzione:</strong> non sommare stime.'], [['Integratore stimato', 'Quantità teorica con obiettivo maggiore'], ['Cambio d acqua stimato', 'Riduzione semplice con obiettivo minore'], ['Attuale → obiettivo', 'Distanza da colmare'], ['Differenza', 'Variazione in ppm o dKH']]),
77
+ faq: [{ question: 'Quale concentrazione devo inserire?', answer: 'Copia la risposta dell etichetta del prodotto: l aumento in ppm di calcio o dKH di alcalinità prodotto da una dose in 100 litri.' }, { question: 'Il calcolatore può scegliere una dose universale?', answer: 'No. I prodotti hanno concentrazioni diverse: usa sempre l etichetta del prodotto scelto.' }, { question: 'Cosa succede se il valore supera già l obiettivo?', answer: 'Il risultato diventa una decisione di riduzione e stima un cambio d acqua con un semplice modello di diluizione.' }, { question: 'Devo aggiungere tutta la dose subito?', answer: 'Usa il risultato come stima iniziale, segui le istruzioni e dividi le correzioni importanti.' }],
78
+ howTo: [{ name: 'Scegli il parametro', text: 'Seleziona calcio per una misura in ppm o alcalinità per una misura in dKH.' }, { name: 'Inserisci le misure', text: 'Aggiungi volume utile, valore attuale e obiettivo.' }, { name: 'Leggi l etichetta', text: 'Indica l aumento prodotto da una dose in millilitri o grammi in 100 litri.' }, { name: 'Agisci e misura di nuovo', text: 'Usa la stima, segui l etichetta e misura prima della correzione successiva.' }], bibliography: bibliography(['NOAA Ocean Service, Acidificazione degli oceani e chimica dell acqua marina', 'USGS Water Science School, Durezza dell acqua', 'FAO, Manuale di acquacoltura']),
79
+ };
80
+
81
+ const ja: LocalizedCopy = {
82
+ language: 'ja', slug: 'aquarium-calcium-alkalinity-dosing-calculator', title: '水槽のカルシウム・アルカリ度 投与量計算', description: '水量、現在値、目標値、製品ラベルの濃度から、カルシウムまたはアルカリ度の補正量を計算し、次の対応を確認できます。',
83
+ ui: ui({ toolKicker: '01 / 添加量を決める', resultTitle: '添加量を決める', configureTitle: '02 / カルシウムまたはアルカリ度', parameterLabel: 'カルシウム / アルカリ度', calciumLabel: 'カルシウム', alkalinityLabel: 'アルカリ度', volumeLabel: '実水量', currentLabel: '現在の測定値', targetLabel: '目標値', productStrengthLabel: '製品ラベルの濃度', productStrengthHint: '100 Lで1単位を加えたときの上昇量です。', doseUnitLabel: '投与単位', volumeUnitLabel: '水量単位', litresLabel: 'リットル', gallonsLabel: '米ガロン', resultActionLabel: '対応', addAction: '少しずつ添加', reduceAction: '減少方法を計画', targetAction: '補正は不要', emptyState: '水槽の値を入力すると対応が表示されます。', doseLabel: '推定添加量', waterChangeLabel: '推定換水量', currentTargetLabel: '現在 → 目標', differenceLabel: '差', unitCalcium: 'ppm Ca', unitAlkalinity: 'dKH', addSummary: 'ラベルの濃度から計算した理論値です。方法や製品の指示に従い、必要なら分けて補正してください。', reduceSummary: '目標値が測定値より低くなっています。割合は単純な希釈モデルによる目安です。', targetSummary: '現在値は入力した目標値と一致しています。通常の頻度で測定を続けてください。', verificationNote: 'これは目安です。ラベル、測定精度、実水量、水槽の消費量を確認し、繰り返す前に再測定してください。', sceneAdd: '測定した添加量を水槽へ入れる', sceneReduce: '換水の判断', sceneTarget: '目標に到達', invalidState: '値を確認し、表示された範囲内で入力してください。', resetLabel: '例をリセット', localOnlyLabel: 'ブラウザ内で計算' }),
84
+ seo: seo('製品ラベルから次の補正量を計算', 'カルシウム用とアルカリ度用の添加剤は同じではありません。この計算では、水槽の測定値、実水量、使用する製品ラベルの反応量を使います。', 'カルシウムとアルカリ度は別の単位', 'カルシウムはppm、アルカリ度はdKHを選び、100 Lに1単位を加えたときの上昇量を入力します。', '目標値が低い場合', '添加剤で測定値を下げることはできません。その場合は単純な希釈による換水の目安を表示します。', '補正後に再測定', '製品の指示に従い、大きな補正は分けて行い、次の補正前に再測定してください。', ['<strong>実水量を使う:</strong>空の水槽容量ではなく、実際の水を数えます。', '<strong>ラベルを確認:</strong>使用する製品の値を入力します。', '<strong>単位を揃える:</strong>カルシウムはppm、アルカリ度はdKHです。', '<strong>毎回測定する:</strong>推定値を連続して使いません。'], [['推定添加量', '目標値が高い場合の理論量'], ['推定換水量', '目標値が低い場合の簡易目安'], ['現在 → 目標', '埋める差分'], ['差', 'ppmまたはdKHでの変化']]),
85
+ faq: [{ question: '製品濃度には何を入力しますか?', answer: '使用する製品ラベルから、100 Lに1回分を加えたときのカルシウムまたはアルカリ度の上昇量を入力します。' }, { question: '製品に関係なく使える量を出せますか?', answer: 'いいえ。製品ごとに濃度が違うため、実際に使う製品のラベル値が必要です。' }, { question: '現在値が目標値を超えている場合は?', answer: '減少の判断に切り替わり、単純な希釈モデルによる換水割合を表示します。' }, { question: '全量を一度に入れてもよいですか?', answer: '結果は開始時の目安です。製品の指示に従い、大きな補正は分けてください。' }],
86
+ howTo: [{ name: 'パラメータを選ぶ', text: 'ppmの測定ならカルシウム、dKHの測定ならアルカリ度を選びます。' }, { name: '測定値を入力', text: '実水量、現在値、目標値を入力します。' }, { name: 'ラベルを読む', text: '100 Lで1回分を加えたときの上昇量を入力します。' }, { name: '対応して再測定', text: '目安として使い、製品の指示に従ってから次の補正前に測定します。' }], bibliography: bibliography(['NOAA Ocean Service, 海洋酸性化と海水の化学', 'USGS Water Science School, 水の硬度', 'FAO, 水産養殖マニュアル']),
87
+ };
88
+
89
+ const ko: LocalizedCopy = {
90
+ language: 'ko', slug: 'aquarium-calcium-alkalinity-dosing-calculator', title: '수족관 칼슘 및 알칼리도 투여량 계산기', description: '수량, 현재 측정값, 목표값, 제품 라벨의 농도로 칼슘 또는 알칼리도 보충량을 계산하고 다음 조치를 확인해 보세요.',
91
+ ui: ui({ toolKicker: '01 / 보충제 추가', resultTitle: '보충제 추가', configureTitle: '02 / 칼슘 또는 알칼리도', parameterLabel: '칼슘 / 알칼리도', calciumLabel: '칼슘', alkalinityLabel: '알칼리도', volumeLabel: '실제 물 용량', currentLabel: '현재 측정값', targetLabel: '목표값', productStrengthLabel: '라벨의 제품 농도', productStrengthHint: '100 L에서 1회 투여 단위가 선택한 값을 얼마나 올리는지 입력합니다.', doseUnitLabel: '투여 단위', volumeUnitLabel: '부피 단위', litresLabel: '리터', gallonsLabel: '미국 갤런', resultActionLabel: '조치', addAction: '보충제를 조금씩 추가', reduceAction: '감소 계획 세우기', targetAction: '보정 필요 없음', emptyState: '수족관 값을 입력하면 조치가 표시됩니다.', doseLabel: '예상 보충량', waterChangeLabel: '예상 환수량', currentTargetLabel: '현재 → 목표', differenceLabel: '차이', addSummary: '라벨 농도로 계산한 이론적인 양입니다. 방법이나 제품 안내에 따라 나누어 보정하세요.', reduceSummary: '목표가 현재 측정값보다 낮습니다. 비율은 단순 희석 가정에 따른 값이므로 환수 방법을 직접 선택하고 다시 측정하세요.', targetSummary: '현재 측정값이 목표와 같습니다. 평소 일정대로 계속 측정하세요.', verificationNote: '예상값일 뿐입니다. 제품 라벨, 측정 정확도, 실제 물량과 수족관의 소비량을 확인하고 반복 전에 재측정하세요.', sceneAdd: '측정한 보충량을 수족관에 넣기', sceneReduce: '환수 결정', sceneTarget: '목표 도달', invalidState: '값을 확인하고 표시된 범위 안에서 입력하세요.', resetLabel: '예시 초기화', localOnlyLabel: '브라우저에서 계산' }),
92
+ seo: seo('제품 라벨에서 다음 보정량 계산하기', '칼슘과 알칼리도 보충제는 서로 바꿔 쓸 수 없습니다. 이 도구는 측정값, 실제 물량과 사용하는 제품 라벨의 반응값을 사용합니다.', '칼슘과 알칼리도는 다른 단위입니다', '칼슘은 ppm, 알칼리도는 dKH를 선택하고 100 L에서 한 번 투여했을 때의 상승량을 입력하세요.', '목표가 더 낮을 때', '보충제는 측정값을 낮출 수 없습니다. 이 경우 단순 희석 모델에 따른 환수 결정을 표시합니다.', '보정 후 다시 측정하기', '제품 안내를 따르고 큰 보정은 나누어 실시한 뒤 다음 보정 전에 다시 측정하세요.', ['<strong>실제 물량 사용:</strong> 현재 수조에 있는 물을 계산하세요.', '<strong>정확한 라벨 확인:</strong> 사용하는 제품의 반응값을 입력하세요.', '<strong>단위 유지:</strong> 칼슘은 ppm, 알칼리도는 dKH입니다.', '<strong>매번 재측정:</strong> 예상값을 이어서 사용하지 마세요.'], [['예상 보충량', '목표가 높을 때의 이론적 양'], ['예상 환수량', '목표가 낮을 때의 단순 감소 추정'], ['현재 → 목표', '줄이려는 차이'], ['차이', 'ppm 또는 dKH의 변화']]),
93
+ faq: [{ question: '제품 농도에는 무엇을 입력하나요?', answer: '제품 라벨에서 100 L에 한 번 투여했을 때 칼슘 또는 알칼리도가 얼마나 올라가는지 입력합니다.' }, { question: '모든 제품에 사용할 수 있는 공통 투여량인가요?', answer: '아니요. 제품마다 농도가 다르므로 실제 사용하는 제품의 라벨값이 필요합니다.' }, { question: '측정값이 이미 목표보다 높으면 어떻게 하나요?', answer: '감소 결정으로 전환되어 단순 희석 가정에 따른 환수 비율을 추정합니다.' }, { question: '한 번에 전량을 넣어도 되나요?', answer: '결과는 시작점인 예상값입니다. 제품 안내를 따르고 큰 보정은 나누어 진행하세요.' }],
94
+ howTo: [{ name: '항목 선택', text: 'ppm 측정이면 칼슘, dKH 측정이면 알칼리도를 선택합니다.' }, { name: '측정값 입력', text: '실제 물 용량, 현재값과 목표값을 입력합니다.' }, { name: '제품 라벨 확인', text: '100 L에서 1회 투여 시 상승량을 입력합니다.' }, { name: '조치 후 재측정', text: '예상값을 참고하고 제품 안내를 따른 뒤 다음 보정 전에 측정합니다.' }], bibliography: bibliography(['NOAA Ocean Service, 해양 산성화와 해수 화학', 'USGS Water Science School, 물의 경도', 'FAO, 양식 매뉴얼']),
95
+ };
96
+
97
+ const nl: LocalizedCopy = {
98
+ language: 'nl', slug: 'aquarium-calcium-alkaliteit-doseringscalculator', title: 'Aquariumcalculator voor calcium en alkaliteitsdosering', description: 'Bereken een theoretische dosis calcium of alkaliteit op basis van watervolume, meting, doelwaarde en het etiket van je product.',
99
+ ui: ui({ toolKicker: '01 / Supplement toevoegen', resultTitle: 'Supplement toevoegen', configureTitle: '02 / Calcium of alkaliteit', parameterLabel: 'Calcium / alkaliteit', calciumLabel: 'Calcium', alkalinityLabel: 'Alkaliteit', volumeLabel: 'Werkelijk watervolume', currentLabel: 'Huidige meting', targetLabel: 'Doelwaarde', productStrengthLabel: 'Productsterkte op etiket', productStrengthHint: 'Hoeveel één dosiseenheid de gekozen waarde in 100 L verhoogt.', doseUnitLabel: 'Doseereenheid', volumeUnitLabel: 'Volume-eenheid', litresLabel: 'Liter', gallonsLabel: 'US gallons', resultActionLabel: 'Actie', addAction: 'Geleidelijk supplement toevoegen', reduceAction: 'Een verlaging plannen', targetAction: 'Geen correctie nodig', emptyState: 'Vul je aquariumwaarden in om de actie te zien.', doseLabel: 'Geschat supplement', waterChangeLabel: 'Geschatte waterwissel', currentTargetLabel: 'Huidig → doel', differenceLabel: 'Verschil', addSummary: 'Dit is de theoretische hoeveelheid op basis van je etiket. Deel de correctie op als je methode of productinstructies dat vragen.', reduceSummary: 'De doelwaarde ligt lager dan de meting. Het percentage gebruikt een eenvoudige verdunningsaanname; kies zelf de methode en meet opnieuw.', targetSummary: 'De huidige waarde komt overeen met het ingevoerde doel. Blijf volgens je normale schema testen.', verificationNote: 'Alleen een schatting. Etiket, meetnauwkeurigheid, werkelijk volume en aquariumverbruik zijn belangrijk. Meet opnieuw voor je herhaalt.', sceneAdd: 'Gemeten dosis komt in het aquarium', sceneReduce: 'Beslissing over waterwissel', sceneTarget: 'Doel bereikt', invalidState: 'Controleer de waarden en blijf binnen de getoonde grenzen.', resetLabel: 'Voorbeeld resetten', localOnlyLabel: 'Draait in je browser' }),
100
+ seo: seo('Bereid de volgende correctie voor met je eigen etiket', 'Calcium- en alkaliteitssupplementen zijn niet uitwisselbaar. Deze calculator gebruikt je meting, het werkelijke volume en de reactie op het etiket van je product.', 'Calcium en alkaliteit gebruiken verschillende schalen', 'Kies calcium voor ppm of alkaliteit voor dKH en voer de stijging per dosis in 100 liter in.', 'Wanneer het doel lager is', 'Een supplement kan een meting niet verlagen. De tool schakelt dan over naar een eenvoudige schatting voor een waterwissel.', 'Meet voordat je herhaalt', 'Volg het etiket, deel grote correcties op en meet opnieuw voordat je een nieuwe dosis geeft.', ['<strong>Gebruik het echte volume:</strong> tel het aanwezige water.', '<strong>Gebruik het juiste etiket:</strong> neem de waarde van jouw product.', '<strong>Houd eenheden gelijk:</strong> ppm voor calcium en dKH voor alkaliteit.', '<strong>Meet opnieuw:</strong> stapel geen schattingen.'], [['Geschat supplement', 'Theoretische hoeveelheid bij een hoger doel'], ['Geschatte waterwissel', 'Eenvoudige reductieschatting bij een lager doel'], ['Huidig → doel', 'Het verschil dat je wilt sluiten'], ['Verschil', 'Verandering in ppm of dKH']]),
101
+ faq: [{ question: 'Welke productsterkte vul ik in?', answer: 'Neem de reactie van het etiket van je product over: hoeveel ppm calcium of dKH alkaliteit één dosis in 100 liter verhoogt.' }, { question: 'Kan de calculator een universele dosis kiezen?', answer: 'Nee. Supplementen verschillen in concentratie; gebruik altijd het etiket van het gekozen product.' }, { question: 'Wat als de meting al boven het doel ligt?', answer: 'De uitkomst wordt een reductiebeslissing en schat een waterwisselpercentage met eenvoudige verdunning.' }, { question: 'Moet ik alles in één keer toevoegen?', answer: 'Gebruik het resultaat als startschatting, volg de instructies en deel grote correcties op.' }],
102
+ howTo: [{ name: 'Kies de parameter', text: 'Kies calcium voor een ppm-meting of alkaliteit voor een dKH-meting.' }, { name: 'Vul je meting in', text: 'Voer werkvolume, huidige waarde en doelwaarde in.' }, { name: 'Lees het etiket', text: 'Vul de stijging per milliliter of gram in 100 liter in.' }, { name: 'Handel en meet opnieuw', text: 'Gebruik de schatting, volg het etiket en meet opnieuw voor de volgende correctie.' }], bibliography: bibliography(['NOAA Ocean Service, Verzuring van de oceaan en zeewaterchemie', 'USGS Water Science School, Waterhardheid', 'FAO, Handboek aquacultuur']),
103
+ };
104
+
105
+ const pl: LocalizedCopy = {
106
+ language: 'pl', slug: 'kalkulator-dawkowania-wapnia-alkalicznosci-akwarium', title: 'Kalkulator dawkowania wapnia i alkaliczności w akwarium', description: 'Oblicz teoretyczną dawkę wapnia lub alkaliczności na podstawie objętości wody, pomiaru, celu i etykiety produktu.',
107
+ ui: ui({ toolKicker: '01 / Dodaj suplement', resultTitle: 'Dodaj suplement', configureTitle: '02 / Wapń lub alkaliczność', parameterLabel: 'Wapń / alkaliczność', calciumLabel: 'Wapń', alkalinityLabel: 'Alkaliczność', volumeLabel: 'Rzeczywista objętość wody', currentLabel: 'Aktualny pomiar', targetLabel: 'Wartość docelowa', productStrengthLabel: 'Stężenie z etykiety', productStrengthHint: 'O ile jedna jednostka dawki podnosi wybrany parametr w 100 l.', doseUnitLabel: 'Jednostka dawki', volumeUnitLabel: 'Jednostka objętości', litresLabel: 'Litry', gallonsLabel: 'Galony US', resultActionLabel: 'Działanie', addAction: 'Dodawaj suplement stopniowo', reduceAction: 'Zaplanuj obniżenie', targetAction: 'Korekta niepotrzebna', emptyState: 'Wpisz dane akwarium, aby zobaczyć działanie.', doseLabel: 'Szacowany suplement', waterChangeLabel: 'Szacowana podmiana wody', currentTargetLabel: 'Aktualnie → cel', differenceLabel: 'Różnica', addSummary: 'To teoretyczna ilość wynikająca z etykiety. Podziel korektę, jeśli wymaga tego metoda lub instrukcja produktu.', reduceSummary: 'Cel jest niższy od pomiaru. Procent opiera się na prostym rozcieńczeniu; wybierz metodę podmiany i zmierz ponownie.', targetSummary: 'Aktualny pomiar odpowiada celowi. Kontynuuj testy według zwykłego harmonogramu.', verificationNote: 'To tylko szacunek. Liczą się etykieta, dokładność testu, rzeczywista objętość i zużycie w akwarium. Zmierz ponownie przed powtórzeniem.', sceneAdd: 'Zmierzona dawka trafia do akwarium', sceneReduce: 'Decyzja o podmianie wody', sceneTarget: 'Cel osiągnięty', invalidState: 'Sprawdź wartości i trzymaj się pokazanych zakresów.', resetLabel: 'Przywróć przykład', localOnlyLabel: 'Działa w przeglądarce' }),
108
+ seo: seo('Zaplanuj następną korektę na podstawie etykiety', 'Suplementy wapnia i alkaliczności nie są zamienne. To narzędzie korzysta z pomiaru, rzeczywistej objętości i reakcji podanej na etykiecie używanego produktu.', 'Wapń i alkaliczność mają różne skale', 'Wybierz wapń dla ppm lub alkaliczność dla dKH, a następnie podaj wzrost po jednej dawce w 100 litrach.', 'Gdy cel jest niższy', 'Suplement nie obniży wyniku testu. Narzędzie przełącza się wtedy na decyzję o redukcji i prosty szacunek podmiany.', 'Zmierz przed powtórzeniem', 'Przestrzegaj etykiety, dziel duże korekty i zmierz wodę ponownie przed kolejną dawką.', ['<strong>Użyj rzeczywistej objętości:</strong> policz obecną wodę.', '<strong>Sprawdź etykietę:</strong> wpisz reakcję właściwego produktu.', '<strong>Zachowaj jednostki:</strong> ppm dla wapnia i dKH dla alkaliczności.', '<strong>Mierz po korekcie:</strong> nie łącz kolejnych szacunków.'], [['Szacowany suplement', 'Teoretyczna ilość przy wyższym celu'], ['Szacowana podmiana', 'Prosta redukcja przy niższym celu'], ['Aktualnie → cel', 'Różnica do zamknięcia'], ['Różnica', 'Zmiana w ppm lub dKH']]),
109
+ faq: [{ question: 'Jaką moc produktu wpisać?', answer: 'Przepisz reakcję z etykiety produktu: o ile ppm wapnia lub dKH alkaliczności podnosi jedna dawka w 100 litrach.' }, { question: 'Czy kalkulator wybiera uniwersalną dawkę?', answer: 'Nie. Produkty mają różne stężenia, dlatego potrzebna jest etykieta używanego produktu.' }, { question: 'Co jeśli pomiar jest już powyżej celu?', answer: 'Wynik zmienia się w decyzję o redukcji i szacuje procent podmiany na podstawie prostego rozcieńczenia.' }, { question: 'Czy dodać całą dawkę od razu?', answer: 'Potraktuj wynik jako punkt wyjścia, przestrzegaj instrukcji i podziel duże korekty.' }],
110
+ howTo: [{ name: 'Wybierz parametr', text: 'Wybierz wapń dla pomiaru w ppm lub alkaliczność dla pomiaru w dKH.' }, { name: 'Wpisz pomiary', text: 'Podaj objętość roboczą, aktualny wynik i cel.' }, { name: 'Odczytaj etykietę', text: 'Wpisz wzrost po jednej dawce w mililitrach lub gramach na 100 litrów.' }, { name: 'Zastosuj i zmierz ponownie', text: 'Użyj szacunku, postępuj zgodnie z etykietą i zmierz przed kolejną korektą.' }], bibliography: bibliography(['NOAA Ocean Service, Zakwaszenie oceanów i chemia wody morskiej', 'USGS Water Science School, Twardość wody', 'FAO, Podręcznik akwakultury']),
111
+ };
112
+
113
+ const pt: LocalizedCopy = {
114
+ language: 'pt', slug: 'calculadora-dosagem-calcio-alcalinidade-aquario', title: 'Calculadora de dosagem de cálcio e alcalinidade para aquário', description: 'Calcule uma dose teórica de cálcio ou alcalinidade a partir do volume, da medição, do objetivo e do rótulo do seu produto.',
115
+ ui: ui({ toolKicker: '01 / Adicionar suplemento', resultTitle: 'Adicionar suplemento', configureTitle: '02 / Cálcio ou alcalinidade', parameterLabel: 'Cálcio / alcalinidade', calciumLabel: 'Cálcio', alkalinityLabel: 'Alcalinidade', volumeLabel: 'Volume real de água', currentLabel: 'Medição atual', targetLabel: 'Valor objetivo', productStrengthLabel: 'Concentração no rótulo', productStrengthHint: 'Quanto uma unidade de dose aumenta o parâmetro escolhido em 100 L.', doseUnitLabel: 'Unidade de dose', volumeUnitLabel: 'Unidade de volume', litresLabel: 'Litros', gallonsLabel: 'Galões US', resultActionLabel: 'Ação', addAction: 'Adicionar gradualmente', reduceAction: 'Planear uma redução', targetAction: 'Não é necessária correção', emptyState: 'Introduza os dados do aquário para ver a ação.', doseLabel: 'Suplemento estimado', waterChangeLabel: 'Troca de água estimada', currentTargetLabel: 'Atual → objetivo', differenceLabel: 'Diferença', addSummary: 'É a quantidade teórica calculada pelo rótulo. Divida a correção quando o método ou as instruções do produto o indicarem.', reduceSummary: 'O objetivo é inferior à medição. A percentagem usa uma diluição simples; escolha o método e volte a medir.', targetSummary: 'A medição atual corresponde ao objetivo. Continue a testar no seu ritmo normal.', verificationNote: 'É apenas uma estimativa. O rótulo, a precisão do teste, o volume real e o consumo do aquário importam. Meça antes de repetir.', sceneAdd: 'Dose medida a entrar no aquário', sceneReduce: 'Decisão de troca de água', sceneTarget: 'Objetivo atingido', invalidState: 'Verifique os valores e respeite os limites apresentados.', resetLabel: 'Repor exemplo', localOnlyLabel: 'Funciona no navegador' }),
116
+ seo: seo('Prepare a próxima correção com o seu rótulo', 'Os suplementos de cálcio e alcalinidade não são equivalentes. Esta calculadora usa a sua medição, o volume real e a resposta indicada no rótulo do produto.', 'Cálcio e alcalinidade usam escalas diferentes', 'Escolha cálcio para ppm ou alcalinidade para dKH e indique o aumento produzido por uma dose em 100 litros.', 'Quando o objetivo é mais baixo', 'Um suplemento não baixa uma medição. Nesse caso, a ferramenta mostra uma decisão de redução e uma estimativa simples de troca de água.', 'Meça antes de repetir', 'Siga o rótulo, divida correções grandes e volte a medir antes de uma nova dose.', ['<strong>Use o volume real:</strong> conte a água presente.', '<strong>Use o rótulo certo:</strong> introduza a resposta do seu produto.', '<strong>Mantenha as unidades:</strong> ppm para cálcio e dKH para alcalinidade.', '<strong>Meça novamente:</strong> não acumule estimativas.'], [['Suplemento estimado', 'Quantidade teórica quando o objetivo é maior'], ['Troca estimada', 'Redução simples quando o objetivo é menor'], ['Atual → objetivo', 'Diferença que se pretende fechar'], ['Diferença', 'Variação em ppm ou dKH']]),
117
+ faq: [{ question: 'Que concentração devo introduzir?', answer: 'Copie a resposta do rótulo do produto: quanto uma dose em 100 litros aumenta o cálcio em ppm ou a alcalinidade em dKH.' }, { question: 'A calculadora escolhe uma dose universal?', answer: 'Não. Os produtos têm concentrações diferentes; use sempre o rótulo do produto escolhido.' }, { question: 'E se a medição já estiver acima do objetivo?', answer: 'O resultado passa a ser uma decisão de redução e estima a percentagem de troca com uma diluição simples.' }, { question: 'Devo adicionar tudo de uma vez?', answer: 'Use o resultado como estimativa inicial, siga as instruções e divida correções grandes.' }],
118
+ howTo: [{ name: 'Escolha o parâmetro', text: 'Selecione cálcio para ppm ou alcalinidade para dKH.' }, { name: 'Introduza as medições', text: 'Indique o volume real, a medição atual e o objetivo.' }, { name: 'Leia o rótulo', text: 'Indique o aumento produzido por uma dose em mililitros ou gramas em 100 litros.' }, { name: 'Aja e volte a medir', text: 'Use a estimativa, siga o rótulo e meça antes da próxima correção.' }], bibliography: bibliography(['NOAA Ocean Service, Acidificação dos oceanos e química da água do mar', 'USGS Water Science School, Dureza da água', 'FAO, Manual de aquacultura']),
119
+ };
120
+
121
+ const ru: LocalizedCopy = {
122
+ language: 'ru', slug: 'kalkulyator-dozirovki-kaltsiya-shchelochnosti-akvariuma', title: 'Калькулятор дозировки кальция и щёлочности для аквариума', description: 'Рассчитайте теоретическую дозу кальция или щёлочности по объёму воды, текущему значению, цели и этикетке вашего продукта.',
123
+ ui: ui({ toolKicker: '01 / Добавить добавку', resultTitle: 'Добавить добавку', configureTitle: '02 / Кальций или щёлочность', parameterLabel: 'Кальций / щёлочность', calciumLabel: 'Кальций', alkalinityLabel: 'Щёлочность', volumeLabel: 'Рабочий объём воды', currentLabel: 'Текущее значение', targetLabel: 'Целевое значение', productStrengthLabel: 'Концентрация на этикетке', productStrengthHint: 'Насколько одна единица дозы повышает выбранный параметр в 100 л.', doseUnitLabel: 'Единица дозы', volumeUnitLabel: 'Единица объёма', litresLabel: 'Литры', gallonsLabel: 'Галлоны США', resultActionLabel: 'Действие', addAction: 'Добавляйте постепенно', reduceAction: 'Запланируйте снижение', targetAction: 'Коррекция не нужна', emptyState: 'Введите данные аквариума, чтобы увидеть действие.', doseLabel: 'Расчётное количество добавки', waterChangeLabel: 'Расчётная подмена воды', currentTargetLabel: 'Сейчас → цель', differenceLabel: 'Разница', addSummary: 'Это теоретическое количество по концентрации на этикетке. Разделите коррекцию, если этого требуют метод или инструкция продукта.', reduceSummary: 'Цель ниже текущего результата. Процент основан на простой модели разбавления; выберите способ подмены и проверьте воду снова.', targetSummary: 'Текущее значение совпадает с целью. Продолжайте измерения по обычному графику.', verificationNote: 'Только оценка. Важны этикетка, точность теста, реальный объём и потребление аквариума. Повторно измерьте перед следующей коррекцией.', sceneAdd: 'Измеренная доза поступает в аквариум', sceneReduce: 'Решение о подмене воды', sceneTarget: 'Цель достигнута', invalidState: 'Проверьте значения и соблюдайте указанные пределы.', resetLabel: 'Сбросить пример', localOnlyLabel: 'Работает в браузере' }),
124
+ seo: seo('Рассчитайте следующую коррекцию по этикетке продукта', 'Добавки кальция и щёлочности нельзя считать взаимозаменяемыми. Инструмент использует ваш результат теста, рабочий объём и реакцию, указанную на этикетке продукта.', 'У кальция и щёлочности разные шкалы', 'Выберите кальций для ppm или щёлочность для dKH и укажите повышение от одной дозы на 100 литров.', 'Если цель ниже текущего значения', 'Добавка не может снизить результат теста. В этом случае инструмент предлагает решение о снижении и простую оценку подмены воды.', 'Измерьте воду перед повтором', 'Следуйте этикетке, делите большие коррекции на части и измеряйте воду перед следующей дозой.', ['<strong>Используйте реальный объём:</strong> считайте воду в аквариуме.', '<strong>Проверьте этикетку:</strong> укажите реакцию именно вашего продукта.', '<strong>Не смешивайте единицы:</strong> ppm для кальция и dKH для щёлочности.', '<strong>Измеряйте снова:</strong> не складывайте последовательные оценки.'], [['Расчётная добавка', 'Теоретическое количество при более высокой цели'], ['Расчётная подмена', 'Простая оценка снижения при меньшей цели'], ['Сейчас → цель', 'Разница, которую нужно закрыть'], ['Разница', 'Изменение в ppm или dKH']]),
125
+ faq: [{ question: 'Какую концентрацию продукта вводить?', answer: 'Перенесите с этикетки реакцию продукта: насколько одна доза в 100 литрах повышает кальций в ppm или щёлочность в dKH.' }, { question: 'Может ли калькулятор выбрать универсальную дозу?', answer: 'Нет. Концентрации различаются, поэтому нужна этикетка используемого продукта.' }, { question: 'Что делать, если результат уже выше цели?', answer: 'Инструмент переключится на решение о снижении и оценит процент подмены по простой модели разбавления.' }, { question: 'Нужно ли вносить всё сразу?', answer: 'Используйте результат как начальную оценку, следуйте инструкции и делите большие коррекции.' }],
126
+ howTo: [{ name: 'Выберите параметр', text: 'Выберите кальций для измерения в ppm или щёлочность для измерения в dKH.' }, { name: 'Введите измерения', text: 'Укажите рабочий объём, текущее и целевое значение.' }, { name: 'Прочитайте этикетку', text: 'Введите повышение от одной дозы в миллилитрах или граммах на 100 литров.' }, { name: 'Выполните действие и измерьте снова', text: 'Используйте оценку, следуйте этикетке и проверьте воду перед следующей коррекцией.' }], bibliography: bibliography(['NOAA Ocean Service, Закисление океана и химия морской воды', 'USGS Water Science School, Жёсткость воды', 'FAO, Руководство по аквакультуре']),
127
+ };
128
+
129
+ const sv: LocalizedCopy = {
130
+ language: 'sv', slug: 'kalkylator-dosering-kalcium-alkalinitet-akvarium', title: 'Kalkylator för kalcium och alkalinitetsdosering i akvarium', description: 'Beräkna en teoretisk dos kalcium eller alkalinitet utifrån vattenvolym, mätvärde, mål och produktens etikett.',
131
+ ui: ui({ toolKicker: '01 / Tillsätt tillskott', resultTitle: 'Tillsätt tillskott', configureTitle: '02 / Kalcium eller alkalinitet', parameterLabel: 'Kalcium / alkalinitet', calciumLabel: 'Kalcium', alkalinityLabel: 'Alkalinitet', volumeLabel: 'Verklig vattenvolym', currentLabel: 'Aktuellt mätvärde', targetLabel: 'Målvärde', productStrengthLabel: 'Produktstyrka på etiketten', productStrengthHint: 'Hur mycket en dosenhet höjer valt värde i 100 L.', doseUnitLabel: 'Dos-enhet', volumeUnitLabel: 'Volymenhet', litresLabel: 'Liter', gallonsLabel: 'US gallons', resultActionLabel: 'Åtgärd', addAction: 'Tillsätt gradvis', reduceAction: 'Planera en sänkning', targetAction: 'Ingen korrigering behövs', emptyState: 'Fyll i akvariets värden för att se åtgärden.', doseLabel: 'Beräknat tillskott', waterChangeLabel: 'Beräknat vattenbyte', currentTargetLabel: 'Aktuellt → mål', differenceLabel: 'Skillnad', addSummary: 'Detta är den teoretiska mängden från etikettens styrka. Dela upp korrigeringen när metoden eller produktens instruktioner kräver det.', reduceSummary: 'Målet ligger under mätvärdet. Procenten bygger på ett enkelt antagande om utspädning; välj metod och mät igen.', targetSummary: 'Det aktuella värdet matchar målet. Fortsätt testa enligt din vanliga rutin.', verificationNote: 'Endast en uppskattning. Etikett, testets noggrannhet, verklig volym och akvariets förbrukning spelar roll. Mät igen innan du upprepar.', sceneAdd: 'Uppmätt dos till akvariet', sceneReduce: 'Beslut om vattenbyte', sceneTarget: 'Målet nått', invalidState: 'Kontrollera värdena och håll dig inom de visade gränserna.', resetLabel: 'Återställ exempel', localOnlyLabel: 'Körs i webbläsaren' }),
132
+ seo: seo('Planera nästa korrigering från din produktetikett', 'Kalcium- och alkalinitetstillskott är inte utbytbara. Verktyget använder ditt mätvärde, den verkliga vattenvolymen och reaktionen på produktens etikett.', 'Kalcium och alkalinitet använder olika skalor', 'Välj kalcium för ppm eller alkalinitet för dKH och ange ökningen från en dos i 100 liter.', 'När målet är lägre', 'Ett tillskott kan inte sänka ett mätvärde. Då visar verktyget ett sänkningsbeslut och en enkel uppskattning av vattenbyte.', 'Mät innan du upprepar', 'Följ etiketten, dela stora korrigeringar och mät igen innan nästa dos.', ['<strong>Använd verklig volym:</strong> räkna vattnet som finns.', '<strong>Kontrollera etiketten:</strong> använd ditt produktsvar.', '<strong>Håll enheterna:</strong> ppm för kalcium och dKH för alkalinitet.', '<strong>Mät igen:</strong> stapla inte uppskattningar.'], [['Beräknat tillskott', 'Teoretisk mängd när målet är högre'], ['Beräknat vattenbyte', 'Enkel sänkning när målet är lägre'], ['Aktuellt → mål', 'Skillnaden som ska stängas'], ['Skillnad', 'Förändringen i ppm eller dKH']]),
133
+ faq: [{ question: 'Vilken produktstyrka ska jag ange?', answer: 'Ange reaktionen från etiketten: hur mycket en dos i 100 liter höjer kalcium i ppm eller alkalinitet i dKH.' }, { question: 'Kan kalkylatorn välja en universell dos?', answer: 'Nej. Produkter har olika koncentrationer, så använd etiketten för produkten du faktiskt använder.' }, { question: 'Vad händer om mätvärdet redan ligger över målet?', answer: 'Resultatet blir ett sänkningsbeslut och uppskattar vattenbytesprocenten med enkel utspädning.' }, { question: 'Ska jag tillsätta allt på en gång?', answer: 'Använd resultatet som startvärde, följ instruktionerna och dela stora korrigeringar.' }],
134
+ howTo: [{ name: 'Välj parameter', text: 'Välj kalcium för ppm eller alkalinitet för dKH.' }, { name: 'Ange mätvärden', text: 'Fyll i verklig volym, aktuellt värde och mål.' }, { name: 'Läs etiketten', text: 'Ange ökningen från en dos i milliliter eller gram per 100 liter.' }, { name: 'Åtgärda och mät igen', text: 'Använd uppskattningen, följ etiketten och mät innan nästa korrigering.' }], bibliography: bibliography(['NOAA Ocean Service, Havsförsurning och havsvattnets kemi', 'USGS Water Science School, Vattnets hårdhet', 'FAO, Handbok i vattenbruk']),
135
+ };
136
+
137
+ const tr: LocalizedCopy = {
138
+ language: 'tr', slug: 'akvaryum-kalsiyum-alkalinite-doz-hesaplayici', title: 'Akvaryum kalsiyum ve alkalinite doz hesaplayıcı', description: 'Su hacmi, ölçüm, hedef ve ürün etiketindeki yoğunluktan teorik kalsiyum veya alkalinite dozunu hesaplayın.',
139
+ ui: ui({ toolKicker: '01 / Takviye ekle', resultTitle: 'Takviye ekle', configureTitle: '02 / Kalsiyum veya alkalinite', parameterLabel: 'Kalsiyum / alkalinite', calciumLabel: 'Kalsiyum', alkalinityLabel: 'Alkalinite', volumeLabel: 'Gerçek su hacmi', currentLabel: 'Mevcut ölçüm', targetLabel: 'Hedef değer', productStrengthLabel: 'Etiketteki ürün gücü', productStrengthHint: 'Bir doz biriminin seçilen değeri 100 L içinde ne kadar artırdığı.', doseUnitLabel: 'Doz birimi', volumeUnitLabel: 'Hacim birimi', litresLabel: 'Litre', gallonsLabel: 'ABD galonu', resultActionLabel: 'İşlem', addAction: 'Takviyeyi kademeli ekle', reduceAction: 'Azaltma planla', targetAction: 'Düzeltme gerekmiyor', emptyState: 'İşlemi görmek için akvaryum değerlerini girin.', doseLabel: 'Tahmini takviye', waterChangeLabel: 'Tahmini su değişimi', currentTargetLabel: 'Mevcut → hedef', differenceLabel: 'Fark', addSummary: 'Bu, etiket gücünden hesaplanan teorik miktardır. Yönteminiz veya ürün talimatı gerektiriyorsa düzeltmeyi bölün.', reduceSummary: 'Hedef ölçümden düşük. Yüzde basit bir seyreltme varsayımı kullanır; su değişimi yöntemini seçin ve tekrar ölçün.', targetSummary: 'Mevcut değer hedefle eşleşiyor. Normal test rutininize devam edin.', verificationNote: 'Yalnızca tahmindir. Etiket, test doğruluğu, gerçek su hacmi ve akvaryum tüketimi önemlidir. Tekrarlamadan önce yeniden ölçün.', sceneAdd: 'Ölçülen doz akvaryuma giriyor', sceneReduce: 'Su değişimi kararı', sceneTarget: 'Hedefe ulaşıldı', invalidState: 'Değerleri kontrol edin ve gösterilen aralıklarda kalın.', resetLabel: 'Örneği sıfırla', localOnlyLabel: 'Tarayıcınızda çalışır' }),
140
+ seo: seo('Sonraki düzeltmeyi ürün etiketinizden planlayın', 'Kalsiyum ve alkalinite takviyeleri birbirinin yerine kullanılamaz. Bu araç ölçümünüzü, gerçek su hacmini ve kullandığınız ürünün etiketindeki tepkiyi kullanır.', 'Kalsiyum ve alkalinite farklı ölçekler kullanır', 'Kalsiyum için ppm, alkalinite için dKH seçin ve 100 litrede tek dozun oluşturduğu artışı girin.', 'Hedef daha düşük olduğunda', 'Takviye ölçümü düşüremez. Araç bu durumda azaltma kararına geçer ve basit bir su değişimi tahmini gösterir.', 'Tekrarlamadan önce ölçün', 'Etiketi izleyin, büyük düzeltmeleri bölün ve sonraki dozdan önce yeniden ölçün.', ['<strong>Gerçek hacmi kullanın:</strong> mevcut suyu sayın.', '<strong>Doğru etiketi okuyun:</strong> kullandığınız ürünün değerini girin.', '<strong>Birimleri koruyun:</strong> kalsiyum için ppm, alkalinite için dKH.', '<strong>Yeniden ölçün:</strong> tahminleri üst üste kullanmayın.'], [['Tahmini takviye', 'Hedef yüksekken teorik miktar'], ['Tahmini su değişimi', 'Hedef düşükken basit azaltma tahmini'], ['Mevcut → hedef', 'Kapatılacak fark'], ['Fark', 'ppm veya dKH değişimi']]),
141
+ faq: [{ question: 'Hangi ürün gücünü girmeliyim?', answer: 'Ürün etiketindeki tepkiyi girin: 100 litrede tek dozun kalsiyumu ppm veya alkaliniteyi dKH olarak ne kadar artırdığı.' }, { question: 'Hesaplayıcı evrensel doz seçebilir mi?', answer: 'Hayır. Ürün yoğunlukları farklıdır; gerçekten kullanacağınız ürünün etiketini kullanın.' }, { question: 'Ölçüm hedefin üzerindeyse ne olur?', answer: 'Sonuç azaltma kararına dönüşür ve basit seyreltmeyle su değişimi yüzdesini tahmin eder.' }, { question: 'Tüm dozu bir anda eklemeli miyim?', answer: 'Sonucu başlangıç tahmini olarak kullanın, talimatları izleyin ve büyük düzeltmeleri bölün.' }],
142
+ howTo: [{ name: 'Parametreyi seçin', text: 'ppm ölçümü için kalsiyum, dKH ölçümü için alkaliniteyi seçin.' }, { name: 'Ölçümleri girin', text: 'Gerçek hacmi, mevcut değeri ve hedefi girin.' }, { name: 'Etiketi okuyun', text: '100 litrede tek dozun oluşturduğu artışı girin.' }, { name: 'Uygulayın ve tekrar ölçün', text: 'Tahmini başlangıç olarak kullanın, etiketi izleyin ve yeni düzeltmeden önce ölçün.' }], bibliography: bibliography(['NOAA Ocean Service, Okyanus asitlenmesi ve deniz suyu kimyası', 'USGS Water Science School, Suyun sertliği', 'FAO, Su ürünleri yetiştiriciliği kılavuzu']),
143
+ };
144
+
145
+ const zh: LocalizedCopy = {
146
+ language: 'zh', slug: 'aquarium-calcium-alkalinity-dosing-calculator', title: '水族箱钙与碱度添加量计算器', description: '根据水量、当前测试值、目标值和产品标签浓度,估算钙或碱度补充剂用量,并查看下一步校正建议。',
147
+ ui: ui({ toolKicker: '01 / 添加补充剂', resultTitle: '添加补充剂', configureTitle: '02 / 钙或碱度', parameterLabel: '钙 / 碱度', calciumLabel: '钙', alkalinityLabel: '碱度', volumeLabel: '实际水量', currentLabel: '当前测试值', targetLabel: '目标值', productStrengthLabel: '标签上的产品浓度', productStrengthHint: '每个剂量单位在 100 L 水中使所选参数升高的数值。', doseUnitLabel: '剂量单位', volumeUnitLabel: '体积单位', litresLabel: '升', gallonsLabel: '美制加仑', resultActionLabel: '操作', addAction: '逐步添加补充剂', reduceAction: '规划降低方案', targetAction: '无需校正', emptyState: '输入水族箱数据即可查看操作建议。', doseLabel: '预计补充剂', waterChangeLabel: '预计换水量', currentTargetLabel: '当前 → 目标', differenceLabel: '差值', addSummary: '这是根据产品标签浓度得到的理论用量。请按方法或产品说明分次校正。', reduceSummary: '目标值低于当前测试值。百分比使用简单稀释假设,请自行选择换水方法并重新测试。', targetSummary: '当前测试值与目标一致。按平时的频率继续测试即可。', verificationNote: '仅供估算。产品标签、测试准确度、实际水量和水族箱消耗都会影响结果。重复校正前请重新测试。', sceneAdd: '测量的剂量进入水族箱', sceneReduce: '换水决策', sceneTarget: '达到目标', invalidState: '请检查数值,并保持在显示的范围内。', resetLabel: '重置示例', localOnlyLabel: '在浏览器中运行' }),
148
+ seo: seo('从产品标签开始计算下一次校正', '钙和碱度补充剂不能互换。本工具使用你的测试值、实际水量以及所用产品标签上的反应值。', '钙和碱度使用不同的单位', '钙选择 ppm,碱度选择 dKH,然后输入每个剂量单位在 100 L 水中的升高量。', '当目标值更低时', '补充剂不能降低测试值。此时工具会转为降低决策,并给出简单稀释模型下的换水估算。', '重复之前先测试', '遵循产品说明,将较大的校正分次进行,并在下一次添加前重新测试。', ['<strong>使用实际水量:</strong>计算水族箱中现有的水。', '<strong>核对产品标签:</strong>填写正在使用的产品数据。', '<strong>保持单位一致:</strong>钙用 ppm,碱度用 dKH。', '<strong>每次重新测试:</strong>不要连续叠加估算值。'], [['预计补充剂', '目标值较高时的理论用量'], ['预计换水量', '目标值较低时的简单降低估算'], ['当前 → 目标', '需要弥合的差值'], ['差值', 'ppm 或 dKH 的变化']]),
149
+ faq: [{ question: '产品浓度应该填什么?', answer: '填写产品标签上的反应值:每个剂量单位在 100 L 水中使钙升高多少 ppm,或使碱度升高多少 dKH。' }, { question: '能否选择通用剂量?', answer: '不能。不同产品浓度不同,必须使用实际产品的标签数据。' }, { question: '如果当前值已经高于目标怎么办?', answer: '结果会切换为降低决策,并用简单稀释模型估算换水百分比。' }, { question: '需要一次加入全部剂量吗?', answer: '把结果作为起始估算,遵循产品说明,并在需要时分次进行较大的校正。' }],
150
+ howTo: [{ name: '选择参数', text: 'ppm 测试选择钙,dKH 测试选择碱度。' }, { name: '输入测试值', text: '填写实际水量、当前值和目标值。' }, { name: '查看产品标签', text: '输入每个剂量单位在 100 L 水中的升高量。' }, { name: '执行并重新测试', text: '参考估算值,遵循标签说明,并在下一次校正前重新测试。' }], bibliography: bibliography(['NOAA Ocean Service,海洋酸化与海水化学', 'USGS Water Science School,水的硬度', 'FAO,水产养殖手册']),
151
+ };
152
+
153
+ export const localizedContents = {
154
+ de: buildContent(de), en: buildContent(en), es: buildContent(es), fr: buildContent(fr), id: buildContent(id), it: buildContent(it), ja: buildContent(ja), ko: buildContent(ko), nl: buildContent(nl), pl: buildContent(pl), pt: buildContent(pt), ru: buildContent(ru), sv: buildContent(sv), tr: buildContent(tr), zh: buildContent(zh),
155
+ };
@@ -0,0 +1,2 @@
1
+ import { localizedContents } from './translations';
2
+ export const content = localizedContents.zh;
@@ -0,0 +1,11 @@
1
+ import { aquariumCalciumAlkalinityDosingCalculator } from './entry';
2
+ import type { ToolDefinition } from '../../types';
3
+
4
+ export * from './entry';
5
+
6
+ export const AQUARIUM_CALCIUM_ALKALINITY_DOSING_CALCULATOR_TOOL: ToolDefinition = {
7
+ entry: aquariumCalciumAlkalinityDosingCalculator,
8
+ Component: () => import('./component.astro'),
9
+ SEOComponent: () => import('./seo.astro'),
10
+ BibliographyComponent: () => import('./bibliography.astro'),
11
+ };
@@ -0,0 +1,33 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { calculateCalciumAlkalinityDosing, DEFAULT_DOSING_INPUT } from './logic';
3
+
4
+ describe('calculateCalciumAlkalinityDosing', () => {
5
+ it('scales a calcium dose by the entered volume', () => {
6
+ const result = calculateCalciumAlkalinityDosing(DEFAULT_DOSING_INPUT);
7
+ expect(result).toEqual({
8
+ ok: true,
9
+ value: expect.objectContaining({ action: 'add', difference: 40, doseAmount: 20 }),
10
+ });
11
+ });
12
+
13
+ it('calculates an alkalinity dose from the product label rate', () => {
14
+ const result = calculateCalciumAlkalinityDosing({ ...DEFAULT_DOSING_INPUT, parameter: 'alkalinity', currentValue: 7, targetValue: 8.5, productStrengthPerDose: 0.5, doseUnit: 'ml' });
15
+ expect(result).toEqual({ ok: true, value: expect.objectContaining({ action: 'add', difference: 1.5, doseAmount: 3 }) });
16
+ });
17
+
18
+ it('returns a dilution decision when the target is lower', () => {
19
+ const result = calculateCalciumAlkalinityDosing({ ...DEFAULT_DOSING_INPUT, currentValue: 450, targetValue: 420 });
20
+ expect(result).toEqual({ ok: true, value: expect.objectContaining({ action: 'reduce', estimatedWaterChangePercent: expect.closeTo(6.6667, 3), doseAmount: 0 }) });
21
+ });
22
+
23
+ it('returns no dose at the target', () => {
24
+ const result = calculateCalciumAlkalinityDosing({ ...DEFAULT_DOSING_INPUT, currentValue: 420, targetValue: 420 });
25
+ expect(result).toEqual({ ok: true, value: expect.objectContaining({ action: 'target', doseAmount: 0, estimatedWaterChangePercent: 0 }) });
26
+ });
27
+
28
+ it('rejects non-finite or out-of-range values', () => {
29
+ expect(calculateCalciumAlkalinityDosing({ ...DEFAULT_DOSING_INPUT, volumeLitres: 0 })).toEqual({ ok: false, error: 'volume' });
30
+ expect(calculateCalciumAlkalinityDosing({ ...DEFAULT_DOSING_INPUT, parameter: 'alkalinity', targetValue: 41 })).toEqual({ ok: false, error: 'value' });
31
+ expect(calculateCalciumAlkalinityDosing({ ...DEFAULT_DOSING_INPUT, productStrengthPerDose: Number.NaN })).toEqual({ ok: false, error: 'strength' });
32
+ });
33
+ });
@@ -0,0 +1,112 @@
1
+ export type AquariumParameter = "calcium" | "alkalinity";
2
+ export type DoseUnit = "ml" | "g";
3
+ export type DosingAction = "add" | "reduce" | "target";
4
+
5
+ export interface CalciumAlkalinityDosingInput {
6
+ parameter: AquariumParameter;
7
+ volumeLitres: number;
8
+ currentValue: number;
9
+ targetValue: number;
10
+ productStrengthPerDose: number;
11
+ doseUnit: DoseUnit;
12
+ }
13
+
14
+ export interface CalciumAlkalinityDosingResult {
15
+ action: DosingAction;
16
+ difference: number;
17
+ doseAmount: number;
18
+ estimatedWaterChangePercent: number;
19
+ input: CalciumAlkalinityDosingInput;
20
+ }
21
+
22
+ export type DosingError = "volume" | "value" | "strength";
23
+ export type CalculationResult =
24
+ | { ok: true; value: CalciumAlkalinityDosingResult }
25
+ | { ok: false; error: DosingError };
26
+
27
+ export const DEFAULT_DOSING_INPUT: CalciumAlkalinityDosingInput = {
28
+ parameter: "calcium",
29
+ volumeLitres: 100,
30
+ currentValue: 380,
31
+ targetValue: 420,
32
+ productStrengthPerDose: 2,
33
+ doseUnit: "ml",
34
+ };
35
+
36
+ const MAX_VOLUME_LITRES = 100_000;
37
+ const MAX_CALCIUM = 1_000;
38
+ const MAX_ALKALINITY = 40;
39
+ const MAX_STRENGTH = 10_000;
40
+
41
+ function inRange(value: number, minimum: number, maximum: number): boolean {
42
+ return Number.isFinite(value) && value >= minimum && value <= maximum;
43
+ }
44
+
45
+ function validate(input: CalciumAlkalinityDosingInput): DosingError | null {
46
+ if (!inRange(input.volumeLitres, 0.1, MAX_VOLUME_LITRES)) return "volume";
47
+ const maximum = input.parameter === "calcium" ? MAX_CALCIUM : MAX_ALKALINITY;
48
+ if (
49
+ !inRange(input.currentValue, 0, maximum) ||
50
+ !inRange(input.targetValue, 0, maximum)
51
+ )
52
+ return "value";
53
+ if (!inRange(input.productStrengthPerDose, 0.001, MAX_STRENGTH))
54
+ return "strength";
55
+ return null;
56
+ }
57
+
58
+ function result(
59
+ input: CalciumAlkalinityDosingInput,
60
+ values: Pick<
61
+ CalciumAlkalinityDosingResult,
62
+ "action" | "difference" | "doseAmount" | "estimatedWaterChangePercent"
63
+ >,
64
+ ): CalculationResult {
65
+ return { ok: true, value: { ...values, input } };
66
+ }
67
+
68
+ function increaseResult(
69
+ input: CalciumAlkalinityDosingInput,
70
+ difference: number,
71
+ ): CalculationResult {
72
+ const doseAmount =
73
+ (difference / input.productStrengthPerDose) * (input.volumeLitres / 100);
74
+ return result(input, {
75
+ action: "add",
76
+ difference,
77
+ doseAmount,
78
+ estimatedWaterChangePercent: 0,
79
+ });
80
+ }
81
+
82
+ function reductionResult(
83
+ input: CalciumAlkalinityDosingInput,
84
+ difference: number,
85
+ ): CalculationResult {
86
+ const estimatedWaterChangePercent =
87
+ (Math.abs(difference) / input.currentValue) * 100;
88
+ return result(input, {
89
+ action: "reduce",
90
+ difference,
91
+ doseAmount: 0,
92
+ estimatedWaterChangePercent,
93
+ });
94
+ }
95
+
96
+ export function calculateCalciumAlkalinityDosing(
97
+ input: CalciumAlkalinityDosingInput,
98
+ ): CalculationResult {
99
+ const error = validate(input);
100
+ if (error) return { ok: false, error };
101
+
102
+ const difference = input.targetValue - input.currentValue;
103
+ if (difference === 0)
104
+ return result(input, {
105
+ action: "target",
106
+ difference,
107
+ doseAmount: 0,
108
+ estimatedWaterChangePercent: 0,
109
+ });
110
+ if (difference > 0) return increaseResult(input, difference);
111
+ return reductionResult(input, difference);
112
+ }
@@ -0,0 +1,11 @@
1
+ ---
2
+ import { SEORenderer } from '@jjlmoya/utils-shared';
3
+ import { aquariumCalciumAlkalinityDosingCalculator } from './entry';
4
+ import type { KnownLocale } from '../../types';
5
+
6
+ const { locale = 'en' } = Astro.props as { locale?: KnownLocale };
7
+ const loader = aquariumCalciumAlkalinityDosingCalculator.i18n[locale] ?? aquariumCalciumAlkalinityDosingCalculator.i18n.en;
8
+ const content = loader === undefined ? undefined : await loader();
9
+ ---
10
+
11
+ {content && <SEORenderer content={{ locale, sections: content.seo }} />}
@@ -0,0 +1,44 @@
1
+ export interface AquariumCalciumAlkalinityDosingCalculatorUI {
2
+ [key: string]: string;
3
+ toolKicker: string;
4
+ resultTitle: string;
5
+ configureTitle: string;
6
+ parameterLabel: string;
7
+ calciumLabel: string;
8
+ alkalinityLabel: string;
9
+ calciumShortLabel: string;
10
+ alkalinityShortLabel: string;
11
+ volumeLabel: string;
12
+ currentLabel: string;
13
+ targetLabel: string;
14
+ productStrengthLabel: string;
15
+ productStrengthHint: string;
16
+ doseUnitLabel: string;
17
+ millilitresLabel: string;
18
+ gramsLabel: string;
19
+ volumeUnitLabel: string;
20
+ litresLabel: string;
21
+ gallonsLabel: string;
22
+ resultActionLabel: string;
23
+ addAction: string;
24
+ reduceAction: string;
25
+ targetAction: string;
26
+ emptyState: string;
27
+ doseLabel: string;
28
+ waterChangeLabel: string;
29
+ currentTargetLabel: string;
30
+ differenceLabel: string;
31
+ unitCalcium: string;
32
+ unitAlkalinity: string;
33
+ unitPercent: string;
34
+ addSummary: string;
35
+ reduceSummary: string;
36
+ targetSummary: string;
37
+ verificationNote: string;
38
+ sceneAdd: string;
39
+ sceneReduce: string;
40
+ sceneTarget: string;
41
+ invalidState: string;
42
+ resetLabel: string;
43
+ localOnlyLabel: string;
44
+ }
@@ -0,0 +1,68 @@
1
+ import type {
2
+ AquariumParameter,
3
+ CalciumAlkalinityDosingInput,
4
+ DosingAction,
5
+ } from "./logic";
6
+ import type { AquariumCalciumAlkalinityDosingCalculatorUI } from "./ui";
7
+
8
+ export function field(root: HTMLElement, name: string): HTMLInputElement {
9
+ return root.querySelector(`[data-field="${name}"]`) as HTMLInputElement;
10
+ }
11
+ export function numberValue(root: HTMLElement, name: string): number {
12
+ return Number(field(root, name).value);
13
+ }
14
+ export function format(number: number, maximumFractionDigits = 2): string {
15
+ return new Intl.NumberFormat("en-US", {
16
+ maximumFractionDigits,
17
+ minimumFractionDigits: 0,
18
+ }).format(number);
19
+ }
20
+ export function parameterUnit(
21
+ ui: AquariumCalciumAlkalinityDosingCalculatorUI,
22
+ parameter: AquariumParameter,
23
+ ): string {
24
+ return parameter === "calcium" ? ui.unitCalcium : ui.unitAlkalinity;
25
+ }
26
+ export function setText(
27
+ root: HTMLElement,
28
+ selector: string,
29
+ text: string,
30
+ ): void {
31
+ root.querySelectorAll(selector).forEach((element) => {
32
+ element.replaceChildren(document.createTextNode(text));
33
+ });
34
+ }
35
+ export function renderValuePath(
36
+ root: HTMLElement,
37
+ input: CalciumAlkalinityDosingInput,
38
+ unit: string,
39
+ ): void {
40
+ setText(root, '[data-output="current-value"]', format(input.currentValue));
41
+ setText(root, '[data-output="target-value"]', format(input.targetValue));
42
+ setText(root, '[data-output="value-unit"]', unit);
43
+ setText(root, '[data-output="value-unit-target"]', unit);
44
+ }
45
+ export function actionText(
46
+ ui: AquariumCalciumAlkalinityDosingCalculatorUI,
47
+ action: DosingAction,
48
+ ): string {
49
+ if (action === "add") return ui.addAction;
50
+ if (action === "reduce") return ui.reduceAction;
51
+ return ui.targetAction;
52
+ }
53
+ export function sceneText(
54
+ ui: AquariumCalciumAlkalinityDosingCalculatorUI,
55
+ action: DosingAction,
56
+ ): string {
57
+ if (action === "add") return ui.sceneAdd;
58
+ if (action === "reduce") return ui.sceneReduce;
59
+ return ui.sceneTarget;
60
+ }
61
+ export function summaryText(
62
+ ui: AquariumCalciumAlkalinityDosingCalculatorUI,
63
+ action: DosingAction,
64
+ ): string {
65
+ if (action === "add") return ui.addSummary;
66
+ if (action === "reduce") return ui.reduceSummary;
67
+ return ui.targetSummary;
68
+ }