Skip to content

Commit 785e83c

Browse files
author
Kalyan Reddy
committed
Added a new sample for Translate, Material design.
1 parent e9837ea commit 785e83c

File tree

5 files changed

+457
-0
lines changed

5 files changed

+457
-0
lines changed

translate/Code.gs

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
/**
2+
* Creates a menu entry in the Google Docs UI when the document is opened.
3+
*
4+
* @param {object} e The event parameter for a simple onOpen trigger. To
5+
* determine which authorization mode (ScriptApp.AuthMode) the trigger is
6+
* running in, inspect e.authMode.
7+
*/
8+
function onOpen(e) {
9+
DocumentApp.getUi().createAddonMenu()
10+
.addItem('Start', 'showSidebar')
11+
.addToUi();
12+
}
13+
14+
/**
15+
* Runs when the add-on is installed.
16+
*
17+
* @param {object} e The event parameter for a simple onInstall trigger. To
18+
* determine which authorization mode (ScriptApp.AuthMode) the trigger is
19+
* running in, inspect e.authMode. (In practice, onInstall triggers always
20+
* run in AuthMode.FULL, but onOpen triggers may be AuthMode.LIMITED or
21+
* AuthMode.NONE.)
22+
*/
23+
function onInstall(e) {
24+
onOpen(e);
25+
}
26+
27+
/**
28+
* Opens a sidebar in the document containing the add-on's user interface.
29+
*/
30+
function showSidebar() {
31+
var ui = HtmlService.createTemplateFromFile('Sidebar').evaluate()
32+
.setSandboxMode(HtmlService.SandboxMode.IFRAME)
33+
.setTitle('Translate');
34+
DocumentApp.getUi().showSidebar(ui);
35+
}
36+
37+
/**
38+
* Gets the text the user has selected. If there is no selection,
39+
* this function displays an error message.
40+
*
41+
* @return {Array.<string>} The selected text.
42+
*/
43+
function getSelectedText() {
44+
var selection = DocumentApp.getActiveDocument().getSelection();
45+
if (selection) {
46+
var text = [];
47+
var elements = selection.getSelectedElements();
48+
for (var i = 0; i < elements.length; i++) {
49+
if (elements[i].isPartial()) {
50+
var element = elements[i].getElement().asText();
51+
var startIndex = elements[i].getStartOffset();
52+
var endIndex = elements[i].getEndOffsetInclusive();
53+
54+
text.push(element.getText().substring(startIndex, endIndex + 1));
55+
} else {
56+
var element = elements[i].getElement();
57+
// Only translate elements that can be edited as text; skip images and
58+
// other non-text elements.
59+
if (element.editAsText) {
60+
var elementText = element.asText().getText();
61+
// This check is necessary to exclude images, which return a blank
62+
// text element.
63+
if (elementText != '') {
64+
text.push(elementText);
65+
}
66+
}
67+
}
68+
}
69+
if (text.length == 0) {
70+
throw 'Please select some text.';
71+
}
72+
return text;
73+
} else {
74+
throw 'Please select some text.';
75+
}
76+
}
77+
78+
/**
79+
* Gets the stored user preferences for the origin and destination languages,
80+
* if they exist.
81+
*
82+
* @return {Object} The user's origin and destination language preferences, if
83+
* they exist.
84+
*/
85+
function getPreferences() {
86+
var userProperties = PropertiesService.getUserProperties();
87+
var languagePrefs = {
88+
originLang: userProperties.getProperty('originLang'),
89+
destLang: userProperties.getProperty('destLang')
90+
};
91+
return languagePrefs;
92+
}
93+
94+
/**
95+
* Gets the user-selected text and translates it from the origin language to the
96+
* destination language. The languages are notated by their two-letter short
97+
* form. For example, English is "en", and Spanish is "es". The origin language
98+
* may be specified as "auto" to indicate that Google Translate should
99+
* auto-detect the language.
100+
*
101+
* @param {string} origin The two-letter short form for the origin language.
102+
* @param {string} dest The two-letter short form for the destination language.
103+
* @param {boolean} savePrefs Whether to save the origin and destination
104+
* language preferences.
105+
* @return {string} The result of the translation.
106+
*/
107+
function runTranslation(origin, dest, savePrefs) {
108+
var text = getSelectedText();
109+
if (savePrefs == true) {
110+
var userProperties = PropertiesService.getUserProperties();
111+
userProperties.setProperty('originLang', origin);
112+
userProperties.setProperty('destLang', dest);
113+
}
114+
115+
// The Translate service expects an empty string to signify auto-detect.
116+
if (origin == "auto") {
117+
origin = "";
118+
}
119+
120+
var translated = [];
121+
for (var i = 0; i < text.length; i++) {
122+
translated.push(LanguageApp.translate(text[i], origin, dest));
123+
}
124+
125+
return translated.join('\n');
126+
}
127+
128+
/**
129+
* Replaces the text of the current selection with the provided text, or
130+
* inserts text at the current cursor location. (There will always be either
131+
* a selection or a cursor.) If multiple elements are selected, only inserts the
132+
* translated text in the first element that can contain text and removes the
133+
* other elements.
134+
*
135+
* @param {string} newText The text with which to replace the current selection.
136+
*/
137+
function insertText(newText) {
138+
var selection = DocumentApp.getActiveDocument().getSelection();
139+
if (selection) {
140+
var replaced = false;
141+
var elements = selection.getSelectedElements();
142+
if (elements.length == 1 &&
143+
elements[0].getElement().getType() ==
144+
DocumentApp.ElementType.INLINE_IMAGE) {
145+
throw "Can't insert text into an image.";
146+
}
147+
for (var i = 0; i < elements.length; i++) {
148+
if (elements[i].isPartial()) {
149+
var element = elements[i].getElement().asText();
150+
var startIndex = elements[i].getStartOffset();
151+
var endIndex = elements[i].getEndOffsetInclusive();
152+
153+
var remainingText = element.getText().substring(endIndex + 1);
154+
element.deleteText(startIndex, endIndex);
155+
if (!replaced) {
156+
element.insertText(startIndex, newText);
157+
replaced = true;
158+
} else {
159+
// This block handles a selection that ends with a partial element. We
160+
// want to copy this partial text to the previous element so we don't
161+
// have a line-break before the last partial.
162+
var parent = element.getParent();
163+
parent.getPreviousSibling().asText().appendText(remainingText);
164+
// We cannot remove the last paragraph of a doc. If this is the case,
165+
// just remove the text within the last paragraph instead.
166+
if (parent.getNextSibling()) {
167+
parent.removeFromParent();
168+
} else {
169+
element.removeFromParent();
170+
}
171+
}
172+
} else {
173+
var element = elements[i].getElement();
174+
if (!replaced && element.editAsText) {
175+
// Only translate elements that can be edited as text, removing other
176+
// elements.
177+
element.clear();
178+
element.asText().setText(newText);
179+
replaced = true;
180+
} else {
181+
// We cannot remove the last paragraph of a doc. If this is the case,
182+
// just clear the element.
183+
if (element.getNextSibling()) {
184+
element.removeFromParent();
185+
} else {
186+
element.clear();
187+
}
188+
}
189+
}
190+
}
191+
} else {
192+
var cursor = DocumentApp.getActiveDocument().getCursor();
193+
var surroundingText = cursor.getSurroundingText().getText();
194+
var surroundingTextOffset = cursor.getSurroundingTextOffset();
195+
196+
// If the cursor follows or preceds a non-space character, insert a space
197+
// between the character and the translation. Otherwise, just insert the
198+
// translation.
199+
if (surroundingTextOffset > 0) {
200+
if (surroundingText.charAt(surroundingTextOffset - 1) != ' ') {
201+
newText = ' ' + newText;
202+
}
203+
}
204+
if (surroundingTextOffset < surroundingText.length) {
205+
if (surroundingText.charAt(surroundingTextOffset) != ' ') {
206+
newText += ' ';
207+
}
208+
}
209+
cursor.insertText(newText);
210+
}
211+
}
212+
213+
// Helper function that puts external JS / CSS into the HTML file.
214+
function include(filename) {
215+
return HtmlService.createHtmlOutputFromFile(filename)
216+
.getContent();
217+
}

translate/README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Translate
2+
3+
Translate is a sample script for Google Docs that allows you to translate
4+
selected text from a set of source languages to a set of destination languages.
5+
The resulting translation can then be inserted back into the Google Document.
6+
This sample was originally designed as a
7+
[quickstart](https://developers.google.com/apps-script/quickstart/docs)
8+
for Google Docs Add-ons, and has since been updated to use
9+
[Material design](http://www.google.com/design/spec/material-design/introduction.html)
10+
as implemented by [Polymer](https://www.polymer-project.org/).
11+
12+
![Translate screenshot](https://googledrive.com/host/0B4YSkkItiZaHXzdFQ1RWbWZidWM/translate_screenshot.png)

translate/Sidebar.css.html

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<style>
2+
body {
3+
font-family: 'RobotoDraft', sans-serif;
4+
margin: 0;
5+
padding: 0;
6+
}
7+
8+
h3 {
9+
text-align: center;
10+
margin: 0;
11+
}
12+
13+
paper-button {
14+
margin: 0;
15+
margin-top: 10px;
16+
}
17+
18+
paper-button + paper-button {
19+
margin-left: 8px;
20+
}
21+
22+
.sidebar {
23+
-moz-box-sizing: border-box;
24+
box-sizing: border-box;
25+
overflow-y: auto;
26+
padding: 12px;
27+
position: absolute;
28+
width: 100%;
29+
}
30+
31+
.bottom {
32+
bottom: 0;
33+
position: absolute;
34+
}
35+
36+
.branding-below {
37+
bottom: 56px;
38+
top: 0;
39+
}
40+
41+
.branding-text {
42+
left: 7px;
43+
position: relative;
44+
top: 3px;
45+
}
46+
47+
.col-contain {
48+
overflow: hidden;
49+
}
50+
51+
.col {
52+
float: left;
53+
width: 50%;
54+
}
55+
56+
#dest {
57+
margin-top: 42px;
58+
}
59+
60+
.logo {
61+
vertical-align: middle;
62+
}
63+
64+
.width-100 {
65+
width: 100%;
66+
}
67+
68+
.error {
69+
color: #dd4b39;
70+
font-size: small;
71+
margin-top: 8px;
72+
}
73+
74+
.gray {
75+
color: #777;
76+
}
77+
78+
.colored {
79+
background: #4285f4;
80+
color: #ffffff;
81+
}
82+
</style>

translate/Sidebar.html

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<script src="//polymerstaticfiles.appspot.com/bower_components/webcomponentsjs/webcomponents.js"></script>
2+
3+
<link rel="import" href="//polymerstaticfiles.appspot.com/bower_components/polymer/polymer.html">
4+
<link rel="import" href="//polymerstaticfiles.appspot.com/bower_components/font-roboto/roboto.html">
5+
<link rel="import" href="//polymerstaticfiles.appspot.com/bower_components/paper-input/paper-input.html">
6+
<link rel="import" href="//polymerstaticfiles.appspot.com/bower_components/paper-button/paper-button.html">
7+
<link rel="import" href="//polymerstaticfiles.appspot.com/bower_components/paper-checkbox/paper-checkbox.html">
8+
<link rel="import" href="//polymerstaticfiles.appspot.com/bower_components/paper-radio-group/paper-radio-group.html">
9+
<link rel="import" href="//polymerstaticfiles.appspot.com/bower_components/paper-radio-button/paper-radio-button.html">
10+
<link rel="import" href="//polymerstaticfiles.appspot.com/bower_components/paper-input/paper-input-decorator.html">
11+
12+
<?!= include('Sidebar.css.html'); ?>
13+
14+
<div class="sidebar branding-below">
15+
<div class="col-contain">
16+
<div class="col">
17+
<h3>Selected text</h3>
18+
<paper-radio-group id="origin" selected="auto">
19+
<paper-radio-button name="auto" id="radio-origin-auto" label="Auto-detect"></paper-radio-button>
20+
<paper-radio-button name="en" id="radio-origin-en" label="English"></paper-radio-button>
21+
<paper-radio-button name="fr" id="radio-origin-fr" label="French"></paper-radio-button>
22+
<paper-radio-button name="de" id="radio-origin-de" label="German"></paper-radio-button>
23+
<paper-radio-button name="ja" id="radio-origin-ja" label="Japanese"></paper-radio-button>
24+
<paper-radio-button name="es" id="radio-origin-es" label="Spanish"></paper-radio-button>
25+
</paper-radio-group>
26+
</div>
27+
<div class="col">
28+
<h3>Translate into</h3>
29+
<paper-radio-group id="dest" selected="ja">
30+
<paper-radio-button name="en" id="radio-dest-en" label="English"></paper-radio-button>
31+
<paper-radio-button name="fr" id="radio-dest-fr" label="French"></paper-radio-button>
32+
<paper-radio-button name="de" id="radio-dest-de" label="German"></paper-radio-button>
33+
<paper-radio-button name="ja" id="radio-dest-ja" label="Japanese"></paper-radio-button>
34+
<paper-radio-button name="es" id="radio-dest-es" label="Spanish"></paper-radio-button>
35+
</paper-radio-group>
36+
</div>
37+
</div>
38+
39+
<paper-input-decorator floatingLabel label="Translation">
40+
<textarea class="width-100" id="translated-text" rows="8"></textarea>
41+
</paper-input-decorator>
42+
43+
44+
<paper-checkbox id="save-prefs" label="Use these languages by default"></paper-checkbox>
45+
46+
47+
<div id="button-bar">
48+
<paper-button raised class="colored" id="run-translation">Translate</paper-button>
49+
<paper-button raised id="insert-text">Insert</paper-button>
50+
</div>
51+
52+
</div>
53+
54+
<div class="sidebar bottom">
55+
<img alt="Add-on logo" class="logo" width="27" height="27"
56+
src="https://googledrive.com/host/0B0G1UdyJGrY6XzdjQWF4a1JYY1k/translate-logo-small.png">
57+
<span class="gray branding-text">Translate sample by Google</span>
58+
</div>
59+
60+
<?!= include('Sidebar.js.html'); ?>

0 commit comments

Comments
 (0)