better_translate 0.5.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.env.example +14 -0
- data/.rspec +3 -0
- data/.rubocop.yml +8 -0
- data/.yardopts +10 -0
- data/CHANGELOG.md +125 -114
- data/CLAUDE.md +385 -0
- data/README.md +629 -244
- data/Rakefile +7 -1
- data/Steepfile +29 -0
- data/docs/implementation/00-overview.md +220 -0
- data/docs/implementation/01-setup_dependencies.md +668 -0
- data/docs/implementation/02-error_handling.md +65 -0
- data/docs/implementation/03-core_components.md +457 -0
- data/docs/implementation/03.5-variable_preservation.md +509 -0
- data/docs/implementation/04-provider_architecture.md +571 -0
- data/docs/implementation/05-translation_logic.md +1065 -0
- data/docs/implementation/06-main_module_api.md +122 -0
- data/docs/implementation/07-direct_translation_helpers.md +582 -0
- data/docs/implementation/08-rails_integration.md +323 -0
- data/docs/implementation/09-testing_suite.md +228 -0
- data/docs/implementation/10-documentation_examples.md +150 -0
- data/docs/implementation/11-quality_security.md +65 -0
- data/docs/implementation/12-cli_standalone.md +698 -0
- data/exe/better_translate +9 -0
- data/lib/better_translate/cache.rb +125 -0
- data/lib/better_translate/cli.rb +304 -0
- data/lib/better_translate/configuration.rb +201 -0
- data/lib/better_translate/direct_translator.rb +131 -0
- data/lib/better_translate/errors.rb +101 -0
- data/lib/better_translate/progress_tracker.rb +157 -0
- data/lib/better_translate/provider_factory.rb +45 -0
- data/lib/better_translate/providers/anthropic_provider.rb +154 -0
- data/lib/better_translate/providers/base_http_provider.rb +239 -0
- data/lib/better_translate/providers/chatgpt_provider.rb +138 -44
- data/lib/better_translate/providers/gemini_provider.rb +123 -61
- data/lib/better_translate/railtie.rb +18 -0
- data/lib/better_translate/rate_limiter.rb +90 -0
- data/lib/better_translate/strategies/base_strategy.rb +58 -0
- data/lib/better_translate/strategies/batch_strategy.rb +56 -0
- data/lib/better_translate/strategies/deep_strategy.rb +45 -0
- data/lib/better_translate/strategies/strategy_selector.rb +43 -0
- data/lib/better_translate/translator.rb +115 -284
- data/lib/better_translate/utils/hash_flattener.rb +104 -0
- data/lib/better_translate/validator.rb +105 -0
- data/lib/better_translate/variable_extractor.rb +259 -0
- data/lib/better_translate/version.rb +2 -9
- data/lib/better_translate/yaml_handler.rb +168 -0
- data/lib/better_translate.rb +97 -73
- data/lib/generators/better_translate/analyze/USAGE +12 -0
- data/lib/generators/better_translate/analyze/analyze_generator.rb +94 -0
- data/lib/generators/better_translate/install/USAGE +13 -0
- data/lib/generators/better_translate/install/install_generator.rb +71 -0
- data/lib/generators/better_translate/install/templates/README +20 -0
- data/lib/generators/better_translate/install/templates/initializer.rb.tt +47 -0
- data/lib/generators/better_translate/translate/USAGE +13 -0
- data/lib/generators/better_translate/translate/translate_generator.rb +114 -0
- data/lib/tasks/better_translate.rake +136 -0
- data/sig/better_translate/cache.rbs +28 -0
- data/sig/better_translate/cli.rbs +24 -0
- data/sig/better_translate/configuration.rbs +78 -0
- data/sig/better_translate/direct_translator.rbs +18 -0
- data/sig/better_translate/errors.rbs +46 -0
- data/sig/better_translate/progress_tracker.rbs +29 -0
- data/sig/better_translate/provider_factory.rbs +8 -0
- data/sig/better_translate/providers/anthropic_provider.rbs +27 -0
- data/sig/better_translate/providers/base_http_provider.rbs +44 -0
- data/sig/better_translate/providers/chatgpt_provider.rbs +25 -0
- data/sig/better_translate/providers/gemini_provider.rbs +22 -0
- data/sig/better_translate/railtie.rbs +7 -0
- data/sig/better_translate/rate_limiter.rbs +20 -0
- data/sig/better_translate/strategies/base_strategy.rbs +19 -0
- data/sig/better_translate/strategies/batch_strategy.rbs +13 -0
- data/sig/better_translate/strategies/deep_strategy.rbs +11 -0
- data/sig/better_translate/strategies/strategy_selector.rbs +10 -0
- data/sig/better_translate/translator.rbs +24 -0
- data/sig/better_translate/utils/hash_flattener.rbs +14 -0
- data/sig/better_translate/validator.rbs +14 -0
- data/sig/better_translate/variable_extractor.rbs +40 -0
- data/sig/better_translate/version.rbs +4 -0
- data/sig/better_translate/yaml_handler.rbs +29 -0
- data/sig/better_translate.rbs +32 -2
- data/sig/faraday.rbs +22 -0
- data/sig/generators/better_translate/analyze/analyze_generator.rbs +18 -0
- data/sig/generators/better_translate/install/install_generator.rbs +14 -0
- data/sig/generators/better_translate/translate/translate_generator.rbs +10 -0
- data/sig/optparse.rbs +9 -0
- data/sig/psych.rbs +5 -0
- data/sig/rails.rbs +34 -0
- metadata +89 -203
- data/lib/better_translate/helper.rb +0 -83
- data/lib/better_translate/providers/base_provider.rb +0 -102
- data/lib/better_translate/service.rb +0 -144
- data/lib/better_translate/similarity_analyzer.rb +0 -218
- data/lib/better_translate/utils.rb +0 -55
- data/lib/better_translate/writer.rb +0 -75
- data/lib/generators/better_translate/analyze_generator.rb +0 -57
- data/lib/generators/better_translate/install_generator.rb +0 -14
- data/lib/generators/better_translate/templates/better_translate.rb +0 -56
- data/lib/generators/better_translate/translate_generator.rb +0 -84
metadata
CHANGED
|
@@ -1,249 +1,135 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: better_translate
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 1.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
|
-
-
|
|
7
|
+
- alessiobussolari
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2025-
|
|
11
|
+
date: 2025-10-22 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
|
-
name:
|
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
|
16
|
-
requirements:
|
|
17
|
-
- - "~>"
|
|
18
|
-
- !ruby/object:Gem::Version
|
|
19
|
-
version: '1.13'
|
|
20
|
-
type: :runtime
|
|
21
|
-
prerelease: false
|
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
-
requirements:
|
|
24
|
-
- - "~>"
|
|
25
|
-
- !ruby/object:Gem::Version
|
|
26
|
-
version: '1.13'
|
|
27
|
-
- !ruby/object:Gem::Dependency
|
|
28
|
-
name: httparty
|
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
|
30
|
-
requirements:
|
|
31
|
-
- - "~>"
|
|
32
|
-
- !ruby/object:Gem::Version
|
|
33
|
-
version: 0.21.0
|
|
34
|
-
type: :runtime
|
|
35
|
-
prerelease: false
|
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
-
requirements:
|
|
38
|
-
- - "~>"
|
|
39
|
-
- !ruby/object:Gem::Version
|
|
40
|
-
version: 0.21.0
|
|
41
|
-
- !ruby/object:Gem::Dependency
|
|
42
|
-
name: zeitwerk
|
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
|
44
|
-
requirements:
|
|
45
|
-
- - "~>"
|
|
46
|
-
- !ruby/object:Gem::Version
|
|
47
|
-
version: '2.6'
|
|
48
|
-
type: :runtime
|
|
49
|
-
prerelease: false
|
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
-
requirements:
|
|
52
|
-
- - "~>"
|
|
53
|
-
- !ruby/object:Gem::Version
|
|
54
|
-
version: '2.6'
|
|
55
|
-
- !ruby/object:Gem::Dependency
|
|
56
|
-
name: bundler
|
|
14
|
+
name: faraday
|
|
57
15
|
requirement: !ruby/object:Gem::Requirement
|
|
58
16
|
requirements:
|
|
59
17
|
- - "~>"
|
|
60
18
|
- !ruby/object:Gem::Version
|
|
61
19
|
version: '2.0'
|
|
62
|
-
type: :
|
|
20
|
+
type: :runtime
|
|
63
21
|
prerelease: false
|
|
64
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
65
23
|
requirements:
|
|
66
24
|
- - "~>"
|
|
67
25
|
- !ruby/object:Gem::Version
|
|
68
26
|
version: '2.0'
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
|
72
|
-
requirements:
|
|
73
|
-
- - "~>"
|
|
74
|
-
- !ruby/object:Gem::Version
|
|
75
|
-
version: '13.0'
|
|
76
|
-
type: :development
|
|
77
|
-
prerelease: false
|
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
-
requirements:
|
|
80
|
-
- - "~>"
|
|
81
|
-
- !ruby/object:Gem::Version
|
|
82
|
-
version: '13.0'
|
|
83
|
-
- !ruby/object:Gem::Dependency
|
|
84
|
-
name: rspec
|
|
85
|
-
requirement: !ruby/object:Gem::Requirement
|
|
86
|
-
requirements:
|
|
87
|
-
- - "~>"
|
|
88
|
-
- !ruby/object:Gem::Version
|
|
89
|
-
version: '3.12'
|
|
90
|
-
type: :development
|
|
91
|
-
prerelease: false
|
|
92
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
-
requirements:
|
|
94
|
-
- - "~>"
|
|
95
|
-
- !ruby/object:Gem::Version
|
|
96
|
-
version: '3.12'
|
|
97
|
-
- !ruby/object:Gem::Dependency
|
|
98
|
-
name: rubocop
|
|
99
|
-
requirement: !ruby/object:Gem::Requirement
|
|
100
|
-
requirements:
|
|
101
|
-
- - "~>"
|
|
102
|
-
- !ruby/object:Gem::Version
|
|
103
|
-
version: '1.50'
|
|
104
|
-
type: :development
|
|
105
|
-
prerelease: false
|
|
106
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
107
|
-
requirements:
|
|
108
|
-
- - "~>"
|
|
109
|
-
- !ruby/object:Gem::Version
|
|
110
|
-
version: '1.50'
|
|
111
|
-
- !ruby/object:Gem::Dependency
|
|
112
|
-
name: rubocop-rake
|
|
113
|
-
requirement: !ruby/object:Gem::Requirement
|
|
114
|
-
requirements:
|
|
115
|
-
- - "~>"
|
|
116
|
-
- !ruby/object:Gem::Version
|
|
117
|
-
version: '0.6'
|
|
118
|
-
type: :development
|
|
119
|
-
prerelease: false
|
|
120
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
121
|
-
requirements:
|
|
122
|
-
- - "~>"
|
|
123
|
-
- !ruby/object:Gem::Version
|
|
124
|
-
version: '0.6'
|
|
125
|
-
- !ruby/object:Gem::Dependency
|
|
126
|
-
name: rubocop-rspec
|
|
127
|
-
requirement: !ruby/object:Gem::Requirement
|
|
128
|
-
requirements:
|
|
129
|
-
- - "~>"
|
|
130
|
-
- !ruby/object:Gem::Version
|
|
131
|
-
version: '2.22'
|
|
132
|
-
type: :development
|
|
133
|
-
prerelease: false
|
|
134
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
135
|
-
requirements:
|
|
136
|
-
- - "~>"
|
|
137
|
-
- !ruby/object:Gem::Version
|
|
138
|
-
version: '2.22'
|
|
139
|
-
- !ruby/object:Gem::Dependency
|
|
140
|
-
name: yard
|
|
141
|
-
requirement: !ruby/object:Gem::Requirement
|
|
142
|
-
requirements:
|
|
143
|
-
- - "~>"
|
|
144
|
-
- !ruby/object:Gem::Version
|
|
145
|
-
version: '0.9'
|
|
146
|
-
type: :development
|
|
147
|
-
prerelease: false
|
|
148
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
149
|
-
requirements:
|
|
150
|
-
- - "~>"
|
|
151
|
-
- !ruby/object:Gem::Version
|
|
152
|
-
version: '0.9'
|
|
153
|
-
- !ruby/object:Gem::Dependency
|
|
154
|
-
name: simplecov
|
|
155
|
-
requirement: !ruby/object:Gem::Requirement
|
|
156
|
-
requirements:
|
|
157
|
-
- - "~>"
|
|
158
|
-
- !ruby/object:Gem::Version
|
|
159
|
-
version: '0.22'
|
|
160
|
-
type: :development
|
|
161
|
-
prerelease: false
|
|
162
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
163
|
-
requirements:
|
|
164
|
-
- - "~>"
|
|
165
|
-
- !ruby/object:Gem::Version
|
|
166
|
-
version: '0.22'
|
|
167
|
-
- !ruby/object:Gem::Dependency
|
|
168
|
-
name: webmock
|
|
169
|
-
requirement: !ruby/object:Gem::Requirement
|
|
170
|
-
requirements:
|
|
171
|
-
- - "~>"
|
|
172
|
-
- !ruby/object:Gem::Version
|
|
173
|
-
version: '3.19'
|
|
174
|
-
type: :development
|
|
175
|
-
prerelease: false
|
|
176
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
177
|
-
requirements:
|
|
178
|
-
- - "~>"
|
|
179
|
-
- !ruby/object:Gem::Version
|
|
180
|
-
version: '3.19'
|
|
181
|
-
- !ruby/object:Gem::Dependency
|
|
182
|
-
name: dotenv
|
|
183
|
-
requirement: !ruby/object:Gem::Requirement
|
|
184
|
-
requirements:
|
|
185
|
-
- - "~>"
|
|
186
|
-
- !ruby/object:Gem::Version
|
|
187
|
-
version: '2.8'
|
|
188
|
-
type: :development
|
|
189
|
-
prerelease: false
|
|
190
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
191
|
-
requirements:
|
|
192
|
-
- - "~>"
|
|
193
|
-
- !ruby/object:Gem::Version
|
|
194
|
-
version: '2.8'
|
|
195
|
-
description: |
|
|
196
|
-
BetterTranslate is a powerful Ruby gem for translating YAML files using AI providers.
|
|
197
|
-
|
|
198
|
-
Key features:
|
|
199
|
-
* Multiple AI providers support (ChatGPT and Google Gemini)
|
|
200
|
-
* Smart translation modes (override/incremental)
|
|
201
|
-
* LRU caching for performance
|
|
202
|
-
* Precise key exclusion control
|
|
203
|
-
* Rails integration with generators
|
|
204
|
-
* Progress tracking with progress bar
|
|
205
|
-
* Comprehensive test coverage
|
|
206
|
-
|
|
207
|
-
Perfect for internationalizing Ruby/Rails applications with minimal effort.
|
|
27
|
+
description: Automatically translate YAML locale files using AI providers (ChatGPT,
|
|
28
|
+
Gemini, Claude). Features intelligent caching, batch processing, and Rails integration.
|
|
208
29
|
email:
|
|
209
30
|
- alessio.bussolari@pandev.it
|
|
210
|
-
executables:
|
|
31
|
+
executables:
|
|
32
|
+
- better_translate
|
|
211
33
|
extensions: []
|
|
212
34
|
extra_rdoc_files: []
|
|
213
35
|
files:
|
|
36
|
+
- ".env.example"
|
|
37
|
+
- ".rspec"
|
|
38
|
+
- ".rubocop.yml"
|
|
39
|
+
- ".yardopts"
|
|
214
40
|
- CHANGELOG.md
|
|
41
|
+
- CLAUDE.md
|
|
215
42
|
- CODE_OF_CONDUCT.md
|
|
216
43
|
- LICENSE.txt
|
|
217
44
|
- README.md
|
|
218
45
|
- Rakefile
|
|
46
|
+
- Steepfile
|
|
47
|
+
- docs/implementation/00-overview.md
|
|
48
|
+
- docs/implementation/01-setup_dependencies.md
|
|
49
|
+
- docs/implementation/02-error_handling.md
|
|
50
|
+
- docs/implementation/03-core_components.md
|
|
51
|
+
- docs/implementation/03.5-variable_preservation.md
|
|
52
|
+
- docs/implementation/04-provider_architecture.md
|
|
53
|
+
- docs/implementation/05-translation_logic.md
|
|
54
|
+
- docs/implementation/06-main_module_api.md
|
|
55
|
+
- docs/implementation/07-direct_translation_helpers.md
|
|
56
|
+
- docs/implementation/08-rails_integration.md
|
|
57
|
+
- docs/implementation/09-testing_suite.md
|
|
58
|
+
- docs/implementation/10-documentation_examples.md
|
|
59
|
+
- docs/implementation/11-quality_security.md
|
|
60
|
+
- docs/implementation/12-cli_standalone.md
|
|
61
|
+
- exe/better_translate
|
|
219
62
|
- lib/better_translate.rb
|
|
220
|
-
- lib/better_translate/
|
|
221
|
-
- lib/better_translate/
|
|
63
|
+
- lib/better_translate/cache.rb
|
|
64
|
+
- lib/better_translate/cli.rb
|
|
65
|
+
- lib/better_translate/configuration.rb
|
|
66
|
+
- lib/better_translate/direct_translator.rb
|
|
67
|
+
- lib/better_translate/errors.rb
|
|
68
|
+
- lib/better_translate/progress_tracker.rb
|
|
69
|
+
- lib/better_translate/provider_factory.rb
|
|
70
|
+
- lib/better_translate/providers/anthropic_provider.rb
|
|
71
|
+
- lib/better_translate/providers/base_http_provider.rb
|
|
222
72
|
- lib/better_translate/providers/chatgpt_provider.rb
|
|
223
73
|
- lib/better_translate/providers/gemini_provider.rb
|
|
224
|
-
- lib/better_translate/
|
|
225
|
-
- lib/better_translate/
|
|
74
|
+
- lib/better_translate/railtie.rb
|
|
75
|
+
- lib/better_translate/rate_limiter.rb
|
|
76
|
+
- lib/better_translate/strategies/base_strategy.rb
|
|
77
|
+
- lib/better_translate/strategies/batch_strategy.rb
|
|
78
|
+
- lib/better_translate/strategies/deep_strategy.rb
|
|
79
|
+
- lib/better_translate/strategies/strategy_selector.rb
|
|
226
80
|
- lib/better_translate/translator.rb
|
|
227
|
-
- lib/better_translate/utils.rb
|
|
81
|
+
- lib/better_translate/utils/hash_flattener.rb
|
|
82
|
+
- lib/better_translate/validator.rb
|
|
83
|
+
- lib/better_translate/variable_extractor.rb
|
|
228
84
|
- lib/better_translate/version.rb
|
|
229
|
-
- lib/better_translate/
|
|
230
|
-
- lib/generators/better_translate/
|
|
231
|
-
- lib/generators/better_translate/
|
|
232
|
-
- lib/generators/better_translate/
|
|
233
|
-
- lib/generators/better_translate/
|
|
85
|
+
- lib/better_translate/yaml_handler.rb
|
|
86
|
+
- lib/generators/better_translate/analyze/USAGE
|
|
87
|
+
- lib/generators/better_translate/analyze/analyze_generator.rb
|
|
88
|
+
- lib/generators/better_translate/install/USAGE
|
|
89
|
+
- lib/generators/better_translate/install/install_generator.rb
|
|
90
|
+
- lib/generators/better_translate/install/templates/README
|
|
91
|
+
- lib/generators/better_translate/install/templates/initializer.rb.tt
|
|
92
|
+
- lib/generators/better_translate/translate/USAGE
|
|
93
|
+
- lib/generators/better_translate/translate/translate_generator.rb
|
|
94
|
+
- lib/tasks/better_translate.rake
|
|
234
95
|
- sig/better_translate.rbs
|
|
96
|
+
- sig/better_translate/cache.rbs
|
|
97
|
+
- sig/better_translate/cli.rbs
|
|
98
|
+
- sig/better_translate/configuration.rbs
|
|
99
|
+
- sig/better_translate/direct_translator.rbs
|
|
100
|
+
- sig/better_translate/errors.rbs
|
|
101
|
+
- sig/better_translate/progress_tracker.rbs
|
|
102
|
+
- sig/better_translate/provider_factory.rbs
|
|
103
|
+
- sig/better_translate/providers/anthropic_provider.rbs
|
|
104
|
+
- sig/better_translate/providers/base_http_provider.rbs
|
|
105
|
+
- sig/better_translate/providers/chatgpt_provider.rbs
|
|
106
|
+
- sig/better_translate/providers/gemini_provider.rbs
|
|
107
|
+
- sig/better_translate/railtie.rbs
|
|
108
|
+
- sig/better_translate/rate_limiter.rbs
|
|
109
|
+
- sig/better_translate/strategies/base_strategy.rbs
|
|
110
|
+
- sig/better_translate/strategies/batch_strategy.rbs
|
|
111
|
+
- sig/better_translate/strategies/deep_strategy.rbs
|
|
112
|
+
- sig/better_translate/strategies/strategy_selector.rbs
|
|
113
|
+
- sig/better_translate/translator.rbs
|
|
114
|
+
- sig/better_translate/utils/hash_flattener.rbs
|
|
115
|
+
- sig/better_translate/validator.rbs
|
|
116
|
+
- sig/better_translate/variable_extractor.rbs
|
|
117
|
+
- sig/better_translate/version.rbs
|
|
118
|
+
- sig/better_translate/yaml_handler.rbs
|
|
119
|
+
- sig/faraday.rbs
|
|
120
|
+
- sig/generators/better_translate/analyze/analyze_generator.rbs
|
|
121
|
+
- sig/generators/better_translate/install/install_generator.rbs
|
|
122
|
+
- sig/generators/better_translate/translate/translate_generator.rbs
|
|
123
|
+
- sig/optparse.rbs
|
|
124
|
+
- sig/psych.rbs
|
|
125
|
+
- sig/rails.rbs
|
|
235
126
|
homepage: https://github.com/alessiobussolari/better_translate
|
|
236
127
|
licenses:
|
|
237
128
|
- MIT
|
|
238
129
|
metadata:
|
|
239
|
-
allowed_push_host: https://rubygems.org
|
|
240
|
-
rubygems_mfa_required: 'true'
|
|
241
130
|
homepage_uri: https://github.com/alessiobussolari/better_translate
|
|
242
131
|
source_code_uri: https://github.com/alessiobussolari/better_translate
|
|
243
132
|
changelog_uri: https://github.com/alessiobussolari/better_translate/blob/main/CHANGELOG.md
|
|
244
|
-
bug_tracker_uri: https://github.com/alessiobussolari/better_translate/issues
|
|
245
|
-
documentation_uri: https://github.com/alessiobussolari/better_translate
|
|
246
|
-
wiki_uri: https://github.com/alessiobussolari/better_translate/wiki
|
|
247
133
|
post_install_message:
|
|
248
134
|
rdoc_options: []
|
|
249
135
|
require_paths:
|
|
@@ -262,5 +148,5 @@ requirements: []
|
|
|
262
148
|
rubygems_version: 3.5.11
|
|
263
149
|
signing_key:
|
|
264
150
|
specification_version: 4
|
|
265
|
-
summary: AI-powered YAML
|
|
151
|
+
summary: AI-powered YAML locale file translator for Rails and Ruby projects
|
|
266
152
|
test_files: []
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
module BetterTranslate
|
|
2
|
-
# Helper class that provides utility methods for translating text and arrays of text
|
|
3
|
-
# to multiple target languages using different translation providers.
|
|
4
|
-
# This class simplifies the process of translating content by abstracting away
|
|
5
|
-
# the provider-specific implementation details.
|
|
6
|
-
#
|
|
7
|
-
# @example Translating a single text to multiple languages
|
|
8
|
-
# BetterTranslate::Helper.translate_text_to_languages(
|
|
9
|
-
# "Hello world!",
|
|
10
|
-
# [{ short_name: "it", name: "Italian" }, { short_name: "fr", name: "French" }],
|
|
11
|
-
# "en",
|
|
12
|
-
# :chatgpt
|
|
13
|
-
# )
|
|
14
|
-
class Helper
|
|
15
|
-
class << self
|
|
16
|
-
# Translates a given text into multiple target languages.
|
|
17
|
-
#
|
|
18
|
-
# @param text [String] The text to be translated.
|
|
19
|
-
# @param target_languages [Array<Hash>] Array of target language hashes,
|
|
20
|
-
# e.g. [{ short_name: "it", name: "Italian" }, { short_name: "fr", name: "French" }]
|
|
21
|
-
# @param source_language [String] The source language code (e.g., "en").
|
|
22
|
-
# @param provider_name [Symbol] The provider to use (e.g., :chatgpt or :gemini).
|
|
23
|
-
# @return [Hash] A hash where each key is a target language code and the value is the translated text.
|
|
24
|
-
#
|
|
25
|
-
# Example:
|
|
26
|
-
# translated = BetterTranslate::Helpers.translate_text_to_languages(
|
|
27
|
-
# "Hello world!",
|
|
28
|
-
# [{ short_name: "it", name: "Italian" }, { short_name: "fr", name: "French" }],
|
|
29
|
-
# "en",
|
|
30
|
-
# :chatgpt
|
|
31
|
-
# )
|
|
32
|
-
# # => { "it" => "Ciao mondo!", "fr" => "Bonjour le monde!" }
|
|
33
|
-
def translate_text_to_languages(text, target_languages, source_language, provider_name)
|
|
34
|
-
provider_instance = case provider_name
|
|
35
|
-
when :chatgpt
|
|
36
|
-
Providers::ChatgptProvider.new(BetterTranslate.configuration.openai_key)
|
|
37
|
-
when :gemini
|
|
38
|
-
Providers::GeminiProvider.new(BetterTranslate.configuration.google_gemini_key)
|
|
39
|
-
else
|
|
40
|
-
raise "Provider not supported: #{provider_name}"
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
result = {}
|
|
44
|
-
target_languages.each do |lang|
|
|
45
|
-
# Optionally, you could also pass the source_language if needed by your provider.
|
|
46
|
-
translated_text = provider_instance.translate(text, lang[:short_name], lang[:name])
|
|
47
|
-
result[lang[:short_name]] = translated_text
|
|
48
|
-
end
|
|
49
|
-
result
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
# Translates an array of texts into multiple target languages using the translate_text_to_languages helper.
|
|
53
|
-
#
|
|
54
|
-
# @param texts [Array<String>] An array of texts to translate.
|
|
55
|
-
# @param target_languages [Array<Hash>] Array of target language hashes,
|
|
56
|
-
# e.g. [{ short_name: "it", name: "Italian" }, { short_name: "fr", name: "French" }].
|
|
57
|
-
# @param source_language [String] The source language code (e.g., "en").
|
|
58
|
-
# @param provider_name [Symbol] The provider to use (e.g., :chatgpt or :gemini).
|
|
59
|
-
# @return [Hash] A hash where each key is a target language code and the value is an array of translated texts.
|
|
60
|
-
#
|
|
61
|
-
# Example:
|
|
62
|
-
# texts = ["Hello world!", "How are you?"]
|
|
63
|
-
# result = BetterTranslate::TranslationHelper.translate_texts_to_languages(
|
|
64
|
-
# texts,
|
|
65
|
-
# [{ short_name: "it", name: "Italian" }, { short_name: "fr", name: "French" }],
|
|
66
|
-
# "en",
|
|
67
|
-
# :chatgpt
|
|
68
|
-
# )
|
|
69
|
-
# # => { "it" => ["Ciao mondo!", "Come stai?"], "fr" => ["Bonjour le monde!", "Comment ça va?"] }
|
|
70
|
-
def translate_texts_to_languages(texts, target_languages, source_language, provider_name)
|
|
71
|
-
result = {}
|
|
72
|
-
target_languages.each do |lang|
|
|
73
|
-
# For each target language, translate each text and collect translations into an array.
|
|
74
|
-
result[lang[:short_name]] = texts.map do |text|
|
|
75
|
-
translation_hash = translate_text_to_languages(text, [lang], source_language, provider_name)
|
|
76
|
-
translation_hash[lang[:short_name]]
|
|
77
|
-
end
|
|
78
|
-
end
|
|
79
|
-
result
|
|
80
|
-
end
|
|
81
|
-
end
|
|
82
|
-
end
|
|
83
|
-
end
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
module BetterTranslate
|
|
2
|
-
module Providers
|
|
3
|
-
# Abstract base class for translation providers.
|
|
4
|
-
# Provides common functionality and defines the interface that all providers must implement.
|
|
5
|
-
# Handles rate limiting, input validation, and retry logic for failed translations.
|
|
6
|
-
#
|
|
7
|
-
# @abstract Subclass and override {#translate_text} to implement a provider
|
|
8
|
-
class BaseProvider
|
|
9
|
-
# Number of retry attempts for failed translations
|
|
10
|
-
MAX_RETRIES = 3
|
|
11
|
-
|
|
12
|
-
# Delay in seconds between retry attempts
|
|
13
|
-
RETRY_DELAY = 2 # seconds
|
|
14
|
-
|
|
15
|
-
# Initializes a new provider instance with the specified API key.
|
|
16
|
-
#
|
|
17
|
-
# @param api_key [String] The API key for the translation service
|
|
18
|
-
# @return [BaseProvider] A new instance of the provider
|
|
19
|
-
def initialize(api_key)
|
|
20
|
-
@api_key = api_key
|
|
21
|
-
@last_request_time = Time.now - 1
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
private
|
|
25
|
-
|
|
26
|
-
# Implements a simple rate limiting mechanism to prevent overloading the API.
|
|
27
|
-
# Ensures at least 0.5 seconds between consecutive requests.
|
|
28
|
-
#
|
|
29
|
-
# @return [void]
|
|
30
|
-
def rate_limit
|
|
31
|
-
time_since_last_request = Time.now - @last_request_time
|
|
32
|
-
sleep(0.5 - time_since_last_request) if time_since_last_request < 0.5
|
|
33
|
-
@last_request_time = Time.now
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
# Validates the input parameters for translation.
|
|
37
|
-
# Ensures that the text is not empty and the language code is in a valid format.
|
|
38
|
-
#
|
|
39
|
-
# @param text [String] The text to translate
|
|
40
|
-
# @param target_lang_code [String] The target language code (e.g., "en", "fr-FR")
|
|
41
|
-
# @raise [ArgumentError] If the text is empty or the language code is invalid
|
|
42
|
-
# @return [void]
|
|
43
|
-
def validate_input(text, target_lang_code)
|
|
44
|
-
raise ArgumentError, "Text cannot be empty" if text.nil? || text.strip.empty?
|
|
45
|
-
raise ArgumentError, "Invalid target language code" unless target_lang_code.match?(/^[a-z]{2}(-[A-Z]{2})?$/)
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
# Public method to translate text with built-in retry logic.
|
|
49
|
-
# Attempts to translate the text and retries on failure up to MAX_RETRIES times.
|
|
50
|
-
#
|
|
51
|
-
# @param text [String] The text to translate
|
|
52
|
-
# @param target_lang_code [String] The target language code (e.g., "en")
|
|
53
|
-
# @param target_lang_name [String] The target language name (e.g., "English")
|
|
54
|
-
# @return [String] The translated text
|
|
55
|
-
# @raise [StandardError] If translation fails after all retry attempts
|
|
56
|
-
def translate(text, target_lang_code, target_lang_name)
|
|
57
|
-
retries = 0
|
|
58
|
-
begin
|
|
59
|
-
perform_translation(text, target_lang_code, target_lang_name)
|
|
60
|
-
rescue StandardError => e
|
|
61
|
-
retries += 1
|
|
62
|
-
if retries <= MAX_RETRIES
|
|
63
|
-
message = "Translation attempt #{retries} failed. Retrying in #{RETRY_DELAY} seconds..."
|
|
64
|
-
BetterTranslate::Utils.logger(message: message)
|
|
65
|
-
sleep(RETRY_DELAY)
|
|
66
|
-
retry
|
|
67
|
-
else
|
|
68
|
-
message = "Translation failed after #{MAX_RETRIES} attempts: #{e.message}"
|
|
69
|
-
BetterTranslate::Utils.logger(message: message)
|
|
70
|
-
raise
|
|
71
|
-
end
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
# Performs the actual translation process.
|
|
76
|
-
# Validates input, applies rate limiting, and calls the provider-specific translation method.
|
|
77
|
-
#
|
|
78
|
-
# @param text [String] The text to translate
|
|
79
|
-
# @param target_lang_code [String] The target language code
|
|
80
|
-
# @param target_lang_name [String] The target language name
|
|
81
|
-
# @return [String] The translated text
|
|
82
|
-
def perform_translation(text, target_lang_code, target_lang_name)
|
|
83
|
-
validate_input(text, target_lang_code)
|
|
84
|
-
rate_limit
|
|
85
|
-
translate_text(text, target_lang_code, target_lang_name)
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
# Provider-specific implementation of the translation logic.
|
|
89
|
-
# Must be overridden by subclasses to implement the actual translation.
|
|
90
|
-
#
|
|
91
|
-
# @abstract
|
|
92
|
-
# @param text [String] The text to translate
|
|
93
|
-
# @param target_lang_code [String] The target language code
|
|
94
|
-
# @param target_lang_name [String] The target language name
|
|
95
|
-
# @return [String] The translated text
|
|
96
|
-
# @raise [NotImplementedError] If the method is not overridden by a subclass
|
|
97
|
-
def translate_text(text, target_lang_code, target_lang_name)
|
|
98
|
-
raise NotImplementedError, "The provider #{self.class} must implement the translate_text method"
|
|
99
|
-
end
|
|
100
|
-
end
|
|
101
|
-
end
|
|
102
|
-
end
|
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
module BetterTranslate
|
|
2
|
-
# Service class that handles translation requests using the configured provider.
|
|
3
|
-
# Implements a Least Recently Used (LRU) cache to avoid redundant translation requests.
|
|
4
|
-
# Supports built-in providers (ChatGPT, Gemini) and custom providers registered via
|
|
5
|
-
# the register_provider class method.
|
|
6
|
-
#
|
|
7
|
-
# @example
|
|
8
|
-
# service = BetterTranslate::Service.new
|
|
9
|
-
# translated_text = service.translate("Hello world", "fr", "French")
|
|
10
|
-
class Service
|
|
11
|
-
# Maximum number of translations to keep in the LRU cache
|
|
12
|
-
MAX_CACHE_SIZE = 1000
|
|
13
|
-
|
|
14
|
-
# Registry for custom providers
|
|
15
|
-
@@provider_registry = {}
|
|
16
|
-
|
|
17
|
-
# Initializes a new Service instance.
|
|
18
|
-
# Sets up the translation provider based on configuration and initializes the LRU cache.
|
|
19
|
-
#
|
|
20
|
-
# @return [BetterTranslate::Service] A new Service instance
|
|
21
|
-
def initialize
|
|
22
|
-
@provider_name = BetterTranslate.configuration.provider
|
|
23
|
-
@translation_cache = {}
|
|
24
|
-
@cache_order = []
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
# Translates text using the configured provider with caching support.
|
|
28
|
-
# First checks if the translation is already in the cache. If not, it uses the
|
|
29
|
-
# provider to translate the text and then caches the result for future use.
|
|
30
|
-
# Also tracks metrics about the translation request duration.
|
|
31
|
-
#
|
|
32
|
-
# @param text [String] The text to translate
|
|
33
|
-
# @param target_lang_code [String] The target language code (e.g., 'fr', 'es')
|
|
34
|
-
# @param target_lang_name [String] The target language name (e.g., 'French', 'Spanish')
|
|
35
|
-
# @return [String] The translated text
|
|
36
|
-
def translate(text, target_lang_code, target_lang_name)
|
|
37
|
-
cache_key = "#{text}:#{target_lang_code}"
|
|
38
|
-
|
|
39
|
-
# Prova a recuperare dalla cache
|
|
40
|
-
cached = cache_get(cache_key)
|
|
41
|
-
return cached if cached
|
|
42
|
-
|
|
43
|
-
# Traduci e salva in cache
|
|
44
|
-
start_time = Time.now
|
|
45
|
-
result = provider_instance.translate_text(text, target_lang_code, target_lang_name)
|
|
46
|
-
duration = Time.now - start_time
|
|
47
|
-
|
|
48
|
-
BetterTranslate::Utils.track_metric("translation_request_duration", {
|
|
49
|
-
provider: @provider_name,
|
|
50
|
-
text_length: text.length,
|
|
51
|
-
duration: duration
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
cache_set(cache_key, result)
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
private
|
|
58
|
-
|
|
59
|
-
# Retrieves a translation from the LRU cache if it exists.
|
|
60
|
-
# Updates the cache order to mark this key as most recently used.
|
|
61
|
-
#
|
|
62
|
-
# @param key [String] The cache key in the format "text:target_lang_code"
|
|
63
|
-
# @return [String, nil] The cached translation or nil if not found
|
|
64
|
-
def cache_get(key)
|
|
65
|
-
if @translation_cache.key?(key)
|
|
66
|
-
# Aggiorna l'ordine LRU
|
|
67
|
-
@cache_order.delete(key)
|
|
68
|
-
@cache_order.push(key)
|
|
69
|
-
@translation_cache[key]
|
|
70
|
-
end
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
# Stores a translation in the LRU cache.
|
|
74
|
-
# If the cache is full, removes the least recently used item before adding the new one.
|
|
75
|
-
#
|
|
76
|
-
# @param key [String] The cache key in the format "text:target_lang_code"
|
|
77
|
-
# @param value [String] The translated text to cache
|
|
78
|
-
# @return [String] The value that was cached
|
|
79
|
-
def cache_set(key, value)
|
|
80
|
-
if @translation_cache.size >= MAX_CACHE_SIZE
|
|
81
|
-
# Rimuovi l'elemento meno recentemente usato
|
|
82
|
-
oldest_key = @cache_order.shift
|
|
83
|
-
@translation_cache.delete(oldest_key)
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
@translation_cache[key] = value
|
|
87
|
-
@cache_order.push(key)
|
|
88
|
-
value
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
# Creates or returns a cached instance of the translation provider.
|
|
94
|
-
# The provider is determined by the configuration and instantiated with the appropriate API key.
|
|
95
|
-
# Supports built-in providers (ChatGPT, Gemini) and custom providers registered via
|
|
96
|
-
# the register_provider class method.
|
|
97
|
-
#
|
|
98
|
-
# @return [BetterTranslate::Providers::BaseProvider] An instance of the configured translation provider
|
|
99
|
-
# @raise [RuntimeError] If the configured provider is not supported
|
|
100
|
-
def provider_instance
|
|
101
|
-
@provider_instance ||= case @provider_name
|
|
102
|
-
when :chatgpt
|
|
103
|
-
Providers::ChatgptProvider.new(BetterTranslate.configuration.openai_key)
|
|
104
|
-
when :gemini
|
|
105
|
-
Providers::GeminiProvider.new(BetterTranslate.configuration.google_gemini_key)
|
|
106
|
-
else
|
|
107
|
-
if @@provider_registry.key?(@provider_name)
|
|
108
|
-
# Get the API key from configuration dynamically
|
|
109
|
-
api_key_method = "#{@provider_name}_key".to_sym
|
|
110
|
-
if BetterTranslate.configuration.respond_to?(api_key_method)
|
|
111
|
-
api_key = BetterTranslate.configuration.send(api_key_method)
|
|
112
|
-
@@provider_registry[@provider_name].call(api_key)
|
|
113
|
-
else
|
|
114
|
-
raise "API key configuration missing for provider: #{@provider_name}. Add config.#{@provider_name}_key to your BetterTranslate configuration."
|
|
115
|
-
end
|
|
116
|
-
else
|
|
117
|
-
raise "Provider not supported: #{@provider_name}. Available providers: #{available_providers.join(', ')}"
|
|
118
|
-
end
|
|
119
|
-
end
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
# Returns a list of all available provider names
|
|
123
|
-
# @return [Array<Symbol>] List of available provider names
|
|
124
|
-
def available_providers
|
|
125
|
-
[:chatgpt, :gemini] + @@provider_registry.keys
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
# Registers a custom provider for use with BetterTranslate
|
|
129
|
-
#
|
|
130
|
-
# @param name [Symbol] The name of the provider to register
|
|
131
|
-
# @param factory [Proc] A proc that takes an API key and returns a provider instance
|
|
132
|
-
# @return [Symbol] The name of the registered provider
|
|
133
|
-
#
|
|
134
|
-
# @example Register a custom DeepL provider
|
|
135
|
-
# BetterTranslate::Service.register_provider(
|
|
136
|
-
# :deepl,
|
|
137
|
-
# ->(api_key) { Providers::DeepLProvider.new(api_key) }
|
|
138
|
-
# )
|
|
139
|
-
def self.register_provider(name, factory)
|
|
140
|
-
@@provider_registry[name] = factory
|
|
141
|
-
name
|
|
142
|
-
end
|
|
143
|
-
end
|
|
144
|
-
end
|