function getTranslucentShaderProgram(context, shaderProgram, keyword, source) {
  const { shaderCache } = context;
  const shader = shaderCache.getDerivedShaderProgram(shaderProgram, keyword);
  if (defined(shader)) {
    return shader;
  }

  const attributeLocations = shaderProgram._attributeLocations;
  const fs = shaderProgram.fragmentShaderSource.clone();

  fs.sources = fs.sources.map(function (fsSource) {
    return ShaderSource.replaceMain(fsSource, "czm_translucent_main")
      .replace(/out_FragColor/g, "czm_out_FragColor")
      .replace(
        /layout\s*\(location\s*=\s*0\)\s*out\s+vec4\s+out_FragColor;/g,
        "",
      )
      .replace(/\bdiscard\b/g, "czm_discard = true")
      .replace(/czm_phong/g, "czm_translucentPhong");
  });

  // Discarding the fragment in main is a workaround for ANGLE D3D9
  // shader compilation errors.
  fs.sources.splice(
    0,
    0,
    `vec4 czm_out_FragColor;\n` + `bool czm_discard = false;\n`,
  );

  const fragDataMatches = [...source.matchAll(/out_FragData_(\d+)/g)];
  let fragDataDeclarations = ``;
  for (let i = 0; i < fragDataMatches.length; i++) {
    const fragDataMatch = fragDataMatches[i];
    fragDataDeclarations = `layout (location = ${fragDataMatch[1]}) out vec4 ${fragDataMatch[0]};\n${fragDataDeclarations}`;
  }
  fs.sources.push(fragDataDeclarations);

  fs.sources.push(
    `${
      "void main()\n" +
      "{\n" +
      "    czm_translucent_main();\n" +
      "    if (czm_discard)\n" +
      "    {\n" +
      "        discard;\n" +
      "    }\n"
    }${source}}\n`,
  );

  return shaderCache.createDerivedShaderProgram(shaderProgram, keyword, {
    vertexShaderSource: shaderProgram.vertexShaderSource,
    fragmentShaderSource: fs,
    attributeLocations: attributeLocations,
  });
}
@private
@param {Context} context
@param {ShaderProgram} shaderProgram
@param {string} keyword
@param {string} source
@returns {ShaderProgram}