i18n_proofreading 0.12.0 → 1.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0dd116667dbbc3243bc9eb5ae929db378fd60d6354dc9101cde3c137474272dd
4
- data.tar.gz: 67cb971e8e0d957f9ce6ccd6e3a39f102242588e7e6599945267423b7a32c0ba
3
+ metadata.gz: 6f31aa1eec6874e5a80180fbd106c1f6ea7f073241e234c8b0c9c64ae474ba45
4
+ data.tar.gz: c1a02f46f915c3fd496793f9b33885026c090fb70b0fcb0474d017ac0c2bbd14
5
5
  SHA512:
6
- metadata.gz: dd83ae4d457202c63398da204778301533d78c575426591eb4c28acc58a299beab35ccbea8861a468688af0ea09e9a7c67053d299fe0174080377e58ada84b8c
7
- data.tar.gz: e88607a7ebc036f1be84553bd88d995b99ca548992ed9e88d519f0f2497759d48933ce7520eed72524d0c804998c15a0d95aaec88384f7447ce94a44fcac6ee6
6
+ metadata.gz: dfca53a8ba1b338b6bca4b29060c0c5f7ecb8077817d26fc6078d22b971195d7577db3ef7a433f0252213b1214bd09adbdec0daaa66938708a3556e9ca766701
7
+ data.tar.gz: 9bd50ba50b9a8c43b26839a9467b43cc50c96ba18e25ed0738433d0013ce18606566c61ca3b954022281ee40266701aa2c9efd5f6414baac5e561fa5b21a5338
data/AGENTS.md CHANGED
@@ -5,7 +5,7 @@ Instructions for coding agents. Two audiences:
5
5
  - **[Installing i18n_proofreading into a Rails app](#installing-into-a-rails-app)** — you are working in a host app and were asked to add in-context translation review or a way for someone to suggest better wording.
6
6
  - **[Working on the gem itself](#working-on-the-gem-itself)** — you are working in this repository.
7
7
 
8
- Requirements: Ruby >= 3.2, Rails >= 7.1. The widget needs the CSRF token from `csrf_meta_tags`, which a standard Rails layout already has.
8
+ Requirements: Ruby 3.2–4.0, Rails 7.1–8.1. The widget needs the CSRF token from `csrf_meta_tags`, which a standard Rails layout already has.
9
9
 
10
10
  **Read this first: the gem never writes to your locale files, and it is not a production tool.** Both are deliberate, and both are covered below.
11
11
 
@@ -151,6 +151,6 @@ Conventions this codebase holds to — follow them rather than the first thing t
151
151
  - **The tool never writes to the host's locale files.** That is why there is deliberately no update or destroy route, and why the dashboard is read-only. Do not add an "apply" action that edits YAML.
152
152
  - **The widget is injected by middleware and served same-origin**, which is what keeps it working under a nonce-based CSP with `strict-dynamic` across Turbo body swaps. Do not inline it.
153
153
  - **Key marking must degrade to plain strings.** A host that reads translations outside a request, or in an environment where the tool is off, has to get ordinary values back.
154
- - **`lib/i18n_proofreading/dashboard.css` is half shared.** Everything above the `GEM-SPECIFIC` banner is the design system all five gems in the family ship — the same tokens, the same `.page-head`/`.tabs`/`.filters`/`.card`/`.badge`/`button`, the same `.dashboard-shell` + `.record-row` + `.detail-panel` two-pane dashboard — identical in every repo apart from the `ip` prefix. Diff it against a sibling before changing it, and carry the change to the other four. Anything only this gem has goes below the banner. New dashboard markup reuses the shared class names rather than inventing a domain-specific one.
154
+ - **`lib/i18n_proofreading/dashboard.css` is half shared.** Everything above the `GEM-SPECIFIC` banner is the design system all five gems in the family ship — the same tokens, the same `.page-head`/`.tabs`/`.filters`/`.card`/`.badge`/`button`/`.status-switch`, the same `.dashboard-shell` + `.record-row` + `.detail-panel` two-pane dashboard — identical in every repo apart from the `ip` prefix. Diff it against a sibling before changing it, and carry the change to the other four. Anything only this gem has goes below the banner. New dashboard markup reuses the shared class names rather than inventing a domain-specific one.
155
155
  - Every user-facing change bumps `lib/i18n_proofreading/version.rb` and adds a `CHANGELOG.md` entry (Keep a Changelog format) that says what it costs, not only what it adds.
156
156
  - Commit messages are prose that explains the tradeoff — read `git log` before writing one.
data/CHANGELOG.md CHANGED
@@ -1,5 +1,42 @@
1
1
  # Changelog
2
2
 
3
+ ## [Unreleased]
4
+
5
+ ## [1.0.0] - 2026-08-11
6
+
7
+ - **The documented integration surface is now the stable 1.x contract.**
8
+ `I18nProofreading.configure`, every generated configuration option,
9
+ `mount_i18n_proofreading`, `i18n_proofreading_tag`, the toggle parameter,
10
+ `I18nProofreading::Suggestion`'s documented fields/status helpers/scopes, and
11
+ the `on_submit` callback will follow semantic versioning. Engine controllers,
12
+ partials, CSS classes and widget internals remain private implementation.
13
+ - **Hosts that load Action View from an initializer boot normally.** `TagHelper`
14
+ is required from `lib` before the engine registers its Action View load hook,
15
+ rather than relying on an `app/helpers` autoload that Rails may not have set
16
+ up yet. A dummy-host initializer now reproduces that early load on every test
17
+ boot. The helper constant and `i18n_proofreading_tag` API are unchanged.
18
+ - Supported combinations are Ruby 3.2 through 4.0 and Rails 7.1 through 8.1.
19
+ Rails now has a `< 9` upper bound: future Rails majors must pass the suite
20
+ before Bundler treats them as supported.
21
+ - Added a security policy and documented the data a suggestion stores, the
22
+ production-off default, private vulnerability reporting, and host-owned
23
+ retention/deletion. Upgrading from 0.12.1 requires no migration or config
24
+ change.
25
+ - Release validation now runs the normal and browser suites, RuboCop, the
26
+ widget syntax check and gem build on Ruby 4.0; it verifies the published
27
+ version and creates the GitHub Release. Generator tests now write only under
28
+ ignored root `tmp/`, so running the suite no longer rewrites tracked fixtures.
29
+
30
+ ## 0.12.1
31
+
32
+ - **The demo suggestions now teach the review contract.** Pending explains the
33
+ inbox decision, applied makes clear that a human edited and committed the
34
+ locale file, and rejected demonstrates a proposal that changes meaning. The
35
+ seed is now a compact tutorial for the gem's deliberately read-only role.
36
+ - `i18n_proofreading:seed_demo` updates its existing demo suggestions in place,
37
+ so rerunning it overwrites edits to those gem-owned rows. Host locale files
38
+ and non-demo suggestions remain untouched.
39
+
3
40
  ## 0.12.0
4
41
 
5
42
  - **One design system across the family.** The stylesheet now opens with a
@@ -62,8 +99,6 @@
62
99
  does, so a uuid-keyed host gets a uuid table.
63
100
  - A `BackboneTest` now fails the build on any of the above regressing.
64
101
 
65
- ## [Unreleased]
66
-
67
102
  ## [0.10.6]
68
103
 
69
104
  - Added `AGENTS.md`: install and integration instructions written for coding
data/README.md CHANGED
@@ -46,8 +46,8 @@ No layout change needed — the widget injects itself into HTML responses.
46
46
  > `%w[development staging]`, and the review dashboard defaults to development
47
47
  > only. This is not a production tool.
48
48
 
49
- Ruby >= 3.2 · Rails >= 7.1 · CSRF token comes from `csrf_meta_tags`, already in
50
- a standard Rails layout.
49
+ Ruby 3.2–4.0 · Rails 7.1–8.1 · CSRF token comes from `csrf_meta_tags`, already
50
+ in a standard Rails layout.
51
51
 
52
52
  Installing with a coding agent? Point it at [AGENTS.md](AGENTS.md) — the same
53
53
  steps in the order an agent needs them, the gates it tends to get wrong, and the
@@ -273,6 +273,36 @@ to use the localized `i18n_proofreading.pill` key.
273
273
 
274
274
  </details>
275
275
 
276
+ ## Compatibility and public API
277
+
278
+ Starting with 1.0, these documented integration points follow semantic
279
+ versioning: incompatible changes wait for a new major version, while a
280
+ deprecation normally ships in a preceding minor release.
281
+
282
+ - `I18nProofreading.configure` and the configuration options listed above.
283
+ - `mount_i18n_proofreading at:` and the mounted dashboard root.
284
+ - `i18n_proofreading_tag` for hosts that disable automatic injection.
285
+ - The configured toggle parameter and its `true` / `false` behavior.
286
+ - `I18nProofreading::Suggestion` fields documented under Reviewing
287
+ suggestions, `STATUSES`, prefixed status helpers/scopes, and `newest_first`.
288
+ - `on_submit`, called with the saved suggestion.
289
+
290
+ Engine controllers, view partials, CSS classes, generated HTML structure, and
291
+ the JavaScript object behind the widget are implementation details. Integrate
292
+ through the APIs above rather than reopening or linking to those internals.
293
+
294
+ Supported versions are Ruby 3.2 through 4.0 and Rails 7.1 through 8.1. The gem
295
+ depends on Rails `< 9`; support for a future Rails major is released after that
296
+ major passes the compatibility and browser suites.
297
+
298
+ ### Upgrading from 0.12.1
299
+
300
+ Change the version requirement and run `bundle update i18n_proofreading`. No
301
+ database migration, generator, initializer change, or data rewrite is needed.
302
+ The `I18nProofreading::TagHelper` constant and `i18n_proofreading_tag` behavior
303
+ are unchanged; only the helper's internal load location moved so hosts that
304
+ configure Action View during initialization can boot reliably.
305
+
276
306
  ## Security
277
307
 
278
308
  - **Gated on the server** for every marker, endpoint, and injection. Setting
@@ -286,6 +316,12 @@ to use the localized `i18n_proofreading.pill` key.
286
316
  survives Turbo visits.
287
317
  - **Rate-limited per IP** on the submission endpoint (30/min by default,
288
318
  Rails 7.2+).
319
+ - **Host-owned review data.** A row may contain translated text, proposed text,
320
+ a comment, page URL, and optional author fields. The host controls access,
321
+ backups, retention, and deletion.
322
+
323
+ Report vulnerabilities privately through the process in
324
+ [SECURITY.md](SECURITY.md), not through a public issue.
289
325
 
290
326
  ## Development
291
327
 
@@ -296,7 +332,8 @@ bundle exec rake test:system # browser tests (headless Chrome)
296
332
  bundle exec rubocop
297
333
  ```
298
334
 
299
- Tests run against a dummy Rails app in `test/dummy`.
335
+ Tests run against a dummy Rails app in `test/dummy`. CI covers Ruby 3.2–4.0
336
+ against Rails 7.1–8.1, plus the real browser flow on Ruby 4.0.
300
337
 
301
338
  ## One family
302
339
 
data/SECURITY.md ADDED
@@ -0,0 +1,38 @@
1
+ # Security policy
2
+
3
+ ## Supported versions
4
+
5
+ Security fixes are released for the latest 1.x version. If a report also
6
+ affects the newest 0.x release, a backport may be published when the fix is
7
+ small and the affected application cannot upgrade immediately.
8
+
9
+ ## Reporting a vulnerability
10
+
11
+ Please do not open a public issue for a vulnerability. Use
12
+ [GitHub private vulnerability reporting](https://github.com/yshmarov/i18n_proofreading/security/advisories/new)
13
+ and include the affected version, a minimal reproduction, impact, and any known
14
+ workaround. Credentials, session cookies, private application URLs, customer
15
+ text, and production database contents should not be included.
16
+
17
+ Public disclosure should wait until a fixed version is available and affected
18
+ users have had a reasonable opportunity to upgrade.
19
+
20
+ ## Data and deployment boundary
21
+
22
+ The proofreading widget is enabled only in development and staging by default.
23
+ Adding production to `enabled_environments` is unsupported: key marking changes
24
+ rendered application text and the suggestion endpoint is intended for trusted
25
+ review environments.
26
+
27
+ A suggestion can store its translation key, locale, current and proposed text,
28
+ comment, page URL, status, timestamps, and optional host-provided author id and
29
+ label. Host applications own that data, its backups, retention period, access
30
+ controls, and deletion. Delete a suggestion with ordinary Active Record when a
31
+ reviewer or application policy requires erasure:
32
+
33
+ ```ruby
34
+ I18nProofreading::Suggestion.find(id).destroy!
35
+ ```
36
+
37
+ The mounted dashboard is independently protected by `authorize_admin`. Outside
38
+ development it fails closed until the host explicitly grants access.
@@ -144,6 +144,19 @@
144
144
  & .actions form { display: inline; }
145
145
  & .form-actions { padding-top: 12px; }
146
146
 
147
+ /* Status switch: one pill holding every state a record can be in, the state it
148
+ is in now lit. Each other segment is a submit button, so moving a record
149
+ takes one click and no JavaScript; the current segment is a `type="button"`
150
+ so clicking it cannot re-file the record it is already filed under. Two
151
+ states or five, the pill sizes itself. A gem tints the lit segment with its
152
+ own status colour — see the `GEM-SPECIFIC` section. */
153
+ & .status-switch { display: inline-flex; flex: 0 0 auto; margin: 0; padding: 3px; border: 1px solid var(--ip-border); border-radius: 999px; background: var(--ip-bg); }
154
+ & .status-switch button { min-height: 30px; padding: 0 12px; border: 0; border-radius: 999px; background: none; color: var(--ip-muted); font-size: 13px; font-weight: 700; white-space: nowrap; transition: background .16s ease, color .16s ease; }
155
+ & .status-switch button:hover { border: 0; background: color-mix(in srgb, var(--ip-muted) 12%, transparent); color: var(--ip-text); }
156
+ & .status-switch button.current { background: var(--ip-surface); color: var(--ip-text); box-shadow: 0 1px 2px rgba(15, 23, 42, .12); cursor: default; }
157
+ & .status-switch button.current:hover { background: var(--ip-surface); }
158
+ & .panel-head .status-switch { margin-left: auto; }
159
+
147
160
  & .card { overflow: hidden; border: 1px solid var(--ip-border); border-radius: 12px; background: var(--ip-surface); }
148
161
  & .card.pad { padding: 16px; overflow: visible; }
149
162
  & table { width: 100%; border-collapse: collapse; }
@@ -266,6 +279,7 @@
266
279
  & .dashboard-detail .detail-panel { min-height: calc(100vh - 64px); }
267
280
  & .mobile-back { display: block; flex: 0 0 auto; margin: 4px 0 10px; font-size: 13px; }
268
281
  & .panel-head h1 { font-size: 18px; }
282
+ & .status-switch button { padding: 0 9px; font-size: 12px; }
269
283
  & dl { grid-template-columns: 1fr; gap: 3px; }
270
284
  & dd { margin-bottom: 8px; }
271
285
  }
@@ -7,34 +7,37 @@ module I18nProofreading
7
7
  translation_key: 'dashboard.title',
8
8
  locale: 'en',
9
9
  old_value: 'Dashboard',
10
- proposed_value: 'Overview',
11
- comment: 'Shorter and clearer for the first screen.',
10
+ proposed_value: 'Product overview',
11
+ comment: 'Start here: pending suggestions are the review inbox. Compare the rendered wording and ' \
12
+ 'page context, then decide whether the proposed copy belongs in your locale file.',
12
13
  page_url: '/dashboard',
13
14
  status: 'pending',
14
15
  author_id: 'i18n-proofreading-demo:reviewer',
15
- author_label: 'Demo Reviewer'
16
+ author_label: 'Demo reviewer · needs a decision'
16
17
  },
17
18
  {
18
19
  translation_key: 'billing.cta',
19
20
  locale: 'en',
20
21
  old_value: 'Go',
21
22
  proposed_value: 'Update billing details',
22
- comment: 'The button should say what will happen.',
23
+ comment: 'Applied is bookkeeping, not automation: a maintainer copied this clearer call to action ' \
24
+ 'into config/locales and committed the change. The gem never edits YAML for you.',
23
25
  page_url: '/billing',
24
26
  status: 'applied',
25
27
  author_id: 'i18n-proofreading-demo:copywriter',
26
- author_label: 'Demo Copywriter'
28
+ author_label: 'Demo copywriter · applied by a human'
27
29
  },
28
30
  {
29
31
  translation_key: 'settings.cancel',
30
32
  locale: 'fr',
31
33
  old_value: 'Annuler',
32
34
  proposed_value: 'Supprimer le compte',
33
- comment: 'Rejected example: this changes the meaning.',
35
+ comment: 'Rejected on purpose: the proposal means “Delete the account,” not “Cancel.” The original ' \
36
+ 'translation stays the source of truth, and the rejected row preserves the review decision.',
34
37
  page_url: '/settings',
35
38
  status: 'rejected',
36
39
  author_id: 'i18n-proofreading-demo:reviewer',
37
- author_label: 'Demo Reviewer'
40
+ author_label: 'Demo reviewer · meaning changed'
38
41
  }
39
42
  ].freeze
40
43
 
@@ -1,9 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module I18nProofreading
4
- # Lets a host place the widget explicitly (`<%= i18n_proofreading_tag %>` at the end
5
- # of a layout) instead of relying on auto-injection. Renders nothing unless the
6
- # tool is available for the current request.
4
+ # Lets a host place the widget explicitly (`<%= i18n_proofreading_tag %>` at
5
+ # the end of a layout) instead of relying on auto-injection. This lives under
6
+ # lib and is required before the engine registers its Action View load hook:
7
+ # a host initializer may load Action View before Rails sets up application
8
+ # autoloaders, when an app/helpers constant cannot be resolved yet.
7
9
  module TagHelper
8
10
  def i18n_proofreading_tag
9
11
  return ''.html_safe unless I18nProofreading.available?(request)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module I18nProofreading
4
- VERSION = '0.12.0'
4
+ VERSION = '1.0.0'
5
5
  end
@@ -4,6 +4,7 @@ require 'i18n_proofreading/version'
4
4
  require 'i18n_proofreading/configuration'
5
5
  require 'i18n_proofreading/marking'
6
6
  require 'i18n_proofreading/widget'
7
+ require 'i18n_proofreading/tag_helper'
7
8
  require 'i18n_proofreading/middleware'
8
9
  require 'i18n_proofreading/seeds'
9
10
  require 'i18n_proofreading/engine'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: i18n_proofreading
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.12.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yaroslav Shmarov
@@ -16,6 +16,9 @@ dependencies:
16
16
  - - ">="
17
17
  - !ruby/object:Gem::Version
18
18
  version: '7.1'
19
+ - - "<"
20
+ - !ruby/object:Gem::Version
21
+ version: '9'
19
22
  type: :runtime
20
23
  prerelease: false
21
24
  version_requirements: !ruby/object:Gem::Requirement
@@ -23,6 +26,9 @@ dependencies:
23
26
  - - ">="
24
27
  - !ruby/object:Gem::Version
25
28
  version: '7.1'
29
+ - - "<"
30
+ - !ruby/object:Gem::Version
31
+ version: '9'
26
32
  description: |
27
33
  A mountable Rails engine that renders each translation alongside its i18n key
28
34
  in development and staging, lets reviewers click any string in the running app
@@ -39,13 +45,13 @@ files:
39
45
  - MIT-LICENSE
40
46
  - README.md
41
47
  - Rakefile
48
+ - SECURITY.md
42
49
  - app/controllers/concerns/i18n_proofreading/request_context.rb
43
50
  - app/controllers/i18n_proofreading/application_controller.rb
44
51
  - app/controllers/i18n_proofreading/dashboard_controller.rb
45
52
  - app/controllers/i18n_proofreading/submissions_controller.rb
46
53
  - app/controllers/i18n_proofreading/suggestions_controller.rb
47
54
  - app/controllers/i18n_proofreading/widgets_controller.rb
48
- - app/helpers/i18n_proofreading/tag_helper.rb
49
55
  - app/models/i18n_proofreading/application_record.rb
50
56
  - app/models/i18n_proofreading/suggestion.rb
51
57
  - app/views/i18n_proofreading/shared/_dashboard.html.erb
@@ -91,6 +97,7 @@ files:
91
97
  - lib/i18n_proofreading/marking.rb
92
98
  - lib/i18n_proofreading/middleware.rb
93
99
  - lib/i18n_proofreading/seeds.rb
100
+ - lib/i18n_proofreading/tag_helper.rb
94
101
  - lib/i18n_proofreading/version.rb
95
102
  - lib/i18n_proofreading/widget.js
96
103
  - lib/i18n_proofreading/widget.rb
@@ -101,6 +108,7 @@ licenses:
101
108
  metadata:
102
109
  source_code_uri: https://github.com/yshmarov/i18n_proofreading
103
110
  changelog_uri: https://github.com/yshmarov/i18n_proofreading/blob/main/CHANGELOG.md
111
+ bug_tracker_uri: https://github.com/yshmarov/i18n_proofreading/issues
104
112
  rubygems_mfa_required: 'true'
105
113
  rdoc_options: []
106
114
  require_paths:
@@ -116,7 +124,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
116
124
  - !ruby/object:Gem::Version
117
125
  version: '0'
118
126
  requirements: []
119
- rubygems_version: 3.6.9
127
+ rubygems_version: 4.0.16
120
128
  specification_version: 4
121
129
  summary: 'In-context i18n proofreading for Rails: click any translated string and
122
130
  suggest a fix.'