static localize(str_, traceEngineI18nObject) {
    /** @type {Record<string, string|number>|undefined} */
    let values;
    if (traceEngineI18nObject.values) {
      values = {};
      for (const [key, value] of Object.entries(traceEngineI18nObject.values)) {
        if (value && typeof value === 'object' && '__i18nBytes' in value) {
          values[key] = value.__i18nBytes;
          // TODO: use an actual byte formatter. Right now, this shows the exact number of bytes.
        } else if (value && typeof value === 'object' && '__i18nMillis' in value) {
          values[key] = `${value.__i18nMillis} ms`;
          // TODO: use an actual time formatter.
        } else if (value && typeof value === 'object' && 'i18nId' in value) {
          // TODO: add support for str_ values to be IcuMessage. For now, we translate it here.
          // This means that locale swapping won't work for this portion of the IcuMessage.
          // @ts-expect-error
          values[key] = str_(value.i18nId, value.values).formattedDefault;
        } else {
          values[key] = value;
        }
      }
    }

    return str_(traceEngineI18nObject.i18nId, values);
  }
Converts the input parameters given to `i18nString` usages in DevTools to a
LH.IcuMessage.

@param {ReturnType<i18n.createIcuMessageFn>} str_
@param {import('../lib/trace-engine.js').DevToolsIcuMessage} traceEngineI18nObject
@return {LH.IcuMessage}