# frozen_string_literal: true require 'i18n/tasks/reports/base' require 'i18n/tasks/rainbow_utils' require 'terminal-table' module I18n module Tasks module Reports class Terminal < Base # rubocop:disable Metrics/ClassLength def missing_keys(forest = task.missing_keys) # rubocop:disable Metrics/AbcSize forest = collapse_missing_tree! forest if forest.present? print_title missing_title(forest) print_table headings: [Rainbow(I18n.t('i18n_tasks.common.locale')).cyan.bright, Rainbow(I18n.t('i18n_tasks.common.key')).cyan.bright, I18n.t('i18n_tasks.missing.details_title')] do |t| t.rows = sort_by_attr!(forest_to_attr(forest)).map do |a| [{ value: Rainbow(format_locale(a[:locale])).cyan, alignment: :center }, format_key(a[:key], a[:data]), missing_key_info(a)] end end else print_success I18n.t('i18n_tasks.missing.none') end end def icon(type) glyph = missing_type_info(type)[:glyph] { missing_used: Rainbow(glyph).red, missing_diff: Rainbow(glyph).yellow }[type] end def used_keys(used_tree = task.used_tree) # For the used tree we may have usage nodes that are not leaves as references. keys_nodes = used_tree.nodes.select { |node| node.data[:occurrences].present? }.map do |node| [node.full_key(root: false), node] end print_title used_title(keys_nodes, used_tree.first.root.data[:key_filter]) # Group multiple nodes if keys_nodes.present? keys_nodes.sort! { |a, b| a[0] <=> b[0] }.each do |key, node| print_occurrences node, key end else print_error I18n.t('i18n_tasks.usages.none') end end def unused_keys(tree = task.unused_keys) keys = tree.root_key_value_data(true) if keys.present? print_title unused_title(keys) print_locale_key_value_data_table keys else print_success I18n.t('i18n_tasks.unused.none') end end def eq_base_keys(tree = task.eq_base_keys) keys = tree.root_key_value_data(true) if keys.present? print_title eq_base_title(keys) print_locale_key_value_data_table keys else print_info Rainbow('No translations are the same as base value').cyan end end def show_tree(tree) print_locale_key_value_data_table tree.root_key_value_data(true) end def forest_stats(forest, stats = task.forest_stats(forest)) text = if stats[:locale_count] == 1 I18n.t('i18n_tasks.data_stats.text_single_locale', stats) else I18n.t('i18n_tasks.data_stats.text', stats) end title = Rainbow(I18n.t('i18n_tasks.data_stats.title', stats.slice(:locales))).bright print_info "#{Rainbow(title).cyan} #{Rainbow(text).cyan}" end def mv_results(results) results.each do |(from, to)| if to print_info "#{Rainbow(from).cyan} #{Rainbow('⮕').yellow.bright} #{Rainbow(to).cyan}" else print_info "#{Rainbow(from).red}#{Rainbow(' 🗑').red.bright}" end end end private def missing_key_info(leaf) if leaf[:type] == :missing_used first_occurrence leaf else "#{Rainbow(leaf[:data][:missing_diff_locale]).cyan} "\ "#{format_value(leaf[:value].is_a?(String) ? leaf[:value].strip : leaf[:value])}" end end def format_key(key, data) if data[:ref_info] from, to = data[:ref_info] resolved = key[0...to.length] after = key[to.length..-1] " #{Rainbow(from).yellow}#{Rainbow(after).cyan}\n" \ "#{Rainbow('⮕').yellow.bright} #{Rainbow(resolved).yellow.bright}" else Rainbow(key).cyan end end def format_value(val) val.is_a?(Symbol) ? "#{Rainbow('⮕ ').yellow.bright}#{Rainbow(val).yellow}" : val.to_s.strip end def format_reference_desc(node_data) return nil unless node_data case node_data[:ref_type] when :reference_usage Rainbow('(ref)').yellow.bright when :reference_usage_resolved Rainbow('(resolved ref)').yellow.bright when :reference_usage_key Rainbow('(ref key)').yellow.bright end end def print_occurrences(node, full_key = node.full_key) occurrences = node.data[:occurrences] puts [Rainbow(full_key).bright, format_reference_desc(node.data), (Rainbow(occurrences.size).green if occurrences.size > 1)].compact.join ' ' occurrences.each do |occurrence| puts " #{key_occurrence full_key, occurrence}" end end def print_locale_key_value_data_table(locale_key_value_datas) if locale_key_value_datas.present? print_table headings: [Rainbow(I18n.t('i18n_tasks.common.locale')).cyan.bright, Rainbow(I18n.t('i18n_tasks.common.key')).cyan.bright, I18n.t('i18n_tasks.common.value')] do |t| t.rows = locale_key_value_datas.map { |(locale, k, v, data)| [{ value: Rainbow(locale).cyan, alignment: :center }, format_key(k, data), format_value(v)] } end else puts 'ø' end end def print_title(title) log_stderr "#{Rainbow(title.strip).bright} #{I18n::Tasks::RainbowUtils.faint_color('|')} " \ "#{"i18n-tasks v#{I18n::Tasks::VERSION}"}" end def print_success(message) log_stderr Rainbow("✓ #{I18n.t('i18n_tasks.cmd.encourage').sample} #{message}").green.bright end def print_error(message) log_stderr(Rainbow(message).red.bright) end def print_info(message) log_stderr message end def indent(txt, n = 2) txt.gsub(/^/, ' ' * n) end def print_table(opts, &block) puts ::Terminal::Table.new(opts, &block) end def key_occurrence(full_key, occurrence) location = Rainbow("#{occurrence.path}:#{occurrence.line_num}").green source = highlight_key(occurrence.raw_key || full_key, occurrence.line, occurrence.line_pos..-1).strip "#{location} #{source}" end def first_occurrence(leaf) # @type [I18n::Tasks::Scanners::KeyOccurrences] occurrences = leaf[:data][:occurrences] # @type [I18n::Tasks::Scanners::Occurrence] first = occurrences.first [ Rainbow("#{first.path}:#{first.line_num}").green, ("(#{I18n.t 'i18n_tasks.common.n_more', count: occurrences.length - 1})" if occurrences.length > 1) ].compact.join(' ') end def highlight_key(full_key, line, range = (0..-1)) line.dup.tap do |s| s[range] = s[range].sub(full_key) do |m| highlight_string m end end end module HighlightUnderline def highlight_string(s) Rainbow(s).underline end end module HighlightOther def highlight_string(s) Rainbow(s).yellow end end if Gem.win_platform? include HighlightOther else include HighlightUnderline end end end end end