From d3ee6fc643de7a320a6bb83432cdcfbb0a4e4289 Mon Sep 17 00:00:00 2001 From: Chris Swithinbank Date: Tue, 23 May 2023 15:18:26 +0200 Subject: Implement UI string translation using data collections (#78) --- .changeset/afraid-zoos-retire.md | 16 ++++ .changeset/light-eggs-relax.md | 5 ++ docs/package.json | 2 +- docs/src/content/config.ts | 7 +- docs/src/content/docs/guides/i18n.md | 59 ++++++++++++++ examples/basics/package.json | 2 +- examples/basics/src/content/config.ts | 7 +- package.json | 2 +- packages/starlight/404.astro | 4 +- packages/starlight/components/EditLink.astro | 5 +- .../components/FallbackContentNotice.astro | 9 ++- packages/starlight/components/Header.astro | 4 +- packages/starlight/components/LanguageSelect.astro | 5 +- packages/starlight/components/LastUpdated.astro | 7 +- .../starlight/components/MobileMenuToggle.astro | 9 ++- packages/starlight/components/PrevNextLinks.astro | 9 ++- packages/starlight/components/RightSidebar.astro | 7 +- packages/starlight/components/Search.astro | 25 ++++-- packages/starlight/components/Sidebar.astro | 8 +- packages/starlight/components/SkipLink.astro | 12 ++- .../starlight/components/TableOfContents.astro | 15 +++- .../TableOfContents/MobileTableOfContents.astro | 14 +++- .../TableOfContents/TableOfContentsList.astro | 4 +- packages/starlight/components/ThemeSelect.astro | 20 +++-- packages/starlight/index.astro | 18 ++--- packages/starlight/layout/PageFrame.astro | 12 ++- packages/starlight/package.json | 4 +- packages/starlight/schema.ts | 1 + packages/starlight/schemas/i18n.ts | 91 ++++++++++++++++++++++ packages/starlight/translations/de.json | 20 +++++ packages/starlight/translations/en.json | 20 +++++ packages/starlight/translations/es.json | 20 +++++ packages/starlight/translations/index.ts | 10 +++ packages/starlight/utils/slugs.ts | 6 +- packages/starlight/utils/translations.ts | 58 ++++++++++++++ packages/starlight/utils/user-config.ts | 7 +- pnpm-lock.yaml | 69 ++++++++++++---- 37 files changed, 513 insertions(+), 80 deletions(-) create mode 100644 .changeset/afraid-zoos-retire.md create mode 100644 .changeset/light-eggs-relax.md create mode 100644 packages/starlight/schemas/i18n.ts create mode 100644 packages/starlight/translations/de.json create mode 100644 packages/starlight/translations/en.json create mode 100644 packages/starlight/translations/es.json create mode 100644 packages/starlight/translations/index.ts create mode 100644 packages/starlight/utils/translations.ts diff --git a/.changeset/afraid-zoos-retire.md b/.changeset/afraid-zoos-retire.md new file mode 100644 index 00000000..e2a41c27 --- /dev/null +++ b/.changeset/afraid-zoos-retire.md @@ -0,0 +1,16 @@ +--- +'@astrojs/starlight': patch +--- + +Add support for customising and translating Starlight’s UI. + +Users can provide translations in JSON files in `src/content/i18n/` which is a data collection. For example, a `src/content/i18n/de.json` might translate the search UI: + +```json +{ + "search.label": "Suchen", + "search.shortcutLabel": "(Drücke / zum Suchen)" +} +``` + +This change also allows Starlight to provide built-in support for more languages than just English and adds German & Spanish support. diff --git a/.changeset/light-eggs-relax.md b/.changeset/light-eggs-relax.md new file mode 100644 index 00000000..f10a7525 --- /dev/null +++ b/.changeset/light-eggs-relax.md @@ -0,0 +1,5 @@ +--- +"@astrojs/starlight": patch +--- + +Require a minimum Astro version of 2.5.0 diff --git a/docs/package.json b/docs/package.json index 312abf25..a5b8663c 100644 --- a/docs/package.json +++ b/docs/package.json @@ -15,7 +15,7 @@ }, "dependencies": { "@astrojs/starlight": "workspace:*", - "astro": "^2.4.3" + "astro": "^2.5.0" }, "devDependencies": { "@size-limit/file": "^8.2.4", diff --git a/docs/src/content/config.ts b/docs/src/content/config.ts index 4cdd345e..02ea2ac0 100644 --- a/docs/src/content/config.ts +++ b/docs/src/content/config.ts @@ -1,8 +1,7 @@ import { defineCollection } from 'astro:content'; -import { docsSchema } from '@astrojs/starlight/schema'; +import { docsSchema, i18nSchema } from '@astrojs/starlight/schema'; export const collections = { - docs: defineCollection({ - schema: docsSchema(), - }), + docs: defineCollection({ schema: docsSchema() }), + i18n: defineCollection({ type: 'data', schema: i18nSchema() }), }; diff --git a/docs/src/content/docs/guides/i18n.md b/docs/src/content/docs/guides/i18n.md index 13fe9e3f..eb2afc76 100644 --- a/docs/src/content/docs/guides/i18n.md +++ b/docs/src/content/docs/guides/i18n.md @@ -92,8 +92,67 @@ When using a `root` locale, place pages for that language directly in `src/conte - zh/ - index.md +#### Monolingual sites + +If you have a single language site, you can set the root locale to configure its language. +This allows you to override Starlight’s default language, which is English, but won’t enable other internationalization features like the language picker. + ## Fallback content Starlight expects you to create equivalent pages in all your languages. For example, if you have an `en/about.md` file, you should create an `about.md` for each other language you support. If a translation is not yet available for a language, Starlight will show readers the content for that page in the default language (set via `defaultLocale`). For example, if you have not yet created a French version of an about page and your default language is English, visitors to `/fr/about` will see the English content. This helps you add content in your default language and then progressively translate it when your translators have time. + +## Translate Starlight’s UI + +Some of Starlight’s default UI requires text labels to work. +For example, the table of contents on this page has an “On this page” heading in English. +We aim to ship these labels in as many languages as possible but currently only have support for English, German, and Spanish. + +You can provide translations for additional languages you support — or override our default labels — via the `i18n` data collection. + +1. Configure the `i18n` data collection in `src/content/config.ts` if it isn’t configured already: + + ```js + import { defineCollection } from 'astro:content'; + import { docsSchema, i18nSchema } from '@astrojs/starlight/schema'; + + export const collections = { + docs: defineCollection({ schema: docsSchema() }), + i18n: defineCollection({ type: 'data', schema: i18nSchema() }), + }; + ``` + +2. Create a JSON file in `src/content/i18n/` for each locale you want to translate Starlight’s UI for. + For example, this would add translation files for Arabic and Simplified Chinese: + + - src/ + - content/ + - i18n/ + - ar.json + - zh-CN.json + +3. Add translations for the keys you want to translate to the JSON files. You can use the English defaults to help you translate: + + ```json + { + "skipLink.label": "Skip to content", + "search.label": "Search", + "search.shortcutLabel": "(Press / to Search)", + "search.cancelLabel": "Cancel", + "themeSelect.accessibleLabel": "Select theme", + "themeSelect.dark": "Dark", + "themeSelect.light": "Light", + "themeSelect.auto": "Auto", + "languageSelect.accessibleLabel": "Select language", + "menuButton.accessibleLabel": "Menu", + "sidebarNav.accessibleLabel": "Main", + "tableOfContents.onThisPage": "On this page", + "tableOfContents.overview": "Overview", + "i18n.untranslatedContent": "This content is not available in your language yet.", + "page.editLink": "Edit page", + "page.lastUpdated": "Last updated:", + "page.previousLink": "Next", + "page.nextLink": "Previous" + } + ``` diff --git a/examples/basics/package.json b/examples/basics/package.json index c5d7a3b9..4700f196 100644 --- a/examples/basics/package.json +++ b/examples/basics/package.json @@ -12,6 +12,6 @@ }, "dependencies": { "@astrojs/starlight": "^0.0.8", - "astro": "^2.4.1" + "astro": "^2.5.0" } } diff --git a/examples/basics/src/content/config.ts b/examples/basics/src/content/config.ts index 4cdd345e..02ea2ac0 100644 --- a/examples/basics/src/content/config.ts +++ b/examples/basics/src/content/config.ts @@ -1,8 +1,7 @@ import { defineCollection } from 'astro:content'; -import { docsSchema } from '@astrojs/starlight/schema'; +import { docsSchema, i18nSchema } from '@astrojs/starlight/schema'; export const collections = { - docs: defineCollection({ - schema: docsSchema(), - }), + docs: defineCollection({ schema: docsSchema() }), + i18n: defineCollection({ type: 'data', schema: i18nSchema() }), }; diff --git a/package.json b/package.json index f4cb8e31..ef3d5816 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "devDependencies": { "@changesets/changelog-github": "^0.4.8", "@changesets/cli": "^2.26.1", - "astro": "^2.4.3" + "astro": "^2.5.0" }, "packageManager": "pnpm@8.2.0" } diff --git a/packages/starlight/404.astro b/packages/starlight/404.astro index de897a79..00d0cab0 100644 --- a/packages/starlight/404.astro +++ b/packages/starlight/404.astro @@ -30,8 +30,8 @@ const { lang = 'en', dir = 'ltr', locale } = config.defaultLocale || {}; - -
+ +

404

diff --git a/packages/starlight/components/EditLink.astro b/packages/starlight/components/EditLink.astro index 833be123..c72c25ac 100644 --- a/packages/starlight/components/EditLink.astro +++ b/packages/starlight/components/EditLink.astro @@ -1,12 +1,15 @@ --- import type { CollectionEntry } from 'astro:content'; import config from 'virtual:starlight/user-config'; +import { useTranslations } from '../utils/translations'; interface Props { data: CollectionEntry<'docs'>['data']; id: CollectionEntry<'docs'>['id']; + locale: string | undefined; } +const t = useTranslations(Astro.props.locale); const { editUrl } = Astro.props.data; let { baseUrl } = config.editLink; @@ -20,4 +23,4 @@ const url = : undefined; --- -{editUrl !== false && url && Edit this page} +{editUrl !== false && url && {t('page.editLink')}} diff --git a/packages/starlight/components/FallbackContentNotice.astro b/packages/starlight/components/FallbackContentNotice.astro index fc05938d..0faf151b 100644 --- a/packages/starlight/components/FallbackContentNotice.astro +++ b/packages/starlight/components/FallbackContentNotice.astro @@ -1,5 +1,12 @@ --- +import { useTranslations } from '../utils/translations'; import Icon from './Icon.astro'; + +interface Props { + locale: string | undefined; +} + +const t = useTranslations(Astro.props.locale); ---

@@ -7,7 +14,7 @@ import Icon from './Icon.astro'; name={'warning'} size="1.5em" color="var(--sl-color-orange-high)" - />This content is not available in your language yet. + />{t('i18n.untranslatedContent')}