wovnrb 0.2.01 → 0.2.02

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -6
  3. data/README.md +116 -12
  4. data/lib/wovnrb/api_data.rb +59 -0
  5. data/lib/wovnrb/headers.rb +30 -5
  6. data/lib/wovnrb/html_replacers/image_replacer.rb +44 -0
  7. data/lib/wovnrb/html_replacers/link_replacer.rb +18 -0
  8. data/lib/wovnrb/html_replacers/meta_replacer.rb +20 -0
  9. data/lib/wovnrb/html_replacers/replacer_base.rb +17 -0
  10. data/lib/wovnrb/html_replacers/script_replacer.rb +39 -0
  11. data/lib/wovnrb/html_replacers/text_replacer.rb +19 -0
  12. data/lib/wovnrb/lang.rb +133 -23
  13. data/lib/wovnrb/services/url.rb +2 -68
  14. data/lib/wovnrb/services/wovn_logger.rb +45 -0
  15. data/lib/wovnrb/store.rb +19 -38
  16. data/lib/wovnrb/text_caches/cache_base.rb +52 -0
  17. data/lib/wovnrb/text_caches/memory_cache.rb +52 -0
  18. data/lib/wovnrb/version.rb +1 -1
  19. data/lib/wovnrb.rb +19 -224
  20. data/test/lib/api_data_test.rb +62 -0
  21. data/test/lib/headers_test.rb +1221 -1212
  22. data/test/lib/html_replacers/image_replacer_test.rb +86 -0
  23. data/test/lib/html_replacers/link_replacer_test.rb +43 -0
  24. data/test/lib/html_replacers/meta_replacer_test.rb +105 -0
  25. data/test/lib/html_replacers/replacer_base_test.rb +31 -0
  26. data/test/lib/html_replacers/script_replacer_test.rb +90 -0
  27. data/test/lib/html_replacers/text_replacer_test.rb +57 -0
  28. data/test/lib/lang_test.rb +446 -33
  29. data/test/lib/services/wovn_logger_test.rb +40 -0
  30. data/test/lib/store_test.rb +39 -29
  31. data/test/lib/text_caches/cache_base_test.rb +31 -0
  32. data/test/lib/text_caches/memory_cache_test.rb +91 -0
  33. data/test/lib/wovnrb_test.rb +39 -886
  34. data/test/test_helper.rb +96 -1
  35. data/wovnrb.gemspec +4 -0
  36. metadata +89 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1531f2d2d0c565e28d1473918d96f1c4566e3a8a
4
- data.tar.gz: 1de5c991d544fc7578de043f53da32e069d7ae36
3
+ metadata.gz: d1df053b985e8697f9164898024e44c06f61e249
4
+ data.tar.gz: 0a516cb7c4712ba7f52c5a70e767721dba64cc42
5
5
  SHA512:
6
- metadata.gz: 08da2a6e09ca5109e475103ee38d213cfb4a45f6d471c12fa55b4270a7406dadb769b7449c5fb06dee1ca87027f73d6c8ed15a2fe98195516c47c235831c8992
7
- data.tar.gz: f70c5edad9d5ae50b8702c4f3c7d55d4160369d0d8faa1cc0bdbdd373161b11d0bdc47b5c9a88430b3cc1f12a14476fa755200e3dd04dfb3139a4edfd417a8bd
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
- /doc/
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
- # Wovn
1
+ # WOVN.io Ruby Library
2
2
 
3
- TODO: Write a gem description
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
- ## Installation
5
+ This document explains the the process of installing WOVN.io Ruby, as well as set up and configuration.
6
6
 
7
- Add this line to your application's Gemfile:
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
- And then execute:
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
- $ bundle
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
- Or install it yourself as:
121
+ If the default_lang is set to 'en', when receiving a request for the following URL,
18
122
 
19
- $ gem install wovnrb
123
+ https://wovn.io/en/contact
20
124
 
21
- ## Usage
125
+ The library will redirect to the following URL.
22
126
 
23
- TODO: Write usage instructions here
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
@@ -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
- @unmasked_host = @env['HTTP_HOST']
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
- @host = settings['url_pattern'] == 'subdomain' ? remove_lang(@env['HTTP_HOST'], self.lang_code) : @env['HTTP_HOST']
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
- match = "#{@env['SERVER_NAME']}#{@env['REQUEST_URI']}".match(rp)
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
- @env["HTTP_HOST"] = remove_lang(@env["HTTP_HOST"])
134
- @env["SERVER_NAME"] = remove_lang(@env["SERVER_NAME"])
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&currentLang=#{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