better_translate 0.5.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.
Files changed (100) hide show
  1. checksums.yaml +4 -4
  2. data/.env.example +14 -0
  3. data/.rspec +3 -0
  4. data/.rubocop.yml +8 -0
  5. data/.yardopts +10 -0
  6. data/CHANGELOG.md +125 -114
  7. data/CLAUDE.md +385 -0
  8. data/README.md +629 -244
  9. data/Rakefile +7 -1
  10. data/Steepfile +29 -0
  11. data/docs/implementation/00-overview.md +220 -0
  12. data/docs/implementation/01-setup_dependencies.md +668 -0
  13. data/docs/implementation/02-error_handling.md +65 -0
  14. data/docs/implementation/03-core_components.md +457 -0
  15. data/docs/implementation/03.5-variable_preservation.md +509 -0
  16. data/docs/implementation/04-provider_architecture.md +571 -0
  17. data/docs/implementation/05-translation_logic.md +1065 -0
  18. data/docs/implementation/06-main_module_api.md +122 -0
  19. data/docs/implementation/07-direct_translation_helpers.md +582 -0
  20. data/docs/implementation/08-rails_integration.md +323 -0
  21. data/docs/implementation/09-testing_suite.md +228 -0
  22. data/docs/implementation/10-documentation_examples.md +150 -0
  23. data/docs/implementation/11-quality_security.md +65 -0
  24. data/docs/implementation/12-cli_standalone.md +698 -0
  25. data/exe/better_translate +9 -0
  26. data/lib/better_translate/cache.rb +125 -0
  27. data/lib/better_translate/cli.rb +304 -0
  28. data/lib/better_translate/configuration.rb +201 -0
  29. data/lib/better_translate/direct_translator.rb +131 -0
  30. data/lib/better_translate/errors.rb +101 -0
  31. data/lib/better_translate/progress_tracker.rb +157 -0
  32. data/lib/better_translate/provider_factory.rb +45 -0
  33. data/lib/better_translate/providers/anthropic_provider.rb +154 -0
  34. data/lib/better_translate/providers/base_http_provider.rb +239 -0
  35. data/lib/better_translate/providers/chatgpt_provider.rb +138 -44
  36. data/lib/better_translate/providers/gemini_provider.rb +123 -61
  37. data/lib/better_translate/railtie.rb +18 -0
  38. data/lib/better_translate/rate_limiter.rb +90 -0
  39. data/lib/better_translate/strategies/base_strategy.rb +58 -0
  40. data/lib/better_translate/strategies/batch_strategy.rb +56 -0
  41. data/lib/better_translate/strategies/deep_strategy.rb +45 -0
  42. data/lib/better_translate/strategies/strategy_selector.rb +43 -0
  43. data/lib/better_translate/translator.rb +115 -284
  44. data/lib/better_translate/utils/hash_flattener.rb +104 -0
  45. data/lib/better_translate/validator.rb +105 -0
  46. data/lib/better_translate/variable_extractor.rb +259 -0
  47. data/lib/better_translate/version.rb +2 -9
  48. data/lib/better_translate/yaml_handler.rb +168 -0
  49. data/lib/better_translate.rb +97 -73
  50. data/lib/generators/better_translate/analyze/USAGE +12 -0
  51. data/lib/generators/better_translate/analyze/analyze_generator.rb +94 -0
  52. data/lib/generators/better_translate/install/USAGE +13 -0
  53. data/lib/generators/better_translate/install/install_generator.rb +71 -0
  54. data/lib/generators/better_translate/install/templates/README +20 -0
  55. data/lib/generators/better_translate/install/templates/initializer.rb.tt +47 -0
  56. data/lib/generators/better_translate/translate/USAGE +13 -0
  57. data/lib/generators/better_translate/translate/translate_generator.rb +114 -0
  58. data/lib/tasks/better_translate.rake +136 -0
  59. data/sig/better_translate/cache.rbs +28 -0
  60. data/sig/better_translate/cli.rbs +24 -0
  61. data/sig/better_translate/configuration.rbs +78 -0
  62. data/sig/better_translate/direct_translator.rbs +18 -0
  63. data/sig/better_translate/errors.rbs +46 -0
  64. data/sig/better_translate/progress_tracker.rbs +29 -0
  65. data/sig/better_translate/provider_factory.rbs +8 -0
  66. data/sig/better_translate/providers/anthropic_provider.rbs +27 -0
  67. data/sig/better_translate/providers/base_http_provider.rbs +44 -0
  68. data/sig/better_translate/providers/chatgpt_provider.rbs +25 -0
  69. data/sig/better_translate/providers/gemini_provider.rbs +22 -0
  70. data/sig/better_translate/railtie.rbs +7 -0
  71. data/sig/better_translate/rate_limiter.rbs +20 -0
  72. data/sig/better_translate/strategies/base_strategy.rbs +19 -0
  73. data/sig/better_translate/strategies/batch_strategy.rbs +13 -0
  74. data/sig/better_translate/strategies/deep_strategy.rbs +11 -0
  75. data/sig/better_translate/strategies/strategy_selector.rbs +10 -0
  76. data/sig/better_translate/translator.rbs +24 -0
  77. data/sig/better_translate/utils/hash_flattener.rbs +14 -0
  78. data/sig/better_translate/validator.rbs +14 -0
  79. data/sig/better_translate/variable_extractor.rbs +40 -0
  80. data/sig/better_translate/version.rbs +4 -0
  81. data/sig/better_translate/yaml_handler.rbs +29 -0
  82. data/sig/better_translate.rbs +32 -2
  83. data/sig/faraday.rbs +22 -0
  84. data/sig/generators/better_translate/analyze/analyze_generator.rbs +18 -0
  85. data/sig/generators/better_translate/install/install_generator.rbs +14 -0
  86. data/sig/generators/better_translate/translate/translate_generator.rbs +10 -0
  87. data/sig/optparse.rbs +9 -0
  88. data/sig/psych.rbs +5 -0
  89. data/sig/rails.rbs +34 -0
  90. metadata +89 -203
  91. data/lib/better_translate/helper.rb +0 -83
  92. data/lib/better_translate/providers/base_provider.rb +0 -102
  93. data/lib/better_translate/service.rb +0 -144
  94. data/lib/better_translate/similarity_analyzer.rb +0 -218
  95. data/lib/better_translate/utils.rb +0 -55
  96. data/lib/better_translate/writer.rb +0 -75
  97. data/lib/generators/better_translate/analyze_generator.rb +0 -57
  98. data/lib/generators/better_translate/install_generator.rb +0 -14
  99. data/lib/generators/better_translate/templates/better_translate.rb +0 -56
  100. data/lib/generators/better_translate/translate_generator.rb +0 -84
@@ -0,0 +1,323 @@
1
+ # 08 - Rails Integration
2
+
3
+ [← Previous: 07-Direct Translation Helpers](./07-direct_translation_helpers.md) | [Back to Index](../../IMPLEMENTATION_PLAN.md) | [Next: 09-Testing Suite →](./09-testing_suite.md)
4
+
5
+ ---
6
+
7
+ ## Rails Integration
8
+
9
+ ### 7.1 `lib/generators/better_translate/install/install_generator.rb`
10
+
11
+ ```ruby
12
+ # frozen_string_literal: true
13
+
14
+ require "rails/generators/base"
15
+
16
+ module BetterTranslate
17
+ module Generators
18
+ # Generator to install BetterTranslate in a Rails application
19
+ #
20
+ # Creates an initializer file with example configuration.
21
+ #
22
+ # @example
23
+ # rails generate better_translate:install
24
+ class InstallGenerator < Rails::Generators::Base
25
+ source_root File.expand_path("templates", __dir__)
26
+
27
+ desc "Creates a BetterTranslate initializer file"
28
+
29
+ # Create initializer file
30
+ #
31
+ # @return [void]
32
+ def create_initializer_file
33
+ template "initializer.rb.tt", "config/initializers/better_translate.rb"
34
+ end
35
+
36
+ # Display post-install message
37
+ #
38
+ # @return [void]
39
+ def show_readme
40
+ readme "INSTALL" if behavior == :invoke
41
+ end
42
+
43
+ private
44
+
45
+ def readme(file)
46
+ say "\n" + "=" * 80
47
+ say "BetterTranslate installed successfully!"
48
+ say "=" * 80
49
+ say "\nNext steps:"
50
+ say "1. Edit config/initializers/better_translate.rb with your settings"
51
+ say "2. Set your API keys in environment variables:"
52
+ say " - OPENAI_API_KEY (for ChatGPT)"
53
+ say " - GEMINI_API_KEY (for Google Gemini)"
54
+ say " - ANTHROPIC_API_KEY (for Anthropic Claude)"
55
+ say "3. Run: rails generate better_translate:translate"
56
+ say "\n" + "=" * 80 + "\n"
57
+ end
58
+ end
59
+ end
60
+ end
61
+ ```
62
+
63
+ ### 7.2 `lib/generators/better_translate/install/templates/initializer.rb.tt`
64
+
65
+ ```ruby
66
+ # frozen_string_literal: true
67
+
68
+ # BetterTranslate Configuration
69
+ #
70
+ # For more information, see: https://github.com/alessiobussolari/better_translate
71
+
72
+ BetterTranslate.configure do |config|
73
+ # ==============================
74
+ # REQUIRED SETTINGS
75
+ # ==============================
76
+
77
+ # Provider: :chatgpt, :gemini, or :anthropic
78
+ config.provider = :chatgpt
79
+
80
+ # API Keys (use environment variables for security)
81
+ config.openai_key = ENV["OPENAI_API_KEY"] # For ChatGPT
82
+ # config.google_gemini_key = ENV["GEMINI_API_KEY"] # For Gemini
83
+ # config.anthropic_key = ENV["ANTHROPIC_API_KEY"] # For Anthropic
84
+
85
+ # Source language code (2 letters)
86
+ config.source_language = "en"
87
+
88
+ # Target languages
89
+ config.target_languages = [
90
+ { short_name: "it", name: "Italian" },
91
+ { short_name: "fr", name: "French" },
92
+ { short_name: "de", name: "German" },
93
+ { short_name: "es", name: "Spanish" }
94
+ ]
95
+
96
+ # File paths
97
+ config.input_file = Rails.root.join("config", "locales", "en.yml").to_s
98
+ config.output_folder = Rails.root.join("config", "locales").to_s
99
+
100
+ # ==============================
101
+ # OPTIONAL SETTINGS
102
+ # ==============================
103
+
104
+ # Translation mode: :override or :incremental
105
+ # - :override replaces entire files
106
+ # - :incremental only translates missing keys
107
+ config.translation_mode = :override
108
+
109
+ # Domain-specific context for better translations
110
+ # config.translation_context = "E-commerce product descriptions and checkout flow"
111
+
112
+ # Caching
113
+ config.cache_enabled = true
114
+ config.cache_size = 1000
115
+ # config.cache_ttl = 3600 # Seconds (nil = no expiration)
116
+
117
+ # Performance
118
+ config.max_concurrent_requests = 3
119
+ config.request_timeout = 30
120
+ config.max_retries = 3
121
+ config.retry_delay = 2.0
122
+
123
+ # Logging
124
+ config.verbose = true
125
+
126
+ # Dry run (don't write files, just test)
127
+ # config.dry_run = false
128
+
129
+ # Exclusions (keys to never translate)
130
+ # config.global_exclusions = ["app.name", "brand.logo_url"]
131
+ # config.exclusions_per_language = {
132
+ # "fr" => ["legal.disclaimer"],
133
+ # "de" => ["privacy.gdpr_text"]
134
+ # }
135
+ end
136
+ ```
137
+
138
+ ### 7.3 `lib/generators/better_translate/translate/translate_generator.rb`
139
+
140
+ ```ruby
141
+ # frozen_string_literal: true
142
+
143
+ require "rails/generators/base"
144
+
145
+ module BetterTranslate
146
+ module Generators
147
+ # Generator to run BetterTranslate translation
148
+ #
149
+ # Triggers the translation process using the current configuration.
150
+ #
151
+ # @example
152
+ # rails generate better_translate:translate
153
+ class TranslateGenerator < Rails::Generators::Base
154
+ desc "Run BetterTranslate translation"
155
+
156
+ # Run translation
157
+ #
158
+ # @return [void]
159
+ def run_translation
160
+ say "Starting translation process...", :green
161
+
162
+ begin
163
+ results = BetterTranslate.translate_all
164
+
165
+ say "\n" + "=" * 80, :green
166
+ say "Translation completed!", :green
167
+ say "=" * 80, :green
168
+ say "✓ Successful: #{results[:success_count]} languages", :green
169
+ say "✗ Failed: #{results[:failure_count]} languages", :red if results[:failure_count] > 0
170
+
171
+ if results[:errors].any?
172
+ say "\nErrors:", :red
173
+ results[:errors].each do |error|
174
+ say " - #{error[:language]}: #{error[:error]}", :red
175
+ end
176
+ end
177
+
178
+ say "=" * 80 + "\n", :green
179
+
180
+ rescue BetterTranslate::ConfigurationError => e
181
+ say "Configuration error: #{e.message}", :red
182
+ say "\nPlease check config/initializers/better_translate.rb", :yellow
183
+ exit 1
184
+ rescue StandardError => e
185
+ say "Unexpected error: #{e.message}", :red
186
+ say e.backtrace.join("\n"), :red
187
+ exit 1
188
+ end
189
+ end
190
+ end
191
+ end
192
+ end
193
+ ```
194
+
195
+ ### 7.4 `lib/generators/better_translate/analyze/analyze_generator.rb`
196
+
197
+ ```ruby
198
+ # frozen_string_literal: true
199
+
200
+ require "rails/generators/base"
201
+
202
+ module BetterTranslate
203
+ module Generators
204
+ # Generator to analyze translation similarities
205
+ #
206
+ # Uses Levenshtein distance to detect potential translation issues.
207
+ #
208
+ # @example
209
+ # rails generate better_translate:analyze
210
+ class AnalyzeGenerator < Rails::Generators::Base
211
+ desc "Analyze translation similarities using Levenshtein distance"
212
+
213
+ # Run analysis
214
+ #
215
+ # @return [void]
216
+ def run_analysis
217
+ say "Analyzing translations...", :cyan
218
+
219
+ config = BetterTranslate.configuration
220
+ raise "BetterTranslate not configured" unless config
221
+
222
+ yaml_handler = YAMLHandler.new(config)
223
+ source_strings = yaml_handler.get_source_strings
224
+
225
+ results = []
226
+
227
+ config.target_languages.each do |lang|
228
+ target_path = yaml_handler.build_output_path(lang[:short_name])
229
+ next unless File.exist?(target_path)
230
+
231
+ target_data = yaml_handler.read_yaml(target_path)
232
+ target_strings = Utils::HashFlattener.flatten(target_data[lang[:short_name]] || target_data)
233
+
234
+ source_strings.each do |key, source_value|
235
+ target_value = target_strings[key]
236
+ next unless target_value
237
+
238
+ similarity = calculate_similarity(source_value, target_value)
239
+
240
+ # Flag if too similar (might indicate untranslated text)
241
+ if similarity > 0.8 && source_value.length > 10
242
+ results << {
243
+ language: lang[:name],
244
+ key: key,
245
+ source: source_value,
246
+ target: target_value,
247
+ similarity: (similarity * 100).round(1)
248
+ }
249
+ end
250
+ end
251
+ end
252
+
253
+ display_results(results)
254
+ end
255
+
256
+ private
257
+
258
+ # Calculate Levenshtein similarity (0.0 to 1.0)
259
+ def calculate_similarity(str1, str2)
260
+ return 1.0 if str1 == str2
261
+ return 0.0 if str1.empty? || str2.empty?
262
+
263
+ distance = levenshtein_distance(str1.downcase, str2.downcase)
264
+ max_length = [str1.length, str2.length].max
265
+
266
+ 1.0 - (distance.to_f / max_length)
267
+ end
268
+
269
+ # Calculate Levenshtein distance
270
+ def levenshtein_distance(str1, str2)
271
+ matrix = Array.new(str1.length + 1) { Array.new(str2.length + 1) }
272
+
273
+ (0..str1.length).each { |i| matrix[i][0] = i }
274
+ (0..str2.length).each { |j| matrix[0][j] = j }
275
+
276
+ (1..str1.length).each do |i|
277
+ (1..str2.length).each do |j|
278
+ cost = str1[i - 1] == str2[j - 1] ? 0 : 1
279
+ matrix[i][j] = [
280
+ matrix[i - 1][j] + 1, # deletion
281
+ matrix[i][j - 1] + 1, # insertion
282
+ matrix[i - 1][j - 1] + cost # substitution
283
+ ].min
284
+ end
285
+ end
286
+
287
+ matrix[str1.length][str2.length]
288
+ end
289
+
290
+ def display_results(results)
291
+ if results.empty?
292
+ say "\n✓ No suspicious translations found!", :green
293
+ return
294
+ end
295
+
296
+ say "\n⚠ Found #{results.size} potentially untranslated strings:", :yellow
297
+ say "=" * 80, :yellow
298
+
299
+ results.each do |result|
300
+ say "\nLanguage: #{result[:language]}", :cyan
301
+ say "Key: #{result[:key]}", :white
302
+ say "Source: #{result[:source]}", :white
303
+ say "Target: #{result[:target]}", :white
304
+ say "Similarity: #{result[:similarity]}%", :yellow
305
+ end
306
+
307
+ say "\n" + "=" * 80, :yellow
308
+ say "\nNote: High similarity might indicate:", :white
309
+ say " - Untranslated text (e.g., brand names that shouldn't be translated)"
310
+ say " - Proper nouns or technical terms"
311
+ say " - Translation errors"
312
+ say "\nReview these translations and add to exclusions if needed.", :white
313
+ end
314
+ end
315
+ end
316
+ end
317
+ ```
318
+
319
+ ---
320
+
321
+ ---
322
+
323
+ [← Previous: 07-Direct Translation Helpers](./07-direct_translation_helpers.md) | [Back to Index](../../IMPLEMENTATION_PLAN.md) | [Next: 09-Testing Suite →](./09-testing_suite.md)
@@ -0,0 +1,228 @@
1
+ # 09 - Testing Suite
2
+
3
+ [← Previous: 08-Rails Integration](./08-rails_integration.md) | [Back to Index](../../IMPLEMENTATION_PLAN.md) | [Next: 10-Documentation Examples →](./10-documentation_examples.md)
4
+
5
+ ---
6
+
7
+ ## Testing Suite
8
+
9
+ ### 8.1 Aggiornare `spec/spec_helper.rb`
10
+
11
+ ```ruby
12
+ # frozen_string_literal: true
13
+
14
+ require "better_translate"
15
+ require "webmock/rspec"
16
+ require "vcr"
17
+
18
+ # Load support files
19
+ Dir[File.expand_path("support/**/*.rb", __dir__)].sort.each { |f| require f }
20
+
21
+ RSpec.configure do |config|
22
+ config.example_status_persistence_file_path = ".rspec_status"
23
+ config.disable_monkey_patching!
24
+
25
+ config.expect_with :rspec do |c|
26
+ c.syntax = :expect
27
+ end
28
+
29
+ # Disable external HTTP requests
30
+ config.before(:each) do
31
+ WebMock.disable_net_connect!(allow_localhost: true)
32
+ end
33
+
34
+ # Reset BetterTranslate configuration before each test
35
+ config.before(:each) do
36
+ BetterTranslate.reset!
37
+ end
38
+ end
39
+ ```
40
+
41
+ ### 8.2 `spec/support/vcr.rb`
42
+
43
+ ```ruby
44
+ # frozen_string_literal: true
45
+
46
+ require "vcr"
47
+
48
+ VCR.configure do |config|
49
+ config.cassette_library_dir = "spec/vcr_cassettes"
50
+ config.hook_into :webmock
51
+ config.configure_rspec_metadata!
52
+
53
+ # Filter sensitive data
54
+ config.filter_sensitive_data("<OPENAI_API_KEY>") { ENV["OPENAI_API_KEY"] }
55
+ config.filter_sensitive_data("<GEMINI_API_KEY>") { ENV["GEMINI_API_KEY"] }
56
+ config.filter_sensitive_data("<ANTHROPIC_API_KEY>") { ENV["ANTHROPIC_API_KEY"] }
57
+
58
+ # Allow real HTTP for specific tests if needed
59
+ config.allow_http_connections_when_no_cassette = false
60
+
61
+ # Default cassette options
62
+ config.default_cassette_options = {
63
+ record: :once,
64
+ match_requests_on: [:method, :uri, :body]
65
+ }
66
+ end
67
+ ```
68
+
69
+ ### 8.3 `spec/support/test_helpers.rb`
70
+
71
+ ```ruby
72
+ # frozen_string_literal: true
73
+
74
+ module TestHelpers
75
+ # Create a temporary YAML file for testing
76
+ #
77
+ # @param content [Hash] YAML content
78
+ # @return [String] Path to temporary file
79
+ def create_temp_yaml(content)
80
+ file = Tempfile.new(["test", ".yml"])
81
+ file.write(YAML.dump(content))
82
+ file.close
83
+ file.path
84
+ end
85
+
86
+ # Create a test configuration
87
+ #
88
+ # @param overrides [Hash] Configuration overrides
89
+ # @return [BetterTranslate::Configuration] Configuration object
90
+ def build_config(**overrides)
91
+ config = BetterTranslate::Configuration.new
92
+
93
+ # Set defaults
94
+ config.provider = :chatgpt
95
+ config.openai_key = "test-api-key"
96
+ config.source_language = "en"
97
+ config.target_languages = [{ short_name: "it", name: "Italian" }]
98
+ config.input_file = create_temp_yaml("en" => { "hello" => "Hello" })
99
+ config.output_folder = Dir.mktmpdir
100
+
101
+ # Apply overrides
102
+ overrides.each do |key, value|
103
+ config.send("#{key}=", value)
104
+ end
105
+
106
+ config
107
+ end
108
+ end
109
+
110
+ RSpec.configure do |config|
111
+ config.include TestHelpers
112
+ end
113
+ ```
114
+
115
+ ### 8.4 `spec/fixtures/en.yml`
116
+
117
+ ```yaml
118
+ en:
119
+ hello: "Hello"
120
+ goodbye: "Goodbye"
121
+ welcome:
122
+ message: "Welcome to our application"
123
+ subtitle: "We're glad you're here"
124
+ user:
125
+ profile:
126
+ name: "Name"
127
+ email: "Email"
128
+ password: "Password"
129
+ errors:
130
+ not_found: "Resource not found"
131
+ unauthorized: "You are not authorized"
132
+ ```
133
+
134
+ ### 8.5 Test Files Listing
135
+
136
+ Creare questi spec files (esempio di struttura per ognuno):
137
+
138
+ - `spec/better_translate/configuration_spec.rb` - Test validazione e configurazione
139
+ - `spec/better_translate/cache_spec.rb` - Test LRU cache
140
+ - `spec/better_translate/rate_limiter_spec.rb` - Test rate limiting
141
+ - `spec/better_translate/validator_spec.rb` - Test validazione input
142
+ - `spec/better_translate/yaml_handler_spec.rb` - Test YAML operations
143
+ - `spec/better_translate/translator_spec.rb` - Test main translator
144
+ - `spec/better_translate/progress_tracker_spec.rb` - Test progress display
145
+ - `spec/better_translate/provider_factory_spec.rb` - Test factory pattern
146
+ - `spec/better_translate/providers/chatgpt_provider_spec.rb` - Test ChatGPT provider
147
+ - `spec/better_translate/providers/gemini_provider_spec.rb` - Test Gemini provider
148
+ - `spec/better_translate/providers/anthropic_provider_spec.rb` - Test Anthropic provider
149
+ - `spec/better_translate/strategies/deep_strategy_spec.rb` - Test deep strategy
150
+ - `spec/better_translate/strategies/batch_strategy_spec.rb` - Test batch strategy
151
+ - `spec/better_translate/utils/hash_flattener_spec.rb` - Test hash utilities
152
+ - `spec/integration/translation_workflow_spec.rb` - End-to-end integration tests
153
+
154
+ ### 8.6 Esempio Test: `spec/better_translate/configuration_spec.rb`
155
+
156
+ ```ruby
157
+ # frozen_string_literal: true
158
+
159
+ RSpec.describe BetterTranslate::Configuration do
160
+ describe "#initialize" do
161
+ it "sets default values" do
162
+ config = described_class.new
163
+
164
+ expect(config.translation_mode).to eq(:override)
165
+ expect(config.max_concurrent_requests).to eq(3)
166
+ expect(config.request_timeout).to eq(30)
167
+ expect(config.cache_enabled).to be true
168
+ expect(config.cache_size).to eq(1000)
169
+ expect(config.verbose).to be false
170
+ end
171
+ end
172
+
173
+ describe "#validate!" do
174
+ let(:config) { build_config }
175
+
176
+ it "validates successfully with all required fields" do
177
+ expect { config.validate! }.not_to raise_error
178
+ end
179
+
180
+ it "raises error when provider is nil" do
181
+ config.provider = nil
182
+ expect { config.validate! }.to raise_error(
183
+ BetterTranslate::ConfigurationError,
184
+ "Provider must be set"
185
+ )
186
+ end
187
+
188
+ it "raises error when API key is missing for ChatGPT" do
189
+ config.provider = :chatgpt
190
+ config.openai_key = nil
191
+ expect { config.validate! }.to raise_error(
192
+ BetterTranslate::ConfigurationError,
193
+ /OpenAI API key is required/
194
+ )
195
+ end
196
+
197
+ it "raises error when source language is empty" do
198
+ config.source_language = ""
199
+ expect { config.validate! }.to raise_error(
200
+ BetterTranslate::ConfigurationError,
201
+ "Source language must be set"
202
+ )
203
+ end
204
+
205
+ it "raises error when target languages is empty" do
206
+ config.target_languages = []
207
+ expect { config.validate! }.to raise_error(
208
+ BetterTranslate::ConfigurationError,
209
+ "At least one target language is required"
210
+ )
211
+ end
212
+
213
+ it "raises error when input file does not exist" do
214
+ config.input_file = "/non/existent/file.yml"
215
+ expect { config.validate! }.to raise_error(
216
+ BetterTranslate::ConfigurationError,
217
+ /Input file does not exist/
218
+ )
219
+ end
220
+ end
221
+ end
222
+ ```
223
+
224
+ ---
225
+
226
+ ---
227
+
228
+ [← Previous: 08-Rails Integration](./08-rails_integration.md) | [Back to Index](../../IMPLEMENTATION_PLAN.md) | [Next: 10-Documentation Examples →](./10-documentation_examples.md)
@@ -0,0 +1,150 @@
1
+ # 10 - Documentation & Examples
2
+
3
+ [← Previous: 09-Testing Suite](./09-testing_suite.md) | [Back to Index](../../IMPLEMENTATION_PLAN.md) | [Next: 11-Quality Security →](./11-quality_security.md)
4
+
5
+ ---
6
+
7
+ ## Documentation & Examples
8
+
9
+ ### 9.1 Creare `examples/basic_usage.rb`
10
+
11
+ ```ruby
12
+ #!/usr/bin/env ruby
13
+ # frozen_string_literal: true
14
+
15
+ require "bundler/setup"
16
+ require "better_translate"
17
+
18
+ # Basic usage example
19
+ BetterTranslate.configure do |config|
20
+ config.provider = :chatgpt
21
+ config.openai_key = ENV["OPENAI_API_KEY"]
22
+ config.source_language = "en"
23
+ config.target_languages = [
24
+ { short_name: "it", name: "Italian" },
25
+ { short_name: "fr", name: "French" }
26
+ ]
27
+ config.input_file = "config/locales/en.yml"
28
+ config.output_folder = "config/locales"
29
+ config.verbose = true
30
+ end
31
+
32
+ puts "Starting translation..."
33
+ results = BetterTranslate.translate_all
34
+
35
+ puts "\n" + "=" * 80
36
+ puts "Translation Results:"
37
+ puts " ✓ Success: #{results[:success_count]} languages"
38
+ puts " ✗ Failed: #{results[:failure_count]} languages"
39
+
40
+ if results[:errors].any?
41
+ puts "\nErrors:"
42
+ results[:errors].each do |error|
43
+ puts " - #{error[:language]}: #{error[:error]}"
44
+ end
45
+ end
46
+ puts "=" * 80
47
+ ```
48
+
49
+ ### 9.2 Creare `examples/advanced_usage.rb`
50
+
51
+ ```ruby
52
+ #!/usr/bin/env ruby
53
+ # frozen_string_literal: true
54
+
55
+ require "bundler/setup"
56
+ require "better_translate"
57
+
58
+ # Advanced usage with all options
59
+ BetterTranslate.configure do |config|
60
+ # Provider selection
61
+ config.provider = :anthropic
62
+ config.anthropic_key = ENV["ANTHROPIC_API_KEY"]
63
+
64
+ # Languages
65
+ config.source_language = "en"
66
+ config.target_languages = [
67
+ { short_name: "it", name: "Italian" },
68
+ { short_name: "fr", name: "French" },
69
+ { short_name: "de", name: "German" },
70
+ { short_name: "es", name: "Spanish" }
71
+ ]
72
+
73
+ # Files
74
+ config.input_file = "config/locales/en.yml"
75
+ config.output_folder = "config/locales"
76
+
77
+ # Translation mode
78
+ config.translation_mode = :incremental # Preserve existing translations
79
+
80
+ # Domain-specific context
81
+ config.translation_context = "Medical terminology for healthcare applications"
82
+
83
+ # Caching
84
+ config.cache_enabled = true
85
+ config.cache_size = 2000
86
+ config.cache_ttl = 3600 # 1 hour
87
+
88
+ # Performance tuning
89
+ config.max_concurrent_requests = 5
90
+ config.request_timeout = 60
91
+ config.max_retries = 5
92
+ config.retry_delay = 3.0
93
+
94
+ # Logging
95
+ config.verbose = true
96
+
97
+ # Exclusions
98
+ config.global_exclusions = ["app.name", "company.logo_url"]
99
+ config.exclusions_per_language = {
100
+ "fr" => ["legal.disclaimer"], # French has custom legal text
101
+ "de" => ["privacy.gdpr_text"] # German GDPR text is manually translated
102
+ }
103
+
104
+ # Dry run (don't actually write files)
105
+ # config.dry_run = true
106
+ end
107
+
108
+ puts "Advanced Translation Configuration"
109
+ puts "=" * 80
110
+ puts "Provider: #{BetterTranslate.configuration.provider}"
111
+ puts "Languages: #{BetterTranslate.configuration.target_languages.map { |l| l[:name] }.join(", ")}"
112
+ puts "Mode: #{BetterTranslate.configuration.translation_mode}"
113
+ puts "Cache: #{BetterTranslate.configuration.cache_enabled ? "enabled" : "disabled"}"
114
+ puts "=" * 80
115
+ puts "\nStarting translation...\n"
116
+
117
+ results = BetterTranslate.translate_all
118
+
119
+ puts "\n" + "=" * 80
120
+ puts "Translation Results:"
121
+ puts " ✓ Success: #{results[:success_count]} languages"
122
+ puts " ✗ Failed: #{results[:failure_count]} languages"
123
+
124
+ if results[:errors].any?
125
+ puts "\nErrors:"
126
+ results[:errors].each do |error|
127
+ puts " - #{error[:language]}: #{error[:error]}"
128
+ puts " Context: #{error[:context]}" if error[:context].any?
129
+ end
130
+ end
131
+ puts "=" * 80
132
+ ```
133
+
134
+ ### 9.3 Aggiornare `README.md`
135
+
136
+ Il README dovrebbe includere:
137
+ - Installation instructions
138
+ - Quick start guide
139
+ - Configuration options
140
+ - Usage examples
141
+ - Rails integration
142
+ - Supported providers
143
+ - Contributing guidelines
144
+ - License
145
+
146
+ ---
147
+
148
+ ---
149
+
150
+ [← Previous: 09-Testing Suite](./09-testing_suite.md) | [Back to Index](../../IMPLEMENTATION_PLAN.md) | [Next: 11-Quality Security →](./11-quality_security.md)