From 95af9f59bd25635b9ebe25f14d965c71e6b8e0de Mon Sep 17 00:00:00 2001 From: Marcin Czachursk Date: Mon, 13 Mar 2023 13:53:36 +0100 Subject: [PATCH] Add support of localisations. --- Localization/en.lproj/Localizable.strings | 231 ++++++++++++++++++ Localization/pl.lproj/Localizable.strings | 231 ++++++++++++++++++ Vernissage.xcodeproj/project.pbxproj | 30 +++ Vernissage/Services/ErrorsService.swift | 3 +- Vernissage/Services/HomeTimelineService.swift | 7 +- Vernissage/Services/ToastrService.swift | 6 +- Vernissage/Views/AccountsPhotoView.swift | 8 +- Vernissage/Views/AccountsView.swift | 14 +- .../Views/ComposeView/ComposeView.swift | 36 +-- .../Subviews/ImageUploadView.swift | 8 +- Vernissage/Views/HashtagsView.swift | 11 +- Vernissage/Views/HomeFeedView.swift | 12 +- Vernissage/Views/MainView.swift | 30 +-- .../NotificationsView/NotificationsView.swift | 12 +- .../Subviews/NotificationRowView.swift | 24 +- Vernissage/Views/PaginableStatusesView.swift | 12 +- Vernissage/Views/PhotoEditorView.swift | 14 +- Vernissage/Views/PlaceSelectorView.swift | 10 +- Vernissage/Views/SearchView.swift | 16 +- .../Views/SettingsView/SettingsView.swift | 6 +- .../Subviews/AccentsSectionView.swift | 2 +- .../Subviews/AccountsSectionView.swift | 4 +- .../Subviews/AvatarShapesSectionView.swift | 6 +- .../Subviews/HapticsSectionView.swift | 10 +- .../Subviews/MediaSettingsView.swift | 10 +- .../Subviews/OtherSectionView.swift | 10 +- .../SettingsView/Subviews/SupportView.swift | 2 +- .../SettingsView/Subviews/ThanksView.swift | 6 +- .../Subviews/ThemeSectionView.swift | 8 +- Vernissage/Views/SignInView/SignInView.swift | 14 +- .../SignInView/Subviews/InstanceRowView.swift | 6 +- Vernissage/Views/StatusView/StatusView.swift | 12 +- .../Subviews/CommentsSectionView.swift | 2 +- Vernissage/Views/StatusesView.swift | 30 +-- Vernissage/Views/ThirdPartyView.swift | 2 +- Vernissage/Views/TrendStatusesView.swift | 22 +- .../Subviews/UserProfileHeaderView.swift | 12 +- .../Subviews/UserProfileStatusesView.swift | 4 +- .../UserProfileView/UserProfileView.swift | 28 +-- Vernissage/Widgets/ContentWarning.swift | 4 +- Vernissage/Widgets/ImageCarouselPicture.swift | 2 +- Vernissage/Widgets/ImageRow.swift | 4 +- Vernissage/Widgets/ImageRowAsync.swift | 2 +- Vernissage/Widgets/ImagesGrid.swift | 2 +- Vernissage/Widgets/InteractionRow.swift | 34 +-- Vernissage/Widgets/NoDataView.swift | 6 +- Vernissage/Widgets/TextView/TextView.swift | 4 +- VernissageWidget/ImageFetcher.swift | 6 +- VernissageWidget/Provider.swift | 4 +- VernissageWidget/VernissageWidget.swift | 2 +- 50 files changed, 744 insertions(+), 237 deletions(-) create mode 100644 Localization/en.lproj/Localizable.strings create mode 100644 Localization/pl.lproj/Localizable.strings diff --git a/Localization/en.lproj/Localizable.strings b/Localization/en.lproj/Localizable.strings new file mode 100644 index 0000000..71a5c8b --- /dev/null +++ b/Localization/en.lproj/Localizable.strings @@ -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 you鈥檙e 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."; diff --git a/Localization/pl.lproj/Localizable.strings b/Localization/pl.lproj/Localizable.strings new file mode 100644 index 0000000..10ea920 --- /dev/null +++ b/Localization/pl.lproj/Localizable.strings @@ -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."; diff --git a/Vernissage.xcodeproj/project.pbxproj b/Vernissage.xcodeproj/project.pbxproj index 98ca949..e65b839 100644 --- a/Vernissage.xcodeproj/project.pbxproj +++ b/Vernissage.xcodeproj/project.pbxproj @@ -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 = ""; }; F829193B2983012400367CE2 /* ImageSizeService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageSizeService.swift; sourceTree = ""; }; F8341F8F295C636C009C8EE6 /* Data+Exif.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Data+Exif.swift"; sourceTree = ""; }; + F835082529BEF9C400DE3247 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; + F835082729BEFA1E00DE3247 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Localizable.strings; sourceTree = ""; }; F837269429A221420098D3C4 /* PixelfedKit */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = PixelfedKit; sourceTree = ""; }; F83901A5295D8EC000456AE2 /* LabelIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelIcon.swift; sourceTree = ""; }; F83CBEFA298298A1002972C8 /* ImageCarouselPicture.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageCarouselPicture.swift; sourceTree = ""; }; @@ -576,6 +580,14 @@ path = CoreData; sourceTree = ""; }; + F835081F29BEF88600DE3247 /* Localization */ = { + isa = PBXGroup; + children = ( + F835082629BEF9C400DE3247 /* Localizable.strings */, + ); + path = Localization; + sourceTree = ""; + }; 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 = ""; + }; +/* 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"; diff --git a/Vernissage/Services/ErrorsService.swift b/Vernissage/Services/ErrorsService.swift index e1b1c88..43e8d6f 100644 --- a/Vernissage/Services/ErrorsService.swift +++ b/Vernissage/Services/ErrorsService.swift @@ -21,6 +21,7 @@ public class ErrorService { } } - print("Error ['\(message)']: \(error.localizedDescription)") + let localizedMessage = NSLocalizedString(message, comment: "Error message") + print("Error ['\(localizedMessage)']: \(error.localizedDescription)") } } diff --git a/Vernissage/Services/HomeTimelineService.swift b/Vernissage/Services/HomeTimelineService.swift index 56a473a..3bd4502 100644 --- a/Vernissage/Services/HomeTimelineService.swift +++ b/Vernissage/Services/HomeTimelineService.swift @@ -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 } } diff --git a/Vernissage/Services/ToastrService.swift b/Vernissage/Services/ToastrService.swift index e17ab81..2738601 100644 --- a/Vernissage/Services/ToastrService.swift +++ b/Vernissage/Services/ToastrService.swift @@ -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)), diff --git a/Vernissage/Views/AccountsPhotoView.swift b/Vernissage/Views/AccountsPhotoView.swift index a3e26b9..3d8dacc 100644 --- a/Vernissage/Views/AccountsPhotoView.swift +++ b/Vernissage/Views/AccountsPhotoView.swift @@ -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) } } } diff --git a/Vernissage/Views/AccountsView.swift b/Vernissage/Views/AccountsView.swift index 53a0cfe..d20c9fb 100644 --- a/Vernissage/Views/AccountsView.swift +++ b/Vernissage/Views/AccountsView.swift @@ -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 } diff --git a/Vernissage/Views/ComposeView/ComposeView.swift b/Vernissage/Views/ComposeView/ComposeView.swift index 46c8d8a..b58676f 100644 --- a/Vernissage/Views/ComposeView/ComposeView.swift +++ b/Vernissage/Views/ComposeView/ComposeView.swift @@ -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) } } diff --git a/Vernissage/Views/ComposeView/Subviews/ImageUploadView.swift b/Vernissage/Views/ComposeView/Subviews/ImageUploadView.swift index 41ef5ca..b6dae34 100644 --- a/Vernissage/Views/ComposeView/Subviews/ImageUploadView.swift +++ b/Vernissage/Views/ComposeView/Subviews/ImageUploadView.swift @@ -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() diff --git a/Vernissage/Views/HashtagsView.swift b/Vernissage/Views/HashtagsView.swift index b776d68..eff65cb 100644 --- a/Vernissage/Views/HashtagsView.swift +++ b/Vernissage/Views/HashtagsView.swift @@ -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) } } } diff --git a/Vernissage/Views/HomeFeedView.swift b/Vernissage/Views/HomeFeedView.swift index 14d32b7..69a6862 100644 --- a/Vernissage/Views/HomeFeedView.swift +++ b/Vernissage/Views/HomeFeedView.swift @@ -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)) diff --git a/Vernissage/Views/MainView.swift b/Vernissage/Views/MainView.swift index b6f1475..a863a4e 100644 --- a/Vernissage/Views/MainView.swift +++ b/Vernissage/Views/MainView.swift @@ -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 } diff --git a/Vernissage/Views/NotificationsView/NotificationsView.swift b/Vernissage/Views/NotificationsView/NotificationsView.swift index d932066..2bc1032 100644 --- a/Vernissage/Views/NotificationsView/NotificationsView.swift +++ b/Vernissage/Views/NotificationsView/NotificationsView.swift @@ -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) } } } diff --git a/Vernissage/Views/NotificationsView/Subviews/NotificationRowView.swift b/Vernissage/Views/NotificationsView/Subviews/NotificationRowView.swift index b235c66..051e960 100644 --- a/Vernissage/Views/NotificationsView/Subviews/NotificationRowView.swift +++ b/Vernissage/Views/NotificationsView/Subviews/NotificationRowView.swift @@ -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" } } diff --git a/Vernissage/Views/PaginableStatusesView.swift b/Vernissage/Views/PaginableStatusesView.swift index f1fe5db..f673cdc 100644 --- a/Vernissage/Views/PaginableStatusesView.swift +++ b/Vernissage/Views/PaginableStatusesView.swift @@ -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" } } } diff --git a/Vernissage/Views/PhotoEditorView.swift b/Vernissage/Views/PhotoEditorView.swift index c00844e..0cc620f 100644 --- a/Vernissage/Views/PhotoEditorView.swift +++ b/Vernissage/Views/PhotoEditorView.swift @@ -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) } } } diff --git a/Vernissage/Views/PlaceSelectorView.swift b/Vernissage/Views/PlaceSelectorView.swift index 2f69ad0..acb5048 100644 --- a/Vernissage/Views/PlaceSelectorView.swift +++ b/Vernissage/Views/PlaceSelectorView.swift @@ -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 diff --git a/Vernissage/Views/SearchView.swift b/Vernissage/Views/SearchView.swift index 3b1ff9e..be9efa3 100644 --- a/Vernissage/Views/SearchView.swift +++ b/Vernissage/Views/SearchView.swift @@ -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") } } diff --git a/Vernissage/Views/SettingsView/SettingsView.swift b/Vernissage/Views/SettingsView/SettingsView.swift index 6019b2b..8ff832f 100644 --- a/Vernissage/Views/SettingsView/SettingsView.swift +++ b/Vernissage/Views/SettingsView/SettingsView.swift @@ -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) diff --git a/Vernissage/Views/SettingsView/Subviews/AccentsSectionView.swift b/Vernissage/Views/SettingsView/Subviews/AccentsSectionView.swift index e72126d..a730106 100644 --- a/Vernissage/Views/SettingsView/Subviews/AccentsSectionView.swift +++ b/Vernissage/Views/SettingsView/Subviews/AccentsSectionView.swift @@ -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 diff --git a/Vernissage/Views/SettingsView/Subviews/AccountsSectionView.swift b/Vernissage/Views/SettingsView/Subviews/AccountsSectionView.swift index baa8f4f..f97b5c3 100644 --- a/Vernissage/Views/SettingsView/Subviews/AccountsSectionView.swift +++ b/Vernissage/Views/SettingsView/Subviews/AccountsSectionView.swift @@ -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") } diff --git a/Vernissage/Views/SettingsView/Subviews/AvatarShapesSectionView.swift b/Vernissage/Views/SettingsView/Subviews/AvatarShapesSectionView.swift index 31e3079..1ff1b79 100644 --- a/Vernissage/Views/SettingsView/Subviews/AvatarShapesSectionView.swift +++ b/Vernissage/Views/SettingsView/Subviews/AvatarShapesSectionView.swift @@ -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() diff --git a/Vernissage/Views/SettingsView/Subviews/HapticsSectionView.swift b/Vernissage/Views/SettingsView/Subviews/HapticsSectionView.swift index a7770a3..cd057b3 100644 --- a/Vernissage/Views/SettingsView/Subviews/HapticsSectionView.swift +++ b/Vernissage/Views/SettingsView/Subviews/HapticsSectionView.swift @@ -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) diff --git a/Vernissage/Views/SettingsView/Subviews/MediaSettingsView.swift b/Vernissage/Views/SettingsView/Subviews/MediaSettingsView.swift index ba1fb35..bb1042b 100644 --- a/Vernissage/Views/SettingsView/Subviews/MediaSettingsView.swift +++ b/Vernissage/Views/SettingsView/Subviews/MediaSettingsView.swift @@ -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) } diff --git a/Vernissage/Views/SettingsView/Subviews/OtherSectionView.swift b/Vernissage/Views/SettingsView/Subviews/OtherSectionView.swift index 77fedc7..49e0897 100644 --- a/Vernissage/Views/SettingsView/Subviews/OtherSectionView.swift +++ b/Vernissage/Views/SettingsView/Subviews/OtherSectionView.swift @@ -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) diff --git a/Vernissage/Views/SettingsView/Subviews/SupportView.swift b/Vernissage/Views/SettingsView/Subviews/SupportView.swift index ab07007..a36c04b 100644 --- a/Vernissage/Views/SettingsView/Subviews/SupportView.swift +++ b/Vernissage/Views/SettingsView/Subviews/SupportView.swift @@ -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)) diff --git a/Vernissage/Views/SettingsView/Subviews/ThanksView.swift b/Vernissage/Views/SettingsView/Subviews/ThanksView.swift index 0eb780a..99df1b1 100644 --- a/Vernissage/Views/SettingsView/Subviews/ThanksView.swift +++ b/Vernissage/Views/SettingsView/Subviews/ThanksView.swift @@ -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 you鈥檙e 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()) { diff --git a/Vernissage/Views/SettingsView/Subviews/ThemeSectionView.swift b/Vernissage/Views/SettingsView/Subviews/ThemeSectionView.swift index 9601d8e..04983a3 100644 --- a/Vernissage/Views/SettingsView/Subviews/ThemeSectionView.swift +++ b/Vernissage/Views/SettingsView/Subviews/ThemeSectionView.swift @@ -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 { diff --git a/Vernissage/Views/SignInView/SignInView.swift b/Vernissage/Views/SignInView/SignInView.swift index d49882f..74e4a11 100644 --- a/Vernissage/Views/SignInView/SignInView.swift +++ b/Vernissage/Views/SignInView/SignInView.swift @@ -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) } } } diff --git a/Vernissage/Views/SignInView/Subviews/InstanceRowView.swift b/Vernissage/Views/SignInView/Subviews/InstanceRowView.swift index da1b9fa..1f97442 100644 --- a/Vernissage/Views/SignInView/Subviews/InstanceRowView.swift +++ b/Vernissage/Views/SignInView/Subviews/InstanceRowView.swift @@ -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() } diff --git a/Vernissage/Views/StatusView/StatusView.swift b/Vernissage/Views/StatusView/StatusView.swift index e309267..a2e92c6 100644 --- a/Vernissage/Views/StatusView/StatusView.swift +++ b/Vernissage/Views/StatusView/StatusView.swift @@ -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) } } } diff --git a/Vernissage/Views/StatusView/Subviews/CommentsSectionView.swift b/Vernissage/Views/StatusView/Subviews/CommentsSectionView.swift index 47c0e91..1f5d29d 100644 --- a/Vernissage/Views/StatusView/Subviews/CommentsSectionView.swift +++ b/Vernissage/Views/StatusView/Subviews/CommentsSectionView.swift @@ -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) } } } diff --git a/Vernissage/Views/StatusesView.swift b/Vernissage/Views/StatusesView.swift index 7c8f011..26b9d16 100644 --- a/Vernissage/Views/StatusesView.swift +++ b/Vernissage/Views/StatusesView.swift @@ -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) } } } diff --git a/Vernissage/Views/ThirdPartyView.swift b/Vernissage/Views/ThirdPartyView.swift index 61237d5..9cbe98f 100644 --- a/Vernissage/Views/ThirdPartyView.swift +++ b/Vernissage/Views/ThirdPartyView.swift @@ -69,7 +69,7 @@ struct ThirdPartyView: View { .font(.footnote) } } - .navigationTitle("Third party") + .navigationTitle("thirdParty.navigationBar.title") .navigationBarTitleDisplayMode(.inline) } } diff --git a/Vernissage/Views/TrendStatusesView.swift b/Vernissage/Views/TrendStatusesView.swift index bd79444..415fc51 100644 --- a/Vernissage/Views/TrendStatusesView.swift +++ b/Vernissage/Views/TrendStatusesView.swift @@ -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) } } } diff --git a/Vernissage/Views/UserProfileView/Subviews/UserProfileHeaderView.swift b/Vernissage/Views/UserProfileView/Subviews/UserProfileHeaderView.swift index 5fb6129..61eed5b 100644 --- a/Vernissage/Views/UserProfileView/Subviews/UserProfileHeaderView.swift +++ b/Vernissage/Views/UserProfileView/Subviews/UserProfileHeaderView.swift @@ -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) } } } diff --git a/Vernissage/Views/UserProfileView/Subviews/UserProfileStatusesView.swift b/Vernissage/Views/UserProfileView/Subviews/UserProfileStatusesView.swift index 18c758a..508a901 100644 --- a/Vernissage/Views/UserProfileView/Subviews/UserProfileStatusesView.swift +++ b/Vernissage/Views/UserProfileView/Subviews/UserProfileStatusesView.swift @@ -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) } } } diff --git a/Vernissage/Views/UserProfileView/UserProfileView.swift b/Vernissage/Views/UserProfileView/UserProfileView.swift index 043cc03..9028e88 100644 --- a/Vernissage/Views/UserProfileView/UserProfileView.swift +++ b/Vernissage/Views/UserProfileView/UserProfileView.swift @@ -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) } } } diff --git a/Vernissage/Widgets/ContentWarning.swift b/Vernissage/Widgets/ContentWarning.swift index 2fd1575..e1cb597 100644 --- a/Vernissage/Widgets/ContentWarning.swift +++ b/Vernissage/Widgets/ContentWarning.swift @@ -49,7 +49,7 @@ struct ContentWarning: 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: View { self.showSensitive = true } } label: { - Text("See post") + Text("global.title.seePost", comment: "See post") .shadow(color: Color.systemBackground, radius: 0.3) } .buttonStyle(.bordered) diff --git a/Vernissage/Widgets/ImageCarouselPicture.swift b/Vernissage/Widgets/ImageCarouselPicture.swift index 317f7f6..b27f730 100644 --- a/Vernissage/Widgets/ImageCarouselPicture.swift +++ b/Vernissage/Widgets/ImageCarouselPicture.swift @@ -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") } } } diff --git a/Vernissage/Widgets/ImageRow.swift b/Vernissage/Widgets/ImageRow.swift index d8e2834..439edea 100644 --- a/Vernissage/Widgets/ImageRow.swift +++ b/Vernissage/Widgets/ImageRow.swift @@ -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 } } diff --git a/Vernissage/Widgets/ImageRowAsync.swift b/Vernissage/Widgets/ImageRowAsync.swift index bdd5f45..308a728 100644 --- a/Vernissage/Widgets/ImageRowAsync.swift +++ b/Vernissage/Widgets/ImageRowAsync.swift @@ -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() } diff --git a/Vernissage/Widgets/ImagesGrid.swift b/Vernissage/Widgets/ImagesGrid.swift index 92126f8..38406a6 100644 --- a/Vernissage/Widgets/ImagesGrid.swift +++ b/Vernissage/Widgets/ImagesGrid.swift @@ -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) } } diff --git a/Vernissage/Widgets/InteractionRow.swift b/Vernissage/Widgets/InteractionRow.swift index c493658..88551cf 100644 --- a/Vernissage/Widgets/InteractionRow.swift +++ b/Vernissage/Widgets/InteractionRow.swift @@ -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) } } } diff --git a/Vernissage/Widgets/NoDataView.swift b/Vernissage/Widgets/NoDataView.swift index d22cbb3..16a4879 100644 --- a/Vernissage/Widgets/NoDataView.swift +++ b/Vernissage/Widgets/NoDataView.swift @@ -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) diff --git a/Vernissage/Widgets/TextView/TextView.swift b/Vernissage/Widgets/TextView/TextView.swift index db31bdd..f3ea3da 100644 --- a/Vernissage/Widgets/TextView/TextView.swift +++ b/Vernissage/Widgets/TextView/TextView.swift @@ -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(_ placeholder: String, _ configure: (Text) -> V) -> TextView { + func placeholder(_ placeholder: LocalizedStringKey, _ configure: (Text) -> V) -> TextView { var view = self let text = Text(placeholder) view.placeholderView = AnyView(configure(text)) diff --git a/VernissageWidget/ImageFetcher.swift b/VernissageWidget/ImageFetcher.swift index 7ef1487..5e58eb5 100644 --- a/VernissageWidget/ImageFetcher.swift +++ b/VernissageWidget/ImageFetcher.swift @@ -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, diff --git a/VernissageWidget/Provider.swift b/VernissageWidget/Provider.swift index 00b7629..274f93f 100644 --- a/VernissageWidget/Provider.swift +++ b/VernissageWidget/Provider.swift @@ -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 { diff --git a/VernissageWidget/VernissageWidget.swift b/VernissageWidget/VernissageWidget.swift index 283fce6..c227da2 100644 --- a/VernissageWidget/VernissageWidget.swift +++ b/VernissageWidget/VernissageWidget.swift @@ -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]) } }