@jjlmoya/utils-pets 1.20.0 → 1.22.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 (42) hide show
  1. package/package.json +1 -1
  2. package/src/category/index.ts +2 -1
  3. package/src/entries.ts +4 -1
  4. package/src/index.ts +1 -0
  5. package/src/tests/i18n_coverage.test.ts +5 -2
  6. package/src/tests/locale_completeness.test.ts +1 -1
  7. package/src/tests/qa-test-helpers.ts +32 -0
  8. package/src/tests/qa_bibliography_links.test.ts +40 -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 +100 -0
  12. package/src/tests/tool_validation.test.ts +3 -3
  13. package/src/tool/petCarrierCrateSizePlanner/bibliography.astro +6 -0
  14. package/src/tool/petCarrierCrateSizePlanner/bibliography.ts +12 -0
  15. package/src/tool/petCarrierCrateSizePlanner/component.astro +89 -0
  16. package/src/tool/petCarrierCrateSizePlanner/controller.ts +128 -0
  17. package/src/tool/petCarrierCrateSizePlanner/dom-views.ts +96 -0
  18. package/src/tool/petCarrierCrateSizePlanner/entry.ts +27 -0
  19. package/src/tool/petCarrierCrateSizePlanner/evaluator.ts +14 -0
  20. package/src/tool/petCarrierCrateSizePlanner/i18n/de.ts +171 -0
  21. package/src/tool/petCarrierCrateSizePlanner/i18n/en.ts +171 -0
  22. package/src/tool/petCarrierCrateSizePlanner/i18n/es.ts +171 -0
  23. package/src/tool/petCarrierCrateSizePlanner/i18n/fr.ts +171 -0
  24. package/src/tool/petCarrierCrateSizePlanner/i18n/id.ts +171 -0
  25. package/src/tool/petCarrierCrateSizePlanner/i18n/it.ts +171 -0
  26. package/src/tool/petCarrierCrateSizePlanner/i18n/ja.ts +171 -0
  27. package/src/tool/petCarrierCrateSizePlanner/i18n/ko.ts +171 -0
  28. package/src/tool/petCarrierCrateSizePlanner/i18n/nl.ts +171 -0
  29. package/src/tool/petCarrierCrateSizePlanner/i18n/pl.ts +171 -0
  30. package/src/tool/petCarrierCrateSizePlanner/i18n/pt.ts +171 -0
  31. package/src/tool/petCarrierCrateSizePlanner/i18n/ru.ts +171 -0
  32. package/src/tool/petCarrierCrateSizePlanner/i18n/sv.ts +171 -0
  33. package/src/tool/petCarrierCrateSizePlanner/i18n/tr.ts +171 -0
  34. package/src/tool/petCarrierCrateSizePlanner/i18n/zh.ts +171 -0
  35. package/src/tool/petCarrierCrateSizePlanner/index.ts +12 -0
  36. package/src/tool/petCarrierCrateSizePlanner/logic.test.ts +49 -0
  37. package/src/tool/petCarrierCrateSizePlanner/logic.ts +77 -0
  38. package/src/tool/petCarrierCrateSizePlanner/pet-carrier-crate-size-planner.css +639 -0
  39. package/src/tool/petCarrierCrateSizePlanner/seo.astro +14 -0
  40. package/src/tool/petCarrierCrateSizePlanner/storage.ts +31 -0
  41. package/src/tool/petCarrierCrateSizePlanner/ui.ts +59 -0
  42. package/src/tools.ts +3 -0
@@ -0,0 +1,96 @@
1
+ import type { CarrierResult, PetSpecies, UnitSystem } from './logic';
2
+ import { convertLength, convertWeight, formatInputValue } from './logic';
3
+ import { evaluateCarrier } from './evaluator';
4
+ import type { PetCarrierCrateSizePlannerUI } from './ui';
5
+
6
+ function escapeText(value: string): string {
7
+ return value.replace(/[&<>"']/g, (character) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[character] || character));
8
+ }
9
+
10
+ function formatMeasure(valueCm: number, unit: UnitSystem): string {
11
+ const value = convertLength(valueCm, 'metric', unit);
12
+ return `${formatInputValue(Number(value.toFixed(1)))} ${unit === 'metric' ? 'cm' : 'in'}`;
13
+ }
14
+
15
+ function formatWeight(valueKg: number, unit: UnitSystem): string {
16
+ const value = convertWeight(valueKg, 'metric', unit);
17
+ return `${formatInputValue(Number(value.toFixed(1)))} ${unit === 'metric' ? 'kg' : 'lb'}`;
18
+ }
19
+
20
+ interface DimensionArrow {
21
+ x1: number;
22
+ y1: number;
23
+ x2: number;
24
+ y2: number;
25
+ label: string;
26
+ className: string;
27
+ }
28
+
29
+ interface PetPlacement {
30
+ species: PetSpecies;
31
+ boxX: number;
32
+ boxY: number;
33
+ boxWidth: number;
34
+ boxHeight: number;
35
+ }
36
+
37
+ function dimensionArrow({ x1, y1, x2, y2, label, className }: DimensionArrow): string {
38
+ return `<line class="measure-line ${className}" x1="${x1}" y1="${y1}" x2="${x2}" y2="${y2}" /><text class="measure-label ${className}" x="${(x1 + x2) / 2}" y="${(y1 + y2) / 2 - 5}">${escapeText(label)}</text>`;
39
+ }
40
+
41
+ function petMarkup({ species, boxX, boxY, boxWidth, boxHeight }: PetPlacement): string {
42
+ const size = Math.min(72, boxWidth - 32, boxHeight - 18);
43
+ const offsetX = boxX + (boxWidth - size) / 2;
44
+ const offsetY = boxY + (boxHeight - size) / 2;
45
+ const figure = species === 'dog'
46
+ ? 'm19 3l-4 4l3 3l1-1l1 1l2-2l-3-3zM3 7L2 8l3 3v3l-1 1v6h2v-3l2-3h7v6h2V11l-3-3l-1 1H5z'
47
+ : 'm12 8l-1.33.09C9.81 7.07 7.4 4.5 5 4.5c0 0-1.97 2.96-.04 6.91c-.55.83-.89 1.26-.96 2.25l-1.93.29l.21.98l1.76-.26l.14.71l-1.57.94l.47.89l1.45-.89C5.68 18.76 8.59 20 12 20s6.32-1.24 7.47-3.68l1.45.89l.47-.89l-1.57-.94l.14-.71l1.76.26l.21-.98l-1.93-.29c-.07-.99-.41-1.42-.96-2.25C20.97 7.46 19 4.5 19 4.5c-2.4 0-4.81 2.57-5.67 3.59zm-3 3a1 1 0 0 1 1 1a1 1 0 0 1-1 1a1 1 0 0 1-1-1a1 1 0 0 1 1-1m6 0a1 1 0 0 1 1 1a1 1 0 0 1-1 1a1 1 0 0 1-1-1a1 1 0 0 1 1-1m-4 3h2l-.7 1.39c.2.64.76 1.11 1.45 1.11a1.5 1.5 0 0 0 1.5-1.5h.5a2 2 0 0 1-2 2c-.75 0-1.4-.41-1.75-1c-.35.59-1 1-1.75 1a2 2 0 0 1-2-2h.5a1.5 1.5 0 0 0 1.5 1.5c.69 0 1.25-.47 1.45-1.11z';
48
+ return `<svg class="pet-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false" x="${offsetX}" y="${offsetY}" width="${size}" height="${size}"><path d="${figure}" /></svg>`;
49
+ }
50
+
51
+ function sceneMarkup(result: CarrierResult, ui: PetCarrierCrateSizePlannerUI): string {
52
+ const { dimensions } = result;
53
+ const width = Math.min(300, Math.max(220, dimensions.lengthCm * 2.3));
54
+ const height = Math.min(150, Math.max(90, dimensions.heightCm * 1.5));
55
+ const boxX = 54;
56
+ const boxY = 50;
57
+ const boxRight = boxX + width;
58
+ const boxBottom = boxY + height;
59
+ const unit = result.input.unit;
60
+ return `<svg viewBox="0 0 430 230" role="img" aria-label="${escapeText(ui.blueprintLabel)}" class="carrier-blueprint">
61
+ <defs><marker id="measure-start" markerWidth="6" markerHeight="6" refX="3" refY="3" orient="auto"><path d="M6 0L0 3L6 6Z" fill="currentColor" /></marker><marker id="measure-end" markerWidth="6" markerHeight="6" refX="3" refY="3" orient="auto"><path d="M0 0L6 3L0 6Z" fill="currentColor" /></marker></defs>
62
+ <rect class="blueprint-paper" x="12" y="12" width="406" height="206" rx="18" />
63
+ <path class="blueprint-grid" d="M30 42H400M30 76H400M30 110H400M30 144H400M30 178H400M80 28V202M130 28V202M180 28V202M230 28V202M280 28V202M330 28V202M380 28V202" />
64
+ <rect class="carrier-outline" x="${boxX}" y="${boxY}" width="${width}" height="${height}" rx="16" />
65
+ <path class="carrier-door" d="M${boxRight - 4} ${boxY + 12}V${boxBottom - 12} M${boxRight - 18} ${boxY + 26}H${boxRight - 7} M${boxRight - 18} ${boxBottom - 26}H${boxRight - 7}" />
66
+ ${petMarkup({ species: result.input.species, boxX, boxY, boxWidth: width, boxHeight: height })}
67
+ ${dimensionArrow({ x1: boxX, y1: boxBottom + 22, x2: boxRight, y2: boxBottom + 22, label: formatMeasure(dimensions.lengthCm, unit), className: 'length' })}
68
+ ${dimensionArrow({ x1: boxRight + 24, y1: boxY, x2: boxRight + 24, y2: boxBottom, label: formatMeasure(dimensions.heightCm, unit), className: 'height' })}
69
+ <text class="blueprint-caption" x="30" y="34">${escapeText(ui.dimensionInside)}</text>
70
+ <text class="blueprint-badge" x="${boxX + 14}" y="${boxY + 20}">${escapeText(ui.checkMark)}</text>
71
+ </svg>`;
72
+ }
73
+
74
+ export function renderScene(element: HTMLElement, result: CarrierResult, ui: PetCarrierCrateSizePlannerUI): void {
75
+ element.innerHTML = sceneMarkup(result, ui);
76
+ }
77
+
78
+ function setText(root: HTMLElement, selector: string, value: string): void {
79
+ const element = root.querySelector<HTMLElement>(selector);
80
+ if (element) element.textContent = value;
81
+ }
82
+
83
+ export function renderEvaluation(root: HTMLElement, result: CarrierResult, ui: PetCarrierCrateSizePlannerUI): void {
84
+ const evaluation = evaluateCarrier(result, ui);
85
+ setText(root, '[data-status]', evaluation.label);
86
+ setText(root, '[data-status-detail]', evaluation.detail);
87
+ setText(root, '[data-length-result]', formatMeasure(result.dimensions.lengthCm, result.input.unit));
88
+ setText(root, '[data-width-result]', formatMeasure(result.dimensions.widthCm, result.input.unit));
89
+ setText(root, '[data-height-result]', formatMeasure(result.dimensions.heightCm, result.input.unit));
90
+ setText(root, '[data-weight-result]', formatWeight(result.input.weight, result.input.unit));
91
+ setText(root, '[data-mode-result]', result.input.mode === 'air' ? ui.modeAir : ui.modeCar);
92
+ const panel = root.querySelector<HTMLElement>('[data-result]');
93
+ if (panel) panel.hidden = false;
94
+ const status = root.querySelector<HTMLElement>('[data-status]');
95
+ if (status) status.dataset.tone = evaluation.tone;
96
+ }
@@ -0,0 +1,27 @@
1
+ import type { PetToolEntry, ToolLocaleContent } from '../../types';
2
+ import type { PetCarrierCrateSizePlannerUI } from './ui';
3
+
4
+ export type { PetCarrierCrateSizePlannerUI } from './ui';
5
+ export type PetCarrierCrateSizePlannerLocaleContent = ToolLocaleContent<PetCarrierCrateSizePlannerUI>;
6
+
7
+ export const petCarrierCrateSizePlanner: PetToolEntry<PetCarrierCrateSizePlannerUI> = {
8
+ id: 'pet-carrier-crate-size-planner',
9
+ icons: { bg: 'mdi:paw', fg: 'mdi:briefcase' },
10
+ i18n: {
11
+ de: () => import('./i18n/de').then((module) => module.content),
12
+ en: () => import('./i18n/en').then((module) => module.content),
13
+ es: () => import('./i18n/es').then((module) => module.content),
14
+ fr: () => import('./i18n/fr').then((module) => module.content),
15
+ id: () => import('./i18n/id').then((module) => module.content),
16
+ it: () => import('./i18n/it').then((module) => module.content),
17
+ ja: () => import('./i18n/ja').then((module) => module.content),
18
+ ko: () => import('./i18n/ko').then((module) => module.content),
19
+ nl: () => import('./i18n/nl').then((module) => module.content),
20
+ pl: () => import('./i18n/pl').then((module) => module.content),
21
+ pt: () => import('./i18n/pt').then((module) => module.content),
22
+ ru: () => import('./i18n/ru').then((module) => module.content),
23
+ sv: () => import('./i18n/sv').then((module) => module.content),
24
+ tr: () => import('./i18n/tr').then((module) => module.content),
25
+ zh: () => import('./i18n/zh').then((module) => module.content),
26
+ },
27
+ };
@@ -0,0 +1,14 @@
1
+ import type { PetCarrierCrateSizePlannerUI } from './ui';
2
+ import type { CarrierResult } from './logic';
3
+
4
+ export interface CarrierEvaluation {
5
+ label: string;
6
+ detail: string;
7
+ tone: 'calm' | 'review' | 'adjusted';
8
+ }
9
+
10
+ export function evaluateCarrier(result: CarrierResult, ui: PetCarrierCrateSizePlannerUI): CarrierEvaluation {
11
+ if (result.dimensions.isSnubNosedAdjusted) return { label: ui.statusSnub, detail: ui.resultDetail, tone: 'adjusted' };
12
+ if (result.airReviewRequired) return { label: ui.statusAirReview, detail: ui.resultDetail, tone: 'review' };
13
+ return { label: ui.statusComfort, detail: ui.resultDetail, tone: 'calm' };
14
+ }
@@ -0,0 +1,171 @@
1
+ import { bibliography } from '../bibliography';
2
+ import type { FAQPage, HowTo, SoftwareApplication, WithContext } from 'schema-dts';
3
+ import type { PetCarrierCrateSizePlannerLocaleContent } from '../entry';
4
+ import type { PetCarrierCrateSizePlannerUI } from '../ui';
5
+
6
+ const slug = 'haustier-transportbox-groessenplaner';
7
+ const title = 'Haustier Transportbox Größenplaner';
8
+ const description = 'Berechnen Sie die passenden Innenmaße einer Transportbox für Hund oder Katze anhand von sechs Körpermaßen, inklusive Flugreise-Check und Komfortliste.';
9
+
10
+ const ui: PetCarrierCrateSizePlannerUI = {
11
+ heroEyebrow: 'Messen Sie zuerst. Kaufen Sie mit Sicherheit.',
12
+ journeyHint: 'Wählen Sie das Haustier und die Reiseart, geben Sie die Maße ein und nutzen Sie die Skizze als Orientierung für den Innenraum der Transportbox.',
13
+ unitLegend: 'Anzeigeeinheiten',
14
+ speciesStep: 'Beginnen Sie mit dem Tier',
15
+ metricUnit: 'Metrisch',
16
+ imperialUnit: 'Imperial',
17
+ speciesLegend: 'Wer reist mit?',
18
+ speciesDog: 'Hund',
19
+ speciesCat: 'Katze',
20
+ modeLegend: 'Wie wird gereist?',
21
+ modeCar: 'Auto',
22
+ modeAir: 'Flugzeug',
23
+ measurementsLegend: 'Messen Sie Ihr Haustier',
24
+ noseTailLabel: 'Nasenspitze bis Rutenansatz',
25
+ noseTailHint: 'Messen Sie ohne die Krümmung der Rute',
26
+ elbowHeightLabel: 'Boden bis Ellenbogen',
27
+ shoulderWidthLabel: 'Schulterbreite',
28
+ standingHeightLabel: 'Höhe im Stehen',
29
+ beddingLabel: 'Dicke der Decke oder Einlage',
30
+ weightLabel: 'Gewicht des Haustiers',
31
+ cmUnit: 'cm',
32
+ inchUnit: 'in',
33
+ kgUnit: 'kg',
34
+ lbUnit: 'lb',
35
+ snubNosedLabel: 'Kurzschnäuzige Rasse',
36
+ snubNosedHint: 'Bei Flugreisen gelten IATA Anpassungen. Sprechen Sie mit Ihrem Tierarzt und der Fluggesellschaft.',
37
+ presetLegend: 'Starten Sie mit einem Profil',
38
+ presetCat: 'Katze',
39
+ presetSmallDog: 'Kleiner Hund',
40
+ presetMediumDog: 'Mittlerer Hund',
41
+ presetLargeDog: 'Großer Hund',
42
+ resultEyebrow: 'Empfohlener Messrahmen',
43
+ resultTitle: 'Ausreichend Raum zum Umdrehen',
44
+ resultDimensionLabel: 'Mindestinnenmaße für den Start',
45
+ lengthLabel: 'Länge',
46
+ widthLabel: 'Breite',
47
+ heightLabel: 'Höhe',
48
+ petWeightLabel: 'Gewicht des Tieres',
49
+ journeyLabel: 'Reiseart',
50
+ statusComfort: 'Komfort Basisstandard',
51
+ statusAirReview: 'Flugreise Prüfung',
52
+ statusSnub: 'Anpassung für Kurzschnauzer',
53
+ resultDetail: 'Prüfen Sie den tatsächlichen Innenraum, die Türöffnung, Belüftung und Bestimmungen vor dem Kauf oder der Abreise.',
54
+ checklistTitle: 'Die vier Körperprüfungen',
55
+ checklistStand: 'Das Tier kann aufrecht stehen und sitzen, ohne dass das Dach den Kopf berührt.',
56
+ checklistTurn: 'Das Tier kann sich im Stehen mühelos umdrehen.',
57
+ checklistLie: 'Das Tier kann in natürlicher Haltung auf der Decke liegen.',
58
+ checklistAirline: 'Bei Flugreisen sind Fluggesellschaftsregeln, Belüftung, Verriegelung und Etiketten zu prüfen.',
59
+ invalidInput: 'Bitte geben Sie für alle Maße und das Gewicht positive Werte ein.',
60
+ noteTitle: 'Nutzen Sie dies als Orientierung, nicht als Flugzulassung',
61
+ noteText: 'Bestimmungen variieren je nach Fluggesellschaft, Fahrzeug und Tier. Ein Tierarzt sollte die gesundheitliche Eignung prüfen, besonders bei stumpfschnäuzigen Rassen.',
62
+ methodTitle: 'Berechnungsmethode',
63
+ methodText: 'Die Berechnung basiert auf den IATA Richtlinien: Länge entspricht Nase bis Rutenansatz plus halbe Ellenbogenhöhe, Breite der doppelten Schulterbreite und Höhe der Stehhöhe plus Deckenstärke.',
64
+ blueprintLabel: 'Maßskizze der Transportbox mit Haustier und Führungslinien für Länge und Höhe',
65
+ dimensionInside: 'Innenabmessungen',
66
+ checkMark: 'OK',
67
+ };
68
+
69
+ const faq: PetCarrierCrateSizePlannerLocaleContent['faq'] = [
70
+ {
71
+ question: 'Wie messe ich meinen Hund oder meine Katze richtig für eine Transportbox?',
72
+ answer: 'Messen Sie von der Nasenspitze bis zum Rutenansatz, vom Boden bis zum Ellenbogengelenk, die breiteste Stelle der Schultern sowie die Höhe vom Boden bis zur Kopfspitze oder den Ohren. Die Dicke der Liegedecke wird separat hinzugerechnet.',
73
+ },
74
+ {
75
+ question: 'Welche Innenmaße berechnet dieser Planer?',
76
+ answer: 'Er berechnet die Mindestinnenlänge aus Körperlänge plus halber Ellenbogenhöhe, die Innenbreite als doppelte Schulterbreite und die Innenhöhe aus Stehhöhe plus Deckenstärke.',
77
+ },
78
+ {
79
+ question: 'Garantieren die Ergebnisse die Zulassung bei einer Fluggesellschaft?',
80
+ answer: 'Nein. Der Reisemodus wendet IATA Referenzwerte an, jedoch legt jede Fluggesellschaft eigene Bestimmungen fest. Prüfen Sie stets die aktuellen Vorgaben der Airline vor der Buchung.',
81
+ },
82
+ {
83
+ question: 'Warum benötigen kurzschnäuzige Rassen bei Flugreisen mehr Platz?',
84
+ answer: 'IATA Richtlinien empfehlen für brachycephale Rassen größere Transportboxen, um Überhitzung und Atembeschwerden vorzubeugen. Der Rechner berechnet einen Zuschlag von 10% zur Orientierung.',
85
+ },
86
+ {
87
+ question: 'Sollte eine Autobox so groß wie möglich sein?',
88
+ answer: 'Nein. Die Box muss ausreichend Platz zum Aufstehen, Sitzen, Drehen und Liegen bieten, sollte jedoch im Fahrzeug stabil gesichert werden können und nicht zu viel Spielraum bei Bremsmanövern lassen.',
89
+ },
90
+ ];
91
+
92
+ const howTo: PetCarrierCrateSizePlannerLocaleContent['howTo'] = [
93
+ { name: 'Reiseart wählen', text: 'Wählen Sie zwischen Auto und Flugzeug, um die entsprechenden Vorgaben zu berücksichtigen.' },
94
+ { name: 'Sechs Maße eingeben', text: 'Messen Sie Körperlänge, Ellenbogenhöhe, Schulterbreite, Stehhöhe, Deckendicke und Gewicht Ihres Tieres im ruhigen Zustand.' },
95
+ { name: 'Innenmaße ablesen', text: 'Nutzen Sie Länge, Breite und Höhe als Mindestmaße beim Vergleich kommerzieller Boxen.' },
96
+ { name: 'Bestimmungen prüfen', text: 'Prüfen Sie vor dem Kauf Verriegelung, Belüftung und Vorgaben von Fluggesellschaft oder Tierarzt.' },
97
+ ];
98
+
99
+ const seo: PetCarrierCrateSizePlannerLocaleContent['seo'] = [
100
+ {
101
+ type: 'summary',
102
+ title: 'Wichtige Hinweise zur Auswahl der passenden Transportbox',
103
+ items: [
104
+ 'Messen Sie das Tier anstelle sich nur am reinen Körpergewicht zu orientieren.',
105
+ 'Nutzen Sie reale Innenmaße zum Vergleich kommerzieller Transportboxen.',
106
+ 'Prüfen Sie Belüftung, Verriegelung und Stabilität des Behälters.',
107
+ 'Flugreisen erfordern besondere Vorbereitung und eine Tierarztberatung.',
108
+ ],
109
+ },
110
+ { type: 'title', text: 'So planen Sie die Abmessungen der Transportbox', level: 2 },
111
+ {
112
+ type: 'paragraph',
113
+ html: 'Eine geeignete Transportbox muss dem Tier vier Grundbewegungen ermöglichen: aufrechtes Stehen ohne den Kopf zu krümmen, aufrechtes Sitzen, müheloses Umdrehen um die eigene Achse und natürliches Liegen in entspannter Haltung. Dieser Rechner ermittelt auf Basis der individuellen Körpermaße die passenden Innenabmessungen für Länge, Breite und Höhe. Dies ist besonders nützlich, wenn Hersteller nur Außenmaße angeben oder die Box abgerundete Ecken aufweist.',
114
+ },
115
+ {
116
+ type: 'paragraph',
117
+ html: 'Die berechneten Werte stellen das reine nutzbare Innenmaß dar. Achten Sie beim Kauf darauf, dass dicke Liegedecken, Futternäpfe oder schräge Außenwände das tatsächliche Innenvolumen verringern können. Sollte ein Modell an einer Stelle knapp bemessen sein, empfiehlt sich die nächstgrößere Variante.',
118
+ },
119
+ { type: 'title', text: 'Bedeutung der einzelnen Körpermaße', level: 2 },
120
+ {
121
+ type: 'table',
122
+ headers: ['Körpermaß', 'Bedeutung für das Tier', 'Verwendung im Rechner'],
123
+ rows: [
124
+ ['Nase bis Rutenansatz', 'Bestimmt die Grundlänge ohne Rute.', 'Innenlänge'],
125
+ ['Boden bis Ellenbogen', 'Ermöglicht Bewegungsfreiheit beim Drehen.', 'Innenlänge'],
126
+ ['Schulterbreite', 'Garantiert ausreichende Breite an der stärksten Stelle.', 'Innenbreite'],
127
+ ['Stehhöhe', 'Schützt Kopf und Ohren vor dem Boxendach.', 'Innenhöhe'],
128
+ ['Deckendicke', 'Sichert die effektive Höhe nach Einlegen der Decke.', 'Innenhöhe'],
129
+ ['Körpergewicht', 'Dient der Überprüfung der maximalen Traglast.', 'Prüfung'],
130
+ ],
131
+ },
132
+ {
133
+ type: 'paragraph',
134
+ html: 'Die Berechnung basiert auf den offiziellen IATA Vorgaben für den Transport von Lebendtieren. Die Innenlänge entspricht der Körperlänge plus der halben Ellenbogenhöhe, die Breite der doppelten Schulterbreite und die Höhe der Stehhöhe zuzüglich der Deckendicke. Diese Parameter garantieren Wohlbefinden und Sicherheit während der gesamten Reise.',
135
+ },
136
+ { type: 'title', text: 'Besonderheiten bei Flugreisen und Stumpfschnauzen', level: 2 },
137
+ {
138
+ type: 'paragraph',
139
+ html: 'Eine reine Maßberechnung ersetzt nicht die formelle Freigabe durch die Fluggesellschaft. Faktoren wie Flugzeugtyp, Temperatur und Kennzeichnung sind entscheidend. Kurzschnäuzige Rassen benötigen wegen des Risikos von Hitzestress mehr Raum und Belüftung. Erkundigen Sie sich rechtzeitig vor Abflug bei Ihrer Airline und Ihrem Tierarzt.',
140
+ },
141
+ {
142
+ type: 'tip',
143
+ title: 'Praktische Passformprüfung im Alltag',
144
+ html: 'Setzen Sie das Tier in ruhiger Umgebung in die Box und beobachten Sie die Bewegungsfreiheit für einige Minuten. Das Tier sollte sich ungehindert drehen können. Wenn es eingeengt wirkt, wählen Sie eine Nummer größer.',
145
+ },
146
+ ];
147
+
148
+ const schemas: PetCarrierCrateSizePlannerLocaleContent['schemas'] = [
149
+ {
150
+ '@context': 'https://schema.org',
151
+ '@type': 'SoftwareApplication',
152
+ name: title,
153
+ description,
154
+ applicationCategory: 'LifestyleApplication',
155
+ operatingSystem: 'Web',
156
+ offers: { '@type': 'Offer', price: '0', priceCurrency: 'EUR' },
157
+ } as WithContext<SoftwareApplication>,
158
+ {
159
+ '@context': 'https://schema.org',
160
+ '@type': 'FAQPage',
161
+ mainEntity: faq.map((item) => ({ '@type': 'Question', name: item.question, acceptedAnswer: { '@type': 'Answer', text: item.answer } })),
162
+ } as WithContext<FAQPage>,
163
+ {
164
+ '@context': 'https://schema.org',
165
+ '@type': 'HowTo',
166
+ name: title,
167
+ step: howTo.map((step) => ({ '@type': 'HowToStep', name: step.name, text: step.text })),
168
+ } as WithContext<HowTo>,
169
+ ];
170
+
171
+ export const content: PetCarrierCrateSizePlannerLocaleContent = { slug, title, description, ui, seo, faq, bibliography, howTo, schemas };
@@ -0,0 +1,171 @@
1
+ import { bibliography } from '../bibliography';
2
+ import type { FAQPage, HowTo, SoftwareApplication, WithContext } from 'schema-dts';
3
+ import type { PetCarrierCrateSizePlannerLocaleContent } from '../entry';
4
+ import type { PetCarrierCrateSizePlannerUI } from '../ui';
5
+
6
+ const slug = 'pet-carrier-crate-size-planner';
7
+ const title = 'Pet Carrier Crate Size Planner';
8
+ const description = 'Estimate practical internal carrier dimensions for a dog or cat from six measurements, with a separate air travel review and a clear comfort checklist.';
9
+
10
+ const ui: PetCarrierCrateSizePlannerUI = {
11
+ heroEyebrow: 'Measure first. Buy with confidence.',
12
+ journeyHint: 'Choose the pet and journey, enter the measurements, then use the blueprint as a shopping starting point for the carrier interior.',
13
+ unitLegend: 'Display units',
14
+ speciesStep: 'Start with the animal',
15
+ metricUnit: 'Metric',
16
+ imperialUnit: 'Imperial',
17
+ speciesLegend: 'Who is traveling?',
18
+ speciesDog: 'Dog',
19
+ speciesCat: 'Cat',
20
+ modeLegend: 'Where is the journey?',
21
+ modeCar: 'Car',
22
+ modeAir: 'Air travel',
23
+ measurementsLegend: 'Measure the pet',
24
+ noseTailLabel: 'Nose to tail base',
25
+ noseTailHint: 'Measure without the tail curve',
26
+ elbowHeightLabel: 'Ground to elbow',
27
+ shoulderWidthLabel: 'Shoulder width',
28
+ standingHeightLabel: 'Standing height',
29
+ beddingLabel: 'Bedding thickness',
30
+ weightLabel: 'Pet weight',
31
+ cmUnit: 'cm',
32
+ inchUnit: 'in',
33
+ kgUnit: 'kg',
34
+ lbUnit: 'lb',
35
+ snubNosedLabel: 'Short nosed breed',
36
+ snubNosedHint: 'Air travel adds the IATA adjustment. Ask your veterinarian and airline about suitability.',
37
+ presetLegend: 'Start with a profile',
38
+ presetCat: 'Cat',
39
+ presetSmallDog: 'Small dog',
40
+ presetMediumDog: 'Medium dog',
41
+ presetLargeDog: 'Large dog',
42
+ resultEyebrow: 'Your measuring frame',
43
+ resultTitle: 'A room to turn in',
44
+ resultDimensionLabel: 'Minimum internal starting dimensions',
45
+ lengthLabel: 'Length',
46
+ widthLabel: 'Width',
47
+ heightLabel: 'Height',
48
+ petWeightLabel: 'Pet weight',
49
+ journeyLabel: 'Journey',
50
+ statusComfort: 'Comfort baseline',
51
+ statusAirReview: 'Air travel review',
52
+ statusSnub: 'Short nose adjustment',
53
+ resultDetail: 'Check the real carrier interior, door shape, ventilation, construction, and the operator rules before buying or traveling.',
54
+ checklistTitle: 'The four body checks',
55
+ checklistStand: 'The pet can stand and sit upright without the roof pressing down.',
56
+ checklistTurn: 'The pet can turn around normally while standing.',
57
+ checklistLie: 'The pet can lie in a natural position on the bedding.',
58
+ checklistAirline: 'For air travel, confirm airline limits, ventilation, secure fasteners, leak protection, and required labels.',
59
+ invalidInput: 'Enter positive values for every pet measurement and weight.',
60
+ noteTitle: 'Use this as a fit study, not a travel approval',
61
+ noteText: 'Carrier rules vary by airline, vehicle, route, animal, and container. A veterinarian can help assess health and travel suitability, especially for short nosed breeds or animals with breathing, anxiety, or mobility concerns.',
62
+ methodTitle: 'Method',
63
+ methodText: 'The internal baseline follows IATA guidance: length is nose to tail plus half the elbow height, width is twice the shoulder width, and height is standing height plus bedding. A 10% dimensional adjustment is applied when short nosed air travel is selected.',
64
+ blueprintLabel: 'A measured carrier blueprint showing the pet inside and the internal length and height guides',
65
+ dimensionInside: 'Interior dimensions',
66
+ checkMark: 'OK',
67
+ };
68
+
69
+ const faq: PetCarrierCrateSizePlannerLocaleContent['faq'] = [
70
+ {
71
+ question: 'How do I measure my dog or cat for a carrier?',
72
+ answer: 'Measure from the tip of the nose to the base of the tail, from the ground to the elbow, across the widest part of the shoulders, and from the ground to the top of the head or ear tip, whichever is higher. Measure the bedding thickness separately because it reduces usable height. Keep the animal standing naturally and use the largest realistic posture rather than a curled sleeping position.',
73
+ },
74
+ {
75
+ question: 'What carrier dimensions does this planner calculate?',
76
+ answer: 'It estimates internal length as nose to tail plus half the elbow height, internal width as twice the shoulder width, and internal height as standing height plus bedding. These are starting dimensions for a single animal. Door arches, sloped roofs, bowls, padding, and structural ribs can reduce usable space, so compare the result with the actual interior shape.',
77
+ },
78
+ {
79
+ question: 'Is the result enough to meet an airline rule?',
80
+ answer: 'No. The air option applies a sizing reference and keeps an airline review visible, but airlines and routes can impose additional limits. Confirm the current operator policy, accepted container construction, ventilation, labels, weight rules, documentation, and cabin or hold restrictions before travel. A carrier that fits the animal can still be rejected for another reason.',
81
+ },
82
+ {
83
+ question: 'Why does a short nosed breed receive a larger result for air travel?',
84
+ answer: 'IATA guidance indicates that short nosed breeds require a larger container. This planner applies a 10% dimensional adjustment as a visible planning allowance when that option is selected. It does not decide whether a particular animal should fly. Ask a veterinarian about respiratory risk and ask the airline whether the breed and route are accepted.',
85
+ },
86
+ {
87
+ question: 'Should a car carrier be as large as possible?',
88
+ answer: 'It should be large enough for the animal to stand, sit, turn, and lie naturally, while remaining secure in the vehicle. A very loose carrier can move during braking and may not work with the available restraint system. Check the vehicle instructions and use a carrier and restraint arrangement appropriate for the animal and journey.',
89
+ },
90
+ ];
91
+
92
+ const howTo: PetCarrierCrateSizePlannerLocaleContent['howTo'] = [
93
+ { name: 'Choose the journey', text: 'Select car or air travel. Air travel keeps an additional operator review visible because the fit calculation cannot approve a route or carrier.' },
94
+ { name: 'Take six measurements', text: 'Measure nose to tail base, elbow height, shoulder width, standing height, bedding thickness, and body weight while the animal is calm and standing naturally.' },
95
+ { name: 'Read the interior blueprint', text: 'Use the length, width, and height as minimum internal starting dimensions. Check the real carrier at its narrowest and lowest usable points.' },
96
+ { name: 'Verify the journey', text: 'Before purchase or departure, confirm the carrier construction, ventilation, restraint, handling, documentation, and current rules with the relevant airline, vehicle guidance, and veterinarian.' },
97
+ ];
98
+
99
+ const seo: PetCarrierCrateSizePlannerLocaleContent['seo'] = [
100
+ {
101
+ type: 'summary',
102
+ title: 'Find a carrier that gives your pet room to move',
103
+ items: [
104
+ 'Measure the animal instead of choosing a carrier from weight alone.',
105
+ 'Use the internal dimensions as a starting point for comparing real carriers.',
106
+ 'A correct fit still needs a construction, ventilation, restraint, and operator check.',
107
+ 'Air travel needs extra preparation, and a short nosed breed needs professional advice.',
108
+ ],
109
+ },
110
+ { type: 'title', text: 'How to use the pet carrier size planner', level: 2 },
111
+ {
112
+ type: 'paragraph',
113
+ html: 'A carrier is a small room that must support four ordinary movements: standing, sitting upright, turning around, and lying down naturally. This planner converts the body measurements that describe those movements into a practical internal length, width, and height. It is useful when a product listing gives an outside size, when a carrier has a sloped roof, or when a cat or dog is between common commercial sizes. Enter the dimensions in the unit system you use for shopping. The unit switch preserves the same physical animal while changing the displayed values.',
114
+ },
115
+ {
116
+ type: 'paragraph',
117
+ html: 'The result is deliberately an internal starting dimension, not an external product recommendation. Compare it with the usable space inside the exact carrier. A thick bed, raised floor, door frame, taper, divider, bowl, or structural rib can take away room even when the headline dimensions look generous. Measure the lowest roof point and the narrowest section that the animal must actually use. If a carrier barely meets one dimension, choose a different shape or size so the animal is not forced to crouch, twist, or lie against hardware.',
118
+ },
119
+ { type: 'title', text: 'What each measurement means', level: 2 },
120
+ {
121
+ type: 'table',
122
+ headers: ['Measurement', 'Why it matters', 'Used in'],
123
+ rows: [
124
+ ['Nose to tail base', 'Sets the main front to back space without depending on a curled posture.', 'Length'],
125
+ ['Ground to elbow', 'Adds turning and posture room to the length estimate.', 'Length'],
126
+ ['Shoulder width', 'Sets side to side clearance at the widest body area.', 'Width'],
127
+ ['Standing height', 'Protects the head and ears from the roof.', 'Height'],
128
+ ['Bedding thickness', 'Keeps the usable height honest after the bed is installed.', 'Height'],
129
+ ['Pet weight', 'Helps you check the carrier and vehicle or operator load information.', 'Review'],
130
+ ],
131
+ },
132
+ {
133
+ type: 'paragraph',
134
+ html: 'The formulas follow the dimension guidance published by the International Air Transport Association for dogs and cats: internal length is A plus one half of B, internal width is C multiplied by two, and internal height is D plus bedding. In that guidance, A is nose to tail base, B is ground to elbow, C is the greater of shoulder width or the widest point, and D is standing height to the top of the head or ear tip. The planner uses the same structure for a car fit study, while making clear that a car journey has different restraint and crash safety questions.',
135
+ },
136
+ { type: 'title', text: 'Air travel needs a second review', level: 2 },
137
+ {
138
+ type: 'paragraph',
139
+ html: 'A dimension calculation cannot certify a carrier for air travel. The current airline, route, aircraft, season, animal health, and container design all matter. IATA and USDA APHIS guidance describe additional concerns such as secure construction, ventilation, leak protection, handling, labels, food and water arrangements, and documentation. A soft carrier that fits under one seat may be unsuitable for another aircraft or airline. Ask the operator for its current policy before buying a carrier or arriving at the airport.',
140
+ },
141
+ {
142
+ type: 'tip',
143
+ title: 'The fit check is a real world movement test',
144
+ html: 'Place the animal in the carrier while calm and observe whether it can stand, sit upright, turn around, and lie naturally without its body pressing into the door, roof, bowl, or bedding. Stop and seek professional advice if breathing, anxiety, pain, weakness, or mobility is a concern. The calculator helps you compare dimensions; it cannot assess an animal or approve a journey.',
145
+ },
146
+ ];
147
+
148
+ const schemas: PetCarrierCrateSizePlannerLocaleContent['schemas'] = [
149
+ {
150
+ '@context': 'https://schema.org',
151
+ '@type': 'SoftwareApplication',
152
+ name: title,
153
+ description,
154
+ applicationCategory: 'LifestyleApplication',
155
+ operatingSystem: 'Web',
156
+ offers: { '@type': 'Offer', price: '0', priceCurrency: 'EUR' },
157
+ } as WithContext<SoftwareApplication>,
158
+ {
159
+ '@context': 'https://schema.org',
160
+ '@type': 'FAQPage',
161
+ mainEntity: faq.map((item) => ({ '@type': 'Question', name: item.question, acceptedAnswer: { '@type': 'Answer', text: item.answer } })),
162
+ } as WithContext<FAQPage>,
163
+ {
164
+ '@context': 'https://schema.org',
165
+ '@type': 'HowTo',
166
+ name: title,
167
+ step: howTo.map((step) => ({ '@type': 'HowToStep', name: step.name, text: step.text })),
168
+ } as WithContext<HowTo>,
169
+ ];
170
+
171
+ export const content: PetCarrierCrateSizePlannerLocaleContent = { slug, title, description, ui, seo, faq, bibliography, howTo, schemas };