Repository: lingui/js-lingui
Pull request #816 (2020-11-02 22:12:40)

Add commandline arg to extract only a specific locale

vercel[bot]: This pull request is being automatically deployed with Vercel ([learn more](https://vercel.link/github-learn-more)).  
To see the status of your deployment, click below or on the icon next to each commit.

🔍 Inspect: [https://vercel.com/lingui-js/js-lingui/iosm1fzyv](https://vercel.com/lingui-js/js-lingui/iosm1fzyv)  
✅ Preview: *In Progress*


codecov[bot]: # [Codecov](https://codecov.io/gh/lingui/js-lingui/pull/816?src=pr&el=h1) Report
> Merging [#816](https://codecov.io/gh/lingui/js-lingui/pull/816?src=pr&el=desc) into [main](https://codecov.io/gh/lingui/js-lingui/commit/2211d095f86e5021f13af48e0ff6e0fa750493d4?el=desc) will **increase** coverage by `0.11%`.
> The diff coverage is `87.50%`.

[![Impacted file tree graph](https://codecov.io/gh/lingui/js-lingui/pull/816/graphs/tree.svg?width=650&height=150&src=pr&token=CuJ1VD5BY5)](https://codecov.io/gh/lingui/js-lingui/pull/816?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##             main     #816      +/-   ##
==========================================
+ Coverage   84.59%   84.71%   +0.11%     
==========================================
  Files          39       39              
  Lines        1292     1295       +3     
  Branches      339      341       +2     
==========================================
+ Hits         1093     1097       +4     
+ Misses        118      117       -1     
  Partials       81       81              
```


| [Impacted Files](https://codecov.io/gh/lingui/js-lingui/pull/816?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [packages/cli/src/api/stats.ts](https://codecov.io/gh/lingui/js-lingui/pull/816/diff?src=pr&el=tree#diff-cGFja2FnZXMvY2xpL3NyYy9hcGkvc3RhdHMudHM=) | `70.00% <50.00%> (-7.78%)` | :arrow_down: |
| [packages/cli/src/api/catalog.ts](https://codecov.io/gh/lingui/js-lingui/pull/816/diff?src=pr&el=tree#diff-cGFja2FnZXMvY2xpL3NyYy9hcGkvY2F0YWxvZy50cw==) | `86.02% <100.00%> (+1.32%)` | :arrow_up: |
| [packages/cli/src/tests.ts](https://codecov.io/gh/lingui/js-lingui/pull/816/diff?src=pr&el=tree#diff-cGFja2FnZXMvY2xpL3NyYy90ZXN0cy50cw==) | `92.30% <100.00%> (-0.55%)` | :arrow_down: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/lingui/js-lingui/pull/816?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/lingui/js-lingui/pull/816?src=pr&el=footer). Last update [2211d09...693260c](https://codecov.io/gh/lingui/js-lingui/pull/816?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/docs/ref/cli.rst b/docs/ref/cli.rst
index 824cb3fe..b8a18b78 100644
--- a/docs/ref/cli.rst
+++ b/docs/ref/cli.rst
@@ -43,7 +43,7 @@ Commands
 ``extract``
 -----------
 
-.. lingui-cli:: extract [--clean] [--overwrite] [--format <format>] [--convert-from <format>] [--verbose]
+.. lingui-cli:: extract [--clean] [--overwrite] [--format <format>] [--locale <locale>] [--convert-from <format>] [--verbose]
 
 This command extracts messages from source files and creates a message catalog for
 each language using the following steps:
@@ -65,6 +65,10 @@ Update translations for :conf:`sourceLocale` from source.
 
 Format of message catalogs (see :conf:`format` option).
 
+.. lingui-cli-option:: --locale <locale>
+
+Only extract data for the specified locale.
+
 .. lingui-cli-option:: --convert-from <format>
 
 Convert message catalogs from previous format (see :conf:`format` option).
diff --git a/packages/cli/src/api/catalog.ts b/packages/cli/src/api/catalog.ts
index 233f15e4..5b3957fe 100644
--- a/packages/cli/src/api/catalog.ts
+++ b/packages/cli/src/api/catalog.ts
@@ -93,7 +93,14 @@ export class Catalog {
         order(options.orderBy)
       )
     ) as unknown) as (catalog: AllCatalogsType) => AllCatalogsType
-    this.writeAll(cleanAndSort(catalogs))
+
+    const sortedCatalogs = cleanAndSort(catalogs);
+
+    if (options.locale) {
+      this.write(options.locale, sortedCatalogs[options.locale])
+    } else {
+      this.writeAll(sortedCatalogs)
+    }
   }
 
   makeTemplate(options: MakeTemplateOptions) {
diff --git a/packages/cli/src/api/stats.ts b/packages/cli/src/api/stats.ts
index 29435e09..4d6b53fc 100644
--- a/packages/cli/src/api/stats.ts
+++ b/packages/cli/src/api/stats.ts
@@ -26,7 +26,11 @@ export function printStats(config: LinguiConfig, catalogs: AllCatalogsType) {
   })
 
   Object.keys(catalogs).forEach((locale) => {
-    const [all, translated] = getStats(catalogs[locale])
+    const catalog = catalogs[locale]
+    // catalog is null if no catalog exists on disk and the locale
+    // was not extracted due to a `--locale` filter
+    const [all, translated] = catalog ? getStats(catalog) : ["-", "-"]
+
     if (config.sourceLocale === locale) {
       table.push({ [`${chalk.bold(locale)} (source)`]: [all, "-"] })
     } else {
diff --git a/packages/cli/src/lingui-extract.ts b/packages/cli/src/lingui-extract.ts
index fa3deaf0..f4e5df2e 100644
--- a/packages/cli/src/lingui-extract.ts
+++ b/packages/cli/src/lingui-extract.ts
@@ -13,6 +13,7 @@ export type CliExtractOptions = {
   verbose: boolean
   clean: boolean
   overwrite: boolean
+  locale: string
   prevFormat: string | null
 }
 
@@ -70,6 +71,7 @@ export default function command(
 if (require.main === module) {
   program
     .option("--config <path>", "Path to the config file")
+    .option("--locale <locale>", "Only extract the specified locale")
     .option("--overwrite", "Overwrite translations for source locale")
     .option("--clean", "Remove obsolete translations")
     .option("--verbose", "Verbose output")
@@ -120,12 +122,19 @@ if (require.main === module) {
     process.exit(1)
   }
 
+  if (program.locale && !config.locales.includes(program.locale)) {
+    hasErrors = true
+    console.error(`Locale ${chalk.bold(program.locale)} does not exist.`)
+    console.error()
+  }
+
   if (hasErrors) process.exit(1)
 
   const result = command(config, {
     verbose: program.verbose || false,
     clean: program.clean || false,
     overwrite: program.overwrite || false,
+    locale: program.locale,
     prevFormat,
   })
 


Test patch:
diff --git a/packages/cli/src/api/__snapshots__/catalog.test.ts.snap b/packages/cli/src/api/__snapshots__/catalog.test.ts.snap
index e74a54d6..1fbc1b33 100644
--- a/packages/cli/src/api/__snapshots__/catalog.test.ts.snap
+++ b/packages/cli/src/api/__snapshots__/catalog.test.ts.snap
@@ -211,6 +211,47 @@ Object {
 }
 `;
 
+exports[`Catalog make should only update the specified locale 1`] = `
+Object {
+  cs: null,
+  en: null,
+}
+`;
+
+exports[`Catalog make should only update the specified locale 2`] = `
+Object {
+  cs: null,
+  en: Object {
+    Component A: Object {
+      comment: undefined,
+      comments: Array [],
+      flags: Array [],
+      obsolete: false,
+      origin: Array [
+        Array [
+          collect/componentA/componentA.js,
+          1,
+        ],
+      ],
+      translation: ,
+    },
+    Hello World: Object {
+      comment: undefined,
+      comments: Array [],
+      flags: Array [],
+      obsolete: false,
+      origin: Array [
+        Array [
+          collect/componentA/index.js,
+          1,
+        ],
+      ],
+      translation: ,
+    },
+  },
+}
+`;
+
 exports[`Catalog makeTemplate should collect and write a template 1`] = `null`;
 
 exports[`Catalog makeTemplate should collect and write a template 2`] = `
diff --git a/packages/cli/src/api/catalog.test.ts b/packages/cli/src/api/catalog.test.ts
index 12392673..41b846c7 100644
--- a/packages/cli/src/api/catalog.test.ts
+++ b/packages/cli/src/api/catalog.test.ts
@@ -59,6 +59,30 @@ describe("Catalog", function () {
       expect(catalog.readAll()).toMatchSnapshot()
     })
 
+    it("should only update the specified locale", function () {
+      const localeDir = copyFixture(fixture("locales", "initial"))
+      const catalog = new Catalog(
+        {
+          name: "messages",
+          path: path.join(localeDir, "{locale}", "messages"),
+          include: [
+            fixture("collect/componentA/"),
+            fixture("collect/componentB"),
+          ],
+          exclude: [],
+        },
+        mockConfig({
+          locales: ["en", "cs"],
+        })
+      )
+
+      // Everything should be empty
+      expect(catalog.readAll()).toMatchSnapshot()
+
+      catalog.make({ ...defaultMakeOptions, locale: "en" })
+      expect(catalog.readAll()).toMatchSnapshot()
+    })
+
     it("should merge with existing catalogs", function () {
       const localeDir = copyFixture(fixture("locales", "existing"))
       const catalog = new Catalog(
diff --git a/packages/cli/src/tests.ts b/packages/cli/src/tests.ts
index 9ca162ab..6e98b66b 100644
--- a/packages/cli/src/tests.ts
+++ b/packages/cli/src/tests.ts
@@ -7,11 +7,10 @@ import { Catalog, MakeOptions, MakeTemplateOptions, MergeOptions } from "./api/c
 import { ExtractedMessageType, MessageType } from "./api/types"
 
 export function copyFixture(fixtureDir) {
-  const tmpDir = path.join(os.tmpdir(), `lingui-test-${process.pid}`)
-
-  if (!fs.existsSync(fixtureDir)) {
-    fs.mkdirpSync(tmpDir)
-  } else {
+  const tmpDir = fs.mkdtempSync(
+    path.join(os.tmpdir(), `lingui-test-${process.pid}`)
+  )
+  if (fs.existsSync(fixtureDir)) {
     fs.copySync(fixtureDir, tmpDir)
   }
   return tmpDir
@@ -21,6 +20,7 @@ export const defaultMakeOptions: MakeOptions = {
   verbose: false,
   clean: false,
   overwrite: false,
+  locale: null,
   prevFormat: null,
   orderBy: "messageId",
 }