auto-l18n 0.1.1 → 0.1.2
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/README.md +6 -6
- data/exe/auto-l18n +36 -5
- data/lib/auto/l18n/version.rb +1 -1
- data/lib/auto/l18n.rb +72 -3
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d827a80d2d978e8855592c2fe411c97f169a5c44b59d2a7e3a87da698bad39ad
|
|
4
|
+
data.tar.gz: 82096e37eb8ad1dad0473c5762c1cd04b5e2f078b952747212bc84656bbacab8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b5cbf6914f5e5c1ae1c42d3f7a18895596b621280eee99071110234c878279096d49a226886b6f8a3a9c583fec8ef55c06d5652c18779fe8d2e63d978d58aa58
|
|
7
|
+
data.tar.gz: 945cb23e8fd293d78a81e89a717f3aea3ebfac24358b84df5bc0ac10bd36c765ecb681b8f49090b1888e2e1fa2a5d38ceacb1a3e792661f643d8470b2699bc1a
|
data/README.md
CHANGED
|
@@ -50,7 +50,7 @@ texts.each { |t| puts "- #{t}" }
|
|
|
50
50
|
# Preview changes (dry run)
|
|
51
51
|
result = Auto::L18n.auto_internationalize(
|
|
52
52
|
"app/views/posts/show.html.erb",
|
|
53
|
-
namespace: "views.posts.show",
|
|
53
|
+
# namespace: "views.posts.show", # Optional – will be derived from the file path if omitted
|
|
54
54
|
dry_run: true
|
|
55
55
|
)
|
|
56
56
|
|
|
@@ -58,8 +58,7 @@ puts "Would replace #{result[:total_replaced]} strings"
|
|
|
58
58
|
|
|
59
59
|
# Apply changes
|
|
60
60
|
result = Auto::L18n.auto_internationalize(
|
|
61
|
-
"app/views/posts/show.html.erb"
|
|
62
|
-
namespace: "views.posts.show"
|
|
61
|
+
"app/views/posts/show.html.erb"
|
|
63
62
|
)
|
|
64
63
|
```
|
|
65
64
|
|
|
@@ -70,12 +69,13 @@ result = Auto::L18n.auto_internationalize(
|
|
|
70
69
|
ruby exe/auto-l18n app/views/posts/show.html.erb
|
|
71
70
|
|
|
72
71
|
# Replace with I18n calls (dry run)
|
|
72
|
+
# Note: If --namespace is omitted, it will be derived from the file path (e.g., app/views/admin/users/show.html.erb -> views.admin.users.show)
|
|
73
73
|
ruby exe/auto-l18n app/views/posts/show.html.erb \
|
|
74
|
-
--replace --
|
|
74
|
+
--replace --dry-run
|
|
75
75
|
|
|
76
76
|
# Actually apply changes
|
|
77
77
|
ruby exe/auto-l18n app/views/posts/show.html.erb \
|
|
78
|
-
--replace
|
|
78
|
+
--replace
|
|
79
79
|
```
|
|
80
80
|
|
|
81
81
|
## Example Transformation
|
|
@@ -178,7 +178,7 @@ Options:
|
|
|
178
178
|
--ext=EXTS File extensions (default: .html.erb)
|
|
179
179
|
--replace Replace hardcoded text with I18n calls
|
|
180
180
|
--locale-path=PATH Locale file path (default: config/locales/en.yml)
|
|
181
|
-
--namespace=NS Translation key namespace (e.g., views.posts)
|
|
181
|
+
--namespace=NS Translation key namespace (e.g., views.posts). If omitted, it is derived from the file path (folder hierarchy).
|
|
182
182
|
--dry-run Preview changes without modifying files
|
|
183
183
|
--no-backup Don't create backup files
|
|
184
184
|
-h, --help Show help
|
data/exe/auto-l18n
CHANGED
|
@@ -42,7 +42,7 @@ parser = OptionParser.new do |opts|
|
|
|
42
42
|
options[:locale_path] = path
|
|
43
43
|
end
|
|
44
44
|
|
|
45
|
-
opts.on("--namespace=NS", "Namespace for translation keys (e.g., views.posts)") do |ns|
|
|
45
|
+
opts.on("--namespace=NS", "Namespace for translation keys (e.g., views.posts). If omitted, it is derived from the file path (folder hierarchy).") do |ns|
|
|
46
46
|
options[:namespace] = ns
|
|
47
47
|
end
|
|
48
48
|
|
|
@@ -101,16 +101,47 @@ if paths.empty?
|
|
|
101
101
|
end
|
|
102
102
|
|
|
103
103
|
if options[:mode] == "find"
|
|
104
|
-
#
|
|
104
|
+
# Find and list hardcoded text; in --dry-run also show the keys that would be used
|
|
105
105
|
total = 0
|
|
106
106
|
paths.each do |p|
|
|
107
107
|
found = Auto::L18n.find_text(p)
|
|
108
108
|
next if found.empty?
|
|
109
109
|
|
|
110
|
-
total += found.size
|
|
111
110
|
puts "\nFile: #{p}"
|
|
112
|
-
found
|
|
113
|
-
|
|
111
|
+
# List found strings
|
|
112
|
+
total += found.size
|
|
113
|
+
found.each { |s| puts " - #{s}" }
|
|
114
|
+
|
|
115
|
+
# In dry-run, show the keys that would be used for replacement
|
|
116
|
+
if options[:dry_run]
|
|
117
|
+
structured = Auto::L18n.find_text(p, structured: true)
|
|
118
|
+
# mimic the sort used by exchange_text_for_l18n_placeholder (by line desc)
|
|
119
|
+
sorted = structured.sort_by { |f| -(f.line || 0) }
|
|
120
|
+
|
|
121
|
+
# Determine effective namespace as in the library code
|
|
122
|
+
effective_ns = options[:namespace]
|
|
123
|
+
if (effective_ns.nil? || effective_ns.empty?)
|
|
124
|
+
# Call private helper via send to derive from path
|
|
125
|
+
begin
|
|
126
|
+
effective_ns = Auto::L18n.send(:derive_namespace_from_path, p, {})
|
|
127
|
+
effective_ns = nil if effective_ns.nil? || effective_ns.empty?
|
|
128
|
+
rescue NoMethodError
|
|
129
|
+
# Fallback: no derived namespace available
|
|
130
|
+
effective_ns = options[:namespace]
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
keys = sorted.each_with_index.map do |f, idx|
|
|
135
|
+
begin
|
|
136
|
+
Auto::L18n.send(:generate_translation_key, f.text, f.type, effective_ns, idx)
|
|
137
|
+
rescue NoMethodError
|
|
138
|
+
# Extremely unlikely; just show placeholder when method not accessible
|
|
139
|
+
"generated_key_#{idx}"
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
puts " Keys that would be used:"
|
|
144
|
+
keys.each { |k| puts " - #{k}" }
|
|
114
145
|
end
|
|
115
146
|
end
|
|
116
147
|
|
data/lib/auto/l18n/version.rb
CHANGED
data/lib/auto/l18n.rb
CHANGED
|
@@ -424,7 +424,7 @@ module Auto
|
|
|
424
424
|
results
|
|
425
425
|
end
|
|
426
426
|
|
|
427
|
-
|
|
427
|
+
# Exchange hardcoded text for I18n placeholders
|
|
428
428
|
#
|
|
429
429
|
# This method replaces hardcoded strings in a file with I18n translation calls
|
|
430
430
|
# and adds the translations to a locale file (default: en.yml).
|
|
@@ -440,7 +440,7 @@ module Auto
|
|
|
440
440
|
# @option options [Boolean] :backup (true) Create backup files before modifying
|
|
441
441
|
#
|
|
442
442
|
# @return [Hash] Summary of changes made
|
|
443
|
-
|
|
443
|
+
def self.exchange_text_for_l18n_placeholder(path, options = {})
|
|
444
444
|
raise ArgumentError, "path must be a String" unless path.is_a?(String)
|
|
445
445
|
raise ArgumentError, "File not found: #{path}" unless File.file?(path)
|
|
446
446
|
|
|
@@ -449,6 +449,9 @@ module Auto
|
|
|
449
449
|
locale_path: "config/locales/en.yml",
|
|
450
450
|
locale: "en",
|
|
451
451
|
namespace: nil,
|
|
452
|
+
namespace_from_path: true, # derive namespace from folder/file path when none provided
|
|
453
|
+
path_root: nil, # optional root to strip (e.g., "app/views")
|
|
454
|
+
namespace_prefix: nil, # optional prefix to prepend (e.g., "views")
|
|
452
455
|
dry_run: false,
|
|
453
456
|
min_length: 2,
|
|
454
457
|
ignore_patterns: [],
|
|
@@ -474,9 +477,17 @@ module Auto
|
|
|
474
477
|
# Process findings in reverse order by position to maintain string positions
|
|
475
478
|
sorted_findings = findings.sort_by { |f| -(f.line || 0) }
|
|
476
479
|
|
|
480
|
+
# Determine effective namespace: user-provided or derived from file path
|
|
481
|
+
effective_namespace = opts[:namespace]
|
|
482
|
+
if (effective_namespace.nil? || effective_namespace.empty?) && opts[:namespace_from_path]
|
|
483
|
+
effective_namespace = derive_namespace_from_path(path, opts)
|
|
484
|
+
# Normalize to nil if empty
|
|
485
|
+
effective_namespace = nil if effective_namespace.nil? || effective_namespace.empty?
|
|
486
|
+
end
|
|
487
|
+
|
|
477
488
|
sorted_findings.each_with_index do |finding, idx|
|
|
478
489
|
# Generate translation key
|
|
479
|
-
key = generate_translation_key(finding.text, finding.type,
|
|
490
|
+
key = generate_translation_key(finding.text, finding.type, effective_namespace, idx)
|
|
480
491
|
|
|
481
492
|
# Add to locale file
|
|
482
493
|
set_nested_key(locale_data, key, finding.text, opts[:locale])
|
|
@@ -603,6 +614,64 @@ module Auto
|
|
|
603
614
|
parts.join('.')
|
|
604
615
|
end
|
|
605
616
|
|
|
617
|
+
# Derive a namespace from the file path, producing a dotted hierarchy
|
|
618
|
+
# Examples:
|
|
619
|
+
# - app/views/admin/users/show.html.erb -> "views.admin.users.show"
|
|
620
|
+
# - app/views/posts/index.html.erb -> "views.posts.index"
|
|
621
|
+
# - src/templates/home.html.erb (with namespace_prefix: nil) -> "src.templates.home"
|
|
622
|
+
# - test/test.html.erb (no root) -> "test.test"
|
|
623
|
+
def self.derive_namespace_from_path(path, options = {})
|
|
624
|
+
require 'pathname'
|
|
625
|
+
abs_path = File.expand_path(path)
|
|
626
|
+
|
|
627
|
+
# Resolve root to strip
|
|
628
|
+
root = options[:path_root]
|
|
629
|
+
prefix = options[:namespace_prefix]
|
|
630
|
+
|
|
631
|
+
# Auto-detect common Rails views root and default prefix
|
|
632
|
+
if root.nil? && abs_path.include?(File.join('app', 'views'))
|
|
633
|
+
root = abs_path.split(File.join('app', 'views')).first + File.join('app', 'views')
|
|
634
|
+
prefix ||= 'views'
|
|
635
|
+
end
|
|
636
|
+
|
|
637
|
+
rel_path = nil
|
|
638
|
+
if root && abs_path.start_with?(File.expand_path(root) + File::SEPARATOR)
|
|
639
|
+
rel_path = Pathname.new(abs_path).relative_path_from(Pathname.new(File.expand_path(root))).to_s
|
|
640
|
+
else
|
|
641
|
+
# Try relative to current working directory
|
|
642
|
+
cwd = Dir.pwd
|
|
643
|
+
if abs_path.start_with?(cwd + File::SEPARATOR)
|
|
644
|
+
rel_path = abs_path.sub(cwd + File::SEPARATOR, '')
|
|
645
|
+
else
|
|
646
|
+
rel_path = File.basename(abs_path)
|
|
647
|
+
end
|
|
648
|
+
end
|
|
649
|
+
|
|
650
|
+
# Remove all extensions (handle multi-extensions like .html.erb)
|
|
651
|
+
base = rel_path.dup
|
|
652
|
+
loop do
|
|
653
|
+
ext = File.extname(base)
|
|
654
|
+
break if ext.nil? || ext.empty?
|
|
655
|
+
base = base.chomp(ext)
|
|
656
|
+
end
|
|
657
|
+
|
|
658
|
+
# Split into segments, normalize, and join with dots
|
|
659
|
+
segments = base.split(File::SEPARATOR).map do |seg|
|
|
660
|
+
seg.downcase
|
|
661
|
+
.gsub(/[^\w\-\.]+/, '_')
|
|
662
|
+
.gsub(/[\-\.]/, '_')
|
|
663
|
+
.gsub(/_+/, '_')
|
|
664
|
+
.gsub(/^_|_$/, '')
|
|
665
|
+
end.reject(&:empty?)
|
|
666
|
+
|
|
667
|
+
derived = segments.join('.')
|
|
668
|
+
if prefix && !prefix.to_s.empty?
|
|
669
|
+
[prefix, derived].reject(&:empty?).join('.')
|
|
670
|
+
else
|
|
671
|
+
derived
|
|
672
|
+
end
|
|
673
|
+
end
|
|
674
|
+
|
|
606
675
|
# Load locale file (YAML)
|
|
607
676
|
def self.load_locale_file(path, locale)
|
|
608
677
|
if File.exist?(path)
|