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
data/README.md CHANGED
@@ -1,348 +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, Google Gemini, and custom providers 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
- - Custom provider support
33
- - Comprehensive test suite
34
- - LRU caching for performance
35
- - Progress tracking
36
- - Detailed YARD documentation
37
- - ๐Ÿ” **Translation Analysis**:
38
- - Similarity detection
39
- - Detailed reports
40
- - Optimization suggestions
41
- - ๐Ÿ”ง **Flexible Helpers**:
42
- - Single text translation
43
- - Bulk text translation
44
- - Multiple target languages support
45
-
46
- ## Installation
47
-
48
- 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:
49
121
 
50
122
  ```ruby
51
- gem 'better_translate', '~> 0.5.0'
123
+ gem "better_translate"
52
124
  ```
53
125
 
54
- Then run:
126
+ And then execute:
55
127
 
56
128
  ```bash
57
129
  bundle install
58
130
  ```
59
131
 
60
- Or install the gem manually:
132
+ Or install it yourself as:
61
133
 
62
134
  ```bash
63
135
  gem install better_translate
64
136
  ```
65
137
 
66
- ## Configuration
138
+ ### Rails Integration
67
139
 
68
- In a Rails application, generate the initializer by running:
140
+ For Rails applications, generate the initializer:
69
141
 
70
142
  ```bash
71
143
  rails generate better_translate:install
72
144
  ```
73
145
 
74
- 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)
75
153
 
76
154
  ```ruby
77
155
  BetterTranslate.configure do |config|
78
- # Choose the provider to use: :chatgpt or :gemini
79
156
  config.provider = :chatgpt
80
-
81
- # API key for ChatGPT (OpenAI)
82
- config.openai_key = ENV.fetch("OPENAI_API_KEY") { "YOUR_OPENAI_API_KEY" }
83
-
84
- # API key for Google Gemini
85
- config.google_gemini_key = ENV.fetch("GOOGLE_GEMINI_KEY") { "YOUR_GOOGLE_GEMINI_KEY" }
86
-
87
- # Source language (e.g., "en" if the source file is in English)
88
- config.source_language = "en"
89
-
90
- # Output folder where the translated files will be saved
91
- config.output_folder = Rails.root.join("config", "locales", "translated").to_s
92
-
93
- # List of target languages (short_name and name)
94
- config.target_languages = [
95
- # Example:
96
- { short_name: "it", name: "italian" }
97
- ]
98
-
99
- # Global exclusions (keys in dot notation) to exclude from translation
100
- config.global_exclusions = [
101
- "key.child_key"
102
- ]
103
-
104
- # Language-specific exclusions: keys to exclude only for specific target languages
105
- config.exclusions_per_language = {
106
- "es" => [],
107
- "it" => ["sample.valid"],
108
- "fr" => [],
109
- "de" => [],
110
- "pt" => [],
111
- "ru" => []
112
- }
113
-
114
- # Path to the input file (e.g., en.yml)
115
- config.input_file = Rails.root.join("config", "locales", "en.yml").to_s
116
-
117
- # Translation mode: :override or :incremental
118
- 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
119
163
  end
120
164
  ```
121
165
 
122
- ## Usage
166
+ Get your API key from [OpenAI Platform](https://platform.openai.com/api-keys).
123
167
 
124
- ### Translating YAML Files
168
+ #### Google Gemini
125
169
 
126
- To start the translation process, simply call the `magic` method:
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
127
184
 
128
185
  ```ruby
129
- BetterTranslate.magic
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
193
+ end
130
194
  ```
131
195
 
132
- This will execute the process that:
133
- 1. Reads the input YAML file.
134
- 2. Applies the global exclusion filtering.
135
- 3. Applies additional language-specific exclusion filtering for each target language.
136
- 4. Translates the strings from the source language into the configured target languages.
137
- 5. Writes the translated files to the output folder, either in **override** or **incremental** mode based on the configuration.
196
+ Get your API key from [Anthropic Console](https://console.anthropic.com/).
138
197
 
139
- ### Using Rails Generators
198
+ ### Language Configuration
140
199
 
141
- The gem includes generators to simplify tasks:
200
+ ```ruby
201
+ config.source_language = "en" # ISO 639-1 code (2 letters)
142
202
 
143
- - **Generate Initializer:**
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
+ ```
144
213
 
145
- ```bash
146
- rails generate better_translate:install
147
- ```
214
+ ### File Paths
148
215
 
149
- - **Trigger Translation Process:**
216
+ ```ruby
217
+ config.input_file = "config/locales/en.yml" # Source file
218
+ config.output_folder = "config/locales" # Output directory
219
+ ```
150
220
 
151
- ```bash
152
- rails generate better_translate:translate
153
- ```
221
+ ## ๐ŸŽจ Features in Detail
154
222
 
155
- The `better_translate:translate` generator will trigger the translation process (via `BetterTranslate.magic`) and display progress in the terminal.
223
+ ### Translation Modes
156
224
 
157
- - **Analyze Translations:**
225
+ #### Override Mode (Default)
158
226
 
159
- ```bash
160
- rails generate better_translate:analyze
161
- ```
227
+ Replaces the entire target file with fresh translations:
162
228
 
163
- - **Create Custom Provider:**
229
+ ```ruby
230
+ config.translation_mode = :override # default
231
+ ```
164
232
 
165
- ```bash
166
- rails generate better_translate:provider YourProviderName
167
- ```
233
+ **Use when:** Starting fresh or regenerating all translations.
168
234
 
169
- The `better_translate:analyze` generator will:
170
- - Scan all YAML files in your locales directory
171
- - Find similar translations using Levenshtein distance
172
- - Generate two reports:
173
- - `translation_similarities.json`: Detailed JSON report
174
- - `translation_similarities_summary.txt`: Human-readable summary
175
-
176
- This helps you:
177
- - Identify potentially redundant translations
178
- - Maintain consistency across your translations
179
- - Optimize your translation files
180
- - Reduce translation costs
235
+ #### Incremental Mode
181
236
 
182
- ### Custom Translation Providers
237
+ Merges with existing translations, only translating missing keys:
183
238
 
184
- BetterTranslate allows you to create and use custom translation providers, enabling integration with any translation API or service. This feature is particularly useful if you want to use a translation service not natively supported by BetterTranslate, such as DeepL, Microsoft Translator, Amazon Translate, or any other API-based translation service.
239
+ ```ruby
240
+ config.translation_mode = :incremental
241
+ ```
185
242
 
186
- 1. **Generate a custom provider template:**
243
+ **Use when:** Preserving manual corrections or adding new keys to existing translations.
187
244
 
188
- ```bash
189
- rails generate better_translate:provider DeepL
190
- ```
245
+ ### Caching System
191
246
 
192
- This creates a new provider class in `app/providers/deep_l_provider.rb` and a README with implementation instructions.
247
+ The LRU (Least Recently Used) cache stores translations to reduce API costs:
193
248
 
194
- 2. **Implement the translation logic:**
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
+ ```
195
254
 
196
- Edit the generated provider file to implement the `translate_text` method with your API-specific code:
255
+ **Cache key format:** `"#{text}:#{target_lang_code}"`
197
256
 
198
- ```ruby
199
- # app/providers/deep_l_provider.rb
200
- module Providers
201
- class DeepLProvider < BetterTranslate::Providers::BaseProvider
202
- def initialize(api_key)
203
- @api_key = api_key
204
- end
257
+ **Benefits:**
258
+ - Reduces API costs for repeated translations
259
+ - Speeds up re-runs during development
260
+ - Thread-safe with Mutex protection
205
261
 
206
- def translate_text(text, target_lang_code, target_lang_name)
207
- # Implement your API call to DeepL here
208
- # Return the translated text as a string
209
- end
210
- end
211
- end
212
- ```
262
+ ### Rate Limiting
213
263
 
214
- 3. **Register your provider:**
264
+ Prevent API overload with built-in rate limiting:
215
265
 
216
- Create an initializer to register your custom provider:
266
+ ```ruby
267
+ config.max_concurrent_requests = 3 # default: 3
268
+ ```
217
269
 
218
- ```ruby
219
- # config/initializers/better_translate_providers.rb
220
- # Require the provider file to ensure it's loaded
221
- require Rails.root.join('app', 'providers', 'deep_l_provider')
222
-
223
- BetterTranslate::Service.register_provider(
224
- :deepl,
225
- ->(api_key) { Providers::DeepLProvider.new(api_key) }
226
- )
227
- ```
228
-
229
- Note: The `require` statement is important to ensure the provider class is loaded before it's used.
270
+ The rate limiter enforces a 0.5-second delay between requests by default. This is handled automatically by the `BaseHttpProvider`.
230
271
 
231
- 4. **Update your configuration:**
272
+ ### Exclusion System
232
273
 
233
- Add your API key to the BetterTranslate configuration:
274
+ #### Global Exclusions
234
275
 
235
- ```ruby
236
- # config/initializers/better_translate.rb
237
- BetterTranslate.configure do |config|
238
- config.provider = :deepl
239
- config.deepl_key = ENV['DEEPL_API_KEY']
240
- # ... other configuration
241
- end
242
- ```
276
+ Keys excluded from translation in **all** target languages (useful for brand names, product codes, etc.):
243
277
 
244
- 5. **Use your custom provider:**
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
283
+ ]
284
+ ```
285
+
286
+ #### Language-Specific Exclusions
245
287
 
246
- Your custom provider will now be used when you call `BetterTranslate.magic` or any other translation method.
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
+ ```
247
309
 
248
- 6. **Troubleshooting:**
310
+ This context is included in the AI system prompt, helping with specialized terminology in fields like:
249
311
 
250
- If you encounter a `NameError` related to the `Providers` module, make sure the provider file is properly required in your initializer as shown in step 3. The `require` statement ensures that the provider class is loaded before it's used.
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"
251
317
 
252
- ### Translation Helpers
318
+ ### Translation Strategies
253
319
 
254
- BetterTranslate provides helper methods to simplify translation tasks.
320
+ BetterTranslate automatically selects the optimal strategy based on content size:
255
321
 
256
- #### Single Text Translation
322
+ #### Deep Translation (< 50 strings)
323
+ - Translates each string individually
324
+ - Detailed progress tracking
325
+ - Best for small to medium files
257
326
 
258
- The `translate_text_to_languages` helper allows you to translate a single text into multiple target languages in one call.
327
+ #### Batch Translation (โ‰ฅ 50 strings)
328
+ - Processes in batches of 10 strings
329
+ - Faster for large files
330
+ - Reduced API overhead
259
331
 
260
- **Example Usage:**
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.
357
+
358
+ ### 3. Analyze Generator
359
+
360
+ Analyze translation similarities using Levenshtein distance:
361
+
362
+ ```bash
363
+ rails generate better_translate:analyze
364
+ ```
365
+
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
377
+
378
+ ### Programmatic Translation
379
+
380
+ #### Translate Multiple Texts to Multiple Languages
261
381
 
262
382
  ```ruby
263
- text = "Hello world!"
264
- target_languages = [
383
+ texts = ["Hello", "Goodbye", "Thank you"]
384
+ target_langs = [
265
385
  { short_name: "it", name: "Italian" },
266
386
  { short_name: "fr", name: "French" }
267
387
  ]
268
388
 
269
- translated = BetterTranslate::TranslationHelper.translate_text_to_languages(
270
- text,
271
- target_languages,
272
- "en", # source language code
273
- :chatgpt # provider: can be :chatgpt or :gemini
274
- )
389
+ results = BetterTranslate::TranslationHelper.translate_texts_to_languages(texts, target_langs)
275
390
 
276
- puts translated
277
- # Expected output:
278
- # { "it" => "Ciao mondo!", "fr" => "Bonjour le monde!" }
391
+ # Results structure:
392
+ # {
393
+ # "it" => ["Ciao", "Arrivederci", "Grazie"],
394
+ # "fr" => ["Bonjour", "Au revoir", "Merci"]
395
+ # }
279
396
  ```
280
397
 
281
- #### Multiple Texts Translation
282
-
283
- 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.
284
-
285
- **Example Usage:**
398
+ #### Translate Single Text to Multiple Languages
286
399
 
287
400
  ```ruby
288
- texts = ["Hello world!", "How are you?"]
289
- target_languages = [
401
+ text = "Welcome to our application"
402
+ target_langs = [
290
403
  { short_name: "it", name: "Italian" },
291
- { short_name: "fr", name: "French" }
404
+ { short_name: "es", name: "Spanish" }
292
405
  ]
293
406
 
294
- translated_texts = BetterTranslate::TranslationHelper.translate_texts_to_languages(
295
- texts,
296
- target_languages,
297
- "en", # source language code
298
- :chatgpt # provider: can be :chatgpt or :gemini
299
- )
407
+ results = BetterTranslate::TranslationHelper.translate_text_to_languages(text, target_langs)
300
408
 
301
- puts translated_texts
302
- # Expected output:
409
+ # Results:
303
410
  # {
304
- # "it" => ["Ciao mondo!", "Come stai?"],
305
- # "fr" => ["Bonjour le monde!", "Comment รงa va?"]
411
+ # "it" => "Benvenuto nella nostra applicazione",
412
+ # "es" => "Bienvenido a nuestra aplicaciรณn"
306
413
  # }
307
414
  ```
308
415
 
309
- ## Testing ๐Ÿงช
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
310
440
 
311
- BetterTranslate comes with a comprehensive test suite using RSpec. To run the tests:
441
+ Enable detailed logging for debugging:
442
+
443
+ ```ruby
444
+ config.verbose = true
445
+ ```
446
+
447
+ ## ๐Ÿงช Development & Testing
448
+
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
312
485
 
313
486
  ```bash
487
+ # Run all tests (unit + integration)
488
+ bundle exec rake spec
489
+ # or
314
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
315
591
  ```
316
592
 
317
- The test suite covers:
318
- - Core translation functionality
319
- - Cache implementation (LRU)
320
- - Provider selection and initialization
321
- - Error handling
322
- - Configuration management
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
323
634
 
324
- ## Contributing ๐Ÿค
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
673
+ ```
674
+
675
+ ## ๐Ÿค Contributing
676
+
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
+ ```
325
716
 
326
- 1. Fork the repository
327
- 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
328
- 3. Commit your changes (`git commit -m 'Add amazing feature'`)
329
- 4. Push to the branch (`git push origin feature/amazing-feature`)
330
- 5. Open a Pull Request
717
+ ## ๐Ÿ“„ License
331
718
 
332
- ## Contact & Support ๐Ÿ“ฌ
719
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
333
720
 
334
- - **Email**: alessio.bussolari@pandev.it
335
- - **Issues**: [GitHub Issues](https://github.com/alessiobussolari/better_translate/issues)
336
- - **Discussions**: [GitHub Discussions](https://github.com/alessiobussolari/better_translate/discussions)
721
+ ## ๐Ÿ“œ Code of Conduct
337
722
 
338
- ## 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).
339
724
 
340
- This project is licensed under the MIT License - see the [LICENSE.txt](LICENSE.txt) file for details.
725
+ ---
341
726
 
342
- 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">
343
728
 
344
- For more details, please visit the [GitHub repository](https://github.com/alessiobussolari/better_translate).
729
+ **Made with โค๏ธ by [Alessio Bussolari](https://github.com/alessiobussolari)**
345
730
 
346
- ## 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)
347
732
 
348
- BetterTranslate is distributed under the MIT license. See the [LICENSE](LICENSE) file for more details.
733
+ </div>