Skip to content

Commit f895f75

Browse files
HiDeoodelucis
andauthored
Expose FS translation system to plugins (#2578)
Co-authored-by: Chris Swithinbank <357379+delucis@users.noreply.github.com> Co-authored-by: Chris Swithinbank <swithinbank@gmail.com>
1 parent 2df9d05 commit f895f75

22 files changed

+696
-261
lines changed

.changeset/chatty-jars-flash.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@astrojs/starlight': minor
3+
---
4+
5+
Adds a new [`HookParameters`](https://starlight.astro.build/reference/plugins/#hooks) utility type to get the type of a plugin hook’s arguments.

.changeset/large-balloons-compete.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
---
2+
'@astrojs/starlight': minor
3+
---
4+
5+
Exposes the built-in localization system in the Starlight plugin `config:setup` hook.
6+
7+
⚠️ **BREAKING CHANGE:**
8+
9+
This addition changes how Starlight plugins add or update translation strings used in Starlight’s localization APIs.
10+
Plugins previously using the [`injectTranslations()`](https://starlight.astro.build/reference/plugins/#injecttranslations) callback function from the plugin [`config:setup`](https://starlight.astro.build/reference/plugins/#configsetup) hook should now use the same function available in the [`i18n:setup`](https://starlight.astro.build/reference/plugins/#i18nsetup) hook.
11+
12+
```diff
13+
export default {
14+
name: 'plugin-with-translations',
15+
hooks: {
16+
- 'config:setup'({ injectTranslations }) {
17+
+ 'i18n:setup'({ injectTranslations }) {
18+
injectTranslations({
19+
en: {
20+
'myPlugin.doThing': 'Do the thing',
21+
},
22+
fr: {
23+
'myPlugin.doThing': 'Faire le truc',
24+
},
25+
});
26+
},
27+
},
28+
};
29+
```

.changeset/loud-wolves-decide.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@astrojs/starlight': minor
3+
---
4+
5+
Adds a new [`useTranslations()`](https://starlight.astro.build/reference/plugins/#usetranslations) callback function to the Starlight plugin `config:setup` hook to generate a utility function to access UI strings for a given language.

.changeset/polite-fishes-remain.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
---
2+
'@astrojs/starlight': minor
3+
---
4+
5+
Deprecates the Starlight plugin `setup` hook in favor of the new `config:setup` hook which provides the same functionality.
6+
7+
⚠️ **BREAKING CHANGE:**
8+
9+
The Starlight plugin `setup` hook is now deprecated and will be removed in a future release. Please update your plugins to use the new `config:setup` hook instead.
10+
11+
```diff
12+
export default {
13+
name: 'plugin-with-translations',
14+
hooks: {
15+
- 'setup'({ config }) {
16+
+ 'config:setup'({ config }) {
17+
// Your plugin configuration setup code
18+
},
19+
},
20+
};
21+
```

.changeset/stupid-turkeys-appear.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
'@astrojs/starlight-docsearch': minor
3+
---
4+
5+
⚠️ **BREAKING CHANGE:** The minimum supported version of Starlight is now 0.32.0
6+
7+
Please use the `@astrojs/upgrade` command to upgrade your project:
8+
9+
```sh
10+
npx @astrojs/upgrade
11+
```

.changeset/wet-cherries-try.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@astrojs/starlight': minor
3+
---
4+
5+
Adds a new [`absolutePathToLang()`](https://starlight.astro.build/reference/plugins/#absolutepathtolang) callback function to the Starlight plugin `config:setup` to get the language for a given absolute file path.

docs/src/content/docs/guides/i18n.mdx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,9 @@ export const GET = (context) => {
299299
};
300300
```
301301

302+
In the context of a Starlight plugin, you can use the [`useTranslations()`](/reference/plugins/#usetranslations) helper to access this API for a specific language.
303+
See the [plugins reference](/reference/plugins/) for more information.
304+
302305
### Rendering a UI string
303306

304307
Render UI strings using the `locals.t()` function.

docs/src/content/docs/reference/plugins.md

Lines changed: 156 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,21 @@ See below for details of the different properties and hook parameters.
1919
interface StarlightPlugin {
2020
name: string;
2121
hooks: {
22-
setup: (options: {
22+
'i18n:setup'?: (options: {
23+
injectTranslations: (
24+
translations: Record<string, Record<string, string>>
25+
) => void;
26+
}) => void | Promise<void>;
27+
'config:setup': (options: {
2328
config: StarlightUserConfig;
2429
updateConfig: (newConfig: StarlightUserConfig) => void;
2530
addIntegration: (integration: AstroIntegration) => void;
2631
astroConfig: AstroConfig;
2732
command: 'dev' | 'build' | 'preview';
2833
isRestart: boolean;
2934
logger: AstroIntegrationLogger;
30-
injectTranslations: (Record<string, Record<string, string>>) => void;
35+
useTranslations: (lang: string) => I18nT;
36+
absolutePathToLang: (path: string) => string;
3137
}) => void | Promise<void>;
3238
};
3339
}
@@ -41,12 +47,100 @@ A plugin must provide a unique name that describes it. The name is used when [lo
4147

4248
## `hooks`
4349

44-
Hooks are functions which Starlight calls to run plugin code at specific times. Currently, Starlight supports a single `setup` hook.
50+
Hooks are functions which Starlight calls to run plugin code at specific times.
51+
52+
To get the type of a hook's arguments, use the `HookParameters` utility type and pass in the hook name.
53+
In the following example, the `options` parameter is typed to match the arguments passed to the `config:setup` hook:
54+
55+
```ts
56+
import type { HookParameters } from '@astrojs/starlight/types';
57+
58+
function configSetup(options: HookParameters['config:setup']) {
59+
options.useTranslations('en');
60+
}
61+
```
62+
63+
### `i18n:setup`
64+
65+
Plugin internationalization setup function called when Starlight is initialized.
66+
The `i18n:setup` hook can be used to inject translation strings so a plugin can support different locales.
67+
These translations will be available via [`useTranslations()`](#usetranslations) in the `config:setup` hook and in UI components via [`Astro.locals.t()`](/guides/i18n/#using-ui-translations).
68+
69+
The `i18n:setup` hook is called with the following options:
70+
71+
#### `injectTranslations`
72+
73+
**type:** `(translations: Record<string, Record<string, string>>) => void`
74+
75+
A callback function to add or update translation strings used in Starlight’s [localization APIs](/guides/i18n/#using-ui-translations).
76+
77+
In the following example, a plugin injects translations for a custom UI string named `myPlugin.doThing` for the `en` and `fr` locales:
78+
79+
```ts {6-13} /(injectTranslations)[^(]/
80+
// plugin.ts
81+
export default {
82+
name: 'plugin-with-translations',
83+
hooks: {
84+
'i18n:setup'({ injectTranslations }) {
85+
injectTranslations({
86+
en: {
87+
'myPlugin.doThing': 'Do the thing',
88+
},
89+
fr: {
90+
'myPlugin.doThing': 'Faire le truc',
91+
},
92+
});
93+
},
94+
},
95+
};
96+
```
97+
98+
To use the injected translations in your plugin UI, follow the [“Using UI translations” guide](/guides/i18n/#using-ui-translations).
99+
If you need to use UI strings in the context of the [`config:setup`](#configsetup) hook of your plugin, you can use the [`useTranslations()`](#usetranslations) callback.
100+
101+
Types for a plugin’s injected translation strings are generated automatically in a user’s project, but are not yet available when working in your plugin’s codebase.
102+
To type the `locals.t` object in the context of your plugin, declare the following global namespaces in a TypeScript declaration file:
103+
104+
```ts
105+
// env.d.ts
106+
declare namespace App {
107+
type StarlightLocals = import('@astrojs/starlight').StarlightLocals;
108+
// Define the `locals.t` object in the context of a plugin.
109+
interface Locals extends StarlightLocals {}
110+
}
111+
112+
declare namespace StarlightApp {
113+
// Define the additional plugin translations in the `I18n` interface.
114+
interface I18n {
115+
'myPlugin.doThing': string;
116+
}
117+
}
118+
```
119+
120+
You can also infer the types for the `StarlightApp.I18n` interface from a source file if you have an object containing your translations.
121+
122+
For example, given the following source file:
123+
124+
```ts title="ui-strings.ts"
125+
export const UIStrings = {
126+
en: { 'myPlugin.doThing': 'Do the thing' },
127+
fr: { 'myPlugin.doThing': 'Faire le truc' },
128+
};
129+
```
130+
131+
The following declaration would infer types from the English keys in the source file:
132+
133+
```ts title="env.d.ts"
134+
declare namespace StarlightApp {
135+
type UIStrings = typeof import('./ui-strings').UIStrings.en;
136+
interface I18n extends UIStrings {}
137+
}
138+
```
45139

46-
### `hooks.setup`
140+
### `config:setup`
47141

48-
Plugin setup function called when Starlight is initialized (during the [`astro:config:setup`](https://docs.astro.build/en/reference/integrations-reference/#astroconfigsetup) integration hook).
49-
The `setup` hook can be used to update the Starlight configuration or add Astro integrations.
142+
Plugin configuration setup function called when Starlight is initialized (during the [`astro:config:setup`](https://docs.astro.build/en/reference/integrations-reference/#astroconfigsetup) integration hook).
143+
The `config:setup` hook can be used to update the Starlight configuration or add Astro integrations.
50144

51145
This hook is called with the following options:
52146

@@ -73,7 +167,7 @@ In the following example, a new [`social`](/reference/configuration/#social) med
73167
export default {
74168
name: 'add-twitter-plugin',
75169
hooks: {
76-
setup({ config, updateConfig }) {
170+
'config:setup'({ config, updateConfig }) {
77171
updateConfig({
78172
social: {
79173
...config.social,
@@ -100,7 +194,7 @@ import react from '@astrojs/react';
100194
export default {
101195
name: 'plugin-using-react',
102196
hooks: {
103-
setup({ addIntegration, astroConfig }) {
197+
'config:setup'({ addIntegration, astroConfig }) {
104198
const isReactLoaded = astroConfig.integrations.find(
105199
({ name }) => name === '@astrojs/react'
106200
);
@@ -149,7 +243,7 @@ All logged messages will be prefixed with the plugin name.
149243
export default {
150244
name: 'long-process-plugin',
151245
hooks: {
152-
setup({ logger }) {
246+
'config:setup'({ logger }) {
153247
logger.info('Starting long process…');
154248
// Some long process…
155249
},
@@ -163,70 +257,78 @@ The example above will log a message that includes the provided info message:
163257
[long-process-plugin] Starting long process…
164258
```
165259

166-
#### `injectTranslations`
167-
168-
**type:** `(translations: Record<string, Record<string, string>>) => void`
260+
#### `useTranslations`
169261

170-
A callback function to add or update translation strings used in Starlight’s [localization APIs](/guides/i18n/#using-ui-translations).
262+
**type:** `(lang: string) => I18nT`
171263

172-
In the following example, a plugin injects translations for a custom UI string named `myPlugin.doThing` for the `en` and `fr` locales:
264+
Call `useTranslations()` with a BCP-47 language tag to generate a utility function that provides access to UI strings for that language.
265+
`useTranslations()` returns an equivalent of the `Astro.locals.t()` API that is available in Astro components.
266+
To learn more about the available APIs, see the [“Using UI translations”](/guides/i18n/#using-ui-translations) guide.
173267

174-
```ts {6-13} /(injectTranslations)[^(]/
268+
```ts {6}
175269
// plugin.ts
176270
export default {
177-
name: 'plugin-with-translations',
271+
name: 'plugin-use-translations',
178272
hooks: {
179-
setup({ injectTranslations }) {
180-
injectTranslations({
181-
en: {
182-
'myPlugin.doThing': 'Do the thing',
183-
},
184-
fr: {
185-
'myPlugin.doThing': 'Faire le truc',
186-
},
187-
});
273+
'config:setup'({ useTranslations, logger }) {
274+
const t = useTranslations('zh-CN');
275+
logger.info(t('builtWithStarlight.label'));
188276
},
189277
},
190278
};
191279
```
192280

193-
To use the injected translations in your plugin UI, follow the [“Using UI translations” guide](/guides/i18n/#using-ui-translations).
281+
The example above will log a message that includes a built-in UI string for the Simplified Chinese language:
194282

195-
Types for a plugin’s injected translation strings are generated automatically in a user’s project, but are not yet available when working in your plugin’s codebase.
196-
To type the `locals.t` object in the context of your plugin, declare the following global namespaces in a TypeScript declaration file:
283+
```shell
284+
[plugin-use-translations] 基于 Starlight 构建
285+
```
197286

198-
```ts
199-
// env.d.ts
200-
declare namespace App {
201-
type StarlightLocals = import('@astrojs/starlight').StarlightLocals;
202-
// Define the `locals.t` object in the context of a plugin.
203-
interface Locals extends StarlightLocals {}
204-
}
287+
#### `absolutePathToLang`
205288

206-
declare namespace StarlightApp {
207-
// Define the additional plugin translations in the `I18n` interface.
208-
interface I18n {
209-
'myPlugin.doThing': string;
210-
}
211-
}
212-
```
289+
**type:** `(path: string) => string`
213290

214-
You can also infer the types for the `StarlightApp.I18n` interface from a source file if you have an object containing your translations.
291+
Call `absolutePathToLang()` with an absolute file path to get the language for that file.
215292

216-
For example, given the following source file:
293+
This can be particularly useful when adding [remark or rehype plugins](https://docs.astro.build/en/guides/markdown-content/#markdown-plugins) to process Markdown or MDX files.
294+
The [virtual file format](https://github.com/vfile/vfile) used by these plugins includes the [absolute path](https://github.com/vfile/vfile#filepath) of the file being processed, which can be used with `absolutePathToLang()` to determine the language of the file.
295+
The returned language can be used with the [`useTranslations()`](#usetranslations) helper to get UI strings for that language.
217296

218-
```ts title="ui-strings.ts"
219-
export const UIStrings = {
220-
en: { 'myPlugin.doThing': 'Do the thing' },
221-
fr: { 'myPlugin.doThing': 'Faire le truc' },
297+
For example, given the following Starlight configuration:
298+
299+
```js
300+
starlight({
301+
title: 'My Docs',
302+
defaultLocale: 'en',
303+
locales: {
304+
// English docs in `src/content/docs/en/`
305+
en: { label: 'English' },
306+
// French docs in `src/content/docs/fr/`
307+
fr: { label: 'Français', lang: 'fr' },
308+
},
309+
});
310+
```
311+
312+
A plugin can determine the language of a file using its absolute path:
313+
314+
```ts {6-8} /fr/
315+
// plugin.ts
316+
export default {
317+
name: 'plugin-use-translations',
318+
hooks: {
319+
'config:setup'({ absolutePathToLang, useTranslations, logger }) {
320+
const lang = absolutePathToLang(
321+
'/absolute/path/to/project/src/content/docs/fr/index.mdx'
322+
);
323+
const t = useTranslations(lang);
324+
logger.info(t('aside.tip'));
325+
},
326+
},
222327
};
223328
```
224329

225-
The following declaration would infer types from the English keys in the source file:
330+
The example above will log a message that includes a built-in UI string for the French language:
226331

227-
```ts title="env.d.ts"
228-
declare namespace StarlightApp {
229-
type UIStrings = typeof import('./ui-strings').UIStrings.en;
230-
interface I18n extends UIStrings {}
231-
}
332+
```shell
333+
[plugin-use-translations] Astuce
232334
```

packages/docsearch/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ export default function starlightDocSearch(userConfig: DocSearchUserConfig): Sta
9595
return {
9696
name: 'starlight-docsearch',
9797
hooks: {
98-
setup({ addIntegration, config, logger, updateConfig }) {
98+
'config:setup'({ addIntegration, config, logger, updateConfig }) {
9999
// If the user has already has a custom override for the Search component, don't override it.
100100
if (config.components?.Search) {
101101
logger.warn(

packages/docsearch/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
"./schema": "./schema.ts"
2626
},
2727
"peerDependencies": {
28-
"@astrojs/starlight": ">=0.30.0"
28+
"@astrojs/starlight": ">=0.32.0"
2929
},
3030
"dependencies": {
3131
"@docsearch/css": "^3.6.0",

0 commit comments

Comments
 (0)