diff --git a/JellyfinPlayer tvOS/Components/MediaPlayButtonRowView.swift b/JellyfinPlayer tvOS/Components/MediaPlayButtonRowView.swift index 810f7662..ae4cd05d 100644 --- a/JellyfinPlayer tvOS/Components/MediaPlayButtonRowView.swift +++ b/JellyfinPlayer tvOS/Components/MediaPlayButtonRowView.swift @@ -20,7 +20,7 @@ struct MediaPlayButtonRowView: View { NavigationLink(destination: VideoPlayerView(item: viewModel.item).ignoresSafeArea()) { MediaViewActionButton(icon: "play.fill", scrollView: $wrappedScrollView) } - Text(viewModel.item.getItemProgressString() != "" ? "\(viewModel.item.getItemProgressString()) left" : "Play") + Text(viewModel.item.getItemProgressString() != "" ? "\(viewModel.item.getItemProgressString()) left" : R.string.localizable.play()) .font(.caption) } VStack { diff --git a/JellyfinPlayer tvOS/Components/PortraitItemElement.swift b/JellyfinPlayer tvOS/Components/PortraitItemElement.swift index e2290ee3..a3fc3127 100644 --- a/JellyfinPlayer tvOS/Components/PortraitItemElement.swift +++ b/JellyfinPlayer tvOS/Components/PortraitItemElement.swift @@ -68,7 +68,7 @@ struct PortraitItemElement: View { .font(.caption) .fontWeight(.medium) } else { - Text("S\(String(item.parentIndexNumber ?? 0)):E\(String(item.indexNumber ?? 0))") + Text(R.string.localizable.seasonAndEpisode(String(item.parentIndexNumber ?? 0), String(item.indexNumber ?? 0))) .foregroundColor(.secondary) .font(.caption) .fontWeight(.medium) diff --git a/JellyfinPlayer tvOS/Views/BasicAppSettingsView.swift b/JellyfinPlayer tvOS/Views/BasicAppSettingsView.swift index 2ff8fe9b..6be6811f 100644 --- a/JellyfinPlayer tvOS/Views/BasicAppSettingsView.swift +++ b/JellyfinPlayer tvOS/Views/BasicAppSettingsView.swift @@ -22,7 +22,7 @@ struct BasicAppSettingsView: View { var body: some View { Form { Section { - Picker(NSLocalizedString("Appearance", comment: ""), selection: $appAppearance) { + Picker(R.string.localizable.appearance(), selection: $appAppearance) { ForEach(self.viewModel.appearances, id: \.self) { appearance in Text(appearance.localizedName).tag(appearance.rawValue) } @@ -30,21 +30,21 @@ struct BasicAppSettingsView: View { UIApplication.shared.windows.first?.overrideUserInterfaceStyle = appAppearance.style }) } header: { - Text("Accessibility") + R.string.localizable.accessibility.text } Button { resetTapped = true } label: { - Text("Reset") + R.string.localizable.reset.text } } - .alert("Reset", isPresented: $resetTapped, actions: { + .alert(R.string.localizable.reset(), isPresented: $resetTapped, actions: { Button(role: .destructive) { viewModel.reset() basicAppSettingsRouter.dismissCoordinator() } label: { - Text("Reset") + R.string.localizable.reset.text } }) .navigationTitle("Settings") diff --git a/JellyfinPlayer tvOS/Views/ConnectToServerView.swift b/JellyfinPlayer tvOS/Views/ConnectToServerView.swift index ce981f88..e3f3fb70 100644 --- a/JellyfinPlayer tvOS/Views/ConnectToServerView.swift +++ b/JellyfinPlayer tvOS/Views/ConnectToServerView.swift @@ -17,7 +17,7 @@ struct ConnectToServerView: View { var body: some View { List { Section { - TextField(NSLocalizedString("Server URL", comment: ""), text: $uri) + TextField(R.string.localizable.serverURL(), text: $uri) .disableAutocorrection(true) .autocapitalization(.none) .keyboardType(.URL) @@ -25,7 +25,7 @@ struct ConnectToServerView: View { viewModel.connectToServer(uri: uri) } label: { HStack { - Text("Connect") + R.string.localizable.connect.text Spacer() if viewModel.isLoading { ProgressView() @@ -37,7 +37,7 @@ struct ConnectToServerView: View { Text("Connect to a Jellyfin server") } - Section(header: Text("Local Servers")) { + Section(header: R.string.localizable.localServers.text) { if viewModel.searching { ProgressView() } @@ -68,6 +68,6 @@ struct ConnectToServerView: View { message: Text(viewModel.errorMessage?.displayMessage ?? "Unknown Error"), dismissButton: .cancel()) } - .navigationTitle("Connect") + .navigationTitle(R.string.localizable.connect()) } } diff --git a/JellyfinPlayer tvOS/Views/ContinueWatchingView.swift b/JellyfinPlayer tvOS/Views/ContinueWatchingView.swift index 0ca800bd..466adbad 100644 --- a/JellyfinPlayer tvOS/Views/ContinueWatchingView.swift +++ b/JellyfinPlayer tvOS/Views/ContinueWatchingView.swift @@ -20,7 +20,7 @@ struct ContinueWatchingView: View { var body: some View { VStack(alignment: .leading) { if items.count > 0 { - Text("Continue Watching") + R.string.localizable.continueWatching.text .font(.headline) .fontWeight(.semibold) .padding(.leading, 90) diff --git a/JellyfinPlayer tvOS/Views/HomeView.swift b/JellyfinPlayer tvOS/Views/HomeView.swift index 6ab1d03f..6a85a821 100644 --- a/JellyfinPlayer tvOS/Views/HomeView.swift +++ b/JellyfinPlayer tvOS/Views/HomeView.swift @@ -38,7 +38,7 @@ struct HomeView: View { self.homeRouter.route(to: \.modalLibrary, (.init(parentID: libraryID, filters: viewModel.recentFilterSet), title: library?.name ?? "")) } label: { HStack { - Text("Latest \(library?.name ?? "")") + Text(R.string.localizable.latestWithString(library?.name ?? "")) .font(.headline) .fontWeight(.semibold) Image(systemName: "chevron.forward.circle.fill") diff --git a/JellyfinPlayer tvOS/Views/ItemView/EpisodeItemView.swift b/JellyfinPlayer tvOS/Views/ItemView/EpisodeItemView.swift index 7343fdc9..88f66b49 100644 --- a/JellyfinPlayer tvOS/Views/ItemView/EpisodeItemView.swift +++ b/JellyfinPlayer tvOS/Views/ItemView/EpisodeItemView.swift @@ -76,7 +76,7 @@ struct EpisodeItemView: View { HStack(alignment: .top) { VStack(alignment: .trailing) { if studio != nil { - Text("STUDIO") + R.string.localizable.studio.text .font(.body) .fontWeight(.semibold) .foregroundColor(.primary) @@ -88,7 +88,7 @@ struct EpisodeItemView: View { } if director != nil { - Text("DIRECTOR") + R.string.localizable.director.text .font(.body) .fontWeight(.semibold) .foregroundColor(.primary) @@ -100,7 +100,7 @@ struct EpisodeItemView: View { } if !actors.isEmpty { - Text("CAST") + R.string.localizable.cast.text .font(.body) .fontWeight(.semibold) .foregroundColor(.primary) @@ -133,7 +133,7 @@ struct EpisodeItemView: View { NavigationLink(destination: VideoPlayerView(item: viewModel.item).ignoresSafeArea()) { MediaViewActionButton(icon: "play.fill") } - Text(viewModel.item.getItemProgressString() != "" ? "\(viewModel.item.getItemProgressString()) left" : "Play") + Text(viewModel.item.getItemProgressString() != "" ? "\(viewModel.item.getItemProgressString()) left" : R.string.localizable.play()) .font(.caption) } VStack { @@ -152,7 +152,7 @@ struct EpisodeItemView: View { }.padding(.top, 50) if !viewModel.similarItems.isEmpty { - Text("More Like This") + R.string.localizable.moreLikeThis.text .font(.headline) .fontWeight(.semibold) ScrollView(.horizontal) { diff --git a/JellyfinPlayer tvOS/Views/ItemView/ItemView.swift b/JellyfinPlayer tvOS/Views/ItemView/ItemView.swift index b62d0702..db2a37e4 100644 --- a/JellyfinPlayer tvOS/Views/ItemView/ItemView.swift +++ b/JellyfinPlayer tvOS/Views/ItemView/ItemView.swift @@ -39,8 +39,8 @@ struct ItemView: View { SeasonItemView(viewModel: .init(item: item)) } else if item.type == "Episode" { EpisodeItemView(viewModel: .init(item: item)) - } else { - Text("Type: \(item.type ?? "") not implemented yet :(") + } else { + Text(R.string.localizable.notImplementedYetWithType(item.type ?? "")) } } } diff --git a/JellyfinPlayer tvOS/Views/ItemView/MovieItemView.swift b/JellyfinPlayer tvOS/Views/ItemView/MovieItemView.swift index e24db62b..e9976be7 100644 --- a/JellyfinPlayer tvOS/Views/ItemView/MovieItemView.swift +++ b/JellyfinPlayer tvOS/Views/ItemView/MovieItemView.swift @@ -77,7 +77,7 @@ struct MovieItemView: View { HStack { VStack(alignment: .trailing) { if studio != nil { - Text("STUDIO") + R.string.localizable.studio.text .font(.body) .fontWeight(.semibold) .foregroundColor(.primary) @@ -89,7 +89,7 @@ struct MovieItemView: View { } if director != nil { - Text("DIRECTOR") + R.string.localizable.director.text .font(.body) .fontWeight(.semibold) .foregroundColor(.primary) @@ -101,7 +101,7 @@ struct MovieItemView: View { } if !actors.isEmpty { - Text("CAST") + R.string.localizable.cast.text .font(.body) .fontWeight(.semibold) .foregroundColor(.primary) @@ -133,7 +133,7 @@ struct MovieItemView: View { }.padding(.top, 50) if !viewModel.similarItems.isEmpty { - Text("More Like This") + R.string.localizable.moreLikeThis.text .font(.headline) .fontWeight(.semibold) ScrollView(.horizontal) { diff --git a/JellyfinPlayer tvOS/Views/ItemView/SeasonItemView.swift b/JellyfinPlayer tvOS/Views/ItemView/SeasonItemView.swift index b04a1c6b..64fe58cc 100644 --- a/JellyfinPlayer tvOS/Views/ItemView/SeasonItemView.swift +++ b/JellyfinPlayer tvOS/Views/ItemView/SeasonItemView.swift @@ -95,7 +95,7 @@ struct SeasonItemView: View { }.padding(.top, 50) if !viewModel.episodes.isEmpty { - Text("Episodes") + R.string.localizable.episodes.text .font(.headline) .fontWeight(.semibold) ScrollView(.horizontal) { diff --git a/JellyfinPlayer tvOS/Views/ItemView/SeriesItemView.swift b/JellyfinPlayer tvOS/Views/ItemView/SeriesItemView.swift index 84c46316..200f2e65 100644 --- a/JellyfinPlayer tvOS/Views/ItemView/SeriesItemView.swift +++ b/JellyfinPlayer tvOS/Views/ItemView/SeriesItemView.swift @@ -79,7 +79,7 @@ struct SeriesItemView: View { HStack { VStack(alignment: .trailing) { if studio != nil { - Text("STUDIO") + R.string.localizable.studio.text .font(.body) .fontWeight(.semibold) .foregroundColor(.primary) @@ -91,7 +91,7 @@ struct SeriesItemView: View { } if director != nil { - Text("DIRECTOR") + R.string.localizable.director.text .font(.body) .fontWeight(.semibold) .foregroundColor(.primary) @@ -103,7 +103,7 @@ struct SeriesItemView: View { } if !actors.isEmpty { - Text("CAST") + R.string.localizable.cast.text .font(.body) .fontWeight(.semibold) .foregroundColor(.primary) @@ -135,7 +135,7 @@ struct SeriesItemView: View { } }.padding(.top, 50) if !viewModel.seasons.isEmpty { - Text("Seasons") + R.string.localizable.seasons.text .font(.headline) .fontWeight(.semibold) ScrollView(.horizontal) { @@ -153,7 +153,7 @@ struct SeriesItemView: View { } if !viewModel.similarItems.isEmpty { - Text("More Like This") + R.string.localizable.moreLikeThis.text .font(.headline) .fontWeight(.semibold) ScrollView(.horizontal) { diff --git a/JellyfinPlayer tvOS/Views/LibraryFilterView.swift b/JellyfinPlayer tvOS/Views/LibraryFilterView.swift index daf9c75c..5e0503eb 100644 --- a/JellyfinPlayer tvOS/Views/LibraryFilterView.swift +++ b/JellyfinPlayer tvOS/Views/LibraryFilterView.swift @@ -31,32 +31,32 @@ struct LibraryFilterView: View { } else { Form { if viewModel.enabledFilterType.contains(.genre) { - MultiSelector(label: NSLocalizedString("Genres", comment: ""), + MultiSelector(label: R.string.localizable.genres(), options: viewModel.possibleGenres, optionToString: { $0.name ?? "" }, selected: $viewModel.modifiedFilters.withGenres) } if viewModel.enabledFilterType.contains(.filter) { - MultiSelector(label: NSLocalizedString("Filters", comment: ""), + MultiSelector(label: R.string.localizable.filters(), options: viewModel.possibleItemFilters, optionToString: { $0.localized }, selected: $viewModel.modifiedFilters.filters) } if viewModel.enabledFilterType.contains(.tag) { - MultiSelector(label: NSLocalizedString("Tags", comment: ""), + MultiSelector(label: R.string.localizable.tags(), options: viewModel.possibleTags, optionToString: { $0 }, selected: $viewModel.modifiedFilters.tags) } if viewModel.enabledFilterType.contains(.sortBy) { - Picker(selection: $viewModel.selectedSortBy, label: Text("Sort by")) { + Picker(selection: $viewModel.selectedSortBy, label: R.string.localizable.sortBy.text) { ForEach(viewModel.possibleSortBys, id: \.self) { so in Text(so.localized).tag(so) } } } if viewModel.enabledFilterType.contains(.sortOrder) { - Picker(selection: $viewModel.selectedSortOrder, label: Text("Display order")) { + Picker(selection: $viewModel.selectedSortOrder, label: R.string.localizable.displayOrder.text) { ForEach(viewModel.possibleSortOrders, id: \.self) { so in Text(so.rawValue).tag(so) } @@ -68,7 +68,7 @@ struct LibraryFilterView: View { self.filters = viewModel.modifiedFilters filterRouter.dismissCoordinator() } label: { - Text("Reset") + R.string.localizable.reset.text } } } @@ -86,7 +86,7 @@ struct LibraryFilterView: View { self.filters = viewModel.modifiedFilters filterRouter.dismissCoordinator() } label: { - Text("Apply") + R.string.localizable.apply.text } } } diff --git a/JellyfinPlayer tvOS/Views/LibrarySearchView.swift b/JellyfinPlayer tvOS/Views/LibrarySearchView.swift index db3490bb..85a7d8cd 100644 --- a/JellyfinPlayer tvOS/Views/LibrarySearchView.swift +++ b/JellyfinPlayer tvOS/Views/LibrarySearchView.swift @@ -44,7 +44,7 @@ struct LibrarySearchView: View { var suggestionsListView: some View { ScrollView { LazyVStack(spacing: 8) { - Text("Suggestions") + R.string.localizable.suggestions.text .font(.headline) .fontWeight(.bold) .foregroundColor(.primary) diff --git a/JellyfinPlayer tvOS/Views/LibraryView.swift b/JellyfinPlayer tvOS/Views/LibraryView.swift index ca272c9e..d7fe3980 100644 --- a/JellyfinPlayer tvOS/Views/LibraryView.swift +++ b/JellyfinPlayer tvOS/Views/LibraryView.swift @@ -88,7 +88,7 @@ struct LibraryView: View { .ignoresSafeArea(.all) } else { VStack { - Text("No results.") + R.string.localizable.noResults.text Button { } label: { Text("Reload") } diff --git a/JellyfinPlayer tvOS/Views/MovieLibrariesView.swift b/JellyfinPlayer tvOS/Views/MovieLibrariesView.swift index 9e388718..22742484 100644 --- a/JellyfinPlayer tvOS/Views/MovieLibrariesView.swift +++ b/JellyfinPlayer tvOS/Views/MovieLibrariesView.swift @@ -77,7 +77,7 @@ struct MovieLibrariesView: View { .ignoresSafeArea(.all) } else { VStack { - Text("No results.") + R.string.localizable.noResults.text Button { print("movieLibraries reload") } label: { diff --git a/JellyfinPlayer tvOS/Views/NextUpView.swift b/JellyfinPlayer tvOS/Views/NextUpView.swift index 3de41d6b..e60cfb50 100644 --- a/JellyfinPlayer tvOS/Views/NextUpView.swift +++ b/JellyfinPlayer tvOS/Views/NextUpView.swift @@ -19,7 +19,7 @@ struct NextUpView: View { var body: some View { VStack(alignment: .leading) { if items.count > 0 { - Text("Next Up") + R.string.localizable.nextUp.text .font(.headline) .fontWeight(.semibold) .padding(.leading, 90) diff --git a/JellyfinPlayer tvOS/Views/ServerListView.swift b/JellyfinPlayer tvOS/Views/ServerListView.swift index 5ca66b3f..4bb23f4b 100644 --- a/JellyfinPlayer tvOS/Views/ServerListView.swift +++ b/JellyfinPlayer tvOS/Views/ServerListView.swift @@ -72,7 +72,7 @@ struct ServerListView: View { Button { serverListRouter.route(to: \.connectToServer) } label: { - Text("Connect") + R.string.localizable.connect.text .bold() .font(.callout) } diff --git a/JellyfinPlayer tvOS/Views/SettingsView.swift b/JellyfinPlayer tvOS/Views/SettingsView.swift index 7442eeeb..b63b31e6 100644 --- a/JellyfinPlayer tvOS/Views/SettingsView.swift +++ b/JellyfinPlayer tvOS/Views/SettingsView.swift @@ -22,7 +22,7 @@ struct SettingsView: View { var body: some View { Form { - Section(header: Text("Playback settings")) { + Section(header: R.string.localizable.playbackSettings()) { Picker("Default local quality", selection: $inNetworkStreamBitrate) { ForEach(self.viewModel.bitrates, id: \.self) { bitrate in Text(bitrate.name).tag(bitrate.value) @@ -36,7 +36,7 @@ struct SettingsView: View { } } - Section(header: Text("Accessibility")) { + Section(header: R.string.localizable.accessibility.text) { Toggle("Automatically show subtitles", isOn: $isAutoSelectSubtitles) SearchablePicker(label: "Preferred subtitle language", options: viewModel.langs, @@ -58,12 +58,12 @@ struct SettingsView: View { Section(header: Text(SessionManager.main.currentLogin.server.name)) { HStack { - Text("Signed in as \(SessionManager.main.currentLogin.user.username)").foregroundColor(.primary) + Text(R.string.localizable.signedInAsWithString(SessionManager.main.currentLogin.user.username))).foregroundColor(.primary) Spacer() Button { SwiftfinNotificationCenter.main.post(name: SwiftfinNotificationCenter.Keys.didSignOut, object: nil) } label: { - Text("Switch user").font(.callout) + R.string.localizable.switchUser.text.font(.callout) } } Button { diff --git a/JellyfinPlayer tvOS/Views/TVLibrariesView.swift b/JellyfinPlayer tvOS/Views/TVLibrariesView.swift index 3ae4d8df..c48a389b 100644 --- a/JellyfinPlayer tvOS/Views/TVLibrariesView.swift +++ b/JellyfinPlayer tvOS/Views/TVLibrariesView.swift @@ -77,7 +77,7 @@ struct TVLibrariesView: View { .ignoresSafeArea(.all) } else { VStack { - Text("No results.") + R.string.localizable.noResults.text Button { print("tvLibraries reload") } label: { diff --git a/JellyfinPlayer tvOS/Views/UserSignInView.swift b/JellyfinPlayer tvOS/Views/UserSignInView.swift index f9b59ba6..e07baeb9 100644 --- a/JellyfinPlayer tvOS/Views/UserSignInView.swift +++ b/JellyfinPlayer tvOS/Views/UserSignInView.swift @@ -20,11 +20,11 @@ struct UserSignInView: View { Form { Section { - TextField("Username", text: $username) + TextField(R.string.localizable.username(), text: $username) .disableAutocorrection(true) .autocapitalization(.none) - SecureField("Password", text: $password) + SecureField(R.string.localizable.password(), text: $password) .disableAutocorrection(true) .autocapitalization(.none) @@ -32,7 +32,7 @@ struct UserSignInView: View { viewModel.login(username: username, password: password) } label: { HStack { - Text("Connect") + R.string.localizable.connect.text Spacer() if viewModel.isLoading { ProgressView() diff --git a/JellyfinPlayer tvOS/Views/VideoPlayer/AudioView.swift b/JellyfinPlayer tvOS/Views/VideoPlayer/AudioView.swift index 3d5b4162..9b198f94 100644 --- a/JellyfinPlayer tvOS/Views/VideoPlayer/AudioView.swift +++ b/JellyfinPlayer tvOS/Views/VideoPlayer/AudioView.swift @@ -14,7 +14,7 @@ class AudioViewController: InfoTabViewController { override func viewDidLoad() { super.viewDidLoad() - tabBarItem.title = NSLocalizedString("Audio", comment: "") + tabBarItem.title = "Audio" } diff --git a/JellyfinPlayer tvOS/Views/VideoPlayer/MediaInfoView.swift b/JellyfinPlayer tvOS/Views/VideoPlayer/MediaInfoView.swift index 94c3a5c0..e914b3ad 100644 --- a/JellyfinPlayer tvOS/Views/VideoPlayer/MediaInfoView.swift +++ b/JellyfinPlayer tvOS/Views/VideoPlayer/MediaInfoView.swift @@ -16,7 +16,7 @@ class MediaInfoViewController: InfoTabViewController { override func viewDidLoad() { super.viewDidLoad() - tabBarItem.title = NSLocalizedString("Info", comment: "") + tabBarItem.title = "Info" } func setMedia(item: BaseItemDto) { diff --git a/JellyfinPlayer tvOS/Views/VideoPlayer/SubtitlesView.swift b/JellyfinPlayer tvOS/Views/VideoPlayer/SubtitlesView.swift index 240d6828..a4e35717 100644 --- a/JellyfinPlayer tvOS/Views/VideoPlayer/SubtitlesView.swift +++ b/JellyfinPlayer tvOS/Views/VideoPlayer/SubtitlesView.swift @@ -14,7 +14,7 @@ class SubtitlesViewController: InfoTabViewController { override func viewDidLoad() { super.viewDidLoad() - tabBarItem.title = NSLocalizedString("Subtitles", comment: "") + tabBarItem.title = "Subtitles" } diff --git a/JellyfinPlayer.xcodeproj/project.pbxproj b/JellyfinPlayer.xcodeproj/project.pbxproj index a07f53b3..76f16358 100644 --- a/JellyfinPlayer.xcodeproj/project.pbxproj +++ b/JellyfinPlayer.xcodeproj/project.pbxproj @@ -15,6 +15,7 @@ 09389CC726819B4600AE350E /* VideoPlayerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09389CC626819B4500AE350E /* VideoPlayerModel.swift */; }; 09389CC826819B4600AE350E /* VideoPlayerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09389CC626819B4500AE350E /* VideoPlayerModel.swift */; }; 0959A5FD2686D29800C7C9A9 /* VideoUpNextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0959A5FC2686D29800C7C9A9 /* VideoUpNextView.swift */; }; + 363CADF08820D3B2055CF1D8 /* Pods_JellyfinPlayer_tvOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4BE2D324B040DCA2629C110D /* Pods_JellyfinPlayer_tvOS.framework */; }; 531069572684E7EE00CFFDBA /* InfoTabBarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531069502684E7EE00CFFDBA /* InfoTabBarViewController.swift */; }; 531069582684E7EE00CFFDBA /* MediaInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531069512684E7EE00CFFDBA /* MediaInfoView.swift */; }; 531069592684E7EE00CFFDBA /* SubtitlesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531069522684E7EE00CFFDBA /* SubtitlesView.swift */; }; @@ -148,15 +149,15 @@ 53DE4BD2267098F300739748 /* SearchBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53DE4BD1267098F300739748 /* SearchBarView.swift */; }; 53DF641E263D9C0600A7CD1A /* LibraryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53DF641D263D9C0600A7CD1A /* LibraryView.swift */; }; 53E4E649263F725B00F67C6B /* MultiSelectorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53E4E648263F725B00F67C6B /* MultiSelectorView.swift */; }; - 53EC6E1E267E80AC006DD26A /* Pods_JellyfinPlayer_tvOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EBFE1F64394BCC2EFFF1610D /* Pods_JellyfinPlayer_tvOS.framework */; }; - 53EC6E21267E80B1006DD26A /* Pods_JellyfinPlayer_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3F905C1D3D3A0C9E13E7A0BC /* Pods_JellyfinPlayer_iOS.framework */; }; 53EE24E6265060780068F029 /* LibrarySearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53EE24E5265060780068F029 /* LibrarySearchView.swift */; }; 53F8377D265FF67C00F456B3 /* VideoPlayerSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53F8377C265FF67C00F456B3 /* VideoPlayerSettingsView.swift */; }; 53F866442687A45F00DCD1D7 /* PortraitItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53F866432687A45F00DCD1D7 /* PortraitItemView.swift */; }; 53FF7F2A263CF3F500585C35 /* LatestMediaView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53FF7F29263CF3F500585C35 /* LatestMediaView.swift */; }; + 560CA59B3956A4CA13EDAC05 /* Pods_JellyfinPlayer_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 86BAC42C3764D232C8DF8F5E /* Pods_JellyfinPlayer_iOS.framework */; }; 62133890265F83A900A81A2A /* LibraryListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6213388F265F83A900A81A2A /* LibraryListView.swift */; }; 621338932660107500A81A2A /* StringExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 621338922660107500A81A2A /* StringExtensions.swift */; }; 621338B32660A07800A81A2A /* LazyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 621338B22660A07800A81A2A /* LazyView.swift */; }; + 62167B882738411700167ECE /* R.generated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6286F0A0271C0AA500C40ED5 /* R.generated.swift */; }; 6220D0AD26D5EABB00B8E046 /* ViewExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6220D0AC26D5EABB00B8E046 /* ViewExtensions.swift */; }; 6220D0AE26D5EABB00B8E046 /* ViewExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6220D0AC26D5EABB00B8E046 /* ViewExtensions.swift */; }; 6220D0AF26D5EABE00B8E046 /* ViewExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6220D0AC26D5EABB00B8E046 /* ViewExtensions.swift */; }; @@ -175,6 +176,7 @@ 625CB5752678C33500530A6E /* LibraryListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 625CB5742678C33500530A6E /* LibraryListViewModel.swift */; }; 625CB5772678C34300530A6E /* ConnectToServerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 625CB5762678C34300530A6E /* ConnectToServerViewModel.swift */; }; 625CB57A2678C4A400530A6E /* ActivityIndicator in Frameworks */ = {isa = PBXBuildFile; productRef = 625CB5792678C4A400530A6E /* ActivityIndicator */; }; + 6264E88A27384A6F0081A12A /* NetworkError.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1FCD08726C35A0D007C8DCF /* NetworkError.swift */; }; 62671DB327159C1800199D95 /* ItemCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6220D0BF26D61C5000B8E046 /* ItemCoordinator.swift */; }; 6267B3D626710B8900A7371D /* CollectionExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6267B3D526710B8900A7371D /* CollectionExtensions.swift */; }; 6267B3D726710B9700A7371D /* CollectionExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6267B3D526710B8900A7371D /* CollectionExtensions.swift */; }; @@ -356,17 +358,17 @@ E1FCD08926C35A0D007C8DCF /* NetworkError.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1FCD08726C35A0D007C8DCF /* NetworkError.swift */; }; E1FCD09626C47118007C8DCF /* ErrorMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1FCD09526C47118007C8DCF /* ErrorMessage.swift */; }; E1FCD09726C47118007C8DCF /* ErrorMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1FCD09526C47118007C8DCF /* ErrorMessage.swift */; }; - E1FCD09926C4F358007C8DCF /* NetworkError.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1FCD08726C35A0D007C8DCF /* NetworkError.swift */; }; E1FCD09A26C4F35A007C8DCF /* ErrorMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1FCD09526C47118007C8DCF /* ErrorMessage.swift */; }; + EABFD69FA6D5DBB248A494AA /* Pods_WidgetExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 59AFF849629F3C787909A911 /* Pods_WidgetExtension.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ - 628B952B2670CABE0091AF3B /* PBXContainerItemProxy */ = { + 6264E888273848760081A12A /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 5377CBE9263B596A003A4E83 /* Project object */; proxyType = 1; remoteGlobalIDString = 628B951F2670CABD0091AF3B; - remoteInfo = WidgetExtensionExtension; + remoteInfo = WidgetExtension; }; /* End PBXContainerItemProxy section */ @@ -398,8 +400,10 @@ 091B5A882683142E00D78B61 /* UDPBroadCastConnection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UDPBroadCastConnection.swift; sourceTree = ""; }; 09389CC626819B4500AE350E /* VideoPlayerModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoPlayerModel.swift; sourceTree = ""; }; 0959A5FC2686D29800C7C9A9 /* VideoUpNextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoUpNextView.swift; sourceTree = ""; }; - 3773C07648173CE7FEC083D5 /* Pods-JellyfinPlayer iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JellyfinPlayer iOS.debug.xcconfig"; path = "Target Support Files/Pods-JellyfinPlayer iOS/Pods-JellyfinPlayer iOS.debug.xcconfig"; sourceTree = ""; }; - 3F905C1D3D3A0C9E13E7A0BC /* Pods_JellyfinPlayer_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_JellyfinPlayer_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 14E199C7BBA98782CAD2F0D4 /* Pods-JellyfinPlayer iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JellyfinPlayer iOS.release.xcconfig"; path = "Target Support Files/Pods-JellyfinPlayer iOS/Pods-JellyfinPlayer iOS.release.xcconfig"; sourceTree = ""; }; + 20CA36DDD247EED8D16438A5 /* Pods-JellyfinPlayer tvOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JellyfinPlayer tvOS.release.xcconfig"; path = "Target Support Files/Pods-JellyfinPlayer tvOS/Pods-JellyfinPlayer tvOS.release.xcconfig"; sourceTree = ""; }; + 4BDCEE3B49CF70A9E9BA3CD8 /* Pods-WidgetExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WidgetExtension.debug.xcconfig"; path = "Target Support Files/Pods-WidgetExtension/Pods-WidgetExtension.debug.xcconfig"; sourceTree = ""; }; + 4BE2D324B040DCA2629C110D /* Pods_JellyfinPlayer_tvOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_JellyfinPlayer_tvOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 531069502684E7EE00CFFDBA /* InfoTabBarViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InfoTabBarViewController.swift; sourceTree = ""; }; 531069512684E7EE00CFFDBA /* MediaInfoView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaInfoView.swift; sourceTree = ""; }; 531069522684E7EE00CFFDBA /* SubtitlesView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubtitlesView.swift; sourceTree = ""; }; @@ -497,6 +501,7 @@ 53F8377C265FF67C00F456B3 /* VideoPlayerSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoPlayerSettingsView.swift; sourceTree = ""; }; 53F866432687A45F00DCD1D7 /* PortraitItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PortraitItemView.swift; sourceTree = ""; }; 53FF7F29263CF3F500585C35 /* LatestMediaView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LatestMediaView.swift; sourceTree = ""; }; + 59AFF849629F3C787909A911 /* Pods_WidgetExtension.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_WidgetExtension.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 6213388F265F83A900A81A2A /* LibraryListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryListView.swift; sourceTree = ""; }; 621338922660107500A81A2A /* StringExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringExtensions.swift; sourceTree = ""; }; 621338B22660A07800A81A2A /* LazyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LazyView.swift; sourceTree = ""; }; @@ -544,8 +549,10 @@ 62EC352E267666A5000E9F2D /* SessionManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionManager.swift; sourceTree = ""; }; 62EC353326766B03000E9F2D /* DeviceRotationViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceRotationViewModifier.swift; sourceTree = ""; }; 62ECA01726FA685A00E8EBB7 /* DeepLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeepLink.swift; sourceTree = ""; }; + 772F6DAB8534FD1DB45B7687 /* Pods-JellyfinPlayer iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JellyfinPlayer iOS.debug.xcconfig"; path = "Target Support Files/Pods-JellyfinPlayer iOS/Pods-JellyfinPlayer iOS.debug.xcconfig"; sourceTree = ""; }; + 86BAC42C3764D232C8DF8F5E /* Pods_JellyfinPlayer_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_JellyfinPlayer_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; AE8C3158265D6F90008AA076 /* bitrates.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = bitrates.json; sourceTree = ""; }; - BEEC50E7EFD4848C0E320941 /* Pods-JellyfinPlayer iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JellyfinPlayer iOS.release.xcconfig"; path = "Target Support Files/Pods-JellyfinPlayer iOS/Pods-JellyfinPlayer iOS.release.xcconfig"; sourceTree = ""; }; + B598C62749E5EFD37280FCC3 /* Pods-JellyfinPlayer tvOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JellyfinPlayer tvOS.debug.xcconfig"; path = "Target Support Files/Pods-JellyfinPlayer tvOS/Pods-JellyfinPlayer tvOS.debug.xcconfig"; sourceTree = ""; }; C40CD921271F8CD8000FB198 /* MoviesLibrariesCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoviesLibrariesCoordinator.swift; sourceTree = ""; }; C40CD924271F8D1E000FB198 /* MovieLibrariesViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MovieLibrariesViewModel.swift; sourceTree = ""; }; C40CD927271F8DAB000FB198 /* MovieLibrariesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MovieLibrariesView.swift; sourceTree = ""; }; @@ -555,8 +562,6 @@ C4BE076D2720FEA8003F4AD1 /* PortraitItemElement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PortraitItemElement.swift; sourceTree = ""; }; C4E508172703E8190045C9AB /* LibraryListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryListView.swift; sourceTree = ""; }; C4E5081C2703F8370045C9AB /* LibrarySearchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibrarySearchView.swift; sourceTree = ""; }; - D79953919FED0C4DF72BA578 /* Pods-JellyfinPlayer tvOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JellyfinPlayer tvOS.release.xcconfig"; path = "Target Support Files/Pods-JellyfinPlayer tvOS/Pods-JellyfinPlayer tvOS.release.xcconfig"; sourceTree = ""; }; - DE5004F745B19E28744A7DE7 /* Pods-JellyfinPlayer tvOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JellyfinPlayer tvOS.debug.xcconfig"; path = "Target Support Files/Pods-JellyfinPlayer tvOS/Pods-JellyfinPlayer tvOS.debug.xcconfig"; sourceTree = ""; }; E100720626BDABC100CE3E31 /* MediaPlayButtonRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaPlayButtonRowView.swift; sourceTree = ""; }; E11B1B6B2718CD68006DA3E8 /* JellyfinAPIError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JellyfinAPIError.swift; sourceTree = ""; }; E1267D3D271A1F46003C492E /* PreferenceUIHostingController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferenceUIHostingController.swift; sourceTree = ""; }; @@ -612,7 +617,7 @@ E1F0204D26CCCA74001C1C3B /* VideoPlayerJumpLength.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoPlayerJumpLength.swift; sourceTree = ""; }; E1FCD08726C35A0D007C8DCF /* NetworkError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkError.swift; sourceTree = ""; }; E1FCD09526C47118007C8DCF /* ErrorMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorMessage.swift; sourceTree = ""; }; - EBFE1F64394BCC2EFFF1610D /* Pods_JellyfinPlayer_tvOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_JellyfinPlayer_tvOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + FDEDADB92FA8523BC8432E45 /* Pods-WidgetExtension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WidgetExtension.release.xcconfig"; path = "Target Support Files/Pods-WidgetExtension/Pods-WidgetExtension.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -622,7 +627,6 @@ files = ( 53649AAF269CFAF600A2D8B7 /* Puppy in Frameworks */, E1218C9E271A2CD600EA0737 /* CombineExt in Frameworks */, - 53EC6E1E267E80AC006DD26A /* Pods_JellyfinPlayer_tvOS.framework in Frameworks */, E1218CA0271A2CF200EA0737 /* Nuke in Frameworks */, 6220D0C926D63F3700B8E046 /* Stinsen in Frameworks */, 53A431BF266B0FFE0016769F /* JellyfinAPI in Frameworks */, @@ -633,6 +637,7 @@ E13DD3CD27164CA7009D4DAF /* CoreStore in Frameworks */, E12186DE2718F1C50010884C /* Defaults in Frameworks */, 53ABFDED26799D7700886593 /* ActivityIndicator in Frameworks */, + 363CADF08820D3B2055CF1D8 /* Pods_JellyfinPlayer_tvOS.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -644,7 +649,6 @@ 53649AAD269CFAEA00A2D8B7 /* Puppy in Frameworks */, 62C29E9C26D0FE4200C1D2E7 /* Stinsen in Frameworks */, E1A99999271A3429008E78C0 /* SwiftUICollection in Frameworks */, - 53EC6E21267E80B1006DD26A /* Pods_JellyfinPlayer_iOS.framework in Frameworks */, E1218C9A271A26BA00EA0737 /* Nuke in Frameworks */, E1B6DCEA271A23880015B715 /* SwiftyJSON in Frameworks */, 53352571265EA0A0006CCA86 /* Introspect in Frameworks */, @@ -652,6 +656,7 @@ 625CB57A2678C4A400530A6E /* ActivityIndicator in Frameworks */, E1B6DCE8271A23780015B715 /* CombineExt in Frameworks */, 53A431BD266B0FF20016769F /* JellyfinAPI in Frameworks */, + 560CA59B3956A4CA13EDAC05 /* Pods_JellyfinPlayer_iOS.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -667,6 +672,7 @@ E13DD3CF27164E1F009D4DAF /* CoreStore in Frameworks */, 628B95352670CAEA0091AF3B /* JellyfinAPI in Frameworks */, E1218C9C271A26C400EA0737 /* Nuke in Frameworks */, + EABFD69FA6D5DBB248A494AA /* Pods_WidgetExtension.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1033,8 +1039,9 @@ 53D5E3DC264B47EE00BADDC8 /* MobileVLCKit.xcframework */, 628B95212670CABD0091AF3B /* WidgetKit.framework */, 628B95232670CABD0091AF3B /* SwiftUI.framework */, - 3F905C1D3D3A0C9E13E7A0BC /* Pods_JellyfinPlayer_iOS.framework */, - EBFE1F64394BCC2EFFF1610D /* Pods_JellyfinPlayer_tvOS.framework */, + 86BAC42C3764D232C8DF8F5E /* Pods_JellyfinPlayer_iOS.framework */, + 4BE2D324B040DCA2629C110D /* Pods_JellyfinPlayer_tvOS.framework */, + 59AFF849629F3C787909A911 /* Pods_WidgetExtension.framework */, ); name = Frameworks; sourceTree = ""; @@ -1140,10 +1147,12 @@ C78797A232E2B8774099D1E9 /* Pods */ = { isa = PBXGroup; children = ( - 3773C07648173CE7FEC083D5 /* Pods-JellyfinPlayer iOS.debug.xcconfig */, - BEEC50E7EFD4848C0E320941 /* Pods-JellyfinPlayer iOS.release.xcconfig */, - DE5004F745B19E28744A7DE7 /* Pods-JellyfinPlayer tvOS.debug.xcconfig */, - D79953919FED0C4DF72BA578 /* Pods-JellyfinPlayer tvOS.release.xcconfig */, + 772F6DAB8534FD1DB45B7687 /* Pods-JellyfinPlayer iOS.debug.xcconfig */, + 14E199C7BBA98782CAD2F0D4 /* Pods-JellyfinPlayer iOS.release.xcconfig */, + B598C62749E5EFD37280FCC3 /* Pods-JellyfinPlayer tvOS.debug.xcconfig */, + 20CA36DDD247EED8D16438A5 /* Pods-JellyfinPlayer tvOS.release.xcconfig */, + 4BDCEE3B49CF70A9E9BA3CD8 /* Pods-WidgetExtension.debug.xcconfig */, + FDEDADB92FA8523BC8432E45 /* Pods-WidgetExtension.release.xcconfig */, ); path = Pods; sourceTree = ""; @@ -1342,12 +1351,12 @@ isa = PBXNativeTarget; buildConfigurationList = 535870712669D21700D05A09 /* Build configuration list for PBXNativeTarget "JellyfinPlayer tvOS" */; buildPhases = ( - E7370E1AA68C6CB254E46F2C /* [CP] Check Pods Manifest.lock */, + 3D0F2756C71CDF6B9EEBD4E0 /* [CP] Check Pods Manifest.lock */, 6286F0A3271C0ABA00C40ED5 /* R.swift */, 5358705C2669D21600D05A09 /* Sources */, 5358705D2669D21600D05A09 /* Frameworks */, 5358705E2669D21600D05A09 /* Resources */, - 6AB6F1DD2C8AD942F71C8A32 /* [CP] Embed Pods Frameworks */, + 879C22C1CCC48E68C86E904C /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -1375,20 +1384,20 @@ isa = PBXNativeTarget; buildConfigurationList = 5377CC1B263B596B003A4E83 /* Build configuration list for PBXNativeTarget "JellyfinPlayer iOS" */; buildPhases = ( - 6435C3C2E610FE34AD537AC1 /* [CP] Check Pods Manifest.lock */, + 1C7487D3432E90546DA855B5 /* [CP] Check Pods Manifest.lock */, 6286F09E271C093000C40ED5 /* R.swift */, 5377CBED263B596A003A4E83 /* Sources */, 5377CBEE263B596A003A4E83 /* Frameworks */, 5377CBEF263B596A003A4E83 /* Resources */, 5302F8322658B74800647A2E /* CopyFiles */, 628B95312670CABE0091AF3B /* Embed App Extensions */, - E8DDF21F62DFCE8CE76666BA /* [CP] Embed Pods Frameworks */, - 83FD120CA10FD0E91DAD83C9 /* [CP] Copy Pods Resources */, + 8D1E0C963DCE6C6F328B3EBB /* [CP] Embed Pods Frameworks */, + DB8CA7C37DF78BEDCE4E37C1 /* [CP] Copy Pods Resources */, ); buildRules = ( ); dependencies = ( - 628B952C2670CABE0091AF3B /* PBXTargetDependency */, + 6264E889273848760081A12A /* PBXTargetDependency */, ); name = "JellyfinPlayer iOS"; packageProductDependencies = ( @@ -1412,6 +1421,7 @@ isa = PBXNativeTarget; buildConfigurationList = 628B952E2670CABE0091AF3B /* Build configuration list for PBXNativeTarget "WidgetExtension" */; buildPhases = ( + D4D3981ADF75BCD341D590C0 /* [CP] Check Pods Manifest.lock */, 628B951C2670CABD0091AF3B /* Sources */, 628B951D2670CABD0091AF3B /* Frameworks */, 628B951E2670CABD0091AF3B /* Resources */, @@ -1586,6 +1596,50 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ + 1C7487D3432E90546DA855B5 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-JellyfinPlayer iOS-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 3D0F2756C71CDF6B9EEBD4E0 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-JellyfinPlayer tvOS-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; 6286F09E271C093000C40ED5 /* R.swift */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -1626,29 +1680,7 @@ shellPath = /bin/sh; shellScript = "# Type a script or drag a script file from your workspace to insert its path.\nif [ $ACTION != \"indexbuild\" ]; then\n \"$PODS_ROOT/R.swift/rswift\" generate \"$SRCROOT/Shared/Generated/R.generated.swift\" --generators string\nfi\n"; }; - 6435C3C2E610FE34AD537AC1 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-JellyfinPlayer iOS-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - 6AB6F1DD2C8AD942F71C8A32 /* [CP] Embed Pods Frameworks */ = { + 879C22C1CCC48E68C86E904C /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -1665,46 +1697,7 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-JellyfinPlayer tvOS/Pods-JellyfinPlayer tvOS-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - 83FD120CA10FD0E91DAD83C9 /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-JellyfinPlayer iOS/Pods-JellyfinPlayer iOS-resources-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Copy Pods Resources"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-JellyfinPlayer iOS/Pods-JellyfinPlayer iOS-resources-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-JellyfinPlayer iOS/Pods-JellyfinPlayer iOS-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; - E7370E1AA68C6CB254E46F2C /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-JellyfinPlayer tvOS-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - E8DDF21F62DFCE8CE76666BA /* [CP] Embed Pods Frameworks */ = { + 8D1E0C963DCE6C6F328B3EBB /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -1721,6 +1714,45 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-JellyfinPlayer iOS/Pods-JellyfinPlayer iOS-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; + D4D3981ADF75BCD341D590C0 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-WidgetExtension-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + DB8CA7C37DF78BEDCE4E37C1 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-JellyfinPlayer iOS/Pods-JellyfinPlayer iOS-resources-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Copy Pods Resources"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-JellyfinPlayer iOS/Pods-JellyfinPlayer iOS-resources-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-JellyfinPlayer iOS/Pods-JellyfinPlayer iOS-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -1985,6 +2017,7 @@ files = ( 53649AB3269D3F5B00A2D8B7 /* LogManager.swift in Sources */, E13DD3CB27164BA8009D4DAF /* UIDeviceExtensions.swift in Sources */, + 6264E88A27384A6F0081A12A /* NetworkError.swift in Sources */, E19169D0272514760085832A /* HTTPScheme.swift in Sources */, 6267B3D726710B9700A7371D /* CollectionExtensions.swift in Sources */, 628B953C2670D2430091AF3B /* StringExtensions.swift in Sources */, @@ -1995,11 +2028,11 @@ E1AD105426D97161003E4A08 /* BaseItemDtoExtensions.swift in Sources */, E1FCD09A26C4F35A007C8DCF /* ErrorMessage.swift in Sources */, 628B95272670CABD0091AF3B /* NextUpWidget.swift in Sources */, + 62167B882738411700167ECE /* R.generated.swift in Sources */, E13DD3F72717E87D009D4DAF /* SwiftfinNotificationCenter.swift in Sources */, E1D4BF8D2719F3A300A11E64 /* VideoPlayerJumpLength.swift in Sources */, 6220D0AF26D5EABE00B8E046 /* ViewExtensions.swift in Sources */, E13DD3D7271693CD009D4DAF /* SwiftfinStoreDefaults.swift in Sources */, - E1FCD09926C4F358007C8DCF /* NetworkError.swift in Sources */, E131691926C583BC0074BFEE /* LogConstructor.swift in Sources */, E13DD3CA27164B80009D4DAF /* SwiftfinStore.swift in Sources */, 62EC353226766849000E9F2D /* SessionManager.swift in Sources */, @@ -2011,10 +2044,10 @@ /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ - 628B952C2670CABE0091AF3B /* PBXTargetDependency */ = { + 6264E889273848760081A12A /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 628B951F2670CABD0091AF3B /* WidgetExtension */; - targetProxy = 628B952B2670CABE0091AF3B /* PBXContainerItemProxy */; + targetProxy = 6264E888273848760081A12A /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ @@ -2152,7 +2185,7 @@ /* Begin XCBuildConfiguration section */ 535870722669D21700D05A09 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = DE5004F745B19E28744A7DE7 /* Pods-JellyfinPlayer tvOS.debug.xcconfig */; + baseConfigurationReference = B598C62749E5EFD37280FCC3 /* Pods-JellyfinPlayer tvOS.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = "Dev App Icon & Top Shelf Image"; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; @@ -2182,7 +2215,7 @@ }; 535870732669D21700D05A09 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D79953919FED0C4DF72BA578 /* Pods-JellyfinPlayer tvOS.release.xcconfig */; + baseConfigurationReference = 20CA36DDD247EED8D16438A5 /* Pods-JellyfinPlayer tvOS.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; @@ -2332,7 +2365,7 @@ }; 5377CC1C263B596B003A4E83 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 3773C07648173CE7FEC083D5 /* Pods-JellyfinPlayer iOS.debug.xcconfig */; + baseConfigurationReference = 772F6DAB8534FD1DB45B7687 /* Pods-JellyfinPlayer iOS.debug.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-Dev"; @@ -2368,7 +2401,7 @@ }; 5377CC1D263B596B003A4E83 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = BEEC50E7EFD4848C0E320941 /* Pods-JellyfinPlayer iOS.release.xcconfig */; + baseConfigurationReference = 14E199C7BBA98782CAD2F0D4 /* Pods-JellyfinPlayer iOS.release.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; @@ -2404,6 +2437,7 @@ }; 628B952F2670CABE0091AF3B /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 4BDCEE3B49CF70A9E9BA3CD8 /* Pods-WidgetExtension.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; @@ -2430,6 +2464,7 @@ }; 628B95302670CABE0091AF3B /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = FDEDADB92FA8523BC8432E45 /* Pods-WidgetExtension.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; diff --git a/JellyfinPlayer/Components/PortraitItemView.swift b/JellyfinPlayer/Components/PortraitItemView.swift index 8cc9e941..2bd276b5 100644 --- a/JellyfinPlayer/Components/PortraitItemView.swift +++ b/JellyfinPlayer/Components/PortraitItemView.swift @@ -75,7 +75,7 @@ struct PortraitItemView: View { .font(.caption) .fontWeight(.medium) } else { - Text("S\(String(item.parentIndexNumber ?? 0)):E\(String(item.indexNumber ?? 0))") + Text(R.string.localizable.seasonAndEpisode(String(item.parentIndexNumber ?? 0), String(item.indexNumber ?? 0)) .foregroundColor(.secondary) .font(.caption) .fontWeight(.medium) diff --git a/JellyfinPlayer/Views/BasicAppSettingsView.swift b/JellyfinPlayer/Views/BasicAppSettingsView.swift index ed73b75f..72e0ccca 100644 --- a/JellyfinPlayer/Views/BasicAppSettingsView.swift +++ b/JellyfinPlayer/Views/BasicAppSettingsView.swift @@ -23,7 +23,7 @@ struct BasicAppSettingsView: View { var body: some View { Form { Section { - Picker(NSLocalizedString("Appearance", comment: ""), selection: $appAppearance) { + Picker(R.string.localizable.appearance(), selection: $appAppearance) { ForEach(self.viewModel.appearances, id: \.self) { appearance in Text(appearance.localizedName).tag(appearance.rawValue) } @@ -31,7 +31,7 @@ struct BasicAppSettingsView: View { UIApplication.shared.windows.first?.overrideUserInterfaceStyle = appAppearance.style }) } header: { - Text("Accessibility") + R.string.localizable.accessibility.text } Section { @@ -47,15 +47,15 @@ struct BasicAppSettingsView: View { Button { resetTapped = true } label: { - Text("Reset") + R.string.localizable.reset.text } } - .alert("Reset", isPresented: $resetTapped, actions: { + .alert(R.string.localizable.reset(), isPresented: $resetTapped, actions: { Button(role: .destructive) { viewModel.reset() basicAppSettingsRouter.dismissCoordinator() } label: { - Text("Reset") + R.string.localizable.reset.text } }) .navigationBarTitle("Settings", displayMode: .inline) diff --git a/JellyfinPlayer/Views/ConnectToServerView.swift b/JellyfinPlayer/Views/ConnectToServerView.swift index f44975d6..0d5a4e69 100644 --- a/JellyfinPlayer/Views/ConnectToServerView.swift +++ b/JellyfinPlayer/Views/ConnectToServerView.swift @@ -20,7 +20,7 @@ struct ConnectToServerView: View { var body: some View { List { Section { - TextField(NSLocalizedString("Server URL", comment: ""), text: $uri) + TextField(R.string.localizable.serverURL(), text: $uri) .disableAutocorrection(true) .autocapitalization(.none) .keyboardType(.URL) @@ -40,7 +40,7 @@ struct ConnectToServerView: View { Button { viewModel.connectToServer(uri: uri) } label: { - Text("Connect") + R.string.localizable.connect.text } .disabled(uri.isEmpty) } @@ -88,7 +88,7 @@ struct ConnectToServerView: View { } } header: { HStack { - Text("Local Servers") + R.string.localizable.localServers.text Spacer() Button { @@ -106,7 +106,7 @@ struct ConnectToServerView: View { message: Text(viewModel.errorMessage?.displayMessage ?? "Unknown Error"), dismissButton: .cancel()) } - .navigationTitle("Connect") + .navigationTitle(R.string.localizable.connect()) .onAppear { viewModel.discoverServers() AppURLHandler.shared.appURLState = .allowedInLogin diff --git a/JellyfinPlayer/Views/HomeView.swift b/JellyfinPlayer/Views/HomeView.swift index 51b3ae1e..58997777 100644 --- a/JellyfinPlayer/Views/HomeView.swift +++ b/JellyfinPlayer/Views/HomeView.swift @@ -34,7 +34,7 @@ struct HomeView: View { ForEach(viewModel.libraries, id: \.self) { library in HStack { - Text("Latest \(library.name ?? "")") + Text(R.string.localizable.latestWithString(library.name ?? "")) .font(.title2) .fontWeight(.bold) Spacer() @@ -45,7 +45,7 @@ struct HomeView: View { title: library.name ?? "")) } label: { HStack { - Text("See All").font(.subheadline).fontWeight(.bold) + R.string.localizable.seeAll.text.font(.subheadline).fontWeight(.bold) Image(systemName: "chevron.right").font(Font.subheadline.bold()) } } @@ -70,7 +70,7 @@ struct HomeView: View { var body: some View { innerBody - .navigationTitle(NSLocalizedString("Home", comment: "")) + .navigationTitle(R.string.localizable.home()) .toolbar { ToolbarItemGroup(placement: .navigationBarTrailing) { Button { diff --git a/JellyfinPlayer/Views/ItemView/ItemViewBody.swift b/JellyfinPlayer/Views/ItemView/ItemViewBody.swift index 3c819b25..3b973fa5 100644 --- a/JellyfinPlayer/Views/ItemView/ItemViewBody.swift +++ b/JellyfinPlayer/Views/ItemView/ItemViewBody.swift @@ -29,7 +29,7 @@ struct ItemViewBody: View { PortraitImageHStackView(items: seriesViewModel.seasons, maxWidth: 150, topBarView: { - Text("Seasons") + R.string.localizable.seasons.text .font(.callout) .fontWeight(.semibold) .padding(.top, 3) @@ -41,7 +41,7 @@ struct ItemViewBody: View { // MARK: Genres - PillHStackView(title: "Genres", + PillHStackView(title: R.string.localizable.genres(), items: viewModel.item.genreItems ?? [], selectedAction: { genre in itemRouter.route(to: \.library, (viewModel: .init(genre: genre), title: genre.title)) @@ -50,7 +50,7 @@ struct ItemViewBody: View { // MARK: Studios if let studios = viewModel.item.studios { - PillHStackView(title: "Studios", + PillHStackView(title: R.string.localizable.studios(), items: studios) { studio in itemRouter.route(to: \.library, (viewModel: .init(studio: studio), title: studio.name ?? "")) } @@ -79,7 +79,7 @@ struct ItemViewBody: View { PortraitImageHStackView(items: viewModel.similarItems, maxWidth: 150, topBarView: { - Text("More Like This") + R.string.localizable.moreLikeThis.text .font(.callout) .fontWeight(.semibold) .padding(.top, 3) diff --git a/JellyfinPlayer/Views/LibraryFilterView.swift b/JellyfinPlayer/Views/LibraryFilterView.swift index 1b1ffb00..228efd35 100644 --- a/JellyfinPlayer/Views/LibraryFilterView.swift +++ b/JellyfinPlayer/Views/LibraryFilterView.swift @@ -31,32 +31,32 @@ struct LibraryFilterView: View { } else { Form { if viewModel.enabledFilterType.contains(.genre) { - MultiSelector(label: NSLocalizedString("Genres", comment: ""), + MultiSelector(label: R.string.localizable.genres(), options: viewModel.possibleGenres, optionToString: { $0.name ?? "" }, selected: $viewModel.modifiedFilters.withGenres) } if viewModel.enabledFilterType.contains(.filter) { - MultiSelector(label: NSLocalizedString("Filters", comment: ""), + MultiSelector(label: R.string.localizable.filters(), options: viewModel.possibleItemFilters, optionToString: { $0.localized }, selected: $viewModel.modifiedFilters.filters) } if viewModel.enabledFilterType.contains(.tag) { - MultiSelector(label: NSLocalizedString("Tags", comment: ""), + MultiSelector(label: R.string.localizable.tags(), options: viewModel.possibleTags, optionToString: { $0 }, selected: $viewModel.modifiedFilters.tags) } if viewModel.enabledFilterType.contains(.sortBy) { - Picker(selection: $viewModel.selectedSortBy, label: Text("Sort by")) { + Picker(selection: $viewModel.selectedSortBy, label: R.string.localizable.sortBy.text) { ForEach(viewModel.possibleSortBys, id: \.self) { so in Text(so.localized).tag(so) } } } if viewModel.enabledFilterType.contains(.sortOrder) { - Picker(selection: $viewModel.selectedSortOrder, label: Text("Display order")) { + Picker(selection: $viewModel.selectedSortOrder, label: R.string.localizable.displayOrder.text) { ForEach(viewModel.possibleSortOrders, id: \.self) { so in Text(so.rawValue).tag(so) } @@ -68,11 +68,11 @@ struct LibraryFilterView: View { self.filters = viewModel.modifiedFilters filterRouter.dismissCoordinator() } label: { - Text("Reset") + R.string.localizable.reset.text } } } - .navigationBarTitle(NSLocalizedString("Filter Results", comment: ""), displayMode: .inline) + .navigationBarTitle(R.string.localizable.filterResults(), displayMode: .inline) .toolbar { ToolbarItemGroup(placement: .navigationBarLeading) { Button { @@ -87,7 +87,7 @@ struct LibraryFilterView: View { self.filters = viewModel.modifiedFilters filterRouter.dismissCoordinator() } label: { - Text("Apply") + R.string.localizable.apply.text } } } diff --git a/JellyfinPlayer/Views/LibraryListView.swift b/JellyfinPlayer/Views/LibraryListView.swift index baac87b8..6861eef7 100644 --- a/JellyfinPlayer/Views/LibraryListView.swift +++ b/JellyfinPlayer/Views/LibraryListView.swift @@ -23,7 +23,7 @@ struct LibraryListView: View { ZStack { HStack { Spacer() - Text("Your Favorites") + R.string.localizable.yourFavorites.text .foregroundColor(.black) .font(.subheadline) .fontWeight(.semibold) @@ -39,12 +39,12 @@ struct LibraryListView: View { .padding(.bottom, 5) NavigationLink(destination: LazyView { - Text("WIP") + R.string.localizable.wip.text }) { ZStack { HStack { Spacer() - Text("All Genres") + R.string.localizable.allGenres.text .foregroundColor(.black) .font(.subheadline) .fontWeight(.semibold) @@ -98,7 +98,7 @@ struct LibraryListView: View { .padding(.trailing, 16) .padding(.top, 8) } - .navigationTitle(NSLocalizedString("All Media", comment: "")) + .navigationTitle(R.string.localizable.allMedia()) .toolbar { ToolbarItemGroup(placement: .navigationBarTrailing) { Button { diff --git a/JellyfinPlayer/Views/LibrarySearchView.swift b/JellyfinPlayer/Views/LibrarySearchView.swift index a9c63a97..54b13ce0 100644 --- a/JellyfinPlayer/Views/LibrarySearchView.swift +++ b/JellyfinPlayer/Views/LibrarySearchView.swift @@ -46,7 +46,7 @@ struct LibrarySearchView: View { var suggestionsListView: some View { ScrollView { LazyVStack(spacing: 8) { - Text("Suggestions") + R.string.localizable.suggestions.text .font(.headline) .fontWeight(.bold) .foregroundColor(.primary) diff --git a/JellyfinPlayer/Views/LibraryView.swift b/JellyfinPlayer/Views/LibraryView.swift index b0fb15b5..1cd93a2f 100644 --- a/JellyfinPlayer/Views/LibraryView.swift +++ b/JellyfinPlayer/Views/LibraryView.swift @@ -55,7 +55,7 @@ struct LibraryView: View { Image(systemName: "chevron.left") .font(.system(size: 25)) }.disabled(!viewModel.hasPreviousPage) - Text("Page \(String(viewModel.currentPage + 1)) of \(String(viewModel.totalPages))") + Text(R.string.localizable.pageOfWithNumbers(String(viewModel.currentPage + 1), String(viewModel.totalPages))) .font(.subheadline) .fontWeight(.medium) Button { @@ -72,7 +72,7 @@ struct LibraryView: View { } } } else { - Text("No results.") + R.string.localizable.noResults.text } } .navigationBarTitle(title, displayMode: .inline) diff --git a/JellyfinPlayer/Views/LoadingView.swift b/JellyfinPlayer/Views/LoadingView.swift index 015ba61f..850aa90b 100644 --- a/JellyfinPlayer/Views/LoadingView.swift +++ b/JellyfinPlayer/Views/LoadingView.swift @@ -32,7 +32,7 @@ struct LoadingView: View where Content: View { // indicator, with some text underneath showing what we are doing HStack { ProgressView() - Text(text ?? "Loading").fontWeight(.semibold).font(.callout).offset(x: 60) + Text(text ?? R.string.localizable.loading()).fontWeight(.semibold).font(.callout).offset(x: 60) Spacer() } .padding(EdgeInsets(top: 20, leading: 20, bottom: 20, trailing: 10)) @@ -70,7 +70,7 @@ struct LoadingViewNoBlur: View where Content: View { // indicator, with some text underneath showing what we are doing HStack { ProgressView() - Text(text ?? "Loading").fontWeight(.semibold).font(.callout).offset(x: 60) + Text(text ?? R.string.localizable.loading()).fontWeight(.semibold).font(.callout).offset(x: 60) Spacer() } .padding(EdgeInsets(top: 20, leading: 20, bottom: 20, trailing: 10)) diff --git a/JellyfinPlayer/Views/NextUpView.swift b/JellyfinPlayer/Views/NextUpView.swift index 5cdd5467..d871fc8e 100644 --- a/JellyfinPlayer/Views/NextUpView.swift +++ b/JellyfinPlayer/Views/NextUpView.swift @@ -17,7 +17,7 @@ struct NextUpView: View { var body: some View { VStack(alignment: .leading) { - Text("Next Up") + R.string.localizable.nextUp.text .font(.title2) .fontWeight(.bold) .padding(.leading, 16) diff --git a/JellyfinPlayer/Views/ServerListView.swift b/JellyfinPlayer/Views/ServerListView.swift index bd0ea63e..e62d3f6c 100644 --- a/JellyfinPlayer/Views/ServerListView.swift +++ b/JellyfinPlayer/Views/ServerListView.swift @@ -81,7 +81,7 @@ struct ServerListView: View { .padding(.horizontal, 30) .padding([.top, .bottom], 20) - Text("Connect") + R.string.localizable.connect.text .foregroundColor(Color.white) .bold() } diff --git a/JellyfinPlayer/Views/SettingsView.swift b/JellyfinPlayer/Views/SettingsView.swift index 7b7a9ff5..db429f9e 100644 --- a/JellyfinPlayer/Views/SettingsView.swift +++ b/JellyfinPlayer/Views/SettingsView.swift @@ -108,7 +108,7 @@ struct SettingsView: View { } } - Section(header: Text("Accessibility")) { + Section(header: R.string.localizable.accessibility.text) { Toggle("Automatically show subtitles", isOn: $isAutoSelectSubtitles) SearchablePicker(label: "Preferred subtitle language", options: viewModel.langs, @@ -129,7 +129,7 @@ struct SettingsView: View { .auto }, set: { autoSelectAudioLangcode = $0.isoCode })) - Picker(NSLocalizedString("Appearance", comment: ""), selection: $appAppearance) { + Picker(R.string.localizable.appearance(), selection: $appAppearance) { ForEach(self.viewModel.appearances, id: \.self) { appearance in Text(appearance.localizedName).tag(appearance.rawValue) } diff --git a/JellyfinPlayer/Views/UserSignInView.swift b/JellyfinPlayer/Views/UserSignInView.swift index 3173cdd7..777fa6e2 100644 --- a/JellyfinPlayer/Views/UserSignInView.swift +++ b/JellyfinPlayer/Views/UserSignInView.swift @@ -20,11 +20,11 @@ struct UserSignInView: View { Form { Section { - TextField("Username", text: $username) + TextField(R.string.localizable.username(), text: $username) .disableAutocorrection(true) .autocapitalization(.none) - SecureField("Password", text: $password) + SecureField(R.string.localizable.password(), text: $password) .disableAutocorrection(true) .autocapitalization(.none) diff --git a/JellyfinPlayer/Views/VideoPlayer/VideoPlayer.swift b/JellyfinPlayer/Views/VideoPlayer/VideoPlayer.swift index e0ddcb71..6094d417 100644 --- a/JellyfinPlayer/Views/VideoPlayer/VideoPlayer.swift +++ b/JellyfinPlayer/Views/VideoPlayer/VideoPlayer.swift @@ -421,7 +421,7 @@ class PlayerViewController: UIViewController, GCKDiscoveryManagerListener, GCKRe if manifest.type == "Movie" { titleLabel.text = manifest.name ?? "" } else { - titleLabel.text = "S\(String(manifest.parentIndexNumber ?? 0)):E\(String(manifest.indexNumber ?? 0)) “\(manifest.name ?? "")”" + titleLabel.text = "\(R.string.localizable.seasonAndEpisode(String(manifest.parentIndexNumber ?? 0), String(manifest.indexNumber ?? 0))) “\(manifest.name ?? "")”" setupNextUpView() upNextViewModel.delegate = self @@ -816,7 +816,7 @@ class PlayerViewController: UIViewController, GCKDiscoveryManagerListener, GCKRe shouldShowLoadingScreen = true videoControlsView.isHidden = true - titleLabel.text = "S\(String(manifest.parentIndexNumber ?? 0)):E\(String(manifest.indexNumber ?? 0)) “\(manifest.name ?? "")”" + titleLabel.text = "\(R.string.localizable.seasonAndEpisode(String(manifest.parentIndexNumber ?? 0), String(manifest.indexNumber ?? 0))) “\(manifest.name ?? "")”" setupMediaPlayer() getNextEpisode() diff --git a/JellyfinPlayer/Views/VideoPlayer/VideoPlayerCastDeviceSelector.swift b/JellyfinPlayer/Views/VideoPlayer/VideoPlayerCastDeviceSelector.swift index ac784125..7cb7f13b 100644 --- a/JellyfinPlayer/Views/VideoPlayer/VideoPlayerCastDeviceSelector.swift +++ b/JellyfinPlayer/Views/VideoPlayer/VideoPlayerCastDeviceSelector.swift @@ -56,7 +56,7 @@ struct VideoPlayerCastDeviceSelector: View { delegate?.castPopoverDismissed() } label: { HStack { - Text("Connect") + R.string.localizable.connect.text .font(.caption) .fontWeight(.medium) Image(systemName: "bonjour") @@ -66,14 +66,14 @@ struct VideoPlayerCastDeviceSelector: View { } } } else { - Text("No Cast devices found..") + R.string.localizable.noCastdevicesfound.text .foregroundColor(.secondary) .font(.subheadline) .fontWeight(.medium) } } .navigationBarTitleDisplayMode(.inline) - .navigationTitle(NSLocalizedString("Select Cast Destination", comment: "")) + .navigationTitle(R.string.localizable.selectCastDestination()) .toolbar { ToolbarItemGroup(placement: .navigationBarLeading) { if UIDevice.current.userInterfaceIdiom == .phone { @@ -82,7 +82,7 @@ struct VideoPlayerCastDeviceSelector: View { } label: { HStack { Image(systemName: "chevron.left") - Text("Back").font(.callout) + R.string.localizable.back.text.font(.callout) } } } diff --git a/JellyfinPlayer/Views/VideoPlayer/VideoPlayerSettingsView.swift b/JellyfinPlayer/Views/VideoPlayer/VideoPlayerSettingsView.swift index 1a10eecc..dad7ccb7 100644 --- a/JellyfinPlayer/Views/VideoPlayer/VideoPlayerSettingsView.swift +++ b/JellyfinPlayer/Views/VideoPlayer/VideoPlayerSettingsView.swift @@ -40,7 +40,7 @@ struct VideoPlayerSettings: View { var body: some View { Form { - Picker(NSLocalizedString("Closed Captions", comment: ""), selection: $captionTrack) { + Picker(R.string.localizable.closedCaptions(), selection: $captionTrack) { ForEach(delegate.subtitleTrackArray, id: \.id) { caption in Text(caption.name).tag(caption.id) } @@ -48,14 +48,14 @@ struct VideoPlayerSettings: View { .onChange(of: captionTrack) { track in self.delegate.subtitleTrackChanged(newTrackID: track) } - Picker(NSLocalizedString("Audio Track", comment: ""), selection: $audioTrack) { + Picker(R.string.localizable.audioTrack(), selection: $audioTrack) { ForEach(delegate.audioTrackArray, id: \.id) { caption in Text(caption.name).tag(caption.id).lineLimit(1) } }.onChange(of: audioTrack) { track in self.delegate.audioTrackChanged(newTrackID: track) } - Picker(NSLocalizedString("Playback Speed", comment: ""), selection: $playbackSpeedSelection) { + Picker(R.string.localizable.playbackSpeed(), selection: $playbackSpeedSelection) { ForEach(delegate.playbackSpeeds.indices, id: \.self) { speedIndex in let speed = delegate.playbackSpeeds[speedIndex] Text("\(String(speed))x").tag(speedIndex) @@ -65,7 +65,7 @@ struct VideoPlayerSettings: View { self.delegate.playbackSpeedChanged(index: index) }) }.navigationBarTitleDisplayMode(.inline) - .navigationTitle(NSLocalizedString("Audio & Captions", comment: "")) + .navigationTitle(R.string.localizable.audioAndCaptions()) .toolbar { ToolbarItemGroup(placement: .navigationBarLeading) { if UIDevice.current.userInterfaceIdiom == .phone { @@ -74,7 +74,7 @@ struct VideoPlayerSettings: View { } label: { HStack { Image(systemName: "chevron.left") - Text("Back").font(.callout) + R.string.localizable.back.text.font(.callout) } } } diff --git a/JellyfinPlayer/Views/VideoPlayer/VideoUpNextView.swift b/JellyfinPlayer/Views/VideoPlayer/VideoUpNextView.swift index 5348d983..8a8e2722 100644 --- a/JellyfinPlayer/Views/VideoPlayer/VideoUpNextView.swift +++ b/JellyfinPlayer/Views/VideoPlayer/VideoUpNextView.swift @@ -32,7 +32,7 @@ struct VideoUpNextView: View { } label: { HStack { VStack { - Text("Play Next") + R.string.localizable.playNext.text .foregroundColor(.white) .font(.subheadline) .fontWeight(.semibold) diff --git a/Shared/Coordinators/MainCoordinator/iOSMainTabCoordinator.swift b/Shared/Coordinators/MainCoordinator/iOSMainTabCoordinator.swift index 3be7131b..81bd6fea 100644 --- a/Shared/Coordinators/MainCoordinator/iOSMainTabCoordinator.swift +++ b/Shared/Coordinators/MainCoordinator/iOSMainTabCoordinator.swift @@ -26,7 +26,7 @@ final class MainTabCoordinator: TabCoordinatable { @ViewBuilder func makeHomeTab(isActive: Bool) -> some View { Image(systemName: "house") - Text("Home") + R.string.localizable.home.text } func makeAllMedia() -> NavigationViewCoordinator { @@ -35,7 +35,7 @@ final class MainTabCoordinator: TabCoordinatable { @ViewBuilder func makeAllMediaTab(isActive: Bool) -> some View { Image(systemName: "folder") - Text("All Media") + R.string.localizable.allMedia.text } @ViewBuilder func customize(_ view: AnyView) -> some View { diff --git a/Shared/Coordinators/MainCoordinator/tvOSMainTabCoordinator.swift b/Shared/Coordinators/MainCoordinator/tvOSMainTabCoordinator.swift index 8be5a5c6..906b1a18 100644 --- a/Shared/Coordinators/MainCoordinator/tvOSMainTabCoordinator.swift +++ b/Shared/Coordinators/MainCoordinator/tvOSMainTabCoordinator.swift @@ -33,7 +33,7 @@ final class MainTabCoordinator: TabCoordinatable { @ViewBuilder func makeHomeTab(isActive: Bool) -> some View { HStack { Image(systemName: "house") - Text("Home") + R.string.localizable.home.text } } diff --git a/Shared/Errors/NetworkError.swift b/Shared/Errors/NetworkError.swift index 10a3c2d2..69447991 100644 --- a/Shared/Errors/NetworkError.swift +++ b/Shared/Errors/NetworkError.swift @@ -85,13 +85,13 @@ enum NetworkError: Error { logMessage = "Cannot connect to host." logConstructor.message = logMessage errorMessage = ErrorMessage(code: err._code, - title: "Error", + title: R.string.localizable.error(), displayMessage: displayMessage, logConstructor: logConstructor) default: logConstructor.message = logMessage errorMessage = ErrorMessage(code: err._code, - title: "Error", + title: R.string.localizable.error(), displayMessage: displayMessage, logConstructor: logConstructor) } @@ -111,7 +111,7 @@ enum NetworkError: Error { case .error: logConstructor.message = logMessage errorMessage = ErrorMessage(code: 0, - title: "Error", + title: R.string.localizable.error(), displayMessage: displayMessage, logConstructor: logConstructor) } @@ -140,7 +140,7 @@ enum NetworkError: Error { default: logConstructor.message = logMessage errorMessage = ErrorMessage(code: code, - title: "Error", + title: R.string.localizable.error(), displayMessage: displayMessage, logConstructor: logConstructor) } diff --git a/Shared/Extensions/JellyfinAPIExtensions/BaseItemDtoExtensions.swift b/Shared/Extensions/JellyfinAPIExtensions/BaseItemDtoExtensions.swift index 78ad8991..525abc09 100644 --- a/Shared/Extensions/JellyfinAPIExtensions/BaseItemDtoExtensions.swift +++ b/Shared/Extensions/JellyfinAPIExtensions/BaseItemDtoExtensions.swift @@ -80,7 +80,7 @@ public extension BaseItemDto { func getEpisodeLocator() -> String? { if let seasonNo = parentIndexNumber, let episodeNo = indexNumber { - return "S\(seasonNo):E\(episodeNo)" + return R.string.localizable.seasonAndEpisode(String(seasonNo), String(episodeNo)) } return nil } diff --git a/Shared/Objects/AppAppearance.swift b/Shared/Objects/AppAppearance.swift index a3bc58a5..d71e6e10 100644 --- a/Shared/Objects/AppAppearance.swift +++ b/Shared/Objects/AppAppearance.swift @@ -16,7 +16,14 @@ enum AppAppearance: String, CaseIterable, Defaults.Serializable { case light var localizedName: String { - return NSLocalizedString(self.rawValue.capitalized, comment: "") + switch self { + case .system: + return R.string.localizable.system() + case .dark: + return R.string.localizable.dark() + case .light: + return R.string.localizable.light() + } } var style: UIUserInterfaceStyle { diff --git a/Shared/Objects/Typings.swift b/Shared/Objects/Typings.swift index 72a46e14..9860cb1d 100644 --- a/Shared/Objects/Typings.swift +++ b/Shared/Objects/Typings.swift @@ -77,7 +77,7 @@ enum ItemType: String { var localized: String { switch self { case .episode: - return "Episodes" + return R.string.localizable.episodes() case .movie: return "Movies" case .series: diff --git a/Shared/ViewModels/ItemViewModel.swift b/Shared/ViewModels/ItemViewModel.swift index 7b2dc5c3..334d130a 100644 --- a/Shared/ViewModels/ItemViewModel.swift +++ b/Shared/ViewModels/ItemViewModel.swift @@ -35,7 +35,7 @@ class ItemViewModel: ViewModel { } func playButtonText() -> String { - return item.getItemProgressString() == "" ? "Play" : item.getItemProgressString() + return item.getItemProgressString() == "" ? R.string.localizable.play() : item.getItemProgressString() } func getItemDisplayName() -> String { diff --git a/Shared/ViewModels/SeasonItemViewModel.swift b/Shared/ViewModels/SeasonItemViewModel.swift index c3d1a739..0dd9c237 100644 --- a/Shared/ViewModels/SeasonItemViewModel.swift +++ b/Shared/ViewModels/SeasonItemViewModel.swift @@ -24,8 +24,8 @@ final class SeasonItemViewModel: ItemViewModel { } override func playButtonText() -> String { - guard let playButtonItem = playButtonItem else { return "Play" } - guard let episodeLocator = playButtonItem.getEpisodeLocator() else { return "Play" } + guard let playButtonItem = playButtonItem else { return R.string.localizable.play() } + guard let episodeLocator = playButtonItem.getEpisodeLocator() else { return R.string.localizable.play() } return episodeLocator } diff --git a/Shared/ViewModels/SeriesItemViewModel.swift b/Shared/ViewModels/SeriesItemViewModel.swift index b204c87b..80a3ccaf 100644 --- a/Shared/ViewModels/SeriesItemViewModel.swift +++ b/Shared/ViewModels/SeriesItemViewModel.swift @@ -23,8 +23,8 @@ final class SeriesItemViewModel: ItemViewModel { } override func playButtonText() -> String { - guard let playButtonItem = playButtonItem else { return "Play" } - guard let episodeLocator = playButtonItem.getEpisodeLocator() else { return "Play" } + guard let playButtonItem = playButtonItem else { return R.string.localizable.play() } + guard let episodeLocator = playButtonItem.getEpisodeLocator() else { return R.string.localizable.play() } return episodeLocator } diff --git a/Shared/Views/SearchBarView.swift b/Shared/Views/SearchBarView.swift index ac4b8f32..de0c098d 100644 --- a/Shared/Views/SearchBarView.swift +++ b/Shared/Views/SearchBarView.swift @@ -16,7 +16,7 @@ struct SearchBar: View { var body: some View { HStack(spacing: 8) { - TextField(NSLocalizedString("Search...", comment: ""), text: $text) + TextField(R.string.localizable.search(), text: $text) .padding(8) .padding(.horizontal, 16) #if os(iOS) diff --git a/WidgetExtension/NextUpWidget.swift b/WidgetExtension/NextUpWidget.swift index eec8417d..a72f06dd 100644 --- a/WidgetExtension/NextUpWidget.swift +++ b/WidgetExtension/NextUpWidget.swift @@ -24,9 +24,8 @@ struct NextUpWidgetProvider: TimelineProvider { } func getSnapshot(in context: Context, completion: @escaping (NextUpEntry) -> Void) { - guard let currentLogin = SessionManager.main.currentLogin else { return } - + let currentDate = Date() let server = currentLogin.server let savedUser = currentLogin.user @@ -68,9 +67,8 @@ struct NextUpWidgetProvider: TimelineProvider { } func getTimeline(in context: Context, completion: @escaping (Timeline) -> Void) { - guard let currentLogin = SessionManager.main.currentLogin else { return } - + let currentDate = Date() let entryDate = Calendar.current.date(byAdding: .hour, value: 1, to: currentDate)! let server = currentLogin.server @@ -137,7 +135,7 @@ struct NextUpEntryView: View { } .background(Color.blue) } else if entry.items.isEmpty { - Text("Empty Next Up") + R.string.localizable.emptyNextUp() .font(.body) .bold() .foregroundColor(.primary) @@ -216,7 +214,7 @@ extension NextUpEntryView { .fontWeight(.semibold) .foregroundColor(.primary) .lineLimit(1) - Text("\(item.0.name ?? "") · S\(item.0.parentIndexNumber ?? 0):E\(item.0.indexNumber ?? 0)") + Text("\(item.0.name ?? "") · \(R.string.localizable.seasonAndEpisode(String(item.0.parentIndexNumber ?? 0), String(item.0.indexNumber ?? 0)))") .font(.caption) .fontWeight(.semibold) .foregroundColor(.secondary) @@ -243,7 +241,8 @@ extension NextUpEntryView { .fontWeight(.semibold) .foregroundColor(.primary) .frame(minWidth: 0, maxWidth: .infinity, alignment: .leading) - Text("\(item.0.name ?? "") · S\(item.0.parentIndexNumber ?? 0):E\(item.0.indexNumber ?? 0)") + + Text("\(item.0.name ?? "") · \(R.string.localizable.seasonAndEpisode(String(item.0.parentIndexNumber ?? 0), String(item.0.indexNumber ?? 0)))") .font(.caption) .fontWeight(.semibold) .foregroundColor(.secondary) @@ -305,7 +304,7 @@ extension NextUpEntryView { .fontWeight(.semibold) .foregroundColor(.white) .frame(minWidth: 0, maxWidth: .infinity, alignment: .leading) - Text("\(firstItem.0.name ?? "") · S\(firstItem.0.parentIndexNumber ?? 0):E\(firstItem.0.indexNumber ?? 0)") + Text("\(firstItem.0.name ?? "") · \(R.string.localizable.seasonAndEpisode(String(firstItem.0.parentIndexNumber ?? 0), String(firstItem.0.indexNumber ?? 0)))") .font(.caption) .fontWeight(.semibold) .foregroundColor(.gray) @@ -347,7 +346,7 @@ struct NextUpWidget: Widget { provider: NextUpWidgetProvider()) { entry in NextUpEntryView(entry: entry) } - .configurationDisplayName("Next Up") + .configurationDisplayName(R.string.localizable.nextUp()) .description("Keep watching where you left off or see what's up next.") .supportedFamilies([.systemSmall, .systemMedium, .systemLarge]) }