wovnrb 3.10.2 → 3.11.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/wovnrb.rb CHANGED
@@ -1,138 +1,145 @@
1
- require 'rack'
2
- require 'wovnrb/api_translator'
3
- require 'wovnrb/headers'
4
- require 'wovnrb/store'
5
- require 'wovnrb/lang'
6
- require 'wovnrb/services/html_converter'
7
- require 'wovnrb/services/html_replace_marker'
8
- require 'wovnrb/url_language_switcher'
9
- require 'nokogiri'
10
- require 'active_support'
11
- require 'json'
12
- require 'wovnrb/helpers/nokogumbo_helper'
13
- require 'wovnrb/railtie' if defined?(Rails)
14
- require 'wovnrb/version'
15
-
16
- module Wovnrb
17
- class Interceptor
18
- def initialize(app, opts = {})
19
- @app = app
20
- @store = Store.instance
21
- opts = opts.transform_keys(&:to_s)
22
- @store.update_settings(opts)
23
- @url_lang_switcher = Wovnrb::UrlLanguageSwitcher.new(@store)
24
- end
25
-
26
- def call(env)
27
- # disabled by previous Rack middleware
28
- return @app.call(env) if Rack::Request.new(env).params['wovn_disable'] == true
29
-
30
- @store.settings.clear_dynamic_settings!
31
- return @app.call(env) unless Store.instance.valid_settings?
32
-
33
- @env = env
34
- headers = Headers.new(env, @store.settings, @url_lang_switcher)
35
- default_lang = @store.settings['default_lang']
36
- return @app.call(env) if @store.settings['test_mode'] && @store.settings['test_url'] != headers.url
37
-
38
- # redirect if the path is set to the default language (for SEO purposes)
39
- if explicit_default_lang?(headers)
40
- redirect_headers = headers.redirect(default_lang)
41
- return [307, redirect_headers, ['']]
42
- end
43
-
44
- # if path containing language code is ignored, do nothing
45
- if headers.lang_code != default_lang && ignore_path?(headers.unmasked_pathname_without_trailing_slash)
46
- status, res_headers, body = @app.call(env)
47
-
48
- return output(headers, status, res_headers, body)
49
- end
50
- # pass to application
51
- status, res_headers, body = @app.call(headers.request_out)
52
-
53
- # disabled by next Rack middleware
54
- return output(headers, status, res_headers, body) unless res_headers['Content-Type']&.include?('html')
55
-
56
- request = Rack::Request.new(env)
57
-
58
- return output(headers, status, res_headers, body) if request.params['wovn_disable'] == true
59
-
60
- @store.settings.update_dynamic_settings!(request.params)
61
- return output(headers, status, res_headers, body) if ignore_path?(headers.pathname)
62
-
63
- body = switch_lang(headers, body) unless /^1|302/.match?(status.to_s)
64
-
65
- content_length = 0
66
- body.each { |b| content_length += b.respond_to?(:bytesize) ? b.bytesize : 0 }
67
- res_headers['Content-Length'] = content_length.to_s
68
-
69
- output(headers, status, res_headers, body)
70
- end
71
-
72
- def switch_lang(headers, body)
73
- translated_body = []
74
-
75
- # Must use `.each` for to support multiple-chunks in Sinatra
76
- string_body = ''
77
- body.each { |chunk| string_body += chunk }
78
- html_body = Helpers::NokogumboHelper.parse_html(string_body)
79
-
80
- if !wovn_ignored?(html_body) && !amp_page?(html_body)
81
-
82
- html_converter = HtmlConverter.new(html_body, @store, headers, @url_lang_switcher)
83
-
84
- if needs_api?(html_body, headers)
85
- converted_html, marker = html_converter.build_api_compatible_html
86
- translated_content = ApiTranslator.new(@store, headers, WovnLogger.uuid).translate(converted_html)
87
- translated_body.push(marker.revert(translated_content))
88
- else
89
- string_body = html_converter.build if html_body.html?
90
- translated_body.push(string_body)
91
- end
92
- else
93
- translated_body.push(string_body)
94
- end
95
-
96
- body.close if body.respond_to?(:close)
97
- translated_body
98
- end
99
-
100
- private
101
-
102
- def output(headers, status, res_headers, body)
103
- headers.out(res_headers)
104
- [status, res_headers, body]
105
- end
106
-
107
- def needs_api?(html_body, headers)
108
- headers.lang_code != @store.settings['default_lang'] &&
109
- (html_body.html? || @store.settings['translate_fragment'])
110
- end
111
-
112
- def wovn_ignored?(html_body)
113
- !html_body.xpath('//html[@wovn-ignore or @data-wovn-ignore]').empty?
114
- end
115
-
116
- def ignore_path?(path)
117
- @store.settings['ignore_globs'].ignore?(path)
118
- end
119
-
120
- # Checks if a given HTML body is an Accelerated Mobile Page (AMP).
121
- # To do so, it looks at the required attributes for the HTML tag:
122
- # https://www.ampproject.org/docs/tutorials/create/basic_markup.
123
- #
124
- # @param {Nokogiri::HTML5::Document} body The HTML body to check.
125
- #
126
- # @returns {Boolean} True is the HTML body is an AMP, false otherwise.
127
- def amp_page?(html_body)
128
- html_attributes = html_body.xpath('//html')[0].try(:attributes) || {}
129
-
130
- !!(html_attributes['amp'] || html_attributes["\u26A1"])
131
- end
132
-
133
- def explicit_default_lang?(headers)
134
- default_lang, url_pattern = @store.settings.values_at('default_lang', 'url_pattern')
135
- default_lang == headers.url_language && url_pattern != 'custom_domain'
136
- end
137
- end
138
- end
1
+ require 'rack'
2
+ require 'wovnrb/api_translator'
3
+ require 'wovnrb/headers'
4
+ require 'wovnrb/store'
5
+ require 'wovnrb/lang'
6
+ require 'wovnrb/services/html_converter'
7
+ require 'wovnrb/services/html_replace_marker'
8
+ require 'wovnrb/url_language_switcher'
9
+ require 'nokogiri'
10
+ require 'active_support'
11
+ require 'json'
12
+ require 'wovnrb/helpers/nokogumbo_helper'
13
+ require 'wovnrb/railtie' if defined?(Rails)
14
+ require 'wovnrb/version'
15
+
16
+ module Wovnrb
17
+ class Interceptor
18
+ def initialize(app, opts = {})
19
+ @app = app
20
+ @store = Store.instance
21
+ opts = opts.transform_keys(&:to_s)
22
+ @store.update_settings(opts)
23
+ @url_lang_switcher = Wovnrb::UrlLanguageSwitcher.new(@store)
24
+ end
25
+
26
+ def call(env)
27
+ # disabled by previous Rack middleware
28
+ return @app.call(env) if Rack::Request.new(env).params['wovn_disable'] == true
29
+
30
+ @store.settings.clear_dynamic_settings!
31
+ return @app.call(env) unless Store.instance.valid_settings?
32
+
33
+ @env = env
34
+ headers = Headers.new(env, @store.settings, @url_lang_switcher)
35
+ default_lang = @store.settings['default_lang']
36
+ return @app.call(env) if @store.settings['test_mode'] && @store.settings['test_url'] != headers.url
37
+
38
+ cookie_lang = Rack::Request.new(env).cookies['wovn_selected_lang']
39
+ request_lang = headers.lang_code
40
+ if @store.settings['use_cookie_lang'] && cookie_lang.present? && request_lang != cookie_lang && request_lang == @store.default_lang
41
+ redirect_headers = headers.redirect(cookie_lang)
42
+ return [302, redirect_headers, ['']]
43
+ end
44
+
45
+ # redirect if the path is set to the default language (for SEO purposes)
46
+ if explicit_default_lang?(headers)
47
+ redirect_headers = headers.redirect(default_lang)
48
+ return [307, redirect_headers, ['']]
49
+ end
50
+
51
+ # if path containing language code is ignored, do nothing
52
+ if headers.lang_code != default_lang && ignore_path?(headers.unmasked_pathname_without_trailing_slash)
53
+ status, res_headers, body = @app.call(env)
54
+
55
+ return output(headers, status, res_headers, body)
56
+ end
57
+ # pass to application
58
+ status, res_headers, body = @app.call(headers.request_out)
59
+
60
+ # disabled by next Rack middleware
61
+ return output(headers, status, res_headers, body) unless res_headers['Content-Type']&.include?('html')
62
+
63
+ request = Rack::Request.new(env)
64
+
65
+ return output(headers, status, res_headers, body) if request.params['wovn_disable'] == true
66
+
67
+ @store.settings.update_dynamic_settings!(request.params)
68
+ return output(headers, status, res_headers, body) if ignore_path?(headers.pathname)
69
+
70
+ body = switch_lang(headers, body) unless /^1|302/.match?(status.to_s)
71
+
72
+ content_length = 0
73
+ body.each { |b| content_length += b.respond_to?(:bytesize) ? b.bytesize : 0 }
74
+ res_headers['Content-Length'] = content_length.to_s
75
+
76
+ output(headers, status, res_headers, body)
77
+ end
78
+
79
+ def switch_lang(headers, body)
80
+ translated_body = []
81
+
82
+ # Must use `.each` for to support multiple-chunks in Sinatra
83
+ string_body = ''
84
+ body.each { |chunk| string_body += chunk }
85
+ html_body = Helpers::NokogumboHelper.parse_html(string_body)
86
+
87
+ if !wovn_ignored?(html_body) && !amp_page?(html_body)
88
+
89
+ html_converter = HtmlConverter.new(html_body, @store, headers, @url_lang_switcher)
90
+
91
+ if needs_api?(html_body, headers)
92
+ converted_html, marker = html_converter.build_api_compatible_html
93
+ translated_content = ApiTranslator.new(@store, headers, WovnLogger.uuid).translate(converted_html)
94
+ translated_body.push(marker.revert(translated_content))
95
+ else
96
+ string_body = html_converter.build if html_body.html?
97
+ translated_body.push(string_body)
98
+ end
99
+ else
100
+ translated_body.push(string_body)
101
+ end
102
+
103
+ body.close if body.respond_to?(:close)
104
+ translated_body
105
+ end
106
+
107
+ private
108
+
109
+ def output(headers, status, res_headers, body)
110
+ headers.out(res_headers)
111
+ [status, res_headers, body]
112
+ end
113
+
114
+ def needs_api?(html_body, headers)
115
+ headers.lang_code != @store.settings['default_lang'] &&
116
+ (html_body.html? || @store.settings['translate_fragment'])
117
+ end
118
+
119
+ def wovn_ignored?(html_body)
120
+ !html_body.xpath('//html[@wovn-ignore or @data-wovn-ignore]').empty?
121
+ end
122
+
123
+ def ignore_path?(path)
124
+ @store.settings['ignore_globs'].ignore?(path)
125
+ end
126
+
127
+ # Checks if a given HTML body is an Accelerated Mobile Page (AMP).
128
+ # To do so, it looks at the required attributes for the HTML tag:
129
+ # https://www.ampproject.org/docs/tutorials/create/basic_markup.
130
+ #
131
+ # @param {Nokogiri::HTML5::Document} body The HTML body to check.
132
+ #
133
+ # @returns {Boolean} True is the HTML body is an AMP, false otherwise.
134
+ def amp_page?(html_body)
135
+ html_attributes = html_body.xpath('//html')[0].try(:attributes) || {}
136
+
137
+ !!(html_attributes['amp'] || html_attributes["\u26A1"])
138
+ end
139
+
140
+ def explicit_default_lang?(headers)
141
+ default_lang, url_pattern = @store.settings.values_at('default_lang', 'url_pattern')
142
+ default_lang == headers.url_language && url_pattern != 'custom_domain'
143
+ end
144
+ end
145
+ end
@@ -1,63 +1,59 @@
1
- require 'test_helper'
2
-
3
- module Wovnrb
4
- class LangTest < WovnMiniTest
5
- def test_langs_exist
6
- refute_nil(Wovnrb::Lang::LANG)
7
- end
8
-
9
- def test_langs_length
10
- assert_equal(77, Wovnrb::Lang::LANG.length)
11
- end
12
-
13
- def test_keys_exist
14
- Wovnrb::Lang::LANG.each do |k, l|
15
- assert(l.key?(:name))
16
- assert(l.key?(:code))
17
- assert(l.key?(:en))
18
- assert_equal(k, l[:code])
19
- end
20
- end
21
-
22
- def test_iso_639_1_normalization
23
- Wovnrb::Lang::LANG.each do |_, l|
24
- case l[:code]
25
- when 'zh-CHS'
26
- assert_equal('zh-Hans', Lang.iso_639_1_normalization('zh-CHS'))
27
- when 'zh-CHT'
28
- assert_equal('zh-Hant', Lang.iso_639_1_normalization('zh-CHT'))
29
- else
30
- assert_equal(l[:code], Lang.iso_639_1_normalization(l[:code]))
31
- end
32
- end
33
- end
34
-
35
- def test_get_code_with_valid_code
36
- assert_equal('ms', Wovnrb::Lang.get_code('ms'))
37
- end
38
-
39
- def test_get_code_with_capital_letters
40
- assert_equal('zh-CHT', Wovnrb::Lang.get_code('zh-cht'))
41
- end
42
-
43
- def test_get_code_with_valid_english_name
44
- assert_equal('pt', Wovnrb::Lang.get_code('Portuguese'))
45
- end
46
-
47
- def test_get_code_with_valid_native_name
48
- assert_equal('hi', Wovnrb::Lang.get_code('हिन्दी'))
49
- end
50
-
51
- def test_get_code_with_invalid_name
52
- assert_nil(Wovnrb::Lang.get_code('WOVN4LYFE'))
53
- end
54
-
55
- def test_get_code_with_empty_string
56
- assert_nil(Wovnrb::Lang.get_code(''))
57
- end
58
-
59
- def test_get_code_with_nil
60
- assert_nil(Wovnrb::Lang.get_code(nil))
61
- end
62
- end
63
- end
1
+ require 'test_helper'
2
+
3
+ module Wovnrb
4
+ class LangTest < WovnMiniTest
5
+ def test_langs_exist
6
+ refute_nil(Wovnrb::Lang::LANG)
7
+ end
8
+
9
+ def test_keys_exist
10
+ Wovnrb::Lang::LANG.each do |k, l|
11
+ assert(l.key?(:name))
12
+ assert(l.key?(:code))
13
+ assert(l.key?(:en))
14
+ assert_equal(k, l[:code])
15
+ end
16
+ end
17
+
18
+ def test_iso_639_1_normalization
19
+ Wovnrb::Lang::LANG.each do |_, l|
20
+ case l[:code]
21
+ when 'zh-CHS'
22
+ assert_equal('zh-Hans', Lang.iso_639_1_normalization('zh-CHS'))
23
+ when 'zh-CHT'
24
+ assert_equal('zh-Hant', Lang.iso_639_1_normalization('zh-CHT'))
25
+ else
26
+ assert_equal(l[:code], Lang.iso_639_1_normalization(l[:code]))
27
+ end
28
+ end
29
+ end
30
+
31
+ def test_get_code_with_valid_code
32
+ assert_equal('ms', Wovnrb::Lang.get_code('ms'))
33
+ end
34
+
35
+ def test_get_code_with_capital_letters
36
+ assert_equal('zh-CHT', Wovnrb::Lang.get_code('zh-cht'))
37
+ end
38
+
39
+ def test_get_code_with_valid_english_name
40
+ assert_equal('pt', Wovnrb::Lang.get_code('Portuguese'))
41
+ end
42
+
43
+ def test_get_code_with_valid_native_name
44
+ assert_equal('hi', Wovnrb::Lang.get_code('हिन्दी'))
45
+ end
46
+
47
+ def test_get_code_with_invalid_name
48
+ assert_nil(Wovnrb::Lang.get_code('WOVN4LYFE'))
49
+ end
50
+
51
+ def test_get_code_with_empty_string
52
+ assert_nil(Wovnrb::Lang.get_code(''))
53
+ end
54
+
55
+ def test_get_code_with_nil
56
+ assert_nil(Wovnrb::Lang.get_code(nil))
57
+ end
58
+ end
59
+ end