Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions l10n/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,15 @@ Adding Fluent comments to explain what context strings appear in is an open task
If one English string is used in multiple places and requires multiple different strings in a locale, file an issue to distinguish the IDs.

Please also file an issue for any other problems you encounter with localizing - either with specific strings which need fixing, or general issues with the l10n process.

### Finding new strings to localise

Instead of having to manually copy strings from `./template.ftl` into your locale file, you can use the `npm run l10n merge` command to add the missing strings, allowing you to localise them inline.

For German, as an example, running from the project root:

```
npm run l10n merge l10n/template.ftl l10n/locales/de.ftl
```

`de.ftl` will now have all the English strings it lacks. You can localise them all, or just a subset, and remove the ones you don't localise. The next run of the merge command will add the strings you removed again, allowing you to localise in batches if you'd prefer.
10 changes: 9 additions & 1 deletion l10n/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import yargs from "yargs";

import { hideBin } from "yargs/helpers";

import { extract } from "./parser/extractor.js";
import { extract, merge } from "./parser/extractor.js";
import { generateQaaLocale } from "./parser/transform.js";

await yargs(hideBin(process.argv))
Expand All @@ -29,5 +29,13 @@ await yargs(hideBin(process.argv))
}
},
})
.command({
command: "merge <source> <target>",
describe:
"Merges source file into target file, adding strings which the target file doesn't have",
handler: async ({ source, target }) => {
await merge(source, target);
},
})
.demandCommand()
.parse();
28 changes: 28 additions & 0 deletions l10n/parser/extractor.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,34 @@ export async function getManualEntries(filePath) {
);
}

/**
* @param {string} sourcePath
* @param {string} targetPath
* @param {{ lint?: boolean }} [options]
*/
export async function merge(sourcePath, targetPath, options) {
const sourceText = await readFile(sourcePath, "utf8");
const targetText = await readFile(targetPath, "utf8");
const sourceResource = parse(sourceText, {});
const targetResource = parse(targetText, {});

const targetIds = new Set(
targetResource.body
.filter((entry) => entry instanceof Message)
.map((entry) => entry.id.name),
);

for (const entry of sourceResource.body) {
if (entry instanceof Message && !targetIds.has(entry.id.name)) {
targetResource.body.push(entry);
}
}

const output = serialize(targetResource, {});
if (!options?.lint) await writeFile(targetPath, output, "utf8");
return output;
}

/**
* @param {string} glob Files to scrape strings from
*/
Expand Down
17 changes: 17 additions & 0 deletions test/unit/l10n/extractor.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
import {
createFluentResource,
getManualEntries,
merge,
scrapeL10nTags,
} from "../../../l10n/parser/extractor.js";

Expand Down Expand Up @@ -91,6 +92,22 @@ describe("l10n extractor", () => {
});
});

describe("merge", () => {
const sourcePath = path.join(__dirname, "fixtures", "merge-source.ftl");
const targetPath = path.join(__dirname, "fixtures", "merge-target.ftl");

it("adds missing messages from source to target", async () => {
const output = await merge(sourcePath, targetPath, { lint: true });
assert.match(output, /id2 = Source two/);
assert.match(output, /id3 = Source three/);
});

it("preserves existing messages in target", async () => {
const output = await merge(sourcePath, targetPath, { lint: true });
assert.match(output, /id1 = Target one/);
});
});

describe("createFluentResource", () => {
it("throws if duplicate ids with different text exist across manual and scraped strings", async () => {
await assert.rejects(
Expand Down
3 changes: 3 additions & 0 deletions test/unit/l10n/fixtures/merge-source.ftl
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
id1 = Source one
id2 = Source two
id3 = Source three
1 change: 1 addition & 0 deletions test/unit/l10n/fixtures/merge-target.ftl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
id1 = Target one
Loading