neeto-translate-cli 0.1.1
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 +7 -0
- data/README.md +119 -0
- data/Rakefile +12 -0
- data/bin/console +11 -0
- data/bin/setup +8 -0
- data/exe/neeto-translate-cli +5 -0
- data/lib/neeto_translate_cli/api.rb +31 -0
- data/lib/neeto_translate_cli/cli.rb +44 -0
- data/lib/neeto_translate_cli/payload_builder.rb +119 -0
- data/lib/neeto_translate_cli/translator.rb +33 -0
- data/lib/neeto_translate_cli/version.rb +5 -0
- data/test/fixtures/backend/en.yml +23 -0
- data/test/fixtures/backend/es.yml +15 -0
- data/test/fixtures/backend/jp.yml +18 -0
- data/test/fixtures/frontend/en.json +22 -0
- data/test/fixtures/frontend/es.json +13 -0
- data/test/fixtures/frontend/jp.json +19 -0
- data/test/neeto_translate_cli/payload_builder_test.rb +63 -0
- data/test/test_helper.rb +6 -0
- metadata +79 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 7f02f39e18528cfe0511c15be9a69c5f5b156d0ebb7497a6f68c3fc702d70be8
|
|
4
|
+
data.tar.gz: 306076cb3c777e33171135b221f745175ffcd380be3c94e2daabe6dcd334672f
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: b28d58ccfd9766d8d7f7660cb3e191856bcb5dcbf6d1f38a025cf925746722802d02c84319cbed03930bba33d35fc55633ca2ebb413daf67120f4eb17fc8bdb8
|
|
7
|
+
data.tar.gz: 282c29211bb41d7f32c48aec95491f7a7a22373bf90d4866144d3b950f94eb170461a4b85dce698c4b4c6fa266ea01e5048d7ef2c3c6b729c021e28c7756d6a7
|
data/README.md
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# neeto-translate-cli
|
|
2
|
+
|
|
3
|
+
A Ruby CLI tool that extracts translation files from Rails applications and makes requests to the neeto-translate API for automated translation management.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Installation](#installation)
|
|
8
|
+
- [Usage](#usage)
|
|
9
|
+
- [Development](#development)
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
Install the gem via RubyGems:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
gem install neeto-translate-cli
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Or add it to your Gemfile:
|
|
20
|
+
|
|
21
|
+
```ruby
|
|
22
|
+
gem 'neeto-translate-cli'
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Then run:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
bundle install
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Usage
|
|
32
|
+
|
|
33
|
+
The `neeto-translate-cli` tool helps you manage translations by:
|
|
34
|
+
|
|
35
|
+
1. Extracting translation keys from your Rails application's frontend (JSON) and backend (YAML) locale files
|
|
36
|
+
2. Identifying missing translations across different languages
|
|
37
|
+
3. Detecting updated translation keys since the last automated translation
|
|
38
|
+
4. Sending translation requests to the neeto-translate API
|
|
39
|
+
|
|
40
|
+
### Basic Usage
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
neeto-translate-cli [OPTIONS]
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
#### Command line options
|
|
47
|
+
|
|
48
|
+
**Options:**
|
|
49
|
+
|
|
50
|
+
- `--frontend PATH` - Path to frontend locales (default: `app/javascript/src/translations`)
|
|
51
|
+
- `--backend PATH` - Path to backend locales (default: `config/locales`)
|
|
52
|
+
- `--defaults LANGUAGES` - Comma-separated list of languages to translate (default: `en,fr,de,es`)
|
|
53
|
+
- `--repo NAME` - GitHub repository name (e.g., `neetozone/neeto-cal-web`)
|
|
54
|
+
|
|
55
|
+
### Examples
|
|
56
|
+
|
|
57
|
+
**Custom paths:**
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
neeto-translate-cli --frontend src/locales --backend config/translations
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**Specific languages:**
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
neeto-translate-cli --defaults "fr,de,es,it"
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**Full example:**
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
neeto-translate-cli \
|
|
73
|
+
--frontend app/javascript/src/translations \
|
|
74
|
+
--backend config/locales \
|
|
75
|
+
--defaults "fr,de,es" \
|
|
76
|
+
--repo "neetozone/neeto-cal-web"
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Environment Variables
|
|
80
|
+
|
|
81
|
+
The following environment variables can be configured:
|
|
82
|
+
|
|
83
|
+
- `NEETO_TRANSLATE_URL` - Base URL for the neeto-translate API (default: `https://translate.neeto.com`)
|
|
84
|
+
- `NEETO_TRANSLATE_X_TOKEN` - Authentication token for the API
|
|
85
|
+
- `DEFAULT_LANGUAGES` - Comma-separated list of default languages (default: `en,fr,de,es`)
|
|
86
|
+
|
|
87
|
+
### How It Works
|
|
88
|
+
|
|
89
|
+
1. **Extraction**: The tool reads your English translation files (`en.json` for frontend, `en.yml` for backend)
|
|
90
|
+
2. **Analysis**: It compares with other language files to identify missing translations
|
|
91
|
+
3. **Change Detection**: It detects keys that have been updated since the last automated translation run
|
|
92
|
+
4. **API Request**: Sends a payload to the neeto-translate API with:
|
|
93
|
+
- Current English translations
|
|
94
|
+
- Missing translation keys for each language
|
|
95
|
+
- Updated keys that need re-translation
|
|
96
|
+
- Repository metadata
|
|
97
|
+
|
|
98
|
+
## Development
|
|
99
|
+
|
|
100
|
+
### Setup
|
|
101
|
+
|
|
102
|
+
1. Clone the repository:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
git clone https://github.com/neetozone/neeto-translate.git
|
|
106
|
+
cd neeto-translate/cli
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
2. Install dependencies:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
bundle install
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
3. Run tests:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
bundle exec rake test
|
|
119
|
+
```
|
data/Rakefile
ADDED
data/bin/console
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "bundler/setup"
|
|
5
|
+
require_relative "../lib/neeto_translate_cli/cli"
|
|
6
|
+
|
|
7
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
|
8
|
+
# with your gem easier. You can also use a different console, if you like.
|
|
9
|
+
|
|
10
|
+
require "irb"
|
|
11
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "faraday"
|
|
4
|
+
require "json"
|
|
5
|
+
|
|
6
|
+
module NeetoTranslateCli
|
|
7
|
+
class Api
|
|
8
|
+
def translate!(payload)
|
|
9
|
+
connection.post("/translation") do |request|
|
|
10
|
+
request.body = payload.to_json
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
def connection
|
|
17
|
+
@connection ||= Faraday.new(url: url, headers: headers)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def url
|
|
21
|
+
ENV["NEETO_TRANSLATE_URL"] || CLI::NEETO_TRANSLATE_URL
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def headers
|
|
25
|
+
{
|
|
26
|
+
"Content-Type" => "application/json",
|
|
27
|
+
"NEETO-TRANSLATE-X-TOKEN" => ENV["NEETO_TRANSLATE_X_TOKEN"]
|
|
28
|
+
}
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "translator"
|
|
4
|
+
|
|
5
|
+
require "optparse"
|
|
6
|
+
|
|
7
|
+
module NeetoTranslateCli
|
|
8
|
+
class CLI
|
|
9
|
+
DEFAULT_LANGUAGES = "en,fr,de,es"
|
|
10
|
+
NEETO_TRANSLATE_URL = "https://translate.neeto.com"
|
|
11
|
+
DEFAULT_BACKEND_LOCALE_PATH = "config/locales"
|
|
12
|
+
DEFAULT_FRONTEND_LOCALE_PATH = "app/javascript/src/translations"
|
|
13
|
+
|
|
14
|
+
def self.start(args)
|
|
15
|
+
new(args).run
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def initialize(args)
|
|
19
|
+
@args = args
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def run
|
|
23
|
+
options = {
|
|
24
|
+
frontend: DEFAULT_FRONTEND_LOCALE_PATH,
|
|
25
|
+
backend: DEFAULT_BACKEND_LOCALE_PATH,
|
|
26
|
+
default_languages: (ENV["DEFAULT_LANGUAGES"] || DEFAULT_LANGUAGES).to_s.split(","),
|
|
27
|
+
repo: `git rev-parse --show-toplevel`.split("/").last.strip
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
OptionParser.new do |opts|
|
|
31
|
+
opts.on("--frontend PATH", "Path to frontend locales.") { |path| options[:frontend] = path }
|
|
32
|
+
opts.on("--backend PATH", "Path to backend locales") { |path| options[:backend] = path }
|
|
33
|
+
opts.on("--defaults ARRAY", "An array of comma separated languages to be translated") do |languages|
|
|
34
|
+
options[:default_languages] = languages.to_s.split(",")
|
|
35
|
+
end
|
|
36
|
+
opts.on("--repo NAME", "Name of the GitHub repository (eg: neetozone/neeto-cal-web).") do |path|
|
|
37
|
+
options[:repo] = path
|
|
38
|
+
end
|
|
39
|
+
end.parse!(@args)
|
|
40
|
+
|
|
41
|
+
Translator.new(options).process!
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
require "yaml"
|
|
3
|
+
|
|
4
|
+
module NeetoTranslateCli
|
|
5
|
+
class PayloadBuilder
|
|
6
|
+
COMMIT_AUTHOR = "neetobot"
|
|
7
|
+
|
|
8
|
+
attr_reader :options, :updated_keys
|
|
9
|
+
|
|
10
|
+
def initialize(options)
|
|
11
|
+
@options = options
|
|
12
|
+
commit_id = `git log --author="#{COMMIT_AUTHOR}" --grep="\[neeto-translate\]" -n 1 --pretty=format:"%H"`.strip
|
|
13
|
+
@updated_keys = commit_id.empty? ? { frontend: [], backend: [] } : find_updated_keys(commit_id)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def process!
|
|
17
|
+
{
|
|
18
|
+
frontend: { en: en_json_flattened, missing_keys: find_missing_keys_for(:frontend) },
|
|
19
|
+
backend: { en: en_yml_flattened, missing_keys: find_missing_keys_for(:backend) },
|
|
20
|
+
metadata: {
|
|
21
|
+
repo: options[:repo],
|
|
22
|
+
frontend: options[:frontend],
|
|
23
|
+
backend: options[:backend]
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def no_new_changes?(payload)
|
|
29
|
+
payload[:frontend][:missing_keys].values.all?(&:empty?) && payload[:backend][:missing_keys].values.all?(&:empty?)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def en_json_flattened
|
|
35
|
+
@_en_json_flattened ||= flatten_hash(load_json(File.expand_path("en.json", options[:frontend])))
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def en_yml_flattened
|
|
39
|
+
@_en_yml_flattened ||= flatten_hash(load_yml(File.expand_path("en.yml", options[:backend]))["en"])
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def find_updated_keys(commit_id)
|
|
43
|
+
previous_en_json_content = `git show #{commit_id}:#{options[:frontend]}/en.json`
|
|
44
|
+
updated_frontend_keys = []
|
|
45
|
+
unless previous_en_json_content.empty?
|
|
46
|
+
previous_en_json = JSON.parse(previous_en_json_content)
|
|
47
|
+
flat_previous_en_json = flatten_hash(previous_en_json)
|
|
48
|
+
flat_current_en_json = en_json_flattened
|
|
49
|
+
|
|
50
|
+
updated_frontend_keys = flat_current_en_json.keys.select do |key|
|
|
51
|
+
flat_previous_en_json.key?(key) && flat_current_en_json[key] != flat_previous_en_json[key]
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
previous_en_yml_content = `git show #{commit_id}:#{options[:backend]}/en.yml`
|
|
56
|
+
updated_backend_keys = []
|
|
57
|
+
unless previous_en_yml_content.empty?
|
|
58
|
+
previous_en_yml = YAML.load(previous_en_yml_content, aliases: true)["en"]
|
|
59
|
+
flat_previous_en_yml = flatten_hash(previous_en_yml)
|
|
60
|
+
flat_current_en_yml = en_yml_flattened
|
|
61
|
+
|
|
62
|
+
updated_backend_keys = flat_current_en_yml.keys.select do |key|
|
|
63
|
+
flat_previous_en_yml.key?(key) && flat_current_en_yml[key] != flat_previous_en_yml[key]
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
{ frontend: updated_frontend_keys, backend: updated_backend_keys }
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def find_missing_keys_for(type)
|
|
71
|
+
dir = options[type]
|
|
72
|
+
if type == :frontend
|
|
73
|
+
ext = "json"
|
|
74
|
+
base_keys = en_json_flattened.keys
|
|
75
|
+
else
|
|
76
|
+
ext = "yml"
|
|
77
|
+
base_keys = en_yml_flattened.keys
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
languages.each_with_object({}) do |lang, missing|
|
|
81
|
+
file_path = File.expand_path("#{lang}.#{ext}", dir)
|
|
82
|
+
if File.file?(file_path)
|
|
83
|
+
translations = ext == "json" ? load_json(file_path) : load_yml(file_path)[lang]
|
|
84
|
+
mk = base_keys - flatten_hash(translations).keys
|
|
85
|
+
else
|
|
86
|
+
mk = base_keys
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
missing[lang] = (mk + updated_keys[type]).uniq
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def load_json(file_path)
|
|
94
|
+
JSON.parse(File.read(file_path, encoding: "utf-8"))
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def load_yml(file_path)
|
|
98
|
+
YAML.load_file(file_path, aliases: true)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def flatten_hash(hash, prefix = nil)
|
|
102
|
+
hash.each_with_object({}) do |(key, value), result|
|
|
103
|
+
current_key = prefix ? "#{prefix}.#{key}" : key.to_s
|
|
104
|
+
if value.is_a?(Hash)
|
|
105
|
+
result.merge!(flatten_hash(value, current_key))
|
|
106
|
+
else
|
|
107
|
+
result[current_key] = value
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def languages
|
|
113
|
+
default_languages = options[:default_languages]
|
|
114
|
+
project_languages = Dir.glob(File.join(options[:frontend], "*.json")).map { |f| File.basename(f, ".json") }
|
|
115
|
+
|
|
116
|
+
(default_languages + project_languages).uniq - ["en"]
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "api"
|
|
4
|
+
require_relative "payload_builder"
|
|
5
|
+
|
|
6
|
+
module NeetoTranslateCli
|
|
7
|
+
class Translator
|
|
8
|
+
attr_reader :api, :options, :job_id
|
|
9
|
+
|
|
10
|
+
def initialize(options)
|
|
11
|
+
@api = NeetoTranslateCli::Api.new
|
|
12
|
+
@options = options
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def process!
|
|
16
|
+
translate!
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def translate!
|
|
22
|
+
payload_builder = PayloadBuilder.new(options)
|
|
23
|
+
payload = payload_builder.process!
|
|
24
|
+
|
|
25
|
+
if payload_builder.no_new_changes?(payload)
|
|
26
|
+
puts "Nothing to see here. You are all good."
|
|
27
|
+
else
|
|
28
|
+
response = api.translate! payload
|
|
29
|
+
puts response.body
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
en:
|
|
2
|
+
common:
|
|
3
|
+
success: "Success"
|
|
4
|
+
error: "An error occurred"
|
|
5
|
+
not_found: "Not Found"
|
|
6
|
+
activerecord:
|
|
7
|
+
models:
|
|
8
|
+
user: "User"
|
|
9
|
+
product: "Product"
|
|
10
|
+
attributes:
|
|
11
|
+
user:
|
|
12
|
+
name: "Name"
|
|
13
|
+
email: "Email"
|
|
14
|
+
product:
|
|
15
|
+
name: "Product Name"
|
|
16
|
+
price: "Price"
|
|
17
|
+
mailer:
|
|
18
|
+
welcome:
|
|
19
|
+
subject: "Welcome to our service!"
|
|
20
|
+
body: "Hi %{name}, welcome!"
|
|
21
|
+
password_reset:
|
|
22
|
+
subject: "Password Reset Request"
|
|
23
|
+
body: "Please click the link to reset your password."
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
es:
|
|
2
|
+
common:
|
|
3
|
+
success: "Éxito"
|
|
4
|
+
error: "Ocurrió un error"
|
|
5
|
+
activerecord:
|
|
6
|
+
models:
|
|
7
|
+
user: "Usuario"
|
|
8
|
+
product: "Producto"
|
|
9
|
+
attributes:
|
|
10
|
+
product:
|
|
11
|
+
name: "Nombre del Producto"
|
|
12
|
+
mailer:
|
|
13
|
+
welcome:
|
|
14
|
+
subject: "¡Bienvenido a nuestro servicio!"
|
|
15
|
+
body: "Hola %{name}, ¡bienvenido!"
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
jp:
|
|
2
|
+
common:
|
|
3
|
+
success: "成功"
|
|
4
|
+
not_found: "見つかりません"
|
|
5
|
+
activerecord:
|
|
6
|
+
models:
|
|
7
|
+
user: "ユーザー"
|
|
8
|
+
attributes:
|
|
9
|
+
user:
|
|
10
|
+
name: "名前"
|
|
11
|
+
email: "メールアドレス"
|
|
12
|
+
product:
|
|
13
|
+
name: "製品名"
|
|
14
|
+
price: "価格"
|
|
15
|
+
mailer:
|
|
16
|
+
password_reset:
|
|
17
|
+
subject: "パスワードリセットリクエスト"
|
|
18
|
+
body: "リンクをクリックしてパスワードをリセットしてください。"
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"common": {
|
|
3
|
+
"ok": "OK",
|
|
4
|
+
"cancel": "Cancel",
|
|
5
|
+
"submit": "Submit"
|
|
6
|
+
},
|
|
7
|
+
"login": {
|
|
8
|
+
"title": "Login",
|
|
9
|
+
"email": "Email Address",
|
|
10
|
+
"password": "Password",
|
|
11
|
+
"forgot_password": "Forgot your password?"
|
|
12
|
+
},
|
|
13
|
+
"dashboard": {
|
|
14
|
+
"title": "Dashboard",
|
|
15
|
+
"welcome_message": "Welcome, {{name,anyCase}}",
|
|
16
|
+
"stats": {
|
|
17
|
+
"users": "Users",
|
|
18
|
+
"revenue": "Revenue",
|
|
19
|
+
"orders": "Orders"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"common": {
|
|
3
|
+
"ok": "はい",
|
|
4
|
+
"cancel": "キャンセル"
|
|
5
|
+
},
|
|
6
|
+
"login": {
|
|
7
|
+
"title": "ログイン",
|
|
8
|
+
"email": "メールアドレス",
|
|
9
|
+
"password": "パスワード",
|
|
10
|
+
"forgot_password": "パスワードをお忘れですか?"
|
|
11
|
+
},
|
|
12
|
+
"dashboard": {
|
|
13
|
+
"title": "ダッシュボード",
|
|
14
|
+
"stats": {
|
|
15
|
+
"users": "ユーザー",
|
|
16
|
+
"orders": "注文"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
require "test_helper"
|
|
2
|
+
|
|
3
|
+
class NeetoTranslateCli::PayloadBuilderTest < Minitest::Test
|
|
4
|
+
def setup
|
|
5
|
+
@options = {
|
|
6
|
+
frontend: "test/fixtures/frontend",
|
|
7
|
+
backend: "test/fixtures/backend",
|
|
8
|
+
default_languages: %w[es de]
|
|
9
|
+
}
|
|
10
|
+
@payload_builder = NeetoTranslateCli::PayloadBuilder.new(@options)
|
|
11
|
+
@payload = @payload_builder.process!
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def test_correct_frontend_payload_is_constructured
|
|
15
|
+
en_json_path = File.expand_path("en.json", @options[:frontend])
|
|
16
|
+
en_json = JSON.parse(File.read(en_json_path, encoding: "utf-8"))
|
|
17
|
+
es_missing_keys = [
|
|
18
|
+
"common.cancel",
|
|
19
|
+
"dashboard.stats.orders",
|
|
20
|
+
"dashboard.stats.revenue",
|
|
21
|
+
"dashboard.stats.users",
|
|
22
|
+
"login.email",
|
|
23
|
+
"login.forgot_password",
|
|
24
|
+
"login.password"
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
assert @payload[:frontend]
|
|
28
|
+
assert_equal flatten_hash(en_json), @payload[:frontend][:en]
|
|
29
|
+
assert_equal %w[es de jp], @payload_builder.send(:languages)
|
|
30
|
+
assert_equal es_missing_keys, @payload.dig(:frontend, :missing_keys, "es").sort
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def test_correct_backend_payload_is_constructured
|
|
34
|
+
en_yml_path = File.expand_path("en.yml", @options[:backend])
|
|
35
|
+
en_yml = YAML.load_file(en_yml_path, aliases: true)["en"]
|
|
36
|
+
es_missing_keys = [
|
|
37
|
+
"activerecord.attributes.product.price",
|
|
38
|
+
"activerecord.attributes.user.email",
|
|
39
|
+
"activerecord.attributes.user.name",
|
|
40
|
+
"common.not_found",
|
|
41
|
+
"mailer.password_reset.body",
|
|
42
|
+
"mailer.password_reset.subject"
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
assert @payload[:backend]
|
|
46
|
+
assert_equal flatten_hash(en_yml), @payload[:backend][:en]
|
|
47
|
+
assert_equal %w[es de jp], @payload_builder.send(:languages)
|
|
48
|
+
assert_equal es_missing_keys, @payload.dig(:backend, :missing_keys, "es").sort
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
def flatten_hash(hash, prefix = nil)
|
|
54
|
+
hash.each_with_object({}) do |(key, value), result|
|
|
55
|
+
current_key = prefix ? "#{prefix}.#{key}" : key.to_s
|
|
56
|
+
if value.is_a?(Hash)
|
|
57
|
+
result.merge!(flatten_hash(value, current_key))
|
|
58
|
+
else
|
|
59
|
+
result[current_key] = value
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: neeto-translate-cli
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Abhay V Ashokan
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: exe
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2025-07-09 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: faraday
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '0'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '0'
|
|
27
|
+
description: neeto-translate CLI tool to extract translation files and make requests
|
|
28
|
+
to the neeto-translate API
|
|
29
|
+
email:
|
|
30
|
+
- abhay.ashokan@bigbinary.com
|
|
31
|
+
executables:
|
|
32
|
+
- neeto-translate-cli
|
|
33
|
+
extensions: []
|
|
34
|
+
extra_rdoc_files: []
|
|
35
|
+
files:
|
|
36
|
+
- README.md
|
|
37
|
+
- Rakefile
|
|
38
|
+
- bin/console
|
|
39
|
+
- bin/setup
|
|
40
|
+
- exe/neeto-translate-cli
|
|
41
|
+
- lib/neeto_translate_cli/api.rb
|
|
42
|
+
- lib/neeto_translate_cli/cli.rb
|
|
43
|
+
- lib/neeto_translate_cli/payload_builder.rb
|
|
44
|
+
- lib/neeto_translate_cli/translator.rb
|
|
45
|
+
- lib/neeto_translate_cli/version.rb
|
|
46
|
+
- test/fixtures/backend/en.yml
|
|
47
|
+
- test/fixtures/backend/es.yml
|
|
48
|
+
- test/fixtures/backend/jp.yml
|
|
49
|
+
- test/fixtures/frontend/en.json
|
|
50
|
+
- test/fixtures/frontend/es.json
|
|
51
|
+
- test/fixtures/frontend/jp.json
|
|
52
|
+
- test/neeto_translate_cli/payload_builder_test.rb
|
|
53
|
+
- test/test_helper.rb
|
|
54
|
+
homepage: https://github.com/neetozone/neeto-translate
|
|
55
|
+
licenses: []
|
|
56
|
+
metadata:
|
|
57
|
+
homepage_uri: https://github.com/neetozone/neeto-translate
|
|
58
|
+
source_code_uri: https://github.com/neetozone/neeto-translate
|
|
59
|
+
post_install_message:
|
|
60
|
+
rdoc_options: []
|
|
61
|
+
require_paths:
|
|
62
|
+
- lib
|
|
63
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - ">="
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: 3.1.0
|
|
68
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
69
|
+
requirements:
|
|
70
|
+
- - ">="
|
|
71
|
+
- !ruby/object:Gem::Version
|
|
72
|
+
version: '0'
|
|
73
|
+
requirements: []
|
|
74
|
+
rubygems_version: 3.5.16
|
|
75
|
+
signing_key:
|
|
76
|
+
specification_version: 4
|
|
77
|
+
summary: neeto-translate CLI tool to extract translation files and make requests to
|
|
78
|
+
the neeto-translate API
|
|
79
|
+
test_files: []
|