export function syncIntlFormatMessage({ translations, fileContent, }: { translations: Record; fileContent: string; }): { updated: boolean; fileContent: string } { let updated = false; const entries = Object.entries(translations); for (const [messageId, messageValue] of entries) { const escapedId = messageId.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); // Find intl.formatMessage({...}) or intl.formatMessage({...}, secondArg) blocks that contain the specific id const outerRegex = new RegExp( // 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: string, innerObject: string, secondArg?: string) => { // Find defaultMessage: '...' or "..." const dmRegex = /defaultMessage\s*:\s*(['"])((?:\\.|[\s\S])*?)\1/; if (!dmRegex.test(innerObject)) return fullMatch; const newInner = innerObject.replace( dmRegex, (_m: unknown, quote: string, _old: unknown) => { // Escape backslashes first, then the surrounding quote, and newlines const escaped = messageValue .replace(/\\/g, "\\\\") .replace(new RegExp(quote, "g"), `\\${quote}`) .replace(/\n/g, "\\n"); return `defaultMessage: ${quote}${escaped}${quote}`; } ); updated = true; // Preserve secondArg if present const secondArgPart = secondArg ? `, ${secondArg}` : ""; return `intl.formatMessage({${newInner}}${secondArgPart})`; } ); } return { updated, fileContent }; }