diff --git a/.github/workflows/appcast.yml b/.github/workflows/appcast.yml index 4cd9181..a47c711 100644 --- a/.github/workflows/appcast.yml +++ b/.github/workflows/appcast.yml @@ -14,16 +14,16 @@ jobs: # If you're using actions/checkout@v4 you must set persist-credentials to false in most cases for the deployment to work correctly. persist-credentials: false - - name: Cache 📦 - uses: actions/cache@v4.0.0 - with: - path: AppCast/vendor/bundle - key: ${{ runner.os }}-gems-v1.0-${{ hashFiles('AppCast/Gemfile') }} - restore-keys: | - ${{ runner.os }}-gems- +# - name: Cache 📦 +# uses: actions/cache@v4.1.1 +# with: +# path: AppCast/vendor/bundle +# key: ${{ runner.os }}-gems-v1.0-${{ hashFiles('AppCast/Gemfile') }} +# restore-keys: | +# ${{ runner.os }}-gems- - name: Setup Ruby, JRuby and TruffleRuby - uses: ruby/setup-ruby@v1.169.0 + uses: ruby/setup-ruby@v1.197.0 with: ruby-version: '3.0' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 788f9af..6047ad0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,10 +8,10 @@ on: jobs: test: - runs-on: macos-13 + runs-on: macos-15 steps: - uses: actions/checkout@v4 - name: Run tests env: - DEVELOPER_DIR: /Applications/Xcode_15.0.1.app + DEVELOPER_DIR: /Applications/Xcode_16.4.app run: xcodebuild test -scheme Xcodes diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index 8ac6c21..048fdaa 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -11,6 +11,6 @@ jobs: update_release_draft: runs-on: ubuntu-latest steps: - - uses: release-drafter/release-drafter@v5 + - uses: release-drafter/release-drafter@v6 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/xcstrings.yml b/.github/workflows/xcstrings.yml new file mode 100644 index 0000000..19404e6 --- /dev/null +++ b/.github/workflows/xcstrings.yml @@ -0,0 +1,22 @@ +name: XCStrings Validation + +on: + workflow_dispatch: + push: + branches: + - main + pull_request: + +jobs: + test: + runs-on: macos-13 + steps: + - uses: actions/checkout@v4 + + - name: Clone SwiftPolyglot + run: git clone --branch 0.3.1 -- https://github.com/appdecostudio/SwiftPolyglot.git ../SwiftPolyglot + + - name: validate translations + run: | + swift build --package-path ../SwiftPolyglot --configuration release + swift run --package-path ../SwiftPolyglot swiftpolyglot "ca,de,el,es,fi,fr,hi,it,ja,ko,nl,pl,pt-BR,ru,tr,uk,zh-Hans,zh-Hant" --errorOnMissing diff --git a/AppCast/Gemfile b/AppCast/Gemfile index b0be499..e70ec58 100644 --- a/AppCast/Gemfile +++ b/AppCast/Gemfile @@ -8,7 +8,7 @@ source "https://rubygems.org" # # This will help ensure the proper Jekyll version is running. # Happy Jekylling! -gem "jekyll", "~> 3.9.0" +gem "jekyll", "~> 4.4.1" gem "jekyll-github-metadata", group: :jekyll_plugins diff --git a/AppCast/Gemfile.lock b/AppCast/Gemfile.lock index dafd74c..712fe9d 100644 --- a/AppCast/Gemfile.lock +++ b/AppCast/Gemfile.lock @@ -1,88 +1,110 @@ GEM remote: https://rubygems.org/ specs: - addressable (2.8.0) - public_suffix (>= 2.0.2, < 5.0) + addressable (2.8.7) + public_suffix (>= 2.0.2, < 7.0) + base64 (0.3.0) + bigdecimal (3.2.2) colorator (1.1.0) - concurrent-ruby (1.1.7) - em-websocket (0.5.2) + concurrent-ruby (1.3.5) + csv (3.3.5) + em-websocket (0.5.3) eventmachine (>= 0.12.9) - http_parser.rb (~> 0.6.0) + http_parser.rb (~> 0) eventmachine (1.2.7) faraday (1.3.0) faraday-net_http (~> 1.0) multipart-post (>= 1.2, < 3) ruby2_keywords faraday-net_http (1.0.1) - ffi (1.14.2) + ffi (1.17.2) + ffi (1.17.2-x86_64-darwin) forwardable-extended (2.6.0) - http_parser.rb (0.6.0) - i18n (0.9.5) + google-protobuf (4.31.1) + bigdecimal + rake (>= 13) + google-protobuf (4.31.1-x86_64-darwin) + bigdecimal + rake (>= 13) + http_parser.rb (0.8.0) + i18n (1.14.7) concurrent-ruby (~> 1.0) - jekyll (3.9.0) + jekyll (4.4.1) addressable (~> 2.4) + base64 (~> 0.2) colorator (~> 1.0) + csv (~> 3.0) em-websocket (~> 0.5) - i18n (~> 0.7) - jekyll-sass-converter (~> 1.0) + i18n (~> 1.0) + jekyll-sass-converter (>= 2.0, < 4.0) jekyll-watch (~> 2.0) - kramdown (>= 1.17, < 3) + json (~> 2.6) + kramdown (~> 2.3, >= 2.3.1) + kramdown-parser-gfm (~> 1.0) liquid (~> 4.0) - mercenary (~> 0.3.3) + mercenary (~> 0.3, >= 0.3.6) pathutil (~> 0.9) - rouge (>= 1.7, < 4) + rouge (>= 3.0, < 5.0) safe_yaml (~> 1.0) + terminal-table (>= 1.8, < 4.0) + webrick (~> 1.7) jekyll-github-metadata (2.13.0) jekyll (>= 3.4, < 5.0) octokit (~> 4.0, != 4.4.0) - jekyll-sass-converter (1.5.2) - sass (~> 3.4) + jekyll-sass-converter (3.1.0) + sass-embedded (~> 1.75) jekyll-watch (2.2.1) listen (~> 3.0) - kramdown (2.3.1) - rexml + json (2.12.2) + kramdown (2.5.1) + rexml (>= 3.3.9) kramdown-parser-gfm (1.1.0) kramdown (~> 2.0) - liquid (4.0.3) - listen (3.4.1) + liquid (4.0.4) + listen (3.9.0) rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) - mercenary (0.3.6) + mercenary (0.4.0) multipart-post (2.1.1) octokit (4.20.0) faraday (>= 0.9) sawyer (~> 0.8.0, >= 0.5.3) pathutil (0.16.2) forwardable-extended (~> 2.6) - public_suffix (4.0.6) - rb-fsevent (0.10.4) - rb-inotify (0.10.1) + public_suffix (6.0.2) + rake (13.3.0) + rb-fsevent (0.11.2) + rb-inotify (0.11.1) ffi (~> 1.0) - rexml (3.2.5) - rouge (3.26.0) + rexml (3.4.1) + rouge (4.5.2) ruby2_keywords (0.0.2) safe_yaml (1.0.5) - sass (3.7.4) - sass-listen (~> 4.0.0) - sass-listen (4.0.0) - rb-fsevent (~> 0.9, >= 0.9.4) - rb-inotify (~> 0.9, >= 0.9.7) + sass-embedded (1.89.2) + google-protobuf (~> 4.31) + rake (>= 13) + sass-embedded (1.89.2-x86_64-darwin) + google-protobuf (~> 4.31) sawyer (0.8.2) addressable (>= 2.3.5) faraday (> 0.8, < 2.0) + terminal-table (3.0.2) + unicode-display_width (>= 1.1.1, < 3) thread_safe (0.3.6) tzinfo (1.2.10) thread_safe (~> 0.1) tzinfo-data (1.2020.6) tzinfo (>= 1.0.0) + unicode-display_width (2.6.0) wdm (0.1.1) + webrick (1.9.1) PLATFORMS ruby x86_64-darwin-20 DEPENDENCIES - jekyll (~> 3.9.0) + jekyll (~> 4.4.1) jekyll-github-metadata kramdown-parser-gfm tzinfo (~> 1.2) @@ -90,4 +112,4 @@ DEPENDENCIES wdm (~> 0.1.0) BUNDLED WITH - 2.2.5 + 2.6.9 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e2e2249..fa48eda 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -7,8 +7,8 @@ We love your input! We want to make contributing to this project as easy and tra - Proposing new features - Becoming a maintainer -## We Develop with Github -We use github to host code, to track issues and feature requests, as well as accept pull requests. +## We Develop with GitHub +We use GitHub to host code, to track issues and feature requests, as well as accept pull requests. ## All Code Changes Happen Through Pull Requests Pull requests are the best way to propose changes to the codebase We actively welcome your pull requests: @@ -23,7 +23,7 @@ Pull requests are the best way to propose changes to the codebase We actively w ## Any contributions you make will be under the MIT Software License In short, when you submit code changes, your submissions are understood to be under the same [MIT License](http://choosealicense.com/licenses/mit/) that covers the project. Feel free to contact the maintainers if that's a concern. -## Report bugs using Github's [issues](https://github.com/robotsandpencils/xcodesapp/issues) +## Report bugs using GitHub [issues](https://github.com/robotsandpencils/xcodesapp/issues) We use GitHub issues to track public bugs. Report a bug by [opening a new issue](); it's that easy! ## Write bug reports with detail, background, and sample code diff --git a/README.md b/README.md index 4a8f228..a5c26de 100644 --- a/README.md +++ b/README.md @@ -22,11 +22,14 @@ XcodesApp is now part of the `XcodesOrg` - [read more here](nextstep.md) - Just click a button to make a version active with `xcode-select`. - View release notes, OS compatibility, included SDKs and compilers from [Xcode Releases](https://xcodereleases.com). - Dark/Light Mode supported +- Security Key Authentication supported ## Platforms/Runtimes - Xcodes supports downloading the Apple runtimes via the app. Simply click on the Platform, and Xcodes will install automatically for you. +**Note: iOS 18+, tvOS 18+, watchOS 11+, visionOS 2+ requires that Xcode 16.1 Beta 3+ be installed and active.** + ## Experiments - Thanks to the wonderful work of [https://github.com/saagarjha/unxip](https://github.com/saagarjha/unxip), turn on the experiment to increase your unxipping time by up to 70%! More can be found on his repo, but bugs, high memory may occur if used. @@ -50,13 +53,13 @@ The following languages are supported because of the following community users! |Ukranian 🇺🇦 |[@gelosi](https://github.com/gelosi)|Japanese 🇯🇵|[@tatsuz0u](https://github.com/tatsuz0u)| |German 🇩🇪|[@drct](https://github.com/drct)|Dutch 🇳🇱|[@jfversluis](https://github/com/jfversluis)| |Brazilian Portuguese 🇧🇷|[@brunomunizaf](https://github.com/brunomunizaf)|Polish 🇵🇱|[@jakex7](https://github.com/jakex7)| -|Catalan|[@ferranabello](https://github.com/ferranabello)| +|Catalan|[@ferranabello](https://github.com/ferranabello)|Greek 🇬🇷|[@alladinian](https://github.com/alladinian) Want to add more languages? Simply create a PR with the updated strings file. ## Installation -v1.X - requires MacOS 11 or newer -v2.X - requires MacOS 13 +v1.X - requires macOS 11 or newer +v2.X - requires macOS 13 ### Install with Homebrew @@ -160,7 +163,8 @@ popd # Attach the zip that was created in the Product directory to the release # Publish the release -# Update the [Homebrew Cask](https://github.com/RobotsAndPencils/homebrew-cask/blob/master/Casks/xcodes.rb). +shasum -a 256 xcodes.zip +# Update the [Homebrew Cask](https://github.com/XcodesOrg/homebrew-cask/blob/master/Casks/x/xcodes.rb). ``` diff --git a/Xcodes.xcodeproj/project.pbxproj b/Xcodes.xcodeproj/project.pbxproj index 4523900..7062ad2 100644 --- a/Xcodes.xcodeproj/project.pbxproj +++ b/Xcodes.xcodeproj/project.pbxproj @@ -7,6 +7,10 @@ objects = { /* Begin PBXBuildFile section */ + 15F5B8902CCF09B900705E2F /* CryptoKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 15F5B88F2CCF09B900705E2F /* CryptoKit.framework */; }; + 33027E342CA8C18800CB387C /* LibFido2Swift in Frameworks */ = {isa = PBXBuildFile; productRef = 334A932B2CA885A400A5E079 /* LibFido2Swift */; }; + 3328073F2CA5E2C80036F691 /* SignInSecurityKeyPinView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3328073E2CA5E2C80036F691 /* SignInSecurityKeyPinView.swift */; }; + 332807412CA5EA820036F691 /* SignInSecurityKeyTouchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 332807402CA5EA820036F691 /* SignInSecurityKeyTouchView.swift */; }; 36741BFD291E4FDB00A85AAE /* DownloadPreferencePane.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36741BFC291E4FDB00A85AAE /* DownloadPreferencePane.swift */; }; 36741BFF291E50F500A85AAE /* FileError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36741BFE291E50F500A85AAE /* FileError.swift */; }; 536CFDD2263C94DE00026CE0 /* SignedInView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 536CFDD1263C94DE00026CE0 /* SignedInView.swift */; }; @@ -25,6 +29,7 @@ B0C6AD042AD6E65700E64698 /* ReleaseDateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0C6AD032AD6E65700E64698 /* ReleaseDateView.swift */; }; B0C6AD0B2AD9178E00E64698 /* IdenticalBuildView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0C6AD0A2AD9178E00E64698 /* IdenticalBuildView.swift */; }; B0C6AD0D2AD91D7900E64698 /* IconView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0C6AD0C2AD91D7900E64698 /* IconView.swift */; }; + BDBAB7452B9FF55800694B0B /* TrailingIconLabelStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDBAB7442B9FF55800694B0B /* TrailingIconLabelStyle.swift */; }; CA11E7BA2598476C00D2EE1C /* XcodeCommands.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA11E7B92598476C00D2EE1C /* XcodeCommands.swift */; }; CA2518EC25A7FF2B00F08414 /* AppStateUpdateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA2518EB25A7FF2B00F08414 /* AppStateUpdateTests.swift */; }; CA25192A25A9644800F08414 /* XcodeInstallState.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA25192925A9644800F08414 /* XcodeInstallState.swift */; }; @@ -110,17 +115,19 @@ CAFE4AB425B7D3AF0064FE51 /* AdvancedPreferencePane.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAFE4AB325B7D3AF0064FE51 /* AdvancedPreferencePane.swift */; }; CAFE4ABC25B7D54B0064FE51 /* UpdatesPreferencePane.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAFE4ABB25B7D54B0064FE51 /* UpdatesPreferencePane.swift */; }; CAFFFED8259CDA5000903F81 /* XcodeListViewRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAFFFED7259CDA5000903F81 /* XcodeListViewRow.swift */; }; + D93F95C12E0C8C1A00238FB5 /* TagView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D93F95C02E0C8C1A00238FB5 /* TagView.swift */; }; E689540325BE8C64000EBCEA /* DockProgress in Frameworks */ = {isa = PBXBuildFile; productRef = E689540225BE8C64000EBCEA /* DockProgress */; }; E81D7EA02805250100A205FC /* Collection+.swift in Sources */ = {isa = PBXBuildFile; fileRef = E81D7E9F2805250100A205FC /* Collection+.swift */; }; E832EAF82B0FBCF4001B570D /* RuntimeInstallationStepDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E832EAF72B0FBCF4001B570D /* RuntimeInstallationStepDetailView.swift */; }; + E83FDC442CBB649100679C6B /* Sparkle in Frameworks */ = {isa = PBXBuildFile; productRef = E83FDC432CBB649100679C6B /* Sparkle */; }; E84B7D0D2B296A8900DBDA2B /* NavigationSplitViewWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = E84B7D0C2B296A8900DBDA2B /* NavigationSplitViewWrapper.swift */; }; E84E4F522B323A5F003F3959 /* CornerRadiusModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = E84E4F512B323A5F003F3959 /* CornerRadiusModifier.swift */; }; E84E4F542B333864003F3959 /* PlatformsListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E84E4F532B333864003F3959 /* PlatformsListView.swift */; }; E84E4F572B335094003F3959 /* OrderedCollections in Frameworks */ = {isa = PBXBuildFile; productRef = E84E4F562B335094003F3959 /* OrderedCollections */; }; + E862D43B2CC8B26F00BAA376 /* SRP in Frameworks */ = {isa = PBXBuildFile; productRef = E862D43A2CC8B26F00BAA376 /* SRP */; }; E86671272B309D2F0048559A /* PlatformsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E86671262B309D2F0048559A /* PlatformsView.swift */; }; E87AB3C52939B65E00D72F43 /* Hardware.swift in Sources */ = {isa = PBXBuildFile; fileRef = E87AB3C42939B65E00D72F43 /* Hardware.swift */; }; E87DD6EB25D053FA00D86808 /* Progress+.swift in Sources */ = {isa = PBXBuildFile; fileRef = E87DD6EA25D053FA00D86808 /* Progress+.swift */; }; - E891A1C42B43ACF900A1B9D1 /* Sparkle in Frameworks */ = {isa = PBXBuildFile; productRef = E891A1C32B43ACF900A1B9D1 /* Sparkle */; }; E89342FA25EDCC17007CF557 /* NotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = E89342F925EDCC17007CF557 /* NotificationManager.swift */; }; E8977EA325C11E1500835F80 /* PreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8977EA225C11E1500835F80 /* PreferencesView.swift */; }; E8B20CBF2A2EDEC20057D816 /* SDKs+Xcode.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8B20CBE2A2EDEC20057D816 /* SDKs+Xcode.swift */; }; @@ -133,6 +140,7 @@ E8DA461125FAF7FB002E85EF /* NotificationsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8DA461025FAF7FB002E85EF /* NotificationsView.swift */; }; E8E98A9025D8631800EC89A0 /* InstallationStepRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAFBC3FF259AC17F00E2A3D8 /* InstallationStepRowView.swift */; }; E8E98A9625D863D700EC89A0 /* InstallationStepDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8E98A9525D863D700EC89A0 /* InstallationStepDetailView.swift */; }; + E8EE58C02E1CC2A50003FA9F /* RuntimeArchitecture.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8EE58BF2E1CC2A50003FA9F /* RuntimeArchitecture.swift */; }; E8F44A1E296B4CD7002D6592 /* Path in Frameworks */ = {isa = PBXBuildFile; productRef = E8F44A1D296B4CD7002D6592 /* Path */; }; E8FA00542B5B109800769CE0 /* com.xcodesorg.xcodesapp.Helper in Copy Helper */ = {isa = PBXBuildFile; fileRef = CA9FF8AE2595967A00E47BAF /* com.xcodesorg.xcodesapp.Helper */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; E8FD5727291EE4AC001E004C /* AsyncNetworkService in Frameworks */ = {isa = PBXBuildFile; productRef = E8FD5726291EE4AC001E004C /* AsyncNetworkService */; }; @@ -191,6 +199,9 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 15F5B88F2CCF09B900705E2F /* CryptoKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CryptoKit.framework; path = System/Library/Frameworks/CryptoKit.framework; sourceTree = SDKROOT; }; + 3328073E2CA5E2C80036F691 /* SignInSecurityKeyPinView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignInSecurityKeyPinView.swift; sourceTree = ""; }; + 332807402CA5EA820036F691 /* SignInSecurityKeyTouchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignInSecurityKeyTouchView.swift; sourceTree = ""; }; 36741BFC291E4FDB00A85AAE /* DownloadPreferencePane.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DownloadPreferencePane.swift; sourceTree = ""; }; 36741BFE291E50F500A85AAE /* FileError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileError.swift; sourceTree = ""; }; 536CFDD1263C94DE00026CE0 /* SignedInView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignedInView.swift; sourceTree = ""; }; @@ -209,6 +220,7 @@ B0C6AD032AD6E65700E64698 /* ReleaseDateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReleaseDateView.swift; sourceTree = ""; }; B0C6AD0A2AD9178E00E64698 /* IdenticalBuildView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IdenticalBuildView.swift; sourceTree = ""; }; B0C6AD0C2AD91D7900E64698 /* IconView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconView.swift; sourceTree = ""; }; + BDBAB7442B9FF55800694B0B /* TrailingIconLabelStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrailingIconLabelStyle.swift; sourceTree = ""; }; CA11E7B92598476C00D2EE1C /* XcodeCommands.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XcodeCommands.swift; sourceTree = ""; }; CA2518EB25A7FF2B00F08414 /* AppStateUpdateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppStateUpdateTests.swift; sourceTree = ""; }; CA25192925A9644800F08414 /* XcodeInstallState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XcodeInstallState.swift; sourceTree = ""; }; @@ -311,6 +323,7 @@ CAFE4ABB25B7D54B0064FE51 /* UpdatesPreferencePane.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UpdatesPreferencePane.swift; sourceTree = ""; }; CAFFFED7259CDA5000903F81 /* XcodeListViewRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XcodeListViewRow.swift; sourceTree = ""; }; CAFFFEEE259CEAC400903F81 /* RingProgressViewStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RingProgressViewStyle.swift; sourceTree = ""; }; + D93F95C02E0C8C1A00238FB5 /* TagView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagView.swift; sourceTree = ""; }; E81D7E9F2805250100A205FC /* Collection+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Collection+.swift"; sourceTree = ""; }; E832EAF72B0FBCF4001B570D /* RuntimeInstallationStepDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuntimeInstallationStepDetailView.swift; sourceTree = ""; }; E84B7D0C2B296A8900DBDA2B /* NavigationSplitViewWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationSplitViewWrapper.swift; sourceTree = ""; }; @@ -330,6 +343,7 @@ E8D655BF288DD04700A139C2 /* SelectedActionType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectedActionType.swift; sourceTree = ""; }; E8DA461025FAF7FB002E85EF /* NotificationsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsView.swift; sourceTree = ""; }; E8E98A9525D863D700EC89A0 /* InstallationStepDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstallationStepDetailView.swift; sourceTree = ""; }; + E8EE58BF2E1CC2A50003FA9F /* RuntimeArchitecture.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuntimeArchitecture.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -344,12 +358,15 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 15F5B8902CCF09B900705E2F /* CryptoKit.framework in Frameworks */, + 33027E342CA8C18800CB387C /* LibFido2Swift in Frameworks */, CABFA9E42592F08E00380FEE /* Version in Frameworks */, CABFA9FD2592F13300380FEE /* LegibleError in Frameworks */, E689540325BE8C64000EBCEA /* DockProgress in Frameworks */, CA9FF86D25951C6E00E47BAF /* XCModel in Frameworks */, CABFA9F82592F0F900380FEE /* KeychainAccess in Frameworks */, - E891A1C42B43ACF900A1B9D1 /* Sparkle in Frameworks */, + E83FDC442CBB649100679C6B /* Sparkle in Frameworks */, + E862D43B2CC8B26F00BAA376 /* SRP in Frameworks */, CAA858CD25A3D8BC00ACF8C0 /* ErrorHandling in Frameworks */, E8C0EB1A291EF43E0081528A /* XcodesKit in Frameworks */, E8FD5727291EE4AC001E004C /* AsyncNetworkService in Frameworks */, @@ -374,12 +391,14 @@ 63EAA4E9259944340046AB8F /* Common */ = { isa = PBXGroup; children = ( + D93F95C02E0C8C1A00238FB5 /* TagView.swift */, CAC281CC259F97FA00B8AB0B /* ObservingProgressIndicator.swift */, 63EAA4EA259944450046AB8F /* ProgressButton.swift */, CA452BAF259FD9770072DFA4 /* ProgressIndicator.swift */, 536CFDD3263C9A8000026CE0 /* XcodesSheet.swift */, 53CBAB2B263DCC9100410495 /* XcodesAlert.swift */, E84B7D0C2B296A8900DBDA2B /* NavigationSplitViewWrapper.swift */, + BDBAB7442B9FF55800694B0B /* TrailingIconLabelStyle.swift */, ); path = Common; sourceTree = ""; @@ -405,6 +424,7 @@ CA538A12255A4F7C00E64DD7 /* Frameworks */ = { isa = PBXGroup; children = ( + 15F5B88F2CCF09B900705E2F /* CryptoKit.framework */, ); name = Frameworks; sourceTree = ""; @@ -451,6 +471,8 @@ CA735108257BF96D00EA9CF8 /* AttributedText.swift */, CA73510C257BFCEF00EA9CF8 /* NSAttributedString+.swift */, CA5D781D257365D6008EDE9D /* PinCodeTextView.swift */, + 3328073E2CA5E2C80036F691 /* SignInSecurityKeyPinView.swift */, + 332807402CA5EA820036F691 /* SignInSecurityKeyTouchView.swift */, CAA1CB34255A5AD5003FD669 /* SignInCredentialsView.swift */, CAA1CB44255A5B60003FD669 /* SignIn2FAView.swift */, CAA1CB48255A5C97003FD669 /* SignInSMSView.swift */, @@ -640,6 +662,7 @@ E8E98A9425D863B100EC89A0 /* InfoPane */ = { isa = PBXGroup; children = ( + E8EE58BF2E1CC2A50003FA9F /* RuntimeArchitecture.swift */, B0403CEF2AD92D7B00137C09 /* ReleaseNotesView.swift */, B0403CF32AD9381D00137C09 /* SDKsView.swift */, B0403CF52AD9849E00137C09 /* CompilersView.swift */, @@ -710,7 +733,9 @@ E8C0EB19291EF43E0081528A /* XcodesKit */, E8F44A1D296B4CD7002D6592 /* Path */, E84E4F562B335094003F3959 /* OrderedCollections */, - E891A1C32B43ACF900A1B9D1 /* Sparkle */, + E83FDC432CBB649100679C6B /* Sparkle */, + 334A932B2CA885A400A5E079 /* LibFido2Swift */, + E862D43A2CC8B26F00BAA376 /* SRP */, ); productName = XcodesMac; productReference = CAD2E79E2449574E00113D76 /* Xcodes.app */; @@ -783,6 +808,7 @@ "pt-BR", nl, pl, + ar, ); mainGroup = CAD2E7952449574E00113D76; packageReferences = ( @@ -797,7 +823,8 @@ E8FD5725291EE4AC001E004C /* XCRemoteSwiftPackageReference "AsyncHTTPNetworkService" */, E8F44A1C296B4CD7002D6592 /* XCRemoteSwiftPackageReference "Path" */, E84E4F552B335094003F3959 /* XCRemoteSwiftPackageReference "swift-collections" */, - E891A1C22B43ACA400A1B9D1 /* XCRemoteSwiftPackageReference "Sparkle" */, + E83FDC422CBB649100679C6B /* XCRemoteSwiftPackageReference "Sparkle" */, + 33027E282CA8BB5800CB387C /* XCRemoteSwiftPackageReference "LibFido2Swift" */, ); productRefGroup = CAD2E79F2449574E00113D76 /* Products */; projectDirPath = ""; @@ -885,6 +912,7 @@ CABFAA492593162500380FEE /* Bundle+InfoPlistValues.swift in Sources */, CA9FF8662595130600E47BAF /* View+IsHidden.swift in Sources */, CAE4248C259A68B800B8B246 /* Optional+IsNotNil.swift in Sources */, + 3328073F2CA5E2C80036F691 /* SignInSecurityKeyPinView.swift in Sources */, B0C6AD0D2AD91D7900E64698 /* IconView.swift in Sources */, CA9FF9362595B44700E47BAF /* HelperClient.swift in Sources */, B0C6AD042AD6E65700E64698 /* ReleaseDateView.swift in Sources */, @@ -910,8 +938,11 @@ E84E4F522B323A5F003F3959 /* CornerRadiusModifier.swift in Sources */, B0403CF22AD934B600137C09 /* CompatibilityView.swift in Sources */, B0403CFE2ADA712C00137C09 /* InfoPaneControls.swift in Sources */, + D93F95C12E0C8C1A00238FB5 /* TagView.swift in Sources */, 53CBAB2C263DCC9100410495 /* XcodesAlert.swift in Sources */, + 332807412CA5EA820036F691 /* SignInSecurityKeyTouchView.swift in Sources */, CA61A6E0259835580008926E /* Xcode.swift in Sources */, + E8EE58C02E1CC2A50003FA9F /* RuntimeArchitecture.swift in Sources */, CAE4247F259A666100B8B246 /* MainWindow.swift in Sources */, CA452BB0259FD9770072DFA4 /* ProgressIndicator.swift in Sources */, B0403CF02AD92D7B00137C09 /* ReleaseNotesView.swift in Sources */, @@ -919,6 +950,7 @@ CA9FF84E2595079F00E47BAF /* ScrollingTextView.swift in Sources */, E832EAF82B0FBCF4001B570D /* RuntimeInstallationStepDetailView.swift in Sources */, CABFA9C12592EEEA00380FEE /* Version+.swift in Sources */, + BDBAB7452B9FF55800694B0B /* TrailingIconLabelStyle.swift in Sources */, E8D655C0288DD04700A139C2 /* SelectedActionType.swift in Sources */, 36741BFD291E4FDB00A85AAE /* DownloadPreferencePane.swift in Sources */, E84E4F542B333864003F3959 /* PlatformsListView.swift in Sources */, @@ -1054,7 +1086,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MACOSX_DEPLOYMENT_TARGET = 12.0; + MACOSX_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; @@ -1074,7 +1106,7 @@ CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 23; + CURRENT_PROJECT_VERSION = 31; DEVELOPMENT_ASSET_PATHS = "\"Xcodes/Preview Content\""; DEVELOPMENT_TEAM = ""; ENABLE_HARDENED_RUNTIME = NO; @@ -1086,7 +1118,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = 2.0.2; + MARKETING_VERSION = 2.4.2; PRODUCT_BUNDLE_IDENTIFIER = com.xcodesorg.xcodesapp; PRODUCT_NAME = Xcodes; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -1249,7 +1281,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MACOSX_DEPLOYMENT_TARGET = 12.0; + MACOSX_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; @@ -1307,7 +1339,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MACOSX_DEPLOYMENT_TARGET = 12.0; + MACOSX_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = macosx; @@ -1326,7 +1358,7 @@ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 23; + CURRENT_PROJECT_VERSION = 31; DEVELOPMENT_ASSET_PATHS = "\"Xcodes/Preview Content\""; DEVELOPMENT_TEAM = ZU6GR6B2FY; ENABLE_HARDENED_RUNTIME = YES; @@ -1338,7 +1370,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = 2.0.2; + MARKETING_VERSION = 2.4.2; PRODUCT_BUNDLE_IDENTIFIER = com.xcodesorg.xcodesapp; PRODUCT_NAME = Xcodes; SWIFT_VERSION = 5.0; @@ -1354,7 +1386,7 @@ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 23; + CURRENT_PROJECT_VERSION = 31; DEVELOPMENT_ASSET_PATHS = "\"Xcodes/Preview Content\""; DEVELOPMENT_TEAM = ZU6GR6B2FY; ENABLE_HARDENED_RUNTIME = YES; @@ -1366,7 +1398,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = 2.0.2; + MARKETING_VERSION = 2.4.2; PRODUCT_BUNDLE_IDENTIFIER = com.xcodesorg.xcodesapp; PRODUCT_NAME = Xcodes; SWIFT_VERSION = 5.0; @@ -1464,6 +1496,14 @@ /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ + 33027E282CA8BB5800CB387C /* XCRemoteSwiftPackageReference "LibFido2Swift" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/kinoroy/LibFido2Swift.git"; + requirement = { + kind = upToNextMinorVersion; + minimumVersion = 0.1.4; + }; + }; CA9FF86B25951C6E00E47BAF /* XCRemoteSwiftPackageReference "data" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/xcodereleases/data"; @@ -1525,7 +1565,15 @@ repositoryURL = "https://github.com/sindresorhus/DockProgress"; requirement = { kind = upToNextMinorVersion; - minimumVersion = 3.2.0; + minimumVersion = 4.3.1; + }; + }; + E83FDC422CBB649100679C6B /* XCRemoteSwiftPackageReference "Sparkle" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/sparkle-project/Sparkle"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 2.6.4; }; }; E84E4F552B335094003F3959 /* XCRemoteSwiftPackageReference "swift-collections" */ = { @@ -1536,14 +1584,6 @@ minimumVersion = 1.0.5; }; }; - E891A1C22B43ACA400A1B9D1 /* XCRemoteSwiftPackageReference "Sparkle" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/sparkle-project/Sparkle"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 2.5.2; - }; - }; E8F44A1C296B4CD7002D6592 /* XCRemoteSwiftPackageReference "Path" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/mxcl/Path.swift"; @@ -1563,6 +1603,10 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ + 334A932B2CA885A400A5E079 /* LibFido2Swift */ = { + isa = XCSwiftPackageProductDependency; + productName = LibFido2Swift; + }; CA9FF86C25951C6E00E47BAF /* XCModel */ = { isa = XCSwiftPackageProductDependency; package = CA9FF86B25951C6E00E47BAF /* XCRemoteSwiftPackageReference "data" */; @@ -1607,15 +1651,19 @@ package = E689540125BE8C64000EBCEA /* XCRemoteSwiftPackageReference "DockProgress" */; productName = DockProgress; }; + E83FDC432CBB649100679C6B /* Sparkle */ = { + isa = XCSwiftPackageProductDependency; + package = E83FDC422CBB649100679C6B /* XCRemoteSwiftPackageReference "Sparkle" */; + productName = Sparkle; + }; E84E4F562B335094003F3959 /* OrderedCollections */ = { isa = XCSwiftPackageProductDependency; package = E84E4F552B335094003F3959 /* XCRemoteSwiftPackageReference "swift-collections" */; productName = OrderedCollections; }; - E891A1C32B43ACF900A1B9D1 /* Sparkle */ = { + E862D43A2CC8B26F00BAA376 /* SRP */ = { isa = XCSwiftPackageProductDependency; - package = E891A1C22B43ACA400A1B9D1 /* XCRemoteSwiftPackageReference "Sparkle" */; - productName = Sparkle; + productName = SRP; }; E8C0EB19291EF43E0081528A /* XcodesKit */ = { isa = XCSwiftPackageProductDependency; diff --git a/Xcodes.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Xcodes.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 220940e..84e3336 100644 --- a/Xcodes.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Xcodes.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -10,6 +10,15 @@ "version": null } }, + { + "package": "big-num", + "repositoryURL": "https://github.com/adam-fowler/big-num", + "state": { + "branch": null, + "revision": "5c5511ad06aeb2b97d0868f7394e14a624bfb1c7", + "version": "2.0.2" + } + }, { "package": "CombineExpectations", "repositoryURL": "https://github.com/groue/CombineExpectations", @@ -33,8 +42,8 @@ "repositoryURL": "https://github.com/sindresorhus/DockProgress", "state": { "branch": null, - "revision": "7100b68571e2dafe3a06ad5905b80fc3b0107b4b", - "version": "3.2.0" + "revision": "d4f23b5a8f5ca0fac393eb7ba78c2fe3e32e52da", + "version": "4.3.1" } }, { @@ -64,6 +73,15 @@ "version": "1.0.4" } }, + { + "package": "LibFido2Swift", + "repositoryURL": "https://github.com/kinoroy/LibFido2Swift.git", + "state": { + "branch": null, + "revision": "94d496d6f850dcbb3e8c4a27cd7eeabfad9f14e3", + "version": "0.1.4" + } + }, { "package": "Path.swift", "repositoryURL": "https://github.com/mxcl/Path.swift", @@ -78,8 +96,8 @@ "repositoryURL": "https://github.com/sparkle-project/Sparkle/", "state": { "branch": null, - "revision": "47d3d90aee3c52b6f61d04ceae426e607df62347", - "version": "2.5.2" + "revision": "0ef1ee0220239b3776f433314515fd849025673f", + "version": "2.6.4" } }, { @@ -91,6 +109,24 @@ "version": "1.0.5" } }, + { + "package": "swift-crypto", + "repositoryURL": "https://github.com/apple/swift-crypto", + "state": { + "branch": null, + "revision": "ddb07e896a2a8af79512543b1c7eb9797f8898a5", + "version": "1.1.7" + } + }, + { + "package": "swift-srp", + "repositoryURL": "https://github.com/xcodesOrg/swift-srp", + "state": { + "branch": "main", + "revision": "543aa0122a0257b992f6c7d62d18a26e3dffb8fe", + "version": null + } + }, { "package": "SwiftSoup", "repositoryURL": "https://github.com/scinfu/SwiftSoup", diff --git a/Xcodes/AppleAPI/Package.swift b/Xcodes/AppleAPI/Package.swift index b4c2d5c..f3eb5b7 100644 --- a/Xcodes/AppleAPI/Package.swift +++ b/Xcodes/AppleAPI/Package.swift @@ -1,24 +1,26 @@ -// swift-tools-version:5.3 +// swift-tools-version:5.7 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription let package = Package( name: "AppleAPI", - platforms: [.macOS(.v10_15)], + platforms: [.macOS(.v11)], products: [ // Products define the executables and libraries a package produces, and make them visible to other packages. .library( name: "AppleAPI", targets: ["AppleAPI"]), ], - dependencies: [], + dependencies: [ + .package(url: "https://github.com/xcodesOrg/swift-srp", branch: "main") + ], targets: [ // Targets are the basic building blocks of a package. A target can define a module or a test suite. // Targets can depend on other targets in this package, and on products in packages this package depends on. .target( name: "AppleAPI", - dependencies: []), + dependencies: [.product(name: "SRP", package: "swift-srp")]), .testTarget( name: "AppleAPITests", dependencies: ["AppleAPI"]), diff --git a/Xcodes/AppleAPI/Sources/AppleAPI/Client.swift b/Xcodes/AppleAPI/Sources/AppleAPI/Client.swift index 5b9bd08..1e8e473 100644 --- a/Xcodes/AppleAPI/Sources/AppleAPI/Client.swift +++ b/Xcodes/AppleAPI/Sources/AppleAPI/Client.swift @@ -1,5 +1,9 @@ import Foundation import Combine +import SRP +import Crypto +import CommonCrypto + public class Client { private static let authTypes = ["sa", "hsa", "non-sa", "hsa2"] @@ -8,8 +12,12 @@ public class Client { // MARK: - Login - public func login(accountName: String, password: String) -> AnyPublisher { + public func srpLogin(accountName: String, password: String) -> AnyPublisher { var serviceKey: String! + + let client = SRPClient(configuration: SRPConfiguration(.N2048)) + let clientKeys = client.generateKeys() + let a = clientKeys.public return Current.network.dataTask(with: URLRequest.itcServiceKey) .map(\.data) @@ -24,11 +32,45 @@ public class Client { .map { return (serviceKey, $0)} .eraseToAnyPublisher() } - .flatMap { (serviceKey, hashcash) -> AnyPublisher in + .flatMap { (serviceKey, hashcash) -> AnyPublisher<(String, String, ServerSRPInitResponse), Swift.Error> in + + return Current.network.dataTask(with: URLRequest.SRPInit(serviceKey: serviceKey, a: Data(a.bytes).base64EncodedString(), accountName: accountName)) + .map(\.data) + .decode(type: ServerSRPInitResponse.self, decoder: JSONDecoder()) + .map { return (serviceKey, hashcash, $0) } + .eraseToAnyPublisher() + } + .flatMap { (serviceKey, hashcash, srpInit) -> AnyPublisher in + guard let decodedB = Data(base64Encoded: srpInit.b) else { + return Fail(error: AuthenticationError.srpInvalidPublicKey) + .eraseToAnyPublisher() + } + + guard let decodedSalt = Data(base64Encoded: srpInit.salt) else { + return Fail(error: AuthenticationError.srpInvalidPublicKey) + .eraseToAnyPublisher() + } + + let iterations = srpInit.iteration + + do { + guard let encryptedPassword = self.pbkdf2(password: password, saltData: decodedSalt, keyByteCount: 32, prf: CCPseudoRandomAlgorithm(kCCPRFHmacAlgSHA256), rounds: iterations, protocol: srpInit.protocol) else { + return Fail(error: AuthenticationError.srpInvalidPublicKey) + .eraseToAnyPublisher() + } - return Current.network.dataTask(with: URLRequest.signIn(serviceKey: serviceKey, accountName: accountName, password: password, hashcash: hashcash)) + let sharedSecret = try client.calculateSharedSecret(password: encryptedPassword, salt: [UInt8](decodedSalt), clientKeys: clientKeys, serverPublicKey: .init([UInt8](decodedB))) + + let m1 = client.calculateClientProof(username: accountName, salt: [UInt8](decodedSalt), clientPublicKey: a, serverPublicKey: .init([UInt8](decodedB)), sharedSecret: .init(sharedSecret.bytes)) + let m2 = client.calculateServerProof(clientPublicKey: a, clientProof: m1, sharedSecret: .init([UInt8](sharedSecret.bytes))) + + return Current.network.dataTask(with: URLRequest.SRPComplete(serviceKey: serviceKey, hashcash: hashcash, accountName: accountName, c: srpInit.c, m1: Data(m1).base64EncodedString(), m2: Data(m2).base64EncodedString())) .mapError { $0 as Swift.Error } .eraseToAnyPublisher() + } catch { + return Fail(error: AuthenticationError.srpInvalidPublicKey) + .eraseToAnyPublisher() + } } .flatMap { result -> AnyPublisher in let (data, response) = result @@ -118,7 +160,7 @@ public class Client { case .twoStep: return Fail(error: AuthenticationError.accountUsesTwoStepAuthentication) .eraseToAnyPublisher() - case .twoFactor: + case .twoFactor, .securityKey: return self.handleTwoFactor(serviceKey: serviceKey, sessionID: sessionID, scnt: scnt, authOptions: authOptions) .eraseToAnyPublisher() case .unknown: @@ -139,7 +181,10 @@ public class Client { // SMS wasn't sent automatically because user needs to choose a phone to send to } else if authOptions.canFallBackToSMS { option = .smsPendingChoice - // Code is shown on trusted devices + // Code is shown on trusted devices + } else if authOptions.fsaChallenge != nil { + option = .securityKey + // User needs to use a physical security key to respond to the challenge } else { option = .codeSent } @@ -193,6 +238,33 @@ public class Client { .eraseToAnyPublisher() } + public func submitChallenge(response: Data, sessionData: AppleSessionData) -> AnyPublisher { + Result { + URLRequest.respondToChallenge(serviceKey: sessionData.serviceKey, sessionID: sessionData.sessionID, scnt: sessionData.scnt, response: response) + } + .publisher + .flatMap { request in + Current.network.dataTask(with: request) + .mapError { $0 as Error } + .tryMap { (data, response) throws -> (Data, URLResponse) in + guard let urlResponse = response as? HTTPURLResponse else { return (data, response) } + switch urlResponse.statusCode { + case 200..<300: + return (data, urlResponse) + case 400, 401: + throw AuthenticationError.incorrectSecurityCode + case 412: + throw AuthenticationError.appleIDAndPrivacyAcknowledgementRequired + case let code: + throw AuthenticationError.badStatusCode(statusCode: code, data: data, response: urlResponse) + } + } + .flatMap { (data, response) -> AnyPublisher in + self.updateSession(serviceKey: sessionData.serviceKey, sessionID: sessionData.sessionID, scnt: sessionData.scnt) + } + }.eraseToAnyPublisher() + } + // MARK: - Session /// Use the olympus session endpoint to see if the existing session is still valid @@ -227,6 +299,49 @@ public class Client { .mapError { $0 as Error } .eraseToAnyPublisher() } + + func sha256(data : Data) -> Data { + var hash = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH)) + data.withUnsafeBytes { + _ = CC_SHA256($0.baseAddress, CC_LONG(data.count), &hash) + } + return Data(hash) + } + + private func pbkdf2(password: String, saltData: Data, keyByteCount: Int, prf: CCPseudoRandomAlgorithm, rounds: Int, protocol srpProtocol: SRPProtocol) -> Data? { + guard let passwordData = password.data(using: .utf8) else { return nil } + let hashedPasswordDataRaw = sha256(data: passwordData) + let hashedPasswordData = switch srpProtocol { + case .s2k: hashedPasswordDataRaw + // the legacy s2k_fo protocol requires hex-encoding the digest before performing PBKDF2. + case .s2k_fo: Data(hashedPasswordDataRaw.hexEncodedString().lowercased().utf8) + } + + var derivedKeyData = Data(repeating: 0, count: keyByteCount) + let derivedCount = derivedKeyData.count + let derivationStatus: Int32 = derivedKeyData.withUnsafeMutableBytes { derivedKeyBytes in + let keyBuffer: UnsafeMutablePointer = + derivedKeyBytes.baseAddress!.assumingMemoryBound(to: UInt8.self) + return saltData.withUnsafeBytes { saltBytes -> Int32 in + let saltBuffer: UnsafePointer = saltBytes.baseAddress!.assumingMemoryBound(to: UInt8.self) + return hashedPasswordData.withUnsafeBytes { hashedPasswordBytes -> Int32 in + let passwordBuffer: UnsafePointer = hashedPasswordBytes.baseAddress!.assumingMemoryBound(to: UInt8.self) + return CCKeyDerivationPBKDF( + CCPBKDFAlgorithm(kCCPBKDF2), + passwordBuffer, + hashedPasswordData.count, + saltBuffer, + saltData.count, + prf, + UInt32(rounds), + keyBuffer, + derivedCount) + } + } + } + return derivationStatus == kCCSuccess ? derivedKeyData : nil + } + } // MARK: - Types @@ -252,6 +367,7 @@ public enum AuthenticationError: Swift.Error, LocalizedError, Equatable { case notDeveloperAppleId case notAuthorized case invalidResult(resultString: String?) + case srpInvalidPublicKey public var errorDescription: String? { switch self { @@ -286,6 +402,8 @@ public enum AuthenticationError: Swift.Error, LocalizedError, Equatable { return "You are not authorized. Please Sign in with your Apple ID first." case let .invalidResult(resultString): return resultString ?? "If you continue to have problems, please submit a bug report in the Help menu." + case .srpInvalidPublicKey: + return "Invalid Key" } } } @@ -326,27 +444,37 @@ public enum TwoFactorOption: Equatable { case smsSent(AuthOptionsResponse.TrustedPhoneNumber) case codeSent case smsPendingChoice + case securityKey +} + +public struct FSAChallenge: Equatable, Decodable { + public let challenge: String + public let keyHandles: [String] + public let allowedCredentials: String } public struct AuthOptionsResponse: Equatable, Decodable { public let trustedPhoneNumbers: [TrustedPhoneNumber]? public let trustedDevices: [TrustedDevice]? - public let securityCode: SecurityCodeInfo + public let securityCode: SecurityCodeInfo? public let noTrustedDevices: Bool? public let serviceErrors: [ServiceError]? + public let fsaChallenge: FSAChallenge? public init( trustedPhoneNumbers: [AuthOptionsResponse.TrustedPhoneNumber]?, trustedDevices: [AuthOptionsResponse.TrustedDevice]?, securityCode: AuthOptionsResponse.SecurityCodeInfo, noTrustedDevices: Bool? = nil, - serviceErrors: [ServiceError]? = nil + serviceErrors: [ServiceError]? = nil, + fsaChallenge: FSAChallenge? = nil ) { self.trustedPhoneNumbers = trustedPhoneNumbers self.trustedDevices = trustedDevices self.securityCode = securityCode self.noTrustedDevices = noTrustedDevices self.serviceErrors = serviceErrors + self.fsaChallenge = fsaChallenge } public var kind: Kind { @@ -354,6 +482,8 @@ public struct AuthOptionsResponse: Equatable, Decodable { return .twoStep } else if trustedPhoneNumbers != nil { return .twoFactor + } else if fsaChallenge != nil { + return .securityKey } else { return .unknown } @@ -416,7 +546,7 @@ public struct AuthOptionsResponse: Equatable, Decodable { } public enum Kind: Equatable { - case twoStep, twoFactor, unknown + case twoStep, twoFactor, securityKey, unknown } } @@ -453,3 +583,24 @@ public struct AppleProvider: Decodable, Equatable { public struct AppleUser: Decodable, Equatable { public let fullName: String } + +public struct ServerSRPInitResponse: Decodable { + let iteration: Int + let salt: String + let b: String + let c: String + let `protocol`: SRPProtocol +} + + + +extension String { + func base64ToU8Array() -> Data { + return Data(base64Encoded: self) ?? Data() + } +} +extension Data { + func hexEncodedString() -> String { + return map { String(format: "%02hhx", $0) }.joined() + } +} diff --git a/Xcodes/AppleAPI/Sources/AppleAPI/URLRequest+Apple.swift b/Xcodes/AppleAPI/Sources/AppleAPI/URLRequest+Apple.swift index d052d63..1460150 100644 --- a/Xcodes/AppleAPI/Sources/AppleAPI/URLRequest+Apple.swift +++ b/Xcodes/AppleAPI/Sources/AppleAPI/URLRequest+Apple.swift @@ -9,6 +9,11 @@ public extension URL { static let trust = URL(string: "https://idmsa.apple.com/appleauth/auth/2sv/trust")! static let federate = URL(string: "https://idmsa.apple.com/appleauth/auth/federate")! static let olympusSession = URL(string: "https://appstoreconnect.apple.com/olympus/v1/session")! + static let keyAuth = URL(string: "https://idmsa.apple.com/appleauth/auth/verify/security/key")! + + static let srpInit = URL(string: "https://idmsa.apple.com/appleauth/auth/signin/init")! + static let srpComplete = URL(string: "https://idmsa.apple.com/appleauth/auth/signin/complete?isRememberMeEnabled=false")! + } public extension URLRequest { @@ -105,6 +110,19 @@ public extension URLRequest { } return request } + + static func respondToChallenge(serviceKey: String, sessionID: String, scnt: String, response: Data) -> URLRequest { + var request = URLRequest(url: .keyAuth) + request.allHTTPHeaderFields = request.allHTTPHeaderFields ?? [:] + request.allHTTPHeaderFields?["X-Apple-ID-Session-Id"] = sessionID + request.allHTTPHeaderFields?["X-Apple-Widget-Key"] = serviceKey + request.allHTTPHeaderFields?["scnt"] = scnt + request.allHTTPHeaderFields?["Accept"] = "application/json" + request.allHTTPHeaderFields?["Content-Type"] = "application/json" + request.httpMethod = "POST" + request.httpBody = response + return request + } static func trust(serviceKey: String, sessionID: String, scnt: String) -> URLRequest { var request = URLRequest(url: .trust) @@ -136,4 +154,51 @@ public extension URLRequest { return request } + + static func SRPInit(serviceKey: String, a: String, accountName: String) -> URLRequest { + struct ServerSRPInitRequest: Encodable { + public let a: String + public let accountName: String + public let protocols: [SRPProtocol] + } + + var request = URLRequest(url: .srpInit) + request.httpMethod = "POST" + request.allHTTPHeaderFields = request.allHTTPHeaderFields ?? [:] + request.allHTTPHeaderFields?["Accept"] = "application/json" + request.allHTTPHeaderFields?["Content-Type"] = "application/json" + request.allHTTPHeaderFields?["X-Requested-With"] = "XMLHttpRequest" + request.allHTTPHeaderFields?["X-Apple-Widget-Key"] = serviceKey + + request.httpBody = try? JSONEncoder().encode(ServerSRPInitRequest(a: a, accountName: accountName, protocols: [.s2k, .s2k_fo])) + return request + } + + static func SRPComplete(serviceKey: String, hashcash: String, accountName: String, c: String, m1: String, m2: String) -> URLRequest { + struct ServerSRPCompleteRequest: Encodable { + let accountName: String + let c: String + let m1: String + let m2: String + let rememberMe: Bool + } + + var request = URLRequest(url: .srpComplete) + request.httpMethod = "POST" + request.allHTTPHeaderFields = request.allHTTPHeaderFields ?? [:] + request.allHTTPHeaderFields?["Accept"] = "application/json" + request.allHTTPHeaderFields?["Content-Type"] = "application/json" + request.allHTTPHeaderFields?["X-Requested-With"] = "XMLHttpRequest" + request.allHTTPHeaderFields?["X-Apple-Widget-Key"] = serviceKey + request.allHTTPHeaderFields?["X-Apple-HC"] = hashcash + + request.httpBody = try? JSONEncoder().encode(ServerSRPCompleteRequest(accountName: accountName, c: c, m1: m1, m2: m2, rememberMe: false)) + return request + } } + +public enum SRPProtocol: String, Codable { + case s2k, s2k_fo +} + + diff --git a/Xcodes/Backend/AppState+Install.swift b/Xcodes/Backend/AppState+Install.swift index 325f314..2c9fc84 100644 --- a/Xcodes/Backend/AppState+Install.swift +++ b/Xcodes/Backend/AppState+Install.swift @@ -13,8 +13,8 @@ extension AppState { // check to see if we should auto install for the user public func autoInstallIfNeeded() { - guard let storageValue = UserDefaults.standard.object(forKey: "autoInstallation") as? Int, let autoInstallType = AutoInstallationType(rawValue: storageValue) else { return } - + guard let storageValue = Current.defaults.get(forKey: "autoInstallation") as? Int, let autoInstallType = AutoInstallationType(rawValue: storageValue) else { return } + if autoInstallType == .none { return } // get newest xcode version @@ -227,6 +227,7 @@ extension AppState { self.error = error self.presentedAlert = .generic(title: localizeString("Alert.InstallArchive.Error.Title"), message: error.legibleLocalizedDescription) } + resetDockProgressTracking() }) .catch { _ in Just(installedXcode) @@ -473,19 +474,24 @@ extension AppState { // MARK: - Dock Progress Tracking private func setupDockProgress() { - DockProgress.progressInstance = nil - DockProgress.style = .bar + Task { @MainActor in + DockProgress.progressInstance = nil + DockProgress.style = .bar + + let progress = Progress(totalUnitCount: AppState.totalProgressUnits) + progress.kind = .file + progress.fileOperationKind = .downloading + overallProgress = progress + + DockProgress.progressInstance = overallProgress + } - let progress = Progress(totalUnitCount: AppState.totalProgressUnits) - progress.kind = .file - progress.fileOperationKind = .downloading - overallProgress = progress - - DockProgress.progressInstance = overallProgress } func resetDockProgressTracking() { - DockProgress.progress = 1 // Only way to completely remove overlay with DockProgress is setting progress to complete + Task { @MainActor in + DockProgress.progress = 1 // Only way to completely remove overlay with DockProgress is setting progress to complete + } } // MARK: - diff --git a/Xcodes/Backend/AppState+Runtimes.swift b/Xcodes/Backend/AppState+Runtimes.swift index 81abc5b..e2b1149 100644 --- a/Xcodes/Backend/AppState+Runtimes.swift +++ b/Xcodes/Backend/AppState+Runtimes.swift @@ -4,6 +4,7 @@ import OSLog import Combine import Path import AppleAPI +import Version extension AppState { func updateDownloadableRuntimes() { @@ -15,10 +16,10 @@ extension AppState { var updatedRuntime = runtime // This loops through and matches up the simulatorVersion to the mappings - let simulatorBuildUpdate = downloadableRuntimes.sdkToSimulatorMappings.first { SDKToSimulatorMapping in + let simulatorBuildUpdate = downloadableRuntimes.sdkToSimulatorMappings.filter { SDKToSimulatorMapping in SDKToSimulatorMapping.simulatorBuildUpdate == runtime.simulatorVersion.buildUpdate } - updatedRuntime.sdkBuildUpdate = simulatorBuildUpdate?.sdkBuildUpdate + updatedRuntime.sdkBuildUpdate = simulatorBuildUpdate.map { $0.sdkBuildUpdate } return updatedRuntime } @@ -48,6 +49,73 @@ extension AppState { } func downloadRuntime(runtime: DownloadableRuntime) { + guard let selectedXcode = self.allXcodes.first(where: { $0.selected }) else { + Logger.appState.error("No selected Xcode") + DispatchQueue.main.async { + self.presentedAlert = .generic(title: localizeString("Alert.Install.Error.Title"), message: "No selected Xcode. Please make an Xcode active") + } + return + } + // new runtimes + if runtime.contentType == .cryptexDiskImage { + // only selected xcodes > 16.1 beta 3 can download runtimes via a xcodebuild -downloadPlatform version + // only Runtimes coming from cryptexDiskImage can be downloaded via xcodebuild + if selectedXcode.version > Version(major: 16, minor: 0, patch: 0) { + downloadRuntimeViaXcodeBuild(runtime: runtime) + } else { + // not supported + Logger.appState.error("Trying to download a runtime we can't download") + DispatchQueue.main.async { + self.presentedAlert = .generic(title: localizeString("Alert.Install.Error.Title"), message: localizeString("Alert.Install.Error.Need.Xcode16.1")) + } + return + } + } else { + downloadRuntimeObseleteWay(runtime: runtime) + } + } + + func downloadRuntimeViaXcodeBuild(runtime: DownloadableRuntime) { + + let downloadRuntimeTask = Current.shell.downloadRuntime(runtime.platform.shortName, runtime.simulatorVersion.buildUpdate) + runtimePublishers[runtime.identifier] = Task { [weak self] in + guard let self = self else { return } + do { + for try await progress in downloadRuntimeTask { + if progress.isIndeterminate { + DispatchQueue.main.async { + self.setInstallationStep(of: runtime, to: .installing, postNotification: false) + } + } else { + DispatchQueue.main.async { + self.setInstallationStep(of: runtime, to: .downloading(progress: progress), postNotification: false) + } + } + + } + Logger.appState.debug("Done downloading runtime - \(runtime.name)") + + DispatchQueue.main.async { + guard let index = self.downloadableRuntimes.firstIndex(where: { $0.identifier == runtime.identifier }) else { return } + self.downloadableRuntimes[index].installState = .installed + self.update() + } + + } catch { + Logger.appState.error("Error downloading runtime: \(error.localizedDescription)") + DispatchQueue.main.async { + self.error = error + if let error = error as? String { + self.presentedAlert = .generic(title: localizeString("Alert.Install.Error.Title"), message: error) + } else { + self.presentedAlert = .generic(title: localizeString("Alert.Install.Error.Title"), message: error.legibleLocalizedDescription) + } + } + } + } + } + + func downloadRuntimeObseleteWay(runtime: DownloadableRuntime) { runtimePublishers[runtime.identifier] = Task { do { let downloadedURL = try await downloadRunTimeFull(runtime: runtime) @@ -57,6 +125,9 @@ extension AppState { self.setInstallationStep(of: runtime, to: .installing) } switch runtime.contentType { + case .cryptexDiskImage: + // not supported yet (do we need to for old packages?) + throw "Installing via cryptexDiskImage not support - please install manually from \(downloadedURL.description)" case .package: // not supported yet (do we need to for old packages?) throw "Installing via package not support - please install manually from \(downloadedURL.description)" @@ -80,19 +151,31 @@ extension AppState { Logger.appState.error("Error downloading runtime: \(error.localizedDescription)") DispatchQueue.main.async { self.error = error - self.presentedAlert = .generic(title: localizeString("Alert.Install.Error.Title"), message: error.legibleLocalizedDescription) + if let error = error as? String { + self.presentedAlert = .generic(title: localizeString("Alert.Install.Error.Title"), message: error) + } else { + self.presentedAlert = .generic(title: localizeString("Alert.Install.Error.Title"), message: error.legibleLocalizedDescription) + } } } } } func downloadRunTimeFull(runtime: DownloadableRuntime) async throws -> URL { + guard let source = runtime.source else { + throw "Invalid runtime source" + } + + guard let downloadPath = runtime.downloadPath else { + throw "Invalid runtime downloadPath" + } + // sets a proper cookie for runtimes - try await validateADCSession(path: runtime.downloadPath) + try await validateADCSession(path: downloadPath) - let downloader = Downloader(rawValue: UserDefaults.standard.string(forKey: "downloader") ?? "aria2") ?? .aria2 + let downloader = Downloader(rawValue: Current.defaults.string(forKey: "downloader") ?? "aria2") ?? .aria2 - let url = URL(string: runtime.source)! + let url = URL(string: source)! let expectedRuntimePath = Path.xcodesApplicationSupport/"\(url.lastPathComponent)" // aria2 downloads directly to the destination (instead of into /tmp first) so we need to make sure that the download isn't incomplete let aria2DownloadMetadataPath = expectedRuntimePath.parent/(expectedRuntimePath.basename() + ".aria2") @@ -123,9 +206,15 @@ extension AppState { } public func downloadRuntimeWithAria2(_ runtime: DownloadableRuntime, to destination: Path, aria2Path: Path) -> AsyncThrowingStream { - let cookies = AppleAPI.Current.network.session.configuration.httpCookieStorage?.cookies(for: runtime.url) ?? [] + guard let url = runtime.url else { + return AsyncThrowingStream { continuation in + continuation.finish(throwing: "Invalid or non existant runtime url") + } + } + + let cookies = AppleAPI.Current.network.session.configuration.httpCookieStorage?.cookies(for: url) ?? [] - return Current.shell.downloadWithAria2Async(aria2Path, runtime.url, destination, cookies) + return Current.shell.downloadWithAria2Async(aria2Path, url, destination, cookies) } @@ -140,7 +229,10 @@ extension AppState { runtimePublishers[runtime.identifier] = nil // If the download is cancelled by the user, clean up the download files that aria2 creates. - let url = URL(string: runtime.source)! + guard let source = runtime.source else { + return + } + let url = URL(string: source)! let expectedRuntimePath = Path.xcodesApplicationSupport/"\(url.lastPathComponent)" let aria2DownloadMetadataPath = expectedRuntimePath.parent/(expectedRuntimePath.basename() + ".aria2") diff --git a/Xcodes/Backend/AppState.swift b/Xcodes/Backend/AppState.swift index 7bec2cb..359c249 100644 --- a/Xcodes/Backend/AppState.swift +++ b/Xcodes/Backend/AppState.swift @@ -9,6 +9,26 @@ import Version import os.log import DockProgress import XcodesKit +import LibFido2Swift + +enum PreferenceKey: String { + case installPath + case localPath + case unxipExperiment + case createSymLinkOnSelect + case onSelectActionType + case showOpenInRosettaOption + case autoInstallation + case SUEnableAutomaticChecks + case includePrereleaseVersions + case downloader + case dataSource + case xcodeListCategory + case allowedMajorVersions + case hideSupportXcodes + + func isManaged() -> Bool { UserDefaults.standard.objectIsForced(forKey: self.rawValue) } +} class AppState: ObservableObject { private let client = AppleAPI.Client() @@ -66,18 +86,24 @@ class AppState: ObservableObject { } } + var disableLocalPathChange: Bool { PreferenceKey.localPath.isManaged() } + @Published var installPath = "" { didSet { Current.defaults.set(installPath, forKey: "installPath") } } - + + var disableInstallPathChange: Bool { PreferenceKey.installPath.isManaged() } + @Published var unxipExperiment = false { didSet { Current.defaults.set(unxipExperiment, forKey: "unxipExperiment") } } + var disableUnxipExperiment: Bool { PreferenceKey.unxipExperiment.isManaged() } + @Published var createSymLinkOnSelect = false { didSet { Current.defaults.set(createSymLinkOnSelect, forKey: "createSymLinkOnSelect") @@ -85,7 +111,7 @@ class AppState: ObservableObject { } var createSymLinkOnSelectDisabled: Bool { - return onSelectActionType == .rename + return onSelectActionType == .rename || PreferenceKey.createSymLinkOnSelect.isManaged() } @Published var onSelectActionType = SelectedActionType.none { @@ -98,12 +124,20 @@ class AppState: ObservableObject { } } + var onSelectActionTypeDisabled: Bool { PreferenceKey.onSelectActionType.isManaged() } + @Published var showOpenInRosettaOption = false { didSet { Current.defaults.set(showOpenInRosettaOption, forKey: "showOpenInRosettaOption") } } + @Published var terminateAfterLastWindowClosed = false { + didSet { + Current.defaults.set(terminateAfterLastWindowClosed, forKey: "terminateAfterLastWindowClosed") + } + } + // MARK: - Runtimes @Published var downloadableRuntimes: [DownloadableRuntime] = [] @@ -173,13 +207,14 @@ class AppState: ObservableObject { onSelectActionType = SelectedActionType(rawValue: Current.defaults.string(forKey: "onSelectActionType") ?? "none") ?? .none installPath = Current.defaults.string(forKey: "installPath") ?? Path.defaultInstallDirectory.string showOpenInRosettaOption = Current.defaults.bool(forKey: "showOpenInRosettaOption") ?? false + terminateAfterLastWindowClosed = Current.defaults.bool(forKey: "terminateAfterLastWindowClosed") ?? false } // MARK: Timer /// Runs a timer every 6 hours when app is open to check if it needs to auto install any xcodes func setupAutoInstallTimer() { - guard let storageValue = UserDefaults.standard.object(forKey: "autoInstallation") as? Int, let autoInstallType = AutoInstallationType(rawValue: storageValue) else { return } - + guard let storageValue = Current.defaults.get(forKey: "autoInstallation") as? Int, let autoInstallType = AutoInstallationType(rawValue: storageValue) else { return } + if autoInstallType == .none { return } autoInstallTimer = Timer.scheduledTimer(withTimeInterval: 60*60*6, repeats: true) { [weak self] _ in @@ -242,7 +277,7 @@ class AppState: ObservableObject { func signIn(username: String, password: String) { authError = nil - signIn(username: username, password: password) + signIn(username: username.lowercased(), password: password) .sink( receiveCompletion: { _ in }, receiveValue: { _ in } @@ -255,7 +290,7 @@ class AppState: ObservableObject { Current.defaults.set(username, forKey: "username") isProcessingAuthRequest = true - return client.login(accountName: username, password: password) + return client.srpLogin(accountName: username, password: password) .receive(on: DispatchQueue.main) .handleEvents( receiveOutput: { authenticationState in @@ -270,11 +305,17 @@ class AppState: ObservableObject { } func handleTwoFactorOption(_ option: TwoFactorOption, authOptions: AuthOptionsResponse, serviceKey: String, sessionID: String, scnt: String) { - self.presentedSheet = .twoFactor(.init( - option: option, - authOptions: authOptions, - sessionData: AppleSessionData(serviceKey: serviceKey, sessionID: sessionID, scnt: scnt) - )) + let sessionData = AppleSessionData(serviceKey: serviceKey, sessionID: sessionID, scnt: scnt) + + if option == .securityKey, fido2DeviceIsPresent() && !fido2DeviceNeedsPin() { + createAndSubmitSecurityKeyAssertationWithPinCode(nil, sessionData: sessionData, authOptions: authOptions) + } else { + self.presentedSheet = .twoFactor(.init( + option: option, + authOptions: authOptions, + sessionData: sessionData + )) + } } func requestSMS(to trustedPhoneNumber: AuthOptionsResponse.TrustedPhoneNumber, authOptions: AuthOptionsResponse, sessionData: AppleSessionData) { @@ -320,6 +361,83 @@ class AppState: ObservableObject { .store(in: &cancellables) } + private lazy var fido2 = FIDO2() + + func createAndSubmitSecurityKeyAssertationWithPinCode(_ pinCode: String?, sessionData: AppleSessionData, authOptions: AuthOptionsResponse) { + self.presentedSheet = .securityKeyTouchToConfirm + + guard let fsaChallenge = authOptions.fsaChallenge else { + // This shouldn't happen + // we shouldn't have called this method without setting the fsaChallenge + // so this is an assertionFailure + assertionFailure() + self.authError = "Something went wrong. Please file a bug report" + return + } + + // The challenge is encoded in Base64URL encoding + let challengeUrl = fsaChallenge.challenge + let challenge = FIDO2.base64urlToBase64(base64url: challengeUrl) + let origin = "https://idmsa.apple.com" + let rpId = "apple.com" + // Allowed creds is sent as a comma separated string + let validCreds = fsaChallenge.allowedCredentials.split(separator: ",").map(String.init) + + Task { + do { + let response = try fido2.respondToChallenge(args: ChallengeArgs(rpId: rpId, validCredentials: validCreds, devPin: pinCode, challenge: challenge, origin: origin)) + + Task { @MainActor in + self.isProcessingAuthRequest = true + } + + let respData = try JSONEncoder().encode(response) + client.submitChallenge(response: respData, sessionData: AppleSessionData(serviceKey: sessionData.serviceKey, sessionID: sessionData.sessionID, scnt: sessionData.scnt)) + .receive(on: DispatchQueue.main) + .handleEvents( + receiveOutput: { authenticationState in + self.authenticationState = authenticationState + }, + receiveCompletion: { completion in + self.handleAuthenticationFlowCompletion(completion) + self.isProcessingAuthRequest = false + } + ).sink( + receiveCompletion: { _ in }, + receiveValue: { _ in } + ).store(in: &cancellables) + } catch FIDO2Error.canceledByUser { + // User cancelled the auth flow + // we don't have to show an error + // because the sheet will already be dismissed + } catch { + Task { @MainActor in + authError = error + } + } + } + } + + func fido2DeviceIsPresent() -> Bool { + fido2.hasDeviceAttached() + } + + func fido2DeviceNeedsPin() -> Bool { + do { + return try fido2.deviceHasPin() + } catch { + Task { @MainActor in + authError = error + } + + return true + } + } + + func cancelSecurityKeyAssertationRequest() { + self.fido2.cancel() + } + private func handleAuthenticationFlowCompletion(_ completion: Subscribers.Completion) { switch completion { case let .failure(error): @@ -408,7 +526,7 @@ class AppState: ObservableObject { func checkMinVersionAndInstall(id: Xcode.ID) { guard let availableXcode = availableXcodes.first(where: { $0.version == id }) else { return } - // Check to see if users MacOS is supported + // Check to see if users macOS is supported if let requiredMacOSVersion = availableXcode.requiredMacOSVersion { if hasMinSupportedOS(requiredMacOSVersion: requiredMacOSVersion) { // prompt @@ -436,6 +554,11 @@ class AppState: ObservableObject { guard let availableXcode = availableXcodes.first(where: { $0.version == id }) else { return } installationPublishers[id] = signInIfNeeded() + .handleEvents( + receiveSubscription: { [unowned self] _ in + self.setInstallationStep(of: availableXcode.version, to: .authenticating) + } + ) .flatMap { [unowned self] in // signInIfNeeded might finish before the user actually authenticates if UI is involved. // This publisher will wait for the @Published authentication state to change to authenticated or unauthenticated before finishing, @@ -479,7 +602,7 @@ class AppState: ObservableObject { .mapError { $0 as Error } } .flatMap { [unowned self] in - self.install(.version(availableXcode), downloader: Downloader(rawValue: UserDefaults.standard.string(forKey: "downloader") ?? "aria2") ?? .aria2) + self.install(.version(availableXcode), downloader: Downloader(rawValue: Current.defaults.string(forKey: "downloader") ?? "aria2") ?? .aria2) } .receive(on: DispatchQueue.main) .sink( @@ -505,7 +628,7 @@ class AppState: ObservableObject { func installWithoutLogin(id: Xcode.ID) { guard let availableXcode = availableXcodes.first(where: { $0.version == id }) else { return } - installationPublishers[id] = self.install(.version(availableXcode), downloader: Downloader(rawValue: UserDefaults.standard.string(forKey: "downloader") ?? "aria2") ?? .aria2) + installationPublishers[id] = self.install(.version(availableXcode), downloader: Downloader(rawValue: Current.defaults.string(forKey: "downloader") ?? "aria2") ?? .aria2) .receive(on: DispatchQueue.main) .sink( receiveCompletion: { [unowned self] completion in diff --git a/Xcodes/Backend/DataSource.swift b/Xcodes/Backend/DataSource.swift index 69363ae..0391184 100644 --- a/Xcodes/Backend/DataSource.swift +++ b/Xcodes/Backend/DataSource.swift @@ -14,4 +14,6 @@ public enum DataSource: String, CaseIterable, Identifiable, CustomStringConverti case .xcodeReleases: return "Xcode Releases" } } + + var isManaged: Bool { PreferenceKey.dataSource.isManaged() } } diff --git a/Xcodes/Backend/Downloader.swift b/Xcodes/Backend/Downloader.swift index 41eb680..e155703 100644 --- a/Xcodes/Backend/Downloader.swift +++ b/Xcodes/Backend/Downloader.swift @@ -13,4 +13,6 @@ public enum Downloader: String, CaseIterable, Identifiable, CustomStringConverti case .aria2: return "aria2" } } + + var isManaged: Bool { PreferenceKey.downloader.isManaged() } } diff --git a/Xcodes/Backend/Environment.swift b/Xcodes/Backend/Environment.swift index 8470913..61574ce 100644 --- a/Xcodes/Backend/Environment.swift +++ b/Xcodes/Backend/Environment.swift @@ -116,7 +116,8 @@ public struct Shell { return AsyncThrowingStream { continuation in Task { - var progress = Progress() + // Assume progress will not have data races, so we manually opt-out isolation checks. + nonisolated(unsafe) var progress = Progress() progress.kind = .file progress.fileOperationKind = .downloading @@ -195,6 +196,77 @@ public struct Shell { return Process.run(unxipPath.url, workingDirectory: url.deletingLastPathComponent(), ["\(url.path)"]) } + public var downloadRuntime: (String, String) -> AsyncThrowingStream = { platform, version in + return AsyncThrowingStream { continuation in + Task { + // Assume progress will not have data races, so we manually opt-out isolation checks. + nonisolated(unsafe) var progress = Progress() + progress.kind = .file + progress.fileOperationKind = .downloading + + let process = Process() + let xcodeBuildPath = Path.root.usr.bin.join("xcodebuild").url + + process.executableURL = xcodeBuildPath + process.arguments = [ + "-downloadPlatform", + "\(platform)", + "-buildVersion", + "\(version)" + ] + + let stdOutPipe = Pipe() + process.standardOutput = stdOutPipe + let stdErrPipe = Pipe() + process.standardError = stdErrPipe + + let observer = NotificationCenter.default.addObserver( + forName: .NSFileHandleDataAvailable, + object: nil, + queue: OperationQueue.main + ) { note in + guard + // This should always be the case for Notification.Name.NSFileHandleDataAvailable + let handle = note.object as? FileHandle, + handle === stdOutPipe.fileHandleForReading || handle === stdErrPipe.fileHandleForReading + else { return } + + defer { handle.waitForDataInBackgroundAndNotify() } + + let string = String(decoding: handle.availableData, as: UTF8.self) + + // TODO: fix warning. ObservingProgressView is currently tied to an updating progress + progress.updateFromXcodebuild(text: string) + + continuation.yield(progress) + } + + stdOutPipe.fileHandleForReading.waitForDataInBackgroundAndNotify() + stdErrPipe.fileHandleForReading.waitForDataInBackgroundAndNotify() + + continuation.onTermination = { @Sendable _ in + process.terminate() + NotificationCenter.default.removeObserver(observer, name: .NSFileHandleDataAvailable, object: nil) + } + + do { + try process.run() + } catch { + continuation.finish(throwing: error) + } + + process.waitUntilExit() + + NotificationCenter.default.removeObserver(observer, name: .NSFileHandleDataAvailable, object: nil) + + guard process.terminationReason == .exit, process.terminationStatus == 0 else { + continuation.finish(throwing: ProcessExecutionError(process: process, standardOutput: "", standardError: "")) + return + } + continuation.finish() + } + } + } } public struct Files { diff --git a/Xcodes/Backend/Progress+.swift b/Xcodes/Backend/Progress+.swift index 6e7688c..ce2ff3b 100644 --- a/Xcodes/Backend/Progress+.swift +++ b/Xcodes/Backend/Progress+.swift @@ -70,5 +70,38 @@ extension Progress { } } + + func updateFromXcodebuild(text: String) { + self.totalUnitCount = 100 + self.completedUnitCount = 0 + self.localizedAdditionalDescription = "" // to not show the addtional + + do { + + let downloadPattern = #"(\d+\.\d+)% \(([\d.]+ (?:MB|GB)) of ([\d.]+ GB)\)"# + let downloadRegex = try NSRegularExpression(pattern: downloadPattern) + + // Search for matches in the text + if let match = downloadRegex.firstMatch(in: text, range: NSRange(text.startIndex..., in: text)) { + // Extract the percentage - simpler then trying to extract size MB/GB and convert to bytes. + if let percentRange = Range(match.range(at: 1), in: text), let percentDouble = Double(text[percentRange]) { + let percent = Int64(percentDouble.rounded()) + self.completedUnitCount = percent + } + } + + // "Downloading tvOS 18.1 Simulator (22J5567a): Installing..." or + // "Downloading tvOS 18.1 Simulator (22J5567a): Installing (registering download)..." + if text.range(of: "Installing") != nil { + // sets the progress to indeterminite to show animating progress + self.totalUnitCount = 0 + self.completedUnitCount = 0 + } + + } catch { + Logger.appState.error("Invalid regular expression") + } + + } } diff --git a/Xcodes/Backend/XcodeCommands.swift b/Xcodes/Backend/XcodeCommands.swift index b168bd4..cd21bf4 100644 --- a/Xcodes/Backend/XcodeCommands.swift +++ b/Xcodes/Backend/XcodeCommands.swift @@ -35,12 +35,11 @@ struct XcodeCommands: Commands { struct InstallButton: View { @EnvironmentObject var appState: AppState - @State private var isLoading = false let xcode: Xcode? var body: some View { - ProgressButton(isInProgress: isLoading) { + Button { install() } label: { Text("Install") @@ -49,7 +48,6 @@ struct InstallButton: View { } private func install() { - isLoading = true guard let xcode = xcode else { return } appState.checkMinVersionAndInstall(id: xcode.id) } @@ -61,7 +59,7 @@ struct CancelInstallButton: View { var body: some View { Button(action: cancelInstall) { - Image(systemName: "xmark.circle.fill") + Label("Cancel", systemImage: "xmark") } .help(localizeString("StopInstallation")) .buttonStyle(.plain) diff --git a/Xcodes/Frontend/About/AcknowledgementsView.swift b/Xcodes/Frontend/About/AcknowledgementsView.swift index bcc22f7..ccbaccd 100644 --- a/Xcodes/Frontend/About/AcknowledgementsView.swift +++ b/Xcodes/Frontend/About/AcknowledgementsView.swift @@ -10,7 +10,7 @@ struct AcknowledgmentsView: View { )! .addingAttribute(.foregroundColor, value: NSColor.labelColor) ) - .frame(minWidth: 500, minHeight: 500) + .frame(minWidth: 600, minHeight: 500) } } diff --git a/Xcodes/Frontend/Common/NavigationSplitViewWrapper.swift b/Xcodes/Frontend/Common/NavigationSplitViewWrapper.swift index 928e49a..92dafff 100644 --- a/Xcodes/Frontend/Common/NavigationSplitViewWrapper.swift +++ b/Xcodes/Frontend/Common/NavigationSplitViewWrapper.swift @@ -20,27 +20,16 @@ struct NavigationSplitViewWrapper: View where Sidebar: View, De } var body: some View { - if #available(iOS 16, macOS 13, tvOS 16, watchOS 9, visionOS 1, *) { - // Use the latest API available - NavigationSplitView { - - if #available(macOS 14, *) { - sidebar - .toolbar(removing: .sidebarToggle) - } else { - sidebar - } - } detail: { - detail - } - } else { - // Alternative code for earlier versions of OS. - NavigationView { - // The first column is the sidebar. + NavigationSplitView { + if #available(macOS 14, *) { + sidebar + .navigationSplitViewColumnWidth(min: 290, ideal: 290) + } else { sidebar - detail } - .navigationViewStyle(.columns) + } detail: { + detail } + .navigationSplitViewStyle(.balanced) } } diff --git a/Xcodes/Frontend/Common/ObservingProgressIndicator.swift b/Xcodes/Frontend/Common/ObservingProgressIndicator.swift index a677486..3302f0a 100644 --- a/Xcodes/Frontend/Common/ObservingProgressIndicator.swift +++ b/Xcodes/Frontend/Common/ObservingProgressIndicator.swift @@ -31,6 +31,7 @@ public struct ObservingProgressIndicator: View { self.progress = progress cancellable = progress.publisher(for: \.fractionCompleted) .combineLatest(progress.publisher(for: \.localizedAdditionalDescription)) + .combineLatest(progress.publisher(for: \.isIndeterminate)) .throttle(for: 1.0, scheduler: DispatchQueue.main, latest: true) .sink { [weak self] _ in self?.objectWillChange.send() } } @@ -82,6 +83,18 @@ struct ObservingProgressBar_Previews: PreviewProvider { style: .bar, showsAdditionalDescription: true ) + + ObservingProgressIndicator( + configure(Progress()) { + $0.kind = .file + $0.fileOperationKind = .downloading + $0.totalUnitCount = 0 + $0.completedUnitCount = 0 + }, + controlSize: .regular, + style: .bar, + showsAdditionalDescription: true + ) } .previewLayout(.sizeThatFits) } diff --git a/Xcodes/Frontend/Common/ProgressIndicator.swift b/Xcodes/Frontend/Common/ProgressIndicator.swift index 4c11f0a..bb80186 100644 --- a/Xcodes/Frontend/Common/ProgressIndicator.swift +++ b/Xcodes/Frontend/Common/ProgressIndicator.swift @@ -22,7 +22,10 @@ struct ProgressIndicator: NSViewRepresentable { nsView.doubleValue = doubleValue nsView.controlSize = controlSize nsView.isIndeterminate = isIndeterminate + nsView.usesThreadedAnimation = true + nsView.style = style + nsView.startAnimation(nil) } } diff --git a/Xcodes/Frontend/Common/TagView.swift b/Xcodes/Frontend/Common/TagView.swift new file mode 100644 index 0000000..9d97614 --- /dev/null +++ b/Xcodes/Frontend/Common/TagView.swift @@ -0,0 +1,24 @@ +// +// TagView.swift +// Xcodes +// +// Created by Matt Kiazyk on 2025-06-25.// + + +import SwiftUI + +struct TagView: View { + let text: String + + var body: some View { + Text(text) + .font(.system(size: 10)) + .foregroundColor(.primary) + .padding(.horizontal, 5) + .padding(.vertical, 2) + .background( + Capsule() + .fill(.quaternary) + ) + } +} diff --git a/Xcodes/Frontend/Common/TrailingIconLabelStyle.swift b/Xcodes/Frontend/Common/TrailingIconLabelStyle.swift new file mode 100644 index 0000000..19b07fc --- /dev/null +++ b/Xcodes/Frontend/Common/TrailingIconLabelStyle.swift @@ -0,0 +1,22 @@ +// +// TrailingIconLabelStyle.swift +// Xcodes +// +// Created by Daniel Chick on 3/11/24. +// Copyright © 2024 Robots and Pencils. All rights reserved. +// + +import SwiftUI + +struct TrailingIconLabelStyle: LabelStyle { + func makeBody(configuration: Configuration) -> some View { + HStack { + configuration.title + configuration.icon + } + } +} + +extension LabelStyle where Self == TrailingIconLabelStyle { + static var trailingIcon: Self { Self() } +} diff --git a/Xcodes/Frontend/Common/XcodesSheet.swift b/Xcodes/Frontend/Common/XcodesSheet.swift index a0270b5..2aa8f2c 100644 --- a/Xcodes/Frontend/Common/XcodesSheet.swift +++ b/Xcodes/Frontend/Common/XcodesSheet.swift @@ -4,6 +4,7 @@ import AppleAPI enum XcodesSheet: Identifiable { case signIn case twoFactor(SecondFactorData) + case securityKeyTouchToConfirm var id: Int { Kind(self).hashValue } @@ -16,12 +17,13 @@ enum XcodesSheet: Identifiable { extension XcodesSheet { private enum Kind: Hashable { - case signIn, twoFactor(TwoFactorOption) + case signIn, twoFactor(TwoFactorOption), securityKeyTouchToConfirm enum TwoFactorOption { case smsSent case codeSent case smsPendingChoice + case securityKeyPin } init(_ sheet: XcodesSheet) { @@ -32,7 +34,9 @@ extension XcodesSheet { case .smsSent: self = .twoFactor(.smsSent) case .smsPendingChoice: self = .twoFactor(.smsPendingChoice) case .codeSent: self = .twoFactor(.codeSent) + case .securityKey: self = .twoFactor(.securityKeyPin) } + case .securityKeyTouchToConfirm: self = .securityKeyTouchToConfirm } } } diff --git a/Xcodes/Frontend/InfoPane/CompilersView.swift b/Xcodes/Frontend/InfoPane/CompilersView.swift index b08458a..962937d 100644 --- a/Xcodes/Frontend/InfoPane/CompilersView.swift +++ b/Xcodes/Frontend/InfoPane/CompilersView.swift @@ -16,7 +16,9 @@ struct CompilersView: View { if let compilers = compilers { VStack(alignment: .leading) { Text("Compilers").font(.headline) - Text(Self.content(from: compilers)).font(.subheadline) + Text(Self.content(from: compilers)) + .font(.subheadline) + .textSelection(.enabled) } } else { EmptyView() diff --git a/Xcodes/Frontend/InfoPane/CornerRadiusModifier.swift b/Xcodes/Frontend/InfoPane/CornerRadiusModifier.swift index 4d9d7e2..2d7332f 100644 --- a/Xcodes/Frontend/InfoPane/CornerRadiusModifier.swift +++ b/Xcodes/Frontend/InfoPane/CornerRadiusModifier.swift @@ -29,7 +29,7 @@ extension View { struct Previews_CornerRadius_Previews: PreviewProvider { static var previews: some View { HStack { - Text("XCODES RULES!") + Text(verbatim: "XCODES RULES!") }.xcodesBackground() } } diff --git a/Xcodes/Frontend/InfoPane/IconView.swift b/Xcodes/Frontend/InfoPane/IconView.swift index c8bf32f..ba9aa4f 100644 --- a/Xcodes/Frontend/InfoPane/IconView.swift +++ b/Xcodes/Frontend/InfoPane/IconView.swift @@ -6,17 +6,18 @@ // Copyright © 2023 Robots and Pencils. All rights reserved. // -import SwiftUI import Path +import SwiftUI +import Version struct IconView: View { - let installState: XcodeInstallState + let xcode: Xcode var body: some View { - if case let .installed(path) = installState { + if case let .installed(path) = xcode.installState { Image(nsImage: NSWorkspace.shared.icon(forFile: path.string)) } else { - Image(systemName: "app.fill") + Image(xcode.version.isPrerelease ? "xcode-beta" : "xcode") .resizable() .frame(width: 32, height: 32) .foregroundColor(.secondary) @@ -25,13 +26,19 @@ struct IconView: View { } #Preview("Installed") { - IconView(installState: XcodeInstallState.installed(Path("/Applications/Xcode.app")!)) - .frame(width: 300, height: 100) - .padding() + IconView(xcode: Xcode(version: Version("12.3.0")!, installState: .installed(Path("/Applications/Xcode-12.3.0.app")!), selected: true, icon: nil)) + .frame(width: 300, height: 100) + .padding() +} + +#Preview("Installed") { + IconView(xcode: Xcode(version: Version("12.3.0")!, installState: .notInstalled, selected: true, icon: nil)) + .frame(width: 300, height: 100) + .padding() } #Preview("Not Installed") { - IconView(installState: XcodeInstallState.notInstalled) - .frame(width: 300, height: 100) - .padding() + IconView(xcode: Xcode(version: Version("12.0.0-1234A")!, installState: .notInstalled, selected: false, icon: nil)) + .frame(width: 300, height: 100) + .padding() } diff --git a/Xcodes/Frontend/InfoPane/IdenticalBuildView.swift b/Xcodes/Frontend/InfoPane/IdenticalBuildView.swift index 04ef3b8..7d2d11a 100644 --- a/Xcodes/Frontend/InfoPane/IdenticalBuildView.swift +++ b/Xcodes/Frontend/InfoPane/IdenticalBuildView.swift @@ -29,7 +29,7 @@ struct IdenticalBuildsView: View { .font(.headline) ForEach(builds, id: \.description) { version in - Text("• \(version.appleDescription)") + Text(verbatim: "• \(version.appleDescription)") .font(.subheadline) } } diff --git a/Xcodes/Frontend/InfoPane/InfoPane.swift b/Xcodes/Frontend/InfoPane/InfoPane.swift index 95826b7..f8753cf 100644 --- a/Xcodes/Frontend/InfoPane/InfoPane.swift +++ b/Xcodes/Frontend/InfoPane/InfoPane.swift @@ -9,30 +9,35 @@ import struct XCModel.SDKs struct InfoPane: View { let xcode: Xcode var body: some View { + if #available(macOS 14.0, *) { + mainContent + .contentMargins(10, for: .scrollContent) + } else { + mainContent + .padding() + } + } + + private var mainContent: some View { ScrollView(.vertical) { HStack(alignment: .top) { VStack { VStack(spacing: 5) { HStack { - IconView(installState: xcode.installState) + IconView(xcode: xcode) Text(verbatim: "Xcode \(xcode.description) \(xcode.version.buildMetadataIdentifiersDisplay)") .font(.title) .frame(maxWidth: .infinity, alignment: .leading) + .textSelection(.enabled) } InfoPaneControls(xcode: xcode) } .xcodesBackground() - - - VStack { - Text("Platforms") - .font(.title3) - .frame(maxWidth: .infinity, alignment: .leading) - PlatformsView(xcode: xcode) - } - .xcodesBackground() + + PlatformsView(xcode: xcode) } + .frame(minWidth: 380) VStack(alignment: .leading) { ReleaseDateView(date: xcode.releaseDate, url: xcode.releaseNotesURL) @@ -64,15 +69,16 @@ struct InfoPane: View { #Preview(XcodePreviewName.allCases[2].rawValue) { makePreviewContent(for: 2) } #Preview(XcodePreviewName.allCases[3].rawValue) { makePreviewContent(for: 3) } #Preview(XcodePreviewName.allCases[4].rawValue) { makePreviewContent(for: 4) } +#Preview(XcodePreviewName.allCases[5].rawValue) { makePreviewContent(for: 5) } private func makePreviewContent(for index: Int) -> some View { - let name = XcodePreviewName.allCases[index] + let name = XcodePreviewName.allCases[index] return InfoPane(xcode: xcodeDict[name]!) .environmentObject(configure(AppState()) { - $0.allXcodes = [xcodeDict[name]!] + $0.allXcodes = [xcodeDict[name]!] }) - .frame(width: 300, height: 400) - .padding() + .frame(width: 600, height: 400) + .padding() } enum XcodePreviewName: String, CaseIterable, Identifiable { @@ -81,7 +87,8 @@ enum XcodePreviewName: String, CaseIterable, Identifiable { case Populated_Uninstalled case Basic_Installed case Basic_Installing - + case Basic_Unarchiving + var id: XcodePreviewName { self } } @@ -141,17 +148,25 @@ var xcodeDict: [XcodePreviewName: Xcode] = [ sdks: nil, compilers: nil ), + .Basic_Unarchiving: .init( + version: _versionWithMeta, + installState: .installing(.unarchiving), + selected: false, + icon: nil, + sdks: nil, + compilers: nil + ), ] var downloadableRuntimes: [DownloadableRuntime] = { var runtimes = try! JSONDecoder().decode([DownloadableRuntime].self, from: Current.files.contents(atPath: Path.runtimeCacheFile.string)!) // set iOS to installed - let iOSIndex = runtimes.firstIndex { $0.sdkBuildUpdate == "19E239" }! + let iOSIndex = 0//runtimes.firstIndex { $0.sdkBuildUpdate.contains == "19E239" }! var iOSRuntime = runtimes[iOSIndex] iOSRuntime.installState = .installed runtimes[iOSIndex] = iOSRuntime - let watchOSIndex = runtimes.firstIndex { $0.sdkBuildUpdate == "20R362" }! + let watchOSIndex = 0//runtimes.firstIndex { $0.sdkBuildUpdate.first == "20R362" }! var runtime = runtimes[watchOSIndex] runtime.installState = .installing( RuntimeInstallationStep.downloading( @@ -163,7 +178,7 @@ var downloadableRuntimes: [DownloadableRuntime] = { $0.completedUnitCount = 848_444_920 $0.throughput = 9_211_681 } - ) + ) ) runtimes[watchOSIndex] = runtime diff --git a/Xcodes/Frontend/InfoPane/InfoPaneControls.swift b/Xcodes/Frontend/InfoPane/InfoPaneControls.swift index 73cdb35..4e6df41 100644 --- a/Xcodes/Frontend/InfoPane/InfoPaneControls.swift +++ b/Xcodes/Frontend/InfoPane/InfoPaneControls.swift @@ -25,6 +25,7 @@ struct InfoPaneControls: View { case .installing(let installationStep): HStack(alignment: .top) { InstallationStepDetailView(installationStep: installationStep) + .frame(maxWidth: .infinity, alignment: .leading) CancelInstallButton(xcode: xcode) } case .installed(_): @@ -39,6 +40,7 @@ struct InfoPaneControls: View { #Preview(XcodePreviewName.allCases[2].rawValue) { makePreviewContent(for: 2) } #Preview(XcodePreviewName.allCases[3].rawValue) { makePreviewContent(for: 3) } #Preview(XcodePreviewName.allCases[4].rawValue) { makePreviewContent(for: 4) } +#Preview(XcodePreviewName.allCases[5].rawValue) { makePreviewContent(for: 5) } private func makePreviewContent(for index: Int) -> some View { let name = XcodePreviewName.allCases[index] @@ -47,6 +49,6 @@ private func makePreviewContent(for index: Int) -> some View { .environmentObject(configure(AppState()) { $0.allXcodes = [xcodeDict[name]!] }) - .frame(width: 300) + .frame(width: 500) .padding() } diff --git a/Xcodes/Frontend/InfoPane/InstallationStepDetailView.swift b/Xcodes/Frontend/InfoPane/InstallationStepDetailView.swift index 75339ab..e4bb64f 100644 --- a/Xcodes/Frontend/InfoPane/InstallationStepDetailView.swift +++ b/Xcodes/Frontend/InfoPane/InstallationStepDetailView.swift @@ -17,7 +17,7 @@ struct InstallationStepDetailView: View { showsAdditionalDescription: true ) - case .unarchiving, .moving, .trashingArchive, .checkingSecurity, .finishing: + case .authenticating, .unarchiving, .moving, .trashingArchive, .checkingSecurity, .finishing: ProgressView() .scaleEffect(0.5) } diff --git a/Xcodes/Frontend/InfoPane/PlatformsView.swift b/Xcodes/Frontend/InfoPane/PlatformsView.swift index 93b2d3b..6e0b9a1 100644 --- a/Xcodes/Frontend/InfoPane/PlatformsView.swift +++ b/Xcodes/Frontend/InfoPane/PlatformsView.swift @@ -11,7 +11,8 @@ import XcodesKit struct PlatformsView: View { @EnvironmentObject var appState: AppState - + @AppStorage("selectedRuntimeArchitecture") private var selectedRuntimeArchitecture: RuntimeArchitecture = .arm64 + let xcode: Xcode var body: some View { @@ -19,17 +20,50 @@ struct PlatformsView: View { let builds = xcode.sdks?.allBuilds() let runtimes = builds?.flatMap { sdkBuild in appState.downloadableRuntimes.filter { - $0.sdkBuildUpdate == sdkBuild + $0.sdkBuildUpdate?.contains(sdkBuild) ?? false && + ($0.architectures?.isEmpty ?? true || + $0.architectures?.contains(selectedRuntimeArchitecture.rawValue) ?? false) } } - - ForEach(runtimes ?? [], id: \.simulatorVersion.buildUpdate) { runtime in - runtimeView(runtime: runtime) - .frame(minWidth: 200) - .padding() - .background(.quinary) - .clipShape(RoundedRectangle(cornerRadius: 5, style: .continuous)) + + let architectures = Set((runtimes ?? []).flatMap { $0.architectures ?? [] }) + + VStack { + HStack { + Text("Platforms") + .font(.title3) + .frame(maxWidth: .infinity, alignment: .leading) + if !architectures.isEmpty { + Spacer() + Button { + switch selectedRuntimeArchitecture { + case .arm64: selectedRuntimeArchitecture = .x86_64 + case .x86_64: selectedRuntimeArchitecture = .arm64 + } + } label: { + switch selectedRuntimeArchitecture { + case .arm64: + Label(selectedRuntimeArchitecture.displayValue, systemImage: "m4.button.horizontal") + .labelStyle(.trailingIcon) + case .x86_64: + Label(selectedRuntimeArchitecture.displayValue, systemImage: "cpu.fill") + .labelStyle(.trailingIcon) + } + } + } + } + + ForEach(runtimes ?? [], id: \.identifier) { runtime in + runtimeView(runtime: runtime) + .frame(minWidth: 200) + .padding() + .background(.quinary) + .clipShape(RoundedRectangle(cornerRadius: 5, style: .continuous)) + } } + .xcodesBackground() + + } @ViewBuilder @@ -39,34 +73,37 @@ struct PlatformsView: View { runtime.icon() Text("\(runtime.visibleIdentifier)") .font(.headline) + ForEach(runtime.architectures ?? [], id: \.self) { architecture in + TagView(text: architecture) + } pathIfAvailable(xcode: xcode, runtime: runtime) + + if runtime.installState == .notInstalled { + // TODO: Update the downloadableRuntimes with the appropriate installState so we don't have to check path awkwardly + if appState.runtimeInstallPath(xcode: xcode, runtime: runtime) != nil { + EmptyView() + } else { + HStack { + Spacer() + DownloadRuntimeButton(runtime: runtime) + } + } + } + Spacer() Text(runtime.downloadFileSizeString) .font(.subheadline) + .frame(width: 70, alignment: .trailing) } - switch runtime.installState { - case .installed: - EmptyView() - case .notInstalled: - // TODO: Update the downloadableRuntimes with the appropriate installState so we don't have to check path awkwardly - if let path = appState.runtimeInstallPath(xcode: xcode, runtime: runtime) { - EmptyView() - } else { - HStack { - Spacer() - DownloadRuntimeButton(runtime: runtime) - } - } - - case .installing(let installationStep): - HStack(alignment: .top, spacing: 5){ - RuntimeInstallationStepDetailView(installationStep: installationStep) - .fixedSize(horizontal: false, vertical: true) - Spacer() - CancelRuntimeInstallButton(runtime: runtime) - } - - } + + if case let .installing(installationStep) = runtime.installState { + HStack(alignment: .top, spacing: 5){ + RuntimeInstallationStepDetailView(installationStep: installationStep) + .fixedSize(horizontal: false, vertical: true) + Spacer() + CancelRuntimeInstallButton(runtime: runtime) + } + } } } diff --git a/Xcodes/Frontend/InfoPane/RuntimeArchitecture.swift b/Xcodes/Frontend/InfoPane/RuntimeArchitecture.swift new file mode 100644 index 0000000..abbdf45 --- /dev/null +++ b/Xcodes/Frontend/InfoPane/RuntimeArchitecture.swift @@ -0,0 +1,17 @@ +// +// RuntimeArchitecture.swift +// Xcodes +// +// Created by Matt Kiazyk on 2025-07-07. +// + +enum RuntimeArchitecture: String, CaseIterable, Identifiable { + case arm64 + case x86_64 + + var id: Self { self } + + var displayValue: String { + return rawValue + } +} diff --git a/Xcodes/Frontend/InfoPane/RuntimeInstallationStepDetailView.swift b/Xcodes/Frontend/InfoPane/RuntimeInstallationStepDetailView.swift index f59f041..251c6bd 100644 --- a/Xcodes/Frontend/InfoPane/RuntimeInstallationStepDetailView.swift +++ b/Xcodes/Frontend/InfoPane/RuntimeInstallationStepDetailView.swift @@ -26,8 +26,12 @@ struct RuntimeInstallationStepDetailView: View { ) case .installing, .trashingArchive: - ProgressView() - .scaleEffect(0.5) + ObservingProgressIndicator( + Progress(), + controlSize: .regular, + style: .bar, + showsAdditionalDescription: false + ) } } } diff --git a/Xcodes/Frontend/InfoPane/SDKsView.swift b/Xcodes/Frontend/InfoPane/SDKsView.swift index 0d3552e..6fa971a 100644 --- a/Xcodes/Frontend/InfoPane/SDKsView.swift +++ b/Xcodes/Frontend/InfoPane/SDKsView.swift @@ -18,7 +18,9 @@ struct SDKsView: View { } else { VStack(alignment: .leading) { Text("SDKs").font(.headline) - Text(content).font(.subheadline) + Text(content) + .font(.subheadline) + .textSelection(.enabled) } } } diff --git a/Xcodes/Frontend/MainWindow.swift b/Xcodes/Frontend/MainWindow.swift index 6bbe1ee..698ba96 100644 --- a/Xcodes/Frontend/MainWindow.swift +++ b/Xcodes/Frontend/MainWindow.swift @@ -16,11 +16,10 @@ struct MainWindow: View { @AppStorage("isShowingInfoPane") private var isShowingInfoPane = false @AppStorage("xcodeListCategory") private var category: XcodeListCategory = .all @AppStorage("isInstalledOnly") private var isInstalledOnly = false - + var body: some View { NavigationSplitViewWrapper { XcodeListView(selectedXcodeID: $selectedXcodeID, searchText: searchText, category: category, isInstalledOnly: isInstalledOnly) - .frame(minWidth: 250) .layoutPriority(1) .alert(item: $appState.xcodeBeingConfirmedForUninstallation) { xcode in Alert(title: Text(String(format: localizeString("Alert.Uninstall.Title"), xcode.description)), @@ -42,7 +41,6 @@ struct MainWindow: View { UnselectedView() } } - .padding() .toolbar { ToolbarItemGroup { Button(action: { appState.presentedSheet = .signIn }, label: { @@ -56,11 +54,7 @@ struct MainWindow: View { .help("PreferencesDescription") } else { Button(action: { - if #available(macOS 13, *) { - NSApp.sendAction(Selector(("showSettingsWindow:")), to: nil, from: nil) - } else { - NSApp.sendAction(Selector(("showPreferencesWindow:")), to: nil, from: nil) - } + NSApp.sendAction(Selector(("showSettingsWindow:")), to: nil, from: nil) }, label: { Label("Preferences", systemImage: "gearshape") }) @@ -82,6 +76,9 @@ struct MainWindow: View { case .twoFactor(let secondFactorData): secondFactorView(secondFactorData) .environmentObject(appState) + case .securityKeyTouchToConfirm: + SignInSecurityKeyTouchView(isPresented: $appState.presentedSheet.isNotNil) + .environmentObject(appState) } } .alert(item: $appState.presentedAlert, content: { presentedAlert in @@ -113,6 +110,8 @@ struct MainWindow: View { SignInSMSView(isPresented: $appState.presentedSheet.isNotNil, trustedPhoneNumber: trustedPhoneNumber, authOptions: secondFactorData.authOptions, sessionData: secondFactorData.sessionData) case .smsPendingChoice: SignInPhoneListView(isPresented: $appState.presentedSheet.isNotNil, authOptions: secondFactorData.authOptions, sessionData: secondFactorData.sessionData) + case .securityKey: + SignInSecurityKeyPinView(isPresented: $appState.presentedSheet.isNotNil, authOptions: secondFactorData.authOptions, sessionData: secondFactorData.sessionData) } } diff --git a/Xcodes/Frontend/Preferences/AdvancedPreferencePane.swift b/Xcodes/Frontend/Preferences/AdvancedPreferencePane.swift index 21d6b8c..13ab718 100644 --- a/Xcodes/Frontend/Preferences/AdvancedPreferencePane.swift +++ b/Xcodes/Frontend/Preferences/AdvancedPreferencePane.swift @@ -36,8 +36,10 @@ struct AdvancedPreferencePane: View { self.appState.installPath = path.string } } + .disabled(appState.disableInstallPathChange) Text("InstallPathDescription") .font(.footnote) + .foregroundStyle(.secondary) .fixedSize(horizontal: false, vertical: true) } } @@ -71,8 +73,10 @@ struct AdvancedPreferencePane: View { self.appState.localPath = path.string } } + .disabled(appState.disableLocalPathChange) Text("LocalCachePathDescription") .font(.footnote) + .foregroundStyle(.secondary) .fixedSize(horizontal: false, vertical: true) } } @@ -80,18 +84,22 @@ struct AdvancedPreferencePane: View { GroupBox(label: Text("Active/Select")) { VStack(alignment: .leading) { - Picker("OnSelect", selection: $appState.onSelectActionType) { + Picker(selection: $appState.onSelectActionType) { Text(SelectedActionType.none.description) .tag(SelectedActionType.none) Text(SelectedActionType.rename.description) .tag(SelectedActionType.rename) + } label: { + Text(verbatim: "OnSelect") } .labelsHidden() .pickerStyle(.inline) - + .disabled(appState.onSelectActionTypeDisabled) + Text(appState.onSelectActionType.detailedDescription) .font(.footnote) + .foregroundStyle(.secondary) .fixedSize(horizontal: false, vertical: true) Spacer() .frame(height: 20) @@ -100,6 +108,7 @@ struct AdvancedPreferencePane: View { .disabled(appState.createSymLinkOnSelectDisabled) Text("AutomaticallyCreateSymbolicLinkDescription") .font(.footnote) + .foregroundStyle(.secondary) .fixedSize(horizontal: false, vertical: true) } .fixedSize(horizontal: false, vertical: true) @@ -112,6 +121,7 @@ struct AdvancedPreferencePane: View { .disabled(appState.createSymLinkOnSelectDisabled) Text("ShowOpenInRosettaDescription") .font(.footnote) + .foregroundStyle(.secondary) .fixedSize(horizontal: false, vertical: true) } .groupBoxStyle(PreferencesGroupBoxStyle()) @@ -126,16 +136,18 @@ struct AdvancedPreferencePane: View { case .installed: Text("HelperInstalled") case .notInstalled: - HStack { - Text("HelperNotInstalled") + VStack(alignment: .leading) { Button("InstallHelper") { appState.installHelperIfNecessary() } + Text("HelperNotInstalled") + .font(.footnote) } } Text("PrivilegedHelperDescription") .font(.footnote) + .foregroundStyle(.secondary) .fixedSize(horizontal: false, vertical: true) Spacer() @@ -151,9 +163,9 @@ struct AdvancedPreferencePane_Previews: PreviewProvider { Group { AdvancedPreferencePane() .environmentObject(AppState()) - .frame(maxWidth: 500) + .frame(maxWidth: 600) } - .frame(width: 500, height: 700, alignment: .center) + .frame(width: 600, height: 700, alignment: .center) } } @@ -161,11 +173,8 @@ struct AdvancedPreferencePane_Previews: PreviewProvider { struct PreferencesGroupBoxStyle: GroupBoxStyle { func makeBody(configuration: Configuration) -> some View { HStack(alignment: .top, spacing: 20) { - HStack { - Spacer() - configuration.label - } - .frame(width: 120) + configuration.label + .frame(width: 180, alignment: .trailing) VStack(alignment: .leading) { configuration.content diff --git a/Xcodes/Frontend/Preferences/DownloadPreferencePane.swift b/Xcodes/Frontend/Preferences/DownloadPreferencePane.swift index e453dc2..854c615 100644 --- a/Xcodes/Frontend/Preferences/DownloadPreferencePane.swift +++ b/Xcodes/Frontend/Preferences/DownloadPreferencePane.swift @@ -18,13 +18,17 @@ struct DownloadPreferencePane: View { } } .labelsHidden() - - AttributedText(dataSourceFootnote) + .fixedSize() + + Text("DataSourceDescription") + .font(.footnote) + .foregroundStyle(.secondary) + .fixedSize(horizontal: false, vertical: true) } - } .groupBoxStyle(PreferencesGroupBoxStyle()) - + .disabled(dataSource.isManaged) + GroupBox(label: Text("Downloader")) { VStack(alignment: .leading) { Picker("Downloader", selection: $downloader) { @@ -34,49 +38,27 @@ struct DownloadPreferencePane: View { } } .labelsHidden() - - AttributedText(downloaderFootnote) + .fixedSize() + + Text("DownloaderDescription") + .font(.footnote) + .foregroundStyle(.secondary) + .fixedSize(horizontal: false, vertical: true) } - } .groupBoxStyle(PreferencesGroupBoxStyle()) - + .disabled(downloader.isManaged) } } - - private var dataSourceFootnote: NSAttributedString { - let string = localizeString("DataSourceDescription") - let attributedString = NSMutableAttributedString( - string: string, - attributes: [ - .font: NSFont.preferredFont(forTextStyle: .footnote, options: [:]), - .foregroundColor: NSColor.labelColor - ] - ) - attributedString.addAttribute(.link, value: URL(string: "https://xcodereleases.com")!, range: NSRange(string.range(of: "Xcode Releases")!, in: string)) - return attributedString - } - - private var downloaderFootnote: NSAttributedString { - let string = localizeString("DownloaderDescription") - let attributedString = NSMutableAttributedString( - string: string, - attributes: [ - .font: NSFont.preferredFont(forTextStyle: .footnote, options: [:]), - .foregroundColor: NSColor.labelColor - ] - ) - attributedString.addAttribute(.link, value: URL(string: "https://github.com/aria2/aria2")!, range: NSRange(string.range(of: "aria2")!, in: string)) - return attributedString - } } struct DownloadPreferencePane_Previews: PreviewProvider { static var previews: some View { Group { - GeneralPreferencePane() + DownloadPreferencePane() .environmentObject(AppState()) - .frame(maxWidth: 500) + .frame(maxWidth: 600) + .frame(minHeight: 300) } } } diff --git a/Xcodes/Frontend/Preferences/ExperiementsPreferencePane.swift b/Xcodes/Frontend/Preferences/ExperiementsPreferencePane.swift index 3696552..68dad51 100644 --- a/Xcodes/Frontend/Preferences/ExperiementsPreferencePane.swift +++ b/Xcodes/Frontend/Preferences/ExperiementsPreferencePane.swift @@ -1,6 +1,6 @@ import AppleAPI -import SwiftUI import Path +import SwiftUI struct ExperimentsPreferencePane: View { @EnvironmentObject var appState: AppState @@ -13,29 +13,17 @@ struct ExperimentsPreferencePane: View { "UseUnxipExperiment", isOn: $appState.unxipExperiment ) - AttributedText(unxipFootnote) + .disabled(appState.disableUnxipExperiment) + Text("FasterUnxipDescription") + .font(.footnote) + .foregroundStyle(.secondary) + .fixedSize(horizontal: false, vertical: true) } .fixedSize(horizontal: false, vertical: true) } .groupBoxStyle(PreferencesGroupBoxStyle()) - - Divider() } } - - private var unxipFootnote: NSAttributedString { - let string = localizeString("FasterUnxipDescription") - let attributedString = NSMutableAttributedString( - string: string, - attributes: [ - .font: NSFont.preferredFont(forTextStyle: .footnote, options: [:]), - .foregroundColor: NSColor.labelColor - ] - ) - attributedString.addAttribute(.link, value: URL(string: "https://twitter.com/_saagarjha")!, range: NSRange(string.range(of: "@_saagarjha")!, in: string)) - attributedString.addAttribute(.link, value: URL(string: "https://github.com/saagarjha/unxip")!, range: NSRange(string.range(of: "https://github.com/saagarjha/unxip")!, in: string)) - return attributedString - } } struct ExperimentsPreferencePane_Previews: PreviewProvider { @@ -43,7 +31,7 @@ struct ExperimentsPreferencePane_Previews: PreviewProvider { Group { ExperimentsPreferencePane() .environmentObject(AppState()) - .frame(maxWidth: 500) + .frame(maxWidth: 600) } } } diff --git a/Xcodes/Frontend/Preferences/GeneralPreferencePane.swift b/Xcodes/Frontend/Preferences/GeneralPreferencePane.swift index 4735c24..b15f5c6 100644 --- a/Xcodes/Frontend/Preferences/GeneralPreferencePane.swift +++ b/Xcodes/Frontend/Preferences/GeneralPreferencePane.swift @@ -20,6 +20,12 @@ struct GeneralPreferencePane: View { NotificationsView().environmentObject(appState) } .groupBoxStyle(PreferencesGroupBoxStyle()) + Divider() + + GroupBox(label: Text("Misc")) { + Toggle("TerminateAfterLastWindowClosed", isOn: $appState.terminateAfterLastWindowClosed) + } + .groupBoxStyle(PreferencesGroupBoxStyle()) } } } @@ -29,7 +35,7 @@ struct GeneralPreferencePane_Previews: PreviewProvider { Group { GeneralPreferencePane() .environmentObject(AppState()) - .frame(maxWidth: 500) + .frame(maxWidth: 600) } } } diff --git a/Xcodes/Frontend/Preferences/PreferencesView.swift b/Xcodes/Frontend/Preferences/PreferencesView.swift index 83ab775..39509cf 100644 --- a/Xcodes/Frontend/Preferences/PreferencesView.swift +++ b/Xcodes/Frontend/Preferences/PreferencesView.swift @@ -39,6 +39,6 @@ struct PreferencesView: View { .tag(Tabs.experiment) } .padding(20) - .frame(width: 500) + .frame(width: 600) } } diff --git a/Xcodes/Frontend/Preferences/UpdatesPreferencePane.swift b/Xcodes/Frontend/Preferences/UpdatesPreferencePane.swift index 624a306..edad69b 100644 --- a/Xcodes/Frontend/Preferences/UpdatesPreferencePane.swift +++ b/Xcodes/Frontend/Preferences/UpdatesPreferencePane.swift @@ -15,11 +15,13 @@ struct UpdatesPreferencePane: View { "AutomaticInstallNewVersion", isOn: $autoInstallationType.isAutoInstalling ) - + .disabled(updater.disableAutoInstallNewVersions) + Toggle( "IncludePreRelease", isOn: $autoInstallationType.isAutoInstallingBeta ) + .disabled(updater.disableIncludePrereleaseVersions) } .fixedSize(horizontal: false, vertical: true) } @@ -34,18 +36,23 @@ struct UpdatesPreferencePane: View { isOn: $updater.automaticallyChecksForUpdates ) .fixedSize(horizontal: true, vertical: false) - + .disabled(updater.disableAutoUpdateXcodesApp) + Toggle( "IncludePreRelease", isOn: $updater.includePrereleaseVersions ) - + .disabled(updater.disableAutoUpdateXcodesAppPrereleaseVersions) + Button("CheckNow") { updater.checkForUpdates() } - + .padding(.top) + .disabled(updater.disableAutoUpdateXcodesApp) + Text(String(format: localizeString("LastChecked"), lastUpdatedString)) .font(.footnote) + .foregroundStyle(.secondary) } .frame(maxWidth: .infinity, alignment: .leading) } @@ -81,12 +88,18 @@ class ObservableUpdater: ObservableObject { private var lastUpdateCheckDateObservation: NSKeyValueObservation? @Published var includePrereleaseVersions = false { didSet { - UserDefaults.standard.setValue(includePrereleaseVersions, forKey: "includePrereleaseVersions") - + Current.defaults.set(includePrereleaseVersions, forKey: "includePrereleaseVersions") + updaterDelegate.includePrereleaseVersions = includePrereleaseVersions } } - + + var disableAutoInstallNewVersions: Bool { PreferenceKey.autoInstallation.isManaged() } + var disableIncludePrereleaseVersions: Bool { PreferenceKey.autoInstallation.isManaged() } + + var disableAutoUpdateXcodesApp: Bool { PreferenceKey.SUEnableAutomaticChecks.isManaged() } + var disableAutoUpdateXcodesAppPrereleaseVersions: Bool { PreferenceKey.includePrereleaseVersions.isManaged() } + init() { updater = SPUStandardUpdaterController(startingUpdater: true, updaterDelegate: updaterDelegate, userDriverDelegate: nil).updater @@ -109,7 +122,7 @@ class ObservableUpdater: ObservableObject { self.lastUpdateCheckDate = updater.lastUpdateCheckDate } ) - includePrereleaseVersions = UserDefaults.standard.bool(forKey: "includePrereleaseVersions") + includePrereleaseVersions = Current.defaults.bool(forKey: "includePrereleaseVersions") ?? false } func checkForUpdates() { @@ -140,7 +153,9 @@ struct UpdatesPreferencePane_Previews: PreviewProvider { Group { UpdatesPreferencePane() .environmentObject(AppState()) - .frame(maxWidth: 500) + .environmentObject(ObservableUpdater()) + .frame(maxWidth: 600) + .frame(minHeight: 300) } } } diff --git a/Xcodes/Frontend/SignIn/SignIn2FAView.swift b/Xcodes/Frontend/SignIn/SignIn2FAView.swift index 1095033..a8dfa98 100644 --- a/Xcodes/Frontend/SignIn/SignIn2FAView.swift +++ b/Xcodes/Frontend/SignIn/SignIn2FAView.swift @@ -10,12 +10,12 @@ struct SignIn2FAView: View { var body: some View { VStack(alignment: .leading) { - Text(String(format: localizeString("DigitCodeDescription"), authOptions.securityCode.length)) + Text(String(format: localizeString("DigitCodeDescription"), authOptions.securityCode!.length)) .fixedSize(horizontal: true, vertical: false) HStack { Spacer() - PinCodeTextField(code: $code, numberOfDigits: authOptions.securityCode.length) { + PinCodeTextField(code: $code, numberOfDigits: authOptions.securityCode!.length) { appState.submitSecurityCode(.device(code: $0), sessionData: sessionData) } Spacer() @@ -32,7 +32,7 @@ struct SignIn2FAView: View { Text("Continue") } .keyboardShortcut(.defaultAction) - .disabled(code.count != authOptions.securityCode.length) + .disabled(code.count != authOptions.securityCode!.length) } .frame(height: 25) } diff --git a/Xcodes/Frontend/SignIn/SignInCredentialsView.swift b/Xcodes/Frontend/SignIn/SignInCredentialsView.swift index 6d07065..b9f527d 100644 --- a/Xcodes/Frontend/SignIn/SignInCredentialsView.swift +++ b/Xcodes/Frontend/SignIn/SignInCredentialsView.swift @@ -1,9 +1,14 @@ import SwiftUI struct SignInCredentialsView: View { + private enum FocusedField { + case username, password + } + @EnvironmentObject var appState: AppState @State private var username: String = "" @State private var password: String = "" + @FocusState private var focusedField: FocusedField? var body: some View { VStack(alignment: .leading) { @@ -13,12 +18,16 @@ struct SignInCredentialsView: View { HStack { Text("AppleID") .frame(minWidth: 100, alignment: .trailing) - TextField("example@icloud.com", text: $username) + TextField(text: $username) { + Text(verbatim: "example@icloud.com") + } + .focused($focusedField, equals: .username) } HStack { Text("Password") .frame(minWidth: 100, alignment: .trailing) SecureField("Required", text: $password) + .focused($focusedField, equals: .password) } if appState.authError != nil { HStack { diff --git a/Xcodes/Frontend/SignIn/SignInPhoneListView.swift b/Xcodes/Frontend/SignIn/SignInPhoneListView.swift index a4341c1..fc31d97 100644 --- a/Xcodes/Frontend/SignIn/SignInPhoneListView.swift +++ b/Xcodes/Frontend/SignIn/SignInPhoneListView.swift @@ -1,5 +1,5 @@ -import SwiftUI import AppleAPI +import SwiftUI struct SignInPhoneListView: View { @EnvironmentObject var appState: AppState @@ -7,12 +7,12 @@ struct SignInPhoneListView: View { @State private var selectedPhoneNumberID: AuthOptionsResponse.TrustedPhoneNumber.ID? let authOptions: AuthOptionsResponse let sessionData: AppleSessionData - + var body: some View { VStack(alignment: .leading) { if let phoneNumbers = authOptions.trustedPhoneNumbers, !phoneNumbers.isEmpty { - Text(String(format: localizeString("SelectTrustedPhone"), authOptions.securityCode.length)) - + Text(String(format: localizeString("SelectTrustedPhone"), authOptions.securityCode!.length)) + List(phoneNumbers, selection: $selectedPhoneNumberID) { Text($0.numberWithDialCode) } @@ -22,19 +22,18 @@ struct SignInPhoneListView: View { } } } else { - AttributedText( - NSAttributedString(string: localizeString("NoTrustedPhones")) - .convertingURLsToLinkAttributes() - ) + Text("NoTrustedPhones") + .font(.callout) Spacer() } - + HStack { Button("Cancel", action: { isPresented = false }) .keyboardShortcut(.cancelAction) Spacer() ProgressButton(isInProgress: appState.isProcessingAuthRequest, - action: { appState.requestSMS(to: authOptions.trustedPhoneNumbers!.first { $0.id == selectedPhoneNumberID }!, authOptions: authOptions, sessionData: sessionData) }) { + action: { appState.requestSMS(to: authOptions.trustedPhoneNumbers!.first { $0.id == selectedPhoneNumberID }!, authOptions: authOptions, sessionData: sessionData) }) + { Text("Continue") } .keyboardShortcut(.defaultAction) @@ -54,9 +53,10 @@ struct SignInPhoneListView_Previews: PreviewProvider { SignInPhoneListView( isPresented: .constant(true), authOptions: AuthOptionsResponse( - trustedPhoneNumbers: [.init(id: 0, numberWithDialCode: "(•••) •••-••90")], + trustedPhoneNumbers: [.init(id: 0, numberWithDialCode: "(•••) •••-••90")], trustedDevices: nil, - securityCode: .init(length: 6)), + securityCode: .init(length: 6) + ), sessionData: AppleSessionData(serviceKey: "", sessionID: "", scnt: "") ) .environmentObject(AppState()) @@ -64,9 +64,10 @@ struct SignInPhoneListView_Previews: PreviewProvider { SignInPhoneListView( isPresented: .constant(true), authOptions: AuthOptionsResponse( - trustedPhoneNumbers: [], + trustedPhoneNumbers: [], trustedDevices: nil, - securityCode: .init(length: 6)), + securityCode: .init(length: 6) + ), sessionData: AppleSessionData(serviceKey: "", sessionID: "", scnt: "") ) .environmentObject(AppState()) diff --git a/Xcodes/Frontend/SignIn/SignInSMSView.swift b/Xcodes/Frontend/SignIn/SignInSMSView.swift index c8a04db..6d42f69 100644 --- a/Xcodes/Frontend/SignIn/SignInSMSView.swift +++ b/Xcodes/Frontend/SignIn/SignInSMSView.swift @@ -11,11 +11,11 @@ struct SignInSMSView: View { var body: some View { VStack(alignment: .leading) { - Text(String(format: localizeString("EnterDigitCodeDescription"), authOptions.securityCode.length, trustedPhoneNumber.numberWithDialCode)) + Text(String(format: localizeString("EnterDigitCodeDescription"), authOptions.securityCode!.length, trustedPhoneNumber.numberWithDialCode)) HStack { Spacer() - PinCodeTextField(code: $code, numberOfDigits: authOptions.securityCode.length) { + PinCodeTextField(code: $code, numberOfDigits: authOptions.securityCode!.length) { appState.submitSecurityCode(.sms(code: $0, phoneNumberId: trustedPhoneNumber.id), sessionData: sessionData) } Spacer() @@ -31,7 +31,7 @@ struct SignInSMSView: View { Text("Continue") } .keyboardShortcut(.defaultAction) - .disabled(code.count != authOptions.securityCode.length) + .disabled(code.count != authOptions.securityCode!.length) } .frame(height: 25) } diff --git a/Xcodes/Frontend/SignIn/SignInSecurityKeyPinView.swift b/Xcodes/Frontend/SignIn/SignInSecurityKeyPinView.swift new file mode 100644 index 0000000..d2b1646 --- /dev/null +++ b/Xcodes/Frontend/SignIn/SignInSecurityKeyPinView.swift @@ -0,0 +1,70 @@ +// +// SignInSecurityKeyPin.swift +// Xcodes +// +// Created by Kino on 2024-09-26. +// Copyright © 2024 Robots and Pencils. All rights reserved. +// + +import SwiftUI +import AppleAPI + +struct SignInSecurityKeyPinView: View { + @EnvironmentObject var appState: AppState + @Binding var isPresented: Bool + @State private var pin: String = "" + let authOptions: AuthOptionsResponse + let sessionData: AppleSessionData + + var body: some View { + VStack(alignment: .leading) { + Text(localizeString("SecurityKeyPinDescription")) + .fixedSize(horizontal: true, vertical: false) + + HStack { + Spacer() + SecureField("PIN", text: $pin) + Spacer() + } + .padding() + + HStack { + Button("Cancel", action: { isPresented = false }) + .keyboardShortcut(.cancelAction) + Spacer() + + Button("PIN not set", action: submitWithoutPinCode) + + ProgressButton(isInProgress: appState.isProcessingAuthRequest, + action: submitPinCode) { + Text("Continue") + } + .keyboardShortcut(.defaultAction) + // FIDO2 device pin codes must be at least 4 code points + // https://docs.yubico.com/yesdk/users-manual/application-fido2/fido2-pin.html + .disabled(pin.count < 4) + } + .frame(height: 25) + } + .padding() + .emittingError($appState.authError, recoveryHandler: { _ in }) + } + + func submitPinCode() { + appState.createAndSubmitSecurityKeyAssertationWithPinCode(pin, sessionData: sessionData, authOptions: authOptions) + } + + func submitWithoutPinCode() { + appState.createAndSubmitSecurityKeyAssertationWithPinCode(nil, sessionData: sessionData, authOptions: authOptions) + } +} + +#Preview { + SignInSecurityKeyPinView(isPresented: .constant(true), + authOptions: AuthOptionsResponse( + trustedPhoneNumbers: nil, + trustedDevices: nil, + securityCode: .init(length: 6) + ), sessionData: AppleSessionData(serviceKey: "", sessionID: "", scnt: "")) + .environmentObject(AppState()) +} diff --git a/Xcodes/Frontend/SignIn/SignInSecurityKeyTouchView.swift b/Xcodes/Frontend/SignIn/SignInSecurityKeyTouchView.swift new file mode 100644 index 0000000..362a54f --- /dev/null +++ b/Xcodes/Frontend/SignIn/SignInSecurityKeyTouchView.swift @@ -0,0 +1,54 @@ +// +// SignInSecurityKeyPin.swift +// Xcodes +// +// Created by Kino on 2024-09-26. +// Copyright © 2024 Robots and Pencils. All rights reserved. +// + +import SwiftUI +import AppleAPI + +struct SignInSecurityKeyTouchView: View { + @EnvironmentObject var appState: AppState + @Binding var isPresented: Bool + + var body: some View { + VStack(alignment: .center) { + Image(systemName: "key.radiowaves.forward") + .font(.system(size: 32)).bold() + .padding(.bottom) + HStack { + Spacer() + Text(localizeString("SecurityKeyTouchDescription")) + .fixedSize(horizontal: true, vertical: false) + Spacer() + } + HStack { + Button("Cancel", action: self.cancel) + .keyboardShortcut(.cancelAction) + Spacer() + + ProgressView() + .progressViewStyle(CircularProgressViewStyle()) + .scaleEffect(x: 0.5, y: 0.5, anchor: .center) + .isHidden(!appState.isProcessingAuthRequest) + + .keyboardShortcut(.defaultAction) + } + .frame(height: 25) + } + .padding() + .emittingError($appState.authError, recoveryHandler: { _ in }) + } + + func cancel() { + appState.cancelSecurityKeyAssertationRequest() + isPresented = false + } +} + +#Preview { + SignInSecurityKeyTouchView(isPresented: .constant(true)) + .environmentObject(AppState()) +} diff --git a/Xcodes/Frontend/View+IsHidden.swift b/Xcodes/Frontend/View+IsHidden.swift index 4da6da0..887605f 100644 --- a/Xcodes/Frontend/View+IsHidden.swift +++ b/Xcodes/Frontend/View+IsHidden.swift @@ -14,10 +14,10 @@ extension View { struct View_IsHidden_Previews: PreviewProvider { static var previews: some View { Group { - Text("Not Hidden") + Text(verbatim: "Not Hidden") .isHidden(false) - Text("Hidden") + Text(verbatim: "Hidden") .isHidden(true) } } diff --git a/Xcodes/Frontend/XcodeList/AppStoreButtonStyle.swift b/Xcodes/Frontend/XcodeList/AppStoreButtonStyle.swift index 3b7b7df..4bad83d 100644 --- a/Xcodes/Frontend/XcodeList/AppStoreButtonStyle.swift +++ b/Xcodes/Frontend/XcodeList/AppStoreButtonStyle.swift @@ -97,33 +97,45 @@ struct AppStoreButtonStyle_Previews: PreviewProvider { Group { ForEach([ColorScheme.light, .dark], id: \.self) { colorScheme in Group { - Button("OPEN", action: {}) + Button{ } label: { + Text(verbatim: "OPEN") + } .buttonStyle(AppStoreButtonStyle(primary: true, highlighted: false)) .padding() .background(Color(.textBackgroundColor)) .previewDisplayName("Primary") - Button("OPEN", action: {}) + Button{ } label: { + Text(verbatim: "OPEN") + } .buttonStyle(AppStoreButtonStyle(primary: true, highlighted: true)) .padding() .background(Color(.controlAccentColor)) .previewDisplayName("Primary, Highlighted") - Button("OPEN", action: {}) + Button{ } label: { + Text(verbatim: "OPEN") + } .buttonStyle(AppStoreButtonStyle(primary: true, highlighted: false)) .padding() .disabled(true) .background(Color(.textBackgroundColor)) .previewDisplayName("Primary, Disabled") - Button("INSTALL", action: {}) + Button{ } label: { + Text(verbatim: "INSTALL") + } .buttonStyle(AppStoreButtonStyle(primary: false, highlighted: false)) .padding() .background(Color(.textBackgroundColor)) .previewDisplayName("Secondary") - Button("INSTALL", action: {}) + Button{ } label: { + Text(verbatim: "INSTALL") + } .buttonStyle(AppStoreButtonStyle(primary: false, highlighted: true)) .padding() .background(Color(.controlAccentColor)) .previewDisplayName("Secondary, Highlighted") - Button("INSTALL", action: {}) + Button{ } label: { + Text(verbatim: "INSTALL") + } .buttonStyle(AppStoreButtonStyle(primary: false, highlighted: false)) .padding() .disabled(true) diff --git a/Xcodes/Frontend/XcodeList/BottomStatusBar.swift b/Xcodes/Frontend/XcodeList/BottomStatusBar.swift index 18eaf2d..3f6e2e6 100644 --- a/Xcodes/Frontend/XcodeList/BottomStatusBar.swift +++ b/Xcodes/Frontend/XcodeList/BottomStatusBar.swift @@ -11,6 +11,8 @@ import SwiftUI struct BottomStatusModifier: ViewModifier { @EnvironmentObject var appState: AppState + @AppStorage(PreferenceKey.hideSupportXcodes.rawValue) var hideSupportXcodes = false + @SwiftUI.Environment(\.openURL) var openURL: OpenURLAction func body(content: Content) -> some View { @@ -20,17 +22,19 @@ struct BottomStatusModifier: ViewModifier { Divider() HStack { Text(appState.bottomStatusBarMessage) - .font(.subheadline) + .font(.subheadline) Spacer() - Button(action: { - openURL(URL(string: "https://opencollective.com/xcodesapp")!) - }) { - HStack { - Image(systemName: "heart.circle") - Text("Support.Xcodes") + if !hideSupportXcodes { + Button(action: { + openURL(URL(string: "https://opencollective.com/xcodesapp")!) + }) { + HStack { + Image(systemName: "heart.circle") + Text("Support.Xcodes") + } } } - Text(Bundle.main.shortVersion!) + Text(verbatim: "\(Bundle.main.shortVersion!) (\(Bundle.main.version!))") .font(.subheadline) } .frame(maxWidth: .infinity, maxHeight: 30, alignment: .leading) @@ -51,8 +55,34 @@ extension View { struct Previews_BottomStatusBar_Previews: PreviewProvider { static var previews: some View { - HStack { - - }.bottomStatusBar() + Group { + HStack { + + } + .bottomStatusBar() + .environmentObject({ () -> AppState in + let a = AppState() + return a }() + ) + .defaultAppStorage({ () -> UserDefaults in + let d = UserDefaults(suiteName: "hide_support")! + d.set(true, forKey: PreferenceKey.hideSupportXcodes.rawValue) + return d + }()) + + HStack { + + } + .bottomStatusBar() + .environmentObject({ () -> AppState in + let a = AppState() + return a }() + ) + .defaultAppStorage({ () -> UserDefaults in + let d = UserDefaults(suiteName: "show_support")! + d.set(false, forKey: PreferenceKey.hideSupportXcodes.rawValue) + return d + }()) + } } } diff --git a/Xcodes/Frontend/XcodeList/InstallationStepRowView.swift b/Xcodes/Frontend/XcodeList/InstallationStepRowView.swift index 3bf7db5..1d605fa 100644 --- a/Xcodes/Frontend/XcodeList/InstallationStepRowView.swift +++ b/Xcodes/Frontend/XcodeList/InstallationStepRowView.swift @@ -18,7 +18,7 @@ struct InstallationStepRowView: View { controlSize: .small, style: .spinning ) - case .unarchiving, .moving, .trashingArchive, .checkingSecurity, .finishing: + case .authenticating, .unarchiving, .moving, .trashingArchive, .checkingSecurity, .finishing: ProgressView() .scaleEffect(0.5) } diff --git a/Xcodes/Frontend/XcodeList/MainToolbar.swift b/Xcodes/Frontend/XcodeList/MainToolbar.swift index b27a998..e3850c6 100644 --- a/Xcodes/Frontend/XcodeList/MainToolbar.swift +++ b/Xcodes/Frontend/XcodeList/MainToolbar.swift @@ -5,7 +5,7 @@ struct MainToolbarModifier: ViewModifier { @Binding var category: XcodeListCategory @Binding var isInstalledOnly: Bool @Binding var isShowingInfoPane: Bool - + func body(content: Content) -> some View { content .toolbar { toolbar } @@ -13,9 +13,8 @@ struct MainToolbarModifier: ViewModifier { private var toolbar: some ToolbarContent { ToolbarItemGroup { - ProgressButton( - isInProgress: appState.isUpdating, + isInProgress: appState.isUpdating, action: appState.update ) { Label("Refresh", systemImage: "arrow.clockwise") @@ -23,6 +22,7 @@ struct MainToolbarModifier: ViewModifier { .keyboardShortcut(KeyEquivalent("r")) .help("RefreshDescription") Spacer() + Button(action: { switch category { case .all: category = .release @@ -35,39 +35,22 @@ struct MainToolbarModifier: ViewModifier { case .all: Label("All", systemImage: "line.horizontal.3.decrease.circle") case .release: - if #available(macOS 11.3, *) { Label("ReleaseOnly", systemImage: "line.horizontal.3.decrease.circle.fill") - .labelStyle(TitleAndIconLabelStyle()) + .labelStyle(.trailingIcon) .foregroundColor(.accentColor) - } else { - Label("ReleaseOnly", systemImage: "line.horizontal.3.decrease.circle.fill") - .labelStyle(TitleOnlyLabelStyle()) - .foregroundColor(.accentColor) - } case .beta: - if #available(macOS 11.3, *) { - Label("BetaOnly", systemImage: "line.horizontal.3.decrease.circle.fill") - .labelStyle(TitleAndIconLabelStyle()) - .foregroundColor(.accentColor) - } else { - Label("BetaOnly", systemImage: "line.horizontal.3.decrease.circle.fill") - .labelStyle(TitleOnlyLabelStyle()) - .foregroundColor(.accentColor) - } + Label("BetaOnly", systemImage: "line.horizontal.3.decrease.circle.fill") + .labelStyle(.trailingIcon) + .foregroundColor(.accentColor) case .releasePlusNewBetas: - if #available(macOS 11.3, *) { - Label("ReleasePlusNewBetas", systemImage: "line.horizontal.3.decrease.circle.fill") - .labelStyle(TitleAndIconLabelStyle()) - .foregroundColor(.accentColor) - } else { - Label("ReleasePlusNewBetas", systemImage: "line.horizontal.3.decrease.circle.fill") - .labelStyle(TitleOnlyLabelStyle()) - .foregroundColor(.accentColor) - } + Label("ReleasePlusNewBetas", systemImage: "line.horizontal.3.decrease.circle.fill") + .labelStyle(.trailingIcon) + .foregroundColor(.accentColor) } } .help("FilterAvailableDescription") - + .disabled(category.isManaged) + Button(action: { isInstalledOnly.toggle() }) { @@ -76,11 +59,9 @@ struct MainToolbarModifier: ViewModifier { .foregroundColor(.accentColor) } else { Label("Filter", systemImage: "arrow.down.app") - } } .help("FilterInstalledDescription") - } } } @@ -91,7 +72,7 @@ extension View { isInstalledOnly: Binding, isShowingInfoPane: Binding ) -> some View { - self.modifier( + modifier( MainToolbarModifier( category: category, isInstalledOnly: isInstalledOnly, diff --git a/Xcodes/Frontend/XcodeList/XcodeListCategory.swift b/Xcodes/Frontend/XcodeList/XcodeListCategory.swift index 89772b4..d9ce2a9 100644 --- a/Xcodes/Frontend/XcodeList/XcodeListCategory.swift +++ b/Xcodes/Frontend/XcodeList/XcodeListCategory.swift @@ -16,4 +16,6 @@ enum XcodeListCategory: String, CaseIterable, Identifiable, CustomStringConverti case .releasePlusNewBetas: return localizeString("ReleasePlusNewBetas") } } + + var isManaged: Bool { PreferenceKey.xcodeListCategory.isManaged() } } diff --git a/Xcodes/Frontend/XcodeList/XcodeListView.swift b/Xcodes/Frontend/XcodeList/XcodeListView.swift index 68dec20..011070c 100644 --- a/Xcodes/Frontend/XcodeList/XcodeListView.swift +++ b/Xcodes/Frontend/XcodeList/XcodeListView.swift @@ -8,7 +8,8 @@ struct XcodeListView: View { private let searchText: String private let category: XcodeListCategory private let isInstalledOnly: Bool - + @AppStorage(PreferenceKey.allowedMajorVersions.rawValue) private var allowedMajorVersions = Int.max + init(selectedXcodeID: Binding, searchText: String, category: XcodeListCategory, isInstalledOnly: Bool) { self._selectedXcodeID = selectedXcodeID self.searchText = searchText @@ -36,6 +37,22 @@ struct XcodeListView: View { } } + let latestMajor = xcodes.sorted(\.version) + .filter { $0.version.isNotPrerelease } + .last? + .version + .major + + xcodes = xcodes.filter { + if $0.installState.notInstalled, + let latestMajor = latestMajor, + $0.version.major < (latestMajor - min(latestMajor,allowedMajorVersions)) { + return false + } + + return true + } + if !searchText.isEmpty { xcodes = xcodes.filter { $0.description.contains(searchText) } } @@ -54,7 +71,8 @@ struct XcodeListView: View { .listStyle(.sidebar) .safeAreaInset(edge: .bottom, spacing: 0) { PlatformsPocket() - .padding() + .padding(.horizontal) + .padding(.vertical, 8) } } } @@ -63,19 +81,21 @@ struct PlatformsPocket: View { @SwiftUI.Environment(\.openWindow) private var openWindow var body: some View { - Button(action: { - openWindow(id: "platforms") } + Button(action: { + openWindow(id: "platforms") + } ) { - VStack(spacing: 5) { + HStack(spacing: 5) { Image(systemName: "square.3.layers.3d") - .font(.title) - Text("Platforms") - .font(.callout) + .font(.title3.weight(.medium)) + Text("PlatformsDescription") + Spacer() } - .frame(width: 70, height: 70) - .background(.quaternary) - .clipShape(RoundedRectangle(cornerRadius: 5, style: .continuous)) - + .font(.body.weight(.medium)) + .padding(.horizontal) + .padding(.vertical, 12) + .background(.quaternary.opacity(0.75)) + .clipShape(RoundedRectangle(cornerRadius: 8, style: .continuous)) } .buttonStyle(.plain) } @@ -93,6 +113,9 @@ struct XcodeListView_Previews: PreviewProvider { Xcode(version: Version("12.2.0")!, installState: .notInstalled, selected: false, icon: nil), Xcode(version: Version("12.1.0")!, installState: .installing(.downloading(progress: configure(Progress(totalUnitCount: 100)) { $0.completedUnitCount = 40 })), selected: false, icon: nil), Xcode(version: Version("12.0.0")!, installState: .installed(Path("/Applications/Xcode-12.3.0.app")!), selected: false, icon: nil), + Xcode(version: Version("10.1.0")!, installState: .notInstalled, selected: false, icon: nil), + Xcode(version: Version("10.0.0")!, installState: .installed(Path("/Applications/Xcode-10.0.0.app")!), selected: false, icon: nil), + Xcode(version: Version("9.0.0")!, installState: .notInstalled, selected: false, icon: nil), ] return a }()) diff --git a/Xcodes/Frontend/XcodeList/XcodeListViewRow.swift b/Xcodes/Frontend/XcodeList/XcodeListViewRow.swift index 55f431c..60777d7 100644 --- a/Xcodes/Frontend/XcodeList/XcodeListViewRow.swift +++ b/Xcodes/Frontend/XcodeList/XcodeListViewRow.swift @@ -30,9 +30,6 @@ struct XcodeListViewRow: View { Text(verbatim: path.string) .font(.caption) .foregroundColor(.secondary) - } else { - Text(verbatim: "") - .font(.caption) } } @@ -42,6 +39,7 @@ struct XcodeListViewRow: View { .padding(.trailing, 16) installControl(for: xcode) } + .padding(.vertical, 4) .contextMenu { switch xcode.installState { case .notInstalled: @@ -75,9 +73,10 @@ struct XcodeListViewRow: View { if let icon = xcode.icon { Image(nsImage: icon) } else { - Color.clear + Image(xcode.version.isPrerelease ? "xcode-beta" : "xcode") + .resizable() .frame(width: 32, height: 32) - .foregroundColor(.secondary) + .opacity(0.2) } } diff --git a/Xcodes/Resources/Assets.xcassets/xcode-beta.imageset/Contents.json b/Xcodes/Resources/Assets.xcassets/xcode-beta.imageset/Contents.json new file mode 100644 index 0000000..62beec5 --- /dev/null +++ b/Xcodes/Resources/Assets.xcassets/xcode-beta.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "Image.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Xcodes/Resources/Assets.xcassets/xcode-beta.imageset/Image.png b/Xcodes/Resources/Assets.xcassets/xcode-beta.imageset/Image.png new file mode 100644 index 0000000..2bc9d7a Binary files /dev/null and b/Xcodes/Resources/Assets.xcassets/xcode-beta.imageset/Image.png differ diff --git a/Xcodes/Resources/Assets.xcassets/xcode.imageset/Contents.json b/Xcodes/Resources/Assets.xcassets/xcode.imageset/Contents.json new file mode 100644 index 0000000..4c2a7f7 --- /dev/null +++ b/Xcodes/Resources/Assets.xcassets/xcode.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "xcode.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Xcodes/Resources/Assets.xcassets/xcode.imageset/xcode.png b/Xcodes/Resources/Assets.xcassets/xcode.imageset/xcode.png new file mode 100644 index 0000000..0545cec Binary files /dev/null and b/Xcodes/Resources/Assets.xcassets/xcode.imageset/xcode.png differ diff --git a/Xcodes/Resources/Info.plist b/Xcodes/Resources/Info.plist index 8e15e9d..21f15c9 100644 --- a/Xcodes/Resources/Info.plist +++ b/Xcodes/Resources/Info.plist @@ -40,6 +40,6 @@ SUFeedURL https://www.xcodes.app/appcast.xml SUPublicEDKey - GrqOrFQHxfqoLFCAGc9luvsAWQifHtG9gQ3NVJ583tE= + SEcz0vgUSeBTOoAXYe+64zea95G6lIf5NgzFs3InYJQ= diff --git a/Xcodes/Resources/Licenses.rtf b/Xcodes/Resources/Licenses.rtf index 079a137..152e9bf 100644 --- a/Xcodes/Resources/Licenses.rtf +++ b/Xcodes/Resources/Licenses.rtf @@ -1,4 +1,4 @@ -{\rtf1\ansi\ansicpg1252\cocoartf2759 +{\rtf1\ansi\ansicpg1252\cocoartf2822 \cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fnil\fcharset0 .SFNS-Regular;} {\colortbl;\red255\green255\blue255;} {\*\expandedcolortbl;;} @@ -58,6 +58,33 @@ SOFTWARE.\ \ \ +\fs34 LibFido2Swift\ +\ + +\fs26 MIT License\ +\ +Copyright (c) 2024 Kino Roy\ +\ +Permission is hereby granted, free of charge, to any person obtaining a copy\ +of this software and associated documentation files (the "Software"), to deal\ +in the Software without restriction, including without limitation the rights\ +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ +copies of the Software, and to permit persons to whom the Software is\ +furnished to do so, subject to the following conditions:\ +\ +The above copyright notice and this permission notice shall be included in all\ +copies or substantial portions of the Software.\ +\ +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\ +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\ +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\ +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\ +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\ +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\ +SOFTWARE.\ +\ +\ + \fs34 ErrorHandling\ \ @@ -86,6 +113,241 @@ SOFTWARE.\ \ \ +\fs34 big-num\ +\ + +\fs26 MIT License\ +\ +Copyright (c) 2019 Adam Fowler\ +\ +Permission is hereby granted, free of charge, to any person obtaining a copy\ +of this software and associated documentation files (the "Software"), to deal\ +in the Software without restriction, including without limitation the rights\ +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ +copies of the Software, and to permit persons to whom the Software is\ +furnished to do so, subject to the following conditions:\ +\ +The above copyright notice and this permission notice shall be included in all\ +copies or substantial portions of the Software.\ +\ +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\ +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\ +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\ +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\ +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\ +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\ +SOFTWARE.\ +\ +\ + +\fs34 swift-crypto\ +\ + +\fs26 \ + Apache License\ + Version 2.0, January 2004\ + http://www.apache.org/licenses/\ +\ + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\ +\ + 1. Definitions.\ +\ + "License" shall mean the terms and conditions for use, reproduction,\ + and distribution as defined by Sections 1 through 9 of this document.\ +\ + "Licensor" shall mean the copyright owner or entity authorized by\ + the copyright owner that is granting the License.\ +\ + "Legal Entity" shall mean the union of the acting entity and all\ + other entities that control, are controlled by, or are under common\ + control with that entity. For the purposes of this definition,\ + "control" means (i) the power, direct or indirect, to cause the\ + direction or management of such entity, whether by contract or\ + otherwise, or (ii) ownership of fifty percent (50%) or more of the\ + outstanding shares, or (iii) beneficial ownership of such entity.\ +\ + "You" (or "Your") shall mean an individual or Legal Entity\ + exercising permissions granted by this License.\ +\ + "Source" form shall mean the preferred form for making modifications,\ + including but not limited to software source code, documentation\ + source, and configuration files.\ +\ + "Object" form shall mean any form resulting from mechanical\ + transformation or translation of a Source form, including but\ + not limited to compiled object code, generated documentation,\ + and conversions to other media types.\ +\ + "Work" shall mean the work of authorship, whether in Source or\ + Object form, made available under the License, as indicated by a\ + copyright notice that is included in or attached to the work\ + (an example is provided in the Appendix below).\ +\ + "Derivative Works" shall mean any work, whether in Source or Object\ + form, that is based on (or derived from) the Work and for which the\ + editorial revisions, annotations, elaborations, or other modifications\ + represent, as a whole, an original work of authorship. For the purposes\ + of this License, Derivative Works shall not include works that remain\ + separable from, or merely link (or bind by name) to the interfaces of,\ + the Work and Derivative Works thereof.\ +\ + "Contribution" shall mean any work of authorship, including\ + the original version of the Work and any modifications or additions\ + to that Work or Derivative Works thereof, that is intentionally\ + submitted to Licensor for inclusion in the Work by the copyright owner\ + or by an individual or Legal Entity authorized to submit on behalf of\ + the copyright owner. For the purposes of this definition, "submitted"\ + means any form of electronic, verbal, or written communication sent\ + to the Licensor or its representatives, including but not limited to\ + communication on electronic mailing lists, source code control systems,\ + and issue tracking systems that are managed by, or on behalf of, the\ + Licensor for the purpose of discussing and improving the Work, but\ + excluding communication that is conspicuously marked or otherwise\ + designated in writing by the copyright owner as "Not a Contribution."\ +\ + "Contributor" shall mean Licensor and any individual or Legal Entity\ + on behalf of whom a Contribution has been received by Licensor and\ + subsequently incorporated within the Work.\ +\ + 2. Grant of Copyright License. Subject to the terms and conditions of\ + this License, each Contributor hereby grants to You a perpetual,\ + worldwide, non-exclusive, no-charge, royalty-free, irrevocable\ + copyright license to reproduce, prepare Derivative Works of,\ + publicly display, publicly perform, sublicense, and distribute the\ + Work and such Derivative Works in Source or Object form.\ +\ + 3. Grant of Patent License. Subject to the terms and conditions of\ + this License, each Contributor hereby grants to You a perpetual,\ + worldwide, non-exclusive, no-charge, royalty-free, irrevocable\ + (except as stated in this section) patent license to make, have made,\ + use, offer to sell, sell, import, and otherwise transfer the Work,\ + where such license applies only to those patent claims licensable\ + by such Contributor that are necessarily infringed by their\ + Contribution(s) alone or by combination of their Contribution(s)\ + with the Work to which such Contribution(s) was submitted. If You\ + institute patent litigation against any entity (including a\ + cross-claim or counterclaim in a lawsuit) alleging that the Work\ + or a Contribution incorporated within the Work constitutes direct\ + or contributory patent infringement, then any patent licenses\ + granted to You under this License for that Work shall terminate\ + as of the date such litigation is filed.\ +\ + 4. Redistribution. You may reproduce and distribute copies of the\ + Work or Derivative Works thereof in any medium, with or without\ + modifications, and in Source or Object form, provided that You\ + meet the following conditions:\ +\ + (a) You must give any other recipients of the Work or\ + Derivative Works a copy of this License; and\ +\ + (b) You must cause any modified files to carry prominent notices\ + stating that You changed the files; and\ +\ + (c) You must retain, in the Source form of any Derivative Works\ + that You distribute, all copyright, patent, trademark, and\ + attribution notices from the Source form of the Work,\ + excluding those notices that do not pertain to any part of\ + the Derivative Works; and\ +\ + (d) If the Work includes a "NOTICE" text file as part of its\ + distribution, then any Derivative Works that You distribute must\ + include a readable copy of the attribution notices contained\ + within such NOTICE file, excluding those notices that do not\ + pertain to any part of the Derivative Works, in at least one\ + of the following places: within a NOTICE text file distributed\ + as part of the Derivative Works; within the Source form or\ + documentation, if provided along with the Derivative Works; or,\ + within a display generated by the Derivative Works, if and\ + wherever such third-party notices normally appear. The contents\ + of the NOTICE file are for informational purposes only and\ + do not modify the License. You may add Your own attribution\ + notices within Derivative Works that You distribute, alongside\ + or as an addendum to the NOTICE text from the Work, provided\ + that such additional attribution notices cannot be construed\ + as modifying the License.\ +\ + You may add Your own copyright statement to Your modifications and\ + may provide additional or different license terms and conditions\ + for use, reproduction, or distribution of Your modifications, or\ + for any such Derivative Works as a whole, provided Your use,\ + reproduction, and distribution of the Work otherwise complies with\ + the conditions stated in this License.\ +\ + 5. Submission of Contributions. Unless You explicitly state otherwise,\ + any Contribution intentionally submitted for inclusion in the Work\ + by You to the Licensor shall be under the terms and conditions of\ + this License, without any additional terms or conditions.\ + Notwithstanding the above, nothing herein shall supersede or modify\ + the terms of any separate license agreement you may have executed\ + with Licensor regarding such Contributions.\ +\ + 6. Trademarks. This License does not grant permission to use the trade\ + names, trademarks, service marks, or product names of the Licensor,\ + except as required for reasonable and customary use in describing the\ + origin of the Work and reproducing the content of the NOTICE file.\ +\ + 7. Disclaimer of Warranty. Unless required by applicable law or\ + agreed to in writing, Licensor provides the Work (and each\ + Contributor provides its Contributions) on an "AS IS" BASIS,\ + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\ + implied, including, without limitation, any warranties or conditions\ + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\ + PARTICULAR PURPOSE. You are solely responsible for determining the\ + appropriateness of using or redistributing the Work and assume any\ + risks associated with Your exercise of permissions under this License.\ +\ + 8. Limitation of Liability. In no event and under no legal theory,\ + whether in tort (including negligence), contract, or otherwise,\ + unless required by applicable law (such as deliberate and grossly\ + negligent acts) or agreed to in writing, shall any Contributor be\ + liable to You for damages, including any direct, indirect, special,\ + incidental, or consequential damages of any character arising as a\ + result of this License or out of the use or inability to use the\ + Work (including but not limited to damages for loss of goodwill,\ + work stoppage, computer failure or malfunction, or any and all\ + other commercial damages or losses), even if such Contributor\ + has been advised of the possibility of such damages.\ +\ + 9. Accepting Warranty or Additional Liability. While redistributing\ + the Work or Derivative Works thereof, You may choose to offer,\ + and charge a fee for, acceptance of support, warranty, indemnity,\ + or other liability obligations and/or rights consistent with this\ + License. However, in accepting such obligations, You may act only\ + on Your own behalf and on Your sole responsibility, not on behalf\ + of any other Contributor, and only if You agree to indemnify,\ + defend, and hold each Contributor harmless for any liability\ + incurred by, or claims asserted against, such Contributor by reason\ + of your accepting any such warranty or additional liability.\ +\ + END OF TERMS AND CONDITIONS\ +\ + APPENDIX: How to apply the Apache License to your work.\ +\ + To apply the Apache License to your work, attach the following\ + boilerplate notice, with the fields enclosed by brackets "[]"\ + replaced with your own identifying information. (Don't include\ + the brackets!) The text should be enclosed in the appropriate\ + comment syntax for the file format. We also recommend that a\ + file or class name and description of purpose be included on the\ + same "printed page" as the copyright notice for easier\ + identification within third-party archives.\ +\ + Copyright [yyyy] [name of copyright owner]\ +\ + Licensed under the Apache License, Version 2.0 (the "License");\ + you may not use this file except in compliance with the License.\ + You may obtain a copy of the License at\ +\ + http://www.apache.org/licenses/LICENSE-2.0\ +\ + Unless required by applicable law or agreed to in writing, software\ + distributed under the License is distributed on an "AS IS" BASIS,\ + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\ + See the License for the specific language governing permissions and\ + limitations under the License.\ +\ +\ + \fs34 Path.swift\ \ @@ -552,12 +814,219 @@ For more information, please refer to <>\ \ \ +\fs34 swift-srp\ +\ + +\fs26 Apache License\ + Version 2.0, January 2004\ + http://www.apache.org/licenses/\ +\ + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\ +\ + 1. Definitions.\ +\ + "License" shall mean the terms and conditions for use, reproduction,\ + and distribution as defined by Sections 1 through 9 of this document.\ +\ + "Licensor" shall mean the copyright owner or entity authorized by\ + the copyright owner that is granting the License.\ +\ + "Legal Entity" shall mean the union of the acting entity and all\ + other entities that control, are controlled by, or are under common\ + control with that entity. For the purposes of this definition,\ + "control" means (i) the power, direct or indirect, to cause the\ + direction or management of such entity, whether by contract or\ + otherwise, or (ii) ownership of fifty percent (50%) or more of the\ + outstanding shares, or (iii) beneficial ownership of such entity.\ +\ + "You" (or "Your") shall mean an individual or Legal Entity\ + exercising permissions granted by this License.\ +\ + "Source" form shall mean the preferred form for making modifications,\ + including but not limited to software source code, documentation\ + source, and configuration files.\ +\ + "Object" form shall mean any form resulting from mechanical\ + transformation or translation of a Source form, including but\ + not limited to compiled object code, generated documentation,\ + and conversions to other media types.\ +\ + "Work" shall mean the work of authorship, whether in Source or\ + Object form, made available under the License, as indicated by a\ + copyright notice that is included in or attached to the work\ + (an example is provided in the Appendix below).\ +\ + "Derivative Works" shall mean any work, whether in Source or Object\ + form, that is based on (or derived from) the Work and for which the\ + editorial revisions, annotations, elaborations, or other modifications\ + represent, as a whole, an original work of authorship. For the purposes\ + of this License, Derivative Works shall not include works that remain\ + separable from, or merely link (or bind by name) to the interfaces of,\ + the Work and Derivative Works thereof.\ +\ + "Contribution" shall mean any work of authorship, including\ + the original version of the Work and any modifications or additions\ + to that Work or Derivative Works thereof, that is intentionally\ + submitted to Licensor for inclusion in the Work by the copyright owner\ + or by an individual or Legal Entity authorized to submit on behalf of\ + the copyright owner. For the purposes of this definition, "submitted"\ + means any form of electronic, verbal, or written communication sent\ + to the Licensor or its representatives, including but not limited to\ + communication on electronic mailing lists, source code control systems,\ + and issue tracking systems that are managed by, or on behalf of, the\ + Licensor for the purpose of discussing and improving the Work, but\ + excluding communication that is conspicuously marked or otherwise\ + designated in writing by the copyright owner as "Not a Contribution."\ +\ + "Contributor" shall mean Licensor and any individual or Legal Entity\ + on behalf of whom a Contribution has been received by Licensor and\ + subsequently incorporated within the Work.\ +\ + 2. Grant of Copyright License. Subject to the terms and conditions of\ + this License, each Contributor hereby grants to You a perpetual,\ + worldwide, non-exclusive, no-charge, royalty-free, irrevocable\ + copyright license to reproduce, prepare Derivative Works of,\ + publicly display, publicly perform, sublicense, and distribute the\ + Work and such Derivative Works in Source or Object form.\ +\ + 3. Grant of Patent License. Subject to the terms and conditions of\ + this License, each Contributor hereby grants to You a perpetual,\ + worldwide, non-exclusive, no-charge, royalty-free, irrevocable\ + (except as stated in this section) patent license to make, have made,\ + use, offer to sell, sell, import, and otherwise transfer the Work,\ + where such license applies only to those patent claims licensable\ + by such Contributor that are necessarily infringed by their\ + Contribution(s) alone or by combination of their Contribution(s)\ + with the Work to which such Contribution(s) was submitted. If You\ + institute patent litigation against any entity (including a\ + cross-claim or counterclaim in a lawsuit) alleging that the Work\ + or a Contribution incorporated within the Work constitutes direct\ + or contributory patent infringement, then any patent licenses\ + granted to You under this License for that Work shall terminate\ + as of the date such litigation is filed.\ +\ + 4. Redistribution. You may reproduce and distribute copies of the\ + Work or Derivative Works thereof in any medium, with or without\ + modifications, and in Source or Object form, provided that You\ + meet the following conditions:\ +\ + (a) You must give any other recipients of the Work or\ + Derivative Works a copy of this License; and\ +\ + (b) You must cause any modified files to carry prominent notices\ + stating that You changed the files; and\ +\ + (c) You must retain, in the Source form of any Derivative Works\ + that You distribute, all copyright, patent, trademark, and\ + attribution notices from the Source form of the Work,\ + excluding those notices that do not pertain to any part of\ + the Derivative Works; and\ +\ + (d) If the Work includes a "NOTICE" text file as part of its\ + distribution, then any Derivative Works that You distribute must\ + include a readable copy of the attribution notices contained\ + within such NOTICE file, excluding those notices that do not\ + pertain to any part of the Derivative Works, in at least one\ + of the following places: within a NOTICE text file distributed\ + as part of the Derivative Works; within the Source form or\ + documentation, if provided along with the Derivative Works; or,\ + within a display generated by the Derivative Works, if and\ + wherever such third-party notices normally appear. The contents\ + of the NOTICE file are for informational purposes only and\ + do not modify the License. You may add Your own attribution\ + notices within Derivative Works that You distribute, alongside\ + or as an addendum to the NOTICE text from the Work, provided\ + that such additional attribution notices cannot be construed\ + as modifying the License.\ +\ + You may add Your own copyright statement to Your modifications and\ + may provide additional or different license terms and conditions\ + for use, reproduction, or distribution of Your modifications, or\ + for any such Derivative Works as a whole, provided Your use,\ + reproduction, and distribution of the Work otherwise complies with\ + the conditions stated in this License.\ +\ + 5. Submission of Contributions. Unless You explicitly state otherwise,\ + any Contribution intentionally submitted for inclusion in the Work\ + by You to the Licensor shall be under the terms and conditions of\ + this License, without any additional terms or conditions.\ + Notwithstanding the above, nothing herein shall supersede or modify\ + the terms of any separate license agreement you may have executed\ + with Licensor regarding such Contributions.\ +\ + 6. Trademarks. This License does not grant permission to use the trade\ + names, trademarks, service marks, or product names of the Licensor,\ + except as required for reasonable and customary use in describing the\ + origin of the Work and reproducing the content of the NOTICE file.\ +\ + 7. Disclaimer of Warranty. Unless required by applicable law or\ + agreed to in writing, Licensor provides the Work (and each\ + Contributor provides its Contributions) on an "AS IS" BASIS,\ + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\ + implied, including, without limitation, any warranties or conditions\ + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\ + PARTICULAR PURPOSE. You are solely responsible for determining the\ + appropriateness of using or redistributing the Work and assume any\ + risks associated with Your exercise of permissions under this License.\ +\ + 8. Limitation of Liability. In no event and under no legal theory,\ + whether in tort (including negligence), contract, or otherwise,\ + unless required by applicable law (such as deliberate and grossly\ + negligent acts) or agreed to in writing, shall any Contributor be\ + liable to You for damages, including any direct, indirect, special,\ + incidental, or consequential damages of any character arising as a\ + result of this License or out of the use or inability to use the\ + Work (including but not limited to damages for loss of goodwill,\ + work stoppage, computer failure or malfunction, or any and all\ + other commercial damages or losses), even if such Contributor\ + has been advised of the possibility of such damages.\ +\ + 9. Accepting Warranty or Additional Liability. While redistributing\ + the Work or Derivative Works thereof, You may choose to offer,\ + and charge a fee for, acceptance of support, warranty, indemnity,\ + or other liability obligations and/or rights consistent with this\ + License. However, in accepting such obligations, You may act only\ + on Your own behalf and on Your sole responsibility, not on behalf\ + of any other Contributor, and only if You agree to indemnify,\ + defend, and hold each Contributor harmless for any liability\ + incurred by, or claims asserted against, such Contributor by reason\ + of your accepting any such warranty or additional liability.\ +\ + END OF TERMS AND CONDITIONS\ +\ + APPENDIX: How to apply the Apache License to your work.\ +\ + To apply the Apache License to your work, attach the following\ + boilerplate notice, with the fields enclosed by brackets "[]"\ + replaced with your own identifying information. (Don't include\ + the brackets!) The text should be enclosed in the appropriate\ + comment syntax for the file format. We also recommend that a\ + file or class name and description of purpose be included on the\ + same "printed page" as the copyright notice for easier\ + identification within third-party archives.\ +\ + Copyright [yyyy] [name of copyright owner]\ +\ + Licensed under the Apache License, Version 2.0 (the "License");\ + you may not use this file except in compliance with the License.\ + You may obtain a copy of the License at\ +\ + http://www.apache.org/licenses/LICENSE-2.0\ +\ + Unless required by applicable law or agreed to in writing, software\ + distributed under the License is distributed on an "AS IS" BASIS,\ + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\ + See the License for the specific language governing permissions and\ + limitations under the License.\ +\ +\ + \fs34 DockProgress\ \ \fs26 MIT License\ \ -Copyright (c) Sindre Sorhus (sindresorhus.com)\ +Copyright (c) Sindre Sorhus (https://sindresorhus.com)\ \ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\ \ diff --git a/Xcodes/Resources/Localizable.xcstrings b/Xcodes/Resources/Localizable.xcstrings index e79ec8a..2b31378 100644 --- a/Xcodes/Resources/Localizable.xcstrings +++ b/Xcodes/Resources/Localizable.xcstrings @@ -3,52 +3,372 @@ "strings" : { "" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : " " + } + }, + "ca" : { + "stringUnit" : { + "state" : "translated", + "value" : "" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "" + } + }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "" } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "" + } } } }, "%@" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" + } + }, + "ca" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" + } + }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "%@" } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" + } } } }, "%@ %@ %@" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@%2$@%3$@" + } + }, + "ca" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ %2$@ %3$@" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ %2$@ %3$@" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ %2$@ %3$@" + } + }, "en" : { "stringUnit" : { "state" : "new", "value" : "%1$@ %2$@ %3$@" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ %2$@ %3$@" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ %2$@ %3$@" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ %2$@ %3$@" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ %2$@ %3$@" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ %2$@ %3$@" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ %2$@ %3$@" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ %2$@ %3$@" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ %2$@ %3$@" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ %2$@ %3$@" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ %2$@ %3$@" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ %2$@ %3$@" + } + }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "%1$@ %2$@ %3$@" } - } - } - }, - "• %@" : { - "localizations" : { - "tr" : { + }, + "uk" : { "stringUnit" : { "state" : "translated", - "value" : "• %@" + "value" : "%1$@ %2$@ %3$@" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ %2$@ %3$@" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ %2$@ %3$@" } } } }, "AccessDenied" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "⚠️ تم الرفض ⚠️

فضلا قوم بفتح اعدادات الاشعارات
واختار تحديث الXcode الذي تريده
ثم اعطي اذن بالسماح" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -61,6 +381,12 @@ "value" : "⚠️ Zugriff verweigert ⚠️\n\nBitte öffne Deine Benachrichtigungs-Einstellungen und wähle Xcodes aus, wenn Du den Zugriff erlauben möchtest." } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "⚠️ Άρνηση Πρόσβασης ⚠️\n\nΑνοίξτε τις Ρυθμίσεις Γνωστοποιήσεων και επιλέξτε το Xcodes αν επιθυμείτε να επιτρέψετε την πρόσβαση." + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -142,7 +468,7 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "⚠️ Сповіщення Заборонено ⚠️\n\nВідкрийте будьласка Налаштування Сповіщень та надайте дозвіл Xcodes для отримання дозволу." + "value" : "⚠️ Сповіщення Заборонено ⚠️\n\nВідкрийте будь ласка Налаштування Сповіщень та надайте дозвіл для Xcodes, якщо ви бажаєте отримувати сповіщення." } }, "zh-Hans" : { @@ -154,7 +480,7 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "⚠️ 通知已停用 ⚠️\n\n如果你想要啟用通知,請打開你的通知設定並選擇 Xcodes。" + "value" : "⚠️ 權限不足⚠️\n\n如果你想要啟用通知,請打開系統的通知設定並選擇 Xcodes。" } } } @@ -162,6 +488,12 @@ "AccessGranted" : { "comment" : "Notifications", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "لقد تم منح الوصول. سوف تتلقى إشعارات من Xcodes." + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -174,6 +506,12 @@ "value" : "Zugriff erlaubt. Du empfängst jetzt Benachrichtigungen von Xcodes." } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Η πρόσβαση εκχωρήθηκε. Θα λαμβάνετε γνωστοποιήσεις από το Xcode's." + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -255,7 +593,7 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Сповіщення Дозволено. Ви будете отримувати сповіщення від Xcodes." + "value" : "Сповіщення Дозволено.\nВи будете отримувати сповіщення від Xcodes." } }, "zh-Hans" : { @@ -267,13 +605,19 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "通知已啟用。你將會開始收到來自 Xcodes 的通知。" + "value" : "已核准權限。你可以收到來自 Xcodes 的通知。" } } } }, "Acknowledgements" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "شكر وتقدير" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -286,6 +630,12 @@ "value" : "Anerkennungen" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Αναγνωρίσεις" + } + }, "es" : { "stringUnit" : { "state" : "translated", @@ -325,7 +675,7 @@ "ko" : { "stringUnit" : { "state" : "translated", - "value" : "Acknowledgements" + "value" : "법적 고지" } }, "nl" : { @@ -380,6 +730,12 @@ }, "Active" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "نشط" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -392,6 +748,12 @@ "value" : "Aktiv" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ενεργή" + } + }, "es" : { "stringUnit" : { "state" : "translated", @@ -479,13 +841,19 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "啟用中" + "value" : "已啟用" } } } }, "Active/Select" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "نشط/اختر" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -498,6 +866,12 @@ "value" : "Aktiv/Auswählen" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ενεργή/Επιλεγμένη" + } + }, "es" : { "stringUnit" : { "state" : "translated", @@ -573,7 +947,7 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Акивний/Обрати" + "value" : "Активний / Вибраний" } }, "zh-Hans" : { @@ -593,6 +967,12 @@ "ActiveVersionDescription" : { "comment" : "List", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "هذا التحديث النشط" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -605,6 +985,12 @@ "value" : "Dies ist die aktive Version" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Αυτή είναι η ενεργή έκδοση" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -698,7 +1084,7 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "這是啟用中版本" + "value" : "這個版本已經啟用" } } } @@ -706,6 +1092,12 @@ "Advanced" : { "comment" : "Advanced Preference Pane", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "متقدم" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -718,6 +1110,12 @@ "value" : "Erweitert" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Προηγμένες" + } + }, "es" : { "stringUnit" : { "state" : "translated", @@ -812,6 +1210,12 @@ }, "Alert.CancelInstall.Message" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "سيتم تجاهل أي تقدم." + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -824,6 +1228,12 @@ "value" : "Jeglicher Fortschritt wird verworfen." } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Όποια πρόοδος θα απορριφθεί." + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -905,7 +1315,7 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Увесь прогрес буде скасовано." + "value" : "Будь-який прогрес буде втрачено." } }, "zh-Hans" : { @@ -917,13 +1327,19 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "所有已進行的安裝步驟將被放棄。" + "value" : "將放棄既有進度。" } } } }, "Alert.CancelInstall.PrimaryButton" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "إيقاف التثبيت" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -936,6 +1352,12 @@ "value" : "Installation anhalten" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Διακοπή εγκατάστασης" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -1017,7 +1439,7 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Зупинити встановлення" + "value" : "Скасувати інсталяцію" } }, "zh-Hans" : { @@ -1038,23 +1460,125 @@ "comment" : "Cancel Runtime Install", "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "هل أنت متأكد أنك تريد إيقاف تثبيت Xcode %@؟" + } + }, + "ca" : { + "stringUnit" : { + "state" : "translated", + "value" : "Are you sure you want to stop the installation of Xcode %@?" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bist du sicher, dass Du die Installation von Xcode %@ anhalten möchtest?" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Είστε βέβαιοι ότι θέλετε να διακόψετε την εγκατάσταση του Xcode %@;" + } + }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Are you sure you want to stop the installation of Xcode %@?" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Are you sure you want to stop the installation of Xcode %@?" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Are you sure you want to stop the installation of Xcode %@?" + } + }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Êtes-vous sûr de vouloir arrêter l'installation de Xcode %@ ?" } }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Are you sure you want to stop the installation of Xcode %@?" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Are you sure you want to stop the installation of Xcode %@?" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "Are you sure you want to stop the installation of Xcode %@?" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xcode %@ 설치를 중지하시겠습니까?" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Are you sure you want to stop the installation of Xcode %@?" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Are you sure you want to stop the installation of Xcode %@?" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Are you sure you want to stop the installation of Xcode %@?" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Вы уверены, что хотите остановить установку Xcode %@?" + } + }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Xcode %@ sürümünü yüklemeyi durdurmak istediğine emin misin?" } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Чи дійсно ви хочете скасувати інсталяцію Xcode %@?" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "你确定要停止安装Xcode %@吗?" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "你確定要停止安裝 Xcode %@ 嗎?" + } } } }, @@ -1062,6 +1586,12 @@ "comment" : "Cancel Install", "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "هل أنت متأكد أنك تريد إيقاف تثبيت Xcode %@؟" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -1074,6 +1604,12 @@ "value" : "Bist du sicher, dass Du die Installation von Xcode %@ anhalten möchtest?" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Είστε βέβαιοι ότι θέλετε να διακόψετε την εγκατάσταση του Xcode %@;" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -1155,7 +1691,7 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Впевнені що хочете скасувати встановлення Xcode %@?" + "value" : "Чи дійсно ви хочете скасувати інсталяцію Xcode %@?" } }, "zh-Hans" : { @@ -1167,7 +1703,7 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "你確定你想要停止安裝 Xcode %@?" + "value" : "你確定要停止安裝 Xcode %@ 嗎?" } } } @@ -1175,34 +1711,375 @@ "Alert.DeletePlatform.PrimaryButton" : { "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "مسح" + } + }, + "ca" : { + "stringUnit" : { + "state" : "translated", + "value" : "Delete" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Löschen" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Διαγραφή" + } + }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Delete" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Delete" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Delete" + } + }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Supprimer" } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Delete" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Delete" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "Delete" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "삭제" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Delete" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Delete" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Delete" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Удалить" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Delete" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Видалити" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "删除" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "刪除" + } } } }, "Alert.DeletePlatform.Title" : { "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "هل أنت متأكد أنك تريد حذف ٪@؟" + } + }, + "ca" : { + "stringUnit" : { + "state" : "translated", + "value" : "Are you sure you want to delete %@?" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bist du sicher, dass du %@ löschen möchtest?" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Θέλετε σίγουρα να διαγράψετε το %@;" + } + }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Are you sure you want to delete %@?" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Are you sure you want to delete %@?" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Are you sure you want to delete %@?" + } + }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Êtes-vous sûr de vouloir supprimer %@ ?" } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Are you sure you want to delete %@?" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Are you sure you want to delete %@?" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "Are you sure you want to delete %@?" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@을(를) 삭제하시겠습니까?" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Are you sure you want to delete %@?" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Are you sure you want to delete %@?" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Are you sure you want to delete %@?" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Вы уверены, что хотите удалить %@?" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Are you sure you want to delete %@?" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Чи дійсно ви хочете видалити %@?" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "你确定要删除%@吗?" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "你確定要刪除 %@ 嗎?" + } + } + } + }, + "Alert.Install.Error.Need.Xcode16.1" : { + "extractionState" : "manual", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apple only supports downloading runtimes iOS 16.0+, watchOS 9.0+, tvOS 16+, visionOS 1.0+ with Xcode 16.1+. Please download, make active and try again." + } + }, + "ca" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apple only supports downloading runtimes iOS 16.0+, watchOS 9.0+, tvOS 16+, visionOS 1.0+ with Xcode 16.1+. Please download, make active and try again." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apple only supports downloading runtimes iOS 16.0+, watchOS 9.0+, tvOS 16+, visionOS 1.0+ with Xcode 16.1+. Please download, make active and try again." + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apple only supports downloading runtimes iOS 16.0+, watchOS 9.0+, tvOS 16+, visionOS 1.0+ with Xcode 16.1+. Please download, make active and try again." + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apple only supports downloading runtimes iOS 16.0+, watchOS 9.0+, tvOS 16+, visionOS 1.0+ with Xcode 16.1+. Please download, make active and try again." + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apple only supports downloading runtimes iOS 16.0+, watchOS 9.0+, tvOS 16+, visionOS 1.0+ with Xcode 16.1+. Please download, make active and try again." + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apple only supports downloading runtimes iOS 16.0+, watchOS 9.0+, tvOS 16+, visionOS 1.0+ with Xcode 16.1+. Please download, make active and try again." + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apple only supports downloading runtimes iOS 16.0+, watchOS 9.0+, tvOS 16+, visionOS 1.0+ with Xcode 16.1+. Please download, make active and try again." + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apple only supports downloading runtimes iOS 16.0+, watchOS 9.0+, tvOS 16+, visionOS 1.0+ with Xcode 16.1+. Please download, make active and try again." + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apple only supports downloading runtimes iOS 16.0+, watchOS 9.0+, tvOS 16+, visionOS 1.0+ with Xcode 16.1+. Please download, make active and try again." + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apple only supports downloading runtimes iOS 16.0+, watchOS 9.0+, tvOS 16+, visionOS 1.0+ with Xcode 16.1+. Please download, make active and try again." + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apple only supports downloading runtimes iOS 16.0+, watchOS 9.0+, tvOS 16+, visionOS 1.0+ with Xcode 16.1+. Please download, make active and try again." + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apple only supports downloading runtimes iOS 16.0+, watchOS 9.0+, tvOS 16+, visionOS 1.0+ with Xcode 16.1+. Please download, make active and try again." + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apple only supports downloading runtimes iOS 16.0+, watchOS 9.0+, tvOS 16+, visionOS 1.0+ with Xcode 16.1+. Please download, make active and try again." + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apple only supports downloading runtimes iOS 16.0+, watchOS 9.0+, tvOS 16+, visionOS 1.0+ with Xcode 16.1+. Please download, make active and try again." + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apple only supports downloading runtimes iOS 16.0+, watchOS 9.0+, tvOS 16+, visionOS 1.0+ with Xcode 16.1+. Please download, make active and try again." + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apple only supports downloading runtimes iOS 16.0+, watchOS 9.0+, tvOS 16+, visionOS 1.0+ with Xcode 16.1+. Please download, make active and try again." + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apple only supports downloading runtimes iOS 16.0+, watchOS 9.0+, tvOS 16+, visionOS 1.0+ with Xcode 16.1+. Please download, make active and try again." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apple only supports downloading runtimes iOS 16.0+, watchOS 9.0+, tvOS 16+, visionOS 1.0+ with Xcode 16.1+. Please download, make active and try again." + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apple only supports downloading runtimes iOS 16.0+, watchOS 9.0+, tvOS 16+, visionOS 1.0+ with Xcode 16.1+. Please download, make active and try again." + } } } }, @@ -1210,6 +2087,12 @@ "comment" : "Install", "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "غير قادر على تثبيت Xcode" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -1222,6 +2105,12 @@ "value" : "Installation von Xcode nicht möglich" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Δεν είναι δυνατή η εγκατάσταση του Xcode" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -1303,7 +2192,7 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Не вдалося встановити Xcode" + "value" : "Не вдалося інсталювати Xcode" } }, "zh-Hans" : { @@ -1323,6 +2212,12 @@ "Alert.InstallArchive.Error.Title" : { "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "غير قادر على تثبيت Xcode المؤرشفة" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -1335,6 +2230,12 @@ "value" : "Installation des archivierten Xcodes nicht möglich" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Αδυναμία εγκατάστασης αρχειοθετημένου Xcode" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -1436,10 +2337,16 @@ "Alert.MinSupported.Message" : { "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "يتطلب Xcode %@ نظام التشغيل macOS %@، ولكنك تستخدم نظام التشغيل macOS %@، فهل مازلت ترغب في تثبيته؟" + } + }, "ca" : { "stringUnit" : { "state" : "translated", - "value" : "L'Xcode %@ necessita MacOS %@, però tu estàs utilitzant MacOS %@, vols instal·lar-lo igualment?" + "value" : "L'Xcode %@ necessita macOS %@, però tu estàs utilitzant macOS %@, vols instal·lar-lo igualment?" } }, "de" : { @@ -1448,16 +2355,22 @@ "value" : "Xcode %@ erfordert macOS %@ aber es läuft nur macOS %@. Möchtest Du es trotzdem installieren?" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Το Xcode %@ απαιτεί macOS %@, όμως το λειτουργικό σας σύστημα είναι macOS %@, εξακολουθείτε να θέλετε να το εγκαταστήσετε;" + } + }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Xcode %@ requires MacOS %@, but you are running MacOS %@, do you still want to install it?" + "value" : "Xcode %@ requires macOS %@, but you are running macOS %@, do you still want to install it?" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Xcode %@ requiere MacOS %@, pero está ejecutando MacOS %@, ¿aún desea instalarlo?" + "value" : "Xcode %@ requiere macOS %@, pero está ejecutando macOS %@, ¿aún desea instalarlo?" } }, "fi" : { @@ -1469,19 +2382,19 @@ "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Xcode %@ nécessite MacOS %@, mais vous utilisez MacOS %@, voulez-vous toujours l'installer ?" + "value" : "Xcode %@ nécessite macOS %@, mais vous utilisez macOS %@, voulez-vous toujours l'installer ?" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "Xcode %@ को MacOS %@ की आवश्यकता है, लेकिन आप MacOS %@ चला रहे हैं, क्या आप अभी भी इसे स्थापित करना चाहते हैं?" + "value" : "Xcode %@ को macOS %@ की आवश्यकता है, लेकिन आप macOS %@ चला रहे हैं, क्या आप अभी भी इसे स्थापित करना चाहते हैं?" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Xcode %@ richiede MacOS %@, ma la tua versione di MacOS è %@, vuoi installarlo comunque?" + "value" : "Xcode %@ richiede macOS %@, ma la tua versione di macOS è %@, vuoi installarlo comunque?" } }, "ja" : { @@ -1493,13 +2406,13 @@ "ko" : { "stringUnit" : { "state" : "translated", - "value" : "Xcode %@는 MacOS %@이(가) 필요하지만 MacOS %@을(를) 실행 중입니다. 그래도 설치하시겠습니까?" + "value" : "Xcode %@는 macOS %@이(가) 필요하지만 macOS %@을(를) 실행 중입니다. 그래도 설치하시겠습니까?" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Xcode %@ vereist MacOS %@, maar jouw MacOS versie is %@, wil je doorgaan met installeren?" + "value" : "Xcode %@ vereist macOS %@, maar jouw macOS versie is %@, wil je doorgaan met installeren?" } }, "pl" : { @@ -1511,7 +2424,7 @@ "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Xcode %@ requere MacOS %@, mas você está rodando MacOS %@, você ainda quer instalá-lo?" + "value" : "Xcode %@ requere macOS %@, mas você está rodando macOS %@, você ainda quer instalá-lo?" } }, "ru" : { @@ -1529,7 +2442,7 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Xcode %@ вимагає як мінімум MacOS %@, але у вас MacOS %@, всеодно продовжити?" + "value" : "Xcode %@ вимагає щонайменше macOS %@, але у вас macOS %@, всеодно продовжити?" } }, "zh-Hans" : { @@ -1541,14 +2454,20 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "Xcode %@ 需要 MacOS %@,但你正在使用 MacOS %@,你確定還要繼續安裝嗎?" + "value" : "Xcode %@ 需要 macOS %@,但你正在使用 macOS %@,你確定還要繼續安裝嗎?" } } } }, "Alert.MinSupported.Title" : { - "comment" : "Min MacOS Supported", + "comment" : "Min macOS Supported", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "لم يتم استيفاء الحد الأدنى من المتطلبات" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -1561,6 +2480,12 @@ "value" : "Minimalanforderungen nicht erfüllt" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Δεν ικανοποιούνται οι ελάχιστες απαιτήσεις συστήματος" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -1642,7 +2567,7 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Мінімальні вимоги не задоволені" + "value" : "Мінімальні вимоги не виконані" } }, "zh-Hans" : { @@ -1663,6 +2588,12 @@ "comment" : "Post install", "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "غير قادر على تنفيذ خطوات ما بعد التثبيت" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -1675,6 +2606,12 @@ "value" : "Ausführung von Post-Installationsschritten nicht möglich" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Αδυναμία εκτέλεσης μετά-εγκαταστατικών βημάτων" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -1762,7 +2699,7 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "无法执行安装前置步骤" + "value" : "无法执行安装后准备步骤" } }, "zh-Hant" : { @@ -1776,6 +2713,12 @@ "Alert.PrivilegedHelper.Error.Title" : { "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "غير قادر على تثبيت المساعد" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -1788,6 +2731,12 @@ "value" : "Installation des Helfers nicht möglich" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Αδυναμία εγκατάστασης του βοηθητικού εργαλείου" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -1869,7 +2818,7 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Не вдалося встановити \"privilege helper\"" + "value" : "Не вдалося інсталювати \"Privileged Helper\"" } }, "zh-Hans" : { @@ -1888,6 +2837,12 @@ }, "Alert.PrivilegedHelper.Message" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "يستخدم Xcodes مساعدًا متميزًا منفصلاً لأداء المهام كجذر. هذه هي الأشياء التي تتطلب استخدام Sudo في سطر الأوامر، بما في ذلك خطوات ما بعد التثبيت وتبديل إصدارات Xcode باستخدام xcode-select.\n\nسيُطلب منك كلمة مرور حساب macOS لتثبيته." + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -1900,6 +2855,12 @@ "value" : "Xcodes verwendet einen separaten privilegierten Helfer, um Aufgaben als root zu erledigen. Das sind Dinge, die sudo in der Kommandozeile erfordern würden, einschließlich Post-Installationsschritte sowie das Umstellen von Xcode-Versionen mit xcode-select.\n\nUm ihn zu installieren, erfolgt eine Aufforderung zur Eingabe des Passworts für Dein macOS-Benutzerkonto." } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Το Xcodes χρησιμοποιεί ένα ξεχωριστό εργαλείο με δικαιώματα ώστε να εκτελέσει εργασίες ως root. Αυτές είναι εργασίες που θα απαιτούσαν sudo στη γραμμή εντολών, συμπεριλαμβανομένων μετά-εγκαταστατικών βημάτων και αλλαγής μεταξύ εκδόσεων του Xcode με το xcode-select.\n\nΘα σας ζητηθεί το συνθηματικό του λογαριασμού σας στο macOS για την εγκατάσταση του." + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -1981,19 +2942,19 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Xcodes використовує спеціальний \"privilege helper\" щоб запускати задачі як суперюзер. Це включає наприклад sudo в терміналі, та кроки після інсталяції або перемикання версії Xcode за допомогою xcode-select.\n\nЗараз буде запит на ваш пароль від Мак щоб встановити цей хелпер." + "value" : "Xcodes використовує спеціальний \"Privileged Helper\" для запуску задач від імені root. Це включає, наприклад, sudo в терміналі, та кроки після інсталяції або перемикання версії Xcode за допомогою xcode-select.\n\nБуде запит на ваш пароль від Mac щоб інсталювати цей помічник." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "Xcodes使用一个独立的提权帮助程序来以root身份执行任务。就是那些需要在命令行中用sudo执行的命令。包括安装前置步骤以及用xcode-select切换Xcode版本。\n\n您需要提供当前用户的密码来安装它。" + "value" : "Xcodes使用一个独立的提权帮助程序来以root身份执行任务。就是那些需要在命令行中用sudo执行的命令。包括安装后准备步骤以及用xcode-select切换Xcode版本。\n\n您需要提供当前用户的密码来安装它。" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "Xcodes 使用一個分開的權限輔助程式以使用 root 身份執行特定工作。這些工作包含了通常需要在命令列使用 sudo 的指令,包含安裝後步驟以及使用 xcode-select 選擇 Xcode 版本。\n\n安裝時,你將會被詢問你的 macOS 帳號密碼。" + "value" : "Xcodes 會利用一個分開的具有權限的輔助程式以 root 身份執行特定工作,例如那些在命令列中通常需使用 sudo 的命令,包含安裝程序後的步驟以及使用 xcode-select 選擇 Xcode 版本。\n\n安裝此程式時,你將會需要輸入 macOS 帳號的密碼。" } } } @@ -2001,6 +2962,12 @@ "Alert.PrivilegedHelper.Title" : { "comment" : "Privileged Helper", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "مساعد مميز" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -2013,6 +2980,12 @@ "value" : "Privilegierter Helfer" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Βοηθητικό Εργαλείο με δικαιώματα" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -2106,7 +3079,7 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "權限輔助程式" + "value" : "具有權限的輔助程式" } } } @@ -2115,6 +3088,12 @@ "comment" : "Active/Select", "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "غير قادر على تحديد Xcode" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -2127,6 +3106,12 @@ "value" : "Auswahl von Xcode nicht möglich" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Αδυναμία επιλογής του Xcode" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -2228,6 +3213,12 @@ "Alert.SymLink.Message" : { "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xcode.app موجود وليس رابطًا رمزيًا" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -2240,6 +3231,12 @@ "value" : "Xcode.app existiert und ist kein symbolischer Link" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Το Xcode.app υπάρχει και δεν είναι συμβολικός σύνδεσμος" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -2321,7 +3318,7 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Xcode.app вже існує, і це не символічна ссилка" + "value" : "Xcode.app вже існує, і це не символічне посилання" } }, "zh-Hans" : { @@ -2333,7 +3330,7 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "Xcode.app 已經存在,但並不是一個 Symlink" + "value" : "Xcode.app 已經存在,而且不是符號連結" } } } @@ -2342,6 +3339,12 @@ "comment" : "Symbolic Links", "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "غير قادر على إنشاء رابط رمزي" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -2354,6 +3357,12 @@ "value" : "Erstellung des symbolischen Links nicht möglich" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Αδυναμία δημιουργίας συμβολικού συνδέσμου" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -2435,7 +3444,7 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Не вдалося створити символічну ссилку" + "value" : "Не вдалося створити символічне посилання" } }, "zh-Hans" : { @@ -2447,7 +3456,7 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "無法建立 Symlink" + "value" : "無法建立符號連結" } } } @@ -2455,53 +3464,137 @@ "Alert.Uninstall.Error.Message.FileNotFound" : { "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "لا يمكن ايجاد الملف \"%@\"." + } + }, + "ca" : { + "stringUnit" : { + "state" : "translated", + "value" : "Could not find file \"%@\"." + } + }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Datei \"%@\" konnte nicht gefunden werden." } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Το αρχείο «%@» δεν βρέθηκε." + } + }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Could not find file \"%@\"." } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Could not find file \"%@\"." + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Could not find file \"%@\"." + } + }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Impossible de trouver le fichier \"%@\"." } }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Could not find file \"%@\"." + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Could not find file \"%@\"." + } + }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "ファイル \"%@\" が見つかりませんでした。" } }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "\"%@\" 파일을 찾을 수 없습니다." + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Could not find file \"%@\"." + } + }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Nie znaleziono pliku \"%@\"." } }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Could not find file \"%@\"." + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Не удалось найти файл «%@»." + } + }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "\"%@\" dosyası bulunamadı." } }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Не вдалося знайти файл \"%@\"." + } + }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "无法找到文件\"%@\"." } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "找不到「%@」檔案。" + } } } }, "Alert.Uninstall.Error.Title" : { "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "غير قادر على إلغاء تثبيت Xcode" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -2514,6 +3607,12 @@ "value" : "Die Deinstallation von Xcode ist nicht möglich" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Αδυναμία απεγκατάστασης του Xcode" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -2614,6 +3713,12 @@ }, "Alert.Uninstall.Message" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "سيتم نقله إلى سلة المهملات، ولكن لن يتم إفراغه." + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -2626,6 +3731,12 @@ "value" : "Die Anwendung wird in den Papierkorb verschoben, dieser wird aber nicht geleert." } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Θα μετακινηθεί στον Κάδο, αλλά δεν θα γίνει άδειασμα." + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -2707,7 +3818,7 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Xcode буде просто переміщено до Кошика, без очищення." + "value" : "Xcode буде просто переміщено до Кошика, без повного видалення." } }, "zh-Hans" : { @@ -2719,7 +3830,7 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "它將會被移到垃圾桶,但不會被清除。" + "value" : "將移到垃圾桶,但垃圾桶不會清空。" } } } @@ -2728,6 +3839,12 @@ "comment" : "Uninstall", "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "إلغاء تثبيت Xcode %@؟" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -2740,6 +3857,12 @@ "value" : "Xcode %@ deinstallieren?" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Απεγκατάσταση του Xcode %@;" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -2842,6 +3965,12 @@ "comment" : "Update", "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "غير قادر على تحديث Xcode المحدد" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -2854,6 +3983,12 @@ "value" : "Update des ausgewählten Xcodes nicht möglich" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Αδυναμία ενημέρωσης του επιλεγμένου Xcode" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -2954,6 +4089,12 @@ }, "All" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "الجميع" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -2966,6 +4107,12 @@ "value" : "Alle" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Όλα" + } + }, "es" : { "stringUnit" : { "state" : "translated", @@ -3060,16 +4207,130 @@ }, "Apple Silicon" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "أبل السيليكون" + } + }, + "ca" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apple Silicon" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apple Silicon" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apple Silicon" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apple Silicon" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apple Silicon" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apple Silicon" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apple Silicon" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apple Silicon" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apple Silicon" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apple Silicon" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apple Silicon" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apple Silicon" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apple Silicon" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apple Silicon" + } + }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Apple Silikon" } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apple Silicon" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apple Silicon" + } } } }, "AppleID" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "ايميل ابل :" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -3082,6 +4343,12 @@ "value" : "Apple-ID:" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "AppleID:" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -3182,6 +4449,12 @@ }, "AppUpdates" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تحديثات Xcodes.app" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -3194,6 +4467,12 @@ "value" : "Xcodes.app-Updates" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ενημερώσεις του Xcodes.app" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -3292,8 +4571,139 @@ } } }, + "Authenticating" : { + "extractionState" : "manual", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "Authenticating" + } + }, + "ca" : { + "stringUnit" : { + "state" : "translated", + "value" : "Authenticating" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Authenticating" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Authenticating" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Authenticating" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Authenticating" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Authenticating" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Authenticating" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Authenticating" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Authenticating" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "認証中" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "Authenticating" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Authenticating" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Authenticating" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Authenticating" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Authenticating" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Authenticating" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Authenticating" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "Authenticating" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "Authenticating" + } + } + } + }, "AutomaticallyCreateSymbolicLink" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "إنشاء رابط رمزي إلى Xcode.app تلقائيًا" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -3306,6 +4716,12 @@ "value" : "Symbolischen Link zur Xcode.app automatisch erstellen" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Αυτόματη δημιουργία συμβολικού συνδέσμου προς το Xcode.app" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -3387,7 +4803,7 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Автоматично створювати символічну ссилку Xcode.app" + "value" : "Автоматично створювати символічне посилання Xcode.app" } }, "zh-Hans" : { @@ -3399,13 +4815,19 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "自動建立 Symlink 至 Xcode.app" + "value" : "自動建立連結至 Xcode.app 的符號連結" } } } }, "AutomaticallyCreateSymbolicLinkDescription" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "عند جعل إصدار Xcode نشطًا/محددًا، حاول إنشاء رابط رمزي باسم Xcode.app في دليل التثبيت" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -3418,6 +4840,12 @@ "value" : "Beim Umstellen einer Xcode-Version auf Aktiv/Ausgewählt versuchen einen symbolischen Link namens Xcode.app im Installationsverzeichnis zu erstellen." } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Όταν ορίζετε μια έκδοση του Xcode ως Ενεργή/Επιλεγμένη, θα γίνεται προσπάθεια δημιουργίας ενός συμβολικού συνδέσμου με το όνομα Xcode.app στον φάκελο της εγκατάστασης" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -3499,7 +4927,7 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Обираючи Акивний Xcode, спробувати створити символічну ссилку Xcode.app що вказує на обрану версію. Ссилка буде розміщена у папці інсталяції Xcode" + "value" : "Створювати символічне посилання Xcode.app що вказує на обрану версію при зміні активного Xcode. \nСимволічне посилання буде розміщено у папці інсталяції Xcode" } }, "zh-Hans" : { @@ -3511,13 +4939,19 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "當你選擇/啟用一個 Xcode 版本,自動建立一個名為 Xcode.app 的 Symlink 到該版本的安裝目錄" + "value" : "選擇或啟用一個 Xcode 版本時,會自動建立一個名為 Xcode.app 的符號連結,並指向該版本的安裝目錄" } } } }, "AutomaticInstallNewVersion" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تثبيت الإصدارات الجديدة من Xcode تلقائيًا" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -3530,6 +4964,12 @@ "value" : "Neue Versionen von Xcode automatisch installieren" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Αυτόματη εγκατάσταση νέων εκδόσεων του Xcode" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -3611,7 +5051,7 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Автоматично встановлювати нові версії Xcode" + "value" : "Автоматично інсталювати нові версії Xcode" } }, "zh-Hans" : { @@ -3631,6 +5071,12 @@ "Beta" : { "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تجربية" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -3643,6 +5089,12 @@ "value" : "Beta" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Beta" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -3743,6 +5195,12 @@ }, "BetaOnly" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تجربية فقط" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -3755,6 +5213,12 @@ "value" : "Nur Beta" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Μόνο Beta" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -3848,13 +5312,19 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "只看測試版" + "value" : "只顯示測試版" } } } }, "Cancel" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "الغاء" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -3867,6 +5337,12 @@ "value" : "Abbrechen" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ακύρωση" + } + }, "es" : { "stringUnit" : { "state" : "translated", @@ -3942,7 +5418,7 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Відміна" + "value" : "Скасувати" } }, "zh-Hans" : { @@ -3961,6 +5437,12 @@ }, "Change" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تغير" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -3973,6 +5455,12 @@ "value" : "Ändern" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Αλλαγή" + } + }, "es" : { "stringUnit" : { "state" : "translated", @@ -4060,13 +5548,19 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "變更" + "value" : "更改" } } } }, "CheckForAppUpdates" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "التحقق تلقائيًا من تحديثات التطبيق" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -4079,6 +5573,12 @@ "value" : "Automatisch auf App-Updates prüfen" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Αυτόματος έλεγχος για ενημερώσεις" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -4160,7 +5660,7 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Автоматично перевіряти наявність оновлень." + "value" : "Автоматично перевіряти наявність оновлень" } }, "zh-Hans" : { @@ -4180,6 +5680,12 @@ "CheckingSecurity" : { "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تصريح امني" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -4192,6 +5698,12 @@ "value" : "Sicherheitsprüfung" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Επαλήθευση ασφαλείας" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -4292,6 +5804,12 @@ }, "CheckNow" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "افحص الان" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -4304,6 +5822,12 @@ "value" : "Jetzt prüfen" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Έλεγχος Τώρα" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -4404,6 +5928,12 @@ }, "Close" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "اغلق" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -4416,6 +5946,12 @@ "value" : "Schließen" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Κλείσιμο" + } + }, "es" : { "stringUnit" : { "state" : "translated", @@ -4510,6 +6046,12 @@ }, "Compatibility" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "التوافق" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -4522,6 +6064,12 @@ "value" : "Kompatibilität" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Συμβατότητα" + } + }, "es" : { "stringUnit" : { "state" : "translated", @@ -4616,6 +6164,12 @@ }, "Compilers" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "المحرر" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -4628,6 +6182,12 @@ "value" : "Compiler" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Compilers" + } + }, "es" : { "stringUnit" : { "state" : "translated", @@ -4722,6 +6282,12 @@ }, "Continue" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "استمرار" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -4734,6 +6300,12 @@ "value" : "Fortfahren" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Συνέχεια" + } + }, "es" : { "stringUnit" : { "state" : "translated", @@ -4828,6 +6400,12 @@ }, "CopyPath" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "نسخ المسار" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -4840,6 +6418,12 @@ "value" : "Pfad kopieren" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Αντιγραφή διαδρομής" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -4940,6 +6524,12 @@ }, "CopyReleaseNoteURL" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "نسخ الرابط" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -4952,6 +6542,12 @@ "value" : "URL kopieren" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Αντιγραφή διεύθυνσης URL" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -4964,6 +6560,12 @@ "value" : "Copiar URL" } }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Copy URL" + } + }, "fr" : { "stringUnit" : { "state" : "translated", @@ -5006,6 +6608,12 @@ "value" : "Kopiuj adres URL" } }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Copy URL" + } + }, "ru" : { "stringUnit" : { "state" : "translated", @@ -5018,6 +6626,12 @@ "value" : "URL'yi kopyala" } }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Скопіювати URL" + } + }, "zh-Hans" : { "stringUnit" : { "state" : "translated", @@ -5027,13 +6641,19 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "複製 URL" + "value" : "拷貝 URL" } } } }, "CreateSymLink" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "قم بإنشاء رابط رمزي كـ Xcode.app" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -5046,6 +6666,12 @@ "value" : "Symlink als Xcode.app erstellen" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Δημιουργία συμβολικού συνδέσμου ως Xcode.app" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -5127,7 +6753,7 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Створити символічну ссилку як Xcode.app" + "value" : "Створити символічне посилання як Xcode.app" } }, "zh-Hans" : { @@ -5139,19 +6765,37 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "製作 Xcode-Beta.app 的 Symlink" + "value" : "製作 Xcode.app 符號連結" } } } }, "CreateSymLinkBeta" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "أنشئ رابطًا رمزيًا باسم Xcode-Beta.app" + } + }, + "ca" : { + "stringUnit" : { + "state" : "translated", + "value" : "Create Symlink as Xcode-Beta.app" + } + }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Symlink als Xcode-Beta.app erstellen" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Δημιουργία συμβολικού συνδέσμου ως Xcode-Beta.app" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -5200,12 +6844,24 @@ "value" : "Xcode-Beta.app과 같은 Symlink 만들기" } }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Create Symlink as Xcode-Beta.app" + } + }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Utwórz Symlink jako Xcode-Beta.app" } }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Create Symlink as Xcode-Beta.app" + } + }, "ru" : { "stringUnit" : { "state" : "translated", @@ -5221,7 +6877,7 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Створити символічну ссилку як Xcode-Beta.app" + "value" : "Створити символічне посилання як Xcode-Beta.app" } }, "zh-Hans" : { @@ -5229,11 +6885,23 @@ "state" : "translated", "value" : "以Xcode-Beta.app创建软链接" } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "製作 Xcode-Beta.app 符號連結" + } } } }, "DataSource" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "مصدر البيانات" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -5246,6 +6914,12 @@ "value" : "Datenquelle" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Πηγή δεδομένων" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -5327,7 +7001,7 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Джерело інформації" + "value" : "Джерело" } }, "zh-Hans" : { @@ -5347,112 +7021,124 @@ "DataSourceDescription" : { "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "يقوم مصدر بيانات Apple بإلغاء موقع Apple Developer. سيُظهر دائمًا أحدث الإصدارات المتوفرة، ولكنها أكثر هشاشة.\n\n[إصدارات Xcode](https://xcodereleases.com) هي قائمة غير رسمية لـ [إصدارات Xcode](https://xcodereleases.com). يتم تقديمها كبيانات جيدة الإعداد، وتحتوي على معلومات إضافية لا تتوفر بسهولة من Apple، ومن غير المرجح أن تتعطل إذا أعادت Apple تصميم موقع الويب الخاص بالمطورين." + } + }, "ca" : { "stringUnit" : { "state" : "translated", - "value" : "La font de dades d'Apple rebusca dins la pàgina web de desenvolupadors d'Apple. Això farà que sempre mostri les últimes versions disponibles, però també és una opció més inestable.\n\nXcode Releases es una llista no oficial de les versions d'Xcode. Està formada per dades ben composades, conté informació extra que no està disponible llegint les dades oficials d'Apple i és menys probable que es trenqui si Apple decideix redissenyar el seu portal de desenvolupadors." + "value" : "La font de dades d'Apple rebusca dins la pàgina web de desenvolupadors d'Apple. Això farà que sempre mostri les últimes versions disponibles, però també és una opció més inestable.\n\n[Xcode Releases](https://xcodereleases.com) es una llista no oficial de les versions d'Xcode. Està formada per dades ben composades, conté informació extra que no està disponible llegint les dades oficials d'Apple i és menys probable que es trenqui si Apple decideix redissenyar el seu portal de desenvolupadors." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Die Apple-Datenquelle liest die Apple Developer-Website aus. Sie zeigt immer die neuesten Releases an, die verfügbar sind, ist allerdings etwas instabiler.\n\nXcode Releases ist eine inoffizielle Liste von Xcode-Veröffentlichungen. Sie wird als formatierte Daten bereitgestellt, enthält Extrainformationen die nicht ohne weiteres von Apple erhältlich sind und ist mit höherer Wahrscheinlichkeit weiter verfügbar, sollte Apple seine Entwickler-Website neu gestalten." + "value" : "Die Apple-Datenquelle liest die Apple Developer-Website aus. Sie zeigt immer die neuesten Releases an, die verfügbar sind, ist allerdings etwas instabiler.\n\n[Xcode Releases](https://xcodereleases.com) ist eine inoffizielle Liste von Xcode-Veröffentlichungen. Sie wird als formatierte Daten bereitgestellt, enthält Extrainformationen die nicht ohne weiteres von Apple erhältlich sind und ist mit höherer Wahrscheinlichkeit weiter verfügbar, sollte Apple seine Entwickler-Website neu gestalten." + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Η πηγή δεδομένων «Apple» αντλεί δεδομένα από τον ιστότοπο «Apple Developer». Εμφανίζει πάντα τις πιο πρόσφατες διαθέσιμες εκδόσεις, αλλά είναι πιο εύθραστη ως πηγή.\n\nΗ επιλογή «[Xcode Releases](https://xcodereleases.com)» είναι μια ανεπίσημη λίστα απο εκδόσεις του Xcode. Παρέχεται ως μια καλοδιατηρημένη λίστα δεδομένων, περιέχει επιπλέον πληροφορίες οι οποίες δεν είναι άμεσα διαθέσιμες απο την Apple και έχει λιγότερες πιθανότητες να υπολειτουργήσει σε περίπτωση που η Apple επανασχεδιάσει τον ιστότοπο της." } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "The Apple data source scrapes the Apple Developer website. It will always show the latest releases that are available, but is more fragile.\n\nXcode Releases is an unofficial list of Xcode releases. It's provided as well-formed data, contains extra information that is not readily available from Apple, and is less likely to break if Apple redesigns their developer website." + "value" : "The Apple data source scrapes the Apple Developer website. It will always show the latest releases that are available, but is more fragile.\n\n[Xcode Releases](https://xcodereleases.com) is an unofficial list of [Xcode Releases](https://xcodereleases.com). It's provided as well-formed data, contains extra information that is not readily available from Apple, and is less likely to break if Apple redesigns their developer website." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "La fuente de datos de Apple la extrae de el sitio web de Apple Developer. Siempre mostrará los últimos lanzamientos disponibles, pero es más frágil.\n\nXcode Releases es una lista no oficial de lanzamientos de Xcode. Se proporciona como datos bien estructurados, contiene información adicional que no está disponible fácilmente en Apple y es menos probable que se rompa si Apple rediseña su sitio web para desarrolladores." + "value" : "La fuente de datos de Apple la extrae de el sitio web de Apple Developer. Siempre mostrará los últimos lanzamientos disponibles, pero es más frágil.\n\n[Xcode Releases](https://xcodereleases.com) es una lista no oficial de lanzamientos de Xcode. Se proporciona como datos bien estructurados, contiene información adicional que no está disponible fácilmente en Apple y es menos probable que se rompa si Apple rediseña su sitio web para desarrolladores." } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Applen tietolähde kaappaa Apple Developer -sivuston. Se näyttää aina uusimmat saatavilla olevat julkaisut, mutta se on herkempi hajoamiselle.\n\nXcode Releases on epävirallinen luettelo Xcode-julkaisuista. Se toimitetaan hyvin muotoiltuina tietoina, sisältää lisätietoa, jota ei ole helposti saatavilla Applelta, ja se ei todennäköisesti hajoa, jos Apple suunnittelee uudelleen kehittäjäsivustonsa." + "value" : "Applen tietolähde kaappaa Apple Developer -sivuston. Se näyttää aina uusimmat saatavilla olevat julkaisut, mutta se on herkempi hajoamiselle.\n\n[Xcode Releases](https://xcodereleases.com) on epävirallinen luettelo Xcode-julkaisuista. Se toimitetaan hyvin muotoiltuina tietoina, sisältää lisätietoa, jota ei ole helposti saatavilla Applelta, ja se ei todennäköisesti hajoa, jos Apple suunnittelee uudelleen kehittäjäsivustonsa." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "La source de données Apple analyse le site Web de développeurs d'Apple. Elle contient les dernières versions disponibles, mais est plus fragile.\n\nXcode Releases est une liste non officielle des versions de Xcode. Elle contient des informations supplémentaires qui ne sont pas facilement disponibles auprès d'Apple et est moins susceptible de se briser si Apple refait son site Web de développeurs." + "value" : "La source de données Apple analyse le site Web de développeurs d'Apple. Elle contient les dernières versions disponibles, mais est plus fragile.\n\n[Xcode Releases](https://xcodereleases.com) est une liste non officielle des versions de Xcode. Elle contient des informations supplémentaires qui ne sont pas facilement disponibles auprès d'Apple et est moins susceptible de se briser si Apple refait son site Web de développeurs." } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "Apple डेटा स्रोत Apple डेवलपर वेबसाइट को स्क्रैप करता है। यह हमेशा नवीनतम रिलीज दिखाएगा जो उपलब्ध हैं, लेकिन अधिक नाजुक है।\n\nXcode Releases Xcode रिलीज़ की एक अनौपचारिक सूची है। यह सुव्यवस्थित डेटा के रूप में प्रदान किया जाता है, इसमें अतिरिक्त जानकारी होती है जो Apple से आसानी से उपलब्ध नहीं होती है, और यदि Apple अपनी डेवलपर वेबसाइट को फिर से डिज़ाइन करता है तो इसके टूटने की संभावना कम होती है।" + "value" : "Apple डेटा स्रोत Apple डेवलपर वेबसाइट को स्क्रैप करता है। यह हमेशा नवीनतम रिलीज दिखाएगा जो उपलब्ध हैं, लेकिन अधिक नाजुक है।\n\n[Xcode Releases](https://xcodereleases.com) Xcode रिलीज़ की एक अनौपचारिक सूची है। यह सुव्यवस्थित डेटा के रूप में प्रदान किया जाता है, इसमें अतिरिक्त जानकारी होती है जो Apple से आसानी से उपलब्ध नहीं होती है, और यदि Apple अपनी डेवलपर वेबसाइट को फिर से डिज़ाइन करता है तो इसके टूटने की संभावना कम होती है।" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "La sorgente dati Apple controlla il sito sviluppatori Apple. Mostra sempre le ultime versioni disponibili, ma è più fragile. Xcode Releases è una lista non ufficiale di versioni di Xcode. E' disponibile con un formato ben strutturato, contiene più in formazioni che non vengono rilasciate da Apple ed è più difficile che smetta di funzionare se Apple ristruttura il suo sito sviluppatori." + "value" : "La sorgente dati Apple controlla il sito sviluppatori Apple. Mostra sempre le ultime versioni disponibili, ma è più fragile. [Xcode Releases](https://xcodereleases.com) è una lista non ufficiale di versioni di Xcode. E' disponibile con un formato ben strutturato, contiene più in formazioni che non vengono rilasciate da Apple ed è più difficile che smetta di funzionare se Apple ristruttura il suo sito sviluppatori." } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "Appleのデータソースは、Apple Developerのウェブサイトをスクレイピングしています。常に最新のリリースが表示されますが、比較的壊れやすくなっています。\n\nXcode Releasesは、非公式なXcodeのリリース一覧です。この一覧は整形されたデータとして提供されます。Appleからは簡単に入手できない特別な情報を含んでおり、AppleがDeveloper ウェブサイトを再設計しても壊れにくくなっています。" + "value" : "Appleのデータソースは、Apple Developerのウェブサイトをスクレイピングしています。常に最新のリリースが表示されますが、比較的壊れやすくなっています。\n\n[Xcode Releases](https://xcodereleases.com)は、非公式なXcodeのリリース一覧です。この一覧は整形されたデータとして提供されます。Appleからは簡単に入手できない特別な情報を含んでおり、AppleがDeveloper ウェブサイトを再設計しても壊れにくくなっています。" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "Apple 데이터 소스는 Apple Developer 웹사이트를 스크랩합니다. Apple Developer에는 항상 사용 가능한 최신 출시 버전이 표시되지만 취약한 면이 있습니다.\n\nXcode Releases는 비공식적인 Xcode 출시 버전 목록입니다. Xcode Releases에서는 정리된 데이터를 제공하며, Apple에서 쉽게 구할 수 없는 정보를 포함하며 Apple이 Apple Developer를 재설계할 경우에도 안전할 수 있습니다." + "value" : "Apple 데이터 소스는 Apple Developer 웹사이트를 스크랩합니다. Apple Developer에는 항상 사용 가능한 최신 출시 버전이 표시되지만 취약한 면이 있습니다.\n\n[Xcode Releases](https://xcodereleases.com)는 비공식적인 Xcode 출시 버전 목록입니다. [Xcode Releases](https://xcodereleases.com)에서는 정리된 데이터를 제공하며, Apple에서 쉽게 구할 수 없는 정보를 포함하며 Apple이 Apple Developer를 재설계할 경우에도 안전할 수 있습니다." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "De Apple databron scraped de Apple Developer website. Dit zal altijd de laatste nieuwe versies tonen die beschikbaar zijn, maar is meer foutgevoelig.\n\nXcode Releases is een onofficiële lijst van Xcode releases. Deze lijst wordt gepresenteerd als gevalideerde data en bevat extra informatie die niet beschikbaar is vanuit Apple. Deze databron is minder foutgevoelig en niet afhankelijk van wanneer Apple de developer website aanpast." + "value" : "De Apple databron scraped de Apple Developer website. Dit zal altijd de laatste nieuwe versies tonen die beschikbaar zijn, maar is meer foutgevoelig.\n\n[Xcode Releases](https://xcodereleases.com) is een onofficiële lijst van [Xcode Releases](https://xcodereleases.com). Deze lijst wordt gepresenteerd als gevalideerde data en bevat extra informatie die niet beschikbaar is vanuit Apple. Deze databron is minder foutgevoelig en niet afhankelijk van wanneer Apple de developer website aanpast." } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Źródło danych Apple przeszukuje stronę internetową Apple Developer. Zawsze wyświetla najnowsze dostępne wydania, ale jest bardziej podatne na problemy.\n\nXcode Releases to nieoficjalna lista wydań Xcode. Udostępnia ona dobrze sformatowane dane, zawiera dodatkowe informacje, które nie są łatwo dostępne w Apple, i jest mniej podatna na problemy, gdy Apple zmienia swoją stronę dla deweloperów." + "value" : "Źródło danych Apple przeszukuje stronę internetową Apple Developer. Zawsze wyświetla najnowsze dostępne wydania, ale jest bardziej podatne na problemy.\n\n[Xcode Releases](https://xcodereleases.com) to nieoficjalna lista wydań Xcode. Udostępnia ona dobrze sformatowane dane, zawiera dodatkowe informacje, które nie są łatwo dostępne w Apple, i jest mniej podatna na problemy, gdy Apple zmienia swoją stronę dla deweloperów." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "A fonte de dados da Apple copia o site Apple Developer. Sempre mostrará os lançamentos mais recentes que estão disponíveis, porém é mais frágil.\n\nLançamentos do Xcode (Xcode Releases) é uma lista de lançamentos do Xcode não-oficial. É provido como dado formatado, contem informação extra que não está prontamente disponível pela Apple, e é menos provável que quebre se a Apple redesenhar o seu site de desenvolvedores." + "value" : "A fonte de dados da Apple copia o site Apple Developer. Sempre mostrará os lançamentos mais recentes que estão disponíveis, porém é mais frágil.\n\nLançamentos do Xcode ([Xcode Releases](https://xcodereleases.com)) é uma lista de lançamentos do Xcode não-oficial. É provido como dado formatado, contem informação extra que não está prontamente disponível pela Apple, e é menos provável que quebre se a Apple redesenhar o seu site de desenvolvedores." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Источник данных Apple применяет технологию \"веб-скрейпинга\" к веб-сайту Apple для разработчиков. Он всегда показывает последние доступные выпуски, но является менее стабильным источником данных.\n\nXcode Releases — это неофициальный список выпусков Xcode. Он предоставляется в виде удобно структурированных данных, содержит дополнительную информацию, которую не всегда можно получить от Apple и который с меньшей вероятностью перестанет работать, если Apple изменит дизайн своего веб-сайта для разработчиков." + "value" : "Источник данных Apple применяет технологию \"веб-скрейпинга\" к веб-сайту Apple для разработчиков. Он всегда показывает последние доступные выпуски, но является менее стабильным источником данных.\n\n[Xcode Releases](https://xcodereleases.com) — это неофициальный список выпусков Xcode. Он предоставляется в виде удобно структурированных данных, содержит дополнительную информацию, которую не всегда можно получить от Apple и который с меньшей вероятностью перестанет работать, если Apple изменит дизайн своего веб-сайта для разработчиков." } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Apple veri kaynağı Apple'ın geliştirici sitesini inceliyor. Uygun olan en son sürümleri gösterir, ama biraz daha hassastır.\n\nXcode Releases, Xcode sürümlerinin bulunduğu resmi olmayan bir listedir. Data düzenli bir formata sahip bilgileri barındırır, Apple tarafında olmayan ek bilgileri içerir ve Apple'ın ileride sitesini değiştireceği için bozabileceği bir kaynak değildir." + "value" : "Apple veri kaynağı Apple'ın geliştirici sitesini inceliyor. Uygun olan en son sürümleri gösterir, ama biraz daha hassastır.\n\n[Xcode Releases](https://xcodereleases.com), Xcode sürümlerinin bulunduğu resmi olmayan bir listedir. Data düzenli bir formata sahip bilgileri barındırır, Apple tarafında olmayan ek bilgileri içerir ve Apple'ın ileride sitesini değiştireceği için bozabileceği bir kaynak değildir." } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Apple – cканування порталу Apple Developer у пошуку доступних версій Xcode. Створюючи список усих нових релізів, але це не завжи спрацьовує.\n\nXcode Releases – це не офіційний список релізів Xcode. Він являє собою відформатований список, що також має додаткову інформацію не завжди доступну напряму з сайту Apple, і менш ймовірно що він зламається якщо Apple випустить редизайн Developer Portal" + "value" : "Apple — сканування Apple Developer Portal у пошуку доступних версій Xcode. Надає список усіх нових релізів, але є менш надійним.\n\n[Xcode Releases](https://xcodereleases.com) — це неофіційний список релізів Xcode. Він є більш структурованим та надає додаткову інформацію (що не завжди доступна напряму від Apple), і менш ймовірно зламається якщо Apple оновить Developer Portal." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "Apple数据源是从Apple开发者网站爬取的。它会及时反应最新版本,但较为脆弱。\n\nXcode Releases是一个非官方的Xcode版本列表。它提供了良好格式化的数据,包含了不便从Apple直接获得的附加信息,且更不容易在Apple开发者网站无法访问时出现问题。" + "value" : "Apple数据源是从Apple开发者网站爬取的。它会及时反应最新版本,但较为脆弱。\n\n[Xcode Releases](https://xcodereleases.com)是一个非官方的Xcode版本列表。它提供了良好格式化的数据,包含了不便从Apple直接获得的附加信息,且更不容易在Apple开发者网站无法访问时出现问题。" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "Apple 資料來源是擷取 Apple 開發者網站而來,永遠會顯示最新的可用版本,但比較容易出錯。\n\nXcode Releases 是一個非官方的 Xcodes 發行版本列表。這個來源提供格式良好的資料,包含了 Apple 開發者網站上未列出的額外資訊並且即使 Apple 決定重新設計他們的開發者網站也比較不容易出錯。" + "value" : "「Apple 資料來源」會爬取 Apple 開發者網站。此資料來源較容易出錯,且只會顯示最新的可用版本。\n\n「[Xcode Releases](https://xcodereleases.com)」是一個非官方的 Xcode 發行版本清單。此資料來源提供彙整好的資料,包含了 Apple 開發者網站上未列出的額外資訊,且不容易因 Apple 網站設計變更而出錯。" } } } @@ -5461,6 +7147,12 @@ "comment" : "SMS/2FA", "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "أدخل الرمز المكون من %d من أحد أجهزتك الموثوقة:" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -5473,6 +7165,12 @@ "value" : "Gib den %d-stelligen Code von einem Deiner Vertrauensgeräte ein:" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Εισάγετε τον %d-ψήφιο κωδικό από μία από τις έμπιστες συσκευές σας:" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -5573,6 +7271,12 @@ }, "Downloader" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "أداة التنزيل" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -5585,6 +7289,12 @@ "value" : "Downloader" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Downloader" + } + }, "es" : { "stringUnit" : { "state" : "translated", @@ -5680,112 +7390,124 @@ "DownloaderDescription" : { "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "يستخدم [aria2](https://github.com/aria2/aria2) ما يصل إلى 16 اتصالًا لتنزيل Xcode بسرعة أكبر بمعدل 3 إلى 5 مرات من URLSession. تم تجميعه كملف قابل للتنفيذ مع كود المصدر الخاص به داخل Xcodes ليتوافق مع ترخيص GPLv2 الخاص به.\n\nURLSession هو واجهة برمجة تطبيقات Apple الافتراضية لتقديم طلبات URL." + } + }, "ca" : { "stringUnit" : { "state" : "translated", - "value" : "aria2 utilitza fins a 16 connexions simultànies per a descarregar l'Xcode 3-5x vegades més de pressa que URLSesson. A més de l'executable, està empaquestat amb el seu codi font dins Xcodes per complir amb la llicència GPLv2.\n\nURLSession is la opció per defecte de l'API d'Apple per a fer crides URL." + "value" : "[aria2](https://github.com/aria2/aria2) utilitza fins a 16 connexions simultànies per a descarregar l'Xcode 3-5x vegades més de pressa que URLSesson. A més de l'executable, està empaquestat amb el seu codi font dins Xcodes per complir amb la llicència GPLv2.\n\nURLSession is la opció per defecte de l'API d'Apple per a fer crides URL." } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "aria2 verwendet bis zu 16 Verbindungen, um Xcode 3-5x schneller als URLSession herunterzuladen. Es ist zusammen mit seinem Quellcode in Xcode enthalten, um seiner GPLv2-Lizenz nachzukommen.\n\nURLSession ist Apples Standard-API für URL-Requests." + "value" : "[aria2](https://github.com/aria2/aria2) verwendet bis zu 16 Verbindungen, um Xcode 3-5x schneller als URLSession herunterzuladen. Es ist zusammen mit seinem Quellcode in Xcode enthalten, um seiner GPLv2-Lizenz nachzukommen.\n\nURLSession ist Apples Standard-API für URL-Requests." + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Το [aria2](https://github.com/aria2/aria2) χρησσιμοποιεί έως και 16 συνδέσεις για να κατεβάσει το Xcode 3-5 φορές ταχύτερα απο το URLSession. Συμπεριλαμβάνεται ως εκτελέσιμο μαζί τον πηγαίο του κώδικα στο Xcodes ώστε να συμμορφώνεται με την άδεια χρήσης του GPLv2.\n\nΤο URLSession είναι το προεπιλεγμένο API για την εκτέλεση των URL requests." } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "aria2 uses up to 16 connections to download Xcode 3-5x faster than URLSession. It's bundled as an executable along with its source code within Xcodes to comply with its GPLv2 license.\n\nURLSession is the default Apple API for making URL requests." + "value" : "[aria2](https://github.com/aria2/aria2) uses up to 16 connections to download Xcode 3-5x faster than URLSession. It's bundled as an executable along with its source code within Xcodes to comply with its GPLv2 license.\n\nURLSession is the default Apple API for making URL requests." } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "aria2 usa hasta 16 conexiones para descargar Xcode de 3 a 5 veces más rápido que URLSession. Se incluye como un ejecutable junto con su código fuente dentro de Xcodes para cumplir con su licencia GPLv2.\n\nURLSession es la API predeterminada de Apple para realizar solicitudes de URL." + "value" : "[aria2](https://github.com/aria2/aria2) usa hasta 16 conexiones para descargar Xcode de 3 a 5 veces más rápido que URLSession. Se incluye como un ejecutable junto con su código fuente dentro de Xcodes para cumplir con su licencia GPLv2.\n\nURLSession es la API predeterminada de Apple para realizar solicitudes de URL." } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "aria2 käyttää jopa 16 yhteyttä ladatakseen Xcoden 3–5 kertaa nopeammin kuin URLSession. Se on niputettu suoritettavaksi tiedostoksi ja sen lähdekoodiin Xcodesissa GPLv2-lisenssin noudattamiseksi.\n\nURLSession on Applen oletussovellusliittymä URL-pyyntöjen tekemiseen.." + "value" : "[aria2](https://github.com/aria2/aria2) käyttää jopa 16 yhteyttä ladatakseen Xcoden 3–5 kertaa nopeammin kuin URLSession. Se on niputettu suoritettavaksi tiedostoksi ja sen lähdekoodiin Xcodesissa GPLv2-lisenssin noudattamiseksi.\n\nURLSession on Applen oletussovellusliittymä URL-pyyntöjen tekemiseen.." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "aria2 utilise jusqu'à 16 connexions pour télécharger Xcode de 3 à 5 fois plus rapidement que URLSession. aria2 est fourni sous forme d'exécutable avec son code source dans Xcodes pour se conformer à sa licence GPLv2.\n\nURLSession est l'API d'Apple utilisée par défaut pour effectuer des requêtes d'URL." + "value" : "[aria2](https://github.com/aria2/aria2) utilise jusqu'à 16 connexions pour télécharger Xcode de 3 à 5 fois plus rapidement que URLSession. [aria2](https://github.com/aria2/aria2) est fourni sous forme d'exécutable avec son code source dans Xcodes pour se conformer à sa licence GPLv2.\n\nURLSession est l'API d'Apple utilisée par défaut pour effectuer des requêtes d'URL." } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "aria2 URLSession की तुलना में Xcode 3-5x तेजी से डाउनलोड करने के लिए 16 कनेक्शन तक का उपयोग करता है। इसे अपने GPLv2 लाइसेंस का अनुपालन करने के लिए Xcodes के भीतर अपने स्रोत कोड के साथ एक निष्पादन योग्य के रूप में बंडल किया गया है।\n\nURL अनुरोध करने के लिए URLSession डिफ़ॉल्ट Apple API है।" + "value" : "[aria2](https://github.com/aria2/aria2) URLSession की तुलना में Xcode 3-5x तेजी से डाउनलोड करने के लिए 16 कनेक्शन तक का उपयोग करता है। इसे अपने GPLv2 लाइसेंस का अनुपालन करने के लिए Xcodes के भीतर अपने स्रोत कोड के साथ एक निष्पादन योग्य के रूप में बंडल किया गया है।\n\nURL अनुरोध करने के लिए URLSession डिफ़ॉल्ट Apple API है।" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "aria2 usa fino a 16 connessioni per scaricar Xcode da 3 a 5 volte più veloce di URLSession. E' incluso come eseguibile assieme ai suoi sogenti dentro Xcodes per rispettare la sua licenza GPLv2. \n\nURLSession è l'API di default di Apple per fare richieste a URL remote." + "value" : "[aria2](https://github.com/aria2/aria2) usa fino a 16 connessioni per scaricar Xcode da 3 a 5 volte più veloce di URLSession. E' incluso come eseguibile assieme ai suoi sogenti dentro Xcodes per rispettare la sua licenza GPLv2. \n\nURLSession è l'API di default di Apple per fare richieste a URL remote." } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "aria2 は、最大16個の接続を使用して、Xcode を URLSession の3-5 倍のスピードでダウンロードします。GPLv2 ライセンスに準拠するため、Xcodes 内にソースコードと一緒に実行ファイルとしてバンドルされています。\n\nURLSession は、URLリクエストを行うための Apple のデフォルト API です。" + "value" : "[aria2](https://github.com/aria2/aria2) は、最大16個の接続を使用して、Xcode を URLSession の3-5 倍のスピードでダウンロードします。GPLv2 ライセンスに準拠するため、Xcodes 内にソースコードと一緒に実行ファイルとしてバンドルされています。\n\nURLSession は、URLリクエストを行うための Apple のデフォルト API です。" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "aria2는 최대 16 개의 연결을 사용하여 URLSession보다 3~5배 더 빠르게 Xcode를 다운로드합니다. GPLv2 라이센스를 준수하기 위해 Xcodes 내 소스 코드와 함께 실행 파일 번들로 제공됩니다.\n\nURLSession은 URL 요청을 만들기 위한 기본 Apple API입니다." + "value" : "[aria2](https://github.com/aria2/aria2)는 최대 16 개의 연결을 사용하여 URLSession보다 3~5배 더 빠르게 Xcode를 다운로드합니다. GPLv2 라이센스를 준수하기 위해 Xcodes 내 소스 코드와 함께 실행 파일 번들로 제공됩니다.\n\nURLSession은 URL 요청을 만들기 위한 기본 Apple API입니다." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "aria2 gebruikt tot 16 verbindingen om Xcode 3-5x sneller te downloaden dan met URLSession. Het is gebundeld als een executable samen met de broncode binnen Xcodes om te voldoen aan de GPLv2 licentie.\n\nURLSession is de standaard Apple API voor het maken van URL verzoeken." + "value" : "[aria2](https://github.com/aria2/aria2) gebruikt tot 16 verbindingen om Xcode 3-5x sneller te downloaden dan met URLSession. Het is gebundeld als een executable samen met de broncode binnen Xcodes om te voldoen aan de GPLv2 licentie.\n\nURLSession is de standaard Apple API voor het maken van URL verzoeken." } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "aria2 używa do pobierania do 16 połączeń, co pozwala na pobieranie Xcode 3-5x szybciej niż URLSession. Jest on dostarczany jako plik wykonywalny wraz ze swoim kodem źródłowym w ramach Xcodes w celu przestrzegania jego licencji GPLv2.\n\nURLSession to domyślne API Apple do tworzenia żądań URL." + "value" : "[aria2](https://github.com/aria2/aria2) używa do pobierania do 16 połączeń, co pozwala na pobieranie Xcode 3-5x szybciej niż URLSession. Jest on dostarczany jako plik wykonywalny wraz ze swoim kodem źródłowym w ramach Xcodes w celu przestrzegania jego licencji GPLv2.\n\nURLSession to domyślne API Apple do tworzenia żądań URL." } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "aria2 usa até 16 conexões para baixar o Xcode 3-5x mais rápido que URLSession. Está empacotado como um executável junto com o seu código fonte dentro do Xcodes para conformar com a licensa GPLv2.\n\nURLSession é a API padrão da Apple para performar requisições URL." + "value" : "[aria2](https://github.com/aria2/aria2) usa até 16 conexões para baixar o Xcode 3-5x mais rápido que URLSession. Está empacotado como um executável junto com o seu código fonte dentro do Xcodes para conformar com a licensa GPLv2.\n\nURLSession é a API padrão da Apple para performar requisições URL." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "aria2 использует до 16 подключений для загрузки Xcode в 3-5 раз быстрее, чем URLSession. Он поставляется в виде исполняемого файла вместе с исходным кодом в Xcodes, чтобы соответствовать лицензии GPLv2.\n\nURLSession — это API Apple по умолчанию для выполнения запросов по сети." + "value" : "[aria2](https://github.com/aria2/aria2) использует до 16 подключений для загрузки Xcode в 3-5 раз быстрее, чем URLSession. Он поставляется в виде исполняемого файла вместе с исходным кодом в Xcodes, чтобы соответствовать лицензии GPLv2.\n\nURLSession — это API Apple по умолчанию для выполнения запросов по сети." } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "aria2 16ya kadar bağlantı kullanarak Xcode'u URLSession'a göre 3-5 kat daha hızlı indirir. Çalıştırılabilir bir dosya olarak Bundle'a dahildir ve kaynak dosyaları GPLv2 lisansıyla uyumlu olması açısından Xcode içerisinde yer alır.\n\nURLSession Apple'ın URL istekleri oluşturmak için sunduğu varsayılan API'dır." + "value" : "[aria2](https://github.com/aria2/aria2) 16ya kadar bağlantı kullanarak Xcode'u URLSession'a göre 3-5 kat daha hızlı indirir. Çalıştırılabilir bir dosya olarak Bundle'a dahildir ve kaynak dosyaları GPLv2 lisansıyla uyumlu olması açısından Xcode içerisinde yer alır.\n\nURLSession Apple'ın URL istekleri oluşturmak için sunduğu varsayılan API'dır." } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "aria2 може використовувати до 16 з'єднань, завантажуючи Xcode у 3-5 разів швидше ніж URLSession. Вона поставляється у вигляді бінарника та коду, відповідно до вимог її GPLv2 ліцензії.\n\nURLSession – це завантажувач по замовчуванню від Apple" + "value" : "[aria2](https://github.com/aria2/aria2) може використовувати до 16 з'єднань, завантажуючи Xcode у 3-5 разів швидше ніж URLSession. Вона поставляється у вигляді бінарника та коду, відповідно до вимог її GPLv2 ліцензії.\n\nURLSession — це завантажувач за-умовчанням від Apple" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "aria2使用最高16线程,能提供3至5倍的下载速度。它与它的源代码被一同打包在Xcodes中以符合GPLv2授权。\n\nURLSession是用于发起URL请求的默认Apple API。" + "value" : "[aria2](https://github.com/aria2/aria2)使用最高16线程,能提供3至5倍的下载速度。它与它的源代码被一同打包在Xcodes中以符合GPLv2授权。\n\nURLSession是用于发起URL请求的默认Apple API。" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "aria2 相較 URLSession 可以同時使用最多 16 條連線以 3 ~ 5 倍的速度下載 Xcode。Xcodes 包含了執行檔與其原始碼以遵循他的 GPLv2 授權合約。\n\nURLSession 是系統內建用來發送 URL 連線請求的 Apple API。" + "value" : "[aria2](https://github.com/aria2/aria2) 可利用多達 16 條連線來下載 Xcode,相較使用 URLSession 快達 3~5 倍。Xcodes 的套件內容中包含了 [aria2](https://github.com/aria2/aria2) 執行檔及其原始碼,以遵循其使用的 GPLv2 授權條款。\n\nURLSession 是系統內建用於發起 URL 連線請求的 Apple API。" } } } @@ -5794,6 +7516,12 @@ "comment" : "Installation Steps", "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "Downloading" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -5806,6 +7534,12 @@ "value" : "Herunterladen" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Λήψη" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -5887,7 +7621,7 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Скачування" + "value" : "Викачування" } }, "zh-Hans" : { @@ -5907,6 +7641,12 @@ "DownloadingError" : { "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "لم يتم العثور على معلومات التنزيل" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -5919,6 +7659,12 @@ "value" : "Keine Download-Informationen gefunden" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Δεν βρέθηκαν πληροφορίες λήψης" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -6000,7 +7746,7 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Не знайдено інформації для закачування" + "value" : "Не знайдено інформації для викачування" } }, "zh-Hans" : { @@ -6020,6 +7766,12 @@ "DownloadingPercentDescription" : { "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "التنزيل: اكتمل %d%%" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -6032,6 +7784,12 @@ "value" : "Download: %d%% vollständig" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Λήψη: %d%% ολοκλήρωση" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -6113,7 +7871,7 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Закачка: %d%% готово" + "value" : "Викачано: %d%%" } }, "zh-Hans" : { @@ -6133,12 +7891,30 @@ "Downloads" : { "comment" : "Download Preference Pane", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "التحميلات" + } + }, + "ca" : { + "stringUnit" : { + "state" : "translated", + "value" : "Downloads" + } + }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Downloads" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Λήψεις" + } + }, "es" : { "stringUnit" : { "state" : "translated", @@ -6181,12 +7957,24 @@ "value" : "다운로드" } }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Downloads" + } + }, "pl" : { "stringUnit" : { "state" : "translated", "value" : "Pobieranie" } }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Downloads" + } + }, "ru" : { "stringUnit" : { "state" : "translated", @@ -6221,6 +8009,12 @@ }, "DownloadSize" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "حجم التنزيل" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -6233,6 +8027,12 @@ "value" : "Download-Größe" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Μέγεθος λήψης" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -6333,6 +8133,12 @@ }, "EnableNotifications" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تمكين الإخطارات" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -6345,6 +8151,12 @@ "value" : "Benachrichtigungen einschalten" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ενεργοποίηση Γνωστοποιήσεων" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -6446,6 +8258,12 @@ "EnterDigitCodeDescription" : { "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "أدخل الرمز المكون من %1$d الذي تم إرساله إلى %2$@:" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -6458,6 +8276,12 @@ "value" : "Gib den %1$d-stelligen Code ein der an '%2$@' gesendet wurde." } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Εισάγετε τον %1$d-ψήφιο κωδικού που εστάλη στο %2$@:" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -6559,26 +8383,124 @@ "Error" : { "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "خطأ" + } + }, + "ca" : { + "stringUnit" : { + "state" : "translated", + "value" : "Error" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Error" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Σφάλμα" + } + }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Error" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Error" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Error" + } + }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Erreur" } - } - } - }, - "example@icloud.com" : { - "localizations" : { + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Error" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Error" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "Error" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "오류" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Error" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Error" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Error" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ошибка" + } + }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "ornek@icloud.com" + "value" : "Error" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Помилка" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "错误" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "錯誤" } } } @@ -6586,6 +8508,12 @@ "Experiments" : { "comment" : "Experiment Preference Pane", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "التجارب" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -6598,6 +8526,12 @@ "value" : "Experimente" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Πειράματα" + } + }, "es" : { "stringUnit" : { "state" : "translated", @@ -6685,13 +8619,19 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "實驗" + "value" : "實驗性功能" } } } }, "FasterUnxip" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "فك الضغط بشكل أسرع" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -6704,6 +8644,12 @@ "value" : "Schnellerer Unxip" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ταχύτερο Unxip" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -6805,118 +8751,136 @@ "FasterUnxipDescription" : { "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "بفضل [@_saagarjha](https://twitter.com/_saagarjha)، يمكن لهذه التجربة زيادة سرعة فك الضغط بنسبة تصل إلى 70% لبعض الأنظمة.\n\nيمكن الاطلاع على مزيد من المعلومات حول كيفية تحقيق ذلك في unxip repo - https://github.com/saagarjha/unxip" + } + }, "ca" : { "stringUnit" : { "state" : "translated", - "value" : "Gràcies a @_saagarjha, aquest experiment pot incrementar la velocitat de l'unxip en un 70% en alguns sistemes.\n\nMés informació sobre com s'aconsegueix pot ser consultada en el seu repositori - https://github.com/saagarjha/unxip" + "value" : "Gràcies a [@_saagarjha](https://twitter.com/_saagarjha), aquest experiment pot incrementar la velocitat de l'unxip en un 70% en alguns sistemes.\n\nMés informació sobre com s'aconsegueix pot ser consultada en el seu repositori - https://github.com/saagarjha/unxip" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Dank an @_saagarjha, dieses Experiment kann die Unxipping-Geschwindigkeit bis zu 70% auf einigen Systemen beschleunigen.\n\nMehr Informationen wie dies erreicht wird sind über das Unxip-Repo erhältlich - https://github.com/saagarjha/unxip" + "value" : "Dank an [@_saagarjha](https://twitter.com/_saagarjha), dieses Experiment kann die Unxipping-Geschwindigkeit bis zu 70% auf einigen Systemen beschleunigen.\n\nMehr Informationen wie dies erreicht wird sind über das Unxip-Repo erhältlich - https://github.com/saagarjha/unxip" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Χάριν στο χρήστη [@_saagarjha](https://twitter.com/_saagarjha), αυτή η πειραματική λειτουργία μπορεί να αυξήσει την ταχύτητα αποσυμπίεσης (unxipping) έως και 70% σε κάποια συστήματα.\n\nΓια περισσότερες πληροφορίες σχετικά με το πως επιτυγχάνεται αυτό μπορείτε να επισκεφθείτε το unxip repo - https://github.com/saagarjha/unxip" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Thanks to @_saagarjha, this experiment can increase unxipping speed by up to 70% for some systems.\n\nMore information on how this is accomplished can be seen on the unxip repo - https://github.com/saagarjha/unxip" + "value" : "Thanks to [@_saagarjha](https://twitter.com/_saagarjha), this experiment can increase unxipping speed by up to 70% for some systems.\n\nMore information on how this is accomplished can be seen on the unxip repo - https://github.com/saagarjha/unxip" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Gracias a @_saagarjha, este experimento puede aumentar la velocidad de liberación hasta en un 70 % para algunos sistemas.\n\nPuede ver más información sobre cómo se logra esto en el repositorio de unxip: https://github.com/saagarjha/unxip" + "value" : "Gracias a [@_saagarjha](https://twitter.com/_saagarjha), este experimento puede aumentar la velocidad de liberación hasta en un 70 % para algunos sistemas.\n\nPuede ver más información sobre cómo se logra esto en el repositorio de unxip: https://github.com/saagarjha/unxip" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "@_saagarjhan ansiosta tämä kokeilu voi nostaa purkamisnopeutta jopa 70 % joissakin järjestelmissä.\n\nLisätietoja siitä, miten tämä tehdään, on unxip-repossa - https://github.com/saagarjha/unxip" + "value" : "[@_saagarjha](https://twitter.com/_saagarjha)n ansiosta tämä kokeilu voi nostaa purkamisnopeutta jopa 70 % joissakin järjestelmissä.\n\nLisätietoja siitä, miten tämä tehdään, on unxip-repossa - https://github.com/saagarjha/unxip" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Grâce à @_saagarjha, cette expérimentation peut augmenter jusqu'à 70% la vitesse de décompression pour certains systèmes.\n\nPour plus d'informations sur la façon dont cela est accompli, consultez le dépôt GitHub unxip - https://github.com/saagarjha/unxip" + "value" : "Grâce à [@_saagarjha](https://twitter.com/_saagarjha), cette expérimentation peut augmenter jusqu'à 70% la vitesse de décompression pour certains systèmes.\n\nPour plus d'informations sur la façon dont cela est accompli, consultez le dépôt GitHub unxip - https://github.com/saagarjha/unxip" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "@_saagarjha के लिए धन्यवाद, यह प्रयोग कुछ प्रणालियों के लिए 70% तक की गति को बढ़ा सकता है।\n\nइसे कैसे पूरा किया जाता है, इस बारे में अधिक जानकारी अनएक्सआईपी रेपो पर देखी जा सकती है - https://github.com/saagarjha/unxip" + "value" : "[@_saagarjha](https://twitter.com/_saagarjha) के लिए धन्यवाद, यह प्रयोग कुछ प्रणालियों के लिए 70% तक की गति को बढ़ा सकता है।\n\nइसे कैसे पूरा किया जाता है, इस बारे में अधिक जानकारी अनएक्सआईपी रेपो पर देखी जा सकती है - https://github.com/saagarjha/unxip" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Grazie a @_saagarjha, questa versione sperimentale può incrementare la velocità di unxipping fino al 70% in certi sistemi.\n\nMaggiori informazioni su come questo è possibile sono disponibili nel repository unxip - https://github.com/saagarjha/unxip" + "value" : "Grazie a [@_saagarjha](https://twitter.com/_saagarjha), questa versione sperimentale può incrementare la velocità di unxipping fino al 70% in certi sistemi.\n\nMaggiori informazioni su come questo è possibile sono disponibili nel repository unxip - https://github.com/saagarjha/unxip" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "@_saagarjha さんの方法で、一部のシステムで Unxip の速度が最大70%向上します。\n\n方法の詳細については、 unxipのリポジトリ (https://github.com/saagarjha/unxip)をご覧ください。" + "value" : "[@_saagarjha](https://twitter.com/_saagarjha) さんの方法で、一部のシステムで Unxip の速度が最大70%向上します。\n\n方法の詳細については、 unxipのリポジトリ (https://github.com/saagarjha/unxip)をご覧ください。" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "@_saagarjha님 덕분에 이 실험 기능을 이용하면 일부 시스템에서 압축 해제 속도를 최대 70%까지 향상시킬 수 있습니다.\n\n이를 수행하는 방법에 대한 자세한 내용은 unxip 저장소 (https://github.com/saagarjha/unxip)에서 확인할 수 있습니다." + "value" : "[@_saagarjha](https://twitter.com/_saagarjha)님 덕분에 이 실험 기능을 이용하면 일부 시스템에서 압축 해제 속도를 최대 70%까지 향상시킬 수 있습니다.\n\n이를 수행하는 방법에 대한 자세한 내용은 unxip 저장소 (https://github.com/saagarjha/unxip)에서 확인할 수 있습니다." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Dankzij @_saagarjha, kan met dit experiment het uitpakken van Xcode tot 70% sneller gaan op sommige systemen.\n\nMeer informatie over hoe dit mogelijk wordt gemaakt kan worden gevonden op de unxip repo - https://github.com/saagarjha/unxip" + "value" : "Dankzij [@_saagarjha](https://twitter.com/_saagarjha), kan met dit experiment het uitpakken van Xcode tot 70% sneller gaan op sommige systemen.\n\nMeer informatie over hoe dit mogelijk wordt gemaakt kan worden gevonden op de unxip repo - https://github.com/saagarjha/unxip" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Dzięki @_saagarjha, wersja eksperymentalna może zwiększyć prędkość rozpakowywania o nawet 70% dla niektórych systemów.\n\nWięcej informacji na temat tego, jak to jest osiągane, można znaleźć w repozytorium unxip - https://github.com/saagarjha/unxip" + "value" : "Dzięki [@_saagarjha](https://twitter.com/_saagarjha), wersja eksperymentalna może zwiększyć prędkość rozpakowywania o nawet 70% dla niektórych systemów.\n\nWięcej informacji na temat tego, jak to jest osiągane, można znaleźć w repozytorium unxip - https://github.com/saagarjha/unxip" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Graças à @_saagarjha, esse experimento pode acelerar o unxip em até 70% para algum sistemas.\n\nMais informações sobre como isso ocorre pode ser encontrada no repositório do unxip - https://github.com/saagarjha/unxip" + "value" : "Graças à [@_saagarjha](https://twitter.com/_saagarjha), esse experimento pode acelerar o unxip em até 70% para algum sistemas.\n\nMais informações sobre como isso ocorre pode ser encontrada no repositório do unxip - https://github.com/saagarjha/unxip" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Благодаря @_saagarjha этот эксперимент может увеличить скорость распаковки до 70% на некоторых системах.\n\nДополнительную информацию о том, как достигается такой результат, можно прочесть в репозитории unxip — https://github.com/saagarjha/unxip. " + "value" : "Благодаря [@_saagarjha](https://twitter.com/_saagarjha) этот эксперимент может увеличить скорость распаковки до 70% на некоторых системах.\n\nДополнительную информацию о том, как достигается такой результат, можно прочесть в репозитории unxip — https://github.com/saagarjha/unxip. " } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "@_saagarjha sayesinde bu deney bazı sistemlerde unxip sürecini %70'e kadar hızlandırabilir.\n\nBu işlemin nasıl yapıldığına dair ayrıntılara unxip repo .- https://github.com/saagarjha/unxip adresinden bakabilirsiniz" + "value" : "[@_saagarjha](https://twitter.com/_saagarjha) sayesinde bu deney bazı sistemlerde unxip sürecini %70'e kadar hızlandırabilir.\n\nBu işlemin nasıl yapıldığına dair ayrıntılara unxip repo .- https://github.com/saagarjha/unxip adresinden bakabilirsiniz" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Завдяки @_saagarjha, цей експеримент може пришвидшити розпаковку майже на 70%. Подробиці про unxip тут – https://github.com/saagarjha/unxip" + "value" : "Завдяки [@_saagarjha](https://twitter.com/_saagarjha), цей експеримент може пришвидшити розпаковку майже на 70%. \n\nПодробиці про unxip тут — https://github.com/saagarjha/unxip" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "感谢@_saagarjha,此实验性功能可在部分系统上将xip解压速度提高70%。\n\n请从此仓库获取关于其实现细节的信息:https://github.com/saagarjha/unxip" + "value" : "感谢[@_saagarjha](https://twitter.com/_saagarjha),此实验性功能可在部分系统上将xip解压速度提高70%。\n\n请从此仓库获取关于其实现细节的信息:https://github.com/saagarjha/unxip" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "感謝 @_saagarjha 的努力,這個試驗版本可以在某些系統上加快 70% 的解壓縮速度。\n\n更多關於這項成就是如何達成的資訊,請參閱 unxip 的 repo - https://github.com/saagarjha/unxip" + "value" : "感謝 [@_saagarjha](https://twitter.com/_saagarjha) 的努力,這個試驗版本可以在某些系統上加快 70% 的解壓縮速度。\n\n更多關於這項成就是如何達成的資訊,請參閱 unxip 的 repo - https://github.com/saagarjha/unxip" } } } }, "Filter" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تصفيه" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -6929,6 +8893,12 @@ "value" : "Filter" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Φίλτρο" + } + }, "es" : { "stringUnit" : { "state" : "translated", @@ -7023,6 +8993,12 @@ }, "FilterAvailableDescription" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تصفية الإصدارات المتاحة" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -7035,6 +9011,12 @@ "value" : "Verfügbare Versionen filtern" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Φιλτράρισμα διαθέσιμων εκδόσεων" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -7135,6 +9117,12 @@ }, "FilterInstalledDescription" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تصفية الإصدارات المثبتة" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -7147,6 +9135,12 @@ "value" : "Installierte Versionen filtern" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Φιλτράρισμα εγκατεστημένων εκδόσεων" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -7228,7 +9222,7 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Фільтрувати встановлені версії" + "value" : "Фільтрувати інстальовані версії" } }, "zh-Hans" : { @@ -7248,6 +9242,12 @@ "Finishing" : { "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "الانتهاء" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -7260,6 +9260,12 @@ "value" : "Abschließen" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Γίνεται ολοκλήρωση" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -7353,7 +9359,7 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "收尾中" + "value" : "即將完成" } } } @@ -7361,6 +9367,12 @@ "General" : { "comment" : "General Preference Pane", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "عام" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -7373,6 +9385,12 @@ "value" : "Allgemein" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Γενικές" + } + }, "es" : { "stringUnit" : { "state" : "translated", @@ -7448,7 +9466,7 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Основне" + "value" : "Загальне" } }, "zh-Hans" : { @@ -7467,6 +9485,12 @@ }, "GithubRepo" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "مستودع GitHub" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -7479,6 +9503,12 @@ "value" : "GitHub-Repo" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "GitHub Repo" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -7580,6 +9610,12 @@ "HelperClient.error" : { "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "غير قادر على التواصل مع المساعد المميز." + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -7592,6 +9628,12 @@ "value" : "Kommunikation mit privilegiertem Helfer nicht möglich." } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Αδυναμία επικοινωνίας με το βοηθητικό εργαλείο." + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -7673,7 +9715,7 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Помилка з'єднання з \"privileged helper\"." + "value" : "Помилка комунікації з \"Privileged Helper\"." } }, "zh-Hans" : { @@ -7685,13 +9727,19 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "無法與權限輔助程式溝通。" + "value" : "無法與具有權限的輔助程式通訊。" } } } }, "HelperInstalled" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تم تثبيت المساعد" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -7704,6 +9752,12 @@ "value" : "Helfer ist installiert" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Το βοηθητικό εργαλείο δεν είναι εγκατεστημένο" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -7785,7 +9839,7 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Helper встановлено" + "value" : "Helper інстальовано" } }, "zh-Hans" : { @@ -7797,13 +9851,19 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "輔助程式已安裝" + "value" : "已安裝輔助程式" } } } }, "HelperNotInstalled" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "لم يتم تثبيت المساعد" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -7816,6 +9876,12 @@ "value" : "Helfer ist nicht installiert" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Το βοηθητικό εργαλείο δεν είναι εγκατεστημένο" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -7897,7 +9963,7 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Helper не встановлено" + "value" : "Helper не інстальовано" } }, "zh-Hans" : { @@ -7909,23 +9975,7 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "輔助程式尚未安裝" - } - } - } - }, - "Hidden" : { - "localizations" : { - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Masqué" - } - }, - "tr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Gizli" + "value" : "尚未安裝輔助程式" } } } @@ -7933,6 +9983,12 @@ "IdenticalBuilds" : { "comment" : "Info Pane", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "بنيات متطابقة" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -7945,6 +10001,12 @@ "value" : "Identische Builds" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Πανομοιότυπες Εκδόσεις" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -8045,6 +10107,12 @@ }, "IdenticalBuilds.help" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "في بعض الأحيان يكون الإصدار التجريبي وإصدار الإصدار هو نفس الإصدار تمامًا. ستعرض Xcodes هذه الإصدارات معًا تلقائيًا." + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -8057,6 +10125,12 @@ "value" : "Manchmal sind Prerelease- and Release-Version der exakt gleich Build. Xcodes zeigt diese beiden Versionen automatisch zusammen an." } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Κάποιες φορές οι προεκδόσεις και εκδόσεις κυκλοφορίας είναι ακριβώς ίδιες. Το Xcodes θα εμφανίσει αυτόματα αυτές τις εκδόσεις μαζί." + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -8138,7 +10212,7 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Буває що prerelease та release насправді ідентичні (то й же Build). У такому разі Xcodes будуть показувати обидві версії разом" + "value" : "Буває що RC та Release насправді ідентичні (однаковий Build). У такому випадку Xcodes буде показувати обидві версії разом." } }, "zh-Hans" : { @@ -8150,13 +10224,19 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "有時候預先發行版與正式版的建制版本號會相同,Xcodes 會自動將這些版本整理在一起。" + "value" : "有時候預先發行版與正式版的建置版本號會相同,Xcodes 會自動將這些版本整理在一起。" } } } }, "IncludePreRelease" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تضمين الإصدارات التجريبية/التجريبية" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -8169,6 +10249,12 @@ "value" : "Prerelease-/Beta-Versionen einschließen" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Συμπερίληψη προεκδόσεων/beta" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -8250,7 +10336,7 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Також встановлювати prerelease/beta версії" + "value" : "Також інсталювати RC / Beta версії" } }, "zh-Hans" : { @@ -8267,235 +10353,15 @@ } } }, - "Info" : { - "extractionState" : "stale", - "localizations" : { - "ca" : { - "stringUnit" : { - "state" : "translated", - "value" : "Informació" - } - }, - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Info" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Info" - } - }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Info" - } - }, - "fi" : { - "stringUnit" : { - "state" : "translated", - "value" : "Tietoja" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Informations" - } - }, - "hi" : { - "stringUnit" : { - "state" : "translated", - "value" : "जानकारी" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Info" - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "情報" - } - }, - "ko" : { - "stringUnit" : { - "state" : "translated", - "value" : "정보" - } - }, - "nl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Info" - } - }, - "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Informacje" - } - }, - "pt-BR" : { - "stringUnit" : { - "state" : "translated", - "value" : "Informação" - } - }, - "ru" : { - "stringUnit" : { - "state" : "translated", - "value" : "Информация" - } - }, - "tr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Bilgi" - } - }, - "uk" : { - "stringUnit" : { - "state" : "translated", - "value" : "Info" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "信息" - } - }, - "zh-Hant" : { - "stringUnit" : { - "state" : "translated", - "value" : "資訊" - } - } - } - }, - "InfoDescription" : { - "extractionState" : "stale", - "localizations" : { - "ca" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mostrar o ocultar el panell d'informació" - } - }, - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Info-Panel anzeigen oder verbergen" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Show or hide the info pane" - } - }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mostrar u ocultar el panel de información" - } - }, - "fi" : { - "stringUnit" : { - "state" : "translated", - "value" : "Näytä tai piilota tietoruutu" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Afficher ou masquer le volet d'informations" - } - }, - "hi" : { - "stringUnit" : { - "state" : "translated", - "value" : "जानकारी फलक दिखाएँ या छिपाएँ" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mostra o nascondi pannello informazioni" - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "情報パネルの切り替え" - } - }, - "ko" : { - "stringUnit" : { - "state" : "translated", - "value" : "정보창 표시 또는 숨기기" - } - }, - "nl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Show or hide the info pane" - } - }, - "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Pokaż lub ukryj okno informacji" - } - }, - "pt-BR" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mostrar ou esconder o painel de informações" - } - }, - "ru" : { - "stringUnit" : { - "state" : "translated", - "value" : "Показать или скрыть информационную панель" - } - }, - "tr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Bilgi panelini göster ya da gizle" - } - }, - "uk" : { - "stringUnit" : { - "state" : "translated", - "value" : "Показати або сховати панель інформації" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "显示或隐藏信息面板" - } - }, - "zh-Hant" : { - "stringUnit" : { - "state" : "translated", - "value" : "顯示或隱藏資訊面板" - } - } - } - }, "Install" : { "comment" : "Common", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تثبيت" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -8508,6 +10374,12 @@ "value" : "Installieren" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Εγκατάσταση" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -8589,7 +10461,7 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Встановити" + "value" : "Інсталювати" } }, "zh-Hans" : { @@ -8606,25 +10478,15 @@ } } }, - "INSTALL" : { - "localizations" : { - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "INSTALLER" - } - }, - "tr" : { - "stringUnit" : { - "state" : "translated", - "value" : "YÜKLE" - } - } - } - }, "InstallationError.CodesignVerifyFailed" : { "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "فشل التحقق من توقيع رمز Xcode الذي تم تنزيله بالإخراج التالي:\n%@" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -8637,6 +10499,12 @@ "value" : "Die heruntergeladene Version von Xcode hat die Code-Signing-Prüfung nicht bestanden, mit folgendem Hinweis:\n%@" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Αποτυχία επαλήθευσης ψηφιακής υπογραφής για το ληφθέν Xcode: \n%@" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -8718,7 +10586,7 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Викачаний Xcode не зміг пройти перевірку коду підпису (code signing verification) з наступним повідомленням:\n%@" + "value" : "Викачаний Xcode не зміг пройти перевірку підпису (code signing verification) з наступним повідомленням:\n%@" } }, "zh-Hans" : { @@ -8739,6 +10607,12 @@ "comment" : "InstallationErrors", "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "الأرشيف \"%@\" تالف ولا يمكن توسيعه." + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -8751,6 +10625,12 @@ "value" : "Das Archiv \"%@\" ist beschädigt und kann nicht entpackt werden." } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Το αρχείο αρχειοθέτησης «%@» είναι κατεστραμμένο και δεν μπορεί να αποσυμπιεστεί." + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -8832,7 +10712,7 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Архів \"%@\" пошкожено і не можливо розпакувати." + "value" : "Архів \"%@\" пошкоджено, його не можливо розпакувати." } }, "zh-Hans" : { @@ -8844,7 +10724,7 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "壓縮檔 \"%@\" 已經損壞並無法解壓縮。" + "value" : "壓縮檔「%@」已經損壞並無法解壓縮。" } } } @@ -8852,6 +10732,12 @@ "InstallationError.FailedSecurityAssessment" : { "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "فشل Xcode %@ في تقييم الأمان الخاص به بالنتيجة التالية:\n%@\nويظل مثبتًا في %@ إذا كنت ترغب في استخدامه على أي حال." + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -8864,6 +10750,12 @@ "value" : "Die Sicherheitsprüfung für Xcode %@ ist mit folgender Meldung gescheitert:\n%@\nXcode bleibt unter %@ installiert, für den Fall, dass es dennoch verwendet werden soll." } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Το Xcode %@ απέτυχε κατά την αξιολόγηση ασφαλείας του με το ακόλουθο μήνυμα:\n%@\nΠαραμένει εγκατεστημένο στο %@ αν θέλετε να το χρησιμοποιήσετε παρ' όλα αυτά." + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -8945,7 +10837,7 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Xcode провалив перевірку безпеки (security assessment) з наступним повідомленням:\n%@\nВін залишиться установлений в %@ якщо ви все ж наважитесь його використовувати." + "value" : "Xcode не пройшов перевірку безпеки (security assessment) з наступним повідомленням:\n%@\n\nВін залишиться інстальованим в %@, на випадок якщо ви забажаєте його використовувати." } }, "zh-Hans" : { @@ -8965,6 +10857,12 @@ "InstallationError.FailedToMoveXcodeToApplications" : { "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "فشل في نقل Xcode إلى الدليل %@." + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -8977,6 +10875,12 @@ "value" : "Das Bewegen von Xcode in das %@-Verzeichnis ist nicht möglich." } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Αδυναμία μετακίνησης του Xcode στον φάκελο %@." + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -9058,7 +10962,7 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Помилка при переміщенні Xcode в %@." + "value" : "Помилка при переміщенні Xcode до %@." } }, "zh-Hans" : { @@ -9078,6 +10982,12 @@ "InstallationError.InvalidVersion" : { "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ ليس رقم إصدار صالح." + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -9090,6 +11000,12 @@ "value" : "%@ ist keine gültige Versionsnummer." } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Η έκδοση %@ δεν είναι έγκυρη." + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -9171,7 +11087,7 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "%@ не є вірним номером версії." + "value" : "%@ не є коректним номером версії." } }, "zh-Hans" : { @@ -9191,6 +11107,12 @@ "InstallationError.MissingSudoerPassword" : { "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "كلمة المرور مفقودة. حاول مرة اخرى." + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -9203,6 +11125,12 @@ "value" : "Passwort fehlt. Bitte erneut versuchen." } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Λείπει το συνθηματικό. Δοκιμάστε ξανά." + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -9284,7 +11212,7 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Не вистачає sudo паролю. Спробуйте ще раз." + "value" : "Не вистачає паролю. Спробуйте ще раз." } }, "zh-Hans" : { @@ -9304,6 +11232,12 @@ "InstallationError.MissingUsernameOrPassword" : { "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "اسم المستخدم أو كلمة المرور مفقودة. حاول مرة اخرى." + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -9316,6 +11250,12 @@ "value" : "Benutzername oder ein Passwort fehlt. Bitte erneut versuchen." } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Λείπει το όνομα χρήστη ή το συνθηματικό. Δοκιμάστε ξανά." + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -9397,7 +11337,7 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Не вистачає юзернейму чи паролю. Спробуйте ще раз." + "value" : "Не вистачає імені користувача чи паролю. Спробуйте ще раз." } }, "zh-Hans" : { @@ -9417,6 +11357,12 @@ "InstallationError.NoNonPrereleaseVersionAvailable" : { "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "لا توجد إصدارات غير تجريبية متاحة." + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -9429,6 +11375,12 @@ "value" : "Keine Nicht-Prerelease-Versionen verfügbar." } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Καμία προέκδοση διαθέσιμη." + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -9510,7 +11462,7 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Немає доступних релізних версій." + "value" : "Немає доступних Release версій." } }, "zh-Hans" : { @@ -9530,6 +11482,12 @@ "InstallationError.NoPrereleaseVersionAvailable" : { "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "لا توجد إصدارات تجريبية متاحة." + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -9542,6 +11500,12 @@ "value" : "Keine Prerelease-Versions verfügbar." } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Καμία προέκδοση διαθέσιμη." + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -9623,7 +11587,7 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Немає доступного пререлізу." + "value" : "Немає доступних RC версій." } }, "zh-Hans" : { @@ -9643,6 +11607,12 @@ "InstallationError.NotEnoughFreeSpaceToExpandArchive" : { "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "لا يمكن توسيع الأرشيف \"%@\" لأن المجلد الحالي لا يحتوي على مساحة خالية كافية.\n\nقم بتوفير مساحة أكبر لتوسيع الأرشيف ثم قم بتثبيت Xcode %@ مرة أخرى لبدء التثبيت من حيث توقف." + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -9655,6 +11625,12 @@ "value" : "Das Archiv \"%@\" kann nicht entpackt werden, da auf dem aktuellen Volume nicht genügend freier Speicherplatz verfügbar ist.\n\nBitte stelle mehr Platz zur Verfügung, um das Archiv zu entpacken und installiere danach Xcode %@ erneut, um die Installation von dort zu beginnen wo sie beendet wurde." } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Το αρχείο αρχειοθέτησης «%@» δεν μπορεί να αποσυμπιεστεί καθώς δεν έχει αρκετός διαθέσιμος χώρος σε αυτόν τον τόμο." + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -9736,7 +11712,7 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Архів \"%@\" не можливо розпакувати, так як бракує місця.\n\nПочистіть файлове сховище щоб вистачило місця на розпаковку Архіву, та спробуйте встановити Xcode %@ знову." + "value" : "Архів \"%@\" не можливо розпакувати, так як бракує місця.\n\nЗвільніть файлове сховище щоб вистачило місця на розпаковку Архіву, та спробуйте інсталювати Xcode %@ знову." } }, "zh-Hans" : { @@ -9748,7 +11724,7 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "壓縮檔 \"%@\" 由於缺乏足夠的磁碟空間,無法解壓縮。\n\n請清空更多磁碟空間以確保可以解壓縮該檔案,然後再重新安裝 Xcode %@ 一次。安裝步驟將會從上次停住的地方繼續。" + "value" : "由於磁碟空間不足,無法將壓縮檔「%@」解壓縮。\n\n請騰出更多磁碟空間以完成解壓縮,並重新安裝 Xcode %@。安裝步驟將會從上次停住的地方繼續。" } } } @@ -9756,6 +11732,12 @@ "InstallationError.PostInstallStepsNotPerformed.Installed" : { "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "اكتمل التثبيت، ولكن لم يتم تنفيذ بعض خطوات ما بعد التثبيت تلقائيًا. سيتم تنفيذ ذلك عند تشغيل Xcode %@ لأول مرة." + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -9768,6 +11750,12 @@ "value" : "Die Installation ist abgeschlossen, allerdings wurden einige Post-Installationsschritte nicht automatisch ausgeführt. Diese werden beim ersten Start von Xcode %@ ausgeführt." } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Η εγκατάσταση ολοκληρώθηκε, αλλά κάποια μετά-εγκαταστατικά βήματα δεν εκτελέστηκαν αυτόματα. Αυτά τα βήματα θα πραγματοποιηθούν κατά την πρώτη εκκίνηση του Xcode %@." + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -9855,13 +11843,13 @@ "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "安装已完成,但一些前置步骤没有被自动执行。它们将会在您第一次运行Xcode %@时执行。" + "value" : "安装已完成,但一些安装后准备步骤没有被自动执行。它们将会在您第一次运行Xcode %@时执行。" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "安裝已經完成,但有些安裝後步驟沒有自動執行。這些步驟在你第一次啟動 Xcode %@ 的時候會自動執行。" + "value" : "已完成安裝,但有些安裝後步驟未完成。這些步驟在首次啟動 Xcode %@ 時會自動執行。" } } } @@ -9869,6 +11857,12 @@ "InstallationError.PostInstallStepsNotPerformed.NotInstalled" : { "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "اكتمل التثبيت، ولكن لم يتم تنفيذ بعض خطوات ما بعد التثبيت تلقائيًا. يقوم Xcodes بتنفيذ هذه الخطوات باستخدام مساعد مميز، والذي يبدو أنه لم يتم تثبيته. يمكنك تثبيته من التفضيلات > المتقدم.\n\nسيتم تنفيذ هذه الخطوات عند تشغيل Xcode %@ لأول مرة." + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -9881,6 +11875,12 @@ "value" : "Die Installation ist abgeschlossen, allerdings wurden einige Post-Installationsschritte nicht automatisch ausgeführt. Xcodes führt diese Schritte mittels eines privilegierten Helfers aus, welcher aber nicht installiert zu sein scheint. Er kann über Einstellungen > Erweitert installiert werden. Diese Schritte werden beim ersten Start von Xcode %@ ausgeführt." } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Η εγκατάσταση ολοκληρώθηκε, αλλά κάποια μετά-εγκαταστατικά βήματα δεν εκτελέστηκαν αυτόματα. Το Xcodes εκτελεί αυτά τα βήματα μέσω ενός εργαλείου με δικαιώματα, το οποίο φαίνεται ως μη εγκατεστημένο. Μπορείτε να το εγκαταστήσετε πηγαίνοντας στο «Ρυθμίσεις» > «Προηγμένες».\n\nΑυτά τα βήματα θα πραγματοποιηθούν κατά την πρώτη εκκίνηση του Xcode %@." + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -9962,19 +11962,19 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Установку завершено. Але деякі post-install кроки не виконано автоматично. Xcodes виконує цю роботу за допомогою \"privileged helper\", який схоже що не встановлено. Ви можете встановити його за доромогою Налаштування > Додатково.\n\nЦі кроки буде виконано коли ви вперше запустите Xcode %@." + "value" : "Інсталяцію завершено. Але деякі кроки після інсталяції не було виконано автоматично. Xcodes виконує цю роботу за допомогою \"Privileged Helper\", який схоже що не інстальовано. Ви можете встановити його за допомогою Налаштування > Додатково.\n\nЦі кроки буде виконано коли ви вперше запустите Xcode %@." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "安装已完成,但一些前置步骤没有被自动执行。Xcodes使用一个提权帮助程序来执行这些步骤,但帮助程序似乎没有被安装。您可以从 偏好设置 > 高级 中安装。\n\n这些步骤将会在您第一次运行Xcode %@时执行。" + "value" : "安装已完成,但一些安装后准备步骤没有被自动执行。Xcodes使用一个提权帮助程序来执行这些步骤,但帮助程序似乎没有被安装。您可以从 偏好设置 > 高级 中安装。\n\n这些步骤将会在您第一次运行Xcode %@时执行。" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "安裝已經完成,但有些安裝後步驟沒有自動執行。 Xcodes 需要權限輔助程式來執行這些步驟,但該程式尚未被安裝。你可以在 偏好設定 > 進階中安裝它。\n\n這些步驟在你第一次啟動 Xcode %@ 的時候會自動執行。" + "value" : "已完成安裝,但有些安裝後步驟未自動執行。Xcodes 需要具有權限的輔助程式來執行這些步驟,但該程式尚未被安裝。你可以在 偏好設定 > 進階中安裝它。\n\n這些步驟在首次啟動 Xcode %@ 時會自動執行。" } } } @@ -9982,6 +11982,12 @@ "InstallationError.UnavailableVersion" : { "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تعذر العثور على الإصدار %@." + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -9994,6 +12000,12 @@ "value" : "Kann Version %@ nicht finden." } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Η έκδοση %@ δεν βρέθηκε." + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -10095,6 +12107,12 @@ "InstallationError.UnexpectedCodeSigningIdentity" : { "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "لا يحتوي Xcode الذي تم تنزيله على هوية توقيع الرمز المتوقعة.\nيملك:\n%@\n%@\nمُتوقع:\n%@\n%@" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -10107,6 +12125,12 @@ "value" : "Die heruntergeladene Version von Xcode hat nicht die erwartete Code-Signing-Identity.\nErhalten:\n%@\n%@\nErwartet:\n%@\n%@" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Το ληφθέν Xcode δεν έχει την αναμενόμενη ταυτότητα υπογραφής κώδικα.\nΥπάρχουσα:\n%@\n%@\nΑναμενόμενη:\n%@\n%@" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -10188,7 +12212,7 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Викачаний Xcode має неочікуваний код підпису (code signing identity).\nМає:\n%@\n%@\nОчікується:\n%@\n%@" + "value" : "Викачаний Xcode має неочікуваний підпис (code signing identity).\nМає:\n%@\n%@\nОчікується:\n%@\n%@" } }, "zh-Hans" : { @@ -10200,7 +12224,7 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "下載的 Xcode 含有未預期的簽章。\n簽章人:\n%@\n%@\n預期的簽章人:\n%@\n%@" + "value" : "下載的 Xcode 含有未預期的簽章。\n簽章者:\n%@\n%@\n預期的簽章者:\n%@\n%@" } } } @@ -10208,6 +12232,12 @@ "InstallationError.UnsupportedFileFormat" : { "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "لا يدعم Xcodes (حتى الآن) تثبيت Xcode من تنسيق الملف %@." + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -10220,6 +12250,12 @@ "value" : "Xcodes unterstützt (bislang) nicht die Installation von Xcode per %@-Dateiformat." } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Το Xcodes δεν υποστηρίζει (ακόμη) την εγκατάσταση του Xcode από αρχεία τύπου %@." + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -10301,7 +12337,7 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Xcode (поки що) не підтримує свою установку у %@ форматі." + "value" : "Xcodes (поки що) не підтримує свою установку у %@ форматі." } }, "zh-Hans" : { @@ -10321,6 +12357,12 @@ "InstallationError.VersionAlreadyInstalled" : { "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ مثبت بالفعل في %@" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -10333,6 +12375,12 @@ "value" : "%@ ist bereits installiert unter %@" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Το %@ είναι ήδη εγκατεστημένο στο %@" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -10414,7 +12462,7 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "%@ вже встановлено в %@" + "value" : "%@ вже інстальовано до %@" } }, "zh-Hans" : { @@ -10426,7 +12474,7 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "%@ 已經安裝於 %@" + "value" : "%@ 已經在 %@ 安裝" } } } @@ -10434,6 +12482,12 @@ "InstallationError.VersionNotInstalled" : { "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ غير مثبت." + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -10446,6 +12500,12 @@ "value" : "%@ ist nicht installiert." } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Το %@ δεν είναι εγκατεστημένο." + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -10527,7 +12587,7 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "%@ не встановлено." + "value" : "%@ не інстальовано." } }, "zh-Hans" : { @@ -10539,7 +12599,7 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "%@ 沒有被安裝。" + "value" : "%@ 尚未安裝。" } } } @@ -10548,6 +12608,12 @@ "comment" : "So if changing order, make sure the positional specficier is retained. ex: \"%3$@: Step %1$d of %2$d\"", "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "Step %1$d of %2$d: %3$@" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -10560,6 +12626,12 @@ "value" : "Schritt %1$d von %2$d: %3$@" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Βήμα %1$d από %2$d: %3$@" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -10660,6 +12732,12 @@ }, "InstallDescription" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تثبيت هذا الإصدار" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -10672,6 +12750,12 @@ "value" : "Diese Version installieren" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Εγκατάσταση αυτής της έκδοσης" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -10753,7 +12837,7 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Встановити цю версію" + "value" : "Інсталювати цю версію" } }, "zh-Hans" : { @@ -10772,6 +12856,12 @@ }, "InstallDirectory" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "دليل التركيب" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -10784,6 +12874,12 @@ "value" : "Installationsverzeichnis" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Φάκελος Εγκατάστασης" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -10796,18 +12892,42 @@ "value" : "Directorio de instalación" } }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Install Directory" + } + }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Répertoire d'installation" } }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Install Directory" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Install Directory" + } + }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "インストール先" } }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "설치 경로" + } + }, "nl" : { "stringUnit" : { "state" : "translated", @@ -10820,6 +12940,12 @@ "value" : "Katalog instalacji" } }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Install Directory" + } + }, "ru" : { "stringUnit" : { "state" : "translated", @@ -10832,16 +12958,34 @@ "value" : "Yükleme Klasörü" } }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Шлях інсталяції" + } + }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "安装目录" } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "安裝目錄" + } } } }, "InstallHelper" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تثبيت المساعد" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -10854,6 +12998,12 @@ "value" : "Helfer installieren" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Εγκατάσταση βοηθητικού εργαλείου" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -10935,7 +13085,7 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Встановити Install helper" + "value" : "Інсталювати Helper" } }, "zh-Hans" : { @@ -10954,6 +13104,12 @@ }, "InstallPathDescription" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "يقوم Xcodes بالبحث والتثبيت في دليل واحد. بشكل افتراضي (والموصى به) هو الاحتفاظ بهذا /Applications.online. قد تؤدي أي تغييرات في مكان تخزين Xcode إلى توقف التطبيقات/الخدمات الأخرى عن العمل." + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -10966,24 +13122,60 @@ "value" : "Xcodes sucht and installiert in ein einzelnes Verzeichnis. Standard (und empfohlen beizubehalten) ist /Programme. Änderungen am Speicherort von Xcode können dazu führen, dass andere Apps/Dienste aufhören zu funktionieren. " } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Το Xcodes αναζητά και εγκαθιστά σε ένα και μόνο φάκελο. Κατά προεπιλογή (όπως και συνίσταται) αυτός είναι ο «Εφαρμογές». Όποιες αλλαγές στην τοποθεσία του Xcode μπορεί να έχει ως αποτέλεσμα την προβληματική λειτουργία άλλων εφαρμογών/υπηρεσιών." + } + }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes searches and installs to a single directory. By default (and recommended) is to keep this /Applications. Any changes to where Xcode is stored may result in other apps/services to stop working. " } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xcodes searches and installs to a single directory. By default (and recommended) is to keep this /Applications. Any changes to where Xcode is stored may result in other apps/services to stop working. " + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xcodes searches and installs to a single directory. By default (and recommended) is to keep this /Applications. Any changes to where Xcode is stored may result in other apps/services to stop working. " + } + }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes recherche et installe dans un répertoire spécifique. Par défaut, il est recommandé de laisser /Applications. Tout changement modifiant l'emplacement de Xcode risque de provoquer des dysfonctionnements aux autres apps/services." } }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xcodes searches and installs to a single directory. By default (and recommended) is to keep this /Applications. Any changes to where Xcode is stored may result in other apps/services to stop working. " + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xcodes searches and installs to a single directory. By default (and recommended) is to keep this /Applications. Any changes to where Xcode is stored may result in other apps/services to stop working. " + } + }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Xcodesは1つのディレクトリを検索し、そこにインストールします。デフォルト (推奨) は /Applications です。Xcodeの格納場所を変更すると、他のアプリケーションやサービスが動作しなくなる可能性があります。" } }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xcodes는 단일 경로를 탐색하고 설치를 수행합니다. 이 경로는 기본적으로 /Applications이며, 이 경로를 유지하는 것을 권장합니다. 경로를 변경하면 다른 앱/서비스가 정상적으로 동작하지 않을 수 있습니다." + } + }, "nl" : { "stringUnit" : { "state" : "translated", @@ -10996,6 +13188,12 @@ "value" : "Xcodes wyszukuje i instaluje w jednym katalogu. Domyślnie (i zalecane) jest to /Applications. Wszelkie zmiany w miejscu przechowywania Xcode mogą spowodować, że inne aplikacje/usługi przestaną działać." } }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xcodes searches and installs to a single directory. By default (and recommended) is to keep this /Applications. Any changes to where Xcode is stored may result in other apps/services to stop working. " + } + }, "ru" : { "stringUnit" : { "state" : "translated", @@ -11008,17 +13206,35 @@ "value" : "Xcodes bir klasörü arayıp oraya yükler. Varsayılan(ve önerilen) yöntem /Uygulamalar klasöründe tutmaktır. Xcode'un bulunduğu ortamdaki herhangi bir değişiklik başka bir uygulamanın/servisin çalışmasını durdurabilir." } }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xcodes шукає інсталяції та встановлює нові версії у вказану директорію. За-умовчанням,до /Applications (рекомендується). \nБудь-які зміни шляху розташування Xcode можуть призвести до втрати дієздатності інших застосунків / сервісів. " + } + }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes会在一个目录中检索及安装。默认(推荐)保持/Applications。任何对Xcode存储位置的变更都可能会导致其他App或服务停止工作。" } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xcodes 搜尋並安裝軟體到這一個路徑。預設值是 /Applications(也不建議改動)。更改 Xcode 的安裝位置可能導致其他應用程式或服務停止運作。" + } } } }, "LastChecked" : { "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "آخر فحص: %@" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -11031,6 +13247,12 @@ "value" : "Letzte Prüfung: %@" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Τελευταίος έλεγχος: %@" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -11131,6 +13353,12 @@ }, "License" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "License" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -11143,6 +13371,12 @@ "value" : "Lizenz" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Άδεια χρήσης" + } + }, "es" : { "stringUnit" : { "state" : "translated", @@ -11230,13 +13464,19 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "軟體許可證" + "value" : "授權條款" } } } }, "LocalCachePath" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "مسار ذاكرة التخزين المؤقت المحلية" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -11249,6 +13489,12 @@ "value" : "Lokaler Cache-Pfad" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Διαδρομή Τοπικής Cache" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -11349,6 +13595,12 @@ }, "LocalCachePathDescription" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "يقوم Xcodes بتخزين إصدارات Xcode المتوفرة مؤقتًا وتنزيل الإصدارات الجديدة مؤقتًا إلى الدليل" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -11361,6 +13613,12 @@ "value" : "Xcodes speichert verfügbare Xcode-Versionen zwischen und lädt neue Versionen temporär in ein Verzeichnis." } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Το Xcodes χρησιμοποιεί cache περιεχομένου για τις διαθέσιμες εκδόσεις του Xcode και κάνει προσωρινές λήψεις νέων εκδόσεων σε κάποιον φάκελο" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -11442,7 +13700,7 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Сюди зберігаються тимчасові закачки встановлюваних версій Xcode" + "value" : "Xcodes кешує доступні версії Xcode та тимчасові файли викачувань нових версій в цю директорію" } }, "zh-Hans" : { @@ -11462,6 +13720,12 @@ "Login" : { "comment" : "ToolBar", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تسجيل الدخول" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -11474,6 +13738,12 @@ "value" : "Login" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Είσοδος" + } + }, "es" : { "stringUnit" : { "state" : "translated", @@ -11568,6 +13838,12 @@ }, "LoginDescription" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "Open Login" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -11580,6 +13856,12 @@ "value" : "Login öffnen" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Άνοιγμα Εισόδου" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -11681,6 +13963,12 @@ "MacOSRequirement" : { "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "يتطلب نظام التشغيل macOS %@ أو أحدث" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -11693,6 +13981,12 @@ "value" : "Erfordert macOS %@ oder neuer" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Απαιτείται macOS %@ ή μεταγενέστερη έκδοση" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -11774,7 +14068,7 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Потрібен macOS %@ чи новіший" + "value" : "Необхідна macOS %@ чи новіша" } }, "zh-Hans" : { @@ -11793,6 +14087,12 @@ }, "MakeActive" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "Make active" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -11805,6 +14105,12 @@ "value" : "Aktivieren" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ορισμός ως ενεργής" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -11905,6 +14211,12 @@ }, "MakeActiveVersionDescription" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "اجعل هذا هو الإصدار النشط" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -11917,6 +14229,12 @@ "value" : "Dies zur aktiven Version machen" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ορισμός ως ενεργής έκδοσης" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -12018,6 +14336,12 @@ "Menu.About" : { "comment" : "Menu", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "About Xcodes" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -12030,6 +14354,12 @@ "value" : "Über Xcodes" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Πληροφορίες για το Xcodes" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -12131,6 +14461,12 @@ "Menu.Acknowledgements" : { "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "شكر وتقدير Xcodes" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -12143,6 +14479,12 @@ "value" : "Xcodes Anerkennungen" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Αναγνωρίσεις του Xcodes" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -12243,6 +14585,12 @@ }, "Menu.CheckForUpdates" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تحقق من وجود تحديثات..." + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -12255,6 +14603,12 @@ "value" : "Prüfe auf Updates..." } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Έλεγχος για ενημερώσεις..." + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -12355,10 +14709,16 @@ }, "Menu.GitHubRepo" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "مستودعات GitHub Xcodes" + } + }, "ca" : { "stringUnit" : { "state" : "translated", - "value" : "Repositori de Github d'Xcodes" + "value" : "Repositori de GitHub d'Xcodes" } }, "de" : { @@ -12367,6 +14727,12 @@ "value" : "Xcodes GitHub-Repo" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xcodes GitHub Repo" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -12400,7 +14766,7 @@ "it" : { "stringUnit" : { "state" : "translated", - "value" : "Xcodes Repository di Github" + "value" : "Xcodes Repository di GitHub" } }, "ja" : { @@ -12448,7 +14814,7 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Xcodes GitHub Repo" + "value" : "Xcodes GitHub репозіторій" } }, "zh-Hans" : { @@ -12467,6 +14833,12 @@ }, "Menu.ReportABug" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "الإبلاغ عن خطأ" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -12479,6 +14851,12 @@ "value" : "Bug melden" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Αναφορά σφάλματος" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -12579,6 +14957,12 @@ }, "Menu.RequestNewFeature" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "Request a New Feature" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -12591,6 +14975,12 @@ "value" : "Neues Feature anfordern" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Αίτημα για νέα δυνατότητα" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -12672,7 +15062,7 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Запит на нову Фічу" + "value" : "Запит на новий функціонал" } }, "zh-Hans" : { @@ -12689,9 +15079,139 @@ } } }, + "Misc" : { + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "Misc" + } + }, + "ca" : { + "stringUnit" : { + "state" : "translated", + "value" : "Misc" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Misc" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Misc" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Misc" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Misc" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Misc" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Misc" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Misc" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Misc" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "Misc" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "Misc" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Misc" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Misc" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Misc" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Misc" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Misc" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Misc" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "杂项" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "雜項" + } + } + } + }, "Moving" : { "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "Moving to %@" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -12704,6 +15224,12 @@ "value" : "In %@ bewegen" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Μετακίνηση σε %@" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -12805,6 +15331,12 @@ "Never" : { "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "أبداً" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -12817,6 +15349,12 @@ "value" : "Nie" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ποτέ" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -12917,6 +15455,12 @@ }, "Next" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "التالي" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -12929,6 +15473,12 @@ "value" : "Nächstes" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Επόμενο" + } + }, "es" : { "stringUnit" : { "state" : "translated", @@ -13021,25 +15571,15 @@ } } }, - "Not Hidden" : { - "localizations" : { - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Visible" - } - }, - "tr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Gizli Değil" - } - } - } - }, "Notification.FinishedInstalling" : { "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "انهاء التثبيت" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -13052,6 +15592,12 @@ "value" : "Installation beendet" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Η εγκατάσταση ολοκληρώθηκε" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -13133,7 +15679,7 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Встановлено" + "value" : "Інстальовано" } }, "zh-Hans" : { @@ -13154,6 +15700,12 @@ "comment" : "Notifications", "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "New version is available" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -13166,6 +15718,12 @@ "value" : "Neue Version verfügbar" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Νέα έκδοση είναι διαθέσιμη" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -13259,7 +15817,7 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "有新的版本可用" + "value" : "新版本已可供使用" } } } @@ -13267,6 +15825,12 @@ "Notification.NewXcodeVersion.Body" : { "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "إصدارات Xcode الجديدة متاحة للتنزيل." + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -13279,6 +15843,12 @@ "value" : "Neue Xcode-Versionen stehen zum Download bereit." } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Νέες εκδόσεις του Xcode είναι διαθέσιμες για λήψη." + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -13360,7 +15930,7 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Нові версії Xcode доступні до завантаження." + "value" : "Доступні нові версії Xcode" } }, "zh-Hans" : { @@ -13372,7 +15942,7 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "新的 Xcode 版本已經可以下載了" + "value" : "新的 Xcode 版本已可供下載。" } } } @@ -13381,6 +15951,12 @@ "comment" : "Notifications", "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "إصدارات Xcode الجديدة" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -13393,6 +15969,12 @@ "value" : "Neue Xcode-Versionen" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Νέες εκδόσεις του Xcode" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -13493,6 +16075,12 @@ }, "Notifications" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "إشعارات" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -13505,6 +16093,12 @@ "value" : "Benachrichtigungen" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Γνωστοποιήσεις" + } + }, "es" : { "stringUnit" : { "state" : "translated", @@ -13599,6 +16193,12 @@ }, "NotificationSettings" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "إعدادات الإشعار" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -13611,6 +16211,12 @@ "value" : "Benachrichtigungs-Einstellungen" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ρυθμίσεις γνωστοποιήσεων" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -13712,6 +16318,12 @@ "NoTrustedPhones" : { "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "لا يحتوي حسابك على أي أرقام هواتف موثوقة، ولكنها مطلوبة للمصادقة الثنائية.\n\nراجع https://support.apple.com/HT204915." + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -13724,6 +16336,12 @@ "value" : "Dein Account verfügt über keine vertrauenswürdigen Telefonnummern, diese sind aber für Zwei-Faktor-Authentifizierung erforderlich.\n\nInformationen dazu unter https://support.apple.com/HT204915." } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ο λογαριασμός σας δεν έχει κάποιους αξιόπιστους τηλεφωνικούς αριθμούς, όμως απαιτούνται για τον έλεγχο ταυτότητας δύο παραγόντων.\n\nΔείτε https://support.apple.com/HT204915." + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -13817,13 +16435,19 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "你的帳號沒有任何已信任的手機號碼,但兩階段認證需要信任的手機號碼。\n\n請參閱 https://support.apple.com/HT204915." + "value" : "你的帳號沒有任何已信任的手機號碼,但兩階段認證需要信任的手機號碼。\n\n請參閱 https://support.apple.com/HT204915。" } } } }, "NoXcodeSelected" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "لم يتم تحديد Xcode" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -13836,6 +16460,12 @@ "value" : "Kein Xcode ausgewählt" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Καμία επιλογή Xcode" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -13929,13 +16559,19 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "沒有已選取的 Xcode" + "value" : "未選取 Xcode" } } } }, "OK" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "OK" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -13948,6 +16584,12 @@ "value" : "OK" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "OK" + } + }, "es" : { "stringUnit" : { "state" : "translated", @@ -13966,6 +16608,12 @@ "value" : "OK" } }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "OK" + } + }, "it" : { "stringUnit" : { "state" : "translated", @@ -14034,19 +16682,15 @@ } } }, - "OnSelect" : { - "localizations" : { - "tr" : { - "stringUnit" : { - "state" : "translated", - "value" : "OnSelect" - } - } - } - }, "OnSelectDoNothing" : { "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "احتفظ بالاسم كـ Xcode-X.X.X.app" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -14059,6 +16703,12 @@ "value" : "Name beibehalten als Xcode-X.X.X.app" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Διατήρηση ονόματος ως Xcode-X.X.X.app" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -14071,18 +16721,42 @@ "value" : "Mantener el nombre como Xcode-X.X.X.app" } }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Keep name as Xcode-X.X.X.app" + } + }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Ne rien faire" } }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Keep name as Xcode-X.X.X.app" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Keep name as Xcode-X.X.X.app" + } + }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "名前をXcode-X.X.X.appのまま維持する" } }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "앱 이름을 Xcode-X.X.X.app 형태로 유지하기" + } + }, "nl" : { "stringUnit" : { "state" : "translated", @@ -14113,17 +16787,35 @@ "value" : "Uygulama ismini Xcode-X.X.X.app gibi tut." } }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Зберігати імʼя як Xcode-X.X.X.app" + } + }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "保持名称为Xcode-X.X.X.app" } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "保留 Xcode-X.X.X.app 名稱" + } } } }, "OnSelectDoNothingDescription" : { "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "On select, will keep the name as the version eg. Xcode-13.4.1.app" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -14136,6 +16828,12 @@ "value" : "Bei Auswahl wird der Name mit Version beibehalten, z. B. Xcode-13.4.1.app" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Κατά την επιλογή, θα διατηρηθεί το όνομα και η έκδοση π.χ. Xcode-13.4.1.app" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -14148,18 +16846,42 @@ "value" : "Al seleccionar, mantener el nombre como la versión p.ej. Xcode-13.4.1.app" } }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "On select, will keep the name as the version eg. Xcode-13.4.1.app" + } + }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Lors de la sélection, gardera le numéro de version. Exemple: Xcode-13.4.1.app" } }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "On select, will keep the name as the version eg. Xcode-13.4.1.app" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "On select, will keep the name as the version eg. Xcode-13.4.1.app" + } + }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "選択時、バージョン付きの名前を維持します。例) Xcode-13.4.1.app" } }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "앱 이름에 버전을 포함하여 Xcode-13.4.1.app과 같은 형식을 유지합니다." + } + }, "nl" : { "stringUnit" : { "state" : "translated", @@ -14172,6 +16894,12 @@ "value" : "Po wybraniu, nazwa pozostanie w formacie z numerem wersji, np. Xcode-13.4.1.app" } }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "On select, will keep the name as the version eg. Xcode-13.4.1.app" + } + }, "ru" : { "stringUnit" : { "state" : "translated", @@ -14184,41 +16912,101 @@ "value" : "Seçildiğinde, ismi Xcode-13.4.1.app örneğindeki gibi tutar." } }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "При виборі, зберігати імʼя як версію, наприклад,\nXcode-13.4.1.app" + } + }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "选中时,将会保持各版本的名称。例如Xcode-13.4.1.app。" } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "選擇時,會保留有版本的檔名,如 Xcode-13.4.1.app" + } } } }, "OnSelectRenameXcode" : { "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "قم دائمًا بإعادة التسمية إلى Xcode.app" + } + }, + "ca" : { + "stringUnit" : { + "state" : "translated", + "value" : "Always rename to Xcode.app" + } + }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Immer in Xcode.app umbenennen" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Μετονομασία πάντα σε Xcode.app" + } + }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Always rename to Xcode.app" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Always rename to Xcode.app" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Always rename to Xcode.app" + } + }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Toujours renommer en Xcode.app" } }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Always rename to Xcode.app" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Always rename to Xcode.app" + } + }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "名前を常にXcode.appに変更する" } }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "앱 이름을 Xcode.app으로 변경하기" + } + }, "nl" : { "stringUnit" : { "state" : "translated", @@ -14249,17 +17037,35 @@ "value" : "Her zaman Xcode.app şeklinde ismi değiştir" } }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Завжди змінювати імʼя на Xcode.app" + } + }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "总是重命名为Xcode.app" } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "總是重新命名為 Xcode.app" + } } } }, "OnSelectRenameXcodeDescription" : { "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "عند التحديد، سيحاول تلقائيًا إعادة تسمية Xcode النشط إلى Xcode.app، وإعادة تسمية Xcode.app السابق إلى اسم الإصدار." + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -14272,24 +17078,60 @@ "value" : "Bei Auswahl wird versucht das aktive Xcode in Xcode.app umzubenennen. Die vorherige Xcode.app wird dazu in den Versionsnamen umbenannt." } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Κατά την επιλογή, θα γίνει αυτόματα προσπάθεια μετονομασίας του ενεργού Xcode σε Xcode.app, μετονομάζοντας το προηγούμενο Xcode.app στο αντίστοιχο όνομα της έκδοσής του." + } + }, "en" : { "stringUnit" : { "state" : "translated", "value" : "On select, will automatically try and rename the active Xcode to Xcode.app, renaming the previous Xcode.app to the version name." } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "On select, will automatically try and rename the active Xcode to Xcode.app, renaming the previous Xcode.app to the version name." + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "On select, will automatically try and rename the active Xcode to Xcode.app, renaming the previous Xcode.app to the version name." + } + }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Lors de la sélection, toujours essayer de renommer la version active de Xcode en Xcode.app, en renommant l'ancienne Xcode.app avec le nom de version." } }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "On select, will automatically try and rename the active Xcode to Xcode.app, renaming the previous Xcode.app to the version name." + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "On select, will automatically try and rename the active Xcode to Xcode.app, renaming the previous Xcode.app to the version name." + } + }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "選択時、自動的にアクティブなXcodeをXcode.appに、以前のXcode.appをバージョン付きの名前に変更します。" } }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "기본 버전으로 선택한 Xcode의 앱 이름을 Xcode.app으로 변경하고, 이전 Xcode.app의 이름은 버전을 포함한 형식으로 변경합니다." + } + }, "nl" : { "stringUnit" : { "state" : "translated", @@ -14320,16 +17162,34 @@ "value" : "Seçildiğinde, aktif olan Xcode'u Xcode.app olarak isimlendirmeye çalışır ve eski Xcode ismine sürüm ismi ekler." } }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "При виборі, автоматично перейменовувати активний Xcode на Xcode.app, змінюючи імʼя попереднього Xcode.app на його версію." + } + }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "选中时,会自动尝试重命名活跃的Xcode为Xcode.app,将之前的Xcode.app重命名为包含版本的名称。" } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "選擇時,會自動嘗試將目前啟用的 Xcode 重新命名為 Xcode.app,而將先前的 Xcode.app 重新命名為有版本的檔名。" + } } } }, "Open" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "افتح" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -14342,6 +17202,12 @@ "value" : "Öffnen" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Άνοιγμα" + } + }, "es" : { "stringUnit" : { "state" : "translated", @@ -14434,40 +17300,132 @@ } } }, - "OPEN" : { - "localizations" : { - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "OUVRIR" - } - }, - "tr" : { - "stringUnit" : { - "state" : "translated", - "value" : "AÇ" - } - } - } - }, "Open In Rosetta" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "فتح في روزيتا" + } + }, + "ca" : { + "stringUnit" : { + "state" : "translated", + "value" : "Open In Rosetta" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Open In Rosetta" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Άνοιγμα με Rosetta" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Open In Rosetta" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Open In Rosetta" + } + }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Ouvrir avec Rosetta" } }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Open In Rosetta" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Open In Rosetta" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "Open In Rosetta" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rosetta를 사용하여 열기" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Open In Rosetta" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Open In Rosetta" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Open In Rosetta" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Открыть в Rosetta" + } + }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Rosetta ile Aç" } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Запустити через Rosetta" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "在Rosetta中打开" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "使用 Rosetta 打開" + } } } }, "OpenDescription" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "افتح هذا الإصدار" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -14480,6 +17438,12 @@ "value" : "Diese Version öffnen" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Άνοιγμα αυτής της έκδοσης" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -14580,6 +17544,12 @@ }, "Password" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "كلمة المرور:" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -14592,6 +17562,12 @@ "value" : "Passwort:" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Συνθηματικό:" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -14692,49 +17668,751 @@ }, "Perform post-install steps" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تنفيذ خطوات ما بعد التثبيت" + } + }, + "ca" : { + "stringUnit" : { + "state" : "translated", + "value" : "Perform post-install steps" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Führe Post-Installationsschritte durch" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Εκτέλεση μετά-εγκαταστατικών βημάτων" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Perform post-install steps" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Perform post-install steps" + } + }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Exécuter des étapes post-installation" } }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Perform post-install steps" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Perform post-install steps" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "Perform post-install steps" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "설치 후 단계 수행" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Perform post-install steps" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Perform post-install steps" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Perform post-install steps" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Perform post-install steps" + } + }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Yükleme sonrası adımları uygula" } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Виконати пост-інсталяційні кроки" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "执行安装后准备步骤" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "執行安裝後步驟" + } + } + } + }, + "PIN" : { + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "PIN" + } + }, + "ca" : { + "stringUnit" : { + "state" : "translated", + "value" : "PIN" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "PIN" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "PIN" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "PIN" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "PIN" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "PIN" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "PIN" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "PIN" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "PIN" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "PIN" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "PIN" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "PIN" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "PIN" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "PIN" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "PIN" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "PIN" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "PIN" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "PIN" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "PIN" + } + } + } + }, + "PIN not set" : { + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "PIN not set" + } + }, + "ca" : { + "stringUnit" : { + "state" : "translated", + "value" : "PIN not set" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "PIN not set" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "PIN not set" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "PIN not set" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "PIN not set" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "PIN not set" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "PIN not set" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "PIN not set" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "PIN not set" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "PIN not set" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "PIN not set" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "PIN not set" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "PIN not set" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "PIN not set" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "PIN not set" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "PIN not set" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "PIN not set" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "PIN not set" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "PIN not set" + } } } }, "Platforms" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "المنصات" + } + }, + "ca" : { + "stringUnit" : { + "state" : "translated", + "value" : "Platforms" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Plattformen" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Πλατφόρμες" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Platforms" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Platforms" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Platforms" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Platforms" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Platforms" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Platforms" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "Platforms" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "플랫폼" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Platforms" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Platforms" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Platforms" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Платформы" + } + }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Platformlar" } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Платформи" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "平台" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "平台" + } + } + } + }, + "PlatformsDescription" : { + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "Installed Platforms" + } + }, + "ca" : { + "stringUnit" : { + "state" : "translated", + "value" : "Installed Platforms" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Installed Platforms" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Installed Platforms" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Installed Platforms" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Installed Platforms" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Installed Platforms" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Installed Platforms" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Installed Platforms" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Installed Platforms" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "Installed Platforms" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "설치된 플랫폼 보기" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Installed Platforms" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Installed Platforms" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Installed Platforms" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Установленные платформы" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Installed Platforms" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Installed Platforms" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "已安装的平台" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "已安裝的平台" + } } } }, "PlatformsList.Title" : { "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "فيما يلي قائمة بالأنظمة الأساسية المثبتة على هذا الجهاز." + } + }, + "ca" : { + "stringUnit" : { + "state" : "translated", + "value" : "Below are a list of platforms that are installed on this machine. " + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hier siehst du eine Liste der auf diesem Rechner installierten Plattformen." + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ακολουθεί λίστα με τις πλατφόρμες που είναι εγκατεστημένες σε αυτό το σύστημα." + } + }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Below are a list of platforms that are installed on this machine. " } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Below are a list of platforms that are installed on this machine. " + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Below are a list of platforms that are installed on this machine. " + } + }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Ci-dessous une liste des plateformes installées sur cette machine." } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Below are a list of platforms that are installed on this machine. " + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Below are a list of platforms that are installed on this machine. " + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "Below are a list of platforms that are installed on this machine. " + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "현재 기기에 설치된 플랫폼 목록입니다." + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Below are a list of platforms that are installed on this machine. " + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Below are a list of platforms that are installed on this machine. " + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Below are a list of platforms that are installed on this machine. " + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Below are a list of platforms that are installed on this machine. " + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Below are a list of platforms that are installed on this machine. " + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Нижче наведений список платформ що доступні на цій машині" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "下面是在这台设备上已经安装的平台列表。" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "下列是本機器安裝的平台名單。" + } } } }, "Preferences" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "التفضيلات" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -14747,6 +18425,18 @@ "value" : "Einstellungen" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Προτιμήσεις" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Preferences" + } + }, "es" : { "stringUnit" : { "state" : "translated", @@ -14841,6 +18531,12 @@ }, "PreferencesDescription" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "افتح التفضيلات" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -14853,6 +18549,12 @@ "value" : "Einstellungen öffnen" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Άνοιγμα Προτιμήσεων" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -14953,6 +18655,12 @@ }, "PrivilegedHelper" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "مساعد مميز" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -14965,6 +18673,12 @@ "value" : "Privilegierter Helfer" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Βοηθητικό Εργαλείο με δικαιώματα" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -15058,13 +18772,19 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "權限輔助程式" + "value" : "具有權限的輔助程式" } } } }, "PrivilegedHelperDescription" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "يستخدم Xcodes مساعدًا متميزًا منفصلاً لأداء المهام كجذر. هذه هي الأشياء التي تتطلب استخدام Sudo في سطر الأوامر، بما في ذلك خطوات ما بعد التثبيت وتبديل إصدارات Xcode باستخدام xcode-select.\n\nسيُطلب منك كلمة مرور حساب macOS لتثبيته." + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -15077,6 +18797,12 @@ "value" : "Xcodes verwendet einen separaten privilegierten Helfer, um Aufgaben als root zu erledigen. Das sind Dinge, die sudo in der Kommandozeile erfordern würden, einschließlich Post-Installationsschritte sowie das Umstellen von Xcode-Versionen mit xcode-select.\n\nUm ihn zu installieren, erfolgt eine Aufforderung zur Eingabe des Passworts für Dein macOS-Benutzerkonto." } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Το Xcodes χρησιμοποιεί ένα ξεχωριστό εργαλείο με δικαιώματα ώστε να εκτελέσει εργασίες ως root. Αυτές είναι εργασίες που θα απαιτούσαν sudo στη γραμμή εντολών, συμπεριλαμβανομένων μετά-εγκαταστατικών βημάτων και αλλαγής μεταξύ εκδόσεων του Xcode με το xcode-select.\n\nΘα σας ζητηθεί το συνθηματικό του λογαριασμού σας στο macOS για την εγκατάσταση του." + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -15158,25 +18884,31 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Xcodes використовує спеціальний \"privilege helper\" щоб запускати задачі як суперюзер. Це включає наприклад sudo в терміналі, та кроки після інсталяції або перемикання версії Xcode за допомогою xcode-select.\n\nБуде запит на ваш пароль від Мак щоб встановити цей хелпер." + "value" : "Xcodes використовує спеціальний \"Privileged Helper\" для запуску задач від імені root. Це включає, наприклад, sudo в терміналі, та кроки після інсталяції або перемикання версії Xcode за допомогою xcode-select.\n\nБуде запит на ваш пароль від Mac щоб інсталювати цей помічник." } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "Xcodes使用一个独立的提权帮助程序来以root身份执行任务。就是那些需要在命令行中用sudo执行的命令。包括一些安装前置步骤以及用xcode-select切换Xcode版本。\n\n您需要提供当前用户的密码来安装它。" + "value" : "Xcodes使用一个独立的提权帮助程序来以root身份执行任务。就是那些需要在命令行中用sudo执行的命令。包括一些安装后准备步骤以及用xcode-select切换Xcode版本。\n\n您需要提供当前用户的密码来安装它。" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "Xcodes 使用一個分開的權限輔助程式以使用 root 身份執行特定工作。這些工作包含了通常需要在命令列使用 sudo 的指令,包含安裝後步驟以及使用 xcode-select 選擇 Xcode 版本。\n\n安裝時,你將會被詢問你的 macOS 帳號密碼。" + "value" : "Xcodes 會利用一個分開的具有權限的輔助程式以 root 身份執行特定工作,例如那些在命令列中通常需使用 sudo 的命令,包含安裝程序後的步驟以及使用 xcode-select 選擇 Xcode 版本。\n\n安裝此程式時,你將會需要輸入 macOS 帳號的密碼。" } } } }, "Refresh" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "ينعش" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -15189,6 +18921,18 @@ "value" : "Aktualisieren" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ανανέωση" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Refresh" + } + }, "es" : { "stringUnit" : { "state" : "translated", @@ -15276,13 +19020,19 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "更新" + "value" : "重新整理" } } } }, "RefreshDescription" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تحديث قائمة Xcode" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -15295,6 +19045,12 @@ "value" : "Xcode-Liste aktualisieren" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ανανέωση λίστας των Xcode" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -15396,6 +19152,12 @@ "Release" : { "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "اصدار" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -15408,6 +19170,12 @@ "value" : "Release" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Κυκλοφορία" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -15489,7 +19257,7 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Release" + "value" : "Реліз" } }, "zh-Hans" : { @@ -15508,6 +19276,12 @@ }, "ReleaseDate" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تاريخ الاصدار" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -15520,6 +19294,12 @@ "value" : "Release-Datum" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ημερομηνία κυκλοφορίας" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -15618,121 +19398,14 @@ } } }, - "ReleaseNotes" : { - "extractionState" : "stale", - "localizations" : { - "ca" : { - "stringUnit" : { - "state" : "translated", - "value" : "Notes de la versió" - } - }, - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Release-Notes" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Release Notes" - } - }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Notas del lanzamiento" - } - }, - "fi" : { - "stringUnit" : { - "state" : "translated", - "value" : "Julkaisutiedot" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Notes de Mise à Jour" - } - }, - "hi" : { - "stringUnit" : { - "state" : "translated", - "value" : "रिलीज नोट्स" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Note di Rilascio" - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "リリースノート" - } - }, - "ko" : { - "stringUnit" : { - "state" : "translated", - "value" : "릴리즈 노트" - } - }, - "nl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Release Notes" - } - }, - "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Notatki wydania" - } - }, - "pt-BR" : { - "stringUnit" : { - "state" : "translated", - "value" : "Notas de lançamento" - } - }, - "ru" : { - "stringUnit" : { - "state" : "translated", - "value" : "Примечания к выпуску" - } - }, - "tr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Yayınlanma Notları" - } - }, - "uk" : { - "stringUnit" : { - "state" : "translated", - "value" : "Деталі релізу" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "更新说明" - } - }, - "zh-Hant" : { - "stringUnit" : { - "state" : "translated", - "value" : "版本附註" - } - } - } - }, "ReleaseNotes.help" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "عرض ملاحظات الإصدار" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -15745,6 +19418,12 @@ "value" : "Release-Notes anzeigen" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Σημειώσεις κυκλοφορίας" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -15845,6 +19524,12 @@ }, "ReleaseOnly" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "الإصدار فقط" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -15857,6 +19542,12 @@ "value" : "Nur Release" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Εκδόσεις κυκλοφορίας μόνο" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -15950,7 +19641,7 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "只看正式版" + "value" : "只顯示正式版" } } } @@ -15968,6 +19659,12 @@ }, "Required" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "مطلوب" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -15980,6 +19677,12 @@ "value" : "Erforderlich" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Απαιτείται" + } + }, "es" : { "stringUnit" : { "state" : "translated", @@ -16074,6 +19777,12 @@ }, "RevealInFinder" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تظهر في البحث" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -16086,6 +19795,12 @@ "value" : "Im Finder anzeigen" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Αποκάλυψη στο Finder" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -16179,13 +19894,19 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "顯示於 Finder" + "value" : "在 Finder 中顯示" } } } }, "SDKs" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "SDKs" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -16198,6 +19919,12 @@ "value" : "SDKs" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "SDKs" + } + }, "es" : { "stringUnit" : { "state" : "translated", @@ -16273,7 +20000,7 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "SDKs" + "value" : "SDK" } }, "zh-Hans" : { @@ -16285,239 +20012,269 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "SDKs" + "value" : "SDK 清單" } } } }, - "Search" : { - "extractionState" : "stale", + "SecurityKeyPinDescription" : { + "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insert your physical security key and enter the PIN" + } + }, "ca" : { "stringUnit" : { "state" : "translated", - "value" : "Cercar..." + "value" : "Insert your physical security key and enter the PIN" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Suchen ..." + "value" : "Insert your physical security key and enter the PIN" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Insert your physical security key and enter the PIN" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Search..." + "value" : "Insert your physical security key and enter the PIN" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Buscar..." + "value" : "Insert your physical security key and enter the PIN" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Etsi..." + "value" : "Insert your physical security key and enter the PIN" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Rechercher..." + "value" : "Insert your physical security key and enter the PIN" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "खोज..." + "value" : "Insert your physical security key and enter the PIN" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Cerca..." + "value" : "Insert your physical security key and enter the PIN" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "検索…" + "value" : "Insert your physical security key and enter the PIN" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "검색하기..." + "value" : "Insert your physical security key and enter the PIN" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Zoeken..." + "value" : "Insert your physical security key and enter the PIN" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Szukaj..." + "value" : "Insert your physical security key and enter the PIN" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Procurar..." + "value" : "Insert your physical security key and enter the PIN" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Поиск..." + "value" : "Insert your physical security key and enter the PIN" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Ara..." + "value" : "Insert your physical security key and enter the PIN" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Пошук..." + "value" : "Insert your physical security key and enter the PIN" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "搜索…" + "value" : "Insert your physical security key and enter the PIN" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "搜尋⋯" + "value" : "Insert your physical security key and enter the PIN" } } } }, - "SearchDescription" : { - "extractionState" : "stale", + "SecurityKeyTouchDescription" : { + "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "Touch your security key to verify that it’s you" + } + }, "ca" : { "stringUnit" : { "state" : "translated", - "value" : "Llistat de cerca" + "value" : "Touch your security key to verify that it’s you" } }, "de" : { "stringUnit" : { "state" : "translated", - "value" : "Suchliste" + "value" : "Touch your security key to verify that it’s you" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Touch your security key to verify that it’s you" } }, "en" : { "stringUnit" : { "state" : "translated", - "value" : "Search list" + "value" : "Touch your security key to verify that it’s you" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Lista de búsqueda" + "value" : "Touch your security key to verify that it’s you" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Hakulista" + "value" : "Touch your security key to verify that it’s you" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Liste de recherche" + "value" : "Touch your security key to verify that it’s you" } }, "hi" : { "stringUnit" : { "state" : "translated", - "value" : "खोज सूची" + "value" : "Touch your security key to verify that it’s you" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Lista Ricerca" + "value" : "Touch your security key to verify that it’s you" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "一覧の検索" + "value" : "Touch your security key to verify that it’s you" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "목록 검색하기" + "value" : "Touch your security key to verify that it’s you" } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Zoek lijst" + "value" : "Touch your security key to verify that it’s you" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Przeszukaj listę" + "value" : "Touch your security key to verify that it’s you" } }, "pt-BR" : { "stringUnit" : { "state" : "translated", - "value" : "Lista de procura" + "value" : "Touch your security key to verify that it’s you" } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Поиск по списку" + "value" : "Touch your security key to verify that it’s you" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Arama listesi" + "value" : "Touch your security key to verify that it’s you" } }, "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Список знайденого" + "value" : "Touch your security key to verify that it’s you" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "搜索列表" + "value" : "Touch your security key to verify that it’s you" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "搜尋列表" + "value" : "Touch your security key to verify that it’s you" } } } }, "Select" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "حدد" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -16530,6 +20287,12 @@ "value" : "Auswählen" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Επιλογή" + } + }, "es" : { "stringUnit" : { "state" : "translated", @@ -16624,6 +20387,12 @@ }, "Selected" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "المحدد" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -16636,6 +20405,12 @@ "value" : "Ausgewählt" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Επιλεγμένα" + } + }, "es" : { "stringUnit" : { "state" : "translated", @@ -16731,6 +20506,12 @@ "SelectTrustedPhone" : { "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "حدد رقم هاتف موثوقًا به لتلقي رمز مكون من %d عبر رسالة نصية قصيرة:" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -16743,6 +20524,12 @@ "value" : "Wähle eine vertrauenswürdige Telefonnummer aus, um einen %d-stelligen Code via SMS zum empfangen:" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Επιλέξτε έναν αξιόπιστο τηλεφωνικό αριθμό για να λάβετε έναν %d-ψήφιο κωδικό με SMS:" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -16843,6 +20630,12 @@ }, "SendSMS" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "Send SMS" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -16855,6 +20648,12 @@ "value" : "SMS senden" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Αποστολή SMS" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -16955,24 +20754,84 @@ }, "ShowOpenInRosetta" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "إظهار خيار فتح في Rosetta" + } + }, + "ca" : { + "stringUnit" : { + "state" : "translated", + "value" : "Show Open In Rosetta option" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Show Open In Rosetta option" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Εμφάνιση της επιλογής «Άνοιγμα με Rosetta»" + } + }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Show Open In Rosetta option" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Show Open In Rosetta option" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Show Open In Rosetta option" + } + }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Afficher l'option Ouvrir avec Rosetta" } }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Show Open In Rosetta option" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Show Open In Rosetta option" + } + }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Rosettaで開くオプションを表示する" } }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "'Rosetta를 사용하여 열기' 옵션 표시하기" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Show Open In Rosetta option" + } + }, "pl" : { "stringUnit" : { "state" : "translated", @@ -16985,40 +20844,118 @@ "value" : "Mostrar opçao abrir com Rosetta" } }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Показать опцию «Открыть в Rosetta»" + } + }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Rosetta ile açma seçeneğini göster" } }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Показувати опцію \"Запустити через Rosetta\"" + } + }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "显示“在Rosetta中打开”选项" } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "顯示「使用 Rosetta 打開」選項" + } } } }, "ShowOpenInRosettaDescription" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "سيُظهر خيار \"فتح في Rosetta\" الأماكن التي تتوفر فيها وظائف \"فتح\" أخرى. ملاحظة: سيظهر هذا فقط لأجهزة Apple Silicon." + } + }, + "ca" : { + "stringUnit" : { + "state" : "translated", + "value" : "Open in Rosetta option will show where other \"Open\" functions are available. Note: This will only show for Apple Silicon machines." + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Open in Rosetta option will show where other \"Open\" functions are available. Note: This will only show for Apple Silicon machines." + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Η επιλογή «Άνοιγμα με Rosetta» θα εμφανίζεται όπου είναι διαθέσιμες οι υπόλοιπες επιλογές «Άνοιγμα». Σημείωση: Θα εμφανίζεται μόνο σε συστήματα με Apple Silicon." + } + }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Open in Rosetta option will show where other \"Open\" functions are available. Note: This will only show for Apple Silicon machines." } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Open in Rosetta option will show where other \"Open\" functions are available. Note: This will only show for Apple Silicon machines." + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Open in Rosetta option will show where other \"Open\" functions are available. Note: This will only show for Apple Silicon machines." + } + }, "fr" : { "stringUnit" : { "state" : "translated", "value" : "Ouvrir avec Rosetta s'affichera d'autres options \"Ouvrir\" sont disponibles. Note: Uniquement pour les machines Apple Silicon." } }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Open in Rosetta option will show where other \"Open\" functions are available. Note: This will only show for Apple Silicon machines." + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Open in Rosetta option will show where other \"Open\" functions are available. Note: This will only show for Apple Silicon machines." + } + }, "ja" : { "stringUnit" : { "state" : "translated", "value" : "Rosettaで開くオプションは \"開く\" 機能が利用できる場所に表示されます。注意: これはApple Siliconマシンにのみ表示されます。" } }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "다른 '열기' 옵션이 가능한 경우 'Rosetta를 사용하여 열기' 옵션을 함께 표시합니다. (이 옵션은 Apple Silicon 기기에서 표시됩니다.)" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Open in Rosetta option will show where other \"Open\" functions are available. Note: This will only show for Apple Silicon machines." + } + }, "pl" : { "stringUnit" : { "state" : "translated", @@ -17031,22 +20968,46 @@ "value" : "Opção de abrir com o Rosetta será mostrar onde outras opções \"Abrir\" estão disponíveis. Nota: Isso só será mostrado pra máquinas Apple Silicon." } }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Опция «Открыть в Rosetta» будет показана там, где доступны другие функции «Открыть». Примечание: опция будет отображаться только для компьютеров Apple Silicon." + } + }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Rosetta ile açma opsiyonu diğer uygun \"Açma\" fonksiyonlarını gösterecektir. Not: Bu sadece Apple Silikon makinelerinde gözükecektir." } }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Опція \"Запустити через Rosetta\" зʼявиться там де наразі знаходиться функція \"Запустити\".\nПримітка: Доступно лише для Apple Silicon машин." + } + }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "“在Rosetta中打开”将会在有其他“打开”方式可用时显示。注:此选项只会在Apple Silicon设备上显示。" } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "「使用 Rosetta 打開」選項會在「打開」選項所在的區塊顯示。注意:這個選項只會在 Apple Silicon 機器顯示。" + } } } }, "SignIn" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تسجيل الدخول" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -17059,6 +21020,12 @@ "value" : "Anmelden" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Σύνδεση" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -17140,7 +21107,7 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Логін" + "value" : "Увійти" } }, "zh-Hans" : { @@ -17160,6 +21127,12 @@ "SignInWithApple" : { "comment" : "SignIn", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تسجيل الدخول بواسطة ابل " + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -17172,6 +21145,12 @@ "value" : "Mit Deiner Apple-ID anmelden." } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Συνδεθείτε με το Apple ID σας." + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -17265,13 +21244,19 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "登入您的Apple ID" + "value" : "使用您的 Apple ID 登入。" } } } }, "SignOut" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تسجيل الخروج" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -17284,6 +21269,12 @@ "value" : "Abmelden" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Αποσύνδεση" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -17384,6 +21375,12 @@ }, "StopInstallation" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "وقف التثبيت" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -17396,6 +21393,12 @@ "value" : "Installation stoppen" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Διακοπή εγκατάστασης" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -17477,7 +21480,7 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Зупинити встановлення" + "value" : "Зупинити інсталяцію" } }, "zh-Hans" : { @@ -17497,17 +21500,261 @@ "Support.Xcodes" : { "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "xcodesدعم " + } + }, + "ca" : { + "stringUnit" : { + "state" : "translated", + "value" : "Support Xcodes" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Support Xcodes" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Υποστηρίξτε το Xcodes" + } + }, "en" : { "stringUnit" : { "state" : "translated", "value" : "Support Xcodes" } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Support Xcodes" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Support Xcodes" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Support Xcodes" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Support Xcodes" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Support Xcodes" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "Support Xcodes" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xcodes 후원하기" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Support Xcodes" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Support Xcodes" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Support Xcodes" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Поддержать Xcodes" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Support Xcodes" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Підтримати Xcodes" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "支持Xcodes" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "支持 Xcodes" + } + } + } + }, + "TerminateAfterLastWindowClosed" : { + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "Terminate App after last window is closed" + } + }, + "ca" : { + "stringUnit" : { + "state" : "translated", + "value" : "Terminate App after last window is closed" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Terminate App after last window is closed" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Terminate App after last window is closed" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Terminate App after last window is closed" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Terminate App after last window is closed" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Terminate App after last window is closed" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Terminate App after last window is closed" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Terminate App after last window is closed" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Terminate App after last window is closed" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "Terminate App after last window is closed" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "Terminate App after last window is closed" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Terminate App after last window is closed" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Terminate App after last window is closed" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Terminate App after last window is closed" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Terminate App after last window is closed" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Terminate App after last window is closed" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Terminate App after last window is closed" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "在最后一个窗口关闭后终止App" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "在最後一個窗口關閉後終止 App" + } } } }, "TrashingArchive" : { "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "نقل الأرشيف إلى سلة المهملات" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -17520,6 +21767,12 @@ "value" : "Archiv in den Papierkorb bewegen" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Μετακίνηση αρχειοθήκης στον Κάδο" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -17613,7 +21866,7 @@ "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "移動封存檔到垃圾桶" + "value" : "正在將封存檔移動至垃圾桶" } } } @@ -17621,6 +21874,12 @@ "Unarchiving" : { "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "إلغاء الأرشفة (قد يستغرق هذا بعض الوقت)" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -17633,6 +21892,12 @@ "value" : "Entpacken (Dies kann etwas dauern)" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Αποσυμπίεση (Ίσως χρειαστεί λίγος χρόνος)" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -17733,6 +21998,12 @@ }, "Uninstall" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "الغاء التثبيت" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -17745,6 +22016,12 @@ "value" : "Deinstallieren" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Απεγκατάσταση" + } + }, "es" : { "stringUnit" : { "state" : "translated", @@ -17839,6 +22116,12 @@ }, "UnxipExperiment" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تجربة فك الضغط" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -17851,6 +22134,12 @@ "value" : "Unxip-Experiment" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Πείραμα Unxip" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -17953,6 +22242,12 @@ "comment" : "MainWindow", "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تم التحديث في" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -17965,6 +22260,12 @@ "value" : "Aktualisiert am" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Τελευταία ενημέρωση" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -18034,7 +22335,7 @@ "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Обновлено в" + "value" : "Обновлено" } }, "tr" : { @@ -18046,7 +22347,7 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Оновлено о" + "value" : "Оновлено" } }, "zh-Hans" : { @@ -18066,6 +22367,12 @@ "Updates" : { "comment" : "Updates Preference Pane", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "التحديثات" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -18078,6 +22385,12 @@ "value" : "Updates" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ενημερώσεις" + } + }, "es" : { "stringUnit" : { "state" : "translated", @@ -18172,6 +22485,12 @@ }, "UseUnxipExperiment" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "عند فك الضغط استخدم التجربة" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -18184,6 +22503,12 @@ "value" : "Beim Unxipping, Experiment verwenden" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Χρήση πειραματικού εργαλείου κατά το unxipping" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -18265,7 +22590,7 @@ "uk" : { "stringUnit" : { "state" : "translated", - "value" : "Під час розпаковки Unxip використовувати експериментальний метод." + "value" : "Експериментальний метод розпаковки" } }, "zh-Hans" : { @@ -18284,6 +22609,12 @@ }, "Versions" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "Versions" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -18296,6 +22627,12 @@ "value" : "Versionen" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Εκδόσεις" + } + }, "es" : { "stringUnit" : { "state" : "translated", @@ -18392,6 +22729,12 @@ "comment" : "About", "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "إصدار ٪@ (٪@)" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -18404,6 +22747,12 @@ "value" : "Version %@ (%@)" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Έκδοση %@ (%@)" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -18506,6 +22855,12 @@ "comment" : "WWDC", "extractionState" : "manual", "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "👨🏻‍💻👩🏼‍💻 سعيد WWDC %@! 👨🏽‍💻🧑🏻‍💻" + } + }, "ca" : { "stringUnit" : { "state" : "translated", @@ -18518,6 +22873,12 @@ "value" : "👨🏻‍💻👩🏼‍💻 Happy WWDC %@! 👨🏽‍💻🧑🏻‍💻" } }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "👨🏻‍💻👩🏼‍💻 Χαρούμενο WWDC %@! 👨🏽‍💻🧑🏻‍💻" + } + }, "en" : { "stringUnit" : { "state" : "translated", @@ -18607,31 +22968,250 @@ "state" : "translated", "value" : "👨🏻‍💻👩🏼‍💻 Happy WWDC %@! 👨🏽‍💻🧑🏻‍💻" } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "👨🏻‍💻👩🏼‍💻 慶祝 WWDC %@!👨🏽‍💻🧑🏻‍💻" + } } } }, "Xcode" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xcode" + } + }, + "ca" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xcode" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xcode" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xcode" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xcode" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xcode" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xcode" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xcode" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xcode" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xcode" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xcode" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xcode" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xcode" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xcode" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xcode" + } + }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Xcode" } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xcode" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xcode" + } } } }, "Xcodes" : { "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xcodes" + } + }, + "ca" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xcodes" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xcodes" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xcodes" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xcodes" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xcodes" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xcodes" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xcodes" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xcodes" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xcodes" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xcodes" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xcodes" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xcodes" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xcodes" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xcodes" + } + }, "tr" : { "stringUnit" : { "state" : "translated", "value" : "Xcodes" } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xcodes" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xcodes" + } } } - }, - "XCODES RULES!" : { - } }, "version" : "1.0" diff --git a/Xcodes/XcodesApp.swift b/Xcodes/XcodesApp.swift index 611341b..a7835dd 100644 --- a/Xcodes/XcodesApp.swift +++ b/Xcodes/XcodesApp.swift @@ -11,7 +11,7 @@ struct XcodesApp: App { @StateObject private var updater = ObservableUpdater() var body: some Scene { - WindowGroup("Xcodes") { + Window("Xcodes", id: "main") { MainWindow() .environmentObject(appState) .environmentObject(updater) @@ -51,19 +51,19 @@ struct XcodesApp: App { CommandGroup(replacing: CommandGroupPlacement.help) { Button("Menu.GitHubRepo") { - let xcodesRepoURL = URL(string: "https://github.com/RobotsAndPencils/XcodesApp/")! + let xcodesRepoURL = URL(string: "https://github.com/XcodesOrg/XcodesApp/")! openURL(xcodesRepoURL) } Divider() Button("Menu.ReportABug") { - let bugReportURL = URL(string: "https://github.com/RobotsAndPencils/XcodesApp/issues/new?assignees=&labels=bug&template=bug_report.md&title=")! + let bugReportURL = URL(string: "https://github.com/XcodesOrg/XcodesApp/issues/new?assignees=&labels=bug&template=bug_report.md&title=")! openURL(bugReportURL) } Button("Menu.RequestNewFeature") { - let featureRequestURL = URL(string: "https://github.com/RobotsAndPencils/XcodesApp/issues/new?assignees=&labels=enhancement&template=feature_request.md&title=")! + let featureRequestURL = URL(string: "https://github.com/XcodesOrg/XcodesApp/issues/new?assignees=&labels=enhancement&template=feature_request.md&title=")! openURL(featureRequestURL) } } @@ -166,12 +166,12 @@ class AppDelegate: NSObject, NSApplicationDelegate { } func applicationDidFinishLaunching(_: Notification) {} + + func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { + return Current.defaults.bool(forKey: "terminateAfterLastWindowClosed") ?? false + } } func localizeString(_ key: String, comment: String = "") -> String { - if #available(macOS 12, *) { - return String(localized: String.LocalizationValue(key)) - } else { - return NSLocalizedString(key, comment: comment) - } + return String(localized: String.LocalizationValue(key)) } diff --git a/Xcodes/XcodesKit/Sources/XcodesKit/Models/Runtimes/Runtimes.swift b/Xcodes/XcodesKit/Sources/XcodesKit/Models/Runtimes/Runtimes.swift index 26289ee..357fa23 100644 --- a/Xcodes/XcodesKit/Sources/XcodesKit/Models/Runtimes/Runtimes.swift +++ b/Xcodes/XcodesKit/Sources/XcodesKit/Models/Runtimes/Runtimes.swift @@ -11,7 +11,8 @@ public struct DownloadableRuntimesResponse: Codable { public struct DownloadableRuntime: Codable, Identifiable, Hashable { public let category: Category public let simulatorVersion: SimulatorVersion - public let source: String + public let source: String? + public let architectures: [String]? public let dictionaryVersion: Int public let contentType: ContentType public let platform: Platform @@ -21,16 +22,19 @@ public struct DownloadableRuntime: Codable, Identifiable, Hashable { public let hostRequirements: HostRequirements? public let name: String public let authentication: Authentication? - public var url: URL { - return URL(string: source)! + public var url: URL? { + if let source { + return URL(string: source)! + } + return nil } - public var downloadPath: String { - url.path + public var downloadPath: String? { + url?.path } // dynamically updated - not decoded public var installState: RuntimeInstallState = .notInstalled - public var sdkBuildUpdate: String? + public var sdkBuildUpdate: [String]? enum CodingKeys: CodingKey { case category @@ -46,10 +50,11 @@ public struct DownloadableRuntime: Codable, Identifiable, Hashable { case name case authentication case sdkBuildUpdate + case architectures } var betaNumber: Int? { - enum Regex { static let shared = try! NSRegularExpression(pattern: "b[0-9]+$") } + enum Regex { static let shared = try! NSRegularExpression(pattern: "b[0-9]+") } guard var foundString = Regex.shared.firstString(in: identifier) else { return nil } foundString.removeFirst() return Int(foundString)! @@ -91,6 +96,7 @@ public struct SDKToSimulatorMapping: Codable { public let sdkBuildUpdate: String public let simulatorBuildUpdate: String public let sdkIdentifier: String + public let downloadableIdentifiers: [String]? } extension DownloadableRuntime { @@ -117,6 +123,7 @@ extension DownloadableRuntime { public enum ContentType: String, Codable { case diskImage = "diskImage" case package = "package" + case cryptexDiskImage = "cryptexDiskImage" } public enum Platform: String, Codable { diff --git a/Xcodes/XcodesKit/Sources/XcodesKit/Models/XcodeInstallationStep.swift b/Xcodes/XcodesKit/Sources/XcodesKit/Models/XcodeInstallationStep.swift index 8d5513d..9a4349e 100644 --- a/Xcodes/XcodesKit/Sources/XcodesKit/Models/XcodeInstallationStep.swift +++ b/Xcodes/XcodesKit/Sources/XcodesKit/Models/XcodeInstallationStep.swift @@ -9,6 +9,7 @@ import Foundation // A numbered step public enum XcodeInstallationStep: Equatable, CustomStringConvertible { + case authenticating case downloading(progress: Progress) case unarchiving case moving(destination: String) @@ -22,6 +23,8 @@ public enum XcodeInstallationStep: Equatable, CustomStringConvertible { public var message: String { switch self { + case .authenticating: + return localizeString("Authenticating") case .downloading: return localizeString("Downloading") case .unarchiving: @@ -39,16 +42,17 @@ public enum XcodeInstallationStep: Equatable, CustomStringConvertible { public var stepNumber: Int { switch self { - case .downloading: return 1 - case .unarchiving: return 2 - case .moving: return 3 - case .trashingArchive: return 4 - case .checkingSecurity: return 5 - case .finishing: return 6 + case .authenticating: return 1 + case .downloading: return 2 + case .unarchiving: return 3 + case .moving: return 4 + case .trashingArchive: return 5 + case .checkingSecurity: return 6 + case .finishing: return 7 } } - public var stepCount: Int { 6 } + public var stepCount: Int { 7 } } func localizeString(_ key: String, comment: String = "") -> String { diff --git a/Xcodes/XcodesKit/Sources/XcodesKit/Services/RuntimeService.swift b/Xcodes/XcodesKit/Sources/XcodesKit/Services/RuntimeService.swift index 3250801..1efbc2e 100644 --- a/Xcodes/XcodesKit/Sources/XcodesKit/Services/RuntimeService.swift +++ b/Xcodes/XcodesKit/Sources/XcodesKit/Services/RuntimeService.swift @@ -22,9 +22,14 @@ public struct RuntimeService { // Apple gives a plist for download let (data, _) = try await networkService.requestData(urlRequest, validators: []) - let decodedResponse = try PropertyListDecoder().decode(DownloadableRuntimesResponse.self, from: data) - - return decodedResponse + do { + let decodedResponse = try PropertyListDecoder().decode(DownloadableRuntimesResponse.self, from: data) + return decodedResponse + } catch { + print("error: \(error)") + throw error + } + } public func installedRuntimes() async throws -> [InstalledRuntime] { diff --git a/XcodesTests/AppStateUpdateTests.swift b/XcodesTests/AppStateUpdateTests.swift index 39d0d21..b510652 100644 --- a/XcodesTests/AppStateUpdateTests.swift +++ b/XcodesTests/AppStateUpdateTests.swift @@ -1,7 +1,11 @@ import Path +import CryptoKit import Version @testable import Xcodes import XCTest +import CommonCrypto +import BigNum +import SRP class AppStateUpdateTests: XCTestCase { var subject: AppState! @@ -258,4 +262,81 @@ class AppStateUpdateTests: XCTestCase { XCTAssertEqual(subject.allXcodes.map(\.version), [Version("12.4.0")!, Version("12.3.0-RC")!]) XCTAssertEqual(subject.allXcodes.map(\.identicalBuilds), [[], []]) } + + func sha256(data : Data) -> Data { + var hash = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH)) + data.withUnsafeBytes { + _ = CC_SHA256($0.baseAddress, CC_LONG(data.count), &hash) + } + return Data(hash) + } + + func testSIRP() throws { + /* + Obtaind by running fastlane spaceauth --verbose -u anand.appleseed@example.com with some custom logging + + Starting SIRP Apple ID login + a: {"a":"VLEKLa+n2cyeYNWbECm45CuS4kCdCxodlTDGlW1FKaUyOrv/RbtN2sM0pVE12zI7k3VkocPC3rN5DZBIkahR6I8JHj/J97dtTvzsR+aNRWTYCT2HGP1PBI0QArp3eitAbFqTWI4+Zw+oOnV8+AYdH/wjbq7gOK4C4dvIHE+FzRwIlmguPb5qu2r47R9W3y1msVdoUGlFBOMOMb7Gyq7F0MaEIFH63lNzGomwq74mfss/cFqurd6fxU+Y7tdVTPZw1GWyBEPiXWpk8sNm2zE+S6zWo5tOsICprU75IC9galh1igfzN7VNe0SUFLNFTbFK+Bb1SFAOrAbBZOmyOG5uSQ==","accountName":"anand.appleseed@example.com","protocols":["s2k","s2k_fo"]} + Received SIRP signin init response: {"iteration"=>20309, "salt"=>"fIjNflgqSJXACWwwvhDU+w==", "protocol"=>"s2k", "b"=>"PMbU75wwG6rDTySXn2ASWyfQuPoW5ham15SzIscpInwOPE2uk7sePsW4ra0dHcLDUMFQn/LgBggIKOo7YZ9hf1VReiAzXwSKSHdJHjHUURTC2eNpANGUPO1qzuXYgc/MP3MR+GipKHsz+KTLT+8wLjNaiCIHsL/7evJBMw9QqiwhyXlAIm5mGZfhdTVbGpLz2/QzrFmI6pUTrHpio6m1Q74DH3FBxxIeiIcuEdGdeVt9iUweowBRyf2woasTvSV1fbMQbl+lsWPwzt/a73+J30eOGFdSubqSVYh2pV0OLqRz7zPzJars12teCWUV+0WUIaxb14Mp7tlmqcTPuqZe9w==", "c"=>"d-533-eccbc4e9-9564-11ef-84a6-018111c8cc60:PRN"} + encrypted_password: 40532b4de9353fc537dc62ee84eacebd7ecb5ec26efca98bd01b0380e302100f 32 + bb: 7672345903537871991962715758896796468138571329014139041563495295907370682045347022183702983061785424983278857706335295545994877883818377653653442007828499221881058994644619578239367613808278802931379172730746665773282250642455690715139985911303055104847971308813151718669484181874342088801251592138154023949370621963319928723678385968989085032385411532317797659749008135679504901238396934480214258070495365760319076978872181485178648397361564241555189629889320567561713407566532187413091018319494367244540399242523126294027225387432028960726767445027313453858210115810946641002311734776929442587065438110307439763191 + x: 726436461883978586175291668001486484510457416477927591386889224605776454162 + u: 49415306980415573732801389514223606278850554555635359953422678270536095422201 + private 23161374166158551996079451276150657702422963034121842124445818241826569345033578345120284496449280736328513302994568402583647660370960353252836732307301957364261384324957527103960720408713825427474127658415917826326829664923997096839970397226662116904369925262192683131695683487505523842260218490007066160096482662715246662018133837725691586205535995663334471723776536640973591229093933458552240634178864509015968350855952558520147559154646484379002445961375384929682566525908284011230815908584648931495968206840416022306138033496705677078512266958633477047047323620540878121579549170392075029336954975132431417099801 + S: 4f75b6ea99c2d7d121357cce80c75c8e1bf74a65e8f66f75f8f66a481301afb8bebf0e54a3fac4f8bfdd60c77d6e670c87968b341f62175e25eb1d4f496e4e6596e1a387f2840688a35002419b70115b7902a46544cc7b31eb4c909c0acaeb752835d1562a687c431421510ebc7535c007a2bd12a4f7696c8c96a75a491b1eb9189ade2bef23dd5b0bb962b4f03e7fba7f6ba6fe67ba34cc18647daf3e474876f85dac5a15eb51c99d1ed78783579ffd6c8b6911f72564d87dc8f76519c8fc1535b83743ed5f7d6b8461d7154ce2a874cbb45bf63018352b9b997fbafbd6b15eac2a544a801c0152470796f3b69a84a4a653e5186b30efeeb148ff3c32ab8e08 + K: c5207f707186a52f1adee41bf0a7bc41e51e6dffc25cdaeca8acb7de2710b20a + hN: 65908899099613711898698321155848703477601840791750658211391687862255842366922 + hG: 23094592799618609623465742609366149076596436609130823198107630312273622653270 + hxor 73599884097654065452785151481733181870375477364472235597514429707329935690908 + response: {"accountName":"anand.appleseed@example.com","c":"d-533-eccbc4e9-9564-11ef-84a6-018111c8cc60:PRN","m1":"f/Bkq8gBTYxl7SyiRd4SXTyE/jM/g6E0mVyZIQDIsPg=","m2":"R2rgqC9cMAtWiXUImOrvs4oF+ccibf8KaFsZQ22WokM=","rememberMe":false} + */ + + let publicKey = Data(base64Encoded: "VLEKLa+n2cyeYNWbECm45CuS4kCdCxodlTDGlW1FKaUyOrv/RbtN2sM0pVE12zI7k3VkocPC3rN5DZBIkahR6I8JHj/J97dtTvzsR+aNRWTYCT2HGP1PBI0QArp3eitAbFqTWI4+Zw+oOnV8+AYdH/wjbq7gOK4C4dvIHE+FzRwIlmguPb5qu2r47R9W3y1msVdoUGlFBOMOMb7Gyq7F0MaEIFH63lNzGomwq74mfss/cFqurd6fxU+Y7tdVTPZw1GWyBEPiXWpk8sNm2zE+S6zWo5tOsICprU75IC9galh1igfzN7VNe0SUFLNFTbFK+Bb1SFAOrAbBZOmyOG5uSQ==".data(using: .utf8)!) + + let clientKeys = SRPKeyPair(public: .init([UInt8](publicKey!)), + private: .init(BigNum("23161374166158551996079451276150657702422963034121842124445818241826569345033578345120284496449280736328513302994568402583647660370960353252836732307301957364261384324957527103960720408713825427474127658415917826326829664923997096839970397226662116904369925262192683131695683487505523842260218490007066160096482662715246662018133837725691586205535995663334471723776536640973591229093933458552240634178864509015968350855952558520147559154646484379002445961375384929682566525908284011230815908584648931495968206840416022306138033496705677078512266958633477047047323620540878121579549170392075029336954975132431417099801")!)) + + let password = sha256(data: "example".data(using: .utf8)!) + let salt = Data(base64Encoded: "fIjNflgqSJXACWwwvhDU+w==".data(using: .utf8)!)! + let iterations: Int = 20309 + let derivedKeyLength: Int = 32 + var keyArray = Array(repeating: 0, count: derivedKeyLength) + + let result:Int32 = keyArray.withUnsafeMutableBytes { keyBytes -> Int32 in + let keyBuffer = UnsafeMutablePointer(keyBytes.baseAddress!.assumingMemoryBound(to: UInt8.self)) + return password.withUnsafeBytes { passwordDigestBytes -> Int32 in + let passwordBuffer = UnsafePointer(passwordDigestBytes.baseAddress!.assumingMemoryBound(to: UInt8.self)) + return salt.withUnsafeBytes { saltBytes -> Int32 in + let saltBuffer = UnsafePointer(saltBytes.baseAddress!.assumingMemoryBound(to: UInt8.self)) + return CCKeyDerivationPBKDF( + CCPBKDFAlgorithm(kCCPBKDF2), + passwordBuffer, + password.count, + saltBuffer, + salt.count, + CCPseudoRandomAlgorithm(kCCPRFHmacAlgSHA256), + UInt32(iterations), + keyBuffer, + derivedKeyLength) + + } + } + } + + let expectedKey: [UInt8] = [0x40, 0x53, 0x2b, 0x4d, 0xe9, 0x35, 0x3f, 0xc5, 0x37, 0xdc, 0x62, 0xee, 0x84, 0xea, 0xce, 0xbd, 0x7e, 0xcb, 0x5e, 0xc2, 0x6e, 0xfc, 0xa9, 0x8b, 0xd0, 0x1b, 0x03, 0x80, 0xe3, 0x02, 0x10, 0x0f] + + XCTAssertEqual(expectedKey, keyArray) + + let decodedB = Data(base64Encoded: "PMbU75wwG6rDTySXn2ASWyfQuPoW5ham15SzIscpInwOPE2uk7sePsW4ra0dHcLDUMFQn/LgBggIKOo7YZ9hf1VReiAzXwSKSHdJHjHUURTC2eNpANGUPO1qzuXYgc/MP3MR+GipKHsz+KTLT+8wLjNaiCIHsL/7evJBMw9QqiwhyXlAIm5mGZfhdTVbGpLz2/QzrFmI6pUTrHpio6m1Q74DH3FBxxIeiIcuEdGdeVt9iUweowBRyf2woasTvSV1fbMQbl+lsWPwzt/a73+J30eOGFdSubqSVYh2pV0OLqRz7zPzJars12teCWUV+0WUIaxb14Mp7tlmqcTPuqZe9w==".data(using: .utf8)!)! + + let client = SRPClient(configuration: SRPConfiguration(.N2048)) + let sharedSecret = try client.calculateSharedSecret(password: Data(keyArray), salt: [UInt8](salt), clientKeys: clientKeys, serverPublicKey: .init([UInt8](decodedB))) + + let accountName = "anand.appleseed@example.com" + let m1 = client.calculateClientProof(username: accountName, salt: [UInt8](salt), clientPublicKey: clientKeys.public, serverPublicKey: .init([UInt8](decodedB)), sharedSecret: .init(sharedSecret.bytes)) + let m2 = client.calculateServerProof(clientPublicKey: clientKeys.public, clientProof: m1, sharedSecret: .init([UInt8](sharedSecret.bytes))) + + XCTAssertEqual(Data(m1).base64EncodedString(), "f/Bkq8gBTYxl7SyiRd4SXTyE/jM/g6E0mVyZIQDIsPg=") + XCTAssertEqual(Data(m2).base64EncodedString(), "R2rgqC9cMAtWiXUImOrvs4oF+ccibf8KaFsZQ22WokM=") + } } diff --git a/experiment_dark.png b/experiment_dark.png index bfb498a..9d4ecd6 100644 Binary files a/experiment_dark.png and b/experiment_dark.png differ diff --git a/experiment_light.png b/experiment_light.png index fc71b91..fd0db64 100644 Binary files a/experiment_light.png and b/experiment_light.png differ diff --git a/screenshot.png b/screenshot.png index 5ef110b..3494902 100644 Binary files a/screenshot.png and b/screenshot.png differ diff --git a/screenshot_dark.png b/screenshot_dark.png index 1c2b134..fe1db11 100644 Binary files a/screenshot_dark.png and b/screenshot_dark.png differ diff --git a/screenshot_light.png b/screenshot_light.png index 3d2f8e8..dff35bb 100644 Binary files a/screenshot_light.png and b/screenshot_light.png differ