{"record":{"id":"7b37e9b681be766c","repo":"GoogleChrome/lighthouse","slug":"string-collision-message-and-description-are","errorCode":null,"errorMessage":"string collision: `message` and `description` are the same but differ in `placeholders`:\\nkey: ${entry.key}\\nmake `placeholders` match or update `message` or `description`s to explain why they don't match","messagePattern":"string collision: `message` and `description` are the same but differ in `placeholders`:\\\\nkey: (.+?)\\\\nmake `placeholders` match or update `message` or `description`s to explain why they don't match","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/scripts/i18n/collect-strings.js","lineNumber":694,"sourceCode":" *   should be changed to explain the need for different `placeholders`.\n *\n * Modifies `strings` in place to fix collisions.\n * @see https://github.com/GoogleChrome/lighthouse/blob/main/core/lib/i18n/README.md#collisions\n * @param {Record<string, CtcMessage>} strings\n */\nfunction resolveMessageCollisions(strings) {\n  const entries = Object.entries(strings).map(([key, ctc]) => ({key, ctc}));\n\n  const stringsByMessage = arrayGroupToMap(entries, (entry) => entry.ctc.message);\n  for (const messageGroup of stringsByMessage.values()) {\n    // If this message didn't collide with anything else, skip it.\n    if (messageGroup.length <= 1) continue;\n\n    // For each group of matching message+description, placeholders must also match.\n    const stringsByDescription = arrayGroupToMap(messageGroup, (entry) => entry.ctc.description);\n    for (const descriptionGroup of stringsByDescription.values()) {\n      if (!doPlaceholdersMatch(descriptionGroup)) {\n        throw new Error('string collision: `message` and `description` are the same but differ in `placeholders`:\\n' +\n          descriptionGroup.map(entry => `key: ${entry.key}\\n`).join('') +\n          'make `placeholders` match or update `message` or `description`s to explain why they don\\'t match');\n      }\n    }\n\n    // If the entire message group has the same description and placeholders,\n    // they can be translated as if a single message, no meaning needed.\n    if (stringsByDescription.size <= 1) continue;\n\n    // We have duplicate messages with different descriptions. Disambiguate using `meaning` for TC.\n    for (const {ctc} of messageGroup) {\n      ctc.meaning = ctc.description;\n    }\n  }\n}\n\n/**\n * Check that fixed collisions match our known list.","sourceCodeStart":676,"sourceCodeEnd":712,"githubUrl":"https://github.com/GoogleChrome/lighthouse/blob/9515cd4e58ebed69f78742d932b501c2cab8ad8f/core/scripts/i18n/collect-strings.js#L676-L712","documentation":"Thrown by resolveMessageCollisions() when two or more UIStrings entries share the same `message` and `description` text but have differing `placeholders`. The deduplication logic can merge identical message+description pairs only if their placeholders also match; a mismatch would create translation ambiguity.","triggerScenarios":"Two different UIStrings keys in the codebase use the same English message and description but define different @example placeholder values, causing the generated placeholder metadata to differ.","commonSituations":"Duplicate messages across audit files where one was updated with new examples but the other wasn't; copy-pasted strings that diverged in placeholder definitions.","solutions":["Find the colliding keys listed in the error message and make their placeholders (example values) identical.","If the placeholders are intentionally different, differentiate the `message` or `description` text to disambiguate.","Consolidate the two keys into one shared message export."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"function findCollidingPlaceholders(strings) {\n  const byMsg = new Map();\n  for (const [key, ctc] of Object.entries(strings)) {\n    const k = ctc.message + '||' + ctc.description;\n    if (!byMsg.has(k)) byMsg.set(k, []);\n    byMsg.get(k).push({key, placeholders: ctc.placeholders});\n  }\n  const collisions = [];\n  for (const group of byMsg.values()) {\n    if (group.length < 2) continue;\n    const first = JSON.stringify(group[0].placeholders);\n    if (group.some(g => JSON.stringify(g.placeholders) !== first)) collisions.push(group);\n  }\n  return collisions;\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["When duplicating a message string, copy its @example tags exactly.","Run collect-strings locally to detect collisions before CI.","If placeholder differences are intentional, change the description to disambiguate."],"tags":["i18n","collision","placeholder","deduplication","collect-strings"],"backgroundTag":null,"analyzedSha":"9515cd4e58ebed69f78742d932b501c2cab8ad8f","analyzedAt":"2026-08-13T06:28:10.346Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}