better_translate 0.4.2 โ†’ 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 -96
  7. data/CLAUDE.md +385 -0
  8. data/README.md +649 -187
  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 -105
  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 -49
  100. data/lib/generators/better_translate/translate_generator.rb +0 -84
data/README.md CHANGED
@@ -1,271 +1,733 @@
1
- # BetterTranslate ๐ŸŒ
2
-
3
- [![Gem Version](https://badge.fury.io/rb/better_translate.svg)](https://badge.fury.io/rb/better_translate)
4
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
- [![Ruby Style Guide](https://img.shields.io/badge/code_style-rubocop-brightgreen.svg)](https://github.com/rubocop/rubocop)
6
-
7
- > ๐Ÿš€ A powerful Ruby gem for seamless YAML file translations using AI providers like ChatGPT and Google Gemini
8
-
9
- BetterTranslate simplifies the translation of YAML files in your Ruby/Rails applications by leveraging advanced AI models. With support for both ChatGPT (OpenAI) and Google Gemini, it offers:
10
-
11
- โœจ **Key Features**
12
- - ๐Ÿ”„ Smart translation modes (Override/Incremental)
13
- - ๐ŸŽฏ Precise key exclusion control
14
- - ๐Ÿ“Š Real-time progress tracking
15
- - ๐Ÿงช Comprehensive test coverage
16
- - โšก๏ธ LRU caching for performance
17
- - ๐Ÿ” Translation similarity analysis
18
- - ๐Ÿ“š Extensive YARD documentation
19
-
20
- ## Why BetterTranslate? ๐Ÿค”
21
-
22
- - ๐ŸŒ **AI-Powered Translation**: Leverage ChatGPT and Google Gemini for high-quality translations
23
- - ๐Ÿ”„ **Smart Translation Modes**:
24
- - `Override`: Full file rewrite
25
- - `Incremental`: Update only new/modified keys
26
- - ๐ŸŽฏ **Precise Control**:
27
- - Global key exclusions
28
- - Language-specific exclusions
29
- - Dot notation support
30
- - ๐Ÿ›  **Developer-Friendly**:
31
- - Rails generators included
32
- - Comprehensive test suite
33
- - LRU caching for performance
34
- - Progress tracking
35
- - Detailed YARD documentation
36
- - ๐Ÿ” **Translation Analysis**:
37
- - Similarity detection
38
- - Detailed reports
39
- - Optimization suggestions
40
- - ๐Ÿ”ง **Flexible Helpers**:
41
- - Single text translation
42
- - Bulk text translation
43
- - Multiple target languages support
44
-
45
- ## Installation
46
-
47
- Add the gem to your Gemfile:
1
+ # ๐ŸŒ BetterTranslate
2
+
3
+ > AI-powered YAML locale file translator for Rails and Ruby projects
4
+
5
+ [![Ruby Version](https://img.shields.io/badge/ruby-%3E%3D%203.0.0-ruby.svg)](https://www.ruby-lang.org/en/)
6
+ [![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE.txt)
7
+ [![Gem Version](https://img.shields.io/badge/version-1.0.0-green.svg)](https://rubygems.org/gems/better_translate)
8
+
9
+ BetterTranslate automatically translates your YAML locale files using cutting-edge AI providers (ChatGPT, Google Gemini, and Anthropic Claude). It's designed for Rails applications but works with any Ruby project that uses YAML-based internationalization.
10
+
11
+ **๐ŸŽฏ Why BetterTranslate?**
12
+ - โœ… **Production-Ready**: Tested with real APIs via VCR cassettes (18 cassettes, 260KB)
13
+ - โœ… **Interactive Demo**: Try it in 2 minutes with `ruby spec/dummy/demo_translation.rb`
14
+ - โœ… **Variable Preservation**: `%{name}` placeholders maintained in translations
15
+ - โœ… **Nested YAML Support**: Complex structures preserved perfectly
16
+ - โœ… **Multiple Providers**: Choose ChatGPT, Gemini, or Claude
17
+
18
+ | Provider | Model | Speed | Quality | Cost |
19
+ |----------|-------|-------|---------|------|
20
+ | **ChatGPT** | GPT-5-nano | โšกโšกโšก Fast | โญโญโญโญโญ Excellent | ๐Ÿ’ฐ๐Ÿ’ฐ Medium |
21
+ | **Gemini** | gemini-2.0-flash-exp | โšกโšกโšกโšก Very Fast | โญโญโญโญ Very Good | ๐Ÿ’ฐ Low |
22
+ | **Claude** | Claude 3.5 | โšกโšก Medium | โญโญโญโญโญ Excellent | ๐Ÿ’ฐ๐Ÿ’ฐ๐Ÿ’ฐ High |
23
+
24
+ ## โœจ Features
25
+
26
+ ### Core Translation Features
27
+ - ๐Ÿค– **Multiple AI Providers**: Support for ChatGPT (GPT-5-nano), Google Gemini (gemini-2.0-flash-exp), and Anthropic Claude
28
+ - โšก **Intelligent Caching**: LRU cache with optional TTL reduces API costs and speeds up repeated translations
29
+ - ๐Ÿ”„ **Translation Modes**: Choose between override (replace entire files) or incremental (merge with existing translations)
30
+ - ๐ŸŽฏ **Smart Strategies**: Automatic selection between deep translation (< 50 strings) and batch translation (โ‰ฅ 50 strings)
31
+ - ๐Ÿšซ **Flexible Exclusions**: Global exclusions for all languages + language-specific exclusions for fine-grained control
32
+ - ๐ŸŽจ **Translation Context**: Provide domain-specific context for medical, legal, financial, or technical terminology
33
+ - ๐Ÿ“Š **Similarity Analysis**: Built-in Levenshtein distance analyzer to identify similar translations
34
+
35
+ ### Development & Quality
36
+ - ๐Ÿงช **Comprehensive Testing**: Unit tests + integration tests with VCR cassettes (18 cassettes, 260KB)
37
+ - ๐ŸŽฌ **Rails Dummy App**: Interactive demo with real translations (`ruby spec/dummy/demo_translation.rb`)
38
+ - ๐Ÿ”’ **VCR Integration**: Record real API responses, test without API keys, CI/CD friendly
39
+ - ๐Ÿ›ก๏ธ **Type-Safe Configuration**: Comprehensive validation with detailed error messages
40
+ - ๐Ÿ“š **YARD Documentation**: Complete API documentation with examples
41
+ - ๐Ÿ” **Retry Logic**: Exponential backoff for failed API calls (3 attempts, configurable)
42
+ - ๐Ÿšฆ **Rate Limiting**: Thread-safe rate limiter prevents API overload
43
+
44
+ ## ๐Ÿš€ Quick Start
45
+
46
+ ### Try It Now (Interactive Demo)
47
+
48
+ Clone the repo and run the demo to see BetterTranslate in action:
49
+
50
+ ```bash
51
+ git clone https://github.com/alessiobussolari/better_translate.git
52
+ cd better_translate
53
+ bundle install
54
+
55
+ # Set your OpenAI API key
56
+ export OPENAI_API_KEY=your_key_here
57
+
58
+ # Run the demo!
59
+ ruby spec/dummy/demo_translation.rb
60
+ ```
61
+
62
+ **What happens:**
63
+ - โœ… Reads `en.yml` with 16 translation keys
64
+ - โœ… Translates to Italian and French using ChatGPT
65
+ - โœ… Generates `it.yml` and `fr.yml` files
66
+ - โœ… Shows progress, results, and sample translations
67
+ - โœ… Takes ~2 minutes (real API calls)
68
+
69
+ **Sample Output:**
70
+ ```yaml
71
+ # en.yml (input)
72
+ en:
73
+ hello: "Hello"
74
+ users:
75
+ greeting: "Hello %{name}"
76
+
77
+ # it.yml (generated) โœ…
78
+ it:
79
+ hello: "Ciao"
80
+ users:
81
+ greeting: "Ciao %{name}" # Variable preserved!
82
+
83
+ # fr.yml (generated) โœ…
84
+ fr:
85
+ hello: "Bonjour"
86
+ users:
87
+ greeting: "Bonjour %{name}" # Variable preserved!
88
+ ```
89
+
90
+ See [`spec/dummy/USAGE_GUIDE.md`](spec/dummy/USAGE_GUIDE.md) for more examples.
91
+
92
+ ### Rails Integration
93
+
94
+ ```ruby
95
+ # config/initializers/better_translate.rb
96
+ BetterTranslate.configure do |config|
97
+ config.provider = :chatgpt
98
+ config.openai_key = ENV["OPENAI_API_KEY"]
99
+
100
+ config.source_language = "en"
101
+ config.target_languages = [
102
+ { short_name: "it", name: "Italian" },
103
+ { short_name: "fr", name: "French" },
104
+ { short_name: "es", name: "Spanish" }
105
+ ]
106
+
107
+ config.input_file = "config/locales/en.yml"
108
+ config.output_folder = "config/locales"
109
+
110
+ # Optional: Provide context for better translations
111
+ config.translation_context = "E-commerce application with product catalog"
112
+ end
113
+
114
+ # Translate all files
115
+ BetterTranslate.translate_all
116
+ ```
117
+
118
+ ## ๐Ÿ“ฆ Installation
119
+
120
+ Add this line to your application's Gemfile:
48
121
 
49
122
  ```ruby
50
- gem 'better_translate', '~> 0.4.2'
123
+ gem "better_translate"
51
124
  ```
52
125
 
53
- Then run:
126
+ And then execute:
54
127
 
55
128
  ```bash
56
129
  bundle install
57
130
  ```
58
131
 
59
- Or install the gem manually:
132
+ Or install it yourself as:
60
133
 
61
134
  ```bash
62
135
  gem install better_translate
63
136
  ```
64
137
 
65
- ## Configuration
138
+ ### Rails Integration
66
139
 
67
- In a Rails application, generate the initializer by running:
140
+ For Rails applications, generate the initializer:
68
141
 
69
142
  ```bash
70
143
  rails generate better_translate:install
71
144
  ```
72
145
 
73
- This command creates the file `config/initializers/better_translate.rb` with a default configuration. An example configuration is:
146
+ This creates `config/initializers/better_translate.rb` with example configuration for all supported providers.
147
+
148
+ ## โš™๏ธ Configuration
149
+
150
+ ### Provider Setup
151
+
152
+ #### ChatGPT (OpenAI)
74
153
 
75
154
  ```ruby
76
155
  BetterTranslate.configure do |config|
77
- # Choose the provider to use: :chatgpt or :gemini
78
156
  config.provider = :chatgpt
79
-
80
- # API key for ChatGPT (OpenAI)
81
- config.openai_key = ENV.fetch("OPENAI_API_KEY") { "YOUR_OPENAI_API_KEY" }
82
-
83
- # API key for Google Gemini
84
- config.google_gemini_key = ENV.fetch("GOOGLE_GEMINI_KEY") { "YOUR_GOOGLE_GEMINI_KEY" }
85
-
86
- # Source language (e.g., "en" if the source file is in English)
87
- config.source_language = "en"
88
-
89
- # Output folder where the translated files will be saved
90
- config.output_folder = Rails.root.join("config", "locales", "translated").to_s
91
-
92
- # List of target languages (short_name and name)
93
- config.target_languages = [
94
- # Example:
95
- { short_name: "it", name: "italian" }
96
- ]
97
-
98
- # Global exclusions (keys in dot notation) to exclude from translation
99
- config.global_exclusions = [
100
- "key.child_key"
101
- ]
102
-
103
- # Language-specific exclusions: keys to exclude only for specific target languages
104
- config.exclusions_per_language = {
105
- "es" => [],
106
- "it" => ["sample.valid"],
107
- "fr" => [],
108
- "de" => [],
109
- "pt" => [],
110
- "ru" => []
111
- }
112
-
113
- # Path to the input file (e.g., en.yml)
114
- config.input_file = Rails.root.join("config", "locales", "en.yml").to_s
115
-
116
- # Translation mode: :override or :incremental
117
- config.translation_mode = :override
157
+ config.openai_key = ENV["OPENAI_API_KEY"]
158
+
159
+ # Optional: customize model settings (defaults shown)
160
+ config.request_timeout = 30 # seconds
161
+ config.max_retries = 3
162
+ config.retry_delay = 2.0 # seconds
163
+ end
164
+ ```
165
+
166
+ Get your API key from [OpenAI Platform](https://platform.openai.com/api-keys).
167
+
168
+ #### Google Gemini
169
+
170
+ ```ruby
171
+ BetterTranslate.configure do |config|
172
+ config.provider = :gemini
173
+ config.google_gemini_key = ENV["GOOGLE_GEMINI_API_KEY"]
174
+
175
+ # Same optional settings as ChatGPT
176
+ config.request_timeout = 30
177
+ config.max_retries = 3
178
+ end
179
+ ```
180
+
181
+ Get your API key from [Google AI Studio](https://makersuite.google.com/app/apikey).
182
+
183
+ #### Anthropic Claude
184
+
185
+ ```ruby
186
+ BetterTranslate.configure do |config|
187
+ config.provider = :anthropic
188
+ config.anthropic_key = ENV["ANTHROPIC_API_KEY"]
189
+
190
+ # Same optional settings
191
+ config.request_timeout = 30
192
+ config.max_retries = 3
118
193
  end
119
194
  ```
120
195
 
121
- ## Usage
196
+ Get your API key from [Anthropic Console](https://console.anthropic.com/).
197
+
198
+ ### Language Configuration
199
+
200
+ ```ruby
201
+ config.source_language = "en" # ISO 639-1 code (2 letters)
122
202
 
123
- ### Translating YAML Files
203
+ config.target_languages = [
204
+ { short_name: "it", name: "Italian" },
205
+ { short_name: "fr", name: "French" },
206
+ { short_name: "de", name: "German" },
207
+ { short_name: "es", name: "Spanish" },
208
+ { short_name: "pt", name: "Portuguese" },
209
+ { short_name: "ja", name: "Japanese" },
210
+ { short_name: "zh", name: "Chinese" }
211
+ ]
212
+ ```
124
213
 
125
- To start the translation process, simply call the `magic` method:
214
+ ### File Paths
126
215
 
127
216
  ```ruby
128
- BetterTranslate.magic
217
+ config.input_file = "config/locales/en.yml" # Source file
218
+ config.output_folder = "config/locales" # Output directory
129
219
  ```
130
220
 
131
- This will execute the process that:
132
- 1. Reads the input YAML file.
133
- 2. Applies the global exclusion filtering.
134
- 3. Applies additional language-specific exclusion filtering for each target language.
135
- 4. Translates the strings from the source language into the configured target languages.
136
- 5. Writes the translated files to the output folder, either in **override** or **incremental** mode based on the configuration.
221
+ ## ๐ŸŽจ Features in Detail
137
222
 
138
- ### Using Rails Generators
223
+ ### Translation Modes
139
224
 
140
- The gem includes generators to simplify tasks:
225
+ #### Override Mode (Default)
141
226
 
142
- - **Generate Initializer:**
227
+ Replaces the entire target file with fresh translations:
143
228
 
144
- ```bash
145
- rails generate better_translate:install
146
- ```
229
+ ```ruby
230
+ config.translation_mode = :override # default
231
+ ```
147
232
 
148
- - **Trigger Translation Process:**
233
+ **Use when:** Starting fresh or regenerating all translations.
149
234
 
150
- ```bash
151
- rails generate better_translate:translate
152
- ```
235
+ #### Incremental Mode
153
236
 
154
- The `better_translate:translate` generator will trigger the translation process (via `BetterTranslate.magic`) and display progress in the terminal.
237
+ Merges with existing translations, only translating missing keys:
155
238
 
156
- - **Analyze Translations:**
239
+ ```ruby
240
+ config.translation_mode = :incremental
241
+ ```
157
242
 
158
- ```bash
159
- rails generate better_translate:analyze
160
- ```
243
+ **Use when:** Preserving manual corrections or adding new keys to existing translations.
161
244
 
162
- The `better_translate:analyze` generator will:
163
- - Scan all YAML files in your locales directory
164
- - Find similar translations using Levenshtein distance
165
- - Generate two reports:
166
- - `translation_similarities.json`: Detailed JSON report
167
- - `translation_similarities_summary.txt`: Human-readable summary
168
-
169
- This helps you:
170
- - Identify potentially redundant translations
171
- - Maintain consistency across your translations
172
- - Optimize your translation files
173
- - Reduce translation costs
245
+ ### Caching System
174
246
 
175
- ### Translation Helpers
247
+ The LRU (Least Recently Used) cache stores translations to reduce API costs:
248
+
249
+ ```ruby
250
+ config.cache_enabled = true # default: true
251
+ config.cache_size = 1000 # default: 1000 items
252
+ config.cache_ttl = 3600 # optional: 1 hour in seconds (nil = no expiration)
253
+ ```
176
254
 
177
- BetterTranslate provides helper methods to simplify translation tasks.
255
+ **Cache key format:** `"#{text}:#{target_lang_code}"`
178
256
 
179
- #### Single Text Translation
257
+ **Benefits:**
258
+ - Reduces API costs for repeated translations
259
+ - Speeds up re-runs during development
260
+ - Thread-safe with Mutex protection
180
261
 
181
- The `translate_text_to_languages` helper allows you to translate a single text into multiple target languages in one call.
262
+ ### Rate Limiting
182
263
 
183
- **Example Usage:**
264
+ Prevent API overload with built-in rate limiting:
184
265
 
185
266
  ```ruby
186
- text = "Hello world!"
187
- target_languages = [
188
- { short_name: "it", name: "Italian" },
189
- { short_name: "fr", name: "French" }
267
+ config.max_concurrent_requests = 3 # default: 3
268
+ ```
269
+
270
+ The rate limiter enforces a 0.5-second delay between requests by default. This is handled automatically by the `BaseHttpProvider`.
271
+
272
+ ### Exclusion System
273
+
274
+ #### Global Exclusions
275
+
276
+ Keys excluded from translation in **all** target languages (useful for brand names, product codes, etc.):
277
+
278
+ ```ruby
279
+ config.global_exclusions = [
280
+ "app.name", # "MyApp" should never be translated
281
+ "app.company", # "ACME Inc." stays the same
282
+ "product.sku" # "SKU-12345" is language-agnostic
190
283
  ]
284
+ ```
285
+
286
+ #### Language-Specific Exclusions
287
+
288
+ Keys excluded only for **specific** languages (useful for manually translated legal text, locale-specific content, etc.):
289
+
290
+ ```ruby
291
+ config.exclusions_per_language = {
292
+ "it" => ["legal.terms", "legal.privacy"], # Italian legal text manually reviewed
293
+ "de" => ["legal.terms", "legal.privacy"], # German legal text manually reviewed
294
+ "fr" => ["marketing.slogan"] # French slogan crafted by marketing team
295
+ }
296
+ ```
297
+
298
+ **Example:**
299
+ - `legal.terms` is translated for Spanish, Portuguese, etc.
300
+ - But excluded for Italian and German (already manually translated)
301
+
302
+ ### Translation Context
303
+
304
+ Provide domain-specific context to improve translation accuracy:
305
+
306
+ ```ruby
307
+ config.translation_context = "Medical terminology for healthcare applications"
308
+ ```
309
+
310
+ This context is included in the AI system prompt, helping with specialized terminology in fields like:
311
+
312
+ - ๐Ÿฅ **Medical/Healthcare**: "patient", "diagnosis", "treatment"
313
+ - โš–๏ธ **Legal**: "plaintiff", "defendant", "liability"
314
+ - ๐Ÿ’ฐ **Financial**: "dividend", "amortization", "escrow"
315
+ - ๐Ÿ›’ **E-commerce**: "checkout", "cart", "inventory"
316
+ - ๐Ÿ”ง **Technical**: "API", "endpoint", "authentication"
317
+
318
+ ### Translation Strategies
319
+
320
+ BetterTranslate automatically selects the optimal strategy based on content size:
321
+
322
+ #### Deep Translation (< 50 strings)
323
+ - Translates each string individually
324
+ - Detailed progress tracking
325
+ - Best for small to medium files
326
+
327
+ #### Batch Translation (โ‰ฅ 50 strings)
328
+ - Processes in batches of 10 strings
329
+ - Faster for large files
330
+ - Reduced API overhead
331
+
332
+ **You don't need to configure this** - it's automatic! ๐ŸŽฏ
333
+
334
+ ## ๐Ÿ”ง Rails Integration
335
+
336
+ BetterTranslate provides three Rails generators:
337
+
338
+ ### 1. Install Generator
339
+
340
+ Generate the initializer with example configuration:
341
+
342
+ ```bash
343
+ rails generate better_translate:install
344
+ ```
345
+
346
+ Creates: `config/initializers/better_translate.rb`
347
+
348
+ ### 2. Translate Generator
349
+
350
+ Run the translation process:
351
+
352
+ ```bash
353
+ rails generate better_translate:translate
354
+ ```
355
+
356
+ This triggers the translation based on your configuration and displays progress messages.
191
357
 
192
- translated = BetterTranslate::TranslationHelper.translate_text_to_languages(
193
- text,
194
- target_languages,
195
- "en", # source language code
196
- :chatgpt # provider: can be :chatgpt or :gemini
197
- )
358
+ ### 3. Analyze Generator
198
359
 
199
- puts translated
200
- # Expected output:
201
- # { "it" => "Ciao mondo!", "fr" => "Bonjour le monde!" }
360
+ Analyze translation similarities using Levenshtein distance:
361
+
362
+ ```bash
363
+ rails generate better_translate:analyze
202
364
  ```
203
365
 
204
- #### Multiple Texts Translation
366
+ **Output:**
367
+ - Console summary with similar translation pairs
368
+ - Detailed JSON report: `tmp/translation_similarity_report.json`
369
+ - Human-readable summary: `tmp/translation_similarity_summary.txt`
370
+
371
+ **Use cases:**
372
+ - Identify potential translation inconsistencies
373
+ - Find duplicate or near-duplicate translations
374
+ - Quality assurance for translation output
375
+
376
+ ## ๐Ÿ“ Advanced Usage
205
377
 
206
- The `translate_texts_to_languages` helper extends the functionality to translate an array of texts into multiple target languages. It returns a hash where each key is a target language code and the value is an array of translated texts.
378
+ ### Programmatic Translation
207
379
 
208
- **Example Usage:**
380
+ #### Translate Multiple Texts to Multiple Languages
209
381
 
210
382
  ```ruby
211
- texts = ["Hello world!", "How are you?"]
212
- target_languages = [
383
+ texts = ["Hello", "Goodbye", "Thank you"]
384
+ target_langs = [
213
385
  { short_name: "it", name: "Italian" },
214
386
  { short_name: "fr", name: "French" }
215
387
  ]
216
388
 
217
- translated_texts = BetterTranslate::TranslationHelper.translate_texts_to_languages(
218
- texts,
219
- target_languages,
220
- "en", # source language code
221
- :chatgpt # provider: can be :chatgpt or :gemini
222
- )
389
+ results = BetterTranslate::TranslationHelper.translate_texts_to_languages(texts, target_langs)
223
390
 
224
- puts translated_texts
225
- # Expected output:
391
+ # Results structure:
226
392
  # {
227
- # "it" => ["Ciao mondo!", "Come stai?"],
228
- # "fr" => ["Bonjour le monde!", "Comment รงa va?"]
393
+ # "it" => ["Ciao", "Arrivederci", "Grazie"],
394
+ # "fr" => ["Bonjour", "Au revoir", "Merci"]
229
395
  # }
230
396
  ```
231
397
 
232
- ## Testing ๐Ÿงช
398
+ #### Translate Single Text to Multiple Languages
399
+
400
+ ```ruby
401
+ text = "Welcome to our application"
402
+ target_langs = [
403
+ { short_name: "it", name: "Italian" },
404
+ { short_name: "es", name: "Spanish" }
405
+ ]
406
+
407
+ results = BetterTranslate::TranslationHelper.translate_text_to_languages(text, target_langs)
408
+
409
+ # Results:
410
+ # {
411
+ # "it" => "Benvenuto nella nostra applicazione",
412
+ # "es" => "Bienvenido a nuestra aplicaciรณn"
413
+ # }
414
+ ```
415
+
416
+ ### Custom Configuration for Specific Tasks
417
+
418
+ ```ruby
419
+ # Separate configuration for different domains
420
+ medical_config = BetterTranslate::Configuration.new
421
+ medical_config.provider = :chatgpt
422
+ medical_config.openai_key = ENV["OPENAI_API_KEY"]
423
+ medical_config.translation_context = "Medical terminology for patient records"
424
+ medical_config.validate!
425
+
426
+ # Use the custom config...
427
+ ```
428
+
429
+ ### Dry Run Mode
430
+
431
+ Test your configuration without writing files:
432
+
433
+ ```ruby
434
+ config.dry_run = true
435
+ ```
436
+
437
+ This validates everything and simulates the translation process without creating output files.
438
+
439
+ ### Verbose Logging
440
+
441
+ Enable detailed logging for debugging:
442
+
443
+ ```ruby
444
+ config.verbose = true
445
+ ```
446
+
447
+ ## ๐Ÿงช Development & Testing
233
448
 
234
- BetterTranslate comes with a comprehensive test suite using RSpec. To run the tests:
449
+ BetterTranslate includes comprehensive testing infrastructure with **unit tests**, **integration tests**, and a **Rails dummy app** for realistic testing.
450
+
451
+ ### Test Structure
452
+
453
+ ```
454
+ spec/
455
+ โ”œโ”€โ”€ better_translate/ # Unit tests (fast, no API calls)
456
+ โ”‚ โ”œโ”€โ”€ cache_spec.rb
457
+ โ”‚ โ”œโ”€โ”€ configuration_spec.rb
458
+ โ”‚ โ”œโ”€โ”€ providers/
459
+ โ”‚ โ”‚ โ”œโ”€โ”€ chatgpt_provider_spec.rb
460
+ โ”‚ โ”‚ โ””โ”€โ”€ gemini_provider_spec.rb
461
+ โ”‚ โ””โ”€โ”€ ...
462
+ โ”‚
463
+ โ”œโ”€โ”€ integration/ # Integration tests (real API via VCR)
464
+ โ”‚ โ”œโ”€โ”€ chatgpt_integration_spec.rb
465
+ โ”‚ โ”œโ”€โ”€ gemini_integration_spec.rb
466
+ โ”‚ โ”œโ”€โ”€ rails_dummy_app_spec.rb
467
+ โ”‚ โ””โ”€โ”€ README.md
468
+ โ”‚
469
+ โ”œโ”€โ”€ dummy/ # Rails dummy app for testing
470
+ โ”‚ โ”œโ”€โ”€ config/
471
+ โ”‚ โ”‚ โ””โ”€โ”€ locales/
472
+ โ”‚ โ”‚ โ”œโ”€โ”€ en.yml # Source file
473
+ โ”‚ โ”‚ โ”œโ”€โ”€ it.yml # Generated translations
474
+ โ”‚ โ”‚ โ””โ”€โ”€ fr.yml
475
+ โ”‚ โ”œโ”€โ”€ demo_translation.rb # Interactive demo script
476
+ โ”‚ โ””โ”€โ”€ USAGE_GUIDE.md
477
+ โ”‚
478
+ โ””โ”€โ”€ vcr_cassettes/ # Recorded API responses (18 cassettes, 260KB)
479
+ โ”œโ”€โ”€ chatgpt/ (7)
480
+ โ”œโ”€โ”€ gemini/ (7)
481
+ โ””โ”€โ”€ rails/ (4)
482
+ ```
483
+
484
+ ### Running Tests
235
485
 
236
486
  ```bash
487
+ # Run all tests (unit + integration)
488
+ bundle exec rake spec
489
+ # or
237
490
  bundle exec rspec
491
+
492
+ # Run only unit tests (fast, no API calls)
493
+ bundle exec rspec spec/better_translate/
494
+
495
+ # Run only integration tests (uses VCR cassettes)
496
+ bundle exec rspec spec/integration/
497
+
498
+ # Run specific test file
499
+ bundle exec rspec spec/better_translate/configuration_spec.rb
500
+
501
+ # Run tests with coverage
502
+ bundle exec rspec --format documentation
503
+ ```
504
+
505
+ ### VCR Cassettes & API Testing
506
+
507
+ BetterTranslate uses **VCR** (Video Cassette Recorder) to record real API interactions for integration tests. This allows:
508
+
509
+ โœ… **Realistic testing** with actual provider responses
510
+ โœ… **No API keys needed** after initial recording
511
+ โœ… **Fast test execution** (no real API calls)
512
+ โœ… **CI/CD friendly** (cassettes committed to repo)
513
+ โœ… **API keys anonymized** (safe to commit)
514
+
515
+ #### Setup API Keys for Recording
516
+
517
+ ```bash
518
+ # Copy environment template
519
+ cp .env.example .env
520
+
521
+ # Edit .env and add your API keys
522
+ OPENAI_API_KEY=sk-...
523
+ GEMINI_API_KEY=...
524
+ ANTHROPIC_API_KEY=sk-ant-...
525
+ ```
526
+
527
+ #### Re-record Cassettes
528
+
529
+ ```bash
530
+ # Delete and re-record all cassettes
531
+ rm -rf spec/vcr_cassettes/
532
+ bundle exec rspec spec/integration/
533
+
534
+ # Re-record specific provider
535
+ rm -rf spec/vcr_cassettes/chatgpt/
536
+ bundle exec rspec spec/integration/chatgpt_integration_spec.rb
537
+ ```
538
+
539
+ **Note**: The `.env` file is gitignored. API keys in cassettes are automatically replaced with `<OPENAI_API_KEY>`, `<GEMINI_API_KEY>`, etc.
540
+
541
+ ### Rails Dummy App Demo
542
+
543
+ Test BetterTranslate with a realistic Rails app:
544
+
545
+ ```bash
546
+ # Run interactive demo
547
+ ruby spec/dummy/demo_translation.rb
548
+ ```
549
+
550
+ **Output:**
551
+ ```
552
+ ๐Ÿš€ Starting translation...
553
+ [BetterTranslate] Italian | hello | 6.3%
554
+ [BetterTranslate] Italian | world | 12.5%
555
+ ...
556
+ โœ… Success: 2 language(s)
557
+ โœ“ it.yml generated (519 bytes)
558
+ โœ“ fr.yml generated (511 bytes)
559
+ ```
560
+
561
+ **Generated files:**
562
+ - `spec/dummy/config/locales/it.yml` - Italian translation
563
+ - `spec/dummy/config/locales/fr.yml` - French translation
564
+
565
+ See [`spec/dummy/USAGE_GUIDE.md`](spec/dummy/USAGE_GUIDE.md) for more examples.
566
+
567
+ ### Code Quality
568
+
569
+ ```bash
570
+ # Run RuboCop linter
571
+ bundle exec rubocop
572
+
573
+ # Auto-fix violations
574
+ bundle exec rubocop -a
575
+
576
+ # Run both tests and linter
577
+ bundle exec rake
578
+ ```
579
+
580
+ ### Documentation
581
+
582
+ ```bash
583
+ # Generate YARD documentation
584
+ bundle exec yard doc
585
+
586
+ # Start documentation server (http://localhost:8808)
587
+ bundle exec yard server
588
+
589
+ # Check documentation coverage
590
+ bundle exec yard stats
591
+ ```
592
+
593
+ ### Interactive Console
594
+
595
+ ```bash
596
+ # Load the gem in an interactive console
597
+ bin/console
598
+ ```
599
+
600
+ ### Security Audit
601
+
602
+ ```bash
603
+ # Check for security vulnerabilities
604
+ bundle exec bundler-audit check --update
605
+ ```
606
+
607
+ ## ๐Ÿ—๏ธ Architecture
608
+
609
+ ### Provider Architecture
610
+
611
+ All providers inherit from `BaseHttpProvider`:
612
+
613
+ ```
614
+ BaseHttpProvider (abstract)
615
+ โ”œโ”€โ”€ ChatGPTProvider
616
+ โ”œโ”€โ”€ GeminiProvider
617
+ โ””โ”€โ”€ AnthropicProvider
618
+ ```
619
+
620
+ **BaseHttpProvider responsibilities:**
621
+ - HTTP communication via Faraday
622
+ - Retry logic with exponential backoff
623
+ - Rate limiting
624
+ - Timeout handling
625
+ - Error wrapping
626
+
627
+ ### Core Components
628
+
629
+ - **Configuration**: Type-safe config with validation
630
+ - **Cache**: LRU cache with optional TTL
631
+ - **RateLimiter**: Thread-safe request throttling
632
+ - **Validator**: Input validation (language codes, text, paths, keys)
633
+ - **HashFlattener**: Converts nested YAML โ†” flat structure
634
+
635
+ ### Error Hierarchy
636
+
637
+ All errors inherit from `BetterTranslate::Error`:
638
+
639
+ ```
640
+ BetterTranslate::Error
641
+ โ”œโ”€โ”€ ConfigurationError
642
+ โ”œโ”€โ”€ ValidationError
643
+ โ”œโ”€โ”€ TranslationError
644
+ โ”œโ”€โ”€ ProviderError
645
+ โ”œโ”€โ”€ ApiError
646
+ โ”œโ”€โ”€ RateLimitError
647
+ โ”œโ”€โ”€ FileError
648
+ โ”œโ”€โ”€ YamlError
649
+ โ””โ”€โ”€ ProviderNotFoundError
650
+ ```
651
+
652
+ ## ๐Ÿ“– Documentation
653
+
654
+ - **[USAGE_GUIDE.md](spec/dummy/USAGE_GUIDE.md)** - Complete guide to dummy app and demos
655
+ - **[VCR Testing Guide](spec/integration/README.md)** - How to test with VCR cassettes
656
+ - **[CLAUDE.md](CLAUDE.md)** - Developer guide for AI assistants (Claude Code)
657
+ - **[YARD Docs](https://rubydoc.info/gems/better_translate)** - Complete API documentation
658
+
659
+ ### Key Documentation Files
660
+
661
+ ```
662
+ better_translate/
663
+ โ”œโ”€โ”€ README.md # This file (main documentation)
664
+ โ”œโ”€โ”€ CLAUDE.md # Development guide (commands, architecture)
665
+ โ”œโ”€โ”€ spec/
666
+ โ”‚ โ”œโ”€โ”€ dummy/
667
+ โ”‚ โ”‚ โ”œโ”€โ”€ USAGE_GUIDE.md # ๐Ÿ“– Interactive demo guide
668
+ โ”‚ โ”‚ โ””โ”€โ”€ demo_translation.rb # ๐Ÿš€ Runnable demo script
669
+ โ”‚ โ””โ”€โ”€ integration/
670
+ โ”‚ โ””โ”€โ”€ README.md # ๐Ÿงช VCR testing guide
671
+ โ””โ”€โ”€ docs/
672
+ โ””โ”€โ”€ implementation/ # Design docs
238
673
  ```
239
674
 
240
- The test suite covers:
241
- - Core translation functionality
242
- - Cache implementation (LRU)
243
- - Provider selection and initialization
244
- - Error handling
245
- - Configuration management
675
+ ## ๐Ÿค Contributing
246
676
 
247
- ## Contributing ๐Ÿค
677
+ Bug reports and pull requests are welcome on GitHub at https://github.com/alessiobussolari/better_translate.
678
+
679
+ ### Development Guidelines
680
+
681
+ 1. **TDD (Test-Driven Development)**: Always write tests before implementing features
682
+ 2. **YARD Documentation**: Document all public methods with `@param`, `@return`, `@raise`, and `@example`
683
+ 3. **RuboCop Compliance**: Ensure code passes `bundle exec rubocop` before committing
684
+ 4. **Frozen String Literals**: Include `# frozen_string_literal: true` at the top of all files
685
+ 5. **HTTP Client**: Use Faraday for all HTTP requests (never Net::HTTP or HTTParty)
686
+ 6. **VCR Cassettes**: Record integration tests with real API responses for CI/CD
687
+
688
+ ### Workflow
689
+
690
+ ```bash
691
+ # 1. Clone and setup
692
+ git clone https://github.com/alessiobussolari/better_translate.git
693
+ cd better_translate
694
+ bundle install
695
+
696
+ # 2. Create a feature branch
697
+ git checkout -b my-feature
698
+
699
+ # 3. Write tests first (TDD)
700
+ # Edit spec/better_translate/my_feature_spec.rb
701
+
702
+ # 4. Implement the feature
703
+ # Edit lib/better_translate/my_feature.rb
704
+
705
+ # 5. Ensure tests pass and code is clean
706
+ bundle exec rspec
707
+ bundle exec rubocop
708
+
709
+ # 6. Commit and push
710
+ git add .
711
+ git commit -m "Add my feature"
712
+ git push origin my-feature
713
+
714
+ # 7. Create a Pull Request
715
+ ```
248
716
 
249
- 1. Fork the repository
250
- 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
251
- 3. Commit your changes (`git commit -m 'Add amazing feature'`)
252
- 4. Push to the branch (`git push origin feature/amazing-feature`)
253
- 5. Open a Pull Request
717
+ ## ๐Ÿ“„ License
254
718
 
255
- ## Contact & Support ๐Ÿ“ฌ
719
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
256
720
 
257
- - **Email**: alessio.bussolari@pandev.it
258
- - **Issues**: [GitHub Issues](https://github.com/alessiobussolari/better_translate/issues)
259
- - **Discussions**: [GitHub Discussions](https://github.com/alessiobussolari/better_translate/discussions)
721
+ ## ๐Ÿ“œ Code of Conduct
260
722
 
261
- ## License ๐Ÿ“„
723
+ Everyone interacting in the BetterTranslate project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](CODE_OF_CONDUCT.md).
262
724
 
263
- This project is licensed under the MIT License - see the [LICENSE.txt](LICENSE.txt) file for details.
725
+ ---
264
726
 
265
- BetterTranslate aims to simplify the translation of YAML files in Ruby projects by providing a flexible, configurable, and extensible system. Whether you need a complete file rewrite or an incremental update, BetterTranslate streamlines the translation process using advanced providers like ChatGPT and Google Gemini. Contributions, feedback, and feature requests are highly encouraged to help improve the gem further.
727
+ <div align="center">
266
728
 
267
- For more details, please visit the [GitHub repository](https://github.com/alessiobussolari/better_translate).
729
+ **Made with โค๏ธ by [Alessio Bussolari](https://github.com/alessiobussolari)**
268
730
 
269
- ## License
731
+ [Report Bug](https://github.com/alessiobussolari/better_translate/issues) ยท [Request Feature](https://github.com/alessiobussolari/better_translate/issues) ยท [Documentation](https://github.com/alessiobussolari/better_translate)
270
732
 
271
- BetterTranslate is distributed under the MIT license. See the [LICENSE](LICENSE) file for more details.
733
+ </div>