zendesk_apps_support 1.7.0 → 1.7.1
Sign up to get free protection for your applications and to get access to all the features.
- data/config/locales/en.yml +1 -0
- data/config/locales/translations/zendesk_apps_support.yml +4 -0
- data/lib/zendesk_apps_support/build_translation.rb +8 -13
- data/lib/zendesk_apps_support/validations/translations.rb +30 -3
- data/spec/build_translation_spec.rb +61 -75
- data/spec/i18n_spec.rb +1 -1
- data/spec/validations/fixture/invalid_en.json +20 -0
- data/spec/validations/fixture/valid_en.json +21 -0
- data/spec/validations/translations_spec.rb +32 -5
- metadata +10 -6
data/config/locales/en.yml
CHANGED
@@ -36,6 +36,7 @@ en:
|
|
36
36
|
duplicate_parameters: ! 'Duplicate app parameters defined: %{duplicate_parameters}'
|
37
37
|
translation:
|
38
38
|
invalid_locale: ! '%{file} is not a valid locale.'
|
39
|
+
invalid_format: ! '%{field} is invalid for translation.'
|
39
40
|
not_json: ! '%{file} is not valid JSON. %{errors}'
|
40
41
|
not_json_object: ! '%{file} is not a JSON object.'
|
41
42
|
stylesheet_error: ! 'Sass error: %{sass_error}'
|
@@ -91,6 +91,10 @@ parts:
|
|
91
91
|
key: "txt.apps.admin.error.app_build.translation.invalid_locale"
|
92
92
|
title: "App builder job: invalid locale file name"
|
93
93
|
value: "%{file} is not a valid locale."
|
94
|
+
- translation:
|
95
|
+
key: "txt.apps.admin.error.app_build.translation.invalid_format"
|
96
|
+
title: "App builder job: file format is invalid for translation"
|
97
|
+
value: "%{field} is invalid for translation."
|
94
98
|
- translation:
|
95
99
|
key: "txt.apps.admin.error.app_build.translation.not_json"
|
96
100
|
title: "App builder job: translation file is invalid json"
|
@@ -1,29 +1,24 @@
|
|
1
1
|
module ZendeskAppsSupport
|
2
2
|
module BuildTranslation
|
3
3
|
|
4
|
-
I18N_TITLE_KEY
|
5
|
-
I18N_VALUE_KEY
|
6
|
-
I18N_KEYS
|
7
|
-
|
8
|
-
def to_flattened_namespaced_hash(hash, prefix = nil)
|
4
|
+
I18N_TITLE_KEY = 'title'
|
5
|
+
I18N_VALUE_KEY = 'value'
|
6
|
+
I18N_KEYS = [I18N_TITLE_KEY, I18N_VALUE_KEY]
|
9
7
|
|
8
|
+
def to_flattened_namespaced_hash(hash, target_key = nil, prefix = nil)
|
10
9
|
hash.inject({}) do |result, (key, value)|
|
11
|
-
key = [
|
12
|
-
|
10
|
+
key = [prefix, key].compact.join('.')
|
13
11
|
if value.kind_of?(Hash)
|
14
|
-
|
15
|
-
|
16
|
-
result[key] = value[I18N_VALUE_KEY]
|
12
|
+
if target_key && is_translation_hash?(value)
|
13
|
+
result[key] = value[target_key]
|
17
14
|
else
|
18
|
-
result.update(
|
15
|
+
result.update(to_flattened_namespaced_hash(value, target_key, key))
|
19
16
|
end
|
20
|
-
|
21
17
|
else
|
22
18
|
result[key] = value
|
23
19
|
end
|
24
20
|
result
|
25
21
|
end
|
26
|
-
|
27
22
|
end
|
28
23
|
|
29
24
|
def remove_zendesk_keys(scope, translations = {})
|
@@ -4,7 +4,10 @@ module ZendeskAppsSupport
|
|
4
4
|
module Validations
|
5
5
|
module Translations
|
6
6
|
TRANSLATIONS_PATH = %r{^translations/(.*)\.json$}
|
7
|
-
VALID_LOCALE
|
7
|
+
VALID_LOCALE = /^[a-z]{2}(-\w{2,3})?$/
|
8
|
+
|
9
|
+
class TranslationFormatError < StandardError
|
10
|
+
end
|
8
11
|
|
9
12
|
class << self
|
10
13
|
def call(package)
|
@@ -25,12 +28,36 @@ module ZendeskAppsSupport
|
|
25
28
|
|
26
29
|
def json_error(file)
|
27
30
|
json = MultiJson.load(file.read)
|
28
|
-
|
29
|
-
|
31
|
+
if json.kind_of?(Hash)
|
32
|
+
if json["app"] && json["app"]["package"]
|
33
|
+
json["app"].delete("package")
|
34
|
+
begin
|
35
|
+
validate_translation_format(json)
|
36
|
+
return
|
37
|
+
rescue TranslationFormatError => e
|
38
|
+
ValidationError.new('translation.invalid_format', :field => e.message)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
else
|
42
|
+
ValidationError.new('translation.not_json_object', :file => file.relative_path)
|
43
|
+
end
|
30
44
|
rescue MultiJson::DecodeError => e
|
31
45
|
ValidationError.new('translation.not_json', :file => file.relative_path, :errors => e)
|
32
46
|
end
|
33
47
|
|
48
|
+
def validate_translation_format(json)
|
49
|
+
json.keys.each do |key|
|
50
|
+
raise TranslationFormatError.new("'#{key}': '#{json[key]}'") unless json[key].kind_of? Hash
|
51
|
+
|
52
|
+
if json[key].keys.sort == BuildTranslation::I18N_KEYS &&
|
53
|
+
json[key][BuildTranslation::I18N_TITLE_KEY].class == String &&
|
54
|
+
json[key][BuildTranslation::I18N_VALUE_KEY].class == String
|
55
|
+
next
|
56
|
+
else
|
57
|
+
validate_translation_format(json[key])
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
34
61
|
end
|
35
62
|
end
|
36
63
|
end
|
@@ -6,99 +6,85 @@ describe ZendeskAppsSupport::BuildTranslation do
|
|
6
6
|
let(:en_json) {
|
7
7
|
{
|
8
8
|
"app" => {
|
9
|
-
"
|
10
|
-
|
11
|
-
|
12
|
-
"disable_tooltip" => {
|
13
|
-
"label" => "Disable tooltip"
|
14
|
-
}
|
9
|
+
"abc" => {
|
10
|
+
"title" => "description for abc field",
|
11
|
+
"value" => "value of abc"
|
15
12
|
}
|
16
13
|
},
|
17
|
-
"
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
"
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
},
|
28
|
-
"parent_with_child_title_nested" => {
|
29
|
-
"title" => {
|
30
|
-
"value" => "Foo"
|
31
|
-
}
|
32
|
-
},
|
33
|
-
"parent_with_nested_invalid_zendesk_translation" => {
|
34
|
-
"title" => {
|
35
|
-
"title" => "Bar",
|
36
|
-
"desc" => "Foo"
|
37
|
-
}
|
38
|
-
},
|
39
|
-
"parent_with_nested_zendesk_translation" => {
|
40
|
-
"title" => {
|
41
|
-
"title" => "Bar",
|
42
|
-
"value" => "Foo"
|
14
|
+
"a" => {
|
15
|
+
"a1" => {
|
16
|
+
"title" => "description for a1 field",
|
17
|
+
"value" => "value of a1"
|
18
|
+
},
|
19
|
+
"b" => {
|
20
|
+
"b1" => {
|
21
|
+
"title" => "description for b1 field",
|
22
|
+
"value" => "value of b1"
|
23
|
+
}
|
43
24
|
}
|
44
25
|
}
|
45
26
|
}
|
46
27
|
}
|
47
28
|
|
48
|
-
describe '#
|
49
|
-
|
50
|
-
context "
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
29
|
+
describe '#to_flattened_namespaced_hash' do
|
30
|
+
|
31
|
+
context "not zendesk i18n format" do
|
32
|
+
it "should flatten hash without removing zendesk keys" do
|
33
|
+
expected = {
|
34
|
+
"app.abc.title" => "description for abc field",
|
35
|
+
"app.abc.value" => "value of abc",
|
36
|
+
"a.a1.title" => "description for a1 field",
|
37
|
+
"a.a1.value" => "value of a1",
|
38
|
+
"a.b.b1.title" => "description for b1 field",
|
39
|
+
"a.b.b1.value" => "value of b1"
|
40
|
+
}
|
59
41
|
|
60
|
-
|
61
|
-
@market_translations['parent_with_nested_invalid_zendesk_translation.title.title'].should eq 'Bar'
|
42
|
+
to_flattened_namespaced_hash(en_json).should == expected
|
62
43
|
end
|
63
|
-
|
64
44
|
end
|
65
45
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
end
|
75
|
-
|
76
|
-
it "value is correct parent without a child" do
|
77
|
-
@app_translations['parent_without_child'].should eq 'Foo'
|
78
|
-
end
|
79
|
-
|
80
|
-
it "value is correct parent with a child" do
|
81
|
-
@app_translations['parent_with_child']['child'].should eq 'Foo'
|
82
|
-
end
|
83
|
-
|
84
|
-
it "value is correct parent with a child with a title" do
|
85
|
-
@app_translations['parent_with_child_title']['title'].should eq 'Foo'
|
86
|
-
end
|
46
|
+
context 'zendesk i18n format' do
|
47
|
+
context 'flatten value keys' do
|
48
|
+
it "should flatten hash by removing zendesk title keys" do
|
49
|
+
expected = {
|
50
|
+
"app.abc" => "value of abc",
|
51
|
+
"a.a1" => "value of a1",
|
52
|
+
"a.b.b1" => "value of b1"
|
53
|
+
}
|
87
54
|
|
88
|
-
|
89
|
-
|
55
|
+
to_flattened_namespaced_hash(en_json, I18N_VALUE_KEY).should == expected
|
56
|
+
end
|
90
57
|
end
|
91
58
|
|
92
|
-
|
93
|
-
|
94
|
-
|
59
|
+
context 'flatten title keys' do
|
60
|
+
it "should flatten hash by removing zendesk value keys" do
|
61
|
+
expected = {
|
62
|
+
"app.abc" => "description for abc field",
|
63
|
+
"a.a1" => "description for a1 field",
|
64
|
+
"a.b.b1" => "description for b1 field"
|
65
|
+
}
|
95
66
|
|
96
|
-
|
97
|
-
|
67
|
+
to_flattened_namespaced_hash(en_json, I18N_TITLE_KEY).should == expected
|
68
|
+
end
|
98
69
|
end
|
99
|
-
|
100
70
|
end
|
101
71
|
|
102
72
|
end
|
103
73
|
|
74
|
+
describe '#remove_zendesk_keys' do
|
75
|
+
it "should remove zendesk translation keys" do
|
76
|
+
expected = {
|
77
|
+
"app" => {
|
78
|
+
"abc" => "value of abc"
|
79
|
+
},
|
80
|
+
"a" => {
|
81
|
+
"a1" => "value of a1",
|
82
|
+
"b" => {
|
83
|
+
"b1" => "value of b1"
|
84
|
+
}
|
85
|
+
}
|
86
|
+
}
|
87
|
+
remove_zendesk_keys(en_json).should == expected
|
88
|
+
end
|
89
|
+
end
|
104
90
|
end
|
data/spec/i18n_spec.rb
CHANGED
@@ -16,7 +16,7 @@ describe 'translations' do
|
|
16
16
|
project_root = Pathname.new(File.expand_path('../../', __FILE__))
|
17
17
|
zendesk_version = project_root.join('config/locales/translations/zendesk_apps_support.yml')
|
18
18
|
standard_version = project_root.join('config/locales/en.yml')
|
19
|
-
File.mtime(zendesk_version).should be <= File.mtime(standard_version)
|
19
|
+
File.mtime(zendesk_version).to_i.should be <= File.mtime(standard_version).to_i
|
20
20
|
end
|
21
21
|
|
22
22
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
{
|
2
|
+
"app": {
|
3
|
+
"package" : "my_app",
|
4
|
+
"abc" : {
|
5
|
+
"title" : "description for abc field",
|
6
|
+
"value" : "value of abc"
|
7
|
+
}
|
8
|
+
},
|
9
|
+
"a": {
|
10
|
+
"a1": {
|
11
|
+
"title": "description for a1 field",
|
12
|
+
"value": "value of a1"
|
13
|
+
},
|
14
|
+
"b": {
|
15
|
+
"b1": {
|
16
|
+
"value": "value of b1"
|
17
|
+
}
|
18
|
+
}
|
19
|
+
}
|
20
|
+
}
|
@@ -0,0 +1,21 @@
|
|
1
|
+
{
|
2
|
+
"app": {
|
3
|
+
"package" : "my_app",
|
4
|
+
"abc" : {
|
5
|
+
"title" : "description for abc field",
|
6
|
+
"value" : "value of abc"
|
7
|
+
}
|
8
|
+
},
|
9
|
+
"a": {
|
10
|
+
"a1": {
|
11
|
+
"title": "description for a1 field",
|
12
|
+
"value": "value of a1"
|
13
|
+
},
|
14
|
+
"b": {
|
15
|
+
"b1": {
|
16
|
+
"title": "description for b1 field",
|
17
|
+
"value": "value of b1"
|
18
|
+
}
|
19
|
+
}
|
20
|
+
}
|
21
|
+
}
|
@@ -1,6 +1,10 @@
|
|
1
1
|
require 'zendesk_apps_support'
|
2
2
|
require 'json'
|
3
3
|
|
4
|
+
def read_fixture_file(file)
|
5
|
+
File.read("#{File.dirname(__FILE__)}/fixture/#{file}")
|
6
|
+
end
|
7
|
+
|
4
8
|
describe ZendeskAppsSupport::Validations::Translations do
|
5
9
|
|
6
10
|
let(:package) { mock('Package', :files => translation_files) }
|
@@ -15,7 +19,7 @@ describe ZendeskAppsSupport::Validations::Translations do
|
|
15
19
|
|
16
20
|
context 'when there is file with invalid JSON' do
|
17
21
|
let(:translation_files) do
|
18
|
-
[
|
22
|
+
[mock('AppFile', :relative_path => 'translations/en.json', :read => '}')]
|
19
23
|
end
|
20
24
|
|
21
25
|
it 'should report the error' do
|
@@ -26,7 +30,7 @@ describe ZendeskAppsSupport::Validations::Translations do
|
|
26
30
|
|
27
31
|
context 'when there is file with JSON representing a non-Object' do
|
28
32
|
let(:translation_files) do
|
29
|
-
[
|
33
|
+
[mock('AppFile', :relative_path => 'translations/en.json', :read => '"foo bar"')]
|
30
34
|
end
|
31
35
|
|
32
36
|
it 'should report the error' do
|
@@ -37,7 +41,7 @@ describe ZendeskAppsSupport::Validations::Translations do
|
|
37
41
|
|
38
42
|
context 'when there is a file with an invalid locale for a name' do
|
39
43
|
let(:translation_files) do
|
40
|
-
[
|
44
|
+
[mock('AppFile', :relative_path => 'translations/en-US-1.json', :read => '{}')]
|
41
45
|
end
|
42
46
|
|
43
47
|
it 'should report the error' do
|
@@ -48,11 +52,34 @@ describe ZendeskAppsSupport::Validations::Translations do
|
|
48
52
|
|
49
53
|
context 'when there is a file with a valid locale containing valid JSON' do
|
50
54
|
let(:translation_files) do
|
51
|
-
[
|
55
|
+
[mock('AppFile', :relative_path => 'translations/en-US.json', :read => '{}')]
|
52
56
|
end
|
53
57
|
|
54
58
|
it 'should be valid' do
|
55
59
|
subject.length.should == 0
|
56
60
|
end
|
57
61
|
end
|
58
|
-
|
62
|
+
|
63
|
+
context 'validate translation format when "package" is defined inside "app"' do
|
64
|
+
context 'all the leaf nodes have defined "title" and "value"' do
|
65
|
+
let(:translation_files) do
|
66
|
+
[mock('AppFile', :relative_path => 'translations/en-US.json', :read => read_fixture_file("valid_en.json"))]
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'should be valid' do
|
70
|
+
subject.length.should == 0
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
context 'when the "title" field is not defined on one leaf node' do
|
75
|
+
let(:translation_files) do
|
76
|
+
[mock('AppFile', :relative_path => 'translations/en-US.json', :read => read_fixture_file("invalid_en.json"))]
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'should be invalid' do
|
80
|
+
subject.length.should == 1
|
81
|
+
subject[0].to_s.should =~ /is invalid for translation/
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: zendesk_apps_support
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.7.
|
4
|
+
version: 1.7.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -12,7 +12,7 @@ authors:
|
|
12
12
|
autorequire:
|
13
13
|
bindir: bin
|
14
14
|
cert_chain: []
|
15
|
-
date: 2013-
|
15
|
+
date: 2013-11-04 00:00:00.000000000 Z
|
16
16
|
dependencies:
|
17
17
|
- !ruby/object:Gem::Dependency
|
18
18
|
name: i18n
|
@@ -67,17 +67,17 @@ dependencies:
|
|
67
67
|
requirement: !ruby/object:Gem::Requirement
|
68
68
|
none: false
|
69
69
|
requirements:
|
70
|
-
- -
|
70
|
+
- - ! '>='
|
71
71
|
- !ruby/object:Gem::Version
|
72
|
-
version:
|
72
|
+
version: '0'
|
73
73
|
type: :runtime
|
74
74
|
prerelease: false
|
75
75
|
version_requirements: !ruby/object:Gem::Requirement
|
76
76
|
none: false
|
77
77
|
requirements:
|
78
|
-
- -
|
78
|
+
- - ! '>='
|
79
79
|
- !ruby/object:Gem::Version
|
80
|
-
version:
|
80
|
+
version: '0'
|
81
81
|
- !ruby/object:Gem::Dependency
|
82
82
|
name: erubis
|
83
83
|
requirement: !ruby/object:Gem::Requirement
|
@@ -183,6 +183,8 @@ files:
|
|
183
183
|
- spec/build_translation_spec.rb
|
184
184
|
- spec/i18n_spec.rb
|
185
185
|
- spec/package_spec.rb
|
186
|
+
- spec/validations/fixture/invalid_en.json
|
187
|
+
- spec/validations/fixture/valid_en.json
|
186
188
|
- spec/validations/jshint_error_spec.rb
|
187
189
|
- spec/validations/manifest_spec.rb
|
188
190
|
- spec/validations/source_spec.rb
|
@@ -228,6 +230,8 @@ test_files:
|
|
228
230
|
- spec/build_translation_spec.rb
|
229
231
|
- spec/i18n_spec.rb
|
230
232
|
- spec/package_spec.rb
|
233
|
+
- spec/validations/fixture/invalid_en.json
|
234
|
+
- spec/validations/fixture/valid_en.json
|
231
235
|
- spec/validations/jshint_error_spec.rb
|
232
236
|
- spec/validations/manifest_spec.rb
|
233
237
|
- spec/validations/source_spec.rb
|