Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(core): bump messageformat parser. Add TS types #1328

Merged
Merged
Show file tree
Hide file tree
Changes from 2 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
2 changes: 1 addition & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"@babel/types": "^7.11.5",
"@lingui/babel-plugin-extract-messages": "^3.15.0",
"@lingui/conf": "^3.15.0",
"@lingui/core": "^3.15.0",
"babel-plugin-macros": "^3.0.1",
"bcp-47": "^1.0.7",
"chalk": "^4.1.0",
Expand All @@ -53,7 +54,6 @@
"glob": "^7.1.4",
"inquirer": "^7.3.3",
"make-plural": "^6.2.2",
"messageformat-parser": "^4.1.3",
"micromatch": "4.0.2",
"mkdirp": "^1.0.4",
"node-gettext": "^3.0.0",
Expand Down
30 changes: 12 additions & 18 deletions packages/cli/src/api/__snapshots__/compile.test.ts.snap
Original file line number Diff line number Diff line change
@@ -1,39 +1,33 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`compile should report failed message on error 1`] = `
Can't parse message. Please check correct syntax: "{value, plural, one {Book} other {Books"

Messageformat-parser trace: Expected "#", "{", "}", doubled apostrophe, escaped string, or plain char but end of input found.
`;

exports[`createCompiledCatalog nested message 1`] = `/*eslint-disable*/module.exports={messages:{"nested":{"one":"Uno","two":"Dos","three":"Tres","hello":["Hola ",["name"]]}}};`;
exports[`createCompiledCatalog nested message 1`] = `/*eslint-disable*/module.exports={messages:JSON.parse("{\\"nested\\":{\\"one\\":\\"Uno\\",\\"two\\":\\"Dos\\",\\"three\\":\\"Tres\\",\\"hello\\":[\\"Hola \\",[\\"name\\"]]}}")};`;

exports[`createCompiledCatalog options.compilerBabelOptions by default should return catalog without ASCII chars 1`] = `/*eslint-disable*/module.exports={messages:{"Hello":"Alohà"}};`;
exports[`createCompiledCatalog options.compilerBabelOptions by default should return catalog without ASCII chars 1`] = `/*eslint-disable*/module.exports={messages:JSON.parse("{\\"Hello\\":\\"Alohà\\"}")};`;

exports[`createCompiledCatalog options.compilerBabelOptions should return catalog without ASCII chars 1`] = `/*eslint-disable*/module.exports={messages:{"Hello":"Aloh\\xE0"}};`;
exports[`createCompiledCatalog options.compilerBabelOptions should return catalog without ASCII chars 1`] = `/*eslint-disable*/module.exports={messages:JSON.parse("{\\"Hello\\":\\"Aloh\\xE0\\"}")};`;

exports[`createCompiledCatalog options.namespace should compile with es 1`] = `/*eslint-disable*/export const messages={};`;
exports[`createCompiledCatalog options.namespace should compile with es 1`] = `/*eslint-disable*/export const messages=JSON.parse("{}");`;

exports[`createCompiledCatalog options.namespace should compile with global 1`] = `/*eslint-disable*/global.test={messages:{}};`;
exports[`createCompiledCatalog options.namespace should compile with global 1`] = `/*eslint-disable*/global.test={messages:JSON.parse("{}")};`;

exports[`createCompiledCatalog options.namespace should compile with ts 1`] = `/*eslint-disable*/export const messages={};`;
exports[`createCompiledCatalog options.namespace should compile with ts 1`] = `/*eslint-disable*/export const messages=JSON.parse("{}");`;

exports[`createCompiledCatalog options.namespace should compile with window 1`] = `/*eslint-disable*/window.test={messages:{}};`;
exports[`createCompiledCatalog options.namespace should compile with window 1`] = `/*eslint-disable*/window.test={messages:JSON.parse("{}")};`;

exports[`createCompiledCatalog options.namespace should error with invalid value 1`] = `Invalid namespace param: "global"`;

exports[`createCompiledCatalog options.pseudoLocale should return catalog with pseudolocalized messages 1`] = `/*eslint-disable*/module.exports={messages:{"Hello":"ÀĥōĴ"}};`;
exports[`createCompiledCatalog options.pseudoLocale should return catalog with pseudolocalized messages 1`] = `/*eslint-disable*/module.exports={messages:JSON.parse("{\\"Hello\\":\\"ÀĥōĴ\\"}")};`;

exports[`createCompiledCatalog options.pseudoLocale should return compiled catalog when pseudoLocale doesn't match current locale 1`] = `/*eslint-disable*/module.exports={messages:{"Hello":"Ahoj"}};`;
exports[`createCompiledCatalog options.pseudoLocale should return compiled catalog when pseudoLocale doesn't match current locale 1`] = `/*eslint-disable*/module.exports={messages:JSON.parse("{\\"Hello\\":\\"Ahoj\\"}")};`;

exports[`createCompiledCatalog options.pure should return code catalog 1`] = `/*eslint-disable*/module.exports={messages:{"Hello":"Ahoj"}};`;
exports[`createCompiledCatalog options.pure should return code catalog 1`] = `/*eslint-disable*/module.exports={messages:JSON.parse("{\\"Hello\\":\\"Ahoj\\"}")};`;

exports[`createCompiledCatalog options.pure should return pure catalog 1`] = `
Object {
Hello: Ahoj,
}
`;

exports[`createCompiledCatalog options.strict should return message key as a fallback translation 1`] = `/*eslint-disable*/module.exports={messages:{"Hello":"Ahoj","Missing":"Missing","Select":[["id","select",{Gen:"Genesis","1John":"1 John",other:"____"}]]}};`;
exports[`createCompiledCatalog options.strict should return message key as a fallback translation 1`] = `/*eslint-disable*/module.exports={messages:JSON.parse("{\\"Hello\\":\\"Ahoj\\",\\"Missing\\":\\"Missing\\",\\"Select\\":[[\\"id\\",\\"select\\",{\\"Gen\\":\\"Genesis\\",\\"1John\\":\\"1 John\\",\\"other\\":\\"____\\"}]]}")};`;

exports[`createCompiledCatalog options.strict should't return message key as a fallback in strict mode 1`] = `/*eslint-disable*/module.exports={messages:{"Hello":"Ahoj","Missing":"","Select":[["id","select",{Gen:"Genesis","1John":"1 John",other:"____"}]]}};`;
exports[`createCompiledCatalog options.strict should't return message key as a fallback in strict mode 1`] = `/*eslint-disable*/module.exports={messages:JSON.parse("{\\"Hello\\":\\"Ahoj\\",\\"Missing\\":\\"\\",\\"Select\\":[[\\"id\\",\\"select\\",{\\"Gen\\":\\"Genesis\\",\\"1John\\":\\"1 John\\",\\"other\\":\\"____\\"}]]}")};`;
130 changes: 21 additions & 109 deletions packages/cli/src/api/compile.test.ts
Original file line number Diff line number Diff line change
@@ -1,141 +1,53 @@
import generate from "@babel/generator"
import { compile, createCompiledCatalog } from "./compile"

describe("compile", () => {
const getSource = (message: string) =>
generate(compile(message) as any, {
compact: true,
minified: true,
jsescOption: { minimal: true },
}).code

it("should optimize string only messages", () => {
expect(getSource("Hello World")).toEqual('"Hello World"')
})

it("should allow escaping syntax characters", () => {
expect(getSource("'{name}'")).toEqual('"{name}"')
expect(getSource("''")).toEqual('"\'"')
})

it("should compile arguments", () => {
expect(getSource("{name}")).toEqual('[["name"]]')

expect(getSource("B4 {name} A4")).toEqual('["B4 ",["name"]," A4"]')
})

it("should compile arguments with formats", () => {
expect(getSource("{name, number}")).toEqual('[["name","number"]]')

expect(getSource("{name, number, percent}")).toEqual(
'[["name","number","percent"]]'
)
})

it("should compile plural", () => {
expect(getSource("{name, plural, one {Book} other {Books}}")).toEqual(
'[["name","plural",{one:"Book",other:"Books"}]]'
)

expect(
getSource("{name, plural, one {Book} other {{name} Books}}")
).toEqual('[["name","plural",{one:"Book",other:[["name"]," Books"]}]]')

expect(getSource("{name, plural, one {Book} other {# Books}}")).toEqual(
'[["name","plural",{one:"Book",other:["#"," Books"]}]]'
)

expect(
getSource(
"{name, plural, offset:1 =0 {No Books} one {Book} other {# Books}}"
)
).toEqual(
'[["name","plural",{offset:1,0:"No Books",one:"Book",other:["#"," Books"]}]]'
)
})

it("should compile select", () => {
expect(getSource("{name, select, male {He} female {She}}")).toEqual(
'[["name","select",{male:"He",female:"She"}]]'
)

expect(getSource("{name, select, male {He} female {{name} She}}")).toEqual(
'[["name","select",{male:"He",female:[["name"]," She"]}]]'
)

expect(getSource("{name, select, male {He} female {# She}}")).toEqual(
'[["name","select",{male:"He",female:"# She"}]]'
)
})

it("should compile multiple plurals", () => {
expect(
getSource(
"{bcount, plural, one {boy} other {# boys}} {gcount, plural, one {girl} other {# girls}}"
)
).toEqual(
'[["bcount","plural",{one:"boy",other:["#"," boys"]}]," ",["gcount","plural",{one:"girl",other:["#"," girls"]}]]'
)
})

it("should report failed message on error", () => {
expect(() =>
getSource("{value, plural, one {Book} other {Books")
).toThrowErrorMatchingSnapshot()
})

describe("with pseudo-localization", () => {
const getPSource = (message: string) =>
generate(compile(message, true) as any, {
compact: true,
minified: true,
jsescOption: { minimal: true },
}).code
const getPSource = (message: string) => compile(message, true)

it("should pseudolocalize strings", () => {
expect(getPSource("Martin Černý")).toEqual('"Màŕţĩń Čēŕńý"')
expect(getPSource("Martin Černý")).toEqual("Màŕţĩń Čēŕńý")
})

it("should pseudolocalize escaping syntax characters", () => {
// TODO: should this turn into pseudoLocale string?
expect(getPSource("'{name}'")).toEqual('"{name}"')
expect(getPSource("'{name}'")).toEqual("{name}")
// expect(getPSource("'{name}'")).toEqual('"{ńàmē}"')
})

it("should not pseudolocalize arguments", () => {
expect(getPSource("{name}")).toEqual('[["name"]]')
expect(getPSource("B4 {name} A4")).toEqual('["ß4 ",["name"]," À4"]')
expect(getPSource("{name}")).toEqual([["name"]])
expect(getPSource("B4 {name} A4")).toEqual(["ß4 ", ["name"], " À4"])
})

it("should not pseudolocalize arguments nor formats", () => {
expect(getPSource("{name, number}")).toEqual('[["name","number"]]')
expect(getPSource("{name, number}")).toEqual([["name", "number"]])
expect(getPSource("{name, number, percent}")).toEqual(
'[["name","number","percent"]]'
[["name", "number", "percent"]]
)
})

it("should not pseudolocalize HTML tags", () => {
expect(getPSource('Martin <span id="spanId">Černý</span>')).toEqual(
JSON.stringify('Màŕţĩń <span id="spanId">Čēŕńý</span>')
'Màŕţĩń <span id="spanId">Čēŕńý</span>'
)
expect(
getPSource("Martin Cerny 123a<span id='id'>Černý</span>")
).toEqual(
JSON.stringify("Màŕţĩń Ćēŕńŷ 123à<span id='id'>Čēŕńý</span>")
"Màŕţĩń Ćēŕńŷ 123à<span id='id'>Čēŕńý</span>"
)
expect(getPSource("Martin <a title='>>a'>a</a>")).toEqual(
JSON.stringify("Màŕţĩń <a title='>>a'>à</a>")
"Màŕţĩń <a title='>>a'>à</a>"
)
expect(getPSource("<a title=TITLE>text</a>")).toEqual(
JSON.stringify("<a title=TITLE>ţēxţ</a>")
"<a title=TITLE>ţēxţ</a>"
)
})

describe("Plurals", () => {
it("with value", () => {
expect(
getPSource("{value, plural, one {# book} other {# books}}")
).toEqual('[["value","plural",{one:["#"," ƀōōķ"],other:["#"," ƀōōķś"]}]]')
).toEqual([["value","plural",{one:["#"," ƀōōķ"],other:["#"," ƀōōķś"]}]])
})

it("with variable placeholder", () => {
Expand All @@ -144,7 +56,7 @@ describe("compile", () => {
"{count, plural, one {{countString} book} other {{countString} books}}"
)
).toEqual(
'[["count","plural",{one:[["countString"]," ƀōōķ"],other:[["countString"]," ƀōōķś"]}]]'
[["count","plural",{one:[["countString"]," ƀōōķ"],other:[["countString"]," ƀōōķś"]}]]
)
})

Expand All @@ -154,7 +66,7 @@ describe("compile", () => {
"{count, plural, offset:1 zero {There are no messages} other {There are # messages in your inbox}}"
)
).toEqual(
'[["count","plural",{offset:1,zero:"Ţĥēŕē àŕē ńō mēśśàĝēś",other:["Ţĥēŕē àŕē ","#"," mēśśàĝēś ĩń ŷōũŕ ĩńƀōx"]}]]'
[["count","plural",{offset:1,zero:"Ţĥēŕē àŕē ńō mēśśàĝēś",other:["Ţĥēŕē àŕē ","#"," mēśśàĝēś ĩń ŷōũŕ ĩńƀōx"]}]]
)
})

Expand All @@ -164,7 +76,7 @@ describe("compile", () => {
"{count, plural, zero {There's # <span>message</span>} other {There are # messages}}"
)
).toEqual(
'[["count","plural",{zero:["Ţĥēŕē\'ś ","#"," <span>mēśśàĝē</span>"],other:["Ţĥēŕē àŕē ","#"," mēśśàĝēś"]}]]'
[["count","plural",{zero:["Ţĥēŕē\'ś ","#"," <span>mēśśàĝē</span>"],other:["Ţĥēŕē àŕē ","#"," mēśśàĝēś"]}]]
)
})

Expand All @@ -174,7 +86,7 @@ describe("compile", () => {
"{count, plural, =0 {There's # <span>message</span>} other {There are # messages}}"
)
).toEqual(
'[["count","plural",{0:["Ţĥēŕē\'ś ","#"," <span>mēśśàĝē</span>"],other:["Ţĥēŕē àŕē ","#"," mēśśàĝēś"]}]]'
[["count","plural",{0:["Ţĥēŕē\'ś ","#"," <span>mēśśàĝē</span>"],other:["Ţĥēŕē àŕē ","#"," mēśśàĝēś"]}]]
)
})
})
Expand All @@ -185,7 +97,7 @@ describe("compile", () => {
"{count, selectordinal, offset:1 one {#st} two {#nd} few {#rd} =4 {4th} many {testMany} other {#th}}"
)
).toEqual(
'[["count","selectordinal",{offset:1,one:["#","śţ"],two:["#","ńď"],few:["#","ŕď"],4:"4ţĥ",many:"ţēśţMàńŷ",other:["#","ţĥ"]}]]'
[["count","selectordinal",{offset:1,one:["#","śţ"],two:["#","ńď"],few:["#","ŕď"],4:"4ţĥ",many:"ţēśţMàńŷ",other:["#","ţĥ"]}]]
)
})

Expand All @@ -195,13 +107,13 @@ describe("compile", () => {
"{gender, select, male {He} female {She} other {<span>Other</span>}}"
)
).toEqual(
'[["gender","select",{male:"Ĥē",female:"Śĥē",other:"<span>Ōţĥēŕ</span>"}]]'
[["gender","select",{male:"Ĥē",female:"Śĥē",other:"<span>Ōţĥēŕ</span>"}]]
)
})

it("should not pseudolocalize variables", () => {
expect(getPSource("replace {count}")).toEqual('["ŕēƥĺàćē ",["count"]]')
expect(getPSource("replace { count }")).toEqual('["ŕēƥĺàćē ",["count"]]')
expect(getPSource("replace {count}")).toEqual(["ŕēƥĺàćē ",["count"]])
expect(getPSource("replace { count }")).toEqual(["ŕēƥĺàćē ",["count"]])
})

it("Multiple Plurals", () => {
Expand All @@ -210,7 +122,7 @@ describe("compile", () => {
"{bcount, plural, one {boy} other {# boys}} {gcount, plural, one {girl} other {# girls}}"
)
).toEqual(
'[["bcount","plural",{one:"ƀōŷ",other:["#"," ƀōŷś"]}]," ",["gcount","plural",{one:"ĝĩŕĺ",other:["#"," ĝĩŕĺś"]}]]'
[["bcount","plural",{one:"ƀōŷ",other:["#"," ƀōŷś"]}]," ",["gcount","plural",{one:"ĝĩŕĺ",other:["#"," ĝĩŕĺś"]}]]
)
})
})
Expand Down
Loading