Skip to content

Commit f8ec68b

Browse files
committed
Merge pull request #1 from monde-sistemas/add_i18n
Add i18n to easily customize errors messages
2 parents 6f02671 + 49b26b6 commit f8ec68b

3 files changed

Lines changed: 142 additions & 51 deletions

File tree

lib/jsonapi/exceptions.rb

Lines changed: 60 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ def errors
1818

1919
[JSONAPI::Error.new(code: JSONAPI::INTERNAL_SERVER_ERROR,
2020
status: :internal_server_error,
21-
title: 'Internal Server Error',
22-
detail: 'Internal Server Error',
21+
title: I18n.translate('exceptions.internal_server_error.title'),
22+
detail: I18n.translate('exceptions.internal_server_error.detail'),
2323
meta: meta)]
2424
end
2525
end
@@ -33,8 +33,8 @@ def initialize(resource)
3333
def errors
3434
[JSONAPI::Error.new(code: JSONAPI::INVALID_RESOURCE,
3535
status: :bad_request,
36-
title: 'Invalid resource',
37-
detail: "#{resource} is not a valid resource.")]
36+
title: I18n.translate('exceptions.invalid_resource.title'),
37+
detail: I18n.translate('exceptions.invalid_resource.detail', resource: resource))]
3838
end
3939
end
4040

@@ -47,8 +47,8 @@ def initialize(id)
4747
def errors
4848
[JSONAPI::Error.new(code: JSONAPI::RECORD_NOT_FOUND,
4949
status: :not_found,
50-
title: 'Record not found',
51-
detail: "The record identified by #{id} could not be found.")]
50+
title: I18n.translate('exceptions.record_not_found.title'),
51+
detail: I18n.translate('exceptions.record_not_found.detail', id: id))]
5252
end
5353
end
5454

@@ -61,8 +61,10 @@ def initialize(media_type)
6161
def errors
6262
[JSONAPI::Error.new(code: JSONAPI::UNSUPPORTED_MEDIA_TYPE,
6363
status: :unsupported_media_type,
64-
title: 'Unsupported media type',
65-
detail: "All requests that create or update resources must use the '#{JSONAPI::MEDIA_TYPE}' Content-Type. This request specified '#{media_type}.'")]
64+
title: I18n.translate('exceptions.unsupported_media_type.title'),
65+
detail: I18n.translate("exceptions.unsupported_media_type.detail",
66+
needed_media_type: JSONAPI::MEDIA_TYPE,
67+
media_type: media_type))]
6668
end
6769
end
6870

@@ -75,17 +77,17 @@ def initialize(id)
7577
def errors
7678
[JSONAPI::Error.new(code: JSONAPI::RELATION_EXISTS,
7779
status: :bad_request,
78-
title: 'Relation exists',
79-
detail: "The relation to #{id} already exists.")]
80+
title: I18n.translate('exceptions.has_many_relation.title'),
81+
detail: I18n.translate('exceptions.has_many_relation.detail', id: id))]
8082
end
8183
end
8284

8385
class ToManySetReplacementForbidden < Error
8486
def errors
8587
[JSONAPI::Error.new(code: JSONAPI::FORBIDDEN,
8688
status: :forbidden,
87-
title: 'Complete replacement forbidden',
88-
detail: 'Complete replacement forbidden for this relationship')]
89+
title: I18n.translate('exceptions.to_many_set_replacement_forbidden.title'),
90+
detail: I18n.translate('exceptions.to_many_set_replacement_forbidden.detail'))]
8991
end
9092
end
9193

@@ -98,8 +100,8 @@ def initialize(filters)
98100
def errors
99101
[JSONAPI::Error.new(code: JSONAPI::INVALID_FILTERS_SYNTAX,
100102
status: :bad_request,
101-
title: 'Invalid filters syntax',
102-
detail: "#{filters} is not a valid syntax for filtering.")]
103+
title: I18n.translate('exceptions.invalid_filter_syntax.title'),
104+
detail: I18n.translate('exceptions.invalid_filter_syntax.title'))]
103105
end
104106
end
105107

@@ -112,8 +114,8 @@ def initialize(filter)
112114
def errors
113115
[JSONAPI::Error.new(code: JSONAPI::FILTER_NOT_ALLOWED,
114116
status: :bad_request,
115-
title: 'Filter not allowed',
116-
detail: "#{filter} is not allowed.")]
117+
title: I18n.translate('exceptions.filter_not_allowed.title'),
118+
detail: I18n.translate('exceptions.filter_not_allowed.detail', filter: filter))]
117119
end
118120
end
119121

@@ -127,8 +129,9 @@ def initialize(filter, value)
127129
def errors
128130
[JSONAPI::Error.new(code: JSONAPI::INVALID_FILTER_VALUE,
129131
status: :bad_request,
130-
title: 'Invalid filter value',
131-
detail: "#{value} is not a valid value for #{filter}.")]
132+
title: I18n.translate('exceptions.invalid_filter_value.title'),
133+
detail: I18n.translate('exceptions.invalid_filter_value.detail'),
134+
value: value, filter: filter)]
132135
end
133136
end
134137

@@ -142,26 +145,27 @@ def initialize(field, value)
142145
def errors
143146
[JSONAPI::Error.new(code: JSONAPI::INVALID_FIELD_VALUE,
144147
status: :bad_request,
145-
title: 'Invalid field value',
146-
detail: "#{value} is not a valid value for #{field}.")]
148+
title: I18n.translate('exceptions.invalid_field_value.title'),
149+
detail: I18n.translate('exceptions.invalid_field_value.detail',
150+
value: value, field: field))]
147151
end
148152
end
149153

150154
class InvalidFieldFormat < Error
151155
def errors
152156
[JSONAPI::Error.new(code: JSONAPI::INVALID_FIELD_FORMAT,
153157
status: :bad_request,
154-
title: 'Invalid field format',
155-
detail: 'Fields must specify a type.')]
158+
title: I18n.translate('exceptions.invalid_field_format.title'),
159+
detail: I18n.translate('exceptions.invalid_field_format.detail'))]
156160
end
157161
end
158162

159163
class InvalidLinksObject < Error
160164
def errors
161165
[JSONAPI::Error.new(code: JSONAPI::INVALID_LINKS_OBJECT,
162166
status: :bad_request,
163-
title: 'Invalid Links Object',
164-
detail: 'Data is not a valid Links Object.')]
167+
title: I18n.translate('exceptions.invalid_links_object.title'),
168+
detail: I18n.translate('exceptions.invalid_links_object.detail'))]
165169
end
166170
end
167171

@@ -174,8 +178,8 @@ def initialize(type)
174178
def errors
175179
[JSONAPI::Error.new(code: JSONAPI::TYPE_MISMATCH,
176180
status: :bad_request,
177-
title: 'Type Mismatch',
178-
detail: "#{type} is not a valid type for this operation.")]
181+
title: I18n.translate('exceptions.type_mismatch.title'),
182+
detail: I18n.translate('exceptions.type_mismatch.detail', type: type))]
179183
end
180184
end
181185

@@ -189,8 +193,9 @@ def initialize(type, field)
189193
def errors
190194
[JSONAPI::Error.new(code: JSONAPI::INVALID_FIELD,
191195
status: :bad_request,
192-
title: 'Invalid field',
193-
detail: "#{field} is not a valid field for #{type}.")]
196+
title: I18n.translate('exceptions.invalid_field.title'),
197+
detail: I18n.translate('exceptions.invalid_field.detail',
198+
field: field, type: type))]
194199
end
195200
end
196201

@@ -204,8 +209,9 @@ def initialize(resource, relationship)
204209
def errors
205210
[JSONAPI::Error.new(code: JSONAPI::INVALID_INCLUDE,
206211
status: :bad_request,
207-
title: 'Invalid field',
208-
detail: "#{relationship} is not a valid relationship of #{resource}")]
212+
title: I18n.translate('exceptions.invalid_include.title'),
213+
detail: I18n.translate('exceptions.invalid_include.detail',
214+
relationship: relationship, resource: resource))]
209215
end
210216
end
211217

@@ -219,8 +225,9 @@ def initialize(resource, sort_criteria)
219225
def errors
220226
[JSONAPI::Error.new(code: JSONAPI::INVALID_SORT_CRITERIA,
221227
status: :bad_request,
222-
title: 'Invalid sort criteria',
223-
detail: "#{sort_criteria} is not a valid sort criteria for #{resource}")]
228+
title: I18n.translate('exceptions.invalid_sort_criteria.title'),
229+
detail: I18n.translate('exceptions.invalid_sort_criteria.detail',
230+
sort_criteria: sort_criteria, resource: resource))]
224231
end
225232
end
226233

@@ -234,8 +241,9 @@ def errors
234241
params.collect do |param|
235242
JSONAPI::Error.new(code: JSONAPI::PARAM_NOT_ALLOWED,
236243
status: :bad_request,
237-
title: 'Param not allowed',
238-
detail: "#{param} is not allowed.")
244+
title: I18n.translate('exceptions.parameters_not_allowed.title'),
245+
detail: I18n.translate('exceptions.parameters_not_allowed.detail', param: param))
246+
239247
end
240248
end
241249
end
@@ -249,17 +257,17 @@ def initialize(param)
249257
def errors
250258
[JSONAPI::Error.new(code: JSONAPI::PARAM_MISSING,
251259
status: :bad_request,
252-
title: 'Missing Parameter',
253-
detail: "The required parameter, #{param}, is missing.")]
260+
title: I18n.translate('exceptions.parameter_missing.title'),
261+
detail: I18n.translate('exceptions.parameter_missing.detail', param: param))]
254262
end
255263
end
256264

257265
class CountMismatch < Error
258266
def errors
259267
[JSONAPI::Error.new(code: JSONAPI::COUNT_MISMATCH,
260268
status: :bad_request,
261-
title: 'Count to key mismatch',
262-
detail: 'The resource collection does not contain the same number of objects as the number of keys.')]
269+
title: I18n.translate('exceptions.count_mismatch.title'),
270+
detail: I18n.translate('exceptions.count_mismatch.detail'))]
263271
end
264272
end
265273

@@ -272,17 +280,18 @@ def initialize(key)
272280
def errors
273281
[JSONAPI::Error.new(code: JSONAPI::KEY_NOT_INCLUDED_IN_URL,
274282
status: :bad_request,
275-
title: 'Key is not included in URL',
276-
detail: "The URL does not support the key #{key}")]
283+
title: I18n.translate('exceptions.key_not_included_in_url.title'),
284+
detail: I18n.translate('exceptions.key_not_included_in_url.detail',
285+
key: key))]
277286
end
278287
end
279288

280289
class MissingKey < Error
281290
def errors
282291
[JSONAPI::Error.new(code: JSONAPI::KEY_ORDER_MISMATCH,
283292
status: :bad_request,
284-
title: 'A key is required',
285-
detail: 'The resource object does not contain a key.')]
293+
title: I18n.translate('exceptions.missing_key.title'),
294+
detail: I18n.translate('exceptions.missing_key.detail'))]
286295
end
287296
end
288297

@@ -295,7 +304,7 @@ def initialize(message)
295304
def errors
296305
[JSONAPI::Error.new(code: JSONAPI::LOCKED,
297306
status: :locked,
298-
title: 'Locked resource',
307+
title: I18n.translate('exceptions.record_locked.title'),
299308
detail: "#{message}")]
300309
end
301310
end
@@ -343,17 +352,17 @@ class SaveFailed < Error
343352
def errors
344353
[JSONAPI::Error.new(code: JSONAPI::SAVE_FAILED,
345354
status: :unprocessable_entity,
346-
title: 'Save failed or was cancelled',
347-
detail: 'Save failed or was cancelled')]
355+
title: I18n.translate('exceptions.save_failed.title'),
356+
detail: I18n.translate('exceptions.save_failed.detail'))]
348357
end
349358
end
350359

351360
class InvalidPageObject < Error
352361
def errors
353362
[JSONAPI::Error.new(code: JSONAPI::INVALID_PAGE_OBJECT,
354363
status: :bad_request,
355-
title: 'Invalid Page Object',
356-
detail: 'Invalid Page Object.')]
364+
title: I18n.translate('exceptions.invalid_page_object.title'),
365+
detail: I18n.translate('exceptions.invalid_page_object.detail'))]
357366
end
358367
end
359368

@@ -367,8 +376,8 @@ def errors
367376
params.collect do |param|
368377
JSONAPI::Error.new(code: JSONAPI::PARAM_NOT_ALLOWED,
369378
status: :bad_request,
370-
title: 'Page parameter not allowed',
371-
detail: "#{param} is not an allowed page parameter.")
379+
title: I18n.translate('exceptions.page_parameters_not_allowed.title'),
380+
detail: I18n.translate('exceptions.page_parameters_not_allowed.detail', param: param))
372381
end
373382
end
374383
end
@@ -378,13 +387,13 @@ class InvalidPageValue < Error
378387
def initialize(page, value, msg = nil)
379388
@page = page
380389
@value = value
381-
@msg = msg || "#{value} is not a valid value for #{page} page parameter."
390+
@msg = msg || I18n.translate('exceptions.invalid_page_value.detail', value: value, page: page)
382391
end
383392

384393
def errors
385394
[JSONAPI::Error.new(code: JSONAPI::INVALID_PAGE_VALUE,
386395
status: :bad_request,
387-
title: 'Invalid page value',
396+
title: I18n.translate('exceptions.invalid_page_value.title'),
388397
detail: @msg)]
389398
end
390399
end

locales/en.yml

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
en:
2+
exceptions:
3+
internal_server_error:
4+
title: 'Internal Server Error'
5+
detail: 'Internal Server Error'
6+
invalid_resource:
7+
title: 'Invalid resource'
8+
detail: "%{resource} is not a valid resource."
9+
record_not_found:
10+
title: 'Record not found'
11+
detail: "The record identified by %{id} could not be found."
12+
unsupported_media_type:
13+
title: 'Unsupported media type'
14+
detail: "All requests that create or update must use the '%{needed_media_type}' Content-Type. This request specified '%{media_type}.'"
15+
has_many_relation:
16+
title: 'Relation exists'
17+
detail: "The relation to %{id} already exists."
18+
to_many_set_replacement_forbidden:
19+
title: 'Complete replacement forbidden'
20+
detail: 'Complete replacement forbidden for this relationship'
21+
invalid_filter_syntax:
22+
title: 'Invalid filters syntax'
23+
detail: "%{filters} is not a valid syntax for filtering."
24+
filter_not_allowed:
25+
title: "Filter not allowed"
26+
detail: "%{filter} is not allowed."
27+
invalid_filter_value:
28+
title: 'Invalid filter value'
29+
detail: "%{value} is not a valid value for %{filter}."
30+
invalid_field_value:
31+
title: 'Invalid field value'
32+
detail: "%{value} is not a valid value for %{field}."
33+
invalid_field_format:
34+
title: 'Invalid field format'
35+
detail: 'Fields must specify a type.'
36+
invalid_links_object:
37+
title: 'Invalid Links Object'
38+
detail: 'Data is not a valid Links Object.'
39+
type_mismatch:
40+
title: 'Type Mismatch'
41+
detail: "%{type} is not a valid type for this operation."
42+
invalid_field:
43+
title: 'Invalid field'
44+
detail: "%{field} is not a valid field for %{type}."
45+
invalid_include:
46+
title: 'Invalid field'
47+
detail: "%{relationship} is not a valid relationship of %{resource}"
48+
invalid_sort_criteria:
49+
title: 'Invalid sort criteria'
50+
detail: "%{sort_criteria} is not a valid sort criteria for %{resource}"
51+
parameters_not_allowed:
52+
title: 'Param not allowed'
53+
detail: "%{param} is not allowed."
54+
parameter_missing:
55+
title: 'Missing Parameter'
56+
detail: "The required parameter, %{param}, is missing."
57+
count_mismatch:
58+
title: 'Count to key mismatch'
59+
detail: 'The resource collection does not contain the same number of objects as the number of keys.'
60+
key_not_included_in_url:
61+
title: 'Key is not included in URL'
62+
detail: "The URL does not support the key %{key}"
63+
missing_key:
64+
title: 'A key is required'
65+
detail: 'The resource object does not contain a key.'
66+
record_locked:
67+
title: 'Locked resource'
68+
save_failed:
69+
title: 'Save failed or was cancelled'
70+
detail: 'Save failed or was cancelled'
71+
invalid_page_object:
72+
title: 'Invalid Page Object'
73+
detail: 'Invalid Page Object.'
74+
page_parameters_not_allowed:
75+
title: 'Page parameter not allowed'
76+
detail: "%{param} is not an allowed page parameter."
77+
invalid_page_value:
78+
title: 'Invalid page value'
79+
detail: "%{value} is not a valid value for %{page} page parameter."

test/test_helper.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@
2424

2525
Rails.env = 'test'
2626

27+
I18n.load_path += Dir[File.expand_path("../../locales/*.yml", __FILE__)]
28+
I18n.enforce_available_locales = false
29+
2730
JSONAPI.configure do |config|
2831
config.json_key_format = :camelized_key
2932
end

0 commit comments

Comments
 (0)