diff --git a/.github/ISSUE_TEMPLATE/feature.yml b/.github/ISSUE_TEMPLATE/feature.yml
new file mode 100644
index 000000000..b849515de
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature.yml
@@ -0,0 +1,48 @@
+---
+name: 🛠️ Feature Request
+description: Suggest an idea to help us improve
+title: "[Feature]: "
+labels:
+ - "feature_request"
+
+body:
+ - type: markdown
+ attributes:
+ value: |
+ **Thanks :heart: for taking the time to fill out this feature request report!**
+ We kindly ask that you search to see if an issue [already exists](https://github.com/mastodon/mastodon-ios/issues?q=is%3Aissue+sort%3Acreated-desc+) for your feature.
+
+ We are also happy to accept contributions from our users. For more details see [here](https://github.com/mastodon/mastodon-ios/blob/develop/Documentation/CONTRIBUTING.md).
+
+ - type: textarea
+ attributes:
+ label: Description
+ description: |
+ A clear and concise description of the feature you're interested in.
+ validations:
+ required: true
+
+ - type: textarea
+ attributes:
+ label: Suggested Solution
+ description: |
+ Describe the solution you'd like. A clear and concise description of what you want to happen.
+ validations:
+ required: true
+
+ - type: textarea
+ attributes:
+ label: Alternatives
+ description: |
+ Describe alternatives you've considered.
+ A clear and concise description of any alternative solutions or features you've considered.
+ validations:
+ required: false
+
+ - type: textarea
+ attributes:
+ label: Additional Context
+ description: |
+ Add any other context about the problem here.
+ validations:
+ required: false
diff --git a/.github/scripts/build-release.sh b/.github/scripts/build-release.sh
index e3efc59df..069aa673e 100755
--- a/.github/scripts/build-release.sh
+++ b/.github/scripts/build-release.sh
@@ -36,6 +36,8 @@ BUILD_NUMBER=$(app-store-connect get-latest-testflight-build-number $ENV_APP_ID
BUILD_NUMBER=$((BUILD_NUMBER+1))
CURRENT_PROJECT_VERSION=${BUILD_NUMBER:-0}
+echo "GITHUB_TAG_NAME=build-$CURRENT_PROJECT_VERSION" >> $GITHUB_ENV
+
agvtool new-version -all $CURRENT_PROJECT_VERSION
xcrun xcodebuild clean \
diff --git a/.github/workflows/develop-build.yml b/.github/workflows/develop-build.yml
index c37f00731..7dd9ebaab 100644
--- a/.github/workflows/develop-build.yml
+++ b/.github/workflows/develop-build.yml
@@ -4,6 +4,7 @@ on:
push:
branches:
- develop
+ - release*
- ci-test
jobs:
@@ -11,18 +12,19 @@ jobs:
name: Build
runs-on: macOS-12
steps:
- - name: checkout
+ - name: Checkout
uses: actions/checkout@v2
- - name: setup
+ - name: Setup
env:
NotificationEndpointDebug: ${{ secrets.NotificationEndpointDebug }}
NotificationEndpointRelease: ${{ secrets.NotificationEndpointRelease }}
run: exec ./.github/scripts/setup.sh
- - uses: actions/setup-python@v4
+ - name: Install codemagic-cli-tools
+ uses: actions/setup-python@v4
with:
- python-version: '3.11'
+ python-version: '3.10'
- run: |
pip3 install codemagic-cli-tools
- run: |
@@ -43,7 +45,7 @@ jobs:
api-key-id: ${{ secrets.APPSTORE_KEY_ID }}
api-private-key: ${{ secrets.APPSTORE_PRIVATE_KEY }}
- - name: build
+ - name: Build
env:
ENV_APP_ID: ${{ secrets.APP_ID }}
ENV_ISSUER_ID: ${{ secrets.APPSTORE_ISSUER_ID }}
@@ -60,6 +62,12 @@ jobs:
api-key-id: ${{ secrets.APPSTORE_KEY_ID }}
api-private-key: ${{ secrets.APPSTORE_PRIVATE_KEY }}
+ - name: Tag commit
+ uses: tvdias/github-tagger@v0.0.1
+ with:
+ repo-token: "${{ secrets.GITHUB_TOKEN }}"
+ tag: "${{ env.GITHUB_TAG_NAME }}"
+
- name: Clean up keychain and provisioning profile
if: ${{ always() }}
run: |
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 827276208..1c40b6556 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -6,6 +6,9 @@ on:
- master
- develop
- feature/*
+ - feature-*
+ - issue/*
+ - issue-*
pull_request:
branches:
- develop
diff --git a/.gitignore b/.gitignore
index 2d787576b..017cfd6fa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -125,4 +125,9 @@ Localization/StringsConvertor/output
.DS_Store
env/**/**
-!env/.env
\ No newline at end of file
+!env/.env
+
+
+## Ruby ###
+vendor/
+.bundle/
diff --git a/Documentation/How-it-works.md b/Documentation/How-it-works.md
new file mode 100644
index 000000000..6b48159e5
--- /dev/null
+++ b/Documentation/How-it-works.md
@@ -0,0 +1,96 @@
+# How it works
+The app is currently build for iOS and iPadOS. We use the MVVM architecture to construct the whole app. Some design detail may not be the best practice. And any suggestions for improvements are welcome.
+
+## Data
+A typical status timeline fetches results from the database using a predicate that fetch the active account's entities. Then data source dequeues an item then configure the view. Likes many other MVVM applications. The app binds the Core Data entity to view via Combine publisher. Because the RunLoop dispatch drawing on the next loop. So we could return quickly.
+
+## Layout
+A timeline has many posts and each post has many components. For example avatar, name, username, timestamp, content, media, toolbar and e.t.c. The app uses `AutoLayout` with `UIStackView` to place it and control whether it should hide or not.
+
+## Performance
+Although it's easily loading timeline with hundreds of thousands of entities due to the Core Data fault mechanism. Some old devices may have slow performance when I/O bottleneck. There are three potential profile chances for entities:
+- preload fulfill
+- layout in background
+- limit the data fetching
+
+## SwiftUI
+Some view models already migrate to `@Published` annotated output. It's future-proof support for SwiftUI. There are some views already transformed to `SwiftUI` likes `MastodonRegisterView` and `ReportReasonView`.
+
+# Take it apart
+## Targets
+The app builds with those targets:
+
+- Mastodon: the app itself
+- NotificationService: E2E push notification service
+- ShareActionExtension: iOS share action
+- MastodonIntent: Siri shortcuts
+
+## MastodonSDK
+There is a self-hosted Swift Package that contains the common libraries to build this app.
+
+- CoreDataStack: Core Data model definition and util methods
+- MastodonAsset: image and font assets
+- MastodonCommon: store App Group ID
+- MastodonCore: the logic for the app
+- MastodonExtension: system API extension utility
+- MastodonLocalization: i18n resources
+- MastodonSDK: Mastodon API client
+- MastodonUI: App UI components
+
+#### CoreDataStack
+App uses Core Data as the backend to persist all entitles from the server. So the app has the capability to keep the timeline and notifications. Another reason for using a database is it makes the app could respond to entity changes between different sources. For example, a user could skim in the home timeline and then interact with the same post on other pages with favorite or reblog action. Core Data will handle the property modifications and notify the home timeline to update the view.
+
+To simplify the database operations. There is only one persistent store for all accounts. We use `domain` to identify entity for different servers (a.k.a instance). Do not mix the `domain` with the Mastodon remote server name. For example. The domain is `mastodon.online` whereever the post (e.g. post come from `mstdn.jp`) and friends from for the account sign in `mastodon.online`. Also, do not only rely on `id` because it has conflict potential between different `domain`. The unique predicate is `domain` + `id`.
+
+The app use "One stack, two context" setup. There is one main managed object context for UI displaying and another background managed context for entities creating and updating. We assert the background context performs in a queue. Also, the app could accept mulitple background context model. Then the flag `-com.apple.CoreData.ConcurrencyDebug 1` will be usful.
+
+###### How to create a new Entity
+First, select the `CoreData.xcdatamodeld` file and in menu `Editor > Add Model Version…` to create a new version. Make sure active the new version in the inspect panel. e.g. `Model Version. Current > "Core Data 5"`
+
+Then use the `Add Entity` button create new Entity.
+1. Give a name in data model inspect panel.
+2. Also, set the `Module` to `CoreDataStack`.
+3. Set the `Codegen` to `Manual/None`. We use `Sourery` generates the template code.
+4. Create the `Entity.swift` file and declear the properties and relationships.
+
+###### How to add or remove property for Entity
+We using the Core Data lightweight migration. Please check the rules detail [here](https://developer.apple.com/documentation/coredata/using_lightweight_migration). And keep in mind that we using two-way relationship. And a relationship could be one-to-one, one-to-many/many-to-one.
+
+Tip:
+
+Please check the `Soucery` and use that generates getter and setter for properties and relationships. It's could save you time. To take the benefit from the dynamic property. We can declare a raw value property and then use compute property to construct the struct we want (e.g. `Feed.acct`). Or control the primitive by hand and declare the mirror type for this value (e.g `Status.attachments`).
+
+###### How to persist the Entity
+Please check the `Persistence+Status.swift`. We follow the pattern: migrate the old one if exists. Otherwise, create a new one. (Maybe some improvements could be adopted?)
+
+#### MastodonAsset
+Sourcery powered assets package.
+
+#### MastodonCommon
+Shared code for preference and configuration.
+
+### MastodonCore
+The core logic to drive the app.
+
+#### MastodonExtension
+Utility extension codes for SDK.
+
+#### MastodonLocalization
+Sourcery powered i18n package.
+
+#### MastodonSDK
+Mastodon API wrapper with Combine style API.
+
+#### MastodonUI
+Mastodon app UI components.
+
+## NotificationService
+Mastodon server accepts push notification register and we use the [toot-relay](https://github.com/DagAgren/toot-relay) to pass the server notifications to APNs. The message is E2E encrypted. The app will create an on-device private key for notification and save it into the keychain.
+
+When the push notification is incoming. iOS will spawn our NotificationService extension to handle the message. At that time the message is decrypted and displayed as a banner or in-app silent notification event when the app is in the foreground. All the notification count and deep-link logic are handled by the main app.
+
+## ShareActionExtension
+The iOS Share Extension allows users to share links or media from other apps. The app uses the same implementation for the main app and the share extension. Then different is less available memoery for extension so maybe some memory bounded task could crash the app. (Plesae file the issue)
+
+## MastodonIntent
+iOS Siri shortcut supports. It allows iOS directly publish posts via Shortcut without app launching.
diff --git a/Gemfile.lock b/Gemfile.lock
index e0ed91c5b..15d02a8ec 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -100,7 +100,9 @@ GEM
PLATFORMS
arm64-darwin-21
+ arm64-darwin-22
x86_64-darwin-21
+ x86_64-darwin-22
DEPENDENCIES
arkana
diff --git a/Localization/Localizable.stringsdict b/Localization/Localizable.stringsdict
index cd97825f4..37ce1f032 100644
--- a/Localization/Localizable.stringsdict
+++ b/Localization/Localizable.stringsdict
@@ -13,15 +13,15 @@
NSStringFormatValueTypeKey
ld
zero
- no unread notification
+ no unread notifications
one
1 unread notification
few
%ld unread notifications
many
- %ld unread notification
+ %ld unread notifications
other
- %ld unread notification
+ %ld unread notifications
a11y.plural.count.input_limit_exceeds
@@ -71,7 +71,7 @@
a11y.plural.count.characters_left
NSStringLocalizedFormatKey
- %#@character_count@ left
+ %#@character_count@
character_count
NSStringFormatSpecTypeKey
@@ -79,15 +79,15 @@
NSStringFormatValueTypeKey
ld
zero
- no characters
+ no characters left
one
- 1 character
+ 1 character left
few
- %ld characters
+ %ld characters left
many
- %ld characters
+ %ld characters left
other
- %ld characters
+ %ld characters left
plural.count.followed_by_and_mutual
diff --git a/Localization/README.md b/Localization/README.md
index 93bf290bc..b44fa350d 100644
--- a/Localization/README.md
+++ b/Localization/README.md
@@ -27,7 +27,7 @@ If there are new translations, Crowdin pushes new commits to a branch called `l1
To update or add new translations, the workflow is as follows:
1. Merge the PR with `l10n_develop` into `develop`. It's usually called `New Crowdin Updates`
-2. Run `update.localization.sh` on your computer.
+2. Run `update_localization.sh` on your computer.
3. Commit the changes and push `develop`.
[crowdin-mastodon-ios]: https://crowdin.com/project/mastodon-for-ios
diff --git a/Localization/StringsConvertor/Intents/input/an.lproj/Intents.strings b/Localization/StringsConvertor/Intents/input/an.lproj/Intents.strings
new file mode 100644
index 000000000..597347680
--- /dev/null
+++ b/Localization/StringsConvertor/Intents/input/an.lproj/Intents.strings
@@ -0,0 +1,51 @@
+"16wxgf" = "Publicar en Mastodon";
+
+"751xkl" = "Conteniu de Texto";
+
+"CsR7G2" = "Publicar en Mastodon";
+
+"HZSGTr" = "Qué conteniu publicar?";
+
+"HdGikU" = "Publicación fallida";
+
+"KDNTJ4" = "Motivo d'o Fallo";
+
+"RHxKOw" = "Ninviar Publicación con conteniu de texto";
+
+"RxSqsb" = "Publicación";
+
+"WCIR3D" = "Publicar ${content} en Mastodon";
+
+"ZKJSNu" = "Publicación";
+
+"ZS1XaK" = "${content}";
+
+"ZbSjzC" = "Visibilidat";
+
+"Zo4jgJ" = "Visibilidat d'o Post";
+
+"apSxMG-dYQ5NN" = "I hai ${count} opcions que coinciden con «Publico».";
+
+"apSxMG-ehFLjY" = "I hai ${count} opcions que coinciden con «Solo seguidores».";
+
+"ayoYEb-dYQ5NN" = "${content}, Publico";
+
+"ayoYEb-ehFLjY" = "${content}, Nomás Seguidores";
+
+"dUyuGg" = "Publicar en Mastodon";
+
+"dYQ5NN" = "Publico";
+
+"ehFLjY" = "Solo Seguidores";
+
+"gfePDu" = "Publicación fallida. ${failureReason}";
+
+"k7dbKQ" = "Publicación ninviada con exito.";
+
+"oGiqmY-dYQ5NN" = "Nomás per confirmar, querebas «Publico»?";
+
+"oGiqmY-ehFLjY" = "Nomás per confirmar, querebas «Nomás seguidores»?";
+
+"rM6dvp" = "URL";
+
+"ryJLwG" = "Publicación ninviada con exito. ";
diff --git a/Localization/StringsConvertor/Intents/input/an.lproj/Intents.stringsdict b/Localization/StringsConvertor/Intents/input/an.lproj/Intents.stringsdict
new file mode 100644
index 000000000..9e972ae51
--- /dev/null
+++ b/Localization/StringsConvertor/Intents/input/an.lproj/Intents.stringsdict
@@ -0,0 +1,38 @@
+
+
+
+
+ There are ${count} options matching ‘${content}’. - 2
+
+ NSStringLocalizedFormatKey
+ I hai %#@count_option@ coincidencias con «${content}».
+ count_option
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ %ld
+ one
+ 1 opción
+ other
+ %ld opcions
+
+
+ There are ${count} options matching ‘${visibility}’.
+
+ NSStringLocalizedFormatKey
+ I hai %#@count_option@ coincidencias con «${visibility}».
+ count_option
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ %ld
+ one
+ 1 opción
+ other
+ %ld opcions
+
+
+
+
diff --git a/Localization/StringsConvertor/Intents/input/ca.lproj/Intents.strings b/Localization/StringsConvertor/Intents/input/ca.lproj/Intents.strings
index 6b92eb263..c02ac08cf 100644
--- a/Localization/StringsConvertor/Intents/input/ca.lproj/Intents.strings
+++ b/Localization/StringsConvertor/Intents/input/ca.lproj/Intents.strings
@@ -22,7 +22,7 @@
"ZbSjzC" = "Visibilitat";
-"Zo4jgJ" = "Visibilitat de la Publicació";
+"Zo4jgJ" = "Visibilitat de la publicació";
"apSxMG-dYQ5NN" = "Hi ha ${count} opcions que coincideixen amb ‘Públic’.";
@@ -30,9 +30,9 @@
"ayoYEb-dYQ5NN" = "${content}, Públic";
-"ayoYEb-ehFLjY" = "${content}, Només Seguidors";
+"ayoYEb-ehFLjY" = "${content}, Només seguidors";
-"dUyuGg" = "Publicació";
+"dUyuGg" = "Publica a Mastodon";
"dYQ5NN" = "Públic";
diff --git a/Localization/StringsConvertor/Intents/input/cs.lproj/Intents.stringsdict b/Localization/StringsConvertor/Intents/input/cs.lproj/Intents.stringsdict
index deea8db12..29249b2cc 100644
--- a/Localization/StringsConvertor/Intents/input/cs.lproj/Intents.stringsdict
+++ b/Localization/StringsConvertor/Intents/input/cs.lproj/Intents.stringsdict
@@ -25,7 +25,7 @@
There are ${count} options matching ‘${visibility}’.
NSStringLocalizedFormatKey
- There are %#@count_option@ matching ‘${visibility}’.
+ Existuje %#@count_option@ odpovídající „${visibility}“.
count_option
NSStringFormatSpecTypeKey
diff --git a/Localization/StringsConvertor/Intents/input/cy.lproj/Intents.strings b/Localization/StringsConvertor/Intents/input/cy.lproj/Intents.strings
index 6877490ba..11007d059 100644
--- a/Localization/StringsConvertor/Intents/input/cy.lproj/Intents.strings
+++ b/Localization/StringsConvertor/Intents/input/cy.lproj/Intents.strings
@@ -1,51 +1,51 @@
-"16wxgf" = "Post on Mastodon";
+"16wxgf" = "Postio ar Mastodon";
-"751xkl" = "Text Content";
+"751xkl" = "Cynnwys Testun";
-"CsR7G2" = "Post on Mastodon";
+"CsR7G2" = "Postio ar Mastodon";
-"HZSGTr" = "What content to post?";
+"HZSGTr" = "Pa gynnwys i bostio?";
-"HdGikU" = "Posting failed";
+"HdGikU" = "Methwyd postio";
-"KDNTJ4" = "Failure Reason";
+"KDNTJ4" = "Rheswm y Gwall";
-"RHxKOw" = "Send Post with text content";
+"RHxKOw" = "Cyhoeddi Post â chynnwys testun";
"RxSqsb" = "Post";
-"WCIR3D" = "Post ${content} on Mastodon";
+"WCIR3D" = "Postio ${content} ar Mastodon";
"ZKJSNu" = "Post";
"ZS1XaK" = "${content}";
-"ZbSjzC" = "Visibility";
+"ZbSjzC" = "Preifatrwydd";
-"Zo4jgJ" = "Post Visibility";
+"Zo4jgJ" = "Preifatrwydd Post";
-"apSxMG-dYQ5NN" = "There are ${count} options matching ‘Public’.";
+"apSxMG-dYQ5NN" = "Ceir ${count} opsiwn ar gyfer ‘Cyhoeddus’.";
-"apSxMG-ehFLjY" = "There are ${count} options matching ‘Followers Only’.";
+"apSxMG-ehFLjY" = "Ceir ${count} opsiwn ar gyfer ‘Dilynwyr yn Unig’.";
-"ayoYEb-dYQ5NN" = "${content}, Public";
+"ayoYEb-dYQ5NN" = "${content}, Cyhoeddus";
-"ayoYEb-ehFLjY" = "${content}, Followers Only";
+"ayoYEb-ehFLjY" = "${content}, Dilynwyr yn Unig";
-"dUyuGg" = "Post on Mastodon";
+"dUyuGg" = "Postio ar Mastodon";
-"dYQ5NN" = "Public";
+"dYQ5NN" = "Cyhoeddus";
-"ehFLjY" = "Followers Only";
+"ehFLjY" = "Dilynwyr yn unig";
-"gfePDu" = "Posting failed. ${failureReason}";
+"gfePDu" = "Methwyd postio. ${failureReason}";
-"k7dbKQ" = "Post was sent successfully.";
+"k7dbKQ" = "Cyhoeddwyd y post yn llwyddiannus.";
-"oGiqmY-dYQ5NN" = "Just to confirm, you wanted ‘Public’?";
+"oGiqmY-dYQ5NN" = "I gadarnhau, rydych chi am ddewis ‘Cyhoeddus’?";
-"oGiqmY-ehFLjY" = "Just to confirm, you wanted ‘Followers Only’?";
+"oGiqmY-ehFLjY" = "I gadarnhau, rydych chi am ddewis ‘Dilynwyr yn Unig’?";
"rM6dvp" = "URL";
-"ryJLwG" = "Post was sent successfully. ";
+"ryJLwG" = "Cyhoeddwyd y post yn llwyddiannus. ";
diff --git a/Localization/StringsConvertor/Intents/input/cy.lproj/Intents.stringsdict b/Localization/StringsConvertor/Intents/input/cy.lproj/Intents.stringsdict
index f273a551d..1f37f6a45 100644
--- a/Localization/StringsConvertor/Intents/input/cy.lproj/Intents.stringsdict
+++ b/Localization/StringsConvertor/Intents/input/cy.lproj/Intents.stringsdict
@@ -5,7 +5,7 @@
There are ${count} options matching ‘${content}’. - 2
NSStringLocalizedFormatKey
- There are %#@count_option@ matching ‘${content}’.
+ Ceir %#@count_option@ ar gyfer '${content}'.
count_option
NSStringFormatSpecTypeKey
@@ -13,23 +13,23 @@
NSStringFormatValueTypeKey
%ld
zero
- %ld options
+ %ld opsiynau
one
- 1 option
+ %ld opsiwn
two
- %ld options
+ %ld opsiwn
few
- %ld options
+ %ld opsiwn
many
- %ld options
+ %ld o opsiynau
other
- %ld options
+ %ld o opsiynau
There are ${count} options matching ‘${visibility}’.
NSStringLocalizedFormatKey
- There are %#@count_option@ matching ‘${visibility}’.
+ Ceir %#@count_option@ ar gyfer '${visibility}'.
count_option
NSStringFormatSpecTypeKey
@@ -37,17 +37,17 @@
NSStringFormatValueTypeKey
%ld
zero
- %ld options
+ %ld opsiynau
one
- 1 option
+ %ld opsiwn
two
- %ld options
+ %ld opsiwn
few
- %ld options
+ %ld opsiwn
many
- %ld options
+ %ld o opsiynau
other
- %ld options
+ %ld opsiwn
diff --git a/Localization/StringsConvertor/Intents/input/de.lproj/Intents.strings b/Localization/StringsConvertor/Intents/input/de.lproj/Intents.strings
index fd3fbd40f..5d0056766 100644
--- a/Localization/StringsConvertor/Intents/input/de.lproj/Intents.strings
+++ b/Localization/StringsConvertor/Intents/input/de.lproj/Intents.strings
@@ -1,12 +1,12 @@
-"16wxgf" = "Auf Mastodon posten";
+"16wxgf" = "Auf Mastodon veröffentlichen";
"751xkl" = "Textinhalt";
-"CsR7G2" = "Auf Mastodon posten";
+"CsR7G2" = "Auf Mastodon veröffentlichen";
-"HZSGTr" = "Welcher Inhalt soll gepostet werden?";
+"HZSGTr" = "Welcher Inhalt soll veröffentlicht werden?";
-"HdGikU" = "Posten fehlgeschlagen";
+"HdGikU" = "Veröffentlichen fehlgeschlagen";
"KDNTJ4" = "Fehlerursache";
diff --git a/Localization/StringsConvertor/Intents/input/he.lproj/Intents.strings b/Localization/StringsConvertor/Intents/input/he.lproj/Intents.strings
new file mode 100644
index 000000000..6877490ba
--- /dev/null
+++ b/Localization/StringsConvertor/Intents/input/he.lproj/Intents.strings
@@ -0,0 +1,51 @@
+"16wxgf" = "Post on Mastodon";
+
+"751xkl" = "Text Content";
+
+"CsR7G2" = "Post on Mastodon";
+
+"HZSGTr" = "What content to post?";
+
+"HdGikU" = "Posting failed";
+
+"KDNTJ4" = "Failure Reason";
+
+"RHxKOw" = "Send Post with text content";
+
+"RxSqsb" = "Post";
+
+"WCIR3D" = "Post ${content} on Mastodon";
+
+"ZKJSNu" = "Post";
+
+"ZS1XaK" = "${content}";
+
+"ZbSjzC" = "Visibility";
+
+"Zo4jgJ" = "Post Visibility";
+
+"apSxMG-dYQ5NN" = "There are ${count} options matching ‘Public’.";
+
+"apSxMG-ehFLjY" = "There are ${count} options matching ‘Followers Only’.";
+
+"ayoYEb-dYQ5NN" = "${content}, Public";
+
+"ayoYEb-ehFLjY" = "${content}, Followers Only";
+
+"dUyuGg" = "Post on Mastodon";
+
+"dYQ5NN" = "Public";
+
+"ehFLjY" = "Followers Only";
+
+"gfePDu" = "Posting failed. ${failureReason}";
+
+"k7dbKQ" = "Post was sent successfully.";
+
+"oGiqmY-dYQ5NN" = "Just to confirm, you wanted ‘Public’?";
+
+"oGiqmY-ehFLjY" = "Just to confirm, you wanted ‘Followers Only’?";
+
+"rM6dvp" = "URL";
+
+"ryJLwG" = "Post was sent successfully. ";
diff --git a/Localization/StringsConvertor/Intents/input/he.lproj/Intents.stringsdict b/Localization/StringsConvertor/Intents/input/he.lproj/Intents.stringsdict
new file mode 100644
index 000000000..c88207000
--- /dev/null
+++ b/Localization/StringsConvertor/Intents/input/he.lproj/Intents.stringsdict
@@ -0,0 +1,46 @@
+
+
+
+
+ There are ${count} options matching ‘${content}’. - 2
+
+ NSStringLocalizedFormatKey
+ There are %#@count_option@ matching ‘${content}’.
+ count_option
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ %ld
+ one
+ 1 option
+ two
+ %ld options
+ many
+ %ld options
+ other
+ %ld options
+
+
+ There are ${count} options matching ‘${visibility}’.
+
+ NSStringLocalizedFormatKey
+ There are %#@count_option@ matching ‘${visibility}’.
+ count_option
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ %ld
+ one
+ 1 option
+ two
+ %ld options
+ many
+ %ld options
+ other
+ %ld options
+
+
+
+
diff --git a/Localization/StringsConvertor/Intents/input/id.lproj/Intents.strings b/Localization/StringsConvertor/Intents/input/id.lproj/Intents.strings
index aa86f7bde..e5376e35e 100644
--- a/Localization/StringsConvertor/Intents/input/id.lproj/Intents.strings
+++ b/Localization/StringsConvertor/Intents/input/id.lproj/Intents.strings
@@ -4,13 +4,13 @@
"CsR7G2" = "Buat Postingan di Mastodon";
-"HZSGTr" = "What content to post?";
+"HZSGTr" = "Konten apa yang ingin diposting?";
"HdGikU" = "Gagal memposting";
"KDNTJ4" = "Alasan Kegagalan";
-"RHxKOw" = "Send Post with text content";
+"RHxKOw" = "Kirim Postingan dengan konten berupa teks";
"RxSqsb" = "Postingan";
@@ -24,9 +24,9 @@
"Zo4jgJ" = "Visibilitas Postingan";
-"apSxMG-dYQ5NN" = "There are ${count} options matching ‘Public’.";
+"apSxMG-dYQ5NN" = "Tidak ada ${count} opsi yang cocok dengan 'Publik'.";
-"apSxMG-ehFLjY" = "There are ${count} options matching ‘Followers Only’.";
+"apSxMG-ehFLjY" = "Tidak ada ${count} opsi yang cocok dengan 'Untuk Pengikut Saja'.";
"ayoYEb-dYQ5NN" = "${content}, Publik";
diff --git a/Localization/StringsConvertor/Intents/input/id.lproj/Intents.stringsdict b/Localization/StringsConvertor/Intents/input/id.lproj/Intents.stringsdict
index a14f8b9ff..61779d15c 100644
--- a/Localization/StringsConvertor/Intents/input/id.lproj/Intents.stringsdict
+++ b/Localization/StringsConvertor/Intents/input/id.lproj/Intents.stringsdict
@@ -5,7 +5,7 @@
There are ${count} options matching ‘${content}’. - 2
NSStringLocalizedFormatKey
- There are %#@count_option@ matching ‘${content}’.
+ Ada %#@count_option@ yang cocok dengan ${content}.
count_option
NSStringFormatSpecTypeKey
@@ -13,13 +13,13 @@
NSStringFormatValueTypeKey
%ld
other
- %ld options
+ %ld opsi
There are ${count} options matching ‘${visibility}’.
NSStringLocalizedFormatKey
- There are %#@count_option@ matching ‘${visibility}’.
+ Ada %#@count_option@ yang cocok dengan ${visibility}.
count_option
NSStringFormatSpecTypeKey
@@ -27,7 +27,7 @@
NSStringFormatValueTypeKey
%ld
other
- %ld options
+ %ld opsi
diff --git a/Localization/StringsConvertor/Intents/input/is.lproj/Intents.strings b/Localization/StringsConvertor/Intents/input/is.lproj/Intents.strings
new file mode 100644
index 000000000..196c33e70
--- /dev/null
+++ b/Localization/StringsConvertor/Intents/input/is.lproj/Intents.strings
@@ -0,0 +1,51 @@
+"16wxgf" = "Birta á Mastodon";
+
+"751xkl" = "Efni texta";
+
+"CsR7G2" = "Birta á Mastodon";
+
+"HZSGTr" = "Hvaða efni á að birta?";
+
+"HdGikU" = "Birting færslu mistókst";
+
+"KDNTJ4" = "Ástæða bilunar";
+
+"RHxKOw" = "Senda færslu með textaefni";
+
+"RxSqsb" = "Færsla";
+
+"WCIR3D" = "Birta ${content} á Mastodon";
+
+"ZKJSNu" = "Færsla";
+
+"ZS1XaK" = "${content}";
+
+"ZbSjzC" = "Sýnileiki";
+
+"Zo4jgJ" = "Sýnileiki færslu";
+
+"apSxMG-dYQ5NN" = "Það eru ${count} valkostir sem samsvara ‘Opinbert’.";
+
+"apSxMG-ehFLjY" = "Það eru ${count} valkostir sem samsvara ‘Einungis fylgjendur’.";
+
+"ayoYEb-dYQ5NN" = "${content}, opinbert";
+
+"ayoYEb-ehFLjY" = "${content}, einungis fylgjendur";
+
+"dUyuGg" = "Birta á Mastodon";
+
+"dYQ5NN" = "Opinbert";
+
+"ehFLjY" = "Einungis fylgjendur";
+
+"gfePDu" = "Birting færslu mistókst. ${failureReason}";
+
+"k7dbKQ" = "Það tókst að senda færsluna.";
+
+"oGiqmY-dYQ5NN" = "Bara til að staðfesta, þú vildir 'Opinbert'?";
+
+"oGiqmY-ehFLjY" = "Bara til að staðfesta, þú vildir ''Einungis fylgjendur'?";
+
+"rM6dvp" = "URL-slóð";
+
+"ryJLwG" = "Það tókst að senda færsluna. ";
diff --git a/Localization/StringsConvertor/Intents/input/is.lproj/Intents.stringsdict b/Localization/StringsConvertor/Intents/input/is.lproj/Intents.stringsdict
new file mode 100644
index 000000000..fe12c972a
--- /dev/null
+++ b/Localization/StringsConvertor/Intents/input/is.lproj/Intents.stringsdict
@@ -0,0 +1,38 @@
+
+
+
+
+ There are ${count} options matching ‘${content}’. - 2
+
+ NSStringLocalizedFormatKey
+ Það eru %#@count_option@ sem samsvara ‘${content}’.
+ count_option
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ %ld
+ one
+ 1 valkostur
+ other
+ %ld valkostir
+
+
+ There are ${count} options matching ‘${visibility}’.
+
+ NSStringLocalizedFormatKey
+ Það eru %#@count_option@ sem samsvara ‘${visibility}’.
+ count_option
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ %ld
+ one
+ 1 valkostur
+ other
+ %ld valkostir
+
+
+
+
diff --git a/Localization/StringsConvertor/Intents/input/kab.lproj/Intents.stringsdict b/Localization/StringsConvertor/Intents/input/kab.lproj/Intents.stringsdict
index a8aeeaaf1..6cead6f34 100644
--- a/Localization/StringsConvertor/Intents/input/kab.lproj/Intents.stringsdict
+++ b/Localization/StringsConvertor/Intents/input/kab.lproj/Intents.stringsdict
@@ -29,9 +29,9 @@
NSStringFormatValueTypeKey
%ld
one
- 1 uɣewwaṛ
+ %ld n uɣewwaṛ
other
- %ld iɣewwaṛen
+ %ld n iɣewwaṛen
diff --git a/Localization/StringsConvertor/Intents/input/lv.lproj/Intents.strings b/Localization/StringsConvertor/Intents/input/lv.lproj/Intents.strings
index eddd2026f..fdd529eee 100644
--- a/Localization/StringsConvertor/Intents/input/lv.lproj/Intents.strings
+++ b/Localization/StringsConvertor/Intents/input/lv.lproj/Intents.strings
@@ -1,51 +1,51 @@
-"16wxgf" = "Post on Mastodon";
+"16wxgf" = "Publicē Mastodon";
-"751xkl" = "Text Content";
+"751xkl" = "Teksta Saturu";
-"CsR7G2" = "Post on Mastodon";
+"CsR7G2" = "Publicē Mastodon";
-"HZSGTr" = "What content to post?";
+"HZSGTr" = "Kādu saturu publicēt?";
-"HdGikU" = "Posting failed";
+"HdGikU" = "Publicēšana neizdevās";
-"KDNTJ4" = "Failure Reason";
+"KDNTJ4" = "Neizdošanās Iemesls";
-"RHxKOw" = "Send Post with text content";
+"RHxKOw" = "Sūtīt Ziņu ar teksta saturu";
"RxSqsb" = "Ziņa";
-"WCIR3D" = "Post ${content} on Mastodon";
+"WCIR3D" = "Publicēt ${content} Mastodon";
-"ZKJSNu" = "Post";
+"ZKJSNu" = "Ziņa";
"ZS1XaK" = "${content}";
-"ZbSjzC" = "Visibility";
+"ZbSjzC" = "Redzamība";
-"Zo4jgJ" = "Post Visibility";
+"Zo4jgJ" = "Ziņu Redzamība";
-"apSxMG-dYQ5NN" = "There are ${count} options matching ‘Public’.";
+"apSxMG-dYQ5NN" = "Ir ${count} opcijas, kas atbilst “Publisks”.";
-"apSxMG-ehFLjY" = "There are ${count} options matching ‘Followers Only’.";
+"apSxMG-ehFLjY" = "Ir ${count} opcijas, kas atbilst “Tikai Sekotājiem”.";
-"ayoYEb-dYQ5NN" = "${content}, Public";
+"ayoYEb-dYQ5NN" = "${content}, Publisks";
-"ayoYEb-ehFLjY" = "${content}, Followers Only";
+"ayoYEb-ehFLjY" = "${content}, Tikai Sekotājiem";
-"dUyuGg" = "Post on Mastodon";
+"dUyuGg" = "Publicē Mastodon";
"dYQ5NN" = "Publisks";
"ehFLjY" = "Tikai sekotājiem";
-"gfePDu" = "Posting failed. ${failureReason}";
+"gfePDu" = "Publicēšana neizdevās. ${failureReason}";
-"k7dbKQ" = "Post was sent successfully.";
+"k7dbKQ" = "Ziņa tika veiksmīgi nosūtīta.";
-"oGiqmY-dYQ5NN" = "Just to confirm, you wanted ‘Public’?";
+"oGiqmY-dYQ5NN" = "Tikai apstiprinājumam, tu vēlējies \"Publisks\"?";
-"oGiqmY-ehFLjY" = "Just to confirm, you wanted ‘Followers Only’?";
+"oGiqmY-ehFLjY" = "Tikai apstiprinājumam, tu vēlējies \"Tikai Sekotājiem\"?";
"rM6dvp" = "URL";
-"ryJLwG" = "Post was sent successfully. ";
+"ryJLwG" = "Ziņa tika veiksmīgi nosūtīta. ";
diff --git a/Localization/StringsConvertor/Intents/input/lv.lproj/Intents.stringsdict b/Localization/StringsConvertor/Intents/input/lv.lproj/Intents.stringsdict
index 8926678aa..2461fec49 100644
--- a/Localization/StringsConvertor/Intents/input/lv.lproj/Intents.stringsdict
+++ b/Localization/StringsConvertor/Intents/input/lv.lproj/Intents.stringsdict
@@ -5,7 +5,7 @@
There are ${count} options matching ‘${content}’. - 2
NSStringLocalizedFormatKey
- There are %#@count_option@ matching ‘${content}’.
+ Ir %#@count_option@, kas atbilst “${content}”.
count_option
NSStringFormatSpecTypeKey
@@ -13,17 +13,17 @@
NSStringFormatValueTypeKey
%ld
zero
- %ld options
+ %ld opciju
one
- 1 option
+ 1 opcija
other
- %ld options
+ %ld opcijas
There are ${count} options matching ‘${visibility}’.
NSStringLocalizedFormatKey
- There are %#@count_option@ matching ‘${visibility}’.
+ Ir %#@count_option@, kas atbilst “${visibility}”.
count_option
NSStringFormatSpecTypeKey
@@ -31,11 +31,11 @@
NSStringFormatValueTypeKey
%ld
zero
- %ld options
+ %ld izvēles
one
- 1 option
+ 1 izvēle
other
- %ld options
+ %ld izvēles
diff --git a/Localization/StringsConvertor/Intents/input/my.lproj/Intents.strings b/Localization/StringsConvertor/Intents/input/my.lproj/Intents.strings
new file mode 100644
index 000000000..8fd6a698c
--- /dev/null
+++ b/Localization/StringsConvertor/Intents/input/my.lproj/Intents.strings
@@ -0,0 +1,51 @@
+"16wxgf" = "Mastodon တွင် ပို့စ်တင်ရန်";
+
+"751xkl" = "အကြောင်းအရာ";
+
+"CsR7G2" = "Mastodon တွင် ပို့စ်တင်ရန်";
+
+"HZSGTr" = "ဘယ်အကြောင်းအရာကို ပို့စ်တင်မလဲ?";
+
+"HdGikU" = "ပို့စ်တင််ခြင်း မအောင်မြင်ပါ";
+
+"KDNTJ4" = "မအောင်မြင်ရသည့် အကြောင်းပြချက်";
+
+"RHxKOw" = "ပို့စ်ကို အကြောင်းအရာနှင့်တကွ တင်ရန်";
+
+"RxSqsb" = "ပို့စ်";
+
+"WCIR3D" = "${content} ကို Mastodon တွင် ပိုစ့်တင်ရန်";
+
+"ZKJSNu" = "ပို့စ်";
+
+"ZS1XaK" = "${content}";
+
+"ZbSjzC" = "မြင်နိုင်မှု";
+
+"Zo4jgJ" = "ပို့်စ်မြင်နိုင်မှု";
+
+"apSxMG-dYQ5NN" = "‘Public’ နှင့် ကိုက်ညီသော ရွေးချယ်စရာ ${count} ခု ရှိသည်။";
+
+"apSxMG-ehFLjY" = "‘Followers Only’ နှင့် ကိုက်ညီသော ရွေးချယ်စရာ ${count} ခု ရှိသည်။";
+
+"ayoYEb-dYQ5NN" = "${content}, အများမြင်";
+
+"ayoYEb-ehFLjY" = "${content}, စောင့်ကြည့်သူများ သီးသန့်";
+
+"dUyuGg" = "Mastodon တွင် ပို့စ်တင်ရန်";
+
+"dYQ5NN" = "အများမြင်";
+
+"ehFLjY" = "စောင့်ကြည့်သူများသီးသန့်";
+
+"gfePDu" = "ပို့စ်တင််ခြင်း မအောင်မြင်ပါ၊ ${failureReason}";
+
+"k7dbKQ" = "ပို့စ်ကို အောင်မြင်စွာ ပို့ခဲ့သည်";
+
+"oGiqmY-dYQ5NN" = "\"အများမြင်\" ကို ရွေးမည်် သေချာပါသလား?";
+
+"oGiqmY-ehFLjY" = "\"စောင့်ကြည့်သူများသီးသန့်\" ကို ရွေးမည်် သေချာပါသလား?";
+
+"rM6dvp" = "URL";
+
+"ryJLwG" = "ပို့စ်ကို အောင်မြင်စွာ ပို့ခဲ့သည်";
diff --git a/Localization/StringsConvertor/Intents/input/my.lproj/Intents.stringsdict b/Localization/StringsConvertor/Intents/input/my.lproj/Intents.stringsdict
new file mode 100644
index 000000000..577ffd124
--- /dev/null
+++ b/Localization/StringsConvertor/Intents/input/my.lproj/Intents.stringsdict
@@ -0,0 +1,34 @@
+
+
+
+
+ There are ${count} options matching ‘${content}’. - 2
+
+ NSStringLocalizedFormatKey
+ ‘${content}’ နှင့် ကိုက်ညီသော ရွေးချယ်စရာ %#@count_option@ ခု ရှိသည်။
+ count_option
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ %ld
+ other
+ ရွေးချယ်စရာ %ld ခု
+
+
+ There are ${count} options matching ‘${visibility}’.
+
+ NSStringLocalizedFormatKey
+ ‘${visibility}’ နှင့် ကိုက်ညီသော ရွေးချယ်စရာ %#@count_option@ ခု ရှိသည်။
+ count_option
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ %ld
+ other
+ ရွေးချယ်စရာ %ld ခု
+
+
+
+
diff --git a/Localization/StringsConvertor/Intents/input/pt-BR.lproj/Intents.strings b/Localization/StringsConvertor/Intents/input/pt-BR.lproj/Intents.strings
index 4d4e426c6..3e6806953 100644
--- a/Localization/StringsConvertor/Intents/input/pt-BR.lproj/Intents.strings
+++ b/Localization/StringsConvertor/Intents/input/pt-BR.lproj/Intents.strings
@@ -4,9 +4,9 @@
"CsR7G2" = "Postar no Mastodon";
-"HZSGTr" = "What content to post?";
+"HZSGTr" = "Qual conteúdo a publicar?";
-"HdGikU" = "Posting failed";
+"HdGikU" = "Falha na publicação";
"KDNTJ4" = "Motivo da falha";
@@ -22,30 +22,30 @@
"ZbSjzC" = "Visibilidade";
-"Zo4jgJ" = "Post Visibility";
+"Zo4jgJ" = "Visibilidade da publicação";
-"apSxMG-dYQ5NN" = "There are ${count} options matching ‘Public’.";
+"apSxMG-dYQ5NN" = "Existem ${count} opções correspondentes a ‘Público’.";
-"apSxMG-ehFLjY" = "There are ${count} options matching ‘Followers Only’.";
+"apSxMG-ehFLjY" = "Existem ${count} opções correspondentes a ‘Apenas para seguidores’.";
-"ayoYEb-dYQ5NN" = "${content}, Public";
+"ayoYEb-dYQ5NN" = "${content}, Público";
-"ayoYEb-ehFLjY" = "${content}, Followers Only";
+"ayoYEb-ehFLjY" = "${content}, Apenas para seguidores";
-"dUyuGg" = "Post on Mastodon";
+"dUyuGg" = "Postar no Mastodon";
-"dYQ5NN" = "Public";
+"dYQ5NN" = "Público";
-"ehFLjY" = "Followers Only";
+"ehFLjY" = "Apenas para seguidores";
-"gfePDu" = "Posting failed. ${failureReason}";
+"gfePDu" = "Falha na publicação. ${failureReason}";
-"k7dbKQ" = "Post was sent successfully.";
+"k7dbKQ" = "Publicação enviada com sucesso.";
-"oGiqmY-dYQ5NN" = "Just to confirm, you wanted ‘Public’?";
+"oGiqmY-dYQ5NN" = "Só para confirmar, você queria ‘Público’?";
-"oGiqmY-ehFLjY" = "Just to confirm, you wanted ‘Followers Only’?";
+"oGiqmY-ehFLjY" = "Só para confirmar, você queria ‘Apenas para seguidores’?";
"rM6dvp" = "URL";
-"ryJLwG" = "Post was sent successfully. ";
+"ryJLwG" = "Publicação enviada com sucesso. ";
diff --git a/Localization/StringsConvertor/Intents/input/uk.lproj/Intents.strings b/Localization/StringsConvertor/Intents/input/uk.lproj/Intents.strings
index 6877490ba..ecbc72785 100644
--- a/Localization/StringsConvertor/Intents/input/uk.lproj/Intents.strings
+++ b/Localization/StringsConvertor/Intents/input/uk.lproj/Intents.strings
@@ -1,51 +1,51 @@
-"16wxgf" = "Post on Mastodon";
+"16wxgf" = "Поділитись в Mastodon";
-"751xkl" = "Text Content";
+"751xkl" = "Текстовий вміст";
-"CsR7G2" = "Post on Mastodon";
+"CsR7G2" = "Поділитись в Mastodon";
-"HZSGTr" = "What content to post?";
+"HZSGTr" = "Який зміст допису?";
-"HdGikU" = "Posting failed";
+"HdGikU" = "Помилка при надсиланні";
-"KDNTJ4" = "Failure Reason";
+"KDNTJ4" = "Причина помилки";
-"RHxKOw" = "Send Post with text content";
+"RHxKOw" = "Надіслати пост із текстом";
-"RxSqsb" = "Post";
+"RxSqsb" = "Допис";
-"WCIR3D" = "Post ${content} on Mastodon";
+"WCIR3D" = "Опублікувати ${content} на Mastodon";
-"ZKJSNu" = "Post";
+"ZKJSNu" = "Допис";
"ZS1XaK" = "${content}";
-"ZbSjzC" = "Visibility";
+"ZbSjzC" = "Видимість";
-"Zo4jgJ" = "Post Visibility";
+"Zo4jgJ" = "Видимість допису";
-"apSxMG-dYQ5NN" = "There are ${count} options matching ‘Public’.";
+"apSxMG-dYQ5NN" = "Знайдено ${count} варіантів, що задовольняють \"Публічні\".";
-"apSxMG-ehFLjY" = "There are ${count} options matching ‘Followers Only’.";
+"apSxMG-ehFLjY" = "Знайдено ${count} варіантів, які відповідають \"Тільки для підписників\".";
-"ayoYEb-dYQ5NN" = "${content}, Public";
+"ayoYEb-dYQ5NN" = "${content}, публічний";
-"ayoYEb-ehFLjY" = "${content}, Followers Only";
+"ayoYEb-ehFLjY" = "${content}, тільки підписники";
-"dUyuGg" = "Post on Mastodon";
+"dUyuGg" = "Поділитись в Mastodon";
-"dYQ5NN" = "Public";
+"dYQ5NN" = "Публічно";
-"ehFLjY" = "Followers Only";
+"ehFLjY" = "Тільки для підписників";
-"gfePDu" = "Posting failed. ${failureReason}";
+"gfePDu" = "Помилка. ${failureReason}";
-"k7dbKQ" = "Post was sent successfully.";
+"k7dbKQ" = "Допис успішно відправлено.";
-"oGiqmY-dYQ5NN" = "Just to confirm, you wanted ‘Public’?";
+"oGiqmY-dYQ5NN" = "Вам дійсно потрібні \"Публічно\"?";
-"oGiqmY-ehFLjY" = "Just to confirm, you wanted ‘Followers Only’?";
+"oGiqmY-ehFLjY" = "Вам дійсно потрібні \"Тільки для підписників\"?";
"rM6dvp" = "URL";
-"ryJLwG" = "Post was sent successfully. ";
+"ryJLwG" = "Допис успішно відправлено. ";
diff --git a/Localization/StringsConvertor/Intents/input/uk.lproj/Intents.stringsdict b/Localization/StringsConvertor/Intents/input/uk.lproj/Intents.stringsdict
index a739f778f..ef8eb5d56 100644
--- a/Localization/StringsConvertor/Intents/input/uk.lproj/Intents.stringsdict
+++ b/Localization/StringsConvertor/Intents/input/uk.lproj/Intents.stringsdict
@@ -5,7 +5,7 @@
There are ${count} options matching ‘${content}’. - 2
NSStringLocalizedFormatKey
- There are %#@count_option@ matching ‘${content}’.
+ Знайдено %#@count_option@ відповідних '${content}.
count_option
NSStringFormatSpecTypeKey
@@ -13,19 +13,19 @@
NSStringFormatValueTypeKey
%ld
one
- 1 option
+ параметр
few
- %ld options
+ %ld параметри
many
- %ld options
+ %ld параметрів
other
- %ld options
+ %ld параметрів
There are ${count} options matching ‘${visibility}’.
NSStringLocalizedFormatKey
- There are %#@count_option@ matching ‘${visibility}’.
+ Знайдено %#@count_option@ відповідних '${visibility}.
count_option
NSStringFormatSpecTypeKey
@@ -33,13 +33,13 @@
NSStringFormatValueTypeKey
%ld
one
- 1 option
+ параметр
few
- %ld options
+ %ld параметри
many
- %ld options
+ %ld параметрів
other
- %ld options
+ %ld параметрів
diff --git a/Localization/StringsConvertor/Sources/StringsConvertor/main.swift b/Localization/StringsConvertor/Sources/StringsConvertor/main.swift
index beba6cb3f..665161e2c 100644
--- a/Localization/StringsConvertor/Sources/StringsConvertor/main.swift
+++ b/Localization/StringsConvertor/Sources/StringsConvertor/main.swift
@@ -53,6 +53,7 @@ private func map(language: String) -> String? {
case "ca.lproj": return "ca" // Catalan
case "zh-Hans.lproj": return "zh-Hans" // Chinese Simplified
case "zh-Hant.lproj": return "zh-Hant" // Chinese Traditional
+ case "cs.lproj": return "cs" // Czech
case "nl.lproj": return "nl" // Dutch
case "en.lproj": return "en"
case "fi.lproj": return "fi" // Finnish
@@ -65,6 +66,7 @@ private func map(language: String) -> String? {
case "kmr.lproj": return "ku" // Kurmanji (Kurdish) [intent mapping]
case "ru.lproj": return "ru" // Russian
case "gd.lproj": return "gd" // Scottish Gaelic
+ case "sl.lproj": return "sl" // Slovenian
case "ckb.lproj": return "ckb" // Sorani (Kurdish)
case "es.lproj": return "es" // Spanish
case "es_AR.lproj": return "es-AR" // Spanish, Argentina
diff --git a/Localization/StringsConvertor/input/Base.lproj/Localizable.stringsdict b/Localization/StringsConvertor/input/Base.lproj/Localizable.stringsdict
index cd97825f4..37ce1f032 100644
--- a/Localization/StringsConvertor/input/Base.lproj/Localizable.stringsdict
+++ b/Localization/StringsConvertor/input/Base.lproj/Localizable.stringsdict
@@ -13,15 +13,15 @@
NSStringFormatValueTypeKey
ld
zero
- no unread notification
+ no unread notifications
one
1 unread notification
few
%ld unread notifications
many
- %ld unread notification
+ %ld unread notifications
other
- %ld unread notification
+ %ld unread notifications
a11y.plural.count.input_limit_exceeds
@@ -71,7 +71,7 @@
a11y.plural.count.characters_left
NSStringLocalizedFormatKey
- %#@character_count@ left
+ %#@character_count@
character_count
NSStringFormatSpecTypeKey
@@ -79,15 +79,15 @@
NSStringFormatValueTypeKey
ld
zero
- no characters
+ no characters left
one
- 1 character
+ 1 character left
few
- %ld characters
+ %ld characters left
many
- %ld characters
+ %ld characters left
other
- %ld characters
+ %ld characters left
plural.count.followed_by_and_mutual
diff --git a/Localization/StringsConvertor/input/Base.lproj/app.json b/Localization/StringsConvertor/input/Base.lproj/app.json
index 30566d8d6..963b4aed1 100644
--- a/Localization/StringsConvertor/input/Base.lproj/app.json
+++ b/Localization/StringsConvertor/input/Base.lproj/app.json
@@ -51,6 +51,11 @@
"clean_cache": {
"title": "Clean Cache",
"message": "Successfully cleaned %s cache."
+ },
+ "translation_failed": {
+ "title": "Note",
+ "message": "Translation failed. Maybe the administrator has not enabled translations on this server or this server is running an older version of Mastodon where translations are not yet supported.",
+ "button": "OK"
}
},
"controls": {
@@ -74,10 +79,11 @@
"take_photo": "Take Photo",
"save_photo": "Save Photo",
"copy_photo": "Copy Photo",
- "sign_in": "Sign In",
- "sign_up": "Sign Up",
+ "sign_in": "Log in",
+ "sign_up": "Create account",
"see_more": "See More",
"preview": "Preview",
+ "copy": "Copy",
"share": "Share",
"share_user": "Share %s",
"share_post": "Share Post",
@@ -91,12 +97,16 @@
"block_domain": "Block %s",
"unblock_domain": "Unblock %s",
"settings": "Settings",
- "delete": "Delete"
+ "delete": "Delete",
+ "translate_post": {
+ "title": "Translate from %s",
+ "unknown_language": "Unknown"
+ }
},
"tabs": {
"home": "Home",
- "search": "Search",
- "notification": "Notification",
+ "search_and_explore": "Search and Explore",
+ "notifications": "Notifications",
"profile": "Profile"
},
"keyboard": {
@@ -132,6 +142,8 @@
"sensitive_content": "Sensitive Content",
"media_content_warning": "Tap anywhere to reveal",
"tap_to_reveal": "Tap to reveal",
+ "load_embed": "Load Embed",
+ "link_via_user": "%s via %s",
"poll": {
"vote": "Vote",
"closed": "Closed"
@@ -153,6 +165,7 @@
"show_image": "Show image",
"show_gif": "Show GIF",
"show_video_player": "Show video player",
+ "share_link_in_post": "Share Link in Post",
"tap_then_hold_to_show_menu": "Tap then hold to show menu"
},
"tag": {
@@ -168,6 +181,18 @@
"private": "Only their followers can see this post.",
"private_from_me": "Only my followers can see this post.",
"direct": "Only mentioned user can see this post."
+ },
+ "translation": {
+ "translated_from": "Translated from %s using %s",
+ "unknown_language": "Unknown",
+ "unknown_provider": "Unknown",
+ "show_original": "Shown Original"
+ },
+ "media": {
+ "accessibility_label": "%s, attachment %d of %d",
+ "expand_image_hint": "Expands the image. Double-tap and hold to show actions",
+ "expand_gif_hint": "Expands the GIF. Double-tap and hold to show actions",
+ "expand_video_hint": "Shows the video player. Double-tap and hold to show actions"
}
},
"friendship": {
@@ -218,10 +243,16 @@
"get_started": "Get Started",
"log_in": "Log In"
},
+ "login": {
+ "title": "Welcome back",
+ "subtitle": "Log you in on the server you created your account on.",
+ "server_search_field": {
+ "placeholder": "Enter URL or search for your server"
+ }
+ },
"server_picker": {
"title": "Mastodon is made of users in different servers.",
- "subtitle": "Pick a server based on your interests, region, or a general purpose one.",
- "subtitle_extend": "Pick a server based on your interests, region, or a general purpose one. Each server is operated by an entirely independent organization or individual.",
+ "subtitle": "Pick a server based on your region, interests, or a general purpose one. You can still chat with anyone on Mastodon, regardless of your servers.",
"button": {
"category": {
"all": "All",
@@ -248,8 +279,7 @@
"category": "CATEGORY"
},
"input": {
- "placeholder": "Search servers",
- "search_servers_or_enter_url": "Search servers or enter URL"
+ "search_servers_or_enter_url": "Search communities or enter URL"
},
"empty_state": {
"finding_servers": "Finding available servers...",
@@ -355,7 +385,7 @@
"published": "Published!",
"Publishing": "Publishing post...",
"accessibility": {
- "logo_label": "Logo Button",
+ "logo_label": "Mastodon",
"logo_hint": "Tap to scroll to top and tap again to previous location"
}
}
@@ -439,11 +469,15 @@
"follows_you": "Follows You"
},
"dashboard": {
- "posts": "posts",
- "following": "following",
- "followers": "followers"
+ "my_posts": "posts",
+ "my_following": "following",
+ "my_followers": "followers",
+ "other_posts": "posts",
+ "other_following": "following",
+ "other_followers": "followers"
},
"fields": {
+ "joined": "Joined",
"add_row": "Add Row",
"placeholder": {
"label": "Label",
@@ -717,6 +751,19 @@
},
"bookmark": {
"title": "Bookmarks"
+
+ },
+ "followed_tags": {
+ "title": "Followed Tags",
+ "header": {
+ "posts": "posts",
+ "participants": "participants",
+ "posts_today": "posts today"
+ },
+ "actions": {
+ "follow": "Follow",
+ "unfollow": "Unfollow"
+ }
}
}
-}
\ No newline at end of file
+}
diff --git a/Localization/StringsConvertor/input/an.lproj/Localizable.stringsdict b/Localization/StringsConvertor/input/an.lproj/Localizable.stringsdict
new file mode 100644
index 000000000..d7187e039
--- /dev/null
+++ b/Localization/StringsConvertor/input/an.lproj/Localizable.stringsdict
@@ -0,0 +1,465 @@
+
+
+
+
+ a11y.plural.count.unread.notification
+
+ NSStringLocalizedFormatKey
+ %#@notification_count_unread_notification@
+ notification_count_unread_notification
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 notificación no leyida
+ other
+ %ld notificacions no leyidas
+
+
+ a11y.plural.count.input_limit_exceeds
+
+ NSStringLocalizedFormatKey
+ Limite de dentrada superau en %#@character_count@ caracters
+ character_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 caracter
+ other
+ %ld caracters
+
+
+ a11y.plural.count.input_limit_remains
+
+ NSStringLocalizedFormatKey
+ Limite de dentrada restante: %#@character_count@ caracters
+ character_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 caracter
+ other
+ %ld caracters
+
+
+ a11y.plural.count.characters_left
+
+ NSStringLocalizedFormatKey
+ queda %#@character_count@
+ character_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 caracter
+ other
+ %ld caracters
+
+
+ plural.count.followed_by_and_mutual
+
+ NSStringLocalizedFormatKey
+ %#@names@%#@count_mutual@
+ names
+
+ one
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ other
+
+
+ count_mutual
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ Seguiu per %1$@ y unatro mutuo
+ other
+ Seguiu per %1$@ y %ld mutuos
+
+
+ plural.count.metric_formatted.post
+
+ NSStringLocalizedFormatKey
+ %@ %#@post_count@
+ post_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ publicación
+ other
+ publicacions
+
+
+ plural.count.media
+
+ NSStringLocalizedFormatKey
+ %#@media_count@
+ media_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 media
+ other
+ %ld media
+
+
+ plural.count.post
+
+ NSStringLocalizedFormatKey
+ %#@post_count@
+ post_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 publicación
+ other
+ %ld publicacions
+
+
+ plural.count.favorite
+
+ NSStringLocalizedFormatKey
+ %#@favorite_count@
+ favorite_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 favorito
+ other
+ %ld favoritos
+
+
+ plural.count.reblog
+
+ NSStringLocalizedFormatKey
+ %#@reblog_count@
+ reblog_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 reblogueo
+ other
+ %ld reblogueos
+
+
+ plural.count.reply
+
+ NSStringLocalizedFormatKey
+ %#@reply_count@
+ reply_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 respuesta
+ other
+ %ld respuestas
+
+
+ plural.count.vote
+
+ NSStringLocalizedFormatKey
+ %#@vote_count@
+ vote_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 voto
+ other
+ %ld votos
+
+
+ plural.count.voter
+
+ NSStringLocalizedFormatKey
+ %#@voter_count@
+ voter_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 votante
+ other
+ %ld votantes
+
+
+ plural.people_talking
+
+ NSStringLocalizedFormatKey
+ %#@count_people_talking@
+ count_people_talking
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 persona charrando
+ other
+ %ld personas son charrando d'esto
+
+
+ plural.count.following
+
+ NSStringLocalizedFormatKey
+ %#@count_following@
+ count_following
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 seguindo
+ other
+ %ld seguindo
+
+
+ plural.count.follower
+
+ NSStringLocalizedFormatKey
+ %#@count_follower@
+ count_follower
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 seguidor
+ other
+ %ld seguidores
+
+
+ date.year.left
+
+ NSStringLocalizedFormatKey
+ %#@count_year_left@
+ count_year_left
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 anyo restante
+ other
+ %ld anyos restantes
+
+
+ date.month.left
+
+ NSStringLocalizedFormatKey
+ %#@count_month_left@
+ count_month_left
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 mes restante
+ other
+ %ld meses restantes
+
+
+ date.day.left
+
+ NSStringLocalizedFormatKey
+ %#@count_day_left@
+ count_day_left
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 día restante
+ other
+ %ld días restantes
+
+
+ date.hour.left
+
+ NSStringLocalizedFormatKey
+ %#@count_hour_left@
+ count_hour_left
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 hora restante
+ other
+ %ld horas restantes
+
+
+ date.minute.left
+
+ NSStringLocalizedFormatKey
+ %#@count_minute_left@
+ count_minute_left
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 minuto restant
+ other
+ %ld minutos restants
+
+
+ date.second.left
+
+ NSStringLocalizedFormatKey
+ %#@count_second_left@
+ count_second_left
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 segundo restante
+ other
+ %ld segundos restantes
+
+
+ date.year.ago.abbr
+
+ NSStringLocalizedFormatKey
+ %#@count_year_ago_abbr@
+ count_year_ago_abbr
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ Fa 1 anyo
+ other
+ Fa %ld anyos
+
+
+ date.month.ago.abbr
+
+ NSStringLocalizedFormatKey
+ %#@count_month_ago_abbr@
+ count_month_ago_abbr
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ Fa 1 mes
+ other
+ Fa %ld meses
+
+
+ date.day.ago.abbr
+
+ NSStringLocalizedFormatKey
+ %#@count_day_ago_abbr@
+ count_day_ago_abbr
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ Fa 1 día
+ other
+ Fa %ld días
+
+
+ date.hour.ago.abbr
+
+ NSStringLocalizedFormatKey
+ %#@count_hour_ago_abbr@
+ count_hour_ago_abbr
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ Fa 1 h
+ other
+ Fa %ld h
+
+
+ date.minute.ago.abbr
+
+ NSStringLocalizedFormatKey
+ %#@count_minute_ago_abbr@
+ count_minute_ago_abbr
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ Fa 1 min
+ other
+ Fa %ld min
+
+
+ date.second.ago.abbr
+
+ NSStringLocalizedFormatKey
+ %#@count_second_ago_abbr@
+ count_second_ago_abbr
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ Fa 1 s
+ other
+ Fa %ld s
+
+
+
+
diff --git a/Localization/StringsConvertor/input/an.lproj/app.json b/Localization/StringsConvertor/input/an.lproj/app.json
new file mode 100644
index 000000000..b0e090cf0
--- /dev/null
+++ b/Localization/StringsConvertor/input/an.lproj/app.json
@@ -0,0 +1,762 @@
+{
+ "common": {
+ "alerts": {
+ "common": {
+ "please_try_again": "Per favor, torna a intentar-lo.",
+ "please_try_again_later": "Per favor, torna a intentar-lo mas enta debant."
+ },
+ "sign_up_failure": {
+ "title": "Error en rechistrar-se"
+ },
+ "server_error": {
+ "title": "Error d'o servidor"
+ },
+ "vote_failure": {
+ "title": "Voto fallido",
+ "poll_ended": "La enquesta ha rematau"
+ },
+ "discard_post_content": {
+ "title": "Descartar borrador",
+ "message": "Confirma pa descartar lo conteniu d'a publicación."
+ },
+ "publish_post_failure": {
+ "title": "Error de publicación",
+ "message": "No s'ha puesto publicar la publicación. Per favor, revise la suya connexión a internet.",
+ "attachments_message": {
+ "video_attach_with_photo": "No puetz adchuntar un video a una publicación que ya contiene imachens.",
+ "more_than_one_video": "No puetz adchuntar mas d'un video."
+ }
+ },
+ "edit_profile_failure": {
+ "title": "Error en a Edición d'o Perfil",
+ "message": "No s'ha puesto editar lo perfil. Per favor, intenta-lo de nuevo."
+ },
+ "sign_out": {
+ "title": "Zarrar Sesión",
+ "message": "Yes seguro de que quiers zarrar la sesión?",
+ "confirm": "Zarrar Sesión"
+ },
+ "block_domain": {
+ "title": "Yes realment seguro, de verdat, que quiers blocar %s a lo completo? En a mayoría d'os casos, uns pocos bloqueyos u silenciaus concretos son suficients y preferibles. No veyerás conteniu d'ixe dominio y totz los tuyos seguidores d'ixe dominio serán eliminaus.",
+ "block_entire_domain": "Blocar Dominio"
+ },
+ "save_photo_failure": {
+ "title": "Error en Alzar Foto",
+ "message": "Per favor, activa lo permiso d'acceso a la biblioteca de fotos pa alzar la foto."
+ },
+ "delete_post": {
+ "title": "Yes seguro de que quiers eliminar esta publicación?",
+ "message": "Yes seguro de que quiers borrar esta publicación?"
+ },
+ "clean_cache": {
+ "title": "Limpiar Caché",
+ "message": "S'ha limpiau con exito %s de caché."
+ },
+ "translation_failed": {
+ "title": "Note",
+ "message": "Translation failed. Maybe the administrator has not enabled translations on this server or this server is running an older version of Mastodon where translations are not yet supported.",
+ "button": "OK"
+ }
+ },
+ "controls": {
+ "actions": {
+ "back": "Dezaga",
+ "next": "Siguient",
+ "previous": "Anterior",
+ "open": "Ubrir",
+ "add": "Anyadir",
+ "remove": "Eliminar",
+ "edit": "Editar",
+ "save": "Alzar",
+ "ok": "Acceptar",
+ "done": "Feito",
+ "confirm": "Confirmar",
+ "continue": "Continar",
+ "compose": "Redactar",
+ "cancel": "Cancelar",
+ "discard": "Descartar",
+ "try_again": "Intenta-lo de nuevo",
+ "take_photo": "Prener foto",
+ "save_photo": "Alzar foto",
+ "copy_photo": "Copiar foto",
+ "sign_in": "Iniciar sesión",
+ "sign_up": "Crear cuenta",
+ "see_more": "Veyer mas",
+ "preview": "Vista previa",
+ "copy": "Copy",
+ "share": "Compartir",
+ "share_user": "Compartir %s",
+ "share_post": "Compartir publicación",
+ "open_in_safari": "Ubrir en Safari",
+ "open_in_browser": "Ubrir en o navegador",
+ "find_people": "Troba chent a la quala seguir",
+ "manually_search": "Millor fer una busqueda manual",
+ "skip": "Omitir",
+ "reply": "Responder",
+ "report_user": "Reportar a %s",
+ "block_domain": "Blocar %s",
+ "unblock_domain": "Desbloquiar %s",
+ "settings": "Configuración",
+ "delete": "Borrar",
+ "translate_post": {
+ "title": "Translate from %s",
+ "unknown_language": "Unknown"
+ }
+ },
+ "tabs": {
+ "home": "Inicio",
+ "search_and_explore": "Search and Explore",
+ "notifications": "Notificacions",
+ "profile": "Perfil"
+ },
+ "keyboard": {
+ "common": {
+ "switch_to_tab": "Cambiar a %s",
+ "compose_new_post": "Escribir Nueva Publicación",
+ "show_favorites": "Amostrar Favoritos",
+ "open_settings": "Ubrir Configuración"
+ },
+ "timeline": {
+ "previous_status": "Publicación Anterior",
+ "next_status": "Siguient Publicación",
+ "open_status": "Ubrir Publicación",
+ "open_author_profile": "Ubrir Perfil de l'Autor",
+ "open_reblogger_profile": "Ubrir Perfil d'o Reblogueador",
+ "reply_status": "Responder Publicación",
+ "toggle_reblog": "Commutar lo Reblogueo en a Publicación",
+ "toggle_favorite": "Commutar la Marca de Favorito en a Publicación",
+ "toggle_content_warning": "Alternar l'Alvertencia de Conteniu",
+ "preview_image": "Previsualizar Imachen"
+ },
+ "segmented_control": {
+ "previous_section": "Sección Anterior",
+ "next_section": "Siguient Sección"
+ }
+ },
+ "status": {
+ "user_reblogged": "%s lo reblogueó",
+ "user_replied_to": "En respuesta a %s",
+ "show_post": "Amostrar Publicación",
+ "show_user_profile": "Amostrar perfil de l'usuario",
+ "content_warning": "Alvertencia de Conteniu",
+ "sensitive_content": "Conteniu sensible",
+ "media_content_warning": "Preta en qualsequier puesto pa amostrar",
+ "tap_to_reveal": "Tocar pa revelar",
+ "load_embed": "Load Embed",
+ "link_via_user": "%s via %s",
+ "poll": {
+ "vote": "Vota",
+ "closed": "Zarrau"
+ },
+ "meta_entity": {
+ "url": "Vinclo: %s",
+ "hashtag": "Hashtag: %s",
+ "mention": "Amostrar lo perfil: %s",
+ "email": "Adreza de correu: %s"
+ },
+ "actions": {
+ "reply": "Responder",
+ "reblog": "Rebloguear",
+ "unreblog": "Desfer reblogueo",
+ "favorite": "Favorito",
+ "unfavorite": "No favorito",
+ "menu": "Menú",
+ "hide": "Amagar",
+ "show_image": "Amostrar imachen",
+ "show_gif": "Amostrar GIF",
+ "show_video_player": "Amostrar reproductor de video",
+ "share_link_in_post": "Share Link in Post",
+ "tap_then_hold_to_show_menu": "Toca, dimpués mantiene pa amostrar lo menú"
+ },
+ "tag": {
+ "url": "URL",
+ "mention": "Mención",
+ "link": "Vinclo",
+ "hashtag": "Etiqueta",
+ "email": "E-mail",
+ "emoji": "Emoji"
+ },
+ "visibility": {
+ "unlisted": "Totz pueden veyer este post pero no amostrar-lo en una linia de tiempo publica.",
+ "private": "Nomás los suyos seguidores pueden veyer este mensache.",
+ "private_from_me": "Nomás los míos seguidores pueden veyer este mensache.",
+ "direct": "Nomás l'usuario mencionau puede veyer este mensache."
+ },
+ "translation": {
+ "translated_from": "Translated from %s using %s",
+ "unknown_language": "Unknown",
+ "unknown_provider": "Unknown",
+ "show_original": "Shown Original"
+ }
+ },
+ "friendship": {
+ "follow": "Seguir",
+ "following": "Seguindo",
+ "request": "Solicitut",
+ "pending": "Pendient",
+ "block": "Blocar",
+ "block_user": "Blocar a %s",
+ "block_domain": "Blocar a %s",
+ "unblock": "Desbloquiar",
+ "unblock_user": "Desbloquiar a %s",
+ "blocked": "Blocau",
+ "mute": "Silenciar",
+ "mute_user": "Silenciar a %s",
+ "unmute": "Desmutear",
+ "unmute_user": "Desmutear a %s",
+ "muted": "Silenciau",
+ "edit_info": "Editar Info",
+ "show_reblogs": "Amostrar los retuts",
+ "hide_reblogs": "Amagar los reblogs"
+ },
+ "timeline": {
+ "filtered": "Filtrau",
+ "timestamp": {
+ "now": "Agora"
+ },
+ "loader": {
+ "load_missing_posts": "Cargar publicacions faltantes",
+ "loading_missing_posts": "Cargando publicacions faltantes...",
+ "show_more_replies": "Amostrar mas respuestas"
+ },
+ "header": {
+ "no_status_found": "No s'ha trobau garra publicación",
+ "blocking_warning": "No puetz veyer lo perfil d'este usuario\n dica que lo desbloqueyes.\nLo tuyo perfil se veye asinas pa ell.",
+ "user_blocking_warning": "No puetz veyer lo perfil de %s\n dica que lo desbloqueyes.\nLo tuyo perfil se veye asinas pa ell.",
+ "blocked_warning": "No puetz veyer lo perfil d'este usuario\n dica que te desbloqueye.",
+ "user_blocked_warning": "No puetz veyer lo perfil de %s\n dica que te desbloqueye.",
+ "suspended_warning": "Este usuario ha estau suspendiu.",
+ "user_suspended_warning": "La cuenta de %s ha estau suspendida."
+ }
+ }
+ }
+ },
+ "scene": {
+ "welcome": {
+ "slogan": "Los retz socials\nde nuevo en as tuyas mans.",
+ "get_started": "Empecipiar",
+ "log_in": "Iniciar sesión"
+ },
+ "login": {
+ "title": "Bienveniu de nuevas",
+ "subtitle": "Dentrar en o servidor an que creyiés la cuenta.",
+ "server_search_field": {
+ "placeholder": "Escribir la URL u buscar lo tuyo servidor"
+ }
+ },
+ "server_picker": {
+ "title": "Tría un servidor,\nqualsequier servidor.",
+ "subtitle": "Tría un servidor basau en a tuya rechión, intereses u uno de proposito cheneral. Podrás seguir connectau con totz en Mastodon, independiement d'o servidor.",
+ "button": {
+ "category": {
+ "all": "Totas",
+ "all_accessiblity_description": "Categoría: Totas",
+ "academia": "academicos",
+ "activism": "activismo",
+ "food": "minchada",
+ "furry": "furry",
+ "games": "chuegos",
+ "general": "cheneral",
+ "journalism": "periodismo",
+ "lgbt": "lgbt",
+ "regional": "rechional",
+ "art": "arte",
+ "music": "mosica",
+ "tech": "tecnolochía"
+ },
+ "see_less": "Veyer Menos",
+ "see_more": "Veyer Más"
+ },
+ "label": {
+ "language": "IDIOMA",
+ "users": "USUARIOS",
+ "category": "CATEGORÍA"
+ },
+ "input": {
+ "search_servers_or_enter_url": "Mirar comunidatz u escribir URL"
+ },
+ "empty_state": {
+ "finding_servers": "Trobando servidors disponibles...",
+ "bad_network": "Bella cosa ha iu malament en cargar los datos. Compreba la tuya connexión a Internet.",
+ "no_results": "Sin resultaus"
+ }
+ },
+ "register": {
+ "title": "Deixa que te configuremos en %s",
+ "lets_get_you_set_up_on_domain": "Deixa que te configuremos en %s",
+ "input": {
+ "avatar": {
+ "delete": "Borrar"
+ },
+ "username": {
+ "placeholder": "nombre d'usuario",
+ "duplicate_prompt": "Este nombre d'usuario ya ye en uso."
+ },
+ "display_name": {
+ "placeholder": "nombre a amostrar"
+ },
+ "email": {
+ "placeholder": "correu electronico"
+ },
+ "password": {
+ "placeholder": "clau",
+ "require": "La tuya clau ha de contener como minimo:",
+ "character_limit": "8 caracters",
+ "accessibility": {
+ "checked": "marcau",
+ "unchecked": "sin marcar"
+ },
+ "hint": "La tuya clau ameneste tener a lo menos ueito caracters"
+ },
+ "invite": {
+ "registration_user_invite_request": "Per qué quiers unir-te?"
+ }
+ },
+ "error": {
+ "item": {
+ "username": "Nombre d'usuario",
+ "email": "Correu electronico",
+ "password": "Clau",
+ "agreement": "Acceptación",
+ "locale": "Idioma",
+ "reason": "Motivo"
+ },
+ "reason": {
+ "blocked": "%s contiene un furnidor de correu no permitiu",
+ "unreachable": "%s pareixe no existir",
+ "taken": "%s ya ye en uso",
+ "reserved": "%s ye una parola clau reservada",
+ "accepted": "%s ha d'estar acceptau",
+ "blank": "Se requiere %s",
+ "invalid": "%s no ye valido",
+ "too_long": "%s ye masiau largo",
+ "too_short": "%s ye masiau tallo",
+ "inclusion": "%s no ye una valor admitida"
+ },
+ "special": {
+ "username_invalid": "Lo nombre d'usuario solo puede contener caracters alfanumericos y guións baixos",
+ "username_too_long": "Lo nombre d'usuario ye masiau largo (no puede tener mas de 30 caracters)",
+ "email_invalid": "Esta no ye una adreza de correu electronico valida",
+ "password_too_short": "La clau ye masiau curta (ha de tener a lo menos 8 caracters)"
+ }
+ }
+ },
+ "server_rules": {
+ "title": "Qualques reglas basicas.",
+ "subtitle": "Estas reglas son establidas per los administradors de %s.",
+ "prompt": "Si continas serás sucheto a los termins de servicio y la politica de privacidat de %s.",
+ "terms_of_service": "termins d'o servicio",
+ "privacy_policy": "politica de privacidat",
+ "button": {
+ "confirm": "Accepto"
+ }
+ },
+ "confirm_email": {
+ "title": "Una zaguera coseta.",
+ "subtitle": "T'acabamos de ninviar un correu a %s, preta en o vinclo pa confirmar la tuya cuenta.",
+ "tap_the_link_we_emailed_to_you_to_verify_your_account": "Toca lo vinclo que te ninviamos per correu electronico pa verificar la tuya cuenta",
+ "button": {
+ "open_email_app": "Ubrir Aplicación de Correu Electronico",
+ "resend": "Reninviar"
+ },
+ "dont_receive_email": {
+ "title": "Revisa lo tuyo correu electronico",
+ "description": "Compreba que la tuya adreza de correu electronico sía correcta y revisa la carpeta de correu no deseyau si no l'has feito ya.",
+ "resend_email": "Tornar a Ninviar Correu Electronico"
+ },
+ "open_email_app": {
+ "title": "Revisa la tuya servilla de dentrada.",
+ "description": "T'acabamos de ninviar un correu electronico. Revisa la tuya carpeta de correu no deseyau si no l'has feito ya.",
+ "mail": "Correu",
+ "open_email_client": "Ubrir Client de Correu Electronico"
+ }
+ },
+ "home_timeline": {
+ "title": "Inicio",
+ "navigation_bar_state": {
+ "offline": "Sin Connexión",
+ "new_posts": "Veyer nuevas publicacions",
+ "published": "Publicau!",
+ "Publishing": "Publicación en curso...",
+ "accessibility": {
+ "logo_label": "Botón d'o logo",
+ "logo_hint": "Toca pa desplazar-te enta alto y toca de nuevo pa la localización anterior"
+ }
+ }
+ },
+ "suggestion_account": {
+ "title": "Troba Chent a la quala Seguir",
+ "follow_explain": "Quan sigas a belún veyerás las suyas publicacions en a tuya pachina d'inicio."
+ },
+ "compose": {
+ "title": {
+ "new_post": "Nueva Publicación",
+ "new_reply": "Nueva Respuesta"
+ },
+ "media_selection": {
+ "camera": "Fer Foto",
+ "photo_library": "Galería de Fotos",
+ "browse": "Explorar"
+ },
+ "content_input_placeholder": "Escribe u apega lo que tiengas en mente",
+ "compose_action": "Publicar",
+ "replying_to_user": "en respuesta a %s",
+ "attachment": {
+ "photo": "foto",
+ "video": "video",
+ "attachment_broken": "Este %s ye roto y no puede\npuyar-se a Mastodon.",
+ "description_photo": "Describe la foto pa los usuarios con dificultat visual...",
+ "description_video": "Describe lo video pa los usuarios con dificultat visual...",
+ "load_failed": "Fallo de carga",
+ "upload_failed": "Fallo de carga",
+ "can_not_recognize_this_media_attachment": "No se puede reconocer este adchunto multimedia",
+ "attachment_too_large": "Adchunto masiau gran",
+ "compressing_state": "Comprimindo...",
+ "server_processing_state": "Lo servidor ye procesando..."
+ },
+ "poll": {
+ "duration_time": "Duración: %s",
+ "thirty_minutes": "30 minutos",
+ "one_hour": "1 Hora",
+ "six_hours": "6 Horas",
+ "one_day": "1 Día",
+ "three_days": "3 Días",
+ "seven_days": "7 Días",
+ "option_number": "Opción %ld",
+ "the_poll_is_invalid": "La enquesta ye invalida",
+ "the_poll_has_empty_option": "La enquesta tiene opcions vuedas"
+ },
+ "content_warning": {
+ "placeholder": "Escribe una alvertencia precisa aquí..."
+ },
+ "visibility": {
+ "public": "Publica",
+ "unlisted": "Sin listar",
+ "private": "Solo seguidores",
+ "direct": "Solo la chent que yo menciono"
+ },
+ "auto_complete": {
+ "space_to_add": "Espacio pa anyadir"
+ },
+ "accessibility": {
+ "append_attachment": "Anyadir Adchunto",
+ "append_poll": "Anyadir Enqüesta",
+ "remove_poll": "Eliminar Enqüesta",
+ "custom_emoji_picker": "Selector de Emojis Personalizaus",
+ "enable_content_warning": "Activar Alvertencia de Conteniu",
+ "disable_content_warning": "Desactivar Alvertencia de Conteniu",
+ "post_visibility_menu": "Menú de Visibilidat d'a Publicación",
+ "post_options": "Opcions d'o tut",
+ "posting_as": "Publicando como %s"
+ },
+ "keyboard": {
+ "discard_post": "Descartar Publicación",
+ "publish_post": "Publicar",
+ "toggle_poll": "Commutar Enqüesta",
+ "toggle_content_warning": "Commutar Alvertencia de Conteniu",
+ "append_attachment_entry": "Anyadir Adchunto - %s",
+ "select_visibility_entry": "Triar Visibilidat - %s"
+ }
+ },
+ "profile": {
+ "header": {
+ "follows_you": "Te sigue"
+ },
+ "dashboard": {
+ "my_posts": "posts",
+ "my_following": "following",
+ "my_followers": "followers",
+ "other_posts": "posts",
+ "other_following": "following",
+ "other_followers": "followers"
+ },
+ "fields": {
+ "joined": "Joined",
+ "add_row": "Anyadir Ringlera",
+ "placeholder": {
+ "label": "Nombre pa lo campo",
+ "content": "Conteniu"
+ },
+ "verified": {
+ "short": "Verificau en %s",
+ "long": "La propiedat d'este vinclo ha estau verificada lo %s"
+ }
+ },
+ "segmented_control": {
+ "posts": "Publicacions",
+ "replies": "Respuestas",
+ "posts_and_replies": "Publicacions y respuestas",
+ "media": "Multimedia",
+ "about": "Sobre"
+ },
+ "relationship_action_alert": {
+ "confirm_mute_user": {
+ "title": "Silenciar cuenta",
+ "message": "Confirmar pa silenciar %s"
+ },
+ "confirm_unmute_user": {
+ "title": "Deixar de Silenciar Cuenta",
+ "message": "Confirmar pa deixar de silenciar a %s"
+ },
+ "confirm_block_user": {
+ "title": "Blocar cuenta",
+ "message": "Confirmar pa blocar a %s"
+ },
+ "confirm_unblock_user": {
+ "title": "Desbloquiar cuenta",
+ "message": "Confirmar pa desbloquiar a %s"
+ },
+ "confirm_show_reblogs": {
+ "title": "Amostrar los reblogs",
+ "message": "Confimrar pa amostrar los reblogs"
+ },
+ "confirm_hide_reblogs": {
+ "title": "Amagar los reblogs",
+ "message": "Comfirmar pa amagar los reblogs"
+ }
+ },
+ "accessibility": {
+ "show_avatar_image": "Amostrar imachen d'o avatar",
+ "edit_avatar_image": "Editar imachen d'o avatar",
+ "show_banner_image": "Amostrar imachen de banner",
+ "double_tap_to_open_the_list": "Preta dos vegadas pa ubrir la lista"
+ }
+ },
+ "follower": {
+ "title": "seguidor",
+ "footer": "No s'amuestran los seguidores d'atros servidors."
+ },
+ "following": {
+ "title": "seguindo",
+ "footer": "No s'amuestran los seguius d'atros servidors."
+ },
+ "familiarFollowers": {
+ "title": "Seguidores que conoixes",
+ "followed_by_names": "Seguiu per %s"
+ },
+ "favorited_by": {
+ "title": "Feito favorito per"
+ },
+ "reblogged_by": {
+ "title": "Reblogueado per"
+ },
+ "search": {
+ "title": "Buscar",
+ "search_bar": {
+ "placeholder": "Buscar etiquetas y usuarios",
+ "cancel": "Cancelar"
+ },
+ "recommend": {
+ "button_text": "Veyer Totas",
+ "hash_tag": {
+ "title": "Tendencias en Mastodon",
+ "description": "Etiquetas que son recibindo pro atención",
+ "people_talking": "%s personas son charrando d'esto"
+ },
+ "accounts": {
+ "title": "Cuentas que talment quieras seguir",
+ "description": "Puede que faiga goyo seguir estas cuentas",
+ "follow": "Seguir"
+ }
+ },
+ "searching": {
+ "segment": {
+ "all": "Tot",
+ "people": "Chent",
+ "hashtags": "Etiquetas",
+ "posts": "Publicacions"
+ },
+ "empty_state": {
+ "no_results": "Sin resultaus"
+ },
+ "recent_search": "Busquedas recients",
+ "clear": "Borrar"
+ }
+ },
+ "discovery": {
+ "tabs": {
+ "posts": "Publicacions",
+ "hashtags": "Etiquetas",
+ "news": "Noticias",
+ "community": "Comunidat",
+ "for_you": "Pa Tu"
+ },
+ "intro": "Estas son las publicacions que son ganando tracción en a tuya rincón de Mastodon."
+ },
+ "favorite": {
+ "title": "Los tuyos Favoritos"
+ },
+ "notification": {
+ "title": {
+ "Everything": "Tot",
+ "Mentions": "Mencions"
+ },
+ "notification_description": {
+ "followed_you": "te siguió",
+ "favorited_your_post": "ha marcau como favorita la tuya publicación",
+ "reblogged_your_post": "reblogueó la tuya publicación",
+ "mentioned_you": "te mencionó",
+ "request_to_follow_you": "solicitó seguir-te",
+ "poll_has_ended": "enqüesta ha rematau"
+ },
+ "keyobard": {
+ "show_everything": "Amostrar Tot",
+ "show_mentions": "Amostrar Mencions"
+ },
+ "follow_request": {
+ "accept": "Acceptar",
+ "accepted": "Acceptau",
+ "reject": "refusar",
+ "rejected": "Refusau"
+ }
+ },
+ "thread": {
+ "back_title": "Publicación",
+ "title": "Publicación de %s"
+ },
+ "settings": {
+ "title": "Configuración",
+ "section": {
+ "appearance": {
+ "title": "Apariencia",
+ "automatic": "Automatica",
+ "light": "Siempre Clara",
+ "dark": "Siempre Fosca"
+ },
+ "look_and_feel": {
+ "title": "Apariencia",
+ "use_system": "Uso d'o sistema",
+ "really_dark": "Realment Fosco",
+ "sorta_dark": "Más u Menos Fosco",
+ "light": "Claro"
+ },
+ "notifications": {
+ "title": "Notificacions",
+ "favorites": "Marque como favorita la mía publicación",
+ "follows": "me siga",
+ "boosts": "Rebloguee la mía publicación",
+ "mentions": "me mencione",
+ "trigger": {
+ "anyone": "qualsequiera",
+ "follower": "un seguidor",
+ "follow": "qualsequiera que yo siga",
+ "noone": "dengún",
+ "title": "Recibir notificación quan"
+ }
+ },
+ "preference": {
+ "title": "Preferencias",
+ "true_black_dark_mode": "Modo fosco negro real",
+ "disable_avatar_animation": "Deshabilitar avatares animaus",
+ "disable_emoji_animation": "Deshabilitar emojis animaus",
+ "using_default_browser": "Usar navegador predeterminau pa ubrir los vinclos",
+ "open_links_in_mastodon": "Ubrir links en Mastodon"
+ },
+ "boring_zone": {
+ "title": "La Zona Aburrida",
+ "account_settings": "Configuración de Cuenta",
+ "terms": "Termins de Servicio",
+ "privacy": "Politica de Privacidat"
+ },
+ "spicy_zone": {
+ "title": "La Zona Picante",
+ "clear": "Borrar Caché de Multimedia",
+ "signout": "Zarrar Sesión"
+ }
+ },
+ "footer": {
+ "mastodon_description": "Mastodon ye software de codigo ubierto. Puetz informar d'errors en GitHub en %s (%s)"
+ },
+ "keyboard": {
+ "close_settings_window": "Zarrar Finestra de Configuración"
+ }
+ },
+ "report": {
+ "title_report": "Reportar",
+ "title": "Reportar %s",
+ "step1": "Paso 1 de 2",
+ "step2": "Paso 2 de 2",
+ "content1": "I hai belatra publicación que te fería goyo d'anyadir a lo reporte?",
+ "content2": "I hai bella cosa que los moderadors habrían de saber sobre este reporte?",
+ "report_sent_title": "Gracias per denunciar, estudiaremos esto.",
+ "send": "Ninviar Denuncia",
+ "skip_to_send": "Ninviar sin comentarios",
+ "text_placeholder": "Escribe u apega comentarios adicionals",
+ "reported": "DENUNCIAU",
+ "step_one": {
+ "step_1_of_4": "Paso 1 de 4",
+ "whats_wrong_with_this_post": "Qué i hai de malo con esta publicación?",
+ "whats_wrong_with_this_account": "Qué i hai de malo con esta cuenta?",
+ "whats_wrong_with_this_username": "Qué i hai de malo con %s?",
+ "select_the_best_match": "Tría la millor opción",
+ "i_dont_like_it": "No me fa goyo",
+ "it_is_not_something_you_want_to_see": "No ye bella cosa que quieras veyer",
+ "its_spam": "Ye spam",
+ "malicious_links_fake_engagement_or_repetetive_replies": "Vinclos maliciosos, compromisos falsos u respuestas repetitivas",
+ "it_violates_server_rules": "Viola las reglas d'o servidor",
+ "you_are_aware_that_it_breaks_specific_rules": "Yes conscient de que infrinche las normas especificas",
+ "its_something_else": "Ye bella cosa mas",
+ "the_issue_does_not_fit_into_other_categories": "Lo problema no encaixa en atras categorías"
+ },
+ "step_two": {
+ "step_2_of_4": "Paso 2 de 4",
+ "which_rules_are_being_violated": "Qué normas se son violando?",
+ "select_all_that_apply": "Tría totz los que correspondan",
+ "i_just_don’t_like_it": "Nomás no me fa goyo"
+ },
+ "step_three": {
+ "step_3_of_4": "Paso 3 de 4",
+ "are_there_any_posts_that_back_up_this_report": "I hai bella publicación que refirme este informe?",
+ "select_all_that_apply": "Tría totz los que correspondan"
+ },
+ "step_four": {
+ "step_4_of_4": "Paso 4 de 4",
+ "is_there_anything_else_we_should_know": "I hai bella cosa mas que habríanos de saber?"
+ },
+ "step_final": {
+ "dont_want_to_see_this": "No quiers veyer esto?",
+ "when_you_see_something_you_dont_like_on_mastodon_you_can_remove_the_person_from_your_experience.": "Quan veigas bella cosa que no te fa goyo en Mastodon, puetz sacar a la persona d'a tuya experiencia.",
+ "unfollow": "Deixar de seguir",
+ "unfollowed": "Ha deixau de seguir-te",
+ "unfollow_user": "Deixar de seguir a %s",
+ "mute_user": "Silenciar a %s",
+ "you_wont_see_their_posts_or_reblogs_in_your_home_feed_they_wont_know_they_ve_been_muted": "No veyerás las suyas publicacions u reblogueos en a tuya linia temporal. No sabrán que han estau silenciaus.",
+ "block_user": "Blocar a %s",
+ "they_will_no_longer_be_able_to_follow_or_see_your_posts_but_they_can_see_if_theyve_been_blocked": "Ya no podrán estar capaces de seguir-te u veyer las tuyas publicacions, pero pueden veyer si han estau blocaus.",
+ "while_we_review_this_you_can_take_action_against_user": "Mientres revisamos esto, puetz prener medidas contra %s"
+ }
+ },
+ "preview": {
+ "keyboard": {
+ "close_preview": "Zarrar Previsualización",
+ "show_next": "Amostrar Siguient",
+ "show_previous": "Amostrar Anterior"
+ }
+ },
+ "account_list": {
+ "tab_bar_hint": "Perfil triau actualment: %s. Fe un doble toque y mantiene pretau pa amostrar lo selector de cuentas",
+ "dismiss_account_switcher": "Descartar lo selector de cuentas",
+ "add_account": "Anyadir cuenta"
+ },
+ "wizard": {
+ "new_in_mastodon": "Nuevo en Mastodon",
+ "multiple_account_switch_intro_description": "Cambie entre quantas cuentas mantenendo presionado lo botón de perfil.",
+ "accessibility_hint": "Fe doble toque pa descartar este asistent"
+ },
+ "bookmark": {
+ "title": "Marcapachinas"
+ },
+ "followed_tags": {
+ "title": "Followed Tags",
+ "header": {
+ "posts": "posts",
+ "participants": "participants",
+ "posts_today": "posts today"
+ },
+ "actions": {
+ "follow": "Follow",
+ "unfollow": "Unfollow"
+ }
+ }
+ }
+}
diff --git a/Localization/StringsConvertor/input/an.lproj/ios-infoPlist.json b/Localization/StringsConvertor/input/an.lproj/ios-infoPlist.json
new file mode 100644
index 000000000..a493d53d9
--- /dev/null
+++ b/Localization/StringsConvertor/input/an.lproj/ios-infoPlist.json
@@ -0,0 +1,6 @@
+{
+ "NSCameraUsageDescription": "S'usa pa quitar fotos pa las publicacions",
+ "NSPhotoLibraryAddUsageDescription": "S'usa pa alzar fotos en a Galería de Fotos",
+ "NewPostShortcutItemTitle": "Nueva Publicación",
+ "SearchShortcutItemTitle": "Buscar"
+}
diff --git a/Localization/StringsConvertor/input/ar.lproj/Localizable.stringsdict b/Localization/StringsConvertor/input/ar.lproj/Localizable.stringsdict
index 862d98184..35727c0d6 100644
--- a/Localization/StringsConvertor/input/ar.lproj/Localizable.stringsdict
+++ b/Localization/StringsConvertor/input/ar.lproj/Localizable.stringsdict
@@ -74,6 +74,30 @@
%ld حَرف
+ a11y.plural.count.characters_left
+
+ NSStringLocalizedFormatKey
+ يتبقى %#@character_count@
+ character_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ zero
+ لَا حَرف
+ one
+ حَرفٌ واحِد
+ two
+ حَرفانِ اِثنان
+ few
+ %ld أحرُف
+ many
+ %ld حَرفًا
+ other
+ %ld حَرف
+
+
plural.count.followed_by_and_mutual
NSStringLocalizedFormatKey
diff --git a/Localization/StringsConvertor/input/ar.lproj/app.json b/Localization/StringsConvertor/input/ar.lproj/app.json
index 4c5ac4c8b..c27cf8b70 100644
--- a/Localization/StringsConvertor/input/ar.lproj/app.json
+++ b/Localization/StringsConvertor/input/ar.lproj/app.json
@@ -51,6 +51,11 @@
"clean_cache": {
"title": "مَحوُ ذاكِرَةِ التَّخزينِ المُؤقَّت",
"message": "مُحِيَ ما مَساحَتُهُ %s مِن ذاكِرَةِ التَّخزينِ المُؤقَّت بِنجاح."
+ },
+ "translation_failed": {
+ "title": "مُلاحظة",
+ "message": "Translation failed. Maybe the administrator has not enabled translations on this server or this server is running an older version of Mastodon where translations are not yet supported.",
+ "button": "حسنًا"
}
},
"controls": {
@@ -74,10 +79,11 @@
"take_photo": "اِلتِقاطُ صُورَة",
"save_photo": "حفظ الصورة",
"copy_photo": "نسخ الصورة",
- "sign_in": "تسجيل الدخول",
- "sign_up": "إنشاء حِساب",
+ "sign_in": "تسجيلُ الدخول",
+ "sign_up": "إنشاءُ حِساب",
"see_more": "عرض المزيد",
"preview": "مُعاينة",
+ "copy": "نَسخ",
"share": "المُشارك",
"share_user": "مُشارَكَةُ %s",
"share_post": "مشارك المنشور",
@@ -91,12 +97,16 @@
"block_domain": "حظر %s",
"unblock_domain": "رفع الحظر عن %s",
"settings": "الإعدادات",
- "delete": "حذف"
+ "delete": "حذف",
+ "translate_post": {
+ "title": "الترجَمَة مِن %s",
+ "unknown_language": "غير مَعرُوفة"
+ }
},
"tabs": {
"home": "الرَّئِيسَة",
- "search": "البَحث",
- "notification": "الإشعارات",
+ "search_and_explore": "البَحث وَالاِستِكشاف",
+ "notifications": "الإشعارات",
"profile": "المِلَفُّ التَّعريفِيّ"
},
"keyboard": {
@@ -132,6 +142,8 @@
"sensitive_content": "مُحتَوى حَسَّاس",
"media_content_warning": "اُنقُر لِلكَشف",
"tap_to_reveal": "اُنقُر لِلكَشف",
+ "load_embed": "تحميل المُضمَن",
+ "link_via_user": "%s via %s",
"poll": {
"vote": "صَوِّت",
"closed": "انتهى"
@@ -153,6 +165,7 @@
"show_image": "أظْهِرِ الصُّورَة",
"show_gif": "أظْهِر GIF",
"show_video_player": "أظْهِر مُشَغِّلَ المَقاطِعِ المَرئِيَّة",
+ "share_link_in_post": "مُشارَكَة الرابِط فِي مَنشور",
"tap_then_hold_to_show_menu": "اُنقُر مُطَوَّلًا لِإظْهَارِ القائِمَة"
},
"tag": {
@@ -168,6 +181,12 @@
"private": "فَقَطْ مُتابِعينَهُم مَن يُمكِنُهُم رُؤيَةُ هَذَا المَنشُور.",
"private_from_me": "فَقَطْ مُتابِعيني أنَا مَن يُمكِنُهُم رُؤيَةُ هَذَا المَنشُور.",
"direct": "المُستخدمِونَ المُشارِ إليهم فَقَطْ مَن يُمكِنُهُم رُؤيَةُ هَذَا المَنشُور."
+ },
+ "translation": {
+ "translated_from": "الترجَمَة مِن %s بِاستِخدَام %s",
+ "unknown_language": "غير مَعرُوفة",
+ "unknown_provider": "غير مَعرُوف",
+ "show_original": "Shown Original"
}
},
"friendship": {
@@ -218,10 +237,16 @@
"get_started": "ابدأ الآن",
"log_in": "تسجيلُ الدخول"
},
+ "login": {
+ "title": "مَرحَبًا بِكَ مُجَدَّدًا",
+ "subtitle": "سَجِّل دُخولَكَ إلى الخادِم الَّذي أنشأتَ حِسابَكَ فيه.",
+ "server_search_field": {
+ "placeholder": "أدخِل عُنوانَ URL أو اِبحَث عَنِ الخادِمِ الخاصّ بِك"
+ }
+ },
"server_picker": {
"title": "اِختر خادِم،\nأيًّا مِنهُم.",
- "subtitle": "اختر مجتمعًا بناءً على اهتماماتك، منطقتك أو يمكنك حتى اختيارُ مجتمعٍ ذي غرضٍ عام.",
- "subtitle_extend": "اختر مجتمعًا بناءً على اهتماماتك، منطقتك أو يمكنك حتى اختيارُ مجتمعٍ ذي غرضٍ عام. تُشغَّل جميعُ المجتمعِ مِن قِبَلِ مُنظمَةٍ أو فردٍ مُستقلٍ تمامًا.",
+ "subtitle": "اِختر خادمًا بناءً على منطقتك، اِهتماماتك أو يُمكنك حتى اِختيارُ مجتمعٍ ذِي غرضٍ عام. بِإمكانِكَ الدردشة مع أي شخص على مَاستودُون، بغض النظر عن الخادم الخاصة بك.",
"button": {
"category": {
"all": "الكُل",
@@ -248,8 +273,7 @@
"category": "الفئة"
},
"input": {
- "placeholder": "اِبحَث عن خادِم أو انضم إلى آخر خاص بك...",
- "search_servers_or_enter_url": "اِبحَث فِي الخَوادِم أو أدخِل رابِط"
+ "search_servers_or_enter_url": "اِبحث عَن مُجتَمَعَات أو أدخِل عُنوانَ URL"
},
"empty_state": {
"finding_servers": "يجري إيجاد خوادم متوفِّرَة...",
@@ -383,10 +407,12 @@
"attachment_broken": "هذا ال%s مُعطَّل\nويتعذَّرُ رفعُه إلى ماستودون.",
"description_photo": "صِف الصورة للمَكفوفين...",
"description_video": "صِف المقطع المرئي للمَكفوفين...",
- "load_failed": "Load Failed",
- "upload_failed": "Upload Failed",
- "can_not_recognize_this_media_attachment": "Can not regonize this media attachment",
- "attachment_too_large": "Attachment too large"
+ "load_failed": "فَشَلَ التَّحميل",
+ "upload_failed": "فَشَلَ الرَّفع",
+ "can_not_recognize_this_media_attachment": "يتعذَّرُ التعرُّفُ على وسائِطِ هذا المُرفَق",
+ "attachment_too_large": "المُرفَق كَبيرٌ جِدًّا",
+ "compressing_state": "يجري الضغط...",
+ "server_processing_state": "مُعالجة الخادم جارِيَة..."
},
"poll": {
"duration_time": "المُدَّة: %s",
@@ -396,7 +422,9 @@
"one_day": "يومٌ واحِد",
"three_days": "ثلاثةُ أيام",
"seven_days": "سبعةُ أيام",
- "option_number": "الخيار %ld"
+ "option_number": "الخيار %ld",
+ "the_poll_is_invalid": "الاِستِطلاعُ غيرُ صالِح",
+ "the_poll_has_empty_option": "يوجَدُ خِيارٌ فارِغٌ فِي الاِستِطلاع"
},
"content_warning": {
"placeholder": "اكتب تَحذيرًا دَقيقًا هُنا..."
@@ -417,7 +445,9 @@
"custom_emoji_picker": "منتقي الرموز التعبيرية المُخصَّص",
"enable_content_warning": "تفعيل تحذير المُحتَوى",
"disable_content_warning": "تعطيل تحذير المُحتَوى",
- "post_visibility_menu": "قائمة ظهور المنشور"
+ "post_visibility_menu": "قائمة ظهور المنشور",
+ "post_options": "خياراتُ المَنشور",
+ "posting_as": "نَشر كَـ %s"
},
"keyboard": {
"discard_post": "تجاهُل المنشور",
@@ -433,15 +463,23 @@
"follows_you": "يُتابِعُك"
},
"dashboard": {
- "posts": "مَنشورات",
- "following": "مُتابَع",
- "followers": "مُتابِع"
+ "my_posts": "مَنشورات",
+ "my_following": "مُتابَعُون",
+ "my_followers": "مُتابِعُون",
+ "other_posts": "مَنشورات",
+ "other_following": "مُتابَعُون",
+ "other_followers": "مُتابِعُون"
},
"fields": {
+ "joined": "Joined",
"add_row": "إضافة صف",
"placeholder": {
"label": "التسمية",
"content": "المُحتَوى"
+ },
+ "verified": {
+ "short": "تمَّ التَّحقق بِتاريخ %s",
+ "long": "تمَّ التَّحقق مِن مِلكية هذا الرابِطِ بِتاريخ %s"
}
},
"segmented_control": {
@@ -707,6 +745,18 @@
},
"bookmark": {
"title": "العَلاماتُ المَرجعيَّة"
+ },
+ "followed_tags": {
+ "title": "وُسُومُ المُتابَع",
+ "header": {
+ "posts": "مَنشورات",
+ "participants": "المُشارِكُون",
+ "posts_today": "مَنشوراتُ اليَوم"
+ },
+ "actions": {
+ "follow": "مُتابَعَة",
+ "unfollow": "إلغاءُ المُتابَعَة"
+ }
}
}
}
diff --git a/Localization/StringsConvertor/input/ca.lproj/Localizable.stringsdict b/Localization/StringsConvertor/input/ca.lproj/Localizable.stringsdict
index cc28edbc6..615fd0c48 100644
--- a/Localization/StringsConvertor/input/ca.lproj/Localizable.stringsdict
+++ b/Localization/StringsConvertor/input/ca.lproj/Localizable.stringsdict
@@ -50,6 +50,22 @@
%ld caràcters
+ a11y.plural.count.characters_left
+
+ NSStringLocalizedFormatKey
+ resten %#@character_count@
+ character_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 caràcter
+ other
+ %ld caràcters
+
+
plural.count.followed_by_and_mutual
NSStringLocalizedFormatKey
@@ -392,7 +408,7 @@
NSStringFormatValueTypeKey
ld
one
- fa 1 día
+ fa 1 dia
other
fa %ld dies
@@ -408,7 +424,7 @@
NSStringFormatValueTypeKey
ld
one
- fa 1h
+ fa 1 h
other
fa %ld hores
@@ -424,9 +440,9 @@
NSStringFormatValueTypeKey
ld
one
- fa 1 minut
+ fa 1 min
other
- fa %ld minuts
+ fa %ld min
date.second.ago.abbr
@@ -440,9 +456,9 @@
NSStringFormatValueTypeKey
ld
one
- fa 1 segon
+ fa 1 s
other
- fa %ld segons
+ fa %ld s
diff --git a/Localization/StringsConvertor/input/ca.lproj/app.json b/Localization/StringsConvertor/input/ca.lproj/app.json
index 7164d1d12..3222ec984 100644
--- a/Localization/StringsConvertor/input/ca.lproj/app.json
+++ b/Localization/StringsConvertor/input/ca.lproj/app.json
@@ -2,55 +2,60 @@
"common": {
"alerts": {
"common": {
- "please_try_again": "Si us plau intenta-ho de nou.",
- "please_try_again_later": "Si us plau, prova-ho més tard."
+ "please_try_again": "Torna-ho a provar.",
+ "please_try_again_later": "Prova-ho més tard."
},
"sign_up_failure": {
"title": "Error en el registre"
},
"server_error": {
- "title": "Error del Servidor"
+ "title": "Error del servidor"
},
"vote_failure": {
- "title": "Error del Vot",
+ "title": "Error en votar",
"poll_ended": "L'enquesta ha finalitzat"
},
"discard_post_content": {
"title": "Descarta l'esborrany",
- "message": "Confirma per a descartar el contingut de la publicació composta."
+ "message": "Confirma per a descartar el contingut de la publicació."
},
"publish_post_failure": {
- "title": "Error de Publicació",
- "message": "No s'ha pogut enviar la publicació.\nComprova la teva connexió a Internet.",
+ "title": "Error en publicar",
+ "message": "No s'ha pogut enviar la publicació.\nComprova la connexió a Internet.",
"attachments_message": {
"video_attach_with_photo": "No es pot adjuntar un vídeo a una publicació que ja contingui imatges.",
- "more_than_one_video": "No pots adjuntar més d'un vídeo."
+ "more_than_one_video": "No es pot adjuntar més d'un vídeo."
}
},
"edit_profile_failure": {
- "title": "Error al Editar el Perfil",
- "message": "No es pot editar el perfil. Si us plau torna-ho a provar."
+ "title": "Error en editar el perfil",
+ "message": "No es pot editar el perfil. Torna-ho a provar."
},
"sign_out": {
- "title": "Tancar Sessió",
- "message": "Estàs segur que vols tancar la sessió?",
- "confirm": "Tancar Sessió"
+ "title": "Tanca la sessió",
+ "message": "Segur que vols tancar la sessió?",
+ "confirm": "Tanca la sessió"
},
"block_domain": {
- "title": "Estàs segur, realment segur que vols bloquejar totalment %s? En la majoria dels casos bloquejar o silenciar uns pocs objectius és suficient i preferible. No veureu contingut d’aquest domini i se suprimirà qualsevol dels vostres seguidors d’aquest domini.",
- "block_entire_domain": "Bloquejar Domini"
+ "title": "Estàs totalment segur que vols bloquejar per complet %s? En la majoria dels casos bloquejar o silenciar uns pocs objectius és suficient i preferible. No veureu contingut d’aquest domini i se suprimirà qualsevol dels vostres seguidors d’aquest domini.",
+ "block_entire_domain": "Bloca el domini"
},
"save_photo_failure": {
- "title": "Error al Desar la Foto",
- "message": "Activa el permís d'accés a la biblioteca de fotos per desar-la."
+ "title": "Error en desar la foto",
+ "message": "Activa el permís d'accés a la biblioteca de fotos per a desar-la."
},
"delete_post": {
- "title": "Esborrar Publicació",
- "message": "Estàs segur que vols suprimir aquesta publicació?"
+ "title": "Eliminar la publicació",
+ "message": "Segur que vols eliminar aquesta publicació?"
},
"clean_cache": {
"title": "Neteja la memòria cau",
"message": "S'ha netejat correctament la memòria cau de %s."
+ },
+ "translation_failed": {
+ "title": "Nota",
+ "message": "La traducció ha fallat. Potser l'administrador d'aquest servidor no ha activat les traduccions o està executant una versió vella de Mastodon on les traduccions encara no eren suportades.",
+ "button": "D'acord"
}
},
"controls": {
@@ -67,20 +72,21 @@
"done": "Fet",
"confirm": "Confirma",
"continue": "Continua",
- "compose": "Composa",
+ "compose": "Redacta",
"cancel": "Cancel·la",
"discard": "Descarta",
"try_again": "Torna a provar",
"take_photo": "Fes una foto",
"save_photo": "Desa la foto",
"copy_photo": "Copia la foto",
- "sign_in": "Iniciar sessió",
- "sign_up": "Registre",
- "see_more": "Veure més",
+ "sign_in": "Inicia sessió",
+ "sign_up": "Crea un compte",
+ "see_more": "Mostra'n més",
"preview": "Vista prèvia",
+ "copy": "Copia",
"share": "Comparteix",
- "share_user": "Compartir %s",
- "share_post": "Compartir Publicació",
+ "share_user": "Comparteix %s",
+ "share_post": "Comparteix la publicació",
"open_in_safari": "Obrir a Safari",
"open_in_browser": "Obre al navegador",
"find_people": "Busca persones a seguir",
@@ -91,12 +97,16 @@
"block_domain": "Bloqueja %s",
"unblock_domain": "Desbloqueja %s",
"settings": "Configuració",
- "delete": "Suprimeix"
+ "delete": "Suprimeix",
+ "translate_post": {
+ "title": "Traduït del %s",
+ "unknown_language": "Desconegut"
+ }
},
"tabs": {
"home": "Inici",
- "search": "Cerca",
- "notification": "Notificació",
+ "search_and_explore": "Cerca i Explora",
+ "notifications": "Notificacions",
"profile": "Perfil"
},
"keyboard": {
@@ -110,9 +120,9 @@
"previous_status": "Publicació anterior",
"next_status": "Publicació següent",
"open_status": "Obre la publicació",
- "open_author_profile": "Obre el Perfil de l'Autor",
- "open_reblogger_profile": "Obre el Perfil del Impulsor",
- "reply_status": "Respon a la Publicació",
+ "open_author_profile": "Obre el perfil de l'autor",
+ "open_reblogger_profile": "Obre el perfil de l'impuls",
+ "reply_status": "Respon a la publicació",
"toggle_reblog": "Commuta l'Impuls de la Publicació",
"toggle_favorite": "Commuta el Favorit de la Publicació",
"toggle_content_warning": "Commuta l'Avís de Contingut",
@@ -132,27 +142,30 @@
"sensitive_content": "Contingut sensible",
"media_content_warning": "Toca qualsevol lloc per a mostrar",
"tap_to_reveal": "Toca per a mostrar",
+ "load_embed": "Carregar incrustat",
+ "link_via_user": "%s través de %s",
"poll": {
"vote": "Vota",
"closed": "Finalitzada"
},
"meta_entity": {
"url": "Enllaç: %s",
- "hashtag": "Etiqueta %s",
- "mention": "Mostra el Perfil: %s",
+ "hashtag": "Etiqueta: %s",
+ "mention": "Mostra el perfil: %s",
"email": "Correu electrònic: %s"
},
"actions": {
"reply": "Respon",
- "reblog": "Impuls",
- "unreblog": "Desfer l'impuls",
+ "reblog": "Impulsa",
+ "unreblog": "Desfés l'impuls",
"favorite": "Favorit",
- "unfavorite": "Desfer Favorit",
+ "unfavorite": "Desfés el favorit",
"menu": "Menú",
"hide": "Amaga",
"show_image": "Mostra la imatge",
"show_gif": "Mostra el GIF",
"show_video_player": "Mostra el reproductor de vídeo",
+ "share_link_in_post": "Compartir l'Enllaç en el Tut",
"tap_then_hold_to_show_menu": "Toca i manté per a veure el menú"
},
"tag": {
@@ -161,26 +174,32 @@
"link": "Enllaç",
"hashtag": "Etiqueta",
"email": "Correu electrònic",
- "emoji": "Emoji"
+ "emoji": "Emojis"
},
"visibility": {
- "unlisted": "Tothom pot veure aquesta publicació però no es mostra en la línia de temps pública.",
+ "unlisted": "Tothom pot veure aquesta publicació, però no es mostra en la línia de temps pública.",
"private": "Només els seus seguidors poden veure aquesta publicació.",
"private_from_me": "Només els meus seguidors poden veure aquesta publicació.",
"direct": "Només l'usuari mencionat pot veure aquesta publicació."
+ },
+ "translation": {
+ "translated_from": "Traduït del %s fent servir %s",
+ "unknown_language": "Desconegut",
+ "unknown_provider": "Desconegut",
+ "show_original": "Mostra l'original"
}
},
"friendship": {
"follow": "Segueix",
"following": "Seguint",
- "request": "Petició",
+ "request": "Sol·licitud",
"pending": "Pendent",
- "block": "Bloqueja",
- "block_user": "Bloqueja %s",
- "block_domain": "Bloqueja %s",
- "unblock": "Desbloqueja",
- "unblock_user": "Desbloqueja %s",
- "blocked": "Bloquejat",
+ "block": "Bloca",
+ "block_user": "Bloca %s",
+ "block_domain": "Bloca %s",
+ "unblock": "Desbloca",
+ "unblock_user": "Desbloca %s",
+ "blocked": "Blocat",
"mute": "Silencia",
"mute_user": "Silencia %s",
"unmute": "Deixa de silenciar",
@@ -196,16 +215,16 @@
"now": "Ara"
},
"loader": {
- "load_missing_posts": "Carrega les publicacions faltants",
- "loading_missing_posts": "Carregant les publicacions faltants...",
+ "load_missing_posts": "Carrega les publicacions restants",
+ "loading_missing_posts": "Carregant les publicacions restants...",
"show_more_replies": "Mostra més respostes"
},
"header": {
"no_status_found": "No s'ha trobat cap publicació",
- "blocking_warning": "No pots veure el perfil d'aquest usuari\n fins que el desbloquegis.\nEl teu perfil els sembla així.",
- "user_blocking_warning": "No pots veure el perfil de %s\n fins que el desbloquegis.\nEl teu perfil els sembla així.",
- "blocked_warning": "No pots veure el perfil d'aquest usuari\nfins que et desbloquegi.",
- "user_blocked_warning": "No pots veure el perfil de %s\n fins que et desbloquegi.",
+ "blocking_warning": "No pots veure el perfil d'aquest usuari\nfins que el desbloquis.\nEl teu perfil els sembla així.",
+ "user_blocking_warning": "No pots veure el perfil de %s\nfins que el desbloquis.\nEl teu perfil els sembla així.",
+ "blocked_warning": "No pots veure el perfil d'aquest usuari\nfins que et desbloqui.",
+ "user_blocked_warning": "No pots veure el perfil de %s\n fins que et desbloqui.",
"suspended_warning": "Aquest usuari ha estat suspès.",
"user_suspended_warning": "El compte de %s ha estat suspès."
}
@@ -218,10 +237,16 @@
"get_started": "Comença",
"log_in": "Inicia sessió"
},
+ "login": {
+ "title": "Ben tornat",
+ "subtitle": "T'inicia sessió en el servidor on has creat el teu compte.",
+ "server_search_field": {
+ "placeholder": "Insereix la URL o cerca el teu servidor"
+ }
+ },
"server_picker": {
- "title": "Mastodon està fet d'usuaris en diferents comunitats.",
- "subtitle": "Tria una comunitat segons els teus interessos, regió o una de propòsit general.",
- "subtitle_extend": "Tria una comunitat segons els teus interessos, regió o una de propòsit general. Cada comunitat és operada per una organització totalment independent o individualment.",
+ "title": "Mastodon està fet d'usuaris en diferents servidors.",
+ "subtitle": "Tria un servidor en funció de la teva regió, interessos o un de propòsit general. Seguiràs podent connectar amb tothom a Mastodon, independentment del servidor.",
"button": {
"category": {
"all": "Totes",
@@ -248,8 +273,7 @@
"category": "CATEGORIA"
},
"input": {
- "placeholder": "Cerca servidors",
- "search_servers_or_enter_url": "Cerca servidors o introdueix l'enllaç"
+ "search_servers_or_enter_url": "Cerca comunitats o introdueix l'URL"
},
"empty_state": {
"finding_servers": "Cercant els servidors disponibles...",
@@ -362,11 +386,11 @@
},
"suggestion_account": {
"title": "Cerca Persones a Seguir",
- "follow_explain": "Quan segueixes algú, veuràs les seves publicacions a Inici."
+ "follow_explain": "Quan segueixes algú, veuràs els seus tuts a Inici."
},
"compose": {
"title": {
- "new_post": "Nova publicació",
+ "new_post": "Nou Tut",
"new_reply": "Nova Resposta"
},
"media_selection": {
@@ -381,12 +405,14 @@
"photo": "foto",
"video": "vídeo",
"attachment_broken": "Aquest %s està trencat i no pot ser\ncarregat a Mastodon.",
- "description_photo": "Descriu la foto per als disminuïts visuals...",
- "description_video": "Descriu el vídeo per als disminuïts visuals...",
+ "description_photo": "Descriu la foto per a les persones amb diversitat funcional...",
+ "description_video": "Descriu el vídeo per a les persones amb diversitat funcional...",
"load_failed": "Ha fallat la càrrega",
"upload_failed": "Pujada fallida",
- "can_not_recognize_this_media_attachment": "No es pot reconèixer l'adjunt multimèdia",
- "attachment_too_large": "El fitxer adjunt és massa gran"
+ "can_not_recognize_this_media_attachment": "No es pot reconèixer aquest adjunt multimèdia",
+ "attachment_too_large": "El fitxer adjunt és massa gran",
+ "compressing_state": "Comprimint...",
+ "server_processing_state": "Servidor processant..."
},
"poll": {
"duration_time": "Durada: %s",
@@ -396,7 +422,9 @@
"one_day": "1 Dia",
"three_days": "3 Dies",
"seven_days": "7 Dies",
- "option_number": "Opció %ld"
+ "option_number": "Opció %ld",
+ "the_poll_is_invalid": "L'enquesta no és vàlida",
+ "the_poll_has_empty_option": "L'enquesta té una opció buida"
},
"content_warning": {
"placeholder": "Escriu un advertiment precís aquí..."
@@ -417,11 +445,13 @@
"custom_emoji_picker": "Selector d'Emoji Personalitzat",
"enable_content_warning": "Activa l'Avís de Contingut",
"disable_content_warning": "Desactiva l'Avís de Contingut",
- "post_visibility_menu": "Menú de Visibilitat de Publicació"
+ "post_visibility_menu": "Menú de Visibilitat del Tut",
+ "post_options": "Opcions del Tut",
+ "posting_as": "Publicant com a %s"
},
"keyboard": {
- "discard_post": "Descarta la Publicació",
- "publish_post": "Envia la Publicació",
+ "discard_post": "Descarta el Tut",
+ "publish_post": "Envia el Tut",
"toggle_poll": "Commuta l'enquesta",
"toggle_content_warning": "Commuta l'Avís de Contingut",
"append_attachment_entry": "Afegeix Adjunt - %s",
@@ -433,21 +463,29 @@
"follows_you": "Et segueix"
},
"dashboard": {
- "posts": "publicacions",
- "following": "seguint",
- "followers": "seguidors"
+ "my_posts": "tuts",
+ "my_following": "seguint",
+ "my_followers": "seguidors",
+ "other_posts": "tuts",
+ "other_following": "seguint",
+ "other_followers": "seguidors"
},
"fields": {
+ "joined": "S'hi va unir",
"add_row": "Afegeix fila",
"placeholder": {
"label": "Etiqueta",
"content": "Contingut"
+ },
+ "verified": {
+ "short": "Verificat a %s",
+ "long": "La propietat d'aquest enllaç es va verificar el dia %s"
}
},
"segmented_control": {
- "posts": "Publicacions",
+ "posts": "Tuts",
"replies": "Respostes",
- "posts_and_replies": "Publicacions i Respostes",
+ "posts_and_replies": "Tuts i Respostes",
"media": "Mèdia",
"about": "Quant a"
},
@@ -461,12 +499,12 @@
"message": "Confirma deixar de silenciar a %s"
},
"confirm_block_user": {
- "title": "Bloqueja el Compte",
- "message": "Confirma per a bloquejar %s"
+ "title": "Bloca el Compte",
+ "message": "Confirma per a blocar %s"
},
"confirm_unblock_user": {
- "title": "Desbloqueja el Compte",
- "message": "Confirma per a desbloquejar %s"
+ "title": "Desbloca el Compte",
+ "message": "Confirma per a desblocar %s"
},
"confirm_show_reblogs": {
"title": "Mostra els Impulsos",
@@ -526,7 +564,7 @@
"all": "Tots",
"people": "Gent",
"hashtags": "Etiquetes",
- "posts": "Publicacions"
+ "posts": "Tuts"
},
"empty_state": {
"no_results": "No hi ha resultats"
@@ -537,13 +575,13 @@
},
"discovery": {
"tabs": {
- "posts": "Publicacions",
+ "posts": "Tuts",
"hashtags": "Etiquetes",
"news": "Notícies",
"community": "Comunitat",
"for_you": "Per a tu"
},
- "intro": "Aquestes son les publicacions que criden l'atenció en el teu racó de Mastodon."
+ "intro": "Aquests son els tuts que criden l'atenció en el teu racó de Mastodon."
},
"favorite": {
"title": "Els teus Favorits"
@@ -573,8 +611,8 @@
}
},
"thread": {
- "back_title": "Publicació",
- "title": "Publicació de %s"
+ "back_title": "Tut",
+ "title": "Tut de %s"
},
"settings": {
"title": "Configuració",
@@ -594,9 +632,9 @@
},
"notifications": {
"title": "Notificacions",
- "favorites": "Ha afavorit el meu estat",
+ "favorites": "Ha afavorit el meu tut",
"follows": "Em segueix",
- "boosts": "Ha impulsat el meu estat",
+ "boosts": "Ha impulsat el meu tut",
"mentions": "M'ha mencionat",
"trigger": {
"anyone": "algú",
@@ -638,7 +676,7 @@
"title": "Informa sobre %s",
"step1": "Pas 1 de 2",
"step2": "Pas 2 de 2",
- "content1": "Hi ha alguna altre publicació que vulguis afegir a l'informe?",
+ "content1": "Hi ha algun altre tut que vulguis afegir a l'informe?",
"content2": "Hi ha alguna cosa que els moderadors hagin de saber sobre aquest informe?",
"report_sent_title": "Gràcies per informar, ho investigarem.",
"send": "Envia Informe",
@@ -647,7 +685,7 @@
"reported": "REPORTAT",
"step_one": {
"step_1_of_4": "Pas 1 de 4",
- "whats_wrong_with_this_post": "Quin és el problema amb aquesta publicació?",
+ "whats_wrong_with_this_post": "Quin és el problema amb aquest tut?",
"whats_wrong_with_this_account": "Quin és el problema amb aquest compte?",
"whats_wrong_with_this_username": "Quin és el problema amb %s?",
"select_the_best_match": "Selecciona la millor coincidència",
@@ -668,7 +706,7 @@
},
"step_three": {
"step_3_of_4": "Pas 3 de 4",
- "are_there_any_posts_that_back_up_this_report": "Hi ha alguna publicació que recolzi aquest informe?",
+ "are_there_any_posts_that_back_up_this_report": "Hi ha alguns tuts que recolzin aquest informe?",
"select_all_that_apply": "Selecciona tot el que correspongui"
},
"step_four": {
@@ -677,14 +715,14 @@
},
"step_final": {
"dont_want_to_see_this": "No vols veure això?",
- "when_you_see_something_you_dont_like_on_mastodon_you_can_remove_the_person_from_your_experience.": "Quan veus alguna cosa que no t'agrada a Mastodon, pots eliminar la persona de la vostra experiència.",
+ "when_you_see_something_you_dont_like_on_mastodon_you_can_remove_the_person_from_your_experience.": "Quan veus alguna cosa que no t'agrada a Mastodon, pots eliminar la persona de la teva experiència.",
"unfollow": "Deixa de seguir",
"unfollowed": "S'ha deixat de seguir",
"unfollow_user": "Deixa de seguir %s",
"mute_user": "Silencia %s",
- "you_wont_see_their_posts_or_reblogs_in_your_home_feed_they_wont_know_they_ve_been_muted": "No veuràs les seves publicacions o impulsos a la teva línia de temps personal. No sabran que han estat silenciats.",
+ "you_wont_see_their_posts_or_reblogs_in_your_home_feed_they_wont_know_they_ve_been_muted": "No veuràs els seus tuts o impulsos a la teva línia de temps personal. No sabran que han estat silenciats.",
"block_user": "Bloca %s",
- "they_will_no_longer_be_able_to_follow_or_see_your_posts_but_they_can_see_if_theyve_been_blocked": "Ja no podran seguir ni veure les teves publicacions, però poden veure si han estat bloquejats.",
+ "they_will_no_longer_be_able_to_follow_or_see_your_posts_but_they_can_see_if_theyve_been_blocked": "Ja no podran seguir ni veure els teus tus, però poden veure si han estat blocats.",
"while_we_review_this_you_can_take_action_against_user": "Mentre ho revisem, pots prendre mesures contra %s"
}
},
@@ -707,6 +745,18 @@
},
"bookmark": {
"title": "Marcadors"
+ },
+ "followed_tags": {
+ "title": "Etiquetes seguides",
+ "header": {
+ "posts": "tuts",
+ "participants": "participants",
+ "posts_today": "tuts d'avui"
+ },
+ "actions": {
+ "follow": "Segueix",
+ "unfollow": "Deixa de seguir"
+ }
}
}
}
diff --git a/Localization/StringsConvertor/input/ckb.lproj/Localizable.stringsdict b/Localization/StringsConvertor/input/ckb.lproj/Localizable.stringsdict
index 001a8a608..8116226ec 100644
--- a/Localization/StringsConvertor/input/ckb.lproj/Localizable.stringsdict
+++ b/Localization/StringsConvertor/input/ckb.lproj/Localizable.stringsdict
@@ -50,6 +50,22 @@
%ld نووسە
+ a11y.plural.count.characters_left
+
+ NSStringLocalizedFormatKey
+ %#@character_count@ left
+ character_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 character
+ other
+ %ld characters
+
+
plural.count.followed_by_and_mutual
NSStringLocalizedFormatKey
diff --git a/Localization/StringsConvertor/input/ckb.lproj/app.json b/Localization/StringsConvertor/input/ckb.lproj/app.json
index 25452f38b..e7222d645 100644
--- a/Localization/StringsConvertor/input/ckb.lproj/app.json
+++ b/Localization/StringsConvertor/input/ckb.lproj/app.json
@@ -51,6 +51,11 @@
"clean_cache": {
"title": "بیرگە پاک بکەوە",
"message": "سەرکەوتووانە بیرگەی %s پاک کرایەوە."
+ },
+ "translation_failed": {
+ "title": "Note",
+ "message": "Translation failed. Maybe the administrator has not enabled translations on this server or this server is running an older version of Mastodon where translations are not yet supported.",
+ "button": "OK"
}
},
"controls": {
@@ -74,10 +79,11 @@
"take_photo": "وێنە بگرە",
"save_photo": "هەڵی بگرە",
"copy_photo": "لەبەری بگرەوە",
- "sign_in": "بچۆ ژوورەوە",
- "sign_up": "خۆت تۆمار بکە",
+ "sign_in": "Log in",
+ "sign_up": "Create account",
"see_more": "زیاتر ببینە",
"preview": "پێشبینین",
+ "copy": "Copy",
"share": "هاوبەشی بکە",
"share_user": "%s هاوبەش بکە",
"share_post": "هاوبەشی بکە",
@@ -91,12 +97,16 @@
"block_domain": "%s ئاستەنگ بکە",
"unblock_domain": "%s ئاستەنگ مەکە",
"settings": "رێکخستنەکان",
- "delete": "بیسڕەوە"
+ "delete": "بیسڕەوە",
+ "translate_post": {
+ "title": "Translate from %s",
+ "unknown_language": "Unknown"
+ }
},
"tabs": {
"home": "ماڵەوە",
- "search": "بگەڕێ",
- "notification": "ئاگادارکردنەوەکان",
+ "search_and_explore": "Search and Explore",
+ "notifications": "Notifications",
"profile": "پرۆفایل"
},
"keyboard": {
@@ -132,6 +142,8 @@
"sensitive_content": "ناوەڕۆکی هەستیار",
"media_content_warning": "دەستی پیا بنێ بۆ نیشاندانی",
"tap_to_reveal": "دەستی پیا بنێ بۆ نیشاندانی",
+ "load_embed": "Load Embed",
+ "link_via_user": "%s via %s",
"poll": {
"vote": "دەنگ بدە",
"closed": "داخراوە"
@@ -153,6 +165,7 @@
"show_image": "وێنەکە نیشان بدە",
"show_gif": "گیفەکە نیشان بدە",
"show_video_player": "ڤیدیۆکە لێ بدە",
+ "share_link_in_post": "Share Link in Post",
"tap_then_hold_to_show_menu": "دەستی پیا بنێ و بیگرە بۆ نیشاندانی پێڕستەکە"
},
"tag": {
@@ -168,6 +181,12 @@
"private": "تەنیا شوێنکەوتووەکانی دەتوانن ئەم پۆستە ببینن.",
"private_from_me": "تەنیا شوێنکەوتووەکانم دەتوانن ئەم پۆستە ببینن.",
"direct": "تەنیا بەکارهێنەرە ئاماژە پێکراوەکە دەتوانێت ئەم پۆستە ببینێت."
+ },
+ "translation": {
+ "translated_from": "Translated from %s using %s",
+ "unknown_language": "Unknown",
+ "unknown_provider": "Unknown",
+ "show_original": "Shown Original"
}
},
"friendship": {
@@ -218,10 +237,16 @@
"get_started": "دەست پێ بکە",
"log_in": "بچۆ ژوورەوە"
},
+ "login": {
+ "title": "Welcome back",
+ "subtitle": "Log you in on the server you created your account on.",
+ "server_search_field": {
+ "placeholder": "Enter URL or search for your server"
+ }
+ },
"server_picker": {
"title": "ماستۆدۆن لە چەندان بەکارهێنەر پێک دێت کە لە ڕاژەکاری جیاواز دان.",
- "subtitle": "ڕاژەکارێکێکی گشتی یان دانەیەک لەسەر بنەمای حەزەکانت و هەرێمەکەت هەڵبژێرە.",
- "subtitle_extend": "ڕاژەکارێکێکی گشتی یان دانەیەک لەسەر بنەمای حەزەکانت و هەرێمەکەت هەڵبژێرە. هەر ڕاژەکارێک لەلایەن ڕێکخراوێک یان تاکەکەسێک بەڕێوە دەبرێت.",
+ "subtitle": "Pick a server based on your region, interests, or a general purpose one. You can still chat with anyone on Mastodon, regardless of your servers.",
"button": {
"category": {
"all": "هەموو",
@@ -248,8 +273,7 @@
"category": "بەش"
},
"input": {
- "placeholder": "بگەڕێ",
- "search_servers_or_enter_url": "Search servers or enter URL"
+ "search_servers_or_enter_url": "Search communities or enter URL"
},
"empty_state": {
"finding_servers": "ڕاژەکار دەدۆزرێتەوە...",
@@ -385,8 +409,10 @@
"description_video": "ڤیدیۆکەت بۆ نابیناکان باس بکە...",
"load_failed": "Load Failed",
"upload_failed": "Upload Failed",
- "can_not_recognize_this_media_attachment": "Can not regonize this media attachment",
- "attachment_too_large": "Attachment too large"
+ "can_not_recognize_this_media_attachment": "Can not recognize this media attachment",
+ "attachment_too_large": "Attachment too large",
+ "compressing_state": "Compressing...",
+ "server_processing_state": "Server Processing..."
},
"poll": {
"duration_time": "کات: %s",
@@ -396,7 +422,9 @@
"one_day": "1 ڕۆژ",
"three_days": "3 ڕۆژ",
"seven_days": "7 ڕۆژ",
- "option_number": "بژاردەی %ld"
+ "option_number": "بژاردەی %ld",
+ "the_poll_is_invalid": "The poll is invalid",
+ "the_poll_has_empty_option": "The poll has empty option"
},
"content_warning": {
"placeholder": "ئاگادارییەکەت لێرە بنووسە..."
@@ -417,7 +445,9 @@
"custom_emoji_picker": "هەڵبژێری ئیمۆجی",
"enable_content_warning": "ئاگاداریی ناوەڕۆک چالاک بکە",
"disable_content_warning": "ئاگاداریی ناوەڕۆک ناچالاک بکە",
- "post_visibility_menu": "پێڕستی شێوازی دەرکەوتنی پۆست"
+ "post_visibility_menu": "پێڕستی شێوازی دەرکەوتنی پۆست",
+ "post_options": "Post Options",
+ "posting_as": "Posting as %s"
},
"keyboard": {
"discard_post": "پۆستەکە هەڵوەشێنەوە",
@@ -433,15 +463,23 @@
"follows_you": "Follows You"
},
"dashboard": {
- "posts": "پۆستەکان",
- "following": "شوێنکەوتن",
- "followers": "شوێنکەوتوو"
+ "my_posts": "posts",
+ "my_following": "following",
+ "my_followers": "followers",
+ "other_posts": "posts",
+ "other_following": "following",
+ "other_followers": "followers"
},
"fields": {
+ "joined": "Joined",
"add_row": "ڕیز زیاد بکە",
"placeholder": {
"label": "ناونیشان",
"content": "ناوەڕۆک"
+ },
+ "verified": {
+ "short": "Verified on %s",
+ "long": "Ownership of this link was checked on %s"
}
},
"segmented_control": {
@@ -707,6 +745,18 @@
},
"bookmark": {
"title": "Bookmarks"
+ },
+ "followed_tags": {
+ "title": "Followed Tags",
+ "header": {
+ "posts": "posts",
+ "participants": "participants",
+ "posts_today": "posts today"
+ },
+ "actions": {
+ "follow": "Follow",
+ "unfollow": "Unfollow"
+ }
}
}
}
diff --git a/Localization/StringsConvertor/input/cs.lproj/Localizable.stringsdict b/Localization/StringsConvertor/input/cs.lproj/Localizable.stringsdict
index 827bd79e6..63e324a1b 100644
--- a/Localization/StringsConvertor/input/cs.lproj/Localizable.stringsdict
+++ b/Localization/StringsConvertor/input/cs.lproj/Localizable.stringsdict
@@ -62,6 +62,26 @@
%ld znaků
+ a11y.plural.count.characters_left
+
+ NSStringLocalizedFormatKey
+ %#@character_count@ zbývá
+ character_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 znak
+ few
+ %ld znaky
+ many
+ %ld znaků
+ other
+ %ld znaků
+
+
plural.count.followed_by_and_mutual
NSStringLocalizedFormatKey
@@ -288,13 +308,13 @@
NSStringFormatValueTypeKey
ld
one
- 1 following
+ 1 sledující
few
- %ld following
+ %ld sledující
many
- %ld following
+ %ld sledujících
other
- %ld following
+ %ld sledujících
plural.count.follower
@@ -328,13 +348,13 @@
NSStringFormatValueTypeKey
ld
one
- 1 year left
+ Zbývá 1 rok
few
- %ld years left
+ Zbývají %ld roky
many
- %ld years left
+ Zbývá %ld roků
other
- %ld years left
+ Zbývá %ld roků
date.month.left
@@ -348,7 +368,7 @@
NSStringFormatValueTypeKey
ld
one
- 1 months left
+ Zbývá 1 měsíc
few
%ld months left
many
diff --git a/Localization/StringsConvertor/input/cs.lproj/app.json b/Localization/StringsConvertor/input/cs.lproj/app.json
index 4050352f4..30d5eb26c 100644
--- a/Localization/StringsConvertor/input/cs.lproj/app.json
+++ b/Localization/StringsConvertor/input/cs.lproj/app.json
@@ -51,6 +51,11 @@
"clean_cache": {
"title": "Vyčistit mezipaměť",
"message": "Úspěšně vyčištěno %s mezipaměti."
+ },
+ "translation_failed": {
+ "title": "Poznámka",
+ "message": "Překlad se nezdařil. Správce možná nepovolil překlad na tomto serveru nebo tento server používá starší verzi Mastodonu, kde překlady ještě nejsou podporovány.",
+ "button": "OK"
}
},
"controls": {
@@ -75,9 +80,10 @@
"save_photo": "Uložit fotku",
"copy_photo": "Kopírovat fotografii",
"sign_in": "Přihlásit se",
- "sign_up": "Zaregistrovat se",
+ "sign_up": "Vytvořit účet",
"see_more": "Zobrazit více",
"preview": "Náhled",
+ "copy": "Kopírovat",
"share": "Sdílet",
"share_user": "Sdílet %s",
"share_post": "Sdílet příspěvek",
@@ -91,12 +97,16 @@
"block_domain": "Blokovat %s",
"unblock_domain": "Odblokovat %s",
"settings": "Nastavení",
- "delete": "Smazat"
+ "delete": "Smazat",
+ "translate_post": {
+ "title": "Přeložit z %s",
+ "unknown_language": "Neznámý"
+ }
},
"tabs": {
"home": "Domů",
- "search": "Hledat",
- "notification": "Oznamování",
+ "search_and_explore": "Hledat a zkoumat",
+ "notifications": "Oznámení",
"profile": "Profil"
},
"keyboard": {
@@ -113,8 +123,8 @@
"open_author_profile": "Otevřít profil autora",
"open_reblogger_profile": "Otevřít rebloggerův profil",
"reply_status": "Odpovědět na příspěvek",
- "toggle_reblog": "Toggle Reblog on Post",
- "toggle_favorite": "Toggle Favorite on Post",
+ "toggle_reblog": "Přepnout Reblog na příspěvku",
+ "toggle_favorite": "Přepnout Oblíbené na příspěvku",
"toggle_content_warning": "Přepnout varování obsahu",
"preview_image": "Náhled obrázku"
},
@@ -132,6 +142,8 @@
"sensitive_content": "Citlivý obsah",
"media_content_warning": "Klepnutím kdekoli zobrazíte",
"tap_to_reveal": "Klepnutím zobrazit",
+ "load_embed": "Načíst vložené",
+ "link_via_user": "%s přes %s",
"poll": {
"vote": "Hlasovat",
"closed": "Uzavřeno"
@@ -153,6 +165,7 @@
"show_image": "Zobrazit obrázek",
"show_gif": "Zobrazit GIF",
"show_video_player": "Zobrazit video přehrávač",
+ "share_link_in_post": "Sdílet odkaz v příspěvku",
"tap_then_hold_to_show_menu": "Klepnutím podržte pro zobrazení nabídky"
},
"tag": {
@@ -168,6 +181,12 @@
"private": "Pouze jejich sledující mohou vidět tento příspěvek.",
"private_from_me": "Pouze moji sledující mohou vidět tento příspěvek.",
"direct": "Pouze zmíněný uživatel může vidět tento příspěvek."
+ },
+ "translation": {
+ "translated_from": "Přeloženo z %s pomocí %s",
+ "unknown_language": "Neznámý",
+ "unknown_provider": "Neznámý",
+ "show_original": "Zobrazit originál"
}
},
"friendship": {
@@ -218,10 +237,16 @@
"get_started": "Začínáme",
"log_in": "Přihlásit se"
},
+ "login": {
+ "title": "Vítejte zpět",
+ "subtitle": "Přihlaste se na serveru, na kterém jste si vytvořili účet.",
+ "server_search_field": {
+ "placeholder": "Zadejte URL nebo vyhledávejte váš server"
+ }
+ },
"server_picker": {
"title": "Mastodon tvoří uživatelé z různých serverů.",
- "subtitle": "Vyberte server založený na vašich zájmech, regionu nebo obecném účelu.",
- "subtitle_extend": "Vyberte server založený na vašich zájmech, regionu nebo obecném účelu. Každý server je provozován zcela nezávislou organizací nebo jednotlivcem.",
+ "subtitle": "Vyberte server založený ve vašem regionu, podle zájmů nebo podle obecného účelu. Stále můžete chatovat s kýmkoli na Mastodonu bez ohledu na vaše servery.",
"button": {
"category": {
"all": "Vše",
@@ -248,8 +273,7 @@
"category": "KATEGORIE"
},
"input": {
- "placeholder": "Hledat servery",
- "search_servers_or_enter_url": "Hledat servery nebo zadat URL"
+ "search_servers_or_enter_url": "Hledejte komunity nebo zadejte URL"
},
"empty_state": {
"finding_servers": "Hledání dostupných serverů...",
@@ -386,7 +410,9 @@
"load_failed": "Načtení se nezdařilo",
"upload_failed": "Nahrání selhalo",
"can_not_recognize_this_media_attachment": "Nelze rozpoznat toto medium přílohy",
- "attachment_too_large": "Příloha je příliš velká"
+ "attachment_too_large": "Příloha je příliš velká",
+ "compressing_state": "Probíhá komprese...",
+ "server_processing_state": "Zpracování serveru..."
},
"poll": {
"duration_time": "Doba trvání: %s",
@@ -396,7 +422,9 @@
"one_day": "1 den",
"three_days": "3 dny",
"seven_days": "7 dní",
- "option_number": "Možnost %ld"
+ "option_number": "Možnost %ld",
+ "the_poll_is_invalid": "Anketa je neplatná",
+ "the_poll_has_empty_option": "Anketa má prázdnou možnost"
},
"content_warning": {
"placeholder": "Zde napište přesné varování..."
@@ -417,7 +445,9 @@
"custom_emoji_picker": "Vlastní výběr Emoji",
"enable_content_warning": "Povolit upozornění na obsah",
"disable_content_warning": "Vypnout upozornění na obsah",
- "post_visibility_menu": "Menu viditelnosti příspěvku"
+ "post_visibility_menu": "Menu viditelnosti příspěvku",
+ "post_options": "Možnosti příspěvku",
+ "posting_as": "Odesílání jako %s"
},
"keyboard": {
"discard_post": "Zahodit příspěvek",
@@ -433,15 +463,23 @@
"follows_you": "Sleduje vás"
},
"dashboard": {
- "posts": "příspěvky",
- "following": "sledování",
- "followers": "sledující"
+ "my_posts": "příspěvky",
+ "my_following": "sledování",
+ "my_followers": "sledující",
+ "other_posts": "příspěvky",
+ "other_following": "sledování",
+ "other_followers": "sledující"
},
"fields": {
+ "joined": "Připojen/a",
"add_row": "Přidat řádek",
"placeholder": {
"label": "Označení",
"content": "Obsah"
+ },
+ "verified": {
+ "short": "Ověřeno na %s",
+ "long": "Vlastnictví tohoto odkazu bylo zkontrolováno na %s"
}
},
"segmented_control": {
@@ -707,6 +745,18 @@
},
"bookmark": {
"title": "Záložky"
+ },
+ "followed_tags": {
+ "title": "Sledované štítky",
+ "header": {
+ "posts": "příspěvky",
+ "participants": "účastníci",
+ "posts_today": "příspěvky dnes"
+ },
+ "actions": {
+ "follow": "Sledovat",
+ "unfollow": "Přestat sledovat"
+ }
}
}
}
diff --git a/Localization/StringsConvertor/input/cy.lproj/Localizable.stringsdict b/Localization/StringsConvertor/input/cy.lproj/Localizable.stringsdict
index 038eaffda..33bcd5a8d 100644
--- a/Localization/StringsConvertor/input/cy.lproj/Localizable.stringsdict
+++ b/Localization/StringsConvertor/input/cy.lproj/Localizable.stringsdict
@@ -13,23 +13,23 @@
NSStringFormatValueTypeKey
ld
zero
- %ld unread notification
+ %ld hysbysiad heb ei ddarllen
one
- 1 unread notification
+ %ld hysbysiad heb ei ddarllen
two
- %ld unread notification
+ %ld hysbysiad heb eu darllen
few
- %ld unread notification
+ %ld hysbysiad heb eu darllen
many
- %ld unread notification
+ %ld hysbysiad heb eu darllen
other
- %ld unread notification
+ %ld hysbysiad heb eu darllen
a11y.plural.count.input_limit_exceeds
NSStringLocalizedFormatKey
- Input limit exceeds %#@character_count@
+ Mae'r terfyn mewnbwn yn fwy na %#@character_count@
character_count
NSStringFormatSpecTypeKey
@@ -37,23 +37,23 @@
NSStringFormatValueTypeKey
ld
zero
- %ld characters
+ %ld nod
one
- 1 character
+ %ld nod
two
- %ld characters
+ %ld nod
few
- %ld characters
+ %ld nod
many
- %ld characters
+ %ld nod
other
- %ld nodau
+ %ld nod
a11y.plural.count.input_limit_remains
NSStringLocalizedFormatKey
- Input limit remains %#@character_count@
+ Mae'r terfyn mewnbwn yn %#@character_count@
character_count
NSStringFormatSpecTypeKey
@@ -61,17 +61,41 @@
NSStringFormatValueTypeKey
ld
zero
- %ld characters
+ %ld nod
one
- 1 character
+ %ld nod
two
- %ld characters
+ %ld nod
few
- %ld characters
+ %ld nod
many
- %ld characters
+ %ld nod
other
- %ld characters
+ %ld nod
+
+
+ a11y.plural.count.characters_left
+
+ NSStringLocalizedFormatKey
+ %#@character_count@ ar ôl
+ character_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ zero
+ %ld nod
+ one
+ %ld nod
+ two
+ %ld nod
+ few
+ %ld nod
+ many
+ %ld nod
+ other
+ %ld nod
plural.count.followed_by_and_mutual
@@ -104,17 +128,17 @@
NSStringFormatValueTypeKey
ld
zero
- Followed by %1$@, and %ld mutuals
+ Dilynwyd gan %1$@, a %ld mewn cyffredin
one
- Followed by %1$@, and another mutual
+ Dilynwyd gan %1$@, a pherson gyffredin
two
- Followed by %1$@, and %ld mutuals
+ Dilynwyd gan %1$@, a %ld mewn cyffredin
few
- Followed by %1$@, and %ld mutuals
+ Dilynwyd gan %1$@, a %ld mewn cyffredin
many
- Followed by %1$@, and %ld mutuals
+ Dilynwyd gan %1$@, a %ld mewn cyffredin
other
- Followed by %1$@, and %ld mutuals
+ Dilynwyd gan %1$@, a %ld mewn cyffredin
plural.count.metric_formatted.post
@@ -128,17 +152,17 @@
NSStringFormatValueTypeKey
ld
zero
- post
+ tŵt
one
- post
+ tŵt
two
- postiau
+ tŵt
few
- posts
+ tŵt
many
- posts
+ tŵt
other
- postiau
+ postiadau
plural.count.media
@@ -152,17 +176,17 @@
NSStringFormatValueTypeKey
ld
zero
- %ld media
+ %ld cyfrwng
one
- 1 media
+ %ld cyfrwng
two
- %ld media
+ %ld gyfrwng
few
- %ld media
+ %ld cyfrwng
many
- %ld media
+ %ld cyfrwng
other
- %ld media
+ %ld cyfrwng
plural.count.post
@@ -176,17 +200,17 @@
NSStringFormatValueTypeKey
ld
zero
- %ld posts
+ %ld post
one
- 1 post
+ %ld post
two
- %ld posts
+ %ld bost
few
- %ld posts
+ %ld post
many
- %ld posts
+ %ld post
other
- %ld posts
+ %ld post
plural.count.favorite
@@ -200,17 +224,17 @@
NSStringFormatValueTypeKey
ld
zero
- %ld favorites
+ %ld ffefrynnau
one
- 1 favorite
+ %ld ffefryn
two
- %ld favorites
+ %ld ffefryn
few
- %ld favorites
+ %ld ffefryn
many
- %ld favorites
+ %ld ffefryn
other
- %ld favorites
+ %ld ffefryn
plural.count.reblog
@@ -224,17 +248,17 @@
NSStringFormatValueTypeKey
ld
zero
- %ld reblogs
+ %ld ailflogiau
one
- 1 reblog
+ %ld ailflog
two
- %ld reblogs
+ %ld ailflog
few
- %ld reblogs
+ %ld ailflog
many
- %ld reblogs
+ %ld ailflog
other
- %ld reblogs
+ %ld o ailflogiau
plural.count.reply
@@ -248,17 +272,17 @@
NSStringFormatValueTypeKey
ld
zero
- %ld replies
+ %ld ymatebau
one
- 1 reply
+ %ld ymateb
two
- %ld replies
+ %ld ymateb
few
- %ld replies
+ %ld ymateb
many
- %ld replies
+ %ld o ymatebau
other
- %ld replies
+ %ld ymateb
plural.count.vote
@@ -272,17 +296,17 @@
NSStringFormatValueTypeKey
ld
zero
- %ld votes
+ %ld pleidleisiau
one
- 1 vote
+ %ld pleidlais
two
- %ld votes
+ %ld bleidlais
few
- %ld votes
+ %ld phleidlais
many
- %ld votes
+ %ld pleidlais
other
- %ld votes
+ %ld pleidlais
plural.count.voter
@@ -296,17 +320,17 @@
NSStringFormatValueTypeKey
ld
zero
- %ld voters
+ %ld pleidleiswyr
one
- 1 voter
+ %ld pleidleisiwr
two
- %ld voters
+ %ld bleidleisiwr
few
- %ld voters
+ %ld phleidleisiwr
many
- %ld voters
+ %ld pleidleisiwr
other
- %ld voters
+ %ld pleidleisiwr
plural.people_talking
@@ -320,17 +344,17 @@
NSStringFormatValueTypeKey
ld
zero
- %ld people talking
+ %ld person yn trafod
one
- 1 people talking
+ %ld person yn trafod
two
- %ld people talking
+ %ld berson yn trafod
few
- %ld people talking
+ %ld o bobl yn trafod
many
- %ld people talking
+ %ld o bobl yn trafod
other
- %ld people talking
+ %ld o bobl yn trafod
plural.count.following
@@ -344,17 +368,17 @@
NSStringFormatValueTypeKey
ld
zero
- %ld following
+ %ld yn dilyn
one
- 1 following
+ %ld yn dilyn
two
- %ld following
+ %ld yn dilyn
few
- %ld following
+ %ld yn dilyn
many
- %ld following
+ %ld yn dilyn
other
- %ld following
+ %ld yn dilyn
plural.count.follower
@@ -368,17 +392,17 @@
NSStringFormatValueTypeKey
ld
zero
- %ld followers
+ %ld dilynwyr
one
- 1 follower
+ %ld dilynwr
two
- %ld followers
+ %ld ddilynwr
few
- %ld followers
+ %ld dilynwr
many
- %ld followers
+ %ld o ddilynwyr
other
- %ld followers
+ %ld dilynwr
date.year.left
@@ -392,17 +416,17 @@
NSStringFormatValueTypeKey
ld
zero
- %ld years left
+ %ld blwyddyn ar ôl
one
- 1 year left
+ %ld blwyddyn ar ôl
two
- %ld years left
+ %ld flwyddyn ar ôl
few
- %ld years left
+ %ld blwyddyn ar ôl
many
- %ld years left
+ %ld blwyddyn ar ôl
other
- %ld years left
+ %ld blwyddyn ar ôl
date.month.left
@@ -416,17 +440,17 @@
NSStringFormatValueTypeKey
ld
zero
- %ld months left
+ %ld mis ar ôl
one
- 1 months left
+ %ld mis ar ôl
two
- %ld months left
+ %ld fis ar ôl
few
- %ld months left
+ %ld mis ar ôl
many
- %ld months left
+ %ld mis ar ôl
other
- %ld months left
+ %ld mis ar ôl
date.day.left
@@ -440,17 +464,17 @@
NSStringFormatValueTypeKey
ld
zero
- %ld days left
+ %ld diwrnod ar ôl
one
- 1 day left
+ %ld diwrnod ar ôl
two
- %ld days left
+ %ld ddiwrnod ar ôl
few
- %ld days left
+ %ld diwrnod ar ôl
many
- %ld days left
+ %ld diwrnod ar ôl
other
- %ld days left
+ %ld diwrnod ar ôl
date.hour.left
@@ -464,17 +488,17 @@
NSStringFormatValueTypeKey
ld
zero
- %ld hours left
+ %ld awr ar ôl
one
- 1 hour left
+ %ld awr ar ôl
two
- %ld hours left
+ %ld awr ar ôl
few
- %ld hours left
+ %ld awr ar ôl
many
- %ld hours left
+ %ld awr ar ôl
other
- %ld hours left
+ %ld awr ar ôl
date.minute.left
@@ -488,17 +512,17 @@
NSStringFormatValueTypeKey
ld
zero
- %ld minutes left
+ %ld munud ar ôl
one
- 1 minute left
+ %ld munud ar ôl
two
- %ld minutes left
+ %ld funud ar ôl
few
- %ld minutes left
+ %ld munud ar ôl
many
- %ld minutes left
+ %ld munud ar ôl
other
- %ld minutes left
+ %ld munud ar ôl
date.second.left
@@ -512,17 +536,17 @@
NSStringFormatValueTypeKey
ld
zero
- %ld seconds left
+ %ld eiliad ar ôl
one
- 1 second left
+ %ld eiliad ar ôl
two
- %ld seconds left
+ %ld eiliad ar ôl
few
- %ld seconds left
+ %ld eiliad ar ôl
many
- %ld seconds left
+ %ld eiliad ar ôl
other
- %ld seconds left
+ %ld eiliad ar ôl
date.year.ago.abbr
@@ -536,17 +560,17 @@
NSStringFormatValueTypeKey
ld
zero
- %ldy ago
+ %ld blwyddyn yn ôl
one
- 1y ago
+ %ld blwyddyn yn ôl
two
- %ldy ago
+ %ld flwyddyn yn ôl
few
- %ldy ago
+ %ld blwyddyn yn ôl
many
- %ldy ago
+ %ld blwyddyn yn ôl
other
- %ldy ago
+ %ld blwyddyn yn ôl
date.month.ago.abbr
@@ -560,17 +584,17 @@
NSStringFormatValueTypeKey
ld
zero
- %ldM ago
+ %ld munud yn ôl
one
- 1M ago
+ %ld munud yn ôl
two
- %ldM ago
+ %ld funud yn ôl
few
- %ldM ago
+ %ld munud yn ôl
many
- %ldM ago
+ %ld munud yn ôl
other
- %ldM ago
+ %ld munud yn ôl
date.day.ago.abbr
@@ -584,17 +608,17 @@
NSStringFormatValueTypeKey
ld
zero
- %ldd ago
+ %ldd yn ôl
one
- 1d ago
+ %ldd yn ôl
two
- %ldd ago
+ %ldd yn ôl
few
- %ldd ago
+ %ldd yn ôl
many
- %ldd ago
+ %ldd yn ôl
other
- %ldd ago
+ %ldd yn ôl
date.hour.ago.abbr
@@ -608,17 +632,17 @@
NSStringFormatValueTypeKey
ld
zero
- %ldh ago
+ %ld awr yn ôl
one
- 1h ago
+ %ld awr yn ôl
two
- %ldh ago
+ %ld awr yn ôl
few
- %ldh ago
+ %ld awr yn ôl
many
- %ldh ago
+ %ld awr yn ôl
other
- %ldh ago
+ %ld awr yn ôl
date.minute.ago.abbr
@@ -632,17 +656,17 @@
NSStringFormatValueTypeKey
ld
zero
- %ldm ago
+ %ld munud yn ôl
one
- 1m ago
+ %ld munud yn ôl
two
- %ldm ago
+ %ld funud yn ôl
few
- %ldm ago
+ %ld munud yn ôl
many
- %ldm ago
+ %ld munud yn ôl
other
- %ldm ago
+ %ld munud yn ôl
date.second.ago.abbr
@@ -656,17 +680,17 @@
NSStringFormatValueTypeKey
ld
zero
- %lds ago
+ %ld eiliad yn ôl
one
- 1s ago
+ %ld eiliad yn ôl
two
- %lds ago
+ %ld eiliad yn ôl
few
- %lds ago
+ %ld eiliad yn ôl
many
- %lds ago
+ %ld eiliad yn ôl
other
- %lds ago
+ %ld eiliad yn ôl
diff --git a/Localization/StringsConvertor/input/cy.lproj/app.json b/Localization/StringsConvertor/input/cy.lproj/app.json
index f36fe7d16..b276dfa4b 100644
--- a/Localization/StringsConvertor/input/cy.lproj/app.json
+++ b/Localization/StringsConvertor/input/cy.lproj/app.json
@@ -2,711 +2,761 @@
"common": {
"alerts": {
"common": {
- "please_try_again": "Please try again.",
- "please_try_again_later": "Please try again later."
+ "please_try_again": "Ceisiwch eto.",
+ "please_try_again_later": "Ceisiwch eto nes ymlaen."
},
"sign_up_failure": {
- "title": "Sign Up Failure"
+ "title": "Gwall Ymgofrestru"
},
"server_error": {
- "title": "Server Error"
+ "title": "Gwall Gweinydd"
},
"vote_failure": {
- "title": "Vote Failure",
- "poll_ended": "The poll has ended"
+ "title": "Gwall Pleidleisio",
+ "poll_ended": "Mae'r pôl wedi dod i ben"
},
"discard_post_content": {
- "title": "Discard Draft",
- "message": "Confirm to discard composed post content."
+ "title": "Dileu Drafft",
+ "message": "Cadarnhau i ddileu post drafft."
},
"publish_post_failure": {
- "title": "Publish Failure",
- "message": "Failed to publish the post.\nPlease check your internet connection.",
+ "title": "Gwall Cyhoeddi",
+ "message": "Gwall wrth gyhoeddi'r tŵt.\nGwiriwch eich cysylltiad i'r rhyngrhwyd.",
"attachments_message": {
- "video_attach_with_photo": "Cannot attach a video to a post that already contains images.",
- "more_than_one_video": "Cannot attach more than one video."
+ "video_attach_with_photo": "Ni ellir ychwanegu fideo at bost sy'n cynnwys lluniau yn barod.",
+ "more_than_one_video": "Ni ellir ychwanegu mwy nag un fideo."
}
},
"edit_profile_failure": {
- "title": "Edit Profile Error",
- "message": "Cannot edit profile. Please try again."
+ "title": "Gwall Golygu Proffil",
+ "message": "Ni ellir golygu proffil. Ceisiwch eto."
},
"sign_out": {
- "title": "Sign Out",
- "message": "Are you sure you want to sign out?",
- "confirm": "Sign Out"
+ "title": "Allgofnodi",
+ "message": "Ydych chi wir eisiau allgofnodi?",
+ "confirm": "Allgofnodi"
},
"block_domain": {
- "title": "Are you really, really sure you want to block the entire %s? In most cases a few targeted blocks or mutes are sufficient and preferable. You will not see content from that domain and any of your followers from that domain will be removed.",
- "block_entire_domain": "Block Domain"
+ "title": "Ydych chi wir, wir eisiau blocio'r holl %s? Fel arfer, mae blocio neu anwybyddu pobl penodol yn broses mwy effeithiol. Ni fyddwch yn gweld cynnwys o'r parth hwnnw a bydd eich dilynwyr o'r parth hwnnw yn cael eu ddileu.",
+ "block_entire_domain": "Blocio parth"
},
"save_photo_failure": {
- "title": "Save Photo Failure",
- "message": "Please enable the photo library access permission to save the photo."
+ "title": "Gwall Cadw Llun",
+ "message": "Rhowch caniatád mynediad i'r Photo Library i gadw'r llun."
},
"delete_post": {
- "title": "Delete Post",
- "message": "Are you sure you want to delete this post?"
+ "title": "Dileu Tŵt",
+ "message": "Ydych chi wir eisiau dileu'r post hwn?"
},
"clean_cache": {
- "title": "Clean Cache",
- "message": "Successfully cleaned %s cache."
+ "title": "Clirio storfa",
+ "message": "Cliriwyd storfa %s yn llwyddiannus."
+ },
+ "translation_failed": {
+ "title": "Nodyn",
+ "message": "Methwyd cyfieithu. Efallai nad yw'r gweinyddwr wedi galluogi cyfieithiadau ar y gweinydd hwn neu mae'r gweinydd hwn yn rhedeg fersiwn hŷn o Mastodon lle nad yw cyfieithiadau ar gael eto.",
+ "button": "Iawn"
}
},
"controls": {
"actions": {
- "back": "Back",
- "next": "Next",
- "previous": "Previous",
- "open": "Open",
- "add": "Add",
- "remove": "Remove",
- "edit": "Edit",
- "save": "Save",
- "ok": "OK",
- "done": "Done",
- "confirm": "Confirm",
- "continue": "Continue",
- "compose": "Compose",
- "cancel": "Cancel",
- "discard": "Discard",
- "try_again": "Try Again",
- "take_photo": "Take Photo",
- "save_photo": "Save Photo",
- "copy_photo": "Copy Photo",
- "sign_in": "Sign In",
- "sign_up": "Sign Up",
- "see_more": "See More",
- "preview": "Preview",
- "share": "Share",
- "share_user": "Share %s",
- "share_post": "Share Post",
- "open_in_safari": "Open in Safari",
- "open_in_browser": "Open in Browser",
- "find_people": "Find people to follow",
- "manually_search": "Manually search instead",
- "skip": "Skip",
- "reply": "Reply",
- "report_user": "Report %s",
- "block_domain": "Block %s",
- "unblock_domain": "Unblock %s",
- "settings": "Settings",
- "delete": "Delete"
+ "back": "Nôl",
+ "next": "Ymlaen",
+ "previous": "Blaenorol",
+ "open": "Agor",
+ "add": "Ychwanegu",
+ "remove": "Dileu",
+ "edit": "Golygu",
+ "save": "Cadw",
+ "ok": "Iawn",
+ "done": "Iawn",
+ "confirm": "Cadarnhau",
+ "continue": "Parhau",
+ "compose": "Creu",
+ "cancel": "Canslo",
+ "discard": "Diddymu",
+ "try_again": "Ceisio eto",
+ "take_photo": "Tynnu Llun",
+ "save_photo": "Cadw Llun",
+ "copy_photo": "Copïo Llun",
+ "sign_in": "Mewngofnodi",
+ "sign_up": "Creu cyfrif",
+ "see_more": "Gweld Mwy",
+ "preview": "Rhagolwg",
+ "copy": "Copïo",
+ "share": "Rhannu",
+ "share_user": "Rhannu %s",
+ "share_post": "Rhannu Tŵt",
+ "open_in_safari": "Agor yn Safari",
+ "open_in_browser": "Agor yn y Porwr",
+ "find_people": "Dilyn pobl newydd",
+ "manually_search": "Chwilio yn fanwl",
+ "skip": "Anwybyddu",
+ "reply": "Ymateb",
+ "report_user": "Riportio %s",
+ "block_domain": "Blocio %s",
+ "unblock_domain": "Dadflocio %s",
+ "settings": "Gosodiadau",
+ "delete": "Dileu",
+ "translate_post": {
+ "title": "Cyfieithu o %s",
+ "unknown_language": "Anhysbys"
+ }
},
"tabs": {
- "home": "Home",
- "search": "Search",
- "notification": "Notification",
- "profile": "Profile"
+ "home": "Hafan",
+ "search_and_explore": "Chwilio ac Archwilio",
+ "notifications": "Hysbysiadau",
+ "profile": "Proffil"
},
"keyboard": {
"common": {
- "switch_to_tab": "Switch to %s",
- "compose_new_post": "Compose New Post",
- "show_favorites": "Show Favorites",
- "open_settings": "Open Settings"
+ "switch_to_tab": "Newid i %s",
+ "compose_new_post": "Creu Tŵt Newydd",
+ "show_favorites": "Dangos Ffefrynnau",
+ "open_settings": "Agor Gosodiadau"
},
"timeline": {
- "previous_status": "Previous Post",
- "next_status": "Next Post",
- "open_status": "Open Post",
- "open_author_profile": "Open Author's Profile",
- "open_reblogger_profile": "Open Reblogger's Profile",
- "reply_status": "Reply to Post",
- "toggle_reblog": "Toggle Reblog on Post",
- "toggle_favorite": "Toggle Favorite on Post",
- "toggle_content_warning": "Toggle Content Warning",
- "preview_image": "Preview Image"
+ "previous_status": "Tŵt Blaenorol",
+ "next_status": "Tŵt Nesaf",
+ "open_status": "Agor Tŵt",
+ "open_author_profile": "Agor Proffil yr Awdur",
+ "open_reblogger_profile": "Agor Proffil yr Ailflogwr",
+ "reply_status": "Ymateb i Tŵt",
+ "toggle_reblog": "Toglo Ailfloggio ar Tŵt",
+ "toggle_favorite": "Toglo Ffefrynnu ar Tŵt",
+ "toggle_content_warning": "Toglo Rhybudd Cynnwys",
+ "preview_image": "Rhagolygu'r Llun"
},
"segmented_control": {
- "previous_section": "Previous Section",
- "next_section": "Next Section"
+ "previous_section": "Adran Flaenorol",
+ "next_section": "Adran Nesaf"
}
},
"status": {
- "user_reblogged": "%s reblogged",
- "user_replied_to": "Replied to %s",
- "show_post": "Show Post",
- "show_user_profile": "Show user profile",
- "content_warning": "Content Warning",
- "sensitive_content": "Sensitive Content",
- "media_content_warning": "Tap anywhere to reveal",
- "tap_to_reveal": "Tap to reveal",
+ "user_reblogged": "%s wedi ailflogio",
+ "user_replied_to": "Wedi ymateb i %s",
+ "show_post": "Dangos Tŵt",
+ "show_user_profile": "Dangos proffil y defnyddiwr",
+ "content_warning": "Rhybudd Cynnwys",
+ "sensitive_content": "Cynnwys Sensitif",
+ "media_content_warning": "Tapiwch unrhywle i weld",
+ "tap_to_reveal": "Tapiwch i weld",
+ "load_embed": "Llwytho Planiad",
+ "link_via_user": "%s trwy %s",
"poll": {
- "vote": "Vote",
- "closed": "Closed"
+ "vote": "Pleidleisio",
+ "closed": "Wedi cau"
},
"meta_entity": {
- "url": "Link: %s",
- "hashtag": "Hashtag: %s",
- "mention": "Show Profile: %s",
- "email": "Email address: %s"
+ "url": "Dolen: %s",
+ "hashtag": "Hashnod: %s",
+ "mention": "Dangos Proffil: %s",
+ "email": "Cyfeiriad e-bost: %s"
},
"actions": {
- "reply": "Reply",
+ "reply": "Ymateb",
"reblog": "Hybwch",
- "unreblog": "Undo reblog",
- "favorite": "Favorite",
- "unfavorite": "Unfavorite",
- "menu": "Menu",
- "hide": "Hide",
- "show_image": "Show image",
- "show_gif": "Show GIF",
- "show_video_player": "Show video player",
- "tap_then_hold_to_show_menu": "Tap then hold to show menu"
+ "unreblog": "Dadwneud ailfloggiad",
+ "favorite": "Ffefrynnu",
+ "unfavorite": "Dad-ffefrynnu",
+ "menu": "Dewislen",
+ "hide": "Cuddio",
+ "show_image": "Dangos llun",
+ "show_gif": "Dangos GIF",
+ "show_video_player": "Dangos chwaraewr fideo",
+ "share_link_in_post": "Rhannu'r Ddolen yn y Postiad",
+ "tap_then_hold_to_show_menu": "Tapiwch a gwasgu i gael y ddewislen"
},
"tag": {
"url": "URL",
- "mention": "Mention",
- "link": "Link",
- "hashtag": "Hashtag",
- "email": "Email",
+ "mention": "Sôn",
+ "link": "Dolen",
+ "hashtag": "Hashnod",
+ "email": "E-bost",
"emoji": "Emoji"
},
"visibility": {
- "unlisted": "Everyone can see this post but not display in the public timeline.",
- "private": "Only their followers can see this post.",
- "private_from_me": "Only my followers can see this post.",
- "direct": "Only mentioned user can see this post."
+ "unlisted": "Gall pawb weld y post hwn ond nid yn y ffrwd cyhoeddus.",
+ "private": "Dim ond eu dilynwyr nhw sy'n gallu gweld y post hwn.",
+ "private_from_me": "Dim ond fy nilynwyr ni sy'n gallu gweld y post hwn.",
+ "direct": "Dim ond y ddefnyddiwr â soniwyd sy'n gallu gweld y post hwn."
+ },
+ "translation": {
+ "translated_from": "Cyfieithwyd o %s gan ddefnyddio %s",
+ "unknown_language": "Anhysbys",
+ "unknown_provider": "Anhysbys",
+ "show_original": "Dangos y Gwreiddiol"
}
},
"friendship": {
- "follow": "Follow",
- "following": "Following",
- "request": "Request",
- "pending": "Pending",
- "block": "Block",
- "block_user": "Block %s",
- "block_domain": "Block %s",
- "unblock": "Unblock",
- "unblock_user": "Unblock %s",
- "blocked": "Blocked",
- "mute": "Mute",
- "mute_user": "Mute %s",
- "unmute": "Unmute",
- "unmute_user": "Unmute %s",
- "muted": "Muted",
- "edit_info": "Edit Info",
- "show_reblogs": "Show Reblogs",
- "hide_reblogs": "Hide Reblogs"
+ "follow": "Dilyn",
+ "following": "Yn dilyn",
+ "request": "Gwneud cais",
+ "pending": "Yn aros",
+ "block": "Blocio",
+ "block_user": "Blocio %s",
+ "block_domain": "Blocio %s",
+ "unblock": "Dadflocio",
+ "unblock_user": "Dadflocio %s",
+ "blocked": "Wedi blocio",
+ "mute": "Anwybyddu",
+ "mute_user": "Anwybyddu %s",
+ "unmute": "Dad-anwybyddu",
+ "unmute_user": "Dad-anwybyddu %s",
+ "muted": "Wedi anwybyddu",
+ "edit_info": "Golygu",
+ "show_reblogs": "Dangos Ailfloggiau",
+ "hide_reblogs": "Cuddio Ailfloggiau"
},
"timeline": {
- "filtered": "Filtered",
+ "filtered": "Wedi'i hidlo",
"timestamp": {
- "now": "Now"
+ "now": "Nawr"
},
"loader": {
- "load_missing_posts": "Load missing posts",
- "loading_missing_posts": "Loading missing posts...",
- "show_more_replies": "Show more replies"
+ "load_missing_posts": "Llwytho postau coll",
+ "loading_missing_posts": "Wrthi'n llwytho postau coll...",
+ "show_more_replies": "Dangos mwy o ymatebion"
},
"header": {
- "no_status_found": "No Post Found",
- "blocking_warning": "You can’t view this user's profile\nuntil you unblock them.\nYour profile looks like this to them.",
- "user_blocking_warning": "You can’t view %s’s profile\nuntil you unblock them.\nYour profile looks like this to them.",
- "blocked_warning": "You can’t view this user’s profile\nuntil they unblock you.",
- "user_blocked_warning": "You can’t view %s’s profile\nuntil they unblock you.",
- "suspended_warning": "This user has been suspended.",
- "user_suspended_warning": "%s’s account has been suspended."
+ "no_status_found": "Ni Chanfuwyd Post",
+ "blocking_warning": "Ni allwch gweld proffil y ddefnyddiwr hwn\nos ydych wedi'u blocio.\nMae'ch proffil chi yn edrych fel hyn iddynt.",
+ "user_blocking_warning": "Ni allwch gweld proffil %s\nos ydych wedi'u blocio.\nMae'ch proffil chi yn edrych fel hyn iddynt.",
+ "blocked_warning": "Ni allwch gweld proffil y ddefnyddiwr hwn\ngan eu bod wedi eich blocio chi.",
+ "user_blocked_warning": "Ni allwch gweld proffil %s\ngan eu bod wedi eich blocio chi.",
+ "suspended_warning": "Mae'r defnyddiwr hwn wedi derbyn gwaharddiad.",
+ "user_suspended_warning": "Mae cyfrif %s wedi derbyn gwaharddiad."
}
}
}
},
"scene": {
"welcome": {
- "slogan": "Social networking\nback in your hands.",
- "get_started": "Get Started",
- "log_in": "Log In"
+ "slogan": "Rhwydweithio cymdeithasol, \nyn eich dwylo chi.",
+ "get_started": "Cychwyn Arni",
+ "log_in": "Mewngofnodi"
+ },
+ "login": {
+ "title": "Croeso nôl",
+ "subtitle": "Mewngofnodi ar y gweinydd y rydych chi wedi creu cyfrif arno.",
+ "server_search_field": {
+ "placeholder": "Mewnosod URL neu chwilio am eich gweinydd"
+ }
},
"server_picker": {
- "title": "Mastodon is made of users in different servers.",
- "subtitle": "Pick a server based on your interests, region, or a general purpose one.",
- "subtitle_extend": "Pick a server based on your interests, region, or a general purpose one. Each server is operated by an entirely independent organization or individual.",
+ "title": "Mae Mastodon yn cynnwys defnyddwyr o weinyddion gwahanol.",
+ "subtitle": "Dewiswch gweinydd yn ôl eich lleoliad, diddordebau, neu defnydd cyffredin. Gallwch siarad gydag unrhywun ar Mastodon, yn annibynnol o weinyddion chi.",
"button": {
"category": {
- "all": "All",
- "all_accessiblity_description": "Category: All",
- "academia": "academia",
- "activism": "activism",
- "food": "food",
+ "all": "Popeth",
+ "all_accessiblity_description": "Categori: Popeth",
+ "academia": "academaidd",
+ "activism": "gweithgarwch",
+ "food": "bwyd",
"furry": "furry",
- "games": "games",
- "general": "general",
- "journalism": "journalism",
- "lgbt": "lgbt",
- "regional": "regional",
- "art": "art",
- "music": "music",
- "tech": "tech"
+ "games": "gemau",
+ "general": "cyffredinol",
+ "journalism": "newyddiaduraeth",
+ "lgbt": "LHDT+",
+ "regional": "rhanbarthol",
+ "art": "celf",
+ "music": "cerddoriaeth",
+ "tech": "technoleg"
},
- "see_less": "See Less",
- "see_more": "See More"
+ "see_less": "Gweld Llai",
+ "see_more": "Gweld Mwy"
},
"label": {
- "language": "LANGUAGE",
- "users": "USERS",
- "category": "CATEGORY"
+ "language": "IAITH",
+ "users": "DEFNYDDWYR",
+ "category": "CATEGORI"
},
"input": {
- "placeholder": "Search servers",
- "search_servers_or_enter_url": "Search servers or enter URL"
+ "search_servers_or_enter_url": "Chwilio cymunedau neu fewnosod URL"
},
"empty_state": {
- "finding_servers": "Finding available servers...",
- "bad_network": "Something went wrong while loading the data. Check your internet connection.",
- "no_results": "No results"
+ "finding_servers": "Wrthi'n chwilio am weinyddion sydd ar gael...",
+ "bad_network": "Digwyddodd gwall wrth lwytho'r data. Gwiriwch eich cyswllt â'r rhyngrwyd.",
+ "no_results": "Dim canlyniadau"
}
},
"register": {
- "title": "Let’s get you set up on %s",
- "lets_get_you_set_up_on_domain": "Let’s get you set up on %s",
+ "title": "Rhoi popeth yn ei le ar %s",
+ "lets_get_you_set_up_on_domain": "Rhoi popeth yn ei le ar %s",
"input": {
"avatar": {
- "delete": "Delete"
+ "delete": "Dileu"
},
"username": {
- "placeholder": "username",
- "duplicate_prompt": "This username is taken."
+ "placeholder": "enw defnyddiwr",
+ "duplicate_prompt": "Defnyddir yr enw hwn yn barod."
},
"display_name": {
- "placeholder": "display name"
+ "placeholder": "enw arddangos"
},
"email": {
- "placeholder": "email"
+ "placeholder": "e-bost"
},
"password": {
- "placeholder": "password",
- "require": "Your password needs at least:",
- "character_limit": "8 characters",
+ "placeholder": "cyfrinair",
+ "require": "Mae angen i'ch cyfrinair gael o leiaf:",
+ "character_limit": "8 nod",
"accessibility": {
- "checked": "checked",
- "unchecked": "unchecked"
+ "checked": "ticiwyd",
+ "unchecked": "na thiciwyd"
},
- "hint": "Your password needs at least eight characters"
+ "hint": "Mae angen o leiaf wyth nod yn eich cyfrinair"
},
"invite": {
- "registration_user_invite_request": "Why do you want to join?"
+ "registration_user_invite_request": "Pam ydych chi eisiau ymuno?"
}
},
"error": {
"item": {
- "username": "Username",
- "email": "Email",
- "password": "Password",
- "agreement": "Agreement",
- "locale": "Locale",
- "reason": "Reason"
+ "username": "Enw defnyddiwr",
+ "email": "E-bost",
+ "password": "Cyfrinair",
+ "agreement": "Cytundeb",
+ "locale": "Lleoliad",
+ "reason": "Rheswm"
},
"reason": {
- "blocked": "%s contains a disallowed email provider",
- "unreachable": "%s does not seem to exist",
- "taken": "%s is already in use",
- "reserved": "%s is a reserved keyword",
- "accepted": "%s must be accepted",
- "blank": "%s is required",
- "invalid": "%s is invalid",
- "too_long": "%s is too long",
- "too_short": "%s is too short",
- "inclusion": "%s is not a supported value"
+ "blocked": "Mae %s yn defnyddio darparwr e-bost nad yw'n cael ei ganiatáu",
+ "unreachable": "Nid yw %s yn bodoli",
+ "taken": "Defnyddir %s yn barod",
+ "reserved": "Mae %s yn air allweddol a chadwyd",
+ "accepted": "Mae angen cytuno â %s",
+ "blank": "Mae angen cael %s",
+ "invalid": "Mae %s yn annilys",
+ "too_long": "Mae %s yn rhy hir",
+ "too_short": "Mae %s yn rhy fyr",
+ "inclusion": "Nid yw %s yn gwerth a chefnogir"
},
"special": {
- "username_invalid": "Username must only contain alphanumeric characters and underscores",
- "username_too_long": "Username is too long (can’t be longer than 30 characters)",
- "email_invalid": "This is not a valid email address",
- "password_too_short": "Password is too short (must be at least 8 characters)"
+ "username_invalid": "Dylai enwau ddefnyddiwr gynnwys nodau alffaniwmerig a thanlinellau yn unig",
+ "username_too_long": "Enw defnyddiwr yn rhy hir (na all fod yn fwy na 30 nodyn)",
+ "email_invalid": "Nid yw'n cyfeiriad e-bost dilys",
+ "password_too_short": "Cyfrinair yn rhy fyr (Rhaid fod o leiaf 8 nodyn)"
}
}
},
"server_rules": {
- "title": "Some ground rules.",
- "subtitle": "These are set and enforced by the %s moderators.",
- "prompt": "By continuing, you’re subject to the terms of service and privacy policy for %s.",
- "terms_of_service": "terms of service",
- "privacy_policy": "privacy policy",
+ "title": "Rhai rheolau sylfaenol.",
+ "subtitle": "Mae'r rhain yn cael eu gosod a'u gorfodi gan gymedrolwyr %s.",
+ "prompt": "Wrth barhau, rydych yn cytuno â'r Telerau Gwasanaeth a Pholisi Preifatrwydd %s.",
+ "terms_of_service": "telerau gwasanaeth",
+ "privacy_policy": "polisi preifatrwydd",
"button": {
- "confirm": "I Agree"
+ "confirm": "Rwy'n cytuno"
}
},
"confirm_email": {
- "title": "One last thing.",
- "subtitle": "Tap the link we emailed to you to verify your account.",
- "tap_the_link_we_emailed_to_you_to_verify_your_account": "Tap the link we emailed to you to verify your account",
+ "title": "Un peth olaf.",
+ "subtitle": "Tapiwch ar y ddolen yn eich e-bost i ddilysu eich cyfrif.",
+ "tap_the_link_we_emailed_to_you_to_verify_your_account": "Tapiwch ar y ddolen yn eich e-bost i ddilysu eich cyfrif",
"button": {
- "open_email_app": "Open Email App",
- "resend": "Resend"
+ "open_email_app": "Agor ap e-byst",
+ "resend": "Ailanfon"
},
"dont_receive_email": {
- "title": "Check your email",
- "description": "Check if your email address is correct as well as your junk folder if you haven’t.",
- "resend_email": "Resend Email"
+ "title": "Gwiriwch eich e-byst",
+ "description": "Gwiriwch a yw eich cyfeiriad e-bost yn gywir a hefyd eich ffolder 'junk' os nad ydych wedi ei gwneud.",
+ "resend_email": "Ailanfon E-bost"
},
"open_email_app": {
- "title": "Check your inbox.",
- "description": "We just sent you an email. Check your junk folder if you haven’t.",
+ "title": "Gwiriwch eich blwch derbyn.",
+ "description": "Rydym newydd anfon e-bost atoch chi. Gwiriwch eich ffolder 'junk' os nad ydych wedi ei gwneud.",
"mail": "Mail",
- "open_email_client": "Open Email Client"
+ "open_email_client": "Agor Cleient E-byst"
}
},
"home_timeline": {
- "title": "Home",
+ "title": "Hafan",
"navigation_bar_state": {
- "offline": "Offline",
- "new_posts": "See new posts",
- "published": "Published!",
- "Publishing": "Publishing post...",
+ "offline": "All-lein",
+ "new_posts": "Gweld tŵts newydd",
+ "published": "Wedi cyhoeddi!",
+ "Publishing": "Wrthi'n cyhoeddi...",
"accessibility": {
- "logo_label": "Logo Button",
- "logo_hint": "Tap to scroll to top and tap again to previous location"
+ "logo_label": "Botwm Logo",
+ "logo_hint": "Tapiwch i sgrolio i'r frig, a thapiwch eto er mwyn mynd i'r lleoliad blaenorol"
}
}
},
"suggestion_account": {
- "title": "Find People to Follow",
- "follow_explain": "When you follow someone, you’ll see their posts in your home feed."
+ "title": "Chwilio a Dilyn Pobl",
+ "follow_explain": "Ar ôl ichi ddilyn rhywun, byddwch yn gweld eu postiadau yn eich ffrwd hafan."
},
"compose": {
"title": {
- "new_post": "New Post",
- "new_reply": "New Reply"
+ "new_post": "Tŵt Newydd",
+ "new_reply": "Ymateb Newydd"
},
"media_selection": {
- "camera": "Take Photo",
+ "camera": "Tynnu Llun",
"photo_library": "Photo Library",
- "browse": "Browse"
+ "browse": "Pori"
},
- "content_input_placeholder": "Type or paste what’s on your mind",
- "compose_action": "Publish",
- "replying_to_user": "replying to %s",
+ "content_input_placeholder": "Teipiwch neu gludo'r hyn sydd ar eich meddwl",
+ "compose_action": "Cyhoeddi",
+ "replying_to_user": "yn ymateb i %s",
"attachment": {
- "photo": "photo",
- "video": "video",
- "attachment_broken": "This %s is broken and can’t be\nuploaded to Mastodon.",
- "description_photo": "Describe the photo for the visually-impaired...",
- "description_video": "Describe the video for the visually-impaired...",
- "load_failed": "Load Failed",
- "upload_failed": "Upload Failed",
- "can_not_recognize_this_media_attachment": "Can not regonize this media attachment",
- "attachment_too_large": "Attachment too large"
+ "photo": "llun",
+ "video": "fideo",
+ "attachment_broken": "Mae %s wedi torri a ni ellir\nuwchlwytho hwn i Mastodon.",
+ "description_photo": "Disgrifio i'r rheini â nam ar eu golwg...",
+ "description_video": "Disgrifio i'r rheini â nam ar eu golwg...",
+ "load_failed": "Methwyd Llwytho",
+ "upload_failed": "Methwyd Uwchlwytho",
+ "can_not_recognize_this_media_attachment": "Ni ellir dilysu'r atodiad cyfrwng",
+ "attachment_too_large": "Mae'r atodiad yn rhy fawr",
+ "compressing_state": "Wrthi'n cywasgu...",
+ "server_processing_state": "Mae'r gweinydd yn prosesu..."
},
"poll": {
- "duration_time": "Duration: %s",
- "thirty_minutes": "30 minutes",
- "one_hour": "1 Hour",
- "six_hours": "6 Hours",
- "one_day": "1 Day",
- "three_days": "3 Days",
- "seven_days": "7 Days",
- "option_number": "Option %ld"
+ "duration_time": "Hyd: %s",
+ "thirty_minutes": "30 munud",
+ "one_hour": "1 awr",
+ "six_hours": "6 awr",
+ "one_day": "1 diwrnod",
+ "three_days": "3 diwrnod",
+ "seven_days": "7 diwrnod",
+ "option_number": "Opsiwn %ld",
+ "the_poll_is_invalid": "Nid yw'r pôl yn ddilys",
+ "the_poll_has_empty_option": "Mae gan y pôl opsiwn gwag"
},
"content_warning": {
- "placeholder": "Write an accurate warning here..."
+ "placeholder": "Ysgrifenwch rybudd manwl yma..."
},
"visibility": {
- "public": "Public",
- "unlisted": "Unlisted",
- "private": "Followers only",
- "direct": "Only people I mention"
+ "public": "Cyhoeddus",
+ "unlisted": "Heb ei rhestru",
+ "private": "Dilynwyr yn unig",
+ "direct": "Dim ond pobl rwy'n eu crybwyll"
},
"auto_complete": {
- "space_to_add": "Space to add"
+ "space_to_add": "Lle i ychwanegu"
},
"accessibility": {
- "append_attachment": "Add Attachment",
- "append_poll": "Add Poll",
- "remove_poll": "Remove Poll",
- "custom_emoji_picker": "Custom Emoji Picker",
- "enable_content_warning": "Enable Content Warning",
- "disable_content_warning": "Disable Content Warning",
- "post_visibility_menu": "Post Visibility Menu"
+ "append_attachment": "Ychwanegu Atodiad",
+ "append_poll": "Ychwanegu Pôl",
+ "remove_poll": "Dileu Pôl",
+ "custom_emoji_picker": "Dewislen Emoji Bersonol",
+ "enable_content_warning": "Galluogi Rhybudd Cynnwys",
+ "disable_content_warning": "Analluogi Rhybudd Cynnwys",
+ "post_visibility_menu": "Dewislen Breifatrwydd Post",
+ "post_options": "Opsiynau Tŵt",
+ "posting_as": "Cyhoeddi fel %s"
},
"keyboard": {
- "discard_post": "Discard Post",
- "publish_post": "Publish Post",
- "toggle_poll": "Toggle Poll",
- "toggle_content_warning": "Toggle Content Warning",
- "append_attachment_entry": "Add Attachment - %s",
- "select_visibility_entry": "Select Visibility - %s"
+ "discard_post": "Dileu Tŵt",
+ "publish_post": "Cyhoeddi Tŵt",
+ "toggle_poll": "Toglo Pôl",
+ "toggle_content_warning": "Toglo Rhybudd Cynnwys",
+ "append_attachment_entry": "Ychwanegu Atodiad - %s",
+ "select_visibility_entry": "Dewis Preifatrwydd - %s"
}
},
"profile": {
"header": {
- "follows_you": "Follows You"
+ "follows_you": "Yn eich dilyn"
},
"dashboard": {
- "posts": "postiadau",
- "following": "following",
- "followers": "followers"
+ "my_posts": "postiadu",
+ "my_following": "yn dilyn",
+ "my_followers": "dilynwyr",
+ "other_posts": "postiadu",
+ "other_following": "yn dilyn",
+ "other_followers": "dilynwyr"
},
"fields": {
- "add_row": "Add Row",
+ "joined": "Ymunwyd",
+ "add_row": "Ychwanegu Rhes",
"placeholder": {
"label": "Label",
- "content": "Content"
+ "content": "Cynnwys"
+ },
+ "verified": {
+ "short": "Wedi dilysu ar %s",
+ "long": "Gwiriwyd perchnogaeth y ddolen yma ar %s"
}
},
"segmented_control": {
"posts": "Postiadau",
- "replies": "Replies",
+ "replies": "Ymatebion",
"posts_and_replies": "Postiadau ac Atebion",
- "media": "Media",
- "about": "About"
+ "media": "Cyfryngau",
+ "about": "Ynghylch"
},
"relationship_action_alert": {
"confirm_mute_user": {
- "title": "Mute Account",
- "message": "Confirm to mute %s"
+ "title": "Anwybyddu Cyfrif",
+ "message": "Parhau i anwybyddu %s"
},
"confirm_unmute_user": {
- "title": "Unmute Account",
- "message": "Confirm to unmute %s"
+ "title": "Dad-anwybyddu Cyfrif",
+ "message": "Parhau i ddad-anwybyddu %s"
},
"confirm_block_user": {
- "title": "Block Account",
- "message": "Confirm to block %s"
+ "title": "Blocio Cyfrif",
+ "message": "Cadarnhau i flocio %s"
},
"confirm_unblock_user": {
- "title": "Unblock Account",
- "message": "Confirm to unblock %s"
+ "title": "Dadflocio Cyfrif",
+ "message": "Parhau i ddadflocio %s"
},
"confirm_show_reblogs": {
- "title": "Show Reblogs",
- "message": "Confirm to show reblogs"
+ "title": "Dangos Ailfloggiau",
+ "message": "Cadarnhau i ddangos hybiau"
},
"confirm_hide_reblogs": {
- "title": "Hide Reblogs",
- "message": "Confirm to hide reblogs"
+ "title": "Cuddio Ailflogiau",
+ "message": "Cadarnhau i guddio ailflogiau"
}
},
"accessibility": {
- "show_avatar_image": "Show avatar image",
- "edit_avatar_image": "Edit avatar image",
- "show_banner_image": "Show banner image",
- "double_tap_to_open_the_list": "Double tap to open the list"
+ "show_avatar_image": "Dangos afatar",
+ "edit_avatar_image": "Golygu afatar",
+ "show_banner_image": "Dangos baner",
+ "double_tap_to_open_the_list": "Tapiwch dwywaith i agor y restr"
}
},
"follower": {
- "title": "follower",
- "footer": "Followers from other servers are not displayed."
+ "title": "dilynwr",
+ "footer": "Ni ddangosir dilynwyr o weinyddion eraill."
},
"following": {
- "title": "following",
- "footer": "Follows from other servers are not displayed."
+ "title": "yn dilyn",
+ "footer": "Ni ddangosir dilynion o weinyddion eraill."
},
"familiarFollowers": {
- "title": "Followers you familiar",
- "followed_by_names": "Followed by %s"
+ "title": "Dilynwyr sy'n gyfarwydd ichi",
+ "followed_by_names": "Dilynwyd gan %s"
},
"favorited_by": {
- "title": "Favorited By"
+ "title": "Ffefrynnwyd gan"
},
"reblogged_by": {
- "title": "Reblogged By"
+ "title": "Wedi'i hybu gan"
},
"search": {
- "title": "Search",
+ "title": "Chwilio",
"search_bar": {
- "placeholder": "Search hashtags and users",
- "cancel": "Cancel"
+ "placeholder": "Chwilio hashnodau a defnyddwyr",
+ "cancel": "Canslo"
},
"recommend": {
- "button_text": "See All",
+ "button_text": "Gweld Popeth",
"hash_tag": {
- "title": "Trending on Mastodon",
- "description": "Hashtags that are getting quite a bit of attention",
- "people_talking": "%s people are talking"
+ "title": "Pynciau Llosg ar Mastodon",
+ "description": "Dyma'r hashnodau sy'n derbyn tipyn o sylw",
+ "people_talking": "%s person yn trafod"
},
"accounts": {
- "title": "Accounts you might like",
- "description": "You may like to follow these accounts",
- "follow": "Follow"
+ "title": "Cyfrifon ar eich cyfer",
+ "description": "Cyfrifon sydd efallai o ddiddordeb ichi",
+ "follow": "Dilyn"
}
},
"searching": {
"segment": {
- "all": "All",
- "people": "People",
- "hashtags": "Hashtags",
+ "all": "Popeth",
+ "people": "Pobl",
+ "hashtags": "Hashnodau",
"posts": "Postiadau"
},
"empty_state": {
- "no_results": "No results"
+ "no_results": "Dim canlyniadau"
},
- "recent_search": "Recent searches",
- "clear": "Clear"
+ "recent_search": "Chwiliadau diweddar",
+ "clear": "Clirio"
}
},
"discovery": {
"tabs": {
"posts": "Postiadau",
- "hashtags": "Hashtags",
- "news": "News",
- "community": "Community",
- "for_you": "For You"
+ "hashtags": "Hashnodau",
+ "news": "Newyddion",
+ "community": "Cymuned",
+ "for_you": "I Ti"
},
- "intro": "These are the posts gaining traction in your corner of Mastodon."
+ "intro": "Dyma'r postiadau sy'n denu tipyn o sylw yn eich cŵr Mastodon."
},
"favorite": {
- "title": "Your Favorites"
+ "title": "Eich Ffefrynnau"
},
"notification": {
"title": {
- "Everything": "Everything",
- "Mentions": "Mentions"
+ "Everything": "Popeth",
+ "Mentions": "Crybwylliadau"
},
"notification_description": {
- "followed_you": "followed you",
- "favorited_your_post": "favorited your post",
- "reblogged_your_post": "reblogged your post",
- "mentioned_you": "mentioned you",
- "request_to_follow_you": "request to follow you",
- "poll_has_ended": "poll has ended"
+ "followed_you": "wedi eich dilyn",
+ "favorited_your_post": "wedi ffefrynnu eich tŵt",
+ "reblogged_your_post": "wedi ailfloggio'ch tŵt",
+ "mentioned_you": "wedi sôn amdanoch",
+ "request_to_follow_you": "wedi gwneud cais i'ch dilyn",
+ "poll_has_ended": "pôl wedi dod i ben"
},
"keyobard": {
- "show_everything": "Show Everything",
- "show_mentions": "Show Mentions"
+ "show_everything": "Dangos Popeth",
+ "show_mentions": "Dangos Crybwylliadau"
},
"follow_request": {
- "accept": "Accept",
- "accepted": "Accepted",
- "reject": "reject",
- "rejected": "Rejected"
+ "accept": "Derbyn",
+ "accepted": "Wedi Derbyn",
+ "reject": "gwrthod",
+ "rejected": "Gwrthodwyd"
}
},
"thread": {
"back_title": "Post",
- "title": "Post from %s"
+ "title": "Tŵt gan %s"
},
"settings": {
- "title": "Settings",
+ "title": "Gosodiadau",
"section": {
"appearance": {
- "title": "Appearance",
- "automatic": "Automatic",
- "light": "Always Light",
- "dark": "Always Dark"
+ "title": "Gwedd",
+ "automatic": "Awtomatig",
+ "light": "Golau",
+ "dark": "Tywyll"
},
"look_and_feel": {
- "title": "Look and Feel",
- "use_system": "Use System",
- "really_dark": "Really Dark",
- "sorta_dark": "Sorta Dark",
- "light": "Light"
+ "title": "Gwedd",
+ "use_system": "Dull y System",
+ "really_dark": "Tywyll Iawn",
+ "sorta_dark": "Eitha' Tywyll",
+ "light": "Golau"
},
"notifications": {
- "title": "Notifications",
- "favorites": "Favorites my post",
- "follows": "Follows me",
- "boosts": "Reblogs my post",
- "mentions": "Mentions me",
+ "title": "Hysbysiadau",
+ "favorites": "Yn ffefrynnu fy mhost",
+ "follows": "Yn fy nilyn",
+ "boosts": "Yn hybu fy mhost",
+ "mentions": "Yn sôn amdana i",
"trigger": {
- "anyone": "anyone",
- "follower": "a follower",
- "follow": "anyone I follow",
- "noone": "no one",
- "title": "Notify me when"
+ "anyone": "unrhyw un",
+ "follower": "dilynwr",
+ "follow": "unrhyw un o'm dilynion",
+ "noone": "neb",
+ "title": "Rhowch wybod i mi pan"
}
},
"preference": {
- "title": "Preferences",
- "true_black_dark_mode": "True black dark mode",
- "disable_avatar_animation": "Disable animated avatars",
- "disable_emoji_animation": "Disable animated emojis",
- "using_default_browser": "Use default browser to open links",
- "open_links_in_mastodon": "Open links in Mastodon"
+ "title": "Dewisiadau",
+ "true_black_dark_mode": "Gwedd tywyll go iawn",
+ "disable_avatar_animation": "Analluogi rhithffurfiau wedi'u hanimeiddio",
+ "disable_emoji_animation": "Analluogi emoji wedi'u hanimeiddio",
+ "using_default_browser": "Defnyddio porwr rhagosodedig i agor dolenni",
+ "open_links_in_mastodon": "Agor dolenni ym Mastodon"
},
"boring_zone": {
- "title": "The Boring Zone",
- "account_settings": "Account Settings",
- "terms": "Terms of Service",
- "privacy": "Privacy Policy"
+ "title": "Yr Ardal Ddiflas",
+ "account_settings": "Gosodiadau Cyfrif",
+ "terms": "Telerau Gwasanaeth",
+ "privacy": "Polisi Preifatrwydd"
},
"spicy_zone": {
- "title": "The Spicy Zone",
- "clear": "Clear Media Cache",
- "signout": "Sign Out"
+ "title": "Yr Ardal Sbeislyd",
+ "clear": "Clirio Storfa Cyfryngau",
+ "signout": "Allgofnodi"
}
},
"footer": {
- "mastodon_description": "Mastodon is open source software. You can report issues on GitHub at %s (%s)"
+ "mastodon_description": "Mae Mastodon yn feddalwedd cod agored. Gallech roi gwybod am wallau ar GitHub: %s (%s)"
},
"keyboard": {
- "close_settings_window": "Close Settings Window"
+ "close_settings_window": "Cau Ffenest Gosodiadau"
}
},
"report": {
- "title_report": "Report",
- "title": "Report %s",
- "step1": "Step 1 of 2",
- "step2": "Step 2 of 2",
- "content1": "Are there any other posts you’d like to add to the report?",
- "content2": "Is there anything the moderators should know about this report?",
- "report_sent_title": "Thanks for reporting, we’ll look into this.",
- "send": "Send Report",
- "skip_to_send": "Send without comment",
- "text_placeholder": "Type or paste additional comments",
- "reported": "REPORTED",
+ "title_report": "Riportio",
+ "title": "Riportio %s",
+ "step1": "Cam 1 o 2",
+ "step2": "Cam 2 o 2",
+ "content1": "Hoffech chi ychwanegu unrhyw postiadau eraill i'r adroddiad?",
+ "content2": "Oes unrhyw beth y dylai'r cymedrolwyr wybod yn yr adroddiad hwn?",
+ "report_sent_title": "Diolch am adrodd, byddwn yn ymchwilio i hyn.",
+ "send": "Anfon adroddiad",
+ "skip_to_send": "Anfon heb sylw",
+ "text_placeholder": "Teipiwch neu gludwch sylwadau ychwanegol",
+ "reported": "WEDI RIPORTIO",
"step_one": {
- "step_1_of_4": "Step 1 of 4",
- "whats_wrong_with_this_post": "What's wrong with this post?",
- "whats_wrong_with_this_account": "What's wrong with this account?",
- "whats_wrong_with_this_username": "What's wrong with %s?",
- "select_the_best_match": "Select the best match",
- "i_dont_like_it": "I don’t like it",
- "it_is_not_something_you_want_to_see": "It is not something you want to see",
- "its_spam": "It’s spam",
- "malicious_links_fake_engagement_or_repetetive_replies": "Malicious links, fake engagement, or repetetive replies",
- "it_violates_server_rules": "It violates server rules",
- "you_are_aware_that_it_breaks_specific_rules": "You are aware that it breaks specific rules",
- "its_something_else": "It’s something else",
- "the_issue_does_not_fit_into_other_categories": "The issue does not fit into other categories"
+ "step_1_of_4": "Cam 1 o 4",
+ "whats_wrong_with_this_post": "Beth sy'n bod gyda'r tŵt hwn?",
+ "whats_wrong_with_this_account": "Beth sy'n bod gyda'r cyfrif hwn?",
+ "whats_wrong_with_this_username": "Beth sy'n bod gyda %s?",
+ "select_the_best_match": "Dewiswch yr opsiwn gorau",
+ "i_dont_like_it": "Dydw i ddim yn ei hoffi",
+ "it_is_not_something_you_want_to_see": "Nid yw'n rhywbeth ydych chi eisiau ei weld",
+ "its_spam": "Mae'n sbam",
+ "malicious_links_fake_engagement_or_repetetive_replies": "Dolenni maleisus, ymgysylltu ffug, neu ymatebion ailadroddus",
+ "it_violates_server_rules": "Mae'n torri rheolau'r gweinydd",
+ "you_are_aware_that_it_breaks_specific_rules": "Rydych yn ymwybodol ei fod yn torri rheolau penodol",
+ "its_something_else": "Mae'n rhywbeth arall",
+ "the_issue_does_not_fit_into_other_categories": "Nid yw'r mater yn ffitio i gategorïau eraill"
},
"step_two": {
- "step_2_of_4": "Step 2 of 4",
- "which_rules_are_being_violated": "Which rules are being violated?",
- "select_all_that_apply": "Select all that apply",
- "i_just_don’t_like_it": "I just don’t like it"
+ "step_2_of_4": "Cam 2 o 4",
+ "which_rules_are_being_violated": "Pa reolau sy'n cael eu torri?",
+ "select_all_that_apply": "Dewiswch bob un sy'n berthnasol",
+ "i_just_don’t_like_it": "Dydw i ddim yn ei hoffi"
},
"step_three": {
- "step_3_of_4": "Step 3 of 4",
- "are_there_any_posts_that_back_up_this_report": "Are there any posts that back up this report?",
- "select_all_that_apply": "Select all that apply"
+ "step_3_of_4": "Cam 3 o 4",
+ "are_there_any_posts_that_back_up_this_report": "Oes postiadau eraill sy'n cefnogi'r adroddiad hwn?",
+ "select_all_that_apply": "Dewiswch bob un sy'n berthnasol"
},
"step_four": {
- "step_4_of_4": "Step 4 of 4",
- "is_there_anything_else_we_should_know": "Is there anything else we should know?"
+ "step_4_of_4": "Cam 4 o 4",
+ "is_there_anything_else_we_should_know": "Oes unrhyw beth arall y dylem ei wybod?"
},
"step_final": {
- "dont_want_to_see_this": "Don’t want to see this?",
- "when_you_see_something_you_dont_like_on_mastodon_you_can_remove_the_person_from_your_experience.": "When you see something you don’t like on Mastodon, you can remove the person from your experience.",
- "unfollow": "Unfollow",
- "unfollowed": "Unfollowed",
- "unfollow_user": "Unfollow %s",
- "mute_user": "Mute %s",
- "you_wont_see_their_posts_or_reblogs_in_your_home_feed_they_wont_know_they_ve_been_muted": "You won’t see their posts or reblogs in your home feed. They won’t know they’ve been muted.",
- "block_user": "Block %s",
- "they_will_no_longer_be_able_to_follow_or_see_your_posts_but_they_can_see_if_theyve_been_blocked": "They will no longer be able to follow or see your posts, but they can see if they’ve been blocked.",
- "while_we_review_this_you_can_take_action_against_user": "While we review this, you can take action against %s"
+ "dont_want_to_see_this": "Ddim eisiau gweld hwn?",
+ "when_you_see_something_you_dont_like_on_mastodon_you_can_remove_the_person_from_your_experience.": "Pan welwch chi gynnwys nad ydych chi'n ei hoffi ar Mastodon, gallwch dynnu'r person o'ch profiad.",
+ "unfollow": "Dad-ddilyn",
+ "unfollowed": "Dad-ddilynwyd",
+ "unfollow_user": "Dad-ddilyn %s",
+ "mute_user": "Anwybyddu %s",
+ "you_wont_see_their_posts_or_reblogs_in_your_home_feed_they_wont_know_they_ve_been_muted": "Ni fyddwch yn gweld eu postiadau neu hybiau yn eich ffrwd hafan. Ni fyddant yn gwybod eich bod wedi eu hanwybyddu.",
+ "block_user": "Blocio %s",
+ "they_will_no_longer_be_able_to_follow_or_see_your_posts_but_they_can_see_if_theyve_been_blocked": "Ni fyddant yn gallu eich dilyn neu weld eich postiadau, ond maen nhw'n gallu gweld eich bod wedi'u blocio.",
+ "while_we_review_this_you_can_take_action_against_user": "Tra byddwn ni'n edrych ar hyn, gallwch gymryd camau yn erbyn %s"
}
},
"preview": {
"keyboard": {
- "close_preview": "Close Preview",
- "show_next": "Show Next",
- "show_previous": "Show Previous"
+ "close_preview": "Cau Rhagolwg",
+ "show_next": "Dangos Nesaf",
+ "show_previous": "Dangos Blaenorol"
}
},
"account_list": {
- "tab_bar_hint": "Current selected profile: %s. Double tap then hold to show account switcher",
- "dismiss_account_switcher": "Dismiss Account Switcher",
- "add_account": "Add Account"
+ "tab_bar_hint": "Proffil cyfredol: %s. Tapiwch dwywaith a gwasgu i ddefnyddio'r newidiwr cyfrifon",
+ "dismiss_account_switcher": "Diddymu Newidiwr Cyfrifon",
+ "add_account": "Ychwanegu Cyfrif"
},
"wizard": {
- "new_in_mastodon": "New in Mastodon",
- "multiple_account_switch_intro_description": "Switch between multiple accounts by holding the profile button.",
- "accessibility_hint": "Double tap to dismiss this wizard"
+ "new_in_mastodon": "Newydd i Mastodon",
+ "multiple_account_switch_intro_description": "Newid rhwng cyfrifon gwahanol wrth wasgu'r botwm proffil.",
+ "accessibility_hint": "Tapiwch dwywaith i ddiddymu'r dewin hwn"
},
"bookmark": {
- "title": "Bookmarks"
+ "title": "Tudalnodau"
+ },
+ "followed_tags": {
+ "title": "Tagiau a Ddilynwyd",
+ "header": {
+ "posts": "postiadau",
+ "participants": "cyfranwyr",
+ "posts_today": "postiadau heddiw"
+ },
+ "actions": {
+ "follow": "Dilyn",
+ "unfollow": "Dad-ddilyn"
+ }
}
}
}
diff --git a/Localization/StringsConvertor/input/cy.lproj/ios-infoPlist.json b/Localization/StringsConvertor/input/cy.lproj/ios-infoPlist.json
index ee0c5bfee..e2a58387a 100644
--- a/Localization/StringsConvertor/input/cy.lproj/ios-infoPlist.json
+++ b/Localization/StringsConvertor/input/cy.lproj/ios-infoPlist.json
@@ -1,6 +1,6 @@
{
- "NSCameraUsageDescription": "Used to take photo for post status",
- "NSPhotoLibraryAddUsageDescription": "Used to save photo into the Photo Library",
+ "NSCameraUsageDescription": "Defnyddir hwn i gymryd llun am bost",
+ "NSPhotoLibraryAddUsageDescription": "Defnyddir hwn i gadw llun yn eich Photo Library",
"NewPostShortcutItemTitle": "Post Newydd",
"SearchShortcutItemTitle": "Chwilio"
}
diff --git a/Localization/StringsConvertor/input/da.lproj/Localizable.stringsdict b/Localization/StringsConvertor/input/da.lproj/Localizable.stringsdict
index bdcae6ac9..788eb95fc 100644
--- a/Localization/StringsConvertor/input/da.lproj/Localizable.stringsdict
+++ b/Localization/StringsConvertor/input/da.lproj/Localizable.stringsdict
@@ -15,7 +15,7 @@
one
1 unread notification
other
- %ld unread notification
+ %ld unread notifications
a11y.plural.count.input_limit_exceeds
@@ -50,6 +50,22 @@
%ld characters
+ a11y.plural.count.characters_left
+
+ NSStringLocalizedFormatKey
+ %#@character_count@ left
+ character_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 character
+ other
+ %ld characters
+
+
plural.count.followed_by_and_mutual
NSStringLocalizedFormatKey
diff --git a/Localization/StringsConvertor/input/da.lproj/app.json b/Localization/StringsConvertor/input/da.lproj/app.json
index a6a971860..4939d3afb 100644
--- a/Localization/StringsConvertor/input/da.lproj/app.json
+++ b/Localization/StringsConvertor/input/da.lproj/app.json
@@ -51,6 +51,11 @@
"clean_cache": {
"title": "Clean Cache",
"message": "Successfully cleaned %s cache."
+ },
+ "translation_failed": {
+ "title": "Note",
+ "message": "Translation failed. Maybe the administrator has not enabled translations on this server or this server is running an older version of Mastodon where translations are not yet supported.",
+ "button": "OK"
}
},
"controls": {
@@ -74,10 +79,11 @@
"take_photo": "Take Photo",
"save_photo": "Save Photo",
"copy_photo": "Copy Photo",
- "sign_in": "Sign In",
- "sign_up": "Sign Up",
+ "sign_in": "Log in",
+ "sign_up": "Create account",
"see_more": "See More",
"preview": "Preview",
+ "copy": "Copy",
"share": "Share",
"share_user": "Share %s",
"share_post": "Share Post",
@@ -91,12 +97,16 @@
"block_domain": "Block %s",
"unblock_domain": "Unblock %s",
"settings": "Settings",
- "delete": "Delete"
+ "delete": "Delete",
+ "translate_post": {
+ "title": "Translate from %s",
+ "unknown_language": "Unknown"
+ }
},
"tabs": {
"home": "Home",
- "search": "Search",
- "notification": "Notification",
+ "search_and_explore": "Search and Explore",
+ "notifications": "Notifications",
"profile": "Profile"
},
"keyboard": {
@@ -132,6 +142,8 @@
"sensitive_content": "Sensitive Content",
"media_content_warning": "Tap anywhere to reveal",
"tap_to_reveal": "Tap to reveal",
+ "load_embed": "Load Embed",
+ "link_via_user": "%s via %s",
"poll": {
"vote": "Vote",
"closed": "Closed"
@@ -153,6 +165,7 @@
"show_image": "Show image",
"show_gif": "Show GIF",
"show_video_player": "Show video player",
+ "share_link_in_post": "Share Link in Post",
"tap_then_hold_to_show_menu": "Tap then hold to show menu"
},
"tag": {
@@ -168,6 +181,12 @@
"private": "Only their followers can see this post.",
"private_from_me": "Only my followers can see this post.",
"direct": "Only mentioned user can see this post."
+ },
+ "translation": {
+ "translated_from": "Translated from %s using %s",
+ "unknown_language": "Unknown",
+ "unknown_provider": "Unknown",
+ "show_original": "Shown Original"
}
},
"friendship": {
@@ -218,10 +237,16 @@
"get_started": "Get Started",
"log_in": "Log In"
},
+ "login": {
+ "title": "Welcome back",
+ "subtitle": "Log you in on the server you created your account on.",
+ "server_search_field": {
+ "placeholder": "Enter URL or search for your server"
+ }
+ },
"server_picker": {
"title": "Mastodon is made of users in different servers.",
- "subtitle": "Pick a server based on your interests, region, or a general purpose one.",
- "subtitle_extend": "Pick a server based on your interests, region, or a general purpose one. Each server is operated by an entirely independent organization or individual.",
+ "subtitle": "Pick a server based on your region, interests, or a general purpose one. You can still chat with anyone on Mastodon, regardless of your servers.",
"button": {
"category": {
"all": "All",
@@ -248,8 +273,7 @@
"category": "CATEGORY"
},
"input": {
- "placeholder": "Search servers",
- "search_servers_or_enter_url": "Search servers or enter URL"
+ "search_servers_or_enter_url": "Search communities or enter URL"
},
"empty_state": {
"finding_servers": "Finding available servers...",
@@ -385,8 +409,10 @@
"description_video": "Describe the video for the visually-impaired...",
"load_failed": "Load Failed",
"upload_failed": "Upload Failed",
- "can_not_recognize_this_media_attachment": "Can not regonize this media attachment",
- "attachment_too_large": "Attachment too large"
+ "can_not_recognize_this_media_attachment": "Can not recognize this media attachment",
+ "attachment_too_large": "Attachment too large",
+ "compressing_state": "Compressing...",
+ "server_processing_state": "Server Processing..."
},
"poll": {
"duration_time": "Duration: %s",
@@ -396,7 +422,9 @@
"one_day": "1 Day",
"three_days": "3 Days",
"seven_days": "7 Days",
- "option_number": "Option %ld"
+ "option_number": "Option %ld",
+ "the_poll_is_invalid": "The poll is invalid",
+ "the_poll_has_empty_option": "The poll has empty option"
},
"content_warning": {
"placeholder": "Write an accurate warning here..."
@@ -417,7 +445,9 @@
"custom_emoji_picker": "Custom Emoji Picker",
"enable_content_warning": "Enable Content Warning",
"disable_content_warning": "Disable Content Warning",
- "post_visibility_menu": "Post Visibility Menu"
+ "post_visibility_menu": "Post Visibility Menu",
+ "post_options": "Post Options",
+ "posting_as": "Posting as %s"
},
"keyboard": {
"discard_post": "Discard Post",
@@ -433,15 +463,23 @@
"follows_you": "Follows You"
},
"dashboard": {
- "posts": "posts",
- "following": "following",
- "followers": "followers"
+ "my_posts": "posts",
+ "my_following": "following",
+ "my_followers": "followers",
+ "other_posts": "posts",
+ "other_following": "following",
+ "other_followers": "followers"
},
"fields": {
+ "joined": "Joined",
"add_row": "Add Row",
"placeholder": {
"label": "Label",
"content": "Content"
+ },
+ "verified": {
+ "short": "Verified on %s",
+ "long": "Ownership of this link was checked on %s"
}
},
"segmented_control": {
@@ -707,6 +745,18 @@
},
"bookmark": {
"title": "Bookmarks"
+ },
+ "followed_tags": {
+ "title": "Followed Tags",
+ "header": {
+ "posts": "posts",
+ "participants": "participants",
+ "posts_today": "posts today"
+ },
+ "actions": {
+ "follow": "Follow",
+ "unfollow": "Unfollow"
+ }
}
}
}
diff --git a/Localization/StringsConvertor/input/de.lproj/Localizable.stringsdict b/Localization/StringsConvertor/input/de.lproj/Localizable.stringsdict
index f60c6b0d7..9d07f80d1 100644
--- a/Localization/StringsConvertor/input/de.lproj/Localizable.stringsdict
+++ b/Localization/StringsConvertor/input/de.lproj/Localizable.stringsdict
@@ -37,7 +37,23 @@
a11y.plural.count.input_limit_remains
NSStringLocalizedFormatKey
- Noch %#@character_count@ übrig
+ Noch %#@character_count@ Zeichen übrig
+ character_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 Zeichen
+ other
+ %ld Zeichen
+
+
+ a11y.plural.count.characters_left
+
+ NSStringLocalizedFormatKey
+ %#@character_count@ übrig
character_count
NSStringFormatSpecTypeKey
diff --git a/Localization/StringsConvertor/input/de.lproj/app.json b/Localization/StringsConvertor/input/de.lproj/app.json
index 481f07a4a..8c28db94f 100644
--- a/Localization/StringsConvertor/input/de.lproj/app.json
+++ b/Localization/StringsConvertor/input/de.lproj/app.json
@@ -45,12 +45,17 @@
"message": "Bitte aktiviere den Zugriff auf die Fotobibliothek, um das Foto zu speichern."
},
"delete_post": {
- "title": "Bist du dir sicher, dass du diesen Beitrag löschen möchtest?",
+ "title": "Beiträge löschen",
"message": "Bist du dir sicher, dass du diesen Beitrag löschen willst?"
},
"clean_cache": {
"title": "Zwischenspeicher leeren",
"message": "%s erfolgreich aus dem Cache gelöscht."
+ },
+ "translation_failed": {
+ "title": "Hinweis",
+ "message": "Übersetzung fehlgeschlagen. Möglicherweise hat der/die Administrator*in die Übersetzungen auf diesem Server nicht aktiviert oder dieser Server läuft mit einer älteren Version von Mastodon, in der Übersetzungen noch nicht unterstützt wurden.",
+ "button": "OK"
}
},
"controls": {
@@ -66,7 +71,7 @@
"ok": "OK",
"done": "Fertig",
"confirm": "Bestätigen",
- "continue": "Fortfahren",
+ "continue": "Weiter",
"compose": "Neue Nachricht",
"cancel": "Abbrechen",
"discard": "Verwerfen",
@@ -75,15 +80,16 @@
"save_photo": "Foto speichern",
"copy_photo": "Foto kopieren",
"sign_in": "Anmelden",
- "sign_up": "Registrieren",
+ "sign_up": "Konto erstellen",
"see_more": "Mehr anzeigen",
"preview": "Vorschau",
+ "copy": "Kopieren",
"share": "Teilen",
"share_user": "%s teilen",
"share_post": "Beitrag teilen",
"open_in_safari": "In Safari öffnen",
"open_in_browser": "Im Browser anzeigen",
- "find_people": "Finde Personen zum Folgen",
+ "find_people": "Personen zum Folgen finden",
"manually_search": "Stattdessen manuell suchen",
"skip": "Überspringen",
"reply": "Antworten",
@@ -91,12 +97,16 @@
"block_domain": "%s blockieren",
"unblock_domain": "Blockierung von %s aufheben",
"settings": "Einstellungen",
- "delete": "Löschen"
+ "delete": "Löschen",
+ "translate_post": {
+ "title": "Von %s übersetzen",
+ "unknown_language": "Unbekannt"
+ }
},
"tabs": {
"home": "Startseite",
- "search": "Suche",
- "notification": "Benachrichtigungen",
+ "search_and_explore": "Suchen und Entdecken",
+ "notifications": "Mitteilungen",
"profile": "Profil"
},
"keyboard": {
@@ -132,6 +142,8 @@
"sensitive_content": "NSFW-Inhalt",
"media_content_warning": "Tippe irgendwo zum Anzeigen",
"tap_to_reveal": "Zum Anzeigen tippen",
+ "load_embed": "Eingebettetes laden",
+ "link_via_user": "%s via %s",
"poll": {
"vote": "Abstimmen",
"closed": "Beendet"
@@ -139,8 +151,8 @@
"meta_entity": {
"url": "Link: %s",
"hashtag": "Hashtag: %s",
- "mention": "Show Profile: %s",
- "email": "Email address: %s"
+ "mention": "Profil anzeigen: %s",
+ "email": "E-Mail-Adresse: %s"
},
"actions": {
"reply": "Antworten",
@@ -153,6 +165,7 @@
"show_image": "Bild anzeigen",
"show_gif": "GIF anzeigen",
"show_video_player": "Zeige Video-Player",
+ "share_link_in_post": "Link im Beitrag teilen",
"tap_then_hold_to_show_menu": "Halte gedrückt um das Menü anzuzeigen"
},
"tag": {
@@ -165,14 +178,20 @@
},
"visibility": {
"unlisted": "Jeder kann diesen Post sehen, aber nicht in der öffentlichen Timeline zeigen.",
- "private": "Nur Follower des Authors können diesen Beitrag sehen.",
- "private_from_me": "Nur meine Follower können diesen Beitrag sehen.",
+ "private": "Nur die, die dem Autor folgen, können diesen Beitrag sehen.",
+ "private_from_me": "Nur die, die mir folgen, können diesen Beitrag sehen.",
"direct": "Nur erwähnte Benutzer können diesen Beitrag sehen."
+ },
+ "translation": {
+ "translated_from": "Übersetzt von %s mit %s",
+ "unknown_language": "Unbekannt",
+ "unknown_provider": "Unbekannt",
+ "show_original": "Original anzeigen"
}
},
"friendship": {
"follow": "Folgen",
- "following": "Folge Ich",
+ "following": "Gefolgt",
"request": "Anfragen",
"pending": "In Warteschlange",
"block": "Blockieren",
@@ -187,8 +206,8 @@
"unmute_user": "%s nicht mehr stummschalten",
"muted": "Stummgeschaltet",
"edit_info": "Information bearbeiten",
- "show_reblogs": "Reblogs anzeigen",
- "hide_reblogs": "Reblogs ausblenden"
+ "show_reblogs": "Teilen anzeigen",
+ "hide_reblogs": "Teilen ausblenden"
},
"timeline": {
"filtered": "Gefiltert",
@@ -197,7 +216,7 @@
},
"loader": {
"load_missing_posts": "Fehlende Beiträge laden",
- "loading_missing_posts": "Lade fehlende Beiträge...",
+ "loading_missing_posts": "Fehlende Beiträge werden geladen …",
"show_more_replies": "Weitere Antworten anzeigen"
},
"header": {
@@ -218,10 +237,16 @@
"get_started": "Registrieren",
"log_in": "Anmelden"
},
+ "login": {
+ "title": "Willkommen zurück",
+ "subtitle": "Melden Sie sich auf dem Server an, auf dem Sie Ihr Konto erstellt haben.",
+ "server_search_field": {
+ "placeholder": "URL eingeben oder nach Server suchen"
+ }
+ },
"server_picker": {
"title": "Wähle einen Server,\nbeliebigen Server.",
- "subtitle": "Wähle eine Gemeinschaft, die auf deinen Interessen, Region oder einem allgemeinen Zweck basiert.",
- "subtitle_extend": "Wähle eine Gemeinschaft basierend auf deinen Interessen, deiner Region oder einem allgemeinen Zweck. Jede Gemeinschaft wird von einer völlig unabhängigen Organisation oder Einzelperson betrieben.",
+ "subtitle": "Wähle einen Server basierend auf deinen Interessen oder deiner Region – oder einfach einen allgemeinen. Du kannst trotzdem mit jedem interagieren, egal auf welchem Server.",
"button": {
"category": {
"all": "Alle",
@@ -248,11 +273,10 @@
"category": "KATEGORIE"
},
"input": {
- "placeholder": "Nach Server suchen oder URL eingeben",
- "search_servers_or_enter_url": "Nach Server suchen oder URL eingeben"
+ "search_servers_or_enter_url": "Suche nach einer Community oder gib eine URL ein"
},
"empty_state": {
- "finding_servers": "Verfügbare Server werden gesucht...",
+ "finding_servers": "Verfügbare Server werden gesucht …",
"bad_network": "Beim Laden der Daten ist etwas schief gelaufen. Überprüfe deine Internetverbindung.",
"no_results": "Keine Ergebnisse"
}
@@ -343,7 +367,7 @@
"open_email_app": {
"title": "Überprüfe deinen Posteingang.",
"description": "Wir haben dir gerade eine E-Mail geschickt. Überprüfe deinen Spam-Ordner, falls du es noch nicht getan hast.",
- "mail": "Mail",
+ "mail": "E-Mail",
"open_email_client": "E-Mail-Client öffnen"
}
},
@@ -353,7 +377,7 @@
"offline": "Offline",
"new_posts": "Neue Beiträge anzeigen",
"published": "Veröffentlicht!",
- "Publishing": "Beitrag wird veröffentlicht...",
+ "Publishing": "Beitrag wird veröffentlicht …",
"accessibility": {
"logo_label": "Logo-Button",
"logo_hint": "Zum Scrollen nach oben tippen und zum vorherigen Ort erneut tippen"
@@ -362,7 +386,7 @@
},
"suggestion_account": {
"title": "Finde Personen zum Folgen",
- "follow_explain": "Wenn du jemandem folgst, dann siehst du deren Beiträge in deinem Home-Feed."
+ "follow_explain": "Sobald du anderen folgst, siehst du deren Beiträge in deinem Home-Feed."
},
"compose": {
"title": {
@@ -381,12 +405,14 @@
"photo": "Foto",
"video": "Video",
"attachment_broken": "Dieses %s scheint defekt zu sein und\nkann nicht auf Mastodon hochgeladen werden.",
- "description_photo": "Für Menschen mit Sehbehinderung beschreiben...",
- "description_video": "Für Menschen mit Sehbehinderung beschreiben...",
+ "description_photo": "Für Menschen mit Sehbehinderung beschreiben …",
+ "description_video": "Für Menschen mit Sehbehinderung beschreiben …",
"load_failed": "Laden fehlgeschlagen",
"upload_failed": "Upload fehlgeschlagen",
"can_not_recognize_this_media_attachment": "Medienanhang wurde nicht erkannt",
- "attachment_too_large": "Anhang zu groß"
+ "attachment_too_large": "Anhang zu groß",
+ "compressing_state": "wird komprimiert …",
+ "server_processing_state": "Serververarbeitung …"
},
"poll": {
"duration_time": "Dauer: %s",
@@ -396,10 +422,12 @@
"one_day": "1 Tag",
"three_days": "3 Tage",
"seven_days": "7 Tage",
- "option_number": "Auswahlmöglichkeit %ld"
+ "option_number": "Auswahlmöglichkeit %ld",
+ "the_poll_is_invalid": "Die Umfrage ist ungültig",
+ "the_poll_has_empty_option": "Die Umfrage hat eine leere Option"
},
"content_warning": {
- "placeholder": "Schreibe eine Inhaltswarnung hier..."
+ "placeholder": "Hier eine Inhaltswarnung schreiben …"
},
"visibility": {
"public": "Öffentlich",
@@ -417,7 +445,9 @@
"custom_emoji_picker": "Benutzerdefinierter Emojiwähler",
"enable_content_warning": "Inhaltswarnung einschalten",
"disable_content_warning": "Inhaltswarnung ausschalten",
- "post_visibility_menu": "Sichtbarkeitsmenü"
+ "post_visibility_menu": "Sichtbarkeitsmenü",
+ "post_options": "Beitragsoptionen",
+ "posting_as": "Veröffentlichen als %s"
},
"keyboard": {
"discard_post": "Beitrag verwerfen",
@@ -433,15 +463,23 @@
"follows_you": "Folgt dir"
},
"dashboard": {
- "posts": "Beiträge",
- "following": "Gefolgte",
- "followers": "Folgende"
+ "my_posts": "Beiträge",
+ "my_following": "folge ich",
+ "my_followers": "followers",
+ "other_posts": "Beiträge",
+ "other_following": "folge ich",
+ "other_followers": "followers"
},
"fields": {
+ "joined": "Beigetreten",
"add_row": "Zeile hinzufügen",
"placeholder": {
"label": "Bezeichnung",
"content": "Inhalt"
+ },
+ "verified": {
+ "short": "Überprüft am %s",
+ "long": "Besitz des Links wurde überprüft am %s"
}
},
"segmented_control": {
@@ -469,12 +507,12 @@
"message": "Bestätige %s zu entsperren"
},
"confirm_show_reblogs": {
- "title": "Reblogs anzeigen",
- "message": "Bestätigen um Reblogs anzuzeigen"
+ "title": "Teilen anzeigen",
+ "message": "Bestätigen, um Teilen anzuzeigen"
},
"confirm_hide_reblogs": {
- "title": "Reblogs ausblenden",
- "message": "Confirm to hide reblogs"
+ "title": "Teilen ausblenden",
+ "message": "Bestätigen, um Teilen auszublenden"
}
},
"accessibility": {
@@ -485,15 +523,15 @@
}
},
"follower": {
- "title": "Follower",
- "footer": "Folger, die nicht auf deinem Server registriert sind, werden nicht angezeigt."
+ "title": "Folgende",
+ "footer": "Folgende, die nicht auf deinem Server registriert sind, werden nicht angezeigt."
},
"following": {
- "title": "Folgende",
+ "title": "Gefolgte",
"footer": "Gefolgte, die nicht auf deinem Server registriert sind, werden nicht angezeigt."
},
"familiarFollowers": {
- "title": "Follower, die dir bekannt vorkommen",
+ "title": "Folgende, die du kennst",
"followed_by_names": "Gefolgt von %s"
},
"favorited_by": {
@@ -517,7 +555,7 @@
},
"accounts": {
"title": "Konten, die dir gefallen könnten",
- "description": "Vielleicht gefallen dir diese Benutzer",
+ "description": "Vielleicht gefallen dir diese Konten",
"follow": "Folgen"
}
},
@@ -707,6 +745,18 @@
},
"bookmark": {
"title": "Lesezeichen"
+ },
+ "followed_tags": {
+ "title": "Gefolgte Hashtags",
+ "header": {
+ "posts": "Beiträge",
+ "participants": "Teilnehmer*innen",
+ "posts_today": "Beiträge heute"
+ },
+ "actions": {
+ "follow": "Folgen",
+ "unfollow": "Entfolgen"
+ }
}
}
}
diff --git a/Localization/StringsConvertor/input/en-US.lproj/Localizable.stringsdict b/Localization/StringsConvertor/input/en-US.lproj/Localizable.stringsdict
index bdcae6ac9..788eb95fc 100644
--- a/Localization/StringsConvertor/input/en-US.lproj/Localizable.stringsdict
+++ b/Localization/StringsConvertor/input/en-US.lproj/Localizable.stringsdict
@@ -15,7 +15,7 @@
one
1 unread notification
other
- %ld unread notification
+ %ld unread notifications
a11y.plural.count.input_limit_exceeds
@@ -50,6 +50,22 @@
%ld characters
+ a11y.plural.count.characters_left
+
+ NSStringLocalizedFormatKey
+ %#@character_count@ left
+ character_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 character
+ other
+ %ld characters
+
+
plural.count.followed_by_and_mutual
NSStringLocalizedFormatKey
diff --git a/Localization/StringsConvertor/input/en-US.lproj/app.json b/Localization/StringsConvertor/input/en-US.lproj/app.json
index a6a971860..b94b9df8a 100644
--- a/Localization/StringsConvertor/input/en-US.lproj/app.json
+++ b/Localization/StringsConvertor/input/en-US.lproj/app.json
@@ -51,6 +51,11 @@
"clean_cache": {
"title": "Clean Cache",
"message": "Successfully cleaned %s cache."
+ },
+ "translation_failed": {
+ "title": "Note",
+ "message": "Translation failed. Maybe the administrator has not enabled translations on this server or this server is running an older version of Mastodon where translations are not yet supported.",
+ "button": "OK"
}
},
"controls": {
@@ -74,10 +79,11 @@
"take_photo": "Take Photo",
"save_photo": "Save Photo",
"copy_photo": "Copy Photo",
- "sign_in": "Sign In",
- "sign_up": "Sign Up",
+ "sign_in": "Log in",
+ "sign_up": "Create account",
"see_more": "See More",
"preview": "Preview",
+ "copy": "Copy",
"share": "Share",
"share_user": "Share %s",
"share_post": "Share Post",
@@ -91,12 +97,16 @@
"block_domain": "Block %s",
"unblock_domain": "Unblock %s",
"settings": "Settings",
- "delete": "Delete"
+ "delete": "Delete",
+ "translate_post": {
+ "title": "Translate from %s",
+ "unknown_language": "Unknown"
+ }
},
"tabs": {
"home": "Home",
- "search": "Search",
- "notification": "Notification",
+ "search_and_explore": "Search and Explore",
+ "notifications": "Notifications",
"profile": "Profile"
},
"keyboard": {
@@ -132,6 +142,8 @@
"sensitive_content": "Sensitive Content",
"media_content_warning": "Tap anywhere to reveal",
"tap_to_reveal": "Tap to reveal",
+ "load_embed": "Load Embed",
+ "link_via_user": "%s via %s",
"poll": {
"vote": "Vote",
"closed": "Closed"
@@ -153,6 +165,7 @@
"show_image": "Show image",
"show_gif": "Show GIF",
"show_video_player": "Show video player",
+ "share_link_in_post": "Share Link in Post",
"tap_then_hold_to_show_menu": "Tap then hold to show menu"
},
"tag": {
@@ -168,6 +181,12 @@
"private": "Only their followers can see this post.",
"private_from_me": "Only my followers can see this post.",
"direct": "Only mentioned user can see this post."
+ },
+ "translation": {
+ "translated_from": "Translated from %s using %s",
+ "unknown_language": "Unknown",
+ "unknown_provider": "Unknown",
+ "show_original": "Shown Original"
}
},
"friendship": {
@@ -218,10 +237,16 @@
"get_started": "Get Started",
"log_in": "Log In"
},
+ "login": {
+ "title": "Welcome back",
+ "subtitle": "Log you in on the server you created your account on.",
+ "server_search_field": {
+ "placeholder": "Enter URL or search for your server"
+ }
+ },
"server_picker": {
"title": "Mastodon is made of users in different servers.",
- "subtitle": "Pick a server based on your interests, region, or a general purpose one.",
- "subtitle_extend": "Pick a server based on your interests, region, or a general purpose one. Each server is operated by an entirely independent organization or individual.",
+ "subtitle": "Pick a server based on your region, interests, or a general purpose one. You can still chat with anyone on Mastodon, regardless of your servers.",
"button": {
"category": {
"all": "All",
@@ -248,8 +273,7 @@
"category": "CATEGORY"
},
"input": {
- "placeholder": "Search servers",
- "search_servers_or_enter_url": "Search servers or enter URL"
+ "search_servers_or_enter_url": "Search communities or enter URL"
},
"empty_state": {
"finding_servers": "Finding available servers...",
@@ -355,7 +379,7 @@
"published": "Published!",
"Publishing": "Publishing post...",
"accessibility": {
- "logo_label": "Logo Button",
+ "logo_label": "Mastodon",
"logo_hint": "Tap to scroll to top and tap again to previous location"
}
}
@@ -385,8 +409,10 @@
"description_video": "Describe the video for the visually-impaired...",
"load_failed": "Load Failed",
"upload_failed": "Upload Failed",
- "can_not_recognize_this_media_attachment": "Can not regonize this media attachment",
- "attachment_too_large": "Attachment too large"
+ "can_not_recognize_this_media_attachment": "Can not recognize this media attachment",
+ "attachment_too_large": "Attachment too large",
+ "compressing_state": "Compressing...",
+ "server_processing_state": "Server Processing..."
},
"poll": {
"duration_time": "Duration: %s",
@@ -396,7 +422,9 @@
"one_day": "1 Day",
"three_days": "3 Days",
"seven_days": "7 Days",
- "option_number": "Option %ld"
+ "option_number": "Option %ld",
+ "the_poll_is_invalid": "The poll is invalid",
+ "the_poll_has_empty_option": "The poll has empty option"
},
"content_warning": {
"placeholder": "Write an accurate warning here..."
@@ -417,7 +445,9 @@
"custom_emoji_picker": "Custom Emoji Picker",
"enable_content_warning": "Enable Content Warning",
"disable_content_warning": "Disable Content Warning",
- "post_visibility_menu": "Post Visibility Menu"
+ "post_visibility_menu": "Post Visibility Menu",
+ "post_options": "Post Options",
+ "posting_as": "Posting as %s"
},
"keyboard": {
"discard_post": "Discard Post",
@@ -433,15 +463,23 @@
"follows_you": "Follows You"
},
"dashboard": {
- "posts": "posts",
- "following": "following",
- "followers": "followers"
+ "my_posts": "posts",
+ "my_following": "following",
+ "my_followers": "followers",
+ "other_posts": "posts",
+ "other_following": "following",
+ "other_followers": "followers"
},
"fields": {
+ "joined": "Joined",
"add_row": "Add Row",
"placeholder": {
"label": "Label",
"content": "Content"
+ },
+ "verified": {
+ "short": "Verified on %s",
+ "long": "Ownership of this link was checked on %s"
}
},
"segmented_control": {
@@ -707,6 +745,18 @@
},
"bookmark": {
"title": "Bookmarks"
+ },
+ "followed_tags": {
+ "title": "Followed Tags",
+ "header": {
+ "posts": "posts",
+ "participants": "participants",
+ "posts_today": "posts today"
+ },
+ "actions": {
+ "follow": "Follow",
+ "unfollow": "Unfollow"
+ }
}
}
}
diff --git a/Localization/StringsConvertor/input/en.lproj/Localizable.stringsdict b/Localization/StringsConvertor/input/en.lproj/Localizable.stringsdict
index 297e6675a..788eb95fc 100644
--- a/Localization/StringsConvertor/input/en.lproj/Localizable.stringsdict
+++ b/Localization/StringsConvertor/input/en.lproj/Localizable.stringsdict
@@ -15,7 +15,7 @@
one
1 unread notification
other
- %ld unread notification
+ %ld unread notifications
a11y.plural.count.input_limit_exceeds
@@ -60,14 +60,8 @@
NSStringPluralRuleType
NSStringFormatValueTypeKey
ld
- zero
- no characters
one
1 character
- few
- %ld characters
- many
- %ld characters
other
%ld characters
diff --git a/Localization/StringsConvertor/input/en.lproj/app.json b/Localization/StringsConvertor/input/en.lproj/app.json
index 25f06ad83..b94b9df8a 100644
--- a/Localization/StringsConvertor/input/en.lproj/app.json
+++ b/Localization/StringsConvertor/input/en.lproj/app.json
@@ -51,6 +51,11 @@
"clean_cache": {
"title": "Clean Cache",
"message": "Successfully cleaned %s cache."
+ },
+ "translation_failed": {
+ "title": "Note",
+ "message": "Translation failed. Maybe the administrator has not enabled translations on this server or this server is running an older version of Mastodon where translations are not yet supported.",
+ "button": "OK"
}
},
"controls": {
@@ -74,10 +79,11 @@
"take_photo": "Take Photo",
"save_photo": "Save Photo",
"copy_photo": "Copy Photo",
- "sign_in": "Sign In",
- "sign_up": "Sign Up",
+ "sign_in": "Log in",
+ "sign_up": "Create account",
"see_more": "See More",
"preview": "Preview",
+ "copy": "Copy",
"share": "Share",
"share_user": "Share %s",
"share_post": "Share Post",
@@ -91,12 +97,16 @@
"block_domain": "Block %s",
"unblock_domain": "Unblock %s",
"settings": "Settings",
- "delete": "Delete"
+ "delete": "Delete",
+ "translate_post": {
+ "title": "Translate from %s",
+ "unknown_language": "Unknown"
+ }
},
"tabs": {
"home": "Home",
- "search": "Search",
- "notification": "Notification",
+ "search_and_explore": "Search and Explore",
+ "notifications": "Notifications",
"profile": "Profile"
},
"keyboard": {
@@ -132,6 +142,8 @@
"sensitive_content": "Sensitive Content",
"media_content_warning": "Tap anywhere to reveal",
"tap_to_reveal": "Tap to reveal",
+ "load_embed": "Load Embed",
+ "link_via_user": "%s via %s",
"poll": {
"vote": "Vote",
"closed": "Closed"
@@ -153,6 +165,7 @@
"show_image": "Show image",
"show_gif": "Show GIF",
"show_video_player": "Show video player",
+ "share_link_in_post": "Share Link in Post",
"tap_then_hold_to_show_menu": "Tap then hold to show menu"
},
"tag": {
@@ -168,6 +181,12 @@
"private": "Only their followers can see this post.",
"private_from_me": "Only my followers can see this post.",
"direct": "Only mentioned user can see this post."
+ },
+ "translation": {
+ "translated_from": "Translated from %s using %s",
+ "unknown_language": "Unknown",
+ "unknown_provider": "Unknown",
+ "show_original": "Shown Original"
}
},
"friendship": {
@@ -218,10 +237,16 @@
"get_started": "Get Started",
"log_in": "Log In"
},
+ "login": {
+ "title": "Welcome back",
+ "subtitle": "Log you in on the server you created your account on.",
+ "server_search_field": {
+ "placeholder": "Enter URL or search for your server"
+ }
+ },
"server_picker": {
"title": "Mastodon is made of users in different servers.",
- "subtitle": "Pick a server based on your interests, region, or a general purpose one.",
- "subtitle_extend": "Pick a server based on your interests, region, or a general purpose one. Each server is operated by an entirely independent organization or individual.",
+ "subtitle": "Pick a server based on your region, interests, or a general purpose one. You can still chat with anyone on Mastodon, regardless of your servers.",
"button": {
"category": {
"all": "All",
@@ -248,8 +273,7 @@
"category": "CATEGORY"
},
"input": {
- "placeholder": "Search servers",
- "search_servers_or_enter_url": "Search servers or enter URL"
+ "search_servers_or_enter_url": "Search communities or enter URL"
},
"empty_state": {
"finding_servers": "Finding available servers...",
@@ -355,7 +379,7 @@
"published": "Published!",
"Publishing": "Publishing post...",
"accessibility": {
- "logo_label": "Logo Button",
+ "logo_label": "Mastodon",
"logo_hint": "Tap to scroll to top and tap again to previous location"
}
}
@@ -385,8 +409,10 @@
"description_video": "Describe the video for the visually-impaired...",
"load_failed": "Load Failed",
"upload_failed": "Upload Failed",
- "can_not_recognize_this_media_attachment": "Can not regonize this media attachment",
- "attachment_too_large": "Attachment too large"
+ "can_not_recognize_this_media_attachment": "Can not recognize this media attachment",
+ "attachment_too_large": "Attachment too large",
+ "compressing_state": "Compressing...",
+ "server_processing_state": "Server Processing..."
},
"poll": {
"duration_time": "Duration: %s",
@@ -396,7 +422,9 @@
"one_day": "1 Day",
"three_days": "3 Days",
"seven_days": "7 Days",
- "option_number": "Option %ld"
+ "option_number": "Option %ld",
+ "the_poll_is_invalid": "The poll is invalid",
+ "the_poll_has_empty_option": "The poll has empty option"
},
"content_warning": {
"placeholder": "Write an accurate warning here..."
@@ -435,15 +463,23 @@
"follows_you": "Follows You"
},
"dashboard": {
- "posts": "posts",
- "following": "following",
- "followers": "followers"
+ "my_posts": "posts",
+ "my_following": "following",
+ "my_followers": "followers",
+ "other_posts": "posts",
+ "other_following": "following",
+ "other_followers": "followers"
},
"fields": {
+ "joined": "Joined",
"add_row": "Add Row",
"placeholder": {
"label": "Label",
"content": "Content"
+ },
+ "verified": {
+ "short": "Verified on %s",
+ "long": "Ownership of this link was checked on %s"
}
},
"segmented_control": {
@@ -709,6 +745,18 @@
},
"bookmark": {
"title": "Bookmarks"
+ },
+ "followed_tags": {
+ "title": "Followed Tags",
+ "header": {
+ "posts": "posts",
+ "participants": "participants",
+ "posts_today": "posts today"
+ },
+ "actions": {
+ "follow": "Follow",
+ "unfollow": "Unfollow"
+ }
}
}
}
diff --git a/Localization/StringsConvertor/input/es-AR.lproj/Localizable.stringsdict b/Localization/StringsConvertor/input/es-AR.lproj/Localizable.stringsdict
index 2bd66395a..fb939a040 100644
--- a/Localization/StringsConvertor/input/es-AR.lproj/Localizable.stringsdict
+++ b/Localization/StringsConvertor/input/es-AR.lproj/Localizable.stringsdict
@@ -50,6 +50,22 @@
%ld caracteres
+ a11y.plural.count.characters_left
+
+ NSStringLocalizedFormatKey
+ Quedan %#@character_count@
+ character_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 caracter
+ other
+ %ld caracteres
+
+
plural.count.followed_by_and_mutual
NSStringLocalizedFormatKey
diff --git a/Localization/StringsConvertor/input/es-AR.lproj/app.json b/Localization/StringsConvertor/input/es-AR.lproj/app.json
index 309cf4d34..120460c44 100644
--- a/Localization/StringsConvertor/input/es-AR.lproj/app.json
+++ b/Localization/StringsConvertor/input/es-AR.lproj/app.json
@@ -51,6 +51,11 @@
"clean_cache": {
"title": "Limpiar caché",
"message": "Se limpió exitosamente %s de la memoria caché."
+ },
+ "translation_failed": {
+ "title": "Nota",
+ "message": "Falló la traducción. Tal vez el administrador no habilitó las traducciones en este servidor, o este servidor está ejecutando una versión antigua de Mastodon en donde las traducciones aún no estaban disponibles.",
+ "button": "Aceptar"
}
},
"controls": {
@@ -75,9 +80,10 @@
"save_photo": "Guardar foto",
"copy_photo": "Copiar foto",
"sign_in": "Iniciar sesión",
- "sign_up": "Registrarse",
+ "sign_up": "Crear cuenta",
"see_more": "Ver más",
"preview": "Previsualización",
+ "copy": "Copiar",
"share": "Compartir",
"share_user": "Compartir %s",
"share_post": "Compartir mensaje",
@@ -91,12 +97,16 @@
"block_domain": "Bloquear a %s",
"unblock_domain": "Desbloquear a %s",
"settings": "Configuración",
- "delete": "Eliminar"
+ "delete": "Eliminar",
+ "translate_post": {
+ "title": "Traducido desde el %s",
+ "unknown_language": "Desconocido"
+ }
},
"tabs": {
"home": "Principal",
- "search": "Buscar",
- "notification": "Notificación",
+ "search_and_explore": "Buscar y explorar",
+ "notifications": "Notificaciones",
"profile": "Perfil"
},
"keyboard": {
@@ -132,15 +142,17 @@
"sensitive_content": "Contenido sensible",
"media_content_warning": "Tocá en cualquier parte para mostrar",
"tap_to_reveal": "Tocá para mostrar",
+ "load_embed": "Cargar lo insertado",
+ "link_via_user": "%s vía %s",
"poll": {
"vote": "Votar",
"closed": "Cerrada"
},
"meta_entity": {
- "url": "Link: %s",
- "hashtag": "Hashtag: %s",
- "mention": "Show Profile: %s",
- "email": "Email address: %s"
+ "url": "Enlace: %s",
+ "hashtag": "Etiqueta: %s",
+ "mention": "Mostrar perfil: %s",
+ "email": "Dirección de correo electrónico: %s"
},
"actions": {
"reply": "Responder",
@@ -153,6 +165,7 @@
"show_image": "Mostrar imagen",
"show_gif": "Mostrar GIF",
"show_video_player": "Mostrar reproductor de video",
+ "share_link_in_post": "Compartir enlace en mensaje",
"tap_then_hold_to_show_menu": "Tocá y mantené presionado para mostrar el menú"
},
"tag": {
@@ -168,6 +181,12 @@
"private": "Sólo sus seguidores pueden ver este mensaje.",
"private_from_me": "Sólo mis seguidores pueden ver este mensaje.",
"direct": "Sólo el usuario mencionado puede ver este mensaje."
+ },
+ "translation": {
+ "translated_from": "Traducido desde el %s vía %s",
+ "unknown_language": "Desconocido",
+ "unknown_provider": "Desconocido",
+ "show_original": "Mostrar original"
}
},
"friendship": {
@@ -218,10 +237,16 @@
"get_started": "Comenzá",
"log_in": "Iniciar sesión"
},
+ "login": {
+ "title": "Hola de nuevo",
+ "subtitle": "Iniciá sesión en el servidor en donde creaste tu cuenta.",
+ "server_search_field": {
+ "placeholder": "Ingresá la dirección web o buscá tu servidor"
+ }
+ },
"server_picker": {
"title": "Mastodon está compuesto de cuentas en diferentes servidores.",
- "subtitle": "Elegí un servidor basado en tus intereses, región, o de propósitos generales.",
- "subtitle_extend": "Elegí un servidor basado en tus intereses, región, o de propósitos generales. Cada servidor es operado por una organización o individuo totalmente independientes.",
+ "subtitle": "Elegí un servidor basado en tu región, en tus intereses o uno de propósitos generales. Vas a poder interactuar con cualquier cuenta de Mastodon, independientemente del servidor.",
"button": {
"category": {
"all": "Todas",
@@ -248,8 +273,7 @@
"category": "CATEGORÍA"
},
"input": {
- "placeholder": "Buscar servidores",
- "search_servers_or_enter_url": "Buscar servidores o introducir dirección web"
+ "search_servers_or_enter_url": "Buscá comunidades o ingresá la dirección web"
},
"empty_state": {
"finding_servers": "Buscando servidores disponibles…",
@@ -386,7 +410,9 @@
"load_failed": "Falló la descarga",
"upload_failed": "Falló la subida",
"can_not_recognize_this_media_attachment": "No se pudo reconocer este archivo adjunto",
- "attachment_too_large": "Adjunto demasiado grande"
+ "attachment_too_large": "Adjunto demasiado grande",
+ "compressing_state": "Comprimiendo…",
+ "server_processing_state": "Servidor procesando…"
},
"poll": {
"duration_time": "Duración: %s",
@@ -396,7 +422,9 @@
"one_day": "1 día",
"three_days": "3 días",
"seven_days": "7 días",
- "option_number": "Opción %ld"
+ "option_number": "Opción %ld",
+ "the_poll_is_invalid": "La encuesta no es válida",
+ "the_poll_has_empty_option": "La encuesta tiene opción vacía"
},
"content_warning": {
"placeholder": "Escribí una advertencia precisa acá…"
@@ -417,7 +445,9 @@
"custom_emoji_picker": "Selector de emoji personalizado",
"enable_content_warning": "Habilitar advertencia de contenido",
"disable_content_warning": "Deshabilitar advertencia de contenido",
- "post_visibility_menu": "Menú de visibilidad del mensaje"
+ "post_visibility_menu": "Menú de visibilidad del mensaje",
+ "post_options": "Opciones de mensaje",
+ "posting_as": "Enviar como %s"
},
"keyboard": {
"discard_post": "Descartar mensaje",
@@ -433,15 +463,23 @@
"follows_you": "Te sigue"
},
"dashboard": {
- "posts": "mensajes",
- "following": "siguiendo",
- "followers": "seguidores"
+ "my_posts": "mensajes",
+ "my_following": "siguiendo",
+ "my_followers": "seguidores",
+ "other_posts": "mensajes",
+ "other_following": "siguiendo",
+ "other_followers": "seguidores"
},
"fields": {
+ "joined": "En este servidor desde",
"add_row": "Agregar fila",
"placeholder": {
"label": "Nombre de campo",
"content": "Valor de campo"
+ },
+ "verified": {
+ "short": "Verificado en %s",
+ "long": "La propiedad de este enlace fue verificada el %s"
}
},
"segmented_control": {
@@ -706,7 +744,19 @@
"accessibility_hint": "Tocá dos veces para descartar este asistente"
},
"bookmark": {
- "title": "Bookmarks"
+ "title": "Marcadores"
+ },
+ "followed_tags": {
+ "title": "Etiquetas seguidas",
+ "header": {
+ "posts": "mensajes",
+ "participants": "participantes",
+ "posts_today": "mensajes hoy"
+ },
+ "actions": {
+ "follow": "Seguir",
+ "unfollow": "Dejar de seguir"
+ }
}
}
}
diff --git a/Localization/StringsConvertor/input/es.lproj/Localizable.stringsdict b/Localization/StringsConvertor/input/es.lproj/Localizable.stringsdict
index def3d7bba..0a904fcfd 100644
--- a/Localization/StringsConvertor/input/es.lproj/Localizable.stringsdict
+++ b/Localization/StringsConvertor/input/es.lproj/Localizable.stringsdict
@@ -50,6 +50,22 @@
%ld caracteres
+ a11y.plural.count.characters_left
+
+ NSStringLocalizedFormatKey
+ Quedan %#@character_count@
+ character_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 carácter
+ other
+ %ld caracteres
+
+
plural.count.followed_by_and_mutual
NSStringLocalizedFormatKey
diff --git a/Localization/StringsConvertor/input/es.lproj/app.json b/Localization/StringsConvertor/input/es.lproj/app.json
index 7eaff340d..6d19e833b 100644
--- a/Localization/StringsConvertor/input/es.lproj/app.json
+++ b/Localization/StringsConvertor/input/es.lproj/app.json
@@ -51,6 +51,11 @@
"clean_cache": {
"title": "Limpiar Caché",
"message": "Se han limpiado con éxito %s de caché."
+ },
+ "translation_failed": {
+ "title": "Nota",
+ "message": "Error al traducir. Tal vez el administrador no ha habilitado las traducciones en este servidor o este servidor está ejecutando una versión antigua de Mastodon donde las traducciones aún no están soportadas.",
+ "button": "Aceptar"
}
},
"controls": {
@@ -75,9 +80,10 @@
"save_photo": "Guardar foto",
"copy_photo": "Copiar foto",
"sign_in": "Iniciar sesión",
- "sign_up": "Regístrate",
+ "sign_up": "Crear cuenta",
"see_more": "Ver más",
"preview": "Vista previa",
+ "copy": "Copiar",
"share": "Compartir",
"share_user": "Compartir %s",
"share_post": "Compartir publicación",
@@ -91,12 +97,16 @@
"block_domain": "Bloquear %s",
"unblock_domain": "Desbloquear %s",
"settings": "Configuración",
- "delete": "Borrar"
+ "delete": "Borrar",
+ "translate_post": {
+ "title": "Traducir desde %s",
+ "unknown_language": "Desconocido"
+ }
},
"tabs": {
"home": "Inicio",
- "search": "Buscar",
- "notification": "Notificación",
+ "search_and_explore": "Buscar y explorar",
+ "notifications": "Notificaciones",
"profile": "Perfil"
},
"keyboard": {
@@ -132,15 +142,17 @@
"sensitive_content": "Contenido sensible",
"media_content_warning": "Pulsa en cualquier sitio para mostrar",
"tap_to_reveal": "Tocar para revelar",
+ "load_embed": "Cargar incrustado",
+ "link_via_user": "%s vía %s",
"poll": {
"vote": "Vota",
"closed": "Cerrado"
},
"meta_entity": {
- "url": "Link: %s",
- "hashtag": "Hashtag: %s",
- "mention": "Show Profile: %s",
- "email": "Email address: %s"
+ "url": "Enlace: %s",
+ "hashtag": "Etiqueta: %s",
+ "mention": "Mostrar Perfil: %s",
+ "email": "Dirección de correo electrónico: %s"
},
"actions": {
"reply": "Responder",
@@ -153,6 +165,7 @@
"show_image": "Mostrar imagen",
"show_gif": "Mostrar GIF",
"show_video_player": "Mostrar reproductor de vídeo",
+ "share_link_in_post": "Compartir enlace en publicación",
"tap_then_hold_to_show_menu": "Toca, después mantén para mostrar el menú"
},
"tag": {
@@ -168,6 +181,12 @@
"private": "Sólo sus seguidores pueden ver este mensaje.",
"private_from_me": "Sólo mis seguidores pueden ver este mensaje.",
"direct": "Sólo el usuario mencionado puede ver este mensaje."
+ },
+ "translation": {
+ "translated_from": "Traducido desde %s usando %s",
+ "unknown_language": "Desconocido",
+ "unknown_provider": "Desconocido",
+ "show_original": "Mostrar Original"
}
},
"friendship": {
@@ -187,8 +206,8 @@
"unmute_user": "Desmutear a %s",
"muted": "Silenciado",
"edit_info": "Editar Info",
- "show_reblogs": "Show Reblogs",
- "hide_reblogs": "Hide Reblogs"
+ "show_reblogs": "Mostrar reblogs",
+ "hide_reblogs": "Ocultar reblogs"
},
"timeline": {
"filtered": "Filtrado",
@@ -218,10 +237,16 @@
"get_started": "Empezar",
"log_in": "Iniciar sesión"
},
+ "login": {
+ "title": "Bienvenido de nuevo",
+ "subtitle": "Inicie sesión en el servidor en el que creó su cuenta.",
+ "server_search_field": {
+ "placeholder": "Introduzca la URL o busque su servidor"
+ }
+ },
"server_picker": {
"title": "Elige un servidor,\ncualquier servidor.",
- "subtitle": "Elige una comunidad relacionada con tus intereses, con tu región o una más genérica.",
- "subtitle_extend": "Elige una comunidad relacionada con tus intereses, con tu región o una más genérica. Cada comunidad está operada por una organización o individuo completamente independiente.",
+ "subtitle": "Escoge un servidor basado en tu región, intereses o un propósito general. Aún puedes chatear con cualquiera en Mastodon, independientemente de tus servidores.",
"button": {
"category": {
"all": "Todas",
@@ -248,8 +273,7 @@
"category": "CATEGORÍA"
},
"input": {
- "placeholder": "Encuentra un servidor o únete al tuyo propio...",
- "search_servers_or_enter_url": "Buscar servidores o introducir la URL"
+ "search_servers_or_enter_url": "Buscar comunidades o introducir URL"
},
"empty_state": {
"finding_servers": "Encontrando servidores disponibles...",
@@ -383,10 +407,12 @@
"attachment_broken": "Este %s está roto y no puede\nsubirse a Mastodon.",
"description_photo": "Describe la foto para los usuarios con dificultad visual...",
"description_video": "Describe el vídeo para los usuarios con dificultad visual...",
- "load_failed": "Load Failed",
- "upload_failed": "Upload Failed",
- "can_not_recognize_this_media_attachment": "Can not regonize this media attachment",
- "attachment_too_large": "Attachment too large"
+ "load_failed": "Carga fallida",
+ "upload_failed": "Error al cargar",
+ "can_not_recognize_this_media_attachment": "No se puede reconocer este archivo adjunto",
+ "attachment_too_large": "Adjunto demasiado grande",
+ "compressing_state": "Comprimiendo...",
+ "server_processing_state": "Procesando en el servidor..."
},
"poll": {
"duration_time": "Duración: %s",
@@ -396,7 +422,9 @@
"one_day": "1 Día",
"three_days": "4 Días",
"seven_days": "7 Días",
- "option_number": "Opción %ld"
+ "option_number": "Opción %ld",
+ "the_poll_is_invalid": "La encuesta no es válida",
+ "the_poll_has_empty_option": "La encuesta tiene una opción vacía"
},
"content_warning": {
"placeholder": "Escribe una advertencia precisa aquí..."
@@ -417,7 +445,9 @@
"custom_emoji_picker": "Selector de Emojis Personalizados",
"enable_content_warning": "Activar Advertencia de Contenido",
"disable_content_warning": "Desactivar Advertencia de Contenido",
- "post_visibility_menu": "Menú de Visibilidad de la Publicación"
+ "post_visibility_menu": "Menú de Visibilidad de la Publicación",
+ "post_options": "Opciones de Publicación",
+ "posting_as": "Publicado como %s"
},
"keyboard": {
"discard_post": "Descartar Publicación",
@@ -433,15 +463,23 @@
"follows_you": "Te sigue"
},
"dashboard": {
- "posts": "publicaciones",
- "following": "siguiendo",
- "followers": "seguidores"
+ "my_posts": "posts",
+ "my_following": "following",
+ "my_followers": "followers",
+ "other_posts": "posts",
+ "other_following": "following",
+ "other_followers": "followers"
},
"fields": {
+ "joined": "Se unió",
"add_row": "Añadir Fila",
"placeholder": {
"label": "Nombre para el campo",
"content": "Contenido"
+ },
+ "verified": {
+ "short": "Verificado en %s",
+ "long": "La propiedad de este enlace fue verificada el %s"
}
},
"segmented_control": {
@@ -469,12 +507,12 @@
"message": "Confirmar para desbloquear a %s"
},
"confirm_show_reblogs": {
- "title": "Show Reblogs",
- "message": "Confirm to show reblogs"
+ "title": "Mostrar reblogs",
+ "message": "Confirmar para mostrar reblogs"
},
"confirm_hide_reblogs": {
- "title": "Hide Reblogs",
- "message": "Confirm to hide reblogs"
+ "title": "Ocultar reblogs",
+ "message": "Confirmar para ocultar reblogs"
}
},
"accessibility": {
@@ -706,7 +744,19 @@
"accessibility_hint": "Haz doble toque para descartar este asistente"
},
"bookmark": {
- "title": "Bookmarks"
+ "title": "Marcadores"
+ },
+ "followed_tags": {
+ "title": "Etiquetas seguidas",
+ "header": {
+ "posts": "publicaciones",
+ "participants": "participantes",
+ "posts_today": "publicaciones de hoy"
+ },
+ "actions": {
+ "follow": "Seguir",
+ "unfollow": "Dejar de seguir"
+ }
}
}
}
diff --git a/Localization/StringsConvertor/input/eu.lproj/Localizable.stringsdict b/Localization/StringsConvertor/input/eu.lproj/Localizable.stringsdict
index 0159a7da9..404deebd3 100644
--- a/Localization/StringsConvertor/input/eu.lproj/Localizable.stringsdict
+++ b/Localization/StringsConvertor/input/eu.lproj/Localizable.stringsdict
@@ -50,10 +50,26 @@
%ld karaktere
+ a11y.plural.count.characters_left
+
+ NSStringLocalizedFormatKey
+ %#@character_count@ left
+ character_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 character
+ other
+ %ld karaktere
+
+
plural.count.followed_by_and_mutual
NSStringLocalizedFormatKey
- %#@names@%#@count_mutual@
+ %#@names@: "%#@count_mutual@
names
one
diff --git a/Localization/StringsConvertor/input/eu.lproj/app.json b/Localization/StringsConvertor/input/eu.lproj/app.json
index 3f58f522c..e7c5021bb 100644
--- a/Localization/StringsConvertor/input/eu.lproj/app.json
+++ b/Localization/StringsConvertor/input/eu.lproj/app.json
@@ -51,6 +51,11 @@
"clean_cache": {
"title": "Garbitu cache-a",
"message": "Behar bezala garbitu da %s cache-a."
+ },
+ "translation_failed": {
+ "title": "Note",
+ "message": "Translation failed. Maybe the administrator has not enabled translations on this server or this server is running an older version of Mastodon where translations are not yet supported.",
+ "button": "OK"
}
},
"controls": {
@@ -75,9 +80,10 @@
"save_photo": "Gorde argazkia",
"copy_photo": "Kopiatu argazkia",
"sign_in": "Hasi saioa",
- "sign_up": "Eman Izena",
+ "sign_up": "Sortu kontua",
"see_more": "Ikusi gehiago",
"preview": "Aurrebista",
+ "copy": "Copy",
"share": "Partekatu",
"share_user": "Partekatu %s",
"share_post": "Partekatu bidalketa",
@@ -91,12 +97,16 @@
"block_domain": "Blokeatu %s",
"unblock_domain": "Desblokeatu %s",
"settings": "Ezarpenak",
- "delete": "Ezabatu"
+ "delete": "Ezabatu",
+ "translate_post": {
+ "title": "Translate from %s",
+ "unknown_language": "Unknown"
+ }
},
"tabs": {
"home": "Hasiera",
- "search": "Bilatu",
- "notification": "Jakinarazpena",
+ "search_and_explore": "Search and Explore",
+ "notifications": "Notifications",
"profile": "Profila"
},
"keyboard": {
@@ -129,18 +139,20 @@
"show_post": "Erakutsi bidalketa",
"show_user_profile": "Erakutsi erabiltzailearen profila",
"content_warning": "Edukiaren abisua",
- "sensitive_content": "Sensitive Content",
+ "sensitive_content": "Eduki hunkigarria",
"media_content_warning": "Ukitu edonon bistaratzeko",
"tap_to_reveal": "Sakatu erakusteko",
+ "load_embed": "Load Embed",
+ "link_via_user": "%s via %s",
"poll": {
"vote": "Bozkatu",
"closed": "Itxita"
},
"meta_entity": {
- "url": "Link: %s",
- "hashtag": "Hashtag: %s",
- "mention": "Show Profile: %s",
- "email": "Email address: %s"
+ "url": "Lotura: %s",
+ "hashtag": "Traolak: %s",
+ "mention": "Erakutsi Profila: %s",
+ "email": "E-posta helbidea: %s"
},
"actions": {
"reply": "Erantzun",
@@ -153,6 +165,7 @@
"show_image": "Erakutsi irudia",
"show_gif": "Erakutsi GIFa",
"show_video_player": "Erakutsi bideo-erreproduzigailua",
+ "share_link_in_post": "Share Link in Post",
"tap_then_hold_to_show_menu": "Sakatu eta eutsi menua erakusteko"
},
"tag": {
@@ -168,6 +181,12 @@
"private": "Beren jarraitzaileek soilik ikus dezakete bidalketa hau.",
"private_from_me": "Nire jarraitzaileek soilik ikus dezakete bidalketa hau.",
"direct": "Aipatutako erabiltzaileek soilik ikus dezakete bidalketa hau."
+ },
+ "translation": {
+ "translated_from": "Translated from %s using %s",
+ "unknown_language": "Unknown",
+ "unknown_provider": "Unknown",
+ "show_original": "Shown Original"
}
},
"friendship": {
@@ -187,8 +206,8 @@
"unmute_user": "Desmututu %s",
"muted": "Mutututa",
"edit_info": "Editatu informazioa",
- "show_reblogs": "Show Reblogs",
- "hide_reblogs": "Hide Reblogs"
+ "show_reblogs": "Ikusi bultzadak",
+ "hide_reblogs": "Ezkutatu bultzadak"
},
"timeline": {
"filtered": "Iragazita",
@@ -218,10 +237,16 @@
"get_started": "Nola hasi",
"log_in": "Hasi saioa"
},
+ "login": {
+ "title": "Ongi etorri berriro ere",
+ "subtitle": "Log you in on the server you created your account on.",
+ "server_search_field": {
+ "placeholder": "Enter URL or search for your server"
+ }
+ },
"server_picker": {
"title": "Aukeratu zerbitzari bat,\nedozein zerbitzari.",
- "subtitle": "Aukeratu komunitate bat zure interes edo lurraldearen arabera, edo erabilera orokorreko bat.",
- "subtitle_extend": "Aukeratu komunitate bat zure interes edo lurraldearen arabera, edo erabilera orokorreko bat. Komunitate bakoitza erakunde edo norbanako independente batek kudeatzen du.",
+ "subtitle": "Pick a server based on your region, interests, or a general purpose one. You can still chat with anyone on Mastodon, regardless of your servers.",
"button": {
"category": {
"all": "Guztiak",
@@ -248,8 +273,7 @@
"category": "KATEGORIA"
},
"input": {
- "placeholder": "Bilatu zerbitzari bat edo sortu zurea...",
- "search_servers_or_enter_url": "Search servers or enter URL"
+ "search_servers_or_enter_url": "Search communities or enter URL"
},
"empty_state": {
"finding_servers": "Erabilgarri dauden zerbitzariak bilatzen...",
@@ -259,7 +283,7 @@
},
"register": {
"title": "Hitz egin iezaguzu zuri buruz.",
- "lets_get_you_set_up_on_domain": "Let’s get you set up on %s",
+ "lets_get_you_set_up_on_domain": "%s zerbitzariko kontua prestatuko dizugu",
"input": {
"avatar": {
"delete": "Ezabatu"
@@ -330,7 +354,7 @@
"confirm_email": {
"title": "Eta azkenik...",
"subtitle": "Sakatu epostaz bidali dizugun loturan zure kontua egiaztatzeko.",
- "tap_the_link_we_emailed_to_you_to_verify_your_account": "Tap the link we emailed to you to verify your account",
+ "tap_the_link_we_emailed_to_you_to_verify_your_account": "Sakatu epostaz bidali dizugun loturan zure kontua egiaztatzeko",
"button": {
"open_email_app": "Ireki eposta aplikazioa",
"resend": "Berbidali"
@@ -355,7 +379,7 @@
"published": "Argitaratua!",
"Publishing": "Bidalketa argitaratzen...",
"accessibility": {
- "logo_label": "Logo Button",
+ "logo_label": "Logo botoia",
"logo_hint": "Tap to scroll to top and tap again to previous location"
}
}
@@ -384,9 +408,11 @@
"description_photo": "Deskribatu argazkia ikusmen arazoak dituztenentzat...",
"description_video": "Deskribatu bideoa ikusmen arazoak dituztenentzat...",
"load_failed": "Load Failed",
- "upload_failed": "Upload Failed",
- "can_not_recognize_this_media_attachment": "Can not regonize this media attachment",
- "attachment_too_large": "Attachment too large"
+ "upload_failed": "Kargatzeak huts egin du",
+ "can_not_recognize_this_media_attachment": "Can not recognize this media attachment",
+ "attachment_too_large": "Eranskina handiegia da",
+ "compressing_state": "Konprimatzen...",
+ "server_processing_state": "Server Processing..."
},
"poll": {
"duration_time": "Iraupena: %s",
@@ -396,7 +422,9 @@
"one_day": "Egun 1",
"three_days": "3 egun",
"seven_days": "7 egun",
- "option_number": "%ld aukera"
+ "option_number": "%ld aukera",
+ "the_poll_is_invalid": "Inkesta ez da balekoa",
+ "the_poll_has_empty_option": "The poll has empty option"
},
"content_warning": {
"placeholder": "Idatzi abisu zehatz bat hemen..."
@@ -417,7 +445,9 @@
"custom_emoji_picker": "Emoji pertsonalizatuen hautatzailea",
"enable_content_warning": "Gaitu edukiaren abisua",
"disable_content_warning": "Desgaitu edukiaren abisua",
- "post_visibility_menu": "Bidalketaren ikusgaitasunaren menua"
+ "post_visibility_menu": "Bidalketaren ikusgaitasunaren menua",
+ "post_options": "Bildalketaren aukerak",
+ "posting_as": "Posting as %s"
},
"keyboard": {
"discard_post": "Baztertu bidalketa",
@@ -430,18 +460,26 @@
},
"profile": {
"header": {
- "follows_you": "Follows You"
+ "follows_you": "Jarraitzen zaitu"
},
"dashboard": {
- "posts": "bidalketa",
- "following": "jarraitzen",
- "followers": "jarraitzaile"
+ "my_posts": "posts",
+ "my_following": "following",
+ "my_followers": "followers",
+ "other_posts": "posts",
+ "other_following": "following",
+ "other_followers": "followers"
},
"fields": {
+ "joined": "Joined",
"add_row": "Gehitu errenkada",
"placeholder": {
"label": "Etiketa",
"content": "Edukia"
+ },
+ "verified": {
+ "short": "Verified on %s",
+ "long": "Esteka honen jabetzaren egiaztaketa data: %s"
}
},
"segmented_control": {
@@ -469,12 +507,12 @@
"message": "Berretsi %s desblokeatzea"
},
"confirm_show_reblogs": {
- "title": "Show Reblogs",
- "message": "Confirm to show reblogs"
+ "title": "Ikusi bultzadak",
+ "message": "Berretsi birbidalketak ikustea"
},
"confirm_hide_reblogs": {
- "title": "Hide Reblogs",
- "message": "Confirm to hide reblogs"
+ "title": "Ezkutatu bultzadak",
+ "message": "Berretsi birbidalketak ezkutatzea"
}
},
"accessibility": {
@@ -485,11 +523,11 @@
}
},
"follower": {
- "title": "follower",
+ "title": "jarraitzaile",
"footer": "Beste zerbitzarietako jarraitzaileak ez dira bistaratzen."
},
"following": {
- "title": "following",
+ "title": "jarraitzen",
"footer": "Beste zerbitzarietan jarraitutakoak ez dira bistaratzen."
},
"familiarFollowers": {
@@ -540,10 +578,10 @@
"posts": "Argitalpenak",
"hashtags": "Traolak",
"news": "Albisteak",
- "community": "Community",
+ "community": "Komunitatea",
"for_you": "Zuretzat"
},
- "intro": "These are the posts gaining traction in your corner of Mastodon."
+ "intro": "Hauek dira zure Mastodon txokoan beraien lekua hartzen ari diren argitalpenak."
},
"favorite": {
"title": "Zure gogokoak"
@@ -566,10 +604,10 @@
"show_mentions": "Erakutsi aipamenak"
},
"follow_request": {
- "accept": "Accept",
- "accepted": "Accepted",
- "reject": "reject",
- "rejected": "Rejected"
+ "accept": "Onartu",
+ "accepted": "Onartuta",
+ "reject": "ukatu",
+ "rejected": "Ukatua"
}
},
"thread": {
@@ -646,46 +684,46 @@
"text_placeholder": "Idatzi edo itsatsi iruzkin gehigarriak",
"reported": "SALATUA",
"step_one": {
- "step_1_of_4": "Step 1 of 4",
- "whats_wrong_with_this_post": "What's wrong with this post?",
- "whats_wrong_with_this_account": "What's wrong with this account?",
- "whats_wrong_with_this_username": "What's wrong with %s?",
- "select_the_best_match": "Select the best match",
- "i_dont_like_it": "I don’t like it",
- "it_is_not_something_you_want_to_see": "It is not something you want to see",
- "its_spam": "It’s spam",
- "malicious_links_fake_engagement_or_repetetive_replies": "Malicious links, fake engagement, or repetetive replies",
- "it_violates_server_rules": "It violates server rules",
- "you_are_aware_that_it_breaks_specific_rules": "You are aware that it breaks specific rules",
- "its_something_else": "It’s something else",
- "the_issue_does_not_fit_into_other_categories": "The issue does not fit into other categories"
+ "step_1_of_4": "1. urratsa 4tik",
+ "whats_wrong_with_this_post": "Zer du txarra argitalpen honek?",
+ "whats_wrong_with_this_account": "Zer du txarra kontu honek?",
+ "whats_wrong_with_this_username": "Zer du txarra %s?",
+ "select_the_best_match": "Aukeratu egokiena",
+ "i_dont_like_it": "Ez dut gustukoa",
+ "it_is_not_something_you_want_to_see": "Ikusi nahi ez dudan zerbait da",
+ "its_spam": "Spama da",
+ "malicious_links_fake_engagement_or_repetetive_replies": "Esteka maltzurrak, gezurrezko elkarrekintzak edo erantzun errepikakorrak",
+ "it_violates_server_rules": "Zerbitzariaren arauak hausten ditu",
+ "you_are_aware_that_it_breaks_specific_rules": "Arau zehatzak urratzen dituela badakizu",
+ "its_something_else": "Beste zerbait da",
+ "the_issue_does_not_fit_into_other_categories": "Arazoa ezin da beste kategorietan sailkatu"
},
"step_two": {
- "step_2_of_4": "Step 2 of 4",
- "which_rules_are_being_violated": "Which rules are being violated?",
- "select_all_that_apply": "Select all that apply",
+ "step_2_of_4": "2. urratsa 4tik",
+ "which_rules_are_being_violated": "Ze arau hautsi ditu?",
+ "select_all_that_apply": "Hautatu dagozkion guztiak",
"i_just_don’t_like_it": "I just don’t like it"
},
"step_three": {
- "step_3_of_4": "Step 3 of 4",
- "are_there_any_posts_that_back_up_this_report": "Are there any posts that back up this report?",
- "select_all_that_apply": "Select all that apply"
+ "step_3_of_4": "3. urratsa 4tik",
+ "are_there_any_posts_that_back_up_this_report": "Salaketa hau babesten duen bidalketarik badago?",
+ "select_all_that_apply": "Hautatu dagozkion guztiak"
},
"step_four": {
- "step_4_of_4": "Step 4 of 4",
- "is_there_anything_else_we_should_know": "Is there anything else we should know?"
+ "step_4_of_4": "4. urratsa 4tik",
+ "is_there_anything_else_we_should_know": "Beste zerbait jakin beharko genuke?"
},
"step_final": {
- "dont_want_to_see_this": "Don’t want to see this?",
- "when_you_see_something_you_dont_like_on_mastodon_you_can_remove_the_person_from_your_experience.": "When you see something you don’t like on Mastodon, you can remove the person from your experience.",
- "unfollow": "Unfollow",
+ "dont_want_to_see_this": "Ez duzu hau ikusi nahi?",
+ "when_you_see_something_you_dont_like_on_mastodon_you_can_remove_the_person_from_your_experience.": "Mastodonen gustuko ez duzun zerbait ikusten duzunean, zure esperientziatik atera dezakezu pertsona hori.",
+ "unfollow": "Utzi jarraitzeari",
"unfollowed": "Unfollowed",
- "unfollow_user": "Unfollow %s",
- "mute_user": "Mute %s",
- "you_wont_see_their_posts_or_reblogs_in_your_home_feed_they_wont_know_they_ve_been_muted": "You won’t see their posts or reblogs in your home feed. They won’t know they’ve been muted.",
- "block_user": "Block %s",
- "they_will_no_longer_be_able_to_follow_or_see_your_posts_but_they_can_see_if_theyve_been_blocked": "They will no longer be able to follow or see your posts, but they can see if they’ve been blocked.",
- "while_we_review_this_you_can_take_action_against_user": "While we review this, you can take action against %s"
+ "unfollow_user": "%s jarraitzeari utzi",
+ "mute_user": "Mututu %s",
+ "you_wont_see_their_posts_or_reblogs_in_your_home_feed_they_wont_know_they_ve_been_muted": "Ez dituzu bere bidalketa eta birbidalketak zure hasierako jarioan ikusiko. Ez dute jakingo isilarazi dituztenik.",
+ "block_user": "Blokeatu %s",
+ "they_will_no_longer_be_able_to_follow_or_see_your_posts_but_they_can_see_if_theyve_been_blocked": "Ezin izango dituzte zure bidalketak jarraitu edo ikusi, baina blokeatuta dauden ikusi ahal izango dute.",
+ "while_we_review_this_you_can_take_action_against_user": "Hau berrikusten dugun bitartean, %s erabiltzailearen aurkako neurriak hartu ditzakezu"
}
},
"preview": {
@@ -706,7 +744,19 @@
"accessibility_hint": "Ukitu birritan morroi hau baztertzeko"
},
"bookmark": {
- "title": "Bookmarks"
+ "title": "Laster-markak"
+ },
+ "followed_tags": {
+ "title": "Followed Tags",
+ "header": {
+ "posts": "posts",
+ "participants": "participants",
+ "posts_today": "posts today"
+ },
+ "actions": {
+ "follow": "Follow",
+ "unfollow": "Unfollow"
+ }
}
}
}
diff --git a/Localization/StringsConvertor/input/fi.lproj/Localizable.stringsdict b/Localization/StringsConvertor/input/fi.lproj/Localizable.stringsdict
index 8048edf2d..ccfee35c9 100644
--- a/Localization/StringsConvertor/input/fi.lproj/Localizable.stringsdict
+++ b/Localization/StringsConvertor/input/fi.lproj/Localizable.stringsdict
@@ -50,6 +50,22 @@
%ld merkkiä
+ a11y.plural.count.characters_left
+
+ NSStringLocalizedFormatKey
+ %#@character_count@ left
+ character_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 character
+ other
+ %ld characters
+
+
plural.count.followed_by_and_mutual
NSStringLocalizedFormatKey
diff --git a/Localization/StringsConvertor/input/fi.lproj/app.json b/Localization/StringsConvertor/input/fi.lproj/app.json
index a42642786..f6beff049 100644
--- a/Localization/StringsConvertor/input/fi.lproj/app.json
+++ b/Localization/StringsConvertor/input/fi.lproj/app.json
@@ -51,6 +51,11 @@
"clean_cache": {
"title": "Puhdista välimuisti",
"message": "%s välimuisti tyhjennetty onnistuneesti."
+ },
+ "translation_failed": {
+ "title": "Note",
+ "message": "Translation failed. Maybe the administrator has not enabled translations on this server or this server is running an older version of Mastodon where translations are not yet supported.",
+ "button": "OK"
}
},
"controls": {
@@ -74,10 +79,11 @@
"take_photo": "Ota kuva",
"save_photo": "Tallenna kuva",
"copy_photo": "Kopioi kuva",
- "sign_in": "Kirjaudu sisään",
- "sign_up": "Rekisteröidy",
+ "sign_in": "Log in",
+ "sign_up": "Create account",
"see_more": "Näytä lisää",
"preview": "Esikatselu",
+ "copy": "Copy",
"share": "Jaa",
"share_user": "Jaa %s",
"share_post": "Jaa julkaisu",
@@ -91,12 +97,16 @@
"block_domain": "Estä %s",
"unblock_domain": "Poista esto %s",
"settings": "Asetukset",
- "delete": "Poista"
+ "delete": "Poista",
+ "translate_post": {
+ "title": "Translate from %s",
+ "unknown_language": "Unknown"
+ }
},
"tabs": {
"home": "Koti",
- "search": "Haku",
- "notification": "Ilmoitus",
+ "search_and_explore": "Search and Explore",
+ "notifications": "Notifications",
"profile": "Profiili"
},
"keyboard": {
@@ -132,6 +142,8 @@
"sensitive_content": "Sensitive Content",
"media_content_warning": "Napauta mistä tahansa paljastaaksesi",
"tap_to_reveal": "Tap to reveal",
+ "load_embed": "Load Embed",
+ "link_via_user": "%s via %s",
"poll": {
"vote": "Vote",
"closed": "Suljettu"
@@ -153,6 +165,7 @@
"show_image": "Show image",
"show_gif": "Show GIF",
"show_video_player": "Show video player",
+ "share_link_in_post": "Share Link in Post",
"tap_then_hold_to_show_menu": "Tap then hold to show menu"
},
"tag": {
@@ -168,6 +181,12 @@
"private": "Only their followers can see this post.",
"private_from_me": "Only my followers can see this post.",
"direct": "Only mentioned user can see this post."
+ },
+ "translation": {
+ "translated_from": "Käännetty kielestä %s palvelulla %s",
+ "unknown_language": "Unknown",
+ "unknown_provider": "Tuntematon",
+ "show_original": "Shown Original"
}
},
"friendship": {
@@ -218,10 +237,16 @@
"get_started": "Get Started",
"log_in": "Log In"
},
+ "login": {
+ "title": "Welcome back",
+ "subtitle": "Log you in on the server you created your account on.",
+ "server_search_field": {
+ "placeholder": "Enter URL or search for your server"
+ }
+ },
"server_picker": {
"title": "Valitse palvelin,\nmikä tahansa palvelin.",
- "subtitle": "Pick a server based on your interests, region, or a general purpose one.",
- "subtitle_extend": "Pick a server based on your interests, region, or a general purpose one. Each server is operated by an entirely independent organization or individual.",
+ "subtitle": "Pick a server based on your region, interests, or a general purpose one. You can still chat with anyone on Mastodon, regardless of your servers.",
"button": {
"category": {
"all": "Kaikki",
@@ -248,8 +273,7 @@
"category": "KATEGORIA"
},
"input": {
- "placeholder": "Etsi palvelin tai liity omaan...",
- "search_servers_or_enter_url": "Search servers or enter URL"
+ "search_servers_or_enter_url": "Search communities or enter URL"
},
"empty_state": {
"finding_servers": "Etsistään saatavilla olevia palvelimia...",
@@ -385,8 +409,10 @@
"description_video": "Kuvaile video näkövammaisille...",
"load_failed": "Load Failed",
"upload_failed": "Upload Failed",
- "can_not_recognize_this_media_attachment": "Can not regonize this media attachment",
- "attachment_too_large": "Attachment too large"
+ "can_not_recognize_this_media_attachment": "Can not recognize this media attachment",
+ "attachment_too_large": "Attachment too large",
+ "compressing_state": "Compressing...",
+ "server_processing_state": "Server Processing..."
},
"poll": {
"duration_time": "Kesto: %s",
@@ -396,7 +422,9 @@
"one_day": "1 päivä",
"three_days": "3 päivää",
"seven_days": "7 päivää",
- "option_number": "Vaihtoehto %ld"
+ "option_number": "Vaihtoehto %ld",
+ "the_poll_is_invalid": "The poll is invalid",
+ "the_poll_has_empty_option": "The poll has empty option"
},
"content_warning": {
"placeholder": "Kirjoita tarkka varoitus tähän..."
@@ -417,7 +445,9 @@
"custom_emoji_picker": "Mukautettu emojivalitsin",
"enable_content_warning": "Ota sisältövaroitus käyttöön",
"disable_content_warning": "Poista sisältövaroitus käytöstä",
- "post_visibility_menu": "Julkaisun näkyvyysvalikko"
+ "post_visibility_menu": "Julkaisun näkyvyysvalikko",
+ "post_options": "Post Options",
+ "posting_as": "Posting as %s"
},
"keyboard": {
"discard_post": "Hylkää julkaisu",
@@ -433,15 +463,23 @@
"follows_you": "Follows You"
},
"dashboard": {
- "posts": "julkaisut",
- "following": "seurataan",
- "followers": "seuraajat"
+ "my_posts": "julkaisut",
+ "my_following": "seurattavat",
+ "my_followers": "seuraajat",
+ "other_posts": "julkaisut",
+ "other_following": "seurattavat",
+ "other_followers": "seuraajat"
},
"fields": {
+ "joined": "Joined",
"add_row": "Lisää rivi",
"placeholder": {
"label": "Nimi",
"content": "Sisältö"
+ },
+ "verified": {
+ "short": "Verified on %s",
+ "long": "Ownership of this link was checked on %s"
}
},
"segmented_control": {
@@ -707,6 +745,18 @@
},
"bookmark": {
"title": "Bookmarks"
+ },
+ "followed_tags": {
+ "title": "Followed Tags",
+ "header": {
+ "posts": "posts",
+ "participants": "participants",
+ "posts_today": "posts today"
+ },
+ "actions": {
+ "follow": "Follow",
+ "unfollow": "Unfollow"
+ }
}
}
}
diff --git a/Localization/StringsConvertor/input/fr.lproj/Localizable.stringsdict b/Localization/StringsConvertor/input/fr.lproj/Localizable.stringsdict
index d9d860a47..4eb068697 100644
--- a/Localization/StringsConvertor/input/fr.lproj/Localizable.stringsdict
+++ b/Localization/StringsConvertor/input/fr.lproj/Localizable.stringsdict
@@ -50,6 +50,22 @@
%ld caractères
+ a11y.plural.count.characters_left
+
+ NSStringLocalizedFormatKey
+ %#@character_count@ restants
+ character_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 caractère
+ other
+ %ld caractères
+
+
plural.count.followed_by_and_mutual
NSStringLocalizedFormatKey
diff --git a/Localization/StringsConvertor/input/fr.lproj/app.json b/Localization/StringsConvertor/input/fr.lproj/app.json
index 25bb6e511..1d998ba77 100644
--- a/Localization/StringsConvertor/input/fr.lproj/app.json
+++ b/Localization/StringsConvertor/input/fr.lproj/app.json
@@ -51,6 +51,11 @@
"clean_cache": {
"title": "Vider le cache",
"message": "Cache de %s effacé avec succès."
+ },
+ "translation_failed": {
+ "title": "Note",
+ "message": "La traduction a échoué. Peut-être que l'administrateur n'a pas activé les traductions sur ce serveur ou que ce serveur utilise une ancienne version de Mastodon où les traductions ne sont pas encore prises en charge.",
+ "button": "OK"
}
},
"controls": {
@@ -78,6 +83,7 @@
"sign_up": "Créer un compte",
"see_more": "Voir plus",
"preview": "Aperçu",
+ "copy": "Copier",
"share": "Partager",
"share_user": "Partager %s",
"share_post": "Partager la publication",
@@ -91,12 +97,16 @@
"block_domain": "Bloquer %s",
"unblock_domain": "Débloquer %s",
"settings": "Paramètres",
- "delete": "Supprimer"
+ "delete": "Supprimer",
+ "translate_post": {
+ "title": "Traduit depuis %s",
+ "unknown_language": "Inconnu"
+ }
},
"tabs": {
"home": "Accueil",
- "search": "Rechercher",
- "notification": "Notification",
+ "search_and_explore": "Rechercher et explorer",
+ "notifications": "Notifications",
"profile": "Profil"
},
"keyboard": {
@@ -132,6 +142,8 @@
"sensitive_content": "Contenu sensible",
"media_content_warning": "Tapotez n’importe où pour révéler la publication",
"tap_to_reveal": "Appuyer pour afficher",
+ "load_embed": "Charger l'intégration",
+ "link_via_user": "%s via %s",
"poll": {
"vote": "Voter",
"closed": "Fermé"
@@ -153,6 +165,7 @@
"show_image": "Afficher l’image",
"show_gif": "Afficher le GIF",
"show_video_player": "Afficher le lecteur vidéo",
+ "share_link_in_post": "Partager le lien dans le message",
"tap_then_hold_to_show_menu": "Appuyez et maintenez pour afficher le menu"
},
"tag": {
@@ -168,6 +181,12 @@
"private": "Seul·e·s leurs abonné·e·s peuvent voir ce message.",
"private_from_me": "Seul·e·s mes abonné·e·s peuvent voir ce message.",
"direct": "Seul·e l’utilisateur·rice mentionnée peut voir ce message."
+ },
+ "translation": {
+ "translated_from": "Traduit de %s en utilisant %s",
+ "unknown_language": "Inconnu",
+ "unknown_provider": "Inconnu",
+ "show_original": "Afficher l’original"
}
},
"friendship": {
@@ -218,10 +237,16 @@
"get_started": "Prise en main",
"log_in": "Se connecter"
},
+ "login": {
+ "title": "Content de vous revoir",
+ "subtitle": "Connectez-vous sur le serveur sur lequel vous avez créé votre compte.",
+ "server_search_field": {
+ "placeholder": "Entrez l'URL ou recherchez votre serveur"
+ }
+ },
"server_picker": {
"title": "Choisissez un serveur,\nn'importe quel serveur.",
- "subtitle": "Choisissez une communauté en fonction de vos intérêts, de votre région ou de votre objectif général.",
- "subtitle_extend": "Choisissez une communauté basée sur vos intérêts, votre région ou un but général. Chaque communauté est gérée par une organisation ou un individu entièrement indépendant.",
+ "subtitle": "Choisissez un serveur basé sur votre région, vos intérêts ou un généraliste. Vous pouvez toujours discuter avec n'importe qui sur Mastodon, indépendamment de vos serveurs.",
"button": {
"category": {
"all": "Tout",
@@ -248,8 +273,7 @@
"category": "CATÉGORIE"
},
"input": {
- "placeholder": "Trouvez un serveur ou rejoignez le vôtre...",
- "search_servers_or_enter_url": "Rechercher des serveurs ou entrer une URL"
+ "search_servers_or_enter_url": "Rechercher parmi les communautés ou renseigner une URL"
},
"empty_state": {
"finding_servers": "Recherche des serveurs disponibles...",
@@ -383,10 +407,12 @@
"attachment_broken": "Ce %s est brisé et ne peut pas être\ntéléversé sur Mastodon.",
"description_photo": "Décrire cette photo pour les personnes malvoyantes...",
"description_video": "Décrire cette vidéo pour les personnes malvoyantes...",
- "load_failed": "Load Failed",
- "upload_failed": "Upload Failed",
- "can_not_recognize_this_media_attachment": "Can not regonize this media attachment",
- "attachment_too_large": "Attachment too large"
+ "load_failed": "Échec du chargement",
+ "upload_failed": "Échec de l’envoi",
+ "can_not_recognize_this_media_attachment": "Impossible de reconnaître cette pièce jointe",
+ "attachment_too_large": "La pièce jointe est trop volumineuse",
+ "compressing_state": "Compression...",
+ "server_processing_state": "Traitement du serveur..."
},
"poll": {
"duration_time": "Durée: %s",
@@ -396,7 +422,9 @@
"one_day": "1 Jour",
"three_days": "3 jour",
"seven_days": "7 jour",
- "option_number": "Option %ld"
+ "option_number": "Option %ld",
+ "the_poll_is_invalid": "Le sondage est invalide",
+ "the_poll_has_empty_option": "Le sondage n'a pas d'options"
},
"content_warning": {
"placeholder": "Écrivez un avertissement précis ici..."
@@ -417,7 +445,9 @@
"custom_emoji_picker": "Sélecteur d’émojis personnalisés",
"enable_content_warning": "Basculer l’avertissement de contenu",
"disable_content_warning": "Désactiver l'avertissement de contenu",
- "post_visibility_menu": "Menu de Visibilité de la publication"
+ "post_visibility_menu": "Menu de Visibilité de la publication",
+ "post_options": "Options de publication",
+ "posting_as": "Publié en tant que %s"
},
"keyboard": {
"discard_post": "Rejeter la publication",
@@ -433,15 +463,23 @@
"follows_you": "Vous suit"
},
"dashboard": {
- "posts": "publications",
- "following": "abonnements",
- "followers": "abonnés"
+ "my_posts": "messages",
+ "my_following": "abonnement",
+ "my_followers": "abonnés",
+ "other_posts": "publications",
+ "other_following": "abonnement",
+ "other_followers": "abonnés"
},
"fields": {
+ "joined": "Ici depuis",
"add_row": "Ajouter une rangée",
"placeholder": {
"label": "Étiquette",
"content": "Contenu"
+ },
+ "verified": {
+ "short": "Vérifié le %s",
+ "long": "La propriété de ce lien a été vérifiée le %s"
}
},
"segmented_control": {
@@ -707,6 +745,18 @@
},
"bookmark": {
"title": "Favoris"
+ },
+ "followed_tags": {
+ "title": "Tags suivis",
+ "header": {
+ "posts": "messages",
+ "participants": "participants",
+ "posts_today": "messages aujourd'hui"
+ },
+ "actions": {
+ "follow": "Suivre",
+ "unfollow": "Ne plus suivre"
+ }
}
}
}
diff --git a/Localization/StringsConvertor/input/gd.lproj/Localizable.stringsdict b/Localization/StringsConvertor/input/gd.lproj/Localizable.stringsdict
index d0ccb5f41..9b3e69ea7 100644
--- a/Localization/StringsConvertor/input/gd.lproj/Localizable.stringsdict
+++ b/Localization/StringsConvertor/input/gd.lproj/Localizable.stringsdict
@@ -62,6 +62,26 @@
%ld caractar
+ a11y.plural.count.characters_left
+
+ NSStringLocalizedFormatKey
+ %#@character_count@ air fhàgail
+ character_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ %ld charactar
+ two
+ %ld charactar
+ few
+ %ld caractaran
+ other
+ %ld caractar
+
+
plural.count.followed_by_and_mutual
NSStringLocalizedFormatKey
diff --git a/Localization/StringsConvertor/input/gd.lproj/app.json b/Localization/StringsConvertor/input/gd.lproj/app.json
index c1d17f813..5f27b8f00 100644
--- a/Localization/StringsConvertor/input/gd.lproj/app.json
+++ b/Localization/StringsConvertor/input/gd.lproj/app.json
@@ -51,6 +51,11 @@
"clean_cache": {
"title": "Falamhaich an tasgadan",
"message": "Chaidh %s a thasgadan fhalamhachadh."
+ },
+ "translation_failed": {
+ "title": "Note",
+ "message": "Translation failed. Maybe the administrator has not enabled translations on this server or this server is running an older version of Mastodon where translations are not yet supported.",
+ "button": "OK"
}
},
"controls": {
@@ -75,9 +80,10 @@
"save_photo": "Sàbhail an dealbh",
"copy_photo": "Dèan lethbhreac dhen dealbh",
"sign_in": "Clàraich a-steach",
- "sign_up": "Clàraich leinn",
+ "sign_up": "Cruthaich cunntas",
"see_more": "Seall a bharrachd",
"preview": "Ro-sheall",
+ "copy": "Copy",
"share": "Co-roinn",
"share_user": "Co-roinn %s",
"share_post": "Co-roinn am post",
@@ -91,12 +97,16 @@
"block_domain": "Bac %s",
"unblock_domain": "Dì-bhac %s",
"settings": "Roghainnean",
- "delete": "Sguab às"
+ "delete": "Sguab às",
+ "translate_post": {
+ "title": "Translate from %s",
+ "unknown_language": "Unknown"
+ }
},
"tabs": {
"home": "Dachaigh",
- "search": "Lorg",
- "notification": "Brath",
+ "search_and_explore": "Search and Explore",
+ "notifications": "Notifications",
"profile": "Pròifil"
},
"keyboard": {
@@ -132,15 +142,17 @@
"sensitive_content": "Susbaint fhrionasach",
"media_content_warning": "Thoir gnogag àite sam bith gus a nochdadh",
"tap_to_reveal": "Thoir gnogag gus a nochdadh",
+ "load_embed": "Load Embed",
+ "link_via_user": "%s via %s",
"poll": {
"vote": "Cuir bhòt",
"closed": "Dùinte"
},
"meta_entity": {
- "url": "Link: %s",
- "hashtag": "Hashtag: %s",
- "mention": "Show Profile: %s",
- "email": "Email address: %s"
+ "url": "Ceangal: %s",
+ "hashtag": "Taga hais: %s",
+ "mention": "Seall a’ phròifil: %s",
+ "email": "Seòladh puist-d: %s"
},
"actions": {
"reply": "Freagair",
@@ -153,6 +165,7 @@
"show_image": "Seall an dealbh",
"show_gif": "Seall an GIF",
"show_video_player": "Seall cluicheadair video",
+ "share_link_in_post": "Share Link in Post",
"tap_then_hold_to_show_menu": "Thoir gnogag ’s cùm sìos a shealltainn a’ chlàir-thaice"
},
"tag": {
@@ -168,6 +181,12 @@
"private": "Chan fhaic ach an luchd-leantainn aca am post seo.",
"private_from_me": "Chan fhaic ach an luchd-leantainn agam am post seo.",
"direct": "Chan fhaic ach an cleachdaiche air an dugadh iomradh am post seo."
+ },
+ "translation": {
+ "translated_from": "Translated from %s using %s",
+ "unknown_language": "Unknown",
+ "unknown_provider": "Unknown",
+ "show_original": "Shown Original"
}
},
"friendship": {
@@ -187,8 +206,8 @@
"unmute_user": "Dì-mhùch %s",
"muted": "’Ga mhùchadh",
"edit_info": "Deasaich",
- "show_reblogs": "Show Reblogs",
- "hide_reblogs": "Hide Reblogs"
+ "show_reblogs": "Seall na brosnachaidhean",
+ "hide_reblogs": "Falaich na brosnachaidhean"
},
"timeline": {
"filtered": "Criathraichte",
@@ -218,10 +237,16 @@
"get_started": "Dèan toiseach-tòiseachaidh",
"log_in": "Clàraich a-steach"
},
+ "login": {
+ "title": "Fàilte air ais",
+ "subtitle": "Clàraich a-steach air an fhrithealaiche far an do chruthaich thu an cunntas agad.",
+ "server_search_field": {
+ "placeholder": "Cuir a-steach URL an fhrithealaiche agad"
+ }
+ },
"server_picker": {
"title": "Tha cleachdaichean Mhastodon air iomadh frithealaiche eadar-dhealaichte.",
- "subtitle": "Tagh frithealaiche stèidhichte air d’ ùidhean, air far a bheil thu no fear coitcheann.",
- "subtitle_extend": "Tagh frithealaiche stèidhichte air d’ ùidhean, air far a bheil thu no fear coitcheann. Tha gach frithealaiche fo stiùireadh buidhinn no neach neo-eisimeilich fa leth.",
+ "subtitle": "Tagh frithealaiche stèidhichte air na sgìre agad, d’ ùidhean, air far a bheil thu no fear coitcheann. ’S urrainn dhut fhathast conaltradh le duine sam bith air Mastodon ge b’ e na frithealaichean agaibh-se.",
"button": {
"category": {
"all": "Na h-uile",
@@ -248,8 +273,7 @@
"category": "ROINN-SEÒRSA"
},
"input": {
- "placeholder": "Lorg frithealaiche",
- "search_servers_or_enter_url": "Lorg frithealaiche no cuir a-steach URL"
+ "search_servers_or_enter_url": "Lorg coimhearsnachd no cuir a-steach URL"
},
"empty_state": {
"finding_servers": "A’ lorg nam frithealaichean ri am faighinn…",
@@ -383,10 +407,12 @@
"attachment_broken": "Seo %s a tha briste is cha ghabh\na luchdadh suas gu Mastodon.",
"description_photo": "Mìnich an dealbh dhan fheadhainn air a bheil cion-lèirsinne…",
"description_video": "Mìnich a’ video dhan fheadhainn air a bheil cion-lèirsinne…",
- "load_failed": "Load Failed",
- "upload_failed": "Upload Failed",
- "can_not_recognize_this_media_attachment": "Can not regonize this media attachment",
- "attachment_too_large": "Attachment too large"
+ "load_failed": "Dh’fhàillig leis an luchdadh",
+ "upload_failed": "Dh’fhàillig leis an luchdadh suas",
+ "can_not_recognize_this_media_attachment": "Cha do dh’aithnich sinn an ceanglachan meadhain seo",
+ "attachment_too_large": "Tha an ceanglachan ro mhòr",
+ "compressing_state": "’Ga dhùmhlachadh…",
+ "server_processing_state": "Tha am frithealaiche ’ga phròiseasadh…"
},
"poll": {
"duration_time": "Faide: %s",
@@ -396,7 +422,9 @@
"one_day": "Latha",
"three_days": "3 làithean",
"seven_days": "Seachdain",
- "option_number": "Roghainn %ld"
+ "option_number": "Roghainn %ld",
+ "the_poll_is_invalid": "Tha an cunntas-bheachd mì-dhligheach",
+ "the_poll_has_empty_option": "Tha roghainn fhalamh aig a’ chunntas-bheachd"
},
"content_warning": {
"placeholder": "Sgrìobh rabhadh pongail an-seo…"
@@ -417,7 +445,9 @@
"custom_emoji_picker": "Roghnaichear nan Emoji gnàthaichte",
"enable_content_warning": "Cuir rabhadh susbainte an comas",
"disable_content_warning": "Cuir rabhadh susbainte à comas",
- "post_visibility_menu": "Clàr-taice faicsinneachd a’ phuist"
+ "post_visibility_menu": "Clàr-taice faicsinneachd a’ phuist",
+ "post_options": "Roghainnean postaidh",
+ "posting_as": "A’ postadh mar %s"
},
"keyboard": {
"discard_post": "Tilg air falbh am post",
@@ -433,15 +463,23 @@
"follows_you": "’Gad leantainn"
},
"dashboard": {
- "posts": "postaichean",
- "following": "a’ leantainn",
- "followers": "luchd-leantainn"
+ "my_posts": "posts",
+ "my_following": "following",
+ "my_followers": "followers",
+ "other_posts": "posts",
+ "other_following": "following",
+ "other_followers": "followers"
},
"fields": {
+ "joined": "Joined",
"add_row": "Cuir ràgh ris",
"placeholder": {
"label": "Leubail",
"content": "Susbaint"
+ },
+ "verified": {
+ "short": "Air a dhearbhadh %s",
+ "long": "Chaidh dearbhadh cò leis a tha an ceangal seo %s"
}
},
"segmented_control": {
@@ -469,12 +507,12 @@
"message": "Dearbh dì-bhacadh %s"
},
"confirm_show_reblogs": {
- "title": "Show Reblogs",
- "message": "Confirm to show reblogs"
+ "title": "Seall na brosnachaidhean",
+ "message": "Dearbh sealladh nam brosnachaidhean"
},
"confirm_hide_reblogs": {
- "title": "Hide Reblogs",
- "message": "Confirm to hide reblogs"
+ "title": "Falaich na brosnachaidhean",
+ "message": "Dearbh falach nam brosnachaidhean"
}
},
"accessibility": {
@@ -706,7 +744,19 @@
"accessibility_hint": "Thoir gnogag dhùbailte a’ leigeil seachad an draoidh seo"
},
"bookmark": {
- "title": "Bookmarks"
+ "title": "Comharran-lìn"
+ },
+ "followed_tags": {
+ "title": "Followed Tags",
+ "header": {
+ "posts": "posts",
+ "participants": "participants",
+ "posts_today": "posts today"
+ },
+ "actions": {
+ "follow": "Follow",
+ "unfollow": "Unfollow"
+ }
}
}
}
diff --git a/Localization/StringsConvertor/input/gl.lproj/Localizable.stringsdict b/Localization/StringsConvertor/input/gl.lproj/Localizable.stringsdict
index ff9d87c18..51b146ed4 100644
--- a/Localization/StringsConvertor/input/gl.lproj/Localizable.stringsdict
+++ b/Localization/StringsConvertor/input/gl.lproj/Localizable.stringsdict
@@ -50,6 +50,22 @@
%ld caracteres
+ a11y.plural.count.characters_left
+
+ NSStringLocalizedFormatKey
+ %#@character_count@ restantes
+ character_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 caracter
+ other
+ %ld caracteres
+
+
plural.count.followed_by_and_mutual
NSStringLocalizedFormatKey
diff --git a/Localization/StringsConvertor/input/gl.lproj/app.json b/Localization/StringsConvertor/input/gl.lproj/app.json
index 3c394be95..f8bda509e 100644
--- a/Localization/StringsConvertor/input/gl.lproj/app.json
+++ b/Localization/StringsConvertor/input/gl.lproj/app.json
@@ -51,6 +51,11 @@
"clean_cache": {
"title": "Limpar caché",
"message": "Baleirouse %s da caché correctamente."
+ },
+ "translation_failed": {
+ "title": "Nota",
+ "message": "Fallou a tradución. É posible que a administración non activase a tradución neste servidor ou que o servidor teña unha versión antiga de Mastodon que non ten soporte para a tradución.",
+ "button": "OK"
}
},
"controls": {
@@ -75,9 +80,10 @@
"save_photo": "Gardar foto",
"copy_photo": "Copiar foto",
"sign_in": "Acceder",
- "sign_up": "Inscribirse",
+ "sign_up": "Crear conta",
"see_more": "Ver máis",
"preview": "Vista previa",
+ "copy": "Copiar",
"share": "Compartir",
"share_user": "Compartir %s",
"share_post": "Compartir publicación",
@@ -91,12 +97,16 @@
"block_domain": "Bloquear a %s",
"unblock_domain": "Desbloquear a %s",
"settings": "Axustes",
- "delete": "Eliminar"
+ "delete": "Eliminar",
+ "translate_post": {
+ "title": "Traducido do %s",
+ "unknown_language": "Descoñecido"
+ }
},
"tabs": {
"home": "Inicio",
- "search": "Busca",
- "notification": "Notificación",
+ "search_and_explore": "Buscar e Explorar",
+ "notifications": "Notificacións",
"profile": "Perfil"
},
"keyboard": {
@@ -132,6 +142,8 @@
"sensitive_content": "Contido sensible",
"media_content_warning": "Toca nalgures para mostrar",
"tap_to_reveal": "Toca para mostrar",
+ "load_embed": "Cargar o contido",
+ "link_via_user": "%s vía %s",
"poll": {
"vote": "Votar",
"closed": "Pechada"
@@ -153,6 +165,7 @@
"show_image": "Mostrar a imaxe",
"show_gif": "Mostrar GIF",
"show_video_player": "Mostrar reprodutor de vídeo",
+ "share_link_in_post": "Compartir Ligazón na Publicación",
"tap_then_hold_to_show_menu": "Toca e mantén preso para menú"
},
"tag": {
@@ -168,6 +181,12 @@
"private": "Só as seguidoras poden ver a publicación.",
"private_from_me": "Só as miñas seguidoras poden ver esta publicación.",
"direct": "Só a usuaria mencionada pode ver a publicación."
+ },
+ "translation": {
+ "translated_from": "Traducido do %s usando %s",
+ "unknown_language": "Descoñecido",
+ "unknown_provider": "Descoñecido",
+ "show_original": "Mostrar o orixinal"
}
},
"friendship": {
@@ -218,10 +237,16 @@
"get_started": "Crear conta",
"log_in": "Acceder"
},
+ "login": {
+ "title": "Benvido outra vez",
+ "subtitle": "Conéctate ao servidor no que creaches a conta.",
+ "server_search_field": {
+ "placeholder": "Escribe o URL ou busca o teu servidor"
+ }
+ },
"server_picker": {
"title": "Mastodon fórmano as persoas das diferentes comunidades.",
- "subtitle": "Elixe unha comunidade en función dos teus intereses, rexión ou unha de propósito xeral.",
- "subtitle_extend": "Elixe unha comunidade en función dos teus intereses, rexión ou unha de propósito xeral. Cada comunidade está xestionada por unha organización totalmente independente ou unha única persoa.",
+ "subtitle": "Elixe un servidor en función dos teus intereses, rexión o un de propósito xeral. Poderás conversar con calquera en Mastodon, independentemente do servidor que elixas.",
"button": {
"category": {
"all": "Todo",
@@ -248,8 +273,7 @@
"category": "CATEGORÍA"
},
"input": {
- "placeholder": "Buscar comunidades",
- "search_servers_or_enter_url": "Busca un servidor ou escribe URL"
+ "search_servers_or_enter_url": "Busca comunidades ou escribe URL"
},
"empty_state": {
"finding_servers": "Buscando servidores dispoñibles...",
@@ -386,7 +410,9 @@
"load_failed": "Fallou a carga",
"upload_failed": "Erro na subida",
"can_not_recognize_this_media_attachment": "Non se recoñece o tipo de multimedia",
- "attachment_too_large": "Adxunto demasiado grande"
+ "attachment_too_large": "Adxunto demasiado grande",
+ "compressing_state": "Comprimindo...",
+ "server_processing_state": "Procesando no servidor..."
},
"poll": {
"duration_time": "Duración: %s",
@@ -396,7 +422,9 @@
"one_day": "1 Día",
"three_days": "3 Días",
"seven_days": "7 Días",
- "option_number": "Opción %ld"
+ "option_number": "Opción %ld",
+ "the_poll_is_invalid": "A enquisa non é válida",
+ "the_poll_has_empty_option": "A enquisa ten unha opción baleira"
},
"content_warning": {
"placeholder": "Escribe o teu aviso aquí..."
@@ -417,7 +445,9 @@
"custom_emoji_picker": "Selector emoji personalizado",
"enable_content_warning": "Marcar con Aviso sobre o contido",
"disable_content_warning": "Retirar Aviso sobre o contido",
- "post_visibility_menu": "Visibilidade da publicación"
+ "post_visibility_menu": "Visibilidade da publicación",
+ "post_options": "Opcións da publicación",
+ "posting_as": "Publicando como %s"
},
"keyboard": {
"discard_post": "Descartar publicación",
@@ -433,15 +463,23 @@
"follows_you": "Séguete"
},
"dashboard": {
- "posts": "publicacións",
- "following": "seguindo",
- "followers": "seguidoras"
+ "my_posts": "publicacións",
+ "my_following": "seguindo",
+ "my_followers": "seguidoras",
+ "other_posts": "publicacións",
+ "other_following": "seguindo",
+ "other_followers": "seguidoras"
},
"fields": {
+ "joined": "Uniuse",
"add_row": "Engadir fila",
"placeholder": {
"label": "Etiqueta",
"content": "Contido"
+ },
+ "verified": {
+ "short": "Verificada en %s",
+ "long": "A propiedade desta ligazón foi verificada o %s"
}
},
"segmented_control": {
@@ -707,6 +745,18 @@
},
"bookmark": {
"title": "Marcadores"
+ },
+ "followed_tags": {
+ "title": "Cancelos seguidos",
+ "header": {
+ "posts": "publicacións",
+ "participants": "participantes",
+ "posts_today": "publicacións de hoxe"
+ },
+ "actions": {
+ "follow": "Seguir",
+ "unfollow": "Deixar de seguir"
+ }
}
}
}
diff --git a/Localization/StringsConvertor/input/he.lproj/Localizable.stringsdict b/Localization/StringsConvertor/input/he.lproj/Localizable.stringsdict
new file mode 100644
index 000000000..63ed25f8a
--- /dev/null
+++ b/Localization/StringsConvertor/input/he.lproj/Localizable.stringsdict
@@ -0,0 +1,581 @@
+
+
+
+
+ a11y.plural.count.unread.notification
+
+ NSStringLocalizedFormatKey
+ %#@notification_count_unread_notification@
+ notification_count_unread_notification
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ התראה אחת שלא נקראה
+ two
+ שתי התראות שלא נקראו
+ many
+ %ld unread notifications
+ other
+ %ld התראות שלא נקראו
+
+
+ a11y.plural.count.input_limit_exceeds
+
+ NSStringLocalizedFormatKey
+ Input limit exceeds %#@character_count@
+ character_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 character
+ two
+ %ld characters
+ many
+ %ld characters
+ other
+ %ld characters
+
+
+ a11y.plural.count.input_limit_remains
+
+ NSStringLocalizedFormatKey
+ Input limit remains %#@character_count@
+ character_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 character
+ two
+ %ld characters
+ many
+ %ld characters
+ other
+ %ld characters
+
+
+ a11y.plural.count.characters_left
+
+ NSStringLocalizedFormatKey
+ %#@character_count@ left
+ character_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 character
+ two
+ %ld characters
+ many
+ %ld characters
+ other
+ %ld characters
+
+
+ plural.count.followed_by_and_mutual
+
+ NSStringLocalizedFormatKey
+ %#@names@%#@count_mutual@
+ names
+
+ one
+
+ two
+
+ many
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ other
+
+
+ count_mutual
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ Followed by %1$@, and another mutual
+ two
+ Followed by %1$@, and %ld mutuals
+ many
+ Followed by %1$@, and %ld mutuals
+ other
+ Followed by %1$@, and %ld mutuals
+
+
+ plural.count.metric_formatted.post
+
+ NSStringLocalizedFormatKey
+ %@ %#@post_count@
+ post_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ post
+ two
+ posts
+ many
+ posts
+ other
+ posts
+
+
+ plural.count.media
+
+ NSStringLocalizedFormatKey
+ %#@media_count@
+ media_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 media
+ two
+ %ld media
+ many
+ %ld media
+ other
+ %ld media
+
+
+ plural.count.post
+
+ NSStringLocalizedFormatKey
+ %#@post_count@
+ post_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 post
+ two
+ %ld posts
+ many
+ %ld posts
+ other
+ %ld posts
+
+
+ plural.count.favorite
+
+ NSStringLocalizedFormatKey
+ %#@favorite_count@
+ favorite_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 favorite
+ two
+ %ld favorites
+ many
+ %ld favorites
+ other
+ %ld favorites
+
+
+ plural.count.reblog
+
+ NSStringLocalizedFormatKey
+ %#@reblog_count@
+ reblog_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 reblog
+ two
+ %ld reblogs
+ many
+ %ld reblogs
+ other
+ %ld reblogs
+
+
+ plural.count.reply
+
+ NSStringLocalizedFormatKey
+ %#@reply_count@
+ reply_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 reply
+ two
+ %ld replies
+ many
+ %ld replies
+ other
+ %ld replies
+
+
+ plural.count.vote
+
+ NSStringLocalizedFormatKey
+ %#@vote_count@
+ vote_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 vote
+ two
+ %ld votes
+ many
+ %ld votes
+ other
+ %ld votes
+
+
+ plural.count.voter
+
+ NSStringLocalizedFormatKey
+ %#@voter_count@
+ voter_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 voter
+ two
+ %ld voters
+ many
+ %ld voters
+ other
+ %ld voters
+
+
+ plural.people_talking
+
+ NSStringLocalizedFormatKey
+ %#@count_people_talking@
+ count_people_talking
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 people talking
+ two
+ %ld people talking
+ many
+ %ld people talking
+ other
+ %ld people talking
+
+
+ plural.count.following
+
+ NSStringLocalizedFormatKey
+ %#@count_following@
+ count_following
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 following
+ two
+ %ld following
+ many
+ %ld following
+ other
+ %ld following
+
+
+ plural.count.follower
+
+ NSStringLocalizedFormatKey
+ %#@count_follower@
+ count_follower
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 follower
+ two
+ %ld followers
+ many
+ %ld followers
+ other
+ %ld followers
+
+
+ date.year.left
+
+ NSStringLocalizedFormatKey
+ %#@count_year_left@
+ count_year_left
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 year left
+ two
+ %ld years left
+ many
+ %ld years left
+ other
+ %ld years left
+
+
+ date.month.left
+
+ NSStringLocalizedFormatKey
+ %#@count_month_left@
+ count_month_left
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 months left
+ two
+ %ld months left
+ many
+ %ld months left
+ other
+ %ld months left
+
+
+ date.day.left
+
+ NSStringLocalizedFormatKey
+ %#@count_day_left@
+ count_day_left
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 day left
+ two
+ %ld days left
+ many
+ %ld days left
+ other
+ %ld days left
+
+
+ date.hour.left
+
+ NSStringLocalizedFormatKey
+ %#@count_hour_left@
+ count_hour_left
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 hour left
+ two
+ %ld hours left
+ many
+ %ld hours left
+ other
+ %ld hours left
+
+
+ date.minute.left
+
+ NSStringLocalizedFormatKey
+ %#@count_minute_left@
+ count_minute_left
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 minute left
+ two
+ %ld minutes left
+ many
+ %ld minutes left
+ other
+ %ld minutes left
+
+
+ date.second.left
+
+ NSStringLocalizedFormatKey
+ %#@count_second_left@
+ count_second_left
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 second left
+ two
+ %ld seconds left
+ many
+ %ld seconds left
+ other
+ %ld seconds left
+
+
+ date.year.ago.abbr
+
+ NSStringLocalizedFormatKey
+ %#@count_year_ago_abbr@
+ count_year_ago_abbr
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ לפני שנה
+ two
+ לפני שנתיים
+ many
+ %ldy ago
+ other
+ לפני %ld שנים
+
+
+ date.month.ago.abbr
+
+ NSStringLocalizedFormatKey
+ %#@count_month_ago_abbr@
+ count_month_ago_abbr
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ לפני חודש
+ two
+ לפני חודשיים
+ many
+ %ldM ago
+ other
+ לפני %ld חודשים
+
+
+ date.day.ago.abbr
+
+ NSStringLocalizedFormatKey
+ %#@count_day_ago_abbr@
+ count_day_ago_abbr
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ לפני יום
+ two
+ לפני יומיים
+ many
+ %ldd ago
+ other
+ לפני %ld ימים
+
+
+ date.hour.ago.abbr
+
+ NSStringLocalizedFormatKey
+ %#@count_hour_ago_abbr@
+ count_hour_ago_abbr
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ לפני שעה
+ two
+ לפני שעתיים
+ many
+ %ldh ago
+ other
+ לפני %ld שעות
+
+
+ date.minute.ago.abbr
+
+ NSStringLocalizedFormatKey
+ %#@count_minute_ago_abbr@
+ count_minute_ago_abbr
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ לפני דקה
+ two
+ לפני שתי דקות
+ many
+ %ldm ago
+ other
+ לפני %ld דקות
+
+
+ date.second.ago.abbr
+
+ NSStringLocalizedFormatKey
+ %#@count_second_ago_abbr@
+ count_second_ago_abbr
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ לפני שנייה
+ two
+ לפני שתי שניות
+ many
+ %lds ago
+ other
+ לפני %ld שניות
+
+
+
+
diff --git a/Localization/StringsConvertor/input/he.lproj/app.json b/Localization/StringsConvertor/input/he.lproj/app.json
new file mode 100644
index 000000000..6b484e301
--- /dev/null
+++ b/Localization/StringsConvertor/input/he.lproj/app.json
@@ -0,0 +1,762 @@
+{
+ "common": {
+ "alerts": {
+ "common": {
+ "please_try_again": "Please try again.",
+ "please_try_again_later": "Please try again later."
+ },
+ "sign_up_failure": {
+ "title": "Sign Up Failure"
+ },
+ "server_error": {
+ "title": "Server Error"
+ },
+ "vote_failure": {
+ "title": "Vote Failure",
+ "poll_ended": "The poll has ended"
+ },
+ "discard_post_content": {
+ "title": "Discard Draft",
+ "message": "Confirm to discard composed post content."
+ },
+ "publish_post_failure": {
+ "title": "Publish Failure",
+ "message": "Failed to publish the post.\nPlease check your internet connection.",
+ "attachments_message": {
+ "video_attach_with_photo": "Cannot attach a video to a post that already contains images.",
+ "more_than_one_video": "Cannot attach more than one video."
+ }
+ },
+ "edit_profile_failure": {
+ "title": "Edit Profile Error",
+ "message": "Cannot edit profile. Please try again."
+ },
+ "sign_out": {
+ "title": "Sign Out",
+ "message": "Are you sure you want to sign out?",
+ "confirm": "Sign Out"
+ },
+ "block_domain": {
+ "title": "Are you really, really sure you want to block the entire %s? In most cases a few targeted blocks or mutes are sufficient and preferable. You will not see content from that domain and any of your followers from that domain will be removed.",
+ "block_entire_domain": "Block Domain"
+ },
+ "save_photo_failure": {
+ "title": "Save Photo Failure",
+ "message": "Please enable the photo library access permission to save the photo."
+ },
+ "delete_post": {
+ "title": "Delete Post",
+ "message": "Are you sure you want to delete this post?"
+ },
+ "clean_cache": {
+ "title": "Clean Cache",
+ "message": "Successfully cleaned %s cache."
+ },
+ "translation_failed": {
+ "title": "Note",
+ "message": "Translation failed. Maybe the administrator has not enabled translations on this server or this server is running an older version of Mastodon where translations are not yet supported.",
+ "button": "OK"
+ }
+ },
+ "controls": {
+ "actions": {
+ "back": "Back",
+ "next": "הבא",
+ "previous": "הקודם",
+ "open": "Open",
+ "add": "Add",
+ "remove": "Remove",
+ "edit": "Edit",
+ "save": "Save",
+ "ok": "OK",
+ "done": "Done",
+ "confirm": "Confirm",
+ "continue": "Continue",
+ "compose": "Compose",
+ "cancel": "Cancel",
+ "discard": "Discard",
+ "try_again": "Try Again",
+ "take_photo": "Take Photo",
+ "save_photo": "Save Photo",
+ "copy_photo": "Copy Photo",
+ "sign_in": "Log in",
+ "sign_up": "יצירת חשבון",
+ "see_more": "See More",
+ "preview": "Preview",
+ "copy": "Copy",
+ "share": "Share",
+ "share_user": "Share %s",
+ "share_post": "Share Post",
+ "open_in_safari": "Open in Safari",
+ "open_in_browser": "Open in Browser",
+ "find_people": "Find people to follow",
+ "manually_search": "Manually search instead",
+ "skip": "Skip",
+ "reply": "Reply",
+ "report_user": "Report %s",
+ "block_domain": "חסימת %s",
+ "unblock_domain": "הסרת חסימה מ־%s",
+ "settings": "הגדרות",
+ "delete": "Delete",
+ "translate_post": {
+ "title": "Translate from %s",
+ "unknown_language": "Unknown"
+ }
+ },
+ "tabs": {
+ "home": "Home",
+ "search_and_explore": "Search and Explore",
+ "notifications": "Notifications",
+ "profile": "פרופיל"
+ },
+ "keyboard": {
+ "common": {
+ "switch_to_tab": "Switch to %s",
+ "compose_new_post": "Compose New Post",
+ "show_favorites": "Show Favorites",
+ "open_settings": "Open Settings"
+ },
+ "timeline": {
+ "previous_status": "Previous Post",
+ "next_status": "Next Post",
+ "open_status": "Open Post",
+ "open_author_profile": "Open Author's Profile",
+ "open_reblogger_profile": "Open Reblogger's Profile",
+ "reply_status": "Reply to Post",
+ "toggle_reblog": "Toggle Reblog on Post",
+ "toggle_favorite": "Toggle Favorite on Post",
+ "toggle_content_warning": "Toggle Content Warning",
+ "preview_image": "Preview Image"
+ },
+ "segmented_control": {
+ "previous_section": "Previous Section",
+ "next_section": "Next Section"
+ }
+ },
+ "status": {
+ "user_reblogged": "%s reblogged",
+ "user_replied_to": "Replied to %s",
+ "show_post": "Show Post",
+ "show_user_profile": "Show user profile",
+ "content_warning": "Content Warning",
+ "sensitive_content": "תוכן רגיש",
+ "media_content_warning": "Tap anywhere to reveal",
+ "tap_to_reveal": "Tap to reveal",
+ "load_embed": "Load Embed",
+ "link_via_user": "%s via %s",
+ "poll": {
+ "vote": "Vote",
+ "closed": "Closed"
+ },
+ "meta_entity": {
+ "url": "Link: %s",
+ "hashtag": "Hashtag: %s",
+ "mention": "Show Profile: %s",
+ "email": "Email address: %s"
+ },
+ "actions": {
+ "reply": "תגובה",
+ "reblog": "Reblog",
+ "unreblog": "Undo reblog",
+ "favorite": "Favorite",
+ "unfavorite": "Unfavorite",
+ "menu": "Menu",
+ "hide": "Hide",
+ "show_image": "Show image",
+ "show_gif": "Show GIF",
+ "show_video_player": "Show video player",
+ "share_link_in_post": "Share Link in Post",
+ "tap_then_hold_to_show_menu": "Tap then hold to show menu"
+ },
+ "tag": {
+ "url": "URL",
+ "mention": "Mention",
+ "link": "Link",
+ "hashtag": "Hashtag",
+ "email": "Email",
+ "emoji": "Emoji"
+ },
+ "visibility": {
+ "unlisted": "Everyone can see this post but not display in the public timeline.",
+ "private": "Only their followers can see this post.",
+ "private_from_me": "Only my followers can see this post.",
+ "direct": "Only mentioned user can see this post."
+ },
+ "translation": {
+ "translated_from": "Translated from %s using %s",
+ "unknown_language": "Unknown",
+ "unknown_provider": "Unknown",
+ "show_original": "Shown Original"
+ }
+ },
+ "friendship": {
+ "follow": "Follow",
+ "following": "Following",
+ "request": "Request",
+ "pending": "Pending",
+ "block": "Block",
+ "block_user": "Block %s",
+ "block_domain": "Block %s",
+ "unblock": "Unblock",
+ "unblock_user": "Unblock %s",
+ "blocked": "Blocked",
+ "mute": "Mute",
+ "mute_user": "Mute %s",
+ "unmute": "Unmute",
+ "unmute_user": "Unmute %s",
+ "muted": "Muted",
+ "edit_info": "Edit Info",
+ "show_reblogs": "Show Reblogs",
+ "hide_reblogs": "Hide Reblogs"
+ },
+ "timeline": {
+ "filtered": "Filtered",
+ "timestamp": {
+ "now": "Now"
+ },
+ "loader": {
+ "load_missing_posts": "Load missing posts",
+ "loading_missing_posts": "Loading missing posts...",
+ "show_more_replies": "Show more replies"
+ },
+ "header": {
+ "no_status_found": "No Post Found",
+ "blocking_warning": "You can’t view this user's profile\nuntil you unblock them.\nYour profile looks like this to them.",
+ "user_blocking_warning": "You can’t view %s’s profile\nuntil you unblock them.\nYour profile looks like this to them.",
+ "blocked_warning": "You can’t view this user’s profile\nuntil they unblock you.",
+ "user_blocked_warning": "You can’t view %s’s profile\nuntil they unblock you.",
+ "suspended_warning": "This user has been suspended.",
+ "user_suspended_warning": "%s’s account has been suspended."
+ }
+ }
+ }
+ },
+ "scene": {
+ "welcome": {
+ "slogan": "Social networking\nback in your hands.",
+ "get_started": "בואו נתחיל",
+ "log_in": "Log In"
+ },
+ "login": {
+ "title": "Welcome back",
+ "subtitle": "Log you in on the server you created your account on.",
+ "server_search_field": {
+ "placeholder": "Enter URL or search for your server"
+ }
+ },
+ "server_picker": {
+ "title": "Mastodon is made of users in different servers.",
+ "subtitle": "Pick a server based on your region, interests, or a general purpose one. You can still chat with anyone on Mastodon, regardless of your servers.",
+ "button": {
+ "category": {
+ "all": "All",
+ "all_accessiblity_description": "Category: All",
+ "academia": "academia",
+ "activism": "אקטיביזם",
+ "food": "אוכל",
+ "furry": "furry",
+ "games": "משחקים",
+ "general": "כללי",
+ "journalism": "journalism",
+ "lgbt": "להט\"ב",
+ "regional": "regional",
+ "art": "אומנות",
+ "music": "מוזיקה",
+ "tech": "tech"
+ },
+ "see_less": "See Less",
+ "see_more": "See More"
+ },
+ "label": {
+ "language": "LANGUAGE",
+ "users": "USERS",
+ "category": "CATEGORY"
+ },
+ "input": {
+ "search_servers_or_enter_url": "Search communities or enter URL"
+ },
+ "empty_state": {
+ "finding_servers": "Finding available servers...",
+ "bad_network": "Something went wrong while loading the data. Check your internet connection.",
+ "no_results": "No results"
+ }
+ },
+ "register": {
+ "title": "Let’s get you set up on %s",
+ "lets_get_you_set_up_on_domain": "Let’s get you set up on %s",
+ "input": {
+ "avatar": {
+ "delete": "Delete"
+ },
+ "username": {
+ "placeholder": "שם משתמש/ת",
+ "duplicate_prompt": "This username is taken."
+ },
+ "display_name": {
+ "placeholder": "שם תצוגה"
+ },
+ "email": {
+ "placeholder": "דוא״ל"
+ },
+ "password": {
+ "placeholder": "סיסמה",
+ "require": "Your password needs at least:",
+ "character_limit": "8 characters",
+ "accessibility": {
+ "checked": "checked",
+ "unchecked": "unchecked"
+ },
+ "hint": "Your password needs at least eight characters"
+ },
+ "invite": {
+ "registration_user_invite_request": "Why do you want to join?"
+ }
+ },
+ "error": {
+ "item": {
+ "username": "שם משתמש/ת",
+ "email": "דוא״ל",
+ "password": "סיסמה",
+ "agreement": "Agreement",
+ "locale": "Locale",
+ "reason": "Reason"
+ },
+ "reason": {
+ "blocked": "%s contains a disallowed email provider",
+ "unreachable": "%s does not seem to exist",
+ "taken": "%s is already in use",
+ "reserved": "%s is a reserved keyword",
+ "accepted": "%s must be accepted",
+ "blank": "%s is required",
+ "invalid": "%s is invalid",
+ "too_long": "%s is too long",
+ "too_short": "%s is too short",
+ "inclusion": "%s is not a supported value"
+ },
+ "special": {
+ "username_invalid": "Username must only contain alphanumeric characters and underscores",
+ "username_too_long": "Username is too long (can’t be longer than 30 characters)",
+ "email_invalid": "This is not a valid email address",
+ "password_too_short": "Password is too short (must be at least 8 characters)"
+ }
+ }
+ },
+ "server_rules": {
+ "title": "Some ground rules.",
+ "subtitle": "These are set and enforced by the %s moderators.",
+ "prompt": "By continuing, you’re subject to the terms of service and privacy policy for %s.",
+ "terms_of_service": "terms of service",
+ "privacy_policy": "privacy policy",
+ "button": {
+ "confirm": "I Agree"
+ }
+ },
+ "confirm_email": {
+ "title": "One last thing.",
+ "subtitle": "Tap the link we emailed to you to verify your account.",
+ "tap_the_link_we_emailed_to_you_to_verify_your_account": "Tap the link we emailed to you to verify your account",
+ "button": {
+ "open_email_app": "Open Email App",
+ "resend": "Resend"
+ },
+ "dont_receive_email": {
+ "title": "Check your email",
+ "description": "Check if your email address is correct as well as your junk folder if you haven’t.",
+ "resend_email": "Resend Email"
+ },
+ "open_email_app": {
+ "title": "Check your inbox.",
+ "description": "We just sent you an email. Check your junk folder if you haven’t.",
+ "mail": "Mail",
+ "open_email_client": "Open Email Client"
+ }
+ },
+ "home_timeline": {
+ "title": "Home",
+ "navigation_bar_state": {
+ "offline": "Offline",
+ "new_posts": "See new posts",
+ "published": "Published!",
+ "Publishing": "Publishing post...",
+ "accessibility": {
+ "logo_label": "Logo Button",
+ "logo_hint": "Tap to scroll to top and tap again to previous location"
+ }
+ }
+ },
+ "suggestion_account": {
+ "title": "Find People to Follow",
+ "follow_explain": "When you follow someone, you’ll see their posts in your home feed."
+ },
+ "compose": {
+ "title": {
+ "new_post": "New Post",
+ "new_reply": "New Reply"
+ },
+ "media_selection": {
+ "camera": "Take Photo",
+ "photo_library": "Photo Library",
+ "browse": "Browse"
+ },
+ "content_input_placeholder": "Type or paste what’s on your mind",
+ "compose_action": "Publish",
+ "replying_to_user": "replying to %s",
+ "attachment": {
+ "photo": "photo",
+ "video": "video",
+ "attachment_broken": "This %s is broken and can’t be\nuploaded to Mastodon.",
+ "description_photo": "Describe the photo for the visually-impaired...",
+ "description_video": "Describe the video for the visually-impaired...",
+ "load_failed": "Load Failed",
+ "upload_failed": "Upload Failed",
+ "can_not_recognize_this_media_attachment": "Can not recognize this media attachment",
+ "attachment_too_large": "Attachment too large",
+ "compressing_state": "Compressing...",
+ "server_processing_state": "Server Processing..."
+ },
+ "poll": {
+ "duration_time": "Duration: %s",
+ "thirty_minutes": "חצי שעה",
+ "one_hour": "שעה",
+ "six_hours": "6 שעות",
+ "one_day": "יום אחד",
+ "three_days": "3 ימים",
+ "seven_days": "7 ימים",
+ "option_number": "Option %ld",
+ "the_poll_is_invalid": "The poll is invalid",
+ "the_poll_has_empty_option": "The poll has empty option"
+ },
+ "content_warning": {
+ "placeholder": "Write an accurate warning here..."
+ },
+ "visibility": {
+ "public": "Public",
+ "unlisted": "Unlisted",
+ "private": "לעוקבים בלבד",
+ "direct": "Only people I mention"
+ },
+ "auto_complete": {
+ "space_to_add": "Space to add"
+ },
+ "accessibility": {
+ "append_attachment": "Add Attachment",
+ "append_poll": "Add Poll",
+ "remove_poll": "Remove Poll",
+ "custom_emoji_picker": "Custom Emoji Picker",
+ "enable_content_warning": "Enable Content Warning",
+ "disable_content_warning": "Disable Content Warning",
+ "post_visibility_menu": "Post Visibility Menu",
+ "post_options": "Post Options",
+ "posting_as": "Posting as %s"
+ },
+ "keyboard": {
+ "discard_post": "Discard Post",
+ "publish_post": "Publish Post",
+ "toggle_poll": "Toggle Poll",
+ "toggle_content_warning": "Toggle Content Warning",
+ "append_attachment_entry": "Add Attachment - %s",
+ "select_visibility_entry": "Select Visibility - %s"
+ }
+ },
+ "profile": {
+ "header": {
+ "follows_you": "Follows You"
+ },
+ "dashboard": {
+ "my_posts": "posts",
+ "my_following": "following",
+ "my_followers": "followers",
+ "other_posts": "posts",
+ "other_following": "following",
+ "other_followers": "followers"
+ },
+ "fields": {
+ "joined": "Joined",
+ "add_row": "Add Row",
+ "placeholder": {
+ "label": "Label",
+ "content": "Content"
+ },
+ "verified": {
+ "short": "Verified on %s",
+ "long": "Ownership of this link was checked on %s"
+ }
+ },
+ "segmented_control": {
+ "posts": "Posts",
+ "replies": "Replies",
+ "posts_and_replies": "Posts and Replies",
+ "media": "Media",
+ "about": "About"
+ },
+ "relationship_action_alert": {
+ "confirm_mute_user": {
+ "title": "Mute Account",
+ "message": "Confirm to mute %s"
+ },
+ "confirm_unmute_user": {
+ "title": "Unmute Account",
+ "message": "Confirm to unmute %s"
+ },
+ "confirm_block_user": {
+ "title": "Block Account",
+ "message": "Confirm to block %s"
+ },
+ "confirm_unblock_user": {
+ "title": "Unblock Account",
+ "message": "Confirm to unblock %s"
+ },
+ "confirm_show_reblogs": {
+ "title": "Show Reblogs",
+ "message": "Confirm to show reblogs"
+ },
+ "confirm_hide_reblogs": {
+ "title": "Hide Reblogs",
+ "message": "Confirm to hide reblogs"
+ }
+ },
+ "accessibility": {
+ "show_avatar_image": "Show avatar image",
+ "edit_avatar_image": "Edit avatar image",
+ "show_banner_image": "Show banner image",
+ "double_tap_to_open_the_list": "Double tap to open the list"
+ }
+ },
+ "follower": {
+ "title": "follower",
+ "footer": "Followers from other servers are not displayed."
+ },
+ "following": {
+ "title": "following",
+ "footer": "Follows from other servers are not displayed."
+ },
+ "familiarFollowers": {
+ "title": "Followers you familiar",
+ "followed_by_names": "Followed by %s"
+ },
+ "favorited_by": {
+ "title": "Favorited By"
+ },
+ "reblogged_by": {
+ "title": "Reblogged By"
+ },
+ "search": {
+ "title": "Search",
+ "search_bar": {
+ "placeholder": "Search hashtags and users",
+ "cancel": "Cancel"
+ },
+ "recommend": {
+ "button_text": "See All",
+ "hash_tag": {
+ "title": "Trending on Mastodon",
+ "description": "Hashtags that are getting quite a bit of attention",
+ "people_talking": "%s people are talking"
+ },
+ "accounts": {
+ "title": "Accounts you might like",
+ "description": "You may like to follow these accounts",
+ "follow": "Follow"
+ }
+ },
+ "searching": {
+ "segment": {
+ "all": "All",
+ "people": "People",
+ "hashtags": "Hashtags",
+ "posts": "Posts"
+ },
+ "empty_state": {
+ "no_results": "No results"
+ },
+ "recent_search": "Recent searches",
+ "clear": "Clear"
+ }
+ },
+ "discovery": {
+ "tabs": {
+ "posts": "Posts",
+ "hashtags": "Hashtags",
+ "news": "News",
+ "community": "Community",
+ "for_you": "For You"
+ },
+ "intro": "These are the posts gaining traction in your corner of Mastodon."
+ },
+ "favorite": {
+ "title": "Your Favorites"
+ },
+ "notification": {
+ "title": {
+ "Everything": "Everything",
+ "Mentions": "Mentions"
+ },
+ "notification_description": {
+ "followed_you": "followed you",
+ "favorited_your_post": "favorited your post",
+ "reblogged_your_post": "reblogged your post",
+ "mentioned_you": "mentioned you",
+ "request_to_follow_you": "request to follow you",
+ "poll_has_ended": "poll has ended"
+ },
+ "keyobard": {
+ "show_everything": "Show Everything",
+ "show_mentions": "Show Mentions"
+ },
+ "follow_request": {
+ "accept": "Accept",
+ "accepted": "Accepted",
+ "reject": "reject",
+ "rejected": "Rejected"
+ }
+ },
+ "thread": {
+ "back_title": "Post",
+ "title": "Post from %s"
+ },
+ "settings": {
+ "title": "Settings",
+ "section": {
+ "appearance": {
+ "title": "Appearance",
+ "automatic": "Automatic",
+ "light": "Always Light",
+ "dark": "Always Dark"
+ },
+ "look_and_feel": {
+ "title": "Look and Feel",
+ "use_system": "Use System",
+ "really_dark": "Really Dark",
+ "sorta_dark": "Sorta Dark",
+ "light": "Light"
+ },
+ "notifications": {
+ "title": "Notifications",
+ "favorites": "Favorites my post",
+ "follows": "Follows me",
+ "boosts": "Reblogs my post",
+ "mentions": "Mentions me",
+ "trigger": {
+ "anyone": "anyone",
+ "follower": "a follower",
+ "follow": "anyone I follow",
+ "noone": "no one",
+ "title": "Notify me when"
+ }
+ },
+ "preference": {
+ "title": "Preferences",
+ "true_black_dark_mode": "True black dark mode",
+ "disable_avatar_animation": "Disable animated avatars",
+ "disable_emoji_animation": "Disable animated emojis",
+ "using_default_browser": "Use default browser to open links",
+ "open_links_in_mastodon": "Open links in Mastodon"
+ },
+ "boring_zone": {
+ "title": "The Boring Zone",
+ "account_settings": "Account Settings",
+ "terms": "Terms of Service",
+ "privacy": "Privacy Policy"
+ },
+ "spicy_zone": {
+ "title": "The Spicy Zone",
+ "clear": "Clear Media Cache",
+ "signout": "Sign Out"
+ }
+ },
+ "footer": {
+ "mastodon_description": "Mastodon is open source software. You can report issues on GitHub at %s (%s)"
+ },
+ "keyboard": {
+ "close_settings_window": "Close Settings Window"
+ }
+ },
+ "report": {
+ "title_report": "Report",
+ "title": "Report %s",
+ "step1": "Step 1 of 2",
+ "step2": "Step 2 of 2",
+ "content1": "Are there any other posts you’d like to add to the report?",
+ "content2": "Is there anything the moderators should know about this report?",
+ "report_sent_title": "Thanks for reporting, we’ll look into this.",
+ "send": "Send Report",
+ "skip_to_send": "Send without comment",
+ "text_placeholder": "Type or paste additional comments",
+ "reported": "REPORTED",
+ "step_one": {
+ "step_1_of_4": "Step 1 of 4",
+ "whats_wrong_with_this_post": "What's wrong with this post?",
+ "whats_wrong_with_this_account": "What's wrong with this account?",
+ "whats_wrong_with_this_username": "What's wrong with %s?",
+ "select_the_best_match": "Select the best match",
+ "i_dont_like_it": "I don’t like it",
+ "it_is_not_something_you_want_to_see": "It is not something you want to see",
+ "its_spam": "It’s spam",
+ "malicious_links_fake_engagement_or_repetetive_replies": "Malicious links, fake engagement, or repetetive replies",
+ "it_violates_server_rules": "It violates server rules",
+ "you_are_aware_that_it_breaks_specific_rules": "You are aware that it breaks specific rules",
+ "its_something_else": "It’s something else",
+ "the_issue_does_not_fit_into_other_categories": "The issue does not fit into other categories"
+ },
+ "step_two": {
+ "step_2_of_4": "Step 2 of 4",
+ "which_rules_are_being_violated": "Which rules are being violated?",
+ "select_all_that_apply": "Select all that apply",
+ "i_just_don’t_like_it": "I just don’t like it"
+ },
+ "step_three": {
+ "step_3_of_4": "Step 3 of 4",
+ "are_there_any_posts_that_back_up_this_report": "Are there any posts that back up this report?",
+ "select_all_that_apply": "Select all that apply"
+ },
+ "step_four": {
+ "step_4_of_4": "Step 4 of 4",
+ "is_there_anything_else_we_should_know": "Is there anything else we should know?"
+ },
+ "step_final": {
+ "dont_want_to_see_this": "Don’t want to see this?",
+ "when_you_see_something_you_dont_like_on_mastodon_you_can_remove_the_person_from_your_experience.": "When you see something you don’t like on Mastodon, you can remove the person from your experience.",
+ "unfollow": "Unfollow",
+ "unfollowed": "Unfollowed",
+ "unfollow_user": "Unfollow %s",
+ "mute_user": "Mute %s",
+ "you_wont_see_their_posts_or_reblogs_in_your_home_feed_they_wont_know_they_ve_been_muted": "You won’t see their posts or reblogs in your home feed. They won’t know they’ve been muted.",
+ "block_user": "Block %s",
+ "they_will_no_longer_be_able_to_follow_or_see_your_posts_but_they_can_see_if_theyve_been_blocked": "They will no longer be able to follow or see your posts, but they can see if they’ve been blocked.",
+ "while_we_review_this_you_can_take_action_against_user": "While we review this, you can take action against %s"
+ }
+ },
+ "preview": {
+ "keyboard": {
+ "close_preview": "Close Preview",
+ "show_next": "Show Next",
+ "show_previous": "Show Previous"
+ }
+ },
+ "account_list": {
+ "tab_bar_hint": "Current selected profile: %s. Double tap then hold to show account switcher",
+ "dismiss_account_switcher": "Dismiss Account Switcher",
+ "add_account": "Add Account"
+ },
+ "wizard": {
+ "new_in_mastodon": "New in Mastodon",
+ "multiple_account_switch_intro_description": "Switch between multiple accounts by holding the profile button.",
+ "accessibility_hint": "Double tap to dismiss this wizard"
+ },
+ "bookmark": {
+ "title": "Bookmarks"
+ },
+ "followed_tags": {
+ "title": "Followed Tags",
+ "header": {
+ "posts": "posts",
+ "participants": "participants",
+ "posts_today": "posts today"
+ },
+ "actions": {
+ "follow": "Follow",
+ "unfollow": "Unfollow"
+ }
+ }
+ }
+}
diff --git a/Localization/StringsConvertor/input/he.lproj/ios-infoPlist.json b/Localization/StringsConvertor/input/he.lproj/ios-infoPlist.json
new file mode 100644
index 000000000..c6db73de0
--- /dev/null
+++ b/Localization/StringsConvertor/input/he.lproj/ios-infoPlist.json
@@ -0,0 +1,6 @@
+{
+ "NSCameraUsageDescription": "Used to take photo for post status",
+ "NSPhotoLibraryAddUsageDescription": "Used to save photo into the Photo Library",
+ "NewPostShortcutItemTitle": "New Post",
+ "SearchShortcutItemTitle": "Search"
+}
diff --git a/Localization/StringsConvertor/input/hi.lproj/Localizable.stringsdict b/Localization/StringsConvertor/input/hi.lproj/Localizable.stringsdict
index bdcae6ac9..788eb95fc 100644
--- a/Localization/StringsConvertor/input/hi.lproj/Localizable.stringsdict
+++ b/Localization/StringsConvertor/input/hi.lproj/Localizable.stringsdict
@@ -15,7 +15,7 @@
one
1 unread notification
other
- %ld unread notification
+ %ld unread notifications
a11y.plural.count.input_limit_exceeds
@@ -50,6 +50,22 @@
%ld characters
+ a11y.plural.count.characters_left
+
+ NSStringLocalizedFormatKey
+ %#@character_count@ left
+ character_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 character
+ other
+ %ld characters
+
+
plural.count.followed_by_and_mutual
NSStringLocalizedFormatKey
diff --git a/Localization/StringsConvertor/input/hi.lproj/app.json b/Localization/StringsConvertor/input/hi.lproj/app.json
index f0fedf75f..e7ea54abb 100644
--- a/Localization/StringsConvertor/input/hi.lproj/app.json
+++ b/Localization/StringsConvertor/input/hi.lproj/app.json
@@ -51,6 +51,11 @@
"clean_cache": {
"title": "Clean Cache",
"message": "Successfully cleaned %s cache."
+ },
+ "translation_failed": {
+ "title": "Note",
+ "message": "Translation failed. Maybe the administrator has not enabled translations on this server or this server is running an older version of Mastodon where translations are not yet supported.",
+ "button": "OK"
}
},
"controls": {
@@ -74,10 +79,11 @@
"take_photo": "Take Photo",
"save_photo": "Save Photo",
"copy_photo": "Copy Photo",
- "sign_in": "Sign In",
- "sign_up": "Sign Up",
+ "sign_in": "Log in",
+ "sign_up": "Create account",
"see_more": "See More",
"preview": "Preview",
+ "copy": "Copy",
"share": "Share",
"share_user": "Share %s",
"share_post": "Share Post",
@@ -91,12 +97,16 @@
"block_domain": "Block %s",
"unblock_domain": "Unblock %s",
"settings": "Settings",
- "delete": "Delete"
+ "delete": "Delete",
+ "translate_post": {
+ "title": "Translate from %s",
+ "unknown_language": "Unknown"
+ }
},
"tabs": {
"home": "Home",
- "search": "Search",
- "notification": "Notification",
+ "search_and_explore": "Search and Explore",
+ "notifications": "Notifications",
"profile": "Profile"
},
"keyboard": {
@@ -132,6 +142,8 @@
"sensitive_content": "Sensitive Content",
"media_content_warning": "Tap anywhere to reveal",
"tap_to_reveal": "Tap to reveal",
+ "load_embed": "Load Embed",
+ "link_via_user": "%s via %s",
"poll": {
"vote": "Vote",
"closed": "Closed"
@@ -153,6 +165,7 @@
"show_image": "Show image",
"show_gif": "Show GIF",
"show_video_player": "Show video player",
+ "share_link_in_post": "Share Link in Post",
"tap_then_hold_to_show_menu": "Tap then hold to show menu"
},
"tag": {
@@ -168,6 +181,12 @@
"private": "Only their followers can see this post.",
"private_from_me": "Only my followers can see this post.",
"direct": "Only mentioned user can see this post."
+ },
+ "translation": {
+ "translated_from": "Translated from %s using %s",
+ "unknown_language": "Unknown",
+ "unknown_provider": "Unknown",
+ "show_original": "Shown Original"
}
},
"friendship": {
@@ -218,10 +237,16 @@
"get_started": "Get Started",
"log_in": "Log In"
},
+ "login": {
+ "title": "Welcome back",
+ "subtitle": "Log you in on the server you created your account on.",
+ "server_search_field": {
+ "placeholder": "Enter URL or search for your server"
+ }
+ },
"server_picker": {
"title": "Mastodon is made of users in different servers.",
- "subtitle": "Pick a server based on your interests, region, or a general purpose one.",
- "subtitle_extend": "Pick a server based on your interests, region, or a general purpose one. Each server is operated by an entirely independent organization or individual.",
+ "subtitle": "Pick a server based on your region, interests, or a general purpose one. You can still chat with anyone on Mastodon, regardless of your servers.",
"button": {
"category": {
"all": "All",
@@ -248,8 +273,7 @@
"category": "CATEGORY"
},
"input": {
- "placeholder": "Search servers",
- "search_servers_or_enter_url": "Search servers or enter URL"
+ "search_servers_or_enter_url": "Search communities or enter URL"
},
"empty_state": {
"finding_servers": "Finding available servers...",
@@ -385,8 +409,10 @@
"description_video": "Describe the video for the visually-impaired...",
"load_failed": "Load Failed",
"upload_failed": "Upload Failed",
- "can_not_recognize_this_media_attachment": "Can not regonize this media attachment",
- "attachment_too_large": "Attachment too large"
+ "can_not_recognize_this_media_attachment": "Can not recognize this media attachment",
+ "attachment_too_large": "Attachment too large",
+ "compressing_state": "Compressing...",
+ "server_processing_state": "Server Processing..."
},
"poll": {
"duration_time": "Duration: %s",
@@ -396,7 +422,9 @@
"one_day": "1 Day",
"three_days": "3 Days",
"seven_days": "7 Days",
- "option_number": "Option %ld"
+ "option_number": "Option %ld",
+ "the_poll_is_invalid": "The poll is invalid",
+ "the_poll_has_empty_option": "The poll has empty option"
},
"content_warning": {
"placeholder": "Write an accurate warning here..."
@@ -417,7 +445,9 @@
"custom_emoji_picker": "Custom Emoji Picker",
"enable_content_warning": "Enable Content Warning",
"disable_content_warning": "Disable Content Warning",
- "post_visibility_menu": "Post Visibility Menu"
+ "post_visibility_menu": "Post Visibility Menu",
+ "post_options": "Post Options",
+ "posting_as": "Posting as %s"
},
"keyboard": {
"discard_post": "Discard Post",
@@ -433,15 +463,23 @@
"follows_you": "Follows You"
},
"dashboard": {
- "posts": "posts",
- "following": "following",
- "followers": "followers"
+ "my_posts": "posts",
+ "my_following": "following",
+ "my_followers": "followers",
+ "other_posts": "posts",
+ "other_following": "following",
+ "other_followers": "followers"
},
"fields": {
+ "joined": "Joined",
"add_row": "Add Row",
"placeholder": {
"label": "Label",
"content": "Content"
+ },
+ "verified": {
+ "short": "Verified on %s",
+ "long": "Ownership of this link was checked on %s"
}
},
"segmented_control": {
@@ -707,6 +745,18 @@
},
"bookmark": {
"title": "Bookmarks"
+ },
+ "followed_tags": {
+ "title": "Followed Tags",
+ "header": {
+ "posts": "posts",
+ "participants": "participants",
+ "posts_today": "posts today"
+ },
+ "actions": {
+ "follow": "Follow",
+ "unfollow": "Unfollow"
+ }
}
}
}
diff --git a/Localization/StringsConvertor/input/id.lproj/Localizable.stringsdict b/Localization/StringsConvertor/input/id.lproj/Localizable.stringsdict
index 2635defb8..3a39c085f 100644
--- a/Localization/StringsConvertor/input/id.lproj/Localizable.stringsdict
+++ b/Localization/StringsConvertor/input/id.lproj/Localizable.stringsdict
@@ -13,13 +13,13 @@
NSStringFormatValueTypeKey
ld
other
- %ld unread notification
+ %ld notifikasi belum dibaca
a11y.plural.count.input_limit_exceeds
NSStringLocalizedFormatKey
- Input limit exceeds %#@character_count@
+ Batas input melebihi %#@character_count@
character_count
NSStringFormatSpecTypeKey
@@ -33,7 +33,21 @@
a11y.plural.count.input_limit_remains
NSStringLocalizedFormatKey
- Input limit remains %#@character_count@
+ Batas input masih tersisa %#@character_count@
+ character_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ other
+ %ld karakter
+
+
+ a11y.plural.count.characters_left
+
+ NSStringLocalizedFormatKey
+ tersisa %#@character_count@
character_count
NSStringFormatSpecTypeKey
@@ -64,7 +78,7 @@
NSStringFormatValueTypeKey
ld
other
- Followed by %1$@, and %ld mutuals
+ Diikuti oleh %1$@, dan %ld mutual
plural.count.metric_formatted.post
@@ -134,7 +148,7 @@
NSStringFormatValueTypeKey
ld
other
- %ld reblogs
+ Posting ulang %ld
plural.count.reply
@@ -148,7 +162,7 @@
NSStringFormatValueTypeKey
ld
other
- %ld replies
+ %ld balasan
plural.count.vote
@@ -190,7 +204,7 @@
NSStringFormatValueTypeKey
ld
other
- %ld people talking
+ %ld obrolan
plural.count.following
@@ -204,7 +218,7 @@
NSStringFormatValueTypeKey
ld
other
- %ld following
+ %ld mengikuti
plural.count.follower
diff --git a/Localization/StringsConvertor/input/id.lproj/app.json b/Localization/StringsConvertor/input/id.lproj/app.json
index d942a22ad..558dad5d1 100644
--- a/Localization/StringsConvertor/input/id.lproj/app.json
+++ b/Localization/StringsConvertor/input/id.lproj/app.json
@@ -51,6 +51,11 @@
"clean_cache": {
"title": "Bersihkan Cache",
"message": "Berhasil menghapus %s cache."
+ },
+ "translation_failed": {
+ "title": "Note",
+ "message": "Translation failed. Maybe the administrator has not enabled translations on this server or this server is running an older version of Mastodon where translations are not yet supported.",
+ "button": "OK"
}
},
"controls": {
@@ -75,9 +80,10 @@
"save_photo": "Simpan Foto",
"copy_photo": "Salin Foto",
"sign_in": "Masuk",
- "sign_up": "Daftar",
+ "sign_up": "Buat akun",
"see_more": "Lihat lebih banyak",
"preview": "Pratinjau",
+ "copy": "Copy",
"share": "Bagikan",
"share_user": "Bagikan %s",
"share_post": "Bagikan Postingan",
@@ -91,12 +97,16 @@
"block_domain": "Blokir %s",
"unblock_domain": "Berhenti memblokir %s",
"settings": "Pengaturan",
- "delete": "Hapus"
+ "delete": "Hapus",
+ "translate_post": {
+ "title": "Translate from %s",
+ "unknown_language": "Unknown"
+ }
},
"tabs": {
"home": "Beranda",
- "search": "Cari",
- "notification": "Notifikasi",
+ "search_and_explore": "Search and Explore",
+ "notifications": "Notifikasi",
"profile": "Profil"
},
"keyboard": {
@@ -129,31 +139,34 @@
"show_post": "Tampilkan Postingan",
"show_user_profile": "Tampilkan Profil Pengguna",
"content_warning": "Peringatan Konten",
- "sensitive_content": "Sensitive Content",
+ "sensitive_content": "Konten Sensitif",
"media_content_warning": "Ketuk di mana saja untuk melihat",
- "tap_to_reveal": "Tap to reveal",
+ "tap_to_reveal": "Ketuk untuk mengungkap",
+ "load_embed": "Load Embed",
+ "link_via_user": "%s via %s",
"poll": {
- "vote": "Vote",
+ "vote": "Pilih",
"closed": "Ditutup"
},
"meta_entity": {
- "url": "Link: %s",
- "hashtag": "Hashtag: %s",
- "mention": "Show Profile: %s",
- "email": "Email address: %s"
+ "url": "Tautan: %s",
+ "hashtag": "Tagar: %s",
+ "mention": "Tampilkan Profile: %s",
+ "email": "Alamat email: %s"
},
"actions": {
"reply": "Balas",
"reblog": "Reblog",
- "unreblog": "Undo reblog",
+ "unreblog": "Batalkan reblog",
"favorite": "Favorit",
- "unfavorite": "Unfavorite",
+ "unfavorite": "Batalkan favorit",
"menu": "Menu",
- "hide": "Hide",
- "show_image": "Show image",
- "show_gif": "Show GIF",
- "show_video_player": "Show video player",
- "tap_then_hold_to_show_menu": "Tap then hold to show menu"
+ "hide": "Sembunyikan",
+ "show_image": "Tampilkan gambar",
+ "show_gif": "Tampilkan GIF",
+ "show_video_player": "Tampilkan pemutar video",
+ "share_link_in_post": "Share Link in Post",
+ "tap_then_hold_to_show_menu": "Ketuk lalu tahan untuk menampilkan menu"
},
"tag": {
"url": "URL",
@@ -164,17 +177,23 @@
"emoji": "Emoji"
},
"visibility": {
- "unlisted": "Everyone can see this post but not display in the public timeline.",
- "private": "Only their followers can see this post.",
- "private_from_me": "Only my followers can see this post.",
- "direct": "Only mentioned user can see this post."
+ "unlisted": "Postingan ini dapat dilihat semua orang tetapi tidak ditampilkan di timeline publik.",
+ "private": "Hanya pengikut mereka yang dapat melihat postingan ini.",
+ "private_from_me": "Hanya pengikut saya yang dapat melihat postingan ini.",
+ "direct": "Hanya pengguna yang disebut yang dapat melihat postingan ini."
+ },
+ "translation": {
+ "translated_from": "Translated from %s using %s",
+ "unknown_language": "Unknown",
+ "unknown_provider": "Unknown",
+ "show_original": "Shown Original"
}
},
"friendship": {
"follow": "Ikuti",
"following": "Mengikuti",
- "request": "Request",
- "pending": "Pending",
+ "request": "Minta",
+ "pending": "Tertunda",
"block": "Blokir",
"block_user": "Blokir %s",
"block_domain": "Blokir %s",
@@ -187,8 +206,8 @@
"unmute_user": "Berhenti membisukan %s",
"muted": "Dibisukan",
"edit_info": "Sunting Info",
- "show_reblogs": "Show Reblogs",
- "hide_reblogs": "Hide Reblogs"
+ "show_reblogs": "Tampilkan Reblog",
+ "hide_reblogs": "Sembunyikan Reblog"
},
"timeline": {
"filtered": "Tersaring",
@@ -196,32 +215,38 @@
"now": "Sekarang"
},
"loader": {
- "load_missing_posts": "Load missing posts",
- "loading_missing_posts": "Loading missing posts...",
+ "load_missing_posts": "Muat postingan yang hilang",
+ "loading_missing_posts": "Memuat postingan yang hilang...",
"show_more_replies": "Tampilkan lebih banyak balasan"
},
"header": {
- "no_status_found": "No Post Found",
- "blocking_warning": "You can’t view this user's profile\nuntil you unblock them.\nYour profile looks like this to them.",
- "user_blocking_warning": "You can’t view %s’s profile\nuntil you unblock them.\nYour profile looks like this to them.",
- "blocked_warning": "You can’t view this user’s profile\nuntil they unblock you.",
- "user_blocked_warning": "You can’t view %s’s profile\nuntil they unblock you.",
- "suspended_warning": "This user has been suspended.",
- "user_suspended_warning": "%s’s account has been suspended."
+ "no_status_found": "Tidak Ditemukan Postingan",
+ "blocking_warning": "Anda tidak dapat melihat profil pengguna ini sampai Anda membuka blokir mereka.\nProfil Anda terlihat seperti ini bagi mereka.",
+ "user_blocking_warning": "Anda tidak dapat melihat profil %s sampai Anda membuka blokir mereka.\nProfil Anda terlihat seperti ini bagi mereka.",
+ "blocked_warning": "Anda tidak dapat melihat profil pengguna ini sampai mereka membuka blokir Anda.",
+ "user_blocked_warning": "Anda tidak dapat melihat profil %s sampai mereka membuka blokir Anda.",
+ "suspended_warning": "Pengguna ini telah ditangguhkan.",
+ "user_suspended_warning": "Akun %s telah ditangguhkan."
}
}
}
},
"scene": {
"welcome": {
- "slogan": "Social networking\nback in your hands.",
- "get_started": "Get Started",
- "log_in": "Log In"
+ "slogan": "Jejaring sosial dalam genggaman Anda.",
+ "get_started": "Mulai",
+ "log_in": "Login"
+ },
+ "login": {
+ "title": "Selamat datang kembali",
+ "subtitle": "Masuklah pada server yang Anda buat di mana akun Anda berada.",
+ "server_search_field": {
+ "placeholder": "Masukkan URL atau pencarian di server Anda"
+ }
},
"server_picker": {
"title": "Pilih sebuah server,\nserver manapun.",
- "subtitle": "Pick a server based on your interests, region, or a general purpose one.",
- "subtitle_extend": "Pick a server based on your interests, region, or a general purpose one. Each server is operated by an entirely independent organization or individual.",
+ "subtitle": "Pilih server berdasarkan agamamu, minat, atau subjek umum lainnya. Kamu masih bisa berkomunikasi dengan semua orang di Mastodon, tanpa memperdulikan server Anda.",
"button": {
"category": {
"all": "Semua",
@@ -248,8 +273,7 @@
"category": "KATEGORI"
},
"input": {
- "placeholder": "Search servers",
- "search_servers_or_enter_url": "Search servers or enter URL"
+ "search_servers_or_enter_url": "Cari komunitas atau masukkan URL"
},
"empty_state": {
"finding_servers": "Mencari server yang tersedia...",
@@ -259,7 +283,7 @@
},
"register": {
"title": "Beritahu kami tentang diri Anda.",
- "lets_get_you_set_up_on_domain": "Let’s get you set up on %s",
+ "lets_get_you_set_up_on_domain": "Mari kita siapkan Anda di %s",
"input": {
"avatar": {
"delete": "Hapus"
@@ -269,18 +293,18 @@
"duplicate_prompt": "Nama pengguna ini sudah diambil."
},
"display_name": {
- "placeholder": "display name"
+ "placeholder": "nama yang ditampilkan"
},
"email": {
"placeholder": "surel"
},
"password": {
"placeholder": "kata sandi",
- "require": "Your password needs at least:",
- "character_limit": "8 characters",
+ "require": "Kata sandi Anda harus memiliki setidaknya:",
+ "character_limit": "8 karakter",
"accessibility": {
- "checked": "checked",
- "unchecked": "unchecked"
+ "checked": "dicentang",
+ "unchecked": "tidak dicentang"
},
"hint": "Kata sandi Anda harus memiliki sekurang-kurangnya delapan karakter"
},
@@ -294,23 +318,23 @@
"email": "Surel",
"password": "Kata sandi",
"agreement": "Persetujuan",
- "locale": "Locale",
+ "locale": "Lokal",
"reason": "Alasan"
},
"reason": {
"blocked": "%s mengandung penyedia surel yang dilarang",
"unreachable": "%s sepertinya tidak ada",
"taken": "%s sudah digunakan",
- "reserved": "%s is a reserved keyword",
+ "reserved": "%s adalah kata kunci yang dipesan",
"accepted": "%s harus diterima",
"blank": "%s diperlukan",
"invalid": "%s tidak valid",
"too_long": "%s terlalu panjang",
"too_short": "%s terlalu pendek",
- "inclusion": "%s is not a supported value"
+ "inclusion": "%s bukan nilai yang didukung"
},
"special": {
- "username_invalid": "Username must only contain alphanumeric characters and underscores",
+ "username_invalid": "Nama pengguna hanya berisi angka karakter dan garis bawah",
"username_too_long": "Nama pengguna terlalu panjang (tidak boleh lebih dari 30 karakter)",
"email_invalid": "Ini bukan alamat surel yang valid",
"password_too_short": "Kata sandi terlalu pendek (harus sekurang-kurangnya 8 karakter)"
@@ -320,7 +344,7 @@
"server_rules": {
"title": "Beberapa aturan dasar.",
"subtitle": "Peraturan ini ditetapkan oleh admin %s.",
- "prompt": "By continuing, you’re subject to the terms of service and privacy policy for %s.",
+ "prompt": "Dengan melanjutkan, Anda tunduk pada ketentuan layanan dan kebijakan privasi untuk %s.",
"terms_of_service": "kebijakan layanan",
"privacy_policy": "kebijakan privasi",
"button": {
@@ -330,10 +354,10 @@
"confirm_email": {
"title": "Satu hal lagi.",
"subtitle": "Kami baru saja mengirim sebuah surel ke %s,\nketuk tautannya untuk mengkonfirmasi akun Anda.",
- "tap_the_link_we_emailed_to_you_to_verify_your_account": "Tap the link we emailed to you to verify your account",
+ "tap_the_link_we_emailed_to_you_to_verify_your_account": "Ketuk tautan yang kami kirimkan kepada Anda via email untuk memverifikasi akun Anda",
"button": {
"open_email_app": "Buka Aplikasi Surel",
- "resend": "Resend"
+ "resend": "Kirim ulang"
},
"dont_receive_email": {
"title": "Periksa surel Anda",
@@ -343,8 +367,8 @@
"open_email_app": {
"title": "Periksa kotak masuk Anda.",
"description": "Kami baru saja mengirimkan Anda sebuah surel. Periksa folder junk Anda jika Anda belum memeriksanya.",
- "mail": "Mail",
- "open_email_client": "Open Email Client"
+ "mail": "Pesan",
+ "open_email_client": "Buka Email Klien"
}
},
"home_timeline": {
@@ -355,24 +379,24 @@
"published": "Dipublikasikan!",
"Publishing": "Mempublikasikan postingan...",
"accessibility": {
- "logo_label": "Logo Button",
- "logo_hint": "Tap to scroll to top and tap again to previous location"
+ "logo_label": "Tombol Logo",
+ "logo_hint": "Ketuk untuk menggulir ke atas dan ketuk lagi ke lokasi sebelumnya"
}
}
},
"suggestion_account": {
- "title": "Find People to Follow",
+ "title": "Temukan Orang untuk Diikuti",
"follow_explain": "Ketika Anda mengikuti seseorang, Anda akan melihat postingan mereka di beranda Anda."
},
"compose": {
"title": {
"new_post": "Postingan Baru",
- "new_reply": "New Reply"
+ "new_reply": "Pesan Baru"
},
"media_selection": {
- "camera": "Take Photo",
- "photo_library": "Photo Library",
- "browse": "Browse"
+ "camera": "Ambil Foto",
+ "photo_library": "Galeri Foto",
+ "browse": "Telusuri"
},
"content_input_placeholder": "Ketik atau tempel apa yang Anda pada pikiran Anda",
"compose_action": "Publikasikan",
@@ -383,10 +407,12 @@
"attachment_broken": "%s ini rusak dan tidak dapat diunggah ke Mastodon.",
"description_photo": "Jelaskan fotonya untuk mereka yang tidak dapat melihat dengan jelas...",
"description_video": "Jelaskan videonya untuk mereka yang tidak dapat melihat dengan jelas...",
- "load_failed": "Load Failed",
- "upload_failed": "Upload Failed",
- "can_not_recognize_this_media_attachment": "Can not regonize this media attachment",
- "attachment_too_large": "Attachment too large"
+ "load_failed": "Gagal Memuat",
+ "upload_failed": "Gagal Mengunggah",
+ "can_not_recognize_this_media_attachment": "Tidak dapat mengenali lampiran media ini",
+ "attachment_too_large": "Lampiran terlalu besar",
+ "compressing_state": "Mengompres...",
+ "server_processing_state": "Server Memproses..."
},
"poll": {
"duration_time": "Durasi: %s",
@@ -396,32 +422,36 @@
"one_day": "1 Hari",
"three_days": "3 Hari",
"seven_days": "7 Hari",
- "option_number": "Option %ld"
+ "option_number": "Opsi %ld",
+ "the_poll_is_invalid": "Japat tidak valid",
+ "the_poll_has_empty_option": "Japat memiliki opsi kosong"
},
"content_warning": {
"placeholder": "Tulis peringatan yang akurat di sini..."
},
"visibility": {
"public": "Publik",
- "unlisted": "Unlisted",
+ "unlisted": "Tidak terdaftar",
"private": "Pengikut saja",
"direct": "Hanya orang yang saya sebut"
},
"auto_complete": {
- "space_to_add": "Space to add"
+ "space_to_add": "Tekan spasi untuk menambahkan"
},
"accessibility": {
"append_attachment": "Tambahkan Lampiran",
"append_poll": "Tambahkan Japat",
"remove_poll": "Hapus Japat",
- "custom_emoji_picker": "Custom Emoji Picker",
+ "custom_emoji_picker": "Pemilih Emoji Kustom",
"enable_content_warning": "Aktifkan Peringatan Konten",
"disable_content_warning": "Nonaktifkan Peringatan Konten",
- "post_visibility_menu": "Post Visibility Menu"
+ "post_visibility_menu": "Menu Visibilitas Postingan",
+ "post_options": "Opsi Postingan",
+ "posting_as": "Posting sebagai %s"
},
"keyboard": {
- "discard_post": "Discard Post",
- "publish_post": "Publish Post",
+ "discard_post": "Hapus Postingan",
+ "publish_post": "Publikasikan Postingan",
"toggle_poll": "Toggle Poll",
"toggle_content_warning": "Toggle Content Warning",
"append_attachment_entry": "Tambahkan Lampiran - %s",
@@ -430,43 +460,51 @@
},
"profile": {
"header": {
- "follows_you": "Follows You"
+ "follows_you": "Mengikutimu"
},
"dashboard": {
- "posts": "postingan",
- "following": "mengikuti",
- "followers": "pengikut"
+ "my_posts": "posts",
+ "my_following": "following",
+ "my_followers": "followers",
+ "other_posts": "posts",
+ "other_following": "following",
+ "other_followers": "followers"
},
"fields": {
- "add_row": "Add Row",
+ "joined": "Bergabung",
+ "add_row": "Tambah Baris",
"placeholder": {
"label": "Label",
"content": "Isi"
+ },
+ "verified": {
+ "short": "Verifikasi %s",
+ "long": "Kepemilikan tautan ini dapat dicek pada %s"
}
},
"segmented_control": {
"posts": "Postingan",
"replies": "Balasan",
- "posts_and_replies": "Posts and Replies",
+ "posts_and_replies": "Kirim dan Balas",
"media": "Media",
- "about": "About"
+ "about": "Tentang"
},
"relationship_action_alert": {
"confirm_mute_user": {
- "title": "Mute Account",
- "message": "Confirm to mute %s"
+ "title": "Bisukan Akun",
+ "message": "Konfirmasi untuk bisukan %s"
},
"confirm_unmute_user": {
"title": "Berhenti Membisukan Akun",
- "message": "Confirm to unmute %s"
+ "message": "Konfirmasi untuk membisukan %s"
},
"confirm_block_user": {
- "title": "Block Account",
- "message": "Confirm to block %s"
+ "title": "Blokir Akun",
+ "message": "Konfirmasi memblokir %s"
},
"confirm_unblock_user": {
- "title": "Unblock Account",
- "message": "Confirm to unblock %s"
+ "title": "Buka Blokir Akun",
+ "message": "Konfirmasi membuka blokir %s"
},
"confirm_show_reblogs": {
"title": "Show Reblogs",
@@ -478,23 +516,23 @@
}
},
"accessibility": {
- "show_avatar_image": "Show avatar image",
- "edit_avatar_image": "Edit avatar image",
+ "show_avatar_image": "Tampilkan gambar avatar",
+ "edit_avatar_image": "Ubah gambar avatar",
"show_banner_image": "Show banner image",
- "double_tap_to_open_the_list": "Double tap to open the list"
+ "double_tap_to_open_the_list": "Ketuk ganda untuk membuka daftar"
}
},
"follower": {
- "title": "follower",
+ "title": "pengikut",
"footer": "Followers from other servers are not displayed."
},
"following": {
- "title": "following",
+ "title": "mengikuti",
"footer": "Follows from other servers are not displayed."
},
"familiarFollowers": {
"title": "Followers you familiar",
- "followed_by_names": "Followed by %s"
+ "followed_by_names": "Diikuti oleh %s"
},
"favorited_by": {
"title": "Favorited By"
@@ -511,9 +549,9 @@
"recommend": {
"button_text": "Lihat Semua",
"hash_tag": {
- "title": "Trending on Mastodon",
+ "title": "Sedang Tren di Mastodon",
"description": "Hashtags that are getting quite a bit of attention",
- "people_talking": "%s people are talking"
+ "people_talking": "%s orang sedang membicarakan"
},
"accounts": {
"title": "Akun-akun yang mungkin Anda sukai",
@@ -531,22 +569,22 @@
"empty_state": {
"no_results": "Tidak ada hasil"
},
- "recent_search": "Recent searches",
+ "recent_search": "Pencarian terbaru",
"clear": "Hapus"
}
},
"discovery": {
"tabs": {
"posts": "Posts",
- "hashtags": "Hashtags",
- "news": "News",
- "community": "Community",
- "for_you": "For You"
+ "hashtags": "Tagar",
+ "news": "Berita",
+ "community": "Komunitas",
+ "for_you": "Untuk Anda"
},
"intro": "These are the posts gaining traction in your corner of Mastodon."
},
"favorite": {
- "title": "Your Favorites"
+ "title": "Favorit Anda"
},
"notification": {
"title": {
@@ -554,11 +592,11 @@
"Mentions": "Sebutan"
},
"notification_description": {
- "followed_you": "followed you",
- "favorited_your_post": "favorited your post",
+ "followed_you": "mengikutimu",
+ "favorited_your_post": "menyukai postinganmu",
"reblogged_your_post": "reblogged your post",
- "mentioned_you": "mentioned you",
- "request_to_follow_you": "request to follow you",
+ "mentioned_you": "menyebutmu",
+ "request_to_follow_you": "meminta mengikutimu",
"poll_has_ended": "poll has ended"
},
"keyobard": {
@@ -566,10 +604,10 @@
"show_mentions": "Tampilkan Sebutan"
},
"follow_request": {
- "accept": "Accept",
- "accepted": "Accepted",
- "reject": "reject",
- "rejected": "Rejected"
+ "accept": "Menerima",
+ "accepted": "Diterima",
+ "reject": "menolak",
+ "rejected": "Ditolak"
}
},
"thread": {
@@ -586,11 +624,11 @@
"dark": "Selalu Gelap"
},
"look_and_feel": {
- "title": "Look and Feel",
+ "title": "Lihat dan Rasakan",
"use_system": "Use System",
- "really_dark": "Really Dark",
- "sorta_dark": "Sorta Dark",
- "light": "Light"
+ "really_dark": "Sangat Gelap",
+ "sorta_dark": "Agak Gelap",
+ "light": "Terang"
},
"notifications": {
"title": "Notifikasi",
@@ -602,17 +640,17 @@
"anyone": "siapapun",
"follower": "seorang pengikut",
"follow": "siapapun yang saya ikuti",
- "noone": "no one",
+ "noone": "tidak ada",
"title": "Beritahu saya ketika"
}
},
"preference": {
"title": "Preferensi",
"true_black_dark_mode": "True black dark mode",
- "disable_avatar_animation": "Disable animated avatars",
- "disable_emoji_animation": "Disable animated emojis",
+ "disable_avatar_animation": "Nonaktifkan animasi avatar",
+ "disable_emoji_animation": "Nonaktifkan animasi emoji",
"using_default_browser": "Use default browser to open links",
- "open_links_in_mastodon": "Open links in Mastodon"
+ "open_links_in_mastodon": "Buka tautan di Mastodon"
},
"boring_zone": {
"title": "Zona Membosankan",
@@ -634,79 +672,91 @@
}
},
"report": {
- "title_report": "Report",
+ "title_report": "Laporkan",
"title": "Laporkan %s",
"step1": "Langkah 1 dari 2",
"step2": "Langkah 2 dari 2",
"content1": "Apakah ada postingan lain yang ingin Anda tambahkan ke laporannya?",
"content2": "Ada yang moderator harus tahu tentang laporan ini?",
- "report_sent_title": "Thanks for reporting, we’ll look into this.",
+ "report_sent_title": "Terima kasih atas pelaporan Anda, kami akan memeriksa ini lebih lanjut.",
"send": "Kirim Laporan",
"skip_to_send": "Kirim tanpa komentar",
"text_placeholder": "Ketik atau tempel komentar tambahan",
- "reported": "REPORTED",
+ "reported": "DILAPORKAN",
"step_one": {
- "step_1_of_4": "Step 1 of 4",
- "whats_wrong_with_this_post": "What's wrong with this post?",
- "whats_wrong_with_this_account": "What's wrong with this account?",
- "whats_wrong_with_this_username": "What's wrong with %s?",
- "select_the_best_match": "Select the best match",
- "i_dont_like_it": "I don’t like it",
- "it_is_not_something_you_want_to_see": "It is not something you want to see",
- "its_spam": "It’s spam",
- "malicious_links_fake_engagement_or_repetetive_replies": "Malicious links, fake engagement, or repetetive replies",
- "it_violates_server_rules": "It violates server rules",
- "you_are_aware_that_it_breaks_specific_rules": "You are aware that it breaks specific rules",
- "its_something_else": "It’s something else",
+ "step_1_of_4": "Langkah 1 dari 4",
+ "whats_wrong_with_this_post": "Ada yang salah dengan postingan ini?",
+ "whats_wrong_with_this_account": "Ada yang salah dengan akun ini?",
+ "whats_wrong_with_this_username": "Ada yang salah dengan %s?",
+ "select_the_best_match": "Pilih yang paling cocok",
+ "i_dont_like_it": "Saya tidak suka",
+ "it_is_not_something_you_want_to_see": "Ini bukan sesuatu yang Anda ingin lihat",
+ "its_spam": "Ini sampah",
+ "malicious_links_fake_engagement_or_repetetive_replies": "Tautan berbahaya, engagement palsu, atau balasan berulang",
+ "it_violates_server_rules": "Melanggar ketentuan server",
+ "you_are_aware_that_it_breaks_specific_rules": "Anda yakin bahwa ini melanggar ketentuan khusus",
+ "its_something_else": "Alasan lainnya",
"the_issue_does_not_fit_into_other_categories": "The issue does not fit into other categories"
},
"step_two": {
- "step_2_of_4": "Step 2 of 4",
- "which_rules_are_being_violated": "Which rules are being violated?",
- "select_all_that_apply": "Select all that apply",
+ "step_2_of_4": "Langkah 2 dari 4",
+ "which_rules_are_being_violated": "Ketentuan manakah yang dilanggar?",
+ "select_all_that_apply": "Pilih semua yang berlaku",
"i_just_don’t_like_it": "I just don’t like it"
},
"step_three": {
- "step_3_of_4": "Step 3 of 4",
+ "step_3_of_4": "Langkah 3 dari 4",
"are_there_any_posts_that_back_up_this_report": "Are there any posts that back up this report?",
- "select_all_that_apply": "Select all that apply"
+ "select_all_that_apply": "Pilih semua yang berlaku"
},
"step_four": {
- "step_4_of_4": "Step 4 of 4",
- "is_there_anything_else_we_should_know": "Is there anything else we should know?"
+ "step_4_of_4": "Langkah 4 dari 4",
+ "is_there_anything_else_we_should_know": "Ada hal lain yang perlu kami ketahui?"
},
"step_final": {
- "dont_want_to_see_this": "Don’t want to see this?",
+ "dont_want_to_see_this": "Tidak ingin melihat ini?",
"when_you_see_something_you_dont_like_on_mastodon_you_can_remove_the_person_from_your_experience.": "When you see something you don’t like on Mastodon, you can remove the person from your experience.",
- "unfollow": "Unfollow",
+ "unfollow": "Berhenti ikuti",
"unfollowed": "Unfollowed",
- "unfollow_user": "Unfollow %s",
- "mute_user": "Mute %s",
+ "unfollow_user": "Berhenti ikuti %s",
+ "mute_user": "Senyapkan %s",
"you_wont_see_their_posts_or_reblogs_in_your_home_feed_they_wont_know_they_ve_been_muted": "You won’t see their posts or reblogs in your home feed. They won’t know they’ve been muted.",
- "block_user": "Block %s",
+ "block_user": "Blokir %s",
"they_will_no_longer_be_able_to_follow_or_see_your_posts_but_they_can_see_if_theyve_been_blocked": "They will no longer be able to follow or see your posts, but they can see if they’ve been blocked.",
"while_we_review_this_you_can_take_action_against_user": "While we review this, you can take action against %s"
}
},
"preview": {
"keyboard": {
- "close_preview": "Close Preview",
- "show_next": "Show Next",
- "show_previous": "Show Previous"
+ "close_preview": "Tutup Pratinjau",
+ "show_next": "Tampilkan Berikutnya",
+ "show_previous": "Tampilkan Sebelumnya"
}
},
"account_list": {
- "tab_bar_hint": "Current selected profile: %s. Double tap then hold to show account switcher",
+ "tab_bar_hint": "Profil yang dipilih saat ini: %s. Ketuk dua kali kemudian tahan untuk tampilkan ikon beralih akun",
"dismiss_account_switcher": "Dismiss Account Switcher",
- "add_account": "Add Account"
+ "add_account": "Tambah Akun"
},
"wizard": {
- "new_in_mastodon": "New in Mastodon",
+ "new_in_mastodon": "Baru di Mastodon",
"multiple_account_switch_intro_description": "Switch between multiple accounts by holding the profile button.",
"accessibility_hint": "Double tap to dismiss this wizard"
},
"bookmark": {
- "title": "Bookmarks"
+ "title": "Tandai"
+ },
+ "followed_tags": {
+ "title": "Followed Tags",
+ "header": {
+ "posts": "posts",
+ "participants": "participants",
+ "posts_today": "posts today"
+ },
+ "actions": {
+ "follow": "Follow",
+ "unfollow": "Unfollow"
+ }
}
}
}
diff --git a/Localization/StringsConvertor/input/id.lproj/ios-infoPlist.json b/Localization/StringsConvertor/input/id.lproj/ios-infoPlist.json
index 0dde7c29e..77bf594c3 100644
--- a/Localization/StringsConvertor/input/id.lproj/ios-infoPlist.json
+++ b/Localization/StringsConvertor/input/id.lproj/ios-infoPlist.json
@@ -1,6 +1,6 @@
{
- "NSCameraUsageDescription": "Used to take photo for post status",
- "NSPhotoLibraryAddUsageDescription": "Used to save photo into the Photo Library",
+ "NSCameraUsageDescription": "Gunakan untuk mengambil foto untuk postingan status",
+ "NSPhotoLibraryAddUsageDescription": "Gunakan untuk menyimpan foto ke dalam Galeri Foto",
"NewPostShortcutItemTitle": "Postingan Baru",
"SearchShortcutItemTitle": "Cari"
}
diff --git a/Localization/StringsConvertor/input/is.lproj/Localizable.stringsdict b/Localization/StringsConvertor/input/is.lproj/Localizable.stringsdict
new file mode 100644
index 000000000..03b29f09b
--- /dev/null
+++ b/Localization/StringsConvertor/input/is.lproj/Localizable.stringsdict
@@ -0,0 +1,465 @@
+
+
+
+
+ a11y.plural.count.unread.notification
+
+ NSStringLocalizedFormatKey
+ %#@notification_count_unread_notification@
+ notification_count_unread_notification
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 ólesin tilkynning
+ other
+ %ld ólesnar tilkynningar
+
+
+ a11y.plural.count.input_limit_exceeds
+
+ NSStringLocalizedFormatKey
+ Inntak fer fram úr takmörkunum %#@character_count@
+ character_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 stafur
+ other
+ %ld stafir
+
+
+ a11y.plural.count.input_limit_remains
+
+ NSStringLocalizedFormatKey
+ Inntakstakmörk haldast %#@character_count@
+ character_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 stafur
+ other
+ %ld stafir
+
+
+ a11y.plural.count.characters_left
+
+ NSStringLocalizedFormatKey
+ %#@character_count@ eftir
+ character_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 stafur
+ other
+ %ld stafir
+
+
+ plural.count.followed_by_and_mutual
+
+ NSStringLocalizedFormatKey
+ %#@names@%#@count_mutual@
+ names
+
+ one
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ other
+
+
+ count_mutual
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ Fylgt af %1$@ og öðrum sameiginlegum
+ other
+ Fylgt af %1$@ og %ld sameiginlegum
+
+
+ plural.count.metric_formatted.post
+
+ NSStringLocalizedFormatKey
+ %@ %#@post_count@
+ post_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ færsla
+ other
+ færslur
+
+
+ plural.count.media
+
+ NSStringLocalizedFormatKey
+ %#@media_count@
+ media_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 gagnamiðill
+ other
+ %ld gagnamiðlar
+
+
+ plural.count.post
+
+ NSStringLocalizedFormatKey
+ %#@post_count@
+ post_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 færsla
+ other
+ %ld færslur
+
+
+ plural.count.favorite
+
+ NSStringLocalizedFormatKey
+ %#@favorite_count@
+ favorite_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 eftirlæti
+ other
+ %ld eftirlæti
+
+
+ plural.count.reblog
+
+ NSStringLocalizedFormatKey
+ %#@reblog_count@
+ reblog_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 endurbirting
+ other
+ %ld endurbirtingar
+
+
+ plural.count.reply
+
+ NSStringLocalizedFormatKey
+ %#@reply_count@
+ reply_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 svar
+ other
+ %ld svör
+
+
+ plural.count.vote
+
+ NSStringLocalizedFormatKey
+ %#@vote_count@
+ vote_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 atkvæði
+ other
+ %ld atkvæði
+
+
+ plural.count.voter
+
+ NSStringLocalizedFormatKey
+ %#@voter_count@
+ voter_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 kjósandi
+ other
+ %ld kjósendur
+
+
+ plural.people_talking
+
+ NSStringLocalizedFormatKey
+ %#@count_people_talking@
+ count_people_talking
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 aðili að spjalla
+ other
+ %ld aðilar að spjalla
+
+
+ plural.count.following
+
+ NSStringLocalizedFormatKey
+ %#@count_following@
+ count_following
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 fylgist með
+ other
+ %ld fylgjast með
+
+
+ plural.count.follower
+
+ NSStringLocalizedFormatKey
+ %#@count_follower@
+ count_follower
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 fylgjandi
+ other
+ %ld fylgjendur
+
+
+ date.year.left
+
+ NSStringLocalizedFormatKey
+ %#@count_year_left@
+ count_year_left
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 ár eftir
+ other
+ %ld ár eftir
+
+
+ date.month.left
+
+ NSStringLocalizedFormatKey
+ %#@count_month_left@
+ count_month_left
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 mánuður eftir
+ other
+ %ld mánuðir eftir
+
+
+ date.day.left
+
+ NSStringLocalizedFormatKey
+ %#@count_day_left@
+ count_day_left
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 dagur eftir
+ other
+ %ld dagar eftir
+
+
+ date.hour.left
+
+ NSStringLocalizedFormatKey
+ %#@count_hour_left@
+ count_hour_left
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 klukkustund eftir
+ other
+ %ld klukkustundir eftir
+
+
+ date.minute.left
+
+ NSStringLocalizedFormatKey
+ %#@count_minute_left@
+ count_minute_left
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 mínúta eftir
+ other
+ %ld mínútur eftir
+
+
+ date.second.left
+
+ NSStringLocalizedFormatKey
+ %#@count_second_left@
+ count_second_left
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 sekúnda eftir
+ other
+ %ld sekúndur eftir
+
+
+ date.year.ago.abbr
+
+ NSStringLocalizedFormatKey
+ %#@count_year_ago_abbr@
+ count_year_ago_abbr
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ Fyrir 1 ári síðan
+ other
+ Fyrir %ld árum síðan
+
+
+ date.month.ago.abbr
+
+ NSStringLocalizedFormatKey
+ %#@count_month_ago_abbr@
+ count_month_ago_abbr
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ Fyrir 1mín síðan
+ other
+ Fyrir %ldmín síðan
+
+
+ date.day.ago.abbr
+
+ NSStringLocalizedFormatKey
+ %#@count_day_ago_abbr@
+ count_day_ago_abbr
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ Fyrir 1 degi síðan
+ other
+ Fyrir %ld dögum síðan
+
+
+ date.hour.ago.abbr
+
+ NSStringLocalizedFormatKey
+ %#@count_hour_ago_abbr@
+ count_hour_ago_abbr
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1klst síðan
+ other
+ %ldklst síðan
+
+
+ date.minute.ago.abbr
+
+ NSStringLocalizedFormatKey
+ %#@count_minute_ago_abbr@
+ count_minute_ago_abbr
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1m síðan
+ other
+ %ldm síðan
+
+
+ date.second.ago.abbr
+
+ NSStringLocalizedFormatKey
+ %#@count_second_ago_abbr@
+ count_second_ago_abbr
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1s síðan
+ other
+ %lds síðan
+
+
+
+
diff --git a/Localization/StringsConvertor/input/is.lproj/app.json b/Localization/StringsConvertor/input/is.lproj/app.json
new file mode 100644
index 000000000..3d01bd737
--- /dev/null
+++ b/Localization/StringsConvertor/input/is.lproj/app.json
@@ -0,0 +1,762 @@
+{
+ "common": {
+ "alerts": {
+ "common": {
+ "please_try_again": "Endilega reyndu aftur.",
+ "please_try_again_later": "Reyndu aftur síðar."
+ },
+ "sign_up_failure": {
+ "title": "Innskráning mistókst"
+ },
+ "server_error": {
+ "title": "Villa á þjóni"
+ },
+ "vote_failure": {
+ "title": "Greiðsla atkvæðis mistókst",
+ "poll_ended": "Könnuninni er lokið"
+ },
+ "discard_post_content": {
+ "title": "Henda drögum",
+ "message": "Staðfestu til að henda efni úr saminni færslu."
+ },
+ "publish_post_failure": {
+ "title": "Mistókst að birta",
+ "message": "Mistókst að birta færsluna.\nAthugaðu nettenginguna þína.",
+ "attachments_message": {
+ "video_attach_with_photo": "Ekki er hægt að hengja myndskeið við færslu sem þegar inniheldur myndir.",
+ "more_than_one_video": "Ekki er hægt að hengja við fleiri en eitt myndskeið."
+ }
+ },
+ "edit_profile_failure": {
+ "title": "Villa við breytingu á notandasniði",
+ "message": "Mistókst að breyta notandasniði. Endilega reyndu aftur."
+ },
+ "sign_out": {
+ "title": "Skrá út",
+ "message": "Ertu viss um að þú viljir skrá þig út?",
+ "confirm": "Skrá út"
+ },
+ "block_domain": {
+ "title": "Ertu alveg algjörlega viss um að þú viljir loka á allt %s? Í flestum tilfellum er vænlegra að nota færri en markvissari útilokanir eða að þagga niður tiltekna aðila. Þú munt ekki sjá neitt efni frá þessu léni og fylgjendur þínir frá þessu léni verða fjarlægðir.",
+ "block_entire_domain": "Útiloka lén"
+ },
+ "save_photo_failure": {
+ "title": "Mistókst að vista mynd",
+ "message": "Virkjaðu heimild til aðgangs að ljósmyndasafninu til að vista myndina."
+ },
+ "delete_post": {
+ "title": "Eyða færslu",
+ "message": "Ertu viss um að þú viljir eyða þessari færslu?"
+ },
+ "clean_cache": {
+ "title": "Hreinsa skyndiminni",
+ "message": "Tókst að hreinsa %s skyndiminni."
+ },
+ "translation_failed": {
+ "title": "Athugasemd",
+ "message": "Þýðing mistókst. Mögulega hefur kerfisstjórinn ekki virkjað þýðingar á þessum netþjóni, eða að netþjónninn sé keyrður á eldri útgáfu Mastodon þar sem þýðingar séu ekki studdar.",
+ "button": "Í lagi"
+ }
+ },
+ "controls": {
+ "actions": {
+ "back": "Til baka",
+ "next": "Næsta",
+ "previous": "Fyrri",
+ "open": "Opna",
+ "add": "Bæta við",
+ "remove": "Fjarlægja",
+ "edit": "Breyta",
+ "save": "Vista",
+ "ok": "Í lagi",
+ "done": "Lokið",
+ "confirm": "Staðfesta",
+ "continue": "Halda áfram",
+ "compose": "Skrifa",
+ "cancel": "Hætta við",
+ "discard": "Henda",
+ "try_again": "Reyna aftur",
+ "take_photo": "Taka ljósmynd",
+ "save_photo": "Vista mynd",
+ "copy_photo": "Afrita mynd",
+ "sign_in": "Skrá inn",
+ "sign_up": "Stofna notandaaðgang",
+ "see_more": "Sjá fleira",
+ "preview": "Forskoða",
+ "copy": "Afrita",
+ "share": "Deila",
+ "share_user": "Deila %s",
+ "share_post": "Deila færslu",
+ "open_in_safari": "Opna í Safari",
+ "open_in_browser": "Opna í vafra",
+ "find_people": "Finna fólk til að fylgjast með",
+ "manually_search": "Leita handvirkt í staðinn",
+ "skip": "Sleppa",
+ "reply": "Svara",
+ "report_user": "Kæra %s",
+ "block_domain": "Útiloka %s",
+ "unblock_domain": "Opna á %s",
+ "settings": "Stillingar",
+ "delete": "Eyða",
+ "translate_post": {
+ "title": "Þýða úr %s",
+ "unknown_language": "Óþekkt"
+ }
+ },
+ "tabs": {
+ "home": "Heim",
+ "search_and_explore": "Leita og kanna",
+ "notifications": "Tilkynningar",
+ "profile": "Notandasnið"
+ },
+ "keyboard": {
+ "common": {
+ "switch_to_tab": "Skipta yfir í %s",
+ "compose_new_post": "Semja nýja færslu",
+ "show_favorites": "Birta eftirlæti",
+ "open_settings": "Opna stillingar"
+ },
+ "timeline": {
+ "previous_status": "Fyrri færsla",
+ "next_status": "Næsta færsla",
+ "open_status": "Opna færslu",
+ "open_author_profile": "Opna notandasnið höfundar",
+ "open_reblogger_profile": "Opna notandasnið þess sem endurbirtir",
+ "reply_status": "Svara færslu",
+ "toggle_reblog": "Víxla endurbirtingu færslu af/á",
+ "toggle_favorite": "Víxla eftirlæti færslu af/á",
+ "toggle_content_warning": "Víxla af/á viðvörun vegna efnis",
+ "preview_image": "Forskoða mynd"
+ },
+ "segmented_control": {
+ "previous_section": "Fyrri hluti",
+ "next_section": "Næsti hluti"
+ }
+ },
+ "status": {
+ "user_reblogged": "%s endurbirti",
+ "user_replied_to": "Svaraði %s",
+ "show_post": "Sýna færslu",
+ "show_user_profile": "Birta notandasnið",
+ "content_warning": "Viðvörun vegna efnis",
+ "sensitive_content": "Viðkvæmt efni",
+ "media_content_warning": "Ýttu hvar sem er til að birta",
+ "tap_to_reveal": "Ýttu til að birta",
+ "load_embed": "Hlaða inn ívöfnu",
+ "link_via_user": "%s með %s",
+ "poll": {
+ "vote": "Greiða atkvæði",
+ "closed": "Lokið"
+ },
+ "meta_entity": {
+ "url": "Tengill: %s",
+ "hashtag": "Myllumerki: %s",
+ "mention": "Sýna notandasnið: %s",
+ "email": "Tölvupóstfang: %s"
+ },
+ "actions": {
+ "reply": "Svara",
+ "reblog": "Endurbirta",
+ "unreblog": "Afturkalla endurbirtingu",
+ "favorite": "Eftirlæti",
+ "unfavorite": "Taka úr eftirlætum",
+ "menu": "Valmynd",
+ "hide": "Fela",
+ "show_image": "Sýna mynd",
+ "show_gif": "Birta GIF-myndir",
+ "show_video_player": "Sýna myndspilara",
+ "share_link_in_post": "Deila tengli í færslu",
+ "tap_then_hold_to_show_menu": "Ýttu og haltu til að sýna valmynd"
+ },
+ "tag": {
+ "url": "URL-slóð",
+ "mention": "Minnst á",
+ "link": "Tengill",
+ "hashtag": "Myllumerki",
+ "email": "Tölvupóstur",
+ "emoji": "Tjáningartákn"
+ },
+ "visibility": {
+ "unlisted": "Allir geta skoðað þessa færslu, en er ekki birt á opinberum tímalínum.",
+ "private": "Einungis fylgjendur þeirra geta séð þessa færslu.",
+ "private_from_me": "Einungis fylgjendur mínir geta séð þessa færslu.",
+ "direct": "Einungis notendur sem minnst er á geta séð þessa færslu."
+ },
+ "translation": {
+ "translated_from": "Þýtt úr %s með %s",
+ "unknown_language": "Óþekkt",
+ "unknown_provider": "Óþekkt",
+ "show_original": "Birta upprunalegt"
+ }
+ },
+ "friendship": {
+ "follow": "Fylgja",
+ "following": "Fylgist með",
+ "request": "Beiðni",
+ "pending": "Í bið",
+ "block": "Útilokun",
+ "block_user": "Útiloka %s",
+ "block_domain": "Útiloka %s",
+ "unblock": "Aflétta útilokun",
+ "unblock_user": "Opna á %s",
+ "blocked": "Útilokað",
+ "mute": "Þagga niður",
+ "mute_user": "Þagga niður í %s",
+ "unmute": "Afþagga",
+ "unmute_user": "Afþagga %s",
+ "muted": "Þaggað",
+ "edit_info": "Breyta upplýsingum",
+ "show_reblogs": "Sýna endurbirtingar",
+ "hide_reblogs": "Fela endurbirtingar"
+ },
+ "timeline": {
+ "filtered": "Síað",
+ "timestamp": {
+ "now": "Núna"
+ },
+ "loader": {
+ "load_missing_posts": "Hlaða inn færslum sem vantar",
+ "loading_missing_posts": "Hleð inn færslum sem vantar...",
+ "show_more_replies": "Birta fleiri svör"
+ },
+ "header": {
+ "no_status_found": "Engar færslur fundust",
+ "blocking_warning": "Þú getur ekki séð snið þessa notanda\nfyrr en þú hættir að útiloka hann.\nSniðið þitt lítur svona út hjá honum.",
+ "user_blocking_warning": "Þú getur ekki séð sniðið hjá %s\nfyrr en þú hættir að útiloka hann.\nSniðið þitt lítur svona út hjá honum.",
+ "blocked_warning": "Þú getur ekki séð sniðið hjá þessum notanda\nfyrr en hann hættir að útiloka þig.",
+ "user_blocked_warning": "Þú getur ekki séð sniðið hjá %s\nfyrr en hann hættir að útiloka þig.",
+ "suspended_warning": "Þessi notandi hefur verið settur í frysti.",
+ "user_suspended_warning": "Notandaaðgangurinn %s hefur verið settur í frysti."
+ }
+ }
+ }
+ },
+ "scene": {
+ "welcome": {
+ "slogan": "Samfélagsmiðlar\naftur í þínar hendur.",
+ "get_started": "Komast í gang",
+ "log_in": "Skrá inn"
+ },
+ "login": {
+ "title": "Velkomin aftur",
+ "subtitle": "Skráðu þig inn á netþjóninum þar sem þú útbjóst aðganginn þinn.",
+ "server_search_field": {
+ "placeholder": "Settu inn slóð eða leitaðu að þjóninum þínum"
+ }
+ },
+ "server_picker": {
+ "title": "Mastodon samanstendur af notendum á mismunandi netþjónum.",
+ "subtitle": "Veldu netþjón út frá svæðinu þínu, áhugamálum, nú eða einhvern almennan. Þú getur samt spjallað við hvern sem er á Mastodon, burtséð frá á hvaða netþjóni þú ert.",
+ "button": {
+ "category": {
+ "all": "Allt",
+ "all_accessiblity_description": "Flokkur: Allt",
+ "academia": "akademískt",
+ "activism": "aðgerðasinnar",
+ "food": "matur",
+ "furry": "loðið",
+ "games": "leikir",
+ "general": "almennt",
+ "journalism": "blaðamennska",
+ "lgbt": "lgbt",
+ "regional": "svæðisbundið",
+ "art": "listir",
+ "music": "tónlist",
+ "tech": "tækni"
+ },
+ "see_less": "Sjá minna",
+ "see_more": "Sjá meira"
+ },
+ "label": {
+ "language": "TUNGUMÁL",
+ "users": "NOTENDUR",
+ "category": "FLOKKUR"
+ },
+ "input": {
+ "search_servers_or_enter_url": "Leitaðu að samfélögum eða settu inn slóð"
+ },
+ "empty_state": {
+ "finding_servers": "Finn tiltæka netþjóna...",
+ "bad_network": "Eitthvað fór úrskeiðis við að hlaða inn gögnunum. Athugaðu nettenginguna þína.",
+ "no_results": "Engar niðurstöður"
+ }
+ },
+ "register": {
+ "title": "Við skulum koma þér í gang á %s",
+ "lets_get_you_set_up_on_domain": "Við skulum koma þér í gang á %s",
+ "input": {
+ "avatar": {
+ "delete": "Eyða"
+ },
+ "username": {
+ "placeholder": "notandanafn",
+ "duplicate_prompt": "Þetta notandanafn er þegar í notkun."
+ },
+ "display_name": {
+ "placeholder": "birtingarnafn"
+ },
+ "email": {
+ "placeholder": "tölvupóstur"
+ },
+ "password": {
+ "placeholder": "lykilorð",
+ "require": "Lykilorðið þitt þarf að minnsta kosti:",
+ "character_limit": "8 stafi",
+ "accessibility": {
+ "checked": "merkt",
+ "unchecked": "ekki merkt"
+ },
+ "hint": "Lykilorðið þitt verður að vera að minnsta kosti 8 stafa langt"
+ },
+ "invite": {
+ "registration_user_invite_request": "Hvers vegna vilt þú taka þátt?"
+ }
+ },
+ "error": {
+ "item": {
+ "username": "Notandanafn",
+ "email": "Tölvupóstur",
+ "password": "Lykilorð",
+ "agreement": "Notkunarskilmálar",
+ "locale": "Staðfærsla",
+ "reason": "Ástæða"
+ },
+ "reason": {
+ "blocked": "%s notar óleyfilega tölvupóstþjónustu",
+ "unreachable": "%s virðist ekki vera til",
+ "taken": "%s er þegar í notkun",
+ "reserved": "%s er frátekið stikkorð",
+ "accepted": "%s verður að samþykkja",
+ "blank": "%s ier nauðsynlegt",
+ "invalid": "%s er ógilt",
+ "too_long": "%s er of langt",
+ "too_short": "%s er of stutt",
+ "inclusion": "%s er ekki stutt gildi"
+ },
+ "special": {
+ "username_invalid": "Notendanöfn geta einungis innihaldið bókstafi og undirstrikun",
+ "username_too_long": "Notandanafnið er of langt (má ekki vera lengra en 30 stafir)",
+ "email_invalid": "Þetta lítur ekki út eins og löglegt tölvupóstfang",
+ "password_too_short": "Lykilorð er of stutt (verður að hafa minnst 8 stafi)"
+ }
+ }
+ },
+ "server_rules": {
+ "title": "Nokkrar grunnreglur.",
+ "subtitle": "Þær eru settar og séð um að þeim sé fylgt af umsjónarmönnum %s.",
+ "prompt": "Með því að halda áfram samþykkir þú þjónustuskilmála og persónuverndarstefnu %s.",
+ "terms_of_service": "þjónustuskilmálar",
+ "privacy_policy": "persónuverndarstefna",
+ "button": {
+ "confirm": "Ég samþykki"
+ }
+ },
+ "confirm_email": {
+ "title": "Eitt að lokum.",
+ "subtitle": "Ýttu á tengilinn sem við sendum þér til að staðfesta tölvupóstfangið þitt.",
+ "tap_the_link_we_emailed_to_you_to_verify_your_account": "Ýttu á tengilinn sem við sendum þér til að staðfesta tölvupóstfangið þitt",
+ "button": {
+ "open_email_app": "Opna tölvupóstforrit",
+ "resend": "Endursenda"
+ },
+ "dont_receive_email": {
+ "title": "Athugaðu tölvupóstinn þinn",
+ "description": "Athugaðu hvort tölvupóstfangið þitt sé rétt auk þess að skoða í ruslpóstmöppuna þína ef þú hefur ekki gert það.",
+ "resend_email": "Endursenda tölvupóst"
+ },
+ "open_email_app": {
+ "title": "Athugaðu pósthólfið þitt.",
+ "description": "Við vorum að senda þér tölvupóst. Skoðaðu í ruslpóstmöppuna þína ef þú hefur ekki gert það.",
+ "mail": "Tölvupóstur",
+ "open_email_client": "Opna tölvupóstforrit"
+ }
+ },
+ "home_timeline": {
+ "title": "Heim",
+ "navigation_bar_state": {
+ "offline": "Ónettengt",
+ "new_posts": "Skoða nýjar færslur",
+ "published": "Birt!",
+ "Publishing": "Birti færslu...",
+ "accessibility": {
+ "logo_label": "Hnappur með táknmerki",
+ "logo_hint": "Ýttu til að skruna efst og ýttu aftur til að fara aftur á fyrri staðsetningu"
+ }
+ }
+ },
+ "suggestion_account": {
+ "title": "Finndu fólk til að fylgjast með",
+ "follow_explain": "Þegar þú fylgist með einhverjum, muntu sjá færslur frá viðkomandi á streyminu þínu."
+ },
+ "compose": {
+ "title": {
+ "new_post": "Ný færsla",
+ "new_reply": "Nýtt svar"
+ },
+ "media_selection": {
+ "camera": "Taktu mynd",
+ "photo_library": "Myndasafn",
+ "browse": "Flakka"
+ },
+ "content_input_placeholder": "Skrifaðu eða límdu það sem þér liggur á hjarta",
+ "compose_action": "Birta",
+ "replying_to_user": "svarar til @%s",
+ "attachment": {
+ "photo": "ljósmynd",
+ "video": "myndskeið",
+ "attachment_broken": "Þetta %s er skemmt og því ekki\nhægt að senda inn á Mastodon.",
+ "description_photo": "Lýstu myndinni fyrir sjónskerta...",
+ "description_video": "Lýstu myndskeiðinu fyrir sjónskerta...",
+ "load_failed": "Hleðsla mistókst",
+ "upload_failed": "Innsending mistókst",
+ "can_not_recognize_this_media_attachment": "Þekki ekki þetta myndviðhengi",
+ "attachment_too_large": "Viðhengi of stórt",
+ "compressing_state": "Þjappa...",
+ "server_processing_state": "Netþjónn er að vinna..."
+ },
+ "poll": {
+ "duration_time": "Tímalengd: %s",
+ "thirty_minutes": "30 mínútur",
+ "one_hour": "1 klukkustund",
+ "six_hours": "6 klukkustundir",
+ "one_day": "1 dagur",
+ "three_days": "3 dagar",
+ "seven_days": "7 dagar",
+ "option_number": "Valkostur %ld",
+ "the_poll_is_invalid": "Könnunin er ógild",
+ "the_poll_has_empty_option": "Könnunin er með auðan valkost"
+ },
+ "content_warning": {
+ "placeholder": "Skrifaðu nákvæma aðvörun hér..."
+ },
+ "visibility": {
+ "public": "Opinbert",
+ "unlisted": "Óskráð",
+ "private": "Einungis fylgjendur",
+ "direct": "Einungis fólk sem ég minnist á"
+ },
+ "auto_complete": {
+ "space_to_add": "Bil sem á að bæta við"
+ },
+ "accessibility": {
+ "append_attachment": "Bæta við viðhengi",
+ "append_poll": "Bæta við könnun",
+ "remove_poll": "Fjarlægja könnun",
+ "custom_emoji_picker": "Sérsniðið emoji-tánmyndaval",
+ "enable_content_warning": "Virkja viðvörun vegna efnis",
+ "disable_content_warning": "Gera viðvörun vegna efnis óvirka",
+ "post_visibility_menu": "Sýnileikavalmynd færslu",
+ "post_options": "Valkostir færslu",
+ "posting_as": "Birti sem %s"
+ },
+ "keyboard": {
+ "discard_post": "Henda færslu",
+ "publish_post": "Birta færslu",
+ "toggle_poll": "Víxla könnun af/á",
+ "toggle_content_warning": "Víxla af/á viðvörun vegna efnis",
+ "append_attachment_entry": "Bæta við viðhengi - %s",
+ "select_visibility_entry": "Veldu sýnileika - %s"
+ }
+ },
+ "profile": {
+ "header": {
+ "follows_you": "Fylgist með þér"
+ },
+ "dashboard": {
+ "my_posts": "færslur",
+ "my_following": "er fylgst með",
+ "my_followers": "fylgjendur",
+ "other_posts": "færslur",
+ "other_following": "er fylgst með",
+ "other_followers": "fylgjendur"
+ },
+ "fields": {
+ "joined": "Gerðist þátttakandi",
+ "add_row": "Bæta við röð",
+ "placeholder": {
+ "label": "Skýring",
+ "content": "Efni"
+ },
+ "verified": {
+ "short": "Sannreynt þann %s",
+ "long": "Eignarhald á þessum tengli var athugað þann %s"
+ }
+ },
+ "segmented_control": {
+ "posts": "Færslur",
+ "replies": "Svör",
+ "posts_and_replies": "Færslur og svör",
+ "media": "Gagnamiðlar",
+ "about": "Um hugbúnaðinn"
+ },
+ "relationship_action_alert": {
+ "confirm_mute_user": {
+ "title": "Þagga niður í aðgangi",
+ "message": "Staðfestu til að þagga niður í %s"
+ },
+ "confirm_unmute_user": {
+ "title": "Hætta að þagga niður í aðgangi",
+ "message": "Staðfestu til hætta að að þagga niður í %s"
+ },
+ "confirm_block_user": {
+ "title": "Útiloka notandaaðgang",
+ "message": "Staðfestu til að útiloka %s"
+ },
+ "confirm_unblock_user": {
+ "title": "Aflétta útilokun aðgangs",
+ "message": "Staðfestu til að hætta að útiloka %s"
+ },
+ "confirm_show_reblogs": {
+ "title": "Sýna endurbirtingar",
+ "message": "Staðfestu til að sýna endurbirtingar"
+ },
+ "confirm_hide_reblogs": {
+ "title": "Fela endurbirtingar",
+ "message": "Staðfestu til að fela endurbirtingar"
+ }
+ },
+ "accessibility": {
+ "show_avatar_image": "Sýna auðkennismynd",
+ "edit_avatar_image": "Breyta auðkennismynd",
+ "show_banner_image": "Sýna myndborða",
+ "double_tap_to_open_the_list": "Tvípikkaðu til að opna listann"
+ }
+ },
+ "follower": {
+ "title": "fylgjandi",
+ "footer": "Fylgjendur af öðrum netþjónum birtast ekki."
+ },
+ "following": {
+ "title": "fylgist með",
+ "footer": "Fylgjendur af öðrum netþjónum birtast ekki."
+ },
+ "familiarFollowers": {
+ "title": "Fylgjendur sem þú kannast við",
+ "followed_by_names": "Fylgt af %s"
+ },
+ "favorited_by": {
+ "title": "Sett í eftirlæti af"
+ },
+ "reblogged_by": {
+ "title": "Endurbirt af"
+ },
+ "search": {
+ "title": "Leita",
+ "search_bar": {
+ "placeholder": "Leita að myllumerkjum og notendum",
+ "cancel": "Hætta við"
+ },
+ "recommend": {
+ "button_text": "Sjá allt",
+ "hash_tag": {
+ "title": "Vinsælt á Mastodon",
+ "description": "Myllumerki sem eru að fá þónokkra athygli",
+ "people_talking": "%s manns eru að spjalla"
+ },
+ "accounts": {
+ "title": "Notandaaðgangar sem þú gætir haft áhuga á",
+ "description": "Þú gætir viljað fylgjast með þessum aðgöngum",
+ "follow": "Fylgjast með"
+ }
+ },
+ "searching": {
+ "segment": {
+ "all": "Allt",
+ "people": "Fólk",
+ "hashtags": "Myllumerki",
+ "posts": "Færslur"
+ },
+ "empty_state": {
+ "no_results": "Engar niðurstöður"
+ },
+ "recent_search": "Nýlegar leitir",
+ "clear": "Hreinsa"
+ }
+ },
+ "discovery": {
+ "tabs": {
+ "posts": "Færslur",
+ "hashtags": "Myllumerki",
+ "news": "Fréttir",
+ "community": "Samfélag",
+ "for_you": "Fyrir þig"
+ },
+ "intro": "Þetta eru færslurnar sem eru að fá aukna athygli í þínu horni á Mastodon."
+ },
+ "favorite": {
+ "title": "Eftirlætin þín"
+ },
+ "notification": {
+ "title": {
+ "Everything": "Allt",
+ "Mentions": "Minnst á"
+ },
+ "notification_description": {
+ "followed_you": "fylgdi þér",
+ "favorited_your_post": "setti færslu frá þér í eftirlæti",
+ "reblogged_your_post": "endurbirti færsluna þína",
+ "mentioned_you": "minntist á þig",
+ "request_to_follow_you": "bað um að fylgjast með þér",
+ "poll_has_ended": "könnun er lokið"
+ },
+ "keyobard": {
+ "show_everything": "Sýna allt",
+ "show_mentions": "Sýna þegar minnst er á"
+ },
+ "follow_request": {
+ "accept": "Samþykkja",
+ "accepted": "Samþykkt",
+ "reject": "hafna",
+ "rejected": "Hafnað"
+ }
+ },
+ "thread": {
+ "back_title": "Færsla",
+ "title": "Færsla frá %s"
+ },
+ "settings": {
+ "title": "Stillingar",
+ "section": {
+ "appearance": {
+ "title": "Útlit",
+ "automatic": "Sjálfvirkt",
+ "light": "Alltaf ljóst",
+ "dark": "Alltaf dökkt"
+ },
+ "look_and_feel": {
+ "title": "Útlit og viðmót",
+ "use_system": "Nota stillingar kerfis",
+ "really_dark": "Mjög dökkt",
+ "sorta_dark": "Nokkuð dökkt",
+ "light": "Ljóst"
+ },
+ "notifications": {
+ "title": "Tilkynningar",
+ "favorites": "Setur færsluna mína í eftirlæti",
+ "follows": "Fylgist með mér",
+ "boosts": "Endurbirtir færsluna mína",
+ "mentions": "Minnist á mig",
+ "trigger": {
+ "anyone": "hver sem er",
+ "follower": "fylgjandi",
+ "follow": "hverjum sá sem ég fylgi",
+ "noone": "enginn",
+ "title": "Tilkynna mér þegar"
+ }
+ },
+ "preference": {
+ "title": "Kjörstillingar",
+ "true_black_dark_mode": "Sannur svartur dökkur hamur",
+ "disable_avatar_animation": "Gera auðkennismyndir með hreyfingu óvirkar",
+ "disable_emoji_animation": "Gera tjáningartákn með hreyfingu óvirkar",
+ "using_default_browser": "Nota sjálfgefinn vafra til að opna tengla",
+ "open_links_in_mastodon": "Opna tengla í Mastodon"
+ },
+ "boring_zone": {
+ "title": "Óhressa svæðið",
+ "account_settings": "Stillingar aðgangs",
+ "terms": "Þjónustuskilmálar",
+ "privacy": "Meðferð persónuupplýsinga"
+ },
+ "spicy_zone": {
+ "title": "Kryddaða svæðið",
+ "clear": "Hreinsa skyndiminni margmiðlunarefnis",
+ "signout": "Skrá út"
+ }
+ },
+ "footer": {
+ "mastodon_description": "Mastodon er frjáls hugbúnaður með opinn grunnkóða. Þú getur tilkynnt vandamál í gegnum GitHub á %s (%s)"
+ },
+ "keyboard": {
+ "close_settings_window": "Loka stillingaglugga"
+ }
+ },
+ "report": {
+ "title_report": "Kæra",
+ "title": "Kæra %s",
+ "step1": "Skref 1 af 2",
+ "step2": "Skref 2 af 2",
+ "content1": "Eru einhverjar færslur sem þú myndir vilja bæta við kæruna?",
+ "content2": "Er eitthvað fleira sem umsjónarmenn ættu að vita varðandi þessa kæru?",
+ "report_sent_title": "Takk fyrir tilkynninguna, við munum skoða málið.",
+ "send": "Senda kæru",
+ "skip_to_send": "Senda án athugasemdar",
+ "text_placeholder": "Skrifaðu eða límdu aðrar athugasemdir",
+ "reported": "TILKYNNT",
+ "step_one": {
+ "step_1_of_4": "Skref 1 af 4",
+ "whats_wrong_with_this_post": "Hvað er athugavert við þessa færslu?",
+ "whats_wrong_with_this_account": "Hvað er athugavert við þennan notandaaðgang?",
+ "whats_wrong_with_this_username": "Hvað er athugavert við %s?",
+ "select_the_best_match": "Velja bestu samsvörun",
+ "i_dont_like_it": "Mér líkar það ekki",
+ "it_is_not_something_you_want_to_see": "Þetta er ekki eitthvað sem þið viljið sjá",
+ "its_spam": "Þetta er ruslpóstur",
+ "malicious_links_fake_engagement_or_repetetive_replies": "Slæmir tenglar, fölsk samskipti eða endurtekin svör",
+ "it_violates_server_rules": "Það gengur þvert á reglur fyrir netþjóninn",
+ "you_are_aware_that_it_breaks_specific_rules": "Þið eruð meðvituð um að þetta brýtur sértækar reglur",
+ "its_something_else": "Það er eitthvað annað",
+ "the_issue_does_not_fit_into_other_categories": "Vandamálið fellur ekki í aðra flokka"
+ },
+ "step_two": {
+ "step_2_of_4": "Skref 2 af 4",
+ "which_rules_are_being_violated": "Hvaða reglur eru brotnar?",
+ "select_all_that_apply": "Veldu allt sem á við",
+ "i_just_don’t_like_it": "Mér bara líkar það ekki"
+ },
+ "step_three": {
+ "step_3_of_4": "Skref 3 af 4",
+ "are_there_any_posts_that_back_up_this_report": "Eru einhverjar færslur sem styðja þessa kæru?",
+ "select_all_that_apply": "Veldu allt sem á við"
+ },
+ "step_four": {
+ "step_4_of_4": "Skref 4 af 4",
+ "is_there_anything_else_we_should_know": "Er eitthvað fleira sem við ættum að vita?"
+ },
+ "step_final": {
+ "dont_want_to_see_this": "Langar þig ekki að sjá þetta?",
+ "when_you_see_something_you_dont_like_on_mastodon_you_can_remove_the_person_from_your_experience.": "Þegar þú sér eitthvað á Mastodon sem þér líkar ekki, þá geturðu fjarlægt viðkomandi eintakling úr umhverfinu þínu.",
+ "unfollow": "Hætta að fylgjast með",
+ "unfollowed": "Hætti að fylgjast með",
+ "unfollow_user": "Hætta að fylgjast með %s",
+ "mute_user": "Þagga niður í %s",
+ "you_wont_see_their_posts_or_reblogs_in_your_home_feed_they_wont_know_they_ve_been_muted": "Þú munt ekki sjá færslur eða endurbirtingar frá viðkomandi á streyminu þínu. Viðkomandi aðilar munu ekki vita að þaggað hefur verið niður í þeim.",
+ "block_user": "Útiloka %s",
+ "they_will_no_longer_be_able_to_follow_or_see_your_posts_but_they_can_see_if_theyve_been_blocked": "Viðkomandi mun ekki lengur geta fylgst með eða séð færslurnar þínar, en munu sjá ef viðkomandi hefur verið útilokaður.",
+ "while_we_review_this_you_can_take_action_against_user": "Á meðan við yfirförum þetta, geturðu tekið til aðgerða gegn %s"
+ }
+ },
+ "preview": {
+ "keyboard": {
+ "close_preview": "Loka forskoðun",
+ "show_next": "Sýna næsta",
+ "show_previous": "Sýna fyrri"
+ }
+ },
+ "account_list": {
+ "tab_bar_hint": "Fyrirliggjandi valið notandasnið: %s. Tvípikkaðu og haltu niðri til að birta aðgangaskiptinn",
+ "dismiss_account_switcher": "Loka aðgangaskipti",
+ "add_account": "Bæta við notandaaðgangi"
+ },
+ "wizard": {
+ "new_in_mastodon": "Nýtt í Mastodon",
+ "multiple_account_switch_intro_description": "Skiptu milli notandaaðganga með því að halda niðri notandasniðshnappnum.",
+ "accessibility_hint": "Tvípikkaðu til að loka þessum leiðarvísi"
+ },
+ "bookmark": {
+ "title": "Bókamerki"
+ },
+ "followed_tags": {
+ "title": "Myllumerki sem fylgst er með",
+ "header": {
+ "posts": "færslur",
+ "participants": "þátttakendur",
+ "posts_today": "færslur í dag"
+ },
+ "actions": {
+ "follow": "Fylgjast með",
+ "unfollow": "Hætta að fylgjast með"
+ }
+ }
+ }
+}
diff --git a/Localization/StringsConvertor/input/is.lproj/ios-infoPlist.json b/Localization/StringsConvertor/input/is.lproj/ios-infoPlist.json
new file mode 100644
index 000000000..24af18431
--- /dev/null
+++ b/Localization/StringsConvertor/input/is.lproj/ios-infoPlist.json
@@ -0,0 +1,6 @@
+{
+ "NSCameraUsageDescription": "Notað til að taka mynd fyrir stöðufærslu",
+ "NSPhotoLibraryAddUsageDescription": "Notað til að vista mynd inn í ljósmyndasafnið",
+ "NewPostShortcutItemTitle": "Ný færsla",
+ "SearchShortcutItemTitle": "Leita"
+}
diff --git a/Localization/StringsConvertor/input/it.lproj/Localizable.stringsdict b/Localization/StringsConvertor/input/it.lproj/Localizable.stringsdict
index 38f986521..3a8549914 100644
--- a/Localization/StringsConvertor/input/it.lproj/Localizable.stringsdict
+++ b/Localization/StringsConvertor/input/it.lproj/Localizable.stringsdict
@@ -50,6 +50,22 @@
%ld caratteri
+ a11y.plural.count.characters_left
+
+ NSStringLocalizedFormatKey
+ %#@character_count@ rimanenti
+ character_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 carattere
+ other
+ %ld caratteri
+
+
plural.count.followed_by_and_mutual
NSStringLocalizedFormatKey
diff --git a/Localization/StringsConvertor/input/it.lproj/app.json b/Localization/StringsConvertor/input/it.lproj/app.json
index 73f42d1eb..d355a4c10 100644
--- a/Localization/StringsConvertor/input/it.lproj/app.json
+++ b/Localization/StringsConvertor/input/it.lproj/app.json
@@ -51,6 +51,11 @@
"clean_cache": {
"title": "Pulisci la cache",
"message": "Cache %s pulita con successo."
+ },
+ "translation_failed": {
+ "title": "Nota",
+ "message": "Traduzione fallita. Forse l'amministratore non ha abilitato le traduzioni su questo server o questo server sta eseguendo una versione precedente di Mastodon in cui le traduzioni non sono ancora supportate.",
+ "button": "OK"
}
},
"controls": {
@@ -75,9 +80,10 @@
"save_photo": "Salva foto",
"copy_photo": "Copia foto",
"sign_in": "Accedi",
- "sign_up": "Registrati",
+ "sign_up": "Crea un account",
"see_more": "Visualizza altro",
"preview": "Anteprima",
+ "copy": "Copia",
"share": "Condividi",
"share_user": "Condividi %s",
"share_post": "Condividi il post",
@@ -91,12 +97,16 @@
"block_domain": "Blocca %s",
"unblock_domain": "Sblocca %s",
"settings": "Impostazioni",
- "delete": "Elimina"
+ "delete": "Elimina",
+ "translate_post": {
+ "title": "Traduci da %s",
+ "unknown_language": "Sconosciuto"
+ }
},
"tabs": {
"home": "Inizio",
- "search": "Cerca",
- "notification": "Notifiche",
+ "search_and_explore": "Cerca ed Esplora",
+ "notifications": "Notifiche",
"profile": "Profilo"
},
"keyboard": {
@@ -132,6 +142,8 @@
"sensitive_content": "Contenuto sensibile",
"media_content_warning": "Tocca ovunque per rivelare",
"tap_to_reveal": "Tocca per rivelare",
+ "load_embed": "Carica Incorpora",
+ "link_via_user": "%s tramite %s",
"poll": {
"vote": "Vota",
"closed": "Chiuso"
@@ -153,13 +165,14 @@
"show_image": "Mostra immagine",
"show_gif": "Mostra GIF",
"show_video_player": "Mostra lettore video",
+ "share_link_in_post": "Condividi il collegamento nel post",
"tap_then_hold_to_show_menu": "Tocca quindi tieni premuto per mostrare il menu"
},
"tag": {
"url": "URL",
"mention": "Menzione",
"link": "Collegamento",
- "hashtag": "Etichetta",
+ "hashtag": "Hashtag",
"email": "Email",
"emoji": "Emoji"
},
@@ -168,6 +181,12 @@
"private": "Solo i loro seguaci possono vedere questo post.",
"private_from_me": "Solo i miei seguaci possono vedere questo post.",
"direct": "Solo l'utente menzionato può vedere questo post."
+ },
+ "translation": {
+ "translated_from": "Tradotto da %s utilizzando %s",
+ "unknown_language": "Sconosciuto",
+ "unknown_provider": "Sconosciuto",
+ "show_original": "Mostra l'originale"
}
},
"friendship": {
@@ -218,10 +237,16 @@
"get_started": "Inizia",
"log_in": "Accedi"
},
+ "login": {
+ "title": "Bentornato/a",
+ "subtitle": "Accedi al server sul quale hai creato il tuo account.",
+ "server_search_field": {
+ "placeholder": "Inserisci l'URL o cerca il tuo server"
+ }
+ },
"server_picker": {
"title": "Mastodon è fatto di utenti in diverse comunità.",
- "subtitle": "Scegli una comunità basata sui tuoi interessi, regione o uno scopo generale.",
- "subtitle_extend": "Scegli una comunità basata sui tuoi interessi, regione o uno scopo generale. Ogni comunità è gestita da un'organizzazione completamente indipendente o individuale.",
+ "subtitle": "Scegli un server in base alla tua regione, ai tuoi interessi o uno generico. Puoi comunque chattare con chiunque su Mastodon, indipendentemente dai tuoi server.",
"button": {
"category": {
"all": "Tutti",
@@ -248,8 +273,7 @@
"category": "CATEGORIA"
},
"input": {
- "placeholder": "Cerca comunità",
- "search_servers_or_enter_url": "Cerca i server o inserisci l'URL"
+ "search_servers_or_enter_url": "Cerca le comunità o inserisci l'URL"
},
"empty_state": {
"finding_servers": "Ricerca server disponibili...",
@@ -386,7 +410,9 @@
"load_failed": "Caricamento fallito",
"upload_failed": "Caricamento fallito",
"can_not_recognize_this_media_attachment": "Impossibile riconoscere questo allegato multimediale",
- "attachment_too_large": "Allegato troppo grande"
+ "attachment_too_large": "Allegato troppo grande",
+ "compressing_state": "Compressione in corso...",
+ "server_processing_state": "Elaborazione del server in corso..."
},
"poll": {
"duration_time": "Durata: %s",
@@ -396,7 +422,9 @@
"one_day": "1 giorno",
"three_days": "3 giorni",
"seven_days": "7 giorni",
- "option_number": "Opzione %ld"
+ "option_number": "Opzione %ld",
+ "the_poll_is_invalid": "Il sondaggio non è valido",
+ "the_poll_has_empty_option": "Il sondaggio ha un'opzione vuota"
},
"content_warning": {
"placeholder": "Scrivi un avviso accurato qui..."
@@ -417,7 +445,9 @@
"custom_emoji_picker": "Selettore Emoji personalizzato",
"enable_content_warning": "Abilita avvertimento contenuti",
"disable_content_warning": "Disabilita avviso di contenuti",
- "post_visibility_menu": "Menu di visibilità del post"
+ "post_visibility_menu": "Menu di visibilità del post",
+ "post_options": "Opzioni del messaggio",
+ "posting_as": "Pubblicazione come %s"
},
"keyboard": {
"discard_post": "Scarta post",
@@ -433,15 +463,23 @@
"follows_you": "Ti segue"
},
"dashboard": {
- "posts": "post",
- "following": "seguendo",
- "followers": "seguaci"
+ "my_posts": "post",
+ "my_following": "seguendo",
+ "my_followers": "seguaci",
+ "other_posts": "post",
+ "other_following": "seguendo",
+ "other_followers": "seguaci"
},
"fields": {
+ "joined": "Profilo iscritto",
"add_row": "Aggiungi riga",
"placeholder": {
"label": "Etichetta",
"content": "Contenuto"
+ },
+ "verified": {
+ "short": "Verificato il %s",
+ "long": "La proprietà di questo collegamento è stata verificata il %s"
}
},
"segmented_control": {
@@ -707,6 +745,18 @@
},
"bookmark": {
"title": "Segnalibri"
+ },
+ "followed_tags": {
+ "title": "Etichette seguite",
+ "header": {
+ "posts": "post",
+ "participants": "partecipanti",
+ "posts_today": "post di oggi"
+ },
+ "actions": {
+ "follow": "Segui",
+ "unfollow": "Smetti di seguire"
+ }
}
}
}
diff --git a/Localization/StringsConvertor/input/ja.lproj/Localizable.stringsdict b/Localization/StringsConvertor/input/ja.lproj/Localizable.stringsdict
index cbc999738..795a971b7 100644
--- a/Localization/StringsConvertor/input/ja.lproj/Localizable.stringsdict
+++ b/Localization/StringsConvertor/input/ja.lproj/Localizable.stringsdict
@@ -44,6 +44,20 @@
%ld 文字
+ a11y.plural.count.characters_left
+
+ NSStringLocalizedFormatKey
+ %#@character_count@ left
+ character_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ other
+ %ld characters
+
+
plural.count.followed_by_and_mutual
NSStringLocalizedFormatKey
diff --git a/Localization/StringsConvertor/input/ja.lproj/app.json b/Localization/StringsConvertor/input/ja.lproj/app.json
index 9ff2a60a6..c16f12dcf 100644
--- a/Localization/StringsConvertor/input/ja.lproj/app.json
+++ b/Localization/StringsConvertor/input/ja.lproj/app.json
@@ -51,6 +51,11 @@
"clean_cache": {
"title": "キャッシュを消去",
"message": "%sのキャッシュを消去しました。"
+ },
+ "translation_failed": {
+ "title": "Note",
+ "message": "Translation failed. Maybe the administrator has not enabled translations on this server or this server is running an older version of Mastodon where translations are not yet supported.",
+ "button": "OK"
}
},
"controls": {
@@ -72,12 +77,13 @@
"discard": "破棄",
"try_again": "再実行",
"take_photo": "写真を撮る",
- "save_photo": "写真を撮る",
+ "save_photo": "写真を保存",
"copy_photo": "写真をコピー",
- "sign_in": "サインイン",
- "sign_up": "サインアップ",
+ "sign_in": "ログイン",
+ "sign_up": "アカウント作成",
"see_more": "もっと見る",
"preview": "プレビュー",
+ "copy": "Copy",
"share": "共有",
"share_user": "%sを共有",
"share_post": "投稿を共有",
@@ -91,12 +97,16 @@
"block_domain": "%sをブロック",
"unblock_domain": "%sのブロックを解除",
"settings": "設定",
- "delete": "削除"
+ "delete": "削除",
+ "translate_post": {
+ "title": "Translate from %s",
+ "unknown_language": "Unknown"
+ }
},
"tabs": {
"home": "ホーム",
- "search": "検索",
- "notification": "通知",
+ "search_and_explore": "Search and Explore",
+ "notifications": "通知",
"profile": "プロフィール"
},
"keyboard": {
@@ -132,15 +142,17 @@
"sensitive_content": "閲覧注意",
"media_content_warning": "どこかをタップして表示",
"tap_to_reveal": "タップして表示",
+ "load_embed": "Load Embed",
+ "link_via_user": "%s via %s",
"poll": {
"vote": "投票",
"closed": "終了"
},
"meta_entity": {
- "url": "Link: %s",
- "hashtag": "Hashtag: %s",
- "mention": "Show Profile: %s",
- "email": "Email address: %s"
+ "url": "リンク: %s",
+ "hashtag": "ハッシュタグ: %s",
+ "mention": "プロフィールを表示: %s",
+ "email": "メールアドレス: %s"
},
"actions": {
"reply": "返信",
@@ -153,6 +165,7 @@
"show_image": "画像を表示",
"show_gif": "GIFを表示",
"show_video_player": "Show video player",
+ "share_link_in_post": "Share Link in Post",
"tap_then_hold_to_show_menu": "Tap then hold to show menu"
},
"tag": {
@@ -168,6 +181,12 @@
"private": "この投稿はフォロワーに限り見ることができます。",
"private_from_me": "この投稿はフォロワーに限り見ることができます。",
"direct": "この投稿はメンションされたユーザーに限り見ることができます。"
+ },
+ "translation": {
+ "translated_from": "Translated from %s using %s",
+ "unknown_language": "Unknown",
+ "unknown_provider": "Unknown",
+ "show_original": "Shown Original"
}
},
"friendship": {
@@ -187,8 +206,8 @@
"unmute_user": "%sのミュートを解除",
"muted": "ミュート済み",
"edit_info": "編集",
- "show_reblogs": "Show Reblogs",
- "hide_reblogs": "Hide Reblogs"
+ "show_reblogs": "ブーストを表示",
+ "hide_reblogs": "ブーストを非表示"
},
"timeline": {
"filtered": "フィルター済み",
@@ -218,10 +237,16 @@
"get_started": "はじめる",
"log_in": "ログイン"
},
+ "login": {
+ "title": "Welcome back",
+ "subtitle": "アカウントを作成したサーバーにログインします。",
+ "server_search_field": {
+ "placeholder": "URLを入力またはサーバーを検索"
+ }
+ },
"server_picker": {
"title": "サーバーを選択",
- "subtitle": "あなたの興味分野・地域に合ったコミュニティや、汎用のものを選択してください。",
- "subtitle_extend": "あなたの興味分野・地域に合ったコミュニティや、汎用のものを選択してください。各コミュニティはそれぞれ完全に独立した組織や個人によって運営されています。",
+ "subtitle": "お住まいの地域、興味、目的に基づいてサーバーを選択してください。 サーバーに関係なく、Mastodonの誰とでも話せます。",
"button": {
"category": {
"all": "すべて",
@@ -248,8 +273,7 @@
"category": "カテゴリー"
},
"input": {
- "placeholder": "サーバーを探す",
- "search_servers_or_enter_url": "サーバーを検索またはURLを入力"
+ "search_servers_or_enter_url": "コミュニティを検索またはURLを入力"
},
"empty_state": {
"finding_servers": "利用可能なサーバーの検索...",
@@ -330,7 +354,7 @@
"confirm_email": {
"title": "さいごにもうひとつ。",
"subtitle": "先程 %s にメールを送信しました。リンクをタップしてアカウントを確認してください。",
- "tap_the_link_we_emailed_to_you_to_verify_your_account": "Tap the link we emailed to you to verify your account",
+ "tap_the_link_we_emailed_to_you_to_verify_your_account": "メールで送られたリンクへアクセスし、アカウントを認証してください",
"button": {
"open_email_app": "メールアプリを開く",
"resend": "再送信"
@@ -383,10 +407,12 @@
"attachment_broken": "%sは壊れていてMastodonにアップロードできません。",
"description_photo": "閲覧が難しいユーザーへの画像説明",
"description_video": "閲覧が難しいユーザーへの映像説明",
- "load_failed": "Load Failed",
- "upload_failed": "Upload Failed",
- "can_not_recognize_this_media_attachment": "Can not regonize this media attachment",
- "attachment_too_large": "Attachment too large"
+ "load_failed": "読み込みに失敗しました",
+ "upload_failed": "アップロードに失敗しました",
+ "can_not_recognize_this_media_attachment": "Can not recognize this media attachment",
+ "attachment_too_large": "添付ファイルが大きすぎます",
+ "compressing_state": "Compressing...",
+ "server_processing_state": "Server Processing..."
},
"poll": {
"duration_time": "期間: %s",
@@ -396,7 +422,9 @@
"one_day": "1日",
"three_days": "3日",
"seven_days": "7日",
- "option_number": "オプション %ld"
+ "option_number": "オプション %ld",
+ "the_poll_is_invalid": "The poll is invalid",
+ "the_poll_has_empty_option": "The poll has empty option"
},
"content_warning": {
"placeholder": "ここに警告を書いてください..."
@@ -411,20 +439,22 @@
"space_to_add": "スペースを追加"
},
"accessibility": {
- "append_attachment": "アタッチメントの追加",
+ "append_attachment": "添付ファイルを追加",
"append_poll": "投票を追加",
"remove_poll": "投票を消去",
"custom_emoji_picker": "カスタム絵文字ピッカー",
"enable_content_warning": "閲覧注意を有効にする",
"disable_content_warning": "閲覧注意を無効にする",
- "post_visibility_menu": "投稿の表示メニュー"
+ "post_visibility_menu": "投稿の表示メニュー",
+ "post_options": "投稿オプション",
+ "posting_as": "Posting as %s"
},
"keyboard": {
"discard_post": "投稿を破棄",
"publish_post": "投稿する",
"toggle_poll": "投票を切り替える",
"toggle_content_warning": "閲覧注意を切り替える",
- "append_attachment_entry": "アタッチメントを追加 - %s",
+ "append_attachment_entry": "添付ファイルを追加 - %s",
"select_visibility_entry": "公開設定を選択 - %s"
}
},
@@ -433,15 +463,23 @@
"follows_you": "フォローされています"
},
"dashboard": {
- "posts": "投稿",
- "following": "フォロー",
- "followers": "フォロワー"
+ "my_posts": "posts",
+ "my_following": "following",
+ "my_followers": "followers",
+ "other_posts": "posts",
+ "other_following": "following",
+ "other_followers": "followers"
},
"fields": {
+ "joined": "Joined",
"add_row": "行追加",
"placeholder": {
"label": "ラベル",
"content": "コンテンツ"
+ },
+ "verified": {
+ "short": "Verified on %s",
+ "long": "Ownership of this link was checked on %s"
}
},
"segmented_control": {
@@ -469,12 +507,12 @@
"message": "%sのブロックを解除しますか?"
},
"confirm_show_reblogs": {
- "title": "Show Reblogs",
- "message": "Confirm to show reblogs"
+ "title": "ブーストを表示",
+ "message": "ブーストを表示しますか?"
},
"confirm_hide_reblogs": {
- "title": "Hide Reblogs",
- "message": "Confirm to hide reblogs"
+ "title": "ブーストを非表示",
+ "message": "ブーストを非表示にしますか?"
}
},
"accessibility": {
@@ -497,10 +535,10 @@
"followed_by_names": "Followed by %s"
},
"favorited_by": {
- "title": "Favorited By"
+ "title": "お気に入り"
},
"reblogged_by": {
- "title": "Reblogged By"
+ "title": "ブースト"
},
"search": {
"title": "検索",
@@ -663,28 +701,28 @@
"step_two": {
"step_2_of_4": "ステップ 2/4",
"which_rules_are_being_violated": "どのルールに違反していますか?",
- "select_all_that_apply": "Select all that apply",
- "i_just_don’t_like_it": "I just don’t like it"
+ "select_all_that_apply": "当てはまるものをすべて選んでください",
+ "i_just_don’t_like_it": "興味がありません"
},
"step_three": {
"step_3_of_4": "ステップ 3/4",
- "are_there_any_posts_that_back_up_this_report": "Are there any posts that back up this report?",
- "select_all_that_apply": "Select all that apply"
+ "are_there_any_posts_that_back_up_this_report": "この通報を裏付けるような投稿はありますか?",
+ "select_all_that_apply": "当てはまるものをすべて選んでください"
},
"step_four": {
"step_4_of_4": "ステップ 4/4",
"is_there_anything_else_we_should_know": "その他に私たちに伝えておくべき事はありますか?"
},
"step_final": {
- "dont_want_to_see_this": "Don’t want to see this?",
- "when_you_see_something_you_dont_like_on_mastodon_you_can_remove_the_person_from_your_experience.": "When you see something you don’t like on Mastodon, you can remove the person from your experience.",
+ "dont_want_to_see_this": "見えないようにしたいですか?",
+ "when_you_see_something_you_dont_like_on_mastodon_you_can_remove_the_person_from_your_experience.": "Mastodonで気に入らないものを見た場合、その人をあなたの体験から取り除くことができます。",
"unfollow": "フォロー解除",
"unfollowed": "フォロー解除しました",
"unfollow_user": "%sをフォロー解除",
"mute_user": "%sをミュート",
- "you_wont_see_their_posts_or_reblogs_in_your_home_feed_they_wont_know_they_ve_been_muted": "You won’t see their posts or reblogs in your home feed. They won’t know they’ve been muted.",
+ "you_wont_see_their_posts_or_reblogs_in_your_home_feed_they_wont_know_they_ve_been_muted": "ホームに投稿やブーストは表示されなくなります。相手にミュートしたことは伝わりません。",
"block_user": "%sをブロック",
- "they_will_no_longer_be_able_to_follow_or_see_your_posts_but_they_can_see_if_theyve_been_blocked": "They will no longer be able to follow or see your posts, but they can see if they’ve been blocked.",
+ "they_will_no_longer_be_able_to_follow_or_see_your_posts_but_they_can_see_if_theyve_been_blocked": "相手はあなたの投稿を見たり、フォローしたりできなくなります。あなたにブロックされていることはわかります。",
"while_we_review_this_you_can_take_action_against_user": "私たちが確認している間でも、あなたは%sさんに対して対応することができます。"
}
},
@@ -706,7 +744,19 @@
"accessibility_hint": "チュートリアルを閉じるには、ダブルタップしてください"
},
"bookmark": {
- "title": "Bookmarks"
+ "title": "ブックマーク"
+ },
+ "followed_tags": {
+ "title": "Followed Tags",
+ "header": {
+ "posts": "posts",
+ "participants": "participants",
+ "posts_today": "posts today"
+ },
+ "actions": {
+ "follow": "Follow",
+ "unfollow": "Unfollow"
+ }
}
}
}
diff --git a/Localization/StringsConvertor/input/kab.lproj/Localizable.stringsdict b/Localization/StringsConvertor/input/kab.lproj/Localizable.stringsdict
index 7fc6a50bb..f18a906c0 100644
--- a/Localization/StringsConvertor/input/kab.lproj/Localizable.stringsdict
+++ b/Localization/StringsConvertor/input/kab.lproj/Localizable.stringsdict
@@ -50,6 +50,22 @@
%ld yisekkilen
+ a11y.plural.count.characters_left
+
+ NSStringLocalizedFormatKey
+ %#@character_count@ left
+ character_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 n usekkil
+ other
+ %ld n isekkilen
+
+
plural.count.followed_by_and_mutual
NSStringLocalizedFormatKey
@@ -120,7 +136,7 @@
NSStringFormatValueTypeKey
ld
one
- 1 tsuffeɣt
+ 1 n tsuffeɣt
other
%ld n tsuffaɣ
@@ -296,9 +312,9 @@
NSStringFormatValueTypeKey
ld
one
- Yeqqim-d 1 wass
+ Yeqqim-d 1 n wass
other
- Qqimen-d %ld wussan
+ Qqimen-d %ld n wussan
date.hour.left
@@ -312,9 +328,9 @@
NSStringFormatValueTypeKey
ld
one
- Yeqqim-d 1 usrag
+ Yeqqim-d 1 n wesrag
other
- Qqimen-d %ld yisragen
+ Qqimen-d %ld n yisragen
date.minute.left
@@ -328,9 +344,9 @@
NSStringFormatValueTypeKey
ld
one
- 1 tesdat i d-yeqqimen
+ 1 n tesdat i d-yeqqimen
other
- %ld tesdatin i d-yeqqimen
+ %ld n tesdatin i d-yeqqimen
date.second.left
@@ -344,9 +360,9 @@
NSStringFormatValueTypeKey
ld
one
- 1 tasint i d-yeqqimen
+ 1 n tasint i d-yeqqimen
other
- %ld tsinin i d-yeqqimen
+ %ld n tasinin i d-yeqqimen
date.year.ago.abbr
@@ -360,9 +376,9 @@
NSStringFormatValueTypeKey
ld
one
- 1 useggas aya
+ %ld n useggas aya
other
- %ld yiseggasen aya
+ %ld n yiseggasen aya
date.month.ago.abbr
@@ -376,9 +392,9 @@
NSStringFormatValueTypeKey
ld
one
- 1 wayyur aya
+ %ld n wayyur aya
other
- %ld wayyuren aya
+ %ld n wayyuren aya
date.day.ago.abbr
@@ -392,9 +408,9 @@
NSStringFormatValueTypeKey
ld
one
- 1 wass aya
+ %ld n wass aya
other
- %ld wussan aya
+ %ld n wussan aya
date.hour.ago.abbr
@@ -408,9 +424,9 @@
NSStringFormatValueTypeKey
ld
one
- 1 usrag aya
+ %ld n wesrag aya
other
- %ld yisragen aya
+ %ld n yisragen aya
date.minute.ago.abbr
@@ -424,9 +440,9 @@
NSStringFormatValueTypeKey
ld
one
- 1 tesdat aya
+ %ld n tesdat aya
other
- %ld tesdatin aya
+ %ld n tesdatin aya
date.second.ago.abbr
@@ -440,9 +456,9 @@
NSStringFormatValueTypeKey
ld
one
- 1 tasint aya
+ %ld n tasint aya
other
- %ld tsinin aya
+ %ld n tasinin aya
diff --git a/Localization/StringsConvertor/input/kab.lproj/app.json b/Localization/StringsConvertor/input/kab.lproj/app.json
index 9c5d7659a..720f979cb 100644
--- a/Localization/StringsConvertor/input/kab.lproj/app.json
+++ b/Localization/StringsConvertor/input/kab.lproj/app.json
@@ -51,6 +51,11 @@
"clean_cache": {
"title": "Sfeḍ tuffirt",
"message": "Yettwasfeḍ %s n tkatut tuffirt akken iwata."
+ },
+ "translation_failed": {
+ "title": "Note",
+ "message": "Translation failed. Maybe the administrator has not enabled translations on this server or this server is running an older version of Mastodon where translations are not yet supported.",
+ "button": "OK"
}
},
"controls": {
@@ -75,9 +80,10 @@
"save_photo": "Sekles tawlaft",
"copy_photo": "Nɣel tawlaft",
"sign_in": "Qqen",
- "sign_up": "Jerred amiḍan",
+ "sign_up": "Snulfu-d amiḍan",
"see_more": "Wali ugar",
"preview": "Taskant",
+ "copy": "Copy",
"share": "Bḍu",
"share_user": "Bḍu %s",
"share_post": "Bḍu tasuffeɣt",
@@ -91,12 +97,16 @@
"block_domain": "Sewḥel %s",
"unblock_domain": "Serreḥ i %s",
"settings": "Iɣewwaṛen",
- "delete": "Kkes"
+ "delete": "Kkes",
+ "translate_post": {
+ "title": "Translate from %s",
+ "unknown_language": "Unknown"
+ }
},
"tabs": {
"home": "Agejdan",
- "search": "Nadi",
- "notification": "Tilɣa",
+ "search_and_explore": "Search and Explore",
+ "notifications": "Notifications",
"profile": "Amaɣnu"
},
"keyboard": {
@@ -132,15 +142,17 @@
"sensitive_content": "Agbur amḥulfu",
"media_content_warning": "Sit anida tebɣiḍ i wakken ad twaliḍ",
"tap_to_reveal": "Sit i uskan",
+ "load_embed": "Load Embed",
+ "link_via_user": "%s via %s",
"poll": {
"vote": "Dɣeṛ",
"closed": "Ifukk"
},
"meta_entity": {
- "url": "Link: %s",
- "hashtag": "Hashtag: %s",
- "mention": "Show Profile: %s",
- "email": "Email address: %s"
+ "url": "Asaɣ : %s",
+ "hashtag": "Ahacṭag : %s",
+ "mention": "Sken-d amaɣnu : %s",
+ "email": "Tansa imayl : %s"
},
"actions": {
"reply": "Err",
@@ -153,6 +165,7 @@
"show_image": "Sken tugna",
"show_gif": "Sken GIF",
"show_video_player": "Sken ameɣri n tvidyut",
+ "share_link_in_post": "Share Link in Post",
"tap_then_hold_to_show_menu": "Sit teǧǧeḍ aḍad-ik•im i wakken ad d-iffeɣ wumuɣ"
},
"tag": {
@@ -168,6 +181,12 @@
"private": "D ineḍfaren-is kan i izemren ad walin tsuffeɣ-a.",
"private_from_me": "D ineḍfaren-is kan i izemren ad walin tsuffeɣ-a.",
"direct": "D ineḍfaren-is kan i izemren ad walin tsuffeɣ-a."
+ },
+ "translation": {
+ "translated_from": "Translated from %s using %s",
+ "unknown_language": "Unknown",
+ "unknown_provider": "Unknown",
+ "show_original": "Shown Original"
}
},
"friendship": {
@@ -218,10 +237,16 @@
"get_started": "Aha bdu tura",
"log_in": "Qqen"
},
+ "login": {
+ "title": "Ansuf yess·ek·em",
+ "subtitle": "Log you in on the server you created your account on.",
+ "server_search_field": {
+ "placeholder": "Sekcem URL neɣ nadi ɣef uqeddac-ik·im"
+ }
+ },
"server_picker": {
"title": "Mastodon yettwaxdem i yiseqdacen deg waṭas n temɣiwnin.",
- "subtitle": "Fren tamɣiwent almend n wayen tḥemmleḍ, n tmurt-ik neɣ n yiswi-inek amatu.",
- "subtitle_extend": "Fren tamɣiwent almend n wayen tḥemmleḍ, n tmurt-ik neɣ n yiswi-inek amatu. Yal tamɣiwent tsedday-itt tkebbanit neɣ amdan ilelliyen.",
+ "subtitle": "Pick a server based on your region, interests, or a general purpose one. You can still chat with anyone on Mastodon, regardless of your servers.",
"button": {
"category": {
"all": "Akk",
@@ -248,7 +273,6 @@
"category": "TAGGAYT"
},
"input": {
- "placeholder": "Nadi timɣiwnin",
"search_servers_or_enter_url": "Nadi timɣiwnin neɣ sekcem URL"
},
"empty_state": {
@@ -352,8 +376,8 @@
"navigation_bar_state": {
"offline": "Beṛṛa n tuqqna",
"new_posts": "Tissufaɣ timaynutin",
- "published": "Yettwasuffeɣ!",
- "Publishing": "Asuffeɣ tasuffeɣt...",
+ "published": "Tettwasuffeɣ!",
+ "Publishing": "Asuffeɣ n tasuffeɣt...",
"accessibility": {
"logo_label": "Taqeffalt n ulugu",
"logo_hint": "Sit i wakken ad tɛeddiḍ i usawen, sit tikkelt-nniḍen i wakken ad tɛeddiḍ ɣer wadig yezrin"
@@ -385,8 +409,10 @@
"description_video": "Glem-d tavidyut i wid yesɛan ugur deg yiẓri...",
"load_failed": "Load Failed",
"upload_failed": "Upload Failed",
- "can_not_recognize_this_media_attachment": "Can not regonize this media attachment",
- "attachment_too_large": "Attachment too large"
+ "can_not_recognize_this_media_attachment": "Can not recognize this media attachment",
+ "attachment_too_large": "Attachment too large",
+ "compressing_state": "Compressing...",
+ "server_processing_state": "Server Processing..."
},
"poll": {
"duration_time": "Tangazt: %s",
@@ -396,7 +422,9 @@
"one_day": "1 n wass",
"three_days": "3 n wussan",
"seven_days": "7 n wussan",
- "option_number": "Taxtiṛt %ld"
+ "option_number": "Taxtiṛt %ld",
+ "the_poll_is_invalid": "The poll is invalid",
+ "the_poll_has_empty_option": "The poll has empty option"
},
"content_warning": {
"placeholder": "Aru alɣu-inek s telqeyt da..."
@@ -417,7 +445,9 @@
"custom_emoji_picker": "Amefran n yimujiten udmawanen",
"enable_content_warning": "Rmed alɣu n ugbur",
"disable_content_warning": "Sens alɣu n ugbur",
- "post_visibility_menu": "Umuɣ n ubani n tsuffeɣt"
+ "post_visibility_menu": "Umuɣ n ubani n tsuffeɣt",
+ "post_options": "Post Options",
+ "posting_as": "Posting as %s"
},
"keyboard": {
"discard_post": "Sefsex tasuffeɣt",
@@ -433,15 +463,23 @@
"follows_you": "Yeṭṭafaṛ-ik•im"
},
"dashboard": {
- "posts": "tisuffaɣ",
- "following": "iṭafaṛ",
- "followers": "imeḍfaren"
+ "my_posts": "posts",
+ "my_following": "following",
+ "my_followers": "followers",
+ "other_posts": "posts",
+ "other_following": "following",
+ "other_followers": "followers"
},
"fields": {
+ "joined": "Joined",
"add_row": "Rnu izirig",
"placeholder": {
"label": "Tabzimt",
"content": "Agbur"
+ },
+ "verified": {
+ "short": "Verified on %s",
+ "long": "Ownership of this link was checked on %s"
}
},
"segmented_control": {
@@ -656,7 +694,7 @@
"its_spam": "D aspam",
"malicious_links_fake_engagement_or_repetetive_replies": "Yir iseɣwan, yir agman d tririyin i d-yettuɣalen",
"it_violates_server_rules": "Truẓi n yilugan n uqeddac",
- "you_are_aware_that_it_breaks_specific_rules": "Teẓriḍ y•tettruẓu kra n yilugan",
+ "you_are_aware_that_it_breaks_specific_rules": "Teẓriḍ y·tettruẓu kra n yilugan",
"its_something_else": "Ɣef ssebba-nniḍen",
"the_issue_does_not_fit_into_other_categories": "Ugur ur yemṣada ara akk d taggayin-nniḍen"
},
@@ -707,6 +745,18 @@
},
"bookmark": {
"title": "Bookmarks"
+ },
+ "followed_tags": {
+ "title": "Followed Tags",
+ "header": {
+ "posts": "posts",
+ "participants": "participants",
+ "posts_today": "posts today"
+ },
+ "actions": {
+ "follow": "Follow",
+ "unfollow": "Unfollow"
+ }
}
}
}
diff --git a/Localization/StringsConvertor/input/kmr.lproj/Localizable.stringsdict b/Localization/StringsConvertor/input/kmr.lproj/Localizable.stringsdict
index 77571439f..c904186d8 100644
--- a/Localization/StringsConvertor/input/kmr.lproj/Localizable.stringsdict
+++ b/Localization/StringsConvertor/input/kmr.lproj/Localizable.stringsdict
@@ -50,6 +50,22 @@
%ld tîp
+ a11y.plural.count.characters_left
+
+ NSStringLocalizedFormatKey
+ %#@character_count@ maye
+ character_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 peyv
+ other
+ %ld peyv
+
+
plural.count.followed_by_and_mutual
NSStringLocalizedFormatKey
diff --git a/Localization/StringsConvertor/input/kmr.lproj/app.json b/Localization/StringsConvertor/input/kmr.lproj/app.json
index 098f514ee..5e23f74b9 100644
--- a/Localization/StringsConvertor/input/kmr.lproj/app.json
+++ b/Localization/StringsConvertor/input/kmr.lproj/app.json
@@ -51,6 +51,11 @@
"clean_cache": {
"title": "Pêşbîrê pak bike",
"message": "Pêşbîra %s biserketî hate pakkirin."
+ },
+ "translation_failed": {
+ "title": "Nîşe",
+ "message": "Werger têk çû. Dibe ku rêvebir werger li ser vê rajakarê çalak nekiribe an jî ev rajakar guhertoyek kevntir a Mastodon e ku werger hîn nehatiye piştgirîkirin.",
+ "button": "BAŞ E"
}
},
"controls": {
@@ -75,9 +80,10 @@
"save_photo": "Wêneyê tomar bike",
"copy_photo": "Wêneyê jê bigire",
"sign_in": "Têkeve",
- "sign_up": "Tomar bibe",
+ "sign_up": "Ajimêr biafirîne",
"see_more": "Bêtir bibîne",
"preview": "Pêşdîtin",
+ "copy": "Jê bigire",
"share": "Parve bike",
"share_user": "%s parve bike",
"share_post": "Şandiyê parve bike",
@@ -91,12 +97,16 @@
"block_domain": "%s asteng bike",
"unblock_domain": "%s asteng neke",
"settings": "Sazkarî",
- "delete": "Jê bibe"
+ "delete": "Jê bibe",
+ "translate_post": {
+ "title": "Ji %s wergerîne",
+ "unknown_language": "Nenas"
+ }
},
"tabs": {
"home": "Serrûpel",
- "search": "Bigere",
- "notification": "Agahdarî",
+ "search_and_explore": "Bigere û vekole",
+ "notifications": "Agahdarî",
"profile": "Profîl"
},
"keyboard": {
@@ -132,6 +142,8 @@
"sensitive_content": "Naveroka hestiyarî",
"media_content_warning": "Ji bo eşkerekirinê li derekî bitikîne",
"tap_to_reveal": "Ji bo dîtinê bitikîne",
+ "load_embed": "Load Embed",
+ "link_via_user": "%s bi riya %s",
"poll": {
"vote": "Deng bide",
"closed": "Girtî"
@@ -153,6 +165,7 @@
"show_image": "Wêneyê nîşan bide",
"show_gif": "GIF nîşan bide",
"show_video_player": "Lêdera vîdyoyê nîşan bide",
+ "share_link_in_post": "Girêdanê di şandiyê de parve bike",
"tap_then_hold_to_show_menu": "Ji bo nîşandana menuyê dirêj bitikîne"
},
"tag": {
@@ -168,6 +181,12 @@
"private": "Tenê şopînerên wan dikarin vê şandiyê bibînin.",
"private_from_me": "Tenê şopînerên min dikarin vê şandiyê bibînin.",
"direct": "Tenê bikarhênerê qalkirî dikare vê şandiyê bibîne."
+ },
+ "translation": {
+ "translated_from": "Hate wergerandin ji %s bi riya %s",
+ "unknown_language": "Nenas",
+ "unknown_provider": "Nenas",
+ "show_original": "A resen nîşan bide"
}
},
"friendship": {
@@ -218,10 +237,16 @@
"get_started": "Dest pê bike",
"log_in": "Têkeve"
},
+ "login": {
+ "title": "Dîsa bi xêr hatî",
+ "subtitle": "Têketinê bike ser rajekarê ku te ajimêrê xwe tê de çê kiriye.",
+ "server_search_field": {
+ "placeholder": "Girêdanê têxe an jî li rajekarê xwe bigere"
+ }
+ },
"server_picker": {
"title": "Mastodon ji bikarhênerên di civakên cuda de pêk tê.",
- "subtitle": "Li gorî berjewendî, herêm, an jî armancek gelemperî civakekê hilbijêre.",
- "subtitle_extend": "Li gorî berjewendî, herêm, an jî armancek gelemperî civakekê hilbijêre. Her civakek ji hêla rêxistinek an kesek bi tevahî serbixwe ve tê xebitandin.",
+ "subtitle": "Li gorî herêm, berjewendî, an jî armanceke giştî rajekarekê hilbijêre. Tu hîn jî dikarî li ser Mastodon bi her kesî re biaxivî, her rajekarê te çi be.",
"button": {
"category": {
"all": "Hemû",
@@ -248,8 +273,7 @@
"category": "BEŞ"
},
"input": {
- "placeholder": "Li rajekaran bigere",
- "search_servers_or_enter_url": "Li rajekaran bigere an jî girêdanê têxe"
+ "search_servers_or_enter_url": "Li civakan bigere an jî girêdanê têxe"
},
"empty_state": {
"finding_servers": "Peydakirina rajekarên berdest...",
@@ -386,7 +410,9 @@
"load_failed": "Barkirin têk çû",
"upload_failed": "Barkirin têk çû",
"can_not_recognize_this_media_attachment": "Nikare ev pêveka medyayê nas bike",
- "attachment_too_large": "Pêvek pir mezin e"
+ "attachment_too_large": "Pêvek pir mezin e",
+ "compressing_state": "Tê guvaştin...",
+ "server_processing_state": "Pêvajoya rajekar pêş de diçe..."
},
"poll": {
"duration_time": "Dirêjî: %s",
@@ -396,7 +422,9 @@
"one_day": "1 Roj",
"three_days": "3 Roj",
"seven_days": "7 Roj",
- "option_number": "Vebijêrk %ld"
+ "option_number": "Vebijêrk %ld",
+ "the_poll_is_invalid": "Ev dengdayîn ne derbasdar e",
+ "the_poll_has_empty_option": "Vebijêrkên vê dengdayînê vala ne"
},
"content_warning": {
"placeholder": "Li vir hişyariyek hûrgilî binivîsine..."
@@ -417,7 +445,9 @@
"custom_emoji_picker": "Hilbijêrê emojî yên kesanekirî",
"enable_content_warning": "Hişyariya naverokê çalak bike",
"disable_content_warning": "Hişyariya naverokê neçalak bike",
- "post_visibility_menu": "Kulîna xuyabûna şandiyê"
+ "post_visibility_menu": "Kulîna xuyabûna şandiyê",
+ "post_options": "Vebijêrkên şandiyê",
+ "posting_as": "Biweşîne wekî %s"
},
"keyboard": {
"discard_post": "Şandî paşguh bike",
@@ -433,15 +463,23 @@
"follows_you": "Te dişopîne"
},
"dashboard": {
- "posts": "şandî",
- "following": "dişopîne",
- "followers": "şopîner"
+ "my_posts": "şandî",
+ "my_following": "dişopîne",
+ "my_followers": "şopîner",
+ "other_posts": "şandî",
+ "other_following": "dişopîne",
+ "other_followers": "şopîner"
},
"fields": {
+ "joined": "Dîroka tevlîbûnê",
"add_row": "Rêzê tevlî bike",
"placeholder": {
"label": "Nîşan",
"content": "Naverok"
+ },
+ "verified": {
+ "short": "Hate piştrastkirin li ser %s",
+ "long": "Xwedaniya li vê girêdanê di %s de hatiye kontrolkirin"
}
},
"segmented_control": {
@@ -707,6 +745,18 @@
},
"bookmark": {
"title": "Şûnpel"
+ },
+ "followed_tags": {
+ "title": "Hashtagên şopandî",
+ "header": {
+ "posts": "şandî",
+ "participants": "beşdar",
+ "posts_today": "şandiyên îro"
+ },
+ "actions": {
+ "follow": "Bişopîne",
+ "unfollow": "Neşopîne"
+ }
}
}
}
diff --git a/Localization/StringsConvertor/input/ko.lproj/Localizable.stringsdict b/Localization/StringsConvertor/input/ko.lproj/Localizable.stringsdict
index 9628be614..77aac5569 100644
--- a/Localization/StringsConvertor/input/ko.lproj/Localizable.stringsdict
+++ b/Localization/StringsConvertor/input/ko.lproj/Localizable.stringsdict
@@ -44,6 +44,20 @@
%ld 글자
+ a11y.plural.count.characters_left
+
+ NSStringLocalizedFormatKey
+ %#@character_count@ 글자 남음
+ character_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ other
+ %ld 글자
+
+
plural.count.followed_by_and_mutual
NSStringLocalizedFormatKey
diff --git a/Localization/StringsConvertor/input/ko.lproj/app.json b/Localization/StringsConvertor/input/ko.lproj/app.json
index 826ac389c..7d4e48e09 100644
--- a/Localization/StringsConvertor/input/ko.lproj/app.json
+++ b/Localization/StringsConvertor/input/ko.lproj/app.json
@@ -51,6 +51,11 @@
"clean_cache": {
"title": "캐시 삭제",
"message": "Successfully cleaned %s cache."
+ },
+ "translation_failed": {
+ "title": "Note",
+ "message": "Translation failed. Maybe the administrator has not enabled translations on this server or this server is running an older version of Mastodon where translations are not yet supported.",
+ "button": "확인"
}
},
"controls": {
@@ -75,9 +80,10 @@
"save_photo": "사진 저장",
"copy_photo": "사진 복사",
"sign_in": "로그인",
- "sign_up": "회원가입",
+ "sign_up": "계정 생성",
"see_more": "더 보기",
"preview": "미리보기",
+ "copy": "Copy",
"share": "공유",
"share_user": "%s를 공유",
"share_post": "게시물 공유",
@@ -91,12 +97,16 @@
"block_domain": "%s 차단하기",
"unblock_domain": "%s 차단 해제",
"settings": "설정",
- "delete": "삭제"
+ "delete": "삭제",
+ "translate_post": {
+ "title": "Translate from %s",
+ "unknown_language": "Unknown"
+ }
},
"tabs": {
"home": "홈",
- "search": "검색",
- "notification": "알림",
+ "search_and_explore": "Search and Explore",
+ "notifications": "Notifications",
"profile": "프로필"
},
"keyboard": {
@@ -132,6 +142,8 @@
"sensitive_content": "민감한 콘텐츠",
"media_content_warning": "아무 곳이나 눌러서 보기",
"tap_to_reveal": "눌러서 확인",
+ "load_embed": "Load Embed",
+ "link_via_user": "%s via %s",
"poll": {
"vote": "투표",
"closed": "마감"
@@ -153,6 +165,7 @@
"show_image": "이미지 표시",
"show_gif": "GIF 보기",
"show_video_player": "비디오 플레이어 보기",
+ "share_link_in_post": "Share Link in Post",
"tap_then_hold_to_show_menu": "Tap then hold to show menu"
},
"tag": {
@@ -168,6 +181,12 @@
"private": "Only their followers can see this post.",
"private_from_me": "Only my followers can see this post.",
"direct": "Only mentioned user can see this post."
+ },
+ "translation": {
+ "translated_from": "%s에서 %s를 사용해 번역됨",
+ "unknown_language": "알 수 없음",
+ "unknown_provider": "알 수 없음",
+ "show_original": "원본 보기"
}
},
"friendship": {
@@ -218,10 +237,16 @@
"get_started": "시작하기",
"log_in": "로그인"
},
+ "login": {
+ "title": "돌아오신 것을 환영합니다",
+ "subtitle": "계정을 만든 서버에 로그인.",
+ "server_search_field": {
+ "placeholder": "URL을 입력하거나 서버를 검색"
+ }
+ },
"server_picker": {
"title": "서버를 고르세요,\n아무 서버나 좋습니다.",
- "subtitle": "Pick a server based on your interests, region, or a general purpose one.",
- "subtitle_extend": "Pick a server based on your interests, region, or a general purpose one. Each server is operated by an entirely independent organization or individual.",
+ "subtitle": "당신의 지역이나, 관심사에 따라, 혹은 그냥 일반적인 목적의 서버를 고르세요. 어떤 서버를 고르더라도 마스토돈의 다른 모두와 소통할 수 있습니다.",
"button": {
"category": {
"all": "모두",
@@ -248,8 +273,7 @@
"category": "분류"
},
"input": {
- "placeholder": "서버 검색",
- "search_servers_or_enter_url": "서버를 검색하거나 URL을 입력하세요"
+ "search_servers_or_enter_url": "커뮤니티를 검색하거나 URL을 입력"
},
"empty_state": {
"finding_servers": "사용 가능한 서버를 찾는 중입니다...",
@@ -385,8 +409,10 @@
"description_video": "시각장애인을 위한 영상 설명…",
"load_failed": "불러오기 실패",
"upload_failed": "업로드 실패",
- "can_not_recognize_this_media_attachment": "이 미디어 첨부파일을 인식할 수 없습니다",
- "attachment_too_large": "첨부파일이 너무 큽니다"
+ "can_not_recognize_this_media_attachment": "Can not recognize this media attachment",
+ "attachment_too_large": "첨부파일이 너무 큽니다",
+ "compressing_state": "Compressing...",
+ "server_processing_state": "Server Processing..."
},
"poll": {
"duration_time": "기간: %s",
@@ -396,7 +422,9 @@
"one_day": "1일",
"three_days": "3일",
"seven_days": "7일",
- "option_number": "옵션 %ld"
+ "option_number": "옵션 %ld",
+ "the_poll_is_invalid": "The poll is invalid",
+ "the_poll_has_empty_option": "The poll has empty option"
},
"content_warning": {
"placeholder": "정확한 경고 문구를 여기에 작성하세요…"
@@ -417,7 +445,9 @@
"custom_emoji_picker": "커스텀 에모지 선택기",
"enable_content_warning": "열람 주의 설정",
"disable_content_warning": "열람 주의 해제",
- "post_visibility_menu": "게시물 공개범위 메뉴"
+ "post_visibility_menu": "게시물 공개범위 메뉴",
+ "post_options": "게시물 옵션",
+ "posting_as": "%s로 게시"
},
"keyboard": {
"discard_post": "글 버리기",
@@ -433,15 +463,23 @@
"follows_you": "Follows You"
},
"dashboard": {
- "posts": "게시물",
- "following": "팔로잉",
- "followers": "팔로워"
+ "my_posts": "posts",
+ "my_following": "following",
+ "my_followers": "followers",
+ "other_posts": "posts",
+ "other_following": "following",
+ "other_followers": "followers"
},
"fields": {
+ "joined": "가입일",
"add_row": "행 추가",
"placeholder": {
"label": "라벨",
"content": "내용"
+ },
+ "verified": {
+ "short": "Verified on %s",
+ "long": "Ownership of this link was checked on %s"
}
},
"segmented_control": {
@@ -707,6 +745,18 @@
},
"bookmark": {
"title": "Bookmarks"
+ },
+ "followed_tags": {
+ "title": "팔로우한 태그",
+ "header": {
+ "posts": "게시물",
+ "participants": "참가자",
+ "posts_today": "오늘"
+ },
+ "actions": {
+ "follow": "팔로우",
+ "unfollow": "팔로우 해제"
+ }
}
}
}
diff --git a/Localization/StringsConvertor/input/lv.lproj/Localizable.stringsdict b/Localization/StringsConvertor/input/lv.lproj/Localizable.stringsdict
index 25f32c98d..fd327d745 100644
--- a/Localization/StringsConvertor/input/lv.lproj/Localizable.stringsdict
+++ b/Localization/StringsConvertor/input/lv.lproj/Localizable.stringsdict
@@ -13,17 +13,17 @@
NSStringFormatValueTypeKey
ld
zero
- %ld unread notification
+ %ld nelasītu paziņojumu
one
- 1 unread notification
+ 1 nelasīts paziņojums
other
- %ld unread notification
+ %ld nelasīti paziņojumi
a11y.plural.count.input_limit_exceeds
NSStringLocalizedFormatKey
- Input limit exceeds %#@character_count@
+ Ievades ierobežojums pārsniedz %#@character_count@
character_count
NSStringFormatSpecTypeKey
@@ -31,17 +31,17 @@
NSStringFormatValueTypeKey
ld
zero
- %ld characters
+ %ld rakstzīmju
one
- 1 character
+ 1 rakstzīme
other
- %ld characters
+ %ld rakstzīmes
a11y.plural.count.input_limit_remains
NSStringLocalizedFormatKey
- Input limit remains %#@character_count@
+ Ievades ierobežojums paliek %#@character_count@
character_count
NSStringFormatSpecTypeKey
@@ -49,11 +49,29 @@
NSStringFormatValueTypeKey
ld
zero
- %ld characters
+ %ld rakstzīmju
one
- 1 character
+ 1 rakstzīme
other
- %ld characters
+ %ld rakstzīmes
+
+
+ a11y.plural.count.characters_left
+
+ NSStringLocalizedFormatKey
+ %#@character_count@ palikušas
+ character_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ zero
+ %ld rakstzīmju
+ one
+ 1 rakstzīme
+ other
+ %ld rakstzīmes
plural.count.followed_by_and_mutual
@@ -80,11 +98,11 @@
NSStringFormatValueTypeKey
ld
zero
- Followed by %1$@, and %ld mutuals
+ Seko %1$@ un %ld kopīgi
one
- Followed by %1$@, and another mutual
+ Seko %1$@ un vēl viens kopīgs
other
- Followed by %1$@, and %ld mutuals
+ Seko %1$@ un %ld kopīgi
plural.count.metric_formatted.post
@@ -98,11 +116,11 @@
NSStringFormatValueTypeKey
ld
zero
- posts
+ ziņu
one
- post
+ ziņa
other
- posts
+ ziņas
plural.count.media
@@ -116,11 +134,11 @@
NSStringFormatValueTypeKey
ld
zero
- %ld media
+ %ld multivide
one
- 1 media
+ 1 multivide
other
- %ld media
+ %ld multivide
plural.count.post
@@ -134,11 +152,11 @@
NSStringFormatValueTypeKey
ld
zero
- %ld posts
+ %ld ziņu
one
- 1 post
+ 1 ziņa
other
- %ld posts
+ %ld ziņas
plural.count.favorite
@@ -152,11 +170,11 @@
NSStringFormatValueTypeKey
ld
zero
- %ld favorites
+ %ld iecienītu
one
- 1 favorite
+ 1 iecienīts
other
- %ld favorites
+ %ld iecienīti
plural.count.reblog
@@ -170,11 +188,11 @@
NSStringFormatValueTypeKey
ld
zero
- %ld reblogs
+ %ld reblogu
one
- 1 reblog
+ 1 reblogs
other
- %ld reblogs
+ %ld reblogi
plural.count.reply
@@ -188,11 +206,11 @@
NSStringFormatValueTypeKey
ld
zero
- %ld replies
+ %ld atbilžu
one
- 1 reply
+ 1 atbilde
other
- %ld replies
+ %ld atbildes
plural.count.vote
@@ -206,11 +224,11 @@
NSStringFormatValueTypeKey
ld
zero
- %ld votes
+ %ld balsu
one
- 1 vote
+ 1 balss
other
- %ld votes
+ %ld balsis
plural.count.voter
@@ -224,11 +242,11 @@
NSStringFormatValueTypeKey
ld
zero
- %ld voters
+ %ld balsotāju
one
- 1 voter
+ 1 balsotājs
other
- %ld voters
+ %ld balsotāji
plural.people_talking
@@ -242,11 +260,11 @@
NSStringFormatValueTypeKey
ld
zero
- %ld people talking
+ %ld cilvēku apspriež
one
- 1 people talking
+ 1 cilvēks apspriež
other
- %ld people talking
+ %ld cilvēki apspriež
plural.count.following
@@ -260,11 +278,11 @@
NSStringFormatValueTypeKey
ld
zero
- %ld following
+ %ld sekotāju
one
- 1 following
+ 1 sekotājs
other
- %ld following
+ %ld sekotāji
plural.count.follower
@@ -278,11 +296,11 @@
NSStringFormatValueTypeKey
ld
zero
- %ld followers
+ %ld sekotāju
one
- 1 follower
+ 1 sekotājs
other
- %ld followers
+ %ld sekotāji
date.year.left
@@ -296,11 +314,11 @@
NSStringFormatValueTypeKey
ld
zero
- %ld years left
+ palicis %ld gadu
one
- 1 year left
+ palicis 1 gads
other
- %ld years left
+ palikuši %ld gadi
date.month.left
@@ -314,11 +332,11 @@
NSStringFormatValueTypeKey
ld
zero
- %ld months left
+ palicis %ld mēnešu
one
- 1 months left
+ palicis 1 mēnesis
other
- %ld months left
+ palikuši %ld mēneši
date.day.left
@@ -332,11 +350,11 @@
NSStringFormatValueTypeKey
ld
zero
- %ld days left
+ palikušas %ld dienas
one
- 1 day left
+ palikusi 1 diena
other
- %ld days left
+ palikušas %ld dienas
date.hour.left
@@ -350,11 +368,11 @@
NSStringFormatValueTypeKey
ld
zero
- %ld hours left
+ atlikušas %ld stundas
one
- 1 hour left
+ atlikusi 1 stunda
other
- %ld hours left
+ atlikušas %ld stundas
date.minute.left
@@ -368,11 +386,11 @@
NSStringFormatValueTypeKey
ld
zero
- %ld minutes left
+ atlikušas %ld minūtes
one
- 1 minute left
+ atlikusi 1 minūte
other
- %ld minutes left
+ atlikušas %ld minūtes
date.second.left
@@ -386,11 +404,11 @@
NSStringFormatValueTypeKey
ld
zero
- %ld seconds left
+ atlikušas %ld sekundes
one
- 1 second left
+ atlikusi 1 sekunde
other
- %ld seconds left
+ atlikušas %ld sekundes
date.year.ago.abbr
@@ -404,11 +422,11 @@
NSStringFormatValueTypeKey
ld
zero
- %ldy ago
+ pirms %ld g.
one
- 1y ago
+ pirms 1 g.
other
- %ldy ago
+ pirms %ld g.
date.month.ago.abbr
@@ -422,11 +440,11 @@
NSStringFormatValueTypeKey
ld
zero
- %ldM ago
+ pirms %ld M.
one
- 1M ago
+ pirms 1 M.
other
- %ldM ago
+ pirms %ld M.
date.day.ago.abbr
@@ -440,11 +458,11 @@
NSStringFormatValueTypeKey
ld
zero
- %ldd ago
+ pirms %ld d.
one
- 1d ago
+ pirms 1 d.
other
- %ldd ago
+ pirms %ld d.
date.hour.ago.abbr
@@ -458,11 +476,11 @@
NSStringFormatValueTypeKey
ld
zero
- %ldh ago
+ pirms %ld st.
one
- 1h ago
+ pirms 1 st.
other
- %ldh ago
+ pirms %ld st.
date.minute.ago.abbr
@@ -476,11 +494,11 @@
NSStringFormatValueTypeKey
ld
zero
- %ldm ago
+ pirms %ld m.
one
- 1m ago
+ pirms 1 m.
other
- %ldm ago
+ pirms %ld m.
date.second.ago.abbr
@@ -494,11 +512,11 @@
NSStringFormatValueTypeKey
ld
zero
- %lds ago
+ pirms %ld s.
one
- 1s ago
+ pirms 1 s.
other
- %lds ago
+ pirms %ld s.
diff --git a/Localization/StringsConvertor/input/lv.lproj/app.json b/Localization/StringsConvertor/input/lv.lproj/app.json
index 2835f0887..01a8d0515 100644
--- a/Localization/StringsConvertor/input/lv.lproj/app.json
+++ b/Localization/StringsConvertor/input/lv.lproj/app.json
@@ -6,30 +6,30 @@
"please_try_again_later": "Lūdzu, mēģiniet vēlreiz vēlāk."
},
"sign_up_failure": {
- "title": "Sign Up Failure"
+ "title": "Reģistrācijas Neveiksme"
},
"server_error": {
"title": "Servera kļūda"
},
"vote_failure": {
- "title": "Vote Failure",
+ "title": "Balsošanas Neveiksme",
"poll_ended": "Balsošana beidzās"
},
"discard_post_content": {
"title": "Atmest malnrakstu",
- "message": "Confirm to discard composed post content."
+ "message": "Apstiprini, lai atmestu izveidotās ziņas saturu."
},
"publish_post_failure": {
- "title": "Publish Failure",
- "message": "Failed to publish the post.\nPlease check your internet connection.",
+ "title": "Publicēšanas Neveiksme",
+ "message": "Neizdevās publicēt ziņu.\nLūdzu, pārbaudi savu interneta savienojumu.",
"attachments_message": {
- "video_attach_with_photo": "Cannot attach a video to a post that already contains images.",
- "more_than_one_video": "Cannot attach more than one video."
+ "video_attach_with_photo": "Nevar pievienot videoklipu ziņai, kurā jau ir attēli.",
+ "more_than_one_video": "Nevar pievienot vairāk kā vienu video."
}
},
"edit_profile_failure": {
- "title": "Edit Profile Error",
- "message": "Cannot edit profile. Please try again."
+ "title": "Profila Rediģēšanas Kļūda",
+ "message": "Nevar rediģēt profilu. Lūdzu mēģini vēlreiz."
},
"sign_out": {
"title": "Iziet",
@@ -37,20 +37,25 @@
"confirm": "Iziet"
},
"block_domain": {
- "title": "Are you really, really sure you want to block the entire %s? In most cases a few targeted blocks or mutes are sufficient and preferable. You will not see content from that domain and any of your followers from that domain will be removed.",
- "block_entire_domain": "Block Domain"
+ "title": "Vai tiešām tiešām vēlies bloķēt visu %s? Vairumā gadījumu pietiek ar dažiem mērķtiecīgiem blokiem vai klusinātājiem, un tie ir vēlami. Tu neredzēsi saturu no šī domēna, un visi tavi sekotāji no šī domēna tiks noņemti.",
+ "block_entire_domain": "Bloķēt Domēnu"
},
"save_photo_failure": {
- "title": "Save Photo Failure",
- "message": "Please enable the photo library access permission to save the photo."
+ "title": "Attēla Saglabāšanas Kļūda",
+ "message": "Lai saglabātu fotoattēlu, lūdzu, iespējo fotoattēlu bibliotēkas piekļuves atļauju."
},
"delete_post": {
"title": "Dzēst ierakstu",
"message": "Vai tiešām vēlies dzēst ierakstu?"
},
"clean_cache": {
- "title": "Clean Cache",
- "message": "Successfully cleaned %s cache."
+ "title": "Iztīrīt Kešatmiņu",
+ "message": "%s kešatmiņa ir veiksmīgi iztīrīta."
+ },
+ "translation_failed": {
+ "title": "Piezīme",
+ "message": "Tulkošana neizdevās. Varbūt administrators nav iespējojis tulkojumus šajā serverī vai arī šajā serverī darbojas vecāka Mastodon versija, kurā tulkojumi vēl netiek atbalstīti.",
+ "button": "Labi"
}
},
"controls": {
@@ -75,47 +80,52 @@
"save_photo": "Saglabāt bildi",
"copy_photo": "Kopēt bildi",
"sign_in": "Pieteikties",
- "sign_up": "Reģistrēties",
+ "sign_up": "Izveidot kontu",
"see_more": "Skatīt vairāk",
"preview": "Priekšskatījums",
+ "copy": "Kopēt",
"share": "Dalīties",
- "share_user": "Share %s",
- "share_post": "Share Post",
+ "share_user": "Kopīgot %s",
+ "share_post": "Kopīgot Ziņu",
"open_in_safari": "Atvērt Safari",
"open_in_browser": "Atvērt pārlūkprogrammā",
"find_people": "Atrodi cilvēkus kam sekot",
- "manually_search": "Manually search instead",
+ "manually_search": "Tā vietā meklēt manuāli",
"skip": "Izlaist",
"reply": "Atbildēt",
"report_user": "Ziņot par lietotāju @%s",
"block_domain": "Bloķēt %s",
"unblock_domain": "Atbloķēt %s",
"settings": "Iestatījumi",
- "delete": "Dzēst"
+ "delete": "Dzēst",
+ "translate_post": {
+ "title": "Tulkot no %s",
+ "unknown_language": "Nezināms"
+ }
},
"tabs": {
"home": "Sākums",
- "search": "Meklēšana",
- "notification": "Paziņojums",
+ "search_and_explore": "Meklēt un Pārlūkot",
+ "notifications": "Paziņojumi",
"profile": "Profils"
},
"keyboard": {
"common": {
"switch_to_tab": "Pārslēgties uz: %s",
"compose_new_post": "Veidot jaunu ziņu",
- "show_favorites": "Show Favorites",
+ "show_favorites": "Parādīt Izlasi",
"open_settings": "Atvērt iestatījumus"
},
"timeline": {
- "previous_status": "Previous Post",
- "next_status": "Next Post",
- "open_status": "Open Post",
- "open_author_profile": "Open Author's Profile",
- "open_reblogger_profile": "Open Reblogger's Profile",
- "reply_status": "Reply to Post",
- "toggle_reblog": "Toggle Reblog on Post",
- "toggle_favorite": "Toggle Favorite on Post",
- "toggle_content_warning": "Toggle Content Warning",
+ "previous_status": "Iepriekšējā Ziņa",
+ "next_status": "Nākamā Ziņa",
+ "open_status": "Atvērt Ziņu",
+ "open_author_profile": "Atvērt Autora Profilu",
+ "open_reblogger_profile": "Atvērt Reblogotāja Profilu",
+ "reply_status": "Atbildēt uz Ziņu",
+ "toggle_reblog": "Pārslēgt Reblogs uz Ziņu",
+ "toggle_favorite": "Pārslēgt Izlasi uz Ziņas",
+ "toggle_content_warning": "Pārslēgt Satura Brīdinājumu",
"preview_image": "Priekšskata attēls"
},
"segmented_control": {
@@ -124,50 +134,59 @@
}
},
"status": {
- "user_reblogged": "%s reblogged",
- "user_replied_to": "Replied to %s",
- "show_post": "Show Post",
+ "user_reblogged": "%s reblogoja",
+ "user_replied_to": "Atbildēja %s",
+ "show_post": "Parādīt Ziņu",
"show_user_profile": "Parādīt lietotāja profilu",
"content_warning": "Satura brīdinājums",
"sensitive_content": "Sensitīvs saturs",
- "media_content_warning": "Tap anywhere to reveal",
- "tap_to_reveal": "Tap to reveal",
+ "media_content_warning": "Pieskarieties jebkurā vietā, lai atklātu",
+ "tap_to_reveal": "Piest, lai atklātu",
+ "load_embed": "Ielādēt Iegultos",
+ "link_via_user": "%s caur %s",
"poll": {
"vote": "Balsot",
"closed": "Aizvērts"
},
"meta_entity": {
- "url": "Link: %s",
- "hashtag": "Hashtag: %s",
- "mention": "Show Profile: %s",
- "email": "Email address: %s"
+ "url": "Saite: %s",
+ "hashtag": "Sajaukt: %s",
+ "mention": "Rādīt Profilu: %s",
+ "email": "E-pasta adrese: %s"
},
"actions": {
"reply": "Atbildēt",
"reblog": "Reblogot",
- "unreblog": "Undo reblog",
+ "unreblog": "Atsaukt reblogu",
"favorite": "Izlase",
"unfavorite": "Izņemt no izlases",
"menu": "Izvēlne",
"hide": "Slēpt",
"show_image": "Rādīt attēlu",
"show_gif": "Rādīt GIF",
- "show_video_player": "Show video player",
- "tap_then_hold_to_show_menu": "Tap then hold to show menu"
+ "show_video_player": "Rādīt video atskaņotāju",
+ "share_link_in_post": "Kopīgot Saiti Ziņā",
+ "tap_then_hold_to_show_menu": "Pieskaries un turi, lai parādītu izvēlni"
},
"tag": {
"url": "URL",
"mention": "Pieminēt",
"link": "Saite",
- "hashtag": "Hashtag",
+ "hashtag": "Tēmturis",
"email": "E-pasts",
"emoji": "Emocijzīmes"
},
"visibility": {
- "unlisted": "Everyone can see this post but not display in the public timeline.",
- "private": "Only their followers can see this post.",
- "private_from_me": "Only my followers can see this post.",
- "direct": "Only mentioned user can see this post."
+ "unlisted": "Ikviens var redzēt šo ziņu, bet to nevar parādīt publiskajā laikrindā.",
+ "private": "Šo ziņu var redzēt tikai viņu sekotāji.",
+ "private_from_me": "Šo ziņu var redzēt tikai mani sekotāji.",
+ "direct": "Šo ziņu var redzēt tikai minētais lietotājs."
+ },
+ "translation": {
+ "translated_from": "Tulkots no %s, izmantojot %s",
+ "unknown_language": "Nezināms",
+ "unknown_provider": "Nezināms",
+ "show_original": "Parādīts Oriģināls"
}
},
"friendship": {
@@ -186,9 +205,9 @@
"unmute": "Noņemt apklusinājumu",
"unmute_user": "Noņemt apklusinājumu @%s",
"muted": "Apklusināts",
- "edit_info": "Edit Info",
- "show_reblogs": "Show Reblogs",
- "hide_reblogs": "Hide Reblogs"
+ "edit_info": "Rediģēt",
+ "show_reblogs": "Rādīt Reblogus",
+ "hide_reblogs": "Paslēpt Reblogus"
},
"timeline": {
"filtered": "Filtrēts",
@@ -196,42 +215,48 @@
"now": "Tagad"
},
"loader": {
- "load_missing_posts": "Load missing posts",
- "loading_missing_posts": "Loading missing posts...",
+ "load_missing_posts": "Ielādēt trūkstošās ziņas",
+ "loading_missing_posts": "Ielādē trūkstošās ziņas...",
"show_more_replies": "Rādīt vairāk atbildes"
},
"header": {
- "no_status_found": "No Post Found",
- "blocking_warning": "You can’t view this user's profile\nuntil you unblock them.\nYour profile looks like this to them.",
- "user_blocking_warning": "You can’t view %s’s profile\nuntil you unblock them.\nYour profile looks like this to them.",
- "blocked_warning": "You can’t view this user’s profile\nuntil they unblock you.",
- "user_blocked_warning": "You can’t view %s’s profile\nuntil they unblock you.",
- "suspended_warning": "This user has been suspended.",
- "user_suspended_warning": "%s’s account has been suspended."
+ "no_status_found": "Nav Atrastu Ziņu",
+ "blocking_warning": "Tu nevari skatīt šī lietotāja profilu,\nlīdz tu tos atbloķē.\nTavs profils viņiem izskatās šādi.",
+ "user_blocking_warning": "Tu nevari skatīt %s profilu,\nlīdz tu tos atbloķē.\nTavs profils viņiem izskatās šādi.",
+ "blocked_warning": "Tu nevari skatīt šī lietotāja profilu\nlīdz tie tevi atbloķē.",
+ "user_blocked_warning": "Tu nevari skatīt %s profilu,\nlīdz tie tevi atbloķē.",
+ "suspended_warning": "Šī lietotāja darbība ir apturēta.",
+ "user_suspended_warning": "%s konta darbība ir apturēta."
}
}
}
},
"scene": {
"welcome": {
- "slogan": "Social networking\nback in your hands.",
- "get_started": "Get Started",
+ "slogan": "Sociālie tīkli\natpakaļ tavās rokās.",
+ "get_started": "Sāc",
"log_in": "Pieteikties"
},
+ "login": {
+ "title": "Laipni lūdzam atpakaļ",
+ "subtitle": "Piesakies serverī, kurā izveidoji savu kontu.",
+ "server_search_field": {
+ "placeholder": "Ievadi URL vai meklē savu serveri"
+ }
+ },
"server_picker": {
- "title": "Mastodon is made of users in different servers.",
- "subtitle": "Pick a server based on your interests, region, or a general purpose one.",
- "subtitle_extend": "Pick a server based on your interests, region, or a general purpose one. Each server is operated by an entirely independent organization or individual.",
+ "title": "Mastodon veido lietotāji dažādos serveros.",
+ "subtitle": "Izvēlieties serveri, pamatojoties uz savu reģionu, interesēm vai vispārīgu mērķi. Tu joprojām vari tērzēt ar jebkuru Mastodon lietotāju neatkarīgi no taviem serveriem.",
"button": {
"category": {
"all": "Visi",
"all_accessiblity_description": "Katekorija: Visi",
- "academia": "academia",
- "activism": "activism",
+ "academia": "akadēmija",
+ "activism": "aktīvisms",
"food": "ēdiens",
- "furry": "furry",
+ "furry": "pūkains",
"games": "spēles",
- "general": "general",
+ "general": "galvenais",
"journalism": "žurnālisms",
"lgbt": "lgbt",
"regional": "regionāli",
@@ -248,18 +273,17 @@
"category": "KATEGORIJA"
},
"input": {
- "placeholder": "Meklēt serverus",
- "search_servers_or_enter_url": "Search servers or enter URL"
+ "search_servers_or_enter_url": "Meklēt kopienas vai ievadīt URL"
},
"empty_state": {
- "finding_servers": "Finding available servers...",
- "bad_network": "Something went wrong while loading the data. Check your internet connection.",
+ "finding_servers": "Meklē piejamos serverus...",
+ "bad_network": "Ielādējot datus, radās problēma. Pārbaudi interneta savienojumu.",
"no_results": "Nav rezultātu"
}
},
"register": {
- "title": "Let’s get you set up on %s",
- "lets_get_you_set_up_on_domain": "Let’s get you set up on %s",
+ "title": "Ļauj tevi iestatīt %s",
+ "lets_get_you_set_up_on_domain": "Ļauj tevi iestatīt %s",
"input": {
"avatar": {
"delete": "Dzēst"
@@ -276,7 +300,7 @@
},
"password": {
"placeholder": "parole",
- "require": "Your password needs at least:",
+ "require": "Tavai parolei ir nepieciešams vismaz:",
"character_limit": "8 rakstzīmes",
"accessibility": {
"checked": "atzīmēts",
@@ -298,29 +322,29 @@
"reason": "Iemesls"
},
"reason": {
- "blocked": "%s contains a disallowed email provider",
+ "blocked": "%s satur neatļautu e-pasta pakalpojumu sniedzēju",
"unreachable": "%s šķiet, ka neeksistē",
"taken": "%s jau tiek izmantots",
- "reserved": "%s is a reserved keyword",
- "accepted": "%s must be accepted",
+ "reserved": "%s ir rezervēts atslēgvārds",
+ "accepted": "%s jābūt apstiprinātām",
"blank": "%s ir obligāts",
"invalid": "%s ir nederīgs",
"too_long": "%s ir pārāk garaš",
"too_short": "%s ir pārāk īs",
- "inclusion": "%s is not a supported value"
+ "inclusion": "%s nav atbalstīta vērtība"
},
"special": {
- "username_invalid": "Username must only contain alphanumeric characters and underscores",
- "username_too_long": "Username is too long (can’t be longer than 30 characters)",
- "email_invalid": "This is not a valid email address",
- "password_too_short": "Password is too short (must be at least 8 characters)"
+ "username_invalid": "Lietotājvārdā drīkst būt tikai burtciparu rakstzīmes un zemsvītras",
+ "username_too_long": "Lietotājvārds ir par garu (nedrīkst būt garāks par 30 rakstzīmēm)",
+ "email_invalid": "Šī nav derīga e-pasta adrese",
+ "password_too_short": "Parole ir pārāk īsa (jābūt vismaz 8 rakstzīmēm)"
}
}
},
"server_rules": {
- "title": "Some ground rules.",
- "subtitle": "These are set and enforced by the %s moderators.",
- "prompt": "By continuing, you’re subject to the terms of service and privacy policy for %s.",
+ "title": "Daži pamatnoteikumi.",
+ "subtitle": "Tos iestata un ievieš %s moderatori.",
+ "prompt": "Turpinot, uz tevi attiecas %s pakalpojumu sniegšanas noteikumi un konfidencialitātes politika.",
"terms_of_service": "pakalpojuma noteikumi",
"privacy_policy": "privātuma nosacījumi",
"button": {
@@ -328,45 +352,45 @@
}
},
"confirm_email": {
- "title": "One last thing.",
- "subtitle": "Tap the link we emailed to you to verify your account.",
- "tap_the_link_we_emailed_to_you_to_verify_your_account": "Tap the link we emailed to you to verify your account",
+ "title": "Pēdējā lieta.",
+ "subtitle": "Pieskaries saitei, ko nosūtījām tev pa e-pastu, lai verificētu savu kontu.",
+ "tap_the_link_we_emailed_to_you_to_verify_your_account": "Pieskaries saitei, ko nosūtījām tev pa e-pastu, lai verificētu savu kontu",
"button": {
- "open_email_app": "Open Email App",
+ "open_email_app": "Atvērt E-pasta Lietotni",
"resend": "Nosūtīt atkārtoti"
},
"dont_receive_email": {
"title": "Pārbaudi savu e-pastu",
- "description": "Check if your email address is correct as well as your junk folder if you haven’t.",
+ "description": "Pārbaudi, vai tava e-pasta adrese ir pareiza, kā arī savu mēstuļu mapi, ja tā nav.",
"resend_email": "Atkārtoti nosūtīt e-pastu"
},
"open_email_app": {
- "title": "Check your inbox.",
- "description": "We just sent you an email. Check your junk folder if you haven’t.",
- "mail": "Mail",
- "open_email_client": "Open Email Client"
+ "title": "Pārbaudi savu iesūtni.",
+ "description": "Mēs tikko nosūtījām tev e-pastu. Pārbaudi savu mēstuļu mapi, ja neesi to saņēmis.",
+ "mail": "Pasts",
+ "open_email_client": "Atvērt E-pasta Klientu"
}
},
"home_timeline": {
- "title": "Home",
+ "title": "Sākums",
"navigation_bar_state": {
- "offline": "Offline",
- "new_posts": "See new posts",
- "published": "Published!",
- "Publishing": "Publishing post...",
+ "offline": "Bezsaistē",
+ "new_posts": "Skatīt jaunās ziņas",
+ "published": "Publicēts!",
+ "Publishing": "Publicē ziņu...",
"accessibility": {
- "logo_label": "Logo Button",
- "logo_hint": "Tap to scroll to top and tap again to previous location"
+ "logo_label": "Logotipa Poga",
+ "logo_hint": "Pieskaries, lai ritinātu uz augšu, un vēlreiz pieskaries iepriekšējai atrašanās vietai"
}
}
},
"suggestion_account": {
- "title": "Find People to Follow",
- "follow_explain": "When you follow someone, you’ll see their posts in your home feed."
+ "title": "Atrodi Cilvēkus kam Sekot",
+ "follow_explain": "Kad seko kādam, tu redzēsi viņu ziņas savā mājas plūsmā."
},
"compose": {
"title": {
- "new_post": "New Post",
+ "new_post": "Jauna Ziņa",
"new_reply": "Jauna atbilde"
},
"media_selection": {
@@ -374,32 +398,36 @@
"photo_library": "Attēlu krātuve",
"browse": "Pārlūkot"
},
- "content_input_placeholder": "Type or paste what’s on your mind",
+ "content_input_placeholder": "Ieraksti vai ielīmē to, ko domā",
"compose_action": "Publicēt",
- "replying_to_user": "replying to %s",
+ "replying_to_user": "atbildot uz %s",
"attachment": {
"photo": "attēls",
"video": "video",
- "attachment_broken": "This %s is broken and can’t be\nuploaded to Mastodon.",
- "description_photo": "Describe the photo for the visually-impaired...",
- "description_video": "Describe the video for the visually-impaired...",
- "load_failed": "Load Failed",
- "upload_failed": "Upload Failed",
- "can_not_recognize_this_media_attachment": "Can not regonize this media attachment",
- "attachment_too_large": "Attachment too large"
+ "attachment_broken": "Šis %s ir salauzts un nevar tikt augšuplādēts Mastodon.",
+ "description_photo": "Apraksti fotoattēlu vājredzīgajiem...",
+ "description_video": "Apraksti video vājredzīgajiem...",
+ "load_failed": "Ielāde Neizdevās",
+ "upload_failed": "Augšupielāde Neizdevās",
+ "can_not_recognize_this_media_attachment": "Nevar atpazīt šo multivides pielikumu",
+ "attachment_too_large": "Pārāk liels pielikums",
+ "compressing_state": "Saspiež...",
+ "server_processing_state": "Notiek servera apstrāde..."
},
"poll": {
- "duration_time": "Duration: %s",
+ "duration_time": "Ilgums: %s",
"thirty_minutes": "30 minūtes",
"one_hour": "1 Stunda",
"six_hours": "6 stundas",
"one_day": "1 Diena",
"three_days": "3 Dienas",
"seven_days": "7 Dienas",
- "option_number": "Option %ld"
+ "option_number": "Izvēle %ld",
+ "the_poll_is_invalid": "Aptauja nav derīga",
+ "the_poll_has_empty_option": "Aptaujai ir tukša opcija"
},
"content_warning": {
- "placeholder": "Write an accurate warning here..."
+ "placeholder": "Uzraksti šeit precīzu brīdinājumu..."
},
"visibility": {
"public": "Publisks",
@@ -408,24 +436,26 @@
"direct": "Tikai cilvēki, kurus es pieminu"
},
"auto_complete": {
- "space_to_add": "Space to add"
+ "space_to_add": "Vieta, ko pievienot"
},
"accessibility": {
"append_attachment": "Pievienot pielikumu",
"append_poll": "Pievienot aptauju",
"remove_poll": "Noņemt aptauju",
- "custom_emoji_picker": "Custom Emoji Picker",
- "enable_content_warning": "Enable Content Warning",
- "disable_content_warning": "Disable Content Warning",
- "post_visibility_menu": "Post Visibility Menu"
+ "custom_emoji_picker": "Pielāgoto Emocijzīmju Atlasītājs",
+ "enable_content_warning": "Iespējot Satura Brīdinājumu",
+ "disable_content_warning": "Atspējot Satura Brīdinājumu",
+ "post_visibility_menu": "Ziņu Redzamības Izvēlne",
+ "post_options": "Ziņas Iespējas",
+ "posting_as": "Publicēt kā %s"
},
"keyboard": {
- "discard_post": "Discard Post",
- "publish_post": "Publish Post",
- "toggle_poll": "Toggle Poll",
- "toggle_content_warning": "Toggle Content Warning",
+ "discard_post": "Izmest Ziņu",
+ "publish_post": "Publicēt Ziņu",
+ "toggle_poll": "Pārslēgt Aptauju",
+ "toggle_content_warning": "Pārslēgt Satura Brīdinājumu",
"append_attachment_entry": "Pievienot pielikumu - %s",
- "select_visibility_entry": "Select Visibility - %s"
+ "select_visibility_entry": "Atlasīt Redzamību — %s"
}
},
"profile": {
@@ -433,19 +463,27 @@
"follows_you": "Seko tev"
},
"dashboard": {
- "posts": "posts",
- "following": "seko",
- "followers": "sekottāji"
+ "my_posts": "ziņas",
+ "my_following": "seko",
+ "my_followers": "sekotāji",
+ "other_posts": "ziņas",
+ "other_following": "seko",
+ "other_followers": "sekotāji"
},
"fields": {
+ "joined": "Pievienojās",
"add_row": "Pievienot rindu",
"placeholder": {
- "label": "Label",
+ "label": "Marķējums",
"content": "Saturs"
+ },
+ "verified": {
+ "short": "Pārbaudīts %s",
+ "long": "Šīs saites piederība tika pārbaudīta %s"
}
},
"segmented_control": {
- "posts": "Posts",
+ "posts": "Ziņas",
"replies": "Atbildes",
"posts_and_replies": "Ziņas un atbildes",
"media": "Multivide",
@@ -453,97 +491,97 @@
},
"relationship_action_alert": {
"confirm_mute_user": {
- "title": "Mute Account",
- "message": "Confirm to mute %s"
+ "title": "Izslēgt Kontu",
+ "message": "Apstiprināt, lai izslēgtu %s skaņu"
},
"confirm_unmute_user": {
- "title": "Unmute Account",
- "message": "Confirm to unmute %s"
+ "title": "Ieslēgt Kontu",
+ "message": "Apstiprināt, lai ieslēgtu %s skaņu"
},
"confirm_block_user": {
"title": "Bloķēts kontu",
- "message": "Confirm to block %s"
+ "message": "Apstiprināt, lai bloķētu %s"
},
"confirm_unblock_user": {
"title": "Atbloķēt kontu",
"message": "Apstiprini lai atbloķētu %s"
},
"confirm_show_reblogs": {
- "title": "Show Reblogs",
- "message": "Confirm to show reblogs"
+ "title": "Rādīt Reblogus",
+ "message": "Apstiprināt, lai rādītu reblogus"
},
"confirm_hide_reblogs": {
- "title": "Hide Reblogs",
- "message": "Confirm to hide reblogs"
+ "title": "Paslēpt Reblogus",
+ "message": "Apstiprināt, lai slēptu reblogus"
}
},
"accessibility": {
- "show_avatar_image": "Show avatar image",
- "edit_avatar_image": "Edit avatar image",
- "show_banner_image": "Show banner image",
- "double_tap_to_open_the_list": "Double tap to open the list"
+ "show_avatar_image": "Rādīt avatara attēlu",
+ "edit_avatar_image": "Rediģēt avatara attēlu",
+ "show_banner_image": "Rādīt bannera attēlu",
+ "double_tap_to_open_the_list": "Dubultskāriens, lai atvērtu sarakstu"
}
},
"follower": {
"title": "sekottājs",
- "footer": "Followers from other servers are not displayed."
+ "footer": "Sekotāji no citiem serveriem netiek rādīti."
},
"following": {
"title": "seko",
- "footer": "Follows from other servers are not displayed."
+ "footer": "Sekojumi no citiem serveriem netiek rādīti."
},
"familiarFollowers": {
- "title": "Followers you familiar",
- "followed_by_names": "Followed by %s"
+ "title": "Tev pazīstamie sekotāji",
+ "followed_by_names": "Seko %s"
},
"favorited_by": {
- "title": "Favorited By"
+ "title": "Pievienoja izlasei"
},
"reblogged_by": {
- "title": "Reblogged By"
+ "title": "Reblogoja"
},
"search": {
"title": "Meklēt",
"search_bar": {
- "placeholder": "Search hashtags and users",
+ "placeholder": "Meklēt tēmturus un lietotājus",
"cancel": "Atcelt"
},
"recommend": {
"button_text": "Skatīt visu",
"hash_tag": {
- "title": "Trending on Mastodon",
- "description": "Hashtags that are getting quite a bit of attention",
- "people_talking": "%s people are talking"
+ "title": "Tendences vietnē Mastodon",
+ "description": "Tēmturi, kuriem tiek pievērsta diezgan liela uzmanība",
+ "people_talking": "%s cilvēki apspriež"
},
"accounts": {
- "title": "Accounts you might like",
- "description": "You may like to follow these accounts",
- "follow": "Follow"
+ "title": "Konti, kuri tev varētu patikt",
+ "description": "Iespējams, tu vēlēsies sekot šiem kontiem",
+ "follow": "Sekot"
}
},
"searching": {
"segment": {
- "all": "All",
- "people": "People",
- "hashtags": "Hashtags",
- "posts": "Posts"
+ "all": "Visi",
+ "people": "Cilvēki",
+ "hashtags": "Tēmturi",
+ "posts": "Ziņas"
},
"empty_state": {
- "no_results": "No results"
+ "no_results": "Nav rezultātu"
},
- "recent_search": "Recent searches",
- "clear": "Clear"
+ "recent_search": "Nesen meklētais",
+ "clear": "Notīrīt"
}
},
"discovery": {
"tabs": {
"posts": "Ziņas",
- "hashtags": "Hashtags",
+ "hashtags": "Tēmturi",
"news": "Ziņas",
- "community": "Community",
+ "community": "Kopiena",
"for_you": "Priekš tevis"
},
- "intro": "These are the posts gaining traction in your corner of Mastodon."
+ "intro": "Šīs ir ziņas, kas iekaro tavu Mastodon stūrīti."
},
"favorite": {
"title": "Tava izlase"
@@ -554,16 +592,16 @@
"Mentions": "Pieminējumi"
},
"notification_description": {
- "followed_you": "followed you",
- "favorited_your_post": "favorited your post",
- "reblogged_your_post": "reblogged your post",
+ "followed_you": "tev sekoja",
+ "favorited_your_post": "izcēla tavu ziņu",
+ "reblogged_your_post": "reblogoja tavu ziņu",
"mentioned_you": "pieminēja tevi",
- "request_to_follow_you": "request to follow you",
+ "request_to_follow_you": "lūgums tev sekot",
"poll_has_ended": "balsošana beidzās"
},
"keyobard": {
"show_everything": "Parādīt man visu",
- "show_mentions": "Show Mentions"
+ "show_mentions": "Rādīt Pieminējumus"
},
"follow_request": {
"accept": "Pieņemt",
@@ -574,7 +612,7 @@
},
"thread": {
"back_title": "Ziņa",
- "title": "Post from %s"
+ "title": "Ziņa no %s"
},
"settings": {
"title": "Iestatījumi",
@@ -587,42 +625,42 @@
},
"look_and_feel": {
"title": "Izskats",
- "use_system": "Use System",
+ "use_system": "Lietot Sistēmas",
"really_dark": "Ļoti tumšs",
"sorta_dark": "Itkā tumšs",
"light": "Gaišs"
},
"notifications": {
"title": "Paziņojumi",
- "favorites": "Favorites my post",
+ "favorites": "Izceļ manu ziņu",
"follows": "Seko man",
- "boosts": "Reblogs my post",
+ "boosts": "Reblogo manu ziņu",
"mentions": "Pieminējumi",
"trigger": {
"anyone": "jebkurš",
"follower": "sekottājs",
- "follow": "anyone I follow",
+ "follow": "jebkurš, kam sekoju",
"noone": "neviens",
- "title": "Notify me when"
+ "title": "Paziņot man, kad"
}
},
"preference": {
"title": "Uzstādījumi",
- "true_black_dark_mode": "True black dark mode",
- "disable_avatar_animation": "Disable animated avatars",
- "disable_emoji_animation": "Disable animated emojis",
- "using_default_browser": "Use default browser to open links",
- "open_links_in_mastodon": "Open links in Mastodon"
+ "true_black_dark_mode": "Īsti melns tumšais režīms",
+ "disable_avatar_animation": "Atspējot animētos avatarus",
+ "disable_emoji_animation": "Atspējot animētās emocijzīmes",
+ "using_default_browser": "Saišu atvēršana noklusētajā pārlūkā",
+ "open_links_in_mastodon": "Atvērt saites Mastodon"
},
"boring_zone": {
- "title": "The Boring Zone",
+ "title": "Garlaicīgā zona",
"account_settings": "Konta iestatījumi",
"terms": "Pakalpojuma noteikumi",
"privacy": "Privātuma politika"
},
"spicy_zone": {
- "title": "The Spicy Zone",
- "clear": "Clear Media Cache",
+ "title": "Pikantā zona",
+ "clear": "Notīrīt Multivides Kešatmiņu",
"signout": "Iziet"
}
},
@@ -630,7 +668,7 @@
"mastodon_description": "Mastodon ir atvērtā koda programmatūra. Tu vari ziņot par problēmām GitHub %s (%s)"
},
"keyboard": {
- "close_settings_window": "Close Settings Window"
+ "close_settings_window": "Aizvērt Iestatījumu Logu"
}
},
"report": {
@@ -638,24 +676,24 @@
"title": "Ziņot %s",
"step1": "1. solis no 2",
"step2": "2. solis no 2",
- "content1": "Are there any other posts you’d like to add to the report?",
- "content2": "Is there anything the moderators should know about this report?",
- "report_sent_title": "Thanks for reporting, we’ll look into this.",
+ "content1": "Vai ir vēl kādas ziņas, kuras vēlies pievienot pārskatam?",
+ "content2": "Vai moderatoriem ir kaut kas jāzina par šo ziņojumu?",
+ "report_sent_title": "Paldies, ka ziņoji, mēs to izskatīsim.",
"send": "Nosūtīt Sūdzību",
"skip_to_send": "Sūtīt bez komentāra",
- "text_placeholder": "Type or paste additional comments",
- "reported": "REPORTED",
+ "text_placeholder": "Ieraksti vai ielīmē papildu komentārus",
+ "reported": "ZIŅOTS",
"step_one": {
"step_1_of_4": "1. solis no 4",
- "whats_wrong_with_this_post": "What's wrong with this post?",
- "whats_wrong_with_this_account": "What's wrong with this account?",
- "whats_wrong_with_this_username": "What's wrong with %s?",
+ "whats_wrong_with_this_post": "Kas vainas šim ierakstam?",
+ "whats_wrong_with_this_account": "Kas vainas šim kontam?",
+ "whats_wrong_with_this_username": "Kas vainas %s?",
"select_the_best_match": "Izvēlieties labāko atbilstību",
"i_dont_like_it": "Man tas nepatīk",
"it_is_not_something_you_want_to_see": "Tas nav kaut kas, ko tu vēlies redzēt",
"its_spam": "Tas ir spams",
- "malicious_links_fake_engagement_or_repetetive_replies": "Malicious links, fake engagement, or repetetive replies",
- "it_violates_server_rules": "It violates server rules",
+ "malicious_links_fake_engagement_or_repetetive_replies": "Ļaunprātīgas saites, viltus iesaistīšana vai atkārtotas atbildes",
+ "it_violates_server_rules": "Tas pārkāpj servera noteikumus",
"you_are_aware_that_it_breaks_specific_rules": "Tu zini, ka tas pārkāpj īpašus noteikumus",
"its_something_else": "Tas ir kaut kas cits",
"the_issue_does_not_fit_into_other_categories": "Šis jautājums neietilpst citās kategorijās"
@@ -664,7 +702,7 @@
"step_2_of_4": "2. solis no 4",
"which_rules_are_being_violated": "Kuri noteikumi tiek pārkāpti?",
"select_all_that_apply": "Atlasi visus atbilstošos",
- "i_just_don’t_like_it": "I just don’t like it"
+ "i_just_don’t_like_it": "Man vienkārši tas nepatīk"
},
"step_three": {
"step_3_of_4": "3. solis no 4",
@@ -677,36 +715,48 @@
},
"step_final": {
"dont_want_to_see_this": "Vai nevēlies to redzēt?",
- "when_you_see_something_you_dont_like_on_mastodon_you_can_remove_the_person_from_your_experience.": "When you see something you don’t like on Mastodon, you can remove the person from your experience.",
+ "when_you_see_something_you_dont_like_on_mastodon_you_can_remove_the_person_from_your_experience.": "Kad pakalpojumā Mastodon redzi kaut ko, kas tev nepatīk, tu vari noņemt šo personu no savas pieredzes.",
"unfollow": "Atsekot",
"unfollowed": "Atsekoja",
"unfollow_user": "Atsekot %s",
"mute_user": "Apklusināt %s",
- "you_wont_see_their_posts_or_reblogs_in_your_home_feed_they_wont_know_they_ve_been_muted": "You won’t see their posts or reblogs in your home feed. They won’t know they’ve been muted.",
+ "you_wont_see_their_posts_or_reblogs_in_your_home_feed_they_wont_know_they_ve_been_muted": "Tu neredzēsi viņu ziņas vai reblogus savā mājas plūsmā. Viņi nezinās, ka ir izslēgti.",
"block_user": "Bloķēt %s",
- "they_will_no_longer_be_able_to_follow_or_see_your_posts_but_they_can_see_if_theyve_been_blocked": "They will no longer be able to follow or see your posts, but they can see if they’ve been blocked.",
+ "they_will_no_longer_be_able_to_follow_or_see_your_posts_but_they_can_see_if_theyve_been_blocked": "Viņi vairs nevarēs sekot tavām ziņām vai redzēt tās, taču varēs redzēt, vai viņi ir bloķēti.",
"while_we_review_this_you_can_take_action_against_user": "Kamēr mēs to izskatām, tu vari veikt darbības pret @%s"
}
},
"preview": {
"keyboard": {
- "close_preview": "Close Preview",
- "show_next": "Show Next",
- "show_previous": "Show Previous"
+ "close_preview": "Aizvērt Priekšskatījumu",
+ "show_next": "Rādīt Nākamo",
+ "show_previous": "Rādīt Iepriekšējo"
}
},
"account_list": {
- "tab_bar_hint": "Current selected profile: %s. Double tap then hold to show account switcher",
- "dismiss_account_switcher": "Dismiss Account Switcher",
+ "tab_bar_hint": "Pašreizējais atlasītais profils: %s. Veic dubultskārienu un pēc tam turi, lai parādītu konta pārslēdzēju",
+ "dismiss_account_switcher": "Noraidīt Konta Pārslēdzēju",
"add_account": "Pievienot kontu"
},
"wizard": {
- "new_in_mastodon": "New in Mastodon",
- "multiple_account_switch_intro_description": "Switch between multiple accounts by holding the profile button.",
- "accessibility_hint": "Double tap to dismiss this wizard"
+ "new_in_mastodon": "Jaunums Mastodonā",
+ "multiple_account_switch_intro_description": "Pārslēdzies starp vairākiem kontiem, turot nospiestu profila pogu.",
+ "accessibility_hint": "Veic dubultskārienu, lai noraidītu šo vedni"
},
"bookmark": {
- "title": "Bookmarks"
+ "title": "Grāmatzīmes"
+ },
+ "followed_tags": {
+ "title": "Sekotie Tēmturi",
+ "header": {
+ "posts": "ziņas",
+ "participants": "dalībnieki",
+ "posts_today": "ziņas šodien"
+ },
+ "actions": {
+ "follow": "Sekot",
+ "unfollow": "Atsekot"
+ }
}
}
}
diff --git a/Localization/StringsConvertor/input/lv.lproj/ios-infoPlist.json b/Localization/StringsConvertor/input/lv.lproj/ios-infoPlist.json
index c6db73de0..860333e51 100644
--- a/Localization/StringsConvertor/input/lv.lproj/ios-infoPlist.json
+++ b/Localization/StringsConvertor/input/lv.lproj/ios-infoPlist.json
@@ -1,6 +1,6 @@
{
- "NSCameraUsageDescription": "Used to take photo for post status",
- "NSPhotoLibraryAddUsageDescription": "Used to save photo into the Photo Library",
- "NewPostShortcutItemTitle": "New Post",
- "SearchShortcutItemTitle": "Search"
+ "NSCameraUsageDescription": "Izmanto, lai fotografētu ziņas statusu",
+ "NSPhotoLibraryAddUsageDescription": "Izmanto, lai saglabātu fotoattēlu Photo Library",
+ "NewPostShortcutItemTitle": "Jauna Ziņa",
+ "SearchShortcutItemTitle": "Meklēt"
}
diff --git a/Localization/StringsConvertor/input/my.lproj/Localizable.stringsdict b/Localization/StringsConvertor/input/my.lproj/Localizable.stringsdict
new file mode 100644
index 000000000..8230962a5
--- /dev/null
+++ b/Localization/StringsConvertor/input/my.lproj/Localizable.stringsdict
@@ -0,0 +1,407 @@
+
+
+
+
+ a11y.plural.count.unread.notification
+
+ NSStringLocalizedFormatKey
+ %#@notification_count_unread_notification@
+ notification_count_unread_notification
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ other
+ မဖတ်ရသေးသောအသိပေးချက် %ld ခု ရှိသည်
+
+
+ a11y.plural.count.input_limit_exceeds
+
+ NSStringLocalizedFormatKey
+ စာလုံးရေ သတ်မှတ်ချက်ထက် %#@character_count@ လုံး ထက်ကျော်လွန်
+ character_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ other
+ စာလုံး %ld လုံး
+
+
+ a11y.plural.count.input_limit_remains
+
+ NSStringLocalizedFormatKey
+ သတ်မှတ်စာလုံးရေ %#@character_count@ လုံး ကျန်ရှိ
+ character_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ other
+ စာလုံး %ld လုံး
+
+
+ a11y.plural.count.characters_left
+
+ NSStringLocalizedFormatKey
+ စာလုံးရေ %#@character_count@ လုံး ကျန်ရှိ
+ character_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ other
+ စာလုံး %ld လုံး
+
+
+ plural.count.followed_by_and_mutual
+
+ NSStringLocalizedFormatKey
+ %#@names@%#@count_mutual@
+ names
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ other
+
+
+ count_mutual
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ other
+ %1$@ နှင့် ဘုံသူငယ်ချင်း %ld ဦးမှ စောင့်ကြည့်နေသည်
+
+
+ plural.count.metric_formatted.post
+
+ NSStringLocalizedFormatKey
+ %@ %#@post_count@
+ post_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ other
+ ပို့စ်များ
+
+
+ plural.count.media
+
+ NSStringLocalizedFormatKey
+ %#@media_count@
+ media_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ other
+ ရုပ်သံ %ld ခု
+
+
+ plural.count.post
+
+ NSStringLocalizedFormatKey
+ %#@post_count@
+ post_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ other
+ ပို့စ် %ld ခု
+
+
+ plural.count.favorite
+
+ NSStringLocalizedFormatKey
+ %#@favorite_count@
+ favorite_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ other
+ အကြိုက်ဆုံး %ld ခု
+
+
+ plural.count.reblog
+
+ NSStringLocalizedFormatKey
+ %#@reblog_count@
+ reblog_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ other
+ ပြန်မျှဝေမှု %ld ခု
+
+
+ plural.count.reply
+
+ NSStringLocalizedFormatKey
+ %#@reply_count@
+ reply_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ other
+ ပြန်စာ %ld ခု
+
+
+ plural.count.vote
+
+ NSStringLocalizedFormatKey
+ %#@vote_count@
+ vote_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ other
+ မဲ %ld မဲ
+
+
+ plural.count.voter
+
+ NSStringLocalizedFormatKey
+ %#@voter_count@
+ voter_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ other
+ မဲပေးသူ %ld ဦး
+
+
+ plural.people_talking
+
+ NSStringLocalizedFormatKey
+ %#@count_people_talking@
+ count_people_talking
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ other
+ လူ %ld ဦး ပြောနေသည်
+
+
+ plural.count.following
+
+ NSStringLocalizedFormatKey
+ %#@count_following@
+ count_following
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ other
+ စောင့်ကြည့်သူ %ld ဦး
+
+
+ plural.count.follower
+
+ NSStringLocalizedFormatKey
+ %#@count_follower@
+ count_follower
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ other
+ စောင့်ကြည့်သူ %ld ဦး
+
+
+ date.year.left
+
+ NSStringLocalizedFormatKey
+ %#@count_year_left@
+ count_year_left
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ other
+ %ld နှစ် ကျန်ရှိ
+
+
+ date.month.left
+
+ NSStringLocalizedFormatKey
+ %#@count_month_left@
+ count_month_left
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ other
+ %ld လ ကျန်ရှိ
+
+
+ date.day.left
+
+ NSStringLocalizedFormatKey
+ %#@count_day_left@
+ count_day_left
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ other
+ %ld ရက် ကျန်ရှိ
+
+
+ date.hour.left
+
+ NSStringLocalizedFormatKey
+ %#@count_hour_left@
+ count_hour_left
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ other
+ %ld နာရီ ကျန်ရှိ
+
+
+ date.minute.left
+
+ NSStringLocalizedFormatKey
+ %#@count_minute_left@
+ count_minute_left
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ other
+ %ld မိနစ် ကျန်ရှိ
+
+
+ date.second.left
+
+ NSStringLocalizedFormatKey
+ %#@count_second_left@
+ count_second_left
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ other
+ %ld စက္ကန့် ကျန်ရှိ
+
+
+ date.year.ago.abbr
+
+ NSStringLocalizedFormatKey
+ %#@count_year_ago_abbr@
+ count_year_ago_abbr
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ other
+ လွန်ခဲ့သော %ld နှစ်က
+
+
+ date.month.ago.abbr
+
+ NSStringLocalizedFormatKey
+ %#@count_month_ago_abbr@
+ count_month_ago_abbr
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ other
+ လွန်ခဲ့သော %ld လက
+
+
+ date.day.ago.abbr
+
+ NSStringLocalizedFormatKey
+ %#@count_day_ago_abbr@
+ count_day_ago_abbr
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ other
+ လွန်ခဲ့သော %ld ရက်က
+
+
+ date.hour.ago.abbr
+
+ NSStringLocalizedFormatKey
+ %#@count_hour_ago_abbr@
+ count_hour_ago_abbr
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ other
+ လွန်ခဲ့သော %ld နာရီက
+
+
+ date.minute.ago.abbr
+
+ NSStringLocalizedFormatKey
+ %#@count_minute_ago_abbr@
+ count_minute_ago_abbr
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ other
+ လွန်ခဲ့သော %ld မိနစ်က
+
+
+ date.second.ago.abbr
+
+ NSStringLocalizedFormatKey
+ %#@count_second_ago_abbr@
+ count_second_ago_abbr
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ other
+ လွန်ခဲ့သော %ld စက္ကန့်က
+
+
+
+
diff --git a/Localization/StringsConvertor/input/my.lproj/app.json b/Localization/StringsConvertor/input/my.lproj/app.json
new file mode 100644
index 000000000..ea235fa53
--- /dev/null
+++ b/Localization/StringsConvertor/input/my.lproj/app.json
@@ -0,0 +1,762 @@
+{
+ "common": {
+ "alerts": {
+ "common": {
+ "please_try_again": "ပြန်လည်ကြိုးစားကြည့်ပါ",
+ "please_try_again_later": "နောက်မှ ပြန်လည်ကြိုးစားကြည့်ပါ"
+ },
+ "sign_up_failure": {
+ "title": "အကောင့်ဖွင့်ခြင်း မအောင်မြင်ပါ"
+ },
+ "server_error": {
+ "title": "ဆာဗာ အမှား"
+ },
+ "vote_failure": {
+ "title": "မဲပေးမှု မအောင်မြင်ခြင်း",
+ "poll_ended": "စစ်တမ်းကောက်မှု ပြီးဆုံးပါပြီ"
+ },
+ "discard_post_content": {
+ "title": "မူကြမ်းကို ပယ်ဖျက်ပါ",
+ "message": "ရေးသားထားသောမူကြမ်းကို ပယ်ဖျက်ရန် အတည်ပြုပါ"
+ },
+ "publish_post_failure": {
+ "title": "ပို့စ်တင်ခြင်း မအောင်မြင်မှု",
+ "message": "ပို့စ်တင်ခြင်း မအောင်မြင်ပါ၊ သင်၏ အင်တာနက်ချိတ်ဆက်မှုကို စစ်ဆေးပါ။",
+ "attachments_message": {
+ "video_attach_with_photo": "ဓာတ်ပုံများပါဝင်သော ပို့စ်တွင် ဗီဒီိယိုကို တွဲတင်၍ မရပါ",
+ "more_than_one_video": "ဗီဒီိယို ၁ ခုထက်ပို၍ တွဲတင်၍ မရပါ"
+ }
+ },
+ "edit_profile_failure": {
+ "title": "ပရိုဖိုင်ပြင်ဆင်ခြင်း အမှား",
+ "message": "ပရိုဖိုင်ကို ပြင်ဆင်၍ မရပါ၊ ပြန်လည်ကြိုးစားကြည့်ပါ။"
+ },
+ "sign_out": {
+ "title": "ထွက်မည်",
+ "message": "အကောင့်မှ ထွက်ရန် သေချာပါသလား?",
+ "confirm": "ထွက်မည်"
+ },
+ "block_domain": {
+ "title": "%s တစ်ခုလုံးကို ဘလော့လုပ်ရန် တကယ် သေချာပါသလား? များသောအားဖြင့် အနည်းစုကို ပစ်မှတ်ထား ဘလော့လုပ်ခြင်းသည် လုံလောက်ပါသည်။ ထို ဒိုမိန်းမှ အကြောင်းအရာ တစ်ခုမှ မြင်ရမည်မဟုတ်သည့်အပြင် ထို ဒိုမိန်းတွင်ရှိသော သင်၏ စောင့်ကြည့်သူများပါ ဖယ်ရှားပစ်မည်ဖြစ်သည်။",
+ "block_entire_domain": "ဒိုမိန်းကို ဘလော့လုပ်ရန်"
+ },
+ "save_photo_failure": {
+ "title": "ဓာတ်ပုံသိမ်းဆည်းခြင်း အမှား",
+ "message": "ကျေးဇူးပြု၍ ဓာတ်ပုံသိမ်းဆည်းနိုင်ရန် ဓာတ်ပုံပြတိုက်သို့ ဝင်ရောက်ခွင့်ပေးပါ။"
+ },
+ "delete_post": {
+ "title": "ပို့စ်ဖျက်ရန်",
+ "message": "ပို့စ်ကို ဖျက်ရန် သေချာပါသလား?"
+ },
+ "clean_cache": {
+ "title": "Cache ကို ရှင်းပါ",
+ "message": "%s cache ကို အောင်မြင်စွာ ရှင်းလင်းပြီးပါပြီ"
+ },
+ "translation_failed": {
+ "title": "Note",
+ "message": "Translation failed. Maybe the administrator has not enabled translations on this server or this server is running an older version of Mastodon where translations are not yet supported.",
+ "button": "OK"
+ }
+ },
+ "controls": {
+ "actions": {
+ "back": "ပြန်၍",
+ "next": "ရှေ့သို့",
+ "previous": "ယခင်",
+ "open": "ဖွင့်",
+ "add": "ထည့်",
+ "remove": "ဖယ်ရှား",
+ "edit": "တည်းဖြတ်",
+ "save": "သိမ်းဆည်း",
+ "ok": "အိုကေ",
+ "done": "ပြီးပြီ",
+ "confirm": "အတည်ပြု",
+ "continue": "ဆက်လက်",
+ "compose": "ရေးဖွဲ့",
+ "cancel": "ပယ်ဖျက်",
+ "discard": "ဖယ်ရှား",
+ "try_again": "ထပ်မံကြိုးစားပါ",
+ "take_photo": "ဓါတ်ပုံရိုက်",
+ "save_photo": "ဓါတ်ပုံသိမ်းဆည်း",
+ "copy_photo": "ဓာတ်ပုံကူး",
+ "sign_in": "လော့ဂ်အင်ဝင်",
+ "sign_up": "အကောင့်ဖန်တီး",
+ "see_more": "ပိုမိုကြည့်ရှုရန်",
+ "preview": "အစမ်းကြည့်",
+ "copy": "Copy",
+ "share": "မျှဝေ",
+ "share_user": "%s ကို မျှဝေပါ",
+ "share_post": "ပို့စ်ကို မျှဝေရန်",
+ "open_in_safari": "Safari တွင် ဖွင့်ရန်",
+ "open_in_browser": "Browser တွင် ဖွင့်ရန်",
+ "find_people": "စောင့်ကြည့်ရန် လူရှာပါ",
+ "manually_search": "ကိုယ်တိုင် ရှာဖွေရန်",
+ "skip": "ကျော်",
+ "reply": "စာပြန်",
+ "report_user": " %s ကို တိုင်ကြားရန်",
+ "block_domain": "%s ကို ဘလော့လုပ်ရန်",
+ "unblock_domain": "%s ကို ဘလော့ဖြုတ်ရန်",
+ "settings": "ဆက်တင်များ",
+ "delete": "ဖျက်",
+ "translate_post": {
+ "title": "Translate from %s",
+ "unknown_language": "Unknown"
+ }
+ },
+ "tabs": {
+ "home": "အိမ်",
+ "search_and_explore": "Search and Explore",
+ "notifications": "အသိပေးချက်များ",
+ "profile": "ကိုယ်ရေးမှတ်တမ်း"
+ },
+ "keyboard": {
+ "common": {
+ "switch_to_tab": "%s သို့ ပြောင်းရန်",
+ "compose_new_post": "ပို့စ်အသစ် ရေးဖွဲ့",
+ "show_favorites": "အကြိုက်ဆုံးများ ပြရန်",
+ "open_settings": "ဆက်တင်ကို ဖွင့်ရန်"
+ },
+ "timeline": {
+ "previous_status": "ယခင်ပို့စ်",
+ "next_status": "နောက်ပို့စ်",
+ "open_status": "ပို့စ်ဖွင့်ရန်",
+ "open_author_profile": "စာရေးသူ၏ ပရိုဖိုင်ကို ဖွင့်ပါ",
+ "open_reblogger_profile": "ပြန်တင်သူ၏ ပရိုဖိုင်ကို ဖွင့်ပါ",
+ "reply_status": "ပို့စ်ကို စာပြန်",
+ "toggle_reblog": "ပို့စ်ကိုပြန်တင်ခွင့်ပေးခြင်းကို ဖွင့်၊ပိတ် လုပ်ပါ",
+ "toggle_favorite": "အကြိုက်ဆုံးလုပ်ခွင့်ပေးခြင်းကို ဖွင့်၊ပိတ် လုပ်ပါ",
+ "toggle_content_warning": "အကြောင်းအရာသတိပေးချက်ကို ဖွင့်၊ပိတ် လုပ်ပါ",
+ "preview_image": "ဓာတ်ပုံကို ကြိုကြည့်"
+ },
+ "segmented_control": {
+ "previous_section": "ယခင်အပိုင်း",
+ "next_section": "နောက်အပိုင်း"
+ }
+ },
+ "status": {
+ "user_reblogged": "%s ကို ပြန်တင်",
+ "user_replied_to": "%s ထံ စာပြန်",
+ "show_post": "ပို့စ်ကို ပြသ",
+ "show_user_profile": "အသုံးပြုသူ၏ ပရိုဖိုင်ကို ပြရန်",
+ "content_warning": "အကြောင်းအရာသတိပေးချက်",
+ "sensitive_content": "ထိလွယ်ရှလွယ် အကြောင်းအရာ",
+ "media_content_warning": "ဖော်ထုတ်ရန် မည်သည့်နေရာမဆို နှိပ်ပါ",
+ "tap_to_reveal": "ဖော်ထုတ်ရန် နှိပ်ပါ",
+ "load_embed": "Load Embed",
+ "link_via_user": "%s via %s",
+ "poll": {
+ "vote": "မဲပေး",
+ "closed": "ပိတ်သွားပြီ"
+ },
+ "meta_entity": {
+ "url": "လင့်ခ်: %s",
+ "hashtag": "ဟက်ရှ်တက်ခ်: %s",
+ "mention": "ပရိုဖိုင် ပြသ: %s",
+ "email": "အီးမေးလ်လိပ်စာ: %s"
+ },
+ "actions": {
+ "reply": "စာပြန်",
+ "reblog": "ပြန်တင်",
+ "unreblog": "ပြန်တင်ခြင်းကို ပယ်ဖျက်",
+ "favorite": "အကြိုက်ဆုံး",
+ "unfavorite": "အကြိုက်ဆုံးမှ ဖယ်ရှားရန်",
+ "menu": "မီနူး",
+ "hide": "ဝှက်ရန်",
+ "show_image": "ဓာတ်ပုံပြရန်",
+ "show_gif": "GIF ပြရန်",
+ "show_video_player": "ဗီဒီယိုဖွင့်စက် ပြရန်",
+ "share_link_in_post": "Share Link in Post",
+ "tap_then_hold_to_show_menu": "မီနူးပြရန် ဖိထားပါ"
+ },
+ "tag": {
+ "url": "URL",
+ "mention": "ရည်ညွှန်း",
+ "link": "လင့်ခ်",
+ "hashtag": "ဟက်ရှ်တက်ခ်",
+ "email": "အီးမေးလ်",
+ "emoji": "အီမိုဂျီ"
+ },
+ "visibility": {
+ "unlisted": "ဒီပို့စ်ကို လူတိုင်းမြင်နိုင်သည်၊ သို့သော် အများမြင်အလင်းစဉ်တွင် မပြသပါ။",
+ "private": "သူတို့၏ စောင့်ကြည့်သူများသာ ဒီပို့စ်ကို မြင်နိုင်သည်",
+ "private_from_me": "ကျွန်ုပ်၏ စောင့်ကြည့်သူများသာ ဒီပို့စ်ကို မြင်နိုင်သည်",
+ "direct": "ရည်ညွှန်းခံရသောအသုံးပြုသူများသာ ဒီပို့စ်ကို မြင်နိုင်သည်"
+ },
+ "translation": {
+ "translated_from": "Translated from %s using %s",
+ "unknown_language": "Unknown",
+ "unknown_provider": "Unknown",
+ "show_original": "Shown Original"
+ }
+ },
+ "friendship": {
+ "follow": "စောင့်ကြည့်ရန်",
+ "following": "စောင့်ကြည့်နေသည်",
+ "request": "တောင်းဆို",
+ "pending": "ဆိုင်းငံ့ထားသည်",
+ "block": "ဘလော့လုပ်ရန်",
+ "block_user": "%s ကို ဘလော့လုပ်ရန်",
+ "block_domain": "%s ကို ဘလော့လုပ်ရန်",
+ "unblock": "ဘလော့ဖြုတ်ရန်",
+ "unblock_user": "%s ကို ဘလော့ဖြုတ်ရန်",
+ "blocked": "ဘလော့ထားသည်",
+ "mute": "ပိတ်ထားရန်",
+ "mute_user": "%s ကို ပိတ်ထားရန်",
+ "unmute": "ပြန်ဖွင့်ရန်",
+ "unmute_user": "%s ကို ပြန်ဖွင့်ရန်",
+ "muted": "ပိတ်ထားဆဲ",
+ "edit_info": "အချက်အလက်တည်းဖြတ်",
+ "show_reblogs": "ပြန်တင်ထားတာတွေ ပြရန်",
+ "hide_reblogs": "ပြန်တင်ထားတာတွေ ဖျောက်ရန်"
+ },
+ "timeline": {
+ "filtered": "စစ်ထုတ်ထားသည်",
+ "timestamp": {
+ "now": "ယခု"
+ },
+ "loader": {
+ "load_missing_posts": "ပျောက်နေသော ပို့စ်များကို လုဒ်ပါ",
+ "loading_missing_posts": "ပျောက်နေသော ပို့စ်များကို လုဒ်ပါ...",
+ "show_more_replies": "ပြန်စာများထပ်ပြပါ"
+ },
+ "header": {
+ "no_status_found": "ပို့စ်ရှာမတွေ့ပါ",
+ "blocking_warning": "ဒီအသုံးပြုသူ၏ ပရိုဖိုင်ကို ဘလော့မဖြုတ်မချင်း ကြည့်၍မရပါ၊ သင်၏ ပရိုဖိုင်သည် ထိုသူတို့ထံ ဤကဲ့သို့ ပေါ်နေပါမည်။",
+ "user_blocking_warning": "%s ၏ ပရိုဖိုင်ကို ဘလော့မဖြုတ်မချင်း ကြည့်၍မရပါ၊ သင်၏ ပရိုဖိုင်သည် ထိုသူ့ထံ ဤကဲ့သို့ ပေါ်နေပါမည်။",
+ "blocked_warning": "ဤပုဂ္ဂိုလ်မှ သင့်ကို ဘလော့မဖြုတ်မချင်း သူ၏ ပရိုဖိုင်သည် သင် ကြည့်၍မရပါ။",
+ "user_blocked_warning": "%s မှ သင့်ကို ဘလော့မဖြုတ်မချင်း သူ၏ ပရိုဖိုင်သည် သင် ကြည့်၍မရပါ။",
+ "suspended_warning": "ဤအသုံးပြုသူသည် ဆိုင်းငံ့ခံထားရသည်။",
+ "user_suspended_warning": "%s ၏အကောင့်သည် ဆိုင်းငံ့ခံထားရသည်။"
+ }
+ }
+ }
+ },
+ "scene": {
+ "welcome": {
+ "slogan": "လူမှုကွန်ယက်ကို သင်၏လက်ထဲသို့ ပြန်လည်ထည့်ပေးလိုက်ပြီ။",
+ "get_started": "စတင်ရန်",
+ "log_in": "လော့ခ်အင်ဝင်ရန်"
+ },
+ "login": {
+ "title": "ပြန်လည်ကြိုဆိုပါသည်",
+ "subtitle": "သင်၏အကောင့်ဖွင့်ခဲ့သော ဆာဗာပေါ်တွင် လော့ခ်အင်ဝင်ရောက်ပါ",
+ "server_search_field": {
+ "placeholder": "URL ကို ထည့်သွင်းပါ (သို့) သင်၏ ဆာဗာကို ရှာပါ"
+ }
+ },
+ "server_picker": {
+ "title": "Mastodon ကို အသိုင်းအဝန်းပေါင်းစုံမှ အသုံးပြုသူများဖြင့် ဖွဲ့စည်းထားသည်။",
+ "subtitle": "သင်၏ ဒေသ၊ စိတ်ဝင်စားမှု အပေါ်အခြေခံသော ဆာဗာတစ်ခု ရွေးချယ်ပါ၊ မည်သည့်ဆာဗာကို ရွေးချယ်ထားစေကာမူ အခြားအသုံးပြုသူများနှင့် ပုံမှန်အတိုင်း ဆက်သွယ်နိုင်သည်။",
+ "button": {
+ "category": {
+ "all": "အားလုံး",
+ "all_accessiblity_description": "အမျိုးအစား - အားလုံး",
+ "academia": "ပညာရှင်",
+ "activism": "တက်ကြွလှုပ်ရှားမှု",
+ "food": "အစားအစာ",
+ "furry": "furry",
+ "games": "ဂိမ်း",
+ "general": "အထွေထွေ",
+ "journalism": "သတင်းစာပညာ",
+ "lgbt": "lgbt",
+ "regional": "နယ်မြေဆိုင်ရာ",
+ "art": "အနုပညာ",
+ "music": "ဂီတ",
+ "tech": "နည်းပညာ"
+ },
+ "see_less": "လျှော့ ကြည့်ရန်",
+ "see_more": "ပိုမိုကြည့်ရှုရန်"
+ },
+ "label": {
+ "language": "ဘာသာစကား",
+ "users": "အသုံးပြုသူများ",
+ "category": "အမျိုးအစား"
+ },
+ "input": {
+ "search_servers_or_enter_url": "အသိုင်းအဝိုင်းများကို ရှာဖွေ (သို့) URL ကို ဝင်ရောက်"
+ },
+ "empty_state": {
+ "finding_servers": "အဆင်သင့်သုံးရသော ဆာဗာများကို ရှာနေသည်...",
+ "bad_network": "ဒေတာဖွင့်နေစဉ် တစ်ခုခုမှားယွင်းသွားသည်၊ အင်တာနက်ချိတ်ဆက်မှုကို စစ်ဆေးပါ။",
+ "no_results": "ရလဒ်မရှိပါ"
+ }
+ },
+ "register": {
+ "title": "သင့်ကို %s တွင် စတင်လိုက်ရအောင်",
+ "lets_get_you_set_up_on_domain": "သင့်ကို %s တွင် စတင်လိုက်ရအောင်",
+ "input": {
+ "avatar": {
+ "delete": "ဖျက်"
+ },
+ "username": {
+ "placeholder": "အသုံးပြုသူအမည်",
+ "duplicate_prompt": "ဤအသုံးပြုသူအမည်သည် ရှိနှင့်ပြီးဖြစ်သည်။"
+ },
+ "display_name": {
+ "placeholder": "ပြသမည့် အမည်"
+ },
+ "email": {
+ "placeholder": "အီးမေးလ်"
+ },
+ "password": {
+ "placeholder": "စကားဝှက်",
+ "require": "သင်၏စကားဝှက်သည် အနည်းဆုံးလိုအပ်သည်:",
+ "character_limit": "အက္ခရာ ၈ လုံး",
+ "accessibility": {
+ "checked": "စစ်ဆေးပြီးပြီ",
+ "unchecked": "မစစ်ဆေးခဲ့ပါ"
+ },
+ "hint": "စကားဝှက်သည် အနည်းဆုံး အက္ခရာ ၈ လုံး ရှိရပါမည်။"
+ },
+ "invite": {
+ "registration_user_invite_request": "သင် ဘာကြောင့် ပါဝင်ချင်တာပါလဲ?"
+ }
+ },
+ "error": {
+ "item": {
+ "username": "အသုံးပြုသူအမည်",
+ "email": "အီးမေးလ်",
+ "password": "စကားဝှက်",
+ "agreement": "သဘောတူညီမှု",
+ "locale": "ဒေသဆိုင်ရာ",
+ "reason": "အကြောင်းပြချက်"
+ },
+ "reason": {
+ "blocked": "%s တွင် ခွင့်မပြုထားသော အီးမေးလ်ထောက်ပံ့သူပါဝင်နေသည်။",
+ "unreachable": "%s တည်ရှိပုံ မပေါ်ပါ",
+ "taken": "%s ကို အသုံးပြုနေသူ ရှိနှင့်ပြီးဖြစ်သည်။",
+ "reserved": "%s သည် သီးသန့်ဖယ်ထားသောစကားလုံး ဖြစ်သည်။",
+ "accepted": "%s ကို လက်ခံရမည်ဖြစ်သည်",
+ "blank": "%s ကို လိုအပ်သည်",
+ "invalid": "%s သည် မခိုင်လုံပါ",
+ "too_long": "%s သည် ရှည်လွန်းသည်",
+ "too_short": "%s သည် တိုလွန်းသည်",
+ "inclusion": "%s သည် ထောက်ပံ့ထားသောတန်ဖိုး မဟုတ်ပါ"
+ },
+ "special": {
+ "username_invalid": "အသုံးပြုသူအမည်တွင် ဂဏန်းအက္ခရာစာလုံးနှင့် အောက်မျဉ်း သာလျှင် ပါဝင်နိုင်သည်",
+ "username_too_long": "အသုံးပြုသူအမည်ရှည်လွန်းသည် (စာလုံး ၃၀ လုံးထက် ရှည်၍ မရပါ)",
+ "email_invalid": "ဤအီးမေးလ်လိပ်စာသည် ခိုင်လုံမှု မရှိပါ",
+ "password_too_short": "စကားဝှက်တိုလွန်းသည် (အနည်းဆုံး စာလုံး ၈ လုံး ရှိရမည်)"
+ }
+ }
+ },
+ "server_rules": {
+ "title": "အခြေခံမှုအချို့",
+ "subtitle": "ဤစည်းမျဉ်းများကို ထိန်းညှိသူ %s ယောက်က သတ်မှတ်ကွပ်ကဲသည်။",
+ "prompt": "ဆက်လက်သွားမည်ဆိုပါက သင်သည် %s အတွက် ဝန်ဆောင်မှုနှင့် ကိုယ်ရေးကိုယ်တာမူဝါဒများကို လိုက်နာရမည်ဖြစ်သည်။",
+ "terms_of_service": "ဝန်ဆောင်မှုစည်းကမ်းချက်များ",
+ "privacy_policy": "ကိုယ်ရေးကိုယ်တာမူဝါဒ",
+ "button": {
+ "confirm": "သဘောတူညီသည်"
+ }
+ },
+ "confirm_email": {
+ "title": "နောက််ဆုံးတစ်ခု",
+ "subtitle": "သင့်ကို ပို့လိုက်သောအီးမေးလ်တွင် ပါဝင်သည့်လင့်ခ်ကို နှိပ်ပါ။",
+ "tap_the_link_we_emailed_to_you_to_verify_your_account": "သင့်ကို ပို့လိုက်သောအီးမေးလ်တွင် ပါဝင်သည့်လင့်ခ်ကို နှိပ်ပါ။",
+ "button": {
+ "open_email_app": "အီးမေးလ်ကို ဖွင့်ပါ",
+ "resend": "ပြန်ပို့ပါ"
+ },
+ "dont_receive_email": {
+ "title": "သင့်အီးမေးလ်ကို စစ်ကြည့်ပါ",
+ "description": "Check if your email address is correct as well as your junk folder if you haven’t.",
+ "resend_email": "အီးမေးလ်ကိုပြန်ပို့ပါ"
+ },
+ "open_email_app": {
+ "title": "သင်၏စာဝင်ပုံးကို စစ်ဆေးပါ",
+ "description": "We just sent you an email. Check your junk folder if you haven’t.",
+ "mail": "မေးလ်",
+ "open_email_client": "Open Email Client"
+ }
+ },
+ "home_timeline": {
+ "title": "အိမ်",
+ "navigation_bar_state": {
+ "offline": "အော့ဖ်လိုင်း",
+ "new_posts": "ပို့စ်အသစ်ကြည့်ရန်",
+ "published": "တင်လိုက်ပါပြီ!",
+ "Publishing": "ပို့စ်ကို တင်နေသည်...",
+ "accessibility": {
+ "logo_label": "လိုဂိုခလုတ်",
+ "logo_hint": "Tap to scroll to top and tap again to previous location"
+ }
+ }
+ },
+ "suggestion_account": {
+ "title": "Find People to Follow",
+ "follow_explain": "When you follow someone, you’ll see their posts in your home feed."
+ },
+ "compose": {
+ "title": {
+ "new_post": "ပို့စ်အသစ်",
+ "new_reply": "စာပြန်အသစ်"
+ },
+ "media_selection": {
+ "camera": "ဓါတ်ပုံရိုက်",
+ "photo_library": "ဓာတ်ပုံပြတိုက်",
+ "browse": "ရှာဖွေ"
+ },
+ "content_input_placeholder": "Type or paste what’s on your mind",
+ "compose_action": "ပို့စ်တင်",
+ "replying_to_user": "%s ထံ စာပြန်နေသည်",
+ "attachment": {
+ "photo": "ဓာတ်ပုံ",
+ "video": "ဗီဒီယို",
+ "attachment_broken": "This %s is broken and can’t be\nuploaded to Mastodon.",
+ "description_photo": "Describe the photo for the visually-impaired...",
+ "description_video": "Describe the video for the visually-impaired...",
+ "load_failed": "Load Failed",
+ "upload_failed": "Upload Failed",
+ "can_not_recognize_this_media_attachment": "Can not recognize this media attachment",
+ "attachment_too_large": "Attachment too large",
+ "compressing_state": "Compressing...",
+ "server_processing_state": "Server Processing..."
+ },
+ "poll": {
+ "duration_time": "Duration: %s",
+ "thirty_minutes": "30 minutes",
+ "one_hour": "1 Hour",
+ "six_hours": "6 Hours",
+ "one_day": "1 Day",
+ "three_days": "3 Days",
+ "seven_days": "7 Days",
+ "option_number": "Option %ld",
+ "the_poll_is_invalid": "The poll is invalid",
+ "the_poll_has_empty_option": "The poll has empty option"
+ },
+ "content_warning": {
+ "placeholder": "Write an accurate warning here..."
+ },
+ "visibility": {
+ "public": "Public",
+ "unlisted": "Unlisted",
+ "private": "Followers only",
+ "direct": "Only people I mention"
+ },
+ "auto_complete": {
+ "space_to_add": "Space to add"
+ },
+ "accessibility": {
+ "append_attachment": "Add Attachment",
+ "append_poll": "Add Poll",
+ "remove_poll": "Remove Poll",
+ "custom_emoji_picker": "Custom Emoji Picker",
+ "enable_content_warning": "Enable Content Warning",
+ "disable_content_warning": "Disable Content Warning",
+ "post_visibility_menu": "Post Visibility Menu",
+ "post_options": "Post Options",
+ "posting_as": "Posting as %s"
+ },
+ "keyboard": {
+ "discard_post": "Discard Post",
+ "publish_post": "Publish Post",
+ "toggle_poll": "Toggle Poll",
+ "toggle_content_warning": "Toggle Content Warning",
+ "append_attachment_entry": "Add Attachment - %s",
+ "select_visibility_entry": "မြင်နိုင်စွမ်း ရွေးချယ်ရန် - %s"
+ }
+ },
+ "profile": {
+ "header": {
+ "follows_you": "Follows You"
+ },
+ "dashboard": {
+ "my_posts": "posts",
+ "my_following": "following",
+ "my_followers": "followers",
+ "other_posts": "posts",
+ "other_following": "following",
+ "other_followers": "followers"
+ },
+ "fields": {
+ "joined": "Joined",
+ "add_row": "Add Row",
+ "placeholder": {
+ "label": "Label",
+ "content": "Content"
+ },
+ "verified": {
+ "short": "Verified on %s",
+ "long": "Ownership of this link was checked on %s"
+ }
+ },
+ "segmented_control": {
+ "posts": "Posts",
+ "replies": "Replies",
+ "posts_and_replies": "Posts and Replies",
+ "media": "Media",
+ "about": "About"
+ },
+ "relationship_action_alert": {
+ "confirm_mute_user": {
+ "title": "Mute Account",
+ "message": "Confirm to mute %s"
+ },
+ "confirm_unmute_user": {
+ "title": "Unmute Account",
+ "message": "Confirm to unmute %s"
+ },
+ "confirm_block_user": {
+ "title": "Block Account",
+ "message": "Confirm to block %s"
+ },
+ "confirm_unblock_user": {
+ "title": "Unblock Account",
+ "message": "Confirm to unblock %s"
+ },
+ "confirm_show_reblogs": {
+ "title": "Show Reblogs",
+ "message": "Confirm to show reblogs"
+ },
+ "confirm_hide_reblogs": {
+ "title": "Hide Reblogs",
+ "message": "Confirm to hide reblogs"
+ }
+ },
+ "accessibility": {
+ "show_avatar_image": "Show avatar image",
+ "edit_avatar_image": "Edit avatar image",
+ "show_banner_image": "Show banner image",
+ "double_tap_to_open_the_list": "Double tap to open the list"
+ }
+ },
+ "follower": {
+ "title": "follower",
+ "footer": "Followers from other servers are not displayed."
+ },
+ "following": {
+ "title": "following",
+ "footer": "Follows from other servers are not displayed."
+ },
+ "familiarFollowers": {
+ "title": "Followers you familiar",
+ "followed_by_names": "Followed by %s"
+ },
+ "favorited_by": {
+ "title": "Favorited By"
+ },
+ "reblogged_by": {
+ "title": "Reblogged By"
+ },
+ "search": {
+ "title": "Search",
+ "search_bar": {
+ "placeholder": "Search hashtags and users",
+ "cancel": "Cancel"
+ },
+ "recommend": {
+ "button_text": "See All",
+ "hash_tag": {
+ "title": "Trending on Mastodon",
+ "description": "Hashtags that are getting quite a bit of attention",
+ "people_talking": "%s people are talking"
+ },
+ "accounts": {
+ "title": "Accounts you might like",
+ "description": "You may like to follow these accounts",
+ "follow": "Follow"
+ }
+ },
+ "searching": {
+ "segment": {
+ "all": "All",
+ "people": "People",
+ "hashtags": "Hashtags",
+ "posts": "Posts"
+ },
+ "empty_state": {
+ "no_results": "No results"
+ },
+ "recent_search": "Recent searches",
+ "clear": "Clear"
+ }
+ },
+ "discovery": {
+ "tabs": {
+ "posts": "Posts",
+ "hashtags": "Hashtags",
+ "news": "News",
+ "community": "Community",
+ "for_you": "For You"
+ },
+ "intro": "These are the posts gaining traction in your corner of Mastodon."
+ },
+ "favorite": {
+ "title": "Your Favorites"
+ },
+ "notification": {
+ "title": {
+ "Everything": "Everything",
+ "Mentions": "Mentions"
+ },
+ "notification_description": {
+ "followed_you": "followed you",
+ "favorited_your_post": "favorited your post",
+ "reblogged_your_post": "reblogged your post",
+ "mentioned_you": "mentioned you",
+ "request_to_follow_you": "request to follow you",
+ "poll_has_ended": "poll has ended"
+ },
+ "keyobard": {
+ "show_everything": "Show Everything",
+ "show_mentions": "Show Mentions"
+ },
+ "follow_request": {
+ "accept": "Accept",
+ "accepted": "Accepted",
+ "reject": "reject",
+ "rejected": "Rejected"
+ }
+ },
+ "thread": {
+ "back_title": "Post",
+ "title": "Post from %s"
+ },
+ "settings": {
+ "title": "Settings",
+ "section": {
+ "appearance": {
+ "title": "Appearance",
+ "automatic": "Automatic",
+ "light": "Always Light",
+ "dark": "Always Dark"
+ },
+ "look_and_feel": {
+ "title": "Look and Feel",
+ "use_system": "Use System",
+ "really_dark": "Really Dark",
+ "sorta_dark": "Sorta Dark",
+ "light": "Light"
+ },
+ "notifications": {
+ "title": "Notifications",
+ "favorites": "Favorites my post",
+ "follows": "Follows me",
+ "boosts": "Reblogs my post",
+ "mentions": "Mentions me",
+ "trigger": {
+ "anyone": "anyone",
+ "follower": "a follower",
+ "follow": "anyone I follow",
+ "noone": "no one",
+ "title": "Notify me when"
+ }
+ },
+ "preference": {
+ "title": "Preferences",
+ "true_black_dark_mode": "True black dark mode",
+ "disable_avatar_animation": "Disable animated avatars",
+ "disable_emoji_animation": "Disable animated emojis",
+ "using_default_browser": "Use default browser to open links",
+ "open_links_in_mastodon": "Open links in Mastodon"
+ },
+ "boring_zone": {
+ "title": "The Boring Zone",
+ "account_settings": "Account Settings",
+ "terms": "Terms of Service",
+ "privacy": "Privacy Policy"
+ },
+ "spicy_zone": {
+ "title": "The Spicy Zone",
+ "clear": "Clear Media Cache",
+ "signout": "Sign Out"
+ }
+ },
+ "footer": {
+ "mastodon_description": "Mastodon is open source software. You can report issues on GitHub at %s (%s)"
+ },
+ "keyboard": {
+ "close_settings_window": "Close Settings Window"
+ }
+ },
+ "report": {
+ "title_report": "Report",
+ "title": "Report %s",
+ "step1": "Step 1 of 2",
+ "step2": "Step 2 of 2",
+ "content1": "Are there any other posts you’d like to add to the report?",
+ "content2": "Is there anything the moderators should know about this report?",
+ "report_sent_title": "Thanks for reporting, we’ll look into this.",
+ "send": "Send Report",
+ "skip_to_send": "Send without comment",
+ "text_placeholder": "Type or paste additional comments",
+ "reported": "REPORTED",
+ "step_one": {
+ "step_1_of_4": "Step 1 of 4",
+ "whats_wrong_with_this_post": "What's wrong with this post?",
+ "whats_wrong_with_this_account": "What's wrong with this account?",
+ "whats_wrong_with_this_username": "What's wrong with %s?",
+ "select_the_best_match": "Select the best match",
+ "i_dont_like_it": "I don’t like it",
+ "it_is_not_something_you_want_to_see": "It is not something you want to see",
+ "its_spam": "It’s spam",
+ "malicious_links_fake_engagement_or_repetetive_replies": "Malicious links, fake engagement, or repetetive replies",
+ "it_violates_server_rules": "It violates server rules",
+ "you_are_aware_that_it_breaks_specific_rules": "You are aware that it breaks specific rules",
+ "its_something_else": "It’s something else",
+ "the_issue_does_not_fit_into_other_categories": "The issue does not fit into other categories"
+ },
+ "step_two": {
+ "step_2_of_4": "Step 2 of 4",
+ "which_rules_are_being_violated": "Which rules are being violated?",
+ "select_all_that_apply": "Select all that apply",
+ "i_just_don’t_like_it": "I just don’t like it"
+ },
+ "step_three": {
+ "step_3_of_4": "Step 3 of 4",
+ "are_there_any_posts_that_back_up_this_report": "Are there any posts that back up this report?",
+ "select_all_that_apply": "Select all that apply"
+ },
+ "step_four": {
+ "step_4_of_4": "Step 4 of 4",
+ "is_there_anything_else_we_should_know": "Is there anything else we should know?"
+ },
+ "step_final": {
+ "dont_want_to_see_this": "Don’t want to see this?",
+ "when_you_see_something_you_dont_like_on_mastodon_you_can_remove_the_person_from_your_experience.": "When you see something you don’t like on Mastodon, you can remove the person from your experience.",
+ "unfollow": "Unfollow",
+ "unfollowed": "Unfollowed",
+ "unfollow_user": "Unfollow %s",
+ "mute_user": "Mute %s",
+ "you_wont_see_their_posts_or_reblogs_in_your_home_feed_they_wont_know_they_ve_been_muted": "You won’t see their posts or reblogs in your home feed. They won’t know they’ve been muted.",
+ "block_user": "Block %s",
+ "they_will_no_longer_be_able_to_follow_or_see_your_posts_but_they_can_see_if_theyve_been_blocked": "They will no longer be able to follow or see your posts, but they can see if they’ve been blocked.",
+ "while_we_review_this_you_can_take_action_against_user": "While we review this, you can take action against %s"
+ }
+ },
+ "preview": {
+ "keyboard": {
+ "close_preview": "Close Preview",
+ "show_next": "Show Next",
+ "show_previous": "Show Previous"
+ }
+ },
+ "account_list": {
+ "tab_bar_hint": "Current selected profile: %s. Double tap then hold to show account switcher",
+ "dismiss_account_switcher": "Dismiss Account Switcher",
+ "add_account": "Add Account"
+ },
+ "wizard": {
+ "new_in_mastodon": "New in Mastodon",
+ "multiple_account_switch_intro_description": "Switch between multiple accounts by holding the profile button.",
+ "accessibility_hint": "Double tap to dismiss this wizard"
+ },
+ "bookmark": {
+ "title": "Bookmarks"
+ },
+ "followed_tags": {
+ "title": "Followed Tags",
+ "header": {
+ "posts": "posts",
+ "participants": "participants",
+ "posts_today": "posts today"
+ },
+ "actions": {
+ "follow": "Follow",
+ "unfollow": "Unfollow"
+ }
+ }
+ }
+}
diff --git a/Localization/StringsConvertor/input/my.lproj/ios-infoPlist.json b/Localization/StringsConvertor/input/my.lproj/ios-infoPlist.json
new file mode 100644
index 000000000..b56ff5096
--- /dev/null
+++ b/Localization/StringsConvertor/input/my.lproj/ios-infoPlist.json
@@ -0,0 +1,6 @@
+{
+ "NSCameraUsageDescription": "ပို့စ်အခြေအနေအတွက် ပုံရိုက်ရန် အသုံးပြုခဲ့သည်",
+ "NSPhotoLibraryAddUsageDescription": "ဓာတ်ပုံပြခန်းတွင် ပုံသိမ်းရန် အသုံးပြုခဲ့သည်",
+ "NewPostShortcutItemTitle": "ပို့စ်အသစ်",
+ "SearchShortcutItemTitle": "ရှာဖွေရန်"
+}
diff --git a/Localization/StringsConvertor/input/nl.lproj/Localizable.stringsdict b/Localization/StringsConvertor/input/nl.lproj/Localizable.stringsdict
index 314600ff7..29d0ec841 100644
--- a/Localization/StringsConvertor/input/nl.lproj/Localizable.stringsdict
+++ b/Localization/StringsConvertor/input/nl.lproj/Localizable.stringsdict
@@ -15,7 +15,7 @@
one
1 unread notification
other
- %ld unread notification
+ %ld unread notifications
a11y.plural.count.input_limit_exceeds
@@ -50,6 +50,22 @@
%ld tekens
+ a11y.plural.count.characters_left
+
+ NSStringLocalizedFormatKey
+ %#@character_count@ left
+ character_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 character
+ other
+ %ld characters
+
+
plural.count.followed_by_and_mutual
NSStringLocalizedFormatKey
diff --git a/Localization/StringsConvertor/input/nl.lproj/app.json b/Localization/StringsConvertor/input/nl.lproj/app.json
index e0b2872fb..938f40a07 100644
--- a/Localization/StringsConvertor/input/nl.lproj/app.json
+++ b/Localization/StringsConvertor/input/nl.lproj/app.json
@@ -51,6 +51,11 @@
"clean_cache": {
"title": "Cache-geheugen Wissen",
"message": "Cache-geheugen (%s) succesvol gewist."
+ },
+ "translation_failed": {
+ "title": "Note",
+ "message": "Translation failed. Maybe the administrator has not enabled translations on this server or this server is running an older version of Mastodon where translations are not yet supported.",
+ "button": "OK"
}
},
"controls": {
@@ -74,10 +79,11 @@
"take_photo": "Maak foto",
"save_photo": "Bewaar foto",
"copy_photo": "Kopieer foto",
- "sign_in": "Aanmelden",
- "sign_up": "Registreren",
+ "sign_in": "Inloggen",
+ "sign_up": "Account aanmaken",
"see_more": "Meer",
"preview": "Voorvertoning",
+ "copy": "Copy",
"share": "Deel",
"share_user": "Delen %s",
"share_post": "Deel bericht",
@@ -91,12 +97,16 @@
"block_domain": "Blokkeer %s",
"unblock_domain": "Deblokkeer %s",
"settings": "Instellingen",
- "delete": "Verwijder"
+ "delete": "Verwijder",
+ "translate_post": {
+ "title": "Translate from %s",
+ "unknown_language": "Unknown"
+ }
},
"tabs": {
"home": "Start",
- "search": "Zoek",
- "notification": "Melding",
+ "search_and_explore": "Search and Explore",
+ "notifications": "Notifications",
"profile": "Profiel"
},
"keyboard": {
@@ -132,6 +142,8 @@
"sensitive_content": "Gevoelige inhoud",
"media_content_warning": "Tap hier om te tonen",
"tap_to_reveal": "Tik om te onthullen",
+ "load_embed": "Load Embed",
+ "link_via_user": "%s via %s",
"poll": {
"vote": "Stemmen",
"closed": "Gesloten"
@@ -139,8 +151,8 @@
"meta_entity": {
"url": "Link: %s",
"hashtag": "Hashtag: %s",
- "mention": "Show Profile: %s",
- "email": "Email address: %s"
+ "mention": "Profiel weergeven: %s",
+ "email": "E-mailadres: %s"
},
"actions": {
"reply": "Reageren",
@@ -153,6 +165,7 @@
"show_image": "Toon afbeelding",
"show_gif": "GIF weergeven",
"show_video_player": "Toon videospeler",
+ "share_link_in_post": "Share Link in Post",
"tap_then_hold_to_show_menu": "Tik en houd vast om menu te tonen"
},
"tag": {
@@ -168,6 +181,12 @@
"private": "Alleen hun volgers kunnen dit bericht zien.",
"private_from_me": "Alleen mijn volgers kunnen dit bericht zien.",
"direct": "Alleen de vermelde persoon kan dit bericht zien."
+ },
+ "translation": {
+ "translated_from": "Translated from %s using %s",
+ "unknown_language": "Unknown",
+ "unknown_provider": "Unknown",
+ "show_original": "Shown Original"
}
},
"friendship": {
@@ -187,8 +206,8 @@
"unmute_user": "%s niet langer negeren",
"muted": "Genegeerd",
"edit_info": "Bewerken",
- "show_reblogs": "Show Reblogs",
- "hide_reblogs": "Hide Reblogs"
+ "show_reblogs": "Toon reblogs",
+ "hide_reblogs": "Verberg reblogs"
},
"timeline": {
"filtered": "Gefilterd",
@@ -218,10 +237,16 @@
"get_started": "Aan de slag",
"log_in": "Log in"
},
+ "login": {
+ "title": "Welkom terug",
+ "subtitle": "Log je in op de server waarop je je account hebt aangemaakt.",
+ "server_search_field": {
+ "placeholder": "Voer URL in of zoek naar uw server"
+ }
+ },
"server_picker": {
"title": "Kies een server, welke dan ook.",
- "subtitle": "Kies een gemeenschap gebaseerd op jouw interesses, regio of een algemeen doel.",
- "subtitle_extend": "Kies een gemeenschap gebaseerd op jouw interesses, regio, of een algemeen doel. Elke gemeenschap wordt beheerd door een volledig onafhankelijke organisatie of individu.",
+ "subtitle": "Kies een server gebaseerd op je regio, interesses, of een algemene server. Je kunt nog steeds chatten met iedereen op Mastodon, ongeacht op welke server je zit.",
"button": {
"category": {
"all": "Alles",
@@ -248,8 +273,7 @@
"category": "CATEGORIE"
},
"input": {
- "placeholder": "Zoek uw server of sluit u bij een nieuwe server aan...",
- "search_servers_or_enter_url": "Search servers or enter URL"
+ "search_servers_or_enter_url": "Zoek naar gemeenschappen of voer URL in"
},
"empty_state": {
"finding_servers": "Beschikbare servers zoeken...",
@@ -259,7 +283,7 @@
},
"register": {
"title": "Vertel ons over uzelf.",
- "lets_get_you_set_up_on_domain": "Let’s get you set up on %s",
+ "lets_get_you_set_up_on_domain": "Laten we je account instellen op %s",
"input": {
"avatar": {
"delete": "Verwijderen"
@@ -330,7 +354,7 @@
"confirm_email": {
"title": "Nog één ding.",
"subtitle": "We hebben een e-mail gestuurd naar %s,\nklik op de link om uw account te bevestigen.",
- "tap_the_link_we_emailed_to_you_to_verify_your_account": "Tap the link we emailed to you to verify your account",
+ "tap_the_link_we_emailed_to_you_to_verify_your_account": "Tik op de link in de e-mail die je hebt ontvangen om uw account te verifiëren",
"button": {
"open_email_app": "Email Openen",
"resend": "Verstuur opnieuw"
@@ -355,8 +379,8 @@
"published": "Gepubliceerd!",
"Publishing": "Bericht publiceren...",
"accessibility": {
- "logo_label": "Logo Button",
- "logo_hint": "Tap to scroll to top and tap again to previous location"
+ "logo_label": "Logo knop",
+ "logo_hint": "Tik om naar boven te scrollen en tik nogmaals om terug te keren naar de vorige locatie"
}
}
},
@@ -383,10 +407,12 @@
"attachment_broken": "Deze %s is corrupt en kan niet geüpload worden naar Mastodon.",
"description_photo": "Omschrijf de foto voor mensen met een visuele beperking...",
"description_video": "Omschrijf de video voor mensen met een visuele beperking...",
- "load_failed": "Load Failed",
- "upload_failed": "Upload Failed",
- "can_not_recognize_this_media_attachment": "Can not regonize this media attachment",
- "attachment_too_large": "Attachment too large"
+ "load_failed": "Laden mislukt",
+ "upload_failed": "Uploaden mislukt",
+ "can_not_recognize_this_media_attachment": "Kan de media in de bijlage niet herkennen",
+ "attachment_too_large": "Bijlage te groot",
+ "compressing_state": "Bezig met comprimeren...",
+ "server_processing_state": "Server is bezig met verwerken..."
},
"poll": {
"duration_time": "Duur: %s",
@@ -396,7 +422,9 @@
"one_day": "1 Dag",
"three_days": "3 Dagen",
"seven_days": "7 Dagen",
- "option_number": "Optie %ld"
+ "option_number": "Optie %ld",
+ "the_poll_is_invalid": "De peiling is ongeldig",
+ "the_poll_has_empty_option": "De peiling heeft een lege optie"
},
"content_warning": {
"placeholder": "Schrijf hier een nauwkeurige waarschuwing..."
@@ -417,7 +445,9 @@
"custom_emoji_picker": "Eigen Emojikiezer",
"enable_content_warning": "Inhoudswaarschuwing inschakelen",
"disable_content_warning": "Inhoudswaarschuwing Uitschakelen",
- "post_visibility_menu": "Berichtzichtbaarheidsmenu"
+ "post_visibility_menu": "Berichtzichtbaarheidsmenu",
+ "post_options": "Plaats Bericht Opties",
+ "posting_as": "Plaats bericht als %s"
},
"keyboard": {
"discard_post": "Bericht Verwijderen",
@@ -430,18 +460,26 @@
},
"profile": {
"header": {
- "follows_you": "Follows You"
+ "follows_you": "Volgt jou"
},
"dashboard": {
- "posts": "berichten",
- "following": "volgend",
- "followers": "volgers"
+ "my_posts": "posts",
+ "my_following": "following",
+ "my_followers": "followers",
+ "other_posts": "posts",
+ "other_following": "following",
+ "other_followers": "followers"
},
"fields": {
+ "joined": "Joined",
"add_row": "Rij Toevoegen",
"placeholder": {
"label": "Etiket",
"content": "Inhoud"
+ },
+ "verified": {
+ "short": "Geverifieerd op %s",
+ "long": "Eigendom van deze link is gecontroleerd op %s"
}
},
"segmented_control": {
@@ -469,12 +507,12 @@
"message": "Bevestig om %s te deblokkeren"
},
"confirm_show_reblogs": {
- "title": "Show Reblogs",
- "message": "Confirm to show reblogs"
+ "title": "Toon reblogs",
+ "message": "Bevestig om reblogs te tonen"
},
"confirm_hide_reblogs": {
- "title": "Hide Reblogs",
- "message": "Confirm to hide reblogs"
+ "title": "Verberg reblogs",
+ "message": "Bevestig om reblogs te verbergen"
}
},
"accessibility": {
@@ -485,16 +523,16 @@
}
},
"follower": {
- "title": "follower",
+ "title": "volger",
"footer": "Volgers van andere servers worden niet weergegeven."
},
"following": {
- "title": "following",
+ "title": "volgend",
"footer": "Volgers van andere servers worden niet weergegeven."
},
"familiarFollowers": {
- "title": "Followers you familiar",
- "followed_by_names": "Followed by %s"
+ "title": "Volgers die je kent",
+ "followed_by_names": "Gevolgd door %s"
},
"favorited_by": {
"title": "Favorited By"
@@ -540,7 +578,7 @@
"posts": "Berichten",
"hashtags": "Hashtags",
"news": "Nieuws",
- "community": "Community",
+ "community": "Gemeenschap",
"for_you": "Voor jou"
},
"intro": "Dit zijn de berichten die populair zijn in jouw Mastodon-kringen."
@@ -566,10 +604,10 @@
"show_mentions": "Vermeldingen weergeven"
},
"follow_request": {
- "accept": "Accept",
- "accepted": "Accepted",
- "reject": "reject",
- "rejected": "Rejected"
+ "accept": "Accepteren",
+ "accepted": "Geaccepteerd",
+ "reject": "afwijzen",
+ "rejected": "Afgewezen"
}
},
"thread": {
@@ -646,11 +684,11 @@
"text_placeholder": "Schrijf of plak aanvullende opmerkingen",
"reported": "Gerapporteerd",
"step_one": {
- "step_1_of_4": "Step 1 of 4",
- "whats_wrong_with_this_post": "What's wrong with this post?",
- "whats_wrong_with_this_account": "What's wrong with this account?",
- "whats_wrong_with_this_username": "What's wrong with %s?",
- "select_the_best_match": "Select the best match",
+ "step_1_of_4": "Stap 1 van 4",
+ "whats_wrong_with_this_post": "Wat is er mis met dit bericht?",
+ "whats_wrong_with_this_account": "Wat is er mis met dit bericht?",
+ "whats_wrong_with_this_username": "Wat is er mis met %s?",
+ "select_the_best_match": "Selecteer de beste overeenkomst",
"i_dont_like_it": "I don’t like it",
"it_is_not_something_you_want_to_see": "It is not something you want to see",
"its_spam": "It’s spam",
@@ -707,6 +745,18 @@
},
"bookmark": {
"title": "Bookmarks"
+ },
+ "followed_tags": {
+ "title": "Followed Tags",
+ "header": {
+ "posts": "posts",
+ "participants": "participants",
+ "posts_today": "posts today"
+ },
+ "actions": {
+ "follow": "Follow",
+ "unfollow": "Unfollow"
+ }
}
}
}
diff --git a/Localization/StringsConvertor/input/pt-BR.lproj/Localizable.stringsdict b/Localization/StringsConvertor/input/pt-BR.lproj/Localizable.stringsdict
index ba1532740..02fbf2d20 100644
--- a/Localization/StringsConvertor/input/pt-BR.lproj/Localizable.stringsdict
+++ b/Localization/StringsConvertor/input/pt-BR.lproj/Localizable.stringsdict
@@ -50,6 +50,22 @@
%ld caracteres
+ a11y.plural.count.characters_left
+
+ NSStringLocalizedFormatKey
+ %#@character_count@ restantes
+ character_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 carácter
+ other
+ %ld carácteres
+
+
plural.count.followed_by_and_mutual
NSStringLocalizedFormatKey
@@ -72,9 +88,9 @@
NSStringFormatValueTypeKey
ld
one
- Followed by %1$@, and another mutual
+ Seguido por %1$@, e outro em comum
other
- Followed by %1$@, and %ld mutuals
+ Seguido por %1$@, e %ld em comum
plural.count.metric_formatted.post
@@ -104,9 +120,9 @@
NSStringFormatValueTypeKey
ld
one
- 1 media
+ 1 mídia
other
- %ld media
+ %ld mídias
plural.count.post
diff --git a/Localization/StringsConvertor/input/pt-BR.lproj/app.json b/Localization/StringsConvertor/input/pt-BR.lproj/app.json
index 26e6edb76..e680db924 100644
--- a/Localization/StringsConvertor/input/pt-BR.lproj/app.json
+++ b/Localization/StringsConvertor/input/pt-BR.lproj/app.json
@@ -2,7 +2,7 @@
"common": {
"alerts": {
"common": {
- "please_try_again": "Por favor tente novamente.",
+ "please_try_again": "Por favor, tente novamente.",
"please_try_again_later": "Tente novamente mais tarde."
},
"sign_up_failure": {
@@ -51,6 +51,11 @@
"clean_cache": {
"title": "Limpar Cache",
"message": "%s do cache removidos com sucesso."
+ },
+ "translation_failed": {
+ "title": "Note",
+ "message": "Translation failed. Maybe the administrator has not enabled translations on this server or this server is running an older version of Mastodon where translations are not yet supported.",
+ "button": "OK"
}
},
"controls": {
@@ -78,6 +83,7 @@
"sign_up": "Criar conta",
"see_more": "Ver mais",
"preview": "Pré-visualização",
+ "copy": "Copy",
"share": "Compartilhar",
"share_user": "Compartilhar %s",
"share_post": "Compartilhar postagem",
@@ -91,12 +97,16 @@
"block_domain": "Bloquear %s",
"unblock_domain": "Desbloquear %s",
"settings": "Configurações",
- "delete": "Excluir"
+ "delete": "Excluir",
+ "translate_post": {
+ "title": "Translate from %s",
+ "unknown_language": "Unknown"
+ }
},
"tabs": {
"home": "Início",
- "search": "Buscar",
- "notification": "Notificação",
+ "search_and_explore": "Search and Explore",
+ "notifications": "Notifications",
"profile": "Perfil"
},
"keyboard": {
@@ -132,6 +142,8 @@
"sensitive_content": "Conteúdo sensível",
"media_content_warning": "Toque em qualquer lugar para revelar",
"tap_to_reveal": "Toque para revelar",
+ "load_embed": "Load Embed",
+ "link_via_user": "%s via %s",
"poll": {
"vote": "Votar",
"closed": "Fechado"
@@ -153,6 +165,7 @@
"show_image": "Exibir imagem",
"show_gif": "Exibir GIF",
"show_video_player": "Mostrar reprodutor de vídeo",
+ "share_link_in_post": "Share Link in Post",
"tap_then_hold_to_show_menu": "Toque e em seguida segure para exibir o menu"
},
"tag": {
@@ -164,10 +177,16 @@
"emoji": "Emoji"
},
"visibility": {
- "unlisted": "Everyone can see this post but not display in the public timeline.",
+ "unlisted": "Todos podem ver esta postagem, mas não são exibidos na linha do tempo pública.",
"private": "Somente seus seguidores podem ver essa postagem.",
"private_from_me": "Somente meus seguidores podem ver essa postagem.",
"direct": "Somente o usuário mencionado pode ver essa postagem."
+ },
+ "translation": {
+ "translated_from": "Translated from %s using %s",
+ "unknown_language": "Unknown",
+ "unknown_provider": "Unknown",
+ "show_original": "Shown Original"
}
},
"friendship": {
@@ -202,8 +221,8 @@
},
"header": {
"no_status_found": "Nenhuma postagem encontrada",
- "blocking_warning": "You can’t view this user's profile\nuntil you unblock them.\nYour profile looks like this to them.",
- "user_blocking_warning": "You can’t view %s’s profile\nuntil you unblock them.\nYour profile looks like this to them.",
+ "blocking_warning": "Você não pode ver o perfil deste usuário até desbloqueá-lo.\nSeu perfil aparece assim para esse usuário.",
+ "user_blocking_warning": "Você não pode ver o perfil de %s até desbloqueá-lo.\nSeu perfil aparece assim para esse usuário.",
"blocked_warning": "Você não pode ver o perfil desse usuário até que ele o desbloqueie.",
"user_blocked_warning": "Você não pode ver o perfil de %s até que ele o desbloqueie.",
"suspended_warning": "Esse usuário foi suspenso.",
@@ -218,10 +237,16 @@
"get_started": "Comece já",
"log_in": "Entrar"
},
+ "login": {
+ "title": "Bem-vindo de volta",
+ "subtitle": "Logado na instância em que você criou a sua conta.",
+ "server_search_field": {
+ "placeholder": "Insira a URL ou procure pela sua instância"
+ }
+ },
"server_picker": {
"title": "Mastodon é feito de usuários em instâncias diferentes.",
- "subtitle": "Escolha uma instância baseada nos seus interesses, região, ou em uma proposta geral.",
- "subtitle_extend": "Escolha uma instância baseada nos seus interesses, região, ou em uma proposta geral. Cada instância é operada por um indivíduo ou uma organização totalmente independente.",
+ "subtitle": "Escolha uma instância baseada na sua região, interesses, ou uma de uso geral. Você ainda poderá conversar com qualquer um no Mastodon, independente da instância.",
"button": {
"category": {
"all": "Todos",
@@ -248,8 +273,7 @@
"category": "Categoria"
},
"input": {
- "placeholder": "Procurar instâncias",
- "search_servers_or_enter_url": "Procurar instâncias ou inserir URL"
+ "search_servers_or_enter_url": "Procurar comunidades ou inserir URL"
},
"empty_state": {
"finding_servers": "Procurando instâncias disponíveis...",
@@ -259,7 +283,7 @@
},
"register": {
"title": "Vamos configurar você em %s",
- "lets_get_you_set_up_on_domain": "Let’s get you set up on %s",
+ "lets_get_you_set_up_on_domain": "Vamos configurar você em %s",
"input": {
"avatar": {
"delete": "Excluir"
@@ -279,13 +303,13 @@
"require": "Sua senha deve ter pelo menos:",
"character_limit": "8 carácteres",
"accessibility": {
- "checked": "checked",
- "unchecked": "unchecked"
+ "checked": "marcado",
+ "unchecked": "desmarcado"
},
"hint": "Sua senha precisa ter pelo menos oito carácteres"
},
"invite": {
- "registration_user_invite_request": "Why do you want to join?"
+ "registration_user_invite_request": "Por que você deseja se inscrever?"
}
},
"error": {
@@ -298,80 +322,80 @@
"reason": "Motivo"
},
"reason": {
- "blocked": "%s contains a disallowed email provider",
+ "blocked": "%s contém um provedor de e-mail não permitido",
"unreachable": "%s parece não existir",
"taken": "%s já está em uso",
- "reserved": "%s is a reserved keyword",
- "accepted": "%s must be accepted",
- "blank": "%s is required",
- "invalid": "%s is invalid",
- "too_long": "%s is too long",
- "too_short": "%s is too short",
- "inclusion": "%s is not a supported value"
+ "reserved": "%s é uma palavra-chave reservada",
+ "accepted": "%s deve ser aceite",
+ "blank": "%s é obrigatório",
+ "invalid": "%s é inválido",
+ "too_long": "%s é muito longo",
+ "too_short": "%s é muito curto",
+ "inclusion": "%s não é um valor suportado"
},
"special": {
- "username_invalid": "Username must only contain alphanumeric characters and underscores",
- "username_too_long": "Username is too long (can’t be longer than 30 characters)",
- "email_invalid": "This is not a valid email address",
- "password_too_short": "Password is too short (must be at least 8 characters)"
+ "username_invalid": "O nome de usuário só pode conter caracteres alfanuméricos e underlines (_)",
+ "username_too_long": "Nome de usuário é muito longo (não pode ter mais de 30 caracteres)",
+ "email_invalid": "Este não é um endereço de e-mail válido",
+ "password_too_short": "A senha é muito curta (deve ter pelo menos 8 caracteres)"
}
}
},
"server_rules": {
- "title": "Some ground rules.",
- "subtitle": "These are set and enforced by the %s moderators.",
- "prompt": "By continuing, you’re subject to the terms of service and privacy policy for %s.",
- "terms_of_service": "terms of service",
- "privacy_policy": "privacy policy",
+ "title": "Algumas regras básicas.",
+ "subtitle": "Estes são definidos e aplicados pelos moderadores da %s.",
+ "prompt": "Ao continuar, você estará sujeito aos termos de serviço e política de privacidade para %s.",
+ "terms_of_service": "termos de serviço",
+ "privacy_policy": "política de privacidade",
"button": {
- "confirm": "I Agree"
+ "confirm": "Eu concordo"
}
},
"confirm_email": {
- "title": "One last thing.",
- "subtitle": "Tap the link we emailed to you to verify your account.",
- "tap_the_link_we_emailed_to_you_to_verify_your_account": "Tap the link we emailed to you to verify your account",
+ "title": "Uma última coisa.",
+ "subtitle": "Clique no link que te enviamos por e-mail para verificar a sua conta.",
+ "tap_the_link_we_emailed_to_you_to_verify_your_account": "Clique no link que te enviamos por e-mail para verificar a sua conta",
"button": {
- "open_email_app": "Open Email App",
- "resend": "Resend"
+ "open_email_app": "Abrir aplicativo de e-mail",
+ "resend": "Reenviar"
},
"dont_receive_email": {
- "title": "Check your email",
- "description": "Check if your email address is correct as well as your junk folder if you haven’t.",
- "resend_email": "Resend Email"
+ "title": "Verifique o seu e-mail",
+ "description": "Verifique se o seu endereço de e-mail está correto, e também a sua pasta de spam caso não tenha verificado.",
+ "resend_email": "Reenviar e-mail"
},
"open_email_app": {
- "title": "Check your inbox.",
- "description": "We just sent you an email. Check your junk folder if you haven’t.",
- "mail": "Mail",
- "open_email_client": "Open Email Client"
+ "title": "Verifique sua caixa de entrada.",
+ "description": "Enviamos um e-mail para você. Verifique sua pasta de spam caso ainda tenha verificado.",
+ "mail": "Correio",
+ "open_email_client": "Abrir Cliente de Email"
}
},
"home_timeline": {
- "title": "Home",
+ "title": "Início",
"navigation_bar_state": {
- "offline": "Offline",
- "new_posts": "See new posts",
- "published": "Published!",
- "Publishing": "Publishing post...",
+ "offline": "Desconectado",
+ "new_posts": "Ver novas postagens",
+ "published": "Publicado!",
+ "Publishing": "Publicando toot...",
"accessibility": {
- "logo_label": "Logo Button",
- "logo_hint": "Tap to scroll to top and tap again to previous location"
+ "logo_label": "Botão do logotipo",
+ "logo_hint": "Toque para rolar para o topo e toque novamente para a localização anterior"
}
}
},
"suggestion_account": {
- "title": "Find People to Follow",
- "follow_explain": "When you follow someone, you’ll see their posts in your home feed."
+ "title": "Encontre pessoas para seguir",
+ "follow_explain": "Ao seguir alguém, você verá as publicações dessa pessoa na sua página inicial."
},
"compose": {
"title": {
- "new_post": "New Post",
- "new_reply": "New Reply"
+ "new_post": "Novo toot",
+ "new_reply": "Nova resposta"
},
"media_selection": {
- "camera": "Take Photo",
- "photo_library": "Photo Library",
+ "camera": "Tirar foto",
+ "photo_library": "Galeria",
"browse": "Navegar"
},
"content_input_placeholder": "Digite ou cole o que está na sua mente",
@@ -380,13 +404,15 @@
"attachment": {
"photo": "foto",
"video": "vídeo",
- "attachment_broken": "This %s is broken and can’t be\nuploaded to Mastodon.",
- "description_photo": "Describe the photo for the visually-impaired...",
- "description_video": "Describe the video for the visually-impaired...",
- "load_failed": "Load Failed",
- "upload_failed": "Upload Failed",
- "can_not_recognize_this_media_attachment": "Can not regonize this media attachment",
- "attachment_too_large": "Attachment too large"
+ "attachment_broken": "Este %s está quebrado e não pode ser\nenviado para o Mastodon.",
+ "description_photo": "Descreva a foto para deficientes visuais...",
+ "description_video": "Descreva o vídeo para os deficientes visuais...",
+ "load_failed": "Falha ao carregar",
+ "upload_failed": "Falha no carregamento",
+ "can_not_recognize_this_media_attachment": "Não é possível reconhecer este anexo de mídia",
+ "attachment_too_large": "O anexo é muito grande",
+ "compressing_state": "Compactando...",
+ "server_processing_state": "Servidor processando..."
},
"poll": {
"duration_time": "Duração: %s",
@@ -396,128 +422,140 @@
"one_day": "1 dia",
"three_days": "3 dias",
"seven_days": "7 dias",
- "option_number": "Opção %ld"
+ "option_number": "Opção %ld",
+ "the_poll_is_invalid": "A enquete é inválida",
+ "the_poll_has_empty_option": "A enquete tem uma opção vazia"
},
"content_warning": {
- "placeholder": "Write an accurate warning here..."
+ "placeholder": "Escreva um aviso de conteúdo preciso aqui..."
},
"visibility": {
- "public": "Public",
- "unlisted": "Unlisted",
- "private": "Followers only",
- "direct": "Only people I mention"
+ "public": "Público",
+ "unlisted": "Não listado",
+ "private": "Apenas seguidores",
+ "direct": "Apenas pessoas que menciono"
},
"auto_complete": {
- "space_to_add": "Space to add"
+ "space_to_add": "Espaço a adicionar"
},
"accessibility": {
- "append_attachment": "Add Attachment",
- "append_poll": "Add Poll",
- "remove_poll": "Remove Poll",
- "custom_emoji_picker": "Custom Emoji Picker",
- "enable_content_warning": "Enable Content Warning",
- "disable_content_warning": "Disable Content Warning",
- "post_visibility_menu": "Post Visibility Menu"
+ "append_attachment": "Adicionar anexo",
+ "append_poll": "Adicionar enquete",
+ "remove_poll": "Remover enquete",
+ "custom_emoji_picker": "Seletor de emoji personalizado",
+ "enable_content_warning": "Ativar Aviso de Conteúdo",
+ "disable_content_warning": "Desativar Aviso de Conteúdo",
+ "post_visibility_menu": "Menu de Visibilidade do Post",
+ "post_options": "Opções de postagem",
+ "posting_as": "Publicando como %s"
},
"keyboard": {
- "discard_post": "Discard Post",
- "publish_post": "Publish Post",
- "toggle_poll": "Toggle Poll",
- "toggle_content_warning": "Toggle Content Warning",
- "append_attachment_entry": "Add Attachment - %s",
- "select_visibility_entry": "Select Visibility - %s"
+ "discard_post": "Descartar postagem",
+ "publish_post": "Publicar postagem",
+ "toggle_poll": "Alternar enquete",
+ "toggle_content_warning": "Ativar/desativar Aviso de Conteúdo",
+ "append_attachment_entry": "Adicionar Anexo - %s",
+ "select_visibility_entry": "Selecionar Visibilidade - %s"
}
},
"profile": {
"header": {
- "follows_you": "Follows You"
+ "follows_you": "Segue você"
},
"dashboard": {
- "posts": "toots",
- "following": "seguindo",
- "followers": "seguidores"
+ "my_posts": "posts",
+ "my_following": "following",
+ "my_followers": "followers",
+ "other_posts": "posts",
+ "other_following": "following",
+ "other_followers": "followers"
},
"fields": {
- "add_row": "Add Row",
+ "joined": "Joined",
+ "add_row": "Adicionar linha",
"placeholder": {
- "label": "Label",
- "content": "Content"
+ "label": "Descrição",
+ "content": "Conteúdo"
+ },
+ "verified": {
+ "short": "Verificado em %s",
+ "long": "O link foi verificado em %s"
}
},
"segmented_control": {
- "posts": "Posts",
- "replies": "Replies",
- "posts_and_replies": "Posts and Replies",
- "media": "Media",
- "about": "About"
+ "posts": "Toots",
+ "replies": "Respostas",
+ "posts_and_replies": "Toots e respostas",
+ "media": "Mídia",
+ "about": "Sobre"
},
"relationship_action_alert": {
"confirm_mute_user": {
- "title": "Mute Account",
- "message": "Confirm to mute %s"
+ "title": "Silenciar conta",
+ "message": "Confirme para silenciar %s"
},
"confirm_unmute_user": {
- "title": "Unmute Account",
- "message": "Confirm to unmute %s"
+ "title": "Tirar conta do silenciado",
+ "message": "Confirme para tirar %s do silenciado"
},
"confirm_block_user": {
- "title": "Block Account",
- "message": "Confirm to block %s"
+ "title": "Bloquear conta",
+ "message": "Confirme para bloquear %s"
},
"confirm_unblock_user": {
- "title": "Unblock Account",
- "message": "Confirm to unblock %s"
+ "title": "Desbloquear conta",
+ "message": "Confirme para desbloquear %s"
},
"confirm_show_reblogs": {
- "title": "Show Reblogs",
- "message": "Confirm to show reblogs"
+ "title": "Mostrar reblogs",
+ "message": "Confirmar para mostrar reblogs"
},
"confirm_hide_reblogs": {
- "title": "Hide Reblogs",
- "message": "Confirm to hide reblogs"
+ "title": "Ocultar reblogs",
+ "message": "Confirmar para ocultar reblogs"
}
},
"accessibility": {
- "show_avatar_image": "Show avatar image",
- "edit_avatar_image": "Edit avatar image",
- "show_banner_image": "Show banner image",
- "double_tap_to_open_the_list": "Double tap to open the list"
+ "show_avatar_image": "Mostrar foto de perfil",
+ "edit_avatar_image": "Editar foto de perfil",
+ "show_banner_image": "Mostrar foto de capa",
+ "double_tap_to_open_the_list": "Toque duas vezes para abrir a lista"
}
},
"follower": {
- "title": "follower",
- "footer": "Followers from other servers are not displayed."
+ "title": "seguidor",
+ "footer": "Seguidores de outras instâncias não são exibidos."
},
"following": {
- "title": "following",
- "footer": "Follows from other servers are not displayed."
+ "title": "seguindo",
+ "footer": "Contas que você segue de outras instâncias não são exibidas."
},
"familiarFollowers": {
- "title": "Followers you familiar",
- "followed_by_names": "Followed by %s"
+ "title": "Seguidores que você conhece",
+ "followed_by_names": "Seguido por %s"
},
"favorited_by": {
- "title": "Favorited By"
+ "title": "Favoritado por"
},
"reblogged_by": {
- "title": "Reblogged By"
+ "title": "Reblogado por"
},
"search": {
- "title": "Search",
+ "title": "Buscar",
"search_bar": {
- "placeholder": "Search hashtags and users",
- "cancel": "Cancel"
+ "placeholder": "Buscar hashtags e usuários",
+ "cancel": "Cancelar"
},
"recommend": {
- "button_text": "See All",
+ "button_text": "Ver tudo",
"hash_tag": {
- "title": "Trending on Mastodon",
- "description": "Hashtags that are getting quite a bit of attention",
- "people_talking": "%s people are talking"
+ "title": "Em tendência no Mastodon",
+ "description": "Hashtags que estão recebendo bastante atenção",
+ "people_talking": "%s pessoas estão falando"
},
"accounts": {
- "title": "Accounts you might like",
- "description": "You may like to follow these accounts",
+ "title": "Contas que você deve gostar",
+ "description": "Você pode gostar de seguir estas contas",
"follow": "Seguir"
}
},
@@ -543,170 +581,182 @@
"community": "Comunidade",
"for_you": "Para você"
},
- "intro": "These are the posts gaining traction in your corner of Mastodon."
+ "intro": "Esses são os posts que estão ganhando força no seu canto do Mastodon."
},
"favorite": {
- "title": "Your Favorites"
+ "title": "Seus favoritos"
},
"notification": {
"title": {
- "Everything": "Everything",
- "Mentions": "Mentions"
+ "Everything": "Tudo",
+ "Mentions": "Menções"
},
"notification_description": {
- "followed_you": "followed you",
- "favorited_your_post": "favorited your post",
- "reblogged_your_post": "reblogged your post",
- "mentioned_you": "mentioned you",
- "request_to_follow_you": "request to follow you",
- "poll_has_ended": "poll has ended"
+ "followed_you": "seguiu você",
+ "favorited_your_post": "favoritou seu toot",
+ "reblogged_your_post": "reblogou seu toot",
+ "mentioned_you": "te mencionou",
+ "request_to_follow_you": "solicitação para te seguir",
+ "poll_has_ended": "enquete encerrada"
},
"keyobard": {
- "show_everything": "Show Everything",
- "show_mentions": "Show Mentions"
+ "show_everything": "Mostrar tudo",
+ "show_mentions": "Mostrar menções"
},
"follow_request": {
- "accept": "Accept",
- "accepted": "Accepted",
- "reject": "reject",
- "rejected": "Rejected"
+ "accept": "Aceitar",
+ "accepted": "Aceito",
+ "reject": "rejeitar",
+ "rejected": "Rejeitado"
}
},
"thread": {
- "back_title": "Post",
- "title": "Post from %s"
+ "back_title": "Toot",
+ "title": "Publicação de %s"
},
"settings": {
- "title": "Settings",
+ "title": "Configurações",
"section": {
"appearance": {
- "title": "Appearance",
- "automatic": "Automatic",
- "light": "Always Light",
- "dark": "Always Dark"
+ "title": "Aparência",
+ "automatic": "Automático",
+ "light": "Sempre Claro",
+ "dark": "Sempre Escuro"
},
"look_and_feel": {
- "title": "Look and Feel",
- "use_system": "Use System",
- "really_dark": "Really Dark",
- "sorta_dark": "Sorta Dark",
- "light": "Light"
+ "title": "Aparência e Comportamento",
+ "use_system": "Usar configuração do sistema",
+ "really_dark": "Bem escuro",
+ "sorta_dark": "Meio escuro",
+ "light": "Claro"
},
"notifications": {
- "title": "Notifications",
- "favorites": "Favorites my post",
- "follows": "Follows me",
- "boosts": "Reblogs my post",
- "mentions": "Mentions me",
+ "title": "Notificações",
+ "favorites": "Favoritaram minha publicação",
+ "follows": "Me segue",
+ "boosts": "Rebloga minha publicação",
+ "mentions": "Me menciona",
"trigger": {
- "anyone": "anyone",
- "follower": "a follower",
- "follow": "anyone I follow",
- "noone": "no one",
- "title": "Notify me when"
+ "anyone": "qualquer pessoa",
+ "follower": "um seguidor",
+ "follow": "qualquer um que eu siga",
+ "noone": "ninguém",
+ "title": "Me notificar quando"
}
},
"preference": {
- "title": "Preferences",
- "true_black_dark_mode": "True black dark mode",
- "disable_avatar_animation": "Disable animated avatars",
- "disable_emoji_animation": "Disable animated emojis",
- "using_default_browser": "Use default browser to open links",
- "open_links_in_mastodon": "Open links in Mastodon"
+ "title": "Preferências",
+ "true_black_dark_mode": "Modo preto",
+ "disable_avatar_animation": "Desativar fotos animadas",
+ "disable_emoji_animation": "Desativar emojis animados",
+ "using_default_browser": "Usar o navegador padrão pra abrir links",
+ "open_links_in_mastodon": "Abrir links no Mastodon"
},
"boring_zone": {
- "title": "The Boring Zone",
- "account_settings": "Account Settings",
- "terms": "Terms of Service",
- "privacy": "Privacy Policy"
+ "title": "A zona chata",
+ "account_settings": "Configurações da conta",
+ "terms": "Termos de serviço",
+ "privacy": "Política de privacidade"
},
"spicy_zone": {
- "title": "The Spicy Zone",
- "clear": "Clear Media Cache",
- "signout": "Sign Out"
+ "title": "A zona apimentada",
+ "clear": "Limpar cachê de mídia",
+ "signout": "Sair"
}
},
"footer": {
- "mastodon_description": "Mastodon is open source software. You can report issues on GitHub at %s (%s)"
+ "mastodon_description": "Mastodon é um software de código aberto. Você pode reportar problemas no GitHub em %s (%s)"
},
"keyboard": {
- "close_settings_window": "Close Settings Window"
+ "close_settings_window": "Fechar janela de configurações"
}
},
"report": {
- "title_report": "Report",
- "title": "Report %s",
- "step1": "Step 1 of 2",
- "step2": "Step 2 of 2",
- "content1": "Are there any other posts you’d like to add to the report?",
- "content2": "Is there anything the moderators should know about this report?",
- "report_sent_title": "Thanks for reporting, we’ll look into this.",
- "send": "Send Report",
- "skip_to_send": "Send without comment",
- "text_placeholder": "Type or paste additional comments",
- "reported": "REPORTED",
+ "title_report": "Denunciar",
+ "title": "Denunciar %s",
+ "step1": "Passo 1 de 2",
+ "step2": "Passo 2 de 2",
+ "content1": "Há outras postagens que você gostaria de adicionar na denúncia?",
+ "content2": "Há algo que os moderadores deveriam saber sobre esta denúncia?",
+ "report_sent_title": "Obrigado por denunciar, iremos analisar.",
+ "send": "Enviar denúncia",
+ "skip_to_send": "Enviar sem comentário",
+ "text_placeholder": "Digite ou cole comentários adicionais",
+ "reported": "DENUNCIADO",
"step_one": {
- "step_1_of_4": "Step 1 of 4",
- "whats_wrong_with_this_post": "What's wrong with this post?",
- "whats_wrong_with_this_account": "What's wrong with this account?",
- "whats_wrong_with_this_username": "What's wrong with %s?",
- "select_the_best_match": "Select the best match",
- "i_dont_like_it": "I don’t like it",
- "it_is_not_something_you_want_to_see": "It is not something you want to see",
- "its_spam": "It’s spam",
- "malicious_links_fake_engagement_or_repetetive_replies": "Malicious links, fake engagement, or repetetive replies",
- "it_violates_server_rules": "It violates server rules",
- "you_are_aware_that_it_breaks_specific_rules": "You are aware that it breaks specific rules",
- "its_something_else": "It’s something else",
- "the_issue_does_not_fit_into_other_categories": "The issue does not fit into other categories"
+ "step_1_of_4": "Passo 1 de 4",
+ "whats_wrong_with_this_post": "O que há de errado com essa publicação?",
+ "whats_wrong_with_this_account": "O que há de errado com essa conta?",
+ "whats_wrong_with_this_username": "O que há de errado com %s?",
+ "select_the_best_match": "Selecione a melhor alternativa",
+ "i_dont_like_it": "Eu não gosto disso",
+ "it_is_not_something_you_want_to_see": "Não é algo que você gostaria de ver",
+ "its_spam": "É spam",
+ "malicious_links_fake_engagement_or_repetetive_replies": "Links maliciosos, engajamento falso, ou respostas repetitivas",
+ "it_violates_server_rules": "Isso viola as regras do servidor",
+ "you_are_aware_that_it_breaks_specific_rules": "Você está ciente que isso quebra regras específicas",
+ "its_something_else": "É outra coisa",
+ "the_issue_does_not_fit_into_other_categories": "O problema não se encaixa em outras categorias"
},
"step_two": {
- "step_2_of_4": "Step 2 of 4",
- "which_rules_are_being_violated": "Which rules are being violated?",
- "select_all_that_apply": "Select all that apply",
- "i_just_don’t_like_it": "I just don’t like it"
+ "step_2_of_4": "Passo 2 de 4",
+ "which_rules_are_being_violated": "Quais regras estão sendo violadas?",
+ "select_all_that_apply": "Selecione todas que se aplicam",
+ "i_just_don’t_like_it": "Simplesmente não gosto"
},
"step_three": {
- "step_3_of_4": "Step 3 of 4",
- "are_there_any_posts_that_back_up_this_report": "Are there any posts that back up this report?",
- "select_all_that_apply": "Select all that apply"
+ "step_3_of_4": "Passo 3 de 4",
+ "are_there_any_posts_that_back_up_this_report": "Existem postagens que apoiam essa denúncia?",
+ "select_all_that_apply": "Selecione todos que se aplicam"
},
"step_four": {
- "step_4_of_4": "Step 4 of 4",
- "is_there_anything_else_we_should_know": "Is there anything else we should know?"
+ "step_4_of_4": "Passo 4 de 4",
+ "is_there_anything_else_we_should_know": "Há algo a mais que deveríamos saber?"
},
"step_final": {
- "dont_want_to_see_this": "Don’t want to see this?",
- "when_you_see_something_you_dont_like_on_mastodon_you_can_remove_the_person_from_your_experience.": "When you see something you don’t like on Mastodon, you can remove the person from your experience.",
- "unfollow": "Unfollow",
- "unfollowed": "Unfollowed",
- "unfollow_user": "Unfollow %s",
- "mute_user": "Mute %s",
- "you_wont_see_their_posts_or_reblogs_in_your_home_feed_they_wont_know_they_ve_been_muted": "You won’t see their posts or reblogs in your home feed. They won’t know they’ve been muted.",
- "block_user": "Block %s",
- "they_will_no_longer_be_able_to_follow_or_see_your_posts_but_they_can_see_if_theyve_been_blocked": "They will no longer be able to follow or see your posts, but they can see if they’ve been blocked.",
- "while_we_review_this_you_can_take_action_against_user": "While we review this, you can take action against %s"
+ "dont_want_to_see_this": "Não quer ver isso?",
+ "when_you_see_something_you_dont_like_on_mastodon_you_can_remove_the_person_from_your_experience.": "Quando você vê algo que não gosta no Mastodon, você pode remover essa pessoa da sua experiência.",
+ "unfollow": "Deixar de seguir",
+ "unfollowed": "Deixou de seguir",
+ "unfollow_user": "Deixar de seguir %s",
+ "mute_user": "Silenciar %s",
+ "you_wont_see_their_posts_or_reblogs_in_your_home_feed_they_wont_know_they_ve_been_muted": "Você não verá as postagens ou reblogs dessa conta na sua pagina inicial. Essa pessoa não saberá que foi silenciada.",
+ "block_user": "Bloquear %s",
+ "they_will_no_longer_be_able_to_follow_or_see_your_posts_but_they_can_see_if_theyve_been_blocked": "Essa conta não poderá mais te seguir ou ver suas postagens, mas ela poderá ver que foi bloqueada.",
+ "while_we_review_this_you_can_take_action_against_user": "Enquanto revisamos isso, você pode tomar medidas contra %s"
}
},
"preview": {
"keyboard": {
- "close_preview": "Close Preview",
- "show_next": "Show Next",
- "show_previous": "Show Previous"
+ "close_preview": "Fechar prévia",
+ "show_next": "Mostrar a próxima",
+ "show_previous": "Mostrar a anterior"
}
},
"account_list": {
- "tab_bar_hint": "Current selected profile: %s. Double tap then hold to show account switcher",
- "dismiss_account_switcher": "Dismiss Account Switcher",
- "add_account": "Add Account"
+ "tab_bar_hint": "Perfil selecionado nesse momento: %s. Toque duas vezes e segure para mostrar o alternador de conta",
+ "dismiss_account_switcher": "Descartar alternador de conta",
+ "add_account": "Adicionar conta"
},
"wizard": {
- "new_in_mastodon": "New in Mastodon",
- "multiple_account_switch_intro_description": "Switch between multiple accounts by holding the profile button.",
- "accessibility_hint": "Double tap to dismiss this wizard"
+ "new_in_mastodon": "Novo no Mastodon",
+ "multiple_account_switch_intro_description": "Alterne entre múltiplas contas segurando o botão de perfil.",
+ "accessibility_hint": "Toque duas vezes para descartar este assistente"
},
"bookmark": {
- "title": "Bookmarks"
+ "title": "Marcados"
+ },
+ "followed_tags": {
+ "title": "Followed Tags",
+ "header": {
+ "posts": "posts",
+ "participants": "participants",
+ "posts_today": "posts today"
+ },
+ "actions": {
+ "follow": "Follow",
+ "unfollow": "Unfollow"
+ }
}
}
}
diff --git a/Localization/StringsConvertor/input/pt.lproj/Localizable.stringsdict b/Localization/StringsConvertor/input/pt.lproj/Localizable.stringsdict
index bdcae6ac9..788eb95fc 100644
--- a/Localization/StringsConvertor/input/pt.lproj/Localizable.stringsdict
+++ b/Localization/StringsConvertor/input/pt.lproj/Localizable.stringsdict
@@ -15,7 +15,7 @@
one
1 unread notification
other
- %ld unread notification
+ %ld unread notifications
a11y.plural.count.input_limit_exceeds
@@ -50,6 +50,22 @@
%ld characters
+ a11y.plural.count.characters_left
+
+ NSStringLocalizedFormatKey
+ %#@character_count@ left
+ character_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 character
+ other
+ %ld characters
+
+
plural.count.followed_by_and_mutual
NSStringLocalizedFormatKey
diff --git a/Localization/StringsConvertor/input/pt.lproj/app.json b/Localization/StringsConvertor/input/pt.lproj/app.json
index a6a971860..4939d3afb 100644
--- a/Localization/StringsConvertor/input/pt.lproj/app.json
+++ b/Localization/StringsConvertor/input/pt.lproj/app.json
@@ -51,6 +51,11 @@
"clean_cache": {
"title": "Clean Cache",
"message": "Successfully cleaned %s cache."
+ },
+ "translation_failed": {
+ "title": "Note",
+ "message": "Translation failed. Maybe the administrator has not enabled translations on this server or this server is running an older version of Mastodon where translations are not yet supported.",
+ "button": "OK"
}
},
"controls": {
@@ -74,10 +79,11 @@
"take_photo": "Take Photo",
"save_photo": "Save Photo",
"copy_photo": "Copy Photo",
- "sign_in": "Sign In",
- "sign_up": "Sign Up",
+ "sign_in": "Log in",
+ "sign_up": "Create account",
"see_more": "See More",
"preview": "Preview",
+ "copy": "Copy",
"share": "Share",
"share_user": "Share %s",
"share_post": "Share Post",
@@ -91,12 +97,16 @@
"block_domain": "Block %s",
"unblock_domain": "Unblock %s",
"settings": "Settings",
- "delete": "Delete"
+ "delete": "Delete",
+ "translate_post": {
+ "title": "Translate from %s",
+ "unknown_language": "Unknown"
+ }
},
"tabs": {
"home": "Home",
- "search": "Search",
- "notification": "Notification",
+ "search_and_explore": "Search and Explore",
+ "notifications": "Notifications",
"profile": "Profile"
},
"keyboard": {
@@ -132,6 +142,8 @@
"sensitive_content": "Sensitive Content",
"media_content_warning": "Tap anywhere to reveal",
"tap_to_reveal": "Tap to reveal",
+ "load_embed": "Load Embed",
+ "link_via_user": "%s via %s",
"poll": {
"vote": "Vote",
"closed": "Closed"
@@ -153,6 +165,7 @@
"show_image": "Show image",
"show_gif": "Show GIF",
"show_video_player": "Show video player",
+ "share_link_in_post": "Share Link in Post",
"tap_then_hold_to_show_menu": "Tap then hold to show menu"
},
"tag": {
@@ -168,6 +181,12 @@
"private": "Only their followers can see this post.",
"private_from_me": "Only my followers can see this post.",
"direct": "Only mentioned user can see this post."
+ },
+ "translation": {
+ "translated_from": "Translated from %s using %s",
+ "unknown_language": "Unknown",
+ "unknown_provider": "Unknown",
+ "show_original": "Shown Original"
}
},
"friendship": {
@@ -218,10 +237,16 @@
"get_started": "Get Started",
"log_in": "Log In"
},
+ "login": {
+ "title": "Welcome back",
+ "subtitle": "Log you in on the server you created your account on.",
+ "server_search_field": {
+ "placeholder": "Enter URL or search for your server"
+ }
+ },
"server_picker": {
"title": "Mastodon is made of users in different servers.",
- "subtitle": "Pick a server based on your interests, region, or a general purpose one.",
- "subtitle_extend": "Pick a server based on your interests, region, or a general purpose one. Each server is operated by an entirely independent organization or individual.",
+ "subtitle": "Pick a server based on your region, interests, or a general purpose one. You can still chat with anyone on Mastodon, regardless of your servers.",
"button": {
"category": {
"all": "All",
@@ -248,8 +273,7 @@
"category": "CATEGORY"
},
"input": {
- "placeholder": "Search servers",
- "search_servers_or_enter_url": "Search servers or enter URL"
+ "search_servers_or_enter_url": "Search communities or enter URL"
},
"empty_state": {
"finding_servers": "Finding available servers...",
@@ -385,8 +409,10 @@
"description_video": "Describe the video for the visually-impaired...",
"load_failed": "Load Failed",
"upload_failed": "Upload Failed",
- "can_not_recognize_this_media_attachment": "Can not regonize this media attachment",
- "attachment_too_large": "Attachment too large"
+ "can_not_recognize_this_media_attachment": "Can not recognize this media attachment",
+ "attachment_too_large": "Attachment too large",
+ "compressing_state": "Compressing...",
+ "server_processing_state": "Server Processing..."
},
"poll": {
"duration_time": "Duration: %s",
@@ -396,7 +422,9 @@
"one_day": "1 Day",
"three_days": "3 Days",
"seven_days": "7 Days",
- "option_number": "Option %ld"
+ "option_number": "Option %ld",
+ "the_poll_is_invalid": "The poll is invalid",
+ "the_poll_has_empty_option": "The poll has empty option"
},
"content_warning": {
"placeholder": "Write an accurate warning here..."
@@ -417,7 +445,9 @@
"custom_emoji_picker": "Custom Emoji Picker",
"enable_content_warning": "Enable Content Warning",
"disable_content_warning": "Disable Content Warning",
- "post_visibility_menu": "Post Visibility Menu"
+ "post_visibility_menu": "Post Visibility Menu",
+ "post_options": "Post Options",
+ "posting_as": "Posting as %s"
},
"keyboard": {
"discard_post": "Discard Post",
@@ -433,15 +463,23 @@
"follows_you": "Follows You"
},
"dashboard": {
- "posts": "posts",
- "following": "following",
- "followers": "followers"
+ "my_posts": "posts",
+ "my_following": "following",
+ "my_followers": "followers",
+ "other_posts": "posts",
+ "other_following": "following",
+ "other_followers": "followers"
},
"fields": {
+ "joined": "Joined",
"add_row": "Add Row",
"placeholder": {
"label": "Label",
"content": "Content"
+ },
+ "verified": {
+ "short": "Verified on %s",
+ "long": "Ownership of this link was checked on %s"
}
},
"segmented_control": {
@@ -707,6 +745,18 @@
},
"bookmark": {
"title": "Bookmarks"
+ },
+ "followed_tags": {
+ "title": "Followed Tags",
+ "header": {
+ "posts": "posts",
+ "participants": "participants",
+ "posts_today": "posts today"
+ },
+ "actions": {
+ "follow": "Follow",
+ "unfollow": "Unfollow"
+ }
}
}
}
diff --git a/Localization/StringsConvertor/input/ro.lproj/Localizable.stringsdict b/Localization/StringsConvertor/input/ro.lproj/Localizable.stringsdict
index 7ae5a1c79..669272590 100644
--- a/Localization/StringsConvertor/input/ro.lproj/Localizable.stringsdict
+++ b/Localization/StringsConvertor/input/ro.lproj/Localizable.stringsdict
@@ -15,9 +15,9 @@
one
1 unread notification
few
- %ld unread notification
+ %ld unread notifications
other
- %ld unread notification
+ %ld unread notifications
a11y.plural.count.input_limit_exceeds
@@ -56,6 +56,24 @@
%ld characters
+ a11y.plural.count.characters_left
+
+ NSStringLocalizedFormatKey
+ %#@character_count@ left
+ character_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 character
+ few
+ %ld characters
+ other
+ %ld characters
+
+
plural.count.followed_by_and_mutual
NSStringLocalizedFormatKey
diff --git a/Localization/StringsConvertor/input/ro.lproj/app.json b/Localization/StringsConvertor/input/ro.lproj/app.json
index a9d3804fa..65ae8eb4e 100644
--- a/Localization/StringsConvertor/input/ro.lproj/app.json
+++ b/Localization/StringsConvertor/input/ro.lproj/app.json
@@ -13,7 +13,7 @@
},
"vote_failure": {
"title": "Eșec la vot",
- "poll_ended": "The poll has ended"
+ "poll_ended": "Sondajul tău s-a încheiat"
},
"discard_post_content": {
"title": "Șterge Schită",
@@ -41,7 +41,7 @@
"block_entire_domain": "Block Domain"
},
"save_photo_failure": {
- "title": "Save Photo Failure",
+ "title": "Salvarea fotografiei a eșuat",
"message": "Please enable the photo library access permission to save the photo."
},
"delete_post": {
@@ -51,17 +51,22 @@
"clean_cache": {
"title": "Clean Cache",
"message": "Successfully cleaned %s cache."
+ },
+ "translation_failed": {
+ "title": "Note",
+ "message": "Translation failed. Maybe the administrator has not enabled translations on this server or this server is running an older version of Mastodon where translations are not yet supported.",
+ "button": "OK"
}
},
"controls": {
"actions": {
- "back": "Back",
- "next": "Next",
+ "back": "Înapoi",
+ "next": "Înainte",
"previous": "Previous",
"open": "Deschide",
- "add": "Add",
+ "add": "Adaugă",
"remove": "Elimină",
- "edit": "Edit",
+ "edit": "Modifică",
"save": "Salvează",
"ok": "OK",
"done": "Done",
@@ -74,29 +79,34 @@
"take_photo": "Take Photo",
"save_photo": "Save Photo",
"copy_photo": "Copy Photo",
- "sign_in": "Sign In",
- "sign_up": "Sign Up",
+ "sign_in": "Log in",
+ "sign_up": "Create account",
"see_more": "See More",
"preview": "Preview",
+ "copy": "Copy",
"share": "Share",
"share_user": "Share %s",
"share_post": "Share Post",
"open_in_safari": "Open in Safari",
- "open_in_browser": "Open in Browser",
- "find_people": "Find people to follow",
+ "open_in_browser": "Deschide în browser",
+ "find_people": "Găsește persoane de urmărit",
"manually_search": "Manually search instead",
- "skip": "Skip",
+ "skip": "Treci peste",
"reply": "Reply",
"report_user": "Report %s",
"block_domain": "Block %s",
"unblock_domain": "Unblock %s",
"settings": "Settings",
- "delete": "Delete"
+ "delete": "Delete",
+ "translate_post": {
+ "title": "Translate from %s",
+ "unknown_language": "Unknown"
+ }
},
"tabs": {
- "home": "Home",
- "search": "Search",
- "notification": "Notification",
+ "home": "Acasă",
+ "search_and_explore": "Search and Explore",
+ "notifications": "Notifications",
"profile": "Profile"
},
"keyboard": {
@@ -132,6 +142,8 @@
"sensitive_content": "Sensitive Content",
"media_content_warning": "Tap anywhere to reveal",
"tap_to_reveal": "Tap to reveal",
+ "load_embed": "Load Embed",
+ "link_via_user": "%s via %s",
"poll": {
"vote": "Vote",
"closed": "Closed"
@@ -153,6 +165,7 @@
"show_image": "Show image",
"show_gif": "Show GIF",
"show_video_player": "Show video player",
+ "share_link_in_post": "Share Link in Post",
"tap_then_hold_to_show_menu": "Tap then hold to show menu"
},
"tag": {
@@ -168,6 +181,12 @@
"private": "Only their followers can see this post.",
"private_from_me": "Only my followers can see this post.",
"direct": "Only mentioned user can see this post."
+ },
+ "translation": {
+ "translated_from": "Translated from %s using %s",
+ "unknown_language": "Unknown",
+ "unknown_provider": "Unknown",
+ "show_original": "Shown Original"
}
},
"friendship": {
@@ -218,10 +237,16 @@
"get_started": "Get Started",
"log_in": "Log In"
},
+ "login": {
+ "title": "Welcome back",
+ "subtitle": "Log you in on the server you created your account on.",
+ "server_search_field": {
+ "placeholder": "Enter URL or search for your server"
+ }
+ },
"server_picker": {
"title": "Mastodon is made of users in different servers.",
- "subtitle": "Pick a server based on your interests, region, or a general purpose one.",
- "subtitle_extend": "Pick a server based on your interests, region, or a general purpose one. Each server is operated by an entirely independent organization or individual.",
+ "subtitle": "Pick a server based on your region, interests, or a general purpose one. You can still chat with anyone on Mastodon, regardless of your servers.",
"button": {
"category": {
"all": "All",
@@ -248,8 +273,7 @@
"category": "CATEGORY"
},
"input": {
- "placeholder": "Search servers",
- "search_servers_or_enter_url": "Search servers or enter URL"
+ "search_servers_or_enter_url": "Search communities or enter URL"
},
"empty_state": {
"finding_servers": "Finding available servers...",
@@ -385,8 +409,10 @@
"description_video": "Describe the video for the visually-impaired...",
"load_failed": "Load Failed",
"upload_failed": "Upload Failed",
- "can_not_recognize_this_media_attachment": "Can not regonize this media attachment",
- "attachment_too_large": "Attachment too large"
+ "can_not_recognize_this_media_attachment": "Can not recognize this media attachment",
+ "attachment_too_large": "Attachment too large",
+ "compressing_state": "Compressing...",
+ "server_processing_state": "Server Processing..."
},
"poll": {
"duration_time": "Duration: %s",
@@ -396,7 +422,9 @@
"one_day": "1 Day",
"three_days": "3 Days",
"seven_days": "7 Days",
- "option_number": "Option %ld"
+ "option_number": "Option %ld",
+ "the_poll_is_invalid": "The poll is invalid",
+ "the_poll_has_empty_option": "The poll has empty option"
},
"content_warning": {
"placeholder": "Write an accurate warning here..."
@@ -417,7 +445,9 @@
"custom_emoji_picker": "Custom Emoji Picker",
"enable_content_warning": "Enable Content Warning",
"disable_content_warning": "Disable Content Warning",
- "post_visibility_menu": "Post Visibility Menu"
+ "post_visibility_menu": "Post Visibility Menu",
+ "post_options": "Post Options",
+ "posting_as": "Posting as %s"
},
"keyboard": {
"discard_post": "Discard Post",
@@ -433,15 +463,23 @@
"follows_you": "Follows You"
},
"dashboard": {
- "posts": "posts",
- "following": "following",
- "followers": "followers"
+ "my_posts": "posts",
+ "my_following": "following",
+ "my_followers": "followers",
+ "other_posts": "posts",
+ "other_following": "following",
+ "other_followers": "followers"
},
"fields": {
+ "joined": "Joined",
"add_row": "Add Row",
"placeholder": {
"label": "Label",
"content": "Content"
+ },
+ "verified": {
+ "short": "Verified on %s",
+ "long": "Ownership of this link was checked on %s"
}
},
"segmented_control": {
@@ -707,6 +745,18 @@
},
"bookmark": {
"title": "Bookmarks"
+ },
+ "followed_tags": {
+ "title": "Followed Tags",
+ "header": {
+ "posts": "posts",
+ "participants": "participants",
+ "posts_today": "posts today"
+ },
+ "actions": {
+ "follow": "Follow",
+ "unfollow": "Unfollow"
+ }
}
}
}
diff --git a/Localization/StringsConvertor/input/ru.lproj/Localizable.stringsdict b/Localization/StringsConvertor/input/ru.lproj/Localizable.stringsdict
index afb29a6aa..d43386ad9 100644
--- a/Localization/StringsConvertor/input/ru.lproj/Localizable.stringsdict
+++ b/Localization/StringsConvertor/input/ru.lproj/Localizable.stringsdict
@@ -15,11 +15,11 @@
one
1 unread notification
few
- %ld unread notification
+ %ld unread notifications
many
- %ld unread notification
+ %ld unread notifications
other
- %ld unread notification
+ %ld unread notifications
a11y.plural.count.input_limit_exceeds
@@ -62,6 +62,26 @@
%ld символа осталось
+ a11y.plural.count.characters_left
+
+ NSStringLocalizedFormatKey
+ %#@character_count@ left
+ character_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 character
+ few
+ %ld characters
+ many
+ %ld characters
+ other
+ %ld characters
+
+
plural.count.followed_by_and_mutual
NSStringLocalizedFormatKey
diff --git a/Localization/StringsConvertor/input/ru.lproj/app.json b/Localization/StringsConvertor/input/ru.lproj/app.json
index 798cdb4c5..bb34e4c1c 100644
--- a/Localization/StringsConvertor/input/ru.lproj/app.json
+++ b/Localization/StringsConvertor/input/ru.lproj/app.json
@@ -51,6 +51,11 @@
"clean_cache": {
"title": "Очистка кэша",
"message": "Успешно очищено %s кэша."
+ },
+ "translation_failed": {
+ "title": "Note",
+ "message": "Translation failed. Maybe the administrator has not enabled translations on this server or this server is running an older version of Mastodon where translations are not yet supported.",
+ "button": "OK"
}
},
"controls": {
@@ -74,10 +79,11 @@
"take_photo": "Сделать фото",
"save_photo": "Сохранить изображение",
"copy_photo": "Скопировать изображение",
- "sign_in": "Войти",
- "sign_up": "Зарегистрироваться",
+ "sign_in": "Log in",
+ "sign_up": "Create account",
"see_more": "Ещё",
"preview": "Предпросмотр",
+ "copy": "Copy",
"share": "Поделиться",
"share_user": "Поделиться %s",
"share_post": "Поделиться постом",
@@ -91,12 +97,16 @@
"block_domain": "Заблокировать %s",
"unblock_domain": "Разблокировать %s",
"settings": "Настройки",
- "delete": "Удалить"
+ "delete": "Удалить",
+ "translate_post": {
+ "title": "Translate from %s",
+ "unknown_language": "Unknown"
+ }
},
"tabs": {
"home": "Главная",
- "search": "Поиск",
- "notification": "Уведомление",
+ "search_and_explore": "Search and Explore",
+ "notifications": "Notifications",
"profile": "Профиль"
},
"keyboard": {
@@ -132,6 +142,8 @@
"sensitive_content": "Sensitive Content",
"media_content_warning": "Нажмите в любом месте, чтобы показать",
"tap_to_reveal": "Нажмите, чтобы показать",
+ "load_embed": "Load Embed",
+ "link_via_user": "%s via %s",
"poll": {
"vote": "Проголосовать",
"closed": "Завершён"
@@ -153,6 +165,7 @@
"show_image": "Показать изображение",
"show_gif": "Показать GIF",
"show_video_player": "Показать видеопроигрыватель",
+ "share_link_in_post": "Share Link in Post",
"tap_then_hold_to_show_menu": "Нажмите и удерживайте, чтобы показать меню"
},
"tag": {
@@ -168,6 +181,12 @@
"private": "Only their followers can see this post.",
"private_from_me": "Only my followers can see this post.",
"direct": "Only mentioned user can see this post."
+ },
+ "translation": {
+ "translated_from": "Translated from %s using %s",
+ "unknown_language": "Unknown",
+ "unknown_provider": "Unknown",
+ "show_original": "Shown Original"
}
},
"friendship": {
@@ -218,10 +237,16 @@
"get_started": "Присоединиться",
"log_in": "Вход"
},
+ "login": {
+ "title": "Welcome back",
+ "subtitle": "Log you in on the server you created your account on.",
+ "server_search_field": {
+ "placeholder": "Enter URL or search for your server"
+ }
+ },
"server_picker": {
"title": "Выберите сервер,\nлюбой сервер.",
- "subtitle": "Выберите сообщество на основе своих интересов, региона или общей тематики.",
- "subtitle_extend": "Pick a server based on your interests, region, or a general purpose one. Each server is operated by an entirely independent organization or individual.",
+ "subtitle": "Pick a server based on your region, interests, or a general purpose one. You can still chat with anyone on Mastodon, regardless of your servers.",
"button": {
"category": {
"all": "Все",
@@ -248,8 +273,7 @@
"category": "КАТЕГОРИЯ"
},
"input": {
- "placeholder": "Найдите сервер или присоединитесь к своему...",
- "search_servers_or_enter_url": "Поиск по серверам или ссылке"
+ "search_servers_or_enter_url": "Search communities or enter URL"
},
"empty_state": {
"finding_servers": "Ищем доступные сервера...",
@@ -385,8 +409,10 @@
"description_video": "Опишите видео для людей с нарушениями зрения...",
"load_failed": "Load Failed",
"upload_failed": "Upload Failed",
- "can_not_recognize_this_media_attachment": "Can not regonize this media attachment",
- "attachment_too_large": "Attachment too large"
+ "can_not_recognize_this_media_attachment": "Can not recognize this media attachment",
+ "attachment_too_large": "Attachment too large",
+ "compressing_state": "Compressing...",
+ "server_processing_state": "Server Processing..."
},
"poll": {
"duration_time": "Продолжительность: %s",
@@ -396,7 +422,9 @@
"one_day": "1 день",
"three_days": "3 дня",
"seven_days": "7 дней",
- "option_number": "Вариант %ld"
+ "option_number": "Вариант %ld",
+ "the_poll_is_invalid": "The poll is invalid",
+ "the_poll_has_empty_option": "The poll has empty option"
},
"content_warning": {
"placeholder": "Напишите предупреждение здесь..."
@@ -417,7 +445,9 @@
"custom_emoji_picker": "Меню пользовательских эмодзи",
"enable_content_warning": "Добавить предупреждение о содержании",
"disable_content_warning": "Убрать предупреждение о содержании",
- "post_visibility_menu": "Меню видимости поста"
+ "post_visibility_menu": "Меню видимости поста",
+ "post_options": "Post Options",
+ "posting_as": "Posting as %s"
},
"keyboard": {
"discard_post": "Удалить пост",
@@ -433,15 +463,23 @@
"follows_you": "Подписан(а) на вас"
},
"dashboard": {
- "posts": "посты",
- "following": "подписки",
- "followers": "подписчики"
+ "my_posts": "posts",
+ "my_following": "following",
+ "my_followers": "followers",
+ "other_posts": "posts",
+ "other_following": "following",
+ "other_followers": "followers"
},
"fields": {
+ "joined": "Joined",
"add_row": "Добавить строку",
"placeholder": {
"label": "Ярлык",
"content": "Содержимое"
+ },
+ "verified": {
+ "short": "Verified on %s",
+ "long": "Ownership of this link was checked on %s"
}
},
"segmented_control": {
@@ -707,6 +745,18 @@
},
"bookmark": {
"title": "Bookmarks"
+ },
+ "followed_tags": {
+ "title": "Followed Tags",
+ "header": {
+ "posts": "posts",
+ "participants": "participants",
+ "posts_today": "posts today"
+ },
+ "actions": {
+ "follow": "Follow",
+ "unfollow": "Unfollow"
+ }
}
}
}
diff --git a/Localization/StringsConvertor/input/si.lproj/Localizable.stringsdict b/Localization/StringsConvertor/input/si.lproj/Localizable.stringsdict
index bdcae6ac9..788eb95fc 100644
--- a/Localization/StringsConvertor/input/si.lproj/Localizable.stringsdict
+++ b/Localization/StringsConvertor/input/si.lproj/Localizable.stringsdict
@@ -15,7 +15,7 @@
one
1 unread notification
other
- %ld unread notification
+ %ld unread notifications
a11y.plural.count.input_limit_exceeds
@@ -50,6 +50,22 @@
%ld characters
+ a11y.plural.count.characters_left
+
+ NSStringLocalizedFormatKey
+ %#@character_count@ left
+ character_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 character
+ other
+ %ld characters
+
+
plural.count.followed_by_and_mutual
NSStringLocalizedFormatKey
diff --git a/Localization/StringsConvertor/input/si.lproj/app.json b/Localization/StringsConvertor/input/si.lproj/app.json
index 816536440..364708892 100644
--- a/Localization/StringsConvertor/input/si.lproj/app.json
+++ b/Localization/StringsConvertor/input/si.lproj/app.json
@@ -51,6 +51,11 @@
"clean_cache": {
"title": "Clean Cache",
"message": "Successfully cleaned %s cache."
+ },
+ "translation_failed": {
+ "title": "Note",
+ "message": "Translation failed. Maybe the administrator has not enabled translations on this server or this server is running an older version of Mastodon where translations are not yet supported.",
+ "button": "OK"
}
},
"controls": {
@@ -74,10 +79,11 @@
"take_photo": "Take Photo",
"save_photo": "Save Photo",
"copy_photo": "Copy Photo",
- "sign_in": "පිවිසෙන්න",
- "sign_up": "ලියාපදිංචිය",
+ "sign_in": "Log in",
+ "sign_up": "Create account",
"see_more": "තව බලන්න",
"preview": "පෙරදසුන",
+ "copy": "Copy",
"share": "බෙදාගන්න",
"share_user": "%s බෙදාගන්න",
"share_post": "Share Post",
@@ -91,12 +97,16 @@
"block_domain": "Block %s",
"unblock_domain": "Unblock %s",
"settings": "Settings",
- "delete": "Delete"
+ "delete": "Delete",
+ "translate_post": {
+ "title": "Translate from %s",
+ "unknown_language": "Unknown"
+ }
},
"tabs": {
"home": "Home",
- "search": "Search",
- "notification": "Notification",
+ "search_and_explore": "Search and Explore",
+ "notifications": "Notifications",
"profile": "Profile"
},
"keyboard": {
@@ -132,6 +142,8 @@
"sensitive_content": "Sensitive Content",
"media_content_warning": "Tap anywhere to reveal",
"tap_to_reveal": "Tap to reveal",
+ "load_embed": "Load Embed",
+ "link_via_user": "%s via %s",
"poll": {
"vote": "ඡන්දය",
"closed": "වසා ඇත"
@@ -153,6 +165,7 @@
"show_image": "Show image",
"show_gif": "Show GIF",
"show_video_player": "Show video player",
+ "share_link_in_post": "Share Link in Post",
"tap_then_hold_to_show_menu": "Tap then hold to show menu"
},
"tag": {
@@ -168,6 +181,12 @@
"private": "Only their followers can see this post.",
"private_from_me": "Only my followers can see this post.",
"direct": "Only mentioned user can see this post."
+ },
+ "translation": {
+ "translated_from": "Translated from %s using %s",
+ "unknown_language": "Unknown",
+ "unknown_provider": "Unknown",
+ "show_original": "Shown Original"
}
},
"friendship": {
@@ -218,10 +237,16 @@
"get_started": "පටන් ගන්න",
"log_in": "පිවිසෙන්න"
},
+ "login": {
+ "title": "Welcome back",
+ "subtitle": "Log you in on the server you created your account on.",
+ "server_search_field": {
+ "placeholder": "Enter URL or search for your server"
+ }
+ },
"server_picker": {
"title": "Mastodon is made of users in different servers.",
- "subtitle": "Pick a server based on your interests, region, or a general purpose one.",
- "subtitle_extend": "Pick a server based on your interests, region, or a general purpose one. Each server is operated by an entirely independent organization or individual.",
+ "subtitle": "Pick a server based on your region, interests, or a general purpose one. You can still chat with anyone on Mastodon, regardless of your servers.",
"button": {
"category": {
"all": "සියල්ල",
@@ -248,8 +273,7 @@
"category": "CATEGORY"
},
"input": {
- "placeholder": "Search servers",
- "search_servers_or_enter_url": "Search servers or enter URL"
+ "search_servers_or_enter_url": "Search communities or enter URL"
},
"empty_state": {
"finding_servers": "Finding available servers...",
@@ -385,8 +409,10 @@
"description_video": "Describe the video for the visually-impaired...",
"load_failed": "Load Failed",
"upload_failed": "Upload Failed",
- "can_not_recognize_this_media_attachment": "Can not regonize this media attachment",
- "attachment_too_large": "Attachment too large"
+ "can_not_recognize_this_media_attachment": "Can not recognize this media attachment",
+ "attachment_too_large": "Attachment too large",
+ "compressing_state": "Compressing...",
+ "server_processing_state": "Server Processing..."
},
"poll": {
"duration_time": "Duration: %s",
@@ -396,7 +422,9 @@
"one_day": "1 Day",
"three_days": "3 Days",
"seven_days": "7 Days",
- "option_number": "Option %ld"
+ "option_number": "Option %ld",
+ "the_poll_is_invalid": "The poll is invalid",
+ "the_poll_has_empty_option": "The poll has empty option"
},
"content_warning": {
"placeholder": "Write an accurate warning here..."
@@ -417,7 +445,9 @@
"custom_emoji_picker": "Custom Emoji Picker",
"enable_content_warning": "Enable Content Warning",
"disable_content_warning": "Disable Content Warning",
- "post_visibility_menu": "Post Visibility Menu"
+ "post_visibility_menu": "Post Visibility Menu",
+ "post_options": "Post Options",
+ "posting_as": "Posting as %s"
},
"keyboard": {
"discard_post": "Discard Post",
@@ -433,15 +463,23 @@
"follows_you": "Follows You"
},
"dashboard": {
- "posts": "posts",
- "following": "following",
- "followers": "followers"
+ "my_posts": "posts",
+ "my_following": "following",
+ "my_followers": "followers",
+ "other_posts": "posts",
+ "other_following": "following",
+ "other_followers": "followers"
},
"fields": {
+ "joined": "Joined",
"add_row": "Add Row",
"placeholder": {
"label": "නම්පත",
"content": "අන්තර්ගතය"
+ },
+ "verified": {
+ "short": "Verified on %s",
+ "long": "Ownership of this link was checked on %s"
}
},
"segmented_control": {
@@ -707,6 +745,18 @@
},
"bookmark": {
"title": "Bookmarks"
+ },
+ "followed_tags": {
+ "title": "Followed Tags",
+ "header": {
+ "posts": "posts",
+ "participants": "participants",
+ "posts_today": "posts today"
+ },
+ "actions": {
+ "follow": "Follow",
+ "unfollow": "Unfollow"
+ }
}
}
}
diff --git a/Localization/StringsConvertor/input/sl.lproj/Localizable.stringsdict b/Localization/StringsConvertor/input/sl.lproj/Localizable.stringsdict
index 8f0bcb42b..87cc42142 100644
--- a/Localization/StringsConvertor/input/sl.lproj/Localizable.stringsdict
+++ b/Localization/StringsConvertor/input/sl.lproj/Localizable.stringsdict
@@ -62,6 +62,26 @@
%ld znakov
+ a11y.plural.count.characters_left
+
+ NSStringLocalizedFormatKey
+ preostaja %#@character_count@
+ character_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ %ld znak
+ two
+ %ld znaka
+ few
+ %ld znaki
+ other
+ %ld znakov
+
+
plural.count.followed_by_and_mutual
NSStringLocalizedFormatKey
diff --git a/Localization/StringsConvertor/input/sl.lproj/app.json b/Localization/StringsConvertor/input/sl.lproj/app.json
index 37b62a45d..3ce4e2daa 100644
--- a/Localization/StringsConvertor/input/sl.lproj/app.json
+++ b/Localization/StringsConvertor/input/sl.lproj/app.json
@@ -51,6 +51,11 @@
"clean_cache": {
"title": "Počisti predpomnilnik",
"message": "Uspešno počiščem predpomnilnik %s."
+ },
+ "translation_failed": {
+ "title": "Opomba",
+ "message": "Prevod je spodletel. Morda skrbnik ni omogočil prevajanja na tem strežniku ali pa strežnik teče na starejši različici Masotodona, na kateri prevajanje še ni podprto.",
+ "button": "V redu"
}
},
"controls": {
@@ -75,9 +80,10 @@
"save_photo": "Shrani fotografijo",
"copy_photo": "Kopiraj fotografijo",
"sign_in": "Prijava",
- "sign_up": "Registracija",
+ "sign_up": "Ustvari račun",
"see_more": "Pokaži več",
"preview": "Predogled",
+ "copy": "Kopiraj",
"share": "Deli",
"share_user": "Deli %s",
"share_post": "Deli objavo",
@@ -91,12 +97,16 @@
"block_domain": "Blokiraj %s",
"unblock_domain": "Odblokiraj %s",
"settings": "Nastavitve",
- "delete": "Izbriši"
+ "delete": "Izbriši",
+ "translate_post": {
+ "title": "Prevedi iz: %s",
+ "unknown_language": "Neznano"
+ }
},
"tabs": {
"home": "Domov",
- "search": "Iskanje",
- "notification": "Obvestilo",
+ "search_and_explore": "Poišči in razišči",
+ "notifications": "Obvestila",
"profile": "Profil"
},
"keyboard": {
@@ -132,6 +142,8 @@
"sensitive_content": "Občutljiva vsebina",
"media_content_warning": "Tapnite kamorkoli, da razkrijete",
"tap_to_reveal": "Tapnite za razkritje",
+ "load_embed": "Naloži vdelano",
+ "link_via_user": "%s prek %s",
"poll": {
"vote": "Glasuj",
"closed": "Zaprto"
@@ -153,6 +165,7 @@
"show_image": "Pokaži sliko",
"show_gif": "Pokaži GIF",
"show_video_player": "Pokaži predvajalnik",
+ "share_link_in_post": "Deli povezavo v objavi",
"tap_then_hold_to_show_menu": "Tapnite, nato držite, da se pojavi meni"
},
"tag": {
@@ -168,6 +181,12 @@
"private": "Samo sledilci osebe lahko vidijo to objavo.",
"private_from_me": "Samo moji sledilci lahko vidijo to objavo.",
"direct": "Samo omenjeni uporabnik lahko vidi to objavo."
+ },
+ "translation": {
+ "translated_from": "Prevedeno iz %s s pomočjo %s",
+ "unknown_language": "Neznano",
+ "unknown_provider": "Neznano",
+ "show_original": "Pokaži izvirnik"
}
},
"friendship": {
@@ -218,10 +237,16 @@
"get_started": "Začnite",
"log_in": "Prijava"
},
+ "login": {
+ "title": "Dobrodošli nazaj",
+ "subtitle": "Prijavite se na strežniku, na katerem ste ustvarili račun.",
+ "server_search_field": {
+ "placeholder": "Vnesite URL ali poiščite svoj strežnik"
+ }
+ },
"server_picker": {
"title": "Mastodon tvorijo uporabniki z različnih strežnikov.",
- "subtitle": "Strežnik izberite glede na svoje interese, regijo ali pa izberite splošnega.",
- "subtitle_extend": "Strežnik izberite glede na svoje interese, regijo ali pa izberite splošnega. Z vsakim strežnikom upravlja povsem neodvisna organizacija ali posameznik.",
+ "subtitle": "Strežnik izberite glede na svojo regijo, zanimanje ali pa kar splošno. Še vedno lahko klepetate s komer koli na Mastodonu, ne glede na strežnik.",
"button": {
"category": {
"all": "Vse",
@@ -248,8 +273,7 @@
"category": "KATEGORIJA"
},
"input": {
- "placeholder": "Išči strežnike",
- "search_servers_or_enter_url": "Iščite strežnike ali vnesite URL"
+ "search_servers_or_enter_url": "Iščite po skupnostih ali vnesite URL"
},
"empty_state": {
"finding_servers": "Iskanje razpoložljivih strežnikov ...",
@@ -383,10 +407,12 @@
"attachment_broken": "To %s je okvarjeno in ga ni\nmožno naložiti v Mastodon.",
"description_photo": "Opiši fotografijo za slabovidne in osebe z okvaro vida ...",
"description_video": "Opiši video za slabovidne in osebe z okvaro vida ...",
- "load_failed": "Load Failed",
- "upload_failed": "Upload Failed",
- "can_not_recognize_this_media_attachment": "Can not regonize this media attachment",
- "attachment_too_large": "Attachment too large"
+ "load_failed": "Nalaganje ni uspelo",
+ "upload_failed": "Nalaganje na strežnik ni uspelo",
+ "can_not_recognize_this_media_attachment": "Te medijske priponke ni mogoče prepoznati",
+ "attachment_too_large": "Priponka je prevelika",
+ "compressing_state": "Stiskanje ...",
+ "server_processing_state": "Obdelovanje na strežniku ..."
},
"poll": {
"duration_time": "Trajanje: %s",
@@ -396,7 +422,9 @@
"one_day": "1 dan",
"three_days": "3 dni",
"seven_days": "7 dni",
- "option_number": "Možnost %ld"
+ "option_number": "Možnost %ld",
+ "the_poll_is_invalid": "Anketa je neveljavna",
+ "the_poll_has_empty_option": "Anketa ima prazno izbiro"
},
"content_warning": {
"placeholder": "Tukaj zapišite opozorilo ..."
@@ -417,7 +445,9 @@
"custom_emoji_picker": "Izbirnik čustvenčkov po meri",
"enable_content_warning": "Omogoči opozorilo o vsebini",
"disable_content_warning": "Onemogoči opozorilo o vsebini",
- "post_visibility_menu": "Meni vidnosti objave"
+ "post_visibility_menu": "Meni vidnosti objave",
+ "post_options": "Možnosti objave",
+ "posting_as": "Objavljate kot %s"
},
"keyboard": {
"discard_post": "Opusti objavo",
@@ -433,15 +463,23 @@
"follows_you": "Vam sledi"
},
"dashboard": {
- "posts": "Objave",
- "following": "sledi",
- "followers": "sledilcev"
+ "my_posts": "objav",
+ "my_following": "sledi",
+ "my_followers": "sledilcev",
+ "other_posts": "objav",
+ "other_following": "sledi",
+ "other_followers": "sledilcev"
},
"fields": {
+ "joined": "Pridružen_a",
"add_row": "Dodaj vrstico",
"placeholder": {
"label": "Oznaka",
"content": "Vsebina"
+ },
+ "verified": {
+ "short": "Preverjeno %s",
+ "long": "Lastništvo te povezave je bilo preverjeno %s"
}
},
"segmented_control": {
@@ -707,6 +745,18 @@
},
"bookmark": {
"title": "Zaznamki"
+ },
+ "followed_tags": {
+ "title": "Sledene značke",
+ "header": {
+ "posts": "objav",
+ "participants": "udeležencev",
+ "posts_today": "objav danes"
+ },
+ "actions": {
+ "follow": "Sledi",
+ "unfollow": "Prenehaj slediti"
+ }
}
}
}
diff --git a/Localization/StringsConvertor/input/sv.lproj/Localizable.stringsdict b/Localization/StringsConvertor/input/sv.lproj/Localizable.stringsdict
index c7317903d..43a0ff8e4 100644
--- a/Localization/StringsConvertor/input/sv.lproj/Localizable.stringsdict
+++ b/Localization/StringsConvertor/input/sv.lproj/Localizable.stringsdict
@@ -50,6 +50,22 @@
%ld tecken
+ a11y.plural.count.characters_left
+
+ NSStringLocalizedFormatKey
+ %#@character_count@ kvar
+ character_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ %ld tecken
+ other
+ %ld tecken
+
+
plural.count.followed_by_and_mutual
NSStringLocalizedFormatKey
@@ -264,7 +280,7 @@
NSStringFormatValueTypeKey
ld
one
- %ld år kvar
+ 1 år kvar
other
%ld år kvar
@@ -280,7 +296,7 @@
NSStringFormatValueTypeKey
ld
one
- %ld månad kvar
+ 1 månad kvar
other
%ld månader kvar
@@ -296,7 +312,7 @@
NSStringFormatValueTypeKey
ld
one
- %ld dag kvar
+ 1 dag kvar
other
%ld dagar kvar
@@ -344,7 +360,7 @@
NSStringFormatValueTypeKey
ld
one
- %ld sekund kvar
+ 1 sekund kvar
other
%ld sekunder kvar
@@ -392,7 +408,7 @@
NSStringFormatValueTypeKey
ld
one
- %ldd sedan
+ 1d sedan
other
%ldd sedan
@@ -408,7 +424,7 @@
NSStringFormatValueTypeKey
ld
one
- %ldt sedan
+ 1t sedan
other
%ldt sedan
diff --git a/Localization/StringsConvertor/input/sv.lproj/app.json b/Localization/StringsConvertor/input/sv.lproj/app.json
index c740609c9..400758cd9 100644
--- a/Localization/StringsConvertor/input/sv.lproj/app.json
+++ b/Localization/StringsConvertor/input/sv.lproj/app.json
@@ -51,6 +51,11 @@
"clean_cache": {
"title": "Rensa cache",
"message": "Rensade %s cache."
+ },
+ "translation_failed": {
+ "title": "Anteckning",
+ "message": "Översättningen misslyckades. Det kan hända att administratören inte har aktiverat översättningar på den här servern eller att servern kör en äldre version av Mastodon som inte har stöd för översättningar ännu.",
+ "button": "OK"
}
},
"controls": {
@@ -75,9 +80,10 @@
"save_photo": "Spara foto",
"copy_photo": "Kopiera foto",
"sign_in": "Logga in",
- "sign_up": "Registrera dig",
+ "sign_up": "Skapa konto",
"see_more": "Visa mer",
"preview": "Förhandsvisa",
+ "copy": "Kopiera",
"share": "Dela",
"share_user": "Dela %s",
"share_post": "Dela inlägg",
@@ -91,12 +97,16 @@
"block_domain": "Blockera %s",
"unblock_domain": "Avblockera %s",
"settings": "Inställningar",
- "delete": "Radera"
+ "delete": "Radera",
+ "translate_post": {
+ "title": "Översätt från %s",
+ "unknown_language": "Okänt"
+ }
},
"tabs": {
"home": "Hem",
- "search": "Sök",
- "notification": "Notis",
+ "search_and_explore": "Sök och utforska",
+ "notifications": "Notiser",
"profile": "Profil"
},
"keyboard": {
@@ -132,6 +142,8 @@
"sensitive_content": "Känsligt innehåll",
"media_content_warning": "Tryck var som helst för att visa",
"tap_to_reveal": "Tryck för att visa",
+ "load_embed": "Ladda inbäddning",
+ "link_via_user": "%s via %s",
"poll": {
"vote": "Rösta",
"closed": "Stängd"
@@ -153,6 +165,7 @@
"show_image": "Visa bild",
"show_gif": "Visa GIF",
"show_video_player": "Visa videospelare",
+ "share_link_in_post": "Dela länk i inlägg",
"tap_then_hold_to_show_menu": "Tryck och håll ned för att visa menyn"
},
"tag": {
@@ -168,6 +181,12 @@
"private": "Endast deras följare kan se detta inlägg.",
"private_from_me": "Bara mina följare kan se det här inlägget.",
"direct": "Endast omnämnda användare kan se detta inlägg."
+ },
+ "translation": {
+ "translated_from": "Översatt från %s med %s",
+ "unknown_language": "Okänt",
+ "unknown_provider": "Okänd",
+ "show_original": "Visa original"
}
},
"friendship": {
@@ -218,10 +237,16 @@
"get_started": "Kom igång",
"log_in": "Logga in"
},
+ "login": {
+ "title": "Välkommen tillbaka",
+ "subtitle": "Logga in på servern där du skapade ditt konto.",
+ "server_search_field": {
+ "placeholder": "Ange URL eller sök efter din server"
+ }
+ },
"server_picker": {
"title": "Mastodon utgörs av användare på olika servrar.",
- "subtitle": "Välj en server baserat på dina intressen, region eller ett allmänt syfte.",
- "subtitle_extend": "Välj en server baserat på dina intressen, region eller ett allmänt syfte. Varje server drivs av en helt oberoende organisation eller individ.",
+ "subtitle": "Välj en server baserat på dina intressen, region eller en allmän server. Du kan fortfarande nå alla, oavsett server.",
"button": {
"category": {
"all": "Alla",
@@ -248,8 +273,7 @@
"category": "KATEGORI"
},
"input": {
- "placeholder": "Sök gemenskaper",
- "search_servers_or_enter_url": "Sök servrar eller ange URL"
+ "search_servers_or_enter_url": "Sök gemenskaper eller ange URL"
},
"empty_state": {
"finding_servers": "Söker tillgängliga servrar...",
@@ -386,7 +410,9 @@
"load_failed": "Det gick inte att läsa in",
"upload_failed": "Uppladdning misslyckades",
"can_not_recognize_this_media_attachment": "Känner inte igen mediebilagan",
- "attachment_too_large": "Bilagan är för stor"
+ "attachment_too_large": "Bilagan är för stor",
+ "compressing_state": "Komprimerar...",
+ "server_processing_state": "Behandlas av servern..."
},
"poll": {
"duration_time": "Längd: %s",
@@ -396,7 +422,9 @@
"one_day": "1 dag",
"three_days": "3 dagar",
"seven_days": "7 dagar",
- "option_number": "Alternativ %ld"
+ "option_number": "Alternativ %ld",
+ "the_poll_is_invalid": "Undersökningen är ogiltig",
+ "the_poll_has_empty_option": "Undersökningen har ett tomt alternativ"
},
"content_warning": {
"placeholder": "Skriv en noggrann varning här..."
@@ -417,7 +445,9 @@
"custom_emoji_picker": "Anpassad emoji-väljare",
"enable_content_warning": "Aktivera innehållsvarning",
"disable_content_warning": "Inaktivera innehållsvarning",
- "post_visibility_menu": "Inläggssynlighetsmeny"
+ "post_visibility_menu": "Inläggssynlighetsmeny",
+ "post_options": "Inläggsalternativ",
+ "posting_as": "Postar som %s"
},
"keyboard": {
"discard_post": "Släng inlägget",
@@ -433,15 +463,23 @@
"follows_you": "Följer dig"
},
"dashboard": {
- "posts": "inlägg",
- "following": "följer",
- "followers": "följare"
+ "my_posts": "inlägg",
+ "my_following": "följer",
+ "my_followers": "följare",
+ "other_posts": "inlägg",
+ "other_following": "följer",
+ "other_followers": "följare"
},
"fields": {
+ "joined": "Gick med",
"add_row": "Lägg till rad",
"placeholder": {
"label": "Etikett",
"content": "Innehåll"
+ },
+ "verified": {
+ "short": "Verifierad på %s",
+ "long": "Ägarskap för denna länk kontrollerades den %s"
}
},
"segmented_control": {
@@ -707,6 +745,18 @@
},
"bookmark": {
"title": "Bokmärken"
+ },
+ "followed_tags": {
+ "title": "Följda hashtaggar",
+ "header": {
+ "posts": "inlägg",
+ "participants": "deltagare",
+ "posts_today": "inlägg idag"
+ },
+ "actions": {
+ "follow": "Följ",
+ "unfollow": "Avfölj"
+ }
}
}
}
diff --git a/Localization/StringsConvertor/input/th.lproj/Localizable.stringsdict b/Localization/StringsConvertor/input/th.lproj/Localizable.stringsdict
index 897d07eca..f25561ad6 100644
--- a/Localization/StringsConvertor/input/th.lproj/Localizable.stringsdict
+++ b/Localization/StringsConvertor/input/th.lproj/Localizable.stringsdict
@@ -44,6 +44,20 @@
%ld ตัวอักษร
+ a11y.plural.count.characters_left
+
+ NSStringLocalizedFormatKey
+ เหลืออีก %#@character_count@
+ character_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ other
+ %ld ตัวอักษร
+
+
plural.count.followed_by_and_mutual
NSStringLocalizedFormatKey
diff --git a/Localization/StringsConvertor/input/th.lproj/app.json b/Localization/StringsConvertor/input/th.lproj/app.json
index 9b4316025..6b3421d47 100644
--- a/Localization/StringsConvertor/input/th.lproj/app.json
+++ b/Localization/StringsConvertor/input/th.lproj/app.json
@@ -51,6 +51,11 @@
"clean_cache": {
"title": "ล้างแคช",
"message": "ล้างแคช %s สำเร็จ"
+ },
+ "translation_failed": {
+ "title": "หมายเหตุ",
+ "message": "การแปลล้มเหลว บางทีผู้ดูแลอาจไม่ได้เปิดใช้งานการแปลในเซิร์ฟเวอร์นี้หรือเซิร์ฟเวอร์นี้กำลังใช้ Mastodon รุ่นเก่ากว่าที่ยังไม่รองรับการแปล",
+ "button": "ตกลง"
}
},
"controls": {
@@ -74,10 +79,11 @@
"take_photo": "ถ่ายรูป",
"save_photo": "บันทึกรูปภาพ",
"copy_photo": "คัดลอกรูปภาพ",
- "sign_in": "ลงชื่อเข้า",
- "sign_up": "ลงทะเบียน",
+ "sign_in": "เข้าสู่ระบบ",
+ "sign_up": "สร้างบัญชี",
"see_more": "ดูเพิ่มเติม",
"preview": "แสดงตัวอย่าง",
+ "copy": "คัดลอก",
"share": "แบ่งปัน",
"share_user": "แบ่งปัน %s",
"share_post": "แบ่งปันโพสต์",
@@ -91,12 +97,16 @@
"block_domain": "ปิดกั้น %s",
"unblock_domain": "เลิกปิดกั้น %s",
"settings": "การตั้งค่า",
- "delete": "ลบ"
+ "delete": "ลบ",
+ "translate_post": {
+ "title": "แปลจาก %s",
+ "unknown_language": "ไม่รู้จัก"
+ }
},
"tabs": {
"home": "หน้าแรก",
- "search": "ค้นหา",
- "notification": "การแจ้งเตือน",
+ "search_and_explore": "ค้นหาและสำรวจ",
+ "notifications": "การแจ้งเตือน",
"profile": "โปรไฟล์"
},
"keyboard": {
@@ -132,6 +142,8 @@
"sensitive_content": "เนื้อหาที่ละเอียดอ่อน",
"media_content_warning": "แตะที่ใดก็ตามเพื่อเปิดเผย",
"tap_to_reveal": "แตะเพื่อเปิดเผย",
+ "load_embed": "โหลดที่ฝังไว้",
+ "link_via_user": "%s ผ่าน %s",
"poll": {
"vote": "ลงคะแนน",
"closed": "ปิดแล้ว"
@@ -153,6 +165,7 @@
"show_image": "แสดงภาพ",
"show_gif": "แสดง GIF",
"show_video_player": "แสดงตัวเล่นวิดีโอ",
+ "share_link_in_post": "แบ่งปันลิงก์ในโพสต์",
"tap_then_hold_to_show_menu": "แตะค้างไว้เพื่อแสดงเมนู"
},
"tag": {
@@ -168,6 +181,12 @@
"private": "เฉพาะผู้ติดตามของเขาเท่านั้นที่สามารถเห็นโพสต์นี้",
"private_from_me": "เฉพาะผู้ติดตามของฉันเท่านั้นที่สามารถเห็นโพสต์นี้",
"direct": "เฉพาะผู้ใช้ที่กล่าวถึงเท่านั้นที่สามารถเห็นโพสต์นี้"
+ },
+ "translation": {
+ "translated_from": "แปลจาก %s โดยใช้ %s",
+ "unknown_language": "ไม่รู้จัก",
+ "unknown_provider": "ไม่รู้จัก",
+ "show_original": "แสดงดั้งเดิมอยู่"
}
},
"friendship": {
@@ -218,10 +237,16 @@
"get_started": "เริ่มต้นใช้งาน",
"log_in": "เข้าสู่ระบบ"
},
+ "login": {
+ "title": "ยินดีต้อนรับกลับมา",
+ "subtitle": "นำคุณเข้าสู่ระบบในเซิร์ฟเวอร์ที่คุณได้สร้างบัญชีของคุณไว้ใน",
+ "server_search_field": {
+ "placeholder": "ป้อน URL หรือค้นหาสำหรับเซิร์ฟเวอร์ของคุณ"
+ }
+ },
"server_picker": {
"title": "Mastodon ประกอบด้วยผู้ใช้ในเซิร์ฟเวอร์ต่าง ๆ",
- "subtitle": "เลือกเซิร์ฟเวอร์ตามความสนใจ, ภูมิภาค หรือวัตถุประสงค์ทั่วไปของคุณ",
- "subtitle_extend": "เลือกเซิร์ฟเวอร์ตามความสนใจ, ภูมิภาค หรือวัตถุประสงค์ทั่วไปของคุณ แต่ละเซิร์ฟเวอร์ได้รับการดำเนินงานโดยองค์กรหรือบุคคลที่เป็นอิสระโดยสิ้นเชิง",
+ "subtitle": "เลือกเซิร์ฟเวอร์ตามภูมิภาค, ความสนใจ หรือวัตถุประสงค์ทั่วไปของคุณ คุณยังคงสามารถแชทกับใครก็ตามใน Mastodon โดยไม่คำนึงถึงเซิร์ฟเวอร์ของคุณ",
"button": {
"category": {
"all": "ทั้งหมด",
@@ -248,8 +273,7 @@
"category": "หมวดหมู่"
},
"input": {
- "placeholder": "ค้นหาเซิร์ฟเวอร์",
- "search_servers_or_enter_url": "ค้นหาเซิร์ฟเวอร์หรือป้อน URL"
+ "search_servers_or_enter_url": "ค้นหาชุมชนหรือป้อน URL"
},
"empty_state": {
"finding_servers": "กำลังค้นหาเซิร์ฟเวอร์ที่พร้อมใช้งาน...",
@@ -299,7 +323,7 @@
},
"reason": {
"blocked": "%s มีผู้ให้บริการอีเมลที่ไม่ได้รับอนุญาต",
- "unreachable": "ดูเหมือนว่า %s จะไม่มีอยู่",
+ "unreachable": "ดูเหมือนว่าจะไม่มี %s อยู่",
"taken": "%s ถูกใช้งานแล้ว",
"reserved": "%s เป็นคำสงวน",
"accepted": "ต้องยอมรับ %s",
@@ -337,12 +361,12 @@
},
"dont_receive_email": {
"title": "ตรวจสอบอีเมลของคุณ",
- "description": "หากคุณยังไม่ได้รับอีเมล ตรวจสอบว่าที่อยู่อีเมลของคุณถูกต้อง รวมถึงโฟลเดอร์อีเมลขยะของคุณ",
+ "description": "ตรวจสอบว่าที่อยู่อีเมลของคุณถูกต้องเช่นเดียวกับโฟลเดอร์อีเมลขยะหากคุณยังไม่ได้ทำ",
"resend_email": "ส่งอีเมลใหม่"
},
"open_email_app": {
"title": "ตรวจสอบกล่องขาเข้าของคุณ",
- "description": "เราเพิ่งส่งอีเมลหาคุณ หากคุณยังไม่ได้รับอีเมล โปรดตรวจสอบโฟลเดอร์อีเมลขยะ",
+ "description": "เราเพิ่งส่งอีเมลถึงคุณ ตรวจสอบโฟลเดอร์อีเมลขยะของคุณหากคุณยังไม่ได้ทำ",
"mail": "จดหมาย",
"open_email_client": "เปิดไคลเอ็นต์อีเมล"
}
@@ -385,8 +409,10 @@
"description_video": "อธิบายวิดีโอสำหรับผู้บกพร่องทางการมองเห็น...",
"load_failed": "การโหลดล้มเหลว",
"upload_failed": "การอัปโหลดล้มเหลว",
- "can_not_recognize_this_media_attachment": "Can not regonize this media attachment",
- "attachment_too_large": "ไฟล์แนบใหญ่เกินไป"
+ "can_not_recognize_this_media_attachment": "ไม่สามารถระบุไฟล์แนบสื่อนี้",
+ "attachment_too_large": "ไฟล์แนบใหญ่เกินไป",
+ "compressing_state": "กำลังบีบอัด...",
+ "server_processing_state": "เซิร์ฟเวอร์กำลังประมวลผล..."
},
"poll": {
"duration_time": "ระยะเวลา: %s",
@@ -396,7 +422,9 @@
"one_day": "1 วัน",
"three_days": "3 วัน",
"seven_days": "7 วัน",
- "option_number": "ตัวเลือก %ld"
+ "option_number": "ตัวเลือก %ld",
+ "the_poll_is_invalid": "การสำรวจความคิดเห็นไม่ถูกต้อง",
+ "the_poll_has_empty_option": "การสำรวจความคิดเห็นมีตัวเลือกที่ว่างเปล่า"
},
"content_warning": {
"placeholder": "เขียนคำเตือนที่ถูกต้องที่นี่..."
@@ -417,7 +445,9 @@
"custom_emoji_picker": "ตัวเลือกอีโมจิที่กำหนดเอง",
"enable_content_warning": "เปิดใช้งานคำเตือนเนื้อหา",
"disable_content_warning": "ปิดใช้งานคำเตือนเนื้อหา",
- "post_visibility_menu": "เมนูการมองเห็นโพสต์"
+ "post_visibility_menu": "เมนูการมองเห็นโพสต์",
+ "post_options": "ตัวเลือกโพสต์",
+ "posting_as": "กำลังโพสต์เป็น %s"
},
"keyboard": {
"discard_post": "ละทิ้งโพสต์",
@@ -433,15 +463,23 @@
"follows_you": "ติดตามคุณ"
},
"dashboard": {
- "posts": "โพสต์",
- "following": "กำลังติดตาม",
- "followers": "ผู้ติดตาม"
+ "my_posts": "โพสต์",
+ "my_following": "กำลังติดตาม",
+ "my_followers": "ผู้ติดตาม",
+ "other_posts": "โพสต์",
+ "other_following": "กำลังติดตาม",
+ "other_followers": "ผู้ติดตาม"
},
"fields": {
+ "joined": "เข้าร่วมเมื่อ",
"add_row": "เพิ่มแถว",
"placeholder": {
"label": "ป้ายชื่อ",
"content": "เนื้อหา"
+ },
+ "verified": {
+ "short": "ตรวจสอบเมื่อ %s",
+ "long": "ตรวจสอบความเป็นเจ้าของของลิงก์นี้เมื่อ %s"
}
},
"segmented_control": {
@@ -707,6 +745,18 @@
},
"bookmark": {
"title": "ที่คั่นหน้า"
+ },
+ "followed_tags": {
+ "title": "แท็กที่ติดตาม",
+ "header": {
+ "posts": "โพสต์",
+ "participants": "ผู้เข้าร่วม",
+ "posts_today": "โพสต์วันนี้"
+ },
+ "actions": {
+ "follow": "ติดตาม",
+ "unfollow": "เลิกติดตาม"
+ }
}
}
}
diff --git a/Localization/StringsConvertor/input/tr.lproj/Localizable.stringsdict b/Localization/StringsConvertor/input/tr.lproj/Localizable.stringsdict
index 29df92c2b..13552b607 100644
--- a/Localization/StringsConvertor/input/tr.lproj/Localizable.stringsdict
+++ b/Localization/StringsConvertor/input/tr.lproj/Localizable.stringsdict
@@ -50,6 +50,22 @@
%ld karakter
+ a11y.plural.count.characters_left
+
+ NSStringLocalizedFormatKey
+ %#@character_count@ kaldı
+ character_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 karakter
+ other
+ %ld karakter
+
+
plural.count.followed_by_and_mutual
NSStringLocalizedFormatKey
@@ -72,9 +88,9 @@
NSStringFormatValueTypeKey
ld
one
- Followed by %1$@, and another mutual
+ %1$@ ve bir ortak kişi tarafından takip edildi
other
- Followed by %1$@, and %ld mutuals
+ %1$@ ve %ld ortak kişi tarafından takip edildi
plural.count.metric_formatted.post
@@ -104,9 +120,9 @@
NSStringFormatValueTypeKey
ld
one
- 1 media
+ 1 medya
other
- %ld media
+ %ld medya
plural.count.post
diff --git a/Localization/StringsConvertor/input/tr.lproj/app.json b/Localization/StringsConvertor/input/tr.lproj/app.json
index 2abb92845..c22c6a3f9 100644
--- a/Localization/StringsConvertor/input/tr.lproj/app.json
+++ b/Localization/StringsConvertor/input/tr.lproj/app.json
@@ -51,6 +51,11 @@
"clean_cache": {
"title": "Önbelleği Temizle",
"message": "%s boyutunda önbellek temizlendi."
+ },
+ "translation_failed": {
+ "title": "Note",
+ "message": "Translation failed. Maybe the administrator has not enabled translations on this server or this server is running an older version of Mastodon where translations are not yet supported.",
+ "button": "OK"
}
},
"controls": {
@@ -75,9 +80,10 @@
"save_photo": "Fotoğrafı Kaydet",
"copy_photo": "Fotoğrafı Kopyala",
"sign_in": "Giriş Yap",
- "sign_up": "Kaydol",
+ "sign_up": "Hesap oluştur",
"see_more": "Daha Fazla Gör",
"preview": "Önizleme",
+ "copy": "Copy",
"share": "Paylaş",
"share_user": "%s ile paylaş",
"share_post": "Gönderiyi Paylaş",
@@ -91,12 +97,16 @@
"block_domain": "%s kişisini engelle",
"unblock_domain": "%s kişisinin engelini kaldır",
"settings": "Ayarlar",
- "delete": "Sil"
+ "delete": "Sil",
+ "translate_post": {
+ "title": "Translate from %s",
+ "unknown_language": "Unknown"
+ }
},
"tabs": {
"home": "Ana Sayfa",
- "search": "Arama",
- "notification": "Bildirimler",
+ "search_and_explore": "Ara ve Keşfet",
+ "notifications": "Bildirimler",
"profile": "Profil"
},
"keyboard": {
@@ -132,15 +142,17 @@
"sensitive_content": "Hassas İçerik",
"media_content_warning": "Göstermek için herhangi bir yere basın",
"tap_to_reveal": "Göstermek için basın",
+ "load_embed": "Load Embed",
+ "link_via_user": "%s via %s",
"poll": {
"vote": "Oy ver",
"closed": "Kapandı"
},
"meta_entity": {
- "url": "Link: %s",
- "hashtag": "Hashtag: %s",
- "mention": "Show Profile: %s",
- "email": "Email address: %s"
+ "url": "Bağlantı: %s",
+ "hashtag": "Etiket: %s",
+ "mention": "Profili Göster: %s",
+ "email": "E-posta adresi: %s"
},
"actions": {
"reply": "Yanıtla",
@@ -153,6 +165,7 @@
"show_image": "Görüntüyü göster",
"show_gif": "GIF'i göster",
"show_video_player": "Video oynatıcıyı göster",
+ "share_link_in_post": "Share Link in Post",
"tap_then_hold_to_show_menu": "Menüyü göstermek için dokunun ve basılı tutun"
},
"tag": {
@@ -168,6 +181,12 @@
"private": "Sadece gönderi sahibinin takipçileri bu gönderiyi görebilir.",
"private_from_me": "Sadece benim takipçilerim bu gönderiyi görebilir.",
"direct": "Sadece bahsedilen kullanıcı bu gönderiyi görebilir."
+ },
+ "translation": {
+ "translated_from": "Translated from %s using %s",
+ "unknown_language": "Unknown",
+ "unknown_provider": "Unknown",
+ "show_original": "Shown Original"
}
},
"friendship": {
@@ -187,8 +206,8 @@
"unmute_user": "Sesini aç %s",
"muted": "Susturuldu",
"edit_info": "Bilgiyi Düzenle",
- "show_reblogs": "Show Reblogs",
- "hide_reblogs": "Hide Reblogs"
+ "show_reblogs": "Yeniden Paylaşımları Göster",
+ "hide_reblogs": "Yeniden Paylaşımları Gizle"
},
"timeline": {
"filtered": "Filtrelenmiş",
@@ -218,10 +237,16 @@
"get_started": "Başlayın",
"log_in": "Oturum Aç"
},
+ "login": {
+ "title": "Tekrar hoş geldin",
+ "subtitle": "Hesabını oluşturduğun sunucuya giriş yap.",
+ "server_search_field": {
+ "placeholder": "Bir URL girin ya da sunucunuzu arayın"
+ }
+ },
"server_picker": {
"title": "Mastodon, farklı topluluklardaki kullanıcılardan oluşur.",
- "subtitle": "İlgi alanlarınıza, bölgenize veya genel amaçlı bir topluluk seçin.",
- "subtitle_extend": "İlgi alanlarınıza, bölgenize veya genel amaçlı bir topluluk seçin. Her topluluk tamamen bağımsız bir kuruluş veya kişi tarafından işletilmektedir.",
+ "subtitle": "Bölgenize dayalı, ilginize dayalı ya da genel amaçlı bir sunucu seçin. Hangi sunucuda olduğunuz fark etmeksizin Mastodon'daki herkes ile konuşabilirsiniz.",
"button": {
"category": {
"all": "Tümü",
@@ -248,8 +273,7 @@
"category": "KATEGORİ"
},
"input": {
- "placeholder": "Toplulukları ara",
- "search_servers_or_enter_url": "Sunucuları ara ya da bir bağlantı gir"
+ "search_servers_or_enter_url": "Topluluklar arayın ya da bir URL girin"
},
"empty_state": {
"finding_servers": "Mevcut sunucular aranıyor...",
@@ -383,10 +407,12 @@
"attachment_broken": "Bu %s bozuk ve Mastodon'a\nyüklenemiyor.",
"description_photo": "Görme engelliler için fotoğrafı tarif edin...",
"description_video": "Görme engelliler için videoyu tarif edin...",
- "load_failed": "Load Failed",
- "upload_failed": "Upload Failed",
- "can_not_recognize_this_media_attachment": "Can not regonize this media attachment",
- "attachment_too_large": "Attachment too large"
+ "load_failed": "Yükleme Başarısız",
+ "upload_failed": "Yükleme Başarısız",
+ "can_not_recognize_this_media_attachment": "Ekteki medya uzantısı görüntülenemiyor",
+ "attachment_too_large": "Ek boyutu çok büyük",
+ "compressing_state": "Sıkıştırılıyor...",
+ "server_processing_state": "Sunucu İşliyor..."
},
"poll": {
"duration_time": "Süre: %s",
@@ -396,7 +422,9 @@
"one_day": "1 Gün",
"three_days": "3 Gün",
"seven_days": "7 Gün",
- "option_number": "Seçenek %ld"
+ "option_number": "Seçenek %ld",
+ "the_poll_is_invalid": "Anket geçersiz",
+ "the_poll_has_empty_option": "The poll has empty option"
},
"content_warning": {
"placeholder": "Buraya kesin bir uyarı yazın..."
@@ -417,7 +445,9 @@
"custom_emoji_picker": "Özel Emoji Seçici",
"enable_content_warning": "İçerik Uyarısını Etkinleştir",
"disable_content_warning": "İçerik Uyarısını Kapat",
- "post_visibility_menu": "Gönderi Görünürlüğü Menüsü"
+ "post_visibility_menu": "Gönderi Görünürlüğü Menüsü",
+ "post_options": "Gönderi Seçenekleri",
+ "posting_as": "%s olarak paylaşılıyor"
},
"keyboard": {
"discard_post": "Gönderiyi İptal Et",
@@ -433,15 +463,23 @@
"follows_you": "Seni takip ediyor"
},
"dashboard": {
- "posts": "gönderiler",
- "following": "takip ediliyor",
- "followers": "takipçi"
+ "my_posts": "posts",
+ "my_following": "following",
+ "my_followers": "followers",
+ "other_posts": "posts",
+ "other_following": "following",
+ "other_followers": "followers"
},
"fields": {
+ "joined": "Joined",
"add_row": "Satır Ekle",
"placeholder": {
"label": "Etiket",
"content": "İçerik"
+ },
+ "verified": {
+ "short": "%s tarafında onaylı",
+ "long": "%s adresinin sahipliği kontrol edilmiş"
}
},
"segmented_control": {
@@ -469,12 +507,12 @@
"message": "%s engellemeyi kaldırmayı onaylayın"
},
"confirm_show_reblogs": {
- "title": "Show Reblogs",
- "message": "Confirm to show reblogs"
+ "title": "Yeniden Paylaşımları Göster",
+ "message": "Yeniden paylaşımları göstermeyi onayla"
},
"confirm_hide_reblogs": {
- "title": "Hide Reblogs",
- "message": "Confirm to hide reblogs"
+ "title": "Yeniden Paylaşımları Gizle",
+ "message": "Yeniden paylaşımları gizlemeyi onayla"
}
},
"accessibility": {
@@ -493,8 +531,8 @@
"footer": "Diğer sunucudaki takip edilenler gösterilemiyor."
},
"familiarFollowers": {
- "title": "Followers you familiar",
- "followed_by_names": "Followed by %s"
+ "title": "Tanıyor olabileceğin takipçiler",
+ "followed_by_names": "%s tarafından takip ediliyor"
},
"favorited_by": {
"title": "Favorited By"
@@ -566,10 +604,10 @@
"show_mentions": "Bahsetmeleri Göster"
},
"follow_request": {
- "accept": "Accept",
- "accepted": "Accepted",
- "reject": "reject",
- "rejected": "Rejected"
+ "accept": "Kabul Et",
+ "accepted": "Kabul Edildi",
+ "reject": "Reddet",
+ "rejected": "Reddedildi"
}
},
"thread": {
@@ -654,9 +692,9 @@
"i_dont_like_it": "Beğenmedim",
"it_is_not_something_you_want_to_see": "Görmek isteyeceğim bir şey değil",
"its_spam": "Spam",
- "malicious_links_fake_engagement_or_repetetive_replies": "Malicious links, fake engagement, or repetetive replies",
+ "malicious_links_fake_engagement_or_repetetive_replies": "Kötü niyetli bağlantılar, sahte etkileşim veya tekrarlayan yanıtlar",
"it_violates_server_rules": "Sunucu kurallarını ihlal ediyor",
- "you_are_aware_that_it_breaks_specific_rules": "You are aware that it breaks specific rules",
+ "you_are_aware_that_it_breaks_specific_rules": "Belirli kuralları ihlal ettiğinin farkındasınız",
"its_something_else": "Başka bir şey",
"the_issue_does_not_fit_into_other_categories": "Sorun bunlardan biri değil"
},
@@ -677,15 +715,15 @@
},
"step_final": {
"dont_want_to_see_this": "Bunu görmek istemiyor musunuz?",
- "when_you_see_something_you_dont_like_on_mastodon_you_can_remove_the_person_from_your_experience.": "When you see something you don’t like on Mastodon, you can remove the person from your experience.",
+ "when_you_see_something_you_dont_like_on_mastodon_you_can_remove_the_person_from_your_experience.": "Mastodon'da beğenmediğiniz bir şey gördüğünüzde, o kişiyi deneyiminizden çıkarabilirsiniz.",
"unfollow": "Takibi bırak",
- "unfollowed": "Unfollowed",
+ "unfollowed": "Takipten çıkıldı",
"unfollow_user": "Takipten çık %s",
"mute_user": "Sustur %s",
"you_wont_see_their_posts_or_reblogs_in_your_home_feed_they_wont_know_they_ve_been_muted": "You won’t see their posts or reblogs in your home feed. They won’t know they’ve been muted.",
- "block_user": "Block %s",
- "they_will_no_longer_be_able_to_follow_or_see_your_posts_but_they_can_see_if_theyve_been_blocked": "They will no longer be able to follow or see your posts, but they can see if they’ve been blocked.",
- "while_we_review_this_you_can_take_action_against_user": "While we review this, you can take action against %s"
+ "block_user": "%s kişisini engelle",
+ "they_will_no_longer_be_able_to_follow_or_see_your_posts_but_they_can_see_if_theyve_been_blocked": "Artık sizi takip edemez ve gönderilerinizi göremezler ama engellendiklerini görebilirler.",
+ "while_we_review_this_you_can_take_action_against_user": "Biz bunu incelerken siz %s hesabına karşı önlem alabilirsiniz"
}
},
"preview": {
@@ -706,7 +744,19 @@
"accessibility_hint": "Bu yardımı kapatmak için çift tıklayın"
},
"bookmark": {
- "title": "Bookmarks"
+ "title": "Yer İmleri"
+ },
+ "followed_tags": {
+ "title": "Takip Edilen Etiketler",
+ "header": {
+ "posts": "gönderiler",
+ "participants": "katılımcılar",
+ "posts_today": "posts today"
+ },
+ "actions": {
+ "follow": "Takip et",
+ "unfollow": "Takibi bırak"
+ }
}
}
}
diff --git a/Localization/StringsConvertor/input/uk.lproj/Localizable.stringsdict b/Localization/StringsConvertor/input/uk.lproj/Localizable.stringsdict
index cdf35477e..21681e1b9 100644
--- a/Localization/StringsConvertor/input/uk.lproj/Localizable.stringsdict
+++ b/Localization/StringsConvertor/input/uk.lproj/Localizable.stringsdict
@@ -13,19 +13,19 @@
NSStringFormatValueTypeKey
ld
one
- 1 unread notification
+ 1 не прочитане сповіщення
few
- %ld unread notification
+ %ld не прочитаних сповіщень
many
- %ld unread notification
+ %ld не прочитаних сповіщень
other
- %ld unread notification
+ %ld не прочитаних сповіщень
a11y.plural.count.input_limit_exceeds
NSStringLocalizedFormatKey
- Input limit exceeds %#@character_count@
+ Перевищено ліміт вводу на %#@character_count@
character_count
NSStringFormatSpecTypeKey
@@ -33,19 +33,19 @@
NSStringFormatValueTypeKey
ld
one
- 1 character
+ 1 символ
few
- %ld characters
+ %ld символи
many
- %ld characters
+ %ld символів
other
- %ld characters
+ %ld символів
a11y.plural.count.input_limit_remains
NSStringLocalizedFormatKey
- Input limit remains %#@character_count@
+ Залишається вхідний ліміт %#@character_count@
character_count
NSStringFormatSpecTypeKey
@@ -53,13 +53,33 @@
NSStringFormatValueTypeKey
ld
one
- 1 character
+ 1 символ
few
- %ld characters
+ %ld символи
many
- %ld characters
+ %ld символів
other
- %ld characters
+ %ld символів
+
+
+ a11y.plural.count.characters_left
+
+ NSStringLocalizedFormatKey
+ %#@character_count@ ліворуч
+ character_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ one
+ 1 символ
+ few
+ %ld символи
+ many
+ %ld символів
+ other
+ %ld символів
plural.count.followed_by_and_mutual
@@ -88,13 +108,13 @@
NSStringFormatValueTypeKey
ld
one
- Followed by %1$@, and another mutual
+ Читають %1$@ та інші
few
- Followed by %1$@, and %ld mutuals
+ Читають %1$@, та %ld взаємних
many
- Followed by %1$@, and %ld mutuals
+ Читають %1$@, та %ld взаємних
other
- Followed by %1$@, and %ld mutuals
+ Читають %1$@, та %ld взаємних
plural.count.metric_formatted.post
@@ -108,13 +128,13 @@
NSStringFormatValueTypeKey
ld
one
- post
+ допис
few
- posts
+ дописи
many
- posts
+ дописів
other
- posts
+ дописів
plural.count.media
@@ -128,13 +148,13 @@
NSStringFormatValueTypeKey
ld
one
- 1 media
+ медіа
few
- %ld media
+ %ld медіа
many
- %ld media
+ %ld медіа
other
- %ld media
+ %ld медіа
plural.count.post
@@ -148,13 +168,13 @@
NSStringFormatValueTypeKey
ld
one
- 1 post
+ 1 пост
few
- %ld posts
+ %ld пости
many
- %ld posts
+ %ld постів
other
- %ld posts
+ %ld постів
plural.count.favorite
@@ -168,13 +188,13 @@
NSStringFormatValueTypeKey
ld
one
- 1 favorite
+ 1 улюблене
few
- %ld favorites
+ %ld улюблених
many
- %ld favorites
+ %ld улюблених
other
- %ld favorites
+ %ld улюблених
plural.count.reblog
@@ -188,13 +208,13 @@
NSStringFormatValueTypeKey
ld
one
- 1 reblog
+ 1 репост
few
- %ld reblogs
+ %ld репости
many
- %ld reblogs
+ %ld репостів
other
- %ld reblogs
+ %ld репостів
plural.count.reply
@@ -208,13 +228,13 @@
NSStringFormatValueTypeKey
ld
one
- 1 reply
+ 1 відповідь
few
- %ld replies
+ %ld відповіді
many
- %ld replies
+ %ld відповідей
other
- %ld replies
+ %ld відповідей
plural.count.vote
@@ -228,13 +248,13 @@
NSStringFormatValueTypeKey
ld
one
- 1 vote
+ 1 голос
few
- %ld votes
+ %ld голоси
many
- %ld votes
+ %ld голосів
other
- %ld votes
+ %ld голосів
plural.count.voter
@@ -248,13 +268,13 @@
NSStringFormatValueTypeKey
ld
one
- 1 voter
+ 1 учасник голосування
few
- %ld voters
+ %ld учасники голосування
many
- %ld voters
+ %ld учасників голосування
other
- %ld voters
+ %ld учасників голосування
plural.people_talking
@@ -268,13 +288,13 @@
NSStringFormatValueTypeKey
ld
one
- 1 people talking
+ 1 людина говорить
few
- %ld people talking
+ %ld людей говорять
many
- %ld people talking
+ %ld людей говорять
other
- %ld people talking
+ %ld людей говорять
plural.count.following
@@ -288,13 +308,13 @@
NSStringFormatValueTypeKey
ld
one
- 1 following
+ підписаний
few
- %ld following
+ Підписаний на %ld
many
- %ld following
+ Підписаний на %ld
other
- %ld following
+ Підписаний на %ld
plural.count.follower
@@ -308,13 +328,13 @@
NSStringFormatValueTypeKey
ld
one
- 1 follower
+ 1 підписник
few
- %ld followers
+ %ld підписників
many
- %ld followers
+ %ld підписників
other
- %ld followers
+ %ld підписників
date.year.left
@@ -328,13 +348,13 @@
NSStringFormatValueTypeKey
ld
one
- 1 year left
+ залишився 1 рік
few
- %ld years left
+ %ld років залишилося
many
- %ld years left
+ %ld років залишилося
other
- %ld years left
+ %ld років залишилося
date.month.left
@@ -348,13 +368,13 @@
NSStringFormatValueTypeKey
ld
one
- 1 months left
+ 1 місяць залишився
few
- %ld months left
+ %ld місяців залишилося
many
- %ld months left
+ %ld місяців залишилося
other
- %ld months left
+ %ld місяців залишилося
date.day.left
@@ -368,13 +388,13 @@
NSStringFormatValueTypeKey
ld
one
- 1 day left
+ Лишився 1 день
few
- %ld days left
+ %ld днів залишилося
many
- %ld days left
+ %ld днів залишилося
other
- %ld days left
+ %ld днів залишилося
date.hour.left
@@ -388,13 +408,13 @@
NSStringFormatValueTypeKey
ld
one
- 1 hour left
+ Залишилася 1 година
few
- %ld hours left
+ %ld годин залишилося
many
- %ld hours left
+ %ld годин залишилося
other
- %ld hours left
+ %ld годин залишилося
date.minute.left
@@ -408,13 +428,13 @@
NSStringFormatValueTypeKey
ld
one
- 1 minute left
+ Залишилась одна хвилина
few
- %ld minutes left
+ %ld хвилин залишилося
many
- %ld minutes left
+ %ld хвилин залишилося
other
- %ld minutes left
+ %ld хвилин залишилося
date.second.left
@@ -428,13 +448,13 @@
NSStringFormatValueTypeKey
ld
one
- 1 second left
+ Залишилась одна секунда
few
- %ld seconds left
+ %ld секунд залишилося
many
- %ld seconds left
+ %ld секунд залишилося
other
- %ld seconds left
+ %ld секунд залишилося
date.year.ago.abbr
@@ -448,13 +468,13 @@
NSStringFormatValueTypeKey
ld
one
- 1y ago
+ 1 рік тому
few
- %ldy ago
+ %ld роки тому
many
- %ldy ago
+ %ld років тому
other
- %ldy ago
+ %ld років тому
date.month.ago.abbr
@@ -468,13 +488,13 @@
NSStringFormatValueTypeKey
ld
one
- 1M ago
+ 1 місяць тому
few
- %ldM ago
+ %ld місяці тому
many
- %ldM ago
+ %ld місяців тому
other
- %ldM ago
+ %ld Місяців тому
date.day.ago.abbr
@@ -488,13 +508,13 @@
NSStringFormatValueTypeKey
ld
one
- 1d ago
+ 1 день тому
few
- %ldd ago
+ %ld дня тому
many
- %ldd ago
+ %ld днів тому
other
- %ldd ago
+ %ld днів тому
date.hour.ago.abbr
@@ -508,13 +528,13 @@
NSStringFormatValueTypeKey
ld
one
- 1h ago
+ 1 годину тому
few
- %ldh ago
+ %ld години тому
many
- %ldh ago
+ %ld годин тому
other
- %ldh ago
+ %ld годин тому
date.minute.ago.abbr
@@ -528,13 +548,13 @@
NSStringFormatValueTypeKey
ld
one
- 1m ago
+ 1 хвилину тому
few
- %ldm ago
+ %ld хвилини тому
many
- %ldm ago
+ %ld хвилин тому
other
- %ldm ago
+ %ld хвилин тому
date.second.ago.abbr
@@ -548,13 +568,13 @@
NSStringFormatValueTypeKey
ld
one
- 1s ago
+ 1 секунду тому
few
- %lds ago
+ %ld секунди тому
many
- %lds ago
+ %ld секунд тому
other
- %lds ago
+ %ld секунд тому
diff --git a/Localization/StringsConvertor/input/uk.lproj/app.json b/Localization/StringsConvertor/input/uk.lproj/app.json
index a6a971860..230d61f7c 100644
--- a/Localization/StringsConvertor/input/uk.lproj/app.json
+++ b/Localization/StringsConvertor/input/uk.lproj/app.json
@@ -2,711 +2,761 @@
"common": {
"alerts": {
"common": {
- "please_try_again": "Please try again.",
- "please_try_again_later": "Please try again later."
+ "please_try_again": "Будь ласка, спробуйте ще раз.",
+ "please_try_again_later": "Будь ласка, спробуйте пізніше."
},
"sign_up_failure": {
- "title": "Sign Up Failure"
+ "title": "Помилка реєстрації"
},
"server_error": {
- "title": "Server Error"
+ "title": "Помилка сервера"
},
"vote_failure": {
- "title": "Vote Failure",
- "poll_ended": "The poll has ended"
+ "title": "Помилка голосування",
+ "poll_ended": "Опитування завершено"
},
"discard_post_content": {
- "title": "Discard Draft",
- "message": "Confirm to discard composed post content."
+ "title": "Видалити чернетку",
+ "message": "Підтвердьте, щоб відхилити створений вміст публікації."
},
"publish_post_failure": {
- "title": "Publish Failure",
- "message": "Failed to publish the post.\nPlease check your internet connection.",
+ "title": "Помилка публікації",
+ "message": "Не вдалося опублікувати допис.\nПеревірте підключення до Інтернету.",
"attachments_message": {
- "video_attach_with_photo": "Cannot attach a video to a post that already contains images.",
- "more_than_one_video": "Cannot attach more than one video."
+ "video_attach_with_photo": "Не можна додати відео до допису з зображенням.",
+ "more_than_one_video": "Не можна додати більше ніж 1 відео."
}
},
"edit_profile_failure": {
- "title": "Edit Profile Error",
- "message": "Cannot edit profile. Please try again."
+ "title": "Помилка редагування профілю",
+ "message": "Не вдалося редагувати профіль. Будь ласка, спробуйте ще раз."
},
"sign_out": {
- "title": "Sign Out",
- "message": "Are you sure you want to sign out?",
- "confirm": "Sign Out"
+ "title": "Вийти",
+ "message": "Ви справді бажаєте вийти?",
+ "confirm": "Вийти"
},
"block_domain": {
- "title": "Are you really, really sure you want to block the entire %s? In most cases a few targeted blocks or mutes are sufficient and preferable. You will not see content from that domain and any of your followers from that domain will be removed.",
- "block_entire_domain": "Block Domain"
+ "title": "Ви точно, точно впевнені, що хочете заблокувати весь домен %s? У більшості випадків для нормальної роботи краще заблокувати або приховати лише деяких користувачів. Ви не зможете бачити контент з цього домену у будь-яких стрічках або ваших сповіщеннях. Ваші підписники з цього домену будуть відписані від вас.",
+ "block_entire_domain": "Заблокувати домен"
},
"save_photo_failure": {
- "title": "Save Photo Failure",
- "message": "Please enable the photo library access permission to save the photo."
+ "title": "Помилка збереження фото",
+ "message": "Будь ласка, увімкніть доступ до фотобібліотеки для збереження фотографій."
},
"delete_post": {
- "title": "Delete Post",
- "message": "Are you sure you want to delete this post?"
+ "title": "Видалити допис",
+ "message": "Ви дійсно бажаєте видалити цей допис?"
},
"clean_cache": {
- "title": "Clean Cache",
- "message": "Successfully cleaned %s cache."
+ "title": "Очистити кеш",
+ "message": "%s успішно очищено."
+ },
+ "translation_failed": {
+ "title": "Note",
+ "message": "Translation failed. Maybe the administrator has not enabled translations on this server or this server is running an older version of Mastodon where translations are not yet supported.",
+ "button": "OK"
}
},
"controls": {
"actions": {
- "back": "Back",
- "next": "Next",
- "previous": "Previous",
- "open": "Open",
- "add": "Add",
- "remove": "Remove",
- "edit": "Edit",
- "save": "Save",
+ "back": "Назад",
+ "next": "Далі",
+ "previous": "Попередній",
+ "open": "Відкрити",
+ "add": "Додати",
+ "remove": "Видалити",
+ "edit": "Редагувати",
+ "save": "Зберегти",
"ok": "OK",
- "done": "Done",
- "confirm": "Confirm",
- "continue": "Continue",
- "compose": "Compose",
- "cancel": "Cancel",
- "discard": "Discard",
- "try_again": "Try Again",
- "take_photo": "Take Photo",
- "save_photo": "Save Photo",
- "copy_photo": "Copy Photo",
- "sign_in": "Sign In",
- "sign_up": "Sign Up",
- "see_more": "See More",
- "preview": "Preview",
- "share": "Share",
- "share_user": "Share %s",
- "share_post": "Share Post",
- "open_in_safari": "Open in Safari",
- "open_in_browser": "Open in Browser",
- "find_people": "Find people to follow",
- "manually_search": "Manually search instead",
- "skip": "Skip",
- "reply": "Reply",
- "report_user": "Report %s",
- "block_domain": "Block %s",
- "unblock_domain": "Unblock %s",
- "settings": "Settings",
- "delete": "Delete"
+ "done": "Готово",
+ "confirm": "Підтвердити",
+ "continue": "Далі",
+ "compose": "Створити",
+ "cancel": "Скасувати",
+ "discard": "Відхилити",
+ "try_again": "Спробувати ще раз",
+ "take_photo": "Зробити фото",
+ "save_photo": "Зберегти фото",
+ "copy_photo": "Копіювати фото",
+ "sign_in": "Увійти",
+ "sign_up": "Створити обліковий запис",
+ "see_more": "Дивіться більше",
+ "preview": "Попередній перегляд",
+ "copy": "Copy",
+ "share": "Поділитись",
+ "share_user": "Поділитися %s",
+ "share_post": "Поділитися записом",
+ "open_in_safari": "Відкрити у Safari",
+ "open_in_browser": "Відкрити в браузері",
+ "find_people": "Знайти людей, аби підписатися",
+ "manually_search": "Натомість шукати вручну",
+ "skip": "Пропустити",
+ "reply": "Відповісти",
+ "report_user": "Поскаржитись на %s",
+ "block_domain": "Блокувати %s",
+ "unblock_domain": "Розблокувати %s",
+ "settings": "Налаштування",
+ "delete": "Видалити",
+ "translate_post": {
+ "title": "Translate from %s",
+ "unknown_language": "Unknown"
+ }
},
"tabs": {
- "home": "Home",
- "search": "Search",
- "notification": "Notification",
- "profile": "Profile"
+ "home": "Головна",
+ "search_and_explore": "Search and Explore",
+ "notifications": "Notifications",
+ "profile": "Профіль"
},
"keyboard": {
"common": {
- "switch_to_tab": "Switch to %s",
- "compose_new_post": "Compose New Post",
- "show_favorites": "Show Favorites",
- "open_settings": "Open Settings"
+ "switch_to_tab": "Перейти до користувача %s",
+ "compose_new_post": "Написати новий допис",
+ "show_favorites": "Показати вибрані",
+ "open_settings": "Відкрити параметри"
},
"timeline": {
- "previous_status": "Previous Post",
- "next_status": "Next Post",
- "open_status": "Open Post",
- "open_author_profile": "Open Author's Profile",
- "open_reblogger_profile": "Open Reblogger's Profile",
- "reply_status": "Reply to Post",
- "toggle_reblog": "Toggle Reblog on Post",
- "toggle_favorite": "Toggle Favorite on Post",
- "toggle_content_warning": "Toggle Content Warning",
- "preview_image": "Preview Image"
+ "previous_status": "Попередній допис",
+ "next_status": "Новий допис",
+ "open_status": "Відкрити допис",
+ "open_author_profile": "Відкрити профіль автора",
+ "open_reblogger_profile": "Відкрити профіль реблогера",
+ "reply_status": "Відповісти на допис",
+ "toggle_reblog": "Додати/прибрати Реблог",
+ "toggle_favorite": "Додати/прибрати з Обраного",
+ "toggle_content_warning": "Додати/прибрати попередження про вміст для дорослих",
+ "preview_image": "Попередній перегляд"
},
"segmented_control": {
- "previous_section": "Previous Section",
- "next_section": "Next Section"
+ "previous_section": "Попередній розділ",
+ "next_section": "Наступний розділ"
}
},
"status": {
- "user_reblogged": "%s reblogged",
- "user_replied_to": "Replied to %s",
- "show_post": "Show Post",
- "show_user_profile": "Show user profile",
- "content_warning": "Content Warning",
- "sensitive_content": "Sensitive Content",
- "media_content_warning": "Tap anywhere to reveal",
- "tap_to_reveal": "Tap to reveal",
+ "user_reblogged": "%s Зробив репост",
+ "user_replied_to": "Відповів %s",
+ "show_post": "Показати дописи",
+ "show_user_profile": "Показати профіль користувача",
+ "content_warning": "Попередження про вміст",
+ "sensitive_content": "Контент 18+",
+ "media_content_warning": "Натисніть будь-де, щоб показати більше",
+ "tap_to_reveal": "Натисніть, щоб відобразити",
+ "load_embed": "Load Embed",
+ "link_via_user": "%s via %s",
"poll": {
- "vote": "Vote",
- "closed": "Closed"
+ "vote": "Проголосувати",
+ "closed": "Зачинено"
},
"meta_entity": {
- "url": "Link: %s",
- "hashtag": "Hashtag: %s",
- "mention": "Show Profile: %s",
- "email": "Email address: %s"
+ "url": "Посилання: %s",
+ "hashtag": "Гештег: %s",
+ "mention": "Показати профіль: %s",
+ "email": "Електронна адреса: %s"
},
"actions": {
- "reply": "Reply",
- "reblog": "Reblog",
- "unreblog": "Undo reblog",
- "favorite": "Favorite",
- "unfavorite": "Unfavorite",
- "menu": "Menu",
- "hide": "Hide",
- "show_image": "Show image",
- "show_gif": "Show GIF",
- "show_video_player": "Show video player",
- "tap_then_hold_to_show_menu": "Tap then hold to show menu"
+ "reply": "Відповісти",
+ "reblog": "Репост",
+ "unreblog": "Скасувати репост",
+ "favorite": "Улюблене",
+ "unfavorite": "Вилучити з улюбленого",
+ "menu": "Меню",
+ "hide": "Сховати",
+ "show_image": "Показати зображення",
+ "show_gif": "Показати GIF",
+ "show_video_player": "Показати відеоплеєр",
+ "share_link_in_post": "Share Link in Post",
+ "tap_then_hold_to_show_menu": "Натисніть та утримуйте, щоб показати меню"
},
"tag": {
- "url": "URL",
- "mention": "Mention",
- "link": "Link",
- "hashtag": "Hashtag",
- "email": "Email",
- "emoji": "Emoji"
+ "url": "Адреса URL",
+ "mention": "Згадати",
+ "link": "Посилання",
+ "hashtag": "Гештег",
+ "email": "Електронна пошта",
+ "emoji": "Емодзі"
},
"visibility": {
- "unlisted": "Everyone can see this post but not display in the public timeline.",
- "private": "Only their followers can see this post.",
- "private_from_me": "Only my followers can see this post.",
- "direct": "Only mentioned user can see this post."
+ "unlisted": "Хто завгодно може бачити цей допис, але його не буде відображено у публічній стрічці.",
+ "private": "Лише їхні підписники можуть бачити цю публікацію.",
+ "private_from_me": "Тільки мої підписники можуть бачити цю публікацію.",
+ "direct": "Тільки згаданий користувач може бачити цю публікацію."
+ },
+ "translation": {
+ "translated_from": "Translated from %s using %s",
+ "unknown_language": "Unknown",
+ "unknown_provider": "Unknown",
+ "show_original": "Shown Original"
}
},
"friendship": {
- "follow": "Follow",
- "following": "Following",
- "request": "Request",
- "pending": "Pending",
- "block": "Block",
- "block_user": "Block %s",
- "block_domain": "Block %s",
- "unblock": "Unblock",
- "unblock_user": "Unblock %s",
- "blocked": "Blocked",
- "mute": "Mute",
- "mute_user": "Mute %s",
- "unmute": "Unmute",
- "unmute_user": "Unmute %s",
- "muted": "Muted",
- "edit_info": "Edit Info",
- "show_reblogs": "Show Reblogs",
- "hide_reblogs": "Hide Reblogs"
+ "follow": "Підписатися",
+ "following": "Підписаний",
+ "request": "Запит",
+ "pending": "Очікується",
+ "block": "Заблокувати",
+ "block_user": "Блокувати %s",
+ "block_domain": "Блокувати %s",
+ "unblock": "Розблокувати",
+ "unblock_user": "Розблокувати %s",
+ "blocked": "Заблоковано",
+ "mute": "Заглушити",
+ "mute_user": "Заглушити %s",
+ "unmute": "Сповіщати",
+ "unmute_user": "Сповіщати %s",
+ "muted": "Зам'ютити",
+ "edit_info": "Редагувати інформацію",
+ "show_reblogs": "Показати реблоги",
+ "hide_reblogs": "Сховати реблоги"
},
"timeline": {
- "filtered": "Filtered",
+ "filtered": "Відфільтровано",
"timestamp": {
- "now": "Now"
+ "now": "Щойно"
},
"loader": {
- "load_missing_posts": "Load missing posts",
- "loading_missing_posts": "Loading missing posts...",
- "show_more_replies": "Show more replies"
+ "load_missing_posts": "Завантажити пропущені дописи",
+ "loading_missing_posts": "Завантаження пропущених дописів...",
+ "show_more_replies": "Показати більше відповідей"
},
"header": {
- "no_status_found": "No Post Found",
- "blocking_warning": "You can’t view this user's profile\nuntil you unblock them.\nYour profile looks like this to them.",
- "user_blocking_warning": "You can’t view %s’s profile\nuntil you unblock them.\nYour profile looks like this to them.",
- "blocked_warning": "You can’t view this user’s profile\nuntil they unblock you.",
- "user_blocked_warning": "You can’t view %s’s profile\nuntil they unblock you.",
- "suspended_warning": "This user has been suspended.",
- "user_suspended_warning": "%s’s account has been suspended."
+ "no_status_found": "Публікацій не знайдено",
+ "blocking_warning": "Ви не можете переглянути профіль цього користувача, поки ви не розблокуєте їх.\nВаш профіль виглядає так.",
+ "user_blocking_warning": "Ви не можете переглядати профіль %s, поки ви не розблокуєте їх.\nВони бачать ваш профіль так.",
+ "blocked_warning": "Ви не можете переглянути профіль цього користувача, поки вони не розблокують вас.",
+ "user_blocked_warning": "Ви не можете переглянути профіль %s, поки він не розблокує вас.",
+ "suspended_warning": "Цього користувача було заблоковано.",
+ "user_suspended_warning": "Обліковий запис %s заблоковано."
}
}
}
},
"scene": {
"welcome": {
- "slogan": "Social networking\nback in your hands.",
- "get_started": "Get Started",
- "log_in": "Log In"
+ "slogan": "Соціальна мережа під вашим контролем.",
+ "get_started": "Почати",
+ "log_in": "Увійти"
+ },
+ "login": {
+ "title": "З поверненням",
+ "subtitle": "Увійдіть на сервері, де ви створили свій обліковий запис.",
+ "server_search_field": {
+ "placeholder": "Введіть URL-адресу або адресу сервера"
+ }
},
"server_picker": {
- "title": "Mastodon is made of users in different servers.",
- "subtitle": "Pick a server based on your interests, region, or a general purpose one.",
- "subtitle_extend": "Pick a server based on your interests, region, or a general purpose one. Each server is operated by an entirely independent organization or individual.",
+ "title": "Mastodon складається з користувачів на різних серверах.",
+ "subtitle": "Оберіть сервер згідно з вашим регіоном, інтересами чи цілями. Ви можете спілкуватися з будь-ким на Mastodon, незалежно від обраних серверів.",
"button": {
"category": {
- "all": "All",
- "all_accessiblity_description": "Category: All",
- "academia": "academia",
- "activism": "activism",
- "food": "food",
- "furry": "furry",
- "games": "games",
- "general": "general",
- "journalism": "journalism",
- "lgbt": "lgbt",
- "regional": "regional",
- "art": "art",
- "music": "music",
- "tech": "tech"
+ "all": "Всі",
+ "all_accessiblity_description": "Категорія: Усі",
+ "academia": "академія",
+ "activism": "активізм",
+ "food": "їжа",
+ "furry": "фурі",
+ "games": "ігри",
+ "general": "загальне",
+ "journalism": "журналістика",
+ "lgbt": "лгбт",
+ "regional": "регіональний",
+ "art": "мистецтво",
+ "music": "музика",
+ "tech": "технології"
},
- "see_less": "See Less",
- "see_more": "See More"
+ "see_less": "Згорнути",
+ "see_more": "Показати більше"
},
"label": {
- "language": "LANGUAGE",
- "users": "USERS",
- "category": "CATEGORY"
+ "language": "МОВА",
+ "users": "КОРИСТУВАЧІ",
+ "category": "КАТЕГОРІЯ"
},
"input": {
- "placeholder": "Search servers",
- "search_servers_or_enter_url": "Search servers or enter URL"
+ "search_servers_or_enter_url": "Знайти спільноти, або ввести URL"
},
"empty_state": {
- "finding_servers": "Finding available servers...",
- "bad_network": "Something went wrong while loading the data. Check your internet connection.",
- "no_results": "No results"
+ "finding_servers": "Пошук доступних серверів...",
+ "bad_network": "Сталася помилка під час завантаження даних. Перевірте підключення до Інтернету.",
+ "no_results": "Жодних результатів"
}
},
"register": {
- "title": "Let’s get you set up on %s",
- "lets_get_you_set_up_on_domain": "Let’s get you set up on %s",
+ "title": "Налаштуймо Вас на %s",
+ "lets_get_you_set_up_on_domain": "Налаштуймо Вас на %s",
"input": {
"avatar": {
- "delete": "Delete"
+ "delete": "Видалити"
},
"username": {
- "placeholder": "username",
- "duplicate_prompt": "This username is taken."
+ "placeholder": "ім'я користувача",
+ "duplicate_prompt": "Це ім'я користувача вже зайняте."
},
"display_name": {
- "placeholder": "display name"
+ "placeholder": "видиме ім'я"
},
"email": {
- "placeholder": "email"
+ "placeholder": "електронна пошта"
},
"password": {
- "placeholder": "password",
- "require": "Your password needs at least:",
- "character_limit": "8 characters",
+ "placeholder": "пароль",
+ "require": "Ваш пароль повинен містити як мінімум:",
+ "character_limit": "8 символів",
"accessibility": {
- "checked": "checked",
- "unchecked": "unchecked"
+ "checked": "встановлено",
+ "unchecked": "знято позначку"
},
- "hint": "Your password needs at least eight characters"
+ "hint": "Ваш пароль повинен містити принаймні вісім символів"
},
"invite": {
- "registration_user_invite_request": "Why do you want to join?"
+ "registration_user_invite_request": "Чому ви хочете приєднатися?"
}
},
"error": {
"item": {
- "username": "Username",
- "email": "Email",
- "password": "Password",
- "agreement": "Agreement",
- "locale": "Locale",
- "reason": "Reason"
+ "username": "Ім'я користувача",
+ "email": "Електронна пошта",
+ "password": "Пароль",
+ "agreement": "Угода",
+ "locale": "Локаль",
+ "reason": "Підстава"
},
"reason": {
- "blocked": "%s contains a disallowed email provider",
- "unreachable": "%s does not seem to exist",
- "taken": "%s is already in use",
- "reserved": "%s is a reserved keyword",
- "accepted": "%s must be accepted",
- "blank": "%s is required",
- "invalid": "%s is invalid",
- "too_long": "%s is too long",
- "too_short": "%s is too short",
- "inclusion": "%s is not a supported value"
+ "blocked": "%s містить заборонених провайдерів email",
+ "unreachable": "%s здається, не існує",
+ "taken": "%s вже використовується",
+ "reserved": "%s є зарезервованим ключовим словом",
+ "accepted": "%s має бути прийнято",
+ "blank": "%s необхідно",
+ "invalid": "%s є недійсним",
+ "too_long": "%s занадто довгий",
+ "too_short": "%s є закоротким",
+ "inclusion": "%s не є підтримуваним значенням"
},
"special": {
- "username_invalid": "Username must only contain alphanumeric characters and underscores",
- "username_too_long": "Username is too long (can’t be longer than 30 characters)",
- "email_invalid": "This is not a valid email address",
- "password_too_short": "Password is too short (must be at least 8 characters)"
+ "username_invalid": "Ім'я користувача повинно містити лише літери, цифрові символи та знак підкреслення",
+ "username_too_long": "Ім'я користувача занадто довге (не може бути більше 30 символів)",
+ "email_invalid": "Це не дійсна адреса електронної пошти",
+ "password_too_short": "Пароль закороткий (має містити мінімум 8 символів)"
}
}
},
"server_rules": {
- "title": "Some ground rules.",
- "subtitle": "These are set and enforced by the %s moderators.",
- "prompt": "By continuing, you’re subject to the terms of service and privacy policy for %s.",
- "terms_of_service": "terms of service",
- "privacy_policy": "privacy policy",
+ "title": "Деякі основні правила.",
+ "subtitle": "Ці призначені для модераторів - %s.",
+ "prompt": "Продовжуючи, ви піддаєтеся умовам надання послуг та політики конфіденційності для хвороби %s.",
+ "terms_of_service": "умови використання",
+ "privacy_policy": "політика конфіденційності",
"button": {
- "confirm": "I Agree"
+ "confirm": "Я погоджуюся"
}
},
"confirm_email": {
- "title": "One last thing.",
- "subtitle": "Tap the link we emailed to you to verify your account.",
- "tap_the_link_we_emailed_to_you_to_verify_your_account": "Tap the link we emailed to you to verify your account",
+ "title": "Остання річ.",
+ "subtitle": "Натисніть на посилання, яке ми надіслали на вашу пошту, щоб підтвердити свій обліковий запис.",
+ "tap_the_link_we_emailed_to_you_to_verify_your_account": "Натисніть на посилання, яке ми надіслали на вашу пошту, щоб підтвердити свій обліковий запис",
"button": {
- "open_email_app": "Open Email App",
- "resend": "Resend"
+ "open_email_app": "Відкрити додаток Електронної пошти",
+ "resend": "Повторно надіслати"
},
"dont_receive_email": {
- "title": "Check your email",
- "description": "Check if your email address is correct as well as your junk folder if you haven’t.",
- "resend_email": "Resend Email"
+ "title": "Перевірте свою електронну пошту",
+ "description": "Перевірте правильність адреси електронної пошти, а також теку зі спамом, якщо ви ще не зробили цього.",
+ "resend_email": "Повторно надіслати лист"
},
"open_email_app": {
- "title": "Check your inbox.",
- "description": "We just sent you an email. Check your junk folder if you haven’t.",
- "mail": "Mail",
- "open_email_client": "Open Email Client"
+ "title": "Перевірте вашу поштову скриньку.",
+ "description": "Ми щойно надіслали вам електронного листа. Перевірте вашу спам теку, якщо ви не зробили цього.",
+ "mail": "Пошта",
+ "open_email_client": "Відкрити поштового клієнта"
}
},
"home_timeline": {
- "title": "Home",
+ "title": "Головна",
"navigation_bar_state": {
- "offline": "Offline",
- "new_posts": "See new posts",
- "published": "Published!",
- "Publishing": "Publishing post...",
+ "offline": "В автономному режимі",
+ "new_posts": "До нових дописів",
+ "published": "Опубліковано!",
+ "Publishing": "Допис публікується...",
"accessibility": {
- "logo_label": "Logo Button",
- "logo_hint": "Tap to scroll to top and tap again to previous location"
+ "logo_label": "Кнопка Логотипу",
+ "logo_hint": "Торкніться для прокручування вгору і натисніть ще раз на попереднє розташування"
}
}
},
"suggestion_account": {
- "title": "Find People to Follow",
- "follow_explain": "When you follow someone, you’ll see their posts in your home feed."
+ "title": "Знайти людей, аби підписатися",
+ "follow_explain": "Коли ви підпишетесь на когось, ви побачите його дописи у своїй домашній стрічці."
},
"compose": {
"title": {
- "new_post": "New Post",
- "new_reply": "New Reply"
+ "new_post": "Новий допис",
+ "new_reply": "Нова відповідь"
},
"media_selection": {
- "camera": "Take Photo",
- "photo_library": "Photo Library",
- "browse": "Browse"
+ "camera": "Зробити фото",
+ "photo_library": "Галерея",
+ "browse": "Огляд"
},
- "content_input_placeholder": "Type or paste what’s on your mind",
- "compose_action": "Publish",
- "replying_to_user": "replying to %s",
+ "content_input_placeholder": "Що у Вас на думці",
+ "compose_action": "Опублікувати",
+ "replying_to_user": "відповідь на: %s",
"attachment": {
- "photo": "photo",
- "video": "video",
- "attachment_broken": "This %s is broken and can’t be\nuploaded to Mastodon.",
- "description_photo": "Describe the photo for the visually-impaired...",
- "description_video": "Describe the video for the visually-impaired...",
- "load_failed": "Load Failed",
- "upload_failed": "Upload Failed",
- "can_not_recognize_this_media_attachment": "Can not regonize this media attachment",
- "attachment_too_large": "Attachment too large"
+ "photo": "фото",
+ "video": "відео",
+ "attachment_broken": "Цей %s пошкоджений і не може бути\nзавантажений в Mastodon.",
+ "description_photo": "Опишіть фото для людей з вадами зору...",
+ "description_video": "Опишіть відео для людей з вадами зору...",
+ "load_failed": "Не вдалося завантажити",
+ "upload_failed": "Не вдалося завантажити",
+ "can_not_recognize_this_media_attachment": "Неможливо розпізнати цей медіафайл",
+ "attachment_too_large": "Вкладення завелике",
+ "compressing_state": "Стиснення...",
+ "server_processing_state": "Обробка сервера..."
},
"poll": {
- "duration_time": "Duration: %s",
- "thirty_minutes": "30 minutes",
- "one_hour": "1 Hour",
- "six_hours": "6 Hours",
- "one_day": "1 Day",
- "three_days": "3 Days",
- "seven_days": "7 Days",
- "option_number": "Option %ld"
+ "duration_time": "Тривалість: %s",
+ "thirty_minutes": "30 хвилин",
+ "one_hour": "1 Година",
+ "six_hours": "6 Годин",
+ "one_day": "1 День",
+ "three_days": "3 Дні",
+ "seven_days": "7 Днів",
+ "option_number": "Параметр %ld",
+ "the_poll_is_invalid": "Неприпустимий варіант опитування",
+ "the_poll_has_empty_option": "В опитуванні є порожній варіант"
},
"content_warning": {
- "placeholder": "Write an accurate warning here..."
+ "placeholder": "Напишіть своє попередження тут..."
},
"visibility": {
- "public": "Public",
- "unlisted": "Unlisted",
- "private": "Followers only",
- "direct": "Only people I mention"
+ "public": "Публічно",
+ "unlisted": "Приховувати зі стрічок",
+ "private": "Тільки для підписників",
+ "direct": "Тільки людям, яких я згадав"
},
"auto_complete": {
- "space_to_add": "Space to add"
+ "space_to_add": "Пробіл, щоб додати"
},
"accessibility": {
- "append_attachment": "Add Attachment",
- "append_poll": "Add Poll",
- "remove_poll": "Remove Poll",
- "custom_emoji_picker": "Custom Emoji Picker",
- "enable_content_warning": "Enable Content Warning",
- "disable_content_warning": "Disable Content Warning",
- "post_visibility_menu": "Post Visibility Menu"
+ "append_attachment": "Додати вкладення",
+ "append_poll": "Додати опитування",
+ "remove_poll": "Видалити опитування",
+ "custom_emoji_picker": "Вибір власних емодзі",
+ "enable_content_warning": "Увімкнути попередження про контент",
+ "disable_content_warning": "Вимкнути попередження про контент",
+ "post_visibility_menu": "Меню видимості поста",
+ "post_options": "Параметри повідомлення",
+ "posting_as": "Опублікувати як %s"
},
"keyboard": {
- "discard_post": "Discard Post",
- "publish_post": "Publish Post",
- "toggle_poll": "Toggle Poll",
- "toggle_content_warning": "Toggle Content Warning",
- "append_attachment_entry": "Add Attachment - %s",
- "select_visibility_entry": "Select Visibility - %s"
+ "discard_post": "Відхилити допис",
+ "publish_post": "Опублікувати повідомлення",
+ "toggle_poll": "Відкрити або закрити опитування",
+ "toggle_content_warning": "Попередження про вміст",
+ "append_attachment_entry": "Додати вкладення - %s",
+ "select_visibility_entry": "Оберіть видимість - %s"
}
},
"profile": {
"header": {
- "follows_you": "Follows You"
+ "follows_you": "Підписаний(-на) на вас"
},
"dashboard": {
- "posts": "posts",
- "following": "following",
- "followers": "followers"
+ "my_posts": "posts",
+ "my_following": "following",
+ "my_followers": "followers",
+ "other_posts": "posts",
+ "other_following": "following",
+ "other_followers": "followers"
},
"fields": {
- "add_row": "Add Row",
+ "joined": "Joined",
+ "add_row": "Додати рядок",
"placeholder": {
- "label": "Label",
- "content": "Content"
+ "label": "Позначка",
+ "content": "Зміст"
+ },
+ "verified": {
+ "short": "Перевірено %s",
+ "long": "Права власності на це посилання були перевірені %s"
}
},
"segmented_control": {
- "posts": "Posts",
- "replies": "Replies",
- "posts_and_replies": "Posts and Replies",
- "media": "Media",
- "about": "About"
+ "posts": "Дописи",
+ "replies": "Відповіді",
+ "posts_and_replies": "Дописи й відповіді",
+ "media": "Медіа",
+ "about": "Про застосунок"
},
"relationship_action_alert": {
"confirm_mute_user": {
- "title": "Mute Account",
- "message": "Confirm to mute %s"
+ "title": "Заглушити обліковий запис",
+ "message": "Підтвердити заглушення %s"
},
"confirm_unmute_user": {
- "title": "Unmute Account",
- "message": "Confirm to unmute %s"
+ "title": "Розглушити обліковий запис",
+ "message": "Підтвердить розглушення %s"
},
"confirm_block_user": {
- "title": "Block Account",
- "message": "Confirm to block %s"
+ "title": "Заблокувати обліковий запис",
+ "message": "Підтвердити блокування %s"
},
"confirm_unblock_user": {
- "title": "Unblock Account",
- "message": "Confirm to unblock %s"
+ "title": "Розблокувати обліковий запис",
+ "message": "Підтвердити розблокування %s"
},
"confirm_show_reblogs": {
- "title": "Show Reblogs",
- "message": "Confirm to show reblogs"
+ "title": "Показати реблоги",
+ "message": "Підтвердити показ реблогів"
},
"confirm_hide_reblogs": {
- "title": "Hide Reblogs",
- "message": "Confirm to hide reblogs"
+ "title": "Сховати реблоги",
+ "message": "Підтвердити, щоб приховати реблоги"
}
},
"accessibility": {
- "show_avatar_image": "Show avatar image",
- "edit_avatar_image": "Edit avatar image",
- "show_banner_image": "Show banner image",
- "double_tap_to_open_the_list": "Double tap to open the list"
+ "show_avatar_image": "Показати аватар користувача",
+ "edit_avatar_image": "Редагувати аватар",
+ "show_banner_image": "Показати зображення банера",
+ "double_tap_to_open_the_list": "Натисніть двічі, щоб відчинити список"
}
},
"follower": {
- "title": "follower",
- "footer": "Followers from other servers are not displayed."
+ "title": "підписник",
+ "footer": "Підписники з інших серверів не відображаються."
},
"following": {
- "title": "following",
- "footer": "Follows from other servers are not displayed."
+ "title": "підписаний",
+ "footer": "Підписки з інших серверів не відображаються."
},
"familiarFollowers": {
- "title": "Followers you familiar",
- "followed_by_names": "Followed by %s"
+ "title": "Підписники, яких ви знаєте",
+ "followed_by_names": "Має серед підписників %s"
},
"favorited_by": {
- "title": "Favorited By"
+ "title": "Є в обраних у"
},
"reblogged_by": {
- "title": "Reblogged By"
+ "title": "Репостнуто"
},
"search": {
- "title": "Search",
+ "title": "Пошук",
"search_bar": {
- "placeholder": "Search hashtags and users",
- "cancel": "Cancel"
+ "placeholder": "Пошук за гештегами та користувачами",
+ "cancel": "Скасувати"
},
"recommend": {
- "button_text": "See All",
+ "button_text": "Переглянути всі",
"hash_tag": {
- "title": "Trending on Mastodon",
- "description": "Hashtags that are getting quite a bit of attention",
- "people_talking": "%s people are talking"
+ "title": "У Трендах на Мастодоні",
+ "description": "Гештеги, що привертають увагу",
+ "people_talking": "%s людей говорять"
},
"accounts": {
- "title": "Accounts you might like",
- "description": "You may like to follow these accounts",
- "follow": "Follow"
+ "title": "Акавнти, що можуть вам сподобатися",
+ "description": "Ви можете зацікавитися цими обліковими записами",
+ "follow": "Підписатися"
}
},
"searching": {
"segment": {
- "all": "All",
- "people": "People",
- "hashtags": "Hashtags",
- "posts": "Posts"
+ "all": "Всі",
+ "people": "Громада",
+ "hashtags": "Гештеги",
+ "posts": "Дописи"
},
"empty_state": {
- "no_results": "No results"
+ "no_results": "Жодних результатів"
},
- "recent_search": "Recent searches",
- "clear": "Clear"
+ "recent_search": "Нещодавні запити",
+ "clear": "Очистити"
}
},
"discovery": {
"tabs": {
- "posts": "Posts",
- "hashtags": "Hashtags",
- "news": "News",
- "community": "Community",
- "for_you": "For You"
+ "posts": "Дописи",
+ "hashtags": "Гештеги",
+ "news": "Новини",
+ "community": "Спільнота",
+ "for_you": "Для Вас"
},
- "intro": "These are the posts gaining traction in your corner of Mastodon."
+ "intro": "Ось найбільш популярні дописи серед вашого серверу Mastodon."
},
"favorite": {
- "title": "Your Favorites"
+ "title": "Ваші Вподобання"
},
"notification": {
"title": {
- "Everything": "Everything",
- "Mentions": "Mentions"
+ "Everything": "Все",
+ "Mentions": "Згадки"
},
"notification_description": {
- "followed_you": "followed you",
- "favorited_your_post": "favorited your post",
- "reblogged_your_post": "reblogged your post",
- "mentioned_you": "mentioned you",
- "request_to_follow_you": "request to follow you",
- "poll_has_ended": "poll has ended"
+ "followed_you": "підписався(-лась) на вас",
+ "favorited_your_post": "вподобав(-ла) ваш допис",
+ "reblogged_your_post": "поширив(-ла) ваш допис",
+ "mentioned_you": "згадав(-ла) вас",
+ "request_to_follow_you": "запит на підписку",
+ "poll_has_ended": "опитування завершено"
},
"keyobard": {
- "show_everything": "Show Everything",
- "show_mentions": "Show Mentions"
+ "show_everything": "Показати все",
+ "show_mentions": "Показати Згадки"
},
"follow_request": {
- "accept": "Accept",
- "accepted": "Accepted",
- "reject": "reject",
- "rejected": "Rejected"
+ "accept": "Прийняти",
+ "accepted": "Прийнято",
+ "reject": "відхилити",
+ "rejected": "Відхилено"
}
},
"thread": {
- "back_title": "Post",
- "title": "Post from %s"
+ "back_title": "Допис",
+ "title": "Публікація від %s"
},
"settings": {
- "title": "Settings",
+ "title": "Налаштування",
"section": {
"appearance": {
- "title": "Appearance",
- "automatic": "Automatic",
- "light": "Always Light",
- "dark": "Always Dark"
+ "title": "Оформлення",
+ "automatic": "Автоматичне",
+ "light": "Завжди світле",
+ "dark": "Завжди темне"
},
"look_and_feel": {
- "title": "Look and Feel",
- "use_system": "Use System",
- "really_dark": "Really Dark",
- "sorta_dark": "Sorta Dark",
- "light": "Light"
+ "title": "Зовнішній вид",
+ "use_system": "Використати системний",
+ "really_dark": "Дуже темний",
+ "sorta_dark": "Трішкий темний",
+ "light": "Світлий"
},
"notifications": {
- "title": "Notifications",
- "favorites": "Favorites my post",
- "follows": "Follows me",
- "boosts": "Reblogs my post",
- "mentions": "Mentions me",
+ "title": "Сповіщення",
+ "favorites": "Вподобав ваш допис",
+ "follows": "Підписався на мене",
+ "boosts": "Реблог мого посту",
+ "mentions": "Згадує мене",
"trigger": {
- "anyone": "anyone",
- "follower": "a follower",
- "follow": "anyone I follow",
- "noone": "no one",
- "title": "Notify me when"
+ "anyone": "усі",
+ "follower": "підписник",
+ "follow": "хтось, за ким я слідкую",
+ "noone": "ніхто",
+ "title": "Повідомити мене, коли"
}
},
"preference": {
- "title": "Preferences",
- "true_black_dark_mode": "True black dark mode",
- "disable_avatar_animation": "Disable animated avatars",
- "disable_emoji_animation": "Disable animated emojis",
- "using_default_browser": "Use default browser to open links",
- "open_links_in_mastodon": "Open links in Mastodon"
+ "title": "Налаштування",
+ "true_black_dark_mode": "Чорний режим",
+ "disable_avatar_animation": "Вимкнути анімовані аватари",
+ "disable_emoji_animation": "Вимкнути анімовані емодзі",
+ "using_default_browser": "Використовувати браузер за замовчуванням, щоб відкрити посилання",
+ "open_links_in_mastodon": "Відкривати посилання в Mastodon"
},
"boring_zone": {
- "title": "The Boring Zone",
- "account_settings": "Account Settings",
- "terms": "Terms of Service",
- "privacy": "Privacy Policy"
+ "title": "Нудна зона",
+ "account_settings": "Налаштування облікового запису",
+ "terms": "Умови використання",
+ "privacy": "Політика конфіденційності"
},
"spicy_zone": {
- "title": "The Spicy Zone",
- "clear": "Clear Media Cache",
- "signout": "Sign Out"
+ "title": "Гостра зона",
+ "clear": "Очистити кеш медіа",
+ "signout": "Вийти"
}
},
"footer": {
- "mastodon_description": "Mastodon is open source software. You can report issues on GitHub at %s (%s)"
+ "mastodon_description": "Mastodon — програма з відкритим вихідним кодом. Ви можете повідомити про проблеми на GitHub на %s (%s)"
},
"keyboard": {
- "close_settings_window": "Close Settings Window"
+ "close_settings_window": "Закрити вікно налаштувань"
}
},
"report": {
- "title_report": "Report",
- "title": "Report %s",
- "step1": "Step 1 of 2",
- "step2": "Step 2 of 2",
- "content1": "Are there any other posts you’d like to add to the report?",
- "content2": "Is there anything the moderators should know about this report?",
- "report_sent_title": "Thanks for reporting, we’ll look into this.",
- "send": "Send Report",
- "skip_to_send": "Send without comment",
- "text_placeholder": "Type or paste additional comments",
- "reported": "REPORTED",
+ "title_report": "Скарга",
+ "title": "Поскаржитись на %s",
+ "step1": "Крок 1 з 2",
+ "step2": "Крок 2 з 2",
+ "content1": "Чи є інші дописи, які Ви хотіли б додати до скарги?",
+ "content2": "Чи є що-небудь модератори повинні знати про цю скаргу?",
+ "report_sent_title": "Дякуємо за скаргу, ми розглянемо її.",
+ "send": "Надіслати скаргу",
+ "skip_to_send": "Надіслати без коментаря",
+ "text_placeholder": "Введіть або вставте додаткові коментарі",
+ "reported": "Мої скарги",
"step_one": {
- "step_1_of_4": "Step 1 of 4",
- "whats_wrong_with_this_post": "What's wrong with this post?",
- "whats_wrong_with_this_account": "What's wrong with this account?",
- "whats_wrong_with_this_username": "What's wrong with %s?",
- "select_the_best_match": "Select the best match",
- "i_dont_like_it": "I don’t like it",
- "it_is_not_something_you_want_to_see": "It is not something you want to see",
- "its_spam": "It’s spam",
- "malicious_links_fake_engagement_or_repetetive_replies": "Malicious links, fake engagement, or repetetive replies",
- "it_violates_server_rules": "It violates server rules",
- "you_are_aware_that_it_breaks_specific_rules": "You are aware that it breaks specific rules",
- "its_something_else": "It’s something else",
- "the_issue_does_not_fit_into_other_categories": "The issue does not fit into other categories"
+ "step_1_of_4": "Крок 1 з 4",
+ "whats_wrong_with_this_post": "Що не так з цим постом?",
+ "whats_wrong_with_this_account": "Що не так з цим обліковим записом?",
+ "whats_wrong_with_this_username": "Що з %s не так?",
+ "select_the_best_match": "Оберіть найвідповідніший збіг",
+ "i_dont_like_it": "Мені це не подобається",
+ "it_is_not_something_you_want_to_see": "Це не те, що ви хотіли б побачити",
+ "its_spam": "Це спам",
+ "malicious_links_fake_engagement_or_repetetive_replies": "Підозрілі посилання, фейкові розмови або відповіді",
+ "it_violates_server_rules": "Це порушує правила сервера",
+ "you_are_aware_that_it_breaks_specific_rules": "Ви впевнені, що це порушує певні правила",
+ "its_something_else": "Це щось інше",
+ "the_issue_does_not_fit_into_other_categories": "Ця проблема не відповідає жодній іншій категорії"
},
"step_two": {
- "step_2_of_4": "Step 2 of 4",
- "which_rules_are_being_violated": "Which rules are being violated?",
- "select_all_that_apply": "Select all that apply",
- "i_just_don’t_like_it": "I just don’t like it"
+ "step_2_of_4": "Крок 2 з 4",
+ "which_rules_are_being_violated": "Які правила порушено?",
+ "select_all_that_apply": "Виберіть усі варіанти, що підходять",
+ "i_just_don’t_like_it": "Мені це не подобається"
},
"step_three": {
- "step_3_of_4": "Step 3 of 4",
- "are_there_any_posts_that_back_up_this_report": "Are there any posts that back up this report?",
- "select_all_that_apply": "Select all that apply"
+ "step_3_of_4": "Крок 3 з 4",
+ "are_there_any_posts_that_back_up_this_report": "Чи є публікації, які підтверджують цю скаргу?",
+ "select_all_that_apply": "Виберіть усі варіанти, що підходять"
},
"step_four": {
- "step_4_of_4": "Step 4 of 4",
- "is_there_anything_else_we_should_know": "Is there anything else we should know?"
+ "step_4_of_4": "Крок 4 з 4",
+ "is_there_anything_else_we_should_know": "Чи є ще щось, що ми повинні знати?"
},
"step_final": {
- "dont_want_to_see_this": "Don’t want to see this?",
- "when_you_see_something_you_dont_like_on_mastodon_you_can_remove_the_person_from_your_experience.": "When you see something you don’t like on Mastodon, you can remove the person from your experience.",
- "unfollow": "Unfollow",
- "unfollowed": "Unfollowed",
- "unfollow_user": "Unfollow %s",
- "mute_user": "Mute %s",
- "you_wont_see_their_posts_or_reblogs_in_your_home_feed_they_wont_know_they_ve_been_muted": "You won’t see their posts or reblogs in your home feed. They won’t know they’ve been muted.",
- "block_user": "Block %s",
- "they_will_no_longer_be_able_to_follow_or_see_your_posts_but_they_can_see_if_theyve_been_blocked": "They will no longer be able to follow or see your posts, but they can see if they’ve been blocked.",
- "while_we_review_this_you_can_take_action_against_user": "While we review this, you can take action against %s"
+ "dont_want_to_see_this": "Не хочете бачити це?",
+ "when_you_see_something_you_dont_like_on_mastodon_you_can_remove_the_person_from_your_experience.": "Якщо бачите щось, що вам не подобається в Mastodon, то можна вилучити людину зі свого оточення.",
+ "unfollow": "Відписатися",
+ "unfollowed": "Відписалися від",
+ "unfollow_user": "Відписатися від %s",
+ "mute_user": "Ігнорувати %s",
+ "you_wont_see_their_posts_or_reblogs_in_your_home_feed_they_wont_know_they_ve_been_muted": "Ви не побачите їхні дописи чи репости на вашій домашній стрічці. Вони не знатимуть, що ви ігноруєте їх.",
+ "block_user": "Заблокувати %s",
+ "they_will_no_longer_be_able_to_follow_or_see_your_posts_but_they_can_see_if_theyve_been_blocked": "Вони більше не зможуть стежити або бачити Ваші пости, але вони зможуть побачити, що вони були заблоковані.",
+ "while_we_review_this_you_can_take_action_against_user": "Поки ми переглядаємо це, ви можете вжити заходів проти %s"
}
},
"preview": {
"keyboard": {
- "close_preview": "Close Preview",
- "show_next": "Show Next",
- "show_previous": "Show Previous"
+ "close_preview": "Закрити перегляд",
+ "show_next": "Показати наступне",
+ "show_previous": "Показувати попереднє"
}
},
"account_list": {
- "tab_bar_hint": "Current selected profile: %s. Double tap then hold to show account switcher",
- "dismiss_account_switcher": "Dismiss Account Switcher",
- "add_account": "Add Account"
+ "tab_bar_hint": "Поточний профіль: %s. Двічі торкніться, щоб показати перемикач ваших профілів",
+ "dismiss_account_switcher": "Відхилити зміну поточного облікового запису",
+ "add_account": "Додати обліковий запис"
},
"wizard": {
- "new_in_mastodon": "New in Mastodon",
- "multiple_account_switch_intro_description": "Switch between multiple accounts by holding the profile button.",
- "accessibility_hint": "Double tap to dismiss this wizard"
+ "new_in_mastodon": "Новий в Mastodon",
+ "multiple_account_switch_intro_description": "Натисніть для переходу між кількома обліковими записами, тримаючи кнопку профілю.",
+ "accessibility_hint": "Двічі торкніться, щоб закрити цей майстер"
},
"bookmark": {
- "title": "Bookmarks"
+ "title": "Закладки"
+ },
+ "followed_tags": {
+ "title": "Followed Tags",
+ "header": {
+ "posts": "posts",
+ "participants": "participants",
+ "posts_today": "posts today"
+ },
+ "actions": {
+ "follow": "Follow",
+ "unfollow": "Unfollow"
+ }
}
}
}
diff --git a/Localization/StringsConvertor/input/uk.lproj/ios-infoPlist.json b/Localization/StringsConvertor/input/uk.lproj/ios-infoPlist.json
index c6db73de0..bc953a077 100644
--- a/Localization/StringsConvertor/input/uk.lproj/ios-infoPlist.json
+++ b/Localization/StringsConvertor/input/uk.lproj/ios-infoPlist.json
@@ -1,6 +1,6 @@
{
- "NSCameraUsageDescription": "Used to take photo for post status",
- "NSPhotoLibraryAddUsageDescription": "Used to save photo into the Photo Library",
- "NewPostShortcutItemTitle": "New Post",
- "SearchShortcutItemTitle": "Search"
+ "NSCameraUsageDescription": "Використовується, щоб зробити фотографію для статусу публікації",
+ "NSPhotoLibraryAddUsageDescription": "Використовується для збереження фото в бібліотеку",
+ "NewPostShortcutItemTitle": "Новий допис",
+ "SearchShortcutItemTitle": "Пошук"
}
diff --git a/Localization/StringsConvertor/input/vi.lproj/Localizable.stringsdict b/Localization/StringsConvertor/input/vi.lproj/Localizable.stringsdict
index 6905b240e..4c772f014 100644
--- a/Localization/StringsConvertor/input/vi.lproj/Localizable.stringsdict
+++ b/Localization/StringsConvertor/input/vi.lproj/Localizable.stringsdict
@@ -44,6 +44,20 @@
%ld ký tự
+ a11y.plural.count.characters_left
+
+ NSStringLocalizedFormatKey
+ %#@character_count@ còn lại
+ character_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ other
+ %ld ký tự
+
+
plural.count.followed_by_and_mutual
NSStringLocalizedFormatKey
diff --git a/Localization/StringsConvertor/input/vi.lproj/app.json b/Localization/StringsConvertor/input/vi.lproj/app.json
index 5b7696727..3a3c66a66 100644
--- a/Localization/StringsConvertor/input/vi.lproj/app.json
+++ b/Localization/StringsConvertor/input/vi.lproj/app.json
@@ -51,6 +51,11 @@
"clean_cache": {
"title": "Xóa bộ nhớ đệm",
"message": "Đã xóa %s bộ nhớ đệm."
+ },
+ "translation_failed": {
+ "title": "Ghi chú",
+ "message": "Dịch không thành công. Có thể quản trị viên chưa bật dịch trên máy chủ này hoặc máy chủ này đang chạy phiên bản cũ hơn của Mastodon chưa hỗ trợ dịch.",
+ "button": "OK"
}
},
"controls": {
@@ -75,9 +80,10 @@
"save_photo": "Lưu ảnh",
"copy_photo": "Sao chép ảnh",
"sign_in": "Đăng nhập",
- "sign_up": "Đăng ký",
+ "sign_up": "Tạo tài khoản",
"see_more": "Xem thêm",
"preview": "Xem trước",
+ "copy": "Chép",
"share": "Chia sẻ",
"share_user": "Chia sẻ %s",
"share_post": "Chia sẻ tút",
@@ -91,12 +97,16 @@
"block_domain": "Chặn %s",
"unblock_domain": "Bỏ chặn %s",
"settings": "Cài đặt",
- "delete": "Xóa"
+ "delete": "Xóa",
+ "translate_post": {
+ "title": "Dịch từ %s",
+ "unknown_language": "Chưa xác định"
+ }
},
"tabs": {
"home": "Bảng tin",
- "search": "Tìm kiếm",
- "notification": "Thông báo",
+ "search_and_explore": "Tìm và Khám Phá",
+ "notifications": "Thông báo",
"profile": "Trang hồ sơ"
},
"keyboard": {
@@ -132,6 +142,8 @@
"sensitive_content": "Nội dung nhạy cảm",
"media_content_warning": "Nhấn để hiển thị",
"tap_to_reveal": "Nhấn để xem",
+ "load_embed": "Nạp mã nhúng",
+ "link_via_user": "%s bởi %s",
"poll": {
"vote": "Bình chọn",
"closed": "Kết thúc"
@@ -153,6 +165,7 @@
"show_image": "Hiển thị hình ảnh",
"show_gif": "Hiển thị GIF",
"show_video_player": "Hiện trình phát video",
+ "share_link_in_post": "Chia sẻ liên kết trong Tút",
"tap_then_hold_to_show_menu": "Nhấn giữ để hiện menu"
},
"tag": {
@@ -168,6 +181,12 @@
"private": "Chỉ người theo dõi của họ có thể thấy tút này.",
"private_from_me": "Chỉ người theo dõi tôi có thể thấy tút này.",
"direct": "Chỉ người được nhắc đến có thể thấy tút."
+ },
+ "translation": {
+ "translated_from": "Dịch từ %s bằng %s",
+ "unknown_language": "Không xác định",
+ "unknown_provider": "Không biết",
+ "show_original": "Bản gốc"
}
},
"friendship": {
@@ -218,10 +237,16 @@
"get_started": "Bắt đầu",
"log_in": "Đăng nhập"
},
+ "login": {
+ "title": "Chào mừng trở lại!",
+ "subtitle": "Đăng nhập vào máy chủ mà bạn đã tạo tài khoản.",
+ "server_search_field": {
+ "placeholder": "Nhập URL hoặc tìm máy chủ"
+ }
+ },
"server_picker": {
"title": "Mastodon gồm nhiều máy chủ với thành viên riêng.",
- "subtitle": "Chọn một máy chủ dựa theo sở thích, tôn giáo, hoặc ý muốn của bạn.",
- "subtitle_extend": "Chọn một máy chủ dựa theo sở thích, tôn giáo, hoặc ý muốn của bạn. Mỗi máy chủ có thể được vận hành bởi một cá nhân hoặc một tổ chức.",
+ "subtitle": "Chọn một máy chủ dựa theo sở thích, tôn giáo, hoặc ý muốn của bạn. Bạn vẫn có thể giao tiếp với bất cứ ai mà không phụ thuộc vào máy chủ của họ.",
"button": {
"category": {
"all": "Toàn bộ",
@@ -248,8 +273,7 @@
"category": "PHÂN LOẠI"
},
"input": {
- "placeholder": "Tìm máy chủ",
- "search_servers_or_enter_url": "Tìm máy chủ hoặc nhập URL"
+ "search_servers_or_enter_url": "Tìm một máy chủ hoặc nhập URL"
},
"empty_state": {
"finding_servers": "Đang tìm máy chủ hoạt động...",
@@ -376,17 +400,19 @@
},
"content_input_placeholder": "Cho thế giới biết bạn đang nghĩ gì",
"compose_action": "Đăng",
- "replying_to_user": "trả lời %s",
+ "replying_to_user": "%s viết tiếp",
"attachment": {
"photo": "ảnh",
"video": "video",
"attachment_broken": "%s này bị lỗi và không thể\ntải lên Mastodon.",
"description_photo": "Mô tả hình ảnh cho người khiếm thị...",
"description_video": "Mô tả video cho người khiếm thị...",
- "load_failed": "Load Failed",
- "upload_failed": "Upload Failed",
- "can_not_recognize_this_media_attachment": "Can not regonize this media attachment",
- "attachment_too_large": "Attachment too large"
+ "load_failed": "Tải thất bại",
+ "upload_failed": "Tải lên thất bại",
+ "can_not_recognize_this_media_attachment": "Không xem được tập tin đính kèm",
+ "attachment_too_large": "Tập tin đính kèm quá lớn",
+ "compressing_state": "Đang nén...",
+ "server_processing_state": "Máy chủ đang xử lý..."
},
"poll": {
"duration_time": "Thời hạn: %s",
@@ -396,7 +422,9 @@
"one_day": "1 ngày",
"three_days": "3 ngày",
"seven_days": "7 ngày",
- "option_number": "Lựa chọn %ld"
+ "option_number": "Lựa chọn %ld",
+ "the_poll_is_invalid": "Bình chọn không hợp lệ",
+ "the_poll_has_empty_option": "Thiếu lựa chọn"
},
"content_warning": {
"placeholder": "Viết nội dung ẩn của bạn ở đây..."
@@ -417,7 +445,9 @@
"custom_emoji_picker": "Chọn emoji",
"enable_content_warning": "Bật nội dung ẩn",
"disable_content_warning": "Tắt nội dung ẩn",
- "post_visibility_menu": "Menu hiển thị tút"
+ "post_visibility_menu": "Menu hiển thị tút",
+ "post_options": "Tùy chọn đăng",
+ "posting_as": "Đăng dưới dạng %s"
},
"keyboard": {
"discard_post": "Hủy đăng tút",
@@ -433,15 +463,23 @@
"follows_you": "Đang theo dõi bạn"
},
"dashboard": {
- "posts": "tút",
- "following": "theo dõi",
- "followers": "người theo dõi"
+ "my_posts": "posts",
+ "my_following": "following",
+ "my_followers": "followers",
+ "other_posts": "posts",
+ "other_following": "following",
+ "other_followers": "followers"
},
"fields": {
+ "joined": "Đã tham gia",
"add_row": "Thêm hàng",
"placeholder": {
"label": "Nhãn",
"content": "Nội dung"
+ },
+ "verified": {
+ "short": "Đã xác minh %s",
+ "long": "Liên kết này đã được xác minh trên %s"
}
},
"segmented_control": {
@@ -707,6 +745,18 @@
},
"bookmark": {
"title": "Tút đã lưu"
+ },
+ "followed_tags": {
+ "title": "Hashtag Theo Dõi",
+ "header": {
+ "posts": "tút",
+ "participants": "người thảo luận",
+ "posts_today": "tút hôm nay"
+ },
+ "actions": {
+ "follow": "Theo dõi",
+ "unfollow": "Ngưng theo dõi"
+ }
}
}
}
diff --git a/Localization/StringsConvertor/input/zh-Hans.lproj/Localizable.stringsdict b/Localization/StringsConvertor/input/zh-Hans.lproj/Localizable.stringsdict
index 5a7af3752..362d55c4f 100644
--- a/Localization/StringsConvertor/input/zh-Hans.lproj/Localizable.stringsdict
+++ b/Localization/StringsConvertor/input/zh-Hans.lproj/Localizable.stringsdict
@@ -44,6 +44,20 @@
%ld 个字符
+ a11y.plural.count.characters_left
+
+ NSStringLocalizedFormatKey
+ %#@character_count@ 剩余
+ character_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ other
+ %ld 个字符
+
+
plural.count.followed_by_and_mutual
NSStringLocalizedFormatKey
diff --git a/Localization/StringsConvertor/input/zh-Hans.lproj/app.json b/Localization/StringsConvertor/input/zh-Hans.lproj/app.json
index ddf89e159..efe5c422e 100644
--- a/Localization/StringsConvertor/input/zh-Hans.lproj/app.json
+++ b/Localization/StringsConvertor/input/zh-Hans.lproj/app.json
@@ -51,6 +51,11 @@
"clean_cache": {
"title": "清除缓存",
"message": "成功清除 %s 缓存。"
+ },
+ "translation_failed": {
+ "title": "Note",
+ "message": "Translation failed. Maybe the administrator has not enabled translations on this server or this server is running an older version of Mastodon where translations are not yet supported.",
+ "button": "OK"
}
},
"controls": {
@@ -75,9 +80,10 @@
"save_photo": "保存照片",
"copy_photo": "拷贝照片",
"sign_in": "登录",
- "sign_up": "注册",
+ "sign_up": "创建账户",
"see_more": "查看更多",
"preview": "预览",
+ "copy": "Copy",
"share": "分享",
"share_user": "分享 %s",
"share_post": "分享帖子",
@@ -91,12 +97,16 @@
"block_domain": "屏蔽 %s",
"unblock_domain": "解除屏蔽 %s",
"settings": "设置",
- "delete": "删除"
+ "delete": "删除",
+ "translate_post": {
+ "title": "Translate from %s",
+ "unknown_language": "Unknown"
+ }
},
"tabs": {
"home": "主页",
- "search": "搜索",
- "notification": "通知",
+ "search_and_explore": "Search and Explore",
+ "notifications": "通知",
"profile": "个人资料"
},
"keyboard": {
@@ -132,15 +142,17 @@
"sensitive_content": "敏感内容",
"media_content_warning": "点击任意位置显示",
"tap_to_reveal": "点击以显示",
+ "load_embed": "Load Embed",
+ "link_via_user": "%s via %s",
"poll": {
"vote": "投票",
"closed": "已关闭"
},
"meta_entity": {
- "url": "Link: %s",
- "hashtag": "Hashtag: %s",
- "mention": "Show Profile: %s",
- "email": "Email address: %s"
+ "url": "链接:%s",
+ "hashtag": "话题:%s",
+ "mention": "显示用户资料:%s",
+ "email": "邮箱地址:%s"
},
"actions": {
"reply": "回复",
@@ -153,6 +165,7 @@
"show_image": "显示图片",
"show_gif": "显示 GIF",
"show_video_player": "显示视频播放器",
+ "share_link_in_post": "Share Link in Post",
"tap_then_hold_to_show_menu": "长按以显示菜单"
},
"tag": {
@@ -168,6 +181,12 @@
"private": "只有作者的关注者才能看到此帖子。",
"private_from_me": "只有我的关注者才能看到此帖子。",
"direct": "只有提到的用户才能看到此帖子。"
+ },
+ "translation": {
+ "translated_from": "Translated from %s using %s",
+ "unknown_language": "Unknown",
+ "unknown_provider": "Unknown",
+ "show_original": "Shown Original"
}
},
"friendship": {
@@ -187,8 +206,8 @@
"unmute_user": "取消静音 %s",
"muted": "已静音",
"edit_info": "编辑",
- "show_reblogs": "Show Reblogs",
- "hide_reblogs": "Hide Reblogs"
+ "show_reblogs": "显示转发",
+ "hide_reblogs": "隐藏转发"
},
"timeline": {
"filtered": "已过滤",
@@ -218,10 +237,16 @@
"get_started": "开始使用",
"log_in": "登录"
},
+ "login": {
+ "title": "欢迎回来",
+ "subtitle": "登入您账户所在的服务器。",
+ "server_search_field": {
+ "placeholder": "输入网址或搜索您的服务器"
+ }
+ },
"server_picker": {
"title": "挑选一个服务器,\n任意服务器。",
- "subtitle": "根据你的兴趣、区域或一般目的选择一个社区。",
- "subtitle_extend": "根据你的兴趣、区域或一般目的选择一个社区。每个社区都由完全独立的组织或个人管理。",
+ "subtitle": "根据你的地区、兴趣挑选一个服务器。无论你选择哪个服务器,你都可以跟其他服务器的任何人一起聊天。",
"button": {
"category": {
"all": "全部",
@@ -248,8 +273,7 @@
"category": "类别"
},
"input": {
- "placeholder": "查找或加入你自己的服务器...",
- "search_servers_or_enter_url": "搜索服务器或输入 URL"
+ "search_servers_or_enter_url": "搜索社区或输入 URL"
},
"empty_state": {
"finding_servers": "正在查找可用的服务器...",
@@ -383,10 +407,12 @@
"attachment_broken": "%s已损坏\n无法上传到 Mastodon",
"description_photo": "为视觉障碍人士添加照片的文字说明...",
"description_video": "为视觉障碍人士添加视频的文字说明...",
- "load_failed": "Load Failed",
- "upload_failed": "Upload Failed",
- "can_not_recognize_this_media_attachment": "Can not regonize this media attachment",
- "attachment_too_large": "Attachment too large"
+ "load_failed": "加载失败",
+ "upload_failed": "上传失败",
+ "can_not_recognize_this_media_attachment": "无法识别此媒体",
+ "attachment_too_large": "附件太大",
+ "compressing_state": "压缩中...",
+ "server_processing_state": "服务器正在处理..."
},
"poll": {
"duration_time": "时长:%s",
@@ -396,7 +422,9 @@
"one_day": "1 天",
"three_days": "3 天",
"seven_days": "7 天",
- "option_number": "选项 %ld"
+ "option_number": "选项 %ld",
+ "the_poll_is_invalid": "投票无效",
+ "the_poll_has_empty_option": "投票含有空选项"
},
"content_warning": {
"placeholder": "在这里写下内容的警告消息..."
@@ -417,7 +445,9 @@
"custom_emoji_picker": "自定义表情选择器",
"enable_content_warning": "启用内容警告",
"disable_content_warning": "关闭内容警告",
- "post_visibility_menu": "帖子可见性"
+ "post_visibility_menu": "帖子可见性",
+ "post_options": "帖子选项",
+ "posting_as": "以 %s 身份发布"
},
"keyboard": {
"discard_post": "丢弃帖子",
@@ -433,15 +463,23 @@
"follows_you": "关注了你"
},
"dashboard": {
- "posts": "帖子",
- "following": "正在关注",
- "followers": "关注者"
+ "my_posts": "posts",
+ "my_following": "following",
+ "my_followers": "followers",
+ "other_posts": "posts",
+ "other_following": "following",
+ "other_followers": "followers"
},
"fields": {
+ "joined": "Joined",
"add_row": "添加",
"placeholder": {
"label": "标签",
"content": "内容"
+ },
+ "verified": {
+ "short": "验证于 %s",
+ "long": "此链接的所有权已在 %s 上检查通过"
}
},
"segmented_control": {
@@ -469,12 +507,12 @@
"message": "确认取消屏蔽 %s"
},
"confirm_show_reblogs": {
- "title": "Show Reblogs",
- "message": "Confirm to show reblogs"
+ "title": "显示转发",
+ "message": "确认显示转发"
},
"confirm_hide_reblogs": {
- "title": "Hide Reblogs",
- "message": "Confirm to hide reblogs"
+ "title": "隐藏转发",
+ "message": "确认隐藏转发"
}
},
"accessibility": {
@@ -706,7 +744,19 @@
"accessibility_hint": "双击关闭此向导"
},
"bookmark": {
- "title": "Bookmarks"
+ "title": "书签"
+ },
+ "followed_tags": {
+ "title": "Followed Tags",
+ "header": {
+ "posts": "posts",
+ "participants": "participants",
+ "posts_today": "posts today"
+ },
+ "actions": {
+ "follow": "Follow",
+ "unfollow": "Unfollow"
+ }
}
}
}
diff --git a/Localization/StringsConvertor/input/zh-Hant.lproj/Localizable.stringsdict b/Localization/StringsConvertor/input/zh-Hant.lproj/Localizable.stringsdict
index c0ce0f9a2..d545fd6a4 100644
--- a/Localization/StringsConvertor/input/zh-Hant.lproj/Localizable.stringsdict
+++ b/Localization/StringsConvertor/input/zh-Hant.lproj/Localizable.stringsdict
@@ -44,6 +44,20 @@
%ld 個字
+ a11y.plural.count.characters_left
+
+ NSStringLocalizedFormatKey
+ 剩餘 %#@character_count@ 字
+ character_count
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ ld
+ other
+ %ld 個字
+
+
plural.count.followed_by_and_mutual
NSStringLocalizedFormatKey
diff --git a/Localization/StringsConvertor/input/zh-Hant.lproj/app.json b/Localization/StringsConvertor/input/zh-Hant.lproj/app.json
index ab7343c99..711084407 100644
--- a/Localization/StringsConvertor/input/zh-Hant.lproj/app.json
+++ b/Localization/StringsConvertor/input/zh-Hant.lproj/app.json
@@ -51,6 +51,11 @@
"clean_cache": {
"title": "清除快取",
"message": "成功清除 %s 快取。"
+ },
+ "translation_failed": {
+ "title": "備註",
+ "message": "翻譯失敗。也許管理員未於此伺服器啟用翻譯功能,或此伺服器為未支援翻譯功能之舊版本 Mastodon。",
+ "button": "OK"
}
},
"controls": {
@@ -75,9 +80,10 @@
"save_photo": "儲存照片",
"copy_photo": "複製照片",
"sign_in": "登入",
- "sign_up": "註冊",
+ "sign_up": "新增帳號",
"see_more": "檢視更多",
"preview": "預覽",
+ "copy": "複製",
"share": "分享",
"share_user": "分享 %s",
"share_post": "分享嘟文",
@@ -91,12 +97,16 @@
"block_domain": "封鎖 %s",
"unblock_domain": "解除封鎖 %s",
"settings": "設定",
- "delete": "刪除"
+ "delete": "刪除",
+ "translate_post": {
+ "title": "翻譯自 %s",
+ "unknown_language": "未知"
+ }
},
"tabs": {
"home": "首頁",
- "search": "搜尋",
- "notification": "通知",
+ "search_and_explore": "搜尋與探索",
+ "notifications": "通知",
"profile": "個人檔案"
},
"keyboard": {
@@ -132,6 +142,8 @@
"sensitive_content": "敏感內容",
"media_content_warning": "輕觸任何地方以顯示",
"tap_to_reveal": "輕觸以顯示",
+ "load_embed": "讀取嵌入內容",
+ "link_via_user": "%s 透過 %s",
"poll": {
"vote": "投票",
"closed": "已關閉"
@@ -153,6 +165,7 @@
"show_image": "顯示圖片",
"show_gif": "顯示 GIF",
"show_video_player": "顯示影片播放器",
+ "share_link_in_post": "於嘟文中分享鏈結",
"tap_then_hold_to_show_menu": "輕觸然後按住以顯示選單"
},
"tag": {
@@ -168,6 +181,12 @@
"private": "只有他們的跟隨者能看到此嘟文。",
"private_from_me": "只有我的跟隨者能看到此嘟文。",
"direct": "只有被提及的使用者能看到此嘟文。"
+ },
+ "translation": {
+ "translated_from": "透過 %s 翻譯 %s",
+ "unknown_language": "未知",
+ "unknown_provider": "未知",
+ "show_original": "顯示原文"
}
},
"friendship": {
@@ -218,10 +237,16 @@
"get_started": "新手上路",
"log_in": "登入"
},
+ "login": {
+ "title": "歡迎回來",
+ "subtitle": "登入您新增帳號之伺服器",
+ "server_search_field": {
+ "placeholder": "請輸入 URL 或搜尋您的伺服器"
+ }
+ },
"server_picker": {
"title": "Mastodon 由不同伺服器的使用者組成。",
- "subtitle": "基於您的興趣、地區、或一般用途選定一個伺服器。",
- "subtitle_extend": "基於您的興趣、地區、或一般用途選定一個伺服器。每個伺服器是由完全獨立的組織或個人營運。",
+ "subtitle": "基於您的興趣、地區、或一般用途選定一個伺服器。您仍會與任何伺服器中的每個人連結。",
"button": {
"category": {
"all": "全部",
@@ -248,8 +273,7 @@
"category": "分類"
},
"input": {
- "placeholder": "搜尋伺服器",
- "search_servers_or_enter_url": "搜尋伺服器或輸入網址"
+ "search_servers_or_enter_url": "搜尋社群或輸入 URL 地址"
},
"empty_state": {
"finding_servers": "尋找可用的伺服器...",
@@ -383,10 +407,12 @@
"attachment_broken": "此 %s 已損毀,並無法被上傳至 Mastodon。",
"description_photo": "為視障人士提供圖片說明...",
"description_video": "為視障人士提供影片說明...",
- "load_failed": "Load Failed",
- "upload_failed": "Upload Failed",
- "can_not_recognize_this_media_attachment": "Can not regonize this media attachment",
- "attachment_too_large": "Attachment too large"
+ "load_failed": "讀取失敗",
+ "upload_failed": "上傳失敗",
+ "can_not_recognize_this_media_attachment": "無法識別此媒體附加檔案",
+ "attachment_too_large": "附加檔案大小過大",
+ "compressing_state": "正在壓縮...",
+ "server_processing_state": "伺服器處理中..."
},
"poll": {
"duration_time": "持續時間:%s",
@@ -396,7 +422,9 @@
"one_day": "一天",
"three_days": "三天",
"seven_days": "七天",
- "option_number": "選項 %ld"
+ "option_number": "選項 %ld",
+ "the_poll_is_invalid": "此投票是無效的",
+ "the_poll_has_empty_option": "此投票有空白選項"
},
"content_warning": {
"placeholder": "請於此處寫下精準的警告..."
@@ -417,7 +445,9 @@
"custom_emoji_picker": "自訂 emoji 選擇器",
"enable_content_warning": "啟用內容警告",
"disable_content_warning": "停用內容警告",
- "post_visibility_menu": "嘟文可見性選單"
+ "post_visibility_menu": "嘟文可見性選單",
+ "post_options": "嘟文選項",
+ "posting_as": "以 %s 發嘟"
},
"keyboard": {
"discard_post": "捨棄嘟文",
@@ -433,15 +463,23 @@
"follows_you": "跟隨了您"
},
"dashboard": {
- "posts": "嘟文",
- "following": "跟隨中",
- "followers": "跟隨者"
+ "my_posts": "嘟文",
+ "my_following": "正在跟隨",
+ "my_followers": "跟隨者",
+ "other_posts": "嘟文",
+ "other_following": "正在跟隨",
+ "other_followers": "跟隨者"
},
"fields": {
+ "joined": "加入時間",
"add_row": "新增列",
"placeholder": {
"label": "標籤",
"content": "內容"
+ },
+ "verified": {
+ "short": "於 %s 上已驗證",
+ "long": "已在 %s 檢查此連結的擁有者權限"
}
},
"segmented_control": {
@@ -707,6 +745,18 @@
},
"bookmark": {
"title": "書籤"
+ },
+ "followed_tags": {
+ "title": "已跟隨主題標籤",
+ "header": {
+ "posts": "嘟文",
+ "participants": "參與者",
+ "posts_today": "本日嘟文"
+ },
+ "actions": {
+ "follow": "跟隨",
+ "unfollow": "取消跟隨"
+ }
}
}
}
diff --git a/Localization/app.json b/Localization/app.json
index 30566d8d6..963b4aed1 100644
--- a/Localization/app.json
+++ b/Localization/app.json
@@ -51,6 +51,11 @@
"clean_cache": {
"title": "Clean Cache",
"message": "Successfully cleaned %s cache."
+ },
+ "translation_failed": {
+ "title": "Note",
+ "message": "Translation failed. Maybe the administrator has not enabled translations on this server or this server is running an older version of Mastodon where translations are not yet supported.",
+ "button": "OK"
}
},
"controls": {
@@ -74,10 +79,11 @@
"take_photo": "Take Photo",
"save_photo": "Save Photo",
"copy_photo": "Copy Photo",
- "sign_in": "Sign In",
- "sign_up": "Sign Up",
+ "sign_in": "Log in",
+ "sign_up": "Create account",
"see_more": "See More",
"preview": "Preview",
+ "copy": "Copy",
"share": "Share",
"share_user": "Share %s",
"share_post": "Share Post",
@@ -91,12 +97,16 @@
"block_domain": "Block %s",
"unblock_domain": "Unblock %s",
"settings": "Settings",
- "delete": "Delete"
+ "delete": "Delete",
+ "translate_post": {
+ "title": "Translate from %s",
+ "unknown_language": "Unknown"
+ }
},
"tabs": {
"home": "Home",
- "search": "Search",
- "notification": "Notification",
+ "search_and_explore": "Search and Explore",
+ "notifications": "Notifications",
"profile": "Profile"
},
"keyboard": {
@@ -132,6 +142,8 @@
"sensitive_content": "Sensitive Content",
"media_content_warning": "Tap anywhere to reveal",
"tap_to_reveal": "Tap to reveal",
+ "load_embed": "Load Embed",
+ "link_via_user": "%s via %s",
"poll": {
"vote": "Vote",
"closed": "Closed"
@@ -153,6 +165,7 @@
"show_image": "Show image",
"show_gif": "Show GIF",
"show_video_player": "Show video player",
+ "share_link_in_post": "Share Link in Post",
"tap_then_hold_to_show_menu": "Tap then hold to show menu"
},
"tag": {
@@ -168,6 +181,18 @@
"private": "Only their followers can see this post.",
"private_from_me": "Only my followers can see this post.",
"direct": "Only mentioned user can see this post."
+ },
+ "translation": {
+ "translated_from": "Translated from %s using %s",
+ "unknown_language": "Unknown",
+ "unknown_provider": "Unknown",
+ "show_original": "Shown Original"
+ },
+ "media": {
+ "accessibility_label": "%s, attachment %d of %d",
+ "expand_image_hint": "Expands the image. Double-tap and hold to show actions",
+ "expand_gif_hint": "Expands the GIF. Double-tap and hold to show actions",
+ "expand_video_hint": "Shows the video player. Double-tap and hold to show actions"
}
},
"friendship": {
@@ -218,10 +243,16 @@
"get_started": "Get Started",
"log_in": "Log In"
},
+ "login": {
+ "title": "Welcome back",
+ "subtitle": "Log you in on the server you created your account on.",
+ "server_search_field": {
+ "placeholder": "Enter URL or search for your server"
+ }
+ },
"server_picker": {
"title": "Mastodon is made of users in different servers.",
- "subtitle": "Pick a server based on your interests, region, or a general purpose one.",
- "subtitle_extend": "Pick a server based on your interests, region, or a general purpose one. Each server is operated by an entirely independent organization or individual.",
+ "subtitle": "Pick a server based on your region, interests, or a general purpose one. You can still chat with anyone on Mastodon, regardless of your servers.",
"button": {
"category": {
"all": "All",
@@ -248,8 +279,7 @@
"category": "CATEGORY"
},
"input": {
- "placeholder": "Search servers",
- "search_servers_or_enter_url": "Search servers or enter URL"
+ "search_servers_or_enter_url": "Search communities or enter URL"
},
"empty_state": {
"finding_servers": "Finding available servers...",
@@ -355,7 +385,7 @@
"published": "Published!",
"Publishing": "Publishing post...",
"accessibility": {
- "logo_label": "Logo Button",
+ "logo_label": "Mastodon",
"logo_hint": "Tap to scroll to top and tap again to previous location"
}
}
@@ -439,11 +469,15 @@
"follows_you": "Follows You"
},
"dashboard": {
- "posts": "posts",
- "following": "following",
- "followers": "followers"
+ "my_posts": "posts",
+ "my_following": "following",
+ "my_followers": "followers",
+ "other_posts": "posts",
+ "other_following": "following",
+ "other_followers": "followers"
},
"fields": {
+ "joined": "Joined",
"add_row": "Add Row",
"placeholder": {
"label": "Label",
@@ -717,6 +751,19 @@
},
"bookmark": {
"title": "Bookmarks"
+
+ },
+ "followed_tags": {
+ "title": "Followed Tags",
+ "header": {
+ "posts": "posts",
+ "participants": "participants",
+ "posts_today": "posts today"
+ },
+ "actions": {
+ "follow": "Follow",
+ "unfollow": "Unfollow"
+ }
}
}
-}
\ No newline at end of file
+}
diff --git a/Mastodon.xcodeproj/project.pbxproj b/Mastodon.xcodeproj/project.pbxproj
index c8b2674b5..3f70434b6 100644
--- a/Mastodon.xcodeproj/project.pbxproj
+++ b/Mastodon.xcodeproj/project.pbxproj
@@ -23,6 +23,17 @@
0FB3D33825E6401400AAD544 /* PickServerCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FB3D33725E6401400AAD544 /* PickServerCell.swift */; };
164F0EBC267D4FE400249499 /* BoopSound.caf in Resources */ = {isa = PBXBuildFile; fileRef = 164F0EBB267D4FE400249499 /* BoopSound.caf */; };
18BC7629F65E6DB12CB8416D /* Pods_Mastodon_MastodonUITests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3C030226D3C73DCC23D67452 /* Pods_Mastodon_MastodonUITests.framework */; };
+ 27D701F5292FC2D60031BCBB /* DataSourceFacade+URL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27D701F4292FC2D60031BCBB /* DataSourceFacade+URL.swift */; };
+ 2A1FE47C2938BB2600784BF1 /* FollowedTagsViewModel+DiffableDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A1FE47B2938BB2600784BF1 /* FollowedTagsViewModel+DiffableDataSource.swift */; };
+ 2A1FE47E2938C11200784BF1 /* Collection+IsNotEmpty.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A1FE47D2938C11200784BF1 /* Collection+IsNotEmpty.swift */; };
+ 2A3F6FE3292ECB5E002E6DA7 /* FollowedTagsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A3F6FE2292ECB5E002E6DA7 /* FollowedTagsViewModel.swift */; };
+ 2A3F6FE5292F6E44002E6DA7 /* FollowedTagsTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A3F6FE4292F6E44002E6DA7 /* FollowedTagsTableViewCell.swift */; };
+ 2A506CF4292CD85800059C37 /* FollowedTagsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A506CF3292CD85800059C37 /* FollowedTagsViewController.swift */; };
+ 2A506CF6292D040100059C37 /* HashtagTimelineHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A506CF5292D040100059C37 /* HashtagTimelineHeaderView.swift */; };
+ 2A76F75C2930D94700B3388D /* HashtagTimelineHeaderViewActionButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A76F75B2930D94700B3388D /* HashtagTimelineHeaderViewActionButton.swift */; };
+ 2A82294F29262EE000D2A1F7 /* AppContext+NextAccount.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A82294E29262EE000D2A1F7 /* AppContext+NextAccount.swift */; };
+ 2AB12E4629362F27006BC925 /* DataSourceFacade+Translate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AB12E4529362F27006BC925 /* DataSourceFacade+Translate.swift */; };
+ 2AE244482927831100BDBF7C /* UIImage+SFSymbols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AE244472927831100BDBF7C /* UIImage+SFSymbols.swift */; };
2D198643261BF09500F0B013 /* SearchResultItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D198642261BF09500F0B013 /* SearchResultItem.swift */; };
2D198649261C0B8500F0B013 /* SearchResultSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D198648261C0B8500F0B013 /* SearchResultSection.swift */; };
2D206B8625F5FB0900143C56 /* Double.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D206B8525F5FB0900143C56 /* Double.swift */; };
@@ -64,7 +75,11 @@
2DAC9E46262FC9FD0062E1A6 /* SuggestionAccountTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DAC9E45262FC9FD0062E1A6 /* SuggestionAccountTableViewCell.swift */; };
2DCB73FD2615C13900EC03D4 /* SearchRecommendCollectionHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DCB73FC2615C13900EC03D4 /* SearchRecommendCollectionHeader.swift */; };
2DE0FACE2615F7AD00CDF649 /* RecommendAccountSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DE0FACD2615F7AD00CDF649 /* RecommendAccountSection.swift */; };
- 2DF123A725C3B0210020F248 /* ActiveLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DF123A625C3B0210020F248 /* ActiveLabel.swift */; };
+ 357FEEAF29523D470021C9DC /* MastodonSDKDynamic in Frameworks */ = {isa = PBXBuildFile; productRef = 357FEEAE29523D470021C9DC /* MastodonSDKDynamic */; };
+ 357FEEB029523D470021C9DC /* MastodonSDKDynamic in Embed Frameworks */ = {isa = PBXBuildFile; productRef = 357FEEAE29523D470021C9DC /* MastodonSDKDynamic */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
+ 357FEEB229523D510021C9DC /* MastodonSDKDynamic in Frameworks */ = {isa = PBXBuildFile; productRef = 357FEEB129523D510021C9DC /* MastodonSDKDynamic */; };
+ 357FEEB629523D5C0021C9DC /* MastodonSDKDynamic in Frameworks */ = {isa = PBXBuildFile; productRef = 357FEEB529523D5C0021C9DC /* MastodonSDKDynamic */; };
+ 357FEEBA29523D660021C9DC /* MastodonSDKDynamic in Frameworks */ = {isa = PBXBuildFile; productRef = 357FEEB929523D660021C9DC /* MastodonSDKDynamic */; };
5B24BBDA262DB14800A9381B /* ReportViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B24BBD7262DB14800A9381B /* ReportViewModel.swift */; };
5B24BBDB262DB14800A9381B /* ReportStatusViewModel+Diffable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B24BBD8262DB14800A9381B /* ReportStatusViewModel+Diffable.swift */; };
5B90C45E262599800002E742 /* SettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B90C456262599800002E742 /* SettingsViewModel.swift */; };
@@ -86,8 +101,16 @@
62FD27D12893707600B205C5 /* BookmarkViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62FD27D02893707600B205C5 /* BookmarkViewController.swift */; };
62FD27D32893707B00B205C5 /* BookmarkViewController+DataSourceProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62FD27D22893707B00B205C5 /* BookmarkViewController+DataSourceProvider.swift */; };
62FD27D52893708A00B205C5 /* BookmarkViewModel+Diffable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62FD27D42893708A00B205C5 /* BookmarkViewModel+Diffable.swift */; };
+ 85904C02293BC0EB0011C817 /* ImageProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85904C01293BC0EB0011C817 /* ImageProvider.swift */; };
+ 85904C04293BC1940011C817 /* URLActivityItemWithMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85904C03293BC1940011C817 /* URLActivityItemWithMetadata.swift */; };
+ 85BC11B32932414900E191CD /* AltViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85BC11B22932414900E191CD /* AltViewController.swift */; };
87FFDA5D898A5C42ADCB35E7 /* Pods_Mastodon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A4ABE34829701A4496C5BB64 /* Pods_Mastodon.framework */; };
C24C97032922F30500BAE8CB /* RefreshControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = C24C97022922F30500BAE8CB /* RefreshControl.swift */; };
+ D87BFC8B291D5C6B00FEE264 /* MastodonLoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D87BFC8A291D5C6B00FEE264 /* MastodonLoginView.swift */; };
+ D87BFC8D291EB81200FEE264 /* MastodonLoginViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D87BFC8C291EB81200FEE264 /* MastodonLoginViewModel.swift */; };
+ D87BFC8F291EC26A00FEE264 /* MastodonLoginServerTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D87BFC8E291EC26A00FEE264 /* MastodonLoginServerTableViewCell.swift */; };
+ D8916DC029211BE500124085 /* ContentSizedTableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8916DBF29211BE500124085 /* ContentSizedTableView.swift */; };
+ D8A6AB6C291C5136003AB663 /* MastodonLoginViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A6AB6B291C5136003AB663 /* MastodonLoginViewController.swift */; };
DB0009A626AEE5DC009B9D2D /* Intents.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = DB0009A926AEE5DC009B9D2D /* Intents.intentdefinition */; settings = {ATTRIBUTES = (codegen, ); }; };
DB0009A726AEE5DC009B9D2D /* Intents.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = DB0009A926AEE5DC009B9D2D /* Intents.intentdefinition */; };
DB0140CF25C42AEE00F9F3CF /* OSLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0140CE25C42AEE00F9F3CF /* OSLog.swift */; };
@@ -112,7 +135,6 @@
DB0618032785A7100030EE79 /* RegisterSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0618022785A7100030EE79 /* RegisterSection.swift */; };
DB0618052785A73D0030EE79 /* RegisterItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0618042785A73D0030EE79 /* RegisterItem.swift */; };
DB0A322E280EE9FD001729D2 /* DiscoveryIntroBannerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0A322D280EE9FD001729D2 /* DiscoveryIntroBannerView.swift */; };
- DB0C947726A7FE840088FB11 /* NotificationAvatarButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0C947626A7FE840088FB11 /* NotificationAvatarButton.swift */; };
DB0EF72B26FDB1D200347686 /* SidebarListCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0EF72A26FDB1D200347686 /* SidebarListCollectionViewCell.swift */; };
DB0EF72E26FDB24F00347686 /* SidebarListContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0EF72D26FDB24F00347686 /* SidebarListContentView.swift */; };
DB0F8150264D1E2500F2A12B /* PickServerLoaderTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0F814F264D1E2500F2A12B /* PickServerLoaderTableViewCell.swift */; };
@@ -152,10 +174,6 @@
DB1FD44425F26CCC004CFCFC /* PickServerSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB1FD44325F26CCC004CFCFC /* PickServerSection.swift */; };
DB1FD45025F26FA1004CFCFC /* MastodonPickServerViewModel+Diffable.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB1FD44F25F26FA1004CFCFC /* MastodonPickServerViewModel+Diffable.swift */; };
DB1FD45A25F27898004CFCFC /* CategoryPickerItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB1FD45925F27898004CFCFC /* CategoryPickerItem.swift */; };
- DB22C92228E700A10082A9E9 /* MastodonSDK in Frameworks */ = {isa = PBXBuildFile; productRef = DB22C92128E700A10082A9E9 /* MastodonSDK */; };
- DB22C92428E700A80082A9E9 /* MastodonSDK in Frameworks */ = {isa = PBXBuildFile; productRef = DB22C92328E700A80082A9E9 /* MastodonSDK */; };
- DB22C92628E700AF0082A9E9 /* MastodonSDK in Frameworks */ = {isa = PBXBuildFile; productRef = DB22C92528E700AF0082A9E9 /* MastodonSDK */; };
- DB22C92828E700B70082A9E9 /* MastodonSDK in Frameworks */ = {isa = PBXBuildFile; productRef = DB22C92728E700B70082A9E9 /* MastodonSDK */; };
DB2B3ABC25E37E15007045F9 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = DB2B3ABE25E37E15007045F9 /* InfoPlist.strings */; };
DB2F073525E8ECF000957B2D /* AuthenticationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB2F073325E8ECF000957B2D /* AuthenticationViewModel.swift */; };
DB2FF510260B113300ADA9FE /* ComposeStatusPollExpiresOptionCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB2FF50F260B113300ADA9FE /* ComposeStatusPollExpiresOptionCollectionViewCell.swift */; };
@@ -289,8 +307,6 @@
DB72602725E36A6F00235243 /* MastodonServerRulesViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB72602625E36A6F00235243 /* MastodonServerRulesViewModel.swift */; };
DB7274F4273BB9B200577D95 /* ListBatchFetchViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB7274F3273BB9B200577D95 /* ListBatchFetchViewModel.swift */; };
DB73B490261F030A002E9E9F /* SafariActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB73B48F261F030A002E9E9F /* SafariActivity.swift */; };
- DB73BF4927140BA300781945 /* UICollectionViewDiffableDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB73BF4827140BA300781945 /* UICollectionViewDiffableDataSource.swift */; };
- DB73BF4B27140C0800781945 /* UITableViewDiffableDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB73BF4A27140C0800781945 /* UITableViewDiffableDataSource.swift */; };
DB75BF1E263C1C1B00EDBF1F /* CustomScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB75BF1D263C1C1B00EDBF1F /* CustomScheduler.swift */; };
DB789A0B25F9F2950071ACA0 /* ComposeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB789A0A25F9F2950071ACA0 /* ComposeViewController.swift */; };
DB789A1225F9F2CC0071ACA0 /* ComposeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB789A1125F9F2CC0071ACA0 /* ComposeViewModel.swift */; };
@@ -475,6 +491,7 @@
dstPath = "";
dstSubfolderSpec = 10;
files = (
+ 357FEEB029523D470021C9DC /* MastodonSDKDynamic in Embed Frameworks */,
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
@@ -513,6 +530,17 @@
0FB3D33725E6401400AAD544 /* PickServerCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PickServerCell.swift; sourceTree = ""; };
164F0EBB267D4FE400249499 /* BoopSound.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = BoopSound.caf; sourceTree = ""; };
1D6D967E77A5357E2C6110D9 /* Pods-Mastodon.asdk - debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Mastodon.asdk - debug.xcconfig"; path = "Target Support Files/Pods-Mastodon/Pods-Mastodon.asdk - debug.xcconfig"; sourceTree = ""; };
+ 27D701F4292FC2D60031BCBB /* DataSourceFacade+URL.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DataSourceFacade+URL.swift"; sourceTree = ""; };
+ 2A1FE47B2938BB2600784BF1 /* FollowedTagsViewModel+DiffableDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FollowedTagsViewModel+DiffableDataSource.swift"; sourceTree = ""; };
+ 2A1FE47D2938C11200784BF1 /* Collection+IsNotEmpty.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Collection+IsNotEmpty.swift"; sourceTree = ""; };
+ 2A3F6FE2292ECB5E002E6DA7 /* FollowedTagsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowedTagsViewModel.swift; sourceTree = ""; };
+ 2A3F6FE4292F6E44002E6DA7 /* FollowedTagsTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowedTagsTableViewCell.swift; sourceTree = ""; };
+ 2A506CF3292CD85800059C37 /* FollowedTagsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowedTagsViewController.swift; sourceTree = ""; };
+ 2A506CF5292D040100059C37 /* HashtagTimelineHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HashtagTimelineHeaderView.swift; sourceTree = ""; };
+ 2A76F75B2930D94700B3388D /* HashtagTimelineHeaderViewActionButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HashtagTimelineHeaderViewActionButton.swift; sourceTree = ""; };
+ 2A82294E29262EE000D2A1F7 /* AppContext+NextAccount.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppContext+NextAccount.swift"; sourceTree = ""; };
+ 2AB12E4529362F27006BC925 /* DataSourceFacade+Translate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DataSourceFacade+Translate.swift"; sourceTree = ""; };
+ 2AE244472927831100BDBF7C /* UIImage+SFSymbols.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+SFSymbols.swift"; sourceTree = ""; };
2D198642261BF09500F0B013 /* SearchResultItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultItem.swift; sourceTree = ""; };
2D198648261C0B8500F0B013 /* SearchResultSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultSection.swift; sourceTree = ""; };
2D206B8525F5FB0900143C56 /* Double.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Double.swift; sourceTree = ""; };
@@ -555,7 +583,6 @@
2DAC9E45262FC9FD0062E1A6 /* SuggestionAccountTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SuggestionAccountTableViewCell.swift; sourceTree = ""; };
2DCB73FC2615C13900EC03D4 /* SearchRecommendCollectionHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchRecommendCollectionHeader.swift; sourceTree = ""; };
2DE0FACD2615F7AD00CDF649 /* RecommendAccountSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecommendAccountSection.swift; sourceTree = ""; };
- 2DF123A625C3B0210020F248 /* ActiveLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActiveLabel.swift; sourceTree = ""; };
2E1F6A67FDF9771D3E064FDC /* Pods-Mastodon.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Mastodon.debug.xcconfig"; path = "Target Support Files/Pods-Mastodon/Pods-Mastodon.debug.xcconfig"; sourceTree = ""; };
3B7FD8F28DDA8FBCE5562B78 /* Pods-NotificationService.asdk - debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NotificationService.asdk - debug.xcconfig"; path = "Target Support Files/Pods-NotificationService/Pods-NotificationService.asdk - debug.xcconfig"; sourceTree = ""; };
3C030226D3C73DCC23D67452 /* Pods_Mastodon_MastodonUITests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Mastodon_MastodonUITests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -591,6 +618,9 @@
7CB58D292DA7ACEF179A9050 /* Pods-Mastodon.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Mastodon.profile.xcconfig"; path = "Target Support Files/Pods-Mastodon/Pods-Mastodon.profile.xcconfig"; sourceTree = ""; };
7CEFFAE9AF9284B13C0A758D /* Pods-MastodonTests.asdk - debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MastodonTests.asdk - debug.xcconfig"; path = "Target Support Files/Pods-MastodonTests/Pods-MastodonTests.asdk - debug.xcconfig"; sourceTree = ""; };
819CEC9DCAD8E8E7BD85A7BB /* Pods-Mastodon.asdk.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Mastodon.asdk.xcconfig"; path = "Target Support Files/Pods-Mastodon/Pods-Mastodon.asdk.xcconfig"; sourceTree = ""; };
+ 85904C01293BC0EB0011C817 /* ImageProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageProvider.swift; sourceTree = ""; };
+ 85904C03293BC1940011C817 /* URLActivityItemWithMetadata.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLActivityItemWithMetadata.swift; sourceTree = ""; };
+ 85BC11B22932414900E191CD /* AltViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AltViewController.swift; sourceTree = ""; };
8850E70A1D5FF51432E43653 /* Pods-Mastodon-MastodonUITests.asdk - release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Mastodon-MastodonUITests.asdk - release.xcconfig"; path = "Target Support Files/Pods-Mastodon-MastodonUITests/Pods-Mastodon-MastodonUITests.asdk - release.xcconfig"; sourceTree = ""; };
8E79CCBE51FBC3F7FE8CF49F /* Pods-MastodonTests.release snapshot.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MastodonTests.release snapshot.xcconfig"; path = "Target Support Files/Pods-MastodonTests/Pods-MastodonTests.release snapshot.xcconfig"; sourceTree = ""; };
8ED8C4B1F1BA2DCFF2926BB1 /* Pods-Mastodon-NotificationService.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Mastodon-NotificationService.debug.xcconfig"; path = "Target Support Files/Pods-Mastodon-NotificationService/Pods-Mastodon-NotificationService.debug.xcconfig"; sourceTree = ""; };
@@ -608,6 +638,11 @@
C3789232A52F43529CA67E95 /* Pods-MastodonIntent.asdk - debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MastodonIntent.asdk - debug.xcconfig"; path = "Target Support Files/Pods-MastodonIntent/Pods-MastodonIntent.asdk - debug.xcconfig"; sourceTree = ""; };
CD92E0F10BDE4FE7C4B999F2 /* Pods_MastodonTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_MastodonTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
D7D7CF93E262178800077512 /* Pods-Mastodon-AppShared.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Mastodon-AppShared.debug.xcconfig"; path = "Target Support Files/Pods-Mastodon-AppShared/Pods-Mastodon-AppShared.debug.xcconfig"; sourceTree = ""; };
+ D87BFC8A291D5C6B00FEE264 /* MastodonLoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonLoginView.swift; sourceTree = ""; };
+ D87BFC8C291EB81200FEE264 /* MastodonLoginViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonLoginViewModel.swift; sourceTree = ""; };
+ D87BFC8E291EC26A00FEE264 /* MastodonLoginServerTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonLoginServerTableViewCell.swift; sourceTree = ""; };
+ D8916DBF29211BE500124085 /* ContentSizedTableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentSizedTableView.swift; sourceTree = ""; };
+ D8A6AB6B291C5136003AB663 /* MastodonLoginViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonLoginViewController.swift; sourceTree = ""; };
DB0009A826AEE5DC009B9D2D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.intentdefinition; name = Base; path = Base.lproj/Intents.intentdefinition; sourceTree = ""; };
DB0009AD26AEE5E4009B9D2D /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/Intents.strings; sourceTree = ""; };
DB0140CE25C42AEE00F9F3CF /* OSLog.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OSLog.swift; sourceTree = ""; };
@@ -632,9 +667,7 @@
DB0618022785A7100030EE79 /* RegisterSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegisterSection.swift; sourceTree = ""; };
DB0618042785A73D0030EE79 /* RegisterItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegisterItem.swift; sourceTree = ""; };
DB0618062785A8880030EE79 /* MastodonRegisterViewModel+Diffable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MastodonRegisterViewModel+Diffable.swift"; sourceTree = ""; };
- DB0618092785B2AB0030EE79 /* MastodonRegisterAvatarTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonRegisterAvatarTableViewCell.swift; sourceTree = ""; };
DB0A322D280EE9FD001729D2 /* DiscoveryIntroBannerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiscoveryIntroBannerView.swift; sourceTree = ""; };
- DB0C947626A7FE840088FB11 /* NotificationAvatarButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationAvatarButton.swift; sourceTree = ""; };
DB0EF72A26FDB1D200347686 /* SidebarListCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarListCollectionViewCell.swift; sourceTree = ""; };
DB0EF72D26FDB24F00347686 /* SidebarListContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarListContentView.swift; sourceTree = ""; };
DB0F814E264CFFD300F2A12B /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/InfoPlist.strings; sourceTree = ""; };
@@ -846,8 +879,6 @@
DB72602625E36A6F00235243 /* MastodonServerRulesViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonServerRulesViewModel.swift; sourceTree = ""; };
DB7274F3273BB9B200577D95 /* ListBatchFetchViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListBatchFetchViewModel.swift; sourceTree = ""; };
DB73B48F261F030A002E9E9F /* SafariActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafariActivity.swift; sourceTree = ""; };
- DB73BF4827140BA300781945 /* UICollectionViewDiffableDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UICollectionViewDiffableDataSource.swift; sourceTree = ""; };
- DB73BF4A27140C0800781945 /* UITableViewDiffableDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITableViewDiffableDataSource.swift; sourceTree = ""; };
DB75BF1D263C1C1B00EDBF1F /* CustomScheduler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomScheduler.swift; sourceTree = ""; };
DB789A0A25F9F2950071ACA0 /* ComposeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeViewController.swift; sourceTree = ""; };
DB789A1125F9F2CC0071ACA0 /* ComposeViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeViewModel.swift; sourceTree = ""; };
@@ -855,8 +886,6 @@
DB7A9F922818F33C0016AF98 /* MastodonServerRulesViewController+Debug.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MastodonServerRulesViewController+Debug.swift"; sourceTree = ""; };
DB7F48442620241000796008 /* ProfileHeaderViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileHeaderViewModel.swift; sourceTree = ""; };
DB8190C52601FF0400020C08 /* AttachmentContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttachmentContainerView.swift; sourceTree = ""; };
- DB8481142788121200BBEABA /* MastodonRegisterTextFieldTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonRegisterTextFieldTableViewCell.swift; sourceTree = ""; };
- DB84811627883C2600BBEABA /* MastodonRegisterPasswordHintTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonRegisterPasswordHintTableViewCell.swift; sourceTree = ""; };
DB848E32282B62A800A302CC /* ReportResultView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportResultView.swift; sourceTree = ""; };
DB852D1826FAEB6B00FC9D81 /* SidebarViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarViewController.swift; sourceTree = ""; };
DB852D1B26FB021500FC9D81 /* RootSplitViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootSplitViewController.swift; sourceTree = ""; };
@@ -888,6 +917,12 @@
DB938F0826240F3C00E5B6C1 /* RemoteThreadViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteThreadViewModel.swift; sourceTree = ""; };
DB938F0E2624119800E5B6C1 /* ThreadViewModel+LoadThreadState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ThreadViewModel+LoadThreadState.swift"; sourceTree = ""; };
DB938F1E2624382F00E5B6C1 /* ThreadViewModel+Diffable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ThreadViewModel+Diffable.swift"; sourceTree = ""; };
+ DB96C25D292505FE00F3B85D /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/Intents.strings; sourceTree = ""; };
+ DB96C25E292505FF00F3B85D /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/InfoPlist.strings; sourceTree = ""; };
+ DB96C25F292505FF00F3B85D /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = cs; path = cs.lproj/Intents.stringsdict; sourceTree = ""; };
+ DB96C260292506D600F3B85D /* sl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sl; path = sl.lproj/Intents.strings; sourceTree = ""; };
+ DB96C261292506D700F3B85D /* sl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sl; path = sl.lproj/InfoPlist.strings; sourceTree = ""; };
+ DB96C262292506D700F3B85D /* sl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = sl; path = sl.lproj/Intents.stringsdict; sourceTree = ""; };
DB98EB4627B0DFAA0082E365 /* ReportStatusViewModel+State.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ReportStatusViewModel+State.swift"; sourceTree = ""; };
DB98EB4827B0F0CD0082E365 /* ReportStatusTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportStatusTableViewCell.swift; sourceTree = ""; };
DB98EB4B27B0F2BC0082E365 /* ReportStatusTableViewCell+ViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ReportStatusTableViewCell+ViewModel.swift"; sourceTree = ""; };
@@ -1048,7 +1083,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
- DB22C92428E700A80082A9E9 /* MastodonSDK in Frameworks */,
+ 357FEEAF29523D470021C9DC /* MastodonSDKDynamic in Frameworks */,
DBF96326262EC0A6001D8D25 /* AuthenticationServices.framework in Frameworks */,
87FFDA5D898A5C42ADCB35E7 /* Pods_Mastodon.framework in Frameworks */,
);
@@ -1075,7 +1110,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
- DB22C92828E700B70082A9E9 /* MastodonSDK in Frameworks */,
+ 357FEEBA29523D660021C9DC /* MastodonSDKDynamic in Frameworks */,
DB8FABC726AEC7B2008E5AF4 /* Intents.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -1084,7 +1119,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
- DB22C92628E700AF0082A9E9 /* MastodonSDK in Frameworks */,
+ 357FEEB629523D5C0021C9DC /* MastodonSDKDynamic in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1092,7 +1127,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
- DB22C92228E700A10082A9E9 /* MastodonSDK in Frameworks */,
+ 357FEEB229523D510021C9DC /* MastodonSDKDynamic in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1107,6 +1142,8 @@
0F202200261326E6000C64BF /* HashtagTimelineViewModel.swift */,
0F20220626134DA4000C64BF /* HashtagTimelineViewModel+Diffable.swift */,
0F20222C261457EE000C64BF /* HashtagTimelineViewModel+State.swift */,
+ 2A506CF5292D040100059C37 /* HashtagTimelineHeaderView.swift */,
+ 2A76F75B2930D94700B3388D /* HashtagTimelineHeaderViewActionButton.swift */,
);
path = HashtagTimeline;
sourceTree = "";
@@ -1209,6 +1246,17 @@
path = Pods;
sourceTree = "";
};
+ 2A506CF2292CD83B00059C37 /* FollowedTags */ = {
+ isa = PBXGroup;
+ children = (
+ 2A506CF3292CD85800059C37 /* FollowedTagsViewController.swift */,
+ 2A3F6FE2292ECB5E002E6DA7 /* FollowedTagsViewModel.swift */,
+ 2A1FE47B2938BB2600784BF1 /* FollowedTagsViewModel+DiffableDataSource.swift */,
+ 2A3F6FE4292F6E44002E6DA7 /* FollowedTagsTableViewCell.swift */,
+ );
+ path = FollowedTags;
+ sourceTree = "";
+ };
2D152A8A25C295B8009AA50C /* Content */ = {
isa = PBXGroup;
children = (
@@ -1497,9 +1545,22 @@
path = Bookmark;
sourceTree = "";
};
+ D8A6AB68291C50F3003AB663 /* Login */ = {
+ isa = PBXGroup;
+ children = (
+ D8A6AB6B291C5136003AB663 /* MastodonLoginViewController.swift */,
+ D87BFC8A291D5C6B00FEE264 /* MastodonLoginView.swift */,
+ D87BFC8C291EB81200FEE264 /* MastodonLoginViewModel.swift */,
+ D87BFC8E291EC26A00FEE264 /* MastodonLoginServerTableViewCell.swift */,
+ D8916DBF29211BE500124085 /* ContentSizedTableView.swift */,
+ );
+ path = Login;
+ sourceTree = "";
+ };
DB01409B25C40BB600F9F3CF /* Onboarding */ = {
isa = PBXGroup;
children = (
+ D8A6AB68291C50F3003AB663 /* Login */,
DB68A03825E900CC00CFDF14 /* Share */,
0FAA0FDD25E0B5700017CCDE /* Welcome */,
0FAA102525E1125D0017CCDE /* PickServer */,
@@ -1564,16 +1625,6 @@
path = Cell;
sourceTree = "";
};
- DB06180B2785B2AF0030EE79 /* Cell */ = {
- isa = PBXGroup;
- children = (
- DB0618092785B2AB0030EE79 /* MastodonRegisterAvatarTableViewCell.swift */,
- DB8481142788121200BBEABA /* MastodonRegisterTextFieldTableViewCell.swift */,
- DB84811627883C2600BBEABA /* MastodonRegisterPasswordHintTableViewCell.swift */,
- );
- path = Cell;
- sourceTree = "";
- };
DB0A322F280EEA00001729D2 /* View */ = {
isa = PBXGroup;
children = (
@@ -1582,14 +1633,6 @@
path = View;
sourceTree = "";
};
- DB0C947826A7FE950088FB11 /* Button */ = {
- isa = PBXGroup;
- children = (
- DB0C947626A7FE840088FB11 /* NotificationAvatarButton.swift */,
- );
- path = Button;
- sourceTree = "";
- };
DB0EF72C26FDB1D600347686 /* View */ = {
isa = PBXGroup;
children = (
@@ -1922,6 +1965,7 @@
DB6180F026391CAB0018D199 /* Image */,
DB6180E1263919780018D199 /* Paging */,
DB6180DC263918E30018D199 /* MediaPreviewViewController.swift */,
+ 85BC11B22932414900E191CD /* AltViewController.swift */,
DB6180F926391F2E0018D199 /* MediaPreviewViewModel.swift */,
);
path = MediaPreview;
@@ -2069,10 +2113,12 @@
DB63F778279ABF9C00455B82 /* DataSourceFacade+Reblog.swift */,
DB63F77A279ACAE500455B82 /* DataSourceFacade+Favorite.swift */,
DB0FCB67279507EF006C02E2 /* DataSourceFacade+Meta.swift */,
+ 27D701F4292FC2D60031BCBB /* DataSourceFacade+URL.swift */,
DB0FCB79279576A2006C02E2 /* DataSourceFacade+Thread.swift */,
DB63F74627990B0600455B82 /* DataSourceFacade+Hashtag.swift */,
DB63F7532799491600455B82 /* DataSourceFacade+SearchHistory.swift */,
DB159C2A27A17BAC0068DC77 /* DataSourceFacade+Media.swift */,
+ 2AB12E4529362F27006BC925 /* DataSourceFacade+Translate.swift */,
DB697DD5278F4C29004EF2F7 /* DataSourceProvider.swift */,
DB697DDA278F4DE3004EF2F7 /* DataSourceProvider+StatusTableViewCellDelegate.swift */,
DB023D2927A0FE5C005AC798 /* DataSourceProvider+NotificationTableViewCellDelegate.swift */,
@@ -2205,7 +2251,7 @@
DB8AF56225C138BC002E6C99 /* Extension */ = {
isa = PBXGroup;
children = (
- 2DF123A625C3B0210020F248 /* ActiveLabel.swift */,
+ 2A82294E29262EE000D2A1F7 /* AppContext+NextAccount.swift */,
5DF1056325F887CB00D6C0D4 /* AVPlayer.swift */,
2D206B8525F5FB0900143C56 /* Double.swift */,
DBB3BA2926A81C020004F2D4 /* FLAnimatedImageView.swift */,
@@ -2225,9 +2271,9 @@
2D3F9E0325DFA133004262D9 /* UITapGestureRecognizer.swift */,
2D84350425FF858100EECE90 /* UIScrollView.swift */,
DB9E0D6E25EE008500CFDD76 /* UIInterpolatingMotionEffect.swift */,
+ 2AE244472927831100BDBF7C /* UIImage+SFSymbols.swift */,
DBCC3B2F261440A50045B23D /* UITabBarController.swift */,
- DB73BF4827140BA300781945 /* UICollectionViewDiffableDataSource.swift */,
- DB73BF4A27140C0800781945 /* UITableViewDiffableDataSource.swift */,
+ 2A1FE47D2938C11200784BF1 /* Collection+IsNotEmpty.swift */,
);
path = Extension;
sourceTree = "";
@@ -2336,7 +2382,6 @@
isa = PBXGroup;
children = (
DB63F765279A5E5600455B82 /* NotificationTimeline */,
- DB0C947826A7FE950088FB11 /* Button */,
2D35237F26256F470031AF25 /* Cell */,
DB9D6BF725E4F5690051B173 /* NotificationViewController.swift */,
2D607AD726242FC500B70763 /* NotificationViewModel.swift */,
@@ -2347,6 +2392,7 @@
DB9D6C0825E4F5A60051B173 /* Profile */ = {
isa = PBXGroup;
children = (
+ 2A506CF2292CD83B00059C37 /* FollowedTags */,
62047EBE28874C8F00A3BA5D /* Bookmark */,
DBB525462611ED57002F1F29 /* Header */,
DBB525262611EBDA002F1F29 /* Paging */,
@@ -2495,6 +2541,8 @@
isa = PBXGroup;
children = (
DBF3B7402733EB9400E21627 /* MastodonLocalCode.swift */,
+ 85904C01293BC0EB0011C817 /* ImageProvider.swift */,
+ 85904C03293BC1940011C817 /* URLActivityItemWithMetadata.swift */,
);
path = Helper;
sourceTree = "";
@@ -2540,7 +2588,6 @@
DBE0821A25CD382900FD6BBD /* Register */ = {
isa = PBXGroup;
children = (
- DB06180B2785B2AF0030EE79 /* Cell */,
DBE0821425CD382600FD6BBD /* MastodonRegisterViewController.swift */,
2D939AE725EE1CF80076FA61 /* MastodonRegisterViewController+Avatar.swift */,
DBE0822325CD3F1E00FD6BBD /* MastodonRegisterViewModel.swift */,
@@ -2683,9 +2730,9 @@
DB427DCF25BAA00100D1B89D /* Frameworks */,
DB89BA0825C10FD0008580ED /* Embed Frameworks */,
DBF8AE1B263293E400C9C23C /* Embed Foundation Extensions */,
- DB3D100425BAA71500EAA174 /* ShellScript */,
- DB025B8E278D6448002F581E /* ShellScript */,
- DB697DD2278F48D5004EF2F7 /* ShellScript */,
+ DB3D100425BAA71500EAA174 /* Run SwiftGen */,
+ DB025B8E278D6448002F581E /* Run Sourcery: Core Data */,
+ DB697DD2278F48D5004EF2F7 /* Run Sourcery */,
);
buildRules = (
);
@@ -2696,7 +2743,7 @@
);
name = Mastodon;
packageProductDependencies = (
- DB22C92328E700A80082A9E9 /* MastodonSDK */,
+ 357FEEAE29523D470021C9DC /* MastodonSDKDynamic */,
);
productName = Mastodon;
productReference = DB427DD225BAA00100D1B89D /* Mastodon.app */;
@@ -2755,7 +2802,7 @@
);
name = MastodonIntent;
packageProductDependencies = (
- DB22C92728E700B70082A9E9 /* MastodonSDK */,
+ 357FEEB929523D660021C9DC /* MastodonSDKDynamic */,
);
productName = MastodonIntent;
productReference = DB8FABC626AEC7B2008E5AF4 /* MastodonIntent.appex */;
@@ -2775,7 +2822,7 @@
);
name = ShareActionExtension;
packageProductDependencies = (
- DB22C92528E700AF0082A9E9 /* MastodonSDK */,
+ 357FEEB529523D5C0021C9DC /* MastodonSDKDynamic */,
);
productName = ShareActionExtension;
productReference = DBC6461226A170AB00B0E31B /* ShareActionExtension.appex */;
@@ -2795,7 +2842,7 @@
);
name = NotificationService;
packageProductDependencies = (
- DB22C92128E700A10082A9E9 /* MastodonSDK */,
+ 357FEEB129523D510021C9DC /* MastodonSDKDynamic */,
);
productName = NotificationService;
productReference = DBF8AE13263293E400C9C23C /* NotificationService.appex */;
@@ -2863,6 +2910,8 @@
gd,
"es-AR",
fi,
+ cs,
+ sl,
);
mainGroup = DB427DC925BAA00100D1B89D;
packageReferences = (
@@ -3001,7 +3050,7 @@
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
- DB025B8E278D6448002F581E /* ShellScript */ = {
+ DB025B8E278D6448002F581E /* Run Sourcery: Core Data */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 12;
@@ -3011,6 +3060,7 @@
);
inputPaths = (
);
+ name = "Run Sourcery: Core Data";
outputFileListPaths = (
);
outputPaths = (
@@ -3019,7 +3069,7 @@
shellPath = /bin/sh;
shellScript = "if [[ -f \"${PODS_ROOT}/Sourcery/bin/sourcery\" ]]; then\n \"${PODS_ROOT}/Sourcery/bin/sourcery\" --config ./MastodonSDK/Sources/CoreDataStack\nelse\n echo \"warning: Sourcery is not installed. Run 'pod install --repo-update' to install it.\"\nfi\n";
};
- DB3D100425BAA71500EAA174 /* ShellScript */ = {
+ DB3D100425BAA71500EAA174 /* Run SwiftGen */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 12;
@@ -3029,6 +3079,7 @@
);
inputPaths = (
);
+ name = "Run SwiftGen";
outputFileListPaths = (
);
outputPaths = (
@@ -3037,7 +3088,7 @@
shellPath = /bin/sh;
shellScript = "if [[ -f \"${PODS_ROOT}/SwiftGen/bin/swiftgen\" ]]; then\n \"${PODS_ROOT}/SwiftGen/bin/swiftgen\" \nelse\n echo \"warning: SwiftGen is not installed. Run 'pod install --repo-update' to install it.\"\nfi\n";
};
- DB697DD2278F48D5004EF2F7 /* ShellScript */ = {
+ DB697DD2278F48D5004EF2F7 /* Run Sourcery */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 12;
@@ -3047,6 +3098,7 @@
);
inputPaths = (
);
+ name = "Run Sourcery";
outputFileListPaths = (
);
outputPaths = (
@@ -3116,6 +3168,7 @@
DB03A793272A7E5700EE37C5 /* SidebarListHeaderView.swift in Sources */,
DB4FFC2B269EC39600D62E92 /* SearchToSearchDetailViewControllerAnimatedTransitioning.swift in Sources */,
DB5B7298273112C800081888 /* FollowingListViewModel.swift in Sources */,
+ 27D701F5292FC2D60031BCBB /* DataSourceFacade+URL.swift in Sources */,
0FB3D2F725E4C24D00AAD544 /* MastodonPickServerViewModel.swift in Sources */,
DB5B54AE2833C15F00DEF8B2 /* UserListViewModel+Diffable.swift in Sources */,
DB482A3F261331E8008AE74C /* UserTimelineViewModel+State.swift in Sources */,
@@ -3142,6 +3195,7 @@
62FD27D52893708A00B205C5 /* BookmarkViewModel+Diffable.swift in Sources */,
DB72602725E36A6F00235243 /* MastodonServerRulesViewModel.swift in Sources */,
2D364F7225E66D7500204FDC /* MastodonResendEmailViewController.swift in Sources */,
+ 85904C04293BC1940011C817 /* URLActivityItemWithMetadata.swift in Sources */,
DB68A06325E905E000CFDF14 /* UIApplication.swift in Sources */,
DB02CDAB26256A9500D0A2AF /* ThreadReplyLoaderTableViewCell.swift in Sources */,
DBEFCD80282A2AA900C0ABEA /* ReportServerRulesViewModel.swift in Sources */,
@@ -3185,6 +3239,7 @@
DB5B54A32833BD1A00DEF8B2 /* UserListViewModel.swift in Sources */,
DBF156DF2701B17600EC00B7 /* SidebarAddAccountCollectionViewCell.swift in Sources */,
DB0617F1278413D00030EE79 /* PickServerServerSectionTableHeaderView.swift in Sources */,
+ D87BFC8F291EC26A00FEE264 /* MastodonLoginServerTableViewCell.swift in Sources */,
DB0FCB7E27958957006C02E2 /* StatusThreadRootTableViewCell+ViewModel.swift in Sources */,
DB789A0B25F9F2950071ACA0 /* ComposeViewController.swift in Sources */,
DB938F0926240F3C00E5B6C1 /* RemoteThreadViewModel.swift in Sources */,
@@ -3207,7 +3262,6 @@
DB603113279EBEBA00A935FE /* DataSourceFacade+Block.swift in Sources */,
DB63F777279A9A2A00455B82 /* NotificationView+Configuration.swift in Sources */,
DB029E95266A20430062874E /* MastodonAuthenticationController.swift in Sources */,
- DB0C947726A7FE840088FB11 /* NotificationAvatarButton.swift in Sources */,
5B90C461262599800002E742 /* SettingsLinkTableViewCell.swift in Sources */,
DB6180DD263918E30018D199 /* MediaPreviewViewController.swift in Sources */,
DBE3CDEC261C6B2900430CC6 /* FavoriteViewController.swift in Sources */,
@@ -3230,6 +3284,7 @@
DBF1572F27046F1A00EC00B7 /* SecondaryPlaceholderViewController.swift in Sources */,
2D4AD8A826316D3500613EFC /* SelectedAccountItem.swift in Sources */,
DBE3CDFB261C6CA500430CC6 /* FavoriteViewModel.swift in Sources */,
+ 2AB12E4629362F27006BC925 /* DataSourceFacade+Translate.swift in Sources */,
DBE3CE01261D623D00430CC6 /* FavoriteViewModel+State.swift in Sources */,
2D82BA0525E7897700E36F0F /* MastodonResendEmailViewModelNavigationDelegateShim.swift in Sources */,
2D38F1EB25CD477000561493 /* HomeTimelineViewModel+LoadLatestState.swift in Sources */,
@@ -3246,12 +3301,11 @@
DB5B549D2833A67400DEF8B2 /* FamiliarFollowersViewModel.swift in Sources */,
DBB9759C262462E1004620BD /* ThreadMetaView.swift in Sources */,
DB5B729E273113F300081888 /* FollowingListViewModel+State.swift in Sources */,
- 2DF123A725C3B0210020F248 /* ActiveLabel.swift in Sources */,
DBF9814C265E339500E4BA07 /* ProfileFieldAddEntryCollectionViewCell.swift in Sources */,
DB63F76227996B6600455B82 /* SearchHistoryViewController+DataSourceProvider.swift in Sources */,
- DB73BF4927140BA300781945 /* UICollectionViewDiffableDataSource.swift in Sources */,
DBA5E7AB263BD3F5004598BB /* TimelineTableViewCellContextMenuConfiguration.swift in Sources */,
DB73B490261F030A002E9E9F /* SafariActivity.swift in Sources */,
+ 2AE244482927831100BDBF7C /* UIImage+SFSymbols.swift in Sources */,
DB63F7492799126300455B82 /* FollowerListViewController+DataSourceProvider.swift in Sources */,
2D198643261BF09500F0B013 /* SearchResultItem.swift in Sources */,
2DAC9E38262FC2320062E1A6 /* SuggestionAccountViewController.swift in Sources */,
@@ -3262,20 +3316,24 @@
2D82B9FF25E7863200E36F0F /* OnboardingViewControllerAppearance.swift in Sources */,
DB025B78278D606A002F581E /* StatusItem.swift in Sources */,
DB697DD4278F4927004EF2F7 /* StatusTableViewCellDelegate.swift in Sources */,
+ 2A506CF6292D040100059C37 /* HashtagTimelineHeaderView.swift in Sources */,
DBA5E7A5263BD28C004598BB /* ContextMenuImagePreviewViewModel.swift in Sources */,
DB3E6FF52807C40300B035AE /* DiscoveryForYouViewController.swift in Sources */,
2D38F1E525CD46C100561493 /* HomeTimelineViewModel.swift in Sources */,
DB0FCB842796B2A2006C02E2 /* FavoriteViewController+DataSourceProvider.swift in Sources */,
DB0FCB68279507EF006C02E2 /* DataSourceFacade+Meta.swift in Sources */,
+ 2A1FE47E2938C11200784BF1 /* Collection+IsNotEmpty.swift in Sources */,
2D84350525FF858100EECE90 /* UIScrollView.swift in Sources */,
6213AF5A28939C8400BCADB6 /* BookmarkViewModel.swift in Sources */,
5B24BBDB262DB14800A9381B /* ReportStatusViewModel+Diffable.swift in Sources */,
DB4F0968269ED8AD00D62E92 /* SearchHistoryTableHeaderView.swift in Sources */,
0FB3D2FE25E4CB6400AAD544 /* OnboardingHeadlineTableViewCell.swift in Sources */,
5DA732CC2629CEF500A92342 /* UIView+Remove.swift in Sources */,
+ 2A506CF4292CD85800059C37 /* FollowedTagsViewController.swift in Sources */,
DB1D843026566512000346B3 /* KeyboardPreference.swift in Sources */,
DB852D1926FAEB6B00FC9D81 /* SidebarViewController.swift in Sources */,
2D206B9225F60EA700143C56 /* UIControl.swift in Sources */,
+ 85904C02293BC0EB0011C817 /* ImageProvider.swift in Sources */,
DBDFF1932805554900557A48 /* DiscoveryPostsViewModel.swift in Sources */,
DB3E6FE72806A7A200B035AE /* DiscoveryItem.swift in Sources */,
DB8AF55D25C138B7002E6C99 /* UIViewController.swift in Sources */,
@@ -3304,9 +3362,11 @@
DB63F769279A5EBB00455B82 /* NotificationTimelineViewModel+Diffable.swift in Sources */,
DBFEEC9B279BDDD9004F81DD /* ProfileAboutViewModel+Diffable.swift in Sources */,
DBB525562611EDCA002F1F29 /* UserTimelineViewModel.swift in Sources */,
+ D8916DC029211BE500124085 /* ContentSizedTableView.swift in Sources */,
DB0618012785732C0030EE79 /* ServerRulesTableViewCell.swift in Sources */,
DB98EB5C27B10A730082E365 /* ReportSupplementaryViewModel.swift in Sources */,
DB0617EF277F12720030EE79 /* NavigationActionView.swift in Sources */,
+ D87BFC8D291EB81200FEE264 /* MastodonLoginViewModel.swift in Sources */,
DB1FD43625F26899004CFCFC /* MastodonPickServerViewModel+LoadIndexedServerState.swift in Sources */,
2D939AE825EE1CF80076FA61 /* MastodonRegisterViewController+Avatar.swift in Sources */,
DB1D186C25EF5BA7003F1F23 /* PollTableView.swift in Sources */,
@@ -3332,7 +3392,6 @@
DB1E346825F518E20079D7DF /* CategoryPickerSection.swift in Sources */,
DB7274F4273BB9B200577D95 /* ListBatchFetchViewModel.swift in Sources */,
DB0618052785A73D0030EE79 /* RegisterItem.swift in Sources */,
- DB73BF4B27140C0800781945 /* UITableViewDiffableDataSource.swift in Sources */,
DBB525642612C988002F1F29 /* MeProfileViewModel.swift in Sources */,
DB3EA8EF281B837000598866 /* DiscoveryCommunityViewController+DataSourceProvider.swift in Sources */,
DB6B74EF272FB55000C70B6E /* FollowerListViewController.swift in Sources */,
@@ -3343,6 +3402,7 @@
DB938EED2623F79B00E5B6C1 /* ThreadViewModel.swift in Sources */,
DB6988DE2848D11C002398EF /* PagerTabStripNavigateable.swift in Sources */,
2DCB73FD2615C13900EC03D4 /* SearchRecommendCollectionHeader.swift in Sources */,
+ 2A1FE47C2938BB2600784BF1 /* FollowedTagsViewModel+DiffableDataSource.swift in Sources */,
DB852D1C26FB021500FC9D81 /* RootSplitViewController.swift in Sources */,
DB697DD1278F4871004EF2F7 /* AutoGenerateTableViewDelegate.swift in Sources */,
DB02CDBF2625AE5000D0A2AF /* AdaptiveUserInterfaceStyleBarButtonItem.swift in Sources */,
@@ -3375,6 +3435,7 @@
DBCC3B8F26148F7B0045B23D /* CachedProfileViewModel.swift in Sources */,
DB4F097526A037F500D62E92 /* SearchHistoryViewModel.swift in Sources */,
DB3EA8E9281B7A3700598866 /* DiscoveryCommunityViewModel.swift in Sources */,
+ D87BFC8B291D5C6B00FEE264 /* MastodonLoginView.swift in Sources */,
DB6180F826391D660018D199 /* MediaPreviewingViewController.swift in Sources */,
DBEFCD71282A12B200C0ABEA /* ReportReasonViewController.swift in Sources */,
DB0140CF25C42AEE00F9F3CF /* OSLog.swift in Sources */,
@@ -3433,16 +3494,20 @@
DBCC3B30261440A50045B23D /* UITabBarController.swift in Sources */,
DB3E6FE42806A5B800B035AE /* DiscoverySection.swift in Sources */,
DB8190C62601FF0400020C08 /* AttachmentContainerView.swift in Sources */,
+ 2A76F75C2930D94700B3388D /* HashtagTimelineHeaderViewActionButton.swift in Sources */,
DB697DDB278F4DE3004EF2F7 /* DataSourceProvider+StatusTableViewCellDelegate.swift in Sources */,
DB87D4512609CF1E00D12C0D /* ComposeStatusPollOptionAppendEntryCollectionViewCell.swift in Sources */,
DBB45B5627B39FC9002DC5A7 /* MediaPreviewVideoViewController.swift in Sources */,
+ D8A6AB6C291C5136003AB663 /* MastodonLoginViewController.swift in Sources */,
DB0FCB8027968F70006C02E2 /* MastodonStatusThreadViewModel.swift in Sources */,
DB67D08627312E67006A36CF /* WizardViewController.swift in Sources */,
DB6746EB278ED8B0008A6B94 /* PollOptionView+Configuration.swift in Sources */,
DB0F9D54283EB3C000379AF8 /* ProfileHeaderView+ViewModel.swift in Sources */,
+ 2A3F6FE3292ECB5E002E6DA7 /* FollowedTagsViewModel.swift in Sources */,
DBFEEC99279BDCDE004F81DD /* ProfileAboutViewModel.swift in Sources */,
2D198649261C0B8500F0B013 /* SearchResultSection.swift in Sources */,
DB4F097B26A039FF00D62E92 /* SearchHistorySection.swift in Sources */,
+ 2A82294F29262EE000D2A1F7 /* AppContext+NextAccount.swift in Sources */,
DBB525302611EBF3002F1F29 /* ProfilePagingViewModel.swift in Sources */,
DB9F58EC26EF435000E7BBE9 /* AccountViewController.swift in Sources */,
2D5A3D6225CFD9CB002347D6 /* HomeTimelineViewController+DebugAction.swift in Sources */,
@@ -3455,6 +3520,7 @@
DB9D6BFF25E4F5940051B173 /* ProfileViewController.swift in Sources */,
2D4AD8A226316CD200613EFC /* SelectedAccountSection.swift in Sources */,
DB3EA8F1281B9EF600598866 /* DiscoveryCommunityViewModel+Diffable.swift in Sources */,
+ 85BC11B32932414900E191CD /* AltViewController.swift in Sources */,
DB63F775279A997D00455B82 /* NotificationTableViewCell+ViewModel.swift in Sources */,
DB98EB5927B109890082E365 /* ReportSupplementaryViewController.swift in Sources */,
DB0617EB277EF3820030EE79 /* GradientBorderView.swift in Sources */,
@@ -3469,6 +3535,7 @@
5BB04FF5262F0E6D0043BFF6 /* ReportSection.swift in Sources */,
DBEFCD82282A2AB100C0ABEA /* ReportServerRulesView.swift in Sources */,
DBA94436265CBB7400C537E1 /* ProfileFieldItem.swift in Sources */,
+ 2A3F6FE5292F6E44002E6DA7 /* FollowedTagsTableViewCell.swift in Sources */,
C24C97032922F30500BAE8CB /* RefreshControl.swift in Sources */,
DB023D2A27A0FE5C005AC798 /* DataSourceProvider+NotificationTableViewCellDelegate.swift in Sources */,
DB98EB6027B10E150082E365 /* ReportCommentTableViewCell.swift in Sources */,
@@ -3593,6 +3660,8 @@
DBC9E3A6282E15190063A4D9 /* gd */,
DBC9E3A9282E17DF0063A4D9 /* es-AR */,
DB8F40042835EE5E006E7513 /* fi */,
+ DB96C25D292505FE00F3B85D /* cs */,
+ DB96C260292506D600F3B85D /* sl */,
);
name = Intents.intentdefinition;
sourceTree = "";
@@ -3624,6 +3693,8 @@
DBC9E3A7282E15190063A4D9 /* gd */,
DBC9E3AA282E17DF0063A4D9 /* es-AR */,
DB8F40052835EE5E006E7513 /* fi */,
+ DB96C25E292505FF00F3B85D /* cs */,
+ DB96C261292506D700F3B85D /* sl */,
);
name = InfoPlist.strings;
sourceTree = "";
@@ -3671,6 +3742,8 @@
DBC9E3A8282E15190063A4D9 /* gd */,
DBC9E3AB282E17DF0063A4D9 /* es-AR */,
DB8F40062835EE5E006E7513 /* fi */,
+ DB96C25F292505FF00F3B85D /* cs */,
+ DB96C262292506D700F3B85D /* sl */,
);
name = Intents.stringsdict;
sourceTree = "";
@@ -3740,7 +3813,7 @@
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
INTENTS_CODEGEN_LANGUAGE = Swift;
- IPHONEOS_DEPLOYMENT_TARGET = 14.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 15.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
@@ -3798,7 +3871,7 @@
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
INTENTS_CODEGEN_LANGUAGE = Swift;
- IPHONEOS_DEPLOYMENT_TARGET = 14.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 15.0;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SDKROOT = iphoneos;
@@ -3825,7 +3898,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
- MARKETING_VERSION = 1.4.7;
+ MARKETING_VERSION = 1.4.10;
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -3849,12 +3922,13 @@
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_ASSET_PATHS = "Mastodon/Resources/Preview\\ Assets.xcassets";
DEVELOPMENT_TEAM = 5Z4GVSS33P;
+ EXCLUDED_SOURCE_FILE_NAMES = "Mastodon/Resources/Preview\\ Assets.xcassets";
INFOPLIST_FILE = Mastodon/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
- MARKETING_VERSION = 1.4.7;
+ MARKETING_VERSION = 1.4.10;
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -4000,7 +4074,7 @@
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
INTENTS_CODEGEN_LANGUAGE = Swift;
- IPHONEOS_DEPLOYMENT_TARGET = 14.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 15.0;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
@@ -4027,7 +4101,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
- MARKETING_VERSION = 1.4.7;
+ MARKETING_VERSION = 1.4.10;
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -4282,7 +4356,7 @@
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
INTENTS_CODEGEN_LANGUAGE = Swift;
- IPHONEOS_DEPLOYMENT_TARGET = 14.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 15.0;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SDKROOT = iphoneos;
@@ -4310,7 +4384,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
- MARKETING_VERSION = 1.4.7;
+ MARKETING_VERSION = 1.4.10;
PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -4555,21 +4629,21 @@
/* End XCConfigurationList section */
/* Begin XCSwiftPackageProductDependency section */
- DB22C92128E700A10082A9E9 /* MastodonSDK */ = {
+ 357FEEAE29523D470021C9DC /* MastodonSDKDynamic */ = {
isa = XCSwiftPackageProductDependency;
- productName = MastodonSDK;
+ productName = MastodonSDKDynamic;
};
- DB22C92328E700A80082A9E9 /* MastodonSDK */ = {
+ 357FEEB129523D510021C9DC /* MastodonSDKDynamic */ = {
isa = XCSwiftPackageProductDependency;
- productName = MastodonSDK;
+ productName = MastodonSDKDynamic;
};
- DB22C92528E700AF0082A9E9 /* MastodonSDK */ = {
+ 357FEEB529523D5C0021C9DC /* MastodonSDKDynamic */ = {
isa = XCSwiftPackageProductDependency;
- productName = MastodonSDK;
+ productName = MastodonSDKDynamic;
};
- DB22C92728E700B70082A9E9 /* MastodonSDK */ = {
+ 357FEEB929523D660021C9DC /* MastodonSDKDynamic */ = {
isa = XCSwiftPackageProductDependency;
- productName = MastodonSDK;
+ productName = MastodonSDKDynamic;
};
/* End XCSwiftPackageProductDependency section */
};
diff --git a/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist b/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist
index 78a3a9e70..b2b5b312a 100644
--- a/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist
+++ b/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist
@@ -117,12 +117,12 @@
NotificationService.xcscheme_^#shared#^_
orderHint
- 16
+ 17
ShareActionExtension.xcscheme_^#shared#^_
orderHint
- 17
+ 16
SuppressBuildableAutocreation
diff --git a/Mastodon/Activity/SafariActivity.swift b/Mastodon/Activity/SafariActivity.swift
index 62a193eaf..e20a9f815 100644
--- a/Mastodon/Activity/SafariActivity.swift
+++ b/Mastodon/Activity/SafariActivity.swift
@@ -58,7 +58,7 @@ final class SafariActivity: UIActivity {
}
Task {
- await sceneCoordinator?.present(scene: .safari(url: url as URL), from: nil, transition: .safariPresent(animated: true, completion: nil))
+ _ = await sceneCoordinator?.present(scene: .safari(url: url as URL), from: nil, transition: .safariPresent(animated: true, completion: nil))
activityDidFinish(true)
}
}
diff --git a/Mastodon/Coordinator/SceneCoordinator.swift b/Mastodon/Coordinator/SceneCoordinator.swift
index 8a0825969..3dc73c020 100644
--- a/Mastodon/Coordinator/SceneCoordinator.swift
+++ b/Mastodon/Coordinator/SceneCoordinator.swift
@@ -71,8 +71,8 @@ final public class SceneCoordinator {
self.setup()
try await Task.sleep(nanoseconds: .second * 1)
- // redirect to notification tab
- self.switchToTabBar(tab: .notification)
+ // redirect to notifications tab
+ self.switchToTabBar(tab: .notifications)
// Delay in next run loop
DispatchQueue.main.async { [weak self] in
@@ -149,6 +149,7 @@ extension SceneCoordinator {
case mastodonConfirmEmail(viewModel: MastodonConfirmEmailViewModel)
case mastodonResendEmail(viewModel: MastodonResendEmailViewModel)
case mastodonWebView(viewModel: WebViewModel)
+ case mastodonLogin
// search
case searchDetail(viewModel: SearchDetailViewModel)
@@ -172,6 +173,7 @@ extension SceneCoordinator {
case rebloggedBy(viewModel: UserListViewModel)
case favoritedBy(viewModel: UserListViewModel)
case bookmark(viewModel: BookmarkViewModel)
+ case followedTags(viewModel: FollowedTagsViewModel)
// setting
case settings(viewModel: SettingsViewModel)
@@ -199,6 +201,7 @@ extension SceneCoordinator {
case .welcome,
.mastodonPickServer,
.mastodonRegister,
+ .mastodonLogin,
.mastodonServerRules,
.mastodonConfirmEmail,
.mastodonResendEmail:
@@ -339,7 +342,7 @@ extension SceneCoordinator {
case .custom(let transitioningDelegate):
viewController.modalPresentationStyle = .custom
viewController.transitioningDelegate = transitioningDelegate
- // viewController.modalPresentationCapturesStatusBarAppearance = true
+ viewController.modalPresentationCapturesStatusBarAppearance = true
(splitViewController ?? presentingViewController)?.present(viewController, animated: true, completion: nil)
case .customPush(let animated):
@@ -403,6 +406,13 @@ private extension SceneCoordinator {
let _viewController = MastodonConfirmEmailViewController()
_viewController.viewModel = viewModel
viewController = _viewController
+ case .mastodonLogin:
+ let loginViewController = MastodonLoginViewController(appContext: appContext,
+ authenticationViewModel: AuthenticationViewModel(context: appContext, coordinator: self, isAuthenticationExist: false),
+ sceneCoordinator: self)
+ loginViewController.delegate = self
+
+ viewController = loginViewController
case .mastodonResendEmail(let viewModel):
let _viewController = MastodonResendEmailViewController()
_viewController.viewModel = viewModel
@@ -439,6 +449,10 @@ private extension SceneCoordinator {
let _viewController = BookmarkViewController()
_viewController.viewModel = viewModel
viewController = _viewController
+ case .followedTags(let viewModel):
+ let _viewController = FollowedTagsViewController()
+ _viewController.viewModel = viewModel
+ viewController = _viewController
case .favorite(let viewModel):
let _viewController = FavoriteViewController()
_viewController.viewModel = viewModel
@@ -529,5 +543,16 @@ private extension SceneCoordinator {
needs?.context = appContext
needs?.coordinator = self
}
-
+}
+
+//MARK: - MastodonLoginViewControllerDelegate
+
+extension SceneCoordinator: MastodonLoginViewControllerDelegate {
+ func backButtonPressed(_ viewController: MastodonLoginViewController) {
+ viewController.navigationController?.popViewController(animated: true)
+ }
+
+ func nextButtonPressed(_ viewController: MastodonLoginViewController) {
+ viewController.login()
+ }
}
diff --git a/Mastodon/Diffable/Notification/NotificationSection.swift b/Mastodon/Diffable/Notification/NotificationSection.swift
index 387affbc7..0271aac20 100644
--- a/Mastodon/Diffable/Notification/NotificationSection.swift
+++ b/Mastodon/Diffable/Notification/NotificationSection.swift
@@ -76,6 +76,7 @@ extension NotificationSection {
viewModel: NotificationTableViewCell.ViewModel,
configuration: Configuration
) {
+ cell.notificationView.viewModel.context = context
cell.notificationView.viewModel.authContext = configuration.authContext
StatusSection.setupStatusPollDataSource(
diff --git a/Mastodon/Diffable/Onboarding/CategoryPickerSection.swift b/Mastodon/Diffable/Onboarding/CategoryPickerSection.swift
index b53b378d6..191d4d166 100644
--- a/Mastodon/Diffable/Onboarding/CategoryPickerSection.swift
+++ b/Mastodon/Diffable/Onboarding/CategoryPickerSection.swift
@@ -21,7 +21,6 @@ extension CategoryPickerSection {
UICollectionViewDiffableDataSource(collectionView: collectionView) { [weak dependency] collectionView, indexPath, item -> UICollectionViewCell? in
guard let _ = dependency else { return nil }
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: String(describing: PickServerCategoryCollectionViewCell.self), for: indexPath) as! PickServerCategoryCollectionViewCell
- cell.categoryView.emojiLabel.text = item.emoji
cell.categoryView.titleLabel.text = item.title
cell.observe(\.isSelected, options: [.initial, .new]) { cell, _ in
cell.categoryView.highlightedIndicatorView.alpha = cell.isSelected ? 1 : 0
diff --git a/Mastodon/Diffable/Onboarding/PickServerSection.swift b/Mastodon/Diffable/Onboarding/PickServerSection.swift
index 01a31f6f6..1af5b23c6 100644
--- a/Mastodon/Diffable/Onboarding/PickServerSection.swift
+++ b/Mastodon/Diffable/Onboarding/PickServerSection.swift
@@ -18,16 +18,14 @@ enum PickServerSection: Equatable, Hashable {
extension PickServerSection {
static func tableViewDiffableDataSource(
for tableView: UITableView,
- dependency: NeedsDependency,
- pickServerCellDelegate: PickServerCellDelegate
+ dependency: NeedsDependency
) -> UITableViewDiffableDataSource {
tableView.register(OnboardingHeadlineTableViewCell.self, forCellReuseIdentifier: String(describing: OnboardingHeadlineTableViewCell.self))
tableView.register(PickServerCell.self, forCellReuseIdentifier: String(describing: PickServerCell.self))
tableView.register(PickServerLoaderTableViewCell.self, forCellReuseIdentifier: String(describing: PickServerLoaderTableViewCell.self))
return UITableViewDiffableDataSource(tableView: tableView) { [
- weak dependency,
- weak pickServerCellDelegate
+ weak dependency
] tableView, indexPath, item -> UITableViewCell? in
guard let _ = dependency else { return nil }
switch item {
@@ -37,7 +35,6 @@ extension PickServerSection {
case .server(let server, let attribute):
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: PickServerCell.self), for: indexPath) as! PickServerCell
PickServerSection.configure(cell: cell, server: server, attribute: attribute)
- cell.delegate = pickServerCellDelegate
return cell
case .loader(let attribute):
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: PickServerLoaderTableViewCell.self), for: indexPath) as! PickServerLoaderTableViewCell
@@ -80,7 +77,7 @@ extension PickServerSection {
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineHeightMultiple = 1.12
let valueAttributedString = NSAttributedString(
- string: parseUsersCount(server.totalUsers),
+ string: server.totalUsers.asAbbreviatedCountString(),
attributes: [
.paragraphStyle: paragraphStyle
]
@@ -128,17 +125,6 @@ extension PickServerSection {
}
.store(in: &cell.disposeBag)
}
-
- private static func parseUsersCount(_ usersCount: Int) -> String {
- switch usersCount {
- case 0..<1000:
- return "\(usersCount)"
- default:
- let usersCountInThousand = Float(usersCount) / 1000.0
- return String(format: "%.1fK", usersCountInThousand)
- }
- }
-
}
extension PickServerSection {
diff --git a/Mastodon/Diffable/Profile/ProfileFieldItem.swift b/Mastodon/Diffable/Profile/ProfileFieldItem.swift
index e33a2f883..37d08bc52 100644
--- a/Mastodon/Diffable/Profile/ProfileFieldItem.swift
+++ b/Mastodon/Diffable/Profile/ProfileFieldItem.swift
@@ -11,10 +11,10 @@ import MastodonSDK
import MastodonMeta
enum ProfileFieldItem: Hashable {
+ case createdAt(date: Date)
case field(field: FieldValue)
case editField(field: FieldValue)
case addEntry
- case noResult
}
extension ProfileFieldItem {
diff --git a/Mastodon/Diffable/Profile/ProfileFieldSection.swift b/Mastodon/Diffable/Profile/ProfileFieldSection.swift
index 6e57e6af9..10946915d 100644
--- a/Mastodon/Diffable/Profile/ProfileFieldSection.swift
+++ b/Mastodon/Diffable/Profile/ProfileFieldSection.swift
@@ -33,44 +33,60 @@ extension ProfileFieldSection {
collectionView.register(ProfileFieldCollectionViewHeaderFooterView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, withReuseIdentifier: ProfileFieldCollectionViewHeaderFooterView.footerReuseIdentifer)
let fieldCellRegistration = UICollectionView.CellRegistration { cell, indexPath, item in
- guard case let .field(field) = item else { return }
+ let key, value: String
+ let emojiMeta: MastodonContent.Emojis
+ let verified: Bool
+
+ switch item {
+ case .field(field: let field):
+ key = field.name.value
+ value = field.value.value
+ emojiMeta = field.emojiMeta
+ verified = field.verifiedAt.value != nil
+ case .createdAt(date: let date):
+ key = L10n.Scene.Profile.Fields.joined
+ let formatter = DateFormatter()
+ formatter.dateStyle = .medium
+ formatter.timeStyle = .none
+ value = formatter.string(from: date)
+ emojiMeta = [:]
+ verified = false
+ default: return
+ }
// set key
+ let keyColor = verified ? Asset.Scene.Profile.About.bioAboutFieldVerifiedText.color : Asset.Colors.Label.secondary.color
do {
- let mastodonContent = MastodonContent(content: field.name.value, emojis: field.emojiMeta)
+ let mastodonContent = MastodonContent(content: key, emojis: emojiMeta)
let metaContent = try MastodonMetaContent.convert(document: mastodonContent)
+ cell.keyMetaLabel.textAttributes[.foregroundColor] = keyColor
cell.keyMetaLabel.configure(content: metaContent)
} catch {
- let content = PlaintextMetaContent(string: field.name.value)
+ let content = PlaintextMetaContent(string: key)
+// cell.keyMetaLabel.textAttributes[.foregroundColor] = keyColor
cell.keyMetaLabel.configure(content: content)
}
// set value
+ let linkColor = verified ? Asset.Scene.Profile.About.bioAboutFieldVerifiedText.color : Asset.Colors.brand.color
do {
- let mastodonContent = MastodonContent(content: field.value.value, emojis: field.emojiMeta)
+ let mastodonContent = MastodonContent(content: value, emojis: emojiMeta)
let metaContent = try MastodonMetaContent.convert(document: mastodonContent)
- cell.valueMetaLabel.linkAttributes[.foregroundColor] = Asset.Colors.brand.color
- if field.verifiedAt.value != nil {
- cell.valueMetaLabel.linkAttributes[.foregroundColor] = Asset.Scene.Profile.About.bioAboutFieldVerifiedLink.color
- }
+ cell.valueMetaLabel.linkAttributes[.foregroundColor] = linkColor
cell.valueMetaLabel.configure(content: metaContent)
} catch {
- let content = PlaintextMetaContent(string: field.value.value)
+ let content = PlaintextMetaContent(string: value)
+ cell.valueMetaLabel.linkAttributes[.foregroundColor] = linkColor
cell.valueMetaLabel.configure(content: content)
}
// set background
var backgroundConfiguration = UIBackgroundConfiguration.listPlainCell()
- backgroundConfiguration.backgroundColor = UIColor.secondarySystemBackground
- if (field.verifiedAt.value != nil) {
- backgroundConfiguration.backgroundColor = Asset.Scene.Profile.About.bioAboutFieldVerifiedBackground.color
- }
+ backgroundConfiguration.backgroundColor = verified ? Asset.Scene.Profile.About.bioAboutFieldVerifiedBackground.color : UIColor.secondarySystemBackground
cell.backgroundConfiguration = backgroundConfiguration
// set checkmark and edit menu label
- cell.checkmark.isHidden = true
- cell.checkmarkPopoverString = nil
- if let verifiedAt = field.verifiedAt.value {
+ if case .field(let field) = item, let verifiedAt = field.verifiedAt.value {
cell.checkmark.isHidden = false
let formatter = DateFormatter()
formatter.dateStyle = .medium
@@ -78,6 +94,9 @@ extension ProfileFieldSection {
let dateString = formatter.string(from: verifiedAt)
cell.checkmark.accessibilityLabel = L10n.Scene.Profile.Fields.Verified.long(dateString)
cell.checkmarkPopoverString = L10n.Scene.Profile.Fields.Verified.short(dateString)
+ } else {
+ cell.checkmark.isHidden = true
+ cell.checkmarkPopoverString = nil
}
cell.delegate = configuration.profileFieldCollectionViewCellDelegate
@@ -128,26 +147,10 @@ extension ProfileFieldSection {
}
cell.backgroundConfiguration = backgroundConfiguration
}
-
- let noResultCellRegistration = UICollectionView.CellRegistration { cell, indexPath, item in
- guard case .noResult = item else { return }
-
- var contentConfiguration = cell.defaultContentConfiguration()
- contentConfiguration.text = L10n.Scene.Search.Searching.EmptyState.noResults // FIXME:
- contentConfiguration.textProperties.alignment = .center
- cell.contentConfiguration = contentConfiguration
-
-
- var backgroundConfiguration = UIBackgroundConfiguration.listPlainCell()
- backgroundConfiguration.backgroundColorTransformer = .init { _ in
- return .secondarySystemBackground
- }
- cell.backgroundConfiguration = backgroundConfiguration
- }
-
+
let dataSource = UICollectionViewDiffableDataSource(collectionView: collectionView) { collectionView, indexPath, item in
switch item {
- case .field:
+ case .field, .createdAt:
return collectionView.dequeueConfiguredReusableCell(
using: fieldCellRegistration,
for: indexPath,
@@ -165,12 +168,6 @@ extension ProfileFieldSection {
for: indexPath,
item: item
)
- case .noResult:
- return collectionView.dequeueConfiguredReusableCell(
- using: noResultCellRegistration,
- for: indexPath,
- item: item
- )
}
}
diff --git a/Mastodon/Diffable/Report/ReportSection.swift b/Mastodon/Diffable/Report/ReportSection.swift
index ba3c5525a..4c8fd4345 100644
--- a/Mastodon/Diffable/Report/ReportSection.swift
+++ b/Mastodon/Diffable/Report/ReportSection.swift
@@ -107,6 +107,7 @@ extension ReportSection {
statusView: cell.statusView
)
+ cell.statusView.viewModel.context = context
cell.statusView.viewModel.authContext = configuration.authContext
cell.configure(
diff --git a/Mastodon/Diffable/Search/SearchResultSection.swift b/Mastodon/Diffable/Search/SearchResultSection.swift
index 8a5d7e75f..e4dcad891 100644
--- a/Mastodon/Diffable/Search/SearchResultSection.swift
+++ b/Mastodon/Diffable/Search/SearchResultSection.swift
@@ -104,6 +104,7 @@ extension SearchResultSection {
statusView: cell.statusView
)
+ cell.statusView.viewModel.context = context
cell.statusView.viewModel.authContext = configuration.authContext
cell.configure(
diff --git a/Mastodon/Diffable/Status/StatusSection.swift b/Mastodon/Diffable/Status/StatusSection.swift
index 38b8e641f..ac02273e9 100644
--- a/Mastodon/Diffable/Status/StatusSection.swift
+++ b/Mastodon/Diffable/Status/StatusSection.swift
@@ -27,6 +27,7 @@ extension StatusSection {
static let logger = Logger(subsystem: "StatusSection", category: "logic")
struct Configuration {
+ let context: AppContext
let authContext: AuthContext
weak var statusTableViewCellDelegate: StatusTableViewCellDelegate?
weak var timelineMiddleLoaderTableViewCellDelegate: TimelineMiddleLoaderTableViewCellDelegate?
@@ -227,11 +228,7 @@ extension StatusSection {
}
var _snapshot = NSDiffableDataSourceSnapshot()
_snapshot.appendSections([.main])
- if #available(iOS 15.0, *) {
- statusView.pollTableViewDiffableDataSource?.applySnapshotUsingReloadData(_snapshot)
- } else {
- statusView.pollTableViewDiffableDataSource?.apply(_snapshot, animatingDifferences: false)
- }
+ statusView.pollTableViewDiffableDataSource?.applySnapshotUsingReloadData(_snapshot)
}
}
@@ -250,6 +247,7 @@ extension StatusSection {
statusView: cell.statusView
)
+ cell.statusView.viewModel.context = configuration.context
cell.statusView.viewModel.authContext = configuration.authContext
cell.configure(
@@ -277,6 +275,7 @@ extension StatusSection {
statusView: cell.statusView
)
+ cell.statusView.viewModel.context = configuration.context
cell.statusView.viewModel.authContext = configuration.authContext
cell.configure(
diff --git a/Mastodon/Extension/ActiveLabel.swift b/Mastodon/Extension/ActiveLabel.swift
deleted file mode 100644
index 4b23d4eb3..000000000
--- a/Mastodon/Extension/ActiveLabel.swift
+++ /dev/null
@@ -1,66 +0,0 @@
-//extension ActiveEntity {
-//
-// var accessibilityLabelDescription: String {
-// switch self.type {
-// case .email: return L10n.Common.Controls.Status.Tag.email
-// case .hashtag: return L10n.Common.Controls.Status.Tag.hashtag
-// case .mention: return L10n.Common.Controls.Status.Tag.mention
-// case .url: return L10n.Common.Controls.Status.Tag.url
-// case .emoji: return L10n.Common.Controls.Status.Tag.emoji
-// }
-// }
-//
-// var accessibilityValueDescription: String {
-// switch self.type {
-// case .email(let text, _): return text
-// case .hashtag(let text, _): return text
-// case .mention(let text, _): return text
-// case .url(_, let trimmed, _, _): return trimmed
-// case .emoji(let text, _, _): return text
-// }
-// }
-//
-// func accessibilityElement(in accessibilityContainer: Any) -> ActiveLabelAccessibilityElement? {
-// if case .emoji = self.type {
-// return nil
-// }
-//
-// let element = ActiveLabelAccessibilityElement(accessibilityContainer: accessibilityContainer)
-// element.accessibilityTraits = .button
-// element.accessibilityLabel = accessibilityLabelDescription
-// element.accessibilityValue = accessibilityValueDescription
-// return element
-// }
-//}
-
-//final class ActiveLabelAccessibilityElement: UIAccessibilityElement {
-// var index: Int!
-//}
-//
-// MARK: - UIAccessibilityContainer
-//extension ActiveLabel {
-//
-// func createAccessibilityElements() -> [UIAccessibilityElement] {
-// var elements: [UIAccessibilityElement] = []
-//
-// let element = ActiveLabelAccessibilityElement(accessibilityContainer: self)
-// element.accessibilityTraits = .staticText
-// element.accessibilityLabel = accessibilityLabel
-// element.accessibilityFrame = superview!.convert(frame, to: nil)
-// element.accessibilityLanguage = accessibilityLanguage
-// elements.append(element)
-//
-// for entity in activeEntities {
-// guard let element = entity.accessibilityElement(in: self) else { continue }
-// var glyphRange = NSRange()
-// layoutManager.characterRange(forGlyphRange: entity.range, actualGlyphRange: &glyphRange)
-// let rect = layoutManager.boundingRect(forGlyphRange: glyphRange, in: textContainer)
-// element.accessibilityFrame = self.convert(rect, to: nil)
-// element.accessibilityContainer = self
-// elements.append(element)
-// }
-//
-// return elements
-// }
-//
-//}
diff --git a/Mastodon/Extension/AppContext+NextAccount.swift b/Mastodon/Extension/AppContext+NextAccount.swift
new file mode 100644
index 000000000..a8eae1e13
--- /dev/null
+++ b/Mastodon/Extension/AppContext+NextAccount.swift
@@ -0,0 +1,47 @@
+//
+// AppContext+NextAccount.swift
+// Mastodon
+//
+// Created by Marcus Kida on 17.11.22.
+//
+
+import CoreData
+import CoreDataStack
+import MastodonCore
+import MastodonSDK
+
+extension AppContext {
+ func nextAccount(in authContext: AuthContext) -> MastodonAuthentication? {
+ let request = MastodonAuthentication.sortedFetchRequest
+ guard
+ let accounts = try? managedObjectContext.fetch(request),
+ accounts.count > 1
+ else { return nil }
+
+ let nextSelectedAccountIndex: Int? = {
+ for (index, account) in accounts.enumerated() {
+ guard account == authContext.mastodonAuthenticationBox
+ .authenticationRecord
+ .object(in: managedObjectContext)
+ else { continue }
+
+ let nextAccountIndex = index + 1
+
+ if accounts.count > nextAccountIndex {
+ return nextAccountIndex
+ } else {
+ return 0
+ }
+ }
+
+ return nil
+ }()
+
+ guard
+ let nextSelectedAccountIndex = nextSelectedAccountIndex,
+ accounts.count > nextSelectedAccountIndex
+ else { return nil }
+
+ return accounts[nextSelectedAccountIndex]
+ }
+}
diff --git a/Mastodon/Extension/Collection+IsNotEmpty.swift b/Mastodon/Extension/Collection+IsNotEmpty.swift
new file mode 100644
index 000000000..d59b8e2fe
--- /dev/null
+++ b/Mastodon/Extension/Collection+IsNotEmpty.swift
@@ -0,0 +1,14 @@
+//
+// Array+IsNotEmpty.swift
+// Mastodon
+//
+// Created by Marcus Kida on 01.12.22.
+//
+
+import Foundation
+
+extension Collection {
+ var isNotEmpty: Bool {
+ !isEmpty
+ }
+}
diff --git a/Mastodon/Extension/String.swift b/Mastodon/Extension/String.swift
index bf70c8937..0aa8acb3a 100644
--- a/Mastodon/Extension/String.swift
+++ b/Mastodon/Extension/String.swift
@@ -15,6 +15,8 @@ extension String {
mutating func capitalizeFirstLetter() {
self = self.capitalizingFirstLetter()
}
+
+ static let empty = ""
}
extension String {
diff --git a/Mastodon/Extension/UIApplication.swift b/Mastodon/Extension/UIApplication.swift
index 38080fdab..74019b0ae 100644
--- a/Mastodon/Extension/UIApplication.swift
+++ b/Mastodon/Extension/UIApplication.swift
@@ -22,5 +22,13 @@ extension UIApplication {
return version == build ? "v\(version)" : "v\(version) (\(build))"
}
+
+ func getKeyWindow() -> UIWindow? {
+ return UIApplication
+ .shared
+ .connectedScenes
+ .flatMap { ($0 as? UIWindowScene)?.windows ?? [] }
+ .first { $0.isKeyWindow }
+ }
}
diff --git a/Mastodon/Extension/UICollectionViewDiffableDataSource.swift b/Mastodon/Extension/UICollectionViewDiffableDataSource.swift
deleted file mode 100644
index 07d2fbd12..000000000
--- a/Mastodon/Extension/UICollectionViewDiffableDataSource.swift
+++ /dev/null
@@ -1,40 +0,0 @@
-//
-// UICollectionViewDiffableDataSource.swift
-// Mastodon
-//
-// Created by Cirno MainasuK on 2021-10-11.
-//
-
-import UIKit
-
-// ref: https://www.jessesquires.com/blog/2021/07/08/diffable-data-source-behavior-changes-and-reconfiguring-cells-in-ios-15/
-extension UICollectionViewDiffableDataSource {
- func reloadData(
- snapshot: NSDiffableDataSourceSnapshot,
- completion: (() -> Void)? = nil
- ) {
- if #available(iOS 15.0, *) {
- self.applySnapshotUsingReloadData(snapshot, completion: completion)
- } else {
- self.apply(snapshot, animatingDifferences: false, completion: completion)
- }
- }
-
- func applySnapshot(
- _ snapshot: NSDiffableDataSourceSnapshot,
- animated: Bool,
- completion: (() -> Void)? = nil) {
-
- if #available(iOS 15.0, *) {
- self.apply(snapshot, animatingDifferences: animated, completion: completion)
- } else {
- if animated {
- self.apply(snapshot, animatingDifferences: true, completion: completion)
- } else {
- UIView.performWithoutAnimation {
- self.apply(snapshot, animatingDifferences: true, completion: completion)
- }
- }
- }
- }
-}
diff --git a/Mastodon/Extension/UIImage+SFSymbols.swift b/Mastodon/Extension/UIImage+SFSymbols.swift
new file mode 100644
index 000000000..cf20055ea
--- /dev/null
+++ b/Mastodon/Extension/UIImage+SFSymbols.swift
@@ -0,0 +1,12 @@
+//
+// UIImage+SFSymbols.swift
+// Mastodon
+//
+// Created by Marcus Kida on 18.11.22.
+//
+
+import UIKit
+
+extension UIImage {
+ static let chevronUpChevronDown = UIImage(systemName: "chevron.up.chevron.down")
+}
diff --git a/Mastodon/Extension/UITableViewDiffableDataSource.swift b/Mastodon/Extension/UITableViewDiffableDataSource.swift
deleted file mode 100644
index 5006417a4..000000000
--- a/Mastodon/Extension/UITableViewDiffableDataSource.swift
+++ /dev/null
@@ -1,40 +0,0 @@
-//
-// UITableViewDiffableDataSource.swift
-// Mastodon
-//
-// Created by Cirno MainasuK on 2021-10-11.
-//
-
-import UIKit
-
-// ref: https://www.jessesquires.com/blog/2021/07/08/diffable-data-source-behavior-changes-and-reconfiguring-cells-in-ios-15/
-extension UITableViewDiffableDataSource {
- func reloadData(
- snapshot: NSDiffableDataSourceSnapshot,
- completion: (() -> Void)? = nil
- ) {
- if #available(iOS 15.0, *) {
- self.applySnapshotUsingReloadData(snapshot, completion: completion)
- } else {
- self.apply(snapshot, animatingDifferences: false, completion: completion)
- }
- }
-
- func applySnapshot(
- _ snapshot: NSDiffableDataSourceSnapshot,
- animated: Bool,
- completion: (() -> Void)? = nil) {
-
- if #available(iOS 15.0, *) {
- self.apply(snapshot, animatingDifferences: animated, completion: completion)
- } else {
- if animated {
- self.apply(snapshot, animatingDifferences: true, completion: completion)
- } else {
- UIView.performWithoutAnimation {
- self.apply(snapshot, animatingDifferences: true, completion: completion)
- }
- }
- }
- }
-}
diff --git a/Mastodon/Helper/ImageProvider.swift b/Mastodon/Helper/ImageProvider.swift
new file mode 100644
index 000000000..11ea6d46e
--- /dev/null
+++ b/Mastodon/Helper/ImageProvider.swift
@@ -0,0 +1,39 @@
+//
+// ImageProvider.swift
+// Mastodon
+//
+// Created by Jed Fox on 2022-12-03.
+//
+
+import Foundation
+import AlamofireImage
+import UniformTypeIdentifiers
+import UIKit
+
+class ImageProvider: NSObject, NSItemProviderWriting {
+ let url: URL
+ let filter: ImageFilter?
+
+ init(url: URL, filter: ImageFilter? = nil) {
+ self.url = url
+ self.filter = filter
+ }
+
+ var itemProvider: NSItemProvider {
+ NSItemProvider(object: self)
+ }
+
+ static var writableTypeIdentifiersForItemProvider: [String] {
+ [UTType.png.identifier]
+ }
+
+ func loadData(withTypeIdentifier typeIdentifier: String, forItemProviderCompletionHandler completionHandler: @escaping @Sendable (Data?, Error?) -> Void) -> Progress? {
+ let receipt = UIImageView.af.sharedImageDownloader.download(URLRequest(url: url), filter: filter, completion: { response in
+ switch response.result {
+ case .failure(let error): completionHandler(nil, error)
+ case .success(let image): completionHandler(image.pngData(), nil)
+ }
+ })
+ return receipt?.request.downloadProgress
+ }
+}
diff --git a/Mastodon/Helper/URLActivityItemWithMetadata.swift b/Mastodon/Helper/URLActivityItemWithMetadata.swift
new file mode 100644
index 000000000..82d1747fe
--- /dev/null
+++ b/Mastodon/Helper/URLActivityItemWithMetadata.swift
@@ -0,0 +1,33 @@
+//
+// URLActivityItemWithMetadata.swift
+// Mastodon
+//
+// Created by Jed Fox on 2022-12-03.
+//
+
+import UIKit
+import LinkPresentation
+
+class URLActivityItemWithMetadata: NSObject, UIActivityItemSource {
+ init(url: URL, configureMetadata: (LPLinkMetadata) -> Void) {
+ self.url = url
+ self.metadata = LPLinkMetadata()
+ metadata.url = url
+ configureMetadata(metadata)
+ }
+
+ let url: URL
+ let metadata: LPLinkMetadata
+
+ func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
+ url
+ }
+
+ func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivity.ActivityType?) -> Any? {
+ url
+ }
+
+ func activityViewControllerLinkMetadata(_ activityViewController: UIActivityViewController) -> LPLinkMetadata? {
+ metadata
+ }
+}
diff --git a/Mastodon/Protocol/Provider/DataSourceFacade+Block.swift b/Mastodon/Protocol/Provider/DataSourceFacade+Block.swift
index 2747f4735..2cf6e3419 100644
--- a/Mastodon/Protocol/Provider/DataSourceFacade+Block.swift
+++ b/Mastodon/Protocol/Provider/DataSourceFacade+Block.swift
@@ -16,10 +16,17 @@ extension DataSourceFacade {
) async throws {
let selectionFeedbackGenerator = await UISelectionFeedbackGenerator()
await selectionFeedbackGenerator.selectionChanged()
-
- _ = try await dependency.context.apiService.toggleBlock(
+
+ let apiService = dependency.context.apiService
+ let authBox = dependency.authContext.mastodonAuthenticationBox
+
+ _ = try await apiService.toggleBlock(
user: user,
- authenticationBox: dependency.authContext.mastodonAuthenticationBox
+ authenticationBox: authBox
+ )
+
+ try await dependency.context.apiService.getBlocked(
+ authenticationBox: authBox
)
} // end func
}
diff --git a/Mastodon/Protocol/Provider/DataSourceFacade+Follow.swift b/Mastodon/Protocol/Provider/DataSourceFacade+Follow.swift
index b3812f198..a85de703a 100644
--- a/Mastodon/Protocol/Provider/DataSourceFacade+Follow.swift
+++ b/Mastodon/Protocol/Provider/DataSourceFacade+Follow.swift
@@ -94,7 +94,7 @@ extension DataSourceFacade {
let alertController = await UIAlertController(for: error, title: nil, preferredStyle: .alert)
let okAction = await UIAlertAction(title: L10n.Common.Controls.Actions.ok, style: .default)
await alertController.addAction(okAction)
- await dependency.coordinator.present(
+ _ = await dependency.coordinator.present(
scene: .alertController(alertController: alertController),
from: nil,
transition: .alertController(animated: true, completion: nil)
diff --git a/Mastodon/Protocol/Provider/DataSourceFacade+Hashtag.swift b/Mastodon/Protocol/Provider/DataSourceFacade+Hashtag.swift
index 43d6b954b..6135c904a 100644
--- a/Mastodon/Protocol/Provider/DataSourceFacade+Hashtag.swift
+++ b/Mastodon/Protocol/Provider/DataSourceFacade+Hashtag.swift
@@ -35,7 +35,7 @@ extension DataSourceFacade {
hashtag: tag.name
)
- provider.coordinator.present(
+ _ = provider.coordinator.present(
scene: .hashtagTimeline(viewModel: hashtagTimelineViewModel),
from: provider,
transition: .show
@@ -61,7 +61,7 @@ extension DataSourceFacade {
hashtag: name
)
- provider.coordinator.present(
+ _ = provider.coordinator.present(
scene: .hashtagTimeline(viewModel: hashtagTimelineViewModel),
from: provider,
transition: .show
diff --git a/Mastodon/Protocol/Provider/DataSourceFacade+Media.swift b/Mastodon/Protocol/Provider/DataSourceFacade+Media.swift
index 3e767f2d3..9dd97f38a 100644
--- a/Mastodon/Protocol/Provider/DataSourceFacade+Media.swift
+++ b/Mastodon/Protocol/Provider/DataSourceFacade+Media.swift
@@ -24,7 +24,7 @@ extension DataSourceFacade {
item: mediaPreviewItem,
transitionItem: mediaPreviewTransitionItem
)
- dependency.coordinator.present(
+ _ = dependency.coordinator.present(
scene: .mediaPreview(viewModel: mediaPreviewViewModel),
from: dependency,
transition: .custom(transitioningDelegate: dependency.mediaPreviewTransitionController)
diff --git a/Mastodon/Protocol/Provider/DataSourceFacade+Meta.swift b/Mastodon/Protocol/Provider/DataSourceFacade+Meta.swift
index 7e0ed37fc..ca3bbd474 100644
--- a/Mastodon/Protocol/Provider/DataSourceFacade+Meta.swift
+++ b/Mastodon/Protocol/Provider/DataSourceFacade+Meta.swift
@@ -48,21 +48,15 @@ extension DataSourceFacade {
assertionFailure()
return
}
- let domain = provider.authContext.mastodonAuthenticationBox.domain
- if url.host == domain,
- url.pathComponents.count >= 4,
- url.pathComponents[0] == "/",
- url.pathComponents[1] == "web",
- url.pathComponents[2] == "statuses" {
- let statusID = url.pathComponents[3]
- let threadViewModel = RemoteThreadViewModel(context: provider.context, authContext: provider.authContext, statusID: statusID)
- await provider.coordinator.present(scene: .thread(viewModel: threadViewModel), from: nil, transition: .show)
- } else {
- await provider.coordinator.present(scene: .safari(url: url), from: nil, transition: .safariPresent(animated: true, completion: nil))
- }
+
+ await responseToURLAction(
+ provider: provider,
+ status: status,
+ url: url
+ )
case .hashtag(_, let hashtag, _):
let hashtagTimelineViewModel = HashtagTimelineViewModel(context: provider.context, authContext: provider.authContext, hashtag: hashtag)
- await provider.coordinator.present(scene: .hashtagTimeline(viewModel: hashtagTimelineViewModel), from: provider, transition: .show)
+ _ = await provider.coordinator.present(scene: .hashtagTimeline(viewModel: hashtagTimelineViewModel), from: provider, transition: .show)
case .mention(_, let mention, let userInfo):
await coordinateToProfileScene(
provider: provider,
diff --git a/Mastodon/Protocol/Provider/DataSourceFacade+Profile.swift b/Mastodon/Protocol/Provider/DataSourceFacade+Profile.swift
index ef01b8394..3c0509d2c 100644
--- a/Mastodon/Protocol/Provider/DataSourceFacade+Profile.swift
+++ b/Mastodon/Protocol/Provider/DataSourceFacade+Profile.swift
@@ -47,7 +47,7 @@ extension DataSourceFacade {
mastodonUser: user
)
- provider.coordinator.present(
+ _ = provider.coordinator.present(
scene: .profile(viewModel: profileViewModel),
from: provider,
transition: .show
@@ -75,7 +75,7 @@ extension DataSourceFacade {
}
guard let mention = mentions?.first(where: { $0.username == mention }) else {
- await provider.coordinator.present(
+ _ = await provider.coordinator.present(
scene: .safari(url: url),
from: provider,
transition: .safariPresent(animated: true, completion: nil)
@@ -102,7 +102,7 @@ extension DataSourceFacade {
}
}()
- await provider.coordinator.present(
+ _ = await provider.coordinator.present(
scene: .profile(viewModel: profileViewModel),
from: provider,
transition: .show
diff --git a/Mastodon/Protocol/Provider/DataSourceFacade+Status.swift b/Mastodon/Protocol/Provider/DataSourceFacade+Status.swift
index 332ed75e4..90b5b184e 100644
--- a/Mastodon/Protocol/Provider/DataSourceFacade+Status.swift
+++ b/Mastodon/Protocol/Provider/DataSourceFacade+Status.swift
@@ -43,7 +43,7 @@ extension DataSourceFacade {
dependency: provider,
status: status
)
- provider.coordinator.present(
+ _ = provider.coordinator.present(
scene: .activityViewController(
activityViewController: activityViewController,
sourceView: button,
@@ -59,8 +59,18 @@ extension DataSourceFacade {
status: ManagedObjectRecord
) async throws -> UIActivityViewController {
var activityItems: [Any] = try await dependency.context.managedObjectContext.perform {
- guard let status = status.object(in: dependency.context.managedObjectContext) else { return [] }
- return [StatusActivityItem(status: status)].compactMap { $0 } as [Any]
+ guard let status = status.object(in: dependency.context.managedObjectContext),
+ let url = URL(string: status.url ?? status.uri)
+ else { return [] }
+ return [
+ URLActivityItemWithMetadata(url: url) { metadata in
+ metadata.title = "\(status.author.displayName) (@\(status.author.acctWithDomain))"
+ metadata.iconProvider = ImageProvider(
+ url: status.author.avatarImageURLWithFallback(domain: status.author.domain),
+ filter: ScaledToSizeFilter(size: CGSize.authorAvatarButtonSize)
+ ).itemProvider
+ }
+ ] as [Any]
}
var applicationActivities: [UIActivity] = [
SafariActivity(sceneCoordinator: dependency.coordinator), // open URL
@@ -77,54 +87,6 @@ extension DataSourceFacade {
)
return activityViewController
}
-
- private class StatusActivityItem: NSObject, UIActivityItemSource {
- init?(status: Status) {
- guard let url = URL(string: status.url ?? status.uri) else { return nil }
- self.url = url
- self.metadata = LPLinkMetadata()
- metadata.url = url
- metadata.title = "\(status.author.displayName) (@\(status.author.username)@\(status.author.domain))"
- metadata.iconProvider = NSItemProvider(object: IconProvider(url: status.author.avatarImageURLWithFallback(domain: status.author.domain)))
- }
-
- let url: URL
- let metadata: LPLinkMetadata
-
- func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
- url
- }
-
- func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivity.ActivityType?) -> Any? {
- url
- }
-
- func activityViewControllerLinkMetadata(_ activityViewController: UIActivityViewController) -> LPLinkMetadata? {
- metadata
- }
-
- private class IconProvider: NSObject, NSItemProviderWriting {
- let url: URL
- init(url: URL) {
- self.url = url
- }
-
- static var writableTypeIdentifiersForItemProvider: [String] {
- [UTType.png.identifier]
- }
-
- func loadData(withTypeIdentifier typeIdentifier: String, forItemProviderCompletionHandler completionHandler: @escaping @Sendable (Data?, Error?) -> Void) -> Progress? {
- let filter = ScaledToSizeFilter(size: CGSize.authorAvatarButtonSize)
- let receipt = UIImageView.af.sharedImageDownloader.download(URLRequest(url: url), filter: filter, completion: { response in
- switch response.result {
- case .failure(let error): completionHandler(nil, error)
- case .success(let image): completionHandler(image.pngData(), nil)
- }
- })
- return receipt?.request.downloadProgress
- }
- }
- }
}
// ActionToolBar
@@ -155,7 +117,7 @@ extension DataSourceFacade {
let composeViewModel = ComposeViewModel(
context: provider.context,
authContext: provider.authContext,
- kind: .reply(status: status)
+ destination: .reply(parent: status)
)
_ = provider.coordinator.present(
scene: .compose(viewModel: composeViewModel),
@@ -358,7 +320,8 @@ extension DataSourceFacade {
dependency: dependency,
status: status
)
- await dependency.coordinator.present(
+
+ _ = dependency.coordinator.present(
scene: .activityViewController(
activityViewController: activityViewController,
sourceView: menuContext.button,
@@ -392,6 +355,18 @@ extension DataSourceFacade {
alertController.addAction(cancelAction)
dependency.present(alertController, animated: true)
+ case .translateStatus:
+ guard let status = menuContext.status else { return }
+ do {
+ try await DataSourceFacade.translateStatus(
+ provider: dependency,
+ status: status
+ )
+ } catch TranslationFailure.emptyOrInvalidResponse {
+ let alertController = UIAlertController(title: L10n.Common.Alerts.TranslationFailed.title, message: L10n.Common.Alerts.TranslationFailed.message, preferredStyle: .alert)
+ alertController.addAction(UIAlertAction(title: L10n.Common.Alerts.TranslationFailed.button, style: .default))
+ dependency.present(alertController, animated: true)
+ }
}
} // end func
}
diff --git a/Mastodon/Protocol/Provider/DataSourceFacade+Translate.swift b/Mastodon/Protocol/Provider/DataSourceFacade+Translate.swift
new file mode 100644
index 000000000..8ce9c2447
--- /dev/null
+++ b/Mastodon/Protocol/Provider/DataSourceFacade+Translate.swift
@@ -0,0 +1,70 @@
+//
+// DataSourceFacade+Translate.swift
+// Mastodon
+//
+// Created by Marcus Kida on 29.11.22.
+//
+
+import UIKit
+import CoreData
+import CoreDataStack
+import MastodonCore
+
+typealias Provider = UIViewController & NeedsDependency & AuthContextProvider
+
+extension DataSourceFacade {
+ enum TranslationFailure: Error {
+ case emptyOrInvalidResponse
+ }
+
+ public static func translateStatus(
+ provider: Provider,
+ status: ManagedObjectRecord
+ ) async throws {
+ let selectionFeedbackGenerator = await UISelectionFeedbackGenerator()
+ await selectionFeedbackGenerator.selectionChanged()
+
+ guard
+ let status = status.object(in: provider.context.managedObjectContext)
+ else {
+ return
+ }
+
+ if let reblog = status.reblog {
+ try await translateAndApply(provider: provider, status: reblog)
+ } else {
+ try await translateAndApply(provider: provider, status: status)
+ }
+ }
+}
+
+private extension DataSourceFacade {
+ static func translateStatus(provider: Provider, status: Status) async throws -> Status.TranslatedContent? {
+ do {
+ let value = try await provider.context
+ .apiService
+ .translateStatus(
+ statusID: status.id,
+ authenticationBox: provider.authContext.mastodonAuthenticationBox
+ ).value
+
+ guard let content = value.content else {
+ throw TranslationFailure.emptyOrInvalidResponse
+ }
+
+ return Status.TranslatedContent(content: content, provider: value.provider)
+ } catch {
+ throw TranslationFailure.emptyOrInvalidResponse
+ }
+ }
+
+ static func translateAndApply(provider: Provider, status: Status) async throws {
+ do {
+ let translated = try await translateStatus(provider: provider, status: status)
+ status.update(translatedContent: translated)
+ } catch {
+ status.update(translatedContent: nil)
+ throw TranslationFailure.emptyOrInvalidResponse
+ }
+ }
+}
diff --git a/Mastodon/Protocol/Provider/DataSourceFacade+URL.swift b/Mastodon/Protocol/Provider/DataSourceFacade+URL.swift
new file mode 100644
index 000000000..a65de9537
--- /dev/null
+++ b/Mastodon/Protocol/Provider/DataSourceFacade+URL.swift
@@ -0,0 +1,32 @@
+//
+// DataSourceFacade+URL.swift
+// Mastodon
+//
+// Created by Kyle Bashour on 11/24/22.
+//
+
+import Foundation
+import CoreDataStack
+import MetaTextKit
+import MastodonCore
+
+extension DataSourceFacade {
+ static func responseToURLAction(
+ provider: DataSourceProvider & AuthContextProvider,
+ status: ManagedObjectRecord,
+ url: URL
+ ) async {
+ let domain = provider.authContext.mastodonAuthenticationBox.domain
+ if url.host == domain,
+ url.pathComponents.count >= 4,
+ url.pathComponents[0] == "/",
+ url.pathComponents[1] == "web",
+ url.pathComponents[2] == "statuses" {
+ let statusID = url.pathComponents[3]
+ let threadViewModel = RemoteThreadViewModel(context: provider.context, authContext: provider.authContext, statusID: statusID)
+ _ = await provider.coordinator.present(scene: .thread(viewModel: threadViewModel), from: nil, transition: .show)
+ } else {
+ _ = await provider.coordinator.present(scene: .safari(url: url), from: nil, transition: .safariPresent(animated: true, completion: nil))
+ }
+ }
+}
diff --git a/Mastodon/Protocol/Provider/DataSourceProvider+StatusTableViewCellDelegate.swift b/Mastodon/Protocol/Provider/DataSourceProvider+StatusTableViewCellDelegate.swift
index 82c25d040..0b4cc3e8f 100644
--- a/Mastodon/Protocol/Provider/DataSourceProvider+StatusTableViewCellDelegate.swift
+++ b/Mastodon/Protocol/Provider/DataSourceProvider+StatusTableViewCellDelegate.swift
@@ -10,6 +10,9 @@ import CoreDataStack
import MetaTextKit
import MastodonCore
import MastodonUI
+import MastodonLocalization
+import MastodonAsset
+import LinkPresentation
// MARK: - header
extension StatusTableViewCellDelegate where Self: DataSourceProvider & AuthContextProvider {
@@ -120,7 +123,127 @@ extension StatusTableViewCellDelegate where Self: DataSourceProvider & AuthConte
)
}
}
-
+
+}
+
+// MARK: - card
+extension StatusTableViewCellDelegate where Self: DataSourceProvider & AuthContextProvider {
+
+ func tableViewCell(
+ _ cell: UITableViewCell,
+ statusView: StatusView,
+ didTapCardWithURL url: URL
+ ) {
+ Task {
+ let source = DataSourceItem.Source(tableViewCell: cell, indexPath: nil)
+ guard let item = await item(from: source) else {
+ assertionFailure()
+ return
+ }
+ guard case let .status(status) = item else {
+ assertionFailure("only works for status data provider")
+ return
+ }
+
+ await DataSourceFacade.responseToURLAction(
+ provider: self,
+ status: status,
+ url: url
+ )
+ }
+ }
+
+ func tableViewCell(
+ _ cell: UITableViewCell,
+ statusView: StatusView,
+ cardControl: StatusCardControl,
+ didTapURL url: URL
+ ) {
+ Task {
+ let source = DataSourceItem.Source(tableViewCell: cell, indexPath: nil)
+ guard let item = await item(from: source) else {
+ assertionFailure()
+ return
+ }
+ guard case let .status(status) = item else {
+ assertionFailure("only works for status data provider")
+ return
+ }
+
+ await DataSourceFacade.responseToURLAction(
+ provider: self,
+ status: status,
+ url: url
+ )
+ }
+ }
+
+ func tableViewCell(
+ _ cell: UITableViewCell,
+ statusView: StatusView,
+ cardControlMenu statusCardControl: StatusCardControl
+ ) -> UIMenu? {
+ guard let card = statusView.viewModel.card,
+ let url = card.url else {
+ return nil
+ }
+
+ return UIMenu(children: [
+ UIAction(
+ title: L10n.Common.Controls.Actions.copy,
+ image: UIImage(systemName: "doc.on.doc")
+ ) { _ in
+ UIPasteboard.general.url = url
+ },
+
+ UIAction(
+ title: L10n.Common.Controls.Actions.share,
+ image: Asset.Arrow.squareAndArrowUp.image.withRenderingMode(.alwaysTemplate)
+ ) { _ in
+ DispatchQueue.main.async {
+ let activityViewController = UIActivityViewController(
+ activityItems: [
+ URLActivityItemWithMetadata(url: url) { metadata in
+ metadata.title = card.title
+
+ if let image = card.imageURL {
+ metadata.iconProvider = ImageProvider(url: image, filter: nil).itemProvider
+ }
+ }
+ ],
+ applicationActivities: []
+ )
+ self.coordinator.present(
+ scene: .activityViewController(
+ activityViewController: activityViewController,
+ sourceView: statusCardControl, barButtonItem: nil
+ ),
+ from: self,
+ transition: .activityViewControllerPresent(animated: true)
+ )
+ }
+ },
+
+ UIAction(
+ title: L10n.Common.Controls.Status.Actions.shareLinkInPost,
+ image: Asset.ObjectsAndTools.squareAndPencil.image.withRenderingMode(.alwaysTemplate)
+ ) { _ in
+ DispatchQueue.main.async {
+ self.coordinator.present(
+ scene: .compose(viewModel: ComposeViewModel(
+ context: self.context,
+ authContext: self.authContext,
+ destination: .topLevel,
+ initialContent: L10n.Common.Controls.Status.linkViaUser(url.absoluteString, "@" + (statusView.viewModel.authorUsername ?? ""))
+ )),
+ from: self,
+ transition: .modal(animated: true)
+ )
+ }
+ }
+ ])
+ }
+
}
// MARK: - media
@@ -360,6 +483,12 @@ extension StatusTableViewCellDelegate where Self: DataSourceProvider & AuthConte
return
}
+ if let cell = cell as? StatusTableViewCell {
+ DispatchQueue.main.async {
+ cell.statusView.viewModel.isCurrentlyTranslating = true
+ }
+ }
+
try await DataSourceFacade.responseToMenuAction(
dependency: self,
action: action,
@@ -487,7 +616,7 @@ extension StatusTableViewCellDelegate where Self: DataSourceProvider & AuthConte
authContext: authContext,
kind: .rebloggedBy(status: status)
)
- await coordinator.present(
+ _ = await coordinator.present(
scene: .rebloggedBy(viewModel: userListViewModel),
from: self,
transition: .show
@@ -511,7 +640,7 @@ extension StatusTableViewCellDelegate where Self: DataSourceProvider & AuthConte
authContext: authContext,
kind: .favoritedBy(status: status)
)
- await coordinator.present(
+ _ = await coordinator.present(
scene: .favoritedBy(viewModel: userListViewModel),
from: self,
transition: .show
diff --git a/Mastodon/Protocol/Provider/DataSourceProvider+StatusTableViewControllerNavigateable.swift b/Mastodon/Protocol/Provider/DataSourceProvider+StatusTableViewControllerNavigateable.swift
index e7b55f91c..0e0a24c9b 100644
--- a/Mastodon/Protocol/Provider/DataSourceProvider+StatusTableViewControllerNavigateable.swift
+++ b/Mastodon/Protocol/Provider/DataSourceProvider+StatusTableViewControllerNavigateable.swift
@@ -100,7 +100,7 @@ extension StatusTableViewControllerNavigateableCore where Self: DataSourceProvid
let composeViewModel = ComposeViewModel(
context: self.context,
authContext: authContext,
- kind: .reply(status: status)
+ destination: .reply(parent: status)
)
_ = self.coordinator.present(
scene: .compose(viewModel: composeViewModel),
diff --git a/Mastodon/Protocol/Provider/DataSourceProvider+UITableViewDelegate.swift b/Mastodon/Protocol/Provider/DataSourceProvider+UITableViewDelegate.swift
index 3a71e5346..6ced42601 100644
--- a/Mastodon/Protocol/Provider/DataSourceProvider+UITableViewDelegate.swift
+++ b/Mastodon/Protocol/Provider/DataSourceProvider+UITableViewDelegate.swift
@@ -143,7 +143,7 @@ extension UITableViewDelegate where Self: DataSourceProvider & MediaPreviewableV
title: L10n.Common.Alerts.SavePhotoFailure.title,
message: L10n.Common.Alerts.SavePhotoFailure.message
)
- self.coordinator.present(
+ _ = self.coordinator.present(
scene: .alertController(alertController: alertController),
from: self,
transition: .alertController(animated: true, completion: nil)
diff --git a/Mastodon/Resources/Base.lproj/infoPlist.strings b/Mastodon/Resources/Base.lproj/infoPlist.strings
new file mode 100644
index 000000000..710865573
--- /dev/null
+++ b/Mastodon/Resources/Base.lproj/infoPlist.strings
@@ -0,0 +1,4 @@
+"NSCameraUsageDescription" = "Used to take photo for post status";
+"NSPhotoLibraryAddUsageDescription" = "Used to save photo into the Photo Library";
+"NewPostShortcutItemTitle" = "New Post";
+"SearchShortcutItemTitle" = "Search";
\ No newline at end of file
diff --git a/Mastodon/Resources/ar.lproj/InfoPlist.strings b/Mastodon/Resources/ar.lproj/InfoPlist.strings
index c3b26f14a..ecb81ddd4 100644
--- a/Mastodon/Resources/ar.lproj/InfoPlist.strings
+++ b/Mastodon/Resources/ar.lproj/InfoPlist.strings
@@ -1,4 +1,4 @@
"NSCameraUsageDescription" = "يُستخدم لالتقاط الصورة عِندَ نشر الحالات";
"NSPhotoLibraryAddUsageDescription" = "يُستخدم لحِفظ الصورة في مكتبة الصور";
-"NewPostShortcutItemTitle" = "منشور جديد";
+"NewPostShortcutItemTitle" = "مَنشُورٌ جَديد";
"SearchShortcutItemTitle" = "البحث";
\ No newline at end of file
diff --git a/Mastodon/Resources/ckb.lproj/InfoPlist.strings b/Mastodon/Resources/ckb.lproj/InfoPlist.strings
index 710865573..8c4946d2d 100644
--- a/Mastodon/Resources/ckb.lproj/InfoPlist.strings
+++ b/Mastodon/Resources/ckb.lproj/InfoPlist.strings
@@ -1,4 +1,4 @@
-"NSCameraUsageDescription" = "Used to take photo for post status";
-"NSPhotoLibraryAddUsageDescription" = "Used to save photo into the Photo Library";
-"NewPostShortcutItemTitle" = "New Post";
-"SearchShortcutItemTitle" = "Search";
\ No newline at end of file
+"NSCameraUsageDescription" = "بەکار دێت بۆ گرتنی وێنەیەک بۆ پۆستەکە";
+"NSPhotoLibraryAddUsageDescription" = "بەکار دێت بۆ هەڵگرتنی وێنە";
+"NewPostShortcutItemTitle" = "پۆستی نوێ";
+"SearchShortcutItemTitle" = "بگەڕێ";
\ No newline at end of file
diff --git a/Mastodon/Resources/cs.lproj/InfoPlist.strings b/Mastodon/Resources/cs.lproj/InfoPlist.strings
new file mode 100644
index 000000000..6989cfcb1
--- /dev/null
+++ b/Mastodon/Resources/cs.lproj/InfoPlist.strings
@@ -0,0 +1,4 @@
+"NSCameraUsageDescription" = "Slouží k pořízení fotografie pro příspěvek";
+"NSPhotoLibraryAddUsageDescription" = "Slouží k uložení fotografie do knihovny fotografií";
+"NewPostShortcutItemTitle" = "Nový příspěvek";
+"SearchShortcutItemTitle" = "Hledat";
\ No newline at end of file
diff --git a/Mastodon/Resources/de.lproj/infoPlist.strings b/Mastodon/Resources/de.lproj/infoPlist.strings
index 9c5feae53..9c8438653 100644
--- a/Mastodon/Resources/de.lproj/infoPlist.strings
+++ b/Mastodon/Resources/de.lproj/infoPlist.strings
@@ -1,4 +1,4 @@
-"NSCameraUsageDescription" = "Verwendet um Fotos für neue Beiträge aufzunehmen";
-"NSPhotoLibraryAddUsageDescription" = "Verwendet um Fotos zu speichern";
+"NSCameraUsageDescription" = "Wird verwendet, um Fotos für neue Beiträge aufzunehmen";
+"NSPhotoLibraryAddUsageDescription" = "Wird verwendet, um Foto in der Foto-Mediathek zu speichern";
"NewPostShortcutItemTitle" = "Neuer Beitrag";
-"SearchShortcutItemTitle" = "Suche";
\ No newline at end of file
+"SearchShortcutItemTitle" = "Suchen";
\ No newline at end of file
diff --git a/Mastodon/Resources/eu.lproj/InfoPlist.strings b/Mastodon/Resources/eu.lproj/InfoPlist.strings
index 710865573..e9d36a901 100644
--- a/Mastodon/Resources/eu.lproj/InfoPlist.strings
+++ b/Mastodon/Resources/eu.lproj/InfoPlist.strings
@@ -1,4 +1,4 @@
-"NSCameraUsageDescription" = "Used to take photo for post status";
-"NSPhotoLibraryAddUsageDescription" = "Used to save photo into the Photo Library";
-"NewPostShortcutItemTitle" = "New Post";
-"SearchShortcutItemTitle" = "Search";
\ No newline at end of file
+"NSCameraUsageDescription" = "Bidalketetarako argazkiak ateratzeko erabiltzen da";
+"NSPhotoLibraryAddUsageDescription" = "Argazkiak Argazki-liburutegian gordetzeko erabiltzen da";
+"NewPostShortcutItemTitle" = "Bidalketa berria";
+"SearchShortcutItemTitle" = "Bilatu";
\ No newline at end of file
diff --git a/Mastodon/Resources/fi.lproj/InfoPlist.strings b/Mastodon/Resources/fi.lproj/InfoPlist.strings
index 710865573..040b25d69 100644
--- a/Mastodon/Resources/fi.lproj/InfoPlist.strings
+++ b/Mastodon/Resources/fi.lproj/InfoPlist.strings
@@ -1,4 +1,4 @@
-"NSCameraUsageDescription" = "Used to take photo for post status";
-"NSPhotoLibraryAddUsageDescription" = "Used to save photo into the Photo Library";
-"NewPostShortcutItemTitle" = "New Post";
-"SearchShortcutItemTitle" = "Search";
\ No newline at end of file
+"NSCameraUsageDescription" = "Käytetään kuvan ottamiseen julkaisua varten";
+"NSPhotoLibraryAddUsageDescription" = "Käytetään kuvan tallentamiseen kuvakirjastoon";
+"NewPostShortcutItemTitle" = "Uusi julkaisu";
+"SearchShortcutItemTitle" = "Haku";
\ No newline at end of file
diff --git a/Mastodon/Resources/gd.lproj/InfoPlist.strings b/Mastodon/Resources/gd.lproj/InfoPlist.strings
index 710865573..ccb39b44e 100644
--- a/Mastodon/Resources/gd.lproj/InfoPlist.strings
+++ b/Mastodon/Resources/gd.lproj/InfoPlist.strings
@@ -1,4 +1,4 @@
-"NSCameraUsageDescription" = "Used to take photo for post status";
-"NSPhotoLibraryAddUsageDescription" = "Used to save photo into the Photo Library";
-"NewPostShortcutItemTitle" = "New Post";
-"SearchShortcutItemTitle" = "Search";
\ No newline at end of file
+"NSCameraUsageDescription" = "’Ga chleachdadh airson dealbh a thogail do staid puist";
+"NSPhotoLibraryAddUsageDescription" = "’Ga chleachdadh airson dealbh a shàbhaladh ann an tasg-lann nan dealbhan";
+"NewPostShortcutItemTitle" = "Post ùr";
+"SearchShortcutItemTitle" = "Lorg";
\ No newline at end of file
diff --git a/Mastodon/Resources/gl.lproj/InfoPlist.strings b/Mastodon/Resources/gl.lproj/InfoPlist.strings
index 710865573..3d6df3f0f 100644
--- a/Mastodon/Resources/gl.lproj/InfoPlist.strings
+++ b/Mastodon/Resources/gl.lproj/InfoPlist.strings
@@ -1,4 +1,4 @@
-"NSCameraUsageDescription" = "Used to take photo for post status";
-"NSPhotoLibraryAddUsageDescription" = "Used to save photo into the Photo Library";
-"NewPostShortcutItemTitle" = "New Post";
-"SearchShortcutItemTitle" = "Search";
\ No newline at end of file
+"NSCameraUsageDescription" = "Utilizado para facer foto e publicar estado";
+"NSPhotoLibraryAddUsageDescription" = "Utilizado para gardar a foto na Biblioteca";
+"NewPostShortcutItemTitle" = "Nova publicación";
+"SearchShortcutItemTitle" = "Buscar";
\ No newline at end of file
diff --git a/Mastodon/Resources/it.lproj/InfoPlist.strings b/Mastodon/Resources/it.lproj/InfoPlist.strings
index 710865573..0da468639 100644
--- a/Mastodon/Resources/it.lproj/InfoPlist.strings
+++ b/Mastodon/Resources/it.lproj/InfoPlist.strings
@@ -1,4 +1,4 @@
-"NSCameraUsageDescription" = "Used to take photo for post status";
-"NSPhotoLibraryAddUsageDescription" = "Used to save photo into the Photo Library";
-"NewPostShortcutItemTitle" = "New Post";
-"SearchShortcutItemTitle" = "Search";
\ No newline at end of file
+"NSCameraUsageDescription" = "Usato per scattare foto per lo stato del post";
+"NSPhotoLibraryAddUsageDescription" = "Utilizzato per salvare la foto nella galleria immagini";
+"NewPostShortcutItemTitle" = "Nuovo post";
+"SearchShortcutItemTitle" = "Cerca";
\ No newline at end of file
diff --git a/Mastodon/Resources/kab.lproj/InfoPlist.strings b/Mastodon/Resources/kab.lproj/InfoPlist.strings
index 710865573..42adc4cfc 100644
--- a/Mastodon/Resources/kab.lproj/InfoPlist.strings
+++ b/Mastodon/Resources/kab.lproj/InfoPlist.strings
@@ -1,4 +1,4 @@
-"NSCameraUsageDescription" = "Used to take photo for post status";
-"NSPhotoLibraryAddUsageDescription" = "Used to save photo into the Photo Library";
-"NewPostShortcutItemTitle" = "New Post";
-"SearchShortcutItemTitle" = "Search";
\ No newline at end of file
+"NSCameraUsageDescription" = "Yettwaseqdac i tuṭṭfa n tewlafin deg usuffeɣ n waddaden";
+"NSPhotoLibraryAddUsageDescription" = "Yettwaseqdac i usekles n tewlafin deg temkarḍit n tewlafin";
+"NewPostShortcutItemTitle" = "Tasuffeɣt tamaynut";
+"SearchShortcutItemTitle" = "Nadi";
\ No newline at end of file
diff --git a/Mastodon/Resources/ku.lproj/InfoPlist.strings b/Mastodon/Resources/ku.lproj/InfoPlist.strings
index 710865573..669ecfacf 100644
--- a/Mastodon/Resources/ku.lproj/InfoPlist.strings
+++ b/Mastodon/Resources/ku.lproj/InfoPlist.strings
@@ -1,4 +1,4 @@
-"NSCameraUsageDescription" = "Used to take photo for post status";
-"NSPhotoLibraryAddUsageDescription" = "Used to save photo into the Photo Library";
-"NewPostShortcutItemTitle" = "New Post";
-"SearchShortcutItemTitle" = "Search";
\ No newline at end of file
+"NSCameraUsageDescription" = "Bo kişandina wêneyê ji bo rewşa şandiyan tê bikaranîn";
+"NSPhotoLibraryAddUsageDescription" = "Ji bo tomarkirina wêneyê di pirtûkxaneya wêneyan de tê bikaranîn";
+"NewPostShortcutItemTitle" = "Şandiya nû";
+"SearchShortcutItemTitle" = "Bigere";
\ No newline at end of file
diff --git a/Mastodon/Resources/nl.lproj/InfoPlist.strings b/Mastodon/Resources/nl.lproj/InfoPlist.strings
index a45991068..36b6e9802 100644
--- a/Mastodon/Resources/nl.lproj/InfoPlist.strings
+++ b/Mastodon/Resources/nl.lproj/InfoPlist.strings
@@ -1,4 +1,4 @@
"NSCameraUsageDescription" = "Gebruikt om foto's te nemen voor je berichten";
"NSPhotoLibraryAddUsageDescription" = "Gebruikt om foto's op te slaan in de fotobibliotheek";
"NewPostShortcutItemTitle" = "Nieuw Bericht";
-"SearchShortcutItemTitle" = "Zoeken";
\ No newline at end of file
+"SearchShortcutItemTitle" = "Zoek";
\ No newline at end of file
diff --git a/Mastodon/Resources/sl.lproj/InfoPlist.strings b/Mastodon/Resources/sl.lproj/InfoPlist.strings
new file mode 100644
index 000000000..57c8319f5
--- /dev/null
+++ b/Mastodon/Resources/sl.lproj/InfoPlist.strings
@@ -0,0 +1,4 @@
+"NSCameraUsageDescription" = "Uporabljeno za zajem fotografij za stanje objave";
+"NSPhotoLibraryAddUsageDescription" = "Uporabljeno za shranjevanje fotografije v knjižnico fotografij";
+"NewPostShortcutItemTitle" = "Nova objava";
+"SearchShortcutItemTitle" = "Iskanje";
\ No newline at end of file
diff --git a/Mastodon/Resources/sv.lproj/InfoPlist.strings b/Mastodon/Resources/sv.lproj/InfoPlist.strings
index 710865573..e0facc1d2 100644
--- a/Mastodon/Resources/sv.lproj/InfoPlist.strings
+++ b/Mastodon/Resources/sv.lproj/InfoPlist.strings
@@ -1,4 +1,4 @@
-"NSCameraUsageDescription" = "Used to take photo for post status";
-"NSPhotoLibraryAddUsageDescription" = "Used to save photo into the Photo Library";
-"NewPostShortcutItemTitle" = "New Post";
-"SearchShortcutItemTitle" = "Search";
\ No newline at end of file
+"NSCameraUsageDescription" = "Används för att ta foto till inlägg";
+"NSPhotoLibraryAddUsageDescription" = "Används för att spara foto till bildbiblioteket";
+"NewPostShortcutItemTitle" = "Nytt inlägg";
+"SearchShortcutItemTitle" = "Sök";
\ No newline at end of file
diff --git a/Mastodon/Resources/tr.lproj/InfoPlist.strings b/Mastodon/Resources/tr.lproj/InfoPlist.strings
index 710865573..1c4e05659 100644
--- a/Mastodon/Resources/tr.lproj/InfoPlist.strings
+++ b/Mastodon/Resources/tr.lproj/InfoPlist.strings
@@ -1,4 +1,4 @@
-"NSCameraUsageDescription" = "Used to take photo for post status";
-"NSPhotoLibraryAddUsageDescription" = "Used to save photo into the Photo Library";
-"NewPostShortcutItemTitle" = "New Post";
-"SearchShortcutItemTitle" = "Search";
\ No newline at end of file
+"NSCameraUsageDescription" = "Fotoğraf çekerek durum paylaşmak için kullanılır";
+"NSPhotoLibraryAddUsageDescription" = "Fotoğraf Albümü'ne fotoğraf kaydetmek için kullanılır";
+"NewPostShortcutItemTitle" = "Yeni Gönderi";
+"SearchShortcutItemTitle" = "Arama";
\ No newline at end of file
diff --git a/Mastodon/Resources/vi.lproj/InfoPlist.strings b/Mastodon/Resources/vi.lproj/InfoPlist.strings
index 710865573..5dd27b7bc 100644
--- a/Mastodon/Resources/vi.lproj/InfoPlist.strings
+++ b/Mastodon/Resources/vi.lproj/InfoPlist.strings
@@ -1,4 +1,4 @@
-"NSCameraUsageDescription" = "Used to take photo for post status";
-"NSPhotoLibraryAddUsageDescription" = "Used to save photo into the Photo Library";
-"NewPostShortcutItemTitle" = "New Post";
-"SearchShortcutItemTitle" = "Search";
\ No newline at end of file
+"NSCameraUsageDescription" = "Được sử dụng để chụp ảnh cho tút";
+"NSPhotoLibraryAddUsageDescription" = "Được sử dụng để lưu ảnh vào Thư viện ảnh";
+"NewPostShortcutItemTitle" = "Viết tút";
+"SearchShortcutItemTitle" = "Tìm kiếm";
\ No newline at end of file
diff --git a/Mastodon/Resources/zh-Hant.lproj/InfoPlist.strings b/Mastodon/Resources/zh-Hant.lproj/InfoPlist.strings
index 710865573..737d2f857 100644
--- a/Mastodon/Resources/zh-Hant.lproj/InfoPlist.strings
+++ b/Mastodon/Resources/zh-Hant.lproj/InfoPlist.strings
@@ -1,4 +1,4 @@
-"NSCameraUsageDescription" = "Used to take photo for post status";
-"NSPhotoLibraryAddUsageDescription" = "Used to save photo into the Photo Library";
-"NewPostShortcutItemTitle" = "New Post";
-"SearchShortcutItemTitle" = "Search";
\ No newline at end of file
+"NSCameraUsageDescription" = "用來拍照發嘟文";
+"NSPhotoLibraryAddUsageDescription" = "用來儲存照片到圖片庫";
+"NewPostShortcutItemTitle" = "新增嘟文";
+"SearchShortcutItemTitle" = "搜尋";
\ No newline at end of file
diff --git a/Mastodon/Scene/Account/AccountListViewModel.swift b/Mastodon/Scene/Account/AccountListViewModel.swift
index e0aaf97fc..2b8c75b8d 100644
--- a/Mastodon/Scene/Account/AccountListViewModel.swift
+++ b/Mastodon/Scene/Account/AccountListViewModel.swift
@@ -52,7 +52,7 @@ final class AccountListViewModel: NSObject {
mastodonAuthenticationFetchedResultsController.delegate = self
do {
try mastodonAuthenticationFetchedResultsController.performFetch()
- authentications = mastodonAuthenticationFetchedResultsController.fetchedObjects?.compactMap { $0.asRecrod } ?? []
+ authentications = mastodonAuthenticationFetchedResultsController.fetchedObjects?.compactMap { $0.asRecord } ?? []
} catch {
assertionFailure(error.localizedDescription)
}
@@ -166,7 +166,7 @@ extension AccountListViewModel {
cell.badgeButton.accessibilityLabel
]
.compactMap { $0 }
- .joined(separator: " ")
+ .joined(separator: ", ")
}
}
@@ -183,7 +183,7 @@ extension AccountListViewModel: NSFetchedResultsControllerDelegate {
return
}
- authentications = mastodonAuthenticationFetchedResultsController.fetchedObjects?.compactMap { $0.asRecrod } ?? []
+ authentications = mastodonAuthenticationFetchedResultsController.fetchedObjects?.compactMap { $0.asRecord } ?? []
}
}
diff --git a/Mastodon/Scene/Account/AccountViewController.swift b/Mastodon/Scene/Account/AccountViewController.swift
index e25c75b01..7a0e529cc 100644
--- a/Mastodon/Scene/Account/AccountViewController.swift
+++ b/Mastodon/Scene/Account/AccountViewController.swift
@@ -34,7 +34,9 @@ final class AccountListViewController: UIViewController, NeedsDependency {
return barButtonItem
}()
- let dragIndicatorView = DragIndicatorView()
+ lazy var dragIndicatorView = DragIndicatorView { [weak self] in
+ self?.dismiss(animated: true, completion: nil)
+ }
var hasLoaded = false
private(set) lazy var tableView: UITableView = {
@@ -130,14 +132,6 @@ extension AccountListViewController {
self.panModalTransition(to: .shortForm)
}
.store(in: &disposeBag)
-
- if UIAccessibility.isVoiceOverRunning {
- let dragIndicatorTapGestureRecognizer = UITapGestureRecognizer.singleTapGestureRecognizer
- dragIndicatorView.addGestureRecognizer(dragIndicatorTapGestureRecognizer)
- dragIndicatorTapGestureRecognizer.addTarget(self, action: #selector(AccountListViewController.dragIndicatorTapGestureRecognizerHandler(_:)))
- dragIndicatorView.isAccessibilityElement = true
- dragIndicatorView.accessibilityLabel = L10n.Scene.AccountList.dismissAccountSwitcher
- }
}
private func setupBackgroundColor(theme: Theme) {
@@ -160,10 +154,11 @@ extension AccountListViewController {
logger.debug("\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public)")
_ = coordinator.present(scene: .welcome, from: self, transition: .modal(animated: true, completion: nil))
}
-
- @objc private func dragIndicatorTapGestureRecognizerHandler(_ sender: UITapGestureRecognizer) {
+
+ override func accessibilityPerformEscape() -> Bool {
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public)")
dismiss(animated: true, completion: nil)
+ return true
}
}
diff --git a/Mastodon/Scene/Account/Cell/AccountListTableViewCell.swift b/Mastodon/Scene/Account/Cell/AccountListTableViewCell.swift
index 66f49efe8..cd214f2c7 100644
--- a/Mastodon/Scene/Account/Cell/AccountListTableViewCell.swift
+++ b/Mastodon/Scene/Account/Cell/AccountListTableViewCell.swift
@@ -69,6 +69,7 @@ extension AccountListTableViewCell {
])
avatarButton.setContentHuggingPriority(.defaultLow, for: .horizontal)
avatarButton.setContentHuggingPriority(.defaultLow, for: .vertical)
+ avatarButton.isAccessibilityElement = false
let labelContainerStackView = UIStackView()
labelContainerStackView.axis = .vertical
@@ -124,6 +125,8 @@ extension AccountListTableViewCell {
badgeButton.setBadge(number: 0)
checkmarkImageView.isHidden = true
+
+ accessibilityTraits.insert(.button)
}
}
diff --git a/Mastodon/Scene/Account/Cell/AddAccountTableViewCell.swift b/Mastodon/Scene/Account/Cell/AddAccountTableViewCell.swift
index 3ff3066a2..bc47aef43 100644
--- a/Mastodon/Scene/Account/Cell/AddAccountTableViewCell.swift
+++ b/Mastodon/Scene/Account/Cell/AddAccountTableViewCell.swift
@@ -108,6 +108,8 @@ extension AddAccountTableViewCell {
separatorLine.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
separatorLine.heightAnchor.constraint(equalToConstant: UIView.separatorLineHeight(of: contentView)),
])
+
+ accessibilityTraits.insert(.button)
}
}
diff --git a/Mastodon/Scene/Account/View/DragIndicatorView.swift b/Mastodon/Scene/Account/View/DragIndicatorView.swift
index 9e0ab77d5..a04d9cd8c 100644
--- a/Mastodon/Scene/Account/View/DragIndicatorView.swift
+++ b/Mastodon/Scene/Account/View/DragIndicatorView.swift
@@ -15,17 +15,17 @@ final class DragIndicatorView: UIView {
let barView = UIView()
let separatorLine = UIView.separatorLine
+ let onDismiss: () -> Void
- override init(frame: CGRect) {
- super.init(frame: frame)
+ init(onDismiss: @escaping () -> Void) {
+ self.onDismiss = onDismiss
+ super.init(frame: .zero)
_init()
}
required init?(coder: NSCoder) {
- super.init(coder: coder)
- _init()
+ fatalError("init(coder:) is not supported")
}
-
}
extension DragIndicatorView {
@@ -52,6 +52,14 @@ extension DragIndicatorView {
separatorLine.bottomAnchor.constraint(equalTo: bottomAnchor),
separatorLine.heightAnchor.constraint(equalToConstant: UIView.separatorLineHeight(of: self)),
])
+
+ isAccessibilityElement = true
+ accessibilityTraits = .button
+ accessibilityLabel = L10n.Scene.AccountList.dismissAccountSwitcher
}
+ override func accessibilityActivate() -> Bool {
+ self.onDismiss()
+ return true
+ }
}
diff --git a/Mastodon/Scene/Compose/ComposeViewController.swift b/Mastodon/Scene/Compose/ComposeViewController.swift
index fd5ed5817..9f287dbf8 100644
--- a/Mastodon/Scene/Compose/ComposeViewController.swift
+++ b/Mastodon/Scene/Compose/ComposeViewController.swift
@@ -34,7 +34,8 @@ final class ComposeViewController: UIViewController, NeedsDependency {
return ComposeContentViewModel(
context: context,
authContext: viewModel.authContext,
- kind: viewModel.kind
+ destination: viewModel.destination,
+ initialContent: viewModel.initialContent
)
}()
private(set) lazy var composeContentViewController: ComposeContentViewController = {
@@ -58,12 +59,7 @@ final class ComposeViewController: UIViewController, NeedsDependency {
let shadowBackgroundContainer = ShadowBackgroundContainer()
publishButton.translatesAutoresizingMaskIntoConstraints = false
shadowBackgroundContainer.addSubview(publishButton)
- NSLayoutConstraint.activate([
- publishButton.topAnchor.constraint(equalTo: shadowBackgroundContainer.topAnchor),
- publishButton.leadingAnchor.constraint(equalTo: shadowBackgroundContainer.leadingAnchor),
- publishButton.trailingAnchor.constraint(equalTo: shadowBackgroundContainer.trailingAnchor),
- publishButton.bottomAnchor.constraint(equalTo: shadowBackgroundContainer.bottomAnchor),
- ])
+ publishButton.pinToParent()
let barButtonItem = UIBarButtonItem(customView: shadowBackgroundContainer)
return barButtonItem
}()
@@ -93,10 +89,7 @@ extension ComposeViewController {
.sink { [weak self] _ in
guard let self = self else { return }
guard self.traitCollection.userInterfaceIdiom == .pad else { return }
- var items = [self.publishBarButtonItem]
- // if self.traitCollection.horizontalSizeClass == .regular {
- // items.append(self.characterCountBarButtonItem)
- // }
+ let items = [self.publishBarButtonItem]
self.navigationItem.rightBarButtonItems = items
}
.store(in: &disposeBag)
@@ -105,12 +98,7 @@ extension ComposeViewController {
addChild(composeContentViewController)
composeContentViewController.view.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(composeContentViewController.view)
- NSLayoutConstraint.activate([
- composeContentViewController.view.topAnchor.constraint(equalTo: view.topAnchor),
- composeContentViewController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor),
- composeContentViewController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor),
- composeContentViewController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor),
- ])
+ composeContentViewController.view.pinToParent()
composeContentViewController.didMove(toParent: self)
// bind title
@@ -175,7 +163,7 @@ extension ComposeViewController {
let alertController = UIAlertController(for: error, title: nil, preferredStyle: .alert)
let okAction = UIAlertAction(title: L10n.Common.Controls.Actions.ok, style: .default, handler: nil)
alertController.addAction(okAction)
- coordinator.present(scene: .alertController(alertController: alertController), from: nil, transition: .alertController(animated: true, completion: nil))
+ _ = coordinator.present(scene: .alertController(alertController: alertController), from: nil, transition: .alertController(animated: true, completion: nil))
return
}
@@ -222,6 +210,7 @@ extension ComposeViewController {
api: viewModel.context.apiService,
authContext: viewModel.authContext,
input: .image(image),
+ sizeLimit: composeContentViewModel.sizeLimit,
delegate: composeContentViewModel
)
}
diff --git a/Mastodon/Scene/Compose/ComposeViewModel.swift b/Mastodon/Scene/Compose/ComposeViewModel.swift
index bf234b095..0dcdd9a2d 100644
--- a/Mastodon/Scene/Compose/ComposeViewModel.swift
+++ b/Mastodon/Scene/Compose/ComposeViewModel.swift
@@ -29,7 +29,8 @@ final class ComposeViewModel {
// input
let context: AppContext
let authContext: AuthContext
- let kind: ComposeContentViewModel.Kind
+ let destination: ComposeContentViewModel.Destination
+ let initialContent: String
let traitCollectionDidChangePublisher = CurrentValueSubject(Void()) // use CurrentValueSubject to make initial event emit
@@ -41,17 +42,19 @@ final class ComposeViewModel {
init(
context: AppContext,
authContext: AuthContext,
- kind: ComposeContentViewModel.Kind
+ destination: ComposeContentViewModel.Destination,
+ initialContent: String = ""
) {
self.context = context
self.authContext = authContext
- self.kind = kind
+ self.destination = destination
+ self.initialContent = initialContent
// end init
self.title = {
- switch kind {
- case .post, .hashtag, .mention: return L10n.Scene.Compose.Title.newPost
- case .reply: return L10n.Scene.Compose.Title.newReply
+ switch destination {
+ case .topLevel: return L10n.Scene.Compose.Title.newPost
+ case .reply: return L10n.Scene.Compose.Title.newReply
}
}()
}
diff --git a/Mastodon/Scene/Discovery/Community/DiscoveryCommunityViewController.swift b/Mastodon/Scene/Discovery/Community/DiscoveryCommunityViewController.swift
index 31635dc85..870bfc72a 100644
--- a/Mastodon/Scene/Discovery/Community/DiscoveryCommunityViewController.swift
+++ b/Mastodon/Scene/Discovery/Community/DiscoveryCommunityViewController.swift
@@ -57,12 +57,7 @@ extension DiscoveryCommunityViewController {
tableView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(tableView)
- NSLayoutConstraint.activate([
- tableView.topAnchor.constraint(equalTo: view.topAnchor),
- tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
- tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
- tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
- ])
+ tableView.pinToParent()
tableView.refreshControl = refreshControl
refreshControl.addTarget(self, action: #selector(DiscoveryCommunityViewController.refreshControlValueChanged(_:)), for: .valueChanged)
diff --git a/Mastodon/Scene/Discovery/Community/DiscoveryCommunityViewModel+Diffable.swift b/Mastodon/Scene/Discovery/Community/DiscoveryCommunityViewModel+Diffable.swift
index 64b4d3b6a..a25dbaf1c 100644
--- a/Mastodon/Scene/Discovery/Community/DiscoveryCommunityViewModel+Diffable.swift
+++ b/Mastodon/Scene/Discovery/Community/DiscoveryCommunityViewModel+Diffable.swift
@@ -18,6 +18,7 @@ extension DiscoveryCommunityViewModel {
tableView: tableView,
context: context,
configuration: StatusSection.Configuration(
+ context: context,
authContext: authContext,
statusTableViewCellDelegate: statusTableViewCellDelegate,
timelineMiddleLoaderTableViewCellDelegate: nil,
@@ -58,7 +59,7 @@ extension DiscoveryCommunityViewModel {
}
}
- diffableDataSource.applySnapshot(snapshot, animated: false)
+ diffableDataSource.apply(snapshot, animatingDifferences: false)
}
.store(in: &disposeBag)
}
diff --git a/Mastodon/Scene/Discovery/Community/DiscoveryCommunityViewModel+State.swift b/Mastodon/Scene/Discovery/Community/DiscoveryCommunityViewModel+State.swift
index 476832b69..5045e825a 100644
--- a/Mastodon/Scene/Discovery/Community/DiscoveryCommunityViewModel+State.swift
+++ b/Mastodon/Scene/Discovery/Community/DiscoveryCommunityViewModel+State.swift
@@ -125,7 +125,7 @@ extension DiscoveryCommunityViewModel.State {
override func didEnter(from previousState: GKState?) {
super.didEnter(from: previousState)
- guard let viewModel = viewModel, let stateMachine = stateMachine else { return }
+ guard let viewModel else { return }
switch previousState {
case is Reloading:
diff --git a/Mastodon/Scene/Discovery/ForYou/DiscoveryForYouViewController.swift b/Mastodon/Scene/Discovery/ForYou/DiscoveryForYouViewController.swift
index 52e5e1856..dec1d9c72 100644
--- a/Mastodon/Scene/Discovery/ForYou/DiscoveryForYouViewController.swift
+++ b/Mastodon/Scene/Discovery/ForYou/DiscoveryForYouViewController.swift
@@ -56,12 +56,7 @@ extension DiscoveryForYouViewController {
tableView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(tableView)
- NSLayoutConstraint.activate([
- tableView.topAnchor.constraint(equalTo: view.topAnchor),
- tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
- tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
- tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
- ])
+ tableView.pinToParent()
tableView.delegate = self
viewModel.setupDiffableDataSource(
diff --git a/Mastodon/Scene/Discovery/ForYou/DiscoveryForYouViewModel+Diffable.swift b/Mastodon/Scene/Discovery/ForYou/DiscoveryForYouViewModel+Diffable.swift
index af8d6ff47..31b14c55d 100644
--- a/Mastodon/Scene/Discovery/ForYou/DiscoveryForYouViewModel+Diffable.swift
+++ b/Mastodon/Scene/Discovery/ForYou/DiscoveryForYouViewModel+Diffable.swift
@@ -41,7 +41,7 @@ extension DiscoveryForYouViewModel {
let items = records.map { DiscoveryItem.user($0) }
snapshot.appendItems(items, toSection: .forYou)
- diffableDataSource.applySnapshot(snapshot, animated: false)
+ diffableDataSource.apply(snapshot, animatingDifferences: false)
}
.store(in: &disposeBag)
}
diff --git a/Mastodon/Scene/Discovery/Hashtags/DiscoveryHashtagsViewController.swift b/Mastodon/Scene/Discovery/Hashtags/DiscoveryHashtagsViewController.swift
index 1c855c254..64d1231fe 100644
--- a/Mastodon/Scene/Discovery/Hashtags/DiscoveryHashtagsViewController.swift
+++ b/Mastodon/Scene/Discovery/Hashtags/DiscoveryHashtagsViewController.swift
@@ -56,12 +56,7 @@ extension DiscoveryHashtagsViewController {
tableView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(tableView)
- NSLayoutConstraint.activate([
- tableView.topAnchor.constraint(equalTo: view.topAnchor),
- tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
- tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
- tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
- ])
+ tableView.pinToParent()
tableView.refreshControl = refreshControl
refreshControl.addTarget(self, action: #selector(DiscoveryHashtagsViewController.refreshControlValueChanged(_:)), for: .valueChanged)
@@ -108,7 +103,7 @@ extension DiscoveryHashtagsViewController: UITableViewDelegate {
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): \(indexPath)")
guard case let .hashtag(tag) = viewModel.diffableDataSource?.itemIdentifier(for: indexPath) else { return }
let hashtagTimelineViewModel = HashtagTimelineViewModel(context: context, authContext: viewModel.authContext, hashtag: tag.name)
- coordinator.present(
+ _ = coordinator.present(
scene: .hashtagTimeline(viewModel: hashtagTimelineViewModel),
from: self,
transition: .show
@@ -218,7 +213,7 @@ extension DiscoveryHashtagsViewController: TableViewControllerNavigateable {
guard case let .hashtag(tag) = item else { return }
let hashtagTimelineViewModel = HashtagTimelineViewModel(context: context, authContext: viewModel.authContext, hashtag: tag.name)
- coordinator.present(
+ _ = coordinator.present(
scene: .hashtagTimeline(viewModel: hashtagTimelineViewModel),
from: self,
transition: .show
diff --git a/Mastodon/Scene/Discovery/News/DiscoveryNewsViewController.swift b/Mastodon/Scene/Discovery/News/DiscoveryNewsViewController.swift
index d1634db82..4884aafac 100644
--- a/Mastodon/Scene/Discovery/News/DiscoveryNewsViewController.swift
+++ b/Mastodon/Scene/Discovery/News/DiscoveryNewsViewController.swift
@@ -56,12 +56,7 @@ extension DiscoveryNewsViewController {
tableView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(tableView)
- NSLayoutConstraint.activate([
- tableView.topAnchor.constraint(equalTo: view.topAnchor),
- tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
- tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
- tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
- ])
+ tableView.pinToParent()
tableView.delegate = self
viewModel.setupDiffableDataSource(
@@ -117,7 +112,7 @@ extension DiscoveryNewsViewController: UITableViewDelegate {
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): \(indexPath)")
guard case let .link(link) = viewModel.diffableDataSource?.itemIdentifier(for: indexPath) else { return }
guard let url = URL(string: link.url) else { return }
- coordinator.present(
+ _ = coordinator.present(
scene: .safari(url: url),
from: self,
transition: .safariPresent(animated: true, completion: nil)
@@ -214,7 +209,7 @@ extension DiscoveryNewsViewController: TableViewControllerNavigateable {
guard case let .link(link) = item else { return }
guard let url = URL(string: link.url) else { return }
- coordinator.present(
+ _ = coordinator.present(
scene: .safari(url: url),
from: self,
transition: .safariPresent(animated: true, completion: nil)
diff --git a/Mastodon/Scene/Discovery/News/DiscoveryNewsViewModel+Diffable.swift b/Mastodon/Scene/Discovery/News/DiscoveryNewsViewModel+Diffable.swift
index 11334dee8..deb8f48a7 100644
--- a/Mastodon/Scene/Discovery/News/DiscoveryNewsViewModel+Diffable.swift
+++ b/Mastodon/Scene/Discovery/News/DiscoveryNewsViewModel+Diffable.swift
@@ -52,7 +52,7 @@ extension DiscoveryNewsViewModel {
}
}
- diffableDataSource.applySnapshot(snapshot, animated: false)
+ diffableDataSource.apply(snapshot, animatingDifferences: false)
}
.store(in: &disposeBag)
}
diff --git a/Mastodon/Scene/Discovery/News/DiscoveryNewsViewModel+State.swift b/Mastodon/Scene/Discovery/News/DiscoveryNewsViewModel+State.swift
index 7c802cde3..8170d5b1c 100644
--- a/Mastodon/Scene/Discovery/News/DiscoveryNewsViewModel+State.swift
+++ b/Mastodon/Scene/Discovery/News/DiscoveryNewsViewModel+State.swift
@@ -125,8 +125,7 @@ extension DiscoveryNewsViewModel.State {
override func didEnter(from previousState: GKState?) {
super.didEnter(from: previousState)
- guard let viewModel = viewModel, let stateMachine = stateMachine else { return }
-
+ guard let viewModel else { return }
switch previousState {
case is Reloading:
diff --git a/Mastodon/Scene/Discovery/Posts/DiscoveryPostsViewController.swift b/Mastodon/Scene/Discovery/Posts/DiscoveryPostsViewController.swift
index 893f564a3..9d772a27e 100644
--- a/Mastodon/Scene/Discovery/Posts/DiscoveryPostsViewController.swift
+++ b/Mastodon/Scene/Discovery/Posts/DiscoveryPostsViewController.swift
@@ -58,12 +58,7 @@ extension DiscoveryPostsViewController {
tableView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(tableView)
- NSLayoutConstraint.activate([
- tableView.topAnchor.constraint(equalTo: view.topAnchor),
- tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
- tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
- tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
- ])
+ tableView.pinToParent()
discoveryIntroBannerView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(discoveryIntroBannerView)
diff --git a/Mastodon/Scene/Discovery/Posts/DiscoveryPostsViewModel+Diffable.swift b/Mastodon/Scene/Discovery/Posts/DiscoveryPostsViewModel+Diffable.swift
index afa0594d5..99d68796d 100644
--- a/Mastodon/Scene/Discovery/Posts/DiscoveryPostsViewModel+Diffable.swift
+++ b/Mastodon/Scene/Discovery/Posts/DiscoveryPostsViewModel+Diffable.swift
@@ -18,6 +18,7 @@ extension DiscoveryPostsViewModel {
tableView: tableView,
context: context,
configuration: StatusSection.Configuration(
+ context: context,
authContext: authContext,
statusTableViewCellDelegate: statusTableViewCellDelegate,
timelineMiddleLoaderTableViewCellDelegate: nil,
@@ -58,7 +59,7 @@ extension DiscoveryPostsViewModel {
}
}
- diffableDataSource.applySnapshot(snapshot, animated: false)
+ diffableDataSource.apply(snapshot, animatingDifferences: false)
}
.store(in: &disposeBag)
}
diff --git a/Mastodon/Scene/Discovery/Posts/DiscoveryPostsViewModel+State.swift b/Mastodon/Scene/Discovery/Posts/DiscoveryPostsViewModel+State.swift
index 3ed245e99..fd413927e 100644
--- a/Mastodon/Scene/Discovery/Posts/DiscoveryPostsViewModel+State.swift
+++ b/Mastodon/Scene/Discovery/Posts/DiscoveryPostsViewModel+State.swift
@@ -126,7 +126,7 @@ extension DiscoveryPostsViewModel.State {
override func didEnter(from previousState: GKState?) {
super.didEnter(from: previousState)
- guard let viewModel = viewModel, let stateMachine = stateMachine else { return }
+ guard let viewModel else { return }
switch previousState {
case is Reloading:
diff --git a/Mastodon/Scene/HashtagTimeline/HashtagTimelineHeaderView.swift b/Mastodon/Scene/HashtagTimeline/HashtagTimelineHeaderView.swift
new file mode 100644
index 000000000..3af2cf07b
--- /dev/null
+++ b/Mastodon/Scene/HashtagTimeline/HashtagTimelineHeaderView.swift
@@ -0,0 +1,173 @@
+//
+// HashtagTimelineHeaderView.swift
+// Mastodon
+//
+// Created by Marcus Kida on 22.11.22.
+//
+
+import UIKit
+import CoreDataStack
+import MastodonSDK
+import MastodonUI
+import MastodonAsset
+import MastodonLocalization
+
+fileprivate extension CGFloat {
+ static let padding: CGFloat = 16
+ static let descriptionLabelSpacing: CGFloat = 12
+}
+
+final class HashtagTimelineHeaderView: UIView {
+ struct Data {
+ let name: String
+ let following: Bool
+ let postCount: Int
+ let participantsCount: Int
+ let postsTodayCount: Int
+
+ static func from(_ entity: Mastodon.Entity.Tag) -> Self {
+ Data(
+ name: entity.name,
+ following: entity.following == true,
+ postCount: (entity.history ?? []).reduce(0) { res, acc in
+ res + (Int(acc.uses) ?? 0)
+ },
+ participantsCount: (entity.history ?? []).reduce(0) { res, acc in
+ res + (Int(acc.accounts) ?? 0)
+ },
+ postsTodayCount: Int(entity.history?.first?.uses ?? "0") ?? 0
+ )
+ }
+
+ static func from(_ entity: Tag) -> Self {
+ Data(
+ name: entity.name,
+ following: entity.following,
+ postCount: entity.histories.reduce(0) { res, acc in
+ res + (Int(acc.uses) ?? 0)
+ },
+ participantsCount: entity.histories.reduce(0) { res, acc in
+ res + (Int(acc.accounts) ?? 0)
+ },
+ postsTodayCount: Int(entity.histories.first?.uses ?? "0") ?? 0
+ )
+ }
+ }
+
+ let titleLabel = UILabel()
+
+ let postCountLabel = UILabel()
+ let participantsLabel = UILabel()
+ let postsTodayLabel = UILabel()
+
+ let postCountDescLabel = UILabel()
+ let participantsDescLabel = UILabel()
+ let postsTodayDescLabel = UILabel()
+
+ private var widthConstraint: NSLayoutConstraint!
+
+ var onButtonTapped: (() -> Void)?
+
+ let followButton: UIButton = {
+ let button = HashtagTimelineHeaderViewActionButton()
+ button.cornerRadius = 10
+ button.contentEdgeInsets = UIEdgeInsets(top: 6, left: 16, bottom: 5, right: 16) // set 28pt height
+ button.titleLabel?.font = .systemFont(ofSize: 14, weight: .bold)
+ return button
+ }()
+
+ init() {
+ super.init(frame: .zero)
+ setupLayout()
+ }
+
+ required init?(coder: NSCoder) {
+ fatalError("init(coder:) has not been implemented")
+ }
+
+}
+
+private extension HashtagTimelineHeaderView {
+ func setupLayout() {
+ [titleLabel, postCountLabel, participantsLabel, postsTodayLabel, postCountDescLabel, participantsDescLabel, postsTodayDescLabel, followButton].forEach {
+ $0.translatesAutoresizingMaskIntoConstraints = false
+ addSubview($0)
+ }
+
+ // hashtag name / title
+ titleLabel.font = UIFontMetrics(forTextStyle: .title2).scaledFont(for: .systemFont(ofSize: 22, weight: .bold))
+
+ [postCountLabel, participantsLabel, postsTodayLabel].forEach {
+ $0.font = UIFontMetrics(forTextStyle: .title3).scaledFont(for: .systemFont(ofSize: 20, weight: .bold))
+ $0.text = "999"
+ }
+
+ [postCountDescLabel, participantsDescLabel, postsTodayDescLabel].forEach {
+ $0.font = UIFontMetrics(forTextStyle: .footnote).scaledFont(for: .systemFont(ofSize: 13, weight: .regular))
+ }
+
+ postCountDescLabel.text = L10n.Scene.FollowedTags.Header.posts
+ participantsDescLabel.text = L10n.Scene.FollowedTags.Header.participants
+ postsTodayDescLabel.text = L10n.Scene.FollowedTags.Header.postsToday
+
+ followButton.addAction(UIAction(handler: { [weak self] _ in
+ self?.onButtonTapped?()
+ }), for: .touchUpInside)
+
+ widthConstraint = widthAnchor.constraint(greaterThanOrEqualToConstant: 0)
+
+ NSLayoutConstraint.activate([
+ widthConstraint,
+
+ titleLabel.topAnchor.constraint(equalTo: topAnchor, constant: .padding),
+ titleLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: .padding),
+ titleLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -CGFloat.padding),
+
+ postCountLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: .padding),
+ postCountLabel.centerXAnchor.constraint(equalTo: postCountDescLabel.centerXAnchor),
+ postCountDescLabel.leadingAnchor.constraint(equalTo: titleLabel.leadingAnchor),
+
+ participantsDescLabel.leadingAnchor.constraint(equalTo: postCountDescLabel.trailingAnchor, constant: .descriptionLabelSpacing),
+ participantsLabel.centerXAnchor.constraint(equalTo: participantsDescLabel.centerXAnchor),
+ participantsLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: .padding),
+
+ postsTodayDescLabel.leadingAnchor.constraint(equalTo: participantsDescLabel.trailingAnchor, constant: .descriptionLabelSpacing),
+ postsTodayLabel.centerXAnchor.constraint(equalTo: postsTodayDescLabel.centerXAnchor),
+ postsTodayLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: .padding),
+
+ postCountDescLabel.topAnchor.constraint(equalTo: postCountLabel.bottomAnchor),
+ participantsDescLabel.topAnchor.constraint(equalTo: participantsLabel.bottomAnchor),
+ postsTodayDescLabel.topAnchor.constraint(equalTo: postsTodayLabel.bottomAnchor),
+
+ postCountDescLabel.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -CGFloat.padding),
+ participantsDescLabel.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -CGFloat.padding),
+ postsTodayDescLabel.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -CGFloat.padding),
+
+ followButton.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -CGFloat.padding),
+ followButton.bottomAnchor.constraint(equalTo: postsTodayDescLabel.bottomAnchor),
+ followButton.topAnchor.constraint(equalTo: postsTodayLabel.topAnchor)
+ ])
+ }
+}
+
+extension HashtagTimelineHeaderView {
+ func update(_ entity: HashtagTimelineHeaderView.Data) {
+ titleLabel.text = "#\(entity.name)"
+ followButton.setTitle(entity.following == true ? L10n.Scene.FollowedTags.Actions.unfollow : L10n.Scene.FollowedTags.Actions.follow, for: .normal)
+
+ followButton.backgroundColor = entity.following == true ? Asset.Colors.Button.tagUnfollow.color : Asset.Colors.Button.tagFollow.color
+
+ followButton.setTitleColor(
+ entity.following == true ? Asset.Colors.Button.tagFollow.color : Asset.Colors.Button.tagUnfollow.color,
+ for: .normal
+ )
+
+ postCountLabel.text = String(entity.postCount)
+ participantsLabel.text = String(entity.participantsCount)
+ postsTodayLabel.text = String(entity.postsTodayCount)
+ }
+
+ func updateWidthConstraint(_ constant: CGFloat) {
+ widthConstraint.constant = constant
+ }
+}
diff --git a/Mastodon/Scene/HashtagTimeline/HashtagTimelineHeaderViewActionButton.swift b/Mastodon/Scene/HashtagTimeline/HashtagTimelineHeaderViewActionButton.swift
new file mode 100644
index 000000000..7efeb15d0
--- /dev/null
+++ b/Mastodon/Scene/HashtagTimeline/HashtagTimelineHeaderViewActionButton.swift
@@ -0,0 +1,47 @@
+//
+// HashtagTimelineHeaderViewActionButton.swift
+// Mastodon
+//
+// Created by Marcus Kida on 25.11.22.
+//
+
+import UIKit
+import MastodonUI
+import MastodonAsset
+
+class HashtagTimelineHeaderViewActionButton: RoundedEdgesButton {
+
+ init() {
+ super.init(frame: .zero)
+ }
+
+ required init?(coder: NSCoder) {
+ fatalError("init(coder:) has not been implemented")
+ }
+
+ public override func layoutSubviews() {
+ super.layoutSubviews()
+
+ let shadowColor: UIColor = {
+ switch traitCollection.userInterfaceStyle {
+ case .dark:
+ return .darkGray
+ default:
+ return .lightGray
+ }
+ }()
+
+ layer.setupShadow(
+ color: shadowColor,
+ alpha: 1,
+ x: 0,
+ y: 1,
+ blur: 2,
+ spread: 0,
+ roundedRect: bounds,
+ byRoundingCorners: .allCorners,
+ cornerRadii: CGSize(width: cornerRadius, height: cornerRadius)
+ )
+ }
+}
+
diff --git a/Mastodon/Scene/HashtagTimeline/HashtagTimelineViewController.swift b/Mastodon/Scene/HashtagTimeline/HashtagTimelineViewController.swift
index aad97283c..75d9029e2 100644
--- a/Mastodon/Scene/HashtagTimeline/HashtagTimelineViewController.swift
+++ b/Mastodon/Scene/HashtagTimeline/HashtagTimelineViewController.swift
@@ -15,6 +15,7 @@ import MastodonAsset
import MastodonCore
import MastodonUI
import MastodonLocalization
+import MastodonSDK
final class HashtagTimelineViewController: UIViewController, NeedsDependency, MediaPreviewableViewController {
@@ -27,6 +28,17 @@ final class HashtagTimelineViewController: UIViewController, NeedsDependency, Me
var disposeBag = Set()
var viewModel: HashtagTimelineViewModel!
+
+ private lazy var headerView: HashtagTimelineHeaderView = {
+ let headerView = HashtagTimelineHeaderView()
+ headerView.translatesAutoresizingMaskIntoConstraints = false
+
+ NSLayoutConstraint.activate([
+ headerView.heightAnchor.constraint(equalToConstant: 118),
+ ])
+
+ return headerView
+ }()
let composeBarButtonItem: UIBarButtonItem = {
let barButtonItem = UIBarButtonItem()
@@ -80,12 +92,7 @@ extension HashtagTimelineViewController {
tableView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(tableView)
- NSLayoutConstraint.activate([
- tableView.topAnchor.constraint(equalTo: view.topAnchor),
- tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
- tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
- tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
- ])
+ tableView.pinToParent()
tableView.delegate = self
viewModel.setupDiffableDataSource(
@@ -119,11 +126,20 @@ extension HashtagTimelineViewController {
self?.updatePromptTitle()
}
.store(in: &disposeBag)
+
+ viewModel.hashtagDetails
+ .receive(on: DispatchQueue.main)
+ .sink { [weak self] tag in
+ guard let tag = tag else { return }
+ self?.updateHeaderView(with: tag)
+ }
+ .store(in: &disposeBag)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
+ viewModel.viewWillAppear()
tableView.deselectRow(with: transitionCoordinator, animated: animated)
}
@@ -153,7 +169,30 @@ extension HashtagTimelineViewController {
subtitle = L10n.Plural.peopleTalking(peopleTalkingNumber)
}
}
+}
+extension HashtagTimelineViewController {
+ private func updateHeaderView(with tag: Mastodon.Entity.Tag) {
+ if tableView.tableHeaderView == nil {
+ tableView.tableHeaderView = headerView
+ }
+ headerView.update(HashtagTimelineHeaderView.Data.from(tag))
+ headerView.onButtonTapped = { [weak self] in
+ switch tag.following {
+ case .some(false):
+ self?.viewModel.followTag()
+ case .some(true):
+ self?.viewModel.unfollowTag()
+ default:
+ break
+ }
+ }
+ }
+
+ override func viewDidLayoutSubviews() {
+ super.viewDidLayoutSubviews()
+ headerView.updateWidthConstraint(tableView.bounds.width)
+ }
}
extension HashtagTimelineViewController {
@@ -167,10 +206,13 @@ extension HashtagTimelineViewController {
@objc private func composeBarButtonItemPressed(_ sender: UIBarButtonItem) {
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
+ let hashtag = "#" + viewModel.hashtag
+ UITextChecker.learnWord(hashtag)
let composeViewModel = ComposeViewModel(
context: context,
authContext: viewModel.authContext,
- kind: .hashtag(hashtag: viewModel.hashtag)
+ destination: .topLevel,
+ initialContent: hashtag
)
_ = coordinator.present(scene: .compose(viewModel: composeViewModel), from: self, transition: .modal(animated: true, completion: nil))
}
diff --git a/Mastodon/Scene/HashtagTimeline/HashtagTimelineViewModel+Diffable.swift b/Mastodon/Scene/HashtagTimeline/HashtagTimelineViewModel+Diffable.swift
index 8d8b0126a..c7c0a3bd7 100644
--- a/Mastodon/Scene/HashtagTimeline/HashtagTimelineViewModel+Diffable.swift
+++ b/Mastodon/Scene/HashtagTimeline/HashtagTimelineViewModel+Diffable.swift
@@ -20,6 +20,7 @@ extension HashtagTimelineViewModel {
tableView: tableView,
context: context,
configuration: StatusSection.Configuration(
+ context: context,
authContext: authContext,
statusTableViewCellDelegate: statusTableViewCellDelegate,
timelineMiddleLoaderTableViewCellDelegate: nil,
diff --git a/Mastodon/Scene/HashtagTimeline/HashtagTimelineViewModel+State.swift b/Mastodon/Scene/HashtagTimeline/HashtagTimelineViewModel+State.swift
index deb9de0a2..c35715f1e 100644
--- a/Mastodon/Scene/HashtagTimeline/HashtagTimelineViewModel+State.swift
+++ b/Mastodon/Scene/HashtagTimeline/HashtagTimelineViewModel+State.swift
@@ -70,7 +70,7 @@ extension HashtagTimelineViewModel.State {
override func didEnter(from previousState: GKState?) {
super.didEnter(from: previousState)
- guard let viewModel = viewModel, let stateMachine = stateMachine else { return }
+ guard let stateMachine = stateMachine else { return }
stateMachine.enter(Loading.self)
}
@@ -127,7 +127,7 @@ extension HashtagTimelineViewModel.State {
override func didEnter(from previousState: GKState?) {
super.didEnter(from: previousState)
- guard let viewModel = viewModel, let stateMachine = stateMachine else { return }
+ guard let viewModel else { return }
switch previousState {
case is Reloading:
diff --git a/Mastodon/Scene/HashtagTimeline/HashtagTimelineViewModel.swift b/Mastodon/Scene/HashtagTimeline/HashtagTimelineViewModel.swift
index af4d2a01a..888bc720c 100644
--- a/Mastodon/Scene/HashtagTimeline/HashtagTimelineViewModel.swift
+++ b/Mastodon/Scene/HashtagTimeline/HashtagTimelineViewModel.swift
@@ -23,7 +23,7 @@ final class HashtagTimelineViewModel {
var disposeBag = Set()
var needLoadMiddleIndex: Int? = nil
-
+
// input
let context: AppContext
let authContext: AuthContext
@@ -32,10 +32,11 @@ final class HashtagTimelineViewModel {
let timelinePredicate = CurrentValueSubject(nil)
let hashtagEntity = CurrentValueSubject(nil)
let listBatchFetchViewModel = ListBatchFetchViewModel()
-
+
// output
var diffableDataSource: UITableViewDiffableDataSource?
let didLoadLatest = PassthroughSubject()
+ let hashtagDetails = CurrentValueSubject(nil)
// bottom loader
private(set) lazy var stateMachine: GKStateMachine = {
@@ -61,6 +62,7 @@ final class HashtagTimelineViewModel {
domain: authContext.mastodonAuthenticationBox.domain,
additionalTweetPredicate: nil
)
+ updateTagInformation()
// end init
}
@@ -68,5 +70,55 @@ final class HashtagTimelineViewModel {
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s:", ((#file as NSString).lastPathComponent), #line, #function)
}
+ func viewWillAppear() {
+ let predicate = Tag.predicate(
+ domain: authContext.mastodonAuthenticationBox.domain,
+ name: hashtag
+ )
+
+ guard
+ let object = Tag.findOrFetch(in: context.managedObjectContext, matching: predicate)
+ else {
+ return hashtagDetails.send(hashtagDetails.value?.copy(following: false))
+ }
+
+ hashtagDetails.send(hashtagDetails.value?.copy(following: object.following))
+ }
}
+extension HashtagTimelineViewModel {
+ func followTag() {
+ self.hashtagDetails.send(hashtagDetails.value?.copy(following: true))
+ Task { @MainActor in
+ let tag = try? await context.apiService.followTag(
+ for: hashtag,
+ authenticationBox: authContext.mastodonAuthenticationBox
+ ).value
+ self.hashtagDetails.send(tag)
+ }
+ }
+
+ func unfollowTag() {
+ self.hashtagDetails.send(hashtagDetails.value?.copy(following: false))
+ Task { @MainActor in
+ let tag = try? await context.apiService.unfollowTag(
+ for: hashtag,
+ authenticationBox: authContext.mastodonAuthenticationBox
+ ).value
+ self.hashtagDetails.send(tag)
+ }
+ }
+}
+
+private extension HashtagTimelineViewModel {
+ func updateTagInformation() {
+ Task { @MainActor in
+ let tag = try? await context.apiService.getTagInformation(
+ for: hashtag,
+ authenticationBox: authContext.mastodonAuthenticationBox
+ ).value
+
+ self.hashtagDetails.send(tag)
+ }
+ }
+}
diff --git a/Mastodon/Scene/HomeTimeline/HomeTimelineViewController+DebugAction.swift b/Mastodon/Scene/HomeTimeline/HomeTimelineViewController+DebugAction.swift
index d057c0376..8ad798165 100644
--- a/Mastodon/Scene/HomeTimeline/HomeTimelineViewController+DebugAction.swift
+++ b/Mastodon/Scene/HomeTimeline/HomeTimelineViewController+DebugAction.swift
@@ -122,6 +122,10 @@ extension HomeTimelineViewController {
identifier: nil,
options: [],
children: [
+ UIAction(title: "Toggle Visible Touches", image: UIImage(systemName: "hand.tap"), attributes: []) { _ in
+ guard let window = UIApplication.shared.getKeyWindow() as? TouchesVisibleWindow else { return }
+ window.touchesVisible = !window.touchesVisible
+ },
UIAction(title: "Toggle EmptyView", image: UIImage(systemName: "clear"), attributes: []) { [weak self] action in
guard let self = self else { return }
if self.emptyView.superview != nil {
@@ -300,7 +304,7 @@ extension HomeTimelineViewController {
}
@objc private func showWelcomeAction(_ sender: UIAction) {
- coordinator.present(scene: .welcome, from: self, transition: .modal(animated: true, completion: nil))
+ _ = coordinator.present(scene: .welcome, from: self, transition: .modal(animated: true, completion: nil))
}
@objc private func showRegisterAction(_ sender: UIAction) {
@@ -332,12 +336,12 @@ extension HomeTimelineViewController {
@objc private func showConfirmEmail(_ sender: UIAction) {
let mastodonConfirmEmailViewModel = MastodonConfirmEmailViewModel()
- coordinator.present(scene: .mastodonConfirmEmail(viewModel: mastodonConfirmEmailViewModel), from: nil, transition: .modal(animated: true, completion: nil))
+ _ = coordinator.present(scene: .mastodonConfirmEmail(viewModel: mastodonConfirmEmailViewModel), from: nil, transition: .modal(animated: true, completion: nil))
}
@objc private func showAccountList(_ sender: UIAction) {
let accountListViewModel = AccountListViewModel(context: context, authContext: viewModel.authContext)
- coordinator.present(scene: .accountList(viewModel: accountListViewModel), from: self, transition: .modal(animated: true, completion: nil))
+ _ = coordinator.present(scene: .accountList(viewModel: accountListViewModel), from: self, transition: .modal(animated: true, completion: nil))
}
@objc private func showProfileAction(_ sender: UIAction) {
@@ -347,12 +351,12 @@ extension HomeTimelineViewController {
guard let self = self else { return }
guard let textField = alertController?.textFields?.first else { return }
let profileViewModel = RemoteProfileViewModel(context: self.context, authContext: self.viewModel.authContext, userID: textField.text ?? "")
- self.coordinator.present(scene: .profile(viewModel: profileViewModel), from: self, transition: .show)
+ _ = self.coordinator.present(scene: .profile(viewModel: profileViewModel), from: self, transition: .show)
}
alertController.addAction(showAction)
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
alertController.addAction(cancelAction)
- coordinator.present(scene: .alertController(alertController: alertController), from: self, transition: .alertController(animated: true, completion: nil))
+ _ = coordinator.present(scene: .alertController(alertController: alertController), from: self, transition: .alertController(animated: true, completion: nil))
}
@objc private func showThreadAction(_ sender: UIAction) {
@@ -362,12 +366,12 @@ extension HomeTimelineViewController {
guard let self = self else { return }
guard let textField = alertController?.textFields?.first else { return }
let threadViewModel = RemoteThreadViewModel(context: self.context, authContext: self.viewModel.authContext, statusID: textField.text ?? "")
- self.coordinator.present(scene: .thread(viewModel: threadViewModel), from: self, transition: .show)
+ _ = self.coordinator.present(scene: .thread(viewModel: threadViewModel), from: self, transition: .show)
}
alertController.addAction(showAction)
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
alertController.addAction(cancelAction)
- coordinator.present(scene: .alertController(alertController: alertController), from: self, transition: .alertController(animated: true, completion: nil))
+ _ = coordinator.present(scene: .alertController(alertController: alertController), from: self, transition: .alertController(animated: true, completion: nil))
}
private func showNotification(_ sender: UIAction, notificationType: Mastodon.Entity.Notification.NotificationType) {
@@ -436,7 +440,7 @@ extension HomeTimelineViewController {
authContext: viewModel.authContext,
setting: currentSetting
)
- coordinator.present(
+ _ = coordinator.present(
scene: .settings(viewModel: settingsViewModel),
from: self,
transition: .modal(animated: true, completion: nil)
diff --git a/Mastodon/Scene/HomeTimeline/HomeTimelineViewController.swift b/Mastodon/Scene/HomeTimeline/HomeTimelineViewController.swift
index e83101796..3caafef3d 100644
--- a/Mastodon/Scene/HomeTimeline/HomeTimelineViewController.swift
+++ b/Mastodon/Scene/HomeTimeline/HomeTimelineViewController.swift
@@ -158,12 +158,7 @@ extension HomeTimelineViewController {
tableView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(tableView)
- NSLayoutConstraint.activate([
- tableView.topAnchor.constraint(equalTo: view.topAnchor),
- tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
- tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
- tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
- ])
+ tableView.pinToParent()
// // layout publish progress
publishProgressView.translatesAutoresizingMaskIntoConstraints = false
@@ -388,14 +383,14 @@ extension HomeTimelineViewController {
@objc private func manuallySearchButtonPressed(_ sender: UIButton) {
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
let searchDetailViewModel = SearchDetailViewModel(authContext: viewModel.authContext)
- coordinator.present(scene: .searchDetail(viewModel: searchDetailViewModel), from: self, transition: .modal(animated: true, completion: nil))
+ _ = coordinator.present(scene: .searchDetail(viewModel: searchDetailViewModel), from: self, transition: .modal(animated: true, completion: nil))
}
@objc private func settingBarButtonItemPressed(_ sender: UIBarButtonItem) {
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
guard let setting = context.settingService.currentSetting.value else { return }
let settingsViewModel = SettingsViewModel(context: context, authContext: viewModel.authContext, setting: setting)
- coordinator.present(scene: .settings(viewModel: settingsViewModel), from: self, transition: .modal(animated: true, completion: nil))
+ _ = coordinator.present(scene: .settings(viewModel: settingsViewModel), from: self, transition: .modal(animated: true, completion: nil))
}
@objc private func refreshControlValueChanged(_ sender: RefreshControl) {
diff --git a/Mastodon/Scene/HomeTimeline/HomeTimelineViewModel+Diffable.swift b/Mastodon/Scene/HomeTimeline/HomeTimelineViewModel+Diffable.swift
index cabc655c9..3d59b3403 100644
--- a/Mastodon/Scene/HomeTimeline/HomeTimelineViewModel+Diffable.swift
+++ b/Mastodon/Scene/HomeTimeline/HomeTimelineViewModel+Diffable.swift
@@ -22,6 +22,7 @@ extension HomeTimelineViewModel {
tableView: tableView,
context: context,
configuration: StatusSection.Configuration(
+ context: context,
authContext: authContext,
statusTableViewCellDelegate: statusTableViewCellDelegate,
timelineMiddleLoaderTableViewCellDelegate: timelineMiddleLoaderTableViewCellDelegate,
@@ -41,7 +42,7 @@ extension HomeTimelineViewModel {
guard let self = self else { return }
guard let diffableDataSource = self.diffableDataSource else { return }
self.logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): incoming \(records.count) objects")
- Task {
+ Task { @MainActor in
let start = CACurrentMediaTime()
defer {
let end = CACurrentMediaTime()
@@ -98,22 +99,22 @@ extension HomeTimelineViewModel {
self.logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): snapshot has changes")
}
- guard let difference = await self.calculateReloadSnapshotDifference(
+ guard let difference = self.calculateReloadSnapshotDifference(
tableView: tableView,
oldSnapshot: oldSnapshot,
newSnapshot: newSnapshot
) else {
- await self.updateSnapshotUsingReloadData(snapshot: newSnapshot)
+ self.updateSnapshotUsingReloadData(snapshot: newSnapshot)
self.didLoadLatest.send()
self.logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): applied new snapshot")
return
}
- await self.updateSnapshotUsingReloadData(snapshot: newSnapshot)
- await tableView.scrollToRow(at: difference.targetIndexPath, at: .top, animated: false)
- var contentOffset = await tableView.contentOffset
- contentOffset.y = await tableView.contentOffset.y - difference.sourceDistanceToTableViewTopEdge
- await tableView.setContentOffset(contentOffset, animated: false)
+ self.updateSnapshotUsingReloadData(snapshot: newSnapshot)
+ tableView.scrollToRow(at: difference.targetIndexPath, at: .top, animated: false)
+ var contentOffset = tableView.contentOffset
+ contentOffset.y = tableView.contentOffset.y - difference.sourceDistanceToTableViewTopEdge
+ tableView.setContentOffset(contentOffset, animated: false)
self.didLoadLatest.send()
self.logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): applied new snapshot")
} // end Task
@@ -130,17 +131,13 @@ extension HomeTimelineViewModel {
snapshot: NSDiffableDataSourceSnapshot,
animatingDifferences: Bool
) async {
- diffableDataSource?.apply(snapshot, animatingDifferences: animatingDifferences)
+ await diffableDataSource?.apply(snapshot, animatingDifferences: animatingDifferences)
}
@MainActor func updateSnapshotUsingReloadData(
snapshot: NSDiffableDataSourceSnapshot
- ) async {
- if #available(iOS 15.0, *) {
- await self.diffableDataSource?.applySnapshotUsingReloadData(snapshot)
- } else {
- diffableDataSource?.applySnapshot(snapshot, animated: false, completion: nil)
- }
+ ) {
+ self.diffableDataSource?.applySnapshotUsingReloadData(snapshot)
}
struct Difference {
diff --git a/Mastodon/Scene/HomeTimeline/HomeTimelineViewModel+LoadLatestState.swift b/Mastodon/Scene/HomeTimeline/HomeTimelineViewModel+LoadLatestState.swift
index 41cea6b58..d243c9a96 100644
--- a/Mastodon/Scene/HomeTimeline/HomeTimelineViewModel+LoadLatestState.swift
+++ b/Mastodon/Scene/HomeTimeline/HomeTimelineViewModel+LoadLatestState.swift
@@ -62,7 +62,7 @@ extension HomeTimelineViewModel.LoadLatestState {
override func didEnter(from previousState: GKState?) {
super.didEnter(from: previousState)
- guard let viewModel = viewModel, let stateMachine = stateMachine else { return }
+ guard let viewModel else { return }
let latestFeedRecords = viewModel.fetchedResultsController.records.prefix(APIService.onceRequestStatusMaxCount)
let parentManagedObjectContext = viewModel.fetchedResultsController.fetchedResultsController.managedObjectContext
@@ -86,6 +86,8 @@ extension HomeTimelineViewModel.LoadLatestState {
await enter(state: Idle.self)
viewModel.homeTimelineNavigationBarTitleViewModel.receiveLoadingStateCompletion(.finished)
+ viewModel.context.instanceService.updateMutesAndBlocks()
+
// stop refresher if no new statuses
let statuses = response.value
let newStatuses = statuses.filter { !latestStatusIDs.contains($0.id) }
diff --git a/Mastodon/Scene/HomeTimeline/HomeTimelineViewModel.swift b/Mastodon/Scene/HomeTimeline/HomeTimelineViewModel.swift
index edd431e52..c7c967305 100644
--- a/Mastodon/Scene/HomeTimeline/HomeTimelineViewModel.swift
+++ b/Mastodon/Scene/HomeTimeline/HomeTimelineViewModel.swift
@@ -14,7 +14,6 @@ import CoreData
import CoreDataStack
import GameplayKit
import AlamofireImage
-import DateToolsSwift
import MastodonCore
import MastodonUI
@@ -149,12 +148,7 @@ extension HomeTimelineViewModel {
}
// reconfigure item
- if #available(iOS 15.0, *) {
- snapshot.reconfigureItems([item])
- } else {
- // Fallback on earlier versions
- snapshot.reloadItems([item])
- }
+ snapshot.reconfigureItems([item])
await updateSnapshotUsingReloadData(snapshot: snapshot)
// fetch data
@@ -177,15 +171,10 @@ extension HomeTimelineViewModel {
}
// reconfigure item again
- if #available(iOS 15.0, *) {
- snapshot.reconfigureItems([item])
- } else {
- // Fallback on earlier versions
- snapshot.reloadItems([item])
- }
+ snapshot.reconfigureItems([item])
await updateSnapshotUsingReloadData(snapshot: snapshot)
}
-
+
}
// MARK: - SuggestionAccountViewModelDelegate
diff --git a/Mastodon/Scene/HomeTimeline/View/HomeTimelineNavigationBarTitleView.swift b/Mastodon/Scene/HomeTimeline/View/HomeTimelineNavigationBarTitleView.swift
index 400b4ee98..7e51057bc 100644
--- a/Mastodon/Scene/HomeTimeline/View/HomeTimelineNavigationBarTitleView.swift
+++ b/Mastodon/Scene/HomeTimeline/View/HomeTimelineNavigationBarTitleView.swift
@@ -47,12 +47,7 @@ extension HomeTimelineNavigationBarTitleView {
private func _init() {
containerView.translatesAutoresizingMaskIntoConstraints = false
addSubview(containerView)
- NSLayoutConstraint.activate([
- containerView.topAnchor.constraint(equalTo: topAnchor),
- containerView.leadingAnchor.constraint(equalTo: leadingAnchor),
- containerView.trailingAnchor.constraint(equalTo: trailingAnchor),
- containerView.bottomAnchor.constraint(equalTo: bottomAnchor),
- ])
+ containerView.pinToParent()
containerView.addArrangedSubview(logoButton)
button.translatesAutoresizingMaskIntoConstraints = false
@@ -108,8 +103,8 @@ extension HomeTimelineNavigationBarTitleView {
logoButton.setImage(Asset.Asset.mastodonTextLogo.image.withRenderingMode(.alwaysTemplate), for: .normal)
logoButton.contentMode = .center
logoButton.isHidden = false
- logoButton.accessibilityLabel = "Logo Button" // TODO :i18n
- logoButton.accessibilityHint = "Tap to scroll to top and tap again to previous location"
+ logoButton.accessibilityLabel = L10n.Scene.HomeTimeline.NavigationBarState.Accessibility.logoLabel // TODO :i18n
+ logoButton.accessibilityHint = L10n.Scene.HomeTimeline.NavigationBarState.Accessibility.logoHint
case .newPostButton:
configureButton(
title: L10n.Scene.HomeTimeline.NavigationBarState.newPosts,
diff --git a/Mastodon/Scene/MediaPreview/AltViewController.swift b/Mastodon/Scene/MediaPreview/AltViewController.swift
new file mode 100644
index 000000000..05a3e5b09
--- /dev/null
+++ b/Mastodon/Scene/MediaPreview/AltViewController.swift
@@ -0,0 +1,92 @@
+//
+// AltViewController.swift
+// Mastodon
+//
+// Created by Jed Fox on 2022-11-26.
+//
+
+import SwiftUI
+
+class AltViewController: UIViewController {
+ let textView = {
+ let textView: UITextView
+
+ if #available(iOS 16, *) {
+ // TODO: update code below to use TextKit 2 when dropping iOS 15 support
+ textView = UITextView(usingTextLayoutManager: false)
+ } else {
+ textView = UITextView()
+ }
+
+ textView.textContainer.maximumNumberOfLines = 0
+ textView.textContainer.lineBreakMode = .byWordWrapping
+ textView.font = .preferredFont(forTextStyle: .callout)
+ textView.isScrollEnabled = true
+ textView.backgroundColor = .clear
+ textView.isOpaque = false
+ textView.isEditable = false
+ textView.tintColor = .white
+ textView.textContainerInset = UIEdgeInsets(top: 12, left: 8, bottom: 8, right: 8)
+ textView.contentInsetAdjustmentBehavior = .always
+ textView.verticalScrollIndicatorInsets.bottom = 4
+
+ return textView
+ }()
+
+ init(alt: String, sourceView: UIView?) {
+ textView.text = alt
+ super.init(nibName: nil, bundle: nil)
+ self.modalPresentationStyle = .popover
+ self.popoverPresentationController?.delegate = self
+ self.popoverPresentationController?.permittedArrowDirections = .up
+ self.popoverPresentationController?.sourceView = sourceView
+ self.overrideUserInterfaceStyle = .dark
+ }
+
+ @MainActor required dynamic init?(coder aDecoder: NSCoder) {
+ fatalError("init(coder:) has not been implemented")
+ }
+
+ override func loadView() {
+ super.loadView()
+ view.translatesAutoresizingMaskIntoConstraints = false
+ }
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+
+ textView.translatesAutoresizingMaskIntoConstraints = false
+ view.backgroundColor = .systemBackground
+ view.addSubview(textView)
+
+ textView.pinToParent()
+ NSLayoutConstraint.activate([
+ textView.widthAnchor.constraint(lessThanOrEqualToConstant: 400),
+ ])
+ }
+
+ override func viewDidLayoutSubviews() {
+ super.viewDidLayoutSubviews()
+ UIView.performWithoutAnimation {
+
+ let size = textView.layoutManager.boundingRect(forGlyphRange: NSMakeRange(0, (textView.textStorage.string as NSString).length), in: textView.textContainer).size
+
+ preferredContentSize = CGSize(
+ width: size.width + (8 + textView.textContainer.lineFragmentPadding) * 2,
+ height: size.height + 12 + (textView.textContainer.lineFragmentPadding) * 2
+ )
+ }
+ }
+
+ override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
+ super.traitCollectionDidChange(previousTraitCollection)
+ textView.font = .preferredFont(forTextStyle: .callout)
+ }
+}
+
+// MARK: UIPopoverPresentationControllerDelegate
+extension AltViewController: UIPopoverPresentationControllerDelegate {
+ func adaptivePresentationStyle(for controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle {
+ .none
+ }
+}
diff --git a/Mastodon/Scene/MediaPreview/Image/MediaPreviewImageView.swift b/Mastodon/Scene/MediaPreview/Image/MediaPreviewImageView.swift
index 05f2ce70f..7df8861b0 100644
--- a/Mastodon/Scene/MediaPreview/Image/MediaPreviewImageView.swift
+++ b/Mastodon/Scene/MediaPreview/Image/MediaPreviewImageView.swift
@@ -9,6 +9,7 @@ import os.log
import func AVFoundation.AVMakeRect
import UIKit
import FLAnimatedImage
+import VisionKit
final class MediaPreviewImageView: UIScrollView {
@@ -28,9 +29,21 @@ final class MediaPreviewImageView: UIScrollView {
tapGestureRecognizer.numberOfTapsRequired = 2
return tapGestureRecognizer
}()
-
+
private var containerFrame: CGRect?
-
+
+ private var _interaction: UIInteraction? = {
+ if #available(iOS 16.0, *) {
+ return ImageAnalysisInteraction()
+ } else {
+ return nil
+ }
+ }()
+ @available(iOS 16.0, *)
+ var liveTextInteraction: ImageAnalysisInteraction {
+ _interaction as! ImageAnalysisInteraction
+ }
+
override init(frame: CGRect) {
super.init(frame: frame)
_init()
@@ -55,10 +68,13 @@ extension MediaPreviewImageView {
maximumZoomScale = 4.0
addSubview(imageView)
-
+
doubleTapGestureRecognizer.addTarget(self, action: #selector(MediaPreviewImageView.doubleTapGestureRecognizerHandler(_:)))
imageView.addGestureRecognizer(doubleTapGestureRecognizer)
-
+ if #available(iOS 16.0, *) {
+ imageView.addInteraction(liveTextInteraction)
+ }
+
delegate = self
}
@@ -112,23 +128,30 @@ extension MediaPreviewImageView {
// reset to normal
zoomScale = minimumZoomScale
- let imageViewSize = AVMakeRect(aspectRatio: image.size, insideRect: container.bounds).size
- let imageContentInset: UIEdgeInsets = {
- if imageViewSize.width == container.bounds.width {
- return UIEdgeInsets(top: 0.5 * (container.bounds.height - imageViewSize.height), left: 0, bottom: 0, right: 0)
- } else {
- return UIEdgeInsets(top: 0, left: 0.5 * (container.bounds.width - imageViewSize.width), bottom: 0, right: 0)
- }
- }()
+ let imageViewSize = AVMakeRect(aspectRatio: image.size, insideRect: container.bounds.inset(by: container.safeAreaInsets)).size
imageView.frame = CGRect(origin: .zero, size: imageViewSize)
if imageView.image == nil {
imageView.image = image
}
contentSize = imageViewSize
- contentInset = imageContentInset
centerScrollViewContents()
- contentOffset = CGPoint(x: -contentInset.left, y: -contentInset.top)
+
+ if #available(iOS 16.0, *) {
+ Task.detached(priority: .userInitiated) {
+ do {
+ let analysis = try await ImageAnalyzer.shared.analyze(image, configuration: ImageAnalyzer.Configuration([.text, .machineReadableCode]))
+ await MainActor.run {
+ self.liveTextInteraction.analysis = analysis
+ self.liveTextInteraction.preferredInteractionTypes = .automatic
+ }
+ } catch {
+ await MainActor.run {
+ self.liveTextInteraction.preferredInteractionTypes = []
+ }
+ }
+ }
+ }
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: setup image for container %s", ((#file as NSString).lastPathComponent), #line, #function, container.frame.debugDescription)
}
@@ -192,10 +215,7 @@ extension MediaPreviewImageView {
frame.size = realImageSize
imageView.frame = frame
- let screenSize = self.frame.size
- let offsetX = screenSize.width > realImageSize.width ? (screenSize.width - realImageSize.width) / 2 : 0
- let offsetY = screenSize.height > realImageSize.height ? (screenSize.height - realImageSize.height) / 2 : 0
- contentInset = UIEdgeInsets(top: offsetY, left: offsetX, bottom: offsetY, right: offsetX)
+ contentInset = self.safeAreaInsets
// The scroll view has zoomed, so you need to re-center the contents
let scrollViewSize = scrollViewVisibleSize
diff --git a/Mastodon/Scene/MediaPreview/Image/MediaPreviewImageViewController.swift b/Mastodon/Scene/MediaPreview/Image/MediaPreviewImageViewController.swift
index 127c4c0c0..68bc0219f 100644
--- a/Mastodon/Scene/MediaPreview/Image/MediaPreviewImageViewController.swift
+++ b/Mastodon/Scene/MediaPreview/Image/MediaPreviewImageViewController.swift
@@ -11,6 +11,7 @@ import Combine
import MastodonAsset
import MastodonLocalization
import FLAnimatedImage
+import VisionKit
protocol MediaPreviewImageViewControllerDelegate: AnyObject {
func mediaPreviewImageViewController(_ viewController: MediaPreviewImageViewController, tapGestureRecognizerDidTrigger tapGestureRecognizer: UITapGestureRecognizer)
@@ -31,7 +32,7 @@ final class MediaPreviewImageViewController: UIViewController {
let tapGestureRecognizer = UITapGestureRecognizer.singleTapGestureRecognizer
let longPressGestureRecognizer = UILongPressGestureRecognizer()
-
+
deinit {
os_log("%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
previewImageView.imageView.af.cancelImageRequest()
@@ -42,7 +43,10 @@ extension MediaPreviewImageViewController {
override func viewDidLoad() {
super.viewDidLoad()
-
+
+ if #available(iOS 16.0, *) {
+ previewImageView.liveTextInteraction.delegate = self
+ }
previewImageView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(previewImageView)
NSLayoutConstraint.activate([
@@ -53,7 +57,9 @@ extension MediaPreviewImageViewController {
])
tapGestureRecognizer.addTarget(self, action: #selector(MediaPreviewImageViewController.tapGestureRecognizerHandler(_:)))
+ tapGestureRecognizer.delegate = self
longPressGestureRecognizer.addTarget(self, action: #selector(MediaPreviewImageViewController.longPressGestureRecognizerHandler(_:)))
+ longPressGestureRecognizer.delegate = self
tapGestureRecognizer.require(toFail: previewImageView.doubleTapGestureRecognizer)
tapGestureRecognizer.require(toFail: longPressGestureRecognizer)
previewImageView.addGestureRecognizer(tapGestureRecognizer)
@@ -62,30 +68,21 @@ extension MediaPreviewImageViewController {
let previewImageViewContextMenuInteraction = UIContextMenuInteraction(delegate: self)
previewImageView.addInteraction(previewImageViewContextMenuInteraction)
- switch viewModel.item {
- case .remote(let imageContext):
- previewImageView.imageView.accessibilityLabel = imageContext.altText
-
- if let thumbnail = imageContext.thumbnail {
- previewImageView.imageView.image = thumbnail
- previewImageView.setup(image: thumbnail, container: self.previewImageView, forceUpdate: true)
- }
-
- previewImageView.imageView.setImage(
- url: imageContext.assetURL,
- placeholder: imageContext.thumbnail,
- scaleToSize: nil
- ) { [weak self] image in
- guard let self = self else { return }
- guard let image = image else { return }
- self.previewImageView.setup(image: image, container: self.previewImageView, forceUpdate: true)
- }
-
- case .local(let imageContext):
- let image = imageContext.image
- previewImageView.imageView.image = image
- previewImageView.setup(image: image, container: previewImageView, forceUpdate: true)
-
+ previewImageView.imageView.accessibilityLabel = viewModel.item.altText
+
+ if let thumbnail = viewModel.item.thumbnail {
+ previewImageView.imageView.image = thumbnail
+ previewImageView.setup(image: thumbnail, container: self.previewImageView, forceUpdate: true)
+ }
+
+ previewImageView.imageView.setImage(
+ url: viewModel.item.assetURL,
+ placeholder: viewModel.item.thumbnail,
+ scaleToSize: nil
+ ) { [weak self] image in
+ guard let self = self else { return }
+ guard let image = image else { return }
+ self.previewImageView.setup(image: image, container: self.previewImageView, forceUpdate: true)
}
}
@@ -105,10 +102,54 @@ extension MediaPreviewImageViewController {
}
+extension MediaPreviewImageViewController: MediaPreviewPage {
+ func setShowingChrome(_ showingChrome: Bool) {
+ if #available(iOS 16.0, *) {
+ UIView.animate(withDuration: 0.3) {
+ self.previewImageView.liveTextInteraction.setSupplementaryInterfaceHidden(!showingChrome, animated: true)
+ }
+ }
+ }
+}
+
+// MARK: - ImageAnalysisInteractionDelegate
+@available(iOS 16.0, *)
+extension MediaPreviewImageViewController: ImageAnalysisInteractionDelegate {
+ func presentingViewController(for interaction: ImageAnalysisInteraction) -> UIViewController? {
+ self
+ }
+}
+
+// MARK: - UIGestureRecognizerDelegate
+extension MediaPreviewImageViewController: UIGestureRecognizerDelegate {
+ func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
+ if #available(iOS 16.0, *) {
+ let location = touch.location(in: previewImageView.imageView)
+ // for tap gestures, only items that can be tapped are relevant
+ if gestureRecognizer is UITapGestureRecognizer {
+ return !previewImageView.liveTextInteraction.hasSupplementaryInterface(at: location)
+ && !previewImageView.liveTextInteraction.hasDataDetector(at: location)
+ } else {
+ // for long press, block out everything
+ return !previewImageView.liveTextInteraction.hasInteractiveItem(at: location)
+ }
+ } else {
+ return true
+ }
+ }
+}
+
// MARK: - UIContextMenuInteractionDelegate
extension MediaPreviewImageViewController: UIContextMenuInteractionDelegate {
func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration? {
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
+
+ if #available(iOS 16.0, *) {
+ if previewImageView.liveTextInteraction.hasInteractiveItem(at: previewImageView.imageView.convert(location, from: previewImageView)) {
+ return nil
+ }
+ }
+
let previewProvider: UIContextMenuContentPreviewProvider = { () -> UIViewController? in
return nil
diff --git a/Mastodon/Scene/MediaPreview/Image/MediaPreviewImageViewModel.swift b/Mastodon/Scene/MediaPreview/Image/MediaPreviewImageViewModel.swift
index e82118f78..3a4d9edd2 100644
--- a/Mastodon/Scene/MediaPreview/Image/MediaPreviewImageViewModel.swift
+++ b/Mastodon/Scene/MediaPreview/Image/MediaPreviewImageViewModel.swift
@@ -30,19 +30,10 @@ class MediaPreviewImageViewModel {
extension MediaPreviewImageViewModel {
- public enum ImagePreviewItem {
- case remote(RemoteImageContext)
- case local(LocalImageContext)
- }
-
- public struct RemoteImageContext {
+ public struct ImagePreviewItem {
let assetURL: URL?
let thumbnail: UIImage?
let altText: String?
}
-
- public struct LocalImageContext {
- let image: UIImage
- }
}
diff --git a/Mastodon/Scene/MediaPreview/MediaPreviewViewController.swift b/Mastodon/Scene/MediaPreview/MediaPreviewViewController.swift
index c6552bcba..ede0be296 100644
--- a/Mastodon/Scene/MediaPreview/MediaPreviewViewController.swift
+++ b/Mastodon/Scene/MediaPreview/MediaPreviewViewController.swift
@@ -16,8 +16,6 @@ import MastodonLocalization
final class MediaPreviewViewController: UIViewController, NeedsDependency {
- static let closeButtonSize = CGSize(width: 30, height: 30)
-
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
@@ -26,24 +24,23 @@ final class MediaPreviewViewController: UIViewController, NeedsDependency {
let visualEffectView = UIVisualEffectView(effect: UIBlurEffect(style: .systemMaterial))
let pagingViewController = MediaPreviewPagingViewController()
-
- let closeButtonBackground: UIVisualEffectView = {
- let backgroundView = UIVisualEffectView(effect: UIBlurEffect(style: .systemUltraThinMaterial))
- backgroundView.alpha = 0.9
- backgroundView.layer.masksToBounds = true
- backgroundView.layer.cornerRadius = MediaPreviewViewController.closeButtonSize.width * 0.5
- return backgroundView
+
+ let topToolbar: UIStackView = {
+ let stackView = TouchTransparentStackView()
+ stackView.axis = .horizontal
+ stackView.distribution = .equalSpacing
+ stackView.alignment = .fill
+ stackView.translatesAutoresizingMaskIntoConstraints = false
+ return stackView
}()
-
- let closeButtonBackgroundVisualEffectView = UIVisualEffectView(effect: UIVibrancyEffect(blurEffect: UIBlurEffect(style: .systemUltraThinMaterial)))
-
- let closeButton: UIButton = {
- let button = HighlightDimmableButton()
- button.expandEdgeInsets = UIEdgeInsets(top: -10, left: -10, bottom: -10, right: -10)
- button.imageView?.tintColor = .label
+
+ let closeButton = HUDButton { button in
button.setImage(UIImage(systemName: "xmark", withConfiguration: UIImage.SymbolConfiguration(pointSize: 16, weight: .bold))!, for: .normal)
- return button
- }()
+ }
+
+ let altButton = HUDButton { button in
+ button.setTitle("ALT", for: .normal)
+ }
deinit {
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
@@ -65,42 +62,32 @@ extension MediaPreviewViewController {
pagingViewController.view.translatesAutoresizingMaskIntoConstraints = false
addChild(pagingViewController)
visualEffectView.contentView.addSubview(pagingViewController.view)
- NSLayoutConstraint.activate([
- visualEffectView.topAnchor.constraint(equalTo: pagingViewController.view.topAnchor),
- visualEffectView.bottomAnchor.constraint(equalTo: pagingViewController.view.bottomAnchor),
- visualEffectView.leadingAnchor.constraint(equalTo: pagingViewController.view.leadingAnchor),
- visualEffectView.trailingAnchor.constraint(equalTo: pagingViewController.view.trailingAnchor),
- ])
+ visualEffectView.pinTo(to: pagingViewController.view)
pagingViewController.didMove(toParent: self)
-
- closeButtonBackground.translatesAutoresizingMaskIntoConstraints = false
- view.addSubview(closeButtonBackground)
- NSLayoutConstraint.activate([
- closeButtonBackground.topAnchor.constraint(equalTo: view.layoutMarginsGuide.topAnchor, constant: 12),
- closeButtonBackground.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor)
- ])
- closeButtonBackgroundVisualEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
- closeButtonBackground.contentView.addSubview(closeButtonBackgroundVisualEffectView)
- closeButton.translatesAutoresizingMaskIntoConstraints = false
- closeButtonBackgroundVisualEffectView.contentView.addSubview(closeButton)
+ view.addSubview(topToolbar)
NSLayoutConstraint.activate([
- closeButton.topAnchor.constraint(equalTo: closeButtonBackgroundVisualEffectView.topAnchor),
- closeButton.leadingAnchor.constraint(equalTo: closeButtonBackgroundVisualEffectView.leadingAnchor),
- closeButtonBackgroundVisualEffectView.trailingAnchor.constraint(equalTo: closeButton.trailingAnchor),
- closeButtonBackgroundVisualEffectView.bottomAnchor.constraint(equalTo: closeButton.bottomAnchor),
- closeButton.heightAnchor.constraint(equalToConstant: MediaPreviewViewController.closeButtonSize.height).priority(.defaultHigh),
- closeButton.widthAnchor.constraint(equalToConstant: MediaPreviewViewController.closeButtonSize.width).priority(.defaultHigh),
+ topToolbar.topAnchor.constraint(equalTo: view.layoutMarginsGuide.topAnchor, constant: 12),
+ topToolbar.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor),
+ topToolbar.trailingAnchor.constraint(equalTo: view.layoutMarginsGuide.trailingAnchor),
])
-
+
+ topToolbar.addArrangedSubview(closeButton)
+ NSLayoutConstraint.activate([
+ closeButton.widthAnchor.constraint(equalToConstant: HUDButton.height).priority(.defaultHigh),
+ ])
+
+ topToolbar.addArrangedSubview(altButton)
+
viewModel.mediaPreviewImageViewControllerDelegate = self
pagingViewController.interPageSpacing = 10
pagingViewController.delegate = self
pagingViewController.dataSource = viewModel
- closeButton.addTarget(self, action: #selector(MediaPreviewViewController.closeButtonPressed(_:)), for: .touchUpInside)
-
+ closeButton.button.addTarget(self, action: #selector(MediaPreviewViewController.closeButtonPressed(_:)), for: .touchUpInside)
+ altButton.button.addTarget(self, action: #selector(MediaPreviewViewController.altButtonPressed(_:)), for: .touchUpInside)
+
// bind view model
viewModel.$currentPage
.receive(on: DispatchQueue.main)
@@ -131,12 +118,37 @@ extension MediaPreviewViewController {
let attachment = previewContext.attachments[index]
return attachment.kind == .video // not hide buttno for audio
}()
- self.closeButtonBackground.isHidden = needsHideCloseButton
+ self.closeButton.isHidden = needsHideCloseButton
default:
break
}
}
.store(in: &disposeBag)
+
+ viewModel.$altText
+ .receive(on: DispatchQueue.main)
+ .sink { [weak self] altText in
+ guard let self else { return }
+ UIView.animate(withDuration: 0.3) {
+ if altText == nil {
+ self.altButton.alpha = 0
+ } else {
+ self.altButton.alpha = 1
+ }
+ }
+ }
+ .store(in: &disposeBag)
+
+ viewModel.$showingChrome
+ .receive(on: DispatchQueue.main)
+ .removeDuplicates()
+ .sink { [weak self] showingChrome in
+ UIView.animate(withDuration: 0.3) {
+ self?.setNeedsStatusBarAppearanceUpdate()
+ self?.topToolbar.alpha = showingChrome ? 1 : 0
+ }
+ }
+ .store(in: &disposeBag)
// viewModel.$isPoping
// .receive(on: DispatchQueue.main)
@@ -153,13 +165,25 @@ extension MediaPreviewViewController {
}
+extension MediaPreviewViewController {
+
+ override var prefersStatusBarHidden: Bool {
+ !viewModel.showingChrome
+ }
+
+}
+
extension MediaPreviewViewController {
@objc private func closeButtonPressed(_ sender: UIButton) {
- os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
dismiss(animated: true, completion: nil)
}
-
+
+ @objc private func altButtonPressed(_ sender: UIButton) {
+ guard let alt = viewModel.altText else { return }
+
+ present(AltViewController(alt: alt, sourceView: sender), animated: true)
+ }
}
// MARK: - MediaPreviewingViewController
@@ -239,8 +263,11 @@ extension MediaPreviewViewController: MediaPreviewImageViewControllerDelegate {
let location = tapGestureRecognizer.location(in: viewController.previewImageView.imageView)
let isContainsTap = viewController.previewImageView.imageView.frame.contains(location)
- guard !isContainsTap else { return }
- dismiss(animated: true, completion: nil)
+ if isContainsTap {
+ self.viewModel.showingChrome.toggle()
+ } else {
+ dismiss(animated: true, completion: nil)
+ }
}
func mediaPreviewImageViewController(_ viewController: MediaPreviewImageViewController, longPressGestureRecognizerDidTrigger longPressGestureRecognizer: UILongPressGestureRecognizer) {
@@ -253,19 +280,8 @@ extension MediaPreviewViewController: MediaPreviewImageViewControllerDelegate {
) {
switch action {
case .savePhoto:
- let _savePublisher: AnyPublisher? = {
- switch viewController.viewModel.item {
- case .remote(let previewContext):
- guard let assetURL = previewContext.assetURL else { return nil }
- return context.photoLibraryService.save(imageSource: .url(assetURL))
- case .local(let previewContext):
- return context.photoLibraryService.save(imageSource: .image(previewContext.image))
- }
- }()
- guard let savePublisher = _savePublisher else {
- return
- }
- savePublisher
+ guard let assetURL = viewController.viewModel.item.assetURL else { return }
+ context.photoLibraryService.save(imageSource: .url(assetURL))
.sink { [weak self] completion in
guard let self = self else { return }
switch completion {
@@ -276,7 +292,7 @@ extension MediaPreviewViewController: MediaPreviewImageViewControllerDelegate {
title: L10n.Common.Alerts.SavePhotoFailure.title,
message: L10n.Common.Alerts.SavePhotoFailure.message
)
- self.coordinator.present(
+ _ = self.coordinator.present(
scene: .alertController(alertController: alertController),
from: self,
transition: .alertController(animated: true, completion: nil)
@@ -289,20 +305,9 @@ extension MediaPreviewViewController: MediaPreviewImageViewControllerDelegate {
}
.store(in: &context.disposeBag)
case .copyPhoto:
- let _copyPublisher: AnyPublisher? = {
- switch viewController.viewModel.item {
- case .remote(let previewContext):
- guard let assetURL = previewContext.assetURL else { return nil }
- return context.photoLibraryService.copy(imageSource: .url(assetURL))
- case .local(let previewContext):
- return context.photoLibraryService.copy(imageSource: .image(previewContext.image))
- }
- }()
- guard let copyPublisher = _copyPublisher else {
- return
- }
+ guard let assetURL = viewController.viewModel.item.assetURL else { return }
- copyPublisher
+ context.photoLibraryService.copy(imageSource: .url(assetURL))
.sink { completion in
switch completion {
case .failure(let error):
@@ -321,13 +326,8 @@ extension MediaPreviewViewController: MediaPreviewImageViewControllerDelegate {
let activityViewController = UIActivityViewController(
activityItems: {
var activityItems: [Any] = []
- switch viewController.viewModel.item {
- case .remote(let previewContext):
- if let assetURL = previewContext.assetURL {
- activityItems.append(assetURL)
- }
- case .local(let previewContext):
- activityItems.append(previewContext.image)
+ if let assetURL = viewController.viewModel.item.assetURL {
+ activityItems.append(assetURL)
}
return activityItems
}(),
@@ -342,12 +342,12 @@ extension MediaPreviewViewController: MediaPreviewImageViewControllerDelegate {
extension MediaPreviewViewController {
- var closeKeyCommand: UIKeyCommand {
+ func closeKeyCommand(input: String) -> UIKeyCommand {
UIKeyCommand(
title: L10n.Scene.Preview.Keyboard.closePreview,
image: nil,
action: #selector(MediaPreviewViewController.closePreviewKeyCommandHandler(_:)),
- input: "i",
+ input: input,
modifierFlags: [],
propertyList: nil,
alternates: [],
@@ -391,7 +391,8 @@ extension MediaPreviewViewController {
override var keyCommands: [UIKeyCommand] {
return [
- closeKeyCommand,
+ closeKeyCommand(input: UIKeyCommand.inputEscape),
+ closeKeyCommand(input: "i"),
showNextKeyCommand,
showPreviousKeyCommand,
]
diff --git a/Mastodon/Scene/MediaPreview/MediaPreviewViewModel.swift b/Mastodon/Scene/MediaPreview/MediaPreviewViewModel.swift
index c43d24945..a6b604d6f 100644
--- a/Mastodon/Scene/MediaPreview/MediaPreviewViewModel.swift
+++ b/Mastodon/Scene/MediaPreview/MediaPreviewViewModel.swift
@@ -12,6 +12,10 @@ import CoreDataStack
import Pageboy
import MastodonCore
+protocol MediaPreviewPage: UIViewController {
+ func setShowingChrome(_ showingChrome: Bool)
+}
+
final class MediaPreviewViewModel: NSObject {
weak var mediaPreviewImageViewControllerDelegate: MediaPreviewImageViewControllerDelegate?
@@ -22,9 +26,13 @@ final class MediaPreviewViewModel: NSObject {
let transitionItem: MediaPreviewTransitionItem
@Published var currentPage: Int
-
+ @Published var showingChrome = true
+ @Published var altText: String?
+
// output
- let viewControllers: [UIViewController]
+ let viewControllers: [MediaPreviewPage]
+
+ private var disposeBag: Set = []
init(
context: AppContext,
@@ -34,9 +42,12 @@ final class MediaPreviewViewModel: NSObject {
self.context = context
self.item = item
var currentPage = 0
- var viewControllers: [UIViewController] = []
+ var viewControllers: [MediaPreviewPage] = []
+ var getAltText = { (page: Int) -> String? in nil }
switch item {
case .attachment(let previewContext):
+ getAltText = { previewContext.attachments[$0].altDescription }
+
currentPage = previewContext.initialIndex
for (i, attachment) in previewContext.attachments.enumerated() {
switch attachment.kind {
@@ -44,11 +55,11 @@ final class MediaPreviewViewModel: NSObject {
let viewController = MediaPreviewImageViewController()
let viewModel = MediaPreviewImageViewModel(
context: context,
- item: .remote(.init(
+ item: .init(
assetURL: attachment.assetURL.flatMap { URL(string: $0) },
thumbnail: previewContext.thumbnail(at: i),
altText: attachment.altDescription
- ))
+ )
)
viewController.viewModel = viewModel
viewControllers.append(viewController)
@@ -58,7 +69,8 @@ final class MediaPreviewViewModel: NSObject {
context: context,
item: .gif(.init(
assetURL: attachment.assetURL.flatMap { URL(string: $0) },
- previewURL: attachment.previewURL.flatMap { URL(string: $0) }
+ previewURL: attachment.previewURL.flatMap { URL(string: $0) },
+ altText: attachment.altDescription
))
)
viewController.viewModel = viewModel
@@ -69,7 +81,8 @@ final class MediaPreviewViewModel: NSObject {
context: context,
item: .video(.init(
assetURL: attachment.assetURL.flatMap { URL(string: $0) },
- previewURL: attachment.previewURL.flatMap { URL(string: $0) }
+ previewURL: attachment.previewURL.flatMap { URL(string: $0) },
+ altText: attachment.altDescription
))
)
viewController.viewModel = viewModel
@@ -80,11 +93,11 @@ final class MediaPreviewViewModel: NSObject {
let viewController = MediaPreviewImageViewController()
let viewModel = MediaPreviewImageViewModel(
context: context,
- item: .remote(.init(
+ item: .init(
assetURL: previewContext.assetURL.flatMap { URL(string: $0) },
thumbnail: previewContext.thumbnail,
altText: nil
- ))
+ )
)
viewController.viewModel = viewModel
viewControllers.append(viewController)
@@ -92,11 +105,11 @@ final class MediaPreviewViewModel: NSObject {
let viewController = MediaPreviewImageViewController()
let viewModel = MediaPreviewImageViewModel(
context: context,
- item: .remote(.init(
+ item: .init(
assetURL: previewContext.assetURL.flatMap { URL(string: $0) },
thumbnail: previewContext.thumbnail,
altText: nil
- ))
+ )
)
viewController.viewModel = viewModel
viewControllers.append(viewController)
@@ -106,6 +119,18 @@ final class MediaPreviewViewModel: NSObject {
self.currentPage = currentPage
self.transitionItem = transitionItem
super.init()
+
+ self.$currentPage
+ .map(getAltText)
+ .assign(to: &$altText)
+
+ for viewController in viewControllers {
+ self.$showingChrome
+ .sink { [weak viewController] showingChrome in
+ viewController?.setShowingChrome(showingChrome)
+ }
+ .store(in: &disposeBag)
+ }
}
}
diff --git a/Mastodon/Scene/MediaPreview/Video/MediaPreviewVideoViewController.swift b/Mastodon/Scene/MediaPreview/Video/MediaPreviewVideoViewController.swift
index 7bdbbfed2..e924f38d4 100644
--- a/Mastodon/Scene/MediaPreview/Video/MediaPreviewVideoViewController.swift
+++ b/Mastodon/Scene/MediaPreview/Video/MediaPreviewVideoViewController.swift
@@ -39,23 +39,13 @@ extension MediaPreviewVideoViewController {
addChild(playerViewController)
playerViewController.view.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(playerViewController.view)
- NSLayoutConstraint.activate([
- playerViewController.view.centerXAnchor.constraint(equalTo: view.centerXAnchor),
- playerViewController.view.centerYAnchor.constraint(equalTo: view.centerYAnchor),
- playerViewController.view.widthAnchor.constraint(equalTo: view.widthAnchor),
- playerViewController.view.heightAnchor.constraint(equalTo: view.heightAnchor),
- ])
+ playerViewController.view.pinToParent()
playerViewController.didMove(toParent: self)
if let contentOverlayView = playerViewController.contentOverlayView {
previewImageView.translatesAutoresizingMaskIntoConstraints = false
contentOverlayView.addSubview(previewImageView)
- NSLayoutConstraint.activate([
- previewImageView.topAnchor.constraint(equalTo: contentOverlayView.topAnchor),
- previewImageView.leadingAnchor.constraint(equalTo: contentOverlayView.leadingAnchor),
- previewImageView.trailingAnchor.constraint(equalTo: contentOverlayView.trailingAnchor),
- previewImageView.bottomAnchor.constraint(equalTo: contentOverlayView.bottomAnchor),
- ])
+ previewImageView.pinToParent()
}
playerViewController.delegate = self
@@ -90,6 +80,12 @@ extension MediaPreviewVideoViewController {
}
}
+ override func viewDidLayoutSubviews() {
+ super.viewDidLayoutSubviews()
+
+ playerViewController.didMove(toParent: self)
+ }
+
}
// MARK: - ShareActivityProvider
@@ -111,6 +107,12 @@ extension MediaPreviewVideoViewController {
// }
//}
+extension MediaPreviewVideoViewController: MediaPreviewPage {
+ func setShowingChrome(_ showingChrome: Bool) {
+ // TODO: does this do anything?
+ }
+}
+
// MARK: - AVPlayerViewControllerDelegate
extension MediaPreviewVideoViewController: AVPlayerViewControllerDelegate {
diff --git a/Mastodon/Scene/MediaPreview/Video/MediaPreviewVideoViewModel.swift b/Mastodon/Scene/MediaPreview/Video/MediaPreviewVideoViewModel.swift
index 97e5f955b..a6542d464 100644
--- a/Mastodon/Scene/MediaPreview/Video/MediaPreviewVideoViewModel.swift
+++ b/Mastodon/Scene/MediaPreview/Video/MediaPreviewVideoViewModel.swift
@@ -130,12 +130,14 @@ extension MediaPreviewVideoViewModel {
struct RemoteVideoContext {
let assetURL: URL?
let previewURL: URL?
+ let altText: String?
// let thumbnail: UIImage?
}
struct RemoteGIFContext {
let assetURL: URL?
let previewURL: URL?
+ let altText: String?
}
}
diff --git a/Mastodon/Scene/Notification/Button/NotificationAvatarButton.swift b/Mastodon/Scene/Notification/Button/NotificationAvatarButton.swift
deleted file mode 100644
index 26abfbd23..000000000
--- a/Mastodon/Scene/Notification/Button/NotificationAvatarButton.swift
+++ /dev/null
@@ -1,86 +0,0 @@
-//
-// NotificationAvatarButton.swift
-// Mastodon
-//
-// Created by MainasuK Cirno on 2021-7-21.
-//
-
-import UIKit
-import FLAnimatedImage
-import MastodonUI
-
-final class NotificationAvatarButton: AvatarButton {
-
- // Size fixed
- static let containerSize = CGSize(width: 35, height: 35)
- static let badgeImageViewSize = CGSize(width: 24, height: 24)
- static let badgeImageMaskSize = CGSize(width: badgeImageViewSize.width + 4, height: badgeImageViewSize.height + 4)
-
- let badgeImageView: UIImageView = {
- let imageView = RoundedImageView()
- imageView.contentMode = .center
- imageView.isOpaque = true
- imageView.layer.shouldRasterize = true
- imageView.layer.rasterizationScale = UIScreen.main.scale
- return imageView
- }()
-
- override func _init() {
- super._init()
-
- size = CGSize(width: 35, height: 35)
-
- let path: CGPath = {
- let path = CGMutablePath()
- path.addRect(CGRect(origin: .zero, size: NotificationAvatarButton.containerSize))
- let x: CGFloat = {
- if UIApplication.shared.userInterfaceLayoutDirection == .rightToLeft {
- return -0.5 * NotificationAvatarButton.badgeImageMaskSize.width
- } else {
- return NotificationAvatarButton.containerSize.width - 0.5 * NotificationAvatarButton.badgeImageMaskSize.width
- }
- }()
- path.addPath(UIBezierPath(
- ovalIn: CGRect(
- x: x,
- y: NotificationAvatarButton.containerSize.height - 0.5 * NotificationAvatarButton.badgeImageMaskSize.width,
- width: NotificationAvatarButton.badgeImageMaskSize.width,
- height: NotificationAvatarButton.badgeImageMaskSize.height
- )
- ).cgPath)
- return path
- }()
-
- let maskShapeLayer = CAShapeLayer()
- maskShapeLayer.backgroundColor = UIColor.black.cgColor
- maskShapeLayer.fillRule = .evenOdd
- maskShapeLayer.path = path
- avatarImageView.layer.mask = maskShapeLayer
-
- badgeImageView.translatesAutoresizingMaskIntoConstraints = false
- addSubview(badgeImageView)
- NSLayoutConstraint.activate([
- badgeImageView.centerXAnchor.constraint(equalTo: trailingAnchor),
- badgeImageView.centerYAnchor.constraint(equalTo: bottomAnchor),
- badgeImageView.widthAnchor.constraint(equalToConstant: NotificationAvatarButton.badgeImageViewSize.width).priority(.required - 1),
- badgeImageView.heightAnchor.constraint(equalToConstant: NotificationAvatarButton.badgeImageViewSize.height).priority(.required - 1),
- ])
- }
-
- override func updateAppearance() {
- super.updateAppearance()
- badgeImageView.alpha = primaryActionState.contains(.highlighted) ? 0.6 : 1.0
- }
-
-}
-
-final class RoundedImageView: UIImageView {
-
- override func layoutSubviews() {
- super.layoutSubviews()
-
- layer.masksToBounds = true
- layer.cornerRadius = bounds.width / 2
- layer.cornerCurve = .circular
- }
-}
diff --git a/Mastodon/Scene/Notification/NotificationTimeline/NotificationTimelineViewController.swift b/Mastodon/Scene/Notification/NotificationTimeline/NotificationTimelineViewController.swift
index d088bb783..9b7416afc 100644
--- a/Mastodon/Scene/Notification/NotificationTimeline/NotificationTimelineViewController.swift
+++ b/Mastodon/Scene/Notification/NotificationTimeline/NotificationTimelineViewController.swift
@@ -55,12 +55,7 @@ extension NotificationTimelineViewController {
tableView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(tableView)
- NSLayoutConstraint.activate([
- tableView.topAnchor.constraint(equalTo: view.topAnchor),
- tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
- tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
- tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
- ])
+ tableView.pinToParent()
tableView.delegate = self
viewModel.setupDiffableDataSource(
diff --git a/Mastodon/Scene/Notification/NotificationTimeline/NotificationTimelineViewModel+Diffable.swift b/Mastodon/Scene/Notification/NotificationTimeline/NotificationTimelineViewModel+Diffable.swift
index cb623ff15..0331f401e 100644
--- a/Mastodon/Scene/Notification/NotificationTimeline/NotificationTimelineViewModel+Diffable.swift
+++ b/Mastodon/Scene/Notification/NotificationTimeline/NotificationTimelineViewModel+Diffable.swift
@@ -111,17 +111,13 @@ extension NotificationTimelineViewModel {
snapshot: NSDiffableDataSourceSnapshot,
animatingDifferences: Bool
) async {
- diffableDataSource?.apply(snapshot, animatingDifferences: animatingDifferences)
+ await diffableDataSource?.apply(snapshot, animatingDifferences: animatingDifferences)
}
@MainActor func updateSnapshotUsingReloadData(
snapshot: NSDiffableDataSourceSnapshot
) async {
- if #available(iOS 15.0, *) {
- await self.diffableDataSource?.applySnapshotUsingReloadData(snapshot)
- } else {
- diffableDataSource?.applySnapshot(snapshot, animated: false, completion: nil)
- }
+ await self.diffableDataSource?.applySnapshotUsingReloadData(snapshot)
}
}
diff --git a/Mastodon/Scene/Onboarding/ConfirmEmail/MastodonConfirmEmailViewController.swift b/Mastodon/Scene/Onboarding/ConfirmEmail/MastodonConfirmEmailViewController.swift
index a90da1e81..ae8a4933e 100644
--- a/Mastodon/Scene/Onboarding/ConfirmEmail/MastodonConfirmEmailViewController.swift
+++ b/Mastodon/Scene/Onboarding/ConfirmEmail/MastodonConfirmEmailViewController.swift
@@ -189,7 +189,7 @@ extension MastodonConfirmEmailViewController {
alertController.addAction(openEmailAction)
alertController.addAction(cancelAction)
alertController.preferredAction = openEmailAction
- self.coordinator.present(scene: .alertController(alertController: alertController), from: self, transition: .alertController(animated: true, completion: nil))
+ _ = self.coordinator.present(scene: .alertController(alertController: alertController), from: self, transition: .alertController(animated: true, completion: nil))
}
@objc private func resendButtonPressed(_ sender: UIButton) {
@@ -197,18 +197,17 @@ extension MastodonConfirmEmailViewController {
let resendAction = UIAlertAction(title: L10n.Scene.ConfirmEmail.DontReceiveEmail.resendEmail, style: .default) { _ in
let url = Mastodon.API.resendEmailURL(domain: self.viewModel.authenticateInfo.domain)
let viewModel = MastodonResendEmailViewModel(resendEmailURL: url, email: self.viewModel.email)
- self.coordinator.present(scene: .mastodonResendEmail(viewModel: viewModel), from: self, transition: .modal(animated: true, completion: nil))
+ _ = self.coordinator.present(scene: .mastodonResendEmail(viewModel: viewModel), from: self, transition: .modal(animated: true, completion: nil))
}
let okAction = UIAlertAction(title: L10n.Common.Controls.Actions.ok, style: .default) { _ in
}
alertController.addAction(resendAction)
alertController.addAction(okAction)
- self.coordinator.present(scene: .alertController(alertController: alertController), from: self, transition: .alertController(animated: true, completion: nil))
+ _ = self.coordinator.present(scene: .alertController(alertController: alertController), from: self, transition: .alertController(animated: true, completion: nil))
}
func showEmailAppAlert() {
let clients = ThirdPartyMailClient.clients
- let application = UIApplication.shared
let availableClients = clients.filter { client -> Bool in
ThirdPartyMailer.isMailClientAvailable(client)
}
@@ -220,14 +219,14 @@ extension MastodonConfirmEmailViewController {
alertController.addAction(alertAction)
_ = availableClients.compactMap { client -> UIAlertAction in
let alertAction = UIAlertAction(title: client.name, style: .default) { _ in
- _ = ThirdPartyMailer.open(client, completionHandler: nil)
+ ThirdPartyMailer.open(client, completionHandler: nil)
}
alertController.addAction(alertAction)
return alertAction
}
let cancelAction = UIAlertAction(title: L10n.Common.Controls.Actions.cancel, style: .cancel, handler: nil)
alertController.addAction(cancelAction)
- self.coordinator.present(scene: .alertController(alertController: alertController), from: self, transition: .alertController(animated: true, completion: nil))
+ _ = self.coordinator.present(scene: .alertController(alertController: alertController), from: self, transition: .alertController(animated: true, completion: nil))
}
}
diff --git a/Mastodon/Scene/Onboarding/Login/ContentSizedTableView.swift b/Mastodon/Scene/Onboarding/Login/ContentSizedTableView.swift
new file mode 100644
index 000000000..e0c8a9228
--- /dev/null
+++ b/Mastodon/Scene/Onboarding/Login/ContentSizedTableView.swift
@@ -0,0 +1,22 @@
+//
+// MastodonLoginTableView.swift
+// Mastodon
+//
+// Created by Nathan Mattes on 13.11.22.
+//
+
+import UIKit
+
+// Source: https://stackoverflow.com/a/48623673
+final class ContentSizedTableView: UITableView {
+ override var contentSize:CGSize {
+ didSet {
+ invalidateIntrinsicContentSize()
+ }
+ }
+
+ override var intrinsicContentSize: CGSize {
+ layoutIfNeeded()
+ return CGSize(width: UIView.noIntrinsicMetric, height: contentSize.height)
+ }
+}
diff --git a/Mastodon/Scene/Onboarding/Login/MastodonLoginServerTableViewCell.swift b/Mastodon/Scene/Onboarding/Login/MastodonLoginServerTableViewCell.swift
new file mode 100644
index 000000000..2fb599015
--- /dev/null
+++ b/Mastodon/Scene/Onboarding/Login/MastodonLoginServerTableViewCell.swift
@@ -0,0 +1,12 @@
+//
+// MastodonLoginServerTableViewCell.swift
+// Mastodon
+//
+// Created by Nathan Mattes on 11.11.22.
+//
+
+import UIKit
+
+class MastodonLoginServerTableViewCell: UITableViewCell {
+ static let reuseIdentifier = "MastodonLoginServerTableViewCell"
+}
diff --git a/Mastodon/Scene/Onboarding/Login/MastodonLoginView.swift b/Mastodon/Scene/Onboarding/Login/MastodonLoginView.swift
new file mode 100644
index 000000000..d5f8b51d4
--- /dev/null
+++ b/Mastodon/Scene/Onboarding/Login/MastodonLoginView.swift
@@ -0,0 +1,151 @@
+//
+// MastodonLoginView.swift
+// Mastodon
+//
+// Created by Nathan Mattes on 10.11.22.
+//
+
+import UIKit
+import MastodonAsset
+import MastodonLocalization
+
+class MastodonLoginView: UIView {
+
+ // List with (filtered) domains
+
+ let titleLabel: UILabel
+ let subtitleLabel: UILabel
+ private let headerStackView: UIStackView
+
+ let searchTextField: UITextField
+ private let searchTextFieldLeftView: UIView
+ private let searchTextFieldMagnifyingGlass: UIImageView
+ private let searchContainerLeftPaddingView: UIView
+
+ let tableView: UITableView
+ let navigationActionView: NavigationActionView
+ var bottomConstraint: NSLayoutConstraint?
+
+ override init(frame: CGRect) {
+
+ titleLabel = UILabel()
+ titleLabel.font = MastodonLoginViewController.largeTitleFont
+ titleLabel.textColor = MastodonLoginViewController.largeTitleTextColor
+ titleLabel.text = L10n.Scene.Login.title
+ titleLabel.numberOfLines = 0
+
+ subtitleLabel = UILabel()
+ subtitleLabel.font = MastodonLoginViewController.subTitleFont
+ subtitleLabel.textColor = MastodonLoginViewController.subTitleTextColor
+ subtitleLabel.text = L10n.Scene.Login.subtitle
+ subtitleLabel.numberOfLines = 0
+
+ headerStackView = UIStackView(arrangedSubviews: [titleLabel, subtitleLabel])
+ headerStackView.axis = .vertical
+ headerStackView.spacing = 16
+ headerStackView.translatesAutoresizingMaskIntoConstraints = false
+
+ searchTextFieldMagnifyingGlass = UIImageView(image: UIImage(
+ systemName: "magnifyingglass",
+ withConfiguration: UIImage.SymbolConfiguration(pointSize: 15, weight: .regular)
+ ))
+ searchTextFieldMagnifyingGlass.tintColor = Asset.Colors.Label.secondary.color.withAlphaComponent(0.6)
+ searchTextFieldMagnifyingGlass.translatesAutoresizingMaskIntoConstraints = false
+
+ searchContainerLeftPaddingView = UIView()
+ searchContainerLeftPaddingView.translatesAutoresizingMaskIntoConstraints = false
+
+ searchTextFieldLeftView = UIView()
+ searchTextFieldLeftView.addSubview(searchTextFieldMagnifyingGlass)
+ searchTextFieldLeftView.addSubview(searchContainerLeftPaddingView)
+
+ searchTextField = UITextField()
+ searchTextField.translatesAutoresizingMaskIntoConstraints = false
+ searchTextField.backgroundColor = Asset.Scene.Onboarding.searchBarBackground.color
+ searchTextField.placeholder = L10n.Scene.Login.ServerSearchField.placeholder
+ searchTextField.leftView = searchTextFieldLeftView
+ searchTextField.leftViewMode = .always
+ searchTextField.layer.cornerRadius = 10
+ searchTextField.keyboardType = .URL
+ searchTextField.autocorrectionType = .no
+ searchTextField.autocapitalizationType = .none
+
+ tableView = ContentSizedTableView()
+ tableView.translatesAutoresizingMaskIntoConstraints = false
+ tableView.backgroundColor = Asset.Scene.Onboarding.textFieldBackground.color
+ tableView.keyboardDismissMode = .onDrag
+ tableView.layer.cornerRadius = 10
+
+ navigationActionView = NavigationActionView()
+ navigationActionView.translatesAutoresizingMaskIntoConstraints = false
+
+ super.init(frame: frame)
+
+ addSubview(headerStackView)
+ addSubview(searchTextField)
+ addSubview(tableView)
+ addSubview(navigationActionView)
+ backgroundColor = Asset.Scene.Onboarding.background.color
+
+ setupConstraints()
+ }
+
+ required init?(coder: NSCoder) {
+ fatalError("init(coder:) has not been implemented")
+ }
+
+ private func setupConstraints() {
+
+ let bottomConstraint = safeAreaLayoutGuide.bottomAnchor.constraint(equalTo: navigationActionView.bottomAnchor)
+
+ let constraints = [
+
+ headerStackView.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor),
+ headerStackView.leadingAnchor.constraint(equalTo: readableContentGuide.leadingAnchor),
+ headerStackView.trailingAnchor.constraint(equalTo: readableContentGuide.trailingAnchor),
+
+ searchTextField.topAnchor.constraint(equalTo: headerStackView.bottomAnchor, constant: 32),
+ searchTextField.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16),
+ searchTextField.heightAnchor.constraint(equalToConstant: 55),
+ trailingAnchor.constraint(equalTo: searchTextField.trailingAnchor, constant: 16),
+
+ searchTextFieldMagnifyingGlass.topAnchor.constraint(equalTo: searchTextFieldLeftView.topAnchor),
+ searchTextFieldMagnifyingGlass.leadingAnchor.constraint(equalTo: searchTextFieldLeftView.leadingAnchor, constant: 8),
+ searchTextFieldMagnifyingGlass.bottomAnchor.constraint(equalTo: searchTextFieldLeftView.bottomAnchor),
+
+ searchContainerLeftPaddingView.topAnchor.constraint(equalTo: searchTextFieldLeftView.topAnchor),
+ searchContainerLeftPaddingView.leadingAnchor.constraint(equalTo: searchTextFieldMagnifyingGlass.trailingAnchor),
+ searchContainerLeftPaddingView.trailingAnchor.constraint(equalTo: searchTextFieldLeftView.trailingAnchor),
+ searchContainerLeftPaddingView.bottomAnchor.constraint(equalTo: searchTextFieldLeftView.bottomAnchor),
+ searchContainerLeftPaddingView.widthAnchor.constraint(equalToConstant: 4).priority(.defaultHigh),
+
+ tableView.topAnchor.constraint(equalTo: searchTextField.bottomAnchor, constant: 2),
+ tableView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16),
+ trailingAnchor.constraint(equalTo: tableView.trailingAnchor, constant: 16),
+ tableView.bottomAnchor.constraint(lessThanOrEqualTo: navigationActionView.topAnchor),
+
+ navigationActionView.leadingAnchor.constraint(equalTo: leadingAnchor),
+ navigationActionView.trailingAnchor.constraint(equalTo: trailingAnchor),
+ bottomConstraint,
+ ]
+
+ self.bottomConstraint = bottomConstraint
+ NSLayoutConstraint.activate(constraints)
+ }
+
+ func updateCorners(numberOfResults: Int = 0) {
+
+ tableView.isHidden = (numberOfResults == 0)
+ tableView.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]
+
+ let maskedCorners: CACornerMask
+
+ if numberOfResults == 0 {
+ maskedCorners = [.layerMinXMinYCorner, .layerMinXMaxYCorner, .layerMaxXMinYCorner, .layerMaxXMaxYCorner]
+ } else {
+ maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
+ }
+
+ searchTextField.layer.maskedCorners = maskedCorners
+ }
+}
diff --git a/Mastodon/Scene/Onboarding/Login/MastodonLoginViewController.swift b/Mastodon/Scene/Onboarding/Login/MastodonLoginViewController.swift
new file mode 100644
index 000000000..9f9ce5d75
--- /dev/null
+++ b/Mastodon/Scene/Onboarding/Login/MastodonLoginViewController.swift
@@ -0,0 +1,288 @@
+//
+// MastodonLoginViewController.swift
+// Mastodon
+//
+// Created by Nathan Mattes on 09.11.22.
+//
+
+import UIKit
+import MastodonSDK
+import MastodonCore
+import MastodonAsset
+import Combine
+import AuthenticationServices
+
+protocol MastodonLoginViewControllerDelegate: AnyObject {
+ func backButtonPressed(_ viewController: MastodonLoginViewController)
+ func nextButtonPressed(_ viewController: MastodonLoginViewController)
+}
+
+enum MastodonLoginViewSection: Hashable {
+ case servers
+}
+
+class MastodonLoginViewController: UIViewController, NeedsDependency {
+
+ weak var delegate: MastodonLoginViewControllerDelegate?
+ var dataSource: UITableViewDiffableDataSource?
+ let viewModel: MastodonLoginViewModel
+ let authenticationViewModel: AuthenticationViewModel
+ var mastodonAuthenticationController: MastodonAuthenticationController?
+
+ weak var context: AppContext!
+ weak var coordinator: SceneCoordinator!
+
+ var disposeBag = Set()
+
+ var contentView: MastodonLoginView {
+ view as! MastodonLoginView
+ }
+
+ init(appContext: AppContext, authenticationViewModel: AuthenticationViewModel, sceneCoordinator: SceneCoordinator) {
+
+ viewModel = MastodonLoginViewModel(appContext: appContext)
+ self.authenticationViewModel = authenticationViewModel
+ self.context = appContext
+ self.coordinator = sceneCoordinator
+
+ super.init(nibName: nil, bundle: nil)
+ viewModel.delegate = self
+
+ navigationItem.hidesBackButton = true
+ }
+
+ required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
+
+ override func loadView() {
+ let loginView = MastodonLoginView()
+
+ loginView.navigationActionView.nextButton.addTarget(self, action: #selector(MastodonLoginViewController.nextButtonPressed(_:)), for: .touchUpInside)
+ loginView.navigationActionView.backButton.addTarget(self, action: #selector(MastodonLoginViewController.backButtonPressed(_:)), for: .touchUpInside)
+ loginView.searchTextField.addTarget(self, action: #selector(MastodonLoginViewController.textfieldDidChange(_:)), for: .editingChanged)
+ loginView.tableView.delegate = self
+ loginView.tableView.register(MastodonLoginServerTableViewCell.self, forCellReuseIdentifier: MastodonLoginServerTableViewCell.reuseIdentifier)
+ loginView.navigationActionView.nextButton.isEnabled = false
+
+ view = loginView
+ }
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+
+ NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShowNotification(_:)), name: UIResponder.keyboardWillShowNotification, object: nil)
+ NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHideNotification(_:)), name: UIResponder.keyboardWillHideNotification, object: nil)
+
+ let dataSource = UITableViewDiffableDataSource(tableView: contentView.tableView) { [weak self] tableView, indexPath, itemIdentifier in
+ guard let cell = tableView.dequeueReusableCell(withIdentifier: MastodonLoginServerTableViewCell.reuseIdentifier, for: indexPath) as? MastodonLoginServerTableViewCell,
+ let self = self else {
+ fatalError("Wrong cell")
+ }
+
+ let server = self.viewModel.filteredServers[indexPath.row]
+ var configuration = cell.defaultContentConfiguration()
+ configuration.text = server.domain
+
+ cell.contentConfiguration = configuration
+ cell.accessoryType = .disclosureIndicator
+
+ cell.backgroundColor = Asset.Scene.Onboarding.textFieldBackground.color
+
+ return cell
+ }
+
+ contentView.tableView.dataSource = dataSource
+ self.dataSource = dataSource
+
+ contentView.updateCorners()
+
+ defer { setupNavigationBarBackgroundView() }
+ setupOnboardingAppearance()
+ }
+
+ override func viewWillAppear(_ animated: Bool) {
+ super.viewWillAppear(animated)
+
+ viewModel.updateServers()
+ }
+
+ override func viewDidAppear(_ animated: Bool) {
+ super.viewDidAppear(animated)
+
+ contentView.searchTextField.becomeFirstResponder()
+ }
+
+ //MARK: - Actions
+
+ @objc func backButtonPressed(_ sender: Any) {
+ contentView.searchTextField.resignFirstResponder()
+ delegate?.backButtonPressed(self)
+ }
+
+ @objc func nextButtonPressed(_ sender: Any) {
+ contentView.searchTextField.resignFirstResponder()
+ delegate?.nextButtonPressed(self)
+ }
+
+ @objc func login() {
+ guard let server = viewModel.selectedServer else { return }
+
+ authenticationViewModel
+ .authenticated
+ .asyncMap { domain, user -> Result in
+ do {
+ let result = try await self.context.authenticationService.activeMastodonUser(domain: domain, userID: user.id)
+ return .success(result)
+ } catch {
+ return .failure(error)
+ }
+ }
+ .receive(on: DispatchQueue.main)
+ .sink { [weak self] result in
+ guard let self = self else { return }
+ switch result {
+ case .failure(let error):
+ assertionFailure(error.localizedDescription)
+ case .success(let isActived):
+ assert(isActived)
+ self.coordinator.setup()
+ }
+ }
+ .store(in: &disposeBag)
+
+ authenticationViewModel.isAuthenticating.send(true)
+ context.apiService.createApplication(domain: server.domain)
+ .tryMap { response -> AuthenticationViewModel.AuthenticateInfo in
+ let application = response.value
+ guard let info = AuthenticationViewModel.AuthenticateInfo(
+ domain: server.domain,
+ application: application,
+ redirectURI: response.value.redirectURI ?? APIService.oauthCallbackURL
+ ) else {
+ throw APIService.APIError.explicit(.badResponse)
+ }
+ return info
+ }
+ .receive(on: DispatchQueue.main)
+ .sink { [weak self] completion in
+ guard let self = self else { return }
+ self.authenticationViewModel.isAuthenticating.send(false)
+
+ switch completion {
+ case .failure(let error):
+ let alert = UIAlertController.standardAlert(of: error)
+ self.present(alert, animated: true)
+ case .finished:
+ // do nothing. There's a subscriber above resulting in `coordinator.setup()`
+ break
+ }
+ } receiveValue: { [weak self] info in
+ guard let self else { return }
+ let authenticationController = MastodonAuthenticationController(
+ context: self.context,
+ authenticateURL: info.authorizeURL
+ )
+
+ self.mastodonAuthenticationController = authenticationController
+ authenticationController.authenticationSession?.presentationContextProvider = self
+ authenticationController.authenticationSession?.start()
+
+ self.authenticationViewModel.authenticate(
+ info: info,
+ pinCodePublisher: authenticationController.pinCodePublisher
+ )
+ }
+ .store(in: &disposeBag)
+ }
+
+ @objc func textfieldDidChange(_ textField: UITextField) {
+ viewModel.filterServers(withText: textField.text)
+
+
+ if let text = textField.text,
+ let domain = AuthenticationViewModel.parseDomain(from: text) {
+
+ viewModel.selectedServer = .init(domain: domain, instance: .init(domain: domain))
+ contentView.navigationActionView.nextButton.isEnabled = true
+ } else {
+ viewModel.selectedServer = nil
+ contentView.navigationActionView.nextButton.isEnabled = false
+ }
+ }
+
+ // MARK: - Notifications
+ @objc func keyboardWillShowNotification(_ notification: Notification) {
+
+ guard let userInfo = notification.userInfo,
+ let keyboardFrameValue = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue,
+ let duration = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? NSNumber
+ else { return }
+
+ // inspired by https://stackoverflow.com/a/30245044
+ let keyboardFrame = keyboardFrameValue.cgRectValue
+
+ let keyboardOrigin = view.convert(keyboardFrame.origin, from: nil)
+ let intersectionY = CGRectGetMaxY(view.frame) - keyboardOrigin.y;
+
+ if intersectionY >= 0 {
+ contentView.bottomConstraint?.constant = intersectionY - view.safeAreaInsets.bottom
+ }
+
+ UIView.animate(withDuration: duration.doubleValue, delay: 0, options: .curveEaseInOut) {
+ self.view.layoutIfNeeded()
+ }
+ }
+
+ @objc func keyboardWillHideNotification(_ notification: Notification) {
+
+ guard let userInfo = notification.userInfo,
+ let duration = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? NSNumber
+ else { return }
+
+ contentView.bottomConstraint?.constant = 0
+
+ UIView.animate(withDuration: duration.doubleValue, delay: 0, options: .curveEaseInOut) {
+ self.view.layoutIfNeeded()
+ }
+ }
+}
+
+// MARK: - OnboardingViewControllerAppearance
+extension MastodonLoginViewController: OnboardingViewControllerAppearance { }
+
+// MARK: - UITableViewDelegate
+extension MastodonLoginViewController: UITableViewDelegate {
+ func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
+ let server = viewModel.filteredServers[indexPath.row]
+ viewModel.selectedServer = server
+
+ contentView.searchTextField.text = server.domain
+ viewModel.filterServers(withText: " ")
+
+ contentView.navigationActionView.nextButton.isEnabled = true
+ tableView.deselectRow(at: indexPath, animated: true)
+ }
+}
+
+// MARK: - MastodonLoginViewModelDelegate
+extension MastodonLoginViewController: MastodonLoginViewModelDelegate {
+ func serversUpdated(_ viewModel: MastodonLoginViewModel) {
+ var snapshot = NSDiffableDataSourceSnapshot()
+
+ snapshot.appendSections([MastodonLoginViewSection.servers])
+ snapshot.appendItems(viewModel.filteredServers)
+
+ dataSource?.apply(snapshot, animatingDifferences: false)
+
+ OperationQueue.main.addOperation {
+ let numberOfResults = viewModel.filteredServers.count
+ self.contentView.updateCorners(numberOfResults: numberOfResults)
+ }
+ }
+}
+
+// MARK: - ASWebAuthenticationPresentationContextProviding
+extension MastodonLoginViewController: ASWebAuthenticationPresentationContextProviding {
+ func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor {
+ return view.window!
+ }
+}
diff --git a/Mastodon/Scene/Onboarding/Login/MastodonLoginViewModel.swift b/Mastodon/Scene/Onboarding/Login/MastodonLoginViewModel.swift
new file mode 100644
index 000000000..61311a1b9
--- /dev/null
+++ b/Mastodon/Scene/Onboarding/Login/MastodonLoginViewModel.swift
@@ -0,0 +1,57 @@
+//
+// MastodonLoginViewModel.swift
+// Mastodon
+//
+// Created by Nathan Mattes on 11.11.22.
+//
+
+import Foundation
+import MastodonSDK
+import MastodonCore
+import Combine
+
+protocol MastodonLoginViewModelDelegate: AnyObject {
+ func serversUpdated(_ viewModel: MastodonLoginViewModel)
+}
+
+class MastodonLoginViewModel {
+
+ private var serverList: [Mastodon.Entity.Server] = []
+ var selectedServer: Mastodon.Entity.Server?
+ var filteredServers: [Mastodon.Entity.Server] = []
+
+ weak var appContext: AppContext?
+ weak var delegate: MastodonLoginViewModelDelegate?
+ var disposeBag = Set()
+
+ init(appContext: AppContext) {
+ self.appContext = appContext
+ }
+
+ func updateServers() {
+ appContext?.apiService.servers().sink(receiveCompletion: { [weak self] completion in
+ switch completion {
+ case .finished:
+ guard let self = self else { return }
+
+ self.delegate?.serversUpdated(self)
+ case .failure(let error):
+ print(error)
+ }
+ }, receiveValue: { content in
+ let servers = content.value
+ self.serverList = servers
+ }).store(in: &disposeBag)
+ }
+
+ func filterServers(withText query: String?) {
+ guard let query else {
+ filteredServers = serverList
+ delegate?.serversUpdated(self)
+ return
+ }
+
+ filteredServers = serverList.filter { $0.domain.lowercased().contains(query) }.sorted {$0.totalUsers > $1.totalUsers }
+ delegate?.serversUpdated(self)
+ }
+}
diff --git a/Mastodon/Scene/Onboarding/PickServer/MastodonPickServerViewController.swift b/Mastodon/Scene/Onboarding/PickServer/MastodonPickServerViewController.swift
index eb26f75be..057c742c1 100644
--- a/Mastodon/Scene/Onboarding/PickServer/MastodonPickServerViewController.swift
+++ b/Mastodon/Scene/Onboarding/PickServer/MastodonPickServerViewController.swift
@@ -43,11 +43,7 @@ final class MastodonPickServerViewController: UIViewController, NeedsDependency
tableView.separatorStyle = .none
tableView.backgroundColor = .clear
tableView.keyboardDismissMode = .onDrag
- if #available(iOS 15.0, *) {
- tableView.sectionHeaderTopPadding = .leastNonzeroMagnitude
- } else {
- // Fallback on earlier versions
- }
+ tableView.sectionHeaderTopPadding = .leastNonzeroMagnitude
return tableView
}()
@@ -143,8 +139,7 @@ extension MastodonPickServerViewController {
viewModel.setupDiffableDataSource(
for: tableView,
dependency: self,
- pickServerServerSectionTableHeaderViewDelegate: self,
- pickServerCellDelegate: self
+ pickServerServerSectionTableHeaderViewDelegate: self
)
KeyboardResponderService
@@ -172,7 +167,7 @@ extension MastodonPickServerViewController {
let alertController = UIAlertController(for: error, title: "Error", preferredStyle: .alert)
let okAction = UIAlertAction(title: L10n.Common.Controls.Actions.ok, style: .default, handler: nil)
alertController.addAction(okAction)
- self.coordinator.present(
+ _ = self.coordinator.present(
scene: .alertController(alertController: alertController),
from: nil,
transition: .alertController(animated: true, completion: nil)
@@ -271,59 +266,6 @@ extension MastodonPickServerViewController {
}
@objc private func nextButtonDidPressed(_ sender: UIButton) {
- switch viewModel.mode {
- case .signIn: doSignIn()
- case .signUp: doSignUp()
- }
- }
-
- private func doSignIn() {
- guard let server = viewModel.selectedServer.value else { return }
- authenticationViewModel.isAuthenticating.send(true)
- context.apiService.createApplication(domain: server.domain)
- .tryMap { response -> AuthenticationViewModel.AuthenticateInfo in
- let application = response.value
- guard let info = AuthenticationViewModel.AuthenticateInfo(
- domain: server.domain,
- application: application,
- redirectURI: response.value.redirectURI ?? APIService.oauthCallbackURL
- ) else {
- throw APIService.APIError.explicit(.badResponse)
- }
- return info
- }
- .receive(on: DispatchQueue.main)
- .sink { [weak self] completion in
- guard let self = self else { return }
- self.authenticationViewModel.isAuthenticating.send(false)
-
- switch completion {
- case .failure(let error):
- os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: sign in fail: %s", ((#file as NSString).lastPathComponent), #line, #function, error.localizedDescription)
- self.viewModel.error.send(error)
- case .finished:
- break
- }
- } receiveValue: { [weak self] info in
- guard let self = self else { return }
- let authenticationController = MastodonAuthenticationController(
- context: self.context,
- authenticateURL: info.authorizeURL
- )
-
- self.mastodonAuthenticationController = authenticationController
- authenticationController.authenticationSession?.presentationContextProvider = self
- authenticationController.authenticationSession?.start()
-
- self.authenticationViewModel.authenticate(
- info: info,
- pinCodePublisher: authenticationController.pinCodePublisher
- )
- }
- .store(in: &disposeBag)
- }
-
- private func doSignUp() {
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
guard let server = viewModel.selectedServer.value else { return }
authenticationViewModel.isAuthenticating.send(true)
@@ -394,7 +336,7 @@ extension MastodonPickServerViewController {
instance: response.instance.value,
applicationToken: response.applicationToken.value
)
- self.coordinator.present(scene: .mastodonServerRules(viewModel: mastodonServerRulesViewModel), from: self, transition: .show)
+ _ = self.coordinator.present(scene: .mastodonServerRules(viewModel: mastodonServerRulesViewModel), from: self, transition: .show)
} else {
let mastodonRegisterViewModel = MastodonRegisterViewModel(
context: self.context,
@@ -403,7 +345,7 @@ extension MastodonPickServerViewController {
instance: response.instance.value,
applicationToken: response.applicationToken.value
)
- self.coordinator.present(scene: .mastodonRegister(viewModel: mastodonRegisterViewModel), from: nil, transition: .show)
+ _ = self.coordinator.present(scene: .mastodonRegister(viewModel: mastodonRegisterViewModel), from: nil, transition: .show)
}
}
.store(in: &disposeBag)
@@ -503,17 +445,5 @@ extension MastodonPickServerViewController: PickServerServerSectionTableHeaderVi
}
}
-// MARK: - PickServerCellDelegate
-extension MastodonPickServerViewController: PickServerCellDelegate {
-
-}
-
// MARK: - OnboardingViewControllerAppearance
extension MastodonPickServerViewController: OnboardingViewControllerAppearance { }
-
-// MARK: - ASWebAuthenticationPresentationContextProviding
-extension MastodonPickServerViewController: ASWebAuthenticationPresentationContextProviding {
- func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor {
- return view.window!
- }
-}
diff --git a/Mastodon/Scene/Onboarding/PickServer/MastodonPickServerViewModel+Diffable.swift b/Mastodon/Scene/Onboarding/PickServer/MastodonPickServerViewModel+Diffable.swift
index 35de40b8f..5db98c218 100644
--- a/Mastodon/Scene/Onboarding/PickServer/MastodonPickServerViewModel+Diffable.swift
+++ b/Mastodon/Scene/Onboarding/PickServer/MastodonPickServerViewModel+Diffable.swift
@@ -13,8 +13,7 @@ extension MastodonPickServerViewModel {
func setupDiffableDataSource(
for tableView: UITableView,
dependency: NeedsDependency,
- pickServerServerSectionTableHeaderViewDelegate: PickServerServerSectionTableHeaderViewDelegate,
- pickServerCellDelegate: PickServerCellDelegate
+ pickServerServerSectionTableHeaderViewDelegate: PickServerServerSectionTableHeaderViewDelegate
) {
// set section header
serverSectionHeaderView.diffableDataSource = CategoryPickerSection.collectionViewDiffableDataSource(
@@ -25,7 +24,7 @@ extension MastodonPickServerViewModel {
sectionHeaderSnapshot.appendSections([.main])
sectionHeaderSnapshot.appendItems(categoryPickerItems, toSection: .main)
serverSectionHeaderView.delegate = pickServerServerSectionTableHeaderViewDelegate
- serverSectionHeaderView.diffableDataSource?.applySnapshot(sectionHeaderSnapshot, animated: false) { [weak self] in
+ serverSectionHeaderView.diffableDataSource?.apply(sectionHeaderSnapshot, animatingDifferences: false) { [weak self] in
guard let self = self else { return }
guard let indexPath = self.serverSectionHeaderView.diffableDataSource?.indexPath(for: .all) else { return }
self.serverSectionHeaderView.collectionView.selectItem(at: indexPath, animated: false, scrollPosition: .centeredHorizontally)
@@ -34,8 +33,7 @@ extension MastodonPickServerViewModel {
// set tableView
diffableDataSource = PickServerSection.tableViewDiffableDataSource(
for: tableView,
- dependency: dependency,
- pickServerCellDelegate: pickServerCellDelegate
+ dependency: dependency
)
var snapshot = NSDiffableDataSourceSnapshot()
diff --git a/Mastodon/Scene/Onboarding/PickServer/MastodonPickServerViewModel.swift b/Mastodon/Scene/Onboarding/PickServer/MastodonPickServerViewModel.swift
index 50c1d7aac..ebbcfe7fd 100644
--- a/Mastodon/Scene/Onboarding/PickServer/MastodonPickServerViewModel.swift
+++ b/Mastodon/Scene/Onboarding/PickServer/MastodonPickServerViewModel.swift
@@ -17,12 +17,7 @@ import MastodonCore
import MastodonUI
class MastodonPickServerViewModel: NSObject {
-
- enum PickServerMode {
- case signUp
- case signIn
- }
-
+
enum EmptyStateViewState {
case none
case loading
@@ -34,7 +29,6 @@ class MastodonPickServerViewModel: NSObject {
let serverSectionHeaderView = PickServerServerSectionTableHeaderView()
// input
- let mode: PickServerMode
let context: AppContext
var categoryPickerItems: [CategoryPickerItem] = {
var items: [CategoryPickerItem] = []
@@ -72,9 +66,8 @@ class MastodonPickServerViewModel: NSObject {
let loadingIndexedServersError = CurrentValueSubject(nil)
let emptyStateViewState = CurrentValueSubject(.none)
- init(context: AppContext, mode: PickServerMode) {
+ init(context: AppContext) {
self.context = context
- self.mode = mode
super.init()
configure()
@@ -115,9 +108,7 @@ extension MastodonPickServerViewModel {
.map { indexedServers, selectCategoryItem, searchText -> [Mastodon.Entity.Server] in
// ignore approval required servers when sign-up
var indexedServers = indexedServers
- if self.mode == .signUp {
- indexedServers = indexedServers.filter { !$0.approvalRequired }
- }
+ indexedServers = indexedServers.filter { !$0.approvalRequired }
// Note:
// sort by calculate last week users count
// and make medium size (~800) server to top
diff --git a/Mastodon/Scene/Onboarding/PickServer/TableViewCell/PickServerCell.swift b/Mastodon/Scene/Onboarding/PickServer/TableViewCell/PickServerCell.swift
index 669067770..4cbe77b9c 100644
--- a/Mastodon/Scene/Onboarding/PickServer/TableViewCell/PickServerCell.swift
+++ b/Mastodon/Scene/Onboarding/PickServer/TableViewCell/PickServerCell.swift
@@ -14,14 +14,8 @@ import Kanna
import MastodonAsset
import MastodonLocalization
-protocol PickServerCellDelegate: AnyObject {
-// func pickServerCell(_ cell: PickServerCell, expandButtonPressed button: UIButton)
-}
-
class PickServerCell: UITableViewCell {
- weak var delegate: PickServerCellDelegate?
-
var disposeBag = Set()
let containerView: UIStackView = {
@@ -88,7 +82,7 @@ class PickServerCell: UITableViewCell {
label.adjustsFontForContentSizeCategory = true
return label
}()
-
+
private var collapseConstraints: [NSLayoutConstraint] = []
private var expandConstraints: [NSLayoutConstraint] = []
diff --git a/Mastodon/Scene/Onboarding/PickServer/View/PickServerCategoryView.swift b/Mastodon/Scene/Onboarding/PickServer/View/PickServerCategoryView.swift
index 9d6cfc85f..aba5a87dc 100644
--- a/Mastodon/Scene/Onboarding/PickServer/View/PickServerCategoryView.swift
+++ b/Mastodon/Scene/Onboarding/PickServer/View/PickServerCategoryView.swift
@@ -19,13 +19,6 @@ class PickServerCategoryView: UIView {
return view
}()
- let emojiLabel: UILabel = {
- let label = UILabel()
- label.textAlignment = .center
- label.font = .systemFont(ofSize: 34, weight: .regular)
- return label
- }()
-
let titleLabel: UILabel = {
let label = UILabel()
label.textAlignment = .center
@@ -50,23 +43,18 @@ extension PickServerCategoryView {
private func configure() {
let container = UIStackView()
container.axis = .vertical
+ container.spacing = 2
container.distribution = .fillProportionally
container.translatesAutoresizingMaskIntoConstraints = false
addSubview(container)
- NSLayoutConstraint.activate([
- container.topAnchor.constraint(equalTo: topAnchor),
- container.leadingAnchor.constraint(equalTo: leadingAnchor),
- container.trailingAnchor.constraint(equalTo: trailingAnchor),
- container.bottomAnchor.constraint(equalTo: bottomAnchor),
- ])
+ container.pinToParent()
- container.addArrangedSubview(emojiLabel)
container.addArrangedSubview(titleLabel)
highlightedIndicatorView.translatesAutoresizingMaskIntoConstraints = false
container.addArrangedSubview(highlightedIndicatorView)
NSLayoutConstraint.activate([
- highlightedIndicatorView.heightAnchor.constraint(equalToConstant: 3).priority(.required - 1),
+ highlightedIndicatorView.heightAnchor.constraint(equalToConstant: 3)//.priority(.required - 1),
])
titleLabel.setContentHuggingPriority(.required - 1, for: .vertical)
}
diff --git a/Mastodon/Scene/Onboarding/PickServer/View/PickServerServerSectionTableHeaderView.swift b/Mastodon/Scene/Onboarding/PickServer/View/PickServerServerSectionTableHeaderView.swift
index d50c62afe..ae8c16bb8 100644
--- a/Mastodon/Scene/Onboarding/PickServer/View/PickServerServerSectionTableHeaderView.swift
+++ b/Mastodon/Scene/Onboarding/PickServer/View/PickServerServerSectionTableHeaderView.swift
@@ -19,7 +19,7 @@ protocol PickServerServerSectionTableHeaderViewDelegate: AnyObject {
final class PickServerServerSectionTableHeaderView: UIView {
- static let collectionViewHeight: CGFloat = 88
+ static let collectionViewHeight: CGFloat = 30
static let searchTextFieldHeight: CGFloat = 38
static let spacing: CGFloat = 11
@@ -177,7 +177,6 @@ extension PickServerServerSectionTableHeaderView {
extension PickServerServerSectionTableHeaderView: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
- os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: indexPath: %s", ((#file as NSString).lastPathComponent), #line, #function, indexPath.debugDescription)
collectionView.selectItem(at: indexPath, animated: true, scrollPosition: .centeredHorizontally)
delegate?.pickServerServerSectionTableHeaderView(self, collectionView: collectionView, didSelectItemAt: indexPath)
}
@@ -205,5 +204,5 @@ extension PickServerServerSectionTableHeaderView: UITextFieldDelegate {
textField.resignFirstResponder()
return false
}
-
+
}
diff --git a/Mastodon/Scene/Onboarding/Register/Cell/MastodonRegisterAvatarTableViewCell.swift b/Mastodon/Scene/Onboarding/Register/Cell/MastodonRegisterAvatarTableViewCell.swift
deleted file mode 100644
index 154385e6a..000000000
--- a/Mastodon/Scene/Onboarding/Register/Cell/MastodonRegisterAvatarTableViewCell.swift
+++ /dev/null
@@ -1,116 +0,0 @@
-//
-// MastodonRegisterAvatarTableViewCell.swift
-// Mastodon
-//
-// Created by MainasuK on 2022-1-5.
-//
-
-import UIKit
-import Combine
-import MastodonAsset
-import MastodonLocalization
-
-final class MastodonRegisterAvatarTableViewCell: UITableViewCell {
-
- static let containerSize = CGSize(width: 88, height: 88)
-
- var disposeBag = Set()
-
- let containerView: UIView = {
- let view = UIView()
- view.backgroundColor = .clear
- view.layer.masksToBounds = true
- view.layer.cornerCurve = .continuous
- view.layer.cornerRadius = 22
- return view
- }()
-
- let avatarButton: HighlightDimmableButton = {
- let button = HighlightDimmableButton()
- button.backgroundColor = Asset.Theme.Mastodon.secondaryGroupedSystemBackground.color
- button.setImage(Asset.Scene.Onboarding.avatarPlaceholder.image, for: .normal)
- return button
- }()
-
- let editBannerView: UIView = {
- let bannerView = UIView()
- bannerView.backgroundColor = UIColor.black.withAlphaComponent(0.5)
- bannerView.isUserInteractionEnabled = false
-
- let label: UILabel = {
- let label = UILabel()
- label.textColor = .white
- label.text = L10n.Common.Controls.Actions.edit
- label.font = .systemFont(ofSize: 13, weight: .semibold)
- label.textAlignment = .center
- label.minimumScaleFactor = 0.5
- label.adjustsFontSizeToFitWidth = true
- return label
- }()
-
- label.translatesAutoresizingMaskIntoConstraints = false
- bannerView.addSubview(label)
- NSLayoutConstraint.activate([
- label.topAnchor.constraint(equalTo: bannerView.topAnchor),
- label.leadingAnchor.constraint(equalTo: bannerView.leadingAnchor),
- label.trailingAnchor.constraint(equalTo: bannerView.trailingAnchor),
- label.bottomAnchor.constraint(equalTo: bannerView.bottomAnchor),
- ])
-
- return bannerView
- }()
-
- override func prepareForReuse() {
- super.prepareForReuse()
-
- disposeBag.removeAll()
- }
-
- override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
- super.init(style: style, reuseIdentifier: reuseIdentifier)
- _init()
- }
-
- required init?(coder: NSCoder) {
- super.init(coder: coder)
- _init()
- }
-
-}
-
-extension MastodonRegisterAvatarTableViewCell {
-
- private func _init() {
- selectionStyle = .none
- backgroundColor = .clear
-
- containerView.translatesAutoresizingMaskIntoConstraints = false
- contentView.addSubview(containerView)
- NSLayoutConstraint.activate([
- containerView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 22),
- containerView.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
- contentView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: 8),
- containerView.widthAnchor.constraint(equalToConstant: MastodonRegisterAvatarTableViewCell.containerSize.width).priority(.required - 1),
- containerView.heightAnchor.constraint(equalToConstant: MastodonRegisterAvatarTableViewCell.containerSize.height).priority(.required - 1),
- ])
-
- avatarButton.translatesAutoresizingMaskIntoConstraints = false
- containerView.addSubview(avatarButton)
- NSLayoutConstraint.activate([
- avatarButton.topAnchor.constraint(equalTo: containerView.topAnchor),
- avatarButton.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
- avatarButton.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
- avatarButton.bottomAnchor.constraint(equalTo: containerView.bottomAnchor),
- ])
-
- editBannerView.translatesAutoresizingMaskIntoConstraints = false
- containerView.addSubview(editBannerView)
- NSLayoutConstraint.activate([
- editBannerView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
- editBannerView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
- editBannerView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor),
- editBannerView.heightAnchor.constraint(equalToConstant: 22),
- ])
- }
-
-}
diff --git a/Mastodon/Scene/Onboarding/Register/Cell/MastodonRegisterPasswordHintTableViewCell.swift b/Mastodon/Scene/Onboarding/Register/Cell/MastodonRegisterPasswordHintTableViewCell.swift
deleted file mode 100644
index 1324c2822..000000000
--- a/Mastodon/Scene/Onboarding/Register/Cell/MastodonRegisterPasswordHintTableViewCell.swift
+++ /dev/null
@@ -1,50 +0,0 @@
-//
-// MastodonRegisterPasswordHintTableViewCell.swift
-// Mastodon
-//
-// Created by MainasuK on 2022-1-7.
-//
-
-import UIKit
-import MastodonAsset
-import MastodonLocalization
-
-final class MastodonRegisterPasswordHintTableViewCell: UITableViewCell {
-
- let passwordRuleLabel: UILabel = {
- let label = UILabel()
- label.font = .preferredFont(forTextStyle: .footnote)
- label.textColor = Asset.Colors.Label.secondary.color
- label.text = L10n.Scene.Register.Input.Password.hint
- return label
- }()
-
- override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
- super.init(style: style, reuseIdentifier: reuseIdentifier)
- _init()
- }
-
- required init?(coder: NSCoder) {
- super.init(coder: coder)
- _init()
- }
-
-}
-
-extension MastodonRegisterPasswordHintTableViewCell {
-
- private func _init() {
- selectionStyle = .none
- backgroundColor = .clear
-
- passwordRuleLabel.translatesAutoresizingMaskIntoConstraints = false
- contentView.addSubview(passwordRuleLabel)
- NSLayoutConstraint.activate([
- passwordRuleLabel.topAnchor.constraint(equalTo: contentView.topAnchor),
- passwordRuleLabel.leadingAnchor.constraint(equalTo: contentView.readableContentGuide.leadingAnchor),
- passwordRuleLabel.trailingAnchor.constraint(equalTo: contentView.readableContentGuide.trailingAnchor),
- passwordRuleLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
- ])
- }
-
-}
diff --git a/Mastodon/Scene/Onboarding/Register/Cell/MastodonRegisterTextFieldTableViewCell.swift b/Mastodon/Scene/Onboarding/Register/Cell/MastodonRegisterTextFieldTableViewCell.swift
deleted file mode 100644
index 15f234834..000000000
--- a/Mastodon/Scene/Onboarding/Register/Cell/MastodonRegisterTextFieldTableViewCell.swift
+++ /dev/null
@@ -1,142 +0,0 @@
-//
-// MastodonRegisterTextFieldTableViewCell.swift
-// Mastodon
-//
-// Created by MainasuK on 2022-1-7.
-//
-
-import UIKit
-import Combine
-import MastodonUI
-import MastodonAsset
-import MastodonLocalization
-
-final class MastodonRegisterTextFieldTableViewCell: UITableViewCell {
-
- static let textFieldHeight: CGFloat = 50
- static let textFieldLabelFont = UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 17, weight: .semibold), maximumPointSize: 22)
-
- var disposeBag = Set()
-
- let textFieldShadowContainer = ShadowBackgroundContainer()
- let textField: UITextField = {
- let textField = UITextField()
- textField.font = MastodonRegisterTextFieldTableViewCell.textFieldLabelFont
- textField.backgroundColor = Asset.Scene.Onboarding.textFieldBackground.color
- textField.layer.masksToBounds = true
- textField.layer.cornerRadius = 10
- textField.layer.cornerCurve = .continuous
- return textField
- }()
-
- override func prepareForReuse() {
- super.prepareForReuse()
-
- disposeBag.removeAll()
- textFieldShadowContainer.shadowColor = .black
- textFieldShadowContainer.shadowAlpha = 0.25
- resetTextField()
- }
-
- override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
- super.init(style: style, reuseIdentifier: reuseIdentifier)
- _init()
- }
-
- required init?(coder: NSCoder) {
- super.init(coder: coder)
- _init()
- }
-
-}
-
-extension MastodonRegisterTextFieldTableViewCell {
-
- private func _init() {
- selectionStyle = .none
- backgroundColor = .clear
-
- textFieldShadowContainer.translatesAutoresizingMaskIntoConstraints = false
- contentView.addSubview(textFieldShadowContainer)
- NSLayoutConstraint.activate([
- textFieldShadowContainer.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 6),
- textFieldShadowContainer.leadingAnchor.constraint(equalTo: contentView.readableContentGuide.leadingAnchor),
- textFieldShadowContainer.trailingAnchor.constraint(equalTo: contentView.readableContentGuide.trailingAnchor),
- contentView.bottomAnchor.constraint(equalTo: textFieldShadowContainer.bottomAnchor, constant: 6),
- ])
-
- textField.translatesAutoresizingMaskIntoConstraints = false
- textFieldShadowContainer.addSubview(textField)
- NSLayoutConstraint.activate([
- textField.topAnchor.constraint(equalTo: textFieldShadowContainer.topAnchor),
- textField.leadingAnchor.constraint(equalTo: textFieldShadowContainer.leadingAnchor),
- textField.trailingAnchor.constraint(equalTo: textFieldShadowContainer.trailingAnchor),
- textField.bottomAnchor.constraint(equalTo: textFieldShadowContainer.bottomAnchor),
- textField.heightAnchor.constraint(equalToConstant: MastodonRegisterTextFieldTableViewCell.textFieldHeight).priority(.required - 1),
- ])
-
- resetTextField()
- }
-
-}
-
-extension MastodonRegisterTextFieldTableViewCell {
- func resetTextField() {
- textField.keyboardType = .default
- textField.autocorrectionType = .default
- textField.autocapitalizationType = .none
- textField.attributedPlaceholder = nil
- textField.isSecureTextEntry = false
- textField.textAlignment = .natural
- textField.semanticContentAttribute = .unspecified
-
- let paddingRect = CGRect(x: 0, y: 0, width: 16, height: 10)
- textField.leftView = UIView(frame: paddingRect)
- textField.leftViewMode = .always
- textField.rightView = UIView(frame: paddingRect)
- textField.rightViewMode = .always
- }
-
- func setupTextViewRightView(text: String) {
- textField.rightView = {
- let containerView = UIView()
-
- let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: 8, height: MastodonRegisterTextFieldTableViewCell.textFieldHeight))
- paddingView.translatesAutoresizingMaskIntoConstraints = false
- containerView.addSubview(paddingView)
- NSLayoutConstraint.activate([
- paddingView.topAnchor.constraint(equalTo: containerView.topAnchor),
- paddingView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
- paddingView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor),
- paddingView.widthAnchor.constraint(equalToConstant: 8).priority(.defaultHigh),
- ])
-
- let label = UILabel()
- label.font = MastodonRegisterTextFieldTableViewCell.textFieldLabelFont
- label.textColor = Asset.Colors.Label.primary.color
- label.text = text
- label.lineBreakMode = .byTruncatingMiddle
-
- label.translatesAutoresizingMaskIntoConstraints = false
- containerView.addSubview(label)
- NSLayoutConstraint.activate([
- label.topAnchor.constraint(equalTo: containerView.topAnchor),
- label.leadingAnchor.constraint(equalTo: paddingView.trailingAnchor),
- containerView.trailingAnchor.constraint(equalTo: label.trailingAnchor, constant: 16),
- label.bottomAnchor.constraint(equalTo: containerView.bottomAnchor),
- label.widthAnchor.constraint(lessThanOrEqualToConstant: 180).priority(.required - 1),
- ])
- return containerView
- }()
- }
-
- func setupTextViewPlaceholder(text: String) {
- textField.attributedPlaceholder = NSAttributedString(
- string: text,
- attributes: [
- .foregroundColor: Asset.Colors.Label.secondary.color,
- .font: MastodonRegisterTextFieldTableViewCell.textFieldLabelFont
- ]
- )
- }
-}
diff --git a/Mastodon/Scene/Onboarding/Register/MastodonRegisterView.swift b/Mastodon/Scene/Onboarding/Register/MastodonRegisterView.swift
index 2be7c61d7..e5ca2798a 100644
--- a/Mastodon/Scene/Onboarding/Register/MastodonRegisterView.swift
+++ b/Mastodon/Scene/Onboarding/Register/MastodonRegisterView.swift
@@ -55,19 +55,10 @@ struct MastodonRegisterView: View {
// Delete
if viewModel.avatarImage != nil {
Divider()
- if #available(iOS 15.0, *) {
- Button(role: .destructive) {
- viewModel.avatarMediaMenuActionPublisher.send(.delete)
- } label: {
- Label(L10n.Scene.Register.Input.Avatar.delete, systemImage: "delete.left")
- }
- } else {
- // Fallback on earlier ve rsions
- Button {
- viewModel.avatarMediaMenuActionPublisher.send(.delete)
- } label: {
- Label(L10n.Scene.Register.Input.Avatar.delete, systemImage: "delete.left")
- }
+ Button(role: .destructive) {
+ viewModel.avatarMediaMenuActionPublisher.send(.delete)
+ } label: {
+ Label(L10n.Scene.Register.Input.Avatar.delete, systemImage: "delete.left")
}
}
} label: {
@@ -168,21 +159,27 @@ struct MastodonRegisterView: View {
func body(content: Content) -> some View {
ZStack {
- let shadowColor: Color = {
+ let borderColor: Color = {
switch validateState {
- case .empty: return .black.opacity(0.125)
- case .invalid: return Color(Asset.Colors.TextField.invalid.color)
- case .valid: return Color(Asset.Colors.TextField.valid.color)
+ case .empty: return Color(Asset.Scene.Onboarding.textFieldBackground.color)
+ case .invalid: return Color(Asset.Colors.TextField.invalid.color)
+ case .valid: return Color(Asset.Scene.Onboarding.textFieldBackground.color)
}
}()
+
Color(Asset.Scene.Onboarding.textFieldBackground.color)
.cornerRadius(10)
- .shadow(color: shadowColor, radius: 1, x: 0, y: 2)
- .animation(.easeInOut, value: validateState)
+ .shadow(color: .black.opacity(0.125), radius: 1, x: 0, y: 2)
+
content
.padding()
.background(Color(Asset.Scene.Onboarding.textFieldBackground.color))
.cornerRadius(10)
+ .overlay(
+ RoundedRectangle(cornerRadius: 10)
+ .stroke(borderColor, lineWidth: 1)
+ .animation(.easeInOut, value: validateState)
+ )
}
}
}
diff --git a/Mastodon/Scene/Onboarding/Register/MastodonRegisterViewController+Avatar.swift b/Mastodon/Scene/Onboarding/Register/MastodonRegisterViewController+Avatar.swift
index 9260f9e21..81924d812 100644
--- a/Mastodon/Scene/Onboarding/Register/MastodonRegisterViewController+Avatar.swift
+++ b/Mastodon/Scene/Onboarding/Register/MastodonRegisterViewController+Avatar.swift
@@ -49,7 +49,7 @@ extension MastodonRegisterViewController: PHPickerViewControllerDelegate {
let alertController = UIAlertController(for: error, title: "", preferredStyle: .alert)
let okAction = UIAlertAction(title: L10n.Common.Controls.Actions.ok, style: .default, handler: nil)
alertController.addAction(okAction)
- self.coordinator.present(
+ _ = self.coordinator.present(
scene: .alertController(alertController: alertController),
from: nil,
transition: .alertController(animated: true, completion: nil)
diff --git a/Mastodon/Scene/Onboarding/Register/MastodonRegisterViewController.swift b/Mastodon/Scene/Onboarding/Register/MastodonRegisterViewController.swift
index 9b10bd48e..f239e59b7 100644
--- a/Mastodon/Scene/Onboarding/Register/MastodonRegisterViewController.swift
+++ b/Mastodon/Scene/Onboarding/Register/MastodonRegisterViewController.swift
@@ -85,12 +85,7 @@ extension MastodonRegisterViewController {
addChild(hostingViewController)
hostingViewController.view.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(hostingViewController.view)
- NSLayoutConstraint.activate([
- hostingViewController.view.topAnchor.constraint(equalTo: view.topAnchor),
- hostingViewController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor),
- hostingViewController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor),
- hostingViewController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor),
- ])
+ hostingViewController.view.pinToParent()
navigationActionView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(navigationActionView)
@@ -145,7 +140,7 @@ extension MastodonRegisterViewController {
let alertController = UIAlertController(for: error, title: "Sign Up Failure", preferredStyle: .alert)
let okAction = UIAlertAction(title: L10n.Common.Controls.Actions.ok, style: .default, handler: nil)
alertController.addAction(okAction)
- self.coordinator.present(
+ _ = self.coordinator.present(
scene: .alertController(alertController: alertController),
from: nil,
transition: .alertController(animated: true, completion: nil)
@@ -322,7 +317,7 @@ extension MastodonRegisterViewController {
)
}()
let viewModel = MastodonConfirmEmailViewModel(context: self.context, email: email, authenticateInfo: self.viewModel.authenticateInfo, userToken: userToken, updateCredentialQuery: updateCredentialQuery)
- self.coordinator.present(scene: .mastodonConfirmEmail(viewModel: viewModel), from: self, transition: .show)
+ _ = self.coordinator.present(scene: .mastodonConfirmEmail(viewModel: viewModel), from: self, transition: .show)
}
.store(in: &disposeBag)
}
diff --git a/Mastodon/Scene/Onboarding/Register/MastodonRegisterViewModel+Diffable.swift b/Mastodon/Scene/Onboarding/Register/MastodonRegisterViewModel+Diffable.swift
index dda59843a..b3e2a2cdb 100644
--- a/Mastodon/Scene/Onboarding/Register/MastodonRegisterViewModel+Diffable.swift
+++ b/Mastodon/Scene/Onboarding/Register/MastodonRegisterViewModel+Diffable.swift
@@ -10,145 +10,6 @@ import Combine
import MastodonAsset
import MastodonLocalization
-extension MastodonRegisterViewModel {
- func setupDiffableDataSource(
- tableView: UITableView
- ) {
- tableView.register(OnboardingHeadlineTableViewCell.self, forCellReuseIdentifier: String(describing: OnboardingHeadlineTableViewCell.self))
- tableView.register(MastodonRegisterAvatarTableViewCell.self, forCellReuseIdentifier: String(describing: MastodonRegisterAvatarTableViewCell.self))
- tableView.register(MastodonRegisterTextFieldTableViewCell.self, forCellReuseIdentifier: String(describing: MastodonRegisterTextFieldTableViewCell.self))
- tableView.register(MastodonRegisterPasswordHintTableViewCell.self, forCellReuseIdentifier: String(describing: MastodonRegisterPasswordHintTableViewCell.self))
-
- diffableDataSource = UITableViewDiffableDataSource(tableView: tableView) { tableView, indexPath, item in
- switch item {
- case .header(let domain):
- let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: OnboardingHeadlineTableViewCell.self), for: indexPath) as! OnboardingHeadlineTableViewCell
- cell.titleLabel.text = L10n.Scene.Register.letsGetYouSetUpOnDomain(domain)
- cell.subTitleLabel.isHidden = true
- return cell
- case .avatar:
- let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: MastodonRegisterAvatarTableViewCell.self), for: indexPath) as! MastodonRegisterAvatarTableViewCell
- self.configureAvatar(cell: cell)
- return cell
- case .name:
- let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: MastodonRegisterTextFieldTableViewCell.self), for: indexPath) as! MastodonRegisterTextFieldTableViewCell
- cell.setupTextViewPlaceholder(text: L10n.Scene.Register.Input.DisplayName.placeholder)
- cell.textField.keyboardType = .default
- cell.textField.autocapitalizationType = .words
- cell.textField.text = self.name
- NotificationCenter.default.publisher(for: UITextField.textDidChangeNotification, object: cell.textField)
- .receive(on: DispatchQueue.main)
- .compactMap { notification in
- guard let textField = notification.object as? UITextField else {
- assertionFailure()
- return nil
- }
- return textField.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
- }
- .assign(to: \.name, on: self)
- .store(in: &cell.disposeBag)
- return cell
- case .username:
- let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: MastodonRegisterTextFieldTableViewCell.self), for: indexPath) as! MastodonRegisterTextFieldTableViewCell
- cell.setupTextViewRightView(text: "@" + self.domain)
- cell.setupTextViewPlaceholder(text: L10n.Scene.Register.Input.Username.placeholder)
- cell.textField.keyboardType = .alphabet
- cell.textField.autocorrectionType = .no
- cell.textField.text = self.username
- cell.textField.textAlignment = .left
- cell.textField.semanticContentAttribute = .forceLeftToRight
- NotificationCenter.default.publisher(for: UITextField.textDidChangeNotification, object: cell.textField)
- .receive(on: DispatchQueue.main)
- .compactMap { notification in
- guard let textField = notification.object as? UITextField else {
- assertionFailure()
- return nil
- }
- return textField.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
- }
- .assign(to: \.username, on: self)
- .store(in: &cell.disposeBag)
- self.configureTextFieldCell(cell: cell, validateState: self.$usernameValidateState)
- return cell
- case .email:
- let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: MastodonRegisterTextFieldTableViewCell.self), for: indexPath) as! MastodonRegisterTextFieldTableViewCell
- cell.setupTextViewPlaceholder(text: L10n.Scene.Register.Input.Email.placeholder)
- cell.textField.keyboardType = .emailAddress
- cell.textField.autocorrectionType = .no
- cell.textField.text = self.email
- NotificationCenter.default.publisher(for: UITextField.textDidChangeNotification, object: cell.textField)
- .receive(on: DispatchQueue.main)
- .compactMap { notification in
- guard let textField = notification.object as? UITextField else {
- assertionFailure()
- return nil
- }
- return textField.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
- }
- .assign(to: \.email, on: self)
- .store(in: &cell.disposeBag)
- self.configureTextFieldCell(cell: cell, validateState: self.$emailValidateState)
- return cell
- case .password:
- let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: MastodonRegisterTextFieldTableViewCell.self), for: indexPath) as! MastodonRegisterTextFieldTableViewCell
- cell.setupTextViewPlaceholder(text: L10n.Scene.Register.Input.Password.placeholder)
- cell.textField.keyboardType = .alphabet
- cell.textField.autocorrectionType = .no
- cell.textField.isSecureTextEntry = true
- cell.textField.text = self.password
- cell.textField.textAlignment = .left
- cell.textField.semanticContentAttribute = .forceLeftToRight
- NotificationCenter.default.publisher(for: UITextField.textDidChangeNotification, object: cell.textField)
- .receive(on: DispatchQueue.main)
- .compactMap { notification in
- guard let textField = notification.object as? UITextField else {
- assertionFailure()
- return nil
- }
- return textField.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
- }
- .assign(to: \.password, on: self)
- .store(in: &cell.disposeBag)
- self.configureTextFieldCell(cell: cell, validateState: self.$passwordValidateState)
- return cell
- case .hint:
- let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: MastodonRegisterPasswordHintTableViewCell.self), for: indexPath) as! MastodonRegisterPasswordHintTableViewCell
- return cell
- case .reason:
- let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: MastodonRegisterTextFieldTableViewCell.self), for: indexPath) as! MastodonRegisterTextFieldTableViewCell
- cell.setupTextViewPlaceholder(text: L10n.Scene.Register.Input.Invite.registrationUserInviteRequest)
- cell.textField.keyboardType = .default
- cell.textField.text = self.reason
- NotificationCenter.default.publisher(for: UITextField.textDidChangeNotification, object: cell.textField)
- .receive(on: DispatchQueue.main)
- .compactMap { notification in
- guard let textField = notification.object as? UITextField else {
- assertionFailure()
- return nil
- }
- return textField.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
- }
- .assign(to: \.reason, on: self)
- .store(in: &cell.disposeBag)
- self.configureTextFieldCell(cell: cell, validateState: self.$reasonValidateState)
- return cell
- default:
- assertionFailure()
- return UITableViewCell()
- }
- }
-
- var snapshot = NSDiffableDataSourceSnapshot()
- snapshot.appendSections([.main])
- snapshot.appendItems([.header(domain: domain)], toSection: .main)
- snapshot.appendItems([.avatar, .name, .username, .email, .password, .hint], toSection: .main)
- if approvalRequired {
- snapshot.appendItems([.reason], toSection: .main)
- }
- diffableDataSource?.applySnapshot(snapshot, animated: false, completion: nil)
- }
-}
-
extension MastodonRegisterViewModel {
private func configureAvatar(cell: MastodonRegisterAvatarTableViewCell) {
self.$avatarImage
diff --git a/Mastodon/Scene/Onboarding/ResendEmail/MastodonResendEmailViewController.swift b/Mastodon/Scene/Onboarding/ResendEmail/MastodonResendEmailViewController.swift
index 178d489be..d6870785c 100644
--- a/Mastodon/Scene/Onboarding/ResendEmail/MastodonResendEmailViewController.swift
+++ b/Mastodon/Scene/Onboarding/ResendEmail/MastodonResendEmailViewController.swift
@@ -49,12 +49,7 @@ extension MastodonResendEmailViewController {
webView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(webView)
- NSLayoutConstraint.activate([
- webView.topAnchor.constraint(equalTo: view.topAnchor),
- webView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
- webView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
- webView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
- ])
+ webView.pinToParent()
let request = URLRequest(url: viewModel.resendEmailURL)
webView.navigationDelegate = self.viewModel.navigationDelegate
diff --git a/Mastodon/Scene/Onboarding/ServerRules/MastodonServerRulesViewController.swift b/Mastodon/Scene/Onboarding/ServerRules/MastodonServerRulesViewController.swift
index 0d4e27d98..f850991f2 100644
--- a/Mastodon/Scene/Onboarding/ServerRules/MastodonServerRulesViewController.swift
+++ b/Mastodon/Scene/Onboarding/ServerRules/MastodonServerRulesViewController.swift
@@ -37,11 +37,7 @@ final class MastodonServerRulesViewController: UIViewController, NeedsDependency
tableView.separatorStyle = .none
tableView.backgroundColor = .clear
tableView.keyboardDismissMode = .onDrag
- if #available(iOS 15.0, *) {
- tableView.sectionHeaderTopPadding = 0
- } else {
- // Fallback on earlier versions
- }
+ tableView.sectionHeaderTopPadding = 0
return tableView
}()
@@ -69,12 +65,7 @@ extension MastodonServerRulesViewController {
tableView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(tableView)
- NSLayoutConstraint.activate([
- tableView.topAnchor.constraint(equalTo: view.topAnchor),
- tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
- tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
- tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
- ])
+ tableView.pinToParent()
navigationActionView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(navigationActionView)
@@ -127,7 +118,7 @@ extension MastodonServerRulesViewController {
instance: viewModel.instance,
applicationToken: viewModel.applicationToken
)
- coordinator.present(scene: .mastodonRegister(viewModel: viewModel), from: self, transition: .show)
+ _ = coordinator.present(scene: .mastodonRegister(viewModel: viewModel), from: self, transition: .show)
}
}
diff --git a/Mastodon/Scene/Onboarding/ServerRules/MastodonServerRulesViewModel+Diffable.swift b/Mastodon/Scene/Onboarding/ServerRules/MastodonServerRulesViewModel+Diffable.swift
index f6385a529..ecb868eb0 100644
--- a/Mastodon/Scene/Onboarding/ServerRules/MastodonServerRulesViewModel+Diffable.swift
+++ b/Mastodon/Scene/Onboarding/ServerRules/MastodonServerRulesViewModel+Diffable.swift
@@ -21,6 +21,6 @@ extension MastodonServerRulesViewModel {
return ServerRuleItem.rule(ruleContext)
}
snapshot.appendItems(ruleItems, toSection: .rules)
- diffableDataSource?.applySnapshot(snapshot, animated: false, completion: nil)
+ diffableDataSource?.apply(snapshot, animatingDifferences: false)
}
}
diff --git a/Mastodon/Scene/Onboarding/Share/NavigationActionView.swift b/Mastodon/Scene/Onboarding/Share/NavigationActionView.swift
index c3236bdb4..fce853959 100644
--- a/Mastodon/Scene/Onboarding/Share/NavigationActionView.swift
+++ b/Mastodon/Scene/Onboarding/Share/NavigationActionView.swift
@@ -13,6 +13,7 @@ import MastodonLocalization
final class NavigationActionView: UIView {
static let buttonHeight: CGFloat = 50
+ static let minimumBackButtonWidth: CGFloat = 100
private var observations = Set()
@@ -27,6 +28,8 @@ final class NavigationActionView: UIView {
let backButton: PrimaryActionButton = {
let button = PrimaryActionButton()
button.action = .back
+ button.contentEdgeInsets = WelcomeViewController.actionButtonPadding
+ button.titleLabel?.adjustsFontForContentSizeCategory = true
button.setTitle(L10n.Common.Controls.Actions.back, for: .normal)
return button
}()
@@ -35,6 +38,8 @@ final class NavigationActionView: UIView {
let nextButton: PrimaryActionButton = {
let button = PrimaryActionButton()
button.action = .next
+ button.contentEdgeInsets = WelcomeViewController.actionButtonPadding
+ button.titleLabel?.adjustsFontForContentSizeCategory = true
button.setTitle(L10n.Common.Controls.Actions.next, for: .normal)
return button
}()
@@ -77,27 +82,27 @@ extension NavigationActionView {
nextButtonShadowContainer.translatesAutoresizingMaskIntoConstraints = false
buttonContainer.addArrangedSubview(nextButtonShadowContainer)
NSLayoutConstraint.activate([
- backButtonShadowContainer.heightAnchor.constraint(equalToConstant: NavigationActionView.buttonHeight).priority(.required - 1),
- nextButtonShadowContainer.heightAnchor.constraint(equalToConstant: NavigationActionView.buttonHeight).priority(.required - 1),
- nextButtonShadowContainer.widthAnchor.constraint(equalTo: backButtonShadowContainer.widthAnchor, multiplier: 2).priority(.required - 1),
+ backButtonShadowContainer.heightAnchor.constraint(greaterThanOrEqualToConstant: NavigationActionView.buttonHeight).priority(.required - 1),
+ nextButtonShadowContainer.heightAnchor.constraint(greaterThanOrEqualToConstant: NavigationActionView.buttonHeight).priority(.required - 1),
])
backButton.translatesAutoresizingMaskIntoConstraints = false
backButtonShadowContainer.addSubview(backButton)
- NSLayoutConstraint.activate([
- backButton.topAnchor.constraint(equalTo: backButtonShadowContainer.topAnchor),
- backButton.leadingAnchor.constraint(equalTo: backButtonShadowContainer.leadingAnchor),
- backButton.trailingAnchor.constraint(equalTo: backButtonShadowContainer.trailingAnchor),
- backButton.bottomAnchor.constraint(equalTo: backButtonShadowContainer.bottomAnchor),
- ])
+ backButton.pinToParent()
nextButton.translatesAutoresizingMaskIntoConstraints = false
- nextButtonShadowContainer.addSubview(nextButton)
+ nextButtonShadowContainer.addSubview(nextButton)
+ nextButton.pinToParent()
+
+ // We want the back button to be as small as possible, allowing the next button to take up
+ // any remaining space. .defaultLow is "the priority level at which a button hugs its
+ // contents horizontally". Setting this on backButton allows nextButton to eat up remaining
+ // space. Note that we have to set this on the backButton, not the container, because it's
+ // backButton's size that determines the compression amount.
+ backButton.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
+ // Ensure that the back button has a reasonable minimum tap area.
NSLayoutConstraint.activate([
- nextButton.topAnchor.constraint(equalTo: nextButtonShadowContainer.topAnchor),
- nextButton.leadingAnchor.constraint(equalTo: nextButtonShadowContainer.leadingAnchor),
- nextButton.trailingAnchor.constraint(equalTo: nextButtonShadowContainer.trailingAnchor),
- nextButton.bottomAnchor.constraint(equalTo: nextButtonShadowContainer.bottomAnchor),
+ backButton.widthAnchor.constraint(greaterThanOrEqualToConstant: NavigationActionView.minimumBackButtonWidth).priority(.defaultLow - 1)
])
}
diff --git a/Mastodon/Scene/Onboarding/Share/OnboardingNavigationController.swift b/Mastodon/Scene/Onboarding/Share/OnboardingNavigationController.swift
index 537102dc9..ac2e5b171 100644
--- a/Mastodon/Scene/Onboarding/Share/OnboardingNavigationController.swift
+++ b/Mastodon/Scene/Onboarding/Share/OnboardingNavigationController.swift
@@ -20,12 +20,7 @@ extension OnboardingNavigationController {
gradientBorderView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(gradientBorderView)
- NSLayoutConstraint.activate([
- gradientBorderView.topAnchor.constraint(equalTo: view.topAnchor),
- gradientBorderView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
- gradientBorderView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
- gradientBorderView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
- ])
+ gradientBorderView.pinToParent()
updateBorderViewDisplay()
}
diff --git a/Mastodon/Scene/Onboarding/Share/OnboardingViewControllerAppearance.swift b/Mastodon/Scene/Onboarding/Share/OnboardingViewControllerAppearance.swift
index ba1eecfc5..89d77435d 100644
--- a/Mastodon/Scene/Onboarding/Share/OnboardingViewControllerAppearance.swift
+++ b/Mastodon/Scene/Onboarding/Share/OnboardingViewControllerAppearance.swift
@@ -22,6 +22,10 @@ extension OnboardingViewControllerAppearance {
static var actionButtonMarginExtend: CGFloat { return 80 }
static var viewBottomPaddingHeight: CGFloat { return 11 }
static var viewBottomPaddingHeightExtend: CGFloat { return 22 }
+
+ // Typically assigned to the button's contentEdgeInsets. Ensures space around content, even when
+ // content is large due to Dynamic Type.
+ static var actionButtonPadding: UIEdgeInsets { return UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8) }
static var largeTitleFont: UIFont {
return UIFontMetrics(forTextStyle: .largeTitle).scaledFont(for: .systemFont(ofSize: 28, weight: .bold))
@@ -62,11 +66,7 @@ extension OnboardingViewControllerAppearance {
navigationItem.standardAppearance = barAppearance
navigationItem.compactAppearance = barAppearance
navigationItem.scrollEdgeAppearance = barAppearance
- if #available(iOS 15.0, *) {
- navigationItem.compactScrollEdgeAppearance = barAppearance
- } else {
- // Fallback on earlier versions
- }
+ navigationItem.compactScrollEdgeAppearance = barAppearance
}
func setupNavigationBarBackgroundView() {
diff --git a/Mastodon/Scene/Onboarding/Welcome/View/WelcomeIllustrationView.swift b/Mastodon/Scene/Onboarding/Welcome/View/WelcomeIllustrationView.swift
index 0530539a2..1fca1afef 100644
--- a/Mastodon/Scene/Onboarding/Welcome/View/WelcomeIllustrationView.swift
+++ b/Mastodon/Scene/Onboarding/Welcome/View/WelcomeIllustrationView.swift
@@ -96,12 +96,7 @@ extension WelcomeIllustrationView {
].forEach { imageView in
imageView.translatesAutoresizingMaskIntoConstraints = false
addSubview(imageView)
- NSLayoutConstraint.activate([
- imageView.topAnchor.constraint(equalTo: cloudBaseImageView.topAnchor),
- imageView.leadingAnchor.constraint(equalTo: cloudBaseImageView.leadingAnchor),
- imageView.trailingAnchor.constraint(equalTo: cloudBaseImageView.trailingAnchor),
- imageView.bottomAnchor.constraint(equalTo: cloudBaseImageView.bottomAnchor),
- ])
+ imageView.pinTo(to: cloudBaseImageView)
}
aspectLayoutConstraint = cloudBaseImageView.widthAnchor.constraint(equalTo: cloudBaseImageView.heightAnchor, multiplier: layout.artworkImageSize.width / layout.artworkImageSize.height)
diff --git a/Mastodon/Scene/Onboarding/Welcome/WelcomeViewController.swift b/Mastodon/Scene/Onboarding/Welcome/WelcomeViewController.swift
index a2b8df83a..b0d2f3c71 100644
--- a/Mastodon/Scene/Onboarding/Welcome/WelcomeViewController.swift
+++ b/Mastodon/Scene/Onboarding/Welcome/WelcomeViewController.swift
@@ -51,6 +51,8 @@ final class WelcomeViewController: UIViewController, NeedsDependency {
private(set) lazy var signUpButton: PrimaryActionButton = {
let button = PrimaryActionButton()
button.adjustsBackgroundImageWhenUserInterfaceStyleChanges = false
+ button.contentEdgeInsets = WelcomeViewController.actionButtonPadding
+ button.titleLabel?.adjustsFontForContentSizeCategory = true
button.titleLabel?.font = UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 17, weight: .semibold))
button.setTitle(L10n.Common.Controls.Actions.signUp, for: .normal)
let backgroundImageColor: UIColor = .white
@@ -65,6 +67,8 @@ final class WelcomeViewController: UIViewController, NeedsDependency {
private(set) lazy var signInButton: PrimaryActionButton = {
let button = PrimaryActionButton()
button.adjustsBackgroundImageWhenUserInterfaceStyleChanges = false
+ button.contentEdgeInsets = WelcomeViewController.actionButtonPadding
+ button.titleLabel?.adjustsFontForContentSizeCategory = true
button.titleLabel?.font = UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 17, weight: .semibold))
button.setTitle(L10n.Scene.Welcome.logIn, for: .normal)
let backgroundImageColor = Asset.Scene.Welcome.signInButtonBackground.color
@@ -113,33 +117,23 @@ extension WelcomeViewController {
signUpButton.translatesAutoresizingMaskIntoConstraints = false
buttonContainer.addArrangedSubview(signUpButton)
NSLayoutConstraint.activate([
- signUpButton.heightAnchor.constraint(equalToConstant: WelcomeViewController.actionButtonHeight).priority(.required - 1),
+ signUpButton.heightAnchor.constraint(greaterThanOrEqualToConstant: WelcomeViewController.actionButtonHeight).priority(.required - 1),
])
signInButton.translatesAutoresizingMaskIntoConstraints = false
buttonContainer.addArrangedSubview(signInButton)
NSLayoutConstraint.activate([
- signInButton.heightAnchor.constraint(equalToConstant: WelcomeViewController.actionButtonHeight).priority(.required - 1),
+ signInButton.heightAnchor.constraint(greaterThanOrEqualToConstant: WelcomeViewController.actionButtonHeight).priority(.required - 1),
])
signUpButtonShadowView.translatesAutoresizingMaskIntoConstraints = false
buttonContainer.addSubview(signUpButtonShadowView)
buttonContainer.sendSubviewToBack(signUpButtonShadowView)
- NSLayoutConstraint.activate([
- signUpButtonShadowView.topAnchor.constraint(equalTo: signUpButton.topAnchor),
- signUpButtonShadowView.leadingAnchor.constraint(equalTo: signUpButton.leadingAnchor),
- signUpButtonShadowView.trailingAnchor.constraint(equalTo: signUpButton.trailingAnchor),
- signUpButtonShadowView.bottomAnchor.constraint(equalTo: signUpButton.bottomAnchor),
- ])
+ signUpButtonShadowView.pinTo(to: signUpButton)
signInButtonShadowView.translatesAutoresizingMaskIntoConstraints = false
buttonContainer.addSubview(signInButtonShadowView)
buttonContainer.sendSubviewToBack(signInButtonShadowView)
- NSLayoutConstraint.activate([
- signInButtonShadowView.topAnchor.constraint(equalTo: signInButton.topAnchor),
- signInButtonShadowView.leadingAnchor.constraint(equalTo: signInButton.leadingAnchor),
- signInButtonShadowView.trailingAnchor.constraint(equalTo: signInButton.trailingAnchor),
- signInButtonShadowView.bottomAnchor.constraint(equalTo: signInButton.bottomAnchor),
- ])
+ signInButtonShadowView.pinTo(to: signInButton)
signUpButton.addTarget(self, action: #selector(signUpButtonDidClicked(_:)), for: .touchUpInside)
signInButton.addTarget(self, action: #selector(signInButtonDidClicked(_:)), for: .touchUpInside)
@@ -172,7 +166,9 @@ extension WelcomeViewController {
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
-
+
+ view.layoutIfNeeded()
+
setupIllustrationLayout()
setupButtonShadowView()
}
@@ -189,7 +185,7 @@ extension WelcomeViewController {
y: 1,
blur: 2,
spread: 0,
- roundedRect: signInButtonShadowView.bounds,
+ roundedRect: signUpButtonShadowView.bounds,
byRoundingCorners: .allCorners,
cornerRadii: CGSize(width: 10, height: 10)
)
@@ -243,7 +239,7 @@ extension WelcomeViewController {
logoImageView.topAnchor.constraint(equalTo: view.layoutMarginsGuide.topAnchor),
logoImageView.leadingAnchor.constraint(equalTo: view.readableContentGuide.leadingAnchor, constant: 35),
view.readableContentGuide.trailingAnchor.constraint(equalTo: logoImageView.trailingAnchor, constant: 35),
- logoImageView.heightAnchor.constraint(equalTo: logoImageView.widthAnchor, multiplier: 65.4/265.1),
+ logoImageView.heightAnchor.constraint(equalTo: logoImageView.widthAnchor, multiplier: 75.0/269.0),
])
logoImageView.setContentHuggingPriority(.defaultHigh, for: .vertical)
}
@@ -317,12 +313,12 @@ extension WelcomeViewController {
extension WelcomeViewController {
@objc
private func signUpButtonDidClicked(_ sender: UIButton) {
- coordinator.present(scene: .mastodonPickServer(viewMode: MastodonPickServerViewModel(context: context, mode: .signUp)), from: self, transition: .show)
+ _ = coordinator.present(scene: .mastodonPickServer(viewMode: MastodonPickServerViewModel(context: context)), from: self, transition: .show)
}
@objc
private func signInButtonDidClicked(_ sender: UIButton) {
- coordinator.present(scene: .mastodonPickServer(viewMode: MastodonPickServerViewModel(context: context, mode: .signIn)), from: self, transition: .show)
+ _ = coordinator.present(scene: .mastodonLogin, from: self, transition: .show)
}
@objc
@@ -340,11 +336,7 @@ extension WelcomeViewController: OnboardingViewControllerAppearance {
navigationItem.standardAppearance = barAppearance
navigationItem.compactAppearance = barAppearance
navigationItem.scrollEdgeAppearance = barAppearance
- if #available(iOS 15.0, *) {
- navigationItem.compactScrollEdgeAppearance = barAppearance
- } else {
- // Fallback on earlier versions
- }
+ navigationItem.compactScrollEdgeAppearance = barAppearance
}
}
diff --git a/Mastodon/Scene/Profile/About/Cell/ProfileFieldCollectionViewCell.swift b/Mastodon/Scene/Profile/About/Cell/ProfileFieldCollectionViewCell.swift
index 1ed76a485..171ce1ac3 100644
--- a/Mastodon/Scene/Profile/About/Cell/ProfileFieldCollectionViewCell.swift
+++ b/Mastodon/Scene/Profile/About/Cell/ProfileFieldCollectionViewCell.swift
@@ -58,7 +58,7 @@ extension ProfileFieldCollectionViewCell {
private func _init() {
// Setup colors
- checkmark.tintColor = Asset.Scene.Profile.About.bioAboutFieldVerifiedCheckmark.color;
+ checkmark.tintColor = Asset.Scene.Profile.About.bioAboutFieldVerifiedText.color;
// Setup gestures
tapGesture.addTarget(self, action: #selector(ProfileFieldCollectionViewCell.didTapCheckmark(_:)))
@@ -68,6 +68,11 @@ extension ProfileFieldCollectionViewCell {
checkmark.addInteraction(editMenuInteraction)
}
+ // Setup Accessibility
+ checkmark.isAccessibilityElement = true
+ checkmark.accessibilityTraits = .none
+ keyMetaLabel.accessibilityTraits = .none
+
// containerStackView: V - [ metaContainer | plainContainer ]
let containerStackView = UIStackView()
containerStackView.axis = .vertical
diff --git a/Mastodon/Scene/Profile/About/ProfileAboutViewController.swift b/Mastodon/Scene/Profile/About/ProfileAboutViewController.swift
index eb1e6b39c..fe92d2ac4 100644
--- a/Mastodon/Scene/Profile/About/ProfileAboutViewController.swift
+++ b/Mastodon/Scene/Profile/About/ProfileAboutViewController.swift
@@ -60,12 +60,7 @@ extension ProfileAboutViewController {
collectionView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(collectionView)
- NSLayoutConstraint.activate([
- collectionView.topAnchor.constraint(equalTo: view.topAnchor),
- collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
- collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
- collectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
- ])
+ collectionView.pinToParent()
collectionView.delegate = self
viewModel.setupDiffableDataSource(
diff --git a/Mastodon/Scene/Profile/About/ProfileAboutViewModel+Diffable.swift b/Mastodon/Scene/Profile/About/ProfileAboutViewModel+Diffable.swift
index 0a11a71f6..eb7a6aaae 100644
--- a/Mastodon/Scene/Profile/About/ProfileAboutViewModel+Diffable.swift
+++ b/Mastodon/Scene/Profile/About/ProfileAboutViewModel+Diffable.swift
@@ -50,23 +50,33 @@ extension ProfileAboutViewModel {
var snapshot = NSDiffableDataSourceSnapshot()
snapshot.appendSections([.main])
diffableDataSource.apply(snapshot)
-
- Publishers.CombineLatest4(
+
+ let fields = Publishers.CombineLatest3(
$isEditing.removeDuplicates(),
profileInfo.$fields.removeDuplicates(),
- profileInfoEditing.$fields.removeDuplicates(),
+ profileInfoEditing.$fields.removeDuplicates()
+ ).map { isEditing, displayFields, editingFields in
+ isEditing ? editingFields : displayFields
+ }
+
+
+ Publishers.CombineLatest4(
+ $isEditing.removeDuplicates(),
+ $createdAt.removeDuplicates(),
+ fields,
$emojiMeta.removeDuplicates()
)
.throttle(for: 0.3, scheduler: DispatchQueue.main, latest: true)
- .sink { [weak self] isEditing, displayFields, editingFields, emojiMeta in
+ .sink { [weak self] isEditing, createdAt, fields, emojiMeta in
guard let self = self else { return }
guard let diffableDataSource = self.diffableDataSource else { return }
var snapshot = NSDiffableDataSourceSnapshot()
snapshot.appendSections([.main])
- let fields: [ProfileFieldItem.FieldValue] = isEditing ? editingFields : displayFields
- var items: [ProfileFieldItem] = fields.map { field in
+ var items: [ProfileFieldItem] = [
+ .createdAt(date: createdAt),
+ ] + fields.map { field in
if isEditing {
return ProfileFieldItem.editField(field: field)
} else {
@@ -78,10 +88,6 @@ extension ProfileAboutViewModel {
items.append(.addEntry)
}
- if !isEditing, items.isEmpty {
- items.append(.noResult)
- }
-
snapshot.appendItems(items, toSection: .main)
diffableDataSource.apply(snapshot, animatingDifferences: false, completion: nil)
diff --git a/Mastodon/Scene/Profile/About/ProfileAboutViewModel.swift b/Mastodon/Scene/Profile/About/ProfileAboutViewModel.swift
index 044894b8a..8ab427c39 100644
--- a/Mastodon/Scene/Profile/About/ProfileAboutViewModel.swift
+++ b/Mastodon/Scene/Profile/About/ProfileAboutViewModel.swift
@@ -31,6 +31,7 @@ final class ProfileAboutViewModel {
@Published var fields: [MastodonField] = []
@Published var emojiMeta: MastodonContent.Emojis = [:]
+ @Published var createdAt: Date = Date()
init(context: AppContext) {
self.context = context
@@ -46,6 +47,11 @@ final class ProfileAboutViewModel {
.compactMap { $0 }
.flatMap { $0.publisher(for: \.fields) }
.assign(to: &$fields)
+
+ $user
+ .compactMap { $0 }
+ .flatMap { $0.publisher(for: \.createdAt) }
+ .assign(to: &$createdAt)
Publishers.CombineLatest(
$fields,
diff --git a/Mastodon/Scene/Profile/Bookmark/BookmarkViewController.swift b/Mastodon/Scene/Profile/Bookmark/BookmarkViewController.swift
index 5edec2618..458c47335 100644
--- a/Mastodon/Scene/Profile/Bookmark/BookmarkViewController.swift
+++ b/Mastodon/Scene/Profile/Bookmark/BookmarkViewController.swift
@@ -64,12 +64,7 @@ extension BookmarkViewController {
tableView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(tableView)
- NSLayoutConstraint.activate([
- tableView.topAnchor.constraint(equalTo: view.topAnchor),
- tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
- tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
- tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
- ])
+ tableView.pinToParent()
tableView.delegate = self
viewModel.setupDiffableDataSource(
diff --git a/Mastodon/Scene/Profile/Bookmark/BookmarkViewModel+Diffable.swift b/Mastodon/Scene/Profile/Bookmark/BookmarkViewModel+Diffable.swift
index 69075a8ce..d52309e92 100644
--- a/Mastodon/Scene/Profile/Bookmark/BookmarkViewModel+Diffable.swift
+++ b/Mastodon/Scene/Profile/Bookmark/BookmarkViewModel+Diffable.swift
@@ -17,6 +17,7 @@ extension BookmarkViewModel {
tableView: tableView,
context: context,
configuration: StatusSection.Configuration(
+ context: context,
authContext: authContext,
statusTableViewCellDelegate: statusTableViewCellDelegate,
timelineMiddleLoaderTableViewCellDelegate: nil,
@@ -58,7 +59,7 @@ extension BookmarkViewModel {
}
}
- diffableDataSource.applySnapshot(snapshot, animated: false)
+ diffableDataSource.apply(snapshot, animatingDifferences: false)
}
.store(in: &disposeBag)
}
diff --git a/Mastodon/Scene/Profile/FamiliarFollowers/FamiliarFollowersViewController.swift b/Mastodon/Scene/Profile/FamiliarFollowers/FamiliarFollowersViewController.swift
index b6d6f8313..3595303d9 100644
--- a/Mastodon/Scene/Profile/FamiliarFollowers/FamiliarFollowersViewController.swift
+++ b/Mastodon/Scene/Profile/FamiliarFollowers/FamiliarFollowersViewController.swift
@@ -53,12 +53,7 @@ extension FamiliarFollowersViewController {
tableView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(tableView)
- NSLayoutConstraint.activate([
- tableView.topAnchor.constraint(equalTo: view.topAnchor),
- tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
- tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
- tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
- ])
+ tableView.pinToParent()
tableView.delegate = self
viewModel.setupDiffableDataSource(
diff --git a/Mastodon/Scene/Profile/Favorite/FavoriteViewController.swift b/Mastodon/Scene/Profile/Favorite/FavoriteViewController.swift
index c15adcf83..19f25f3ad 100644
--- a/Mastodon/Scene/Profile/Favorite/FavoriteViewController.swift
+++ b/Mastodon/Scene/Profile/Favorite/FavoriteViewController.swift
@@ -67,12 +67,7 @@ extension FavoriteViewController {
tableView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(tableView)
- NSLayoutConstraint.activate([
- tableView.topAnchor.constraint(equalTo: view.topAnchor),
- tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
- tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
- tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
- ])
+ tableView.pinToParent()
tableView.delegate = self
viewModel.setupDiffableDataSource(
diff --git a/Mastodon/Scene/Profile/Favorite/FavoriteViewModel+Diffable.swift b/Mastodon/Scene/Profile/Favorite/FavoriteViewModel+Diffable.swift
index 3723dae5d..367a4d51f 100644
--- a/Mastodon/Scene/Profile/Favorite/FavoriteViewModel+Diffable.swift
+++ b/Mastodon/Scene/Profile/Favorite/FavoriteViewModel+Diffable.swift
@@ -17,6 +17,7 @@ extension FavoriteViewModel {
tableView: tableView,
context: context,
configuration: StatusSection.Configuration(
+ context: context,
authContext: authContext,
statusTableViewCellDelegate: statusTableViewCellDelegate,
timelineMiddleLoaderTableViewCellDelegate: nil,
@@ -58,7 +59,7 @@ extension FavoriteViewModel {
}
}
- diffableDataSource.applySnapshot(snapshot, animated: false)
+ diffableDataSource.apply(snapshot, animatingDifferences: false)
}
.store(in: &disposeBag)
}
diff --git a/Mastodon/Scene/Profile/Favorite/FavoriteViewModel+State.swift b/Mastodon/Scene/Profile/Favorite/FavoriteViewModel+State.swift
index 803a9d45e..c2dc27201 100644
--- a/Mastodon/Scene/Profile/Favorite/FavoriteViewModel+State.swift
+++ b/Mastodon/Scene/Profile/Favorite/FavoriteViewModel+State.swift
@@ -46,7 +46,7 @@ extension FavoriteViewModel {
extension FavoriteViewModel.State {
class Initial: FavoriteViewModel.State {
override func isValidNextState(_ stateClass: AnyClass) -> Bool {
- guard let viewModel = viewModel else { return false }
+ guard viewModel != nil else { return false }
switch stateClass {
case is Reloading.Type:
return true
@@ -130,13 +130,12 @@ extension FavoriteViewModel.State {
override func didEnter(from previousState: GKState?) {
super.didEnter(from: previousState)
- guard let viewModel = viewModel, let stateMachine = stateMachine else { return }
+ guard let viewModel else { return }
if previousState is Reloading {
maxID = nil
}
-
Task {
do {
let response = try await viewModel.context.apiService.favoritedStatuses(
diff --git a/Mastodon/Scene/Profile/FollowedTags/FollowedTagsTableViewCell.swift b/Mastodon/Scene/Profile/FollowedTags/FollowedTagsTableViewCell.swift
new file mode 100644
index 000000000..6adb15a9c
--- /dev/null
+++ b/Mastodon/Scene/Profile/FollowedTags/FollowedTagsTableViewCell.swift
@@ -0,0 +1,78 @@
+//
+// FollowedTagsTableViewCell.swift
+// Mastodon
+//
+// Created by Marcus Kida on 24.11.22.
+//
+
+import UIKit
+import CoreDataStack
+
+final class FollowedTagsTableViewCell: UITableViewCell {
+ private var hashtagView: HashtagTimelineHeaderView!
+ private let separatorLine = UIView.separatorLine
+ private weak var viewModel: FollowedTagsViewModel?
+ private weak var hashtag: Tag?
+
+ override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
+ super.init(style: style, reuseIdentifier: reuseIdentifier)
+ setup()
+ }
+
+ required init?(coder: NSCoder) {
+ fatalError("Not implemented")
+ }
+
+ override func prepareForReuse() {
+ hashtagView.removeFromSuperview()
+ viewModel = nil
+ hashtagView = nil
+ super.prepareForReuse()
+ setup()
+ }
+}
+
+private extension FollowedTagsTableViewCell {
+ func setup() {
+ selectionStyle = .none
+
+ hashtagView = HashtagTimelineHeaderView()
+ hashtagView.translatesAutoresizingMaskIntoConstraints = false
+
+ contentView.addSubview(hashtagView)
+ contentView.backgroundColor = .clear
+
+ NSLayoutConstraint.activate([
+ hashtagView.heightAnchor.constraint(equalToConstant: 118).priority(.required),
+ hashtagView.topAnchor.constraint(equalTo: contentView.topAnchor),
+ hashtagView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
+ hashtagView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
+ hashtagView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
+ ])
+
+ separatorLine.translatesAutoresizingMaskIntoConstraints = false
+ contentView.addSubview(separatorLine)
+ NSLayoutConstraint.activate([
+ separatorLine.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
+ separatorLine.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
+ separatorLine.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
+ separatorLine.heightAnchor.constraint(equalToConstant: UIView.separatorLineHeight(of: contentView)),
+ ])
+
+ hashtagView.onButtonTapped = { [weak self] in
+ guard let self = self, let tag = self.hashtag else { return }
+ self.viewModel?.followOrUnfollow(tag)
+ }
+ }
+}
+
+extension FollowedTagsTableViewCell {
+ func populate(with tag: Tag) {
+ self.hashtag = tag
+ hashtagView.update(HashtagTimelineHeaderView.Data.from(tag))
+ }
+
+ func setup(_ viewModel: FollowedTagsViewModel) {
+ self.viewModel = viewModel
+ }
+}
diff --git a/Mastodon/Scene/Profile/FollowedTags/FollowedTagsViewController.swift b/Mastodon/Scene/Profile/FollowedTags/FollowedTagsViewController.swift
new file mode 100644
index 000000000..9a31fe0c3
--- /dev/null
+++ b/Mastodon/Scene/Profile/FollowedTags/FollowedTagsViewController.swift
@@ -0,0 +1,77 @@
+//
+// FollowedTagsViewController.swift
+// Mastodon
+//
+// Created by Marcus Kida on 22.11.22.
+//
+
+import os
+import UIKit
+import Combine
+import MastodonAsset
+import MastodonCore
+import MastodonUI
+import MastodonLocalization
+
+final class FollowedTagsViewController: UIViewController, NeedsDependency {
+ let logger = Logger(subsystem: String(describing: FollowedTagsViewController.self), category: "ViewController")
+
+ weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
+ weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
+
+ var disposeBag = Set()
+ var viewModel: FollowedTagsViewModel!
+
+ let titleView = DoubleTitleLabelNavigationBarTitleView()
+
+ lazy var tableView: UITableView = {
+ let tableView = UITableView()
+ tableView.register(FollowedTagsTableViewCell.self, forCellReuseIdentifier: String(describing: FollowedTagsTableViewCell.self))
+ tableView.rowHeight = UITableView.automaticDimension
+ tableView.separatorStyle = .none
+ tableView.backgroundColor = .clear
+ return tableView
+ }()
+
+ deinit {
+ os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
+ }
+}
+
+extension FollowedTagsViewController {
+ override func viewDidLoad() {
+ super.viewDidLoad()
+
+ let _title = L10n.Scene.FollowedTags.title
+ title = _title
+ titleView.update(title: _title, subtitle: nil)
+
+ navigationItem.titleView = titleView
+
+ view.backgroundColor = ThemeService.shared.currentTheme.value.secondarySystemBackgroundColor
+ ThemeService.shared.currentTheme
+ .receive(on: RunLoop.main)
+ .sink { [weak self] theme in
+ guard let self = self else { return }
+ self.view.backgroundColor = theme.secondarySystemBackgroundColor
+ }
+ .store(in: &disposeBag)
+
+ tableView.translatesAutoresizingMaskIntoConstraints = false
+ view.addSubview(tableView)
+ tableView.pinToParent()
+ viewModel.setupTableView(tableView)
+
+ viewModel.presentHashtagTimeline
+ .receive(on: DispatchQueue.main)
+ .sink { [weak self] hashtagTimelineViewModel in
+ guard let self = self else { return }
+ _ = self.coordinator.present(
+ scene: .hashtagTimeline(viewModel: hashtagTimelineViewModel),
+ from: self,
+ transition: .show
+ )
+ }
+ .store(in: &disposeBag)
+ }
+}
diff --git a/Mastodon/Scene/Profile/FollowedTags/FollowedTagsViewModel+DiffableDataSource.swift b/Mastodon/Scene/Profile/FollowedTags/FollowedTagsViewModel+DiffableDataSource.swift
new file mode 100644
index 000000000..91f2cc5e9
--- /dev/null
+++ b/Mastodon/Scene/Profile/FollowedTags/FollowedTagsViewModel+DiffableDataSource.swift
@@ -0,0 +1,51 @@
+//
+// FollowedTagsViewModel+DiffableDataSource.swift
+// Mastodon
+//
+// Created by Marcus Kida on 01.12.22.
+//
+
+import UIKit
+import Combine
+import CoreData
+import CoreDataStack
+import MastodonSDK
+import MastodonCore
+
+extension FollowedTagsViewModel {
+ enum Section: Hashable {
+ case main
+ }
+
+ enum Item: Hashable {
+ case hashtag(Tag)
+ }
+
+ func tableViewDiffableDataSource(
+ for tableView: UITableView
+ ) -> UITableViewDiffableDataSource {
+ UITableViewDiffableDataSource(tableView: tableView) { tableView, indexPath, item in
+ switch item {
+ case let .hashtag(tag):
+ guard let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: FollowedTagsTableViewCell.self), for: indexPath) as? FollowedTagsTableViewCell else {
+ assertionFailure()
+ return UITableViewCell()
+ }
+
+ cell.setup(self)
+ cell.populate(with: tag)
+ return cell
+ }
+ }
+ }
+
+ func setupDiffableDataSource(
+ tableView: UITableView
+ ) {
+ diffableDataSource = tableViewDiffableDataSource(for: tableView)
+
+ var snapshot = NSDiffableDataSourceSnapshot()
+ snapshot.appendSections([.main])
+ diffableDataSource?.apply(snapshot)
+ }
+}
diff --git a/Mastodon/Scene/Profile/FollowedTags/FollowedTagsViewModel.swift b/Mastodon/Scene/Profile/FollowedTags/FollowedTagsViewModel.swift
new file mode 100644
index 000000000..dbcf4d756
--- /dev/null
+++ b/Mastodon/Scene/Profile/FollowedTags/FollowedTagsViewModel.swift
@@ -0,0 +1,109 @@
+//
+// FollowedTagsViewModel.swift
+// Mastodon
+//
+// Created by Marcus Kida on 23.11.22.
+//
+
+import os
+import UIKit
+import Combine
+import CoreData
+import CoreDataStack
+import MastodonSDK
+import MastodonCore
+
+final class FollowedTagsViewModel: NSObject {
+ let logger = Logger(subsystem: String(describing: FollowedTagsViewModel.self), category: "ViewModel")
+ var disposeBag = Set()
+ let fetchedResultsController: FollowedTagsFetchedResultController
+
+ private weak var tableView: UITableView?
+ var diffableDataSource: UITableViewDiffableDataSource?
+
+ // input
+ let context: AppContext
+ let authContext: AuthContext
+
+ // output
+ let presentHashtagTimeline = PassthroughSubject()
+
+ init(context: AppContext, authContext: AuthContext) {
+ self.context = context
+ self.authContext = authContext
+ self.fetchedResultsController = FollowedTagsFetchedResultController(
+ managedObjectContext: context.managedObjectContext,
+ domain: authContext.mastodonAuthenticationBox.domain,
+ user: authContext.mastodonAuthenticationBox.authenticationRecord.object(in: context.managedObjectContext)!.user
+ )
+
+ super.init()
+
+ self.fetchedResultsController
+ .$records
+ .receive(on: DispatchQueue.main)
+ .sink { [weak self] records in
+ guard let self = self else { return }
+ var snapshot = NSDiffableDataSourceSnapshot()
+ snapshot.appendSections([.main])
+ snapshot.appendItems(records.map {.hashtag($0) })
+ self.diffableDataSource?.apply(snapshot, animatingDifferences: true)
+ }
+ .store(in: &disposeBag)
+ }
+}
+
+extension FollowedTagsViewModel {
+ func setupTableView(_ tableView: UITableView) {
+ self.tableView = tableView
+ setupDiffableDataSource(tableView: tableView)
+ tableView.delegate = self
+
+ fetchFollowedTags()
+ }
+
+ func fetchFollowedTags() {
+ Task { @MainActor in
+ try await context.apiService.getFollowedTags(
+ domain: authContext.mastodonAuthenticationBox.domain,
+ query: Mastodon.API.Account.FollowedTagsQuery(limit: nil),
+ authenticationBox: authContext.mastodonAuthenticationBox
+ )
+ }
+ }
+
+ func followOrUnfollow(_ tag: Tag) {
+ Task { @MainActor in
+ switch tag.following {
+ case true:
+ _ = try? await context.apiService.unfollowTag(
+ for: tag.name,
+ authenticationBox: authContext.mastodonAuthenticationBox
+ )
+ case false:
+ _ = try? await context.apiService.followTag(
+ for: tag.name,
+ authenticationBox: authContext.mastodonAuthenticationBox
+ )
+ }
+ fetchFollowedTags()
+ }
+ }
+}
+
+extension FollowedTagsViewModel: UITableViewDelegate {
+ func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
+ logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): \(indexPath)")
+ tableView.deselectRow(at: indexPath, animated: true)
+
+ let object = fetchedResultsController.records[indexPath.row]
+
+ let hashtagTimelineViewModel = HashtagTimelineViewModel(
+ context: self.context,
+ authContext: self.authContext,
+ hashtag: object.name
+ )
+
+ presentHashtagTimeline.send(hashtagTimelineViewModel)
+ }
+}
diff --git a/Mastodon/Scene/Profile/Follower/FollowerListViewController.swift b/Mastodon/Scene/Profile/Follower/FollowerListViewController.swift
index 190fa27e5..c111d13d0 100644
--- a/Mastodon/Scene/Profile/Follower/FollowerListViewController.swift
+++ b/Mastodon/Scene/Profile/Follower/FollowerListViewController.swift
@@ -58,12 +58,7 @@ extension FollowerListViewController {
tableView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(tableView)
- NSLayoutConstraint.activate([
- tableView.topAnchor.constraint(equalTo: view.topAnchor),
- tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
- tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
- tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
- ])
+ tableView.pinToParent()
tableView.delegate = self
viewModel.setupDiffableDataSource(
diff --git a/Mastodon/Scene/Profile/Follower/FollowerListViewModel+Diffable.swift b/Mastodon/Scene/Profile/Follower/FollowerListViewModel+Diffable.swift
index 7a30c3234..199879c83 100644
--- a/Mastodon/Scene/Profile/Follower/FollowerListViewModel+Diffable.swift
+++ b/Mastodon/Scene/Profile/Follower/FollowerListViewModel+Diffable.swift
@@ -27,12 +27,7 @@ extension FollowerListViewModel {
var snapshot = NSDiffableDataSourceSnapshot()
snapshot.appendSections([.main])
snapshot.appendItems([.bottomLoader], toSection: .main)
- if #available(iOS 15.0, *) {
- diffableDataSource?.applySnapshotUsingReloadData(snapshot, completion: nil)
- } else {
- // Fallback on earlier versions
- diffableDataSource?.apply(snapshot, animatingDifferences: false)
- }
+ diffableDataSource?.applySnapshotUsingReloadData(snapshot, completion: nil)
userFetchedResultsController.$records
.receive(on: DispatchQueue.main)
diff --git a/Mastodon/Scene/Profile/Following/FollowingListViewController.swift b/Mastodon/Scene/Profile/Following/FollowingListViewController.swift
index e16b600c2..3fa65918a 100644
--- a/Mastodon/Scene/Profile/Following/FollowingListViewController.swift
+++ b/Mastodon/Scene/Profile/Following/FollowingListViewController.swift
@@ -58,12 +58,7 @@ extension FollowingListViewController {
tableView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(tableView)
- NSLayoutConstraint.activate([
- tableView.topAnchor.constraint(equalTo: view.topAnchor),
- tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
- tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
- tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
- ])
+ tableView.pinToParent()
tableView.delegate = self
viewModel.setupDiffableDataSource(
diff --git a/Mastodon/Scene/Profile/Following/FollowingListViewModel+Diffable.swift b/Mastodon/Scene/Profile/Following/FollowingListViewModel+Diffable.swift
index e022c5736..f139a3b79 100644
--- a/Mastodon/Scene/Profile/Following/FollowingListViewModel+Diffable.swift
+++ b/Mastodon/Scene/Profile/Following/FollowingListViewModel+Diffable.swift
@@ -28,13 +28,8 @@ extension FollowingListViewModel {
var snapshot = NSDiffableDataSourceSnapshot()
snapshot.appendSections([.main])
snapshot.appendItems([.bottomLoader], toSection: .main)
- if #available(iOS 15.0, *) {
- diffableDataSource?.applySnapshotUsingReloadData(snapshot, completion: nil)
- } else {
- // Fallback on earlier versions
- diffableDataSource?.apply(snapshot, animatingDifferences: false)
- }
-
+ diffableDataSource?.applySnapshotUsingReloadData(snapshot)
+
userFetchedResultsController.$records
.receive(on: DispatchQueue.main)
.sink { [weak self] records in
diff --git a/Mastodon/Scene/Profile/Header/ProfileHeaderViewController.swift b/Mastodon/Scene/Profile/Header/ProfileHeaderViewController.swift
index c5dbbecd4..55e73a1e8 100644
--- a/Mastodon/Scene/Profile/Header/ProfileHeaderViewController.swift
+++ b/Mastodon/Scene/Profile/Header/ProfileHeaderViewController.swift
@@ -60,7 +60,8 @@ final class ProfileHeaderViewController: UIViewController, NeedsDependency, Medi
// private var isAdjustBannerImageViewForSafeAreaInset = false
private var containerSafeAreaInset: UIEdgeInsets = .zero
-
+
+ private var currentImageType = ImageType.avatar
private(set) lazy var imagePicker: PHPickerViewController = {
var configuration = PHPickerConfiguration()
configuration.filter = .images
@@ -125,7 +126,9 @@ extension ProfileHeaderViewController {
}
.store(in: &disposeBag)
- profileHeaderView.editAvatarButtonOverlayIndicatorView.menu = createAvatarContextMenu()
+ profileHeaderView.editBannerButton.menu = createImageContextMenu(.banner)
+ profileHeaderView.editBannerButton.showsMenuAsPrimaryAction = true
+ profileHeaderView.editAvatarButtonOverlayIndicatorView.menu = createImageContextMenu(.avatar)
profileHeaderView.editAvatarButtonOverlayIndicatorView.showsMenuAsPrimaryAction = true
profileHeaderView.delegate = self
@@ -150,12 +153,18 @@ extension ProfileHeaderViewController {
viewModel.$relationshipActionOptionSet
.assign(to: \.relationshipActionOptionSet, on: profileHeaderView.viewModel)
.store(in: &disposeBag)
+ viewModel.$isMyself
+ .assign(to: \.isMyself, on: profileHeaderView.viewModel)
+ .store(in: &disposeBag)
viewModel.$isEditing
.assign(to: \.isEditing, on: profileHeaderView.viewModel)
.store(in: &disposeBag)
viewModel.$isUpdating
.assign(to: \.isUpdating, on: profileHeaderView.viewModel)
.store(in: &disposeBag)
+ viewModel.profileInfoEditing.$header
+ .assign(to: \.headerImageEditing, on: profileHeaderView.viewModel)
+ .store(in: &disposeBag)
viewModel.profileInfoEditing.$avatar
.assign(to: \.avatarImageEditing, on: profileHeaderView.viewModel)
.store(in: &disposeBag)
@@ -173,7 +182,7 @@ extension ProfileHeaderViewController {
profileHeaderView.viewModel.viewDidAppear.send()
// set display after view appear
- profileHeaderView.setupAvatarOverlayViews()
+ profileHeaderView.setupImageOverlayViews()
}
override func viewDidLayoutSubviews() {
@@ -185,11 +194,16 @@ extension ProfileHeaderViewController {
}
extension ProfileHeaderViewController {
- private func createAvatarContextMenu() -> UIMenu {
+ fileprivate enum ImageType {
+ case avatar
+ case banner
+ }
+ private func createImageContextMenu(_ type: ImageType) -> UIMenu {
var children: [UIMenuElement] = []
let photoLibraryAction = UIAction(title: L10n.Scene.Compose.MediaSelection.photoLibrary, image: UIImage(systemName: "rectangle.on.rectangle"), identifier: nil, discoverabilityTitle: nil, attributes: [], state: .off) { [weak self] _ in
guard let self = self else { return }
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: mediaSelectionType: .photoLibaray", ((#file as NSString).lastPathComponent), #line, #function)
+ self.currentImageType = type
self.present(self.imagePicker, animated: true, completion: nil)
}
children.append(photoLibraryAction)
@@ -197,6 +211,7 @@ extension ProfileHeaderViewController {
let cameraAction = UIAction(title: L10n.Scene.Compose.MediaSelection.camera, image: UIImage(systemName: "camera"), identifier: nil, discoverabilityTitle: nil, attributes: [], state: .off, handler: { [weak self] _ in
guard let self = self else { return }
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: mediaSelectionType: .camera", ((#file as NSString).lastPathComponent), #line, #function)
+ self.currentImageType = type
self.present(self.imagePickerController, animated: true, completion: nil)
})
children.append(cameraAction)
@@ -204,6 +219,7 @@ extension ProfileHeaderViewController {
let browseAction = UIAction(title: L10n.Scene.Compose.MediaSelection.browse, image: UIImage(systemName: "ellipsis"), identifier: nil, discoverabilityTitle: nil, attributes: [], state: .off) { [weak self] _ in
guard let self = self else { return }
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: mediaSelectionType: .browse", ((#file as NSString).lastPathComponent), #line, #function)
+ self.currentImageType = type
self.present(self.documentPickerController, animated: true, completion: nil)
}
children.append(browseAction)
@@ -215,7 +231,13 @@ extension ProfileHeaderViewController {
DispatchQueue.main.async {
let cropController = CropViewController(croppingStyle: .default, image: image)
cropController.delegate = self
- cropController.setAspectRatioPreset(.presetSquare, animated: true)
+ switch self.currentImageType {
+ case .banner:
+ cropController.customAspectRatio = CGSize(width: 3, height: 1)
+ cropController.setAspectRatioPreset(.presetCustom, animated: true)
+ case .avatar:
+ cropController.setAspectRatioPreset(.presetSquare, animated: true)
+ }
cropController.aspectRatioPickerButtonHidden = true
cropController.aspectRatioLockEnabled = true
pickerViewController.dismiss(animated: true, completion: {
@@ -443,7 +465,12 @@ extension ProfileHeaderViewController: UIDocumentPickerDelegate {
// MARK: - CropViewControllerDelegate
extension ProfileHeaderViewController: CropViewControllerDelegate {
public func cropViewController(_ cropViewController: CropViewController, didCropToImage image: UIImage, withRect cropRect: CGRect, angle: Int) {
- viewModel.profileInfoEditing.avatar = image
+ switch currentImageType {
+ case .banner:
+ viewModel.profileInfoEditing.header = image
+ case .avatar:
+ viewModel.profileInfoEditing.avatar = image
+ }
cropViewController.dismiss(animated: true, completion: nil)
}
}
diff --git a/Mastodon/Scene/Profile/Header/ProfileHeaderViewModel.swift b/Mastodon/Scene/Profile/Header/ProfileHeaderViewModel.swift
index 65f15efa7..3025907e0 100644
--- a/Mastodon/Scene/Profile/Header/ProfileHeaderViewModel.swift
+++ b/Mastodon/Scene/Profile/Header/ProfileHeaderViewModel.swift
@@ -18,6 +18,7 @@ import MastodonUI
final class ProfileHeaderViewModel {
static let avatarImageMaxSizeInPixel = CGSize(width: 400, height: 400)
+ static let bannerImageMaxSizeInPixel = CGSize(width: 1500, height: 500)
static let maxProfileFieldCount = 4
var disposeBag = Set()
@@ -29,6 +30,7 @@ final class ProfileHeaderViewModel {
@Published var user: MastodonUser?
@Published var relationshipActionOptionSet: RelationshipActionOptionSet = .none
+ @Published var isMyself = false
@Published var isEditing = false
@Published var isUpdating = false
@@ -52,6 +54,9 @@ final class ProfileHeaderViewModel {
.sink { [weak self] account in
guard let self = self else { return }
guard let account = account else { return }
+ // banner
+ self.profileInfo.header = nil
+ self.profileInfoEditing.header = nil
// avatar
self.profileInfo.avatar = nil
self.profileInfoEditing.avatar = nil
@@ -72,6 +77,7 @@ final class ProfileHeaderViewModel {
extension ProfileHeaderViewModel {
class ProfileInfo {
// input
+ @Published var header: UIImage?
@Published var avatar: UIImage?
@Published var name: String?
@Published var note: String?
@@ -99,6 +105,7 @@ extension ProfileHeaderViewModel: ProfileViewModelEditable {
var isEdited: Bool {
guard isEditing else { return false }
+ guard profileInfoEditing.header == nil else { return true }
guard profileInfoEditing.avatar == nil else { return true }
guard profileInfo.name == profileInfoEditing.name else { return true }
guard profileInfo.note == profileInfoEditing.note else { return true }
diff --git a/Mastodon/Scene/Profile/Header/View/ProfileHeaderView+ViewModel.swift b/Mastodon/Scene/Profile/Header/View/ProfileHeaderView+ViewModel.swift
index c51ccfab3..c5389958a 100644
--- a/Mastodon/Scene/Profile/Header/View/ProfileHeaderView+ViewModel.swift
+++ b/Mastodon/Scene/Profile/Header/View/ProfileHeaderView+ViewModel.swift
@@ -28,6 +28,7 @@ extension ProfileHeaderView {
@Published var emojiMeta: MastodonContent.Emojis = [:]
@Published var headerImageURL: URL?
+ @Published var headerImageEditing: UIImage?
@Published var avatarImageURL: URL?
@Published var avatarImageEditing: UIImage?
@@ -47,6 +48,7 @@ extension ProfileHeaderView {
@Published var relationshipActionOptionSet: RelationshipActionOptionSet = .none
@Published var isRelationshipActionButtonHidden = false
+ @Published var isMyself = false
init() {
$relationshipActionOptionSet
@@ -61,14 +63,19 @@ extension ProfileHeaderView.ViewModel {
func bind(view: ProfileHeaderView) {
// header
- Publishers.CombineLatest(
+ Publishers.CombineLatest4(
$headerImageURL,
+ $headerImageEditing,
+ $isEditing,
viewDidAppear
)
- .sink { headerImageURL, _ in
+ .sink { headerImageURL, headerImageEditing, isEditing, _ in
view.bannerImageView.af.cancelImageRequest()
- let placeholder = UIImage.placeholder(color: ProfileHeaderView.bannerImageViewPlaceholderColor)
- guard let bannerImageURL = headerImageURL else {
+ let defaultPlaceholder = UIImage.placeholder(color: ProfileHeaderView.bannerImageViewPlaceholderColor)
+ let placeholder = isEditing ? (headerImageEditing ?? defaultPlaceholder) : defaultPlaceholder
+ guard let bannerImageURL = headerImageURL,
+ !isEditing || headerImageEditing == nil
+ else {
view.bannerImageView.image = placeholder
return
}
@@ -183,6 +190,19 @@ extension ProfileHeaderView.ViewModel {
}
.store(in: &disposeBag)
// dashboard
+ $isMyself
+ .sink { isMyself in
+ if isMyself {
+ view.statusDashboardView.postDashboardMeterView.textLabel.text = L10n.Scene.Profile.Dashboard.myPosts
+ view.statusDashboardView.followingDashboardMeterView.textLabel.text = L10n.Scene.Profile.Dashboard.myFollowing
+ view.statusDashboardView.followersDashboardMeterView.textLabel.text = L10n.Scene.Profile.Dashboard.myFollowers
+ } else {
+ view.statusDashboardView.postDashboardMeterView.textLabel.text = L10n.Scene.Profile.Dashboard.otherPosts
+ view.statusDashboardView.followingDashboardMeterView.textLabel.text = L10n.Scene.Profile.Dashboard.otherFollowing
+ view.statusDashboardView.followersDashboardMeterView.textLabel.text = L10n.Scene.Profile.Dashboard.otherFollowers
+ }
+ }
+ .store(in: &disposeBag)
$statusesCount
.sink { count in
let text = count.flatMap { MastodonMetricFormatter().string(from: $0) } ?? "-"
@@ -262,22 +282,29 @@ extension ProfileHeaderView {
animator.addAnimations {
self.bannerImageViewOverlayVisualEffectView.backgroundColor = ProfileHeaderView.bannerImageViewOverlayViewBackgroundNormalColor
self.nameTextFieldBackgroundView.backgroundColor = .clear
+ self.editBannerButton.alpha = 0
self.editAvatarBackgroundView.alpha = 0
}
animator.addCompletion { _ in
+ self.editBannerButton.isHidden = true
self.editAvatarBackgroundView.isHidden = true
+ self.bannerImageViewSingleTapGestureRecognizer.isEnabled = true
}
case .editing:
nameMetaText.textView.alpha = 0
nameTextField.isEnabled = true
nameTextField.alpha = 1
+ editBannerButton.isHidden = false
+ editBannerButton.alpha = 0
editAvatarBackgroundView.isHidden = false
editAvatarBackgroundView.alpha = 0
bioMetaText.textView.backgroundColor = .clear
+ bannerImageViewSingleTapGestureRecognizer.isEnabled = false
animator.addAnimations {
self.bannerImageViewOverlayVisualEffectView.backgroundColor = ProfileHeaderView.bannerImageViewOverlayViewBackgroundEditingColor
self.nameTextFieldBackgroundView.backgroundColor = Asset.Scene.Profile.Banner.nameEditBackgroundGray.color
+ self.editBannerButton.alpha = 1
self.editAvatarBackgroundView.alpha = 1
self.bioMetaText.textView.backgroundColor = Asset.Scene.Profile.Banner.bioEditBackgroundGray.color
}
diff --git a/Mastodon/Scene/Profile/Header/View/ProfileHeaderView.swift b/Mastodon/Scene/Profile/Header/View/ProfileHeaderView.swift
index a2194758b..5204cf567 100644
--- a/Mastodon/Scene/Profile/Header/View/ProfileHeaderView.swift
+++ b/Mastodon/Scene/Profile/Header/View/ProfileHeaderView.swift
@@ -28,7 +28,6 @@ final class ProfileHeaderView: UIView {
static let avatarImageViewSize = CGSize(width: 98, height: 98)
static let avatarImageViewCornerRadius: CGFloat = 25
- static let avatarImageViewBorderColor = UIColor.white
static let avatarImageViewBorderWidth: CGFloat = 2
static let friendshipActionButtonSize = CGSize(width: 108, height: 34)
static let bannerImageViewPlaceholderColor = UIColor.systemGray
@@ -50,6 +49,7 @@ final class ProfileHeaderView: UIView {
return viewModel
}()
+ let bannerImageViewSingleTapGestureRecognizer = UITapGestureRecognizer.singleTapGestureRecognizer
let bannerContainerView = UIView()
let bannerImageView: UIImageView = {
let imageView = UIImageView()
@@ -89,7 +89,6 @@ final class ProfileHeaderView: UIView {
view.layer.masksToBounds = true
view.layer.cornerRadius = ProfileHeaderView.avatarImageViewCornerRadius
view.layer.cornerCurve = .continuous
- view.layer.borderColor = ProfileHeaderView.avatarImageViewBorderColor.cgColor
view.layer.borderWidth = ProfileHeaderView.avatarImageViewBorderWidth
return view
}()
@@ -101,7 +100,9 @@ final class ProfileHeaderView: UIView {
return button
}()
- func setupAvatarOverlayViews() {
+ func setupImageOverlayViews() {
+ editBannerButton.tintColor = .white
+
editAvatarBackgroundView.backgroundColor = UIColor.black.withAlphaComponent(0.6)
editAvatarButtonOverlayIndicatorView.tintColor = .white
}
@@ -113,6 +114,13 @@ final class ProfileHeaderView: UIView {
return visualEffectView
}()
+ let editBannerButton: HighlightDimmableButton = {
+ let button = HighlightDimmableButton()
+ button.setImage(UIImage(systemName: "photo", withConfiguration: UIImage.SymbolConfiguration(pointSize: 28)), for: .normal)
+ button.tintColor = .clear
+ return button
+ }()
+
let editAvatarBackgroundView: UIView = {
let view = UIView()
view.backgroundColor = .clear // set value after view appeared
@@ -232,12 +240,13 @@ final class ProfileHeaderView: UIView {
extension ProfileHeaderView {
private func _init() {
- backgroundColor = ThemeService.shared.currentTheme.value.systemBackgroundColor
- ThemeService.shared.currentTheme
+ let currentTheme = ThemeService.shared.currentTheme
+ setColors(from: currentTheme.value)
+
+ currentTheme
.receive(on: DispatchQueue.main)
.sink { [weak self] theme in
- guard let self = self else { return }
- self.backgroundColor = theme.systemBackgroundColor
+ self?.setColors(from: theme)
}
.store(in: &_disposeBag)
@@ -265,13 +274,13 @@ extension ProfileHeaderView {
bannerImageViewOverlayVisualEffectView.translatesAutoresizingMaskIntoConstraints = false
bannerImageView.addSubview(bannerImageViewOverlayVisualEffectView)
- NSLayoutConstraint.activate([
- bannerImageViewOverlayVisualEffectView.topAnchor.constraint(equalTo: bannerImageView.topAnchor),
- bannerImageViewOverlayVisualEffectView.leadingAnchor.constraint(equalTo: bannerImageView.leadingAnchor),
- bannerImageViewOverlayVisualEffectView.trailingAnchor.constraint(equalTo: bannerImageView.trailingAnchor),
- bannerImageViewOverlayVisualEffectView.bottomAnchor.constraint(equalTo: bannerImageView.bottomAnchor),
- ])
-
+ bannerImageViewOverlayVisualEffectView.pinToParent()
+
+ editBannerButton.translatesAutoresizingMaskIntoConstraints = false
+ bannerContainerView.addSubview(editBannerButton)
+ editBannerButton.pinTo(to: bannerImageView)
+ bannerContainerView.isUserInteractionEnabled = true
+
// follows you
followsYouBlurEffectView.translatesAutoresizingMaskIntoConstraints = false
addSubview(followsYouBlurEffectView)
@@ -286,12 +295,7 @@ extension ProfileHeaderView {
followsYouVibrantEffectView.translatesAutoresizingMaskIntoConstraints = false
followsYouBlurEffectView.contentView.addSubview(followsYouVibrantEffectView)
- NSLayoutConstraint.activate([
- followsYouVibrantEffectView.topAnchor.constraint(equalTo: followsYouBlurEffectView.topAnchor),
- followsYouVibrantEffectView.leadingAnchor.constraint(equalTo: followsYouBlurEffectView.leadingAnchor),
- followsYouVibrantEffectView.trailingAnchor.constraint(equalTo: followsYouBlurEffectView.trailingAnchor),
- followsYouVibrantEffectView.bottomAnchor.constraint(equalTo: followsYouBlurEffectView.bottomAnchor),
- ])
+ followsYouVibrantEffectView.pinTo(to: followsYouBlurEffectView)
followsYouLabel.translatesAutoresizingMaskIntoConstraints = false
followsYouVibrantEffectView.contentView.addSubview(followsYouLabel)
@@ -317,40 +321,25 @@ extension ProfileHeaderView {
avatarButton.translatesAutoresizingMaskIntoConstraints = false
avatarImageViewBackgroundView.addSubview(avatarButton)
NSLayoutConstraint.activate([
- avatarButton.topAnchor.constraint(equalTo: avatarImageViewBackgroundView.topAnchor, constant: 0.5 * ProfileHeaderView.avatarImageViewBorderWidth),
- avatarButton.leadingAnchor.constraint(equalTo: avatarImageViewBackgroundView.leadingAnchor, constant: 0.5 * ProfileHeaderView.avatarImageViewBorderWidth),
- avatarImageViewBackgroundView.trailingAnchor.constraint(equalTo: avatarButton.trailingAnchor, constant: 0.5 * ProfileHeaderView.avatarImageViewBorderWidth),
- avatarImageViewBackgroundView.bottomAnchor.constraint(equalTo: avatarButton.bottomAnchor, constant: 0.5 * ProfileHeaderView.avatarImageViewBorderWidth),
+ avatarButton.topAnchor.constraint(equalTo: avatarImageViewBackgroundView.topAnchor, constant: ProfileHeaderView.avatarImageViewBorderWidth),
+ avatarButton.leadingAnchor.constraint(equalTo: avatarImageViewBackgroundView.leadingAnchor, constant: ProfileHeaderView.avatarImageViewBorderWidth),
+ avatarImageViewBackgroundView.trailingAnchor.constraint(equalTo: avatarButton.trailingAnchor, constant: ProfileHeaderView.avatarImageViewBorderWidth),
+ avatarImageViewBackgroundView.bottomAnchor.constraint(equalTo: avatarButton.bottomAnchor, constant: ProfileHeaderView.avatarImageViewBorderWidth),
avatarButton.widthAnchor.constraint(equalToConstant: ProfileHeaderView.avatarImageViewSize.width).priority(.required - 1),
avatarButton.heightAnchor.constraint(equalToConstant: ProfileHeaderView.avatarImageViewSize.height).priority(.required - 1),
])
avatarImageViewOverlayVisualEffectView.translatesAutoresizingMaskIntoConstraints = false
avatarImageViewBackgroundView.addSubview(avatarImageViewOverlayVisualEffectView)
- NSLayoutConstraint.activate([
- avatarImageViewOverlayVisualEffectView.topAnchor.constraint(equalTo: avatarImageViewBackgroundView.topAnchor),
- avatarImageViewOverlayVisualEffectView.leadingAnchor.constraint(equalTo: avatarImageViewBackgroundView.leadingAnchor),
- avatarImageViewOverlayVisualEffectView.trailingAnchor.constraint(equalTo: avatarImageViewBackgroundView.trailingAnchor),
- avatarImageViewOverlayVisualEffectView.bottomAnchor.constraint(equalTo: avatarImageViewBackgroundView.bottomAnchor),
- ])
+ avatarImageViewOverlayVisualEffectView.pinToParent()
editAvatarBackgroundView.translatesAutoresizingMaskIntoConstraints = false
avatarButton.addSubview(editAvatarBackgroundView)
- NSLayoutConstraint.activate([
- editAvatarBackgroundView.topAnchor.constraint(equalTo: avatarButton.topAnchor),
- editAvatarBackgroundView.leadingAnchor.constraint(equalTo: avatarButton.leadingAnchor),
- editAvatarBackgroundView.trailingAnchor.constraint(equalTo: avatarButton.trailingAnchor),
- editAvatarBackgroundView.bottomAnchor.constraint(equalTo: avatarButton.bottomAnchor),
- ])
+ editAvatarBackgroundView.pinToParent()
editAvatarButtonOverlayIndicatorView.translatesAutoresizingMaskIntoConstraints = false
editAvatarBackgroundView.addSubview(editAvatarButtonOverlayIndicatorView)
- NSLayoutConstraint.activate([
- editAvatarButtonOverlayIndicatorView.topAnchor.constraint(equalTo: editAvatarBackgroundView.topAnchor),
- editAvatarButtonOverlayIndicatorView.leadingAnchor.constraint(equalTo: editAvatarBackgroundView.leadingAnchor),
- editAvatarButtonOverlayIndicatorView.trailingAnchor.constraint(equalTo: editAvatarBackgroundView.trailingAnchor),
- editAvatarButtonOverlayIndicatorView.bottomAnchor.constraint(equalTo: editAvatarBackgroundView.bottomAnchor),
- ])
+ editAvatarButtonOverlayIndicatorView.pinToParent()
editAvatarBackgroundView.isUserInteractionEnabled = true
avatarButton.isUserInteractionEnabled = true
@@ -436,11 +425,8 @@ extension ProfileHeaderView {
relationshipActionButton.translatesAutoresizingMaskIntoConstraints = false
relationshipActionButtonShadowContainer.addSubview(relationshipActionButton)
+ relationshipActionButton.pinToParent()
NSLayoutConstraint.activate([
- relationshipActionButton.topAnchor.constraint(equalTo: relationshipActionButtonShadowContainer.topAnchor),
- relationshipActionButton.leadingAnchor.constraint(equalTo: relationshipActionButtonShadowContainer.leadingAnchor),
- relationshipActionButton.trailingAnchor.constraint(equalTo: relationshipActionButtonShadowContainer.trailingAnchor),
- relationshipActionButton.bottomAnchor.constraint(equalTo: relationshipActionButtonShadowContainer.bottomAnchor),
relationshipActionButton.widthAnchor.constraint(greaterThanOrEqualToConstant: ProfileHeaderView.friendshipActionButtonSize.width).priority(.required - 1),
relationshipActionButton.heightAnchor.constraint(equalToConstant: ProfileHeaderView.friendshipActionButtonSize.height).priority(.defaultHigh),
])
@@ -455,8 +441,7 @@ extension ProfileHeaderView {
statusDashboardView.delegate = self
bioMetaText.textView.delegate = self
bioMetaText.textView.linkDelegate = self
-
- let bannerImageViewSingleTapGestureRecognizer = UITapGestureRecognizer.singleTapGestureRecognizer
+
bannerImageView.addGestureRecognizer(bannerImageViewSingleTapGestureRecognizer)
bannerImageViewSingleTapGestureRecognizer.addTarget(self, action: #selector(ProfileHeaderView.bannerImageViewDidPressed(_:)))
@@ -467,6 +452,12 @@ extension ProfileHeaderView {
updateLayoutMargins()
}
+
+ private func setColors(from theme: Theme) {
+ backgroundColor = theme.systemBackgroundColor
+ avatarButton.backgroundColor = theme.secondarySystemBackgroundColor
+ avatarImageViewBackgroundView.layer.borderColor = theme.systemBackgroundColor.cgColor
+ }
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
diff --git a/Mastodon/Scene/Profile/Paging/ProfilePagingViewController.swift b/Mastodon/Scene/Profile/Paging/ProfilePagingViewController.swift
index db92617b7..27c539d18 100644
--- a/Mastodon/Scene/Profile/Paging/ProfilePagingViewController.swift
+++ b/Mastodon/Scene/Profile/Paging/ProfilePagingViewController.swift
@@ -99,12 +99,7 @@ extension ProfilePagingViewController {
if let buttonBarView = self.buttonBarView {
buttonBarShadowView.translatesAutoresizingMaskIntoConstraints = false
view.insertSubview(buttonBarShadowView, belowSubview: buttonBarView)
- NSLayoutConstraint.activate([
- buttonBarShadowView.topAnchor.constraint(equalTo: buttonBarView.topAnchor),
- buttonBarShadowView.leadingAnchor.constraint(equalTo: buttonBarView.leadingAnchor),
- buttonBarShadowView.trailingAnchor.constraint(equalTo: buttonBarView.trailingAnchor),
- buttonBarShadowView.bottomAnchor.constraint(equalTo: buttonBarView.bottomAnchor),
- ])
+ buttonBarShadowView.pinTo(to: buttonBarView)
viewModel.$needsSetupBottomShadow
.receive(on: DispatchQueue.main)
diff --git a/Mastodon/Scene/Profile/ProfileViewController.swift b/Mastodon/Scene/Profile/ProfileViewController.swift
index d9e56630f..fa10bed9b 100644
--- a/Mastodon/Scene/Profile/ProfileViewController.swift
+++ b/Mastodon/Scene/Profile/ProfileViewController.swift
@@ -104,6 +104,13 @@ final class ProfileViewController: UIViewController, NeedsDependency, MediaPrevi
barButtonItem.accessibilityLabel = L10n.Common.Controls.Actions.seeMore
return barButtonItem
}()
+
+ private(set) lazy var followedTagsBarButtonItem: UIBarButtonItem = {
+ let barButtonItem = UIBarButtonItem(image: UIImage(systemName: "number"), style: .plain, target: self, action: #selector(ProfileViewController.followedTagsItemPressed(_:)))
+ barButtonItem.tintColor = .white
+ barButtonItem.accessibilityLabel = L10n.Scene.FollowedTags.title
+ return barButtonItem
+ }()
let refreshControl: RefreshControl = {
let refreshControl = RefreshControl()
@@ -243,6 +250,11 @@ extension ProfileViewController {
items.append(self.shareBarButtonItem)
items.append(self.favoriteBarButtonItem)
items.append(self.bookmarkBarButtonItem)
+
+ if self.currentInstance?.canFollowTags == true {
+ items.append(self.followedTagsBarButtonItem)
+ }
+
return
}
@@ -259,12 +271,7 @@ extension ProfileViewController {
tabBarPagerController.view.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(tabBarPagerController.view)
tabBarPagerController.didMove(toParent: self)
- NSLayoutConstraint.activate([
- tabBarPagerController.view.topAnchor.constraint(equalTo: view.topAnchor),
- tabBarPagerController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor),
- tabBarPagerController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor),
- tabBarPagerController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor),
- ])
+ tabBarPagerController.view.pinToParent()
tabBarPagerController.delegate = self
tabBarPagerController.dataSource = self
@@ -304,6 +311,9 @@ extension ProfileViewController {
viewModel.$isUpdating
.assign(to: \.isUpdating, on: headerViewModel)
.store(in: &disposeBag)
+ viewModel.relationshipViewModel.$isMyself
+ .assign(to: \.isMyself, on: headerViewModel)
+ .store(in: &disposeBag)
viewModel.relationshipViewModel.$optionSet
.map { $0 ?? .none }
.assign(to: \.relationshipActionOptionSet, on: headerViewModel)
@@ -503,7 +513,7 @@ extension ProfileViewController {
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
guard let setting = context.settingService.currentSetting.value else { return }
let settingsViewModel = SettingsViewModel(context: context, authContext: viewModel.authContext, setting: setting)
- coordinator.present(scene: .settings(viewModel: settingsViewModel), from: self, transition: .modal(animated: true, completion: nil))
+ _ = coordinator.present(scene: .settings(viewModel: settingsViewModel), from: self, transition: .modal(animated: true, completion: nil))
}
@objc private func shareBarButtonItemPressed(_ sender: UIBarButtonItem) {
@@ -543,13 +553,23 @@ extension ProfileViewController {
@objc private func replyBarButtonItemPressed(_ sender: UIBarButtonItem) {
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
guard let mastodonUser = viewModel.user else { return }
+ let mention = "@" + mastodonUser.acct
+ UITextChecker.learnWord(mention)
let composeViewModel = ComposeViewModel(
context: context,
authContext: viewModel.authContext,
- kind: .mention(user: mastodonUser.asRecrod)
+ destination: .topLevel,
+ initialContent: mention
)
_ = coordinator.present(scene: .compose(viewModel: composeViewModel), from: self, transition: .modal(animated: true, completion: nil))
}
+
+ @objc private func followedTagsItemPressed(_ sender: UIBarButtonItem) {
+ os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
+
+ let followedTagsViewModel = FollowedTagsViewModel(context: context, authContext: viewModel.authContext)
+ _ = coordinator.present(scene: .followedTags(viewModel: followedTagsViewModel), from: self, transition: .show)
+ }
@objc private func refreshControlValueChanged(_ sender: RefreshControl) {
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
@@ -890,7 +910,7 @@ extension ProfileViewController: MastodonMenuDelegate {
// MARK: - ScrollViewContainer
extension ProfileViewController: ScrollViewContainer {
var scrollView: UIScrollView {
- return tabBarPagerController.containerScrollView
+ return tabBarPagerController.relayScrollView
}
}
@@ -919,3 +939,13 @@ extension ProfileViewController: PagerTabStripNavigateable {
}
+private extension ProfileViewController {
+ var currentInstance: Instance? {
+ guard let authenticationRecord = authContext.mastodonAuthenticationBox
+ .authenticationRecord
+ .object(in: context.managedObjectContext)
+ else { return nil }
+
+ return authenticationRecord.instance
+ }
+}
diff --git a/Mastodon/Scene/Profile/ProfileViewModel.swift b/Mastodon/Scene/Profile/ProfileViewModel.swift
index e23b465d4..5c81c7920 100644
--- a/Mastodon/Scene/Profile/ProfileViewModel.swift
+++ b/Mastodon/Scene/Profile/ProfileViewModel.swift
@@ -215,8 +215,17 @@ extension ProfileViewModel {
let authenticationBox = authContext.mastodonAuthenticationBox
let domain = authenticationBox.domain
let authorization = authenticationBox.userAuthorization
-
- let _image: UIImage? = {
+
+ // TODO: constrain size?
+ let _header: UIImage? = {
+ guard let image = headerProfileInfo.header else { return nil }
+ guard image.size.width <= ProfileHeaderViewModel.bannerImageMaxSizeInPixel.width else {
+ return image.af.imageScaled(to: ProfileHeaderViewModel.bannerImageMaxSizeInPixel)
+ }
+ return image
+ }()
+
+ let _avatar: UIImage? = {
guard let image = headerProfileInfo.avatar else { return nil }
guard image.size.width <= ProfileHeaderViewModel.avatarImageMaxSizeInPixel.width else {
return image.af.imageScaled(to: ProfileHeaderViewModel.avatarImageMaxSizeInPixel)
@@ -233,8 +242,8 @@ extension ProfileViewModel {
bot: nil,
displayName: headerProfileInfo.name,
note: headerProfileInfo.note,
- avatar: _image.flatMap { Mastodon.Query.MediaAttachment.png($0.pngData()) },
- header: nil,
+ avatar: _avatar.flatMap { Mastodon.Query.MediaAttachment.png($0.pngData()) },
+ header: _header.flatMap { Mastodon.Query.MediaAttachment.png($0.pngData()) },
locked: nil,
source: nil,
fieldsAttributes: fieldsAttributes
diff --git a/Mastodon/Scene/Profile/Timeline/UserTimelineViewController.swift b/Mastodon/Scene/Profile/Timeline/UserTimelineViewController.swift
index 8a983da33..6cdd6a594 100644
--- a/Mastodon/Scene/Profile/Timeline/UserTimelineViewController.swift
+++ b/Mastodon/Scene/Profile/Timeline/UserTimelineViewController.swift
@@ -60,12 +60,7 @@ extension UserTimelineViewController {
tableView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(tableView)
- NSLayoutConstraint.activate([
- tableView.topAnchor.constraint(equalTo: view.topAnchor),
- tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
- tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
- tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
- ])
+ tableView.pinToParent()
tableView.delegate = self
viewModel.setupDiffableDataSource(
diff --git a/Mastodon/Scene/Profile/Timeline/UserTimelineViewModel+Diffable.swift b/Mastodon/Scene/Profile/Timeline/UserTimelineViewModel+Diffable.swift
index 863d7b44e..67f2b8035 100644
--- a/Mastodon/Scene/Profile/Timeline/UserTimelineViewModel+Diffable.swift
+++ b/Mastodon/Scene/Profile/Timeline/UserTimelineViewModel+Diffable.swift
@@ -18,6 +18,7 @@ extension UserTimelineViewModel {
tableView: tableView,
context: context,
configuration: StatusSection.Configuration(
+ context: context,
authContext: authContext,
statusTableViewCellDelegate: statusTableViewCellDelegate,
timelineMiddleLoaderTableViewCellDelegate: nil,
@@ -82,7 +83,7 @@ extension UserTimelineViewModel {
}
}
- diffableDataSource.applySnapshot(snapshot, animated: false)
+ diffableDataSource.apply(snapshot, animatingDifferences: false)
}
.store(in: &disposeBag)
}
diff --git a/Mastodon/Scene/Profile/UserLIst/FavoritedBy/FavoritedByViewController.swift b/Mastodon/Scene/Profile/UserLIst/FavoritedBy/FavoritedByViewController.swift
index ebce374e7..07316cd3d 100644
--- a/Mastodon/Scene/Profile/UserLIst/FavoritedBy/FavoritedByViewController.swift
+++ b/Mastodon/Scene/Profile/UserLIst/FavoritedBy/FavoritedByViewController.swift
@@ -61,12 +61,7 @@ extension FavoritedByViewController {
tableView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(tableView)
- NSLayoutConstraint.activate([
- tableView.topAnchor.constraint(equalTo: view.topAnchor),
- tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
- tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
- tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
- ])
+ tableView.pinToParent()
tableView.delegate = self
viewModel.setupDiffableDataSource(
diff --git a/Mastodon/Scene/Profile/UserLIst/RebloggedBy/RebloggedByViewController.swift b/Mastodon/Scene/Profile/UserLIst/RebloggedBy/RebloggedByViewController.swift
index 0688bcccb..a45c491fc 100644
--- a/Mastodon/Scene/Profile/UserLIst/RebloggedBy/RebloggedByViewController.swift
+++ b/Mastodon/Scene/Profile/UserLIst/RebloggedBy/RebloggedByViewController.swift
@@ -61,12 +61,7 @@ extension RebloggedByViewController {
tableView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(tableView)
- NSLayoutConstraint.activate([
- tableView.topAnchor.constraint(equalTo: view.topAnchor),
- tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
- tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
- tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
- ])
+ tableView.pinToParent()
tableView.delegate = self
viewModel.setupDiffableDataSource(
diff --git a/Mastodon/Scene/Profile/UserLIst/UserListViewModel+Diffable.swift b/Mastodon/Scene/Profile/UserLIst/UserListViewModel+Diffable.swift
index acd225f0c..e843dd005 100644
--- a/Mastodon/Scene/Profile/UserLIst/UserListViewModel+Diffable.swift
+++ b/Mastodon/Scene/Profile/UserLIst/UserListViewModel+Diffable.swift
@@ -28,13 +28,8 @@ extension UserListViewModel {
var snapshot = NSDiffableDataSourceSnapshot()
snapshot.appendSections([.main])
snapshot.appendItems([.bottomLoader], toSection: .main)
- if #available(iOS 15.0, *) {
- diffableDataSource?.applySnapshotUsingReloadData(snapshot, completion: nil)
- } else {
- // Fallback on earlier versions
- diffableDataSource?.apply(snapshot, animatingDifferences: false)
- }
-
+ diffableDataSource?.applySnapshotUsingReloadData(snapshot)
+
// trigger initial loading
stateMachine.enter(UserListViewModel.State.Reloading.self)
diff --git a/Mastodon/Scene/Profile/UserLIst/UserListViewModel+State.swift b/Mastodon/Scene/Profile/UserLIst/UserListViewModel+State.swift
index c7b3e20cd..d0f7feb88 100644
--- a/Mastodon/Scene/Profile/UserLIst/UserListViewModel+State.swift
+++ b/Mastodon/Scene/Profile/UserLIst/UserListViewModel+State.swift
@@ -134,7 +134,7 @@ extension UserListViewModel.State {
maxID = nil
}
- guard let viewModel = viewModel, let stateMachine = stateMachine else { return }
+ guard let viewModel = viewModel else { return }
let maxID = self.maxID
diff --git a/Mastodon/Scene/Report/Report/ReportViewController.swift b/Mastodon/Scene/Report/Report/ReportViewController.swift
index f1418c5a1..fb0417a7f 100644
--- a/Mastodon/Scene/Report/Report/ReportViewController.swift
+++ b/Mastodon/Scene/Report/Report/ReportViewController.swift
@@ -61,12 +61,7 @@ extension ReportViewController {
reportReasonViewController.view.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(reportReasonViewController.view)
reportReasonViewController.didMove(toParent: self)
- NSLayoutConstraint.activate([
- reportReasonViewController.view.topAnchor.constraint(equalTo: view.topAnchor),
- reportReasonViewController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor),
- reportReasonViewController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor),
- reportReasonViewController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor),
- ])
+ reportReasonViewController.view.pinToParent()
}
}
@@ -126,7 +121,7 @@ extension ReportViewController: ReportServerRulesViewControllerDelegate {
return
}
- coordinator.present(
+ _ = coordinator.present(
scene: .reportStatus(viewModel: viewModel.reportStatusViewModel),
from: self,
transition: .show
diff --git a/Mastodon/Scene/Report/ReportReason/ReportReasonViewController.swift b/Mastodon/Scene/Report/ReportReason/ReportReasonViewController.swift
index 2e8e53d18..517873abe 100644
--- a/Mastodon/Scene/Report/ReportReason/ReportReasonViewController.swift
+++ b/Mastodon/Scene/Report/ReportReason/ReportReasonViewController.swift
@@ -57,12 +57,7 @@ extension ReportReasonViewController {
addChild(hostingViewController)
hostingViewController.view.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(hostingViewController.view)
- NSLayoutConstraint.activate([
- hostingViewController.view.topAnchor.constraint(equalTo: view.topAnchor),
- hostingViewController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor),
- hostingViewController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor),
- hostingViewController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor),
- ])
+ hostingViewController.view.pinToParent()
navigationActionView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(navigationActionView)
diff --git a/Mastodon/Scene/Report/ReportResult/ReportResultViewController.swift b/Mastodon/Scene/Report/ReportResult/ReportResultViewController.swift
index 10dcdf373..1d67ad6c3 100644
--- a/Mastodon/Scene/Report/ReportResult/ReportResultViewController.swift
+++ b/Mastodon/Scene/Report/ReportResult/ReportResultViewController.swift
@@ -60,12 +60,7 @@ extension ReportResultViewController {
addChild(hostingViewController)
hostingViewController.view.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(hostingViewController.view)
- NSLayoutConstraint.activate([
- hostingViewController.view.topAnchor.constraint(equalTo: view.topAnchor),
- hostingViewController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor),
- hostingViewController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor),
- hostingViewController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor),
- ])
+ hostingViewController.view.pinToParent()
navigationActionView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(navigationActionView)
diff --git a/Mastodon/Scene/Report/ReportServerRules/ReportServerRulesViewController.swift b/Mastodon/Scene/Report/ReportServerRules/ReportServerRulesViewController.swift
index 3f1cdf331..00be4d800 100644
--- a/Mastodon/Scene/Report/ReportServerRules/ReportServerRulesViewController.swift
+++ b/Mastodon/Scene/Report/ReportServerRules/ReportServerRulesViewController.swift
@@ -63,12 +63,7 @@ extension ReportServerRulesViewController {
addChild(hostingViewController)
hostingViewController.view.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(hostingViewController.view)
- NSLayoutConstraint.activate([
- hostingViewController.view.topAnchor.constraint(equalTo: view.topAnchor),
- hostingViewController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor),
- hostingViewController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor),
- hostingViewController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor),
- ])
+ hostingViewController.view.pinToParent()
navigationActionView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(navigationActionView)
diff --git a/Mastodon/Scene/Report/ReportStatus/ReportStatusViewController.swift b/Mastodon/Scene/Report/ReportStatus/ReportStatusViewController.swift
index d45c196cd..3385a12ef 100644
--- a/Mastodon/Scene/Report/ReportStatus/ReportStatusViewController.swift
+++ b/Mastodon/Scene/Report/ReportStatus/ReportStatusViewController.swift
@@ -47,11 +47,7 @@ class ReportStatusViewController: UIViewController, NeedsDependency, ReportViewC
tableView.backgroundColor = .clear
tableView.keyboardDismissMode = .onDrag
tableView.allowsMultipleSelection = true
- if #available(iOS 15.0, *) {
- tableView.sectionHeaderTopPadding = .leastNonzeroMagnitude
- } else {
- // Fallback on earlier versions
- }
+ tableView.sectionHeaderTopPadding = .leastNonzeroMagnitude
return tableView
}()
@@ -80,12 +76,7 @@ extension ReportStatusViewController {
tableView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(tableView)
- NSLayoutConstraint.activate([
- tableView.topAnchor.constraint(equalTo: view.topAnchor),
- tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
- tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
- tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
- ])
+ tableView.pinToParent()
tableView.delegate = self
viewModel.setupDiffableDataSource(
@@ -128,6 +119,10 @@ extension ReportStatusViewController {
.assign(to: \.isEnabled, on: navigationActionView.nextButton)
.store(in: &disposeBag)
+ if !viewModel.selectStatuses.isEmpty {
+ navigationActionView.hidesBackButton = true
+ }
+
navigationActionView.backButton.addTarget(self, action: #selector(ReportStatusViewController.skipButtonDidPressed(_:)), for: .touchUpInside)
navigationActionView.nextButton.addTarget(self, action: #selector(ReportStatusViewController.nextButtonDidPressed(_:)), for: .touchUpInside)
}
diff --git a/Mastodon/Scene/Report/ReportStatus/ReportStatusViewModel+Diffable.swift b/Mastodon/Scene/Report/ReportStatus/ReportStatusViewModel+Diffable.swift
index 9879863d6..fce56a2b9 100644
--- a/Mastodon/Scene/Report/ReportStatus/ReportStatusViewModel+Diffable.swift
+++ b/Mastodon/Scene/Report/ReportStatus/ReportStatusViewModel+Diffable.swift
@@ -65,7 +65,7 @@ extension ReportStatusViewModel {
break
}
- diffableDataSource.applySnapshot(snapshot, animated: false) { [weak self] in
+ diffableDataSource.apply(snapshot, animatingDifferences: false) { [weak self] in
guard let self = self else { return }
guard let diffableDataSource = self.diffableDataSource else { return }
diff --git a/Mastodon/Scene/Report/ReportStatus/ReportStatusViewModel+State.swift b/Mastodon/Scene/Report/ReportStatus/ReportStatusViewModel+State.swift
index 01e8715d1..79807cf0f 100644
--- a/Mastodon/Scene/Report/ReportStatus/ReportStatusViewModel+State.swift
+++ b/Mastodon/Scene/Report/ReportStatus/ReportStatusViewModel+State.swift
@@ -75,7 +75,7 @@ extension ReportStatusViewModel.State {
override func didEnter(from previousState: GKState?) {
super.didEnter(from: previousState)
- guard let viewModel = viewModel, let stateMachine = stateMachine else { return }
+ guard let viewModel else { return }
let maxID = viewModel.statusFetchedResultsController.statusIDs.last
diff --git a/Mastodon/Scene/Report/ReportStatus/ReportStatusViewModel.swift b/Mastodon/Scene/Report/ReportStatus/ReportStatusViewModel.swift
index 5b80a9f3a..c1c79af48 100644
--- a/Mastodon/Scene/Report/ReportStatus/ReportStatusViewModel.swift
+++ b/Mastodon/Scene/Report/ReportStatus/ReportStatusViewModel.swift
@@ -71,9 +71,7 @@ class ReportStatusViewModel {
}
$selectStatuses
- .map { statuses -> Bool in
- return status == nil ? !statuses.isEmpty : statuses.count > 1
- }
+ .map { !$0.isEmpty }
.assign(to: &$isNextButtonEnabled)
}
diff --git a/Mastodon/Scene/Report/ReportSupplementary/ReportSupplementaryViewController.swift b/Mastodon/Scene/Report/ReportSupplementary/ReportSupplementaryViewController.swift
index e644c29ea..a84a3a650 100644
--- a/Mastodon/Scene/Report/ReportSupplementary/ReportSupplementaryViewController.swift
+++ b/Mastodon/Scene/Report/ReportSupplementary/ReportSupplementaryViewController.swift
@@ -52,11 +52,7 @@ final class ReportSupplementaryViewController: UIViewController, NeedsDependency
tableView.separatorStyle = .none
tableView.backgroundColor = .clear
tableView.keyboardDismissMode = .onDrag
- if #available(iOS 15.0, *) {
- tableView.sectionHeaderTopPadding = .leastNonzeroMagnitude
- } else {
- // Fallback on earlier versions
- }
+ tableView.sectionHeaderTopPadding = .leastNonzeroMagnitude
return tableView
}()
@@ -93,12 +89,7 @@ extension ReportSupplementaryViewController {
tableView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(tableView)
- NSLayoutConstraint.activate([
- tableView.topAnchor.constraint(equalTo: view.topAnchor),
- tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
- tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
- tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
- ])
+ tableView.pinToParent()
tableView.delegate = self
viewModel.setupDiffableDataSource(
diff --git a/Mastodon/Scene/Report/Share/Cell/ReportCommentTableViewCell.swift b/Mastodon/Scene/Report/Share/Cell/ReportCommentTableViewCell.swift
index d735a094c..ad6ce252a 100644
--- a/Mastodon/Scene/Report/Share/Cell/ReportCommentTableViewCell.swift
+++ b/Mastodon/Scene/Report/Share/Cell/ReportCommentTableViewCell.swift
@@ -74,11 +74,8 @@ extension ReportCommentTableViewCell {
commentTextView.translatesAutoresizingMaskIntoConstraints = false
commentTextViewShadowBackgroundContainer.addSubview(commentTextView)
+ commentTextView.pinToParent()
NSLayoutConstraint.activate([
- commentTextView.topAnchor.constraint(equalTo: commentTextViewShadowBackgroundContainer.topAnchor),
- commentTextView.leadingAnchor.constraint(equalTo: commentTextViewShadowBackgroundContainer.leadingAnchor),
- commentTextView.trailingAnchor.constraint(equalTo: commentTextViewShadowBackgroundContainer.trailingAnchor),
- commentTextView.bottomAnchor.constraint(equalTo: commentTextViewShadowBackgroundContainer.bottomAnchor),
commentTextView.heightAnchor.constraint(greaterThanOrEqualToConstant: 100).priority(.defaultHigh),
])
}
diff --git a/Mastodon/Scene/Report/Share/Cell/ReportResultActionTableViewCell.swift b/Mastodon/Scene/Report/Share/Cell/ReportResultActionTableViewCell.swift
index 9b605a0c7..1828035a6 100644
--- a/Mastodon/Scene/Report/Share/Cell/ReportResultActionTableViewCell.swift
+++ b/Mastodon/Scene/Report/Share/Cell/ReportResultActionTableViewCell.swift
@@ -103,12 +103,7 @@ extension ReportResultActionTableViewCell {
reportBannerLabel.translatesAutoresizingMaskIntoConstraints = false
reportBannerShadowContainer.addSubview(reportBannerLabel)
- NSLayoutConstraint.activate([
- reportBannerLabel.topAnchor.constraint(equalTo: reportBannerShadowContainer.topAnchor),
- reportBannerLabel.leadingAnchor.constraint(equalTo: reportBannerShadowContainer.leadingAnchor),
- reportBannerLabel.trailingAnchor.constraint(equalTo: reportBannerShadowContainer.trailingAnchor),
- reportBannerLabel.bottomAnchor.constraint(equalTo: reportBannerShadowContainer.bottomAnchor),
- ])
+ reportBannerLabel.pinToParent()
}
diff --git a/Mastodon/Scene/Report/Share/ReportViewControllerAppearance.swift b/Mastodon/Scene/Report/Share/ReportViewControllerAppearance.swift
index fb9bcd63f..34d59a437 100644
--- a/Mastodon/Scene/Report/Share/ReportViewControllerAppearance.swift
+++ b/Mastodon/Scene/Report/Share/ReportViewControllerAppearance.swift
@@ -42,11 +42,7 @@ extension ReportViewControllerAppearance {
navigationItem.standardAppearance = barAppearance
navigationItem.compactAppearance = barAppearance
navigationItem.scrollEdgeAppearance = barAppearance
- if #available(iOS 15.0, *) {
- navigationItem.compactScrollEdgeAppearance = barAppearance
- } else {
- // Fallback on earlier versions
- }
+ navigationItem.compactScrollEdgeAppearance = barAppearance
}
func setupNavigationBarBackgroundView() {
diff --git a/Mastodon/Scene/Root/ContentSplitViewController.swift b/Mastodon/Scene/Root/ContentSplitViewController.swift
index 3f4758e8e..a10f0ed9b 100644
--- a/Mastodon/Scene/Root/ContentSplitViewController.swift
+++ b/Mastodon/Scene/Root/ContentSplitViewController.swift
@@ -124,4 +124,17 @@ extension ContentSplitViewController: SidebarViewControllerDelegate {
accountListViewController.preferredContentSize = CGSize(width: 375, height: 400)
}
+ func sidebarViewController(_ sidebarViewController: SidebarViewController, didDoubleTapItem item: SidebarViewModel.Item, sourceView: UIView) {
+ guard case let .tab(tab) = item, tab == .me else { return }
+ guard let authContext = authContext else { return }
+ assert(Thread.isMainThread)
+
+ guard let nextAccount = context.nextAccount(in: authContext) else { return }
+
+ Task { @MainActor in
+ let isActive = try await context.authenticationService.activeMastodonUser(domain: nextAccount.domain, userID: nextAccount.userID)
+ guard isActive else { return }
+ self.coordinator.setup()
+ }
+ }
}
diff --git a/Mastodon/Scene/Root/MainTab/MainTabBarController.swift b/Mastodon/Scene/Root/MainTab/MainTabBarController.swift
index 987c1141b..b0af9b6d6 100644
--- a/Mastodon/Scene/Root/MainTab/MainTabBarController.swift
+++ b/Mastodon/Scene/Root/MainTab/MainTabBarController.swift
@@ -8,6 +8,7 @@
import os.log
import UIKit
import Combine
+import CoreDataStack
import SafariServices
import MastodonAsset
import MastodonCore
@@ -42,6 +43,7 @@ class MainTabBarController: UITabBarController {
static let avatarButtonSize = CGSize(width: 25, height: 25)
let avatarButton = CircleAvatarButton()
+ let accountSwitcherChevron = UIImageView(image: .chevronUpChevronDown)
@Published var currentTab: Tab = .home
@@ -49,7 +51,7 @@ class MainTabBarController: UITabBarController {
case home
case search
case compose
- case notification
+ case notifications
case me
var tag: Int {
@@ -59,9 +61,9 @@ class MainTabBarController: UITabBarController {
var title: String {
switch self {
case .home: return L10n.Common.Controls.Tabs.home
- case .search: return L10n.Common.Controls.Tabs.search
+ case .search: return L10n.Common.Controls.Tabs.searchAndExplore
case .compose: return L10n.Common.Controls.Actions.compose
- case .notification: return L10n.Common.Controls.Tabs.notification
+ case .notifications: return L10n.Common.Controls.Tabs.notifications
case .me: return L10n.Common.Controls.Tabs.profile
}
}
@@ -71,7 +73,7 @@ class MainTabBarController: UITabBarController {
case .home: return Asset.ObjectsAndTools.house.image.withRenderingMode(.alwaysTemplate)
case .search: return Asset.ObjectsAndTools.magnifyingglass.image.withRenderingMode(.alwaysTemplate)
case .compose: return Asset.ObjectsAndTools.squareAndPencil.image.withRenderingMode(.alwaysTemplate)
- case .notification: return Asset.ObjectsAndTools.bell.image.withRenderingMode(.alwaysTemplate)
+ case .notifications: return Asset.ObjectsAndTools.bell.image.withRenderingMode(.alwaysTemplate)
case .me: return UIImage(systemName: "person")!
}
}
@@ -81,7 +83,7 @@ class MainTabBarController: UITabBarController {
case .home: return Asset.ObjectsAndTools.houseFill.image.withRenderingMode(.alwaysTemplate)
case .search: return Asset.ObjectsAndTools.magnifyingglassFill.image.withRenderingMode(.alwaysTemplate)
case .compose: return Asset.ObjectsAndTools.squareAndPencil.image.withRenderingMode(.alwaysTemplate)
- case .notification: return Asset.ObjectsAndTools.bellFill.image.withRenderingMode(.alwaysTemplate)
+ case .notifications: return Asset.ObjectsAndTools.bellFill.image.withRenderingMode(.alwaysTemplate)
case .me: return UIImage(systemName: "person.fill")!
}
}
@@ -91,7 +93,7 @@ class MainTabBarController: UITabBarController {
case .home: return Asset.ObjectsAndTools.house.image.withRenderingMode(.alwaysTemplate).resized(size: CGSize(width: 80, height: 80))
case .search: return Asset.ObjectsAndTools.magnifyingglass.image.withRenderingMode(.alwaysTemplate).resized(size: CGSize(width: 80, height: 80))
case .compose: return Asset.ObjectsAndTools.squareAndPencil.image.withRenderingMode(.alwaysTemplate).resized(size: CGSize(width: 80, height: 80))
- case .notification: return Asset.ObjectsAndTools.bell.image.withRenderingMode(.alwaysTemplate).resized(size: CGSize(width: 80, height: 80))
+ case .notifications: return Asset.ObjectsAndTools.bell.image.withRenderingMode(.alwaysTemplate).resized(size: CGSize(width: 80, height: 80))
case .me: return UIImage(systemName: "person", withConfiguration: UIImage.SymbolConfiguration(pointSize: 80))!
}
}
@@ -101,7 +103,7 @@ class MainTabBarController: UITabBarController {
case .home: return Asset.ObjectsAndTools.house.image.withRenderingMode(.alwaysTemplate)
case .search: return Asset.ObjectsAndTools.magnifyingglass.image.withRenderingMode(.alwaysTemplate)
case .compose: return Asset.ObjectsAndTools.squareAndPencil.image.withRenderingMode(.alwaysTemplate)
- case .notification: return Asset.ObjectsAndTools.bell.image.withRenderingMode(.alwaysTemplate)
+ case .notifications: return Asset.ObjectsAndTools.bell.image.withRenderingMode(.alwaysTemplate)
case .me: return UIImage(systemName: "person")!
}
}
@@ -127,7 +129,7 @@ class MainTabBarController: UITabBarController {
viewController = _viewController
case .compose:
viewController = UIViewController()
- case .notification:
+ case .notifications:
let _viewController = NotificationViewController()
_viewController.context = context
_viewController.coordinator = coordinator
@@ -218,7 +220,7 @@ extension MainTabBarController {
let alertController = UIAlertController(for: error, title: nil, preferredStyle: .alert)
let okAction = UIAlertAction(title: "OK", style: .default, handler: nil)
alertController.addAction(okAction)
- coordinator.present(
+ _ = coordinator.present(
scene: .alertController(alertController: alertController),
from: nil,
transition: .alertController(animated: true, completion: nil)
@@ -272,7 +274,7 @@ extension MainTabBarController {
} ?? false
let image: UIImage = {
- if currentTab == .notification {
+ if currentTab == .notifications {
return hasUnreadPushNotification ? Asset.ObjectsAndTools.bellBadgeFill.image.withRenderingMode(.alwaysTemplate) : Asset.ObjectsAndTools.bellFill.image.withRenderingMode(.alwaysTemplate)
} else {
return hasUnreadPushNotification ? Asset.ObjectsAndTools.bellBadge.image.withRenderingMode(.alwaysTemplate) : Asset.ObjectsAndTools.bell.image.withRenderingMode(.alwaysTemplate)
@@ -306,12 +308,11 @@ extension MainTabBarController {
guard user.managedObjectContext != nil else { return }
self.avatarURL = user.avatarImageURL()
}
-
+
// a11y
let _profileTabItem = self.tabBar.items?.first { item in item.tag == Tab.me.tag }
guard let profileTabItem = _profileTabItem else { return }
- let currentUserDisplayName = user.displayNameWithFallback ?? "no user"
- profileTabItem.accessibilityHint = L10n.Scene.AccountList.tabBarHint(currentUserDisplayName)
+ profileTabItem.accessibilityHint = L10n.Scene.AccountList.tabBarHint(user.displayNameWithFallback)
context.authenticationService.updateActiveUserAccountPublisher
.sink { [weak self] in
@@ -325,7 +326,14 @@ extension MainTabBarController {
let tabBarLongPressGestureRecognizer = UILongPressGestureRecognizer()
tabBarLongPressGestureRecognizer.addTarget(self, action: #selector(MainTabBarController.tabBarLongPressGestureRecognizerHandler(_:)))
tabBar.addGestureRecognizer(tabBarLongPressGestureRecognizer)
-
+
+ // todo: reconsider the "double tap to change account" feature -> https://github.com/mastodon/mastodon-ios/issues/628
+// let tabBarDoubleTapGestureRecognizer = UITapGestureRecognizer()
+// tabBarDoubleTapGestureRecognizer.numberOfTapsRequired = 2
+// tabBarDoubleTapGestureRecognizer.addTarget(self, action: #selector(MainTabBarController.tabBarDoubleTapGestureRecognizerHandler(_:)))
+// tabBarDoubleTapGestureRecognizer.delaysTouchesEnded = false
+// tabBar.addGestureRecognizer(tabBarDoubleTapGestureRecognizer)
+
self.isReadyForWizardAvatarButton = authContext != nil
$currentTab
@@ -371,14 +379,12 @@ extension MainTabBarController {
let composeViewModel = ComposeViewModel(
context: context,
authContext: authContext,
- kind: .post
+ destination: .topLevel
)
_ = coordinator.present(scene: .compose(viewModel: composeViewModel), from: nil, transition: .modal(animated: true, completion: nil))
}
- @objc private func tabBarLongPressGestureRecognizerHandler(_ sender: UILongPressGestureRecognizer) {
- guard sender.state == .began else { return }
-
+ private func touchedTab(by sender: UIGestureRecognizer) -> Tab? {
var _tab: Tab?
let location = sender.location(in: tabBar)
for item in tabBar.items ?? [] {
@@ -390,7 +396,34 @@ extension MainTabBarController {
break
}
- guard let tab = _tab else { return }
+ return _tab
+ }
+
+ @objc private func tabBarDoubleTapGestureRecognizerHandler(_ sender: UITapGestureRecognizer) {
+ guard sender.state == .ended else { return }
+ guard let tab = touchedTab(by: sender) else { return }
+ logger.debug("\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): double tap \(tab.title) tab")
+
+ switch tab {
+ case .me:
+ guard let authContext = authContext else { return }
+ assert(Thread.isMainThread)
+
+ guard let nextAccount = context.nextAccount(in: authContext) else { return }
+
+ Task { @MainActor in
+ let isActive = try await context.authenticationService.activeMastodonUser(domain: nextAccount.domain, userID: nextAccount.userID)
+ guard isActive else { return }
+ self.coordinator.setup()
+ }
+ default:
+ break
+ }
+ }
+
+ @objc private func tabBarLongPressGestureRecognizerHandler(_ sender: UILongPressGestureRecognizer) {
+ guard sender.state == .began else { return }
+ guard let tab = touchedTab(by: sender) else { return }
logger.debug("\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): long press \(tab.title) tab")
switch tab {
@@ -443,12 +476,7 @@ extension MainTabBarController {
composeButton.translatesAutoresizingMaskIntoConstraints = false
composeButttonShadowBackgroundContainer.addSubview(composeButton)
- NSLayoutConstraint.activate([
- composeButton.topAnchor.constraint(equalTo: composeButttonShadowBackgroundContainer.topAnchor),
- composeButton.leadingAnchor.constraint(equalTo: composeButttonShadowBackgroundContainer.leadingAnchor),
- composeButton.trailingAnchor.constraint(equalTo: composeButttonShadowBackgroundContainer.trailingAnchor),
- composeButton.bottomAnchor.constraint(equalTo: composeButttonShadowBackgroundContainer.bottomAnchor),
- ])
+ composeButton.pinToParent()
composeButton.setContentHuggingPriority(.required - 1, for: .horizontal)
composeButton.setContentHuggingPriority(.required - 1, for: .vertical)
}
@@ -474,13 +502,20 @@ extension MainTabBarController {
}
anchorImageView.alpha = 0
+ accountSwitcherChevron.translatesAutoresizingMaskIntoConstraints = false
+ view.addSubview(accountSwitcherChevron)
+
self.avatarButton.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(self.avatarButton)
NSLayoutConstraint.activate([
- self.avatarButton.centerXAnchor.constraint(equalTo: anchorImageView.centerXAnchor),
+ self.avatarButton.centerXAnchor.constraint(equalTo: anchorImageView.centerXAnchor, constant: -16),
self.avatarButton.centerYAnchor.constraint(equalTo: anchorImageView.centerYAnchor),
self.avatarButton.widthAnchor.constraint(equalToConstant: MainTabBarController.avatarButtonSize.width).priority(.required - 1),
self.avatarButton.heightAnchor.constraint(equalToConstant: MainTabBarController.avatarButtonSize.height).priority(.required - 1),
+ accountSwitcherChevron.widthAnchor.constraint(equalToConstant: 10),
+ accountSwitcherChevron.heightAnchor.constraint(equalToConstant: 18),
+ accountSwitcherChevron.leadingAnchor.constraint(equalTo: avatarButton.trailingAnchor, constant: 8),
+ accountSwitcherChevron.centerYAnchor.constraint(equalTo: avatarButton.centerYAnchor)
])
self.avatarButton.setContentHuggingPriority(.required - 1, for: .horizontal)
self.avatarButton.setContentHuggingPriority(.required - 1, for: .vertical)
@@ -488,6 +523,7 @@ extension MainTabBarController {
}
private func updateAvatarButtonAppearance() {
+ accountSwitcherChevron.tintColor = currentTab == .me ? .label : .secondaryLabel
avatarButton.borderColor = currentTab == .me ? .label : .systemFill
avatarButton.setNeedsLayout()
}
@@ -533,25 +569,24 @@ extension MainTabBarController: UITabBarControllerDelegate {
composeButtonDidPressed(tabBarController)
return false
}
+
+ // Assert index is as same as the tab rawValue. This check needs to be done `shouldSelect`
+ // because the nav controller has already popped in `didSelect`.
+ if currentTab.rawValue == tabBarController.selectedIndex,
+ let navigationController = viewController as? UINavigationController,
+ navigationController.viewControllers.count == 1,
+ let scrollViewContainer = navigationController.topViewController as? ScrollViewContainer {
+ scrollViewContainer.scrollToTop(animated: true)
+ }
+
return true
}
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: select %s", ((#file as NSString).lastPathComponent), #line, #function, viewController.debugDescription)
- defer {
- if let tab = Tab(rawValue: viewController.tabBarItem.tag) {
- currentTab = tab
- }
+ if let tab = Tab(rawValue: viewController.tabBarItem.tag) {
+ currentTab = tab
}
- // assert index is as same as the tab rawValue
- guard currentTab.rawValue == tabBarController.selectedIndex,
- let navigationController = viewController as? UINavigationController,
- navigationController.viewControllers.count == 1,
- let scrollViewContainer = navigationController.topViewController as? ScrollViewContainer else {
- return
- }
-
- scrollViewContainer.scrollToTop(animated: true)
}
}
@@ -618,7 +653,7 @@ extension MainTabBarController {
let tabs: [Tab] = [
.home,
.search,
- .notification,
+ .notifications,
.me
]
for (i, tab) in tabs.enumerated() {
@@ -768,7 +803,7 @@ extension MainTabBarController {
let composeViewModel = ComposeViewModel(
context: context,
authContext: authContext,
- kind: .post
+ destination: .topLevel
)
_ = coordinator.present(scene: .compose(viewModel: composeViewModel), from: nil, transition: .modal(animated: true, completion: nil))
}
diff --git a/Mastodon/Scene/Root/Sidebar/SidebarViewController.swift b/Mastodon/Scene/Root/Sidebar/SidebarViewController.swift
index 70e1239b6..0ffcd4c8b 100644
--- a/Mastodon/Scene/Root/Sidebar/SidebarViewController.swift
+++ b/Mastodon/Scene/Root/Sidebar/SidebarViewController.swift
@@ -15,6 +15,7 @@ import MastodonUI
protocol SidebarViewControllerDelegate: AnyObject {
func sidebarViewController(_ sidebarViewController: SidebarViewController, didSelectTab tab: MainTabBarController.Tab)
func sidebarViewController(_ sidebarViewController: SidebarViewController, didLongPressItem item: SidebarViewModel.Item, sourceView: UIView)
+ func sidebarViewController(_ sidebarViewController: SidebarViewController, didDoubleTapItem item: SidebarViewModel.Item, sourceView: UIView)
}
final class SidebarViewController: UIViewController, NeedsDependency {
@@ -101,12 +102,7 @@ extension SidebarViewController {
collectionView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(collectionView)
- NSLayoutConstraint.activate([
- collectionView.topAnchor.constraint(equalTo: view.topAnchor),
- collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
- collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
- collectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
- ])
+ collectionView.pinToParent()
secondaryCollectionView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(secondaryCollectionView)
@@ -143,6 +139,15 @@ extension SidebarViewController {
let sidebarLongPressGestureRecognizer = UILongPressGestureRecognizer()
sidebarLongPressGestureRecognizer.addTarget(self, action: #selector(SidebarViewController.sidebarLongPressGestureRecognizerHandler(_:)))
collectionView.addGestureRecognizer(sidebarLongPressGestureRecognizer)
+
+ // todo: reconsider the "double tap to change account" feature -> https://github.com/mastodon/mastodon-ios/issues/628
+// let sidebarDoubleTapGestureRecognizer = UITapGestureRecognizer()
+// sidebarDoubleTapGestureRecognizer.numberOfTapsRequired = 2
+// sidebarDoubleTapGestureRecognizer.addTarget(self, action: #selector(SidebarViewController.sidebarDoubleTapGestureRecognizerHandler(_:)))
+// sidebarDoubleTapGestureRecognizer.delaysTouchesEnded = false
+// sidebarDoubleTapGestureRecognizer.cancelsTouchesInView = true
+// collectionView.addGestureRecognizer(sidebarDoubleTapGestureRecognizer)
+
}
private func setupBackground(theme: Theme) {
@@ -176,6 +181,20 @@ extension SidebarViewController {
guard let cell = collectionView.cellForItem(at: indexPath) else { return }
delegate?.sidebarViewController(self, didLongPressItem: item, sourceView: cell)
}
+
+ @objc private func sidebarDoubleTapGestureRecognizerHandler(_ sender: UITapGestureRecognizer) {
+ guard sender.state == .ended else { return }
+
+ logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public)")
+ assert(sender.view === collectionView)
+
+ let position = sender.location(in: collectionView)
+ guard let indexPath = collectionView.indexPathForItem(at: position) else { return }
+ guard let diffableDataSource = viewModel.diffableDataSource else { return }
+ guard let item = diffableDataSource.itemIdentifier(for: indexPath) else { return }
+ guard let cell = collectionView.cellForItem(at: indexPath) else { return }
+ delegate?.sidebarViewController(self, didDoubleTapItem: item, sourceView: cell)
+ }
}
@@ -208,7 +227,7 @@ extension SidebarViewController: UICollectionViewDelegate {
let composeViewModel = ComposeViewModel(
context: context,
authContext: authContext,
- kind: .post
+ destination: .topLevel
)
_ = coordinator.present(scene: .compose(viewModel: composeViewModel), from: self, transition: .modal(animated: true, completion: nil))
default:
diff --git a/Mastodon/Scene/Root/Sidebar/SidebarViewModel.swift b/Mastodon/Scene/Root/Sidebar/SidebarViewModel.swift
index c3f9e3e36..f6e90dcd9 100644
--- a/Mastodon/Scene/Root/Sidebar/SidebarViewModel.swift
+++ b/Mastodon/Scene/Root/Sidebar/SidebarViewModel.swift
@@ -16,7 +16,6 @@ import MastodonCore
import MastodonLocalization
final class SidebarViewModel {
-
var disposeBag = Set()
// input
@@ -80,6 +79,7 @@ extension SidebarViewModel {
}()
cell.item = SidebarListContentView.Item(
isActive: false,
+ accessoryImage: item == .me ? .chevronUpChevronDown : nil,
title: item.title,
image: item.image,
activeImage: item.selectedImage,
@@ -100,7 +100,7 @@ extension SidebarViewModel {
.store(in: &cell.disposeBag)
switch item {
- case .notification:
+ case .notifications:
Publishers.CombineLatest(
self.context.notificationService.unreadNotificationCountDidUpdate,
self.$currentTab
@@ -116,7 +116,7 @@ extension SidebarViewModel {
}()
let image: UIImage = {
- if currentTab == .notification {
+ if currentTab == .notifications {
return hasUnreadPushNotification ? Asset.ObjectsAndTools.bellBadgeFill.image.withRenderingMode(.alwaysTemplate) : Asset.ObjectsAndTools.bellFill.image.withRenderingMode(.alwaysTemplate)
} else {
return hasUnreadPushNotification ? Asset.ObjectsAndTools.bellBadge.image.withRenderingMode(.alwaysTemplate) : Asset.ObjectsAndTools.bell.image.withRenderingMode(.alwaysTemplate)
@@ -166,6 +166,7 @@ extension SidebarViewModel {
case .compose:
let item = SidebarListContentView.Item(
isActive: false,
+ accessoryImage: self.currentTab == .me ? .chevronUpChevronDown : nil,
title: L10n.Common.Controls.Actions.compose,
image: Asset.ObjectsAndTools.squareAndPencil.image.withRenderingMode(.alwaysTemplate),
activeImage: Asset.ObjectsAndTools.squareAndPencil.image.withRenderingMode(.alwaysTemplate),
@@ -192,7 +193,7 @@ extension SidebarViewModel {
let items: [Item] = [
.tab(.home),
.tab(.search),
- .tab(.notification),
+ .tab(.notifications),
.tab(.me),
.setting,
]
diff --git a/Mastodon/Scene/Root/Sidebar/View/SidebarListContentView.swift b/Mastodon/Scene/Root/Sidebar/View/SidebarListContentView.swift
index 515988405..c2aa1a4f5 100644
--- a/Mastodon/Scene/Root/Sidebar/View/SidebarListContentView.swift
+++ b/Mastodon/Scene/Root/Sidebar/View/SidebarListContentView.swift
@@ -23,7 +23,8 @@ final class SidebarListContentView: UIView, UIContentView {
button.borderColor = UIColor.label
return button
}()
-
+ private let accessoryImageView = UIImageView(image: nil)
+
private var currentConfiguration: ContentConfiguration!
var configuration: UIContentConfiguration {
get {
@@ -60,6 +61,9 @@ extension SidebarListContentView {
imageView.widthAnchor.constraint(equalToConstant: 40).priority(.required - 1),
imageView.heightAnchor.constraint(equalToConstant: 40).priority(.required - 1),
])
+
+ accessoryImageView.translatesAutoresizingMaskIntoConstraints = false
+ addSubview(accessoryImageView)
avatarButton.translatesAutoresizingMaskIntoConstraints = false
addSubview(avatarButton)
@@ -68,6 +72,10 @@ extension SidebarListContentView {
avatarButton.centerYAnchor.constraint(equalTo: imageView.centerYAnchor),
avatarButton.widthAnchor.constraint(equalTo: imageView.widthAnchor, multiplier: 1.0).priority(.required - 2),
avatarButton.heightAnchor.constraint(equalTo: imageView.heightAnchor, multiplier: 1.0).priority(.required - 2),
+ accessoryImageView.widthAnchor.constraint(equalToConstant: 12),
+ accessoryImageView.heightAnchor.constraint(equalToConstant: 22),
+ accessoryImageView.leadingAnchor.constraint(equalTo: avatarButton.trailingAnchor, constant: 4),
+ accessoryImageView.centerYAnchor.constraint(equalTo: avatarButton.centerYAnchor)
])
avatarButton.setContentHuggingPriority(.defaultLow - 10, for: .vertical)
avatarButton.setContentHuggingPriority(.defaultLow - 10, for: .horizontal)
@@ -96,6 +104,9 @@ extension SidebarListContentView {
imageView.isHidden = item.imageURL != nil
avatarButton.isHidden = item.imageURL == nil
imageView.image = item.isActive ? item.activeImage.withRenderingMode(.alwaysTemplate) : item.image.withRenderingMode(.alwaysTemplate)
+ accessoryImageView.image = item.accessoryImage
+ accessoryImageView.isHidden = item.accessoryImage == nil
+ accessoryImageView.tintColor = item.isActive ? .label : .secondaryLabel
avatarButton.avatarImageView.setImage(
url: item.imageURL,
placeholder: avatarButton.avatarImageView.image ?? .placeholder(color: .systemFill), // reuse to avoid blink
@@ -112,7 +123,8 @@ extension SidebarListContentView {
var isSelected: Bool = false
var isHighlighted: Bool = false
var isActive: Bool
-
+ var accessoryImage: UIImage? = nil
+
// model
let title: String
var image: UIImage
@@ -124,6 +136,7 @@ extension SidebarListContentView {
return lhs.isSelected == rhs.isSelected
&& lhs.isHighlighted == rhs.isHighlighted
&& lhs.isActive == rhs.isActive
+ && lhs.accessoryImage == rhs.accessoryImage
&& lhs.title == rhs.title
&& lhs.image == rhs.image
&& lhs.activeImage == rhs.activeImage
@@ -134,6 +147,7 @@ extension SidebarListContentView {
hasher.combine(isSelected)
hasher.combine(isHighlighted)
hasher.combine(isActive)
+ hasher.combine(accessoryImage)
hasher.combine(title)
hasher.combine(image)
hasher.combine(activeImage)
diff --git a/Mastodon/Scene/Search/Search/Cell/TrendCollectionViewCell.swift b/Mastodon/Scene/Search/Search/Cell/TrendCollectionViewCell.swift
index 30f618625..4436459fb 100644
--- a/Mastodon/Scene/Search/Search/Cell/TrendCollectionViewCell.swift
+++ b/Mastodon/Scene/Search/Search/Cell/TrendCollectionViewCell.swift
@@ -48,12 +48,7 @@ extension TrendCollectionViewCell {
trendView.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(trendView)
- NSLayoutConstraint.activate([
- trendView.topAnchor.constraint(equalTo: contentView.topAnchor),
- trendView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
- trendView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
- trendView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
- ])
+ trendView.pinToParent()
}
override func updateConfiguration(using state: UICellConfigurationState) {
diff --git a/Mastodon/Scene/Search/Search/SearchViewController.swift b/Mastodon/Scene/Search/Search/SearchViewController.swift
index 581fa08b3..e1505121d 100644
--- a/Mastodon/Scene/Search/Search/SearchViewController.swift
+++ b/Mastodon/Scene/Search/Search/SearchViewController.swift
@@ -105,12 +105,7 @@ extension SearchViewController {
addChild(discoveryViewController)
discoveryViewController.view.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(discoveryViewController.view)
- NSLayoutConstraint.activate([
- discoveryViewController.view.topAnchor.constraint(equalTo: view.topAnchor),
- discoveryViewController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor),
- discoveryViewController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor),
- discoveryViewController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor),
- ])
+ discoveryViewController.view.pinToParent()
// discoveryViewController.view.isHidden = true
@@ -140,10 +135,7 @@ extension SearchViewController {
navigationItem.standardAppearance = navigationBarAppearance
navigationItem.scrollEdgeAppearance = navigationBarAppearance
navigationItem.compactAppearance = navigationBarAppearance
-
- if #available(iOS 15, *) {
- navigationItem.compactScrollEdgeAppearance = navigationBarAppearance
- }
+ navigationItem.compactScrollEdgeAppearance = navigationBarAppearance
}
private func setupSearchBar() {
@@ -151,12 +143,7 @@ extension SearchViewController {
searchBar.delegate = self
searchBar.translatesAutoresizingMaskIntoConstraints = false
titleViewContainer.addSubview(searchBar)
- NSLayoutConstraint.activate([
- searchBar.topAnchor.constraint(equalTo: titleViewContainer.topAnchor),
- searchBar.leadingAnchor.constraint(equalTo: titleViewContainer.leadingAnchor),
- searchBar.trailingAnchor.constraint(equalTo: titleViewContainer.trailingAnchor),
- searchBar.bottomAnchor.constraint(equalTo: titleViewContainer.bottomAnchor),
- ])
+ searchBar.pinToParent()
searchBar.setContentHuggingPriority(.required, for: .horizontal)
searchBar.setContentHuggingPriority(.required, for: .vertical)
navigationItem.titleView = titleViewContainer
@@ -174,7 +161,7 @@ extension SearchViewController {
// FIXME:
// use `.customPush(animated: false)` false to disable navigation bar animation for searchBar layout
// but that should be a fade transition whe fixed size searchBar
- self.coordinator.present(scene: .searchDetail(viewModel: searchDetailViewModel), from: self, transition: .customPush(animated: false))
+ _ = self.coordinator.present(scene: .searchDetail(viewModel: searchDetailViewModel), from: self, transition: .customPush(animated: false))
}
.store(in: &disposeBag)
}
diff --git a/Mastodon/Scene/Search/Search/View/SearchRecommendCollectionHeader.swift b/Mastodon/Scene/Search/Search/View/SearchRecommendCollectionHeader.swift
index 1d4788ac8..0a65c6a25 100644
--- a/Mastodon/Scene/Search/Search/View/SearchRecommendCollectionHeader.swift
+++ b/Mastodon/Scene/Search/Search/View/SearchRecommendCollectionHeader.swift
@@ -62,12 +62,7 @@ extension SearchRecommendCollectionHeader {
containerStackView.isLayoutMarginsRelativeArrangement = true
containerStackView.translatesAutoresizingMaskIntoConstraints = false
addSubview(containerStackView)
- NSLayoutConstraint.activate([
- containerStackView.topAnchor.constraint(equalTo: topAnchor),
- containerStackView.leadingAnchor.constraint(equalTo: leadingAnchor),
- containerStackView.bottomAnchor.constraint(equalTo: bottomAnchor),
- containerStackView.trailingAnchor.constraint(equalTo: trailingAnchor)
- ])
+ containerStackView.pinToParent()
let horizontalStackView = UIStackView()
horizontalStackView.spacing = 8
diff --git a/Mastodon/Scene/Search/SearchDetail/SearchDetailViewController.swift b/Mastodon/Scene/Search/SearchDetail/SearchDetailViewController.swift
index 62dd1a2cc..4ec9bce31 100644
--- a/Mastodon/Scene/Search/SearchDetail/SearchDetailViewController.swift
+++ b/Mastodon/Scene/Search/SearchDetail/SearchDetailViewController.swift
@@ -60,7 +60,7 @@ final class SearchDetailViewController: PageboyViewController, NeedsDependency {
let searchController: CustomSearchController = {
let searchController = CustomSearchController()
searchController.automaticallyShowsScopeBar = false
- searchController.dimsBackgroundDuringPresentation = false
+ searchController.obscuresBackgroundDuringPresentation = false
return searchController
}()
private(set) lazy var searchBar: UISearchBar = {
@@ -116,12 +116,7 @@ extension SearchDetailViewController {
searchHistoryViewController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor),
])
} else {
- NSLayoutConstraint.activate([
- searchHistoryViewController.view.topAnchor.constraint(equalTo: view.topAnchor),
- searchHistoryViewController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor),
- searchHistoryViewController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor),
- searchHistoryViewController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor),
- ])
+ searchHistoryViewController.view.pinToParent()
}
transition = Transition(style: .fade, duration: 0.1)
@@ -289,12 +284,7 @@ extension SearchDetailViewController {
navigationBarVisualEffectBackgroundView.translatesAutoresizingMaskIntoConstraints = false
view.insertSubview(navigationBarVisualEffectBackgroundView, belowSubview: navigationBarBackgroundView)
- NSLayoutConstraint.activate([
- navigationBarVisualEffectBackgroundView.topAnchor.constraint(equalTo: navigationBarBackgroundView.topAnchor),
- navigationBarVisualEffectBackgroundView.leadingAnchor.constraint(equalTo: navigationBarBackgroundView.leadingAnchor),
- navigationBarVisualEffectBackgroundView.trailingAnchor.constraint(equalTo: navigationBarBackgroundView.trailingAnchor),
- navigationBarVisualEffectBackgroundView.bottomAnchor.constraint(equalTo: navigationBarBackgroundView.bottomAnchor),
- ])
+ navigationBarVisualEffectBackgroundView.pinTo(to: navigationBarBackgroundView)
} else {
navigationItem.setHidesBackButton(true, animated: false)
navigationItem.titleView = nil
diff --git a/Mastodon/Scene/Search/SearchDetail/SearchHistory/SearchHistoryViewController.swift b/Mastodon/Scene/Search/SearchDetail/SearchHistory/SearchHistoryViewController.swift
index 52d0ffb9c..ac08e7906 100644
--- a/Mastodon/Scene/Search/SearchDetail/SearchHistory/SearchHistoryViewController.swift
+++ b/Mastodon/Scene/Search/SearchDetail/SearchHistory/SearchHistoryViewController.swift
@@ -47,12 +47,7 @@ extension SearchHistoryViewController {
collectionView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(collectionView)
- NSLayoutConstraint.activate([
- collectionView.topAnchor.constraint(equalTo: view.topAnchor),
- collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
- collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
- collectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
- ])
+ collectionView.pinToParent()
collectionView.delegate = self
viewModel.setupDiffableDataSource(
diff --git a/Mastodon/Scene/Search/SearchDetail/SearchHistory/SearchHistoryViewModel+Diffable.swift b/Mastodon/Scene/Search/SearchDetail/SearchHistory/SearchHistoryViewModel+Diffable.swift
index c559523a7..201fa10d2 100644
--- a/Mastodon/Scene/Search/SearchDetail/SearchHistory/SearchHistoryViewModel+Diffable.swift
+++ b/Mastodon/Scene/Search/SearchDetail/SearchHistory/SearchHistoryViewModel+Diffable.swift
@@ -54,7 +54,7 @@ extension SearchHistoryViewModel {
var snapshot = NSDiffableDataSourceSnapshot()
snapshot.appendSections([.main])
snapshot.appendItems(items, toSection: .main)
- diffableDataSource.apply(snapshot, animatingDifferences: false)
+ await diffableDataSource.apply(snapshot, animatingDifferences: false)
} catch {
// do nothing
}
diff --git a/Mastodon/Scene/Search/SearchDetail/SearchResult/SearchResultViewController.swift b/Mastodon/Scene/Search/SearchDetail/SearchResult/SearchResultViewController.swift
index 67de62bf3..6a840f9b5 100644
--- a/Mastodon/Scene/Search/SearchDetail/SearchResult/SearchResultViewController.swift
+++ b/Mastodon/Scene/Search/SearchDetail/SearchResult/SearchResultViewController.swift
@@ -49,12 +49,7 @@ extension SearchResultViewController {
tableView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(tableView)
- NSLayoutConstraint.activate([
- tableView.topAnchor.constraint(equalTo: view.topAnchor),
- tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
- tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
- tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
- ])
+ tableView.pinToParent()
tableView.delegate = self
// tableView.prefetchDataSource = self
diff --git a/Mastodon/Scene/Search/SearchDetail/SearchResult/SearchResultViewModel+Diffable.swift b/Mastodon/Scene/Search/SearchDetail/SearchResult/SearchResultViewModel+Diffable.swift
index 7d243b1fa..b9eb5a9ae 100644
--- a/Mastodon/Scene/Search/SearchDetail/SearchResult/SearchResultViewModel+Diffable.swift
+++ b/Mastodon/Scene/Search/SearchDetail/SearchResult/SearchResultViewModel+Diffable.swift
@@ -33,7 +33,7 @@ extension SearchResultViewModel {
userFetchedResultsController.$records,
$hashtags
)
- .map { statusRecrods, userRecords, hashtags in
+ .map { statusRecords, userRecords, hashtags in
var items: [SearchResultItem] = []
let userItems = userRecords.map { SearchResultItem.user($0) }
@@ -42,7 +42,7 @@ extension SearchResultViewModel {
let hashtagItems = hashtags.map { SearchResultItem.hashtag(tag: $0) }
items.append(contentsOf: hashtagItems)
- let statusItems = statusRecrods.map { SearchResultItem.status($0) }
+ let statusItems = statusRecords.map { SearchResultItem.status($0) }
items.append(contentsOf: statusItems)
return items
diff --git a/Mastodon/Scene/Settings/Cell/SettingsAppearanceTableViewCell.swift b/Mastodon/Scene/Settings/Cell/SettingsAppearanceTableViewCell.swift
index 44b6b39c3..efad9693d 100644
--- a/Mastodon/Scene/Settings/Cell/SettingsAppearanceTableViewCell.swift
+++ b/Mastodon/Scene/Settings/Cell/SettingsAppearanceTableViewCell.swift
@@ -101,12 +101,7 @@ extension SettingsAppearanceTableViewCell {
stackView.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(stackView)
- NSLayoutConstraint.activate([
- stackView.topAnchor.constraint(equalTo: contentView.topAnchor),
- stackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
- stackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
- stackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
- ])
+ stackView.pinToParent()
stackView.addArrangedSubview(systemAppearanceView)
stackView.addArrangedSubview(darkAppearanceView)
diff --git a/Mastodon/Scene/Settings/Cell/SettingsToggleTableViewCell.swift b/Mastodon/Scene/Settings/Cell/SettingsToggleTableViewCell.swift
index 4926dbfce..05d5ecea2 100644
--- a/Mastodon/Scene/Settings/Cell/SettingsToggleTableViewCell.swift
+++ b/Mastodon/Scene/Settings/Cell/SettingsToggleTableViewCell.swift
@@ -86,7 +86,7 @@ extension SettingsToggleTableViewCell {
return nil
default:
// set tint black for Light Mode
- return self.contentView.window?.tintColor ?? nil
+ return self.contentView.window?.tintColor
}
}()
}
diff --git a/Mastodon/Scene/Settings/SettingsViewController.swift b/Mastodon/Scene/Settings/SettingsViewController.swift
index 53a856fd0..fa6948ced 100644
--- a/Mastodon/Scene/Settings/SettingsViewController.swift
+++ b/Mastodon/Scene/Settings/SettingsViewController.swift
@@ -225,12 +225,7 @@ extension SettingsViewController {
setupNavigation()
view.addSubview(tableView)
- NSLayoutConstraint.activate([
- tableView.topAnchor.constraint(equalTo: view.topAnchor),
- tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
- tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
- tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
- ])
+ tableView.pinToParent()
setupTableView()
updateSectionHeaderStackViewLayout()
@@ -401,7 +396,7 @@ extension SettingsViewController: UITableViewDelegate {
)
let okAction = UIAlertAction(title: L10n.Common.Controls.Actions.ok, style: .default, handler: nil)
alertController.addAction(okAction)
- self.coordinator.present(scene: .alertController(alertController: alertController), from: nil, transition: .alertController(animated: true, completion: nil))
+ _ = self.coordinator.present(scene: .alertController(alertController: alertController), from: nil, transition: .alertController(animated: true, completion: nil))
}
.store(in: &disposeBag)
case .signOut:
@@ -549,7 +544,7 @@ extension SettingsViewController: MetaLabelDelegate {
switch meta {
case .url(_, _, let url, _):
guard let url = URL(string: url) else { return }
- coordinator.present(scene: .safari(url: url), from: self, transition: .safariPresent(animated: true, completion: nil))
+ _ = coordinator.present(scene: .safari(url: url), from: self, transition: .safariPresent(animated: true, completion: nil))
default:
assertionFailure()
}
diff --git a/Mastodon/Scene/Settings/View/AppearanceView.swift b/Mastodon/Scene/Settings/View/AppearanceView.swift
index cdc29100b..a09401537 100644
--- a/Mastodon/Scene/Settings/View/AppearanceView.swift
+++ b/Mastodon/Scene/Settings/View/AppearanceView.swift
@@ -85,12 +85,7 @@ extension AppearanceView {
private func setupUI() {
imageView.translatesAutoresizingMaskIntoConstraints = false
imageViewShadowBackgroundContainer.addSubview(imageView)
- NSLayoutConstraint.activate([
- imageView.topAnchor.constraint(equalTo: imageViewShadowBackgroundContainer.topAnchor),
- imageView.leadingAnchor.constraint(equalTo: imageViewShadowBackgroundContainer.leadingAnchor),
- imageView.trailingAnchor.constraint(equalTo: imageViewShadowBackgroundContainer.trailingAnchor),
- imageView.bottomAnchor.constraint(equalTo: imageViewShadowBackgroundContainer.bottomAnchor),
- ])
+ imageView.pinToParent()
imageViewShadowBackgroundContainer.cornerRadius = 4
stackView.addArrangedSubview(imageViewShadowBackgroundContainer)
@@ -100,11 +95,8 @@ extension AppearanceView {
addSubview(stackView)
translatesAutoresizingMaskIntoConstraints = false
stackView.translatesAutoresizingMaskIntoConstraints = false
+ stackView.pinToParent()
NSLayoutConstraint.activate([
- stackView.topAnchor.constraint(equalTo: self.topAnchor),
- stackView.leadingAnchor.constraint(equalTo: self.leadingAnchor),
- stackView.bottomAnchor.constraint(equalTo: self.bottomAnchor),
- stackView.trailingAnchor.constraint(equalTo: self.trailingAnchor),
imageView.heightAnchor.constraint(equalTo: imageView.widthAnchor, multiplier: 121.0 / 100.0), // height / width
])
}
diff --git a/Mastodon/Scene/Share/ContextMenu/ImagePreview/ContextMenuImagePreviewViewController.swift b/Mastodon/Scene/Share/ContextMenu/ImagePreview/ContextMenuImagePreviewViewController.swift
index 0d3c4f574..12820e627 100644
--- a/Mastodon/Scene/Share/ContextMenu/ImagePreview/ContextMenuImagePreviewViewController.swift
+++ b/Mastodon/Scene/Share/ContextMenu/ImagePreview/ContextMenuImagePreviewViewController.swift
@@ -31,12 +31,7 @@ extension ContextMenuImagePreviewViewController {
imageView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(imageView)
- NSLayoutConstraint.activate([
- imageView.topAnchor.constraint(equalTo: view.topAnchor),
- imageView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
- imageView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
- imageView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
- ])
+ imageView.pinToParent()
imageView.image = viewModel.thumbnail
diff --git a/Mastodon/Scene/Share/NavigationController/AdaptiveStatusBarStyleNavigationController.swift b/Mastodon/Scene/Share/NavigationController/AdaptiveStatusBarStyleNavigationController.swift
index 80df8938e..88441f9ad 100644
--- a/Mastodon/Scene/Share/NavigationController/AdaptiveStatusBarStyleNavigationController.swift
+++ b/Mastodon/Scene/Share/NavigationController/AdaptiveStatusBarStyleNavigationController.swift
@@ -14,7 +14,7 @@ class AdaptiveStatusBarStyleNavigationController: UINavigationController {
// Make status bar style adaptive for child view controller
// SeeAlso: `modalPresentationCapturesStatusBarAppearance`
override var childForStatusBarStyle: UIViewController? {
- visibleViewController
+ topViewController
}
}
diff --git a/Mastodon/Scene/Share/View/Content/ContentWarningOverlayView.swift b/Mastodon/Scene/Share/View/Content/ContentWarningOverlayView.swift
index ca46193b6..eb55dc575 100644
--- a/Mastodon/Scene/Share/View/Content/ContentWarningOverlayView.swift
+++ b/Mastodon/Scene/Share/View/Content/ContentWarningOverlayView.swift
@@ -93,12 +93,7 @@ extension ContentWarningOverlayView {
vibrancyVisualEffectView.translatesAutoresizingMaskIntoConstraints = false
blurVisualEffectView.contentView.addSubview(vibrancyVisualEffectView)
- NSLayoutConstraint.activate([
- vibrancyVisualEffectView.topAnchor.constraint(equalTo: blurVisualEffectView.topAnchor),
- vibrancyVisualEffectView.leadingAnchor.constraint(equalTo: blurVisualEffectView.leadingAnchor),
- vibrancyVisualEffectView.trailingAnchor.constraint(equalTo: blurVisualEffectView.trailingAnchor),
- vibrancyVisualEffectView.bottomAnchor.constraint(equalTo: blurVisualEffectView.bottomAnchor),
- ])
+ vibrancyVisualEffectView.pinTo(to: blurVisualEffectView)
vibrancyContentWarningLabel.translatesAutoresizingMaskIntoConstraints = false
vibrancyVisualEffectView.contentView.addSubview(vibrancyContentWarningLabel)
@@ -110,12 +105,7 @@ extension ContentWarningOverlayView {
blurVisualEffectView.translatesAutoresizingMaskIntoConstraints = false
addSubview(blurVisualEffectView)
- NSLayoutConstraint.activate([
- blurVisualEffectView.topAnchor.constraint(equalTo: topAnchor),
- blurVisualEffectView.leadingAnchor.constraint(equalTo: leadingAnchor),
- blurVisualEffectView.trailingAnchor.constraint(equalTo: trailingAnchor),
- blurVisualEffectView.bottomAnchor.constraint(equalTo: bottomAnchor),
- ])
+ blurVisualEffectView.pinToParent()
// blur image style
contentOverlayView.translatesAutoresizingMaskIntoConstraints = false
@@ -134,12 +124,7 @@ extension ContentWarningOverlayView {
blurContentWarningLabelContainer.translatesAutoresizingMaskIntoConstraints = false
contentOverlayView.addSubview(blurContentWarningLabelContainer)
- NSLayoutConstraint.activate([
- blurContentWarningLabelContainer.topAnchor.constraint(equalTo: topAnchor),
- blurContentWarningLabelContainer.leadingAnchor.constraint(equalTo: leadingAnchor),
- blurContentWarningLabelContainer.trailingAnchor.constraint(equalTo: trailingAnchor),
- blurContentWarningLabelContainer.bottomAnchor.constraint(equalTo: bottomAnchor),
- ])
+ blurContentWarningLabelContainer.pinTo(to: self)
let topPaddingView = UIView()
let bottomPaddingView = UIView()
diff --git a/Mastodon/Scene/Share/View/Content/DoubleTitleLabelNavigationBarTitleView.swift b/Mastodon/Scene/Share/View/Content/DoubleTitleLabelNavigationBarTitleView.swift
index 6734b7b77..52d9c9f1a 100644
--- a/Mastodon/Scene/Share/View/Content/DoubleTitleLabelNavigationBarTitleView.swift
+++ b/Mastodon/Scene/Share/View/Content/DoubleTitleLabelNavigationBarTitleView.swift
@@ -47,25 +47,24 @@ extension DoubleTitleLabelNavigationBarTitleView {
containerView.distribution = .fill
containerView.translatesAutoresizingMaskIntoConstraints = false
addSubview(containerView)
- NSLayoutConstraint.activate([
- containerView.topAnchor.constraint(equalTo: topAnchor),
- containerView.leadingAnchor.constraint(equalTo: leadingAnchor),
- containerView.trailingAnchor.constraint(equalTo: trailingAnchor),
- containerView.bottomAnchor.constraint(equalTo: bottomAnchor),
- ])
+ containerView.pinToParent()
containerView.addArrangedSubview(titleLabel)
containerView.addArrangedSubview(subtitleLabel)
+
+ isAccessibilityElement = true
}
func update(title: String, subtitle: String?) {
titleLabel.configure(content: PlaintextMetaContent(string: title))
update(subtitle: subtitle)
+ accessibilityLabel = subtitle.map { "\(title), \($0)" } ?? title
}
func update(titleMetaContent: MetaContent, subtitle: String?) {
titleLabel.configure(content: titleMetaContent)
update(subtitle: subtitle)
+ accessibilityLabel = subtitle.map { "\(titleMetaContent.string), \($0)" } ?? titleMetaContent.string
}
func update(subtitle: String?) {
diff --git a/Mastodon/Scene/Share/View/Content/NotificationView+Configuration.swift b/Mastodon/Scene/Share/View/Content/NotificationView+Configuration.swift
index 98d06fd92..018628473 100644
--- a/Mastodon/Scene/Share/View/Content/NotificationView+Configuration.swift
+++ b/Mastodon/Scene/Share/View/Content/NotificationView+Configuration.swift
@@ -111,6 +111,7 @@ extension NotificationView {
self.viewModel.notificationIndicatorText = nil
return
}
+ self.viewModel.type = type
func createMetaContent(text: String, emojis: MastodonContent.Emojis) -> MetaContent {
let content = MastodonContent(content: text, emojis: emojis)
@@ -154,7 +155,7 @@ extension NotificationView {
)
case .status:
self.viewModel.notificationIndicatorText = createMetaContent(
- text: L10n.Scene.Notification.NotificationDescription.mentionedYou,
+ text: .empty,
emojis: emojis.asDictionary
)
case ._other:
diff --git a/Mastodon/Scene/Share/View/Content/PollOptionView+Configuration.swift b/Mastodon/Scene/Share/View/Content/PollOptionView+Configuration.swift
index 334c9ce15..cbc7c61f8 100644
--- a/Mastodon/Scene/Share/View/Content/PollOptionView+Configuration.swift
+++ b/Mastodon/Scene/Share/View/Content/PollOptionView+Configuration.swift
@@ -33,12 +33,12 @@ extension PollOptionView {
.store(in: &disposeBag)
// percentage
Publishers.CombineLatest(
- option.poll.publisher(for: \.votesCount),
+ option.poll.publisher(for: \.votersCount),
option.publisher(for: \.votesCount)
)
- .map { pollVotesCount, optionVotesCount -> Double? in
- guard pollVotesCount > 0, optionVotesCount >= 0 else { return 0 }
- return Double(optionVotesCount) / Double(pollVotesCount)
+ .map { pollVotersCount, optionVotesCount -> Double? in
+ guard pollVotersCount > 0, optionVotesCount >= 0 else { return 0 }
+ return Double(optionVotesCount) / Double(pollVotersCount)
}
.assign(to: \.percentage, on: viewModel)
.store(in: &disposeBag)
diff --git a/Mastodon/Scene/Share/View/TableviewCell/StatusTableViewCell+ViewModel.swift b/Mastodon/Scene/Share/View/TableviewCell/StatusTableViewCell+ViewModel.swift
index 85184d406..2702d726b 100644
--- a/Mastodon/Scene/Share/View/TableviewCell/StatusTableViewCell+ViewModel.swift
+++ b/Mastodon/Scene/Share/View/TableviewCell/StatusTableViewCell+ViewModel.swift
@@ -67,6 +67,21 @@ extension StatusTableViewCell {
}
}
.store(in: &disposeBag)
+
+ statusView.viewModel.$card
+ .removeDuplicates()
+ .dropFirst()
+ .receive(on: DispatchQueue.main)
+ .sink { [weak tableView, weak self] _ in
+ guard let tableView = tableView else { return }
+ guard let _ = self else { return }
+
+ UIView.performWithoutAnimation {
+ tableView.beginUpdates()
+ tableView.endUpdates()
+ }
+ }
+ .store(in: &disposeBag)
}
}
diff --git a/Mastodon/Scene/Share/View/TableviewCell/StatusTableViewCell.swift b/Mastodon/Scene/Share/View/TableviewCell/StatusTableViewCell.swift
index c9850a0d3..b8482c564 100644
--- a/Mastodon/Scene/Share/View/TableviewCell/StatusTableViewCell.swift
+++ b/Mastodon/Scene/Share/View/TableviewCell/StatusTableViewCell.swift
@@ -86,6 +86,14 @@ extension StatusTableViewCell {
self.accessibilityLabel = accessibilityLabel
}
.store(in: &_disposeBag)
+
+ statusView.viewModel
+ .$translatedFromLanguage
+ .receive(on: DispatchQueue.main)
+ .sink(receiveValue: { [weak self] _ in
+ self?.invalidateIntrinsicContentSize()
+ })
+ .store(in: &_disposeBag)
}
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
diff --git a/Mastodon/Scene/Share/View/TableviewCell/StatusTableViewCellDelegate.swift b/Mastodon/Scene/Share/View/TableviewCell/StatusTableViewCellDelegate.swift
index 034985c03..f21e0573c 100644
--- a/Mastodon/Scene/Share/View/TableviewCell/StatusTableViewCellDelegate.swift
+++ b/Mastodon/Scene/Share/View/TableviewCell/StatusTableViewCellDelegate.swift
@@ -27,6 +27,7 @@ protocol StatusTableViewCellDelegate: AnyObject, AutoGenerateProtocolDelegate {
func tableViewCell(_ cell: UITableViewCell, statusView: StatusView, authorAvatarButtonDidPressed button: AvatarButton)
func tableViewCell(_ cell: UITableViewCell, statusView: StatusView, contentSensitiveeToggleButtonDidPressed button: UIButton)
func tableViewCell(_ cell: UITableViewCell, statusView: StatusView, metaText: MetaText, didSelectMeta meta: Meta)
+ func tableViewCell(_ cell: UITableViewCell, statusView: StatusView, didTapCardWithURL url: URL)
func tableViewCell(_ cell: UITableViewCell, statusView: StatusView, mediaGridContainerView: MediaGridContainerView, mediaView: MediaView, didSelectMediaViewAt index: Int)
func tableViewCell(_ cell: UITableViewCell, statusView: StatusView, pollTableView tableView: UITableView, didSelectRowAt indexPath: IndexPath)
func tableViewCell(_ cell: UITableViewCell, statusView: StatusView, pollVoteButtonPressed button: UIButton)
@@ -36,6 +37,8 @@ protocol StatusTableViewCellDelegate: AnyObject, AutoGenerateProtocolDelegate {
func tableViewCell(_ cell: UITableViewCell, statusView: StatusView, mediaGridContainerView: MediaGridContainerView, mediaSensitiveButtonDidPressed button: UIButton)
func tableViewCell(_ cell: UITableViewCell, statusView: StatusView, statusMetricView: StatusMetricView, reblogButtonDidPressed button: UIButton)
func tableViewCell(_ cell: UITableViewCell, statusView: StatusView, statusMetricView: StatusMetricView, favoriteButtonDidPressed button: UIButton)
+ func tableViewCell(_ cell: UITableViewCell, statusView: StatusView, cardControl: StatusCardControl, didTapURL url: URL)
+ func tableViewCell(_ cell: UITableViewCell, statusView: StatusView, cardControlMenu: StatusCardControl) -> UIMenu?
func tableViewCell(_ cell: UITableViewCell, statusView: StatusView, accessibilityActivate: Void)
// sourcery:end
}
@@ -61,6 +64,10 @@ extension StatusViewDelegate where Self: StatusViewContainerTableViewCell {
delegate?.tableViewCell(self, statusView: statusView, metaText: metaText, didSelectMeta: meta)
}
+ func statusView(_ statusView: StatusView, didTapCardWithURL url: URL) {
+ delegate?.tableViewCell(self, statusView: statusView, didTapCardWithURL: url)
+ }
+
func statusView(_ statusView: StatusView, mediaGridContainerView: MediaGridContainerView, mediaView: MediaView, didSelectMediaViewAt index: Int) {
delegate?.tableViewCell(self, statusView: statusView, mediaGridContainerView: mediaGridContainerView, mediaView: mediaView, didSelectMediaViewAt: index)
}
@@ -97,6 +104,14 @@ extension StatusViewDelegate where Self: StatusViewContainerTableViewCell {
delegate?.tableViewCell(self, statusView: statusView, statusMetricView: statusMetricView, favoriteButtonDidPressed: button)
}
+ func statusView(_ statusView: StatusView, cardControl: StatusCardControl, didTapURL url: URL) {
+ delegate?.tableViewCell(self, statusView: statusView, cardControl: cardControl, didTapURL: url)
+ }
+
+ func statusView(_ statusView: StatusView, cardControlMenu: StatusCardControl) -> UIMenu? {
+ return delegate?.tableViewCell(self, statusView: statusView, cardControlMenu: cardControlMenu)
+ }
+
func statusView(_ statusView: StatusView, accessibilityActivate: Void) {
delegate?.tableViewCell(self, statusView: statusView, accessibilityActivate: accessibilityActivate)
}
diff --git a/Mastodon/Scene/Share/View/TableviewCell/StatusThreadRootTableViewCell.swift b/Mastodon/Scene/Share/View/TableviewCell/StatusThreadRootTableViewCell.swift
index 64f3456b5..73b700e27 100644
--- a/Mastodon/Scene/Share/View/TableviewCell/StatusThreadRootTableViewCell.swift
+++ b/Mastodon/Scene/Share/View/TableviewCell/StatusThreadRootTableViewCell.swift
@@ -81,6 +81,14 @@ extension StatusThreadRootTableViewCell {
// a11y
statusView.contentMetaText.textView.isAccessibilityElement = true
statusView.contentMetaText.textView.isSelectable = true
+
+ statusView.viewModel
+ .$translatedFromLanguage
+ .receive(on: DispatchQueue.main)
+ .sink(receiveValue: { [weak self] _ in
+ self?.invalidateIntrinsicContentSize()
+ })
+ .store(in: &disposeBag)
}
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
@@ -96,7 +104,6 @@ extension StatusThreadRootTableViewCell {
override var accessibilityElements: [Any]? {
get {
var elements = [
- statusView.headerContainerView,
statusView.authorView,
statusView.viewModel.isContentReveal
? statusView.contentMetaText.textView
diff --git a/Mastodon/Scene/Share/Webview/WebViewController.swift b/Mastodon/Scene/Share/Webview/WebViewController.swift
index cb690de93..209ee483d 100644
--- a/Mastodon/Scene/Share/Webview/WebViewController.swift
+++ b/Mastodon/Scene/Share/Webview/WebViewController.swift
@@ -49,12 +49,7 @@ extension WebViewController {
webView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(webView)
- NSLayoutConstraint.activate([
- webView.topAnchor.constraint(equalTo: view.topAnchor),
- webView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
- webView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
- webView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
- ])
+ webView.pinToParent()
let request = URLRequest(url: viewModel.url)
webView.load(request)
diff --git a/Mastodon/Scene/SuggestionAccount/CollectionViewCell/SuggestionAccountCollectionViewCell.swift b/Mastodon/Scene/SuggestionAccount/CollectionViewCell/SuggestionAccountCollectionViewCell.swift
index 2fb467fbd..296935521 100644
--- a/Mastodon/Scene/SuggestionAccount/CollectionViewCell/SuggestionAccountCollectionViewCell.swift
+++ b/Mastodon/Scene/SuggestionAccount/CollectionViewCell/SuggestionAccountCollectionViewCell.swift
@@ -53,11 +53,6 @@ extension SuggestionAccountCollectionViewCell {
private func configure() {
contentView.addSubview(imageView)
imageView.translatesAutoresizingMaskIntoConstraints = false
- NSLayoutConstraint.activate([
- imageView.topAnchor.constraint(equalTo: contentView.topAnchor),
- imageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
- imageView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
- imageView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
- ])
+ imageView.pinToParent()
}
}
diff --git a/Mastodon/Scene/SuggestionAccount/SuggestionAccountViewController.swift b/Mastodon/Scene/SuggestionAccount/SuggestionAccountViewController.swift
index 13c8311c7..7eacdc205 100644
--- a/Mastodon/Scene/SuggestionAccount/SuggestionAccountViewController.swift
+++ b/Mastodon/Scene/SuggestionAccount/SuggestionAccountViewController.swift
@@ -161,7 +161,7 @@ extension SuggestionAccountViewController: UITableViewDelegate {
case .account(let record):
guard let account = record.object(in: context.managedObjectContext) else { return }
let cachedProfileViewModel = CachedProfileViewModel(context: context, authContext: viewModel.authContext, mastodonUser: account)
- coordinator.present(
+ _ = coordinator.present(
scene: .profile(viewModel: cachedProfileViewModel),
from: self,
transition: .show
diff --git a/Mastodon/Scene/SuggestionAccount/SuggestionAccountViewModel+Diffable.swift b/Mastodon/Scene/SuggestionAccount/SuggestionAccountViewModel+Diffable.swift
index 35ba305bc..afb3eb7ec 100644
--- a/Mastodon/Scene/SuggestionAccount/SuggestionAccountViewModel+Diffable.swift
+++ b/Mastodon/Scene/SuggestionAccount/SuggestionAccountViewModel+Diffable.swift
@@ -34,12 +34,7 @@ extension SuggestionAccountViewModel {
let items: [RecommendAccountItem] = records.map { RecommendAccountItem.account($0) }
snapshot.appendItems(items, toSection: .main)
- if #available(iOS 15.0, *) {
- tableViewDiffableDataSource.applySnapshotUsingReloadData(snapshot, completion: nil)
- } else {
- // Fallback on earlier versions
- tableViewDiffableDataSource.applySnapshot(snapshot, animated: false, completion: nil)
- }
+ tableViewDiffableDataSource.applySnapshotUsingReloadData(snapshot, completion: nil)
}
.store(in: &disposeBag)
}
@@ -71,13 +66,7 @@ extension SuggestionAccountViewModel {
}
snapshot.appendItems(items, toSection: .main)
-
- if #available(iOS 15.0, *) {
- collectionViewDiffableDataSource.applySnapshotUsingReloadData(snapshot, completion: nil)
- } else {
- // Fallback on earlier versions
- collectionViewDiffableDataSource.applySnapshot(snapshot, animated: false, completion: nil)
- }
+ collectionViewDiffableDataSource.applySnapshotUsingReloadData(snapshot, completion: nil)
}
.store(in: &disposeBag)
}
diff --git a/Mastodon/Scene/SuggestionAccount/TableViewCell/SuggestionAccountTableViewCell.swift b/Mastodon/Scene/SuggestionAccount/TableViewCell/SuggestionAccountTableViewCell.swift
index 47bd9d6b3..d259ebed4 100644
--- a/Mastodon/Scene/SuggestionAccount/TableViewCell/SuggestionAccountTableViewCell.swift
+++ b/Mastodon/Scene/SuggestionAccount/TableViewCell/SuggestionAccountTableViewCell.swift
@@ -97,12 +97,7 @@ extension SuggestionAccountTableViewCell {
containerStackView.isLayoutMarginsRelativeArrangement = true
containerStackView.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(containerStackView)
- NSLayoutConstraint.activate([
- containerStackView.topAnchor.constraint(equalTo: contentView.topAnchor),
- containerStackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
- containerStackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
- containerStackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
- ])
+ containerStackView.pinToParent()
avatarButton.translatesAutoresizingMaskIntoConstraints = false
containerStackView.addArrangedSubview(avatarButton)
diff --git a/Mastodon/Scene/Thread/ThreadViewController.swift b/Mastodon/Scene/Thread/ThreadViewController.swift
index 5dee182c6..1b386aa6a 100644
--- a/Mastodon/Scene/Thread/ThreadViewController.swift
+++ b/Mastodon/Scene/Thread/ThreadViewController.swift
@@ -87,12 +87,7 @@ extension ThreadViewController {
tableView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(tableView)
- NSLayoutConstraint.activate([
- tableView.topAnchor.constraint(equalTo: view.topAnchor),
- tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
- tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
- tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
- ])
+ tableView.pinToParent()
tableView.delegate = self
viewModel.setupDiffableDataSource(
@@ -122,7 +117,7 @@ extension ThreadViewController {
let composeViewModel = ComposeViewModel(
context: context,
authContext: viewModel.authContext,
- kind: .reply(status: threadContext.status)
+ destination: .reply(parent: threadContext.status)
)
_ = coordinator.present(
scene: .compose(viewModel: composeViewModel),
diff --git a/Mastodon/Scene/Thread/ThreadViewModel+Diffable.swift b/Mastodon/Scene/Thread/ThreadViewModel+Diffable.swift
index 834d478e6..850c6bab2 100644
--- a/Mastodon/Scene/Thread/ThreadViewModel+Diffable.swift
+++ b/Mastodon/Scene/Thread/ThreadViewModel+Diffable.swift
@@ -24,6 +24,7 @@ extension ThreadViewModel {
tableView: tableView,
context: context,
configuration: StatusSection.Configuration(
+ context: context,
authContext: authContext,
statusTableViewCellDelegate: statusTableViewCellDelegate,
timelineMiddleLoaderTableViewCellDelegate: nil,
@@ -153,17 +154,13 @@ extension ThreadViewModel {
snapshot: NSDiffableDataSourceSnapshot,
animatingDifferences: Bool
) async {
- diffableDataSource?.apply(snapshot, animatingDifferences: animatingDifferences)
+ await diffableDataSource?.apply(snapshot, animatingDifferences: animatingDifferences)
}
@MainActor func updateSnapshotUsingReloadData(
snapshot: NSDiffableDataSourceSnapshot
) async {
- if #available(iOS 15.0, *) {
- await self.diffableDataSource?.applySnapshotUsingReloadData(snapshot)
- } else {
- diffableDataSource?.applySnapshot(snapshot, animated: false, completion: nil)
- }
+ await self.diffableDataSource?.applySnapshotUsingReloadData(snapshot)
}
// Some UI tweaks to present replies and conversation smoothly
diff --git a/Mastodon/Scene/Thread/ThreadViewModel+LoadThreadState.swift b/Mastodon/Scene/Thread/ThreadViewModel+LoadThreadState.swift
index 050670be7..1c6c40190 100644
--- a/Mastodon/Scene/Thread/ThreadViewModel+LoadThreadState.swift
+++ b/Mastodon/Scene/Thread/ThreadViewModel+LoadThreadState.swift
@@ -92,19 +92,27 @@ extension ThreadViewModel.LoadThreadState {
from: response.value.ancestors
)
)
+ // deprecated: Tree mode replies
+ // viewModel.mastodonStatusThreadViewModel.appendDescendant(
+ // domain: threadContext.domain,
+ // nodes: MastodonStatusThreadViewModel.Node.children(
+ // of: threadContext.statusID,
+ // from: response.value.descendants
+ // )
+ // )
+
+ // new: the same order from API
viewModel.mastodonStatusThreadViewModel.appendDescendant(
domain: threadContext.domain,
- nodes: MastodonStatusThreadViewModel.Node.children(
- of: threadContext.statusID,
- from: response.value.descendants
- )
+ nodes: response.value.descendants.map { status in
+ return .init(statusID: status.id, children: [])
+ }
)
} catch {
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): fetch status context for \(threadContext.statusID) fail: \(error.localizedDescription)")
await enter(state: Fail.self)
}
-
- }
+ } // end Task
}
}
diff --git a/Mastodon/Scene/Thread/ThreadViewModel.swift b/Mastodon/Scene/Thread/ThreadViewModel.swift
index 735d85cd4..c845ce64c 100644
--- a/Mastodon/Scene/Thread/ThreadViewModel.swift
+++ b/Mastodon/Scene/Thread/ThreadViewModel.swift
@@ -28,12 +28,6 @@ class ThreadViewModel {
let context: AppContext
let authContext: AuthContext
let mastodonStatusThreadViewModel: MastodonStatusThreadViewModel
-
-// let cellFrameCache = NSCache()
-// let existStatusFetchedResultsController: StatusFetchedResultsController
-
-// weak var contentOffsetAdjustableTimelineViewControllerDelegate: ContentOffsetAdjustableTimelineViewControllerDelegate?
-// weak var tableView: UITableView?
// output
var diffableDataSource: UITableViewDiffableDataSource?
@@ -62,12 +56,6 @@ class ThreadViewModel {
self.authContext = authContext
self.root = optionalRoot
self.mastodonStatusThreadViewModel = MastodonStatusThreadViewModel(context: context)
-// self.rootNode = CurrentValueSubject(optionalStatus.flatMap { RootNode(domain: $0.domain, statusID: $0.id, replyToID: $0.inReplyToID) })
-// self.rootItem = CurrentValueSubject(optionalStatus.flatMap { Item.root(statusObjectID: $0.objectID, attribute: Item.StatusAttribute()) })
-// self.existStatusFetchedResultsController = StatusFetchedResultsController(managedObjectContext: context.managedObjectContext, domain: nil, additionalTweetPredicate: nil)
-// self.navigationBarTitle = CurrentValueSubject(
-// optionalStatus.flatMap { L10n.Scene.Thread.title($0.author.displayNameWithFallback) })
-// self.navigationBarTitleEmojiMeta = CurrentValueSubject(optionalStatus.flatMap { $0.author.emojis.asDictionary } ?? [:])
// end init
ManagedObjectObserver.observe(context: context.managedObjectContext)
@@ -85,24 +73,6 @@ class ThreadViewModel {
})
.store(in: &disposeBag)
-// // bind fetcher domain
-// context.authenticationService.activeMastodonAuthenticationBox
-// .receive(on: RunLoop.main)
-// .sink { [weak self] box in
-// guard let self = self else { return }
-// self.existStatusFetchedResultsController.domain.value = box?.domain
-// }
-// .store(in: &disposeBag)
-//
-// rootNode
-// .receive(on: DispatchQueue.main)
-// .sink { [weak self] rootNode in
-// guard let self = self else { return }
-// guard rootNode != nil else { return }
-// self.loadThreadStateMachine.enter(LoadThreadState.Loading.self)
-// }
-// .store(in: &disposeBag)
-
$root
.receive(on: DispatchQueue.main)
.sink { [weak self] root in
@@ -125,102 +95,6 @@ class ThreadViewModel {
}()
}
.store(in: &disposeBag)
-
-// rootItem
-// .receive(on: DispatchQueue.main)
-// .sink { [weak self] rootItem in
-// guard let self = self else { return }
-// guard case let .root(objectID, _) = rootItem else { return }
-// self.context.managedObjectContext.perform {
-// guard let status = self.context.managedObjectContext.object(with: objectID) as? Status else {
-// return
-// }
-// self.rootItemObserver = ManagedObjectObserver.observe(object: status)
-// .receive(on: DispatchQueue.main)
-// .sink(receiveCompletion: { _ in
-// // do nothing
-// }, receiveValue: { [weak self] change in
-// guard let self = self else { return }
-// switch change.changeType {
-// case .delete:
-// self.rootItem.value = nil
-// default:
-// break
-// }
-// })
-// }
-// }
-// .store(in: &disposeBag)
-//
-// ancestorNodes
-// .receive(on: DispatchQueue.main)
-// .compactMap { [weak self] nodes -> [Item]? in
-// guard let self = self else { return nil }
-// guard !nodes.isEmpty else { return [] }
-//
-// guard let diffableDataSource = self.diffableDataSource else { return nil }
-// let oldSnapshot = diffableDataSource.snapshot()
-// var oldSnapshotAttributeDict: [NSManagedObjectID : Item.StatusAttribute] = [:]
-// for item in oldSnapshot.itemIdentifiers {
-// switch item {
-// case .reply(let objectID, let attribute):
-// oldSnapshotAttributeDict[objectID] = attribute
-// default:
-// break
-// }
-// }
-//
-// var items: [Item] = []
-// for node in nodes {
-// let attribute = oldSnapshotAttributeDict[node.statusObjectID] ?? Item.StatusAttribute()
-// items.append(Item.reply(statusObjectID: node.statusObjectID, attribute: attribute))
-// }
-//
-// return items.reversed()
-// }
-// .assign(to: \.value, on: ancestorItems)
-// .store(in: &disposeBag)
-//
-// descendantNodes
-// .receive(on: DispatchQueue.main)
-// .compactMap { [weak self] nodes -> [Item]? in
-// guard let self = self else { return nil }
-// guard !nodes.isEmpty else { return [] }
-//
-// guard let diffableDataSource = self.diffableDataSource else { return nil }
-// let oldSnapshot = diffableDataSource.snapshot()
-// var oldSnapshotAttributeDict: [NSManagedObjectID : Item.StatusAttribute] = [:]
-// for item in oldSnapshot.itemIdentifiers {
-// switch item {
-// case .leaf(let objectID, let attribute):
-// oldSnapshotAttributeDict[objectID] = attribute
-// default:
-// break
-// }
-// }
-//
-// var items: [Item] = []
-//
-// func buildThread(node: LeafNode) {
-// let attribute = oldSnapshotAttributeDict[node.objectID] ?? Item.StatusAttribute()
-// items.append(Item.leaf(statusObjectID: node.objectID, attribute: attribute))
-// // only expand the first child
-// if let firstChild = node.children.first {
-// if !node.isChildrenExpanded {
-// items.append(Item.leafBottomLoader(statusObjectID: node.objectID))
-// } else {
-// buildThread(node: firstChild)
-// }
-// }
-// }
-//
-// for node in nodes {
-// buildThread(node: node)
-// }
-// return items
-// }
-// .assign(to: \.value, on: descendantItems)
-// .store(in: &disposeBag)
}
deinit {
diff --git a/Mastodon/Scene/Transition/MediaPreview/MediaHostToMediaPreviewViewControllerAnimatedTransitioning.swift b/Mastodon/Scene/Transition/MediaPreview/MediaHostToMediaPreviewViewControllerAnimatedTransitioning.swift
index 5381feb0d..ce9cfca96 100644
--- a/Mastodon/Scene/Transition/MediaPreview/MediaHostToMediaPreviewViewControllerAnimatedTransitioning.swift
+++ b/Mastodon/Scene/Transition/MediaPreview/MediaHostToMediaPreviewViewControllerAnimatedTransitioning.swift
@@ -65,7 +65,7 @@ extension MediaHostToMediaPreviewViewControllerAnimatedTransitioning {
let initialFrame = transitionItem.initialFrame ?? toViewEndFrame
let transitionTargetFrame: CGRect = {
let aspectRatio = transitionItem.aspectRatio ?? CGSize(width: initialFrame.width, height: initialFrame.height)
- return AVMakeRect(aspectRatio: aspectRatio, insideRect: toView.bounds)
+ return AVMakeRect(aspectRatio: aspectRatio, insideRect: toView.bounds.inset(by: toView.safeAreaInsets))
}()
let transitionImageView: UIImageView = {
let imageView = UIImageView(frame: transitionContext.containerView.convert(initialFrame, from: nil))
@@ -81,8 +81,8 @@ extension MediaHostToMediaPreviewViewControllerAnimatedTransitioning {
transitionItem.transitionView = transitionImageView
transitionContext.containerView.addSubview(transitionImageView)
- toVC.closeButtonBackground.alpha = 0
-
+ toVC.topToolbar.alpha = 0
+
if UIAccessibility.isReduceTransparencyEnabled {
toVC.visualEffectView.alpha = 0
}
@@ -101,7 +101,7 @@ extension MediaHostToMediaPreviewViewControllerAnimatedTransitioning {
toVC.pagingViewController.view.alpha = 1
transitionImageView.removeFromSuperview()
UIView.animate(withDuration: 0.33, delay: 0, options: [.curveEaseInOut]) {
- toVC.closeButtonBackground.alpha = 1
+ toVC.topToolbar.alpha = 1
}
transitionContext.completeTransition(position == .end)
}
@@ -131,14 +131,20 @@ extension MediaHostToMediaPreviewViewControllerAnimatedTransitioning {
}
return animator
}
+
+ if let toVC = transitionContext.viewController(forKey: .to) {
+ animator.addCompletion { _ in
+ toVC.setNeedsStatusBarAppearanceUpdate()
+ }
+ }
- // update close button
+ // update top toolbar
UIView.animate(withDuration: 0.33, delay: 0, options: [.curveEaseInOut]) {
- fromVC.closeButtonBackground.alpha = 0
+ fromVC.topToolbar.alpha = 0
}
animator.addCompletion { position in
UIView.animate(withDuration: 0.33, delay: 0, options: [.curveEaseInOut]) {
- fromVC.closeButtonBackground.alpha = position == .end ? 0 : 1
+ fromVC.topToolbar.alpha = position == .end ? 0 : 1
}
}
@@ -196,7 +202,7 @@ extension MediaHostToMediaPreviewViewControllerAnimatedTransitioning {
mediaPreviewTransitionContext.snapshot.contentMode = .scaleAspectFill
mediaPreviewTransitionContext.snapshot.clipsToBounds = true
transitionMaskView.addSubview(mediaPreviewTransitionContext.snapshot)
- fromVC.view.bringSubviewToFront(fromVC.closeButtonBackground)
+ fromVC.view.bringSubviewToFront(fromVC.topToolbar)
transitionItem.transitionView = mediaPreviewTransitionContext.transitionView
transitionItem.snapshotTransitioning = mediaPreviewTransitionContext.snapshot
@@ -247,23 +253,6 @@ extension MediaHostToMediaPreviewViewControllerAnimatedTransitioning {
return rect
}()
let maskLayerToPath = maskLayerToRect.flatMap { UIBezierPath(rect: $0) }?.cgPath
- let maskLayerToFinalRect: CGRect? = {
- guard case .attachments = transitionItem.source else { return nil }
- var rect = maskLayerToRect ?? transitionMaskView.frame
- // clip tabBar when bar visible
- guard let tabBarController = toVC.tabBarController,
- !tabBarController.tabBar.isHidden,
- let tabBarSuperView = tabBarController.tabBar.superview
- else { return rect }
- let tabBarFrameInWindow = tabBarSuperView.convert(tabBarController.tabBar.frame, to: nil)
- let offset = rect.maxY - tabBarFrameInWindow.minY
- guard offset > 0 else { return rect }
- rect.size.height -= offset
- return rect
- }()
-
- // FIXME:
- let maskLayerToFinalPath = maskLayerToFinalRect.flatMap { UIBezierPath(rect: $0) }?.cgPath
if let maskLayerToPath = maskLayerToPath {
maskLayer.path = maskLayerToPath
@@ -528,7 +517,7 @@ extension MediaHostToMediaPreviewViewControllerAnimatedTransitioning {
let scaleTransform = CGAffineTransform(scaleX: (itemWidth / size.width), y: (itemHeight / size.height))
let scaledOffset = transitionItem.touchOffset.apply(transform: scaleTransform)
- let center = transitionView.convert(transitionView.center, to: nil)
+ let center = transitionView.convert(CGPoint(x: transitionView.bounds.midX, y: transitionView.bounds.midY), to: nil)
snapshot.center = (center + (translation + (transitionItem.touchOffset - scaledOffset))).point
snapshot.bounds = CGRect(origin: CGPoint.zero, size: CGSize(width: itemWidth, height: itemHeight))
transitionItem.touchOffset = scaledOffset
diff --git a/Mastodon/Supporting Files/AppDelegate.swift b/Mastodon/Supporting Files/AppDelegate.swift
index 3bf38f6da..84b819a5c 100644
--- a/Mastodon/Supporting Files/AppDelegate.swift
+++ b/Mastodon/Supporting Files/AppDelegate.swift
@@ -65,11 +65,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
extension AppDelegate {
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
- #if DEBUG
- return .all
- #else
return UIDevice.current.userInterfaceIdiom == .phone ? .portrait : .all
- #endif
}
}
diff --git a/Mastodon/Supporting Files/SceneDelegate.swift b/Mastodon/Supporting Files/SceneDelegate.swift
index bf02b8031..e2f045a7b 100644
--- a/Mastodon/Supporting Files/SceneDelegate.swift
+++ b/Mastodon/Supporting Files/SceneDelegate.swift
@@ -11,6 +11,7 @@ import Combine
import CoreDataStack
import MastodonCore
import MastodonExtension
+import MastodonUI
#if PROFILE
import FPSIndicator
@@ -35,8 +36,13 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = scene as? UIWindowScene else { return }
+ #if DEBUG
+ let window = TouchesVisibleWindow(windowScene: windowScene)
+ self.window = window
+ #else
let window = UIWindow(windowScene: windowScene)
self.window = window
+ #endif
// set tint color
window.tintColor = UIColor.label
@@ -112,6 +118,9 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
// trigger authenticated user account update
AppContext.shared.authenticationService.updateActiveUserAccountPublisher.send()
+
+ // update mutes and blocks and remove related data
+ AppContext.shared.instanceService.updateMutesAndBlocks()
if let shortcutItem = savedShortCutItem {
Task {
@@ -175,7 +184,7 @@ extension SceneDelegate {
return false
}
- coordinator.switchToTabBar(tab: .notification)
+ coordinator.switchToTabBar(tab: .notifications)
case "org.joinmastodon.app.new-post":
if coordinator?.tabBarController.topMost is ComposeViewController {
@@ -185,7 +194,7 @@ extension SceneDelegate {
let composeViewModel = ComposeViewModel(
context: AppContext.shared,
authContext: authContext,
- kind: .post
+ destination: .topLevel
)
_ = coordinator?.present(scene: .compose(viewModel: composeViewModel), from: nil, transition: .modal(animated: true, completion: nil))
logger.debug("\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): present compose scene")
diff --git a/Mastodon/Template/AutoGenerateProtocolDelegate.swifttemplate b/Mastodon/Template/AutoGenerateProtocolDelegate.swifttemplate
index 47eb4ce19..e8677d970 100644
--- a/Mastodon/Template/AutoGenerateProtocolDelegate.swifttemplate
+++ b/Mastodon/Template/AutoGenerateProtocolDelegate.swifttemplate
@@ -1,3 +1,17 @@
+<%
+func methodDeclaration(_ method: SourceryRuntime.Method, newName: String) -> String {
+ var result = newName
+ if method.throws {
+ result = result + " throws"
+ } else if method.rethrows {
+ result = result + " rethrows"
+ }
+ if method.returnTypeName.isVoid {
+ return result
+ }
+ return result + " -> \(method.returnTypeName)"
+}
+-%>
<% for type in types.implementing["AutoGenerateProtocolDelegate"] {
guard let replaceOf = type.annotations["replaceOf"] as? String else { continue }
guard let replaceWith = type.annotations["replaceWith"] as? String else { continue }
@@ -5,7 +19,7 @@
guard let aProtocol = types.protocols.first(where: { $0.name == protocolToGenerate }) else { continue } -%>
// sourcery:inline:<%= type.name %>.AutoGenerateProtocolDelegate
<% for method in aProtocol.methods { -%>
-<%= method.name.replacingOccurrences(of: replaceOf, with: replaceWith) %>
+<%= methodDeclaration(method, newName: method.name.replacingOccurrences(of: replaceOf, with: replaceWith)) %>
<% } -%>
// sourcery:end
<% } %>
diff --git a/Mastodon/Template/AutoGenerateProtocolRelayDelegate.swifttemplate b/Mastodon/Template/AutoGenerateProtocolRelayDelegate.swifttemplate
index b57f26038..d2b0dd58c 100644
--- a/Mastodon/Template/AutoGenerateProtocolRelayDelegate.swifttemplate
+++ b/Mastodon/Template/AutoGenerateProtocolRelayDelegate.swifttemplate
@@ -6,6 +6,9 @@ func methodDeclaration(_ method: SourceryRuntime.Method) -> String {
} else if method.rethrows {
result = result + " rethrows"
}
+ if method.returnTypeName.isVoid {
+ return result
+ }
return result + " -> \(method.returnTypeName)"
}
-%>
@@ -42,7 +45,7 @@ func methodCall(
guard let aProtocol = types.protocols.first(where: { $0.name == protocolToGenerate }) else { continue } -%>
// sourcery:inline:<%= type.name %>.AutoGenerateProtocolRelayDelegate
<% for method in aProtocol.methods { -%>
-func <%= method.name -%> {
+func <%= methodDeclaration(method) -%> {
<%= methodCall(method, replaceOf: replaceOf, replaceWith: replaceWith) %>
}
diff --git a/MastodonIntent/ca.lproj/Intents.strings b/MastodonIntent/ca.lproj/Intents.strings
index 6b92eb263..c02ac08cf 100644
--- a/MastodonIntent/ca.lproj/Intents.strings
+++ b/MastodonIntent/ca.lproj/Intents.strings
@@ -22,7 +22,7 @@
"ZbSjzC" = "Visibilitat";
-"Zo4jgJ" = "Visibilitat de la Publicació";
+"Zo4jgJ" = "Visibilitat de la publicació";
"apSxMG-dYQ5NN" = "Hi ha ${count} opcions que coincideixen amb ‘Públic’.";
@@ -30,9 +30,9 @@
"ayoYEb-dYQ5NN" = "${content}, Públic";
-"ayoYEb-ehFLjY" = "${content}, Només Seguidors";
+"ayoYEb-ehFLjY" = "${content}, Només seguidors";
-"dUyuGg" = "Publicació";
+"dUyuGg" = "Publica a Mastodon";
"dYQ5NN" = "Públic";
diff --git a/MastodonIntent/cs.lproj/Intents.strings b/MastodonIntent/cs.lproj/Intents.strings
new file mode 100644
index 000000000..6f29830a1
--- /dev/null
+++ b/MastodonIntent/cs.lproj/Intents.strings
@@ -0,0 +1,51 @@
+"16wxgf" = "Příspěvek na Mastodon";
+
+"751xkl" = "Textový obsah";
+
+"CsR7G2" = "Příspěvek na Mastodon";
+
+"HZSGTr" = "Jaký obsah se má přidat?";
+
+"HdGikU" = "Odeslání se nezdařilo";
+
+"KDNTJ4" = "Důvod selhání";
+
+"RHxKOw" = "Odeslat příspěvek s textovým obsahem";
+
+"RxSqsb" = "Příspěvek";
+
+"WCIR3D" = "Zveřejnit ${content} na Mastodon";
+
+"ZKJSNu" = "Příspěvek";
+
+"ZS1XaK" = "${content}";
+
+"ZbSjzC" = "Viditelnost";
+
+"Zo4jgJ" = "Viditelnost příspěvku";
+
+"apSxMG-dYQ5NN" = "Existuje ${count} možností odpovídajících 'Veřejný'.";
+
+"apSxMG-ehFLjY" = "Existuje ${count} možností, které odpovídají „jen sledujícím“.";
+
+"ayoYEb-dYQ5NN" = "${content}, veřejné";
+
+"ayoYEb-ehFLjY" = "${content}, pouze sledující";
+
+"dUyuGg" = "Příspěvek na Mastodon";
+
+"dYQ5NN" = "Veřejný";
+
+"ehFLjY" = "Pouze sledující";
+
+"gfePDu" = "Odeslání se nezdařilo. ${failureReason}";
+
+"k7dbKQ" = "Příspěvek byl úspěšně odeslán.";
+
+"oGiqmY-dYQ5NN" = "Jen pro kontrolu, chtěli jste „Veřejný“?";
+
+"oGiqmY-ehFLjY" = "Jen pro kontrolu, chtěli jste „Pouze sledující“?";
+
+"rM6dvp" = "URL";
+
+"ryJLwG" = "Příspěvek byl úspěšně odeslán. ";
diff --git a/MastodonIntent/cs.lproj/Intents.stringsdict b/MastodonIntent/cs.lproj/Intents.stringsdict
new file mode 100644
index 000000000..5a39d5e64
--- /dev/null
+++ b/MastodonIntent/cs.lproj/Intents.stringsdict
@@ -0,0 +1,54 @@
+
+
+
+
+ There are ${count} options matching ‘${content}’. - 2
+
+ NSStringLocalizedFormatKey
+ There are %#@count_option@ matching ‘${content}’.
+ count_option
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ %ld
+ zero
+ 0 options
+ one
+ 1 option
+ two
+ 2 options
+ few
+ %ld options
+ many
+ %ld options
+ other
+ %ld options
+
+
+ There are ${count} options matching ‘${visibility}’.
+
+ NSStringLocalizedFormatKey
+ There are %#@count_option@ matching ‘${visibility}’.
+ count_option
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ %ld
+ zero
+ 0 options
+ one
+ 1 option
+ two
+ 2 options
+ few
+ %ld options
+ many
+ %ld options
+ other
+ %ld options
+
+
+
+
diff --git a/MastodonIntent/de.lproj/Intents.strings b/MastodonIntent/de.lproj/Intents.strings
index fd3fbd40f..5d0056766 100644
--- a/MastodonIntent/de.lproj/Intents.strings
+++ b/MastodonIntent/de.lproj/Intents.strings
@@ -1,12 +1,12 @@
-"16wxgf" = "Auf Mastodon posten";
+"16wxgf" = "Auf Mastodon veröffentlichen";
"751xkl" = "Textinhalt";
-"CsR7G2" = "Auf Mastodon posten";
+"CsR7G2" = "Auf Mastodon veröffentlichen";
-"HZSGTr" = "Welcher Inhalt soll gepostet werden?";
+"HZSGTr" = "Welcher Inhalt soll veröffentlicht werden?";
-"HdGikU" = "Posten fehlgeschlagen";
+"HdGikU" = "Veröffentlichen fehlgeschlagen";
"KDNTJ4" = "Fehlerursache";
diff --git a/MastodonIntent/sl.lproj/Intents.strings b/MastodonIntent/sl.lproj/Intents.strings
new file mode 100644
index 000000000..72de87df2
--- /dev/null
+++ b/MastodonIntent/sl.lproj/Intents.strings
@@ -0,0 +1,51 @@
+"16wxgf" = "Objavi na Mastodonu";
+
+"751xkl" = "besedilo";
+
+"CsR7G2" = "Objavi na Mastodonu";
+
+"HZSGTr" = "Kakšno vsebino želite objaviti?";
+
+"HdGikU" = "Objava ni uspela";
+
+"KDNTJ4" = "Vzrok za neuspeh";
+
+"RHxKOw" = "Pošlji objavo z besedilom";
+
+"RxSqsb" = "Objavi";
+
+"WCIR3D" = "Objavite ${content} na Mastodonu";
+
+"ZKJSNu" = "Objavi";
+
+"ZS1XaK" = "${content}";
+
+"ZbSjzC" = "Vidnost";
+
+"Zo4jgJ" = "Vidnost objave";
+
+"apSxMG-dYQ5NN" = "Z \"Javno\" se ujema ${count} možnosti.";
+
+"apSxMG-ehFLjY" = "S \"Samo sledilci\" se ujema ${count} možnosti.";
+
+"ayoYEb-dYQ5NN" = "${content}, javno";
+
+"ayoYEb-ehFLjY" = "${content}, samo sledilci";
+
+"dUyuGg" = "Objavi na Mastodonu";
+
+"dYQ5NN" = "Javno";
+
+"ehFLjY" = "Samo sledilci";
+
+"gfePDu" = "Objava je spodletela. ${failureReason}";
+
+"k7dbKQ" = "Uspešno poslana objava.";
+
+"oGiqmY-dYQ5NN" = "Da ne bo nesporazuma - želeli ste \"Javno\"?";
+
+"oGiqmY-ehFLjY" = "Da ne bo nesporazuma - želeli ste \"Samo sledilci\"?";
+
+"rM6dvp" = "URL";
+
+"ryJLwG" = "Uspešno poslana objava. ";
diff --git a/MastodonIntent/sl.lproj/Intents.stringsdict b/MastodonIntent/sl.lproj/Intents.stringsdict
new file mode 100644
index 000000000..5a39d5e64
--- /dev/null
+++ b/MastodonIntent/sl.lproj/Intents.stringsdict
@@ -0,0 +1,54 @@
+
+
+
+
+ There are ${count} options matching ‘${content}’. - 2
+
+ NSStringLocalizedFormatKey
+ There are %#@count_option@ matching ‘${content}’.
+ count_option
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ %ld
+ zero
+ 0 options
+ one
+ 1 option
+ two
+ 2 options
+ few
+ %ld options
+ many
+ %ld options
+ other
+ %ld options
+
+
+ There are ${count} options matching ‘${visibility}’.
+
+ NSStringLocalizedFormatKey
+ There are %#@count_option@ matching ‘${visibility}’.
+ count_option
+
+ NSStringFormatSpecTypeKey
+ NSStringPluralRuleType
+ NSStringFormatValueTypeKey
+ %ld
+ zero
+ 0 options
+ one
+ 1 option
+ two
+ 2 options
+ few
+ %ld options
+ many
+ %ld options
+ other
+ %ld options
+
+
+
+
diff --git a/MastodonSDK/Package.resolved b/MastodonSDK/Package.resolved
index 06843faa3..4a4fa51de 100644
--- a/MastodonSDK/Package.resolved
+++ b/MastodonSDK/Package.resolved
@@ -1,241 +1,257 @@
{
- "object": {
- "pins": [
- {
- "package": "Alamofire",
- "repositoryURL": "https://github.com/Alamofire/Alamofire.git",
- "state": {
- "branch": null,
- "revision": "8dd85aee02e39dd280c75eef88ffdb86eed4b07b",
- "version": "5.6.2"
- }
- },
- {
- "package": "AlamofireImage",
- "repositoryURL": "https://github.com/Alamofire/AlamofireImage.git",
- "state": {
- "branch": null,
- "revision": "98cbb00ce0ec5fc8e52a5b50a6bfc08d3e5aee10",
- "version": "4.2.0"
- }
- },
- {
- "package": "CommonOSLog",
- "repositoryURL": "https://github.com/MainasuK/CommonOSLog",
- "state": {
- "branch": null,
- "revision": "c121624a30698e9886efe38aebb36ff51c01b6c2",
- "version": "0.1.1"
- }
- },
- {
- "package": "FaviconFinder",
- "repositoryURL": "https://github.com/will-lumley/FaviconFinder.git",
- "state": {
- "branch": null,
- "revision": "1f74844f77f79b95c0bb0130b3a87d4f340e6d3a",
- "version": "3.3.0"
- }
- },
- {
- "package": "FLAnimatedImage",
- "repositoryURL": "https://github.com/Flipboard/FLAnimatedImage.git",
- "state": {
- "branch": null,
- "revision": "d4f07b6f164d53c1212c3e54d6460738b1981e9f",
- "version": "1.0.17"
- }
- },
- {
- "package": "FPSIndicator",
- "repositoryURL": "https://github.com/MainasuK/FPSIndicator.git",
- "state": {
- "branch": null,
- "revision": "e4a5067ccd5293b024c767f09e51056afd4a4796",
- "version": "1.1.0"
- }
- },
- {
- "package": "Fuzi",
- "repositoryURL": "https://github.com/cezheng/Fuzi.git",
- "state": {
- "branch": null,
- "revision": "f08c8323da21e985f3772610753bcfc652c2103f",
- "version": "3.1.3"
- }
- },
- {
- "package": "KeychainAccess",
- "repositoryURL": "https://github.com/kishikawakatsumi/KeychainAccess.git",
- "state": {
- "branch": null,
- "revision": "84e546727d66f1adc5439debad16270d0fdd04e7",
- "version": "4.2.2"
- }
- },
- {
- "package": "MetaTextKit",
- "repositoryURL": "https://github.com/TwidereProject/MetaTextKit.git",
- "state": {
- "branch": null,
- "revision": "dcd5255d6930c2fab408dc8562c577547e477624",
- "version": "2.2.5"
- }
- },
- {
- "package": "Nuke",
- "repositoryURL": "https://github.com/kean/Nuke.git",
- "state": {
- "branch": null,
- "revision": "a002b7fd786f2df2ed4333fe73a9727499fd9d97",
- "version": "10.11.2"
- }
- },
- {
- "package": "NukeFLAnimatedImagePlugin",
- "repositoryURL": "https://github.com/kean/Nuke-FLAnimatedImage-Plugin.git",
- "state": {
- "branch": null,
- "revision": "b59c346a7d536336db3b0f12c72c6e53ee709e16",
- "version": "8.0.0"
- }
- },
- {
- "package": "Pageboy",
- "repositoryURL": "https://github.com/uias/Pageboy",
- "state": {
- "branch": null,
- "revision": "af8fa81788b893205e1ff42ddd88c5b0b315d7c5",
- "version": "3.7.0"
- }
- },
- {
- "package": "PanModal",
- "repositoryURL": "https://github.com/slackhq/PanModal.git",
- "state": {
- "branch": null,
- "revision": "b012aecb6b67a8e46369227f893c12544846613f",
- "version": "1.2.7"
- }
- },
- {
- "package": "SDWebImage",
- "repositoryURL": "https://github.com/SDWebImage/SDWebImage.git",
- "state": {
- "branch": null,
- "revision": "9248fe561a2a153916fb9597e3af4434784c6d32",
- "version": "5.13.4"
- }
- },
- {
- "package": "swift-collections",
- "repositoryURL": "https://github.com/apple/swift-collections.git",
- "state": {
- "branch": null,
- "revision": "f504716c27d2e5d4144fa4794b12129301d17729",
- "version": "1.0.3"
- }
- },
- {
- "package": "swift-nio",
- "repositoryURL": "https://github.com/apple/swift-nio.git",
- "state": {
- "branch": null,
- "revision": "546610d52b19be3e19935e0880bb06b9c03f5cef",
- "version": "1.14.4"
- }
- },
- {
- "package": "swift-nio-zlib-support",
- "repositoryURL": "https://github.com/apple/swift-nio-zlib-support.git",
- "state": {
- "branch": null,
- "revision": "37760e9a52030bb9011972c5213c3350fa9d41fd",
- "version": "1.0.0"
- }
- },
- {
- "package": "SwiftSoup",
- "repositoryURL": "https://github.com/scinfu/SwiftSoup.git",
- "state": {
- "branch": null,
- "revision": "6778575285177365cbad3e5b8a72f2a20583cfec",
- "version": "2.4.3"
- }
- },
- {
- "package": "Introspect",
- "repositoryURL": "https://github.com/siteline/SwiftUI-Introspect.git",
- "state": {
- "branch": null,
- "revision": "f2616860a41f9d9932da412a8978fec79c06fe24",
- "version": "0.1.4"
- }
- },
- {
- "package": "SwiftyJSON",
- "repositoryURL": "https://github.com/SwiftyJSON/SwiftyJSON.git",
- "state": {
- "branch": null,
- "revision": "b3dcd7dbd0d488e1a7077cb33b00f2083e382f07",
- "version": "5.0.1"
- }
- },
- {
- "package": "TabBarPager",
- "repositoryURL": "https://github.com/TwidereProject/TabBarPager.git",
- "state": {
- "branch": null,
- "revision": "488aa66d157a648901b61721212c0dec23d27ee5",
- "version": "0.1.0"
- }
- },
- {
- "package": "Tabman",
- "repositoryURL": "https://github.com/uias/Tabman",
- "state": {
- "branch": null,
- "revision": "4a4f7c755b875ffd4f9ef10d67a67883669d2465",
- "version": "2.13.0"
- }
- },
- {
- "package": "ThirdPartyMailer",
- "repositoryURL": "https://github.com/vtourraine/ThirdPartyMailer.git",
- "state": {
- "branch": null,
- "revision": "44c1cfaa6969963f22691aa67f88a69e3b6d651f",
- "version": "2.1.0"
- }
- },
- {
- "package": "TOCropViewController",
- "repositoryURL": "https://github.com/TimOliver/TOCropViewController.git",
- "state": {
- "branch": null,
- "revision": "d0470491f56e734731bbf77991944c0dfdee3e0e",
- "version": "2.6.1"
- }
- },
- {
- "package": "UIHostingConfigurationBackport",
- "repositoryURL": "https://github.com/woxtu/UIHostingConfigurationBackport.git",
- "state": {
- "branch": null,
- "revision": "6091f2d38faa4b24fc2ca0389c651e2f666624a3",
- "version": "0.1.0"
- }
- },
- {
- "package": "UITextView+Placeholder",
- "repositoryURL": "https://github.com/MainasuK/UITextView-Placeholder.git",
- "state": {
- "branch": null,
- "revision": "20f513ded04a040cdf5467f0891849b1763ede3b",
- "version": "1.4.1"
- }
+ "pins" : [
+ {
+ "identity" : "alamofire",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/Alamofire/Alamofire.git",
+ "state" : {
+ "revision" : "8dd85aee02e39dd280c75eef88ffdb86eed4b07b",
+ "version" : "5.6.2"
}
- ]
- },
- "version": 1
+ },
+ {
+ "identity" : "alamofireimage",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/Alamofire/AlamofireImage.git",
+ "state" : {
+ "revision" : "98cbb00ce0ec5fc8e52a5b50a6bfc08d3e5aee10",
+ "version" : "4.2.0"
+ }
+ },
+ {
+ "identity" : "commonoslog",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/MainasuK/CommonOSLog",
+ "state" : {
+ "revision" : "c121624a30698e9886efe38aebb36ff51c01b6c2",
+ "version" : "0.1.1"
+ }
+ },
+ {
+ "identity" : "faviconfinder",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/will-lumley/FaviconFinder.git",
+ "state" : {
+ "revision" : "1f74844f77f79b95c0bb0130b3a87d4f340e6d3a",
+ "version" : "3.3.0"
+ }
+ },
+ {
+ "identity" : "flanimatedimage",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/Flipboard/FLAnimatedImage.git",
+ "state" : {
+ "revision" : "d4f07b6f164d53c1212c3e54d6460738b1981e9f",
+ "version" : "1.0.17"
+ }
+ },
+ {
+ "identity" : "fpsindicator",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/MainasuK/FPSIndicator.git",
+ "state" : {
+ "revision" : "e4a5067ccd5293b024c767f09e51056afd4a4796",
+ "version" : "1.1.0"
+ }
+ },
+ {
+ "identity" : "fuzi",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/cezheng/Fuzi.git",
+ "state" : {
+ "revision" : "f08c8323da21e985f3772610753bcfc652c2103f",
+ "version" : "3.1.3"
+ }
+ },
+ {
+ "identity" : "keychainaccess",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/kishikawakatsumi/KeychainAccess.git",
+ "state" : {
+ "revision" : "84e546727d66f1adc5439debad16270d0fdd04e7",
+ "version" : "4.2.2"
+ }
+ },
+ {
+ "identity" : "kingfisher",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/onevcat/Kingfisher.git",
+ "state" : {
+ "revision" : "44e891bdb61426a95e31492a67c7c0dfad1f87c5",
+ "version" : "7.4.1"
+ }
+ },
+ {
+ "identity" : "metatextkit",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/TwidereProject/MetaTextKit.git",
+ "state" : {
+ "revision" : "dcd5255d6930c2fab408dc8562c577547e477624",
+ "version" : "2.2.5"
+ }
+ },
+ {
+ "identity" : "nextlevelsessionexporter",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/NextLevel/NextLevelSessionExporter.git",
+ "state" : {
+ "revision" : "b6c0cce1aa37fe1547d694f958fac3c3524b74da",
+ "version" : "0.4.6"
+ }
+ },
+ {
+ "identity" : "nuke",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/kean/Nuke.git",
+ "state" : {
+ "revision" : "a002b7fd786f2df2ed4333fe73a9727499fd9d97",
+ "version" : "10.11.2"
+ }
+ },
+ {
+ "identity" : "nuke-flanimatedimage-plugin",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/kean/Nuke-FLAnimatedImage-Plugin.git",
+ "state" : {
+ "revision" : "b59c346a7d536336db3b0f12c72c6e53ee709e16",
+ "version" : "8.0.0"
+ }
+ },
+ {
+ "identity" : "pageboy",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/uias/Pageboy",
+ "state" : {
+ "revision" : "af8fa81788b893205e1ff42ddd88c5b0b315d7c5",
+ "version" : "3.7.0"
+ }
+ },
+ {
+ "identity" : "panmodal",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/slackhq/PanModal.git",
+ "state" : {
+ "revision" : "b012aecb6b67a8e46369227f893c12544846613f",
+ "version" : "1.2.7"
+ }
+ },
+ {
+ "identity" : "sdwebimage",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/SDWebImage/SDWebImage.git",
+ "state" : {
+ "revision" : "9248fe561a2a153916fb9597e3af4434784c6d32",
+ "version" : "5.13.4"
+ }
+ },
+ {
+ "identity" : "stripes",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/eneko/Stripes.git",
+ "state" : {
+ "revision" : "d533fd44b8043a3abbf523e733599173d6f98c11",
+ "version" : "0.2.0"
+ }
+ },
+ {
+ "identity" : "swift-collections",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/apple/swift-collections.git",
+ "state" : {
+ "revision" : "f504716c27d2e5d4144fa4794b12129301d17729",
+ "version" : "1.0.3"
+ }
+ },
+ {
+ "identity" : "swift-nio",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/apple/swift-nio.git",
+ "state" : {
+ "revision" : "546610d52b19be3e19935e0880bb06b9c03f5cef",
+ "version" : "1.14.4"
+ }
+ },
+ {
+ "identity" : "swift-nio-zlib-support",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/apple/swift-nio-zlib-support.git",
+ "state" : {
+ "revision" : "37760e9a52030bb9011972c5213c3350fa9d41fd",
+ "version" : "1.0.0"
+ }
+ },
+ {
+ "identity" : "swiftsoup",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/scinfu/SwiftSoup.git",
+ "state" : {
+ "revision" : "6778575285177365cbad3e5b8a72f2a20583cfec",
+ "version" : "2.4.3"
+ }
+ },
+ {
+ "identity" : "swiftui-introspect",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/siteline/SwiftUI-Introspect.git",
+ "state" : {
+ "revision" : "f2616860a41f9d9932da412a8978fec79c06fe24",
+ "version" : "0.1.4"
+ }
+ },
+ {
+ "identity" : "tabbarpager",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/TwidereProject/TabBarPager.git",
+ "state" : {
+ "revision" : "488aa66d157a648901b61721212c0dec23d27ee5",
+ "version" : "0.1.0"
+ }
+ },
+ {
+ "identity" : "tabman",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/uias/Tabman",
+ "state" : {
+ "revision" : "4a4f7c755b875ffd4f9ef10d67a67883669d2465",
+ "version" : "2.13.0"
+ }
+ },
+ {
+ "identity" : "thirdpartymailer",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/vtourraine/ThirdPartyMailer.git",
+ "state" : {
+ "revision" : "44c1cfaa6969963f22691aa67f88a69e3b6d651f",
+ "version" : "2.1.0"
+ }
+ },
+ {
+ "identity" : "tocropviewcontroller",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/TimOliver/TOCropViewController.git",
+ "state" : {
+ "revision" : "d0470491f56e734731bbf77991944c0dfdee3e0e",
+ "version" : "2.6.1"
+ }
+ },
+ {
+ "identity" : "uihostingconfigurationbackport",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/woxtu/UIHostingConfigurationBackport.git",
+ "state" : {
+ "revision" : "6091f2d38faa4b24fc2ca0389c651e2f666624a3",
+ "version" : "0.1.0"
+ }
+ },
+ {
+ "identity" : "uitextview-placeholder",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/MainasuK/UITextView-Placeholder.git",
+ "state" : {
+ "revision" : "20f513ded04a040cdf5467f0891849b1763ede3b",
+ "version" : "1.4.1"
+ }
+ }
+ ],
+ "version" : 2
}
diff --git a/MastodonSDK/Package.swift b/MastodonSDK/Package.swift
index 7a4201894..32dbfb2c8 100644
--- a/MastodonSDK/Package.swift
+++ b/MastodonSDK/Package.swift
@@ -3,25 +3,35 @@
import PackageDescription
+let publicLibraryTargets = [
+ "CoreDataStack",
+ "MastodonAsset",
+ "MastodonCommon",
+ "MastodonCore",
+ "MastodonExtension",
+ "MastodonLocalization",
+ "MastodonSDK",
+ "MastodonUI",
+]
+
let package = Package(
name: "MastodonSDK",
defaultLocalization: "en",
platforms: [
- .iOS(.v14),
+ .iOS(.v15),
],
products: [
+ // Static Library
.library(
name: "MastodonSDK",
- targets: [
- "CoreDataStack",
- "MastodonAsset",
- "MastodonCommon",
- "MastodonCore",
- "MastodonExtension",
- "MastodonLocalization",
- "MastodonSDK",
- "MastodonUI",
- ])
+ targets: publicLibraryTargets
+ ),
+ // Dynamic Library
+ .library(
+ name: "MastodonSDKDynamic",
+ type: .dynamic,
+ targets: publicLibraryTargets
+ )
],
dependencies: [
.package(name: "ArkanaKeys", path: "../dependencies/ArkanaKeys"),
@@ -32,7 +42,6 @@ let package = Package(
.package(url: "https://github.com/Alamofire/AlamofireImage.git", from: "4.1.0"),
.package(url: "https://github.com/apple/swift-collections.git", from: "1.0.3"),
.package(url: "https://github.com/apple/swift-nio.git", from: "1.0.0"),
- .package(url: "https://github.com/cezheng/Fuzi.git", from: "3.1.3"),
.package(url: "https://github.com/Flipboard/FLAnimatedImage.git", from: "1.0.0"),
.package(url: "https://github.com/kean/Nuke-FLAnimatedImage-Plugin.git", from: "8.0.0"),
.package(url: "https://github.com/kean/Nuke.git", from: "10.3.1"),
diff --git a/MastodonSDK/Sources/CoreDataStack/CoreData.xcdatamodeld/.xccurrentversion b/MastodonSDK/Sources/CoreDataStack/CoreData.xcdatamodeld/.xccurrentversion
index 1d5ea989f..b2d4c7f6e 100644
--- a/MastodonSDK/Sources/CoreDataStack/CoreData.xcdatamodeld/.xccurrentversion
+++ b/MastodonSDK/Sources/CoreDataStack/CoreData.xcdatamodeld/.xccurrentversion
@@ -3,6 +3,6 @@
_XCCurrentVersionName
- CoreData 4.xcdatamodel
+ CoreData 7.xcdatamodel
diff --git a/MastodonSDK/Sources/CoreDataStack/CoreData.xcdatamodeld/CoreData 5.xcdatamodel/contents b/MastodonSDK/Sources/CoreDataStack/CoreData.xcdatamodeld/CoreData 5.xcdatamodel/contents
new file mode 100644
index 000000000..9f07cca78
--- /dev/null
+++ b/MastodonSDK/Sources/CoreDataStack/CoreData.xcdatamodeld/CoreData 5.xcdatamodel/contents
@@ -0,0 +1,258 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MastodonSDK/Sources/CoreDataStack/CoreData.xcdatamodeld/CoreData 6.xcdatamodel/contents b/MastodonSDK/Sources/CoreDataStack/CoreData.xcdatamodeld/CoreData 6.xcdatamodel/contents
new file mode 100644
index 000000000..30e89ccb4
--- /dev/null
+++ b/MastodonSDK/Sources/CoreDataStack/CoreData.xcdatamodeld/CoreData 6.xcdatamodel/contents
@@ -0,0 +1,260 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MastodonSDK/Sources/CoreDataStack/CoreData.xcdatamodeld/CoreData 7.xcdatamodel/contents b/MastodonSDK/Sources/CoreDataStack/CoreData.xcdatamodeld/CoreData 7.xcdatamodel/contents
new file mode 100644
index 000000000..7198c75c6
--- /dev/null
+++ b/MastodonSDK/Sources/CoreDataStack/CoreData.xcdatamodeld/CoreData 7.xcdatamodel/contents
@@ -0,0 +1,278 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MastodonSDK/Sources/CoreDataStack/Entity/Mastodon/Card.swift b/MastodonSDK/Sources/CoreDataStack/Entity/Mastodon/Card.swift
new file mode 100644
index 000000000..64f5e2120
--- /dev/null
+++ b/MastodonSDK/Sources/CoreDataStack/Entity/Mastodon/Card.swift
@@ -0,0 +1,180 @@
+//
+// Card.swift
+// CoreDataStack
+//
+// Created by Kyle Bashour on 11/23/22.
+//
+
+import Foundation
+import CoreData
+
+public final class Card: NSManagedObject {
+ // sourcery: autoGenerateProperty
+ @NSManaged public private(set) var urlRaw: String
+ public var url: URL? {
+ URL(string: urlRaw)
+ }
+
+ // sourcery: autoGenerateProperty
+ @NSManaged public private(set) var title: String
+ // sourcery: autoGenerateProperty
+ @NSManaged public private(set) var desc: String
+
+ @NSManaged public private(set) var typeRaw: String
+ // sourcery: autoGenerateProperty
+ public var type: MastodonCardType {
+ get { MastodonCardType(rawValue: typeRaw) }
+ set { typeRaw = newValue.rawValue }
+ }
+
+ // sourcery: autoGenerateProperty
+ @NSManaged public private(set) var authorName: String?
+ // sourcery: autoGenerateProperty
+ @NSManaged public private(set) var authorURLRaw: String?
+ // sourcery: autoGenerateProperty
+ @NSManaged public private(set) var providerName: String?
+ // sourcery: autoGenerateProperty
+ @NSManaged public private(set) var providerURLRaw: String?
+ // sourcery: autoGenerateProperty
+ @NSManaged public private(set) var width: Int64
+ // sourcery: autoGenerateProperty
+ @NSManaged public private(set) var height: Int64
+ // sourcery: autoGenerateProperty
+ @NSManaged public private(set) var image: String?
+ public var imageURL: URL? {
+ image.flatMap(URL.init)
+ }
+
+ // sourcery: autoGenerateProperty
+ @NSManaged public private(set) var embedURLRaw: String?
+ // sourcery: autoGenerateProperty
+ @NSManaged public private(set) var blurhash: String?
+ // sourcery: autoGenerateProperty
+ @NSManaged public private(set) var html: String?
+
+ // sourcery: autoGenerateRelationship
+ @NSManaged public private(set) var status: Status
+}
+
+extension Card {
+
+ @discardableResult
+ public static func insert(
+ into context: NSManagedObjectContext,
+ property: Property
+ ) -> Card {
+ let object: Card = context.insertObject()
+
+ object.configure(property: property)
+
+ return object
+ }
+
+}
+
+extension Card: Managed {
+ public static var defaultSortDescriptors: [NSSortDescriptor] {
+ return []
+ }
+}
+
+// MARK: - AutoGenerateProperty
+extension Card: AutoGenerateProperty {
+ // sourcery:inline:Card.AutoGenerateProperty
+
+ // Generated using Sourcery
+ // DO NOT EDIT
+ public struct Property {
+ public let urlRaw: String
+ public let title: String
+ public let desc: String
+ public let type: MastodonCardType
+ public let authorName: String?
+ public let authorURLRaw: String?
+ public let providerName: String?
+ public let providerURLRaw: String?
+ public let width: Int64
+ public let height: Int64
+ public let image: String?
+ public let embedURLRaw: String?
+ public let blurhash: String?
+ public let html: String?
+
+ public init(
+ urlRaw: String,
+ title: String,
+ desc: String,
+ type: MastodonCardType,
+ authorName: String?,
+ authorURLRaw: String?,
+ providerName: String?,
+ providerURLRaw: String?,
+ width: Int64,
+ height: Int64,
+ image: String?,
+ embedURLRaw: String?,
+ blurhash: String?,
+ html: String?
+ ) {
+ self.urlRaw = urlRaw
+ self.title = title
+ self.desc = desc
+ self.type = type
+ self.authorName = authorName
+ self.authorURLRaw = authorURLRaw
+ self.providerName = providerName
+ self.providerURLRaw = providerURLRaw
+ self.width = width
+ self.height = height
+ self.image = image
+ self.embedURLRaw = embedURLRaw
+ self.blurhash = blurhash
+ self.html = html
+ }
+ }
+
+ public func configure(property: Property) {
+ self.urlRaw = property.urlRaw
+ self.title = property.title
+ self.desc = property.desc
+ self.type = property.type
+ self.authorName = property.authorName
+ self.authorURLRaw = property.authorURLRaw
+ self.providerName = property.providerName
+ self.providerURLRaw = property.providerURLRaw
+ self.width = property.width
+ self.height = property.height
+ self.image = property.image
+ self.embedURLRaw = property.embedURLRaw
+ self.blurhash = property.blurhash
+ self.html = property.html
+ }
+
+ public func update(property: Property) {
+ }
+
+ // sourcery:end
+}
+
+// MARK: - AutoGenerateRelationship
+extension Card: AutoGenerateRelationship {
+ // sourcery:inline:Card.AutoGenerateRelationship
+
+ // Generated using Sourcery
+ // DO NOT EDIT
+ public struct Relationship {
+ public let status: Status
+
+ public init(
+ status: Status
+ ) {
+ self.status = status
+ }
+ }
+
+ public func configure(relationship: Relationship) {
+ self.status = relationship.status
+ }
+
+ // sourcery:end
+}
diff --git a/MastodonSDK/Sources/CoreDataStack/Entity/Mastodon/Instance.swift b/MastodonSDK/Sources/CoreDataStack/Entity/Mastodon/Instance.swift
index 8976097ef..c11a92b76 100644
--- a/MastodonSDK/Sources/CoreDataStack/Entity/Mastodon/Instance.swift
+++ b/MastodonSDK/Sources/CoreDataStack/Entity/Mastodon/Instance.swift
@@ -10,12 +10,14 @@ import CoreData
public final class Instance: NSManagedObject {
@NSManaged public var domain: String
-
+ @NSManaged public var version: String?
+
@NSManaged public private(set) var createdAt: Date
@NSManaged public private(set) var updatedAt: Date
@NSManaged public private(set) var configurationRaw: Data?
-
+ @NSManaged public private(set) var configurationV2Raw: Data?
+
// MARK: one-to-many relationships
@NSManaged public var authentications: Set
}
@@ -35,6 +37,7 @@ extension Instance {
) -> Instance {
let instance: Instance = context.insertObject()
instance.domain = property.domain
+ instance.version = property.version
return instance
}
@@ -42,6 +45,10 @@ extension Instance {
self.configurationRaw = configurationRaw
}
+ public func update(configurationV2Raw: Data?) {
+ self.configurationV2Raw = configurationV2Raw
+ }
+
public func didUpdate(at networkDate: Date) {
self.updatedAt = networkDate
}
@@ -50,9 +57,11 @@ extension Instance {
extension Instance {
public struct Property {
public let domain: String
-
- public init(domain: String) {
+ public let version: String?
+
+ public init(domain: String, version: String?) {
self.domain = domain
+ self.version = version
}
}
}
diff --git a/MastodonSDK/Sources/CoreDataStack/Entity/Mastodon/MastodonUser.swift b/MastodonSDK/Sources/CoreDataStack/Entity/Mastodon/MastodonUser.swift
index 760985d68..fe668ccb9 100644
--- a/MastodonSDK/Sources/CoreDataStack/Entity/Mastodon/MastodonUser.swift
+++ b/MastodonSDK/Sources/CoreDataStack/Entity/Mastodon/MastodonUser.swift
@@ -77,6 +77,7 @@ final public class MastodonUser: NSManagedObject {
@NSManaged public private(set) var votePollOptions: Set
@NSManaged public private(set) var votePolls: Set
// relationships
+ @NSManaged public private(set) var followedTags: Set
@NSManaged public private(set) var following: Set
@NSManaged public private(set) var followingBy: Set
@NSManaged public private(set) var followRequested: Set
diff --git a/MastodonSDK/Sources/CoreDataStack/Entity/Mastodon/Status.swift b/MastodonSDK/Sources/CoreDataStack/Entity/Mastodon/Status.swift
index 0c7291913..08f58ca3f 100644
--- a/MastodonSDK/Sources/CoreDataStack/Entity/Mastodon/Status.swift
+++ b/MastodonSDK/Sources/CoreDataStack/Entity/Mastodon/Status.swift
@@ -11,6 +11,16 @@ import Foundation
public final class Status: NSManagedObject {
public typealias ID = String
+ public class TranslatedContent: NSObject {
+ public let content: String
+ public let provider: String?
+
+ public init(content: String, provider: String?) {
+ self.content = content
+ self.provider = provider
+ }
+ }
+
// sourcery: autoGenerateProperty
@NSManaged public private(set) var identifier: ID
// sourcery: autoGenerateProperty
@@ -84,7 +94,9 @@ public final class Status: NSManagedObject {
@NSManaged public private(set) var pinnedBy: MastodonUser?
// sourcery: autoGenerateRelationship
@NSManaged public private(set) var poll: Poll?
-
+ // sourcery: autoGenerateRelationship
+ @NSManaged public private(set) var card: Card?
+
// one-to-many relationship
@NSManaged public private(set) var feeds: Set
@@ -99,6 +111,9 @@ public final class Status: NSManagedObject {
@NSManaged public private(set) var deletedAt: Date?
// sourcery: autoUpdatableObject
@NSManaged public private(set) var revealedAt: Date?
+
+ // sourcery: autoUpdatableObject
+ @NSManaged public private(set) var translatedContent: TranslatedContent?
}
extension Status {
@@ -379,15 +394,18 @@ extension Status: AutoGenerateRelationship {
public let author: MastodonUser
public let reblog: Status?
public let poll: Poll?
+ public let card: Card?
public init(
author: MastodonUser,
reblog: Status?,
- poll: Poll?
+ poll: Poll?,
+ card: Card?
) {
self.author = author
self.reblog = reblog
self.poll = poll
+ self.card = card
}
}
@@ -395,6 +413,7 @@ extension Status: AutoGenerateRelationship {
self.author = relationship.author
self.reblog = relationship.reblog
self.poll = relationship.poll
+ self.card = relationship.card
}
// sourcery:end
}
@@ -495,6 +514,11 @@ extension Status: AutoUpdatableObject {
self.revealedAt = revealedAt
}
}
+ public func update(translatedContent: TranslatedContent?) {
+ if self.translatedContent != translatedContent {
+ self.translatedContent = translatedContent
+ }
+ }
public func update(attachments: [MastodonAttachment]) {
if self.attachments != attachments {
self.attachments = attachments
diff --git a/MastodonSDK/Sources/CoreDataStack/Entity/Mastodon/Tag.swift b/MastodonSDK/Sources/CoreDataStack/Entity/Mastodon/Tag.swift
index b5c335db3..8332f3d4c 100644
--- a/MastodonSDK/Sources/CoreDataStack/Entity/Mastodon/Tag.swift
+++ b/MastodonSDK/Sources/CoreDataStack/Entity/Mastodon/Tag.swift
@@ -24,10 +24,13 @@ public final class Tag: NSManagedObject {
@NSManaged public private(set) var name: String
// sourcery: autoUpdatableObject, autoGenerateProperty
@NSManaged public private(set) var url: String
-
+ // sourcery: autoUpdatableObject, autoGenerateProperty
+ @NSManaged public private(set) var following: Bool
+
// one-to-one relationship
// many-to-many relationship
+ @NSManaged public private(set) var followedBy: Set
// one-to-many relationship
@NSManaged public private(set) var searchHistories: Set
@@ -88,7 +91,16 @@ public extension Tag {
}
static func predicate(name: String) -> NSPredicate {
- NSPredicate(format: "%K == %@", #keyPath(Tag.name), name)
+ // use case-insensitive query as tags #CaN #BE #speLLed #USiNG #arbITRARy #cASe
+ NSPredicate(format: "%K MATCHES[c] %@", #keyPath(Tag.name), name)
+ }
+
+ static func predicate(domain: String, following: Bool) -> NSPredicate {
+ NSPredicate(format: "%K == %@ AND %K == %d", #keyPath(Tag.domain), domain, #keyPath(Tag.following), following)
+ }
+
+ static func predicate(followedBy user: MastodonUser) -> NSPredicate {
+ NSPredicate(format: "ANY %K.%K == %@", #keyPath(Tag.followedBy), #keyPath(MastodonUser.id), user.id)
}
static func predicate(domain: String, name: String) -> NSPredicate {
@@ -97,6 +109,13 @@ public extension Tag {
predicate(name: name),
])
}
+
+ static func predicate(domain: String, following: Bool, by user: MastodonUser) -> NSPredicate {
+ NSCompoundPredicate(andPredicateWithSubpredicates: [
+ predicate(domain: domain, following: following),
+ predicate(followedBy: user)
+ ])
+ }
}
// MARK: - AutoGenerateProperty
@@ -112,6 +131,7 @@ extension Tag: AutoGenerateProperty {
public let updatedAt: Date
public let name: String
public let url: String
+ public let following: Bool
public let histories: [MastodonTagHistory]
public init(
@@ -121,6 +141,7 @@ extension Tag: AutoGenerateProperty {
updatedAt: Date,
name: String,
url: String,
+ following: Bool,
histories: [MastodonTagHistory]
) {
self.identifier = identifier
@@ -129,6 +150,7 @@ extension Tag: AutoGenerateProperty {
self.updatedAt = updatedAt
self.name = name
self.url = url
+ self.following = following
self.histories = histories
}
}
@@ -140,12 +162,14 @@ extension Tag: AutoGenerateProperty {
self.updatedAt = property.updatedAt
self.name = property.name
self.url = property.url
+ self.following = property.following
self.histories = property.histories
}
public func update(property: Property) {
update(updatedAt: property.updatedAt)
update(url: property.url)
+ update(following: property.following)
update(histories: property.histories)
}
// sourcery:end
@@ -167,12 +191,30 @@ extension Tag: AutoUpdatableObject {
self.url = url
}
}
+ public func update(following: Bool) {
+ if self.following != following {
+ self.following = following
+ }
+ }
public func update(histories: [MastodonTagHistory]) {
if self.histories != histories {
self.histories = histories
}
}
// sourcery:end
+
+ public func update(followed: Bool, by mastodonUser: MastodonUser) {
+ if following {
+ if !self.followedBy.contains(mastodonUser) {
+ self.mutableSetValue(forKey: #keyPath(Tag.followedBy)).add(mastodonUser)
+ }
+ } else {
+ if self.followedBy.contains(mastodonUser) {
+ self.mutableSetValue(forKey: #keyPath(Tag.followedBy)).remove(mastodonUser)
+ }
+ }
+ }
+
}
diff --git a/MastodonSDK/Sources/CoreDataStack/Entity/Transient/MastodonCardType.swift b/MastodonSDK/Sources/CoreDataStack/Entity/Transient/MastodonCardType.swift
new file mode 100644
index 000000000..59401cf4a
--- /dev/null
+++ b/MastodonSDK/Sources/CoreDataStack/Entity/Transient/MastodonCardType.swift
@@ -0,0 +1,34 @@
+//
+// MastodonCardType.swift
+// CoreDataStack
+//
+// Created by Kyle Bashour on 11/23/22.
+//
+
+import Foundation
+
+public enum MastodonCardType: RawRepresentable, Equatable {
+ case link
+ case photo
+ case video
+
+ case _other(String)
+
+ public init(rawValue: String) {
+ switch rawValue {
+ case "link": self = .link
+ case "photo": self = .photo
+ case "video": self = .video
+ default: self = ._other(rawValue)
+ }
+ }
+
+ public var rawValue: String {
+ switch self {
+ case .link: return "link"
+ case .photo: return "photo"
+ case .video: return "video"
+ case ._other(let value): return value
+ }
+ }
+}
diff --git a/MastodonSDK/Sources/CoreDataStack/Entity/Transient/MastodonNotificationType.swift b/MastodonSDK/Sources/CoreDataStack/Entity/Transient/MastodonNotificationType.swift
index 9e3029f26..455230d5e 100644
--- a/MastodonSDK/Sources/CoreDataStack/Entity/Transient/MastodonNotificationType.swift
+++ b/MastodonSDK/Sources/CoreDataStack/Entity/Transient/MastodonNotificationType.swift
@@ -7,7 +7,7 @@
import Foundation
-public enum MastodonNotificationType: RawRepresentable {
+public enum MastodonNotificationType: RawRepresentable, Equatable {
case follow
case followRequest
case mention
diff --git a/MastodonSDK/Sources/CoreDataStack/Extension/NSManagedObjectContext.swift b/MastodonSDK/Sources/CoreDataStack/Extension/NSManagedObjectContext.swift
index b921de819..3b4818992 100644
--- a/MastodonSDK/Sources/CoreDataStack/Extension/NSManagedObjectContext.swift
+++ b/MastodonSDK/Sources/CoreDataStack/Extension/NSManagedObjectContext.swift
@@ -50,43 +50,16 @@ extension NSManagedObjectContext {
extension NSManagedObjectContext {
public func perform(block: @escaping () throws -> T) async throws -> T {
- if #available(iOS 15.0, *) {
- return try await perform(schedule: .enqueued) {
- try block()
- }
- } else {
- return try await withCheckedThrowingContinuation { continuation in
- self.perform {
- do {
- let value = try block()
- continuation.resume(returning: value)
- } catch {
- continuation.resume(throwing: error)
- }
- }
- } // end return
+ return try await perform(schedule: .enqueued) {
+ try block()
}
}
-
+
public func performChanges(block: @escaping () throws -> T) async throws -> T {
- if #available(iOS 15.0, *) {
- return try await perform(schedule: .enqueued) {
- let value = try block()
- try self.saveOrRollback()
- return value
- }
- } else {
- return try await withCheckedThrowingContinuation { continuation in
- self.perform {
- do {
- let value = try block()
- try self.saveOrRollback()
- continuation.resume(returning: value)
- } catch {
- continuation.resume(throwing: error)
- }
- }
- } // end return
+ return try await perform(schedule: .enqueued) {
+ let value = try block()
+ try self.saveOrRollback()
+ return value
}
} // end func
}
diff --git a/MastodonSDK/Sources/CoreDataStack/Extension/UIFont.swift b/MastodonSDK/Sources/CoreDataStack/Extension/UIFont.swift
index a1a97a112..97fb2f397 100644
--- a/MastodonSDK/Sources/CoreDataStack/Extension/UIFont.swift
+++ b/MastodonSDK/Sources/CoreDataStack/Extension/UIFont.swift
@@ -22,9 +22,9 @@ extension UIFont {
let fontDescription = UIFontDescriptor.preferredFontDescriptor(withTextStyle: textStyle).addingAttributes([
UIFontDescriptor.AttributeName.featureSettings: [
[
- UIFontDescriptor.FeatureKey.featureIdentifier:
+ UIFontDescriptor.FeatureKey.type:
kNumberSpacingType,
- UIFontDescriptor.FeatureKey.typeIdentifier:
+ UIFontDescriptor.FeatureKey.selector:
kMonospacedNumbersSelector
]
]
diff --git a/MastodonSDK/Sources/CoreDataStack/Utility/ManagedObjectRecord.swift b/MastodonSDK/Sources/CoreDataStack/Utility/ManagedObjectRecord.swift
index 4910145cf..88b08fc3c 100644
--- a/MastodonSDK/Sources/CoreDataStack/Utility/ManagedObjectRecord.swift
+++ b/MastodonSDK/Sources/CoreDataStack/Utility/ManagedObjectRecord.swift
@@ -32,7 +32,7 @@ public class ManagedObjectRecord: Hashable {
}
extension Managed where Self: NSManagedObject {
- public var asRecrod: ManagedObjectRecord {
+ public var asRecord: ManagedObjectRecord {
return .init(objectID: objectID)
}
}
diff --git a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Scene/Profile/About/bio.about.field.verified.link.colorset/Contents.json b/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Colors/Button/tagFollow.colorset/Contents.json
similarity index 76%
rename from MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Scene/Profile/About/bio.about.field.verified.link.colorset/Contents.json
rename to MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Colors/Button/tagFollow.colorset/Contents.json
index f5112f04f..e7e41c6ff 100644
--- a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Scene/Profile/About/bio.about.field.verified.link.colorset/Contents.json
+++ b/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Colors/Button/tagFollow.colorset/Contents.json
@@ -5,9 +5,9 @@
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
- "blue" : "0.371",
- "green" : "0.565",
- "red" : "0.290"
+ "blue" : "0x38",
+ "green" : "0x29",
+ "red" : "0x2B"
}
},
"idiom" : "universal"
@@ -23,9 +23,9 @@
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
- "blue" : "0.603",
- "green" : "0.742",
- "red" : "0.476"
+ "blue" : "0xFF",
+ "green" : "0xFF",
+ "red" : "0xFF"
}
},
"idiom" : "universal"
diff --git a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Colors/Button/tagUnfollow.colorset/Contents.json b/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Colors/Button/tagUnfollow.colorset/Contents.json
new file mode 100644
index 000000000..e7fbb60a6
--- /dev/null
+++ b/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Colors/Button/tagUnfollow.colorset/Contents.json
@@ -0,0 +1,38 @@
+{
+ "colors" : [
+ {
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0xFF",
+ "green" : "0xFF",
+ "red" : "0xFF"
+ }
+ },
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0x38",
+ "green" : "0x29",
+ "red" : "0x2B"
+ }
+ },
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Scene/Profile/About/bio.about.field.verified.checkmark.colorset/Contents.json b/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Scene/Profile/About/bio.about.field.verified.text.colorset/Contents.json
similarity index 100%
rename from MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Scene/Profile/About/bio.about.field.verified.checkmark.colorset/Contents.json
rename to MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Scene/Profile/About/bio.about.field.verified.text.colorset/Contents.json
diff --git a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Scene/Welcome/mastodon.logo.black.imageset/Contents.json b/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Scene/Welcome/mastodon.logo.black.imageset/Contents.json
deleted file mode 100644
index 3018f4b9a..000000000
--- a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Scene/Welcome/mastodon.logo.black.imageset/Contents.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "images" : [
- {
- "filename" : "mastodon.logo.black.pdf",
- "idiom" : "universal"
- }
- ],
- "info" : {
- "author" : "xcode",
- "version" : 1
- }
-}
diff --git a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Scene/Welcome/mastodon.logo.black.imageset/mastodon.logo.black.pdf b/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Scene/Welcome/mastodon.logo.black.imageset/mastodon.logo.black.pdf
deleted file mode 100644
index 773ed5e77..000000000
--- a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Scene/Welcome/mastodon.logo.black.imageset/mastodon.logo.black.pdf
+++ /dev/null
@@ -1,339 +0,0 @@
-%PDF-1.7
-
-1 0 obj
- << /BBox [ 0.000000 0.000000 480.000000 119.097778 ]
- /Resources << >>
- /Subtype /Form
- /Length 2 0 R
- /Group << /Type /Group
- /S /Transparency
- >>
- /Type /XObject
- >>
-stream
-/DeviceRGB CS
-/DeviceRGB cs
-q
-1.000000 0.000000 -0.000000 1.000000 0.001709 -0.290527 cm
-0.188235 0.533333 0.831373 scn
-107.682541 47.532677 m
-106.063889 39.212074 93.195923 30.106506 78.416977 28.341690 c
-70.709908 27.421394 63.121937 26.576881 55.030510 26.946808 c
-41.798027 27.553123 31.357124 30.104706 31.357124 30.104706 c
-31.357124 28.818085 31.436523 27.591019 31.595320 26.443352 c
-33.315022 13.385902 44.544495 12.602745 55.180283 12.238235 c
-65.917122 11.870117 75.475624 14.885460 75.475624 14.885460 c
-75.917725 5.177185 l
-75.917725 5.177185 68.407349 1.147720 55.030510 0.406067 c
-47.655472 0.000053 38.495773 0.590118 27.827501 3.414185 c
-4.693666 9.538696 0.712913 34.197334 0.106597 59.224106 c
--0.079267 66.653275 0.034417 73.660202 0.034417 79.517647 c
-0.034417 105.105621 16.798328 112.606972 16.798328 112.606972 c
-25.250660 116.488472 39.757122 118.121559 54.837425 118.244263 c
-55.207348 118.244263 l
-70.287651 118.121559 84.801338 116.488472 93.255470 112.606972 c
-93.255470 112.606972 110.019386 105.105621 110.019386 79.517647 c
-110.019386 79.517647 110.230507 60.638844 107.682541 47.532677 c
-h
-f
-n
-Q
-q
-1.000000 0.000000 -0.000000 1.000000 23.245789 45.780518 cm
-0.121569 0.137255 0.168627 scn
-0.000000 39.648720 m
-0.000000 43.373230 3.018947 46.392181 6.743458 46.392181 c
-10.467969 46.392181 13.486919 43.373230 13.486919 39.648720 c
-13.486919 35.924210 10.467969 32.905262 6.743458 32.905262 c
-3.018947 32.905262 0.000000 35.924210 0.000000 39.648720 c
-h
-96.718201 31.461655 m
-96.718201 0.478188 l
-84.443916 0.478188 l
-84.443916 30.553986 l
-84.443916 36.893234 81.776840 40.110676 76.440903 40.110676 c
-70.541954 40.110676 67.586166 36.292332 67.586166 28.745865 c
-67.586166 12.286915 l
-55.384064 12.286915 l
-55.384064 28.745865 l
-55.384064 36.294136 52.426468 40.110676 46.529324 40.110676 c
-41.193382 40.110676 38.524509 36.893234 38.524509 30.553986 c
-38.524509 0.481804 l
-26.252031 0.481804 l
-26.252031 31.465263 l
-26.252031 37.795486 27.865263 42.828270 31.104361 46.550980 c
-34.442707 50.273685 38.816845 52.181053 44.246616 52.181053 c
-50.526318 52.181053 55.284817 49.768425 58.430077 44.939552 c
-61.486919 39.814735 l
-64.543762 44.939552 l
-67.689026 49.768425 72.445709 52.182858 78.727219 52.182858 c
-84.156990 52.182858 88.529327 50.273685 91.869476 46.552784 c
-95.108574 42.828270 96.720009 37.795490 96.720009 31.463459 c
-96.718201 31.461655 l
-h
-139.005112 16.060150 m
-141.536835 18.736240 142.758499 22.105263 142.758499 26.170826 c
-142.758499 30.236389 141.536835 33.605415 139.005112 36.184063 c
-136.565414 38.860153 133.468872 40.148571 129.715500 40.148571 c
-125.962112 40.148571 122.867371 38.860153 120.427673 36.184063 c
-117.987968 33.605415 116.768120 30.236389 116.768120 26.170826 c
-116.768120 22.107067 117.987968 18.736240 120.427673 16.060150 c
-122.867371 13.483311 125.962112 12.194881 129.715500 12.194881 c
-133.468872 12.194881 136.565414 13.483311 139.005112 16.060150 c
-139.005112 16.060150 l
-h
-142.758499 50.953987 m
-154.859543 50.953987 l
-154.859543 1.389473 l
-142.754898 1.389473 l
-142.754898 7.236088 l
-139.097153 2.378342 134.030075 -0.000008 127.463463 -0.000008 c
-121.176544 -0.000008 115.827965 2.477585 111.323906 7.533829 c
-106.915489 12.590069 104.665268 18.835487 104.665268 26.169022 c
-104.665268 33.405113 106.915489 39.650528 111.323906 44.706768 c
-115.827965 49.763008 121.176544 52.339851 127.463463 52.339851 c
-134.030075 52.339851 139.097153 49.959702 142.754898 45.103760 c
-142.754898 50.950378 l
-142.758499 50.953987 l
-h
-195.581955 27.062256 m
-199.147659 24.387970 200.930527 20.620148 200.836685 15.863457 c
-200.836685 10.807217 199.053848 6.840900 195.394302 4.065559 c
-191.734756 1.389473 187.326309 0.001801 181.977737 0.001801 c
-172.314575 0.001801 165.746170 3.966316 162.274292 11.797894 c
-172.783768 18.041504 l
-174.191299 13.781052 177.286011 11.599396 181.977737 11.599396 c
-186.294128 11.599396 188.452347 12.988873 188.452347 15.863457 c
-188.452347 17.944057 185.637283 19.827969 179.913376 21.313084 c
-177.755188 21.908573 175.972336 22.504059 174.566620 23.000301 c
-172.596085 23.792480 170.907074 24.685715 169.499557 25.775639 c
-166.027664 28.451729 164.246628 32.019249 164.246628 36.579250 c
-164.246628 41.436996 165.933823 45.302254 169.311874 48.079399 c
-172.783752 50.953987 177.004501 52.341656 182.071579 52.341656 c
-190.141342 52.341656 196.051117 48.871578 199.898331 41.833984 c
-189.578354 35.886318 l
-188.076996 39.255341 185.543457 40.940754 182.071579 40.940754 c
-178.412018 40.940754 176.630966 39.553085 176.630966 36.876991 c
-176.630966 34.796391 179.444214 32.912483 185.168121 31.425564 c
-189.578339 30.433083 193.048416 28.947971 195.581955 27.064060 c
-195.581955 27.062256 l
-h
-234.052322 38.661655 m
-223.449036 38.661655 l
-223.449036 18.043308 l
-223.449036 15.563908 224.389175 14.078796 226.170227 13.384060 c
-227.483917 12.887817 230.111267 12.788570 234.052322 12.987068 c
-234.052322 1.389473 l
-225.890518 0.396988 219.978958 1.190971 216.507080 3.868866 c
-213.037003 6.445709 211.346176 11.202404 211.346176 18.043308 c
-211.346176 38.661655 l
-203.184372 38.661655 l
-203.184372 50.953987 l
-211.346176 50.953987 l
-211.346176 60.965416 l
-223.449036 64.830681 l
-223.449036 50.953987 l
-234.052322 50.953987 l
-234.052322 38.661655 l
-234.052322 38.661655 l
-h
-272.614716 16.357895 m
-275.054413 18.936543 276.274261 22.208122 276.274261 26.172634 c
-276.274261 30.137146 275.054413 33.408722 272.614716 35.985565 c
-270.176819 38.562408 267.174133 39.850830 263.514587 39.850830 c
-259.855011 39.850830 256.854126 38.562408 254.414429 35.985565 c
-252.068558 33.309475 250.848709 30.037895 250.848709 26.172634 c
-250.848709 22.305565 252.068558 19.033985 254.414429 16.357895 c
-256.854126 13.781052 259.855011 12.492626 263.514587 12.492626 c
-267.174133 12.492626 270.176819 13.781052 272.614716 16.357895 c
-h
-245.875488 7.535637 m
-241.091721 12.590076 238.745850 18.736240 238.745850 26.172634 c
-238.745850 33.507973 241.091721 39.652328 245.875488 44.708572 c
-250.661057 49.763008 256.570801 52.341656 263.514587 52.341656 c
-270.458344 52.341656 276.368134 49.763008 281.153687 44.708572 c
-285.939240 39.652328 288.377136 33.408722 288.377136 26.172634 c
-288.377136 18.835491 285.939240 12.590076 281.153687 7.535637 c
-276.368134 2.479401 270.552155 0.001801 263.514587 0.001801 c
-256.476990 0.001801 250.661057 2.479401 245.875488 7.535637 c
-h
-328.818054 16.060150 m
-331.257751 18.736240 332.475769 22.105263 332.475769 26.170826 c
-332.475769 30.236389 331.257751 33.605415 328.818054 36.184063 c
-326.378357 38.860153 323.281799 40.148571 319.528412 40.148571 c
-315.775024 40.148571 312.680298 38.860153 310.146759 36.184063 c
-307.708893 33.605415 306.487213 30.236389 306.487213 26.170826 c
-306.487213 22.107067 307.708893 18.736240 310.146759 16.060150 c
-312.680298 13.483311 315.870667 12.194881 319.528412 12.194881 c
-323.281799 12.194881 326.378357 13.483311 328.818054 16.060150 c
-328.818054 16.060150 l
-h
-332.475769 70.780151 m
-344.580475 70.780151 l
-344.580475 1.389473 l
-332.475769 1.389473 l
-332.475769 7.236088 l
-328.911865 2.378342 323.844818 -0.000008 317.278198 -0.000008 c
-310.991272 -0.000008 305.550690 2.477585 301.046631 7.533829 c
-296.636414 12.590069 294.384369 18.835487 294.384369 26.169022 c
-294.384369 33.405113 296.636414 39.650528 301.046631 44.706768 c
-305.550690 49.763008 310.991272 52.339851 317.278198 52.339851 c
-323.844818 52.339851 328.911865 49.959702 332.475769 45.103760 c
-332.475769 70.780151 l
-332.475769 70.780151 l
-h
-387.083923 16.357895 m
-389.523621 18.936543 390.743469 22.208122 390.743469 26.172634 c
-390.743469 30.137146 389.523621 33.408722 387.083923 35.985565 c
-384.644226 38.562408 381.643311 39.850830 377.983765 39.850830 c
-374.324219 39.850830 371.321503 38.562408 368.881805 35.985565 c
-366.535950 33.309475 365.316101 30.037895 365.316101 26.172634 c
-365.316101 22.305565 366.535950 19.033985 368.881805 16.357895 c
-371.321503 13.781052 374.324219 12.492626 377.983765 12.492626 c
-381.643311 12.492626 384.644226 13.781052 387.083923 16.357895 c
-387.083923 16.357895 l
-h
-360.344666 7.535637 m
-355.559082 12.590076 353.215027 18.736240 353.215027 26.172634 c
-353.215027 33.507973 355.559082 39.652328 360.344666 44.708572 c
-365.130219 49.763008 371.040009 52.341656 377.983765 52.341656 c
-384.925720 52.341656 390.837280 49.763008 395.622864 44.708572 c
-400.408417 39.652328 402.846313 33.408722 402.846313 26.172634 c
-402.846313 18.835491 400.408417 12.590076 395.622864 7.535637 c
-390.837280 2.479401 385.019562 0.001801 377.983765 0.001801 c
-370.946167 0.001801 365.130219 2.479401 360.344666 7.535637 c
-360.344666 7.535637 l
-h
-455.202423 31.822556 m
-455.202423 1.389473 l
-443.097748 1.389473 l
-443.097748 30.236389 l
-443.097748 33.507969 442.255035 35.985565 440.566010 37.869476 c
-438.970825 39.553085 436.718811 40.446320 433.809937 40.446320 c
-426.960022 40.446320 423.489929 36.382557 423.489929 28.153984 c
-423.489929 1.389473 l
-411.387054 1.389473 l
-411.387054 50.953987 l
-423.489929 50.953987 l
-423.489929 45.403309 l
-426.398773 50.060753 430.994904 52.341656 437.469482 52.341656 c
-442.630371 52.341656 446.851135 50.556992 450.135345 46.888420 c
-453.513397 43.221657 455.202423 38.264664 455.202423 31.820751 c
-455.202423 31.822556 l
-h
-f
-n
-Q
-
-endstream
-endobj
-
-2 0 obj
- 9224
-endobj
-
-3 0 obj
- << /BBox [ 0.000000 0.000000 480.000000 119.097778 ]
- /Resources << >>
- /Subtype /Form
- /Length 4 0 R
- /Group << /Type /Group
- /S /Transparency
- >>
- /Type /XObject
- >>
-stream
-/DeviceRGB CS
-/DeviceRGB cs
-q
-1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm
-0.000000 0.000000 0.000000 scn
-0.000000 119.097778 m
-480.000000 119.097778 l
-480.000000 0.000031 l
-0.000000 0.000031 l
-0.000000 119.097778 l
-h
-f
-n
-Q
-
-endstream
-endobj
-
-4 0 obj
- 237
-endobj
-
-5 0 obj
- << /XObject << /X1 1 0 R >>
- /ExtGState << /E1 << /SMask << /Type /Mask
- /G 3 0 R
- /S /Alpha
- >>
- /Type /ExtGState
- >> >>
- >>
-endobj
-
-6 0 obj
- << /Length 7 0 R >>
-stream
-/DeviceRGB CS
-/DeviceRGB cs
-q
-/E1 gs
-/X1 Do
-Q
-
-endstream
-endobj
-
-7 0 obj
- 46
-endobj
-
-8 0 obj
- << /Annots []
- /Type /Page
- /MediaBox [ 0.000000 0.000000 480.000000 119.097778 ]
- /Resources 5 0 R
- /Contents 6 0 R
- /Parent 9 0 R
- >>
-endobj
-
-9 0 obj
- << /Kids [ 8 0 R ]
- /Count 1
- /Type /Pages
- >>
-endobj
-
-10 0 obj
- << /Type /Catalog
- /Pages 9 0 R
- >>
-endobj
-
-xref
-0 11
-0000000000 65535 f
-0000000010 00000 n
-0000009484 00000 n
-0000009507 00000 n
-0000009994 00000 n
-0000010016 00000 n
-0000010314 00000 n
-0000010416 00000 n
-0000010437 00000 n
-0000010612 00000 n
-0000010686 00000 n
-trailer
-<< /ID [ (some) (id) ]
- /Root 10 0 R
- /Size 11
->>
-startxref
-10746
-%%EOF
\ No newline at end of file
diff --git a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Scene/Welcome/mastodon.logo.black.large.imageset/Contents.json b/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Scene/Welcome/mastodon.logo.black.large.imageset/Contents.json
deleted file mode 100644
index fefc19832..000000000
--- a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Scene/Welcome/mastodon.logo.black.large.imageset/Contents.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "images" : [
- {
- "filename" : "mastodon.logo.black.large.pdf",
- "idiom" : "universal"
- }
- ],
- "info" : {
- "author" : "xcode",
- "version" : 1
- }
-}
diff --git a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Scene/Welcome/mastodon.logo.black.large.imageset/mastodon.logo.black.large.pdf b/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Scene/Welcome/mastodon.logo.black.large.imageset/mastodon.logo.black.large.pdf
deleted file mode 100644
index b6244d04e..000000000
--- a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Scene/Welcome/mastodon.logo.black.large.imageset/mastodon.logo.black.large.pdf
+++ /dev/null
@@ -1,339 +0,0 @@
-%PDF-1.7
-
-1 0 obj
- << /BBox [ 0.000000 0.000000 960.000000 238.195496 ]
- /Resources << >>
- /Subtype /Form
- /Length 2 0 R
- /Group << /Type /Group
- /S /Transparency
- >>
- /Type /XObject
- >>
-stream
-/DeviceRGB CS
-/DeviceRGB cs
-q
-1.000000 0.000000 -0.000000 1.000000 0.003357 -0.580933 cm
-0.188235 0.533333 0.831373 scn
-215.365082 95.065247 m
-212.127777 78.424042 186.391846 60.212906 156.833954 56.683273 c
-141.419815 54.842682 126.243874 53.153656 110.061020 53.893509 c
-83.596054 55.106140 62.714249 60.209290 62.714249 60.209290 c
-62.714249 57.636063 62.873047 55.181931 63.190639 52.886597 c
-66.630043 26.771698 89.088989 25.205383 110.360565 24.476364 c
-131.834244 23.740112 150.951248 29.770813 150.951248 29.770813 c
-151.835449 10.354263 l
-151.835449 10.354263 136.814697 2.295334 110.061020 0.812027 c
-95.310944 0.000000 76.991547 1.180130 55.655003 6.828262 c
-9.387332 19.077271 1.425826 68.394562 0.213194 118.448097 c
--0.158535 133.306442 0.068834 147.320282 0.068834 159.035172 c
-0.068834 210.211121 33.596657 225.213821 33.596657 225.213821 c
-50.501320 232.976822 79.514244 236.242996 109.674850 236.488403 c
-110.414696 236.488403 l
-140.575302 236.242996 169.602676 232.976822 186.510941 225.213821 c
-186.510941 225.213821 220.038773 210.211121 220.038773 159.035172 c
-220.038773 159.035172 220.461014 121.277573 215.365082 95.065247 c
-h
-f
-n
-Q
-q
-1.000000 0.000000 -0.000000 1.000000 46.491440 91.561096 cm
-0.121569 0.137255 0.168627 scn
-0.000000 79.297432 m
-0.000000 86.746460 6.037893 92.784363 13.486916 92.784363 c
-20.935938 92.784363 26.973839 86.746460 26.973839 79.297432 c
-26.973839 71.848412 20.935938 65.810516 13.486916 65.810516 c
-6.037893 65.810516 0.000000 71.848412 0.000000 79.297432 c
-h
-193.436401 62.923302 m
-193.436401 0.956360 l
-168.887833 0.956360 l
-168.887833 61.107964 l
-168.887833 73.786461 163.553680 80.221344 152.881805 80.221344 c
-141.083908 80.221344 135.172333 72.584648 135.172333 57.491714 c
-135.172333 24.573814 l
-110.768127 24.573814 l
-110.768127 57.491714 l
-110.768127 72.588257 104.852936 80.221344 93.058647 80.221344 c
-82.386765 80.221344 77.049019 73.786461 77.049019 61.107964 c
-77.049019 0.963593 l
-52.504063 0.963593 l
-52.504063 62.930511 l
-52.504063 75.590965 55.730526 85.656540 62.208721 93.101944 c
-68.885414 100.547363 77.633690 104.362106 88.493233 104.362106 c
-101.052635 104.362106 110.569633 99.536835 116.860153 89.879089 c
-122.973839 79.629471 l
-129.087524 89.879089 l
-135.378052 99.536835 144.891418 104.365707 157.454437 104.365707 c
-168.313980 104.365707 177.058655 100.547363 183.738953 93.105560 c
-190.217148 85.656540 193.440018 75.590973 193.440018 62.926910 c
-193.436401 62.923302 l
-h
-278.010223 32.120285 m
-283.073669 37.472473 285.516998 44.210510 285.516998 52.341637 c
-285.516998 60.472771 283.073669 67.210823 278.010223 72.368118 c
-273.130829 77.720299 266.937744 80.297134 259.431000 80.297134 c
-251.924225 80.297134 245.734741 77.720299 240.855347 72.368118 c
-235.975937 67.210823 233.536240 60.472771 233.536240 52.341637 c
-233.536240 44.214119 235.975937 37.472473 240.855347 32.120285 c
-245.734741 26.966606 251.924225 24.389748 259.431000 24.389748 c
-266.937744 24.389748 273.130829 26.966606 278.010223 32.120285 c
-278.010223 32.120285 l
-h
-285.516998 101.907967 m
-309.719086 101.907967 l
-309.719086 2.778915 l
-285.509796 2.778915 l
-285.509796 14.472160 l
-278.194305 4.756668 268.060150 -0.000031 254.926926 -0.000031 c
-242.353088 -0.000031 231.655930 4.955154 222.647812 15.067642 c
-213.830978 25.180122 209.330536 37.670959 209.330536 52.338036 c
-209.330536 66.810219 213.830978 79.301048 222.647812 89.413528 c
-231.655930 99.526016 242.353088 104.679695 254.926926 104.679695 c
-268.060150 104.679695 278.194305 99.919395 285.509796 90.207512 c
-285.509796 101.900742 l
-285.516998 101.907967 l
-h
-391.163910 54.124504 m
-398.295319 48.775932 401.861053 41.240288 401.673370 31.726898 c
-401.673370 21.614418 398.107697 13.681786 390.788605 8.131104 c
-383.469513 2.778931 374.652618 0.003571 363.955475 0.003571 c
-344.629150 0.003571 331.492340 7.932617 324.548584 23.595772 c
-345.567535 36.082993 l
-348.382599 27.562088 354.572021 23.198776 363.955475 23.198776 c
-372.588257 23.198776 376.904694 25.977730 376.904694 31.726898 c
-376.904694 35.888107 371.274567 39.655930 359.826752 42.626152 c
-355.510376 43.817131 351.944672 45.008102 349.133240 46.000587 c
-345.192169 47.584946 341.814148 49.371414 338.999115 51.551262 c
-332.055328 56.903450 328.493256 64.038490 328.493256 73.158493 c
-328.493256 82.873978 331.867645 90.604507 338.623749 96.158791 c
-345.567505 101.907967 354.009003 104.683304 364.143158 104.683304 c
-380.282684 104.683304 392.102234 97.743149 399.796661 83.667961 c
-379.156708 71.772621 l
-376.153992 78.510666 371.086914 81.881500 364.143158 81.881500 c
-356.824036 81.881500 353.261932 79.106155 353.261932 73.753975 c
-353.261932 69.592773 358.888428 65.824951 370.336243 62.851120 c
-379.156677 60.866158 386.096832 57.895927 391.163910 54.128113 c
-391.163910 54.124504 l
-h
-468.104645 77.323303 m
-446.898071 77.323303 l
-446.898071 36.086601 l
-446.898071 31.127808 448.778351 28.157578 452.340454 26.768105 c
-454.967834 25.775620 460.222534 25.577126 468.104645 25.974121 c
-468.104645 2.778915 l
-451.781036 0.793961 439.957916 2.381927 433.014160 7.737717 c
-426.074005 12.891403 422.692352 22.404793 422.692352 36.086601 c
-422.692352 77.323303 l
-406.368744 77.323303 l
-406.368744 101.907967 l
-422.692352 101.907967 l
-422.692352 121.930832 l
-446.898071 129.661346 l
-446.898071 101.907967 l
-468.104645 101.907967 l
-468.104645 77.323303 l
-468.104645 77.323303 l
-h
-545.229431 32.715775 m
-550.108826 37.873070 552.548523 44.416229 552.548523 52.345253 c
-552.548523 60.274277 550.108826 66.817436 545.229431 71.971123 c
-540.353638 77.124809 534.348267 79.701645 527.029175 79.701645 c
-519.710022 79.701645 513.708252 77.124809 508.828857 71.971123 c
-504.137115 66.618942 501.697418 60.075783 501.697418 52.345253 c
-501.697418 44.611115 504.137115 38.067955 508.828857 32.715775 c
-513.708252 27.562088 519.710022 24.985237 527.029175 24.985237 c
-534.348267 24.985237 540.353638 27.562088 545.229431 32.715775 c
-h
-491.750977 15.071259 m
-482.183441 25.180138 477.491699 37.472473 477.491699 52.345253 c
-477.491699 67.015930 482.183441 79.304657 491.750977 89.417137 c
-501.322113 99.526016 513.141602 104.683304 527.029175 104.683304 c
-540.916687 104.683304 552.736267 99.526016 562.307373 89.417137 c
-571.878479 79.304657 576.754272 66.817436 576.754272 52.345253 c
-576.754272 37.670967 571.878479 25.180138 562.307373 15.071259 c
-552.736267 4.958771 541.104309 0.003571 527.029175 0.003571 c
-512.953979 0.003571 501.322113 4.958771 491.750977 15.071259 c
-h
-657.636108 32.120285 m
-662.515503 37.472473 664.951538 44.210510 664.951538 52.341637 c
-664.951538 60.472771 662.515503 67.210823 657.636108 72.368118 c
-652.756714 77.720299 646.563599 80.297134 639.056824 80.297134 c
-631.550049 80.297134 625.360596 77.720299 620.293518 72.368118 c
-615.417786 67.210823 612.974426 60.472771 612.974426 52.341637 c
-612.974426 44.214119 615.417786 37.472473 620.293518 32.120285 c
-625.360596 26.966606 631.741333 24.389748 639.056824 24.389748 c
-646.563599 24.389748 652.756714 26.966606 657.636108 32.120285 c
-657.636108 32.120285 l
-h
-664.951538 141.560303 m
-689.160950 141.560303 l
-689.160950 2.778915 l
-664.951538 2.778915 l
-664.951538 14.472160 l
-657.823730 4.756668 647.689636 -0.000031 634.556396 -0.000031 c
-621.982544 -0.000031 611.101379 4.955154 602.093262 15.067642 c
-593.272827 25.180122 588.768738 37.670959 588.768738 52.338036 c
-588.768738 66.810219 593.272827 79.301048 602.093262 89.413528 c
-611.101379 99.526016 621.982544 104.679695 634.556396 104.679695 c
-647.689636 104.679695 657.823730 99.919395 664.951538 90.207512 c
-664.951538 141.560303 l
-664.951538 141.560303 l
-h
-774.167847 32.715775 m
-779.047241 37.873070 781.486938 44.416229 781.486938 52.345253 c
-781.486938 60.274277 779.047241 66.817436 774.167847 71.971123 c
-769.288452 77.124809 763.286621 79.701645 755.967529 79.701645 c
-748.648438 79.701645 742.643005 77.124809 737.763611 71.971123 c
-733.071899 66.618942 730.632202 60.075783 730.632202 52.345253 c
-730.632202 44.611115 733.071899 38.067955 737.763611 32.715775 c
-742.643005 27.562088 748.648438 24.985237 755.967529 24.985237 c
-763.286621 24.985237 769.288452 27.562088 774.167847 32.715775 c
-774.167847 32.715775 l
-h
-720.689331 15.071259 m
-711.118164 25.180138 706.430054 37.472473 706.430054 52.345253 c
-706.430054 67.015930 711.118164 79.304657 720.689331 89.417137 c
-730.260437 99.526016 742.080017 104.683304 755.967529 104.683304 c
-769.851440 104.683304 781.674561 99.526016 791.245728 89.417137 c
-800.816833 79.304657 805.692627 66.817436 805.692627 52.345253 c
-805.692627 37.670967 800.816833 25.180138 791.245728 15.071259 c
-781.674561 4.958771 770.039124 0.003571 755.967529 0.003571 c
-741.892334 0.003571 730.260437 4.958771 720.689331 15.071259 c
-720.689331 15.071259 l
-h
-910.404846 63.645103 m
-910.404846 2.778915 l
-886.195496 2.778915 l
-886.195496 60.472771 l
-886.195496 67.015930 884.510071 71.971123 881.132019 75.738945 c
-877.941650 79.106163 873.437622 80.892624 867.619873 80.892624 c
-853.920044 80.892624 846.979858 72.765106 846.979858 56.307961 c
-846.979858 2.778915 l
-822.774109 2.778915 l
-822.774109 101.907967 l
-846.979858 101.907967 l
-846.979858 90.806610 l
-852.797546 100.121506 861.989807 104.683304 874.938965 104.683304 c
-885.260742 104.683304 893.702271 101.113983 900.270691 93.776840 c
-907.026794 86.443298 910.404846 76.529312 910.404846 63.641495 c
-910.404846 63.645103 l
-h
-f
-n
-Q
-
-endstream
-endobj
-
-2 0 obj
- 9343
-endobj
-
-3 0 obj
- << /BBox [ 0.000000 0.000000 960.000000 238.195496 ]
- /Resources << >>
- /Subtype /Form
- /Length 4 0 R
- /Group << /Type /Group
- /S /Transparency
- >>
- /Type /XObject
- >>
-stream
-/DeviceRGB CS
-/DeviceRGB cs
-q
-1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm
-0.000000 0.000000 0.000000 scn
-0.000000 238.195496 m
-960.000000 238.195496 l
-960.000000 0.000000 l
-0.000000 0.000000 l
-0.000000 238.195496 l
-h
-f
-n
-Q
-
-endstream
-endobj
-
-4 0 obj
- 237
-endobj
-
-5 0 obj
- << /XObject << /X1 1 0 R >>
- /ExtGState << /E1 << /SMask << /Type /Mask
- /G 3 0 R
- /S /Alpha
- >>
- /Type /ExtGState
- >> >>
- >>
-endobj
-
-6 0 obj
- << /Length 7 0 R >>
-stream
-/DeviceRGB CS
-/DeviceRGB cs
-q
-/E1 gs
-/X1 Do
-Q
-
-endstream
-endobj
-
-7 0 obj
- 46
-endobj
-
-8 0 obj
- << /Annots []
- /Type /Page
- /MediaBox [ 0.000000 0.000000 960.000000 238.195496 ]
- /Resources 5 0 R
- /Contents 6 0 R
- /Parent 9 0 R
- >>
-endobj
-
-9 0 obj
- << /Kids [ 8 0 R ]
- /Count 1
- /Type /Pages
- >>
-endobj
-
-10 0 obj
- << /Type /Catalog
- /Pages 9 0 R
- >>
-endobj
-
-xref
-0 11
-0000000000 65535 f
-0000000010 00000 n
-0000009603 00000 n
-0000009626 00000 n
-0000010113 00000 n
-0000010135 00000 n
-0000010433 00000 n
-0000010535 00000 n
-0000010556 00000 n
-0000010731 00000 n
-0000010805 00000 n
-trailer
-<< /ID [ (some) (id) ]
- /Root 10 0 R
- /Size 11
->>
-startxref
-10865
-%%EOF
\ No newline at end of file
diff --git a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Scene/Welcome/mastodon.logo.imageset/Contents.json b/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Scene/Welcome/mastodon.logo.imageset/Contents.json
index 6a0bfc87a..76d42c15e 100644
--- a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Scene/Welcome/mastodon.logo.imageset/Contents.json
+++ b/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Scene/Welcome/mastodon.logo.imageset/Contents.json
@@ -1,7 +1,7 @@
{
"images" : [
{
- "filename" : "logotypeFull1.pdf",
+ "filename" : "logo.small.pdf",
"idiom" : "universal"
}
],
diff --git a/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Scene/Welcome/mastodon.logo.imageset/logo.small.pdf b/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Scene/Welcome/mastodon.logo.imageset/logo.small.pdf
new file mode 100644
index 000000000..581801f38
--- /dev/null
+++ b/MastodonSDK/Sources/MastodonAsset/Assets.xcassets/Scene/Welcome/mastodon.logo.imageset/logo.small.pdf
@@ -0,0 +1,648 @@
+%PDF-1.7
+
+1 0 obj
+ << /Type /XObject
+ /Length 2 0 R
+ /Group << /Type /Group
+ /S /Transparency
+ >>
+ /Subtype /Form
+ /Resources << >>
+ /BBox [ 0.000000 0.000000 269.000000 75.000000 ]
+ >>
+stream
+/DeviceRGB CS
+/DeviceRGB cs
+q
+1.000000 0.000000 -0.000000 1.000000 4.000000 4.000000 cm
+0.000000 0.000000 0.000000 scn
+0.000000 67.000000 m
+261.000000 67.000000 l
+261.000000 0.000000 l
+0.000000 0.000000 l
+0.000000 67.000000 l
+h
+f
+n
+Q
+
+endstream
+endobj
+
+2 0 obj
+ 234
+endobj
+
+3 0 obj
+ << /Length 4 0 R
+ /Range [ 0.000000 1.000000 0.000000 1.000000 0.000000 1.000000 ]
+ /Domain [ 0.000000 1.000000 ]
+ /FunctionType 4
+ >>
+stream
+{ 0.388235 exch 0.392157 exch 1.000000 exch dup 0.000000 gt { exch pop exch pop exch pop dup 0.000000 sub -0.050980 mul 0.388235 add exch dup 0.000000 sub -0.164706 mul 0.392157 add exch dup 0.000000 sub -0.200000 mul 1.000000 add exch } if dup 1.000000 gt { exch pop exch pop exch pop 0.337255 exch 0.227451 exch 0.800000 exch } if pop }
+endstream
+endobj
+
+4 0 obj
+ 339
+endobj
+
+5 0 obj
+ << /Type /XObject
+ /Length 6 0 R
+ /Group << /Type /Group
+ /S /Transparency
+ >>
+ /Subtype /Form
+ /Resources << /Pattern << /P1 << /Matrix [ 0.000000 -65.993195 65.993195 0.000000 -61.993195 70.224548 ]
+ /Shading << /Coords [ 0.000000 0.000000 1.000000 0.000000 ]
+ /ColorSpace /DeviceRGB
+ /Function 3 0 R
+ /Domain [ 0.000000 1.000000 ]
+ /ShadingType 2
+ /Extend [ true true ]
+ >>
+ /PatternType 2
+ /Type /Pattern
+ >> >> >>
+ /BBox [ 0.000000 0.000000 269.000000 75.000000 ]
+ >>
+stream
+/DeviceRGB CS
+/DeviceRGB cs
+q
+1.000000 0.000000 -0.000000 1.000000 4.000000 3.632446 cm
+/Pattern cs
+/P1 scn
+60.826767 51.982300 m
+59.885666 59.068882 53.787518 64.663071 46.568073 65.739265 c
+45.346546 65.922020 40.730431 66.592102 30.036348 66.592102 c
+29.956215 66.592102 l
+19.252211 66.592102 16.959164 65.922020 15.737550 65.739265 c
+8.708311 64.683380 2.299911 59.667900 0.737860 52.489872 c
+-0.003115 48.956699 -0.083220 45.037697 0.056964 41.443653 c
+0.257227 36.286026 0.297280 31.148746 0.757885 26.011383 c
+1.078305 22.600037 1.629032 19.219128 2.420071 15.889084 c
+3.902017 9.736488 9.889900 4.619389 15.757648 2.538147 c
+22.035824 0.365425 28.794806 0.000015 35.263187 1.492470 c
+35.974140 1.665001 36.675091 1.857872 37.376038 2.081261 c
+38.948124 2.588921 40.790466 3.157455 42.152252 4.152424 c
+42.172348 4.162594 42.182354 4.182858 42.192364 4.203201 c
+42.202370 4.223465 42.212376 4.243816 42.212376 4.274334 c
+42.212376 9.249176 l
+42.212376 9.249176 42.212376 9.289791 42.192364 9.310051 c
+42.192364 9.330315 42.172348 9.350658 42.152252 9.360832 c
+42.132240 9.370922 42.112228 9.381180 42.092300 9.391270 c
+42.072121 9.391270 42.052189 9.391270 42.032177 9.391270 c
+37.886612 8.386127 33.631145 7.878468 29.375511 7.888641 c
+22.035824 7.888641 20.063231 11.421898 19.502539 12.883831 c
+19.051918 14.152893 18.761566 15.482990 18.641405 16.823097 c
+18.641405 16.843441 18.641405 16.863705 18.651411 16.884052 c
+18.651411 16.904316 18.671425 16.924664 18.691521 16.934837 c
+18.711451 16.944927 18.731462 16.955097 18.751476 16.965271 c
+18.821604 16.965271 l
+22.896957 15.970303 27.082464 15.462643 31.277977 15.462643 c
+32.289371 15.462643 33.290596 15.462646 34.301991 15.493084 c
+38.517517 15.614994 42.963440 15.828209 47.118843 16.650486 c
+47.218906 16.670830 47.329144 16.691097 47.419201 16.711441 c
+53.967884 17.990677 60.196030 21.990898 60.826767 32.123367 c
+60.846947 32.519287 60.906982 36.306290 60.906982 36.712379 c
+60.906982 38.123699 61.357521 46.692589 60.836857 51.961868 c
+60.826767 51.982300 l
+h
+f
+n
+Q
+q
+1.000000 0.000000 -0.000000 1.000000 16.353134 47.918640 cm
+1.000000 1.000000 1.000000 scn
+0.000000 3.736245 m
+0.000000 5.807401 1.632209 7.472382 3.654834 7.472382 c
+5.677542 7.472382 7.309585 5.797228 7.309585 3.736245 c
+7.309585 1.675262 5.677542 0.000028 3.654834 0.000028 c
+1.632209 0.000028 0.000000 1.675262 0.000000 3.736245 c
+h
+f
+n
+Q
+q
+1.000000 0.000000 -0.000000 1.000000 32.524460 30.973694 cm
+1.000000 1.000000 1.000000 scn
+38.200230 17.462723 m
+38.200230 0.284256 l
+31.541471 0.284256 l
+31.541471 16.955149 l
+31.541471 20.467976 30.099640 22.244776 27.205791 22.244776 c
+24.011585 22.244776 22.399469 20.122755 22.399469 15.950090 c
+22.399469 6.822678 l
+15.790834 6.822678 l
+15.790834 15.950090 l
+15.790834 20.143185 14.198734 22.244776 10.984432 22.244776 c
+8.100758 22.244776 6.648746 20.467976 6.648746 16.955149 c
+6.648746 0.294346 l
+0.000000 0.294346 l
+0.000000 17.462723 l
+0.000000 20.965542 0.871139 23.757576 2.623425 25.828648 c
+4.435832 27.899887 6.808930 28.945639 9.742804 28.945639 c
+13.147311 28.945639 15.730712 27.605450 17.432966 24.925154 c
+19.095194 22.082256 l
+20.757338 24.925154 l
+22.459507 27.595276 25.032986 28.945639 28.447416 28.945639 c
+31.381290 28.945639 33.754387 27.889629 35.566792 25.828648 c
+37.319077 23.757576 38.190220 20.985806 38.190220 17.462723 c
+38.200230 17.462723 l
+h
+61.110268 8.924355 m
+62.491982 10.416807 63.143234 12.264570 63.143234 14.518509 c
+63.143234 16.772449 62.481976 18.640476 61.110268 20.061882 c
+59.788589 21.554337 58.106689 22.265121 56.073723 22.265121 c
+54.041008 22.265121 52.368858 21.554337 51.037174 20.061882 c
+49.715416 18.640476 49.054577 16.772449 49.054577 14.518509 c
+49.054577 12.264570 49.715416 10.396461 51.037174 8.924355 c
+52.358852 7.502947 54.041008 6.781986 56.073723 6.781986 c
+58.106689 6.781986 59.778584 7.492773 61.110268 8.924355 c
+h
+63.143234 28.255198 m
+69.701591 28.255198 l
+69.701591 0.781738 l
+63.143234 0.781738 l
+63.143234 4.020473 l
+61.160301 1.330006 58.416882 -0.000011 54.852188 -0.000011 c
+51.287495 -0.000011 48.543831 1.370613 46.110611 4.172821 c
+43.717499 6.974941 42.505974 10.437069 42.505974 14.498163 c
+42.505974 18.559340 43.727505 21.970684 46.110611 24.772808 c
+48.553837 27.574930 51.457687 28.996338 54.852188 28.996338 c
+58.246773 28.996338 61.160301 27.676495 63.143234 24.996117 c
+63.143234 28.234852 l
+63.143234 28.255198 l
+h
+91.770676 15.036257 m
+93.703575 13.543886 94.665031 11.462475 94.615005 8.832880 c
+94.615005 6.030758 93.653549 3.827599 91.670616 2.304710 c
+89.688515 0.812176 87.294495 0.050774 84.390968 0.050774 c
+79.154297 0.050774 75.599525 2.253845 73.716660 6.579025 c
+79.405289 10.041067 l
+80.164940 7.685646 81.837677 6.467285 84.390968 6.467285 c
+86.734138 6.467285 87.895714 7.228771 87.895714 8.822790 c
+87.895714 9.980196 86.373070 11.025948 83.269424 11.838133 c
+82.097839 12.163090 81.127220 12.498138 80.375908 12.772230 c
+79.303558 13.208757 78.392975 13.706240 77.631653 14.315380 c
+75.749619 15.807833 74.789009 17.777590 74.789009 20.305622 c
+74.789009 22.996006 75.699593 25.138290 77.531593 26.681526 c
+79.415298 28.275459 81.706749 29.037029 84.451004 29.037029 c
+88.826294 29.037029 92.020851 27.118137 94.113853 23.219398 c
+88.526115 19.929964 l
+87.715591 21.798073 86.333038 22.732088 84.451004 22.732088 c
+82.468071 22.732088 81.506630 21.970686 81.506630 20.478231 c
+81.506630 19.320744 83.029274 18.274992 86.132919 17.462723 c
+88.526115 16.914539 90.408142 16.092180 91.770676 15.036257 c
+91.780693 15.036257 l
+91.770676 15.036257 l
+h
+112.618164 21.452854 m
+106.870323 21.452854 l
+106.870323 10.020803 l
+106.870323 8.650179 107.381485 7.817646 108.352104 7.441906 c
+109.063393 7.167816 110.485130 7.117115 112.628166 7.218597 c
+112.628166 0.791912 l
+108.212013 0.243645 105.008308 0.690430 103.126274 2.162537 c
+101.243401 3.583942 100.331985 6.233803 100.331985 10.010632 c
+100.331985 21.452854 l
+95.915833 21.452854 l
+95.915833 28.265369 l
+100.331985 28.265369 l
+100.331985 33.808784 l
+106.890335 35.951027 l
+106.890335 28.255198 l
+112.638176 28.255198 l
+112.638176 21.442680 l
+112.628166 21.442680 l
+112.618164 21.452854 l
+h
+133.525681 9.086706 m
+134.847351 10.508114 135.507782 12.325527 135.507782 14.528599 c
+135.507782 16.731840 134.847351 18.528820 133.525681 19.970573 c
+132.193161 21.391897 130.571289 22.112776 128.589188 22.112776 c
+126.606262 22.112776 124.984390 21.402071 123.651871 19.970573 c
+122.381058 18.478121 121.719803 16.680973 121.719803 14.528599 c
+121.719803 12.376308 122.381058 10.579159 123.651871 9.086706 c
+124.974380 7.665382 126.606262 6.944508 128.589188 6.944508 c
+130.571289 6.944508 132.193161 7.655127 133.525681 9.086706 c
+h
+119.037262 4.203255 m
+116.443100 7.005379 115.171455 10.406551 115.171455 14.528599 c
+115.171455 18.650732 116.443100 22.001038 119.037262 24.803242 c
+121.629745 27.605450 124.834297 29.026855 128.589188 29.026855 c
+132.343262 29.026855 135.558640 27.605450 138.141953 24.803242 c
+140.725266 22.001038 142.056961 18.538994 142.056961 14.528599 c
+142.056961 10.518290 140.725266 7.005379 138.141953 4.203255 c
+135.547806 1.401051 132.394119 0.030510 128.589188 0.030510 c
+124.784264 0.030510 121.619743 1.401051 119.037262 4.203255 c
+h
+163.985123 8.934444 m
+165.306808 10.426897 165.968063 12.274660 165.968063 14.528599 c
+165.968063 16.782539 165.306808 18.650732 163.985123 20.072056 c
+162.664291 21.564592 160.982376 22.275211 158.948578 22.275211 c
+156.916458 22.275211 155.233719 21.564592 153.862839 20.072056 c
+152.540329 18.650732 151.879898 16.782539 151.879898 14.528599 c
+151.879898 12.274660 152.540329 10.406551 153.862839 8.934444 c
+155.243713 7.513037 156.966492 6.792244 158.948578 6.792244 c
+160.932358 6.792244 162.654282 7.502947 163.985123 8.934444 c
+h
+165.968063 39.260834 m
+172.526413 39.260834 l
+172.526413 0.791912 l
+165.968063 0.791912 l
+165.968063 4.030647 l
+164.035995 1.340179 161.291748 0.010162 157.727798 0.010162 c
+154.163025 0.010162 151.379578 1.380703 148.925522 4.182991 c
+146.532318 6.985115 145.321548 10.447161 145.321548 14.508337 c
+145.321548 18.569429 146.542328 21.980774 148.925522 24.782898 c
+151.358734 27.585186 154.312286 29.006512 157.727798 29.006512 c
+161.141647 29.006512 164.035995 27.686666 165.968063 25.006289 c
+165.968063 39.250683 l
+165.968063 39.260834 l
+h
+195.566971 9.117228 m
+196.888641 10.538635 197.549896 12.355879 197.549896 14.559118 c
+197.549896 16.762192 196.888641 18.559340 195.566971 20.001011 c
+194.245285 21.422335 192.623413 22.143211 190.630478 22.143211 c
+188.638367 22.143211 187.026520 21.432508 185.694000 20.001011 c
+184.422363 18.508474 183.761093 16.711493 183.761093 14.559118 c
+183.761093 12.406662 184.422363 10.609680 185.694000 9.117228 c
+187.016510 7.695736 188.648376 6.974945 190.630478 6.974945 c
+192.613403 6.974945 194.235291 7.685648 195.566971 9.117228 c
+h
+181.077713 4.233692 m
+178.495224 7.035900 177.212753 10.437071 177.212753 14.559118 c
+177.212753 18.681084 178.485229 22.031557 181.077713 24.833679 c
+183.671036 27.635885 186.875580 29.057293 190.630478 29.057293 c
+194.385376 29.057293 197.599915 27.635885 200.183228 24.833679 c
+202.775726 22.031557 204.098236 18.569429 204.098236 14.559118 c
+204.098236 10.548725 202.775726 7.035900 200.183228 4.233692 c
+197.589905 1.431572 194.435410 0.060863 190.630478 0.060863 c
+186.825546 0.060863 183.661026 1.431572 181.077713 4.233692 c
+h
+232.475540 17.696289 m
+232.475540 0.822433 l
+225.917206 0.822433 l
+225.917206 16.812975 l
+225.917206 18.630386 225.466064 20.001011 224.535477 21.036505 c
+223.674088 21.970686 222.452469 22.457994 220.879807 22.457994 c
+217.175766 22.457994 215.292923 20.204056 215.292923 15.645479 c
+215.292923 0.812172 l
+208.734528 0.812172 l
+208.734528 28.265369 l
+215.292923 28.265369 l
+215.292923 25.178900 l
+216.864761 27.757797 219.367996 29.026855 222.862732 29.026855 c
+225.656174 29.026855 227.949310 28.041977 229.732117 26.011431 c
+231.564957 23.980885 232.475540 21.229462 232.475540 17.665854 c
+f
+n
+Q
+
+endstream
+endobj
+
+6 0 obj
+ 9965
+endobj
+
+7 0 obj
+ << /Type /XObject
+ /Subtype /Image
+ /BitsPerComponent 8
+ /Length 8 0 R
+ /Height 148
+ /Width 538
+ /ColorSpace /DeviceGray
+ /Filter [ /FlateDecode ]
+ >>
+stream
+xC#[ֆ;ݍ;$$@pwwwwww~wW̽LPU[}ӿ>_a?CL]0qoAtUǏ>QoN|ȥ\Q;iϢАPCa6OP8u!!\@?Qw
3̡̞ǯIL 4DFFDEECQ11(66&6oGEGErN 5?%fSHlDEEBDDd$6vdq.11!!!>..N0
<~ՑqDdt\BfіkΗ` .dNMMK@^×E/zҠTt$;2)1!.&:2tpP;?G&:4&1͕+J
O!1q IvGM3|9yy~*r ?//77';;+3ӗ e6~ }gsz|yy>k-$ŧEL|-gfWVVUT։E7TWUUVVfefx)(A+(iчq6WFnQyUM]mUyqn_ PZϖddWU646wtvwYo~0/Mu:bPLf|1$<:˯klb"~).z";pxdtl|brrjjzzzffv2 LOMMMNLtu46TWfz]ShW"#P@K2"cщɉс<#."XCqI73pdlbjfn~~qiyeuu
ZX:0?7;=59>:4TWU
+6#|@sI9bxbDdZ~Jp:VV:|΄пMfv.ovAiuckW*@;888<<:<:::X萓{{;;[dianzbd('HD02X0s4xkY>|
+Jtg5O.mn.M
gѼ` 8['odrf~emc{wJtͷ!>\svvzrrrR @(!6/
M0X'B DucKͫ<`F;sla1Ca1Ԍ殁sp}ss'7B
X&gLJkKC=exEaEF&$=wпn~
+{
+j'wOnNV&k_CfhB#cS<9EUMKG'gW7@Woo|0Ĺ/ϟ??~6`gcynb8ۓ+@a˲Mz}YDbS 1Hw>ڽ뇗,t{ˇsb¢bmΌÓ˛ϰ~,?'tpngd@{}Y7%)NRE£ѠiB"6eYH5! }2f6XNW3
E_7awgV6u
O-n]b4 O?!.7(pwceA+)6ׯ0nT pgW5( &eO1!cvl}Q=6,/odjXT=5wlnm\FPi?}Ï!_<^͍TZ֠)1n ݙm==y丈пo& q2$332^eԶL.n\ DAHx^lLJ˓Bb|#bəARB!3&&B
ذ)!SrRK:Wvϯ%.`%?E rws~4eKx7o|vy}}ea`Bb_@B2a!HO`KD-5cxfuᵼy;h{Y(HID!JQ\DWficp[eaE?GdFQ2mſUY~Wd
+o>c]YhgvO.Ӣ|gh@/J(Jɷ't5q8P(?5&º뛫=:\]D*El\կ*M u_ 3eFQd+ϙ&T
+hUP G9c*haNеO~e^ Eg`ڐZi(r%My9x8h9c,S)2S r#sf4 /o 3yNT ˹BGk*VVK_giӧPDY(