翻译 Google 文档、表格和幻灯片中的文字

编码级别:中级
时长:30 分钟
项目类型:Google Workspace 插件

目标

  • 了解该解决方案的用途。
  • 了解 Apps Script 服务在解决方案中的作用。
  • 设置环境。
  • 设置脚本。
  • 运行脚本。

关于此解决方案

借助此解决方案,您可以在 Google 文档、表格和幻灯片中轻松翻译文本。

Google Workspace 插件“翻译”的屏幕截图

运作方式

当您在 Google 文档、表格或幻灯片中选择文本,然后点击该插件中的获取所选内容时,脚本会将文本复制到该插件中,进行翻译,并显示译文。

默认情况下,该脚本会检测源语言并将文本翻译成英语。您可以修改源语言和目标语言。

Apps 脚本服务

此解决方案使用以下服务:

前提条件

如需使用此示例,您需要满足以下前提条件:

  • Google 账号(Google Workspace 账号可能需要管理员批准)。
  • 一个能够访问互联网的网络浏览器。

  • 具有一个 Google Cloud 项目

设置环境

在 Google Cloud 控制台中打开您的 Cloud 项目

如果尚未打开,请打开您打算用于此示例的 Cloud 项目:

  1. 在 Google Cloud 控制台中,前往选择项目页面。

    选择 Cloud 项目

  2. 选择您要使用的 Google Cloud 项目。或者,点击创建项目,然后按照屏幕上的说明操作。如果您创建 Google Cloud 项目,则可能需要为项目启用结算功能

Google Workspace 插件需要配置意见征求界面。配置插件 OAuth 权限请求页面可定义 Google 向用户显示的内容。

  1. 在 Google Cloud 控制台中,依次点击“菜单”图标 > > 品牌

    前往“品牌”页面

  2. 如果您已配置 ,则可以在品牌受众群体数据访问中配置以下 OAuth 同意屏幕设置。如果您看到一条消息,其中显示 尚未配置,请点击开始
    1. 应用信息下的应用名称中,输入应用的名称。
    2. 用户支持电子邮件中,选择一个支持电子邮件地址,以便用户在对其同意问题有疑问时与您联系。
    3. 点击下一步
    4. 观众下,选择内部
    5. 点击下一步
    6. 联系信息下,输入一个电子邮件地址,以便您接收有关项目的任何更改的通知。
    7. 点击下一步
    8. 完成下方,查看 Google API 服务用户数据政策,如果您同意,请选择我同意 Google API 服务:用户数据政策
    9. 点击继续
    10. 点击创建
  3. 目前,您可以跳过添加镜重。 今后,如果您创建的应用供 Google Workspace 组织之外的用户使用,则必须将用户类型更改为外部。然后,添加您的应用所需的授权范围。如需了解详情,请参阅完整的配置 OAuth 同意指南。

设置脚本

创建 Apps 脚本项目

  1. 点击以下按钮打开 Translate Apps 脚本项目。
    打开项目

  2. 点击概览

  3. 在概览页面上,点击“复制”图标 用于创建副本的图标

复制 Cloud 项目编号

  1. 在 Google Cloud 控制台中,依次选择“菜单”图标 > IAM 和管理 > 设置

    前往“IAM 和管理”设置

  2. 项目编号字段中,复制相应值。

设置 Apps 脚本项目的 Cloud 项目

  1. 在复制的 Apps 脚本项目中,点击项目设置 项目设置的图标
  2. Google Cloud Platform (GCP) Project(Google Cloud Platform [GCP] 项目)下,点击 Change project(更改项目)。
  3. GCP project number(GCP 项目编号)中,粘贴 Google Cloud 项目编号。
  4. 点击设置项目

安装测试部署

  1. 在复制的 Apps 脚本项目中,点击编辑器图标
  2. 打开 Code.gs 文件,然后点击运行。根据提示为脚本授权。
  3. 依次点击 Deploy > Test deployments
  4. 依次点击安装 > 完成

运行脚本

  1. Google 文档表格幻灯片中打开文件,或创建新文件。
  2. 在右侧边栏中,打开“翻译”插件
  3. 如果系统提示,请为该插件授权。
  4. 选择文件中的文本。
  5. 在该插件中,依次点击获取所选内容 > 翻译

查看代码

如需查看此解决方案的 Apps 脚本代码,请点击下方的查看源代码

查看源代码

Code.gs

const DEFAULT_INPUT_TEXT = ''; const DEFAULT_OUTPUT_TEXT = ''; const DEFAULT_ORIGIN_LAN = ''; // Empty string means detect langauge const DEFAULT_DESTINATION_LAN = 'en' // English  const LANGUAGE_MAP =   [     { text: 'Detect Language', val: '' },     { text: 'Afrikaans', val: 'af' },     { text: 'Albanian', val: 'sq' },     { text: 'Amharic', val: 'am' },     { text: 'Arabic', val: 'ar' },     { text: 'Armenian', val: 'hy' },     { text: 'Azerbaijani', val: 'az' },     { text: 'Basque', val: 'eu' },     { text: 'Belarusian', val: 'be' },     { text: 'Bengali', val: 'bn' },     { text: 'Bosnian', val: 'bs' },     { text: 'Bulgarian', val: 'bg' },     { text: 'Catalan', val: 'ca' },     { text: 'Cebuano', val: 'ceb' },     { text: 'Chinese (Simplified)', val: 'zh-CN' },     { text: 'Chinese (Traditional)', val: 'zh-TW' },     { text: 'Corsican', val: 'co' },     { text: 'Croatian', val: 'hr' },     { text: 'Czech', val: 'cs' },     { text: 'Danish', val: 'da' },     { text: 'Dutch', val: 'nl' },     { text: 'English', val: 'en' },     { text: 'Esperanto', val: 'eo' },     { text: 'Estonian', val: 'et' },     { text: 'Finnish', val: 'fi' },     { text: 'French', val: 'fr' },     { text: 'Frisian', val: 'fy' },     { text: 'Galician', val: 'gl' },     { text: 'Georgian', val: 'ka' },     { text: 'German', val: 'de' },     { text: 'Greek', val: 'el' },     { text: 'Gujarati', val: 'gu' },     { text: 'Haitian Creole', val: 'ht' },     { text: 'Hausa', val: 'ha' },     { text: 'Hawaiian', val: 'haw' },     { text: 'Hebrew', val: 'he' },     { text: 'Hindi', val: 'hi' },     { text: 'Hmong', val: 'hmn' },     { text: 'Hungarian', val: 'hu' },     { text: 'Icelandic', val: 'is' },     { text: 'Igbo', val: 'ig' },     { text: 'Indonesian', val: 'id' },     { text: 'Irish', val: 'ga' },     { text: 'Italian', val: 'it' },     { text: 'Japanese', val: 'ja' },     { text: 'Javanese', val: 'jv' },     { text: 'Kannada', val: 'kn' },     { text: 'Kazakh', val: 'kk' },     { text: 'Khmer', val: 'km' },     { text: 'Korean', val: 'ko' },     { text: 'Kurdish', val: 'ku' },     { text: 'Kyrgyz', val: 'ky' },     { text: 'Lao', val: 'lo' },     { text: 'Latin', val: 'la' },     { text: 'Latvian', val: 'lv' },     { text: 'Lithuanian', val: 'lt' },     { text: 'Luxembourgish', val: 'lb' },     { text: 'Macedonian', val: 'mk' },     { text: 'Malagasy', val: 'mg' },     { text: 'Malay', val: 'ms' },     { text: 'Malayalam', val: 'ml' },     { text: 'Maltese', val: 'mt' },     { text: 'Maori', val: 'mi' },     { text: 'Marathi', val: 'mr' },     { text: 'Mongolian', val: 'mn' },     { text: 'Myanmar (Burmese)', val: 'my' },     { text: 'Nepali', val: 'ne' },     { text: 'Norwegian', val: 'no' },     { text: 'Nyanja (Chichewa)', val: 'ny' },     { text: 'Pashto', val: 'ps' },     { text: 'Persian', val: 'fa' },     { text: 'Polish', val: 'pl' },     { text: 'Portuguese (Portugal, Brazil)', val: 'pt' },     { text: 'Punjabi', val: 'pa' },     { text: 'Romanian', val: 'ro' },     { text: 'Russian', val: 'ru' },     { text: 'Samoan', val: 'sm' },     { text: 'Scots Gaelic', val: 'gd' },     { text: 'Serbian', val: 'sr' },     { text: 'Sesotho', val: 'st' },     { text: 'Shona', val: 'sn' },     { text: 'Sindhi', val: 'sd' },     { text: 'Sinhala (Sinhalese)', val: 'si' },     { text: 'Slovak', val: 'sk' },     { text: 'Slovenian', val: 'sl' },     { text: 'Somali', val: 'so' },     { text: 'Spanish', val: 'es' },     { text: 'Sundanese', val: 'su' },     { text: 'Swahili', val: 'sw' },     { text: 'Swedish', val: 'sv' },     { text: 'Tagalog (Filipino)', val: 'tl' },     { text: 'Tajik', val: 'tg' },     { text: 'Tamil', val: 'ta' },     { text: 'Telugu', val: 'te' },     { text: 'Thai', val: 'th' },     { text: 'Turkish', val: 'tr' },     { text: 'Ukrainian', val: 'uk' },     { text: 'Urdu', val: 'ur' },     { text: 'Uzbek', val: 'uz' },     { text: 'Vietnamese', val: 'vi' },     { text: 'Welsh', val: 'cy' },     { text: 'Xhosa', val: 'xh' },     { text: 'Yiddish', val: 'yi' },     { text: 'Yoruba', val: 'yo' },     { text: 'Zulu', val: 'zu' }   ];   /**  * Callback for rendering the main card.  * @return {CardService.Card} The card to show the user.  */ function onHomepage(e) {   return createSelectionCard(e, DEFAULT_ORIGIN_LAN, DEFAULT_DESTINATION_LAN, DEFAULT_INPUT_TEXT, DEFAULT_OUTPUT_TEXT); }  /**  * Main function to generate the main card.  * @param {String} originLanguage Language of the original text.  * @param {String} destinationLanguage Language of the translation.  * @param {String} inputText The text to be translated.  * @param {String} outputText The text translated.  * @return {CardService.Card} The card to show to the user.  */ function createSelectionCard(e, originLanguage, destinationLanguage, inputText, outputText) {   var hostApp = e['hostApp'];   var builder = CardService.newCardBuilder();    // "From" language selection & text input section   var fromSection = CardService.newCardSection()     .addWidget(generateLanguagesDropdown('origin', 'From: ', originLanguage))     .addWidget(CardService.newTextInput()       .setFieldName('input')       .setValue(inputText)       .setTitle('Enter text...')       .setMultiline(true));    if (hostApp === 'docs') {     fromSection.addWidget(CardService.newButtonSet()       .addButton(CardService.newTextButton()         .setText('Get Selection')         .setOnClickAction(CardService.newAction().setFunctionName('getDocsSelection'))         .setDisabled(false)))   } else if (hostApp === 'sheets') {     fromSection.addWidget(CardService.newButtonSet()       .addButton(CardService.newTextButton()         .setText('Get Selection')         .setOnClickAction(CardService.newAction().setFunctionName('getSheetsSelection'))         .setDisabled(false)))   } else if (hostApp === 'slides') {     fromSection.addWidget(CardService.newButtonSet()       .addButton(CardService.newTextButton()         .setText('Get Selection')         .setOnClickAction(CardService.newAction().setFunctionName('getSlidesSelection'))         .setDisabled(false)))   }     builder.addSection(fromSection);    // "Translation" language selection & text input section   builder.addSection(CardService.newCardSection()     .addWidget(generateLanguagesDropdown('destination', 'To: ', destinationLanguage))     .addWidget(CardService.newTextInput()       .setFieldName('output')       .setValue(outputText)       .setTitle('Translation...')       .setMultiline(true)));    //Buttons section   builder.addSection(CardService.newCardSection()     .addWidget(CardService.newButtonSet()       .addButton(CardService.newTextButton()         .setText('Translate')         .setTextButtonStyle(CardService.TextButtonStyle.FILLED)         .setOnClickAction(CardService.newAction().setFunctionName('translateText'))         .setDisabled(false))       .addButton(CardService.newTextButton()         .setText('Clear')         .setOnClickAction(CardService.newAction().setFunctionName('clearText'))         .setDisabled(false))));    return builder.build();  }  /**  * Helper function to generate the drop down language menu. It checks what language the user had selected.  * @param {String} fieldName  * @param {String} fieldTitle  * @param {String} previousSelected The language the user previously had selected.  * @return {CardService.SelectionInput} The card to show to the user.  */ function generateLanguagesDropdown(fieldName, fieldTitle, previousSelected) {   var selectionInput = CardService.newSelectionInput().setTitle(fieldTitle)     .setFieldName(fieldName)     .setType(CardService.SelectionInputType.DROPDOWN);    LANGUAGE_MAP.forEach((language, index, array) => {     selectionInput.addItem(language.text, language.val, language.val == previousSelected);   })    return selectionInput; }  /**  * Helper function to translate the text. If the originLanguage is an empty string, the API detects the language  * @return {CardService.Card} The card to show to the user.  */ function translateText(e) {   var originLanguage = e.formInput.origin;   var destinationLanguage = e.formInput.destination;   var inputText = e.formInput.input;    if (originLanguage !== destinationLanguage && inputText !== undefined) {     var translation = LanguageApp.translate(e.formInput.input, e.formInput.origin, e.formInput.destination);     return createSelectionCard(e, originLanguage, destinationLanguage, inputText, translation);   } }  /**  * Helper function to clean the text.  * @return {CardService.Card} The card to show to the user.  */ function clearText(e) {   var originLanguage = e.formInput.origin;   var destinationLanguage = e.formInput.destination;   return createSelectionCard(e, originLanguage, destinationLanguage, DEFAULT_INPUT_TEXT, DEFAULT_OUTPUT_TEXT); }  /**  * Helper function to get the text selected.  * @return {CardService.Card} The selected text.  */ function getDocsSelection(e) {   var text = '';   var selection = DocumentApp.getActiveDocument().getSelection();   Logger.log(selection)   if (selection) {     var elements = selection.getRangeElements();     for (var i = 0; i < elements.length; i++) {       Logger.log(elements[i]);       var element = elements[i];       // Only modify elements that can be edited as text; skip images and other non-text elements.       if (element.getElement().asText() && element.getElement().asText().getText() !== '') {         text += element.getElement().asText().getText() + '\n';       }     }   }    if (text !== '') {     var originLanguage = e.formInput.origin;     var destinationLanguage = e.formInput.destination;     var translation = LanguageApp.translate(text, e.formInput.origin, e.formInput.destination);     return createSelectionCard(e, originLanguage, destinationLanguage, text, translation);   } }  /**  * Helper function to get the text of the selected cells.  * @return {CardService.Card} The selected text.  */ function getSheetsSelection(e) {   var text = '';   var ranges = SpreadsheetApp.getActive().getSelection().getActiveRangeList().getRanges();   for (var i = 0; i < ranges.length; i++) {     const range = ranges[i];     const numRows = range.getNumRows();     const numCols = range.getNumColumns();     for (let i = 1; i <= numCols; i++) {       for (let j = 1; j <= numRows; j++) {         const cell = range.getCell(j, i);         if (cell.getValue()) {           text += cell.getValue() + '\n';         }       }     }   }   if (text !== '') {     var originLanguage = e.formInput.origin;     var destinationLanguage = e.formInput.destination;     var translation = LanguageApp.translate(text, e.formInput.origin, e.formInput.destination);     return createSelectionCard(e, originLanguage, destinationLanguage, text, translation);   } }  /**  * Helper function to get the selected text of the active slide.  * @return {CardService.Card} The selected text.  */ function getSlidesSelection(e) {   var text = '';   var selection = SlidesApp.getActivePresentation().getSelection();   var selectionType = selection.getSelectionType();   if (selectionType === SlidesApp.SelectionType.TEXT) {     var textRange = selection.getTextRange();     if (textRange.asString() !== '') {       text += textRange.asString() + '\n';     }   }   if (text !== '') {     var originLanguage = e.formInput.origin;     var destinationLanguage = e.formInput.destination;     var translation = LanguageApp.translate(text, e.formInput.origin, e.formInput.destination);     return createSelectionCard(e, originLanguage, destinationLanguage, text, translation);   } }

appsscript.json

{   "timeZone": "America/New_York",   "dependencies": {},   "exceptionLogging": "STACKDRIVER",   "oauthScopes": [     "https://www.googleapis.com/auth/documents.currentonly",     "https://www.googleapis.com/auth/spreadsheets.currentonly",     "https://www.googleapis.com/auth/presentations.currentonly"   ],   "runtimeVersion": "V8",   "addOns": {     "common": {       "name": "Translate",       "logoUrl": "https://www.gstatic.com/images/branding/product/1x/translate_24dp.png",       "layoutProperties": {         "primaryColor": "#2772ed"       },       "homepageTrigger": {         "runFunction": "onHomepage"       }     },     "docs" : {},     "slides" : {},     "sheets" : {}   } }

贡献者

此示例由 Google 维护,并由 Google 开发者专家提供帮助。

后续步骤