@@ -263,7 +263,7 @@ extension Locale.Region {
263
263
264
264
internal static let _isoRegionCodes : [ String ] = {
265
265
var status = U_ZERO_ERROR
266
- let types = [ URGN_WORLD, URGN_CONTINENT, URGN_SUBCONTINENT, URGN_TERRITORY]
266
+ let types = [ URGN_WORLD, URGN_CONTINENT, URGN_SUBCONTINENT, URGN_TERRITORY, URGN_GROUPING ]
267
267
var codes : [ String ] = [ ]
268
268
for t in types {
269
269
status = U_ZERO_ERROR
@@ -275,6 +275,182 @@ extension Locale.Region {
275
275
}
276
276
return codes
277
277
} ( )
278
+
279
+ /// Categories of a region. See https://www.unicode.org/reports/tr35/tr35-35/tr35-info.html#Territory_Data
280
+ @available ( FoundationPreview 6 . 2 , * )
281
+ public struct Category : Codable , Sendable , Hashable , CustomDebugStringConvertible {
282
+ public var debugDescription : String {
283
+ switch inner {
284
+ case . world:
285
+ return " world "
286
+ case . continent:
287
+ return " continent "
288
+ case . subcontinent:
289
+ return " subcontinent "
290
+ case . territory:
291
+ return " territory "
292
+ case . grouping:
293
+ return " grouping "
294
+ }
295
+ }
296
+
297
+ enum Inner {
298
+ case world
299
+ case continent
300
+ case subcontinent
301
+ case territory
302
+ case grouping
303
+ }
304
+
305
+ var inner : Inner
306
+ fileprivate init ( _ inner: Inner ) {
307
+ self . inner = inner
308
+ }
309
+
310
+ var uregionType : URegionType {
311
+ switch inner {
312
+ case . world:
313
+ return URGN_WORLD
314
+ case . continent:
315
+ return URGN_CONTINENT
316
+ case . subcontinent:
317
+ return URGN_SUBCONTINENT
318
+ case . territory:
319
+ return URGN_TERRITORY
320
+ case . grouping:
321
+ return URGN_GROUPING
322
+ }
323
+ }
324
+
325
+ fileprivate init ? ( uregionType: URegionType ) {
326
+ switch uregionType {
327
+ case URGN_CONTINENT:
328
+ self = . init( . continent)
329
+ case URGN_WORLD:
330
+ self = . init( . world)
331
+ case URGN_SUBCONTINENT:
332
+ self = . init( . subcontinent)
333
+ case URGN_TERRITORY:
334
+ self = . init( . territory)
335
+ case URGN_GROUPING:
336
+ self = . init( . grouping)
337
+ default :
338
+ return nil
339
+ }
340
+ }
341
+
342
+ /// Category representing the whold world.
343
+ public static let world : Category = Category ( . world)
344
+
345
+ /// Category representing a continent, regions contained directly by world.
346
+ public static let continent : Category = Category ( . continent)
347
+
348
+ /// Category representing a sub-continent, regions contained directly by a continent.
349
+ public static let subcontinent : Category = Category ( . subcontinent)
350
+
351
+ /// Category representing a territory.
352
+ public static let territory : Category = Category ( . territory)
353
+
354
+ /// Category representing a grouping, regions that has a well defined membership.
355
+ public static let grouping : Category = Category ( . grouping)
356
+
357
+ public init ( from decoder: Decoder ) throws {
358
+ let container = try decoder. singleValueContainer ( )
359
+ let inner : Inner
360
+ switch try container. decode ( Int . self) {
361
+ case 0 :
362
+ inner = . world
363
+ case 1 :
364
+ inner = . continent
365
+ case 2 :
366
+ inner = . subcontinent
367
+ case 3 :
368
+ inner = . territory
369
+ case 4 :
370
+ inner = . grouping
371
+ default :
372
+ throw DecodingError . dataCorrupted ( . init( codingPath: decoder. codingPath, debugDescription: " Unknown Category " ) )
373
+ }
374
+ self = . init( inner)
375
+ }
376
+
377
+ public func encode( to encoder: Encoder ) throws {
378
+ var container = encoder. singleValueContainer ( )
379
+ switch inner {
380
+ case . world:
381
+ try container. encode ( 0 )
382
+ case . continent:
383
+ try container. encode ( 1 )
384
+ case . subcontinent:
385
+ try container. encode ( 2 )
386
+ case . territory:
387
+ try container. encode ( 3 )
388
+ case . grouping:
389
+ try container. encode ( 4 )
390
+
391
+ }
392
+ }
393
+ }
394
+
395
+ /// An array of regions matching the specified categories.
396
+ @available ( FoundationPreview 6 . 2 , * )
397
+ public static func isoRegions( ofCategory category: Category ) -> [ Locale . Region ] {
398
+ var status = U_ZERO_ERROR
399
+ let values = uregion_getAvailable ( category. uregionType, & status)
400
+ guard let values, status. isSuccess else {
401
+ return [ ]
402
+ }
403
+ return ICU . Enumerator ( enumerator: values) . elements. map { Locale . Region ( $0) }
404
+ }
405
+
406
+ /// The category of the region.
407
+ @available ( FoundationPreview 6 . 2 , * )
408
+ public var category : Category ? {
409
+ var status = U_ZERO_ERROR
410
+ let icuRegion = uregion_getRegionFromCode ( identifier, & status)
411
+ guard status. isSuccess, let icuRegion else {
412
+ return nil
413
+ }
414
+ let type = uregion_getType ( icuRegion)
415
+ return Category ( uregionType: type)
416
+ }
417
+
418
+ /// An array of the sub-regions, matching the specified category of the region.
419
+ @available ( FoundationPreview 6 . 2 , * )
420
+ public func subRegions( ofCategoy category: Category ) -> [ Locale . Region ] {
421
+ var status = U_ZERO_ERROR
422
+ let icuRegion = uregion_getRegionFromCode ( identifier, & status)
423
+ guard let icuRegion, status. isSuccess else {
424
+ return [ ]
425
+ }
426
+
427
+ status = U_ZERO_ERROR
428
+ let enumerator = uregion_getContainedRegionsOfType ( icuRegion, category. uregionType, & status)
429
+ guard let enumerator, status. isSuccess else {
430
+ return [ ]
431
+ }
432
+ return ICU . Enumerator ( enumerator: enumerator) . elements. map { Locale . Region ( $0) }
433
+ }
434
+
435
+ /// The subcontinent that contains this region, if any.
436
+ @available ( FoundationPreview 6 . 2 , * )
437
+ public var subcontinent : Locale . Region ? {
438
+ var status = U_ZERO_ERROR
439
+ let icuRegion = uregion_getRegionFromCode ( identifier, & status)
440
+ guard let icuRegion, status. isSuccess else {
441
+ return nil
442
+ }
443
+
444
+ guard let containing = uregion_getContainingRegionOfType ( icuRegion, URGN_SUBCONTINENT) else {
445
+ return nil
446
+ }
447
+
448
+ guard let code = String ( validatingCString: uregion_getRegionCode ( containing) ) else {
449
+ return nil
450
+ }
451
+
452
+ return Locale . Region ( code)
453
+ }
278
454
}
279
455
280
456
@available ( macOS 13 , iOS 16 , tvOS 16 , watchOS 9 , * )
0 commit comments