Add support of localisations.

This commit is contained in:
Marcin Czachursk 2023-03-13 13:53:36 +01:00
parent 33f0d07782
commit 95af9f59bd
50 changed files with 744 additions and 237 deletions

View File

@ -0,0 +1,231 @@
// MARK: Common strings.
"global.title.contentWarning" = "Sensitive content";
"global.title.seePost" = "See post";
"global.title.seePost" = "Refresh";
// MARK: Global errors.
"global.error.unexpected" = "Unexpected error.";
"global.error.statusesNotRetrieved" = "Statuses not retrieved.";
"global.error.errorDuringDownloadStatuses" = "Error during download statuses from server.";
"global.error.errorDuringDownloadHashtag" = "Error during download tag from server.";
"global.error.hashtagNotExists" = "Hashtag not exists.";
"global.error.errorDuringImageDownload" = "Cannot download image.";
"global.error.canceledImageDownload" = "Download image has been canceled.";
"global.error.errorDuringDataLoad" = "Loading data failed.";
// MARK: Main view (main navigation bar).
"mainview.tab.homeTimeline" = "Home";
"mainview.tab.localTimeline" = "Local";
"mainview.tab.federatedTimeline" = "Federated";
"mainview.tab.trendingPhotos" = "Photos";
"mainview.tab.trendingTags" = "Tags";
"mainview.tab.trendingAccounts" = "Accounts";
"mainview.tab.userProfile" = "Profile";
"mainview.tab.notifications" = "Notifications";
"mainview.tab.search" = "Search";
"mainview.tab.trending" = "Trending";
// MARK: Main view (leading navigation bar).
"mainview.menu.settings" = "Settings";
// MARK: Main view (error notifications).
"mainview.error.switchAccounts" = "Cannot switch accounts.";
// MARK: Home timeline.
"home.title.allCaughtUp" = "You're all caught up";
"home.title.noPhotos" = "Unfortunately, there are no photos here.";
// MARK: Statuses timeline (local/federated/favourite/bookmarks etc.).
"statuses.navigationBar.localTimeline" = "Local";
"statuses.navigationBar.federatedTimeline" = "Federated";
"statuses.navigationBar.favourites" = "Favourites";
"statuses.navigationBar.bookmarks" = "Bookmarks";
"statuses.title.noPhotos" = "Unfortunately, there are no photos here.";
"statuses.title.tagFollowed" = "You are following the tag.";
"statuses.title.tagUnfollowed" = "Tag has been unfollowed.";
"statuses.error.loadingStatusesFailed" = "Loading statuses failed.";
"statuses.error.tagFollowFailed" = "Follow tag failed.";
"statuses.error.tagUnfollowFailed" = "Unfollow tag failed.";
// Mark: Search view.
"search.navigationBar.title" = "Search";
"search.title.placeholder" = "Search...";
"search.title.usersWith" = "Users with %@";
"search.title.goToUser" = "Go to user %@";
"search.title.hashtagWith" = "Hashtags with %@";
"search.title.goToHashtag" = "Go to hashtag %@";
// Mark: Trending statuses.
"trendingStatuses.navigationBar.title" = "Photos";
"trendingStatuses.title.daily" = "Daily";
"trendingStatuses.title.monthly" = "Monthly";
"trendingStatuses.title.yearly" = "Yearly";
"trendingStatuses.error.loadingStatusesFailed" = "Loading statuses failed.";
"trendingStatuses.title.noPhotos" = "Unfortunately, there are no photos here.";
// Mark: Trending tags.
"trendingTags.navigationBar.title" = "Tags";
"trendingTags.title.noTags" = "Unfortunately, there are no tags here.";
"trendingTags.title.amountOfPosts" = "%d posts";
"trendingTags.error.loadingTagsFailed" = "Loading tags failed.";
// Mark: Trending accounts.
"trendingAccounts.navigationBar.title" = "Accounts";
"trendingAccounts.title.noAccounts" = "Unfortunately, there is no one here.";
"trendingAccounts.error.loadingAccountsFailed" = "Loading accounts failed.";
// Mark: User profile view.
"userProfile.title.openInBrowser" = "Open in browser";
"userProfile.title.share" = "Share";
"userProfile.title.unmute" = "Unmute";
"userProfile.title.mute" = "Mute";
"userProfile.title.unblock" = "Unblock";
"userProfile.title.block" = "Block";
"userProfile.title.favourites" = "Favourites";
"userProfile.title.bookmarks" = "Bookmarks";
"userProfile.title.posts" = "Posts";
"userProfile.title.followers" = "Followers";
"userProfile.title.following" = "Following";
"userProfile.title.joined" = "Joined %@";
"userProfile.title.unfollow" = "Unfollow";
"userProfile.title.followBack" = "Follow back";
"userProfile.title.follow" = "Follow";
"userProfile.error.notExists" = "Account not exists.";
"userProfile.error.loadingAccountFailed" = "Error during download account from server.";
"userProfile.error.muting" = "Muting/unmuting action failed.";
"userProfile.error.block" = "Block/unblock action failed.";
"userProfile.error.relationship" = "Relationship action failed.";
// Mark: Notifications view.
"notifications.navigationBar.title" = "Notifications";
"notifications.title.noNotifications" = "Unfortunately, there is nothing here.";
"notifications.title.followedYou" = "followed you";
"notifications.title.mentionedYou" = "mentioned you";
"notifications.title.boosted" = "boosted";
"notifications.title.favourited" = "favourited";
"notifications.title.postedStatus" = "posted status";
"notifications.title.followRequest" = "follow request";
"notifications.title.poll" = "poll";
"notifications.title.updatedStatus" = "updated status";
"notifications.title.signedUp" = "signed up";
"notifications.title.newReport" = "new report";
"notifications.error.loadingNotificationsFailed" = "Loading notifications failed.";
// Mark: Compose view.
"compose.navigationBar.title" = "Compose";
"compose.title.everyone" = "Everyone";
"compose.title.unlisted" = "Unlisted";
"compose.title.followers" = "Followers";
"compose.title.attachPhotoFull" = "Attach a photo and type what's on your mind";
"compose.title.attachPhotoMini" = "Type what's on your mind";
"compose.title.publish" = "Publish";
"compose.title.cancel" = "Cancel";
"compose.title.writeContentWarning" = "Write content warning";
"compose.title.commentsWillBeDisabled" = "Comments will be disabled";
"compose.title.statusPublished" = "Status published";
"compose.title.tryToUpload" = "Try to upload";
"compose.title.delete" = "Delete";
"compose.title.edit" = "Edit";
"compose.error.loadingPhotosFailed" = "Cannot retreive image from library.";
"compose.error.postingPhotoFailed" = "Error during posting photo.";
"compose.error.postingStatusFailed" = "Error during posting status.";
// Mark: Photo editor view.
"photoEdit.navigationBar.title" = "Photo details";
"photoEdit.title.photo" = "Photo";
"photoEdit.title.accessibility" = "Accessibility";
"photoEdit.title.accessibilityDescription" = "Description for the visually impaired";
"photoEdit.title.save" = "Save";
"photoEdit.title.cancel" = "Cancel";
"photoEdit.error.updatePhotoFailed" = "Error during updating photo.";
// Mark: Place selector view.
"placeSelector.navigationBar.title" = "Places";
"placeSelector.title.search" = "Search...";
"placeSelector.title.buttonSearch" = "Search";
"placeSelector.title.cancel" = "Cancel";
"placeSelector.error.loadingPlacesFailed" = "Loading notifications failed.";
// Mark: Settings view.
"settings.navigationBar.title" = "Settings";
"settings.title.close" = "Close";
"settings.title.version" = "Version";
"settings.title.accounts" = "Accounts";
"settings.title.newAccount" = "New account";
"settings.title.accent" = "Accent";
"settings.title.theme" = "Theme";
"settings.title.system" = "System";
"settings.title.light" = "Light";
"settings.title.dark" = "Dark";
"settings.title.avatar" = "Avatar";
"settings.title.circle" = "Circle";
"settings.title.rounderRectangle" = "Rounded rectangle";
"settings.title.other" = "Other";
"settings.title.thirdParty" = "Third party";
"settings.title.reportBug" = "Report a bug";
"settings.title.githubIssues" = "Issues on Github";
"settings.title.follow" = "Follow me on Mastodon";
"settings.title.support" = "Support";
"settings.title.thankYouTitle" = "Thank you 💕";
"settings.title.thankYouMessage" = "Thanks for your purchase. Purchases both big and small help us keep our dream of providing the best quality products to our customers. We hope youre loving Vernissage.";
"settings.title.thankYouClose" = "Close";
"settings.title.haptics" = "Haptics";
"settings.title.hapticsTabSelection" = "Tab selection";
"settings.title.hapticsButtonPress" = "Button press";
"settings.title.hapticsListRefresh" = "List refresh";
"settings.title.hapticsAnimationFinished" = "Animation finished";
"settings.title.mediaSettings" = "Media settings";
"settings.title.alwaysShowSensitiveTitle" = "Always show NSFW";
"settings.title.alwaysShowSensitiveDescription" = "Force show all NFSW (sensitive) media without warnings";
"settings.title.alwaysShowAltTitle" = "Show alternative text";
"settings.title.alwaysShowAltDescription" = "Show alternative text if present on status details screen";
// Mark: Signin view.
"signin.navigationBar.title" = "Sign in to Pixelfed";
"signin.title.serverAddress" = "Server address";
"signin.title.signIn" = "Sign in";
"signin.title.enterServerAddress" = "Enter server address";
"signin.title.howToJoinLink" = "How to join Pixelfed";
"signin.title.chooseServer" = "Or choose Pixelfed server";
"signin.title.amountOfUsers" = "%d users";
"signin.title.amountOStatuses" = "%d statuses";
"signin.error.communicationFailed" = "Communication with server failed.";
// Mark: Status view.
"status.navigationBar.title" = "Details";
"status.title.uploaded" = "Uploaded";
"status.title.via" = "via %@";
"status.title.reboostedBy" = "Reboosted by";
"status.title.favouritedBy" = "Favourited by";
"status.title.openInBrowser" = "Open in browser";
"status.title.shareStatus" = "Share status";
"status.title.yourStatus" = "Your status";
"status.title.delete" = "Delete";
"status.title.reboosted" = "Reboosted";
"status.title.unreboosted" = "Unreboosted";
"status.title.favourited" = "Favourited";
"status.title.unfavourited" = "Unfavourited";
"status.title.bookmarked" = "Bookmarked";
"status.title.unbookmarked" = "Unbookmarked";
"status.title.statusDeleted" = "Status deleted";
"status.error.loadingStatusFailed" = "Loading status failed.";
"status.error.notFound" = "Status not existing anymore.";
"status.error.loadingCommentsFailed" = "Comments cannot be downloaded.";
"status.error.reboostFailed" = "Reboost action failed.";
"status.error.favouriteFailed" = "Favourite action failed.";
"status.error.bookmarkFailed" = "Bookmark action failed.";
"status.error.deleteFailed" = "Delete action failed.";
// Mark: Accounts view.
"accounts.navigationBar.followers" = "Followers";
"accounts.navigationBar.following" = "Following";
"accounts.navigationBar.favouritedBy" = "Favourited by";
"accounts.navigationBar.reboostedBy" = "Reboosted by";
"accounts.title.noAccounts" = "Unfortunately, there is no one here.";
"accounts.error.loadingAccountsFailed" = "Loading accounts failed.";
// Mark: Third party view.
"thirdParty.navigationBar.title" = "Third party";
// Mark: Widget view.
"widget.title.description" = "Widget with photos from Pixelfed.";

View File

@ -0,0 +1,231 @@
// MARK: Common strings.
"global.title.contentWarning" = "Wrażliwe treści";
"global.title.seePost" = "Pokaż zdjęcie";
"global.title.seePost" = "Odśwież";
// MARK: Global errors.
"global.error.unexpected" = "Wystąpił nieoczekiwany błąd.";
"global.error.statusesNotRetrieved" = "Statusy nie zostały pobrane.";
"global.error.errorDuringDownloadStatuses" = "Błąd podczas pobierania statusów.";
"global.error.errorDuringDownloadHashtag" = "Błąd podczas pobierania taga.";
"global.error.hashtagNotExists" = "Tag nie istnieje.";
"global.error.errorDuringImageDownload" = "Błąd podczas pobierania zdjęcia.";
"global.error.canceledImageDownload" = "Pobieranie zdjęcia zostało anulowane.";
"global.error.errorDuringDataLoad" = "Błąd podczas pobierania danych.";
// MARK: Main view (main navigation bar).
"mainview.tab.homeTimeline" = "Główna";
"mainview.tab.localTimeline" = "Lokalne";
"mainview.tab.federatedTimeline" = "Globalne";
"mainview.tab.trendingPhotos" = "Zdjęcia";
"mainview.tab.trendingTags" = "Tagi";
"mainview.tab.trendingAccounts" = "Użytkownicy";
"mainview.tab.userProfile" = "Profil";
"mainview.tab.notifications" = "Powiadomienia";
"mainview.tab.search" = "Wyszukaj";
"mainview.tab.trending" = "Popularne";
// MARK: Main view (leading navigation bar).
"mainview.menu.settings" = "Ustawienia";
// MARK: Main view (error notifications).
"mainview.error.switchAccounts" = "Błąd podczas przełączania kont.";
// MARK: Home timeline.
"home.title.allCaughtUp" = "Jesteś na bieżąco";
"home.title.noPhotos" = "Niestety nie ma jeszcze żadnych zdjęć.";
// MARK: Statuses timeline (local/federated/favourite/bookmarks etc.).
"statuses.navigationBar.localTimeline" = "Lokalne";
"statuses.navigationBar.federatedTimeline" = "Globalne";
"statuses.navigationBar.favourites" = "Polubione";
"statuses.navigationBar.bookmarks" = "Zakładki";
"statuses.title.noPhotos" = "Niestety nie ma jeszcze żadnych zdjęć.";
"statuses.title.tagFollowed" = "Od teraz śledzisz taga.";
"statuses.title.tagUnfollowed" = "Nie śledzisz już taga.";
"statuses.error.loadingStatusesFailed" = "Błąd podczas wczytywania statusów.";
"statuses.error.tagFollowFailed" = "Błąd podczas żądania śledzenia taga.";
"statuses.error.tagUnfollowFailed" = "Błąd podczas wyłączenia śledzenia taga.";
// Mark: Search view.
"search.navigationBar.title" = "Wyszukaj";
"search.title.placeholder" = "Wyszukaj...";
"search.title.usersWith" = "Użytkownicy zawierający %@";
"search.title.goToUser" = "Przejdź do użytkownika %@";
"search.title.hashtagWith" = "Tagi zawierające %@";
"search.title.goToHashtag" = "Przejdź do taga %@";
// Mark: Trending statuses.
"trendingStatuses.navigationBar.title" = "Zdjęcia";
"trendingStatuses.title.daily" = "Dzień";
"trendingStatuses.title.monthly" = "Miesiąc";
"trendingStatuses.title.yearly" = "Rok";
"trendingStatuses.error.loadingStatusesFailed" = "Błąd podczas wczytywania statusów.";
"trendingStatuses.title.noPhotos" = "Niestety nie ma jeszcze żadnych zdjęć.";
// Mark: Trending tags.
"trendingTags.navigationBar.title" = "Tags";
"trendingTags.title.noTags" = "Niestety nie ma jeszcze żadnych tagów.";
"trendingTags.title.amountOfPosts" = "%d statusów";
"trendingTags.error.loadingTagsFailed" = "Błąd podczas wczytywania tagów.";
// Mark: Trending accounts.
"trendingAccounts.navigationBar.title" = "Użytkownicy";
"trendingAccounts.title.noAccounts" = "Niestety nie ma tutaj nikogo.";
"trendingAccounts.error.loadingAccountsFailed" = "Błąd podczas wczytywania użytkownikow.";
// Mark: User profile view.
"userProfile.title.openInBrowser" = "Otwórz w przeglądarce";
"userProfile.title.share" = "Udostępnij";
"userProfile.title.unmute" = "Wyłącz wyciszenie";
"userProfile.title.mute" = "Wycisz";
"userProfile.title.unblock" = "Odblokuj";
"userProfile.title.block" = "Zablokuj";
"userProfile.title.favourites" = "Polubione";
"userProfile.title.bookmarks" = "Zakładki";
"userProfile.title.posts" = "Statusy";
"userProfile.title.followers" = "Obserwujący";
"userProfile.title.following" = "Obserwowani";
"userProfile.title.joined" = "Dołączył(a) %@";
"userProfile.title.unfollow" = "Przestań obserwować";
"userProfile.title.followBack" = "Również obserwuj";
"userProfile.title.follow" = "Obserwuj";
"userProfile.error.notExists" = "Konto nie istnieje.";
"userProfile.error.notExists" = "Błąd podczas pobierania danych użytkownika.";
"userProfile.error.mute" = "Błąd podczas wyciszania użytkownika.";
"userProfile.error.block" = "Błąd podczas blokowania/odblokowywania użytkownika.";
"userProfile.error.relationship" = "Błąd podczas zmiany relacji z użytkownikiem.";
// Mark: Notifications view.
"notifications.navigationBar.title" = "Powiadomienia";
"notifications.title.noNotifications" = "Niestety nic tutaj nie ma.";
"notifications.title.followedYou" = "obserwuje ciebie";
"notifications.title.mentionedYou" = "wspomniał ciebie";
"notifications.title.boosted" = "podbił";
"notifications.title.favourited" = "polubił";
"notifications.title.postedStatus" = "stworzył status";
"notifications.title.followRequest" = "chce obserwować";
"notifications.title.poll" = "ankieta";
"notifications.title.updatedStatus" = "zaktualizował status";
"notifications.title.signedUp" = "zalogował się";
"notifications.title.newReport" = "nowy raport";
"notifications.error.loadingNotificationsFailed" = "Błąd podczas wczytywania powiadomień.";
// Mark: Compose view.
"compose.navigationBar.title" = "Utwórz";
"compose.title.everyone" = "Publiczny";
"compose.title.unlisted" = "Publiczny (niewidoczny)";
"compose.title.followers" = "Tylko obserwujący";
"compose.title.attachPhotoFull" = "Dołącz zdjęcie i napisz, co myślisz";
"compose.title.attachPhotoMini" = "Wpisz, co masz na myśli";
"compose.title.publish" = "Wyślij";
"compose.title.cancel" = "Anuluj";
"compose.title.writeContentWarning" = "Napisz ostrzeżenie o treści";
"compose.title.commentsWillBeDisabled" = "Komentarze zostaną wyłączone";
"compose.title.statusPublished" = "Stan opublikowany";
"compose.title.tryToUpload" = "Ponów";
"compose.title.delete" = "Usuń";
"compose.title.edit" = "Edytuj";
"compose.error.loadingPhotosFailed" = "Nie można pobrać zdjęcia z biblioteki.";
"compose.error.postingPhotoFailed" = "Błąd podczas publikowania zdjęcia.";
"compose.error.postingStatusFailed" = "Błąd podczas wysyłania statusu.";
// Mark: Photo editor view.
"photoEdit.navigationBar.title" = "Szczegóły zdjęcia";
"photoEdit.title.photo" = "Zdjęcie";
"photoEdit.title.accessibility" = "Dostępność";
"photoEdit.title.accessibilityDescription" = "Opis dla osób niedowidzących";
"photoEdit.title.save" = "Zapisz";
"photoEdit.title.cancel" = "Anuluj";
"photoEdit.error.updatePhotoFailed" = "Błąd podczas aktualizowania zdjęcia.";
// Mark: Place selector view.
"placeSelector.navigationBar.title" = "Lokalizacja";
"placeSelector.title.search" = "Wyszukaj...";
"placeSelector.title.buttonSearch" = "Szukaj";
"placeSelector.title.cancel" = "Anuluj";
"placeSelector.error.loadingPlacesFailed" = "Błąd podczas wczytywanie lokalizacji.";
// Mark: Settings view.
"settings.navigationBar.title" = "Ustawienia";
"settings.title.close" = "Zamknij";
"settings.title.version" = "Wersja";
"settings.title.accounts" = "Konta";
"settings.title.newAccount" = "Dodaj konto";
"settings.title.accent" = "Akcent";
"settings.title.theme" = "Wygląd";
"settings.title.system" = "Systemowy";
"settings.title.light" = "Jasny";
"settings.title.dark" = "Ciemny";
"settings.title.avatar" = "Awatar";
"settings.title.circle" = "Okrągły";
"settings.title.rounderRectangle" = "Zaokrąglony kwadratowy";
"settings.title.other" = "Inne";
"settings.title.thirdParty" = "Zewnętrzne biblioteki";
"settings.title.reportBug" = "Zgłoś błąd";
"settings.title.githubIssues" = "Błędy na Github";
"settings.title.follow" = "Obserwuj mnie na Mastodon";
"settings.title.support" = "Wsparcie";
"settings.title.thankYouTitle" = "Dziękuję 💕";
"settings.title.thankYouMessage" = "Dziękujemy za twój zakup. Zakupy zarówno te duże, jak i te małe pomagają nam w realizacji marzenia o dostarczaniu naszym klientom produktów najwyższej jakości. Mamy nadzieję, że Vernissage spełnia Twoje oczekiwania.";
"settings.title.thankYouClose" = "Zamknij";
"settings.title.haptics" = "Haptyka";
"settings.title.hapticsTabSelection" = "Wybór zakładki";
"settings.title.hapticsButtonPress" = "Naciśnięcie przycisku";
"settings.title.hapticsListRefresh" = "Odświeżanie listy";
"settings.title.hapticsAnimationFinished" = "Zakończenie animacji";
"settings.title.mediaSettings" = "Ustawienia mediów";
"settings.title.alwaysShowSensitiveTitle" = "Zawsze pokazuj statusy NSFW";
"settings.title.alwaysShowSensitiveDescription" = "Wymuś pokazywanie statusów NFSW (czułych) bez ostrzeżeń";
"settings.title.alwaysShowAltTitle" = "Pokaż tekst alternatywny";
"settings.title.alwaysShowAltDescription" = "Pokaż alternatywny tekst, jeśli jest obecny na szczegółach statusu";
// Mark: Signin view.
"signin.navigationBar.title" = "Zaloguj się do Pixelfed";
"signin.title.serverAddress" = "Adres serwera";
"signin.title.signIn" = "Zaloguj się";
"signin.title.enterServerAddress" = "Wpisz adres serwera";
"signin.title.howToJoinLink" = "Jak przyłączyć się do Pixelfed";
"signin.title.chooseServer" = "Lub wybierz serwer Pixelfed";
"signin.title.amountOfUsers" = "%d użytkowników";
"signin.title.amountOStatuses" = "%d statusów";
"signin.error.communicationFailed" = "Błąd podczas komunikacji z serwerem.";
// Mark: Status view.
"status.navigationBar.title" = "Szczegóły";
"status.title.uploaded" = "Wysłano";
"status.title.via" = "przez %@";
"status.title.reboostedBy" = "Podbite przez";
"status.title.favouritedBy" = "Polubione przez";
"status.title.openInBrowser" = "Otwórz w przeglądarce";
"status.title.shareStatus" = "Udostępnij";
"status.title.yourStatus" = "Twój status";
"status.title.delete" = "Usuń";
"status.title.reboosted" = "Podbite";
"status.title.unreboosted" = "Podbicie wycofane";
"status.title.favourited" = "Polubione";
"status.title.unfavourited" = "Polubienie wycofane";
"status.title.bookmarked" = "Dodane do zakładek";
"status.title.unbookmarked" = "Usunięte z zakładek";
"status.title.statusDeleted" = "Status usunięty";
"status.error.loadingStatusFailed" = "Błąd podczas wczytywanie statusu.";
"status.error.notFound" = "Status już nie istnieje.";
"status.error.loadingCommentsFailed" =" Błąd podczas wczytywanie komentarzy.";
"status.error.reboostFailed" = "Błąd podczas podbijania.";
"status.error.favouriteFailed" = "Błąd podczas polubiania.";
"status.error.bookmarkFailed" = "Błąd podczas dodawania/uzuwania z zakładek.";
"status.error.deleteFailed" = "Błąd podczas usuwania.";
// Mark: Accounts view.
"accounts.navigationBar.followers" = "Obserwujący";
"accounts.navigationBar.following" = "Obserwowani";
"accounts.navigationBar.favouritedBy" = "Polubione przez";
"accounts.navigationBar.reboostedBy" = "Podbite przez";
"accounts.title.noAccounts" = "Niestety nie ma tutaj nikogo.";
"accounts.error.loadingAccountsFailed" = "Błąd podczas wczytywania użytkownikow.";
// Mark: Third party view.
"thirdParty.navigationBar.title" = "Zewnętrzne biblioteki";
// Mark: Widget view.
"widget.title.description" = "Widget ze zdjęciami z Pixelfed.";

View File

@ -27,6 +27,8 @@
F8210DEA2966E4F9001D9973 /* AnimatePlaceholderModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8210DE92966E4F9001D9973 /* AnimatePlaceholderModifier.swift */; };
F829193C2983012400367CE2 /* ImageSizeService.swift in Sources */ = {isa = PBXBuildFile; fileRef = F829193B2983012400367CE2 /* ImageSizeService.swift */; };
F8341F90295C636C009C8EE6 /* Data+Exif.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8341F8F295C636C009C8EE6 /* Data+Exif.swift */; };
F835082329BEF9C400DE3247 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = F835082629BEF9C400DE3247 /* Localizable.strings */; };
F835082429BEF9C400DE3247 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = F835082629BEF9C400DE3247 /* Localizable.strings */; };
F83901A6295D8EC000456AE2 /* LabelIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = F83901A5295D8EC000456AE2 /* LabelIcon.swift */; };
F83CBEFB298298A1002972C8 /* ImageCarouselPicture.swift in Sources */ = {isa = PBXBuildFile; fileRef = F83CBEFA298298A1002972C8 /* ImageCarouselPicture.swift */; };
F83E00ED29A2237C005D25A3 /* PixelfedKit in Frameworks */ = {isa = PBXBuildFile; productRef = F83E00EC29A2237C005D25A3 /* PixelfedKit */; };
@ -247,6 +249,8 @@
F8210DE92966E4F9001D9973 /* AnimatePlaceholderModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnimatePlaceholderModifier.swift; sourceTree = "<group>"; };
F829193B2983012400367CE2 /* ImageSizeService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageSizeService.swift; sourceTree = "<group>"; };
F8341F8F295C636C009C8EE6 /* Data+Exif.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Data+Exif.swift"; sourceTree = "<group>"; };
F835082529BEF9C400DE3247 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
F835082729BEFA1E00DE3247 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Localizable.strings; sourceTree = "<group>"; };
F837269429A221420098D3C4 /* PixelfedKit */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = PixelfedKit; sourceTree = "<group>"; };
F83901A5295D8EC000456AE2 /* LabelIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelIcon.swift; sourceTree = "<group>"; };
F83CBEFA298298A1002972C8 /* ImageCarouselPicture.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageCarouselPicture.swift; sourceTree = "<group>"; };
@ -576,6 +580,14 @@
path = CoreData;
sourceTree = "<group>";
};
F835081F29BEF88600DE3247 /* Localization */ = {
isa = PBXGroup;
children = (
F835082629BEF9C400DE3247 /* Localizable.strings */,
);
path = Localization;
sourceTree = "<group>";
};
F83901A2295D863B00456AE2 /* Widgets */ = {
isa = PBXGroup;
children = (
@ -724,6 +736,7 @@
F8F6E44329BC5CAA0004795E /* VernissageWidgetExtension.entitlements */,
F837269429A221420098D3C4 /* PixelfedKit */,
F88ABD9529687D4D004EF61E /* README.md */,
F835081F29BEF88600DE3247 /* Localization */,
F864F79C29BB9D2400B13921 /* Models */,
F8341F96295C6427009C8EE6 /* CoreData */,
F88C246A295C37B80006098B /* Vernissage */,
@ -972,6 +985,7 @@
knownRegions = (
en,
Base,
pl,
);
mainGroup = F88C245F295C37B80006098B;
packageReferences = (
@ -996,6 +1010,7 @@
buildActionMask = 2147483647;
files = (
F864F76829BB91B600B13921 /* Assets.xcassets in Resources */,
F835082429BEF9C400DE3247 /* Localizable.strings in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -1004,6 +1019,7 @@
buildActionMask = 2147483647;
files = (
F88C2473295C37BB0006098B /* Preview Assets.xcassets in Resources */,
F835082329BEF9C400DE3247 /* Localizable.strings in Resources */,
F88C2470295C37BB0006098B /* Assets.xcassets in Resources */,
F86A42FF299A8C5500DF7645 /* InAppPurchaseStoreKitConfiguration.storekit in Resources */,
);
@ -1216,6 +1232,18 @@
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
F835082629BEF9C400DE3247 /* Localizable.strings */ = {
isa = PBXVariantGroup;
children = (
F835082529BEF9C400DE3247 /* en */,
F835082729BEFA1E00DE3247 /* pl */,
);
name = Localizable.strings;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
F864F76E29BB91B600B13921 /* Debug */ = {
isa = XCBuildConfiguration;
@ -1278,6 +1306,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
@ -1338,6 +1367,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";

View File

@ -21,6 +21,7 @@ public class ErrorService {
}
}
print("Error ['\(message)']: \(error.localizedDescription)")
let localizedMessage = NSLocalizedString(message, comment: "Error message")
print("Error ['\(localizedMessage)']: \(error.localizedDescription)")
}
}

View File

@ -108,10 +108,13 @@ public class HomeTimelineService {
break
}
amountOfStatuses = amountOfStatuses + downloadedStatuses.count
// We have to include in the counter only statuses with images.
let statusesWithImagesOnly = downloadedStatuses.getStatusesWithImagesOnly()
amountOfStatuses = amountOfStatuses + statusesWithImagesOnly.count
newestStatusId = firstStatus.id
} catch {
ErrorService.shared.handle(error, message: "Error during downloading new statuses for amountof new statuses.")
ErrorService.shared.handle(error, message: "Error during downloading new statuses for amount of new statuses.")
break
}
}

View File

@ -14,7 +14,7 @@ public class ToastrService {
public func showSuccess(_ title: String, imageSystemName: String, subtitle: String? = nil) {
let drop = Drop(
title: title,
title: NSLocalizedString(title, comment: "Success displayed to the user."),
subtitle: subtitle,
subtitleNumberOfLines: 2,
icon: self.createImage(systemName: imageSystemName, color: UIColor(Color.accentColor)),
@ -29,9 +29,9 @@ public class ToastrService {
Drops.show(drop)
}
public func showError(title: String = "Unexpected error", imageSystemName: String = "ant.circle.fill", subtitle: String? = nil) {
public func showError(title: String = "global.error.unexpected", imageSystemName: String = "ant.circle.fill", subtitle: String? = nil) {
let drop = Drop(
title: title,
title: NSLocalizedString(title, comment: "Error displayed to the user."),
subtitle: subtitle,
subtitleNumberOfLines: 2,
icon: self.createImage(systemName: imageSystemName, color: UIColor(Color.red)),

View File

@ -25,7 +25,7 @@ struct AccountsPhotoView: View {
var body: some View {
self.mainBody()
.navigationTitle("Accounts")
.navigationTitle("trendingAccounts.navigationBar.title")
}
@ViewBuilder
@ -38,7 +38,7 @@ struct AccountsPhotoView: View {
}
case .loaded:
if self.accounts.isEmpty {
NoDataView(imageSystemName: "person.3.sequence", text: "Unfortunately, there is no one here.")
NoDataView(imageSystemName: "person.3.sequence", text: "trendingAccounts.title.noAccounts")
} else {
List {
ForEach(self.accounts, id: \.id) { account in
@ -84,10 +84,10 @@ struct AccountsPhotoView: View {
self.state = .loaded
} catch {
if !Task.isCancelled {
ErrorService.shared.handle(error, message: "Accounts not retrieved.", showToastr: true)
ErrorService.shared.handle(error, message: "trendingAccounts.error.loadingAccountsFailed", showToastr: true)
self.state = .error(error)
} else {
ErrorService.shared.handle(error, message: "Accounts not retrieved.", showToastr: false)
ErrorService.shared.handle(error, message: "trendingAccounts.error.loadingAccountsFailed", showToastr: false)
}
}
}

View File

@ -42,7 +42,7 @@ struct AccountsView: View {
}
case .loaded:
if self.accounts.isEmpty {
NoDataView(imageSystemName: "person.3.sequence", text: "Unfortunately, there is no one here.")
NoDataView(imageSystemName: "person.3.sequence", text: "accounts.title.noAccounts")
} else {
List {
ForEach(accounts, id: \.id) { account in
@ -92,10 +92,10 @@ struct AccountsView: View {
self.state = .loaded
} catch {
if !Task.isCancelled {
ErrorService.shared.handle(error, message: "Accounts not retrieved.", showToastr: true)
ErrorService.shared.handle(error, message: "accounts.error.loadingAccountsFailed", showToastr: true)
self.state = .error(error)
} else {
ErrorService.shared.handle(error, message: "Accounts not retrieved.", showToastr: false)
ErrorService.shared.handle(error, message: "accounts.error.loadingAccountsFailed", showToastr: false)
}
}
}
@ -115,13 +115,13 @@ struct AccountsView: View {
private func getTitle() -> String {
switch self.listType {
case .followers:
return "Followers"
return NSLocalizedString("accounts.navigationBar.followers", comment: "Followers")
case .following:
return "Following"
return NSLocalizedString("accounts.navigationBar.following", comment: "Following")
case .favourited:
return "Favourited by"
return NSLocalizedString("accounts.navigationBar.favouritedBy", comment: "Favourited by")
case .reblogged:
return "Reboosted by"
return NSLocalizedString("accounts.navigationBar.reboostedBy", comment: "Reboosted by")
case .search(let query):
return query
}

View File

@ -35,7 +35,7 @@ struct ComposeView: View {
@State private var photosAttachment: [PhotoAttachment] = []
@State private var visibility = Pixelfed.Statuses.Visibility.pub
@State private var visibilityText = "Everyone"
@State private var visibilityText: LocalizedStringKey = "compose.title.everyone"
@State private var visibilityImage = "globe.europe.africa"
@FocusState private var focusedField: FocusField?
@ -91,14 +91,14 @@ struct ComposeView: View {
ActionButton(showLoader: false) {
await self.publishStatus()
} label: {
Text("Publish")
Text("compose.title.publish", comment: "Publish")
}
.disabled(self.publishDisabled)
.buttonStyle(.borderedProminent)
}
ToolbarItem(placement: .cancellationAction) {
Button("Cancel", role: .cancel) {
Button(NSLocalizedString("compose.title.cancel", comment: "Cancel"), role: .cancel) {
dismiss()
}
}
@ -131,7 +131,7 @@ struct ComposeView: View {
selection: $selectedItems,
maxSelectionCount: self.applicationState.statusMaxMediaAttachments,
matching: .images)
.navigationTitle("Compose")
.navigationTitle("compose.navigationBar.title")
.navigationBarTitleDisplayMode(.inline)
}
.withAppRouteur()
@ -256,7 +256,7 @@ struct ComposeView: View {
@ViewBuilder
private func contentWarningView() -> some View {
if self.isSensitive {
TextField("Write content warning", text: $spoilerText, axis: .vertical)
TextField("compose.title.writeContentWarning", text: $spoilerText, axis: .vertical)
.padding(8)
.lineLimit(1...2)
.focused($focusedField, equals: .spoilerText)
@ -270,7 +270,7 @@ struct ComposeView: View {
if self.commentsDisabled {
HStack {
Spacer()
Text("Comments will be disabled")
Text("compose.title.commentsWillBeDisabled")
.textCase(.uppercase)
.font(.caption2)
.foregroundColor(.dangerColor)
@ -285,26 +285,26 @@ struct ComposeView: View {
Menu {
Button {
self.visibility = .pub
self.visibilityText = "Everyone"
self.visibilityText = "compose.title.everyone"
self.visibilityImage = "globe.europe.africa"
} label: {
Label("Everyone", systemImage: "globe.europe.africa")
Label("compose.title.everyone", systemImage: "globe.europe.africa")
}
Button {
self.visibility = .unlisted
self.visibilityText = "Unlisted"
self.visibilityText = "compose.title.unlisted"
self.visibilityImage = "lock.open"
} label: {
Label("Unlisted", systemImage: "lock.open")
Label("compose.title.unlisted", systemImage: "lock.open")
}
Button {
self.visibility = .priv
self.visibilityText = "Followers"
self.visibilityText = "compose.title.followers"
self.visibilityImage = "lock"
} label: {
Label("Followers", systemImage: "lock")
Label("compose.title.followers", systemImage: "lock")
}
} label: {
HStack {
@ -452,8 +452,8 @@ struct ComposeView: View {
.background(Color.keyboardToolbarColor)
}
private func placeholder() -> String {
self.statusViewModel == nil ? "Attach a photo and type what's on your mind" : "Type what's on your mind"
private func placeholder() -> LocalizedStringKey {
self.statusViewModel == nil ? "compose.title.attachPhotoFull" : "compose.title.attachPhotoMini"
}
private func isPublishButtonDisabled() -> Bool {
@ -528,7 +528,7 @@ struct ComposeView: View {
self.photosAreUploading = false
self.refreshScreenState()
} catch {
ErrorService.shared.handle(error, message: "Cannot retreive image from library.", showToastr: true)
ErrorService.shared.handle(error, message: "compose.error.loadingPhotosFailed", showToastr: true)
}
}
@ -567,7 +567,7 @@ struct ComposeView: View {
}
} catch {
photoAttachment.error = error
ErrorService.shared.handle(error, message: "Error during post photo.", showToastr: true)
ErrorService.shared.handle(error, message: "compose.error.postingPhotoFailed", showToastr: true)
}
}
@ -599,7 +599,7 @@ struct ComposeView: View {
do {
let status = self.createStatus()
if let newStatus = try await self.client.statuses?.new(status: status) {
ToastrService.shared.showSuccess("Status published", imageSystemName: "message.fill")
ToastrService.shared.showSuccess("compose.title.statusPublished", imageSystemName: "message.fill")
let statusModel = StatusModel(status: newStatus)
let commentModel = CommentModel(status: statusModel, showDivider: false)
@ -608,7 +608,7 @@ struct ComposeView: View {
dismiss()
}
} catch {
ErrorService.shared.handle(error, message: "Error during post status.", showToastr: true)
ErrorService.shared.handle(error, message: "compose.error.postingStatusFailed", showToastr: true)
}
}

View File

@ -28,7 +28,7 @@ struct ImageUploadView: View {
HapticService.shared.fireHaptic(of: .buttonPress)
self.upload()
} label: {
Label("Try to upload", systemImage: "exclamationmark.arrow.triangle.2.circlepath")
Label("compose.title.tryToUpload", systemImage: "exclamationmark.arrow.triangle.2.circlepath")
}
Divider()
@ -37,7 +37,7 @@ struct ImageUploadView: View {
HapticService.shared.fireHaptic(of: .buttonPress)
self.delete()
} label: {
Label("Delete", systemImage: "trash")
Label("compose.title.delete", systemImage: "trash")
.tint(.red)
}
} label: {
@ -63,7 +63,7 @@ struct ImageUploadView: View {
HapticService.shared.fireHaptic(of: .buttonPress)
self.open()
} label: {
Label("Edit", systemImage: "pencil")
Label("compose.title.edit", systemImage: "pencil")
}
Divider()
@ -72,7 +72,7 @@ struct ImageUploadView: View {
HapticService.shared.fireHaptic(of: .buttonPress)
self.delete()
} label: {
Label("Delete", systemImage: "trash")
Label("compose.title.delete", systemImage: "trash")
}
} label: {
self.imageView()

View File

@ -25,7 +25,7 @@ struct HashtagsView: View {
var body: some View {
self.mainBody()
.navigationTitle("Tags")
.navigationTitle("trendingTags.navigationBar.title")
}
@ViewBuilder
@ -38,7 +38,7 @@ struct HashtagsView: View {
}
case .loaded:
if self.tags.isEmpty {
NoDataView(imageSystemName: "person.3.sequence", text: "Unfortunately, there is no one here.")
NoDataView(imageSystemName: "person.3.sequence", text: "trendingTags.title.noTags")
} else {
List {
ForEach(self.tags, id: \.id) { tag in
@ -49,7 +49,8 @@ struct HashtagsView: View {
Text(tag.name).font(.headline)
Spacer()
if let total = tag.total {
Text("\(total) posts").font(.caption)
Text(String(format: NSLocalizedString("trendingTags.title.amountOfPosts", comment: "Amount of posts"), total))
.font(.caption)
}
}
.onTapGesture {
@ -76,10 +77,10 @@ struct HashtagsView: View {
self.state = .loaded
} catch {
if !Task.isCancelled {
ErrorService.shared.handle(error, message: "Tags not retrieved.", showToastr: true)
ErrorService.shared.handle(error, message: "trendingTags.error.loadingTagsFailed", showToastr: true)
self.state = .error(error)
} else {
ErrorService.shared.handle(error, message: "Tags not retrieved.", showToastr: false)
ErrorService.shared.handle(error, message: "trendingTags.error.loadingTagsFailed", showToastr: false)
}
}
}

View File

@ -40,7 +40,7 @@ struct HomeFeedView: View {
}
case .loaded:
if self.dbStatuses.isEmpty {
NoDataView(imageSystemName: "photo.on.rectangle.angled", text: "Unfortunately, there are no photos here.")
NoDataView(imageSystemName: "photo.on.rectangle.angled", text: "home.title.noPhotos")
} else {
self.timeline()
}
@ -77,7 +77,7 @@ struct HomeFeedView: View {
}
}
} catch {
ErrorService.shared.handle(error, message: "Error during download statuses from server.", showToastr: !Task.isCancelled)
ErrorService.shared.handle(error, message: "global.error.errorDuringDownloadStatuses", showToastr: !Task.isCancelled)
}
}
}
@ -111,7 +111,7 @@ struct HomeFeedView: View {
}
}
} catch {
ErrorService.shared.handle(error, message: "Error during download statuses from server.", showToastr: !Task.isCancelled)
ErrorService.shared.handle(error, message: "global.error.errorDuringDownloadStatuses", showToastr: !Task.isCancelled)
}
}
@ -125,10 +125,10 @@ struct HomeFeedView: View {
self.state = .loaded
} catch {
if !Task.isCancelled {
ErrorService.shared.handle(error, message: "Statuses not retrieved.", showToastr: true)
ErrorService.shared.handle(error, message: "global.error.statusesNotRetrieved", showToastr: true)
self.state = .error(error)
} else {
ErrorService.shared.handle(error, message: "Statuses not retrieved.", showToastr: false)
ErrorService.shared.handle(error, message: "global.error.statusesNotRetrieved", showToastr: false)
}
}
}
@ -167,7 +167,7 @@ struct HomeFeedView: View {
.frame(width: 64, height: 64)
.fontWeight(.ultraLight)
.foregroundColor(.accentColor.opacity(0.6))
Text("You're all caught up")
Text("home.title.allCaughtUp", comment: "You're all caught up")
.font(.title2)
.fontWeight(.thin)
.foregroundColor(Color.mainTextColor.opacity(0.6))

View File

@ -17,7 +17,7 @@ struct MainView: View {
@EnvironmentObject var routerPath: RouterPath
@EnvironmentObject var tipsStore: TipsStore
@State private var navBarTitle: String = "Home"
@State private var navBarTitle: LocalizedStringKey = "mainview.tab.homeTimeline"
@State private var viewMode: ViewMode = .home {
didSet {
self.navBarTitle = self.getViewTitle(viewMode: viewMode)
@ -159,7 +159,7 @@ struct MainView: View {
}
} label: {
HStack {
Text("Trending")
Text("mainview.tab.trending", comment: "Trending menu section")
Image(systemName: "chart.line.uptrend.xyaxis")
}
}
@ -185,7 +185,7 @@ struct MainView: View {
}
} label: {
HStack {
Text(navBarTitle)
Text(navBarTitle, comment: "Navbar title")
.font(.headline)
Image(systemName: "chevron.down")
.fontWeight(.semibold)
@ -219,7 +219,7 @@ struct MainView: View {
HapticService.shared.fireHaptic(of: .buttonPress)
self.routerPath.presentedSheet = .settings
} label: {
Label("Settings", systemImage: "gear")
Label("mainview.menu.settings", systemImage: "gear")
}
} label: {
self.getAvatarImage(avatarUrl: self.applicationState.account?.avatar,
@ -271,26 +271,26 @@ struct MainView: View {
}
}
private func getViewTitle(viewMode: ViewMode) -> String {
private func getViewTitle(viewMode: ViewMode) -> LocalizedStringKey {
switch viewMode {
case .home:
return "Home"
return "mainview.tab.homeTimeline"
case .trendingPhotos:
return "Photos"
return "mainview.tab.trendingPhotos"
case .trendingTags:
return "Tags"
return "mainview.tab.trendingTags"
case .trendingAccounts:
return "Accounts"
return "mainview.tab.trendingAccounts"
case .local:
return "Local"
return "mainview.tab.localTimeline"
case .federated:
return "Federated"
return "mainview.tab.federatedTimeline"
case .profile:
return "Profile"
return "mainview.tab.userProfile"
case .notifications:
return "Notifications"
return "mainview.tab.notifications"
case .search:
return "Search"
return "mainview.tab.search"
}
}
@ -326,7 +326,7 @@ struct MainView: View {
let authorizationSession = AuthorizationSession()
await AuthorizationService.shared.verifyAccount(session: authorizationSession, currentAccount: account) { accountData in
guard let accountData = accountData else {
ToastrService.shared.showError(subtitle: "Cannot switch accounts.")
ToastrService.shared.showError(subtitle: "mainview.error.switchAccounts")
return
}

View File

@ -23,7 +23,7 @@ struct NotificationsView: View {
var body: some View {
self.mainBody()
.navigationTitle("Notifications")
.navigationTitle("notifications.navigationBar.title")
}
@ViewBuilder
@ -36,7 +36,7 @@ struct NotificationsView: View {
}
case .loaded:
if self.notifications.isEmpty {
NoDataView(imageSystemName: "bell", text: "Unfortunately, there is nothing here.")
NoDataView(imageSystemName: "bell", text: "notifications.title.noNotifications")
} else {
List {
ForEach(notifications, id: \.id) { notification in
@ -86,10 +86,10 @@ struct NotificationsView: View {
}
} catch {
if !Task.isCancelled {
ErrorService.shared.handle(error, message: "Notifications not retrieved.", showToastr: true)
ErrorService.shared.handle(error, message: "notifications.error.loadingNotificationsFailed", showToastr: true)
self.state = .error(error)
} else {
ErrorService.shared.handle(error, message: "Notifications not retrieved.", showToastr: false)
ErrorService.shared.handle(error, message: "notifications.error.loadingNotificationsFailed", showToastr: false)
}
}
}
@ -106,7 +106,7 @@ struct NotificationsView: View {
self.notifications.append(contentsOf: linkable.data)
}
} catch {
ErrorService.shared.handle(error, message: "Error during download notifications from server.", showToastr: !Task.isCancelled)
ErrorService.shared.handle(error, message: "notifications.error.loadingNotificationsFailed", showToastr: !Task.isCancelled)
}
}
@ -122,7 +122,7 @@ struct NotificationsView: View {
self.notifications.insert(contentsOf: linkable.data, at: 0)
}
} catch {
ErrorService.shared.handle(error, message: "Error during download notifications from server.", showToastr: !Task.isCancelled)
ErrorService.shared.handle(error, message: "notifications.error.loadingNotificationsFailed", showToastr: !Task.isCancelled)
}
}
}

View File

@ -58,7 +58,7 @@ struct NotificationRowView: View {
.font(.footnote)
}
Text(self.getTitle())
Text(self.getTitle(), comment: "Notification type")
.foregroundColor(.lightGrayColor)
.font(.footnote)
.fontWeight(.light)
@ -154,28 +154,28 @@ struct NotificationRowView: View {
}
}
private func getTitle() -> String {
private func getTitle() -> LocalizedStringKey {
switch notification.type {
case .follow:
return "followed you"
return "notifications.title.followedYou"
case .mention:
return "mentioned you"
return "notifications.title.mentionedYou"
case .reblog:
return "boosted"
return "notifications.title.boosted"
case .favourite:
return "favourited"
return "notifications.title.favourited"
case .status:
return "posted status"
return "notifications.title.postedStatus"
case .followRequest:
return "follow request"
return "notifications.title.followRequest"
case .poll:
return "poll"
return "notifications.title.poll"
case .update:
return "updated post"
return "notifications.title.updatedStatus"
case .adminSignUp:
return "signed up"
return "notifications.title.signedUp"
case .adminReport:
return "new report"
return "notifications.title.newReport"
}
}

View File

@ -41,7 +41,7 @@ struct PaginableStatusesView: View {
}
case .loaded:
if self.statusViewModels.isEmpty {
NoDataView(imageSystemName: "photo.on.rectangle.angled", text: "Unfortunately, there are no photos here.")
NoDataView(imageSystemName: "photo.on.rectangle.angled", text: "statuses.title.noPhotos")
} else {
ScrollView {
LazyVStack(alignment: .center) {
@ -66,7 +66,7 @@ struct PaginableStatusesView: View {
do {
try await self.loadMoreStatuses()
} catch {
ErrorService.shared.handle(error, message: "Loading more statuses failed.", showToastr: !Task.isCancelled)
ErrorService.shared.handle(error, message: "statuses.error.loadingStatusesFailed", showToastr: !Task.isCancelled)
}
}
Spacer()
@ -92,7 +92,7 @@ struct PaginableStatusesView: View {
try await self.loadStatuses()
self.state = .loaded
} catch {
ErrorService.shared.handle(error, message: "Loading statuses failed.", showToastr: !Task.isCancelled)
ErrorService.shared.handle(error, message: "statuses.error.loadingStatusesFailed", showToastr: !Task.isCancelled)
self.state = .error(error)
}
}
@ -144,12 +144,12 @@ struct PaginableStatusesView: View {
}
}
private func getTitle() -> String {
private func getTitle() -> LocalizedStringKey {
switch self.listType {
case .favourites:
return "Favourites"
return "statuses.navigationBar.favourites"
case .bookmarks:
return "Bookmarks"
return "statuses.navigationBar.bookmarks"
}
}
}

View File

@ -18,7 +18,7 @@ struct PhotoEditorView: View {
VStack(alignment: .leading) {
if let data = photoAttachment.photoData, let uiImage = UIImage(data: data) {
List {
Section(header: Text("Photo")) {
Section(header: Text("photoEdit.title.photo")) {
HStack {
Spacer()
Image(uiImage: uiImage)
@ -30,8 +30,8 @@ struct PhotoEditorView: View {
}
}
Section(header: Text("Accessibility")) {
TextField("Description for the visually impaired", text: $description, axis: .vertical)
Section(header: Text("photoEdit.title.accessibility")) {
TextField("photoEdit.title.accessibilityDescription", text: $description, axis: .vertical)
.keyboardType(.default)
.lineLimit(3...6)
.multilineTextAlignment(.leading)
@ -47,7 +47,7 @@ struct PhotoEditorView: View {
.onAppear {
self.description = self.photoAttachment.uploadedAttachment?.description ?? String.empty()
}
.navigationTitle("Photo details")
.navigationTitle("photoEdit.navigationBar.title")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
self.getTrailingToolbar()
@ -61,12 +61,12 @@ struct PhotoEditorView: View {
ActionButton(showLoader: false) {
await self.update()
} label: {
Text("Save")
Text("photoEdit.title.save", comment: "Save")
}.buttonStyle(.borderedProminent)
}
ToolbarItem(placement: .cancellationAction) {
Button("Cancel", role: .cancel) {
Button(NSLocalizedString("photoEdit.title.cancel", comment: "Cancel"), role: .cancel) {
dismiss()
}
}
@ -85,7 +85,7 @@ struct PhotoEditorView: View {
self.photoAttachment.uploadedAttachment = updated
self.dismiss()
} catch {
ErrorService.shared.handle(error, message: "Cannot update attachment.", showToastr: true)
ErrorService.shared.handle(error, message: "photoEdit.error.updatePhotoFailed", showToastr: true)
}
}
}

View File

@ -30,7 +30,7 @@ struct PlaceSelectorView: View {
List {
Section {
HStack {
TextField("Search...", text: $query)
TextField("placeSelector.title.search", text: $query)
.padding(8)
.focused($focusedField, equals: .search)
.keyboardType(.default)
@ -43,7 +43,7 @@ struct PlaceSelectorView: View {
await self.searchPlaces()
}
} label: {
Text("Search")
Text("placeSelector.title.buttonSearch", comment: "Search")
}
.buttonStyle(.bordered)
@ -86,7 +86,7 @@ struct PlaceSelectorView: View {
}
}
}
.navigationTitle("Places")
.navigationTitle("placeSelector.navigationBar.title")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
self.getTrailingToolbar()
@ -97,7 +97,7 @@ struct PlaceSelectorView: View {
@ToolbarContentBuilder
private func getTrailingToolbar() -> some ToolbarContent {
ToolbarItem(placement: .cancellationAction) {
Button("Cancel", role: .cancel) {
Button(NSLocalizedString("placeSelector.title.cancel", comment: "Cancel"), role: .cancel) {
self.dismiss()
}
}
@ -111,7 +111,7 @@ struct PlaceSelectorView: View {
self.places = placesFromApi
}
} catch {
ErrorService.shared.handle(error, message: "Cannot download places.", showToastr: true)
ErrorService.shared.handle(error, message: "placeSelector.error.loadingPlacesFailed", showToastr: true)
}
self.showLoader = false

View File

@ -20,7 +20,7 @@ struct SearchView: View {
var body: some View {
List {
Section {
TextField("Search...", text: $query)
TextField("search.title.placeholder", text: $query)
.padding(8)
.focused($focusedField, equals: .search)
.keyboardType(.default)
@ -36,25 +36,29 @@ struct SearchView: View {
if self.query.isEmpty == false {
Section {
NavigationLink(value: RouteurDestinations.accountsPhoto(listType: .search(query: self.query))) {
Label("Users with \"\(self.query)\"", systemImage: "person.3.sequence")
Label(String(format: NSLocalizedString("search.title.usersWith", comment: "Users with "), self.query),
systemImage: "person.3.sequence")
}
NavigationLink(value: RouteurDestinations.userProfile(accountId: "", accountDisplayName: "", accountUserName: self.query)) {
Label("Go to user \"@\(self.query)\"", systemImage: "person.crop.circle")
Label(String(format: NSLocalizedString("search.title.goToUser", comment: "Go to user "), "\"@\(self.query)\""),
systemImage: "person.crop.circle")
}
}
Section {
NavigationLink(value: RouteurDestinations.hashtags(listType: .search(query: self.query))) {
Label("Hashtags with \"\(self.query)\"", systemImage: "tag")
Label(String(format: NSLocalizedString("search.title.hashtagWith", comment: "Hashtags with "), self.query),
systemImage: "tag")
}
NavigationLink(value: RouteurDestinations.statuses(listType: .hashtag(tag: self.query))) {
Label("Go to hashtag \"#\(self.query)\"", systemImage: "tag.circle")
Label(String(format: NSLocalizedString("search.title.goToHashtag", comment: "Go to hashtag "), "\"#\(self.query)\""),
systemImage: "tag.circle")
}
}
}
}
.navigationTitle("Search")
.navigationTitle("search.navigationBar.title")
}
}

View File

@ -52,7 +52,7 @@ struct SettingsView: View {
.frame(alignment: .topLeading)
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button("Close", role: .cancel) {
Button(NSLocalizedString("settings.title.close", comment: "Close"), role: .cancel) {
self.dismiss()
}
}
@ -64,7 +64,7 @@ struct SettingsView: View {
.onReceive(NotificationCenter.default.publisher(for: UIApplication.didBecomeActiveNotification), perform: { _ in
self.theme = applicationState.theme.colorScheme() ?? self.getSystemColorScheme()
})
.navigationTitle("Settings")
.navigationTitle("settings.navigationBar.title")
.navigationBarTitleDisplayMode(.inline)
.preferredColorScheme(self.theme)
}
@ -95,7 +95,7 @@ struct SettingsView: View {
private func version() -> some View {
Section() {
HStack {
Text("Version")
Text("settings.title.version", comment: "Version")
Spacer()
Text("\(appVersion ?? String.empty()) (\(appBundleVersion ?? String.empty()))")
.foregroundColor(.accentColor)

View File

@ -13,7 +13,7 @@ struct AccentsSectionView: View {
private let accentColors2: [TintColor] = [.accentColor6, .accentColor7, .accentColor8, .accentColor9, .accentColor10]
var body: some View {
Section("Accent") {
Section("settings.title.accent") {
VStack(alignment: .leading) {
HStack(alignment: .center) {
ForEach(accentColors1, id: \.self) { color in

View File

@ -14,7 +14,7 @@ struct AccountsSectionView: View {
@State private var dbAccounts: [AccountData] = []
var body: some View {
Section("Accounts") {
Section("settings.title.accounts") {
ForEach(self.accounts) { account in
HStack(alignment: .center) {
UsernameRow(accountId: account.id,
@ -33,7 +33,7 @@ struct AccountsSectionView: View {
NavigationLink(value: RouteurDestinations.signIn) {
HStack {
Text("New account")
Text("settings.title.newAccount", comment: "New account")
Spacer()
Image(systemName: "person.crop.circle.badge.plus")
}

View File

@ -10,7 +10,7 @@ struct AvatarShapesSectionView: View {
@EnvironmentObject var applicationState: ApplicationState
var body: some View {
Section("Avatar") {
Section("settings.title.avatar") {
Button {
self.applicationState.avatarShape = .circle
ApplicationSettingsHandler.shared.setDefaultAvatarShape(avatarShape: .circle)
@ -22,7 +22,7 @@ struct AvatarShapesSectionView: View {
.aspectRatio(contentMode: .fit)
.frame(width: 32, height: 32)
Text("Circle")
Text("settings.title.circle", comment: "Circle")
.foregroundColor(.label)
Spacer()
@ -44,7 +44,7 @@ struct AvatarShapesSectionView: View {
.aspectRatio(contentMode: .fit)
.frame(width: 32, height: 32)
Text("Rounded rectangle")
Text("settings.title.rounderRectangle", comment: "Rounded rectangle")
.foregroundColor(.label)
Spacer()

View File

@ -17,27 +17,27 @@ struct HapticsSectionView: View {
@State var hapticNotificationEnabled = true
var body: some View {
Section("Haptics") {
Section("settings.title.haptics") {
Toggle("Tab selection", isOn: $hapticTabSelectionEnabled)
Toggle("settings.title.hapticsTabSelection", isOn: $hapticTabSelectionEnabled)
.onChange(of: hapticTabSelectionEnabled) { newValue in
self.applicationState.hapticTabSelectionEnabled = newValue
ApplicationSettingsHandler.shared.setHapticTabSelectionEnabled(value: newValue)
}
Toggle("Button press", isOn: $hapticButtonPressEnabled)
Toggle("settings.title.hapticsButtonPress", isOn: $hapticButtonPressEnabled)
.onChange(of: hapticButtonPressEnabled) { newValue in
self.applicationState.hapticButtonPressEnabled = newValue
ApplicationSettingsHandler.shared.setHapticButtonPressEnabled(value: newValue)
}
Toggle("List refresh", isOn: $hapticRefreshEnabled)
Toggle("settings.title.hapticsListRefresh", isOn: $hapticRefreshEnabled)
.onChange(of: hapticRefreshEnabled) { newValue in
self.applicationState.hapticRefreshEnabled = newValue
ApplicationSettingsHandler.shared.setHapticRefreshEnabled(value: newValue)
}
Toggle("Animation finished", isOn: $hapticAnimationEnabled)
Toggle("settings.title.hapticsAnimationFinished", isOn: $hapticAnimationEnabled)
.onChange(of: hapticAnimationEnabled) { newValue in
self.applicationState.hapticAnimationEnabled = newValue
ApplicationSettingsHandler.shared.setHapticAnimationEnabled(value: newValue)

View File

@ -14,12 +14,12 @@ struct MediaSettingsView: View {
@State var showPhotoDescription = true
var body: some View {
Section("Media settings") {
Section("settings.title.mediaSettings") {
Toggle(isOn: $showSensitive) {
VStack(alignment: .leading) {
Text("Always show NSFW")
Text("Force show all NFSW (sensitive) media without warnings")
Text("settings.title.alwaysShowSensitiveTitle", comment: "Always show NSFW")
Text("settings.title.alwaysShowSensitiveDescription", comment: "Force show all NFSW (sensitive) media without warnings")
.font(.footnote)
.foregroundColor(.lightGrayColor)
}
@ -31,8 +31,8 @@ struct MediaSettingsView: View {
Toggle(isOn: $showPhotoDescription) {
VStack(alignment: .leading) {
Text("Show alternative text")
Text("Show alternative text if present on status details screen")
Text("settings.title.alwaysShowAltTitle", comment: "Show alternative text")
Text("settings.title.alwaysShowAltDescription", comment: "Show alternative text if present on status details screen")
.font(.footnote)
.foregroundColor(.lightGrayColor)
}

View File

@ -8,20 +8,20 @@ import SwiftUI
struct OtherSectionView: View {
var body: some View {
Section("Other") {
Section("settings.title.other") {
NavigationLink(value: RouteurDestinations.thirdParty) {
Text("Third party")
Text("settings.title.thirdParty", comment: "Third party")
}
HStack {
Text("Report a bug")
Text("settings.title.reportBug", comment: "Report a bug")
Spacer()
Link("Issues on Github", destination: URL(string: "https://github.com/VernissageApp/Home/issues")!)
Link(NSLocalizedString("settings.title.githubIssues", comment: "Issues on GitHub"), destination: URL(string: "https://github.com/VernissageApp/Home/issues")!)
.font(.footnote)
}
HStack {
Text("Follow me on Mastodon")
Text("settings.title.follow", comment: "Follow me on Mastodon")
Spacer()
Link("@mczachurski", destination: URL(string: "https://mastodon.social/@mczachurski")!)
.font(.footnote)

View File

@ -11,7 +11,7 @@ struct SupportView: View {
@EnvironmentObject var tipsStore: TipsStore
var body: some View {
Section("Support") {
Section("settings.title.support") {
ForEach(tipsStore.items) { product in
HStack(alignment: .center) {
Text(self.getIcon(for: product))

View File

@ -14,17 +14,17 @@ struct ThanksView: View {
Spacer()
VStack {
Group {
Text("Thank you 💕")
Text("settings.title.thankYouTitle", comment: "Thank you 💕")
.font(.title3)
.fontWeight(.bold)
.foregroundColor(.viewTextColor)
.padding(.top, 8)
Text("Thanks for your purchase. Purchases both big and small help us keep our dream of providing the best quality products to our customers. We hope youre loving Vernissage.")
Text("settings.title.thankYouMessage", comment: "Thank you message")
.font(.footnote)
.multilineTextAlignment(.center)
.foregroundColor(.viewTextColor)
Button("Close") {
Button(NSLocalizedString("settings.title.thankYouClose", comment: "Close")) {
HapticService.shared.fireHaptic(of: .buttonPress)
withAnimation(.spring()) {

View File

@ -11,13 +11,13 @@ struct ThemeSectionView: View {
@Environment(\.colorScheme) var colorScheme
var body: some View {
Section("Theme") {
Section("settings.title.theme") {
Button {
self.applicationState.theme = .system
ApplicationSettingsHandler.shared.setDefaultTheme(theme: .system)
} label: {
HStack {
Text("System")
Text("settings.title.system", comment: "System")
.foregroundColor(.label)
Spacer()
if self.applicationState.theme == .system {
@ -31,7 +31,7 @@ struct ThemeSectionView: View {
ApplicationSettingsHandler.shared.setDefaultTheme(theme: .light)
} label: {
HStack {
Text("Light")
Text("settings.title.light", comment: "Light")
.foregroundColor(.label)
Spacer()
if self.applicationState.theme == .light {
@ -45,7 +45,7 @@ struct ThemeSectionView: View {
ApplicationSettingsHandler.shared.setDefaultTheme(theme: .dark)
} label: {
HStack {
Text("Dark")
Text("settings.title.dark", comment: "Dark")
.foregroundColor(.label)
Spacer()
if self.applicationState.theme == .dark {

View File

@ -26,7 +26,7 @@ struct SignInView: View {
Section {
VStack(alignment: .center) {
HStack(alignment: .center, spacing: 4) {
TextField("Server address", text: $serverAddress)
TextField("signin.title.serverAddress", text: $serverAddress)
.onSubmit {
let baseAddress = self.getServerAddress(uri: self.serverAddress)
self.signIn(baseAddress: baseAddress)
@ -36,7 +36,7 @@ struct SignInView: View {
.disableAutocorrection(true)
.clearButton(text: $serverAddress)
Button("Sign in") {
Button(NSLocalizedString("signin.title.signIn", comment: "Sign in")) {
HapticService.shared.fireHaptic(of: .buttonPress)
let baseAddress = self.getServerAddress(uri: self.serverAddress)
@ -49,19 +49,19 @@ struct SignInView: View {
.buttonStyle(PlainButtonStyle())
} header: {
Text("Enter server address")
Text("signin.title.enterServerAddress", comment: "Enter server address")
} footer: {
if let instructionsUrlString = self.instructionsUrlString,
let instructionsUrl = URL(string: instructionsUrlString) {
HStack {
Spacer()
Link("How to join Pixelfed", destination: instructionsUrl)
Link(NSLocalizedString("signin.title.howToJoinLink", comment: "How to join Pixelfed"), destination: instructionsUrl)
.font(.caption)
}
}
}
Section("Or choose Pixelfed server") {
Section("signin.title.chooseServer") {
if self.instances.isEmpty {
HStack {
Spacer()
@ -83,7 +83,7 @@ struct SignInView: View {
self.instances = await self.client.instances.instances(instanceUrls: metadata.instances)
self.instructionsUrlString = metadata.instructionsUrl
}
.navigationTitle("Sign in to Pixelfed")
.navigationTitle("signin.navigationBar.title")
.navigationBarTitleDisplayMode(.inline)
}
@ -102,7 +102,7 @@ struct SignInView: View {
ErrorService.shared.handle(error, message: error.localizedDescription, showToastr: true)
}
catch {
ErrorService.shared.handle(error, message: "Communication with server failed.", showToastr: true)
ErrorService.shared.handle(error, message: "signin.error.communicationFailed", showToastr: true)
}
}
}

View File

@ -47,7 +47,7 @@ struct InstanceRowView: View {
Spacer()
Button("Sign in") {
Button(NSLocalizedString("signin.title.signIn", comment: "Sign in")) {
HapticService.shared.fireHaptic(of: .buttonPress)
self.action(instance.uri)
}
@ -66,10 +66,10 @@ struct InstanceRowView: View {
if let stats = instance.stats {
HStack {
Image(systemName: "person.2.fill")
Text("\(stats.userCount) users")
Text(String(format: NSLocalizedString("signin.title.amountOfUsers", comment: "users"), stats.userCount))
Image(systemName: "photo.stack.fill")
Text("\(stats.statusCount) posts")
Text(String(format: NSLocalizedString("signin.title.amountOStatuses", comment: "statuses"), stats.statusCount))
Spacer()
}

View File

@ -35,7 +35,7 @@ struct StatusView: View {
var body: some View {
self.mainBody()
.navigationTitle("Details")
.navigationTitle("status.navigationBar.title")
.fullScreenCover(item: $tappedAttachmentModel, content: { attachmentModel in
ImageViewer(attachmentModel: attachmentModel)
})
@ -102,11 +102,11 @@ struct StatusView: View {
.foregroundColor(.lightGrayColor)
HStack {
Text("Uploaded")
Text("status.title.uploaded", comment: "Uploaded")
Text(statusViewModel.createdAt.toRelative(.isoDateTimeMilliSec))
.padding(.horizontal, -4)
if let applicationName = statusViewModel.application?.name {
Text("via \(applicationName)")
Text(String(format: NSLocalizedString("status.title.via", comment: "via"), applicationName))
}
}
.foregroundColor(.lightGrayColor)
@ -179,16 +179,16 @@ struct StatusView: View {
} catch NetworkError.notSuccessResponse(let response) {
if response.statusCode() == HTTPStatusCode.notFound, let accountId = self.applicationState.account?.id {
StatusDataHandler.shared.remove(accountId: accountId, statusId: self.statusId)
ErrorService.shared.handle(NetworkError.notSuccessResponse(response), message: "Status not existing anymore.", showToastr: true)
ErrorService.shared.handle(NetworkError.notSuccessResponse(response), message: "status.error.notFound", showToastr: true)
self.dismiss()
}
}
catch {
if !Task.isCancelled {
ErrorService.shared.handle(error, message: "Status not retreived.", showToastr: true)
ErrorService.shared.handle(error, message: "status.error.loadingStatusFailed", showToastr: true)
self.state = .loaded
} else {
ErrorService.shared.handle(error, message: "Status not retreived.", showToastr: false)
ErrorService.shared.handle(error, message: "status.error.loadingStatusFailed", showToastr: false)
}
}
}

View File

@ -69,7 +69,7 @@ struct CommentsSectionView: View {
do {
self.commentViewModels = try await self.client.statuses?.comments(to: statusId) ?? []
} catch {
ErrorService.shared.handle(error, message: "Comments cannot be downloaded.", showToastr: !Task.isCancelled)
ErrorService.shared.handle(error, message: "status.error.loadingCommentsFailed", showToastr: !Task.isCancelled)
}
}
}

View File

@ -50,7 +50,7 @@ struct StatusesView: View {
}
case .loaded:
if self.statusViewModels.isEmpty {
NoDataView(imageSystemName: "photo.on.rectangle.angled", text: "Unfortunately, there are no photos here.")
NoDataView(imageSystemName: "photo.on.rectangle.angled", text: "statuses.title.noPhotos")
} else {
ScrollView {
LazyVStack(alignment: .center) {
@ -75,7 +75,7 @@ struct StatusesView: View {
do {
try await self.loadMoreStatuses()
} catch {
ErrorService.shared.handle(error, message: "Loading more statuses failed.", showToastr: !Task.isCancelled)
ErrorService.shared.handle(error, message: "statuses.error.loadingStatusesFailed", showToastr: !Task.isCancelled)
}
}
Spacer()
@ -89,7 +89,7 @@ struct StatusesView: View {
try await self.loadTopStatuses()
HapticService.shared.fireHaptic(of: .dataRefresh(intensity: 0.7))
} catch {
ErrorService.shared.handle(error, message: "Loading statuses failed.", showToastr: !Task.isCancelled)
ErrorService.shared.handle(error, message: "statuses.error.loadingStatusesFailed", showToastr: !Task.isCancelled)
}
}
}
@ -112,7 +112,7 @@ struct StatusesView: View {
self.state = .loaded
} catch {
ErrorService.shared.handle(error, message: "Loading statuses failed.", showToastr: !Task.isCancelled)
ErrorService.shared.handle(error, message: "statuses.error.loadingStatusesFailed", showToastr: !Task.isCancelled)
self.state = .error(error)
}
}
@ -197,7 +197,7 @@ struct StatusesView: View {
case .hashtag(let tag):
let hashtagsFromApi = try await self.client.search?.search(query: tag, resultsType: .hashtags)
guard let hashtagsFromApi = hashtagsFromApi, hashtagsFromApi.hashtags.isEmpty == false else {
ToastrService.shared.showError(title: "Hashtag not exists", imageSystemName: "exclamationmark.octagon")
ToastrService.shared.showError(title: "global.error.hashtagNotExists", imageSystemName: "exclamationmark.octagon")
dismiss()
return []
@ -214,16 +214,16 @@ struct StatusesView: View {
}
}
private func getTitle() -> String {
private func getTitle() -> LocalizedStringKey {
switch self.listType {
case .local:
return "Local"
return "statuses.navigationBar.localTimeline"
case .federated:
return "Federated"
return "statuses.navigationBar.federatedTimeline"
case .favourites:
return "Favourites"
return "statuses.navigationBar.favourites"
case .bookmarks:
return "Bookmarks"
return "statuses.navigationBar.bookmarks"
case .hashtag(let tag):
return "#\(tag)"
}
@ -253,25 +253,25 @@ struct StatusesView: View {
do {
self.tag = try await self.client.tags?.get(tag: hashtag)
} catch {
ErrorService.shared.handle(error, message: "Error during loading tag from server.", showToastr: false)
ErrorService.shared.handle(error, message: "global.error.errorDuringDownloadHashtag", showToastr: false)
}
}
private func follow(hashtag: String) async {
do {
self.tag = try await self.client.tags?.follow(tag: hashtag)
ToastrService.shared.showSuccess("You are following the tag.", imageSystemName: "number.square.fill")
ToastrService.shared.showSuccess("statuses.title.tagFollowed", imageSystemName: "number.square.fill")
} catch {
ErrorService.shared.handle(error, message: "Follow tag failed.", showToastr: true)
ErrorService.shared.handle(error, message: "statuses.error.tagFollowFailed", showToastr: true)
}
}
private func unfollow(hashtag: String) async {
do {
self.tag = try await self.client.tags?.unfollow(tag: hashtag)
ToastrService.shared.showSuccess("Tag has been unfollowed.", imageSystemName: "number.square")
ToastrService.shared.showSuccess("statuses.title.tagUnfollowed", imageSystemName: "number.square")
} catch {
ErrorService.shared.handle(error, message: "Unfollow tag failed.", showToastr: true)
ErrorService.shared.handle(error, message: "statuses.error.tagUnfollowFailed", showToastr: true)
}
}
}

View File

@ -69,7 +69,7 @@ struct ThirdPartyView: View {
.font(.footnote)
}
}
.navigationTitle("Third party")
.navigationTitle("thirdParty.navigationBar.title")
.navigationBarTitleDisplayMode(.inline)
}
}

View File

@ -20,9 +20,9 @@ struct TrendStatusesView: View {
var body: some View {
ScrollView {
Picker(selection: $tabSelectedValue, label: Text("")) {
Text("Daily").tag(Pixelfed.Trends.TrendRange.daily)
Text("Monthly").tag(Pixelfed.Trends.TrendRange.monthly)
Text("Yearly").tag(Pixelfed.Trends.TrendRange.yearly)
Text("trendingStatuses.title.daily", comment: "Daily").tag(Pixelfed.Trends.TrendRange.daily)
Text("trendingStatuses.title.monthly", comment: "Monthly").tag(Pixelfed.Trends.TrendRange.monthly)
Text("trendingStatuses.title.yearly", comment: "Yearly").tag(Pixelfed.Trends.TrendRange.yearly)
}
.padding()
@ -34,14 +34,14 @@ struct TrendStatusesView: View {
self.statusViewModels = []
try await self.loadStatuses()
} catch {
ErrorService.shared.handle(error, message: "Loading statuses failed.", showToastr: !Task.isCancelled)
ErrorService.shared.handle(error, message: "trendingStatuses.error.loadingStatusesFailed", showToastr: !Task.isCancelled)
}
}
}
self.mainBody()
}
.navigationTitle("Trends")
.navigationTitle("trendingStatuses.navigationBar.title")
}
@ViewBuilder
@ -55,16 +55,16 @@ struct TrendStatusesView: View {
self.state = .loaded
} catch {
if !Task.isCancelled {
ErrorService.shared.handle(error, message: "Loading statuses failed.", showToastr: true)
ErrorService.shared.handle(error, message: "trendingStatuses.error.loadingStatusesFailed", showToastr: true)
self.state = .error(error)
} else {
ErrorService.shared.handle(error, message: "Loading statuses failed.", showToastr: false)
ErrorService.shared.handle(error, message: "trendingStatuses.error.loadingStatusesFailed", showToastr: false)
}
}
}
case .loaded:
if self.statusViewModels.isEmpty {
NoDataView(imageSystemName: "photo.on.rectangle.angled", text: "Unfortunately, there are no photos here.")
NoDataView(imageSystemName: "photo.on.rectangle.angled", text: "trendingStatuses.title.noPhotos")
} else {
LazyVStack(alignment: .center) {
ForEach(self.statusViewModels, id: \.id) { item in
@ -86,7 +86,7 @@ struct TrendStatusesView: View {
try await self.loadStatuses()
HapticService.shared.fireHaptic(of: .dataRefresh(intensity: 0.7))
} catch {
ErrorService.shared.handle(error, message: "Loading statuses failed.", showToastr: !Task.isCancelled)
ErrorService.shared.handle(error, message: "trendingStatuses.error.loadingStatusesFailed", showToastr: !Task.isCancelled)
}
}
}
@ -99,10 +99,10 @@ struct TrendStatusesView: View {
self.state = .loaded
} catch {
if !Task.isCancelled {
ErrorService.shared.handle(error, message: "Loading statuses failed.", showToastr: true)
ErrorService.shared.handle(error, message: "trendingStatuses.error.loadingStatusesFailed", showToastr: true)
self.state = .error(error)
} else {
ErrorService.shared.handle(error, message: "Loading statuses failed.", showToastr: false)
ErrorService.shared.handle(error, message: "trendingStatuses.error.loadingStatusesFailed", showToastr: false)
}
}
}

View File

@ -25,7 +25,7 @@ struct UserProfileHeaderView: View {
VStack(alignment: .center) {
Text("\(account.statusesCount)")
.font(.title3)
Text("Posts")
Text("userProfile.title.posts", comment: "Posts")
.font(.subheadline)
.opacity(0.6)
}
@ -36,7 +36,7 @@ struct UserProfileHeaderView: View {
VStack(alignment: .center) {
Text("\(account.followersCount)")
.font(.title3)
Text("Followers")
Text("userProfile.title.followers", comment: "Followers")
.font(.subheadline)
.opacity(0.6)
}
@ -48,7 +48,7 @@ struct UserProfileHeaderView: View {
VStack(alignment: .center) {
Text("\(account.followingCount)")
.font(.title3)
Text("Following")
Text("userProfile.title.following", comment: "Following")
.font(.subheadline)
.opacity(0.6)
}
@ -80,7 +80,7 @@ struct UserProfileHeaderView: View {
})
}
Text("Joined \(account.createdAt.toRelative(.isoDateTimeMilliSec))")
Text(String(format: NSLocalizedString("userProfile.title.joined", comment: "Joined"), account.createdAt.toRelative(.isoDateTimeMilliSec)))
.foregroundColor(.lightGrayColor.opacity(0.5))
.font(.footnote)
@ -95,7 +95,7 @@ struct UserProfileHeaderView: View {
} label: {
HStack {
Image(systemName: relationship?.following == true ? "person.badge.minus" : "person.badge.plus")
Text(relationship?.following == true ? "Unfollow" : (relationship?.followedBy == true ? "Follow back" : "Follow"))
Text(relationship?.following == true ? "userProfile.title.unfollow" : (relationship?.followedBy == true ? "userProfile.title.followBack" : "userProfile.title.follow"), comment: "Follow/unfollow actions")
}
}
.buttonStyle(.borderedProminent)
@ -114,7 +114,7 @@ struct UserProfileHeaderView: View {
}
}
} catch {
ErrorService.shared.handle(error, message: "Relationship action failed.", showToastr: true)
ErrorService.shared.handle(error, message: "userProfile.error.relationship", showToastr: true)
}
}
}

View File

@ -42,7 +42,7 @@ struct UserProfileStatusesView: View {
do {
try await self.loadMoreStatuses()
} catch {
ErrorService.shared.handle(error, message: "Loading more statuses failed.", showToastr: true)
ErrorService.shared.handle(error, message: "global.error.errorDuringDownloadStatuses", showToastr: true)
}
}
Spacer()
@ -56,7 +56,7 @@ struct UserProfileStatusesView: View {
do {
try await self.loadStatuses()
} catch {
ErrorService.shared.handle(error, message: "Loading statuses failed.", showToastr: !Task.isCancelled)
ErrorService.shared.handle(error, message: "global.error.errorDuringDownloadStatuses", showToastr: !Task.isCancelled)
}
}
}

View File

@ -66,7 +66,7 @@ struct UserProfileView: View {
if let accountFromApi = accountsFromApi?.accounts.first {
self.accountId = accountFromApi.id
} else {
ToastrService.shared.showError(title: "Account not exists", imageSystemName: "exclamationmark.octagon")
ToastrService.shared.showError(title: "userProfile.error.notExists", imageSystemName: "exclamationmark.octagon")
dismiss()
return
@ -81,7 +81,7 @@ struct UserProfileView: View {
self.state = .loaded
} catch {
ErrorService.shared.handle(error, message: "Error during download account from server.", showToastr: !Task.isCancelled)
ErrorService.shared.handle(error, message: "userProfile.error.loadingAccountFailed", showToastr: !Task.isCancelled)
self.state = .error(error)
}
}
@ -92,11 +92,11 @@ struct UserProfileView: View {
Menu (content: {
if let accountUrl = account.url {
Link(destination: accountUrl) {
Label("Open in browser", systemImage: "safari")
Label(NSLocalizedString("userProfile.title.openInBrowser", comment: "Open in browser"), systemImage: "safari")
}
ShareLink(item: accountUrl) {
Label("Share", systemImage: "square.and.arrow.up")
Label(NSLocalizedString("userProfile.title.share", comment: "Share"), systemImage: "square.and.arrow.up")
}
Divider()
@ -108,9 +108,9 @@ struct UserProfileView: View {
}
} label: {
if self.relationship?.muting == true {
Label("Unute", systemImage: "message.and.waveform.fill")
Label(NSLocalizedString("userProfile.title.unmute", comment: "Unute"), systemImage: "message.and.waveform.fill")
} else {
Label("Mute", systemImage: "message.and.waveform")
Label(NSLocalizedString("userProfile.title.mute", comment: "Mute"), systemImage: "message.and.waveform")
}
}
@ -120,9 +120,9 @@ struct UserProfileView: View {
}
} label: {
if self.relationship?.blocking == true {
Label("Unblock", systemImage: "hand.raised.fill")
Label(NSLocalizedString("userProfile.title.unblock", comment: "Unblock"), systemImage: "hand.raised.fill")
} else {
Label("Block", systemImage: "hand.raised")
Label(NSLocalizedString("userProfile.title.block", comment: "Block"), systemImage: "hand.raised")
}
}
@ -141,22 +141,22 @@ struct UserProfileView: View {
Menu (content: {
if let accountUrl = account.url {
Link(destination: accountUrl) {
Label("Open in browser", systemImage: "safari")
Label(NSLocalizedString("userProfile.title.openInBrowser", comment: "Open in browser"), systemImage: "safari")
}
ShareLink(item: accountUrl) {
Label("Share", systemImage: "square.and.arrow.up")
Label(NSLocalizedString("userProfile.title.share", comment: "Share"), systemImage: "square.and.arrow.up")
}
Divider()
}
NavigationLink(value: RouteurDestinations.favourites) {
Label("Favourites", systemImage: "hand.thumbsup")
Label(NSLocalizedString("userProfile.title.favourites", comment: "Favourites"), systemImage: "hand.thumbsup")
}
NavigationLink(value: RouteurDestinations.bookmarks) {
Label("Bookmarks", systemImage: "bookmark")
Label(NSLocalizedString("userProfile.title.bookmarks", comment: "Bookmarks"), systemImage: "bookmark")
}
}, label: {
@ -179,7 +179,7 @@ struct UserProfileView: View {
}
}
} catch {
ErrorService.shared.handle(error, message: "Muting/unmuting action failed.", showToastr: true)
ErrorService.shared.handle(error, message: "userProfile.error.mute", showToastr: true)
}
}
@ -195,7 +195,7 @@ struct UserProfileView: View {
}
}
} catch {
ErrorService.shared.handle(error, message: "Block/unblock action failed.", showToastr: true)
ErrorService.shared.handle(error, message: "userProfile.error.block", showToastr: true)
}
}
}

View File

@ -49,7 +49,7 @@ struct ContentWarning<Content: View>: View {
Image(systemName: "eye.slash.fill")
.font(.title2)
.shadow(color: Color.systemBackground, radius: 0.3)
Text("Sensitive content")
Text("global.title.contentWarning", comment: "Sensitive content")
.font(.title2)
.shadow(color: Color.systemBackground, radius: 0.3)
if let spoilerText {
@ -63,7 +63,7 @@ struct ContentWarning<Content: View>: View {
self.showSensitive = true
}
} label: {
Text("See post")
Text("global.title.seePost", comment: "See post")
.shadow(color: Color.systemBackground, radius: 0.3)
}
.buttonStyle(.bordered)

View File

@ -30,7 +30,7 @@ struct ImageCarouselPicture: View {
self.onImageDownloaded(attachment, imageData)
}
} catch {
ErrorService.shared.handle(error, message: "Cannot download image for status")
ErrorService.shared.handle(error, message: "global.error.errorDuringImageDownload")
}
}
}

View File

@ -148,10 +148,10 @@ struct ImageRow: View {
}
} catch {
if !Task.isCancelled {
ErrorService.shared.handle(error, message: "Cannot download the image.")
ErrorService.shared.handle(error, message: "global.error.errorDuringImageDownload")
self.error = error
} else {
ErrorService.shared.handle(error, message: "Download image has been canceled.")
ErrorService.shared.handle(error, message: "global.error.canceledImageDownload")
self.cancelled = true
}
}

View File

@ -82,7 +82,7 @@ struct ImageRowAsync: View {
VStack(alignment: .center) {
Spacer()
Text("Cannot download image")
Text("global.error.errorDuringImageDownload", comment: "Cannot download image")
.foregroundColor(.systemBackground)
Spacer()
}

View File

@ -70,7 +70,7 @@ struct ImagesGrid: View {
let statusesWithImages = statusesFromApi.getStatusesWithImagesOnly()
self.updatePhotos(statusesWithImages: statusesWithImages)
} catch {
ErrorService.shared.handle(error, message: "Loading tags failed.", showToastr: !Task.isCancelled)
ErrorService.shared.handle(error, message: "global.error.errorDuringDataLoad", showToastr: !Task.isCancelled)
}
}

View File

@ -81,31 +81,31 @@ struct InteractionRow: View {
Menu {
NavigationLink(value: RouteurDestinations.accounts(listType: .reblogged(entityId: statusModel.id))) {
Label("Reboosted by", systemImage: "paperplane")
Label("status.title.reboostedBy", systemImage: "paperplane")
}
NavigationLink(value: RouteurDestinations.accounts(listType: .favourited(entityId: statusModel.id))) {
Label("Favourited by", systemImage: "hand.thumbsup")
Label("status.title.favouritedBy", systemImage: "hand.thumbsup")
}
if let url = statusModel.url {
Divider()
Link(destination: url) {
Label("Open in browser", systemImage: "safari")
Label("status.title.openInBrowser", systemImage: "safari")
}
ShareLink(item: url) {
Label("Share post", systemImage: "square.and.arrow.up")
Label("status.title.shareStatus", systemImage: "square.and.arrow.up")
}
}
if self.statusModel.account.id == self.applicationState.account?.id {
Section(header: Text("Your post")) {
Section(header: Text("status.title.yourStatus", comment: "Your post")) {
Button(role: .destructive) {
self.deleteStatus()
} label: {
Label("Delete", systemImage: "trash")
Label("status.title.delete", systemImage: "trash")
}
}
}
@ -143,9 +143,11 @@ struct InteractionRow: View {
self.reblogged = status.reblogged
}
ToastrService.shared.showSuccess(self.reblogged ? "Reboosted" : "Unreboosted", imageSystemName: "paperplane.fill")
ToastrService.shared.showSuccess(self.reblogged
? NSLocalizedString("status.title.reboosted", comment: "Reboosted")
: NSLocalizedString("status.title.unreboosted", comment: "Unreboosted"), imageSystemName: "paperplane.fill")
} catch {
ErrorService.shared.handle(error, message: "Reboost action failed.", showToastr: true)
ErrorService.shared.handle(error, message: "status.error.reboostFailed", showToastr: true)
}
}
@ -163,9 +165,11 @@ struct InteractionRow: View {
self.favourited = status.favourited
}
ToastrService.shared.showSuccess(self.favourited ? "Favourited" : "Unfavourited", imageSystemName: "hand.thumbsup.fill")
ToastrService.shared.showSuccess(self.favourited
? NSLocalizedString("status.title.favourited", comment: "Favourited")
: NSLocalizedString("status.title.unfavourited", comment: "Unfavourited"), imageSystemName: "hand.thumbsup.fill")
} catch {
ErrorService.shared.handle(error, message: "Favourite action failed.", showToastr: true)
ErrorService.shared.handle(error, message: "status.error.favouriteFailed", showToastr: true)
}
}
@ -176,9 +180,11 @@ struct InteractionRow: View {
: try await self.client.statuses?.bookmark(statusId: self.statusModel.id)
self.bookmarked.toggle()
ToastrService.shared.showSuccess(self.bookmarked ? "Bookmarked" : "Unbookmarked", imageSystemName: "bookmark.fill")
ToastrService.shared.showSuccess(self.bookmarked
? NSLocalizedString("status.title.bookmarked", comment: "Bookmarked")
: NSLocalizedString("status.title.unbookmarked", comment: "Unbookmarked"), imageSystemName: "bookmark.fill")
} catch {
ErrorService.shared.handle(error, message: "Bookmark action failed.", showToastr: true)
ErrorService.shared.handle(error, message: "status.error.bookmarkFailed", showToastr: true)
}
}
@ -186,11 +192,11 @@ struct InteractionRow: View {
Task {
do {
try await self.client.statuses?.delete(statusId: self.statusModel.id)
ToastrService.shared.showSuccess("Post deleted", imageSystemName: "checkmark.circle.fill")
ToastrService.shared.showSuccess("status.title.statusDeleted", imageSystemName: "checkmark.circle.fill")
self.delete?()
} catch {
ErrorService.shared.handle(error, message: "Delete action failed.", showToastr: true)
ErrorService.shared.handle(error, message: "status.error.deleteFailed", showToastr: true)
}
}
}

View File

@ -9,9 +9,9 @@ import SwiftUI
struct NoDataView: View {
private let imageSystemName: String
private let text: String
private let text: LocalizedStringKey
init(imageSystemName: String, text: String) {
init(imageSystemName: String, text: LocalizedStringKey) {
self.imageSystemName = imageSystemName
self.text = text
}
@ -21,7 +21,7 @@ struct NoDataView: View {
Image(systemName: self.imageSystemName)
.font(.largeTitle)
.padding(.bottom, 4)
Text(self.text)
Text(self.text, comment: "No data message")
.font(.title3)
}
.foregroundColor(.lightGrayColor)

View File

@ -185,7 +185,7 @@ extension TextView.Representable.Coordinator {
public extension TextView {
/// Specify a placeholder text
/// - Parameter placeholder: The placeholder text
func placeholder(_ placeholder: String) -> TextView {
func placeholder(_ placeholder: LocalizedStringKey) -> TextView {
self.placeholder(placeholder) { $0 }
}
@ -197,7 +197,7 @@ public extension TextView {
/// .placeholder("placeholder") { view in
/// view.foregroundColor(.red)
/// }
func placeholder<V: View>(_ placeholder: String, _ configure: (Text) -> V) -> TextView {
func placeholder<V: View>(_ placeholder: LocalizedStringKey, _ configure: (Text) -> V) -> TextView {
var view = self
let text = Text(placeholder)
view.placeholderView = AnyView(configure(text))

View File

@ -14,7 +14,7 @@ public class ImageFetcher {
private let maxImageSize = 1000.0
func fetchWidgetEntries(length: Int = 6) async throws -> [WidgetEntry] {
func fetchWidgetEntries(length: Int = 8) async throws -> [WidgetEntry] {
let defaultSettings = ApplicationSettingsHandler.shared.getDefaultSettings()
guard let accountId = defaultSettings.currentAccount else {
return [self.placeholder()]
@ -29,7 +29,7 @@ public class ImageFetcher {
}
let client = PixelfedClient(baseURL: account.serverUrl).getAuthenticated(token: accessToken)
let statuses = try await client.getHomeTimeline(limit: 10)
let statuses = try await client.getHomeTimeline(limit: 20)
var widgetEntries: [WidgetEntry] = []
for status in statuses {
@ -54,7 +54,7 @@ public class ImageFetcher {
continue
}
let displayDate = Calendar.current.date(byAdding: .minute, value: widgetEntries.count * 10, to: Date())
let displayDate = Calendar.current.date(byAdding: .minute, value: widgetEntries.count * 15, to: Date())
widgetEntries.append(WidgetEntry(date: displayDate ?? Date(),
image: uiImage,

View File

@ -31,13 +31,13 @@ struct Provider: TimelineProvider {
let currentDate = Date()
let widgetEntries = await self.getWidgetEntries()
let nextUpdateDate = Calendar.current.date(byAdding: .hour, value: 1, to: currentDate)!
let nextUpdateDate = Calendar.current.date(byAdding: .hour, value: 2, to: currentDate)!
let timeline = Timeline(entries: widgetEntries, policy: .after(nextUpdateDate))
completion(timeline)
}
}
func getWidgetEntries(length: Int = 6) async -> [WidgetEntry] {
func getWidgetEntries(length: Int = 8) async -> [WidgetEntry] {
do {
return try await ImageFetcher.shared.fetchWidgetEntries(length: length)
} catch {

View File

@ -15,7 +15,7 @@ struct VernissageWidget: Widget {
VernissageWidgetEntryView(entry: entry)
}
.configurationDisplayName("Vernissage")
.description("Widget with photos from Pixelfed.")
.description("widget.title.description")
.supportedFamilies([.systemSmall, .systemMedium, .systemLarge])
}
}