mods_display 1.0.0.alpha4 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +42 -0
  3. data/.rubocop_todo.yml +72 -401
  4. data/Gemfile +4 -1
  5. data/README.md +0 -63
  6. data/Rakefile +8 -6
  7. data/app/components/mods_display/field_component.rb +2 -0
  8. data/app/components/mods_display/list_field_component.rb +2 -0
  9. data/app/components/mods_display/record_component.rb +2 -0
  10. data/app/helpers/mods_display/record_helper.rb +6 -7
  11. data/app/models/mods_display/record.rb +2 -1
  12. data/config.ru +2 -2
  13. data/lib/mods_display/country_codes.rb +2 -1
  14. data/lib/mods_display/fields/abstract.rb +2 -0
  15. data/lib/mods_display/fields/access_condition.rb +11 -8
  16. data/lib/mods_display/fields/audience.rb +2 -0
  17. data/lib/mods_display/fields/cartographics.rb +11 -4
  18. data/lib/mods_display/fields/collection.rb +7 -3
  19. data/lib/mods_display/fields/contact.rb +6 -1
  20. data/lib/mods_display/fields/contents.rb +2 -0
  21. data/lib/mods_display/fields/description.rb +4 -3
  22. data/lib/mods_display/fields/extent.rb +4 -1
  23. data/lib/mods_display/fields/field.rb +10 -9
  24. data/lib/mods_display/fields/form.rb +4 -1
  25. data/lib/mods_display/fields/genre.rb +3 -1
  26. data/lib/mods_display/fields/geo.rb +5 -2
  27. data/lib/mods_display/fields/identifier.rb +21 -18
  28. data/lib/mods_display/fields/imprint.rb +168 -217
  29. data/lib/mods_display/fields/language.rb +5 -1
  30. data/lib/mods_display/fields/location.rb +6 -3
  31. data/lib/mods_display/fields/name.rb +39 -23
  32. data/lib/mods_display/fields/nested_related_item.rb +18 -2
  33. data/lib/mods_display/fields/note.rb +10 -8
  34. data/lib/mods_display/fields/related_item.rb +32 -27
  35. data/lib/mods_display/fields/resource_type.rb +3 -1
  36. data/lib/mods_display/fields/sub_title.rb +2 -0
  37. data/lib/mods_display/fields/subject.rb +17 -50
  38. data/lib/mods_display/fields/title.rb +39 -28
  39. data/lib/mods_display/fields/values.rb +2 -0
  40. data/lib/mods_display/html.rb +4 -3
  41. data/lib/mods_display/related_item_concerns.rb +4 -2
  42. data/lib/mods_display/relator_codes.rb +2 -0
  43. data/lib/mods_display/version.rb +3 -1
  44. data/lib/mods_display.rb +5 -3
  45. data/mods_display.gemspec +1 -1
  46. metadata +14 -8
data/README.md CHANGED
@@ -22,69 +22,6 @@ Or install it yourself as:
22
22
 
23
23
  $ gem install mods_display
24
24
 
25
- Include the `ModelExtension` into your model.
26
-
27
- class MyClass
28
- include ModsDisplay::ModelExtension
29
- end
30
-
31
- Configure the source of the MODS XML in your model. You can pass a string of XML to the mods_xml_source method, however it will also accept a block where you can call methods on self (so if the MODS XML string is held in MyClass#mods):
32
-
33
- class MyClass
34
- ....
35
-
36
- mods_xml_source do |model|
37
- model.mods
38
- end
39
-
40
- end
41
-
42
- Include the `ControllerExtension` into your rails controller (or another class if not using rails).
43
-
44
- class MyController
45
- include ModsDisplay::ControllerExtension
46
- end
47
-
48
- Optionally configure the mods display gem (more on configuration later).
49
-
50
- class MyController
51
- ....
52
- configure_mods_display do
53
- ....
54
- end
55
- end
56
-
57
- ## Usage
58
-
59
- Once installed, the class that included the `ControllerExtension` (`MyController`) will have the `render_mods_display` method available. This method takes one argument which is an instance of the class that included the `ModelExtension` (`MyClass`).
60
-
61
- render_mods_display(@model) # where @model.is_a?(MyClass)
62
-
63
- The basic render call will return the top-level ModsDisplay::HTML class object. Any String method (e.g. #html_safe) you call on this top-level object will be sent down to the #to_html method which will return the HTML for all the metadata in the MODS document.
64
-
65
- render_mods_display(@model).to_html
66
-
67
- You can abstract the main (first) title by calling #title on the top-level HTML method
68
-
69
- render_mods_display(@model).title
70
-
71
- When getting JUST the main (first) title out of the metadata, it will be useful to get the rest of the metadata without the main title. You can accomplish this by calling #body on the top-level HTML object.
72
-
73
- render_mods_display(@model).body
74
-
75
- ## Advanced Usage
76
-
77
- You can also access the array of ModsDisplay::Values objects for a given class directly by calling the name of the class. The class names are not always intuitive for public consumption so you may want to check the code the particular method to call.
78
-
79
- render_mods_display(@model).abstract
80
- => [#<ModsDisplay::Values @label="Abstract:", @values=["Hey. I'm an abstract."]>]
81
-
82
- Given that this semantics that we're concerned with here are more about titles and data construction rather than XML it may be required that you find something by the label. A common example of this is the imprint class. The imprint class can return other publication data that is not the imprint statement. You'll want to select (using your favorite enumerable method) the element in the array that is an imprint.
83
-
84
- imprint = render_mods_display(@model).imprint.find do |data|
85
- data.label == "Imprint:"
86
- end.values
87
-
88
25
  ## Release/Upgrade Notes
89
26
 
90
27
  #### v0.5.0
data/Rakefile CHANGED
@@ -1,16 +1,18 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'bundler/gem_tasks'
2
4
  require 'rspec/core/rake_task'
3
5
  require 'rubocop/rake_task'
4
6
 
5
- RuboCop::RakeTask.new
7
+ RuboCop::RakeTask.new do |task|
8
+ task.requires << 'rubocop-rspec'
9
+ task.options = ['--fail-level', 'error']
10
+ end
11
+
6
12
  RSpec::Core::RakeTask.new(:spec)
7
13
 
8
14
  task :ci do
9
- # Commenting out Rubocop as part of the CI build temporarily
10
- # because TravisCI is not respecting the .rubocop_todo.yml
11
- # You can and should still run rubocop in your editor or
12
- # from the command line manually.
13
- # Rake::Task['rubocop'].invoke
15
+ Rake::Task['rubocop'].invoke
14
16
  Rake::Task['spec'].invoke
15
17
  end
16
18
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ModsDisplay
2
4
  class FieldComponent < ViewComponent::Base
3
5
  with_collection_parameter :field
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ModsDisplay
2
4
  class ListFieldComponent < ModsDisplay::FieldComponent
3
5
  def initialize(field:, list_html_attributes: {}, list_item_html_attributes: {}, **args)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ModsDisplay
2
4
  class RecordComponent < ViewComponent::Base
3
5
  DEFAULT_FIELDS = [
@@ -15,7 +15,7 @@ module ModsDisplay
15
15
  render component.new(field: field, delimiter: delimiter, value_transformer: block)
16
16
  end
17
17
 
18
- def mods_name_field(field, &block)
18
+ def mods_name_field(field)
19
19
  mods_record_field(field) do |name|
20
20
  block_given? ? capture { yield(name.name) } : name.name
21
21
  end
@@ -55,15 +55,13 @@ module ModsDisplay
55
55
  link_buffer = []
56
56
  linked_subjects = []
57
57
  subjects.each do |subject|
58
- if subject.present?
59
- linked_subjects << link_to_mods_subject(subject, link_buffer, &block)
60
- end
58
+ linked_subjects << link_to_mods_subject(subject, link_buffer, &block) if subject.present?
61
59
  end
62
60
  linked_subjects
63
61
  end
64
62
 
65
63
  # @private
66
- def link_to_mods_subject(subject, buffer = [], &block)
64
+ def link_to_mods_subject(subject, buffer = [])
67
65
  subject_text = subject.respond_to?(:name) ? subject.name : subject
68
66
  link = block_given? ? capture { yield(subject_text, buffer) } : subject_text
69
67
  buffer << subject_text.strip
@@ -73,7 +71,7 @@ module ModsDisplay
73
71
 
74
72
  # rubocop:disable Layout/LineLength
75
73
  # @private, but used in PURL currently
76
- def link_urls_and_email(val, tags: %w[a dl dd dt i b em strong])
74
+ def link_urls_and_email(val, tags: %w[a dl dd dt i b em strong cite br])
77
75
  val = val.gsub(%r{<[^/> ]+}) do |possible_tag|
78
76
  # Allow potentially valid HTML tags through to the sanitizer step, and HTML escape the rest
79
77
  if tags.include? possible_tag[1..]
@@ -93,6 +91,8 @@ module ModsDisplay
93
91
  if match =~ email
94
92
  val.gsub!(match, "<a href='mailto:#{match}'>#{match}</a>")
95
93
  else
94
+ match = match.delete_suffix('&gt')
95
+
96
96
  val.gsub!(match, "<a href='#{match}'>#{match}</a>")
97
97
  end
98
98
  end
@@ -101,6 +101,5 @@ module ModsDisplay
101
101
  sanitize val, tags: tags, attributes: %w[href]
102
102
  end
103
103
  # rubocop:enable Layout/LineLength
104
-
105
104
  end
106
105
  end
@@ -14,7 +14,8 @@ module ModsDisplay
14
14
 
15
15
  def mods_record
16
16
  return if xml.nil?
17
- @mods_record ||= Stanford::Mods::Record.new.tap { |mods| mods.from_str(xml, false) }
17
+
18
+ @mods_record ||= Stanford::Mods::Record.new.tap { |mods| mods.from_str(xml) }
18
19
  end
19
20
 
20
21
  def mods_display_html
data/config.ru CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "rubygems"
4
- require "bundler"
3
+ require 'rubygems'
4
+ require 'bundler'
5
5
 
6
6
  Bundler.require :default, :development
7
7
 
@@ -1,4 +1,5 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
+
2
3
  module ModsDisplay
3
4
  module CountryCodes
4
5
  def country_codes
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ModsDisplay
2
4
  class Abstract < Field
3
5
  private
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ModsDisplay
2
4
  class AccessCondition < Field
3
5
  LICENSES = {
@@ -71,18 +73,18 @@ module ModsDisplay
71
73
  when 'license'
72
74
  license_statement(element)
73
75
  else
74
- element.text
76
+ element_text(element)
75
77
  end
76
78
  end
77
79
 
78
80
  def copyright_statement(element)
79
- element.text.gsub(/\(c\) copyright/i, '&copy;').gsub(/\(c\)/i, '&copy;')
81
+ element_text(element).gsub(/\(c\) copyright/i, '&copy;').gsub(/\(c\)/i, '&copy;')
80
82
  end
81
83
 
82
84
  def license_statement(element)
83
- matches = element.text.match(/^(?<code>.*) (?<type>.*):(?<description>.*)$/)
85
+ matches = element_text(element).match(/^(?<code>.*) (?<type>.*):(?<description>.*)$/)
84
86
 
85
- return element.text unless matches
87
+ return element_text(element) unless matches
86
88
 
87
89
  code = matches[:code].downcase
88
90
  type = matches[:type].downcase
@@ -111,21 +113,22 @@ module ModsDisplay
111
113
  def access_label(element)
112
114
  type = normalize_type(element)
113
115
  return access_labels[type] if access_labels.key?(type)
116
+
114
117
  I18n.t('mods_display.access_condition')
115
118
  end
116
119
 
117
120
  def normalize_type(element)
118
121
  type = element.attributes['type']
119
122
  return type.value.strip.gsub(/\s*/, '').downcase if type.respond_to?(:value)
123
+
120
124
  ''
121
125
  end
122
126
 
123
127
  def access_labels
124
- { 'useandreproduction' => I18n.t('mods_display.use_and_reproduction'),
128
+ { 'useandreproduction' => I18n.t('mods_display.use_and_reproduction'),
125
129
  'restrictiononaccess' => I18n.t('mods_display.restriction_on_access'),
126
- 'copyright' => I18n.t('mods_display.copyright'),
127
- 'license' => I18n.t('mods_display.license')
128
- }
130
+ 'copyright' => I18n.t('mods_display.copyright'),
131
+ 'license' => I18n.t('mods_display.license') }
129
132
  end
130
133
  end
131
134
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ModsDisplay
2
4
  class Audience < Field
3
5
  private
@@ -1,15 +1,22 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ModsDisplay
2
4
  class Cartographics < Field
3
5
  def fields
4
6
  return nil if @values.nil?
7
+
5
8
  return_fields = []
6
9
  @values.each do |value|
7
10
  next unless value.respond_to?(:cartographics)
11
+
8
12
  value.cartographics.each do |field|
9
- scale = field.scale.empty? ? 'Scale not given' : field.scale.text
10
- projection = field.projection.empty? ? nil : field.projection.text
11
- coordinates = field.coordinates.empty? ? nil : field.coordinates.text
12
- post_scale = [projection, coordinates].compact.length > 0 ? [projection, coordinates].compact.join(' ') : nil
13
+ scale = field.scale.empty? ? 'Scale not given' : element_text(field.scale)
14
+ projection = field.projection.empty? ? nil : element_text(field.projection)
15
+ coordinates = field.coordinates.empty? ? nil : element_text(field.coordinates)
16
+ post_scale = if [projection,
17
+ coordinates].compact.length.positive?
18
+ [projection, coordinates].compact.join(' ')
19
+ end
13
20
  return_fields << ModsDisplay::Values.new(
14
21
  label: (displayLabel(field) || label || I18n.t('mods_display.map_data')),
15
22
  values: [[scale, post_scale].compact.join(' ; ')]
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ModsDisplay
2
4
  ###
3
5
  # Collection class to parse collection data out of Mods relatedItem fields
@@ -11,9 +13,10 @@ module ModsDisplay
11
13
  return_fields = []
12
14
  @values.each do |value|
13
15
  next unless related_item_is_a_collection?(value)
16
+
14
17
  return_fields << ModsDisplay::Values.new(
15
18
  label: collection_label(value),
16
- values: [value.titleInfo.text.strip]
19
+ values: [element_text(value.titleInfo)]
17
20
  )
18
21
  end
19
22
  collapse_fields(return_fields)
@@ -27,8 +30,9 @@ module ModsDisplay
27
30
 
28
31
  def resource_type_is_collection?(value)
29
32
  return unless value.respond_to?(:typeOfResource)
30
- return unless value.typeOfResource.attributes.length > 0
31
- value.typeOfResource.attributes.length > 0 &&
33
+ return unless value.typeOfResource.attributes.length.positive?
34
+
35
+ value.typeOfResource.attributes.length.positive? &&
32
36
  value.typeOfResource.attributes.first.key?('collection') &&
33
37
  value.typeOfResource.attributes.first['collection'].value == 'yes'
34
38
  end
@@ -1,8 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ModsDisplay
2
4
  class Contact < Field
3
5
  def fields
4
6
  return_fields = contact_fields.map do |value|
5
- ModsDisplay::Values.new(label: displayLabel(value) || I18n.t('mods_display.contact'), values: [value.text])
7
+ ModsDisplay::Values.new(
8
+ label: displayLabel(value) || I18n.t('mods_display.contact'),
9
+ values: [element_text(value)]
10
+ )
6
11
  end
7
12
  collapse_fields(return_fields)
8
13
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ModsDisplay
2
4
  class Contents < Field
3
5
  def to_html(view_context = ApplicationController.renderer)
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ModsDisplay
2
4
  class Description < Field
3
5
  def fields
4
6
  return_fields = description_fields.map do |value|
5
- ModsDisplay::Values.new(label: description_label(value), values: [value.text])
7
+ ModsDisplay::Values.new(label: description_label(value), values: [element_text(value)])
6
8
  end
7
9
  collapse_fields(return_fields)
8
10
  end
@@ -21,8 +23,7 @@ module ModsDisplay
21
23
 
22
24
  def labels
23
25
  { digitalOrigin: I18n.t('mods_display.digital_origin'),
24
- note: I18n.t('mods_display.note')
25
- }
26
+ note: I18n.t('mods_display.note') }
26
27
  end
27
28
  end
28
29
  end
@@ -1,11 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ModsDisplay
2
4
  class Extent < Field
3
5
  def fields
4
6
  return [] unless extent_fields.present?
7
+
5
8
  [
6
9
  ModsDisplay::Values.new(
7
10
  label: I18n.t('mods_display.extent'),
8
- values: extent_fields.map(&:text)
11
+ values: extent_fields.map { |x| element_text(x) }
9
12
  )
10
13
  ]
11
14
  end
@@ -1,4 +1,5 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
+
2
3
  module ModsDisplay
3
4
  class Field
4
5
  def initialize(values)
@@ -9,7 +10,7 @@ module ModsDisplay
9
10
  return_fields = @values.map do |value|
10
11
  ModsDisplay::Values.new(
11
12
  label: displayLabel(value) || label,
12
- values: [displayForm(@values) || value.text].flatten
13
+ values: [element_text(value)]
13
14
  )
14
15
  end
15
16
  collapse_fields(return_fields)
@@ -17,6 +18,7 @@ module ModsDisplay
17
18
 
18
19
  def label
19
20
  return nil if @values.nil?
21
+
20
22
  displayLabel(@values.first)
21
23
  end
22
24
 
@@ -34,15 +36,10 @@ module ModsDisplay
34
36
  nil
35
37
  end
36
38
 
37
- def displayForm(element)
38
- return element unless element # basically return nil
39
- display = element.children.find { |c| c.name == 'displayForm' }
40
- return display.text if display
41
- end
42
-
43
39
  def displayLabel(element)
44
40
  return unless element.respond_to?(:attributes) &&
45
41
  element.attributes['displayLabel'].respond_to?(:value)
42
+
46
43
  "#{element.attributes['displayLabel'].value}:"
47
44
  end
48
45
 
@@ -52,8 +49,12 @@ module ModsDisplay
52
49
  display_fields.slice_when { |before, after| before.label != after.label }.map do |group|
53
50
  next group.first if group.length == 1
54
51
 
55
- ModsDisplay::Values.new(label: group.first.label, values: group.map(&:values).flatten)
52
+ ModsDisplay::Values.new(label: group.first.label, values: group.map(&:values).flatten(1))
56
53
  end
57
54
  end
55
+
56
+ def element_text(element)
57
+ element.xpath('.//text()').to_html.strip
58
+ end
58
59
  end
59
60
  end
@@ -1,11 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ModsDisplay
2
4
  class Form < Field
3
5
  def fields
4
6
  return [] unless form_fields.present?
7
+
5
8
  [
6
9
  ModsDisplay::Values.new(
7
10
  label: I18n.t('mods_display.form'),
8
- values: form_fields.map(&:text).uniq { |x| x.downcase.gsub(/\s/, '').gsub(/[[:punct:]]/, '') }
11
+ values: form_fields.map { |x| element_text(x) }.uniq { |x| x.downcase.gsub(/\s/, '').gsub(/[[:punct:]]/, '') }
9
12
  )
10
13
  ]
11
14
  end
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ModsDisplay
2
4
  class Genre < Field
3
5
  def fields
4
6
  return_fields = @values.map do |value|
5
- ModsDisplay::Values.new(label: displayLabel(value) || label, values: [value.text.strip.capitalize].flatten)
7
+ ModsDisplay::Values.new(label: displayLabel(value) || label, values: [element_text(value).capitalize].flatten)
6
8
  end
7
9
  collapse_fields(return_fields)
8
10
  end
@@ -1,7 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ModsDisplay
2
4
  class Geo < Field
3
5
  def fields
4
6
  return [] unless geo_extensions.present?
7
+
5
8
  extensions = geo_extensions.map(&method(:process_geo_extension))
6
9
  [
7
10
  ModsDisplay::Values.new(
@@ -16,8 +19,8 @@ module ModsDisplay
16
19
  def process_geo_extension(extension)
17
20
  rdf = Nokogiri::XML(extension.children.to_s)
18
21
  [
19
- rdf.xpath('//format').text[/format=(.*)$/, 1],
20
- rdf.xpath('//type').text[/#(.*)$/, 1]
22
+ rdf.xpath('//dc:format', dc: 'http://purl.org/dc/elements/1.1/').text[/format=(.*)$/, 1],
23
+ rdf.xpath('//dc:type', dc: 'http://purl.org/dc/elements/1.1/').text[/#(.*)$/, 1]
21
24
  ].compact.join('; ')
22
25
  end
23
26
 
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ModsDisplay
2
4
  class Identifier < Field
3
5
  def fields
4
6
  return_fields = @values.map do |value|
5
- ModsDisplay::Values.new(label: displayLabel(value) || identifier_label(value), values: [value.text])
7
+ ModsDisplay::Values.new(label: displayLabel(value) || identifier_label(value), values: [element_text(value)])
6
8
  end
7
9
  collapse_fields(return_fields)
8
10
  end
@@ -13,28 +15,29 @@ module ModsDisplay
13
15
  if element.attributes['type'].respond_to?(:value)
14
16
  return identifier_labels[element.attributes['type'].value] || "#{element.attributes['type'].value}:"
15
17
  end
18
+
16
19
  I18n.t('mods_display.identifier')
17
20
  end
18
21
 
19
22
  def identifier_labels
20
- { 'local' => I18n.t('mods_display.identifier'),
21
- 'isbn' => I18n.t('mods_display.isbn'),
22
- 'issn' => I18n.t('mods_display.issn'),
23
- 'issn-l' => I18n.t('mods_display.issn'),
24
- 'doi' => I18n.t('mods_display.doi'),
25
- 'hdl' => I18n.t('mods_display.handle'),
26
- 'isrc' => I18n.t('mods_display.isrc'),
27
- 'ismn' => I18n.t('mods_display.ismn'),
28
- 'issue number' => I18n.t('mods_display.issue_number'),
29
- 'lccn' => I18n.t('mods_display.lccn'),
30
- 'oclc' => I18n.t('mods_display.oclc'),
31
- 'matrix number' => I18n.t('mods_display.matrix_number'),
32
- 'music publisher' => I18n.t('mods_display.music_publisher'),
33
- 'music plate' => I18n.t('mods_display.music_plate'),
34
- 'sici' => I18n.t('mods_display.sici'),
35
- 'upc' => I18n.t('mods_display.upc'),
23
+ { 'local' => I18n.t('mods_display.identifier'),
24
+ 'isbn' => I18n.t('mods_display.isbn'),
25
+ 'issn' => I18n.t('mods_display.issn'),
26
+ 'issn-l' => I18n.t('mods_display.issn'),
27
+ 'doi' => I18n.t('mods_display.doi'),
28
+ 'hdl' => I18n.t('mods_display.handle'),
29
+ 'isrc' => I18n.t('mods_display.isrc'),
30
+ 'ismn' => I18n.t('mods_display.ismn'),
31
+ 'issue number' => I18n.t('mods_display.issue_number'),
32
+ 'lccn' => I18n.t('mods_display.lccn'),
33
+ 'oclc' => I18n.t('mods_display.oclc'),
34
+ 'matrix number' => I18n.t('mods_display.matrix_number'),
35
+ 'music publisher' => I18n.t('mods_display.music_publisher'),
36
+ 'music plate' => I18n.t('mods_display.music_plate'),
37
+ 'sici' => I18n.t('mods_display.sici'),
38
+ 'upc' => I18n.t('mods_display.upc'),
36
39
  'videorecording identifier' => I18n.t('mods_display.videorecording_identifier'),
37
- 'stock number' => I18n.t('mods_display.stock_number') }
40
+ 'stock number' => I18n.t('mods_display.stock_number') }
38
41
  end
39
42
  end
40
43
  end