localization + LazyVStack

This commit is contained in:
cranci1 2025-06-09 14:25:27 +02:00
parent 65f1fcf0d4
commit 73773f3265
3 changed files with 445 additions and 42 deletions

435
Sora/Localizable.xcstrings Normal file
View file

@ -0,0 +1,435 @@
{
"sourceLanguage" : "en",
"strings" : {
"" : {
},
"%lld" : {
},
"%lld Episodes" : {
},
"%lld of %lld" : {
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "new",
"value" : "%1$lld of %2$lld"
}
}
}
},
"%lld-%lld" : {
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "new",
"value" : "%1$lld-%2$lld"
}
}
}
},
"%lld%%" : {
},
"%lld%% seen" : {
},
"•" : {
},
"About" : {
},
"Actively downloading media can be tracked from here." : {
},
"Add Module" : {
},
"AKA Sulfur" : {
},
"All Bookmarks" : {
},
"All Prev" : {
},
"All Watching" : {
},
"AniList ID" : {
},
"AniList Match" : {
},
"AniList.co" : {
},
"App Data" : {
},
"Are you sure you want to delete '%@'?" : {
},
"Are you sure you want to delete all %lld episodes in '%@'?" : {
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "new",
"value" : "Are you sure you want to delete all %1$lld episodes in '%2$@'?"
}
}
}
},
"Are you sure you want to delete all downloaded assets? You can choose to clear only the library while preserving the downloaded files for future use." : {
},
"Are you sure you want to erase all app data? This action cannot be undone." : {
},
"Are you sure you want to remove all files in the Documents folder? This will remove all modules." : {
},
"Author" : {
},
"Bookmark items for an easier access later." : {
},
"Bookmarks" : {
},
"Cancel" : {
},
"Check out some community modules here!" : {
},
"Clear" : {
},
"Clear All Caches" : {
},
"Clear All Downloads" : {
},
"Clear Library Only" : {
},
"Clear Logs" : {
},
"Click the plus button to add a module!" : {
},
"Continue Watching" : {
},
"Copy to Clipboard" : {
},
"Copy URL" : {
},
"cranci1" : {
},
"Current Cache Size" : {
},
"DATA/LOGS" : {
},
"Delete" : {
},
"Delete All" : {
},
"Delete All Downloads" : {
},
"Delete All Episodes" : {
},
"Delete Download" : {
},
"Delete Episode" : {
},
"Download" : {
},
"Download Episode" : {
},
"Downloads" : {
},
"Enter the AniList ID for this media" : {
},
"Episode %lld" : {
},
"Episodes" : {
},
"Episodes might not be available yet or there could be an issue with the source." : {
},
"Erase" : {
},
"Erase App Data" : {
},
"Error" : {
},
"Failed to load contributors" : {
},
"Files Downloaded" : {
},
"General" : {
},
"INFOS" : {
},
"LESS" : {
},
"Library" : {
},
"Loading Episode %lld..." : {
},
"Loading logs..." : {
},
"Loading module information..." : {
},
"Loading Stream" : {
},
"Log Debug Info" : {
},
"Log Filters" : {
},
"Log In with AniList" : {
},
"Log In with Trakt" : {
},
"Log Out from AniList" : {
},
"Log Out from Trakt" : {
},
"Logged in as " : {
},
"Logs" : {
},
"MAIN" : {
},
"Mark All Previous Watched" : {
},
"Mark as Watched" : {
},
"Match with AniList" : {
},
"Matched with: %@" : {
},
"Max Concurrent Downloads" : {
},
"me frfr" : {
},
"Modules" : {
},
"MORE" : {
},
"No Active Downloads" : {
},
"No Data Available" : {
},
"No Downloads" : {
},
"No episodes available" : {
},
"No Episodes Available" : {
},
"No items to continue watching." : {
},
"No matches found" : {
},
"No Module Selected" : {
},
"No Modules" : {
},
"No Results Found" : {
},
"OK" : {
},
"Open Community Library" : {
},
"Open in AniList" : {
},
"Play" : {
},
"Player" : {
},
"Please select a module from settings" : {
},
"Queued" : {
},
"Recently watched content will appear here." : {
},
"Refresh Storage Info" : {
},
"Remove" : {
},
"Remove Documents" : {
},
"Remove from Bookmarks" : {
},
"Remove Item" : {
},
"Reset" : {
},
"Reset AniList ID" : {
},
"Reset Progress" : {
},
"Running Sora %@ - cranci1" : {
},
"Save" : {
},
"Search" : {
},
"Search downloads" : {
},
"Search for something..." : {
},
"Search..." : {
},
"Season %lld" : {
},
"Select Module" : {
},
"Set Custom AniList ID" : {
},
"Settings" : {
},
"Show More (%lld more characters)" : {
},
"Sora" : {
},
"Sort" : {
},
"Storage Used" : {
},
"Tap a title to override the current match." : {
},
"The module provided only a single episode, this is most likely a movie, so we decided to make separate screens for these cases." : {
},
"Trackers" : {
},
"Trakt.tv" : {
},
"Try different keywords" : {
},
"Use TMDB Poster Image" : {
},
"v%@" : {
},
"View All" : {
},
"Watched" : {
},
"Why am I not seeing any episodes?" : {
},
"You have no items saved." : {
},
"Your downloaded episodes will appear here" : {
}
},
"version" : "1.0"
}

View file

@ -123,38 +123,6 @@ struct MediaInfoView: View {
.ignoresSafeArea(.container, edges: .top)
.onAppear {
buttonRefreshTrigger.toggle()
let savedID = UserDefaults.standard.integer(forKey: "custom_anilist_id_\(href)")
if savedID != 0 { customAniListID = savedID }
if let savedPoster = UserDefaults.standard.string(forKey: "tmdbPosterURL_\(href)") {
self.imageUrl = savedPoster
}
if !hasFetched {
DropManager.shared.showDrop(
title: "Fetching Data",
subtitle: "Please wait while fetching.",
duration: 0.5,
icon: UIImage(systemName: "arrow.triangle.2.circlepath")
)
fetchDetails()
if let savedID = UserDefaults.standard.object(forKey: "custom_anilist_id_\(href)") as? Int {
customAniListID = savedID
itemID = savedID
Logger.shared.log("Using custom AniList ID: \(savedID)", type: "Debug")
} else {
fetchMetadataIDIfNeeded()
}
hasFetched = true
AnalyticsManager.shared.sendEvent(
event: "MediaInfoView",
additionalData: ["title": title]
)
}
tabBarController.hideTabBar()
}
.onChange(of: selectedRange) { newValue in
@ -174,12 +142,8 @@ struct MediaInfoView: View {
if let savedPoster = UserDefaults.standard.string(forKey: "tmdbPosterURL_\(href)") {
imageUrl = savedPoster
}
DropManager.shared.showDrop(
title: "Fetching Data",
subtitle: "Please wait while fetching.",
duration: 0.5,
icon: UIImage(systemName: "arrow.triangle.2.circlepath")
)
DropManager.shared.showDrop(title: "Fetching Data", subtitle: "Please wait while fetching.", duration: 0.5, icon: UIImage(systemName: "arrow.triangle.2.circlepath"))
fetchDetails()
if savedCustomID != 0 {
@ -255,11 +219,12 @@ struct MediaInfoView: View {
.clipped()
}
}
VStack(spacing: 0) {
Rectangle()
.fill(Color.clear)
.frame(height: 400)
VStack(alignment: .leading, spacing: 16) {
LazyVStack(alignment: .leading, spacing: 16) {
headerSection
if !episodeLinks.isEmpty {
episodesSection
@ -289,12 +254,11 @@ struct MediaInfoView: View {
.onAppear {
UIScrollView.appearance().bounces = false
}
.ignoresSafeArea(.container, edges: .top)
}
@ViewBuilder
private var headerSection: some View {
VStack(alignment: .leading, spacing: 8) {
LazyVStack(alignment: .leading, spacing: 8) {
Spacer()
HStack(spacing: 16) {
@ -449,7 +413,7 @@ struct MediaInfoView: View {
@ViewBuilder
private var contentSection: some View {
VStack(alignment: .leading, spacing: 20) {
LazyVStack(alignment: .leading, spacing: 20) {
playAndBookmarkSection
if !episodeLinks.isEmpty {

View file

@ -34,6 +34,7 @@
132AF1252D9995F900A0140B /* JSController-Search.swift in Sources */ = {isa = PBXBuildFile; fileRef = 132AF1242D9995F900A0140B /* JSController-Search.swift */; };
13367ECC2DF70698009CB33F /* Nuke in Frameworks */ = {isa = PBXBuildFile; productRef = 13367ECB2DF70698009CB33F /* Nuke */; };
13367ECE2DF70698009CB33F /* NukeUI in Frameworks */ = {isa = PBXBuildFile; productRef = 13367ECD2DF70698009CB33F /* NukeUI */; };
13367ED02DF70819009CB33F /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 13367ECF2DF70819009CB33F /* Localizable.xcstrings */; };
133D7C6E2D2BE2500075467E /* SoraApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 133D7C6D2D2BE2500075467E /* SoraApp.swift */; };
133D7C702D2BE2500075467E /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 133D7C6F2D2BE2500075467E /* ContentView.swift */; };
133D7C722D2BE2520075467E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 133D7C712D2BE2520075467E /* Assets.xcassets */; };
@ -124,6 +125,7 @@
132AF1202D99951700A0140B /* JSController-Streams.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "JSController-Streams.swift"; sourceTree = "<group>"; };
132AF1222D9995C300A0140B /* JSController-Details.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "JSController-Details.swift"; sourceTree = "<group>"; };
132AF1242D9995F900A0140B /* JSController-Search.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "JSController-Search.swift"; sourceTree = "<group>"; };
13367ECF2DF70819009CB33F /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = "<group>"; };
133D7C6A2D2BE2500075467E /* Sulfur.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Sulfur.app; sourceTree = BUILT_PRODUCTS_DIR; };
133D7C6D2D2BE2500075467E /* SoraApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SoraApp.swift; sourceTree = "<group>"; };
133D7C6F2D2BE2500075467E /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
@ -322,6 +324,7 @@
133D7C6F2D2BE2500075467E /* ContentView.swift */,
133D7C712D2BE2520075467E /* Assets.xcassets */,
133D7C732D2BE2520075467E /* Preview Content */,
13367ECF2DF70819009CB33F /* Localizable.xcstrings */,
);
path = Sora;
sourceTree = "<group>";
@ -669,6 +672,7 @@
files = (
133D7C752D2BE2520075467E /* Preview Assets.xcassets in Resources */,
133D7C722D2BE2520075467E /* Assets.xcassets in Resources */,
13367ED02DF70819009CB33F /* Localizable.xcstrings in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};