cocina_display 0.5.0 → 0.7.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.
@@ -0,0 +1,104 @@
1
+ require_relative "subject"
2
+ require_relative "../contributor"
3
+ require_relative "../title_builder"
4
+ require_relative "../dates/date"
5
+
6
+ module CocinaDisplay
7
+ module Subjects
8
+ # A descriptive value that can be part of a Subject.
9
+ class SubjectValue
10
+ attr_reader :cocina
11
+
12
+ # The type of the subject value, like "person", "title", or "time".
13
+ # @see https://github.com/sul-dlss/cocina-models/blob/main/docs/description_types.md#subject-part-types-for-structured-value
14
+ attr_accessor :type
15
+
16
+ # Create a SubjectValue from Cocina structured data.
17
+ # @param cocina [Hash] The Cocina structured data for the subject.
18
+ # @return [SubjectValue]
19
+ def self.from_cocina(cocina)
20
+ SUBJECT_VALUE_TYPES.fetch(cocina["type"], SubjectValue).new(cocina)
21
+ end
22
+
23
+ # All subject value types that should not be further destructured.
24
+ # @return [Array<String>]
25
+ def self.atomic_types
26
+ SUBJECT_VALUE_TYPES.keys
27
+ end
28
+
29
+ # Initialize a SubjectValue object with Cocina structured data.
30
+ # @param cocina [Hash] The Cocina structured data for the subject value.
31
+ def initialize(cocina)
32
+ @cocina = cocina
33
+ @type = cocina["type"]
34
+ end
35
+
36
+ # The display string for the subject value.
37
+ # Subclasses should override this method to provide specific formatting.
38
+ # @return [String]
39
+ def display_str
40
+ cocina["value"]
41
+ end
42
+ end
43
+
44
+ # A subject value representing a named entity.
45
+ class NameSubjectValue < SubjectValue
46
+ attr_reader :name
47
+
48
+ # Initialize a NameSubjectValue object with Cocina structured data.
49
+ # @param cocina [Hash] The Cocina structured data for the subject.
50
+ def initialize(cocina)
51
+ super
52
+ @name = Contributor::Name.new(cocina)
53
+ end
54
+
55
+ # Use the contributor name formatting rules for display.
56
+ # @return [String] The formatted name string, including life dates
57
+ # @see CocinaDisplay::Contributor::Name#display_str
58
+ def display_str
59
+ @name.display_str(with_date: true)
60
+ end
61
+ end
62
+
63
+ # A subject value representing an entity with a title.
64
+ class TitleSubjectValue < SubjectValue
65
+ # Construct a title string to use for display.
66
+ # @see CocinaDisplay::TitleBuilder.build
67
+ # @note Unclear how often structured title subjects occur "in the wild".
68
+ # @return [String]
69
+ def display_str
70
+ TitleBuilder.build([cocina])
71
+ end
72
+ end
73
+
74
+ # A subject value representing a date and/or time.
75
+ class TemporalSubjectValue < SubjectValue
76
+ attr_reader :date
77
+
78
+ def initialize(cocina)
79
+ super
80
+ @date = Dates::Date.from_cocina(cocina)
81
+ end
82
+
83
+ # @return [String] The formatted date/time string for display
84
+ def display_str
85
+ @date.qualified_value
86
+ end
87
+ end
88
+ end
89
+ end
90
+
91
+ # Map Cocina subject types to specific SubjectValue classes for rendering.
92
+ # @see SubjectValue#type
93
+ SUBJECT_VALUE_TYPES = {
94
+ "person" => CocinaDisplay::Subjects::NameSubjectValue,
95
+ "family" => CocinaDisplay::Subjects::NameSubjectValue,
96
+ "organization" => CocinaDisplay::Subjects::NameSubjectValue,
97
+ "conference" => CocinaDisplay::Subjects::NameSubjectValue,
98
+ "event" => CocinaDisplay::Subjects::NameSubjectValue,
99
+ "name" => CocinaDisplay::Subjects::NameSubjectValue,
100
+ "title" => CocinaDisplay::Subjects::TitleSubjectValue,
101
+ "time" => CocinaDisplay::Subjects::TemporalSubjectValue
102
+ # TODO: special handling for geospatial subjects
103
+ # "map coordinates", "bounding box coordinates", "point coordinates"
104
+ }.freeze
@@ -53,7 +53,7 @@ module CocinaDisplay
53
53
  def self.sort_title(titles, catalog_links: [])
54
54
  part_label = catalog_links.find { |link| link["catalog"] == "folio" }&.fetch("partLabel", nil)
55
55
  [new(strategy: :first, add_punctuation: false, only_one_parallel_value: false, part_label: part_label, sortable: true).build(titles)]
56
- .flatten.compact.map { |title| title.gsub(/[[:punct:]]*/, "").strip }
56
+ .flatten.compact.map { |title| title.gsub(/[[:punct:]]*/, "").squeeze(" ").strip }
57
57
  end
58
58
 
59
59
  # @param strategy [Symbol] ":first" selects a single title value based on precedence of
@@ -116,6 +116,7 @@ module CocinaDisplay
116
116
  end
117
117
 
118
118
  def extract_title(cocina_title)
119
+ return if cocina_title.blank?
119
120
  title_values = if cocina_title["value"]
120
121
  cocina_title["value"]
121
122
  elsif cocina_title["structuredValue"].present?
@@ -19,11 +19,12 @@ module CocinaDisplay
19
19
  end.delete_suffix(delimiter)
20
20
  end
21
21
 
22
- # Recursively flatten structured and parallel values in Cocina metadata.
22
+ # Recursively flatten structured, parallel, and grouped values in Cocina metadata.
23
23
  # Returns a list of hashes representing the "leaf" nodes with +value+ key.
24
24
  # @return [Array<Hash>] List of node hashes with "value" present
25
25
  # @param cocina [Hash] The Cocina structured data to flatten
26
26
  # @param output [Array] Used for recursion, should be empty on first call
27
+ # @param atomic_types [Array<String>] Types considered atomic; will not be flattened
27
28
  # @example simple value
28
29
  # cocina = { "value" => "John Doe", "type" => "name" }
29
30
  # Utils.flatten_nested_values(cocina)
@@ -36,14 +37,38 @@ module CocinaDisplay
36
37
  # cocina = { "parallelValue" => [{"value" => "foo" }, { "structuredValue" => [{"value" => "bar"}, {"value" => "baz"}] }] }
37
38
  # Utils.flatten_nested_values(cocina)
38
39
  # #=> [{"value" => "foo"}, {"value" => "foo"}, {"value" => "baz"}]
39
- def self.flatten_nested_values(cocina, output = [])
40
+ def self.flatten_nested_values(cocina, output = [], atomic_types: [])
40
41
  return [cocina] if cocina["value"].present?
41
- return cocina.flat_map { |node| flatten_nested_values(node, output) } if cocina.is_a?(Array)
42
+ return [cocina] if atomic_types.include?(cocina["type"])
43
+ return cocina.flat_map { |node| flatten_nested_values(node, output, atomic_types: atomic_types) } if cocina.is_a?(Array)
42
44
 
43
- nested_values = Array(cocina["structuredValue"]) + Array(cocina["parallelValue"])
45
+ nested_values = Array(cocina["structuredValue"]) + Array(cocina["parallelValue"]) + Array(cocina["groupedValue"])
44
46
  return output unless nested_values.any?
45
47
 
46
- nested_values.flat_map { |node| flatten_nested_values(node, output) }
48
+ nested_values.flat_map { |node| flatten_nested_values(node, output, atomic_types: atomic_types) }
49
+ end
50
+
51
+ # Recursively remove empty values from a hash, including nested hashes and arrays.
52
+ # @param hash [Hash] The hash to process
53
+ # @param output [Hash] Used for recursion, should be empty on first call
54
+ # @return [Hash] The hash with empty values removed
55
+ # @example
56
+ # hash = { "name" => "", "age" => nil, "address => { "city" => "Anytown", "state" => [] } }
57
+ # # Utils.remove_empty_values(hash)
58
+ # #=> { "address" => { "city" => "Anytown" } }
59
+ def self.deep_compact_blank(hash, output = {})
60
+ hash.each do |key, value|
61
+ if value.is_a?(Hash)
62
+ nested = deep_compact_blank(value)
63
+ output[key] = nested unless nested.empty?
64
+ elsif value.is_a?(Array)
65
+ compacted_array = value.map { |v| deep_compact_blank(v) }.reject(&:blank?)
66
+ output[key] = compacted_array unless compacted_array.empty?
67
+ elsif value.present?
68
+ output[key] = value
69
+ end
70
+ end
71
+ output
47
72
  end
48
73
  end
49
74
  end
@@ -2,5 +2,5 @@
2
2
 
3
3
  # :nodoc:
4
4
  module CocinaDisplay
5
- VERSION = "0.5.0" # :nodoc:
5
+ VERSION = "0.7.0" # :nodoc:
6
6
  end
@@ -0,0 +1,393 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CocinaDisplay
4
+ module Vocabularies
5
+ # Map of MARC country codes to country names.
6
+ # @see http://www.loc.gov/marc/countries/countries_code.html
7
+ # @note Ported from stanford-mods gem.
8
+ MARC_COUNTRY = {
9
+ "aa" => "Albania",
10
+ "abc" => "Alberta",
11
+ "ac" => "Ashmore and Cartier Islands", # discontinued
12
+ "aca" => "Australian Capital Territory",
13
+ "ae" => "Algeria",
14
+ "af" => "Afghanistan",
15
+ "ag" => "Argentina",
16
+ "ai" => "Armenia (Republic)",
17
+ "air" => "Armenian S.S.R.", # discontinued
18
+ "aj" => "Azerbaijan",
19
+ "ajr" => "Azerbaijan S.S.R.", # discontinued
20
+ "aku" => "Alaska",
21
+ "alu" => "Alabama",
22
+ "am" => "Anguilla",
23
+ "an" => "Andorra",
24
+ "ao" => "Angola",
25
+ "aq" => "Antigua and Barbuda",
26
+ "aru" => "Arkansas",
27
+ "as" => "American Samoa",
28
+ "at" => "Australia",
29
+ "au" => "Austria",
30
+ "aw" => "Aruba",
31
+ "ay" => "Antarctica",
32
+ "azu" => "Arizona",
33
+ "ba" => "Bahrain",
34
+ "bb" => "Barbados",
35
+ "bcc" => "British Columbia",
36
+ "bd" => "Burundi",
37
+ "be" => "Belgium",
38
+ "bf" => "Bahamas",
39
+ "bg" => "Bangladesh",
40
+ "bh" => "Belize",
41
+ "bi" => "British Indian Ocean Territory",
42
+ "bl" => "Brazil",
43
+ "bm" => "Bermuda Islands",
44
+ "bn" => "Bosnia and Herzegovina",
45
+ "bo" => "Bolivia",
46
+ "bp" => "Solomon Islands",
47
+ "br" => "Burma",
48
+ "bs" => "Botswana",
49
+ "bt" => "Bhutan",
50
+ "bu" => "Bulgaria",
51
+ "bv" => "Bouvet Island",
52
+ "bw" => "Belarus",
53
+ "bwr" => "Byelorussian S.S.R.", # discontinued
54
+ "bx" => "Brunei",
55
+ "ca" => "Caribbean Netherlands",
56
+ "cau" => "California",
57
+ "cb" => "Cambodia",
58
+ "cc" => "China",
59
+ "cd" => "Chad",
60
+ "ce" => "Sri Lanka",
61
+ "cf" => "Congo (Brazzaville)",
62
+ "cg" => "Congo (Democratic Republic)",
63
+ "ch" => "China (Republic : 1949- )",
64
+ "ci" => "Croatia",
65
+ "cj" => "Cayman Islands",
66
+ "ck" => "Colombia",
67
+ "cl" => "Chile",
68
+ "cm" => "Cameroon",
69
+ "cn" => "Canada", # discontinued
70
+ "co" => "Curaçao",
71
+ "cou" => "Colorado",
72
+ "cp" => "Canton and Enderbury Islands", # discontinued
73
+ "cq" => "Comoros",
74
+ "cr" => "Costa Rica",
75
+ "cs" => "Czechoslovakia", # discontinued
76
+ "ctu" => "Connecticut",
77
+ "cu" => "Cuba",
78
+ "cv" => "Cabo Verde",
79
+ "cw" => "Cook Islands",
80
+ "cx" => "Central African Republic",
81
+ "cy" => "Cyprus",
82
+ "cz" => "Canal Zone", # discontinued
83
+ "dcu" => "District of Columbia",
84
+ "deu" => "Delaware",
85
+ "dk" => "Denmark",
86
+ "dm" => "Benin",
87
+ "dq" => "Dominica",
88
+ "dr" => "Dominican Republic",
89
+ "ea" => "Eritrea",
90
+ "ec" => "Ecuador",
91
+ "eg" => "Equatorial Guinea",
92
+ "em" => "Timor-Leste",
93
+ "enk" => "England",
94
+ "er" => "Estonia",
95
+ "err" => "Estonia", # discontinued
96
+ "es" => "El Salvador",
97
+ "et" => "Ethiopia",
98
+ "fa" => "Faroe Islands",
99
+ "fg" => "French Guiana",
100
+ "fi" => "Finland",
101
+ "fj" => "Fiji",
102
+ "fk" => "Falkland Islands",
103
+ "flu" => "Florida",
104
+ "fm" => "Micronesia (Federated States)",
105
+ "fp" => "French Polynesia",
106
+ "fr" => "France",
107
+ "fs" => "Terres australes et antarctiques françaises",
108
+ "ft" => "Djibouti",
109
+ "gau" => "Georgia",
110
+ "gb" => "Kiribati",
111
+ "gd" => "Grenada",
112
+ "ge" => "Germany (East)", # discontinued
113
+ "gg" => "Guernsey",
114
+ "gh" => "Ghana",
115
+ "gi" => "Gibraltar",
116
+ "gl" => "Greenland",
117
+ "gm" => "Gambia",
118
+ "gn" => "Gilbert and Ellice Islands", # discontinued
119
+ "go" => "Gabon",
120
+ "gp" => "Guadeloupe",
121
+ "gr" => "Greece",
122
+ "gs" => "Georgia (Republic)",
123
+ "gsr" => "Georgian S.S.R.", # discontinued
124
+ "gt" => "Guatemala",
125
+ "gu" => "Guam",
126
+ "gv" => "Guinea",
127
+ "gw" => "Germany",
128
+ "gy" => "Guyana",
129
+ "gz" => "Gaza Strip",
130
+ "hiu" => "Hawaii",
131
+ "hk" => "Hong Kong", # discontinued
132
+ "hm" => "Heard and McDonald Islands",
133
+ "ho" => "Honduras",
134
+ "ht" => "Haiti",
135
+ "hu" => "Hungary",
136
+ "iau" => "Iowa",
137
+ "ic" => "Iceland",
138
+ "idu" => "Idaho",
139
+ "ie" => "Ireland",
140
+ "ii" => "India",
141
+ "ilu" => "Illinois",
142
+ "im" => "Isle of Man",
143
+ "inu" => "Indiana",
144
+ "io" => "Indonesia",
145
+ "iq" => "Iraq",
146
+ "ir" => "Iran",
147
+ "is" => "Israel",
148
+ "it" => "Italy",
149
+ "iu" => "Israel-Syria Demilitarized Zones", # discontinued
150
+ "iv" => "Côte d'Ivoire",
151
+ "iw" => "Israel-Jordan Demilitarized Zones", # discontinued
152
+ "iy" => "Iraq-Saudi Arabia Neutral Zone",
153
+ "ja" => "Japan",
154
+ "je" => "Jersey",
155
+ "ji" => "Johnston Atoll",
156
+ "jm" => "Jamaica",
157
+ "jn" => "Jan Mayen", # discontinued
158
+ "jo" => "Jordan",
159
+ "ke" => "Kenya",
160
+ "kg" => "Kyrgyzstan",
161
+ "kgr" => "Kirghiz S.S.R.", # discontinued
162
+ "kn" => "Korea (North)",
163
+ "ko" => "Korea (South)",
164
+ "ksu" => "Kansas",
165
+ "ku" => "Kuwait",
166
+ "kv" => "Kosovo",
167
+ "kyu" => "Kentucky",
168
+ "kz" => "Kazakhstan",
169
+ "kzr" => "Kazakh S.S.R.", # discontinued
170
+ "lau" => "Louisiana",
171
+ "lb" => "Liberia",
172
+ "le" => "Lebanon",
173
+ "lh" => "Liechtenstein",
174
+ "li" => "Lithuania",
175
+ "lir" => "Lithuania", # discontinued
176
+ "ln" => "Central and Southern Line Islands", # discontinued
177
+ "lo" => "Lesotho",
178
+ "ls" => "Laos",
179
+ "lu" => "Luxembourg",
180
+ "lv" => "Latvia",
181
+ "lvr" => "Latvia", # discontinued
182
+ "ly" => "Libya",
183
+ "mau" => "Massachusetts",
184
+ "mbc" => "Manitoba",
185
+ "mc" => "Monaco",
186
+ "mdu" => "Maryland",
187
+ "meu" => "Maine",
188
+ "mf" => "Mauritius",
189
+ "mg" => "Madagascar",
190
+ "mh" => "Macao", # discontinued
191
+ "miu" => "Michigan",
192
+ "mj" => "Montserrat",
193
+ "mk" => "Oman",
194
+ "ml" => "Mali",
195
+ "mm" => "Malta",
196
+ "mnu" => "Minnesota",
197
+ "mo" => "Montenegro",
198
+ "mou" => "Missouri",
199
+ "mp" => "Mongolia",
200
+ "mq" => "Martinique",
201
+ "mr" => "Morocco",
202
+ "msu" => "Mississippi",
203
+ "mtu" => "Montana",
204
+ "mu" => "Mauritania",
205
+ "mv" => "Moldova",
206
+ "mvr" => "Moldavian S.S.R.", # discontinued
207
+ "mw" => "Malawi",
208
+ "mx" => "Mexico",
209
+ "my" => "Malaysia",
210
+ "mz" => "Mozambique",
211
+ "na" => "Netherlands Antilles", # discontinued
212
+ "nbu" => "Nebraska",
213
+ "ncu" => "North Carolina",
214
+ "ndu" => "North Dakota",
215
+ "ne" => "Netherlands",
216
+ "nfc" => "Newfoundland and Labrador",
217
+ "ng" => "Niger",
218
+ "nhu" => "New Hampshire",
219
+ "nik" => "Northern Ireland",
220
+ "nju" => "New Jersey",
221
+ "nkc" => "New Brunswick",
222
+ "nl" => "New Caledonia",
223
+ "nm" => "Northern Mariana Islands", # discontinued
224
+ "nmu" => "New Mexico",
225
+ "nn" => "Vanuatu",
226
+ "no" => "Norway",
227
+ "np" => "Nepal",
228
+ "nq" => "Nicaragua",
229
+ "nr" => "Nigeria",
230
+ "nsc" => "Nova Scotia",
231
+ "ntc" => "Northwest Territories",
232
+ "nu" => "Nauru",
233
+ "nuc" => "Nunavut",
234
+ "nvu" => "Nevada",
235
+ "nw" => "Northern Mariana Islands",
236
+ "nx" => "Norfolk Island",
237
+ "nyu" => "New York (State)",
238
+ "nz" => "New Zealand",
239
+ "ohu" => "Ohio",
240
+ "oku" => "Oklahoma",
241
+ "onc" => "Ontario",
242
+ "oru" => "Oregon",
243
+ "ot" => "Mayotte",
244
+ "pau" => "Pennsylvania",
245
+ "pc" => "Pitcairn Island",
246
+ "pe" => "Peru",
247
+ "pf" => "Paracel Islands",
248
+ "pg" => "Guinea-Bissau",
249
+ "ph" => "Philippines",
250
+ "pic" => "Prince Edward Island",
251
+ "pk" => "Pakistan",
252
+ "pl" => "Poland",
253
+ "pn" => "Panama",
254
+ "po" => "Portugal",
255
+ "pp" => "Papua New Guinea",
256
+ "pr" => "Puerto Rico",
257
+ "pt" => "Portuguese Timor", # discontinued
258
+ "pw" => "Palau",
259
+ "py" => "Paraguay",
260
+ "qa" => "Qatar",
261
+ "qea" => "Queensland",
262
+ "quc" => "Québec (Province)",
263
+ "rb" => "Serbia",
264
+ "re" => "Réunion",
265
+ "rh" => "Zimbabwe",
266
+ "riu" => "Rhode Island",
267
+ "rm" => "Romania",
268
+ "ru" => "Russia (Federation)",
269
+ "rur" => "Russian S.F.S.R.", # discontinued
270
+ "rw" => "Rwanda",
271
+ "ry" => "Ryukyu Islands, Southern", # discontinued
272
+ "sa" => "South Africa",
273
+ "sb" => "Svalbard", # discontinued
274
+ "sc" => "Saint-Barthélemy",
275
+ "scu" => "South Carolina",
276
+ "sd" => "South Sudan",
277
+ "sdu" => "South Dakota",
278
+ "se" => "Seychelles",
279
+ "sf" => "Sao Tome and Principe",
280
+ "sg" => "Senegal",
281
+ "sh" => "Spanish North Africa",
282
+ "si" => "Singapore",
283
+ "sj" => "Sudan",
284
+ "sk" => "Sikkim", # discontinued
285
+ "sl" => "Sierra Leone",
286
+ "sm" => "San Marino",
287
+ "sn" => "Sint Maarten",
288
+ "snc" => "Saskatchewan",
289
+ "so" => "Somalia",
290
+ "sp" => "Spain",
291
+ "sq" => "Eswatini",
292
+ "sr" => "Surinam",
293
+ "ss" => "Western Sahara",
294
+ "st" => "Saint-Martin",
295
+ "stk" => "Scotland",
296
+ "su" => "Saudi Arabia",
297
+ "sv" => "Swan Islands", # discontinued
298
+ "sw" => "Sweden",
299
+ "sx" => "Namibia",
300
+ "sy" => "Syria",
301
+ "sz" => "Switzerland",
302
+ "ta" => "Tajikistan",
303
+ "tar" => "Tajik S.S.R.", # discontinued
304
+ "tc" => "Turks and Caicos Islands",
305
+ "tg" => "Togo",
306
+ "th" => "Thailand",
307
+ "ti" => "Tunisia",
308
+ "tk" => "Turkmenistan",
309
+ "tkr" => "Turkmen S.S.R.", # discontinued
310
+ "tl" => "Tokelau",
311
+ "tma" => "Tasmania",
312
+ "tnu" => "Tennessee",
313
+ "to" => "Tonga",
314
+ "tr" => "Trinidad and Tobago",
315
+ "ts" => "United Arab Emirates",
316
+ "tt" => "Trust Territory of the Pacific Islands", # discontinued
317
+ "tu" => "Turkey",
318
+ "tv" => "Tuvalu",
319
+ "txu" => "Texas",
320
+ "tz" => "Tanzania",
321
+ "ua" => "Egypt",
322
+ "uc" => "United States Misc. Caribbean Islands",
323
+ "ug" => "Uganda",
324
+ "ui" => "United Kingdom Misc. Islands", # discontinued
325
+ "uik" => "United Kingdom Misc. Islands",
326
+ "uk" => "United Kingdom", # discontinued
327
+ "un" => "Ukraine",
328
+ "unr" => "Ukraine", # discontinued
329
+ "up" => "United States Misc. Pacific Islands",
330
+ "ur" => "Soviet Union", # discontinued
331
+ "us" => "United States", # discontinued
332
+ "utu" => "Utah",
333
+ "uv" => "Burkina Faso",
334
+ "uy" => "Uruguay",
335
+ "uz" => "Uzbekistan",
336
+ "uzr" => "Uzbek S.S.R.", # discontinued
337
+ "vau" => "Virginia",
338
+ "vb" => "British Virgin Islands",
339
+ "vc" => "Vatican City",
340
+ "ve" => "Venezuela",
341
+ "vi" => "Virgin Islands of the United States",
342
+ "vm" => "Vietnam",
343
+ "vn" => "Vietnam, North", # discontinued
344
+ "vp" => "Various places",
345
+ "vra" => "Victoria",
346
+ "vs" => "Vietnam, South", # discontinued
347
+ "vtu" => "Vermont",
348
+ "wau" => "Washington (State)",
349
+ "wb" => "West Berlin", # discontinued
350
+ "wea" => "Western Australia",
351
+ "wf" => "Wallis and Futuna",
352
+ "wiu" => "Wisconsin",
353
+ "wj" => "West Bank of the Jordan River",
354
+ "wk" => "Wake Island",
355
+ "wlk" => "Wales",
356
+ "ws" => "Samoa",
357
+ "wvu" => "West Virginia",
358
+ "wyu" => "Wyoming",
359
+ "xa" => "Christmas Island (Indian Ocean)",
360
+ "xb" => "Cocos (Keeling) Islands",
361
+ "xc" => "Maldives",
362
+ "xd" => "Saint Kitts-Nevis",
363
+ "xe" => "Marshall Islands",
364
+ "xf" => "Midway Islands",
365
+ "xga" => "Coral Sea Islands Territory",
366
+ "xh" => "Niue",
367
+ "xi" => "Saint Kitts-Nevis-Anguilla", # discontinued
368
+ "xj" => "Saint Helena",
369
+ "xk" => "Saint Lucia",
370
+ "xl" => "Saint Pierre and Miquelon",
371
+ "xm" => "Saint Vincent and the Grenadines",
372
+ "xn" => "North Macedonia",
373
+ "xna" => "New South Wales",
374
+ "xo" => "Slovakia",
375
+ "xoa" => "Northern Territory",
376
+ "xp" => "Spratly Island",
377
+ "xr" => "Czech Republic",
378
+ "xra" => "South Australia",
379
+ "xs" => "South Georgia and the South Sandwich Islands",
380
+ "xv" => "Slovenia",
381
+ "xx" => "No place, unknown, or undetermined",
382
+ "xxc" => "Canada",
383
+ "xxk" => "United Kingdom",
384
+ "xxr" => "Soviet Union", # discontinued
385
+ "xxu" => "United States",
386
+ "ye" => "Yemen",
387
+ "ykc" => "Yukon Territory",
388
+ "ys" => "Yemen (People's Democratic Republic)", # discontinued
389
+ "yu" => "Serbia and Montenegro", # discontinued
390
+ "za" => "Zambia"
391
+ }.freeze
392
+ end
393
+ end