├── .github └── workflows │ └── CI.yml ├── .gitignore ├── Cask ├── Makefile ├── README.md ├── features ├── google-translate-default-ui.feature ├── google-translate-smooth-ui.feature ├── step-definitions │ ├── google-translate-default-ui-steps.el │ ├── google-translate-smooth-ui-steps.el │ └── google-translate-steps.el └── support │ └── env.el ├── google-translate-backend.el ├── google-translate-core-ui.el ├── google-translate-core.el ├── google-translate-default-ui.el ├── google-translate-smooth-ui.el ├── google-translate.el └── test ├── fixtures ├── sentence │ ├── 1.fixture │ └── 2.fixture └── word │ ├── 1.fixture │ ├── 2.fixture │ ├── 3.fixture │ ├── 4.fixture │ ├── 5.fixture │ ├── 6.fixture │ └── 7.fixture ├── google-translate-core-test.el ├── google-translate-core-ui-test.el └── test-helper.el /.github/workflows/CI.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | paths-ignore: 6 | - '*.md' 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | timeout-minutes: 3 12 | strategy: 13 | matrix: 14 | emacs_version: 15 | - "24.3" 16 | - "25.3" 17 | - "26.3" 18 | - "27.2" 19 | - "28.1" 20 | - snapshot 21 | include: 22 | - emacs_version: snapshot 23 | allow_failure: true 24 | steps: 25 | - uses: actions/setup-python@v2 26 | with: 27 | python-version: '3.6' 28 | architecture: 'x64' 29 | - uses: purcell/setup-emacs@master 30 | with: 31 | version: ${{ matrix.emacs_version }} 32 | - uses: conao3/setup-cask@master 33 | with: 34 | version: 'snapshot' 35 | - uses: actions/checkout@v3 36 | - name: Setup Cask and compile lisp files 37 | run: 'make' 38 | - name: Run tests 39 | if: matrix.allow_failure != true 40 | run: 'make unit-test' 41 | - name: Run tests (allow failure) 42 | if: matrix.allow_failure == true 43 | run: 'make test || true' 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.elc 2 | /.cask/* 3 | /.dir-locals-2.el 4 | -------------------------------------------------------------------------------- /Cask: -------------------------------------------------------------------------------- 1 | (source melpa) 2 | 3 | (package "google-translate" "0.8.2" 4 | "Emacs interface to Google Translate.") 5 | 6 | (package-file "google-translate.el") 7 | 8 | (development 9 | (depends-on "f") 10 | (depends-on "s") 11 | (depends-on "dash") 12 | (depends-on "el-mock") 13 | (depends-on "ert-runner") 14 | (depends-on "ecukes") 15 | (depends-on "espuds")) 16 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY : all test unit-test ecukes 2 | 3 | EMACS ?= emacs 4 | SRC = $(filter-out %-pkg.el, $(wildcard *.el reporters/*.el)) 5 | ELC = $(SRC:.el=.elc) 6 | AUTOLOADS = google-translate-autoloads.el 7 | CASK ?= cask 8 | PKG_DIR := $(shell $(CASK) package-directory) 9 | FEATURES = $(wildcard features/*.feature) 10 | VERSION = 0.12.0 11 | 12 | %.elc: %.el autoloads 13 | $(EMACS) -Q -batch -L . --eval \ 14 | "(let ((default-directory (expand-file-name \".cask\" default-directory))) \ 15 | (require 'package) \ 16 | (normal-top-level-add-subdirs-to-load-path))" \ 17 | -f package-initialize -f batch-byte-compile $< 18 | 19 | all: cask $(ELCS) autoloads 20 | 21 | autoloads: $(AUTOLOADS) 22 | 23 | $(AUTOLOADS): $(ELCS) 24 | $(EMACS) -Q -batch -L . --eval \ 25 | "(progn \ 26 | (require 'package) \ 27 | (normal-top-level-add-subdirs-to-load-path) \ 28 | (package-generate-autoloads \"phpactor\" default-directory))" 29 | 30 | cask: .cask/installed 31 | 32 | .cask/installed: Cask 33 | $(CASK) install 34 | @touch .cask/installed 35 | 36 | test: 37 | $(MAKE) unit-test 38 | $(MAKE) ecukes 39 | 40 | unit-test: .cask $(ELCS) 41 | $(CASK) exec ert-runner 42 | 43 | $(PKG_DIR): 44 | touch $@ 45 | 46 | ecukes: 47 | $(CASK) exec ecukes --reporter magnars --script $(FEATURES) --no-win 48 | 49 | version: 50 | @echo $(VERSION) 51 | 52 | tag: 53 | git tag v$(VERSION) && git push origin --tags 54 | 55 | clean: 56 | rm -f $(ELC) $(AUTOLOADS) 57 | 58 | .PHONY: all autoloads cask clean elc ecukes tag test unit-test version 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Emacs interface to Google Translate 2 | 3 | [![Join the chat at https://gitter.im/atykhonov/google-translate](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/atykhonov/google-translate?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 4 | [![MELPA](https://melpa.org/packages/google-translate-badge.svg)](https://melpa.org/#/google-translate) 5 | [![MELPA Stable](https://stable.melpa.org/packages/google-translate-badge.svg)](https://stable.melpa.org/#/google-translate) 6 | 7 | ## Summary 8 | 9 | This package allows to translate the strings using Google Translate 10 | service directly from GNU Emacs. 11 | 12 | ## Installation 13 | 14 | #### From MELPA 15 | 16 | Just run `M-x package-install RET google-translate RET` 17 | 18 | #### Manual installation 19 | 20 | Assuming that the file `google-translate.el` is somewhere on the 21 | load path, add the following lines to your `.emacs` file: 22 | 23 | (require 'google-translate) 24 | (require 'google-translate-default-ui) 25 | (global-set-key "\C-ct" 'google-translate-at-point) 26 | (global-set-key "\C-cT" 'google-translate-query-translate) 27 | 28 | or 29 | 30 | (require 'google-translate) 31 | (require 'google-translate-smooth-ui) 32 | (global-set-key "\C-ct" 'google-translate-smooth-translate) 33 | 34 | The difference between these configurations is in UI which will be 35 | used: Default UI or Smooth UI. 36 | 37 | ## Default UI (google-translate-default-ui.el) 38 | 39 | This file provides default UI for the Google Translate package. It was 40 | originally written by Oleksandr Manzyuk and was part of 41 | google-translate.el. It was extracted to 42 | google-translate-default-ui.el file due to refactoring (the goal of 43 | which is to separate backend from UI and provide better way for having 44 | different UIs for Google Translate package). 45 | 46 | Invoking the function `google-translate-query-translate` queries the 47 | source and target languages and text to translate, and shows a buffer 48 | with available translations of the text. Invoking the function 49 | `google-translate-at-point` translates the word at point or the active 50 | region. 51 | 52 | #### Default UI Customization 53 | 54 | You can customize the following variables: 55 | 56 | - `google-translate-default-source-language` 57 | 58 | - `google-translate-default-target-language` 59 | 60 | If the variable `google-translate-default-source-language` is set 61 | to a non-NIL value, the source language won't be queried and that 62 | value will be used instead. Analogously, if you set the variable 63 | `google-translate-default-target-language` to some non-NIL value, 64 | that value will be used without querying. 65 | 66 | You can always override this behavior by supplying a `C-u` prefix 67 | argument to the function `google-translate-query-translate`. 68 | 69 | Here is an example. Suppose that your native language is Russian 70 | and you frequently need to translate from various languages to 71 | Russian. Then it is reasonable 72 | 73 | - to set the variable `google-translate-default-target-language` 74 | to "ru", and 75 | 76 | - to leave `google-translate-default-source-language` set to its 77 | default value, NIL. 78 | 79 | In this case, the function `google-translate-query-translate` is 80 | only going to query the source language and text to translate. 81 | If you need to translate to some language other than Russian, you 82 | can override the default for the target language by supplying a 83 | `C-u` prefix argument, in which case you will be queried for both 84 | the source and target languages, as well as text to translate. 85 | 86 | If you frequently translate from some fixed language, it is also 87 | reasonable to set `google-translate-default-source-language` to 88 | an appropriate value. 89 | 90 | If you have both the default source and target languages specified, 91 | you may like to bind functions `google-translate-at-point-reverse` 92 | and `google-translate-query-translate-reverse` to some keys, e.g.: 93 | 94 | (global-set-key (kbd "C-c r") 'google-translate-at-point-reverse) 95 | (global-set-key (kbd "C-c R") 'google-translate-query-translate-reverse) 96 | 97 | This will allow you to quickly translate in the reverse direction. 98 | When the default source (resp. target) language is not set, the 99 | target (resp. source) language of the reverse translation will be 100 | queried interactively. 101 | 102 | The admitted values of `google-translate-default-source-language` 103 | and `google-translate-default-target-language` are the codes of the 104 | languages supported by Google Translate (like "ru" for Russian 105 | above). See `google-translate-supported-languages` for the list of 106 | the supported languages, or customize the defaults using the 107 | customization mechanism of Emacs. Setting a default language to 108 | NIL means that language will always be queried. Moreover, the 109 | variable `google-translate-default-source-language` can be set to a 110 | special value "auto" that is interpreted as the instruction for 111 | Google Translate to detect the source language. This option is 112 | also available when you are queried for the source language: simply 113 | leave this parameter blank by pressing RET. (If you have enabled 114 | the ido-style completion, "Detect language" is going to be the 115 | first option, which you can select simply by hitting RET.) 116 | 117 | ## Smooth UI (google-translate-smooth-ui.el) 118 | 119 | Smooth UI is a just alternative to the Default UI. It was written with 120 | mind to provide improved user interface and, especially, to achieve 121 | better supporting of many default languages. Default UI supports two 122 | default languages very well but there is no space for the third one. 123 | 124 | Invoking the function `google-translate-smooth-translate` queries 125 | text and (optionally) the source and target languages to translate, 126 | and shows a buffer with available translations of the text. 127 | 128 | #### Smooth UI Configuration: 129 | 130 | It is reasonable to define the following variable: 131 | 132 | - `google-translate-translation-directions-alist` 133 | 134 | - `google-translate-preferable-input-methods-alist` 135 | 136 | `google-translate-translation-directions-alist` alist is intended 137 | to contain translation directions. 138 | 139 | For example it could be defined (in your .emacs or init.el) as: 140 | 141 | (setq google-translate-translation-directions-alist '(("en" . "ru"))) 142 | 143 | in this way one translation direction ("en" > "ru") is defined and 144 | when `google-translate-smooth-translate` function executes it will 145 | output the prompt (in minibuffer) which will looks like as the 146 | following: 147 | 148 | ``` 149 | [English > Russian] Translate: 150 | ``` 151 | 152 | You may set as many translation directions as you would like 153 | to. For example such piece of code will define four translation 154 | directions: 155 | 156 | ``` 157 | (setq google-translate-translation-directions-alist 158 | '(("de" . "en") ("en" . "de") ("de" . "fr") ("fr" . "de"))) 159 | ``` 160 | 161 | in this way, when `google-translate-smooth-translate` function 162 | executes you'll be queried by the prompt which will looks like the 163 | following: 164 | 165 | ``` 166 | [German > English] Translate: 167 | ``` 168 | 169 | and, also in this way, you'll be able to switch between different 170 | translation directions directly from minibuffer by using `C-n` and 171 | `C-p` key bindings. `C-n` key binding changes current translation 172 | direction to the next direction defined in the 173 | `google-translate-translation-directions-alist` variable. And `C-p` 174 | key binding changes current translation direction to the previous 175 | one. Thus, while executing `google-translate-smooth-translate` 176 | function and having in minibuffer such prompt: 177 | 178 | ``` 179 | [German > English] Translate: 180 | ``` 181 | 182 | then after pressing `C-n` you'll get the following prompt: 183 | 184 | ``` 185 | [English > German] Translate: 186 | ``` 187 | 188 | By default `google-translate-translation-directions-alist` is empty 189 | and thus during execution of `google-translate-smooth-translate` 190 | you'll be queried (to input a text) by the prompt: 191 | 192 | ``` 193 | Translate: 194 | ``` 195 | 196 | And after inputed text you'll be queried also for the source and 197 | target languages. To let the package to be known which languages 198 | you would like to always use and to avoid repetitive language 199 | quering it is reasonable to define them in the mentioned 200 | `google-translate-translation-directions-alist` variable. 201 | 202 | ## Common UI Customization 203 | 204 | Described customization options are actual for both UI features: 205 | Default UI and Smooth UI. 206 | 207 | You can customize the following variables: 208 | 209 | - `google-translate-output-destination` 210 | 211 | - `google-translate-enable-ido-completion` 212 | 213 | - `google-translate-show-phonetic` 214 | 215 | - `google-translate-listen-program` 216 | 217 | - `google-translate-pop-up-buffer-set-focus` 218 | 219 | `google-translate-output-destination` determines translation output 220 | destination. If `nil` the translation output will be displayed in the 221 | pop up buffer. If value equal to `echo-area` then translation outputs 222 | in the Echo Area 223 | (see 224 | [Echo Area](http://www.gnu.org/software/emacs/manual/html_node/elisp/The-Echo-Area.html)). In 225 | case of `popup` the translation outputs to the popup tooltip using 226 | `popup` package. In case of `kill-ring` the translation outputs to the 227 | kill ring. And in case of `current-buffer` the translation outputs to 228 | the current buffer. If you would like output translation to the Echo 229 | Area you would probably like to increase it because only part of 230 | translation could be visible there with the default settings. To 231 | increase Echo Area you could increase the value of 232 | `max-mini-window-height` variable, for example: `(setq 233 | max-mini-window-height 0.5)`. 234 | 235 | If `google-translate-enable-ido-completion` is non-NIL, the input will 236 | be read with ido-style completion. 237 | 238 | The variable `google-translate-show-phonetic` controls whether the 239 | phonetic spelling of the original text and its translation is 240 | displayed if available. If you want to see the phonetics, set this 241 | variable to t. 242 | 243 | The variable `google-translate-listen-program` determines the program 244 | to use to listen translations. By default the program looks for 245 | `mplayer` in the PATH, if `mplayer` is found then listening function 246 | will be available and you'll see `Listen` button in the buffer with 247 | the translation. You can use any other suitable program. If you use 248 | Windows please download and unpack `mplayer` and add its path 249 | (directory) to to the system PATH variable. Please note that 250 | translation listening is not available if 251 | `google-translate-output-destination` is set to `echo-area` or 252 | `pop-up`. 253 | 254 | The variable `google-translate-pop-up-buffer-set-focus` determines 255 | whether window (buffer) with translation gets focus when it pop 256 | ups. If `nil`, it doesn't get focus and focus remains in the same 257 | window as was before translation. If `t`, window (buffer with 258 | translation) gets focus. Please note that that setting works only for 259 | pop up buffer, i.e. when `google-translate-output-destination` is 260 | `nil`. 261 | 262 | The `google-translate-input-method-auto-toggling` variable 263 | determines whether input method auto toggling is enabled or not. 264 | 265 | While switching among languages I noticed that I change input 266 | method quite often. Input method auto toggling allows switch on 267 | appropriate input method while switching among languages. Auto 268 | toggling will work in case of 269 | `google-translate-input-method-auto-toggling` is set to `t` and 270 | `google-translate-preferable-input-methods-alist` is defined 271 | properly. 272 | 273 | This variable may be defined as follow (just for example): 274 | 275 | ``` 276 | (setq google-translate-preferable-input-methods-alist '((nil . ("en")) 277 | (ukrainian-programmer-dvorak . ("ru" "uk")))) 278 | ``` 279 | 280 | In this way, input method is disabled (because of nil) for the 281 | minibuffer when source language is English. And 282 | "ukrainian-programmer-dvorak" input method is enabled when source 283 | language is Russian or Ukrainian. 284 | 285 | #### Customization of faces 286 | 287 | - `google-translate-text-face`, used to display the original text 288 | (defaults to `default`) 289 | 290 | - `google-translate-phonetic-face`, used to display the phonetics 291 | (defaults to `shadow`) 292 | 293 | - `google-translate-translation-face`, used to display the highest 294 | ranking translation (defaults to `default` with the `weight` 295 | attribute set to `bold`) 296 | 297 | - `google-translate-suggestion-label-face` used to display the label 298 | for suggestion (defaults to `default` with the `foreground` 299 | attribute set to `red`) 300 | 301 | - `google-translate-suggestion-face` used to display the suggestion in 302 | case of word is misspelled (defaults to `default` with the `slant` 303 | attribute set to `italic` and `underline` attribute set to `t`) 304 | 305 | - `google-translate-listen-button-face` used to display the "Listen" button (defaults 306 | to `height' 0.8). 307 | 308 | For example, to show the translation in a larger font change the 309 | `height` attribute of the face `google-translate-translation-face` 310 | like so: 311 | 312 | ``` 313 | (set-face-attribute 'google-translate-translation-face nil :height 1.4) 314 | ``` 315 | #### Utilize curl, wget or else as a last resort 316 | 317 | If you have any troubles that relate to http, like `Search failed: ",tkk:'"`, 318 | try to use `curl` or `wget` for the backend method. 319 | 320 | The variable `'google-translate-backend-method` switches the backend 321 | method and currently available symbols are below: 322 | 323 | - emacs: use built in `url-retrieve-synchronously` (default) 324 | - curl: invoke curl 325 | - wget: invoke wget 326 | 327 | So if you prefer curl, put following line to your init.el: 328 | 329 | ``` 330 | (setq google-translate-backend-method 'curl) 331 | ``` 332 | 333 | In case neither curl nor wget is your preference, you can add another 334 | command to the variable `'google-translate-backend-commands` and 335 | employ it, for example: 336 | 337 | ``` 338 | (push '(foo :name "foo-x86" :args ("-q" "--agent")) 339 | google-translate-backend-commands) 340 | (setq google-translate-backend-method 'foo) 341 | ``` 342 | 343 | For further information, please refer to the documentation of 344 | `'google-translate-backend-commands`. 345 | 346 | Additionally, these variables would be useful for troubleshooting: 347 | 348 | - `google-translate-backend-user-agent`, user agent string for HTTP 349 | request header 350 | (defaults to `"Emacs"`) 351 | 352 | - `google-translate-backend-debug`, log URL access activities to the 353 | buffer `*google-translate-backend-debug*` 354 | (defaults to nil) 355 | 356 | ## Contributors 357 | 358 | - Tassilo Horn 359 | - Bernard Hurley 360 | - Chris Bilson 361 | - [Takumi Kinjo](https://github.com/kinjo) 362 | - [momomo5717](https://github.com/momomo5717) 363 | - [Michihito Shigemura](https://github.com/shigemk2) 364 | - [Tomotaka SUWA](https://github.com/t-suwa) 365 | - [stardiviner](https://github.com/stardiviner) 366 | -------------------------------------------------------------------------------- /features/google-translate-default-ui.feature: -------------------------------------------------------------------------------- 1 | Feature: Default UI for Google Translate 2 | 3 | Background: 4 | Given default UI 5 | Given I am in buffer "*Lorem Ipsum*" 6 | And the buffer is empty 7 | And I insert "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum." 8 | 9 | Scenario: Translate a word at point 10 | Given I go to word "dummy" 11 | When I translate word at point from "en" to "ru" 12 | Then I should see translation "манекен" 13 | 14 | Scenario: Translate an empty string 15 | When I translate " " from "en" to "ru" 16 | Then I should see message "Nothing to translate." 17 | 18 | Scenario: Translate a word using query translate 19 | When I translate "book" from "en" to "ru" 20 | Then I should see translation "книга" 21 | 22 | Scenario: Translate inputed word using language auto-detection and ido 23 | Given I set google-translate-enable-ido-completion to t 24 | When I translate "book" from "auto" to "ru" 25 | Then I should see translation "книга" 26 | 27 | Scenario: Translate inputed word using language auto-detection without ido 28 | Given I set google-translate-enable-ido-completion to nil 29 | When I translate "pen" from "auto" to "ru" 30 | Then I should see translation "ручка" 31 | 32 | Scenario: Translate a region 33 | When I go to word "It has" 34 | And I set the mark 35 | And I go to word "but" 36 | And I translate selected region from "en" to "ru" 37 | Then there is no region selected 38 | Then I should see translation "Он пережил не только пять веков," 39 | 40 | Scenario: Translate a word at point using default source language 41 | Given I set google-translate-default-source-language to "en" 42 | Given I go to word "dummy" 43 | When I translate word at point to "ru" 44 | Then I should see translation "манекен" 45 | 46 | Scenario: Translate a word using query translate and default source language 47 | Given I set google-translate-default-source-language to "en" 48 | When I translate "book" to "ru" 49 | Then I should see translation "книга" 50 | 51 | Scenario: Translate a word at point using default target language 52 | Given I set google-translate-default-source-language to nil 53 | Given I set google-translate-default-target-language to "ru" 54 | Given I go to word "dummy" 55 | When I translate word at point from "en" 56 | Then I should see translation "манекен" 57 | 58 | Scenario: Translate a word using query translate and default target language 59 | Given I set google-translate-default-source-language to nil 60 | Given I set google-translate-default-target-language to "ru" 61 | When I translate "book" from "en" 62 | Then I should see translation "книга" 63 | 64 | Scenario: Translate a word at point using defaults source and target language 65 | Given I set google-translate-default-source-language to "en" 66 | Given I set google-translate-default-target-language to "ru" 67 | Given I go to word "dummy" 68 | When I translate word at point 69 | Then I should see translation "манекен" 70 | 71 | Scenario: Translate a word at point using defaults source and target language 72 | Given I set google-translate-default-source-language to "en" 73 | Given I set google-translate-default-target-language to "ru" 74 | Given I go to word "book" 75 | When I translate word at point 76 | Then I should see translation "книга" 77 | 78 | Scenario: Reverse translate word at point 79 | Given I set google-translate-default-source-language to "ru" 80 | Given I set google-translate-default-target-language to "en" 81 | Given I go to word "leap" 82 | When I reverse translate word at point 83 | Then I should see translation "прыжок" 84 | 85 | Scenario: Reverse translate a word 86 | Given I set google-translate-default-source-language to "ru" 87 | Given I set google-translate-default-target-language to "en" 88 | When I reverse translate "printer" 89 | Then I should see translation "принтер" 90 | 91 | Scenario: Translate a word at point using defaults source and target language 92 | Given I set google-translate-default-source-language to "ru" 93 | Given I set google-translate-default-target-language to "en" 94 | Given I go to word "book" 95 | When I reverse translate word at point 96 | Then I should see translation "книга" 97 | 98 | Scenario: Translate a word using query translate and defaults source and target language 99 | Given I set google-translate-default-source-language to "en" 100 | Given I set google-translate-default-target-language to "ru" 101 | When I translate "leap" 102 | Then I should see translation "прыжок" 103 | 104 | Scenario: Translate inputed word using language auto-detection 105 | Given I set google-translate-default-source-language to "auto" 106 | Given I set google-translate-default-target-language to "ru" 107 | When I translate "book" 108 | Then I should see translation "книга" 109 | 110 | Scenario: Suggestion when word is misspelled 111 | Given I set google-translate-default-source-language to "en" 112 | Given I set google-translate-default-target-language to "ru" 113 | When I translate "sugest" 114 | Then I should see suggestion "suggest" 115 | 116 | Scenario: Linked suggestion: click on suggestion 117 | Given I set google-translate-default-source-language to "en" 118 | Given I set google-translate-default-target-language to "ru" 119 | When I translate "sugest" 120 | Then I should see suggestion "suggest" 121 | And I press "TAB" 122 | And I press "TAB" 123 | And I press "TAB" 124 | And I press "RET" 125 | Then I should see translation "предлагать" 126 | 127 | Scenario: Translate a word emphasized with asterisks like *bold* such as in Org mode 128 | Given I insert "You can make words *bold*, /italic/, _underlined_, =verbatim= and ~code~, and, if you must, ‘+strike-through+’." 129 | And I go to word "bold" 130 | When I translate word at point from "en" to "ru" 131 | Then I should see translation "смелый" 132 | 133 | Scenario: Translate a word emphasized with slashes like /italic/ such as in Org mode 134 | Given I insert "You can make words *bold*, /italic/, _underlined_, =verbatim= and ~code~, and, if you must, ‘+strike-through+’." 135 | And I go to word "italic" 136 | When I translate word at point from "en" to "ru" 137 | Then I should see translation "курсив" 138 | 139 | Scenario: Translate a word emphasized with underscores like _underlined_ such as in Org mode 140 | Given I insert "You can make words *bold*, /italic/, _underlined_, =verbatim= and ~code~, and, if you must, ‘+strike-through+’." 141 | And I go to word "underlined" 142 | When I translate word at point from "en" to "ru" 143 | Then I should see translation "подчеркнутый" 144 | 145 | Scenario: Translate a word emphasized with equals signs like =verbatim= such as in Org mode 146 | Given I insert "You can make words *bold*, /italic/, _underlined_, =verbatim= and ~code~, and, if you must, ‘+strike-through+’." 147 | And I go to word "verbatim" 148 | When I translate word at point from "en" to "ru" 149 | Then I should see translation "дословно" 150 | 151 | Scenario: Translate a word emphasized with tildes like ~code~ such as in Org mode 152 | Given I insert "You can make words *bold*, /italic/, _underlined_, =verbatim= and ~code~, and, if you must, ‘+strike-through+’." 153 | And I go to word "code" 154 | When I translate word at point from "en" to "ru" 155 | Then I should see translation "код" 156 | 157 | Scenario: Translate a word emphasized with pluses like ‘+strike-through+’ such as in Org mode 158 | Given I insert "You can make words *bold*, /italic/, _underlined_, =verbatim= and ~code~, and, if you must, ‘+strike-through+’." 159 | And I go to word "strike" 160 | When I translate word at point from "en" to "ru" 161 | Then I should see translation "забастовка" 162 | -------------------------------------------------------------------------------- /features/google-translate-smooth-ui.feature: -------------------------------------------------------------------------------- 1 | Feature: Smooth UI for Google Translate 2 | 3 | Background: 4 | Given smooth UI 5 | Given I am in buffer "*Lorem Ipsum*" 6 | And the buffer is empty 7 | And I insert "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum." 8 | 9 | Scenario: Translate a word at point 10 | Given I go to word "dummy" 11 | When I translate word at point from "en" to "ru" 12 | Then I should see translation "манекен" 13 | 14 | Scenario: Translate inputed word 15 | When I translate "book" from "en" to "ru" 16 | Then I should see translation "книга" 17 | 18 | Scenario: Translate inputed word using language auto-detection 19 | When I translate "book" from "auto" to "ru" 20 | Then I should see translation "книга" 21 | 22 | Scenario: Translate a word using current translation direction 23 | Given I set google-translate-translation-directions-alist to (("en" . "ru") ("ru" . "en")) 24 | When I translate "dummy" 25 | Then I should see translation "манекен" 26 | 27 | Scenario: Translate a word using next translation direction 28 | Given I set google-translate-translation-directions-alist to (("en" . "ru") ("ru" . "en") ("ru" . "uk")) 29 | When I translate "ручка" using 1 direction 30 | Then I should see translation "pen" 31 | 32 | Scenario: Translate a word using previous translation direction 33 | Given I set google-translate-translation-directions-alist to (("en" . "ru") ("ru" . "uk") ("ru" . "en")) 34 | When I translate "ручка" using -1 direction 35 | Then I should see translation "pen" 36 | 37 | Scenario: Last translation direction should be switched to the first one 38 | Given I set google-translate-translation-directions-alist to (("en" . "ru") ("ru" . "en") ("ru" . "uk")) 39 | When I translate "pen" using 3 direction 40 | Then I should see translation "ручка" 41 | 42 | Scenario: Translate using language auto-detection 43 | Given I set google-translate-translation-directions-alist to (("auto" . "ru") ("ru" . "en") ("ru" . "uk")) 44 | When I translate "car" 45 | Then I should see translation "автомобиль" 46 | 47 | Scenario: Translate a region 48 | Given I set google-translate-translation-directions-alist to (("en" . "ru") ("ru" . "en") ("ru" . "uk")) 49 | When I go to word "It has" 50 | And I set the mark 51 | And I go to word "but" 52 | And I translate "" 53 | Then there is no region selected 54 | Then I should see translation "Он пережил не только пять веков," 55 | 56 | Scenario: Suggestion when word is misspelled 57 | Given I set google-translate-translation-directions-alist to (("en" . "ru")) 58 | When I translate "sugest" 59 | Then I should see suggestion "suggest" 60 | 61 | Scenario: Linked suggestion: click on suggestion 62 | Given I set google-translate-translation-directions-alist to (("en" . "ru")) 63 | When I translate "sugest" 64 | Then I should see suggestion "suggest" 65 | And I press "TAB" 66 | And I press "TAB" 67 | And I press "TAB" 68 | And I press "RET" 69 | Then I should see translation "предлагать" 70 | 71 | Scenario: Translate a word emphasized with asterisks like *bold* such as in Org mode 72 | Given I insert "You can make words *bold*, /italic/, _underlined_, =verbatim= and ~code~, and, if you must, ‘+strike-through+’." 73 | And I go to word "bold" 74 | When I translate word at point from "en" to "ru" 75 | Then I should see translation "смелый" 76 | 77 | Scenario: Translate a word emphasized with slashes like /italic/ such as in Org mode 78 | Given I insert "You can make words *bold*, /italic/, _underlined_, =verbatim= and ~code~, and, if you must, ‘+strike-through+’." 79 | And I go to word "italic" 80 | When I translate word at point from "en" to "ru" 81 | Then I should see translation "курсив" 82 | 83 | Scenario: Translate a word emphasized with underscores like _underlined_ such as in Org mode 84 | Given I insert "You can make words *bold*, /italic/, _underlined_, =verbatim= and ~code~, and, if you must, ‘+strike-through+’." 85 | And I go to word "underlined" 86 | When I translate word at point from "en" to "ru" 87 | Then I should see translation "подчеркнутый" 88 | 89 | Scenario: Translate a word emphasized with equals signs like =verbatim= such as in Org mode 90 | Given I insert "You can make words *bold*, /italic/, _underlined_, =verbatim= and ~code~, and, if you must, ‘+strike-through+’." 91 | And I go to word "verbatim" 92 | When I translate word at point from "en" to "ru" 93 | Then I should see translation "дословно" 94 | 95 | Scenario: Translate a word emphasized with tildes like ~code~ such as in Org mode 96 | Given I insert "You can make words *bold*, /italic/, _underlined_, =verbatim= and ~code~, and, if you must, ‘+strike-through+’." 97 | And I go to word "code" 98 | When I translate word at point from "en" to "ru" 99 | Then I should see translation "код" 100 | 101 | Scenario: Translate a word emphasized with pluses like ‘+strike-through+’ such as in Org mode 102 | Given I insert "You can make words *bold*, /italic/, _underlined_, =verbatim= and ~code~, and, if you must, ‘+strike-through+’." 103 | And I go to word "strike" 104 | When I translate word at point from "en" to "ru" 105 | Then I should see translation "забастовка" 106 | -------------------------------------------------------------------------------- /features/step-definitions/google-translate-default-ui-steps.el: -------------------------------------------------------------------------------- 1 | (When "^I translate (via default UI) \"\\([^\"]*\\)\" from \"\\([^\"]*\\)\" to \"\\([^\"]*\\)\"$" 2 | (lambda (text source-language target-language) 3 | (And "I start an action chain") 4 | (And "I press \"M-x\"") 5 | (And "I type \"google-translate-query-translate\"") 6 | (And "I press \"RET\"") 7 | (when (and (stringp source-language) 8 | (> (length source-language) 0)) 9 | (when (not (equal source-language "auto")) 10 | (And (format "I type \"%s\"" 11 | (google-translate-language-display-name source-language)))) 12 | (And "I press \"RET\"")) 13 | (when (and (stringp target-language) 14 | (> (length target-language) 0)) 15 | (And (format "I type \"%s\"" 16 | (google-translate-language-display-name target-language))) 17 | (And "I press \"RET\"")) 18 | (And (format "I type \"%s\"" text)) 19 | (And "I press \"RET\"") 20 | (And "I execute the action chain"))) 21 | 22 | (When "^I translate \"\\([^\"]*\\)\"$" 23 | (lambda (text) 24 | (Given (format "I translate \"%s\" from \"\" to \"\"" text)))) 25 | 26 | (When "^I translate \"\\([^\"]*\\)\" to \"\\([^\"]*\\)\"$" 27 | (lambda (text target-language) 28 | (Given (format "I translate \"%s\" from \"\" to \"%s\"" text target-language)))) 29 | 30 | (When "^I translate \"\\([^\"]*\\)\" from \"\\([^\"]*\\)\"$" 31 | (lambda (text source-language) 32 | (Given (format "I translate \"%s\" from \"%s\" to \"\"" text source-language)))) 33 | 34 | (When "^I translate (via default UI) thing at point from \"\\([^\"]*\\)\" to \"\\([^\"]*\\)\"$" 35 | (lambda (source-language target-language) 36 | (And "I start an action chain") 37 | (And "I press \"M-x\"") 38 | (And "I type \"google-translate-at-point\"") 39 | (And "I press \"RET\"") 40 | (when (and (stringp source-language) 41 | (> (length source-language) 0)) 42 | (when (not (equal source-language "auto")) 43 | (And (format "I type \"%s\"" 44 | (google-translate-language-display-name 45 | source-language)))) 46 | (And "I press \"RET\"")) 47 | (when (and (stringp target-language) 48 | (> (length target-language) 0)) 49 | (And (format "I type \"%s\"" 50 | (google-translate-language-display-name 51 | target-language))) 52 | (And "I press \"RET\"")) 53 | (And "I execute the action chain"))) 54 | 55 | (When "^I reverse translate \"\\([^\"]*\\)\"$" 56 | (lambda (text) 57 | (And "I start an action chain") 58 | (And "I press \"M-x\"") 59 | (And "I type \"google-translate-query-translate-reverse\"") 60 | (And "I press \"RET\"") 61 | (And (format "I type \"%s\"" text)) 62 | (And "I press \"RET\"") 63 | (And "I execute the action chain"))) 64 | 65 | (When "^I reverse translate word at point$" 66 | (lambda () 67 | (And "I start an action chain") 68 | (And "I press \"M-x\"") 69 | (And "I type \"google-translate-at-point-reverse\"") 70 | (And "I press \"RET\"") 71 | (And "I execute the action chain"))) 72 | 73 | (When "^I translate word at point from \"\\([^\"]*\\)\"$" 74 | (lambda (source-language) 75 | (When (format "I translate thing at point from \"%s\" to \"\"" 76 | source-language)))) 77 | 78 | (When "^I translate word at point to \"\\([^\"]*\\)\"$" 79 | (lambda (target-language) 80 | (When (format "I translate thing at point from \"\" to \"%s\"" 81 | target-language)))) 82 | 83 | (When "^I translate word at point$" 84 | (lambda () 85 | (When (format "I translate thing at point from \"\" to \"\"")))) 86 | 87 | (When "^I translate selected region from \"\\(.+\\)\" to \"\\(.+\\)\"$" 88 | (lambda (source-language target-language) 89 | (When (format "I translate thing at point from \"%s\" to \"%s\"" 90 | source-language 91 | target-language)))) 92 | -------------------------------------------------------------------------------- /features/step-definitions/google-translate-smooth-ui-steps.el: -------------------------------------------------------------------------------- 1 | (When "^I translate (via smooth UI) \"\\([^\"]*\\)\" from \"\\([^\"]*\\)\" to \"\\([^\"]*\\)\"$" 2 | (lambda (text source-language target-language) 3 | (And "I start an action chain") 4 | (And "I press \"M-x\"") 5 | (And "I type \"google-translate-smooth-translate\"") 6 | (And "I press \"RET\"") 7 | (when (and (stringp text) 8 | (> (length text) 0)) 9 | (delete-minibuffer-contents) 10 | (And "I type \"%s\"" text)) 11 | (And "I press \"RET\"") 12 | (when (and (stringp source-language) 13 | (> (length source-language) 0)) 14 | (when (not (equal source-language "auto")) 15 | (And (format "I type \"%s\"" 16 | (google-translate-language-display-name source-language)))) 17 | (And "I press \"RET\"")) 18 | (when (and (stringp target-language) 19 | (> (length target-language) 0)) 20 | (And (format "I type \"%s\"" 21 | (google-translate-language-display-name target-language))) 22 | (And "I press \"RET\"")) 23 | (And "I execute the action chain"))) 24 | 25 | (When "^I translate (via smooth UI) thing at point from \"\\([^\"]*\\)\" to \"\\([^\"]*\\)\"$" 26 | (lambda (source-language target-language) 27 | (When (format "I translate \"\" from \"%s\" to \"%s\"" 28 | source-language 29 | target-language)))) 30 | 31 | (When "^I translate \"\\([^\"]*\\)\" using \\(.+\\) direction$" 32 | (lambda (text nth-direction) 33 | (And "I start an action chain") 34 | (And "I press \"M-x\"") 35 | (And "I type \"google-translate-smooth-translate\"") 36 | (And "I press \"RET\"") 37 | (when (and (stringp text) 38 | (> (length text) 0)) 39 | (delete-minibuffer-contents) 40 | (And "I type \"%s\"" text)) 41 | (let ((index 0) 42 | (nth-direction (string-to-int nth-direction))) 43 | (if (> nth-direction 0) 44 | (while (< index nth-direction) 45 | (And "I press \"C-n\"") 46 | (incf index)) 47 | (while (> index nth-direction) 48 | (And "I press \"C-p\"") 49 | (decf index)))) 50 | (And "I press \"RET\"") 51 | (And "I execute the action chain"))) 52 | 53 | ;; (Then "^I should see in the minibuffer \"\\([^\"]*\\)\"$" 54 | ;; (lambda (expected) 55 | ;; (let ((actual (minibuffer-contents-no-properties)) 56 | ;; (message "Expected '%s' to be part of '%s', but was not.")) 57 | ;; (cl-assert (s-contains? expected actual) nil message expected actual)))) 58 | -------------------------------------------------------------------------------- /features/step-definitions/google-translate-steps.el: -------------------------------------------------------------------------------- 1 | (defvar it-google-translate-current-ui "") 2 | 3 | (Given "^\\(.+\\) UI$" 4 | (lambda (ui) 5 | (setq it-google-translate-current-ui ui))) 6 | 7 | (When "^I translate \"\\([^\"]*\\)\" from \"\\([^\"]*\\)\" to \"\\([^\"]*\\)\"$" 8 | (lambda (text source-language target-language) 9 | (When 10 | (format 11 | "I translate (via %s UI) \"%s\" from \"%s\" to \"%s\"" 12 | it-google-translate-current-ui 13 | text 14 | source-language 15 | target-language)))) 16 | 17 | (When "^I translate thing at point from \"\\([^\"]*\\)\" to \"\\([^\"]*\\)\"$" 18 | (lambda (source-language target-language) 19 | (When 20 | (format 21 | "I translate (via %s UI) thing at point from \"%s\" to \"%s\"" 22 | it-google-translate-current-ui 23 | source-language 24 | target-language)))) 25 | 26 | (When "^I translate word at point from \"\\([^\"]*\\)\" to \"\\([^\"]*\\)\"$" 27 | (lambda (source-language target-language) 28 | (When (format "I translate (via %s UI) thing at point from \"%s\" to \"%s\"" 29 | it-google-translate-current-ui 30 | source-language 31 | target-language)))) 32 | 33 | (Then "^I should see translation \"\\(.+\\)\"$" 34 | (lambda (translation) 35 | (Given "I am in buffer \"*Google Translate*\"") 36 | (Then (format "I should see \"%s\"" translation)))) 37 | 38 | (Then "^I should see suggestion \"\\(.+\\)\"$" 39 | (lambda (translation) 40 | (Given "I am in buffer \"*Google Translate*\"") 41 | (Then (format "I should see \"%s\"" translation)))) 42 | -------------------------------------------------------------------------------- /features/support/env.el: -------------------------------------------------------------------------------- 1 | (require 'f) 2 | 3 | (defvar google-translate-support-path 4 | (f-dirname load-file-name)) 5 | 6 | (defvar google-translate-features-path 7 | (f-parent google-translate-support-path)) 8 | 9 | (defvar google-translate-root-path 10 | (f-parent google-translate-features-path)) 11 | 12 | (add-to-list 'load-path google-translate-root-path) 13 | 14 | (require 'google-translate) 15 | (require 'google-translate-default-ui) 16 | (require 'google-translate-smooth-ui) 17 | (require 'espuds) 18 | (require 'ert) 19 | 20 | (Setup 21 | ;; Before anything has run 22 | ) 23 | 24 | (Before 25 | ;; Before each scenario is run 26 | ) 27 | 28 | (After 29 | ;; After each scenario is run 30 | ) 31 | 32 | (Teardown 33 | ;; After when everything has been run 34 | ) 35 | -------------------------------------------------------------------------------- /google-translate-backend.el: -------------------------------------------------------------------------------- 1 | ;;; google-translate-backend.el --- Backend methods for url retrieve. 2 | 3 | ;; Copyright (C) 2019 Tomotaka SUWA 4 | 5 | ;; Author: Oleksandr Manzyuk 6 | ;; Maintainer: Andrey Tykhonov 7 | ;; URL: https://github.com/atykhonov/google-translate 8 | ;; Version: 0.11.17 9 | ;; Keywords: convenience 10 | 11 | ;; This file is NOT part of GNU Emacs. 12 | 13 | ;; This is free software; you can redistribute it and/or modify it 14 | ;; under the terms of the GNU General Public License as published by 15 | ;; the Free Software Foundation; either version 2, or (at your option) 16 | ;; any later version. 17 | 18 | ;; This file is distributed in the hope that it will be useful, but 19 | ;; WITHOUT ANY WARRANTY; without even the implied warranty of 20 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 21 | ;; General Public License for more details. 22 | 23 | ;; You should have received a copy of the GNU General Public License 24 | ;; along with GNU Emacs. If not, see . 25 | 26 | ;;; Commentary: 27 | 28 | ;; Provide backend facilities to cope with google translate. 29 | 30 | ;;; Code: 31 | 32 | (defvar google-translate-backend-method 'emacs 33 | "The backend method for URL retrieve. 34 | 35 | Valid symbols are below: 36 | 37 | - emacs: use built in `url-retrieve-synchronously' 38 | - curl: invoke curl command 39 | - wget: invoke wget command 40 | 41 | and any other keys of `google-translate-backend-commands'.") 42 | 43 | (defvar google-translate-backend-user-agent "Emacs" 44 | "The user agent string for HTTP request header.") 45 | 46 | (defvar google-translate-backend-commands 47 | '((curl :args ("-s" "-L" "-A")) 48 | (wget :args ("-q" "-O" "-" "-U"))) 49 | "An alist of external program specifications. 50 | 51 | The form of each element is (KEY P-LIST). The variable 52 | `google-translate-backend-method' may have one of KEYs and is 53 | used for determine the command to execute. The P-LIST of each 54 | element represents command specific information. 55 | 56 | Available properties: 57 | 58 | - Property `:name': the program name(optional) 59 | - Property `:args': a list of arguments passed to the program 60 | 61 | If you omit the `:name' property, (symbol-name KEY) will be used 62 | as the program name. The `:args' property must be composed to 63 | satisfy all the following conditions: 64 | 65 | - Output content to standard output 66 | - Suppress non-content(HTTP headers, progress messages, etc) 67 | - Handle location response header 68 | - Place User-Agent option at last 69 | 70 | So if you would like to use another program \"foo\", for example: 71 | 72 | \(push \\='(foo :name \"foo-x86\" 73 | :args (\"-q\" \"--agent\")) 74 | google-translate-backend-commands) 75 | 76 | \(setq google-translate-backend-method \\='foo) 77 | 78 | And the command line looks like: 79 | 80 | foo-x86 -q --agent ['google-translate-backend-user-agent] [URL]") 81 | 82 | (defvar google-translate-backend-debug nil 83 | "Non-nil means log http activities to the *google-translate-debug* buffer.") 84 | 85 | (defun google-translate-backend--log (&rest args) 86 | "Log http activities to the *google-translate-debug* buffer along with ARGS. 87 | 88 | Disabled if `google-translate-backend-debug' is nil." 89 | (when google-translate-backend-debug 90 | (let ((message (mapconcat 'identity 91 | (list (current-time-string) 92 | (prin1-to-string args) 93 | "-- begin --" 94 | (buffer-string) 95 | "-- end --") 96 | "\n"))) 97 | (with-current-buffer 98 | (get-buffer-create "*google-translate-backend-debug*") 99 | (goto-char (point-max)) 100 | (insert message) 101 | (newline))))) 102 | 103 | (defun google-translate-backend--emacs (url) 104 | "Get URL contents by `url-retrieve-synchronously'." 105 | (insert 106 | (let ((url-user-agent google-translate-backend-user-agent)) 107 | (with-current-buffer (url-retrieve-synchronously url nil nil 15) 108 | (set-buffer-multibyte t) 109 | (google-translate-backend--log url 'emacs) 110 | (goto-char (point-min)) 111 | (re-search-forward "\n\n") 112 | (prog1 (buffer-substring (point) 113 | (point-max)) 114 | (kill-buffer)))))) 115 | 116 | (defun google-translate-backend--process (url key spec) 117 | "Get URL contents by `call-process'. 118 | 119 | \(KEY SPEC) would be exist in `google-translate-backend-commands'." 120 | (let ((name (or (plist-get spec :name) 121 | (symbol-name key))) 122 | (args (plist-get spec :args)) 123 | (agent google-translate-backend-user-agent)) 124 | (apply 'call-process 125 | (append (list name nil t nil) 126 | args 127 | (list agent url))) 128 | (google-translate-backend--log url key spec))) 129 | 130 | (defun google-translate-backend-retrieve (url) 131 | "Get URL contents via `google-translate-backend-method'." 132 | (let ((method google-translate-backend-method)) 133 | (if (eq method 'emacs) 134 | (google-translate-backend--emacs url) 135 | (let ((spec (cdr (assq method 136 | google-translate-backend-commands)))) 137 | (if (null spec) 138 | (error "Unknown backend method: %s" method) 139 | (google-translate-backend--process url method spec)))))) 140 | 141 | (provide 'google-translate-backend) 142 | ;;; google-translate-backend.el ends here 143 | -------------------------------------------------------------------------------- /google-translate-core-ui.el: -------------------------------------------------------------------------------- 1 | ;;; google-translate-core-ui.el --- The google translate core UI 2 | 3 | ;; Copyright (C) 2012 Oleksandr Manzyuk 4 | 5 | ;; Author: Oleksandr Manzyuk 6 | ;; Maintainer: Andrey Tykhonov 7 | ;; URL: https://github.com/atykhonov/google-translate 8 | ;; Package-Requires: ((emacs "24.3") (popup "0.5.8")) 9 | ;; Version: 0.12.0 10 | ;; Keywords: convenience 11 | 12 | ;; Contributors: 13 | ;; Tassilo Horn 14 | ;; Bernard Hurley 15 | ;; Chris Bilson 16 | ;; Takumi Kinjo 17 | ;; momomo5717 18 | ;; stardiviner 19 | 20 | ;; This file is NOT part of GNU Emacs. 21 | 22 | ;; This is free software; you can redistribute it and/or modify it 23 | ;; under the terms of the GNU General Public License as published by 24 | ;; the Free Software Foundation; either version 2, or (at your option) 25 | ;; any later version. 26 | 27 | ;; This file is distributed in the hope that it will be useful, but 28 | ;; WITHOUT ANY WARRANTY; without even the implied warranty of 29 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 30 | ;; General Public License for more details. 31 | 32 | ;; You should have received a copy of the GNU General Public License 33 | ;; along with GNU Emacs. If not, see . 34 | 35 | ;;; Commentary: 36 | 37 | ;; This script provides the most common functions and variables for 38 | ;; UI. It does not contain any interactive functions and overall is 39 | ;; not going to be used directly by means of 40 | ;; `execute-extended-command' (M-x). Its purpose to provide the most 41 | ;; valuable and useful functionality for packages and scripts which 42 | ;; provide UI. 43 | ;; 44 | ;; The most important functions are the following: 45 | ;; 46 | ;; - `google-translate-translate' 47 | ;; 48 | ;; - `google-translate-read-source-language' 49 | ;; 50 | ;; - `google-translate-read-target-language' 51 | ;; 52 | ;; `google-translate-translate' translates the given text from source 53 | ;; language to target language and shows a translation. 54 | ;; 55 | ;; `google-translate-read-source-language' reads source language from 56 | ;; minibuffer and returns language 57 | ;; abbreviation. `google-translate-read-target-language' reads target 58 | ;; language from minibuffer and returns language abbreviation. 59 | 60 | ;; Customization: 61 | 62 | ;; You can customize the following variables: 63 | ;; 64 | ;; - `google-translate-output-destination' 65 | ;; 66 | ;; - `google-translate-enable-ido-completion' 67 | ;; 68 | ;; - `google-translate-show-phonetic' 69 | ;; 70 | ;; - `google-translate-listen-program' 71 | ;; 72 | ;; - `google-translate-pop-up-buffer-set-focus' 73 | ;; 74 | ;; - `google-translate-preferable-input-methods-alist' 75 | ;; 76 | ;; `google-translate-output-destination' determines translation output 77 | ;; destination. If `nil' the translation output will be displayed in 78 | ;; the pop up buffer. If value equal to `echo-area' then translation 79 | ;; outputs in the Echo Area. In case of `popup' the translation 80 | ;; outputs to the popup tooltip using `popup' package. In case of 81 | ;; `kill-ring' the translation outputs to the kill ring. And in case 82 | ;; of `current-buffer' the translation outputs to the current 83 | ;; buffer. If you would like output translation to the Echo Area you 84 | ;; would probably like to increase it because only part of translation 85 | ;; could visible there with the default settings. To increase Echo 86 | ;; Area you could increase the value of `max-mini-window-height' 87 | ;; variable, for example: `(setq max-mini-window-height 0.5)'. 88 | ;; 89 | ;; If `google-translate-enable-ido-completion' is non-NIL, the input 90 | ;; will be read with ido-style completion. 91 | ;; 92 | ;; The variable `google-translate-show-phonetic' controls whether the 93 | ;; phonetic spelling of the original text and its translation is 94 | ;; displayed if available. If you want to see the phonetics, set this 95 | ;; variable to t. 96 | ;; 97 | ;; The variable `google-translate-listen-program' determines the 98 | ;; program to use to listen translations. By default the program looks 99 | ;; for `mplayer' in the PATH, if `mplayer' is found then listening 100 | ;; function will be available and you'll see `Listen' button in the 101 | ;; buffer with the translation. You can use any other suitable 102 | ;; program. If you use Windows please download and unpack `mplayer' 103 | ;; and add its path (directory) to the system PATH variable. Please 104 | ;; note that translation listening is not available if 105 | ;; `google-translate-output-destination' is set to `echo-area' or 106 | ;; `popup'. 107 | ;; 108 | ;; The variable `google-translate-pop-up-buffer-set-focus' determines 109 | ;; whether window (buffer) with translation gets focus when it pop 110 | ;; ups. If `nil', it doesn't get focus and focus remains in the same 111 | ;; window as was before translation. If `t', window (buffer with 112 | ;; translation) gets focus. Please note that that setting works only 113 | ;; for pop up buffer, i.e. when `google-translate-output-destination' 114 | ;; is `nil'. 115 | ;; 116 | ;; The `google-translate-input-method-auto-toggling' variable 117 | ;; determines whether input method auto toggling is enabled or not. 118 | ;; 119 | ;; While switching among languages I noticed that I change input 120 | ;; method quite often. Input method auto toggling allows switch on 121 | ;; appropriate input method while switching among languages. Auto 122 | ;; toggling will work in case of 123 | ;; `google-translate-input-method-auto-toggling' is set to `t' and 124 | ;; `google-translate-preferable-input-methods-alist' is defined 125 | ;; properly. 126 | ;; 127 | ;; This variable may be defined as follow (just for example): 128 | ;; 129 | ;; (setq google-translate-preferable-input-methods-alist '((nil . ("en")) 130 | ;; (ukrainian-programmer-dvorak . ("ru" "uk")))) 131 | ;; 132 | ;; In this way, input method is disabled (because of nil) for the 133 | ;; minibuffer when source language is English. And 134 | ;; "ukrainian-programmer-dvorak" input method is enabled when source 135 | ;; language is Russian or Ukrainian. 136 | 137 | ;; Customization of faces: 138 | 139 | ;; - `google-translate-text-face', used to display the original text 140 | ;; (defaults to `default') 141 | ;; 142 | ;; - `google-translate-phonetic-face', used to display the phonetics 143 | ;; (defaults to `shadow') 144 | ;; 145 | ;; - `google-translate-translation-face', used to display the highest 146 | ;; ranking translation (defaults to `default' with the `weight' 147 | ;; attribute set to `bold') 148 | ;; 149 | ;; - `google-translate-suggestion-label-face' used to display the 150 | ;; label for suggestion (defaults to `default' with the `foreground' 151 | ;; attribute set to `red') 152 | ;; 153 | ;; - `google-translate-suggestion-face' used to display the suggestion 154 | ;; in case of word is misspelled (defaults to `default' with the 155 | ;; `slant' attribute set to `italic' and `underline' attribute set 156 | ;; to `t') 157 | ;; 158 | ;; - `google-translate-listen-button-face' used to display the "Listen" 159 | ;; button (defaults to `height' 0.8). 160 | ;; 161 | ;; For example, to show the translation in a larger font change the 162 | ;; `height' attribute of the face `google-translate-translation-face' 163 | ;; like so: 164 | ;; 165 | ;; (set-face-attribute 'google-translate-translation-face nil :height 1.4) 166 | ;; 167 | ;; 168 | ;;; Code: 169 | ;; 170 | 171 | (eval-when-compile (require 'cl-lib)) 172 | (require 'google-translate-core) 173 | (require 'ido) 174 | (require 'popup) 175 | (require 'color) 176 | (require 'facemenu) 177 | 178 | (defvar google-translate-supported-languages-alist 179 | '(("Afrikaans" . "af") 180 | ("Albanian" . "sq") 181 | ("Amharic" . "am") 182 | ("Arabic" . "ar") 183 | ("Armenian" . "hy") 184 | ("Azerbaijani" . "az") 185 | ("Basque" . "eu") 186 | ("Belarusian" . "be") 187 | ("Bengali" . "bn") 188 | ("Bosnian" . "bs") 189 | ("Bulgarian" . "bg") 190 | ("Catalan" . "ca") 191 | ("Cebuano" . "ceb") 192 | ("Chichewa" . "ny") 193 | ("Chinese Simplified" . "zh-CN") 194 | ("Chinese Traditional" . "zh-TW") 195 | ("Corsican" . "co") 196 | ("Croatian" . "hr") 197 | ("Czech" . "cs") 198 | ("Danish" . "da") 199 | ("Dutch" . "nl") 200 | ("English" . "en") 201 | ("Esperanto" . "eo") 202 | ("Estonian" . "et") 203 | ("Filipino" . "tl") 204 | ("Finnish" . "fi") 205 | ("French" . "fr") 206 | ("Frisian" . "fy") 207 | ("Galician" . "gl") 208 | ("Georgian" . "ka") 209 | ("German" . "de") 210 | ("Greek" . "el") 211 | ("Gujarati" . "gu") 212 | ("Haitian Creole" . "ht") 213 | ("Hausa" . "ha") 214 | ("Hawaiian" . "haw") 215 | ("Hebrew" . "iw") 216 | ("Hindi" . "hi") 217 | ("Hmong" . "hmn") 218 | ("Hungarian" . "hu") 219 | ("Icelandic" . "is") 220 | ("Igbo" . "ig") 221 | ("Indonesian" . "id") 222 | ("Irish" . "ga") 223 | ("Italian" . "it") 224 | ("Japanese" . "ja") 225 | ("Javanese" . "jw") 226 | ("Kannada" . "kn") 227 | ("Kazakh" . "kk") 228 | ("Khmer" . "km") 229 | ("Korean" . "ko") 230 | ("Kurdish (Kurmanji)" . "ku") 231 | ("Kyrgyz" . "ky") 232 | ("Lao" . "lo") 233 | ("Latin" . "la") 234 | ("Latvian" . "lv") 235 | ("Lithuanian" . "lt") 236 | ("Luxembourgish" . "lb") 237 | ("Macedonian" . "mk") 238 | ("Malagasy" . "mg") 239 | ("Malay" . "ms") 240 | ("Malayalam" . "ml") 241 | ("Maltese" . "mt") 242 | ("Maori" . "mi") 243 | ("Marathi" . "mr") 244 | ("Mongolian" . "mn") 245 | ("Myanmar (Burmese)" . "my") 246 | ("Nepali" . "ne") 247 | ("Norwegian" . "no") 248 | ("Pashto" . "ps") 249 | ("Persian" . "fa") 250 | ("Polish" . "pl") 251 | ("Portuguese" . "pt") 252 | ("Punjabi" . "pa") 253 | ("Romanian" . "ro") 254 | ("Russian" . "ru") 255 | ("Samoan" . "sm") 256 | ("Scots Gaelic" . "gd") 257 | ("Serbian" . "sr") 258 | ("Sesotho" . "st") 259 | ("Shona" . "sn") 260 | ("Sindhi" . "sd") 261 | ("Sinhala" . "si") 262 | ("Slovak" . "sk") 263 | ("Slovenian" . "sl") 264 | ("Somali" . "so") 265 | ("Spanish" . "es") 266 | ("Sundanese" . "su") 267 | ("Swahili" . "sw") 268 | ("Swedish" . "sv") 269 | ("Tajik" . "tg") 270 | ("Tamil" . "ta") 271 | ("Telugu" . "te") 272 | ("Thai" . "th") 273 | ("Turkish" . "tr") 274 | ("Ukrainian" . "uk") 275 | ("Urdu" . "ur") 276 | ("Uzbek" . "uz") 277 | ("Vietnamese" . "vi") 278 | ("Welsh" . "cy") 279 | ("Xhosa" . "xh") 280 | ("Yiddish" . "yi") 281 | ("Yoruba" . "yo") 282 | ("Zulu" . "zu")) 283 | "Alist of the languages supported by Google Translate. 284 | 285 | Each element is a cons-cell of the form (NAME . CODE), where NAME 286 | is a human-readable language name and CODE is its code used as a 287 | query parameter in HTTP requests.") 288 | 289 | (defvar google-translate-translation-listening-debug nil 290 | "For debug translation listening purposes.") 291 | 292 | (cl-defstruct gtos 293 | "google translate output structure contains miscellaneous 294 | information which intended to be outputed to the buffer, echo 295 | area or popup tooltip." 296 | source-language target-language text 297 | auto-detected-language text-phonetic translation 298 | translation-phonetic detailed-translation suggestion detailed-definition) 299 | 300 | (defgroup google-translate-core-ui nil 301 | "Emacs core UI script for the Google Translate package." 302 | :group 'processes) 303 | 304 | (defcustom google-translate-enable-ido-completion nil 305 | "If non-NIL, use `ido-completing-read' rather than `completing-read' for reading input." 306 | :group 'google-translate-core-ui 307 | :type '(choice (const :tag "No" nil) 308 | (other :tag "Yes" t))) 309 | 310 | (defcustom google-translate-show-phonetic nil 311 | "If non-NIL, try to show the phonetic spelling." 312 | :group 'google-translate-core-ui 313 | :type '(choice (const :tag "No" nil) 314 | (const :tag "Yes" t))) 315 | 316 | (defcustom google-translate-listen-program 317 | (executable-find "mplayer") 318 | "The program to use to listen translations. 319 | 320 | By default the program looks for `mplayer' in the PATH, if 321 | `mplayer' is found then listening function will be available and 322 | you'll see `Listen' button in the buffer with the translation. 323 | You can use any other suitable program." 324 | :group 'google-translate-core-ui 325 | :type '(string)) 326 | 327 | (defcustom google-translate-output-destination 328 | nil 329 | "Determines where translation output will be displayed. 330 | 331 | - If it is `nil', output to temporary pop up buffer (default). 332 | - `echo-area': output to the Echo Area. 333 | - `popup': output to the popup tooltip using `popup' package. 334 | - `kill-ring': the output will be added in `kill-ring'. 335 | - `current-buffer': the output will be inserted to current buffer. 336 | - `help': output to help buffer. 337 | - `paragraph-overlay': output in current buffer overlay paragraph by paragraph. 338 | - `paragraph-insert': output will be inserted in buffer paragraph by paragraph." 339 | :group 'google-translate-core-ui 340 | :type '(repeat (choice (const :tag "temporary popup buffer" nil) 341 | (const :tag "Echo Area" echo-area) 342 | (const :tag "popup tooltip using popup.el" popup) 343 | (const :tag "kill-ring" kill-ring) 344 | (const :tag "current buffer" current-buffer) 345 | (const :tag "popup Help buffer" help) 346 | (const :tag "paragraph-by-paragraph in overlay" paragraph-overlay) 347 | (const :tag "paragraph-by-paragraph inserted in buffer" paragraph-insert)))) 348 | 349 | (defcustom google-translate-pop-up-buffer-set-focus 350 | nil 351 | "Determines whether result window (buffer) gets focus when it pop ups. 352 | 353 | If nil, it doesn't get focus and focus remains in the same 354 | window as was before translation. If t, window (buffer with 355 | translation) gets focus.") 356 | 357 | (defcustom google-translate-display-translation-phonetic t 358 | "Determines whether display phonetic transcription of the translating text.") 359 | 360 | (defcustom google-translate-listen-button-label 361 | "[Listen]" 362 | "Label of the 'Listen' button." 363 | :group 'google-translate-core-ui 364 | :type 'string) 365 | 366 | (defcustom google-translate-translation-to-kill-ring nil 367 | "Add translation to kill-ring after translate commands if it's `t'." 368 | :type 'boolean 369 | :safe #'booleanp 370 | :group 'google-translate-core-ui) 371 | 372 | (define-derived-mode google-translate-mode 373 | help-mode "Google Translate" 374 | "Major mode for google translate output.") 375 | 376 | (defvar google-translate-mode-mode-map 377 | (let ((map (make-sparse-keymap))) 378 | map) 379 | "Keymap for `google-translate-mode'.") 380 | 381 | (defface google-translate-text-face 382 | '((t (:inherit default))) 383 | "Face used to display the original text." 384 | :group 'google-translate-core-ui) 385 | 386 | (defface google-translate-phonetic-face 387 | '((t (:inherit shadow))) 388 | "Face used to display the phonetic spelling." 389 | :group 'google-translate-core-ui) 390 | 391 | (defface google-translate-translation-face 392 | '((t (:weight bold))) 393 | "Face used to display the probable translation." 394 | :group 'google-translate-core-ui) 395 | 396 | (defface google-translate-suggestion-label-face 397 | '((t (:foreground "red"))) 398 | "Face used to display the suggestion label." 399 | :group 'google-translate-core-ui) 400 | 401 | (defface google-translate-suggestion-face 402 | '((t (:slant italic :underline t))) 403 | "Face used to display the suggestion." 404 | :group 'google-translate-core-ui) 405 | 406 | (defface google-translate-listen-button-face 407 | '((t (:inherit button :height 0.8))) 408 | "Face used to display button \"Listen\"." 409 | :group 'google-translate-core-ui) 410 | 411 | (defvar google-translate-input-method-auto-toggling nil 412 | "When t, the current source language is compared. 413 | 414 | Compared with the values from 415 | `google-translate-preferable-input-methods-alist' and enables 416 | appropriate input method for the minibuffer. So this feature may 417 | allow to avoid switching between input methods while translating 418 | using different languages.") 419 | 420 | (defvar google-translate-preferable-input-methods-alist 421 | '((nil . nil)) 422 | "Alist of preferable input methods for certain languages. 423 | 424 | Each element is a cons-cell of the form (INPUT-METHOD 425 | . LANGUAGES-LIST), where INPUT-METHOD is the input method which 426 | will be switched on, when translation source language equals to 427 | one of the language from the LANGUAGE-LIST. 428 | 429 | INPUT-METHOD could be specified as nil. In such case input method 430 | disables. 431 | 432 | As example, this alist could looks like the following: 433 | 434 | '((nil . \"en\") 435 | (ukrainian-programmer-dvorak . (\"ru\" \"uk\"))) 436 | 437 | In this way, `ukrainian-programmer-dvorak' will be auto enabled 438 | for the minibuffer when Russian or Ukrainian (as source language) 439 | is active.") 440 | 441 | 442 | (defvar google-translate-result-translation nil 443 | "The result translation of `google-translate-translate'.") 444 | 445 | (defun google-translate-supported-languages () 446 | "Return a list of names of languages supported by Google Translate." 447 | (mapcar #'car google-translate-supported-languages-alist)) 448 | 449 | (defun google-translate-language-abbreviation (language) 450 | "Return the abbreviation of LANGUAGE." 451 | (if (string-equal language "Detect language") 452 | "auto" 453 | (cdr (assoc language google-translate-supported-languages-alist)))) 454 | 455 | (defun google-translate-language-display-name (abbreviation) 456 | "Return a name suitable for use in prompts of the language whose abbreviation is ABBREVIATION." 457 | (if (string-equal abbreviation "auto") 458 | "unspecified language" 459 | (car (rassoc abbreviation google-translate-supported-languages-alist)))) 460 | 461 | (defun google-translate-paragraph (text face &optional output-format) 462 | "Return TEXT as a filled paragraph into the current buffer. 463 | And apply FACE to it. Optionally use OUTPUT-FORMAT." 464 | (let ((beg (point)) 465 | (output-format 466 | (if output-format output-format "\n%s\n"))) 467 | (with-temp-buffer 468 | (insert (format output-format text)) 469 | (facemenu-set-face face beg (point)) 470 | (fill-region beg (point)) 471 | (buffer-substring (point-min) (point-max))))) 472 | 473 | (defun google-translate-setup-preferable-input-method (source-language) 474 | "Set input method which takes from the value of 475 | `google-translate-preferable-input-methods-alist' variable." 476 | (interactive) 477 | (let* ((preferable-input-method 478 | (google-translate-find-preferable-input-method source-language))) 479 | (set-input-method preferable-input-method))) 480 | 481 | (defun google-translate-find-preferable-input-method (source-language) 482 | "Look for the SOURCE-LANGUAGE in the 483 | `google-translate-preferable-input-methods-alist' and return 484 | input method for it." 485 | (let ((input-method nil)) 486 | (dolist (item google-translate-preferable-input-methods-alist) 487 | (dolist (language (cdr item)) 488 | (when (string-equal source-language language) 489 | (setq input-method (car item))))) 490 | input-method)) 491 | 492 | (defun google-translate--translation-title (gtos format) 493 | "Return translation title which contains information about used 494 | source and target languages." 495 | (let ((source-language (gtos-source-language gtos)) 496 | (target-language (gtos-target-language gtos)) 497 | (auto-detected-language (gtos-auto-detected-language gtos))) 498 | (format format 499 | (if (string-equal source-language "auto") 500 | (format "%s (detected)" 501 | (google-translate-language-display-name 502 | auto-detected-language)) 503 | (google-translate-language-display-name 504 | source-language)) 505 | (google-translate-language-display-name 506 | target-language)))) 507 | 508 | (defun google-translate--translating-text (gtos format) 509 | "Outputs in buffer translating text." 510 | (let ((text (gtos-text gtos))) 511 | (let ((output-format format)) 512 | (google-translate-paragraph 513 | text 514 | 'google-translate-text-face 515 | output-format)))) 516 | 517 | (defun google-translate--text-phonetic (gtos format) 518 | "Outputs in buffer text phonetic in case of 519 | `google-translate-show-phonetic' is set to t." 520 | (let ((text-phonetic (gtos-text-phonetic gtos))) 521 | (if (and google-translate-show-phonetic 522 | (not (string-equal text-phonetic ""))) 523 | (google-translate-paragraph 524 | text-phonetic 525 | 'google-translate-phonetic-face 526 | format) 527 | ""))) 528 | 529 | (defun google-translate--translated-text (gtos format) 530 | "Output in buffer translation." 531 | (let ((translation (gtos-translation gtos))) 532 | (google-translate-paragraph 533 | translation 534 | 'google-translate-translation-face 535 | format))) 536 | 537 | (defun google-translate--translation-phonetic (gtos format) 538 | "Output in buffer translation phonetic in case of 539 | `google-translate-show-phonetic' is set to t." 540 | (let ((translation-phonetic (gtos-translation-phonetic gtos))) 541 | (if (and google-translate-show-phonetic 542 | (not (string-equal translation-phonetic ""))) 543 | (google-translate-paragraph 544 | translation-phonetic 545 | 'google-translate-phonetic-face 546 | format) 547 | ""))) 548 | 549 | (defun google-translate--detailed-translation (detailed-translation translation 550 | format1 551 | format2) 552 | "Return detailed translation." 553 | (with-temp-buffer 554 | (cl-loop for item across detailed-translation do 555 | (let ((index 0) 556 | (label (aref item 0))) 557 | (unless (string-equal label "") 558 | (put-text-property 0 (length label) 559 | 'font-lock-face 560 | 'google-translate-translation-face 561 | label) 562 | (insert (format format1 label)) 563 | (cl-loop for translation across (aref item 2) do 564 | (let ((content 565 | (format "%s (%s)" 566 | (aref translation 0) 567 | (mapconcat 'identity 568 | (aref translation 1) 569 | ", ")))) 570 | (insert (format format2 571 | (cl-incf index) 572 | content))))))) 573 | (buffer-substring (point-min) (point-max)))) 574 | 575 | (defun google-translate--detailed-definition (detailed-definition definition 576 | format1 577 | format2) 578 | "Return detailed definition." 579 | (with-temp-buffer 580 | (let ((section "DEFINITION")) 581 | (put-text-property 0 (length section) 582 | 'font-lock-face 583 | 'google-translate-translation-face 584 | section) 585 | (insert (format "\n%s\n" section))) 586 | (cl-loop for item across detailed-definition do 587 | (let ((index 0) 588 | (label (aref item 0))) 589 | (unless (string-equal label "") 590 | (put-text-property 0 (length label) 591 | 'font-lock-face 592 | 'google-translate-translation-face 593 | label) 594 | (insert (format format1 label)) 595 | (cl-loop for definition across (aref item 1) do 596 | (insert (format format2 597 | (cl-incf index) 598 | (if (> (length definition) 2) 599 | (format "%s\n \"%s\"" 600 | (aref definition 0) 601 | (aref definition 2)) 602 | (format "%s" (aref definition 0))))))))) 603 | (buffer-substring (point-min) (point-max)))) 604 | 605 | (defun google-translate--suggestion (gtos) 606 | "Return suggestion." 607 | (let ((source-language (gtos-source-language gtos)) 608 | (target-language (gtos-target-language gtos)) 609 | (suggestion (gtos-suggestion gtos))) 610 | (if suggestion 611 | (with-temp-buffer 612 | (insert "\n") 613 | (let ((beg (point))) 614 | (insert "Did you mean: ") 615 | (facemenu-set-face 'google-translate-suggestion-label-face 616 | beg (point))) 617 | (goto-char (+ (point) 1)) 618 | (let ((beg (point))) 619 | (insert-text-button suggestion 620 | 'action 'google-translate--suggestion-action 621 | 'follow-link t 622 | 'suggestion suggestion 623 | 'source-language source-language 624 | 'target-language target-language) 625 | (facemenu-set-face 'google-translate-suggestion-face 626 | beg (point)) 627 | (insert "\n")) 628 | (buffer-substring (point-min) (point-max))) 629 | ""))) 630 | 631 | (defun google-translate--suggestion-action (button) 632 | "Suggestion action which occur when suggestion button is 633 | clicked." 634 | (interactive) 635 | (let ((suggestion (button-get button 'suggestion)) 636 | (source-language (button-get button 'source-language)) 637 | (target-language (button-get button 'target-language))) 638 | (google-translate-translate source-language 639 | target-language 640 | suggestion))) 641 | 642 | (defun google-translate--listen-button (language text) 643 | "Return listen button." 644 | (with-temp-buffer 645 | (insert " ") 646 | (insert-text-button google-translate-listen-button-label 647 | 'action 'google-translate--listen-action 648 | 'face 'google-translate-listen-button-face 649 | 'follow-link t 650 | 'text text 651 | 'language language) 652 | (insert "\n") 653 | (buffer-substring (point-min) (point-max)))) 654 | 655 | (defun google-translate--listen-action (button) 656 | "Do translation listening." 657 | (interactive) 658 | (let ((text (button-get button 'text)) 659 | (language (button-get button 'language))) 660 | (google-translate-listen-translation language text))) 661 | 662 | (defun google-translate-listen-translation (language text) 663 | (let ((buf "*mplayer output*")) 664 | (message "Retrieving audio message...") 665 | (if google-translate-translation-listening-debug 666 | (with-current-buffer (get-buffer-create buf) 667 | (insert (format "Listen program: %s\r\n" google-translate-listen-program)) 668 | (mapc (lambda (x) (insert (format "Listen URL: %s\r\n" x))) 669 | (google-translate-format-listen-urls text language)) 670 | (apply 'call-process google-translate-listen-program nil t nil 671 | (google-translate-format-listen-urls text language)) 672 | (switch-to-buffer buf)) 673 | (apply 'call-process google-translate-listen-program nil nil nil 674 | (google-translate-format-listen-urls text language))))) 675 | 676 | (defun google-translate-translate (source-language target-language text &optional output-destination) 677 | "Translate TEXT from SOURCE-LANGUAGE to TARGET-LANGUAGE. 678 | 679 | About the OUTPUT-DESTINATION, check out option 680 | `google-translate-output-destination'. 681 | 682 | To deal with multi-line regions, sequences of white space 683 | are replaced with a single space. If the region contains not text, a 684 | message is printed. 685 | 686 | At last will save result translation to `google-translate-result-translation'." 687 | (let* ((json (google-translate-request source-language 688 | target-language 689 | text))) 690 | (if (null json) 691 | (message "Nothing to translate.") 692 | (let* ((detailed-translation 693 | (google-translate-json-detailed-translation json)) 694 | (detailed-definition 695 | (google-translate-json-detailed-definition json)) 696 | (gtos 697 | (make-gtos 698 | :source-language source-language 699 | :target-language target-language 700 | :auto-detected-language (aref json 2) 701 | :text text 702 | :text-phonetic (google-translate-json-text-phonetic json) 703 | :translation (google-translate-json-translation json) 704 | :translation-phonetic (if google-translate-display-translation-phonetic 705 | (google-translate-json-translation-phonetic json) "") 706 | :detailed-translation detailed-translation 707 | :detailed-definition detailed-definition 708 | :suggestion (when (null detailed-translation) 709 | (google-translate-json-suggestion json)))) 710 | (output-destination (if (null output-destination) 711 | google-translate-output-destination 712 | output-destination))) 713 | (cond 714 | ((null output-destination) 715 | (google-translate-buffer-output-translation gtos)) 716 | ((equal output-destination 'echo-area) 717 | (google-translate-echo-area-output-translation gtos)) 718 | ((equal output-destination 'popup) 719 | (google-translate-popup-output-translation gtos)) 720 | ((equal output-destination 'kill-ring) 721 | (google-translate-kill-ring-output-translation gtos)) 722 | ((equal output-destination 'current-buffer) 723 | (google-translate-current-buffer-output-translation gtos)) 724 | ((equal output-destination 'help) 725 | (let ((describe-func 726 | (function 727 | (lambda (gtos) 728 | (google-translate-help-buffer-output-translation gtos))))) 729 | (help-setup-xref (list 'google-translate-translate source-language target-language text) nil) 730 | (with-help-window (help-buffer) 731 | (funcall describe-func gtos)))) 732 | ((equal output-destination 'paragraph-overlay) 733 | (google-translate-paragraph-overlay-output-translation gtos)) 734 | ((equal output-destination 'paragraph-insert) 735 | (google-translate-paragraph-insert-output-translation gtos))) 736 | (setq google-translate-result-translation (gtos-translation gtos)) 737 | (when google-translate-translation-to-kill-ring 738 | (kill-new google-translate-result-translation)))))) 739 | 740 | (defun google-translate-popup-output-translation (gtos) 741 | "Output translation to the popup tooltip using `popup' 742 | package." 743 | (require 'popup) 744 | (popup-tip 745 | (with-temp-buffer 746 | (google-translate-insert-translation gtos) 747 | (google-translate--trim-string 748 | (buffer-substring (point-min) (point-max)))))) 749 | 750 | (defun google-translate-echo-area-output-translation (gtos) 751 | "Output translation to the echo area (See 752 | http://www.gnu.org/software/emacs/manual/html_node/elisp/The-Echo-Area.html)" 753 | (message 754 | (with-temp-buffer 755 | (google-translate-insert-translation gtos) 756 | (google-translate--trim-string 757 | (buffer-substring (point-min) (point-max)))))) 758 | 759 | (defun google-translate-kill-ring-output-translation (gtos) 760 | "Output translation to the kill ring." 761 | (kill-new 762 | (with-temp-buffer 763 | (insert 764 | (gtos-translation gtos)) 765 | (google-translate--trim-string 766 | (buffer-substring (point-min) (point-max))))) 767 | (message "Translated text was added to the kill ring.")) 768 | 769 | (defun google-translate-current-buffer-output-translation (gtos) 770 | "Output translation to current buffer." 771 | (insert 772 | (gtos-translation gtos)) 773 | (message "Translated text was added to current buffer.")) 774 | 775 | (defun google-translate-insert-translation (gtos) 776 | "Insert translation to the current buffer." 777 | (let ((translation (gtos-translation gtos)) 778 | (detailed-translation (gtos-detailed-translation gtos))) 779 | (insert 780 | (google-translate--translation-title gtos "%s -> %s:") 781 | (google-translate--translating-text gtos " %s") 782 | (google-translate--text-phonetic gtos " [%s]") 783 | " - " 784 | (google-translate--translated-text gtos "%s") 785 | (google-translate--translation-phonetic gtos " [%s]") 786 | (if detailed-translation 787 | (google-translate--detailed-translation 788 | detailed-translation translation 789 | "\n* %s " "%d. %s ") 790 | (google-translate--suggestion gtos))))) 791 | 792 | (defun google-translate-buffer-output-translation (gtos) 793 | "Output translation to the temp buffer." 794 | (let ((buffer-name "*Google Translate*")) 795 | (with-output-to-temp-buffer buffer-name 796 | (if google-translate-pop-up-buffer-set-focus 797 | (select-window (display-buffer buffer-name)) 798 | (set-buffer buffer-name)) 799 | (google-translate-buffer-insert-translation gtos)))) 800 | 801 | (defun google-translate-paragraph-overlay-output-translation (gtos) 802 | "Output translation below the paragraph with overlay." 803 | (let ((start (save-excursion (start-of-paragraph-text) (point))) 804 | (end (save-excursion (end-of-paragraph-text) (point))) 805 | (below-paragraph (if (eq major-mode 'org-mode) 806 | (save-excursion (previous-line) (point)) 807 | (save-excursion (end-of-paragraph-text) (forward-line) (point)))) 808 | (translation (gtos-translation gtos))) 809 | (with-silent-modifications 810 | (put-text-property 811 | (1- below-paragraph) below-paragraph 812 | 'display (propertize 813 | (concat "\n\n" translation "\n") 814 | 'face `(:background ,(color-darken-name (face-background 'default) 4))))))) 815 | 816 | (defun google-translate-paragraph-insert-output-translation (gtos) 817 | "Insert translation below the paragraph with overlay." 818 | (let ((start (save-excursion (start-of-paragraph-text) (point))) 819 | (end (save-excursion (end-of-paragraph-text) (point))) 820 | (below-paragraph (save-excursion (end-of-paragraph-text) (forward-line) (point))) 821 | (translation (gtos-translation gtos))) 822 | (goto-char below-paragraph) 823 | (insert (concat "\n" translation "\n")))) 824 | 825 | (defun google-translate-help-buffer-output-translation (gtos) 826 | "Output translation to the help buffer." 827 | (and google-translate-pop-up-buffer-set-focus 828 | (select-window (display-buffer "*Help*"))) 829 | (google-translate-buffer-insert-translation gtos)) 830 | 831 | (defun google-translate-buffer-insert-translation (gtos) 832 | "Insert translation to the current temp buffer." 833 | (let ((translation (gtos-translation gtos)) 834 | (detailed-translation (gtos-detailed-translation gtos)) 835 | (detailed-definition (gtos-detailed-definition gtos)) 836 | (source-language (gtos-source-language gtos)) 837 | (target-language (gtos-target-language gtos)) 838 | (auto-detected-language (gtos-auto-detected-language gtos)) 839 | (text (gtos-text gtos))) 840 | (insert 841 | (google-translate--translation-title gtos "Translate from %s to %s:\n") 842 | "\n" 843 | (google-translate--translating-text 844 | gtos 845 | (if (null google-translate-listen-program) 846 | "%s\n" 847 | "%s")) 848 | (if google-translate-listen-program 849 | (google-translate--listen-button 850 | (if (string-equal source-language "auto") 851 | auto-detected-language 852 | source-language) text) "") 853 | (google-translate--text-phonetic gtos "\n%s\n") 854 | "\n" 855 | (google-translate--translated-text 856 | gtos 857 | (if (null google-translate-listen-program) 858 | "%s\n" 859 | "%s")) 860 | (if google-translate-listen-program 861 | (google-translate--listen-button target-language translation) "") 862 | (google-translate--translation-phonetic gtos "\n%s\n") 863 | (if detailed-translation 864 | (google-translate--detailed-translation 865 | detailed-translation translation 866 | "\n%s\n" "%2d. %s\n") 867 | (google-translate--suggestion gtos)) 868 | (if detailed-definition 869 | (google-translate--detailed-definition 870 | detailed-definition translation 871 | "\n%s\n" "%2d. %s\n") 872 | "")) 873 | (google-translate-mode))) 874 | 875 | (defun google-translate-read-source-language (&optional prompt) 876 | "Read a source language, with completion, and return its abbreviation. 877 | 878 | The null input is equivalent to \"Detect language\"." 879 | (let ((completion-ignore-case t) 880 | (prompt 881 | (if (null prompt) 882 | "Translate from: " 883 | prompt))) 884 | (google-translate-language-abbreviation 885 | (google-translate-completing-read 886 | prompt 887 | (google-translate-supported-languages) 888 | "Detect language")))) 889 | 890 | (defun google-translate-read-target-language (&optional prompt) 891 | "Read a target language, with completion, and return its abbreviation. 892 | 893 | The input is guaranteed to be non-null." 894 | (let ((completion-ignore-case t) 895 | (prompt 896 | (if (null prompt) 897 | "Translate to: " 898 | prompt))) 899 | (cl-flet ((read-language () 900 | (google-translate-completing-read 901 | prompt 902 | (google-translate-supported-languages)))) 903 | (let ((target-language (read-language))) 904 | (while (string-equal target-language "") 905 | (setq target-language (read-language))) 906 | (google-translate-language-abbreviation target-language))))) 907 | 908 | (defun google-translate-completing-read (prompt choices &optional def) 909 | "Read a string in the minibuffer with completion. 910 | 911 | If `google-translate-enable-ido-completion' is non-NIL, use 912 | ido-style completion." 913 | (funcall (if google-translate-enable-ido-completion 914 | #'ido-completing-read 915 | #'completing-read) 916 | prompt choices nil t nil nil def)) 917 | 918 | (provide 'google-translate-core-ui) 919 | 920 | ;;; google-translate-core-ui.el ends here 921 | -------------------------------------------------------------------------------- /google-translate-core.el: -------------------------------------------------------------------------------- 1 | ;;; google-translate-core.el --- google-translate core script. 2 | 3 | ;; Copyright (C) 2012 Oleksandr Manzyuk 4 | 5 | ;; Author: Oleksandr Manzyuk 6 | ;; Maintainer: Andrey Tykhonov 7 | ;; URL: https://github.com/atykhonov/google-translate 8 | ;; Package-Requires: ((emacs "25.1")) 9 | ;; Version: 0.12.0 10 | ;; Keywords: convenience 11 | 12 | ;; Contributors: 13 | ;; Tassilo Horn 14 | ;; Bernard Hurley 15 | ;; Chris Bilson 16 | ;; Takumi Kinjo 17 | ;; momomo5717 18 | ;; stardiviner 19 | 20 | ;; This file is NOT part of GNU Emacs. 21 | 22 | ;; This is free software; you can redistribute it and/or modify it 23 | ;; under the terms of the GNU General Public License as published by 24 | ;; the Free Software Foundation; either version 2, or (at your option) 25 | ;; any later version. 26 | 27 | ;; This file is distributed in the hope that it will be useful, but 28 | ;; WITHOUT ANY WARRANTY; without even the implied warranty of 29 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 30 | ;; General Public License for more details. 31 | 32 | ;; You should have received a copy of the GNU General Public License 33 | ;; along with GNU Emacs. If not, see . 34 | 35 | ;;; Commentary: 36 | 37 | ;; This file is the core of `google-translate' package. It does contains 38 | ;; the most important and vital functions and variables for the 39 | ;; package functionality. 40 | ;; 41 | ;; The most important is the `google-translate-request' function which 42 | ;; is intended to be used by other scripts and packages, expecially by 43 | ;; the packages which provides UI. 44 | ;; 45 | ;; This, `google-translate-core', package doesn't provide any UI. 46 | ;; 47 | ;; `google-translate-request' function sends http request to the 48 | ;; google service and returns JSON response which contains translation 49 | ;; and other info. 50 | ;; 51 | ;; There are also a set of helper functions which are going to be 52 | ;; useful to retrieve data from the mentioned JSON response: 53 | ;; 54 | ;; `google-translate-json-text-phonetic' - retrieves text phonetic; 55 | ;; 56 | ;; `google-translate-json-translation' - retrieves translation; 57 | ;; 58 | ;; `google-translate-json-translation-phonetic' - retrieves 59 | ;; phonetic translation; 60 | ;; 61 | ;; `google-translate-json-detailed-translation' - retrieves 62 | ;; additional, detailed information which relates to the 63 | ;; translation. 64 | ;; 65 | 66 | ;;; Code: 67 | 68 | (eval-when-compile (require 'cl-lib)) 69 | 70 | (require 'json) 71 | (require 'url) 72 | (require 'google-translate-backend) 73 | 74 | (defgroup google-translate-core nil 75 | "Google Translate core script." 76 | :group 'processes) 77 | 78 | (defcustom google-translate-base-url 79 | "http://translate.google.com/translate_a/single" 80 | "Google Translate base url" 81 | :group 'google-translate-core 82 | :type 'string) 83 | 84 | (defcustom google-translate-listen-url 85 | "http://translate.google.com/translate_tts" 86 | "Google Translate listen url" 87 | :group 'google-translate-core 88 | :type 'string) 89 | 90 | (defvar google-translate-host-language 91 | (if current-iso639-language 92 | (symbol-name current-iso639-language) 93 | "en") 94 | "Host language to translate.") 95 | 96 | (defvar google-translate-punctuation-re "[,、]" 97 | "Regexp describing the punctuation.") 98 | 99 | (defvar google-translate-listen-maxlen 200 100 | "Split text for tts url to less than this. If 0, disable split.") 101 | 102 | (defun google-translate--split-text (text maxlen) 103 | "Split TEXT to less than MAXLEN at applicable point for translating." 104 | (let (result) 105 | (if (or (null maxlen) (<= maxlen 0)) 106 | (push text result) 107 | ;; split long text? 108 | (with-temp-buffer 109 | (save-excursion (insert text)) 110 | ;; strategy to split at applicable point 111 | ;; 1) fill-region remaining text by maxlen 112 | ;; 2) find end of sentence, end of punctuation, word boundary 113 | ;; 3) consume from remaining text between start and (2) 114 | ;; 4) repeat 115 | (let ((fill-column (* maxlen 3)) 116 | (sentence-end-double-space nil) 117 | (pos (point-min))) 118 | (while (< pos (point-max)) 119 | (save-restriction 120 | (narrow-to-region pos (point-max)) 121 | (fill-region pos (point-max)) 122 | (let ((limit (+ pos maxlen))) 123 | (if (>= limit (point-max)) 124 | (setq limit (point-max)) 125 | (goto-char limit) 126 | ;; try to split at end of sentence 127 | (if (> (backward-sentence) pos) 128 | (setq limit (point)) 129 | ;; try to split at end of punctuation 130 | (goto-char limit) 131 | (if (re-search-backward google-translate-punctuation-re 132 | pos t) 133 | (setq limit (1+ (point))) ; include punctuation 134 | (goto-char limit) 135 | ;; try to split at word boundary 136 | (forward-word-strictly -1) 137 | (when (> (point) pos) 138 | (setq limit (point)))))) 139 | (push (buffer-substring-no-properties pos limit) result) 140 | (goto-char limit) 141 | (setq pos limit))))))) 142 | (reverse result))) 143 | 144 | (defun google-translate--format-query-string (query-params) 145 | "Format QUERY-PARAMS as a query string. 146 | 147 | QUERY-PARAMS must be an alist of field-value pairs." 148 | (mapconcat #'(lambda (p) 149 | (format "%s=%s" 150 | (url-hexify-string (car p)) 151 | (url-hexify-string (cdr p)))) 152 | query-params "&")) 153 | 154 | (defun google-translate--format-request-url (query-params) 155 | "Format QUERY-PARAMS as a Google Translate HTTP request URL. 156 | 157 | QUERY-PARAMS must be an alist of field-value pairs." 158 | (concat google-translate-base-url 159 | "?" 160 | (google-translate--format-query-string query-params))) 161 | 162 | (defun google-translate--format-listen-url (query-params) 163 | "Format QUERY-PARAMS as a Google Translate HTTP request URL for listen translation. 164 | 165 | QUERY-PARAMS must be an alist of field-value pairs." 166 | (concat google-translate-listen-url 167 | "?" 168 | (google-translate--format-query-string query-params))) 169 | 170 | (defun google-translate-format-listen-url (text language &optional total idx) 171 | "Format listen url for TEXT and TARGET-LANGUAGE." 172 | (google-translate--format-listen-url `(("ie" . "UTF-8") 173 | ("q" . ,text) 174 | ("tl" . ,language) 175 | ("total" . ,(or total "1")) 176 | ("idx" . ,(or idx "0")) 177 | ("textlen" . ,(number-to-string (length text))) 178 | ("client" . "gtx") 179 | ("prev" . "input")))) 180 | 181 | (defun google-translate-format-listen-urls (text language) 182 | "Split TEXT with `google-translate--split-text', then format 183 | listen url for TEXT and TARGET-LANGUAGE." 184 | (let* ((texts (google-translate--split-text 185 | text google-translate-listen-maxlen)) 186 | (total (number-to-string (length texts))) 187 | (idx 0)) 188 | (mapcar (lambda (x) 189 | (prog1 (google-translate-format-listen-url x language total 190 | (number-to-string idx)) 191 | (setq idx (1+ idx)))) 192 | texts))) 193 | 194 | (defun google-translate--http-response-body (url &optional for-test-purposes) 195 | "Retrieve URL and return the response body as a string." 196 | (let ((google-translate-backend-debug (or for-test-purposes 197 | google-translate-backend-debug))) 198 | (with-temp-buffer 199 | (save-excursion 200 | (google-translate-backend-retrieve url)) 201 | (set-buffer-multibyte t) 202 | (buffer-string)))) 203 | 204 | (defun google-translate--insert-nulls (string) 205 | "Google Translate responses with an almost valid JSON string 206 | respresentation except that the nulls appear to be dropped. In 207 | particular the response may contain the substrings \"[,\", 208 | \",,\", and \",]\". This function undoes that." 209 | (with-temp-buffer 210 | (set-buffer-multibyte t) 211 | (insert string) 212 | (goto-char (point-min)) 213 | (while (re-search-forward "\\(\\[,\\|,,\\|,\\]\\)" (point-max) t) 214 | (backward-char) 215 | (insert "null")) 216 | (buffer-string))) 217 | 218 | (defun google-translate--trim-string (string) 219 | "Remove whitespaces in beginning and ending of STRING. 220 | White space here is any of: space, tab, emacs newline (line feed, ASCII 10)." 221 | (replace-regexp-in-string "\\`[ \t\n\r]*" "" 222 | (replace-regexp-in-string "[ \t\n\r]*\\'" "" string))) 223 | 224 | (defun google-translate--strip-string (string) 225 | "Replace spaces, tabs, line feeds (ASCII 10) and carridge 226 | returns (ASCII 13) by a single space symbol." 227 | (replace-regexp-in-string "[[:space:]\n\r]+" " " string)) 228 | 229 | (defun google-translate-prepare-text-for-request (text) 230 | "Make TEXT as clean as possible berofe sending it in the 231 | request." 232 | (google-translate--trim-string 233 | (google-translate--strip-string text))) 234 | 235 | (defun google-translate-request (source-language target-language text) 236 | "Send to the Google Translate http request which consigned to 237 | translate TEXT from SOURCE-LANGUAGE to TARGET-LANGUAGE. Returns 238 | response in json format." 239 | (let ((cleaned-text (google-translate-prepare-text-for-request text))) 240 | (when (and 241 | (stringp cleaned-text) 242 | (> (length cleaned-text) 0)) 243 | (json-read-from-string 244 | (google-translate--insert-nulls 245 | (google-translate--request source-language target-language text)))))) 246 | 247 | (defun google-translate--request (source-language 248 | target-language 249 | text 250 | &optional for-test-purposes) 251 | "Send to the Google Translate http request which consigned to 252 | translate TEXT from SOURCE-LANGUAGE to TARGET-LANGUAGE." 253 | (google-translate--http-response-body 254 | (google-translate--format-request-url 255 | `(("client" . "gtx") 256 | ("ie" . "UTF-8") 257 | ("oe" . "UTF-8") 258 | ("hl" . ,google-translate-host-language) 259 | ("sl" . ,source-language) 260 | ("tl" . ,target-language) 261 | ("q" . ,text) 262 | ("dt" . "bd") 263 | ("dt" . "ex") 264 | ("dt" . "ld") 265 | ("dt" . "md") 266 | ("dt" . "qc") 267 | ("dt" . "rw") 268 | ("dt" . "rm") 269 | ("dt" . "ss") 270 | ("dt" . "t") 271 | ("dt" . "at") 272 | ("pc" . "1") 273 | ("otf" . "1") 274 | ("srcrom" . "1") 275 | ("ssel" . "0") 276 | ("tsel" . "0"))) 277 | for-test-purposes)) 278 | 279 | (defun google-translate-json-text-phonetic (json) 280 | "Retrieve from the JSON (which returns by the 281 | `google-translate-request' function) phonetic transcription of 282 | the translating text." 283 | (mapconcat (lambda (item) (if (> (length item) 3) (aref item 3) "")) 284 | (aref json 0) "")) 285 | 286 | (defun google-translate-json-translation (json) 287 | "Retrieve from the JSON (which returns by the 288 | `google-translate-request' function) translation of the 289 | translating text." 290 | (mapconcat #'(lambda (item) (aref item 0)) 291 | (aref json 0) "")) 292 | 293 | (defun google-translate-json-translation-phonetic (json) 294 | "Retrieve from the JSON (which returns by the 295 | `google-translate-request' function) phonetic transcription of 296 | the translating text." 297 | (mapconcat #'(lambda (item) (if (> (length item) 2) (aref item 2) "")) 298 | (aref json 0) "")) 299 | 300 | (defun google-translate-json-detailed-translation (json) 301 | "Retrieve from the JSON (which returns by the 302 | `google-translate-request' function) a dictionary article 303 | represented by a vector of items, where each item is a 2-element 304 | vector whose zeroth element is the name of a part of speech and 305 | whose first element is a vector of translations for that part of 306 | speech." 307 | (aref json 1)) 308 | 309 | (defun google-translate-json-detailed-definition (json) 310 | "Retrieve the definition of translating text in source language from the JSON 311 | which returned by the `google-translate-request' function. 312 | 313 | This function returns the definition if it's included within the JSON as 12th 314 | element, or returns nil if not included. 315 | 316 | The definition is a dictionary article represented by a vector of items, where 317 | each item is a 2-element vector whose zeroth element is the name of a part of 318 | speech and whose first element is a vector of definitions for that part of 319 | speech." 320 | (if (> (length json) 12) 321 | (aref json 12))) 322 | 323 | (defun google-translate-json-suggestion (json) 324 | "Retrieve from JSON (which returns by the 325 | `google-translate-request' function) suggestion. This function 326 | does matter when translating misspelled word. So instead of 327 | translation it is possible to get suggestion." 328 | (let ((info (aref json 7))) 329 | (unless (seq-empty-p info) 330 | (aref info 1)))) 331 | 332 | (defun google-translate-version () 333 | (interactive) 334 | (message "Google Translate (version): %s" "0.12.0")) 335 | 336 | 337 | (provide 'google-translate-core) 338 | 339 | ;;; google-translate-core.el ends here 340 | -------------------------------------------------------------------------------- /google-translate-default-ui.el: -------------------------------------------------------------------------------- 1 | ;;; google-translate-default-ui.el --- default UI for Google Translate 2 | ;;; package 3 | 4 | ;; Copyright (C) 2012 Oleksandr Manzyuk 5 | 6 | ;; Author: Oleksandr Manzyuk 7 | ;; Maintainer: Andrey Tykhonov 8 | ;; URL: https://github.com/atykhonov/google-translate 9 | ;; Version: 0.12.0 10 | ;; Keywords: convenience 11 | 12 | ;; Contributors: 13 | ;; Tassilo Horn 14 | ;; Bernard Hurley 15 | ;; Chris Bilson 16 | ;; Takumi Kinjo 17 | ;; momomo5717 18 | ;; stardiviner 19 | 20 | ;; This file is NOT part of GNU Emacs. 21 | 22 | ;; This is free software; you can redistribute it and/or modify it 23 | ;; under the terms of the GNU General Public License as published by 24 | ;; the Free Software Foundation; either version 2, or (at your option) 25 | ;; any later version. 26 | 27 | ;; This file is distributed in the hope that it will be useful, but 28 | ;; WITHOUT ANY WARRANTY; without even the implied warranty of 29 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 30 | ;; General Public License for more details. 31 | 32 | ;; You should have received a copy of the GNU General Public License 33 | ;; along with GNU Emacs. If not, see . 34 | 35 | ;;; Commentary: 36 | 37 | ;; This file provides default UI for the Google Translate package. It 38 | ;; was originally written by Oleksandr Manzyuk and was part of 39 | ;; google-translate.el. It was extracted to this, 40 | ;; google-translate-default-ui.el file due to refactoring (the goal of 41 | ;; which is to separate backend from UI and provide better way for 42 | ;; having different UIs for Google Translate package). 43 | ;; 44 | ;; Invoking the function `google-translate-query-translate' queries the source 45 | ;; and target languages and text to translate, and shows a buffer with 46 | ;; available translations of the text. Invoking the function 47 | ;; `google-translate-at-point' translates the word at point or the active 48 | ;; region. 49 | 50 | ;; Customization: 51 | 52 | ;; You can customize the following variables: 53 | ;;;; 54 | ;; - `google-translate-default-source-language' 55 | ;; 56 | ;; - `google-translate-default-target-language' 57 | 58 | ;; If the variable `google-translate-default-source-language' is set 59 | ;; to a non-NIL value, the source language won't be queried and that 60 | ;; value will be used instead. Analogously, if you set the variable 61 | ;; `google-translate-default-target-language' to some non-NIL value, 62 | ;; that value will be used without querying. 63 | 64 | ;; You can always override this behavior by supplying a `C-u' prefix 65 | ;; argument to the function `google-translate-query-translate'. 66 | 67 | ;; Here is an example. Suppose that your native language is Russian 68 | ;; and you frequently need to translate from various languages to 69 | ;; Russian. Then it is reasonable 70 | ;; 71 | ;; - to set the variable `google-translate-default-target-language' 72 | ;; to "ru", and 73 | ;; 74 | ;; - to leave `google-translate-default-source-language' set to its 75 | ;; default value, NIL. 76 | ;; 77 | ;; In this case, the function `google-translate-query-translate' is 78 | ;; only going to query the source language and text to translate. 79 | ;; If you need to translate to some language other than Russian, you 80 | ;; can override the default for the target language by supplying a 81 | ;; `C-u' prefix argument, in which case you will be queried for both 82 | ;; the source and target languages, as well as text to translate. 83 | 84 | ;; If you frequently translate from some fixed language, it is also 85 | ;; reasonable to set `google-translate-default-source-language' to 86 | ;; an appropriate value. 87 | ;; 88 | ;; If you have both the default source and target languages specified, 89 | ;; you may like to bind functions `google-translate-at-point-reverse' 90 | ;; and `google-translate-query-translate-reverse' to some keys, e.g.: 91 | ;; 92 | ;; (global-set-key (kbd "C-c r") 'google-translate-at-point-reverse) 93 | ;; (global-set-key (kbd "C-c R") 'google-translate-query-translate-reverse) 94 | ;; 95 | ;; This will allow you to quickly translate in the reverse direction. 96 | ;; When the default source (resp. target) language is not set, the 97 | ;; target (resp. source) language of the reverse translation will be 98 | ;; queried interactively. 99 | 100 | ;; The admitted values of `google-translate-default-source-language' 101 | ;; and `google-translate-default-target-language' are the codes of the 102 | ;; languages supported by Google Translate (like "ru" for Russian 103 | ;; above). See `google-translate-supported-languages' for the list of 104 | ;; the supported languages, or customize the defaults using the 105 | ;; customization mechanism of Emacs. Setting a default language to 106 | ;; NIL means that language will always be queried. Moreover, the 107 | ;; variable `google-translate-default-source-language' can be set to a 108 | ;; special value "auto" that is interpreted as the instruction for 109 | ;; Google Translate to detect the source language. This option is 110 | ;; also available when you are queried for the source language: simply 111 | ;; leave this parameter blank by pressing RET. (If you have enabled 112 | ;; the ido-style completion, "Detect language" is going to be the 113 | ;; first option, which you can select simply by hitting RET.) 114 | ;; 115 | 116 | ;;; Code: 117 | 118 | 119 | (require 'google-translate-core-ui) 120 | 121 | 122 | (defgroup google-translate-default-ui nil 123 | "Default UI interface to the Google Translate package." 124 | :group 'processes) 125 | 126 | (defcustom google-translate-default-source-language nil 127 | "Default source language. 128 | 129 | A string designating a language supported by Google Translate. 130 | Set this variable to NIL (the default value) if you want to 131 | always be queried for the source language, or to \"auto\" if you 132 | want Google Translate to always detect the source language. 133 | 134 | See the variable `google-translate-supported-languages-alist' for 135 | the list of available languages." 136 | :group 'google-translate-manzyuk-ui 137 | :type `(radio ,@(mapcar #'(lambda (lang) 138 | `(const :tag ,(car lang) ,(cdr lang))) 139 | google-translate-supported-languages-alist) 140 | (const :tag "Detect language" "auto") 141 | (other :tag "Always ask" nil))) 142 | 143 | (defcustom google-translate-default-target-language nil 144 | "Default target language. 145 | 146 | A string designating a language supported by Google Translate. 147 | Set this variable to NIL (the default value) if you want to 148 | always be queried for the target language. 149 | 150 | See the variable `google-translate-supported-languages-alist' for 151 | the list of available languages." 152 | :group 'google-translate-manzyuk-ui 153 | :type `(radio ,@(mapcar #'(lambda (lang) 154 | `(const :tag ,(car lang) ,(cdr lang))) 155 | google-translate-supported-languages-alist) 156 | (other :tag "Always ask" nil))) 157 | 158 | (defun google-translate-read-args (override-p reverse-p) 159 | "Query and return the language arguments of `google-translate-translate'. 160 | 161 | When OVERRIDE-P is NIL, the source (resp. target) language is queried 162 | only if the variable `google-translate-default-source-language' (resp. 163 | `google-translate-default-target-language') is NIL. If OVERRIDE-P is 164 | non-NIL, both the source and target languages are queried, allowing 165 | one to override the defaults if they are specified. 166 | 167 | REVERSE-P is used to reverse the default direction of translation: if 168 | it's non-NIL, the value of `google-translate-default-source-language' 169 | becomes the default target language and vice versa." 170 | (let* ((default-source-language 171 | (if reverse-p 172 | google-translate-default-target-language 173 | google-translate-default-source-language)) 174 | (default-target-language 175 | (if reverse-p 176 | google-translate-default-source-language 177 | google-translate-default-target-language)) 178 | (source-language 179 | (if (and default-source-language 180 | (not override-p)) 181 | default-source-language 182 | (google-translate-read-source-language 183 | "Translate from: "))) 184 | (target-language 185 | (if (and default-target-language 186 | (not override-p)) 187 | default-target-language 188 | (google-translate-read-target-language 189 | (format "Translate from %s to: " 190 | (google-translate-language-display-name 191 | source-language)))))) 192 | (list source-language target-language))) 193 | 194 | (defun %google-translate-query-translate (override-p reverse-p) 195 | (let* ((langs (google-translate-read-args override-p reverse-p)) 196 | (source-language (car langs)) 197 | (target-language (cadr langs))) 198 | (google-translate-translate 199 | source-language target-language 200 | (if google-translate-input-method-auto-toggling 201 | (minibuffer-with-setup-hook 202 | (lambda () 203 | (google-translate-setup-preferable-input-method source-language)) 204 | (%google-translate-default-ui-read-from-minibuffer source-language target-language)) 205 | (%google-translate-default-ui-read-from-minibuffer source-language target-language))))) 206 | 207 | (defun %google-translate-default-ui-read-from-minibuffer (source-language target-language) 208 | (read-from-minibuffer 209 | (format "Translate from %s to %s: " 210 | (google-translate-language-display-name source-language) 211 | (google-translate-language-display-name target-language)))) 212 | 213 | ;;;###autoload 214 | (defun google-translate-query-translate (&optional override-p) 215 | "Interactively translate text with Google Translate. 216 | 217 | Query a text (a word or a phrase), and pop up a buffer named *Google 218 | Translate* displaying available translations of the text. 219 | 220 | If no defaults for the source and target languages are specified (by 221 | setting the variables `google-translate-default-source-language' and 222 | `google-translate-default-target-language'), interactively query the 223 | missing parts. For example, a reasonable option may be to specify a 224 | default for the target language and always be queried for the source 225 | language. 226 | 227 | With a `C-u' prefix argument, query the source and target languages, 228 | even if any defaults are specified. For example, you may frequently 229 | need to translate from English to Russian, and you may choose to set 230 | the default source and target languages to \"en\" and \"ru\", resp. 231 | However, occasionally you may also need to translate from Russian to 232 | English. With a `C-u' prefix argument you can override the defaults 233 | and specify the source and target languages explicitly. 234 | 235 | The languages are queried with completion, and the null input at the 236 | source language prompt is considered as an instruction for Google 237 | Translate to detect the source language." 238 | (interactive "P") 239 | (%google-translate-query-translate override-p nil)) 240 | 241 | ;;;###autoload 242 | (defun google-translate-query-translate-reverse (&optional override-p) 243 | "Like `google-translate-query-translate', but performs translation 244 | in the reverse direction. 245 | 246 | The value of the variable `google-translate-default-source-language' 247 | \(if set) becomes the target language, and the value of the variable 248 | `google-translate-default-target-language' (if also set) becomes the 249 | source language. 250 | 251 | In particular, when both variables are set, translation is performed 252 | in the reverse direction." 253 | (interactive "P") 254 | (%google-translate-query-translate override-p t)) 255 | 256 | (defun %google-translate-at-point (override-p reverse-p) 257 | (let* ((langs (google-translate-read-args override-p reverse-p)) 258 | (source-language (car langs)) 259 | (target-language (cadr langs)) 260 | (bounds nil)) 261 | (google-translate-translate 262 | source-language target-language 263 | (cond ((string-equal major-mode "pdf-view-mode") (car (pdf-view-active-region-text))) 264 | ((use-region-p) (buffer-substring-no-properties (region-beginning) (region-end))) 265 | (t (or (and (setq bounds (bounds-of-thing-at-point 'word)) 266 | (buffer-substring-no-properties (car bounds) (cdr bounds))) 267 | (error "No word at point."))))))) 268 | 269 | ;;;###autoload 270 | (defun google-translate-at-point (&optional override-p) 271 | "Translate the word at point or the words in the active region. 272 | 273 | For the meaning of OVERRIDE-P, see `google-translate-query-translate'." 274 | (interactive "P") 275 | (%google-translate-at-point override-p nil)) 276 | 277 | ;;;###autoload 278 | (defun google-translate-at-point-reverse (&optional override-p) 279 | "Like `google-translate-at-point', but performs translation in the 280 | reverse direction." 281 | (interactive "P") 282 | (%google-translate-at-point override-p t)) 283 | 284 | ;;;###autoload 285 | (defun google-translate-buffer (&optional override-p reverse-p) 286 | "Translate current buffer. 287 | 288 | For the meaning of OVERRIDE-P, see `google-translate-query-translate'." 289 | (interactive "P") 290 | (if (string-equal major-mode "pdf-view-mode") 291 | (message "In PDF, select region and use google-translate-at-point") 292 | (let* ((langs (google-translate-read-args override-p reverse-p)) 293 | (source-language (car langs)) 294 | (target-language (cadr langs))) 295 | (google-translate-translate 296 | source-language target-language 297 | (if (use-region-p) 298 | (buffer-substring-no-properties (region-beginning) (region-end)) 299 | (or (buffer-substring-no-properties (point-min) (point-max)) 300 | (error "Translate current buffer error."))))))) 301 | 302 | ;;;###autoload 303 | (defun google-translate-paragraphs-overlay (&optional override-p reverse-p) 304 | "Translate current buffer with paragraph by paragraph and SHOW results in overlay below paragraph. 305 | This command also specificly support org-mode." 306 | (interactive "P") 307 | (let* ((langs (google-translate-read-args override-p reverse-p)) 308 | (source-language (car langs)) 309 | (target-language (cadr langs)) 310 | (last-paragraph-begin 1)) 311 | (goto-char (point-min)) 312 | (while (not (equal (point) (point-max))) ; reached end of buffer 313 | (if (eq major-mode 'org-mode) 314 | (if (eq (car (org-element-at-point)) 'paragraph) 315 | (progn 316 | (org-mark-element) 317 | (exchange-point-and-mark) 318 | (unless (= last-paragraph-begin (region-beginning)) 319 | (google-translate-translate 320 | source-language target-language 321 | (buffer-substring-no-properties (region-beginning) (region-end)) 322 | 'paragraph-overlay) 323 | (setq last-paragraph-begin (region-beginning))) 324 | (deactivate-mark)) 325 | (forward-line 2)) 326 | (google-translate-translate 327 | source-language target-language 328 | (save-excursion 329 | (mark-paragraph) 330 | (buffer-substring-no-properties (region-beginning) (region-end))) 331 | 'paragraph-overlay) 332 | (deactivate-mark) 333 | (forward-paragraph) 334 | (forward-line))))) 335 | 336 | ;;;###autoload 337 | (defun google-translate-paragraphs-insert (&optional override-p reverse-p) 338 | "Translate current buffer with paragraph by paragraph and INSERT results below paragraph. 339 | This command does NOT support document format like org-mode." 340 | (interactive "P") 341 | (let* ((langs (google-translate-read-args override-p reverse-p)) 342 | (source-language (car langs)) 343 | (target-language (cadr langs))) 344 | (goto-char (point-min)) 345 | (while (not (equal (point) (point-max))) ; reached end of buffer 346 | (google-translate-translate 347 | source-language target-language 348 | (save-excursion 349 | (mark-paragraph) 350 | (buffer-substring-no-properties (region-beginning) (region-end))) 351 | 'paragraph-insert) 352 | (deactivate-mark) 353 | (forward-line)))) 354 | 355 | (provide 'google-translate-default-ui) 356 | 357 | 358 | ;;; google-translate-default-ui.el ends here 359 | -------------------------------------------------------------------------------- /google-translate-smooth-ui.el: -------------------------------------------------------------------------------- 1 | ;;; google-translate-smooth-ui.el --- Just another UI to Google 2 | ;;; Translate package 3 | 4 | ;; Copyright (C) 2012 Oleksandr Manzyuk 5 | 6 | ;; Author: Oleksandr Manzyuk 7 | ;; Maintainer: Andrey Tykhonov 8 | ;; URL: https://github.com/atykhonov/google-translate 9 | ;; Version: 0.12.0 10 | ;; Keywords: convenience 11 | 12 | ;; Contributors: 13 | ;; Tassilo Horn 14 | ;; Bernard Hurley 15 | ;; Chris Bilson 16 | ;; Takumi Kinjo 17 | ;; momomo5717 18 | ;; stardiviner 19 | 20 | ;; This file is NOT part of GNU Emacs. 21 | 22 | ;; This is free software; you can redistribute it and/or modify it 23 | ;; under the terms of the GNU General Public License as published by 24 | ;; the Free Software Foundation; either version 2, or (at your option) 25 | ;; any later version. 26 | 27 | ;; This file is distributed in the hope that it will be useful, but 28 | ;; WITHOUT ANY WARRANTY; without even the implied warranty of 29 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 30 | ;; General Public License for more details. 31 | 32 | ;; You should have received a copy of the GNU General Public License 33 | ;; along with GNU Emacs. If not, see . 34 | 35 | ;;; Commentary: 36 | 37 | ;; `google-translate-smooth-ui' is a just alternative to the default 38 | ;; `google-translate-default-ui'. It was written with mind to provide 39 | ;; impoved user interface and, especially, to achieve better 40 | ;; supporting of many default languages. `google-translate-default-ui' 41 | ;; supports two default languages very well but there is no space for 42 | ;; the third one. 43 | ;; 44 | ;; Invoking the function `google-translate-smooth-translate' queries 45 | ;; text and (optionally) the source and target languages to translate, 46 | ;; and shows a buffer with available translations of the text. 47 | 48 | ;; Installation: 49 | 50 | ;; Assuming that the files `google-translate.el', 51 | ;; `google-translate-core.el', `google-translate-core-ui.el' and 52 | ;; `google-translate-smooth-ui.el' are somewhere on the load path, add 53 | ;; the following lines to your .emacs file: 54 | ;; 55 | ;; (require 'google-translate-smooth-ui) 56 | ;; (global-set-key "\C-ct" 'google-translate-smooth-translate) 57 | ;; 58 | ;; Change the key bindings to your liking. 59 | ;; 60 | ;; Configuration: 61 | ;; 62 | ;; `google-translate-translation-directions-alist' alist is intended 63 | ;; to contain translation directions. 64 | ;; 65 | ;; For example it could be defined (in your .emacs or init.el) as: 66 | ;; 67 | ;; (setq google-translate-translation-directions-alist '(("en" . "ru")) 68 | ;; 69 | ;; in this way one translation direction ("en" > "ru") is defined and 70 | ;; when `google-translate-smooth-translate' function executes it will 71 | ;; output the prompt (in minibuffer) which will looks like as the 72 | ;; following: 73 | ;; 74 | ;; [English > Russian] Translate: 75 | ;; 76 | ;; You may set as many translation directions as you would like 77 | ;; to. For example such piece of code will define four translation 78 | ;; directions: 79 | ;; 80 | ;; (setq google-translate-translation-directions-alist 81 | ;; '(("de" . "en") ("en" . "de") ("de" . "fr") ("fr" . "de"))) 82 | ;; 83 | ;; in this way, when `google-translate-smooth-translate' function 84 | ;; executes you'll be queried by the prompt which will looks like the 85 | ;; following: 86 | ;; 87 | ;; [German > English] Translate: 88 | ;; 89 | ;; and, also in this way, you'll be able to switch between different 90 | ;; translation directions directly from minibuffer by using "C-n" and 91 | ;; "C-p" key bindings. "C-n" key binding changes current translation 92 | ;; direction to the next direction defined in the 93 | ;; `google-translate-translation-directions-alist' variable. And "C-p" 94 | ;; key binding changes current translation direction to the previous 95 | ;; one. Thus, while executing `google-translate-smooth-translate' 96 | ;; function and having in minibuffer such prompt: 97 | ;; 98 | ;; [German > English] Translate: 99 | ;; 100 | ;; then after pressing "C-n" you'll get the following prompt: 101 | ;; 102 | ;; [English > German] Translate: 103 | ;; 104 | ;; By default `google-translate-translation-directions-alist' is empty 105 | ;; and thus during execution of `google-translate-smooth-translate' 106 | ;; you'll be queried (to input a text) by the prompt: 107 | ;; 108 | ;; Translate: 109 | ;; 110 | ;; And after inputed text you'll be queried also for the source and 111 | ;; target languages. To let the package to be known which languages 112 | ;; you would like to always use and to avoid repetitive language 113 | ;; quering it is reasonable to define them in the mentioned 114 | ;; `google-translate-translation-directions-alist' variable. 115 | 116 | ;; Customization: 117 | 118 | ;; `google-translate-smooth-ui' doesn't contain any customizable 119 | ;; variables. But `google-translate-smooth-ui' extends 120 | ;; `google-translate-core-ui' and thus it could be customized via this 121 | ;; package's variables. Please read documentation for the 122 | ;; `google-translate-core-ui' package. 123 | ;; 124 | 125 | ;;; Code: 126 | 127 | 128 | (require 'google-translate-core-ui) 129 | 130 | 131 | (defgroup google-translate-smooth-ui nil 132 | "Just Another UI for Google Translate package." 133 | :group 'processes) 134 | 135 | (defvar google-translate-translation-directions-alist 136 | '() 137 | "Alist of translation directions. Each of direction could be 138 | selected directly in the minibuffer during translation. 139 | 140 | Each element is a cons-cell of the form (SOURCE_CODE 141 | . TARGET_CODE), where SOURCE_CODE is a source language code and 142 | TARGET_CODE is a target language code. 143 | 144 | Language codes are defined in 145 | `google-translate-supported-languages-alist' variable. 146 | 147 | As example, this alist could looks like the following: 148 | 149 | '((\"en\" . \"ru\") 150 | (\"ru\" . \"en\") 151 | (\"uk\" . \"ru\") 152 | (\"ru\" . \"uk\"))") 153 | 154 | (defvar google-translate-current-translation-direction 0 155 | "Points to nth element of 156 | `google-translate-translation-directions-alist' variable and 157 | keeps current translation direction while changing translation 158 | directions.") 159 | 160 | (defvar google-translate-last-translation-direction nil 161 | "The last used translation direction. 162 | Points to nth element of `google-translate-translation-directions-alist' variable.") 163 | 164 | (defvar google-translate-translation-direction-query "" 165 | "Temporal variable which keeps a minibuffer text while 166 | switching translation directions.") 167 | 168 | (defvar google-translate-try-other-direction nil 169 | "Indicates that other translation direction is going to be 170 | used.") 171 | 172 | (defvar google-translate-minibuffer-keymap nil 173 | "Keymap for minibuffer for changing translation directions.") 174 | 175 | (defun google-translate-change-translation-direction (direction) 176 | "Change translation direction. If DIRECTION is 'next then 177 | change current direction by the next one. Otherwise change it to 178 | the previous one." 179 | (let ((current google-translate-current-translation-direction) 180 | (length (length google-translate-translation-directions-alist))) 181 | (setq current 182 | (if (equal direction 'next) 183 | (+ current 1) 184 | (- current 1))) 185 | (when (< current 0) 186 | (setq current (- length 1))) 187 | (when (> current (- length 1)) 188 | (setq current 0)) 189 | (setq google-translate-current-translation-direction current) 190 | (setq google-translate-translation-direction-query 191 | (minibuffer-contents)))) 192 | 193 | (defun google-translate-next-translation-direction () 194 | "Switch to the next translation direction. If current direction 195 | is the last in the list of existing directions then switch to the 196 | first one." 197 | (interactive) 198 | (google-translate-change-translation-direction 'next) 199 | (setq google-translate-try-other-direction t) 200 | (exit-minibuffer)) 201 | 202 | (defun google-translate-previous-translation-direction () 203 | "Switch to the previous translation direction. If current 204 | direction is the first in the list of existing directions then 205 | switch to the last one." 206 | (interactive) 207 | (google-translate-change-translation-direction 'previous) 208 | (setq google-translate-try-other-direction t) 209 | (exit-minibuffer)) 210 | 211 | (defun google-translate-query-translate-using-directions () 212 | "Tranlate query using translation directions described by 213 | `google-translate-translation-directions-alist' variable. 214 | 215 | This function allows to select desired translation direction 216 | directly in the minibuffer while translating a word or a 217 | sentence. 218 | 219 | This function defines two key bindings for the minibuffer which 220 | allow to select direction: 221 | C-p - to select previous direction, 222 | C-n - to select next direction." 223 | (interactive) 224 | (let ((text "")) 225 | (setq google-translate-try-other-direction nil) 226 | (setq text 227 | (if google-translate-input-method-auto-toggling 228 | (minibuffer-with-setup-hook 229 | (lambda () 230 | (google-translate-setup-preferable-input-method 231 | (google-translate--current-direction-source-language))) 232 | 'google-translate-setup-preferable-input-method 233 | (google-translate--read-from-minibuffer)) 234 | (google-translate--read-from-minibuffer))) 235 | (if google-translate-try-other-direction 236 | (call-interactively 'google-translate-query-translate-using-directions) 237 | text))) 238 | 239 | (defun google-translate--setup-minibuffer-keymap () 240 | "Setup additional key bindings for minibuffer." 241 | (unless google-translate-minibuffer-keymap 242 | (setq google-translate-minibuffer-keymap 243 | (let ((map (make-sparse-keymap))) 244 | (define-key map "\C-p" 'google-translate-previous-translation-direction) 245 | (define-key map "\C-n" 'google-translate-next-translation-direction) 246 | (define-key map "\C-l" 'google-translate-clear-minibuffer) 247 | (set-keymap-parent map minibuffer-local-map) 248 | map)))) 249 | 250 | (defun google-translate-clear-minibuffer () 251 | "Delete minibuffer contents." 252 | (interactive) 253 | (delete-minibuffer-contents)) 254 | 255 | (defun google-translate--read-from-minibuffer () 256 | "Read string from minibuffer." 257 | (let* ((source-language 258 | (google-translate--current-direction-source-language)) 259 | (target-language 260 | (google-translate--current-direction-target-language)) 261 | (prompt (if (or (null source-language) 262 | (null target-language)) 263 | "Translate: " 264 | (format "[%s > %s] Translate: " 265 | (google-translate-language-display-name source-language) 266 | (google-translate-language-display-name target-language))))) 267 | (google-translate--setup-minibuffer-keymap) 268 | (read-from-minibuffer 269 | prompt 270 | google-translate-translation-direction-query 271 | google-translate-minibuffer-keymap nil nil 272 | google-translate-translation-direction-query t))) 273 | 274 | (defun google-translate--current-direction-source-language () 275 | "Retrieve source language from the current translation 276 | direction." 277 | (car (nth google-translate-current-translation-direction 278 | google-translate-translation-directions-alist))) 279 | 280 | (defun google-translate--current-direction-target-language () 281 | "Retrieve target language from the current translation 282 | direction." 283 | (cdr (nth google-translate-current-translation-direction 284 | google-translate-translation-directions-alist))) 285 | 286 | ;;;###autoload 287 | (defun google-translate-smooth-translate () 288 | "Translate a text using translation directions. 289 | 290 | Make a prompt in minibuffer for a text to translate. Default text 291 | is word at point. 292 | 293 | In case of `google-translate-translation-directions-alist' is 294 | empty list then after inputed translating text prompts for source 295 | language and then for target languages. 296 | 297 | In case of `google-translate-translation-directions-alist' is not 298 | empty list takes current translation direction and makes 299 | appropriate translation. Current translation direction indicates 300 | in the minibuffers' prompt. 301 | 302 | A current translation direction could be changed directly in the 303 | minibuffer by means of key bindings such as C-n and C-p for 304 | changing to the next translation direction and to the previous 305 | one respectively." 306 | (interactive) 307 | 308 | (setq google-translate-translation-direction-query 309 | (if (use-region-p) 310 | (google-translate--strip-string 311 | (buffer-substring-no-properties (region-beginning) (region-end))) 312 | (unless current-prefix-arg 313 | (current-word t t)))) 314 | 315 | (setq google-translate-current-translation-direction 316 | (or google-translate-last-translation-direction 0)) 317 | 318 | (let* ((text (google-translate-query-translate-using-directions)) 319 | (source-language (google-translate--current-direction-source-language)) 320 | (target-language (google-translate--current-direction-target-language))) 321 | (when (null source-language) 322 | (setq source-language (google-translate-read-source-language))) 323 | (when (null target-language) 324 | (setq target-language (google-translate-read-target-language))) 325 | (google-translate-translate source-language target-language text) 326 | (setq google-translate-last-translation-direction 327 | google-translate-current-translation-direction))) 328 | 329 | 330 | (provide 'google-translate-smooth-ui) 331 | 332 | ;;; google-translate-smooth-ui.el ends here 333 | -------------------------------------------------------------------------------- /google-translate.el: -------------------------------------------------------------------------------- 1 | ;;; google-translate.el --- Emacs interface to Google Translate 2 | 3 | ;; Copyright (C) 2012 Oleksandr Manzyuk 4 | 5 | ;; Author: Oleksandr Manzyuk 6 | ;; Maintainer: Andrey Tykhonov 7 | ;; URL: https://github.com/atykhonov/google-translate 8 | ;; Package-Requires: ((emacs "24.3") (popup "0.5.8")) 9 | ;; Version: 0.12.0 10 | ;; Keywords: convenience 11 | 12 | ;; Contributors: 13 | ;; Tassilo Horn 14 | ;; Bernard Hurley 15 | ;; Chris Bilson 16 | ;; Takumi Kinjo 17 | ;; momomo5717 18 | ;; stardiviner 19 | 20 | ;; This file is NOT part of GNU Emacs. 21 | 22 | ;; This is free software; you can redistribute it and/or modify it 23 | ;; under the terms of the GNU General Public License as published by 24 | ;; the Free Software Foundation; either version 2, or (at your option) 25 | ;; any later version. 26 | 27 | ;; This file is distributed in the hope that it will be useful, but 28 | ;; WITHOUT ANY WARRANTY; without even the implied warranty of 29 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 30 | ;; General Public License for more details. 31 | 32 | ;; You should have received a copy of the GNU General Public License 33 | ;; along with GNU Emacs. If not, see . 34 | 35 | ;;; Commentary: 36 | 37 | ;; Installation: 38 | 39 | ;; From MELPA or Marmalade. 40 | 41 | ;; Just run `M-x package-install RET google-translate RET` 42 | 43 | ;; Manual installation. 44 | 45 | ;; Assuming that the file `google-translate.el' and other files which 46 | ;; relates to this package is somewhere on the load path, add the 47 | ;; following lines to your `.emacs' file: 48 | 49 | ;; (require 'google-translate) 50 | ;; (require 'google-translate-default-ui) 51 | ;; (global-set-key "\C-ct" 'google-translate-at-point) 52 | ;; (global-set-key "\C-cT" 'google-translate-query-translate) 53 | 54 | ;; or 55 | 56 | ;; (require 'google-translate) 57 | ;; (require 'google-translate-smooth-ui) 58 | ;; (global-set-key "\C-ct" 'google-translate-smooth-translate) 59 | ;; 60 | ;; Change the key bindings to your liking. 61 | 62 | ;; The difference between these configurations is in UI which will be 63 | ;; used: Default UI or Smooth UI. 64 | ;; 65 | ;; Please read the source of `google-translate-default-ui.el' and 66 | ;; `google-translate-smooth-ui.el' for more details. 67 | 68 | ;; Customization: 69 | 70 | ;; Variables which are available for customization are depends on UI 71 | ;; package which is selected for the google-translate 72 | ;; package. google-translate-default-ui - is UI which is selected by 73 | ;; default. It loads by default and is available right after 74 | ;; google-translate installation and its initialization. Please read 75 | ;; documentation for the `google-translate-core-ui.el' and 76 | ;; `google-translate-default-ui.el' packages for more info about 77 | ;; customization. 78 | ;; 79 | 80 | ;;; Code: 81 | 82 | (require 'google-translate-default-ui) 83 | 84 | (provide 'google-translate) 85 | 86 | ;;; google-translate.el ends here 87 | -------------------------------------------------------------------------------- /test/fixtures/sentence/1.fixture: -------------------------------------------------------------------------------- 1 | спочатку було слово 2 | 3 | 4 | HTTP/1.1 200 OK 5 | Pragma: no-cache 6 | Date: Wed, 05 Mar 2014 20:58:32 GMT 7 | Expires: Wed, 05 Mar 2014 20:58:32 GMT 8 | Cache-Control: private, max-age=600 9 | Content-Type: text/javascript; charset=UTF-8 10 | Content-Language: en 11 | X-Content-Type-Options: nosniff 12 | Content-Disposition: attachment 13 | Server: HTTP server (unknown) 14 | X-XSS-Protection: 1; mode=block 15 | Alternate-Protocol: 80:quic 16 | Transfer-Encoding: chunked 17 | 18 | [[["beginning was the word","спочатку було слово","","spochatku bulo slovo"]],,"uk",,[["beginning was the word",[1],true,false,570,0,4,0]],[["спочатку було слово",1,[["beginning was the word",570,true,false]],[[0,19]],"спочатку було слово"]],,,[["uk"]],21] 19 | 20 | text-phonetic: 21 | spochatku bulo slovo 22 | 23 | translation-phonetic: 24 | 25 | 26 | translation: 27 | beginning was the word 28 | 29 | suggestion: 30 | 31 | 32 | detailed-translation: 33 | -------------------------------------------------------------------------------- /test/fixtures/sentence/2.fixture: -------------------------------------------------------------------------------- 1 | век живи, век учись 2 | 3 | 4 | HTTP/1.1 200 OK 5 | Pragma: no-cache 6 | Date: Wed, 05 Mar 2014 20:58:32 GMT 7 | Expires: Wed, 05 Mar 2014 20:58:32 GMT 8 | Cache-Control: private, max-age=600 9 | Content-Type: text/javascript; charset=UTF-8 10 | Content-Language: uk 11 | X-Content-Type-Options: nosniff 12 | Content-Disposition: attachment 13 | Server: HTTP server (unknown) 14 | X-XSS-Protection: 1; mode=block 15 | Alternate-Protocol: 80:quic 16 | Transfer-Encoding: chunked 17 | 18 | [[["вік живи , вік учись","век живи, век учись","vik zhyvy , vik uchysʹ","vek zhivi, vek uchis'"]],,"ru",,[["вік живи",[1],true,false,986,0,2,0],[",",[2],false,false,986,2,3,0],["вік учись",[3],true,false,986,3,5,0]],[["век живи",1,[["вік живи",986,true,false]],[[0,8]],"век живи, век учись"],[",",2,[[",",986,false,false]],[[8,9]],""],["век учись",3,[["вік учись",986,true,false],["век учись",0,true,false],["століття вчися",0,true,false],["вік учися",0,true,false],["вік навчайся",0,true,false]],[[10,19]],""]],,,[["sr"]],17] 19 | 20 | text-phonetic: 21 | vek zhivi, vek uchis' 22 | 23 | translation-phonetic: 24 | vik zhyvy , vik uchysʹ 25 | 26 | translation: 27 | вік живи , вік учись 28 | 29 | suggestion: 30 | 31 | 32 | detailed-translation: 33 | -------------------------------------------------------------------------------- /test/fixtures/word/1.fixture: -------------------------------------------------------------------------------- 1 | return 2 | 3 | 4 | HTTP/1.1 200 OK 5 | Pragma: no-cache 6 | Date: Wed, 05 Mar 2014 20:58:28 GMT 7 | Expires: Wed, 05 Mar 2014 20:58:28 GMT 8 | Cache-Control: private, max-age=600 9 | Content-Type: text/javascript; charset=UTF-8 10 | Content-Language: ru 11 | X-Content-Type-Options: nosniff 12 | Content-Disposition: attachment 13 | Server: HTTP server (unknown) 14 | X-XSS-Protection: 1; mode=block 15 | Alternate-Protocol: 80:quic 16 | Transfer-Encoding: chunked 17 | 18 | [[["возвращение","return","vozvrashcheniye",""]],[["noun",["возвращение","возврат","ответ","доход","прибыль","отдача","обратный путь","возмещение","отчет","возвращенный товар","оборот","результат выборов","избрание","официальный отчет","рапорт","ответная подача","обратная сеть","обратный провод","непроданный товар","возражение","вентиляционный просек","вентиляционный ходок"],[["возвращение",["return","comeback","restitution","repatriation","repayment","retrieval"],,0.26914635],["возврат",["return","refund","recovery","recurrence","reclamation"],,0.12516467],["ответ",["answer","reply","response","return","replication","rejoinder"],,0.000721358],["доход",["income","revenue","earnings","profit","yield","return"],,0.00018238787],["прибыль",["profit","income","earnings","gain","return","margin"],,0.00017403573],["отдача",["return","recoil","output","efficiency","kickback","rebound"],,0.00016606605],["обратный путь",["return","homing"],,7.1424118e-05],["возмещение",["compensation","offset","reimbursement","recovery","redress","return"],,3.0719129e-05],["отчет",["report","record","account","return","reply"],,2.0785603e-05],["возвращенный товар",["return"],,1.5689797e-05],["оборот",["turnover","turn","revolution","rev","turnaround","return"],,9.368805e-06],["результат выборов",["return"],,4.5659908e-06],["избрание",["election","return"],,2.857324e-06],["официальный отчет",["statement","record","return"],,2.8130255e-06],["рапорт",["report","return"],,2.8130255e-06],["ответная подача",["return"],,2.7694134e-06],["обратная сеть",["return"],,2.7694134e-06],["обратный провод",["return"],,2.7694134e-06],["непроданный товар",["return"],,1.7880677e-06],["возражение",["objection","retort","rejoinder","answer","exception","return"],,1.7880677e-06],["вентиляционный просек",["return"],,1.7880677e-06],["вентиляционный ходок",["return"],,1.7880677e-06]],"return",1],["verb",["возвращать","возвращаться","отдавать","отвечать","приносить","давать ответ","вновь обращаться","возражать","отплачивать","идти обратно","избирать","официально заявлять","докладывать"],[["возвращать",["return","repay","restore","bring back","give back","reimburse"],,0.11396374],["возвращаться",["return","come back","go back","get back","revert","be back"],,0.11045744],["отдавать",["give","give away","return","give back","render","contribute"],,0.00035156182],["отвечать",["answer","respond","reply","account","return","account for"],,0.00020031388],["приносить",["bring","yield","bring in","offer up","carry","return"],,5.6501125e-05],["давать ответ",["return"],,7.2964326e-06],["вновь обращаться",["return"],,6.4390792e-06],["возражать",["mind","object","object to","protest","contradict","return"],,4.4255103e-06],["отплачивать",["repay","pay","retaliate","pay back","return","pay out"],,3.8449498e-06],["идти обратно",["return","cut back"],,2.9480252e-06],["избирать",["elect","choose","embrace","return"],,2.9023204e-06],["официально заявлять",["return"],,2.7694134e-06],["докладывать",["report","announce","render an account","return","tell"],,8.990969e-07]],"return",2],["adjective",["обратный","вернувшийся"],[["обратный",["back","return","reverse","inverse","converse","opposite"],,0.00077997398],["вернувшийся",["return"],,3.3738364e-05]],"return",3]],"en",,[["возвращение",[1],true,false,1000,0,1,0]],[["return",1,[["возвращение",1000,true,false],["возврат",0,true,false],["возвращения",0,true,false],["обратный",0,true,false],["доход",0,true,false]],[[0,6]],"return"]],,,[],27] 19 | 20 | text-phonetic: 21 | 22 | 23 | translation-phonetic: 24 | vozvrashcheniye 25 | 26 | translation: 27 | возвращение 28 | 29 | suggestion: 30 | 31 | 32 | detailed-translation: 33 | noun 34 | возвращение 35 | возврат 36 | ответ 37 | доход 38 | прибыль 39 | отдача 40 | обратный путь 41 | возмещение 42 | отчет 43 | возвращенный товар 44 | оборот 45 | результат выборов 46 | избрание 47 | официальный отчет 48 | рапорт 49 | ответная подача 50 | обратная сеть 51 | обратный провод 52 | непроданный товар 53 | возражение 54 | вентиляционный просек 55 | вентиляционный ходок 56 | verb 57 | возвращать 58 | возвращаться 59 | отдавать 60 | отвечать 61 | приносить 62 | давать ответ 63 | вновь обращаться 64 | возражать 65 | отплачивать 66 | идти обратно 67 | избирать 68 | официально заявлять 69 | докладывать 70 | adjective 71 | обратный 72 | вернувшийся 73 | -------------------------------------------------------------------------------- /test/fixtures/word/2.fixture: -------------------------------------------------------------------------------- 1 | belongs 2 | 3 | 4 | HTTP/1.1 200 OK 5 | Pragma: no-cache 6 | Date: Wed, 05 Mar 2014 20:58:29 GMT 7 | Expires: Wed, 05 Mar 2014 20:58:29 GMT 8 | Cache-Control: private, max-age=600 9 | Content-Type: text/javascript; charset=UTF-8 10 | Content-Language: ru 11 | X-Content-Type-Options: nosniff 12 | Content-Disposition: attachment 13 | Server: HTTP server (unknown) 14 | X-XSS-Protection: 1; mode=block 15 | Alternate-Protocol: 80:quic 16 | Transfer-Encoding: chunked 17 | 18 | [[["принадлежит","belongs","prinadlezhit",""]],[["verb",["принадлежать","относиться","находиться","происходить","быть связанным","быть родом из","помещаться","быть одним из группы","быть 'своим'"],[["принадлежать",["belong","pertain","reside","appertain","inhere"],,0.26914635],["относиться",["treat","refer","belong","relate","concern","apply"],,0.10376516],["находиться",["be","locate","be found","reside","lie","belong"],,0.0010172778],["происходить",["occur","happen","take place","be","come","belong"],,2.5467052e-05],["быть связанным",["relate","belong","link"],,1.4510689e-05],["быть родом из",["belong"],,1.2607105e-05],["помещаться",["place","house","stand","belong"],,1.241165e-05],["быть одним из группы",["be of a group","belong"],,6.4390792e-06],["быть 'своим'",["belong"]]],"belong",2]],"en",,[["принадлежит",[1],true,false,1000,0,1,0]],[["belongs",1,[["принадлежит",1000,true,false],["относится",0,true,false],["состоит",0,true,false],["входящий",0,true,false],["принадлежат",0,true,false]],[[0,7]],"belongs"]],,,[],20] 19 | 20 | text-phonetic: 21 | 22 | 23 | translation-phonetic: 24 | prinadlezhit 25 | 26 | translation: 27 | принадлежит 28 | 29 | suggestion: 30 | 31 | 32 | detailed-translation: 33 | verb 34 | принадлежать 35 | относиться 36 | находиться 37 | происходить 38 | быть связанным 39 | быть родом из 40 | помещаться 41 | быть одним из группы 42 | быть 'своим' 43 | -------------------------------------------------------------------------------- /test/fixtures/word/3.fixture: -------------------------------------------------------------------------------- 1 | first 2 | 3 | 4 | HTTP/1.1 200 OK 5 | Pragma: no-cache 6 | Date: Wed, 05 Mar 2014 20:58:29 GMT 7 | Expires: Wed, 05 Mar 2014 20:58:29 GMT 8 | Cache-Control: private, max-age=600 9 | Content-Type: text/javascript; charset=UTF-8 10 | Content-Language: ru 11 | X-Content-Type-Options: nosniff 12 | Content-Disposition: attachment 13 | Server: HTTP server (unknown) 14 | X-XSS-Protection: 1; mode=block 15 | Alternate-Protocol: 80:quic 16 | Transfer-Encoding: chunked 17 | 18 | [[["первый","first","pervyy",""]],[["adjective",["первый","ранний","ведущий","выдающийся","значительный"],[["первый",["first","former","maiden","opening","top","premier"],,0.65581602],["ранний",["early","first","forward","rathe","youthful","rareripe"],,7.4851829e-05],["ведущий",["leading","master","principal","key","guiding","first"],,1.4823602e-06],["выдающийся",["outstanding","prominent","eminent","distinguished","remarkable","first"],,1.3287791e-06],["значительный",["significant","great","considerable","large","substantial","first"],,1.0738182e-07]],"first",3],["",["первый"],[["первый",["first"],,0.65581602]],"first",15],["adverb",["сначала","впервые","сперва","в первую очередь","предпочтительно","скорее"],[["сначала",["first","initially","at first","primarily","at the beginning","in the first instance"],,0.046770621],["впервые",["first"],,0.020432571],["сперва",["first","at first","primarily","in the first place","in the first instance"],,0.0039610346],["в первую очередь",["primarily","first of all","first","in the first place","first and foremost","in the first instance"],,0.0031334364],["предпочтительно",["preferably","for preference","first"],,1.0030151e-06],["скорее",["rather","sooner","rater","first"],,2.1355423e-07]],"first",4],["noun",["начало","первое число","высшая оценка","товары высшего качества"],[["начало",["start","starting","beginning","outbreak","origin","first"],,0.00039837172],["первое число",["first"],,4.0696235e-05],["высшая оценка",["first"],,1.4005229e-07],["товары высшего качества",["first"],,1.0407803e-07]],"first",1]],"en",,[["первый",[1],true,false,831,0,1,0]],[["first",1,[["первый",831,true,false],["сначала",168,true,false],["первым",0,true,false],["первая",0,true,false],["первой",0,true,false]],[[0,5]],"first"]],,,[],27] 19 | 20 | text-phonetic: 21 | 22 | 23 | translation-phonetic: 24 | pervyy 25 | 26 | translation: 27 | первый 28 | 29 | suggestion: 30 | 31 | 32 | detailed-translation: 33 | adjective 34 | первый 35 | ранний 36 | ведущий 37 | выдающийся 38 | значительный 39 | adverb 40 | сначала 41 | впервые 42 | сперва 43 | в первую очередь 44 | предпочтительно 45 | скорее 46 | noun 47 | начало 48 | первое число 49 | высшая оценка 50 | товары высшего качества 51 | -------------------------------------------------------------------------------- /test/fixtures/word/4.fixture: -------------------------------------------------------------------------------- 1 | колобок 2 | 3 | 4 | HTTP/1.1 200 OK 5 | Pragma: no-cache 6 | Date: Wed, 05 Mar 2014 20:58:30 GMT 7 | Expires: Wed, 05 Mar 2014 20:58:30 GMT 8 | Cache-Control: private, max-age=600 9 | Content-Type: text/javascript; charset=UTF-8 10 | Content-Language: uk 11 | X-Content-Type-Options: nosniff 12 | Content-Disposition: attachment 13 | Server: HTTP server (unknown) 14 | X-XSS-Protection: 1; mode=block 15 | Alternate-Protocol: 80:quic 16 | Transfer-Encoding: chunked 17 | 18 | [[["колобок","колобок","kolobok","kolobok"]],,"ru",,[["колобок",[1],true,false,1000,0,1,0]],[["колобок",1,[["колобок",1000,true,false]],[[0,7]],"колобок"]],,,[["uk"]],31] 19 | 20 | text-phonetic: 21 | kolobok 22 | 23 | translation-phonetic: 24 | kolobok 25 | 26 | translation: 27 | колобок 28 | 29 | suggestion: 30 | 31 | 32 | detailed-translation: 33 | -------------------------------------------------------------------------------- /test/fixtures/word/5.fixture: -------------------------------------------------------------------------------- 1 | sucesful 2 | 3 | 4 | HTTP/1.1 200 OK 5 | Pragma: no-cache 6 | Date: Wed, 05 Mar 2014 20:58:30 GMT 7 | Expires: Wed, 05 Mar 2014 20:58:30 GMT 8 | Cache-Control: private, max-age=600 9 | Content-Type: text/javascript; charset=UTF-8 10 | Content-Language: ru 11 | X-Content-Type-Options: nosniff 12 | Content-Disposition: attachment 13 | Server: HTTP server (unknown) 14 | X-XSS-Protection: 1; mode=block 15 | Alternate-Protocol: 80:quic 16 | Transfer-Encoding: chunked 17 | 18 | [[["sucesful","sucesful","sucesful",""]],,"en",,[["sucesful",[1],true,false,1000,0,1,0]],[["sucesful",1,[["sucesful",1000,true,false]],[[0,8]],"sucesful"]],,["\u003cb\u003e\u003ci\u003esuccessful\u003c/i\u003e\u003c/b\u003e","successful",[1]],[["en"]],7] 19 | 20 | text-phonetic: 21 | 22 | 23 | translation-phonetic: 24 | sucesful 25 | 26 | translation: 27 | sucesful 28 | 29 | suggestion: 30 | successful 31 | 32 | detailed-translation: 33 | -------------------------------------------------------------------------------- /test/fixtures/word/6.fixture: -------------------------------------------------------------------------------- 1 | развести 2 | 3 | 4 | HTTP/1.1 200 OK 5 | Pragma: no-cache 6 | Date: Wed, 05 Mar 2014 20:58:31 GMT 7 | Expires: Wed, 05 Mar 2014 20:58:31 GMT 8 | Cache-Control: private, max-age=600 9 | Content-Type: text/javascript; charset=UTF-8 10 | Content-Language: en 11 | X-Content-Type-Options: nosniff 12 | Content-Disposition: attachment 13 | Server: HTTP server (unknown) 14 | X-XSS-Protection: 1; mode=block 15 | Alternate-Protocol: 80:quic 16 | Transfer-Encoding: chunked 17 | 18 | [[["diluted","развести","","razvesti"]],,"ru",,[["diluted",[1],true,false,480,0,1,0]],[["развести",1,[["diluted",480,true,false],["dilute",290,true,false],["dissolve",161,true,false],["breed",36,true,false],["divorce",29,true,false]],[[0,8]],"развести"]],,,[["ru"]],30] 19 | 20 | text-phonetic: 21 | razvesti 22 | 23 | translation-phonetic: 24 | 25 | 26 | translation: 27 | diluted 28 | 29 | suggestion: 30 | 31 | 32 | detailed-translation: 33 | -------------------------------------------------------------------------------- /test/fixtures/word/7.fixture: -------------------------------------------------------------------------------- 1 | разочарование 2 | 3 | 4 | HTTP/1.1 200 OK 5 | Pragma: no-cache 6 | Date: Wed, 05 Mar 2014 20:58:31 GMT 7 | Expires: Wed, 05 Mar 2014 20:58:31 GMT 8 | Cache-Control: private, max-age=600 9 | Content-Type: text/javascript; charset=UTF-8 10 | Content-Language: uk 11 | X-Content-Type-Options: nosniff 12 | Content-Disposition: attachment 13 | Server: HTTP server (unknown) 14 | X-XSS-Protection: 1; mode=block 15 | Alternate-Protocol: 80:quic 16 | Transfer-Encoding: chunked 17 | 18 | [[["розчарування","разочарование","rozcharuvannya","razocharovaniye"]],,"ru",,[["розчарування",[1],true,false,1000,0,1,0]],[["разочарование",1,[["розчарування",1000,true,false],["зневіра",0,true,false]],[[0,13]],"разочарование"]],,,[["ru"]],13] 19 | 20 | text-phonetic: 21 | razocharovaniye 22 | 23 | translation-phonetic: 24 | rozcharuvannya 25 | 26 | translation: 27 | розчарування 28 | 29 | suggestion: 30 | 31 | 32 | detailed-translation: 33 | -------------------------------------------------------------------------------- /test/google-translate-core-test.el: -------------------------------------------------------------------------------- 1 | (ert-deftest test-google-translate--insert-nulls () 2 | (should (string-equal 3 | (google-translate--insert-nulls "[,[,[,,],,],,]") 4 | "[null,[null,[null,null,null],null,null],null,null]"))) 5 | 6 | ;; (ert-deftest test-google-translate-request-words-fixtures () 7 | ;; (dolist (file (f-files google-translate-test/word-fixture-path)) 8 | ;; (let ((fixture (th-google-translate-load-fixture file))) 9 | ;; (th-google-translate-request-fixture fixture) 10 | ;; ;; assertions are skipped. In case of no errors assume that test pass. 11 | ;; ))) 12 | 13 | ;; (ert-deftest test-google-translate-request-sentences-fixtures () 14 | ;; (dolist (file (f-files google-translate-test/sentence-fixture-path)) 15 | ;; (let ((fixture (th-google-translate-load-fixture file))) 16 | ;; (th-google-translate-request-fixture fixture) 17 | ;; ;; assertions are skipped. In case of no errors assume that test pass. 18 | ;; ))) 19 | 20 | (ert-deftest test-google-translate--strip-string-with-spaces () 21 | (should (string-equal 22 | (google-translate--strip-string " spaces spaces ") 23 | " spaces spaces "))) 24 | 25 | (ert-deftest test-google-translate--strip-string-with-carriage-return-and-line-feeds () 26 | (should (string-equal 27 | (google-translate--strip-string "\n\n\r\nspaces\r\n\n\r\r\n\nspaces\r\n\r\r\r\n") 28 | " spaces spaces "))) 29 | 30 | (ert-deftest test-google-translate--trim-string-with-spaces () 31 | (should (string-equal 32 | (google-translate--trim-string " spaces spaces ") 33 | "spaces spaces"))) 34 | 35 | (ert-deftest test-google-translate-prepare-text-for-request () 36 | (should (string-equal 37 | (google-translate-prepare-text-for-request "\n\r\nspaces\r \n\n\rspaces\n\n\r") 38 | "spaces spaces"))) 39 | 40 | ;; (ert-deftest test-google-translate-json-text-phonetic () 41 | ;; (dolist (file (f-files google-translate-test/word-fixture-path)) 42 | ;; (let ((fixture (th-google-translate-load-fixture file))) 43 | ;; (should (string-equal 44 | ;; (th-google-translate-fixture-text-phonetic fixture) 45 | ;; (google-translate-json-text-phonetic 46 | ;; (th-google-translate-request-fixture fixture))))))) 47 | 48 | ;; (ert-deftest test-google-translate-json-translation-phonetic () 49 | ;; (dolist (file (f-files google-translate-test/word-fixture-path)) 50 | ;; (let ((fixture (th-google-translate-load-fixture file))) 51 | ;; (should (string-equal 52 | ;; (th-google-translate-fixture-translation-phonetic fixture) 53 | ;; (google-translate-json-translation-phonetic 54 | ;; (th-google-translate-request-fixture fixture))))))) 55 | 56 | ;; (ert-deftest test-google-translate-json-translation () 57 | ;; (dolist (file (f-files google-translate-test/word-fixture-path)) 58 | ;; (let ((fixture (th-google-translate-load-fixture file))) 59 | ;; (should (string-equal 60 | ;; (th-google-translate-fixture-translation fixture) 61 | ;; (google-translate-json-translation 62 | ;; (th-google-translate-request-fixture fixture))))))) 63 | 64 | ;; (ert-deftest test-google-translate-json-suggestion () 65 | ;; (dolist (file (f-files google-translate-test/word-fixture-path)) 66 | ;; (let ((fixture (th-google-translate-load-fixture file))) 67 | ;; (should (string-equal 68 | ;; (th-google-translate-fixture-suggestion fixture) 69 | ;; (google-translate-json-suggestion 70 | ;; (th-google-translate-request-fixture fixture))))))) 71 | 72 | ;; (ert-deftest test-google-translate-json-detailed-translation () 73 | ;; (dolist (file (f-files google-translate-test/word-fixture-path)) 74 | ;; (let* ((fixture (th-google-translate-load-fixture file)) 75 | ;; (index 0) 76 | ;; (detailed-translation (google-translate-json-detailed-translation 77 | ;; (th-google-translate-request-fixture fixture))) 78 | ;; (fixture-dt (th-google-translate-fixture-detailed-translation fixture)) 79 | ;; (detailed-translation-str "") 80 | ;; (fixture-dt-str "")) 81 | ;; (when fixture-dt 82 | ;; (with-temp-buffer 83 | ;; (th-google-translate-detailed-translation-to-string detailed-translation) 84 | ;; (setq detailed-translation-str (buffer-substring-no-properties (point-min) (point-max)))) 85 | ;; (setq fixture-dt-str (mapconcat (lambda (w) w) fixture-dt "\n")) 86 | ;; (should (string-equal 87 | ;; detailed-translation-str 88 | ;; fixture-dt-str)))))) 89 | 90 | (ert-deftest test-google-translate-request-empty-text () 91 | (should (null 92 | (google-translate-request "en" "ru" "")))) 93 | 94 | (defvar test-example-query "client=t&ie=UTF-8&oe=UTF-8&sl=en&tl=ru&sc=2&text=first") 95 | 96 | (defvar test-example-query-params '(("client" . "t") 97 | ("ie" . "UTF-8") 98 | ("oe" . "UTF-8") 99 | ("sl" . "en") 100 | ("tl" . "ru") 101 | ("sc" . "2") 102 | ("text" . "first"))) 103 | 104 | (ert-deftest test-google-translate--format-query-string () 105 | (should (string-equal 106 | test-example-query 107 | (google-translate--format-query-string 108 | test-example-query-params)))) 109 | 110 | (ert-deftest test-google-translate--format-request-url () 111 | (should (string-equal 112 | (concat google-translate-base-url "?" test-example-query) 113 | (google-translate--format-request-url test-example-query-params)))) 114 | -------------------------------------------------------------------------------- /test/google-translate-core-ui-test.el: -------------------------------------------------------------------------------- 1 | ;; (ert-deftest test-google-translate-request-words-fixtures () 2 | ;; (dolist (file (f-files google-translate-test/word-fixture-path)) 3 | ;; (let ((fixture (th-google-translate-load-fixture file))) 4 | ;; (th-google-translate-request-fixture fixture) 5 | ;; ;; assertions are skipped. In case of no errors assume that test pass. 6 | ;; ))) 7 | 8 | (ert-deftest test-google-translate-language-abbreviation/English/en () 9 | (should 10 | (string-equal 11 | (google-translate-language-abbreviation "English") 12 | "en"))) 13 | 14 | (ert-deftest test-google-translate-language-abbreviation/Detect-Language/auto () 15 | (should 16 | (string-equal 17 | (google-translate-language-abbreviation "Detect language") 18 | "auto"))) 19 | 20 | (ert-deftest test-google-translate-language-display-name/auto/unspecified () 21 | (should 22 | (string-equal 23 | (google-translate-language-display-name "auto") 24 | "unspecified language"))) 25 | 26 | (ert-deftest test-google-translate-language-display-name/en/English () 27 | (should 28 | (string-equal 29 | (google-translate-language-display-name "en") 30 | "English"))) 31 | 32 | (ert-deftest test-google-translate--translation-title/source-auto/detected () 33 | (should 34 | (string-equal 35 | "Translate from English (detected) to Russian:\n" 36 | (google-translate--translation-title (make-gtos :source-language "auto" 37 | :target-language "ru" 38 | :auto-detected-language "en") 39 | "Translate from %s to %s:\n")))) 40 | 41 | (ert-deftest test-google-translate--translation-title/source-auto/detected-nil () 42 | (should 43 | (string-equal 44 | "Translate from English to Russian:\n" 45 | (google-translate--translation-title (make-gtos :source-language "en" 46 | :target-language "ru" 47 | :auto-detected-language nil) 48 | "Translate from %s to %s:\n")))) 49 | 50 | (ert-deftest test-google-translate--text-phonetic/do-not-show-phonetic () 51 | (should 52 | (string-equal 53 | "" 54 | (google-translate--text-phonetic 55 | (make-gtos :text-phonetic "phonetic") "%s")))) 56 | 57 | (ert-deftest test-google-translate--text-phonetic/show-phonetic-but-empty () 58 | (setq google-translate-show-phonetic t) 59 | (should 60 | (string-equal 61 | "" 62 | (google-translate--text-phonetic (make-gtos :text-phonetic "") "%s"))) 63 | (setq google-translate-show-phonetic nil)) 64 | 65 | (ert-deftest test-google-translate--text-phonetic/show-phonetic () 66 | (setq google-translate-show-phonetic t) 67 | (should 68 | (string-equal 69 | "phonetic" 70 | (google-translate--text-phonetic 71 | (make-gtos :text-phonetic "phonetic") "%s"))) 72 | (setq google-translate-show-phonetic nil)) 73 | 74 | (ert-deftest test-google-translate--translated-text () 75 | (should 76 | (string-equal 77 | "translation" 78 | (google-translate--translated-text 79 | (make-gtos :translation "translation") "%s")))) 80 | 81 | (ert-deftest test-google-translate--suggestion () 82 | (should 83 | (string-equal 84 | "\nDid you mean: suggest\n" 85 | (google-translate--suggestion (make-gtos 86 | :suggestion "suggest" 87 | :source-language "en" 88 | :target-language "ru"))))) 89 | 90 | (ert-deftest test-google-translate--suggestion-action () 91 | (with-temp-buffer 92 | (let* ((suggestion "suggestion") 93 | (source-language "en") 94 | (target-language "ru") 95 | (button (insert-text-button "Foo" 96 | 'action 'test 97 | 'suggestion suggestion 98 | 'source-language source-language 99 | 'target-language target-language))) 100 | (with-mock 101 | (mock (google-translate-translate source-language 102 | target-language 103 | suggestion)) 104 | (google-translate--suggestion-action button))))) 105 | 106 | (ert-deftest test-google-translate--translation-phonetic/do-not-show-phonetic () 107 | (should 108 | (string-equal 109 | "" 110 | (google-translate--translation-phonetic 111 | (make-gtos :translation-phonetic "phonetic") "%s")))) 112 | 113 | (ert-deftest test-google-translate--translation-phonetic/show-phonetic-but-empty () 114 | (setq google-translate-show-phonetic t) 115 | (should 116 | (string-equal 117 | "" 118 | (google-translate--translation-phonetic 119 | (make-gtos :translation-phonetic "") "%s"))) 120 | (setq google-translate-show-phonetic nil)) 121 | 122 | (ert-deftest test-google-translate--translation-phonetic/show-phonetic () 123 | (setq google-translate-show-phonetic t) 124 | (should 125 | (string-equal 126 | "phonetic" 127 | (google-translate--translation-phonetic 128 | (make-gtos :translation-phonetic "phonetic") "%s"))) 129 | (setq google-translate-show-phonetic nil)) 130 | 131 | (ert-deftest test-google-translate-read-source-language/detect-language () 132 | (with-mock 133 | (stub google-translate-completing-read => "Detect language") 134 | (should 135 | (string-equal 136 | (google-translate-read-source-language) 137 | "auto")))) 138 | 139 | (ert-deftest test-google-translate-read-source-language/english () 140 | (with-mock 141 | (stub google-translate-completing-read => "English") 142 | (should 143 | (string-equal 144 | (google-translate-read-source-language) 145 | "en")))) 146 | -------------------------------------------------------------------------------- /test/test-helper.el: -------------------------------------------------------------------------------- 1 | (require 'f) 2 | (require 's) 3 | (require 'dash) 4 | (require 'el-mock) 5 | 6 | (defvar google-translate-test/test-path 7 | (if (null load-file-name) 8 | (f-dirname (buffer-file-name)) 9 | (f-dirname load-file-name))) 10 | 11 | (defvar google-translate-test/fixture-path 12 | (f-expand "fixtures" google-translate-test/test-path)) 13 | 14 | (defvar google-translate-test/word-fixture-path 15 | (f-expand "word" google-translate-test/fixture-path)) 16 | 17 | (defvar google-translate-test/phrase-fixture-path 18 | (f-expand "phrase" google-translate-test/fixture-path)) 19 | 20 | (defvar google-translate-test/sentence-fixture-path 21 | (f-expand "sentence" google-translate-test/fixture-path)) 22 | 23 | (defvar google-translate-test/root-path 24 | (f-parent google-translate-test/test-path)) 25 | 26 | (setq debug-on-entry t) 27 | (setq debug-on-error t) 28 | 29 | (add-to-list 'load-path google-translate-test/root-path) 30 | 31 | (require 'google-translate 32 | (f-expand "google-translate" 33 | google-translate-test/root-path)) 34 | 35 | (require 'google-translate-default-ui 36 | (f-expand "google-translate-default-ui" 37 | google-translate-test/root-path)) 38 | 39 | (defun th-google-translate-load-fixture (file) 40 | (with-temp-buffer 41 | (insert-file-contents file) 42 | (set-buffer-multibyte t) 43 | (goto-char (point-min)) 44 | (re-search-forward (format "\n\n\n")) 45 | (delete-region (point-min) (point)) 46 | (buffer-string))) 47 | 48 | (defun th-google-translate-fixture-response (fixture) 49 | (with-temp-buffer 50 | (insert fixture) 51 | (set-buffer-multibyte t) 52 | (goto-char (point-min)) 53 | (let ((stpoint (point))) 54 | (re-search-forward (format "\n\n")) 55 | (re-search-forward (format "\n\n")) 56 | (buffer-substring-no-properties stpoint (point))))) 57 | 58 | (defun th-google-translate-temp-buffer (contents) 59 | (let ((buffer (get-buffer-create "*Google Translate Temp Buffer*"))) 60 | (with-current-buffer buffer 61 | (erase-buffer) 62 | (insert contents) 63 | buffer))) 64 | 65 | (defun th-google-translate-request-fixture (fixture) 66 | (with-mock 67 | (stub url-retrieve-synchronously => 68 | (th-google-translate-temp-buffer 69 | (th-google-translate-fixture-response fixture))) 70 | (google-translate-request "en" "ru" "return"))) 71 | 72 | (defun th-google-translate-fixture-text-phonetic (fixture) 73 | (with-current-buffer (th-google-translate-temp-buffer fixture) 74 | (goto-char (point-min)) 75 | (search-forward "text-phonetic:") 76 | (forward-line 1) 77 | (buffer-substring-no-properties (line-beginning-position) 78 | (line-end-position)))) 79 | 80 | (defun th-google-translate-fixture-translation-phonetic (fixture) 81 | (with-current-buffer (th-google-translate-temp-buffer fixture) 82 | (goto-char (point-min)) 83 | (search-forward "translation-phonetic:") 84 | (forward-line 1) 85 | (buffer-substring-no-properties (line-beginning-position) 86 | (line-end-position)))) 87 | 88 | (defun th-google-translate-fixture-translation (fixture) 89 | (with-current-buffer (th-google-translate-temp-buffer fixture) 90 | (goto-char (point-min)) 91 | (search-forward "translation:") 92 | (forward-line 1) 93 | (buffer-substring-no-properties (line-beginning-position) 94 | (line-end-position)))) 95 | 96 | (defun th-google-translate-fixture-suggestion (fixture) 97 | (with-current-buffer (th-google-translate-temp-buffer fixture) 98 | (goto-char (point-min)) 99 | (search-forward "suggestion:") 100 | (forward-line 1) 101 | (let ((result (buffer-substring-no-properties 102 | (line-beginning-position) 103 | (line-end-position)))) 104 | (if (equal result "") 105 | nil 106 | result)))) 107 | 108 | (defun th-google-translate-fixture-detailed-translation (fixture) 109 | (with-current-buffer (th-google-translate-temp-buffer fixture) 110 | (goto-char (point-min)) 111 | (search-forward "detailed-translation:") 112 | (let ((line " ") (result '())) 113 | (while (not (equal line "")) 114 | (forward-line 1) 115 | (setq line (buffer-substring-no-properties (line-beginning-position) 116 | (line-end-position))) 117 | (setq result (append result `(,line)))) 118 | result))) 119 | 120 | (defun th-google-translate-detailed-translation-to-string (detailed-translation) 121 | (unless (null detailed-translation) 122 | (loop for item across detailed-translation do 123 | (unless (string-equal (aref item 0) "") 124 | (insert (format "%s\n" (aref item 0))) 125 | (loop for translation across (aref item 1) do 126 | (insert (format "%s\n" translation))))))) 127 | 128 | 129 | 130 | (defun th-google-translate-generate-fixtures () 131 | (interactive) 132 | (th-google-translate-generate-word-fixtures) 133 | (th-google-translate-generate-sentence-fixtures)) 134 | 135 | (defun th-google-translate-generate-word-fixtures () 136 | (interactive) 137 | (th-google-translate-generate-word-fixture "return" "en" "ru" 1) 138 | (th-google-translate-generate-word-fixture "belongs" "en" "ru" 2) 139 | (th-google-translate-generate-word-fixture "first" "en" "ru" 3) 140 | (th-google-translate-generate-word-fixture "колобок" "ru" "uk" 4) 141 | (th-google-translate-generate-word-fixture "sucesful" "en" "ru" 5) 142 | (th-google-translate-generate-word-fixture "развести" "ru" "en" 6) 143 | (th-google-translate-generate-word-fixture "разочарование" "ru" "uk" 7)) 144 | 145 | (defun th-google-translate-generate-sentence-fixtures () 146 | (interactive) 147 | (th-google-translate-generate-sentence-fixture "спочатку було слово" "uk" "en" 1) 148 | (th-google-translate-generate-sentence-fixture "век живи, век учись" "ru" "uk" 2)) 149 | 150 | (defun th-google-translate-generate-word-fixture (word 151 | source-language 152 | target-language 153 | fixture-num) 154 | (interactive "sWord: \nsFrom: \nsTo: \nsFixture-num: ") 155 | (th-google-translate-generate-fixture 156 | word 157 | source-language 158 | target-language 159 | fixture-num 160 | google-translate-test/word-fixture-path)) 161 | 162 | (defun th-google-translate-generate-sentence-fixture (word 163 | source-language 164 | target-language 165 | fixture-num) 166 | (interactive "sSentence: \nsFrom: \nsTo: \nsFixture-num: ") 167 | (th-google-translate-generate-fixture 168 | word 169 | source-language 170 | target-language 171 | fixture-num 172 | google-translate-test/sentence-fixture-path)) 173 | 174 | (defun th-google-translate-generate-fixture (word 175 | source-language 176 | target-language 177 | fixture-num 178 | fixture-dir-path) 179 | (interactive "sWord: \nsFrom: \nsTo: \nsFixture-num: ") 180 | (let* ((response (with-current-buffer 181 | (google-translate--request 182 | source-language 183 | target-language 184 | word 185 | t) 186 | (buffer-string))) 187 | (json (json-read-from-string 188 | (google-translate--insert-nulls 189 | (with-temp-buffer 190 | (insert response) 191 | (goto-char (point-min)) 192 | (re-search-forward (format "\n\n")) 193 | (delete-region (point-min) (point)) 194 | (buffer-string)))))) 195 | (with-temp-buffer 196 | (insert (format "%s\n\n\n" word)) 197 | (insert response) 198 | (insert (format "\n\n%s\n" "text-phonetic:")) 199 | (let ((text-phonetic (google-translate-json-text-phonetic json))) 200 | (when (null text-phonetic) 201 | (setq text-phonetic "")) 202 | (insert (format "%s\n" text-phonetic))) 203 | (insert (format "\n%s\n" "translation-phonetic:")) 204 | (let ((translation-phonetic (google-translate-json-translation-phonetic json))) 205 | (when (null translation-phonetic) 206 | (setq translation-phonetic "")) 207 | (insert (format "%s\n" translation-phonetic))) 208 | (insert (format "\n%s\n" "translation:")) 209 | (let ((translation (google-translate-json-translation json))) 210 | (when (null translation) 211 | (setq translation "")) 212 | (insert (format "%s\n" translation))) 213 | (insert (format "\n%s\n" "suggestion:")) 214 | (let ((suggestion (google-translate-json-suggestion json))) 215 | (when (null suggestion) 216 | (setq suggestion "")) 217 | (insert (format "%s\n" suggestion))) 218 | (insert (format "\n%s\n" "detailed-translation:")) 219 | (let ((detailed-translation (google-translate-json-detailed-translation json))) 220 | (unless (null detailed-translation) 221 | (loop for item across detailed-translation do 222 | (unless (string-equal (aref item 0) "") 223 | (insert (format "%s\n" (aref item 0))) 224 | (loop for translation across (aref item 1) do 225 | (insert (format "%s\n" translation))))))) 226 | (write-file 227 | (concat 228 | fixture-dir-path 229 | "/" 230 | (format "%s.fixture" fixture-num)))))) 231 | --------------------------------------------------------------------------------