wovnrb 0.2.01 → 0.2.02
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +3 -6
- data/README.md +116 -12
- data/lib/wovnrb/api_data.rb +59 -0
- data/lib/wovnrb/headers.rb +30 -5
- data/lib/wovnrb/html_replacers/image_replacer.rb +44 -0
- data/lib/wovnrb/html_replacers/link_replacer.rb +18 -0
- data/lib/wovnrb/html_replacers/meta_replacer.rb +20 -0
- data/lib/wovnrb/html_replacers/replacer_base.rb +17 -0
- data/lib/wovnrb/html_replacers/script_replacer.rb +39 -0
- data/lib/wovnrb/html_replacers/text_replacer.rb +19 -0
- data/lib/wovnrb/lang.rb +133 -23
- data/lib/wovnrb/services/url.rb +2 -68
- data/lib/wovnrb/services/wovn_logger.rb +45 -0
- data/lib/wovnrb/store.rb +19 -38
- data/lib/wovnrb/text_caches/cache_base.rb +52 -0
- data/lib/wovnrb/text_caches/memory_cache.rb +52 -0
- data/lib/wovnrb/version.rb +1 -1
- data/lib/wovnrb.rb +19 -224
- data/test/lib/api_data_test.rb +62 -0
- data/test/lib/headers_test.rb +1221 -1212
- data/test/lib/html_replacers/image_replacer_test.rb +86 -0
- data/test/lib/html_replacers/link_replacer_test.rb +43 -0
- data/test/lib/html_replacers/meta_replacer_test.rb +105 -0
- data/test/lib/html_replacers/replacer_base_test.rb +31 -0
- data/test/lib/html_replacers/script_replacer_test.rb +90 -0
- data/test/lib/html_replacers/text_replacer_test.rb +57 -0
- data/test/lib/lang_test.rb +446 -33
- data/test/lib/services/wovn_logger_test.rb +40 -0
- data/test/lib/store_test.rb +39 -29
- data/test/lib/text_caches/cache_base_test.rb +31 -0
- data/test/lib/text_caches/memory_cache_test.rb +91 -0
- data/test/lib/wovnrb_test.rb +39 -886
- data/test/test_helper.rb +96 -1
- data/wovnrb.gemspec +4 -0
- metadata +89 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d1df053b985e8697f9164898024e44c06f61e249
|
4
|
+
data.tar.gz: 0a516cb7c4712ba7f52c5a70e767721dba64cc42
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 62e2aa38e740242c0ef89c88e076aeaeb9e69d72c9bc0ac2164547165bc531f8a8d39346541530b5ab0e6164213e6ca074e9c3be93da0a3b5a4b39ccb014eac3
|
7
|
+
data.tar.gz: 14ac3a44106d4a114023b91c73aa00740bec2f6c3387861d7002cfee0a8792a73fac3a8f8607620ec2ae62d8915e2d913378ae0890bdfec70e678f796d24dc47
|
data/.gitignore
CHANGED
@@ -1,14 +1,10 @@
|
|
1
1
|
/.bundle/
|
2
|
-
<<<<<<< HEAD
|
3
2
|
/vendor
|
4
|
-
|
5
|
-
/vendor/bundle/
|
6
|
-
>>>>>>> 78ccf8410c19bdee508e96a2386a5273abfad0ba
|
7
|
-
/.yardoc
|
3
|
+
.yardoc/
|
8
4
|
/Gemfile.lock
|
9
5
|
/_yardoc/
|
10
6
|
/coverage/
|
11
|
-
|
7
|
+
doc/
|
12
8
|
/pkg/
|
13
9
|
/spec/reports/
|
14
10
|
/tmp/
|
@@ -19,4 +15,5 @@
|
|
19
15
|
*.swp
|
20
16
|
mkmf.log
|
21
17
|
.idea/
|
18
|
+
*.iml
|
22
19
|
.ruby-version
|
data/README.md
CHANGED
@@ -1,28 +1,132 @@
|
|
1
|
-
#
|
1
|
+
# WOVN.io Ruby Library
|
2
2
|
|
3
|
-
|
3
|
+
The WOVN.io Ruby library is a library that uses WOVN.io in order to provide translations. The WOVN.io Ruby Library is packaged as Rack Middleware.
|
4
4
|
|
5
|
-
|
5
|
+
This document explains the the process of installing WOVN.io Ruby, as well as set up and configuration.
|
6
6
|
|
7
|
-
|
7
|
+
## 1. Install
|
8
|
+
|
9
|
+
### 1.1. Creating a WOVN.io account.
|
10
|
+
|
11
|
+
In order to use the WOVN.io Ruby Library, you need a WOVN.io account. If you do not have an account, please first sign up for one at WOVN.io.
|
12
|
+
|
13
|
+
### 1.2. Adding a Page
|
14
|
+
|
15
|
+
After logging into WOVN.io, add a page you would like translated.
|
16
|
+
|
17
|
+
### 1.3. Ruby Application Settings
|
18
|
+
|
19
|
+
To use the WOVN.io Ruby Library, insert the following line into your Ruby Application's Gemfile. WOVN.io currently supports version 0.2 and up.
|
8
20
|
|
9
21
|
```ruby
|
10
|
-
gem 'wovnrb'
|
22
|
+
gem 'wovnrb', '>= 0.2'
|
23
|
+
```
|
24
|
+
|
25
|
+
After setting the above, execute the following command to install the WOVN.io Ruby Library.
|
26
|
+
|
27
|
+
```bash
|
28
|
+
bundle install
|
29
|
+
```
|
30
|
+
|
31
|
+
After installing the library, insert the following into your Ruby Application's settings file.
|
32
|
+
|
33
|
+
* If you're using Ruby on Rails
|
34
|
+
|
35
|
+
Insert the following into either config/application.rb or config/environments/.
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
...
|
39
|
+
|
40
|
+
config.wovnrb = {
|
41
|
+
:user_token => '2Wle3',
|
42
|
+
:secret_key => 'secret',
|
43
|
+
}
|
44
|
+
|
45
|
+
...
|
46
|
+
```
|
47
|
+
|
48
|
+
* If you're using Sinatra
|
49
|
+
|
50
|
+
Insert the following into either the Application File or config.ru.
|
51
|
+
|
52
|
+
```ruby
|
53
|
+
...
|
54
|
+
|
55
|
+
require 'wovnrb'
|
56
|
+
|
57
|
+
use Wovnrb::Interceptor, {
|
58
|
+
:user_token => '2Wle3',
|
59
|
+
:secret_key => 'secret',
|
60
|
+
}
|
61
|
+
|
62
|
+
...
|
11
63
|
```
|
12
64
|
|
13
|
-
|
65
|
+
After completing setup, start the Ruby Application, and make sure the WOVN.io library is working correctly.
|
66
|
+
|
67
|
+
## 2. Parameter Setting
|
68
|
+
|
69
|
+
WOVN.io Ruby Library's valid parameters are as follows.
|
70
|
+
|
71
|
+
Parameter Name | Required | Default Setting
|
72
|
+
-------------- | -------- | ----------------
|
73
|
+
user_token | yes | ''
|
74
|
+
secret_key | yes | ''
|
75
|
+
url_pattern | yes | 'path'
|
76
|
+
query | | []
|
77
|
+
default_lang | yes | 'en'
|
78
|
+
|
79
|
+
### 2.1. user_token
|
80
|
+
|
81
|
+
Set your WOVN.io Account's user token. This parameter is required.
|
82
|
+
|
83
|
+
### 2.2. secret_key
|
84
|
+
|
85
|
+
This parameter is in development; it is not currently used. However, it is a required parameter, so ensure to set a value for it.
|
86
|
+
|
87
|
+
### 2.3. url_pattern
|
88
|
+
|
89
|
+
The Library works in the Ruby Application by adding new URL's to be translated. You can set the type of url with the url_pattern parameter. There are 3 types that can be set.
|
90
|
+
|
91
|
+
parameters | Translated page's URL | Notes
|
92
|
+
----------- | ------------------------------- | -------
|
93
|
+
'path' | https://wovn.io/ja/contact | Default Value. If no settings have been set, url_pattern defaults to this value.
|
94
|
+
'subdomain' | https://ja.wovn.io/contact | DNS settings must be set.
|
95
|
+
'query' | https://wovn.io/contact?wovn=ja | The least amount of changes to the application required to complete setup.
|
96
|
+
|
97
|
+
※ The following is an example of a URL that has been translated by the library using the above URL's.
|
98
|
+
|
99
|
+
https://wovn.io/contact
|
100
|
+
|
101
|
+
### 2.4. query
|
102
|
+
|
103
|
+
Using the query parameter, you can setup query parameters you wish to be ignored within the URL when translating via WOVN.io. There is no default value, in this case, all query parameters are included within the translated page's URL.
|
104
|
+
|
105
|
+
https://wovn.io/ja/contact?os=mac&keyboard=us
|
106
|
+
|
107
|
+
If the defualt_lang is 'en', and the query is set to 'os=mac&keyboard=us', the above URL will be modified into the following URL to search for the page's translation.
|
108
|
+
|
109
|
+
https://wovn.io/contact?os=mac&keyboard=us
|
110
|
+
|
111
|
+
If the default_lang is 'en', and the query is set to 'os=mac', the above URL will be modified into the following URL to search for the page's translation.
|
112
|
+
|
113
|
+
https://wovn.io/contact?os=mac
|
114
|
+
|
115
|
+
### 2.5. default_lang
|
116
|
+
|
117
|
+
This sets the Ruby application's default language. The default value is English ('en').
|
14
118
|
|
15
|
-
|
119
|
+
If a requested page, where the default language's parameter is included in the URL, the request is redirected before translating. The default_lang parameter is used for this purpose.
|
16
120
|
|
17
|
-
|
121
|
+
If the default_lang is set to 'en', when receiving a request for the following URL,
|
18
122
|
|
19
|
-
|
123
|
+
https://wovn.io/en/contact
|
20
124
|
|
21
|
-
|
125
|
+
The library will redirect to the following URL.
|
22
126
|
|
23
|
-
|
127
|
+
https://wovn.io/contact
|
24
128
|
|
25
|
-
## Contributing
|
129
|
+
## 3. Contributing
|
26
130
|
|
27
131
|
1. Fork it ( https://github.com/[my-github-username]/wovnrb/fork )
|
28
132
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Wovnrb
|
2
|
+
class ApiData
|
3
|
+
def initialize(access_url, store)
|
4
|
+
@access_url = access_url.gsub(/\/$/, '')
|
5
|
+
@store = store
|
6
|
+
end
|
7
|
+
|
8
|
+
def get_data
|
9
|
+
cache_key = to_key(@access_url)
|
10
|
+
data = get_data_value(cache_key)
|
11
|
+
JSON.parse(data)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
def get_data_value(cache_key)
|
16
|
+
cache_value = CacheBase.get_single.get(cache_key)
|
17
|
+
return cache_value if cache_value
|
18
|
+
|
19
|
+
uri = build_api_uri
|
20
|
+
begin
|
21
|
+
response = get_from_api_server(uri)
|
22
|
+
rescue => e
|
23
|
+
response = '{}'
|
24
|
+
WovnLogger.instance.error("API server GET request failed :\nurl: #{uri}\n#{e.message}")
|
25
|
+
end
|
26
|
+
|
27
|
+
# Always cache response, even when error returns to avoid DDOS
|
28
|
+
CacheBase.get_single.put(cache_key, response)
|
29
|
+
response
|
30
|
+
end
|
31
|
+
|
32
|
+
@@cache_prefix = 'api::cache::'
|
33
|
+
def to_key(url)
|
34
|
+
"#{@@cache_prefix}#{url}"
|
35
|
+
end
|
36
|
+
|
37
|
+
def build_api_uri
|
38
|
+
t = CGI::escape(@store.settings['user_token'])
|
39
|
+
u = CGI::escape(@access_url)
|
40
|
+
URI.parse("#{@store.settings['api_url']}?token=#{t}&url=#{u}")
|
41
|
+
end
|
42
|
+
|
43
|
+
def get_from_api_server(uri)
|
44
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
45
|
+
http.use_ssl = true if uri.scheme == 'https'
|
46
|
+
http.open_timeout = @store.settings['api_timeout_seconds']
|
47
|
+
http.read_timeout= @store.settings['api_timeout_seconds']
|
48
|
+
response = http.start {
|
49
|
+
http.get(uri.request_uri)
|
50
|
+
}
|
51
|
+
|
52
|
+
if response.code == '200'
|
53
|
+
response.body
|
54
|
+
else
|
55
|
+
raise "Response Code is not success: #{response.code}"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/wovnrb/headers.rb
CHANGED
@@ -10,11 +10,18 @@ module Wovnrb
|
|
10
10
|
attr_reader :pathname
|
11
11
|
attr_reader :redis_url
|
12
12
|
|
13
|
+
# Generates new instance of Wovnrb::Headers.
|
14
|
+
# Its parameters are set by parsing env variable.
|
15
|
+
#
|
13
16
|
def initialize(env, settings)
|
14
17
|
@env = env
|
15
18
|
@settings = settings
|
16
19
|
@protocol = @env['rack.url_scheme']
|
17
|
-
|
20
|
+
if settings['use_proxy'] && @env.has_key?('HTTP_X_FORWARDED_HOST')
|
21
|
+
@unmasked_host = @env['HTTP_X_FORWARDED_HOST']
|
22
|
+
else
|
23
|
+
@unmasked_host = @env['HTTP_HOST']
|
24
|
+
end
|
18
25
|
unless @env.has_key?('REQUEST_URI')
|
19
26
|
# Add '/' to PATH_INFO as a possible fix for pow server
|
20
27
|
@env['REQUEST_URI'] = (@env['PATH_INFO'] =~ /^[^\/]/ ? '/' : '') + @env['PATH_INFO'] + (@env['QUERY_STRING'].size == 0 ? '' : "?#{@env['QUERY_STRING']}")
|
@@ -27,7 +34,12 @@ module Wovnrb
|
|
27
34
|
@unmasked_pathname = @env['REQUEST_URI'].split('?')[0]
|
28
35
|
@unmasked_pathname += '/' unless @unmasked_pathname =~ /\/$/ || @unmasked_pathname =~ /\/[^\/.]+\.[^\/.]+$/
|
29
36
|
@unmasked_url = "#{@protocol}://#{@unmasked_host}#{@unmasked_pathname}"
|
30
|
-
|
37
|
+
if settings['use_proxy'] && @env.has_key?('HTTP_X_FORWARDED_HOST')
|
38
|
+
@host = @env['HTTP_X_FORWARDED_HOST']
|
39
|
+
else
|
40
|
+
@host = @env['HTTP_HOST']
|
41
|
+
end
|
42
|
+
@host = settings['url_pattern'] == 'subdomain' ? remove_lang(@host, self.lang_code) : @host
|
31
43
|
@pathname, @query = @env['REQUEST_URI'].split('?')
|
32
44
|
@pathname = settings['url_pattern'] == 'path' ? remove_lang(@pathname, self.lang_code) : @pathname
|
33
45
|
@query = @query || ''
|
@@ -59,10 +71,19 @@ module Wovnrb
|
|
59
71
|
(self.path_lang && self.path_lang.length > 0) ? self.path_lang : @settings['default_lang']
|
60
72
|
end
|
61
73
|
|
74
|
+
# picks up language code from requested URL by using url_pattern_reg setting.
|
75
|
+
# when language code is invalid, this method returns empty string.
|
76
|
+
# if you want examples, please see test/lib/headers_test.rb.
|
77
|
+
#
|
78
|
+
# @return [String] language code in requrested URL.
|
62
79
|
def path_lang
|
63
80
|
if @path_lang.nil?
|
64
81
|
rp = Regexp.new(@settings['url_pattern_reg'])
|
65
|
-
|
82
|
+
if @settings['use_proxy'] && @env.has_key?('HTTP_X_FORWARDED_HOST')
|
83
|
+
match = "#{@env['HTTP_X_FORWARDED_HOST']}#{@env['REQUEST_URI']}".match(rp)
|
84
|
+
else
|
85
|
+
match = "#{@env['SERVER_NAME']}#{@env['REQUEST_URI']}".match(rp)
|
86
|
+
end
|
66
87
|
if match && match[:lang] && Lang.get_lang(match[:lang])
|
67
88
|
@path_lang = Lang.get_code(match[:lang])
|
68
89
|
else
|
@@ -130,8 +151,12 @@ module Wovnrb
|
|
130
151
|
@env['QUERY_STRING'] = remove_lang(@env['QUERY_STRING']) if @env.has_key?('QUERY_STRING')
|
131
152
|
@env['ORIGINAL_FULLPATH'] = remove_lang(@env['ORIGINAL_FULLPATH']) if @env.has_key?('ORIGINAL_FULLPATH')
|
132
153
|
when 'subdomain'
|
133
|
-
@
|
134
|
-
|
154
|
+
if @settings['use_proxy'] && @env.has_key?('HTTP_X_FORWARDED_HOST')
|
155
|
+
@env['HTTP_X_FORWARDED_HOST'] = remove_lang(@env['HTTP_X_FORWARDED_HOST'])
|
156
|
+
else
|
157
|
+
@env["HTTP_HOST"] = remove_lang(@env["HTTP_HOST"])
|
158
|
+
@env["SERVER_NAME"] = remove_lang(@env["SERVER_NAME"])
|
159
|
+
end
|
135
160
|
if @env.has_key?('HTTP_REFERER')
|
136
161
|
@env["HTTP_REFERER"] = remove_lang(@env["HTTP_REFERER"])
|
137
162
|
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Wovnrb
|
2
|
+
class ImageReplacer < ReplacerBase
|
3
|
+
def initialize(url, text_index, src_index, img_src_prefix)
|
4
|
+
@url = url
|
5
|
+
@text_index = text_index
|
6
|
+
@src_index = src_index
|
7
|
+
@img_src_prefix = img_src_prefix
|
8
|
+
end
|
9
|
+
|
10
|
+
def replace(dom, lang)
|
11
|
+
dom.xpath('//img').each do |node|
|
12
|
+
next if wovn_ignore?(node)
|
13
|
+
|
14
|
+
# use regular expressions to support case insensitivity (right?)
|
15
|
+
if node.to_html =~ /src=['"][^'"]*['"]/i
|
16
|
+
src = node.to_html.match(/src=['"]([^'"]*)['"]/i)[1]
|
17
|
+
# THIS SRC CORRECTION DOES NOT HANDLE ONE IMPORTANT CASE
|
18
|
+
# 1) "../path/with/ellipse"
|
19
|
+
# if this is not an absolute src
|
20
|
+
if src !~ /:\/\//
|
21
|
+
# if this is a path with a leading slash
|
22
|
+
if src =~ /^\//
|
23
|
+
src = "#{@url[:protocol]}://#{@url[:host]}#{src}"
|
24
|
+
else
|
25
|
+
src = "#{@url[:protocol]}://#{@url[:host]}#{@url[:path]}#{src}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# shouldn't need size check, but for now...
|
30
|
+
if @src_index[src] && @src_index[src][lang.lang_code] && @src_index[src][lang.lang_code].size > 0
|
31
|
+
node.attribute('src').value = "#{@img_src_prefix}#{@src_index[src][lang.lang_code][0]['data']}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
if node.get_attribute('alt')
|
36
|
+
alt = node.get_attribute('alt').strip
|
37
|
+
if @text_index[alt] && @text_index[alt][lang.lang_code] && @text_index[alt][lang.lang_code].size > 0
|
38
|
+
node.attribute('alt').value = alt.gsub(/^(\s*)[\S\s]*(\s*)$/, '\1' + @text_index[alt][lang.lang_code][0]['data'] + '\2')
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Wovnrb
|
2
|
+
class LinkReplacer < ReplacerBase
|
3
|
+
def initialize(pattern, headers)
|
4
|
+
@pattern = pattern
|
5
|
+
@headers = headers
|
6
|
+
end
|
7
|
+
|
8
|
+
def replace(dom, lang)
|
9
|
+
dom.xpath('//a').each do |node|
|
10
|
+
next if wovn_ignore?(node)
|
11
|
+
|
12
|
+
href = node.get_attribute('href')
|
13
|
+
new_href = lang.add_lang_code(href, @pattern, @headers)
|
14
|
+
node.set_attribute('href', new_href)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Wovnrb
|
2
|
+
class MetaReplacer < ReplacerBase
|
3
|
+
def initialize(text_index)
|
4
|
+
@text_index = text_index
|
5
|
+
end
|
6
|
+
|
7
|
+
def replace(dom, lang)
|
8
|
+
dom.xpath('//meta').select { |node|
|
9
|
+
next if wovn_ignore?(node)
|
10
|
+
(node.get_attribute('name') || node.get_attribute('property') || '') =~ /^(description|title|og:title|og:description|twitter:title|twitter:description)$/
|
11
|
+
}.each do |node|
|
12
|
+
node_content = node.get_attribute('content').strip
|
13
|
+
# shouldn't need size check, but for now...
|
14
|
+
if @text_index[node_content] && @text_index[node_content][lang.lang_code] && @text_index[node_content][lang.lang_code].size > 0
|
15
|
+
node.set_attribute('content', node.get_attribute('content').gsub(/^(\s*)[\S\s]*?(\s*)$/, '\1' + @text_index[node_content][lang.lang_code][0]['data'] + '\2'))
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Wovnrb
|
2
|
+
class ReplacerBase
|
3
|
+
def replace(dom, lang)
|
4
|
+
raise NotImplementedError.new('replace is not defined')
|
5
|
+
end
|
6
|
+
|
7
|
+
protected
|
8
|
+
def wovn_ignore?(node)
|
9
|
+
if !node.get_attribute('wovn-ignore').nil?
|
10
|
+
return true
|
11
|
+
elsif node.name === 'html'
|
12
|
+
return false
|
13
|
+
end
|
14
|
+
wovn_ignore?(node.parent)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Wovnrb
|
2
|
+
class ScriptReplacer < ReplacerBase
|
3
|
+
def initialize(store)
|
4
|
+
@store = store
|
5
|
+
end
|
6
|
+
|
7
|
+
def replace(dom, lang)
|
8
|
+
remove_embed_wovn_script(dom)
|
9
|
+
add_wovn_script(dom, lang)
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
def remove_embed_wovn_script(dom)
|
14
|
+
dom.xpath('//script').each do |script_node|
|
15
|
+
if script_node['src'] && script_node['src'] =~ /^\/\/j.(dev-)?wovn.io(:3000)?\//
|
16
|
+
script_node.remove
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def add_wovn_script(dom, lang)
|
22
|
+
parent_node = dom.at_css('head') || dom.at_css('body') || dom.at_css('html')
|
23
|
+
|
24
|
+
# INSERT BACKEND WIDGET
|
25
|
+
insert_node = Nokogiri::XML::Node.new('script', dom)
|
26
|
+
insert_node['src'] = '//j.wovn.io/1'
|
27
|
+
insert_node['async'] = true
|
28
|
+
version = defined?(VERSION) ? VERSION : ''
|
29
|
+
insert_node['data-wovnio'] = "key=#{@store.settings['user_token']}&backend=true¤tLang=#{lang.lang_code}&defaultLang=#{@store.settings['default_lang']}&urlPattern=#{@store.settings['url_pattern']}&version=#{version}"
|
30
|
+
# do this so that there will be a closing tag (better compatibility with browsers)
|
31
|
+
insert_node.content = ' '
|
32
|
+
if parent_node.children.size > 0
|
33
|
+
parent_node.children.first.add_previous_sibling(insert_node)
|
34
|
+
else
|
35
|
+
parent_node.add_child(insert_node)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Wovnrb
|
2
|
+
class TextReplacer < ReplacerBase
|
3
|
+
def initialize(text_index)
|
4
|
+
@text_index = text_index
|
5
|
+
end
|
6
|
+
|
7
|
+
def replace(dom, lang)
|
8
|
+
dom.xpath('//text()').each do |node|
|
9
|
+
next if wovn_ignore?(node)
|
10
|
+
|
11
|
+
node_text = node.content.strip
|
12
|
+
# shouldn't need size check, but for now...
|
13
|
+
if @text_index[node_text] && @text_index[node_text][lang.lang_code] && @text_index[node_text][lang.lang_code].size > 0
|
14
|
+
node.content = node.content.gsub(/^(\s*)[\S\s]*?(\s*)$/, '\1' + @text_index[node_text][lang.lang_code][0]['data'] + '\2')
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/wovnrb/lang.rb
CHANGED
@@ -30,29 +30,6 @@ module Wovnrb
|
|
30
30
|
'tr' => {name: 'Türkçe', code: 'tr', en: 'Turkish'},
|
31
31
|
'uk' => {name: 'Українська', code: 'uk', en: 'Ukrainian'},
|
32
32
|
'vi' => {name: 'Tiếng Việt', code: 'vi', en: 'Vietnamese'},
|
33
|
-
|
34
|
-
=begin
|
35
|
-
* denotes no Google support
|
36
|
-
*{name: 'Urdu', code: 'ur', en: 'Urdu'},
|
37
|
-
{name: 'Català', code: 'ca'},
|
38
|
-
{name: 'Čeština', code: 'cs'},
|
39
|
-
{name: 'Български', code: 'bg'},
|
40
|
-
{name: 'Estonian', code: 'et'},
|
41
|
-
*{name: 'Haitian Creoloe', code: 'ht'},
|
42
|
-
*{name: 'Hmong Daw', code: 'mww'},
|
43
|
-
{name: 'Hungarian', code: 'hu'},
|
44
|
-
*{name: 'Klingon', code: 'tlh'},
|
45
|
-
*{name: 'Klingon (plqaD)', code: 'tlh-Qaak'},
|
46
|
-
{name: 'Latvian', code: 'lv'},
|
47
|
-
{name: 'Lithuanian', code: 'lt'},
|
48
|
-
{name: 'Maltese', code: 'mt'},
|
49
|
-
{name: 'Persian', code: 'fa'},
|
50
|
-
{name: 'Romanian', code: 'ro'},
|
51
|
-
{name: 'Slovak', code: 'sk'},
|
52
|
-
{name: 'Slovenian', code: 'sl'},
|
53
|
-
{name: 'Ukranian', code: 'uk'},
|
54
|
-
{name: 'Welsh', code: 'cy'},
|
55
|
-
=end
|
56
33
|
}
|
57
34
|
|
58
35
|
def self.get_code(lang_name)
|
@@ -71,5 +48,138 @@ module Wovnrb
|
|
71
48
|
return LANG[lang_code]
|
72
49
|
end
|
73
50
|
|
51
|
+
def initialize(lang_name)
|
52
|
+
@lang_code = Lang.get_code(lang_name)
|
53
|
+
end
|
54
|
+
|
55
|
+
def lang_code
|
56
|
+
@lang_code
|
57
|
+
end
|
58
|
+
|
59
|
+
# Adds language code to URL in "href" variable by "pattern" variable and own @lang_code.
|
60
|
+
# When @lang_code is 'ja', add_lang_code('https://wovn.io', 'path', headers) returns 'https://wovn.io/ja/'.
|
61
|
+
# If you want to know more examples, see also test/lib/lang_test.rb.
|
62
|
+
#
|
63
|
+
# @param [String] href original URL.
|
64
|
+
# @param [String] pattern url_pattern of the settings. ('path', 'subdomain' or 'query')
|
65
|
+
# @param [Wovnrb::Header] headers instance of Wovn::Header. It generates new env variable for original request.
|
66
|
+
# @return [String] URL added langauge code.
|
67
|
+
def add_lang_code(href, pattern, headers)
|
68
|
+
return href if href =~ /^(#.*)?$/
|
69
|
+
# absolute links
|
70
|
+
new_href = href
|
71
|
+
if href && href =~ /^(https?:)?\/\//i
|
72
|
+
# in the future, perhaps validate url rather than using begin rescue
|
73
|
+
# "#{url =~ /\// ? 'http:' : ''}#{url}" =~ URI::regexp
|
74
|
+
begin
|
75
|
+
uri = URI(href)
|
76
|
+
rescue
|
77
|
+
return new_href
|
78
|
+
end
|
79
|
+
# only add lang if it's an internal link
|
80
|
+
# DNS names are case insensitive
|
81
|
+
if uri.host.downcase === headers.host.downcase
|
82
|
+
case pattern
|
83
|
+
when 'subdomain'
|
84
|
+
sub_d = href.match(/\/\/([^\.]*)\./)[1]
|
85
|
+
sub_code = Lang.get_code(sub_d)
|
86
|
+
if sub_code && sub_code.downcase == @lang_code.downcase
|
87
|
+
new_href = href.sub(Regexp.new(@lang_code, 'i'), @lang_code.downcase)
|
88
|
+
else
|
89
|
+
new_href = href.sub(/(\/\/)([^\.]*)/, '\1' + @lang_code.downcase + '.' + '\2')
|
90
|
+
end
|
91
|
+
when 'query'
|
92
|
+
new_href = href =~ /\?/ ? href + '&wovn=' + @lang_code : href + '?wovn=' + @lang_code
|
93
|
+
else # path
|
94
|
+
new_href = href.sub(/([^\.]*\.[^\/]*)(\/|$)/, '\1/' + @lang_code + '/')
|
95
|
+
end
|
96
|
+
end
|
97
|
+
elsif href
|
98
|
+
case pattern
|
99
|
+
when 'subdomain'
|
100
|
+
lang_url = headers.protocol + '://' + @lang_code.downcase + '.' + headers.host
|
101
|
+
current_dir = headers.pathname.sub(/[^\/]*\.[^\.]{2,6}$/, '')
|
102
|
+
if href =~ /^\.\..*$/
|
103
|
+
# ../path
|
104
|
+
new_href = lang_url + '/' + href.gsub(/^\.\.\//, '')
|
105
|
+
elsif href =~ /^\..*$/
|
106
|
+
# ./path
|
107
|
+
new_href = lang_url + current_dir + '/' + href.gsub(/^\.\//, '')
|
108
|
+
elsif href =~ /^\/.*$/
|
109
|
+
# /path
|
110
|
+
new_href = lang_url + href
|
111
|
+
else
|
112
|
+
# path
|
113
|
+
new_href = lang_url + current_dir + '/' + href
|
114
|
+
end
|
115
|
+
when 'query'
|
116
|
+
new_href = href =~ /\?/ ? href + '&wovn=' + @lang_code : href + '?wovn=' + @lang_code
|
117
|
+
else # path
|
118
|
+
if href =~ /^\//
|
119
|
+
new_href = '/' + @lang_code + href
|
120
|
+
else
|
121
|
+
current_dir = headers.pathname.sub(/[^\/]*\.[^\.]{2,6}$/, '')
|
122
|
+
current_dir = '/' if current_dir == ''
|
123
|
+
new_href = '/' + @lang_code + current_dir + href
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
new_href
|
128
|
+
end
|
129
|
+
|
130
|
+
def switch_dom_lang(dom, store, values, url, headers)
|
131
|
+
replace_dom_values(dom, values, store, url, headers)
|
132
|
+
|
133
|
+
# INSERT LANGUAGE METALINKS
|
134
|
+
parent_node = dom.at_css('head') || dom.at_css('body') || dom.at_css('html')
|
135
|
+
published_langs = get_langs(values)
|
136
|
+
published_langs.each do |l|
|
137
|
+
insert_node = Nokogiri::XML::Node.new('link', dom)
|
138
|
+
insert_node['rel'] = 'alternate'
|
139
|
+
insert_node['hreflang'] = l
|
140
|
+
insert_node['href'] = headers.redirect_location(l)
|
141
|
+
parent_node.add_child(insert_node)
|
142
|
+
end
|
143
|
+
|
144
|
+
# set lang property on HTML tag
|
145
|
+
if dom.at_css('html') || dom.at_css('HTML')
|
146
|
+
(dom.at_css('html') || dom.at_css('HTML')).set_attribute('lang', @lang_code)
|
147
|
+
end
|
148
|
+
|
149
|
+
dom.to_html.gsub(/href="([^"]*)"/) { |m| "href=\"#{URI.decode($1)}\"" }
|
150
|
+
end
|
151
|
+
|
152
|
+
private
|
153
|
+
def replace_dom_values(dom, values, store, url, headers)
|
154
|
+
text_index = values['text_vals'] || {}
|
155
|
+
src_index = values['img_vals'] || {}
|
156
|
+
img_src_prefix = values['img_src_prefix'] || ''
|
157
|
+
|
158
|
+
replacers = []
|
159
|
+
# add lang code to anchors href if not default lang
|
160
|
+
if @lang_code != store.settings['default_lang']
|
161
|
+
pattern = store.settings['url_pattern']
|
162
|
+
replacers << LinkReplacer.new(pattern, headers)
|
163
|
+
end
|
164
|
+
|
165
|
+
replacers << TextReplacer.new(text_index)
|
166
|
+
replacers << MetaReplacer.new(text_index)
|
167
|
+
replacers << ImageReplacer.new(url, text_index, src_index, img_src_prefix)
|
168
|
+
replacers << ScriptReplacer.new(store)
|
169
|
+
|
170
|
+
replacers.each do |replacer|
|
171
|
+
replacer.replace(dom, self)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def get_langs(values)
|
176
|
+
langs = Set.new
|
177
|
+
(values['text_vals'] || {}).merge(values['img_vals'] || {}).each do |key, index|
|
178
|
+
index.each do |l, val|
|
179
|
+
langs.add(l)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
langs
|
183
|
+
end
|
74
184
|
end
|
75
185
|
end
|