better_translate 0.4.0 → 0.4.2

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: 6941ef1c334c2ddbbb7141c8257f8906957fd0b60eadeee867d9a11b83788ae6
4
- data.tar.gz: 8678aa23790a06d905592efc1571a91408e58a9f1359e70ee90174d5d2ee8975
3
+ metadata.gz: 04ce5f4b9e73378af8eec2409688e26dfef6fb42268b8817800c8c3433877eaf
4
+ data.tar.gz: 0b34308c1ef74dd3ad74b8cbc0b546cf831f2e43c3f3e2628cb636511ab33289
5
5
  SHA512:
6
- metadata.gz: 548433e5223f37320a42c7ba35644b46470dc89baf42581612753d0542d039e2729cad8dd8bf4fbde1cd962ac970f397fdd1a3d98374f83fbc0380805771daae
7
- data.tar.gz: 704eaeb04b49d7f165631443567e89afdff27a30c42893d0d04e424fe340fb113a9a4553ded29c1b4f8e9af36d75eccea0e138d0056c72442f75570340b9fa97
6
+ metadata.gz: e63e571fa9f0a881c25d35d4cb31f5cfaa0fbed2e84d3847e50f9b6f811685b5a13666408b48f0e61666232916c8cbf803dda11a6e51bcca9813f1e97b696435
7
+ data.tar.gz: 94f9664d32402395d9a575c2e1ae8f04b29781421fd7a4b266ae7bf0c7a2c21d345ab73762397d50028d01482255b0a10319c4799c25056256c39e0d43269df0
data/CHANGELOG.md CHANGED
@@ -5,6 +5,32 @@ All notable changes to BetterTranslate will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.4.2] - 2025-03-11
9
+
10
+ ### Added
11
+ - Comprehensive YARD-style documentation across the entire codebase:
12
+ - Added detailed class and method documentation for all core components
13
+ - Added parameter and return type documentation
14
+ - Added usage examples for key classes and methods
15
+ - Improved inline comments for complex logic
16
+
17
+ ### Changed
18
+ - Improved code readability and maintainability through better documentation
19
+ - Enhanced developer experience with clearer API documentation
20
+
21
+ ## [0.4.1] - 2025-03-11
22
+
23
+ ### Fixed
24
+ - Migliorati i test RSpec per garantire maggiore affidabilità:
25
+ - Corretti gli stub per i provider di traduzione
26
+ - Migliorata la gestione delle richieste HTTP nei test
27
+ - Ottimizzati i file YAML temporanei per i test di similarità
28
+ - Risolti problemi di compatibilità con WebMock
29
+
30
+ ### Changed
31
+ - Sostituito l'approccio di stubbing specifico con pattern più flessibili
32
+ - Migliorata la struttura dei test per il SimilarityAnalyzer
33
+
8
34
  ## [0.4.0] - 2025-03-11
9
35
 
10
36
  ### Added
data/README.md CHANGED
@@ -15,6 +15,7 @@ BetterTranslate simplifies the translation of YAML files in your Ruby/Rails appl
15
15
  - 🧪 Comprehensive test coverage
16
16
  - ⚡️ LRU caching for performance
17
17
  - 🔍 Translation similarity analysis
18
+ - 📚 Extensive YARD documentation
18
19
 
19
20
  ## Why BetterTranslate? 🤔
20
21
 
@@ -31,6 +32,7 @@ BetterTranslate simplifies the translation of YAML files in your Ruby/Rails appl
31
32
  - Comprehensive test suite
32
33
  - LRU caching for performance
33
34
  - Progress tracking
35
+ - Detailed YARD documentation
34
36
  - 🔍 **Translation Analysis**:
35
37
  - Similarity detection
36
38
  - Detailed reports
@@ -45,7 +47,7 @@ BetterTranslate simplifies the translation of YAML files in your Ruby/Rails appl
45
47
  Add the gem to your Gemfile:
46
48
 
47
49
  ```ruby
48
- gem 'better_translate', '~> 0.1.0'
50
+ gem 'better_translate', '~> 0.4.2'
49
51
  ```
50
52
 
51
53
  Then run:
@@ -1,4 +1,16 @@
1
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
+ # )
2
14
  class Helper
3
15
  class << self
4
16
  # Translates a given text into multiple target languages.
@@ -1,6 +1,21 @@
1
1
  module BetterTranslate
2
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
3
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
4
19
  def initialize(api_key)
5
20
  @api_key = api_key
6
21
  @last_request_time = Time.now - 1
@@ -8,25 +23,36 @@ module BetterTranslate
8
23
 
9
24
  private
10
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]
11
30
  def rate_limit
12
31
  time_since_last_request = Time.now - @last_request_time
13
32
  sleep(0.5 - time_since_last_request) if time_since_last_request < 0.5
14
33
  @last_request_time = Time.now
15
34
  end
16
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]
17
43
  def validate_input(text, target_lang_code)
18
44
  raise ArgumentError, "Text cannot be empty" if text.nil? || text.strip.empty?
19
45
  raise ArgumentError, "Invalid target language code" unless target_lang_code.match?(/^[a-z]{2}(-[A-Z]{2})?$/)
20
46
  end
21
47
 
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").
26
- # @return [String] testo tradotto.
27
- MAX_RETRIES = 3
28
- RETRY_DELAY = 2 # seconds
29
-
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
30
56
  def translate(text, target_lang_code, target_lang_name)
31
57
  retries = 0
32
58
  begin
@@ -46,12 +72,28 @@ module BetterTranslate
46
72
  end
47
73
  end
48
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
49
82
  def perform_translation(text, target_lang_code, target_lang_name)
50
83
  validate_input(text, target_lang_code)
51
84
  rate_limit
52
85
  translate_text(text, target_lang_code, target_lang_name)
53
86
  end
54
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
55
97
  def translate_text(text, target_lang_code, target_lang_name)
56
98
  raise NotImplementedError, "The provider #{self.class} must implement the translate_text method"
57
99
  end
@@ -1,7 +1,23 @@
1
1
  module BetterTranslate
2
2
  module Providers
3
+ # Translation provider that uses OpenAI's ChatGPT API to perform translations.
4
+ # Implements the BaseProvider interface with ChatGPT-specific translation logic.
5
+ # Uses the gpt-3.5-turbo model with a specialized prompt for accurate translations.
6
+ #
7
+ # @example
8
+ # provider = BetterTranslate::Providers::ChatgptProvider.new(ENV['OPENAI_API_KEY'])
9
+ # translated_text = provider.translate("Hello world", "fr", "French")
3
10
  class ChatgptProvider < BaseProvider
4
- # Uses the OpenAI API to translate the text.
11
+ # Translates text using the OpenAI ChatGPT API.
12
+ # Implements the provider-specific translation logic using OpenAI's ChatGPT API.
13
+ # Sends a carefully crafted prompt to the API to ensure high-quality translations
14
+ # without explanations or additional text.
15
+ #
16
+ # @param text [String] The text to translate
17
+ # @param target_lang_code [String] The target language code (e.g., "fr")
18
+ # @param target_lang_name [String] The target language name (e.g., "French")
19
+ # @return [String] The translated text
20
+ # @raise [StandardError] If the API request fails or returns an error
5
21
  def translate_text(text, target_lang_code, target_lang_name)
6
22
  uri = URI("https://api.openai.com/v1/chat/completions")
7
23
  headers = {
@@ -13,7 +29,7 @@ module BetterTranslate
13
29
  body = {
14
30
  model: "gpt-3.5-turbo",
15
31
  messages: [
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:" },
32
+ { role: "system", content: "You are a professional translator. Translate the following text exactly from #{BetterTranslate.configuration.source_language} to #{target_lang_name}. Provide ONLY the direct translation without any explanations, alternatives, or additional text. Do not include the original text. Do not use markdown formatting. Do not add any prefixes or notes. Just return the plain translated text." },
17
33
  { role: "user", content: "#{text}" }
18
34
  ],
19
35
  temperature: 0.3
@@ -30,8 +46,10 @@ module BetterTranslate
30
46
  translated_text = json.dig("choices", 0, "message", "content")
31
47
  translated_text ? translated_text.strip : text
32
48
  else
33
- raise "Error during translation with ChatGPT: #{response.body}"
49
+ raise "Errore HTTP #{response.code}: #{response.body}"
34
50
  end
51
+ rescue StandardError => e
52
+ raise "Errore durante la traduzione con ChatGPT: #{e.message}"
35
53
  end
36
54
  end
37
55
  end
@@ -1,33 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "net/http"
4
+ require "json"
5
+ require "uri"
6
+
1
7
  module BetterTranslate
2
8
  module Providers
9
+ # Translation provider that uses Google's Gemini API to perform translations.
10
+ # Implements the BaseProvider interface with Gemini-specific translation logic.
11
+ # Uses the gemini-2.0-flash model with a specialized prompt for accurate translations.
12
+ #
13
+ # @example
14
+ # provider = BetterTranslate::Providers::GeminiProvider.new(ENV['GOOGLE_GEMINI_KEY'])
15
+ # translated_text = provider.translate("Hello world", "fr", "French")
3
16
  class GeminiProvider < BaseProvider
4
- # Esempio di implementazione per Google Gemini.
5
- # Nota: L'endpoint e i parametri sono ipotetici e vanno sostituiti con quelli reali secondo la documentazione ufficiale.
17
+ # The base URL for the Gemini API
18
+ # @return [String] The API endpoint URL
19
+ GEMINI_API_URL = "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent"
20
+
21
+ # Implements the provider-specific translation logic using Google's Gemini API.
22
+ # Sends a carefully crafted prompt to the API to ensure high-quality translations
23
+ # without explanations or additional text. Includes minimal text cleaning to handle
24
+ # any formatting issues in the response.
25
+ #
26
+ # @param text [String] The text to translate
27
+ # @param target_lang_code [String] The target language code (e.g., "fr")
28
+ # @param target_lang_name [String] The target language name (e.g., "French")
29
+ # @return [String] The translated text
30
+ # @raise [StandardError] If the API request fails or returns an error
6
31
  def translate_text(text, target_lang_code, target_lang_name)
7
- uri = URI("https://gemini.googleapis.com/v1/translate")
8
- headers = {
9
- "Content-Type" => "application/json",
10
- "Authorization" => "Bearer #{@api_key}"
11
- }
32
+ url = "#{GEMINI_API_URL}?key=#{@api_key}"
33
+ uri = URI(url)
12
34
 
35
+ headers = { "Content-Type" => "application/json" }
13
36
  body = {
14
- input_text: text,
15
- target_language: target_lang_code,
16
- model: "gemini" # oppure altri parametri richiesti dall'API
37
+ contents: [{
38
+ parts: [{
39
+ text: "Translate the following text to #{target_lang_name}. Provide ONLY the direct translation without any explanations, alternatives, or additional text. Do not include the original text. Do not use markdown formatting. Do not add any prefixes or notes. Just return the plain translated text:\n\n#{text}"
40
+ }]
41
+ }]
17
42
  }
18
43
 
19
44
  http = Net::HTTP.new(uri.host, uri.port)
20
45
  http.use_ssl = true
21
- request = Net::HTTP::Post.new(uri.path, headers)
46
+ request = Net::HTTP::Post.new(uri.path + "?" + uri.query, headers)
22
47
  request.body = body.to_json
23
48
 
24
- response = http.request(request)
25
- if response.is_a?(Net::HTTPSuccess)
26
- json = JSON.parse(response.body)
27
- translated_text = json["translatedText"]
28
- translated_text ? translated_text.strip : text
29
- else
30
- raise "Errore durante la traduzione con Gemini: #{response.body}"
49
+ begin
50
+ response = http.request(request)
51
+
52
+ if response.is_a?(Net::HTTPSuccess)
53
+ json = JSON.parse(response.body)
54
+
55
+ if json["candidates"]&.any? && json["candidates"][0]["content"]["parts"]&.any?
56
+ translated_text = json["candidates"][0]["content"]["parts"][0]["text"]
57
+
58
+ # Pulizia minima del testo, dato che il prompt è già specifico
59
+ cleaned_text = translated_text.strip
60
+ .gsub(/[\*\`\n"]/, '') # Rimuovi markdown, newline e virgolette
61
+ .gsub(/\s+/, ' ') # Riduci spazi multipli a uno singolo
62
+
63
+ cleaned_text.empty? ? text : cleaned_text
64
+ else
65
+ raise "Risposta Gemini non valida: #{json}"
66
+ end
67
+ else
68
+ raise "Errore HTTP #{response.code}: #{response.body}"
69
+ end
70
+ rescue => e
71
+ raise "Errore durante la traduzione con Gemini: #{e.message}"
31
72
  end
32
73
  end
33
74
  end
@@ -1,14 +1,33 @@
1
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
+ #
5
+ # @example
6
+ # service = BetterTranslate::Service.new
7
+ # translated_text = service.translate("Hello world", "fr", "French")
2
8
  class Service
9
+ # Maximum number of translations to keep in the LRU cache
3
10
  MAX_CACHE_SIZE = 1000
4
11
 
12
+ # Initializes a new Service instance.
13
+ # Sets up the translation provider based on configuration and initializes the LRU cache.
14
+ #
15
+ # @return [BetterTranslate::Service] A new Service instance
5
16
  def initialize
6
17
  @provider_name = BetterTranslate.configuration.provider
7
18
  @translation_cache = {}
8
19
  @cache_order = []
9
20
  end
10
21
 
11
- # Method to translate a text using the selected provider.
22
+ # Translates text using the configured provider with caching support.
23
+ # First checks if the translation is already in the cache. If not, it uses the
24
+ # provider to translate the text and then caches the result for future use.
25
+ # Also tracks metrics about the translation request duration.
26
+ #
27
+ # @param text [String] The text to translate
28
+ # @param target_lang_code [String] The target language code (e.g., 'fr', 'es')
29
+ # @param target_lang_name [String] The target language name (e.g., 'French', 'Spanish')
30
+ # @return [String] The translated text
12
31
  def translate(text, target_lang_code, target_lang_name)
13
32
  cache_key = "#{text}:#{target_lang_code}"
14
33
 
@@ -32,6 +51,11 @@ module BetterTranslate
32
51
 
33
52
  private
34
53
 
54
+ # Retrieves a translation from the LRU cache if it exists.
55
+ # Updates the cache order to mark this key as most recently used.
56
+ #
57
+ # @param key [String] The cache key in the format "text:target_lang_code"
58
+ # @return [String, nil] The cached translation or nil if not found
35
59
  def cache_get(key)
36
60
  if @translation_cache.key?(key)
37
61
  # Aggiorna l'ordine LRU
@@ -41,6 +65,12 @@ module BetterTranslate
41
65
  end
42
66
  end
43
67
 
68
+ # Stores a translation in the LRU cache.
69
+ # If the cache is full, removes the least recently used item before adding the new one.
70
+ #
71
+ # @param key [String] The cache key in the format "text:target_lang_code"
72
+ # @param value [String] The translated text to cache
73
+ # @return [String] The value that was cached
44
74
  def cache_set(key, value)
45
75
  if @translation_cache.size >= MAX_CACHE_SIZE
46
76
  # Rimuovi l'elemento meno recentemente usato
@@ -55,6 +85,12 @@ module BetterTranslate
55
85
 
56
86
 
57
87
 
88
+ # Creates or returns a cached instance of the translation provider.
89
+ # The provider is determined by the configuration and instantiated with the appropriate API key.
90
+ # Supports ChatGPT and Gemini providers.
91
+ #
92
+ # @return [BetterTranslate::Providers::BaseProvider] An instance of the configured translation provider
93
+ # @raise [RuntimeError] If the configured provider is not supported
58
94
  def provider_instance
59
95
  @provider_instance ||= case @provider_name
60
96
  when :chatgpt
@@ -6,15 +6,37 @@ require "json"
6
6
  require "time"
7
7
 
8
8
  module BetterTranslate
9
+ # Analyzes translation YAML files to find similar content across keys.
10
+ # Uses the Levenshtein distance algorithm to identify strings that are similar
11
+ # but not identical, which could indicate redundant translations.
12
+ #
13
+ # @example
14
+ # analyzer = BetterTranslate::SimilarityAnalyzer.new(["config/locales/en.yml", "config/locales/fr.yml"])
15
+ # analyzer.analyze
9
16
  class SimilarityAnalyzer
17
+ # Default threshold for considering two strings similar (75%)
18
+ # @return [Float] The similarity threshold as a decimal between 0 and 1
10
19
  SIMILARITY_THRESHOLD = 0.75 # Abbassiamo la soglia per trovare più similarità
20
+
21
+ # Default filename for the detailed JSON report
22
+ # @return [String] The filename for the JSON report
11
23
  REPORT_FILE = "translation_similarities.json"
12
24
 
25
+ # Initializes a new SimilarityAnalyzer with the specified YAML files.
26
+ #
27
+ # @param yaml_files [Array<String>] An array of paths to YAML translation files
28
+ # @return [SimilarityAnalyzer] A new instance of the analyzer
13
29
  def initialize(yaml_files)
14
30
  @yaml_files = yaml_files
15
31
  @similarities = {}
16
32
  end
17
33
 
34
+ # Performs the complete analysis process:
35
+ # 1. Loads all translation files
36
+ # 2. Finds similarities between translations
37
+ # 3. Generates JSON and text reports
38
+ #
39
+ # @return [void]
18
40
  def analyze
19
41
  translations_by_language = load_translations
20
42
  find_similarities(translations_by_language)
@@ -23,6 +45,10 @@ module BetterTranslate
23
45
 
24
46
  private
25
47
 
48
+ # Loads all YAML translation files specified during initialization.
49
+ # Parses each file and organizes translations by language code.
50
+ #
51
+ # @return [Hash] A hash mapping language codes to their translation data structures
26
52
  def load_translations
27
53
  translations = {}
28
54
  puts "Loading YAML files..."
@@ -36,6 +62,12 @@ module BetterTranslate
36
62
  translations
37
63
  end
38
64
 
65
+ # Finds similar translations within each language file.
66
+ # For each language, flattens the translation structure and compares each pair of strings
67
+ # to identify similarities based on the Levenshtein distance algorithm.
68
+ #
69
+ # @param translations_by_language [Hash] A hash mapping language codes to their translation data
70
+ # @return [void]
39
71
  def find_similarities(translations_by_language)
40
72
  translations_by_language.each do |lang, translations|
41
73
  puts "\nAnalyzing #{lang} translations..."
@@ -62,6 +94,13 @@ module BetterTranslate
62
94
  end
63
95
  end
64
96
 
97
+ # Flattens a nested translation hash into a single-level hash with dot-notation keys.
98
+ # For example, {"en" => {"hello" => "world"}} becomes {"en.hello" => "world"}.
99
+ #
100
+ # @param hash [Hash] The nested hash to flatten
101
+ # @param prefix [String] The current key prefix (used recursively)
102
+ # @param result [Hash] The accumulator for flattened key-value pairs
103
+ # @return [Hash] A flattened hash with dot-notation keys
65
104
  def flatten_translations(hash, prefix = "", result = {})
66
105
  hash.each do |key, value|
67
106
  current_key = prefix.empty? ? key.to_s : "#{prefix}.#{key}"
@@ -76,6 +115,13 @@ module BetterTranslate
76
115
  result
77
116
  end
78
117
 
118
+ # Calculates the similarity between two strings using normalized Levenshtein distance.
119
+ # Returns a value between 0 (completely different) and 1 (identical).
120
+ # The normalization accounts for the length of the strings.
121
+ #
122
+ # @param str1 [String] The first string to compare
123
+ # @param str2 [String] The second string to compare
124
+ # @return [Float] A similarity score between 0 and 1
79
125
  def calculate_similarity(str1, str2)
80
126
  # Implementazione della distanza di Levenshtein normalizzata
81
127
  matrix = Array.new(str1.length + 1) { Array.new(str2.length + 1) }
@@ -98,6 +144,16 @@ module BetterTranslate
98
144
  1 - (matrix[str1.length][str2.length].to_f / max_length)
99
145
  end
100
146
 
147
+ # Records a similarity finding in the internal data structure.
148
+ # Organizes findings by language and stores all relevant information about the similar strings.
149
+ #
150
+ # @param lang [String] The language code
151
+ # @param key1 [String] The first translation key
152
+ # @param key2 [String] The second translation key
153
+ # @param value1 [String] The first translation value
154
+ # @param value2 [String] The second translation value
155
+ # @param similarity [Float] The calculated similarity score
156
+ # @return [void]
101
157
  def record_similarity(lang, key1, key2, value1, value2, similarity)
102
158
  @similarities[lang] ||= []
103
159
  @similarities[lang] << {
@@ -109,6 +165,11 @@ module BetterTranslate
109
165
  }
110
166
  end
111
167
 
168
+ # Generates both JSON and human-readable reports of the similarity findings.
169
+ # The JSON report contains detailed information about each similarity found.
170
+ # The text summary provides a more readable format for quick review.
171
+ #
172
+ # @return [void]
112
173
  def generate_report
113
174
  puts "\nGenerating reports..."
114
175
  report = {
@@ -125,6 +186,12 @@ module BetterTranslate
125
186
  puts " - Generated translation_similarities_summary.txt"
126
187
  end
127
188
 
189
+ # Generates a human-readable summary of the similarity findings.
190
+ # Creates a formatted text report organized by language, showing each pair of similar strings
191
+ # with their keys, values, and similarity percentage.
192
+ #
193
+ # @param report [Hash] The complete similarity report data
194
+ # @return [String] A formatted text summary
128
195
  def generate_summary(report)
129
196
  summary = ["Translation Similarities Report", "=" * 30, ""]
130
197
 
@@ -1,21 +1,33 @@
1
1
  module BetterTranslate
2
2
  class Translator
3
3
  class << self
4
+ # Main method that orchestrates the translation process.
5
+ # Reads the source file, applies exclusions, and translates the content
6
+ # to all configured target languages sequentially.
7
+ #
8
+ # @return [void]
4
9
  def work
5
- message = "Starting file translation..."
10
+ message = "\n[BetterTranslate] Reading source file: #{BetterTranslate.configuration.input_file}\n"
6
11
  BetterTranslate::Utils.logger(message: message)
7
12
 
8
13
  translations = read_yml_source
14
+ message = "[BetterTranslate] Source file loaded successfully."
15
+ BetterTranslate::Utils.logger(message: message)
9
16
 
10
17
  # Removes the keys to exclude (global_exclusions) from the read structure
18
+ message = "[BetterTranslate] Applying global exclusions..."
19
+ BetterTranslate::Utils.logger(message: message)
11
20
  global_filtered_translations = remove_exclusions(
12
21
  translations, BetterTranslate.configuration.global_exclusions
13
22
  )
23
+ message = "[BetterTranslate] Global exclusions applied."
24
+ BetterTranslate::Utils.logger(message: message)
14
25
 
15
26
  start_time = Time.now
16
- threads = BetterTranslate.configuration.target_languages.map do |target_lang|
17
- Thread.new do
18
-
27
+ results = []
28
+
29
+ # Elabora ogni lingua target in sequenza invece di utilizzare thread
30
+ BetterTranslate.configuration.target_languages.each do |target_lang|
19
31
  # Phase 2: Apply the target language specific filter
20
32
  lang_exclusions = BetterTranslate.configuration.exclusions_per_language[target_lang[:short_name]] || []
21
33
  filtered_translations = remove_exclusions(
@@ -24,26 +36,36 @@ module BetterTranslate
24
36
 
25
37
  message = "Starting translation from #{BetterTranslate.configuration.source_language} to #{target_lang[:short_name]}"
26
38
  BetterTranslate::Utils.logger(message: message)
39
+ message = "\n[BetterTranslate] Starting translation to #{target_lang[:name]} (#{target_lang[:short_name]})..."
40
+ BetterTranslate::Utils.logger(message: message)
41
+
27
42
  service = BetterTranslate::Service.new
28
43
  translated_data = translate_with_progress(filtered_translations, service, target_lang[:short_name], target_lang[:name])
44
+
45
+ message = "[BetterTranslate] Writing translations for #{target_lang[:short_name]}..."
46
+ BetterTranslate::Utils.logger(message: message)
29
47
  BetterTranslate::Writer.write_translations(translated_data, target_lang[:short_name])
30
- end_time = Time.now
31
- duration = end_time - start_time
48
+
49
+ lang_end_time = Time.now
50
+ duration = lang_end_time - start_time
32
51
  BetterTranslate::Utils.track_metric("translation_duration", duration)
33
52
 
34
53
  message = "Translation completed from #{BetterTranslate.configuration.source_language} to #{target_lang[:short_name]} in #{duration.round(2)} seconds"
35
54
  BetterTranslate::Utils.logger(message: message)
36
- end
55
+ message = "[BetterTranslate] Completed translation to #{target_lang[:name]} in #{duration.round(2)} seconds."
56
+ BetterTranslate::Utils.logger(message: message)
57
+
58
+ results << translated_data
37
59
  end
38
60
  end
39
61
 
40
62
  private
41
63
 
42
- # Reads the YAML file based on the provided path.
64
+ # Reads the YAML file specified in the configuration.
65
+ # The file path is taken from BetterTranslate.configuration.input_file.
43
66
  #
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
67
+ # @return [Hash] The parsed YAML data structure containing the translations
68
+ # @raise [StandardError] If the input file does not exist or cannot be parsed
47
69
  def read_yml_source
48
70
  file_path = BetterTranslate.configuration.input_file
49
71
  unless File.exist?(file_path)
@@ -61,11 +83,14 @@ module BetterTranslate
61
83
  # and global_exclusions = ["sample.excluded"],
62
84
  # the result will be:
63
85
  # { "en" => { "sample" => { "valid" => "valid" } } }
86
+ # Removes specified keys from the translation data structure.
87
+ # Recursively traverses the data structure and excludes any keys that match the exclusion list.
88
+ # Keys can be excluded at any nesting level using dot notation paths.
64
89
  #
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.
90
+ # @param data [Hash, Array, Object] The data structure to filter
91
+ # @param exclusion_list [Array<String>] List of dot-separated key paths to exclude
92
+ # @param current_path [Array] The current path in the traversal (used recursively, default: [])
93
+ # @return [Hash, Array, Object] The filtered data structure with excluded keys removed
69
94
  def remove_exclusions(data, exclusion_list, current_path = [])
70
95
  if data.is_a?(Hash)
71
96
  data.each_with_object({}) do |(key, value), result|
@@ -93,12 +118,16 @@ module BetterTranslate
93
118
 
94
119
  # Recursive method that traverses the structure, translating each string and updating progress.
95
120
  #
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.
121
+ # Recursively translates a data structure by traversing it deeply.
122
+ # This method is used for smaller datasets (less than 50 strings) and translates each string individually.
123
+ # It maintains the original structure of the data while replacing string values with their translations.
124
+ #
125
+ # @param data [Hash, Array, String] The data structure to translate
126
+ # @param service [BetterTranslate::Service] The service instance to use for translation
127
+ # @param target_lang_code [String] The target language code (e.g., 'fr', 'es')
128
+ # @param target_lang_name [String] The target language name (e.g., 'French', 'Spanish')
129
+ # @param progress [ProgressBar] A progress bar instance to track translation progress
130
+ # @return [Hash, Array, String] The translated data structure with the same structure as the input
102
131
  def deep_translate_with_progress(data, service, target_lang_code, target_lang_name, progress)
103
132
  if data.is_a?(Hash)
104
133
  data.each_with_object({}) do |(key, value), result|
@@ -116,18 +145,27 @@ module BetterTranslate
116
145
  end
117
146
  end
118
147
 
119
- # Main method to translate the entire data structure, with progress monitoring.
148
+ # Translates the entire data structure with progress monitoring.
149
+ # Automatically selects between batch and deep translation methods based on the number of strings.
150
+ # For datasets with more than 50 strings, batch processing is used for better performance.
120
151
  #
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
152
+ # @param data [Hash, Array, String] The data structure to translate
153
+ # @param service [BetterTranslate::Service] The service instance to use for translation
154
+ # @param target_lang_code [String] The target language code (e.g., 'fr', 'es')
155
+ # @param target_lang_name [String] The target language name (e.g., 'French', 'Spanish')
156
+ # @return [Hash, Array, String] The translated data structure with the same structure as the input
126
157
  def translate_with_progress(data, service, target_lang_code, target_lang_name)
127
158
  total = count_strings(data)
159
+ message = "[BetterTranslate] Found #{total} strings to translate to #{target_lang_name}"
160
+ BetterTranslate::Utils.logger(message: message)
161
+
162
+ # Creiamo la barra di progresso ma aggiungiamo anche output visibile
128
163
  progress = ProgressBar.create(total: total, format: '%a %B %p%% %t')
129
164
 
130
165
  start_time = Time.now
166
+ message = "[BetterTranslate] Using #{total > 50 ? 'batch' : 'deep'} translation method"
167
+ BetterTranslate::Utils.logger(message: message)
168
+
131
169
  result = if total > 50 # Usa il batch processing per dataset grandi
132
170
  batch_translate_with_progress(data, service, target_lang_code, target_lang_name, progress)
133
171
  else
@@ -135,6 +173,9 @@ module BetterTranslate
135
173
  end
136
174
 
137
175
  duration = Time.now - start_time
176
+ message = "[BetterTranslate] Translation processing completed in #{duration.round(2)} seconds"
177
+ BetterTranslate::Utils.logger(message: message)
178
+
138
179
  BetterTranslate::Utils.track_metric("translation_method_duration", {
139
180
  method: total > 50 ? 'batch' : 'deep',
140
181
  duration: duration,
@@ -144,6 +185,16 @@ module BetterTranslate
144
185
  result
145
186
  end
146
187
 
188
+ # Translates data in batches for improved performance with larger datasets.
189
+ # This method first extracts all translatable strings, processes them in batches of 10,
190
+ # and then reinserts the translations back into the original structure.
191
+ #
192
+ # @param data [Hash, Array, String] The data structure to translate
193
+ # @param service [BetterTranslate::Service] The service instance to use for translation
194
+ # @param target_lang_code [String] The target language code (e.g., 'fr', 'es')
195
+ # @param target_lang_name [String] The target language name (e.g., 'French', 'Spanish')
196
+ # @param progress [ProgressBar] A progress bar instance to track translation progress
197
+ # @return [Hash, Array, String] The translated data structure with the same structure as the input
147
198
  def batch_translate_with_progress(data, service, target_lang_code, target_lang_name, progress)
148
199
  texts = extract_translatable_texts(data)
149
200
  translations = {}
@@ -170,6 +221,12 @@ module BetterTranslate
170
221
  replace_translations(data, translations)
171
222
  end
172
223
 
224
+ # Extracts all unique translatable strings from a data structure.
225
+ # This is used by the batch translation method to collect all strings for efficient processing.
226
+ # Only non-empty strings are included in the result.
227
+ #
228
+ # @param data [Hash, Array, String] The data structure to extract strings from
229
+ # @return [Array<String>] An array of unique strings found in the data structure
173
230
  def extract_translatable_texts(data)
174
231
  texts = Set.new
175
232
  traverse_structure(data) do |value|
@@ -178,6 +235,13 @@ module BetterTranslate
178
235
  texts.to_a
179
236
  end
180
237
 
238
+ # Replaces strings in the original data structure with their translations.
239
+ # Used by the batch translation method to reinsert translated strings into the original structure.
240
+ # Only non-empty strings that have translations in the provided hash are replaced.
241
+ #
242
+ # @param data [Hash, Array, String] The original data structure
243
+ # @param translations [Hash] A hash mapping original strings to their translations
244
+ # @return [Hash, Array, String] The data structure with strings replaced by translations
181
245
  def replace_translations(data, translations)
182
246
  traverse_structure(data) do |value|
183
247
  if value.is_a?(String) && !value.strip.empty? && translations.key?(value)
@@ -188,6 +252,13 @@ module BetterTranslate
188
252
  end
189
253
  end
190
254
 
255
+ # Traverses a nested data structure and applies a block to each element.
256
+ # This is a utility method used by extract_translatable_texts and replace_translations.
257
+ # Handles Hash, Array, and scalar values recursively.
258
+ #
259
+ # @param data [Hash, Array, Object] The data structure to traverse
260
+ # @yield [Object] Yields each value in the data structure to the block
261
+ # @return [Hash, Array, Object] The transformed data structure after applying the block
191
262
  def traverse_structure(data, &block)
192
263
  case data
193
264
  when Hash
@@ -199,7 +270,12 @@ module BetterTranslate
199
270
  end
200
271
  end
201
272
 
202
- # Recursively counts the number of translatable strings in the data structure.
273
+ # Counts the number of translatable strings in a data structure.
274
+ # Used to determine the total number of strings for progress tracking and method selection.
275
+ # Recursively traverses Hash and Array structures, counting each String as 1.
276
+ #
277
+ # @param data [Hash, Array, String, Object] The data structure to count strings in
278
+ # @return [Integer] The total number of strings found in the data structure
203
279
  def count_strings(data)
204
280
  if data.is_a?(Hash)
205
281
  data.values.sum { |v| count_strings(v) }
@@ -1,25 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # lib/better_seeder/utils.rb
4
- #
5
- # = BetterSeeder::Utils
6
- #
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
-
11
3
  module BetterTranslate
4
+ # Utility class providing helper methods for logging and metrics tracking.
5
+ # Used throughout the BetterTranslate gem to standardize logging and performance monitoring.
12
6
  class Utils
13
7
  class << self
14
- ##
15
- # Logs a message using the Rails logger if available, otherwise prints it to standard output.
16
- #
17
- # ==== Parametri
18
- # * +message+ - The message to log (can be a string or nil).
19
- #
20
- # ==== Ritorno
21
- # Does not return a significant value.
8
+
9
+ # Logs a message using the Rails logger if available, otherwise prints to standard output.
10
+ # Provides a consistent logging interface across different environments.
22
11
  #
12
+ # @param message [String, nil] The message to log
13
+ # @return [void]
23
14
  def logger(message: nil)
24
15
  if defined?(Rails) && Rails.respond_to?(:logger) && Rails.logger
25
16
  Rails.logger.info message
@@ -28,6 +19,13 @@ module BetterTranslate
28
19
  end
29
20
  end
30
21
 
22
+ # Tracks a metric with the given name and value.
23
+ # Stores metrics in memory with timestamps for performance analysis.
24
+ # Automatically limits each metric type to the most recent 1000 entries.
25
+ #
26
+ # @param name [Symbol, String] The name of the metric to track
27
+ # @param value [Numeric] The value to record for this metric
28
+ # @return [void]
31
29
  def track_metric(name, value)
32
30
  @metrics ||= {}
33
31
  @metrics[name] ||= []
@@ -37,10 +35,18 @@ module BetterTranslate
37
35
  @metrics[name] = @metrics[name].last(1000) if @metrics[name].size > 1000
38
36
  end
39
37
 
38
+ # Retrieves all tracked metrics.
39
+ # Returns a hash where keys are metric names and values are arrays of recorded values with timestamps.
40
+ #
41
+ # @return [Hash] The collected metrics or an empty hash if no metrics have been tracked
40
42
  def get_metrics
41
43
  @metrics || {}
42
44
  end
43
45
 
46
+ # Clears all tracked metrics.
47
+ # Useful for resetting metrics between translation jobs or testing.
48
+ #
49
+ # @return [Hash] An empty hash representing the cleared metrics
44
50
  def clear_metrics
45
51
  @metrics = {}
46
52
  end
@@ -1,5 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module BetterTranslate
4
- VERSION = "0.4.0"
4
+ # Current version of the BetterTranslate gem.
5
+ #
6
+ # The versioning follows Semantic Versioning 2.0.0 (https://semver.org/):
7
+ # - MAJOR version for incompatible API changes
8
+ # - MINOR version for backwards-compatible functionality additions
9
+ # - PATCH version for backwards-compatible bug fixes
10
+ #
11
+ # @return [String] The current version in the format "MAJOR.MINOR.PATCH"
12
+ VERSION = "0.4.2"
5
13
  end
@@ -1,4 +1,8 @@
1
1
  module BetterTranslate
2
+ # Responsible for writing translated content to YAML files.
3
+ # Supports both incremental and override translation methods.
4
+ # Incremental mode preserves existing translations and adds new ones,
5
+ # while override mode completely replaces the target file.
2
6
  class Writer
3
7
  class << self
4
8
  # Writes the translation file for a target language.
@@ -35,14 +39,19 @@ module BetterTranslate
35
39
 
36
40
  private
37
41
 
38
- # Metodo di deep merge: unisce in modo ricorsivo i due hash.
39
- # Se una chiave esiste in entrambi gli hash e i valori sono hash, li unisce ricorsivamente.
40
- # Otherwise, if the key already exists in the existing hash, it keeps it and doesn't overwrite it.
41
- # Se la chiave non esiste, la aggiunge.
42
+ # Recursively merges two hashes while preserving existing values.
43
+ # If a key exists in both hashes and the values are hashes, they are merged recursively.
44
+ # If a key exists in both hashes but the values are not hashes, the existing value is preserved.
45
+ # If a key only exists in the new hash, it is added to the merged result.
42
46
  #
43
- # @param existing [Hash] existing hash (current file)
44
- # @param new_data [Hash] new hash with translations to merge
45
- # @return [Hash] merged hash
47
+ # @param existing [Hash] The existing hash (current file content)
48
+ # @param new_data [Hash] The new hash with translations to merge
49
+ # @return [Hash] The merged hash with preserved existing values
50
+ # @example
51
+ # existing = { "en" => { "hello" => "Hello", "nested" => { "key" => "Value" } } }
52
+ # new_data = { "en" => { "hello" => "New Hello", "nested" => { "key2" => "Value2" } } }
53
+ # deep_merge(existing, new_data)
54
+ # # => { "en" => { "hello" => "Hello", "nested" => { "key" => "Value", "key2" => "Value2" } } }
46
55
  def deep_merge(existing, new_data)
47
56
  merged = existing.dup
48
57
  new_data.each do |key, value|
@@ -15,17 +15,41 @@ require 'better_translate/providers/gemini_provider'
15
15
 
16
16
  require 'ostruct'
17
17
 
18
+ # Main module for the BetterTranslate gem.
19
+ # Provides functionality for translating YAML files using various AI providers.
20
+ #
21
+ # @example Basic configuration and usage
22
+ # BetterTranslate.configure do |config|
23
+ # config.provider = :chatgpt
24
+ # config.openai_key = ENV['OPENAI_API_KEY']
25
+ # config.input_file = 'config/locales/en.yml'
26
+ # config.target_languages = [{short_name: 'fr', name: 'French'}]
27
+ # end
28
+ #
29
+ # BetterTranslate.magic # Start the translation process
18
30
  module BetterTranslate
19
31
  class << self
32
+ # Configuration object for the gem
33
+ # @return [OpenStruct] The configuration object
20
34
  attr_accessor :configuration
21
35
 
22
- # Method to configure the gem
36
+ # Configures the gem with the provided block.
37
+ # Sets up the configuration object and yields it to the block.
38
+ #
39
+ # @yield [configuration] Yields the configuration object to the block
40
+ # @yieldparam configuration [OpenStruct] The configuration object
41
+ # @return [OpenStruct] The updated configuration object
23
42
  def configure
24
43
  self.configuration ||= OpenStruct.new
25
44
  yield(configuration) if block_given?
26
45
  end
27
46
 
28
- # Install method to generate the configuration file (initializer)
47
+ # Installs the gem configuration file (initializer) in a Rails application.
48
+ # Copies the template initializer to the Rails config/initializers directory.
49
+ # Only works in a Rails environment.
50
+ #
51
+ # @return [void]
52
+ # @note This method will log an error if not called from a Rails application
29
53
  def install
30
54
  unless defined?(Rails) && Rails.respond_to?(:root)
31
55
  message = "The install method is only available in a Rails application."
@@ -48,13 +72,27 @@ module BetterTranslate
48
72
  end
49
73
  end
50
74
 
75
+ # Starts the translation process using the configured settings.
76
+ # This is the main entry point for the translation functionality.
77
+ # Logs the start and completion of the translation process.
78
+ #
79
+ # @return [void]
80
+ # @example
81
+ # BetterTranslate.magic
51
82
  def magic
52
83
  message = "Magic method invoked: Translation will begin..."
53
84
  BetterTranslate::Utils.logger(message: message)
85
+ # Utilizziamo il logger per tutti i messaggi
86
+ message = "\n[BetterTranslate] Starting translation process...\n"
87
+ BetterTranslate::Utils.logger(message: message)
54
88
 
55
89
  BetterTranslate::Translator.work
90
+
56
91
  message = "Magic method invoked: Translation completed successfully!"
57
92
  BetterTranslate::Utils.logger(message: message)
93
+ # Utilizziamo il logger per tutti i messaggi
94
+ message = "\n[BetterTranslate] Translation completed successfully!\n"
95
+ BetterTranslate::Utils.logger(message: message)
58
96
  end
59
97
 
60
98
  end
@@ -4,9 +4,23 @@ require "rails/generators"
4
4
 
5
5
  module BetterTranslate
6
6
  module Generators
7
+ # Rails generator for analyzing translation files to find similar content.
8
+ # Uses the SimilarityAnalyzer to identify potentially redundant translations
9
+ # across locale files and generates both JSON and human-readable reports.
10
+ #
11
+ # @example
12
+ # rails generate better_translate:analyze
7
13
  class AnalyzeGenerator < Rails::Generators::Base
8
14
  desc "Analyze translation files for similarities"
9
15
 
16
+ # Main generator method that analyzes translation files for similarities.
17
+ # Finds all YAML files in the config/locales directory, runs the analysis,
18
+ # and displays a summary of the results.
19
+ #
20
+ # The analysis uses the Levenshtein distance algorithm to identify strings
21
+ # that are similar but not identical, which could indicate redundant translations.
22
+ #
23
+ # @return [void]
10
24
  def analyze_translations
11
25
  say "Starting translation similarity analysis...", :blue
12
26
 
@@ -1,16 +1,83 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rails/generators'
2
4
 
3
5
  module BetterTranslate
4
6
  module Generators
7
+ # Rails generator for executing translations with BetterTranslate.
8
+ # This generator validates the configuration and starts the translation process.
9
+ #
10
+ # @example
11
+ # rails generate better_translate:translate
5
12
  class TranslateGenerator < Rails::Generators::Base
6
13
  desc "Starts the translation process configured in BetterTranslate"
7
14
 
8
- def run_translation
9
- message = "Starting translation with BetterTranslate..."
10
- BetterTranslate::Utils.logger(message: message)
11
- BetterTranslate.magic
12
- message = "Translation completed."
13
- BetterTranslate::Utils.logger(message: message)
15
+ # Main generator method that initiates the translation process.
16
+ # Validates the configuration before starting and provides status messages.
17
+ #
18
+ # @return [void]
19
+ def translate
20
+ say "Starting translation with BetterTranslate...", :blue
21
+
22
+ # Verifica che la configurazione sia valida
23
+ if validate_configuration
24
+ say "Configuration validated. Starting translation...", :green
25
+ BetterTranslate.magic
26
+ say "Translation completed.", :green
27
+ else
28
+ say "Translation aborted due to configuration issues.", :red
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ # Validates the BetterTranslate configuration.
35
+ # Checks for required settings and reports any issues found.
36
+ #
37
+ # @return [Boolean] true if the configuration is valid, false otherwise
38
+ def validate_configuration
39
+ valid = true
40
+
41
+ # Verifica che il file di input esista
42
+ unless BetterTranslate.configuration.respond_to?(:input_file)
43
+ say "Error: input_file not configured. Please check your initializer.", :red
44
+ return false
45
+ end
46
+
47
+ input_file = BetterTranslate.configuration.input_file
48
+ unless File.exist?(input_file)
49
+ say "Error: Input file not found: #{input_file}", :red
50
+ valid = false
51
+ end
52
+
53
+ # Verifica che ci siano lingue target attive
54
+ if !BetterTranslate.configuration.respond_to?(:target_languages) ||
55
+ BetterTranslate.configuration.target_languages.empty?
56
+ say "Error: No target languages configured. Please uncomment or add target languages in your initializer.", :red
57
+ valid = false
58
+ end
59
+
60
+ # Verifica che il provider sia configurato
61
+ if !BetterTranslate.configuration.respond_to?(:provider)
62
+ say "Error: No provider configured. Please set config.provider in your initializer.", :red
63
+ valid = false
64
+ end
65
+
66
+ # Verifica che la chiave API sia configurata per il provider selezionato
67
+ if BetterTranslate.configuration.respond_to?(:provider)
68
+ provider = BetterTranslate.configuration.provider
69
+ if provider == :chatgpt && (!BetterTranslate.configuration.respond_to?(:openai_key) ||
70
+ BetterTranslate.configuration.openai_key == "YOUR_OPENAI_API_KEY")
71
+ say "Error: OpenAI API key not configured. Please set config.openai_key in your initializer.", :red
72
+ valid = false
73
+ elsif provider == :gemini && (!BetterTranslate.configuration.respond_to?(:google_gemini_key) ||
74
+ BetterTranslate.configuration.google_gemini_key == "YOUR_GOOGLE_GEMINI_KEY")
75
+ say "Error: Gemini API key not configured. Please set config.google_gemini_key in your initializer.", :red
76
+ valid = false
77
+ end
78
+ end
79
+
80
+ valid
14
81
  end
15
82
  end
16
83
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: better_translate
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.4.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alessio Bussolari
@@ -179,19 +179,19 @@ dependencies:
179
179
  - !ruby/object:Gem::Version
180
180
  version: '3.19'
181
181
  - !ruby/object:Gem::Dependency
182
- name: vcr
182
+ name: dotenv
183
183
  requirement: !ruby/object:Gem::Requirement
184
184
  requirements:
185
185
  - - "~>"
186
186
  - !ruby/object:Gem::Version
187
- version: '6.1'
187
+ version: '2.8'
188
188
  type: :development
189
189
  prerelease: false
190
190
  version_requirements: !ruby/object:Gem::Requirement
191
191
  requirements:
192
192
  - - "~>"
193
193
  - !ruby/object:Gem::Version
194
- version: '6.1'
194
+ version: '2.8'
195
195
  description: |
196
196
  BetterTranslate is a powerful Ruby gem for translating YAML files using AI providers.
197
197