Repository: lingui/js-lingui
Pull request #887 (2020-11-24 20:14:34)

fix: plural pseudolocalization with offset
I have improved RegEx for plurals. It now correctly escapes plural with offset. This is fix for #743, but it does not close it as there is need to escape other React components like Select, SelectOrdinal & DateFormat


I was not able to test the code because of this #886 

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/i9jwxr4x8](https://vercel.com/lingui-js/js-lingui/i9jwxr4x8)  
✅ Preview: *In Progress*


codecov[bot]: # [Codecov](https://codecov.io/gh/lingui/js-lingui/pull/887?src=pr&el=h1) Report
> Merging [#887](https://codecov.io/gh/lingui/js-lingui/pull/887?src=pr&el=desc) (744cd70) into [main](https://codecov.io/gh/lingui/js-lingui/commit/5cade1924dd038ba73ff85cdcf7ce80d31ddbd0f?el=desc) (5cade19) will **not change** coverage.
> The diff coverage is `100.00%`.

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

```diff
@@           Coverage Diff           @@
##             main     #887   +/-   ##
=======================================
  Coverage   82.91%   82.91%           
=======================================
  Files          51       51           
  Lines        1440     1440           
  Branches      395      395           
=======================================
  Hits         1194     1194           
  Misses        145      145           
  Partials      101      101           
```


| [Impacted Files](https://codecov.io/gh/lingui/js-lingui/pull/887?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [packages/cli/src/api/pseudoLocalize.ts](https://codecov.io/gh/lingui/js-lingui/pull/887/diff?src=pr&el=tree#diff-cGFja2FnZXMvY2xpL3NyYy9hcGkvcHNldWRvTG9jYWxpemUudHM=) | `100.00% <100.00%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/lingui/js-lingui/pull/887?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/887?src=pr&el=footer). Last update [5cade19...744cd70](https://codecov.io/gh/lingui/js-lingui/pull/887?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 2c8ef803..837fe3b6 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -75,17 +75,27 @@ local build of packages in examples. You can do the same in your project:
    docker run -d -it --rm --name verdaccio -p 4873:4873 verdaccio/verdaccio
    ```
 
-2. Publish local build of packages to registry. Run release script with `--dev` flag:
+> Make sure that your verdaccio user is the same that appears in verdacio-release.js script.
+2. Publish local build of packages to registry. Run local release script:
 
    ```sh
-   yarn release --dev
+   node ./scripts/verdaccio-release.js
    ```
 
-3. Inside your project, run `yarn install @lingui/package@next` to install pre-release
-   of packages.
+3. If you enter inside http://0.0.0.0:4873 (verdaccio instance), you will see your packages published, so you're ready to install them.
 
-4. After you make some changes, you need to run `yarn release --dev` and then
-   `yalc upgrade` inside your project.
+4. Inside your project, run:
+
+```sh
+   # Install a small package to update all lingui packages
+   npm i -g update-by-scope
+   # Point to your local registry
+   npm config set registry http://0.0.0.0:4873/
+   # Run update-by-scope will update all @lingui packages
+   update-by-scope @lingui
+```
+
+4. After you make some changes, you need to run the same process. (Releasing + yarn upgrade)
 
 ### Finalize changes
 
diff --git a/packages/cli/src/api/pseudoLocalize.ts b/packages/cli/src/api/pseudoLocalize.ts
index ab904cc7..098bc429 100644
--- a/packages/cli/src/api/pseudoLocalize.ts
+++ b/packages/cli/src/api/pseudoLocalize.ts
@@ -16,9 +16,9 @@ Example: https://regex101.com/r/bDHD9z/3
 const HTMLRegex = /<\/?\w+((\s+\w+(\s*=\s*(?:".*?"|'.*?'|[^'">\s]+))?)+\s*|\s*)\/?>/g
 /*
 Regex should match js-lingui plurals
-Example: https://regex101.com/r/utnbQw/1
+Example: https://regex101.com/r/utnbQw/3
 */
-const PluralRegex = /{\w*,\s*plural,\s*\w*\s*{|}\s*(zero|one|two|few|many|other)\s*({|})/g
+const PluralRegex = /({\w*,\s*plural,(.|\n)*?{)|(}\s*(offset|zero|one|two|few|many|other)\s*{)/g
 /*
 Regex should match js-lingui variables
 Example: https://regex101.com/r/dw1QHb/2
diff --git a/packages/macro/index.d.ts b/packages/macro/index.d.ts
index 33f64fa4..cb3a6172 100644
--- a/packages/macro/index.d.ts
+++ b/packages/macro/index.d.ts
@@ -40,10 +40,26 @@ export type ChoiceProps = {
 } & TransProps &
   ChoiceOptions<ReactNode>
 
+/**
+ * The types should be changed after this PR is merged
+ * https://github.com/Microsoft/TypeScript/pull/26797
+ *
+ * then we should be able to specify that key of values is same type as value.
+ * We would be able to remove separate type Values = {...} definition
+ * eg.
+ * type SelectProps<Values> = {
+ *  value?: Values
+ *  [key: Values]: string
+ * }
+ *
+ */
+type Values = { [key: string]: string }
+
 export type SelectProps = {
-  value?: string
-  other?: ReactNode
-} & TransProps
+  value: string
+  other: ReactNode
+} & TransProps &
+  Values
 
 export const Trans: ComponentType<TransProps>
 export const Plural: ComponentType<ChoiceProps>


Test patch:
diff --git a/packages/cli/src/api/pseudoLocalize.test.ts b/packages/cli/src/api/pseudoLocalize.test.ts
index e354e919..6108606f 100644
--- a/packages/cli/src/api/pseudoLocalize.test.ts
+++ b/packages/cli/src/api/pseudoLocalize.test.ts
@@ -20,30 +20,55 @@ describe("PseudoLocalization", () => {
     )
   })
 
-  it("should pseudlocalize plurals with HTML tags", () => {
-    expect(
-      pseudoLocalize(
-        "{messagesCount, plural, zero {There's # <span>message</span>} other {There're # messages}"
+  describe("Plurals", () => {
+    it("with value", () => {
+      expect(
+        pseudoLocalize("{value, plural, one {# book} other {# books}}")
+      ).toEqual("{value, plural, one {# ƀōōķ} other {# ƀōōķś}}")
+    })
+
+    it("with variable placeholder", () => {
+      expect(
+        pseudoLocalize(
+          "{count, plural, one {{countString} book} other {{countString} books}}"
+        )
+      ).toEqual(
+        "{count, plural, one {{countString} ƀōōķ} other {{countString} ƀōōķś}}"
       )
-    ).toEqual(
-      "{messagesCount, plural, zero {Ţĥēŕē'ś # <span>mēśśàĝē</span>} other {Ţĥēŕē'ŕē # mēśśàĝēś}"
-    )
-  })
+    })
 
-  it("should pseudolocalize plurals", () => {
-    expect(
-      pseudoLocalize("{value, plural, one {# book} other {# books}}")
-    ).toEqual("{value, plural, one {# ƀōōķ} other {# ƀōōķś}}")
-    expect(
-      pseudoLocalize(
-        "{count, plural, one {{countString} book} other {{countString} books}}"
+    it("with offset", () => {
+      expect(
+        pseudoLocalize(
+          "{count, plural, offset:1 zero {There're no messages} other {There're # messages in your inbox}}"
+        )
+      ).toEqual(
+        "{count, plural, offset:1 zero {Ţĥēŕē'ŕē ńō mēśśàĝēś} other {Ţĥēŕē'ŕē # mēśśàĝēś ĩń ŷōũŕ ĩńƀōx}}"
       )
-    ).toEqual(
-      "{count, plural, one {{countString} ƀōōķ} other {{countString} ƀōōķś}}"
-    )
+    })
+
+    it("with HTML tags", () => {
+      expect(
+        pseudoLocalize(
+          "{count, plural, zero {There's # <span>message</span>} other {There're # messages}"
+        )
+      ).toEqual(
+        "{count, plural, zero {Ţĥēŕē'ś # <span>mēśśàĝē</span>} other {Ţĥēŕē'ŕē # mēśśàĝēś}"
+      )
+    })
+
+    it("with exact number", () => {
+      expect(
+        pseudoLocalize(
+          "{count, plural, =0 {There's # <span>message</span>} other {There're # messages}"
+        )
+      ).toEqual(
+        "{count, plural, =0 {Ţĥēŕē'ś # <span>mēśśàĝē</span>} other {Ţĥēŕē'ŕē # mēśśàĝēś}"
+      )
+    })
   })
 
-  it("shouldn't pseudolocalize variables", () => {
+  it("should not pseudolocalize variables", () => {
     expect(pseudoLocalize("replace {count}")).toEqual("ŕēƥĺàćē {count}")
     expect(pseudoLocalize("replace { count }")).toEqual("ŕēƥĺàćē { count }")
   })