inertia_i18n 0.6.2 → 0.7.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/CHANGELOG.md +6 -0
- data/README.md +43 -0
- data/lib/generators/inertia_i18n/templates/inertia_i18n.rb.tt +6 -0
- data/lib/inertia_i18n/cli.rb +14 -0
- data/lib/inertia_i18n/configuration.rb +5 -0
- data/lib/inertia_i18n/health_checker.rb +34 -11
- data/lib/inertia_i18n/parsers/javascript_parsing.rb +4 -2
- data/lib/inertia_i18n/scan_results.rb +16 -1
- data/lib/inertia_i18n/version.rb +1 -1
- 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: 4e612133c6b6af1e6269626c3af83a4c27c846b50f2b75f1a12d51559425a3d5
|
|
4
|
+
data.tar.gz: f2524dc747a6314a7a482566b086cc854b1a1276ae048b04cb8002946d5a4910
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e2675724ebf16a84ce0a13c3b7d09cc7d16b4c587f79c5ba05840100e796f16d86193e590011f08c6685d557926f63966191456c7bb72d4345397013d4fee80f
|
|
7
|
+
data.tar.gz: 166504db3b51629f77c9fb112c53f5ba87e661d1558f72beb22f2849996ded50dbee7c94a8879cfe07fd54590291df22ac97bd87e28a6a39c956b5e1e19a323a
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.7.0] - 2026-02-27
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **Dynamic Keys Configuration**: Added `dynamic_keys` configuration property to expand dynamic patterns into explicit static keys. This allows the scanner to check for missing variants of dynamic keys (e.g. `status.active`, `status.inactive`) and mark them as used. Supported in both Ruby initializer and YAML configuration formats.
|
|
8
|
+
|
|
3
9
|
## [0.6.2] - 2026-02-23
|
|
4
10
|
|
|
5
11
|
### Fixed
|
data/README.md
CHANGED
|
@@ -231,6 +231,38 @@ Handles:
|
|
|
231
231
|
- Template literals: `t(\`user.\${type}.title\`)` (flagged for review)
|
|
232
232
|
- Dynamic patterns: `t(keyVariable)` (flagged for review)
|
|
233
233
|
|
|
234
|
+
### Dynamic Keys Configuration
|
|
235
|
+
|
|
236
|
+
When you use template literals or dynamic variables for translations, the scanner might flag them as unused or missing. You can configure exact mappings for these dynamic keys using the `dynamic_keys` setting.
|
|
237
|
+
|
|
238
|
+
```ruby
|
|
239
|
+
# config/initializers/inertia_i18n.rb
|
|
240
|
+
config.dynamic_keys = {
|
|
241
|
+
"status." => ["active", "inactive", "pending"]
|
|
242
|
+
}
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
**Option B: YAML config**
|
|
246
|
+
|
|
247
|
+
```yaml
|
|
248
|
+
# config/inertia_i18n.yml
|
|
249
|
+
dynamic_keys:
|
|
250
|
+
"status.":
|
|
251
|
+
- active
|
|
252
|
+
- inactive
|
|
253
|
+
- pending
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
Now, when the scanner sees `t(\`status.\${statusKey}\`)`, it will automatically expand it to `status.active`, `status.inactive`, and `status.pending`, ensuring they are checked for existence and marked as used.
|
|
257
|
+
|
|
258
|
+
Alternatively, you can just ignore the prefix if the possible values are not known using `dynamic_patterns`:
|
|
259
|
+
|
|
260
|
+
```ruby
|
|
261
|
+
config.dynamic_patterns = {
|
|
262
|
+
"status." => "Dynamic status keys"
|
|
263
|
+
}
|
|
264
|
+
```
|
|
265
|
+
|
|
234
266
|
### Magic Comments
|
|
235
267
|
|
|
236
268
|
Use magic comments to mark dynamic keys as used or suppress warnings. Both `inertia-i18n-use` and `i18n-tasks-use` are supported.
|
|
@@ -416,6 +448,12 @@ InertiaI18n.configure do |config|
|
|
|
416
448
|
# "status." => "Dynamic status keys"
|
|
417
449
|
}
|
|
418
450
|
|
|
451
|
+
# Dynamic keys exact mapping (prefix => array of possible values)
|
|
452
|
+
# These keys will be automatically expanded and checked for existence
|
|
453
|
+
config.dynamic_keys = {
|
|
454
|
+
# "status." => ["active", "inactive", "pending"]
|
|
455
|
+
}
|
|
456
|
+
|
|
419
457
|
# Keys to ignore during unused/missing checks
|
|
420
458
|
config.ignore_unused = []
|
|
421
459
|
config.ignore_missing = []
|
|
@@ -467,6 +505,11 @@ translation_functions:
|
|
|
467
505
|
- i18n.t
|
|
468
506
|
# dynamic_patterns:
|
|
469
507
|
# "status.": Dynamic status keys
|
|
508
|
+
dynamic_keys:
|
|
509
|
+
"status.":
|
|
510
|
+
- active
|
|
511
|
+
- inactive
|
|
512
|
+
- pending
|
|
470
513
|
ignore_unused: []
|
|
471
514
|
ignore_missing: []
|
|
472
515
|
key_properties:
|
|
@@ -34,6 +34,12 @@ InertiaI18n.configure do |config|
|
|
|
34
34
|
# "status." => "Dynamic status keys"
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
# Dynamic keys (prefix => array of possible values)
|
|
38
|
+
# These keys will be automatically expanded and checked for existence
|
|
39
|
+
config.dynamic_keys = {
|
|
40
|
+
# "status." => ["active", "inactive", "pending"]
|
|
41
|
+
}
|
|
42
|
+
|
|
37
43
|
# Keys to ignore during unused check
|
|
38
44
|
# "common" namespace is generated by inertia_i18n:install as sample data
|
|
39
45
|
config.ignore_unused = ["common"]
|
data/lib/inertia_i18n/cli.rb
CHANGED
|
@@ -205,6 +205,12 @@ module InertiaI18n
|
|
|
205
205
|
# "status." => "Dynamic status keys"
|
|
206
206
|
}
|
|
207
207
|
|
|
208
|
+
# Dynamic keys (prefix => array of possible values)
|
|
209
|
+
# These keys will be automatically expanded and checked for existence
|
|
210
|
+
config.dynamic_keys = {
|
|
211
|
+
# "status." => ["active", "inactive", "pending"]
|
|
212
|
+
}
|
|
213
|
+
|
|
208
214
|
# Keys to ignore during unused check
|
|
209
215
|
config.ignore_unused = []
|
|
210
216
|
|
|
@@ -253,6 +259,14 @@ module InertiaI18n
|
|
|
253
259
|
# dynamic_patterns:
|
|
254
260
|
# "status.": Dynamic status keys
|
|
255
261
|
|
|
262
|
+
# Dynamic keys (prefix => array of possible values)
|
|
263
|
+
# These keys will be automatically expanded and checked for existence
|
|
264
|
+
# dynamic_keys:
|
|
265
|
+
# "status.":
|
|
266
|
+
# - active
|
|
267
|
+
# - inactive
|
|
268
|
+
# - pending
|
|
269
|
+
|
|
256
270
|
# Keys to ignore during unused check
|
|
257
271
|
ignore_unused: []
|
|
258
272
|
|
|
@@ -35,6 +35,9 @@ module InertiaI18n
|
|
|
35
35
|
# Dynamic key patterns (prefix => regex)
|
|
36
36
|
attr_accessor :dynamic_patterns
|
|
37
37
|
|
|
38
|
+
# Dynamic keys exact mapping (prefix => array of possible values)
|
|
39
|
+
attr_accessor :dynamic_keys
|
|
40
|
+
|
|
38
41
|
# Keys to ignore during unused check
|
|
39
42
|
attr_accessor :ignore_unused
|
|
40
43
|
|
|
@@ -65,6 +68,7 @@ module InertiaI18n
|
|
|
65
68
|
}
|
|
66
69
|
@translation_functions = %w[t $t i18n.t]
|
|
67
70
|
@dynamic_patterns = {}
|
|
71
|
+
@dynamic_keys = {}
|
|
68
72
|
@ignore_unused = []
|
|
69
73
|
@ignore_missing = []
|
|
70
74
|
@key_properties = %w[titleKey labelKey messageKey descriptionKey placeholderKey key]
|
|
@@ -104,6 +108,7 @@ module InertiaI18n
|
|
|
104
108
|
|
|
105
109
|
load_hash_attribute(:interpolation, data)
|
|
106
110
|
self.dynamic_patterns = data["dynamic_patterns"] || {} if data.key?("dynamic_patterns")
|
|
111
|
+
self.dynamic_keys = data["dynamic_keys"] || {} if data.key?("dynamic_keys")
|
|
107
112
|
load_sibling_detection(data) if data.key?("sibling_detection")
|
|
108
113
|
load_missing_key_filters(data) if data.key?("missing_key_filters")
|
|
109
114
|
end
|
|
@@ -79,6 +79,7 @@ module InertiaI18n
|
|
|
79
79
|
|
|
80
80
|
# Add configured dynamic patterns
|
|
81
81
|
dynamic_prefixes.concat(@config.dynamic_patterns&.keys || [])
|
|
82
|
+
dynamic_prefixes.concat(@config.dynamic_keys&.keys || [])
|
|
82
83
|
|
|
83
84
|
unused = available_keys - used_keys
|
|
84
85
|
|
|
@@ -102,11 +103,14 @@ module InertiaI18n
|
|
|
102
103
|
# Filter out keys covered by ignore_unused config
|
|
103
104
|
unused = filter_ignored_keys(unused, @config.ignore_unused)
|
|
104
105
|
|
|
105
|
-
|
|
106
|
+
normalized_unused = normalize_keys_for_sync(unused)
|
|
107
|
+
|
|
108
|
+
normalized_unused.each do |key|
|
|
109
|
+
display_key = key.sub("_[plural]", " (plural forms)")
|
|
106
110
|
@issues[:unused] << {
|
|
107
|
-
key:
|
|
111
|
+
key: display_key,
|
|
108
112
|
severity: :warning,
|
|
109
|
-
message: "Key '#{
|
|
113
|
+
message: "Key '#{display_key}' exists in #{primary_locale}.json but is not used in code"
|
|
110
114
|
}
|
|
111
115
|
end
|
|
112
116
|
end
|
|
@@ -114,34 +118,53 @@ module InertiaI18n
|
|
|
114
118
|
def check_locale_sync
|
|
115
119
|
primary_locale = @config.primary_locale
|
|
116
120
|
primary_keys = Set.new(LocaleLoader.extract_keys(@locales[primary_locale] || {}))
|
|
121
|
+
normalized_primary = normalize_keys_for_sync(primary_keys)
|
|
117
122
|
|
|
118
123
|
@config.secondary_locales.each do |locale|
|
|
119
124
|
locale_data = @locales[locale] || {}
|
|
120
125
|
locale_keys = Set.new(LocaleLoader.extract_keys(locale_data))
|
|
126
|
+
normalized_locale = normalize_keys_for_sync(locale_keys)
|
|
121
127
|
|
|
122
|
-
missing_in_locale =
|
|
123
|
-
extra_in_locale =
|
|
128
|
+
missing_in_locale = normalized_primary - normalized_locale
|
|
129
|
+
extra_in_locale = normalized_locale - normalized_primary
|
|
124
130
|
|
|
125
|
-
missing_in_locale.each do |
|
|
131
|
+
missing_in_locale.each do |n_key|
|
|
132
|
+
display_key = n_key.sub("_[plural]", " (plural forms)")
|
|
126
133
|
@issues[:unsync] << {
|
|
127
|
-
key:
|
|
134
|
+
key: display_key,
|
|
128
135
|
locale: locale,
|
|
129
136
|
severity: :error,
|
|
130
|
-
message: "Key '#{
|
|
137
|
+
message: "Key '#{display_key}' exists in #{primary_locale}.json but missing from #{locale}.json"
|
|
131
138
|
}
|
|
132
139
|
end
|
|
133
140
|
|
|
134
|
-
extra_in_locale.each do |
|
|
141
|
+
extra_in_locale.each do |n_key|
|
|
142
|
+
display_key = n_key.sub("_[plural]", " (plural forms)")
|
|
135
143
|
@issues[:unsync] << {
|
|
136
|
-
key:
|
|
144
|
+
key: display_key,
|
|
137
145
|
locale: locale,
|
|
138
146
|
severity: :warning,
|
|
139
|
-
message: "Key '#{
|
|
147
|
+
message: "Key '#{display_key}' exists in #{locale}.json but missing from #{primary_locale}.json"
|
|
140
148
|
}
|
|
141
149
|
end
|
|
142
150
|
end
|
|
143
151
|
end
|
|
144
152
|
|
|
153
|
+
def normalize_keys_for_sync(keys)
|
|
154
|
+
plural_suffixes = %w[_zero _one _two _few _many _other]
|
|
155
|
+
|
|
156
|
+
normalized = Set.new
|
|
157
|
+
keys.each do |key|
|
|
158
|
+
suffix = plural_suffixes.find { |s| key.end_with?(s) }
|
|
159
|
+
if suffix
|
|
160
|
+
normalized.add(key.chomp(suffix) + "_[plural]")
|
|
161
|
+
else
|
|
162
|
+
normalized.add(key)
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
normalized
|
|
166
|
+
end
|
|
167
|
+
|
|
145
168
|
def filter_ignored_keys(keys, ignore_patterns)
|
|
146
169
|
return keys if ignore_patterns.empty?
|
|
147
170
|
|
|
@@ -83,13 +83,15 @@ module InertiaI18n
|
|
|
83
83
|
content.scan(/(?<!\w)#{escaped_func}\(\s*`([^`]*\$\{.+?)`/) do |match|
|
|
84
84
|
template = match[0]
|
|
85
85
|
prefix = template.split("${").first
|
|
86
|
-
|
|
86
|
+
line = content[0..Regexp.last_match.begin(0)].count("\n") + 1
|
|
87
|
+
patterns << {pattern: prefix, type: :template_literal, raw: template, line: line}
|
|
87
88
|
end
|
|
88
89
|
|
|
89
90
|
# Matches t('prefix.' + var) or t("prefix." + var) - string concatenation
|
|
90
91
|
content.scan(/(?<!\w)#{escaped_func}\(\s*(['"])([^'"]+\.)\1\s*\+/) do |match|
|
|
91
92
|
prefix = match[1]
|
|
92
|
-
|
|
93
|
+
line = content[0..Regexp.last_match.begin(0)].count("\n") + 1
|
|
94
|
+
patterns << {pattern: prefix, type: :string_concat, raw: "#{prefix} + ...", line: line}
|
|
93
95
|
end
|
|
94
96
|
end
|
|
95
97
|
|
|
@@ -26,7 +26,22 @@ module InertiaI18n
|
|
|
26
26
|
end
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
config = InertiaI18n.configuration
|
|
30
|
+
dynamic_keys_config = config.dynamic_keys || {}
|
|
31
|
+
|
|
32
|
+
keys[:dynamic].each do |dynamic|
|
|
33
|
+
pattern = dynamic[:pattern]
|
|
34
|
+
@dynamic_patterns << dynamic
|
|
35
|
+
|
|
36
|
+
if dynamic_keys_config.key?(pattern)
|
|
37
|
+
dynamic_keys_config[pattern].each do |value|
|
|
38
|
+
# Append value to pattern (e.g. "status." + "active" -> "status.active")
|
|
39
|
+
expanded_key = "#{pattern}#{value}"
|
|
40
|
+
@static_keys.add(expanded_key)
|
|
41
|
+
@occurrences[expanded_key] << {file: file, line: dynamic[:line]}
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
30
45
|
end
|
|
31
46
|
|
|
32
47
|
def used_keys
|
data/lib/inertia_i18n/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: inertia_i18n
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.7.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Alexey Poimtsev
|
|
@@ -198,7 +198,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
198
198
|
- !ruby/object:Gem::Version
|
|
199
199
|
version: '0'
|
|
200
200
|
requirements: []
|
|
201
|
-
rubygems_version: 4.0.
|
|
201
|
+
rubygems_version: 4.0.7
|
|
202
202
|
specification_version: 4
|
|
203
203
|
summary: Translation management for Inertia.js applications
|
|
204
204
|
test_files: []
|