summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHiDeoo2024-11-04 11:28:50 +0100
committerGitHub2024-11-04 11:28:50 +0100
commit236467bb745cea7a284ae3d398874d3edbcd846e (patch)
tree2dd7d22e0c4c9103d45071fd688e088314b91f82
parentc846696c48b3d21645185a001f00a820bddfc22b (diff)
downloadIT.starlight-236467bb745cea7a284ae3d398874d3edbcd846e.tar.gz
IT.starlight-236467bb745cea7a284ae3d398874d3edbcd846e.tar.bz2
IT.starlight-236467bb745cea7a284ae3d398874d3edbcd846e.zip
Add YAML support to the FS translation system (#2565)
-rw-r--r--.changeset/thick-houses-pull.md5
-rw-r--r--packages/starlight/__tests__/i18n/malformed-json-src/content/i18n/en.json (renamed from packages/starlight/__tests__/i18n/malformed-src/content/i18n/en.json)0
-rw-r--r--packages/starlight/__tests__/i18n/malformed-yaml-src/content/i18n/en.yml1
-rw-r--r--packages/starlight/__tests__/i18n/src/content/i18n/fr.yml1
-rw-r--r--packages/starlight/__tests__/i18n/translations-fs.test.ts26
-rw-r--r--packages/starlight/package.json2
-rw-r--r--packages/starlight/utils/translations-fs.ts26
-rw-r--r--pnpm-lock.yaml10
8 files changed, 58 insertions, 13 deletions
diff --git a/.changeset/thick-houses-pull.md b/.changeset/thick-houses-pull.md
new file mode 100644
index 00000000..69b82f1b
--- /dev/null
+++ b/.changeset/thick-houses-pull.md
@@ -0,0 +1,5 @@
+---
+'@astrojs/starlight': patch
+---
+
+Fixes an issue with custom UI strings defined in YAML files not being loaded in some contexts.
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
index e69de29b..e69de29b 100644
--- a/packages/starlight/__tests__/i18n/malformed-src/content/i18n/en.json
+++ b/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 00000000..6fa1f172
--- /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 00000000..df8ac1bd
--- /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 0115f1d3..549d2cb9 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 f2c2bc21..f0422afa 100644
--- a/packages/starlight/package.json
+++ b/packages/starlight/package.json
@@ -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 cd927aa5..4bcfcdaa 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<T extends i18nSchemaOutput>(
// 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 556e52ec..d95a2f9d 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -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