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.
- checksums.yaml +4 -4
- data/.env.example +14 -0
- data/.rspec +3 -0
- data/.rubocop.yml +8 -0
- data/.yardopts +10 -0
- data/CHANGELOG.md +125 -114
- data/CLAUDE.md +385 -0
- data/README.md +629 -244
- data/Rakefile +7 -1
- data/Steepfile +29 -0
- data/docs/implementation/00-overview.md +220 -0
- data/docs/implementation/01-setup_dependencies.md +668 -0
- data/docs/implementation/02-error_handling.md +65 -0
- data/docs/implementation/03-core_components.md +457 -0
- data/docs/implementation/03.5-variable_preservation.md +509 -0
- data/docs/implementation/04-provider_architecture.md +571 -0
- data/docs/implementation/05-translation_logic.md +1065 -0
- data/docs/implementation/06-main_module_api.md +122 -0
- data/docs/implementation/07-direct_translation_helpers.md +582 -0
- data/docs/implementation/08-rails_integration.md +323 -0
- data/docs/implementation/09-testing_suite.md +228 -0
- data/docs/implementation/10-documentation_examples.md +150 -0
- data/docs/implementation/11-quality_security.md +65 -0
- data/docs/implementation/12-cli_standalone.md +698 -0
- data/exe/better_translate +9 -0
- data/lib/better_translate/cache.rb +125 -0
- data/lib/better_translate/cli.rb +304 -0
- data/lib/better_translate/configuration.rb +201 -0
- data/lib/better_translate/direct_translator.rb +131 -0
- data/lib/better_translate/errors.rb +101 -0
- data/lib/better_translate/progress_tracker.rb +157 -0
- data/lib/better_translate/provider_factory.rb +45 -0
- data/lib/better_translate/providers/anthropic_provider.rb +154 -0
- data/lib/better_translate/providers/base_http_provider.rb +239 -0
- data/lib/better_translate/providers/chatgpt_provider.rb +138 -44
- data/lib/better_translate/providers/gemini_provider.rb +123 -61
- data/lib/better_translate/railtie.rb +18 -0
- data/lib/better_translate/rate_limiter.rb +90 -0
- data/lib/better_translate/strategies/base_strategy.rb +58 -0
- data/lib/better_translate/strategies/batch_strategy.rb +56 -0
- data/lib/better_translate/strategies/deep_strategy.rb +45 -0
- data/lib/better_translate/strategies/strategy_selector.rb +43 -0
- data/lib/better_translate/translator.rb +115 -284
- data/lib/better_translate/utils/hash_flattener.rb +104 -0
- data/lib/better_translate/validator.rb +105 -0
- data/lib/better_translate/variable_extractor.rb +259 -0
- data/lib/better_translate/version.rb +2 -9
- data/lib/better_translate/yaml_handler.rb +168 -0
- data/lib/better_translate.rb +97 -73
- data/lib/generators/better_translate/analyze/USAGE +12 -0
- data/lib/generators/better_translate/analyze/analyze_generator.rb +94 -0
- data/lib/generators/better_translate/install/USAGE +13 -0
- data/lib/generators/better_translate/install/install_generator.rb +71 -0
- data/lib/generators/better_translate/install/templates/README +20 -0
- data/lib/generators/better_translate/install/templates/initializer.rb.tt +47 -0
- data/lib/generators/better_translate/translate/USAGE +13 -0
- data/lib/generators/better_translate/translate/translate_generator.rb +114 -0
- data/lib/tasks/better_translate.rake +136 -0
- data/sig/better_translate/cache.rbs +28 -0
- data/sig/better_translate/cli.rbs +24 -0
- data/sig/better_translate/configuration.rbs +78 -0
- data/sig/better_translate/direct_translator.rbs +18 -0
- data/sig/better_translate/errors.rbs +46 -0
- data/sig/better_translate/progress_tracker.rbs +29 -0
- data/sig/better_translate/provider_factory.rbs +8 -0
- data/sig/better_translate/providers/anthropic_provider.rbs +27 -0
- data/sig/better_translate/providers/base_http_provider.rbs +44 -0
- data/sig/better_translate/providers/chatgpt_provider.rbs +25 -0
- data/sig/better_translate/providers/gemini_provider.rbs +22 -0
- data/sig/better_translate/railtie.rbs +7 -0
- data/sig/better_translate/rate_limiter.rbs +20 -0
- data/sig/better_translate/strategies/base_strategy.rbs +19 -0
- data/sig/better_translate/strategies/batch_strategy.rbs +13 -0
- data/sig/better_translate/strategies/deep_strategy.rbs +11 -0
- data/sig/better_translate/strategies/strategy_selector.rbs +10 -0
- data/sig/better_translate/translator.rbs +24 -0
- data/sig/better_translate/utils/hash_flattener.rbs +14 -0
- data/sig/better_translate/validator.rbs +14 -0
- data/sig/better_translate/variable_extractor.rbs +40 -0
- data/sig/better_translate/version.rbs +4 -0
- data/sig/better_translate/yaml_handler.rbs +29 -0
- data/sig/better_translate.rbs +32 -2
- data/sig/faraday.rbs +22 -0
- data/sig/generators/better_translate/analyze/analyze_generator.rbs +18 -0
- data/sig/generators/better_translate/install/install_generator.rbs +14 -0
- data/sig/generators/better_translate/translate/translate_generator.rbs +10 -0
- data/sig/optparse.rbs +9 -0
- data/sig/psych.rbs +5 -0
- data/sig/rails.rbs +34 -0
- metadata +89 -203
- data/lib/better_translate/helper.rb +0 -83
- data/lib/better_translate/providers/base_provider.rb +0 -102
- data/lib/better_translate/service.rb +0 -144
- data/lib/better_translate/similarity_analyzer.rb +0 -218
- data/lib/better_translate/utils.rb +0 -55
- data/lib/better_translate/writer.rb +0 -75
- data/lib/generators/better_translate/analyze_generator.rb +0 -57
- data/lib/generators/better_translate/install_generator.rb +0 -14
- data/lib/generators/better_translate/templates/better_translate.rb +0 -56
- 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)
|