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

Extract parameter type translations #59

Merged
merged 3 commits into from Apr 5, 2019
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
59 changes: 43 additions & 16 deletions src/BabelPluginI18n.ts
Expand Up @@ -5,14 +5,17 @@ import {
hasStringLiteralJSXAttribute,
isSvgElementAttribute
} from "./visitorChecks";
import { NodePath } from "ast-types";
import { JSXElement } from "ast-types/gen/nodes";
import jsx from "ast-types/def/jsx";

let keyMaxLength = 40;
let phrases: string[] = [];
let i18nMap = {};

const addPhrase = (str: string) => {
const key = getStableKey(str, keyMaxLength);
const value = getStableValue(str);
const addPhrase = (displayText: string, keyText?: string) => {
const key = getStableKey(keyText ? keyText: displayText, keyMaxLength);
const value = getStableValue(displayText);

if (!key || !value) {
return null;
Expand All @@ -31,26 +34,21 @@ function BabelPluginI18n(): PluginObj {
return {
name: 'i18n',
visitor: {
JSXText(path) {
const { node } = path;

if (node.value && node.value.trim()) {
const values = addPhrase(node.value);

if (!values) {
return;
}

path.node.value = `t('${values.key}')`
}
},
JSXAttribute(path) {
const { node } = path;

if (hasStringLiteralJSXAttribute(path) && !isSvgElementAttribute(path)) {
addPhrase(node.value.value);
}
},
JSXElement(path) {
const { node } = path;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

better extract this logic to a function to make it clear

const jsxContentNodes = node.children;
extractArguments(jsxContentNodes)
.forEach(({textWithArgs, textWithoutArgs}) =>
addPhrase(textWithArgs, textWithoutArgs));

},
JSXExpressionContainer(path) {
const { node } = path;

Expand Down Expand Up @@ -91,6 +89,35 @@ function BabelPluginI18n(): PluginObj {
}
}

function extractArguments(jsxContentNodes: NodePath<any>[]) {
let textWithArgs = '';
let textWithoutArgs = '';
let argIndex = 0;
let hasText = false;
const texts = [];
for(let i = 0; i < jsxContentNodes.length; i++) {
const element = jsxContentNodes[i];
if (element.type === 'JSXText') {
hasText = true;
textWithArgs += element.value;
textWithoutArgs += element.value;
} else if (element.type === 'JSXExpressionContainer') {
textWithArgs += `{arg${argIndex}}`;
argIndex++;
} else {
if (hasText) {
texts.push({ textWithArgs, textWithoutArgs });
textWithArgs = '';
textWithoutArgs = ''
}
}
}
if (hasText) {
texts.push({ textWithArgs, textWithoutArgs });
}
return texts;
}

BabelPluginI18n.clear = () => {
phrases = [];
i18nMap = {};
Expand Down
1 change: 1 addition & 0 deletions src/__test__/generateResources.spec.ts
Expand Up @@ -38,4 +38,5 @@ describe('generateResources', () => {
defineTest(__dirname, 'Yup');
defineTest(__dirname, 'CallExpression');
defineTest(__dirname, 'Svg');
defineTest(__dirname, 'Parameters');
});
1 change: 1 addition & 0 deletions src/__test__/i18nTransfomerCodemod.test.ts
Expand Up @@ -16,4 +16,5 @@ describe('i18nTransformerCodemod', () => {
defineTest(__dirname, 'i18nTransformerCodemod', null, 'HooksInlineExport');
defineTest(__dirname, 'i18nTransformerCodemod', null, 'Classnames');
defineTest(__dirname, 'i18nTransformerCodemod', null, 'Svg');
defineTest(__dirname, 'i18nTransformerCodemod', null, 'Parameters');
});
12 changes: 12 additions & 0 deletions src/__testfixtures__/Parameters.input.tsx
@@ -0,0 +1,12 @@
import React, { useState } from "react";

const SiteHeader = () => {
const [number] = useState(42);
return <span>
My simple {number} text
<span>Other text</span>
Even more Text
</span>;
};

export default SiteHeader;
15 changes: 15 additions & 0 deletions src/__testfixtures__/Parameters.output.tsx
@@ -0,0 +1,15 @@
import React, { useState } from "react";

import { useTranslation } from 'react-i18next';

const SiteHeader = () => {
const { t } = useTranslation();
const [number] = useState(42);
return (
<span>{t('my_simple_text', {
arg0: number
})}<span>{t('other_text')}</span>{t('even_more_text')}</span>
);
};

export default SiteHeader;
7 changes: 7 additions & 0 deletions src/__testfixtures__/Parameters.resource.tsx
@@ -0,0 +1,7 @@
export default {
translation: {
my_simple_text: `My simple {arg0} text`,
even_more_text: `Even more Text`,
other_text: `Other text`,
},
};
2 changes: 1 addition & 1 deletion src/__testfixtures__/Props.output.tsx
Expand Up @@ -10,7 +10,7 @@ const Custom = (props: CustomProps) => {
<div>
<span>{props.title}</span>
</div>
)
);
}

const Component123 = () => {
Expand Down
2 changes: 1 addition & 1 deletion src/__testfixtures__/Tsx.output.tsx
Expand Up @@ -10,7 +10,7 @@ const Custom = (props: CustomProps) => {
<div>
<span>{props.title}</span>
</div>
)
);
}

const Simple = () => {
Expand Down
64 changes: 64 additions & 0 deletions src/i18nTransformerCodemod.ts
Expand Up @@ -222,6 +222,53 @@ function translateFunctionArguments(j: JSCodeshift, root: Collection<any>) {
//<span>test</span>
function translateJsxContent(j: JSCodeshift, root: Collection<any>) {
let hasI18nUsage = false;
root.find(j.JSXElement)
.forEach((n: NodePath<JSXElement>) => {
const jsxContentNodes = n.value.children;
let text = '';
let translateArgs = [];
let newChildren = [];
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

better move to a separate function

for(let i = 0; i < jsxContentNodes.length; i++) {
const element = jsxContentNodes[i];
if (j.JSXText.check(element)) {
if (element.value.trim().length > 0) {
text += element.value;
} else {
newChildren.push(element);
}
continue;
} else if (j.JSXExpressionContainer.check(element)) {
translateArgs.push(element.expression);
continue;
}
if (text.trim().length > 0) {
hasI18nUsage = true;
newChildren.push(buildTranslationWithArgumentsCall(
j, translateArgs, text.trim()
));
}
text = '';
translateArgs = [];
newChildren.push(element);
}

if (text.trim().length > 0) {
hasI18nUsage = true;
newChildren.push(buildTranslationWithArgumentsCall(
j, translateArgs, text.trim()
));
}
if (newChildren.length > 0) {
//n.value.children = newChildren;
n.replace(
j.jsxElement(
n.node.openingElement, n.node.closingElement,
newChildren
)
)
}
});

root
.find(j.JSXText)
.filter((path: NodePath<JSXText>) => path.node.value && path.node.value.trim())
Expand Down Expand Up @@ -290,6 +337,23 @@ function translateJsxProps(j: JSCodeshift, root: Collection<any>) {
return hasI18nUsage;
}

function buildTranslationWithArgumentsCall(j: JSCodeshift, translateArgs: any, text: string) {
const translationCallArguments = [
j.literal(getStableKey(text)),
] as any;
if (translateArgs.length > 0) {
translationCallArguments.push(
j.objectExpression(
translateArgs.map((expression: any, index: any) =>
j.property('init', j.identifier('arg' + index), expression )
)
)
)
}
return j.jsxExpressionContainer(
j.callExpression(j.identifier('t'), translationCallArguments));
}

function withTranslationHoc(j: JSCodeshift, identifier: any) {
return j.callExpression(
j.callExpression(
Expand Down