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.
- 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 -96
- data/CLAUDE.md +385 -0
- data/README.md +649 -187
- 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 -105
- 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 -49
- data/lib/generators/better_translate/translate_generator.rb +0 -84
data/README.md
CHANGED
|
@@ -1,271 +1,733 @@
|
|
|
1
|
-
# BetterTranslate
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
[](https://www.ruby-lang.org/en/)
|
|
6
|
+
[](LICENSE.txt)
|
|
7
|
+
[](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
|
|
123
|
+
gem "better_translate"
|
|
51
124
|
```
|
|
52
125
|
|
|
53
|
-
|
|
126
|
+
And then execute:
|
|
54
127
|
|
|
55
128
|
```bash
|
|
56
129
|
bundle install
|
|
57
130
|
```
|
|
58
131
|
|
|
59
|
-
Or install
|
|
132
|
+
Or install it yourself as:
|
|
60
133
|
|
|
61
134
|
```bash
|
|
62
135
|
gem install better_translate
|
|
63
136
|
```
|
|
64
137
|
|
|
65
|
-
|
|
138
|
+
### Rails Integration
|
|
66
139
|
|
|
67
|
-
|
|
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
|
|
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
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
config.
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
config.
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
config.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
214
|
+
### File Paths
|
|
126
215
|
|
|
127
216
|
```ruby
|
|
128
|
-
|
|
217
|
+
config.input_file = "config/locales/en.yml" # Source file
|
|
218
|
+
config.output_folder = "config/locales" # Output directory
|
|
129
219
|
```
|
|
130
220
|
|
|
131
|
-
|
|
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
|
-
###
|
|
223
|
+
### Translation Modes
|
|
139
224
|
|
|
140
|
-
|
|
225
|
+
#### Override Mode (Default)
|
|
141
226
|
|
|
142
|
-
|
|
227
|
+
Replaces the entire target file with fresh translations:
|
|
143
228
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
229
|
+
```ruby
|
|
230
|
+
config.translation_mode = :override # default
|
|
231
|
+
```
|
|
147
232
|
|
|
148
|
-
|
|
233
|
+
**Use when:** Starting fresh or regenerating all translations.
|
|
149
234
|
|
|
150
|
-
|
|
151
|
-
rails generate better_translate:translate
|
|
152
|
-
```
|
|
235
|
+
#### Incremental Mode
|
|
153
236
|
|
|
154
|
-
|
|
237
|
+
Merges with existing translations, only translating missing keys:
|
|
155
238
|
|
|
156
|
-
|
|
239
|
+
```ruby
|
|
240
|
+
config.translation_mode = :incremental
|
|
241
|
+
```
|
|
157
242
|
|
|
158
|
-
|
|
159
|
-
rails generate better_translate:analyze
|
|
160
|
-
```
|
|
243
|
+
**Use when:** Preserving manual corrections or adding new keys to existing translations.
|
|
161
244
|
|
|
162
|
-
|
|
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
|
-
|
|
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
|
-
|
|
255
|
+
**Cache key format:** `"#{text}:#{target_lang_code}"`
|
|
178
256
|
|
|
179
|
-
|
|
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
|
-
|
|
262
|
+
### Rate Limiting
|
|
182
263
|
|
|
183
|
-
|
|
264
|
+
Prevent API overload with built-in rate limiting:
|
|
184
265
|
|
|
185
266
|
```ruby
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
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
|
-
|
|
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
|
-
|
|
200
|
-
|
|
201
|
-
|
|
360
|
+
Analyze translation similarities using Levenshtein distance:
|
|
361
|
+
|
|
362
|
+
```bash
|
|
363
|
+
rails generate better_translate:analyze
|
|
202
364
|
```
|
|
203
365
|
|
|
204
|
-
|
|
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
|
-
|
|
378
|
+
### Programmatic Translation
|
|
207
379
|
|
|
208
|
-
|
|
380
|
+
#### Translate Multiple Texts to Multiple Languages
|
|
209
381
|
|
|
210
382
|
```ruby
|
|
211
|
-
texts = ["Hello
|
|
212
|
-
|
|
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
|
-
|
|
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
|
-
|
|
225
|
-
# Expected output:
|
|
391
|
+
# Results structure:
|
|
226
392
|
# {
|
|
227
|
-
# "it" => ["Ciao
|
|
228
|
-
# "fr" => ["Bonjour
|
|
393
|
+
# "it" => ["Ciao", "Arrivederci", "Grazie"],
|
|
394
|
+
# "fr" => ["Bonjour", "Au revoir", "Merci"]
|
|
229
395
|
# }
|
|
230
396
|
```
|
|
231
397
|
|
|
232
|
-
|
|
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
|
|
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
|
-
|
|
241
|
-
- Core translation functionality
|
|
242
|
-
- Cache implementation (LRU)
|
|
243
|
-
- Provider selection and initialization
|
|
244
|
-
- Error handling
|
|
245
|
-
- Configuration management
|
|
675
|
+
## ๐ค Contributing
|
|
246
676
|
|
|
247
|
-
|
|
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
|
-
|
|
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
|
-
|
|
719
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
|
256
720
|
|
|
257
|
-
|
|
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
|
-
|
|
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
|
-
|
|
725
|
+
---
|
|
264
726
|
|
|
265
|
-
|
|
727
|
+
<div align="center">
|
|
266
728
|
|
|
267
|
-
|
|
729
|
+
**Made with โค๏ธ by [Alessio Bussolari](https://github.com/alessiobussolari)**
|
|
268
730
|
|
|
269
|
-
|
|
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
|
-
|
|
733
|
+
</div>
|