Class: BetterTranslate::Translator
- Inherits:
-
Object
- Object
- BetterTranslate::Translator
- Defined in:
- lib/better_translate/translator.rb
Overview
Main translator class
Coordinates the translation process using configuration, providers, strategies, and YAML handling.
Instance Attribute Summary collapse
-
#config ⇒ Configuration
readonly
Configuration object.
Instance Method Summary collapse
-
#build_output_path_for_file(input_file, target_lang_code) ⇒ String
private
private
Build output path for a specific file and language.
-
#initialize(config) ⇒ Translator
constructor
Initialize translator.
-
#record_error(results, lang, error) ⇒ void
private
private
Record translation error in results.
-
#resolve_input_files ⇒ Array<String>
private
private
Resolve input files from config.
-
#translate_all ⇒ Hash
Translate to all target languages.
-
#translate_language(source_strings, lang, handler) ⇒ void
private
private
Translate to a single language.
-
#translate_parallel(source_strings, handler) ⇒ Hash
private
private
Translate languages in parallel.
-
#translate_sequential(source_strings, handler) ⇒ Hash
private
private
Translate languages sequentially.
Constructor Details
#initialize(config) ⇒ Translator
Initialize translator
32 33 34 35 36 37 38 39 40 41 42 43 |
# File 'lib/better_translate/translator.rb', line 32 def initialize(config) @config = config @config.validate! provider_name = config.provider || :chatgpt @provider = ProviderFactory.create(provider_name, config) # Resolve input files (supports input_file, input_files array, or glob pattern) @input_files = resolve_input_files # We'll determine handler per file during translation @progress_tracker = ProgressTracker.new(enabled: config.verbose) end |
Instance Attribute Details
#config ⇒ Configuration (readonly)
Returns Configuration object.
23 24 25 |
# File 'lib/better_translate/translator.rb', line 23 def config @config end |
Instance Method Details
#build_output_path_for_file(input_file, target_lang_code) ⇒ String (private)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Build output path for a specific file and language
Replaces source language code with target language code in filename and preserves directory structure
286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 |
# File 'lib/better_translate/translator.rb', line 286 def build_output_path_for_file(input_file, target_lang_code) # Get file basename and directory dir = File.dirname(input_file) basename = File.basename(input_file) ext = File.extname(basename) name_without_ext = File.basename(basename, ext) # Replace source language code with target language code # Handles patterns like: common.en.yml -> common.it.yml new_basename = name_without_ext.gsub(/\.#{config.source_language}$/, ".#{target_lang_code}") + ext # If no language code in filename, use simple pattern new_basename = "#{target_lang_code}#{ext}" if new_basename == basename # Build output path if config.output_folder # Use output_folder but preserve relative directory structure if input was nested File.join(config.output_folder, new_basename) else File.join(dir, new_basename) end end |
#record_error(results, lang, error) ⇒ void (private)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
This method returns an undefined value.
Record translation error in results
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 |
# File 'lib/better_translate/translator.rb', line 204 def record_error(results, lang, error) results[:failure_count] += 1 # @type var error_context: Hash[Symbol, untyped] error_context = if error.is_a?(BetterTranslate::Error) error.context else {} end results[:errors] << { language: lang[:name], error: error., context: error_context } @progress_tracker.error(lang[:name], error) end |
#resolve_input_files ⇒ Array<String> (private)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Resolve input files from config
Handles input_file, input_files array, or glob patterns
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
# File 'lib/better_translate/translator.rb', line 106 def resolve_input_files files = if config.input_files # Handle input_files (array or glob pattern) if config.input_files.is_a?(Array) config.input_files else # Glob pattern Dir.glob(config.input_files) end elsif config.input_file # Backward compatibility with single input_file [config.input_file] else [] # : Array[String] end # Validate files exist (unless glob pattern that found nothing) if files.empty? if config.input_files && !config.input_files.is_a?(Array) raise FileError, "No files found matching pattern: #{config.input_files}" end raise FileError, "No input files specified" end files.each do |file| Validator.validate_file_exists!(file) end files end |
#translate_all ⇒ Hash
Translate to all target languages
Uses parallel execution when max_concurrent_requests > 1, sequential execution otherwise. Supports multiple input files.
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
# File 'lib/better_translate/translator.rb', line 56 def translate_all # @type var combined_results: translation_results combined_results = { success_count: 0, failure_count: 0, errors: [] } @input_files.each do |input_file| # Create appropriate handler for this file handler = if input_file.end_with?(".json") JsonHandler.new(config) else YAMLHandler.new(config) end # Temporarily set input_file for this iteration original_input_file = config.input_file config.input_file = input_file source_strings = handler.get_source_strings results = if config.max_concurrent_requests > 1 translate_parallel(source_strings, handler) else translate_sequential(source_strings, handler) end # Restore original config config.input_file = original_input_file # Accumulate results combined_results[:success_count] += results[:success_count] combined_results[:failure_count] += results[:failure_count] combined_results[:errors].concat(results[:errors]) end combined_results end |
#translate_language(source_strings, lang, handler) ⇒ void (private)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
This method returns an undefined value.
Translate to a single language
228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 |
# File 'lib/better_translate/translator.rb', line 228 def translate_language(source_strings, lang, handler) target_lang_code = lang[:short_name] target_lang_name = lang[:name] # Filter exclusions strings_to_translate = handler.filter_exclusions(source_strings, target_lang_code) return if strings_to_translate.empty? # Select strategy strategy = Strategies::StrategySelector.select( strings_to_translate.size, config, @provider, @progress_tracker ) # Translate @progress_tracker.reset translated = strategy.translate(strings_to_translate, target_lang_code, target_lang_name) # Save - generate output path with proper filename current_input_file = config.input_file or raise "No input file set" output_path = build_output_path_for_file(current_input_file, target_lang_code) final_translations = if config.translation_mode == :incremental handler.merge_translations(output_path, translated) else Utils::HashFlattener.unflatten(translated) end # Wrap in language key (e.g., "it:") wrapped = { target_lang_code => final_translations } # Write using appropriate handler method (write_yaml or write_json) if handler.is_a?(JsonHandler) handler.write_json(output_path, wrapped) else handler.write_yaml(output_path, wrapped) end @progress_tracker.complete(target_lang_name, translated.size) end |
#translate_parallel(source_strings, handler) ⇒ Hash (private)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Translate languages in parallel
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 |
# File 'lib/better_translate/translator.rb', line 146 def translate_parallel(source_strings, handler) # @type var results: translation_results results = { success_count: 0, failure_count: 0, errors: [] } mutex = Mutex.new # Process languages in batches of max_concurrent_requests config.target_languages.each_slice(config.max_concurrent_requests) do |batch| threads = batch.map do |lang| Thread.new do translate_language(source_strings, lang, handler) mutex.synchronize { results[:success_count] += 1 } rescue StandardError => e mutex.synchronize { record_error(results, lang, e) } end end threads.each(&:join) end results end |
#translate_sequential(source_strings, handler) ⇒ Hash (private)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Translate languages sequentially
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 |
# File 'lib/better_translate/translator.rb', line 178 def translate_sequential(source_strings, handler) # @type var results: translation_results results = { success_count: 0, failure_count: 0, errors: [] } config.target_languages.each do |lang| translate_language(source_strings, lang, handler) results[:success_count] += 1 rescue StandardError => e record_error(results, lang, e) end results end |