better_translate 1.1.0 → 1.1.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.
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BetterTranslate
4
+ module Analyzer
5
+ # Scans YAML translation files and extracts all keys in flatten format
6
+ class KeyScanner
7
+ # Path to the YAML file
8
+ attr_reader file_path: String
9
+
10
+ # Flatten keys extracted from YAML
11
+ attr_reader keys: Hash[String, untyped]
12
+
13
+ # Initialize scanner with YAML file path
14
+ #
15
+ # @param file_path Path to YAML file
16
+ def initialize: (String file_path) -> void
17
+
18
+ # Scan YAML file and extract all flatten keys
19
+ #
20
+ # @return Flatten keys with their values
21
+ def scan: () -> Hash[String, untyped]
22
+
23
+ # Get total count of keys
24
+ #
25
+ # @return Number of keys
26
+ def key_count: () -> Integer
27
+
28
+ private
29
+
30
+ # Validate that file exists
31
+ def validate_file!: () -> void
32
+
33
+ # Flatten nested hash into dot-notation keys
34
+ #
35
+ # @param hash Nested hash to flatten
36
+ # @param prefix Prefix for current level
37
+ def flatten_keys: (Hash[untyped, untyped] hash, ?String? prefix) -> void
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BetterTranslate
4
+ module Analyzer
5
+ # Detects orphan i18n keys (keys defined but never used in code)
6
+ class OrphanDetector
7
+ # All translation keys with their values
8
+ attr_reader all_keys: Hash[String, untyped]
9
+
10
+ # Keys that are used in code
11
+ attr_reader used_keys: Set[String]
12
+
13
+ # Orphan keys (not used in code)
14
+ attr_reader orphans: Array[String]
15
+
16
+ # Initialize detector
17
+ #
18
+ # @param all_keys All translation keys from YAML files
19
+ # @param used_keys Keys found in code
20
+ def initialize: (Hash[String, untyped] all_keys, Set[String] used_keys) -> void
21
+
22
+ # Detect orphan keys
23
+ #
24
+ # @return List of orphan key names
25
+ def detect: () -> Array[String]
26
+
27
+ # Get count of orphan keys
28
+ #
29
+ # @return Number of orphan keys
30
+ def orphan_count: () -> Integer
31
+
32
+ # Get details of orphan keys with their values
33
+ #
34
+ # @return Hash of orphan keys and their translation values
35
+ def orphan_details: () -> Hash[String, untyped]
36
+
37
+ # Calculate usage percentage
38
+ #
39
+ # @return Percentage of keys that are used (0.0 to 100.0)
40
+ def usage_percentage: () -> Float
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BetterTranslate
4
+ module Analyzer
5
+ # Generates reports for orphan key analysis
6
+ class Reporter
7
+ # List of orphan keys
8
+ attr_reader orphans: Array[String]
9
+
10
+ # Orphan keys with their values
11
+ attr_reader orphan_details: Hash[String, untyped]
12
+
13
+ # Total number of keys
14
+ attr_reader total_keys: Integer
15
+
16
+ # Number of used keys
17
+ attr_reader used_keys: Integer
18
+
19
+ # Usage percentage
20
+ attr_reader usage_percentage: Float
21
+
22
+ # Output format (:text, :json, :csv)
23
+ attr_reader format: Symbol
24
+
25
+ # Initialize reporter
26
+ #
27
+ # @param orphans List of orphan keys
28
+ # @param orphan_details Orphan keys with values
29
+ # @param total_keys Total number of keys
30
+ # @param used_keys Number of used keys
31
+ # @param usage_percentage Usage percentage
32
+ # @param format Output format (:text, :json, :csv)
33
+ def initialize: (
34
+ orphans: Array[String],
35
+ orphan_details: Hash[String, untyped],
36
+ total_keys: Integer,
37
+ used_keys: Integer,
38
+ usage_percentage: Float,
39
+ ?format: Symbol
40
+ ) -> void
41
+
42
+ # Generate report in specified format
43
+ #
44
+ # @return Generated report
45
+ def generate: () -> String
46
+
47
+ # Save report to file
48
+ #
49
+ # @param file_path Output file path
50
+ def save_to_file: (String file_path) -> void
51
+
52
+ private
53
+
54
+ # Generate text format report
55
+ #
56
+ # @return Text report
57
+ def generate_text: () -> String
58
+
59
+ # Generate JSON format report
60
+ #
61
+ # @return JSON report
62
+ def generate_json: () -> String
63
+
64
+ # Generate CSV format report
65
+ #
66
+ # @return CSV report
67
+ def generate_csv: () -> String
68
+ end
69
+ end
70
+ end
@@ -20,5 +20,7 @@ module BetterTranslate
20
20
  def run_generate: () -> void
21
21
 
22
22
  def run_direct: () -> void
23
+
24
+ def run_analyze: () -> void
23
25
  end
24
26
  end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BetterTranslate
4
+ # Handles JSON file operations
5
+ class JsonHandler
6
+ # Configuration object
7
+ attr_reader config: Configuration
8
+
9
+ # Initialize JSON handler
10
+ #
11
+ # @param config Configuration object
12
+ def initialize: (Configuration config) -> void
13
+
14
+ # Read and parse JSON file
15
+ #
16
+ # @param file_path Path to JSON file
17
+ # @return Parsed JSON content
18
+ def read_json: (String file_path) -> Hash[untyped, untyped]
19
+
20
+ # Write hash to JSON file
21
+ #
22
+ # @param file_path Output file path
23
+ # @param data Data to write
24
+ # @param diff_preview Optional diff preview instance
25
+ # @return Summary hash if dry_run, nil otherwise
26
+ def write_json: (String file_path, Hash[untyped, untyped] data, ?diff_preview: untyped) -> (Hash[Symbol, untyped] | nil)
27
+
28
+ # Get translatable strings from source JSON
29
+ #
30
+ # @return Flattened hash of translatable strings
31
+ def get_source_strings: () -> Hash[String, untyped]
32
+
33
+ # Filter out excluded keys for a specific language
34
+ #
35
+ # @param strings Flattened strings
36
+ # @param target_lang_code Target language code
37
+ # @return Filtered strings
38
+ def filter_exclusions: (Hash[String, untyped] strings, String target_lang_code) -> Hash[String, untyped]
39
+
40
+ # Merge translated strings with existing file (incremental mode)
41
+ #
42
+ # @param file_path Existing file path
43
+ # @param new_translations New translations (flattened)
44
+ # @return Merged translations (nested)
45
+ def merge_translations: (String file_path, Hash[String, untyped] new_translations) -> Hash[String, untyped]
46
+
47
+ # Build output file path for target language
48
+ #
49
+ # @param target_lang_code Target language code
50
+ # @return Output file path
51
+ def build_output_path: (String target_lang_code) -> String
52
+
53
+ private
54
+
55
+ # Create backup file with rotation support
56
+ #
57
+ # @param file_path Path to file to backup
58
+ def create_backup_file: (String file_path) -> void
59
+
60
+ # Rotate backup files, keeping only max_backups
61
+ #
62
+ # @param file_path Base file path
63
+ def rotate_backups: (String file_path) -> void
64
+ end
65
+ end
@@ -14,7 +14,7 @@ module BetterTranslate
14
14
 
15
15
  def complete: (String language, Integer total_strings) -> void
16
16
 
17
- def error: (String language, StandardError error) -> void
17
+ def error: (String language, Exception error) -> void
18
18
 
19
19
  def reset: () -> void
20
20
 
@@ -5,6 +5,10 @@
5
5
  module BetterTranslate
6
6
  type translation_results = { success_count: Integer, failure_count: Integer, errors: Array[Hash[Symbol, untyped]] }
7
7
 
8
+ # Analyzer module for translation file analysis
9
+ module Analyzer
10
+ end
11
+
8
12
  # Module-level instance variable (for singleton methods)
9
13
  self.@configuration: Configuration?
10
14
 
data/sig/csv.rbs ADDED
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ # CSV library type signatures
4
+ class CSV
5
+ # Generate CSV string
6
+ #
7
+ # @yield CSV instance for writing rows
8
+ # @return CSV string
9
+ def self.generate: () { (CSV) -> void } -> String
10
+
11
+ # Add row to CSV
12
+ #
13
+ # @param row Array of values
14
+ # @return CSV instance
15
+ def <<: (Array[untyped]) -> CSV
16
+ end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: better_translate
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - alessiobussolari
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-10-22 00:00:00.000000000 Z
11
+ date: 2025-10-24 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: csv
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: faraday
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -41,11 +55,15 @@ files:
41
55
  - CHANGELOG.md
42
56
  - CLAUDE.md
43
57
  - CODE_OF_CONDUCT.md
58
+ - CONTRIBUTING.md
44
59
  - LICENSE.txt
45
60
  - README.md
46
61
  - RELEASE_NOTES_v1.0.0.md
47
62
  - Rakefile
63
+ - SECURITY.md
48
64
  - Steepfile
65
+ - brakeman.yml
66
+ - codecov.yml
49
67
  - docs/implementation/00-overview.md
50
68
  - docs/implementation/01-setup_dependencies.md
51
69
  - docs/implementation/02-error_handling.md
@@ -99,13 +117,17 @@ files:
99
117
  - lib/generators/better_translate/translate/USAGE
100
118
  - lib/generators/better_translate/translate/translate_generator.rb
101
119
  - lib/tasks/better_translate.rake
102
- - regenerate_vcr.rb
103
120
  - sig/better_translate.rbs
121
+ - sig/better_translate/analyzer/code_scanner.rbs
122
+ - sig/better_translate/analyzer/key_scanner.rbs
123
+ - sig/better_translate/analyzer/orphan_detector.rbs
124
+ - sig/better_translate/analyzer/reporter.rbs
104
125
  - sig/better_translate/cache.rbs
105
126
  - sig/better_translate/cli.rbs
106
127
  - sig/better_translate/configuration.rbs
107
128
  - sig/better_translate/direct_translator.rbs
108
129
  - sig/better_translate/errors.rbs
130
+ - sig/better_translate/json_handler.rbs
109
131
  - sig/better_translate/progress_tracker.rbs
110
132
  - sig/better_translate/provider_factory.rbs
111
133
  - sig/better_translate/providers/anthropic_provider.rbs
@@ -124,6 +146,7 @@ files:
124
146
  - sig/better_translate/variable_extractor.rbs
125
147
  - sig/better_translate/version.rbs
126
148
  - sig/better_translate/yaml_handler.rbs
149
+ - sig/csv.rbs
127
150
  - sig/faraday.rbs
128
151
  - sig/generators/better_translate/analyze/analyze_generator.rbs
129
152
  - sig/generators/better_translate/install/install_generator.rbs
data/regenerate_vcr.rb DELETED
@@ -1,47 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- require "bundler/setup"
5
- require "dotenv/load"
6
- require "vcr"
7
- require "webmock/rspec"
8
- require_relative "lib/better_translate"
9
- require "tmpdir"
10
-
11
- # Setup VCR
12
- VCR.configure do |config|
13
- config.cassette_library_dir = "spec/vcr_cassettes"
14
- config.hook_into :webmock
15
- config.filter_sensitive_data("<GEMINI_API_KEY>") { ENV["GEMINI_API_KEY"] }
16
- config.default_cassette_options = { record: :all }
17
- end
18
-
19
- test_dir = Dir.mktmpdir("gemini_test")
20
- puts "Test output dir: #{test_dir}"
21
-
22
- VCR.use_cassette("rails/dummy_app_gemini_translation", record: :all) do
23
- config = BetterTranslate::Configuration.new
24
- config.provider = :gemini
25
- config.gemini_key = ENV["GEMINI_API_KEY"]
26
- config.source_language = "en"
27
- config.target_languages = [{ short_name: "fr", name: "French" }]
28
- config.input_file = "spec/dummy/config/locales/en.yml"
29
- config.output_folder = test_dir
30
- config.cache_enabled = false
31
- config.verbose = true
32
- config.validate!
33
-
34
- puts "Starting translation..."
35
- translator = BetterTranslate::Translator.new(config)
36
- results = translator.translate_all
37
-
38
- puts "\nResults: #{results.inspect}"
39
- puts "\nFiles created:"
40
- Dir.entries(test_dir).each { |f| puts " - #{f}" unless f.start_with?(".") }
41
-
42
- if results[:success_count].positive?
43
- puts "\n✓ Successfully regenerated VCR cassette!"
44
- else
45
- puts "\n✗ Translation failed"
46
- end
47
- end