Has anyone figure out how to use Google Translate on their forms in version 11? I know you could do it in the classic designer, but I am pretty new to LF and cannot seem to figure out how to do it in version 11.
Question
Question
Google Translate in Forms Version 11
Answer
Hi everyone. I was able to get with Zachary and get a copy of the solution he created. I created a video that walks through the basics of how it works (with Zac's permission). You will want to go to the actual YouTube video as there is a link to download the Forms processes talked about in the video from the video description.
Here is part 2 of this solution:
The links to download the forms does not work. Can you provide a new link please? Thanks!
The link to download the Forms process has been updated, so you should be able to download it now.
There is now a version 2 of this solution.
Version 2 supports custom html fields, default values for single line fields (and some others but mostly untested), and dropdown/checkbox/radio choice labels. Please post here if you come across bugs or need additional features!
Thank you Zac! I implemented this today and it looks great.
Hi Zachary,
What if the form performs lookups so you don't know what the choices will be for particular fields ahead of time?
Also, wouldn't this solution require a lot of maintenance that scales with the number of languages, since every time a field is added or removed you would need to make a change to the Javascript for each language? I am trying to set up translation for a form with more than 200 fields and 11 languages and am worried about the future maintenance, let alone the initial setup.
Replies
Which languages do you need to support? We are implementing a feature that should allow you to update all text elements on the form that will be released in Q4 of this year for self hosted systems. Unfortunately, this still does not enable you to use the Google Translate widget out of the box, and instead requires you to setup the translations yourself.
Like another commenter suggested, you can use the Classic designer to implement this still. This is on my use case list to look into though so stay tuned
@████████ Any updates on this feature? Thanks
It looks like they just released it for LF Cloud, so Self-hosted systems should be next.
Did this end up getting implemented in the latest version to use in the new designer.
Yes, the ability to update text elements was included in Forms 11 Update 5.
Is this part of the HTML or the JS. I can't seem to get the translate element to work in Modern.
This is what I was using in classic.
$.getScript("//translate.google.com/translate_a/element.js?cb=googleTranslateElementInit");
function googleTranslateElementInit() {
new google.translate.TranslateElement({pageLanguage: 'en'}, 'google_translate_element');
You are still not able to use that method. The method that Zachary was referring to is that there were new interfaces within the LFForms object to change the field labels, which can in turn be used to change a field label to a different language. I haven't seen any examples of code on how to accomplish this yet though.
Also, jquery is not available in the new form designer, I would recommend using the JS fetch method directly to interact with Google's API. The general flow I designed was
- Finalize form design
- Establish the languages I want to support
- Include these languages in a radio/dropdown on the form
- Run the form and fields through the Google translate API to the desired languages, including english
- save these values to the form JS in the form of a JSON object that allows you to simply map the fields and ids back to their various settings
{
"en": {39: {
}
"label": "Contract Upload",
"subtext": "",
"tooltip": "",
"buttonLabel": "Choose files"
}
} - disable the translation
- run code on field change of the language field to parse the saved JSON onto the form field settings
A video of the solution that I built was shown in the cloud release video here
https://doc.laserfiche.com/releases/Content/2023.10.htm at the 2:50 mark
We do at some point hope to have more native support for designer defined languages without scripting but we believe this feature filled as a stop gap for those that needed the feature asap. Please do let me know if you need help or if this does not quite meet your needs!
Will this work on Custom HTML as well as fields?
@████████, would it be possible to not even use the Google Translate API now? For example, if you had a radio button where each option was a Language value (English, Spanish, and French). And when the radio button value is changed, update the field labels accordingly using the changFieldSettings? Granted you would have to manually set each value instead of using the Google API to do it. If yes, are you able to share some code of how that would work?
Also, if you are able to share the code for using the Google Translate API that is shown in the video, that would be great. The video doesn't show the full code because of the width of the screen and it stops short of scrolling to the bottom.
Yes you could definitely build the JSON yourself instead of relying on Google's translations to generate the JSON. It is just a matter of making sure you format it properly so you can easily parse it.
Let me locate the code and I will email it to you.
can you send it to me too please?
Zachary, could you send me the code as well? The youtube video has an old link to the forms that does not work.
The link from the YouTube video works when I try it. Are you getting an error?
Doesn’t work for me either. Just opens a new tab that immediately closes. No download
The email I have for you doesn’t work anymore
It worked for me using Microsoft Edge but gave a warning that it couldn't be downloaded securely. Google Chrome just opened a new tab and closed it immediately. Either way I have updated the link in the video, and it should work correctly regardless of which browser you are using.
Thanks guys!
I think you'd be hard pressed to make this work in the Forms Layout Designer (the Modern Designer). It requires adding a widget to the body of the form, which you can do with the Custom HTML field element, and then it requires running a script on the page that interacts with that widget in the Custom HTML field element, which is where things get difficult. Because the Javascript is running within a sandboxed iFrame, it cannot directly access the components of the form or fields, so it can't coordinate with the widget directly - it also wouldn't be able to access all the parts of the form to actually translate the text.
I messed with it for a while trying to make it work but without any success.
I'm guessing this is one that isn't going to be possible without some sort of built-in integration from Laserfiche.
Wow, thank you Matthew. I was afraid of that kind of answer. I suspected someone had to have seen this as well.
@████████ please take a look at the marked answer and let me know if it works for you!
I am going to be upgrading to Forms 11 Update 5 soon and will try this. Right now we are on update 4.
Fantastic, looking forward to hearing how it goes!
@laserfiche, FWIW this has blocked adoption by three of our larger municipalities.
Just to clarfy what Matthew mentioned.
Laserfiche 11 has 2 forms Designers: Classic and Modern Designer. The Google translate configuration as Matthew describes works great under LF 11, just use the classic designer.
@████████ Can you take a look at my answer detailed here and let me know if it would work for you and these municipalities? If not I would be interested to pick your brain about the use cases and what a solution would look like.
Classic designer requires SO MUCH MORE coding to format forms for layout efficiency or to have any aesthetic appeal at all. I don't know how it is in a lot of municipals, but at least with my organization, each department has to have their own users who have to learn to build their forms, workflows and processes and most of us don't have the background for that much coding work (nor the excess time to do it).
I REALLY hope that you can find a way to make this kind of accessibility able to be easily achieved in the future.
Thanks for all the hard work on this solution! And the packaging by Blake :)
I'd like to echo @████████ and ask about if this works with custom fields? Or what do we need to think about to accomplish that?
How does this work on the input values of the fields? Will it change the characters if say, it is set to Russian?
What about the content of existing inputs? Say there is a field that is populated prior to getting to the translate form, with the value in the field input also get translated?
Thanks!
This won't work on custom HTML if that is what you are asking. I just had to create an English and Spanish version for all my custom HTMLs and show/hide based on the language selected. I only have two languages though, so it would be a lot if you have more.
Values are not translated with this setup. All the fields get translated up front like in Blake's video. If you need to make changes, add or delete fields you will have to adjust the JS manually or rerun the API translation.
Default values don't translate either. Its essentially tabs, heading, field names, text below fields, and pagination.
Correct, although self hosted installations do support changing options in checkbox/radio/dropdowns I felt it slightly overcomplicated the setup of the whole translation form JS supporting cloud and self hosted environments. The best way to accomplish this would be with a lookup rule right now.
It does look like Google Translate supports HTML markup so that might be an easy addition to this solution
Anyone else notice it won't translate the "Text Above Field"? I thought it was the description label so I added it but I don't think the code accommodates this for most field types.
Wow, I'm not really sure how I missed that. I don't have a good way to test it, but it does look like its missing in my code.
If you search for the comment "// #region Field setttings" you will see an object named basicTranslatableFieldSettings if you had a key/value of description: "description" just like the rest of the key/value pairs and re-run your translation it should just work
Does this capture the placeholder text somewhere?
label: 'label',
subtext: 'subtext',
options: 'options',
description: 'description'
tooltip: 'tooltip',
default: 'default',
signButtonLabel: 'signButtonLabel',
rowLabels: 'rowLabels',
prevBtn: 'prevBtn',
nextBtn: 'nextBtn',
No I might have missed that one too. if you add
placeholder: 'placeholder,
Like the others it should work
This also appears to need to be added.
error
Should error be in basicTranslatableFieldSettings or otherTranslatableFieldSettings?
error isn't accessible by the LFForm object so we can't target it unfortunatey
Is this still working for people today? I just tried to run it on a form that I had it working on but had to make some updates and it's not translating to spanish anymore. The spanish version just has english text where the spanish text should be.
This is the most disappointing thing ever! We JUST completed our upgrade and the VERY FIRST thing I did was run to add google translate to our new forms designer forms (especially my residential utility service application we have two college/universities with MANY international students).
Not being able to very quickly and easily add language support for my customers in any language they choose is a real bummer.
I will be going through the videos to see what will be involved in trying to provide spanish support and do appreciate some kind of limited work around.
The JS I wrote to support translation might look scary at first because it's a decent amount of code. The only thing you need to touch is at the top of the code!
Okay. So I have figured out how to make the sample file work and understand how I could build from the template to get english/spanish support, but what is required to apply this to an existing form?
I tried recreating the selection field, custom HTML, CSS, and javascript from the form, but it doesn't seem to be able to generate the info. ALSO.... when I go to do this, does it see hidden fields and pages? :) I know this isn't a supported thing, but I just was hoping for a bit more info.
(And... it is very slick!)
OMG. Disregard. USER ERROR.
Well, no, it wasn't. I can't get the google translate button to work.
Do you have the API Key added to the JS?
I do. Here is everything I have set up on my existing form:
Buttons:
<div class="btn-container">
<button class="btn btn-default" onclick="getFieldtranslations()" title="Generate translations with Google">
Google Translate
</button>
<button class="btn btn-default" onclick="getFieldtranslations(true)" title="Generate an empty translation JSON">
Custom Translation
</button>
</div>
Added to CSS:
/* DO NOT REMOVE. REQUIRED FOR GOOGLE SUPPORT! */
.red-description fl-helptext > label {
color: red;
}
.btn-container {
display: flex;
align-items: center;
justify-content: space-around;
height: 100%;
}
.translate-custom-html > fl-custom-html > div {
height: 100%;
}
Key (along with all other JS in the template:
Are the field IDs for the translateCustomHtmlFieldId and translateLangFieldId correct? Your screenshot shows the default IDs, so you may need to change those.
OMG. That is ABSOLUTELY what is wrong and why I shouldn't try to work things while dealing with other things. I totally missed that I need to reference the object ID of those two fields which CERTAINLY are not 1 and 2. I am going to go try that.
So, I have now hit a brick wall. I have forms that have a LOT of text. It hits the Google translate character limit. Anyone hit that and have modified the code so it sends it in blocks? I can get the code to manage it and get me everything except the HTML, but the HTML text is the kicker for me. It is stuff like my service applications and a couple of service requests that require some detailed explanation to ensure that you qualify and/or understand the expectations.
STILL super thankful for this code for the smaller stuff... It just isn't usually our highest volume stuff. I had a bilingual who misunderstood a form and signed up for a $300+ monthly recurring donation to our social service agency when they were actually seeking assistance.
Well thats certainly a first. How many characters are you trying to translate? I haven't added any code to handle rate limits on the API side. I could easily add code to split text fire off a bunch of requests and then join them back, but there are rate limit of 300k characters per minute which I haven't accounted for in my code.
There is a new v3 of the API that has a higher character limit, but lower free tier and more complex setup/authentication on the code side. I'll have to play with it and see how easy it is to setup. I'm pretty busy until after empower unfortunately, but poke me back next month (if you can wait that long) and I'll take a look at a new update to the code.
That would be AMAZING! I am assuming that I am hitting a character limit because the code returned about 112K characters and hadn't gotten past all the English. We are a full service utility (water, wastewater, solid waste, and electric) and it is my residential service application. There is a lot of hidden logic so it can validate service date options, services at the location, deposit estimates... that kind of thing. It also has a lot of comparison information on our prepaid and postpaid electric programs.
I have the same kind of situation with our residential solid waste self-service form. It has a LOT of information and covers three different types of solid waste requests.
Well I had my agent make a fix. I tested it doesn't regress but I don't know if it will work for your large form. There is a function `getTranslation` replace the one function with the following two functions:
/**
* Splits text into chunks for translation while preserving gap information.
* @param {string} text - The long string to split.
* @param {number} limit - Character limit (5000 is recommended).
* @returns {Array} Array with alternating chunks and gaps between them.
*/
function splitForTranslate(text, limit = 5e3) {
const words = text.split(/(\s+)/);
const chunks = [];
let currentChunk = "";
words.forEach((word) => {
if (currentChunk.length + word.length > limit && currentChunk.length > 0) {
chunks.push(currentChunk);
currentChunk = word;
} else currentChunk += word;
});
if (currentChunk.length > 0) chunks.push(currentChunk);
return chunks;
}
/**
* Asynchronously translates text to the target language using Google Translate API and returns a Promise.
* If translation fails, the original text is returned as a fallback.
* @param {string} text - The text to translate.
* @param {string} targetLang - The target language code (e.g., 'en', 'es').
* @param {number} splitTextCharacterLimit - Character limit for splitting text (default is 5000).
* @returns {Promise<string>} A Promise that resolves to the translated text, or the original text if translation fails.
* @description Translate text to target language. This should be removed from your code when a static translation is saved to the translations variable.
*/
async function getTranslation(text, targetLang, splitTextCharacterLimit = 5e3) {
const sourceLang = defaultFormLang;
const url = `https://translation.googleapis.com/language/translate/v2?key=${GOOGLE_TRANSLATE_API_KEY}`;
const charLimit = splitTextCharacterLimit > 5e3 || splitTextCharacterLimit <= 0 ? 5e3 : splitTextCharacterLimit;
const textToTranslate = text.length > charLimit ? splitForTranslate(text, charLimit) : [text];
try {
return (await Promise.all(textToTranslate.map((chunk) => {
const translateChunk = async (retryCount = 0) => {
try {
const data = await (await fetch(url, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
q: chunk,
source: sourceLang,
target: targetLang
})
})).json();
if (!data || !data.data || !data.data.translations || !data.data.translations[0]) throw new Error("Translation API did not return expected data");
return data.data.translations[0].translatedText;
} catch (error) {
if (retryCount >= 3) {
console.error(`Failed to translate chunk after 3 retries:`, error);
throw new Error("Failed to translate chunk");
}
console.error(`Error translating chunk (retry ${retryCount}):`, error);
const delay = 1e3 * Math.pow(2, retryCount);
return new Promise((resolve, reject) => {
setTimeout(() => {
translateChunk(retryCount + 1).then(resolve).catch(reject);
}, delay);
});
}
};
return translateChunk();
}))).join("");
} catch (error) {
console.error("Translation failed:", error);
return text;
}
}
THIS WORKED LIKE A CHARM!!!! You just made my life (and the lives of my 82K customers MUCH BETTER! :)
Awesome! Sounds like I need to publish a v3 of this to consolidate all of the smaller fixes I've made across this page