diff --git a/.prettierignore b/.prettierignore index 12c98894584..79270814364 100644 --- a/.prettierignore +++ b/.prettierignore @@ -17,3 +17,6 @@ pnpm-lock.yaml # https://github.com/withastro/prettier-plugin-astro/issues/337 packages/starlight/user-components/Tabs.astro + +# Malformed YAML file used for testing +packages/starlight/__tests__/i18n/malformed-yaml-src/content/i18n/*.yml diff --git a/docs/src/content/docs/components/icons.mdx b/docs/src/content/docs/components/icons.mdx index 73f9d861a1b..042b152794b 100644 --- a/docs/src/content/docs/components/icons.mdx +++ b/docs/src/content/docs/components/icons.mdx @@ -64,7 +64,7 @@ The [`class`](#class) attribute can be used to add custom CSS classes to the ico ```mdx -import { Card } from '@astrojs/starlight/components'; +import { Icon } from '@astrojs/starlight/components'; diff --git a/docs/src/content/docs/de/components/icons.mdx b/docs/src/content/docs/de/components/icons.mdx index b04ea3ef86a..58e5ec44ee0 100644 --- a/docs/src/content/docs/de/components/icons.mdx +++ b/docs/src/content/docs/de/components/icons.mdx @@ -64,7 +64,7 @@ Das Attribut [`class`](#class) kann verwendet werden, um dem Symbol eigene CSS-K ```mdx -import { Card } from '@astrojs/starlight/components'; +import { Icon } from '@astrojs/starlight/components'; diff --git a/docs/src/content/docs/es/components/icons.mdx b/docs/src/content/docs/es/components/icons.mdx index b34ec906e42..35d005842ae 100644 --- a/docs/src/content/docs/es/components/icons.mdx +++ b/docs/src/content/docs/es/components/icons.mdx @@ -64,7 +64,7 @@ El atributo [`class`](#class) se puede usar para agregar clases CSS personalizad ```mdx -import { Card } from '@astrojs/starlight/components'; +import { Icon } from '@astrojs/starlight/components'; diff --git a/docs/src/content/docs/fr/components/icons.mdx b/docs/src/content/docs/fr/components/icons.mdx index 5eedbeb8761..00721112c8d 100644 --- a/docs/src/content/docs/fr/components/icons.mdx +++ b/docs/src/content/docs/fr/components/icons.mdx @@ -64,7 +64,7 @@ L'attribut [`class`](#class) peut être utilisé pour ajouter des classes CSS pe ```mdx -import { Card } from '@astrojs/starlight/components'; +import { Icon } from '@astrojs/starlight/components'; diff --git a/docs/src/content/docs/ja/components/icons.mdx b/docs/src/content/docs/ja/components/icons.mdx index 3d844b00dcd..bb71c1c45f4 100644 --- a/docs/src/content/docs/ja/components/icons.mdx +++ b/docs/src/content/docs/ja/components/icons.mdx @@ -62,7 +62,7 @@ import { Icon } from '@astrojs/starlight/components'; ```mdx -import { Card } from '@astrojs/starlight/components'; +import { Icon } from '@astrojs/starlight/components'; diff --git a/docs/src/content/docs/ko/components/icons.mdx b/docs/src/content/docs/ko/components/icons.mdx index 74a400c692c..44a2477bde9 100644 --- a/docs/src/content/docs/ko/components/icons.mdx +++ b/docs/src/content/docs/ko/components/icons.mdx @@ -64,7 +64,7 @@ CSS 단위와 색상 값을 사용하여 아이콘의 모양을 조정하는 데 ```mdx -import { Card } from '@astrojs/starlight/components'; +import { Icon } from '@astrojs/starlight/components'; diff --git a/docs/src/content/docs/ru/components/icons.mdx b/docs/src/content/docs/ru/components/icons.mdx index 371f1c6b1b8..ca95a2400bb 100644 --- a/docs/src/content/docs/ru/components/icons.mdx +++ b/docs/src/content/docs/ru/components/icons.mdx @@ -64,7 +64,7 @@ import { Icon } from '@astrojs/starlight/components'; ```mdx -import { Card } from '@astrojs/starlight/components'; +import { Icon } from '@astrojs/starlight/components'; diff --git a/docs/src/content/docs/zh-cn/components/icons.mdx b/docs/src/content/docs/zh-cn/components/icons.mdx index af1219e1e67..bd425fc4669 100644 --- a/docs/src/content/docs/zh-cn/components/icons.mdx +++ b/docs/src/content/docs/zh-cn/components/icons.mdx @@ -64,7 +64,7 @@ import { Icon } from '@astrojs/starlight/components'; ```mdx -import { Card } from '@astrojs/starlight/components'; +import { Icon } from '@astrojs/starlight/components'; diff --git a/examples/basics/package.json b/examples/basics/package.json index 1079b420406..52788edc873 100644 --- a/examples/basics/package.json +++ b/examples/basics/package.json @@ -11,7 +11,7 @@ "astro": "astro" }, "dependencies": { - "@astrojs/starlight": "^0.28.5", + "@astrojs/starlight": "^0.28.6", "astro": "^4.15.3", "sharp": "^0.32.5" } diff --git a/examples/markdoc/package.json b/examples/markdoc/package.json index 7c5a550bb5e..e5f44be729a 100644 --- a/examples/markdoc/package.json +++ b/examples/markdoc/package.json @@ -12,7 +12,7 @@ }, "dependencies": { "@astrojs/markdoc": "^0.11.4", - "@astrojs/starlight": "^0.28.5", + "@astrojs/starlight": "^0.28.6", "@astrojs/starlight-markdoc": "^0.1.0", "astro": "^4.15.3", "sharp": "^0.32.5" diff --git a/examples/tailwind/package.json b/examples/tailwind/package.json index 2343eddee6d..6d909047b7f 100644 --- a/examples/tailwind/package.json +++ b/examples/tailwind/package.json @@ -11,7 +11,7 @@ "astro": "astro" }, "dependencies": { - "@astrojs/starlight": "^0.28.5", + "@astrojs/starlight": "^0.28.6", "@astrojs/starlight-tailwind": "^2.0.3", "@astrojs/tailwind": "^5.1.0", "astro": "^4.15.3", diff --git a/packages/starlight/CHANGELOG.md b/packages/starlight/CHANGELOG.md index a34b7179ba2..6e394950312 100644 --- a/packages/starlight/CHANGELOG.md +++ b/packages/starlight/CHANGELOG.md @@ -1,5 +1,11 @@ # @astrojs/starlight +## 0.28.6 + +### Patch Changes + +- [#2565](https://github.com/withastro/starlight/pull/2565) [`236467b`](https://github.com/withastro/starlight/commit/236467bb745cea7a284ae3d398874d3edbcd846e) Thanks [@HiDeoo](https://github.com/HiDeoo)! - Fixes an issue with custom UI strings defined in YAML files not being loaded in some contexts. + ## 0.28.5 ### Patch Changes diff --git a/packages/starlight/__tests__/i18n/malformed-src/content/i18n/en.json b/packages/starlight/__tests__/i18n/malformed-json-src/content/i18n/en.json similarity index 100% rename from packages/starlight/__tests__/i18n/malformed-src/content/i18n/en.json rename to packages/starlight/__tests__/i18n/malformed-json-src/content/i18n/en.json diff --git a/packages/starlight/__tests__/i18n/malformed-yaml-src/content/i18n/en.yml b/packages/starlight/__tests__/i18n/malformed-yaml-src/content/i18n/en.yml new file mode 100644 index 00000000000..6fa1f17258a --- /dev/null +++ b/packages/starlight/__tests__/i18n/malformed-yaml-src/content/i18n/en.yml @@ -0,0 +1 @@ +test: 'Malformed YAML file with dangling trailing comma', diff --git a/packages/starlight/__tests__/i18n/src/content/i18n/fr.yml b/packages/starlight/__tests__/i18n/src/content/i18n/fr.yml new file mode 100644 index 00000000000..df8ac1bd57b --- /dev/null +++ b/packages/starlight/__tests__/i18n/src/content/i18n/fr.yml @@ -0,0 +1 @@ +page.editLink: Rendre cette page différente diff --git a/packages/starlight/__tests__/i18n/translations-fs.test.ts b/packages/starlight/__tests__/i18n/translations-fs.test.ts index 0115f1d355d..549d2cb99ab 100644 --- a/packages/starlight/__tests__/i18n/translations-fs.test.ts +++ b/packages/starlight/__tests__/i18n/translations-fs.test.ts @@ -1,5 +1,6 @@ import { describe, expect, test } from 'vitest'; import { createTranslationSystemFromFs } from '../../utils/translations-fs'; +import { YAMLException } from 'js-yaml'; describe('createTranslationSystemFromFs', () => { test('creates a translation system that returns default strings', () => { @@ -18,14 +19,21 @@ describe('createTranslationSystemFromFs', () => { test('creates a translation system that uses custom strings', () => { const useTranslations = createTranslationSystemFromFs( { - locales: { en: { label: 'English', dir: 'ltr' } }, + locales: { + en: { label: 'English', dir: 'ltr', lang: 'en' }, + fr: { label: 'Français', dir: 'ltr', lang: 'fr' }, + }, defaultLocale: { label: 'English', locale: 'en', dir: 'ltr' }, }, // Using `src/` to load custom files in this test fixture. { srcDir: new URL('./src/', import.meta.url) } ); - const t = useTranslations('en'); + // From an i18n JSON file + let t = useTranslations('en'); expect(t('page.editLink')).toMatchInlineSnapshot('"Make this page different"'); + // From an i18n YAML file + t = useTranslations('fr'); + expect(t('page.editLink')).toMatchInlineSnapshot('"Rendre cette page différente"'); }); test('supports root locale', () => { @@ -68,12 +76,22 @@ describe('createTranslationSystemFromFs', () => { expect(() => createTranslationSystemFromFs( { locales: {}, defaultLocale: { label: 'English', locale: 'en', dir: 'ltr' } }, - // Using `malformed-src/` to trigger syntax error in bad JSON file. - { srcDir: new URL('./malformed-src/', import.meta.url) } + // Using `malformed-json-src/` to trigger syntax error in bad JSON file. + { srcDir: new URL('./malformed-json-src/', import.meta.url) } ) ).toThrow(SyntaxError); }); + test('throws on malformed i18n YAML', () => { + expect(() => + createTranslationSystemFromFs( + { locales: {}, defaultLocale: { label: 'English', locale: 'en', dir: 'ltr' } }, + // Using `malformed-yaml-src/` to trigger syntax error in bad YAML file. + { srcDir: new URL('./malformed-yaml-src/', import.meta.url) } + ) + ).toThrow(YAMLException); + }); + test('creates a translation system that uses custom strings injected by plugins', () => { const useTranslations = createTranslationSystemFromFs( { diff --git a/packages/starlight/package.json b/packages/starlight/package.json index f2c2bc21538..afd7cc7a1d7 100644 --- a/packages/starlight/package.json +++ b/packages/starlight/package.json @@ -1,6 +1,6 @@ { "name": "@astrojs/starlight", - "version": "0.28.5", + "version": "0.28.6", "description": "Build beautiful, high-performance documentation websites with Astro", "scripts": { "test": "vitest", @@ -177,6 +177,7 @@ "devDependencies": { "@astrojs/markdown-remark": "^5.1.0", "@playwright/test": "^1.45.0", + "@types/js-yaml": "^4.0.9", "@types/node": "^18.16.19", "@vitest/coverage-v8": "^1.6.0", "astro": "^4.15.3", @@ -196,6 +197,7 @@ "hast-util-to-string": "^3.0.0", "hastscript": "^9.0.0", "i18next": "^23.11.5", + "js-yaml": "^4.1.0", "mdast-util-directive": "^3.0.0", "mdast-util-to-markdown": "^2.1.0", "mdast-util-to-string": "^4.0.0", diff --git a/packages/starlight/utils/translations-fs.ts b/packages/starlight/utils/translations-fs.ts index cd927aa5b11..4bcfcdaa6bd 100644 --- a/packages/starlight/utils/translations-fs.ts +++ b/packages/starlight/utils/translations-fs.ts @@ -1,9 +1,14 @@ import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import yaml from 'js-yaml'; import type { i18nSchemaOutput } from '../schemas/i18n'; import { createTranslationSystem } from './createTranslationSystem'; import type { StarlightConfig } from './user-config'; import type { AstroConfig } from 'astro'; +const contentCollectionFileExtensions = ['.json', '.yaml', '.yml']; + /** * Loads and creates a translation system from the file system. * Only for use in integration code. @@ -23,15 +28,18 @@ export function createTranslationSystemFromFs( // Load the user’s i18n directory const files = fs.readdirSync(i18nDir, 'utf-8'); // Load the user’s i18n collection and ignore the error if it doesn’t exist. - userTranslations = Object.fromEntries( - files - .filter((file) => file.endsWith('.json')) - .map((file) => { - const id = file.slice(0, -5); - const data = JSON.parse(fs.readFileSync(new URL(file, i18nDir), 'utf-8')); - return [id, data] as const; - }) - ); + for (const file of files) { + const filePath = path.parse(file); + if (!contentCollectionFileExtensions.includes(filePath.ext)) continue; + const id = filePath.name; + const url = new URL(filePath.base, i18nDir); + const content = fs.readFileSync(new URL(file, i18nDir), 'utf-8'); + const data = + filePath.ext === '.json' + ? JSON.parse(content) + : yaml.load(content, { filename: fileURLToPath(url) }); + userTranslations[id] = data as i18nSchemaOutput; + } } catch (e: unknown) { if (e instanceof Error && 'code' in e && e.code === 'ENOENT') { // i18nDir doesn’t exist, so we ignore the error. diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 556e52ec5e4..f403311f208 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -73,7 +73,7 @@ importers: examples/basics: dependencies: '@astrojs/starlight': - specifier: ^0.28.5 + specifier: ^0.28.6 version: link:../../packages/starlight astro: specifier: ^4.15.3 @@ -88,7 +88,7 @@ importers: specifier: ^0.11.4 version: 0.11.4(astro@4.15.3) '@astrojs/starlight': - specifier: ^0.28.5 + specifier: ^0.28.6 version: link:../../packages/starlight '@astrojs/starlight-markdoc': specifier: ^0.1.0 @@ -103,7 +103,7 @@ importers: examples/tailwind: dependencies: '@astrojs/starlight': - specifier: ^0.28.5 + specifier: ^0.28.6 version: link:../../packages/starlight '@astrojs/starlight-tailwind': specifier: ^2.0.3 @@ -197,6 +197,9 @@ importers: i18next: specifier: ^23.11.5 version: 23.11.5 + js-yaml: + specifier: ^4.1.0 + version: 4.1.0 mdast-util-directive: specifier: ^3.0.0 version: 3.0.0 @@ -234,6 +237,9 @@ importers: '@playwright/test': specifier: ^1.45.0 version: 1.45.0 + '@types/js-yaml': + specifier: ^4.0.9 + version: 4.0.9 '@types/node': specifier: ^18.16.19 version: 18.16.19 @@ -1997,6 +2003,10 @@ packages: dependencies: '@types/unist': 3.0.0 + /@types/js-yaml@4.0.9: + resolution: {integrity: sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==} + dev: true + /@types/linkify-it@5.0.0: resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==} requiresBuild: true