translatomatic 0.1.3 → 0.2.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.
- checksums.yaml +5 -5
- data/.gitattributes +20 -20
- data/.gitignore +19 -15
- data/.rspec +3 -3
- data/.rubocop.yml +28 -0
- data/.translatomatic/config.yml +4 -0
- data/.travis.yml +4 -6
- data/.yardopts +9 -9
- data/Gemfile +8 -4
- data/Guardfile +4 -5
- data/README.de.md +55 -50
- data/README.en.md +177 -0
- data/README.es.md +53 -48
- data/README.fr.md +53 -48
- data/README.it.md +54 -49
- data/README.ja.md +63 -58
- data/README.ko.md +59 -54
- data/README.md +17 -13
- data/README.ms.md +50 -45
- data/README.pt.md +54 -49
- data/README.ru.md +57 -52
- data/README.sv.md +51 -46
- data/README.zh.md +60 -55
- data/Rakefile +3 -3
- data/TODO.txt +6 -0
- data/bin/console +3 -3
- data/bin/translatomatic +4 -2
- data/config/i18n-tasks.yml +130 -0
- data/config/locales/translatomatic/de.yml +141 -99
- data/config/locales/translatomatic/en.yml +129 -89
- data/config/locales/translatomatic/es.yml +136 -99
- data/config/locales/translatomatic/fr.yml +139 -100
- data/config/locales/translatomatic/it.yml +135 -97
- data/config/locales/translatomatic/ja.yml +137 -98
- data/config/locales/translatomatic/ko.yml +138 -98
- data/config/locales/translatomatic/ms.yml +138 -100
- data/config/locales/translatomatic/pt.yml +137 -101
- data/config/locales/translatomatic/ru.yml +136 -98
- data/config/locales/translatomatic/sv.yml +134 -96
- data/config/locales/translatomatic/zh.yml +136 -97
- data/db/migrate/201712170000_initial.rb +2 -3
- data/lib/translatomatic.rb +40 -25
- data/lib/translatomatic/cli.rb +5 -1
- data/lib/translatomatic/cli/base.rb +61 -58
- data/lib/translatomatic/cli/common_options.rb +14 -11
- data/lib/translatomatic/cli/config.rb +96 -91
- data/lib/translatomatic/cli/database.rb +85 -23
- data/lib/translatomatic/cli/main.rb +158 -104
- data/lib/translatomatic/cli/thor.rb +29 -0
- data/lib/translatomatic/cli/translate.rb +134 -157
- data/lib/translatomatic/config.rb +10 -301
- data/lib/translatomatic/config/display.rb +78 -0
- data/lib/translatomatic/config/files.rb +60 -0
- data/lib/translatomatic/config/location_settings.rb +133 -0
- data/lib/translatomatic/config/options.rb +68 -0
- data/lib/translatomatic/config/selector.rb +127 -0
- data/lib/translatomatic/config/settings.rb +148 -0
- data/lib/translatomatic/converter.rb +40 -28
- data/lib/translatomatic/database.rb +127 -110
- data/lib/translatomatic/define_options.rb +4 -5
- data/lib/translatomatic/escaped_unicode.rb +86 -76
- data/lib/translatomatic/extractor.rb +5 -2
- data/lib/translatomatic/extractor/base.rb +12 -12
- data/lib/translatomatic/extractor/ruby.rb +7 -6
- data/lib/translatomatic/file_translator.rb +101 -244
- data/lib/translatomatic/flattenation.rb +39 -0
- data/lib/translatomatic/http.rb +13 -0
- data/lib/translatomatic/http/client.rb +144 -0
- data/lib/translatomatic/http/exception.rb +43 -0
- data/lib/translatomatic/http/file_param.rb +27 -0
- data/lib/translatomatic/http/param.rb +37 -0
- data/lib/translatomatic/http/request.rb +91 -0
- data/lib/translatomatic/i18n.rb +43 -0
- data/lib/translatomatic/locale.rb +71 -59
- data/lib/translatomatic/logger.rb +43 -28
- data/lib/translatomatic/metadata.rb +58 -0
- data/lib/translatomatic/model.rb +4 -2
- data/lib/translatomatic/model/locale.rb +5 -5
- data/lib/translatomatic/model/text.rb +5 -5
- data/lib/translatomatic/option.rb +57 -34
- data/lib/translatomatic/path_utils.rb +126 -0
- data/lib/translatomatic/progress_updater.rb +13 -16
- data/lib/translatomatic/provider.rb +101 -0
- data/lib/translatomatic/provider/base.rb +136 -0
- data/lib/translatomatic/provider/frengly.rb +55 -0
- data/lib/translatomatic/provider/google.rb +78 -0
- data/lib/translatomatic/provider/google_web.rb +50 -0
- data/lib/translatomatic/provider/microsoft.rb +144 -0
- data/lib/translatomatic/provider/my_memory.rb +75 -0
- data/lib/translatomatic/provider/yandex.rb +61 -0
- data/lib/translatomatic/resource_file.rb +59 -53
- data/lib/translatomatic/resource_file/base.rb +171 -237
- data/lib/translatomatic/resource_file/csv.rb +176 -24
- data/lib/translatomatic/resource_file/html.rb +21 -42
- data/lib/translatomatic/resource_file/key_value_support.rb +117 -0
- data/lib/translatomatic/resource_file/markdown.rb +36 -38
- data/lib/translatomatic/resource_file/plist.rb +121 -126
- data/lib/translatomatic/resource_file/po.rb +104 -82
- data/lib/translatomatic/resource_file/properties.rb +48 -77
- data/lib/translatomatic/resource_file/properties.treetop +87 -0
- data/lib/translatomatic/resource_file/resw.rb +56 -41
- data/lib/translatomatic/resource_file/subtitle.rb +86 -54
- data/lib/translatomatic/resource_file/text.rb +18 -18
- data/lib/translatomatic/resource_file/xcode_strings.rb +32 -63
- data/lib/translatomatic/resource_file/xcode_strings.treetop +85 -0
- data/lib/translatomatic/resource_file/xml.rb +94 -81
- data/lib/translatomatic/resource_file/yaml.rb +54 -68
- data/lib/translatomatic/retry_executor.rb +37 -0
- data/lib/translatomatic/slurp.rb +32 -0
- data/lib/translatomatic/string_batcher.rb +50 -0
- data/lib/translatomatic/string_escaping.rb +61 -0
- data/lib/translatomatic/text.rb +263 -0
- data/lib/translatomatic/text_collection.rb +66 -0
- data/lib/translatomatic/tmx.rb +5 -3
- data/lib/translatomatic/tmx/document.rb +107 -82
- data/lib/translatomatic/tmx/translation_unit.rb +19 -18
- data/lib/translatomatic/translation.rb +8 -28
- data/lib/translatomatic/translation/collection.rb +199 -0
- data/lib/translatomatic/translation/fetcher.rb +123 -0
- data/lib/translatomatic/translation/munging.rb +112 -0
- data/lib/translatomatic/translation/result.rb +50 -0
- data/lib/translatomatic/translation/sharer.rb +32 -0
- data/lib/translatomatic/translation/stats.rb +44 -0
- data/lib/translatomatic/translator.rb +91 -88
- data/lib/translatomatic/type_cast.rb +63 -0
- data/lib/translatomatic/util.rb +37 -33
- data/lib/translatomatic/version.rb +2 -2
- data/translatomatic.gemspec +57 -46
- metadata +136 -59
- data/lib/translatomatic/http_request.rb +0 -162
- data/lib/translatomatic/string.rb +0 -188
- data/lib/translatomatic/translation_result.rb +0 -86
- data/lib/translatomatic/translation_stats.rb +0 -31
- data/lib/translatomatic/translator/base.rb +0 -128
- data/lib/translatomatic/translator/frengly.rb +0 -62
- data/lib/translatomatic/translator/google.rb +0 -37
- data/lib/translatomatic/translator/microsoft.rb +0 -41
- data/lib/translatomatic/translator/my_memory.rb +0 -68
- data/lib/translatomatic/translator/yandex.rb +0 -56
@@ -0,0 +1,13 @@
|
|
1
|
+
module Translatomatic
|
2
|
+
# HTTP request functionality
|
3
|
+
module HTTP
|
4
|
+
# @private
|
5
|
+
USER_AGENT = "Translatomatic #{VERSION} (+#{URL})".freeze
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
require 'translatomatic/http/exception'
|
10
|
+
require 'translatomatic/http/param'
|
11
|
+
require 'translatomatic/http/file_param'
|
12
|
+
require 'translatomatic/http/request'
|
13
|
+
require 'translatomatic/http/client'
|
@@ -0,0 +1,144 @@
|
|
1
|
+
require 'http-cookie'
|
2
|
+
|
3
|
+
module Translatomatic
|
4
|
+
module HTTP
|
5
|
+
# HTTP client
|
6
|
+
class Client
|
7
|
+
def initialize(options = {})
|
8
|
+
@redirects = 0
|
9
|
+
@jar = ::HTTP::CookieJar.new
|
10
|
+
@retry_delay = options[:retry_delay] || 2
|
11
|
+
end
|
12
|
+
|
13
|
+
# Send an HTTP GET request
|
14
|
+
# @param url [String,URI] URL of the request
|
15
|
+
# @param query [Hash<String,String>] Optional query parameters
|
16
|
+
# @param options [Hash<Symbol,Object>] Request options
|
17
|
+
# @return [Net::HTTP::Response]
|
18
|
+
def get(url, query = nil, options = {})
|
19
|
+
send_request_with_method(:get, url, options.merge(query: query))
|
20
|
+
end
|
21
|
+
|
22
|
+
# Send an HTTP POST request
|
23
|
+
# @param url [String,URI] URL of the request
|
24
|
+
# @param body [String,Hash] Body of the request
|
25
|
+
# @param options [Hash<Symbol,Object>] Request options
|
26
|
+
# @return [Net::HTTP::Response]
|
27
|
+
def post(url, body, options = {})
|
28
|
+
send_request_with_method(:post, url, options.merge(body: body))
|
29
|
+
end
|
30
|
+
|
31
|
+
# Send an HTTP HEAD request
|
32
|
+
# @param url [String,URI] URL of the request
|
33
|
+
# @param options [Hash<Symbol,Object>] Request options
|
34
|
+
# @return [Net::HTTP::Response]
|
35
|
+
def head(url, options = {})
|
36
|
+
send_request_with_method(:head, url, options)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Send an HTTP DELETE request
|
40
|
+
# @param url [String,URI] URL of the request
|
41
|
+
# @param options [Hash<Symbol,Object>] Request options
|
42
|
+
# @return [Net::HTTP::Response]
|
43
|
+
def delete(url, options = {})
|
44
|
+
send_request_with_method(:delete, url, options)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Start an HTTP request. Yields the http object.
|
48
|
+
# @param url [String,URI] URL of the request, requires host and port
|
49
|
+
# @return [Object] The result of the yielded block
|
50
|
+
def start(url, _options = {})
|
51
|
+
uri = url.respond_to?(:host) ? url : URI.parse(url)
|
52
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
53
|
+
http.use_ssl = uri.scheme == 'https'
|
54
|
+
# http.set_debug_output(Translatomatic.config.logger) if ENV['DEBUG']
|
55
|
+
result = http.start do
|
56
|
+
@http = http
|
57
|
+
yield http
|
58
|
+
end
|
59
|
+
@http = nil
|
60
|
+
result
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
include Util
|
66
|
+
|
67
|
+
MAX_REDIRECTS = 5
|
68
|
+
RETRIABLE = [Net::HTTPServerError, Net::HTTPTooManyRequests].freeze
|
69
|
+
RETRIABLE_CODES = [408].freeze
|
70
|
+
|
71
|
+
# Retry requests on server errors
|
72
|
+
class HttpRetryExecutor < RetryExecutor
|
73
|
+
def retriable?(exception)
|
74
|
+
http_exception?(exception) && retriable_exception?(exception)
|
75
|
+
end
|
76
|
+
|
77
|
+
def http_exception?(exception)
|
78
|
+
exception.is_a?(Translatomatic::HTTP::Exception)
|
79
|
+
end
|
80
|
+
|
81
|
+
def retriable_exception?(exception)
|
82
|
+
RETRIABLE.any? { |i| exception.response.kind_of?(i) } ||
|
83
|
+
RETRIABLE_CODES.include?(exception.response.code.to_i)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def send_request_with_method(method, url, options = {})
|
88
|
+
cookies = ::HTTP::Cookie.cookie_value(@jar.cookies(url))
|
89
|
+
options = options.merge(cookies: cookies) if cookies.present?
|
90
|
+
request = Request.new(method, url, options)
|
91
|
+
send_request(request)
|
92
|
+
end
|
93
|
+
|
94
|
+
def send_request(req)
|
95
|
+
executor = HttpRetryExecutor.new(retry_delay: @retry_delay)
|
96
|
+
executor.run { handle_response(send_request_http(req)) }
|
97
|
+
end
|
98
|
+
|
99
|
+
def send_request_http(req)
|
100
|
+
if @http
|
101
|
+
log.debug("HTTP request: #{req.http_request.uri}")
|
102
|
+
@http.request(req.http_request)
|
103
|
+
else
|
104
|
+
start(req.uri) { |_http| send_request_http(req) }
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def save_cookies(response)
|
109
|
+
cookies = response.get_fields('set-cookie')
|
110
|
+
return unless cookies
|
111
|
+
cookies.each { |i| @jar.parse(i, response.uri) }
|
112
|
+
end
|
113
|
+
|
114
|
+
def handle_response(response)
|
115
|
+
log.debug("HTTP response: #{response.code} #{response.msg}")
|
116
|
+
|
117
|
+
case response
|
118
|
+
when Net::HTTPSuccess
|
119
|
+
@redirects = 0
|
120
|
+
save_cookies(response)
|
121
|
+
response
|
122
|
+
when Net::HTTPRedirection
|
123
|
+
@redirects += 1
|
124
|
+
raise 'Maximum redirects exceeded' if @redirects > MAX_REDIRECTS
|
125
|
+
new_uri = redirect_uri(response)
|
126
|
+
get(new_uri, nil)
|
127
|
+
else
|
128
|
+
# error
|
129
|
+
@redirects = 0
|
130
|
+
raise Translatomatic::HTTP::Exception, response
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def redirect_uri(response)
|
135
|
+
location = URI.parse(response['Location'])
|
136
|
+
if location.relative?
|
137
|
+
response.uri + response.location
|
138
|
+
else
|
139
|
+
location
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Translatomatic
|
2
|
+
module HTTP
|
3
|
+
# Exception used for unexpected http responses
|
4
|
+
class Exception < StandardError
|
5
|
+
attr_reader :response
|
6
|
+
|
7
|
+
def initialize(response)
|
8
|
+
@response = response
|
9
|
+
end
|
10
|
+
|
11
|
+
# Exception string
|
12
|
+
def to_s
|
13
|
+
error_from_response(@response) || default_error
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def default_error
|
19
|
+
"error #{@response.code}"
|
20
|
+
end
|
21
|
+
|
22
|
+
def error_from_response(response)
|
23
|
+
if response.content_type == 'text/html'
|
24
|
+
error_from_html(response.body)
|
25
|
+
elsif response.body.present?
|
26
|
+
response.body
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def error_from_html(body)
|
31
|
+
doc = Nokogiri::HTML(body)
|
32
|
+
texts = doc.search('//text()')
|
33
|
+
msg = texts.find { |i| error_from_string(i.content) }
|
34
|
+
msg ? error_from_string(msg.content) : nil
|
35
|
+
end
|
36
|
+
|
37
|
+
def error_from_string(string)
|
38
|
+
match = string.match(/(?:Message|Error):\s*(.*)/i)
|
39
|
+
match ? match[1] : nil
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Translatomatic
|
2
|
+
module HTTP
|
3
|
+
# Formats the contents of a file or string for a multipart post
|
4
|
+
class FileParam < Param
|
5
|
+
attr_accessor :filename, :content, :mime_type
|
6
|
+
|
7
|
+
def initialize(key:, filename:, content:, mime_type:)
|
8
|
+
@key = key
|
9
|
+
@filename = filename
|
10
|
+
@content = content
|
11
|
+
@mime_type = mime_type
|
12
|
+
end
|
13
|
+
|
14
|
+
# (see Param#to_s)
|
15
|
+
def to_s
|
16
|
+
header(header_data) +
|
17
|
+
header('Content-Type' => mime_type) + "\r\n#{content}\r\n"
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def header_data
|
23
|
+
super.merge(filename: %("#{filename}"))
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Translatomatic
|
2
|
+
module HTTP
|
3
|
+
# Formats a basic string key/value pair for a multipart post
|
4
|
+
class Param
|
5
|
+
attr_accessor :key, :value
|
6
|
+
|
7
|
+
def initialize(key:, value:)
|
8
|
+
@key = key
|
9
|
+
@value = value
|
10
|
+
end
|
11
|
+
|
12
|
+
# @return [String] Representation of this parameter as it appears
|
13
|
+
# within a multipart post request.
|
14
|
+
def to_s
|
15
|
+
header(header_data) + "\r\n#{value}\r\n"
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def header_data
|
21
|
+
name = CGI.escape(key.to_s)
|
22
|
+
{ 'Content-Disposition' => 'form-data', name: %("#{name}") }
|
23
|
+
end
|
24
|
+
|
25
|
+
def header(options)
|
26
|
+
out = []
|
27
|
+
idx = 0
|
28
|
+
options.each do |key, value|
|
29
|
+
separator = idx.zero? ? ': ' : '='
|
30
|
+
out << "#{key}#{separator}#{value}"
|
31
|
+
idx += 1
|
32
|
+
end
|
33
|
+
out.join('; ') + "\r\n"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
require 'net/http'
|
3
|
+
|
4
|
+
module Translatomatic
|
5
|
+
module HTTP
|
6
|
+
# HTTP request
|
7
|
+
# wrapper for Net::HTTP functionality
|
8
|
+
class Request
|
9
|
+
# @return [String] the text to use to denote multipart boundaries. By
|
10
|
+
# default, a random hexadecimal string is used.
|
11
|
+
attr_accessor :multipart_boundary
|
12
|
+
|
13
|
+
# @return [String] the HTTP body
|
14
|
+
attr_accessor :body
|
15
|
+
|
16
|
+
# @return [URI] the URI of the request
|
17
|
+
attr_reader :uri
|
18
|
+
|
19
|
+
# @return [String] the HTTP method
|
20
|
+
attr_reader :method
|
21
|
+
|
22
|
+
# @param method [Symbol] HTTP method
|
23
|
+
# @param url [String,URI] URL of the request
|
24
|
+
# @return [Translatomatic::HTTPRequest] Create a new request
|
25
|
+
def initialize(method, url, options = {})
|
26
|
+
@method = method
|
27
|
+
@options = options
|
28
|
+
@uri = url.respond_to?(:host) ? url.dup : URI.parse(url)
|
29
|
+
query = options[:query]
|
30
|
+
@uri.query = URI.encode_www_form(query) if query
|
31
|
+
@multipart_boundary = generate_multipart_boundary
|
32
|
+
@body = @options[:body]
|
33
|
+
end
|
34
|
+
|
35
|
+
# @return [Object] The request object for use with Net::HTTP
|
36
|
+
def http_request
|
37
|
+
@request ||= create_request
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def generate_multipart_boundary
|
43
|
+
SecureRandom.hex(16)
|
44
|
+
end
|
45
|
+
|
46
|
+
def multipartify(parts)
|
47
|
+
string_parts = parts.collect do |i|
|
48
|
+
part = paramify(i)
|
49
|
+
'--' + @multipart_boundary + "\r\n" + part.to_s
|
50
|
+
end
|
51
|
+
string_parts.join('') + '--' + @multipart_boundary + "--\r\n"
|
52
|
+
end
|
53
|
+
|
54
|
+
def paramify(object)
|
55
|
+
return object if object.is_a?(Param) || object.is_a?(FileParam)
|
56
|
+
raise 'invalid multipart parameter' unless object.is_a?(Hash)
|
57
|
+
object[:filename] ? FileParam.new(object) : Param.new(object)
|
58
|
+
end
|
59
|
+
|
60
|
+
def create_request
|
61
|
+
klass = Net::HTTP.const_get(@method.to_s.classify)
|
62
|
+
request = klass.new(@uri)
|
63
|
+
request['User-Agent'] = USER_AGENT
|
64
|
+
|
65
|
+
(@options[:headers] || {}).each do |key, value|
|
66
|
+
request[key] = value
|
67
|
+
end
|
68
|
+
|
69
|
+
request['Cookie'] = @options[:cookies] if @options[:cookies]
|
70
|
+
|
71
|
+
content_type = @options[:content_type]
|
72
|
+
|
73
|
+
if body
|
74
|
+
if @options[:multipart] || body.is_a?(Array)
|
75
|
+
boundary = "boundary=#{@multipart_boundary}"
|
76
|
+
content_type = 'multipart/form-data; ' + boundary
|
77
|
+
request.body = multipartify(body)
|
78
|
+
elsif body.is_a?(Hash)
|
79
|
+
# set_form_data does url encoding
|
80
|
+
request.set_form_data(body)
|
81
|
+
else
|
82
|
+
request.body = body
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
request.content_type = content_type if content_type
|
87
|
+
request
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'i18n'
|
2
|
+
require 'rails_i18n' # date/time/currency formats
|
3
|
+
|
4
|
+
module Translatomatic
|
5
|
+
# I18n initialisation and translation fallback handling
|
6
|
+
class I18n
|
7
|
+
class << self
|
8
|
+
# Get string translation
|
9
|
+
# @param key translation key
|
10
|
+
def t(key, options = {})
|
11
|
+
tkey = "translatomatic.#{key}"
|
12
|
+
raise "missing translation: #{tkey}" unless ::I18n.exists?(tkey)
|
13
|
+
|
14
|
+
::I18n.t(tkey, options.merge(locale: t_locale(options)))
|
15
|
+
end
|
16
|
+
|
17
|
+
# Localises dates and numbers to local formatting
|
18
|
+
# @param object [Object] Object to localise
|
19
|
+
def l(object, options = {})
|
20
|
+
::I18n.l(object, options)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
FALLBACK_LOCALE = 'en'.freeze
|
26
|
+
|
27
|
+
def init_i18n(root_path)
|
28
|
+
locale_path = File.join(root_path, 'config', 'locales')
|
29
|
+
::I18n.load_path += Dir[File.join(locale_path, '**', '*.yml')]
|
30
|
+
end
|
31
|
+
|
32
|
+
def t_locale(options)
|
33
|
+
locale = options[:locale] || Locale.default.to_s
|
34
|
+
locale = FALLBACK_LOCALE unless ::I18n.locale_available?(locale)
|
35
|
+
locale
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
begin
|
40
|
+
init_i18n(File.join(__dir__, '..', '..'))
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -1,76 +1,88 @@
|
|
1
|
-
|
2
|
-
# @see https://en.wikipedia.org/wiki/Locale_(computer_software)
|
3
|
-
class Translatomatic::Locale
|
1
|
+
require 'i18n_data'
|
4
2
|
|
5
|
-
|
6
|
-
|
3
|
+
module Translatomatic
|
4
|
+
# Represents a locale
|
5
|
+
# @see https://en.wikipedia.org/wiki/Locale_(computer_software)
|
6
|
+
class Locale
|
7
|
+
# @return [String] ISO 639-1 language
|
8
|
+
attr_reader :language
|
7
9
|
|
8
|
-
|
9
|
-
|
10
|
+
# @return [String] ISO 15924 script
|
11
|
+
attr_reader :script
|
10
12
|
|
11
|
-
|
12
|
-
|
13
|
+
# @return [String] ISO 3166-1 alpha-2 country code
|
14
|
+
attr_reader :region
|
13
15
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
# @return [Locale] A locale object
|
18
|
-
def self.parse(tag, validate = true)
|
19
|
-
return nil if tag.nil?
|
16
|
+
class << self
|
17
|
+
# @return [Locale] The default locale
|
18
|
+
attr_accessor :default
|
20
19
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
locale
|
25
|
-
|
26
|
-
|
27
|
-
end
|
20
|
+
# Parse the given tag
|
21
|
+
# @param tag [String] A string representing a locale
|
22
|
+
# @param validate [boolean] If true, return nil if the locale is invalid
|
23
|
+
# @return [Locale] A locale object
|
24
|
+
def parse(tag, validate = true)
|
25
|
+
return nil if tag.nil?
|
28
26
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
27
|
+
locale = tag
|
28
|
+
unless tag.is_a?(Translatomatic::Locale)
|
29
|
+
tag = tag.to_s.tr('_', '-')
|
30
|
+
locale = new(tag)
|
31
|
+
end
|
32
|
+
validate && !locale.valid? ? nil : locale
|
33
|
+
end
|
33
34
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
@language = data.language
|
39
|
-
@script = data.script
|
40
|
-
@region = data.region
|
35
|
+
# @return [Array<String>] A list of ISO 639-1 language codes
|
36
|
+
def language_codes
|
37
|
+
VALID_LANGUAGES
|
38
|
+
end
|
41
39
|
end
|
42
|
-
end
|
43
40
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
41
|
+
# @return [Locale] create a new locale object
|
42
|
+
def initialize(tag)
|
43
|
+
data = ::I18n::Locale::Tag::Rfc4646.tag(tag)
|
44
|
+
if data
|
45
|
+
@language = data.language
|
46
|
+
@script = data.script
|
47
|
+
@region = data.region
|
48
|
+
end
|
49
|
+
end
|
48
50
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
51
|
+
# @return true if language is a valid ISO 639-1 language
|
52
|
+
def valid?
|
53
|
+
VALID_LANGUAGES.include?(language)
|
54
|
+
end
|
53
55
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
other.kind_of?(Translatomatic::Locale) && other.hash == hash
|
59
|
-
end
|
56
|
+
# @return [String] Locale as a string
|
57
|
+
def to_s
|
58
|
+
[language, script, region].compact.join('-')
|
59
|
+
end
|
60
60
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
61
|
+
# @param other [Object] Another object
|
62
|
+
# @return [boolean] true if the other object is a {Translatomatic::Locale}
|
63
|
+
# object and has equal language, script and region.
|
64
|
+
def eql?(other)
|
65
|
+
other.is_a?(Translatomatic::Locale) && other.hash == hash
|
66
|
+
end
|
65
67
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
68
|
+
# (see #eql?)
|
69
|
+
def ==(other)
|
70
|
+
eql?(other)
|
71
|
+
end
|
70
72
|
|
71
|
-
|
73
|
+
# @!visibility private
|
74
|
+
def hash
|
75
|
+
[language, script, region].hash
|
76
|
+
end
|
72
77
|
|
73
|
-
|
74
|
-
|
78
|
+
# list of 2 letter country codes
|
79
|
+
VALID_LANGUAGES = ::I18nData.languages.keys.collect(&:downcase).sort.freeze
|
80
|
+
private_constant :VALID_LANGUAGES
|
75
81
|
|
82
|
+
begin
|
83
|
+
# get default locale from the environment
|
84
|
+
lang = (ENV['LANG'] || '').split(/\./)[0]
|
85
|
+
self.default = parse(lang) || parse('en')
|
86
|
+
end
|
87
|
+
end
|
76
88
|
end
|