wovnrb 3.11.0 → 3.13.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 +4 -4
- data/.rubocop.yml +3 -0
- data/README.en.md +256 -256
- data/README.ja.md +218 -218
- data/docker/rails/TestSite/config/application.rb +1 -2
- data/docker/rails/TestSite/yarn.lock +7449 -7353
- data/lib/wovnrb/api_translator.rb +186 -183
- data/lib/wovnrb/headers.rb +192 -192
- data/lib/wovnrb/lang.rb +260 -260
- data/lib/wovnrb/services/html_converter.rb +240 -222
- data/lib/wovnrb/services/html_replace_marker.rb +48 -48
- data/lib/wovnrb/settings.rb +2 -2
- data/lib/wovnrb/store.rb +230 -222
- data/lib/wovnrb/version.rb +3 -3
- data/lib/wovnrb.rb +146 -145
- data/test/lib/api_translator_test.rb +228 -217
- data/test/lib/custom_domain/custom_domain_langs_test.rb +2 -2
- data/test/lib/lang_test.rb +59 -59
- data/test/lib/services/html_converter_test.rb +570 -532
- data/test/lib/services/html_replace_marker_test.rb +149 -149
- data/test/lib/url_language_switcher_test.rb +1097 -1097
- data/test/lib/wovnrb_test.rb +477 -454
- data/test/test_helper.rb +1 -0
- metadata +6 -6
data/lib/wovnrb.rb
CHANGED
|
@@ -1,145 +1,146 @@
|
|
|
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
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
#
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
#
|
|
129
|
-
#
|
|
130
|
-
#
|
|
131
|
-
#
|
|
132
|
-
#
|
|
133
|
-
#
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
default_lang
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
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
|
+
request = Rack::Request.new(env)
|
|
29
|
+
return @app.call(env) if request.params['wovn_disable'] == true
|
|
30
|
+
|
|
31
|
+
@store.settings.clear_dynamic_settings!
|
|
32
|
+
return @app.call(env) unless Store.instance.valid_settings?
|
|
33
|
+
|
|
34
|
+
@env = env
|
|
35
|
+
headers = Headers.new(env, @store.settings, @url_lang_switcher)
|
|
36
|
+
default_lang = @store.settings['default_lang']
|
|
37
|
+
return @app.call(env) if @store.settings['test_mode'] && @store.settings['test_url'] != headers.url
|
|
38
|
+
|
|
39
|
+
cookie_lang = request.cookies['wovn_selected_lang']
|
|
40
|
+
request_lang = headers.lang_code
|
|
41
|
+
is_get_request = request.get?
|
|
42
|
+
|
|
43
|
+
if @store.settings['use_cookie_lang'] && cookie_lang.present? && request_lang != cookie_lang && request_lang == @store.default_lang && is_get_request
|
|
44
|
+
redirect_headers = headers.redirect(cookie_lang)
|
|
45
|
+
return [302, redirect_headers, ['']]
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# redirect if the path is set to the default language (for SEO purposes)
|
|
49
|
+
if explicit_default_lang?(headers) && is_get_request
|
|
50
|
+
redirect_headers = headers.redirect(default_lang)
|
|
51
|
+
return [307, redirect_headers, ['']]
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# if path containing language code is ignored, do nothing
|
|
55
|
+
if headers.lang_code != default_lang && ignore_path?(headers.unmasked_pathname_without_trailing_slash)
|
|
56
|
+
status, res_headers, body = @app.call(env)
|
|
57
|
+
|
|
58
|
+
return output(headers, status, res_headers, body)
|
|
59
|
+
end
|
|
60
|
+
# pass to application
|
|
61
|
+
status, res_headers, body = @app.call(headers.request_out)
|
|
62
|
+
|
|
63
|
+
# disabled by next Rack middleware
|
|
64
|
+
return output(headers, status, res_headers, body) unless res_headers['Content-Type']&.include?('html')
|
|
65
|
+
|
|
66
|
+
return output(headers, status, res_headers, body) if request.params['wovn_disable'] == true
|
|
67
|
+
|
|
68
|
+
@store.settings.update_dynamic_settings!(request.params)
|
|
69
|
+
return output(headers, status, res_headers, body) if ignore_path?(headers.pathname)
|
|
70
|
+
|
|
71
|
+
body = switch_lang(headers, body, status) unless /^1|302/.match?(status.to_s)
|
|
72
|
+
|
|
73
|
+
content_length = 0
|
|
74
|
+
body.each { |b| content_length += b.respond_to?(:bytesize) ? b.bytesize : 0 }
|
|
75
|
+
res_headers['Content-Length'] = content_length.to_s
|
|
76
|
+
|
|
77
|
+
output(headers, status, res_headers, body)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def switch_lang(headers, body, response_status_code)
|
|
81
|
+
translated_body = []
|
|
82
|
+
|
|
83
|
+
# Must use `.each` for to support multiple-chunks in Sinatra
|
|
84
|
+
string_body = ''
|
|
85
|
+
body.each { |chunk| string_body += chunk }
|
|
86
|
+
html_body = Helpers::NokogumboHelper.parse_html(string_body)
|
|
87
|
+
|
|
88
|
+
if !wovn_ignored?(html_body) && !amp_page?(html_body)
|
|
89
|
+
|
|
90
|
+
html_converter = HtmlConverter.new(html_body, @store, headers, @url_lang_switcher)
|
|
91
|
+
|
|
92
|
+
if needs_api?(html_body, headers)
|
|
93
|
+
converted_html, marker = html_converter.build_api_compatible_html
|
|
94
|
+
translated_content = ApiTranslator.new(@store, headers, WovnLogger.uuid, response_status_code).translate(converted_html)
|
|
95
|
+
translated_body.push(marker.revert(translated_content))
|
|
96
|
+
else
|
|
97
|
+
string_body = html_converter.build if html_body.html?
|
|
98
|
+
translated_body.push(string_body)
|
|
99
|
+
end
|
|
100
|
+
else
|
|
101
|
+
translated_body.push(string_body)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
body.close if body.respond_to?(:close)
|
|
105
|
+
translated_body
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
private
|
|
109
|
+
|
|
110
|
+
def output(headers, status, res_headers, body)
|
|
111
|
+
headers.out(res_headers)
|
|
112
|
+
[status, res_headers, body]
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def needs_api?(html_body, headers)
|
|
116
|
+
headers.lang_code != @store.settings['default_lang'] &&
|
|
117
|
+
(html_body.html? || @store.settings['translate_fragment'])
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def wovn_ignored?(html_body)
|
|
121
|
+
!html_body.xpath('//html[@wovn-ignore or @data-wovn-ignore]').empty?
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def ignore_path?(path)
|
|
125
|
+
@store.settings['ignore_globs'].ignore?(path)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# Checks if a given HTML body is an Accelerated Mobile Page (AMP).
|
|
129
|
+
# To do so, it looks at the required attributes for the HTML tag:
|
|
130
|
+
# https://www.ampproject.org/docs/tutorials/create/basic_markup.
|
|
131
|
+
#
|
|
132
|
+
# @param {Nokogiri::HTML5::Document} body The HTML body to check.
|
|
133
|
+
#
|
|
134
|
+
# @returns {Boolean} True is the HTML body is an AMP, false otherwise.
|
|
135
|
+
def amp_page?(html_body)
|
|
136
|
+
html_attributes = html_body.xpath('//html')[0].try(:attributes) || {}
|
|
137
|
+
|
|
138
|
+
!!(html_attributes['amp'] || html_attributes["\u26A1"])
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def explicit_default_lang?(headers)
|
|
142
|
+
default_lang, url_pattern = @store.settings.values_at('default_lang', 'url_pattern')
|
|
143
|
+
default_lang == headers.url_language && url_pattern != 'custom_domain'
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|