diff options
author | Chris Swithinbank | 2023-05-28 22:11:05 +0200 |
---|---|---|
committer | GitHub | 2023-05-28 22:11:05 +0200 |
commit | de24b54971577912979a3fb67570f4c95efe27a6 (patch) | |
tree | 86be98c39cfe7b7fb0bd2b9e76ded40232dbf9c2 | |
parent | fe1c489a41a48eceed3cf559374d07d67c325248 (diff) | |
download | IT.starlight-de24b54971577912979a3fb67570f4c95efe27a6.tar.gz IT.starlight-de24b54971577912979a3fb67570f4c95efe27a6.tar.bz2 IT.starlight-de24b54971577912979a3fb67570f4c95efe27a6.zip |
Support translations in sidebar config (#95)
-rw-r--r-- | .changeset/calm-buttons-wink.md | 5 | ||||
-rw-r--r-- | docs/astro.config.mjs | 26 | ||||
-rw-r--r-- | docs/src/content/docs/reference/configuration.md | 36 | ||||
-rw-r--r-- | packages/starlight/utils/i18n.ts | 16 | ||||
-rw-r--r-- | packages/starlight/utils/navigation.ts | 10 | ||||
-rw-r--r-- | packages/starlight/utils/user-config.ts | 44 |
6 files changed, 96 insertions, 41 deletions
diff --git a/.changeset/calm-buttons-wink.md b/.changeset/calm-buttons-wink.md new file mode 100644 index 00000000..22be7e42 --- /dev/null +++ b/.changeset/calm-buttons-wink.md @@ -0,0 +1,5 @@ +--- +"@astrojs/starlight": patch +--- + +Support translations in sidebar config diff --git a/docs/astro.config.mjs b/docs/astro.config.mjs index c81072e1..b64d8383 100644 --- a/docs/astro.config.mjs +++ b/docs/astro.config.mjs @@ -30,22 +30,14 @@ export default defineConfig({ }, ], locales: { - root: { - label: 'English', - lang: 'en', - }, - de: { - label: 'Deutsch', - lang: 'de', - }, - es: { - label: 'Español', - lang: 'es', - } + root: { label: 'English', lang: 'en' }, + de: { label: 'Deutsch', lang: 'de' }, + es: { label: 'Español', lang: 'es' }, }, sidebar: [ { label: 'Start Here', + translations: { de: 'Beginne hier', es: 'Comienza aqui' }, items: [ { label: 'Welcome, world', link: '/' }, { label: 'Getting Started', link: 'getting-started' }, @@ -54,15 +46,13 @@ export default defineConfig({ }, { label: 'Guides', - autogenerate: { - directory: 'guides', - }, + translations: { de: 'Anleitungen', es: 'GuÃas' }, + autogenerate: { directory: 'guides' }, }, { label: 'Reference', - autogenerate: { - directory: 'reference', - }, + translations: { de: 'Referenz', es: 'Referencias' }, + autogenerate: { directory: 'reference' }, }, ], }), diff --git a/docs/src/content/docs/reference/configuration.md b/docs/src/content/docs/reference/configuration.md index 06381c86..4413311d 100644 --- a/docs/src/content/docs/reference/configuration.md +++ b/docs/src/content/docs/reference/configuration.md @@ -89,18 +89,42 @@ starlight({ // A group linking to all pages in the reference directory. { label: 'Reference', - autogenerate: { - directory: 'reference', - }, + autogenerate: { directory: 'reference' }, }, ], }); ``` -:::note[Sorting] +#### Sorting + Autogenerated sidebar groups are sorted by filename alphabetically. For example, a page generated from `astro.md` would appear above the page for `starlight.md`. -::: + +#### Translating labels + +If your site is multilingual, each item’s `label` is considered to be in the default locale. You can set a `translations` property to provide labels for your other supported languages: + +```js +sidebar: [ + // An example sidebar with labels translated to French. + { + label: 'Start Here', + translations: { fr: 'Commencez ici' }, + items: [ + { + label: 'Getting Started', + translations: { fr: 'Bien démarrer' }, + link: '/getting-started', + }, + { + label: 'Project Structure', + translations: { fr: 'Structure du projet' }, + link: '/structure', + }, + ], + }, +]; +``` #### `SidebarGroup` @@ -108,10 +132,12 @@ For example, a page generated from `astro.md` would appear above the page for `s type SidebarGroup = | { label: string; + translations?: Record<string, string>; items: Array<LinkItem | SidebarGroup>; } | { label: string; + translations?: Record<string, string>; autogenerate: { directory: string; }; diff --git a/packages/starlight/utils/i18n.ts b/packages/starlight/utils/i18n.ts new file mode 100644 index 00000000..2dd43c3b --- /dev/null +++ b/packages/starlight/utils/i18n.ts @@ -0,0 +1,16 @@ +/** + * Get the string for the passed language from a dictionary object. + * + * TODO: Make this clever. Currently a simple key look-up, but should use + * BCP-47 mapping so that e.g. `en-US` returns `en` strings, and use the + * site’s default locale as a last resort. + * + * @example + * pickLang({ en: 'Hello', fr: 'Bonjour' }, 'en'); // => 'Hello' + */ +export function pickLang<T extends Record<string, string>>( + dictionary: T, + lang: keyof T +): string | undefined { + return dictionary[lang]; +} diff --git a/packages/starlight/utils/navigation.ts b/packages/starlight/utils/navigation.ts index e7e6a91e..bcdf79cf 100644 --- a/packages/starlight/utils/navigation.ts +++ b/packages/starlight/utils/navigation.ts @@ -1,8 +1,9 @@ import { basename, dirname } from 'node:path'; import config from 'virtual:starlight/user-config'; import { withBase } from './base'; +import { pickLang } from './i18n'; import { Route, getLocaleRoutes, routes } from './routing'; -import { slugToPathname } from './slugs'; +import { localeToLang, slugToPathname } from './slugs'; import type { AutoSidebarGroup, SidebarItem, @@ -48,7 +49,7 @@ function configItemToEntry( } else { return { type: 'group', - label: item.label, + label: pickLang(item.translations, localeToLang(locale)) || item.label, entries: item.items.map((i) => configItemToEntry(i, currentPathname, locale, routes) ), @@ -75,7 +76,7 @@ function groupFromAutogenerateConfig( const tree = treeify(dirDocs, localeDir); return { type: 'group', - label: item.label, + label: pickLang(item.translations, localeToLang(locale)) || item.label, entries: sidebarFromDir(tree, currentPathname, locale), }; } @@ -102,7 +103,8 @@ function linkFromConfig( // Inject current locale into link. if (locale) href = '/' + locale + href; } - return makeLink(href, item.label, currentPathname); + const label = pickLang(item.translations, localeToLang(locale)) || item.label; + return makeLink(href, label, currentPathname); } /** Create a link entry. */ diff --git a/packages/starlight/utils/user-config.ts b/packages/starlight/utils/user-config.ts index 9aea7325..bca6cfff 100644 --- a/packages/starlight/utils/user-config.ts +++ b/packages/starlight/utils/user-config.ts @@ -27,17 +27,20 @@ const LocaleSchema = z.object({ ), }); -const SidebarLinkItemSchema = z.object({ +const SidebarBaseSchema = z.object({ /** The visible label for this item in the sidebar. */ label: z.string(), + /** Translations of the `label` for each supported language. */ + translations: z.record(z.string()).default({}), +}); + +const SidebarLinkItemSchema = SidebarBaseSchema.extend({ /** The link to this item’s content. Can be a relative link to local files or the full URL of an external page. */ link: z.string(), }); export type SidebarLinkItem = z.infer<typeof SidebarLinkItemSchema>; -const AutoSidebarGroupSchema = z.object({ - /** The visible label for this item in the sidebar. */ - label: z.string(), +const AutoSidebarGroupSchema = SidebarBaseSchema.extend({ /** Enable autogenerating a sidebar category from a specific docs directory. */ autogenerate: z.object({ /** The directory to generate sidebar items for. */ @@ -49,20 +52,29 @@ const AutoSidebarGroupSchema = z.object({ }); export type AutoSidebarGroup = z.infer<typeof AutoSidebarGroupSchema>; -type ManualSidebarGroup = { - /** The visible label for this item in the sidebar. */ - label: string; +type ManualSidebarGroupInput = z.input<typeof SidebarBaseSchema> & { /** Array of links and subcategories to display in this category. */ items: Array< - | SidebarLinkItem - | z.infer<typeof AutoSidebarGroupSchema> - | ManualSidebarGroup + | z.input<typeof SidebarLinkItemSchema> + | z.input<typeof AutoSidebarGroupSchema> + | ManualSidebarGroupInput >; }; -const ManualSidebarGroupSchema: z.ZodType<ManualSidebarGroup> = z.object({ - /** The visible label for this item in the sidebar. */ - label: z.string(), +type ManualSidebarGroupOutput = z.output<typeof SidebarBaseSchema> & { + /** Array of links and subcategories to display in this category. */ + items: Array< + | z.output<typeof SidebarLinkItemSchema> + | z.output<typeof AutoSidebarGroupSchema> + | ManualSidebarGroupOutput + >; +}; + +const ManualSidebarGroupSchema: z.ZodType< + ManualSidebarGroupOutput, + z.ZodTypeDef, + ManualSidebarGroupInput +> = SidebarBaseSchema.extend({ /** Array of links and subcategories to display in this category. */ items: z.lazy(() => z @@ -83,7 +95,11 @@ const SidebarItemSchema = z.union([ export type SidebarItem = z.infer<typeof SidebarItemSchema>; const SidebarGroupSchema: z.ZodType< - ManualSidebarGroup | z.infer<typeof AutoSidebarGroupSchema> + | z.output<typeof ManualSidebarGroupSchema> + | z.output<typeof AutoSidebarGroupSchema>, + z.ZodTypeDef, + | z.input<typeof ManualSidebarGroupSchema> + | z.input<typeof AutoSidebarGroupSchema> > = z.union([ManualSidebarGroupSchema, AutoSidebarGroupSchema]); const UserConfigSchema = z.object({ |