docs: add changeset and documentation for translations CLI commands
Adds a changeset for the new translations export/import CLI commands. Updates the internationalization docs with the CLI-based workflow as the recommended approach for managing translations, and adds the commands to the CLI reference. Also adds the internationalization page to the mkdocs navigation. Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com> Co-authored-by: Cursor <cursoragent@cursor.com> Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com> Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
---
|
||||
'@backstage/cli': minor
|
||||
---
|
||||
|
||||
Added `translations export` and `translations import` commands for managing translation files.
|
||||
|
||||
The `translations export` command discovers all `TranslationRef` definitions across frontend plugin dependencies and exports their default messages as JSON files. The `translations import` command generates `TranslationResource` wiring code from translated JSON files, ready to be plugged into the app.
|
||||
|
||||
Both commands support a `--pattern` option for controlling the message file layout, for example `--pattern '{lang}/{id}.json'` for language-based directory grouping.
|
||||
@@ -1,12 +1,12 @@
|
||||
---
|
||||
id: internationalization
|
||||
title: Internationalization (Experimental)
|
||||
description: Documentation on adding internationalization to the plugin
|
||||
title: Internationalization
|
||||
description: Documentation on adding internationalization to plugins and apps
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
The Backstage core function provides internationalization for plugins. The underlying library is [`i18next`](https://www.i18next.com/) with some additional Backstage typescript magic for type safety with keys.
|
||||
The Backstage core function provides internationalization for plugins and apps. The underlying library is [`i18next`](https://www.i18next.com/) with some additional Backstage typescript magic for type safety with keys.
|
||||
|
||||
## For a plugin developer
|
||||
|
||||
@@ -183,16 +183,56 @@ return (
|
||||
|
||||
The return type of the outer `t` function will be a `JSX.Element`, with the underlying value being a React fragment of the different parts of the message.
|
||||
|
||||
## For an application developer overwrite plugin messages
|
||||
## For an application developer
|
||||
|
||||
Step 1: Create translation resources
|
||||
As an app developer you can both override the default English messages of any plugin, and provide translations for additional languages.
|
||||
|
||||
You should separate different translations to their own files and import them in the main file:
|
||||
### Overriding messages
|
||||
|
||||
To customize specific messages without adding new languages, create a translation resource that overrides the default English messages:
|
||||
|
||||
```ts
|
||||
// packages/app/src/translations/catalog.ts
|
||||
|
||||
import { createTranslationResource } from '@backstage/frontend-plugin-api';
|
||||
import { catalogTranslationRef } from '@backstage/plugin-catalog/alpha';
|
||||
|
||||
export const catalogTranslations = createTranslationResource({
|
||||
ref: catalogTranslationRef,
|
||||
translations: {
|
||||
en: () =>
|
||||
Promise.resolve({
|
||||
default: {
|
||||
'indexPage.title': 'Service directory',
|
||||
'indexPage.createButtonTitle': 'Register new service',
|
||||
},
|
||||
}),
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
Then register it in your app:
|
||||
|
||||
```diff
|
||||
+ import { catalogTranslations } from './translations/catalog';
|
||||
|
||||
const app = createApp({
|
||||
+ __experimentalTranslations: {
|
||||
+ resources: [catalogTranslations],
|
||||
+ },
|
||||
})
|
||||
```
|
||||
|
||||
You only need to include the keys you want to override — any missing keys fall back to the plugin's defaults.
|
||||
|
||||
### Adding language translations
|
||||
|
||||
To add support for additional languages, create translation resources with lazy-loaded message files for each language:
|
||||
|
||||
```ts
|
||||
// packages/app/src/translations/userSettings.ts
|
||||
|
||||
import { createTranslationResource } from '@backstage/core-plugin-api/alpha';
|
||||
import { createTranslationResource } from '@backstage/frontend-plugin-api';
|
||||
import { userSettingsTranslationRef } from '@backstage/plugin-user-settings/alpha';
|
||||
|
||||
export const userSettingsTranslations = createTranslationResource({
|
||||
@@ -203,10 +243,12 @@ export const userSettingsTranslations = createTranslationResource({
|
||||
});
|
||||
```
|
||||
|
||||
The translation messages can be defined using `createTranslationMessages` for type safety:
|
||||
|
||||
```ts
|
||||
// packages/app/src/translations/userSettings-zh.ts
|
||||
|
||||
import { createTranslationMessages } from '@backstage/core-plugin-api/alpha';
|
||||
import { createTranslationMessages } from '@backstage/frontend-plugin-api';
|
||||
import { userSettingsTranslationRef } from '@backstage/plugin-user-settings/alpha';
|
||||
|
||||
const zh = createTranslationMessages({
|
||||
@@ -221,7 +263,7 @@ const zh = createTranslationMessages({
|
||||
export default zh;
|
||||
```
|
||||
|
||||
It's also possible to export the list of messages directly:
|
||||
Or as a plain object export:
|
||||
|
||||
```ts
|
||||
// packages/app/src/translations/userSettings-zh.ts
|
||||
@@ -239,11 +281,7 @@ export default {
|
||||
};
|
||||
```
|
||||
|
||||
You should change `zh` under the translations object to your local language.
|
||||
|
||||
Step 2: Config translations in `packages/app/src/App.tsx`
|
||||
|
||||
In an app you can both override the default messages, as well as register translations for additional languages:
|
||||
Register it with the available languages declared:
|
||||
|
||||
```diff
|
||||
+ import { userSettingsTranslations } from './translations/userSettings';
|
||||
@@ -256,6 +294,116 @@ In an app you can both override the default messages, as well as register transl
|
||||
})
|
||||
```
|
||||
|
||||
Step 3: Check everything is working correctly
|
||||
Go to the Settings page — you should see language switching buttons. Switch languages to verify your translations are loaded correctly.
|
||||
|
||||
Go to `Settings` page, you should see change language buttons just under change theme buttons. And then switch language, you should see language had changed
|
||||
### Using the CLI for full translation workflows
|
||||
|
||||
When translating your app to other languages at scale — especially when working with translation management systems (TMS) like Smartling, Crowdin, or Lokalise — the Backstage CLI provides `translations export` and `translations import` commands that automate the extraction and wiring of translation messages across all your plugin dependencies.
|
||||
|
||||
#### Exporting default messages
|
||||
|
||||
From your app package directory (e.g. `packages/app`), run:
|
||||
|
||||
```bash
|
||||
yarn backstage-cli translations export
|
||||
```
|
||||
|
||||
This scans all frontend plugin dependencies (including transitive ones) for `TranslationRef` definitions and writes their default English messages as JSON files:
|
||||
|
||||
```text
|
||||
translations/
|
||||
manifest.json
|
||||
messages/
|
||||
catalog.en.json
|
||||
org.en.json
|
||||
scaffolder.en.json
|
||||
...
|
||||
```
|
||||
|
||||
Each `.en.json` file contains the flattened message keys and their default values:
|
||||
|
||||
```json
|
||||
{
|
||||
"indexPage.title": "All your components",
|
||||
"indexPage.createButtonTitle": "Create new component",
|
||||
"entityPage.notFound": "Entity not found"
|
||||
}
|
||||
```
|
||||
|
||||
#### Creating translations
|
||||
|
||||
Copy the exported files and translate them for your target languages:
|
||||
|
||||
```bash
|
||||
cp translations/messages/catalog.en.json translations/messages/catalog.zh.json
|
||||
```
|
||||
|
||||
Then edit `catalog.zh.json` with the translated strings. You only need to include the keys you want to translate — missing keys fall back to the English defaults at runtime.
|
||||
|
||||
#### Generating wiring code
|
||||
|
||||
Once you have translated files in place, run:
|
||||
|
||||
```bash
|
||||
yarn backstage-cli translations import
|
||||
```
|
||||
|
||||
This generates a TypeScript module at `src/translations/resources.ts` that wires everything together:
|
||||
|
||||
```ts
|
||||
// This file is auto-generated by backstage-cli translations import
|
||||
// Do not edit manually.
|
||||
|
||||
import { createTranslationResource } from '@backstage/frontend-plugin-api';
|
||||
import { catalogTranslationRef } from '@backstage/plugin-catalog/alpha';
|
||||
|
||||
export default [
|
||||
createTranslationResource({
|
||||
ref: catalogTranslationRef,
|
||||
translations: {
|
||||
zh: () => import('../../translations/messages/catalog.zh.json'),
|
||||
},
|
||||
}),
|
||||
];
|
||||
```
|
||||
|
||||
Import the generated resources in your app:
|
||||
|
||||
```ts
|
||||
import translationResources from './translations/resources';
|
||||
|
||||
const app = createApp({
|
||||
__experimentalTranslations: {
|
||||
availableLanguages: ['en', 'zh'],
|
||||
resources: translationResources,
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
#### Custom file patterns
|
||||
|
||||
By default, message files use the pattern `{id}.{lang}.json` (e.g. `catalog.en.json`). You can change this with the `--pattern` option:
|
||||
|
||||
```bash
|
||||
yarn backstage-cli translations export --pattern '{lang}/{id}.json'
|
||||
```
|
||||
|
||||
This produces a directory structure grouped by language instead:
|
||||
|
||||
```text
|
||||
translations/messages/en/catalog.json
|
||||
translations/messages/zh/catalog.json
|
||||
```
|
||||
|
||||
The pattern is stored in the manifest, so the `import` command automatically uses the same layout.
|
||||
|
||||
#### Integration with a TMS
|
||||
|
||||
The exported JSON files are standard key-value pairs compatible with most translation management systems. A typical workflow looks like:
|
||||
|
||||
1. Run `translations export` to generate the source English files
|
||||
2. Upload the `.en.json` files to your TMS
|
||||
3. Download the translated files from your TMS into the `messages/` directory
|
||||
4. Run `translations import` to regenerate the wiring code
|
||||
|
||||
For full command reference, see the [CLI commands documentation](../tooling/cli/03-commands.md#translations-export).
|
||||
|
||||
@@ -24,6 +24,7 @@ repo [command] Command that run across an entire
|
||||
package [command] Lifecycle scripts for individual packages
|
||||
migrate [command] Migration utilities
|
||||
versions:bump [options] Bump Backstage packages to the latest versions
|
||||
translations [command] Translation message management
|
||||
clean Delete cache directories [DEPRECATED]
|
||||
build-workspace <workspace-dir> [packages...] Builds a temporary dist workspace from the provided
|
||||
packages
|
||||
@@ -428,6 +429,71 @@ YAML file that can be referenced in the GitHub integration configuration.
|
||||
Usage: backstage-cli create-github-app <github-org>
|
||||
```
|
||||
|
||||
## translations export
|
||||
|
||||
Export translation messages from all frontend plugin dependencies of the current
|
||||
package. This command must be run from within a package directory (e.g.
|
||||
`packages/app`), not from the repository root.
|
||||
|
||||
The command discovers all `TranslationRef` definitions in the dependency tree,
|
||||
extracts their default messages using the TypeScript type system, and writes
|
||||
them as JSON files along with a manifest.
|
||||
|
||||
For more details on the translation workflow, see the
|
||||
[Internationalization](../../plugins/internationalization.md) documentation.
|
||||
|
||||
```text
|
||||
Usage: backstage-cli translations export [options]
|
||||
|
||||
Options:
|
||||
--output <dir> Output directory for exported messages and manifest (default: "translations")
|
||||
--pattern <pattern> File path pattern for message files, with {id} and {lang}
|
||||
placeholders (default: "{id}.{lang}.json")
|
||||
-h, --help display help for command
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
Export translations with default settings:
|
||||
|
||||
```bash
|
||||
cd packages/app
|
||||
yarn backstage-cli translations export
|
||||
```
|
||||
|
||||
Export with language-based directory grouping:
|
||||
|
||||
```bash
|
||||
yarn backstage-cli translations export --pattern '{lang}/{id}.json'
|
||||
```
|
||||
|
||||
## translations import
|
||||
|
||||
Generate translation resource wiring code from translated JSON files. Reads the
|
||||
manifest and translated message files produced by `translations export`, and
|
||||
generates a TypeScript module that creates `TranslationResource` objects for each
|
||||
translated ref.
|
||||
|
||||
```text
|
||||
Usage: backstage-cli translations import [options]
|
||||
|
||||
Options:
|
||||
--input <dir> Input directory containing the manifest and translated message files (default: "translations")
|
||||
--output <path> Output path for the generated wiring module (default: "src/translations/resources.ts")
|
||||
--pattern <pattern> File path pattern for message files, with {id} and {lang}
|
||||
placeholders (default: "{id}.{lang}.json")
|
||||
-h, --help display help for command
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
Generate wiring code with default settings:
|
||||
|
||||
```bash
|
||||
cd packages/app
|
||||
yarn backstage-cli translations import
|
||||
```
|
||||
|
||||
## info
|
||||
|
||||
Outputs debug information which is useful when opening an issue. Outputs system
|
||||
|
||||
@@ -142,6 +142,7 @@ nav:
|
||||
- Composability System: 'plugins/composability.md'
|
||||
- Plugin Analytics: 'plugins/analytics.md'
|
||||
- Feature Flags: 'plugins/feature-flags.md'
|
||||
- Internationalization (i18n): 'plugins/internationalization.md'
|
||||
- OpenAPI:
|
||||
- Schema-first plugins with OpenAPI (Experimental): 'openapi/01-getting-started.md'
|
||||
- Generate a client from your OpenAPI spec: 'openapi/generate-client.md'
|
||||
|
||||
Reference in New Issue
Block a user