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.
Files changed (139) hide show
  1. checksums.yaml +5 -5
  2. data/.gitattributes +20 -20
  3. data/.gitignore +19 -15
  4. data/.rspec +3 -3
  5. data/.rubocop.yml +28 -0
  6. data/.translatomatic/config.yml +4 -0
  7. data/.travis.yml +4 -6
  8. data/.yardopts +9 -9
  9. data/Gemfile +8 -4
  10. data/Guardfile +4 -5
  11. data/README.de.md +55 -50
  12. data/README.en.md +177 -0
  13. data/README.es.md +53 -48
  14. data/README.fr.md +53 -48
  15. data/README.it.md +54 -49
  16. data/README.ja.md +63 -58
  17. data/README.ko.md +59 -54
  18. data/README.md +17 -13
  19. data/README.ms.md +50 -45
  20. data/README.pt.md +54 -49
  21. data/README.ru.md +57 -52
  22. data/README.sv.md +51 -46
  23. data/README.zh.md +60 -55
  24. data/Rakefile +3 -3
  25. data/TODO.txt +6 -0
  26. data/bin/console +3 -3
  27. data/bin/translatomatic +4 -2
  28. data/config/i18n-tasks.yml +130 -0
  29. data/config/locales/translatomatic/de.yml +141 -99
  30. data/config/locales/translatomatic/en.yml +129 -89
  31. data/config/locales/translatomatic/es.yml +136 -99
  32. data/config/locales/translatomatic/fr.yml +139 -100
  33. data/config/locales/translatomatic/it.yml +135 -97
  34. data/config/locales/translatomatic/ja.yml +137 -98
  35. data/config/locales/translatomatic/ko.yml +138 -98
  36. data/config/locales/translatomatic/ms.yml +138 -100
  37. data/config/locales/translatomatic/pt.yml +137 -101
  38. data/config/locales/translatomatic/ru.yml +136 -98
  39. data/config/locales/translatomatic/sv.yml +134 -96
  40. data/config/locales/translatomatic/zh.yml +136 -97
  41. data/db/migrate/201712170000_initial.rb +2 -3
  42. data/lib/translatomatic.rb +40 -25
  43. data/lib/translatomatic/cli.rb +5 -1
  44. data/lib/translatomatic/cli/base.rb +61 -58
  45. data/lib/translatomatic/cli/common_options.rb +14 -11
  46. data/lib/translatomatic/cli/config.rb +96 -91
  47. data/lib/translatomatic/cli/database.rb +85 -23
  48. data/lib/translatomatic/cli/main.rb +158 -104
  49. data/lib/translatomatic/cli/thor.rb +29 -0
  50. data/lib/translatomatic/cli/translate.rb +134 -157
  51. data/lib/translatomatic/config.rb +10 -301
  52. data/lib/translatomatic/config/display.rb +78 -0
  53. data/lib/translatomatic/config/files.rb +60 -0
  54. data/lib/translatomatic/config/location_settings.rb +133 -0
  55. data/lib/translatomatic/config/options.rb +68 -0
  56. data/lib/translatomatic/config/selector.rb +127 -0
  57. data/lib/translatomatic/config/settings.rb +148 -0
  58. data/lib/translatomatic/converter.rb +40 -28
  59. data/lib/translatomatic/database.rb +127 -110
  60. data/lib/translatomatic/define_options.rb +4 -5
  61. data/lib/translatomatic/escaped_unicode.rb +86 -76
  62. data/lib/translatomatic/extractor.rb +5 -2
  63. data/lib/translatomatic/extractor/base.rb +12 -12
  64. data/lib/translatomatic/extractor/ruby.rb +7 -6
  65. data/lib/translatomatic/file_translator.rb +101 -244
  66. data/lib/translatomatic/flattenation.rb +39 -0
  67. data/lib/translatomatic/http.rb +13 -0
  68. data/lib/translatomatic/http/client.rb +144 -0
  69. data/lib/translatomatic/http/exception.rb +43 -0
  70. data/lib/translatomatic/http/file_param.rb +27 -0
  71. data/lib/translatomatic/http/param.rb +37 -0
  72. data/lib/translatomatic/http/request.rb +91 -0
  73. data/lib/translatomatic/i18n.rb +43 -0
  74. data/lib/translatomatic/locale.rb +71 -59
  75. data/lib/translatomatic/logger.rb +43 -28
  76. data/lib/translatomatic/metadata.rb +58 -0
  77. data/lib/translatomatic/model.rb +4 -2
  78. data/lib/translatomatic/model/locale.rb +5 -5
  79. data/lib/translatomatic/model/text.rb +5 -5
  80. data/lib/translatomatic/option.rb +57 -34
  81. data/lib/translatomatic/path_utils.rb +126 -0
  82. data/lib/translatomatic/progress_updater.rb +13 -16
  83. data/lib/translatomatic/provider.rb +101 -0
  84. data/lib/translatomatic/provider/base.rb +136 -0
  85. data/lib/translatomatic/provider/frengly.rb +55 -0
  86. data/lib/translatomatic/provider/google.rb +78 -0
  87. data/lib/translatomatic/provider/google_web.rb +50 -0
  88. data/lib/translatomatic/provider/microsoft.rb +144 -0
  89. data/lib/translatomatic/provider/my_memory.rb +75 -0
  90. data/lib/translatomatic/provider/yandex.rb +61 -0
  91. data/lib/translatomatic/resource_file.rb +59 -53
  92. data/lib/translatomatic/resource_file/base.rb +171 -237
  93. data/lib/translatomatic/resource_file/csv.rb +176 -24
  94. data/lib/translatomatic/resource_file/html.rb +21 -42
  95. data/lib/translatomatic/resource_file/key_value_support.rb +117 -0
  96. data/lib/translatomatic/resource_file/markdown.rb +36 -38
  97. data/lib/translatomatic/resource_file/plist.rb +121 -126
  98. data/lib/translatomatic/resource_file/po.rb +104 -82
  99. data/lib/translatomatic/resource_file/properties.rb +48 -77
  100. data/lib/translatomatic/resource_file/properties.treetop +87 -0
  101. data/lib/translatomatic/resource_file/resw.rb +56 -41
  102. data/lib/translatomatic/resource_file/subtitle.rb +86 -54
  103. data/lib/translatomatic/resource_file/text.rb +18 -18
  104. data/lib/translatomatic/resource_file/xcode_strings.rb +32 -63
  105. data/lib/translatomatic/resource_file/xcode_strings.treetop +85 -0
  106. data/lib/translatomatic/resource_file/xml.rb +94 -81
  107. data/lib/translatomatic/resource_file/yaml.rb +54 -68
  108. data/lib/translatomatic/retry_executor.rb +37 -0
  109. data/lib/translatomatic/slurp.rb +32 -0
  110. data/lib/translatomatic/string_batcher.rb +50 -0
  111. data/lib/translatomatic/string_escaping.rb +61 -0
  112. data/lib/translatomatic/text.rb +263 -0
  113. data/lib/translatomatic/text_collection.rb +66 -0
  114. data/lib/translatomatic/tmx.rb +5 -3
  115. data/lib/translatomatic/tmx/document.rb +107 -82
  116. data/lib/translatomatic/tmx/translation_unit.rb +19 -18
  117. data/lib/translatomatic/translation.rb +8 -28
  118. data/lib/translatomatic/translation/collection.rb +199 -0
  119. data/lib/translatomatic/translation/fetcher.rb +123 -0
  120. data/lib/translatomatic/translation/munging.rb +112 -0
  121. data/lib/translatomatic/translation/result.rb +50 -0
  122. data/lib/translatomatic/translation/sharer.rb +32 -0
  123. data/lib/translatomatic/translation/stats.rb +44 -0
  124. data/lib/translatomatic/translator.rb +91 -88
  125. data/lib/translatomatic/type_cast.rb +63 -0
  126. data/lib/translatomatic/util.rb +37 -33
  127. data/lib/translatomatic/version.rb +2 -2
  128. data/translatomatic.gemspec +57 -46
  129. metadata +136 -59
  130. data/lib/translatomatic/http_request.rb +0 -162
  131. data/lib/translatomatic/string.rb +0 -188
  132. data/lib/translatomatic/translation_result.rb +0 -86
  133. data/lib/translatomatic/translation_stats.rb +0 -31
  134. data/lib/translatomatic/translator/base.rb +0 -128
  135. data/lib/translatomatic/translator/frengly.rb +0 -62
  136. data/lib/translatomatic/translator/google.rb +0 -37
  137. data/lib/translatomatic/translator/microsoft.rb +0 -41
  138. data/lib/translatomatic/translator/my_memory.rb +0 -68
  139. 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
- # Represents a locale
2
- # @see https://en.wikipedia.org/wiki/Locale_(computer_software)
3
- class Translatomatic::Locale
1
+ require 'i18n_data'
4
2
 
5
- # @return [String] ISO 639-1 language
6
- attr_reader :language
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
- # @return [String] ISO 15924 script
9
- attr_reader :script
10
+ # @return [String] ISO 15924 script
11
+ attr_reader :script
10
12
 
11
- # @return [String] ISO 3166-1 alpha-2 country code
12
- attr_reader :region
13
+ # @return [String] ISO 3166-1 alpha-2 country code
14
+ attr_reader :region
13
15
 
14
- # Parse the given tag
15
- # @param tag [String] A string representing a locale
16
- # @param validate [boolean] If true, return nil if the locale is invalid
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
- locale = tag
22
- unless tag.kind_of?(Translatomatic::Locale)
23
- tag = tag.to_s.gsub(/_/, '-')
24
- locale = new(tag)
25
- end
26
- validate && !locale.valid? ? nil : locale
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
- # @return [Locale] the default locale
30
- def self.default
31
- parse(Translatomatic.config.default_locale)
32
- end
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
- # @return [Locale] create a new locale object
35
- def initialize(tag)
36
- data = I18n::Locale::Tag::Rfc4646.tag(tag)
37
- if data
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
- # @return true if language is a valid ISO 639-1 language
45
- def valid?
46
- VALID_LANGUAGES.include?(language)
47
- end
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
- # @return [String] Locale as a string
50
- def to_s
51
- [language, script, region].compact.join("-")
52
- end
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
- # @param other [Object] Another object
55
- # @return [boolean] true if the other object is a {Translatomatic::Locale}
56
- # object and has equal language, script and region.
57
- def eql?(other)
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
- # (see #eql?)
62
- def ==(other)
63
- eql?(other)
64
- end
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
- # @!visibility private
67
- def hash
68
- [language, script, region].hash
69
- end
68
+ # (see #eql?)
69
+ def ==(other)
70
+ eql?(other)
71
+ end
70
72
 
71
- private
73
+ # @!visibility private
74
+ def hash
75
+ [language, script, region].hash
76
+ end
72
77
 
73
- # list of 2 letter country codes
74
- VALID_LANGUAGES = ::I18nData.languages.keys.collect { |i| i.downcase }.sort
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