lightnet 4.0.7 → 4.1.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/CHANGELOG.md +25 -0
- package/README.md +1 -1
- package/exports/content.ts +5 -7
- package/package.json +13 -13
- package/src/api/versions.ts +1 -1
- package/src/astro-integration/config.ts +15 -25
- package/src/components/CategoriesSection.astro +2 -2
- package/src/components/MediaGallerySection.astro +4 -9
- package/src/components/MediaList.astro +13 -13
- package/src/content/content-schema.ts +5 -292
- package/src/content/get-categories.ts +26 -29
- package/src/content/get-languages.ts +13 -26
- package/src/content/get-media-collections.ts +1 -1
- package/src/content/get-media-items.ts +1 -1
- package/src/content/get-media-types.ts +21 -14
- package/src/content/query-media-items.ts +2 -1
- package/src/content/schema/category.ts +40 -0
- package/src/content/schema/media-collection.ts +31 -0
- package/src/content/schema/media-item.ts +137 -0
- package/src/content/schema/media-type.ts +90 -0
- package/src/i18n/locals.d.ts +22 -0
- package/src/i18n/locals.ts +3 -1
- package/src/i18n/record-translation.ts +74 -0
- package/src/i18n/resolve-language.ts +12 -5
- package/src/i18n/translate-map.ts +129 -19
- package/src/i18n/translate.ts +38 -0
- package/src/i18n/translation-map-schema.ts +17 -0
- package/src/i18n/translations/TRANSLATION-STATUS.md +13 -41
- package/src/i18n/translations/ar.yml +1 -0
- package/src/i18n/translations/bn.yml +1 -0
- package/src/i18n/translations/de.yml +1 -0
- package/src/i18n/translations/es.yml +1 -0
- package/src/i18n/translations/fi.yml +1 -0
- package/src/i18n/translations/fr.yml +1 -0
- package/src/i18n/translations/hi.yml +1 -0
- package/src/i18n/translations/kk.yml +3 -0
- package/src/i18n/translations/pt.yml +1 -0
- package/src/i18n/translations/ru.yml +1 -0
- package/src/i18n/translations/uk.yml +1 -0
- package/src/i18n/translations/ur.yml +1 -0
- package/src/i18n/translations/zh.yml +1 -0
- package/src/i18n/translations.ts +5 -2
- package/src/layouts/Page.astro +3 -4
- package/src/layouts/components/Footer.astro +72 -10
- package/src/layouts/components/LanguagePicker.astro +2 -2
- package/src/layouts/components/PageNavigation.astro +17 -21
- package/src/layouts/components/PageTitle.astro +4 -13
- package/src/pages/details-page/DefaultDetailsPage.astro +2 -15
- package/src/pages/details-page/components/AudioPanel.astro +5 -4
- package/src/pages/details-page/components/ContentSection.astro +5 -4
- package/src/pages/details-page/components/MediaCollection.astro +2 -6
- package/src/pages/details-page/components/main-details/OpenButton.astro +20 -18
- package/src/pages/details-page/components/more-details/Categories.astro +3 -5
- package/src/pages/details-page/components/more-details/Languages.astro +7 -3
- package/src/pages/details-page/utils/create-content-metadata.ts +8 -22
- package/src/pages/search-page/api/search.ts +1 -1
- package/src/pages/search-page/components/SearchFilter.astro +5 -5
- package/src/pages/search-page/components/SearchList.astro +22 -19
- package/src/astro-integration/validators/validate-inline-translations.ts +0 -51
|
@@ -9,28 +9,24 @@ import Menu from "./Menu.astro"
|
|
|
9
9
|
import MenuItem from "./MenuItem.astro"
|
|
10
10
|
|
|
11
11
|
const currentPath = Astro.url.pathname
|
|
12
|
-
const { t,
|
|
12
|
+
const { t, tConfigField, currentLocale } = Astro.locals.i18n
|
|
13
13
|
|
|
14
|
-
const items = (config.mainMenu ?? []).map(
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
isActive,
|
|
31
|
-
}
|
|
32
|
-
},
|
|
33
|
-
)
|
|
14
|
+
const items = (config.mainMenu ?? []).map(({ href, label, requiresLocale }) => {
|
|
15
|
+
const isExternal = isExternalUrl(href)
|
|
16
|
+
const path =
|
|
17
|
+
isExternal || !requiresLocale ? href : localizePath(currentLocale, href)
|
|
18
|
+
const isActive =
|
|
19
|
+
!isExternal &&
|
|
20
|
+
(currentPath === localizePath(currentLocale, href) ||
|
|
21
|
+
currentPath === localizePath(currentLocale, `${href}/`) ||
|
|
22
|
+
currentPath === href)
|
|
23
|
+
return {
|
|
24
|
+
path,
|
|
25
|
+
isExternal,
|
|
26
|
+
labelText: tConfigField(label, config),
|
|
27
|
+
isActive,
|
|
28
|
+
}
|
|
29
|
+
})
|
|
34
30
|
---
|
|
35
31
|
|
|
36
32
|
<nav class="-me-3 flex items-center">
|
|
@@ -14,18 +14,14 @@ function getWidth(logo: ImageMetadata) {
|
|
|
14
14
|
return Math.floor(size * Math.max(1, logo.width / logo.height))
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
const { currentLocale,
|
|
17
|
+
const { currentLocale, tConfigField } = Astro.locals.i18n
|
|
18
18
|
|
|
19
19
|
const logoAlt = (() => {
|
|
20
20
|
if (config.logo?.alt) {
|
|
21
|
-
return
|
|
22
|
-
path: ["config", "logo", "alt"],
|
|
23
|
-
})
|
|
21
|
+
return tConfigField(config.logo.alt, config)
|
|
24
22
|
}
|
|
25
23
|
if (config.logo?.replacesTitle) {
|
|
26
|
-
return
|
|
27
|
-
path: ["config", "title"],
|
|
28
|
-
})
|
|
24
|
+
return tConfigField(config.title, config)
|
|
29
25
|
}
|
|
30
26
|
return ""
|
|
31
27
|
})()
|
|
@@ -53,10 +49,5 @@ const logoAlt = (() => {
|
|
|
53
49
|
loading="eager"
|
|
54
50
|
/>
|
|
55
51
|
))
|
|
56
|
-
}{
|
|
57
|
-
!config.logo?.replacesTitle &&
|
|
58
|
-
tMap(config.title, {
|
|
59
|
-
path: ["config", "title"],
|
|
60
|
-
})
|
|
61
|
-
}</a
|
|
52
|
+
}{!config.logo?.replacesTitle && tConfigField(config.title, config)}</a
|
|
62
53
|
>
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
---
|
|
2
|
-
import type { TranslationMap } from "../../i18n/translate-map"
|
|
3
2
|
import ContentSection from "./components/ContentSection.astro"
|
|
4
3
|
import DescriptionSection from "./components/DescriptionSection.astro"
|
|
5
4
|
import DetailsPage from "./components/DetailsPage.astro"
|
|
@@ -10,25 +9,13 @@ import MoreDetailsSection from "./components/MoreDetailsSection.astro"
|
|
|
10
9
|
|
|
11
10
|
export type Props = {
|
|
12
11
|
mediaId: string
|
|
13
|
-
openActionLabel?: TranslationMap
|
|
14
12
|
}
|
|
15
|
-
|
|
16
|
-
const defaultOpenActionLabel = Object.fromEntries(
|
|
17
|
-
Astro.locals.i18n.locales.map((locale) => [
|
|
18
|
-
locale,
|
|
19
|
-
Astro.locals.i18n.t("ln.details.open", { lng: locale }),
|
|
20
|
-
]),
|
|
21
|
-
)
|
|
22
|
-
const { mediaId, openActionLabel = defaultOpenActionLabel } = Astro.props
|
|
13
|
+
const { mediaId } = Astro.props
|
|
23
14
|
---
|
|
24
15
|
|
|
25
16
|
<DetailsPage mediaId={mediaId}>
|
|
26
17
|
<MainDetailsSection mediaId={mediaId}>
|
|
27
|
-
<OpenButton
|
|
28
|
-
className="mt-8"
|
|
29
|
-
mediaId={mediaId}
|
|
30
|
-
openActionLabel={openActionLabel}
|
|
31
|
-
/>
|
|
18
|
+
<OpenButton className="mt-8" mediaId={mediaId} />
|
|
32
19
|
</MainDetailsSection>
|
|
33
20
|
<DescriptionSection mediaId={mediaId} />
|
|
34
21
|
<ContentSection mediaId={mediaId} />
|
|
@@ -9,13 +9,14 @@ interface Props {
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
const { mediaId, className } = Astro.props
|
|
12
|
-
const {
|
|
12
|
+
const { tContentField } = Astro.locals.i18n
|
|
13
13
|
|
|
14
14
|
const item = await getMediaItem(mediaId)
|
|
15
15
|
|
|
16
|
-
const content = item.data.content.map((
|
|
17
|
-
createContentMetadata(
|
|
18
|
-
|
|
16
|
+
const content = item.data.content.map(({ url, label }) =>
|
|
17
|
+
createContentMetadata({
|
|
18
|
+
url,
|
|
19
|
+
labelText: label && tContentField(label, item),
|
|
19
20
|
}),
|
|
20
21
|
)
|
|
21
22
|
---
|
|
@@ -30,11 +30,12 @@ const item = await getMediaItem(mediaId)
|
|
|
30
30
|
if (item.data.content.length < minimumItems) {
|
|
31
31
|
return
|
|
32
32
|
}
|
|
33
|
-
const { t,
|
|
33
|
+
const { t, tContentField, direction } = Astro.locals.i18n
|
|
34
34
|
|
|
35
|
-
const content = item.data.content.map((
|
|
36
|
-
createContentMetadata(
|
|
37
|
-
|
|
35
|
+
const content = item.data.content.map(({ url, label }) =>
|
|
36
|
+
createContentMetadata({
|
|
37
|
+
url,
|
|
38
|
+
labelText: label && tContentField(label, item),
|
|
38
39
|
}),
|
|
39
40
|
)
|
|
40
41
|
const typeIcons: Record<UrlType, LucideIcon> = {
|
|
@@ -26,7 +26,7 @@ const items = (await getEntries(collection.data.mediaItems)).map((item) => ({
|
|
|
26
26
|
if (items.length < 2) {
|
|
27
27
|
return
|
|
28
28
|
}
|
|
29
|
-
const { t,
|
|
29
|
+
const { t, tContentField } = Astro.locals.i18n
|
|
30
30
|
---
|
|
31
31
|
|
|
32
32
|
<section class="mx-auto mt-16 max-w-screen-md px-4 md:mt-20 md:px-8">
|
|
@@ -34,11 +34,7 @@ const { t, tMap } = Astro.locals.i18n
|
|
|
34
34
|
{t("ln.details.part-of-collection")}
|
|
35
35
|
</h2>
|
|
36
36
|
<h3 class="mb-6 text-lg font-bold text-gray-700 sm:mb-8">
|
|
37
|
-
{
|
|
38
|
-
tMap(collection.data.label, {
|
|
39
|
-
path: ["media-collections", collectionId, "label"],
|
|
40
|
-
})
|
|
41
|
-
}
|
|
37
|
+
{tContentField(collection.data.label, collection)}
|
|
42
38
|
</h3>
|
|
43
39
|
<MediaList items={items} />
|
|
44
40
|
</section>
|
|
@@ -2,20 +2,33 @@
|
|
|
2
2
|
import { ExternalLinkIcon } from "lucide-react"
|
|
3
3
|
|
|
4
4
|
import { getMediaItem } from "../../../../content/get-media-items"
|
|
5
|
-
import
|
|
5
|
+
import { getMediaType } from "../../../../content/get-media-types"
|
|
6
6
|
import { createContentMetadata } from "../../utils/create-content-metadata"
|
|
7
7
|
|
|
8
8
|
interface Props {
|
|
9
9
|
mediaId: string
|
|
10
|
-
openActionLabel: TranslationMap
|
|
11
10
|
className?: string
|
|
12
11
|
}
|
|
13
|
-
const { mediaId,
|
|
14
|
-
const {
|
|
12
|
+
const { mediaId, className } = Astro.props
|
|
13
|
+
const { tContentField, t } = Astro.locals.i18n
|
|
15
14
|
const item = await getMediaItem(mediaId)
|
|
16
|
-
const
|
|
17
|
-
|
|
15
|
+
const { url, label } = item.data.content[0]
|
|
16
|
+
const content = createContentMetadata({
|
|
17
|
+
url,
|
|
18
|
+
labelText: label && tContentField(label, item),
|
|
18
19
|
})
|
|
20
|
+
const mediaType = await getMediaType(item.data.type.id)
|
|
21
|
+
|
|
22
|
+
const getOpenActionLabel = () => {
|
|
23
|
+
const defaultLabel = t("ln.details.open")
|
|
24
|
+
if (mediaType.data.detailsPage?.layout !== "default") {
|
|
25
|
+
return defaultLabel
|
|
26
|
+
}
|
|
27
|
+
if (!mediaType.data.detailsPage.openActionLabel) {
|
|
28
|
+
return defaultLabel
|
|
29
|
+
}
|
|
30
|
+
return tContentField(mediaType.data.detailsPage.openActionLabel, mediaType)
|
|
31
|
+
}
|
|
19
32
|
---
|
|
20
33
|
|
|
21
34
|
<a
|
|
@@ -26,16 +39,5 @@ const content = createContentMetadata(item.data.content[0], tMap, {
|
|
|
26
39
|
class:list={[className]}
|
|
27
40
|
>
|
|
28
41
|
{content.isExternal && <ExternalLinkIcon className="shrink-0" />}
|
|
29
|
-
{
|
|
30
|
-
content.canBeOpened
|
|
31
|
-
? tMap(openActionLabel, {
|
|
32
|
-
path: [
|
|
33
|
-
"media-types",
|
|
34
|
-
item.data.type.id,
|
|
35
|
-
"detailsPage",
|
|
36
|
-
"openActionLabel",
|
|
37
|
-
],
|
|
38
|
-
})
|
|
39
|
-
: Astro.locals.i18n.t("ln.details.download")
|
|
40
|
-
}
|
|
42
|
+
{content.canBeOpened ? getOpenActionLabel() : t("ln.details.download")}
|
|
41
43
|
</a>
|
|
@@ -10,10 +10,10 @@ interface Props {
|
|
|
10
10
|
|
|
11
11
|
const item = await getMediaItem(Astro.props.mediaId)
|
|
12
12
|
|
|
13
|
-
const { t,
|
|
13
|
+
const { t, tContentField, currentLocale } = Astro.locals.i18n
|
|
14
14
|
|
|
15
15
|
const categories = await Promise.all(
|
|
16
|
-
item.data.categories?.map(({ id }) => getCategory(id)) ?? [],
|
|
16
|
+
item.data.categories?.map(({ id }) => getCategory(id, tContentField)) ?? [],
|
|
17
17
|
)
|
|
18
18
|
---
|
|
19
19
|
|
|
@@ -29,9 +29,7 @@ const categories = await Promise.all(
|
|
|
29
29
|
category: category.id,
|
|
30
30
|
})}
|
|
31
31
|
>
|
|
32
|
-
{
|
|
33
|
-
path: ["categories", category.id, "label"],
|
|
34
|
-
})}
|
|
32
|
+
{category.labelText}
|
|
35
33
|
</a>
|
|
36
34
|
</li>
|
|
37
35
|
))}
|
|
@@ -14,12 +14,16 @@ const item = await getMediaItem(Astro.props.mediaId)
|
|
|
14
14
|
const { mediaId } = Astro.props
|
|
15
15
|
const translations = await getTranslations(mediaId)
|
|
16
16
|
|
|
17
|
-
const { t,
|
|
18
|
-
const currentLanguage = resolveTranslatedLanguage(
|
|
17
|
+
const { t, tConfigField, currentLocale } = Astro.locals.i18n
|
|
18
|
+
const currentLanguage = resolveTranslatedLanguage(
|
|
19
|
+
item.data.language,
|
|
20
|
+
tConfigField,
|
|
21
|
+
)
|
|
19
22
|
const translationLanguages = await Promise.all(
|
|
20
23
|
translations.map(async (translation) => ({
|
|
21
24
|
...translation,
|
|
22
|
-
labelText: resolveTranslatedLanguage(translation.language,
|
|
25
|
+
labelText: resolveTranslatedLanguage(translation.language, tConfigField)
|
|
26
|
+
.labelText,
|
|
23
27
|
})),
|
|
24
28
|
)
|
|
25
29
|
---
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
type TranslateMapFn,
|
|
3
|
-
type TranslationMap,
|
|
4
|
-
} from "../../../i18n/translate-map"
|
|
5
1
|
import { isExternalUrl } from "../../../utils/urls"
|
|
6
2
|
|
|
7
3
|
export type UrlType =
|
|
@@ -47,17 +43,13 @@ const KNOWN_EXTENSIONS: Record<
|
|
|
47
43
|
docx: { type: "text" },
|
|
48
44
|
} as const
|
|
49
45
|
|
|
50
|
-
export function createContentMetadata(
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
},
|
|
58
|
-
tMap: TranslateMapFn,
|
|
59
|
-
context: { path: (string | number)[] },
|
|
60
|
-
) {
|
|
46
|
+
export function createContentMetadata({
|
|
47
|
+
url,
|
|
48
|
+
labelText: customLabel,
|
|
49
|
+
}: {
|
|
50
|
+
url: string
|
|
51
|
+
labelText?: string
|
|
52
|
+
}) {
|
|
61
53
|
const isExternal = isExternalUrl(url)
|
|
62
54
|
const path = isExternal ? new URL(url).pathname : url
|
|
63
55
|
|
|
@@ -72,13 +64,7 @@ export function createContentMetadata(
|
|
|
72
64
|
? lastPathSegment.slice(0, -(extension.length + 1))
|
|
73
65
|
: undefined
|
|
74
66
|
|
|
75
|
-
const labelText =
|
|
76
|
-
(customLabel &&
|
|
77
|
-
tMap(customLabel, {
|
|
78
|
-
path: [...context.path, "label"],
|
|
79
|
-
})) ??
|
|
80
|
-
fileName ??
|
|
81
|
-
linkName
|
|
67
|
+
const labelText = customLabel ?? fileName ?? linkName
|
|
82
68
|
const type = KNOWN_EXTENSIONS[extension]?.type ?? "link"
|
|
83
69
|
const canBeOpened =
|
|
84
70
|
!hasExtension || !!KNOWN_EXTENSIONS[extension]?.canBeOpened
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { APIRoute } from "astro"
|
|
2
2
|
import { getImage } from "astro:assets"
|
|
3
3
|
|
|
4
|
-
import type { MediaItemEntry } from "../../../content/content-schema"
|
|
5
4
|
import { getMediaItems } from "../../../content/get-media-items"
|
|
5
|
+
import type { MediaItemEntry } from "../../../content/schema/media-item"
|
|
6
6
|
import { markdownToText } from "../../../utils/markdown"
|
|
7
7
|
import type { SearchItem } from "./search-response"
|
|
8
8
|
|
|
@@ -2,22 +2,22 @@
|
|
|
2
2
|
import config from "virtual:lightnet/config"
|
|
3
3
|
|
|
4
4
|
import { getUsedCategories } from "../../../content/get-categories"
|
|
5
|
-
import {
|
|
5
|
+
import { getContentLanguages } from "../../../content/get-languages"
|
|
6
6
|
import { getUsedMediaTypes } from "../../../content/get-media-types"
|
|
7
7
|
import { prepareI18nConfig } from "../../../i18n/react/prepare-i18n-config"
|
|
8
8
|
import SearchFilterReact from "./SearchFilter.tsx"
|
|
9
9
|
|
|
10
|
-
const { currentLocale,
|
|
10
|
+
const { currentLocale, tConfigField, tContentField } = Astro.locals.i18n
|
|
11
11
|
|
|
12
|
-
const categories = (await getUsedCategories(currentLocale,
|
|
12
|
+
const categories = (await getUsedCategories(currentLocale, tContentField)).map(
|
|
13
13
|
({ id, labelText }) => ({ id, labelText }),
|
|
14
14
|
)
|
|
15
15
|
|
|
16
|
-
const mediaTypes = (await getUsedMediaTypes(currentLocale,
|
|
16
|
+
const mediaTypes = (await getUsedMediaTypes(currentLocale, tContentField)).map(
|
|
17
17
|
({ id, labelText }) => ({ id, labelText }),
|
|
18
18
|
)
|
|
19
19
|
|
|
20
|
-
const languages = (await
|
|
20
|
+
const languages = (await getContentLanguages(currentLocale, tConfigField)).map(
|
|
21
21
|
({ code: id, labelText }) => ({ id, labelText }),
|
|
22
22
|
)
|
|
23
23
|
|
|
@@ -2,42 +2,45 @@
|
|
|
2
2
|
import { getCollection } from "astro:content"
|
|
3
3
|
|
|
4
4
|
import { getUsedCategories } from "../../../content/get-categories"
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import { getContentLanguages } from "../../../content/get-languages"
|
|
6
|
+
import { getTranslatedMediaTypes } from "../../../content/get-media-types"
|
|
7
7
|
import { prepareI18nConfig } from "../../../i18n/react/prepare-i18n-config"
|
|
8
8
|
import SearchListReact from "./SearchList.tsx"
|
|
9
9
|
|
|
10
|
-
const { currentLocale,
|
|
10
|
+
const { currentLocale, tContentField, tConfigField } = Astro.locals.i18n
|
|
11
11
|
|
|
12
12
|
const categories: Record<string, string> = {}
|
|
13
|
-
for (const { id, labelText } of await getUsedCategories(
|
|
13
|
+
for (const { id, labelText } of await getUsedCategories(
|
|
14
|
+
currentLocale,
|
|
15
|
+
tContentField,
|
|
16
|
+
)) {
|
|
14
17
|
categories[id] = labelText
|
|
15
18
|
}
|
|
16
19
|
|
|
17
20
|
const mediaTypes = Object.fromEntries(
|
|
18
|
-
(await
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
21
|
+
(await getTranslatedMediaTypes(currentLocale, tContentField)).map(
|
|
22
|
+
({ labelText, icon, coverImageStyle, id }) => [
|
|
23
|
+
id,
|
|
24
|
+
{
|
|
25
|
+
labelText,
|
|
26
|
+
icon,
|
|
27
|
+
coverImageStyle,
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
),
|
|
28
31
|
)
|
|
29
32
|
|
|
30
|
-
const
|
|
33
|
+
const contentLanguages = await getContentLanguages(currentLocale, tConfigField)
|
|
31
34
|
const languages = Object.fromEntries(
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
{ direction
|
|
35
|
+
contentLanguages.map(({ code, direction, labelText }) => [
|
|
36
|
+
code,
|
|
37
|
+
{ direction, labelText },
|
|
35
38
|
]),
|
|
36
39
|
)
|
|
37
40
|
|
|
38
41
|
const mediaItemsTotal = (await getCollection("media")).length
|
|
39
42
|
|
|
40
|
-
const showLanguage =
|
|
43
|
+
const showLanguage = contentLanguages.length > 1
|
|
41
44
|
|
|
42
45
|
const i18nConfig = prepareI18nConfig(Astro.locals.i18n, [
|
|
43
46
|
"ln.search.no-results",
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import { z } from "astro/zod"
|
|
2
|
-
|
|
3
|
-
export const validateInlineTranslations = (
|
|
4
|
-
config: {
|
|
5
|
-
title: Record<string, string>
|
|
6
|
-
languages: { label: Record<string, string> }[]
|
|
7
|
-
mainMenu?: { label: Record<string, string> }[]
|
|
8
|
-
logo?: { alt?: Record<string, string> }
|
|
9
|
-
},
|
|
10
|
-
locales: string[],
|
|
11
|
-
defaultLocale: string,
|
|
12
|
-
ctx: z.RefinementCtx,
|
|
13
|
-
) => {
|
|
14
|
-
const validateInlineTranslation = (
|
|
15
|
-
inlineTranslation: Record<string, string> | undefined,
|
|
16
|
-
path: (string | number)[],
|
|
17
|
-
) => {
|
|
18
|
-
if (!inlineTranslation) {
|
|
19
|
-
return
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
if (!(defaultLocale in inlineTranslation)) {
|
|
23
|
-
ctx.addIssue({
|
|
24
|
-
code: "custom",
|
|
25
|
-
message: `Missing translation for default locale "${defaultLocale}"`,
|
|
26
|
-
path: [...path, defaultLocale],
|
|
27
|
-
})
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
for (const locale of Object.keys(inlineTranslation)) {
|
|
31
|
-
if (locales.includes(locale)) {
|
|
32
|
-
continue
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
ctx.addIssue({
|
|
36
|
-
code: "custom",
|
|
37
|
-
message: `Invalid locale "${locale}". Inline translations only support configured site locales: ${locales.join(", ")}`,
|
|
38
|
-
path: [...path, locale],
|
|
39
|
-
})
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
validateInlineTranslation(config.title, ["title"])
|
|
44
|
-
for (const [index, language] of config.languages.entries()) {
|
|
45
|
-
validateInlineTranslation(language.label, ["languages", index, "label"])
|
|
46
|
-
}
|
|
47
|
-
for (const [index, link] of (config.mainMenu ?? []).entries()) {
|
|
48
|
-
validateInlineTranslation(link.label, ["mainMenu", index, "label"])
|
|
49
|
-
}
|
|
50
|
-
validateInlineTranslation(config.logo?.alt, ["logo", "alt"])
|
|
51
|
-
}
|