Skip to content

Commit b7db1cb

Browse files
Add prune command (#695)
Co-authored-by: Gleb Mazovetskiy <glex.spb@gmail.com>
1 parent 408a57d commit b7db1cb

7 files changed

Lines changed: 150 additions & 0 deletions

File tree

config/locales/en.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ en:
5151
missing: show missing translations, optionally match a pattern
5252
mv: rename/merge the keys in locale data that match the given pattern
5353
normalize: 'normalize translation data: sort and move to the right files'
54+
prune: removes keys from non-base locales that are not present in base locale
5455
remove_unused: remove unused keys
5556
rm: remove the keys in locale data that match the given pattern
5657
translate_missing: translate missing keys with Google Translate or DeepL Pro, optionally match
@@ -121,6 +122,15 @@ en:
121122
Set OpenAI API key via OPENAI_API_KEY environment variable or translation.openai_api_key
122123
in config/i18n-tasks.yml. Get the key at https://openai.com/.
123124
no_results: OpenAI returned no results.
125+
prune:
126+
count:
127+
one: "%{count} key to prune in %{locales}."
128+
other: "%{count} keys to prune in %{locales}."
129+
zero: No keys to prune.
130+
pruned:
131+
one: "%{count} key pruned from %{locales}."
132+
other: "%{count} keys pruned from %{locales}."
133+
title: Pruning keys in %{locales} that are not present in %{base_locale}
124134
remove_unused:
125135
confirm:
126136
one: "%{count} translation will be removed from %{locales}."

config/locales/ru.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ ru:
5050
missing: показать недостающие переводы
5151
mv: переименовать / объединить ключи, которые соответствуют заданному шаблону
5252
normalize: нормализовать файлы переводов (сортировка и распределение)
53+
prune: удаляет ключи из небазовых локалей, которые отсутствуют в базовой локали
5354
remove_unused: удалить неиспользуемые ключи
5455
rm: удалить ключи, которые соответствуют заданному шаблону
5556
translate_missing: перевести недостающие переводы с Google Translate / DeepL Pro
@@ -121,6 +122,19 @@ ru:
121122
Установить ключ API Яндекса с помощью переменной среды OPENAI_API_KEY или translation.openai_api_key
122123
в config / i18n-tasks.yml. Получите ключ по адресу https://openai.com/.
123124
no_results: Яндекс не дал результатов.
125+
prune:
126+
count:
127+
few: "%{count} ключа для удаления в %{locales}."
128+
many: "%{count} ключей для удаления в %{locales}."
129+
one: "%{count} ключ удаления в %{locales}."
130+
other: "%{count} ключей для удаления в %{locales}."
131+
zero: Нет ключей для удаления.
132+
pruned:
133+
few: "%{count} ключа удалено из %{locales}."
134+
many: "%{count} ключeй удалены из %{locales}."
135+
one: "%{count} ключ удалён из %{locales}."
136+
other: "%{count} ключeй удалены из %{locales}."
137+
title: Обрезка ключей в %{locales}, которые не присутствуют в %{base_locale}
124138
remove_unused:
125139
confirm:
126140
few: Переводы (%{count}) будут удалены из %{locales}.

lib/i18n/tasks/base_task.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
require "i18n/tasks/data"
2020
require "i18n/tasks/configuration"
2121
require "i18n/tasks/stats"
22+
require "i18n/tasks/prune"
2223

2324
module I18n
2425
module Tasks
@@ -38,6 +39,7 @@ class BaseTask
3839
include Configuration
3940
include Data
4041
include Stats
42+
include Prune
4143

4244
def initialize(config_file: nil, **config)
4345
@config_override = config_file
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
module I18n::Tasks
2+
module Command
3+
module Commands
4+
module Prune
5+
include ::I18n::Tasks::Command::Collection
6+
7+
cmd :prune, desc: t("i18n_tasks.cmd.desc.prune"), args: %i[confirm]
8+
def prune(opts = {})
9+
diff_forest = i18n.prunable_keys(
10+
base_locale: i18n.base_locale,
11+
locales: i18n.locales - [base_locale]
12+
)
13+
14+
print_info t("i18n_tasks.prune.title", locales: locales.join(", "), base_locale: base_locale)
15+
16+
if diff_forest.empty?
17+
print_success(t("i18n_tasks.prune.count", count: 0))
18+
return
19+
end
20+
21+
count = diff_forest.root_key_values.size
22+
23+
terminal_report.show_tree(diff_forest)
24+
print_error(t("i18n_tasks.prune.count", count:, locales: locales.join(", ")))
25+
26+
# The user should confirm before we proceed with the deletion unless the force option is set
27+
unless opts[:confirm] || agree(t("i18n_tasks.common.continue_q"))
28+
return
29+
end
30+
print_success(t("i18n_tasks.prune.pruned", count:, locales: locales.join(", ")))
31+
32+
i18n.data.remove_by_key!(diff_forest)
33+
end
34+
35+
private
36+
37+
def print_success(message)
38+
log_stderr(Rainbow("✓ #{message}").green.bright)
39+
end
40+
41+
def print_error(message)
42+
log_stderr(Rainbow(message).red.bright)
43+
end
44+
45+
def print_info(message)
46+
log_stderr(message)
47+
end
48+
end
49+
end
50+
end
51+
end

lib/i18n/tasks/commands.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
require "i18n/tasks/command/commands/tree"
1212
require "i18n/tasks/command/commands/meta"
1313
require "i18n/tasks/command/commands/check_prism"
14+
require "i18n/tasks/command/commands/prune"
1415
require "i18n/tasks/command/commander"
1516

1617
module I18n::Tasks
@@ -25,6 +26,7 @@ class Commands < Command::Commander
2526
include Command::Commands::Tree
2627
include Command::Commands::Meta
2728
include Command::Commands::CheckPrism
29+
include Command::Commands::Prune
2830

2931
require "highline/import"
3032
end

lib/i18n/tasks/prune.rb

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# frozen_string_literal: true
2+
3+
module I18n::Tasks::Prune
4+
def prunable_keys(base_locale:, locales:)
5+
diff_forest = empty_forest
6+
locales.each do |locale|
7+
# Based on the docs, missing_diff_tree only returns keys that are in the second argument but not in the first
8+
locale_forest =
9+
missing_diff_tree(base_locale, locale).tap do |locale_forest|
10+
locale_forest.mv_key!(compile_key_pattern(base_locale), locale, root: true)
11+
end
12+
13+
diff_forest.merge!(locale_forest)
14+
end
15+
16+
diff_forest
17+
end
18+
end
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# frozen_string_literal: true
2+
3+
require "spec_helper"
4+
5+
RSpec.describe "Prune commands" do
6+
delegate :run_cmd, to: :TestCodebase
7+
8+
let(:locale_data) do
9+
{
10+
"en" => {
11+
"hello" => "Hello",
12+
"en_only" => "This key exists only in English"
13+
},
14+
"fr" => {
15+
"hello" => "Bonjour",
16+
"fr_de" => "This key exists only in French and german"
17+
},
18+
"de" => {
19+
"hello" => "Hallo",
20+
"fr_de" => "This key exists only in French and german"
21+
},
22+
"es" => {
23+
"hello" => "Hello"
24+
}
25+
}
26+
end
27+
28+
let(:config) { {base_locale: "en", locales: %w[en de fr es]} }
29+
30+
around do |ex|
31+
TestCodebase.setup(
32+
"config/i18n-tasks.yml" => config.to_yaml,
33+
"config/locales/en.yml" => {"en" => locale_data["en"]}.to_yaml,
34+
"config/locales/de.yml" => {"de" => locale_data["de"]}.to_yaml,
35+
"config/locales/fr.yml" => {"fr" => locale_data["fr"]}.to_yaml,
36+
"config/locales/es.yml" => {"es" => locale_data["es"]}.to_yaml
37+
)
38+
TestCodebase.in_test_app_dir { ex.call }
39+
TestCodebase.teardown
40+
end
41+
42+
it "#prune" do
43+
run_cmd("prune", "--confirm")
44+
45+
locale_data["fr"].delete("fr_de")
46+
locale_data["de"].delete("fr_de")
47+
48+
expect(YAML.load_file("config/locales/en.yml")).to eq("en" => locale_data["en"])
49+
expect(YAML.load_file("config/locales/fr.yml")).to eq("fr" => locale_data["fr"])
50+
expect(YAML.load_file("config/locales/de.yml")).to eq("de" => locale_data["de"])
51+
expect(YAML.load_file("config/locales/es.yml")).to eq("es" => locale_data["es"])
52+
end
53+
end

0 commit comments

Comments
 (0)