├── .gitignore ├── Translateme.sketchplugin └── Contents │ ├── Resources │ ├── .gitkeep │ └── logo@2x.png │ └── Sketch │ ├── main.cocoascript │ ├── manifest.json │ ├── common.js │ └── translator.js ├── assets ├── demo.gif └── sketchpacks.png ├── appcast.xml └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | assets/.DS_Store 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /Translateme.sketchplugin/Contents/Resources/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eddiesigner/sketch-translate-me/HEAD/assets/demo.gif -------------------------------------------------------------------------------- /assets/sketchpacks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eddiesigner/sketch-translate-me/HEAD/assets/sketchpacks.png -------------------------------------------------------------------------------- /Translateme.sketchplugin/Contents/Resources/logo@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eddiesigner/sketch-translate-me/HEAD/Translateme.sketchplugin/Contents/Resources/logo@2x.png -------------------------------------------------------------------------------- /Translateme.sketchplugin/Contents/Sketch/main.cocoascript: -------------------------------------------------------------------------------- 1 | @import "translator.js"; 2 | 3 | function runSimpleTranslation (context) { 4 | var translator = new Translator(); 5 | translator.translateSingleText(context); 6 | } 7 | 8 | function runArtboardTranslation (context) { 9 | var translator = new Translator(); 10 | translator.translateArtboard(context); 11 | } 12 | 13 | function runEverythingTranslation (context) { 14 | var translator = new Translator(); 15 | translator.translateEverything(context); 16 | } 17 | 18 | function runSetApiKey (context) { 19 | var translator = new Translator(); 20 | translator.openApiKeyWindow(context); 21 | } -------------------------------------------------------------------------------- /Translateme.sketchplugin/Contents/Sketch/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "author" : "Craftbot", 3 | "bundleVersion" : 1, 4 | "commands" : [ 5 | { 6 | "script" : "main.cocoascript", 7 | "name" : "Translate Layer", 8 | "handler" : "runSimpleTranslation", 9 | "identifier" : "scriptSingle", 10 | "shortcut" : "shift cmd w" 11 | }, 12 | { 13 | "script" : "main.cocoascript", 14 | "name" : "Translate Artboard", 15 | "handler" : "runArtboardTranslation", 16 | "identifier" : "scriptArtboard", 17 | "shortcut" : "shift cmd x" 18 | }, 19 | { 20 | "script" : "main.cocoascript", 21 | "name" : "Translate Everything", 22 | "handler" : "runEverythingTranslation", 23 | "identifier" : "scriptEverything", 24 | "shortcut" : "shift cmd y" 25 | }, 26 | { 27 | "script" : "main.cocoascript", 28 | "name" : "Set Google API Key...", 29 | "handler" : "runSetApiKey", 30 | "identifier" : "scriptKey", 31 | "shortcut" : "shift cmd k" 32 | } 33 | ], 34 | "menu" : { 35 | "items" : [ 36 | "scriptSingle", 37 | "scriptArtboard", 38 | "scriptEverything", 39 | "scriptKey" 40 | ], 41 | "title" : "Translate.me" 42 | }, 43 | "compatibleVersion" : 4, 44 | "homepage" : "https://github.com/eddiesigner/sketch-translate-me", 45 | "identifier" : "io.craftbot.sketch.translate-me", 46 | "appcast": "https://raw.githubusercontent.com/eddiesigner/sketch-translate-me/master/appcast.xml", 47 | "version" : "0.1.1", 48 | "description" : "Plugin to translate Sketch files in any language", 49 | "authorEmail" : "", 50 | "name" : "Translate.me" 51 | } 52 | -------------------------------------------------------------------------------- /appcast.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Translate.me 5 | http://sparkle-project.org/files/sparkletestcast.xml 6 | Plugin to translate Sketch files in any language 7 | en 8 | 9 | Version 0.0.1 10 | 11 | 13 |
  • Alpha version
  • 14 | 15 | ]]> 16 |
    17 | 18 |
    19 | 20 | Version 0.0.2 21 | 22 | 24 |
  • New documentation
  • 25 |
  • New icon
  • 26 | 27 | ]]> 28 |
    29 | 30 |
    31 | 32 | Version 0.1.0 33 | 34 | 36 |
  • The plugin now uses your own Google API Key
  • 37 |
  • Improvements and bugfixes
  • 38 |
  • New documentation
  • 39 | 40 | ]]> 41 |
    42 | 43 |
    44 | 45 | Version 0.1.1 46 | 47 | 49 |
  • Fixes the problem that made the plugin not work after installation
  • 50 | 51 | ]]> 52 |
    53 | 54 |
    55 |
    56 |
    -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Translate.me 0.1.1 - Sketch App Plugin 2 | 3 | Translations. Simplified. 4 | 5 | Translate your Sketch text layers, artboards and pages in over 100 languages with Translate.me plugin. Powered by Google. 6 | 7 | Translate.me helps you to save time, understand and communicate across your international team or app. 8 | 9 | If you use it and you like it, please consider to make a donation to support its development! 🙏🏼 10 | 11 | Buy Me A Coffee 12 | 13 | ![Translate.me](/assets/demo.gif?raw=true "Translations. Simplified.") 14 | 15 | ## How it works? 16 | 17 | 1. Translate.me automatically detects the language which you used on your file. 18 | 19 | 2. Choose over 100 languages 20 | 21 | 3. Witness the plugin in action 22 | 23 | ##### Translate Layer 24 | Plugins > Translate.me > Translate Layer 25 |
    26 | Shortcut: Shift+Cmd+W 27 | ##### Translate Artboard 28 | Plugins > Translate.me > Translate Artboard 29 |
    30 | Shortcut: Shift+Cmd+X 31 |
    32 | ##### Translate Everything 33 | Plugins > Translate.me > Translate Everything 34 |
    35 | Shortcut: Shift+Cmd+Y 36 |
    37 | ##### Set Google API Key... 38 | Plugins > Translate.me > Set Google API Key... 39 |
    40 | Shortcut: Shift+Cmd+K 41 |
    42 | 43 | ## Installation 44 | 45 | [![Install Translate.me with Sketchpacks](/assets/sketchpacks.png?raw=true "Install Translate.me with Sketchpacks")](https://www.sketchpacks.com/eddiesigner/sketch-translate-me/install) 46 | 47 | 48 | #### Install with Sketch Runner 49 | With Sketch Runner, just go to the `install` command and search for `Translate.me`. Runner allows you to manage plugins and do much more to speed up your workflow in Sketch. [Download Runner here](http://www.sketchrunner.com). 50 | 51 | ![Sketch Runner screenshot](https://res.cloudinary.com/edev/image/upload/v1511169274/Screen_Shot_2017-11-20_at_10.13.50_AM_yij9xq.png) 52 | 53 | #### Manual 54 | 55 | 1. [Download the Zip](https://github.com/eddiesigner/sketch-translate-me/archive/master.zip) and unzip the package 56 | 2. Double click on `Translateme.sketchplugin` for auto installation 57 | 58 | ## Configuration 59 | 60 | Be sure to read [this article](https://github.com/eddiesigner/sketch-translate-me/wiki/Generate-a-Google-API-Key) as you will need a Google API key to use the plugin. 61 | This article describes the process for obtaining a key and how to integrate it into the plugin, don't worry, it's easy ;) 62 | 63 | ## Compatibility 64 | 65 | The plugin is compatible with Sketch 4+. 66 | 67 | ## Contact 68 | 69 | If you have any questions or troubles with our product, please feel free to open an issue [here](https://github.com/eddiesigner/sketch-translate-me/issues). 70 | 71 | If you prefer, you can send an email to: [hello@craftbot.io](mailto:hello@craftbot.io) 72 | -------------------------------------------------------------------------------- /Translateme.sketchplugin/Contents/Sketch/common.js: -------------------------------------------------------------------------------- 1 | var pluginIdentifier = "io.craftbot.sketch.translate-me"; 2 | var app = NSApplication.sharedApplication(); 3 | var googleApiKey = getOption('apiKey', ''); 4 | 5 | 6 | function checkCount (context) { 7 | if (context.selection.count() != 1) { 8 | app.displayDialog_withTitle("You have to select something.", "Wrong shape layer selection"); 9 | return false; 10 | } 11 | 12 | return true; 13 | } 14 | 15 | 16 | function checkTextLayerType (context) { 17 | var layer = context.selection[0]; 18 | 19 | if ([layer class] != MSTextLayer) { 20 | app.displayDialog_withTitle("Your selection was a “" + [layer name] + "”, that is not a text layer. Please select a text layer.", "Text layer only"); 21 | return false; 22 | } 23 | 24 | return true; 25 | } 26 | 27 | 28 | function duplicatePage (context, language) { 29 | var doc = context.document; 30 | var page = [doc currentPage]; 31 | var newPage = [page copy]; 32 | 33 | newPage.setName([page name] + " " + language); 34 | 35 | [[doc documentData] addPage:newPage]; 36 | [doc setCurrentPage:newPage]; 37 | 38 | return newPage; 39 | } 40 | 41 | 42 | function checkArtboardLayerType (context) { 43 | var layer = context.selection[0]; 44 | 45 | if ([layer class] != MSArtboardGroup) { 46 | app.displayDialog_withTitle("Your selection was a “" + [layer name] + "”, that is not an artboard. Please select an artboard.", "Artboard only"); 47 | return false; 48 | } 49 | 50 | return true; 51 | } 52 | 53 | 54 | function selectLayersOfTypeInContainer (doc, layerType, containerLayer) { 55 | var scope = (typeof containerLayer !== 'undefined') ? [containerLayer children] : [[doc currentPage] children]; 56 | var predicate = NSPredicate.predicateWithFormat("(className == %@)", layerType); 57 | var layers = [scope filteredArrayUsingPredicate:predicate]; 58 | var page = (layerType === 'MSArtboardGroup') ? containerLayer : [doc currentPage]; 59 | 60 | if (page.deselectAllLayers) { 61 | page.deselectAllLayers(); 62 | } else { 63 | page.changeSelectionBySelectingLayers_([]); 64 | } 65 | 66 | var loop = [layers objectEnumerator]; 67 | var layers = []; 68 | var layer; 69 | 70 | while (layer = [loop nextObject]) { 71 | layers.push(layer); 72 | 73 | if (MSApplicationMetadata.metadata().appVersion > 45) { 74 | layer.select_byExpandingSelection(true, true); 75 | } else { 76 | layer.select_byExtendingSelection(true, true); 77 | } 78 | } 79 | 80 | return layers; 81 | } 82 | 83 | 84 | function handleAlertResponse (dialog, responseCode) { 85 | if (responseCode == "1000") { 86 | return dialog.viewAtIndex(0).indexOfSelectedItem(); 87 | } else { 88 | return null; 89 | } 90 | } 91 | 92 | 93 | function handleKeyAlertResponse (dialog, responseCode) { 94 | if (responseCode == "1000") { 95 | var apiKeyValue = dialog.viewAtIndex(0).stringValue(); 96 | 97 | setPreferences('apiKey', apiKeyValue); 98 | 99 | return true; 100 | } else { 101 | return false; 102 | } 103 | } 104 | 105 | 106 | function createSelect (options) { 107 | var select = NSPopUpButton.alloc().initWithFrame(NSMakeRect(0, 0, 200, 28)); 108 | 109 | select.addItemsWithTitles(options); 110 | select.selectItemAtIndex(0); 111 | 112 | return select; 113 | } 114 | 115 | 116 | function detectLenguage (text) { 117 | var escapedText = text.replace('"', '\"'); 118 | var data = JSON.stringify({q:escapedText}); 119 | 120 | var languageDetected = networkRequest(["-X", "POST", "https://translation.googleapis.com/language/translate/v2/detect?key=" + googleApiKey, "-H", "Content-Type: application/json; charset=utf-8", "-d", data]); 121 | 122 | return languageDetected.data.detections[0][0].language; 123 | } 124 | 125 | 126 | function getSingleTranslation (text, baseLanguage, toLanguage) { 127 | var escapedText = text.replace('"', '\"'); 128 | var data = JSON.stringify({q:escapedText, source: baseLanguage, target: toLanguage}); 129 | 130 | var singleTranslation = networkRequest(["-X", "POST", "https://translation.googleapis.com/language/translate/v2?key=" + googleApiKey, "-H", "Content-Type: application/json; charset=utf-8", "-d", data]); 131 | 132 | return singleTranslation.data.translations[0].translatedText.replace(/"/g, '"'); 133 | } 134 | 135 | 136 | function networkRequest (args) { 137 | var task = NSTask.alloc().init(); 138 | task.setLaunchPath("/usr/bin/curl"); 139 | task.setArguments(args); 140 | 141 | var outputPipe = [NSPipe pipe]; 142 | [task setStandardOutput:outputPipe]; 143 | task.launch(); 144 | 145 | var responseData = [[outputPipe fileHandleForReading] readDataToEndOfFile]; 146 | var responseString = [[[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding]]; 147 | var parsed = tryParseJSON(responseString); 148 | 149 | if (!parsed) { 150 | log("Error invoking curl"); 151 | log("args:"); 152 | log(args); 153 | log("responseString"); 154 | log(responseString); 155 | throw "Error communicating with server"; 156 | } 157 | 158 | return parsed; 159 | } 160 | 161 | 162 | function tryParseJSON (jsonString){ 163 | try { 164 | var o = JSON.parse(jsonString); 165 | 166 | if (o && typeof o === "object" && o !== null) { 167 | return o; 168 | } 169 | } 170 | catch (e) { } 171 | 172 | return false; 173 | } 174 | 175 | 176 | function getOption(key, defaultValue) { 177 | return getPreferences(key, defaultValue); 178 | } 179 | 180 | 181 | function getPreferences(key, defaultValue) { 182 | var userDefaults = NSUserDefaults.standardUserDefaults(); 183 | 184 | if (!userDefaults.dictionaryForKey(pluginIdentifier)) { 185 | var defaultPreferences = NSMutableDictionary.alloc().init(); 186 | 187 | userDefaults.setObject_forKey(defaultPreferences, pluginIdentifier); 188 | userDefaults.synchronize(); 189 | } 190 | 191 | var value = userDefaults.dictionaryForKey(pluginIdentifier).objectForKey(key); 192 | 193 | return (value === null) ? defaultValue : value; 194 | } 195 | 196 | 197 | function setPreferences(key, value) { 198 | var userDefaults = NSUserDefaults.standardUserDefaults(); 199 | var preferences; 200 | 201 | if (!userDefaults.dictionaryForKey(pluginIdentifier)) { 202 | preferences = NSMutableDictionary.alloc().init(); 203 | } else { 204 | preferences = NSMutableDictionary.dictionaryWithDictionary(userDefaults.dictionaryForKey(pluginIdentifier)); 205 | } 206 | 207 | preferences.setObject_forKey(value, key); 208 | 209 | userDefaults.setObject_forKey(preferences, pluginIdentifier); 210 | userDefaults.synchronize(); 211 | } -------------------------------------------------------------------------------- /Translateme.sketchplugin/Contents/Sketch/translator.js: -------------------------------------------------------------------------------- 1 | @import "common.js"; 2 | 3 | function Translator () {} 4 | 5 | Translator.prototype.languageLabels = [ 6 | 'Afrikaans', 7 | 'Albanian', 8 | 'Amharic', 9 | 'Arabic', 10 | 'Armenian', 11 | 'Azeerbaijani', 12 | 'Basque', 13 | 'Belarusian', 14 | 'Bengali', 15 | 'Bosnian', 16 | 'Bulgarian', 17 | 'Catalan', 18 | 'Cebuano', 19 | 'Chinese (Simplified)', 20 | 'Chinese (Traditional)', 21 | 'Corsican', 22 | 'Croatian', 23 | 'Czech', 24 | 'Danish', 25 | 'Dutch', 26 | 'English', 27 | 'Esperanto', 28 | 'Estonian', 29 | 'Finnish', 30 | 'French', 31 | 'Frisian', 32 | 'Galician', 33 | 'Georgian', 34 | 'German', 35 | 'Greek', 36 | 'Gujarati', 37 | 'Haitian Creole', 38 | 'Hausa', 39 | 'Hawaiian', 40 | 'Hebrew', 41 | 'Hindi', 42 | 'Hmong', 43 | 'Hungarian', 44 | 'Icelandic', 45 | 'Igbo', 46 | 'Indonesian', 47 | 'Irish', 48 | 'Italian', 49 | 'Japanese', 50 | 'Javanese', 51 | 'Kannada', 52 | 'Kazakh', 53 | 'Khmer', 54 | 'Korean', 55 | 'Kurdish', 56 | 'Kyrgyz', 57 | 'Lao', 58 | 'Latin', 59 | 'Latvian', 60 | 'Lithuanian', 61 | 'Luxembourgish', 62 | 'Macedonian', 63 | 'Malagasy', 64 | 'Malay', 65 | 'Malayalam', 66 | 'Maltese', 67 | 'Maori', 68 | 'Marathi', 69 | 'Mongolian', 70 | 'Myanmar (Burmese)', 71 | 'Nepali', 72 | 'Norwegian', 73 | 'Nyanja (Chichewa)', 74 | 'Pashto', 75 | 'Persian', 76 | 'Polish', 77 | 'Portuguese (Portugal, Brazil)', 78 | 'Punjabi', 79 | 'Romanian', 80 | 'Russian', 81 | 'Samoan', 82 | 'Scots Gaelic', 83 | 'Serbian', 84 | 'Sesotho', 85 | 'Shona', 86 | 'Sindhi', 87 | 'Sinhala (Sinhalese)', 88 | 'Slovak', 89 | 'Slovenian', 90 | 'Somali', 91 | 'Spanish', 92 | 'Sundanese', 93 | 'Swahili', 94 | 'Swedish', 95 | 'Tagalog (Filipino)', 96 | 'Tajik', 97 | 'Tamil', 98 | 'Telugu', 99 | 'Thai', 100 | 'Turkish', 101 | 'Ukrainian', 102 | 'Urdu', 103 | 'Uzbek', 104 | 'Vietnamese', 105 | 'Welsh', 106 | 'Xhosa', 107 | 'Yiddish', 108 | 'Yoruba', 109 | 'Zulu' 110 | ]; 111 | 112 | 113 | Translator.prototype.languageCodes = [ 114 | 'af', 115 | 'sq', 116 | 'am', 117 | 'ar', 118 | 'hy', 119 | 'az', 120 | 'eu', 121 | 'be', 122 | 'bn', 123 | 'bs', 124 | 'bg', 125 | 'ca', 126 | 'ceb', 127 | 'zh-CN', 128 | 'zh-TW', 129 | 'co', 130 | 'hr', 131 | 'cs', 132 | 'da', 133 | 'nl', 134 | 'en', 135 | 'eo', 136 | 'et', 137 | 'fi', 138 | 'fr', 139 | 'fy', 140 | 'gl', 141 | 'ka', 142 | 'de', 143 | 'el', 144 | 'gu', 145 | 'ht', 146 | 'ha', 147 | 'haw', 148 | 'iw', 149 | 'hi', 150 | 'hmn', 151 | 'hu', 152 | 'is', 153 | 'ig', 154 | 'id', 155 | 'ga', 156 | 'it', 157 | 'ja', 158 | 'jw', 159 | 'kn', 160 | 'kk', 161 | 'km', 162 | 'ko', 163 | 'ku', 164 | 'ky', 165 | 'lo', 166 | 'la', 167 | 'lv', 168 | 'lt', 169 | 'lb', 170 | 'mk', 171 | 'mg', 172 | 'ms', 173 | 'ml', 174 | 'mt', 175 | 'mi', 176 | 'mr', 177 | 'mn', 178 | 'my', 179 | 'ne', 180 | 'no', 181 | 'ny', 182 | 'ps', 183 | 'fa', 184 | 'pl', 185 | 'pt', 186 | 'pa', 187 | 'ro', 188 | 'ru', 189 | 'sm', 190 | 'gd', 191 | 'sr', 192 | 'st', 193 | 'sn', 194 | 'sd', 195 | 'si', 196 | 'sk', 197 | 'sl', 198 | 'so', 199 | 'es', 200 | 'su', 201 | 'sw', 202 | 'sv', 203 | 'tl', 204 | 'tg', 205 | 'ta', 206 | 'te', 207 | 'th', 208 | 'tr', 209 | 'uk', 210 | 'ur', 211 | 'uz', 212 | 'vi', 213 | 'cy', 214 | 'xh', 215 | 'yi', 216 | 'yo', 217 | 'zu' 218 | ]; 219 | 220 | 221 | Translator.prototype.translateSingleText = function (context) { 222 | if (!checkCount(context)) { 223 | return; 224 | } else { 225 | if (!checkTextLayerType(context)) { 226 | return; 227 | } else { 228 | var dialog = this.buildDialog(context); 229 | var languageIndex = handleAlertResponse(dialog, dialog.runModal()); 230 | 231 | if (languageIndex == null) { 232 | return; 233 | } 234 | 235 | var textLayer = context.selection[0]; 236 | var baseLanguage = detectLenguage(textLayer.stringValue()); 237 | 238 | if (!baseLanguage) { 239 | return; 240 | } 241 | 242 | var toLanguage = this.languageCodes[languageIndex]; 243 | 244 | if (!toLanguage) { 245 | return; 246 | } 247 | 248 | if (baseLanguage == toLanguage) { 249 | context.document.showMessage('Please select a different language'); 250 | } 251 | 252 | textLayer.setStringValue(getSingleTranslation(textLayer.stringValue(), baseLanguage, toLanguage)); 253 | } 254 | } 255 | } 256 | 257 | Translator.prototype.translateArtboard = function (context) { 258 | if (!checkCount(context)) { 259 | return; 260 | } else { 261 | if (!checkArtboardLayerType(context)) { 262 | return; 263 | } else { 264 | var dialog = this.buildDialog(context); 265 | var languageIndex = handleAlertResponse(dialog, dialog.runModal()); 266 | 267 | if (languageIndex == null) { 268 | return; 269 | } 270 | 271 | var artboardCopy = context.selection[0].duplicate(); 272 | artboardCopy.frame().x = artboardCopy.frame().x() + context.selection[0].frame().width() + 100; 273 | 274 | if (MSApplicationMetadata.metadata().appVersion > 45) { 275 | artboardCopy.select_byExpandingSelection(true, false); 276 | } else { 277 | artboardCopy.select_byExtendingSelection(true, false); 278 | } 279 | 280 | var textLayers = selectLayersOfTypeInContainer(context.document, "MSTextLayer", artboardCopy); 281 | var toLanguage = this.languageCodes[languageIndex]; 282 | 283 | for (var x = 0, l = textLayers.length; x < l; x++) { 284 | var textLayer = textLayers[x]; 285 | var baseLanguage = detectLenguage(textLayer.stringValue()); 286 | 287 | if(!baseLanguage) { 288 | return; 289 | } 290 | 291 | textLayer.setStringValue(getSingleTranslation(textLayer.stringValue(), baseLanguage, toLanguage)); 292 | } 293 | } 294 | } 295 | } 296 | 297 | 298 | Translator.prototype.translateEverything = function (context) { 299 | var doc = context.document; 300 | var initialPage = [doc currentPage]; 301 | var artboards = [initialPage artboards]; 302 | 303 | if (artboards.length === 0) { 304 | return; 305 | } 306 | 307 | var dialog = this.buildDialog(context); 308 | var languageIndex = handleAlertResponse(dialog, dialog.runModal()); 309 | 310 | if (languageIndex == null) { 311 | return; 312 | } 313 | 314 | var newPage = duplicatePage(context, this.languageLabels[languageIndex]); 315 | var artboards = selectLayersOfTypeInContainer(context.document, "MSArtboardGroup", newPage); 316 | 317 | for (var x = artboards.length - 1; x >= 0; x--) { 318 | var artboardCopy = artboards[x]; 319 | 320 | var textLayers = selectLayersOfTypeInContainer(context.document, "MSTextLayer", artboardCopy); 321 | var toLanguage = this.languageCodes[languageIndex]; 322 | 323 | for (var x = 0, l = textLayers.length; x < l; x++) { 324 | var textLayer = textLayers[x]; 325 | var baseLanguage = detectLenguage(textLayer.stringValue()); 326 | 327 | if(!baseLanguage) { 328 | return; 329 | } 330 | 331 | textLayer.setStringValue(getSingleTranslation(textLayer.stringValue(), baseLanguage, toLanguage)); 332 | } 333 | } 334 | } 335 | 336 | 337 | Translator.prototype.buildDialog = function (context) { 338 | var apiKey = getOption('apiKey', ''); 339 | var dialogWindow = COSAlertWindow.new(); 340 | var informativeText = ''; 341 | 342 | if (apiKey.length == 0) { 343 | informativeText = 'You have to set your Google API Key into the plugin settings (Plugins > Translate.me > Set Google API Key...)'; 344 | } else { 345 | informativeText = 'Please select the language in which you want to translate the text:'; 346 | } 347 | 348 | dialogWindow.setMessageText('Translate.me'); 349 | dialogWindow.setInformativeText(informativeText); 350 | 351 | 352 | if (apiKey.length == 0) { 353 | var link = NSButton.alloc().initWithFrame(NSMakeRect(0, 0, 200, 20))); 354 | link.setTitle('How to get a Google API Key'); 355 | link.setBezelStyle(NSInlineBezelStyle); 356 | 357 | link.setCOSJSTargetFunction(function() { 358 | var url = NSURL.URLWithString(@"https://github.com/eddiesigner/sketch-translate-me/wiki/Generate-a-Google-API-Key"); 359 | 360 | if (!NSWorkspace.sharedWorkspace().openURL(url)) { 361 | log(@"Failed to open url:" + url.description()); 362 | } 363 | }); 364 | 365 | dialogWindow.addAccessoryView(link); 366 | } else { 367 | var languageSelect = createSelect(this.languageLabels); 368 | dialogWindow.addAccessoryView(languageSelect); 369 | 370 | dialogWindow.addButtonWithTitle('OK'); 371 | dialogWindow.addButtonWithTitle('Cancel'); 372 | } 373 | 374 | dialogWindow.setIcon(NSImage.alloc().initByReferencingFile(context.plugin.urlForResourceNamed("logo@2x.png").path())); 375 | 376 | return dialogWindow; 377 | } 378 | 379 | 380 | Translator.prototype.openApiKeyWindow = function (context) { 381 | var dialog = this.buildKeyWindow(context); 382 | var response = handleKeyAlertResponse(dialog, dialog.runModal()); 383 | } 384 | 385 | 386 | Translator.prototype.buildKeyWindow = function (context) { 387 | var apiKey = getOption('apiKey', ''); 388 | var dialogWindow = COSAlertWindow.new(); 389 | 390 | dialogWindow.setMessageText('Translate.me'); 391 | dialogWindow.setInformativeText('Paste here your Google API Key (you have to do this just once):'); 392 | 393 | dialogWindow.addTextFieldWithValue(apiKey.length == 0 ? '' : getOption('apiKey')); 394 | 395 | var apiKeyTextBox = dialogWindow.viewAtIndex(0); 396 | 397 | dialogWindow.alert().window().setInitialFirstResponder(apiKeyTextBox); 398 | 399 | dialogWindow.addButtonWithTitle('OK'); 400 | dialogWindow.addButtonWithTitle('Cancel'); 401 | 402 | dialogWindow.setIcon(NSImage.alloc().initByReferencingFile(context.plugin.urlForResourceNamed("logo@2x.png").path())); 403 | 404 | return dialogWindow; 405 | } --------------------------------------------------------------------------------