HubSpot Forms

The proxy supports translation of HubSpot (or similar) forms via a combination of project linking and JS translation.

Method #1 marshals a combination of advanced proxy features. It is entirely hands-off from the site maintainer’s perspective, no change on the original server is necessary (which is a rather frequent constraint).

Method #2 relies on injected JS and HubSpot to provide separate, localized forms for each target language. Compared to #1, it is a clean and simple approach.

Method #1: Proxy

The proxy approach traces the structure of the main and form domains via linked projects. Affected JS resources/endpoints are overridden and the responses marked as translatable.

Project Creation & Setup

HubSpot uses several external domains to drive a form. You will see referenced in the page source. This file itself references, which is where the translatable form contents are coming from.

Domains used by a HubSpot form are related to the main project and each other in the following manner:

../../_images/hs-form-w-js.pngHubSpot Projects

Assuming that is already set up, at most two additional projects are required:

  1. creation of this project is optional, though not complicated. At the time of writing, visiting the landing page results in a 403 Forbidden page (but this is not a problem).
  2. this URL redirects you to To create a project for it, disable Redirect checking in the Add Project dialog. Click on Advanced settings to reveal the option and uncheck the checkbox:

../../_images/create_project_disable_redirect_check.pngDisabling Redirect checks

Don’t forget to add every target language of the main project to each project you create.

Alternative: Search & Replace

The project is not, strictly speaking, necessary. Its true purpose is merely to expose a slightly modified version of the /forms/v2.js script. If its URL is referred to in a way that makes it possible, you can sidestep the domain using a combination of Search & Replace and a page content override. The setup steps for this are as follows (done on the main project):

  1. create a path override for the exact URL where the form is present (the diagram above shows /contact as an illustration).
  2. add a Search & Replace rule: replace https?:// (a regex matching both HTTP and HTTPS versions of the same URL) with the empty string. This turns the reference to /forms/v2.js into a relative URL, pointing it toward a page content override that is to be created in a moment).

Overriding v2.js

This resource contains a crucial variable called urlRoot, which has to be remappable over the proxy. However, it is set via a computed expression, which is unsupported by the proxy for reasons discussed in the section on JS translation, so an override and a small change is unavoidable (regardless of the presence/absence of the intermediate project). Follow the steps below to create the override:

  1. visit and copy & paste the contents of the JS file.
  2. use the DevTool or an online pretty printer before pasting the code. Though optional, it is highly recommended that you do this (such minified code is cumbersome to work with as it is).
  3. create a PCO for the /forms/v2.js pathname in Page modifiers > Content Override. The response code default is 200, and the Content-Type header is application/javascript; charset=utf-8. We’ll return to Cache-Control and Pragma later, after setup is complete.
  4. Add the following line to the top of the PCO:
  1. Search for this.urlRoot. It is set in a line similar to the one below:
 o ? this.urlRoot = "https://f.hsforms" + e + ".net/fallback" : null != a ? this.urlRoot = "" + a : this.urlRoot = "https://forms.hubspot" + e + ".com";
  1. Add the following line after it to make it use the “accessible” value:
this.urlRoot = HUBSPOT_URL_ROOT
  1. Use the Mark multiple resources as Translatable text field in Advanced settings. Simply add the pathname prefix of the PCO to the list:
  1. URL Translation & HTTP/HTTPS Finally, add the following JS path to the list of translatable paths in Advanced settings:

Open the PCO link over any one of the proxy preview domains to test it. If all projects are correctly linked and you followed the setup steps correctly, the HUBSPOT_URL_ROOT variable will hold an appropriate proxy-mapped domain (and consequently this.urlRoot will be set to the same value).

Form Contents

Set up the HubSpot content endpoint as translatable on the project for according to the JS translation section. In summary:

  1. locate the form request using the DevTool and add it to the “Mark multiple resources as translatable” list of prefixes. For any given HubSpot form, translatable content will usually be associated with a prefix similar to the one below (it will also have a callback query parameter).
  1. use the JSON path tester tool in Advanced settings to process the response. HubSpot forms come in a response format called JSONP or padded JSON (a function call such as hs_request_0 with the form data passed to it as argument). It is not necessary to prefix JS paths with "json" in this case.
  2. use the x-proxy to test your JS paths, and use Preview for content extraction.

Publishing & Caching

All projects need to be published together in all target languages. Note that you don’t need to publish on a subdomain of the original server: you are free to proxy the German version of through, for example.

Once setup, translation and publishing is complete, you are free to set an appropriate Cache Header on your page content overrides (either on the PCO itself or on a prefix-basis) to reduce page request costs.

Method #2: HubSpot & Injected JS

You can rely on HubSpot to localize the form property names after cloning your form for each target language, and rely on a little JavaScript zo drive the forms on the client-side for each target language. This approach is cleaner than the one described above, as long as you don’t mind having a separate form for each target language.

The proxy sets the lang attribute of the <html> tag to the appropriate locale code on each target language domain, which you can use for branching. The code below demonstrates one example of how such code could look in practice:

var lang = document.querySelector("html").getAttribute("lang");

var HSFormId = {
  "en-US": "English9-bb4c-45b4-8e32-21cdeaa3a7f0",
  "fr-FR": "Frenche9-bb4c-45b4-8e32-21cdeaa3a7f0",
  "de-DE": "Germane9-bb4c-45b4-8e32-21cdeaa3a7f0"

// refer to the HubSpot documentation for further customization at
  portalId: "portalId",
  formId: HubSpotFormId[lang]