module LostInTranslation

Public Class Methods

ask_for_file(master, namespace = '') click to toggle source
# File lib/lit/user_interface.rb, line 7
def self.ask_for_file(master, namespace = '')
  text = master ? "# Master-Locale (e.g. 'en'): " : "# Slave-Locale (e.g. 'de'): "
  file = {}
  file[:lang] = [(print text), Readline.readline()][1]
  file[:filename] = namespace.blank? ? file[:lang] : "#{namespace}.#{file[:lang]}"
  file[:path] = defined?(Rails) ? "#{Rails.root}/config/locales/#{file[:filename]}.yml" : ask_for_path
  if !File.exist?(file[:path]) && master
    log "File #{file[:path]} not found!"
    file[:path] = ask_for_path
  elsif !File.exist?(file[:path]) && !master
    log "File #{file[:filename]} not found! It will be created!"
    File.open(file[:path], 'w') { |f| f.write({ file[:lang] => {} }.to_yaml(line_width: -1)) }
  end
  file[:app_name] = Rails&.application&.class&.parent_name
  file
end
ask_for_locales_path() click to toggle source
# File lib/lit/user_interface.rb, line 24
def self.ask_for_locales_path
  return "#{Rails.root}/config/locales" if defined?(Rails)
  log
  log 'Please type in the absolute path your locales-folder?'
  log 'e.g. /home/youruser/Documents/your-awesome-app/config/locales'
  path = [(print '# '), Readline.readline()][1]
  path
end
ask_for_permission(master, count, slave = nil) click to toggle source
# File lib/lit/user_interface.rb, line 33
def self.ask_for_permission(master, count, slave = nil)
  log
  log 'Comparing Locales'
  log "Application: #{master[:app_name]}" if master[:app_name]
  log "Master Locale: #{master[:filename]}.yml" if master
  log "Slave Locale: #{slave[:filename]}.yml" if slave
  log "Difference: #{count} Entries"
  a = [(print "# Wanna do it? [Y/n]: "), Readline.readline()][1]
  a == 'y' || a == 'Y' || a == ''
end
ask_for_sorting(file, app_name) click to toggle source
# File lib/lit/user_interface.rb, line 67
def self.ask_for_sorting(file, app_name)
  log
  log "Application: #{app_name}" unless app_name.blank?
  log "Do you want the #{file}.yml to be sorted?"
  log 'Alphabetically & Recursive (ASC)'
  a = [(print "# [Y/n]: "), Readline.readline()][1]
  a == 'y' || a == 'Y' || a == ''
end
ask_for_split(file, count) click to toggle source
# File lib/lit/user_interface.rb, line 59
def self.ask_for_split(file, count)
  log
  log "Splitting File #{file}.yml"
  log "File-Count: #{count} new files will be create!"
  a = [(print "# Wanna do it? [Y/n]: "), Readline.readline()][1]
  a == 'y' || a == 'Y' || a == ''
end
ask_for_task(tasks) click to toggle source
# File lib/lit/user_interface.rb, line 44
def self.ask_for_task(tasks)
  log
  log 'Welcome, stranger!'
  log 'Are you lost in translation, too?'
  log 'Then I will be glad to help you one of the following tasks!'
  tasks.each_with_index do |task, i|
    log "#{i + 1} => #{task[:text]}"
  end
  log 'Soo ... what can I do for you? (Type in the Number)'
  id = [(print 'Task No.: '), Readline.readline()][1]
  tasks[(id.to_i - 1)]
rescue
  puts "Cannot find task with id #{id}"
end
ask_for_translation(value, new_structure, lang1, lang2, diff_count, diff_counter) click to toggle source
# File lib/lit/user_interface.rb, line 76
def self.ask_for_translation(value, new_structure, lang1, lang2, diff_count, diff_counter)
  system 'clear'
  log
  log "#{diff_counter} / #{diff_count}"
  new_s = new_structure.join('---')
  log "#{new_s} is missing"

  snippets = LostInTranslation.define_snippets.reverse
  snippets.each do |snippet|
    value = value.gsub(snippet.second, snippet.first)
  end

  puts "# In #{lang1.upcase}: #{value}"
  new_val = [(print "# In #{lang2.upcase}: "), Readline.readline()][1]
  new_val = (new_s + '---' + new_val) unless new_val.blank?
  new_val
end
breakout() click to toggle source
# File lib/lost_in_translation.rb, line 43
def self.breakout
  raise "Well in that case, forget it!"
end
compare() click to toggle source
# File lib/tasks/compare.rb, line 4
def self.compare
  @master, @slave, paths = init

  prepare_for_edit(master: @master[:path], slave: @slave[:path])
  prepare_paths([paths[:master], paths[:slave]])

  master_file = YAML.load_file(paths[:master])
  slave_file = YAML.load_file(paths[:slave])

  count = diff(master_file[@master[:lang]], slave_file[@slave[:lang]], @master[:lang],
               @slave[:lang], [@slave[:lang]], [], mode: 'count')

  breakout unless ask_for_permission(@master, count, @slave)

  new_strings_array = diff(master_file[@master[:lang]], slave_file[@slave[:lang]], @master[:lang],
                           @slave[:lang], [@slave[:lang]], [], diff_count: count, mode: 'replace')

  new_strings_array.each do |string|
    result = string_to_hash(string)
    merge_hash(result, slave_file)
  end

  File.open(paths[:slave], 'w') { |file| file.write(slave_file.to_yaml(line_width: -1)) }
  postpare_paths([paths[:slave]])
  FileUtils.cp(paths[:slave], @slave[:path])

  reset_tmp_files

  log 'Done'
  log
end
count() click to toggle source
# File lib/tasks/count.rb, line 4
def self.count
  @master, @slave, paths = init

  prepare_for_edit(master: @master[:path], slave: @slave[:path])
  prepare_paths([paths[:master], paths[:slave]])

  master_file = YAML.load_file(paths[:master])
  slave_file = YAML.load_file(paths[:slave])

  count = diff(master_file[@master[:lang]], slave_file[@slave[:lang]], @master[:lang], @slave[:lang],
               [@slave[:lang]], [], mode: 'count')

  count2 = diff(slave_file[@slave[:lang]], master_file[@master[:lang]], @slave[:lang], @master[:lang],
                [@master[:lang]], [], mode: 'count')


  reset_tmp_files
  log
  log 'Differences between Locales'
  log "#{@master[:filename]}.yml => #{@slave[:filename]}.yml = #{count}"
  log "#{@slave[:filename]}.yml => #{@master[:filename]}.yml = #{count2}"
  log 'Done'
  log
end
define_snippets() click to toggle source
# File lib/lit/file_functions.rb, line 23
def self.define_snippets
  snippets = []
  snippets << ['<<', 'a_greater_than_sign']
  snippets << ['*', 'an_asterik_sign']
  snippets << ['!', 'a_bang_sign']
  snippets << ['%', 'a_percentage_sign']
  snippets << ['a_bang_sign \'', '\'a_bang_sign']
  snippets
end
delete_missing() click to toggle source
# File lib/tasks/delete_missing.rb, line 4
def self.delete_missing
  tbd
  @master, @slave, paths = init

  prepare_for_edit(master: @master[:path], slave: @slave[:path])
  prepare_paths([paths[:master], paths[:slave]])

  master_file = YAML.load_file(paths[:master])
  slave_file = YAML.load_file(paths[:slave])

  breakout unless ask_for_permission(@master, count, @slave)

  new_strings_array = diff(master_file[@master[:lang]], slave_file[@slave[:lang]], @master[:lang],
                           @slave[:lang], [@slave[:lang]], [], mode: 'clean')

  master_file = {}
  new_strings_array.each do |string|
    string = string.split('---').map { |x| x == @slave[:lang] ? @master[:lang] : x }.join('---')
    result = string_to_hash(string)
    merge_hash(result, master_file)
  end

  File.open(@master[:path], 'w') { |file| file.write(master_file.to_yaml) }
  postpare_paths([@master[:path], @slave[:path]])
  reset_tmp_files
  log 'Done'
  log
end
diff(root, compared, lang1, lang2, structure = [], new_array = [], options = {}) click to toggle source
# File lib/lit/difference.rb, line 5
def self.diff(root, compared, lang1, lang2, structure = [], new_array = [], options = {})
  @slave_file = options[:slave_file] || ''
  @mode = options[:mode] || 'count' # replace, clean, count
  @diff_count = options[:diff_count] || 0
  @diff_counter = options[:diff_counter] || 0
  root.each_pair do |key, value|
    next_root     = root[key]
    next_compared = compared.nil? ? nil : compared[key]
    new_structure = structure.dup << key
    if value.is_a?(String) && (compared.nil? || compared[key].nil?)
      if value_missing(@slave_file, new_structure)
        @diff_counter += 1
        if @mode == 'replace'
          new_val = ask_for_translation(value, new_structure, lang1, lang2, @diff_count, @diff_counter)
          new_array << new_val unless new_val.blank?
        elsif @mode == 'clean'
          new_array << "#{new_structure.join('---')}---#{value}"
        end
      end
    end
    if next_root.is_a? Hash
      diff(next_root, next_compared, lang1, lang2, new_structure, new_array,
           diff_counter: @diff_counter, diff_count: @diff_count, mode: @mode, slave_file: @slave_file)
    end
  end
  @mode == 'count' ? @diff_counter : new_array
end
fully_automatic() click to toggle source
# File lib/tasks/fully_automatic.rb, line 4
def self.fully_automatic
  tbd
  @master, @slave, paths = init

  prepare_for_edit(master: @master[:path], slave: @slave[:path])
  prepare_paths([paths[:master], paths[:slave]])

  master_file = YAML.load_file(paths[:master])
  slave_file = YAML.load_file(paths[:slave])

  count = diff(master_file[@master[:lang]], slave_file[@slave[:lang]], @master[:lang],
               @slave[:lang], [@slave[:lang]], [], mode: 'count')

  breakout unless ask_for_permission(@master, count, @slave)

  new_strings_array = diff(master_file[@master[:lang]], slave_file[@slave[:lang]], @master[:lang],
                           @slave[:lang], [@slave[:lang]], [], diff_count: count, mode: 'replace')

  new_strings_array.each do |string|
    result = string_to_hash(string)
    merge_hash(result, slave_file)
  end

  File.open(paths[:slave], 'w') { |file| file.write(slave_file.to_yaml(line_width: -1)) }
  postpare_paths([paths[:slave]])
  FileUtils.cp(paths[:slave], @slave[:path])

  reset_tmp_files
end
get_locale(path) click to toggle source
# File lib/lit/difference.rb, line 38
def self.get_locale(path)
  path.split('/').last.split('.').first unless path.empty?
end
half_automatic() click to toggle source
# File lib/tasks/half_automatic.rb, line 4
def self.half_automatic
  tbd
  @master, @slave, paths = init

  prepare_for_edit(master: @master[:path], slave: @slave[:path])
  prepare_paths([paths[:master], paths[:slave]])

  master_file = YAML.load_file(paths[:master])
  slave_file = YAML.load_file(paths[:slave])

  count = diff(master_file[@master[:lang]], slave_file[@slave[:lang]], @master[:lang],
               @slave[:lang], [@slave[:lang]], [], mode: 'count')

  breakout unless ask_for_permission(@master, count, @slave)

  new_strings_array = diff(master_file[@master[:lang]], slave_file[@slave[:lang]], @master[:lang],
                           @slave[:lang], [@slave[:lang]], [], diff_count: count, mode: 'replace')

  new_strings_array.each do |string|
    result = string_to_hash(string)
    merge_hash(result, slave_file)
  end

  File.open(paths[:slave], 'w') { |file| file.write(slave_file.to_yaml(line_width: -1)) }
  postpare_paths([paths[:slave]])
  FileUtils.cp(paths[:slave], @slave[:path])

  reset_tmp_files
end
init(with_slave = true) click to toggle source
# File lib/lost_in_translation.rb, line 32
def self.init(with_slave = true)
  system 'clear'
  log
  log 'What I18n-yaml files do you want to compare?'
  namespace = [(print "# Namespace? ('namespace' for 'namespace.en.yml'): "), Readline.readline()][1]
  master = ask_for_file(true, namespace) # lang path app_name
  slave = with_slave ? ask_for_file(false, namespace) : {}
  invalid_filepath if !File.exist?(master[:path]) && (!File.exist?(slave[:path]) && with_slave)
  [master, slave, tmp_paths]
end
invalid_filepath() click to toggle source
# File lib/lost_in_translation.rb, line 47
def self.invalid_filepath
  raise "One on the filepaths are invalid!"
end
joke() click to toggle source
# File lib/tasks/start.rb, line 30
def self.joke
  system 'clear'
  log
  log 'A: Knock, Knock!'
  log 'B: Who is there?'
  log 'A: Ya.'
  log 'B: Ya who?'
  log 'A: Oh, I prefer google.'
  log
end
list_files() click to toggle source
# File lib/tasks/start.rb, line 48
def self.list_files
  system 'clear'

  path = ask_for_locales_path
  files = Dir["#{path}/**/*.yml"]
  puts "No Files found in #{path}" if files.empty?
  files&.sort&.each do |file|
    puts file.split('/').last
  end
  start
end
log(text = '') click to toggle source
# File lib/lit/user_interface.rb, line 94
def self.log(text = '')
  max_length = 80
  outside = 4
  puts '#' * max_length if text.blank?
  arr = text.scan(/.{1,#{max_length - outside}}/)
  arr.each do |str|
    fill_length = max_length - str.length - outside
    fill = ' ' * fill_length
    puts '# ' + str + fill + ' #'
  end
end
merge_hash(merge_from, merge_to) click to toggle source
# File lib/lit/hash.rb, line 12
def self.merge_hash(merge_from, merge_to)
  return if merge_from.is_a?(String) || merge_to.is_a?(String)
  merged_hash = merge_to
  first_key = merge_from.keys.first
  merged_hash[first_key] = if merge_to.key?(first_key)
                             merge_hash(merge_from[first_key], merge_to[first_key])
                           else
                             merge_from[first_key]
                           end
  merged_hash
end
postpare_paths(paths = []) click to toggle source
# File lib/lit/file_functions.rb, line 17
def self.postpare_paths(paths = [])
  paths.each do |p|
    prepare_yaml(p, true)
  end
end
prepare_for_edit(options = {}) click to toggle source
# File lib/lit/file_functions.rb, line 5
def self.prepare_for_edit(options = {})
  paths = tmp_paths
  FileUtils.cp(options[:master], paths[:master]) if options[:master]
  FileUtils.cp(options[:slave], paths[:slave]) if options[:slave]
end
prepare_paths(paths = []) click to toggle source
# File lib/lit/file_functions.rb, line 11
def self.prepare_paths(paths = [])
  paths.each do |p|
    prepare_yaml(p, false)
  end
end
quit() click to toggle source
# File lib/tasks/start.rb, line 41
def self.quit
  system 'clear'
  log
  log 'Thank you and goodbye!'
  log
end
recent() click to toggle source
# File lib/tasks/recent.rb, line 4
def self.recent
  @master, @slave, paths = init

  FileUtils.cp(@master[:path], paths[:copy])
  `git checkout "#{@master[:path]}"`

  prepare_for_edit(master: @master[:path], slave: @slave[:path])
  FileUtils.cp(paths[:copy], @master[:path])

  prepare_paths([paths[:master], paths[:slave], paths[:copy]])

  master_file = YAML.load_file(paths[:master])
  master_copy_file = YAML.load_file(paths[:copy])
  slave_file = YAML.load_file(paths[:slave])

  count = diff(master_copy_file[@master[:lang]], master_file[@master[:lang]], @master[:lang],
               @slave[:lang], [@slave[:lang]], [], mode: 'count', slave_file: slave_file)

  breakout unless ask_for_permission(@master, count, @slave)

  new_strings_array = diff(master_copy_file[@master[:lang]], master_file[@master[:lang]], @master[:lang],
                           @slave[:lang], [@slave[:lang]], [], mode: 'replace', diff_counter: 0, diff_count: count,
                           slave_file: slave_file)

  new_strings_array.each do |string|
    result = string_to_hash(string)
    merge_hash(result, slave_file)
  end

  File.open(paths[:slave], 'w') { |file| file.write(slave_file.to_yaml(line_width: -1)) }
  postpare_paths([paths[:slave]])
  FileUtils.cp(paths[:slave], @slave[:path])

  reset_tmp_files
  log 'Done'
  log
end
reset_tmp_files() click to toggle source
# File lib/lit/file_functions.rb, line 33
def self.reset_tmp_files
  paths = tmp_paths
  paths.each_value do |path|
    File.open(path, 'w') { |file| file.write('') }
  end
end
root() click to toggle source
# File lib/lost_in_translation.rb, line 20
def self.root
  File.dirname __dir__
end
sort_file() click to toggle source
# File lib/tasks/sort_file.rb, line 4
def self.sort_file
  @master, @slave, paths = init(false)

  prepare_for_edit(master: @master[:path], slave: @slave[:path])
  prepare_paths([paths[:master]])

  master_file = YAML.load_file(paths[:master])

  if ask_for_sorting(@master[:lang], @master[:app_name])
    master_file = sort_hash(master_file)
    File.open(paths[:master], 'w') { |file| file.write(master_file.to_yaml(line_width: -1)) }
  end

  postpare_paths([paths[:master]])

  FileUtils.cp(paths[:master], @master[:path])

  reset_tmp_files
  log 'Done'
  log
end
sort_hash(object) click to toggle source
# File lib/lit/hash.rb, line 24
def self.sort_hash(object)
  return object unless object.is_a?(Hash)
  hash = {}
  object.each { |k, v| hash[k] = sort_hash(v) }
  sorted = hash.sort { |a, b| a[0].to_s <=> b[0].to_s }
  hash.class[sorted]
end
split_file() click to toggle source
# File lib/tasks/split_file.rb, line 4
def self.split_file
  @master, @slave, paths = init(false)

  prepare_for_edit(master: @master[:path])
  prepare_paths([paths[:master]])

  master_file = YAML.load_file(paths[:master])[@master[:lang]]

  count = master_file.select { |_k, v| v.is_a?(Hash) }.size
  new_hash = {}

  if ask_for_split(@master[:lang], count)
    master_file.each_pair do |key, value|
      next unless value.is_a?(Hash)
      new_hash[@master[:lang]] = { key => value }
      File.open(paths[:slave], 'w') { |file| file.write(new_hash.to_yaml(line_width: -1)) }
      postpare_paths([paths[:slave]])
      root_path = @master[:path].split('/').reverse.drop(1).reverse.join('/')
      filename = @master[:path].split('/').last
      FileUtils.cp(paths[:slave], "#{root_path}/#{key}.#{filename}")
      master_file.delete(key)
    end

    new_hash[@master[:lang]] = master_file
    File.open(paths[:master], 'w') { |file| file.write(new_hash.to_yaml(line_width: -1)) }
    postpare_paths([paths[:master]])
    FileUtils.cp(paths[:master], @master[:path])
  end

  reset_tmp_files
  log 'Done'
  log
end
start() click to toggle source
# File lib/tasks/start.rb, line 4
def self.start
  tasks = [
    { text: 'Compare & Translate two I18n-Locale-Files', method: :start_compare },
    { text: 'Count Differences of 2 I18n-Locale-Files', method: :count },
    { text: 'Sort an I18n-Locale-File Alphabetically & Recursive (ASC)', method: :sort_file },
    { text: 'Split an I18n-Locale-File into multiple namespaced Files (1. Level)', method: :split_file },
    { text: 'Show a list of available I18n-Locale-Files', method: :list_files },
    { text: 'Tell me joke', method: :joke },
    { text: 'Quit', method: :quit }
  ]
  task = ask_for_task(tasks)
  LostInTranslation.send(task[:method])
end
start_compare() click to toggle source
# File lib/tasks/start.rb, line 18
def self.start_compare
  tasks = [
    { text: 'Compare everything', method: :compare },
    { text: 'Just compare the recent changes (since last git commit)', method: :recent },
    { text: 'Automatic translation via API', method: :fully_automatic },
    { text: 'Translation suggentions from API', method: :half_automatic },
    { text: 'Compare and delete the differences', method: :delete_missing },
  ]
  task = ask_for_task(tasks)
  LostInTranslation.send(task[:method])
end
string_to_hash(string) click to toggle source
# File lib/lit/hash.rb, line 5
def self.string_to_hash(string)
  array = string.split('---')
  value = array.pop
  arr = array.reverse
  arr[1..-1].inject(arr[0] => value) { |memo, i| { i => memo } }
end
tbd() click to toggle source
# File lib/lost_in_translation.rb, line 51
def self.tbd
  raise "This feature is not yet implemented! Please take another!"
end
tmp_paths() click to toggle source
# File lib/lost_in_translation.rb, line 24
def self.tmp_paths
  tmp_paths = {}
  tmp_paths[:copy] = "#{root}/tmp/tmp_master_copy.yml"
  tmp_paths[:master] = "#{root}/tmp/tmp_master.yml"
  tmp_paths[:slave] = "#{root}/tmp/tmp_slave.yml"
  tmp_paths
end
value_missing(slave_file, structure) click to toggle source
# File lib/lit/difference.rb, line 33
def self.value_missing(slave_file, structure)
  return true if slave_file.blank?
  slave_file.dig(*structure).blank?
end

Private Class Methods

add_single_quotes(text) click to toggle source
# File lib/lit/file_functions.rb, line 82
def self.add_single_quotes(text)
  tmp = ''
  text.each_line do |line|
    tmp += edit_line(line)
  end
  tmp
end
ask_for_path() click to toggle source
# File lib/lit/user_interface.rb, line 108
def self.ask_for_path
  log
  log 'Please type in the absolute path your locale?'
  log 'e.g. /home/youruser/Documents/your-awesome-app/config/locales/de.yml'
  path = [(print '# '), Readline.readline()][1]
  path
end
edit_line(line) click to toggle source
# File lib/lit/file_functions.rb, line 90
def self.edit_line(line)
  return line if line.match(/: .*%.*$/).blank?
  arr = line.split(': ', 2)
  return line if arr[1].blank?
  arr[1] = arr[1].gsub("\'", '')
  arr[1] = arr[1].gsub("\"", '')
  arr[1] = "'" + arr[1] unless arr[1].start_with?("\'") || arr[1].start_with?("\"")
  arr[1] = arr[1].squish + "'\n" unless arr[1].squish.end_with?("\'") || arr[1].squish.end_with?("\"")
  arr[1] = arr[1].gsub("\'! ", "! \'")
  return arr.join(': ')
end
postpare(variables, text) click to toggle source
# File lib/lit/file_functions.rb, line 56
def self.postpare(variables, text)
  variables = text.scan(/varyberryterry.*/)
  variables.each do |var|
    new_var = var.delete(':')
    new_var = new_var.gsub('varyberryterry', ': &')
    text = text.gsub(var, new_var)
  end
  text = add_single_quotes(text)
  text = text.gsub(': !,', ": ! ','")
  text = text.gsub('\'!', '! \'')
  text = text.gsub(/---\n/, '')
  text = text.gsub('!&hellip;', "! '&hellip;'")
  text = text.gsub('!:distance_in_words', "! ':distance_in_words'")
  text
end
prepare(variables, text) click to toggle source
# File lib/lit/file_functions.rb, line 72
def self.prepare(variables, text)
  variables = text.scan(/: &.*/)
  variables.each do |var|
    new_var = var.gsub(': &', 'varyberryterry')
    new_var += ':'
    text = text.gsub(var, new_var)
  end
  text
end
prepare_yaml(path, post) click to toggle source
# File lib/lit/file_functions.rb, line 42
def self.prepare_yaml(path, post)
  text = File.read(path)
  snippets = define_snippets

  snippets = snippets.reverse if post
  snippets.each do |snippet|
    snippet = snippet.reverse if post
    text = text.gsub(snippet.first, snippet.second)
  end
  text = post ? postpare(snippets, text) : prepare(snippets, text)

  File.open(path, 'w') { |file| file.puts text }
end