better_translate 0.3.0 → 0.3.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1110968d8e49f85d5231775f9c05fcbca610021a33723999c75246b687f56b77
4
- data.tar.gz: 255e51ff5e5105ebf314b703d9aa841c66eaa6b0ffb5ca1f631ea2cbe81ab6db
3
+ metadata.gz: a2ff034a6db3498226ddff2111cf66e94a371f65741f82986b6ec8324daaf2dc
4
+ data.tar.gz: 991a78781adfc647d789729f385bca6e7688a6269bf102e0bff8b40b7bf8aca7
5
5
  SHA512:
6
- metadata.gz: b971940521bce1ad359990e13fe7d17a339f9d5e62fe5ea9b565fe1ffbd51fe2c2dd24789cd02b7e838a20a3ae88de919ed529adf325b3c11d7377c6bced68e5
7
- data.tar.gz: 1d8d785607ba4439df35ceb70b0c302ba1944bfdd74e197d2686cbe7781cfab55bfc93ed7a2affa9ef50b5fc1a459034b1d3cdf6f60420cb2df1b0252d5e3ac5
6
+ metadata.gz: 68f06a1006c8fc3e43184485d8f71ec9b080649fa68e2f8c5563d55b20c356b501a20a308f0c00f967b5387ac8d3f62abf11d837ca25b325a646658bf8126146
7
+ data.tar.gz: 719cb1a9bb0217934ca0ecacd455bcc6c81afab081fbc43d24b8add5a50eb33b7947ab715a37938460d162fdcf88e9e629b8cc18d08e1f47ecec085101c35e8c
data/CHANGELOG.md ADDED
@@ -0,0 +1,74 @@
1
+ # Changelog
2
+
3
+ All notable changes to BetterTranslate will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.3.1] - 2025-03-11
9
+
10
+ ### Added
11
+ - Comprehensive RSpec test suite covering:
12
+ - Core translation functionality
13
+ - LRU cache implementation
14
+ - Provider selection and initialization
15
+ - Error handling
16
+ - Configuration management
17
+ - Improved documentation with badges and testing information
18
+
19
+ ### Changed
20
+ - Made `translate` method public in `Service` class for better testability
21
+ - Reorganized README.md with better structure and modern layout
22
+
23
+ ## [0.3.0] - 2025-03-11
24
+
25
+ ### Added
26
+ - New translation helper methods:
27
+ - `translate_text_to_languages`: Translate single text to multiple languages
28
+ - `translate_texts_to_languages`: Translate multiple texts to multiple languages
29
+ - LRU caching for improved performance
30
+
31
+ ### Changed
32
+ - Enhanced error handling in translation providers
33
+ - Improved method documentation
34
+
35
+ ## [0.2.0] - 2025-03-11
36
+
37
+ ### Added
38
+ - Two-step filtering process:
39
+ - Global exclusions using `global_exclusions`
40
+ - Language-specific exclusions using `exclusions_per_language`
41
+
42
+ ### Changed
43
+ - Improved filtering logic to handle language-specific exclusions independently
44
+ - Enhanced documentation for exclusion configuration
45
+
46
+ ### Fixed
47
+ - Issue with language-specific exclusions affecting global filtering
48
+
49
+ ## [0.1.1] - 2025-03-10
50
+
51
+ ### Added
52
+ - New Rails generator: `rails generate better_translate:translate`
53
+ - Triggers translation process
54
+ - Displays progress messages
55
+ - Integrates with existing configuration
56
+
57
+ ## [0.1.0] - 2025-03-10
58
+
59
+ ### Added
60
+ - Initial release with core features:
61
+ - YAML file translation from source to multiple target languages
62
+ - Multiple provider support (ChatGPT and Google Gemini)
63
+ - Progress tracking with ruby-progressbar
64
+ - Centralized configuration via initializer
65
+ - Two translation modes: override and incremental
66
+ - Key exclusion system
67
+ - Rails integration
68
+
69
+ ### Configuration
70
+ - API key management for providers
71
+ - Source and target language settings
72
+ - Output folder configuration
73
+ - Translation mode selection
74
+ - Exclusion patterns support
@@ -0,0 +1,132 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ We as members, contributors, and leaders pledge to make participation in our
6
+ community a harassment-free experience for everyone, regardless of age, body
7
+ size, visible or invisible disability, ethnicity, sex characteristics, gender
8
+ identity and expression, level of experience, education, socio-economic status,
9
+ nationality, personal appearance, race, caste, color, religion, or sexual
10
+ identity and orientation.
11
+
12
+ We pledge to act and interact in ways that contribute to an open, welcoming,
13
+ diverse, inclusive, and healthy community.
14
+
15
+ ## Our Standards
16
+
17
+ Examples of behavior that contributes to a positive environment for our
18
+ community include:
19
+
20
+ * Demonstrating empathy and kindness toward other people
21
+ * Being respectful of differing opinions, viewpoints, and experiences
22
+ * Giving and gracefully accepting constructive feedback
23
+ * Accepting responsibility and apologizing to those affected by our mistakes,
24
+ and learning from the experience
25
+ * Focusing on what is best not just for us as individuals, but for the overall
26
+ community
27
+
28
+ Examples of unacceptable behavior include:
29
+
30
+ * The use of sexualized language or imagery, and sexual attention or advances of
31
+ any kind
32
+ * Trolling, insulting or derogatory comments, and personal or political attacks
33
+ * Public or private harassment
34
+ * Publishing others' private information, such as a physical or email address,
35
+ without their explicit permission
36
+ * Other conduct which could reasonably be considered inappropriate in a
37
+ professional setting
38
+
39
+ ## Enforcement Responsibilities
40
+
41
+ Community leaders are responsible for clarifying and enforcing our standards of
42
+ acceptable behavior and will take appropriate and fair corrective action in
43
+ response to any behavior that they deem inappropriate, threatening, offensive,
44
+ or harmful.
45
+
46
+ Community leaders have the right and responsibility to remove, edit, or reject
47
+ comments, commits, code, wiki edits, issues, and other contributions that are
48
+ not aligned to this Code of Conduct, and will communicate reasons for moderation
49
+ decisions when appropriate.
50
+
51
+ ## Scope
52
+
53
+ This Code of Conduct applies within all community spaces, and also applies when
54
+ an individual is officially representing the community in public spaces.
55
+ Examples of representing our community include using an official email address,
56
+ posting via an official social media account, or acting as an appointed
57
+ representative at an online or offline event.
58
+
59
+ ## Enforcement
60
+
61
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
62
+ reported to the community leaders responsible for enforcement at
63
+ [INSERT CONTACT METHOD].
64
+ All complaints will be reviewed and investigated promptly and fairly.
65
+
66
+ All community leaders are obligated to respect the privacy and security of the
67
+ reporter of any incident.
68
+
69
+ ## Enforcement Guidelines
70
+
71
+ Community leaders will follow these Community Impact Guidelines in determining
72
+ the consequences for any action they deem in violation of this Code of Conduct:
73
+
74
+ ### 1. Correction
75
+
76
+ **Community Impact**: Use of inappropriate language or other behavior deemed
77
+ unprofessional or unwelcome in the community.
78
+
79
+ **Consequence**: A private, written warning from community leaders, providing
80
+ clarity around the nature of the violation and an explanation of why the
81
+ behavior was inappropriate. A public apology may be requested.
82
+
83
+ ### 2. Warning
84
+
85
+ **Community Impact**: A violation through a single incident or series of
86
+ actions.
87
+
88
+ **Consequence**: A warning with consequences for continued behavior. No
89
+ interaction with the people involved, including unsolicited interaction with
90
+ those enforcing the Code of Conduct, for a specified period of time. This
91
+ includes avoiding interactions in community spaces as well as external channels
92
+ like social media. Violating these terms may lead to a temporary or permanent
93
+ ban.
94
+
95
+ ### 3. Temporary Ban
96
+
97
+ **Community Impact**: A serious violation of community standards, including
98
+ sustained inappropriate behavior.
99
+
100
+ **Consequence**: A temporary ban from any sort of interaction or public
101
+ communication with the community for a specified period of time. No public or
102
+ private interaction with the people involved, including unsolicited interaction
103
+ with those enforcing the Code of Conduct, is allowed during this period.
104
+ Violating these terms may lead to a permanent ban.
105
+
106
+ ### 4. Permanent Ban
107
+
108
+ **Community Impact**: Demonstrating a pattern of violation of community
109
+ standards, including sustained inappropriate behavior, harassment of an
110
+ individual, or aggression toward or disparagement of classes of individuals.
111
+
112
+ **Consequence**: A permanent ban from any sort of public interaction within the
113
+ community.
114
+
115
+ ## Attribution
116
+
117
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage],
118
+ version 2.1, available at
119
+ [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
120
+
121
+ Community Impact Guidelines were inspired by
122
+ [Mozilla's code of conduct enforcement ladder][Mozilla CoC].
123
+
124
+ For answers to common questions about this code of conduct, see the FAQ at
125
+ [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
126
+ [https://www.contributor-covenant.org/translations][translations].
127
+
128
+ [homepage]: https://www.contributor-covenant.org
129
+ [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
130
+ [Mozilla CoC]: https://github.com/mozilla/diversity
131
+ [FAQ]: https://www.contributor-covenant.org/faq
132
+ [translations]: https://www.contributor-covenant.org/translations
data/README.md CHANGED
@@ -1,30 +1,39 @@
1
- # BetterTranslate
2
-
3
- BetterTranslate is a Ruby gem that enables you to translate YAML files from a source language into one or more target languages using translation providers such as ChatGPT (OpenAI) and Google Gemini. The gem supports two translation modes:
4
-
5
- - **Override**: Completely rewrites the translated file.
6
- - **Incremental**: Updates the translated file only with new or modified keys while keeping the existing ones.
7
-
8
- Configuration is centralized via an initializer (for example, in a Rails app), where you can set API keys, the source language, target languages, key exclusions, the output folder, and the translation mode. Additionally, BetterTranslate integrates progress tracking using the [ruby-progressbar](https://github.com/jfelchner/ruby-progressbar) gem.
9
-
10
- ## Features
11
-
12
- - **Multi-language YAML Translation**: Translates YAML files from a source language into one or more target languages.
13
- - **Multiple Providers**: Supports ChatGPT (OpenAI) and Google Gemini (with potential for extension to other providers in the future).
14
- - **Translation Modes**:
15
- - **Override**: Rewrites the file from scratch.
16
- - **Incremental**: Updates only missing or modified keys.
17
- - **Centralized Configuration**: Configured via an initializer with settings for API keys, source language, target languages, exclusions (using dot notation), and the output folder.
18
- - **Two-Step Exclusion Filtering**:
19
- - **Global Exclusions**: Removes keys defined in `global_exclusions` from the entire YAML structure.
20
- - **Language-Specific Exclusions**: Applies additional filtering using the `exclusions_per_language` map for each target language.
21
- - **Progress Bar**: Displays translation progress using ruby-progressbar.
22
- - **Rails Generators**:
23
- - `rails generate better_translate:install` to generate the initializer.
24
- - `rails generate better_translate:translate` to trigger the translation process directly from your Rails app.
25
- - **Translation Helpers**:
26
- - `translate_text_to_languages` for translating a single text into multiple target languages.
27
- - `translate_texts_to_languages` for translating an array of texts into multiple target languages.
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
+
18
+ ## Why BetterTranslate? 🤔
19
+
20
+ - 🌐 **AI-Powered Translation**: Leverage ChatGPT and Google Gemini for high-quality translations
21
+ - 🔄 **Smart Translation Modes**:
22
+ - `Override`: Full file rewrite
23
+ - `Incremental`: Update only new/modified keys
24
+ - 🎯 **Precise Control**:
25
+ - Global key exclusions
26
+ - Language-specific exclusions
27
+ - Dot notation support
28
+ - 🛠 **Developer-Friendly**:
29
+ - Rails generators included
30
+ - Comprehensive test suite
31
+ - LRU caching for performance
32
+ - Progress tracking
33
+ - 🔧 **Flexible Helpers**:
34
+ - Single text translation
35
+ - Bulk text translation
36
+ - Multiple target languages support
28
37
 
29
38
  ## Installation
30
39
 
@@ -194,11 +203,38 @@ puts translated_texts
194
203
  # }
195
204
  ```
196
205
 
197
- ## Contact & Feature Requests
206
+ ## Testing 🧪
198
207
 
199
- For suggestions, bug reports, or to request new features, please reach out via email at: **alessio.bussolari@pandev.it**.
208
+ BetterTranslate comes with a comprehensive test suite using RSpec. To run the tests:
200
209
 
201
- ## Conclusions
210
+ ```bash
211
+ bundle exec rspec
212
+ ```
213
+
214
+ The test suite covers:
215
+ - Core translation functionality
216
+ - Cache implementation (LRU)
217
+ - Provider selection and initialization
218
+ - Error handling
219
+ - Configuration management
220
+
221
+ ## Contributing 🤝
222
+
223
+ 1. Fork the repository
224
+ 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
225
+ 3. Commit your changes (`git commit -m 'Add amazing feature'`)
226
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
227
+ 5. Open a Pull Request
228
+
229
+ ## Contact & Support 📬
230
+
231
+ - **Email**: alessio.bussolari@pandev.it
232
+ - **Issues**: [GitHub Issues](https://github.com/alessiobussolari/better_translate/issues)
233
+ - **Discussions**: [GitHub Discussions](https://github.com/alessiobussolari/better_translate/discussions)
234
+
235
+ ## License 📄
236
+
237
+ This project is licensed under the MIT License - see the [LICENSE.txt](LICENSE.txt) file for details.
202
238
 
203
239
  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.
204
240
 
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ require "rubocop/rake_task"
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: %i[spec rubocop]
@@ -68,8 +68,4 @@ module BetterTranslate
68
68
  end
69
69
  end
70
70
  end
71
- end
72
-
73
- __END__
74
-
75
- translated = BetterTranslate::Helper.translate_text_to_languages("Hello world!",[{ short_name: "it", name: "Italian" }, { short_name: "fr", name: "French" }],"en",:chatgpt)
71
+ end
@@ -3,15 +3,57 @@ module BetterTranslate
3
3
  class BaseProvider
4
4
  def initialize(api_key)
5
5
  @api_key = api_key
6
+ @last_request_time = Time.now - 1
6
7
  end
7
8
 
8
- # Metodo da implementare nelle classi derivate.
9
- # @param text [String] testo da tradurre.
10
- # @param target_lang_code [String] codice della lingua di destinazione (es. "en").
11
- # @param target_lang_name [String] nome della lingua di destinazione (es. "English").
9
+ private
10
+
11
+ def rate_limit
12
+ time_since_last_request = Time.now - @last_request_time
13
+ sleep(0.5 - time_since_last_request) if time_since_last_request < 0.5
14
+ @last_request_time = Time.now
15
+ end
16
+
17
+ def validate_input(text, target_lang_code)
18
+ raise ArgumentError, "Text cannot be empty" if text.nil? || text.strip.empty?
19
+ raise ArgumentError, "Invalid target language code" unless target_lang_code.match?(/^[a-z]{2}(-[A-Z]{2})?$/)
20
+ end
21
+
22
+ # Method to be implemented in derived classes.
23
+ # @param text [String] text to translate.
24
+ # @param target_lang_code [String] target language code (e.g. "en").
25
+ # @param target_lang_name [String] target language name (e.g. "English").
12
26
  # @return [String] testo tradotto.
27
+ MAX_RETRIES = 3
28
+ RETRY_DELAY = 2 # seconds
29
+
13
30
  def translate(text, target_lang_code, target_lang_name)
14
- raise NotImplementedError, "Il provider #{self.class} deve implementare il metodo translate"
31
+ retries = 0
32
+ begin
33
+ perform_translation(text, target_lang_code, target_lang_name)
34
+ rescue StandardError => e
35
+ retries += 1
36
+ if retries <= MAX_RETRIES
37
+ message = "Translation attempt #{retries} failed. Retrying in #{RETRY_DELAY} seconds..."
38
+ BetterTranslate::Utils.logger(message: message)
39
+ sleep(RETRY_DELAY)
40
+ retry
41
+ else
42
+ message = "Translation failed after #{MAX_RETRIES} attempts: #{e.message}"
43
+ BetterTranslate::Utils.logger(message: message)
44
+ raise
45
+ end
46
+ end
47
+ end
48
+
49
+ def perform_translation(text, target_lang_code, target_lang_name)
50
+ validate_input(text, target_lang_code)
51
+ rate_limit
52
+ translate_text(text, target_lang_code, target_lang_name)
53
+ end
54
+
55
+ def translate_text(text, target_lang_code, target_lang_name)
56
+ raise NotImplementedError, "The provider #{self.class} must implement the translate_text method"
15
57
  end
16
58
  end
17
59
  end
@@ -1,19 +1,19 @@
1
1
  module BetterTranslate
2
2
  module Providers
3
3
  class ChatgptProvider < BaseProvider
4
- # Utilizza l'API di OpenAI per tradurre il testo.
5
- def translate(text, target_lang_code, target_lang_name)
4
+ # Uses the OpenAI API to translate the text.
5
+ def translate_text(text, target_lang_code, target_lang_name)
6
6
  uri = URI("https://api.openai.com/v1/chat/completions")
7
7
  headers = {
8
8
  "Content-Type" => "application/json",
9
9
  "Authorization" => "Bearer #{@api_key}"
10
10
  }
11
11
 
12
- # Costruiamo il prompt per tradurre il testo.
12
+ # Build the prompt to translate the text.
13
13
  body = {
14
14
  model: "gpt-3.5-turbo",
15
15
  messages: [
16
- { role: "system", content: "Sei un traduttore professionale. Traduci esattamente il seguente testo da #{BetterTranslate.configuration.source_language} a #{target_lang_name} senza aggiungere commenti, spiegazioni o alternative. Fornisci solamente la traduzione diretta:" },
16
+ { role: "system", content: "You are a professional translator. Translate the following text exactly from #{BetterTranslate.configuration.source_language} to #{target_lang_name} without adding comments, explanations, or alternatives. Provide only the direct translation:" },
17
17
  { role: "user", content: "#{text}" }
18
18
  ],
19
19
  temperature: 0.3
@@ -30,7 +30,7 @@ module BetterTranslate
30
30
  translated_text = json.dig("choices", 0, "message", "content")
31
31
  translated_text ? translated_text.strip : text
32
32
  else
33
- raise "Errore durante la traduzione con ChatGPT: #{response.body}"
33
+ raise "Error during translation with ChatGPT: #{response.body}"
34
34
  end
35
35
  end
36
36
  end
@@ -3,7 +3,7 @@ module BetterTranslate
3
3
  class GeminiProvider < BaseProvider
4
4
  # Esempio di implementazione per Google Gemini.
5
5
  # Nota: L'endpoint e i parametri sono ipotetici e vanno sostituiti con quelli reali secondo la documentazione ufficiale.
6
- def translate(text, target_lang_code, target_lang_name)
6
+ def translate_text(text, target_lang_code, target_lang_name)
7
7
  uri = URI("https://gemini.googleapis.com/v1/translate")
8
8
  headers = {
9
9
  "Content-Type" => "application/json",
@@ -1,16 +1,60 @@
1
1
  module BetterTranslate
2
2
  class Service
3
+ MAX_CACHE_SIZE = 1000
4
+
3
5
  def initialize
4
6
  @provider_name = BetterTranslate.configuration.provider
7
+ @translation_cache = {}
8
+ @cache_order = []
5
9
  end
6
10
 
7
- # Metodo per tradurre un testo utilizzando il provider selezionato.
11
+ # Method to translate a text using the selected provider.
8
12
  def translate(text, target_lang_code, target_lang_name)
9
- provider_instance.translate(text, target_lang_code, target_lang_name)
13
+ cache_key = "#{text}:#{target_lang_code}"
14
+
15
+ # Prova a recuperare dalla cache
16
+ cached = cache_get(cache_key)
17
+ return cached if cached
18
+
19
+ # Traduci e salva in cache
20
+ start_time = Time.now
21
+ result = provider_instance.translate_text(text, target_lang_code, target_lang_name)
22
+ duration = Time.now - start_time
23
+
24
+ BetterTranslate::Utils.track_metric("translation_request_duration", {
25
+ provider: @provider_name,
26
+ text_length: text.length,
27
+ duration: duration
28
+ })
29
+
30
+ cache_set(cache_key, result)
10
31
  end
11
32
 
12
33
  private
13
34
 
35
+ def cache_get(key)
36
+ if @translation_cache.key?(key)
37
+ # Aggiorna l'ordine LRU
38
+ @cache_order.delete(key)
39
+ @cache_order.push(key)
40
+ @translation_cache[key]
41
+ end
42
+ end
43
+
44
+ def cache_set(key, value)
45
+ if @translation_cache.size >= MAX_CACHE_SIZE
46
+ # Rimuovi l'elemento meno recentemente usato
47
+ oldest_key = @cache_order.shift
48
+ @translation_cache.delete(oldest_key)
49
+ end
50
+
51
+ @translation_cache[key] = value
52
+ @cache_order.push(key)
53
+ value
54
+ end
55
+
56
+
57
+
14
58
  def provider_instance
15
59
  @provider_instance ||= case @provider_name
16
60
  when :chatgpt
@@ -2,65 +2,75 @@ module BetterTranslate
2
2
  class Translator
3
3
  class << self
4
4
  def work
5
- puts "Avvio della traduzione dei file..."
5
+ message = "Starting file translation..."
6
+ BetterTranslate::Utils.logger(message: message)
6
7
 
7
8
  translations = read_yml_source
8
9
 
9
- # Rimuove le chiavi da escludere (global_exclusions) dalla struttura letta
10
+ # Removes the keys to exclude (global_exclusions) from the read structure
10
11
  global_filtered_translations = remove_exclusions(
11
12
  translations, BetterTranslate.configuration.global_exclusions
12
13
  )
13
14
 
14
- BetterTranslate.configuration.target_languages.each do |target_lang|
15
+ start_time = Time.now
16
+ threads = BetterTranslate.configuration.target_languages.map do |target_lang|
17
+ Thread.new do
15
18
 
16
- # Fase 2: Applica il filtro specifico per la lingua target
19
+ # Phase 2: Apply the target language specific filter
17
20
  lang_exclusions = BetterTranslate.configuration.exclusions_per_language[target_lang[:short_name]] || []
18
21
  filtered_translations = remove_exclusions(
19
22
  global_filtered_translations, lang_exclusions
20
23
  )
21
24
 
22
- puts "Inizio traduzione da #{BetterTranslate.configuration.source_language} a #{target_lang[:short_name]}"
25
+ message = "Starting translation from #{BetterTranslate.configuration.source_language} to #{target_lang[:short_name]}"
26
+ BetterTranslate::Utils.logger(message: message)
23
27
  service = BetterTranslate::Service.new
24
28
  translated_data = translate_with_progress(filtered_translations, service, target_lang[:short_name], target_lang[:name])
25
29
  BetterTranslate::Writer.write_translations(translated_data, target_lang[:short_name])
26
- puts "Traduzione completata da #{BetterTranslate.configuration.source_language} a #{target_lang[:short_name]}"
30
+ end_time = Time.now
31
+ duration = end_time - start_time
32
+ BetterTranslate::Utils.track_metric("translation_duration", duration)
33
+
34
+ message = "Translation completed from #{BetterTranslate.configuration.source_language} to #{target_lang[:short_name]} in #{duration.round(2)} seconds"
35
+ BetterTranslate::Utils.logger(message: message)
36
+ end
27
37
  end
28
38
  end
29
39
 
30
40
  private
31
41
 
32
- # Legge il file YAML in base al percorso fornito.
42
+ # Reads the YAML file based on the provided path.
33
43
  #
34
- # @param file_path [String] percorso del file YAML da leggere
35
- # @return [Hash] struttura dati contenente le traduzioni
36
- # @raise [StandardError] se il file non esiste
44
+ # @param file_path [String] path of the YAML file to read
45
+ # @return [Hash] data structure containing the translations
46
+ # @raise [StandardError] if the file does not exist
37
47
  def read_yml_source
38
48
  file_path = BetterTranslate.configuration.input_file
39
49
  unless File.exist?(file_path)
40
- raise "File non trovato: #{file_path}"
50
+ raise "File not found: #{file_path}"
41
51
  end
42
52
 
43
53
  YAML.load_file(file_path)
44
54
  end
45
55
 
46
- # Rimuove le chiavi globali da escludere dalla struttura dati,
47
- # calcolando i percorsi a partire dal contenuto della lingua di partenza.
56
+ # Removes the global keys to exclude from the data structure,
57
+ # calculating paths starting from the source language content.
48
58
  #
49
- # Ad esempio, se il file YAML è:
59
+ # For example, if the YAML file is:
50
60
  # { "en" => { "sample" => { "valid" => "valid", "excluded" => "Excluded" } } }
51
- # e global_exclusions = ["sample.excluded"],
52
- # il risultato sarà:
61
+ # and global_exclusions = ["sample.excluded"],
62
+ # the result will be:
53
63
  # { "en" => { "sample" => { "valid" => "valid" } } }
54
64
  #
55
- # @param data [Hash, Array, Object] La struttura dati da filtrare.
56
- # @param global_exclusions [Array<String>] Lista dei percorsi (in dot notation) da escludere globalmente.
57
- # @param current_path [Array] Il percorso corrente (usato in maniera ricorsiva, default: []).
58
- # @return [Hash, Array, Object] La struttura dati filtrata.
65
+ # @param data [Hash, Array, Object] The data structure to filter.
66
+ # @param global_exclusions [Array<String>] List of paths (in dot notation) to exclude globally.
67
+ # @param current_path [Array] The current path (used recursively, default: []).
68
+ # @return [Hash, Array, Object] The filtered data structure.
59
69
  def remove_exclusions(data, exclusion_list, current_path = [])
60
70
  if data.is_a?(Hash)
61
71
  data.each_with_object({}) do |(key, value), result|
62
- # Se siamo al livello top-level e la chiave corrisponde alla lingua di partenza,
63
- # resettare il percorso (così da escludere "en" dal percorso finale)
72
+ # If we are at the top-level and the key matches the source language,
73
+ # reset the path (to exclude "en" from the final path)
64
74
  new_path = if current_path.empty? && key == BetterTranslate.configuration.source_language
65
75
  []
66
76
  else
@@ -81,14 +91,14 @@ module BetterTranslate
81
91
  end
82
92
  end
83
93
 
84
- # Metodo ricorsivo che percorre la struttura, traducendo ogni stringa e aggiornando il progresso.
94
+ # Recursive method that traverses the structure, translating each string and updating progress.
85
95
  #
86
- # @param data [Hash, Array, String] La struttura dati da tradurre.
87
- # @param provider [Object] Il provider che risponde al metodo translate.
88
- # @param target_lang_code [String] Codice della lingua target (es. "en").
89
- # @param target_lang_name [String] Nome della lingua target (es. "English").
90
- # @param progress [Hash] Un hash con le chiavi :count e :total per monitorare il progresso.
91
- # @return [Hash, Array, String] La struttura tradotta.
96
+ # @param data [Hash, Array, String] The data structure to translate.
97
+ # @param provider [Object] The provider that responds to the translate method.
98
+ # @param target_lang_code [String] Target language code (e.g. "en").
99
+ # @param target_lang_name [String] Target language name (e.g. "English").
100
+ # @param progress [Hash] A hash with :count and :total keys to monitor progress.
101
+ # @return [Hash, Array, String] The translated structure.
92
102
  def deep_translate_with_progress(data, service, target_lang_code, target_lang_name, progress)
93
103
  if data.is_a?(Hash)
94
104
  data.each_with_object({}) do |(key, value), result|
@@ -106,20 +116,90 @@ module BetterTranslate
106
116
  end
107
117
  end
108
118
 
109
- # Metodo principale per tradurre l'intera struttura dati, con monitoraggio del progresso.
119
+ # Main method to translate the entire data structure, with progress monitoring.
110
120
  #
111
- # @param data [Hash, Array, String] la struttura dati da tradurre
112
- # @param provider [Object] il provider da usare per tradurre (deve implementare translate)
113
- # @param target_lang_code [String] codice della lingua target
114
- # @param target_lang_name [String] nome della lingua target
115
- # @return la struttura tradotta
121
+ # @param data [Hash, Array, String] the data structure to translate
122
+ # @param provider [Object] the provider to use for translation (must implement translate)
123
+ # @param target_lang_code [String] target language code
124
+ # @param target_lang_name [String] target language name
125
+ # @return the translated structure
116
126
  def translate_with_progress(data, service, target_lang_code, target_lang_name)
117
127
  total = count_strings(data)
118
128
  progress = ProgressBar.create(total: total, format: '%a %B %p%% %t')
119
- deep_translate_with_progress(data, service, target_lang_code, target_lang_name, progress)
129
+
130
+ start_time = Time.now
131
+ result = if total > 50 # Usa il batch processing per dataset grandi
132
+ batch_translate_with_progress(data, service, target_lang_code, target_lang_name, progress)
133
+ else
134
+ deep_translate_with_progress(data, service, target_lang_code, target_lang_name, progress)
135
+ end
136
+
137
+ duration = Time.now - start_time
138
+ BetterTranslate::Utils.track_metric("translation_method_duration", {
139
+ method: total > 50 ? 'batch' : 'deep',
140
+ duration: duration,
141
+ total_strings: total
142
+ })
143
+
144
+ result
145
+ end
146
+
147
+ def batch_translate_with_progress(data, service, target_lang_code, target_lang_name, progress)
148
+ texts = extract_translatable_texts(data)
149
+ translations = {}
150
+
151
+ texts.each_slice(10).each_with_index do |batch, index|
152
+ batch_start = Time.now
153
+
154
+ batch_translations = batch.map do |text|
155
+ translated = service.translate(text, target_lang_code, target_lang_name)
156
+ progress.increment
157
+ [text, translated]
158
+ end.to_h
159
+
160
+ translations.merge!(batch_translations)
161
+
162
+ batch_duration = Time.now - batch_start
163
+ BetterTranslate::Utils.track_metric("batch_translation_duration", {
164
+ batch_number: index + 1,
165
+ size: batch.size,
166
+ duration: batch_duration
167
+ })
168
+ end
169
+
170
+ replace_translations(data, translations)
171
+ end
172
+
173
+ def extract_translatable_texts(data)
174
+ texts = Set.new
175
+ traverse_structure(data) do |value|
176
+ texts.add(value) if value.is_a?(String) && !value.strip.empty?
177
+ end
178
+ texts.to_a
179
+ end
180
+
181
+ def replace_translations(data, translations)
182
+ traverse_structure(data) do |value|
183
+ if value.is_a?(String) && !value.strip.empty? && translations.key?(value)
184
+ translations[value]
185
+ else
186
+ value
187
+ end
188
+ end
189
+ end
190
+
191
+ def traverse_structure(data, &block)
192
+ case data
193
+ when Hash
194
+ data.transform_values { |v| traverse_structure(v, &block) }
195
+ when Array
196
+ data.map { |v| traverse_structure(v, &block) }
197
+ else
198
+ yield data
199
+ end
120
200
  end
121
201
 
122
- # Conta ricorsivamente il numero di stringhe traducibili nella struttura dati.
202
+ # Recursively counts the number of translatable strings in the data structure.
123
203
  def count_strings(data)
124
204
  if data.is_a?(Hash)
125
205
  data.values.sum { |v| count_strings(v) }
@@ -4,21 +4,21 @@
4
4
  #
5
5
  # = BetterSeeder::Utils
6
6
  #
7
- # Questo modulo fornisce metodi di utilità per la gestione dei seed. In particolare,
8
- # consente di trasformare i nomi delle classi in formato snake_case con il suffisso "_structure.rb",
9
- # gestire i messaggi di log e configurare il livello del logger per ActiveRecord.
7
+ # This module provides utility methods for seed management. In particular,
8
+ # allows transforming class names into snake_case format with the "_structure.rb" suffix,
9
+ # manage log messages and configure the logger level for ActiveRecord.
10
10
 
11
11
  module BetterTranslate
12
12
  class Utils
13
13
  class << self
14
14
  ##
15
- # Registra un messaggio usando il logger di Rails se disponibile, altrimenti lo stampa su standard output.
15
+ # Logs a message using the Rails logger if available, otherwise prints it to standard output.
16
16
  #
17
17
  # ==== Parametri
18
- # * +message+ - Il messaggio da loggare (può essere una stringa o nil).
18
+ # * +message+ - The message to log (can be a string or nil).
19
19
  #
20
20
  # ==== Ritorno
21
- # Non ritorna un valore significativo.
21
+ # Does not return a significant value.
22
22
  #
23
23
  def logger(message: nil)
24
24
  if defined?(Rails) && Rails.respond_to?(:logger) && Rails.logger
@@ -27,6 +27,23 @@ module BetterTranslate
27
27
  puts message
28
28
  end
29
29
  end
30
+
31
+ def track_metric(name, value)
32
+ @metrics ||= {}
33
+ @metrics[name] ||= []
34
+ @metrics[name] << { value: value, timestamp: Time.now }
35
+
36
+ # Mantieni solo le ultime 1000 metriche per tipo
37
+ @metrics[name] = @metrics[name].last(1000) if @metrics[name].size > 1000
38
+ end
39
+
40
+ def get_metrics
41
+ @metrics || {}
42
+ end
43
+
44
+ def clear_metrics
45
+ @metrics = {}
46
+ end
30
47
  end
31
48
  end
32
49
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module BetterTranslate
4
- VERSION = "0.3.0"
4
+ VERSION = "0.3.1"
5
5
  end
@@ -1,19 +1,19 @@
1
1
  module BetterTranslate
2
2
  class Writer
3
3
  class << self
4
- # Scrive il file di traduzione per una lingua target.
5
- # Se il metodo di traduzione è :override, il file viene riscritto da zero;
6
- # se è :incremental, il file esistente viene aggiornato inserendo le nuove traduzioni nelle posizioni corrette.
4
+ # Writes the translation file for a target language.
5
+ # If the translation method is :override, the file is rewritten from scratch;
6
+ # if it's :incremental, the existing file is updated by inserting new translations in the correct positions.
7
7
  #
8
- # @param translated_data [Hash] La struttura dati tradotta (ad es. { "sample" => { "valid" => "tradotto", "new_key" => "nuova traduzione" } }).
9
- # @param target_lang_code [String] Codice della lingua target (es. "ru").
8
+ # @param translated_data [Hash] The translated data structure (e.g. { "sample" => { "valid" => "translated", "new_key" => "new translation" } }).
9
+ # @param target_lang_code [String] Target language code (e.g. "ru").
10
10
  def write_translations(translated_data, target_lang_code)
11
11
  output_folder = BetterTranslate.configuration.output_folder
12
12
  output_file = File.join(output_folder, "#{target_lang_code}.yml")
13
13
 
14
- # Riformatta la struttura per utilizzare il codice target come chiave principale.
14
+ # Reformats the structure to use the target code as the main key.
15
15
  output_content = if translated_data.is_a?(Hash) && translated_data.key?(BetterTranslate.configuration.source_language)
16
- # Sostituisce la chiave della lingua di partenza con quella target.
16
+ # Replaces the source language key with the target one.
17
17
  { target_lang_code => translated_data[BetterTranslate.configuration.source_language] }
18
18
  else
19
19
  { target_lang_code => translated_data }
@@ -23,11 +23,13 @@ module BetterTranslate
23
23
  existing_data = YAML.load_file(output_file)
24
24
  merged = deep_merge(existing_data, output_content)
25
25
  File.write(output_file, merged.to_yaml(line_width: -1))
26
- puts "File aggiornato in modalità incremental: #{output_file}"
26
+ message = "File updated in incremental mode: #{output_file}"
27
+ BetterTranslate::Utils.logger(message: message)
27
28
  else
28
29
  FileUtils.mkdir_p(output_folder) unless Dir.exist?(output_folder)
29
30
  File.write(output_file, output_content.to_yaml(line_width: -1))
30
- puts "File riscritto in modalità override: #{output_file}"
31
+ message = "File rewritten in override mode: #{output_file}"
32
+ BetterTranslate::Utils.logger(message: message)
31
33
  end
32
34
  end
33
35
 
@@ -35,12 +37,12 @@ module BetterTranslate
35
37
 
36
38
  # Metodo di deep merge: unisce in modo ricorsivo i due hash.
37
39
  # Se una chiave esiste in entrambi gli hash e i valori sono hash, li unisce ricorsivamente.
38
- # Altrimenti, se la chiave esiste già nell'hash esistente, la mantiene e non la sovrascrive.
40
+ # Otherwise, if the key already exists in the existing hash, it keeps it and doesn't overwrite it.
39
41
  # Se la chiave non esiste, la aggiunge.
40
42
  #
41
- # @param existing [Hash] hash esistente (file corrente)
42
- # @param new_data [Hash] nuovo hash con le traduzioni da unire
43
- # @return [Hash] hash unito
43
+ # @param existing [Hash] existing hash (current file)
44
+ # @param new_data [Hash] new hash with translations to merge
45
+ # @return [Hash] merged hash
44
46
  def deep_merge(existing, new_data)
45
47
  merged = existing.dup
46
48
  new_data.each do |key, value|
@@ -48,8 +50,8 @@ module BetterTranslate
48
50
  if merged[key].is_a?(Hash) && value.is_a?(Hash)
49
51
  merged[key] = deep_merge(merged[key], value)
50
52
  else
51
- # Se la chiave esiste già, non sovrascrivo il valore (modalità incrementale)
52
- # oppure potresti decidere di aggiornare comunque, a seconda delle esigenze.
53
+ # If the key already exists, don't overwrite the value (incremental mode)
54
+ # or you might decide to update anyway, depending on the requirements.
53
55
  merged[key] = merged[key]
54
56
  end
55
57
  else
@@ -12,45 +12,47 @@ require 'better_translate/providers/base_provider'
12
12
  require 'better_translate/providers/chatgpt_provider'
13
13
  require 'better_translate/providers/gemini_provider'
14
14
 
15
+ require 'ostruct'
16
+
15
17
  module BetterTranslate
16
18
  class << self
17
19
  attr_accessor :configuration
18
20
 
19
- # Metodo per configurare la gemma
21
+ # Method to configure the gem
20
22
  def configure
21
23
  self.configuration ||= OpenStruct.new
22
24
  yield(configuration) if block_given?
23
25
  end
24
26
 
25
- # Metodo install per generare il file di configurazione (initializer)
27
+ # Install method to generate the configuration file (initializer)
26
28
  def install
27
29
  unless defined?(Rails) && Rails.respond_to?(:root)
28
- message = "Il metodo install è disponibile solo in un'applicazione Rails."
30
+ message = "The install method is only available in a Rails application."
29
31
  BetterTranslate::Utils.logger(message: message)
30
32
  return
31
33
  end
32
34
 
33
- # Costruisce il percorso della cartella template all'interno della gemma
35
+ # Builds the path to the template folder inside the gem
34
36
  source = File.expand_path("../generators/better_translate/templates/better_translate.rb", __dir__)
35
37
  destination = File.join(Rails.root, "config", "initializers", "better_translate.rb")
36
38
 
37
39
  if File.exist?(destination)
38
- message = "Il file initializer esiste già: #{destination}"
40
+ message = "The initializer file already exists: #{destination}"
39
41
  BetterTranslate::Utils.logger(message: message)
40
42
  else
41
43
  FileUtils.mkdir_p(File.dirname(destination))
42
44
  FileUtils.cp(source, destination)
43
- message = "Il file initializer esiste già: #{destination}"
45
+ message = "The initializer file already exists: #{destination}"
44
46
  BetterTranslate::Utils.logger(message: message)
45
47
  end
46
48
  end
47
49
 
48
50
  def magic
49
- message = "Metodo magic invocato: eseguirò la traduzione dei file..."
51
+ message = "Magic method invoked: Translation will begin..."
50
52
  BetterTranslate::Utils.logger(message: message)
51
53
 
52
54
  BetterTranslate::Translator.work
53
- message = "Metodo magic invocato: Traduzione completata con successo!"
55
+ message = "Magic method invoked: Translation completed successfully!"
54
56
  BetterTranslate::Utils.logger(message: message)
55
57
  end
56
58
 
@@ -4,7 +4,7 @@ module BetterTranslate
4
4
  module Generators
5
5
  class InstallGenerator < Rails::Generators::Base
6
6
  source_root File.expand_path('templates', __dir__)
7
- desc "Genera il file config/initializers/better_translate.rb con la configurazione di default"
7
+ desc "Generates the config/initializers/better_translate.rb file with default configuration"
8
8
 
9
9
  def copy_initializer
10
10
  template "better_translate.rb", "config/initializers/better_translate.rb"
@@ -1,17 +1,17 @@
1
1
  BetterTranslate.configure do |config|
2
- # Scegli il provider da utilizzare: :chatgpt oppure :gemini
2
+ # Choose the provider to use: :chatgpt or :gemini
3
3
  config.provider = :chatgpt
4
4
 
5
- # API key per OpenAI
5
+ # API key for OpenAI
6
6
  config.openai_key = ENV.fetch("OPENAI_API_KEY") { "YOUR_OPENAI_API_KEY" }
7
7
 
8
- # API key per Google Gemini
8
+ # API key for Google Gemini
9
9
  config.google_gemini_key = ENV.fetch("GOOGLE_GEMINI_KEY") { "YOUR_GOOGLE_GEMINI_KEY" }
10
10
 
11
- # Lingua sorgente (ad esempio "en" se il file sorgente è in inglese)
11
+ # Source language (for example "en" if the source file is in English)
12
12
  config.source_language = "en"
13
13
 
14
- # Lista delle lingue target (short_name e name)
14
+ # List of target languages (short_name and name)
15
15
  config.target_languages = [
16
16
  # { short_name: 'es', name: 'spagnolo' },
17
17
  # { short_name: 'it', name: 'italiano' },
@@ -21,12 +21,12 @@ BetterTranslate.configure do |config|
21
21
  { short_name: "ru", name: "russian" }
22
22
  ]
23
23
 
24
- # Esclusioni globali (percorsi in dot notation) da escludere per tutte le lingue
24
+ # Global exclusions (paths in dot notation) to exclude for all languages
25
25
  config.global_exclusions = [
26
26
  "key.value"
27
27
  ]
28
28
 
29
- # Esclusioni specifiche per lingua
29
+ # Language-specific exclusions
30
30
  config.exclusions_per_language = {
31
31
  "es" => [],
32
32
  "it" => [],
@@ -36,14 +36,14 @@ BetterTranslate.configure do |config|
36
36
  "ru" => []
37
37
  }
38
38
 
39
- # Percorso del file di input (ad es. en.yml)
39
+ # Input file path (e.g. en.yml)
40
40
  config.input_file = Rails.root.join("config", "locales", "en.yml").to_s
41
41
 
42
- # Cartella di output dove verranno salvati i file tradotti
42
+ # Output folder where translated files will be saved
43
43
  config.output_folder = Rails.root.join("config", "locales", "translated").to_s
44
44
 
45
- # Metodo di traduzione:
46
- # - :override => rigenera tutte le traduzioni
47
- # - :incremental => aggiorna solo le chiavi mancanti (o quelle che hanno subito modifiche)
45
+ # Translation method:
46
+ # - :override => regenerates all translations
47
+ # - :incremental => updates only missing keys (or those that have been modified)
48
48
  config.translation_method = :override
49
49
  end
@@ -3,12 +3,14 @@ require 'rails/generators'
3
3
  module BetterTranslate
4
4
  module Generators
5
5
  class TranslateGenerator < Rails::Generators::Base
6
- desc "Lancia il processo di traduzione configurato in BetterTranslate"
6
+ desc "Starts the translation process configured in BetterTranslate"
7
7
 
8
8
  def run_translation
9
- say_status("Starting", "Esecuzione della traduzione con BetterTranslate...", :green)
9
+ message = "Starting translation with BetterTranslate..."
10
+ BetterTranslate::Utils.logger(message: message)
10
11
  BetterTranslate.magic
11
- say_status("Done", "Traduzione completata.", :green)
12
+ message = "Translation completed."
13
+ BetterTranslate::Utils.logger(message: message)
12
14
  end
13
15
  end
14
16
  end
@@ -0,0 +1,4 @@
1
+ module BetterTranslate
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
metadata CHANGED
@@ -1,57 +1,57 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: better_translate
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
- - alessio_bussolari
7
+ - Alessio Bussolari
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
11
  date: 2025-03-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: yaml
14
+ name: ruby-progressbar
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ">="
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: '1.13'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ">="
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '0'
26
+ version: '1.13'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: httparty
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ">="
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '0'
33
+ version: 0.21.0
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ">="
38
+ - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '0'
40
+ version: 0.21.0
41
41
  - !ruby/object:Gem::Dependency
42
- name: ruby-progressbar
42
+ name: zeitwerk
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '1.11'
47
+ version: '2.6'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '1.11'
54
+ version: '2.6'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: bundler
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -80,22 +80,142 @@ dependencies:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
82
  version: '13.0'
83
- description: "\nBetterTranslate is a gem that allows you to translate YAML files from
84
- a source language into one or more target languages.\nThe gem supports different
85
- translation providers, currently ChatGPT (OpenAI) and Google Gemini, and allows
86
- you to choose\nthe translation mode: \"override\" to regenerate all translations
87
- or \"incremental\" to update only the missing keys.\n\nThe gem's configuration is
88
- centralized via an initializer, where you can define API keys, target languages,
89
- source language,\nkey exclusions, and the output folder. BetterTranslate also integrates
90
- translation progress tracking using a progress bar.\n "
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: vcr
183
+ requirement: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - "~>"
186
+ - !ruby/object:Gem::Version
187
+ version: '6.1'
188
+ type: :development
189
+ prerelease: false
190
+ version_requirements: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - "~>"
193
+ - !ruby/object:Gem::Version
194
+ version: '6.1'
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.
91
208
  email:
92
209
  - alessio.bussolari@pandev.it
93
210
  executables: []
94
211
  extensions: []
95
212
  extra_rdoc_files: []
96
213
  files:
214
+ - CHANGELOG.md
215
+ - CODE_OF_CONDUCT.md
97
216
  - LICENSE.txt
98
217
  - README.md
218
+ - Rakefile
99
219
  - lib/better_translate.rb
100
220
  - lib/better_translate/helper.rb
101
221
  - lib/better_translate/providers/base_provider.rb
@@ -109,6 +229,7 @@ files:
109
229
  - lib/generators/better_translate/install_generator.rb
110
230
  - lib/generators/better_translate/templates/better_translate.rb
111
231
  - lib/generators/better_translate/translate_generator.rb
232
+ - sig/better_translate.rbs
112
233
  homepage: https://github.com/alessiobussolari/better_translate
113
234
  licenses:
114
235
  - MIT
@@ -118,6 +239,9 @@ metadata:
118
239
  homepage_uri: https://github.com/alessiobussolari/better_translate
119
240
  source_code_uri: https://github.com/alessiobussolari/better_translate
120
241
  changelog_uri: https://github.com/alessiobussolari/better_translate/blob/main/CHANGELOG.md
242
+ bug_tracker_uri: https://github.com/alessiobussolari/better_translate/issues
243
+ documentation_uri: https://github.com/alessiobussolari/better_translate
244
+ wiki_uri: https://github.com/alessiobussolari/better_translate/wiki
121
245
  post_install_message:
122
246
  rdoc_options: []
123
247
  require_paths:
@@ -136,6 +260,5 @@ requirements: []
136
260
  rubygems_version: 3.5.11
137
261
  signing_key:
138
262
  specification_version: 4
139
- summary: Gem for translating YAML files into multiple languages using providers (ChatGPT
140
- and Gemini)
263
+ summary: AI-powered YAML translation with ChatGPT and Google Gemini
141
264
  test_files: []