{"record":{"id":"bae8156b83d21222","repo":"GoogleChrome/lighthouse","slug":"could-not-fetch-data-for-locale-locale","errorCode":null,"errorMessage":"could not fetch data for locale: ${locale}","messagePattern":"could not fetch data for locale: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"viewer/app/src/viewer-ui-features.js","lineNumber":90,"sourceCode":"    }\n  }\n\n  /**\n   * @param {LH.Locale} locale\n   * @return {Promise<LhlMessages>}\n   */\n  async _fetchLocaleMessages(locale) {\n    const response = await fetch(`./locales/${locale}.json`);\n    return response.json();\n  }\n\n  /**\n   * @param {LH.Locale} locale\n   */\n  async _swapLocale(locale) {\n    const lhlMessages = await this._fetchLocaleMessages(locale);\n    const i18nModule = await this._getI18nModule();\n    if (!lhlMessages) throw new Error(`could not fetch data for locale: ${locale}`);\n\n    i18nModule.format.registerLocaleData(locale, lhlMessages);\n    const newLhr = i18nModule.swapLocale(this.json, locale).lhr;\n    this._refreshCallback(newLhr);\n  }\n\n  /**\n   * The i18n module is only need for swap-locale-feature.js, and is ~30KB,\n   * so it is lazily loaded.\n   * TODO: reduce the size of the formatting code and include it always (remove lazy load),\n   *       possibly moving into base ReportUIFeatures.\n   */\n  _getI18nModule() {\n    return import('../../../shared/localization/i18n-module.js');\n  }\n}\n","sourceCodeStart":72,"sourceCodeEnd":107,"githubUrl":"https://github.com/GoogleChrome/lighthouse/blob/9515cd4e58ebed69f78742d932b501c2cab8ad8f/viewer/app/src/viewer-ui-features.js#L72-L107","documentation":"Thrown by the viewer's _swapLocale when the freshly fetched locale-messages value is falsy. _fetchLocaleMessages does fetch('./locales/<locale>.json').json(); if that resolves to null/empty (missing or malformed locale file, failed fetch that still parsed), _swapLocale aborts with the offending locale in the message. It runs only after locale data registration is attempted.","triggerScenarios":"Selecting a locale in the viewer whose locales/<locale>.json file is missing from the build, returns an empty body, or resolves to null/empty — then _swapLocale receives no usable messages.","commonSituations":"Viewer build that did not bundle the requested locale file; locale filename mismatch (case/region code); served locales directory incomplete; CDN/path misconfiguration so the file 404s but parsing yields a falsy result.","solutions":["Ensure locales/<locale>.json is present and non-empty in the deployed viewer build for every locale offered in the picker.","Gate the locale picker on availability: verify the locale file is bundled before allowing selection.","On this error, fall back to the current/default locale instead of leaving the UI broken."],"exampleFix":"// before\nasync _fetchLocaleMessages(locale) {\n  const r = await fetch(`./locales/${locale}.json`);\n  return r.json(); // may be null/empty -> throws in _swapLocale\n}\n\n// after\nasync _fetchLocaleMessages(locale) {\n  const r = await fetch(`./locales/${locale}.json`);\n  if (!r.ok) return null;\n  const msgs = await r.json();\n  return msgs && Object.keys(msgs).length ? msgs : null;\n}","handlingStrategy":"validation","validationCode":"async _fetchLocaleMessages(locale) {\n  const r = await fetch(`./locales/${locale}.json`);\n  if (!r.ok) return null;\n  const msgs = await r.json();\n  return msgs && Object.keys(msgs).length ? msgs : null;\n}\n// Then only proceed in _swapLocale when the result is non-null.","typeGuard":"/** @param {unknown} m */\nfunction hasLocaleMessages(m) {\n  return m != null && typeof m === 'object' && Object.keys(/** @type {object} */ (m)).length > 0;\n}","tryCatchPattern":"try {\n  await this._swapLocale(locale);\n} catch (err) {\n  if (err.message.startsWith('could not fetch data for locale')) {\n    showMessage(`Locale '${locale}' is unavailable; staying on the current language.`);\n  } else throw err;\n}","preventionTips":["Bundle locales/<locale>.json for every locale offered in the viewer's picker.","Verify fetch response.ok and that the parsed body is non-empty before registering.","Fall back to the current/default locale when a locale file is unavailable."],"tags":["localization","locale","viewer","network","i18n"],"backgroundTag":null,"analyzedSha":"9515cd4e58ebed69f78742d932b501c2cab8ad8f","analyzedAt":"2026-08-13T06:28:10.346Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}