neeto-translate-cli 0.1.8.beta0 → 0.2.0
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/lib/neeto_translate_cli/cli.rb +8 -2
- data/lib/neeto_translate_cli/next_i18next/payload_builder.rb +126 -0
- data/lib/neeto_translate_cli/payload_builder.rb +2 -1
- data/lib/neeto_translate_cli/translator.rb +4 -1
- data/lib/neeto_translate_cli/version.rb +1 -1
- data/test/neeto_translate_cli/cli_test.rb +17 -17
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 19601fee62f445e2d199a073ab27dd65f1e5cb01364ca2acd92acfac9e290e4c
|
|
4
|
+
data.tar.gz: 77c013b6ec3d310416e8d6e89bee8da9ab340ee9da4b2783b3d2debebef1e32e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 176e1e1dae72b2ceebac27716bda5eca56d798238cd8632a15e6806155369cf16d7fba50c9bb0df2749a756ad2858d69fb2f4da85e56913d618f87807be63a69
|
|
7
|
+
data.tar.gz: b3ce3265a73a5254b249ef9192d0c93660e320a7470ce9c9c7fc95a1059437afa04562a2092d3f46c6b37380fd4b404101d2a533534fa604d9d92f1a4c93606e
|
|
@@ -6,7 +6,7 @@ require "optparse"
|
|
|
6
6
|
|
|
7
7
|
module NeetoTranslateCli
|
|
8
8
|
class CLI
|
|
9
|
-
DEFAULT_LANGUAGES = "en,ar,bg,ca,zh-
|
|
9
|
+
DEFAULT_LANGUAGES = "en,ar,bg,ca,zh-CN,zh-TW,hr,cs,da,nl,et,fil,fi,fr,de,hi,id,it,ja,ko,pl,pt,pt-BR,ro,ru,sk,sl,es,es-MX,sv,th,tr,uk,vi"
|
|
10
10
|
NEETO_TRANSLATE_URL = "https://translate.neeto.com"
|
|
11
11
|
DEFAULT_BACKEND_LOCALE_PATH = "config/locales"
|
|
12
12
|
DEFAULT_FRONTEND_LOCALE_PATH = "app/javascript/src/translations"
|
|
@@ -31,7 +31,8 @@ module NeetoTranslateCli
|
|
|
31
31
|
frontend: DEFAULT_FRONTEND_LOCALE_PATH,
|
|
32
32
|
backend: DEFAULT_BACKEND_LOCALE_PATH,
|
|
33
33
|
default_languages: (ENV["DEFAULT_LANGUAGES"] || DEFAULT_LANGUAGES).to_s.split(","),
|
|
34
|
-
repo: `git rev-parse --show-toplevel`.split("/").last.strip
|
|
34
|
+
repo: `git rev-parse --show-toplevel`.split("/").last.strip,
|
|
35
|
+
branch: "main"
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
OptionParser.new do |opts|
|
|
@@ -43,6 +44,11 @@ module NeetoTranslateCli
|
|
|
43
44
|
opts.on("--repo NAME", "Name of the GitHub repository (eg: neetozone/neeto-cal-web).") do |path|
|
|
44
45
|
options[:repo] = path
|
|
45
46
|
end
|
|
47
|
+
opts.on("--branch NAME", "Base branch to which the PR should be created") { |branch| options[:branch] = branch}
|
|
48
|
+
|
|
49
|
+
# This arg is explicitly added to handle translations in neeto-website and bigbinary-website.
|
|
50
|
+
# Usage: --strategy next-i18next
|
|
51
|
+
opts.on("--strategy NAME", "Name of the i18next package used to generate the translations") { |name| options[:strategy] = name }
|
|
46
52
|
end.parse!(args)
|
|
47
53
|
|
|
48
54
|
options
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "pathname"
|
|
4
|
+
|
|
5
|
+
module NeetoTranslateCli
|
|
6
|
+
module NextI18next
|
|
7
|
+
class PayloadBuilder
|
|
8
|
+
attr_reader :options, :updated_keys
|
|
9
|
+
|
|
10
|
+
def initialize(options)
|
|
11
|
+
@options = options
|
|
12
|
+
commit_id = git_commit_id
|
|
13
|
+
@updated_keys = commit_id.empty? ? { frontend: {} } : find_updated_keys(commit_id)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def process!
|
|
17
|
+
{
|
|
18
|
+
frontend: {
|
|
19
|
+
en: flatten_hash_with_colon(frontend_translations[:en]),
|
|
20
|
+
missing_keys: find_missing_keys
|
|
21
|
+
},
|
|
22
|
+
metadata: {
|
|
23
|
+
repo: options[:repo],
|
|
24
|
+
frontend: options[:frontend],
|
|
25
|
+
strategy: options[:strategy],
|
|
26
|
+
branch: options[:branch],
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def no_new_changes?(payload)
|
|
32
|
+
payload[:frontend][:missing_keys].values.all?(&:empty?)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
def git_commit_id
|
|
38
|
+
`git log --grep="[neeto-translate]" --fixed-strings -n 1 --pretty=format:"%H"`.strip
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def find_updated_keys(commit_id)
|
|
42
|
+
updated_frontend_keys = {}
|
|
43
|
+
frontend_translations[:en].each do |file_path, content|
|
|
44
|
+
relative_path_from_pwd = Pathname.new(file_path).relative_path_from(Pathname.new(Dir.pwd)).to_s
|
|
45
|
+
previous_content = `git show #{commit_id}:#{relative_path_from_pwd}`
|
|
46
|
+
next if previous_content.empty?
|
|
47
|
+
|
|
48
|
+
previous_json = JSON.parse(previous_content)
|
|
49
|
+
flat_previous_json = flatten_hash(previous_json)
|
|
50
|
+
flat_current_json = flatten_hash(content)
|
|
51
|
+
|
|
52
|
+
updated_keys_for_file = flat_current_json.keys.select do |key|
|
|
53
|
+
flat_previous_json.key?(key) && flat_current_json[key] != flat_previous_json[key]
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
next if updated_keys_for_file.empty?
|
|
57
|
+
|
|
58
|
+
relative_path_from_en_dir = Pathname.new(file_path)
|
|
59
|
+
.relative_path_from(Pathname.new(File.expand_path(File.join(options[:frontend], "en")))).to_s
|
|
60
|
+
path_without_ext = relative_path_from_en_dir.chomp(File.extname(relative_path_from_en_dir))
|
|
61
|
+
updated_frontend_keys[path_without_ext] = updated_keys_for_file
|
|
62
|
+
end
|
|
63
|
+
{ frontend: updated_frontend_keys }
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def find_missing_keys
|
|
67
|
+
missing_keys = {}
|
|
68
|
+
base_translations = frontend_translations[:en]
|
|
69
|
+
|
|
70
|
+
options[:default_languages].each do |lang|
|
|
71
|
+
missing_keys[lang] = []
|
|
72
|
+
base_translations.each do |base_file_path, base_content|
|
|
73
|
+
lang_file_path = "#{options[:frontend]}/#{lang}/#{base_file_path}.json"
|
|
74
|
+
|
|
75
|
+
base_keys = flatten_hash(base_content).keys
|
|
76
|
+
lang_keys = File.file?(lang_file_path) ? flatten_hash(load_json(lang_file_path)).keys : []
|
|
77
|
+
|
|
78
|
+
missing_in_file = (base_keys - lang_keys).uniq
|
|
79
|
+
missing_keys[lang].concat(missing_in_file.map { |key| "#{base_file_path}:#{key}" })
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
missing_keys
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def frontend_translations
|
|
87
|
+
return @_frontend_translations if defined?(@_frontend_translations)
|
|
88
|
+
|
|
89
|
+
all_files = Dir.glob("#{options[:frontend]}/en/**/*").select { |path| File.file?(path) }
|
|
90
|
+
@_frontend_translations = { en: {} }
|
|
91
|
+
|
|
92
|
+
all_files.each do |path|
|
|
93
|
+
lang_key = path.gsub("#{options[:frontend]}/en/", "").gsub(".json", "")
|
|
94
|
+
@_frontend_translations[:en][lang_key] = load_json(path)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
@_frontend_translations
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def load_json(file_path)
|
|
101
|
+
return {} unless File.file?(file_path)
|
|
102
|
+
|
|
103
|
+
JSON.parse(File.read(file_path, encoding: "utf-8"))
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def flatten_hash(hash, prefix = nil)
|
|
107
|
+
hash.each_with_object({}) do |(key, value), result|
|
|
108
|
+
current_key = prefix ? "#{prefix}.#{key}" : key.to_s
|
|
109
|
+
if value.is_a?(Hash)
|
|
110
|
+
result.merge!(flatten_hash(value, current_key))
|
|
111
|
+
else
|
|
112
|
+
result[current_key] = value
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def flatten_hash_with_colon(hash)
|
|
118
|
+
hash.flat_map do |file_key, file_content|
|
|
119
|
+
flatten_hash(file_content).map do |translation_key, translation_value|
|
|
120
|
+
["#{file_key}:#{translation_key}", translation_value]
|
|
121
|
+
end
|
|
122
|
+
end.to_h
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative "api"
|
|
4
4
|
require_relative "payload_builder"
|
|
5
|
+
require_relative "next_i18next/payload_builder"
|
|
5
6
|
|
|
6
7
|
module NeetoTranslateCli
|
|
7
8
|
class Translator
|
|
@@ -19,7 +20,9 @@ module NeetoTranslateCli
|
|
|
19
20
|
private
|
|
20
21
|
|
|
21
22
|
def translate!
|
|
22
|
-
payload_builder =
|
|
23
|
+
payload_builder = options[:strategy] == "next-i18next" ?
|
|
24
|
+
NeetoTranslateCli::NextI18next::PayloadBuilder.new(options)
|
|
25
|
+
: PayloadBuilder.new(options)
|
|
23
26
|
payload = payload_builder.process!
|
|
24
27
|
|
|
25
28
|
if payload_builder.no_new_changes?(payload)
|
|
@@ -13,17 +13,17 @@ class NeetoTranslateCli::CLITest < Minitest::Test
|
|
|
13
13
|
def test_start_with_default_options
|
|
14
14
|
cli = NeetoTranslateCli::CLI.new([])
|
|
15
15
|
options = cli.send(:parse_options, [])
|
|
16
|
-
|
|
16
|
+
|
|
17
17
|
assert_equal "app/javascript/src/translations", options[:frontend]
|
|
18
18
|
assert_equal "config/locales", options[:backend]
|
|
19
|
-
assert_equal
|
|
19
|
+
assert_equal ::NeetoTranslateCli::CLI::DEFAULT_LANGUAGES.split(","), options[:default_languages]
|
|
20
20
|
assert_equal @original_git_repo, options[:repo]
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
def test_start_with_custom_frontend_path
|
|
24
24
|
cli = NeetoTranslateCli::CLI.new([])
|
|
25
25
|
options = cli.send(:parse_options, ["--frontend", "custom/frontend/path"])
|
|
26
|
-
|
|
26
|
+
|
|
27
27
|
assert_equal "custom/frontend/path", options[:frontend]
|
|
28
28
|
assert_equal "config/locales", options[:backend]
|
|
29
29
|
end
|
|
@@ -31,7 +31,7 @@ class NeetoTranslateCli::CLITest < Minitest::Test
|
|
|
31
31
|
def test_start_with_custom_backend_path
|
|
32
32
|
cli = NeetoTranslateCli::CLI.new([])
|
|
33
33
|
options = cli.send(:parse_options, ["--backend", "custom/backend/path"])
|
|
34
|
-
|
|
34
|
+
|
|
35
35
|
assert_equal "app/javascript/src/translations", options[:frontend]
|
|
36
36
|
assert_equal "custom/backend/path", options[:backend]
|
|
37
37
|
end
|
|
@@ -39,26 +39,26 @@ class NeetoTranslateCli::CLITest < Minitest::Test
|
|
|
39
39
|
def test_start_with_custom_languages
|
|
40
40
|
cli = NeetoTranslateCli::CLI.new([])
|
|
41
41
|
options = cli.send(:parse_options, ["--defaults", "ja,ko,zh"])
|
|
42
|
-
|
|
42
|
+
|
|
43
43
|
assert_equal %w[ja ko zh], options[:default_languages]
|
|
44
44
|
end
|
|
45
45
|
|
|
46
46
|
def test_start_with_custom_repo
|
|
47
47
|
cli = NeetoTranslateCli::CLI.new([])
|
|
48
48
|
options = cli.send(:parse_options, ["--repo", "neetozone/custom-repo"])
|
|
49
|
-
|
|
49
|
+
|
|
50
50
|
assert_equal "neetozone/custom-repo", options[:repo]
|
|
51
51
|
end
|
|
52
52
|
|
|
53
53
|
def test_start_with_all_custom_options
|
|
54
54
|
cli = NeetoTranslateCli::CLI.new([])
|
|
55
55
|
options = cli.send(:parse_options, [
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
56
|
+
"--frontend", "custom/frontend",
|
|
57
|
+
"--backend", "custom/backend",
|
|
58
|
+
"--defaults", "ja,ko",
|
|
59
|
+
"--repo", "neetozone/test-repo"
|
|
60
|
+
])
|
|
61
|
+
|
|
62
62
|
assert_equal "custom/frontend", options[:frontend]
|
|
63
63
|
assert_equal "custom/backend", options[:backend]
|
|
64
64
|
assert_equal %w[ja ko], options[:default_languages]
|
|
@@ -69,26 +69,26 @@ class NeetoTranslateCli::CLITest < Minitest::Test
|
|
|
69
69
|
ENV["DEFAULT_LANGUAGES"] = "ja,ko,zh"
|
|
70
70
|
cli = NeetoTranslateCli::CLI.new([])
|
|
71
71
|
options = cli.send(:parse_options, [])
|
|
72
|
-
|
|
72
|
+
|
|
73
73
|
assert_equal %w[ja ko zh], options[:default_languages]
|
|
74
74
|
end
|
|
75
75
|
|
|
76
76
|
def test_run_calls_translator
|
|
77
77
|
mock_translator = Minitest::Mock.new
|
|
78
78
|
mock_translator.expect :process!, nil
|
|
79
|
-
|
|
79
|
+
|
|
80
80
|
options = {
|
|
81
81
|
frontend: "test/fixtures/frontend",
|
|
82
82
|
backend: "test/fixtures/backend",
|
|
83
83
|
default_languages: %w[es de],
|
|
84
84
|
repo: "test-repo"
|
|
85
85
|
}
|
|
86
|
-
|
|
86
|
+
|
|
87
87
|
NeetoTranslateCli::Translator.stub :new, mock_translator do
|
|
88
88
|
cli = NeetoTranslateCli::CLI.new([])
|
|
89
89
|
cli.send(:run_with_options, options)
|
|
90
90
|
end
|
|
91
|
-
|
|
91
|
+
|
|
92
92
|
mock_translator.verify
|
|
93
93
|
end
|
|
94
94
|
|
|
@@ -98,4 +98,4 @@ class NeetoTranslateCli::CLITest < Minitest::Test
|
|
|
98
98
|
cli = NeetoTranslateCli::CLI.new(args)
|
|
99
99
|
cli.send(:parse_options, args)
|
|
100
100
|
end
|
|
101
|
-
end
|
|
101
|
+
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: neeto-translate-cli
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Abhay V Ashokan
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2025-
|
|
11
|
+
date: 2025-09-17 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: faraday
|
|
@@ -40,6 +40,7 @@ files:
|
|
|
40
40
|
- exe/neeto-translate-cli
|
|
41
41
|
- lib/neeto_translate_cli/api.rb
|
|
42
42
|
- lib/neeto_translate_cli/cli.rb
|
|
43
|
+
- lib/neeto_translate_cli/next_i18next/payload_builder.rb
|
|
43
44
|
- lib/neeto_translate_cli/payload_builder.rb
|
|
44
45
|
- lib/neeto_translate_cli/translator.rb
|
|
45
46
|
- lib/neeto_translate_cli/version.rb
|