@swiftbrowser/swift 0.1.0 → 0.3.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.
- package/Package.swift +200 -9
- package/README.md +7 -9
- package/Sources/AVFoundation/AVFoundation.swift +131 -0
- package/Sources/AVFoundation/Asset.swift +487 -0
- package/Sources/AVFoundation/AudioSession.swift +144 -0
- package/Sources/AVFoundation/CMTime.swift +303 -0
- package/Sources/AVFoundation/Capture.swift +688 -0
- package/Sources/AVFoundation/Player.swift +749 -0
- package/Sources/AVFoundation/PlayerView.swift +117 -0
- package/Sources/AVKit/AVKit.swift +132 -0
- package/Sources/Accelerate/Accelerate.swift +122 -0
- package/Sources/AppIntents/AppIntents.swift +144 -0
- package/Sources/AppIntents/Parameters.swift +220 -0
- package/Sources/AppIntents/Shortcuts.swift +159 -0
- package/Sources/AudioToolbox/AudioToolbox.swift +55 -0
- package/Sources/AuthenticationServices/AuthenticationServices.swift +257 -0
- package/Sources/AuthenticationServices/Authorization.swift +251 -0
- package/Sources/Charts/Charts.swift +0 -4
- package/Sources/Combine/AnyCancellable.swift +65 -0
- package/Sources/Combine/AnyPublisher.swift +148 -0
- package/Sources/Combine/AnySubscriber.swift +230 -0
- package/Sources/Combine/Cancellable.swift +35 -0
- package/Sources/Combine/Codable.swift +29 -0
- package/Sources/Combine/CombineIdentifier.swift +45 -0
- package/Sources/Combine/Concurrency/ConcurrencyHelpers.swift +24 -0
- package/Sources/Combine/Concurrency/Future+Concurrency.swift +136 -0
- package/Sources/Combine/Concurrency/GENERATED-Publisher+Concurrency.swift +397 -0
- package/Sources/Combine/CurrentValueSubject.swift +252 -0
- package/Sources/Combine/CustomCombineIdentifierConvertible.swift +28 -0
- package/Sources/Combine/Future.swift +228 -0
- package/Sources/Combine/GENERATED-RootProtocols.swift +426 -0
- package/Sources/Combine/Helpers/ConduitBase.swift +40 -0
- package/Sources/Combine/Helpers/ConduitList.swift +59 -0
- package/Sources/Combine/Helpers/DebugHook.swift +177 -0
- package/Sources/Combine/Helpers/FilterProducer.swift +174 -0
- package/Sources/Combine/Helpers/Locking.swift +30 -0
- package/Sources/Combine/Helpers/PartialCompletion.swift +26 -0
- package/Sources/Combine/Helpers/ReduceProducer.swift +249 -0
- package/Sources/Combine/Helpers/SubjectSubscriber.swift +96 -0
- package/Sources/Combine/Helpers/SubscriberTap.swift +69 -0
- package/Sources/Combine/Helpers/SubscriptionStatus.swift +33 -0
- package/Sources/Combine/Helpers/SubscriptionTap.swift +33 -0
- package/Sources/Combine/Helpers/Utils.swift +30 -0
- package/Sources/Combine/Helpers/Violations.swift +33 -0
- package/Sources/Combine/ImmediateScheduler.swift +178 -0
- package/Sources/Combine/LICENSE +21 -0
- package/Sources/Combine/NotificationPublisher.swift +48 -0
- package/Sources/Combine/ObjectWillChange.swift +46 -0
- package/Sources/Combine/PassthroughSubject.swift +222 -0
- package/Sources/Combine/Publisher+Subscribe.swift +53 -0
- package/Sources/Combine/Publishers/Deferred.swift +48 -0
- package/Sources/Combine/Publishers/Empty.swift +54 -0
- package/Sources/Combine/Publishers/Fail.swift +42 -0
- package/Sources/Combine/Publishers/GENERATED-Publishers.Catch.swift +673 -0
- package/Sources/Combine/Publishers/GENERATED-Publishers.Encode.swift +388 -0
- package/Sources/Combine/Publishers/GENERATED-Publishers.MapKeyPath.swift +384 -0
- package/Sources/Combine/Publishers/Just.swift +313 -0
- package/Sources/Combine/Publishers/Optional.Publisher.swift +315 -0
- package/Sources/Combine/Publishers/Publishers.AllSatisfy.swift +217 -0
- package/Sources/Combine/Publishers/Publishers.AssertNoFailure.swift +158 -0
- package/Sources/Combine/Publishers/Publishers.Autoconnect.swift +193 -0
- package/Sources/Combine/Publishers/Publishers.Breakpoint.swift +231 -0
- package/Sources/Combine/Publishers/Publishers.Buffer.swift +356 -0
- package/Sources/Combine/Publishers/Publishers.Collect.swift +102 -0
- package/Sources/Combine/Publishers/Publishers.CollectByCount.swift +195 -0
- package/Sources/Combine/Publishers/Publishers.CompactMap.swift +241 -0
- package/Sources/Combine/Publishers/Publishers.Comparison.swift +371 -0
- package/Sources/Combine/Publishers/Publishers.Concatenate.swift +474 -0
- package/Sources/Combine/Publishers/Publishers.Contains.swift +293 -0
- package/Sources/Combine/Publishers/Publishers.Count.swift +87 -0
- package/Sources/Combine/Publishers/Publishers.Debounce.swift +332 -0
- package/Sources/Combine/Publishers/Publishers.Delay.swift +269 -0
- package/Sources/Combine/Publishers/Publishers.Drop.swift +155 -0
- package/Sources/Combine/Publishers/Publishers.DropUntilOutput.swift +283 -0
- package/Sources/Combine/Publishers/Publishers.DropWhile.swift +378 -0
- package/Sources/Combine/Publishers/Publishers.Filter.swift +248 -0
- package/Sources/Combine/Publishers/Publishers.First.swift +261 -0
- package/Sources/Combine/Publishers/Publishers.FlatMap.swift +533 -0
- package/Sources/Combine/Publishers/Publishers.HandleEvents.swift +260 -0
- package/Sources/Combine/Publishers/Publishers.IgnoreOutput.swift +109 -0
- package/Sources/Combine/Publishers/Publishers.Last.swift +252 -0
- package/Sources/Combine/Publishers/Publishers.MakeConnectable.swift +82 -0
- package/Sources/Combine/Publishers/Publishers.Map.swift +351 -0
- package/Sources/Combine/Publishers/Publishers.MapError.swift +140 -0
- package/Sources/Combine/Publishers/Publishers.MeasureInterval.swift +187 -0
- package/Sources/Combine/Publishers/Publishers.Multicast.swift +284 -0
- package/Sources/Combine/Publishers/Publishers.Output.swift +241 -0
- package/Sources/Combine/Publishers/Publishers.PrefixUntilOutput.swift +197 -0
- package/Sources/Combine/Publishers/Publishers.PrefixWhile.swift +176 -0
- package/Sources/Combine/Publishers/Publishers.Print.swift +206 -0
- package/Sources/Combine/Publishers/Publishers.ReceiveOn.swift +227 -0
- package/Sources/Combine/Publishers/Publishers.Reduce.swift +204 -0
- package/Sources/Combine/Publishers/Publishers.RemoveDuplicates.swift +264 -0
- package/Sources/Combine/Publishers/Publishers.ReplaceEmpty.swift +201 -0
- package/Sources/Combine/Publishers/Publishers.ReplaceError.swift +211 -0
- package/Sources/Combine/Publishers/Publishers.Retry.swift +274 -0
- package/Sources/Combine/Publishers/Publishers.Scan.swift +331 -0
- package/Sources/Combine/Publishers/Publishers.Sequence.swift +437 -0
- package/Sources/Combine/Publishers/Publishers.SetFailureType.swift +120 -0
- package/Sources/Combine/Publishers/Publishers.Share.swift +104 -0
- package/Sources/Combine/Publishers/Publishers.SubscribeOn.swift +197 -0
- package/Sources/Combine/Publishers/Publishers.SwitchToLatest.swift +444 -0
- package/Sources/Combine/Publishers/Publishers.Throttle.swift +359 -0
- package/Sources/Combine/Publishers/Publishers.Timeout.swift +301 -0
- package/Sources/Combine/Publishers/Publishers.Zip.swift +720 -0
- package/Sources/Combine/Publishers/Publishers.swift +13 -0
- package/Sources/Combine/Publishers/Record.swift +237 -0
- package/Sources/Combine/Publishers/Result.Publisher.swift +337 -0
- package/Sources/Combine/Result.swift +46 -0
- package/Sources/Combine/Scheduler.swift +89 -0
- package/Sources/Combine/Schedulers.swift +211 -0
- package/Sources/Combine/Subject+Void.swift +17 -0
- package/Sources/Combine/Subscriber+Void.swift +20 -0
- package/Sources/Combine/Subscribers/Subscribers.Assign.swift +175 -0
- package/Sources/Combine/Subscribers/Subscribers.Completion.swift +90 -0
- package/Sources/Combine/Subscribers/Subscribers.Demand.swift +476 -0
- package/Sources/Combine/Subscribers/Subscribers.Sink.swift +206 -0
- package/Sources/Combine/Subscribers/Subscribers.swift +9 -0
- package/Sources/Combine/Subscription.swift +22 -0
- package/Sources/Combine/Subscriptions.swift +42 -0
- package/Sources/Combine/SwiftUIObservation.swift +10 -0
- package/Sources/Combine/_Introspection.swift +107 -0
- package/Sources/CommonCrypto/CommonCrypto.swift +80 -0
- package/Sources/Compression/Compression.swift +365 -0
- package/Sources/Compression/Deflate.swift +407 -0
- package/Sources/Compression/Zlib.swift +179 -0
- package/Sources/CoreFoundationTypes/CoreFoundationTypes.swift +101 -0
- package/Sources/CoreGraphics/CoreGraphics.swift +411 -0
- package/Sources/CoreGraphics/ProjectionTransforms.swift +28 -0
- package/Sources/CoreHaptics/CoreHaptics.swift +399 -0
- package/Sources/CoreImage/CIColor.swift +41 -0
- package/Sources/CoreImage/CIContext.swift +31 -0
- package/Sources/CoreImage/CIDetector.swift +57 -0
- package/Sources/CoreImage/CIFilter.swift +243 -0
- package/Sources/CoreImage/CIFilterBuiltins.swift +136 -0
- package/Sources/CoreImage/CIImage.swift +162 -0
- package/Sources/CoreImage/QRCode.swift +393 -0
- package/Sources/CoreLocation/Coordinate.swift +30 -0
- package/Sources/CoreLocation/Location.swift +65 -0
- package/Sources/CoreML/Evaluation.swift +414 -0
- package/Sources/CoreML/FeatureValue.swift +266 -0
- package/Sources/CoreML/Model.swift +269 -0
- package/Sources/CoreML/MultiArray.swift +92 -0
- package/Sources/CoreML/Specification.swift +233 -0
- package/Sources/CoreText/CoreText.swift +64 -0
- package/Sources/CoreTransferable/CoreTransferable.swift +606 -0
- package/Sources/CryptoKit/ChaChaPoly.swift +258 -0
- package/Sources/CryptoKit/CryptoKit.swift +375 -0
- package/Sources/CryptoKit/Curve25519.swift +317 -0
- package/Sources/CryptoKit/P256.swift +321 -0
- package/Sources/FoundationBridge/AppStorage.swift +165 -0
- package/Sources/FoundationBridge/AssetResources.swift +90 -0
- package/Sources/FoundationBridge/AsyncBytes.swift +128 -0
- package/Sources/FoundationBridge/AsyncImage.swift +116 -0
- package/Sources/FoundationBridge/AttributedStrings.swift +16 -0
- package/Sources/{_FoundationCommon → FoundationBridge}/Bundle.swift +41 -4
- package/Sources/FoundationBridge/CombineExports.swift +8 -0
- package/Sources/FoundationBridge/CoreFoundation.swift +77 -0
- package/Sources/FoundationBridge/Dispatch.swift +458 -0
- package/Sources/FoundationBridge/EnvironmentFoundation.swift +41 -0
- package/Sources/FoundationBridge/FileImporter.swift +128 -0
- package/Sources/FoundationBridge/FileSystem.swift +68 -0
- package/Sources/FoundationBridge/FoundationBridge.swift +24 -72
- package/Sources/FoundationBridge/HostResolution.swift +31 -0
- package/Sources/FoundationBridge/ItemProvider.swift +655 -0
- package/Sources/FoundationBridge/KeyValueObserving.swift +138 -0
- package/Sources/FoundationBridge/Links.swift +127 -0
- package/Sources/FoundationBridge/ListFormatter.swift +32 -0
- package/Sources/FoundationBridge/Localization.swift +149 -0
- package/Sources/FoundationBridge/Locks.swift +50 -0
- package/Sources/FoundationBridge/Markdown.swift +527 -0
- package/Sources/FoundationBridge/MeasurementFormat.swift +95 -0
- package/Sources/FoundationBridge/NotificationCenter.swift +154 -0
- package/Sources/FoundationBridge/OpenURLHandlers.swift +60 -0
- package/Sources/FoundationBridge/Operation.swift +195 -0
- package/Sources/FoundationBridge/ProcessInfo.swift +32 -0
- package/Sources/FoundationBridge/RelativeDateTimeFormatter.swift +136 -0
- package/Sources/FoundationBridge/SceneStorage.swift +78 -0
- package/Sources/FoundationBridge/SecurityExports.swift +8 -0
- package/Sources/FoundationBridge/Selector.swift +226 -0
- package/Sources/FoundationBridge/StringComparators.swift +17 -0
- package/Sources/FoundationBridge/Table.swift +211 -0
- package/Sources/FoundationBridge/TextAttributes.swift +42 -0
- package/Sources/FoundationBridge/Timer.swift +6 -1
- package/Sources/FoundationBridge/URLSession.swift +718 -0
- package/Sources/{_FoundationCommon → FoundationBridge}/UserDefaults.swift +17 -4
- package/Sources/FoundationBridge/WebAuthRedirects.swift +227 -0
- package/Sources/FoundationBridge/WebSocket.swift +187 -0
- package/Sources/FoundationModels/FoundationModels.swift +1098 -0
- package/Sources/ImageIO/GIF.swift +72 -0
- package/Sources/ImageIO/ImageIO.swift +248 -0
- package/Sources/ImageIO/ImageTypes.swift +28 -0
- package/Sources/LinkPresentation/LinkPresentation.swift +249 -0
- package/Sources/LocalAuthentication/LAContext.swift +86 -0
- package/Sources/MapKit/Geometry.swift +223 -0
- package/Sources/MapKit/Map.swift +638 -0
- package/Sources/MapKit/MapView.swift +229 -0
- package/Sources/MobileCoreServices/MobileCoreServices.swift +28 -0
- package/Sources/NaturalLanguage/LanguageIdentifier.swift +317 -0
- package/Sources/NaturalLanguage/NaturalLanguage.swift +135 -0
- package/Sources/OSLog/OSLog.swift +6 -0
- package/Sources/Photos/Photos.swift +572 -0
- package/Sources/PhotosUI/PhotosPicker.swift +248 -0
- package/Sources/PhotosUI/PhotosPickerItem.swift +261 -0
- package/Sources/QuickLook/QuickLook.swift +168 -0
- package/Sources/SafariServices/SafariServices.swift +106 -0
- package/Sources/Security/Security.swift +332 -0
- package/Sources/StoreKit/AppStore.swift +207 -0
- package/Sources/StoreKit/Products.swift +280 -0
- package/Sources/StoreKit/RequestReview.swift +28 -0
- package/Sources/StoreKit/StoreKit1.swift +486 -0
- package/Sources/StoreKit/StoreViews.swift +428 -0
- package/Sources/StoreKit/Transactions.swift +197 -0
- package/Sources/SwiftData/Macros.swift +58 -0
- package/Sources/SwiftData/ModelActor.swift +38 -0
- package/Sources/SwiftData/ModelContainer.swift +129 -0
- package/Sources/SwiftData/ModelContext.swift +451 -0
- package/Sources/SwiftData/PersistentModel.swift +238 -0
- package/Sources/SwiftData/Properties.swift +261 -0
- package/Sources/SwiftData/Query.swift +256 -0
- package/Sources/SwiftData/Schema.swift +192 -0
- package/Sources/SwiftData/Store.swift +146 -0
- package/Sources/SwiftData/StringPredicates.swift +125 -0
- package/Sources/SwiftDataMacros/EntryMacro.swift +82 -0
- package/Sources/SwiftDataMacros/GenerableMacro.swift +183 -0
- package/Sources/SwiftDataMacros/ModelActorMacro.swift +46 -0
- package/Sources/SwiftDataMacros/ModelMacro.swift +260 -0
- package/Sources/SwiftDataMacros/Plugin.swift +18 -0
- package/Sources/SwiftDataMacros/PredicateMacro.swift +1244 -0
- package/Sources/SwiftUI/API/Accessibility.swift +55 -6
- package/Sources/SwiftUI/API/AccessibilityExtras.swift +33 -0
- package/Sources/SwiftUI/API/AccessibilityFocus.swift +63 -0
- package/Sources/SwiftUI/API/Alert.swift +1 -1
- package/Sources/SwiftUI/API/AlignmentGuides.swift +68 -0
- package/Sources/SwiftUI/API/Animation.swift +37 -5
- package/Sources/SwiftUI/API/App.swift +31 -1
- package/Sources/SwiftUI/API/BindingCollections.swift +35 -0
- package/Sources/SwiftUI/API/BindingForEach.swift +27 -0
- package/Sources/SwiftUI/API/Bitmaps.swift +254 -0
- package/Sources/SwiftUI/API/ButtonStyles.swift +96 -0
- package/Sources/SwiftUI/API/Canvas.swift +337 -0
- package/Sources/SwiftUI/API/Color.swift +104 -28
- package/Sources/SwiftUI/API/ColorEffects.swift +52 -0
- package/Sources/SwiftUI/API/ColorPickerAndLabeledContent.swift +138 -0
- package/Sources/SwiftUI/API/ColorSpaces.swift +161 -0
- package/Sources/SwiftUI/API/Commands.swift +106 -0
- package/Sources/SwiftUI/API/ContainerBackground.swift +28 -0
- package/Sources/SwiftUI/API/ContentShape.swift +33 -0
- package/Sources/SwiftUI/API/ContentSizeCategory.swift +35 -0
- package/Sources/SwiftUI/API/ContentUnavailable.swift +101 -0
- package/Sources/SwiftUI/API/Controls.swift +59 -27
- package/Sources/SwiftUI/API/CustomLayout.swift +49 -10
- package/Sources/SwiftUI/API/CustomToolbarContent.swift +46 -0
- package/Sources/SwiftUI/API/Decorations.swift +1 -1
- package/Sources/SwiftUI/API/EditMode.swift +25 -82
- package/Sources/SwiftUI/API/Effects.swift +125 -10
- package/Sources/SwiftUI/API/Entry.swift +8 -0
- package/Sources/SwiftUI/API/Environment.swift +100 -9
- package/Sources/SwiftUI/API/EnvironmentSendable.swift +6 -0
- package/Sources/SwiftUI/API/Focus.swift +11 -3
- package/Sources/SwiftUI/API/Gauge.swift +26 -10
- package/Sources/SwiftUI/API/GeometryEffects.swift +232 -0
- package/Sources/SwiftUI/API/GeometryReader.swift +13 -2
- package/Sources/SwiftUI/API/GestureState.swift +65 -0
- package/Sources/SwiftUI/API/Gestures.swift +1 -1
- package/Sources/SwiftUI/API/Glass.swift +127 -0
- package/Sources/SwiftUI/API/Grid.swift +1 -1
- package/Sources/SwiftUI/API/GroupBoxAndControlGroup.swift +130 -0
- package/Sources/SwiftUI/API/KeyPress.swift +54 -0
- package/Sources/SwiftUI/API/LabelStyle.swift +13 -6
- package/Sources/SwiftUI/API/Layout.swift +71 -12
- package/Sources/SwiftUI/API/LazyStacks.swift +49 -0
- package/Sources/SwiftUI/API/LegacyAlert.swift +97 -0
- package/Sources/SwiftUI/API/Lists.swift +61 -6
- package/Sources/SwiftUI/API/Menu.swift +8 -1
- package/Sources/SwiftUI/API/Modifiers.swift +88 -12
- package/Sources/SwiftUI/API/MoreModifiers.swift +100 -0
- package/Sources/SwiftUI/API/Namespace.swift +44 -0
- package/Sources/SwiftUI/API/Navigation.swift +24 -9
- package/Sources/SwiftUI/API/NavigationDestinationBindings.swift +55 -0
- package/Sources/SwiftUI/API/ObservableObject.swift +16 -2
- package/Sources/SwiftUI/API/Path.swift +543 -0
- package/Sources/SwiftUI/API/Phase8Controls.swift +48 -31
- package/Sources/SwiftUI/API/PhaseAnimator.swift +130 -0
- package/Sources/SwiftUI/API/Picker.swift +14 -4
- package/Sources/SwiftUI/API/PointerAndKeyboard.swift +77 -0
- package/Sources/SwiftUI/API/Popover.swift +41 -0
- package/Sources/SwiftUI/API/Preferences.swift +262 -0
- package/Sources/SwiftUI/API/Presentation.swift +17 -4
- package/Sources/SwiftUI/API/PresentationExtras.swift +60 -0
- package/Sources/SwiftUI/API/Primitives.swift +168 -39
- package/Sources/SwiftUI/API/Publishers.swift +4 -4
- package/Sources/SwiftUI/API/Redaction.swift +75 -0
- package/Sources/SwiftUI/API/RowActions.swift +3 -2
- package/Sources/SwiftUI/API/Scenes.swift +131 -0
- package/Sources/SwiftUI/API/ScrollModifiers.swift +132 -0
- package/Sources/SwiftUI/API/ScrollTargets.swift +3 -3
- package/Sources/SwiftUI/API/ScrollViewReader.swift +144 -0
- package/Sources/SwiftUI/API/SectionSupplements.swift +86 -0
- package/Sources/SwiftUI/API/Shape.swift +214 -0
- package/Sources/SwiftUI/API/ShapeStyle.swift +187 -8
- package/Sources/SwiftUI/API/ShapeTrim.swift +78 -0
- package/Sources/SwiftUI/API/Sharing.swift +193 -0
- package/Sources/SwiftUI/API/StackLayouts.swift +199 -0
- package/Sources/SwiftUI/API/State.swift +18 -1
- package/Sources/SwiftUI/API/StatusGaps.swift +121 -0
- package/Sources/SwiftUI/API/SymbolEffects.swift +203 -0
- package/Sources/SwiftUI/API/TabContent.swift +841 -0
- package/Sources/SwiftUI/API/Table.swift +382 -0
- package/Sources/SwiftUI/API/Tabs.swift +148 -4
- package/Sources/SwiftUI/API/Tasks.swift +2 -2
- package/Sources/SwiftUI/API/Text.swift +386 -0
- package/Sources/SwiftUI/API/TextDates.swift +61 -0
- package/Sources/SwiftUI/API/TextDecorations.swift +52 -0
- package/Sources/SwiftUI/API/TextFieldAxis.swift +103 -0
- package/Sources/SwiftUI/API/TextFieldCommit.swift +52 -0
- package/Sources/SwiftUI/API/Toolbar.swift +52 -16
- package/Sources/SwiftUI/API/Transfer.swift +145 -0
- package/Sources/SwiftUI/API/UnevenRoundedRectangle.swift +66 -0
- package/Sources/SwiftUI/API/Video.swift +64 -0
- package/Sources/SwiftUI/API/View.swift +46 -2
- package/Sources/SwiftUI/API/ViewModifier.swift +4 -1
- package/Sources/SwiftUI/API/VisualEffect.swift +74 -1
- package/Sources/SwiftUI/API/Windows.swift +25 -0
- package/Sources/SwiftUI/Charts/Axis.swift +7 -1
- package/Sources/SwiftUI/Charts/AxisFormatsAndRanges.swift +68 -0
- package/Sources/SwiftUI/Charts/Chart.swift +17 -3
- package/Sources/SwiftUI/Charts/ChartProxy.swift +57 -0
- package/Sources/SwiftUI/Localization/Localization.swift +524 -0
- package/Sources/SwiftUI/Localization/PluralRules.swift +127 -0
- package/Sources/SwiftUI/Runtime/Executor.swift +2 -0
- package/Sources/SwiftUI/Runtime/Exports.swift +37 -1
- package/Sources/SwiftUI/Runtime/ImageSource.swift +310 -0
- package/Sources/SwiftUI/Runtime/JSON.swift +76 -4
- package/Sources/SwiftUI/Runtime/LazyRows.swift +99 -0
- package/Sources/SwiftUI/Runtime/Measurements.swift +140 -0
- package/Sources/SwiftUI/Runtime/Ops.swift +58 -12
- package/Sources/SwiftUI/Runtime/Reconciler.swift +10 -1
- package/Sources/SwiftUI/Runtime/Replay.swift +382 -0
- package/Sources/SwiftUI/Runtime/Runtime.swift +977 -198
- package/Sources/SwiftUI/Runtime/Sameness.swift +152 -0
- package/Sources/SwiftUI/Runtime/ViewPath.swift +89 -0
- package/Sources/SwiftUI/Runtime/ViewTraits.swift +175 -0
- package/Sources/SwiftUI/Runtime/WebAuthHeadless.swift +49 -0
- package/Sources/Symbols/Symbols.swift +25 -0
- package/Sources/Translation/Translation.swift +423 -0
- package/Sources/UIKit/Activity.swift +213 -0
- package/Sources/UIKit/Alerts.swift +69 -0
- package/Sources/UIKit/AppDelegate.swift +222 -0
- package/Sources/UIKit/Appearance.swift +84 -0
- package/Sources/UIKit/ColorConversions.swift +70 -0
- package/Sources/UIKit/Controls.swift +225 -0
- package/Sources/UIKit/Device.swift +200 -0
- package/Sources/UIKit/Feedback.swift +65 -0
- package/Sources/UIKit/Font.swift +307 -0
- package/Sources/UIKit/Graphics.swift +204 -0
- package/Sources/UIKit/HostingController.swift +185 -0
- package/Sources/UIKit/Identity.swift +24 -0
- package/Sources/UIKit/Introspectable.swift +510 -0
- package/Sources/UIKit/Pasteboard.swift +363 -0
- package/Sources/UIKit/PhotoAlbum.swift +81 -0
- package/Sources/UIKit/RemoteNotifications.swift +15 -0
- package/Sources/UIKit/Representable.swift +288 -0
- package/Sources/UIKit/SafeArea.swift +12 -0
- package/Sources/UIKit/SceneDelegates.swift +132 -0
- package/Sources/UIKit/Scenes.swift +40 -0
- package/Sources/UIKit/ScrollView.swift +157 -0
- package/Sources/UIKit/ShortcutItems.swift +67 -0
- package/Sources/UIKit/SymbolConfiguration.swift +124 -0
- package/Sources/UIKit/TargetAction.swift +123 -0
- package/Sources/UIKit/TextPaste.swift +161 -0
- package/Sources/UIKit/TextView.swift +538 -0
- package/Sources/UIKit/UIKit.swift +292 -0
- package/Sources/UIKit/UIView.swift +855 -0
- package/Sources/UIKit/ViewTranslation.swift +311 -0
- package/Sources/UniformTypeIdentifiers/UTType.swift +196 -0
- package/Sources/UserNotifications/Content.swift +370 -0
- package/Sources/UserNotifications/NotificationCenter.swift +309 -0
- package/Sources/WeatherKit/WeatherKit.swift +228 -0
- package/Sources/WebKit/WebKit.swift +245 -0
- package/Sources/os/os.swift +376 -0
- package/Sources/zlib/zlib.swift +6 -0
- package/index.d.ts +1 -1
- package/index.mjs +2 -2
- package/manifest.d.mts +2 -0
- package/manifest.mjs +41 -0
- package/package.json +5 -3
- package/sync-sources.mjs +21 -7
- package/Sources/FoundationLite/Common.swift +0 -3
- package/Sources/FoundationLite/FoundationLite.swift +0 -171
- package/Sources/FoundationLite/Interaction.swift +0 -146
- package/Sources/FoundationLite/Timer.swift +0 -48
- package/Sources/_FoundationCommon/_FoundationCommon.swift +0 -10
|
@@ -0,0 +1,524 @@
|
|
|
1
|
+
// Localized strings, as `Text("key")`, `String(localized:)` and
|
|
2
|
+
// `Bundle.localizedString(forKey:value:table:)` find them: the app's string
|
|
3
|
+
// tables are `<language>.lproj/<Table>.strings` and `.stringsdict` files in its
|
|
4
|
+
// bundle, which the CLI compiles from the Xcode target's string catalogs
|
|
5
|
+
// (`.xcstrings`) and classic `.strings` files, as Xcode does (packages/cli,
|
|
6
|
+
// strings.ts). Both are XML property lists: a `.strings` table maps keys to
|
|
7
|
+
// format strings, a `.stringsdict` maps keys to an `NSStringLocalizedFormatKey`
|
|
8
|
+
// whose `%#@name@` tokens a variable resolves (a plural rule).
|
|
9
|
+
//
|
|
10
|
+
// A key is looked up in the table of the language the app runs in (the
|
|
11
|
+
// viewer's, among the bundle's `.lproj` folders), then in the development
|
|
12
|
+
// language's; a key found in neither is its own text. Formatting follows
|
|
13
|
+
// `String(format:)` for the specifiers Xcode puts in keys (`%@`, `%lld`, `%lf`,
|
|
14
|
+
// `%d`, positional `%1$@`…), with each argument in the slot its specifier names.
|
|
15
|
+
// The engine lives in SwiftUI so that `Text` uses it; FoundationBridge's
|
|
16
|
+
// `Bundle` and `String(localized:)` call it too.
|
|
17
|
+
|
|
18
|
+
import Foundation
|
|
19
|
+
|
|
20
|
+
/// A value interpolated into a localized string: what its specifier formats.
|
|
21
|
+
public enum _LocalizationArgument: Hashable, Sendable, Codable {
|
|
22
|
+
case string(String)
|
|
23
|
+
case int(Int64)
|
|
24
|
+
case uint(UInt64)
|
|
25
|
+
case double(Double)
|
|
26
|
+
|
|
27
|
+
/// The number a plural rule counts (nil for a string that is no number).
|
|
28
|
+
var number: Double? {
|
|
29
|
+
switch self {
|
|
30
|
+
case .int(let value): Double(value)
|
|
31
|
+
case .uint(let value): Double(value)
|
|
32
|
+
case .double(let value): value
|
|
33
|
+
case .string(let value): Double(value)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
var text: String {
|
|
38
|
+
switch self {
|
|
39
|
+
case .string(let value): value
|
|
40
|
+
case .int(let value): String(value)
|
|
41
|
+
case .uint(let value): String(value)
|
|
42
|
+
case .double(let value): value.rounded() == value && abs(value) < 1e15 ? String(Int64(value)) : String(value)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/// A number type and the specifier SwiftUI and Foundation write for it in a
|
|
48
|
+
/// localization key (`%lld` for `Int`): one generic interpolation takes them
|
|
49
|
+
/// all, as SwiftUI's does, which keeps type checking fast.
|
|
50
|
+
public protocol _FormatSpecifiable {
|
|
51
|
+
static var _specifier: String { get }
|
|
52
|
+
var _localizationArgument: _LocalizationArgument { get }
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
extension Int: _FormatSpecifiable {
|
|
56
|
+
public static var _specifier: String { "%lld" }
|
|
57
|
+
public var _localizationArgument: _LocalizationArgument { .int(Int64(self)) }
|
|
58
|
+
}
|
|
59
|
+
extension Int64: _FormatSpecifiable {
|
|
60
|
+
public static var _specifier: String { "%lld" }
|
|
61
|
+
public var _localizationArgument: _LocalizationArgument { .int(self) }
|
|
62
|
+
}
|
|
63
|
+
extension Int32: _FormatSpecifiable {
|
|
64
|
+
public static var _specifier: String { "%d" }
|
|
65
|
+
public var _localizationArgument: _LocalizationArgument { .int(Int64(self)) }
|
|
66
|
+
}
|
|
67
|
+
extension Int16: _FormatSpecifiable {
|
|
68
|
+
public static var _specifier: String { "%d" }
|
|
69
|
+
public var _localizationArgument: _LocalizationArgument { .int(Int64(self)) }
|
|
70
|
+
}
|
|
71
|
+
extension Int8: _FormatSpecifiable {
|
|
72
|
+
public static var _specifier: String { "%d" }
|
|
73
|
+
public var _localizationArgument: _LocalizationArgument { .int(Int64(self)) }
|
|
74
|
+
}
|
|
75
|
+
extension UInt: _FormatSpecifiable {
|
|
76
|
+
public static var _specifier: String { "%llu" }
|
|
77
|
+
public var _localizationArgument: _LocalizationArgument { .uint(UInt64(self)) }
|
|
78
|
+
}
|
|
79
|
+
extension UInt64: _FormatSpecifiable {
|
|
80
|
+
public static var _specifier: String { "%llu" }
|
|
81
|
+
public var _localizationArgument: _LocalizationArgument { .uint(self) }
|
|
82
|
+
}
|
|
83
|
+
extension UInt32: _FormatSpecifiable {
|
|
84
|
+
public static var _specifier: String { "%u" }
|
|
85
|
+
public var _localizationArgument: _LocalizationArgument { .uint(UInt64(self)) }
|
|
86
|
+
}
|
|
87
|
+
extension UInt16: _FormatSpecifiable {
|
|
88
|
+
public static var _specifier: String { "%u" }
|
|
89
|
+
public var _localizationArgument: _LocalizationArgument { .uint(UInt64(self)) }
|
|
90
|
+
}
|
|
91
|
+
extension UInt8: _FormatSpecifiable {
|
|
92
|
+
public static var _specifier: String { "%u" }
|
|
93
|
+
public var _localizationArgument: _LocalizationArgument { .uint(UInt64(self)) }
|
|
94
|
+
}
|
|
95
|
+
extension Double: _FormatSpecifiable {
|
|
96
|
+
public static var _specifier: String { "%lf" }
|
|
97
|
+
public var _localizationArgument: _LocalizationArgument { .double(self) }
|
|
98
|
+
}
|
|
99
|
+
extension Float: _FormatSpecifiable {
|
|
100
|
+
public static var _specifier: String { "%f" }
|
|
101
|
+
public var _localizationArgument: _LocalizationArgument { .double(Double(self)) }
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/// A piece of a formatted localized string: text, or the argument (by index)
|
|
105
|
+
/// that a view takes the place of (an `Image` or `Text` in `Text("…\(image)")`).
|
|
106
|
+
public enum _LocalizedPiece: Equatable, Sendable {
|
|
107
|
+
case text(String)
|
|
108
|
+
case argument(Int)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/// A table entry: a format string, or a `.stringsdict` rule.
|
|
112
|
+
enum _LocalizedEntry: Equatable {
|
|
113
|
+
case format(String)
|
|
114
|
+
case rule(formatKey: String, variables: [String: [String: String]])
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
public enum _Localization {
|
|
118
|
+
/// Where the app's resources are mounted (`SB_BUNDLE_PATH`), as `Bundle.main` reads them.
|
|
119
|
+
public static var mainBundlePath: String { _environment("SB_BUNDLE_PATH") ?? "/bundle" }
|
|
120
|
+
|
|
121
|
+
// MARK: Languages
|
|
122
|
+
|
|
123
|
+
/// The `<language>.lproj` folders of a bundle (without `Base`), sorted;
|
|
124
|
+
/// read once per bundle (an app's bundle does not change while it runs).
|
|
125
|
+
public static func localizations(bundlePath: String) -> [String] {
|
|
126
|
+
cacheLock.lock()
|
|
127
|
+
defer { cacheLock.unlock() }
|
|
128
|
+
if let cached = languagesByBundle[bundlePath] { return cached }
|
|
129
|
+
let names = (try? FileManager.default.contentsOfDirectory(atPath: bundlePath)) ?? []
|
|
130
|
+
let found = names.filter { $0.hasSuffix(".lproj") }.map { String($0.dropLast(6)) }.filter { $0 != "Base" }.sorted()
|
|
131
|
+
languagesByBundle[bundlePath] = found
|
|
132
|
+
return found
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/// The localization the app runs in: the viewer's language (`SB_LOCALE`)
|
|
136
|
+
/// matched against `available`, else the development localization.
|
|
137
|
+
public static func preferredLocalization(from available: [String]) -> String? {
|
|
138
|
+
preferredLocalizations(from: available, forPreferences: _environment("SB_LOCALE").map { [$0] }).first
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/// The first of `available` that matches a preference, by whole tag
|
|
142
|
+
/// (`pt-BR`, case and `_` / `-` aside), then by script (`zh-Hant` for
|
|
143
|
+
/// `zh-TW`), then by language (`pt`); without a match, `en` when listed,
|
|
144
|
+
/// else the first.
|
|
145
|
+
public static func preferredLocalizations(from available: [String], forPreferences preferences: [String]?) -> [String] {
|
|
146
|
+
guard !available.isEmpty else { return [] }
|
|
147
|
+
func normalized(_ tag: String) -> String { tag.replacingOccurrences(of: "_", with: "-").lowercased() }
|
|
148
|
+
func language(_ tag: String) -> Substring { normalized(tag).split(separator: "-").first ?? "" }
|
|
149
|
+
for preference in preferences ?? [] {
|
|
150
|
+
if let exact = available.first(where: { normalized($0) == normalized(preference) }) { return [exact] }
|
|
151
|
+
if let script = chineseScript(preference), let match = available.first(where: { normalized($0) == "zh-\(script)" }) { return [match] }
|
|
152
|
+
if let close = available.first(where: { language($0) == language(preference) && !normalized($0).contains("-") }) { return [close] }
|
|
153
|
+
if let close = available.first(where: { language($0) == language(preference) }) { return [close] }
|
|
154
|
+
}
|
|
155
|
+
if available.contains("en") { return ["en"] }
|
|
156
|
+
return [available[0]]
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/// `hant` for traditional Chinese (`zh-TW`, `zh-HK`, `zh-MO`, `zh-Hant…`), `hans` for the rest of `zh`.
|
|
160
|
+
private static func chineseScript(_ tag: String) -> String? {
|
|
161
|
+
let parts = tag.replacingOccurrences(of: "_", with: "-").lowercased().split(separator: "-")
|
|
162
|
+
guard parts.first == "zh" else { return nil }
|
|
163
|
+
if parts.contains("hant") || parts.contains("tw") || parts.contains("hk") || parts.contains("mo") { return "hant" }
|
|
164
|
+
return "hans"
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/// The language the app's strings are written in. Xcode records it in the
|
|
168
|
+
/// app's Info.plist; the CLI ships its tables, and English is the default.
|
|
169
|
+
static func developmentLocalization(among available: [String]) -> String? {
|
|
170
|
+
available.contains("en") ? "en" : available.first
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// MARK: Tables
|
|
174
|
+
|
|
175
|
+
/// Loaded tables and bundles' languages, behind a lock: views on any
|
|
176
|
+
/// thread look strings up (tests run in parallel on the host).
|
|
177
|
+
private static let cacheLock = NSLock()
|
|
178
|
+
nonisolated(unsafe) private static var tables: [String: [String: _LocalizedEntry]] = [:]
|
|
179
|
+
nonisolated(unsafe) private static var languagesByBundle: [String: [String]] = [:]
|
|
180
|
+
|
|
181
|
+
/// The entries of `<bundle>/<language>.lproj/<table>.strings` and `.stringsdict` (empty when absent).
|
|
182
|
+
static func table(_ name: String, language: String, bundlePath: String) -> [String: _LocalizedEntry] {
|
|
183
|
+
let base = "\(bundlePath)/\(language).lproj/\(name)"
|
|
184
|
+
cacheLock.lock()
|
|
185
|
+
defer { cacheLock.unlock() }
|
|
186
|
+
if let cached = tables[base] { return cached }
|
|
187
|
+
var entries: [String: _LocalizedEntry] = [:]
|
|
188
|
+
if let data = FileManager.default.contents(atPath: "\(base).strings"),
|
|
189
|
+
let strings = try? PropertyListDecoder().decode([String: String].self, from: data) {
|
|
190
|
+
for (key, value) in strings { entries[key] = .format(value) }
|
|
191
|
+
}
|
|
192
|
+
if let data = FileManager.default.contents(atPath: "\(base).stringsdict"),
|
|
193
|
+
let rules = try? PropertyListDecoder().decode([String: [String: _PlistValue]].self, from: data) {
|
|
194
|
+
for (key, rule) in rules {
|
|
195
|
+
guard case .string(let formatKey)? = rule["NSStringLocalizedFormatKey"] else { continue }
|
|
196
|
+
var variables: [String: [String: String]] = [:]
|
|
197
|
+
for (name, value) in rule where name != "NSStringLocalizedFormatKey" {
|
|
198
|
+
if case .dictionary(let cases) = value { variables[name] = cases }
|
|
199
|
+
}
|
|
200
|
+
entries[key] = .rule(formatKey: formatKey, variables: variables)
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
tables[base] = entries
|
|
204
|
+
return entries
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/// Forgets the loaded tables and languages (tests write new ones).
|
|
208
|
+
public static func _resetTables() {
|
|
209
|
+
cacheLock.lock()
|
|
210
|
+
defer { cacheLock.unlock() }
|
|
211
|
+
tables = [:]
|
|
212
|
+
languagesByBundle = [:]
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/// The entry for `key`: the running language's table, then the development language's.
|
|
216
|
+
/// `language` overrides the viewer's (a test's, or a resource's locale).
|
|
217
|
+
static func entry(forKey key: String, table: String?, bundlePath: String, language: String? = nil) -> (entry: _LocalizedEntry, language: String)? {
|
|
218
|
+
let available = localizations(bundlePath: bundlePath)
|
|
219
|
+
guard !available.isEmpty else { return nil }
|
|
220
|
+
let name = (table?.isEmpty ?? true) ? "Localizable" : table!
|
|
221
|
+
var languages: [String] = []
|
|
222
|
+
let preferred = language.flatMap { preferredLocalizations(from: available, forPreferences: [$0]).first } ?? preferredLocalization(from: available)
|
|
223
|
+
if let preferred { languages.append(preferred) }
|
|
224
|
+
if let development = developmentLocalization(among: available), !languages.contains(development) { languages.append(development) }
|
|
225
|
+
for language in languages {
|
|
226
|
+
if let entry = self.table(name, language: language, bundlePath: bundlePath)[key] { return (entry, language) }
|
|
227
|
+
}
|
|
228
|
+
return nil
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/// The language plural rules follow: the one the app runs in.
|
|
232
|
+
static func runningLanguage(bundlePath: String) -> String {
|
|
233
|
+
preferredLocalization(from: localizations(bundlePath: bundlePath)) ?? _environment("SB_LOCALE") ?? "en"
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// MARK: Resolving
|
|
237
|
+
|
|
238
|
+
/// `Bundle.localizedString(forKey:value:table:)`: the key's format string
|
|
239
|
+
/// (a rule's, with its tokens resolved for no particular count, as
|
|
240
|
+
/// `other`), else `value` when it is not empty, else the key.
|
|
241
|
+
public static func localizedString(forKey key: String, value: String?, table: String?, bundlePath: String) -> String {
|
|
242
|
+
switch entry(forKey: key, table: table, bundlePath: bundlePath) {
|
|
243
|
+
case (.format(let format), _)?: return format
|
|
244
|
+
case (.rule(let formatKey, let variables), let language)?:
|
|
245
|
+
return expand(formatKey, variables: variables, arguments: [], language: language)
|
|
246
|
+
case nil:
|
|
247
|
+
if let value, !value.isEmpty { return value }
|
|
248
|
+
return key
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/// `key` localized and formatted with `arguments`: the pieces of text, and
|
|
253
|
+
/// the indices of the arguments (nil entries: views) that go between them.
|
|
254
|
+
/// A key with no arguments is not formatted (`"100%"` stays as it is).
|
|
255
|
+
/// `defaultValue` is the format when the tables lack the key (else the key itself).
|
|
256
|
+
public static func resolve(key: String, arguments: [_LocalizationArgument?], table: String? = nil,
|
|
257
|
+
bundlePath: String? = nil, defaultValue: String? = nil, language: String? = nil) -> [_LocalizedPiece] {
|
|
258
|
+
let path = bundlePath ?? mainBundlePath
|
|
259
|
+
let found = entry(forKey: key, table: table, bundlePath: path, language: language)
|
|
260
|
+
let format: String
|
|
261
|
+
switch found {
|
|
262
|
+
case (.format(let text), _)?: format = text
|
|
263
|
+
case (.rule(let formatKey, let variables), let language)?:
|
|
264
|
+
format = expand(formatKey, variables: variables, arguments: arguments, language: language)
|
|
265
|
+
case nil: format = defaultValue ?? key
|
|
266
|
+
}
|
|
267
|
+
if arguments.isEmpty { return [.text(format)] }
|
|
268
|
+
return _LocalizedFormat.format(format, arguments: arguments)
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/// `resolve` as one string, views left out.
|
|
272
|
+
public static func string(key: String, arguments: [_LocalizationArgument?], table: String? = nil, bundlePath: String? = nil,
|
|
273
|
+
defaultValue: String? = nil, language: String? = nil) -> String {
|
|
274
|
+
resolve(key: key, arguments: arguments, table: table, bundlePath: bundlePath, defaultValue: defaultValue, language: language).map { piece in
|
|
275
|
+
if case .text(let text) = piece { text } else { "" }
|
|
276
|
+
}.joined()
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/// `key` formatted with `arguments`, not looked up (a key with no arguments is its own text).
|
|
280
|
+
public static func formatted(key: String, arguments: [_LocalizationArgument]) -> String {
|
|
281
|
+
if arguments.isEmpty { return key }
|
|
282
|
+
return _LocalizedFormat.format(key, arguments: arguments).map { piece in
|
|
283
|
+
if case .text(let text) = piece { text } else { "" }
|
|
284
|
+
}.joined()
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/// A rule's format key with each `%#@name@` token replaced by the case its
|
|
288
|
+
/// variable picks for the token's argument (plural rules), repeatedly, as
|
|
289
|
+
/// cases may hold tokens. Every specifier is numbered first, so a case's
|
|
290
|
+
/// own unnumbered specifiers take its token's argument and the others keep theirs.
|
|
291
|
+
static func expand(_ formatKey: String, variables: [String: [String: String]], arguments: [_LocalizationArgument?], language: String) -> String {
|
|
292
|
+
var format = _LocalizedFormat.numbered(formatKey)
|
|
293
|
+
for _ in 0..<8 {
|
|
294
|
+
guard let token = _LocalizedFormat.firstToken(in: format) else { break }
|
|
295
|
+
let variable = variables[token.name] ?? [:]
|
|
296
|
+
let argument = token.position - 1 < arguments.count ? arguments[token.position - 1] : nil
|
|
297
|
+
let chosen: String
|
|
298
|
+
if variable["NSStringFormatSpecTypeKey"] == "NSStringPluralRuleType" {
|
|
299
|
+
let number = argument?.number
|
|
300
|
+
let category = number.map { _PluralRules.category(for: $0, language: language) } ?? "other"
|
|
301
|
+
if number == 0, let zero = variable["zero"] {
|
|
302
|
+
chosen = zero
|
|
303
|
+
} else {
|
|
304
|
+
chosen = variable[category] ?? variable["other"] ?? ""
|
|
305
|
+
}
|
|
306
|
+
} else {
|
|
307
|
+
// A device rule (settled at compile time) or one the browser has no use for.
|
|
308
|
+
chosen = variable["iphone"] ?? variable["other"] ?? ""
|
|
309
|
+
}
|
|
310
|
+
let replacement = _LocalizedFormat.numbered(chosen, defaultPosition: token.position)
|
|
311
|
+
format.replaceSubrange(token.range, with: replacement)
|
|
312
|
+
}
|
|
313
|
+
return format
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/// A property list value of a `.stringsdict`: a string or a dictionary of strings.
|
|
318
|
+
enum _PlistValue: Decodable {
|
|
319
|
+
case string(String)
|
|
320
|
+
case dictionary([String: String])
|
|
321
|
+
case other
|
|
322
|
+
|
|
323
|
+
init(from decoder: any Decoder) throws {
|
|
324
|
+
let container = try decoder.singleValueContainer()
|
|
325
|
+
if let string = try? container.decode(String.self) {
|
|
326
|
+
self = .string(string)
|
|
327
|
+
} else if let dictionary = try? container.decode([String: String].self) {
|
|
328
|
+
self = .dictionary(dictionary)
|
|
329
|
+
} else {
|
|
330
|
+
self = .other
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// MARK: - Formatting
|
|
336
|
+
|
|
337
|
+
enum _LocalizedFormat {
|
|
338
|
+
/// One `%` specifier: its range, explicit position (`%2$@`), the rest of
|
|
339
|
+
/// the spec without the position (`-5`, `.2`, `ll`) and its conversion.
|
|
340
|
+
struct Specifier {
|
|
341
|
+
let range: Range<String.Index>
|
|
342
|
+
let position: Int?
|
|
343
|
+
let spec: String
|
|
344
|
+
let conversion: Character
|
|
345
|
+
/// `%#@name@`: a stringsdict token.
|
|
346
|
+
let token: String?
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/// The specifiers of a format string, in order (`%%` left out).
|
|
350
|
+
static func specifiers(in format: String) -> [Specifier] {
|
|
351
|
+
var out: [Specifier] = []
|
|
352
|
+
var index = format.startIndex
|
|
353
|
+
while let percent = format[index...].firstIndex(of: "%") {
|
|
354
|
+
var cursor = format.index(after: percent)
|
|
355
|
+
guard cursor < format.endIndex else { break }
|
|
356
|
+
if format[cursor] == "%" {
|
|
357
|
+
index = format.index(after: cursor)
|
|
358
|
+
continue
|
|
359
|
+
}
|
|
360
|
+
// Position: digits followed by `$`.
|
|
361
|
+
var position: Int?
|
|
362
|
+
var digits = cursor
|
|
363
|
+
while digits < format.endIndex, format[digits].isASCII, format[digits].isNumber { digits = format.index(after: digits) }
|
|
364
|
+
if digits > cursor, digits < format.endIndex, format[digits] == "$" {
|
|
365
|
+
position = Int(format[cursor..<digits])
|
|
366
|
+
cursor = format.index(after: digits)
|
|
367
|
+
}
|
|
368
|
+
let specStart = cursor
|
|
369
|
+
if cursor < format.endIndex, format[cursor] == "#", format.index(after: cursor) < format.endIndex, format[format.index(after: cursor)] == "@" {
|
|
370
|
+
// `%#@name@`
|
|
371
|
+
let nameStart = format.index(cursor, offsetBy: 2)
|
|
372
|
+
if let end = format[nameStart...].firstIndex(of: "@") {
|
|
373
|
+
out.append(Specifier(range: percent..<format.index(after: end), position: position, spec: "",
|
|
374
|
+
conversion: "@", token: String(format[nameStart..<end])))
|
|
375
|
+
index = format.index(after: end)
|
|
376
|
+
continue
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
while cursor < format.endIndex, "-+ #0'".contains(format[cursor]) { cursor = format.index(after: cursor) }
|
|
380
|
+
while cursor < format.endIndex, format[cursor].isASCII, format[cursor].isNumber || format[cursor] == "*" { cursor = format.index(after: cursor) }
|
|
381
|
+
if cursor < format.endIndex, format[cursor] == "." {
|
|
382
|
+
cursor = format.index(after: cursor)
|
|
383
|
+
while cursor < format.endIndex, format[cursor].isASCII, format[cursor].isNumber { cursor = format.index(after: cursor) }
|
|
384
|
+
}
|
|
385
|
+
while cursor < format.endIndex, "hlqLztj".contains(format[cursor]) { cursor = format.index(after: cursor) }
|
|
386
|
+
guard cursor < format.endIndex else { break }
|
|
387
|
+
let conversion = format[cursor]
|
|
388
|
+
guard "@dDiuUxXoOfFeEgGaAcCsSp".contains(conversion) else {
|
|
389
|
+
index = format.index(after: cursor)
|
|
390
|
+
continue
|
|
391
|
+
}
|
|
392
|
+
out.append(Specifier(range: percent..<format.index(after: cursor), position: position,
|
|
393
|
+
spec: String(format[specStart..<cursor]), conversion: conversion, token: nil))
|
|
394
|
+
index = format.index(after: cursor)
|
|
395
|
+
}
|
|
396
|
+
return out
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/// `format` with every unnumbered specifier numbered: in order from 1, or
|
|
400
|
+
/// all `defaultPosition` (a plural case's specifiers are its token's argument).
|
|
401
|
+
static func numbered(_ format: String, defaultPosition: Int? = nil) -> String {
|
|
402
|
+
var result = ""
|
|
403
|
+
var last = format.startIndex
|
|
404
|
+
var next = 1
|
|
405
|
+
for specifier in specifiers(in: format) {
|
|
406
|
+
result += format[last..<specifier.range.lowerBound]
|
|
407
|
+
let position = specifier.position ?? defaultPosition ?? next
|
|
408
|
+
if specifier.position == nil, defaultPosition == nil { next += 1 }
|
|
409
|
+
if let token = specifier.token {
|
|
410
|
+
result += "%\(position)$#@\(token)@"
|
|
411
|
+
} else {
|
|
412
|
+
result += "%\(position)$\(specifier.spec)\(specifier.conversion)"
|
|
413
|
+
}
|
|
414
|
+
last = specifier.range.upperBound
|
|
415
|
+
}
|
|
416
|
+
result += format[last...]
|
|
417
|
+
return result
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
static func firstToken(in format: String) -> (name: String, position: Int, range: Range<String.Index>)? {
|
|
421
|
+
for specifier in specifiers(in: format) {
|
|
422
|
+
if let token = specifier.token { return (token, specifier.position ?? 1, specifier.range) }
|
|
423
|
+
}
|
|
424
|
+
return nil
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/// The pieces of `format` with its specifiers filled: unnumbered ones take
|
|
428
|
+
/// the arguments in order, numbered ones theirs; a view argument (nil)
|
|
429
|
+
/// becomes an `.argument` piece; `%%` is `%`.
|
|
430
|
+
static func format(_ format: String, arguments: [_LocalizationArgument?]) -> [_LocalizedPiece] {
|
|
431
|
+
var pieces: [_LocalizedPiece] = []
|
|
432
|
+
var text = ""
|
|
433
|
+
var last = format.startIndex
|
|
434
|
+
var next = 0
|
|
435
|
+
func literal(_ part: Substring) { text += part.replacingOccurrences(of: "%%", with: "%") }
|
|
436
|
+
for specifier in specifiers(in: format) {
|
|
437
|
+
literal(format[last..<specifier.range.lowerBound])
|
|
438
|
+
last = specifier.range.upperBound
|
|
439
|
+
let index: Int
|
|
440
|
+
if let position = specifier.position {
|
|
441
|
+
index = position - 1
|
|
442
|
+
} else {
|
|
443
|
+
index = next
|
|
444
|
+
next += 1
|
|
445
|
+
}
|
|
446
|
+
guard index >= 0, index < arguments.count else { continue }
|
|
447
|
+
guard let argument = arguments[index] else {
|
|
448
|
+
if !text.isEmpty { pieces.append(.text(text)); text = "" }
|
|
449
|
+
pieces.append(.argument(index))
|
|
450
|
+
continue
|
|
451
|
+
}
|
|
452
|
+
text += render(argument, spec: specifier.spec, conversion: specifier.conversion)
|
|
453
|
+
}
|
|
454
|
+
literal(format[last...])
|
|
455
|
+
if !text.isEmpty || pieces.isEmpty { pieces.append(.text(text)) }
|
|
456
|
+
return pieces
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/// A formatted number's integer digits grouped with the locale's separator
|
|
460
|
+
/// and its decimal point the locale's, as Apple's localized formatting
|
|
461
|
+
/// (`String.localizedStringWithFormat`, which `Text` and
|
|
462
|
+
/// `String(localized:)` use) writes `%d`, `%u` and `%f`: `"\(1024)"` is
|
|
463
|
+
/// "1,024" in English (and a year "2,026", as on iOS).
|
|
464
|
+
static func localizedDigits(_ text: String) -> String {
|
|
465
|
+
let locale = Locale.current
|
|
466
|
+
let grouping = locale.groupingSeparator ?? ","
|
|
467
|
+
let decimal = locale.decimalSeparator ?? "."
|
|
468
|
+
guard let start = text.firstIndex(where: \.isNumber) else { return text }
|
|
469
|
+
let end = text[start...].firstIndex(where: { !$0.isNumber }) ?? text.endIndex
|
|
470
|
+
let digits = text[start..<end]
|
|
471
|
+
var grouped = ""
|
|
472
|
+
for (offset, digit) in digits.enumerated() {
|
|
473
|
+
if offset > 0 && (digits.count - offset) % 3 == 0 { grouped += grouping }
|
|
474
|
+
grouped.append(digit)
|
|
475
|
+
}
|
|
476
|
+
var rest = String(text[end...])
|
|
477
|
+
if rest.hasPrefix(".") { rest = decimal + rest.dropFirst() }
|
|
478
|
+
return String(text[..<start]) + grouped + rest
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/// One argument as its specifier formats it.
|
|
482
|
+
static func render(_ argument: _LocalizationArgument, spec: String, conversion: Character) -> String {
|
|
483
|
+
let flags = spec.filter { !"hlqLztj".contains($0) }
|
|
484
|
+
switch conversion {
|
|
485
|
+
case "@", "s", "S":
|
|
486
|
+
return argument.text
|
|
487
|
+
case "d", "D", "i":
|
|
488
|
+
let value: Int64 = switch argument {
|
|
489
|
+
case .int(let v): v
|
|
490
|
+
case .uint(let v): Int64(truncatingIfNeeded: v)
|
|
491
|
+
case .double(let v): v.isFinite ? Int64(v) : 0
|
|
492
|
+
case .string(let v): Int64(v) ?? 0
|
|
493
|
+
}
|
|
494
|
+
return flags.isEmpty ? localizedDigits(String(value)) : String(format: "%\(flags)lld", value)
|
|
495
|
+
case "u", "U", "x", "X", "o", "O":
|
|
496
|
+
let value: UInt64 = switch argument {
|
|
497
|
+
case .int(let v): UInt64(bitPattern: v)
|
|
498
|
+
case .uint(let v): v
|
|
499
|
+
case .double(let v): v.isFinite && v >= 0 ? UInt64(v) : 0
|
|
500
|
+
case .string(let v): UInt64(v) ?? 0
|
|
501
|
+
}
|
|
502
|
+
let lower = Character(conversion.lowercased())
|
|
503
|
+
let code = lower == "u" ? "u" : String(conversion)
|
|
504
|
+
if lower == "u" && flags.isEmpty { return localizedDigits(String(value)) }
|
|
505
|
+
return String(format: "%\(flags)ll\(code)", value)
|
|
506
|
+
case "f", "F", "e", "E", "g", "G", "a", "A":
|
|
507
|
+
let value: Double = switch argument {
|
|
508
|
+
case .int(let v): Double(v)
|
|
509
|
+
case .uint(let v): Double(v)
|
|
510
|
+
case .double(let v): v
|
|
511
|
+
case .string(let v): Double(v) ?? 0
|
|
512
|
+
}
|
|
513
|
+
let text = String(format: "%\(flags)\(conversion)", value)
|
|
514
|
+
// A precision alone (`%.1f`) keeps the locale's digits; a width or a flag pads as given.
|
|
515
|
+
let plain = flags.isEmpty || flags.first == "." && flags.dropFirst().allSatisfy(\.isNumber)
|
|
516
|
+
return plain && (conversion == "f" || conversion == "F") ? localizedDigits(text) : text
|
|
517
|
+
case "c", "C":
|
|
518
|
+
if case .int(let v) = argument, let scalar = Unicode.Scalar(UInt32(truncatingIfNeeded: v)) { return String(Character(scalar)) }
|
|
519
|
+
return argument.text
|
|
520
|
+
default:
|
|
521
|
+
return argument.text
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
// CLDR's cardinal plural rules, which pick a `.stringsdict` plural case
|
|
2
|
+
// (`zero`, `one`, `two`, `few`, `many`, `other`) for a count in a language.
|
|
3
|
+
// The operands follow CLDR: `n` the absolute value, `i` its integer digits,
|
|
4
|
+
// `v` whether it has visible fraction digits (a `Double` with a fractional
|
|
5
|
+
// part; the browser cannot tell `1.0` from `1`).
|
|
6
|
+
|
|
7
|
+
import Foundation
|
|
8
|
+
|
|
9
|
+
enum _PluralRules {
|
|
10
|
+
static func category(for value: Double, language tag: String) -> String {
|
|
11
|
+
let n = abs(value)
|
|
12
|
+
let i = n.rounded(.towardZero)
|
|
13
|
+
let fractional = n != i
|
|
14
|
+
let integer = Int64(clamping: Int(exactly: i) ?? Int.max)
|
|
15
|
+
let i10 = integer % 10
|
|
16
|
+
let i100 = integer % 100
|
|
17
|
+
let language = tag.replacingOccurrences(of: "_", with: "-").lowercased().split(separator: "-").first.map(String.init) ?? ""
|
|
18
|
+
let millions = !fractional && integer != 0 && integer % 1_000_000 == 0
|
|
19
|
+
|
|
20
|
+
switch language {
|
|
21
|
+
// No plural forms.
|
|
22
|
+
case "ja", "zh", "ko", "th", "vi", "id", "ms", "my", "lo", "km", "yue", "jv", "su":
|
|
23
|
+
return "other"
|
|
24
|
+
case "fr":
|
|
25
|
+
if !fractional && (integer == 0 || integer == 1) { return "one" }
|
|
26
|
+
if fractional && i < 2 { return "one" }
|
|
27
|
+
return millions ? "many" : "other"
|
|
28
|
+
case "pt":
|
|
29
|
+
if tag.lowercased().hasPrefix("pt-pt") {
|
|
30
|
+
if !fractional && integer == 1 { return "one" }
|
|
31
|
+
return millions ? "many" : "other"
|
|
32
|
+
}
|
|
33
|
+
if i < 2 { return "one" }
|
|
34
|
+
return millions ? "many" : "other"
|
|
35
|
+
case "es", "it", "ca":
|
|
36
|
+
if !fractional && integer == 1 { return "one" }
|
|
37
|
+
return millions ? "many" : "other"
|
|
38
|
+
case "ru", "uk", "be":
|
|
39
|
+
if fractional { return "other" }
|
|
40
|
+
if i10 == 1 && i100 != 11 { return "one" }
|
|
41
|
+
if (2...4).contains(i10) && !(12...14).contains(i100) { return "few" }
|
|
42
|
+
return "many"
|
|
43
|
+
case "pl":
|
|
44
|
+
if fractional { return "other" }
|
|
45
|
+
if integer == 1 { return "one" }
|
|
46
|
+
if (2...4).contains(i10) && !(12...14).contains(i100) { return "few" }
|
|
47
|
+
return "many"
|
|
48
|
+
case "cs", "sk":
|
|
49
|
+
if fractional { return "many" }
|
|
50
|
+
if integer == 1 { return "one" }
|
|
51
|
+
if (2...4).contains(integer) { return "few" }
|
|
52
|
+
return "other"
|
|
53
|
+
case "hr", "sr", "bs", "sh":
|
|
54
|
+
if fractional { return "other" }
|
|
55
|
+
if i10 == 1 && i100 != 11 { return "one" }
|
|
56
|
+
if (2...4).contains(i10) && !(12...14).contains(i100) { return "few" }
|
|
57
|
+
return "other"
|
|
58
|
+
case "lt":
|
|
59
|
+
if fractional { return "many" }
|
|
60
|
+
if i10 == 1 && !(11...19).contains(i100) { return "one" }
|
|
61
|
+
if (2...9).contains(i10) && !(11...19).contains(i100) { return "few" }
|
|
62
|
+
return "other"
|
|
63
|
+
case "lv":
|
|
64
|
+
if fractional { return "other" }
|
|
65
|
+
if i10 == 0 || (11...19).contains(i100) { return "zero" }
|
|
66
|
+
if i10 == 1 && i100 != 11 { return "one" }
|
|
67
|
+
return "other"
|
|
68
|
+
case "ro", "mo":
|
|
69
|
+
if !fractional && integer == 1 { return "one" }
|
|
70
|
+
if fractional || integer == 0 || (2...19).contains(i100) { return "few" }
|
|
71
|
+
return "other"
|
|
72
|
+
case "sl":
|
|
73
|
+
if fractional { return "few" }
|
|
74
|
+
if i100 == 1 { return "one" }
|
|
75
|
+
if i100 == 2 { return "two" }
|
|
76
|
+
if i100 == 3 || i100 == 4 { return "few" }
|
|
77
|
+
return "other"
|
|
78
|
+
case "ar":
|
|
79
|
+
if fractional { return "other" }
|
|
80
|
+
if integer == 0 { return "zero" }
|
|
81
|
+
if integer == 1 { return "one" }
|
|
82
|
+
if integer == 2 { return "two" }
|
|
83
|
+
if (3...10).contains(i100) { return "few" }
|
|
84
|
+
if (11...99).contains(i100) { return "many" }
|
|
85
|
+
return "other"
|
|
86
|
+
case "he", "iw":
|
|
87
|
+
if !fractional && integer == 1 { return "one" }
|
|
88
|
+
if !fractional && integer == 2 { return "two" }
|
|
89
|
+
return "other"
|
|
90
|
+
case "ga":
|
|
91
|
+
if fractional { return "other" }
|
|
92
|
+
switch integer {
|
|
93
|
+
case 1: return "one"
|
|
94
|
+
case 2: return "two"
|
|
95
|
+
case 3...6: return "few"
|
|
96
|
+
case 7...10: return "many"
|
|
97
|
+
default: return "other"
|
|
98
|
+
}
|
|
99
|
+
case "cy":
|
|
100
|
+
if fractional { return "other" }
|
|
101
|
+
switch integer {
|
|
102
|
+
case 0: return "zero"
|
|
103
|
+
case 1: return "one"
|
|
104
|
+
case 2: return "two"
|
|
105
|
+
case 3: return "few"
|
|
106
|
+
case 6: return "many"
|
|
107
|
+
default: return "other"
|
|
108
|
+
}
|
|
109
|
+
case "da":
|
|
110
|
+
if n == 1 || (fractional && (i == 0 || i == 1)) { return "one" }
|
|
111
|
+
return "other"
|
|
112
|
+
case "is":
|
|
113
|
+
if !fractional && i10 == 1 && i100 != 11 { return "one" }
|
|
114
|
+
return "other"
|
|
115
|
+
case "mk":
|
|
116
|
+
if !fractional && i10 == 1 && i100 != 11 { return "one" }
|
|
117
|
+
return "other"
|
|
118
|
+
case "hi", "bn", "gu", "kn", "mr", "fa", "zu", "am":
|
|
119
|
+
if i == 0 || n == 1 { return "one" }
|
|
120
|
+
return "other"
|
|
121
|
+
default:
|
|
122
|
+
// English and most languages: one for exactly 1.
|
|
123
|
+
if !fractional && integer == 1 { return "one" }
|
|
124
|
+
return "other"
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
@@ -64,6 +64,8 @@ final class _HostExecutor: MainExecutor, SchedulingExecutor, TaskExecutor, @unch
|
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
var hasTimers: Bool { !timers.isEmpty }
|
|
67
|
+
/// Whether jobs are waiting to run now (enqueued since the last `runJobs`).
|
|
68
|
+
var hasQueuedJobs: Bool { !queue.isEmpty }
|
|
67
69
|
}
|
|
68
70
|
|
|
69
71
|
enum _HostExecutorFactory: ExecutorFactory {
|