@jjlmoya/utils-astronomy 1.7.0 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/category/i18n/de.ts +1 -1
- package/src/category/i18n/id.ts +1 -1
- package/src/category/i18n/it.ts +1 -1
- package/src/category/i18n/nl.ts +1 -1
- package/src/category/i18n/pl.ts +1 -1
- package/src/category/i18n/pt.ts +1 -1
- package/src/category/i18n/sv.ts +1 -1
- package/src/category/index.ts +2 -1
- package/src/data.ts +1 -1
- package/src/index.ts +1 -1
- package/src/tests/category_validation.test.ts +73 -0
- package/src/tests/slug_language_code_format.test.ts +23 -0
package/package.json
CHANGED
package/src/category/i18n/de.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { CategoryLocaleContent } from '../../types';
|
|
2
2
|
|
|
3
3
|
export const content: CategoryLocaleContent = {
|
|
4
|
-
slug: '
|
|
4
|
+
slug: 'hobbyastronomie',
|
|
5
5
|
title: 'Astronomie Tools',
|
|
6
6
|
description: 'Kostenlose Rechner und Simulatoren für Hobbyastronomen. Planen Sie Ihre Beobachtungen und Astrofotografie.',
|
|
7
7
|
seo: [
|
package/src/category/i18n/id.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { CategoryLocaleContent } from '../../types';
|
|
2
2
|
|
|
3
3
|
export const content: CategoryLocaleContent = {
|
|
4
|
-
slug: 'astronomi',
|
|
4
|
+
slug: 'astronomi-amatir',
|
|
5
5
|
title: 'Alat Astronomi',
|
|
6
6
|
description: 'Kalkulator dan simulator gratis untuk astronom amatir. Rencanakan sesi observasi dan astrofotografi Anda.',
|
|
7
7
|
seo: [
|
package/src/category/i18n/it.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { CategoryLocaleContent } from '../../types';
|
|
2
2
|
|
|
3
3
|
export const content: CategoryLocaleContent = {
|
|
4
|
-
slug: 'astronomia',
|
|
4
|
+
slug: 'astronomia-amatoriale',
|
|
5
5
|
title: 'Strumenti di Astronomia',
|
|
6
6
|
description: 'Calcolatori e simulatori gratuiti per astronomi dilettanti. Pianifica le tue sessioni di osservazione e astrofotografia.',
|
|
7
7
|
seo: [
|
package/src/category/i18n/nl.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { CategoryLocaleContent } from '../../types';
|
|
2
2
|
|
|
3
3
|
export const content: CategoryLocaleContent = {
|
|
4
|
-
slug: 'astronomie',
|
|
4
|
+
slug: 'amateur-astronomie',
|
|
5
5
|
title: 'Astronomie instrumenten',
|
|
6
6
|
description: 'Gratis rekenmachines en simulators voor amateurasronomen. Plan uw observatiesessies en astrofotografie.',
|
|
7
7
|
seo: [
|
package/src/category/i18n/pl.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { CategoryLocaleContent } from '../../types';
|
|
2
2
|
|
|
3
3
|
export const content: CategoryLocaleContent = {
|
|
4
|
-
slug: 'astronomia',
|
|
4
|
+
slug: 'astronomia-amatorska',
|
|
5
5
|
title: 'Narzędzia Astronomiczne',
|
|
6
6
|
description: 'Darmowe kalkulatory i symulatory dla astronomów amatorów. Planuj sesje obserwacyjne i astrofotografię.',
|
|
7
7
|
seo: [
|
package/src/category/i18n/pt.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { CategoryLocaleContent } from '../../types';
|
|
2
2
|
|
|
3
3
|
export const content: CategoryLocaleContent = {
|
|
4
|
-
slug: 'astronomia',
|
|
4
|
+
slug: 'astronomia-amadora',
|
|
5
5
|
title: 'Ferramentas de Astronomia',
|
|
6
6
|
description: 'Calculadoras e simuladores gratuitos para astrônomos amadores. Planeje suas sessões de observação e astrofotografia.',
|
|
7
7
|
seo: [
|
package/src/category/i18n/sv.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { CategoryLocaleContent } from '../../types';
|
|
2
2
|
|
|
3
3
|
export const content: CategoryLocaleContent = {
|
|
4
|
-
slug: '
|
|
4
|
+
slug: 'amatorastronomi',
|
|
5
5
|
title: 'Astronomiverktyg',
|
|
6
6
|
description: 'Gratis kalkylatorer och simulatorer för amatörastronomer. Planera dina observationspass och astrofotografering.',
|
|
7
7
|
seo: [
|
package/src/category/index.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { deepSpaceScope } from '../tool/deepSpaceScope';
|
|
|
4
4
|
import { starExposureCalculator } from '../tool/starExposureCalculator';
|
|
5
5
|
import { telescopeResolution } from '../tool/telescopeResolution';
|
|
6
6
|
|
|
7
|
-
export const
|
|
7
|
+
export const toolsCategory: AstronomyCategoryEntry = {
|
|
8
8
|
icon: 'mdi:telescope',
|
|
9
9
|
tools: [bortleVisualizer, deepSpaceScope, starExposureCalculator, telescopeResolution],
|
|
10
10
|
i18n: {
|
|
@@ -25,4 +25,5 @@ export const astronomyCategory: AstronomyCategoryEntry = {
|
|
|
25
25
|
zh: () => import('./i18n/zh').then((m) => m.content),
|
|
26
26
|
},
|
|
27
27
|
};
|
|
28
|
+
export const astronomyCategory = toolsCategory;
|
|
28
29
|
|
package/src/data.ts
CHANGED
|
@@ -2,7 +2,7 @@ export { bortleVisualizer } from './tool/bortleVisualizer';
|
|
|
2
2
|
export { deepSpaceScope } from './tool/deepSpaceScope';
|
|
3
3
|
export { starExposureCalculator } from './tool/starExposureCalculator';
|
|
4
4
|
export { telescopeResolution } from './tool/telescopeResolution';
|
|
5
|
-
export { astronomyCategory } from './category';
|
|
5
|
+
export { toolsCategory as astronomyCategory } from './category';
|
|
6
6
|
|
|
7
7
|
export type { BortleVisualizerUI, BortleVisualizerLocaleContent } from './tool/bortleVisualizer';
|
|
8
8
|
export type { DeepSpaceScopeUI, DeepSpaceScopeLocaleContent } from './tool/deepSpaceScope';
|
package/src/index.ts
CHANGED
|
@@ -3,7 +3,7 @@ export * from './tool/deepSpaceScope';
|
|
|
3
3
|
export * from './tool/starExposureCalculator';
|
|
4
4
|
export * from './tool/telescopeResolution';
|
|
5
5
|
|
|
6
|
-
export { astronomyCategory } from './category';
|
|
6
|
+
export { toolsCategory as astronomyCategory } from './category';
|
|
7
7
|
export { default as AstronomyCategorySEO } from './category/seo.astro';
|
|
8
8
|
|
|
9
9
|
export type {
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { toolsCategory } from '../category/index';
|
|
3
|
+
import type { CategoryLocaleContent } from '../types';
|
|
4
|
+
|
|
5
|
+
const EXPECTED_LOCALES = [
|
|
6
|
+
'de', 'en', 'es', 'fr', 'id', 'it', 'ja', 'ko', 'nl', 'pl', 'pt', 'ru', 'sv', 'tr', 'zh'
|
|
7
|
+
];
|
|
8
|
+
|
|
9
|
+
const sharingLocales = ['ja', 'ko', 'zh'];
|
|
10
|
+
|
|
11
|
+
describe('Category Validation', () => {
|
|
12
|
+
it('should have all 15 required locales', () => {
|
|
13
|
+
const registeredLocales = Object.keys(toolsCategory.i18n);
|
|
14
|
+
EXPECTED_LOCALES.forEach((locale) => {
|
|
15
|
+
expect(
|
|
16
|
+
registeredLocales,
|
|
17
|
+
`Category is missing locale "${locale}"`,
|
|
18
|
+
).toContain(locale);
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
describe('Category Slug Validation', () => {
|
|
23
|
+
it('every locale should have a unique, translated slug and follow format rules', async () => {
|
|
24
|
+
const slugs = new Map<string, string>();
|
|
25
|
+
const locales = Object.keys(toolsCategory.i18n);
|
|
26
|
+
|
|
27
|
+
let enSlug = '';
|
|
28
|
+
if (locales.includes('en')) {
|
|
29
|
+
const enLoader = toolsCategory.i18n['en' as keyof typeof toolsCategory.i18n];
|
|
30
|
+
const enContent = (await enLoader?.()) as CategoryLocaleContent;
|
|
31
|
+
enSlug = enContent.slug;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
for (const locale of locales) {
|
|
35
|
+
const loader = toolsCategory.i18n[locale as keyof typeof toolsCategory.i18n];
|
|
36
|
+
const content = (await loader?.()) as CategoryLocaleContent;
|
|
37
|
+
|
|
38
|
+
expect(
|
|
39
|
+
content.slug,
|
|
40
|
+
`Category locale "${locale}" has an invalid slug ("${content.slug}"). Slugs must be transliterated (only a-z, 0-9, and -).`,
|
|
41
|
+
).toMatch(/^[a-z0-9-]+$/);
|
|
42
|
+
|
|
43
|
+
expect(
|
|
44
|
+
content.slug,
|
|
45
|
+
`Category locale "${locale}" slug ("${content.slug}") cannot end with a 2-letter language code (e.g., -ja, -ru, -ko).`,
|
|
46
|
+
).not.toMatch(/-[a-z]{2}$/);
|
|
47
|
+
|
|
48
|
+
if (locale !== 'en') {
|
|
49
|
+
if (sharingLocales.includes(locale)) {
|
|
50
|
+
expect(
|
|
51
|
+
content.slug,
|
|
52
|
+
`Category locale "${locale}" must use the same slug as "en" ("${enSlug}").`,
|
|
53
|
+
).toBe(enSlug);
|
|
54
|
+
} else {
|
|
55
|
+
expect(
|
|
56
|
+
content.slug,
|
|
57
|
+
`Category locale "${locale}" has the same slug as "en" ("${enSlug}"). Cada slug tiene que estar en su propio idioma`,
|
|
58
|
+
).not.toBe(enSlug);
|
|
59
|
+
|
|
60
|
+
if (slugs.has(content.slug)) {
|
|
61
|
+
const previousLocale = slugs.get(content.slug);
|
|
62
|
+
expect(
|
|
63
|
+
false,
|
|
64
|
+
`Category locales "${locale}" and "${previousLocale}" share the same slug ("${content.slug}"). Cada slug tiene que estar en su propia idioma`,
|
|
65
|
+
).toBe(true);
|
|
66
|
+
}
|
|
67
|
+
slugs.set(content.slug, locale);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { ALL_TOOLS } from '../tools';
|
|
3
|
+
import type { ToolLocaleContent } from '../types';
|
|
4
|
+
|
|
5
|
+
describe('Slug Language Code Format Validation', () => {
|
|
6
|
+
ALL_TOOLS.forEach((tool) => {
|
|
7
|
+
describe(`Tool: ${tool.entry.id}`, () => {
|
|
8
|
+
it('slug should not end with 2-letter language codes like -ja, -ru, -ko', async () => {
|
|
9
|
+
const locales = Object.keys(tool.entry.i18n);
|
|
10
|
+
|
|
11
|
+
for (const locale of locales) {
|
|
12
|
+
const loader = tool.entry.i18n[locale as keyof typeof tool.entry.i18n];
|
|
13
|
+
const content = (await loader?.()) as ToolLocaleContent;
|
|
14
|
+
|
|
15
|
+
expect(
|
|
16
|
+
content.slug,
|
|
17
|
+
`Tool "${tool.entry.id}" locale "${locale}" slug ("${content.slug}") cannot end with a 2-letter language code (e.g., -ja, -ru, -ko).`,
|
|
18
|
+
).not.toMatch(/-[a-z]{2}$/);
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
});
|