@jjlmoya/utils-astronomy 1.40.0 → 1.41.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.
@@ -0,0 +1,52 @@
1
+ const getNodeText = (node: HTMLAnchorElement, selector: string): string =>
2
+ node.querySelector(selector)?.textContent ?? "";
3
+
4
+ const setText = (element: HTMLElement | null, text: string): void => {
5
+ element?.replaceChildren(document.createTextNode(text));
6
+ };
7
+
8
+ interface MissionElements {
9
+ title: HTMLElement | null;
10
+ description: HTMLElement | null;
11
+ status: HTMLElement | null;
12
+ link: HTMLAnchorElement | null;
13
+ }
14
+
15
+ const updateMission = (node: HTMLAnchorElement, mission: MissionElements, label: string): void => {
16
+ const title = getNodeText(node, "strong");
17
+ setText(mission.title, title);
18
+ setText(mission.description, getNodeText(node, "small"));
19
+ setText(mission.status, title);
20
+ mission.link?.setAttribute("href", node.href);
21
+ mission.link?.setAttribute("aria-label", label);
22
+ };
23
+
24
+ export function enhanceAstronomyHome(): void {
25
+ const home = document.querySelector<HTMLElement>("[data-astronomy-home]");
26
+ if (!home) return;
27
+ const nodes = [...home.querySelectorAll<HTMLAnchorElement>("[data-tool-node]")];
28
+ if (nodes.length === 0) return;
29
+ const routeButton = home.querySelector<HTMLButtonElement>("[data-route-button]");
30
+ const missionTitle = home.querySelector<HTMLElement>("[data-mission-title]");
31
+ const missionDescription = home.querySelector<HTMLElement>("[data-mission-description]");
32
+ const missionLink = home.querySelector<HTMLAnchorElement>("[data-mission-link]");
33
+ const missionStatus = home.querySelector<HTMLElement>("[data-mission-status]");
34
+ const labels = nodes.map((node) => node.getAttribute("aria-label") ?? "");
35
+ let activeIndex = 0;
36
+
37
+ const selectNode = (index: number, focus = false): void => {
38
+ const node = nodes[index];
39
+ if (!node) return;
40
+ activeIndex = index;
41
+ nodes.forEach((item, itemIndex) => item.classList.toggle("is-active", itemIndex === index));
42
+ updateMission(node, { title: missionTitle, description: missionDescription, status: missionStatus, link: missionLink }, labels[index] ?? "");
43
+ if (focus) node.focus({ preventScroll: false });
44
+ };
45
+
46
+ nodes.forEach((node, index) => {
47
+ node.addEventListener("focus", () => selectNode(index));
48
+ node.addEventListener("pointerenter", () => selectNode(index));
49
+ });
50
+ selectNode(0);
51
+ routeButton?.addEventListener("click", () => selectNode((activeIndex + 1) % nodes.length, true));
52
+ }
@@ -5,43 +5,44 @@ export const content: CategoryLocaleContent = {
5
5
  title: 'Herramientas de Astronomía',
6
6
  description: 'Calculadoras y simuladores gratuitos para astrónomos aficionados. Planifica tus sesiones de observación y astrofotografía.',
7
7
  seo: [
8
- { type: 'title', text: 'Astronomía Amateur: Herramientas para Explorar el Universo', level: 2 },
8
+ { type: 'title', text: 'Astronomía amateur: prepara la noche antes de salir', level: 2 },
9
9
  {
10
10
  type: 'paragraph',
11
- html: 'La astronomía aficionada es una de las ciencias ciudadanas más apasionantes del mundo. Con el equipo adecuado y las herramientas correctas para planificar, cualquier persona puede explorar galaxias a millones de años luz, contemplar nebulosas donde nacen nuevas estrellas o estudiar la superficie de planetas de nuestro sistema solar. Esta suite de <strong>calculadoras astronómicas gratuitas</strong> está diseñada para elevar tu experiencia como observador.',
11
+ html: 'La astronomía amateur no empieza cuando pegas el ojo al ocular. Empieza al mirar el cielo que tendrás, elegir qué quieres observar y comprobar qué puede hacer tu equipo. Estas <strong>herramientas astronómicas gratuitas</strong> convierten esa preparación en decisiones concretas: cuánto ampliar, qué detalle esperar y cuánto tiempo puedes exponer sin dibujar trazas con las estrellas.',
12
12
  },
13
13
  {
14
14
  type: 'paragraph',
15
- html: 'Desde la evaluación de la calidad de tu cielo con la escala de Bortle hasta el cálculo de la resolución óptica de tu telescopio, cada herramienta explica la fórmula que utiliza para que puedas planificar una sesión con expectativas realistas.',
15
+ html: 'No prometen un cielo perfecto ni sustituyen la experiencia. Te ayudan a llegar con menos dudas, aprovechar mejor una ventana de observación y entender por qué una noche funciona, o por qué conviene dejar el telescopio en casa y esperar a otra.',
16
16
  },
17
- { type: 'title', text: 'Contaminación Lumínica: El Enemigo Número Uno del Astrónomo', level: 2 },
17
+ { type: 'title', text: 'Primero, averigua qué cielo tienes', level: 2 },
18
18
  {
19
19
  type: 'paragraph',
20
- html: 'Vivimos en un mundo cada vez más iluminado. La <strong>contaminación lumínica</strong> crece a un ritmo del 10% anual y borra el cielo nocturno para más del 80% de la población mundial. La escala de Bortle, con sus 9 niveles, es el estándar internacional para cuantificar esta pérdida. Nuestro simulador de cielo oscuro te permite visualizar interactivamente cómo la luz artificial transforma el firmamento, desde el negro absoluto de un desierto remoto hasta el naranja opresivo de una metrópolis.',
20
+ html: 'La <strong>escala de Bortle</strong> pone nombre a algo que cualquier observador reconoce: la diferencia entre un cielo lavado por las farolas y uno donde la Vía Láctea vuelve a aparecer. El simulador de cielo oscuro recorre sus nueve niveles para que compares escenarios y sepas si la siguiente mejora está en el telescopio o en encontrar un lugar más oscuro.',
21
21
  },
22
- { type: 'title', text: 'Tu Telescopio: Entender sus Capacidades Reales', level: 2 },
22
+ { type: 'title', text: 'Después, mide lo que puede darte el equipo', level: 2 },
23
23
  {
24
24
  type: 'paragraph',
25
- html: 'El mercado está lleno de telescopios que prometen "ver Saturno con sus anillos" con cualquier apertura. La realidad es que la <strong>apertura del objetivo</strong> es el parámetro más crítico. Determina cuánta luz recolecta (magnitud límite) y cuánto detalle puede resolver (poder de resolución). Las calculadoras de alcance y resolución de esta sección te dan números concretos: qué catálogos de objetos están a tu alcance y cuánto detalle puedes esperar en las superficies planetarias.',
25
+ html: 'La apertura importa, pero no trabaja sola. La <strong>magnitud límite</strong> estima hasta dónde puede llegar tu telescopio; la resolución de Dawes y Rayleigh ayuda a entender cuándo dos estrellas están demasiado juntas para separarlas. El resultado nunca vence a la atmósfera, al enfoque ni a la turbulencia: sirve para poner expectativas realistas y observar con más criterio.',
26
26
  },
27
27
  {
28
28
  type: 'list',
29
29
  items: [
30
- '<strong>Escala de Bortle:</strong> Evalúa la calidad de tu cielo antes de desplazarte a un lugar de observación.',
31
- '<strong>Alcance del Telescopio:</strong> Calcula la magnitud límite de tu equipo en función de la apertura y el cielo.',
32
- '<strong>Regla de los 500 / NPF:</strong> Determina el tiempo de exposición máximo para astrofotografía sin trazas.',
33
- '<strong>Resolución Óptica:</strong> Conoce el límite de Dawes y Rayleigh de tu telescopio.',
30
+ '<strong>Cielo oscuro:</strong> Recorre la escala de Bortle y descubre cuánto cambia una misma noche con menos contaminación lumínica.',
31
+ '<strong>Magnitud límite:</strong> Calcula una referencia del alcance visual de tu telescopio a partir de su apertura.',
32
+ '<strong>Resolución:</strong> Usa Dawes y Rayleigh para saber cuándo un sistema doble puede empezar a separarse.',
33
+ '<strong>Oculares:</strong> Relaciona aumento, pupila de salida y campo real antes de cambiar de ocular.',
34
+ '<strong>Astrofotografía:</strong> Ajusta la exposición, la regla de los 500 o NPF y el apilado para mantener estrellas puntuales.',
34
35
  ],
35
36
  },
36
37
  {
37
38
  type: 'tip',
38
- title: 'El Primer Paso: Conoce tu Cielo',
39
- html: 'Antes de invertir en un telescopio más grande, considera desplazarte a un cielo más oscuro. Un telescopio de 150mm bajo un cielo Bortle 3 supera ampliamente a un instrumento de 300mm bajo un Bortle 7. La oscuridad del cielo multiplica el rendimiento de cualquier equipo.',
39
+ title: 'La mejora que no se compra con más apertura',
40
+ html: 'Antes de cambiar de telescopio, prueba el mismo objeto bajo un cielo más oscuro. Pasar de un entorno Bortle 7 a uno Bortle 3 puede revelar contraste, estrellas y estructuras que no faltaban en tu equipo: faltaba oscuridad.',
40
41
  },
41
- { type: 'title', text: 'Astrofotografía de Cielo Profundo: Captura el Cosmos', level: 2 },
42
+ { type: 'title', text: 'Si haces fotos, calcula antes de disparar', level: 2 },
42
43
  {
43
44
  type: 'paragraph',
44
- html: 'La astrofotografía ha democratizado el acceso al universo. Con una cámara DSLR estándar y un objetivo gran angular, puedes capturar la Vía Láctea en su esplendor. Con un telescopio y un seguidor ecuatorial, puedes fotografiar nebulosas y galaxias que superan lo que los mejores observatorios podían ver hace solo décadas. La clave está en el <strong>tiempo de exposición</strong>: demasiado corto y pierdes detalles tenues; demasiado largo y obtienes trazos de estrella por la rotación terrestre.',
45
+ html: 'En una toma fija, la rotación terrestre termina convirtiendo las estrellas en trazas. La regla de los 500 y la NPF ofrecen un tiempo de exposición inicial según la focal y la cámara; el apilado suma señal sin pedirle todo a una sola imagen. Son referencias, no leyes: revisa las esquinas, mira el histograma y ajusta en el campo.',
45
46
  },
46
47
  {
47
48
  type: 'stats',
@@ -0,0 +1,224 @@
1
+ ---
2
+ import { Icon } from "astro-icon/components";
3
+ import { SEORenderer } from "@jjlmoya/utils-shared";
4
+ import ProductionBreadcrumb from "../components/ProductionBreadcrumb.astro";
5
+ import ProductionPage from "./ProductionPage.astro";
6
+ import { getCategoryPath } from "../mfe/routes";
7
+ import { getCategoryUi } from "../mfe/category-ui";
8
+ import type { CategoryLocaleContent, KnownLocale } from "../types";
9
+ import "../category/astronomy-category-home.css";
10
+
11
+ interface CategoryTool {
12
+ id: string;
13
+ icon: string;
14
+ title: string;
15
+ description: string;
16
+ href: string;
17
+ }
18
+
19
+ interface Props {
20
+ locale: KnownLocale;
21
+ category: CategoryLocaleContent;
22
+ tools: CategoryTool[];
23
+ alternates: { language: KnownLocale; url: string }[];
24
+ image?: string | undefined;
25
+ }
26
+
27
+ const { locale, category, tools, alternates, image } = Astro.props;
28
+ const publicPath = getCategoryPath(locale, category.slug);
29
+ const ui = getCategoryUi(locale);
30
+ const firstTool = tools[0];
31
+ const constellationTools = tools;
32
+ const getToolPointSize = (count: number): string => {
33
+ if (count > 500) return "0.28rem";
34
+ if (count > 80) return "0.4rem";
35
+ return "0.72rem";
36
+ };
37
+ const toolPointSize = getToolPointSize(tools.length);
38
+ const getStarSize = (index: number): string => {
39
+ if (index % 11 === 0) return "0.28rem";
40
+ if (index % 4 === 0) return "0.16rem";
41
+ return "0.1rem";
42
+ };
43
+ const getStarOpacity = (index: number): string => {
44
+ if (index % 7 === 0) return "0.95";
45
+ if (index % 3 === 0) return "0.65";
46
+ return "0.38";
47
+ };
48
+ const stars = Array.from({ length: 72 }, (_, index) => ({
49
+ left: `${((index * 47) % 97) + 1}%`,
50
+ top: `${((index * 61) % 91) + 3}%`,
51
+ size: getStarSize(index),
52
+ opacity: getStarOpacity(index),
53
+ delay: `${index % 9}`,
54
+ }));
55
+ const getConstellationSide = (left: number, top: number): "left" | "right" | "bottom" | "top" => {
56
+ if (left > 62) return "right";
57
+ if (left < 38) return "left";
58
+ return top > 50 ? "bottom" : "top";
59
+ };
60
+ const handPlacedPositions = [
61
+ [55, 12],
62
+ [80, 28],
63
+ [88, 57],
64
+ [69, 81],
65
+ [43, 91],
66
+ [17, 73],
67
+ [11, 43],
68
+ [30, 20],
69
+ [63, 24],
70
+ [83, 73],
71
+ [25, 57],
72
+ [39, 11],
73
+ ] as const;
74
+ const constellationPositions = constellationTools.map((_, index) => {
75
+ const count = Math.max(constellationTools.length, 1);
76
+ const handPlaced = handPlacedPositions[index];
77
+ const drift = ((index * 17) % 13) - 6;
78
+ const angle = index * 2.399963 + drift * 0.025;
79
+ const radius = 12 + Math.sqrt((index + 0.5) / count) * 34 + drift * 0.25;
80
+ const left = handPlaced?.[0] ?? 50 + Math.cos(angle) * radius;
81
+ const top = handPlaced?.[1] ?? 50 + Math.sin(angle) * radius;
82
+ return {
83
+ left: `${left.toFixed(2)}%`,
84
+ top: `${top.toFixed(2)}%`,
85
+ side: getConstellationSide(left, top),
86
+ };
87
+ });
88
+ ---
89
+
90
+ <ProductionPage
91
+ title={category.title}
92
+ description={category.description}
93
+ {locale}
94
+ {publicPath}
95
+ {alternates}
96
+ {image}
97
+ >
98
+ <div class="astronomy-home" data-astronomy-home>
99
+ <div class="astronomy-page-stars" aria-hidden="true">
100
+ {stars.slice(0, 42).map((star) => (
101
+ <i
102
+ class="astronomy-page-star"
103
+ style={`--star-left: ${star.left}; --star-top: ${star.top}; --star-size: ${star.size}; --star-opacity: ${star.opacity}; --star-delay: ${star.delay}s;`}
104
+ ></i>
105
+ ))}
106
+ </div>
107
+ <div class="astronomy-shell">
108
+ <ProductionBreadcrumb locale={locale} currentTitle={category.title} />
109
+
110
+ <section class="astronomy-orbit" aria-labelledby="astronomy-title">
111
+ <div id="astronomy-sky" class:list={["astronomy-sky", tools.length > 12 && "astronomy-sky-dense"]} aria-label={category.title}>
112
+ <div class="astronomy-sky-stars" aria-hidden="true">
113
+ {stars.map((star) => (
114
+ <i
115
+ class="astronomy-star"
116
+ style={`--star-left: ${star.left}; --star-top: ${star.top}; --star-size: ${star.size}; --star-opacity: ${star.opacity}; --star-delay: ${star.delay}s;`}
117
+ ></i>
118
+ ))}
119
+ </div>
120
+ <div class="astronomy-sky-dust" aria-hidden="true"></div>
121
+ <div class="astronomy-sky-coordinates" aria-hidden="true">
122
+ <span>α 05h 55m 10s</span>
123
+ <span>δ +07° 24′ 25″</span>
124
+ </div>
125
+ <div class="astronomy-celestial-scene" aria-hidden="true">
126
+ <div class="astronomy-orbit-track astronomy-orbit-track-one"><span class="astronomy-orbit-body astronomy-orbit-body-one"></span></div>
127
+ <div class="astronomy-orbit-track astronomy-orbit-track-two"><span class="astronomy-orbit-body astronomy-orbit-body-two"></span></div>
128
+ <div class="astronomy-orbit-track astronomy-orbit-track-three"><span class="astronomy-orbit-body astronomy-orbit-body-three"></span></div>
129
+ <div class="astronomy-planet"><span></span></div>
130
+ </div>
131
+ <div class="astronomy-constellation-center" aria-hidden="true">
132
+ <span>ASTRONOMIA</span>
133
+ <b>*</b>
134
+ </div>
135
+
136
+ {constellationTools.map((tool, index) => (
137
+ <a
138
+ class:list={["astronomy-star-link", `astronomy-star-link-${constellationPositions[index]?.side ?? "top"}`]}
139
+ href={tool.href}
140
+ data-tool-node
141
+ data-tool-index={index}
142
+ style={`--node-left: ${constellationPositions[index]?.left}; --node-top: ${constellationPositions[index]?.top}; --node-size: ${toolPointSize};`}
143
+ aria-label={`${tool.title}: ${ui.openTool}`}
144
+ >
145
+ <span class="astronomy-star-link-point" aria-hidden="true"></span>
146
+ <span class="astronomy-star-link-label">
147
+ <span class="astronomy-star-link-icon" aria-hidden="true"><Icon name={tool.icon} /></span>
148
+ <b>{String(index + 1).padStart(2, "0")}</b>
149
+ <strong>{tool.title}</strong>
150
+ <small>{tool.description}</small>
151
+ </span>
152
+ </a>
153
+ ))}
154
+
155
+ <p class="astronomy-sky-caption">{String(tools.length).padStart(2, "0")} {ui.catalogLabel}</p>
156
+ </div>
157
+
158
+ <div class="astronomy-editorial">
159
+ <p class="astronomy-overline">{ui.observationEyebrow}</p>
160
+ <h1 id="astronomy-title">{category.title}</h1>
161
+ <p class="astronomy-lede">{category.description}</p>
162
+
163
+ {firstTool && (
164
+ <div class="astronomy-observation" data-mission-panel>
165
+ <div class="astronomy-observation-heading">
166
+ <span class="astronomy-observation-dot" aria-hidden="true"></span>
167
+ <p>{ui.missionLabel}</p>
168
+ </div>
169
+ <h2 data-mission-title>{firstTool.title}</h2>
170
+ <p data-mission-description>{firstTool.description}</p>
171
+ <div class="astronomy-observation-actions">
172
+ <button type="button" data-route-button aria-controls="astronomy-sky">
173
+ {ui.exploreRoute}<span aria-hidden="true">↗</span>
174
+ </button>
175
+ <a data-mission-link href={firstTool.href}>
176
+ {ui.openTool}<span aria-hidden="true">→</span>
177
+ </a>
178
+ </div>
179
+ <p class="astronomy-observation-status" aria-live="polite" data-mission-status>
180
+ {firstTool.title}
181
+ </p>
182
+ </div>
183
+ )}
184
+
185
+ <nav class="astronomy-tool-index" aria-label={category.title}>
186
+ <details open={tools.length <= 12}>
187
+ <summary class="astronomy-tool-index-title">
188
+ <span>{ui.toolIndexLabel}</span>
189
+ <span>{String(tools.length).padStart(2, "0")}</span>
190
+ </summary>
191
+ <ol>
192
+ {tools.map((tool, index) => (
193
+ <li>
194
+ <a href={tool.href} data-tool-index-link data-tool-index={index}>
195
+ <span>{String(index + 1).padStart(2, "0")}</span>
196
+ <strong>{tool.title}</strong>
197
+ <i aria-hidden="true">↗</i>
198
+ </a>
199
+ </li>
200
+ ))}
201
+ </ol>
202
+ </details>
203
+ </nav>
204
+ </div>
205
+ </section>
206
+
207
+ <section class="astronomy-field-notes" aria-labelledby="astronomy-notes-title">
208
+ <div class="astronomy-field-notes-title">
209
+ <span class="astronomy-overline">{ui.fieldNotesLabel}</span>
210
+ <h2 id="astronomy-notes-title">{ui.fieldNotesTitle}</h2>
211
+ </div>
212
+ <div class="astronomy-seo">
213
+ <SEORenderer content={{ locale, sections: category.seo }} />
214
+ </div>
215
+ </section>
216
+ </div>
217
+ </div>
218
+ </ProductionPage>
219
+
220
+ <script>
221
+ import { enhanceAstronomyHome } from "../category/astronomy-category-home";
222
+
223
+ enhanceAstronomyHome();
224
+ </script>
@@ -3,6 +3,13 @@ import type { KnownLocale } from '../types';
3
3
  export interface CategoryUi {
4
4
  useTool: string;
5
5
  openTool: string;
6
+ exploreRoute: string;
7
+ missionLabel: string;
8
+ catalogLabel: string;
9
+ observationEyebrow: string;
10
+ toolIndexLabel: string;
11
+ fieldNotesLabel: string;
12
+ fieldNotesTitle: string;
6
13
  relatedEyebrow: string;
7
14
  moreToolsIn: string;
8
15
  zoom: string;
@@ -14,21 +21,21 @@ export interface CategoryUi {
14
21
  }
15
22
 
16
23
  export const CATEGORY_UI: Record<KnownLocale, CategoryUi> = {
17
- es: { useTool: 'Usar herramienta', openTool: 'Abrir herramienta', relatedEyebrow: 'Sigue explorando', moreToolsIn: 'Más herramientas en', zoom: 'Zoom', zoomControls: 'Controles de zoom', zoomOut: 'Reducir zoom', resetZoom: 'Restablecer zoom', zoomIn: 'Aumentar zoom', breadcrumb: 'Migas de pan' },
18
- en: { useTool: 'Use tool', openTool: 'Open tool', relatedEyebrow: 'Keep exploring', moreToolsIn: 'More tools in', zoom: 'Zoom', zoomControls: 'Zoom controls', zoomOut: 'Zoom out', resetZoom: 'Reset zoom', zoomIn: 'Zoom in', breadcrumb: 'Breadcrumb' },
19
- fr: { useTool: "Utiliser l'outil", openTool: "Ouvrir l'outil", relatedEyebrow: 'Continuez à explorer', moreToolsIn: "Plus d'outils dans", zoom: 'Zoom', zoomControls: 'Commandes de zoom', zoomOut: 'Réduire le zoom', resetZoom: 'Réinitialiser le zoom', zoomIn: 'Augmenter le zoom', breadcrumb: 'Fil d’Ariane' },
20
- de: { useTool: 'Werkzeug verwenden', openTool: 'Werkzeug öffnen', relatedEyebrow: 'Mehr entdecken', moreToolsIn: 'Weitere Werkzeuge in', zoom: 'Zoom', zoomControls: 'Zoom-Steuerung', zoomOut: 'Verkleinern', resetZoom: 'Zoom zurücksetzen', zoomIn: 'Vergrößern', breadcrumb: 'Brotkrümelnavigation' },
21
- it: { useTool: 'Usa lo strumento', openTool: 'Apri lo strumento', relatedEyebrow: 'Continua a esplorare', moreToolsIn: 'Altri strumenti in', zoom: 'Zoom', zoomControls: 'Controlli zoom', zoomOut: 'Riduci zoom', resetZoom: 'Reimposta zoom', zoomIn: 'Aumenta zoom', breadcrumb: 'Breadcrumb' },
22
- pt: { useTool: 'Usar ferramenta', openTool: 'Abrir ferramenta', relatedEyebrow: 'Continue a explorar', moreToolsIn: 'Mais ferramentas em', zoom: 'Zoom', zoomControls: 'Controlos de zoom', zoomOut: 'Reduzir zoom', resetZoom: 'Repor zoom', zoomIn: 'Aumentar zoom', breadcrumb: 'Trilho de navegação' },
23
- nl: { useTool: 'Tool gebruiken', openTool: 'Tool openen', relatedEyebrow: 'Blijf ontdekken', moreToolsIn: 'Meer tools in', zoom: 'Zoom', zoomControls: 'Zoomknoppen', zoomOut: 'Uitzoomen', resetZoom: 'Zoom herstellen', zoomIn: 'Inzoomen', breadcrumb: 'Broodkruimelnavigatie' },
24
- sv: { useTool: 'Använd verktyget', openTool: 'Öppna verktyget', relatedEyebrow: 'Utforska mer', moreToolsIn: 'Fler verktyg i', zoom: 'Zoom', zoomControls: 'Zoomkontroller', zoomOut: 'Zooma ut', resetZoom: 'Återställ zoom', zoomIn: 'Zooma in', breadcrumb: 'Brödsmulenavigering' },
25
- pl: { useTool: 'Użyj narzędzia', openTool: 'Otwórz narzędzie', relatedEyebrow: 'Odkrywaj dalej', moreToolsIn: 'Więcej narzędzi w', zoom: 'Powiększenie', zoomControls: 'Sterowanie powiększeniem', zoomOut: 'Pomniejsz', resetZoom: 'Resetuj powiększenie', zoomIn: 'Powiększ', breadcrumb: 'Okruszki nawigacji' },
26
- id: { useTool: 'Gunakan alat', openTool: 'Buka alat', relatedEyebrow: 'Jelajahi lebih lanjut', moreToolsIn: 'Alat lainnya di', zoom: 'Zoom', zoomControls: 'Kontrol zoom', zoomOut: 'Perkecil', resetZoom: 'Atur ulang zoom', zoomIn: 'Perbesar', breadcrumb: 'Breadcrumb' },
27
- tr: { useTool: 'Aracı kullan', openTool: 'Aracı aç', relatedEyebrow: 'Keşfetmeye devam edin', moreToolsIn: 'Diğer araçlar:', zoom: 'Yakınlaştırma', zoomControls: 'Yakınlaştırma denetimleri', zoomOut: 'Uzaklaştır', resetZoom: 'Yakınlaştırmayı sıfırla', zoomIn: 'Yakınlaştır', breadcrumb: 'İçerik haritası' },
28
- ru: { useTool: 'Использовать инструмент', openTool: 'Открыть инструмент', relatedEyebrow: 'Продолжить изучение', moreToolsIn: 'Другие инструменты в', zoom: 'Масштаб', zoomControls: 'Управление масштабом', zoomOut: 'Уменьшить', resetZoom: 'Сбросить масштаб', zoomIn: 'Увеличить', breadcrumb: 'Навигационная цепочка' },
29
- ja: { useTool: 'ツールを使う', openTool: 'ツールを開く', relatedEyebrow: 'さらに探索', moreToolsIn: 'その他のツール:', zoom: 'ズーム', zoomControls: 'ズーム操作', zoomOut: '縮小', resetZoom: 'ズームをリセット', zoomIn: '拡大', breadcrumb: 'パンくずリスト' },
30
- ko: { useTool: '도구 사용', openTool: '도구 열기', relatedEyebrow: '더 탐색하기', moreToolsIn: '카테고리의 다른 도구:', zoom: '확대/축소', zoomControls: '확대/축소 컨트롤', zoomOut: '축소', resetZoom: '확대/축소 초기화', zoomIn: '확대', breadcrumb: '탐색 경로' },
31
- zh: { useTool: '使用工具', openTool: '打开工具', relatedEyebrow: '继续探索', moreToolsIn: '更多工具:', zoom: '缩放', zoomControls: '缩放控件', zoomOut: '缩小', resetZoom: '重置缩放', zoomIn: '放大', breadcrumb: '面包屑导航' },
24
+ es: { useTool: 'Usar herramienta', openTool: 'Abrir herramienta', exploreRoute: 'Apuntar al siguiente', missionLabel: 'Objetivo de observación', catalogLabel: 'estrellas', observationEyebrow: 'Cuaderno de observación · 01', toolIndexLabel: 'Todas las estrellas', fieldNotesLabel: 'Notas de campo', fieldNotesTitle: 'Para cuando el cielo pide más tiempo', relatedEyebrow: 'Sigue explorando', moreToolsIn: 'Más herramientas en', zoom: 'Zoom', zoomControls: 'Controles de zoom', zoomOut: 'Reducir zoom', resetZoom: 'Restablecer zoom', zoomIn: 'Aumentar zoom', breadcrumb: 'Migas de pan' },
25
+ en: { useTool: 'Use tool', openTool: 'Open tool', exploreRoute: 'Point to next', missionLabel: 'Observation target', catalogLabel: 'stars', observationEyebrow: 'Observation log · 01', toolIndexLabel: 'All stars', fieldNotesLabel: 'Field notes', fieldNotesTitle: 'For when the sky asks for more time', relatedEyebrow: 'Keep exploring', moreToolsIn: 'More tools in', zoom: 'Zoom', zoomControls: 'Zoom controls', zoomOut: 'Zoom out', resetZoom: 'Reset zoom', zoomIn: 'Zoom in', breadcrumb: 'Breadcrumb' },
26
+ fr: { useTool: "Utiliser l'outil", openTool: "Ouvrir l'outil", exploreRoute: 'Pointer vers la suivante', missionLabel: "Cible d'observation", catalogLabel: 'étoiles', observationEyebrow: "Carnet d'observation · 01", toolIndexLabel: 'Toutes les étoiles', fieldNotesLabel: 'Notes de terrain', fieldNotesTitle: 'Quand le ciel demande plus de temps', relatedEyebrow: 'Continuez à explorer', moreToolsIn: "Plus d'outils dans", zoom: 'Zoom', zoomControls: 'Commandes de zoom', zoomOut: 'Réduire zoom', resetZoom: 'Réinitialiser le zoom', zoomIn: 'Augmenter zoom', breadcrumb: 'Fil d’Ariane' },
27
+ de: { useTool: 'Werkzeug verwenden', openTool: 'Werkzeug öffnen', exploreRoute: 'Nächsten anpeilen', missionLabel: 'Beobachtungsziel', catalogLabel: 'Sterne', observationEyebrow: 'Beobachtungsnotiz · 01', toolIndexLabel: 'Alle Sterne', fieldNotesLabel: 'Feldnotizen', fieldNotesTitle: 'Wenn der Himmel nach mehr Zeit verlangt', relatedEyebrow: 'Mehr entdecken', moreToolsIn: 'Weitere Werkzeuge in', zoom: 'Zoom', zoomControls: 'Zoom-Steuerung', zoomOut: 'Verkleinern', resetZoom: 'Zoom zurücksetzen', zoomIn: 'Vergrößern', breadcrumb: 'Brotkrümelnavigation' },
28
+ it: { useTool: 'Usa lo strumento', openTool: 'Apri lo strumento', exploreRoute: 'Punta alla prossima', missionLabel: 'Obiettivo di osservazione', catalogLabel: 'stelle', observationEyebrow: 'Diario di osservazione · 01', toolIndexLabel: 'Tutte le stelle', fieldNotesLabel: 'Note di campo', fieldNotesTitle: 'Quando il cielo chiede più tempo', relatedEyebrow: 'Continua a esplorare', moreToolsIn: 'Altri strumenti in', zoom: 'Zoom', zoomControls: 'Controlli zoom', zoomOut: 'Riduci zoom', resetZoom: 'Reimposta zoom', zoomIn: 'Aumenta zoom', breadcrumb: 'Breadcrumb' },
29
+ pt: { useTool: 'Usar ferramenta', openTool: 'Abrir ferramenta', exploreRoute: 'Apontar para a próxima', missionLabel: 'Alvo de observação', catalogLabel: 'estrelas', observationEyebrow: 'Diário de observação · 01', toolIndexLabel: 'Todas as estrelas', fieldNotesLabel: 'Notas de campo', fieldNotesTitle: 'Quando o céu pede mais tempo', relatedEyebrow: 'Continue a explorar', moreToolsIn: 'Mais ferramentas em', zoom: 'Zoom', zoomControls: 'Controlos de zoom', zoomOut: 'Reduzir zoom', resetZoom: 'Repor zoom', zoomIn: 'Aumentar zoom', breadcrumb: 'Trilho de navegação' },
30
+ nl: { useTool: 'Tool gebruiken', openTool: 'Tool openen', exploreRoute: 'Volgende aanwijzen', missionLabel: 'Observatiedoel', catalogLabel: 'sterren', observationEyebrow: 'Observatienotitie · 01', toolIndexLabel: 'Alle sterren', fieldNotesLabel: 'Veldnotities', fieldNotesTitle: 'Wanneer de hemel om meer tijd vraagt', relatedEyebrow: 'Blijf ontdekken', moreToolsIn: 'Meer tools in', zoom: 'Zoom', zoomControls: 'Zoomknoppen', zoomOut: 'Uitzoomen', resetZoom: 'Zoom herstellen', zoomIn: 'Inzoomen', breadcrumb: 'Broodkruimelnavigatie' },
31
+ sv: { useTool: 'Använd verktyget', openTool: 'Öppna verktyget', exploreRoute: 'Peka på nästa', missionLabel: 'Observationsmål', catalogLabel: 'stjärnor', observationEyebrow: 'Observationslogg · 01', toolIndexLabel: 'Alla stjärnor', fieldNotesLabel: 'Fältanteckningar', fieldNotesTitle: 'När himlen ber om mer tid', relatedEyebrow: 'Utforska mer', moreToolsIn: 'Fler verktyg i', zoom: 'Zoom', zoomControls: 'Zoomkontroller', zoomOut: 'Zooma ut', resetZoom: 'Återställ zoom', zoomIn: 'Zooma in', breadcrumb: 'Brödsmulenavigering' },
32
+ pl: { useTool: 'Użyj narzędzia', openTool: 'Otwórz narzędzie', exploreRoute: 'Wskaż następną', missionLabel: 'Cel obserwacji', catalogLabel: 'gwiazd', observationEyebrow: 'Dziennik obserwacji · 01', toolIndexLabel: 'Wszystkie gwiazdy', fieldNotesLabel: 'Notatki terenowe', fieldNotesTitle: 'Gdy niebo prosi o więcej czasu', relatedEyebrow: 'Odkrywaj dalej', moreToolsIn: 'Więcej narzędzi w', zoom: 'Powiększenie', zoomControls: 'Sterowanie powiększeniem', zoomOut: 'Pomniejsz', resetZoom: 'Resetuj powiększenie', zoomIn: 'Powiększ', breadcrumb: 'Okruszki nawigacji' },
33
+ id: { useTool: 'Gunakan alat', openTool: 'Buka alat', exploreRoute: 'Arahkan ke berikutnya', missionLabel: 'Target pengamatan', catalogLabel: 'bintang', observationEyebrow: 'Catatan pengamatan · 01', toolIndexLabel: 'Semua bintang', fieldNotesLabel: 'Catatan lapangan', fieldNotesTitle: 'Saat langit meminta lebih banyak waktu', relatedEyebrow: 'Jelajahi lebih lanjut', moreToolsIn: 'Alat lainnya di', zoom: 'Zoom', zoomControls: 'Kontrol zoom', zoomOut: 'Perkecil', resetZoom: 'Atur ulang zoom', zoomIn: 'Perbesar', breadcrumb: 'Breadcrumb' },
34
+ tr: { useTool: 'Aracı kullan', openTool: 'Aracı aç', exploreRoute: 'Sonrakine yönel', missionLabel: 'Gözlem hedefi', catalogLabel: 'yıldız', observationEyebrow: 'Gözlem günlüğü · 01', toolIndexLabel: 'Tüm yıldızlar', fieldNotesLabel: 'Saha notları', fieldNotesTitle: 'Gökyüzü daha fazla zaman istediğinde', relatedEyebrow: 'Keşfetmeye devam edin', moreToolsIn: 'Diğer araçlar:', zoom: 'Yakınlaştırma', zoomControls: 'Yakınlaştırma denetimleri', zoomOut: 'Uzaklaştır', resetZoom: 'Yakınlaştırmayı sıfırla', zoomIn: 'Yakınlaştır', breadcrumb: 'İçerik haritası' },
35
+ ru: { useTool: 'Использовать инструмент', openTool: 'Открыть инструмент', exploreRoute: 'Навести на следующую', missionLabel: 'Цель наблюдения', catalogLabel: 'звёзд', observationEyebrow: 'Журнал наблюдений · 01', toolIndexLabel: 'Все звёзды', fieldNotesLabel: 'Полевые заметки', fieldNotesTitle: 'Когда небо просит больше времени', relatedEyebrow: 'Продолжить изучение', moreToolsIn: 'Другие инструменты в', zoom: 'Масштаб', zoomControls: 'Управление масштабом', zoomOut: 'Уменьшить', resetZoom: 'Сбросить масштаб', zoomIn: 'Увеличить', breadcrumb: 'Навигационная цепочка' },
36
+ ja: { useTool: 'ツールを使う', openTool: 'ツールを開く', exploreRoute: '次へ向ける', missionLabel: '観測対象', catalogLabel: '星', observationEyebrow: '観測ノート · 01', toolIndexLabel: 'すべての星', fieldNotesLabel: 'フィールドノート', fieldNotesTitle: '空がもっと時間を求めるとき', relatedEyebrow: 'さらに探索', moreToolsIn: 'その他のツール:', zoom: 'ズーム', zoomControls: 'ズーム操作', zoomOut: '縮小', resetZoom: 'ズームをリセット', zoomIn: '拡大', breadcrumb: 'パンくずリスト' },
37
+ ko: { useTool: '도구 사용', openTool: '도구 열기', exploreRoute: '다음 별 가리키기', missionLabel: '관측 대상', catalogLabel: '별', observationEyebrow: '관측 기록 · 01', toolIndexLabel: '모든 별', fieldNotesLabel: '현장 기록', fieldNotesTitle: '하늘이 더 많은 시간을 요구할 때', relatedEyebrow: '더 탐색하기', moreToolsIn: '카테고리의 다른 도구:', zoom: '확대/축소', zoomControls: '확대/축소 컨트롤', zoomOut: '축소', resetZoom: '확대/축소 초기화', zoomIn: '확대', breadcrumb: '탐색 경로' },
38
+ zh: { useTool: '使用工具', openTool: '打开工具', exploreRoute: '指向下一颗', missionLabel: '观测目标', catalogLabel: '颗星', observationEyebrow: '观测记录 · 01', toolIndexLabel: '所有星辰', fieldNotesLabel: '观测笔记', fieldNotesTitle: '当夜空需要更多时间', relatedEyebrow: '继续探索', moreToolsIn: '更多工具:', zoom: '缩放', zoomControls: '缩放控件', zoomOut: '缩小', resetZoom: '重置缩放', zoomIn: '放大', breadcrumb: '面包屑导航' },
32
39
  };
33
40
 
34
41
  export const getCategoryUi = (locale: KnownLocale): CategoryUi => CATEGORY_UI[locale];
@@ -1,5 +1,5 @@
1
1
  ---
2
- import ProductionCategoryPage from "../../../../layouts/ProductionCategoryPage.astro";
2
+ import AstronomyCategoryHome from "../../../../layouts/AstronomyCategoryHome.astro";
3
3
  import { ALL_TOOLS, astronomyCategory } from "../../../../index";
4
4
  import { LANGUAGE_CODES, type Language } from "../../../../i18n/languages";
5
5
  import { getCategoryNamespace, getCategoryPath, getUtilityNamespace, getUtilityPath } from "../../../../mfe/routes";
@@ -41,4 +41,4 @@ const tools = await Promise.all(ALL_TOOLS.map(async ({ entry }) => {
41
41
  }));
42
42
  ---
43
43
 
44
- <ProductionCategoryPage locale={locale} {category} {tools} {alternates} image={CATEGORY_OG_IMAGE} />
44
+ <AstronomyCategoryHome locale={locale} {category} {tools} {alternates} image={CATEGORY_OG_IMAGE} />
@@ -1,5 +1,5 @@
1
1
  ---
2
- import ProductionCategoryPage from "../../../layouts/ProductionCategoryPage.astro";
2
+ import AstronomyCategoryHome from "../../../layouts/AstronomyCategoryHome.astro";
3
3
  import { ALL_TOOLS, astronomyCategory } from "../../../index";
4
4
  import { LANGUAGE_CODES, type Language } from "../../../i18n/languages";
5
5
  import { getCategoryPath, getUtilityPath } from "../../../mfe/routes";
@@ -35,4 +35,4 @@ const tools = await Promise.all(ALL_TOOLS.map(async ({ entry }) => {
35
35
  }));
36
36
  ---
37
37
 
38
- <ProductionCategoryPage locale="es" {category} {tools} {alternates} image={CATEGORY_OG_IMAGE} />
38
+ <AstronomyCategoryHome locale="es" {category} {tools} {alternates} image={CATEGORY_OG_IMAGE} />