Google翻訳を利用して選択したテキストを他の言語に変換するGoogle Docsのアドオンを作成します。
上図のようにサイドバーとしてテキストを翻訳する機能を追加します。
準備
新しいGoogleドキュメントを作成します。
新しい文書の中から、メニュー項目の[ツール]> [スクリプトエディタ]を選択します。ようこそ画面が表示されたら、[空のプロジェクト]をクリックします。
スクリプトエディタでコードを削除し、下のコードに貼り付けます。
/**
* @OnlyCurrentDoc
*
*/
/**
* ドキュメントを開いたときに、GoogleドキュメントのUIにメニューを作成します。
* このメソッドは通常のアドオンでのみ使用され、モバイルでは呼び出されません。
*
* @param {object} e 単純な onOpen トリガーのイベントパラメーターです。
* トリガーが実行されている認証モード(ScriptApp.AuthMode)を確認するには、
* e.authModeを調べます。
*/
function onOpen(e) {
DocumentApp.getUi().createAddonMenu()
.addItem('Start', 'showSidebar')
.addToUi();
}
/**
* アドオンがインストールされているときに実行されます。
* モバイルでは動きません。
*
* @param {object} e 単純なonInstallトリガのイベントパラメータです。
* トリガーが実行されている認証モード(ScriptApp.AuthMode)を
* 確認するには、e.authModeを調べます。(実際には、onInstall
* トリガーは常にAuthMode.FULLで実行されますが、onOpenトリガーは
* AuthMode.LIMITEDまたはAuthMode.NONEです。)
*/
function onInstall(e) {
onOpen(e);
}
/**
* サイドバーを開きます。
* モバイルでは動きません。
*/
function showSidebar() {
var ui = HtmlService.createHtmlOutputFromFile('Sidebar')
.setTitle('Translate');
DocumentApp.getUi().showSidebar(ui);
}
/**
* ユーザーが選択したテキストを取得します。
* 選択がない場合は、エラーメッセージを表示します。
*
* @return {Array.} 選択されたテキスト
*/
function getSelectedText() {
var selection = DocumentApp.getActiveDocument().getSelection();
if (selection) {
var text = [];
var elements = selection.getSelectedElements();
for (var i = 0; i < elements.length; i++) {
if (elements[i].isPartial()) {
var element = elements[i].getElement().asText();
var startIndex = elements[i].getStartOffset();
var endIndex = elements[i].getEndOffsetInclusive();
text.push(element.getText().substring(startIndex, endIndex + 1));
} else {
var element = elements[i].getElement();
// テキストとして編集できる要素のみを翻訳します。
// 画像やその他のテキスト以外の要素をスキップします。
if (element.editAsText) {
var elementText = element.asText().getText();
// このチェックは、画像を除外して空のテキスト要素を返すために必要です。
if (elementText != '') {
text.push(elementText);
}
}
}
}
if (text.length == 0) {
throw 'Please select some text.';
}
return text;
} else {
throw 'Please select some text.';
}
}
/**
* 設定されているオリジナルの言語と目的の言語(翻訳後)を取得します。
* モバイルでは動きません。
*
* @return {Object} オリジナルの言語と目的の言語
*/
function getPreferences() {
var userProperties = PropertiesService.getUserProperties();
var languagePrefs = {
originLang: userProperties.getProperty('originLang'),
destLang: userProperties.getProperty('destLang')
};
return languagePrefs;
}
/**
* ユーザーが選択したテキストを取得し、オリジナルの言語から目的の言語
* (翻訳後)に変換します。言語は2文字の短い形式で表記されています。
* たとえば、英語は「en」、スペイン語は「es」です。
* オリジナルの言語は、Google Translateが自動的にその言語を検出する
* ことを示す空の文字列として指定することができます。
*
* @param {string} origin オリジナルの言語の2文字の短縮形
* @param {string} dest 目的の言語の2文字の短縮形
* @param {boolean} savePrefs オリジナルの言語と目的の言語を保存するかどうか
* @return {Object} 元のテキストと翻訳後のテキストを含むオブジェクト
*/
function getTextAndTranslation(origin, dest, savePrefs) {
var result = {};
var text = getSelectedText();
result['text'] = text.join('\n');
if (savePrefs == true) {
var userProperties = PropertiesService.getUserProperties();
userProperties.setProperty('originLang', origin);
userProperties.setProperty('destLang', dest);
}
result['translation'] = translateText(result['text'], origin, dest);
return result;
}
/**
* 現在の選択範囲のテキストを指定されたテキストに置き換えるか、
* 現在のカーソル位置にテキストを挿入します。
* 複数の要素が選択されている場合は、テキストを含むことができる
* 最初の要素に翻訳されたテキストを挿入し、他の要素を削除します。
*
* @param {string} newText 現在の選択を置き換えるテキスト
*/
function insertText(newText) {
var selection = DocumentApp.getActiveDocument().getSelection();
if (selection) {
var replaced = false;
var elements = selection.getSelectedElements();
if (elements.length == 1 &&
elements[0].getElement().getType() ==
DocumentApp.ElementType.INLINE_IMAGE) {
throw "Can't insert text into an image.";
}
for (var i = 0; i < elements.length; i++) {
if (elements[i].isPartial()) {
var element = elements[i].getElement().asText();
var startIndex = elements[i].getStartOffset();
var endIndex = elements[i].getEndOffsetInclusive();
var remainingText = element.getText().substring(endIndex + 1);
element.deleteText(startIndex, endIndex);
if (!replaced) {
element.insertText(startIndex, newText);
replaced = true;
} else {
// このブロックは、部分的な要素で終わる選択を処理します。
// この部分テキストを前の要素にコピーして、最後の部分的な
// 部分の前に改行がないようにします。
var parent = element.getParent();
parent.getPreviousSibling().asText().appendText(remainingText);
// ドキュメントの最後の段落は削除できません。
// この場合は、代わりに最後の段落内のテキストを
// 削除してください。
if (parent.getNextSibling()) {
parent.removeFromParent();
} else {
element.removeFromParent();
}
}
} else {
var element = elements[i].getElement();
if (!replaced && element.editAsText) {
// テキストとして編集できる要素のみを翻訳し、
// 他の要素は削除しないでください。
element.clear();
element.asText().setText(newText);
replaced = true;
} else {
// ドキュメントの最後の段落は削除できません。
// このような場合は、要素をクリアしてください。
if (element.getNextSibling()) {
element.removeFromParent();
} else {
element.clear();
}
}
}
}
} else {
var cursor = DocumentApp.getActiveDocument().getCursor();
var surroundingText = cursor.getSurroundingText().getText();
var surroundingTextOffset = cursor.getSurroundingTextOffset();
// カーソルがスペース以外の文字の前または後にある場合は、
// 文字と翻訳の間にスペースを挿入します。
// それ以外の場合は、翻訳を挿入するだけです。
if (surroundingTextOffset > 0) {
if (surroundingText.charAt(surroundingTextOffset - 1) != ' ') {
newText = ' ' + newText;
}
}
if (surroundingTextOffset < surroundingText.length) {
if (surroundingText.charAt(surroundingTextOffset) != ' ') {
newText += ' ';
}
}
cursor.insertText(newText);
}
}
/**
* Google翻訳を使って翻訳をします。
* 言語は2文字の短い形式で表記されています。
* たとえば、英語は「en」、スペイン語は「es」です。
* オリジナル言語は、Google Translateが自動的にその言語を検出することを示す
* 空の文字列として指定することができます。
*
* @param {string} text 翻訳するテキスト
* @param {string} origin オリジナルの言語の2文字の短縮形
* @param {string} dest 目的の言語の2文字の短縮形
* @return {string} 翻訳の結果
*/
function translateText(text, origin, dest) {
if (origin === dest) {
return text;
}
return LanguageApp.translate(text, origin, dest);
}
メニューから[ファイル] > [新規作成] > [HTMLファイル] 選択して新しいファイルを作成します。ファイルに「Sidebar」という名前を付けます。
新しいエディタタブでコードを削除し、下のコードに貼り付けます。
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<link rel="stylesheet" href="https://ssl.gstatic.com/docs/script/css/add-ons1.css">
<!-- The CSS package above applies Google styling to buttons and other elements. -->
<style>
.branding-below {
bottom: 56px;
top: 0;
}
.branding-text {
left: 7px;
position: relative;
top: 3px;
}
.col-contain {
overflow: hidden;
}
.col-one {
float: left;
width: 50%;
}
.logo {
vertical-align: middle;
}
.radio-spacer {
height: 20px;
}
.width-100 {
width: 100%;
}
</style>
</head>
<body>
<div class="sidebar branding-below">
<form>
<div class="block col-contain">
<div class="col-one">
<b>Selected text</b>
<div>
<input type="radio" name="origin" id="radio-origin-auto" value="" checked="checked">
<label for="radio-origin-auto">Auto-detect</label>
</div>
<div>
<input type="radio" name="origin" id="radio-origin-en" value="en">
<label for="radio-origin-en">English</label>
</div>
<div>
<input type="radio" name="origin" id="radio-origin-fr" value="fr">
<label for="radio-origin-fr">French</label>
</div>
<div>
<input type="radio" name="origin" id="radio-origin-de" value="de">
<label for="radio-origin-de">German</label>
</div>
<div>
<input type="radio" name="origin" id="radio-origin-ja" value="ja">
<label for="radio-origin-ja">Japanese</label>
</div>
<div>
<input type="radio" name="origin" id="radio-origin-es" value="es">
<label for="radio-origin-es">Spanish</label>
</div>
</div>
<div>
<b>Translate into</b>
<div class="radio-spacer">
</div>
<div>
<input type="radio" name="dest" id="radio-dest-en" value="en">
<label for="radio-dest-en">English</label>
</div>
<div>
<input type="radio" name="dest" id="radio-dest-fr" value="fr">
<label for="radio-dest-fr">French</label>
</div>
<div>
<input type="radio" name="dest" id="radio-dest-de" value="de">
<label for="radio-dest-de">German</label>
</div>
<div>
<input type="radio" name="dest" id="radio-dest-ja" value="ja" checked="checked">
<label for="radio-dest-ja">Japanese</label>
</div>
<div>
<input type="radio" name="dest" id="radio-dest-es" value="es">
<label for="radio-dest-es">Spanish</label>
</div>
</div>
</div>
<div class="block form-group">
<label for="translated-text"><b>Translation</b></label>
<textarea class="width-100" id="translated-text" rows="10"></textarea>
</div>
<div class="block">
<input type="checkbox" id="save-prefs">
<label for="save-prefs">Use these languages by default</label>
</div>
<div class="block" id="button-bar">
<button class="blue" id="run-translation">Translate</button>
<button id="insert-text">Insert</button>
</div>
</form>
</div>
<div class="sidebar bottom">
<img alt="Add-on logo" class="logo" src="https://www.gstatic.com/images/branding/product/1x/translate_48dp.png" width="27" height="27">
<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>
<script>
/**
* ドキュメントの読み込み時に、各ボタンにクリックハンドラを割り当てます。
* 設定されている場合は、オリジナルの言語と目的の言語(翻訳後)の
* 設定を読み込みます。
*/
$(function() {
$('#run-translation').click(runTranslation);
$('#insert-text').click(insertText);
google.script.run.withSuccessHandler(loadPreferences)
.withFailureHandler(showError).getPreferences();
});
/**
* サーバーから渡されたオリジナルの言語と目的の言語を設定するための
* コールバック関数。
*
* @param {Object} languagePrefs 保存されたオリジナルの言語と目的の言語
*/
function loadPreferences(languagePrefs) {
$('input:radio[name="origin"]')
.filter('[value=' + languagePrefs.originLang + ']')
.attr('checked', true);
$('input:radio[name="dest"]')
.filter('[value=' + languagePrefs.destLang + ']')
.attr('checked', true);
}
/**
* サーバーサイドで翻訳を実行して、サイドバーの結果を更新します。
*/
function runTranslation() {
this.disabled = true;
$('#error').remove();
var origin = $('input[name=origin]:checked').val();
var dest = $('input[name=dest]:checked').val();
var savePrefs = $('#save-prefs').is(':checked');
google.script.run
.withSuccessHandler(
function(textAndTranslation, element) {
$('#translated-text').val(textAndTranslation.translation);
element.disabled = false;
})
.withFailureHandler(
function(msg, element) {
showError(msg, $('#button-bar'));
element.disabled = false;
})
.withUserObject(this)
.getTextAndTranslation(origin, dest, savePrefs);
}
/**
* サーバー側の機能を実行して、翻訳されたテキストをユーザーのカーソルに
* 挿入または選択したテキストを置き換えます。
*/
function insertText() {
this.disabled = true;
$('#error').remove();
google.script.run
.withSuccessHandler(
function(returnSuccess, element) {
element.disabled = false;
})
.withFailureHandler(
function(msg, element) {
showError(msg, $('#button-bar'));
element.disabled = false;
})
.withUserObject(this)
.insertText($('#translated-text').val());
}
/**
* 指定された要素(element)にエラーメッセージを含むdivタグを挿入します。
*
* @param msg 表示するエラーメッセージ
* @param element エラーを表示する要素
*/
function showError(msg, element) {
var div = $('<div id="error" class="error">' + msg + '</div>');
$(element).after(div);
}
</script>
</body>
</html>
メニューの [ファイル] > [すべてを保存] を選択します。新しいプロジェクト名を「Translate Quickstart」とし、「OK」をクリックします(プロジェクトの名前は、承認ダイアログを含むいくつかの場所のエンドユーザーに表示されます)。
実行
ドキュメントに戻ってページをリロードしてください。アドオンが読み込まれるまで数秒待ちます。
メニュー [アドオン] > [Translate Quickstart] > [Start] を選択します。(スクリプトに別の名前を選択した場合は、代わりにその名前が表示されます)
3.
承認が必要となるので[継続]ボタンを選択してください。
4.
[許可]ボタンを選択してください。
5.
サイドバーが表示されます。
ドキュメントにテキストを入力し、それを選択して青の翻訳ボタンをクリックします。ドキュメント内のテキストを置き換えるには、[Insert]ボタンをクリックします。