diff --git a/docs/plugins/internationalization.md b/docs/plugins/internationalization.md
index f1d5d8a81d..0c62058e5a 100644
--- a/docs/plugins/internationalization.md
+++ b/docs/plugins/internationalization.md
@@ -382,7 +382,7 @@ const app = createApp({
#### 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:
+By default, message files use the pattern `messages/{id}.{lang}.json` (e.g. `messages/catalog.en.json`). You can change this with the `--pattern` option:
```bash
yarn backstage-cli translations export --pattern '{lang}/{id}.json'
@@ -391,8 +391,8 @@ 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
+translations/en/catalog.json
+translations/zh/catalog.json
```
The pattern is stored in the manifest, so the `import` command automatically uses the same layout.
diff --git a/docs/tooling/cli/03-commands.md b/docs/tooling/cli/03-commands.md
index efdf197113..44b9058f19 100644
--- a/docs/tooling/cli/03-commands.md
+++ b/docs/tooling/cli/03-commands.md
@@ -447,8 +447,9 @@ Usage: backstage-cli translations export [options]
Options:
--output
Output directory for exported messages and manifest (default: "translations")
- --pattern File path pattern for message files, with {id} and {lang}
- placeholders (default: "{id}.{lang}.json")
+ --pattern File path pattern for message files relative to the output
+ directory, with {id} and {lang} placeholders
+ (default: "messages/{id}.{lang}.json")
-h, --help display help for command
```
diff --git a/packages/cli/src/modules/translations/commands/export.ts b/packages/cli/src/modules/translations/commands/export.ts
index ca8bd2c60b..cf6630ae39 100644
--- a/packages/cli/src/modules/translations/commands/export.ts
+++ b/packages/cli/src/modules/translations/commands/export.ts
@@ -39,12 +39,8 @@ export default async (options: ExportOptions) => {
paths.targetRoot,
);
- const messagesDir = resolvePath(paths.targetDir, options.output, 'messages');
- const manifestPath = resolvePath(
- paths.targetDir,
- options.output,
- 'manifest.json',
- );
+ const outputDir = resolvePath(paths.targetDir, options.output);
+ const manifestPath = resolvePath(outputDir, 'manifest.json');
const tsconfigPath = paths.resolveTargetRoot('tsconfig.json');
if (!(await fs.pathExists(tsconfigPath))) {
@@ -104,7 +100,7 @@ export default async (options: ExportOptions) => {
ref.id,
DEFAULT_LANGUAGE,
);
- const filePath = resolvePath(messagesDir, relPath);
+ const filePath = resolvePath(outputDir, relPath);
await fs.ensureDir(dirname(filePath));
await fs.writeJson(filePath, ref.messages, { spaces: 2 });
}
@@ -132,6 +128,6 @@ export default async (options: ExportOptions) => {
console.log(
`\nExported ${allRefs.length} translation ref(s) to ${options.output}/`,
);
- console.log(` Messages: ${options.output}/messages/${examplePath}`);
+ console.log(` Messages: ${options.output}/${examplePath}`);
console.log(` Manifest: ${options.output}/manifest.json`);
};
diff --git a/packages/cli/src/modules/translations/commands/import.ts b/packages/cli/src/modules/translations/commands/import.ts
index 35b554ce30..cab2030d1f 100644
--- a/packages/cli/src/modules/translations/commands/import.ts
+++ b/packages/cli/src/modules/translations/commands/import.ts
@@ -48,7 +48,6 @@ export default async (options: ImportOptions) => {
await readTargetPackage(paths.targetDir, paths.targetRoot);
const inputDir = resolvePath(paths.targetDir, options.input);
- const messagesDir = resolvePath(inputDir, 'messages');
const manifestPath = resolvePath(inputDir, 'manifest.json');
const outputPath = resolvePath(paths.targetDir, options.output);
@@ -59,13 +58,6 @@ export default async (options: ImportOptions) => {
);
}
- if (!(await fs.pathExists(messagesDir))) {
- throw new Error(
- `No messages directory found at ${messagesDir}. ` +
- 'Run "backstage-cli translations export" first.',
- );
- }
-
const manifest: Manifest = await fs.readJson(manifestPath);
if (!manifest.pattern) {
@@ -77,8 +69,10 @@ export default async (options: ImportOptions) => {
const parsePath = createMessagePathParser(pattern);
- // Discover all JSON files under the messages directory
- const allFiles = await collectJsonFiles(messagesDir);
+ // Discover all JSON files under the translations directory
+ const allFiles = (await collectJsonFiles(inputDir)).filter(
+ f => f !== 'manifest.json',
+ );
// Parse each file to extract id + lang, filtering out default language files
const translationsByRef = new Map<
@@ -120,7 +114,7 @@ export default async (options: ImportOptions) => {
console.log('No translated message files found.');
const example = formatMessagePath(pattern, '', 'sv');
console.log(
- `Add translated files as messages/${example} in the translations directory.`,
+ `Add translated files as ${example} in the translations directory.`,
);
return;
}
@@ -150,7 +144,7 @@ export default async (options: ImportOptions) => {
const jsonRelPath = posixPath.normalize(
relativePath(
resolvePath(outputPath, '..'),
- resolvePath(messagesDir, relPath),
+ resolvePath(inputDir, relPath),
),
);
return ` ${JSON.stringify(lang)}: () => import('./${jsonRelPath}'),`;
diff --git a/packages/cli/src/modules/translations/lib/messageFilePath.test.ts b/packages/cli/src/modules/translations/lib/messageFilePath.test.ts
index 6a6f3a8873..4c276e114c 100644
--- a/packages/cli/src/modules/translations/lib/messageFilePath.test.ts
+++ b/packages/cli/src/modules/translations/lib/messageFilePath.test.ts
@@ -26,13 +26,13 @@ describe('messageFilePath', () => {
describe('formatMessagePath', () => {
it('formats the default pattern', () => {
expect(formatMessagePath(DEFAULT_MESSAGE_PATTERN, 'org', 'en')).toBe(
- 'org.en.json',
+ 'messages/org.en.json',
);
});
it('formats with a different language', () => {
expect(formatMessagePath(DEFAULT_MESSAGE_PATTERN, 'catalog', 'sv')).toBe(
- 'catalog.sv.json',
+ 'messages/catalog.sv.json',
);
});
@@ -52,12 +52,12 @@ describe('messageFilePath', () => {
describe('createMessagePathParser', () => {
it('parses the default pattern', () => {
const parse = createMessagePathParser(DEFAULT_MESSAGE_PATTERN);
- expect(parse('org.en.json')).toEqual({ id: 'org', lang: 'en' });
+ expect(parse('messages/org.en.json')).toEqual({ id: 'org', lang: 'en' });
});
it('parses dotted ref IDs in the default pattern', () => {
const parse = createMessagePathParser(DEFAULT_MESSAGE_PATTERN);
- expect(parse('plugin.notifications.sv.json')).toEqual({
+ expect(parse('messages/plugin.notifications.sv.json')).toEqual({
id: 'plugin.notifications',
lang: 'sv',
});
@@ -71,7 +71,7 @@ describe('messageFilePath', () => {
it('returns undefined for non-matching paths', () => {
const parse = createMessagePathParser(DEFAULT_MESSAGE_PATTERN);
expect(parse('not-a-match.txt')).toBeUndefined();
- expect(parse('dir/org.en.json')).toBeUndefined();
+ expect(parse('other/org.en.json')).toBeUndefined();
});
it('returns undefined for invalid language code', () => {
@@ -100,7 +100,9 @@ describe('messageFilePath', () => {
describe('messagePatternToGlob', () => {
it('converts the default pattern', () => {
- expect(messagePatternToGlob(DEFAULT_MESSAGE_PATTERN)).toBe('*.*.json');
+ expect(messagePatternToGlob(DEFAULT_MESSAGE_PATTERN)).toBe(
+ 'messages/*.*.json',
+ );
});
it('converts a language-directory pattern', () => {
@@ -109,8 +111,8 @@ describe('messageFilePath', () => {
});
describe('patternHasSubdirectories', () => {
- it('returns false for flat patterns', () => {
- expect(patternHasSubdirectories(DEFAULT_MESSAGE_PATTERN)).toBe(false);
+ it('returns true for the default pattern', () => {
+ expect(patternHasSubdirectories(DEFAULT_MESSAGE_PATTERN)).toBe(true);
});
it('returns true for patterns with directories', () => {
diff --git a/packages/cli/src/modules/translations/lib/messageFilePath.ts b/packages/cli/src/modules/translations/lib/messageFilePath.ts
index 5f84c1ad13..0eb2547759 100644
--- a/packages/cli/src/modules/translations/lib/messageFilePath.ts
+++ b/packages/cli/src/modules/translations/lib/messageFilePath.ts
@@ -17,9 +17,9 @@
// The default language for exported translation messages.
export const DEFAULT_LANGUAGE = 'en';
-// Default file path pattern for translation message files.
-// Supported placeholders: {id} (ref ID) and {lang} (language code).
-export const DEFAULT_MESSAGE_PATTERN = '{id}.{lang}.json';
+// Default file path pattern for translation message files relative to the
+// translations directory. Supported placeholders: {id} and {lang}.
+export const DEFAULT_MESSAGE_PATTERN = 'messages/{id}.{lang}.json';
/** Formats a message file pattern into a concrete relative path. */
export function formatMessagePath(