├── .rspec
├── Gemfile
├── .gitignore
├── lib
├── easy_translate
│ ├── version.rb
│ ├── easy_translate_exception.rb
│ ├── threadable.rb
│ ├── translation_target.rb
│ ├── detection.rb
│ ├── request.rb
│ ├── languages.rb
│ └── translation.rb
└── easy_translate.rb
├── spec
├── spec_helper.rb
└── examples
│ ├── easy_translate_spec.rb
│ ├── translation_target_spec.rb
│ ├── request_spec.rb
│ ├── real_world_spec.rb
│ ├── detection_spec.rb
│ └── translation_spec.rb
├── easy_translate.gemspec
├── .github
└── workflows
│ └── test.yml
├── Rakefile
└── README.md
/.rspec:
--------------------------------------------------------------------------------
1 | -c -fd
2 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 | gemspec
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.swp
2 | *.swo
3 | *.gem
4 | .ruby-version
5 | coverage/*
6 |
7 | Gemfile.lock
8 |
--------------------------------------------------------------------------------
/lib/easy_translate/version.rb:
--------------------------------------------------------------------------------
1 | module EasyTranslate
2 |
3 | VERSION = '0.5.0'
4 |
5 | end
6 |
--------------------------------------------------------------------------------
/lib/easy_translate/easy_translate_exception.rb:
--------------------------------------------------------------------------------
1 | module EasyTranslate
2 |
3 | class EasyTranslateException < StandardError
4 | end
5 |
6 | end
7 |
--------------------------------------------------------------------------------
/spec/spec_helper.rb:
--------------------------------------------------------------------------------
1 | # Start SimpleCov
2 | begin
3 | require 'ostruct'
4 | require 'simplecov'
5 | SimpleCov.start
6 | rescue LoadError
7 | puts 'for coverage please install SimpleCov'
8 | end
9 |
10 | require 'ostruct'
11 |
12 | # Require the actual project
13 | $: << File.expand_path('../lib', __FILE__)
14 | require 'easy_translate'
15 |
--------------------------------------------------------------------------------
/spec/examples/easy_translate_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe EasyTranslate do
4 |
5 | it 'should have LANGUAGES' do
6 | expect(EasyTranslate::LANGUAGES).to be_a(Hash)
7 | expect(EasyTranslate::LANGUAGES['en']).to eq('english')
8 | end
9 |
10 | it 'should have a version' do
11 | expect(EasyTranslate::VERSION.split('.').length).to eq(3)
12 | end
13 |
14 | end
15 |
--------------------------------------------------------------------------------
/lib/easy_translate.rb:
--------------------------------------------------------------------------------
1 | require 'easy_translate/detection'
2 | require 'easy_translate/translation'
3 | require 'easy_translate/translation_target'
4 |
5 | module EasyTranslate
6 |
7 | autoload :EasyTranslateException, 'easy_translate/easy_translate_exception'
8 | autoload :Request, 'easy_translate/request'
9 |
10 | autoload :LANGUAGES, 'easy_translate/languages'
11 | autoload :VERSION, 'easy_translate/version'
12 |
13 | extend Detection # Language Detection
14 | extend Translation # Language Translation
15 | extend TranslationTarget # Language Translation Targets
16 |
17 | class << self
18 | attr_accessor :api_key
19 | end
20 |
21 | end
22 |
--------------------------------------------------------------------------------
/easy_translate.gemspec:
--------------------------------------------------------------------------------
1 | require File.expand_path('lib/easy_translate/version', File.dirname(__FILE__))
2 |
3 | spec = Gem::Specification.new do |s|
4 | s.name = 'easy_translate'
5 | s.author = 'John Crepezzi'
6 | s.add_development_dependency('rspec')
7 | s.add_development_dependency('rake')
8 | s.add_dependency 'thread'
9 | s.add_dependency 'thread_safe'
10 | s.description = 'easy_translate is a wrapper for the google translate API that makes sense programatically, and implements API keys'
11 | s.email = 'john.crepezzi@gmail.com'
12 | s.files = Dir['lib/**/*.rb']
13 | s.has_rdoc = true
14 | s.homepage = 'https://github.com/seejohnrun/easy_translate'
15 | s.platform = Gem::Platform::RUBY
16 | s.require_paths = ['lib']
17 | s.summary = 'Google Translate API Wrapper for Ruby'
18 | s.test_files = Dir.glob('spec/*.rb')
19 | s.version = EasyTranslate::VERSION
20 | s.license = 'MIT'
21 | end
22 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: Test
2 | on:
3 | push:
4 | branches: [ main ]
5 | pull_request:
6 | branches: ['*']
7 |
8 | jobs:
9 | test_sqlite:
10 | runs-on: ubuntu-latest
11 | strategy:
12 | fail-fast: false
13 | matrix:
14 | include:
15 | ### TEST ALL RUBY VERSIONS
16 | - ruby: 2.3 ### min version supported by Bundler 2.x, ruby: 2.3
17 | - ruby: 2.4
18 | - ruby: 2.5
19 | - ruby: 2.6
20 | - ruby: 2.7
21 | - ruby: "3.0" ### must be quoted otherwise will be treated as "3" which resolves to latest 3.x version
22 | - ruby: 3.1
23 |
24 | env:
25 | BUNDLE_GEMFILE: "${{ matrix.gemfile }}"
26 |
27 | steps:
28 | - uses: actions/checkout@v2
29 |
30 | - name: Install ruby
31 | uses: ruby/setup-ruby@v1
32 | with:
33 | ruby-version: "${{ matrix.ruby }}"
34 | bundler-cache: true
35 |
36 | - name: Run tests
37 | run: |
38 | bundle exec rake
39 |
--------------------------------------------------------------------------------
/spec/examples/translation_target_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | klass = EasyTranslate::TranslationTarget::TranslationTargetRequest
4 | describe klass do
5 |
6 | describe :params do
7 |
8 | it 'should include target in params if given' do
9 | req = klass.new('en')
10 | expect(req.params[:target]).to eq('en')
11 | end
12 |
13 | it 'should not include target by default' do
14 | req = klass.new
15 | expect(req.params[:target]).to be_nil
16 | end
17 |
18 | it 'should use default key' do
19 | EasyTranslate.api_key = 'abc'
20 | request = klass.new('en')
21 | expect(request.params[:key]).to eq('abc')
22 | end
23 |
24 | it 'should allow overriding of params' do
25 | EasyTranslate.api_key = 'abc'
26 | request = klass.new('en', :key => 'def')
27 | expect(request.params[:key]).to eq('def')
28 | end
29 |
30 | it 'should allow overriding of key as api_key' do
31 | EasyTranslate.api_key = 'abc'
32 | request = klass.new('abc', :api_key => 'def', :to => 'es')
33 | expect(request.params[:key]).to eq('def')
34 | expect(request.params[:api_key]).to be_nil
35 | end
36 |
37 | end
38 |
39 | end
40 |
--------------------------------------------------------------------------------
/lib/easy_translate/threadable.rb:
--------------------------------------------------------------------------------
1 | require 'thread/pool'
2 | require 'thread_safe'
3 |
4 | module EasyTranslate
5 |
6 | module Threadable
7 | def threaded_process(method, *args)
8 | texts = args[0]
9 | options = args[1]
10 | http_options = args[2]
11 | options = options.dup
12 | batch_size = options.delete(:batch_size) || 100
13 | concurrency = options.delete(:concurrency) || 4
14 | batches = Array(texts).each_slice(batch_size).to_a
15 | if concurrency > 1 && batches.size > 1
16 | pool = Thread::Pool.new([concurrency, 1 + (texts.length - 1) / batch_size].min)
17 | batch_results = ThreadSafe::Array.new
18 | batches.each_with_index do |texts_batch, i|
19 | pool.process { batch_results[i] = self.send(method, texts_batch, options, http_options) }
20 | end
21 | pool.shutdown
22 | results = batch_results.reduce(:+)
23 | else
24 | results = batches.map { |texts_batch| self.send(method, texts_batch, options, http_options) }.reduce(:+)
25 | end
26 | # if they only asked for one, only give one back
27 | texts.is_a?(String) ? results[0] : results
28 | ensure
29 | pool.shutdown! if pool && !pool.shutdown?
30 | end
31 | end
32 |
33 | end
34 |
--------------------------------------------------------------------------------
/spec/examples/request_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe EasyTranslate::Request do
4 |
5 | describe :path do
6 |
7 | it 'should raise a NotImplementedError on this base class' do
8 | request = EasyTranslate::Request.new
9 | expect do
10 | request.path
11 | end.to raise_error NotImplementedError
12 | end
13 |
14 | end
15 |
16 | describe :body do
17 |
18 | it 'should be blank by default' do
19 | request = EasyTranslate::Request.new
20 | expect(request.body).to be_empty
21 | end
22 |
23 | end
24 |
25 | describe :params do
26 |
27 | it 'should include the key if given at the base' do
28 | EasyTranslate.api_key = 'abc'
29 | request = EasyTranslate::Request.new
30 | expect(request.params[:key]).to eq('abc')
31 | end
32 |
33 | it 'should turn off prettyPrint' do
34 | request = EasyTranslate::Request.new
35 | expect(request.params[:prettyPrint]).to eq('false')
36 | end
37 |
38 | end
39 |
40 | describe :param_s do
41 |
42 | it 'should skip nil parameters' do
43 | request = EasyTranslate::Request.new
44 | expect(request).to receive(:params).and_return({ :something => nil })
45 | expect(request.send(:param_s)).to be_empty
46 | end
47 |
48 | end
49 |
50 | end
51 |
--------------------------------------------------------------------------------
/lib/easy_translate/translation_target.rb:
--------------------------------------------------------------------------------
1 | require 'json'
2 | require 'easy_translate/request'
3 |
4 | module EasyTranslate
5 |
6 | module TranslationTarget
7 |
8 | # Determine what translations are available
9 | # @param [String] source - The source language (optional)
10 | # @param [Hash] options - extra options
11 | # @return [Array] an array of strings representing languages
12 | def translations_available(target = nil, options = {})
13 | request = TranslationTargetRequest.new(target, options)
14 | raw = request.perform_raw
15 | languages = JSON.parse(raw)['data']['languages'].map do |res|
16 | res['language']
17 | end
18 |
19 | languages.push('zh-CN') if !languages.index('zh').nil?
20 |
21 | languages
22 | end
23 |
24 | class TranslationTargetRequest < EasyTranslate::Request
25 |
26 | def initialize(target = nil, options = nil)
27 | super(options)
28 | @target = target
29 | if @options
30 | if replacement_api_key = @options.delete(:api_key)
31 | @options[:key] = replacement_api_key
32 | end
33 | end
34 | end
35 |
36 | def params
37 | params = super || {}
38 | params[:target] = @target unless @target.nil?
39 | params.merge! @options if @options
40 | params
41 | end
42 |
43 | def path
44 | '/language/translate/v2/languages'
45 | end
46 |
47 | end
48 |
49 | end
50 |
51 | end
52 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | require 'rspec/core/rake_task'
2 | require 'bundler'
3 |
4 | task :build => :test do
5 | system "gem build easy_translate.gemspec"
6 | end
7 |
8 | task :release => :build do
9 | # tag and push
10 | version = Bundler.load_gemspec('easy_translate.gemspec').version
11 | system "git tag v#{version}"
12 | system "git push origin --tags"
13 | # push the gem
14 | system "gem push easy_translate-#{version}.gem"
15 | end
16 |
17 | RSpec::Core::RakeTask.new(:test) do |t|
18 | t.pattern = 'spec/**/*_spec.rb'
19 | fail_on_error = true # be explicit
20 | end
21 |
22 | task :default => :test
23 |
24 | desc "Cache API languages into lib/easy_translate/languages.rb, must set GOOGLE_TRANSLATE_API_KEY"
25 | task :cache_languages do
26 | $: << "lib"
27 | require "easy_translate"
28 | EasyTranslate.api_key = ENV.fetch("GOOGLE_TRANSLATE_API_KEY")
29 |
30 | language_filename = "lib/easy_translate/languages.rb"
31 | previous_contents = File.read(language_filename)
32 |
33 | response = JSON.parse(EasyTranslate::TranslationTarget::TranslationTargetRequest.new("en").perform_raw)
34 | language_keys = response.dig("data", "languages").map do |info|
35 | [info["name"].downcase.gsub(/[^a-z ]/, "").tr(" ", "_"), info["language"]]
36 | end.sort.map do |n, l|
37 | " '#{l}' => '#{n}'"
38 | end.join(",\n")
39 | new_contents = previous_contents.sub(/(LANGUAGES = {).*?(})/m, "\\1\n#{language_keys}\n \\2")
40 |
41 | File.open(language_filename, "w") do |f|
42 | f.write(new_contents)
43 | end
44 | end
45 |
--------------------------------------------------------------------------------
/spec/examples/real_world_spec.rb:
--------------------------------------------------------------------------------
1 | # encoding: utf-8
2 | require 'spec_helper'
3 |
4 | describe EasyTranslate do
5 |
6 | before :each do
7 | if ENV['API_KEY']
8 | EasyTranslate.api_key = ENV['API_KEY']
9 | else
10 | pending 'please provide an API_KEY for this suite'
11 | end
12 | end
13 |
14 | describe :translate do
15 | it 'should be able to translate one' do
16 | res = EasyTranslate.translate 'hello world', :to => :spanish
17 | expect(res).to eq('Hola Mundo')
18 | end
19 |
20 | it 'should be able to translate multiple' do
21 | res = EasyTranslate.translate ['hello world', 'good afternoon'], :to => :spanish
22 | expect(res).to eq(['Hola Mundo', 'buenas tardes'])
23 | end
24 |
25 | it 'should work concurrently' do
26 | res = EasyTranslate.translate ['hello world', 'good afternoon', 'i can translate text'], :to => :spanish, :concurrency => 2, :batch_size => 1
27 | expect(res).to eq(['Hola Mundo', 'buenas tardes', 'Puedo traducir texto'])
28 | end
29 | end
30 |
31 | describe :detect do
32 |
33 | it 'should be able to detect one' do
34 | res = EasyTranslate.detect 'hello world'
35 | expect(res).to eq('en')
36 | end
37 |
38 | it 'should be able to detect multiple' do
39 | res = EasyTranslate.detect ['hello world', 'hola mundo']
40 | expect(res).to eq(['en', 'es'])
41 | end
42 |
43 | end
44 |
45 | describe :translations_available_from do
46 |
47 | it 'should be able to get a list of all' do
48 | res = EasyTranslate.translations_available
49 | expect(res).to be_a Array
50 | end
51 |
52 | it 'should be able to get a list of all from es' do
53 | res = EasyTranslate.translations_available('yi')
54 | expect(res).to be_a Array
55 | end
56 |
57 | end
58 |
59 | end
60 |
--------------------------------------------------------------------------------
/lib/easy_translate/detection.rb:
--------------------------------------------------------------------------------
1 | require 'json'
2 | require 'cgi'
3 | require 'easy_translate/request'
4 | require 'easy_translate/threadable'
5 |
6 | module EasyTranslate
7 |
8 | module Detection
9 | include Threadable
10 |
11 | # Detect language
12 | # @param [String, Array] texts - A single string or set of strings to detect for
13 | # @param [Hash] options - Extra options to pass along with the request
14 | # @return [String, Array] The resultant language or languages
15 | def detect(texts, options = {}, http_options = {})
16 | threaded_process(:request_detection, texts, options, http_options)
17 | end
18 |
19 | private
20 | def request_detection(texts, options, http_options)
21 | request = DetectionRequest.new(texts, options, http_options)
22 | raw = request.perform_raw
23 | detections = JSON.parse(raw)['data']['detections'].map do |res|
24 | res.empty? ? nil :
25 | options[:confidence] ?
26 | { :language => res.first['language'], :confidence => res.first['confidence'] } : res.first['language']
27 | end
28 | end
29 |
30 | # A convenience class for wrapping a detection request
31 | class DetectionRequest < EasyTranslate::Request
32 |
33 | # Set the texts and options
34 | # @param [String, Array] texts - The text (or texts) to translate
35 | # @param [Hash] options - Options to override or pass along with the request
36 | def initialize(texts, options = {}, http_options = {})
37 | super(options, http_options)
38 | if replacement_api_key = @options.delete(:api_key)
39 | @options[:key] = replacement_api_key
40 | end
41 | self.texts = texts
42 | end
43 |
44 | # The params for this request
45 | # @return [Hash] the params for the request
46 | def params
47 | params = super || {}
48 | params.merge! @options if @options
49 | params
50 | end
51 |
52 | # The path for the request
53 | # @return [String] The path for the request
54 | def path
55 | '/language/translate/v2/detect'
56 | end
57 |
58 | # The body for the request
59 | # @return [String] the body for the request
60 | def body
61 | JSON.generate({ q: @texts })
62 | end
63 |
64 | # Whether or not this was a request for multiple texts
65 | # @return [Boolean]
66 | def multi?
67 | @multi
68 | end
69 |
70 | private
71 |
72 | # Set the texts for this request
73 | # @param [String, Array] texts - The text or texts for this request
74 | def texts=(texts)
75 | if texts.is_a?(String)
76 | @multi = false
77 | @texts = [texts]
78 | else
79 | @multi = true
80 | @texts = texts
81 | end
82 | end
83 |
84 | end
85 |
86 | end
87 |
88 | end
89 |
--------------------------------------------------------------------------------
/lib/easy_translate/request.rb:
--------------------------------------------------------------------------------
1 | require 'net/http'
2 | require 'net/https'
3 | require 'uri'
4 |
5 | module EasyTranslate
6 |
7 | class Request
8 | attr_accessor :http_options
9 |
10 | def initialize(options = {}, http_options = {})
11 | @options = options
12 | @http_options = http_options
13 | end
14 |
15 | # Body, blank by default
16 | # @return [String] The body for this request
17 | def body
18 | ''
19 | end
20 |
21 | # The path for the request
22 | # @return [String] The path for this request
23 | def path
24 | raise NotImplementedError.new('path is not implemented')
25 | end
26 |
27 | # The base params for a request
28 | # @return [Hash] a hash of the base parameters for any request
29 | def params
30 | params = {}
31 | params[:key] = EasyTranslate.api_key if EasyTranslate.api_key
32 | params[:prettyPrint] = 'false' # eliminate unnecessary overhead
33 | params
34 | end
35 |
36 | # Perform the given request
37 | # @return [String] The response String
38 | def perform_raw
39 | request = Net::HTTP::Post.new(uri.request_uri, { 'Content-Type' => 'application/json' })
40 | request.body = body
41 | response = http.request(request)
42 | raise_exception(response) unless response.code == '200'
43 | response.body
44 | end
45 |
46 | private
47 |
48 | def raise_exception(response)
49 | err = JSON.parse(response.body)['error']['errors'].first['message']
50 | rescue JSON::ParserError => _e
51 | err = "#{response.code} - #{response.message}"
52 | ensure
53 | raise EasyTranslateException.new(err)
54 | end
55 |
56 | def uri
57 | @uri ||= URI.parse("https://translation.googleapis.com#{path}?#{param_s}")
58 | end
59 |
60 | def http
61 | @http ||= Net::HTTP.new(uri.host, uri.port).tap do |http|
62 | configure_timeouts(http)
63 | configure_ssl(http)
64 | end
65 | end
66 |
67 | def configure_timeouts(http)
68 | http.read_timeout = http.open_timeout = http_options[:timeout] if http_options[:timeout]
69 | http.open_timeout = http_options[:open_timeout] if http_options[:open_timeout]
70 | end
71 |
72 | def configure_ssl(http)
73 | http.use_ssl = true
74 | http.verify_mode = OpenSSL::SSL::VERIFY_PEER
75 | http.cert_store = ssl_cert_store
76 |
77 | http.cert = ssl_options[:client_cert] if ssl_options[:client_cert]
78 | http.key = ssl_options[:client_key] if ssl_options[:client_key]
79 | http.ca_file = ssl_options[:ca_file] if ssl_options[:ca_file]
80 | http.ca_path = ssl_options[:ca_path] if ssl_options[:ca_path]
81 | http.verify_depth = ssl_options[:verify_depth] if ssl_options[:verify_depth]
82 | http.ssl_version = ssl_options[:version] if ssl_options[:version]
83 | end
84 |
85 | def ssl_cert_store
86 | return ssl_options[:cert_store] if ssl_options[:cert_store]
87 | # Use the default cert store by default, i.e. system ca certs
88 | cert_store = OpenSSL::X509::Store.new
89 | cert_store.set_default_paths
90 | cert_store
91 | end
92 |
93 | def ssl_options
94 | http_options[:ssl] || {}
95 | end
96 |
97 | # Stringify the params
98 | # @return [String] The params as a string
99 | def param_s
100 | params.map do |k, v|
101 | "#{k}=#{v}" unless v.nil?
102 | end.compact.join('&')
103 | end
104 |
105 | end
106 |
107 | end
108 |
--------------------------------------------------------------------------------
/lib/easy_translate/languages.rb:
--------------------------------------------------------------------------------
1 | module EasyTranslate
2 |
3 | LANGUAGES = {
4 | 'af' => 'afrikaans',
5 | 'sq' => 'albanian',
6 | 'am' => 'amharic',
7 | 'ar' => 'arabic',
8 | 'hy' => 'armenian',
9 | 'as' => 'assamese',
10 | 'ay' => 'aymara',
11 | 'az' => 'azerbaijani',
12 | 'bm' => 'bambara',
13 | 'eu' => 'basque',
14 | 'be' => 'belarusian',
15 | 'bn' => 'bengali',
16 | 'bho' => 'bhojpuri',
17 | 'bs' => 'bosnian',
18 | 'bg' => 'bulgarian',
19 | 'ca' => 'catalan',
20 | 'ceb' => 'cebuano',
21 | 'ny' => 'chichewa',
22 | 'zh' => 'chinese_simplified',
23 | 'zh-CN' => 'chinese_simplified',
24 | 'zh-TW' => 'chinese_traditional',
25 | 'co' => 'corsican',
26 | 'hr' => 'croatian',
27 | 'cs' => 'czech',
28 | 'da' => 'danish',
29 | 'dv' => 'divehi',
30 | 'doi' => 'dogri',
31 | 'nl' => 'dutch',
32 | 'en' => 'english',
33 | 'eo' => 'esperanto',
34 | 'et' => 'estonian',
35 | 'ee' => 'ewe',
36 | 'tl' => 'filipino',
37 | 'fi' => 'finnish',
38 | 'fr' => 'french',
39 | 'fy' => 'frisian',
40 | 'gl' => 'galician',
41 | 'lg' => 'ganda',
42 | 'ka' => 'georgian',
43 | 'de' => 'german',
44 | 'el' => 'greek',
45 | 'gn' => 'guarani',
46 | 'gu' => 'gujarati',
47 | 'ht' => 'haitian_creole',
48 | 'ha' => 'hausa',
49 | 'haw' => 'hawaiian',
50 | 'he' => 'hebrew',
51 | 'iw' => 'hebrew',
52 | 'hi' => 'hindi',
53 | 'hmn' => 'hmong',
54 | 'hu' => 'hungarian',
55 | 'is' => 'icelandic',
56 | 'ig' => 'igbo',
57 | 'ilo' => 'iloko',
58 | 'id' => 'indonesian',
59 | 'ga' => 'irish_gaelic',
60 | 'it' => 'italian',
61 | 'ja' => 'japanese',
62 | 'jv' => 'javanese',
63 | 'jw' => 'javanese',
64 | 'kn' => 'kannada',
65 | 'kk' => 'kazakh',
66 | 'km' => 'khmer',
67 | 'rw' => 'kinyarwanda',
68 | 'gom' => 'konkani',
69 | 'ko' => 'korean',
70 | 'kri' => 'krio',
71 | 'ku' => 'kurdish_kurmanji',
72 | 'ckb' => 'kurdish_sorani',
73 | 'ky' => 'kyrgyz',
74 | 'lo' => 'lao',
75 | 'la' => 'latin',
76 | 'lv' => 'latvian',
77 | 'ln' => 'lingala',
78 | 'lt' => 'lithuanian',
79 | 'lb' => 'luxembourgish',
80 | 'mk' => 'macedonian',
81 | 'mai' => 'maithili',
82 | 'mg' => 'malagasy',
83 | 'ms' => 'malay',
84 | 'ml' => 'malayalam',
85 | 'mt' => 'maltese',
86 | 'mi' => 'maori',
87 | 'mr' => 'marathi',
88 | 'mni-Mtei' => 'meiteilon_manipuri',
89 | 'lus' => 'mizo',
90 | 'mn' => 'mongolian',
91 | 'my' => 'myanmar_burmese',
92 | 'ne' => 'nepali',
93 | 'nso' => 'northern_sotho',
94 | 'no' => 'norwegian',
95 | 'or' => 'odia_oriya',
96 | 'om' => 'oromo',
97 | 'ps' => 'pashto',
98 | 'fa' => 'persian',
99 | 'pl' => 'polish',
100 | 'pt' => 'portuguese',
101 | 'pa' => 'punjabi',
102 | 'qu' => 'quechua',
103 | 'ro' => 'romanian',
104 | 'ru' => 'russian',
105 | 'sm' => 'samoan',
106 | 'sa' => 'sanskrit',
107 | 'gd' => 'scots_gaelic',
108 | 'sr' => 'serbian',
109 | 'st' => 'sesotho',
110 | 'sn' => 'shona',
111 | 'sd' => 'sindhi',
112 | 'si' => 'sinhala',
113 | 'sk' => 'slovak',
114 | 'sl' => 'slovenian',
115 | 'so' => 'somali',
116 | 'es' => 'spanish',
117 | 'su' => 'sundanese',
118 | 'sw' => 'swahili',
119 | 'sv' => 'swedish',
120 | 'tg' => 'tajik',
121 | 'ta' => 'tamil',
122 | 'tt' => 'tatar',
123 | 'te' => 'telugu',
124 | 'th' => 'thai',
125 | 'ti' => 'tigrinya',
126 | 'ts' => 'tsonga',
127 | 'tr' => 'turkish',
128 | 'tk' => 'turkmen',
129 | 'ak' => 'twi',
130 | 'uk' => 'ukrainian',
131 | 'ur' => 'urdu',
132 | 'ug' => 'uyghur',
133 | 'uz' => 'uzbek',
134 | 'vi' => 'vietnamese',
135 | 'cy' => 'welsh',
136 | 'xh' => 'xhosa',
137 | 'yi' => 'yiddish',
138 | 'yo' => 'yoruba',
139 | 'zu' => 'zulu'
140 | }
141 |
142 | end
143 |
--------------------------------------------------------------------------------
/lib/easy_translate/translation.rb:
--------------------------------------------------------------------------------
1 | require 'json'
2 | require 'cgi'
3 | require 'easy_translate/request'
4 | require 'easy_translate/threadable'
5 |
6 | module EasyTranslate
7 |
8 | module Translation
9 | include Threadable
10 |
11 | # Translate text
12 | # @param [String, Array] texts - A single string or set of strings to translate
13 | # @option options [Fixnum] :batch_size - Maximum keys per request (optional, default 100)
14 | # @option options [Fixnum] :concurrency - Maximum concurrent requests (optional, default 4)
15 | # @option options [String, Symbol] :source - The source language (optional)
16 | # @option options [String, Symbol] :target - The target language (required)
17 | # @option options [Boolean] :html - Whether or not the supplied string is HTML (optional)
18 | # @return [String, Array] Translated text or texts
19 | def translate(texts, options = {}, http_options = {})
20 | threaded_process(:request_translations, texts, options, http_options)
21 | end
22 |
23 | private
24 |
25 | # Perform a single request to translate texts
26 | # @param [Array] texts - Texts to translate
27 | # @option options [String, Symbol] :source - The source language (optional)
28 | # @option options [String, Symbol] :target - The target language (required)
29 | # @option options [Boolean] :html - Whether or not the supplied string is HTML (optional)
30 | # @return [String, Array] Translated text or texts
31 | def request_translations(texts, options = {}, http_options = {})
32 | request = TranslationRequest.new(texts, options, http_options)
33 | # Turn the response into an array of translations
34 | raw = request.perform_raw
35 | JSON.parse(raw)['data']['translations'].map do |res|
36 | raw_translation = res['translatedText']
37 | CGI.unescapeHTML(raw_translation)
38 | end
39 | end
40 |
41 | # A convenience class for wrapping a translation request
42 | class TranslationRequest < EasyTranslate::Request
43 |
44 | # Set the texts and options
45 | # @param [String, Array] texts - the text (or texts) to translate
46 | # @param [Hash] options - Options to override or pass along with the request
47 | def initialize(texts, options, http_options = {})
48 | options = options.dup
49 | self.texts = texts
50 | self.html = options.delete(:html)
51 | @source = options.delete(:from)
52 | @target = options.delete(:to)
53 | @model = options.delete(:model)
54 | raise ArgumentError.new('No target language provided') unless @target
55 | raise ArgumentError.new('Support for multiple targets dropped in V2') if @target.is_a?(Array)
56 | @http_options = http_options
57 | if options
58 | @options = options
59 | if replacement_api_key = @options.delete(:api_key)
60 | @options[:key] = replacement_api_key
61 | end
62 | end
63 | end
64 |
65 | # The params for this request
66 | # @return [Hash] the params for the request
67 | def params
68 | params = super || {}
69 | params[:source] = lang(@source) unless @source.nil?
70 | params[:target] = lang(@target) unless @target.nil?
71 | params[:model] = @model unless @model.nil?
72 | params[:format] = @format unless @format.nil?
73 | params.merge! @options if @options
74 | params
75 | end
76 |
77 | # The path for the request
78 | # @return [String] The path for the request
79 | def path
80 | '/language/translate/v2'
81 | end
82 |
83 | # The body for the request
84 | # @return [String] the body for the request
85 | def body
86 | JSON.generate({ q: @texts })
87 | end
88 |
89 | # Whether or not this was a request for multiple texts
90 | # @return [Boolean]
91 | def multi?
92 | @multi
93 | end
94 |
95 | private
96 |
97 | # Look up a language in the table (if needed)
98 | def lang(orig)
99 | look = orig.is_a?(String) ? orig : orig.to_s
100 | return look if LANGUAGES[look] # shortcut iteration
101 | if val = LANGUAGES.detect { |k, v| v == look }
102 | return val.first
103 | end
104 | look
105 | end
106 |
107 | # Set the HTML attribute, if true add a format
108 | # @param [Boolean] b - Whether or not the text supplied iS HTML
109 | def html=(b)
110 | @format = b ? 'html' : nil
111 | end
112 |
113 | # Set the texts for this request
114 | # @param [String, Array] texts - The text or texts for this request
115 | def texts=(texts)
116 | if texts.is_a?(String)
117 | @multi = false
118 | @texts = [texts]
119 | else
120 | @multi = true
121 | @texts = texts
122 | end
123 | end
124 |
125 | end
126 |
127 | end
128 |
129 | end
130 |
--------------------------------------------------------------------------------
/spec/examples/detection_spec.rb:
--------------------------------------------------------------------------------
1 | # encoding: UTF-8
2 |
3 | require 'spec_helper'
4 |
5 | describe EasyTranslate::Detection do
6 |
7 | it 'should return a single if given a single - from doc' do
8 | expect(EasyTranslate::Detection::DetectionRequest).to receive(:new).and_return(OpenStruct.new({
9 | :perform_raw => '{"data":{"detections":[[{"language":"en","isReliable":false,"confidence":0.6595744}]]}}',
10 | :multi? => false
11 | }))
12 | lang = EasyTranslate.detect 'Google Translate Rocks'
13 | expect(lang).to eq('en')
14 | end
15 |
16 | it 'should return a single with confidence if given a single with confidence - from doc' do
17 | expect(EasyTranslate::Detection::DetectionRequest).to receive(:new).and_return(OpenStruct.new({
18 | :perform_raw => '{"data":{"detections":[[{"language":"en","isReliable":false,"confidence":0.6595744}]]}}',
19 | :multi? => false
20 | }))
21 | lang = EasyTranslate.detect 'Google Translate Rocks', :confidence => true
22 | expect(lang).to eq({ :language => 'en', :confidence => 0.6595744 })
23 | end
24 |
25 | it 'should return a multiple if given multiple - from doc' do
26 | expect(EasyTranslate::Detection::DetectionRequest).to receive(:new).and_return(OpenStruct.new({
27 | :perform_raw => '{"data":{"detections":[[{"language":"en","isReliable":false,"confidence":0.6315789}],[{"language":"zh-CN","isReliable":false,"confidence":1.0}]]}}',
28 | :multi? => true
29 | }))
30 | lang = EasyTranslate.detect ['Hello World', '我姓譚']
31 | expect(lang).to eq(['en', 'zh-CN'])
32 | end
33 |
34 | it 'should return a multiple with confidence if given multiple with confidence - from doc' do
35 | expect(EasyTranslate::Detection::DetectionRequest).to receive(:new).and_return(OpenStruct.new({
36 | :perform_raw => '{"data":{"detections":[[{"language":"en","isReliable":false,"confidence":0.6315789}],[{"language":"zh-CN","isReliable":false,"confidence":1.0}]]}}',
37 | :multi? => true
38 | }))
39 | lang = EasyTranslate.detect ['Hello World', '我姓譚'], :confidence => true
40 | expect(lang).to eq([{ :language => 'en', :confidence => 0.6315789 }, { :language => 'zh-CN', :confidence => 1.0 }])
41 | end
42 |
43 | klass = EasyTranslate::Detection::DetectionRequest
44 | describe klass do
45 |
46 | describe :path do
47 |
48 | it 'should have a valid path' do
49 | request = klass.new('abc')
50 | expect(request.path).not_to be_empty
51 | end
52 |
53 | end
54 |
55 | describe :body do
56 | it 'should insert the texts into a JSON-encoded body' do
57 | request = klass.new(['abc', 'def'])
58 | parsed_payload = JSON.parse(request.body)
59 | expect(parsed_payload).to eq({ 'q' => ['abc', 'def'] })
60 | end
61 |
62 | it 'should insert the text into a JSON-encoded body' do
63 | request = klass.new('abc')
64 | parsed_payload = JSON.parse(request.body)
65 | expect(parsed_payload).to eq({ 'q' => ['abc'] })
66 | end
67 | end
68 |
69 | describe :params do
70 |
71 | it 'should use default params' do
72 | EasyTranslate.api_key = 'abc'
73 | request = klass.new('abc')
74 | expect(request.params[:key]).to eq('abc')
75 | end
76 |
77 | it 'should allow overriding of params' do
78 | EasyTranslate.api_key = 'abc'
79 | request = klass.new('abc', :key => 'def')
80 | expect(request.params[:key]).to eq('def')
81 | end
82 |
83 | it 'should allow overriding of key as api_key' do
84 | EasyTranslate.api_key = 'abc'
85 | request = klass.new('abc', :api_key => 'def')
86 | expect(request.params[:key]).to eq('def')
87 | expect(request.params[:api_key]).to be_nil
88 | end
89 |
90 | end
91 |
92 | describe :options do
93 |
94 | it 'should accept timeouts options' do
95 | request = EasyTranslate::Detection::DetectionRequest.new "test", {}, {:timeout => 1, :open_timeout => 2}
96 | http = request.send(:http)
97 | expect(http.open_timeout).to eq(2)
98 | expect(http.read_timeout).to eq(1)
99 | end
100 |
101 | it 'should accept ssl options' do
102 | request = EasyTranslate::Detection::DetectionRequest.new "test", {}, {:ssl => {:verify_depth => 3, :ca_file => 'path/to/ca/file'}}
103 | http = request.send(:http)
104 | expect(http.verify_depth).to eq(3)
105 | expect(http.ca_file).to eq('path/to/ca/file')
106 | end
107 |
108 | it 'should accept confidence option' do
109 | request = EasyTranslate::Detection::DetectionRequest.new "test", {:confidence => true}, {}
110 | expect(request.params[:confidence]).to eq(true)
111 | end
112 |
113 | end
114 |
115 | describe :multi? do
116 |
117 | it 'should be true if multiple are passed' do
118 | request = klass.new(['abc', 'def'])
119 | expect(request).to be_multi
120 | end
121 |
122 | it 'should be true if one is passed, but in an array' do
123 | request = klass.new(['abc'])
124 | expect(request).to be_multi
125 | end
126 |
127 | it 'should be true if one is passed as a string' do
128 | request = klass.new('abc')
129 | expect(request).not_to be_multi
130 | end
131 |
132 | end
133 |
134 | end
135 |
136 | end
137 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## EasyTranslate
2 |
3 |
4 |
5 | This is a Ruby library for Google Translate that makes working with bulk calls,
6 | user_ips and access via API Key easy.
7 |
8 | Unlike the official google client, `easy_translate` does not have a dependency on the `grpc` gem. This can be an advantage because `grpc` may be difficult to install on some systems or configurations.
9 |
10 | ---
11 |
12 | ### Installation
13 |
14 | ```bash
15 | $ gem install easy_translate
16 | ```
17 |
18 | Or in your Gemfile:
19 |
20 | ```ruby
21 | gem 'easy_translate'
22 | ```
23 |
24 | ---
25 |
26 | ## Single translation
27 |
28 | ```ruby
29 | # auto-detect
30 | EasyTranslate.translate('Hello, world', to: :spanish) # => "Hola, mundo"
31 | EasyTranslate.translate('Hello, world', to: 'es') # => "Hola, mundo"
32 |
33 | # feel free to specify explicitly
34 | EasyTranslate.translate('Hola, mundo', from: :spanish, to: :en) # => "Hello, world"
35 | ```
36 |
37 | ## Batch translation (Yay!)
38 |
39 | ```ruby
40 | # multiple strings
41 | EasyTranslate.translate(['Hello', 'Goodbye'], to: :spanish) # => ["¡Hola", "Despedida"]
42 | ```
43 |
44 | ## API Keys
45 |
46 | ```ruby
47 | # make google happy - (NOTE: use these anywhere)
48 | EasyTranslate.translate('Hello, world', to: :es, key: 'xxx')
49 |
50 | # don't want to set the key on every call? ** Me either! **
51 | EasyTranslate.api_key = 'xxx'
52 | ```
53 |
54 | ## You want language detection too?
55 |
56 | ```ruby
57 | # detect language
58 | EasyTranslate.detect "This is definitely English!" # => 'en'
59 | ```
60 |
61 | ## Batch detection (Woohoo!)
62 |
63 | ```ruby
64 | # detect language
65 | EasyTranslate.detect ['Hello World', '我姓譚'] # => ['en', 'zh-CN']
66 | ```
67 |
68 | ## Need confidence in your detections?
69 |
70 | ```ruby
71 | # detect language with confidence
72 | EasyTranslate.detect "This is definitely English!", confidence: true # => { :language => 'en', :confidence => 0.77272725 }
73 | ```
74 |
75 | ```ruby
76 | # detect batch languages with confidence
77 | EasyTranslate.detect ['This is definitely English!', '我姓譚'], confidence: true # => [{ :language => 'en', :confidence => 0.77272725 }, { :language => 'zh-CN', :confidence => 1.0 }]
78 | ```
79 |
80 | ## Explicitly select translation model (NMT, PBMT)
81 | Google Translate now replaces Phrase Based Machine Translation (PBMT) with Neural Machine Translation (NMT) automatically where possible. If you prefer PBMT or need to compare the results, you can use the `model:` parameter with either `nmt` or `base` to force the model selection:
82 |
83 | ```ruby
84 | EasyTranslate.translate("El cuervo americano es un ave con plumas negras iridiscentes sobre todo su cuerpo.", from: "es", to: "en", model: "nmt")
85 | # => "The American Raven is a bird with iridescent black feathers over its entire body."
86 | ```
87 |
88 | ```ruby
89 | EasyTranslate.translate("El cuervo americano es un ave con plumas negras iridiscentes sobre todo su cuerpo.", from: "es", to: "en", model: "base")
90 | # => "The American crow is a bird with iridescent black feathers over her body."
91 | ```
92 |
93 | See https://research.googleblog.com/2016/09/a-neural-network-for-machine.html for more background
94 |
95 | ## Google Translate supports HTML (default) and plain text formats
96 |
97 | ```ruby
98 | EasyTranslate.translate "Las doce en punto", format: 'text', to: :en
99 | # => "Twelve o'clock"
100 | EasyTranslate.translate "Las doce en punto", format: 'html', to: :en
101 | # => "Twelve o'clock"
102 | ```
103 |
104 | ---
105 |
106 | ## A note on concurrency as of v0.4.0
107 |
108 | Due to limitations with the Google Translate batch API, above a certain
109 | number of translations - this library will begin making calls concurrently.
110 |
111 | The default concurrency is 4, but if you'd prefer to run without threads,
112 | you can set `:concurrency => 1` to run the translation calls serially.
113 |
114 | ---
115 |
116 | ## List of languages
117 |
118 | ```ruby
119 | # list from
120 | EasyTranslate::LANGUAGES # => { 'en' => 'english', ... }
121 | ```
122 |
123 | ### List of supported languages
124 |
125 | ```ruby
126 | # List all languages (from API)
127 | EasyTranslate.translations_available
128 |
129 | # List all languages supported by some language
130 | EasyTranslate.translations_available('zh-CN')
131 | ```
132 |
133 | ---
134 |
135 | ### EasyTranslate in PHP
136 |
137 | [Kofel](https://github.com/Kofel) ported this library to PHP.
138 | You can find the port [on GitHub](https://github.com/Kofel/EasyTranslate)
139 |
140 | ---
141 |
142 | ### Contributors
143 |
144 | * John Crepezzi - john.crepezzi@gmail.com
145 | * Guy Maliar - gmaliar@gmail.com
146 | * [Gleb Mazovetskiy](https://github.com/glebm)
147 |
148 | Full contributor data at:
149 | https://github.com/seejohnrun/easy_translate/contributors
150 |
151 | ---
152 |
153 | ### License
154 |
155 | (The MIT License)
156 |
157 | Copyright © 2010-2018 John Crepezzi
158 |
159 | Permission is hereby granted, free of charge, to any person obtaining a copy of
160 | this software and associated documentation files (the ‘Software’), to deal in
161 | the Software without restriction, including without limitation the rights to
162 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
163 | of the Software, and to permit persons to whom the Software is furnished to do
164 | so, subject to the following conditions:
165 |
166 | The above copyright notice and this permission notice shall be included in all
167 | copies or substantial portions of the Software.
168 |
169 | THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
170 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
171 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
172 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
173 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
174 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
175 |
--------------------------------------------------------------------------------
/spec/examples/translation_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe EasyTranslate::Translation do
4 |
5 | it 'should return a single if given a single - from doc' do
6 | fake_request(
7 | :perform_raw => '{"data":{"translations":[{"translatedText":"Hallo Welt"}]}}',
8 | :multi? => false
9 | )
10 | trans = EasyTranslate.translate 'Hello world', :to => 'de'
11 | expect(trans).to eq('Hallo Welt')
12 | end
13 |
14 | it 'should return a multiple if given multiple - from doc' do
15 | fake_request(
16 | :perform_raw => '{"data":{"translations":[{"translatedText": "Hallo Welt"},{"translatedText":"Mein Name ist Jeff"}]}}',
17 | :multi? => true
18 | )
19 | trans = EasyTranslate.translate ['Hello world', 'my name is jeff'], :to => 'de'
20 | expect(trans).to eq(['Hallo Welt', 'Mein Name ist Jeff'])
21 | end
22 |
23 | it 'should decode HTML entities in the response' do
24 | fake_request(
25 | :perform_raw => '{"data":{"translations":[{"translatedText":"Hallo ' & " Welt"}]}}',
26 | :multi? => false
27 | )
28 | trans = EasyTranslate.translate %{Hello ' & " world}, :to => 'de'
29 | expect(trans).to eq(%{Hallo ' & " Welt})
30 | end
31 |
32 | context 'detecting availale language' do
33 | before :each do
34 | pending 'please provide an API_KEY for this suite' if ENV['API_KEY'].nil?
35 |
36 | EasyTranslate.api_key = ENV['API_KEY']
37 | end
38 |
39 | it 'should detect simplified chinese as zh-CN' do
40 | expect(EasyTranslate.translations_available.include?('zh-CN')).to eq(true)
41 | expect(EasyTranslate.translations_available.include?('zh')).to eq(true)
42 | end
43 | end
44 |
45 | def fake_request(hash)
46 | expect(EasyTranslate::Translation::TranslationRequest).to receive(:new).and_return(OpenStruct.new(hash))
47 | end
48 |
49 | klass = EasyTranslate::Translation::TranslationRequest
50 | describe klass do
51 |
52 | describe :path do
53 |
54 | it 'should have a valid path' do
55 | request = klass.new('abc', :to => 'en')
56 | expect(request.path).not_to be_empty
57 | end
58 |
59 | end
60 |
61 | describe :initialize do
62 |
63 | it 'should raise an error when there is no to given' do
64 | expect do
65 | req = klass.new('abc', :from => 'en')
66 | end.to raise_error ArgumentError
67 | end
68 |
69 | it 'should raise an error when tos are given as an array' do
70 | expect do
71 | req = klass.new('abc', :from => 'en', :to => ['es', 'de'])
72 | end.to raise_error ArgumentError
73 | end
74 |
75 | end
76 |
77 | describe :params do
78 |
79 | it 'should include from in params if given' do
80 | req = klass.new('abc', :from => 'en', :to => 'es')
81 | expect(req.params[:source]).to eq('en')
82 | end
83 |
84 | it 'should not include from by default' do
85 | req = klass.new('abc', :to => 'es')
86 | expect(req.params[:source]).to be_nil
87 | end
88 |
89 | it 'should include to' do
90 | req = klass.new('abc', :to => 'es')
91 | expect(req.params[:target]).to eq('es')
92 | end
93 |
94 | it 'should not include format by default' do
95 | req = klass.new('abc', :to => 'es')
96 | expect(req.params[:format]).to be_nil
97 | end
98 |
99 | it 'should not include format when given as false' do
100 | req = klass.new('abc', :html => false, :to => 'es')
101 | expect(req.params[:format]).to be_nil
102 | end
103 |
104 | it 'should include format when html is true' do
105 | req = klass.new('abc', :html => true, :to => 'es')
106 | expect(req.params[:format]).to eq('html')
107 | end
108 |
109 | it 'should include format when specified as text' do
110 | req = klass.new('abc', :format => 'text', :to => 'es')
111 | expect(req.params[:format]).to eq('text')
112 | end
113 |
114 | it 'should use default params' do
115 | EasyTranslate.api_key = 'abc'
116 | request = klass.new('abc', :to => 'es')
117 | expect(request.params[:key]).to eq('abc')
118 | end
119 |
120 | it 'should allow overriding of params' do
121 | EasyTranslate.api_key = 'abc'
122 | request = klass.new('abc', :key => 'def', :to => 'es')
123 | expect(request.params[:key]).to eq('def')
124 | end
125 |
126 | it 'should allow overriding of key as api_key' do
127 | EasyTranslate.api_key = 'abc'
128 | request = klass.new('abc', :api_key => 'def', :to => 'es')
129 | expect(request.params[:key]).to eq('def')
130 | expect(request.params[:api_key]).to be_nil
131 | end
132 |
133 | it 'should be able to supply a language as a string' do
134 | request = klass.new('abc', :to => 'es')
135 | expect(request.params[:target]).to eq('es')
136 | end
137 |
138 | it 'should be able to supply a language as a symbol' do
139 | request = klass.new('abc', :to => :es)
140 | expect(request.params[:target]).to eq('es')
141 | end
142 |
143 | it 'should be able to supply a language as a word' do
144 | request = klass.new('abc', :to => 'spanish')
145 | expect(request.params[:target]).to eq('es')
146 | end
147 |
148 | it 'should be able to supply a language as a word symbol' do
149 | request = klass.new('abc', :to => :spanish)
150 | expect(request.params[:target]).to eq('es')
151 | end
152 |
153 | it 'should fall back when a word is not in the lookup' do
154 | request = klass.new('abc', :to => 'zzz')
155 | expect(request.params[:target]).to eq('zzz')
156 | end
157 |
158 | end
159 |
160 | describe :multi? do
161 |
162 | it 'should be true if multiple are passed' do
163 | request = klass.new(['abc', 'def'], :to => 'es')
164 | expect(request).to be_multi
165 | end
166 |
167 | it 'should be true if one is passed, but in an array' do
168 | request = klass.new(['abc'], :to => 'es')
169 | expect(request).to be_multi
170 | end
171 |
172 | it 'should be true if one is passed as a string' do
173 | request = klass.new('abc', :to => 'es')
174 | expect(request).not_to be_multi
175 | end
176 |
177 | end
178 |
179 | describe :body do
180 | it 'should insert the texts into a JSON-encoded body' do
181 | request = klass.new(['abc', 'def'], :to => 'es')
182 | parsed_payload = JSON.parse(request.body)
183 | expect(parsed_payload).to eq( { 'q' => ['abc', 'def'] } )
184 | end
185 |
186 | it 'should insert the text into a JSON-encoded body' do
187 | request = klass.new('abc', :to => 'es')
188 | parsed_payload = JSON.parse(request.body)
189 | expect(parsed_payload).to eq( { 'q' => ['abc'] } )
190 | end
191 |
192 | end
193 |
194 | end
195 |
196 | end
197 |
--------------------------------------------------------------------------------