i18n_feedback 0.1.0 → 0.2.1
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/CHANGELOG.md +13 -0
- data/README.md +2 -2
- data/app/assets/i18n_feedback/widget.js +18 -6
- data/app/helpers/i18n_feedback/tag_helper.rb +2 -1
- data/lib/i18n_feedback/middleware.rb +15 -3
- data/lib/i18n_feedback/version.rb +1 -1
- data/lib/i18n_feedback/widget.rb +7 -4
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: fb6db4ee347586bcb037eaeedb37564d969e9d1395db2a8b6ca450710d943796
|
|
4
|
+
data.tar.gz: 4d9aeaa1a8ece352719e3019ae08049b7ad9103dc1ce61447e317149269451d3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 48c4b5ac039436c560d5e667d7ed36f1474a784b1330e8efaea55376e306eecd0865d6ddb81fa8001cc20d1c790bfdb0668b8435322f3a8f13aca95b02a5a99e
|
|
7
|
+
data.tar.gz: cde3e82b0c34baf896c2d54c887bf581f48c8d00e65a74fe34d11e531b5264ed85c2267d69ab94c1715bd2abf5ba9ad102c22b746ec6f6d36721a61bf4b202d4
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.2.1]
|
|
6
|
+
|
|
7
|
+
- Keep the suggest pill and active-mode highlighting working across Turbo Drive
|
|
8
|
+
navigations. Turbo replaces `<body>` on each visit, which removed the pill; the
|
|
9
|
+
widget now re-runs its per-page setup on `turbo:load` instead of only on the
|
|
10
|
+
initial load, so it no longer requires a hard reload.
|
|
11
|
+
|
|
12
|
+
## [0.2.0]
|
|
13
|
+
|
|
14
|
+
- Stamp the injected widget scripts with the request's Content-Security-Policy
|
|
15
|
+
nonce when one is present, so the tool works under a nonce-based CSP (including
|
|
16
|
+
`strict-dynamic`). No-op for apps without a CSP nonce.
|
|
17
|
+
|
|
5
18
|
## [0.1.0]
|
|
6
19
|
|
|
7
20
|
- Initial release.
|
data/README.md
CHANGED
|
@@ -35,11 +35,11 @@ It is meant for development and staging, never production.
|
|
|
35
35
|
|
|
36
36
|
## Installation
|
|
37
37
|
|
|
38
|
-
Add the gem
|
|
38
|
+
Add the gem:
|
|
39
39
|
|
|
40
40
|
```ruby
|
|
41
41
|
# Gemfile
|
|
42
|
-
gem "i18n_feedback"
|
|
42
|
+
gem "i18n_feedback"
|
|
43
43
|
```
|
|
44
44
|
|
|
45
45
|
```bash
|
|
@@ -42,18 +42,27 @@
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
ready(function () {
|
|
45
|
-
|
|
46
|
-
if (config.showPill !== false) buildPill();
|
|
45
|
+
// Document-level listeners survive Turbo navigations, so register them once.
|
|
47
46
|
document.addEventListener("keydown", handleKeydown);
|
|
47
|
+
if (config.active) document.addEventListener("click", handleClick, true);
|
|
48
|
+
|
|
49
|
+
// Everything else lives in <body>, which Turbo replaces on every visit —
|
|
50
|
+
// taking the pill and the active-mode highlighting with it. Re-run the
|
|
51
|
+
// per-page setup on each visit so the widget keeps working without a hard
|
|
52
|
+
// reload. render() also runs now for the initial (or non-Turbo) load.
|
|
53
|
+
render();
|
|
54
|
+
document.addEventListener("turbo:load", render);
|
|
55
|
+
document.addEventListener("turbo:frame-load", strip);
|
|
56
|
+
});
|
|
48
57
|
|
|
58
|
+
function render() {
|
|
59
|
+
injectStyles();
|
|
60
|
+
if (config.showPill !== false) buildPill();
|
|
49
61
|
if (config.active) {
|
|
50
62
|
document.documentElement.classList.add("i18nf-active");
|
|
51
|
-
document.addEventListener("click", handleClick, true);
|
|
52
|
-
document.addEventListener("turbo:load", strip);
|
|
53
|
-
document.addEventListener("turbo:frame-load", strip);
|
|
54
63
|
strip();
|
|
55
64
|
}
|
|
56
|
-
}
|
|
65
|
+
}
|
|
57
66
|
|
|
58
67
|
// --- suggest-mode toggle --------------------------------------------------
|
|
59
68
|
|
|
@@ -123,6 +132,9 @@
|
|
|
123
132
|
// --- pill -----------------------------------------------------------------
|
|
124
133
|
|
|
125
134
|
function buildPill() {
|
|
135
|
+
var existing = document.querySelector(".i18nf-pill");
|
|
136
|
+
if (existing) existing.remove();
|
|
137
|
+
|
|
126
138
|
var pill = document.createElement("button");
|
|
127
139
|
pill.type = "button";
|
|
128
140
|
pill.className = "i18nf-pill" + (config.active ? " i18nf-pill-on" : "");
|
|
@@ -11,7 +11,8 @@ module I18nFeedback
|
|
|
11
11
|
Widget.snippet(
|
|
12
12
|
endpoint: I18nFeedback.config.suggestions_endpoint,
|
|
13
13
|
locale: I18n.locale,
|
|
14
|
-
active: I18nFeedback::Marking.enabled
|
|
14
|
+
active: I18nFeedback::Marking.enabled?,
|
|
15
|
+
nonce: (content_security_policy_nonce if respond_to?(:content_security_policy_nonce))
|
|
15
16
|
).html_safe
|
|
16
17
|
end
|
|
17
18
|
end
|
|
@@ -30,7 +30,7 @@ module I18nFeedback
|
|
|
30
30
|
headers = persist_choice(headers, override) if available && !override.nil?
|
|
31
31
|
|
|
32
32
|
if available && I18nFeedback.config.auto_inject && html?(headers) && !request.xhr?
|
|
33
|
-
status, headers, body = inject(status, headers, body, marking)
|
|
33
|
+
status, headers, body = inject(status, headers, body, marking, csp_nonce(env))
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
[status, headers, body]
|
|
@@ -44,6 +44,17 @@ module I18nFeedback
|
|
|
44
44
|
!request.cookies[COOKIE].to_s.empty?
|
|
45
45
|
end
|
|
46
46
|
|
|
47
|
+
# The request's CSP nonce, so the injected scripts run under a nonce-based
|
|
48
|
+
# Content-Security-Policy. Reads the value Rails memoizes on the env, which is
|
|
49
|
+
# the same nonce the CSP header uses. nil when the app sets no nonce.
|
|
50
|
+
def csp_nonce(env)
|
|
51
|
+
return nil unless defined?(ActionDispatch::Request)
|
|
52
|
+
|
|
53
|
+
ActionDispatch::Request.new(env).content_security_policy_nonce
|
|
54
|
+
rescue StandardError
|
|
55
|
+
nil
|
|
56
|
+
end
|
|
57
|
+
|
|
47
58
|
def toggle_override(request)
|
|
48
59
|
param = I18nFeedback.config.toggle_param
|
|
49
60
|
return nil unless request.params.key?(param)
|
|
@@ -80,7 +91,7 @@ module I18nFeedback
|
|
|
80
91
|
headers['Content-Type'] || headers['content-type']
|
|
81
92
|
end
|
|
82
93
|
|
|
83
|
-
def inject(status, headers, body, marking)
|
|
94
|
+
def inject(status, headers, body, marking, nonce)
|
|
84
95
|
html = +''
|
|
85
96
|
body.each { |part| html << part.to_s }
|
|
86
97
|
body.close if body.respond_to?(:close)
|
|
@@ -88,7 +99,8 @@ module I18nFeedback
|
|
|
88
99
|
snippet = Widget.snippet(
|
|
89
100
|
endpoint: I18nFeedback.config.suggestions_endpoint,
|
|
90
101
|
locale: I18n.locale,
|
|
91
|
-
active: marking
|
|
102
|
+
active: marking,
|
|
103
|
+
nonce: nonce
|
|
92
104
|
)
|
|
93
105
|
html = html.include?('</body>') ? html.sub('</body>', "#{snippet}</body>") : html + snippet
|
|
94
106
|
|
data/lib/i18n_feedback/widget.rb
CHANGED
|
@@ -17,8 +17,10 @@ module I18nFeedback
|
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
# The two <script> tags to place before </body>: one carrying the runtime
|
|
20
|
-
# config, one carrying the widget itself.
|
|
21
|
-
|
|
20
|
+
# config, one carrying the widget itself. `nonce:` stamps both tags so the
|
|
21
|
+
# widget runs under a nonce-based Content-Security-Policy (e.g. one using
|
|
22
|
+
# 'strict-dynamic'); pass nil when the app has no CSP nonce.
|
|
23
|
+
def snippet(endpoint:, locale:, active:, nonce: nil)
|
|
22
24
|
config = {
|
|
23
25
|
endpoint: endpoint,
|
|
24
26
|
locale: locale.to_s,
|
|
@@ -26,9 +28,10 @@ module I18nFeedback
|
|
|
26
28
|
showPill: I18nFeedback.config.show_pill ? true : false,
|
|
27
29
|
pillLabel: I18nFeedback.config.pill_label
|
|
28
30
|
}
|
|
31
|
+
nonce_attr = nonce ? %( nonce="#{nonce}") : ''
|
|
29
32
|
|
|
30
|
-
%(<script data-i18n-feedback>window.__i18nFeedback=#{config.to_json};</script>) +
|
|
31
|
-
%(<script data-i18n-feedback-widget>#{javascript}</script>)
|
|
33
|
+
%(<script data-i18n-feedback#{nonce_attr}>window.__i18nFeedback=#{config.to_json};</script>) +
|
|
34
|
+
%(<script data-i18n-feedback-widget#{nonce_attr}>#{javascript}</script>)
|
|
32
35
|
end
|
|
33
36
|
end
|
|
34
37
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: i18n_feedback
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1
|
|
4
|
+
version: 0.2.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Yaroslav Shmarov
|
|
@@ -76,7 +76,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
76
76
|
- !ruby/object:Gem::Version
|
|
77
77
|
version: '0'
|
|
78
78
|
requirements: []
|
|
79
|
-
rubygems_version: 4.0.
|
|
79
|
+
rubygems_version: 4.0.6
|
|
80
80
|
specification_version: 4
|
|
81
81
|
summary: 'In-context i18n proofreading for Rails: click any translated string and
|
|
82
82
|
suggest a fix.'
|