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

[Tools] Add TemplateLiteral parsing to i18n_check tool #24580

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
14 changes: 13 additions & 1 deletion src/dev/i18n/extractors/__snapshots__/i18n_call.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,19 @@ Array [
]
`;

exports[`dev/i18n/extractors/i18n_call throws if defaultMessage is not a string literal 1`] = `"defaultMessage value should be a string literal (\\"message-id\\")."`;
exports[`dev/i18n/extractors/i18n_call extracts "i18n" and "i18n.translate" functions call message 3`] = `
Array [
"message-id-3",
Object {
"context": "Message
context 3",
"message": "Default
message 3",
},
]
`;

exports[`dev/i18n/extractors/i18n_call throws if defaultMessage is not a string literal 1`] = `"defaultMessage value should be a string or template literal (\\"message-id\\")."`;

exports[`dev/i18n/extractors/i18n_call throws if message id value is not a string literal 1`] = `"Message id should be a string literal."`;

Expand Down
4 changes: 2 additions & 2 deletions src/dev/i18n/extractors/__snapshots__/react.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ Array [
]
`;

exports[`dev/i18n/extractors/react extractIntlMessages throws if context value is not a string literal 1`] = `"context value should be a string literal (\\"message-id\\")."`;
exports[`dev/i18n/extractors/react extractIntlMessages throws if context value is not a string literal 1`] = `"context value should be a string or template literal (\\"message-id\\")."`;

exports[`dev/i18n/extractors/react extractIntlMessages throws if defaultMessage value is not a string literal 1`] = `"defaultMessage value should be a string literal (\\"message-id\\")."`;
exports[`dev/i18n/extractors/react extractIntlMessages throws if defaultMessage value is not a string literal 1`] = `"defaultMessage value should be a string or template literal (\\"message-id\\")."`;

exports[`dev/i18n/extractors/react extractIntlMessages throws if message id is not a string literal 1`] = `"Message id should be a string literal."`;
12 changes: 12 additions & 0 deletions src/dev/i18n/extractors/i18n_call.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ const translateCallMessageSource = `
i18n.translate('message-id-2', { defaultMessage: 'Default message 2', context: 'Message context 2' });
`;

const i18nCallMessageWithTemplateLiteralSource = `
i18n('message-id-3', { defaultMessage: \`Default
message 3\`, context: \`Message
context 3\` });
`;

describe('dev/i18n/extractors/i18n_call', () => {
test('extracts "i18n" and "i18n.translate" functions call message', () => {
let callExpressionNode = [...traverseNodes(parse(i18nCallMessageSource).program.body)].find(
Expand All @@ -44,6 +50,12 @@ describe('dev/i18n/extractors/i18n_call', () => {
);

expect(extractI18nCallMessages(callExpressionNode)).toMatchSnapshot();

callExpressionNode = [
...traverseNodes(parse(i18nCallMessageWithTemplateLiteralSource).program.body),
].find(node => isCallExpression(node));

expect(extractI18nCallMessages(callExpressionNode)).toMatchSnapshot();
});

test('throws if message id value is not a string literal', () => {
Expand Down
39 changes: 33 additions & 6 deletions src/dev/i18n/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
isObjectExpression,
isObjectProperty,
isStringLiteral,
isTemplateLiteral,
} from '@babel/types';
import fs from 'fs';
import glob from 'glob';
Expand Down Expand Up @@ -197,20 +198,46 @@ export function extractMessageIdFromNode(node) {
return node.value;
}

function parseTemplateLiteral(node, messageId) {
// TemplateLiteral consists of quasis (strings) and expressions.
// If we have at least one expression in template literal, then quasis length
// will be greater than 1
if (node.quasis.length > 1) {
throw createFailError(`expressions are not allowed in template literals ("${messageId}").`);
}

// Babel reads 'cooked' and 'raw' versions of a string.
// 'cooked' acts like a normal StringLiteral value and interprets backslashes
// 'raw' is primarily designed for TaggedTemplateLiteral and escapes backslashes
return node.quasis[0].value.cooked;
LeanidShutau marked this conversation as resolved.
Show resolved Hide resolved
}

export function extractMessageValueFromNode(node, messageId) {
if (!isStringLiteral(node)) {
throw createFailError(`defaultMessage value should be a string literal ("${messageId}").`);
if (isStringLiteral(node)) {
return node.value;
}

return node.value;
if (isTemplateLiteral(node)) {
return parseTemplateLiteral(node, messageId);
}

throw createFailError(
`defaultMessage value should be a string or template literal ("${messageId}").`
);
}

export function extractContextValueFromNode(node, messageId) {
if (!isStringLiteral(node)) {
throw createFailError(`context value should be a string literal ("${messageId}").`);
if (isStringLiteral(node)) {
return node.value;
}

return node.value;
if (isTemplateLiteral(node)) {
return parseTemplateLiteral(node, messageId);
}

throw createFailError(
`context value should be a string or template literal ("${messageId}").`
);
}

export function extractValuesKeysFromNode(node, messageId) {
Expand Down