Merge branch 'develop' into pizzax-indexeddb

This commit is contained in:
tamaina 2022-03-27 22:42:05 +09:00
commit 7314643b8d
484 changed files with 5750 additions and 4207 deletions

View file

@ -15,10 +15,7 @@ url: https://example.tld/
#───┘ Port and TLS settings └─────────────────────────────────── #───┘ Port and TLS settings └───────────────────────────────────
# #
# Misskey supports two deployment options for public. # Misskey requires a reverse proxy to support HTTPS connections.
#
# Option 1: With Reverse Proxy
# #
# +----- https://example.tld/ ------------+ # +----- https://example.tld/ ------------+
# +------+ |+-------------+ +----------------+| # +------+ |+-------------+ +----------------+|
@ -26,30 +23,12 @@ url: https://example.tld/
# +------+ |+-------------+ +----------------+| # +------+ |+-------------+ +----------------+|
# +---------------------------------------+ # +---------------------------------------+
# #
# You need to setup reverse proxy. (eg. nginx) # You need to set up a reverse proxy. (e.g. nginx)
# You do not define 'https' section. # An encrypted connection with HTTPS is highly recommended
# because tokens may be transferred in GET requests.
# Option 2: Standalone # The port that your Misskey server should listen on.
# port: 3000
# +- https://example.tld/ -+
# +------+ | +---------------+ |
# | User | ---> | | Misskey (443) | |
# +------+ | +---------------+ |
# +------------------------+
#
# You need to run Misskey as root.
# You need to set Certificate in 'https' section.
# To use option 1, uncomment below line.
#port: 3000 # A port that your Misskey server should listen.
# To use option 2, uncomment below lines.
#port: 443
#https:
# # path for certification
# key: /etc/letsencrypt/live/example.tld/privkey.pem
# cert: /etc/letsencrypt/live/example.tld/fullchain.pem
# ┌──────────────────────────┐ # ┌──────────────────────────┐
#───┘ PostgreSQL configuration └──────────────────────────────── #───┘ PostgreSQL configuration └────────────────────────────────

View file

@ -12,21 +12,52 @@ You should also include the user name that made the change.
## 12.x.x (unreleased) ## 12.x.x (unreleased)
### Improvements
- Bull Dashboardを組み込み、ジョブキューの確認や操作を行えるように @syuilo
- Check that installed Node.js version fulfills version requirement @ThatOneCalculator
- Server: performance improvements @syuilo
### Bugfixes
- API: fix endpoint endpoint @Johann150
## 12.108.1 (2022/03/12)
### Bugfixes
- リレーが動作しない問題を修正 @xianonn
- ulidを使用していると動作しない問題を修正 @syuilo
- 外部からOGPが正しく取得できない問題を修正 @syuilo
- instance can not get the files from other instance when there are items in allowedPrivateNetworks in .config/default.yml @ybw2016v
## 12.108.0 (2022/03/09)
### NOTE ### NOTE
このバージョンからNode v16.14.0以降が必要です このバージョンからNode v16.14.0以降が必要です
### Changes ### Changes
- ートの最大文字数を設定できる機能が廃止され、デフォルトで一律3000文字になりました @syuilo - ートの最大文字数を設定できる機能が廃止され、デフォルトで一律3000文字になりました @syuilo
- Misskey can no longer terminate HTTPS connections. @Johann150
- If you did not use a reverse proxy (e.g. nginx) before, you will probably need to adjust
your configuration file and set up a reverse proxy. The `https` configuration key is no
longer recognized!
### Improvements ### Improvements
- インスタンスデフォルトテーマを設定できるように @syuilo - インスタンスデフォルトテーマを設定できるように @syuilo
- ミュートに期限を設定できるように @syuilo
- アンケートが終了したときに通知が作成されるように @syuilo
- プロフィールの追加情報を最大16まで保存できるように @syuilo - プロフィールの追加情報を最大16まで保存できるように @syuilo
- 連合チャートにPub&Subを追加 @syuilo - 連合チャートにPub&Subを追加 @syuilo
- 連合チャートにActiveを追加 @syuilo
- デフォルトで10秒以上時間がかかるデータベースへのクエリは中断されるように @syuilo
- 設定ファイルの`db.extra`に`statement_timeout`を設定することでタイムアウト時間を変更できます
- Client: スプラッシュスクリーンにインスタンスのアイコンを表示するように @syuilo
### Bugfixes ### Bugfixes
- Client: リアクションピッカーの高さが低くなったまま戻らないことがあるのを修正 @syuilo - Client: リアクションピッカーの高さが低くなったまま戻らないことがあるのを修正 @syuilo
- Client: ユーザー名オートコンプリートが正しく動作しない問題を修正 @syuilo - Client: ユーザー名オートコンプリートが正しく動作しない問題を修正 @syuilo
- Client: タッチ操作だとウィジェットの編集がしにくいのを修正 @xianonn - Client: タッチ操作だとウィジェットの編集がしにくいのを修正 @xianonn
- Client: register_note_view_interruptor()が動かないのを修正 @syuilo
- Client: iPhone X以降(?)でページの内容が全て表示しきれないのを修正 @tamaina
- Client: fix image caption on mobile @nullobsi
## 12.107.0 (2022/02/12) ## 12.107.0 (2022/02/12)

View file

@ -6,6 +6,9 @@ Also, you might receive comments on your Issue/PR in Japanese, but you do not ne
The accuracy of machine translation into Japanese is not high, so it will be easier for us to understand if you write it in the original language. The accuracy of machine translation into Japanese is not high, so it will be easier for us to understand if you write it in the original language.
It will also allow the reader to use the translation tool of their preference if necessary. It will also allow the reader to use the translation tool of their preference if necessary.
## Roadmap
See [ROADMAP.md](./ROADMAP.md)
## Issues ## Issues
Before creating an issue, please check the following: Before creating an issue, please check the following:
- To avoid duplication, please search for similar issues before creating a new issue. - To avoid duplication, please search for similar issues before creating a new issue.
@ -198,11 +201,13 @@ MongoDBの時とは違い、findOneでレコードを取得する時に対象レ
MongoDBは`null`で返してきてたので、その感覚で`if (x === null)`とか書くとバグる。代わりに`if (x == null)`と書いてください MongoDBは`null`で返してきてたので、その感覚で`if (x === null)`とか書くとバグる。代わりに`if (x == null)`と書いてください
### Migration作成方法 ### Migration作成方法
``` packages/backendで:
npx ts-node ./node_modules/typeorm/cli.js migration:generate -n 変更の名前 -o ```sh
npx typeorm migration:generate -d ormconfig.js -o <migration name>
``` ```
作成されたスクリプトは不必要な変更を含むため除去してください。 - 生成後、ファイルをmigration下に移してください
- 作成されたスクリプトは不必要な変更を含むため除去してください
### コネクションには`markRaw`せよ ### コネクションには`markRaw`せよ
**Vueのコンポーネントのdataオプションとして**misskey.jsのコネクションを設定するとき、必ず`markRaw`でラップしてください。インスタンスが不必要にリアクティブ化されることで、misskey.js内の処理で不具合が発生するとともに、パフォーマンス上の問題にも繋がる。なお、Composition APIを使う場合はこの限りではない(リアクティブ化はマニュアルなため)。 **Vueのコンポーネントのdataオプションとして**misskey.jsのコネクションを設定するとき、必ず`markRaw`でラップしてください。インスタンスが不必要にリアクティブ化されることで、misskey.js内の処理で不具合が発生するとともに、パフォーマンス上の問題にも繋がる。なお、Composition APIを使う場合はこの限りではない(リアクティブ化はマニュアルなため)。

View file

@ -48,7 +48,7 @@
## Sponsors ## Sponsors
<div align="center"> <div align="center">
<a class="rss3" title="RSS3" href="https://rss3.io/" target="_blank" style="display: inline-block;"><img src="https://rss3.io/assets/images/Logo.svg" alt="RSS3" style="display: inline-block; height: 60px;"></a> <a class="rss3" title="RSS3" href="https://rss3.io/" target="_blank"><img src="https://rss3.mypinata.cloud/ipfs/QmUG6H3Z7D5P511shn7sB4CPmpjH5uZWu4m5mWX7U3Gqbu" alt="RSS3" height="60"></a>
</div> </div>
## Backers ## Backers

28
ROADMAP.md Normal file
View file

@ -0,0 +1,28 @@
# Roadmap
The order of individual tasks is a guide only and is subject to change depending on the situation.
Also, the later tasks are more indefinite and are subject to change as development progresses.
## (1) Improve maintainability \<current phase\>
This is the phase we are at now. We need to make a high-maintenance environment that can withstand future development.
- Make the number of type errors zero (backend)
- Probably need to switch some libraries to others that make it difficult to reduce type errors
- e.g. koa to fastify https://github.com/misskey-dev/misskey/issues/7537
- Improve CI
- Fix tests
- mocha, jest, etc. do not support the combination of `TypeScript + ESM + Path alias`, and the tests currently do not work.
- Fix random test failures - https://github.com/misskey-dev/misskey/issues/7985 and https://github.com/misskey-dev/misskey/issues/7986
- Add more tests
- May need to implement a mechanism that allows for DI
- Improve documentation
## (2) Improve functionality
Once Phase 1 is complete and an environment conducive to the development of a stable system is in place, the implementation of new functions can begin gradually.
- OAuth2 support https://github.com/misskey-dev/misskey/issues/8262
- GraphQL support?
## (3) Improve scalability
Once the development of the feature has settled down, this may be an opportunity to make larger modifications.
- Rewriting in Rust?

View file

@ -32,7 +32,7 @@ uploading: "يرفع..."
save: "حفظ" save: "حفظ"
users: "المستخدمون" users: "المستخدمون"
addUser: "اضافة مستخدم" addUser: "اضافة مستخدم"
favorite: "إضافة إلى المفضلة" favorite: "أضفها للمفضلة"
favorites: "المفضلات" favorites: "المفضلات"
unfavorite: "إزالة من المفضلة" unfavorite: "إزالة من المفضلة"
favorited: "أُضيف إلى المفضلة." favorited: "أُضيف إلى المفضلة."
@ -106,6 +106,7 @@ clickToShow: "اضغط للعرض"
sensitive: "محتوى حساس" sensitive: "محتوى حساس"
add: "إضافة" add: "إضافة"
reaction: "التفاعلات" reaction: "التفاعلات"
reactionSetting: "التفاعلات المراد عرضها في منتقي التفاعلات."
reactionSettingDescription2: "اسحب لترتيب ، انقر للحذف ، استخدم \"+\" للإضافة." reactionSettingDescription2: "اسحب لترتيب ، انقر للحذف ، استخدم \"+\" للإضافة."
rememberNoteVisibility: "تذكر إعدادت مدى رؤية الملاحظات" rememberNoteVisibility: "تذكر إعدادت مدى رؤية الملاحظات"
attachCancel: "أزل المرفق" attachCancel: "أزل المرفق"
@ -139,6 +140,8 @@ flagAsBot: "علّمه كحساب آلي"
flagAsBotDescription: "فعّل هذا الخيار إذا كان هذا الحساب يُدار عبر برمجية. إذا فُعل فسيكون بمثابة علامة للمطورين الآخرين لتجنب سلاسل لا متناهية من التفاعل بين حسابات الآلية وضبط أنظمة ميسكي للتعامل مع هذا الحساب كآلي." flagAsBotDescription: "فعّل هذا الخيار إذا كان هذا الحساب يُدار عبر برمجية. إذا فُعل فسيكون بمثابة علامة للمطورين الآخرين لتجنب سلاسل لا متناهية من التفاعل بين حسابات الآلية وضبط أنظمة ميسكي للتعامل مع هذا الحساب كآلي."
flagAsCat: "علّم هذا الحساب كحساب قط" flagAsCat: "علّم هذا الحساب كحساب قط"
flagAsCatDescription: "فعّل هذا الخيار لوضع علامة على الحساب لتوضيح أنه حساب قط." flagAsCatDescription: "فعّل هذا الخيار لوضع علامة على الحساب لتوضيح أنه حساب قط."
flagShowTimelineReplies: "أظهر التعليقات في الخيط الزمني"
flagShowTimelineRepliesDescription: "يظهر الردود في الخط الزمني"
autoAcceptFollowed: "اقبل طلبات المتابعة تلقائيا من الحسابات المتابَعة" autoAcceptFollowed: "اقبل طلبات المتابعة تلقائيا من الحسابات المتابَعة"
addAccount: "أضف حساباً" addAccount: "أضف حساباً"
loginFailed: "فشل الولوج" loginFailed: "فشل الولوج"
@ -230,6 +233,8 @@ resetAreYouSure: "هل تريد إعادة التعيين؟"
saved: "حُفظ" saved: "حُفظ"
messaging: "المحادثة" messaging: "المحادثة"
upload: "ارفع" upload: "ارفع"
keepOriginalUploading: "ابق الصورة الأصلية"
keepOriginalUploadingDescription: "يحفظ الصور المرفوعة على حالتها الأصلية، وان عطّل ستولد نسخة مخصصة من الصورة."
fromDrive: "من المخزن" fromDrive: "من المخزن"
fromUrl: "عبر رابط" fromUrl: "عبر رابط"
uploadFromUrl: "ارفع عبر رابط" uploadFromUrl: "ارفع عبر رابط"
@ -276,6 +281,7 @@ emptyDrive: "قرص التخزين فارغ"
emptyFolder: "هذا المجلد فارغ" emptyFolder: "هذا المجلد فارغ"
unableToDelete: "لا يمكن حذفه" unableToDelete: "لا يمكن حذفه"
inputNewFileName: "ادخل الإسم الجديد للملف" inputNewFileName: "ادخل الإسم الجديد للملف"
inputNewDescription: "أدخل تعليقًا توضيحيًا"
inputNewFolderName: "ادخل الإسم الجديد للمجلد" inputNewFolderName: "ادخل الإسم الجديد للمجلد"
circularReferenceFolder: "المجلد المستهدف ينتمي للمجلد الذي تريد حذفه" circularReferenceFolder: "المجلد المستهدف ينتمي للمجلد الذي تريد حذفه"
hasChildFilesOrFolders: "الان الملف غير فارغ. لا يمكن حذفه" hasChildFilesOrFolders: "الان الملف غير فارغ. لا يمكن حذفه"
@ -315,8 +321,6 @@ disablingTimelinesInfo: "سيتمكن المديرون والمشرفون من
registration: "إنشاء حساب" registration: "إنشاء حساب"
enableRegistration: "تفعيل إنشاء الحسابات الجديدة" enableRegistration: "تفعيل إنشاء الحسابات الجديدة"
invite: "دعوة" invite: "دعوة"
proxyRemoteFiles: "جلب الملفات البعيدة عبر وكيل"
proxyRemoteFilesDescription: "إذا فُعّل هذا الإعداد ، ستُجلب الملفات البعيدة غير الموجودة في التخزين المحلي للخادم عبر وكيل وتُنشأ لها صور مصغرة. لن يأثر على تخزين الخادم."
driveCapacityPerLocalAccount: "حصة التخزين لكل مستخدم محلي" driveCapacityPerLocalAccount: "حصة التخزين لكل مستخدم محلي"
driveCapacityPerRemoteAccount: "حصة التخزين لكل مستخدم بعيد" driveCapacityPerRemoteAccount: "حصة التخزين لكل مستخدم بعيد"
inMb: "بالميغابايت" inMb: "بالميغابايت"
@ -345,6 +349,7 @@ name: "الإسم"
antennaSource: "مصدر الهوائي" antennaSource: "مصدر الهوائي"
antennaKeywords: "الكلمات المفتاحية للإستقبال" antennaKeywords: "الكلمات المفتاحية للإستقبال"
antennaExcludeKeywords: "الكلمات المفتاحية المستثناة" antennaExcludeKeywords: "الكلمات المفتاحية المستثناة"
antennaKeywordsDescription: "افصل بينهم بمسافة لاستخدام معامل \"و\" أو بسطر لاستخدام معامل \"أو\""
notifyAntenna: "نبهني بصول ملاحظات جديدة" notifyAntenna: "نبهني بصول ملاحظات جديدة"
withFileAntenna: "ملاحظات تحوي ملفات فقط" withFileAntenna: "ملاحظات تحوي ملفات فقط"
antennaUsersDescription: "اكتب اسم مستخدم لكل سطر" antennaUsersDescription: "اكتب اسم مستخدم لكل سطر"
@ -410,7 +415,6 @@ next: "التالية"
retype: "أعد الكتابة" retype: "أعد الكتابة"
noteOf: "ملاحظات {user}" noteOf: "ملاحظات {user}"
inviteToGroup: "دعوة إلى فريق" inviteToGroup: "دعوة إلى فريق"
maxNoteTextLength: "حد عدد المحارف لكل ملاحظة"
quoteAttached: "اِقتُبسَ" quoteAttached: "اِقتُبسَ"
quoteQuestion: "أتريد تضمينها كاقتباس" quoteQuestion: "أتريد تضمينها كاقتباس"
noMessagesYet: "ليس هناك رسائل بعد" noMessagesYet: "ليس هناك رسائل بعد"
@ -469,6 +473,7 @@ hideThisNote: "إخفاء هذه الملاحظة"
showFeaturedNotesInTimeline: "أظهر الملاحظات الشائعة في الخيط الزمني" showFeaturedNotesInTimeline: "أظهر الملاحظات الشائعة في الخيط الزمني"
objectStorageBaseUrl: "الرابط الأساسي" objectStorageBaseUrl: "الرابط الأساسي"
objectStoragePrefix: "البادئة" objectStoragePrefix: "البادئة"
objectStoragePrefixDesc: "ستُحفظ الملفات في مجلدات تحوي اسماءها هذه البادئة."
objectStorageEndpoint: "نقطة النهاية" objectStorageEndpoint: "نقطة النهاية"
objectStorageRegion: "المنطقة" objectStorageRegion: "المنطقة"
objectStorageUseSSL: "استخدم SSL" objectStorageUseSSL: "استخدم SSL"
@ -630,7 +635,10 @@ experimentalFeatures: "ميّزات اختبارية"
developer: "المطور" developer: "المطور"
makeExplorable: "أظهر الحساب في صفحة \"استكشاف\"" makeExplorable: "أظهر الحساب في صفحة \"استكشاف\""
makeExplorableDescription: "بتعطيل هذا الخيار لن يظهر حسابك في صفحة \"استكشاف\"" makeExplorableDescription: "بتعطيل هذا الخيار لن يظهر حسابك في صفحة \"استكشاف\""
wide: "عريض"
narrow: "رفيع"
reloadToApplySetting: "سيُطبق هذا الإعداد بعد إعادة تحميل الصفحة، أتريد إعادة تحميلها الآن؟" reloadToApplySetting: "سيُطبق هذا الإعداد بعد إعادة تحميل الصفحة، أتريد إعادة تحميلها الآن؟"
needReloadToApply: "سيطبق هذا بعد إعادة التحميل."
showTitlebar: "اعرض شريط العنوان" showTitlebar: "اعرض شريط العنوان"
clearCache: "امسح التخزين المؤقت" clearCache: "امسح التخزين المؤقت"
onlineUsersCount: "{n} مستخدم متصل" onlineUsersCount: "{n} مستخدم متصل"
@ -661,6 +669,7 @@ capacity: "السعة"
inUse: "مستخدم" inUse: "مستخدم"
editCode: "حرر الشفرة" editCode: "حرر الشفرة"
apply: "تطبيق" apply: "تطبيق"
receiveAnnouncementFromInstance: "استلم إشعارات من هذا المثيل"
emailNotification: "إشعارات البريد الكتروني" emailNotification: "إشعارات البريد الكتروني"
inChannelSearch: "ابحث عن قناة" inChannelSearch: "ابحث عن قناة"
useReactionPickerForContextMenu: "افتح منتقي التفاعلات عند النقر بالزر الأيمن" useReactionPickerForContextMenu: "افتح منتقي التفاعلات عند النقر بالزر الأيمن"
@ -674,6 +683,7 @@ unlikeConfirm: "أتريد إلغاء إعجابك؟"
fullView: "ملء الشاشة" fullView: "ملء الشاشة"
quitFullView: "اخرج من وضع ملء للشاشة" quitFullView: "اخرج من وضع ملء للشاشة"
addDescription: "أضف وصفًا" addDescription: "أضف وصفًا"
userPagePinTip: "لعرض ملاحظة هنا اختر \"دبسها على الصفحة الشخصية\" من قائمة تلك الملاحظة."
notSpecifiedMentionWarning: "في الملاحظة ذكر لمستخدمين لن يستلموها." notSpecifiedMentionWarning: "في الملاحظة ذكر لمستخدمين لن يستلموها."
info: "عن" info: "عن"
userInfo: "معلومات المستخدم" userInfo: "معلومات المستخدم"
@ -748,11 +758,30 @@ makeReactionsPublicDescription: "هذا سيجعل قائمة تفاعلاتك
classic: "تقليدي" classic: "تقليدي"
muteThread: "اكتم النقاش" muteThread: "اكتم النقاش"
unmuteThread: "ارفع الكتم عن النقاش" unmuteThread: "ارفع الكتم عن النقاش"
ffVisibility: "مرئية المتابِعين/المتابَعين"
ffVisibilityDescription: "يسمح لك بتحديد من يمكنهم رؤية متابِعيك ومتابَعيك."
deleteAccountConfirm: "سيحذف حسابك نهائيًا، أتريد المتابعة؟" deleteAccountConfirm: "سيحذف حسابك نهائيًا، أتريد المتابعة؟"
incorrectPassword: "كلمة السر خاطئة." incorrectPassword: "كلمة السر خاطئة."
voteConfirm: "متيقِّن من تصويتك لـ {choice}؟"
hide: "إخفاء" hide: "إخفاء"
leaveGroup: "مغادرة الفريق" leaveGroup: "مغادرة الفريق"
leaveGroupConfirm: "متيقن من مغادرة \"{name}\"؟"
welcomeBackWithName: "مرحبًا بك مجددًا {name}" welcomeBackWithName: "مرحبًا بك مجددًا {name}"
clickToFinishEmailVerification: "انقر [{ok}] لاستيثاق بريدك الإلكتروني."
overridedDeviceKind: "نوع الجهاز"
smartphone: "هاتف ذكي"
tablet: "جهاز لوحي"
auto: "تلقائي"
themeColor: "لون السمة"
size: "الحجم"
numberOfColumn: "عدد الأعمدة"
searchByGoogle: "غوغل"
mutePeriod: "مدة الكتم"
indefinitely: "أبدًا"
tenMinutes: "10 دقائق"
oneHour: "ساعة"
oneDay: "يوم"
oneWeek: "أسبوع"
_emailUnavailable: _emailUnavailable:
used: "هذا البريد الإلكتروني مستخدم" used: "هذا البريد الإلكتروني مستخدم"
format: "صيغة البريد الإلكتروني غير صالحة" format: "صيغة البريد الإلكتروني غير صالحة"
@ -775,6 +804,7 @@ _accountDelete:
inProgress: "عملية الحذف جارية" inProgress: "عملية الحذف جارية"
_ad: _ad:
back: "رجوع" back: "رجوع"
reduceFrequencyOfThisAd: "قلل عرض هذا الإعلان"
_forgotPassword: _forgotPassword:
enterEmail: "أدخل البريد الإلكتروني المرتبط بحسابك لكي يرسل إليك رابط لإعادة تعيين كلمة المرور." enterEmail: "أدخل البريد الإلكتروني المرتبط بحسابك لكي يرسل إليك رابط لإعادة تعيين كلمة المرور."
ifNoEmail: "إذا لم تربط حسابك ببريد إلكتروني سيتوجب عليك التواصل مع مدير الموقع." ifNoEmail: "إذا لم تربط حسابك ببريد إلكتروني سيتوجب عليك التواصل مع مدير الموقع."
@ -801,7 +831,7 @@ _registry:
createKey: "أنشئ مفتاحًا" createKey: "أنشئ مفتاحًا"
_aboutMisskey: _aboutMisskey:
about: "ميسكي هو برمجية مفتوحة المصدر يطورها syuilo منذ 2014." about: "ميسكي هو برمجية مفتوحة المصدر يطورها syuilo منذ 2014."
contributors: "المساهم الرئيسي" contributors: "المساهمون الرئيسيون"
allContributors: "كل المساهمين" allContributors: "كل المساهمين"
source: "الشفرة المصدرية" source: "الشفرة المصدرية"
translation: "ترجم ميسكي" translation: "ترجم ميسكي"
@ -823,8 +853,11 @@ _mfm:
urlDescription: "يمكن عرض الروابط" urlDescription: "يمكن عرض الروابط"
link: "رابط" link: "رابط"
bold: "عريض" bold: "عريض"
boldDescription: "جعل الحروف أثخن لإبرازها."
small: "صغير" small: "صغير"
smallDescription: "يعرض المحتوى صغيرًا ورفيعًا."
center: "وسط" center: "وسط"
centerDescription: "يمركز المحتوى في الوَسَط."
quote: "اقتبس" quote: "اقتبس"
emoji: "إيموجي مخصص" emoji: "إيموجي مخصص"
search: "البحث" search: "البحث"
@ -1088,9 +1121,12 @@ _postForm:
quotePlaceholder: "اقتبس هذه الملاحظة…" quotePlaceholder: "اقتبس هذه الملاحظة…"
channelPlaceholder: "انشر في قناة..." channelPlaceholder: "انشر في قناة..."
_placeholders: _placeholders:
a: "ما الذي تنوي فعله؟"
b: "ماذا يحدث حولك ؟"
c: "ما الذي تفكر فيه؟" c: "ما الذي تفكر فيه؟"
d: "ما الذي تريد قوله؟" d: "ما الذي تريد قوله؟"
e: "أكتب..." e: "أكتب..."
f: "بانتظارك لتكتب..."
_profile: _profile:
name: "الإسم" name: "الإسم"
username: "اسم المستخدم" username: "اسم المستخدم"
@ -1109,23 +1145,25 @@ _exportOrImport:
muteList: "المستخدمون المكتومون" muteList: "المستخدمون المكتومون"
blockingList: "المستخدمون المحجوبون" blockingList: "المستخدمون المحجوبون"
userLists: "القوائم" userLists: "القوائم"
excludeMutingUsers: "استثن الحسابات المكتومة"
excludeInactiveUsers: "استثن المستخدمين الخاملين"
_charts: _charts:
federation: "الفديرالية" federation: "الفديرالية"
apRequest: "الطلبات" apRequest: "الطلبات"
usersIncDec: "اختلاف عدد المستخدمين" usersIncDec: "تباين عدد المستخدمين"
usersTotal: "مجموع عدد المستخدمين والمستخدمات" usersTotal: "مجموع عدد المستخدمين والمستخدمات"
activeUsers: "المستخدمون النشطون" activeUsers: "المستخدمون النشطون"
notesIncDec: "اختلاف عدد الملاحظات" notesIncDec: "تباين عدد الملاحظات"
localNotesIncDec: "اختلاف عدد الملاحظات المحلية" localNotesIncDec: "تباين عدد الملاحظات المحلية"
remoteNotesIncDec: "اختلاف عدد الملاحظات البعيدة" remoteNotesIncDec: "تباين عدد الملاحظات البعيدة"
notesTotal: "إجمالي الملاحظات" notesTotal: "إجمالي الملاحظات"
filesIncDec: "اختلاف عدد الملفات" filesIncDec: "تباين عدد الملفات"
filesTotal: "العدد الإجمالي للملفات" filesTotal: "العدد الإجمالي للملفات"
_instanceCharts: _instanceCharts:
requests: "الطلبات" requests: "الطلبات"
users: "اختلاف عدد المستخدمين" users: "تباين عدد المستخدمين"
notes: "اختلاف عدد الملاحظات" notes: "تباين عدد الملاحظات"
files: "اختلاف عدد الملفات" files: "تباين عدد الملفات"
_timelines: _timelines:
home: "الرئيسي" home: "الرئيسي"
local: "المحلي" local: "المحلي"
@ -1139,14 +1177,21 @@ _pages:
updated: "نجح تعديل الصفحة" updated: "نجح تعديل الصفحة"
deleted: "نجح حذف الصفحة" deleted: "نجح حذف الصفحة"
pageSetting: "إعدادات الصفحة" pageSetting: "إعدادات الصفحة"
nameAlreadyExists: "رابط الصفحة موجود مسبقًا"
invalidNameTitle: "رابط الصفحة ليس صالحًا"
invalidNameText: "تأكد أن عنوان الصفحة ليس فارغًا"
editThisPage: "عدّل هذه الصفحة"
viewSource: "اظهر المصدر" viewSource: "اظهر المصدر"
viewPage: "اعرض صفحاتك" viewPage: "اعرض صفحاتك"
like: "أعجبني" like: "أعجبني"
unlike: "أزل الإعجاب" unlike: "أزل الإعجاب"
my: "صفحاتي" my: "صفحاتي"
liked: "الصفحات المُعجب بها"
featured: "الأكثر شعبية" featured: "الأكثر شعبية"
contents: "المحتوى" contents: "المحتوى"
variables: "متغيّرات"
title: "العنوان" title: "العنوان"
url: "رابط الصفحة"
summary: "ملخص الصفحة" summary: "ملخص الصفحة"
alignCenter: "توسيط العناصر" alignCenter: "توسيط العناصر"
hideTitleWhenPinned: "اخف عنوان الصفحة عند تدبيسها في ملف الشخصي" hideTitleWhenPinned: "اخف عنوان الصفحة عند تدبيسها في ملف الشخصي"
@ -1401,6 +1446,7 @@ _notification:
youReceivedFollowRequest: "تلقيتَ طلب متابعة" youReceivedFollowRequest: "تلقيتَ طلب متابعة"
yourFollowRequestAccepted: "قُبل طلب المتابعة" yourFollowRequestAccepted: "قُبل طلب المتابعة"
youWereInvitedToGroup: "دُعيت إلى فريقٍ" youWereInvitedToGroup: "دُعيت إلى فريقٍ"
pollEnded: "ظهرت نتائج الاستطلاع"
_types: _types:
all: "الكل" all: "الكل"
follow: "متابِعون جدد" follow: "متابِعون جدد"
@ -1409,6 +1455,7 @@ _notification:
renote: "أعد النشر" renote: "أعد النشر"
quote: "الاقتباسات" quote: "الاقتباسات"
reaction: "التفاعلات" reaction: "التفاعلات"
pollVote: "مصوِت شارك في الاستطلاع"
receiveFollowRequest: "طلبات المتابعة المتلقاة" receiveFollowRequest: "طلبات المتابعة المتلقاة"
followRequestAccepted: "طلبات المتابعة المقبولة" followRequestAccepted: "طلبات المتابعة المقبولة"
app: "إشعارات التطبيقات المرتبطة" app: "إشعارات التطبيقات المرتبطة"

View file

@ -325,8 +325,6 @@ disablingTimelinesInfo: "আপনি এই টাইমলাইনগুল
registration: "নিবন্ধন" registration: "নিবন্ধন"
enableRegistration: "নতুন ব্যাবহারকারী নিবন্ধন চালু করুন" enableRegistration: "নতুন ব্যাবহারকারী নিবন্ধন চালু করুন"
invite: "আমন্ত্রণ" invite: "আমন্ত্রণ"
proxyRemoteFiles: "রিমোট ফাইলসমুহ প্রক্সি করুন"
proxyRemoteFilesDescription: "যখন এই সেটিংটি চালু থাকে, তখন অসংরক্ষিত বা অতিরিক্ত ক্ষমতার কারণে দূরবর্তী ফাইলগুলিকে স্থানীয়ভাবে প্রক্সি করা হবে এবং থাম্বনেলগুলিও তৈরি করা হবে৷ সার্ভার স্টোরেজ ব্যাবহার করে না,"
driveCapacityPerLocalAccount: "প্রত্যেক স্থানীয় ব্যাবহারকারীর জন্য ড্রাইভের জায়গা" driveCapacityPerLocalAccount: "প্রত্যেক স্থানীয় ব্যাবহারকারীর জন্য ড্রাইভের জায়গা"
driveCapacityPerRemoteAccount: "প্রত্যেক রিমোট ব্যাবহারকারীর জন্য ড্রাইভের জায়গা" driveCapacityPerRemoteAccount: "প্রত্যেক রিমোট ব্যাবহারকারীর জন্য ড্রাইভের জায়গা"
inMb: "মেগাবাইটে লিখুন" inMb: "মেগাবাইটে লিখুন"
@ -422,7 +420,6 @@ next: "পরবর্তী"
retype: "পুনঃ প্রবেশ" retype: "পুনঃ প্রবেশ"
noteOf: "{user} এর নোট" noteOf: "{user} এর নোট"
inviteToGroup: "গ্রুপে আমন্ত্রণ জানান" inviteToGroup: "গ্রুপে আমন্ত্রণ জানান"
maxNoteTextLength: "নোট এর সর্বোচ্চ দৈর্ঘ্য"
quoteAttached: "উদ্ধৃত" quoteAttached: "উদ্ধৃত"
quoteQuestion: "উদ্ধৃতি হিসাবে সংযুক্ত করবেন?" quoteQuestion: "উদ্ধৃতি হিসাবে সংযুক্ত করবেন?"
noMessagesYet: "কোন মেসেজ নেই" noMessagesYet: "কোন মেসেজ নেই"
@ -831,6 +828,10 @@ smartphone: "স্মার্টফোন"
tablet: "ট্যাবলেট" tablet: "ট্যাবলেট"
auto: "স্বয়ংক্রিয়" auto: "স্বয়ংক্রিয়"
themeColor: "থিমের রং" themeColor: "থিমের রং"
size: "আকার"
numberOfColumn: "কলামের সংখ্যা"
searchByGoogle: "গুগল"
indefinitely: "অনির্দিষ্ট"
_emailUnavailable: _emailUnavailable:
used: "এই ইমেইল ঠিকানাটি ইতোমধ্যে ব্যবহৃত হয়েছে" used: "এই ইমেইল ঠিকানাটি ইতোমধ্যে ব্যবহৃত হয়েছে"
format: "এই ইমেল ঠিকানাটি সঠিকভাবে লিখা হয়নি" format: "এই ইমেল ঠিকানাটি সঠিকভাবে লিখা হয়নি"

26
locales/ca-ES.yml Normal file
View file

@ -0,0 +1,26 @@
---
_lang_: "Català"
headlineMisskey: "Una xarxa connectada per notes"
search: "Cercar"
notifications: "Notificacions"
username: "Nom d'usuari"
password: "Contrasenya"
forgotPassword: "Contrasenya oblidada"
fetchingAsApObject: "Cercant en el Fediverse..."
ok: "OK"
gotIt: "Ho he entès!"
cancel: "Cancel·lar"
smtpUser: "Nom d'usuari"
smtpPass: "Contrasenya"
searchByGoogle: "Cercar"
_mfm:
search: "Cercar"
_sfx:
notification: "Notificacions"
_widgets:
notifications: "Notificacions"
_profile:
username: "Nom d'usuari"
_deck:
_columns:
notifications: "Notificacions"

View file

@ -449,6 +449,7 @@ clearCache: "Vyprázdnit mezipaměť"
info: "Informace" info: "Informace"
user: "Uživatelé" user: "Uživatelé"
administration: "Administrace" administration: "Administrace"
searchByGoogle: "Vyhledávání"
_email: _email:
_follow: _follow:
title: "Máte nového následovníka" title: "Máte nového následovníka"

View file

@ -14,16 +14,16 @@ gotIt: "Verstanden!"
cancel: "Abbrechen" cancel: "Abbrechen"
enterUsername: "Benutzername eingeben" enterUsername: "Benutzername eingeben"
renotedBy: "Renote von {user}" renotedBy: "Renote von {user}"
noNotes: "Keine Notizen" noNotes: "Keine Notizen gefunden"
noNotifications: "Keine Benachrichtigungen" noNotifications: "Keine Benachrichtigungen gefunden"
instance: "Instanz" instance: "Instanz"
settings: "Einstellungen" settings: "Einstellungen"
basicSettings: "Allgemeine Einstellungen" basicSettings: "Allgemeine Einstellungen"
otherSettings: "Weitere Einstellungen" otherSettings: "Weitere Einstellungen"
openInWindow: "In Fenster öffnen" openInWindow: "In einem Fenster öffnen"
profile: "Profil" profile: "Profil"
timeline: "Chronik" timeline: "Chronik"
noAccountDescription: "Dieser Nutzer hat seine Profilbeschreibung noch nicht ausgefüllt." noAccountDescription: "Dieser Nutzer hat seine Profilbeschreibung noch nicht ausgefüllt"
login: "Anmelden" login: "Anmelden"
loggingIn: "Du wirst angemeldet …" loggingIn: "Du wirst angemeldet …"
logout: "Abmelden" logout: "Abmelden"
@ -38,8 +38,8 @@ unfavorite: "Aus Favoriten entfernen"
favorited: "Zu Favoriten hinzugefügt." favorited: "Zu Favoriten hinzugefügt."
alreadyFavorited: "Bereits zu den Favoriten hinzugefügt." alreadyFavorited: "Bereits zu den Favoriten hinzugefügt."
cantFavorite: "Hinzufügen zu Favoriten fehlgeschlagen." cantFavorite: "Hinzufügen zu Favoriten fehlgeschlagen."
pin: "Anheften" pin: "An dein Profil anheften"
unpin: "Lösen" unpin: "Von deinem Profil lösen"
copyContent: "Inhalt kopieren" copyContent: "Inhalt kopieren"
copyLink: "Link kopieren" copyLink: "Link kopieren"
delete: "Löschen" delete: "Löschen"
@ -47,7 +47,7 @@ deleteAndEdit: "Löschen und Bearbeiten"
deleteAndEditConfirm: "Möchtest du diese Notiz wirklich löschen und bearbeiten? Alle Reaktionen, Renotes und Antworten dieser Notiz werden verloren gehen." deleteAndEditConfirm: "Möchtest du diese Notiz wirklich löschen und bearbeiten? Alle Reaktionen, Renotes und Antworten dieser Notiz werden verloren gehen."
addToList: "Zu Liste hinzufügen" addToList: "Zu Liste hinzufügen"
sendMessage: "Nachricht senden" sendMessage: "Nachricht senden"
copyUsername: "Benutzername kopieren" copyUsername: "Benutzernamen kopieren"
searchUser: "Nach einem Benutzer suchen" searchUser: "Nach einem Benutzer suchen"
reply: "Antworten" reply: "Antworten"
loadMore: "Mehr laden" loadMore: "Mehr laden"
@ -63,12 +63,12 @@ import: "Import"
export: "Export" export: "Export"
files: "Dateien" files: "Dateien"
download: "Herunterladen" download: "Herunterladen"
driveFileDeleteConfirm: "Möchtest du die Datei „{name}“ wirklich löschen? Die zugehörige Notiz wird ebenso verschwinden." driveFileDeleteConfirm: "Möchtest du die Datei „{name}“ wirklich löschen? Notizen mit dieser Datei werden ebenso verschwinden."
unfollowConfirm: "Möchtest du {name} nicht mehr folgen?" unfollowConfirm: "Möchtest du {name} nicht mehr folgen?"
exportRequested: "Du hast einen Export angefragt. Dies kann etwas Zeit in Anspruch nehmen. Sobald der Export abgeschlossen ist, wird er deiner Drive hinzugefügt." exportRequested: "Du hast einen Export angefragt. Dies kann etwas Zeit in Anspruch nehmen. Sobald der Export abgeschlossen ist, wird er deiner Drive hinzugefügt."
importRequested: "Du hast einen Import angefragt. Dies kann etwas Zeit in Anspruch nehmen." importRequested: "Du hast einen Import angefragt. Dies kann etwas Zeit in Anspruch nehmen."
lists: "Listen" lists: "Listen"
noLists: "Keine Listen" noLists: "Keine Listen gefunden"
note: "Notiz" note: "Notiz"
notes: "Notizen" notes: "Notizen"
following: "Folgt" following: "Folgt"
@ -79,16 +79,16 @@ manageLists: "Listen verwalten"
error: "Fehler" error: "Fehler"
somethingHappened: "Ein Fehler ist aufgetreten" somethingHappened: "Ein Fehler ist aufgetreten"
retry: "Wiederholen" retry: "Wiederholen"
pageLoadError: "Laden der Seite fehlgeschlagen." pageLoadError: "Die Seite konnte nicht geladen werden."
pageLoadErrorDescription: "Dieser Fehler wird meist durch Netzwerkfehler oder den Browser-Cache verursacht. Bitte leere den Cache oder versuche es nach einiger Zeit erneut." pageLoadErrorDescription: "Dieser Fehler wird meist durch Netzwerkfehler oder den Browser-Cache verursacht. Bitte leere den Cache oder versuche es nach einiger Zeit erneut."
serverIsDead: "Dieser Server antwortet nicht. Bitte warte einen Moment und versuche es dann erneut." serverIsDead: "Dieser Server antwortet nicht. Bitte warte einen Moment und versuche es dann erneut."
youShouldUpgradeClient: "Bitte aktualisiere diese Seite, um eine neuere Version deines Clients zu verwenden." youShouldUpgradeClient: "Bitte aktualisiere diese Seite, um eine neuere Version deines Clients zu verwenden."
enterListName: "Name der Liste eingeben" enterListName: "Listennamen eingeben"
privacy: "Privatsphäre" privacy: "Privatsphäre"
makeFollowManuallyApprove: "Follow-Anfragen benötigen Bestätigung" makeFollowManuallyApprove: "Follow-Anfragen benötigen Bestätigung"
defaultNoteVisibility: "Standardsichtbarkeit" defaultNoteVisibility: "Standardsichtbarkeit"
follow: "Folgen" follow: "Folgen"
followRequest: "Follow-Anfrage" followRequest: "Follow-Anfrage senden"
followRequests: "Follow-Anfragen" followRequests: "Follow-Anfragen"
unfollow: "Nicht mehr folgen" unfollow: "Nicht mehr folgen"
followRequestPending: "Follow-Anfrage ausstehend" followRequestPending: "Follow-Anfrage ausstehend"
@ -107,11 +107,11 @@ sensitive: "NSFW"
add: "Hinzufügen" add: "Hinzufügen"
reaction: "Reaktionen" reaction: "Reaktionen"
reactionSetting: "In der Reaktionsauswahl anzuzeigende Reaktionen" reactionSetting: "In der Reaktionsauswahl anzuzeigende Reaktionen"
reactionSettingDescription2: "Ziehe zum Anordnen, klicke zum Löschen, drücke + zum Hinzufügen" reactionSettingDescription2: "Ziehe um Anzuordnen, klicke um zu löschen, drücke „+“ um hinzuzufügen"
rememberNoteVisibility: "Notizsichtbarkeit merken" rememberNoteVisibility: "Notizsichtbarkeit merken"
attachCancel: "Anhang entfernen" attachCancel: "Anhang entfernen"
markAsSensitive: "Als NSFW markieren" markAsSensitive: "Als NSFW markieren"
unmarkAsSensitive: "NSFW-Markierung entfernen" unmarkAsSensitive: "Als nicht NSFW markieren"
enterFileName: "Dateinamen eingeben" enterFileName: "Dateinamen eingeben"
mute: "Stummschalten" mute: "Stummschalten"
unmute: "Stummschaltung aufheben" unmute: "Stummschaltung aufheben"
@ -129,20 +129,20 @@ selectWidget: "Widget auswählen"
editWidgets: "Widgets bearbeiten" editWidgets: "Widgets bearbeiten"
editWidgetsExit: "Fertig" editWidgetsExit: "Fertig"
customEmojis: "Benutzerdefinierte Emojis" customEmojis: "Benutzerdefinierte Emojis"
emoji: "Emojis" emoji: "Emoji"
emojis: "Emojis" emojis: "Emojis"
emojiName: "Emoji-Name" emojiName: "Emoji-Name"
emojiUrl: "Emoji-URL" emojiUrl: "Emoji-URL"
addEmoji: "Emoji hinzufügen" addEmoji: "Emoji hinzufügen"
settingGuide: "Empfohlene Einstellung" settingGuide: "Empfohlene Einstellung"
cacheRemoteFiles: "Dateien von fremden Instanzen im Cache speichern" cacheRemoteFiles: "Dateien von fremden Instanzen im Cache speichern"
cacheRemoteFilesDescription: "Ist diese Einstellung deaktiviert, so werden Dateien fremder Instanzen direkt von dort geladen. Hierdurch wird Speicherplatz auf dem Server gespart, aber durch fehlende Generierung von Vorschaubildern mehr Bandbreite verwendet." cacheRemoteFilesDescription: "Ist diese Einstellung deaktiviert, so werden Dateien fremder Instanzen direkt von dort geladen. Hierdurch wird Speicherplatz auf diesem Server gespart, aber durch fehlende Generierung von Vorschaubildern mehr Bandbreite verwendet."
flagAsBot: "Als Bot markieren" flagAsBot: "Als Bot markieren"
flagAsBotDescription: "Aktiviere diese Option, falls dieses Benutzerkonto durch ein Programm gesteuert wird. Falls aktiviert, agiert es als Flag für andere Entwickler zur Verhinderung von endlosen Kettenreaktionen mit anderen Bots und lässt Misskeys interne Systeme dieses Benutzerkonto als Bot behandeln." flagAsBotDescription: "Aktiviere diese Option, falls dieses Benutzerkonto durch ein Programm gesteuert wird. Falls aktiviert, agiert es als Flag für andere Entwickler zur Verhinderung von endlosen Kettenreaktionen mit anderen Bots und lässt Misskeys interne Systeme dieses Benutzerkonto als Bot behandeln."
flagAsCat: "Als Katze markieren" flagAsCat: "Als Katze markieren"
flagAsCatDescription: "Aktiviere diese Option, um dieses Benutzerkonto als Katze zu markieren." flagAsCatDescription: "Aktiviere diese Option, um dieses Benutzerkonto als Katze zu markieren."
flagShowTimelineReplies: "Antworten in der Chronik anzeigen" flagShowTimelineReplies: "Antworten in der Chronik anzeigen"
flagShowTimelineRepliesDescription: "Ist diese Option aktiviert, so werden Antworten von Benutzern auf die Notizen anderer Benuzter in der Chronik an angezeigt." flagShowTimelineRepliesDescription: "Ist diese Option aktiviert, so werden Antworten von Benutzern auf die Notizen anderer Benutzer in der Chronik angezeigt."
autoAcceptFollowed: "Follow-Anfragen von Benutzern, denen du folgst, automatisch akzeptieren" autoAcceptFollowed: "Follow-Anfragen von Benutzern, denen du folgst, automatisch akzeptieren"
addAccount: "Benutzerkonto hinzufügen" addAccount: "Benutzerkonto hinzufügen"
loginFailed: "Anmeldung fehlgeschlagen" loginFailed: "Anmeldung fehlgeschlagen"
@ -151,12 +151,12 @@ general: "Allgemein"
wallpaper: "Hintergrund" wallpaper: "Hintergrund"
setWallpaper: "Hintergrund festlegen" setWallpaper: "Hintergrund festlegen"
removeWallpaper: "Hintergrund entfernen" removeWallpaper: "Hintergrund entfernen"
searchWith: "Suche: {q}" searchWith: "Suchen: {q}"
youHaveNoLists: "Du hast keine Listen" youHaveNoLists: "Du hast keine Listen"
followConfirm: "Möchtest du {name} wirklich folgen?" followConfirm: "Möchtest du {name} wirklich folgen?"
proxyAccount: "Proxy-Benutzerkonto" proxyAccount: "Proxy-Benutzerkonto"
proxyAccountDescription: "Ein Proxy-Benutzerkonto ist ein Benutzerkonto, das sich für Nutzer unter bestimmten Konditionen wie ein Follower aus einer fremden Instanz verhält. Zum Beispiel wird die Aktivität eines Nutzers aus einer fremden Instanz nicht an diese Instanz übermittelt, falls es keinen Benutzer dieser Instanz gibt, der diesem Nutzer aus fremder Instanz folgt. In diesem Fall folgt stattdessen das Proxy-Benutzerkonto." proxyAccountDescription: "Ein Proxy-Benutzerkonto ist ein Benutzerkonto, das sich für Nutzer unter bestimmten Konditionen wie ein Follower aus einer fremden Instanz verhält. Zum Beispiel wird die Aktivität eines Nutzers aus einer fremden Instanz nicht an diese Instanz übermittelt, falls es keinen Benutzer dieser Instanz gibt, der diesem Nutzer aus fremder Instanz folgt. In diesem Fall folgt stattdessen das Proxy-Benutzerkonto."
host: "Host" host: "Hostname"
selectUser: "Benutzer auswählen" selectUser: "Benutzer auswählen"
recipient: "Empfänger" recipient: "Empfänger"
annotation: "Anmerkung" annotation: "Anmerkung"
@ -194,17 +194,17 @@ blockedInstancesDescription: "Gib die Hostnamen der Instanzen, welche blockiert
muteAndBlock: "Stummschaltungen und Blockierungen" muteAndBlock: "Stummschaltungen und Blockierungen"
mutedUsers: "Stummgeschaltete Benutzer" mutedUsers: "Stummgeschaltete Benutzer"
blockedUsers: "Blockierte Benutzer" blockedUsers: "Blockierte Benutzer"
noUsers: "Keine Benutzer" noUsers: "Keine Benutzer gefunden"
editProfile: "Profil bearbeiten" editProfile: "Profil bearbeiten"
noteDeleteConfirm: "Möchtest du diese Notiz wirklich löschen?" noteDeleteConfirm: "Möchtest du diese Notiz wirklich löschen?"
pinLimitExceeded: "Es können nicht noch mehr Notizen angeheftet werden" pinLimitExceeded: "Du kannst nicht noch mehr Notizen anheften."
intro: "Misskey Installation abgeschlossen! Lass uns nun ein Administratorkonto erstellen." intro: "Misskey ist installiert! Lass uns nun ein Administratorkonto einrichten."
done: "Fertig" done: "Fertig"
processing: "In Bearbeitung …" processing: "In Bearbeitung …"
preview: "Vorschau" preview: "Vorschau"
default: "Standard" default: "Standard"
noCustomEmojis: "Keine benutzerdefinierten Emojis vorhanden" noCustomEmojis: "Keine benutzerdefinierten Emojis gefunden"
noJobs: "Es gibt keine Jobs" noJobs: "Keine Jobs vorhanden"
federating: "Wird föderiert" federating: "Wird föderiert"
blocked: "Blockiert" blocked: "Blockiert"
suspended: "Gesperrt" suspended: "Gesperrt"
@ -217,10 +217,10 @@ instanceFollowers: "Follower der Instanz"
instanceUsers: "Benutzer der Instanz" instanceUsers: "Benutzer der Instanz"
changePassword: "Passwort ändern" changePassword: "Passwort ändern"
security: "Sicherheit" security: "Sicherheit"
retypedNotMatch: "Beide Eingaben stimmen nicht überein." retypedNotMatch: "Die Eingaben stimmen nicht überein."
currentPassword: "Aktuelles Passwort" currentPassword: "Aktuelles Passwort"
newPassword: "Neues Passwort" newPassword: "Neues Passwort"
newPasswordRetype: "Neues Passwort (wiederholen)" newPasswordRetype: "Neues Passwort bestätigen"
attachFile: "Datei anhängen" attachFile: "Datei anhängen"
more: "Mehr!" more: "Mehr!"
featured: "Beliebt" featured: "Beliebt"
@ -234,7 +234,7 @@ removed: "Erfolgreich gelöscht"
removeAreYouSure: "Möchtest du „{x}“ wirklich entfernen?" removeAreYouSure: "Möchtest du „{x}“ wirklich entfernen?"
deleteAreYouSure: "Möchtest du „{x}“ wirklich löschen?" deleteAreYouSure: "Möchtest du „{x}“ wirklich löschen?"
resetAreYouSure: "Wirklich zurücksetzen?" resetAreYouSure: "Wirklich zurücksetzen?"
saved: "Gespeichert" saved: "Erfolgreich gespeichert"
messaging: "Chat" messaging: "Chat"
upload: "Hochladen" upload: "Hochladen"
keepOriginalUploading: "Originalbild speichern" keepOriginalUploading: "Originalbild speichern"
@ -295,7 +295,7 @@ avatar: "Profilbild"
banner: "Banner" banner: "Banner"
nsfw: "NSFW" nsfw: "NSFW"
whenServerDisconnected: "Bei Verbindungsverlust zum Server" whenServerDisconnected: "Bei Verbindungsverlust zum Server"
disconnectedFromServer: "Verbindung zum Server wurde getrennt" disconnectedFromServer: "Die Verbindung zum Server wurde getrennt"
reload: "Aktualisieren" reload: "Aktualisieren"
doNothing: "Ignorieren" doNothing: "Ignorieren"
reloadConfirm: "Seite neu laden?" reloadConfirm: "Seite neu laden?"
@ -307,7 +307,7 @@ normal: "Normal"
instanceName: "Name der Instanz" instanceName: "Name der Instanz"
instanceDescription: "Beschreibung der Instanz" instanceDescription: "Beschreibung der Instanz"
maintainerName: "Betreiber" maintainerName: "Betreiber"
maintainerEmail: "Betreiber-E-Mail" maintainerEmail: "Betreiber-Email"
tosUrl: "URL der Nutzungsbedingungen" tosUrl: "URL der Nutzungsbedingungen"
thisYear: "Jahr" thisYear: "Jahr"
thisMonth: "Monat" thisMonth: "Monat"
@ -325,20 +325,18 @@ disablingTimelinesInfo: "Administratoren und Moderatoren haben immer Zugriff auf
registration: "Registrieren" registration: "Registrieren"
enableRegistration: "Registration neuer Benutzer erlauben" enableRegistration: "Registration neuer Benutzer erlauben"
invite: "Einladen" invite: "Einladen"
proxyRemoteFiles: "Dateien fremder Instanzen durch Proxy leiten" driveCapacityPerLocalAccount: "Drive-Kapazität pro lokalem Benutzerkonto"
proxyRemoteFilesDescription: "Wenn diese Einstellung aktiviert ist, dann werden Dateien von fremdem Instanzen, welche entweder nicht lokal gespeichert sind oder durch Überschreiten des Speicherlimits gelöscht wurden, durch einen Proxy geleitet. Hierbei wird auch ein Vorschaubild generiert. \n Dies hat keinen Effekt auf den Speicherplatz des Servers."
driveCapacityPerLocalAccount: "Drive-Kapazität pro lokales Benutzerkonto"
driveCapacityPerRemoteAccount: "Drive-Kapazität pro Benutzer fremder Instanzen" driveCapacityPerRemoteAccount: "Drive-Kapazität pro Benutzer fremder Instanzen"
inMb: "In Megabytes" inMb: "In Megabytes"
iconUrl: "Icon-URL (favicon etc)" iconUrl: "Icon-URL (favicon etc)"
bannerUrl: "Banner-URL" bannerUrl: "Banner-URL"
backgroundImageUrl: "Hintergrundbild-URL" backgroundImageUrl: "Hintergrundbild-URL"
basicInfo: "Basisdaten" basicInfo: "Grundlegende Informationen"
pinnedUsers: "Angeheftete Benutzer" pinnedUsers: "Angeheftete Benutzer"
pinnedUsersDescription: "Gib durch Leerzeichen getrennte Benutzer an, die an die \"Erkunden\"-Seite angeheftet werden sollen." pinnedUsersDescription: "Gib durch Leerzeichen getrennte Benutzer an, die an die \"Erkunden\"-Seite angeheftet werden sollen."
pinnedPages: "Angeheftete Seiten" pinnedPages: "Angeheftete Seiten"
pinnedPagesDescription: "Gib durch Leerzeilen getrennte Pfäde zu Seiten an, die du an die Startseite dieser Instanz anheften möchtest.\n" pinnedPagesDescription: "Gib durch Leerzeilen getrennte Pfäde zu Seiten an, die an die Startseite dieser Instanz angeheftet werden sollen.\n"
pinnedClipId: "ID des angehefteten Clips" pinnedClipId: "ID des anzuheftenden Clips"
pinnedNotes: "Angeheftete Notizen" pinnedNotes: "Angeheftete Notizen"
hcaptcha: "hCaptcha" hcaptcha: "hCaptcha"
enableHcaptcha: "hCaptcha aktivieren" enableHcaptcha: "hCaptcha aktivieren"
@ -348,14 +346,14 @@ recaptcha: "reCAPTCHA"
enableRecaptcha: "reCAPTCHA aktivieren" enableRecaptcha: "reCAPTCHA aktivieren"
recaptchaSiteKey: "Site key" recaptchaSiteKey: "Site key"
recaptchaSecretKey: "Secret key" recaptchaSecretKey: "Secret key"
avoidMultiCaptchaConfirm: "Das Verwenden von mehreren Captcha-Systemen kann zu Störungen führen. Möchtest du die anderen Systeme deaktivieren? Du kannst mehrere Systeme aktiviert lassen, in dem du auf Abbrechen drückst." avoidMultiCaptchaConfirm: "Das Verwenden von mehreren Captcha-Systemen kann zu Störungen führen. Sollen die anderen Systeme deaktiviert werden? Durch Abbrechen können mehrere Systeme aktiviert bleiben."
antennas: "Antennen" antennas: "Antennen"
manageAntennas: "Antennen verwalten" manageAntennas: "Antennen verwalten"
name: "Name" name: "Name"
antennaSource: "Antennenquelle" antennaSource: "Antennenquelle"
antennaKeywords: "Zu beobachtende Schlüsselwörter" antennaKeywords: "Zu beobachtende Schlüsselwörter"
antennaExcludeKeywords: "Zu ignorierende Schlüsselwörter" antennaExcludeKeywords: "Zu ignorierende Schlüsselwörter"
antennaKeywordsDescription: "Mit Leerzeichen für eine \"UND\"-Verknüpfung trennen, durch Zeilenumbrüche für eine \"ODER\"-Verknüpfung trennen" antennaKeywordsDescription: "Zum Nutzen einer \"UND\"-Verknüpfung Einträge mit Leerzeichen trennen, zum Nutzen einer \"ODER\"-Verknüpfung Einträge mit einem Zeilenumbruch trennen"
notifyAntenna: "Über neue Notizen benachrichtigen" notifyAntenna: "Über neue Notizen benachrichtigen"
withFileAntenna: "Nur Notizen mit Dateien" withFileAntenna: "Nur Notizen mit Dateien"
enableServiceworker: "ServiceWorker aktivieren" enableServiceworker: "ServiceWorker aktivieren"
@ -376,14 +374,14 @@ recentlyDiscoveredUsers: "Vor kurzem gefundene Benutzer"
exploreUsersCount: "Es gibt {count} Benutzer" exploreUsersCount: "Es gibt {count} Benutzer"
exploreFediverse: "Das Fediverse erkunden" exploreFediverse: "Das Fediverse erkunden"
popularTags: "Beliebte Schlagwörter" popularTags: "Beliebte Schlagwörter"
userList: "Listen" userList: "Liste"
about: "Über" about: "Über"
aboutMisskey: "Über Misskey" aboutMisskey: "Über Misskey"
administrator: "Administrator" administrator: "Administrator"
token: "Token" token: "Token"
twoStepAuthentication: "Zwei-Faktor-Authentifizierung" twoStepAuthentication: "Zwei-Faktor-Authentifizierung"
moderator: "Moderator" moderator: "Moderator"
nUsersMentioned: "{n} Benutzer reden darüber" nUsersMentioned: "Von {n} Benutzern erwähnt"
securityKey: "Sicherheitsschlüssel" securityKey: "Sicherheitsschlüssel"
securityKeyName: "Schlüsselname" securityKeyName: "Schlüsselname"
registerSecurityKey: "Sicherheitsschlüssel registrieren" registerSecurityKey: "Sicherheitsschlüssel registrieren"
@ -391,7 +389,7 @@ lastUsed: "Zuletzt benutzt"
unregister: "Deaktivieren" unregister: "Deaktivieren"
passwordLessLogin: "Passwortloses Anmelden einrichten" passwordLessLogin: "Passwortloses Anmelden einrichten"
resetPassword: "Passwort zurücksetzen" resetPassword: "Passwort zurücksetzen"
newPasswordIs: "Das neue Passwort ist \"{password}\"" newPasswordIs: "Das neue Passwort ist „{password}“"
reduceUiAnimation: "Animationen der Benutzeroberfläche reduzieren" reduceUiAnimation: "Animationen der Benutzeroberfläche reduzieren"
share: "Teilen" share: "Teilen"
notFound: "Nicht gefunden" notFound: "Nicht gefunden"
@ -407,7 +405,7 @@ close: "Schließen"
group: "Gruppe" group: "Gruppe"
groups: "Gruppen" groups: "Gruppen"
createGroup: "Gruppe erstellen" createGroup: "Gruppe erstellen"
ownedGroups: "Eigene Gruppen" ownedGroups: "Meine Gruppen"
joinedGroups: "Beigetretene Gruppen" joinedGroups: "Beigetretene Gruppen"
invites: "Einladungen" invites: "Einladungen"
groupName: "Gruppenname" groupName: "Gruppenname"
@ -415,30 +413,29 @@ members: "Mitglieder"
transfer: "Übertragen" transfer: "Übertragen"
messagingWithUser: "Privatchat" messagingWithUser: "Privatchat"
messagingWithGroup: "Gruppenchat" messagingWithGroup: "Gruppenchat"
title: "Betreff" title: "Titel"
text: "Text" text: "Text"
enable: "Aktivieren" enable: "Aktivieren"
next: "Weiter" next: "Weiter"
retype: "Erneut eingeben" retype: "Erneut eingeben"
noteOf: "Notiz von {user}" noteOf: "Notiz von {user}"
inviteToGroup: "Zu Gruppe einladen" inviteToGroup: "Zu Gruppe einladen"
maxNoteTextLength: "Maximale Länge von Notizen" quoteAttached: "Zitat"
quoteAttached: "Zitiert" quoteQuestion: "Als Zitat anhängen?"
quoteQuestion: "Als Zitat anfügen?" noMessagesYet: "Noch keine Nachrichten vorhanden"
noMessagesYet: "Noch keine Nachrichten"
newMessageExists: "Du hast eine neue Nachricht" newMessageExists: "Du hast eine neue Nachricht"
onlyOneFileCanBeAttached: "Es kann pro Nachricht nur eine Datei angehängt werden" onlyOneFileCanBeAttached: "Es kann pro Nachricht nur eine Datei angehängt werden"
signinRequired: "Anmeldung erforderlich" signinRequired: "Bitte melde dich an"
invitations: "Einladungen" invitations: "Einladungen"
invitationCode: "Einladungscode" invitationCode: "Einladungscode"
checking: "Wird überprüft..." checking: "Wird überprüft"
available: "Verfügbar" available: "Verfügbar"
unavailable: "Unverfügbar" unavailable: "Unverfügbar"
usernameInvalidFormat: "Klein- und Großbuchstaben, Zahlen sowie Unterstriche sind verwendbar." usernameInvalidFormat: "Du kannst Klein- und Großbuchstaben, Zahlen sowie Unterstriche verwenden"
tooShort: "Zu kurz" tooShort: "Zu kurz"
tooLong: "Zu lang" tooLong: "Zu lang"
weakPassword: "Schwaches Passwort" weakPassword: "Schwaches Passwort"
normalPassword: "Normales Passwort" normalPassword: "Durchschnittliches Passwort"
strongPassword: "Starkes Passwort" strongPassword: "Starkes Passwort"
passwordMatched: "Stimmt überein" passwordMatched: "Stimmt überein"
passwordNotMatched: "Stimmt nicht überein" passwordNotMatched: "Stimmt nicht überein"
@ -454,18 +451,18 @@ useOsNativeEmojis: "Eingebaute Emojis des Betriebssystems benutzen"
disableDrawer: "Keine ausfahrbaren Menüs verwenden" disableDrawer: "Keine ausfahrbaren Menüs verwenden"
youHaveNoGroups: "Keine Gruppen vorhanden" youHaveNoGroups: "Keine Gruppen vorhanden"
joinOrCreateGroup: "Lass dich zu einer Gruppe einladen oder erstelle deine eigene." joinOrCreateGroup: "Lass dich zu einer Gruppe einladen oder erstelle deine eigene."
noHistory: "Kein Verlauf" noHistory: "Kein Verlauf gefunden"
signinHistory: "Anmeldungsverlauf" signinHistory: "Anmeldungsverlauf"
disableAnimatedMfm: "MFM, die Animationen enthalten, deaktivieren" disableAnimatedMfm: "MFM, die Animationen enthalten, deaktivieren"
doing: "In Bearbeitung..." doing: "In Bearbeitung"
category: "Kategorie" category: "Kategorie"
tags: "Schlagwörter" tags: "Schlagwörter"
docSource: "Quelle dieses Dokuments" docSource: "Quellcode dieses Dokuments"
createAccount: "Benutzerkonto erstellen" createAccount: "Benutzerkonto erstellen"
existingAccount: "Bestehendes Benutzerkonto" existingAccount: "Bestehendes Benutzerkonto"
regenerate: "Regenerieren" regenerate: "Regenerieren"
fontSize: "Schriftgröße" fontSize: "Schriftgröße"
noFollowRequests: "Du hast keine ausstehende Follow-Anfragen" noFollowRequests: "Keine ausstehenden Follow-Anfragen vorhanden"
openImageInNewTab: "Bilder in neuem Tab öffnen" openImageInNewTab: "Bilder in neuem Tab öffnen"
dashboard: "Dashboard" dashboard: "Dashboard"
local: "Lokal" local: "Lokal"
@ -476,25 +473,25 @@ dayOverDayChanges: "Veränderung zu Gestern"
appearance: "Aussehen" appearance: "Aussehen"
clientSettings: "Client-Einstellungen" clientSettings: "Client-Einstellungen"
accountSettings: "Benutzerkonto-Einstellungen" accountSettings: "Benutzerkonto-Einstellungen"
promotion: "Hervorgehoben" promotion: "Werbung"
promote: "Hervorheben" promote: "Werbung schalten"
numberOfDays: "Anzahl der Tage" numberOfDays: "Anzahl der Tage"
hideThisNote: "Diese Notiz verstecken" hideThisNote: "Diese Notiz verstecken"
showFeaturedNotesInTimeline: "Beliebte Notizen in Chronik anzeigen" showFeaturedNotesInTimeline: "Beliebte Notizen in der Chronik anzeigen"
objectStorage: "Objektspeicher" objectStorage: "Objektspeicher"
useObjectStorage: "Objektspeicher verwenden" useObjectStorage: "Objektspeicher verwenden"
objectStorageBaseUrl: "Basis-URL" objectStorageBaseUrl: "Basis-URL"
objectStorageBaseUrlDesc: "Als Referenz verwendete URL. Verwendest du einen CDN oder Proxy, gib dessen URL an. S3: 'https://<bucket>.s3.amazonaws.com', GCS: 'https://storage.googleapis.com/<bucket>' etc." objectStorageBaseUrlDesc: "Die als Referenz verwendete URL. Verwendest du einen CDN oder Proxy, gib dessen URL an. Für S3 verwende 'https://<bucket>.s3.amazonaws.com'. Für GCS o.ä. verwende 'https://storage.googleapis.com/<bucket>'."
objectStorageBucket: "Bucket" objectStorageBucket: "Bucket"
objectStorageBucketDesc: "Bitte gib den Bucket-Namen an, der bei deinem Anbieter verwendet wird." objectStorageBucketDesc: "Bitte gib den Namen des Buckets an, der bei deinem Anbieter verwendet wird."
objectStoragePrefix: "Prefix" objectStoragePrefix: "Prefix"
objectStoragePrefixDesc: "Dateien werden in Ordnern unter diesem Prefix gespeichert." objectStoragePrefixDesc: "Dateien werden in Ordnern unter diesem Prefix gespeichert."
objectStorageEndpoint: "Endpoint" objectStorageEndpoint: "Endpoint"
objectStorageEndpointDesc: "Im Falle von S3 leerlassen, für andere Anbieter den relevanten Endpoint im Format \"<host>\" oder \"<host>:<port>\" angeben." objectStorageEndpointDesc: "Im Falle von S3 leerlassen, für andere Anbieter den relevanten Endpoint im Format „<host>“ oder „<host>:<port>“ angeben."
objectStorageRegion: "Region" objectStorageRegion: "Region"
objectStorageRegionDesc: "Gib eine Region wie z.B. \"xx-east-1\" an. Falls dein Anbieter nicht zwischen Regionen unterscheidet, lass dieses Feld leer oder gib \"us-east-1\" an." objectStorageRegionDesc: "Gib eine Region wie z.B. „xx-east-1“ an. Falls dein Anbieter nicht zwischen Regionen unterscheidet, lass dieses Feld leer oder gib „us-east-1“ an."
objectStorageUseSSL: "SSL verwenden" objectStorageUseSSL: "SSL verwenden"
objectStorageUseSSLDesc: "Deaktiviere dies, falls du für die API-Verbindungen kein HTTPS verwenden wirst" objectStorageUseSSLDesc: "Deaktiviere dies, falls du für API-Verbindungen kein HTTPS verwenden wirst"
objectStorageUseProxy: "Über Proxy verbinden" objectStorageUseProxy: "Über Proxy verbinden"
objectStorageUseProxyDesc: "Deaktiviere dies, falls du keinen Proxy für den Objektspeicher verwenden wirst" objectStorageUseProxyDesc: "Deaktiviere dies, falls du keinen Proxy für den Objektspeicher verwenden wirst"
objectStorageSetPublicRead: "Bei Upload auf \"public-read\" stellen" objectStorageSetPublicRead: "Bei Upload auf \"public-read\" stellen"
@ -505,7 +502,7 @@ newNoteRecived: "Es gibt neue Notizen"
sounds: "Töne" sounds: "Töne"
listen: "Anhören" listen: "Anhören"
none: "Nichts" none: "Nichts"
showInPage: "In Seite anzeigen" showInPage: "In einer Seite anzeigen"
popout: "Pop-Up" popout: "Pop-Up"
volume: "Lautstärke" volume: "Lautstärke"
masterVolume: "Gesamtlautstärke" masterVolume: "Gesamtlautstärke"
@ -524,7 +521,7 @@ sort: "Sortieren"
ascendingOrder: "Aufsteigende Reihenfolge" ascendingOrder: "Aufsteigende Reihenfolge"
descendingOrder: "Absteigende Reihenfolge" descendingOrder: "Absteigende Reihenfolge"
scratchpad: "Testumgebung" scratchpad: "Testumgebung"
scratchpadDescription: "Die Testumgebung bietet eine Umgebung für AiScript-Experimente. Dort kannst du AiScript schreiben, ausführen sowie dessen Auswirkungen auf Misskey überprüfen." scratchpadDescription: "Die Testumgebung bietet einen Bereich für AiScript-Experimente. Dort kannst du AiScript schreiben, ausführen sowie dessen Auswirkungen auf Misskey überprüfen."
output: "Ausgabe" output: "Ausgabe"
script: "Skript" script: "Skript"
disablePagesScript: "AiScript auf Seiten deaktivieren" disablePagesScript: "AiScript auf Seiten deaktivieren"
@ -547,14 +544,14 @@ addedRelays: "Hinzugefügte Relays"
serviceworkerInfo: "Muss für Push-Benachrichtigungen aktiviert sein." serviceworkerInfo: "Muss für Push-Benachrichtigungen aktiviert sein."
deletedNote: "Gelöschte Notiz" deletedNote: "Gelöschte Notiz"
invisibleNote: "Private Notiz" invisibleNote: "Private Notiz"
enableInfiniteScroll: "Automatisch mehr Notizen laden" enableInfiniteScroll: "Automatisch mehr laden"
visibility: "Sichtbarkeit" visibility: "Sichtbarkeit"
poll: "Umfrage" poll: "Umfrage"
useCw: "Inhaltswarnung verwenden" useCw: "Inhaltswarnung verwenden"
enablePlayer: "Video-Player öffnen" enablePlayer: "Video-Player öffnen"
disablePlayer: "Video-Player schließen" disablePlayer: "Video-Player schließen"
expandTweet: "Tweet ausklappen" expandTweet: "Tweet ausklappen"
themeEditor: "Farbthemen-Editor" themeEditor: "Farbschema-Editor"
description: "Beschreibung" description: "Beschreibung"
describeFile: "Beschreibung hinzufügen" describeFile: "Beschreibung hinzufügen"
enterFileDescription: "Beschreibung eingeben" enterFileDescription: "Beschreibung eingeben"
@ -590,13 +587,13 @@ smtpHost: "Host"
smtpPort: "Port" smtpPort: "Port"
smtpUser: "Benutzername" smtpUser: "Benutzername"
smtpPass: "Passwort" smtpPass: "Passwort"
emptyToDisableSmtpAuth: "Benutzername und Passwort leer lassen um SMTP-Verifizierung zu deaktivieren" emptyToDisableSmtpAuth: "Benutzername und Passwort leer lassen, um SMTP-Verifizierung zu deaktivieren"
smtpSecure: "Für SMTP-Verbindungen implizit SSL/TLS verwenden" smtpSecure: "Für SMTP-Verbindungen implizit SSL/TLS verwenden"
smtpSecureInfo: "Schalte dies aus, falls du STARTTLS verwendest" smtpSecureInfo: "Schalte dies aus, falls du STARTTLS verwendest."
testEmail: "Email-Versand testen" testEmail: "Emailversand testen"
wordMute: "Wort-Stummschaltung" wordMute: "Wortstummschaltung"
regexpError: "Regular Expression error" regexpError: "Fehler in einem regulären Ausdruck"
regexpErrorDescription: "Error in the regular expression on line {line} in your {tab} word mutes:" regexpErrorDescription: "Im regulären Ausdruck deiner {tab}en Wortstummschaltungen ist ein Fehler aufgetreten:"
instanceMute: "Instanzstummschaltungen" instanceMute: "Instanzstummschaltungen"
userSaysSomething: "{name} hat etwas gesagt" userSaysSomething: "{name} hat etwas gesagt"
makeActive: "Aktivieren" makeActive: "Aktivieren"
@ -610,11 +607,11 @@ database: "Datenbank"
channel: "Kanäle" channel: "Kanäle"
create: "Erstellen" create: "Erstellen"
notificationSetting: "Benachrichtigungseinstellungen" notificationSetting: "Benachrichtigungseinstellungen"
notificationSettingDesc: "Wähle die Art der anzuzeigenden Benachrichtigung" notificationSettingDesc: "Wähle die Art der anzuzeigenden Benachrichtigungen."
useGlobalSetting: "Globale Einstellung verwenden" useGlobalSetting: "Globale Einstellung verwenden"
useGlobalSettingDesc: "Ist dies eingeschaltet, so werden die Benachrichtigungseinstellungen deines Benutzerkontos verwendet. Wenn dies ausgeschaltet ist, können individuelle Einstellungen vorgenommen werden." useGlobalSettingDesc: "Ist diese Option aktiviert, werden die Benachrichtigungseinstellungen deines Benutzerkontos verwendet. Durch ausschalten dieser Option können individuelle Einstellungen vorgenommen werden."
other: "Andere" other: "Anderes"
regenerateLoginToken: "Anmeldungstoken regenerieren" regenerateLoginToken: "Anmeldetoken regenerieren"
regenerateLoginTokenDescription: "Den zur Anmeldung intern verwendeten Token regenerieren. Normalerweise wird dies nicht benötigt. Bei Regeneration werden alle Geräte ausgeloggt." regenerateLoginTokenDescription: "Den zur Anmeldung intern verwendeten Token regenerieren. Normalerweise wird dies nicht benötigt. Bei Regeneration werden alle Geräte ausgeloggt."
setMultipleBySeparatingWithSpace: "Trenne Elemente durch ein Leerzeichen um mehrere Einstellungen zu kofigurieren." setMultipleBySeparatingWithSpace: "Trenne Elemente durch ein Leerzeichen um mehrere Einstellungen zu kofigurieren."
fileIdOrUrl: "Datei-ID oder URL" fileIdOrUrl: "Datei-ID oder URL"
@ -624,7 +621,7 @@ abuseReports: "Meldungen"
reportAbuse: "Melden" reportAbuse: "Melden"
reportAbuseOf: "{name} melden" reportAbuseOf: "{name} melden"
fillAbuseReportDescription: "Bitte gib zusätzliche Informationen zu dieser Meldung an. Falls es sich um eine spezielle Notiz handelt, bitte gib dessen URL an." fillAbuseReportDescription: "Bitte gib zusätzliche Informationen zu dieser Meldung an. Falls es sich um eine spezielle Notiz handelt, bitte gib dessen URL an."
abuseReported: "Die Meldung wurde versendet. Vielen Dank." abuseReported: "Deine Meldung wurde versendet. Vielen Dank."
reporter: "Melder" reporter: "Melder"
reporteeOrigin: "Herkunft des Gemeldeten" reporteeOrigin: "Herkunft des Gemeldeten"
reporterOrigin: "Herkunft des Meldenden" reporterOrigin: "Herkunft des Meldenden"
@ -637,18 +634,18 @@ openInSideView: "In Seitenansicht öffnen"
defaultNavigationBehaviour: "Standardnavigationsverhalten" defaultNavigationBehaviour: "Standardnavigationsverhalten"
editTheseSettingsMayBreakAccount: "Bei Bearbeitung dieser Einstellungen besteht die Gefahr, dein Benutzerkonto zu beschädigen." editTheseSettingsMayBreakAccount: "Bei Bearbeitung dieser Einstellungen besteht die Gefahr, dein Benutzerkonto zu beschädigen."
instanceTicker: "Instanz-Informationen von Notizen" instanceTicker: "Instanz-Informationen von Notizen"
waitingFor: "Warte auf {x}" waitingFor: "Warte auf {x}"
random: "Zufällig" random: "Zufällig"
system: "System" system: "System"
switchUi: "UI wechseln" switchUi: "UI wechseln"
desktop: "Desktop" desktop: "Desktop"
clip: "Clip" clip: "Clip erstellen"
createNew: "Neu erstellen" createNew: "Neu erstellen"
optional: "Optional" optional: "Optional"
createNewClip: "Neuen Clip erstellen" createNewClip: "Neuen Clip erstellen"
public: "Öffentlich" public: "Öffentlich"
i18nInfo: "Misskey wird durch freiwillige Helfer in viele verschiedene Sprachen übersetzt. Auf {link} kannst du mithelfen." i18nInfo: "Misskey wird durch freiwillige Helfer in viele verschiedene Sprachen übersetzt. Auf {link} kannst du mithelfen."
manageAccessTokens: "Zugriffstoken verwalten" manageAccessTokens: "Zugriffstokens verwalten"
accountInfo: "Benutzerkonto-Informationen" accountInfo: "Benutzerkonto-Informationen"
notesCount: "Anzahl der Notizen" notesCount: "Anzahl der Notizen"
repliesCount: "Anzahl gesendeter Antworten" repliesCount: "Anzahl gesendeter Antworten"
@ -663,15 +660,15 @@ pollVotesCount: "Anzahl gesendeter Antworten auf Umfragen"
pollVotedCount: "Anzahl erhaltener Antworten auf Umfragen" pollVotedCount: "Anzahl erhaltener Antworten auf Umfragen"
yes: "Ja" yes: "Ja"
no: "Nein" no: "Nein"
driveFilesCount: "Anzahl an Drive-Dateien" driveFilesCount: "Anzahl der Dateien in Drive"
driveUsage: "Drive-Auslastung" driveUsage: "Drive-Auslastung"
noCrawle: "Crawler-Indexierung ablehnen" noCrawle: "Crawler-Indexierung ablehnen"
noCrawleDescription: "Suchmaschinen bitten, die eigene Profilseite, Notizen, Seiten usw. nicht zu indexieren" noCrawleDescription: "Suchmaschinen bitten, die eigene Profilseite, Notizen, Seiten usw. nicht zu indexieren."
lockedAccountInfo: "Auch wenn du Follow-Anfragen auf manuelle Bestätigung setzt, wird jeder deine Notizen öffentlich sehen können, sofern du die Notizsichtbarkeit nicht auf \"Nur Follower\" setzt." lockedAccountInfo: "Auch wenn du Follow-Anfragen auf manuelle Bestätigung setzt, wird jede deiner Notizen öffentlich sichtbar sein, sofern du ihre Notizsichtbarkeit nicht auf \"Nur Follower\" setzt."
alwaysMarkSensitive: "Medien standardmäßig als NSFW markieren" alwaysMarkSensitive: "Medien standardmäßig als NSFW markieren"
loadRawImages: "Anstatt Vorschaubilder immer Originalbilder anzeigen" loadRawImages: "Anstatt Vorschaubilder immer Originalbilder anzeigen"
disableShowingAnimatedImages: "Animierte Bilder nicht abspielen" disableShowingAnimatedImages: "Animierte Bilder nicht abspielen"
verificationEmailSent: "Eine Verifizierungsnachricht wurde versendet. Besuche den dort enthaltenen Link, um die Verifizierung abzuschließen." verificationEmailSent: "Eine Bestätigungsmail wurde an deine Email-Adresse versendet. Besuche den dort enthaltenen Link, um die Verifizierung abzuschließen."
notSet: "Nicht konfiguriert" notSet: "Nicht konfiguriert"
emailVerified: "Email-Adresse bestätigt" emailVerified: "Email-Adresse bestätigt"
noteFavoritesCount: "Anzahl an als Favorit markierter Notizen" noteFavoritesCount: "Anzahl an als Favorit markierter Notizen"
@ -682,12 +679,12 @@ useSystemFont: "Standardschriftart des Systems verwenden"
clips: "Clips" clips: "Clips"
experimentalFeatures: "Experimentelle Funktionalitäten" experimentalFeatures: "Experimentelle Funktionalitäten"
developer: "Entwickler" developer: "Entwickler"
makeExplorable: "Benutzerkonto in \"Erkunden\" sichtbar machen" makeExplorable: "Benutzerkonto in „Erkunden“ sichtbar machen"
makeExplorableDescription: "Wenn diese Option deaktiviert ist, ist dein Benutzerkonto nicht im \"Erkunden\"-Bereich sichtbar." makeExplorableDescription: "Wenn diese Option deaktiviert ist, ist dein Benutzerkonto nicht im „Erkunden“-Bereich sichtbar."
showGapBetweenNotesInTimeline: "Abstände zwischen Notizen auf der Chronik anzeigen" showGapBetweenNotesInTimeline: "Abstände zwischen Notizen auf der Chronik anzeigen"
duplicate: "Duplizieren" duplicate: "Duplizieren"
left: "Links" left: "Links"
center: "Mitte" center: "Mittig"
wide: "Breit" wide: "Breit"
narrow: "Schmal" narrow: "Schmal"
reloadToApplySetting: "Diese Einstellung tritt nach einer Aktualisierung der Seite in Kraft. Jetzt aktualisieren?" reloadToApplySetting: "Diese Einstellung tritt nach einer Aktualisierung der Seite in Kraft. Jetzt aktualisieren?"
@ -698,19 +695,19 @@ onlineUsersCount: "{n} Benutzer sind online"
nUsers: "{n} Benutzer" nUsers: "{n} Benutzer"
nNotes: "{n} Notizen" nNotes: "{n} Notizen"
sendErrorReports: "Fehlerberichte senden" sendErrorReports: "Fehlerberichte senden"
sendErrorReportsDescription: "Ist diese Option aktiviert, so werden beim Auftreten von Fehlern detaillierte Fehlerinformationen an Misskey weitergegeben, was zur Verbesserung der Qualität von Misskey beiträgt.\nEnthalten in diesen Informationen sind u.a. die Version deines Betriebssystems, welchen Browser du verwendest und ein Verlauf deiner Aktivitäten." sendErrorReportsDescription: "Ist diese Option aktiviert, so werden beim Auftreten von Fehlern detaillierte Fehlerinformationen an Misskey weitergegeben, was zur Verbesserung der Qualität von Misskey beiträgt.\nEnthalten in diesen Informationen sind u.a. die Version deines Betriebssystems, welchen Browser du verwendest und ein Verlauf deiner Aktivitäten innerhalb Misskey."
myTheme: "Mein Farbthema" myTheme: "Mein Farbschema"
backgroundColor: "Hintergrundfarbe" backgroundColor: "Hintergrundfarbe"
accentColor: "Akzentfarbe" accentColor: "Akzentfarbe"
textColor: "Textfarbe" textColor: "Textfarbe"
saveAs: "Speichern als…" saveAs: "Speichern als …"
advanced: "Fortgeschritten" advanced: "Fortgeschritten"
value: "Wert" value: "Wert"
createdAt: "Erstellt am" createdAt: "Erstellt am"
updatedAt: "Zuletzt geändert am" updatedAt: "Zuletzt geändert am"
saveConfirm: "Änderungen speichern?" saveConfirm: "Änderungen speichern?"
deleteConfirm: "Wirklich löschen?" deleteConfirm: "Wirklich löschen?"
invalidValue: "Ungültiger Wert." invalidValue: "Dieser Wert ist ungültig."
registry: "Registry" registry: "Registry"
closeAccount: "Benutzerkonto schließen" closeAccount: "Benutzerkonto schließen"
currentVersion: "Momentane Version" currentVersion: "Momentane Version"
@ -723,11 +720,11 @@ inUse: "Verwendet"
editCode: "Code bearbeiten" editCode: "Code bearbeiten"
apply: "Anwenden" apply: "Anwenden"
receiveAnnouncementFromInstance: "Benachrichtigungen von dieser Instanz empfangen" receiveAnnouncementFromInstance: "Benachrichtigungen von dieser Instanz empfangen"
emailNotification: "E-Mail-Benachrichtigungen" emailNotification: "Email-Benachrichtigungen"
publish: "Veröffentlichen" publish: "Veröffentlichen"
inChannelSearch: "In Kanal suchen" inChannelSearch: "In Kanal suchen"
useReactionPickerForContextMenu: "Reaktionsauswahl durch Rechtsklick öffnen" useReactionPickerForContextMenu: "Reaktionsauswahl durch Rechtsklick öffnen"
typingUsers: "{users} ist/sind am schreiben..." typingUsers: "{users} ist/sind am schreiben"
jumpToSpecifiedDate: "Zu bestimmtem Datum springen" jumpToSpecifiedDate: "Zu bestimmtem Datum springen"
showingPastTimeline: "Es wird eine alte Chronik angezeigt" showingPastTimeline: "Es wird eine alte Chronik angezeigt"
clear: "Zurückkehren" clear: "Zurückkehren"
@ -737,19 +734,19 @@ unlikeConfirm: "\"Gefällt mir\" wirklich entfernen?"
fullView: "Vollansicht" fullView: "Vollansicht"
quitFullView: "Vollansicht verlassen" quitFullView: "Vollansicht verlassen"
addDescription: "Beschreibung hinzufügen" addDescription: "Beschreibung hinzufügen"
userPagePinTip: "Um Notizen hier erscheinen zu lassen, drücke \"Anheften\" im Menü individueller Notizen." userPagePinTip: "Um Notizen hier erscheinen zu lassen, drücke \"An dein Profil anheften\" im Menü individueller Notizen."
notSpecifiedMentionWarning: "Diese Notiz enthält Erwähnungen von Nutzern, die nicht als Empfänger ausgewählt sind" notSpecifiedMentionWarning: "Diese Notiz enthält Erwähnungen von Nutzern, die nicht als Empfänger ausgewählt sind"
info: "Über" info: "Über"
userInfo: "Benutzerinformation" userInfo: "Benutzerinformation"
unknown: "Unbekannt" unknown: "Unbekannt"
onlineStatus: "Online-Status" onlineStatus: "Onlinestatus"
hideOnlineStatus: "Online-Status verbergen" hideOnlineStatus: "Onlinestatus verbergen"
hideOnlineStatusDescription: "Das Verbergen deines Online-Statuses reduziert die Nützlichkeit von Funktionen wie der Suche." hideOnlineStatusDescription: "Das Verbergen deines Onlinestatuses reduziert die Nützlichkeit von Funktionen wie der Suche."
online: "Online" online: "Online"
active: "Aktiv" active: "Aktiv"
offline: "Offline" offline: "Offline"
notRecommended: "Nicht empfohlen" notRecommended: "Nicht empfohlen"
botProtection: "Bot-Schutz" botProtection: "Schutz vor Bots"
instanceBlocking: "Blockierte Instanzen" instanceBlocking: "Blockierte Instanzen"
selectAccount: "Benutzerkonto auswählen" selectAccount: "Benutzerkonto auswählen"
switchAccount: "Konto wechseln" switchAccount: "Konto wechseln"
@ -761,9 +758,9 @@ administration: "Verwaltung"
accounts: "Benutzerkonten" accounts: "Benutzerkonten"
switch: "Wechseln" switch: "Wechseln"
noMaintainerInformationWarning: "Betreiberinformationen sind nicht konfiguriert." noMaintainerInformationWarning: "Betreiberinformationen sind nicht konfiguriert."
noBotProtectionWarning: "Bot-Schutz ist nicht konfiguriert." noBotProtectionWarning: "Schutz vor Bots ist nicht konfiguriert."
configure: "Konfigurieren" configure: "Konfigurieren"
postToGallery: "Neuen Galerie-Beitrag erstellen" postToGallery: "Neuen Galeriebeitrag erstellen"
gallery: "Galerie" gallery: "Galerie"
recentPosts: "Neue Beiträge" recentPosts: "Neue Beiträge"
popularPosts: "Beliebte Beiträge" popularPosts: "Beliebte Beiträge"
@ -775,7 +772,7 @@ priority: "Priorität"
high: "Hoch" high: "Hoch"
middle: "Mittel" middle: "Mittel"
low: "Niedrig" low: "Niedrig"
emailNotConfiguredWarning: "Keine Email-Adresse hinterlegt" emailNotConfiguredWarning: "Keine Email-Adresse hinterlegt."
ratio: "Verhältnis" ratio: "Verhältnis"
previewNoteText: "Vorschau anzeigen" previewNoteText: "Vorschau anzeigen"
customCss: "Benutzerdefiniertes CSS" customCss: "Benutzerdefiniertes CSS"
@ -793,9 +790,9 @@ misskeyUpdated: "Misskey wurde aktualisiert!"
whatIsNew: "Änderungen anzeigen" whatIsNew: "Änderungen anzeigen"
translate: "Übersetzen" translate: "Übersetzen"
translatedFrom: "Aus {x} übersetzt" translatedFrom: "Aus {x} übersetzt"
accountDeletionInProgress: "Löschung des Benutzerkontos momentan in Bearbeitung" accountDeletionInProgress: "Die Löschung deines Benutzerkontos ist momentan in Bearbeitung."
usernameInfo: "Ein Name, durch den dein Benutzerkonto auf diesem Server identifiziert werden kann. Du kannst das Alphabet (a~z, A~Z), Ziffern (0~9) oder Unterstriche (_) verwenden. Benutzernamen können später nicht geändert werden." usernameInfo: "Ein Name, durch den dein Benutzerkonto auf diesem Server identifiziert werden kann. Du kannst das Alphabet (a~z, A~Z), Ziffern (0~9) oder Unterstriche (_) verwenden. Benutzernamen können später nicht geändert werden."
aiChanMode: "Ai Modus" aiChanMode: "Ai-Modus"
keepCw: "Inhaltswarnungen beibehalten" keepCw: "Inhaltswarnungen beibehalten"
pubSub: "Pub/Sub Benutzerkonten" pubSub: "Pub/Sub Benutzerkonten"
lastCommunication: "Letzte Kommunikation" lastCommunication: "Letzte Kommunikation"
@ -804,7 +801,7 @@ unresolved: "Ungelöst"
breakFollow: "Follower entfernen" breakFollow: "Follower entfernen"
itsOn: "Eingeschaltet" itsOn: "Eingeschaltet"
itsOff: "Ausgeschaltet" itsOff: "Ausgeschaltet"
emailRequiredForSignup: "Angaben einer Email-Adresse als benötigt markieren" emailRequiredForSignup: "Angabe einer Email-Adresse als benötigt markieren"
unread: "Ungelesen" unread: "Ungelesen"
filter: "Filter" filter: "Filter"
controlPanel: "Systemsteuerung" controlPanel: "Systemsteuerung"
@ -819,10 +816,10 @@ ffVisibilityDescription: "Konfiguriere wer sehen kann, wem du folgst sowie wer d
continueThread: "Weiteren Threadverlauf anzeigen" continueThread: "Weiteren Threadverlauf anzeigen"
deleteAccountConfirm: "Dein Benutzerkonto wird unwiderruflich gelöscht. Trotzdem fortfahren?" deleteAccountConfirm: "Dein Benutzerkonto wird unwiderruflich gelöscht. Trotzdem fortfahren?"
incorrectPassword: "Falsches Passwort." incorrectPassword: "Falsches Passwort."
voteConfirm: "Wirklich für \"{choice}\" abstimmen?" voteConfirm: "Wirklich für „{choice}“ abstimmen?"
hide: "Inhalt verbergen" hide: "Inhalt verbergen"
leaveGroup: "Gruppe verlassen" leaveGroup: "Gruppe verlassen"
leaveGroupConfirm: "Möchtest du \"{name}\" wirklich verlassen?" leaveGroupConfirm: "Möchtest du „{name}“ wirklich verlassen?"
useDrawerReactionPickerForMobile: "Auf mobilen Geräten ausfahrbare Reaktionsauswahl anzeigen" useDrawerReactionPickerForMobile: "Auf mobilen Geräten ausfahrbare Reaktionsauswahl anzeigen"
welcomeBackWithName: "Willkommen zurück, {name}" welcomeBackWithName: "Willkommen zurück, {name}"
clickToFinishEmailVerification: "Drücke bitte auf [{ok}], um die Email-Bestätigung abzuschließen." clickToFinishEmailVerification: "Drücke bitte auf [{ok}], um die Email-Bestätigung abzuschließen."
@ -833,6 +830,16 @@ auto: "Automatisch"
themeColor: "Instanzfarbe" themeColor: "Instanzfarbe"
size: "Größe" size: "Größe"
numberOfColumn: "Spaltenanzahl" numberOfColumn: "Spaltenanzahl"
searchByGoogle: "Googlen"
instanceDefaultLightTheme: "Instanzweites Standardfarbschema (Hell)"
instanceDefaultDarkTheme: "Instanzweites Standardfarbschema (Dunkel)"
instanceDefaultThemeDescription: "Gib den Farbschemencode im Objektformat ein."
mutePeriod: "Stummschaltungsdauer"
indefinitely: "Dauerhaft"
tenMinutes: "10 Minuten"
oneHour: "Eine Stunde"
oneDay: "Einen Tag"
oneWeek: "Eine Woche"
_emailUnavailable: _emailUnavailable:
used: "Diese Email-Adresse wird bereits verwendet" used: "Diese Email-Adresse wird bereits verwendet"
format: "Das Format dieser Email-Adresse ist ungültig" format: "Das Format dieser Email-Adresse ist ungültig"
@ -845,20 +852,20 @@ _ffVisibility:
private: "Privat" private: "Privat"
_signup: _signup:
almostThere: "Fast geschafft" almostThere: "Fast geschafft"
emailAddressInfo: "Bitte gib deine Email-Adresse ein." emailAddressInfo: "Bitte gib deine Email-Adresse ein. Sie wird nicht öffentlich einsehbar sein."
emailSent: "An deine Email-Adresse ({email}) wurde soeben eine Bestätigungsmail geschickt. Bitte klicke auf den enthaltenen Link, um die Erstellung deines Benutzerkontos abzuschließen." emailSent: "An deine Email-Adresse ({email}) wurde soeben eine Bestätigungsmail geschickt. Bitte klicke auf den enthaltenen Link, um die Erstellung deines Benutzerkontos abzuschließen."
_accountDelete: _accountDelete:
accountDelete: "Benutzerkonto löschen" accountDelete: "Benutzerkonto löschen"
mayTakeTime: "Da die Löschung eines Benutzerkontos ein aufwendiger Prozess ist, kann dessen Dauer davon abhängen, wie viel Inhalt in diesem erstellt wurde oder wie viele Dateien hochgeladen wurden." mayTakeTime: "Da die Löschung eines Benutzerkontos ein aufwendiger Prozess ist, kann dessen Dauer davon abhängen, wie viel Inhalt von diesem erstellt wurde oder wie viele Dateien von diesem hochgeladen wurden."
sendEmail: "Sobald die Löschung abgeschlossen ist, wird an die mit ihm verknüpfte Email-Adresse eine Benachrichtigung versendet." sendEmail: "Sobald die Löschung abgeschlossen ist, wird an die mit ihm verknüpfte Email-Adresse eine Benachrichtigung versendet."
requestAccountDelete: "Löschung des Benutzerkontos anfordern" requestAccountDelete: "Löschung deines Benutzerkontos anfordern"
started: "Löschung wurde eingeleitet." started: "Die Löschung wurde eingeleitet."
inProgress: "Löschung in Bearbeitung" inProgress: "Löschung in Bearbeitung"
_ad: _ad:
back: "Zurück" back: "Zurück"
reduceFrequencyOfThisAd: "Diese Werbung weniger anzeigen" reduceFrequencyOfThisAd: "Diese Werbung weniger anzeigen"
_forgotPassword: _forgotPassword:
enterEmail: "Gib die Email-Adresse ein, mit der du dich registriert hast. An diese wird ein Link gesendet, mit der du dein Passwort zurücksetzen kannst." enterEmail: "Gib die Email-Adresse ein, mit der du dich registriert hast. An diese wird ein Link gesendet, mit dem du dein Passwort zurücksetzen kannst."
ifNoEmail: "Solltest du bei der Registrierung keine Email-Adresse angegeben haben, wende dich bitte an den Administrator." ifNoEmail: "Solltest du bei der Registrierung keine Email-Adresse angegeben haben, wende dich bitte an den Administrator."
contactAdmin: "Diese Instanz unterstützt die Verwendung von Email-Adressen nicht. Wende dich an den Administrator, um dein Passwort zurückzusetzen." contactAdmin: "Diese Instanz unterstützt die Verwendung von Email-Adressen nicht. Wende dich an den Administrator, um dein Passwort zurückzusetzen."
_gallery: _gallery:
@ -882,7 +889,7 @@ _registry:
domain: "Domain" domain: "Domain"
createKey: "Schlüssel erstellen" createKey: "Schlüssel erstellen"
_aboutMisskey: _aboutMisskey:
about: "Misskey ist Open-Source-Software die von syuilo seit 2014 entwickelt wird." about: "Misskey ist Open-Source-Software, welche von syuilo seit 2014 entwickelt wird."
contributors: "Hauptmitwirkende" contributors: "Hauptmitwirkende"
allContributors: "Alle Mitwirkenden" allContributors: "Alle Mitwirkenden"
source: "Quellcode" source: "Quellcode"
@ -899,13 +906,13 @@ _mfm:
intro: "MFM ist eine Misskey-exklusive Markup-Sprache, die in Misskey an vielen Stellen verwendet werden kann. Hier kannst du eine Liste von verfügbarer MFM-Syntax einsehen." intro: "MFM ist eine Misskey-exklusive Markup-Sprache, die in Misskey an vielen Stellen verwendet werden kann. Hier kannst du eine Liste von verfügbarer MFM-Syntax einsehen."
dummy: "Misskey erweitert die Welt des Fediverse" dummy: "Misskey erweitert die Welt des Fediverse"
mention: "Erwähnung" mention: "Erwähnung"
mentionDescription: "Mit At-Zeichen und Nutzername kann ein individueller Nutzer angegeben werden." mentionDescription: "Mit At-Zeichen und Benutzername kann ein individueller Nutzer angegeben werden."
hashtag: "Hashtag" hashtag: "Hashtag"
hashtagDescription: "Mit einer Raute und Text kann ein Hashtag angegeben werden." hashtagDescription: "Mit einer Raute und Text kann ein Hashtag angegeben werden."
url: "URL" url: "URL"
urlDescription: "Zeigt URLs an." urlDescription: "Zeigt URLs an."
link: "Link" link: "Link"
linkDescription: "Spezifische Textabschnitte als URL anzeigen." linkDescription: "Zeigt spezifische Textabschnitte als URL an."
bold: "Fett" bold: "Fett"
boldDescription: "Zeichen zur Betonung dicker erscheinen lassen." boldDescription: "Zeichen zur Betonung dicker erscheinen lassen."
small: "Klein" small: "Klein"
@ -929,19 +936,19 @@ _mfm:
flip: "Spiegelung" flip: "Spiegelung"
flipDescription: "Inhalt horizontal oder vertikal gespiegelt anzeigen." flipDescription: "Inhalt horizontal oder vertikal gespiegelt anzeigen."
jelly: "Animation (Dehnen)" jelly: "Animation (Dehnen)"
jellyDescription: "Verleiht dem Inhalt eine sich dehnende Animation." jellyDescription: "Verleiht Inhalt eine sich dehnende Animation."
tada: "Animation (Tada)" tada: "Animation (Tada)"
tadaDescription: "Verleiht eine Animation mit \"Tada!\"-Gefühl" tadaDescription: "Verleiht Inhalt eine Animation mit \"Tada!\"-Gefühl"
jump: "Animation (Sprung)" jump: "Animation (Sprung)"
jumpDescription: "Verleiht dem Inhalt eine springende Animation." jumpDescription: "Verleiht Inhalt eine springende Animation."
bounce: "Animation (Federn)" bounce: "Animation (Federn)"
bounceDescription: "Verleiht dem Inhalt eine federnde Animation." bounceDescription: "Verleiht Inhalt eine federnde Animation."
shake: "Animation (Zittern)" shake: "Animation (Zittern)"
shakeDescription: "Verleiht dem Inhalt eine zitternde Animation." shakeDescription: "Verleiht Inhalt eine zitternde Animation."
twitch: "Animation (Zucken)" twitch: "Animation (Zucken)"
twitchDescription: "Verleiht dem Inhalt eine sehr stark zuckende Animation." twitchDescription: "Verleiht Inhalt eine sehr stark zuckende Animation."
spin: "Animation (Rotieren)" spin: "Animation (Rotieren)"
spinDescription: "Verleiht dem Inhalt eine rotierende Animation." spinDescription: "Verleiht Inhalt eine rotierende Animation."
x2: "Groß" x2: "Groß"
x2Description: "Inhalte größer anzeigen." x2Description: "Inhalte größer anzeigen."
x3: "Sehr groß" x3: "Sehr groß"
@ -949,7 +956,7 @@ _mfm:
x4: "Unglaublich groß" x4: "Unglaublich groß"
x4Description: "Lässt Inhalte noch größer als größer als groß angezeigt werden." x4Description: "Lässt Inhalte noch größer als größer als groß angezeigt werden."
blur: "Weichzeichnen" blur: "Weichzeichnen"
blurDescription: "Inhalte durch Weihzeichnung verschwimmen lassen. Durch das Bewegen des Mauszeigers auf den Inhalt wird er klar angezeigt." blurDescription: "Inhalte durch Weihzeichnung verschwimmen lassen. Durch das Bewegen des Mauszeigers über den Inhalt wird er klar angezeigt."
font: "Schriftart" font: "Schriftart"
fontDescription: "Setzt die Schriftart des Inhaltes fest." fontDescription: "Setzt die Schriftart des Inhaltes fest."
rainbow: "Regenbogen" rainbow: "Regenbogen"
@ -957,7 +964,7 @@ _mfm:
sparkle: "Glitzer" sparkle: "Glitzer"
sparkleDescription: "Verleiht Inhalt einen glitzernden Partikeleffekt." sparkleDescription: "Verleiht Inhalt einen glitzernden Partikeleffekt."
rotate: "Drehen" rotate: "Drehen"
rotateDescription: "Dreht den Inhalt um einen angegebenen Winkel" rotateDescription: "Dreht den Inhalt um einen angegebenen Winkel."
_instanceTicker: _instanceTicker:
none: "Nie anzeigen" none: "Nie anzeigen"
remote: "Für Benutzer fremder Instanzen anzeigen" remote: "Für Benutzer fremder Instanzen anzeigen"
@ -983,7 +990,7 @@ _menuDisplay:
hide: "Ausblenden" hide: "Ausblenden"
_wordMute: _wordMute:
muteWords: "Stummgeschaltete Wörter" muteWords: "Stummgeschaltete Wörter"
muteWordsDescription: "Mit Leerzeichen für eine \"UND\"-Verknüpfung trennen, durch Zeilenumbrüche für eine \"ODER\"-Verknüpfung trennen." muteWordsDescription: "Zum Nutzen einer \"UND\"-Verknüpfung Einträge mit Leerzeichen trennen, zum Nutzen einer \"ODER\"-Verknüpfung Einträge mit einem Zeilenumbruch trennen."
muteWordsDescription2: "Umgib Schlüsselworter mit Schrägstrichen, um Reguläre Ausdrücke zu verwenden." muteWordsDescription2: "Umgib Schlüsselworter mit Schrägstrichen, um Reguläre Ausdrücke zu verwenden."
softDescription: "Notizen, die die angegebenen Konditionen erfüllen, in der Chronik ausblenden." softDescription: "Notizen, die die angegebenen Konditionen erfüllen, in der Chronik ausblenden."
hardDescription: "Verhindern, dass Notizen, die die angegebenen Konditionen erfüllen, der Chronik hinzugefügt werden. Zudem werden diese Notizen auch nicht der Chronik hinzugefügt, falls die Konditionen geändert werden." hardDescription: "Verhindern, dass Notizen, die die angegebenen Konditionen erfüllen, der Chronik hinzugefügt werden. Zudem werden diese Notizen auch nicht der Chronik hinzugefügt, falls die Konditionen geändert werden."
@ -996,17 +1003,17 @@ _instanceMute:
title: "Blendet Notizen von stummgeschalteten Instanzen aus." title: "Blendet Notizen von stummgeschalteten Instanzen aus."
heading: "Liste der stummzuschaltenden Instanzen" heading: "Liste der stummzuschaltenden Instanzen"
_theme: _theme:
explore: "Themen erforschen" explore: "Farbschemata erforschen"
install: "Thema installieren" install: "Farbschmata installieren"
manage: "Themaverwaltung" manage: "Farbschemaverwaltung"
code: "Themencode" code: "Farbschemencode"
description: "Beschreibung" description: "Beschreibung"
installed: "{name} wurde installiert" installed: "{name} wurde installiert"
installedThemes: "Installierte Themen" installedThemes: "Installierte Farbschemata"
builtinThemes: "Eingebaute Themen" builtinThemes: "Eingebaute Farbschemata"
alreadyInstalled: "Dieses Thema ist bereits installiert" alreadyInstalled: "Dieses Farbschema ist bereits installiert"
invalid: "Der Themencode dieses Themas ist ungültig" invalid: "Der Code dieses Farbschemas ist ungültig"
make: "Farbthema erstellen" make: "Farbschema erstellen"
base: "Vorlage" base: "Vorlage"
addConstant: "Konstante hinzufügen" addConstant: "Konstante hinzufügen"
constant: "Konstante" constant: "Konstante"
@ -1023,7 +1030,7 @@ _theme:
darken: "Verdunkeln" darken: "Verdunkeln"
lighten: "Erhellen" lighten: "Erhellen"
inputConstantName: "Name der Konstanten eingeben" inputConstantName: "Name der Konstanten eingeben"
importInfo: "Du kannst hier Themencode einfügen, um ihn in den Editor zu importieren" importInfo: "Hier kannst du Farbschemencode einfügen, um ihn in den Editor zu importieren"
deleteConstantConfirm: "Die Konstante {const} wirklich löschen?" deleteConstantConfirm: "Die Konstante {const} wirklich löschen?"
keys: keys:
accent: "Akzentfarbe" accent: "Akzentfarbe"
@ -1060,7 +1067,7 @@ _theme:
toastFg: "Text von Benachrichtigungen" toastFg: "Text von Benachrichtigungen"
buttonBg: "Hintergrund von Schaltflächen" buttonBg: "Hintergrund von Schaltflächen"
buttonHoverBg: "Hintergrund von Schaltflächen (Mouseover)" buttonHoverBg: "Hintergrund von Schaltflächen (Mouseover)"
inputBorder: "Rahmen des Eingabefelds" inputBorder: "Rahmen von Eingabefeldern"
listItemHoverBg: "Hintergrund von Listeneinträgen (Mouseover)" listItemHoverBg: "Hintergrund von Listeneinträgen (Mouseover)"
driveFolderBg: "Hintergrund von Drive-Ordnern" driveFolderBg: "Hintergrund von Drive-Ordnern"
wallpaperOverlay: "Hintergrundbild-Overlay" wallpaperOverlay: "Hintergrundbild-Overlay"
@ -1101,7 +1108,7 @@ _tutorial:
step2_1: "Lass uns zuerst dein Profil vervollständigen, bevor du Notizen schreibst oder jemandem folgst." step2_1: "Lass uns zuerst dein Profil vervollständigen, bevor du Notizen schreibst oder jemandem folgst."
step2_2: "Informationen darüber, was für eine Person du bist, macht es anderen leichter zu wissen, ob sie deine Notizen sehen wollen und ob sie dir folgen möchten." step2_2: "Informationen darüber, was für eine Person du bist, macht es anderen leichter zu wissen, ob sie deine Notizen sehen wollen und ob sie dir folgen möchten."
step3_1: "Mit dem Einrichten deines Profils fertig?" step3_1: "Mit dem Einrichten deines Profils fertig?"
step3_2: "Dann lass uns als nächstes versuchen, eine Notiz zu schreiben. Dies kannst du tun, indem du auf das Stift-Icon oben auf dem Bildschirm drückst." step3_2: "Dann lass uns als nächstes versuchen, eine Notiz zu schreiben. Dies kannst du tun, indem du auf den Knopf mit dem Stift-Icon auf dem Bildschirm drückst."
step3_3: "Fülle das Fenster aus und drücke auf den Knopf oben rechts zum Senden." step3_3: "Fülle das Fenster aus und drücke auf den Knopf oben rechts zum Senden."
step3_4: "Fällt dir nichts ein, das du schreiben möchtest? Versuch's mit \"Hallo Misskey!\"" step3_4: "Fällt dir nichts ein, das du schreiben möchtest? Versuch's mit \"Hallo Misskey!\""
step4_1: "Fertig mit dem Senden deiner ersten Notiz?" step4_1: "Fertig mit dem Senden deiner ersten Notiz?"
@ -1111,8 +1118,8 @@ _tutorial:
step5_3: "Klicke zum Anzeigen des Profils eines Benutzers auf dessen Profilbild und dann auf den \"Folgen\"-Knopf, um diesem zu folgen." step5_3: "Klicke zum Anzeigen des Profils eines Benutzers auf dessen Profilbild und dann auf den \"Folgen\"-Knopf, um diesem zu folgen."
step5_4: "Je nach Benutzer kann es etwas Zeit in Anspruch nehmen, bis dieser deine Follow-Anfrage bestätigt." step5_4: "Je nach Benutzer kann es etwas Zeit in Anspruch nehmen, bis dieser deine Follow-Anfrage bestätigt."
step6_1: "Wenn du nun auch die Notizen anderer Benutzer in deiner Chronik siehst, hast du auch diesmal alles richtig gemacht." step6_1: "Wenn du nun auch die Notizen anderer Benutzer in deiner Chronik siehst, hast du auch diesmal alles richtig gemacht."
step6_2: "Du kannst ebenso \"Reaktionen\" verwenden, um schnell auf Notizen anderer Benutzer zu reagieren." step6_2: "Du kannst ebenso „Reaktionen“ verwenden, um schnell auf Notizen anderer Benutzer zu reagieren."
step6_3: "Um eine Reaktion anzufügen, klicke auf das \"+\"-Symbol in der Notiz und wähle ein Emoji aus, mit dem du reagieren möchtest." step6_3: "Um eine Reaktion anzufügen, klicke auf das „+“-Symbol in der Notiz und wähle ein Emoji aus, mit dem du reagieren möchtest."
step7_1: "Glückwunsch! Du hast die Einführung in die Verwendung von Misskey abgeschlossen." step7_1: "Glückwunsch! Du hast die Einführung in die Verwendung von Misskey abgeschlossen."
step7_2: "Wenn du mehr über Misskey lernen möchtest, schau dich im {help}-Bereich um." step7_2: "Wenn du mehr über Misskey lernen möchtest, schau dich im {help}-Bereich um."
step7_3: "Und nun, viel Spaß mit Misskey! 🚀" step7_3: "Und nun, viel Spaß mit Misskey! 🚀"
@ -1159,7 +1166,7 @@ _permissions:
"read:gallery-likes": "Liste deiner mit \"Gefällt mir\" markierten Galerie-Beiträge lesen" "read:gallery-likes": "Liste deiner mit \"Gefällt mir\" markierten Galerie-Beiträge lesen"
"write:gallery-likes": "Liste deiner mit \"Gefällt mir\" markierten Galerie-Beiträge bearbeiten" "write:gallery-likes": "Liste deiner mit \"Gefällt mir\" markierten Galerie-Beiträge bearbeiten"
_auth: _auth:
shareAccess: "Möchtest du \"{name}\" authorisieren, auf dieses Benuzerkonto zugreifen zu können?" shareAccess: "Möchtest du „{name}“ authorisieren, auf dieses Benutzerkonto zugreifen zu können?"
shareAccessAsk: "Bist du dir sicher, dass du diese Anwendung authorisieren möchtest, auf dein Benutzerkonto zugreifen zu können?" shareAccessAsk: "Bist du dir sicher, dass du diese Anwendung authorisieren möchtest, auf dein Benutzerkonto zugreifen zu können?"
permissionAsk: "Diese Anwendung fordert folgende Berechtigungen" permissionAsk: "Diese Anwendung fordert folgende Berechtigungen"
pleaseGoBack: "Bitte kehre zur Anwendung zurück" pleaseGoBack: "Bitte kehre zur Anwendung zurück"
@ -1180,7 +1187,7 @@ _weekday:
friday: "Freitag" friday: "Freitag"
saturday: "Samstag" saturday: "Samstag"
_widgets: _widgets:
memo: "Memo" memo: "Merkzettel"
notifications: "Benachrichtigungen" notifications: "Benachrichtigungen"
timeline: "Chronik" timeline: "Chronik"
calendar: "Kalender" calendar: "Kalender"
@ -1211,8 +1218,8 @@ _poll:
canMultipleVote: "Auswahl mehrerer Antworten erlauben" canMultipleVote: "Auswahl mehrerer Antworten erlauben"
expiration: "Abstimmung beenden" expiration: "Abstimmung beenden"
infinite: "Nie" infinite: "Nie"
at: "Beenden am..." at: "Beenden am"
after: "Beenden nach..." after: "Beenden nach"
deadlineDate: "Enddatum" deadlineDate: "Enddatum"
deadlineTime: "Zeit" deadlineTime: "Zeit"
duration: "Dauer" duration: "Dauer"
@ -1238,24 +1245,24 @@ _visibility:
localOnly: "Nur Lokal" localOnly: "Nur Lokal"
localOnlyDescription: "Unsichtbar für Benutzer anderer Instanzen" localOnlyDescription: "Unsichtbar für Benutzer anderer Instanzen"
_postForm: _postForm:
replyPlaceholder: "Dieser Notiz antworten..." replyPlaceholder: "Dieser Notiz antworten"
quotePlaceholder: "Diese Notiz zitieren..." quotePlaceholder: "Diese Notiz zitieren"
channelPlaceholder: "In einen Kanal senden" channelPlaceholder: "In einen Kanal senden"
_placeholders: _placeholders:
a: "Was machst du momentan?" a: "Was machst du momentan?"
b: "Was ist um dich herum los?" b: "Was ist um dich herum los?"
c: "Was geht dir durch den Kopf?" c: "Was geht dir durch den Kopf?"
d: "Was möchtest du sagen?" d: "Was möchtest du sagen?"
e: "Fang an zu schreiben..." e: "Fang an zu schreiben"
f: "Ich warte darauf, dass du schreibst..." f: "Ich warte darauf, dass du schreibst"
_profile: _profile:
name: "Name" name: "Name"
username: "Benutzername" username: "Benutzername"
description: "Über mich" description: "Profilbeschreibung"
youCanIncludeHashtags: "Du kannst auch Hashtags in deiner Beschreibung verwenden." youCanIncludeHashtags: "Du kannst auch Hashtags in deiner Profilbeschreibung verwenden."
metadata: "Zusätzliche Informationen" metadata: "Zusätzliche Informationen"
metadataEdit: "Zusätzliche Informationen bearbeiten" metadataEdit: "Zusätzliche Informationen bearbeiten"
metadataDescription: "Du kannst auf deinem Profil vier zusätzliche Informationsblöcke anzeigen lassen." metadataDescription: "Hierdurch kannst du auf deinem Profil zusätzliche Informationsblöcke anzeigen lassen."
metadataLabel: "Beschriftung" metadataLabel: "Beschriftung"
metadataContent: "Inhalt" metadataContent: "Inhalt"
changeAvatar: "Profilbild ändern" changeAvatar: "Profilbild ändern"
@ -1274,25 +1281,25 @@ _charts:
usersIncDec: "Unterschied in der Anzahl von Benutzern" usersIncDec: "Unterschied in der Anzahl von Benutzern"
usersTotal: "Anzahl aller Benutzer" usersTotal: "Anzahl aller Benutzer"
activeUsers: "Aktive Benutzer" activeUsers: "Aktive Benutzer"
notesIncDec: "Unterschied in der Anzahl von Notizen" notesIncDec: "Unterschied in der Anzahl an Notizen"
localNotesIncDec: "Unterschied in der Anzahl von lokalen Notizen" localNotesIncDec: "Unterschied in der Anzahl an lokalen Notizen"
remoteNotesIncDec: "Unterschied in Anzahl der Notizen von fremden Instanzen" remoteNotesIncDec: "Unterschied in der Anzahl an Notizen von fremden Instanzen"
notesTotal: "Anzahl aller Notizen" notesTotal: "Anzahl aller Notizen"
filesIncDec: "Unterschied an Dateien" filesIncDec: "Unterschied in der Anzahl an Dateien"
filesTotal: "Summe der Dateien" filesTotal: "Anzahl aller Dateien"
storageUsageIncDec: "Unterschied in der Höhe der Speichernutzung" storageUsageIncDec: "Unterschied in der Höhe der Speichernutzung"
storageUsageTotal: "Gesamte Speichernutzung" storageUsageTotal: "Gesamte Speichernutzung"
_instanceCharts: _instanceCharts:
requests: "Anfragen" requests: "Anfragen"
users: "Unterschied in der Anzahl von Benutzern" users: "Unterschied in der Anzahl an Benutzern"
usersTotal: "Gesamtanzahl an Benutzern" usersTotal: "Gesamtanzahl an Benutzern"
notes: "Unterschied in der Anzahl von Notizen" notes: "Unterschied in der Anzahl an Notizen"
notesTotal: "Gesamtanzahl an Notizen" notesTotal: "Gesamtanzahl an Notizen"
ff: "Unterschied in der Anzahl von gefolgten Benutzern und Followern" ff: "Unterschied in der Anzahl an gefolgten Benutzern und Followern"
ffTotal: "Gesamtanzahl an gefolgten Benutzern und Followern" ffTotal: "Gesamtanzahl an gefolgten Benutzern und Followern"
cacheSize: "Unterschied in der Größe des Caches" cacheSize: "Unterschied in der Größe des Caches"
cacheSizeTotal: "Gesamtgröße des Caches" cacheSizeTotal: "Gesamtgröße des Caches"
files: "Unterschied in der Anzahl der Dateien" files: "Unterschied in der Anzahl an Dateien"
filesTotal: "Gesamtanzahl an Dateien" filesTotal: "Gesamtanzahl an Dateien"
_timelines: _timelines:
home: "Startseite" home: "Startseite"
@ -1302,7 +1309,7 @@ _timelines:
_pages: _pages:
newPage: "Seite erstellen" newPage: "Seite erstellen"
editPage: "Seite bearbeiten" editPage: "Seite bearbeiten"
readPage: "Quelltext-Ansicht" readPage: "Quelltextansicht"
created: "Seite erfolgreich erstellt" created: "Seite erfolgreich erstellt"
updated: "Seite erfolgreich aktualisiert" updated: "Seite erfolgreich aktualisiert"
deleted: "Seite erfolgreich gelöscht" deleted: "Seite erfolgreich gelöscht"
@ -1326,7 +1333,7 @@ _pages:
url: "Seiten-URL" url: "Seiten-URL"
summary: "Zusammenfassung" summary: "Zusammenfassung"
alignCenter: "Zentrieren" alignCenter: "Zentrieren"
hideTitleWhenPinned: "Seitentitel ausblenden, wenn angeheftet" hideTitleWhenPinned: "Seitentitel wenn angeheftet ausblenden"
font: "Schriftart" font: "Schriftart"
fontSerif: "Serif" fontSerif: "Serif"
fontSansSerif: "Sans Serif" fontSansSerif: "Sans Serif"
@ -1363,7 +1370,7 @@ _pages:
name: "Variablenname" name: "Variablenname"
text: "Titel" text: "Titel"
default: "Standardwert" default: "Standardwert"
numberInput: "Nummereingabe" numberInput: "Zahleneingabe"
_numberInput: _numberInput:
name: "Variablenname" name: "Variablenname"
text: "Titel" text: "Titel"
@ -1387,7 +1394,7 @@ _pages:
_counter: _counter:
name: "Variablenname" name: "Variablenname"
text: "Titel" text: "Titel"
inc: "Erhöhen um" inc: "Schrittgröße"
_button: _button:
text: "Titel" text: "Titel"
colored: "Farbig" colored: "Farbig"
@ -1433,10 +1440,10 @@ _pages:
strLen: "Textlänge" strLen: "Textlänge"
_strLen: _strLen:
arg1: "Text" arg1: "Text"
strPick: "Zeichen extrahieren" strPick: "Text extrahieren"
_strPick: _strPick:
arg1: "Text" arg1: "Text"
arg2: "Zeichenposition" arg2: "Textposition"
strReplace: "Textersetzung" strReplace: "Textersetzung"
_strReplace: _strReplace:
arg1: "Text" arg1: "Text"
@ -1447,7 +1454,7 @@ _pages:
arg1: "Text" arg1: "Text"
join: "Text zusammenfügen" join: "Text zusammenfügen"
_join: _join:
arg1: "Listen" arg1: "Liste"
arg2: "Trennzeichen" arg2: "Trennzeichen"
add: "Addieren" add: "Addieren"
_add: _add:
@ -1576,7 +1583,7 @@ _pages:
_for: _for:
arg1: "Anzahl der Schleifendurchläufe" arg1: "Anzahl der Schleifendurchläufe"
arg2: "Aktion" arg2: "Aktion"
typeError: "Slot {slot} akzeptiert Werte vom Typ \"{expect}\", aber es wurde ein \"{actual}\" Wert angegeben!" typeError: "Slot {slot} akzeptiert Werte vom Typ „{expect}“, aber es wurde ein „{actual}“ Wert angegeben!"
thereIsEmptySlot: "Slot {slot} ist leer!" thereIsEmptySlot: "Slot {slot} ist leer!"
types: types:
string: "Text" string: "Text"
@ -1587,7 +1594,7 @@ _pages:
emptySlot: "Leerer Slot" emptySlot: "Leerer Slot"
enviromentVariables: "Umgebungsvariable" enviromentVariables: "Umgebungsvariable"
pageVariables: "Seitenelemente" pageVariables: "Seitenelemente"
argVariables: "Eingabe-Slots" argVariables: "Eingabeslots"
_relayStatus: _relayStatus:
requesting: "Ausstehend" requesting: "Ausstehend"
accepted: "Akzeptiert" accepted: "Akzeptiert"
@ -1605,6 +1612,7 @@ _notification:
youReceivedFollowRequest: "Du hast eine Follow-Anfrage erhalten" youReceivedFollowRequest: "Du hast eine Follow-Anfrage erhalten"
yourFollowRequestAccepted: "Deine Follow-Anfrage wurde akzeptiert" yourFollowRequestAccepted: "Deine Follow-Anfrage wurde akzeptiert"
youWereInvitedToGroup: "Du wurdest in eine Gruppe eingeladen" youWereInvitedToGroup: "Du wurdest in eine Gruppe eingeladen"
pollEnded: "Umfrageergebnisse sind verfügbar"
_types: _types:
all: "Alle" all: "Alle"
follow: "Neue Follower" follow: "Neue Follower"
@ -1614,6 +1622,7 @@ _notification:
quote: "Zitationen" quote: "Zitationen"
reaction: "Reaktionen" reaction: "Reaktionen"
pollVote: "Antworten auf Umfragen" pollVote: "Antworten auf Umfragen"
pollEnded: "Ende von Umfragen"
receiveFollowRequest: "Erhaltene Follow-Anfragen" receiveFollowRequest: "Erhaltene Follow-Anfragen"
followRequestAccepted: "Akzeptierte Follow-Anfragen" followRequestAccepted: "Akzeptierte Follow-Anfragen"
groupInvited: "Erhaltene Gruppeneinladungen" groupInvited: "Erhaltene Gruppeneinladungen"
@ -1624,10 +1633,10 @@ _deck:
columnMargin: "Spaltenabstand" columnMargin: "Spaltenabstand"
columnHeaderHeight: "Spaltenkopfhöhe" columnHeaderHeight: "Spaltenkopfhöhe"
addColumn: "Spalte hinzufügen" addColumn: "Spalte hinzufügen"
swapLeft: "Nach links verschieben" swapLeft: "Mit linker Spalte tauschen"
swapRight: "Nach rechts verschieben" swapRight: "Mit rechter Spalte tauschen"
swapUp: "Nach oben verschieben" swapUp: "Mit oberer Spalte tauschen"
swapDown: "Nach unten verschieben" swapDown: "Mit unterer Spalte tauschen"
stackLeft: "Auf linke Spalte stapeln" stackLeft: "Auf linke Spalte stapeln"
popRight: "Nach rechts vom Stapel nehmen" popRight: "Nach rechts vom Stapel nehmen"
profile: "Profil" profile: "Profil"

View file

@ -8,7 +8,7 @@ notifications: "Notifications"
username: "Username" username: "Username"
password: "Password" password: "Password"
forgotPassword: "Forgot password" forgotPassword: "Forgot password"
fetchingAsApObject: "Fetching from Fediverse..." fetchingAsApObject: "Fetching from the Fediverse..."
ok: "OK" ok: "OK"
gotIt: "Got it!" gotIt: "Got it!"
cancel: "Cancel" cancel: "Cancel"
@ -32,9 +32,9 @@ uploading: "Uploading..."
save: "Save" save: "Save"
users: "Users" users: "Users"
addUser: "Add a user" addUser: "Add a user"
favorite: "Favorite" favorite: "Add to favorites"
favorites: "Favorites" favorites: "Favorites"
unfavorite: "Unfavorite" unfavorite: "Remove from favorites"
favorited: "Added to favorites." favorited: "Added to favorites."
alreadyFavorited: "Already added to favorites." alreadyFavorited: "Already added to favorites."
cantFavorite: "Couldn't add to favorites." cantFavorite: "Couldn't add to favorites."
@ -43,7 +43,7 @@ unpin: "Unpin from profile"
copyContent: "Copy contents" copyContent: "Copy contents"
copyLink: "Copy link" copyLink: "Copy link"
delete: "Delete" delete: "Delete"
deleteAndEdit: "Delete and Edit" deleteAndEdit: "Delete and edit"
deleteAndEditConfirm: "Are you sure you want to delete this note and edit it? You will lose all reactions, renotes and replies to it." deleteAndEditConfirm: "Are you sure you want to delete this note and edit it? You will lose all reactions, renotes and replies to it."
addToList: "Add to list" addToList: "Add to list"
sendMessage: "Send a message" sendMessage: "Send a message"
@ -77,21 +77,21 @@ followsYou: "Follows you"
createList: "Create list" createList: "Create list"
manageLists: "Manage lists" manageLists: "Manage lists"
error: "Error" error: "Error"
somethingHappened: "An error occurred" somethingHappened: "An error has occurred"
retry: "Retry" retry: "Retry"
pageLoadError: "Failed to load page." pageLoadError: "An error occurred loading the page."
pageLoadErrorDescription: "This is normally caused by network errors or the browser's cache. Try clearing the cache and then try again after waiting a little while." pageLoadErrorDescription: "This is normally caused by network errors or the browser's cache. Try clearing the cache and then try again after waiting a little while."
serverIsDead: "This server is not responding. Please wait for a while and try again." serverIsDead: "This server is not responding. Please wait for a while and try again."
youShouldUpgradeClient: "To view this page, please refresh to update your client." youShouldUpgradeClient: "To view this page, please refresh to update your client."
enterListName: "Enter a list name" enterListName: "Enter a name for the list"
privacy: "Privacy" privacy: "Privacy"
makeFollowManuallyApprove: "Follow requests require approval" makeFollowManuallyApprove: "Follow requests require approval"
defaultNoteVisibility: "Default visibility" defaultNoteVisibility: "Default visibility"
follow: "Follow" follow: "Follow"
followRequest: "Request follow" followRequest: "Send follow request"
followRequests: "Follow requests" followRequests: "Follow requests"
unfollow: "Unfollow" unfollow: "Unfollow"
followRequestPending: "Pending follow request" followRequestPending: "Follow request pending"
enterEmoji: "Enter an emoji" enterEmoji: "Enter an emoji"
renote: "Renote" renote: "Renote"
unrenote: "Take back renote" unrenote: "Take back renote"
@ -107,12 +107,12 @@ sensitive: "NSFW"
add: "Add" add: "Add"
reaction: "Reactions" reaction: "Reactions"
reactionSetting: "Reactions to show in the reaction picker" reactionSetting: "Reactions to show in the reaction picker"
reactionSettingDescription2: "Drag to reorder, Click to delete, Press \"+\" to add" reactionSettingDescription2: "Drag to reorder, click to delete, press \"+\" to add."
rememberNoteVisibility: "Remember note visibility settings" rememberNoteVisibility: "Remember note visibility settings"
attachCancel: "Remove attachment" attachCancel: "Remove attachment"
markAsSensitive: "Mark as NSFW" markAsSensitive: "Mark as NSFW"
unmarkAsSensitive: "Undo NSFW" unmarkAsSensitive: "Unmark as NSFW"
enterFileName: "Enter file name" enterFileName: "Enter filename"
mute: "Mute" mute: "Mute"
unmute: "Unmute" unmute: "Unmute"
block: "Block" block: "Block"
@ -168,8 +168,8 @@ latestRequestReceivedAt: "Last request received"
latestStatus: "Latest status" latestStatus: "Latest status"
storageUsage: "Storage usage" storageUsage: "Storage usage"
charts: "Charts" charts: "Charts"
perHour: "per Hour" perHour: "Per Hour"
perDay: "per Day" perDay: "Per Day"
stopActivityDelivery: "Stop sending activities" stopActivityDelivery: "Stop sending activities"
blockThisInstance: "Block this instance" blockThisInstance: "Block this instance"
operations: "Operations" operations: "Operations"
@ -190,7 +190,7 @@ clearQueueConfirmText: "Any undelivered notes remaining in the queue will not be
clearCachedFiles: "Clear cache" clearCachedFiles: "Clear cache"
clearCachedFilesConfirm: "Are you sure that you want to delete all cached remote files?" clearCachedFilesConfirm: "Are you sure that you want to delete all cached remote files?"
blockedInstances: "Blocked Instances" blockedInstances: "Blocked Instances"
blockedInstancesDescription: "List the hosts of the instances to be blocked separated by line breaks. Blocked instances will no longer be able to communicate with this instance." blockedInstancesDescription: "List the hostnames of the instances that you want to block. Listed instances will no longer be able to communicate with this instance."
muteAndBlock: "Mutes and Blocks" muteAndBlock: "Mutes and Blocks"
mutedUsers: "Muted users" mutedUsers: "Muted users"
blockedUsers: "Blocked users" blockedUsers: "Blocked users"
@ -325,8 +325,6 @@ disablingTimelinesInfo: "Adminstrators and Moderators will always have access to
registration: "Register" registration: "Register"
enableRegistration: "Enable new user registration" enableRegistration: "Enable new user registration"
invite: "Invite" invite: "Invite"
proxyRemoteFiles: "Proxy remote files"
proxyRemoteFilesDescription: "If enabled, remote files that are either not stored locally or were deleted due to exceeding the storage limit will be proxied, including generation of thumbnails. This does not affect the server's storage."
driveCapacityPerLocalAccount: "Drive capacity per local user" driveCapacityPerLocalAccount: "Drive capacity per local user"
driveCapacityPerRemoteAccount: "Drive capacity per remote user" driveCapacityPerRemoteAccount: "Drive capacity per remote user"
inMb: "In megabytes" inMb: "In megabytes"
@ -336,9 +334,9 @@ backgroundImageUrl: "Background image URL"
basicInfo: "Basic info" basicInfo: "Basic info"
pinnedUsers: "Pinned users" pinnedUsers: "Pinned users"
pinnedUsersDescription: "List usernames separated by line breaks to be pinned in the \"Explore\" tab." pinnedUsersDescription: "List usernames separated by line breaks to be pinned in the \"Explore\" tab."
pinnedPages: "Pinned pages" pinnedPages: "Pinned Pages"
pinnedPagesDescription: "Enter the paths of the pages you want to pin to the top page of this instance, separated by line breaks." pinnedPagesDescription: "Enter the paths of the Pages you want to pin to the top page of this instance, separated by line breaks."
pinnedClipId: "ID of the pinned clip" pinnedClipId: "ID of the clip to pin"
pinnedNotes: "Pinned notes" pinnedNotes: "Pinned notes"
hcaptcha: "hCaptcha" hcaptcha: "hCaptcha"
enableHcaptcha: "Enable hCaptcha" enableHcaptcha: "Enable hCaptcha"
@ -348,7 +346,7 @@ recaptcha: "reCAPTCHA"
enableRecaptcha: "Enable reCAPTCHA" enableRecaptcha: "Enable reCAPTCHA"
recaptchaSiteKey: "Site key" recaptchaSiteKey: "Site key"
recaptchaSecretKey: "Secret key" recaptchaSecretKey: "Secret key"
avoidMultiCaptchaConfirm: "Using multiple Captcha systems may cause interferences. Would you like to disable the other Captcha systems? You can leave multiple enabled by pressing cancel." avoidMultiCaptchaConfirm: "Using multiple Captcha systems may cause interference between them. Would you like to disable the other Captcha systems currently active? If you would like them to stay enabled, press cancel."
antennas: "Antennas" antennas: "Antennas"
manageAntennas: "Manage Antennas" manageAntennas: "Manage Antennas"
name: "Name" name: "Name"
@ -369,13 +367,13 @@ silence: "Silence"
silenceConfirm: "Are you sure that you want to silence this user?" silenceConfirm: "Are you sure that you want to silence this user?"
unsilence: "Undo silencing" unsilence: "Undo silencing"
unsilenceConfirm: "Are you sure that you want to undo the silencing of this user?" unsilenceConfirm: "Are you sure that you want to undo the silencing of this user?"
popularUsers: "Trending users" popularUsers: "Popular users"
recentlyUpdatedUsers: "Users with recent activity" recentlyUpdatedUsers: "Recently active users"
recentlyRegisteredUsers: "Newly joined users" recentlyRegisteredUsers: "Newly joined users"
recentlyDiscoveredUsers: "Newly discovered users" recentlyDiscoveredUsers: "Newly discovered users"
exploreUsersCount: "There are {count} users" exploreUsersCount: "There are {count} users"
exploreFediverse: "Explore the Fediverse" exploreFediverse: "Explore the Fediverse"
popularTags: "Trending Tags" popularTags: "Popular tags"
userList: "Lists" userList: "Lists"
about: "About" about: "About"
aboutMisskey: "About Misskey" aboutMisskey: "About Misskey"
@ -383,13 +381,13 @@ administrator: "Administrator"
token: "Token" token: "Token"
twoStepAuthentication: "Two-factor authentication" twoStepAuthentication: "Two-factor authentication"
moderator: "Moderator" moderator: "Moderator"
nUsersMentioned: "{n} users mentioned" nUsersMentioned: "Mentioned by {n} users"
securityKey: "Security key" securityKey: "Security key"
securityKeyName: "Key name" securityKeyName: "Key name"
registerSecurityKey: "Register a security key" registerSecurityKey: "Register a security key"
lastUsed: "Last used" lastUsed: "Last used"
unregister: "Unregister" unregister: "Unregister"
passwordLessLogin: "Set up password-less login" passwordLessLogin: "Password-less login"
resetPassword: "Reset password" resetPassword: "Reset password"
newPasswordIs: "The new password is \"{password}\"" newPasswordIs: "The new password is \"{password}\""
reduceUiAnimation: "Reduce UI animations" reduceUiAnimation: "Reduce UI animations"
@ -422,11 +420,10 @@ next: "Next"
retype: "Enter again" retype: "Enter again"
noteOf: "Note by {user}" noteOf: "Note by {user}"
inviteToGroup: "Invite to group" inviteToGroup: "Invite to group"
maxNoteTextLength: "Character limit for notes" quoteAttached: "Quote"
quoteAttached: "Quoted"
quoteQuestion: "Append as quote?" quoteQuestion: "Append as quote?"
noMessagesYet: "No messages yet" noMessagesYet: "No messages yet"
newMessageExists: "You've got a new message" newMessageExists: "There are new messages"
onlyOneFileCanBeAttached: "You can only attach one file to a message" onlyOneFileCanBeAttached: "You can only attach one file to a message"
signinRequired: "Please sign in" signinRequired: "Please sign in"
invitations: "Invites" invitations: "Invites"
@ -438,7 +435,7 @@ usernameInvalidFormat: "You can use upper- and lowercase letters, numbers, and u
tooShort: "Too short" tooShort: "Too short"
tooLong: "Too long" tooLong: "Too long"
weakPassword: "Weak password" weakPassword: "Weak password"
normalPassword: "Normal password" normalPassword: "Average password"
strongPassword: "Strong password" strongPassword: "Strong password"
passwordMatched: "Matches" passwordMatched: "Matches"
passwordNotMatched: "Does not match" passwordNotMatched: "Does not match"
@ -466,7 +463,7 @@ existingAccount: "Existing account"
regenerate: "Regenerate" regenerate: "Regenerate"
fontSize: "Font size" fontSize: "Font size"
noFollowRequests: "You don't have any pending follow requests" noFollowRequests: "You don't have any pending follow requests"
openImageInNewTab: "Open image in new tab" openImageInNewTab: "Open images in new tab"
dashboard: "Dashboard" dashboard: "Dashboard"
local: "Local" local: "Local"
remote: "Remote" remote: "Remote"
@ -474,17 +471,17 @@ total: "Total"
weekOverWeekChanges: "Changes to last week" weekOverWeekChanges: "Changes to last week"
dayOverDayChanges: "Changes to yesterday" dayOverDayChanges: "Changes to yesterday"
appearance: "Appearance" appearance: "Appearance"
clientSettings: "Client settings" clientSettings: "Client Settings"
accountSettings: "Account Settings" accountSettings: "Account Settings"
promotion: "Promoted" promotion: "Promoted"
promote: "Promote" promote: "Promote"
numberOfDays: "Number of days" numberOfDays: "Number of days"
hideThisNote: "Hide this note" hideThisNote: "Hide this note"
showFeaturedNotesInTimeline: "Show Featured notes in Timelines" showFeaturedNotesInTimeline: "Show featured notes in timelines"
objectStorage: "Object Storage" objectStorage: "Object Storage"
useObjectStorage: "Use object storage" useObjectStorage: "Use object storage"
objectStorageBaseUrl: "Base URL" objectStorageBaseUrl: "Base URL"
objectStorageBaseUrlDesc: "URL used as reference. Specify the URL of your CDN or Proxy if you are using either. S3: 'https://<bucket>.s3.amazonaws.com', GCS: 'https://storage.googleapis.com/<bucket>' etc." objectStorageBaseUrlDesc: "The URL used as reference. Specify the URL of your CDN or Proxy if you are using either.\nFor S3 use 'https://<bucket>.s3.amazonaws.com' and for GCS or equivalent services use 'https://storage.googleapis.com/<bucket>', etc."
objectStorageBucket: "Bucket" objectStorageBucket: "Bucket"
objectStorageBucketDesc: "Please specify the bucket name used at your provider." objectStorageBucketDesc: "Please specify the bucket name used at your provider."
objectStoragePrefix: "Prefix" objectStoragePrefix: "Prefix"
@ -492,7 +489,7 @@ objectStoragePrefixDesc: "Files will be stored under directories with this prefi
objectStorageEndpoint: "Endpoint" objectStorageEndpoint: "Endpoint"
objectStorageEndpointDesc: "Leave this empty if you are using AWS S3, otherwise specify the endpoint as '<host>' or '<host>:<port>', depending on the service you are using." objectStorageEndpointDesc: "Leave this empty if you are using AWS S3, otherwise specify the endpoint as '<host>' or '<host>:<port>', depending on the service you are using."
objectStorageRegion: "Region" objectStorageRegion: "Region"
objectStorageRegionDesc: "Specify a region like 'xx-east-1'. If your service does not distinct between regions, leave this blank or enter 'us-east-1'." objectStorageRegionDesc: "Specify a region like 'xx-east-1'. If your service does not distinguish between regions, leave this blank or enter 'us-east-1'."
objectStorageUseSSL: "Use SSL" objectStorageUseSSL: "Use SSL"
objectStorageUseSSLDesc: "Turn this off if you are not going to use HTTPS for API connections" objectStorageUseSSLDesc: "Turn this off if you are not going to use HTTPS for API connections"
objectStorageUseProxy: "Connect over Proxy" objectStorageUseProxy: "Connect over Proxy"
@ -524,17 +521,17 @@ sort: "Sort"
ascendingOrder: "Ascending" ascendingOrder: "Ascending"
descendingOrder: "Descending" descendingOrder: "Descending"
scratchpad: "Scratchpad" scratchpad: "Scratchpad"
scratchpadDescription: "The Scratchpad provides an environment for AiScript experiments. You can write, execute, and check the results of it interacting with Misskey." scratchpadDescription: "The Scratchpad provides an environment for AiScript experiments. You can write, execute, and check the results of it interacting with Misskey in it."
output: "Output" output: "Output"
script: "Script" script: "Script"
disablePagesScript: "Disable AiScript on Pages" disablePagesScript: "Disable AiScript on Pages"
updateRemoteUser: "Update remote user information" updateRemoteUser: "Update remote user information"
deleteAllFiles: "Delete All Files" deleteAllFiles: "Delete all files"
deleteAllFilesConfirm: "Are you sure that you want to delete all files?" deleteAllFilesConfirm: "Are you sure that you want to delete all files?"
removeAllFollowing: "Unfollow all followed users" removeAllFollowing: "Unfollow all followed users"
removeAllFollowingDescription: "Executing this unfollows all accounts from {host}. Please run this if the instance e.g. no longer exists." removeAllFollowingDescription: "Executing this unfollows all accounts from {host}. Please run this if the instance e.g. no longer exists."
userSuspended: "This user has been suspended." userSuspended: "This user has been suspended."
userSilenced: "This user has been silenced." userSilenced: "This user is being silenced."
yourAccountSuspendedTitle: "This account is suspended" yourAccountSuspendedTitle: "This account is suspended"
yourAccountSuspendedDescription: "This account has been suspended due to breaking the server's terms of services or similar. Contact the administrator if you would like to know a more detailed reason. Please do not create a new account." yourAccountSuspendedDescription: "This account has been suspended due to breaking the server's terms of services or similar. Contact the administrator if you would like to know a more detailed reason. Please do not create a new account."
menu: "Menu" menu: "Menu"
@ -547,7 +544,7 @@ addedRelays: "Added Relays"
serviceworkerInfo: "Must be enabled for push notifications." serviceworkerInfo: "Must be enabled for push notifications."
deletedNote: "Deleted note" deletedNote: "Deleted note"
invisibleNote: "Invisible note" invisibleNote: "Invisible note"
enableInfiniteScroll: "Enable infinite scrolling" enableInfiniteScroll: "Automatically load more"
visibility: "Visiblility" visibility: "Visiblility"
poll: "Poll" poll: "Poll"
useCw: "Hide content" useCw: "Hide content"
@ -585,7 +582,7 @@ enableEmail: "Enable email distribution"
emailConfigInfo: "Used to confirm your email during sign-up or if you forget your password" emailConfigInfo: "Used to confirm your email during sign-up or if you forget your password"
email: "Email" email: "Email"
emailAddress: "Email address" emailAddress: "Email address"
smtpConfig: "SMTP Server configuration" smtpConfig: "SMTP Server Configuration"
smtpHost: "Host" smtpHost: "Host"
smtpPort: "Port" smtpPort: "Port"
smtpUser: "Username" smtpUser: "Username"
@ -595,7 +592,9 @@ smtpSecure: "Use implicit SSL/TLS for SMTP connections"
smtpSecureInfo: "Turn this off when using STARTTLS" smtpSecureInfo: "Turn this off when using STARTTLS"
testEmail: "Test email delivery" testEmail: "Test email delivery"
wordMute: "Word mute" wordMute: "Word mute"
instanceMute: "Instance mutes" regexpError: "Regular Expression error"
regexpErrorDescription: "An error occurred in the regular expression on line {line} of your {tab} word mutes:"
instanceMute: "Instance Mutes"
userSaysSomething: "{name} said something" userSaysSomething: "{name} said something"
makeActive: "Activate" makeActive: "Activate"
display: "Display" display: "Display"
@ -608,14 +607,14 @@ database: "Database"
channel: "Channels" channel: "Channels"
create: "Create" create: "Create"
notificationSetting: "Notification settings" notificationSetting: "Notification settings"
notificationSettingDesc: "Select the type of notification to display" notificationSettingDesc: "Select the types of notification to display."
useGlobalSetting: "Use global setting" useGlobalSetting: "Use global settings"
useGlobalSettingDesc: "If turned on, your account's notification settings will be used. If turned off, individual configurations can be made." useGlobalSettingDesc: "If turned on, your account's notification settings will be used. If turned off, individual configurations can be made."
other: "Other" other: "Other"
regenerateLoginToken: "Regenerate login token" regenerateLoginToken: "Regenerate login token"
regenerateLoginTokenDescription: "Regenerate the token used internally during login. Normally this action is not necessary. If regenerated, all devices will be logged out." regenerateLoginTokenDescription: "Regenerates the token used internally during login. Normally this action is not necessary. If regenerated, all devices will be logged out."
setMultipleBySeparatingWithSpace: "Separate multiple entries with spaces." setMultipleBySeparatingWithSpace: "Separate multiple entries with spaces."
fileIdOrUrl: "File-ID or URL" fileIdOrUrl: "File ID or URL"
behavior: "Behavior" behavior: "Behavior"
sample: "Sample" sample: "Sample"
abuseReports: "Reports" abuseReports: "Reports"
@ -689,14 +688,14 @@ center: "Center"
wide: "Wide" wide: "Wide"
narrow: "Narrow" narrow: "Narrow"
reloadToApplySetting: "This setting will only apply after a page reload. Reload now?" reloadToApplySetting: "This setting will only apply after a page reload. Reload now?"
needReloadToApply: "This setting will only apply after a page reload." needReloadToApply: "A reload is required for this to be reflected."
showTitlebar: "Show title bar" showTitlebar: "Show title bar"
clearCache: "Clear cache" clearCache: "Clear cache"
onlineUsersCount: "{n} users are online" onlineUsersCount: "{n} users are online"
nUsers: "{n} Users" nUsers: "{n} Users"
nNotes: "{n} Notes" nNotes: "{n} Notes"
sendErrorReports: "Send error reports" sendErrorReports: "Send error reports"
sendErrorReportsDescription: "When turned on, detailed error information will be shared with Misskey when a problem occurs, helping to improve the quality of Misskey.\nThis will include information such the version of your OS, what browser you're using, your activity history, etc." sendErrorReportsDescription: "When turned on, detailed error information will be shared with Misskey when a problem occurs, helping to improve the quality of Misskey.\nThis will include information such the version of your OS, what browser you're using, your activity in Misskey, etc."
myTheme: "My theme" myTheme: "My theme"
backgroundColor: "Background color" backgroundColor: "Background color"
accentColor: "Accent color" accentColor: "Accent color"
@ -794,12 +793,12 @@ translatedFrom: "Translated from {x}"
accountDeletionInProgress: "Account deletion is currently in progress" accountDeletionInProgress: "Account deletion is currently in progress"
usernameInfo: "A name that identifies your account from others on this server. You can use the alphabet (a~z, A~Z), digits (0~9) or underscores (_). Usernames cannot be changed later." usernameInfo: "A name that identifies your account from others on this server. You can use the alphabet (a~z, A~Z), digits (0~9) or underscores (_). Usernames cannot be changed later."
aiChanMode: "Ai Mode" aiChanMode: "Ai Mode"
keepCw: "Keep Content Warnings" keepCw: "Keep content warnings"
pubSub: "Pub/Sub Accounts" pubSub: "Pub/Sub Accounts"
lastCommunication: "Last communication" lastCommunication: "Last communication"
resolved: "Resolved" resolved: "Resolved"
unresolved: "Unresolved" unresolved: "Unresolved"
breakFollow: "Unfollow" breakFollow: "Remove follower"
itsOn: "Enabled" itsOn: "Enabled"
itsOff: "Disabled" itsOff: "Disabled"
emailRequiredForSignup: "Require email address for sign-up" emailRequiredForSignup: "Require email address for sign-up"
@ -819,7 +818,7 @@ deleteAccountConfirm: "This will irreversibly delete your account. Proceed?"
incorrectPassword: "Incorrect password." incorrectPassword: "Incorrect password."
voteConfirm: "Confirm your vote for \"{choice}\"?" voteConfirm: "Confirm your vote for \"{choice}\"?"
hide: "Hide" hide: "Hide"
leaveGroup: "Leave Group" leaveGroup: "Leave group"
leaveGroupConfirm: "Are you sure you want to leave \"{name}\"?" leaveGroupConfirm: "Are you sure you want to leave \"{name}\"?"
useDrawerReactionPickerForMobile: "Display reaction picker as drawer on mobile" useDrawerReactionPickerForMobile: "Display reaction picker as drawer on mobile"
welcomeBackWithName: "Welcome back, {name}" welcomeBackWithName: "Welcome back, {name}"
@ -831,6 +830,16 @@ auto: "Auto"
themeColor: "Theme Color" themeColor: "Theme Color"
size: "Size" size: "Size"
numberOfColumn: "Number of columns" numberOfColumn: "Number of columns"
searchByGoogle: "Google"
instanceDefaultLightTheme: "Instance-wide default light theme"
instanceDefaultDarkTheme: "Instance-wide default dark theme"
instanceDefaultThemeDescription: "Enter the theme code in object format."
mutePeriod: "Mute duration"
indefinitely: "Permanently"
tenMinutes: "10 minutes"
oneHour: "One hour"
oneDay: "One day"
oneWeek: "One week"
_emailUnavailable: _emailUnavailable:
used: "This email address is already being used" used: "This email address is already being used"
format: "The format of this email address is invalid" format: "The format of this email address is invalid"
@ -843,10 +852,10 @@ _ffVisibility:
private: "Private" private: "Private"
_signup: _signup:
almostThere: "Almost there" almostThere: "Almost there"
emailAddressInfo: "Please enter your email address." emailAddressInfo: "Please enter your email address. It will not be made public."
emailSent: "A confirmation email has been sent to your email address ({email}). Please click the included link to complete account creation." emailSent: "A confirmation email has been sent to your email address ({email}). Please click the included link to complete account creation."
_accountDelete: _accountDelete:
accountDelete: "Delete Account" accountDelete: "Delete account"
mayTakeTime: "As account deletion is a resource-heavy process, it may take some time to complete depending on how much content you have created and how many files you have uploaded." mayTakeTime: "As account deletion is a resource-heavy process, it may take some time to complete depending on how much content you have created and how many files you have uploaded."
sendEmail: "Once account deletion has been completed, an email will be sent to the email address registered to this account." sendEmail: "Once account deletion has been completed, an email will be sent to the email address registered to this account."
requestAccountDelete: "Request account deletion" requestAccountDelete: "Request account deletion"
@ -857,8 +866,8 @@ _ad:
reduceFrequencyOfThisAd: "Show this ad less" reduceFrequencyOfThisAd: "Show this ad less"
_forgotPassword: _forgotPassword:
enterEmail: "Enter the email address you used to register. A link with which you can reset your password will then be sent to it." enterEmail: "Enter the email address you used to register. A link with which you can reset your password will then be sent to it."
ifNoEmail: "If you did not use an email during registration, please contact the administrator instead." ifNoEmail: "If you did not use an email during registration, please contact the instance administrator instead."
contactAdmin: "This instance does not support using email addresses, please contact the administrator to reset your password instead." contactAdmin: "This instance does not support using email addresses, please contact the instance administrator to reset your password instead."
_gallery: _gallery:
my: "My Gallery" my: "My Gallery"
liked: "Liked Posts" liked: "Liked Posts"
@ -910,14 +919,14 @@ _mfm:
smallDescription: "Displays content small and thin." smallDescription: "Displays content small and thin."
center: "Center" center: "Center"
centerDescription: "Displays content centered." centerDescription: "Displays content centered."
inlineCode: "Code (In-line)" inlineCode: "Code (Inline)"
inlineCodeDescription: "Displays inline syntax highlighting for (program) code." inlineCodeDescription: "Displays inline syntax highlighting for (program) code."
blockCode: "Code (Block)" blockCode: "Code (Block)"
blockCodeDescription: "Displays syntax highlighting for multi-line (program) code in a block." blockCodeDescription: "Displays syntax highlighting for multi-line (program) code in a block."
inlineMath: "Math (In-line)" inlineMath: "Math (Inline)"
inlineMathDescription: "Display math formulas (KaTeX) in-line" inlineMathDescription: "Display math formulas (KaTeX) in-line"
blockMath: "Math (Block)" blockMath: "Math (Block)"
blockMathDescription: "Display multi-line Math formulas (KaTeX) in a block" blockMathDescription: "Display multi-line math formulas (KaTeX) in a block"
quote: "Quote" quote: "Quote"
quoteDescription: "Displays content as a quote." quoteDescription: "Displays content as a quote."
emoji: "Custom Emoji" emoji: "Custom Emoji"
@ -947,7 +956,7 @@ _mfm:
x4: "Unbelievably big" x4: "Unbelievably big"
x4Description: "Displays content even bigger than bigger than big." x4Description: "Displays content even bigger than bigger than big."
blur: "Blur" blur: "Blur"
blurDescription: "Content can be blurred via this effect. It will be displayed clearly when hovered over." blurDescription: "Blurs content. It will be displayed clearly when hovered over."
font: "Font" font: "Font"
fontDescription: "Sets the font to display content in." fontDescription: "Sets the font to display content in."
rainbow: "Rainbow" rainbow: "Rainbow"
@ -1079,10 +1088,10 @@ _ago:
unknown: "Unknown" unknown: "Unknown"
future: "Future" future: "Future"
justNow: "Just now" justNow: "Just now"
secondsAgo: "{n} seconds ago" secondsAgo: "{n} second(s) ago"
minutesAgo: "{n} minutes ago" minutesAgo: "{n} minute(s) ago"
hoursAgo: "{n} hours ago" hoursAgo: "{n} hour(s) ago"
daysAgo: "{n} days ago" daysAgo: "{n} day(s) ago"
weeksAgo: "{n} week(s) ago" weeksAgo: "{n} week(s) ago"
monthsAgo: "{n} month(s) ago" monthsAgo: "{n} month(s) ago"
yearsAgo: "{n} year(s) ago" yearsAgo: "{n} year(s) ago"
@ -1099,17 +1108,17 @@ _tutorial:
step2_1: "Let's finish setting up your profile before writing a note or following anyone." step2_1: "Let's finish setting up your profile before writing a note or following anyone."
step2_2: "Providing some information about who you are will make it easier for others to tell if they want to see your notes or follow you." step2_2: "Providing some information about who you are will make it easier for others to tell if they want to see your notes or follow you."
step3_1: "Finished setting up your profile?" step3_1: "Finished setting up your profile?"
step3_2: "Then let's try posting a note next. You can do this by pressing the pencil icon on the top of the screen." step3_2: "Then let's try posting a note next. You can do so by pressing the button with a pencil icon on the screen."
step3_3: "Fill in the modal and press the button on the top right to post." step3_3: "Fill in the modal and press the button on the top right to post."
step3_4: "Have nothing to say? Try \"just setting up my msky\"!" step3_4: "Have nothing to say? Try \"just setting up my msky\"!"
step4_1: "Finished posting your first note?" step4_1: "Finished posting your first note?"
step4_2: "Hurray! Now your first note should be displayed on your timeline." step4_2: "Hurray! Now your first note should be displayed on your timeline."
step5_1: "Now, let's try making your timeline more lively by following other people." step5_1: "Now, let's try making your timeline more lively by following other people."
step5_2: "{featured} will show you trending notes in this instance. {explore} will let you find trending users. Try finding people you'd like to follow there!" step5_2: "{featured} will show you popular notes in this instance. {explore} will let you find popular users. Try finding people you'd like to follow there!"
step5_3: "To follow other users, click on their icon and press the \"Follow\" button on their profile." step5_3: "To follow other users, click on their icon and press the \"Follow\" button on their profile."
step5_4: "If the other user has a lock icon next to their name, it may take some time for that user to manually approve your follow request." step5_4: "If the other user has a lock icon next to their name, it may take some time for that user to manually approve your follow request."
step6_1: "You should be able to see other users' notes on your timeline now." step6_1: "You should be able to see other users' notes on your timeline now."
step6_2: "You can also put \"reactions\" on other people's notes to quickly respond." step6_2: "You can also put \"reactions\" on other people's notes to quickly respond to them."
step6_3: "To attach a \"reaction\", press the \"+\" mark on another user's note and choose an emoji you'd like to react with." step6_3: "To attach a \"reaction\", press the \"+\" mark on another user's note and choose an emoji you'd like to react with."
step7_1: "Congratulations! You have now finished Misskey's basic tutorial." step7_1: "Congratulations! You have now finished Misskey's basic tutorial."
step7_2: "If you would like to learn more about Misskey, try the {help} section." step7_2: "If you would like to learn more about Misskey, try the {help} section."
@ -1140,7 +1149,7 @@ _permissions:
"write:mutes": "Edit your list of muted users" "write:mutes": "Edit your list of muted users"
"write:notes": "Compose or delete notes" "write:notes": "Compose or delete notes"
"read:notifications": "View your notifications" "read:notifications": "View your notifications"
"write:notifications": "Work with your notifications" "write:notifications": "Manage your notifications"
"read:reactions": "View your reactions" "read:reactions": "View your reactions"
"write:reactions": "Edit your reactions" "write:reactions": "Edit your reactions"
"write:votes": "Vote on a poll" "write:votes": "Vote on a poll"
@ -1150,18 +1159,18 @@ _permissions:
"write:page-likes": "Edit your likes on pages" "write:page-likes": "Edit your likes on pages"
"read:user-groups": "View your user groups" "read:user-groups": "View your user groups"
"write:user-groups": "Edit or delete your user groups" "write:user-groups": "Edit or delete your user groups"
"read:channels": "Read your channels" "read:channels": "View your channels"
"write:channels": "Modify your channels" "write:channels": "Edit your channels"
"read:gallery": "View your gallery" "read:gallery": "View your gallery"
"write:gallery": "Edit your gallery" "write:gallery": "Edit your gallery"
"read:gallery-likes": "View list of liked gallery posts" "read:gallery-likes": "View your list of liked gallery posts"
"write:gallery-likes": "Edit list of liked gallery posts" "write:gallery-likes": "Edit your list of liked gallery posts"
_auth: _auth:
shareAccess: "Would you like to authorize \"{name}\" to access this account?" shareAccess: "Would you like to authorize \"{name}\" to access this account?"
shareAccessAsk: "Are you sure you want to authorize this application to access your account?" shareAccessAsk: "Are you sure you want to authorize this application to access your account?"
permissionAsk: "This application requests the following permissions" permissionAsk: "This application requests the following permissions"
pleaseGoBack: "Please go back to the application" pleaseGoBack: "Please go back to the application"
callback: "Returning back to the application" callback: "Returning to the application"
denied: "Access denied" denied: "Access denied"
_antennaSources: _antennaSources:
all: "All notes" all: "All notes"
@ -1189,7 +1198,7 @@ _widgets:
photos: "Photos" photos: "Photos"
digitalClock: "Digital clock" digitalClock: "Digital clock"
federation: "Federation" federation: "Federation"
postForm: "Compose a note" postForm: "Posting form"
slideshow: "Slideshow" slideshow: "Slideshow"
button: "Button" button: "Button"
onlineUsers: "Online users" onlineUsers: "Online users"
@ -1220,10 +1229,10 @@ _poll:
showResult: "View results" showResult: "View results"
voted: "Voted" voted: "Voted"
closed: "Ended" closed: "Ended"
remainingDays: "{d} days {h} hours remaining" remainingDays: "{d} day(s) {h} hour(s) remaining"
remainingHours: "{h} hours {m} minutes remaining" remainingHours: "{h} hour(s) {m} minute(s) remaining"
remainingMinutes: "{m} minutes {s} seconds remaining" remainingMinutes: "{m} minute(s) {s} second(s) remaining"
remainingSeconds: "{s} seconds remaining" remainingSeconds: "{s} second(s) remaining"
_visibility: _visibility:
public: "Public" public: "Public"
publicDescription: "Your note will be visible for all users" publicDescription: "Your note will be visible for all users"
@ -1253,7 +1262,7 @@ _profile:
youCanIncludeHashtags: "You can also include hashtags in your bio." youCanIncludeHashtags: "You can also include hashtags in your bio."
metadata: "Additional Information" metadata: "Additional Information"
metadataEdit: "Edit additional Information" metadataEdit: "Edit additional Information"
metadataDescription: "You can display up to four additional information fields in your profile." metadataDescription: "Using these, you can display additional information fields in your profile."
metadataLabel: "Label" metadataLabel: "Label"
metadataContent: "Content" metadataContent: "Content"
changeAvatar: "Change avatar" changeAvatar: "Change avatar"
@ -1269,29 +1278,29 @@ _exportOrImport:
_charts: _charts:
federation: "Federation" federation: "Federation"
apRequest: "Requests" apRequest: "Requests"
usersIncDec: "Difference in # of users" usersIncDec: "Difference in the number of users"
usersTotal: "Total # of users" usersTotal: "Total number of users"
activeUsers: "Active users" activeUsers: "Active users"
notesIncDec: "Difference in # of notes" notesIncDec: "Difference in the number of notes"
localNotesIncDec: "Difference in # of local notes" localNotesIncDec: "Difference in the number of local notes"
remoteNotesIncDec: "Difference in # of remote notes" remoteNotesIncDec: "Difference in the number of remote notes"
notesTotal: "Total # of notes" notesTotal: "Total number of notes"
filesIncDec: "Difference in # of files" filesIncDec: "Difference in the number of files"
filesTotal: "Total # of files" filesTotal: "Total number of files"
storageUsageIncDec: "Difference in storage usage" storageUsageIncDec: "Difference in storage usage"
storageUsageTotal: "Total storage usage" storageUsageTotal: "Total storage usage"
_instanceCharts: _instanceCharts:
requests: "Requests" requests: "Requests"
users: "Difference in # of users" users: "Difference in the number of users"
usersTotal: "Cumulative total # of users" usersTotal: "Cumulative number of users"
notes: "Difference in # of notes" notes: "Difference in the number of notes"
notesTotal: "Cumulative total # of notes" notesTotal: "Cumulative number of notes"
ff: "Difference in # of followed users / followers " ff: "Difference in the number of followed users / followers "
ffTotal: "Cumulative total # of followed users / followers" ffTotal: "Cumulative number of followed users / followers"
cacheSize: "Difference in cache size" cacheSize: "Difference in cache size"
cacheSizeTotal: "Cumulative total cache size" cacheSizeTotal: "Cumulative total cache size"
files: "Difference in # of files" files: "Difference in the number of files"
filesTotal: "Cumulative total # of files" filesTotal: "Cumulative number of files"
_timelines: _timelines:
home: "Home" home: "Home"
local: "Local" local: "Local"
@ -1300,7 +1309,7 @@ _timelines:
_pages: _pages:
newPage: "Create a new Page" newPage: "Create a new Page"
editPage: "Edit this Page" editPage: "Edit this Page"
readPage: "Source view activated" readPage: "Viewing this Page's source"
created: "Page successfully created" created: "Page successfully created"
updated: "Page successfully edited" updated: "Page successfully edited"
deleted: "Page successfully deleted" deleted: "Page successfully deleted"
@ -1315,7 +1324,7 @@ _pages:
unlike: "Remove like" unlike: "Remove like"
my: "My Pages" my: "My Pages"
liked: "Liked Pages" liked: "Liked Pages"
featured: "Featured" featured: "Popular"
inspector: "Inspector" inspector: "Inspector"
contents: "Contents" contents: "Contents"
content: "Page block" content: "Page block"
@ -1346,10 +1355,10 @@ _pages:
if: "If" if: "If"
_if: _if:
variable: "Variable" variable: "Variable"
post: "Compose a note" post: "Posting form"
_post: _post:
text: "Content" text: "Content"
attachCanvasImage: "Post with canvas image" attachCanvasImage: "Attach canvas image"
canvasId: "Canvas ID" canvasId: "Canvas ID"
textInput: "Text input" textInput: "Text input"
_textInput: _textInput:
@ -1385,11 +1394,11 @@ _pages:
_counter: _counter:
name: "Variable name" name: "Variable name"
text: "Title" text: "Title"
inc: "Increase by" inc: "Step"
_button: _button:
text: "Title" text: "Title"
colored: "Colored" colored: "Colored"
action: "Operation when the button is pressed" action: "Behavior when the button is pressed"
_action: _action:
dialog: "Show a dialog" dialog: "Show a dialog"
_dialog: _dialog:
@ -1431,11 +1440,11 @@ _pages:
strLen: "Text length" strLen: "Text length"
_strLen: _strLen:
arg1: "Text" arg1: "Text"
strPick: "Extract character" strPick: "Extract string"
_strPick: _strPick:
arg1: "Text" arg1: "Text"
arg2: "Character location" arg2: "String location"
strReplace: "Text replacement" strReplace: "Replacement string"
_strReplace: _strReplace:
arg1: "Text" arg1: "Text"
arg2: "Text to be replaced" arg2: "Text to be replaced"
@ -1529,7 +1538,7 @@ _pages:
arg2: "Maximum value" arg2: "Maximum value"
dailyRandomPick: "Randomly choose from a list (Changes once a day for each user)" dailyRandomPick: "Randomly choose from a list (Changes once a day for each user)"
_dailyRandomPick: _dailyRandomPick:
arg1: "Lists" arg1: "List"
seedRandom: "Random (with seed)" seedRandom: "Random (with seed)"
_seedRandom: _seedRandom:
arg1: "Seed" arg1: "Seed"
@ -1603,6 +1612,7 @@ _notification:
youReceivedFollowRequest: "You've received a follow request" youReceivedFollowRequest: "You've received a follow request"
yourFollowRequestAccepted: "Your follow request was accepted" yourFollowRequestAccepted: "Your follow request was accepted"
youWereInvitedToGroup: "You've been invited to a group" youWereInvitedToGroup: "You've been invited to a group"
pollEnded: "Poll results have become available"
_types: _types:
all: "All" all: "All"
follow: "New followers" follow: "New followers"
@ -1612,6 +1622,7 @@ _notification:
quote: "Quotes" quote: "Quotes"
reaction: "Reactions" reaction: "Reactions"
pollVote: "Votes on polls" pollVote: "Votes on polls"
pollEnded: "Polls ending"
receiveFollowRequest: "Received follow requests" receiveFollowRequest: "Received follow requests"
followRequestAccepted: "Accepted follow requests" followRequestAccepted: "Accepted follow requests"
groupInvited: "Group invitations" groupInvited: "Group invitations"
@ -1622,12 +1633,12 @@ _deck:
columnMargin: "Margin between columns" columnMargin: "Margin between columns"
columnHeaderHeight: "Column header height" columnHeaderHeight: "Column header height"
addColumn: "Add column" addColumn: "Add column"
swapLeft: "Swap left" swapLeft: "Swap with the left column"
swapRight: "Swap right" swapRight: "Swap with the right column"
swapUp: "Swap with above" swapUp: "Swap with the above column"
swapDown: "Swap with below" swapDown: "Swap with the below column"
stackLeft: "Stack on left column" stackLeft: "Stack with the left column"
popRight: "Pop to the right" popRight: "Pop column to the right"
profile: "Profile" profile: "Profile"
_columns: _columns:
main: "Main" main: "Main"

View file

@ -9,7 +9,7 @@ username: "Uzantnomo"
password: "Pasvorto" password: "Pasvorto"
forgotPassword: "Ĉu vi forgesis pasvorton?" forgotPassword: "Ĉu vi forgesis pasvorton?"
fetchingAsApObject: "Informpetado de la Fediverso…" fetchingAsApObject: "Informpetado de la Fediverso…"
ok: "Bone" ok: "Okej"
gotIt: "Kompreni" gotIt: "Kompreni"
cancel: "Nuligi" cancel: "Nuligi"
enterUsername: "Entajpu uzantnomon" enterUsername: "Entajpu uzantnomon"
@ -71,7 +71,7 @@ lists: "Listoj"
noLists: "Neniu listo" noLists: "Neniu listo"
note: "Noti" note: "Noti"
notes: "Notoj" notes: "Notoj"
following: "Sekvata" following: "Sekvatoj"
followers: "Sekvantoj" followers: "Sekvantoj"
followsYou: "Sekvas vin" followsYou: "Sekvas vin"
createList: "Krei liston" createList: "Krei liston"
@ -224,7 +224,7 @@ resetAreYouSure: "Ĉu vi certas restarigi?"
saved: "Konservita" saved: "Konservita"
messaging: "Retbabili" messaging: "Retbabili"
upload: "Alŝuti" upload: "Alŝuti"
keepOriginalUploading: "Konservi la originalan bildon" keepOriginalUploading: "Konservi originalon"
fromDrive: "De la disko" fromDrive: "De la disko"
fromUrl: "De URL" fromUrl: "De URL"
uploadFromUrl: "Alŝuti de URL" uploadFromUrl: "Alŝuti de URL"
@ -278,6 +278,7 @@ rename: "Alinomi"
avatar: "Bildsimbolo" avatar: "Bildsimbolo"
banner: "Standardo" banner: "Standardo"
nsfw: "Enhavo ne estas deca por laborejo (NSFW)" nsfw: "Enhavo ne estas deca por laborejo (NSFW)"
whenServerDisconnected: "Kiam vi malligiĝas de servilo"
disconnectedFromServer: "Malkonektita de servilo" disconnectedFromServer: "Malkonektita de servilo"
reload: "Reŝargi" reload: "Reŝargi"
doNothing: "Ignori" doNothing: "Ignori"
@ -396,7 +397,6 @@ next: "Sekve"
retype: "Retajpu" retype: "Retajpu"
noteOf: "Noto de {user}" noteOf: "Noto de {user}"
inviteToGroup: "Inviti al grupo" inviteToGroup: "Inviti al grupo"
maxNoteTextLength: "Limnombro de leteroj en noto"
quoteAttached: "Kun citaĵo" quoteAttached: "Kun citaĵo"
quoteQuestion: "Ĉu vi volas aldoni citaĵon?" quoteQuestion: "Ĉu vi volas aldoni citaĵon?"
noMessagesYet: "Ankoraŭ neniu mesaĝo" noMessagesYet: "Ankoraŭ neniu mesaĝo"
@ -420,7 +420,7 @@ signinWith: "Saluti kun {x}"
signinFailed: "Fuŝiĝis ensaluti. Bonvolu kontroli uzantnomon kaj pasvorton." signinFailed: "Fuŝiĝis ensaluti. Bonvolu kontroli uzantnomon kaj pasvorton."
or: "Aŭ" or: "Aŭ"
language: "Lingvo" language: "Lingvo"
uiLanguage: "Lingvo de fasado" uiLanguage: "Lingvo de la fasado"
groupInvited: "Invitita al grupo" groupInvited: "Invitita al grupo"
aboutX: "Pri {x}" aboutX: "Pri {x}"
useOsNativeEmojis: "Uzi la emoĵiojn implicitan de la operaciumo" useOsNativeEmojis: "Uzi la emoĵiojn implicitan de la operaciumo"
@ -648,7 +648,7 @@ high: "Alta"
middle: "Meza" middle: "Meza"
low: "Malalta" low: "Malalta"
emailNotConfiguredWarning: "Vi ne agordis retpoŝtadreso." emailNotConfiguredWarning: "Vi ne agordis retpoŝtadreso."
customCss: "Personecigita CSS" customCss: "Propra CSS"
global: "Malloka" global: "Malloka"
squareAvatars: "Montri bildsimbolon kiel kvadrata" squareAvatars: "Montri bildsimbolon kiel kvadrata"
sent: "Sendi" sent: "Sendi"
@ -683,14 +683,20 @@ hide: "Kaŝi"
leaveGroup: "Eliĝi el la grupo" leaveGroup: "Eliĝi el la grupo"
leaveGroupConfirm: "Ĉu vi certas ke vi volas eliĝi el la grupo {name}?" leaveGroupConfirm: "Ĉu vi certas ke vi volas eliĝi el la grupo {name}?"
welcomeBackWithName: "Bonrevenon, {name}!" welcomeBackWithName: "Bonrevenon, {name}!"
clickToFinishEmailVerification: "Volu klaki [{ok}] por fini la konfirmon de vian retadreson" clickToFinishEmailVerification: "Volu klaki [{ok}] por fini konfirmon de via retadreso."
smartphone: "Saĝtelefono" smartphone: "Saĝtelefono"
tablet: "Platkomputilo" tablet: "Platkomputilo"
auto: "Aŭtomate" auto: "Aŭtomate"
searchByGoogle: "Serĉi en Google-Serĉo"
tenMinutes: "10 minutoj"
oneHour: "1 horo"
oneDay: "1 tago"
oneWeek: "1 semajno"
_emailUnavailable: _emailUnavailable:
used: "La retpoŝto jam estas uzita." used: "La retpoŝto jam estas uzita."
format: "Nevalida formato." format: "Nevalida formato."
disposable: "Dumtempa retpoŝto ne estas uzebla." disposable: "Dumtempa retpoŝto ne estas uzebla."
mx: "Ĉi retpoŝto-servilo ne estas uzebla."
smtp: "Tiu retpoŝta servilo ne respondas" smtp: "Tiu retpoŝta servilo ne respondas"
_ffVisibility: _ffVisibility:
public: "Publika" public: "Publika"
@ -770,9 +776,9 @@ _channel:
usersCount: "{n} partoprenantoj" usersCount: "{n} partoprenantoj"
notesCount: "{n} notoj" notesCount: "{n} notoj"
_menuDisplay: _menuDisplay:
sideFull: "Flanko" sideFull: "Sur la flanko"
sideIcon: "Flanko (bildsimbolo)" sideIcon: "Sur la flanko (bildsimbolo)"
top: "Supro" top: "Sur la supro"
hide: "Kaŝi" hide: "Kaŝi"
_wordMute: _wordMute:
muteWords: "Silentigitaj vortoj" muteWords: "Silentigitaj vortoj"
@ -920,7 +926,6 @@ _postForm:
c: "Kio estas sur via penso?" c: "Kio estas sur via penso?"
d: "Kion vi volas diri?" d: "Kion vi volas diri?"
e: "Komencu skribi tie" e: "Komencu skribi tie"
f: "Atendanta de vi skribon…"
_profile: _profile:
name: "Nomo" name: "Nomo"
username: "Uzantnomo" username: "Uzantnomo"

View file

@ -321,8 +321,6 @@ disablingTimelinesInfo: "Aunque se desactiven estas lineas de tiempo, por conven
registration: "Registro" registration: "Registro"
enableRegistration: "Permitir nuevos registros" enableRegistration: "Permitir nuevos registros"
invite: "Invitar" invite: "Invitar"
proxyRemoteFiles: "Hacer proxy de archivos remotos"
proxyRemoteFilesDescription: "Si activa esta configuración, los archivos remotos no almacenados o borrados por exceso de capacidad serán mostrados via proxy local y generarán una miniatura. Eso no afectará el almacenamiento del servidor."
driveCapacityPerLocalAccount: "Capacidad del drive por usuario local" driveCapacityPerLocalAccount: "Capacidad del drive por usuario local"
driveCapacityPerRemoteAccount: "Capacidad del drive por usuario remoto" driveCapacityPerRemoteAccount: "Capacidad del drive por usuario remoto"
inMb: "En megabytes" inMb: "En megabytes"
@ -418,7 +416,6 @@ next: "Siguiente"
retype: "Intentar de nuevo" retype: "Intentar de nuevo"
noteOf: "Notas de {user}" noteOf: "Notas de {user}"
inviteToGroup: "Invitar al grupo" inviteToGroup: "Invitar al grupo"
maxNoteTextLength: "Límite de caracteres en una nota"
quoteAttached: "Cita añadida" quoteAttached: "Cita añadida"
quoteQuestion: "¿Quiere añadir una cita?" quoteQuestion: "¿Quiere añadir una cita?"
noMessagesYet: "Aún no hay chat" noMessagesYet: "Aún no hay chat"
@ -765,6 +762,8 @@ muteThread: "Ocultar hilo"
unmuteThread: "Mostrar hilo" unmuteThread: "Mostrar hilo"
ffVisibility: "Visibilidad de seguidores y seguidos" ffVisibility: "Visibilidad de seguidores y seguidos"
hide: "Ocultar" hide: "Ocultar"
searchByGoogle: "Buscar"
indefinitely: "Sin límite de tiempo"
_ffVisibility: _ffVisibility:
public: "Publicar" public: "Publicar"
_accountDelete: _accountDelete:

View file

@ -323,8 +323,6 @@ disablingTimelinesInfo: "Même si vous désactivez ces fils, les administrateur
registration: "Sinscrire" registration: "Sinscrire"
enableRegistration: "Autoriser les nouvelles inscriptions" enableRegistration: "Autoriser les nouvelles inscriptions"
invite: "Inviter" invite: "Inviter"
proxyRemoteFiles: "Utiliser les fichiers distants comme proxy"
proxyRemoteFilesDescription: "Si vous activez ce paramètre, les fichiers distants non stockés ou supprimés en raison d'une capacité excédentaire seront affichés via un proxy local et généreront une miniature. Cela n'affectera pas le stockage du serveur."
driveCapacityPerLocalAccount: "Volume du Drive par utilisateur local" driveCapacityPerLocalAccount: "Volume du Drive par utilisateur local"
driveCapacityPerRemoteAccount: "Volume du Drive par utilisateur distant" driveCapacityPerRemoteAccount: "Volume du Drive par utilisateur distant"
inMb: "en mégaoctets" inMb: "en mégaoctets"
@ -420,7 +418,6 @@ next: "Suivant"
retype: "Confirmation" retype: "Confirmation"
noteOf: "Notes de {user}" noteOf: "Notes de {user}"
inviteToGroup: "Inviter dans un groupe" inviteToGroup: "Inviter dans un groupe"
maxNoteTextLength: "Limite du nombre de caractères pour les notes"
quoteAttached: "Avec citation" quoteAttached: "Avec citation"
quoteQuestion: "Souhaitez-vous ajouter une citation ?" quoteQuestion: "Souhaitez-vous ajouter une citation ?"
noMessagesYet: "Pas encore de discussion" noMessagesYet: "Pas encore de discussion"
@ -592,6 +589,7 @@ smtpSecure: "Utiliser SSL/TLS implicitement dans les connexions SMTP"
smtpSecureInfo: "Désactiver cette option lorsque STARTTLS est utilisé" smtpSecureInfo: "Désactiver cette option lorsque STARTTLS est utilisé"
testEmail: "Tester la distribution de courriel" testEmail: "Tester la distribution de courriel"
wordMute: "Filtre de mots" wordMute: "Filtre de mots"
regexpError: "Erreur dexpression régulière"
instanceMute: "Instance en sourdine" instanceMute: "Instance en sourdine"
userSaysSomething: "{name} a dit quelque chose" userSaysSomething: "{name} a dit quelque chose"
makeActive: "Activer" makeActive: "Activer"
@ -620,6 +618,7 @@ reportAbuse: "Signaler"
reportAbuseOf: "Signaler {name}" reportAbuseOf: "Signaler {name}"
fillAbuseReportDescription: "Veuillez expliquer les raisons du signalement. S'il s'agit d'une note précise, veuillez en donner le lien." fillAbuseReportDescription: "Veuillez expliquer les raisons du signalement. S'il s'agit d'une note précise, veuillez en donner le lien."
abuseReported: "Le rapport est envoyé. Merci." abuseReported: "Le rapport est envoyé. Merci."
reporter: "Signalé par"
reporteeOrigin: "Origine du signalement" reporteeOrigin: "Origine du signalement"
reporterOrigin: "Signalé par" reporterOrigin: "Signalé par"
forwardReport: "Transférer le signalement à linstance distante" forwardReport: "Transférer le signalement à linstance distante"
@ -818,6 +817,22 @@ leaveGroup: "Quitter le groupe"
leaveGroupConfirm: "Êtes vous sûr de vouloir quitter \"{name}\" ?" leaveGroupConfirm: "Êtes vous sûr de vouloir quitter \"{name}\" ?"
welcomeBackWithName: "Heureux de vous revoir, {name}" welcomeBackWithName: "Heureux de vous revoir, {name}"
clickToFinishEmailVerification: "Veuillez cliquer sur [{ok}] afin de compléter la vérification par courriel." clickToFinishEmailVerification: "Veuillez cliquer sur [{ok}] afin de compléter la vérification par courriel."
overridedDeviceKind: "Type dappareil"
smartphone: "Smartphone"
tablet: "Tablette"
auto: "Automatique"
themeColor: "Couleur du thème"
size: "Taille"
numberOfColumn: "Nombre de colonnes"
searchByGoogle: "Google"
instanceDefaultLightTheme: "Thème clair par défaut sur toute linstance"
instanceDefaultDarkTheme: "Thème sombre par défaut sur toute linstance"
mutePeriod: "Durée de mise en sourdine"
indefinitely: "Illimité"
tenMinutes: "10 minutes"
oneHour: "1 heure"
oneDay: "1 jour"
oneWeek: "1 semaine"
_emailUnavailable: _emailUnavailable:
used: "Non disponible" used: "Non disponible"
format: "Le format de cette adresse de courriel est invalide" format: "Le format de cette adresse de courriel est invalide"

1
locales/hr-HR.yml Normal file
View file

@ -0,0 +1 @@
---

View file

@ -325,8 +325,6 @@ disablingTimelinesInfo: "Admin dan Moderator akan selalu memiliki akses ke semua
registration: "Pendaftaran" registration: "Pendaftaran"
enableRegistration: "Nyalakan pendaftaran pengguna baru" enableRegistration: "Nyalakan pendaftaran pengguna baru"
invite: "Undang" invite: "Undang"
proxyRemoteFiles: "Proksi berkas remote"
proxyRemoteFilesDescription: "Jika diaktifkan, berkas luar yang (1) tidak disimpan secara lokal atau (2) terhapus dari melewati batas penyimpanan akan diproksi secara lokal (dengan thumbnail). Ini tidak akan mempengaruhi server penyimpanan."
driveCapacityPerLocalAccount: "Kapasitas drive per pengguna lokal" driveCapacityPerLocalAccount: "Kapasitas drive per pengguna lokal"
driveCapacityPerRemoteAccount: "Kapasitas drive per pengguna remote" driveCapacityPerRemoteAccount: "Kapasitas drive per pengguna remote"
inMb: "dalam Megabytes" inMb: "dalam Megabytes"
@ -422,7 +420,6 @@ next: "Selanjutnya"
retype: "Masukkan ulang" retype: "Masukkan ulang"
noteOf: "Catatan milik {user}" noteOf: "Catatan milik {user}"
inviteToGroup: "Undang ke grup" inviteToGroup: "Undang ke grup"
maxNoteTextLength: "Batas karakter catatan"
quoteAttached: "Dikutip" quoteAttached: "Dikutip"
quoteQuestion: "Apakah kamu ingin menambahkan kutipan?" quoteQuestion: "Apakah kamu ingin menambahkan kutipan?"
noMessagesYet: "Tidak ada pesan" noMessagesYet: "Tidak ada pesan"
@ -828,6 +825,8 @@ overridedDeviceKind: "Tipe perangkat"
smartphone: "Ponsel" smartphone: "Ponsel"
tablet: "Tablet" tablet: "Tablet"
auto: "Otomatis" auto: "Otomatis"
searchByGoogle: "Penelusuran"
indefinitely: "Selamanya"
_emailUnavailable: _emailUnavailable:
used: "Alamat surel ini telah digunakan" used: "Alamat surel ini telah digunakan"
format: "Format tidak valid." format: "Format tidak valid."

View file

@ -321,8 +321,6 @@ disablingTimelinesInfo: "Anche se disabiliti queste timeline, gli amministratori
registration: "Iscriviti" registration: "Iscriviti"
enableRegistration: "Permettere nuove registrazioni" enableRegistration: "Permettere nuove registrazioni"
invite: "Invita" invite: "Invita"
proxyRemoteFiles: "Usare file remoti come proxy"
proxyRemoteFilesDescription: "Attivando questa opzione i file remoti non salvati o cancellati perché eccedenti il limite di archiviazione verranno inoltrati tramite proxy, inclusa la generazione di anteprime. Non ha effetto sullo spazio di archiviazione del server."
driveCapacityPerLocalAccount: "Volume del Drive per utente locale" driveCapacityPerLocalAccount: "Volume del Drive per utente locale"
driveCapacityPerRemoteAccount: "Volume del Drive per utente remoto" driveCapacityPerRemoteAccount: "Volume del Drive per utente remoto"
inMb: "in Megabytes" inMb: "in Megabytes"
@ -418,7 +416,6 @@ next: "Avanti"
retype: "Conferma" retype: "Conferma"
noteOf: "Note di {user}" noteOf: "Note di {user}"
inviteToGroup: "Invitare al gruppo" inviteToGroup: "Invitare al gruppo"
maxNoteTextLength: "Lunghezza massima delle note"
quoteAttached: "Citazione allegata" quoteAttached: "Citazione allegata"
quoteQuestion: "Vuoi aggiungere una citazione?" quoteQuestion: "Vuoi aggiungere una citazione?"
noMessagesYet: "Ancora nessuna chat" noMessagesYet: "Ancora nessuna chat"
@ -805,6 +802,8 @@ leaveGroupConfirm: "Uscire da「{name}」?"
useDrawerReactionPickerForMobile: "Mostra sul drawer da dispositivo mobile" useDrawerReactionPickerForMobile: "Mostra sul drawer da dispositivo mobile"
welcomeBackWithName: "Bentornato/a, {name}" welcomeBackWithName: "Bentornato/a, {name}"
clickToFinishEmailVerification: "Fai click su [{ok}] per completare la verifica dell'indirizzo email." clickToFinishEmailVerification: "Fai click su [{ok}] per completare la verifica dell'indirizzo email."
searchByGoogle: "Cerca"
indefinitely: "Non scade"
_emailUnavailable: _emailUnavailable:
used: "Email già in uso" used: "Email già in uso"
format: "Formato email non valido" format: "Formato email non valido"

View file

@ -830,10 +830,18 @@ auto: "自動"
themeColor: "テーマカラー" themeColor: "テーマカラー"
size: "サイズ" size: "サイズ"
numberOfColumn: "列の数" numberOfColumn: "列の数"
searchByGoogle: "ググる" searchByGoogle: "検索"
instanceDefaultLightTheme: "インスタンスデフォルトのライトテーマ" instanceDefaultLightTheme: "インスタンスデフォルトのライトテーマ"
instanceDefaultDarkTheme: "インスタンスデフォルトのダークテーマ" instanceDefaultDarkTheme: "インスタンスデフォルトのダークテーマ"
instanceDefaultThemeDescription: "オブジェクト形式のテーマコードを記入します。" instanceDefaultThemeDescription: "オブジェクト形式のテーマコードを記入します。"
mutePeriod: "ミュートする期限"
indefinitely: "無期限"
tenMinutes: "10分"
oneHour: "1時間"
oneDay: "1日"
oneWeek: "1週間"
reflectMayTakeTime: "反映されるまで時間がかかる場合があります。"
failedToFetchAccountInformation: "アカウント情報の取得に失敗しました"
_emailUnavailable: _emailUnavailable:
used: "既に使用されています" used: "既に使用されています"
@ -1661,6 +1669,7 @@ _notification:
youReceivedFollowRequest: "フォローリクエストが来ました" youReceivedFollowRequest: "フォローリクエストが来ました"
yourFollowRequestAccepted: "フォローリクエストが承認されました" yourFollowRequestAccepted: "フォローリクエストが承認されました"
youWereInvitedToGroup: "グループに招待されました" youWereInvitedToGroup: "グループに招待されました"
pollEnded: "アンケートの結果が出ました"
_types: _types:
all: "すべて" all: "すべて"
@ -1671,6 +1680,7 @@ _notification:
quote: "引用" quote: "引用"
reaction: "リアクション" reaction: "リアクション"
pollVote: "アンケートに投票された" pollVote: "アンケートに投票された"
pollEnded: "アンケートが終了"
receiveFollowRequest: "フォロー申請を受け取った" receiveFollowRequest: "フォロー申請を受け取った"
followRequestAccepted: "フォローが受理された" followRequestAccepted: "フォローが受理された"
groupInvited: "グループに招待された" groupInvited: "グループに招待された"

View file

@ -323,8 +323,6 @@ disablingTimelinesInfo: "ここらへんのタイムラインを使えんよう
registration: "登録" registration: "登録"
enableRegistration: "一見さんでも誰でもいらっしゃ~い" enableRegistration: "一見さんでも誰でもいらっしゃ~い"
invite: "来てや" invite: "来てや"
proxyRemoteFiles: "リモートのファイルをプロキシする"
proxyRemoteFilesDescription: "この設定を有効にしたら、保存してなかったり容量が足らんくて消されたリモートファイルをローカルでプロキシして、サムネイルを作るようになるで。サーバーの容量には関係ないで。"
driveCapacityPerLocalAccount: "ローカルユーザーひとりあたりのドライブ容量" driveCapacityPerLocalAccount: "ローカルユーザーひとりあたりのドライブ容量"
driveCapacityPerRemoteAccount: "リモートユーザーひとりあたりのドライブ容量" driveCapacityPerRemoteAccount: "リモートユーザーひとりあたりのドライブ容量"
inMb: "メガバイト単位" inMb: "メガバイト単位"
@ -417,7 +415,6 @@ next: "次"
retype: "もっかい入力" retype: "もっかい入力"
noteOf: "{user}のノート" noteOf: "{user}のノート"
inviteToGroup: "グループに招く" inviteToGroup: "グループに招く"
maxNoteTextLength: "ノートの文字数制限"
quoteAttached: "引用付いとるで" quoteAttached: "引用付いとるで"
quoteQuestion: "引用として添付してもええか?" quoteQuestion: "引用として添付してもええか?"
noMessagesYet: "まだチャットはあらへんで" noMessagesYet: "まだチャットはあらへんで"
@ -658,6 +655,8 @@ global: "グローバル"
sent: "送信" sent: "送信"
hashtags: "ハッシュタグ" hashtags: "ハッシュタグ"
hide: "隠す" hide: "隠す"
searchByGoogle: "探す"
indefinitely: "無期限"
_ad: _ad:
back: "戻る" back: "戻る"
_gallery: _gallery:

View file

@ -55,6 +55,7 @@ accountInfo: "Talɣut n umiḍan"
emailNotification: "Ilɣa imayl" emailNotification: "Ilɣa imayl"
selectAccount: "Fren amiḍan" selectAccount: "Fren amiḍan"
accounts: "Imiḍan" accounts: "Imiḍan"
searchByGoogle: "Nadi"
_email: _email:
_follow: _follow:
title: "Yeṭṭafaṛ-ik·em-id" title: "Yeṭṭafaṛ-ik·em-id"

View file

@ -59,6 +59,7 @@ remove: "ಅಳಿಸು"
smtpUser: "ಬಳಕೆಹೆಸರು" smtpUser: "ಬಳಕೆಹೆಸರು"
smtpPass: "ಗುಪ್ತಪದ" smtpPass: "ಗುಪ್ತಪದ"
user: "ಬಳಕೆದಾರ" user: "ಬಳಕೆದಾರ"
searchByGoogle: "ಹುಡುಕು"
_email: _email:
_follow: _follow:
title: "ಹಿಂಬಾಲಿಸಿದರು" title: "ಹಿಂಬಾಲಿಸಿದರು"

View file

@ -325,8 +325,6 @@ disablingTimelinesInfo: "특정 타임라인을 비활성화하더라도 관리
registration: "등록" registration: "등록"
enableRegistration: "신규 회원가입을 활성화" enableRegistration: "신규 회원가입을 활성화"
invite: "초대" invite: "초대"
proxyRemoteFiles: "리모트 파일 프록시"
proxyRemoteFilesDescription: "이 설정을 활성화할 경우, 저장되지 않았거나 저장용량 초과로 삭제된 리모트 파일을 로컬에서 프록시하여 썸네일을 생성하게 됩니다. 서버의 스토리지에는 영향을 주지 않습니다."
driveCapacityPerLocalAccount: "로컬 유저 한 명당 드라이브 용량" driveCapacityPerLocalAccount: "로컬 유저 한 명당 드라이브 용량"
driveCapacityPerRemoteAccount: "리모트 유저 한 명당 드라이브 용량" driveCapacityPerRemoteAccount: "리모트 유저 한 명당 드라이브 용량"
inMb: "메가바이트 단위" inMb: "메가바이트 단위"
@ -422,7 +420,6 @@ next: "다음"
retype: "다시 입력" retype: "다시 입력"
noteOf: "{user}의 노트" noteOf: "{user}의 노트"
inviteToGroup: "그룹에 초대하기" inviteToGroup: "그룹에 초대하기"
maxNoteTextLength: "노트의 문자 수 제한"
quoteAttached: "인용함" quoteAttached: "인용함"
quoteQuestion: "인용해서 작성하시겠습니까?" quoteQuestion: "인용해서 작성하시겠습니까?"
noMessagesYet: "아직 대화가 없습니다" noMessagesYet: "아직 대화가 없습니다"
@ -828,6 +825,8 @@ overridedDeviceKind: "장치 유형"
smartphone: "스마트폰" smartphone: "스마트폰"
tablet: "태블릿" tablet: "태블릿"
auto: "자동" auto: "자동"
searchByGoogle: "검색"
indefinitely: "무기한"
_emailUnavailable: _emailUnavailable:
used: "이 메일 주소는 사용중입니다" used: "이 메일 주소는 사용중입니다"
format: "형식이 올바르지 않습니다" format: "형식이 올바르지 않습니다"

View file

@ -256,6 +256,7 @@ user: "Gebruikers"
muteThread: "Discussies dempen " muteThread: "Discussies dempen "
unmuteThread: "Dempen van discussie ongedaan maken" unmuteThread: "Dempen van discussie ongedaan maken"
hide: "Verbergen" hide: "Verbergen"
searchByGoogle: "Zoeken"
_email: _email:
_follow: _follow:
title: "volgde jou" title: "volgde jou"

View file

@ -320,8 +320,6 @@ disablingTimelinesInfo: "Administratorzy i moderatorzy będą zawsze mieć dost
registration: "Zarejestruj się" registration: "Zarejestruj się"
enableRegistration: "Włącz rejestrację nowych użytkowników" enableRegistration: "Włącz rejestrację nowych użytkowników"
invite: "Zaproś" invite: "Zaproś"
proxyRemoteFiles: "Przekierowuj pliki obcych instancji przez proxy"
proxyRemoteFilesDescription: "Gdy ta opcja jest włączona, zdalne pliki które nie są przechowywane lokalnie, lub zostały usunięte z powodu przekroczenia limitu miejsca będą kierowane przez proxy, razem z generowaniem miniatur. Nie ma to żadnego wpływu na przestrzeń dyskową serwera."
driveCapacityPerLocalAccount: "Powierzchnia dyskowa na lokalnego użytkownika" driveCapacityPerLocalAccount: "Powierzchnia dyskowa na lokalnego użytkownika"
driveCapacityPerRemoteAccount: "Powierzchnia dyskowa na zdalnego użytkownika" driveCapacityPerRemoteAccount: "Powierzchnia dyskowa na zdalnego użytkownika"
inMb: "W megabajtach" inMb: "W megabajtach"
@ -417,7 +415,6 @@ next: "Dalej"
retype: "Wprowadź ponownie" retype: "Wprowadź ponownie"
noteOf: "Wpisy {user}" noteOf: "Wpisy {user}"
inviteToGroup: "Zaproś do grupy" inviteToGroup: "Zaproś do grupy"
maxNoteTextLength: "Limit znaków dla wpisów"
quoteAttached: "Zacytowano" quoteAttached: "Zacytowano"
quoteQuestion: "Czy na pewno chcesz umieścić cytat?" quoteQuestion: "Czy na pewno chcesz umieścić cytat?"
noMessagesYet: "Nie napisano jeszcze wiadomości" noMessagesYet: "Nie napisano jeszcze wiadomości"
@ -761,6 +758,8 @@ received: "Otrzymane"
hashtags: "Hashtag" hashtags: "Hashtag"
pubSub: "Konta Pub/Sub" pubSub: "Konta Pub/Sub"
hide: "Ukryj" hide: "Ukryj"
searchByGoogle: "Szukaj"
indefinitely: "Nigdy"
_ffVisibility: _ffVisibility:
public: "Publikuj" public: "Publikuj"
_ad: _ad:

View file

@ -61,6 +61,7 @@ pinnedNotes: "Post fixado"
smtpUser: "Nome de usuário" smtpUser: "Nome de usuário"
smtpPass: "Senha" smtpPass: "Senha"
user: "Usuários" user: "Usuários"
searchByGoogle: "Pesquisar"
_email: _email:
_follow: _follow:
title: "Você tem um novo seguidor" title: "Você tem um novo seguidor"

556
locales/ro-RO.yml Normal file
View file

@ -0,0 +1,556 @@
---
_lang_: "Română"
headlineMisskey: "O rețea conectată prin note"
introMisskey: "Bine ai venit! Misskey este un serviciu de microblogging open source și decentralizat.\nCreează \"note\" cu care să îți poți împărți gândurile cu oricine din jurul tău. 📡\nCu \"reacții\" îți poți expirma rapid părerea despre notele oricui. 👍\nHai să explorăm o lume nouă! 🚀"
monthAndDay: "{day}/{month}"
search: "Caută"
notifications: "Notificări"
username: "Nume de utilizator"
password: "Parolă"
forgotPassword: "Am uitat parola"
fetchingAsApObject: "Se aduce din Fediverse..."
ok: "OK"
gotIt: "Am înțeles!"
cancel: "Anulează"
enterUsername: "Introdu numele de utilizator"
renotedBy: "Re-notat de {user}"
noNotes: "Nicio notă"
noNotifications: "Nicio notificare"
instance: "Instanță"
settings: "Setări"
basicSettings: "Setări generale"
otherSettings: "Alte Setări"
openInWindow: "Deschide într-o fereastră"
profile: "Profil"
timeline: "Cronologie"
noAccountDescription: "Acest utilizator încă nu a scris un bio."
login: "Autentifică-te"
loggingIn: "Se autentifică"
logout: "Deconectează-te"
signup: "Înregistrează-te"
uploading: "Se încarcă"
save: "Salvează"
users: "Utilizatori"
addUser: "Adăugă utilizator"
favorite: "Adaugă la favorite"
favorites: "Favorite"
unfavorite: "Elimină din favorite"
favorited: "Adăugat la favorite."
alreadyFavorited: "Deja adăugat la favorite."
cantFavorite: "Nu se poate adăuga la favorite."
pin: "Fixează pe profil"
unpin: "Anulati fixare"
copyContent: "Copiază conținutul"
copyLink: "Copiază link-ul"
delete: "Şterge"
deleteAndEdit: "Șterge și editează"
deleteAndEditConfirm: "Ești sigur că vrei să ștergi această notă și să o editezi? Vei pierde reacțiile, re-notele și răspunsurile acesteia."
addToList: "Adaugă în listă"
sendMessage: "Trimite un mesaj"
copyUsername: "Copiază numele de utilizator"
searchUser: "Caută un utilizator"
reply: "Răspunde"
loadMore: "Incarcă mai mult"
showMore: "Arată mai mult"
youGotNewFollower: "te-a urmărit"
receiveFollowRequest: "Cerere de urmărire primită"
followRequestAccepted: "Cerere de urmărire acceptată"
mention: "Mențiune"
mentions: "Mențiuni"
directNotes: "Note directe"
importAndExport: "Importă / Exportă"
import: "Importă"
export: "Exportă"
files: "Fișiere"
download: "Descarcă"
driveFileDeleteConfirm: "Ești sigur ca vrei să ștergi fișierul \"{name}\"? Notele atașate fișierului vor fi șterse și ele."
unfollowConfirm: "Ești sigur ca vrei să nu mai urmărești pe {name}?"
exportRequested: "Ai cerut un export. S-ar putea să ia un pic. Va fi adăugat in Drive-ul tău odată completat."
importRequested: "Ai cerut un import. S-ar putea să ia un pic."
lists: "Liste"
noLists: "Nu ai nici o listă"
note: "Notă"
notes: "Note"
following: "Urmărești"
followers: "Urmăritori"
followsYou: "Te urmărește"
createList: "Creează listă"
manageLists: "Gestionează listele"
error: "Eroare"
somethingHappened: "A survenit o eroare"
retry: "Reîncearcă"
pageLoadError: "A apărut o eroare la încărcarea paginii."
pageLoadErrorDescription: "De obicei asta este cauzat de o eroare de rețea sau cache-ul browser-ului. Încearcă să cureți cache-ul și apoi să încerci din nou puțin mai târziu."
serverIsDead: "Serverul nu răspunde. Te rugăm să aștepți o perioadă și să încerci din nou."
youShouldUpgradeClient: "Pentru a vedea această pagină, te rugăm să îți actualizezi clientul."
enterListName: "Introdu un nume pentru listă"
privacy: "Confidenţialitate"
makeFollowManuallyApprove: "Fă cererile de urmărire să necesite aprobare"
defaultNoteVisibility: "Vizibilitate implicită"
follow: "Urmărești"
followRequest: "Trimite cerere de urmărire"
followRequests: "Cereri de urmărire"
unfollow: "Nu mai urmări"
followRequestPending: "Cerere de urmărire în așteptare"
enterEmoji: "Introdu un emoji"
renote: "Re-notează"
unrenote: "Ia înapoi re-nota"
renoted: "Re-notat."
cantRenote: "Această postare nu poate fi re-notată."
cantReRenote: "O re-notă nu poate fi re-notată."
quote: "Citează"
pinnedNote: "Notă fixată"
pinned: "Fixat pe profil"
you: "Tu"
clickToShow: "Click pentru a afișa"
sensitive: "NSFW"
add: "Adaugă"
reaction: "Reacție"
reactionSetting: "Reacții care să apară in selectorul de reacții"
reactionSettingDescription2: "Trage pentru a rearanja, apasă pe \"+\" pentru a adăuga."
rememberNoteVisibility: "Amintește setarea de vizibilitate a notelor"
attachCancel: "Înlătură atașament"
markAsSensitive: "Marchează ca NSFW"
unmarkAsSensitive: "Demarchează ca NSFW"
enterFileName: "Introduceţi numele fişierului"
mute: "Amuțește"
unmute: "Înlătură amuțirea"
block: "Blochează"
unblock: "Deblochează"
suspend: "Suspendă"
unsuspend: "Anulează suspendare"
blockConfirm: "Ești sigur că vrei să blochezi acest cont?"
unblockConfirm: "Ești sigur ca vrei să deblochezi acest cont?"
suspendConfirm: "Ești sigur ca vrei să suspendezi acest cont?"
unsuspendConfirm: "Ești sigur ca vrei să nu mai suspendezi acest cont?"
selectList: "Selectează o listă"
selectAntenna: "Selectează o antenă"
selectWidget: "Selectați un widget"
editWidgets: "Editează widget-urile"
editWidgetsExit: "Terminat"
customEmojis: "Emoji personalizat"
emoji: "Emoji"
emojis: "Emoji-uri"
emojiName: "Numele emoji-ului"
emojiUrl: "URL-ul emoji-ului"
addEmoji: "Adaugă un emoji"
settingGuide: "Setări recomandate"
cacheRemoteFiles: "Ține fișierele externe in cache"
cacheRemoteFilesDescription: "Când această setare este dezactivată, fișierele externe sunt încărcate direct din instanța externă. Dezactivarea va scădea utilizarea spațiului de stocare, dar va crește traficul, deoarece thumbnail-urile nu vor fi generate."
flagAsBot: "Marchează acest cont ca bot"
flagAsBotDescription: "Activează această opțiune dacă acest cont este controlat de un program. Daca e activată, aceasta va juca rolul unui indicator pentru dezvoltatori pentru a preveni interacțiunea în lanțuri infinite cu ceilalți boți și ajustează sistemele interne al Misskey pentru a trata acest cont drept un bot."
flagAsCat: "Marchează acest cont ca pisică"
flagAsCatDescription: "Activează această opțiune dacă acest cont este o pisică."
flagShowTimelineReplies: "Arată răspunsurile în cronologie"
flagShowTimelineRepliesDescription: "Dacă e activată vor fi arătate în cronologie răspunsurile utilizatorilor către alte notele altor utilizatori."
autoAcceptFollowed: "Aprobă automat cererile de urmărire de la utilizatorii pe care îi urmărești"
addAccount: "Adaugă un cont"
loginFailed: "Autentificare eșuată"
showOnRemote: "Vezi mai multe pe instanța externă"
general: "General"
wallpaper: "Imagine de fundal"
setWallpaper: "Setați imaginea de fundal"
removeWallpaper: "Șterge imagine de fundal"
searchWith: "Caută: {q}"
youHaveNoLists: "Nu ai nici o listă"
followConfirm: "Ești sigur ca vrei să urmărești pe {name}?"
proxyAccount: "Cont proxy"
proxyAccountDescription: "Un cont proxy este un cont care se comportă ca un urmăritor extern pentru utilizatorii puși sub anumite condiții. De exemplu, când un cineva adaugă un utilizator extern intr-o listă, activitatea utilizatorului extern nu va fi adusă în instanță daca nici un utilizator local nu urmărește acel utilizator, așa că în schimb contul proxy îl va urmări."
host: "Gazdă"
selectUser: "Selectează un utilizator"
recipient: "Destinatar"
annotation: "Adnotări"
federation: "Federație"
instances: "Instanțe"
registeredAt: "Înregistrat în"
latestRequestSentAt: "Ultima cerere trimisă"
latestRequestReceivedAt: "Ultima cerere primită"
latestStatus: "Ultimul status"
storageUsage: "Utilizare stocare"
charts: "Diagrame"
perHour: "Pe oră"
perDay: "Pe zi"
stopActivityDelivery: "Nu mai trimite activități"
blockThisInstance: "Blochează această instanță"
operations: "Operațiuni"
software: "Software"
version: "Versiune"
metadata: "Metadata"
withNFiles: "{n} fișier(e)"
monitor: "Monitor"
jobQueue: "coada de job-uri"
cpuAndMemory: "CPU și memorie"
network: "Rețea"
disk: "Disk"
instanceInfo: "Informații despre instanță"
statistics: "Statistici"
clearQueue: "Șterge coada"
clearQueueConfirmTitle: "Ești sigur că vrei să cureți coada?"
clearQueueConfirmText: "Orice notă rămasă în coadă nu va fi federată. De obicei această operație nu este necesară."
clearCachedFiles: "Golește cache-ul"
clearCachedFilesConfirm: "Ești sigur că vrei să ștergi toate fișierele externe din cache?"
blockedInstances: "Instanțe blocate"
blockedInstancesDescription: "Scrie hostname-urile instanțelor pe care dorești să le blochezi. Instanțele listate nu vor mai putea să comunice cu această instanță."
muteAndBlock: "Amuțiri și Blocări"
mutedUsers: "Utilizatori amuțiți"
blockedUsers: "Utilizatori blocați"
noUsers: "Niciun utilizator"
editProfile: "Editează profilul"
noteDeleteConfirm: "Ești sigur că vrei să ștergi această notă?"
pinLimitExceeded: "Nu poți mai fixa mai multe note"
intro: "Misskey s-a instalat! Te rog crează un utilizator admin."
done: "Gata"
processing: "Se procesează"
preview: "Previzualizare"
default: "Prestabilit"
noCustomEmojis: "Nu e niciun emoji"
noJobs: "Nu e niciun job"
federating: "Federație"
blocked: "Blocat"
suspended: "Suspendat"
all: "Tot"
subscribing: "Abonare"
publishing: "Publicare"
notResponding: "Nu răspunde"
instanceFollowing: "Urmărind în instanță"
instanceFollowers: "Urmăritori ai instanței"
instanceUsers: "Utilizatori ai acestei instanțe"
changePassword: "Schimbă parolă"
security: "Securitate"
retypedNotMatch: "Intrările nu corespund"
currentPassword: "Parola curentă"
newPassword: "Parola nouă"
newPasswordRetype: "Rescrie parola nouă"
attachFile: "Atașează fișiere"
more: "Mai mult!"
featured: "Evidențiat"
usernameOrUserId: "Nume sau ID de utilizator"
noSuchUser: "Utilizatorul nu a fost găsit"
lookup: "Privire"
announcements: "Anunțuri"
imageUrl: "URL-ul imaginii"
remove: "Şterge"
removed: "Șterș cu succes"
removeAreYouSure: "Ești sigur că vrei să înlături {x}?"
deleteAreYouSure: "Ești sigur că vrei să ștergi {x}?"
resetAreYouSure: "Sigur vrei să resetezi?"
saved: "Salvat"
messaging: "Chat"
upload: "Încarcă"
keepOriginalUploading: "Păstrează imaginea originală"
keepOriginalUploadingDescription: "Salvează imaginea originala încărcată fără modificări. Dacă e oprită, o versiune pentru afișarea pe web va fi generată la încărcare."
fromDrive: "Din Drive"
fromUrl: "Din URL"
uploadFromUrl: "Încarcă dintr-un URL"
uploadFromUrlDescription: "URL-ul fișierului pe care dorești să îl încarci"
uploadFromUrlRequested: "Încărcare solicitată"
uploadFromUrlMayTakeTime: "S-ar putea să ia puțin până se finalizează încărcarea."
explore: "Explorează"
messageRead: "Citit"
noMoreHistory: "Nu există mai mult istoric"
startMessaging: "Începe un chat nou"
nUsersRead: "citit de {n}"
agreeTo: "Sunt de acord cu {0}"
tos: "Termenii de utilizare"
start: "Să începem"
home: "Acasă"
remoteUserCaution: "Deoarece acest utilizator este dintr-o instanță externă, informația afișată poate fi incompletă."
activity: "Activitate"
images: "Imagini"
birthday: "Zi de naștere"
yearsOld: "{age} ani"
registeredDate: "Data înregistrării"
location: "Locație"
theme: "Teme"
themeForLightMode: "Temă folosită pentru Modul Luminat"
themeForDarkMode: "Temă folosită pentru Modul Întunecat"
light: "Luminos"
dark: "Întunecat"
lightThemes: "Teme luminoase"
darkThemes: "Teme întunecate"
syncDeviceDarkMode: "Sincronizează Modul Întunecat cu setările dispozitivului"
drive: "Drive"
fileName: "Nume fișier"
selectFile: "Alege un fisier"
selectFiles: "Alege fișiere"
selectFolder: "Selectează un folder"
selectFolders: "Selectează folderele"
renameFile: "Redenumește fișier"
folderName: "Nume folder"
createFolder: "Crează folder"
renameFolder: "Redenumește acest folder"
deleteFolder: "Șterge acest folder"
addFile: "Adăugați un fișier"
emptyDrive: "Drive-ul tău e gol"
emptyFolder: "Folder-ul acesta este gol"
unableToDelete: "Nu se poate șterge"
inputNewFileName: "Introdu un nou nume de fișier"
inputNewDescription: "Introdu o descriere nouă"
inputNewFolderName: "Introdu un nume de folder nou"
circularReferenceFolder: "Destinația folderului este un subfolder al folderului pe care dorești să îl muți."
hasChildFilesOrFolders: "Acest folder nu este gol, așa că nu poate fi șters."
copyUrl: "Copiază URL"
rename: "Redenumește"
avatar: "Avatar"
banner: "Banner"
nsfw: "NSFW"
whenServerDisconnected: "Când pierzi conexiunea cu serverul"
disconnectedFromServer: "Conecțiunea cu serverul a fost pierdută"
reload: "Reîncarcă"
doNothing: "Ignoră"
reloadConfirm: "Ai dori să reîmprospătezi cronologia?"
watch: "Vezi"
unwatch: "Oprește-te din văzut"
accept: "Acceptă"
reject: "Respinge"
normal: "Normal"
instanceName: "Numele instanței"
instanceDescription: "Descrierea instanței"
maintainerName: "Administrator"
maintainerEmail: "Email-ul administratorului"
tosUrl: "URL-ul Termenilor de utilizare"
thisYear: "An"
thisMonth: "Lună"
today: "Azi"
dayX: "{day}"
monthX: "{month}"
yearX: "{year}"
pages: "Pagini"
integration: "Integrare"
connectService: "Conectează"
disconnectService: "Deconectează"
enableLocalTimeline: "Activează cronologia locală"
enableGlobalTimeline: "Activeaza cronologia globală"
disablingTimelinesInfo: "Administratorii și Moderatorii vor avea mereu access la toate cronologiile, chiar dacă nu sunt activate."
registration: "Inregistrare"
enableRegistration: "Activează înregistrările pentru utilizatori noi"
invite: "Invită"
driveCapacityPerLocalAccount: "Capacitatea Drive-ului per utilizator local"
driveCapacityPerRemoteAccount: "Capacitatea Drive-ului per utilizator extern"
inMb: "În megabytes"
iconUrl: "URL-ul iconiței"
bannerUrl: "URL-ul imaginii de banner"
backgroundImageUrl: "URL-ul imaginii de fundal"
basicInfo: "Informații de bază"
pinnedUsers: "Utilizatori fixați"
pinnedUsersDescription: "Scrie utilizatorii, separați prin pauză de rând, care vor fi fixați pe pagina \"Explorează\"."
pinnedPages: "Pagini fixate"
pinnedPagesDescription: "Introdu linkurile Paginilor pe care le vrei fixate in vâruful paginii acestei instanțe, separate de pauze de rând."
pinnedClipId: "ID-ul clip-ului pe care să îl fixezi"
pinnedNotes: "Notă fixată"
hcaptcha: "hCaptcha"
enableHcaptcha: "Activează hCaptcha"
hcaptchaSiteKey: "Site key"
hcaptchaSecretKey: "Secret key"
recaptcha: "reCAPTCHA"
enableRecaptcha: "Activează reCAPTCHA"
recaptchaSiteKey: "Site key"
recaptchaSecretKey: "Secret key"
avoidMultiCaptchaConfirm: "Folosirea mai multor sisteme Captcha poate cauza interferență între acestea. Ai dori să dezactivezi alte sisteme Captcha acum active? Dacă preferi să rămână activate, apasă Anulare."
antennas: "Antene"
manageAntennas: "Gestionează Antenele"
name: "Nume"
antennaSource: "Sursa antenei"
antennaKeywords: "Cuvinte cheie ascultate"
antennaExcludeKeywords: "Cuvinte cheie excluse"
antennaKeywordsDescription: "Separă cu spații pentru o condiție ȘI sau cu o întrerupere de rând pentru o condiție SAU."
notifyAntenna: "Notifică-mă pentru note noi"
withFileAntenna: "Doar note cu fișiere"
enableServiceworker: "Activează ServiceWorker"
antennaUsersDescription: "Scrie un nume de utilizator per linie"
caseSensitive: "Sensibil la majuscule și minuscule"
withReplies: "Include răspunsuri"
connectedTo: "Următoarele conturi sunt conectate"
notesAndReplies: "Note și răspunsuri"
withFiles: "Incluzând fișiere"
silence: "Amuțește"
silenceConfirm: "Ești sigur că vrei să amuțești acest utilizator?"
unsilence: "Anulează amuțirea"
unsilenceConfirm: "Ești sigur că vrei să anulezi amuțirea acestui utilizator?"
popularUsers: "Utilizatori populari"
recentlyUpdatedUsers: "Utilizatori activi recent"
recentlyRegisteredUsers: "Utilizatori ce s-au alăturat recent"
recentlyDiscoveredUsers: "Utilizatori descoperiți recent"
exploreUsersCount: "Aici sunt {count} utilizatori"
exploreFediverse: "Explorează Fediverse-ul"
popularTags: "Taguri populare"
userList: "Liste"
about: "Despre"
aboutMisskey: "Despre Misskey"
administrator: "Administrator"
token: "Token"
twoStepAuthentication: "Autentificare în doi pași"
moderator: "Moderator"
nUsersMentioned: "Menționat de {n} utilizatori"
securityKey: "Cheie de securitate"
securityKeyName: "Numele cheii"
registerSecurityKey: "Înregistrează o cheie de securitate"
lastUsed: "Ultima utilizată"
unregister: "Dezînregistrează"
passwordLessLogin: "Autentificare fără parolă"
resetPassword: "Resetează parola"
newPasswordIs: "Noua parolă este \"{password}\""
reduceUiAnimation: "Redu animațiile interfeței"
share: "Distribuie"
notFound: "Nu a fost găsit"
notFoundDescription: "N-a fost găsită nicio pagină cu acest URL."
uploadFolder: "Folder implicit pentru încărcări"
cacheClear: "Golește cache-ul"
markAsReadAllNotifications: "Marchează toate notificările drept citit"
markAsReadAllUnreadNotes: "Marchează toate notele drept citit"
markAsReadAllTalkMessages: "Marchează toate mesajele drept citit"
help: "Ajutor"
inputMessageHere: "Introdu un mesaj aici"
close: "Închide"
group: "Grup"
groups: "Grupuri"
createGroup: "Crează un grup"
ownedGroups: "Grupuri deținute"
joinedGroups: "Grupuri alăturate"
invites: "Invită"
groupName: "Numele grupului"
members: "Membri"
transfer: "Transferă"
messagingWithUser: "Chat privat"
messagingWithGroup: "Chat de grup"
title: "Titlu"
text: "Text"
enable: "Activează"
next: "Următorul"
retype: "Introdu din nou"
noteOf: "Notă de {user}"
inviteToGroup: "Invită în grup"
quoteAttached: "Citat"
quoteQuestion: "Vrei să adaugi ca citat?"
noMessagesYet: "Niciun mesaj încă"
newMessageExists: "Ai mesaje noi"
onlyOneFileCanBeAttached: "Poți atașa un singur fișier la un mesaj"
signinRequired: "Te rog autentifică-te"
invitations: "Invită"
invitationCode: "Cod de invitație"
checking: "Se verifică..."
available: "Disponibil"
unavailable: "Indisponibil"
usernameInvalidFormat: "Poți folosi litere mari și mici, numere și underscore-uri."
tooShort: "Prea scurt"
tooLong: "Prea lung"
weakPassword: "Parolă slabă"
normalPassword: "Parolă medie"
strongPassword: "Parolă puternică"
passwordMatched: "Se potrivește!"
passwordNotMatched: "Nu se potrivește"
signinWith: "Autentifică-te cu {x}"
signinFailed: "Nu se poate autentifica. Numele de utilizator sau parola introduse sunt incorecte."
tapSecurityKey: "Apasă pe cheia ta de securitate."
or: "Sau"
language: "Limbă"
uiLanguage: "Limba interfeței"
groupInvited: "Ai fost invitat într-un grup"
aboutX: "Despre {x}"
useOsNativeEmojis: "Folosește emojiuri native OS-ului"
disableDrawer: "Nu folosi meniuri în stil sertar"
sounds: "Sunete"
listen: "Ascultă"
none: "Nimic"
showInPage: "Arată în pagină"
popout: "Scoate în afară"
volume: "Volum"
masterVolume: "Volumul principal"
details: "Detalii"
chooseEmoji: "Alege un emoji"
unableToProcess: "Această operație nu poate fi completată"
recentUsed: "Folosit recent"
install: "Instalează"
uninstall: "Dezinstalează"
installedApps: "Aplicații autorizate"
nothing: "Nu e nimic de văzut aici"
installedDate: "Autorizat la data de"
lastUsedDate: "Folosit ultima oara la"
state: "Stare"
sort: "Sortează"
ascendingOrder: "Crescător"
descendingOrder: "Descrescător"
scratchpad: "Scratchpad"
smtpHost: "Gazdă"
smtpUser: "Nume de utilizator"
smtpPass: "Parolă"
clearCache: "Golește cache-ul"
info: "Despre"
user: "Utilizatori"
searchByGoogle: "Caută"
_email:
_follow:
title: "te-a urmărit"
_mfm:
mention: "Mențiune"
quote: "Citează"
emoji: "Emoji personalizat"
search: "Caută"
_theme:
keys:
mention: "Mențiune"
renote: "Re-notează"
_sfx:
note: "Note"
notification: "Notificări"
chat: "Chat"
_widgets:
notifications: "Notificări"
timeline: "Cronologie"
activity: "Activitate"
federation: "Federație"
jobQueue: "coada de job-uri"
_cw:
show: "Incarcă mai mult"
_visibility:
home: "Acasă"
followers: "Urmăritori"
_profile:
name: "Nume"
username: "Nume de utilizator"
_exportOrImport:
followingList: "Urmărești"
muteList: "Amuțește"
blockingList: "Blochează"
userLists: "Liste"
_charts:
federation: "Federație"
_timelines:
home: "Acasă"
_pages:
blocks:
image: "Imagini"
script:
categories:
list: "Liste"
blocks:
_join:
arg1: "Liste"
_randomPick:
arg1: "Liste"
_dailyRandomPick:
arg1: "Liste"
_seedRandomPick:
arg2: "Liste"
_pick:
arg1: "Liste"
_listLen:
arg1: "Liste"
types:
array: "Liste"
_notification:
youWereFollowed: "te-a urmărit"
youWereInvitedToGroup: "Ai fost invitat într-un grup"
_types:
follow: "Urmărești"
mention: "Mențiune"
renote: "Re-notează"
quote: "Citează"
reaction: "Reacție"
_deck:
_columns:
notifications: "Notificări"
tl: "Cronologie"
antenna: "Antene"
list: "Liste"
mentions: "Mențiuni"

View file

@ -322,8 +322,6 @@ disablingTimelinesInfo: "У администраторов и модератор
registration: "Регистрация" registration: "Регистрация"
enableRegistration: "Разрешить регистрацию" enableRegistration: "Разрешить регистрацию"
invite: "Пригласить" invite: "Пригласить"
proxyRemoteFiles: "Файлы с других сайтов пускать через прокси"
proxyRemoteFilesDescription: "Когда эта настройка включена, файлы с других серверов, которые не сохранены или удалены для освобождения места, будут проксироваться локально, а так же для них будут создаваться миниатюры. Эта настройка не затрагивает хранение на сервере."
driveCapacityPerLocalAccount: "Объём диска на одного локального пользователя" driveCapacityPerLocalAccount: "Объём диска на одного локального пользователя"
driveCapacityPerRemoteAccount: "Объём диска на одного пользователя с другого сайта" driveCapacityPerRemoteAccount: "Объём диска на одного пользователя с другого сайта"
inMb: "В мегабайтах" inMb: "В мегабайтах"
@ -419,7 +417,6 @@ next: "Дальше"
retype: "Введите ещё раз" retype: "Введите ещё раз"
noteOf: "Что пишет {user}" noteOf: "Что пишет {user}"
inviteToGroup: "Пригласить в группу" inviteToGroup: "Пригласить в группу"
maxNoteTextLength: "Максимальная длина текста"
quoteAttached: "Цитата" quoteAttached: "Цитата"
quoteQuestion: "Хотите добавить цитату?" quoteQuestion: "Хотите добавить цитату?"
noMessagesYet: "Пока ни одного сообщения" noMessagesYet: "Пока ни одного сообщения"
@ -818,6 +815,8 @@ leaveGroupConfirm: "Покинуть группу «{name}»?"
useDrawerReactionPickerForMobile: "Выдвижная палитра на мобильном устройстве" useDrawerReactionPickerForMobile: "Выдвижная палитра на мобильном устройстве"
welcomeBackWithName: "С возвращением, {name}!" welcomeBackWithName: "С возвращением, {name}!"
clickToFinishEmailVerification: "Пожалуйста, нажмите [{ok}], чтобы завершить подтверждение адреса электронной почты." clickToFinishEmailVerification: "Пожалуйста, нажмите [{ok}], чтобы завершить подтверждение адреса электронной почты."
searchByGoogle: "Поиск"
indefinitely: "вечно"
_emailUnavailable: _emailUnavailable:
used: "Уже используется" used: "Уже используется"
format: "Неверный формат" format: "Неверный формат"

1
locales/si-LK.yml Normal file
View file

@ -0,0 +1 @@
---

View file

@ -325,8 +325,6 @@ disablingTimelinesInfo: "Administrátori a moderátori majú vždy prístup ku v
registration: "Registrácia" registration: "Registrácia"
enableRegistration: "Povoliť registráciu nových používateľov" enableRegistration: "Povoliť registráciu nových používateľov"
invite: "Pozvať" invite: "Pozvať"
proxyRemoteFiles: "Proxy vzdialených súborov"
proxyRemoteFilesDescription: "Ak je zapnuté, vzdialené súbory, ktoré nie sú uložené lokálne alebo boli odstránené kvôli obmedzeniam úložiska, budú vyžiadané cez proxy, vrátane generovani miniatúr. Neovplyvní to úložisko na serveri."
driveCapacityPerLocalAccount: "Kapacita disku pre používateľa" driveCapacityPerLocalAccount: "Kapacita disku pre používateľa"
driveCapacityPerRemoteAccount: "Kapacita disku pre vzdialeného používateľa" driveCapacityPerRemoteAccount: "Kapacita disku pre vzdialeného používateľa"
inMb: "V megabajtoch" inMb: "V megabajtoch"
@ -422,7 +420,6 @@ next: "Ďalší"
retype: "Zadajte znovu" retype: "Zadajte znovu"
noteOf: "Poznámky používateľa {user}" noteOf: "Poznámky používateľa {user}"
inviteToGroup: "Pozvať do skupiny" inviteToGroup: "Pozvať do skupiny"
maxNoteTextLength: "Maximálny počet znakov poznámky"
quoteAttached: "Citované" quoteAttached: "Citované"
quoteQuestion: "Pripojiť ako citát?" quoteQuestion: "Pripojiť ako citát?"
noMessagesYet: "Zatiaľ žiadne správy" noMessagesYet: "Zatiaľ žiadne správy"
@ -832,6 +829,16 @@ auto: "Automaticky"
themeColor: "Farba témy" themeColor: "Farba témy"
size: "Veľkosť" size: "Veľkosť"
numberOfColumn: "Počet stĺpcov" numberOfColumn: "Počet stĺpcov"
searchByGoogle: "Hľadať cez Google"
instanceDefaultLightTheme: "Predvolená svetlá téma"
instanceDefaultDarkTheme: "Predvolená tmavá téma"
instanceDefaultThemeDescription: "Vložte kód témy v objektovom formáte"
mutePeriod: "Trvanie stíšenia"
indefinitely: "Navždy"
tenMinutes: "10 minút"
oneHour: "1 hodina"
oneDay: "1 deň"
oneWeek: "1 týždeň"
_emailUnavailable: _emailUnavailable:
used: "Táto emailová adresa sa už používa" used: "Táto emailová adresa sa už používa"
format: "Formát emailovej adresy je nesprávny" format: "Formát emailovej adresy je nesprávny"
@ -1604,6 +1611,7 @@ _notification:
youReceivedFollowRequest: "Dostali ste žiadosť o sledovanie" youReceivedFollowRequest: "Dostali ste žiadosť o sledovanie"
yourFollowRequestAccepted: "Vaša žiadosť o sledovanie bola prijatá" yourFollowRequestAccepted: "Vaša žiadosť o sledovanie bola prijatá"
youWereInvitedToGroup: "Pozvať do skupiny" youWereInvitedToGroup: "Pozvať do skupiny"
pollEnded: "Výsledky hlasovania sú k dispozícii."
_types: _types:
all: "Všetky" all: "Všetky"
follow: "Sledujete" follow: "Sledujete"
@ -1613,6 +1621,7 @@ _notification:
quote: "Citovať" quote: "Citovať"
reaction: "Reakcie" reaction: "Reakcie"
pollVote: "Hlasy v hlasovaniach" pollVote: "Hlasy v hlasovaniach"
pollEnded: "Hlasovanie skončilo"
receiveFollowRequest: "Doručené žiadosti o sledovanie" receiveFollowRequest: "Doručené žiadosti o sledovanie"
followRequestAccepted: "Schválené žiadosti o sledovanie" followRequestAccepted: "Schválené žiadosti o sledovanie"
groupInvited: "Pozvánky do skupín" groupInvited: "Pozvánky do skupín"

View file

@ -47,6 +47,7 @@ remove: "Sil"
smtpUser: "Kullanıcı Adı" smtpUser: "Kullanıcı Adı"
smtpPass: "Şifre" smtpPass: "Şifre"
user: "Kullanıcı" user: "Kullanıcı"
searchByGoogle: "Arama"
_mfm: _mfm:
search: "Arama" search: "Arama"
_sfx: _sfx:

View file

@ -1,5 +1,6 @@
--- ---
_lang_: "ياپونچە" _lang_: "ياپونچە"
search: "ئىزدەش" search: "ئىزدەش"
searchByGoogle: "ئىزدەش"
_mfm: _mfm:
search: "ئىزدەش" search: "ئىزدەش"

View file

@ -312,8 +312,6 @@ disablingTimelinesInfo: "Адміністратори та модератори
registration: "Реєстрація" registration: "Реєстрація"
enableRegistration: "Дозволити реєстрацію" enableRegistration: "Дозволити реєстрацію"
invite: "Запросити" invite: "Запросити"
proxyRemoteFiles: "Проксувати файли з інших інстансів"
proxyRemoteFilesDescription: "При увімкненні віддалені файли, які не зберігаються локально або були видалені через недостатнії обсяг пам'яті, будуть локально проксовані включаючи обкладинки. Це налаштування не впливає на пам'ять серверу. "
driveCapacityPerLocalAccount: "Об'єм диска на одного локального користувача" driveCapacityPerLocalAccount: "Об'єм диска на одного локального користувача"
driveCapacityPerRemoteAccount: "Об'єм диска на одного віддаленого користувача" driveCapacityPerRemoteAccount: "Об'єм диска на одного віддаленого користувача"
inMb: "В мегабайтах" inMb: "В мегабайтах"
@ -407,7 +405,6 @@ next: "Далі"
retype: "Введіть ще раз" retype: "Введіть ще раз"
noteOf: "Нотатка {user}" noteOf: "Нотатка {user}"
inviteToGroup: "Запрошення до групи" inviteToGroup: "Запрошення до групи"
maxNoteTextLength: "Максимальна довжина нотатки"
quoteAttached: "Цитата" quoteAttached: "Цитата"
quoteQuestion: "Ви хочете додати цитату?" quoteQuestion: "Ви хочете додати цитату?"
noMessagesYet: "Ще немає повідомлень" noMessagesYet: "Ще немає повідомлень"
@ -688,6 +685,8 @@ global: "Глобальна"
sent: "Відправити" sent: "Відправити"
hashtags: "Хештеґ" hashtags: "Хештеґ"
hide: "Сховати" hide: "Сховати"
searchByGoogle: "Пошук"
indefinitely: "Ніколи"
_ad: _ad:
back: "Назад" back: "Назад"
_gallery: _gallery:

48
locales/vi-VN.yml Normal file
View file

@ -0,0 +1,48 @@
---
_lang_: "Tiếng Việt"
headlineMisskey: "Mạng xã hội liên hợp"
monthAndDay: "{day} tháng {month}"
search: "Tìm kiếm"
notifications: "Thông báo"
username: "Tên người dùng"
password: "Mật khẩu"
forgotPassword: "Quên mật khẩu"
ok: "Đồng ý"
renotedBy: "Chia sẻ bởi {user}"
flagAsBot: "Đánh dấu đây là tài khoản bot"
searchWith: "Tìm kiếm: {q}"
followConfirm: "Bạn có chắc muốn theo dõi {name}"
cpuAndMemory: "CPU và Dung lượng"
dayX: "{day}"
yearX: "{year}"
aboutMisskey: "Về Misskey"
smtpUser: "Tên người dùng"
smtpPass: "Mật khẩu"
reportAbuseOf: "Báo cáo {name}"
renotedCount: "Lượt chia sẻ"
translatedFrom: "Dịch từ {x}"
searchByGoogle: "Google"
_mfm:
search: "Tìm kiếm"
_theme:
installed: "{name} đã được cài đặt"
_sfx:
notification: "Thông báo"
_ago:
unknown: "Không rõ"
future: "Tương lai"
justNow: "Vừa xong"
secondsAgo: "{n}s trước"
minutesAgo: "{n} phút trước"
hoursAgo: "{n} giờ trước"
daysAgo: "{n} ngày trước"
weeksAgo: "{n} tuần trước"
monthsAgo: "{n} tháng trước"
yearsAgo: "{n} năm trước"
_widgets:
notifications: "Thông báo"
_profile:
username: "Tên người dùng"
_deck:
_columns:
notifications: "Thông báo"

View file

@ -325,8 +325,6 @@ disablingTimelinesInfo: "即使时间线功能被禁用,出于便利性的原
registration: "注册" registration: "注册"
enableRegistration: "允许新用户注册" enableRegistration: "允许新用户注册"
invite: "邀请" invite: "邀请"
proxyRemoteFiles: "代理远程文件"
proxyRemoteFilesDescription: "启用此设置后,由于超出存储容量而导致未保存被删除的远程文件将被本地代理,并且会生成缩略图。不会影响服务器的存储。"
driveCapacityPerLocalAccount: "每个用户的网盘空间" driveCapacityPerLocalAccount: "每个用户的网盘空间"
driveCapacityPerRemoteAccount: "每个远程用户的网盘容量" driveCapacityPerRemoteAccount: "每个远程用户的网盘容量"
inMb: "以兆字节(MegaByte)为单位" inMb: "以兆字节(MegaByte)为单位"
@ -422,7 +420,6 @@ next: "下一个"
retype: "重新输入" retype: "重新输入"
noteOf: "{user}的帖子" noteOf: "{user}的帖子"
inviteToGroup: "群组邀请" inviteToGroup: "群组邀请"
maxNoteTextLength: "帖子的字数限制"
quoteAttached: "已引用" quoteAttached: "已引用"
quoteQuestion: "是否引用此链接内容?" quoteQuestion: "是否引用此链接内容?"
noMessagesYet: "现在没有新的聊天" noMessagesYet: "现在没有新的聊天"
@ -833,6 +830,16 @@ auto: "自动"
themeColor: "主题颜色" themeColor: "主题颜色"
size: "大小" size: "大小"
numberOfColumn: "列数" numberOfColumn: "列数"
searchByGoogle: "Google"
instanceDefaultLightTheme: "实例默认浅色主题"
instanceDefaultDarkTheme: "实例默认深色主题"
instanceDefaultThemeDescription: "以对象格式键入主题代码"
mutePeriod: "屏蔽期限"
indefinitely: "永久"
tenMinutes: "10分钟"
oneHour: "1小时"
oneDay: "1天"
oneWeek: "1周"
_emailUnavailable: _emailUnavailable:
used: "已经被使用过" used: "已经被使用过"
format: "无效的格式" format: "无效的格式"
@ -1210,7 +1217,7 @@ _poll:
noMore: "无法再添加更多了" noMore: "无法再添加更多了"
canMultipleVote: "允许多个投票" canMultipleVote: "允许多个投票"
expiration: "截止时间" expiration: "截止时间"
infinite: "不限时间" infinite: "永久"
at: "指定日期" at: "指定日期"
after: "指定时间" after: "指定时间"
deadlineDate: "截止日期" deadlineDate: "截止日期"
@ -1605,6 +1612,7 @@ _notification:
youReceivedFollowRequest: "您有新的关注请求" youReceivedFollowRequest: "您有新的关注请求"
yourFollowRequestAccepted: "您的关注请求已通过" yourFollowRequestAccepted: "您的关注请求已通过"
youWereInvitedToGroup: "您有新的群组邀请" youWereInvitedToGroup: "您有新的群组邀请"
pollEnded: "问卷调查结果已生成。"
_types: _types:
all: "全部" all: "全部"
follow: "关注中" follow: "关注中"
@ -1614,6 +1622,7 @@ _notification:
quote: "引用" quote: "引用"
reaction: "回应" reaction: "回应"
pollVote: "问卷调查被投票" pollVote: "问卷调查被投票"
pollEnded: "问卷调查结束"
receiveFollowRequest: "收到关注请求" receiveFollowRequest: "收到关注请求"
followRequestAccepted: "关注请求已通过" followRequestAccepted: "关注请求已通过"
groupInvited: "加入群组邀请" groupInvited: "加入群组邀请"

View file

@ -318,8 +318,6 @@ disablingTimelinesInfo: "即使您關閉了時間線功能,管理員和協調
registration: "註冊" registration: "註冊"
enableRegistration: "開啟新使用者註冊" enableRegistration: "開啟新使用者註冊"
invite: "邀請" invite: "邀請"
proxyRemoteFiles: "遠端代理檔案"
proxyRemoteFilesDescription: "啟用此設置後,由於超出存儲容量而未保存或刪除的遠程文件將被本地代理,並且將生成預覽圖。這不影響伺服器的存儲。"
driveCapacityPerLocalAccount: "每個本地用戶的雲端空間大小" driveCapacityPerLocalAccount: "每個本地用戶的雲端空間大小"
driveCapacityPerRemoteAccount: "每個非本地用戶的雲端容量" driveCapacityPerRemoteAccount: "每個非本地用戶的雲端容量"
inMb: "以Mbps為單位" inMb: "以Mbps為單位"
@ -415,7 +413,6 @@ next: "下一步"
retype: "重新輸入" retype: "重新輸入"
noteOf: "{user}的貼文" noteOf: "{user}的貼文"
inviteToGroup: "邀請至群組" inviteToGroup: "邀請至群組"
maxNoteTextLength: "貼文的字數限制"
quoteAttached: "引用" quoteAttached: "引用"
quoteQuestion: "是否要引用?" quoteQuestion: "是否要引用?"
noMessagesYet: "沒有訊息" noMessagesYet: "沒有訊息"
@ -750,6 +747,8 @@ global: "公開"
sent: "發送" sent: "發送"
hashtags: "#tag" hashtags: "#tag"
hide: "隱藏" hide: "隱藏"
searchByGoogle: "搜尋"
indefinitely: "無期限"
_ffVisibility: _ffVisibility:
public: "發佈" public: "發佈"
_ad: _ad:

View file

@ -1,6 +1,6 @@
{ {
"name": "misskey", "name": "misskey",
"version": "12.107.0", "version": "12.108.1",
"codename": "indigo", "codename": "indigo",
"repository": { "repository": {
"type": "git", "type": "git",
@ -13,8 +13,7 @@
"start": "cd packages/backend && node --experimental-json-modules ./built/index.js", "start": "cd packages/backend && node --experimental-json-modules ./built/index.js",
"start:test": "cd packages/backend && cross-env NODE_ENV=test node --experimental-json-modules ./built/index.js", "start:test": "cd packages/backend && cross-env NODE_ENV=test node --experimental-json-modules ./built/index.js",
"init": "npm run migrate", "init": "npm run migrate",
"ormconfig": "node ./packages/backend/ormconfig.js", "migrate": "cd packages/backend && npx typeorm migration:run -d ormconfig.js",
"migrate": "cd packages/backend && npx typeorm migration:run",
"migrateandstart": "npm run migrate && npm run start", "migrateandstart": "npm run migrate && npm run start",
"gulp": "gulp build", "gulp": "gulp build",
"watch": "npm run dev", "watch": "npm run dev",
@ -42,10 +41,10 @@
"js-yaml": "4.1.0" "js-yaml": "4.1.0"
}, },
"devDependencies": { "devDependencies": {
"@typescript-eslint/parser": "5.12.0", "@typescript-eslint/parser": "5.16.0",
"cross-env": "7.0.3", "cross-env": "7.0.3",
"cypress": "9.5.0", "cypress": "9.5.2",
"start-server-and-test": "1.14.0", "start-server-and-test": "1.14.0",
"typescript": "4.5.5" "typescript": "4.6.3"
} }
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View file

@ -0,0 +1,13 @@
export class muteExpiresAt1646387162108 {
name = 'muteExpiresAt1646387162108'
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "muting" ADD "expiresAt" TIMESTAMP WITH TIME ZONE`);
await queryRunner.query(`CREATE INDEX "IDX_c1fd1c3dfb0627aa36c253fd14" ON "muting" ("expiresAt") `);
}
async down(queryRunner) {
await queryRunner.query(`DROP INDEX "public"."IDX_c1fd1c3dfb0627aa36c253fd14"`);
await queryRunner.query(`ALTER TABLE "muting" DROP COLUMN "expiresAt"`);
}
}

View file

@ -0,0 +1,18 @@
export class pollEndedNotification1646549089451 {
name = 'pollEndedNotification1646549089451'
async up(queryRunner) {
await queryRunner.query(`ALTER TYPE "public"."notification_type_enum" RENAME TO "notification_type_enum_old"`);
await queryRunner.query(`CREATE TYPE "public"."notification_type_enum" AS ENUM('follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'app')`);
await queryRunner.query(`ALTER TABLE "notification" ALTER COLUMN "type" TYPE "public"."notification_type_enum" USING "type"::"text"::"public"."notification_type_enum"`);
await queryRunner.query(`DROP TYPE "public"."notification_type_enum_old"`);
}
async down(queryRunner) {
await queryRunner.query(`CREATE TYPE "public"."notification_type_enum_old" AS ENUM('follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'app')`);
await queryRunner.query(`ALTER TABLE "notification" ALTER COLUMN "type" TYPE "public"."notification_type_enum_old" USING "type"::"text"::"public"."notification_type_enum_old"`);
await queryRunner.query(`DROP TYPE "public"."notification_type_enum"`);
await queryRunner.query(`ALTER TYPE "public"."notification_type_enum_old" RENAME TO "notification_type_enum"`);
}
}

View file

@ -0,0 +1,13 @@
export class chartFederationActive1646633030285 {
name = 'chartFederationActive1646633030285'
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "__chart__federation" ADD "___active" smallint NOT NULL DEFAULT '0'`);
await queryRunner.query(`ALTER TABLE "__chart_day__federation" ADD "___active" smallint NOT NULL DEFAULT '0'`);
}
async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "__chart_day__federation" DROP COLUMN "___active"`);
await queryRunner.query(`ALTER TABLE "__chart__federation" DROP COLUMN "___active"`);
}
}

View file

@ -0,0 +1,13 @@
export class removeInstanceDriveColumns1646655454495 {
name = 'removeInstanceDriveColumns1646655454495'
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "instance" DROP COLUMN "driveUsage"`);
await queryRunner.query(`ALTER TABLE "instance" DROP COLUMN "driveFiles"`);
}
async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "instance" ADD "driveFiles" integer NOT NULL DEFAULT '0'`);
await queryRunner.query(`ALTER TABLE "instance" ADD "driveUsage" bigint NOT NULL DEFAULT '0'`);
}
}

View file

@ -0,0 +1,21 @@
export class chartFederationActiveSubPub1646732390560 {
name = 'chartFederationActiveSubPub1646732390560'
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "__chart__federation" DROP COLUMN "___active"`);
await queryRunner.query(`ALTER TABLE "__chart_day__federation" DROP COLUMN "___active"`);
await queryRunner.query(`ALTER TABLE "__chart__federation" ADD "___subActive" smallint NOT NULL DEFAULT '0'`);
await queryRunner.query(`ALTER TABLE "__chart__federation" ADD "___pubActive" smallint NOT NULL DEFAULT '0'`);
await queryRunner.query(`ALTER TABLE "__chart_day__federation" ADD "___subActive" smallint NOT NULL DEFAULT '0'`);
await queryRunner.query(`ALTER TABLE "__chart_day__federation" ADD "___pubActive" smallint NOT NULL DEFAULT '0'`);
}
async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "__chart_day__federation" DROP COLUMN "___pubActive"`);
await queryRunner.query(`ALTER TABLE "__chart_day__federation" DROP COLUMN "___subActive"`);
await queryRunner.query(`ALTER TABLE "__chart__federation" DROP COLUMN "___pubActive"`);
await queryRunner.query(`ALTER TABLE "__chart__federation" DROP COLUMN "___subActive"`);
await queryRunner.query(`ALTER TABLE "__chart_day__federation" ADD "___active" smallint NOT NULL DEFAULT '0'`);
await queryRunner.query(`ALTER TABLE "__chart__federation" ADD "___active" smallint NOT NULL DEFAULT '0'`);
}
}

View file

@ -1,7 +1,8 @@
import { DataSource } from 'typeorm';
import config from './built/config/index.js'; import config from './built/config/index.js';
import { entities } from './built/db/postgre.js'; import { entities } from './built/db/postgre.js';
export default { export default new DataSource({
type: 'postgres', type: 'postgres',
host: config.db.host, host: config.db.host,
port: config.db.port, port: config.db.port,
@ -11,7 +12,4 @@ export default {
extra: config.db.extra, extra: config.db.extra,
entities: entities, entities: entities,
migrations: ['migration/*.js'], migrations: ['migration/*.js'],
cli: { });
migrationsDir: 'migration'
}
};

View file

@ -3,7 +3,6 @@
"private": true, "private": true,
"type": "module", "type": "module",
"scripts": { "scripts": {
"init": "npm run migrate",
"build": "tsc -p tsconfig.json || echo done. && tsc-alias -p tsconfig.json", "build": "tsc -p tsconfig.json || echo done. && tsc-alias -p tsconfig.json",
"watch": "node watch.mjs", "watch": "node watch.mjs",
"lint": "eslint --quiet src/**/*.ts", "lint": "eslint --quiet src/**/*.ts",
@ -15,12 +14,12 @@
"lodash": "^4.17.21" "lodash": "^4.17.21"
}, },
"dependencies": { "dependencies": {
"@discordapp/twemoji": "13.1.0", "@discordapp/twemoji": "13.1.1",
"@elastic/elasticsearch": "7.11.0", "@elastic/elasticsearch": "7.11.0",
"@koa/cors": "3.1.0", "@koa/cors": "3.1.0",
"@koa/multer": "3.0.0", "@koa/multer": "3.0.0",
"@koa/router": "10.1.1", "@koa/router": "9.0.1",
"@sinonjs/fake-timers": "9.1.0", "@sinonjs/fake-timers": "9.1.1",
"@syuilo/aiscript": "0.11.1", "@syuilo/aiscript": "0.11.1",
"@types/bcryptjs": "2.4.2", "@types/bcryptjs": "2.4.2",
"@types/bull": "3.15.8", "@types/bull": "3.15.8",
@ -31,7 +30,7 @@
"@types/jsdom": "16.2.14", "@types/jsdom": "16.2.14",
"@types/jsonld": "1.5.6", "@types/jsonld": "1.5.6",
"@types/koa": "2.13.4", "@types/koa": "2.13.4",
"@types/koa-bodyparser": "4.3.5", "@types/koa-bodyparser": "4.3.7",
"@types/koa-cors": "0.0.2", "@types/koa-cors": "0.0.2",
"@types/koa-favicon": "2.0.21", "@types/koa-favicon": "2.0.21",
"@types/koa-logger": "3.1.2", "@types/koa-logger": "3.1.2",
@ -42,7 +41,7 @@
"@types/koa__multer": "2.0.4", "@types/koa__multer": "2.0.4",
"@types/koa__router": "8.0.11", "@types/koa__router": "8.0.11",
"@types/mocha": "9.1.0", "@types/mocha": "9.1.0",
"@types/node": "17.0.19", "@types/node": "17.0.23",
"@types/node-fetch": "3.0.3", "@types/node-fetch": "3.0.3",
"@types/nodemailer": "6.4.4", "@types/nodemailer": "6.4.4",
"@types/oauth": "0.9.1", "@types/oauth": "0.9.1",
@ -56,9 +55,8 @@
"@types/redis": "4.0.11", "@types/redis": "4.0.11",
"@types/rename": "1.0.4", "@types/rename": "1.0.4",
"@types/sanitize-html": "2.6.2", "@types/sanitize-html": "2.6.2",
"@types/seedrandom": "3.0.1", "@types/sharp": "0.30.0",
"@types/sharp": "0.29.5", "@types/sinonjs__fake-timers": "8.1.2",
"@types/sinonjs__fake-timers": "8.1.1",
"@types/speakeasy": "2.0.7", "@types/speakeasy": "2.0.7",
"@types/throttle-debounce": "2.1.0", "@types/throttle-debounce": "2.1.0",
"@types/tinycolor2": "1.4.3", "@types/tinycolor2": "1.4.3",
@ -66,44 +64,44 @@
"@types/uuid": "8.3.4", "@types/uuid": "8.3.4",
"@types/web-push": "3.3.2", "@types/web-push": "3.3.2",
"@types/websocket": "1.0.5", "@types/websocket": "1.0.5",
"@types/ws": "8.2.3", "@types/ws": "8.5.3",
"@typescript-eslint/eslint-plugin": "5.12.1", "@typescript-eslint/eslint-plugin": "5.16.0",
"@typescript-eslint/parser": "5.12.1", "@typescript-eslint/parser": "5.16.0",
"@bull-board/koa": "3.10.1",
"abort-controller": "3.0.0", "abort-controller": "3.0.0",
"ajv": "8.10.0", "ajv": "8.11.0",
"archiver": "5.3.0", "archiver": "5.3.0",
"autobind-decorator": "2.4.0", "autobind-decorator": "2.4.0",
"autwh": "0.1.0", "autwh": "0.1.0",
"aws-sdk": "2.1079.0", "aws-sdk": "2.1100.0",
"bcryptjs": "2.4.3", "bcryptjs": "2.4.3",
"blurhash": "1.1.5", "blurhash": "1.1.5",
"broadcast-channel": "4.10.0", "broadcast-channel": "4.10.0",
"bull": "4.6.2", "bull": "4.8.1",
"cacheable-lookup": "6.0.4", "cacheable-lookup": "6.0.4",
"cafy": "15.2.1", "cafy": "15.2.1",
"cbor": "8.1.0", "cbor": "8.1.0",
"chalk": "5.0.0", "chalk": "5.0.1",
"chalk-template": "0.3.1", "chalk-template": "0.4.0",
"cli-highlight": "2.1.11", "cli-highlight": "2.1.11",
"color-convert": "2.0.1", "color-convert": "2.0.1",
"content-disposition": "0.5.4", "content-disposition": "0.5.4",
"date-fns": "2.28.0", "date-fns": "2.28.0",
"deep-email-validator": "0.1.21", "deep-email-validator": "0.1.21",
"escape-regexp": "0.0.1", "escape-regexp": "0.0.1",
"eslint": "8.9.0", "eslint": "8.12.0",
"eslint-plugin-import": "2.25.4", "eslint-plugin-import": "2.25.4",
"eventemitter3": "4.0.7",
"feed": "4.2.2", "feed": "4.2.2",
"file-type": "16.5.3", "file-type": "17.1.1",
"fluent-ffmpeg": "2.1.2", "fluent-ffmpeg": "2.1.2",
"got": "11.8.2", "got": "12.0.3",
"hpagent": "0.1.2", "hpagent": "0.1.2",
"http-signature": "1.3.6", "http-signature": "1.3.6",
"ip-cidr": "3.0.4", "ip-cidr": "3.0.4",
"is-svg": "4.3.2", "is-svg": "4.3.2",
"js-yaml": "4.1.0", "js-yaml": "4.1.0",
"jsdom": "19.0.0", "jsdom": "19.0.0",
"json5": "2.2.0", "json5": "2.2.1",
"json5-loader": "4.0.1", "json5-loader": "4.0.1",
"jsonld": "5.2.0", "jsonld": "5.2.0",
"jsrsasign": "8.0.20", "jsrsasign": "8.0.20",
@ -117,14 +115,14 @@
"koa-slow": "2.1.0", "koa-slow": "2.1.0",
"koa-views": "7.0.2", "koa-views": "7.0.2",
"mfm-js": "0.21.0", "mfm-js": "0.21.0",
"mime-types": "2.1.34", "mime-types": "2.1.35",
"misskey-js": "0.0.14", "misskey-js": "0.0.14",
"mocha": "9.2.1", "mocha": "9.2.2",
"ms": "3.0.0-canary.1", "ms": "3.0.0-canary.1",
"multer": "1.4.4", "multer": "1.4.4",
"nested-property": "4.0.0", "nested-property": "4.0.0",
"node-fetch": "2.6.7", "node-fetch": "3.2.3",
"nodemailer": "6.7.2", "nodemailer": "6.7.3",
"os-utils": "0.0.14", "os-utils": "0.0.14",
"parse5": "6.0.1", "parse5": "6.0.1",
"pg": "8.7.3", "pg": "8.7.3",
@ -147,25 +145,25 @@
"rndstr": "1.0.0", "rndstr": "1.0.0",
"s-age": "1.1.2", "s-age": "1.1.2",
"sanitize-html": "2.7.0", "sanitize-html": "2.7.0",
"seedrandom": "3.0.5", "semver": "7.3.5",
"sharp": "0.30.1", "sharp": "0.30.3",
"speakeasy": "2.0.0", "speakeasy": "2.0.0",
"strict-event-emitter-types": "2.0.0", "strict-event-emitter-types": "2.0.0",
"stringz": "2.1.0", "stringz": "2.1.0",
"style-loader": "3.3.1", "style-loader": "3.3.1",
"summaly": "2.5.0", "summaly": "2.5.0",
"syslog-pro": "1.0.0", "syslog-pro": "1.0.0",
"systeminformation": "5.11.4", "systeminformation": "5.11.9",
"throttle-debounce": "3.0.1", "throttle-debounce": "3.0.1",
"tinycolor2": "1.4.2", "tinycolor2": "1.4.2",
"tmp": "0.2.1", "tmp": "0.2.1",
"ts-loader": "9.2.6", "ts-loader": "9.2.8",
"ts-node": "10.5.0", "ts-node": "10.7.0",
"tsc-alias": "1.4.1", "tsc-alias": "1.4.1",
"tsconfig-paths": "3.12.0", "tsconfig-paths": "3.14.1",
"twemoji-parser": "13.1.0", "twemoji-parser": "14.0.0",
"typeorm": "0.2.44", "typeorm": "0.3.4",
"typescript": "4.5.5", "typescript": "4.6.3",
"ulid": "2.3.0", "ulid": "2.3.0",
"unzipper": "0.10.11", "unzipper": "0.10.11",
"uuid": "8.3.2", "uuid": "8.3.2",
@ -175,7 +173,7 @@
"xev": "2.0.1" "xev": "2.0.1"
}, },
"devDependencies": { "devDependencies": {
"@redocly/openapi-core": "1.0.0-beta.83", "@redocly/openapi-core": "1.0.0-beta.90",
"@types/fluent-ffmpeg": "2.1.20", "@types/fluent-ffmpeg": "2.1.20",
"cross-env": "7.0.3", "cross-env": "7.0.3",
"execa": "6.1.0" "execa": "6.1.0"

View file

@ -6,7 +6,7 @@ import cluster from 'node:cluster';
import chalk from 'chalk'; import chalk from 'chalk';
import chalkTemplate from 'chalk-template'; import chalkTemplate from 'chalk-template';
import * as portscanner from 'portscanner'; import * as portscanner from 'portscanner';
import { getConnection } from 'typeorm'; import semver from 'semver';
import Logger from '@/services/logger.js'; import Logger from '@/services/logger.js';
import loadConfig from '@/config/load.js'; import loadConfig from '@/config/load.js';
@ -14,7 +14,7 @@ import { Config } from '@/config/types.js';
import { lessThan } from '@/prelude/array.js'; import { lessThan } from '@/prelude/array.js';
import { envOption } from '../env.js'; import { envOption } from '../env.js';
import { showMachineInfo } from '@/misc/show-machine-info.js'; import { showMachineInfo } from '@/misc/show-machine-info.js';
import { initDb } from '../db/postgre.js'; import { db, initDb } from '../db/postgre.js';
const _filename = fileURLToPath(import.meta.url); const _filename = fileURLToPath(import.meta.url);
const _dirname = dirname(_filename); const _dirname = dirname(_filename);
@ -88,10 +88,6 @@ export async function masterMain() {
} }
} }
const runningNodejsVersion = process.version.slice(1).split('.').map(x => parseInt(x, 10));
const requiredNodejsVersion = [11, 7, 0];
const satisfyNodejsVersion = !lessThan(runningNodejsVersion, requiredNodejsVersion);
function showEnvironment(): void { function showEnvironment(): void {
const env = process.env.NODE_ENV; const env = process.env.NODE_ENV;
const logger = bootLogger.createSubLogger('env'); const logger = bootLogger.createSubLogger('env');
@ -108,10 +104,11 @@ function showEnvironment(): void {
function showNodejsVersion(): void { function showNodejsVersion(): void {
const nodejsLogger = bootLogger.createSubLogger('nodejs'); const nodejsLogger = bootLogger.createSubLogger('nodejs');
nodejsLogger.info(`Version ${runningNodejsVersion.join('.')}`); nodejsLogger.info(`Version ${process.version} detected.`);
if (!satisfyNodejsVersion) { const minVersion = fs.readFileSync(`${_dirname}/../../../../.node-version`, 'utf-8').trim();
nodejsLogger.error(`Node.js version is less than ${requiredNodejsVersion.join('.')}. Please upgrade it.`, null, true); if (semver.lt(process.version, minVersion)) {
nodejsLogger.error(`At least Node.js ${minVersion} required!`);
process.exit(1); process.exit(1);
} }
} }
@ -146,7 +143,7 @@ async function connectDb(): Promise<void> {
try { try {
dbLogger.info('Connecting...'); dbLogger.info('Connecting...');
await initDb(); await initDb();
const v = await getConnection().query('SHOW server_version').then(x => x[0].server_version); const v = await db.query('SHOW server_version').then(x => x[0].server_version);
dbLogger.succ(`Connected: v${v}`); dbLogger.succ(`Connected: v${v}`);
} catch (e) { } catch (e) {
dbLogger.error('Cannot connect', null, true); dbLogger.error('Cannot connect', null, true);

View file

@ -6,7 +6,6 @@ export type Source = {
feedback_url?: string; feedback_url?: string;
url: string; url: string;
port: number; port: number;
https?: { [x: string]: string };
disableHsts?: boolean; disableHsts?: boolean;
db: { db: {
host: string; host: string;

View file

@ -2,9 +2,10 @@
import pg from 'pg'; import pg from 'pg';
pg.types.setTypeParser(20, Number); pg.types.setTypeParser(20, Number);
import { createConnection, Logger, getConnection } from 'typeorm'; import { Logger, DataSource } from 'typeorm';
import * as highlight from 'cli-highlight'; import * as highlight from 'cli-highlight';
import config from '@/config/index.js'; import config from '@/config/index.js';
import { envOption } from '../env.js';
import { dbLogger } from './logger.js'; import { dbLogger } from './logger.js';
@ -61,7 +62,6 @@ import { Antenna } from '@/models/entities/antenna.js';
import { AntennaNote } from '@/models/entities/antenna-note.js'; import { AntennaNote } from '@/models/entities/antenna-note.js';
import { PromoNote } from '@/models/entities/promo-note.js'; import { PromoNote } from '@/models/entities/promo-note.js';
import { PromoRead } from '@/models/entities/promo-read.js'; import { PromoRead } from '@/models/entities/promo-read.js';
import { envOption } from '../env.js';
import { Relay } from '@/models/entities/relay.js'; import { Relay } from '@/models/entities/relay.js';
import { MutedNote } from '@/models/entities/muted-note.js'; import { MutedNote } from '@/models/entities/muted-note.js';
import { Channel } from '@/models/entities/channel.js'; import { Channel } from '@/models/entities/channel.js';
@ -74,7 +74,7 @@ import { UserPending } from '@/models/entities/user-pending.js';
import { entities as charts } from '@/services/chart/entities.js'; import { entities as charts } from '@/services/chart/entities.js';
const sqlLogger = dbLogger.createSubLogger('sql', 'white', false); const sqlLogger = dbLogger.createSubLogger('sql', 'gray', false);
class MyCustomLogger implements Logger { class MyCustomLogger implements Logger {
private highlight(sql: string) { private highlight(sql: string) {
@ -84,9 +84,7 @@ class MyCustomLogger implements Logger {
} }
public logQuery(query: string, parameters?: any[]) { public logQuery(query: string, parameters?: any[]) {
if (envOption.verbose) { sqlLogger.info(this.highlight(query).substring(0, 100));
sqlLogger.info(this.highlight(query));
}
} }
public logQueryError(error: string, query: string, parameters?: any[]) { public logQueryError(error: string, query: string, parameters?: any[]) {
@ -176,52 +174,51 @@ export const entities = [
...charts, ...charts,
]; ];
export function initDb(justBorrow = false, sync = false, forceRecreate = false) { const log = process.env.NODE_ENV !== 'production';
if (!forceRecreate) {
try {
const conn = getConnection();
return Promise.resolve(conn);
} catch (e) {}
}
const log = process.env.NODE_ENV != 'production'; export const db = new DataSource({
type: 'postgres',
host: config.db.host,
port: config.db.port,
username: config.db.user,
password: config.db.pass,
database: config.db.db,
extra: {
statement_timeout: 1000 * 10,
...config.db.extra,
},
synchronize: process.env.NODE_ENV === 'test',
dropSchema: process.env.NODE_ENV === 'test',
cache: !config.db.disableCache ? {
type: 'redis',
options: {
host: config.redis.host,
port: config.redis.port,
password: config.redis.pass,
prefix: `${config.redis.prefix}:query:`,
db: config.redis.db || 0,
},
} : false,
logging: log,
logger: log ? new MyCustomLogger() : undefined,
maxQueryExecutionTime: 300,
entities: entities,
migrations: ['../../migration/*.js'],
});
return createConnection({ export async function initDb() {
type: 'postgres', await db.connect();
host: config.db.host,
port: config.db.port,
username: config.db.user,
password: config.db.pass,
database: config.db.db,
extra: config.db.extra,
synchronize: process.env.NODE_ENV === 'test' || sync,
dropSchema: process.env.NODE_ENV === 'test' && !justBorrow,
cache: !config.db.disableCache ? {
type: 'redis',
options: {
host: config.redis.host,
port: config.redis.port,
password: config.redis.pass,
prefix: `${config.redis.prefix}:query:`,
db: config.redis.db || 0,
},
} : false,
logging: log,
logger: log ? new MyCustomLogger() : undefined,
entities: entities,
});
} }
export async function resetDb() { export async function resetDb() {
const reset = async () => { const reset = async () => {
const conn = await getConnection(); const tables = await db.query(`SELECT relname AS "table"
const tables = await conn.query(`SELECT relname AS "table"
FROM pg_class C LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace) FROM pg_class C LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
WHERE nspname NOT IN ('pg_catalog', 'information_schema') WHERE nspname NOT IN ('pg_catalog', 'information_schema')
AND C.relkind = 'r' AND C.relkind = 'r'
AND nspname !~ '^pg_toast';`); AND nspname !~ '^pg_toast';`);
for (const table of tables) { for (const table of tables) {
await conn.query(`DELETE FROM "${table.table}" CASCADE`); await db.query(`DELETE FROM "${table.table}" CASCADE`);
} }
}; };

View file

@ -1,5 +1,5 @@
export class Cache<T> { export class Cache<T> {
private cache: Map<string | null, { date: number; value: T; }>; public cache: Map<string | null, { date: number; value: T; }>;
private lifetime: number; private lifetime: number;
constructor(lifetime: Cache<never>['lifetime']) { constructor(lifetime: Cache<never>['lifetime']) {
@ -28,16 +28,52 @@ export class Cache<T> {
this.cache.delete(key); this.cache.delete(key);
} }
public async fetch(key: string | null, fetcher: () => Promise<T>): Promise<T> { /**
* fetcherを呼び出して結果をキャッシュ&
* optional: キャッシュが存在してもvalidatorでfalseを返すとキャッシュ無効扱いにします
*/
public async fetch(key: string | null, fetcher: () => Promise<T>, validator?: (cachedValue: T) => boolean): Promise<T> {
const cachedValue = this.get(key); const cachedValue = this.get(key);
if (cachedValue !== undefined) { if (cachedValue !== undefined) {
// Cache HIT if (validator) {
return cachedValue; if (validator(cachedValue)) {
// Cache HIT
return cachedValue;
}
} else {
// Cache HIT
return cachedValue;
}
} }
// Cache MISS // Cache MISS
const value = await fetcher(); const value = await fetcher();
this.set(key, value); return value;
}
/**
* fetcherを呼び出して結果をキャッシュ&
* optional: キャッシュが存在してもvalidatorでfalseを返すとキャッシュ無効扱いにします
*/
public async fetchMaybe(key: string | null, fetcher: () => Promise<T | undefined>, validator?: (cachedValue: T) => boolean): Promise<T | undefined> {
const cachedValue = this.get(key);
if (cachedValue !== undefined) {
if (validator) {
if (validator(cachedValue)) {
// Cache HIT
return cachedValue;
}
} else {
// Cache HIT
return cachedValue;
}
}
// Cache MISS
const value = await fetcher();
if (value !== undefined) {
this.set(key, value);
}
return value; return value;
} }
} }

View file

@ -1,17 +1,26 @@
import { Antenna } from '@/models/entities/antenna.js'; import { Antenna } from '@/models/entities/antenna.js';
import { Note } from '@/models/entities/note.js'; import { Note } from '@/models/entities/note.js';
import { User } from '@/models/entities/user.js'; import { User } from '@/models/entities/user.js';
import { UserListJoinings, UserGroupJoinings } from '@/models/index.js'; import { UserListJoinings, UserGroupJoinings, Blockings } from '@/models/index.js';
import { getFullApAccount } from './convert-host.js'; import { getFullApAccount } from './convert-host.js';
import * as Acct from '@/misc/acct.js'; import * as Acct from '@/misc/acct.js';
import { Packed } from './schema.js'; import { Packed } from './schema.js';
import { Cache } from './cache.js';
const blockingCache = new Cache<User['id'][]>(1000 * 60 * 5);
// NOTE: フォローしているユーザーのノート、リストのユーザーのノート、グループのユーザーのノート指定はパフォーマンス上の理由で無効になっている
/** /**
* noteUserFollowers / antennaUserFollowing * noteUserFollowers / antennaUserFollowing
*/ */
export async function checkHitAntenna(antenna: Antenna, note: (Note | Packed<'Note'>), noteUser: { username: string; host: string | null; }, noteUserFollowers?: User['id'][], antennaUserFollowing?: User['id'][]): Promise<boolean> { export async function checkHitAntenna(antenna: Antenna, note: (Note | Packed<'Note'>), noteUser: { id: User['id']; username: string; host: string | null; }, noteUserFollowers?: User['id'][], antennaUserFollowing?: User['id'][]): Promise<boolean> {
if (note.visibility === 'specified') return false; if (note.visibility === 'specified') return false;
// アンテナ作成者がノート作成者にブロックされていたらスキップ
const blockings = await blockingCache.fetch(noteUser.id, () => Blockings.findBy({ blockerId: noteUser.id }).then(res => res.map(x => x.blockeeId)));
if (blockings.some(blocking => blocking === antenna.userId)) return false;
if (note.visibility === 'followers') { if (note.visibility === 'followers') {
if (noteUserFollowers && !noteUserFollowers.includes(antenna.userId)) return false; if (noteUserFollowers && !noteUserFollowers.includes(antenna.userId)) return false;
if (antennaUserFollowing && !antennaUserFollowing.includes(note.userId)) return false; if (antennaUserFollowing && !antennaUserFollowing.includes(note.userId)) return false;
@ -23,15 +32,15 @@ export async function checkHitAntenna(antenna: Antenna, note: (Note | Packed<'No
if (noteUserFollowers && !noteUserFollowers.includes(antenna.userId)) return false; if (noteUserFollowers && !noteUserFollowers.includes(antenna.userId)) return false;
if (antennaUserFollowing && !antennaUserFollowing.includes(note.userId)) return false; if (antennaUserFollowing && !antennaUserFollowing.includes(note.userId)) return false;
} else if (antenna.src === 'list') { } else if (antenna.src === 'list') {
const listUsers = (await UserListJoinings.find({ const listUsers = (await UserListJoinings.findBy({
userListId: antenna.userListId!, userListId: antenna.userListId!,
})).map(x => x.userId); })).map(x => x.userId);
if (!listUsers.includes(note.userId)) return false; if (!listUsers.includes(note.userId)) return false;
} else if (antenna.src === 'group') { } else if (antenna.src === 'group') {
const joining = await UserGroupJoinings.findOneOrFail(antenna.userGroupJoiningId!); const joining = await UserGroupJoinings.findOneByOrFail({ id: antenna.userGroupJoiningId! });
const groupUsers = (await UserGroupJoinings.find({ const groupUsers = (await UserGroupJoinings.findBy({
userGroupId: joining.userGroupId, userGroupId: joining.userGroupId,
})).map(x => x.userId); })).map(x => x.userId);

View file

@ -6,7 +6,7 @@ import { httpAgent, httpsAgent, StatusError } from './fetch.js';
import config from '@/config/index.js'; import config from '@/config/index.js';
import chalk from 'chalk'; import chalk from 'chalk';
import Logger from '@/services/logger.js'; import Logger from '@/services/logger.js';
import * as IPCIDR from 'ip-cidr'; import IPCIDR from 'ip-cidr';
import PrivateIp from 'private-ip'; import PrivateIp from 'private-ip';
const pipeline = util.promisify(stream.pipeline); const pipeline = util.promisify(stream.pipeline);

View file

@ -1,19 +1,21 @@
import { db } from '@/db/postgre.js';
import { Meta } from '@/models/entities/meta.js'; import { Meta } from '@/models/entities/meta.js';
import { getConnection } from 'typeorm';
let cache: Meta; let cache: Meta;
export async function fetchMeta(noCache = false): Promise<Meta> { export async function fetchMeta(noCache = false): Promise<Meta> {
if (!noCache && cache) return cache; if (!noCache && cache) return cache;
return await getConnection().transaction(async transactionalEntityManager => { return await db.transaction(async transactionalEntityManager => {
// 過去のバグでレコードが複数出来てしまっている可能性があるので新しいIDを優先する // 過去のバグでレコードが複数出来てしまっている可能性があるので新しいIDを優先する
const meta = await transactionalEntityManager.findOne(Meta, { const metas = await transactionalEntityManager.find(Meta, {
order: { order: {
id: 'DESC', id: 'DESC',
}, },
}); });
const meta = metas[0];
if (meta) { if (meta) {
cache = meta; cache = meta;
return meta; return meta;

View file

@ -5,5 +5,5 @@ import { Users } from '@/models/index.js';
export async function fetchProxyAccount(): Promise<ILocalUser | null> { export async function fetchProxyAccount(): Promise<ILocalUser | null> {
const meta = await fetchMeta(); const meta = await fetchMeta();
if (meta.proxyAccountId == null) return null; if (meta.proxyAccountId == null) return null;
return await Users.findOneOrFail(meta.proxyAccountId) as ILocalUser; return await Users.findOneByOrFail({ id: meta.proxyAccountId }) as ILocalUser;
} }

View file

@ -2,7 +2,7 @@ import * as fs from 'node:fs';
import * as crypto from 'node:crypto'; import * as crypto from 'node:crypto';
import * as stream from 'node:stream'; import * as stream from 'node:stream';
import * as util from 'node:util'; import * as util from 'node:util';
import fileType from 'file-type'; import { fileTypeFromFile } from 'file-type';
import isSvg from 'is-svg'; import isSvg from 'is-svg';
import probeImageSize from 'probe-image-size'; import probeImageSize from 'probe-image-size';
import sharp from 'sharp'; import sharp from 'sharp';
@ -109,7 +109,7 @@ export async function detectType(path: string): Promise<{
return TYPE_OCTET_STREAM; return TYPE_OCTET_STREAM;
} }
const type = await fileType.fromFile(path); const type = await fileTypeFromFile(path);
if (type) { if (type) {
// XMLはSVGかもしれない // XMLはSVGかもしれない

View file

@ -6,5 +6,5 @@ import { Cache } from './cache.js';
const cache = new Cache<UserKeypair>(Infinity); const cache = new Cache<UserKeypair>(Infinity);
export async function getUserKeypair(userId: User['id']): Promise<UserKeypair> { export async function getUserKeypair(userId: User['id']): Promise<UserKeypair> {
return await cache.fetch(userId, () => UserKeypairs.findOneOrFail(userId)); return await cache.fetch(userId, () => UserKeypairs.findOneByOrFail({ userId: userId }));
} }

View file

@ -1,4 +1,4 @@
import { In } from 'typeorm'; import { In, IsNull } from 'typeorm';
import { Emojis } from '@/models/index.js'; import { Emojis } from '@/models/index.js';
import { Emoji } from '@/models/entities/emoji.js'; import { Emoji } from '@/models/entities/emoji.js';
import { Note } from '@/models/entities/note.js'; import { Note } from '@/models/entities/note.js';
@ -52,9 +52,9 @@ export async function populateEmoji(emojiName: string, noteUserHost: string | nu
const { name, host } = parseEmojiStr(emojiName, noteUserHost); const { name, host } = parseEmojiStr(emojiName, noteUserHost);
if (name == null) return null; if (name == null) return null;
const queryOrNull = async () => (await Emojis.findOne({ const queryOrNull = async () => (await Emojis.findOneBy({
name, name,
host, host: host ?? IsNull(),
})) || null; })) || null;
const emoji = await cache.fetch(`${name} ${host}`, queryOrNull); const emoji = await cache.fetch(`${name} ${host}`, queryOrNull);
@ -112,7 +112,7 @@ export async function prefetchEmojis(emojis: { name: string; host: string | null
for (const host of hosts) { for (const host of hosts) {
emojisQuery.push({ emojisQuery.push({
name: In(notCachedEmojis.filter(e => e.host === host).map(e => e.name)), name: In(notCachedEmojis.filter(e => e.host === host).map(e => e.name)),
host: host, host: host ?? IsNull(),
}); });
} }
const _emojis = emojisQuery.length > 0 ? await Emojis.find({ const _emojis = emojisQuery.length > 0 ? await Emojis.find({

View file

@ -3,6 +3,7 @@ import { emojiRegex } from './emoji-regex.js';
import { fetchMeta } from './fetch-meta.js'; import { fetchMeta } from './fetch-meta.js';
import { Emojis } from '@/models/index.js'; import { Emojis } from '@/models/index.js';
import { toPunyNullable } from './convert-host.js'; import { toPunyNullable } from './convert-host.js';
import { IsNull } from 'typeorm';
const legacies: Record<string, string> = { const legacies: Record<string, string> = {
'like': '👍', 'like': '👍',
@ -74,8 +75,8 @@ export async function toDbReaction(reaction?: string | null, reacterHost?: strin
const custom = reaction.match(/^:([\w+-]+)(?:@\.)?:$/); const custom = reaction.match(/^:([\w+-]+)(?:@\.)?:$/);
if (custom) { if (custom) {
const name = custom[1]; const name = custom[1];
const emoji = await Emojis.findOne({ const emoji = await Emojis.findOneBy({
host: reacterHost || null, host: reacterHost ?? IsNull(),
name, name,
}); });

View file

@ -59,22 +59,6 @@ export class Instance {
}) })
public followersCount: number; public followersCount: number;
/**
* 使
*/
@Column('bigint', {
default: 0,
})
public driveUsage: number;
/**
*
*/
@Column('integer', {
default: 0,
})
public driveFiles: number;
/** /**
* *
*/ */

View file

@ -14,6 +14,13 @@ export class Muting {
}) })
public createdAt: Date; public createdAt: Date;
@Index()
@Column('timestamp with time zone', {
nullable: true,
default: null,
})
public expiresAt: Date | null;
@Index() @Index()
@Column({ @Column({
...id(), ...id(),

View file

@ -59,7 +59,8 @@ export class Notification {
* renote - (Watchしている)稿Renoteされた * renote - (Watchしている)稿Renoteされた
* quote - (Watchしている)稿Renoteされた * quote - (Watchしている)稿Renoteされた
* reaction - (Watchしている)稿 * reaction - (Watchしている)稿
* pollVote - (Watchしている)稿 * pollVote - (Watchしている)稿
* pollEnded -
* receiveFollowRequest - * receiveFollowRequest -
* followRequestAccepted - * followRequestAccepted -
* groupInvited - * groupInvited -

View file

@ -234,3 +234,9 @@ export interface ILocalUser extends User {
export interface IRemoteUser extends User { export interface IRemoteUser extends User {
host: string; host: string;
} }
export type CacheableLocalUser = ILocalUser;
export type CacheableRemoteUser = IRemoteUser;
export type CacheableUser = CacheableLocalUser | CacheableRemoteUser;

View file

@ -1,4 +1,6 @@
import { getRepository, getCustomRepository } from 'typeorm'; import { } from 'typeorm';
import { db } from '@/db/postgre.js';
import { Announcement } from './entities/announcement.js'; import { Announcement } from './entities/announcement.js';
import { AnnouncementRead } from './entities/announcement-read.js'; import { AnnouncementRead } from './entities/announcement-read.js';
import { Instance } from './entities/instance.js'; import { Instance } from './entities/instance.js';
@ -63,65 +65,65 @@ import { PasswordResetRequest } from './entities/password-reset-request.js';
import { UserPending } from './entities/user-pending.js'; import { UserPending } from './entities/user-pending.js';
import { InstanceRepository } from './repositories/instance.js'; import { InstanceRepository } from './repositories/instance.js';
export const Announcements = getRepository(Announcement); export const Announcements = db.getRepository(Announcement);
export const AnnouncementReads = getRepository(AnnouncementRead); export const AnnouncementReads = db.getRepository(AnnouncementRead);
export const Apps = getCustomRepository(AppRepository); export const Apps = (AppRepository);
export const Notes = getCustomRepository(NoteRepository); export const Notes = (NoteRepository);
export const NoteFavorites = getCustomRepository(NoteFavoriteRepository); export const NoteFavorites = (NoteFavoriteRepository);
export const NoteWatchings = getRepository(NoteWatching); export const NoteWatchings = db.getRepository(NoteWatching);
export const NoteThreadMutings = getRepository(NoteThreadMuting); export const NoteThreadMutings = db.getRepository(NoteThreadMuting);
export const NoteReactions = getCustomRepository(NoteReactionRepository); export const NoteReactions = (NoteReactionRepository);
export const NoteUnreads = getRepository(NoteUnread); export const NoteUnreads = db.getRepository(NoteUnread);
export const Polls = getRepository(Poll); export const Polls = db.getRepository(Poll);
export const PollVotes = getRepository(PollVote); export const PollVotes = db.getRepository(PollVote);
export const Users = getCustomRepository(UserRepository); export const Users = (UserRepository);
export const UserProfiles = getRepository(UserProfile); export const UserProfiles = db.getRepository(UserProfile);
export const UserKeypairs = getRepository(UserKeypair); export const UserKeypairs = db.getRepository(UserKeypair);
export const UserPendings = getRepository(UserPending); export const UserPendings = db.getRepository(UserPending);
export const AttestationChallenges = getRepository(AttestationChallenge); export const AttestationChallenges = db.getRepository(AttestationChallenge);
export const UserSecurityKeys = getRepository(UserSecurityKey); export const UserSecurityKeys = db.getRepository(UserSecurityKey);
export const UserPublickeys = getRepository(UserPublickey); export const UserPublickeys = db.getRepository(UserPublickey);
export const UserLists = getCustomRepository(UserListRepository); export const UserLists = (UserListRepository);
export const UserListJoinings = getRepository(UserListJoining); export const UserListJoinings = db.getRepository(UserListJoining);
export const UserGroups = getCustomRepository(UserGroupRepository); export const UserGroups = (UserGroupRepository);
export const UserGroupJoinings = getRepository(UserGroupJoining); export const UserGroupJoinings = db.getRepository(UserGroupJoining);
export const UserGroupInvitations = getCustomRepository(UserGroupInvitationRepository); export const UserGroupInvitations = (UserGroupInvitationRepository);
export const UserNotePinings = getRepository(UserNotePining); export const UserNotePinings = db.getRepository(UserNotePining);
export const UsedUsernames = getRepository(UsedUsername); export const UsedUsernames = db.getRepository(UsedUsername);
export const Followings = getCustomRepository(FollowingRepository); export const Followings = (FollowingRepository);
export const FollowRequests = getCustomRepository(FollowRequestRepository); export const FollowRequests = (FollowRequestRepository);
export const Instances = getCustomRepository(InstanceRepository); export const Instances = (InstanceRepository);
export const Emojis = getCustomRepository(EmojiRepository); export const Emojis = (EmojiRepository);
export const DriveFiles = getCustomRepository(DriveFileRepository); export const DriveFiles = (DriveFileRepository);
export const DriveFolders = getCustomRepository(DriveFolderRepository); export const DriveFolders = (DriveFolderRepository);
export const Notifications = getCustomRepository(NotificationRepository); export const Notifications = (NotificationRepository);
export const Metas = getRepository(Meta); export const Metas = db.getRepository(Meta);
export const Mutings = getCustomRepository(MutingRepository); export const Mutings = (MutingRepository);
export const Blockings = getCustomRepository(BlockingRepository); export const Blockings = (BlockingRepository);
export const SwSubscriptions = getRepository(SwSubscription); export const SwSubscriptions = db.getRepository(SwSubscription);
export const Hashtags = getCustomRepository(HashtagRepository); export const Hashtags = (HashtagRepository);
export const AbuseUserReports = getCustomRepository(AbuseUserReportRepository); export const AbuseUserReports = (AbuseUserReportRepository);
export const RegistrationTickets = getRepository(RegistrationTicket); export const RegistrationTickets = db.getRepository(RegistrationTicket);
export const AuthSessions = getCustomRepository(AuthSessionRepository); export const AuthSessions = (AuthSessionRepository);
export const AccessTokens = getRepository(AccessToken); export const AccessTokens = db.getRepository(AccessToken);
export const Signins = getCustomRepository(SigninRepository); export const Signins = (SigninRepository);
export const MessagingMessages = getCustomRepository(MessagingMessageRepository); export const MessagingMessages = (MessagingMessageRepository);
export const Pages = getCustomRepository(PageRepository); export const Pages = (PageRepository);
export const PageLikes = getCustomRepository(PageLikeRepository); export const PageLikes = (PageLikeRepository);
export const GalleryPosts = getCustomRepository(GalleryPostRepository); export const GalleryPosts = (GalleryPostRepository);
export const GalleryLikes = getCustomRepository(GalleryLikeRepository); export const GalleryLikes = (GalleryLikeRepository);
export const ModerationLogs = getCustomRepository(ModerationLogRepository); export const ModerationLogs = (ModerationLogRepository);
export const Clips = getCustomRepository(ClipRepository); export const Clips = (ClipRepository);
export const ClipNotes = getRepository(ClipNote); export const ClipNotes = db.getRepository(ClipNote);
export const Antennas = getCustomRepository(AntennaRepository); export const Antennas = (AntennaRepository);
export const AntennaNotes = getRepository(AntennaNote); export const AntennaNotes = db.getRepository(AntennaNote);
export const PromoNotes = getRepository(PromoNote); export const PromoNotes = db.getRepository(PromoNote);
export const PromoReads = getRepository(PromoRead); export const PromoReads = db.getRepository(PromoRead);
export const Relays = getCustomRepository(RelayRepository); export const Relays = (RelayRepository);
export const MutedNotes = getRepository(MutedNote); export const MutedNotes = db.getRepository(MutedNote);
export const Channels = getCustomRepository(ChannelRepository); export const Channels = (ChannelRepository);
export const ChannelFollowings = getRepository(ChannelFollowing); export const ChannelFollowings = db.getRepository(ChannelFollowing);
export const ChannelNotePinings = getRepository(ChannelNotePining); export const ChannelNotePinings = db.getRepository(ChannelNotePining);
export const RegistryItems = getRepository(RegistryItem); export const RegistryItems = db.getRepository(RegistryItem);
export const Ads = getRepository(Ad); export const Ads = db.getRepository(Ad);
export const PasswordResetRequests = getRepository(PasswordResetRequest); export const PasswordResetRequests = db.getRepository(PasswordResetRequest);

View file

@ -1,14 +1,13 @@
import { EntityRepository, Repository } from 'typeorm'; import { db } from '@/db/postgre.js';
import { Users } from '../index.js'; import { Users } from '../index.js';
import { AbuseUserReport } from '@/models/entities/abuse-user-report.js'; import { AbuseUserReport } from '@/models/entities/abuse-user-report.js';
import { awaitAll } from '@/prelude/await-all.js'; import { awaitAll } from '@/prelude/await-all.js';
@EntityRepository(AbuseUserReport) export const AbuseUserReportRepository = db.getRepository(AbuseUserReport).extend({
export class AbuseUserReportRepository extends Repository<AbuseUserReport> { async pack(
public async pack(
src: AbuseUserReport['id'] | AbuseUserReport, src: AbuseUserReport['id'] | AbuseUserReport,
) { ) {
const report = typeof src === 'object' ? src : await this.findOneOrFail(src); const report = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
return await awaitAll({ return await awaitAll({
id: report.id, id: report.id,
@ -29,11 +28,11 @@ export class AbuseUserReportRepository extends Repository<AbuseUserReport> {
}) : null, }) : null,
forwarded: report.forwarded, forwarded: report.forwarded,
}); });
} },
public packMany( packMany(
reports: any[], reports: any[],
) { ) {
return Promise.all(reports.map(x => this.pack(x))); return Promise.all(reports.map(x => this.pack(x)));
} },
} });

View file

@ -1,17 +1,16 @@
import { EntityRepository, Repository } from 'typeorm'; import { db } from '@/db/postgre.js';
import { Antenna } from '@/models/entities/antenna.js'; import { Antenna } from '@/models/entities/antenna.js';
import { Packed } from '@/misc/schema.js'; import { Packed } from '@/misc/schema.js';
import { AntennaNotes, UserGroupJoinings } from '../index.js'; import { AntennaNotes, UserGroupJoinings } from '../index.js';
@EntityRepository(Antenna) export const AntennaRepository = db.getRepository(Antenna).extend({
export class AntennaRepository extends Repository<Antenna> { async pack(
public async pack(
src: Antenna['id'] | Antenna, src: Antenna['id'] | Antenna,
): Promise<Packed<'Antenna'>> { ): Promise<Packed<'Antenna'>> {
const antenna = typeof src === 'object' ? src : await this.findOneOrFail(src); const antenna = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
const hasUnreadNote = (await AntennaNotes.findOne({ antennaId: antenna.id, read: false })) != null; const hasUnreadNote = (await AntennaNotes.findOneBy({ antennaId: antenna.id, read: false })) != null;
const userGroupJoining = antenna.userGroupJoiningId ? await UserGroupJoinings.findOne(antenna.userGroupJoiningId) : null; const userGroupJoining = antenna.userGroupJoiningId ? await UserGroupJoinings.findOneBy({ id: antenna.userGroupJoiningId }) : null;
return { return {
id: antenna.id, id: antenna.id,
@ -29,5 +28,5 @@ export class AntennaRepository extends Repository<Antenna> {
withFile: antenna.withFile, withFile: antenna.withFile,
hasUnreadNote, hasUnreadNote,
}; };
} },
} });

View file

@ -1,12 +1,11 @@
import { EntityRepository, Repository } from 'typeorm'; import { db } from '@/db/postgre.js';
import { App } from '@/models/entities/app.js'; import { App } from '@/models/entities/app.js';
import { AccessTokens } from '../index.js'; import { AccessTokens } from '../index.js';
import { Packed } from '@/misc/schema.js'; import { Packed } from '@/misc/schema.js';
import { User } from '../entities/user.js'; import { User } from '../entities/user.js';
@EntityRepository(App) export const AppRepository = db.getRepository(App).extend({
export class AppRepository extends Repository<App> { async pack(
public async pack(
src: App['id'] | App, src: App['id'] | App,
me?: { id: User['id'] } | null | undefined, me?: { id: User['id'] } | null | undefined,
options?: { options?: {
@ -21,7 +20,7 @@ export class AppRepository extends Repository<App> {
includeProfileImageIds: false, includeProfileImageIds: false,
}, options); }, options);
const app = typeof src === 'object' ? src : await this.findOneOrFail(src); const app = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
return { return {
id: app.id, id: app.id,
@ -30,11 +29,11 @@ export class AppRepository extends Repository<App> {
permission: app.permission, permission: app.permission,
...(opts.includeSecret ? { secret: app.secret } : {}), ...(opts.includeSecret ? { secret: app.secret } : {}),
...(me ? { ...(me ? {
isAuthorized: await AccessTokens.count({ isAuthorized: await AccessTokens.countBy({
appId: app.id, appId: app.id,
userId: me.id, userId: me.id,
}).then(count => count > 0), }).then(count => count > 0),
} : {}), } : {}),
}; };
} },
} });

View file

@ -1,21 +1,20 @@
import { EntityRepository, Repository } from 'typeorm'; import { db } from '@/db/postgre.js';
import { Apps } from '../index.js'; import { Apps } from '../index.js';
import { AuthSession } from '@/models/entities/auth-session.js'; import { AuthSession } from '@/models/entities/auth-session.js';
import { awaitAll } from '@/prelude/await-all.js'; import { awaitAll } from '@/prelude/await-all.js';
import { User } from '@/models/entities/user.js'; import { User } from '@/models/entities/user.js';
@EntityRepository(AuthSession) export const AuthSessionRepository = db.getRepository(AuthSession).extend({
export class AuthSessionRepository extends Repository<AuthSession> { async pack(
public async pack(
src: AuthSession['id'] | AuthSession, src: AuthSession['id'] | AuthSession,
me?: { id: User['id'] } | null | undefined me?: { id: User['id'] } | null | undefined
) { ) {
const session = typeof src === 'object' ? src : await this.findOneOrFail(src); const session = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
return await awaitAll({ return await awaitAll({
id: session.id, id: session.id,
app: Apps.pack(session.appId, me), app: Apps.pack(session.appId, me),
token: session.token, token: session.token,
}); });
} },
} });

View file

@ -1,17 +1,16 @@
import { EntityRepository, Repository } from 'typeorm'; import { db } from '@/db/postgre.js';
import { Users } from '../index.js'; import { Users } from '../index.js';
import { Blocking } from '@/models/entities/blocking.js'; import { Blocking } from '@/models/entities/blocking.js';
import { awaitAll } from '@/prelude/await-all.js'; import { awaitAll } from '@/prelude/await-all.js';
import { Packed } from '@/misc/schema.js'; import { Packed } from '@/misc/schema.js';
import { User } from '@/models/entities/user.js'; import { User } from '@/models/entities/user.js';
@EntityRepository(Blocking) export const BlockingRepository = db.getRepository(Blocking).extend({
export class BlockingRepository extends Repository<Blocking> { async pack(
public async pack(
src: Blocking['id'] | Blocking, src: Blocking['id'] | Blocking,
me?: { id: User['id'] } | null | undefined me?: { id: User['id'] } | null | undefined
): Promise<Packed<'Blocking'>> { ): Promise<Packed<'Blocking'>> {
const blocking = typeof src === 'object' ? src : await this.findOneOrFail(src); const blocking = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
return await awaitAll({ return await awaitAll({
id: blocking.id, id: blocking.id,
@ -21,12 +20,12 @@ export class BlockingRepository extends Repository<Blocking> {
detail: true, detail: true,
}), }),
}); });
} },
public packMany( packMany(
blockings: any[], blockings: any[],
me: { id: User['id'] } me: { id: User['id'] }
) { ) {
return Promise.all(blockings.map(x => this.pack(x, me))); return Promise.all(blockings.map(x => this.pack(x, me)));
} },
} });

View file

@ -1,23 +1,22 @@
import { EntityRepository, Repository } from 'typeorm'; import { db } from '@/db/postgre.js';
import { Channel } from '@/models/entities/channel.js'; import { Channel } from '@/models/entities/channel.js';
import { Packed } from '@/misc/schema.js'; import { Packed } from '@/misc/schema.js';
import { DriveFiles, ChannelFollowings, NoteUnreads } from '../index.js'; import { DriveFiles, ChannelFollowings, NoteUnreads } from '../index.js';
import { User } from '@/models/entities/user.js'; import { User } from '@/models/entities/user.js';
@EntityRepository(Channel) export const ChannelRepository = db.getRepository(Channel).extend({
export class ChannelRepository extends Repository<Channel> { async pack(
public async pack(
src: Channel['id'] | Channel, src: Channel['id'] | Channel,
me?: { id: User['id'] } | null | undefined, me?: { id: User['id'] } | null | undefined,
): Promise<Packed<'Channel'>> { ): Promise<Packed<'Channel'>> {
const channel = typeof src === 'object' ? src : await this.findOneOrFail(src); const channel = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
const meId = me ? me.id : null; const meId = me ? me.id : null;
const banner = channel.bannerId ? await DriveFiles.findOne(channel.bannerId) : null; const banner = channel.bannerId ? await DriveFiles.findOneBy({ id: channel.bannerId }) : null;
const hasUnreadNote = meId ? (await NoteUnreads.findOne({ noteChannelId: channel.id, userId: meId })) != null : undefined; const hasUnreadNote = meId ? (await NoteUnreads.findOneBy({ noteChannelId: channel.id, userId: meId })) != null : undefined;
const following = meId ? await ChannelFollowings.findOne({ const following = meId ? await ChannelFollowings.findOneBy({
followerId: meId, followerId: meId,
followeeId: channel.id, followeeId: channel.id,
}) : null; }) : null;
@ -38,5 +37,5 @@ export class ChannelRepository extends Repository<Channel> {
hasUnreadNote, hasUnreadNote,
} : {}), } : {}),
}; };
} },
} });

View file

@ -1,15 +1,14 @@
import { EntityRepository, Repository } from 'typeorm'; import { db } from '@/db/postgre.js';
import { Clip } from '@/models/entities/clip.js'; import { Clip } from '@/models/entities/clip.js';
import { Packed } from '@/misc/schema.js'; import { Packed } from '@/misc/schema.js';
import { Users } from '../index.js'; import { Users } from '../index.js';
import { awaitAll } from '@/prelude/await-all.js'; import { awaitAll } from '@/prelude/await-all.js';
@EntityRepository(Clip) export const ClipRepository = db.getRepository(Clip).extend({
export class ClipRepository extends Repository<Clip> { async pack(
public async pack(
src: Clip['id'] | Clip, src: Clip['id'] | Clip,
): Promise<Packed<'Clip'>> { ): Promise<Packed<'Clip'>> {
const clip = typeof src === 'object' ? src : await this.findOneOrFail(src); const clip = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
return await awaitAll({ return await awaitAll({
id: clip.id, id: clip.id,
@ -20,12 +19,12 @@ export class ClipRepository extends Repository<Clip> {
description: clip.description, description: clip.description,
isPublic: clip.isPublic, isPublic: clip.isPublic,
}); });
} },
public packMany( packMany(
clips: Clip[], clips: Clip[],
) { ) {
return Promise.all(clips.map(x => this.pack(x))); return Promise.all(clips.map(x => this.pack(x)));
} },
} });

View file

@ -1,4 +1,4 @@
import { EntityRepository, Repository } from 'typeorm'; import { db } from '@/db/postgre.js';
import { DriveFile } from '@/models/entities/drive-file.js'; import { DriveFile } from '@/models/entities/drive-file.js';
import { Users, DriveFolders } from '../index.js'; import { Users, DriveFolders } from '../index.js';
import { User } from '@/models/entities/user.js'; import { User } from '@/models/entities/user.js';
@ -16,9 +16,8 @@ type PackOptions = {
withUser?: boolean, withUser?: boolean,
}; };
@EntityRepository(DriveFile) export const DriveFileRepository = db.getRepository(DriveFile).extend({
export class DriveFileRepository extends Repository<DriveFile> { validateFileName(name: string): boolean {
public validateFileName(name: string): boolean {
return ( return (
(name.trim().length > 0) && (name.trim().length > 0) &&
(name.length <= 200) && (name.length <= 200) &&
@ -26,9 +25,9 @@ export class DriveFileRepository extends Repository<DriveFile> {
(name.indexOf('/') === -1) && (name.indexOf('/') === -1) &&
(name.indexOf('..') === -1) (name.indexOf('..') === -1)
); );
} },
public getPublicProperties(file: DriveFile): DriveFile['properties'] { getPublicProperties(file: DriveFile): DriveFile['properties'] {
if (file.properties.orientation != null) { if (file.properties.orientation != null) {
const properties = JSON.parse(JSON.stringify(file.properties)); const properties = JSON.parse(JSON.stringify(file.properties));
if (file.properties.orientation >= 5) { if (file.properties.orientation >= 5) {
@ -39,9 +38,9 @@ export class DriveFileRepository extends Repository<DriveFile> {
} }
return file.properties; return file.properties;
} },
public getPublicUrl(file: DriveFile, thumbnail = false): string | null { getPublicUrl(file: DriveFile, thumbnail = false): string | null {
// リモートかつメディアプロキシ // リモートかつメディアプロキシ
if (file.uri != null && file.userHost != null && config.mediaProxy != null) { if (file.uri != null && file.userHost != null && config.mediaProxy != null) {
return appendQuery(config.mediaProxy, query({ return appendQuery(config.mediaProxy, query({
@ -62,9 +61,9 @@ export class DriveFileRepository extends Repository<DriveFile> {
const isImage = file.type && ['image/png', 'image/apng', 'image/gif', 'image/jpeg', 'image/webp', 'image/svg+xml'].includes(file.type); const isImage = file.type && ['image/png', 'image/apng', 'image/gif', 'image/jpeg', 'image/webp', 'image/svg+xml'].includes(file.type);
return thumbnail ? (file.thumbnailUrl || (isImage ? (file.webpublicUrl || file.url) : null)) : (file.webpublicUrl || file.url); return thumbnail ? (file.thumbnailUrl || (isImage ? (file.webpublicUrl || file.url) : null)) : (file.webpublicUrl || file.url);
} },
public async calcDriveUsageOf(user: User['id'] | { id: User['id'] }): Promise<number> { async calcDriveUsageOf(user: User['id'] | { id: User['id'] }): Promise<number> {
const id = typeof user === 'object' ? user.id : user; const id = typeof user === 'object' ? user.id : user;
const { sum } = await this const { sum } = await this
@ -75,9 +74,9 @@ export class DriveFileRepository extends Repository<DriveFile> {
.getRawOne(); .getRawOne();
return parseInt(sum, 10) || 0; return parseInt(sum, 10) || 0;
} },
public async calcDriveUsageOfHost(host: string): Promise<number> { async calcDriveUsageOfHost(host: string): Promise<number> {
const { sum } = await this const { sum } = await this
.createQueryBuilder('file') .createQueryBuilder('file')
.where('file.userHost = :host', { host: toPuny(host) }) .where('file.userHost = :host', { host: toPuny(host) })
@ -86,9 +85,9 @@ export class DriveFileRepository extends Repository<DriveFile> {
.getRawOne(); .getRawOne();
return parseInt(sum, 10) || 0; return parseInt(sum, 10) || 0;
} },
public async calcDriveUsageOfLocal(): Promise<number> { async calcDriveUsageOfLocal(): Promise<number> {
const { sum } = await this const { sum } = await this
.createQueryBuilder('file') .createQueryBuilder('file')
.where('file.userHost IS NULL') .where('file.userHost IS NULL')
@ -97,9 +96,9 @@ export class DriveFileRepository extends Repository<DriveFile> {
.getRawOne(); .getRawOne();
return parseInt(sum, 10) || 0; return parseInt(sum, 10) || 0;
} },
public async calcDriveUsageOfRemote(): Promise<number> { async calcDriveUsageOfRemote(): Promise<number> {
const { sum } = await this const { sum } = await this
.createQueryBuilder('file') .createQueryBuilder('file')
.where('file.userHost IS NOT NULL') .where('file.userHost IS NOT NULL')
@ -108,11 +107,9 @@ export class DriveFileRepository extends Repository<DriveFile> {
.getRawOne(); .getRawOne();
return parseInt(sum, 10) || 0; return parseInt(sum, 10) || 0;
} },
public async pack(src: DriveFile['id'], options?: PackOptions): Promise<Packed<'DriveFile'> | null>; async pack(
public async pack(src: DriveFile, options?: PackOptions): Promise<Packed<'DriveFile'>>;
public async pack(
src: DriveFile['id'] | DriveFile, src: DriveFile['id'] | DriveFile,
options?: PackOptions options?: PackOptions
): Promise<Packed<'DriveFile'> | null> { ): Promise<Packed<'DriveFile'> | null> {
@ -121,11 +118,9 @@ export class DriveFileRepository extends Repository<DriveFile> {
self: false, self: false,
}, options); }, options);
const file = typeof src === 'object' ? src : await this.findOne(src); const file = typeof src === 'object' ? src : await this.findOneBy({ id: src });
if (file == null) return null; if (file == null) return null;
const meta = await fetchMeta();
return await awaitAll<Packed<'DriveFile'>>({ return await awaitAll<Packed<'DriveFile'>>({
id: file.id, id: file.id,
createdAt: file.createdAt.toISOString(), createdAt: file.createdAt.toISOString(),
@ -146,13 +141,13 @@ export class DriveFileRepository extends Repository<DriveFile> {
userId: opts.withUser ? file.userId : null, userId: opts.withUser ? file.userId : null,
user: (opts.withUser && file.userId) ? Users.pack(file.userId) : null, user: (opts.withUser && file.userId) ? Users.pack(file.userId) : null,
}); });
} },
public async packMany( async packMany(
files: (DriveFile['id'] | DriveFile)[], files: (DriveFile['id'] | DriveFile)[],
options?: PackOptions options?: PackOptions
) { ) {
const items = await Promise.all(files.map(f => this.pack(f, options))); const items = await Promise.all(files.map(f => this.pack(f, options)));
return items.filter(x => x != null); return items.filter(x => x != null);
} },
} });

View file

@ -1,12 +1,11 @@
import { EntityRepository, Repository } from 'typeorm'; import { db } from '@/db/postgre.js';
import { DriveFolders, DriveFiles } from '../index.js'; import { DriveFolders, DriveFiles } from '../index.js';
import { DriveFolder } from '@/models/entities/drive-folder.js'; import { DriveFolder } from '@/models/entities/drive-folder.js';
import { awaitAll } from '@/prelude/await-all.js'; import { awaitAll } from '@/prelude/await-all.js';
import { Packed } from '@/misc/schema.js'; import { Packed } from '@/misc/schema.js';
@EntityRepository(DriveFolder) export const DriveFolderRepository = db.getRepository(DriveFolder).extend({
export class DriveFolderRepository extends Repository<DriveFolder> { async pack(
public async pack(
src: DriveFolder['id'] | DriveFolder, src: DriveFolder['id'] | DriveFolder,
options?: { options?: {
detail: boolean detail: boolean
@ -16,7 +15,7 @@ export class DriveFolderRepository extends Repository<DriveFolder> {
detail: false, detail: false,
}, options); }, options);
const folder = typeof src === 'object' ? src : await this.findOneOrFail(src); const folder = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
return await awaitAll({ return await awaitAll({
id: folder.id, id: folder.id,
@ -25,10 +24,10 @@ export class DriveFolderRepository extends Repository<DriveFolder> {
parentId: folder.parentId, parentId: folder.parentId,
...(opts.detail ? { ...(opts.detail ? {
foldersCount: DriveFolders.count({ foldersCount: DriveFolders.countBy({
parentId: folder.id, parentId: folder.id,
}), }),
filesCount: DriveFiles.count({ filesCount: DriveFiles.countBy({
folderId: folder.id, folderId: folder.id,
}), }),
@ -39,5 +38,5 @@ export class DriveFolderRepository extends Repository<DriveFolder> {
} : {}), } : {}),
} : {}), } : {}),
}); });
} },
} });

View file

@ -1,13 +1,12 @@
import { EntityRepository, Repository } from 'typeorm'; import { db } from '@/db/postgre.js';
import { Emoji } from '@/models/entities/emoji.js'; import { Emoji } from '@/models/entities/emoji.js';
import { Packed } from '@/misc/schema.js'; import { Packed } from '@/misc/schema.js';
@EntityRepository(Emoji) export const EmojiRepository = db.getRepository(Emoji).extend({
export class EmojiRepository extends Repository<Emoji> { async pack(
public async pack(
src: Emoji['id'] | Emoji, src: Emoji['id'] | Emoji,
): Promise<Packed<'Emoji'>> { ): Promise<Packed<'Emoji'>> {
const emoji = typeof src === 'object' ? src : await this.findOneOrFail(src); const emoji = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
return { return {
id: emoji.id, id: emoji.id,
@ -18,11 +17,11 @@ export class EmojiRepository extends Repository<Emoji> {
// || emoji.originalUrl してるのは後方互換性のため // || emoji.originalUrl してるのは後方互換性のため
url: emoji.publicUrl || emoji.originalUrl, url: emoji.publicUrl || emoji.originalUrl,
}; };
} },
public packMany( packMany(
emojis: any[], emojis: any[],
) { ) {
return Promise.all(emojis.map(x => this.pack(x))); return Promise.all(emojis.map(x => this.pack(x)));
} },
} });

View file

@ -1,20 +1,19 @@
import { EntityRepository, Repository } from 'typeorm'; import { db } from '@/db/postgre.js';
import { FollowRequest } from '@/models/entities/follow-request.js'; import { FollowRequest } from '@/models/entities/follow-request.js';
import { Users } from '../index.js'; import { Users } from '../index.js';
import { User } from '@/models/entities/user.js'; import { User } from '@/models/entities/user.js';
@EntityRepository(FollowRequest) export const FollowRequestRepository = db.getRepository(FollowRequest).extend({
export class FollowRequestRepository extends Repository<FollowRequest> { async pack(
public async pack(
src: FollowRequest['id'] | FollowRequest, src: FollowRequest['id'] | FollowRequest,
me?: { id: User['id'] } | null | undefined me?: { id: User['id'] } | null | undefined
) { ) {
const request = typeof src === 'object' ? src : await this.findOneOrFail(src); const request = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
return { return {
id: request.id, id: request.id,
follower: await Users.pack(request.followerId, me), follower: await Users.pack(request.followerId, me),
followee: await Users.pack(request.followeeId, me), followee: await Users.pack(request.followeeId, me),
}; };
} },
} });

View file

@ -1,4 +1,4 @@
import { EntityRepository, Repository } from 'typeorm'; import { db } from '@/db/postgre.js';
import { Users } from '../index.js'; import { Users } from '../index.js';
import { Following } from '@/models/entities/following.js'; import { Following } from '@/models/entities/following.js';
import { awaitAll } from '@/prelude/await-all.js'; import { awaitAll } from '@/prelude/await-all.js';
@ -29,25 +29,24 @@ type RemoteFolloweeFollowing = Following & {
followeeSharedInbox: string; followeeSharedInbox: string;
}; };
@EntityRepository(Following) export const FollowingRepository = db.getRepository(Following).extend({
export class FollowingRepository extends Repository<Following> { isLocalFollower(following: Following): following is LocalFollowerFollowing {
public isLocalFollower(following: Following): following is LocalFollowerFollowing {
return following.followerHost == null; return following.followerHost == null;
} },
public isRemoteFollower(following: Following): following is RemoteFollowerFollowing { isRemoteFollower(following: Following): following is RemoteFollowerFollowing {
return following.followerHost != null; return following.followerHost != null;
} },
public isLocalFollowee(following: Following): following is LocalFolloweeFollowing { isLocalFollowee(following: Following): following is LocalFolloweeFollowing {
return following.followeeHost == null; return following.followeeHost == null;
} },
public isRemoteFollowee(following: Following): following is RemoteFolloweeFollowing { isRemoteFollowee(following: Following): following is RemoteFolloweeFollowing {
return following.followeeHost != null; return following.followeeHost != null;
} },
public async pack( async pack(
src: Following['id'] | Following, src: Following['id'] | Following,
me?: { id: User['id'] } | null | undefined, me?: { id: User['id'] } | null | undefined,
opts?: { opts?: {
@ -55,7 +54,7 @@ export class FollowingRepository extends Repository<Following> {
populateFollower?: boolean; populateFollower?: boolean;
} }
): Promise<Packed<'Following'>> { ): Promise<Packed<'Following'>> {
const following = typeof src === 'object' ? src : await this.findOneOrFail(src); const following = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
if (opts == null) opts = {}; if (opts == null) opts = {};
@ -71,9 +70,9 @@ export class FollowingRepository extends Repository<Following> {
detail: true, detail: true,
}) : undefined, }) : undefined,
}); });
} },
public packMany( packMany(
followings: any[], followings: any[],
me?: { id: User['id'] } | null | undefined, me?: { id: User['id'] } | null | undefined,
opts?: { opts?: {
@ -82,5 +81,5 @@ export class FollowingRepository extends Repository<Following> {
} }
) { ) {
return Promise.all(followings.map(x => this.pack(x, me, opts))); return Promise.all(followings.map(x => this.pack(x, me, opts)));
} },
} });

View file

@ -1,25 +1,24 @@
import { EntityRepository, Repository } from 'typeorm'; import { db } from '@/db/postgre.js';
import { GalleryLike } from '@/models/entities/gallery-like.js'; import { GalleryLike } from '@/models/entities/gallery-like.js';
import { GalleryPosts } from '../index.js'; import { GalleryPosts } from '../index.js';
@EntityRepository(GalleryLike) export const GalleryLikeRepository = db.getRepository(GalleryLike).extend({
export class GalleryLikeRepository extends Repository<GalleryLike> { async pack(
public async pack(
src: GalleryLike['id'] | GalleryLike, src: GalleryLike['id'] | GalleryLike,
me?: any me?: any
) { ) {
const like = typeof src === 'object' ? src : await this.findOneOrFail(src); const like = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
return { return {
id: like.id, id: like.id,
post: await GalleryPosts.pack(like.post || like.postId, me), post: await GalleryPosts.pack(like.post || like.postId, me),
}; };
} },
public packMany( packMany(
likes: any[], likes: any[],
me: any me: any
) { ) {
return Promise.all(likes.map(x => this.pack(x, me))); return Promise.all(likes.map(x => this.pack(x, me)));
} },
} });

View file

@ -1,18 +1,17 @@
import { EntityRepository, Repository } from 'typeorm'; import { db } from '@/db/postgre.js';
import { GalleryPost } from '@/models/entities/gallery-post.js'; import { GalleryPost } from '@/models/entities/gallery-post.js';
import { Packed } from '@/misc/schema.js'; import { Packed } from '@/misc/schema.js';
import { Users, DriveFiles, GalleryLikes } from '../index.js'; import { Users, DriveFiles, GalleryLikes } from '../index.js';
import { awaitAll } from '@/prelude/await-all.js'; import { awaitAll } from '@/prelude/await-all.js';
import { User } from '@/models/entities/user.js'; import { User } from '@/models/entities/user.js';
@EntityRepository(GalleryPost) export const GalleryPostRepository = db.getRepository(GalleryPost).extend({
export class GalleryPostRepository extends Repository<GalleryPost> { async pack(
public async pack(
src: GalleryPost['id'] | GalleryPost, src: GalleryPost['id'] | GalleryPost,
me?: { id: User['id'] } | null | undefined, me?: { id: User['id'] } | null | undefined,
): Promise<Packed<'GalleryPost'>> { ): Promise<Packed<'GalleryPost'>> {
const meId = me ? me.id : null; const meId = me ? me.id : null;
const post = typeof src === 'object' ? src : await this.findOneOrFail(src); const post = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
return await awaitAll({ return await awaitAll({
id: post.id, id: post.id,
@ -27,14 +26,14 @@ export class GalleryPostRepository extends Repository<GalleryPost> {
tags: post.tags.length > 0 ? post.tags : undefined, tags: post.tags.length > 0 ? post.tags : undefined,
isSensitive: post.isSensitive, isSensitive: post.isSensitive,
likedCount: post.likedCount, likedCount: post.likedCount,
isLiked: meId ? await GalleryLikes.findOne({ postId: post.id, userId: meId }).then(x => x != null) : undefined, isLiked: meId ? await GalleryLikes.findOneBy({ postId: post.id, userId: meId }).then(x => x != null) : undefined,
}); });
} },
public packMany( packMany(
posts: GalleryPost[], posts: GalleryPost[],
me?: { id: User['id'] } | null | undefined, me?: { id: User['id'] } | null | undefined,
) { ) {
return Promise.all(posts.map(x => this.pack(x, me))); return Promise.all(posts.map(x => this.pack(x, me)));
} },
} });

View file

@ -1,10 +1,9 @@
import { EntityRepository, Repository } from 'typeorm'; import { db } from '@/db/postgre.js';
import { Hashtag } from '@/models/entities/hashtag.js'; import { Hashtag } from '@/models/entities/hashtag.js';
import { Packed } from '@/misc/schema.js'; import { Packed } from '@/misc/schema.js';
@EntityRepository(Hashtag) export const HashtagRepository = db.getRepository(Hashtag).extend({
export class HashtagRepository extends Repository<Hashtag> { async pack(
public async pack(
src: Hashtag, src: Hashtag,
): Promise<Packed<'Hashtag'>> { ): Promise<Packed<'Hashtag'>> {
return { return {
@ -16,11 +15,11 @@ export class HashtagRepository extends Repository<Hashtag> {
attachedLocalUsersCount: src.attachedLocalUsersCount, attachedLocalUsersCount: src.attachedLocalUsersCount,
attachedRemoteUsersCount: src.attachedRemoteUsersCount, attachedRemoteUsersCount: src.attachedRemoteUsersCount,
}; };
} },
public packMany( packMany(
hashtags: Hashtag[], hashtags: Hashtag[],
) { ) {
return Promise.all(hashtags.map(x => this.pack(x))); return Promise.all(hashtags.map(x => this.pack(x)));
} },
} });

View file

@ -1,10 +1,9 @@
import { EntityRepository, Repository } from 'typeorm'; import { db } from '@/db/postgre.js';
import { Instance } from '@/models/entities/instance.js'; import { Instance } from '@/models/entities/instance.js';
import { Packed } from '@/misc/schema.js'; import { Packed } from '@/misc/schema.js';
@EntityRepository(Instance) export const InstanceRepository = db.getRepository(Instance).extend({
export class InstanceRepository extends Repository<Instance> { async pack(
public async pack(
instance: Instance, instance: Instance,
): Promise<Packed<'FederationInstance'>> { ): Promise<Packed<'FederationInstance'>> {
return { return {
@ -29,11 +28,11 @@ export class InstanceRepository extends Repository<Instance> {
iconUrl: instance.iconUrl, iconUrl: instance.iconUrl,
infoUpdatedAt: instance.infoUpdatedAt ? instance.infoUpdatedAt.toISOString() : null, infoUpdatedAt: instance.infoUpdatedAt ? instance.infoUpdatedAt.toISOString() : null,
}; };
} },
public packMany( packMany(
instances: Instance[], instances: Instance[],
) { ) {
return Promise.all(instances.map(x => this.pack(x))); return Promise.all(instances.map(x => this.pack(x)));
} },
} });

View file

@ -1,12 +1,11 @@
import { EntityRepository, Repository } from 'typeorm'; import { db } from '@/db/postgre.js';
import { MessagingMessage } from '@/models/entities/messaging-message.js'; import { MessagingMessage } from '@/models/entities/messaging-message.js';
import { Users, DriveFiles, UserGroups } from '../index.js'; import { Users, DriveFiles, UserGroups } from '../index.js';
import { Packed } from '@/misc/schema.js'; import { Packed } from '@/misc/schema.js';
import { User } from '@/models/entities/user.js'; import { User } from '@/models/entities/user.js';
@EntityRepository(MessagingMessage) export const MessagingMessageRepository = db.getRepository(MessagingMessage).extend({
export class MessagingMessageRepository extends Repository<MessagingMessage> { async pack(
public async pack(
src: MessagingMessage['id'] | MessagingMessage, src: MessagingMessage['id'] | MessagingMessage,
me?: { id: User['id'] } | null | undefined, me?: { id: User['id'] } | null | undefined,
options?: { options?: {
@ -19,7 +18,7 @@ export class MessagingMessageRepository extends Repository<MessagingMessage> {
populateGroup: true, populateGroup: true,
}; };
const message = typeof src === 'object' ? src : await this.findOneOrFail(src); const message = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
return { return {
id: message.id, id: message.id,
@ -36,5 +35,5 @@ export class MessagingMessageRepository extends Repository<MessagingMessage> {
isRead: message.isRead, isRead: message.isRead,
reads: message.reads, reads: message.reads,
}; };
} },
} });

View file

@ -1,14 +1,13 @@
import { EntityRepository, Repository } from 'typeorm'; import { db } from '@/db/postgre.js';
import { Users } from '../index.js'; import { Users } from '../index.js';
import { ModerationLog } from '@/models/entities/moderation-log.js'; import { ModerationLog } from '@/models/entities/moderation-log.js';
import { awaitAll } from '@/prelude/await-all.js'; import { awaitAll } from '@/prelude/await-all.js';
@EntityRepository(ModerationLog) export const ModerationLogRepository = db.getRepository(ModerationLog).extend({
export class ModerationLogRepository extends Repository<ModerationLog> { async pack(
public async pack(
src: ModerationLog['id'] | ModerationLog, src: ModerationLog['id'] | ModerationLog,
) { ) {
const log = typeof src === 'object' ? src : await this.findOneOrFail(src); const log = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
return await awaitAll({ return await awaitAll({
id: log.id, id: log.id,
@ -20,11 +19,11 @@ export class ModerationLogRepository extends Repository<ModerationLog> {
detail: true, detail: true,
}), }),
}); });
} },
public packMany( packMany(
reports: any[], reports: any[],
) { ) {
return Promise.all(reports.map(x => this.pack(x))); return Promise.all(reports.map(x => this.pack(x)));
} },
} });

View file

@ -1,32 +1,32 @@
import { EntityRepository, Repository } from 'typeorm'; import { db } from '@/db/postgre.js';
import { Users } from '../index.js'; import { Users } from '../index.js';
import { Muting } from '@/models/entities/muting.js'; import { Muting } from '@/models/entities/muting.js';
import { awaitAll } from '@/prelude/await-all.js'; import { awaitAll } from '@/prelude/await-all.js';
import { Packed } from '@/misc/schema.js'; import { Packed } from '@/misc/schema.js';
import { User } from '@/models/entities/user.js'; import { User } from '@/models/entities/user.js';
@EntityRepository(Muting) export const MutingRepository = db.getRepository(Muting).extend({
export class MutingRepository extends Repository<Muting> { async pack(
public async pack(
src: Muting['id'] | Muting, src: Muting['id'] | Muting,
me?: { id: User['id'] } | null | undefined me?: { id: User['id'] } | null | undefined
): Promise<Packed<'Muting'>> { ): Promise<Packed<'Muting'>> {
const muting = typeof src === 'object' ? src : await this.findOneOrFail(src); const muting = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
return await awaitAll({ return await awaitAll({
id: muting.id, id: muting.id,
createdAt: muting.createdAt.toISOString(), createdAt: muting.createdAt.toISOString(),
expiresAt: muting.expiresAt ? muting.expiresAt.toISOString() : null,
muteeId: muting.muteeId, muteeId: muting.muteeId,
mutee: Users.pack(muting.muteeId, me, { mutee: Users.pack(muting.muteeId, me, {
detail: true, detail: true,
}), }),
}); });
} },
public packMany( packMany(
mutings: any[], mutings: any[],
me: { id: User['id'] } me: { id: User['id'] }
) { ) {
return Promise.all(mutings.map(x => this.pack(x, me))); return Promise.all(mutings.map(x => this.pack(x, me)));
} },
} });

View file

@ -1,15 +1,14 @@
import { EntityRepository, Repository } from 'typeorm'; import { db } from '@/db/postgre.js';
import { NoteFavorite } from '@/models/entities/note-favorite.js'; import { NoteFavorite } from '@/models/entities/note-favorite.js';
import { Notes } from '../index.js'; import { Notes } from '../index.js';
import { User } from '@/models/entities/user.js'; import { User } from '@/models/entities/user.js';
@EntityRepository(NoteFavorite) export const NoteFavoriteRepository = db.getRepository(NoteFavorite).extend({
export class NoteFavoriteRepository extends Repository<NoteFavorite> { async pack(
public async pack(
src: NoteFavorite['id'] | NoteFavorite, src: NoteFavorite['id'] | NoteFavorite,
me?: { id: User['id'] } | null | undefined me?: { id: User['id'] } | null | undefined
) { ) {
const favorite = typeof src === 'object' ? src : await this.findOneOrFail(src); const favorite = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
return { return {
id: favorite.id, id: favorite.id,
@ -17,12 +16,12 @@ export class NoteFavoriteRepository extends Repository<NoteFavorite> {
noteId: favorite.noteId, noteId: favorite.noteId,
note: await Notes.pack(favorite.note || favorite.noteId, me), note: await Notes.pack(favorite.note || favorite.noteId, me),
}; };
} },
public packMany( packMany(
favorites: any[], favorites: any[],
me: { id: User['id'] } me: { id: User['id'] }
) { ) {
return Promise.all(favorites.map(x => this.pack(x, me))); return Promise.all(favorites.map(x => this.pack(x, me)));
} },
} });

View file

@ -1,13 +1,12 @@
import { EntityRepository, Repository } from 'typeorm'; import { db } from '@/db/postgre.js';
import { NoteReaction } from '@/models/entities/note-reaction.js'; import { NoteReaction } from '@/models/entities/note-reaction.js';
import { Notes, Users } from '../index.js'; import { Notes, Users } from '../index.js';
import { Packed } from '@/misc/schema.js'; import { Packed } from '@/misc/schema.js';
import { convertLegacyReaction } from '@/misc/reaction-lib.js'; import { convertLegacyReaction } from '@/misc/reaction-lib.js';
import { User } from '@/models/entities/user.js'; import { User } from '@/models/entities/user.js';
@EntityRepository(NoteReaction) export const NoteReactionRepository = db.getRepository(NoteReaction).extend({
export class NoteReactionRepository extends Repository<NoteReaction> { async pack(
public async pack(
src: NoteReaction['id'] | NoteReaction, src: NoteReaction['id'] | NoteReaction,
me?: { id: User['id'] } | null | undefined, me?: { id: User['id'] } | null | undefined,
options?: { options?: {
@ -18,7 +17,7 @@ export class NoteReactionRepository extends Repository<NoteReaction> {
withNote: false, withNote: false,
}, options); }, options);
const reaction = typeof src === 'object' ? src : await this.findOneOrFail(src); const reaction = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
return { return {
id: reaction.id, id: reaction.id,
@ -29,5 +28,5 @@ export class NoteReactionRepository extends Repository<NoteReaction> {
note: await Notes.pack(reaction.note ?? reaction.noteId, me), note: await Notes.pack(reaction.note ?? reaction.noteId, me),
} : {}), } : {}),
}; };
} },
} });

View file

@ -1,4 +1,4 @@
import { EntityRepository, Repository, In } from 'typeorm'; import { In } from 'typeorm';
import * as mfm from 'mfm-js'; import * as mfm from 'mfm-js';
import { Note } from '@/models/entities/note.js'; import { Note } from '@/models/entities/note.js';
import { User } from '@/models/entities/user.js'; import { User } from '@/models/entities/user.js';
@ -9,10 +9,133 @@ import { awaitAll } from '@/prelude/await-all.js';
import { convertLegacyReaction, convertLegacyReactions, decodeReaction } from '@/misc/reaction-lib.js'; import { convertLegacyReaction, convertLegacyReactions, decodeReaction } from '@/misc/reaction-lib.js';
import { NoteReaction } from '@/models/entities/note-reaction.js'; import { NoteReaction } from '@/models/entities/note-reaction.js';
import { aggregateNoteEmojis, populateEmojis, prefetchEmojis } from '@/misc/populate-emojis.js'; import { aggregateNoteEmojis, populateEmojis, prefetchEmojis } from '@/misc/populate-emojis.js';
import { db } from '@/db/postgre.js';
@EntityRepository(Note) async function hideNote(packedNote: Packed<'Note'>, meId: User['id'] | null) {
export class NoteRepository extends Repository<Note> { // TODO: isVisibleForMe を使うようにしても良さそう(型違うけど)
public async isVisibleForMe(note: Note, meId: User['id'] | null): Promise<boolean> { let hide = false;
// visibility が specified かつ自分が指定されていなかったら非表示
if (packedNote.visibility === 'specified') {
if (meId == null) {
hide = true;
} else if (meId === packedNote.userId) {
hide = false;
} else {
// 指定されているかどうか
const specified = packedNote.visibleUserIds!.some((id: any) => meId === id);
if (specified) {
hide = false;
} else {
hide = true;
}
}
}
// visibility が followers かつ自分が投稿者のフォロワーでなかったら非表示
if (packedNote.visibility === 'followers') {
if (meId == null) {
hide = true;
} else if (meId === packedNote.userId) {
hide = false;
} else if (packedNote.reply && (meId === packedNote.reply.userId)) {
// 自分の投稿に対するリプライ
hide = false;
} else if (packedNote.mentions && packedNote.mentions.some(id => meId === id)) {
// 自分へのメンション
hide = false;
} else {
// フォロワーかどうか
const following = await Followings.findOneBy({
followeeId: packedNote.userId,
followerId: meId,
});
if (following == null) {
hide = true;
} else {
hide = false;
}
}
}
if (hide) {
packedNote.visibleUserIds = undefined;
packedNote.fileIds = [];
packedNote.files = [];
packedNote.text = null;
packedNote.poll = undefined;
packedNote.cw = null;
packedNote.isHidden = true;
}
}
async function populatePoll(note: Note, meId: User['id'] | null) {
const poll = await Polls.findOneByOrFail({ noteId: note.id });
const choices = poll.choices.map(c => ({
text: c,
votes: poll.votes[poll.choices.indexOf(c)],
isVoted: false,
}));
if (meId) {
if (poll.multiple) {
const votes = await PollVotes.findBy({
userId: meId,
noteId: note.id,
});
const myChoices = votes.map(v => v.choice);
for (const myChoice of myChoices) {
choices[myChoice].isVoted = true;
}
} else {
const vote = await PollVotes.findOneBy({
userId: meId,
noteId: note.id,
});
if (vote) {
choices[vote.choice].isVoted = true;
}
}
}
return {
multiple: poll.multiple,
expiresAt: poll.expiresAt,
choices,
};
}
async function populateMyReaction(note: Note, meId: User['id'], _hint_?: {
myReactions: Map<Note['id'], NoteReaction | null>;
}) {
if (_hint_?.myReactions) {
const reaction = _hint_.myReactions.get(note.id);
if (reaction) {
return convertLegacyReaction(reaction.reaction);
} else if (reaction === null) {
return undefined;
}
// 実装上抜けがあるだけかもしれないので、「ヒントに含まれてなかったら(=undefinedなら)return」のようにはしない
}
const reaction = await NoteReactions.findOneBy({
userId: meId,
noteId: note.id,
});
if (reaction) {
return convertLegacyReaction(reaction.reaction);
}
return undefined;
}
export const NoteRepository = db.getRepository(Note).extend({
async isVisibleForMe(note: Note, meId: User['id'] | null): Promise<boolean> {
// visibility が specified かつ自分が指定されていなかったら非表示 // visibility が specified かつ自分が指定されていなかったら非表示
if (note.visibility === 'specified') { if (note.visibility === 'specified') {
if (meId == null) { if (meId == null) {
@ -45,7 +168,7 @@ export class NoteRepository extends Repository<Note> {
return true; return true;
} else { } else {
// フォロワーかどうか // フォロワーかどうか
const following = await Followings.findOne({ const following = await Followings.findOneBy({
followeeId: note.userId, followeeId: note.userId,
followerId: meId, followerId: meId,
}); });
@ -59,69 +182,9 @@ export class NoteRepository extends Repository<Note> {
} }
return true; return true;
} },
private async hideNote(packedNote: Packed<'Note'>, meId: User['id'] | null) { async pack(
// TODO: isVisibleForMe を使うようにしても良さそう(型違うけど)
let hide = false;
// visibility が specified かつ自分が指定されていなかったら非表示
if (packedNote.visibility === 'specified') {
if (meId == null) {
hide = true;
} else if (meId === packedNote.userId) {
hide = false;
} else {
// 指定されているかどうか
const specified = packedNote.visibleUserIds!.some((id: any) => meId === id);
if (specified) {
hide = false;
} else {
hide = true;
}
}
}
// visibility が followers かつ自分が投稿者のフォロワーでなかったら非表示
if (packedNote.visibility === 'followers') {
if (meId == null) {
hide = true;
} else if (meId === packedNote.userId) {
hide = false;
} else if (packedNote.reply && (meId === packedNote.reply.userId)) {
// 自分の投稿に対するリプライ
hide = false;
} else if (packedNote.mentions && packedNote.mentions.some(id => meId === id)) {
// 自分へのメンション
hide = false;
} else {
// フォロワーかどうか
const following = await Followings.findOne({
followeeId: packedNote.userId,
followerId: meId,
});
if (following == null) {
hide = true;
} else {
hide = false;
}
}
}
if (hide) {
packedNote.visibleUserIds = undefined;
packedNote.fileIds = [];
packedNote.files = [];
packedNote.text = null;
packedNote.poll = undefined;
packedNote.cw = null;
packedNote.isHidden = true;
}
}
public async pack(
src: Note['id'] | Note, src: Note['id'] | Note,
me?: { id: User['id'] } | null | undefined, me?: { id: User['id'] } | null | undefined,
options?: { options?: {
@ -138,68 +201,9 @@ export class NoteRepository extends Repository<Note> {
}, options); }, options);
const meId = me ? me.id : null; const meId = me ? me.id : null;
const note = typeof src === 'object' ? src : await this.findOneOrFail(src); const note = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
const host = note.userHost; const host = note.userHost;
async function populatePoll() {
const poll = await Polls.findOneOrFail(note.id);
const choices = poll.choices.map(c => ({
text: c,
votes: poll.votes[poll.choices.indexOf(c)],
isVoted: false,
}));
if (poll.multiple) {
const votes = await PollVotes.find({
userId: meId!,
noteId: note.id,
});
const myChoices = votes.map(v => v.choice);
for (const myChoice of myChoices) {
choices[myChoice].isVoted = true;
}
} else {
const vote = await PollVotes.findOne({
userId: meId!,
noteId: note.id,
});
if (vote) {
choices[vote.choice].isVoted = true;
}
}
return {
multiple: poll.multiple,
expiresAt: poll.expiresAt,
choices,
};
}
async function populateMyReaction() {
if (options?._hint_?.myReactions) {
const reaction = options._hint_.myReactions.get(note.id);
if (reaction) {
return convertLegacyReaction(reaction.reaction);
} else if (reaction === null) {
return undefined;
}
// 実装上抜けがあるだけかもしれないので、「ヒントに含まれてなかったら(=undefinedなら)return」のようにはしない
}
const reaction = await NoteReactions.findOne({
userId: meId!,
noteId: note.id,
});
if (reaction) {
return convertLegacyReaction(reaction.reaction);
}
return undefined;
}
let text = note.text; let text = note.text;
if (note.name && (note.url ?? note.uri)) { if (note.name && (note.url ?? note.uri)) {
@ -209,7 +213,7 @@ export class NoteRepository extends Repository<Note> {
const channel = note.channelId const channel = note.channelId
? note.channel ? note.channel
? note.channel ? note.channel
: await Channels.findOne(note.channelId) : await Channels.findOneBy({ id: note.channelId })
: null; : null;
const reactionEmojiNames = Object.keys(note.reactions).filter(x => x?.startsWith(':')).map(x => decodeReaction(x).reaction).map(x => x.replace(/:/g, '')); const reactionEmojiNames = Object.keys(note.reactions).filter(x => x?.startsWith(':')).map(x => decodeReaction(x).reaction).map(x => x.replace(/:/g, ''));
@ -255,10 +259,10 @@ export class NoteRepository extends Repository<Note> {
_hint_: options?._hint_, _hint_: options?._hint_,
}) : undefined, }) : undefined,
poll: note.hasPoll ? populatePoll() : undefined, poll: note.hasPoll ? populatePoll(note, meId) : undefined,
...(meId ? { ...(meId ? {
myReaction: populateMyReaction(), myReaction: populateMyReaction(note, meId, options?._hint_),
} : {}), } : {}),
} : {}), } : {}),
}); });
@ -275,13 +279,13 @@ export class NoteRepository extends Repository<Note> {
} }
if (!opts.skipHide) { if (!opts.skipHide) {
await this.hideNote(packed, meId); await hideNote(packed, meId);
} }
return packed; return packed;
} },
public async packMany( async packMany(
notes: Note[], notes: Note[],
me?: { id: User['id'] } | null | undefined, me?: { id: User['id'] } | null | undefined,
options?: { options?: {
@ -296,7 +300,7 @@ export class NoteRepository extends Repository<Note> {
if (meId) { if (meId) {
const renoteIds = notes.filter(n => n.renoteId != null).map(n => n.renoteId!); const renoteIds = notes.filter(n => n.renoteId != null).map(n => n.renoteId!);
const targets = [...notes.map(n => n.id), ...renoteIds]; const targets = [...notes.map(n => n.id), ...renoteIds];
const myReactions = await NoteReactions.find({ const myReactions = await NoteReactions.findBy({
userId: meId, userId: meId,
noteId: In(targets), noteId: In(targets),
}); });
@ -314,5 +318,5 @@ export class NoteRepository extends Repository<Note> {
myReactions: myReactionsMap, myReactions: myReactionsMap,
}, },
}))); })));
} },
} });

View file

@ -1,4 +1,4 @@
import { EntityRepository, In, Repository } from 'typeorm'; import { In, Repository } from 'typeorm';
import { Users, Notes, UserGroupInvitations, AccessTokens, NoteReactions } from '../index.js'; import { Users, Notes, UserGroupInvitations, AccessTokens, NoteReactions } from '../index.js';
import { Notification } from '@/models/entities/notification.js'; import { Notification } from '@/models/entities/notification.js';
import { awaitAll } from '@/prelude/await-all.js'; import { awaitAll } from '@/prelude/await-all.js';
@ -8,10 +8,10 @@ import { NoteReaction } from '@/models/entities/note-reaction.js';
import { User } from '@/models/entities/user.js'; import { User } from '@/models/entities/user.js';
import { aggregateNoteEmojis, prefetchEmojis } from '@/misc/populate-emojis.js'; import { aggregateNoteEmojis, prefetchEmojis } from '@/misc/populate-emojis.js';
import { notificationTypes } from '@/types.js'; import { notificationTypes } from '@/types.js';
import { db } from '@/db/postgre.js';
@EntityRepository(Notification) export const NotificationRepository = db.getRepository(Notification).extend({
export class NotificationRepository extends Repository<Notification> { async pack(
public async pack(
src: Notification['id'] | Notification, src: Notification['id'] | Notification,
options: { options: {
_hintForEachNotes_?: { _hintForEachNotes_?: {
@ -19,8 +19,8 @@ export class NotificationRepository extends Repository<Notification> {
}; };
} }
): Promise<Packed<'Notification'>> { ): Promise<Packed<'Notification'>> {
const notification = typeof src === 'object' ? src : await this.findOneOrFail(src); const notification = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
const token = notification.appAccessTokenId ? await AccessTokens.findOneOrFail(notification.appAccessTokenId) : null; const token = notification.appAccessTokenId ? await AccessTokens.findOneByOrFail({ id: notification.appAccessTokenId }) : null;
return await awaitAll({ return await awaitAll({
id: notification.id, id: notification.id,
@ -67,6 +67,12 @@ export class NotificationRepository extends Repository<Notification> {
}), }),
choice: notification.choice, choice: notification.choice,
} : {}), } : {}),
...(notification.type === 'pollEnded' ? {
note: Notes.pack(notification.note || notification.noteId!, { id: notification.notifieeId }, {
detail: true,
_hint_: options._hintForEachNotes_,
}),
} : {}),
...(notification.type === 'groupInvited' ? { ...(notification.type === 'groupInvited' ? {
invitation: UserGroupInvitations.pack(notification.userGroupInvitationId!), invitation: UserGroupInvitations.pack(notification.userGroupInvitationId!),
} : {}), } : {}),
@ -76,9 +82,9 @@ export class NotificationRepository extends Repository<Notification> {
icon: notification.customIcon || token?.iconUrl, icon: notification.customIcon || token?.iconUrl,
} : {}), } : {}),
}); });
} },
public async packMany( async packMany(
notifications: Notification[], notifications: Notification[],
meId: User['id'] meId: User['id']
) { ) {
@ -89,7 +95,7 @@ export class NotificationRepository extends Repository<Notification> {
const myReactionsMap = new Map<Note['id'], NoteReaction | null>(); const myReactionsMap = new Map<Note['id'], NoteReaction | null>();
const renoteIds = notes.filter(n => n.renoteId != null).map(n => n.renoteId!); const renoteIds = notes.filter(n => n.renoteId != null).map(n => n.renoteId!);
const targets = [...noteIds, ...renoteIds]; const targets = [...noteIds, ...renoteIds];
const myReactions = await NoteReactions.find({ const myReactions = await NoteReactions.findBy({
userId: meId, userId: meId,
noteId: In(targets), noteId: In(targets),
}); });
@ -105,5 +111,5 @@ export class NotificationRepository extends Repository<Notification> {
myReactions: myReactionsMap, myReactions: myReactionsMap,
}, },
}))); })));
} },
} });

View file

@ -1,26 +1,25 @@
import { EntityRepository, Repository } from 'typeorm'; import { db } from '@/db/postgre.js';
import { PageLike } from '@/models/entities/page-like.js'; import { PageLike } from '@/models/entities/page-like.js';
import { Pages } from '../index.js'; import { Pages } from '../index.js';
import { User } from '@/models/entities/user.js'; import { User } from '@/models/entities/user.js';
@EntityRepository(PageLike) export const PageLikeRepository = db.getRepository(PageLike).extend({
export class PageLikeRepository extends Repository<PageLike> { async pack(
public async pack(
src: PageLike['id'] | PageLike, src: PageLike['id'] | PageLike,
me?: { id: User['id'] } | null | undefined me?: { id: User['id'] } | null | undefined
) { ) {
const like = typeof src === 'object' ? src : await this.findOneOrFail(src); const like = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
return { return {
id: like.id, id: like.id,
page: await Pages.pack(like.page || like.pageId, me), page: await Pages.pack(like.page || like.pageId, me),
}; };
} },
public packMany( packMany(
likes: any[], likes: any[],
me: { id: User['id'] } me: { id: User['id'] }
) { ) {
return Promise.all(likes.map(x => this.pack(x, me))); return Promise.all(likes.map(x => this.pack(x, me)));
} },
} });

View file

@ -1,4 +1,4 @@
import { EntityRepository, Repository } from 'typeorm'; import { db } from '@/db/postgre.js';
import { Page } from '@/models/entities/page.js'; import { Page } from '@/models/entities/page.js';
import { Packed } from '@/misc/schema.js'; import { Packed } from '@/misc/schema.js';
import { Users, DriveFiles, PageLikes } from '../index.js'; import { Users, DriveFiles, PageLikes } from '../index.js';
@ -6,20 +6,19 @@ import { awaitAll } from '@/prelude/await-all.js';
import { DriveFile } from '@/models/entities/drive-file.js'; import { DriveFile } from '@/models/entities/drive-file.js';
import { User } from '@/models/entities/user.js'; import { User } from '@/models/entities/user.js';
@EntityRepository(Page) export const PageRepository = db.getRepository(Page).extend({
export class PageRepository extends Repository<Page> { async pack(
public async pack(
src: Page['id'] | Page, src: Page['id'] | Page,
me?: { id: User['id'] } | null | undefined, me?: { id: User['id'] } | null | undefined,
): Promise<Packed<'Page'>> { ): Promise<Packed<'Page'>> {
const meId = me ? me.id : null; const meId = me ? me.id : null;
const page = typeof src === 'object' ? src : await this.findOneOrFail(src); const page = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
const attachedFiles: Promise<DriveFile | undefined>[] = []; const attachedFiles: Promise<DriveFile | undefined>[] = [];
const collectFile = (xs: any[]) => { const collectFile = (xs: any[]) => {
for (const x of xs) { for (const x of xs) {
if (x.type === 'image') { if (x.type === 'image') {
attachedFiles.push(DriveFiles.findOne({ attachedFiles.push(DriveFiles.findOneBy({
id: x.fileId, id: x.fileId,
userId: page.userId, userId: page.userId,
})); }));
@ -76,14 +75,14 @@ export class PageRepository extends Repository<Page> {
eyeCatchingImage: page.eyeCatchingImageId ? await DriveFiles.pack(page.eyeCatchingImageId) : null, eyeCatchingImage: page.eyeCatchingImageId ? await DriveFiles.pack(page.eyeCatchingImageId) : null,
attachedFiles: DriveFiles.packMany(await Promise.all(attachedFiles)), attachedFiles: DriveFiles.packMany(await Promise.all(attachedFiles)),
likedCount: page.likedCount, likedCount: page.likedCount,
isLiked: meId ? await PageLikes.findOne({ pageId: page.id, userId: meId }).then(x => x != null) : undefined, isLiked: meId ? await PageLikes.findOneBy({ pageId: page.id, userId: meId }).then(x => x != null) : undefined,
}); });
} },
public packMany( packMany(
pages: Page[], pages: Page[],
me?: { id: User['id'] } | null | undefined, me?: { id: User['id'] } | null | undefined,
) { ) {
return Promise.all(pages.map(x => this.pack(x, me))); return Promise.all(pages.map(x => this.pack(x, me)));
} },
} });

View file

@ -1,6 +1,5 @@
import { EntityRepository, Repository } from 'typeorm'; import { db } from '@/db/postgre.js';
import { Relay } from '@/models/entities/relay.js'; import { Relay } from '@/models/entities/relay.js';
@EntityRepository(Relay) export const RelayRepository = db.getRepository(Relay).extend({
export class RelayRepository extends Repository<Relay> { });
}

View file

@ -1,11 +1,10 @@
import { EntityRepository, Repository } from 'typeorm'; import { db } from '@/db/postgre.js';
import { Signin } from '@/models/entities/signin.js'; import { Signin } from '@/models/entities/signin.js';
@EntityRepository(Signin) export const SigninRepository = db.getRepository(Signin).extend({
export class SigninRepository extends Repository<Signin> { async pack(
public async pack(
src: Signin, src: Signin,
) { ) {
return src; return src;
} },
} });

View file

@ -1,23 +1,22 @@
import { EntityRepository, Repository } from 'typeorm'; import { db } from '@/db/postgre.js';
import { UserGroupInvitation } from '@/models/entities/user-group-invitation.js'; import { UserGroupInvitation } from '@/models/entities/user-group-invitation.js';
import { UserGroups } from '../index.js'; import { UserGroups } from '../index.js';
@EntityRepository(UserGroupInvitation) export const UserGroupInvitationRepository = db.getRepository(UserGroupInvitation).extend({
export class UserGroupInvitationRepository extends Repository<UserGroupInvitation> { async pack(
public async pack(
src: UserGroupInvitation['id'] | UserGroupInvitation, src: UserGroupInvitation['id'] | UserGroupInvitation,
) { ) {
const invitation = typeof src === 'object' ? src : await this.findOneOrFail(src); const invitation = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
return { return {
id: invitation.id, id: invitation.id,
group: await UserGroups.pack(invitation.userGroup || invitation.userGroupId), group: await UserGroups.pack(invitation.userGroup || invitation.userGroupId),
}; };
} },
public packMany( packMany(
invitations: any[], invitations: any[],
) { ) {
return Promise.all(invitations.map(x => this.pack(x))); return Promise.all(invitations.map(x => this.pack(x)));
} },
} });

View file

@ -1,16 +1,15 @@
import { EntityRepository, Repository } from 'typeorm'; import { db } from '@/db/postgre.js';
import { UserGroup } from '@/models/entities/user-group.js'; import { UserGroup } from '@/models/entities/user-group.js';
import { UserGroupJoinings } from '../index.js'; import { UserGroupJoinings } from '../index.js';
import { Packed } from '@/misc/schema.js'; import { Packed } from '@/misc/schema.js';
@EntityRepository(UserGroup) export const UserGroupRepository = db.getRepository(UserGroup).extend({
export class UserGroupRepository extends Repository<UserGroup> { async pack(
public async pack(
src: UserGroup['id'] | UserGroup, src: UserGroup['id'] | UserGroup,
): Promise<Packed<'UserGroup'>> { ): Promise<Packed<'UserGroup'>> {
const userGroup = typeof src === 'object' ? src : await this.findOneOrFail(src); const userGroup = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
const users = await UserGroupJoinings.find({ const users = await UserGroupJoinings.findBy({
userGroupId: userGroup.id, userGroupId: userGroup.id,
}); });
@ -21,5 +20,5 @@ export class UserGroupRepository extends Repository<UserGroup> {
ownerId: userGroup.userId, ownerId: userGroup.userId,
userIds: users.map(x => x.userId), userIds: users.map(x => x.userId),
}; };
} },
} });

View file

@ -1,16 +1,15 @@
import { EntityRepository, Repository } from 'typeorm'; import { db } from '@/db/postgre.js';
import { UserList } from '@/models/entities/user-list.js'; import { UserList } from '@/models/entities/user-list.js';
import { UserListJoinings } from '../index.js'; import { UserListJoinings } from '../index.js';
import { Packed } from '@/misc/schema.js'; import { Packed } from '@/misc/schema.js';
@EntityRepository(UserList) export const UserListRepository = db.getRepository(UserList).extend({
export class UserListRepository extends Repository<UserList> { async pack(
public async pack(
src: UserList['id'] | UserList, src: UserList['id'] | UserList,
): Promise<Packed<'UserList'>> { ): Promise<Packed<'UserList'>> {
const userList = typeof src === 'object' ? src : await this.findOneOrFail(src); const userList = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
const users = await UserListJoinings.find({ const users = await UserListJoinings.findBy({
userListId: userList.id, userListId: userList.id,
}); });
@ -20,5 +19,5 @@ export class UserListRepository extends Repository<UserList> {
name: userList.name, name: userList.name,
userIds: users.map(x => x.userId), userIds: users.map(x => x.userId),
}; };
} },
} });

View file

@ -8,6 +8,11 @@ import { awaitAll, Promiseable } from '@/prelude/await-all.js';
import { populateEmojis } from '@/misc/populate-emojis.js'; import { populateEmojis } from '@/misc/populate-emojis.js';
import { getAntennas } from '@/misc/antenna-cache.js'; import { getAntennas } from '@/misc/antenna-cache.js';
import { USER_ACTIVE_THRESHOLD, USER_ONLINE_THRESHOLD } from '@/const.js'; import { USER_ACTIVE_THRESHOLD, USER_ONLINE_THRESHOLD } from '@/const.js';
import { Cache } from '@/misc/cache.js';
import { Instance } from '../entities/instance.js';
import { db } from '@/db/postgre.js';
const userInstanceCache = new Cache<Instance | null>(1000 * 60 * 60 * 3);
type IsUserDetailed<Detailed extends boolean> = Detailed extends true ? Packed<'UserDetailed'> : Packed<'UserLite'>; type IsUserDetailed<Detailed extends boolean> = Detailed extends true ? Packed<'UserDetailed'> : Packed<'UserLite'>;
type IsMeAndIsUserDetailed<ExpectsMe extends boolean | null, Detailed extends boolean> = type IsMeAndIsUserDetailed<ExpectsMe extends boolean | null, Detailed extends boolean> =
@ -19,51 +24,69 @@ type IsMeAndIsUserDetailed<ExpectsMe extends boolean | null, Detailed extends bo
const ajv = new Ajv(); const ajv = new Ajv();
@EntityRepository(User) const localUsernameSchema = { type: 'string', pattern: /^\w{1,20}$/.toString().slice(1, -1) } as const;
export class UserRepository extends Repository<User> { const passwordSchema = { type: 'string', minLength: 1 } as const;
public localUsernameSchema = { type: 'string', pattern: /^\w{1,20}$/.toString().slice(1, -1) } as const; const nameSchema = { type: 'string', minLength: 1, maxLength: 50 } as const;
public passwordSchema = { type: 'string', minLength: 1 } as const; const descriptionSchema = { type: 'string', minLength: 1, maxLength: 500 } as const;
public nameSchema = { type: 'string', minLength: 1, maxLength: 50 } as const; const locationSchema = { type: 'string', minLength: 1, maxLength: 50 } as const;
public descriptionSchema = { type: 'string', minLength: 1, maxLength: 500 } as const; const birthdaySchema = { type: 'string', pattern: /^([0-9]{4})-([0-9]{2})-([0-9]{2})$/.toString().slice(1, -1) } as const;
public locationSchema = { type: 'string', minLength: 1, maxLength: 50 } as const;
public birthdaySchema = { type: 'string', pattern: /^([0-9]{4})-([0-9]{2})-([0-9]{2})$/.toString().slice(1, -1) } as const; function isLocalUser(user: User): user is ILocalUser;
function isLocalUser<T extends { host: User['host'] }>(user: T): user is T & { host: null; };
function isLocalUser(user: User | { host: User['host'] }): boolean {
return user.host == null;
}
function isRemoteUser(user: User): user is IRemoteUser;
function isRemoteUser<T extends { host: User['host'] }>(user: T): user is T & { host: string; };
function isRemoteUser(user: User | { host: User['host'] }): boolean {
return !isLocalUser(user);
}
export const UserRepository = db.getRepository(User).extend({
localUsernameSchema,
passwordSchema,
nameSchema,
descriptionSchema,
locationSchema,
birthdaySchema,
//#region Validators //#region Validators
public validateLocalUsername = ajv.compile(this.localUsernameSchema); validateLocalUsername: ajv.compile(localUsernameSchema),
public validatePassword = ajv.compile(this.passwordSchema); validatePassword: ajv.compile(passwordSchema),
public validateName = ajv.compile(this.nameSchema); validateName: ajv.compile(nameSchema),
public validateDescription = ajv.compile(this.descriptionSchema); validateDescription: ajv.compile(descriptionSchema),
public validateLocation = ajv.compile(this.locationSchema); validateLocation: ajv.compile(locationSchema),
public validateBirthday = ajv.compile(this.birthdaySchema); validateBirthday: ajv.compile(birthdaySchema),
//#endregion //#endregion
public async getRelation(me: User['id'], target: User['id']) { async getRelation(me: User['id'], target: User['id']) {
const [following1, following2, followReq1, followReq2, toBlocking, fromBlocked, mute] = await Promise.all([ const [following1, following2, followReq1, followReq2, toBlocking, fromBlocked, mute] = await Promise.all([
Followings.findOne({ Followings.findOneBy({
followerId: me, followerId: me,
followeeId: target, followeeId: target,
}), }),
Followings.findOne({ Followings.findOneBy({
followerId: target, followerId: target,
followeeId: me, followeeId: me,
}), }),
FollowRequests.findOne({ FollowRequests.findOneBy({
followerId: me, followerId: me,
followeeId: target, followeeId: target,
}), }),
FollowRequests.findOne({ FollowRequests.findOneBy({
followerId: target, followerId: target,
followeeId: me, followeeId: me,
}), }),
Blockings.findOne({ Blockings.findOneBy({
blockerId: me, blockerId: me,
blockeeId: target, blockeeId: target,
}), }),
Blockings.findOne({ Blockings.findOneBy({
blockerId: target, blockerId: target,
blockeeId: me, blockeeId: me,
}), }),
Mutings.findOne({ Mutings.findOneBy({
muterId: me, muterId: me,
muteeId: target, muteeId: target,
}), }),
@ -79,14 +102,14 @@ export class UserRepository extends Repository<User> {
isBlocked: fromBlocked != null, isBlocked: fromBlocked != null,
isMuted: mute != null, isMuted: mute != null,
}; };
} },
public async getHasUnreadMessagingMessage(userId: User['id']): Promise<boolean> { async getHasUnreadMessagingMessage(userId: User['id']): Promise<boolean> {
const mute = await Mutings.find({ const mute = await Mutings.findBy({
muterId: userId, muterId: userId,
}); });
const joinings = await UserGroupJoinings.find({ userId: userId }); const joinings = await UserGroupJoinings.findBy({ userId: userId });
const groupQs = Promise.all(joinings.map(j => MessagingMessages.createQueryBuilder('message') const groupQs = Promise.all(joinings.map(j => MessagingMessages.createQueryBuilder('message')
.where(`message.groupId = :groupId`, { groupId: j.userGroupId }) .where(`message.groupId = :groupId`, { groupId: j.userGroupId })
@ -108,44 +131,44 @@ export class UserRepository extends Repository<User> {
]); ]);
return withUser || withGroups.some(x => x); return withUser || withGroups.some(x => x);
} },
public async getHasUnreadAnnouncement(userId: User['id']): Promise<boolean> { async getHasUnreadAnnouncement(userId: User['id']): Promise<boolean> {
const reads = await AnnouncementReads.find({ const reads = await AnnouncementReads.findBy({
userId: userId, userId: userId,
}); });
const count = await Announcements.count(reads.length > 0 ? { const count = await Announcements.countBy(reads.length > 0 ? {
id: Not(In(reads.map(read => read.announcementId))), id: Not(In(reads.map(read => read.announcementId))),
} : {}); } : {});
return count > 0; return count > 0;
} },
public async getHasUnreadAntenna(userId: User['id']): Promise<boolean> { async getHasUnreadAntenna(userId: User['id']): Promise<boolean> {
const myAntennas = (await getAntennas()).filter(a => a.userId === userId); const myAntennas = (await getAntennas()).filter(a => a.userId === userId);
const unread = myAntennas.length > 0 ? await AntennaNotes.findOne({ const unread = myAntennas.length > 0 ? await AntennaNotes.findOneBy({
antennaId: In(myAntennas.map(x => x.id)), antennaId: In(myAntennas.map(x => x.id)),
read: false, read: false,
}) : null; }) : null;
return unread != null; return unread != null;
} },
public async getHasUnreadChannel(userId: User['id']): Promise<boolean> { async getHasUnreadChannel(userId: User['id']): Promise<boolean> {
const channels = await ChannelFollowings.find({ followerId: userId }); const channels = await ChannelFollowings.findBy({ followerId: userId });
const unread = channels.length > 0 ? await NoteUnreads.findOne({ const unread = channels.length > 0 ? await NoteUnreads.findOneBy({
userId: userId, userId: userId,
noteChannelId: In(channels.map(x => x.followeeId)), noteChannelId: In(channels.map(x => x.followeeId)),
}) : null; }) : null;
return unread != null; return unread != null;
} },
public async getHasUnreadNotification(userId: User['id']): Promise<boolean> { async getHasUnreadNotification(userId: User['id']): Promise<boolean> {
const mute = await Mutings.find({ const mute = await Mutings.findBy({
muterId: userId, muterId: userId,
}); });
const mutedUserIds = mute.map(m => m.muteeId); const mutedUserIds = mute.map(m => m.muteeId);
@ -160,17 +183,17 @@ export class UserRepository extends Repository<User> {
}); });
return count > 0; return count > 0;
} },
public async getHasPendingReceivedFollowRequest(userId: User['id']): Promise<boolean> { async getHasPendingReceivedFollowRequest(userId: User['id']): Promise<boolean> {
const count = await FollowRequests.count({ const count = await FollowRequests.countBy({
followeeId: userId, followeeId: userId,
}); });
return count > 0; return count > 0;
} },
public getOnlineStatus(user: User): 'unknown' | 'online' | 'active' | 'offline' { getOnlineStatus(user: User): 'unknown' | 'online' | 'active' | 'offline' {
if (user.hideOnlineStatus) return 'unknown'; if (user.hideOnlineStatus) return 'unknown';
if (user.lastActiveDate == null) return 'unknown'; if (user.lastActiveDate == null) return 'unknown';
const elapsed = Date.now() - user.lastActiveDate.getTime(); const elapsed = Date.now() - user.lastActiveDate.getTime();
@ -179,22 +202,22 @@ export class UserRepository extends Repository<User> {
elapsed < USER_ACTIVE_THRESHOLD ? 'active' : elapsed < USER_ACTIVE_THRESHOLD ? 'active' :
'offline' 'offline'
); );
} },
public getAvatarUrl(user: User): string { getAvatarUrl(user: User): string {
// TODO: avatarIdがあるがavatarがない(JOINされてない)場合のハンドリング // TODO: avatarIdがあるがavatarがない(JOINされてない)場合のハンドリング
if (user.avatar) { if (user.avatar) {
return DriveFiles.getPublicUrl(user.avatar, true) || this.getIdenticonUrl(user.id); return DriveFiles.getPublicUrl(user.avatar, true) || this.getIdenticonUrl(user.id);
} else { } else {
return this.getIdenticonUrl(user.id); return this.getIdenticonUrl(user.id);
} }
} },
public getIdenticonUrl(userId: User['id']): string { getIdenticonUrl(userId: User['id']): string {
return `${config.url}/identicon/${userId}`; return `${config.url}/identicon/${userId}`;
} },
public async pack<ExpectsMe extends boolean | null = null, D extends boolean = false>( async pack<ExpectsMe extends boolean | null = null, D extends boolean = false>(
src: User['id'] | User, src: User['id'] | User,
me?: { id: User['id'] } | null | undefined, me?: { id: User['id'] } | null | undefined,
options?: { options?: {
@ -211,11 +234,15 @@ export class UserRepository extends Repository<User> {
if (typeof src === 'object') { if (typeof src === 'object') {
user = src; user = src;
if (src.avatar === undefined && src.avatarId) src.avatar = await DriveFiles.findOne(src.avatarId) ?? null; if (src.avatar === undefined && src.avatarId) src.avatar = await DriveFiles.findOneBy({ id: src.avatarId }) ?? null;
if (src.banner === undefined && src.bannerId) src.banner = await DriveFiles.findOne(src.bannerId) ?? null; if (src.banner === undefined && src.bannerId) src.banner = await DriveFiles.findOneBy({ id: src.bannerId }) ?? null;
} else { } else {
user = await this.findOneOrFail(src, { user = await this.findOneOrFail({
relations: ['avatar', 'banner'], where: { id: src },
relations: {
avatar: true,
banner: true,
},
}); });
} }
@ -228,7 +255,7 @@ export class UserRepository extends Repository<User> {
.innerJoinAndSelect('pin.note', 'note') .innerJoinAndSelect('pin.note', 'note')
.orderBy('pin.id', 'DESC') .orderBy('pin.id', 'DESC')
.getMany() : []; .getMany() : [];
const profile = opts.detail ? await UserProfiles.findOneOrFail(user.id) : null; const profile = opts.detail ? await UserProfiles.findOneByOrFail({ userId: user.id }) : null;
const followingCount = profile == null ? null : const followingCount = profile == null ? null :
(profile.ffVisibility === 'public') || isMe ? user.followingCount : (profile.ffVisibility === 'public') || isMe ? user.followingCount :
@ -254,8 +281,10 @@ export class UserRepository extends Repository<User> {
isModerator: user.isModerator || falsy, isModerator: user.isModerator || falsy,
isBot: user.isBot || falsy, isBot: user.isBot || falsy,
isCat: user.isCat || falsy, isCat: user.isCat || falsy,
showTimelineReplies: user.showTimelineReplies || falsy, instance: user.host ? userInstanceCache.fetch(user.host,
instance: user.host ? Instances.findOne({ host: user.host }).then(instance => instance ? { () => Instances.findOneBy({ host: user.host! }),
v => v != null
).then(instance => instance ? {
name: instance.name, name: instance.name,
softwareName: instance.softwareName, softwareName: instance.softwareName,
softwareVersion: instance.softwareVersion, softwareVersion: instance.softwareVersion,
@ -297,7 +326,7 @@ export class UserRepository extends Repository<User> {
twoFactorEnabled: profile!.twoFactorEnabled, twoFactorEnabled: profile!.twoFactorEnabled,
usePasswordLessLogin: profile!.usePasswordLessLogin, usePasswordLessLogin: profile!.usePasswordLessLogin,
securityKeys: profile!.twoFactorEnabled securityKeys: profile!.twoFactorEnabled
? UserSecurityKeys.count({ ? UserSecurityKeys.countBy({
userId: user.id, userId: user.id,
}).then(result => result >= 1) }).then(result => result >= 1)
: false, : false,
@ -334,6 +363,7 @@ export class UserRepository extends Repository<User> {
mutedInstances: profile!.mutedInstances, mutedInstances: profile!.mutedInstances,
mutingNotificationTypes: profile!.mutingNotificationTypes, mutingNotificationTypes: profile!.mutingNotificationTypes,
emailNotificationTypes: profile!.emailNotificationTypes, emailNotificationTypes: profile!.emailNotificationTypes,
showTimelineReplies: user.showTimelineReplies || falsy,
} : {}), } : {}),
...(opts.includeSecrets ? { ...(opts.includeSecrets ? {
@ -344,7 +374,11 @@ export class UserRepository extends Repository<User> {
where: { where: {
userId: user.id, userId: user.id,
}, },
select: ['id', 'name', 'lastUsed'], select: {
id: true,
name: true,
lastUsed: true,
},
}) })
: [], : [],
} : {}), } : {}),
@ -361,9 +395,9 @@ export class UserRepository extends Repository<User> {
} as Promiseable<Packed<'User'>> as Promiseable<IsMeAndIsUserDetailed<ExpectsMe, D>>; } as Promiseable<Packed<'User'>> as Promiseable<IsMeAndIsUserDetailed<ExpectsMe, D>>;
return await awaitAll(packed); return await awaitAll(packed);
} },
public packMany<D extends boolean = false>( packMany<D extends boolean = false>(
users: (User['id'] | User)[], users: (User['id'] | User)[],
me?: { id: User['id'] } | null | undefined, me?: { id: User['id'] } | null | undefined,
options?: { options?: {
@ -372,17 +406,8 @@ export class UserRepository extends Repository<User> {
} }
): Promise<IsUserDetailed<D>[]> { ): Promise<IsUserDetailed<D>[]> {
return Promise.all(users.map(u => this.pack(u, me, options))); return Promise.all(users.map(u => this.pack(u, me, options)));
} },
public isLocalUser(user: User): user is ILocalUser; isLocalUser,
public isLocalUser<T extends { host: User['host'] }>(user: T): user is T & { host: null; }; isRemoteUser,
public isLocalUser(user: User | { host: User['host'] }): boolean { });
return user.host == null;
}
public isRemoteUser(user: User): user is IRemoteUser;
public isRemoteUser<T extends { host: User['host'] }>(user: T): user is T & { host: string; };
public isRemoteUser(user: User | { host: User['host'] }): boolean {
return !this.isLocalUser(user);
}
}

View file

@ -12,6 +12,11 @@ export const packedMutingSchema = {
optional: false, nullable: false, optional: false, nullable: false,
format: 'date-time', format: 'date-time',
}, },
expiresAt: {
type: 'string',
optional: false, nullable: true,
format: 'date-time',
},
muteeId: { muteeId: {
type: 'string', type: 'string',
optional: false, nullable: false, optional: false, nullable: false,

View file

@ -8,10 +8,11 @@ import processInbox from './processors/inbox.js';
import processDb from './processors/db/index.js'; import processDb from './processors/db/index.js';
import processObjectStorage from './processors/object-storage/index.js'; import processObjectStorage from './processors/object-storage/index.js';
import processSystemQueue from './processors/system/index.js'; import processSystemQueue from './processors/system/index.js';
import { endedPollNotification } from './processors/ended-poll-notification.js';
import { queueLogger } from './logger.js'; import { queueLogger } from './logger.js';
import { DriveFile } from '@/models/entities/drive-file.js'; import { DriveFile } from '@/models/entities/drive-file.js';
import { getJobInfo } from './get-job-info.js'; import { getJobInfo } from './get-job-info.js';
import { systemQueue, dbQueue, deliverQueue, inboxQueue, objectStorageQueue } from './queues.js'; import { systemQueue, dbQueue, deliverQueue, inboxQueue, objectStorageQueue, endedPollNotificationQueue } from './queues.js';
import { ThinUser } from './types.js'; import { ThinUser } from './types.js';
import { IActivity } from '@/remote/activitypub/type.js'; import { IActivity } from '@/remote/activitypub/type.js';
@ -255,12 +256,14 @@ export default function() {
deliverQueue.process(config.deliverJobConcurrency || 128, processDeliver); deliverQueue.process(config.deliverJobConcurrency || 128, processDeliver);
inboxQueue.process(config.inboxJobConcurrency || 16, processInbox); inboxQueue.process(config.inboxJobConcurrency || 16, processInbox);
endedPollNotificationQueue.process(endedPollNotification);
processDb(dbQueue); processDb(dbQueue);
processObjectStorage(objectStorageQueue); processObjectStorage(objectStorageQueue);
systemQueue.add('tickCharts', { systemQueue.add('tickCharts', {
}, { }, {
repeat: { cron: '55 * * * *' }, repeat: { cron: '55 * * * *' },
removeOnComplete: true,
}); });
systemQueue.add('resyncCharts', { systemQueue.add('resyncCharts', {
@ -273,6 +276,12 @@ export default function() {
repeat: { cron: '0 0 * * *' }, repeat: { cron: '0 0 * * *' },
}); });
systemQueue.add('checkExpiredMutings', {
}, {
repeat: { cron: '*/5 * * * *' },
removeOnComplete: true,
});
processSystemQueue(systemQueue); processSystemQueue(systemQueue);
} }

View file

@ -13,7 +13,7 @@ const logger = queueLogger.createSubLogger('delete-account');
export async function deleteAccount(job: Bull.Job<DbUserDeleteJobData>): Promise<string | void> { export async function deleteAccount(job: Bull.Job<DbUserDeleteJobData>): Promise<string | void> {
logger.info(`Deleting account of ${job.data.user.id} ...`); logger.info(`Deleting account of ${job.data.user.id} ...`);
const user = await Users.findOne(job.data.user.id); const user = await Users.findOneBy({ id: job.data.user.id });
if (user == null) { if (user == null) {
return; return;
} }
@ -75,7 +75,7 @@ export async function deleteAccount(job: Bull.Job<DbUserDeleteJobData>): Promise
} }
{ // Send email notification { // Send email notification
const profile = await UserProfiles.findOneOrFail(user.id); const profile = await UserProfiles.findOneByOrFail({ userId: user.id });
if (profile.email && profile.emailVerified) { if (profile.email && profile.emailVerified) {
sendEmail(profile.email, 'Account deleted', sendEmail(profile.email, 'Account deleted',
`Your account has been deleted.`, `Your account has been deleted.`,

View file

@ -11,7 +11,7 @@ const logger = queueLogger.createSubLogger('delete-drive-files');
export async function deleteDriveFiles(job: Bull.Job<DbUserJobData>, done: any): Promise<void> { export async function deleteDriveFiles(job: Bull.Job<DbUserJobData>, done: any): Promise<void> {
logger.info(`Deleting drive files of ${job.data.user.id} ...`); logger.info(`Deleting drive files of ${job.data.user.id} ...`);
const user = await Users.findOne(job.data.user.id); const user = await Users.findOneBy({ id: job.data.user.id });
if (user == null) { if (user == null) {
done(); done();
return; return;
@ -44,7 +44,7 @@ export async function deleteDriveFiles(job: Bull.Job<DbUserJobData>, done: any):
deletedCount++; deletedCount++;
} }
const total = await DriveFiles.count({ const total = await DriveFiles.countBy({
userId: user.id, userId: user.id,
}); });

View file

@ -15,7 +15,7 @@ const logger = queueLogger.createSubLogger('export-blocking');
export async function exportBlocking(job: Bull.Job<DbUserJobData>, done: any): Promise<void> { export async function exportBlocking(job: Bull.Job<DbUserJobData>, done: any): Promise<void> {
logger.info(`Exporting blocking of ${job.data.user.id} ...`); logger.info(`Exporting blocking of ${job.data.user.id} ...`);
const user = await Users.findOne(job.data.user.id); const user = await Users.findOneBy({ id: job.data.user.id });
if (user == null) { if (user == null) {
done(); done();
return; return;
@ -56,7 +56,7 @@ export async function exportBlocking(job: Bull.Job<DbUserJobData>, done: any): P
cursor = blockings[blockings.length - 1].id; cursor = blockings[blockings.length - 1].id;
for (const block of blockings) { for (const block of blockings) {
const u = await Users.findOne({ id: block.blockeeId }); const u = await Users.findOneBy({ id: block.blockeeId });
if (u == null) { if (u == null) {
exportedCount++; continue; exportedCount++; continue;
} }
@ -75,7 +75,7 @@ export async function exportBlocking(job: Bull.Job<DbUserJobData>, done: any): P
exportedCount++; exportedCount++;
} }
const total = await Blockings.count({ const total = await Blockings.countBy({
blockerId: user.id, blockerId: user.id,
}); });

View file

@ -12,13 +12,14 @@ import { Users, Emojis } from '@/models/index.js';
import { } from '@/queue/types.js'; import { } from '@/queue/types.js';
import { downloadUrl } from '@/misc/download-url.js'; import { downloadUrl } from '@/misc/download-url.js';
import config from '@/config/index.js'; import config from '@/config/index.js';
import { IsNull } from 'typeorm';
const logger = queueLogger.createSubLogger('export-custom-emojis'); const logger = queueLogger.createSubLogger('export-custom-emojis');
export async function exportCustomEmojis(job: Bull.Job, done: () => void): Promise<void> { export async function exportCustomEmojis(job: Bull.Job, done: () => void): Promise<void> {
logger.info(`Exporting custom emojis ...`); logger.info(`Exporting custom emojis ...`);
const user = await Users.findOne(job.data.user.id); const user = await Users.findOneBy({ id: job.data.user.id });
if (user == null) { if (user == null) {
done(); done();
return; return;
@ -57,7 +58,7 @@ export async function exportCustomEmojis(job: Bull.Job, done: () => void): Promi
const customEmojis = await Emojis.find({ const customEmojis = await Emojis.find({
where: { where: {
host: null, host: IsNull(),
}, },
order: { order: {
id: 'ASC', id: 'ASC',

View file

@ -16,7 +16,7 @@ const logger = queueLogger.createSubLogger('export-following');
export async function exportFollowing(job: Bull.Job<DbUserJobData>, done: () => void): Promise<void> { export async function exportFollowing(job: Bull.Job<DbUserJobData>, done: () => void): Promise<void> {
logger.info(`Exporting following of ${job.data.user.id} ...`); logger.info(`Exporting following of ${job.data.user.id} ...`);
const user = await Users.findOne(job.data.user.id); const user = await Users.findOneBy({ id: job.data.user.id });
if (user == null) { if (user == null) {
done(); done();
return; return;
@ -36,7 +36,7 @@ export async function exportFollowing(job: Bull.Job<DbUserJobData>, done: () =>
let cursor: Following['id'] | null = null; let cursor: Following['id'] | null = null;
const mutings = job.data.excludeMuting ? await Mutings.find({ const mutings = job.data.excludeMuting ? await Mutings.findBy({
muterId: user.id, muterId: user.id,
}) : []; }) : [];
@ -60,7 +60,7 @@ export async function exportFollowing(job: Bull.Job<DbUserJobData>, done: () =>
cursor = followings[followings.length - 1].id; cursor = followings[followings.length - 1].id;
for (const following of followings) { for (const following of followings) {
const u = await Users.findOne({ id: following.followeeId }); const u = await Users.findOneBy({ id: following.followeeId });
if (u == null) { if (u == null) {
continue; continue;
} }

View file

@ -7,7 +7,7 @@ import { addFile } from '@/services/drive/add-file.js';
import { format as dateFormat } from 'date-fns'; import { format as dateFormat } from 'date-fns';
import { getFullApAccount } from '@/misc/convert-host.js'; import { getFullApAccount } from '@/misc/convert-host.js';
import { Users, Mutings } from '@/models/index.js'; import { Users, Mutings } from '@/models/index.js';
import { MoreThan } from 'typeorm'; import { IsNull, MoreThan } from 'typeorm';
import { DbUserJobData } from '@/queue/types.js'; import { DbUserJobData } from '@/queue/types.js';
const logger = queueLogger.createSubLogger('export-mute'); const logger = queueLogger.createSubLogger('export-mute');
@ -15,7 +15,7 @@ const logger = queueLogger.createSubLogger('export-mute');
export async function exportMute(job: Bull.Job<DbUserJobData>, done: any): Promise<void> { export async function exportMute(job: Bull.Job<DbUserJobData>, done: any): Promise<void> {
logger.info(`Exporting mute of ${job.data.user.id} ...`); logger.info(`Exporting mute of ${job.data.user.id} ...`);
const user = await Users.findOne(job.data.user.id); const user = await Users.findOneBy({ id: job.data.user.id });
if (user == null) { if (user == null) {
done(); done();
return; return;
@ -40,6 +40,7 @@ export async function exportMute(job: Bull.Job<DbUserJobData>, done: any): Promi
const mutes = await Mutings.find({ const mutes = await Mutings.find({
where: { where: {
muterId: user.id, muterId: user.id,
expiresAt: IsNull(),
...(cursor ? { id: MoreThan(cursor) } : {}), ...(cursor ? { id: MoreThan(cursor) } : {}),
}, },
take: 100, take: 100,
@ -56,7 +57,7 @@ export async function exportMute(job: Bull.Job<DbUserJobData>, done: any): Promi
cursor = mutes[mutes.length - 1].id; cursor = mutes[mutes.length - 1].id;
for (const mute of mutes) { for (const mute of mutes) {
const u = await Users.findOne({ id: mute.muteeId }); const u = await Users.findOneBy({ id: mute.muteeId });
if (u == null) { if (u == null) {
exportedCount++; continue; exportedCount++; continue;
} }
@ -75,7 +76,7 @@ export async function exportMute(job: Bull.Job<DbUserJobData>, done: any): Promi
exportedCount++; exportedCount++;
} }
const total = await Mutings.count({ const total = await Mutings.countBy({
muterId: user.id, muterId: user.id,
}); });

Some files were not shown because too many files have changed in this diff Show more