Skip to content

Commit

Permalink
feat(core): add separate node/js translation api for usage without ma…
Browse files Browse the repository at this point in the history
…cros
  • Loading branch information
Christoffer Jahren committed Mar 24, 2023
1 parent ead2db3 commit 1d90bbf
Show file tree
Hide file tree
Showing 7 changed files with 130 additions and 21 deletions.
2 changes: 1 addition & 1 deletion lerna.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "4.0.12",
"version": "4.0.0-next.3",
"packages": [
"packages/*"
],
Expand Down
25 changes: 8 additions & 17 deletions packages/babel-plugin-extract-messages/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ export default function ({ types: t }: { types: BabelTypes }): PluginObj {
const isNodeJSI18nMethod = (node: Node) =>
t.isMemberExpression(node) &&
t.isIdentifier(node.object, { name: "i18n" }) &&
t.isIdentifier(node.property, { name: "nodeTranslate" })
t.isIdentifier(node.property, { name: "t" })

function hasI18nComment(node: Node): boolean {
return (
Expand Down Expand Up @@ -225,9 +225,6 @@ export default function ({ types: t }: { types: BabelTypes }): PluginObj {
},

CallExpression(path, ctx) {
console.log("call expr", {
isNodie: isNodeJSI18nMethod(path.node.callee),
})
const hasComment = [path.node, path.parent].some((node) =>
hasI18nComment(node)
)
Expand All @@ -236,23 +233,17 @@ export default function ({ types: t }: { types: BabelTypes }): PluginObj {

let props: Record<string, unknown> = {}

if (isNodeJSI18nMethod(path.node.callee)) {
console.log("arg which is obj expr", {
...path.node.arguments.map((arg) => t.isObjectExpression(arg)),
})
}

if (
isNodeJSI18nMethod(path.node.callee) &&
t.isObjectExpression(path.node.arguments[0])
t.isObjectExpression(firstArgument)
) {
props = {
...extractFromObjectExpression(
t,
path.node.arguments[0],
ctx.file.hub,
["id", "message", "comment", "context"]
),
...extractFromObjectExpression(t, firstArgument, ctx.file.hub, [
"id",
"message",
"comment",
"context",
]),
}

collectMessage(path, props, ctx)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,16 @@ exports[`@lingui/babel-plugin-extract-messages CallExpression i18n._() should ex
9,
],
},
{
comment: My comment,
context: undefined,
id: my.id,
message: My Id Message,
origin: [
js-call-expression.js,
11,
],
},
]
`;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ const withValues = i18n._('Values {param}', { param: param });

const withContext = i18n._('Some id', {},{ context: 'Context1'});

const withNodeMessageDescriptor = i18n.nodeTranslate({ id: 'my.id', message: 'My Id Message', comment: 'My comment'});
const withNodeMessageDescriptor = i18n.t({ id: 'my.id', message: 'My Id Message', comment: 'My comment'});
84 changes: 84 additions & 0 deletions packages/core/src/i18n.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,90 @@ describe("I18n", () => {
})
})

it(".t should format message from catalog", () => {
const messages = {
Hello: "Salut",
"My name is {name}": "Je m'appelle {name}",
}

const i18n = setupI18n({
locale: "fr",
messages: { fr: messages },
})

expect(i18n.t({ id: "Hello" })).toEqual("Salut")
expect(i18n.t({ id: "My name is {name}" }, { name: "Fred" })).toEqual(
"Je m'appelle Fred"
)

// missing { name }
expect(i18n.t({ id: "My name is {name}" })).toEqual("Je m'appelle")

// Untranslated message
expect(i18n.t({ id: "Missing message" })).toEqual("Missing message")
expect(i18n.t({ id: "Missing {name}" }, { name: "Fred" })).toEqual(
"Missing Fred"
)
expect(
i18n.t(
{ id: "Missing with default", message: "Missing {name}" },
{ name: "Fred" }
)
).toEqual("Missing Fred")
})

it(".t allow escaping syntax characters", () => {
const messages = {
"My ''name'' is '{name}'": "Mi ''nombre'' es '{name}'",
}

const i18n = setupI18n({
locale: "es",
messages: { es: messages },
})

expect(i18n.t({ id: "My ''name'' is '{name}'" })).toEqual(
"Mi 'nombre' es {name}"
)
})

it(".t shouldn't compile messages in production", () => {
const messages = {
Hello: "Salut",
"My name is {name}": "Je m'appelle {name}",
}

mockEnv("production", () => {
const { setupI18n } = require("@lingui/core")
const i18n = setupI18n({
locale: "fr",
messages: { fr: messages },
})

expect(i18n.t({ id: "My name is {name}" }, { name: "Fred" })).toEqual(
"Je m'appelle {name}"
)
})
})

it(".t should emit missing event for missing translation", () => {
const i18n = setupI18n({
locale: "en",
messages: { en: { exists: "exists" } },
})

const handler = jest.fn()
i18n.on("missing", handler)
i18n.t({ id: "exists" })
expect(handler).toHaveBeenCalledTimes(0)
i18n.t({ id: "missing" })
expect(handler).toHaveBeenCalledTimes(1)
expect(handler).toHaveBeenCalledWith({
id: "missing",
locale: "en",
})
})

describe("params.missing - handling missing translations", () => {
it("._ should return custom string for missing translations", () => {
const i18n = setupI18n({
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/i18n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -262,9 +262,9 @@ export class I18n extends EventEmitter<Events> {
)(values, formats)
}

// Alias for _ to be used in node without macro setting
// Alias for _ to be used in node/js without macro setting
// uses message descriptors only
nodeTranslate(id: MessageDescriptor, values: Values | undefined = {}) {
t(id: MessageDescriptor, values: Values | undefined = {}) {
return this._(id, values, {})
}

Expand Down
24 changes: 24 additions & 0 deletions website/docs/ref/core.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,30 @@ i18n._("My name is {name}", { name: "Tom" })
i18n._("msg.id", { name: "Tom" }, { message: "My name is {name}" })
```

### `i18n.t(messageDescriptor, values)` {#i18n.t}

A small wrapper on the core translation meant for NodeJS/JS usage without macros.

`messageDescriptor` is an object of message parameters.

`values` is an object of variables used in translated message.

```ts
import { i18n } from "@lingui/core"

// Simple message
i18n.t({ id: "Hello" })

// Simple message using custom ID
i18n.t({ id: "msg.hello", message: "Hello"})

// Message with variable
i18n.t({ id: "My name is {name}" }, { name: "Tom"});

// Message with comment, custom ID and variable
i18n.t({ id: "msg.name", message: "My name is {name}", comment: "Message showing the passed in name" }, { name: "Tom"});
```

### `i18n.date(value: string | Date[, format: Intl.DateTimeFormatOptions])` {#i18n.date}

> **Returns**: Formatted date string Format a date using the conventional format for the active language.
Expand Down

0 comments on commit 1d90bbf

Please sign in to comment.