better_translate 0.4.1 โ 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 +4 -4
- data/CHANGELOG.md +13 -0
- data/README.md +3 -1
- data/lib/better_translate/helper.rb +12 -0
- data/lib/better_translate/providers/base_provider.rb +50 -8
- data/lib/better_translate/providers/chatgpt_provider.rb +17 -1
- data/lib/better_translate/providers/gemini_provider.rb +19 -0
- data/lib/better_translate/service.rb +38 -2
- data/lib/better_translate/similarity_analyzer.rb +67 -0
- data/lib/better_translate/translator.rb +104 -28
- data/lib/better_translate/utils.rb +22 -16
- data/lib/better_translate/version.rb +9 -1
- data/lib/better_translate/writer.rb +16 -7
- data/lib/better_translate.rb +40 -2
- data/lib/generators/better_translate/analyze_generator.rb +14 -0
- data/lib/generators/better_translate/translate_generator.rb +73 -6
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 04ce5f4b9e73378af8eec2409688e26dfef6fb42268b8817800c8c3433877eaf
|
4
|
+
data.tar.gz: 0b34308c1ef74dd3ad74b8cbc0b546cf831f2e43c3f3e2628cb636511ab33289
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e63e571fa9f0a881c25d35d4cb31f5cfaa0fbed2e84d3847e50f9b6f811685b5a13666408b48f0e61666232916c8cbf803dda11a6e51bcca9813f1e97b696435
|
7
|
+
data.tar.gz: 94f9664d32402395d9a575c2e1ae8f04b29781421fd7a4b266ae7bf0c7a2c21d345ab73762397d50028d01482255b0a10319c4799c25056256c39e0d43269df0
|
data/CHANGELOG.md
CHANGED
@@ -5,6 +5,19 @@ 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
|
+
|
8
21
|
## [0.4.1] - 2025-03-11
|
9
22
|
|
10
23
|
### Fixed
|
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.
|
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
|
-
#
|
23
|
-
#
|
24
|
-
#
|
25
|
-
# @param
|
26
|
-
# @
|
27
|
-
|
28
|
-
|
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
|
-
#
|
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 = {
|
@@ -6,9 +6,28 @@ require "uri"
|
|
6
6
|
|
7
7
|
module BetterTranslate
|
8
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")
|
9
16
|
class GeminiProvider < BaseProvider
|
17
|
+
# The base URL for the Gemini API
|
18
|
+
# @return [String] The API endpoint URL
|
10
19
|
GEMINI_API_URL = "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent"
|
11
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
|
12
31
|
def translate_text(text, target_lang_code, target_lang_name)
|
13
32
|
url = "#{GEMINI_API_URL}?key=#{@api_key}"
|
14
33
|
uri = URI(url)
|
@@ -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
|
-
#
|
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,12 +85,18 @@ 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
|
61
97
|
Providers::ChatgptProvider.new(BetterTranslate.configuration.openai_key)
|
62
98
|
when :gemini
|
63
|
-
Providers::GeminiProvider.new(BetterTranslate.configuration.
|
99
|
+
Providers::GeminiProvider.new(BetterTranslate.configuration.google_gemini_key)
|
64
100
|
else
|
65
101
|
raise "Provider non supportato: #{@provider_name}"
|
66
102
|
end
|
@@ -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 = "
|
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
|
-
|
17
|
-
|
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
|
-
|
31
|
-
|
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
|
-
|
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
|
64
|
+
# Reads the YAML file specified in the configuration.
|
65
|
+
# The file path is taken from BetterTranslate.configuration.input_file.
|
43
66
|
#
|
44
|
-
# @
|
45
|
-
# @
|
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
|
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
|
-
#
|
97
|
-
#
|
98
|
-
#
|
99
|
-
#
|
100
|
-
# @param
|
101
|
-
# @
|
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
|
-
#
|
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]
|
122
|
-
# @param
|
123
|
-
# @param target_lang_code [String] target language code
|
124
|
-
# @param target_lang_name [String] target language name
|
125
|
-
# @return
|
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
|
-
#
|
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
|
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
|
-
|
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
|
-
#
|
39
|
-
#
|
40
|
-
#
|
41
|
-
#
|
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|
|
data/lib/better_translate.rb
CHANGED
@@ -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
|
-
#
|
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
|
-
#
|
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
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
BetterTranslate
|
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
|