stack-site-builder 1.23.2 → 1.24.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 +56 -0
- package/README.md +16 -1
- package/package.json +1 -1
- package/src/components/CardsHome.astro +2 -2
- package/src/components/DeckView.astro +2 -3
- package/src/components/Home.astro +2 -2
- package/src/i18n/ui.ts +63 -12
- package/src/layouts/BaseLayout.astro +10 -6
- package/src/lib/articles.ts +3 -2
- package/src/lib/concepts.ts +3 -2
- package/src/lib/courses.ts +3 -2
- package/src/lib/locales.ts +30 -0
- package/src/lib/pages.ts +3 -2
- package/src/lib/papers.ts +3 -2
- package/src/lib/products.ts +3 -2
- package/src/lib/slides.ts +3 -2
- package/src/lib/stacks.ts +3 -2
- package/src/pages/[...lang]/[page].astro +2 -2
- package/src/pages/[...lang]/article/[...id].astro +2 -2
- package/src/pages/[...lang]/article/category/[id].astro +2 -2
- package/src/pages/[...lang]/article/index.astro +2 -3
- package/src/pages/[...lang]/categories/[id].astro +2 -2
- package/src/pages/[...lang]/concept/[...id].astro +2 -2
- package/src/pages/[...lang]/concept/category/[id].astro +2 -2
- package/src/pages/[...lang]/concept/index.astro +2 -3
- package/src/pages/[...lang]/course/[...id].astro +2 -2
- package/src/pages/[...lang]/course/category/[id].astro +2 -2
- package/src/pages/[...lang]/course/index.astro +2 -3
- package/src/pages/[...lang]/glossary.astro +2 -3
- package/src/pages/[...lang]/index.astro +2 -2
- package/src/pages/[...lang]/paper/[...id].astro +2 -2
- package/src/pages/[...lang]/paper/category/[id].astro +2 -2
- package/src/pages/[...lang]/paper/index.astro +2 -3
- package/src/pages/[...lang]/products/[...id].astro +2 -2
- package/src/pages/[...lang]/products/index.astro +2 -3
- package/src/pages/[...lang]/rss.xml.ts +2 -3
- package/src/pages/[...lang]/sample/[folder].astro +2 -2
- package/src/pages/[...lang]/sample/index.astro +2 -3
- package/src/pages/[...lang]/slides/index.astro +2 -3
- package/src/pages/[...lang]/stack/[...id].astro +2 -2
- package/src/pages/[...lang]/tags/[tag].astro +2 -2
- package/src/pages/[...lang]/vendors/[vendor].astro +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -11,6 +11,61 @@ content schema, while a consuming site supplies only content, taxonomy data and
|
|
|
11
11
|
config. Sites track the theme with `pnpm up stack-site-builder`, so each release
|
|
12
12
|
here is a plain version bump they pull in.
|
|
13
13
|
|
|
14
|
+
## [1.24.0] - 2026-08-25
|
|
15
|
+
|
|
16
|
+
Adding a locale was already meant to be site config only — the README said so —
|
|
17
|
+
but four strings never got the memo, so a site shipping a language beyond the
|
|
18
|
+
theme's en/ko had no way to translate its pricing chips, difficulty tooltips or
|
|
19
|
+
license labels, and no way to give the site itself a per-language name. This
|
|
20
|
+
release closes those, along with two hardcodings that assumed the default locale
|
|
21
|
+
is `en` and that a locale code is two bare letters.
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
|
|
25
|
+
- **Sites supply the enum label tables** — `site.pricingLabels` (the `pricing`
|
|
26
|
+
frontmatter enum), `site.difficultyLabels` (course `level` 1–5) and
|
|
27
|
+
`site.licenseLabels` (descriptive licenses like `proprietary`) now feed
|
|
28
|
+
`pricingLabel()` / `difficultyLabel()` / `licenseLabel()` with the same
|
|
29
|
+
precedence `useTranslations` already used: the site's table for this locale,
|
|
30
|
+
the theme's, then both again for the default locale. Each is
|
|
31
|
+
`{ <code>: { <key>: <label> } }`, merged per key, so a site lists only what it
|
|
32
|
+
needs — and a site can retranslate a locale the theme ships, since the site's
|
|
33
|
+
entry wins. Previously these three read en/ko literals with no override point,
|
|
34
|
+
which left a third locale's chips and tooltips rendering in the default
|
|
35
|
+
locale's language with nothing a site could do about it.
|
|
36
|
+
- **`site.name` can be per-locale** — either one string for every locale (as
|
|
37
|
+
before, unchanged) or a record like
|
|
38
|
+
`{ en: 'Advanced Algorithms', ko: '고급 알고리즘' }`, resolved through the new
|
|
39
|
+
`siteName(lang)` export and falling back to the default locale then any entry.
|
|
40
|
+
Every place the name appears — page titles, the header wordmark and its
|
|
41
|
+
aria-label, the RSS channel title, the catalog and cards homepages, the deck
|
|
42
|
+
view — now goes through it.
|
|
43
|
+
|
|
44
|
+
### Fixed
|
|
45
|
+
|
|
46
|
+
- **The language auto-detect redirect works on non-`en`-default sites** — the
|
|
47
|
+
root home gated its redirect script on `lang === 'en'` instead of
|
|
48
|
+
`lang === defaultLang`, so a site whose first locale is anything else (a
|
|
49
|
+
ko-default lecture site, say) shipped the script on no page at all and never
|
|
50
|
+
auto-detected. The redirect now lands on whichever locale's home is served
|
|
51
|
+
without a prefix.
|
|
52
|
+
- **Locale codes that aren't two bare letters** — every collection derived its
|
|
53
|
+
slugs with a copy of `/^[a-z]{2}\//` and filtered its listings with
|
|
54
|
+
`id.startsWith(`${lang}/`)`, so a `zh-CN` or `pt-BR` locale produced slugs
|
|
55
|
+
with the locale still glued on and listings that matched nothing. Both now go
|
|
56
|
+
through `stripLocale()` / `inLocale()` in `lib/locales.ts`, built from the
|
|
57
|
+
site's own locale codes. The prefix is compared lowercased because Astro's
|
|
58
|
+
glob loader lowercases every path segment when it derives content ids, so the
|
|
59
|
+
content folder may be cased either way (`stacks/zh-CN/` or `stacks/zh-cn/`).
|
|
60
|
+
|
|
61
|
+
### Changed
|
|
62
|
+
|
|
63
|
+
- **The playground demos a fourth locale, `zh-CN`** — deliberately a code that
|
|
64
|
+
is neither two letters nor all-lowercase, with its own `pricingLabels`,
|
|
65
|
+
`difficultyLabels`, `licenseLabels`, a per-locale `site.name` and a partial
|
|
66
|
+
`site.ui` table, so the fallbacks and the slug derivation stay covered by the
|
|
67
|
+
build.
|
|
68
|
+
|
|
14
69
|
## [1.23.2] - 2026-08-06
|
|
15
70
|
|
|
16
71
|
### Fixed
|
|
@@ -465,6 +520,7 @@ catalog sites from a thin content-only repository.
|
|
|
465
520
|
- **Standalone development setup** — a devcontainer and a minimal `playground/`
|
|
466
521
|
consuming site for developing and previewing the theme on its own.
|
|
467
522
|
|
|
523
|
+
[1.24.0]: https://github.com/CodeComposeStudio/stack-site-builder/compare/v1.23.2...v1.24.0
|
|
468
524
|
[1.23.2]: https://github.com/CodeComposeStudio/stack-site-builder/compare/v1.23.1...v1.23.2
|
|
469
525
|
[1.23.1]: https://github.com/CodeComposeStudio/stack-site-builder/compare/v1.23.0...v1.23.1
|
|
470
526
|
[1.23.0]: https://github.com/CodeComposeStudio/stack-site-builder/compare/v1.22.0...v1.23.0
|
package/README.md
CHANGED
|
@@ -36,7 +36,7 @@ export const collections = defineAasCollections({ categoryMap });
|
|
|
36
36
|
|
|
37
37
|
| Where | What |
|
|
38
38
|
| --- | --- |
|
|
39
|
-
| `src/data/site.ts` | Site identity: name, repo URL (`repoNav: false` hides the header's GitHub link), the `locales` it ships, optional `sections` toggles, browser icons (`icons: { favicon, appleTouch, manifest }`), the `home` template, per-locale UI string overrides |
|
|
39
|
+
| `src/data/site.ts` | Site identity: name (one string, or per-locale), repo URL (`repoNav: false` hides the header's GitHub link), the `locales` it ships, optional `sections` toggles, browser icons (`icons: { favicon, appleTouch, manifest }`), the `home` template, per-locale UI string overrides and enum label tables (`pricingLabels`, `difficultyLabels`, `licenseLabels`) |
|
|
40
40
|
| `src/data/categories.ts` | The tool-catalog category tree (validated against content) |
|
|
41
41
|
| `src/data/concept-categories.ts` · `article-categories.ts` · `course-categories.ts` · `product-categories.ts` · `paper-categories.ts` (opt-in) | Taxonomies for concepts / articles / courses / products / papers |
|
|
42
42
|
| `src/data/glossary.mjs` | `[[Term]]` wikilink targets — each entry links a term to a `stack` / `concept` / `article` / `course` / `paper` page, an external `href`, or is a definition-only term (`def`) |
|
|
@@ -63,6 +63,21 @@ locale from one source. To add a language (say Japanese):
|
|
|
63
63
|
you omit falls back to the default locale. Add the `<code>` translations to
|
|
64
64
|
your content (`src/content/<collection>/<code>/…`), glossary and category
|
|
65
65
|
labels the same way you did for the built-in locales.
|
|
66
|
+
4. Supply the enum labels the theme only ships in en/ko, under the same
|
|
67
|
+
per-locale shape: `site.pricingLabels` (the `pricing` frontmatter enum),
|
|
68
|
+
`site.difficultyLabels` (course `level` 1–5) and `site.licenseLabels`
|
|
69
|
+
(descriptive licenses like `proprietary`; real license names pass through).
|
|
70
|
+
Each is `{ <code>: { <key>: <label> } }`, merged per key over the theme's
|
|
71
|
+
table, so you list only what you need. A site can also retranslate a locale
|
|
72
|
+
the theme ships — the site's entry wins.
|
|
73
|
+
5. If the site's own name differs per language, make `site.name` a per-locale
|
|
74
|
+
record — `name: { en: 'Advanced Algorithms', ko: '고급 알고리즘' }` — instead of
|
|
75
|
+
one string. A locale it omits falls back to the default locale.
|
|
76
|
+
|
|
77
|
+
A locale code doesn't have to be two letters: `zh-CN` and `pt-BR` work, and the
|
|
78
|
+
content folder may be cased either way (`stacks/zh-CN/` or `stacks/zh-cn/`) —
|
|
79
|
+
Astro lowercases the path segment when it derives content ids, which the theme
|
|
80
|
+
accounts for.
|
|
66
81
|
|
|
67
82
|
No theme files change — adding a locale is entirely site config and content.
|
|
68
83
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stack-site-builder",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.24.0",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "The engine behind the awesome-*-stack catalog sites: an Astro theme with the catalog/concepts/articles/slides/samples routes, components, styles and markdown pipeline. Sites provide content, taxonomy data and config.",
|
|
7
7
|
"repository": {
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import { site } from '@aas-data/site';
|
|
7
7
|
import { getRelativeLocaleUrl } from 'astro:i18n';
|
|
8
8
|
import { home, loc, locList } from '../lib/home';
|
|
9
|
-
import { useTranslations, type Lang } from '../i18n/ui';
|
|
9
|
+
import { useTranslations, siteName, type Lang } from '../i18n/ui';
|
|
10
10
|
|
|
11
11
|
interface Props {
|
|
12
12
|
lang: Lang;
|
|
@@ -18,7 +18,7 @@ const h = home;
|
|
|
18
18
|
const base = import.meta.env.BASE_URL.replace(/\/$/, '');
|
|
19
19
|
const withBase = (p?: string) => (p && p.startsWith('/') ? base + p : p);
|
|
20
20
|
|
|
21
|
-
const heroTitle = loc(h?.hero?.title, lang) ??
|
|
21
|
+
const heroTitle = loc(h?.hero?.title, lang) ?? siteName(lang);
|
|
22
22
|
const heroSubtitle = loc(h?.hero?.subtitle, lang) ?? t('site.tagline');
|
|
23
23
|
const extAttrs = (external?: boolean) =>
|
|
24
24
|
external ? { target: '_blank', rel: 'noopener noreferrer' } : {};
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
---
|
|
2
|
-
import { site } from '@aas-data/site';
|
|
3
2
|
import '../styles/global.css';
|
|
4
3
|
import { getRelativeLocaleUrl } from 'astro:i18n';
|
|
5
4
|
import { render, type CollectionEntry } from 'astro:content';
|
|
6
5
|
import MermaidLoader from './MermaidLoader.astro';
|
|
7
6
|
import PrivateGate from './PrivateGate.astro';
|
|
8
|
-
import { useTranslations, type Lang } from '../i18n/ui';
|
|
7
|
+
import { useTranslations, siteName, type Lang } from '../i18n/ui';
|
|
9
8
|
import { siteIcons } from '../lib/icons';
|
|
10
9
|
|
|
11
10
|
// Renders a full-screen slide deck as a horizontal scroll-snap presentation.
|
|
@@ -31,7 +30,7 @@ const priv = entry.data.private;
|
|
|
31
30
|
<head>
|
|
32
31
|
<meta charset="utf-8" />
|
|
33
32
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
34
|
-
<title>{entry.data.title} — {
|
|
33
|
+
<title>{entry.data.title} — {siteName(lang)}</title>
|
|
35
34
|
<meta name="description" content={(priv ? entry.data.teaser : entry.data.description) ?? ''} />
|
|
36
35
|
{priv && <meta name="robots" content="noindex" />}
|
|
37
36
|
<link rel="icon" {...(siteIcons.faviconType ? { type: siteIcons.faviconType } : {})} href={`${base}${siteIcons.favicon}`} />
|
|
@@ -8,7 +8,7 @@ import { getStacks, slugOf } from '../lib/stacks';
|
|
|
8
8
|
import { site } from '@aas-data/site';
|
|
9
9
|
import { deriveFacets } from '../lib/facets';
|
|
10
10
|
import { memberOnlyInIndex } from '../lib/listing';
|
|
11
|
-
import { useTranslations, type Lang } from '../i18n/ui';
|
|
11
|
+
import { useTranslations, siteName, type Lang } from '../i18n/ui';
|
|
12
12
|
|
|
13
13
|
interface Props {
|
|
14
14
|
lang: Lang;
|
|
@@ -33,7 +33,7 @@ const facets = deriveFacets(entries, lang);
|
|
|
33
33
|
---
|
|
34
34
|
|
|
35
35
|
<section class="py-6">
|
|
36
|
-
<h1 class="text-4xl font-bold tracking-tight">{
|
|
36
|
+
<h1 class="text-4xl font-bold tracking-tight">{siteName(lang)}</h1>
|
|
37
37
|
<p class="mt-3 max-w-2xl text-lg text-[var(--aas-muted)]">{t('site.tagline')}</p>
|
|
38
38
|
</section>
|
|
39
39
|
|
package/src/i18n/ui.ts
CHANGED
|
@@ -25,9 +25,23 @@ const themeLocales: LocaleDef[] = [
|
|
|
25
25
|
{ code: 'en', label: 'English', dateLocale: 'en-US' },
|
|
26
26
|
{ code: 'ko', label: '한국어', dateLocale: 'ko-KR' },
|
|
27
27
|
];
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
/** A per-locale label table: `{ <locale>: { <key>: <label> } }`. Sites supply
|
|
29
|
+
* these for locales the theme doesn't ship (see {@link pricingLabels}). */
|
|
30
|
+
export type LabelTable = Record<string, Record<string, string>>;
|
|
31
|
+
|
|
32
|
+
// Read defensively: every field below is optional, and a site that declares
|
|
33
|
+
// none of them must not be a type error — the `@aas-data/site` alias points at
|
|
34
|
+
// the site's own object, whose exact shape the theme can't know.
|
|
35
|
+
const siteCfg = site as {
|
|
36
|
+
name?: string | Record<string, string>;
|
|
37
|
+
locales?: LocaleDef[];
|
|
38
|
+
pricingLabels?: LabelTable;
|
|
39
|
+
difficultyLabels?: LabelTable;
|
|
40
|
+
licenseLabels?: LabelTable;
|
|
41
|
+
};
|
|
42
|
+
// A site that hasn't opted into custom locales simply has no `locales` field,
|
|
43
|
+
// and should keep the theme's en/ko default.
|
|
44
|
+
const siteLocales = siteCfg.locales;
|
|
31
45
|
const localeList: LocaleDef[] =
|
|
32
46
|
Array.isArray(siteLocales) && siteLocales.length ? siteLocales : themeLocales;
|
|
33
47
|
|
|
@@ -51,6 +65,20 @@ export function dateLocaleOf(lang: Lang): string {
|
|
|
51
65
|
);
|
|
52
66
|
}
|
|
53
67
|
|
|
68
|
+
/**
|
|
69
|
+
* The site's name in `lang`. `site.name` is either one string used for every
|
|
70
|
+
* locale (the common case) or a per-locale record — `{ ko: '고급 알고리즘', en:
|
|
71
|
+
* 'Advanced Algorithms' }` — which falls back to the default locale, then to
|
|
72
|
+
* any entry. Same rule as `loc()` in lib/home.ts, inlined here because that
|
|
73
|
+
* module imports this one.
|
|
74
|
+
*/
|
|
75
|
+
export function siteName(lang: Lang): string {
|
|
76
|
+
const n = siteCfg.name;
|
|
77
|
+
if (n == null) return '';
|
|
78
|
+
if (typeof n === 'string') return n;
|
|
79
|
+
return n[lang] ?? n[defaultLang] ?? Object.values(n)[0] ?? '';
|
|
80
|
+
}
|
|
81
|
+
|
|
54
82
|
/** UI chrome strings, keyed by a dotted id. */
|
|
55
83
|
export const ui = {
|
|
56
84
|
en: {
|
|
@@ -436,14 +464,35 @@ export function useTranslations(lang: Lang) {
|
|
|
436
464
|
};
|
|
437
465
|
}
|
|
438
466
|
|
|
467
|
+
/**
|
|
468
|
+
* Look one label up with the same precedence `useTranslations` uses: the site's
|
|
469
|
+
* table for this locale, the theme's, then both again for the default locale.
|
|
470
|
+
* The site-first order lets a site retranslate a locale the theme ships; the
|
|
471
|
+
* default-locale fallback keeps a site-added locale from rendering a bare key.
|
|
472
|
+
*/
|
|
473
|
+
function lookupLabel(
|
|
474
|
+
siteTable: LabelTable | undefined,
|
|
475
|
+
themeTable: LabelTable,
|
|
476
|
+
lang: Lang,
|
|
477
|
+
key: string,
|
|
478
|
+
): string | undefined {
|
|
479
|
+
return (
|
|
480
|
+
siteTable?.[lang]?.[key] ??
|
|
481
|
+
themeTable[lang]?.[key] ??
|
|
482
|
+
siteTable?.[defaultLang]?.[key] ??
|
|
483
|
+
themeTable[defaultLang]?.[key]
|
|
484
|
+
);
|
|
485
|
+
}
|
|
486
|
+
|
|
439
487
|
/** Localized label for a `pricing` enum value, falling back to the default
|
|
440
488
|
* locale then the raw value (so a site-added locale never crashes). */
|
|
441
489
|
export function pricingLabel(lang: Lang, value: string): string {
|
|
442
|
-
return pricingLabels
|
|
490
|
+
return lookupLabel(siteCfg.pricingLabels, pricingLabels, lang, value) ?? value;
|
|
443
491
|
}
|
|
444
492
|
|
|
445
|
-
/** Human labels for the `pricing` frontmatter enum, per locale.
|
|
446
|
-
|
|
493
|
+
/** Human labels for the `pricing` frontmatter enum, per locale. A site adds its
|
|
494
|
+
* own locales via `site.pricingLabels` — same shape, merged per-key. */
|
|
495
|
+
export const pricingLabels: LabelTable = {
|
|
447
496
|
en: {
|
|
448
497
|
'completely-free': 'Completely free',
|
|
449
498
|
'open-source': 'Open source',
|
|
@@ -464,20 +513,22 @@ export const pricingLabels: Record<string, Record<string, string>> = {
|
|
|
464
513
|
* locale then the bare number (so a site-added locale never crashes). */
|
|
465
514
|
export function difficultyLabel(lang: Lang, level: number): string {
|
|
466
515
|
const key = String(level);
|
|
467
|
-
return difficultyLabels
|
|
516
|
+
return lookupLabel(siteCfg.difficultyLabels, difficultyLabels, lang, key) ?? key;
|
|
468
517
|
}
|
|
469
518
|
|
|
470
|
-
/** Human labels for the course `level` frontmatter (1–5), per locale.
|
|
471
|
-
|
|
519
|
+
/** Human labels for the course `level` frontmatter (1–5), per locale. A site
|
|
520
|
+
* adds its own locales via `site.difficultyLabels`. */
|
|
521
|
+
export const difficultyLabels: LabelTable = {
|
|
472
522
|
en: { '1': 'Beginner', '2': 'Elementary', '3': 'Intermediate', '4': 'Advanced', '5': 'Expert' },
|
|
473
523
|
ko: { '1': '입문', '2': '초급', '3': '중급', '4': '고급', '5': '전문가' },
|
|
474
524
|
};
|
|
475
525
|
|
|
476
|
-
/** Descriptive (non-name) licenses get localized; real license names pass
|
|
477
|
-
|
|
526
|
+
/** Descriptive (non-name) licenses get localized; real license names pass
|
|
527
|
+
* through. A site adds its own locales via `site.licenseLabels`. */
|
|
528
|
+
const licenseLabels: LabelTable = {
|
|
478
529
|
en: { proprietary: 'Proprietary' },
|
|
479
530
|
ko: { proprietary: '독점' },
|
|
480
531
|
};
|
|
481
532
|
export function licenseLabel(lang: Lang, value: string): string {
|
|
482
|
-
return licenseLabels
|
|
533
|
+
return lookupLabel(siteCfg.licenseLabels, licenseLabels, lang, value) ?? value;
|
|
483
534
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import '../styles/global.css';
|
|
3
3
|
import { site } from '@aas-data/site';
|
|
4
4
|
import { getRelativeLocaleUrl } from 'astro:i18n';
|
|
5
|
-
import { useTranslations, languages, type Lang } from '../i18n/ui';
|
|
5
|
+
import { useTranslations, languages, siteName, defaultLang, type Lang } from '../i18n/ui';
|
|
6
6
|
import LanguageSwitcher from '../components/LanguageSwitcher.astro';
|
|
7
7
|
import ThemeToggle from '../components/ThemeToggle.astro';
|
|
8
8
|
import BackToTop from '../components/BackToTop.astro';
|
|
@@ -55,7 +55,7 @@ const baseNoSlash = base.replace(/\/$/, '');
|
|
|
55
55
|
|
|
56
56
|
// Header brand: `site.header = { logo?: '/path.png', logoInvert?: bool,
|
|
57
57
|
// name?: bool }`. A logo replaces the ⚡ bolt; `name: false` drops the
|
|
58
|
-
// wordmark (logo-only brand — the link keeps
|
|
58
|
+
// wordmark (logo-only brand — the link keeps siteName(lang) as its aria-label).
|
|
59
59
|
const header = (site as { header?: { logo?: string; logoInvert?: boolean; name?: boolean } })
|
|
60
60
|
.header;
|
|
61
61
|
const brandLogo = header?.logo;
|
|
@@ -65,7 +65,11 @@ const brandName = header?.name !== false;
|
|
|
65
65
|
const localeHomes = Object.fromEntries(
|
|
66
66
|
(Object.keys(languages) as Lang[]).map((c) => [c, getRelativeLocaleUrl(c, '')]),
|
|
67
67
|
);
|
|
68
|
-
|
|
68
|
+
// The auto-detect redirect belongs on the ROOT home — the one page served
|
|
69
|
+
// without a locale prefix — which is the default locale's, whatever it is. This
|
|
70
|
+
// read 'en' until 1.24.0, so a ko-default (or any non-en-default) site never
|
|
71
|
+
// redirected at all.
|
|
72
|
+
const isDefaultHome = path === '' && lang === defaultLang;
|
|
69
73
|
|
|
70
74
|
// Standalone pages (the `pages` collection) that opt into a header-nav link,
|
|
71
75
|
// e.g. an About/소개 page. Rendered as their own nav items alongside the
|
|
@@ -216,7 +220,7 @@ const navItems = allNavItems.filter((item) => !item.section || sectionEnabled(it
|
|
|
216
220
|
<link
|
|
217
221
|
rel="alternate"
|
|
218
222
|
type="application/rss+xml"
|
|
219
|
-
title={`${
|
|
223
|
+
title={`${siteName(lang)} — ${t('blog.title')}`}
|
|
220
224
|
href={getRelativeLocaleUrl(lang, 'rss.xml')}
|
|
221
225
|
/>
|
|
222
226
|
)
|
|
@@ -287,7 +291,7 @@ const navItems = allNavItems.filter((item) => !item.section || sectionEnabled(it
|
|
|
287
291
|
<a
|
|
288
292
|
href={home}
|
|
289
293
|
data-keep-filters
|
|
290
|
-
aria-label={
|
|
294
|
+
aria-label={siteName(lang)}
|
|
291
295
|
class="flex min-w-0 items-center gap-1.5 text-base font-bold tracking-tight whitespace-nowrap no-underline sm:text-lg"
|
|
292
296
|
>
|
|
293
297
|
{
|
|
@@ -302,7 +306,7 @@ const navItems = allNavItems.filter((item) => !item.section || sectionEnabled(it
|
|
|
302
306
|
)
|
|
303
307
|
}
|
|
304
308
|
{/* Below 600px only the mark remains, so the narrow header never truncates the wordmark. */}
|
|
305
|
-
{brandName && <span class="hidden truncate min-[600px]:inline">{
|
|
309
|
+
{brandName && <span class="hidden truncate min-[600px]:inline">{siteName(lang)}</span>}
|
|
306
310
|
</a>
|
|
307
311
|
{/* Section links are icon-only (labels live in aria-label). On phones
|
|
308
312
|
(<sm) they collapse into the labelled dropdown menu below, so the
|
package/src/lib/articles.ts
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
import { getCollection, type CollectionEntry } from 'astro:content';
|
|
2
2
|
import type { Lang } from '../i18n/ui';
|
|
3
|
+
import { inLocale, stripLocale } from './locales';
|
|
3
4
|
|
|
4
5
|
export type ArticleEntry = CollectionEntry<'articles'>;
|
|
5
6
|
|
|
6
7
|
/** The url slug of an article, i.e. its id with the `<lang>/` prefix removed. */
|
|
7
8
|
export function articleSlugOf(entry: ArticleEntry): string {
|
|
8
|
-
return entry.id
|
|
9
|
+
return stripLocale(entry.id);
|
|
9
10
|
}
|
|
10
11
|
|
|
11
12
|
/** Published articles for one locale, newest first. */
|
|
12
13
|
export async function getArticles(lang: Lang): Promise<ArticleEntry[]> {
|
|
13
14
|
const all = await getCollection('articles');
|
|
14
15
|
return all
|
|
15
|
-
.filter((e) => e.id
|
|
16
|
+
.filter((e) => inLocale(e.id, lang) && !e.data.draft)
|
|
16
17
|
.sort((a, b) => b.data.date.valueOf() - a.data.date.valueOf());
|
|
17
18
|
}
|
|
18
19
|
|
package/src/lib/concepts.ts
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
import { getCollection, type CollectionEntry } from 'astro:content';
|
|
2
2
|
import type { Lang } from '../i18n/ui';
|
|
3
|
+
import { inLocale, stripLocale } from './locales';
|
|
3
4
|
|
|
4
5
|
export type ConceptEntry = CollectionEntry<'concepts'>;
|
|
5
6
|
|
|
6
7
|
/** The url slug of a concept, i.e. its id with the `<lang>/` prefix removed. */
|
|
7
8
|
export function conceptSlugOf(entry: ConceptEntry): string {
|
|
8
|
-
return entry.id
|
|
9
|
+
return stripLocale(entry.id);
|
|
9
10
|
}
|
|
10
11
|
|
|
11
12
|
/** Published concepts for one locale, by `order` then title. */
|
|
12
13
|
export async function getConcepts(lang: Lang): Promise<ConceptEntry[]> {
|
|
13
14
|
const all = await getCollection('concepts');
|
|
14
15
|
return all
|
|
15
|
-
.filter((e) => e.id
|
|
16
|
+
.filter((e) => inLocale(e.id, lang) && !e.data.draft)
|
|
16
17
|
.sort(
|
|
17
18
|
(a, b) =>
|
|
18
19
|
(a.data.order ?? 999) - (b.data.order ?? 999) ||
|
package/src/lib/courses.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { getCollection, type CollectionEntry } from 'astro:content';
|
|
2
2
|
import type { Lang } from '../i18n/ui';
|
|
3
|
+
import { inLocale, stripLocale } from './locales';
|
|
3
4
|
|
|
4
5
|
export type CourseEntry = CollectionEntry<'courses'>;
|
|
5
6
|
|
|
6
7
|
/** The url slug of a course, i.e. its id with the `<lang>/` prefix removed. */
|
|
7
8
|
export function courseSlugOf(entry: CourseEntry): string {
|
|
8
|
-
return entry.id
|
|
9
|
+
return stripLocale(entry.id);
|
|
9
10
|
}
|
|
10
11
|
|
|
11
12
|
/**
|
|
@@ -16,7 +17,7 @@ export function courseSlugOf(entry: CourseEntry): string {
|
|
|
16
17
|
export async function getCourses(lang: Lang): Promise<CourseEntry[]> {
|
|
17
18
|
const all = await getCollection('courses');
|
|
18
19
|
return all
|
|
19
|
-
.filter((e) => e.id
|
|
20
|
+
.filter((e) => inLocale(e.id, lang) && !e.data.draft)
|
|
20
21
|
.sort(
|
|
21
22
|
(a, b) =>
|
|
22
23
|
(b.data.order ?? '').localeCompare(a.data.order ?? '') ||
|
package/src/lib/locales.ts
CHANGED
|
@@ -14,3 +14,33 @@ export const allLocales = Object.keys(languages) as Lang[];
|
|
|
14
14
|
/** The `[...lang]` param for a locale: `undefined` for the default, else the code. */
|
|
15
15
|
export const langParam = (lang: Lang): string | undefined =>
|
|
16
16
|
lang === defaultLang ? undefined : lang;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Content-id helpers. Every collection is locale-partitioned by folder
|
|
20
|
+
* (`stacks/<code>/<slug>.mdx`), so an entry's id carries a `<code>/` prefix
|
|
21
|
+
* that listings filter on and slugs strip.
|
|
22
|
+
*
|
|
23
|
+
* Two details make the prefix worth deriving instead of hardcoding: Astro's
|
|
24
|
+
* glob loader runs every path segment through github-slugger, which
|
|
25
|
+
* LOWERCASES it, so a `zh-CN` folder yields the id `zh-cn/…`; and a locale
|
|
26
|
+
* code isn't necessarily two bare letters (`zh-CN`, `pt-BR`). Both are handled
|
|
27
|
+
* here once, so adding such a locale stays a site-config change.
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
/** The `<locale>/` prefix an entry id carries for `lang`. */
|
|
31
|
+
export const contentPrefix = (lang: Lang): string => `${lang.toLowerCase()}/`;
|
|
32
|
+
|
|
33
|
+
/** Whether a content id belongs to `lang`. */
|
|
34
|
+
export const inLocale = (id: string, lang: Lang): boolean => id.startsWith(contentPrefix(lang));
|
|
35
|
+
|
|
36
|
+
const escapeRe = (s: string): string => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
37
|
+
// Longest code first, so `zh` can't shadow `zh-cn` on a site shipping both.
|
|
38
|
+
const localePrefixRe = new RegExp(
|
|
39
|
+
`^(?:${allLocales
|
|
40
|
+
.map((l) => escapeRe(l.toLowerCase()))
|
|
41
|
+
.sort((a, b) => b.length - a.length)
|
|
42
|
+
.join('|')})/`,
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
/** An entry id with its `<locale>/` prefix removed — the basis of every slug. */
|
|
46
|
+
export const stripLocale = (id: string): string => id.replace(localePrefixRe, '');
|
package/src/lib/pages.ts
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
import { getCollection, type CollectionEntry } from 'astro:content';
|
|
2
2
|
import type { Lang } from '../i18n/ui';
|
|
3
|
+
import { inLocale, stripLocale } from './locales';
|
|
3
4
|
|
|
4
5
|
export type PageEntry = CollectionEntry<'pages'>;
|
|
5
6
|
|
|
6
7
|
/** The url slug of a page, i.e. its id with the `<lang>/` prefix removed. */
|
|
7
8
|
export function pageSlugOf(entry: PageEntry): string {
|
|
8
|
-
return entry.id
|
|
9
|
+
return stripLocale(entry.id);
|
|
9
10
|
}
|
|
10
11
|
|
|
11
12
|
/** Standalone pages for one locale, in `order` (then title) order. */
|
|
12
13
|
export async function getPages(lang: Lang): Promise<PageEntry[]> {
|
|
13
14
|
const all = await getCollection('pages');
|
|
14
15
|
return all
|
|
15
|
-
.filter((e) => e.id
|
|
16
|
+
.filter((e) => inLocale(e.id, lang) && !e.data.draft)
|
|
16
17
|
.sort(
|
|
17
18
|
(a, b) => a.data.order - b.data.order || a.data.title.localeCompare(b.data.title),
|
|
18
19
|
);
|
package/src/lib/papers.ts
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
import { getCollection, type CollectionEntry } from 'astro:content';
|
|
2
2
|
import type { Lang } from '../i18n/ui';
|
|
3
|
+
import { inLocale, stripLocale } from './locales';
|
|
3
4
|
|
|
4
5
|
export type PaperEntry = CollectionEntry<'papers'>;
|
|
5
6
|
|
|
6
7
|
/** The url slug of a paper, i.e. its id with the `<lang>/` prefix removed. */
|
|
7
8
|
export function paperSlugOf(entry: PaperEntry): string {
|
|
8
|
-
return entry.id
|
|
9
|
+
return stripLocale(entry.id);
|
|
9
10
|
}
|
|
10
11
|
|
|
11
12
|
/** Published papers for one locale — newest publication year first, then title. */
|
|
12
13
|
export async function getPapers(lang: Lang): Promise<PaperEntry[]> {
|
|
13
14
|
const all = await getCollection('papers');
|
|
14
15
|
return all
|
|
15
|
-
.filter((e) => e.id
|
|
16
|
+
.filter((e) => inLocale(e.id, lang) && !e.data.draft)
|
|
16
17
|
.sort(
|
|
17
18
|
(a, b) => (b.data.year ?? 0) - (a.data.year ?? 0) || a.data.title.localeCompare(b.data.title),
|
|
18
19
|
);
|
package/src/lib/products.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { getCollection, type CollectionEntry } from 'astro:content';
|
|
2
2
|
import type { Lang } from '../i18n/ui';
|
|
3
|
+
import { inLocale, stripLocale } from './locales';
|
|
3
4
|
|
|
4
5
|
export type ProductEntry = CollectionEntry<'products'>;
|
|
5
6
|
|
|
@@ -7,14 +8,14 @@ export type ProductEntry = CollectionEntry<'products'>;
|
|
|
7
8
|
* nested (`flowstate-ai/privacy`), which the catch-all route renders at the
|
|
8
9
|
* matching subpath. */
|
|
9
10
|
export function productSlugOf(entry: ProductEntry): string {
|
|
10
|
-
return entry.id
|
|
11
|
+
return stripLocale(entry.id);
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
/** Published product pages for one locale, in `order` (then title) order. */
|
|
14
15
|
export async function getProducts(lang: Lang): Promise<ProductEntry[]> {
|
|
15
16
|
const all = await getCollection('products');
|
|
16
17
|
return all
|
|
17
|
-
.filter((e) => e.id
|
|
18
|
+
.filter((e) => inLocale(e.id, lang) && !e.data.draft)
|
|
18
19
|
.sort((a, b) => a.data.order - b.data.order || a.data.title.localeCompare(b.data.title));
|
|
19
20
|
}
|
|
20
21
|
|
package/src/lib/slides.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { getCollection, type CollectionEntry } from 'astro:content';
|
|
2
2
|
import type { Lang } from '../i18n/ui';
|
|
3
|
+
import { inLocale, stripLocale } from './locales';
|
|
3
4
|
|
|
4
5
|
export type SlideDeck = CollectionEntry<'slides'>;
|
|
5
6
|
|
|
@@ -9,14 +10,14 @@ export type SlideDeck = CollectionEntry<'slides'>;
|
|
|
9
10
|
* deck co-locates images) shares the same `<name>` slug as a flat `<name>.mdx`.
|
|
10
11
|
*/
|
|
11
12
|
export function deckSlugOf(entry: SlideDeck): string {
|
|
12
|
-
return entry.id
|
|
13
|
+
return stripLocale(entry.id).replace(/\/index$/, '');
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
/** Published slide decks for one locale, by `order` then title. */
|
|
16
17
|
export async function getDecks(lang: Lang): Promise<SlideDeck[]> {
|
|
17
18
|
const all = await getCollection('slides');
|
|
18
19
|
return all
|
|
19
|
-
.filter((e) => e.id
|
|
20
|
+
.filter((e) => inLocale(e.id, lang) && !e.data.draft)
|
|
20
21
|
.sort(
|
|
21
22
|
(a, b) =>
|
|
22
23
|
(a.data.order ?? 999) - (b.data.order ?? 999) ||
|
package/src/lib/stacks.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { getCollection, type CollectionEntry } from 'astro:content';
|
|
2
2
|
import type { Lang } from '../i18n/ui';
|
|
3
3
|
import { descendantIds } from '@aas-data/categories';
|
|
4
|
+
import { inLocale, stripLocale } from './locales';
|
|
4
5
|
|
|
5
6
|
export type StackEntry = CollectionEntry<'stacks'>;
|
|
6
7
|
|
|
7
8
|
/** The url slug of an entry, i.e. its id with the `<lang>/` prefix removed. */
|
|
8
9
|
export function slugOf(entry: StackEntry): string {
|
|
9
|
-
return entry.id
|
|
10
|
+
return stripLocale(entry.id);
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
/** A name → url-safe slug, e.g. "Roo Code" → "roo-code". */
|
|
@@ -43,7 +44,7 @@ export async function getStackAliases(lang: Lang): Promise<{ alias: string; targ
|
|
|
43
44
|
/** All stack entries for one locale (entries live in `stacks/<lang>/*.mdx`). */
|
|
44
45
|
export async function getStacks(lang: Lang): Promise<StackEntry[]> {
|
|
45
46
|
const all = await getCollection('stacks');
|
|
46
|
-
return all.filter((e) => e.id
|
|
47
|
+
return all.filter((e) => inLocale(e.id, lang));
|
|
47
48
|
}
|
|
48
49
|
|
|
49
50
|
/** Unique, sorted list of tags used by entries in a locale. */
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
|
-
import { site } from '@aas-data/site';
|
|
3
2
|
import type { GetStaticPaths } from 'astro';
|
|
4
3
|
import BaseLayout from '../../layouts/BaseLayout.astro';
|
|
5
4
|
import PageDetail from '../../components/PageDetail.astro';
|
|
6
5
|
import PrivateGate from '../../components/PrivateGate.astro';
|
|
7
6
|
import { getPages, pageSlugOf } from '../../lib/pages';
|
|
8
7
|
import { allLocales, langParam } from '../../lib/locales';
|
|
8
|
+
import { siteName } from '../../i18n/ui';
|
|
9
9
|
|
|
10
10
|
export const getStaticPaths = (async () => {
|
|
11
11
|
const paths: Awaited<ReturnType<GetStaticPaths>> = [];
|
|
@@ -23,7 +23,7 @@ const priv = entry.data.private;
|
|
|
23
23
|
---
|
|
24
24
|
|
|
25
25
|
<BaseLayout
|
|
26
|
-
title={`${entry.data.title} — ${
|
|
26
|
+
title={`${entry.data.title} — ${siteName(lang)}`}
|
|
27
27
|
description={priv ? entry.data.teaser : entry.data.description}
|
|
28
28
|
lang={lang}
|
|
29
29
|
path={`${slug}/`}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
|
-
import { site } from '@aas-data/site';
|
|
3
2
|
import type { GetStaticPaths } from 'astro';
|
|
4
3
|
import BaseLayout from '../../../layouts/BaseLayout.astro';
|
|
5
4
|
import ArticleDetail from '../../../components/ArticleDetail.astro';
|
|
6
5
|
import PrivateGate from '../../../components/PrivateGate.astro';
|
|
7
6
|
import { getArticles, articleSlugOf } from '../../../lib/articles';
|
|
8
7
|
import { allLocales, langParam } from '../../../lib/locales';
|
|
8
|
+
import { siteName } from '../../../i18n/ui';
|
|
9
9
|
|
|
10
10
|
export const getStaticPaths = (async () => {
|
|
11
11
|
const paths: Awaited<ReturnType<GetStaticPaths>> = [];
|
|
@@ -23,7 +23,7 @@ const priv = entry.data.private;
|
|
|
23
23
|
---
|
|
24
24
|
|
|
25
25
|
<BaseLayout
|
|
26
|
-
title={`${entry.data.title} — ${
|
|
26
|
+
title={`${entry.data.title} — ${siteName(lang)}`}
|
|
27
27
|
description={priv ? entry.data.teaser : entry.data.description}
|
|
28
28
|
lang={lang}
|
|
29
29
|
path={`article/${slug}/`}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
---
|
|
2
|
-
import { site } from '@aas-data/site';
|
|
3
2
|
import type { GetStaticPaths } from 'astro';
|
|
4
3
|
import BaseLayout from '../../../../layouts/BaseLayout.astro';
|
|
5
4
|
import BlogIndex from '../../../../components/BlogIndex.astro';
|
|
6
5
|
import { articleTree } from '@aas-data/article-categories';
|
|
7
6
|
import { allLocales, langParam } from '../../../../lib/locales';
|
|
7
|
+
import { siteName } from '../../../../i18n/ui';
|
|
8
8
|
|
|
9
9
|
export const getStaticPaths = (() => {
|
|
10
10
|
const paths: Awaited<ReturnType<GetStaticPaths>> = [];
|
|
@@ -20,6 +20,6 @@ const { lang, id } = Astro.props as { lang: string; id: string };
|
|
|
20
20
|
const node = articleTree.map.get(id)!;
|
|
21
21
|
---
|
|
22
22
|
|
|
23
|
-
<BaseLayout title={`${node.label[lang]} — ${
|
|
23
|
+
<BaseLayout title={`${node.label[lang]} — ${siteName(lang)}`} lang={lang} path={`article/category/${id}/`}>
|
|
24
24
|
<BlogIndex lang={lang} categoryId={id} />
|
|
25
25
|
</BaseLayout>
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
---
|
|
2
|
-
import { site } from '@aas-data/site';
|
|
3
2
|
import type { GetStaticPaths } from 'astro';
|
|
4
3
|
import BaseLayout from '../../../layouts/BaseLayout.astro';
|
|
5
4
|
import BlogIndex from '../../../components/BlogIndex.astro';
|
|
6
|
-
import { useTranslations } from '../../../i18n/ui';
|
|
5
|
+
import { useTranslations, siteName } from '../../../i18n/ui';
|
|
7
6
|
import { allLocales, langParam } from '../../../lib/locales';
|
|
8
7
|
|
|
9
8
|
export const getStaticPaths = (() =>
|
|
@@ -13,6 +12,6 @@ const { lang } = Astro.props;
|
|
|
13
12
|
const t = useTranslations(lang);
|
|
14
13
|
---
|
|
15
14
|
|
|
16
|
-
<BaseLayout title={`${t('blog.title')} — ${
|
|
15
|
+
<BaseLayout title={`${t('blog.title')} — ${siteName(lang)}`} lang={lang} path="article/">
|
|
17
16
|
<BlogIndex lang={lang} />
|
|
18
17
|
</BaseLayout>
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
|
-
import { site } from '@aas-data/site';
|
|
3
2
|
import type { GetStaticPaths } from 'astro';
|
|
4
3
|
import BaseLayout from '../../../layouts/BaseLayout.astro';
|
|
5
4
|
import CategoryIndex from '../../../components/CategoryIndex.astro';
|
|
6
5
|
import { allCategoryIds, categoryMap } from '@aas-data/categories';
|
|
7
6
|
import { getStacksByCategory } from '../../../lib/stacks';
|
|
8
7
|
import { allLocales, langParam } from '../../../lib/locales';
|
|
8
|
+
import { siteName } from '../../../i18n/ui';
|
|
9
9
|
|
|
10
10
|
export const getStaticPaths = (async () => {
|
|
11
11
|
const paths: Awaited<ReturnType<GetStaticPaths>> = [];
|
|
@@ -23,6 +23,6 @@ const { lang, id } = Astro.props;
|
|
|
23
23
|
const node = categoryMap.get(id)!;
|
|
24
24
|
---
|
|
25
25
|
|
|
26
|
-
<BaseLayout title={`${node.label[lang]} — ${
|
|
26
|
+
<BaseLayout title={`${node.label[lang]} — ${siteName(lang)}`} lang={lang} path={`categories/${id}/`}>
|
|
27
27
|
<CategoryIndex lang={lang} id={id} />
|
|
28
28
|
</BaseLayout>
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
|
-
import { site } from '@aas-data/site';
|
|
3
2
|
import type { GetStaticPaths } from 'astro';
|
|
4
3
|
import BaseLayout from '../../../layouts/BaseLayout.astro';
|
|
5
4
|
import ConceptDetail from '../../../components/ConceptDetail.astro';
|
|
6
5
|
import PrivateGate from '../../../components/PrivateGate.astro';
|
|
7
6
|
import { getConcepts, conceptSlugOf } from '../../../lib/concepts';
|
|
8
7
|
import { allLocales, langParam } from '../../../lib/locales';
|
|
8
|
+
import { siteName } from '../../../i18n/ui';
|
|
9
9
|
|
|
10
10
|
export const getStaticPaths = (async () => {
|
|
11
11
|
const paths: Awaited<ReturnType<GetStaticPaths>> = [];
|
|
@@ -23,7 +23,7 @@ const priv = entry.data.private;
|
|
|
23
23
|
---
|
|
24
24
|
|
|
25
25
|
<BaseLayout
|
|
26
|
-
title={`${entry.data.title} — ${
|
|
26
|
+
title={`${entry.data.title} — ${siteName(lang)}`}
|
|
27
27
|
description={priv ? entry.data.teaser : entry.data.description}
|
|
28
28
|
lang={lang}
|
|
29
29
|
path={`concept/${slug}/`}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
---
|
|
2
|
-
import { site } from '@aas-data/site';
|
|
3
2
|
import type { GetStaticPaths } from 'astro';
|
|
4
3
|
import BaseLayout from '../../../../layouts/BaseLayout.astro';
|
|
5
4
|
import ConceptIndex from '../../../../components/ConceptIndex.astro';
|
|
6
5
|
import { conceptTree } from '@aas-data/concept-categories';
|
|
7
6
|
import { allLocales, langParam } from '../../../../lib/locales';
|
|
7
|
+
import { siteName } from '../../../../i18n/ui';
|
|
8
8
|
|
|
9
9
|
export const getStaticPaths = (() => {
|
|
10
10
|
const paths: Awaited<ReturnType<GetStaticPaths>> = [];
|
|
@@ -20,6 +20,6 @@ const { lang, id } = Astro.props as { lang: string; id: string };
|
|
|
20
20
|
const node = conceptTree.map.get(id)!;
|
|
21
21
|
---
|
|
22
22
|
|
|
23
|
-
<BaseLayout title={`${node.label[lang]} — ${
|
|
23
|
+
<BaseLayout title={`${node.label[lang]} — ${siteName(lang)}`} lang={lang} path={`concept/category/${id}/`}>
|
|
24
24
|
<ConceptIndex lang={lang} categoryId={id} />
|
|
25
25
|
</BaseLayout>
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
---
|
|
2
|
-
import { site } from '@aas-data/site';
|
|
3
2
|
import type { GetStaticPaths } from 'astro';
|
|
4
3
|
import BaseLayout from '../../../layouts/BaseLayout.astro';
|
|
5
4
|
import ConceptIndex from '../../../components/ConceptIndex.astro';
|
|
6
|
-
import { useTranslations } from '../../../i18n/ui';
|
|
5
|
+
import { useTranslations, siteName } from '../../../i18n/ui';
|
|
7
6
|
import { allLocales, langParam } from '../../../lib/locales';
|
|
8
7
|
|
|
9
8
|
export const getStaticPaths = (() =>
|
|
@@ -13,6 +12,6 @@ const { lang } = Astro.props;
|
|
|
13
12
|
const t = useTranslations(lang);
|
|
14
13
|
---
|
|
15
14
|
|
|
16
|
-
<BaseLayout title={`${t('concept.title')} — ${
|
|
15
|
+
<BaseLayout title={`${t('concept.title')} — ${siteName(lang)}`} lang={lang} path="concept/">
|
|
17
16
|
<ConceptIndex lang={lang} />
|
|
18
17
|
</BaseLayout>
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
|
-
import { site } from '@aas-data/site';
|
|
3
2
|
import type { GetStaticPaths } from 'astro';
|
|
4
3
|
import BaseLayout from '../../../layouts/BaseLayout.astro';
|
|
5
4
|
import CourseDetail from '../../../components/CourseDetail.astro';
|
|
6
5
|
import PrivateGate from '../../../components/PrivateGate.astro';
|
|
7
6
|
import { getCourses, courseSlugOf } from '../../../lib/courses';
|
|
8
7
|
import { allLocales, langParam } from '../../../lib/locales';
|
|
8
|
+
import { siteName } from '../../../i18n/ui';
|
|
9
9
|
|
|
10
10
|
export const getStaticPaths = (async () => {
|
|
11
11
|
const paths: Awaited<ReturnType<GetStaticPaths>> = [];
|
|
@@ -23,7 +23,7 @@ const priv = entry.data.private;
|
|
|
23
23
|
---
|
|
24
24
|
|
|
25
25
|
<BaseLayout
|
|
26
|
-
title={`${entry.data.title} — ${
|
|
26
|
+
title={`${entry.data.title} — ${siteName(lang)}`}
|
|
27
27
|
description={priv ? entry.data.teaser : entry.data.description}
|
|
28
28
|
lang={lang}
|
|
29
29
|
path={`course/${slug}/`}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
---
|
|
2
|
-
import { site } from '@aas-data/site';
|
|
3
2
|
import type { GetStaticPaths } from 'astro';
|
|
4
3
|
import BaseLayout from '../../../../layouts/BaseLayout.astro';
|
|
5
4
|
import CourseIndex from '../../../../components/CourseIndex.astro';
|
|
6
5
|
import { courseTree } from '@aas-data/course-categories';
|
|
7
6
|
import { allLocales, langParam } from '../../../../lib/locales';
|
|
7
|
+
import { siteName } from '../../../../i18n/ui';
|
|
8
8
|
|
|
9
9
|
export const getStaticPaths = (() => {
|
|
10
10
|
const paths: Awaited<ReturnType<GetStaticPaths>> = [];
|
|
@@ -20,6 +20,6 @@ const { lang, id } = Astro.props as { lang: string; id: string };
|
|
|
20
20
|
const node = courseTree.map.get(id)!;
|
|
21
21
|
---
|
|
22
22
|
|
|
23
|
-
<BaseLayout title={`${node.label[lang]} — ${
|
|
23
|
+
<BaseLayout title={`${node.label[lang]} — ${siteName(lang)}`} lang={lang} path={`course/category/${id}/`}>
|
|
24
24
|
<CourseIndex lang={lang} categoryId={id} />
|
|
25
25
|
</BaseLayout>
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
---
|
|
2
|
-
import { site } from '@aas-data/site';
|
|
3
2
|
import type { GetStaticPaths } from 'astro';
|
|
4
3
|
import BaseLayout from '../../../layouts/BaseLayout.astro';
|
|
5
4
|
import CourseIndex from '../../../components/CourseIndex.astro';
|
|
6
|
-
import { useTranslations } from '../../../i18n/ui';
|
|
5
|
+
import { useTranslations, siteName } from '../../../i18n/ui';
|
|
7
6
|
import { allLocales, langParam } from '../../../lib/locales';
|
|
8
7
|
|
|
9
8
|
export const getStaticPaths = (() =>
|
|
@@ -13,6 +12,6 @@ const { lang } = Astro.props;
|
|
|
13
12
|
const t = useTranslations(lang);
|
|
14
13
|
---
|
|
15
14
|
|
|
16
|
-
<BaseLayout title={`${t('course.title')} — ${
|
|
15
|
+
<BaseLayout title={`${t('course.title')} — ${siteName(lang)}`} lang={lang} path="course/">
|
|
17
16
|
<CourseIndex lang={lang} />
|
|
18
17
|
</BaseLayout>
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
---
|
|
2
|
-
import { site } from '@aas-data/site';
|
|
3
2
|
import type { GetStaticPaths } from 'astro';
|
|
4
3
|
import BaseLayout from '../../layouts/BaseLayout.astro';
|
|
5
4
|
import Glossary from '../../components/Glossary.astro';
|
|
6
|
-
import { useTranslations } from '../../i18n/ui';
|
|
5
|
+
import { useTranslations, siteName } from '../../i18n/ui';
|
|
7
6
|
import { allLocales, langParam } from '../../lib/locales';
|
|
8
7
|
|
|
9
8
|
export const getStaticPaths = (() =>
|
|
@@ -13,6 +12,6 @@ const { lang } = Astro.props;
|
|
|
13
12
|
const t = useTranslations(lang);
|
|
14
13
|
---
|
|
15
14
|
|
|
16
|
-
<BaseLayout title={`${t('glossary.title')} — ${
|
|
15
|
+
<BaseLayout title={`${t('glossary.title')} — ${siteName(lang)}`} lang={lang} path="glossary/">
|
|
17
16
|
<Glossary lang={lang} />
|
|
18
17
|
</BaseLayout>
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
|
-
import { site } from '@aas-data/site';
|
|
3
2
|
import type { GetStaticPaths } from 'astro';
|
|
4
3
|
import BaseLayout from '../../layouts/BaseLayout.astro';
|
|
5
4
|
import Home from '../../components/Home.astro';
|
|
6
5
|
import CardsHome from '../../components/CardsHome.astro';
|
|
7
6
|
import { homeTemplate } from '../../lib/home';
|
|
8
7
|
import { allLocales, langParam } from '../../lib/locales';
|
|
8
|
+
import { siteName } from '../../i18n/ui';
|
|
9
9
|
|
|
10
10
|
export const getStaticPaths = (() =>
|
|
11
11
|
allLocales.map((lang) => ({ params: { lang: langParam(lang) }, props: { lang } }))) satisfies GetStaticPaths;
|
|
@@ -13,6 +13,6 @@ export const getStaticPaths = (() =>
|
|
|
13
13
|
const { lang } = Astro.props;
|
|
14
14
|
---
|
|
15
15
|
|
|
16
|
-
<BaseLayout title={
|
|
16
|
+
<BaseLayout title={siteName(lang)} lang={lang} path="">
|
|
17
17
|
{homeTemplate === 'cards' ? <CardsHome lang={lang} /> : <Home lang={lang} />}
|
|
18
18
|
</BaseLayout>
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
|
-
import { site } from '@aas-data/site';
|
|
3
2
|
import type { GetStaticPaths } from 'astro';
|
|
4
3
|
import BaseLayout from '../../../layouts/BaseLayout.astro';
|
|
5
4
|
import PaperDetail from '../../../components/PaperDetail.astro';
|
|
6
5
|
import PrivateGate from '../../../components/PrivateGate.astro';
|
|
7
6
|
import { getPapers, paperSlugOf } from '../../../lib/papers';
|
|
8
7
|
import { allLocales, langParam } from '../../../lib/locales';
|
|
8
|
+
import { siteName } from '../../../i18n/ui';
|
|
9
9
|
|
|
10
10
|
export const getStaticPaths = (async () => {
|
|
11
11
|
const paths: Awaited<ReturnType<GetStaticPaths>> = [];
|
|
@@ -23,7 +23,7 @@ const priv = entry.data.private;
|
|
|
23
23
|
---
|
|
24
24
|
|
|
25
25
|
<BaseLayout
|
|
26
|
-
title={`${entry.data.title} — ${
|
|
26
|
+
title={`${entry.data.title} — ${siteName(lang)}`}
|
|
27
27
|
description={priv ? entry.data.teaser : entry.data.description}
|
|
28
28
|
lang={lang}
|
|
29
29
|
path={`paper/${slug}/`}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
---
|
|
2
|
-
import { site } from '@aas-data/site';
|
|
3
2
|
import type { GetStaticPaths } from 'astro';
|
|
4
3
|
import BaseLayout from '../../../../layouts/BaseLayout.astro';
|
|
5
4
|
import PapersIndex from '../../../../components/PapersIndex.astro';
|
|
6
5
|
import { paperTree } from '@aas-data/paper-categories';
|
|
7
6
|
import { allLocales, langParam } from '../../../../lib/locales';
|
|
7
|
+
import { siteName } from '../../../../i18n/ui';
|
|
8
8
|
|
|
9
9
|
export const getStaticPaths = (() => {
|
|
10
10
|
const paths: Awaited<ReturnType<GetStaticPaths>> = [];
|
|
@@ -20,6 +20,6 @@ const { lang, id } = Astro.props as { lang: string; id: string };
|
|
|
20
20
|
const node = paperTree.map.get(id)!;
|
|
21
21
|
---
|
|
22
22
|
|
|
23
|
-
<BaseLayout title={`${node.label[lang]} — ${
|
|
23
|
+
<BaseLayout title={`${node.label[lang]} — ${siteName(lang)}`} lang={lang} path={`paper/category/${id}/`}>
|
|
24
24
|
<PapersIndex lang={lang} categoryId={id} />
|
|
25
25
|
</BaseLayout>
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
---
|
|
2
|
-
import { site } from '@aas-data/site';
|
|
3
2
|
import type { GetStaticPaths } from 'astro';
|
|
4
3
|
import BaseLayout from '../../../layouts/BaseLayout.astro';
|
|
5
4
|
import PapersIndex from '../../../components/PapersIndex.astro';
|
|
6
|
-
import { useTranslations } from '../../../i18n/ui';
|
|
5
|
+
import { useTranslations, siteName } from '../../../i18n/ui';
|
|
7
6
|
import { allLocales, langParam } from '../../../lib/locales';
|
|
8
7
|
|
|
9
8
|
export const getStaticPaths = (() =>
|
|
@@ -13,6 +12,6 @@ const { lang } = Astro.props;
|
|
|
13
12
|
const t = useTranslations(lang);
|
|
14
13
|
---
|
|
15
14
|
|
|
16
|
-
<BaseLayout title={`${t('paper.title')} — ${
|
|
15
|
+
<BaseLayout title={`${t('paper.title')} — ${siteName(lang)}`} lang={lang} path="paper/">
|
|
17
16
|
<PapersIndex lang={lang} />
|
|
18
17
|
</BaseLayout>
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
---
|
|
2
|
-
import { site } from '@aas-data/site';
|
|
3
2
|
import { render } from 'astro:content';
|
|
4
3
|
import type { GetStaticPaths } from 'astro';
|
|
5
4
|
import BaseLayout from '../../../layouts/BaseLayout.astro';
|
|
@@ -10,6 +9,7 @@ import MermaidLoader from '../../../components/MermaidLoader.astro';
|
|
|
10
9
|
import { getProducts, productSlugOf } from '../../../lib/products';
|
|
11
10
|
import { allLocales, langParam } from '../../../lib/locales';
|
|
12
11
|
import { inlineMd } from '../../../lib/inline-md';
|
|
12
|
+
import { siteName } from '../../../i18n/ui';
|
|
13
13
|
|
|
14
14
|
export const getStaticPaths = (async () => {
|
|
15
15
|
const paths: Awaited<ReturnType<GetStaticPaths>> = [];
|
|
@@ -32,7 +32,7 @@ const bodyHasMermaid = (entry.body ?? '').includes('```mermaid');
|
|
|
32
32
|
---
|
|
33
33
|
|
|
34
34
|
<BaseLayout
|
|
35
|
-
title={`${entry.data.title} — ${
|
|
35
|
+
title={`${entry.data.title} — ${siteName(lang)}`}
|
|
36
36
|
description={priv ? entry.data.teaser : (entry.data.description ?? entry.data.subtitle)}
|
|
37
37
|
lang={lang}
|
|
38
38
|
path={`products/${slug}/`}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
---
|
|
2
|
-
import { site } from '@aas-data/site';
|
|
3
2
|
import type { GetStaticPaths } from 'astro';
|
|
4
3
|
import BaseLayout from '../../../layouts/BaseLayout.astro';
|
|
5
4
|
import ProductsIndex from '../../../components/ProductsIndex.astro';
|
|
6
|
-
import { useTranslations } from '../../../i18n/ui';
|
|
5
|
+
import { useTranslations, siteName } from '../../../i18n/ui';
|
|
7
6
|
import { allLocales, langParam } from '../../../lib/locales';
|
|
8
7
|
|
|
9
8
|
export const getStaticPaths = (() =>
|
|
@@ -13,6 +12,6 @@ const { lang } = Astro.props;
|
|
|
13
12
|
const t = useTranslations(lang);
|
|
14
13
|
---
|
|
15
14
|
|
|
16
|
-
<BaseLayout title={`${t('products.title')} — ${
|
|
15
|
+
<BaseLayout title={`${t('products.title')} — ${siteName(lang)}`} lang={lang} path="products/">
|
|
17
16
|
<ProductsIndex lang={lang} />
|
|
18
17
|
</BaseLayout>
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import rss from '@astrojs/rss';
|
|
2
2
|
import type { APIContext, GetStaticPaths } from 'astro';
|
|
3
3
|
import { getRelativeLocaleUrl } from 'astro:i18n';
|
|
4
|
-
import { site } from '@aas-data/site';
|
|
5
4
|
import { getArticles, articleSlugOf } from '../../lib/articles';
|
|
6
5
|
import { allLocales, langParam } from '../../lib/locales';
|
|
7
|
-
import { useTranslations } from '../../i18n/ui';
|
|
6
|
+
import { useTranslations, siteName } from '../../i18n/ui';
|
|
8
7
|
|
|
9
8
|
// Per-locale RSS feed of the articles collection: /rss.xml (default locale)
|
|
10
9
|
// and /<code>/rss.xml. Injected with the `articles` section, so a site that
|
|
@@ -25,7 +24,7 @@ export async function GET(context: APIContext) {
|
|
|
25
24
|
// mirroring the sitemap's private-page filter.
|
|
26
25
|
const articles = (await getArticles(lang)).filter((a) => !a.data.private);
|
|
27
26
|
return rss({
|
|
28
|
-
title: `${
|
|
27
|
+
title: `${siteName(lang)} — ${t('blog.title')}`,
|
|
29
28
|
description: t('blog.tagline'),
|
|
30
29
|
site: context.site,
|
|
31
30
|
customData: `<language>${lang}</language>`,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
---
|
|
2
|
-
import { site } from '@aas-data/site';
|
|
3
2
|
import type { GetStaticPaths } from 'astro';
|
|
4
3
|
import BaseLayout from '../../../layouts/BaseLayout.astro';
|
|
5
4
|
import SamplePage from '../../../components/SamplePage.astro';
|
|
6
5
|
import { listSampleFolders } from '../../../lib/project';
|
|
7
6
|
import { allLocales, langParam } from '../../../lib/locales';
|
|
7
|
+
import { siteName } from '../../../i18n/ui';
|
|
8
8
|
|
|
9
9
|
export const getStaticPaths = (() => {
|
|
10
10
|
const paths: Awaited<ReturnType<GetStaticPaths>> = [];
|
|
@@ -20,6 +20,6 @@ export const getStaticPaths = (() => {
|
|
|
20
20
|
const { lang, folder } = Astro.props;
|
|
21
21
|
---
|
|
22
22
|
|
|
23
|
-
<BaseLayout title={`samples/${folder} — ${
|
|
23
|
+
<BaseLayout title={`samples/${folder} — ${siteName(lang)}`} lang={lang} path={`sample/${folder}/`}>
|
|
24
24
|
<SamplePage lang={lang} folder={folder} />
|
|
25
25
|
</BaseLayout>
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
---
|
|
2
|
-
import { site } from '@aas-data/site';
|
|
3
2
|
import type { GetStaticPaths } from 'astro';
|
|
4
3
|
import BaseLayout from '../../../layouts/BaseLayout.astro';
|
|
5
4
|
import SampleIndex from '../../../components/SampleIndex.astro';
|
|
6
|
-
import { useTranslations } from '../../../i18n/ui';
|
|
5
|
+
import { useTranslations, siteName } from '../../../i18n/ui';
|
|
7
6
|
import { allLocales, langParam } from '../../../lib/locales';
|
|
8
7
|
|
|
9
8
|
export const getStaticPaths = (() =>
|
|
@@ -13,6 +12,6 @@ const { lang } = Astro.props;
|
|
|
13
12
|
const t = useTranslations(lang);
|
|
14
13
|
---
|
|
15
14
|
|
|
16
|
-
<BaseLayout title={`${t('sample.title')} — ${
|
|
15
|
+
<BaseLayout title={`${t('sample.title')} — ${siteName(lang)}`} lang={lang} path="sample/">
|
|
17
16
|
<SampleIndex lang={lang} />
|
|
18
17
|
</BaseLayout>
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
---
|
|
2
|
-
import { site } from '@aas-data/site';
|
|
3
2
|
import type { GetStaticPaths } from 'astro';
|
|
4
3
|
import BaseLayout from '../../../layouts/BaseLayout.astro';
|
|
5
4
|
import SlidesIndex from '../../../components/SlidesIndex.astro';
|
|
6
|
-
import { useTranslations } from '../../../i18n/ui';
|
|
5
|
+
import { useTranslations, siteName } from '../../../i18n/ui';
|
|
7
6
|
import { allLocales, langParam } from '../../../lib/locales';
|
|
8
7
|
|
|
9
8
|
export const getStaticPaths = (() =>
|
|
@@ -13,6 +12,6 @@ const { lang } = Astro.props;
|
|
|
13
12
|
const t = useTranslations(lang);
|
|
14
13
|
---
|
|
15
14
|
|
|
16
|
-
<BaseLayout title={`${t('slides.title')} — ${
|
|
15
|
+
<BaseLayout title={`${t('slides.title')} — ${siteName(lang)}`} lang={lang} path="slides/">
|
|
17
16
|
<SlidesIndex lang={lang} />
|
|
18
17
|
</BaseLayout>
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
---
|
|
2
|
-
import { site } from '@aas-data/site';
|
|
3
2
|
import type { GetStaticPaths } from 'astro';
|
|
4
3
|
import { getRelativeLocaleUrl } from 'astro:i18n';
|
|
5
4
|
import BaseLayout from '../../../layouts/BaseLayout.astro';
|
|
@@ -7,6 +6,7 @@ import StackDetail from '../../../components/StackDetail.astro';
|
|
|
7
6
|
import PrivateGate from '../../../components/PrivateGate.astro';
|
|
8
7
|
import { getStacks, getStackAliases, slugOf, type StackEntry } from '../../../lib/stacks';
|
|
9
8
|
import { allLocales, langParam } from '../../../lib/locales';
|
|
9
|
+
import { siteName } from '../../../i18n/ui';
|
|
10
10
|
|
|
11
11
|
export const getStaticPaths = (async () => {
|
|
12
12
|
const paths: Awaited<ReturnType<GetStaticPaths>> = [];
|
|
@@ -49,7 +49,7 @@ const priv = entry?.data.private ?? false;
|
|
|
49
49
|
</html>
|
|
50
50
|
) : (
|
|
51
51
|
<BaseLayout
|
|
52
|
-
title={`${entry!.data.name} — ${
|
|
52
|
+
title={`${entry!.data.name} — ${siteName(lang)}`}
|
|
53
53
|
description={priv ? entry!.data.teaser : entry!.data.description}
|
|
54
54
|
lang={lang}
|
|
55
55
|
path={`stack/${slugOf(entry!)}/`}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
---
|
|
2
|
-
import { site } from '@aas-data/site';
|
|
3
2
|
import type { GetStaticPaths } from 'astro';
|
|
4
3
|
import BaseLayout from '../../../layouts/BaseLayout.astro';
|
|
5
4
|
import TagIndex from '../../../components/TagIndex.astro';
|
|
6
5
|
import { getAllTags } from '../../../lib/stacks';
|
|
7
6
|
import { allLocales, langParam } from '../../../lib/locales';
|
|
7
|
+
import { siteName } from '../../../i18n/ui';
|
|
8
8
|
|
|
9
9
|
export const getStaticPaths = (async () => {
|
|
10
10
|
const paths: Awaited<ReturnType<GetStaticPaths>> = [];
|
|
@@ -19,6 +19,6 @@ export const getStaticPaths = (async () => {
|
|
|
19
19
|
const { lang, tag } = Astro.props;
|
|
20
20
|
---
|
|
21
21
|
|
|
22
|
-
<BaseLayout title={`#${tag} — ${
|
|
22
|
+
<BaseLayout title={`#${tag} — ${siteName(lang)}`} lang={lang} path={`tags/${tag}/`}>
|
|
23
23
|
<TagIndex lang={lang} tag={tag} />
|
|
24
24
|
</BaseLayout>
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
---
|
|
2
|
-
import { site } from '@aas-data/site';
|
|
3
2
|
import type { GetStaticPaths } from 'astro';
|
|
4
3
|
import BaseLayout from '../../../layouts/BaseLayout.astro';
|
|
5
4
|
import VendorIndex from '../../../components/VendorIndex.astro';
|
|
6
5
|
import { getAllVendors } from '../../../lib/stacks';
|
|
7
6
|
import { allLocales, langParam } from '../../../lib/locales';
|
|
7
|
+
import { siteName } from '../../../i18n/ui';
|
|
8
8
|
|
|
9
9
|
export const getStaticPaths = (async () => {
|
|
10
10
|
const paths: Awaited<ReturnType<GetStaticPaths>> = [];
|
|
@@ -19,6 +19,6 @@ export const getStaticPaths = (async () => {
|
|
|
19
19
|
const { lang, vendor } = Astro.props;
|
|
20
20
|
---
|
|
21
21
|
|
|
22
|
-
<BaseLayout title={`${vendor} — ${
|
|
22
|
+
<BaseLayout title={`${vendor} — ${siteName(lang)}`} lang={lang} path={`vendors/${vendor}/`}>
|
|
23
23
|
<VendorIndex lang={lang} vendor={vendor} />
|
|
24
24
|
</BaseLayout>
|