transfluent 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5183e0b9ea8f665f492a6159975ced1985bdae0f
4
+ data.tar.gz: 07e6914be61014a7b9242eca63aa74f6263043bd
5
+ SHA512:
6
+ metadata.gz: cb2ab465c0d87c5f8a7856a3c613da0d43b0d4abfeb610bc78cadd37db1dbb8a6982624cd469f46074256a60893c6b6b7710facc68ba15e6d8c3f98e1636648d
7
+ data.tar.gz: d4bef9a5e6b2e3a66685d349c8ff817501e54db24236a2930d2c3a66cb19fc4a949759a1c6af317c907502663f1488561a9aeb5a23548a4f8ddb5f4abf0d9e75
@@ -0,0 +1,4 @@
1
+ require 'transfluent/api_error'
2
+ require 'transfluent/api_helper'
3
+ require 'transfluent/language'
4
+ require 'transfluent/api'
@@ -0,0 +1,82 @@
1
+ require 'transfluent/transfluent_text_api'
2
+ require 'transfluent/transfluent_file_api'
3
+
4
+ module Transfluent
5
+ class Api
6
+ include ApiHelper
7
+ include TransfluentTextApi
8
+ include TransfluentFileApi
9
+
10
+ # The authentication token associated with this instance
11
+ #
12
+ # @return [string]
13
+ attr_accessor :auth_token
14
+
15
+ # Creates a new instance if Transfluent Api
16
+ #
17
+ # @param [Hash] default_settings default settings for some calls
18
+ # @option default_settings [String] auth_token your auth token
19
+ # @option default_settings [String] group default text group
20
+ # @option default_settings [Transfluent::Language] language default text and file source language
21
+ def initialize default_settings = {}
22
+ @auth_token = default_settings[:auth_token]
23
+ @defaults = default_settings
24
+
25
+ if @defaults.has_key? :api_root
26
+ @_ROOT_URL = @defaults.delete :api_root
27
+ end
28
+ end
29
+
30
+ # Get the default text group
31
+ #
32
+ # @return [string]
33
+ def default_group
34
+ @defaults[:group]
35
+ end
36
+
37
+ # Set the default text group
38
+ #
39
+ # @param [string] value new default group for texts
40
+ def default_group=(value)
41
+ @defaults[:group] = value
42
+ end
43
+
44
+ # Get the default language
45
+ #
46
+ # @return [Transfluent::Language]
47
+ def default_language
48
+ @defaults[:language]
49
+ end
50
+
51
+ # Set the default language
52
+ #
53
+ # @param [Transfluent::Language] value new default language for texts
54
+ def default_language=(value)
55
+ @defaults[:language] = value
56
+ end
57
+
58
+ # Echoes back the parameter prepended with Hello
59
+ #
60
+ # @param world [string] any text
61
+ # @return [string] "Hello " + world
62
+ def hello world
63
+ response = Net::HTTP.get(api_uri("/hello/" + world))
64
+
65
+ parse_response response
66
+ end
67
+
68
+ # Authenticate against the backend
69
+ #
70
+ # @param [string] email Email address
71
+ # @param [string] password Your password
72
+ #
73
+ # @return [string] authentication token
74
+ def authenticate email, password
75
+ uri = api_uri('authenticate')
76
+ uri.query = URI.encode_www_form({'email' => email, 'password' => password})
77
+ response = parse_response(Net::HTTP.get(uri))
78
+
79
+ @auth_token = response[:token].to_s
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,7 @@
1
+ module Transfluent
2
+ class ApiError < Exception
3
+ def initialize type, message
4
+ super "Backend error: %s -- %s" % [ type, message ]
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,61 @@
1
+ require 'net/http'
2
+ require 'json'
3
+
4
+ module Transfluent
5
+ module ApiHelper
6
+ def api_uri method
7
+ method = '/' + method unless method[0] == '/'
8
+ url = @_ROOT_URL || 'https://transfluent.com/v2'
9
+ URI(url + method)
10
+ end
11
+
12
+ def execute_get uri, query = nil
13
+ unless query.nil?
14
+ uri.query = URI.encode_www_form query
15
+ end
16
+
17
+ parse_response(Net::HTTP.get(uri))
18
+ end
19
+
20
+ def execute_post uri, data
21
+ parse_response(Net::HTTP.post_form(uri, data).body)
22
+ end
23
+
24
+ def parse_response response
25
+ if response.is_a? String then
26
+ json = JSON.parse(response)
27
+ else
28
+ json = response
29
+ end
30
+
31
+ if json["status"] == "OK"
32
+ response = json['response']
33
+
34
+ if response.is_a? Hash
35
+ symbolize_hash response
36
+ elsif response.is_a? Array
37
+ symbolize_array response
38
+ else
39
+ response
40
+ end
41
+ elsif json["status"] == "ERROR"
42
+ raise Transfluent::ApiError.new json['error']['type'], json['error']['message']
43
+ end
44
+ end
45
+
46
+ # Convert string keys in a hash to symbols recursively
47
+ def symbolize_hash hash
48
+ hash.inject({}) do |memo,(k,v)|
49
+ memo[(k.to_sym rescue k) || k] = if v.is_a? Hash then symbolize_hash v else v end
50
+ memo
51
+ end
52
+ end
53
+
54
+ def symbolize_array array
55
+ array.inject([]) do |memo, v|
56
+ memo.push(if v.is_a? Hash then symbolize_hash v else v end)
57
+ memo
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,52 @@
1
+
2
+ module Transfluent
3
+
4
+ # Represents a language that can be identified by an IETF language tag
5
+ # or Transfluent specific id
6
+ class Language
7
+
8
+ attr_reader :id, :code, :name
9
+
10
+ # Get an instance of language by code
11
+ #
12
+ # @param [string] code code for language, 'en-us', 'en-gb' etc.
13
+ #
14
+ # @return [Language]
15
+ def self.get code
16
+ Language.fetch if $languages_by_code.nil?
17
+
18
+ $languages_by_code[code]
19
+ end
20
+
21
+ def self.get_by_id id
22
+ $languages_by_id[id]
23
+ end
24
+
25
+ def initialize id, code, name
26
+ @id = id.to_i
27
+ @code = code
28
+ @name = name
29
+ end
30
+
31
+ private
32
+
33
+ def self.fetch
34
+ api = Object.new
35
+ api.extend(ApiHelper)
36
+
37
+ uri = api.api_uri('languages')
38
+
39
+ response = api.execute_get(uri)
40
+
41
+ $languages_by_code = {}
42
+ $languages_by_id = {}
43
+ response.each do |item|
44
+ item.each do |id, lang|
45
+ $languages_by_code[lang[:code]] = Language.new lang[:id], lang[:code], lang[:name]
46
+ $languages_by_id[lang[:id]] = $languages_by_code[lang[:code]]
47
+ end
48
+ end
49
+ end
50
+ end
51
+
52
+ end
@@ -0,0 +1,150 @@
1
+ require 'base64'
2
+
3
+ module Transfluent
4
+ # File types supported by the backend
5
+ module FileType
6
+ IOS_RESOURCE = 'iOS-strings'
7
+ ANDROID_RESOURCE = 'Android-strings'
8
+ ANDROID_ARRAYS_RESOURCE = 'Android-arrays'
9
+ MOOTOOLS_LOCALE_RESOURCE = 'MooTools-locale'
10
+ JAVASCRIPT_RESOURCE = 'js-file'
11
+ SYMBIAN_TS_RESOURCE = 'Symbian-TS'
12
+ WP_RESX_RESOURCE = 'WP-RESX'
13
+ PROPERTIES_RESOURCE = 'Properties-file'
14
+ YAML_RESOURCE = 'YAML-file'
15
+ SIMPLE_XML_RESOURCE = 'XML-file'
16
+ GETTEXT_PO_RESOURCE = 'po-file'
17
+ SIMPLE_EXCEL_RESOURCE = 'Excel-file'
18
+ end
19
+
20
+ module TransfluentFileApi
21
+
22
+ # Saves a file to Transfluent
23
+ #
24
+ # @param [Hash] options
25
+ # @option options [String] identifier unique identifier for the file
26
+ # @option options [String] content_base64 base64 encoded content of the file. Has priority over file option.
27
+ # @option options [File, String] file File instance or file name to read. Used if content_base64 is not defined.
28
+ # @option options [String] type File type. Must be one of FileType constants
29
+ # @option options ['UTF-8', 'UTF-16'] encoding endocing of the file. Defaults to UTF-8
30
+ # @option options [Transfluent::Language] language language of the file. Defaults to default_language
31
+ # @option options [true, false] save_only_data If you have previously translated content,
32
+ # you may save the translations by specifying this variable as true.
33
+ # Remember to use the same file identifier as the source file has. Defaults to false
34
+ #
35
+ # @return [Hash] hash that contains count of saved words
36
+ def file_save options
37
+ uri = api_uri 'file/save'
38
+
39
+ opts = @defaults.merge({
40
+ :encoding => 'UTF-8'
41
+ }).merge options
42
+
43
+ data = {
44
+ 'format' => opts[:encoding],
45
+ 'identifier' => opts[:identifier],
46
+ 'language' => opts[:language].id,
47
+ 'save_only_data' => opts[:save_only_data],
48
+ 'type' => opts[:type],
49
+ 'token' => auth_token
50
+ }
51
+
52
+ if opts.has_key? :content_base64
53
+ data['content'] = opts[:content_base64]
54
+ elsif opts.has_key? :file
55
+ f = opts[:file]
56
+ f = File.new(f) unless f.is_a? File
57
+
58
+ data['content'] = Base64.encode64 f.read
59
+ end
60
+
61
+ execute_post uri, data
62
+ end
63
+
64
+ # Read a file from Transfluent
65
+ #
66
+ # @param [String] identifier file identifier
67
+ # @param [Transfluent::Language] language language of the file. Defaults to default_language
68
+ #
69
+ # @return [Hash] a hash with keys :name and :contents
70
+ def file_read identifier, language = nil
71
+ language = default_language if language.nil?
72
+ uri = api_uri 'file/read'
73
+
74
+ query = {
75
+ 'identifier' => identifier,
76
+ 'language' => language.id,
77
+ 'token' => auth_token
78
+ }
79
+
80
+ uri.query = URI.encode_www_form query
81
+ response = Net::HTTP.get_response uri
82
+
83
+ file = {
84
+ :name => response['Content-Disposition'].partition('=').last,
85
+ :contents => response.body
86
+ }
87
+ end
88
+
89
+ # Query translation status for a file
90
+ #
91
+ # @param [String] identifier file identifier
92
+ # @param [Transfluent::Language] language language of the file. Defaults to default_language
93
+ #
94
+ # @return [Hash] a hash with progress information
95
+ def file_status identifier, language = nil
96
+ language = default_language if language.nil?
97
+
98
+ uri = api_uri 'file/status'
99
+
100
+ query = {
101
+ 'identifier' => identifier,
102
+ 'language' => language.id,
103
+ 'token' => auth_token
104
+ }
105
+
106
+ response = execute_get uri, query
107
+
108
+ response[:progress_str] = response[:progress]
109
+ response[:word_count] = response[:word_count].to_i
110
+ response[:word_count_translated] = response[:word_count_translated].to_i
111
+ response[:progress] = (response[:word_count_translated].to_f / response[:word_count]).round(4)
112
+ response[:word_count] = response[:word_count].to_i
113
+
114
+ response
115
+ end
116
+
117
+ # Order translation for a file
118
+ #
119
+ # @param [Hash] options
120
+ # @option options [String] identifier an unique identifier for a file
121
+ # @option options [Array] target_languages array of Transfluent::Language to translate file to
122
+ # @option options [Transfluent::Language] language language of the file. Defaults to default_language
123
+ # @option options [String] comment a context comment of the file for the translators
124
+ # @option options [1, 2, 3] level level of the translation. 1 == native, 2 == professional, 3 == pair of translators. Defaults to 3
125
+ # @option options [URI] callback_uri optional URI to post the translated file once complete.
126
+ #
127
+ # @return [Hash] a hash of word counts
128
+ def file_translate options
129
+ opts = @defaults.merge options
130
+
131
+ uri = api_uri 'file/translate'
132
+
133
+ data = {
134
+ 'identifier' => opts[:identifier],
135
+ 'language' => opts[:language].id,
136
+ 'target_languages' => '[%s]' % opts[:target_languages].map { |lang| lang.id },
137
+ 'token' => auth_token
138
+ }
139
+
140
+ data['comment'] = opts[:comment] if opts.has_key? :comment
141
+ data['level'] = opts[:level] if opts.has_key? :level
142
+ data['callback_url'] = opts[:callback_uri].to_s if opts.has_key? :callback_uri
143
+
144
+ execute_post(uri, data).inject({}) do |memo, (k, v)|
145
+ memo[k] = v.to_i
146
+ memo
147
+ end
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,166 @@
1
+ module Transfluent
2
+ module TransfluentTextApi
3
+
4
+ # Add a single translatable text to Transfluent
5
+ #
6
+ # Any text added to the system can be queued for translation
7
+ # to any language.
8
+ #
9
+ # @param [string] text_id an unique id for the text
10
+ # @param [string] text the text to add
11
+ # @param [Hash] options optional options to create text with
12
+ # @option options [String] :group text group
13
+ # @option options [Transfluent::Language] :language text language
14
+ # @option options [true, false] :invalidate_translations invalidate previous translations for the text
15
+ # @option options [true, false] :is_draft is the text a draft
16
+ #
17
+ # @return [true]
18
+ def add_text text_id, text, options = {}
19
+ opts = @defaults.merge({
20
+ :invalidate_translations => true,
21
+ :is_draft => false
22
+ }).merge(options)
23
+
24
+ group_id = opts[:group]
25
+ language = opts[:language]
26
+
27
+
28
+
29
+ uri = api_uri('text')
30
+ data = {
31
+ 'text_id' => text_id,
32
+ 'group_id' => group_id,
33
+ 'language' => language.id,
34
+ 'text' => text,
35
+ 'invalidate_translations' => opts[:invalidate_translations] ? '1' : '0',
36
+ 'is_draft' => opts[:is_draft] ? '1' : '0',
37
+ 'token' => auth_token
38
+ }
39
+
40
+ execute_post(uri, data)
41
+ end
42
+
43
+ # Add multiple translatable texts to Transfluent
44
+ #
45
+ # Any text added to the system can be queued for translation
46
+ # to any language.
47
+ #
48
+ # @param [Hash] texts texts to create
49
+ # @option texts [Hash] :texts a hash of texts, where key is text id and value is the text to translate
50
+ # @option texts [String] :group text group
51
+ # @option texts [Transfluent::Language] :language text language
52
+ # @option texts [true, false] :invalidate_translations invalidate previous translations for the text
53
+ # @option texts [true, false] :is_draft is the text a draft
54
+ def add_texts texts
55
+ opts = @defaults.merge({
56
+ :invalidate_translations => true
57
+ }).merge(texts)
58
+
59
+ texts_to_translate = {}
60
+ texts[:texts].each do | text_id, text |
61
+ texts_to_translate['texts[%s]' % text_id] = text
62
+ end
63
+
64
+ group_id = opts[:group]
65
+ language = opts[:language]
66
+
67
+ uri = api_uri('texts')
68
+ data = {
69
+ 'group_id' => group_id,
70
+ 'language' => language.id,
71
+ 'invalidate_translations' => opts[:invalidate_translations] ? '1' : '0',
72
+ 'token' => auth_token
73
+ }
74
+
75
+ response = execute_post(uri, data.merge(texts_to_translate))
76
+
77
+ end
78
+
79
+ # Request translation status for a text
80
+ #
81
+ # @param [string] text_id an unique id for the text
82
+ # @param [Hash] options optional options
83
+ # @option options [String] :group text group
84
+ # @option options [Transfluent::Language] :language text language
85
+ #
86
+ # @return [true, false]
87
+ def text_translated? text_id, options = {}
88
+ opts = @defaults.merge options
89
+
90
+ uri = api_uri 'text/status'
91
+ query = {
92
+ 'text_id' => text_id,
93
+ 'group_id' => opts[:group],
94
+ 'language' => opts[:language].id,
95
+ 'token' => auth_token
96
+ }
97
+
98
+ response = execute_get uri, query
99
+
100
+ response[:is_translated]
101
+ end
102
+
103
+ # Query text order status
104
+ #
105
+ # @param [Hash] options optional options
106
+ # @option options [String] :group text group. Using default_group if not specified
107
+ # @option options [Fixnum] :limit number items to query. Defaults to 100
108
+ # @option options [Fixnum] :offset where to start querying. Defaults to 0
109
+ #
110
+ # @return [Hash]
111
+ def text_orders options = {}
112
+ opts = @defaults.merge({
113
+ :limit => 100,
114
+ :offset => 0
115
+ }).merge options
116
+
117
+ uri = api_uri 'texts/orders'
118
+ query = {
119
+ 'group_id' => opts[:group],
120
+ 'limit' => opts[:limit],
121
+ 'offset' => opts[:offset],
122
+ 'token' => auth_token
123
+ }
124
+
125
+ response = execute_get uri, query
126
+
127
+ response.inject([]) do |memo, v|
128
+ v[:source_language] = Transfluent::Language.get_by_id v[:source_language]
129
+ v[:target_language] = Transfluent::Language.get_by_id v[:target_language]
130
+ memo.push(v)
131
+ end
132
+ end
133
+
134
+ # Request a translation for submitted texts
135
+ #
136
+ # @param [Hash] options
137
+ # @option options [String] group_id the text group id. Defaults to default_group
138
+ # @option options [Transfluent::Language] source_language source language of the texts. Defaults to default_language
139
+ # @option options [Array] target_languages array of languages to translate the texts to
140
+ # @option options [1, 2, 3] level translator level. 1 == native, 2 == professional, 3 == pair of translators. Defaults to 2
141
+ # @option options [Fixnum] max_words maximum words to translate. Defaults to 1000.
142
+ # @option options [String] comment a comment for translators about these texts
143
+ # @option options [URI] callback_url a request will be made to this url when the translations are complete
144
+ # @option options [Array] texts array of text ids to translate
145
+ #
146
+ # @return [Hash] count of words ordered to translate
147
+ def texts_translate options
148
+ opts = @defaults.merge({}).merge options
149
+
150
+ uri = api_uri 'texts/translate'
151
+ query = {
152
+ 'group_id' => opts[:group],
153
+ 'source_language' => opts[:language].id,
154
+ 'target_languages' => '[%s]' % opts[:target_languages].map { |lang| lang.id },
155
+ 'texts' => '[%s]' % opts[:texts].map { |text_id| '{"id":"%s"}' % text_id }.join(','),
156
+ 'token' => auth_token
157
+ }
158
+ query['level'] = opts[:level] unless opts[:level].nil?
159
+ query['max_words'] = opts[:max_words] unless opts[:max_words].nil?
160
+ query['comment'] = opts[:comment] unless opts[:comment].nil?
161
+ query['callback_url'] = opts[:callback_uri].to_s unless opts[:callback_uri].nil?
162
+
163
+ execute_get uri, query
164
+ end
165
+ end
166
+ end
metadata ADDED
@@ -0,0 +1,107 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: transfluent
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Transfluent Oy
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-02-06 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '10.1'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '10.1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: webmock
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '1.17'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '1.17'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '4.1'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '4.1'
55
+ - !ruby/object:Gem::Dependency
56
+ name: yard
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0.8'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0.8'
69
+ description: Order human powered translations directly from your Ruby code!
70
+ email: coders@transfluent.com
71
+ executables: []
72
+ extensions: []
73
+ extra_rdoc_files: []
74
+ files:
75
+ - lib/transfluent.rb
76
+ - lib/transfluent/api.rb
77
+ - lib/transfluent/api_error.rb
78
+ - lib/transfluent/api_helper.rb
79
+ - lib/transfluent/transfluent_file_api.rb
80
+ - lib/transfluent/transfluent_text_api.rb
81
+ - lib/transfluent/language.rb
82
+ homepage: https://github.com/Transfluent/Transfluent-Ruby
83
+ licenses:
84
+ - MIT
85
+ metadata: {}
86
+ post_install_message:
87
+ rdoc_options: []
88
+ require_paths:
89
+ - lib
90
+ required_ruby_version: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ required_rubygems_version: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ requirements: []
101
+ rubyforge_project:
102
+ rubygems_version: 2.1.11
103
+ signing_key:
104
+ specification_version: 4
105
+ summary: Transfluent API client
106
+ test_files: []
107
+ has_rdoc: