rails_translation_manager 1.0.0 → 1.1.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/CHANGELOG.md +4 -0
- data/Jenkinsfile +3 -1
- data/lib/rails_translation_manager/importer.rb +32 -14
- data/lib/rails_translation_manager/version.rb +1 -1
- data/lib/rails_translation_manager.rb +2 -4
- data/lib/tasks/translation.rake +24 -44
- data/spec/locales/importer/fr.csv +8 -0
- data/spec/rails_translation_manager/importer_spec.rb +81 -0
- data/spec/tasks/translation_spec.rb +102 -17
- data/test/rails_translation_manager/yaml_writer_test.rb +2 -0
- metadata +6 -10
- data/lib/rails_translation_manager/stealer.rb +0 -85
- data/lib/rails_translation_manager/validator.rb +0 -92
- data/test/rails_translation_manager/importer_test.rb +0 -132
- data/test/rails_translation_manager/stealer_test.rb +0 -251
- data/test/rails_translation_manager/validator_test.rb +0 -51
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0b6a096ee29746cead2529ea0cb83660c0e0217b710e085790969f203f246871
|
4
|
+
data.tar.gz: 3c13cb54190c17f1382246827c120cf928e7763a9de4b4724ec36ffe6005fc62
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 37abc2c32de6245dc3d1cd57da56f14ab3c68f88f9b3802a65735d3c36c02984f823d45ff177729daad6b2234929d03535ca28239a4188889a14be60f35875df
|
7
|
+
data.tar.gz: 73f08794d826203ba2be01695751d742bd7a546dee1bf9f8946c98cf57350a48651addd5eef53cfcd9b23303900d6efe0dcd41fb0ae81d67ba060ed851b5061a
|
data/CHANGELOG.md
CHANGED
data/Jenkinsfile
CHANGED
@@ -5,36 +5,39 @@ require_relative "yaml_writer"
|
|
5
5
|
class RailsTranslationManager::Importer
|
6
6
|
include YAMLWriter
|
7
7
|
|
8
|
-
|
9
|
-
|
8
|
+
attr_reader :locale, :csv_path, :import_directory, :multiple_files_per_language
|
9
|
+
|
10
|
+
def initialize(locale:, csv_path:, import_directory:, multiple_files_per_language:)
|
10
11
|
@locale = locale
|
12
|
+
@csv_path = csv_path
|
11
13
|
@import_directory = import_directory
|
14
|
+
@multiple_files_per_language = multiple_files_per_language
|
12
15
|
end
|
13
16
|
|
14
17
|
def import
|
15
|
-
csv = CSV.read(
|
16
|
-
|
17
|
-
csv
|
18
|
+
csv = CSV.read(csv_path, headers: true, header_converters: :downcase)
|
19
|
+
|
20
|
+
multiple_files_per_language ? import_csv_into_multiple_files(csv) : import_csv(csv)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def import_csv(csv, import_yml_path = File.join(import_directory, "#{locale}.yml"))
|
26
|
+
data = csv.each_with_object({}) do |row, hash|
|
18
27
|
key = row["key"]
|
19
28
|
key_parts = key.split(".")
|
20
29
|
if key_parts.length > 1
|
21
|
-
leaf_node = (
|
30
|
+
leaf_node = (hash[key_parts.first] ||= {})
|
22
31
|
key_parts[1..-2].each do |part|
|
23
32
|
leaf_node = (leaf_node[part] ||= {})
|
24
33
|
end
|
25
34
|
leaf_node[key_parts.last] = parse_translation(row["translation"])
|
26
35
|
else
|
27
|
-
|
36
|
+
hash[key_parts.first] = parse_translation(row["translation"])
|
28
37
|
end
|
29
38
|
end
|
30
39
|
|
31
|
-
write_yaml(import_yml_path, {
|
32
|
-
end
|
33
|
-
|
34
|
-
private
|
35
|
-
|
36
|
-
def import_yml_path
|
37
|
-
File.join(@import_directory, "#{@locale}.yml")
|
40
|
+
write_yaml(import_yml_path, { locale.to_s => data })
|
38
41
|
end
|
39
42
|
|
40
43
|
def parse_translation(translation)
|
@@ -55,4 +58,19 @@ class RailsTranslationManager::Importer
|
|
55
58
|
translation
|
56
59
|
end
|
57
60
|
end
|
61
|
+
|
62
|
+
def import_csv_into_multiple_files(csv)
|
63
|
+
group_csv_by_file(csv).each do |group|
|
64
|
+
language_dir = File.join(import_directory, locale)
|
65
|
+
|
66
|
+
Dir.mkdir(language_dir) unless Dir.exists?(language_dir)
|
67
|
+
|
68
|
+
import_yml_path = File.join(import_directory, locale, "#{group[0]}.yml")
|
69
|
+
import_csv(group[1], import_yml_path)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def group_csv_by_file(csv)
|
74
|
+
csv.group_by { |row| row["key"].split(".").first }
|
75
|
+
end
|
58
76
|
end
|
@@ -13,12 +13,10 @@ require "rails_translation_manager/locale_checker/incompatible_plurals"
|
|
13
13
|
require "rails_translation_manager/locale_checker/all_locales"
|
14
14
|
require "rails_translation_manager/locale_checker"
|
15
15
|
require "rails_translation_manager/cleaner"
|
16
|
+
require "rails_translation_manager/exporter"
|
17
|
+
require "rails_translation_manager/importer"
|
16
18
|
|
17
19
|
module RailsTranslationManager
|
18
|
-
autoload :Exporter, "rails_translation_manager/exporter"
|
19
|
-
autoload :Importer, "rails_translation_manager/importer"
|
20
|
-
autoload :Stealer, "rails_translation_manager/stealer"
|
21
|
-
|
22
20
|
rails_i18n_path = Gem::Specification.find_by_name("rails-i18n").gem_dir
|
23
21
|
rails_translation_manager = Gem::Specification.find_by_name("rails_translation_manager").gem_dir
|
24
22
|
|
data/lib/tasks/translation.rake
CHANGED
@@ -4,14 +4,6 @@ require_relative "../tasks/translation_helper"
|
|
4
4
|
|
5
5
|
namespace :translation do
|
6
6
|
|
7
|
-
desc "Regenerate all locales from the EN locale - run this after adding keys"
|
8
|
-
task(:regenerate, [:directory] => [:environment]) do |t, args|
|
9
|
-
directory = args[:directory] || "tmp/locale_csv"
|
10
|
-
|
11
|
-
Rake::Task["translation:export:all"].invoke(directory)
|
12
|
-
Rake::Task["translation:import:all"].invoke(directory)
|
13
|
-
end
|
14
|
-
|
15
7
|
desc "Export a specific locale to CSV."
|
16
8
|
task :export, [:directory, :base_locale, :target_locale] => [:environment] do |t, args|
|
17
9
|
FileUtils.mkdir_p(args[:directory]) unless File.exist?(args[:directory])
|
@@ -38,50 +30,38 @@ namespace :translation do
|
|
38
30
|
end
|
39
31
|
|
40
32
|
desc "Import a specific locale CSV to YAML within the app."
|
41
|
-
task :import, [:
|
42
|
-
|
33
|
+
task :import, [:csv_path, :multiple_files_per_language] => [:environment] do |t, args|
|
34
|
+
import_dir = Rails.root.join("config", "locales")
|
35
|
+
csv_path = args[:csv_path]
|
36
|
+
|
37
|
+
importer = RailsTranslationManager::Importer.new(
|
38
|
+
locale: File.basename(args[:csv_path], ".csv"),
|
39
|
+
csv_path: csv_path,
|
40
|
+
import_directory: Rails.root.join("config", "locales"),
|
41
|
+
multiple_files_per_language: args[:multiple_files_per_language] || false
|
42
|
+
)
|
43
43
|
importer.import
|
44
|
+
|
45
|
+
puts "Imported CSV from: #{csv_path} to #{import_dir}"
|
44
46
|
end
|
45
47
|
|
46
48
|
namespace :import do
|
47
49
|
desc "Import all locale CSV files to YAML within the app."
|
48
|
-
task :all, [:
|
49
|
-
directory = args[:
|
50
|
-
|
51
|
-
|
52
|
-
|
50
|
+
task :all, [:csv_directory, :multiple_files_per_language] => [:environment] do |t, args|
|
51
|
+
directory = args[:csv_directory] || "tmp/locale_csv"
|
52
|
+
import_dir = Rails.root.join("config", "locales")
|
53
|
+
|
54
|
+
Dir[Rails.root.join(directory, "*.csv")].each do |csv_path|
|
55
|
+
importer = RailsTranslationManager::Importer.new(
|
56
|
+
locale: File.basename(csv_path, ".csv"),
|
57
|
+
csv_path: csv_path,
|
58
|
+
import_directory: import_dir,
|
59
|
+
multiple_files_per_language: args[:multiple_files_per_language] || false
|
60
|
+
)
|
53
61
|
importer.import
|
54
62
|
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
desc "Check translation files for errors"
|
59
|
-
task :validate do
|
60
|
-
require 'rails_translation_manager/validator'
|
61
|
-
logger = Logger.new(STDOUT)
|
62
|
-
validator = RailsTranslationManager::Validator.new(Rails.root.join('config', 'locales'), logger)
|
63
|
-
errors = validator.check!
|
64
|
-
if errors.any?
|
65
|
-
puts "Found #{errors.size} errors:"
|
66
|
-
puts errors.map(&:to_s).join("\n")
|
67
|
-
else
|
68
|
-
puts "Success! No unexpected interpolation keys found."
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
desc "Import and convert a locale file from another app."
|
73
|
-
task :steal, [:locale, :source_app_path, :mapping_file_path] do |t, args|
|
74
|
-
stealer = RailsTranslationManager::Stealer.new(args[:locale], args[:source_app_path], args[:mapping_file_path], Rails.root.join('config', 'locales'))
|
75
|
-
stealer.steal_locale
|
76
|
-
end
|
77
63
|
|
78
|
-
|
79
|
-
desc "Import and convert all locale files from another app."
|
80
|
-
task :all, [:source_app_path, :mapping_file_path] => [:environment] do |t, args|
|
81
|
-
I18n.available_locales.reject { |l| l == :en }.each do |locale|
|
82
|
-
stealer = RailsTranslationManager::Stealer.new(locale.to_s, args[:source_app_path], args[:mapping_file_path], Rails.root.join('config', 'locales'))
|
83
|
-
stealer.steal_locale
|
84
|
-
end
|
64
|
+
puts "Imported all CSVs from: #{directory} to #{import_dir}"
|
85
65
|
end
|
86
66
|
end
|
87
67
|
|
@@ -0,0 +1,8 @@
|
|
1
|
+
key,source,translation
|
2
|
+
world_location.type.country,Country,Pays
|
3
|
+
world_location.fruit,"[Apples, Bananas, Pears]","[Pommes, Bananes, Poires]"
|
4
|
+
world_location.things,nil,nil
|
5
|
+
world_location.sentiment,:whatever,:bof
|
6
|
+
shared.price,123,123
|
7
|
+
shared.key1,is true,true
|
8
|
+
shared.key2,is false,false
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "tmpdir"
|
3
|
+
|
4
|
+
RSpec.describe RailsTranslationManager::Importer do
|
5
|
+
let(:import_directory) { Dir.mktmpdir }
|
6
|
+
|
7
|
+
context "when there is one locale file per language" do
|
8
|
+
let(:yaml_translation_data) { YAML.load_file(import_directory + "/fr.yml")["fr"] }
|
9
|
+
|
10
|
+
before do
|
11
|
+
importer = described_class.new(
|
12
|
+
locale: "fr",
|
13
|
+
csv_path: "spec/locales/importer/fr.csv",
|
14
|
+
import_directory: import_directory,
|
15
|
+
multiple_files_per_language: false
|
16
|
+
)
|
17
|
+
importer.import
|
18
|
+
end
|
19
|
+
|
20
|
+
it "creates one YAML file per language" do
|
21
|
+
expect(File).to exist(import_directory + "/fr.yml")
|
22
|
+
end
|
23
|
+
|
24
|
+
it "imports nested locales" do
|
25
|
+
expected = { "type" => { "country" => "Pays" } }
|
26
|
+
expect(yaml_translation_data).to include("world_location" => hash_including(expected))
|
27
|
+
end
|
28
|
+
|
29
|
+
it "imports arrays from CSV as arrays" do
|
30
|
+
expected = { "fruit" => ["Pommes", "Bananes", "Poires"] }
|
31
|
+
expect(yaml_translation_data).to include("world_location" => hash_including(expected))
|
32
|
+
end
|
33
|
+
|
34
|
+
it "imports string 'nil' as nil" do
|
35
|
+
expected = { "things" => nil }
|
36
|
+
expect(yaml_translation_data).to include("world_location" => hash_including(expected))
|
37
|
+
end
|
38
|
+
|
39
|
+
it "imports string ':thing' as symbol" do
|
40
|
+
expected = { "sentiment" => :bof }
|
41
|
+
expect(yaml_translation_data).to include("world_location" => hash_including(expected))
|
42
|
+
end
|
43
|
+
|
44
|
+
it "imports integer strings as integers" do
|
45
|
+
expected = { "price" => 123 }
|
46
|
+
expect(yaml_translation_data).to include("shared" => hash_including(expected))
|
47
|
+
end
|
48
|
+
|
49
|
+
it "imports boolean values as booleans, not strings" do
|
50
|
+
expected = { "key1" => true, "key2" => false }
|
51
|
+
expect(yaml_translation_data).to include("shared" => hash_including(expected))
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context "when there are multiple files per locale" do
|
56
|
+
before do
|
57
|
+
importer = described_class.new(
|
58
|
+
locale: "fr",
|
59
|
+
csv_path: "spec/locales/importer/fr.csv",
|
60
|
+
import_directory: import_directory,
|
61
|
+
multiple_files_per_language: true
|
62
|
+
)
|
63
|
+
importer.import
|
64
|
+
end
|
65
|
+
|
66
|
+
it "creates multiple YAML files per language in the language's directory" do
|
67
|
+
expect(File).to exist(import_directory + "/fr/world_location.yml")
|
68
|
+
.and exist(import_directory + "/fr/shared.yml")
|
69
|
+
end
|
70
|
+
|
71
|
+
it "imports only 'world_location' locales to the relevant file" do
|
72
|
+
yaml_translation_data = YAML.load_file(import_directory + "/fr/world_location.yml")["fr"]
|
73
|
+
expect(yaml_translation_data).to match("world_location" => anything)
|
74
|
+
end
|
75
|
+
|
76
|
+
it "imports only 'shared' locales to the relevant file" do
|
77
|
+
yaml_translation_data = YAML.load_file(import_directory + "/fr/shared.yml")["fr"]
|
78
|
+
expect(yaml_translation_data).to match("shared" => anything)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -1,38 +1,123 @@
|
|
1
|
-
require
|
2
|
-
require_relative
|
1
|
+
require "spec_helper"
|
2
|
+
require_relative "../../spec/support/tasks"
|
3
3
|
|
4
|
-
describe
|
4
|
+
describe "rake tasks" do
|
5
5
|
before do
|
6
6
|
fake_rails = double()
|
7
|
-
fake_rails.stub(:root) { Pathname.new(
|
7
|
+
fake_rails.stub(:root) { Pathname.new("spec") }
|
8
8
|
stub_const("Rails", fake_rails)
|
9
9
|
end
|
10
10
|
|
11
|
-
describe
|
12
|
-
let(:task) { Rake::Task["translation:
|
13
|
-
|
14
|
-
|
15
|
-
|
11
|
+
describe "translation:import", type: :task do
|
12
|
+
let(:task) { Rake::Task["translation:import"] }
|
13
|
+
let(:csv_path) { "path/to/import/fr.csv" }
|
14
|
+
let!(:importer_instance) { stub_importer }
|
15
|
+
|
16
|
+
it "outputs to stdout" do
|
17
|
+
expect { task.execute(csv_path: csv_path) }
|
18
|
+
.to output("Imported CSV from: #{csv_path} to #{Rails.root.join("config", "locales")}\n")
|
19
|
+
.to_stdout
|
20
|
+
end
|
21
|
+
|
22
|
+
it "calls the Importer class with the csv and import paths" do
|
23
|
+
task.execute(csv_path: csv_path)
|
24
|
+
|
25
|
+
expect(RailsTranslationManager::Importer)
|
26
|
+
.to have_received(:new)
|
27
|
+
.with(locale: "fr",
|
28
|
+
csv_path: csv_path,
|
29
|
+
import_directory: Rails.root.join("config", "locales"),
|
30
|
+
multiple_files_per_language: false)
|
31
|
+
expect(importer_instance).to have_received(:import)
|
16
32
|
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "translation:import:all", type: :task do
|
36
|
+
let(:task) { Rake::Task["translation:import:all"] }
|
37
|
+
let(:csv_directory) { "locales/importer" }
|
38
|
+
let!(:importer_instance) { stub_importer }
|
39
|
+
|
40
|
+
it "outputs to stdout" do
|
41
|
+
expect { task.execute(csv_directory: csv_directory) }
|
42
|
+
.to output("Imported all CSVs from: #{csv_directory} to #{Rails.root.join("config", "locales")}\n")
|
43
|
+
.to_stdout
|
44
|
+
end
|
45
|
+
|
46
|
+
it "calls the importer class for each target path" do
|
47
|
+
task.execute(csv_directory: csv_directory, multiple_files_per_language: true)
|
48
|
+
|
49
|
+
expect(RailsTranslationManager::Importer)
|
50
|
+
.to have_received(:new)
|
51
|
+
.with(locale: "fr",
|
52
|
+
csv_path: "spec/locales/importer/fr.csv",
|
53
|
+
import_directory: Rails.root.join("config", "locales"),
|
54
|
+
multiple_files_per_language: true)
|
55
|
+
expect(importer_instance).to have_received(:import)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe "translation:add_missing", type: :task do
|
60
|
+
let(:task) { Rake::Task["translation:add_missing"] }
|
61
|
+
let!(:cleaner_instance) { stub_cleaner }
|
17
62
|
|
18
|
-
|
63
|
+
before do
|
19
64
|
allow(I18n::Tasks::CLI).to receive(:start)
|
65
|
+
end
|
66
|
+
|
67
|
+
it "triggers Cleaner and allows to receive the right arguments" do
|
20
68
|
task.execute
|
21
|
-
expect(
|
69
|
+
expect(RailsTranslationManager::Cleaner)
|
70
|
+
.to have_received(:new)
|
71
|
+
.with(Rails.root.join("config", "locales"))
|
72
|
+
expect(cleaner_instance).to have_received(:clean)
|
73
|
+
end
|
74
|
+
|
75
|
+
it "triggers i18n task and allows to receive the right arguments" do
|
76
|
+
task.execute(locale: "fr")
|
77
|
+
expect(I18n::Tasks::CLI).to have_received(:start).with(
|
78
|
+
["add-missing", "--nil-value", ["-l", "fr"]]
|
79
|
+
)
|
22
80
|
end
|
23
81
|
end
|
24
82
|
|
25
|
-
describe
|
83
|
+
describe "translation:normalize", type: :task do
|
26
84
|
let(:task) { Rake::Task["translation:normalize"] }
|
27
|
-
|
28
|
-
it 'is executed' do
|
29
|
-
expect { task.execute }.to_not output.to_stdout
|
30
|
-
end
|
85
|
+
let!(:cleaner_instance) { stub_cleaner }
|
31
86
|
|
32
|
-
|
87
|
+
before do
|
33
88
|
allow(I18n::Tasks::CLI).to receive(:start)
|
89
|
+
end
|
90
|
+
|
91
|
+
it "triggers Cleaner and allows to receive the right arguments" do
|
92
|
+
task.execute(locale_directory: "config/locales")
|
93
|
+
expect(RailsTranslationManager::Cleaner)
|
94
|
+
.to have_received(:new)
|
95
|
+
.with(Rails.root.join("config", "locales"))
|
96
|
+
expect(cleaner_instance).to have_received(:clean)
|
97
|
+
end
|
98
|
+
|
99
|
+
it "triggers i18n task and allows to receive the right arguments" do
|
34
100
|
task.execute
|
35
101
|
expect(I18n::Tasks::CLI).to have_received(:start).with(["normalize"])
|
36
102
|
end
|
37
103
|
end
|
104
|
+
|
105
|
+
def stub_importer
|
106
|
+
importer_instance = instance_double(RailsTranslationManager::Importer)
|
107
|
+
allow(RailsTranslationManager::Importer).to receive(:new)
|
108
|
+
.and_return(importer_instance)
|
109
|
+
allow(importer_instance).to receive(:import)
|
110
|
+
|
111
|
+
importer_instance
|
112
|
+
end
|
113
|
+
|
114
|
+
def stub_cleaner
|
115
|
+
cleaner_instance = instance_double(RailsTranslationManager::Cleaner)
|
116
|
+
allow(RailsTranslationManager::Cleaner)
|
117
|
+
.to receive(:new)
|
118
|
+
.and_return(cleaner_instance)
|
119
|
+
allow(cleaner_instance).to receive(:clean)
|
120
|
+
|
121
|
+
cleaner_instance
|
122
|
+
end
|
38
123
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rails_translation_manager
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Edd Sowden
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-11-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails-i18n
|
@@ -153,8 +153,6 @@ files:
|
|
153
153
|
- lib/rails_translation_manager/locale_checker/missing_foreign_locales.rb
|
154
154
|
- lib/rails_translation_manager/locale_checker/plural_forms.rb
|
155
155
|
- lib/rails_translation_manager/railtie.rb
|
156
|
-
- lib/rails_translation_manager/stealer.rb
|
157
|
-
- lib/rails_translation_manager/validator.rb
|
158
156
|
- lib/rails_translation_manager/version.rb
|
159
157
|
- lib/rails_translation_manager/yaml_writer.rb
|
160
158
|
- lib/tasks/translation.rake
|
@@ -162,11 +160,13 @@ files:
|
|
162
160
|
- rails_translation_manager.gemspec
|
163
161
|
- spec/locales/cleaner/clean.yml
|
164
162
|
- spec/locales/cleaner/with_whitespace.yml
|
163
|
+
- spec/locales/importer/fr.csv
|
165
164
|
- spec/locales/in_sync/cy/browse.yml
|
166
165
|
- spec/locales/in_sync/en/browse.yml
|
167
166
|
- spec/locales/out_of_sync/cy.yml
|
168
167
|
- spec/locales/out_of_sync/en.yml
|
169
168
|
- spec/rails_translation_manager/cleaner_spec.rb
|
169
|
+
- spec/rails_translation_manager/importer_spec.rb
|
170
170
|
- spec/rails_translation_manager/locale_checker/all_locales_spec.rb
|
171
171
|
- spec/rails_translation_manager/locale_checker/incompatible_plurals_spec.rb
|
172
172
|
- spec/rails_translation_manager/locale_checker/locale_checker_helper_spec.rb
|
@@ -178,9 +178,6 @@ files:
|
|
178
178
|
- spec/support/tasks.rb
|
179
179
|
- spec/tasks/translation_spec.rb
|
180
180
|
- test/rails_translation_manager/exporter_test.rb
|
181
|
-
- test/rails_translation_manager/importer_test.rb
|
182
|
-
- test/rails_translation_manager/stealer_test.rb
|
183
|
-
- test/rails_translation_manager/validator_test.rb
|
184
181
|
- test/rails_translation_manager/yaml_writer_test.rb
|
185
182
|
- test/test_helper.rb
|
186
183
|
- tmp/.gitkeep
|
@@ -210,11 +207,13 @@ summary: Tasks to manage translation files
|
|
210
207
|
test_files:
|
211
208
|
- spec/locales/cleaner/clean.yml
|
212
209
|
- spec/locales/cleaner/with_whitespace.yml
|
210
|
+
- spec/locales/importer/fr.csv
|
213
211
|
- spec/locales/in_sync/cy/browse.yml
|
214
212
|
- spec/locales/in_sync/en/browse.yml
|
215
213
|
- spec/locales/out_of_sync/cy.yml
|
216
214
|
- spec/locales/out_of_sync/en.yml
|
217
215
|
- spec/rails_translation_manager/cleaner_spec.rb
|
216
|
+
- spec/rails_translation_manager/importer_spec.rb
|
218
217
|
- spec/rails_translation_manager/locale_checker/all_locales_spec.rb
|
219
218
|
- spec/rails_translation_manager/locale_checker/incompatible_plurals_spec.rb
|
220
219
|
- spec/rails_translation_manager/locale_checker/locale_checker_helper_spec.rb
|
@@ -226,8 +225,5 @@ test_files:
|
|
226
225
|
- spec/support/tasks.rb
|
227
226
|
- spec/tasks/translation_spec.rb
|
228
227
|
- test/rails_translation_manager/exporter_test.rb
|
229
|
-
- test/rails_translation_manager/importer_test.rb
|
230
|
-
- test/rails_translation_manager/stealer_test.rb
|
231
|
-
- test/rails_translation_manager/validator_test.rb
|
232
228
|
- test/rails_translation_manager/yaml_writer_test.rb
|
233
229
|
- test/test_helper.rb
|
@@ -1,85 +0,0 @@
|
|
1
|
-
require "yaml"
|
2
|
-
require "i18n"
|
3
|
-
require_relative "yaml_writer"
|
4
|
-
|
5
|
-
class RailsTranslationManager::Stealer
|
6
|
-
include YAMLWriter
|
7
|
-
|
8
|
-
# locale is the locale name as a string.
|
9
|
-
# source_app_path is the path to the root of the app to steal from.
|
10
|
-
# mapping_file_path is the path to a YAML file mapping translation keys in
|
11
|
-
# the source app to those in the target app. For example:
|
12
|
-
# document.type: content_item.format
|
13
|
-
# document.published: content_item.metadata.published
|
14
|
-
# which will import everything under "document.type" and "document.published"
|
15
|
-
# in the source app, and write it to "content_item.format" and
|
16
|
-
# "content_item.metadata.published" in the target app.
|
17
|
-
# locales_path is the path to the locale files to output, which is usually
|
18
|
-
# Rails.root.join('config/locales').
|
19
|
-
# The process will preserve data already in the output file if it is not
|
20
|
-
# referenced in the mapping, but will always override data belonging to keys
|
21
|
-
# that are in the mapping.
|
22
|
-
def initialize(locale, source_app_path, mapping_file_path, locales_path)
|
23
|
-
@locale = locale
|
24
|
-
@source_app_path = source_app_path
|
25
|
-
@mapping_file_path = mapping_file_path
|
26
|
-
@locales_path = locales_path
|
27
|
-
end
|
28
|
-
|
29
|
-
def steal_locale
|
30
|
-
target_data = convert_locale(get_target_data)
|
31
|
-
write_yaml(target_locale_path, target_data)
|
32
|
-
end
|
33
|
-
|
34
|
-
def convert_locale(target_data)
|
35
|
-
mapping_data.each do |source, target|
|
36
|
-
data = source_data[@locale]
|
37
|
-
source.split('.').each { |key| data = data.fetch(key, {}) }
|
38
|
-
set_recursive(target_data[@locale], target.split("."), data)
|
39
|
-
end
|
40
|
-
target_data
|
41
|
-
end
|
42
|
-
|
43
|
-
private
|
44
|
-
|
45
|
-
def set_recursive(hash, keys, data)
|
46
|
-
if keys.empty?
|
47
|
-
data
|
48
|
-
else
|
49
|
-
key = keys.shift
|
50
|
-
hash.tap do |h|
|
51
|
-
h.merge!({ key => set_recursive(hash.fetch(key, {}), keys, data)})
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
def source_locale_path
|
57
|
-
File.join(@source_app_path, 'config', 'locales', "#{@locale}.yml")
|
58
|
-
end
|
59
|
-
|
60
|
-
def source_data
|
61
|
-
@source_data ||= YAML.load_file(source_locale_path)
|
62
|
-
end
|
63
|
-
|
64
|
-
def target_locale_path
|
65
|
-
File.join(@locales_path, "#{@locale}.yml")
|
66
|
-
end
|
67
|
-
|
68
|
-
def default_target_data
|
69
|
-
{ @locale => {} }
|
70
|
-
end
|
71
|
-
|
72
|
-
def get_target_data
|
73
|
-
if File.exist?(target_locale_path)
|
74
|
-
YAML.load_file(target_locale_path) || default_target_data
|
75
|
-
else
|
76
|
-
default_target_data
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
def mapping_data
|
81
|
-
@mapping_data ||= YAML.load_file(@mapping_file_path)
|
82
|
-
end
|
83
|
-
|
84
|
-
end
|
85
|
-
|
@@ -1,92 +0,0 @@
|
|
1
|
-
require 'yaml'
|
2
|
-
require 'logger'
|
3
|
-
|
4
|
-
class RailsTranslationManager::Validator
|
5
|
-
def initialize(translation_file_path, logger = Logger.new(nil))
|
6
|
-
@translation_file_path = translation_file_path
|
7
|
-
@logger = logger
|
8
|
-
end
|
9
|
-
|
10
|
-
def check!
|
11
|
-
@logger.info "Checking translation files in '#{@translation_file_path}' for unexpected interpolation keys"
|
12
|
-
@logger.info "Loading reference file (#{reference_file_name})"
|
13
|
-
@logger.info "Checking..."
|
14
|
-
reference = load_translation_file("#{@translation_file_path}/#{reference_file_name}")
|
15
|
-
Dir["#{@translation_file_path}/*.yml"].reject do |entry|
|
16
|
-
File.basename(entry) == reference_file_name
|
17
|
-
end.inject([]) do |errors, entry|
|
18
|
-
translation_file = load_translation_file(entry)
|
19
|
-
errors + unexpected_substitution_keys(reference, translation_file)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
def unexpected_substitution_keys(reference, translation_file)
|
24
|
-
reference_substitutions = substitutions_in(reference)
|
25
|
-
target_substitutions = substitutions_in(translation_file)
|
26
|
-
|
27
|
-
targets_by_path = target_substitutions.each_with_object({}) do |target, hash|
|
28
|
-
hash[exclude_locale_from_path(target.path)] = target
|
29
|
-
end
|
30
|
-
|
31
|
-
reference_substitutions.each_with_object([]) do |reference, unexpected_substitutions|
|
32
|
-
target = targets_by_path[exclude_locale_from_path(reference.path)]
|
33
|
-
next if target.nil? || reference.has_all_substitutions?(target)
|
34
|
-
unexpected_substitutions << UnexpectedSubstition.new(target, reference)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
def substitutions_in(translation_file)
|
39
|
-
flatten(translation_file).reject do |translation|
|
40
|
-
translation.substitutions.empty?
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
class TranslationEntry < Struct.new(:path, :value)
|
45
|
-
def substitutions
|
46
|
-
@substitutions ||= self.value.scan(/%{([^}]*)}/)
|
47
|
-
end
|
48
|
-
|
49
|
-
def has_all_substitutions?(other)
|
50
|
-
(other.substitutions - self.substitutions).empty?
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
class UnexpectedSubstition < Struct.new(:target, :reference)
|
55
|
-
def to_s
|
56
|
-
missing = (self.reference.substitutions - self.target.substitutions)
|
57
|
-
extras = (self.target.substitutions - self.reference.substitutions)
|
58
|
-
message = %Q{Key "#{target.path.join('.')}":}
|
59
|
-
if extras.any?
|
60
|
-
message << %Q{ Extra substitutions: ["#{extras.join('", "')}"].}
|
61
|
-
end
|
62
|
-
if missing.any?
|
63
|
-
message << %Q{ Missing substitutions: ["#{missing.join('", "')}"].}
|
64
|
-
end
|
65
|
-
message
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
def flatten(translation_file, path = [])
|
70
|
-
translation_file.map do |key, value|
|
71
|
-
case value
|
72
|
-
when Hash
|
73
|
-
flatten(value, path + [key])
|
74
|
-
else
|
75
|
-
TranslationEntry.new(path + [key], value || "")
|
76
|
-
end
|
77
|
-
end.flatten
|
78
|
-
end
|
79
|
-
|
80
|
-
def load_translation_file(filename)
|
81
|
-
YAML.load_file(filename)
|
82
|
-
end
|
83
|
-
|
84
|
-
def reference_file_name
|
85
|
-
"en.yml"
|
86
|
-
end
|
87
|
-
|
88
|
-
private
|
89
|
-
def exclude_locale_from_path(path)
|
90
|
-
path[1..-1]
|
91
|
-
end
|
92
|
-
end
|
@@ -1,132 +0,0 @@
|
|
1
|
-
require "test_helper"
|
2
|
-
require "rails_translation_manager/importer"
|
3
|
-
require "tmpdir"
|
4
|
-
require "csv"
|
5
|
-
|
6
|
-
module RailsTranslationManager
|
7
|
-
class ImporterTest < Minitest::Test
|
8
|
-
test 'should create a new locale file for a filled in translation csv file' do
|
9
|
-
given_csv(:fr,
|
10
|
-
[:key, :source, :translation],
|
11
|
-
["world_location.type.country", "Country", "Pays"],
|
12
|
-
["world_location.country", "Germany", "Allemange"],
|
13
|
-
["other.nested.key", "original", "translated"]
|
14
|
-
)
|
15
|
-
|
16
|
-
Importer.new(:fr, csv_path(:fr), import_directory).import
|
17
|
-
|
18
|
-
yaml_translation_data = YAML.load_file(File.join(import_directory, "fr.yml"))
|
19
|
-
expected = {"fr" => {
|
20
|
-
"world_location" => {
|
21
|
-
"country" => "Allemange",
|
22
|
-
"type" => {
|
23
|
-
"country" => "Pays"
|
24
|
-
}
|
25
|
-
},
|
26
|
-
"other" => {
|
27
|
-
"nested" => {
|
28
|
-
"key" => "translated"
|
29
|
-
}
|
30
|
-
}
|
31
|
-
}}
|
32
|
-
assert_equal expected, yaml_translation_data
|
33
|
-
end
|
34
|
-
|
35
|
-
test 'imports arrays from CSV as arrays' do
|
36
|
-
given_csv(:fr,
|
37
|
-
[:key, :source, :translation],
|
38
|
-
["fruit", ["Apples", "Bananas", "Pears"], ["Pommes", "Bananes", "Poires"]]
|
39
|
-
)
|
40
|
-
|
41
|
-
Importer.new(:fr, csv_path(:fr), import_directory).import
|
42
|
-
|
43
|
-
yaml_translation_data = YAML.load_file(File.join(import_directory, "fr.yml"))
|
44
|
-
expected = {"fr" => {
|
45
|
-
"fruit" => ["Pommes", "Bananes", "Poires"]
|
46
|
-
}}
|
47
|
-
assert_equal expected, yaml_translation_data
|
48
|
-
end
|
49
|
-
|
50
|
-
test 'interprets string "nil" as nil' do
|
51
|
-
given_csv(:fr,
|
52
|
-
[:key, :source, :translation],
|
53
|
-
["things", ["one", nil, "two"], ["une", nil, "deux"]]
|
54
|
-
)
|
55
|
-
|
56
|
-
Importer.new(:fr, csv_path(:fr), import_directory).import
|
57
|
-
|
58
|
-
yaml_translation_data = YAML.load_file(File.join(import_directory, "fr.yml"))
|
59
|
-
expected = {"fr" => {
|
60
|
-
"things" => ["une", nil, "deux"]
|
61
|
-
}}
|
62
|
-
assert_equal expected, yaml_translation_data
|
63
|
-
end
|
64
|
-
|
65
|
-
test 'interprets string ":thing" as symbol' do
|
66
|
-
given_csv(:fr,
|
67
|
-
[:key, :source, :translation],
|
68
|
-
["sentiment", ":whatever", ":bof"]
|
69
|
-
)
|
70
|
-
|
71
|
-
Importer.new(:fr, csv_path(:fr), import_directory).import
|
72
|
-
|
73
|
-
yaml_translation_data = YAML.load_file(File.join(import_directory, "fr.yml"))
|
74
|
-
expected = {"fr" => {
|
75
|
-
"sentiment" => :bof
|
76
|
-
}}
|
77
|
-
assert_equal expected, yaml_translation_data
|
78
|
-
end
|
79
|
-
|
80
|
-
test 'interprets integer strings as integers' do
|
81
|
-
given_csv(:fr,
|
82
|
-
[:key, :source, :translation],
|
83
|
-
["price", "123", "123"]
|
84
|
-
)
|
85
|
-
|
86
|
-
Importer.new(:fr, csv_path(:fr), import_directory).import
|
87
|
-
|
88
|
-
yaml_translation_data = YAML.load_file(File.join(import_directory, "fr.yml"))
|
89
|
-
expected = {"fr" => {
|
90
|
-
"price" => 123
|
91
|
-
}}
|
92
|
-
assert_equal expected, yaml_translation_data
|
93
|
-
end
|
94
|
-
|
95
|
-
test 'interprets boolean values as booleans, not strings' do
|
96
|
-
given_csv(:fr,
|
97
|
-
[:key, :source, :translation],
|
98
|
-
["key1", "is true", "true"],
|
99
|
-
["key2", "is false", "false"]
|
100
|
-
)
|
101
|
-
|
102
|
-
Importer.new(:fr, csv_path(:fr), import_directory).import
|
103
|
-
|
104
|
-
yaml_translation_data = YAML.load_file(File.join(import_directory, "fr.yml"))
|
105
|
-
expected = {"fr" => {
|
106
|
-
"key1" => true,
|
107
|
-
"key2" => false
|
108
|
-
}}
|
109
|
-
assert_equal expected, yaml_translation_data
|
110
|
-
end
|
111
|
-
|
112
|
-
private
|
113
|
-
|
114
|
-
def csv_path(locale)
|
115
|
-
File.join(import_directory, "#{locale}.csv")
|
116
|
-
end
|
117
|
-
|
118
|
-
def given_csv(locale, header_row, *rows)
|
119
|
-
csv = CSV.generate do |csv|
|
120
|
-
csv << CSV::Row.new(["key", "source", "translation"], ["key", "source", "translation"], true)
|
121
|
-
rows.each do |row|
|
122
|
-
csv << CSV::Row.new(["key", "source", "translation"], row)
|
123
|
-
end
|
124
|
-
end
|
125
|
-
File.open(csv_path(locale), "w") { |f| f.write csv.to_s }
|
126
|
-
end
|
127
|
-
|
128
|
-
def import_directory
|
129
|
-
@import_directory ||= Dir.mktmpdir
|
130
|
-
end
|
131
|
-
end
|
132
|
-
end
|
@@ -1,251 +0,0 @@
|
|
1
|
-
require "test_helper"
|
2
|
-
|
3
|
-
require "rails_translation_manager/stealer"
|
4
|
-
require "fileutils"
|
5
|
-
require "tmpdir"
|
6
|
-
require "csv"
|
7
|
-
require "i18n"
|
8
|
-
|
9
|
-
module RailsTranslationManager
|
10
|
-
class StealerTest < Minitest::Test
|
11
|
-
|
12
|
-
test "converts subtree of items to the same depth" do
|
13
|
-
|
14
|
-
original = {
|
15
|
-
"fr" => {
|
16
|
-
"document" => {
|
17
|
-
"type" => {
|
18
|
-
"type1" => 'premier genre',
|
19
|
-
"type2" => 'deuxième genre',
|
20
|
-
"type3" => 'troisième genre'
|
21
|
-
}
|
22
|
-
}
|
23
|
-
}
|
24
|
-
}
|
25
|
-
|
26
|
-
conversion_mapping = {
|
27
|
-
"document.type" => "content_item.format",
|
28
|
-
}
|
29
|
-
|
30
|
-
expected = {
|
31
|
-
"fr" => {
|
32
|
-
"content_item" => {
|
33
|
-
"format" => {
|
34
|
-
"type1" => 'premier genre',
|
35
|
-
"type2" => 'deuxième genre',
|
36
|
-
"type3" => 'troisième genre'
|
37
|
-
}
|
38
|
-
}
|
39
|
-
}
|
40
|
-
}
|
41
|
-
stealer_test(original, conversion_mapping, expected)
|
42
|
-
end
|
43
|
-
|
44
|
-
test "converts a subtree of items to a different depth" do
|
45
|
-
original = {
|
46
|
-
"fr" => {
|
47
|
-
"document" => {
|
48
|
-
"published" => 'publiée',
|
49
|
-
}
|
50
|
-
}
|
51
|
-
}
|
52
|
-
conversion_mapping = {
|
53
|
-
"document.published" => "content_item.metadata.published"
|
54
|
-
}
|
55
|
-
|
56
|
-
expected = {
|
57
|
-
"fr" => {
|
58
|
-
"content_item" => {
|
59
|
-
"metadata" => {
|
60
|
-
"published" => 'publiée'
|
61
|
-
}
|
62
|
-
}
|
63
|
-
}
|
64
|
-
}
|
65
|
-
|
66
|
-
stealer_test(original, conversion_mapping, expected)
|
67
|
-
end
|
68
|
-
|
69
|
-
test "combines multiple mappings" do
|
70
|
-
original = {
|
71
|
-
"fr" => {
|
72
|
-
"document" => {
|
73
|
-
"type" => {
|
74
|
-
"type1" => 'premier genre',
|
75
|
-
},
|
76
|
-
"published" => 'publiée',
|
77
|
-
}
|
78
|
-
}
|
79
|
-
}
|
80
|
-
|
81
|
-
conversion_mapping = {
|
82
|
-
"document.type" => "content_item.format",
|
83
|
-
"document.published" => "content_item.metadata.published"
|
84
|
-
}
|
85
|
-
expected = {
|
86
|
-
"fr" => {
|
87
|
-
"content_item" => {
|
88
|
-
"format" => {
|
89
|
-
"type1" => 'premier genre',
|
90
|
-
},
|
91
|
-
"metadata" => {
|
92
|
-
"published" => 'publiée',
|
93
|
-
}
|
94
|
-
}
|
95
|
-
}
|
96
|
-
}
|
97
|
-
stealer_test(original, conversion_mapping, expected)
|
98
|
-
end
|
99
|
-
|
100
|
-
test "does not copy over keys with no mapping" do
|
101
|
-
original = {
|
102
|
-
"fr" => {
|
103
|
-
"document" => {
|
104
|
-
"published" => 'publiée',
|
105
|
-
"do_not_want" => 'non voulu'
|
106
|
-
}
|
107
|
-
}
|
108
|
-
}
|
109
|
-
conversion_mapping = {
|
110
|
-
"document.published" => "content_item.metadata.published"
|
111
|
-
}
|
112
|
-
|
113
|
-
expected = {
|
114
|
-
"fr" => {
|
115
|
-
"content_item" => {
|
116
|
-
"metadata" => {
|
117
|
-
"published" => 'publiée'
|
118
|
-
}
|
119
|
-
}
|
120
|
-
}
|
121
|
-
}
|
122
|
-
|
123
|
-
stealer_test(original, conversion_mapping, expected)
|
124
|
-
end
|
125
|
-
|
126
|
-
test "overrides existing translations present in mapping" do
|
127
|
-
original = {
|
128
|
-
"fr" => {
|
129
|
-
"document" => {
|
130
|
-
"published" => 'publiée',
|
131
|
-
"updated" => 'mise au jour',
|
132
|
-
}
|
133
|
-
}
|
134
|
-
}
|
135
|
-
|
136
|
-
conversion_mapping = {
|
137
|
-
"document.published" => "content_item.metadata.published",
|
138
|
-
"document.updated" => "content_item.metadata.updated"
|
139
|
-
}
|
140
|
-
|
141
|
-
existing = {
|
142
|
-
"fr" => {
|
143
|
-
"content_item" => {
|
144
|
-
"metadata" => {
|
145
|
-
"updated" => 'mauvaise traduction'
|
146
|
-
}
|
147
|
-
}
|
148
|
-
}
|
149
|
-
}
|
150
|
-
|
151
|
-
expected = {
|
152
|
-
"fr" => {
|
153
|
-
"content_item" => {
|
154
|
-
"metadata" => {
|
155
|
-
"published" => 'publiée',
|
156
|
-
"updated" => 'mise au jour',
|
157
|
-
}
|
158
|
-
}
|
159
|
-
}
|
160
|
-
}
|
161
|
-
stealer_test(original, conversion_mapping, expected, existing)
|
162
|
-
end
|
163
|
-
|
164
|
-
test "does not override existing translations not in mapping" do
|
165
|
-
original = {
|
166
|
-
"fr" => {
|
167
|
-
"document" => {
|
168
|
-
"published" => 'publiée',
|
169
|
-
}
|
170
|
-
}
|
171
|
-
}
|
172
|
-
|
173
|
-
conversion_mapping = {
|
174
|
-
"document.published" => "content_item.metadata.published"
|
175
|
-
}
|
176
|
-
|
177
|
-
existing = {
|
178
|
-
"fr" => {
|
179
|
-
"content_item" => {
|
180
|
-
"metadata" => {
|
181
|
-
"updated" => 'mise au jour',
|
182
|
-
}
|
183
|
-
}
|
184
|
-
}
|
185
|
-
}
|
186
|
-
|
187
|
-
expected = {
|
188
|
-
"fr" => {
|
189
|
-
"content_item" => {
|
190
|
-
"metadata" => {
|
191
|
-
"published" => 'publiée',
|
192
|
-
"updated" => 'mise au jour',
|
193
|
-
}
|
194
|
-
}
|
195
|
-
}
|
196
|
-
}
|
197
|
-
stealer_test(original, conversion_mapping, expected, existing)
|
198
|
-
end
|
199
|
-
|
200
|
-
private
|
201
|
-
|
202
|
-
def stealer_test(original, mapping, expected, existing=nil)
|
203
|
-
write_source_data(original)
|
204
|
-
write_converter_data(mapping)
|
205
|
-
|
206
|
-
if existing.present?
|
207
|
-
File.open(locale_file, 'w') do |f|
|
208
|
-
f.puts(existing.to_yaml)
|
209
|
-
end
|
210
|
-
end
|
211
|
-
|
212
|
-
stealer = RailsTranslationManager::Stealer.new("fr", source_dir, converter_path, locales_dir)
|
213
|
-
stealer.steal_locale
|
214
|
-
|
215
|
-
assert_equal expected, YAML.load_file(locale_file)
|
216
|
-
end
|
217
|
-
|
218
|
-
def source_dir
|
219
|
-
@source_dir ||= Dir.mktmpdir
|
220
|
-
end
|
221
|
-
|
222
|
-
def source_locale_path
|
223
|
-
File.join(source_dir, 'config/locales')
|
224
|
-
end
|
225
|
-
|
226
|
-
def write_source_data(data)
|
227
|
-
FileUtils.mkdir_p(source_locale_path)
|
228
|
-
File.open(File.join(source_locale_path, 'fr.yml'), 'w') do |f|
|
229
|
-
f.puts(data.to_yaml)
|
230
|
-
end
|
231
|
-
end
|
232
|
-
|
233
|
-
def write_converter_data(data)
|
234
|
-
File.open(converter_path, 'w') do |f|
|
235
|
-
f.puts(data.to_yaml)
|
236
|
-
end
|
237
|
-
end
|
238
|
-
|
239
|
-
def converter_path
|
240
|
-
@converter_path ||= Tempfile.new('fr').path
|
241
|
-
end
|
242
|
-
|
243
|
-
def locales_dir
|
244
|
-
@locales_dir ||= Dir.mktmpdir
|
245
|
-
end
|
246
|
-
|
247
|
-
def locale_file
|
248
|
-
File.join(locales_dir, 'fr.yml')
|
249
|
-
end
|
250
|
-
end
|
251
|
-
end
|
@@ -1,51 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
require 'test_helper'
|
3
|
-
require 'rails_translation_manager/validator'
|
4
|
-
require 'tmpdir'
|
5
|
-
require 'fileutils'
|
6
|
-
|
7
|
-
module RailsTranslationManager
|
8
|
-
class ValidatorTest < Minitest::Test
|
9
|
-
def setup
|
10
|
-
@translation_path = Dir.mktmpdir
|
11
|
-
@translation_validator = Validator.new(@translation_path)
|
12
|
-
end
|
13
|
-
|
14
|
-
def teardown
|
15
|
-
FileUtils.remove_entry_secure(@translation_path)
|
16
|
-
end
|
17
|
-
|
18
|
-
def create_translation_file(locale, content)
|
19
|
-
File.open(File.join(@translation_path, "#{locale}.yml"), "w") do |f|
|
20
|
-
f.write(content.lstrip)
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
test "can create a flattened list of substitutions" do
|
25
|
-
translation_file = YAML.load(%q{
|
26
|
-
en:
|
27
|
-
view: View '%{title}'
|
28
|
-
test: foo
|
29
|
-
})
|
30
|
-
expected = [Validator::TranslationEntry.new(%w{en view}, "View '%{title}'")]
|
31
|
-
assert_equal expected, @translation_validator.substitutions_in(translation_file)
|
32
|
-
end
|
33
|
-
|
34
|
-
test "detects extra substitution keys" do
|
35
|
-
create_translation_file("en", %q{
|
36
|
-
en:
|
37
|
-
document:
|
38
|
-
view: View '%{title}'
|
39
|
-
})
|
40
|
-
create_translation_file("sr", %q{
|
41
|
-
sr:
|
42
|
-
document:
|
43
|
-
view: Pročitajte '%{naslov}'
|
44
|
-
})
|
45
|
-
errors = Validator.new(@translation_path).check!
|
46
|
-
|
47
|
-
expected = %q{Key "sr.document.view": Extra substitutions: ["naslov"]. Missing substitutions: ["title"].}
|
48
|
-
assert_equal [expected], errors.map(&:to_s)
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|