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

feat: msgctxt support #1094

Merged
merged 3 commits into from
Oct 18, 2021
Merged
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
94 changes: 72 additions & 22 deletions packages/babel-plugin-extract-messages/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,33 +17,83 @@ const VISITED = Symbol("I18nVisited")
function addMessage(
path,
messages,
{ id, message: newDefault, origin, comment, ...props }
{ id, message: newDefault, origin, comment, context, ...props }
) {
// prevent from adding undefined msgid
if (id === undefined) return

if (messages.has(id)) {
const message = messages.get(id)
const extractedComments = comment ? [comment] : []

if (context) {
if (messages.has(context)) {
const existingContext = messages.get(context)
if (existingContext.has(id)) {
const message = messages.get(id)
// only set/check default language when it's defined.
if (message.message && newDefault && message.message !== newDefault) {
throw path.buildCodeFrameError(
"Different defaults for the same message ID."
)
}

if (newDefault) {
message.message = newDefault
}

// only set/check default language when it's defined.
if (message.message && newDefault && message.message !== newDefault) {
throw path.buildCodeFrameError(
"Different defaults for the same message ID."
)
;[].push.apply(message.origin, origin)

if (comment) {
;[].push.apply(message.extractedComments, [comment])
}
} else {
existingContext.set(id, { ...props, message: newDefault, origin, extractedComments })
messages.set(context, existingContext)
}
} else {
const newContext = new Map();
newContext.set(id, { ...props, message: newDefault, origin, extractedComments })
messages.set(context, newContext)
}

if (newDefault) {
message.message = newDefault
} else {
if (messages.has(id)) {
const message = messages.get(id)

// only set/check default language when it's defined.
if (message.message && newDefault && message.message !== newDefault) {
throw path.buildCodeFrameError(
"Different defaults for the same message ID."
)
}

if (newDefault) {
message.message = newDefault
}

;[].push.apply(message.origin, origin)
if (comment) {
;[].push.apply(message.extractedComments, [comment])
}
} else {
messages.set(id, { ...props, message: newDefault, origin, extractedComments })
}
}
}

;[].push.apply(message.origin, origin)
if (comment) {
;[].push.apply(message.extractedComments, [comment])
}
} else {
const extractedComments = comment ? [comment] : []
messages.set(id, { ...props, message: newDefault, origin, extractedComments })
/**
* An ES6 Map type is not possible to encode with JSON.stringify,
* so we can instead use a replacer function as an argument to
* tell the JSON parser how to serialize / deserialize the Maps
* it encounters.
*/
function mapReplacer(key, value) {
if (value instanceof Map) {
const object = {}
value.forEach((v, k) => {
return object[k] = v;
});
return object;
}
return value;
}

export default function ({ types: t }) {
Expand Down Expand Up @@ -117,7 +167,7 @@ export default function ({ types: t }) {

const props = attrs.reduce((acc, item) => {
const key = item.name.name
if (key === "id" || key === "message" || key === "comment") {
if (key === "id" || key === "message" || key === "comment" || key === "context") {
if (item.value.value) {
acc[key] = item.value.value
} else if (
Expand Down Expand Up @@ -169,7 +219,7 @@ export default function ({ types: t }) {
return
}

const copyOptions = ["message", "comment"]
const copyOptions = ["message", "comment", "context"]

if (t.isObjectExpression(path.node.arguments[2])) {
path.node.arguments[2].properties.forEach((property) => {
Expand Down Expand Up @@ -228,7 +278,7 @@ export default function ({ types: t }) {
visited.add(path.node)

const props = {}
const copyProps = ["id", "message", "comment"]
const copyProps = ["id", "message", "comment", "context"]
path.node.properties
.filter(({ key }) => copyProps.indexOf(key.name) !== -1)
.forEach(({ key, value }, i) => {
Expand Down Expand Up @@ -298,7 +348,7 @@ export default function ({ types: t }) {
catalog[key] = value
})

fs.writeFileSync(catalogFilename, JSON.stringify(catalog, null, 2))
fs.writeFileSync(catalogFilename, JSON.stringify(catalog, mapReplacer, 2))
},
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,17 @@

exports[`@lingui/babel-plugin-extract-messages should extract Plural messages from JSX files when there's no Trans tag (integration) 1`] = `
Object {
Some context: Object {
{count, plural, one {# book} other {# books}}: Object {
extractedComments: Array [],
origin: Array [
Array [
jsx-without-trans.js,
4,
],
],
},
},
{count, plural, one {# book} other {# books}}: Object {
extractedComments: Array [],
origin: Array [
Expand All @@ -25,6 +36,37 @@ Object {
],
],
},
Context1: Object {
Some ID: Object {
extractedComments: Array [],
origin: Array [
Array [
js-with-macros.js,
34,
],
],
},
Some other ID: Object {
extractedComments: Array [],
origin: Array [
Array [
js-with-macros.js,
39,
],
],
},
},
Context2: Object {
Some ID: Object {
extractedComments: Array [],
origin: Array [
Array [
js-with-macros.js,
44,
],
],
},
},
Description: Object {
extractedComments: Array [
description,
Expand Down Expand Up @@ -83,6 +125,17 @@ Object {

exports[`@lingui/babel-plugin-extract-messages should extract all messages from JS files 1`] = `
Object {
Context1: Object {
Some id: Object {
extractedComments: Array [],
origin: Array [
Array [
js-without-macros.js,
11,
],
],
},
},
Description: Object {
extractedComments: Array [
description,
Expand Down Expand Up @@ -127,6 +180,37 @@ Object {

exports[`@lingui/babel-plugin-extract-messages should extract all messages from JSX files (macros) 1`] = `
Object {
Context1: Object {
Some message: Object {
extractedComments: Array [],
origin: Array [
Array [
jsx-with-macros.js,
4,
],
],
},
Some other message: Object {
extractedComments: Array [],
origin: Array [
Array [
jsx-with-macros.js,
5,
],
],
},
},
Context2: Object {
Some message: Object {
extractedComments: Array [],
origin: Array [
Array [
jsx-with-macros.js,
6,
],
],
},
},
Hi, my name is {name}: Object {
extractedComments: Array [],
origin: Array [
Expand All @@ -141,7 +225,7 @@ Object {
origin: Array [
Array [
jsx-with-macros.js,
4,
7,
],
],
},
Expand All @@ -150,7 +234,7 @@ Object {
origin: Array [
Array [
jsx-with-macros.js,
6,
9,
],
],
},
Expand All @@ -159,12 +243,43 @@ Object {

exports[`@lingui/babel-plugin-extract-messages should extract all messages from JSX files 1`] = `
Object {
Context1: Object {
msg.context: Object {
extractedComments: Array [],
origin: Array [
Array [
jsx-without-macros.js,
7,
],
],
},
msg.notcontext: Object {
extractedComments: Array [],
origin: Array [
Array [
jsx-without-macros.js,
8,
],
],
},
},
Context2: Object {
msg.context: Object {
extractedComments: Array [],
origin: Array [
Array [
jsx-without-macros.js,
9,
],
],
},
},
Hi, my name is <0>{name}</0>: Object {
extractedComments: Array [],
origin: Array [
Array [
jsx-without-macros.js,
9,
12,
],
],
},
Expand All @@ -174,11 +289,11 @@ Object {
origin: Array [
Array [
jsx-without-macros.js,
7,
10,
],
Array [
jsx-without-macros.js,
8,
11,
],
],
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,8 @@ import { Trans } from "@lingui/react"
// OK - Default message is defined here
;<Trans id="msg" message="Hello World" />

// OK - same id, different context
;<Trans id="msg" context="Some context" message="Hello World" />

// Error! - Attempt to redefine the defaultm essage
;<Trans id="msg" message="Different default message" />
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,18 @@ const id = 'message id'
const withUnknownId = t({
id: id
})

const tWithContextA = t({
id: "Some ID",
context: "Context1"
})

const tWithContextB = t({
id: "Some other ID",
context: "Context1"
})

const defineMessageWithContext = defineMessage({
id: "Some ID",
context: "Context2"
})
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ const withDescription = /*i18n*/{id: 'Description', comment: "description"}
const withId = /*i18n*/{id: 'ID', message: 'Message with id'}

const withValues = /*i18n*/{id: 'Values {param}', values: { param: param }}

const withContext = /*i18n*/{id: 'Some id', context: 'Context1'}
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { t, plural, Trans } from "@lingui/macro"

;<Trans>Hi, my name is {name}</Trans>
;<Trans context="Context1">Some message</Trans>
;<Trans context="Context1">Some other message</Trans>
;<Trans context="Context2">Some message</Trans>
;<span title={t`Title`} />
;<span
title={plural(count, {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import { Trans } from "@lingui/react"
;<Trans /> // this should be ignored
;<Trans message="Missing ID" /> // this should be ignored
;<Trans id={"msg.hello"} comment="Description" />
;<Trans id="msg.context" context="Context1" />
;<Trans id="msg.notcontext" context="Context1" />
;<Trans id="msg.context" context="Context2" />
;<Trans id="msg.default" message="Hello World" />
;<Trans id="msg.default" message="Hello World" />
;<Trans id="Hi, my name is <0>{name}</0>" values={{ count }} />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Plural } from "@lingui/macro";

<Plural value={count} one="# book" other="# books" />
;<Plural value={count} one="# book" other="# books" />
;<Plural value={count} one="# book" other="# books" context="Some context" />
Loading