Skip to content

Conversation

@happygoluckycodeeditor
Copy link

Description

This contribution adds an enhanced translation feature for Google Slides that allows users to translate entire presentations with full control over source and target languages. Helps in quick translation of all slides.
The code is based on the already existing sample app.

Use-case: Usually used in office scenarios to translate lots of slides and its elements quickly in one-go.

New files added:

  • translateAllSlides.gs - Enhanced version with translateAllSlides() function to translate all slides at once
  • sidebarEnhanced.html - Improved UI with source language selection and expanded language support

Key improvements:

  • Added translateAllSlides() function to translate entire presentations in one click
  • Expanded language support from 9 to 30+ languages
  • Added source language selection with auto-detect option
  • Two translation modes: translate selected elements or all slides
  • Original translate.gs and sidebar.html remain unchanged (used in official documentation)

Fixes # (N/A - this is a new feature addition)

Is it been tested?

  • Development testing done

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have performed a peer-reviewed with team member(s)
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • Any dependent changes have been merged and published in downstream modules

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @happygoluckycodeeditor, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly upgrades the Google Slides translation add-on by introducing an "enhanced" version. The primary goal is to facilitate quick and comprehensive translation of entire presentations, particularly useful in professional settings. It achieves this by offering a new sidebar interface that allows users to select both source and target languages (with an auto-detect option for source) and provides the choice to translate either selected elements or all text content across all slides, supporting a much broader range of languages.

Highlights

  • Enhanced Translation Functionality: Introduces the ability to translate entire Google Slides presentations with a single click.
  • Source Language Selection: Adds an option for users to explicitly select the source language or utilize an auto-detect feature.
  • Expanded Language Support: Increases the number of supported translation languages from 9 to over 30.
  • Dual Translation Modes: Provides two distinct modes: translating only selected elements or translating all text across all slides.
  • New Files: Adds translateAllSlides.gs for backend logic and sidebarEnhanced.html for the new user interface.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces an enhanced translation feature for Google Slides, including the ability to translate all slides at once and select a source language. The implementation is a good step forward, but there are a few important issues to address. A critical security vulnerability exists due to an outdated version of jQuery. Additionally, the 'Translate Selected' feature has a functional bug where it doesn't use the source language selector, and a critical bug that can cause the script to crash when translating text selected inside a table. My review includes suggestions to fix these issues.

<span class="gray branding-text">Translate sample by Google</span>
</div>

<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The version of jQuery being used (1.9.1) is outdated and has known security vulnerabilities, such as Cross-Site Scripting (XSS) (e.g., CVE-2020-11022, CVE-2015-9251). It is highly recommended to upgrade to a recent version of jQuery (e.g., 3.7.1) to mitigate these security risks.

Suggested change
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>

Comment on lines 88 to 125
function translateSelectedElements(targetLanguage) {
// Get selected elements.
const selection = SlidesApp.getActivePresentation().getSelection();
const selectionType = selection.getSelectionType();
/** @type {GoogleAppsScript.Slides.TextRange[]} */
let texts = [];
switch (selectionType) {
case SlidesApp.SelectionType.PAGE:
for (const page of selection.getPageRange().getPages()) {
texts = texts.concat(getElementTexts(page.getPageElements()));
}
break;
case SlidesApp.SelectionType.PAGE_ELEMENT: {
const pageElements = selection.getPageElementRange().getPageElements();
texts = texts.concat(getElementTexts(pageElements));
break;
}
case SlidesApp.SelectionType.TABLE_CELL:
for (const cell of selection.getTableCellRange().getTableCells()) {
texts.push(cell.getText());
}
break;
case SlidesApp.SelectionType.TEXT:
for (const element of selection.getPageElementRange().getPageElements()) {
texts.push(element.asShape().getText());
}
break;
}

// Translate all elements in-place.
for (const text of texts) {
text.setText(
LanguageApp.translate(text.asRenderedString(), "", targetLanguage),
);
}

return texts.length;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The translateSelectedElements function has two issues:

  1. High: It does not accept a source language parameter, forcing it to use auto-detection. This is inconsistent with the new UI which allows source language selection.
  2. Critical: The code handling SelectionType.TEXT assumes the selected text is always within a Shape. If a user selects text within a table cell, element.asShape() will throw an error, crashing the script.

The suggested code below fixes both issues.

function translateSelectedElements(sourceLanguage, targetLanguage) {
  // Get selected elements.
  const selection = SlidesApp.getActivePresentation().getSelection();
  const selectionType = selection.getSelectionType();
  /** @type {GoogleAppsScript.Slides.TextRange[]} */
  let texts = [];
  switch (selectionType) {
    case SlidesApp.SelectionType.PAGE:
      for (const page of selection.getPageRange().getPages()) {
        texts = texts.concat(getElementTexts(page.getPageElements()));
      }
      break;
    case SlidesApp.SelectionType.PAGE_ELEMENT: {
      const pageElements = selection.getPageElementRange().getPageElements();
      texts = texts.concat(getElementTexts(pageElements));
      break;
    }
    case SlidesApp.SelectionType.TABLE_CELL:
      for (const cell of selection.getTableCellRange().getTableCells()) {
        texts.push(cell.getText());
      }
      break;
    case SlidesApp.SelectionType.TEXT:
      for (const element of selection.getPageElementRange().getPageElements()) {
        if (element.getPageElementType() == SlidesApp.PageElementType.SHAPE) {
          texts.push(element.asShape().getText());
        }
      }
      break;
  }

  // Translate all elements in-place.
  for (const text of texts) {
    text.setText(
      LanguageApp.translate(text.asRenderedString(), sourceLanguage, targetLanguage),
    );
  }

  return texts.length;
}

Comment on lines 124 to 148
function runTranslation() {
this.disabled = true;
$('#error').text('');
$('#success').text('');
google.script.run
.withSuccessHandler((numTranslatedElements, element) =>{
element.disabled = false;
if (numTranslatedElements === 0) {
$('#error').empty()
.append('Did you select elements to translate?')
.append('<br/>')
.append('Please select slides or individual elements.');
} else {
$('#success').text('Successfully translated ' + numTranslatedElements + ' element(s)!');
}
return false;
})
.withFailureHandler((msg, element)=> {
element.disabled = false;
$('#error').text('Something went wrong. Please check the add-on logs.');
return false;
})
.withUserObject(this)
.translateSelectedElements($('input[name=dest]:checked').val());
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The runTranslation function for 'Translate Selected' does not use the selected source language from the UI. It calls translateSelectedElements with only the target language, which causes the backend to always use auto-detect for the source language. This is inconsistent with the 'Translate All Slides' functionality and doesn't use the full capability of the new UI. The function should be updated to retrieve and pass the selected source language.

  function runTranslation() {
    this.disabled = true;
    $('#error').text('');
    $('#success').text('');
    const sourceLanguage = $('input[name=source]:checked').val();
    const targetLanguage = $('input[name=dest]:checked').val();
    google.script.run
            .withSuccessHandler((numTranslatedElements, element) =>{
              element.disabled = false;
              if (numTranslatedElements === 0) {
                $('#error').empty()
                        .append('Did you select elements to translate?')
                        .append('<br/>')
                        .append('Please select slides or individual elements.');
              } else {
                $('#success').text('Successfully translated ' + numTranslatedElements + ' element(s)!');
              }
              return false;
            })
            .withFailureHandler((msg, element)=> {
              element.disabled = false;
              $('#error').text('Something went wrong. Please check the add-on logs.');
              return false;
            })
            .withUserObject(this)
            .translateSelectedElements(sourceLanguage, targetLanguage);
  }

@happygoluckycodeeditor
Copy link
Author

Working on the feedback! Thank you very much! Be right back! :D

@happygoluckycodeeditor
Copy link
Author

happygoluckycodeeditor commented Nov 27, 2025

@gemini-code-assist I have added/made the necessary changes. Can you have a look?

Also removed the Auto-detect thing because it does not serve much purpose when the person doing the translate already knows the content of the slide and is just trying to change it to another language.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces an excellent new feature for translating entire presentations and selecting source languages. The implementation is solid, but I've identified a few high-priority issues that should be addressed. These include improving user feedback on errors, ensuring text selection translation behaves as expected, and optimizing the translation process by batching API calls to enhance performance and avoid hitting service quotas. The suggested changes will make the feature more robust and user-friendly.

Comment on lines +142 to +146
.withFailureHandler((msg, element)=> {
element.disabled = false;
$('#error').text('Something went wrong. Please check the add-on logs.');
return false;
})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The failure handler for runTranslation currently shows a generic error message and ignores the specific error details (msg) returned from the server. This makes it difficult for users to understand what went wrong. To improve user experience, you should display the msg content in the error message, similar to how it's done in runAllTranslation's failure handler.

Suggested change
.withFailureHandler((msg, element)=> {
element.disabled = false;
$('#error').text('Something went wrong. Please check the add-on logs.');
return false;
})
.withFailureHandler((msg, element)=> {
element.disabled = false;
$('#error').text('Something went wrong: ' + msg);
return false;
})

Comment on lines +121 to +131
for (const text of texts) {
text.setText(
LanguageApp.translate(
text.asRenderedString(),
sourceLanguage,
targetLanguage,
),
);
}

return texts.length;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This function makes individual calls to LanguageApp.translate in a loop. This is inefficient and can lead to quota issues, especially when many elements are selected. It's much better to batch these calls. You can do this by collecting all text strings into an array, translating them with a single API call, and then updating the elements. This also allows you to return a more accurate count of translated elements.

  // Translate all elements in-place.
  const textsToTranslate = [];
  const originalStrings = [];
  for (const text of texts) {
    const originalText = text.asRenderedString();
    if (originalText.trim().length > 0) {
      textsToTranslate.push(text);
      originalStrings.push(originalText);
    }
  }

  if (originalStrings.length > 0) {
    const translatedStrings = LanguageApp.translate(
      originalStrings,
      sourceLanguage,
      targetLanguage,
    );
    for (let i = 0; i < textsToTranslate.length; i++) {
      textsToTranslate[i].setText(translatedStrings[i]);
    }
  }

  return textsToTranslate.length;

Comment on lines +141 to +169
function translateAllSlides(sourceLanguage, targetLanguage) {
// Get the active presentation and all slides
const presentation = SlidesApp.getActivePresentation();
const slides = presentation.getSlides();

let totalTranslated = 0;

// Loop through each slide
for (const slide of slides) {
// Get all text-containing elements from the slide
const texts = getElementTexts(slide.getPageElements());

// Translate all text elements in-place
for (const text of texts) {
const originalText = text.asRenderedString();
if (originalText.trim().length > 0) {
const translatedText = LanguageApp.translate(
originalText,
sourceLanguage,
targetLanguage,
);
text.setText(translatedText);
totalTranslated++;
}
}
}

return totalTranslated;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The translateAllSlides function calls LanguageApp.translate for each text element individually. For presentations with many text elements, this is inefficient and can easily exceed Apps Script quotas for service calls. To improve performance and avoid quota issues, you should batch these calls. Collect all text strings into an array, make a single call to LanguageApp.translate, and then iterate through the results to update the text elements.

function translateAllSlides(sourceLanguage, targetLanguage) {
  // Get the active presentation and all slides
  const presentation = SlidesApp.getActivePresentation();
  const slides = presentation.getSlides();

  const textsToTranslate = [];
  const originalStrings = [];

  // Loop through each slide to gather all text elements
  for (const slide of slides) {
    const texts = getElementTexts(slide.getPageElements());
    for (const text of texts) {
      const originalText = text.asRenderedString();
      if (originalText.trim().length > 0) {
        textsToTranslate.push(text);
        originalStrings.push(originalText);
      }
    }
  }

  if (originalStrings.length === 0) {
    return 0;
  }

  // Translate all strings in a single batch call
  const translatedStrings = LanguageApp.translate(originalStrings, sourceLanguage, targetLanguage);

  // Update all text elements with translated content
  for (let i = 0; i < textsToTranslate.length; i++) {
    textsToTranslate[i].setText(translatedStrings[i]);
  }

  return textsToTranslate.length;
}

@happygoluckycodeeditor
Copy link
Author

happygoluckycodeeditor commented Nov 27, 2025

Thanks for the feedback. I am not sure if all of the suggestions are worth it.
I ran the script on a 50 page Japanese Presentation with lots of text and the script did not time-out or went over the quota or give that sort of an error :D

  • I added a function which ignores images (it was throwing errors if the shapes were images).
    The code is now properly updated. Hope that will be it!

P.S. Tested and reviewed by peers as well

image

@happygoluckycodeeditor
Copy link
Author

@jpoehnelt
Hello!
I made a small contribution and would love your feedback whenever you have time.
Totally understand if you're busy — thanks in advance~

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants