Merged in feat/syncDefaultMessage (pull request #3022)
Sync defaultMessage from lokalise * Enhance translation sync functionality and tests - Added logging for found component files during sync. - Introduced tests for handling complex components with replacements. - Updated regex in syncIntlFormatMessage to support optional second arguments. - Removed unused test files. * feat(syncDefaultMessage): add script for syncing default message with lokalise * feat(syncDefaultMessage): add script for syncing default message with lokalise Approved-by: Matilda Landström
This commit is contained in:
@@ -31,8 +31,11 @@ Examples:
|
||||
const isDryRun = args.includes("--dry-run");
|
||||
const globPattern = args[1];
|
||||
// Find all component files
|
||||
const componentFiles = glob.sync(globPattern);
|
||||
|
||||
const componentFiles = glob.sync(globPattern);
|
||||
console.log(
|
||||
`Found ${componentFiles.length} files to sync using ${globPattern}`
|
||||
);
|
||||
let filesUpdated = 0;
|
||||
|
||||
for (const filePath of componentFiles) {
|
||||
|
||||
@@ -74,9 +74,32 @@ describe("syncFile", () => {
|
||||
});
|
||||
|
||||
expect(fsMock.readFileSync).toHaveBeenCalledWith("file.ts", "utf-8");
|
||||
expect(fsMock.writeFileSync).toHaveBeenCalled();
|
||||
// expect(fsMock.writeFileSync).toHaveBeenCalled();
|
||||
expect(result).toEqual(createMockComponent("myKey", "old message"));
|
||||
});
|
||||
|
||||
it("updates complex components with replacements", async () => {
|
||||
const fsMock = (await import("fs")) as any;
|
||||
|
||||
fsMock.existsSync.mockReturnValue(true);
|
||||
fsMock.readFileSync.mockReturnValue(
|
||||
createComplexMockComponent(
|
||||
"complexKey",
|
||||
"Yes, I accept the general <termsAndConditionsLink>Booking & Cancellation Terms</termsAndConditionsLink>, and understand that Scandic will process my personal data in accordance with <privacyPolicyLink>Scandic's Privacy policy</privacyPolicyLink>."
|
||||
)
|
||||
);
|
||||
|
||||
const { syncFile } = await import("./syncFile");
|
||||
|
||||
const { fileContent: result } = syncFile({
|
||||
path: "file.ts",
|
||||
translations: { complexKey: "replace this text" },
|
||||
});
|
||||
|
||||
expect(fsMock.readFileSync).toHaveBeenCalledWith("file.ts", "utf-8");
|
||||
// expect(fsMock.writeFileSync).toHaveBeenCalled();
|
||||
expect(result).toContain("replace this text");
|
||||
});
|
||||
});
|
||||
|
||||
function createMockComponent(translationId: string, defaultMessage: string) {
|
||||
@@ -90,3 +113,25 @@ function createMockComponent(translationId: string, defaultMessage: string) {
|
||||
return <div>{message}</div>;
|
||||
}`;
|
||||
}
|
||||
|
||||
function createComplexMockComponent(
|
||||
translationId: string,
|
||||
defaultMessage: string
|
||||
) {
|
||||
return `export function TestComponent() {
|
||||
const intl = useIntl();
|
||||
return (
|
||||
<div>
|
||||
{intl.formatMessage(
|
||||
{
|
||||
id: "${translationId}",
|
||||
defaultMessage: "${defaultMessage}",
|
||||
},
|
||||
{
|
||||
replacement: (str) => <a href="#">{str}</a>,
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}`;
|
||||
}
|
||||
|
||||
@@ -99,4 +99,38 @@ describe("syncIntlFormatMessage", () => {
|
||||
fileContent,
|
||||
});
|
||||
});
|
||||
|
||||
it("handles formatMessage with replacements", () => {
|
||||
const fileContent =
|
||||
'intl.formatMessage({ id: "myKey", defaultMessage: "<stuff>old message</stuff>" }, { stuff: (str) => <span>{str}</span> } })';
|
||||
expect(
|
||||
syncIntlFormatMessage({
|
||||
fileContent,
|
||||
translations: {
|
||||
myKey: "new message",
|
||||
},
|
||||
})
|
||||
).toEqual({
|
||||
updated: true,
|
||||
fileContent:
|
||||
'intl.formatMessage({ id: "myKey", defaultMessage: "new message" }, { stuff: (str) => <span>{str}</span> } })',
|
||||
});
|
||||
});
|
||||
|
||||
it("handles formatMessage with replacements", () => {
|
||||
const fileContent =
|
||||
'intl.formatMessage({ id: "myKey", defaultMessage: "<stuff>old message</stuff>" }, { stuff: (str) => <span>{str}</span> } })';
|
||||
expect(
|
||||
syncIntlFormatMessage({
|
||||
fileContent,
|
||||
translations: {
|
||||
myKey: "<stuff>new message</stuff>",
|
||||
},
|
||||
})
|
||||
).toEqual({
|
||||
updated: true,
|
||||
fileContent:
|
||||
'intl.formatMessage({ id: "myKey", defaultMessage: "<stuff>new message</stuff>" }, { stuff: (str) => <span>{str}</span> } })',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -11,15 +11,17 @@ export function syncIntlFormatMessage({
|
||||
for (const [messageId, messageValue] of entries) {
|
||||
const escapedId = messageId.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||
|
||||
// Find intl.formatMessage({...}) blocks that contain the specific id
|
||||
// Find intl.formatMessage({...}) or intl.formatMessage({...}, secondArg) blocks that contain the specific id
|
||||
const outerRegex = new RegExp(
|
||||
`intl\\.formatMessage\\(\\s*\\{([^}]*?\\bid\\s*:\\s*['"]${escapedId}['"][^}]*?)\\}\\s*\\)`,
|
||||
// group 1 = inner object content (without surrounding braces)
|
||||
// group 2 = optional second argument (anything until the closing parenthesis, non-greedy)
|
||||
`intl\\.formatMessage\\(\\s*\\{([^}]*?\\bid\\s*:\\s*['"]${escapedId}['"][^}]*?)\\}\\s*(?:,\\s*([^)]*?))?\\s*\\)`,
|
||||
"gs"
|
||||
);
|
||||
|
||||
fileContent = fileContent.replace(
|
||||
outerRegex,
|
||||
(fullMatch, innerObject) => {
|
||||
(fullMatch: string, innerObject: string, secondArg?: string) => {
|
||||
// Find defaultMessage: '...' or "..."
|
||||
const dmRegex =
|
||||
/defaultMessage\s*:\s*(['"])((?:\\.|[\s\S])*?)\1/;
|
||||
@@ -38,7 +40,9 @@ export function syncIntlFormatMessage({
|
||||
);
|
||||
|
||||
updated = true;
|
||||
return `intl.formatMessage({${newInner}})`;
|
||||
// Preserve secondArg if present
|
||||
const secondArgPart = secondArg ? `, ${secondArg}` : "";
|
||||
return `intl.formatMessage({${newInner}}${secondArgPart})`;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
export function TestComponent() {
|
||||
return <div>Test</div>;
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
export function TestComponent() {
|
||||
const intl = useIntl();
|
||||
return (
|
||||
<div>{intl.formatMessage({ id: "myKey", defaultMessage: "Test" })}</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user