Merge branch 'develop' into ed25519

This commit is contained in:
tamaina 2024-02-29 20:34:55 +00:00
commit a4e7d6940b
56 changed files with 1952 additions and 1221 deletions

View File

@ -14,10 +14,15 @@
## 202x.x.x (unreleased)
### General
- 通知がミュート、凍結を考慮するようになりました
- Enhance: 投稿者のロールに応じて、一つのノートに含むことのできるメンションとダイレクト投稿の宛先の人数に上限を設定できるように
* デフォルトのメンション上限は20アカウントに設定されます。管理者はベースロールの設定で変更可能です。)
* 連合の問い合わせに応答しないサーバーのリモートユーザーへのメンションは、上限の人数に含めない実装になっています。
- Enhance: 通知がミュート、凍結を考慮するようになりました
- Enhance: サーバーごとにモデレーションノートを残せるように
- Enhance: コンディショナルロールの条件に「マニュアルロールへのアサイン」を追加
- Enhance: 通知の受信設定に「フォロー中またはフォロワー」を追加
- Enhance: 通知の履歴をリセットできるように
- Fix: ダイレクトなノートに対してはダイレクトでしか返信できないように
### Client
- Enhance: ノート作成画面のファイル添付メニューの区切り線の位置を調整
@ -30,14 +35,14 @@
- Fix: 絵文字サジェストの順位で、絵文字自体の名前が同じものよりもタグで一致しているものが優先されてしまう問題を修正
### Server
- Enhance: エンドポイント`flash/update`の`flashId`以外のパラメータは必須ではなくなりました
- Fix: nodeinfoにenableMcaptchaとenableTurnstileが無いのを修正
- Fix: 破損した通知をクライアントに送信しないように
* 通知欄が無限にリロードされる問題が改善する可能性があります
- エンドポイント`flash/update`の`flashId`以外のパラメータは必須ではなくなりました
- Fix: 禁止キーワードを含むートがDelayed Queueに追加されて再処理される問題を修正
- Fix: 自分がフォローしていないアカウントのフォロワー限定ノートが閲覧できることがある問題を修正
- Fix: タイムラインのオプションで「リノートを表示」を無効にしている際、投票のみの引用リノートが流れてこない問題を修正
- エンドポイント`admin/emoji/update`の各種修正
- Fix: エンドポイント`admin/emoji/update`の各種修正
- 必須パラメータを`id`または`name`のいずれかのみに
- `id`の代わりに`name`で絵文字を指定可能に(`id`・`name`両指定時は従来通り`name`を変更する挙動)
- `category`および`licence`が指定なしの時勝手にnullに上書きされる挙動を修正

View File

@ -1014,6 +1014,7 @@ renotes: "أعد النشر"
sourceCode: "الشفرة المصدرية"
flip: "اقلب"
lastNDays: "آخر {n} أيام"
surrender: "ألغِ"
_initialAccountSetting:
accountCreated: "نجح إنشاء حسابك!"
letsStartAccountSetup: "إذا كنت جديدًا لنعدّ حسابك الشخصي."

View File

@ -1210,6 +1210,7 @@ hemisphere: "Geolocalització"
withSensitive: "Incloure notes amb fitxers sensibles"
userSaysSomethingSensitive: "La publicació de {name} conte material sensible"
enableHorizontalSwipe: "Lliscar per canviar de pestanya"
surrender: "Cancel·lar "
_bubbleGame:
howToPlay: "Com es juga"
_howToPlay:

View File

@ -1098,6 +1098,7 @@ renotes: "Přeposlat"
sourceCode: "Zdrojový kód"
flip: "Otočit"
lastNDays: "Posledních {n} dnů"
surrender: "Zrušit"
_initialAccountSetting:
accountCreated: "Váš účet byl úspěšně vytvořen!"
letsStartAccountSetup: "Pro začátek si nastavte svůj profil."

View File

@ -1184,6 +1184,7 @@ decorate: "Dekorieren"
addMfmFunction: "MFM hinzufügen"
sfx: "Soundeffekte"
lastNDays: "Letzten {n} Tage"
surrender: "Abbrechen"
_announcement:
forExistingUsers: "Nur für existierende Nutzer"
forExistingUsersDescription: "Ist diese Option aktiviert, wird diese Ankündigung nur Nutzern angezeigt, die zum Zeitpunkt der Ankündigung bereits registriert sind. Ist sie deaktiviert, wird sie auch Nutzern, die sich nach dessen Veröffentlichung registrieren, angezeigt."

View File

@ -991,6 +991,7 @@ neverShow: "Don't show again"
remindMeLater: "Maybe later"
didYouLikeMisskey: "Have you taken a liking to Misskey?"
pleaseDonate: "{host} uses the free software, Misskey. We would highly appreciate your donations so development of Misskey can continue!"
correspondingSourceIsAvailable: "The corresponding source code is available at {anchor}"
roles: "Roles"
role: "Role"
noRole: "Role not found"
@ -1041,6 +1042,8 @@ resetPasswordConfirm: "Really reset your password?"
sensitiveWords: "Sensitive words"
sensitiveWordsDescription: "The visibility of all notes containing any of the configured words will be set to \"Home\" automatically. You can list multiple by separating them via line breaks."
sensitiveWordsDescription2: "Using spaces will create AND expressions and surrounding keywords with slashes will turn them into a regular expression."
prohibitedWords: "Prohibited words"
prohibitedWordsDescription: "Enables an error when attempting to post a note containing the set word(s). Multiple words can be set, separated by a new line."
prohibitedWordsDescription2: "Using spaces will create AND expressions and surrounding keywords with slashes will turn them into a regular expression."
hiddenTags: "Hidden hashtags"
hiddenTagsDescription: "Select tags which will not shown on trend list.\nMultiple tags could be registered by lines."
@ -1166,6 +1169,12 @@ confirmShowRepliesAll: "This operation is irreversible. Would you really like to
confirmHideRepliesAll: "This operation is irreversible. Would you really like to hide replies to others from everyone you follow in your timeline?"
externalServices: "External Services"
sourceCode: "Source code"
sourceCodeIsNotYetProvided: "Source code is not yet available. Contact the administrator to fix this problem."
repositoryUrl: "Repository URL"
repositoryUrlDescription: "If you are using Misskey as is (without any changes to the source code), enter https://github.com/misskey-dev/misskey"
repositoryUrlOrTarballRequired: "If you have not published a repository, you must provide a tarball instead. See .config/example.yml for more information."
feedback: "Feedback"
feedbackUrl: "Feedback URL"
impressum: "Impressum"
impressumUrl: "Impressum URL"
impressumDescription: "In some countries, like germany, the inclusion of operator contact information (an Impressum) is legally required for commercial websites."
@ -1201,6 +1210,8 @@ soundWillBePlayed: "Sound will be played"
showReplay: "View Replay"
replay: "Replay"
replaying: "Showing replay"
endReplay: "Exit Replay"
copyReplayData: "Copy replay data"
ranking: "Ranking"
lastNDays: "Last {n} days"
backToTitle: "Go back to title"
@ -1208,8 +1219,20 @@ hemisphere: "Where are you located"
withSensitive: "Include notes with sensitive files"
userSaysSomethingSensitive: "Post by {name} contains sensitive content"
enableHorizontalSwipe: "Swipe to switch tabs"
loading: "Loading"
surrender: "Cancel"
gameRetry: "Retry"
_bubbleGame:
howToPlay: "How to play"
hold: "Hold"
_score:
score: "Score"
scoreYen: "Amount of money earned"
highScore: "High score"
maxChain: "Maximum number of chains"
yen: "{yen} Yen"
estimatedQty: "{qty} Pieces"
scoreSweets: "{onigiriQtyWithUnit} Onigiri"
_howToPlay:
section1: "Adjust the position and drop the object into the box."
section2: "When two objects of the same type touch each other, they will change into a different object and you score points."
@ -1754,6 +1777,8 @@ _aboutMisskey:
contributors: "Main contributors"
allContributors: "All contributors"
source: "Source code"
original: "Original"
thisIsModifiedVersion: "{name} uses a modified version of the original Misskey."
translation: "Translate Misskey"
donate: "Donate to Misskey"
morePatrons: "We also appreciate the support of many other helpers not listed here. Thank you! 🥰"
@ -2369,6 +2394,7 @@ _moderationLogTypes:
resetPassword: "Password reset"
suspendRemoteInstance: "Remote instance suspended"
unsuspendRemoteInstance: "Remote instance unsuspended"
updateRemoteInstanceNote: "Moderation note updated for remote instance."
markSensitiveDriveFile: "File marked as sensitive"
unmarkSensitiveDriveFile: "File unmarked as sensitive"
resolveAbuseReport: "Report resolved"
@ -2489,6 +2515,8 @@ _reversi:
opponentHasSettingsChanged: "The opponent has changed their settings."
allowIrregularRules: "Irregular rules (completely free)"
disallowIrregularRules: "No irregular rules"
showBoardLabels: "Display row and column numbering on the board"
useAvatarAsStone: "Turn stones into user avatars"
_offlineScreen:
title: "Offline - cannot connect to the server"
header: "Unable to connect to the server"

View File

@ -1209,6 +1209,7 @@ hemisphere: "Región"
withSensitive: "Mostrar notas que contengan material sensible"
userSaysSomethingSensitive: "La publicación de {name} contiene material sensible"
enableHorizontalSwipe: "Deslice para cambiar de pestaña"
surrender: "detener"
_bubbleGame:
howToPlay: "Cómo jugar"
_howToPlay:

View File

@ -1164,7 +1164,11 @@ remainingN: "Restants : {n}"
overwriteContentConfirm: "Voulez-vous remplacer le contenu actuel ?"
seasonalScreenEffect: "Effet d'écran saisonnier"
decorate: "Décorer"
sfx: "Effets sonores"
showReplay: "Voir le replay"
ranking: "Classement"
lastNDays: "Derniers {n} jours"
surrender: "Annuler"
_announcement:
forExistingUsers: "Pour les utilisateurs existants seulement"
readConfirmTitle: "Marquer comme lu ?"
@ -1302,10 +1306,13 @@ _achievements:
title: "Régulier III"
description: "Se connecter pour un total de 400 jours"
_login500:
title: "Expert I"
description: "Se connecter pour un total de 500 jours"
_login600:
title: "Expert II"
description: "Se connecter pour un total de 600 jours"
_login700:
title: "Expert III"
description: "Se connecter pour un total de 700 jours"
_login800:
description: "Se connecter pour un total de 800 jours"
@ -1400,9 +1407,12 @@ _role:
description: "Description du rôle"
permission: "Rôle et autorisations"
assignTarget: "Attribuer"
manual: "Manuel"
manualRoles: "Rôles manuels"
conditional: "Conditionnel"
conditionalRoles: "Rôles conditionnels"
condition: "Condition"
isConditionalRole: "Ceci est un rôle conditionnel."
isPublic: "Rôle public"
options: "Options"
policies: "Stratégies"

View File

@ -1209,6 +1209,7 @@ hemisphere: "Letak kamu tinggal"
withSensitive: "Lampirkan catatan dengan berkas sensitif"
userSaysSomethingSensitive: "Postingan oleh {name} mengandung konten sensitif"
enableHorizontalSwipe: "Geser untuk mengganti tab"
surrender: "Batalkan"
_bubbleGame:
howToPlay: "Cara bermain"
_howToPlay:

8
locales/index.d.ts vendored
View File

@ -6442,6 +6442,10 @@ export interface Locale extends ILocale {
* 稿
*/
"canPublicNote": string;
/**
*
*/
"mentionMax": string;
/**
*
*/
@ -8913,6 +8917,10 @@ export interface Locale extends ILocale {
* {n}
*/
"followedBySomeUsers": ParameterizedString<"n">;
/**
*
*/
"flushNotification": string;
"_types": {
/**
*

View File

@ -991,6 +991,7 @@ neverShow: "Non mostrare più"
remindMeLater: "Rimanda"
didYouLikeMisskey: "Ti piace Misskey?"
pleaseDonate: "Misskey è il software libero utilizzato su {host}. Offrendo una donazione è più facile continuare a svilupparlo!"
correspondingSourceIsAvailable: ""
roles: "Ruoli"
role: "Ruolo"
noRole: "Ruolo non trovato"
@ -1168,6 +1169,12 @@ confirmShowRepliesAll: "Questa è una attività irreversibile. Vuoi davvero incl
confirmHideRepliesAll: "Questa è una attività irreversibile. Vuoi davvero escludere tutte le risposte dei following in TL?"
externalServices: "Servizi esterni"
sourceCode: "Codice sorgente"
sourceCodeIsNotYetProvided: ""
repositoryUrl: "URL della repository"
repositoryUrlDescription: "Se esiste un repository il cui il codice sorgente è disponibile pubblicamente, inserisci il suo URL. Se stai utilizzando Misskey così com'è (senza alcuna modifica al codice sorgente), inserisci https://github.com/misskey-dev/misskey."
repositoryUrlOrTarballRequired: "Se non disponi di un repository pubblico, dovrai fornire un file tarball (tar). Vedere .config/example.yml per i dettagli."
feedback: "Feedback"
feedbackUrl: "URL di feedback"
impressum: "Dichiarazione di proprietà"
impressumUrl: "URL della dichiarazione di proprietà"
impressumDescription: "La dichiarazione di proprietà, è obbligatoria in alcuni paesi come la Germania (Impressum)."
@ -1199,7 +1206,7 @@ addMfmFunction: "Aggiungi decorazioni"
enableQuickAddMfmFunction: "Attiva il selettore di funzioni MFM"
bubbleGame: "Bubble Game"
sfx: "Effetti sonori"
soundWillBePlayed: "Verrà riprodotto il suono"
soundWillBePlayed: "Con musica ed effetti sonori"
showReplay: "Vedi i replay"
replay: "Replay"
replaying: "Replay in corso"
@ -1210,12 +1217,13 @@ hemisphere: "Geolocalizzazione"
withSensitive: "Mostra le Note con allegati espliciti"
userSaysSomethingSensitive: "Note da {name} con allegati espliciti"
enableHorizontalSwipe: "Trascina per invertire i tab"
surrender: "Annulla"
_bubbleGame:
howToPlay: "Come giocare"
_howToPlay:
section1: "Regola la posizione e rilascia l'oggetto nella casella."
section2: "Ottieni un punteggio, quando due oggetti dello stesso tipo si toccano e si trasformano in un oggetto diverso."
section3: "Se gli oggetti traboccano dalla scatola, il gioco finisce. Cerca di ottenere un punteggio elevato fondendo gli oggetti, evitando che escano dalla scatola!"
section1: "Scegli la posizione e rilascia l'oggetto nel contenitore."
section2: "Se due oggetti dello stesso tipo si toccano, si trasformano in un oggetto diverso, aumentando il punteggio."
section3: "Se gli oggetti escono dal limite superiore del contenitore, il gioco finisce. Cerca di ottenere un punteggio elevato fondendo gli oggetti, evitando che escano dal contenitore!"
_announcement:
forExistingUsers: "Solo ai profili attuali"
forExistingUsersDescription: "L'annuncio sarà visibile solo ai profili esistenti in questo momento. Se disabilitato, sarà visibile anche ai profili che verranno creati dopo la pubblicazione di questo annuncio."
@ -1756,6 +1764,8 @@ _aboutMisskey:
contributors: "Principali sostenitori"
allContributors: "Tutti i sostenitori"
source: "Codice sorgente"
original: "Originale"
thisIsModifiedVersion: "{name} sta usando una versione modificata diversa da Misskey originale."
translation: "Tradurre Misskey"
donate: "Sostieni Misskey"
morePatrons: "Apprezziamo sinceramente il supporto di tante altre persone. Grazie mille! 🥰"

View File

@ -1665,6 +1665,7 @@ _role:
gtlAvailable: "グローバルタイムラインの閲覧"
ltlAvailable: "ローカルタイムラインの閲覧"
canPublicNote: "パブリック投稿の許可"
mentionMax: "ノート内の最大メンション数"
canInvite: "サーバー招待コードの発行"
inviteLimit: "招待コードの作成可能数"
inviteLimitCycle: "招待コードの発行間隔"
@ -2356,6 +2357,7 @@ _notification:
reactedBySomeUsers: "{n}人がリアクションしました"
renotedBySomeUsers: "{n}人がリノートしました"
followedBySomeUsers: "{n}人にフォローされました"
flushNotification: "通知の履歴をリセットする"
_types:
all: "すべて"

View File

@ -991,6 +991,7 @@ neverShow: "今後表示しない"
remindMeLater: "また後で"
didYouLikeMisskey: "Misskey気に入ってくれた"
pleaseDonate: "Misskeyは{host}が使うとる無料のソフトウェアやで。これからも開発を続けれるように、寄付したってな~。"
correspondingSourceIsAvailable: "{anchor}"
roles: "ロール"
role: "ロール"
noRole: "ロールはありまへん"
@ -1208,6 +1209,7 @@ hemisphere: "住んでる地域"
withSensitive: "センシティブなファイルを含むノートを表示"
userSaysSomethingSensitive: "{name}のセンシティブなファイルを含む投稿"
enableHorizontalSwipe: "スワイプしてタブを切り替える"
surrender: "やめとく"
_bubbleGame:
howToPlay: "遊び方"
_howToPlay:

View File

@ -640,6 +640,7 @@ icon: "아바타"
replies: "답하기"
renotes: "리노트"
attach: "옇기"
surrender: "아이예"
_initialAccountSetting:
startTutorial: "길라잡이 하기"
_initialTutorial:

View File

@ -991,6 +991,7 @@ neverShow: "다시 보지 않기"
remindMeLater: "나중에 알림"
didYouLikeMisskey: "Misskey가 마음에 드시나요?"
pleaseDonate: "Misskey는 {host} 서버의 무료 소프트웨어입니다. 앞으로도 개발을 이어 나가려면 후원이 절실히 필요합니다!"
correspondingSourceIsAvailable: "소스 코드는 {anchor}에서 받아보실 수 있습니다."
roles: "역할"
role: "역할"
noRole: "역할이 없습니다"
@ -1168,6 +1169,12 @@ confirmShowRepliesAll: "이 조작은 되돌릴 수 없습니다. 정말로 타
confirmHideRepliesAll: "이 조작은 되돌릴 수 없습니다. 정말로 타임라인에 현재 팔로우 중인 사람 전원의 답글이 나오지 않게 하시겠습니까?"
externalServices: "외부 서비스"
sourceCode: "소스 코드"
sourceCodeIsNotYetProvided: "소스 코드를 아직 제공하지 않습니다. 이 문제를 해결하려면 관리자에게 문의해 주세요."
repositoryUrl: "저장소 URL"
repositoryUrlDescription: "소스 코드를 공개한 저장소가 있는 경우, 그 URL을 적습니다. Misskey를 원본 그대로 (소스 코드를 어떤 식으로도 변경하지 않고) 쓰고 있는 경우 https://github.com/misskey-dev/misskey 라고 적습니다."
repositoryUrlOrTarballRequired: "저장소를 공개하지 않은 경우 대신 tarball을 제공할 필요가 있습니다. 세부사항은 .config/example.yml을 참조해 주세요."
feedback: "피드백"
feedbackUrl: "피드백 URL"
impressum: "운영자 정보"
impressumUrl: "운영자 정보 URL"
impressumDescription: "독일 등의 일부 나라와 지역에서는 꼭 표시해야 합니다(Impressum)."
@ -1210,6 +1217,7 @@ hemisphere: "거주 지역"
withSensitive: "민감한 파일이 포함된 노트 보기"
userSaysSomethingSensitive: "{name}의 민감한 파일이 포함된 게시물"
enableHorizontalSwipe: "스와이프하여 탭 전환"
surrender: "그만두기"
_bubbleGame:
howToPlay: "설명"
_howToPlay:
@ -1756,6 +1764,8 @@ _aboutMisskey:
contributors: "주요 기여자"
allContributors: "모든 기여자"
source: "소스 코드"
original: "원본"
thisIsModifiedVersion: "{name}에서는 원본 미스키를 수정한 버전을 사용하고 있습니다."
translation: "Misskey를 번역하기"
donate: "Misskey에 기부하기"
morePatrons: "이 외에도 다른 많은 분들이 도움을 주시고 계십니다. 감사합니다🥰"
@ -2371,6 +2381,7 @@ _moderationLogTypes:
resetPassword: "비밀번호 재설정"
suspendRemoteInstance: "리모트 서버를 정지"
unsuspendRemoteInstance: "리모트 서버의 정지를 해제"
updateRemoteInstanceNote: "리모트 서버의 조정 기록 갱신"
markSensitiveDriveFile: "파일에 열람주의를 설정"
unmarkSensitiveDriveFile: "파일에 열람주의를 해제"
resolveAbuseReport: "신고 처리"

View File

@ -463,6 +463,7 @@ options: "Alternativ"
icon: "Avatar"
replies: "Svar"
renotes: "Renote"
surrender: "Avbryt"
_initialAccountSetting:
theseSettingsCanEditLater: "Du kan endre disse innstillingene senere."
_achievements:

View File

@ -1011,6 +1011,7 @@ renotes: "Repostar"
keepScreenOn: "Manter a tela do dispositivo sempre ligada"
flip: "Inversão"
lastNDays: "Últimos {n} dias"
surrender: "Cancelar"
_initialAccountSetting:
followUsers: "Siga usuários que lhe interessam para criar a sua linha do tempo."
_serverSettings:

View File

@ -1085,6 +1085,7 @@ loadReplies: "Показать ответы"
sourceCode: "Исходный код"
flip: "Переворот"
lastNDays: "Последние {n} сут"
surrender: "Этот пост не может быть отменен."
_initialAccountSetting:
accountCreated: "Аккаунт успешно создан!"
letsStartAccountSetup: "Давайте настроим вашу учётную запись."

View File

@ -8,12 +8,12 @@ search: "ค้นหา"
notifications: "การเเจ้งเตือน"
username: "ชื่อผู้ใช้"
password: "รหัสผ่าน"
forgotPassword: "ลืมรหัสผ่านใช่ไหม"
forgotPassword: "ลืมรหัสผ่าน"
fetchingAsApObject: "กำลังดึงข้อมูลจากสหพันธ์..."
ok: "ตกลง"
gotIt: "เข้าใจแล้ว !"
cancel: "ยกเลิก"
noThankYou: "ไม่เป็นไร"
noThankYou: "ไม่เอาดีกว่า"
enterUsername: "กรอกชื่อผู้ใช้"
renotedBy: "รีโน้ตโดย {user}"
noNotes: "ไม่มีโน้ต"
@ -31,16 +31,16 @@ login: "เข้าสู่ระบบ"
loggingIn: "กำลังเข้าสู่ระบบ"
logout: "ออกจากระบบ"
signup: "สร้างบัญชีผู้ใช้"
uploading: "กำลังอัพโหลด..."
uploading: "กำลังอัปโหลด"
save: "บันทึก"
users: "ผู้ใช้งาน"
addUser: "เพิ่มผู้ใช้"
favorite: "รายการโปรด"
favorites: "รายการโปรด"
unfavorite: "ลบออกจากรายการโปรด"
favorited: "เพิ่มแล้วในรายการโปรด"
alreadyFavorited: "เพิ่มในรายการโปรดอยู่แล้ว"
cantFavorite: "ไม่สามารถเพิ่มในรายการโปรดได้"
favorited: "เพิ่มลงรายการโปรดแล้ว"
alreadyFavorited: "เพิ่มลงรายการโปรดอยู่แล้ว"
cantFavorite: "ไม่สามารถเพิ่มลงรายการโปรดได้"
pin: "ปักหมุด"
unpin: "เลิกปักหมุด"
copyContent: "คัดลอกเนื้อหา"
@ -65,18 +65,18 @@ loadMore: "แสดงเพิ่มเติม"
showMore: "แสดงเพิ่มเติม"
showLess: "ปิด"
youGotNewFollower: "ได้ติดตามคุณ"
receiveFollowRequest: "คำขอผู้ติดตามที่ได้รับ"
followRequestAccepted: "อนุมัติการติดตามแล้ว"
receiveFollowRequest: "มีคำขอติดตามส่งมาหา"
followRequestAccepted: "การติดตามได้รับการอนุมัติแล้ว"
mention: "กล่าวถึง"
mentions: "พูดถึง"
directNotes: "ไดเร็คโน้ต"
directNotes: "โพสต์แบบไดเร็กต์"
importAndExport: "นำเข้า / ส่งออก"
import: "นำเข้า"
export: "ส่งออก"
files: "ไฟล์"
download: "ดาวน์โหลด"
driveFileDeleteConfirm: "ต้องการลบไฟล์ “{name}” ใช่หรือไม? โน้ตที่แนบมากับไฟล์นี้ก็จะถูกลบไปด้วย"
unfollowConfirm: "ต้องการเลิกติดตาม {name}?"
driveFileDeleteConfirm: "ต้องการลบไฟล์ “{name}” ใช่ม? โน้ตที่แนบมากับไฟล์นี้ก็จะถูกลบไปด้วย"
unfollowConfirm: "ต้องการเลิกติดตาม {name} ใช่ไหม?"
exportRequested: "คุณได้ร้องขอการส่งออก อาจใช้เวลาสักครู่ และจะถูกเพิ่มในไดรฟ์ของคุณเมื่อเสร็จสิ้นแล้ว"
importRequested: "คุณได้ร้องขอการนำเข้า การดำเนินการนี้อาจใช้เวลาสักครู่"
lists: "รายชื่อ"
@ -128,9 +128,9 @@ emojiPickerDisplay: "แสดงตัวจิ้มเอโมจิ"
overwriteFromPinnedEmojisForReaction: "เขียนทับการตั้งค่ารีแอคชั่น"
overwriteFromPinnedEmojis: "เขียนทับการตั้งค่าทั่วไป"
reactionSettingDescription2: "ลากเพื่อจัดลำดับใหม่ คลิกที่เอโมจินั้นเพื่อลบ กด “+” เพื่อเพิ่ม"
rememberNoteVisibility: "จดจำการตั้งค่าการมองเห็นตัวโน้ต"
attachCancel: "ลบไฟล์ออกที่แนบมา"
deleteFile: "ลบไฟล์ออกแล้ว"
rememberNoteVisibility: "จำการตั้งค่าการมองเห็นโน้ต"
attachCancel: "ยกเิกแนบไฟล์"
deleteFile: "ลบไฟล์ออก"
markAsSensitive: "ทำเครื่องหมายว่ามีเนื้อหาละเอียดอ่อน"
unmarkAsSensitive: "ยกเลิกทำเครื่องหมายว่ามีเนื้อหาละเอียดอ่อน"
enterFileName: "พิมพ์ชื่อไฟล์"
@ -138,14 +138,14 @@ mute: "ปิดเสียง"
unmute: "ยกเลิกการปิดเสียง"
renoteMute: "ปิดเสียงรีโน้ต"
renoteUnmute: "เปิดเสียง รีโน้ต"
block: "บล็อ"
unblock: "เลิกปิดกั้น"
suspend: "ถูกระงับ"
unsuspend: "ยกเลิกระงับ"
blockConfirm: "ต้องการบล็อกบัญชีนี้?"
unblockConfirm: "ต้องการปลดบล็อคบัญชีนี้?"
suspendConfirm: "ต้องการระงับบัญชีนี้?"
unsuspendConfirm: "ต้องการยกเลิกการระงับบัญชีนี้?"
block: "บล็อ"
unblock: "เลิกบล็อก"
suspend: "ระงับ"
unsuspend: "เลิกระงับ"
blockConfirm: "ต้องการบล็อกบัญชีนี้ใช่ไหม?"
unblockConfirm: "ต้องการเลิกบล็อกบัญชีนี้ใช่ไหม?"
suspendConfirm: "ต้องการระงับบัญชีนี้ใช่ไหม?"
unsuspendConfirm: "ต้องการยกเลิกการระงับบัญชีนี้ใช่ไหม?"
selectList: "เลือกรายชื่อ"
editList: "แก้ไขรายชื่อ"
selectChannel: "เลือกช่อง"
@ -168,7 +168,7 @@ cacheRemoteSensitiveFiles: "แคชไฟล์ระยะไกลที่
cacheRemoteSensitiveFilesDescription: "เมื่อปิดการใช้งานการตั้งค่านี้ ไฟล์ระยะไกลที่มีเครื่องหมายว่ามีเนื้อหาละเอียดอ่อนนั้นจะถูกโหลดโดยตรงจากอินสแตนซ์ระยะไกลโดยที่ไม่มีการแคช"
flagAsBot: "ทำเครื่องหมายบอกว่าบัญชีนี้เป็นบอท"
flagAsBotDescription: "การเปิดใช้งานตัวเลือกนี้หากบัญชีนี้ถูกควบคุมโดยนักเขียนโปรแกรม หรือ ถ้าหากเปิดใช้งาน มันจะทำหน้าที่เป็นแฟล็กสำหรับนักพัฒนารายอื่นๆ และเพื่อป้องกันการโต้ตอบแบบไม่มีที่สิ้นสุดกับบอทตัวอื่นๆ และยังสามารถปรับเปลี่ยนระบบภายในของ Misskey เพื่อปฏิบัติต่อบัญชีนี้เป็นบอท"
flagAsCat: "เมี้ยววววววว!!!!!!!!!!! (ทำเครื่องหมายว่าบัญชีนี้เป็นแมว)"
flagAsCat: "เมี้ยววววววววววววววว!!!!!!!!!!!"
flagAsCatDescription: "เหมียวเหมียวเมี้ยว??"
flagShowTimelineReplies: "แสดงตอบกลับ ในไทม์ไลน์"
flagShowTimelineRepliesDescription: "แสดงการตอบกลับของผู้ใช้งานไปยังโน้ตของผู้ใช้งานรายอื่นๆในไทม์ไลน์หากได้เปิดเอาไว้"
@ -180,7 +180,7 @@ showOnRemote: "ดูบนอินสแตนซ์ระยะไกล"
general: "ทั่วไป"
wallpaper: "ภาพพื้นหลัง"
setWallpaper: "ตั้งค่าภาพพื้นหลัง"
removeWallpaper: "นำภาพพื้นหลังออก"
removeWallpaper: "นำภาพพื้นหลังออก"
searchWith: "ค้นหา: {q}"
youHaveNoLists: "คุณไม่มีรายชื่อใดๆ "
followConfirm: "ต้องการติดตาม {name} ใช่ไหม?"
@ -189,11 +189,11 @@ proxyAccountDescription: "บัญชีพร็อกซี่ คือ บ
host: "โฮสต์"
selectUser: "เลือกผู้ใช้งาน"
recipient: "ผู้รับ"
annotation: "ความคิดเห็น"
annotation: "หมายเหตุประกอบ"
federation: "สหพันธ์"
instances: "อินสแตนซ์"
registeredAt: "จดทะเบียนที่"
latestRequestReceivedAt: "ได้รับคำขอล่าสุดไปแล้ว"
registeredAt: "วันที่ลงทะเบียน"
latestRequestReceivedAt: "คำขอล่าสุดที่ได้รับ"
latestStatus: "สถานะล่าสุด"
storageUsage: "พื้นที่จัดเก็บข้อมูลที่ใช้ไป"
charts: "โดดเด่น"
@ -215,10 +215,10 @@ disk: "ดิสก์"
instanceInfo: "ข้อมูลอินสแตนซ์"
statistics: "สถิติการใช้งาน"
clearQueue: "ล้างคิว"
clearQueueConfirmTitle: "คุณแน่ใจแล้วหรอว่าต้องการที่จะล้างคิว?"
clearQueueConfirmTitle: "ต้องการล้างคิวใช่ไหม?"
clearQueueConfirmText: "โพสต์ที่ยังค้างในคิวจะไม่ถูกจัดส่งอีกต่อไป โดยปกติแล้วการดำเนินการนี้ไม่จำเป็น"
clearCachedFiles: "ล้างแคช"
clearCachedFilesConfirm: "ต้องการลบไฟล์ระยะไกลที่แคชไว้ทั้งหมด?"
clearCachedFilesConfirm: "ต้องการลบไฟล์ระยะไกลที่แคชไว้ทั้งหมดใช่ไหม?"
blockedInstances: "อินสแตนซ์ที่ถูกบล็อก"
blockedInstancesDescription: "ระบุชื่อโฮสต์ของอินสแตนซ์ที่คุณต้องการบล็อก อินสแตนซ์ที่อยู่ในรายการนั้นจะไม่สามารถพูดคุยกับอินสแตนซ์นี้ได้อีกต่อไป"
silencedInstances: "ปิดปากอินสแตนซ์นี้แล้ว"
@ -228,7 +228,7 @@ mutedUsers: "ผู้ใช้ที่ถูกปิดเสียง"
blockedUsers: "ผู้ใช้ที่ถูกบล็อก"
noUsers: "ไม่พบผู้ใช้งาน"
editProfile: "แก้ไขโปรไฟล์"
noteDeleteConfirm: "ต้องการลบโน้ตนี้?"
noteDeleteConfirm: "ต้องการลบโน้ตนี้ใช่ไหม?"
pinLimitExceeded: "คุณไม่สามารถปักหมุดโน้ตเพิ่มเติมใดๆได้อีก"
intro: "การติดตั้ง Misskey เสร็จสิ้นแล้วนะ! โปรดสร้างผู้ใช้งานที่เป็นผู้ดูแลระบบ"
done: "เสร็จสิ้น"
@ -237,7 +237,7 @@ preview: "แสดงตัวอย่าง"
default: "ค่าเริ่มต้น"
defaultValueIs: "ค่าเริ่มต้น: {value}"
noCustomEmojis: "ไม่มีเอโมจิ"
noJobs: "ไม่มีชิ้นงาน"
noJobs: "ไม่มีงาน"
federating: "สหพันธ์"
blocked: "ถูกบล็อก"
suspended: "ถูกระงับ"
@ -261,11 +261,11 @@ usernameOrUserId: "ชื่อผู้ใช้หรือรหัสผู
noSuchUser: "ไม่พบผู้ใช้"
lookup: "การค้นหา"
announcements: "ประกาศ"
imageUrl: "url รูปภาพ"
imageUrl: "URL รูปภาพ"
remove: "ลบ"
removed: "ถูกลบไปแล้ว"
removeAreYouSure: "ต้องการที่จะลบ “{x}” ออก?"
deleteAreYouSure: "ต้องการลบ {x} หรือไ่คะ?"
removeAreYouSure: "ต้องการลบ “{x}” ใช่ไหม?"
deleteAreYouSure: "ต้องการลบ {x} ใช่ไหม?"
resetAreYouSure: "รีเซ็ตเลยไหม?"
areYouSure: "แน่ใจแล้วใช่ไหมคะ?"
saved: "บันทึกแล้ว"
@ -275,7 +275,7 @@ keepOriginalUploading: "เก็บภาพต้นฉบับ"
keepOriginalUploadingDescription: "เก็บภาพต้นฉบับไว้เมื่ออัปโหลดภาพ หากปิด รูปภาพสำหรับการเผยแพร่ทางเว็บจะถูกสร้างขึ้นในเบราว์เซอร์เมื่อทำการอัปโหลด"
fromDrive: "จากไดรฟ์"
fromUrl: "จาก URL"
uploadFromUrl: "อัโหลดจาก URL"
uploadFromUrl: "อัโหลดจาก URL"
uploadFromUrlDescription: "URL ของไฟล์ที่คุณต้องการอัปโหลด"
uploadFromUrlRequested: "ร้องขอการอัปโหลดแล้ว"
uploadFromUrlMayTakeTime: "การอัปโหลดอาจใช้เวลาสักครู่จึงจะเสร็จสมบูรณ์"
@ -289,7 +289,7 @@ agree: "ยอมรับ"
agreeBelow: "ฉันยอมรับถึงด้านล่าง"
basicNotesBeforeCreateAccount: "หมายเหตุสำคัญ"
termsOfService: "เงื่อนไขการให้บริการ"
start: "เริ่มต้น​ใช้งาน​"
start: "เริ่ม"
home: "หน้าแรก"
remoteUserCaution: "ข้อมูลอาจไม่สมบูรณ์เนื่องจากผู้ใช้รายนี้มาจากอินสแตนซ์ระยะไกล"
activity: "กิจกรรม"
@ -333,11 +333,11 @@ rename: "เปลี่ยนชื่อ"
avatar: "ไอคอน"
banner: "แบนเนอร์"
displayOfSensitiveMedia: "แสดงสื่อที่มีเนื้อหาละเอียดอ่อน"
whenServerDisconnected: "สูญเสียการเชื่อมต่อกับเซิร์ฟเวอร์"
disconnectedFromServer: "ถูกตัดการเชื่อมต่อออกจากเซิร์ฟเวอร์"
whenServerDisconnected: "เมื่อสูญเสียการเชื่อมต่อกับเซิร์ฟเวอร์"
disconnectedFromServer: "การเชื่อมต่อเซิร์ฟเวอร์ถูกตัด"
reload: "รีโหลด"
doNothing: "เมิน"
reloadConfirm: "นายต้องการรีเฟรชไทม์ไลน์หรือป่าว?"
reloadConfirm: "รีโหลดเลยไหม?"
watch: "ดู"
unwatch: "หยุดดู"
accept: "ยอมรับ"
@ -347,7 +347,7 @@ instanceName: "ชื่ออินสแตนซ์"
instanceDescription: "คำอธิบายอินสแตนซ์"
maintainerName: "ผู้ดูแล"
maintainerEmail: "อีเมลผู้ดูแลระบบ"
tosUrl: "เงื่อนไขการให้บริการ URL"
tosUrl: "URL เงื่อนไขการให้บริการ"
thisYear: "ปีนี้"
thisMonth: "เดือนนี้"
today: "วันนี้"
@ -370,7 +370,7 @@ inMb: "เป็นเมกะไบต์"
bannerUrl: "URL รูปภาพแบนเนอร์"
backgroundImageUrl: "URL ภาพพื้นหลัง"
basicInfo: "ข้อมูลเบื้องต้น"
pinnedUsers: "ผู้ใช้งานที่ได้รับการปักหมุด"
pinnedUsers: "ผู้ใช้ที่ถูกปักหมุด"
pinnedUsersDescription: "ป้อนชื่อผู้ใช้ที่คุณต้องการปักหมุดในหน้า “ค้นพบ” ฯลฯ คั่นด้วยการขึ้นบรรทัดใหม่"
pinnedPages: "หน้าเพจที่ปักหมุด"
pinnedPagesDescription: "ป้อนเส้นทางของหน้าเพจที่คุณต้องการปักหมุดไว้ที่หน้าแรกของอินสแตนซ์นี้ คั่นด้วยขึ้นบรรทัดใหม่"
@ -409,16 +409,16 @@ caseSensitive: "อักษรพิมพ์ใหญ่-พิมพ์เล
withReplies: "รวมตอบกลับ"
connectedTo: "บัญชีดังต่อไปนี้มีการเชื่อมต่อกัน"
notesAndReplies: "โพสต์และการตอบกลับ"
withFiles: "รวบรวมไฟล์"
withFiles: "ไฟล์"
silence: "ถูกปิดปาก"
silenceConfirm: "ต้องการที่จะ ปิดปาก ผู้ใช้รายนี้?"
silenceConfirm: "ต้องการปิดปากผู้ใช้รายนี้ใช่ไหม?"
unsilence: "ยกเลิกการปิดปาก"
unsilenceConfirm: "ต้องการยกเลิกปิดปากผู้ใช้รายนี้?"
unsilenceConfirm: "ต้องการเลิกปิดปากผู้ใช้รายนี้ใช่ไหม?"
popularUsers: "ผู้ใช้ที่เป็นที่นิยม"
recentlyUpdatedUsers: "ผู้ใช้ที่เพิ่งใช้งานล่าสุด"
recentlyRegisteredUsers: "ผู้ใช้ที่เข้าร่วมใหม่"
recentlyDiscoveredUsers: "ผู้ใช้ที่เพิ่งค้นพบใหม่"
exploreUsersCount: "มีผู้ใช้ {จำนวน} ราย"
exploreUsersCount: "มีผู้ใช้ {count} ราย"
exploreFediverse: "สำรวจสหพันธ์"
popularTags: "แท็กยอดนิยม"
userList: "ลิสต์"
@ -435,7 +435,7 @@ moderation: "การกลั่นกรอง"
moderationNote: "โน้ตการกลั่นกรอง"
addModerationNote: "เพิ่มโน้ตการกลั่นกรอง"
moderationLogs: "ปูมการแก้ไข"
nUsersMentioned: "กล่าวถึงโดยผู้ใช้ {n} รายนี้"
nUsersMentioned: "กล่าวถึงโดยผู้ใช้ {n} ราย"
securityKeyAndPasskey: "ความปลอดภัยและรหัสผ่าน"
securityKey: "กุญแจความปลอดภัย"
lastUsed: "ใช้ล่าสุด"
@ -449,7 +449,7 @@ reduceUiAnimation: "ลดภาพเคลื่อนไหว UI"
share: "แบ่งปัน"
notFound: "ไม่พบหน้าที่ต้องการ"
notFoundDescription: "ไม่พบหน้าตาม URL ที่ระบุ"
uploadFolder: "โฟลเดอร์เริ่มต้นสำหรับอัโหลด"
uploadFolder: "โฟลเดอร์เริ่มต้นสำหรับอัโหลด"
markAsReadAllNotifications: "ทำเครื่องหมายการแจ้งเตือนทั้งหมดว่าอ่านแล้ว"
markAsReadAllUnreadNotes: "ทำเครื่องหมายโน้ตทั้งหมดว่าอ่านแล้ว"
markAsReadAllTalkMessages: "ทำเครื่องหมายข้อความทั้งหมดว่าอ่านแล้ว"
@ -464,7 +464,7 @@ text: "ข้อความ"
enable: "เปิดใช้งาน"
next: "ถัด​ไป"
retype: "พิมพ์รหัสอีกครั้ง"
noteOf: "โน้ต โดย {user}"
noteOf: "โน้ตของ {user}"
quoteAttached: "อ้างอิง"
quoteQuestion: "ต้องการที่จะแนบมันเพื่ออ้างอิงใช่ไหม?"
noMessagesYet: "ยังไม่มีข้อความ"
@ -472,7 +472,7 @@ newMessageExists: "คุณมีข้อความใหม่"
onlyOneFileCanBeAttached: "สามารถแนบไฟล์ได้เพียงไฟล์เดียวต่อ 1 ข้อความ"
signinRequired: "กรุณาลงทะเบียนหรือลงชื่อเข้าใช้ก่อนดำเนินการต่อ"
invitations: "คำเชิญ"
invitationCode: "รหัสคำเชิญ"
invitationCode: "รหัสเชิญ"
checking: "Checking"
available: "พร้อมใช้งาน"
unavailable: "ไม่พร้อมใช้"
@ -557,7 +557,7 @@ popout: "ป๊อปเอาต์"
volume: "ระดับเสียง"
masterVolume: "ระดับเสียงหลัก"
notUseSound: "ไม่ใช้เสียง"
useSoundOnlyWhenActive: "มีเสียงออกเฉพาะเมื่อ Misskey ทำงานอยู่"
useSoundOnlyWhenActive: "มีเสียงออกเฉพาะตอนกำลังใช้ Misskey อยู่เท่านั้น"
details: "รายละเอียด"
chooseEmoji: "เลือกเอโมจิ"
unableToProcess: "ไม่สามารถดำเนินการให้เสร็จสิ้นได้"
@ -570,8 +570,8 @@ installedDate: "วันที่ติดตั้ง"
lastUsedDate: "ใช้งานครั้งล่าสุด"
state: "สถานะ"
sort: "เรียงลำดับ"
ascendingOrder: "เรียงจากน้อยไปมาก"
descendingOrder: "เรียงจากมากไปน้อย"
ascendingOrder: "เรียงลำดับขึ้น"
descendingOrder: "เรียงลำดับลง"
scratchpad: "Scratchpad"
scratchpadDescription: "Scratchpad เป็นการจัดเตรียมสภาพแวดล้อมสำหรับการทดลอง AiScript แต่คุณสามารถเขียน ดำเนินการ และตรวจสอบผลลัพธ์ของการโต้ตอบกับ Misskey มันได้ด้วยนะ"
output: "เอาท์พุต"
@ -579,15 +579,15 @@ script: "สคริปต์"
disablePagesScript: "ปิดการใช้งาน AiScript บนเพจ"
updateRemoteUser: "อัปเดตข้อมูลผู้ใช้งานระยะไกล"
unsetUserAvatar: "เลิกตั้งอวตาร"
unsetUserAvatarConfirm: "ต้องการเลิกตั้งอวตาร?"
unsetUserAvatarConfirm: "ต้องการเลิกตั้งอวตารใข่ไหม?"
unsetUserBanner: "เลิกตั้งแบนเนอร์"
unsetUserBannerConfirm: "ต้องการเลิกตั้งแบนเนอร์?"
deleteAllFiles: "ลบไฟล์ทั้งหมด"
deleteAllFilesConfirm: "ต้องการลบไฟล์ทั้งหมดรือไ?"
deleteAllFilesConfirm: "ต้องการลบไฟล์ทั้งหมดใช่ไหม?"
removeAllFollowing: "เลิกติดตามผู้ใช้ที่ติดตามทั้งหมด"
removeAllFollowingDescription: "เลิกติดตามทั้งหมดจาก {host} โปรดเรียกใช้สิ่งนี้เมื่ออินสแตนซ์ดังกล่าวได้สูญหายตายจากไปแล้ว"
userSuspended: "ผู้ใช้รายนี้ถูกระงับการใช้งาน"
userSilenced: "ผู้ใช้รายนี้กำลังถูกปิดกั้น"
userSilenced: "ผู้ใช้รายนี้ถูกปิดปากอยู่"
yourAccountSuspendedTitle: "บัญชีนี้นั้นถูกระงับ"
yourAccountSuspendedDescription: "บัญชีนี้ถูกระงับ เนื่องจากละเมิดข้อกำหนดในการให้บริการของเซิร์ฟเวอร์หรืออาจจะละเมิดหลักเกณฑ์ชุมชน หรือ อาจจะโดนร้องเรียนเรื่องการละเมิดลิขสิทธิ์และอื่นๆอย่างต่อเนื่องซ้ำๆ หากคุณคิดว่าไม่ได้ทำผิดจริงๆหรือตัดสินผิดพลาด ได้โปรดกรุณาติดต่อผู้ดูแลระบบหากคุณต้องการทราบเหตุผลโดยละเอียดเพิ่มเติม และขอความกรุณาอย่าสร้างบัญชีใหม่"
tokenRevoked: "โทเค็นไม่ถูกต้อง"
@ -600,7 +600,7 @@ addItem: "เพิ่มรายการ"
rearrange: "จัดใหม่"
relays: "รีเลย์"
addRelay: "เพิ่มรีเลย์"
inboxUrl: "อินบ็อกซ์ URL"
inboxUrl: "URL ของอินบ็อกซ์"
addedRelays: "เพิ่มรีเลย์แล้ว"
serviceworkerInfo: "ต้องเปิดใช้งานสำหรับการแจ้งเตือนแบบพุช"
deletedNote: "โน้ตที่ถูกลบ"
@ -617,7 +617,7 @@ description: "รายละเอียด"
describeFile: "เพิ่มแคปชั่น"
enterFileDescription: "ใส่แคปชั่น"
author: "ผู้เขียน"
leaveConfirm: "คุณมีการเปลี่ยนแปลงที่ไม่ได้บันทึกนะ นายต้องการทิ้งการเปลี่ยนแปลงเหล่านั้นหรอ?"
leaveConfirm: "มีการเปลี่ยนแปลงที่ยังไม่ได้บันทึก ต้องการละทิ้งมันใช่ไหม?"
manage: "การจัดการ"
plugins: "ปลั๊กอิน"
preferencesBackups: "ตั้งค่าการสำรองข้อมูล"
@ -664,7 +664,7 @@ display: "แสดงผล"
copy: "คัดลอก"
metrics: "เมตริก"
overview: "ภาพรวม"
logs: "บันทึกข้อมูลระบบ"
logs: "ปูม"
delayed: "ดีเลย์"
database: "ฐานข้อมูล"
channel: "ช่อง"
@ -672,11 +672,11 @@ create: "สร้าง"
notificationSetting: "ตั้งค่าการแจ้งเตือน"
notificationSettingDesc: "เลือกประเภทการแจ้งเตือนที่ต้องการจะแสดง"
useGlobalSetting: "ใช้การตั้งค่าส่วนกลาง"
useGlobalSettingDesc: "หากเปิดไว้ ระบบจะใช้การตั้งค่าการแจ้งเตือนของบัญชีของคุณ หากปิดอยู่ สามารถทำการกำหนดค่าแต่ละรายการได้นะ"
useGlobalSettingDesc: "เมื่อเปิดใช้งาน ใช้การตั้งค่าการแจ้งเตือนจากบัญชีคุณ เมื่อปิดใช้งาน สามารถตั้งค่าได้อย่างอิสระ"
other: "อื่น ๆ"
regenerateLoginToken: "สร้างโทเค็นการเข้าสู่ระบบอีกครั้ง"
regenerateLoginTokenDescription: "สร้างโทเค็นใหม่ที่ใช้ภายในระหว่างการเข้าสู่ระบบ โดยตามหลักปกติแล้วการดำเนินการนี้ไม่จำเป็น หากสร้างใหม่ อุปกรณ์ทั้งหมดจะถูกออกจากระบบนะ"
theKeywordWhenSearchingForCustomEmoji: "คีย์เวิร์ดสำหรับใช้ค้นหาโมจิที่กำหนดเอง"
theKeywordWhenSearchingForCustomEmoji: "คีย์เวิร์ดสำหรับใช้ค้นหาอโมจิที่กำหนดเอง"
setMultipleBySeparatingWithSpace: "คั่นหลายรายการด้วยช่องว่าง"
fileIdOrUrl: "ไฟล์ ID หรือ URL"
behavior: "พฤติกรรม"
@ -684,14 +684,14 @@ sample: "ตัวอย่าง"
abuseReports: "รายงาน"
reportAbuse: "รายงาน"
reportAbuseRenote: "รายงานรีโน้ต"
reportAbuseOf: "รายงาน {ชื่อ}"
reportAbuseOf: "รายงาน {name}"
fillAbuseReportDescription: "กรุณากรอกรายละเอียดเกี่ยวกับรายงานนี้ หากเป็นเรื่องเกี่ยวกับโน้ตโดยเฉพาะ ได้โปรดระบุ URL"
abuseReported: "เราได้ส่งรายงานของคุณไปแล้ว ขอบคุณมากๆนะ"
reporter: "นักข่าว"
reporter: "ผู้รายงาน"
reporteeOrigin: "รายงานต้นทาง"
reporterOrigin: "นักข่าวต้นทาง"
reporterOrigin: "แหล่งผู้รายงาน"
forwardReport: "ส่งต่อรายงานไปยังอินสแตนซ์ระยะไกล"
forwardReportIsAnonymous: "แทนที่จะเป็นบัญชีของคุณ บัญชีระบบที่ไม่ระบุตัวตนจะแสดงเป็นนักข่าวที่อินสแตนซ์ระยะไกล"
forwardReportIsAnonymous: "ข้อมูลของคุณจะไม่ปรากฏบนอินสแตนซ์ระยะไกลและปรากฏเป็นบัญชีระบบที่ไม่ระบุชื่อ"
send: "ส่ง"
abuseMarkAsResolved: "ทำเครื่องหมายรายงานว่าแก้ไขแล้ว"
openInNewTab: "เปิดในแท็บใหม่"
@ -699,7 +699,7 @@ openInSideView: "เปิดในมุมมองด้านข้าง"
defaultNavigationBehaviour: "พฤติกรรมการนำทางที่เป็นค่าเริ่มต้น"
editTheseSettingsMayBreakAccount: "การแก้ไขการตั้งค่าเหล่านี้อาจทำให้บัญชีของคุณเสียหายนะ"
instanceTicker: "ข้อมูลอินสแตนซ์ของโน้ต"
waitingFor: "กำลังรอคอย {x}"
waitingFor: "กำลังรอ {x}"
random: "สุ่มค่า"
system: "ระบบ"
switchUi: "สลับ UI"
@ -709,7 +709,7 @@ createNew: "สร้างใหม่"
optional: "ไม่บังคับ"
createNewClip: "สร้างคลิปใหม่"
unclip: "ลบคลิป"
confirmToUnclipAlreadyClippedNote: "โน้ตนี้เป็นส่วนหนึ่งของคลิป \"{name}\" แล้ว คุณต้องการลบออกจากคลิปนี้แทนอย่างงั้นหรอ?"
confirmToUnclipAlreadyClippedNote: "โน้ตนี้เป็นส่วนหนึ่งของคลิป “{name}” อยู่แล้ว ต้องการนำมันออกจากคลิปใช่ไหม?"
public: "สาธารณะ"
private: "ส่วนตัว"
i18nInfo: "Misskey กำลังได้รับการแปลเป็นภาษาต่างๆ โดยอาสาสมัคร คุณสามารถช่วยเหลือได้ที่ {link}"
@ -732,7 +732,7 @@ driveFilesCount: "จำนวนไฟล์ไดรฟ์"
driveUsage: "การใช้พื้นที่ไดรฟ์"
noCrawle: "ปฏิเสธการจัดทำดัชนีของโปรแกรมรวบรวมข้อมูล"
noCrawleDescription: "ขอให้เครื่องมือค้นหาไม่จัดทำดัชนีหน้าโปรไฟล์ โน้ต หน้าเพจ ฯลฯ"
lockedAccountInfo: "เว้นแต่ว่าคุณจะต้องตั้งค่าการเปิดเผยโน้ตเป็น \"ผู้ติดตามเท่านั้น\" โน้ตย่อของคุณจะปรากฏแก่ทุกคน ถึงแม้ว่าคุณจะเป็นกำหนดให้ผู้ติดตามต้องได้รับการอนุมัติด้วยตนเองก็ตาม"
lockedAccountInfo: "แม้ว่าการอนุมัติการติดตามถูกเปิดใช้งานอยู่ทุกคนก็ยังคงสามารถเห็นโน้ตของคุณได้ เว้นแต่ว่าคุณจะเปลี่ยนการเปิดเผยโน้ตของคุณเป็น “เฉพาะผู้ติดตาม”"
alwaysMarkSensitive: "ทำเครื่องหมายว่ามีเนื้อหาละเอียดอ่อนเป็นค่าเริ่มต้น"
loadRawImages: "โหลดภาพต้นฉบับแทนการแสดงภาพขนาดย่อ"
disableShowingAnimatedImages: "ไม่ต้องเล่นภาพเคลื่อนไหว"
@ -768,29 +768,29 @@ nNotes: "{n} โน้ต"
sendErrorReports: "ส่งรายงานว่าข้อผิดพลาด"
sendErrorReportsDescription: "เมื่อเปิดใช้งาน ข้อมูลข้อผิดพลาดโดยรายละเอียดนั้นจะถูกแชร์ให้กับ Misskey เมื่อเกิดปัญหา ซึ่งช่วยปรับปรุงคุณภาพของ Misskey\nซึ่งจะรวมถึงข้อมูล เช่น เวอร์ชั่นของระบบปฏิบัติการ เบราว์เซอร์ที่คุณใช้ กิจกรรมของคุณใน Misskey เป็นต้น"
myTheme: "ธีมของฉัน"
backgroundColor: "ภาพพื้นหลัง"
accentColor: "รูปแบบสี"
backgroundColor: "สีพื้นหลัง"
accentColor: "สีหลัก"
textColor: "สีข้อความ"
saveAs: "บันทึกเป็น..."
advanced: "ขั้นสูง"
advancedSettings: "การตั้งค่าขั้นสูง"
value: "ค่า"
createdAt: "สร้างเมื่อ"
updatedAt: "อัพเดทล่าสุด"
updatedAt: "อัปเดตล่าสุด"
saveConfirm: "บันทึกเปลี่ยนแปลงมั้ย?"
deleteConfirm: "ลบจริงๆเหรอ?"
invalidValue: "ค่านี้ไม่ถูกต้อง"
registry: "ทะเบียน"
closeAccount: "ปิด บัญชี"
currentVersion: "เวอร์ชั่นปัจจุบัน"
latestVersion: "รุ่นปัจจุบัน"
latestVersion: "เวอร์ชั่นล่าสุด"
youAreRunningUpToDateClient: "คุณกำลังใช้ไคลเอ็นต์เวอร์ชันใหม่ล่าสุดนะ"
newVersionOfClientAvailable: "มีไคลเอ็นต์เวอร์ชันใหม่กว่าของคุณพร้อมใช้งานนะ"
usageAmount: "การใช้งาน"
capacity: "ความจุ"
inUse: "ใช้แล้ว"
editCode: "แก้ไขโค้ด"
apply: "ตกลง"
apply: "นำไปใช้"
receiveAnnouncementFromInstance: "รับการแจ้งเตือนจากอินสแตนซ์นี้"
emailNotification: "การแจ้งเตือนทางอีเมล"
publish: "เผยแพร่"
@ -802,7 +802,7 @@ showingPastTimeline: "กำลังแสดงผลไทม์ไลน์
clear: "ล้าง"
markAllAsRead: "ทำเครื่องหมายทั้งหมดว่าอ่านแล้ว"
goBack: "ย้อนกลับ"
unlikeConfirm: "เลิกถูกใจจริงๆ หรือ?"
unlikeConfirm: "ต้องการเลิกถูกใจใช่ไหม?"
fullView: "มุมมองแบบเต็ม"
quitFullView: "ออกจากมุมมองแบบเต็ม"
addDescription: "เพิ่มคำอธิบาย"
@ -813,12 +813,12 @@ userInfo: "ข้อมูลผู้ใช้"
unknown: "ไม่ทราบสถานะ"
onlineStatus: "สถานะออนไลน์"
hideOnlineStatus: "ซ่อนสถานะออนไลน์"
hideOnlineStatusDescription: "การซ่อนสถานะออนไลน์ของคุณช่วยลดความสะดวกของคุณสมบัติบางอย่าง เช่น การค้นหา อ่ะนะ"
hideOnlineStatusDescription: "การซ่อนสถานะออนไลน์อาจทำให้ฟังก์ชันบางอย่าง เช่น การค้นหา สะดวกน้อยลง"
online: "ออนไลน์"
active: "ใช้งานอยู่"
offline: "ออฟไลน์"
notRecommended: "ไม่แนะนำ"
botProtection: "การป้องกัน Bot (or AI)"
botProtection: "การป้องกัน Bot"
instanceBlocking: "อินสแตนซ์ที่ถูกบล็อก"
selectAccount: "เลือกบัญชี"
switchAccount: "สลับบัญชีผู้ใช้"
@ -880,7 +880,7 @@ itsOff: "ปิดใช้งาน"
on: "เปิด"
off: "ปิด"
emailRequiredForSignup: "จำเป็นต้องการใช้ที่อยู่อีเมลสำหรับการสมัคร"
unread: "ไม่ได้อ่าน"
unread: "ยังไม่ได้อ่าน"
filter: "กรอง"
controlPanel: "แผงควบคุม"
manageAccounts: "จัดการบัญชี"
@ -888,13 +888,13 @@ makeReactionsPublic: "ตั้งค่าประวัติการรี
makeReactionsPublicDescription: "การทำเช่นนี้จะทำให้รายการรีแอคชั่นของคุณที่ผ่านมาทั้งหมดปรากฏต่อสาธารณะ"
classic: "คลาสสิค"
muteThread: "ปิดเสียงเธรด"
unmuteThread: "เปิดเสียงเธรด"
unmuteThread: "เลิกปิดเสียงเธรด"
followingVisibility: "การมองเห็นที่เรากำลังติดตาม"
followersVisibility: "การมองเห็นผู้ที่กำลังติดตามเรา"
continueThread: "ดูความต่อเนื่องเธรด"
deleteAccountConfirm: "การดำเนินการนี้จะลบบัญชีของคุณอย่างถาวรเลยนะ แน่ใจหรอดำเนินการ?"
incorrectPassword: "รหัสผ่านไม่ถูกต้อง"
voteConfirm: "ยืนยันการโหวต “{choice}” ไหม?"
voteConfirm: "ต้องการโหวต “{choice}” ใช่ไหม?"
hide: "ซ่อน"
useDrawerReactionPickerForMobile: "แสดง ตัวจิ้มรีแอคชั่น เป็นแบบลิ้นชัก เมื่อใช้บนมือถือ"
welcomeBackWithName: "ยินดีต้อนรับการกลับมานะคะ, คุณ{name}"
@ -941,13 +941,13 @@ deleteAccount: "ลบบัญชี"
document: "เอกสาร"
numberOfPageCache: "จำนวนหน้าเพจที่แคช"
numberOfPageCacheDescription: "การเพิ่มจำนวนนี้จะช่วยเพิ่มความสะดวกให้กับผู้ใช้งาน แต่จะทำให้เซิร์ฟเวอร์โหลดมากขึ้นและต้องใช้หน่วยความจำมากขึ้นอีกด้วย"
logoutConfirm: "ต้องการออกจากระบบ?"
lastActiveDate: "ใช้งานล่าสุดที่"
logoutConfirm: "ต้องการออกจากระบบใช่ไหม?"
lastActiveDate: "ใช้งานล่าสุดเมื่อ"
statusbar: "แถบสถานะ"
pleaseSelect: "ตัวเลือก"
reverse: "ย้อนกลับ"
reverse: "พลิก"
colored: "สี"
refreshInterval: "รอบการอัพเดต"
refreshInterval: "ความถี่ในการอัปเดต"
label: "ป้ายชื่อ"
type: "รูปแบบ"
speed: "ความเร็ว"
@ -974,8 +974,8 @@ unsubscribePushNotification: "ปิดการแจ้งเตือนแ
pushNotificationAlreadySubscribed: "การแจ้งเตือนแบบพุชได้เปิดใช้งานแล้ว"
pushNotificationNotSupported: "เบราว์เซอร์หรืออินสแตนซ์ของคุณนั้นไม่รองรับการแจ้งเตือนแบบพุช"
sendPushNotificationReadMessage: "ลบการแจ้งเตือนแบบพุชเมื่ออ่านการแจ้งเตือนหรือข้อความที่เกี่ยวข้องแล้ว"
sendPushNotificationReadMessageCaption: "การแจ้งเตือนที่มีข้อความ \"{emptyPushNotificationMessage}\" จะแสดงขึ้นมาในช่วงระยะเวลาสั้นๆ การดำเนินการนี้อาจทำให้เพิ่มการใช้งานแบตเตอรี่ของอุปกรณ์ถ้าหากมีนะ"
windowMaximize: "ขยายใหญ่สุดแล้ว"
sendPushNotificationReadMessageCaption: "อาจทำให้อุปกรณ์ของคุณใช้พลังงานมากขึ้น"
windowMaximize: "ขยายใหญ่สุด"
windowMinimize: "ย่อเล็กที่สุด"
windowRestore: "เลิกทำ"
caption: "คำอธิบาย"
@ -991,6 +991,7 @@ neverShow: "ไม่ต้องแสดงข้อความนี้อ
remindMeLater: "ไว้ครั้งหน้าแล้วกัน"
didYouLikeMisskey: "คุณชอบ Misskey ไหม?"
pleaseDonate: "Misskey เป็นซอฟต์แวร์ฟรีที่ใช้งานโดย {host} เราขอขอบคุณการสนับสนุนของคุณอย่างสูงเพื่อให้การพัฒนา Misskey สามารถดำเนินต่อไปได้!"
correspondingSourceIsAvailable: "ซอร์สโค้ดที่เกี่ยวข้องมีอยู่ที่ {anchor}"
roles: "บทบาท"
role: "บทบาท"
noRole: "ไม่พบบทบาท"
@ -1059,7 +1060,7 @@ enableChartsForFederatedInstances: "สร้างแผนภูมิข้
showClipButtonInNoteFooter: "เพิ่ม “คลิป” ไปยังเมนูสั่งการของโน้ต"
reactionsDisplaySize: "ขนาดของรีแอคชั่น"
limitWidthOfReaction: "จำกัดความกว้างสูงสุดของรีแอคชั่นและแสดงให้เล็กลง"
noteIdOrUrl: "โน้ต ID หรือ URL"
noteIdOrUrl: "ID ของโน้ต หรือ URL"
video: "วีดีโอ"
videos: "วีดีโอ"
audio: "เสียง"
@ -1081,7 +1082,7 @@ leftBottom: "ล่างซ้าย"
rightBottom: "ล่างขวา"
stackAxis: "ทิศทางการซ้อน"
vertical: "แนวตั้ง"
horizontal: "ด้านข้าง"
horizontal: "แนวนอน"
position: "ตำแหน่ง"
serverRules: "กฎของเซิร์ฟเวอร์"
pleaseConfirmBelowBeforeSignup: "โปรดยืนยันที่ด้านล่างก่อนสมัครใช้งาน"
@ -1097,17 +1098,17 @@ thisChannelArchived: "ช่องนี้ถูกเก็บถาวรแ
displayOfNote: "การแสดงโน้ต"
initialAccountSetting: "ตั้งค่าโปรไฟล์"
youFollowing: "ติดตามแล้ว"
preventAiLearning: "ปฏิเสธการใช้งาน ในการเรียนรู้ของเครื่อง (Generative AI)"
preventAiLearningDescription: "การส่งคำร้องขอโปรแกรมรวบรวมข้อมูลไม่ให้ใช้ข้อความที่โพสต์หรือรูปภาพ ฯลฯ ในชุดข้อมูลแมชชีนเลิร์นนิง (Predictive / Generative AI) สิ่งนี้นั้นทำได้โดยการเพิ่มแฟล็กการตอบสนอง \"noai\" HTML ให้กับเนื้อหาที่เกี่ยวข้อง แต่อย่างไรก็ตามแล้ว การป้องกันโดยสมบูรณ์นั้นไม่สามารถทำได้ผ่านแฟล็กนี้เนื่องจากอาจจะทำให้ถูกเพิกเฉยได้"
preventAiLearning: "ปฏิเสธการเรียนรู้ด้วย generative AI"
preventAiLearningDescription: "ส่งคำร้องขอไม่ให้ใช้ ข้อความในโน้ตที่โพสต์, หรือเนื้อหารูปภาพ ฯลฯ ในการเรียนรู้ของเครื่อง(machine learning) / Predictive AI / Generative AI โดยการเพิ่มแฟล็ก “noai” ลง HTML-Response ให้กับเนื้อหาที่เกี่ยวข้อง แต่ทั้งนี้ ไม่ได้ป้องกัน AI จากการเรียนรู้ได้อย่างสมบูรณ์ เนื่องจากมี AI บางตัวเท่านั้นที่จะเคารพคำขอดังกล่าว"
options: "ตัวเลือกบทบาท"
specifyUser: "ผู้ใช้เฉพาะ"
failedToPreviewUrl: "ไม่สามารถดูตัวอย่างได้"
update: "อัปเดต"
rolesThatCanBeUsedThisEmojiAsReaction: "บทบาทที่สามารถใช้เอโมจินี้เป็นรีแอคชั่นได้"
rolesThatCanBeUsedThisEmojiAsReactionEmptyDescription: "ถ้าหากไม่ได้ระบุบทบาท ทุกคนนั้นก็สามารถใช้เอโมจินี้เพื่อรีแอคชั่นได้นะ"
rolesThatCanBeUsedThisEmojiAsReactionEmptyDescription: "ถ้าหากไม่ได้ระบุบทบาท ใคร ๆ ก็สามารถใช้เอโมจินี้เพื่อรีแอคชั่นได้"
rolesThatCanBeUsedThisEmojiAsReactionPublicRoleWarn: "บทบาทเหล่านี้ต้องเป็นสาธารณะ"
cancelReactionConfirm: "ต้องการลบรีแอคชั่นของคุณจริงๆหรอ?"
changeReactionConfirm: "ต้องการเปลี่ยนรีแอคชั่นของคุณจริงๆหรอ?"
cancelReactionConfirm: "ต้องการลบรีแอคชั่นใช่ไหม?"
changeReactionConfirm: "ต้องการเปลี่ยนรีแอคชั่นใช่ไหม?"
later: "ไว้ทีหลัง"
goToMisskey: "ถึง Misskey"
additionalEmojiDictionary: "พจนานุกรมเอโมจิเพิ่มเติม"
@ -1116,20 +1117,20 @@ branding: "แบรนดิ้ง"
enableServerMachineStats: "เผยแพร่สถานะฮาร์ดแวร์ของเซิร์ฟเวอร์"
enableIdenticonGeneration: "เปิดใช้งานผู้ใช้สร้างตัวระบุ"
turnOffToImprovePerformance: "การปิดส่วนนี้สามารถเพิ่มประสิทธิภาพได้"
createInviteCode: "สร้างคำเชิญ"
createInviteCode: "สร้างรหัสเชิญ"
createWithOptions: "สร้างด้วยตัวเลือก"
createCount: "จำนวนการเชิญ"
inviteCodeCreated: "สร้างคำเชิญแล้ว"
inviteLimitExceeded: "คุณสร้างคำเชิญเกินถึงขีดจำกัดแล้วนะ"
createLimitRemaining: "ขีดจำกัดการเชิญ: {limit} ที่เหลืออยู่"
inviteLimitResetCycle: "ขีดจำกัดนี้จะถูกรีเซ็ตเป็น {limit} ที่ {time}."
createCount: "จำนวนหัสเชิญ"
inviteCodeCreated: "สร้างรหัสเชิญแล้ว"
inviteLimitExceeded: "จำนวนรหัสเชิญที่สามารถสร้างได้ถึงขีดจำกัดแล้ว"
createLimitRemaining: "รหัสเชิญที่สามารถสร้างได้: เหลืออยู่ {limit} รหัส"
inviteLimitResetCycle: "สามารถสร้างรหัสเชิญได้อีกสูงสุด {limit} รหัส ภายใน {time}"
expirationDate: "วันที่หมดอายุ"
noExpirationDate: "ไม่มีหมดอายุ"
inviteCodeUsedAt: "รหัสคำเชิญใช้แล้วที่"
registeredUserUsingInviteCode: "ใช้คำเชิญแล้วโดย"
inviteCodeUsedAt: "วันเวลาที่ใช้รหัสเชิญ"
registeredUserUsingInviteCode: "ผู้ใช้ที่ใช้รหัสเชิญ"
waitingForMailAuth: "กำลังรอการยืนยันอีเมล"
inviteCodeCreator: "สร้างการเชิญแล้วโดย"
usedAt: "ใช้แล้วที่"
inviteCodeCreator: "ผู้ใช้ที่สร้างรหัสเชิญ"
usedAt: "ันเวลาที่ถูกใช้"
unused: "ยังไม่ได้ใช้"
used: "ถูกใช้แล้ว"
expired: "หมดอายุแล้ว"
@ -1148,7 +1149,7 @@ renotes: "รีโน้ต"
loadReplies: "แสดงการตอบกลับ"
loadConversation: "แสดงบทสนทนา"
pinnedList: "รายชื่อที่ปักหมุดไว้"
keepScreenOn: "เปิดหน้าจอไว้"
keepScreenOn: "เปิดหน้าจออุปกรณ์ค้างไว้"
verifiedLink: "ความเป็นเจ้าของลิงก์ได้รับการยืนยันแล้ว"
notifyNotes: "แจ้งเตือนเกี่ยวกับโพสต์ใหม่"
unnotifyNotes: "หยุดการแจ้งเตือนเกี่ยวกับโน้ตใหม่"
@ -1168,6 +1169,12 @@ confirmShowRepliesAll: "การดำเนินการนี้ไม่
confirmHideRepliesAll: "การดำเนินการนี้ไม่สามารถย้อนกลับได้ คุณต้องการซ่อนการตอบกลับผู้อื่นจากผู้ใช้ทุกคนที่คุณติดตามอยู่ในไทม์ไลน์ของคุณหรือไม่?"
externalServices: "บริการภายนอก"
sourceCode: "ซอร์สโค้ด"
sourceCodeIsNotYetProvided: "ซอร์สโค้ดยังไม่พร้อมใช้งาน โปรดติดต่อผู้ดูแลระบบของคุณเพื่อแก้ไขปัญหานี้"
repositoryUrl: "URL ของ repository"
repositoryUrlDescription: "หากมีที่เก็บซอร์สโค้ดที่เปิดเผยต่อสาธารณะ ให้ป้อน URL ที่เก็บซอร์สโค้ดนั้น แต่หากคุณใช้ Misskey ตามต้นฉบับ (ไม่มีการเปลี่ยนแปลงซอร์สโค้ด) ให้ป้อน https://github.com/misskey-dev/misskey"
repositoryUrlOrTarballRequired: "หากคุณไม่มี repository สาธารณะ คุณจะต้องจัดเตรียม tarball แทน ดู .config/example.yml สำหรับรายละเอียด"
feedback: "ฟีดแบ็ก"
feedbackUrl: "URLของฟีดแบ็ก"
impressum: "อิมเพรสชั่น"
impressumUrl: "URL อิมเพรสชั่น"
impressumDescription: "การติดป้ายกำกับ (Impressum) มีผลบังคับใช้ในบางประเทศและภูมิภาค เช่น ประเทศเยอรมนี"
@ -1179,7 +1186,7 @@ attach: "แนบ"
detach: "นำออก"
detachAll: "เอาออกทั้งหมด"
angle: "แองเกิล"
flip: "ย้อนกลับ"
flip: "พลิก"
showAvatarDecorations: "แสดงตกแต่งอวตาร"
releaseToRefresh: "ปล่อยเพื่อรีเฟรช"
refreshing: "กำลังรีเฟรช..."
@ -1203,6 +1210,8 @@ soundWillBePlayed: "จะมีการเล่นเอฟเฟกต์เ
showReplay: "ดูรีเพลย์"
replay: "รีเพลย์"
replaying: "กำลังรีเพลย์"
endReplay: "ออกจากรีเพลย์"
copyReplayData: "คัดลอกข้อมูลรีเพลย์"
ranking: "อันดับ"
lastNDays: "ล่าสุด {n} วันที่แล้ว"
backToTitle: "กลับไปหน้าไตเติ้ล"
@ -1210,8 +1219,20 @@ hemisphere: "พื้นที่ที่อาศัยอยู่"
withSensitive: "แสดงโน้ตที่มีไฟล์ที่ระบุว่ามีเนื้อหาละเอียดอ่อน"
userSaysSomethingSensitive: "โพสต์ที่มีไฟล์เนื้อหาละเอียดอ่อนของ {name}"
enableHorizontalSwipe: "ปัดเพื่อสลับแท็บ"
loading: "กำลังโหลด"
surrender: "ยอมแพ้"
gameRetry: "เริ่มเกมใหม่"
_bubbleGame:
howToPlay: "วิธีเล่น"
hold: "หยุดชั่วคราว"
_score:
score: "คะแนน"
scoreYen: "จำนวนเงินที่ได้รับ"
highScore: "คะแนนสูงสุด"
maxChain: "จำนวน chain สูงสุด"
yen: "{yen} เยน"
estimatedQty: "{qty} อัน"
scoreSweets: "โอนิงิริ {onigiriQtyWithUnit}"
_howToPlay:
section1: "ขยับตำแหน่งและวางวัตถุลงในกล่อง"
section2: "เมื่อวัตถุประเภทเดียวกันมารวมกัน พวกมันจะกลายเป็นวัตถุใหม่และคุณจะได้รับคะแนน"
@ -1219,16 +1240,16 @@ _bubbleGame:
_announcement:
forExistingUsers: "ผู้ใช้งานที่มีอยู่เท่านั้น"
forExistingUsersDescription: "การประกาศนี้จะแสดงต่อผู้ใช้ที่มีอยู่ ณ จุดที่เผยแพร่นั้นๆถ้าหากเปิดใช้งาน ถ้าหากปิดใช้งานผู้ที่กำลังสมัครใหม่หลังจากโพสต์แล้วนั้นก็จะเห็นเช่นกัน"
needConfirmationToRead: "จำเป็นต้องยืนยันเพื่อทำเครื่องหมายบอกว่าอ่านแล้ว"
needConfirmationToReadDescription: "ข้อความแจ้งแยก ถ้าหากต้องการเพื่อยืนยันว่ากำลังทำเครื่องหมายประกาศนี้ว่าอ่านแล้วจะแสดงขึ้นถ้าหากเปิดใช้งาน การประกาศนั้นจะไม่รวมอยู่ในฟังก์ชั่นว่า \"ทำเครื่องหมายทั้งหมดว่าอ่านแล้ว\""
needConfirmationToRead: "จำเป็นต้องยืนยันว่าอ่านแล้ว"
needConfirmationToReadDescription: "กล่องโต้ตอบการยืนยันจะปรากฏขึ้นเมื่อจะทำเครื่องหมายว่าอ่านแล้ว นอกจากนี้ยังทำให้ประกาศนี้ยังไม่ถูกอ่านเมื่อใช้ฟังก์ชั่น “ทำเครื่องหมายฯ ทั้งหมดว่าอ่านแล้ว”"
end: "เก็บประกาศ"
tooManyActiveAnnouncementDescription: "การมีประกาศที่ใช้งานมากเกินไปนั้นอาจจะทำให้ประสบการณ์ของผู้ใช้งานนั้นดูแย่ลง โปรดกรุณาพิจารณาการเก็บประกาศที่ล้าสมัยด้วยนะค่ะ"
readConfirmTitle: "ทำเครื่องหมายบอกว่าอ่านแล้วเลยมั้ย?"
readConfirmText: "การดำเนินการนี้จะทำเครื่องหมายเนื้อหาของ \"{title}\" บอกว่าอ่านแล้วนะ"
readConfirmTitle: "ทำเครื่องหมายว่าอ่านแล้วเลยไหม?"
readConfirmText: "จะทำเครื่องหมายใส่ “{title}” ว่าอ่านแล้ว"
shouldNotBeUsedToPresentPermanentInfo: "เราขอแนะนำให้ใช้ประกาศเพื่อโพสต์ข้อมูลแบบ flow มากกว่าข้อมูลแบบ stock เนื่องจากมีแนวโน้มที่จะส่งผลเสียต่อ UX โดยเฉพาะสำหรับผู้ใช้ใหม่"
dialogAnnouncementUxWarn: "เราขอแนะนำให้ใช้ด้วยความระมัดระวัง เนื่องจากการแจ้งเตือนแบบกล่องโต้ตอบตั้งแต่ 2 รายการขึ้นไปพร้อมกันอาจส่งผลเสียต่อ UX ได้อย่างมาก"
silence: "ไม่มีการแจ้งเตือน"
silenceDescription: "หากเปิดใช้งาน จะไม่ได้แจ้งเตือนประกาศนี้ และผู้ใช้จะไม่จำเป็นต้องอ่าน"
silenceDescription: "หากเปิดใช้งาน จะไม่มีการแจ้งเตือนประกาศนี้ และผู้ใช้จะไม่จำเป็นต้องทำเครื่องหมายว่าอ่านแล้ว"
_initialAccountSetting:
accountCreated: "คุณได้สร้างบัญชีของคุณสำเร็จเรียบร้อยแล้ว!"
letsStartAccountSetup: "สำหรับผู้เริ่มต้นมาตั้งค่าโปรไฟล์ของคุณกันเถอะ"
@ -1315,7 +1336,7 @@ _timelineDescription:
_serverRules:
description: "ชุดของกฎที่จะแสดงก่อนการลงทะเบียนเราขอแนะนำให้ตั้งค่าสรุปข้อกำหนดในการให้บริการ"
_serverSettings:
iconUrl: "ไอคอน URL"
iconUrl: "URL ไอคอน"
appIconDescription: "ระบุไอคอนที่จะใช้เมื่อ {host} แสดงเป็นแอป"
appIconUsageExample: "E.g. เป็น PWA หรือเมื่อแสดงผลเป็นบุ๊กมาร์กหน้าจอหลักบนโทรศัพท์"
appIconStyleRecommendation: "เนื่องจากไอคอนอาจถูกครอบตัดเป็นสี่เหลี่ยมจัตุรัสหรือวงกลม จึงแนะนำให้ใช้ไอคอนที่มีขอบสีรอบๆ เนื้อหา"
@ -1603,7 +1624,7 @@ _role:
assignTarget: "มอบหมาย"
descriptionOfAssignTarget: "แบบ<b>ปรับเอง</b> เพิ่มถอนบทบาทนี้แก่ผู้ใช้ด้วยตัวเอง\nแบบ<b>มีเงื่อนไข</b> เพิ่มถอนบทบาทนี้แก่ผู้ใช้โดยอัตโนมัติหากเข้าเงื่อนไขใดต่อไปนี้"
manual: "ปรับเอง"
manualRoles: "บทบาทแบบทำเอง"
manualRoles: "บทบาทแบบทำมือ"
conditional: "มีเงื่อนไข"
conditionalRoles: "บทบาทแบบมีเงื่อนไข"
condition: "เงื่อนไข"
@ -1615,13 +1636,13 @@ _role:
baseRole: "เทมเพลตบทบาท"
useBaseValue: "ใช้ตามเทมเพลตบทบาท"
chooseRoleToAssign: "เลือกบทบาทที่ต้องการกำหนด"
iconUrl: "ไอคอน URL"
iconUrl: "URL ไอคอน"
asBadge: "แสดงเป็นตรา"
descriptionOfAsBadge: "เมื่อเปิดใช้งาน ไอคอนบทบาทจะปรากฏถัดจากชื่อผู้ใช้"
isExplorable: "ค้นหาผู้ใช้ได้ง่ายขึ้นโดยดูจากบทบาท"
descriptionOfIsExplorable: "เมื่อเปิดใช้งาน ไทมไลน์บทบาทนี้และสมาชิกที่มีบทบาทนี้จะเปิดเผยเป็นสาธารณะ"
displayOrder: "ตำแหน่ง"
descriptionOfDisplayOrder: "ยิ่งตัวเลขสูง ตำแหน่ง UI ก็ยิ่งสูงขึ้นนะ"
displayOrder: "ลำดับการแสดงผล"
descriptionOfDisplayOrder: "เลขที่สูงกว่าจะแสดงบน UI ก่อน"
canEditMembersByModerator: "อนุญาตให้ผู้ควบคุมแก้ไขสมาชิก"
descriptionOfCanEditMembersByModerator: "เมื่อเปิดใช้ นอกเหนือจากผู้ควบคุมและผู้ดูแลระบบแล้ว จะสามารถเพิ่มถอนบทบาทนี้แก่ผู้ใช้ได้ แต่เมื่อปิดใช้ จะมีเฉพาะผู้ดูแลระบบเท่านั้นที่จะสามารถดำเนินการได้"
priority: "ลำดับความสำคัญ"
@ -1656,6 +1677,7 @@ _role:
canUseTranslator: "การใช้งานแปล"
avatarDecorationLimit: "จำนวนการตกแต่งไอคอนสูงสุดที่สามารถติดตั้งได้"
_condition:
roleAssignedTo: "มอบหมายให้มีบทบาทแบบทำมือ"
isLocal: "ผู้ใช้ในพื้นที่"
isRemote: "ผู้ใช้ระยะไกล"
createdLessThan: "สร้างน้อยกว่า"
@ -1685,13 +1707,13 @@ _emailUnavailable:
smtp: "เซิร์ฟเวอร์อีเมลนี้ไม่มีการตอบสนอง"
banned: "คุณไม่สามารถลงทะเบียนด้วยที่อยู่อีเมลนี้ได้"
_ffVisibility:
public: "เผยแพร่"
public: "สาธารณะ"
followers: "ปรากฏให้แก่ผู้ติดตามเท่านั้น"
private: "ส่วนตัว"
_signup:
almostThere: "เกือบจะเสร็จแล้ว"
emailAddressInfo: "กรุณากรอกที่อยู่อีเมลที่คุณใช้ ที่อยู่อีเมลของคุณจะไม่ถูกเผยแพร่สู่สาธารณชน"
emailSent: "เราได้ส่งอีเมลยืนยันไปยังที่อยู่อีเมลของคุณแล้วนะ ({email}) โปรดคลิกลิงก์ที่รวมไว้เพื่อสร้างบัญชีให้เสร็จสิ้น"
emailSent: "อีเมลยืนยันได้ถูกส่งไปยังที่อยู่อีเมลที่คุณป้อน ({email}) แล้ว กรุณาติดตามลิงก์ในอีเมลเพื่อสร้างบัญชีให้เสร็จสมบูรณ์ ลงก์ที่ใหไว้จะหมดอายุใ 30 นาที"
_accountDelete:
accountDelete: "ลบบัญชีผู้ใช้"
mayTakeTime: "เนื่องจากการลบบัญชีนี้จะเป็นกระบวนการที่ต้องใช้ทรัพยากรมาก จึงอาจจะต้องใช้เวลาสักครู่ถึงจะเสร็จสมบูรณ์ ทั้งนี้ขึ้นอยู่กับจำนวนเนื้อหาที่คุณสร้างและจำนวนไฟล์ที่คุณอัปโหลดนะ"
@ -1729,7 +1751,7 @@ _plugin:
viewSource: "ดูต้นฉบับ"
_preferencesBackups:
list: "สร้างการสำรองข้อมูล"
saveNew: "บันทึกใหม่"
saveNew: "บันทึกข้อมูลสำรองใหม่"
loadFile: "โหลดจากไฟล์"
apply: "นำไปใช้กับอุปกรณ์นี้"
save: "บันทึก"
@ -1739,8 +1761,8 @@ _preferencesBackups:
applyConfirm: "คุณต้องการใช้ข้อมูลสำรอง \"{name}\" กับอุปกรณ์นี้อย่างงั้นจริงหรอ การตั้งค่าที่มีอยู่ของอุปกรณ์นี้จะถูกเขียนทับนะ"
saveConfirm: "บันทึกข้อมูลสำรองเป็น {name} มั้ย?"
deleteConfirm: "ลบข้อมูลสำรอง {name} มั้ย?"
renameConfirm: "เปลี่ยนชื่อข้อมูลสำรองนี้จาก \"{old}\" เป็น \"{new}\" หรือไม่?"
noBackups: "ไม่มีข้อมูลสำรองนะ คุณสามารถสำรองข้อมูลการตั้งค่าไคลเอนต์ของคุณบนเซิร์ฟเวอร์นี้โดยใช้ \"สร้างการสำรองข้อมูลใหม่\"ได้นะ"
renameConfirm: "ต้องการเปลี่ยนชื่อข้อมูลสำรองจาก “{old}” เป็น “{new}” ใช่ไหม?"
noBackups: "ไม่มีข้อมูลสำรอง สามารถบันทึกการตั้งค่าไคลเอนต์ปัจจุบันไปยังเซิร์ฟเวอร์ด้วย “บันทึกข้อมูลสำรองใหม่”"
createdAt: "สร้างเมื่อ: {date} {time}"
updatedAt: "อัปเดตเมื่อ: {date} {time}"
cannotLoad: "การโหลดล้มเหลว"
@ -1756,10 +1778,12 @@ _aboutMisskey:
contributors: "ผู้สนับสนุนหลัก"
allContributors: "ผู้มีส่วนร่วมทั้งหมด"
source: "ซอร์สโค้ด"
original: "ต้นฉบับ"
thisIsModifiedVersion: "{name} ใช้ Misskey เวอร์ชันดัดแปลง"
translation: "แปลภาษา Misskey"
donate: "บริจาคให้กับ Misskey"
morePatrons: " ขอบคุณทุกท่านที่ร่วมกันช่วยเหลือตลอดมานะคะ 🥰"
patrons: "สมาชิกพันธมิตร"
patrons: "ผู้อุปถัมภ์"
projectMembers: "สมาชิกในโครงการ"
_displayOfSensitiveMedia:
respect: "ซ่อนสื่อที่ทำเครื่องหมายว่ามีเนื้อหาละเอียดอ่อน"
@ -1831,8 +1855,8 @@ _theme:
importInfo: "ถ้าหากต้องการป้อนโค้ดที่นี่ คุณยังสามารถนำเข้าไปยังโปรแกรมแก้ไขธีมได้"
deleteConstantConfirm: "คุณต้องการลบค่าคงที่ {const} หรือป่าว?"
keys:
accent: "เน้น"
bg: "ภาพพื้นหลัง"
accent: "สีหลัก"
bg: "พื้นหลัง"
fg: "ข้อความ"
focus: "โฟกัส"
indicator: "ตัวบ่งชี้"
@ -1868,11 +1892,11 @@ _theme:
wallpaperOverlay: "วอลล์เปเปอร์ซ้อนทับ"
badge: "ตรา"
messageBg: "พื้นหลังแชท"
accentDarken: "เน้น (มืด)"
accentLighten: "เน้น (สว่าง)"
accentDarken: "สีหลัก (มืด)"
accentLighten: "สีหลัก (สว่าง)"
fgHighlighted: "ข้อความที่ไฮไลต์"
_sfx:
note: "หมายเหตุ"
note: "โน้ต"
noteMy: "โน้ตของตัวเอง"
notification: "การเเจ้งเตือน"
antenna: "เสาอากาศ"
@ -1959,7 +1983,7 @@ _permissions:
"read:reactions": "ดูรีแอคชั่นของคุณ"
"write:reactions": "แก้ไขรีแอคชั่นของคุณ"
"write:votes": "โหวตบนสำรวจความคิดเห็น"
"read:pages": "ดหน้าเพจ"
"read:pages": "ดหน้าเพจ"
"write:pages": "แก้ไขหรือลบเพจของคุณ"
"read:page-likes": "ดูรายการเพจที่ถูกใจไว้"
"write:page-likes": "แก้ไขรายการเพจที่ถูกใจ"
@ -1971,8 +1995,8 @@ _permissions:
"write:gallery": "แก้ไขแกลเลอรี่ของคุณ"
"read:gallery-likes": "ดูรายการโพสต์แกลเลอรีที่ถูกใจไว้"
"write:gallery-likes": "แก้ไขรายการโพสต์แกลเลอรีที่ถูกใจไว้"
"read:flash": "วิว เพลย์"
"write:flash": "แก้ไขเพลย์"
"read:flash": "ดู Play"
"write:flash": "แก้ไข Play"
"read:flash-likes": "ดูรายการ play ที่ถูกใจไว้"
"write:flash-likes": "แก้ไขรายการ play ที่ถูกใจไว้"
"read:admin:abuse-user-reports": "ดูรายงานจากผู้ใช้"
@ -1999,8 +2023,8 @@ _permissions:
"read:admin:roles": "ดูบทบาท"
"write:admin:relays": "จัดการรีเลย์"
"read:admin:relays": "ดูรีเลย์"
"write:admin:invite-codes": "จัดการคำเชิญ"
"read:admin:invite-codes": "ดูรหัสคำเชิญ"
"write:admin:invite-codes": "จัดการรหัสเชิญ"
"read:admin:invite-codes": "ดูรหัสเชิญ"
"write:admin:announcements": "จัดการประกาศ"
"read:admin:announcements": "ดูประกาศ"
"write:admin:avatar-decorations": "จัดการการตกแต่งอวตาร"
@ -2018,7 +2042,7 @@ _permissions:
"read:admin:stream": "ใช้ Websocket API สำหรับผู้ดูแลระบบ"
"write:admin:ad": "จัดการโฆษณา"
"read:admin:ad": "ดูโฆษณา"
"write:invite-codes": "สร้างรหัสคำเชิญ"
"write:invite-codes": "สร้างรหัสเชิญ"
"read:invite-codes": "รับรหัสเชิญ"
"write:clip-favorite": "ควบคุมการถูกใจของคลิป"
"read:clip-favorite": "ดูการถูกใจของคลิป"
@ -2071,8 +2095,8 @@ _widgets:
onlineUsers: "ผู้ใช้ที่ออนไลน์"
jobQueue: "คิวงาน"
serverMetric: "ตัวชี้วัดเซิร์ฟเวอร์"
aiscript: "AiScript คอนโซล"
aiscriptApp: "AiScript แอพ"
aiscript: " คอนโซล AiScript"
aiscriptApp: "แอป AiScript"
aichan: "ไอ"
userList: "รายชื่อผู้ใช้"
_userList:
@ -2086,15 +2110,15 @@ _cw:
files: "{count} ไฟล์"
_poll:
noOnlyOneChoice: "จำเป็นต้องมีอย่างน้อยสองตัวเลือก"
choiceN: "ตัวเลือก {n}"
noMore: "คุณไม่สามารถเพิ่มตัวเลือกอื่นได้"
choiceN: "ตัวเลือกที่ {n}"
noMore: "เพิ่มตัวเลือกอีกไม่ได้แล้ว"
canMultipleVote: "สามารถตอบได้หลายคำตอบ"
expiration: "สิ้นสุดการสำรวจความคิดเห็น"
infinite: "ไม่ต้องเลย"
at: "จบที่..."
after: "สิ้นสุดหลัง..."
expiration: "สิ้นสุดโพล"
infinite: "ไม่กำหนดระยะเวลา"
at: "ระบุวันเวลา"
after: "ระบุระยะเวลา"
deadlineDate: "วันสิ้นสุด"
deadlineTime: "ชั่วโมง"
deadlineTime: "เวลา"
duration: "ระยะเวลา"
votesCount: "{n} คะแนนเสียง"
totalVotes: "{n} คะแนนเสียงทั้งหมด"
@ -2102,17 +2126,17 @@ _poll:
showResult: "ดูผลลัพธ์"
voted: "โหวตแล้ว"
closed: "สิ้นสุดแล้ว"
remainingDays: "จะเสร็จสิ้นในอีก {d} วัน {h} ชั่วโมง"
remainingHours: "{h} ชั่วโมง(s) {m} นาที(s) ที่เหลืออยู่"
remainingMinutes: "{m} นาที(s) {s} วินาที(s) ที่เหลืออยู่"
remainingSeconds: "{s} นาที(s) ที่เหลืออยู่"
remainingDays: "เหลืออีก {d} วัน {h} ชั่วโมง"
remainingHours: "เหลืออีก {h} ชั่วโมง {m} นาที"
remainingMinutes: "เหลืออีก {m} นาที {s} วินาที"
remainingSeconds: "เหลืออีก {s} วินาที"
_visibility:
public: "สาธารณะ"
publicDescription: "โน้ตของคุณจะปรากฏแก่ผู้ใช้ทุกคน"
home: "หน้าแรก"
homeDescription: "โพสลงไทม์ไลน์ที่บ้านเท่านั้น"
followers: "ผู้ติดตาม"
followersDescription: "ทำให้ผู้ติดตามนั้นมองเห็นแค่คุณเท่านั้น"
followersDescription: "เฉพาะผู้ติดตามเท่านั้นที่มองเห็นได้"
specified: "ไดเร็ค"
specifiedDescription: "ทำให้มองเห็นได้เฉพาะผู้ใช้ที่ระบุเท่านั้น"
disableFederation: "ไม่มีสหพันธ์"
@ -2122,11 +2146,11 @@ _postForm:
quotePlaceholder: "อ้างโน้ตนี้..."
channelPlaceholder: "โพสต์ลงช่อง..."
_placeholders:
a: "คุณเป็นอะไรไปหรอ?"
b: "เกิดอะไรขึ้นรอบตัวคุณ?"
c: "คุณกำลังคิดอะไรอยู่?"
d: "คุณต้องการจะพูดอะไร?"
e: "เริ่มเขียน..."
a: "ตอนนี้เป็นยังไงบ้าง?"
b: "มีอะไรเกิดขึ้นหรือเปล่า?"
c: "กำลังคิดอะไรอยู่?"
d: "ต้องการจะพูดอะไรไหม?"
e: "มาเขียนกันเถอะ"
f: "กำลังรอให้คุณเขียน..."
_profile:
name: "ชื่อ"
@ -2140,11 +2164,11 @@ _profile:
metadataContent: "เนื้อหา"
changeAvatar: "เปลี่ยนอวาตาร์"
changeBanner: "เปลี่ยนแบนเนอร์"
verifiedLinkDescription: "โดยการป้อน URL ที่มีลิงก์ไปยังโปรไฟล์ของคุณตรงนี้ ส่วนไอคอนการยืนยันความเป็นเจ้าของนั้นก็สามารถแสดงถัดจากฟิลด์ได้นะ"
verifiedLinkDescription: "หากป้อน URL ที่มีลิงก์ไปยังโปรไฟล์ของคุณ ไอคอนการยืนยันความเป็นเจ้าของจะแสดงถัดจากฟิลด์นั้น ๆ"
avatarDecorationMax: "คุณสามารถเพิ่มการตกแต่งได้สูงสุด {max}"
_exportOrImport:
allNotes: "โน้ตทั้งหมด"
favoritedNotes: "บันทึกที่ชื่นชอบ"
favoritedNotes: "โน้ตที่ถูกใจไว้"
clips: "คลิป"
followingList: "กำลังติดตาม"
muteList: "ปิดเสียง"
@ -2253,26 +2277,26 @@ _relayStatus:
accepted: "ได้รับการอนุมัติ"
rejected: "ถูกปฏิเสธ"
_notification:
fileUploaded: "ไฟล์ถูกอัพโหลดแล้วน่ะ"
fileUploaded: "ไฟล์ถูกอัปโหลดแล้ว"
youGotMention: "{name} กล่าวถึงคุณ"
youGotReply: "{name} ตอบกลับถึงคุณ"
youGotQuote: "{name} อ้างถึงคุณ"
youGotQuote: "{name} อ้างอิงคุณ"
youRenoted: "รีโน้ตจาก {name}"
youWereFollowed: "ได้ติดตามคุณ"
youReceivedFollowRequest: "คุณมีคำขอติดตามใหม่น่ะ"
yourFollowRequestAccepted: "คำขอติดตามของคุณได้รับการยอมรับแล้วน่ะ"
pollEnded: "โพลสำรวจความคิดเห็นผลลัพธ์มีพร้อมใช้งาน"
youReceivedFollowRequest: "ได้รับคำขอติดตาม"
yourFollowRequestAccepted: "คำขอติดตามได้รับการอนุมัติแล้ว"
pollEnded: "ผลโพลออกมาแล้ว"
newNote: "โพสต์ใหม่"
unreadAntennaNote: "เสาอากาศ {name}"
roleAssigned: "ได้รับบทบาท"
emptyPushNotificationMessage: "การแจ้งเตือนแบบพุชได้รับการอัพเดทแล้ว"
emptyPushNotificationMessage: "อัปเดตการแจ้งเตือนแบบพุชแล้ว"
achievementEarned: "รับความสำเร็จ"
testNotification: "ทดสอบการแจ้งเตือน"
checkNotificationBehavior: "กดเพื่อดูลักษณะการแจ้งเตือน"
sendTestNotification: "ส่งทดสอบการแจ้งเตือน"
notificationWillBeDisplayedLikeThis: "การแจ้งเตือนมีลักษณะแบบนี้"
reactedBySomeUsers: "ถูกรีแอคชั่นโดยผู้ใช้ {n} ราย"
renotedBySomeUsers: "Renote จากผู้ใช้จำนวน {n} ราย"
renotedBySomeUsers: "รีโน้ตจากผู้ใช้ {n} ราย"
followedBySomeUsers: "มีผู้ติดตาม {n} ราย"
_types:
all: "ทั้งหมด"
@ -2283,9 +2307,9 @@ _notification:
renote: "รีโน้ต"
quote: "อ้างคำพูด"
reaction: "รีแอคชั่น"
pollEnded: "โพลนี้สิ้นสุดลงแล้ว"
receiveFollowRequest: "ได้รับคำขอติดตาม\n"
followRequestAccepted: "ยอมรับคำขอติดตาม"
pollEnded: "โพลสิ้นสุดแล้ว"
receiveFollowRequest: "ได้รับคำร้องขอติดตาม"
followRequestAccepted: "อนุมัติให้ติดตามแล้ว"
roleAssigned: "ให้บทบาท"
achievementEarned: "ปลดล็อกความสำเร็จแล้ว"
app: "การแจ้งเตือนจากแอปที่มีลิงก์"
@ -2322,7 +2346,7 @@ _deck:
list: "รายการ"
channel: "ช่อง"
mentions: "พูดถึง"
direct: "ไดเร็"
direct: "ไดเร็กต์"
roleTimeline: "บทบาทไทม์ไลน์"
_dialog:
charactersExceeded: "คุณกำลังมีตัวอักขระเกินขีดจำกัดสูงสุดแล้วนะ! ปัจจุบันอยู่ที่ {current} จาก {max}"
@ -2353,8 +2377,8 @@ _moderationLogTypes:
updateRole: "อัปเดตบทบาทแล้ว"
assignRole: "ได้รับมอบหมายบทบาท"
unassignRole: "ถอดออกจากบทบาทแล้ว"
suspend: "ถูกระงับ"
unsuspend: "เลิกถูกระงับ"
suspend: "ระงับ"
unsuspend: "เลิกระงับ"
addCustomEmoji: "เพิ่มเอโมจิที่กำหนดเองแล้ว"
updateCustomEmoji: "อัปเดตเอโมจิที่กำหนดเองแล้ว"
deleteCustomEmoji: "ลบเอโมจิที่กำหนดเองออกแล้ว"
@ -2369,12 +2393,13 @@ _moderationLogTypes:
deleteGlobalAnnouncement: "ลบประกาศทั่วโลกออกแล้ว"
deleteUserAnnouncement: "ลบประกาศผู้ใช้ออกแล้ว"
resetPassword: "รีเซ็ตรหัสผ่าน"
suspendRemoteInstance: "อินสแตนซ์ระยะไกลถูกระงับ"
unsuspendRemoteInstance: "อินสแตนซ์ระยะไกลเลิกการระงับ"
suspendRemoteInstance: "ระงับอินสแตนซ์ระยะไกล"
unsuspendRemoteInstance: "เลิกระงับอินสแตนซ์ระยะไกล"
updateRemoteInstanceNote: "อัปเดตโน้ตการกลั่นกรองของอินสแตนซ์ระยะไกลแล้ว"
markSensitiveDriveFile: "ทำเครื่องหมายไฟล์ว่ามีเนื้อหาละเอียดอ่อน"
unmarkSensitiveDriveFile: "ยกเลิกทำเครื่องหมายไฟล์ว่ามีเนื้อหาละเอียดอ่อน"
resolveAbuseReport: "รายงานได้รับการแก้ไขแล้ว"
createInvitation: "สร้างคำเชิญ"
createInvitation: "สร้างรหัสเชิญ"
createAd: "สร้างโฆษณาแล้ว"
deleteAd: "ลบโฆษณาออกแล้ว"
updateAd: "อัปเดตโฆษณาแล้ว"
@ -2491,6 +2516,8 @@ _reversi:
opponentHasSettingsChanged: "อีกฝ่ายเปลี่ยนการตั้งค่า"
allowIrregularRules: "อนุญาตกฎที่ไม่ปรกติ (โหมดฟรีทุกอย่าง)"
disallowIrregularRules: "ไม่อนุญาตกฎที่ไม่ปรกติ"
showBoardLabels: "แสดงหมายเลขแถว/คอลัมน์บนกระดาน"
useAvatarAsStone: "ใช้รูปอวตารเป็นหมาก"
_offlineScreen:
title: "ออฟไลน์ - ไม่สามารถเชื่อมต่อกับเซิร์ฟเวอร์ได้"
header: "ไม่สามารถเชื่อมต่อกับเซิร์ฟเวอร์ได้"

View File

@ -1048,6 +1048,7 @@ verifiedLink: "Chúng tôi đã xác nhận bạn là chủ sở hữu của đ
sourceCode: "Mã nguồn"
flip: "Lật"
lastNDays: "{n} ngày trước"
surrender: "Từ chối"
_announcement:
forExistingUsers: "Chỉ những người dùng đã tồn tại"
forExistingUsersDescription: "Nếu được bật, thông báo này sẽ chỉ hiển thị với những người dùng đã tồn tại vào lúc thông báo được tạo. Nếu tắt đi, những tài khoản mới đăng ký sau khi thông báo được đăng lên cũng sẽ thấy nó."

View File

@ -336,7 +336,7 @@ displayOfSensitiveMedia: "显示敏感媒体"
whenServerDisconnected: "与服务器连接中断时"
disconnectedFromServer: "已和服务器断开连接"
reload: "重新加载"
doNothing: "关闭弹窗"
doNothing: "关闭"
reloadConfirm: "确定要重新加载吗?"
watch: "关注"
unwatch: "取消关注"
@ -991,6 +991,7 @@ neverShow: "不再显示"
remindMeLater: "稍后提醒我"
didYouLikeMisskey: "您喜欢 Misskey 吗?"
pleaseDonate: "Misskey 是 {host} 所使用的免费软件。为了今后也能够维持 Misskey 的开发,请在有余力的情况下进行捐助!"
correspondingSourceIsAvailable: "对应的源代码可在{anchor}找到"
roles: "角色"
role: "角色"
noRole: "角色不存在"
@ -1042,6 +1043,7 @@ sensitiveWords: "敏感词"
sensitiveWordsDescription: "将包含设置词的帖子的可见范围设置为首页。可以通过用换行符分隔来设置多个。"
sensitiveWordsDescription2: "AND 条件用空格分隔,正则表达式用斜线包裹。"
prohibitedWords: "禁用词"
prohibitedWordsDescription: "发布包含设定词汇的帖子时将出错。可用换行设定多个关键字"
prohibitedWordsDescription2: "AND 条件用空格分隔,正则表达式用斜线包裹。"
hiddenTags: "隐藏标签"
hiddenTagsDescription: "设定的标签将不会在时间线上显示。可使用换行来设置多个标签。"
@ -1115,7 +1117,7 @@ branding: "品牌"
enableServerMachineStats: "公开服务器硬件统计信息"
enableIdenticonGeneration: "启用生成用户 Identicon"
turnOffToImprovePerformance: "关闭该选项可以提高性能。"
createInviteCode: "发行邀请码"
createInviteCode: "生成邀请码"
createWithOptions: "使用选项来创建"
createCount: "发行数"
inviteCodeCreated: "已创建邀请码"
@ -1127,7 +1129,7 @@ noExpirationDate: "不设置有效日期"
inviteCodeUsedAt: "邀请码被使用的日期和时间"
registeredUserUsingInviteCode: "使用了邀请码的用户"
waitingForMailAuth: "等待验证电子邮件"
inviteCodeCreator: "发行邀请码的用户"
inviteCodeCreator: "生成邀请码的用户"
usedAt: "使用时间"
unused: "未使用"
used: "已使用"
@ -1158,6 +1160,7 @@ showRenotes: "显示转帖"
edited: "已编辑"
notificationRecieveConfig: "通知接收设置"
mutualFollow: "互相关注"
followingOrFollower: "关注中或关注者"
fileAttachedOnly: "仅限媒体"
showRepliesToOthersInTimeline: "在时间线中包含给别人的回复"
hideRepliesToOthersInTimeline: "在时间线中隐藏给别人的回复"
@ -1167,6 +1170,12 @@ confirmShowRepliesAll: "此操作不可撤销。确认要在时间线中包含
confirmHideRepliesAll: "此操作不可撤销。确认要在时间线中隐藏现在关注的所有人的回复吗?"
externalServices: "外部服务"
sourceCode: "源代码"
sourceCodeIsNotYetProvided: "还未提供源代码。要解决此问题请联系管理员。"
repositoryUrl: "仓库地址"
repositoryUrlDescription: "若源代码所在的仓库是公开的,请填入对应的 URL。若是按原样使用 Misskey并未追加或者修改代码的情况请填入 https://github.com/misskey-dev/misskey。"
repositoryUrlOrTarballRequired: "若仓库并未公开,则需要提供 tarball 作为替代。详情请看 .config/example.yml。"
feedback: "反馈"
feedbackUrl: "反馈地址"
impressum: "运营商信息"
impressumUrl: "运营商信息地址"
impressumDescription: "德国等国家和地区有义务展示此类信息Impressum。"
@ -1196,11 +1205,14 @@ seasonalScreenEffect: "应景的画面效果"
decorate: "装饰"
addMfmFunction: "添加装饰"
enableQuickAddMfmFunction: "显示高级 MFM 选择器"
bubbleGame: "泡泡游戏"
sfx: "音效"
soundWillBePlayed: "声音将会播放"
showReplay: "查看重播"
showReplay: "观看回放"
replay: "重播"
replaying: "重播中"
endReplay: "结束回放"
copyReplayData: "复制回放数据"
ranking: "排行榜"
lastNDays: "最近 {n} 天"
backToTitle: "返回标题"
@ -1208,8 +1220,19 @@ hemisphere: "居住地区"
withSensitive: "显示包含敏感媒体的帖子"
userSaysSomethingSensitive: "含 {name} 敏感文件的帖子"
enableHorizontalSwipe: "滑动切换标签页"
loading: "读取中"
surrender: "取消"
gameRetry: "重试"
_bubbleGame:
howToPlay: "游戏说明"
hold: "抓住"
_score:
score: "得分"
scoreYen: "赚到的钱"
highScore: "最高分"
maxChain: "最高连击数"
yen: "{yen} 日元"
estimatedQty: "约 {qty} 个"
_howToPlay:
section1: "对准位置将Emoji投入盒子。"
section2: "相同的Emoji相互接触合成后会得到新的Emoji以此获得分数。"
@ -1298,8 +1321,8 @@ _initialTutorial:
description: "对于服务器方针所要求要求的,又或者不适合直接展示的附件,请添加「敏感」标记。\n"
tryThisFile: "试试看,将附加到此窗口的图像标注为敏感!"
_exampleNote:
note: "不该打开纳豆的盖子的……"
method: "要标注附件为敏感内容,请单击该文件以打开菜单,然后单击“设置为敏感”。"
note: "拆纳豆包装时出错了…"
method: "要标注附件为敏感内容,请单击该文件以打开菜单,然后单击“标记为敏感内容”。"
sensitiveSucceeded: "附加文件时,请遵循服务器的条款来设置正确敏感设定。\n"
doItToContinue: "将图像标记为敏感后才能够继续"
_done:
@ -1631,7 +1654,7 @@ _role:
ltlAvailable: "查看本地时间线"
canPublicNote: "允许公开发帖"
canInvite: "发放服务器邀请码"
inviteLimit: "可发行邀请码的数量"
inviteLimit: "可生成邀请码的数量"
inviteLimitCycle: "邀请码的发行间隔"
inviteExpirationTime: "邀请码的有效日期"
canManageCustomEmojis: "管理自定义表情符号"
@ -1653,6 +1676,7 @@ _role:
canUseTranslator: "使用翻译功能"
avatarDecorationLimit: "可添加头像挂件的最大个数"
_condition:
roleAssignedTo: "已分配给手动角色"
isLocal: "是本地用户"
isRemote: "是远程用户"
createdLessThan: "账户创建时间少于"
@ -1753,6 +1777,8 @@ _aboutMisskey:
contributors: "主要贡献者"
allContributors: "全体贡献者"
source: "源代码"
original: "原版"
thisIsModifiedVersion: "{name}正在使用修改后的 Misskey。"
translation: "翻译 Misskey"
donate: "赞助 Misskey"
morePatrons: "还有很多其它的人也在支持我们,非常感谢🥰"
@ -2015,7 +2041,7 @@ _permissions:
"read:admin:stream": "使用管理员用的 Websocket API"
"write:admin:ad": "编辑广告"
"read:admin:ad": "查看广告"
"write:invite-codes": "发行邀请码"
"write:invite-codes": "生成邀请码"
"read:invite-codes": "获取已发行的邀请码"
"write:clip-favorite": "编辑便签的点赞"
"read:clip-favorite": "查看便签的点赞"
@ -2368,10 +2394,11 @@ _moderationLogTypes:
resetPassword: "重置密码"
suspendRemoteInstance: "停止远程服务器"
unsuspendRemoteInstance: "恢复远程服务器"
updateRemoteInstanceNote: "更新远程服务器的管理笔记"
markSensitiveDriveFile: "标记网盘文件为敏感媒体"
unmarkSensitiveDriveFile: "取消标记网盘文件为敏感媒体"
resolveAbuseReport: "处理举报"
createInvitation: "发行邀请码"
createInvitation: "生成邀请码"
createAd: "创建了广告"
deleteAd: "删除了广告"
updateAd: "更新了广告"
@ -2462,6 +2489,8 @@ _reversi:
myTurn: "你的回合"
turnOf: "{name}的回合"
pastTurnOf: "{name}的回合"
surrender: "认输"
surrendered: "已认输"
timeout: "超时"
drawn: "平局"
won: "{name}获胜"
@ -2483,6 +2512,8 @@ _reversi:
opponentHasSettingsChanged: "对手更改了设定"
allowIrregularRules: "允许非常规规则(完全自由)"
disallowIrregularRules: "禁止非常规规则"
showBoardLabels: "显示行号和列号"
useAvatarAsStone: "用头像作为棋子"
_offlineScreen:
title: "离线——无法连接到服务器"
header: "无法连接到服务器"

View File

@ -991,6 +991,7 @@ neverShow: "不再顯示"
remindMeLater: "以後再說"
didYouLikeMisskey: "您喜歡 Misskey 嗎?"
pleaseDonate: "Misskey 是由 {host} 使用的免費軟體。請贊助我們,讓開發得以持續!"
correspondingSourceIsAvailable: "對應的原始碼可以在 {anchor} 處找到。"
roles: "角色"
role: "角色"
noRole: "沒有角色"
@ -1159,6 +1160,7 @@ showRenotes: "顯示其他人的轉發貼文"
edited: "已編輯"
notificationRecieveConfig: "接受通知的設定"
mutualFollow: "互相追隨"
followingOrFollower: "追隨中或追隨者"
fileAttachedOnly: "顯示包含附件的貼文"
showRepliesToOthersInTimeline: "顯示給其他人的回覆"
hideRepliesToOthersInTimeline: "在時間軸上隱藏給其他人的回覆"
@ -1168,6 +1170,12 @@ confirmShowRepliesAll: "進行此操作後無法復原。您真的希望時間
confirmHideRepliesAll: "進行此操作後無法復原。您真的希望時間軸「不包含」您目前追隨的所有人的回覆嗎?"
externalServices: "外部服務"
sourceCode: "原始碼"
sourceCodeIsNotYetProvided: "尚未提供原始碼,請洽詢管理員解決這個問題。"
repositoryUrl: "儲存庫 URL"
repositoryUrlDescription: "如果存在可公開取得原始碼的儲存庫,請輸入其 URL。 如果您按原樣使用 Misskey不對原始碼進行任何更改請輸入 https://github.com/misskey-dev/misskey。"
repositoryUrlOrTarballRequired: "如果儲存庫不是公開的,則必須提供 tarball。 詳細資訊請參閱 .config/example.yml。"
feedback: "意見回饋"
feedbackUrl: "意見回饋 URL"
impressum: "營運者資訊"
impressumUrl: "營運者資訊網址"
impressumDescription: "在德國與部份地區必須要明確顯示營運者資訊。"
@ -1203,6 +1211,8 @@ soundWillBePlayed: "將播放音效"
showReplay: "觀看重播"
replay: "重播"
replaying: "重播中"
endReplay: "退出重播"
copyReplayData: "複製重播資料"
ranking: "排行榜"
lastNDays: "過去 {n} 天"
backToTitle: "回到遊戲標題頁"
@ -1210,8 +1220,20 @@ hemisphere: "您居住的地區"
withSensitive: "顯示包含敏感檔案的貼文"
userSaysSomethingSensitive: "包含 {name} 敏感檔案的貼文"
enableHorizontalSwipe: "滑動切換時間軸"
loading: "載入中"
surrender: "退出"
gameRetry: "再試一次"
_bubbleGame:
howToPlay: "玩法說明"
hold: "保留"
_score:
score: "分數"
scoreYen: "賺取的金額"
highScore: "最高分"
maxChain: "最大結合數"
yen: "{yen} 日圓"
estimatedQty: "{qty}個"
scoreSweets: "飯糰 {onigiriQtyWithUnit}"
_howToPlay:
section1: "調整位置並將物體放入盒子中。"
section2: "當相同類型的物體黏在一起時,它們會變成不同的物體,您就會得到分數。"
@ -1615,7 +1637,7 @@ _role:
baseRole: "基本角色"
useBaseValue: "使用基本角色的值"
chooseRoleToAssign: "選擇要指派的角色"
iconUrl: "圖示的URL"
iconUrl: "圖示的 URL"
asBadge: "顯示為徽章"
descriptionOfAsBadge: "開啟的話,角色圖示會顯示在使用者名稱旁邊。"
isExplorable: "讓使用者更容易找到您"
@ -1656,6 +1678,7 @@ _role:
canUseTranslator: "使用翻譯功能"
avatarDecorationLimit: "頭像裝飾的最大設置量"
_condition:
roleAssignedTo: "手動指派角色完成"
isLocal: "本地使用者"
isRemote: "遠端使用者"
createdLessThan: "帳戶加入時間不超過"
@ -1756,6 +1779,8 @@ _aboutMisskey:
contributors: "主要貢獻者"
allContributors: "全體貢獻人員"
source: "原始碼"
original: "原始"
thisIsModifiedVersion: "{name} 使用原始 Misskey 的修改版本。"
translation: "翻譯 Misskey"
donate: "贊助 Misskey"
morePatrons: "還有許許多多幫助我們的其他人,非常感謝你們。 🥰"
@ -2359,7 +2384,7 @@ _moderationLogTypes:
updateCustomEmoji: "更新自訂表情符號"
deleteCustomEmoji: "刪除自訂表情符號"
updateServerSettings: "更新伺服器設定"
updateUserNote: "更新管理筆記"
updateUserNote: "更新了使用者的管理筆記"
deleteDriveFile: "刪除檔案"
deleteNote: "刪除貼文"
createGlobalAnnouncement: "建立全網通知"
@ -2371,6 +2396,7 @@ _moderationLogTypes:
resetPassword: "重設密碼"
suspendRemoteInstance: "封鎖遠端伺服器"
unsuspendRemoteInstance: "解除封鎖遠端伺服器"
updateRemoteInstanceNote: "更新了遠端伺服器的管理筆記"
markSensitiveDriveFile: "標記為敏感檔案"
unmarkSensitiveDriveFile: "撤銷標記為敏感檔案"
resolveAbuseReport: "解決檢舉"
@ -2491,6 +2517,8 @@ _reversi:
opponentHasSettingsChanged: "對手更改了設定"
allowIrregularRules: "允許異常規則(完全自由)"
disallowIrregularRules: "不允許異常規則"
showBoardLabels: "在棋盤上顯示行、列號"
useAvatarAsStone: "用大頭貼當作棋子"
_offlineScreen:
title: "離線-無法連接伺服器"
header: "無法連接伺服器"

View File

@ -48,21 +48,21 @@
"lodash": "4.17.21"
},
"dependencies": {
"cssnano": "6.0.3",
"cssnano": "6.0.5",
"execa": "8.0.1",
"fast-glob": "3.3.2",
"ignore-walk": "6.0.4",
"js-yaml": "4.1.0",
"postcss": "8.4.33",
"postcss": "8.4.35",
"tar": "6.2.0",
"terser": "5.27.0",
"terser": "5.28.1",
"typescript": "5.3.3"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "6.18.1",
"@typescript-eslint/parser": "6.18.1",
"cross-env": "7.0.3",
"cypress": "13.6.3",
"cypress": "13.6.6",
"eslint": "8.56.0",
"ncp": "2.0.0",
"start-server-and-test": "2.0.3"

View File

@ -67,9 +67,9 @@
"dependencies": {
"@aws-sdk/client-s3": "3.412.0",
"@aws-sdk/lib-storage": "3.412.0",
"@bull-board/api": "5.14.0",
"@bull-board/fastify": "5.14.0",
"@bull-board/ui": "5.14.0",
"@bull-board/api": "5.14.2",
"@bull-board/fastify": "5.14.2",
"@bull-board/ui": "5.14.2",
"@discordapp/twemoji": "15.0.2",
"@fastify/accepts": "4.3.0",
"@fastify/cookie": "9.3.1",
@ -79,13 +79,13 @@
"@fastify/multipart": "8.1.0",
"@fastify/static": "6.12.0",
"@fastify/view": "8.2.0",
"@misskey-dev/sharp-read-bmp": "^1.2.0",
"@misskey-dev/summaly": "^5.0.3",
"@misskey-dev/sharp-read-bmp": "1.2.0",
"@misskey-dev/summaly": "5.0.3",
"@nestjs/common": "10.2.10",
"@nestjs/core": "10.2.10",
"@nestjs/testing": "10.2.10",
"@peertube/http-signature": "1.7.0",
"@simplewebauthn/server": "9.0.2",
"@simplewebauthn/server": "9.0.3",
"@sinonjs/fake-timers": "11.2.2",
"@smithy/node-http-handler": "2.1.10",
"@swc/cli": "0.1.63",
@ -98,7 +98,7 @@
"bcryptjs": "2.4.3",
"blurhash": "2.0.5",
"body-parser": "1.20.2",
"bullmq": "5.1.9",
"bullmq": "5.4.0",
"cacheable-lookup": "7.0.0",
"cbor": "9.0.2",
"chalk": "5.3.0",
@ -115,11 +115,11 @@
"file-type": "19.0.0",
"fluent-ffmpeg": "2.1.2",
"form-data": "4.0.0",
"got": "14.1.0",
"got": "14.2.0",
"happy-dom": "10.0.3",
"hpagent": "1.2.0",
"htmlescape": "^1.1.1",
"http-link-header": "1.1.1",
"htmlescape": "1.1.1",
"http-link-header": "1.1.2",
"ioredis": "5.3.2",
"ip-cidr": "3.1.0",
"ipaddr.js": "2.1.0",
@ -128,7 +128,7 @@
"jsdom": "23.2.0",
"json5": "2.2.3",
"jsonld": "8.3.2",
"jsrsasign": "11.0.0",
"jsrsasign": "11.1.0",
"meilisearch": "0.37.0",
"mfm-js": "0.24.0",
"microformats-parser": "2.0.2",
@ -136,10 +136,10 @@
"misskey-js": "workspace:*",
"misskey-reversi": "workspace:*",
"ms": "3.0.0-canary.1",
"nanoid": "5.0.4",
"nanoid": "5.0.6",
"nested-property": "4.0.0",
"node-fetch": "3.3.2",
"nodemailer": "6.9.8",
"nodemailer": "6.9.10",
"nsfwjs": "2.4.2",
"oauth": "0.10.0",
"oauth2orize": "1.12.0",
@ -163,15 +163,15 @@
"rename": "1.0.4",
"rss-parser": "3.13.0",
"rxjs": "7.8.1",
"sanitize-html": "2.11.0",
"sanitize-html": "2.12.1",
"secure-json-parse": "2.7.0",
"sharp": "0.33.2",
"slacc": "0.0.10",
"strict-event-emitter-types": "2.0.0",
"stringz": "2.1.0",
"systeminformation": "5.21.24",
"systeminformation": "5.22.0",
"tinycolor2": "1.6.0",
"tmp": "0.2.1",
"tmp": "0.2.2",
"tsc-alias": "1.8.8",
"tsconfig-paths": "4.2.0",
"typeorm": "0.3.20",
@ -185,7 +185,7 @@
"devDependencies": {
"@jest/globals": "29.7.0",
"@misskey-dev/eslint-plugin": "1.0.0",
"@nestjs/platform-express": "10.3.1",
"@nestjs/platform-express": "10.3.3",
"@simplewebauthn/types": "9.0.1",
"@swc/jest": "0.2.31",
"@types/accepts": "1.3.7",
@ -204,21 +204,21 @@
"@types/jsrsasign": "10.5.12",
"@types/mime-types": "2.1.4",
"@types/ms": "0.7.34",
"@types/node": "20.11.17",
"@types/node": "20.11.22",
"@types/node-fetch": "3.0.3",
"@types/nodemailer": "6.4.14",
"@types/oauth": "0.9.4",
"@types/oauth2orize": "1.11.3",
"@types/oauth2orize-pkce": "0.1.2",
"@types/pg": "8.11.0",
"@types/pg": "8.11.2",
"@types/pug": "2.0.10",
"@types/punycode": "2.1.3",
"@types/punycode": "2.1.4",
"@types/qrcode": "1.5.5",
"@types/random-seed": "0.3.5",
"@types/ratelimiter": "3.4.6",
"@types/rename": "1.0.7",
"@types/sanitize-html": "2.9.5",
"@types/semver": "7.5.6",
"@types/sanitize-html": "2.11.0",
"@types/semver": "7.5.8",
"@types/simple-oauth2": "5.0.7",
"@types/sinonjs__fake-timers": "8.1.5",
"@types/tinycolor2": "1.4.6",
@ -236,7 +236,7 @@
"fkill": "^9.0.0",
"jest": "29.7.0",
"jest-mock": "29.7.0",
"nodemon": "3.0.3",
"nodemon": "3.1.0",
"pid-port": "1.0.0",
"simple-oauth2": "5.0.0"
}

View File

@ -69,6 +69,7 @@ export interface MainEventTypes {
file: Packed<'DriveFile'>;
};
readAllNotifications: undefined;
notificationFlushed: undefined;
unreadNotification: Packed<'Notification'>;
unreadMention: MiNote['id'];
readAllUnreadMentions: undefined;

View File

@ -379,6 +379,10 @@ export class NoteCreateService implements OnApplicationShutdown {
}
}
if (mentionedUsers.length > 0 && mentionedUsers.length > (await this.roleService.getUserPolicies(user.id)).mentionLimit) {
throw new IdentifiableError('9f466dab-c856-48cd-9e65-ff90ff750580', 'Note contains too many mentions');
}
const note = await this.insertNote(user, data, tags, emojis, mentionedUsers);
setImmediate('post created', { signal: this.#shutdownController.signal }).then(

View File

@ -214,6 +214,15 @@ export class NotificationService implements OnApplicationShutdown {
*/
}
@bindThis
public async flushAllNotifications(userId: MiUser['id']) {
await Promise.all([
this.redisClient.del(`notificationTimeline:${userId}`),
this.redisClient.del(`latestReadNotification:${userId}`),
]);
this.globalEventService.publishMainStream(userId, 'notificationFlushed');
}
@bindThis
public dispose(): void {
this.#shutdownController.abort();

View File

@ -35,6 +35,7 @@ export type RolePolicies = {
gtlAvailable: boolean;
ltlAvailable: boolean;
canPublicNote: boolean;
mentionLimit: number;
canInvite: boolean;
inviteLimit: number;
inviteLimitCycle: number;
@ -62,6 +63,7 @@ export const DEFAULT_POLICIES: RolePolicies = {
gtlAvailable: true,
ltlAvailable: true,
canPublicNote: true,
mentionLimit: 20,
canInvite: false,
inviteLimit: 0,
inviteLimitCycle: 60 * 24 * 7,
@ -328,6 +330,7 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit {
gtlAvailable: calc('gtlAvailable', vs => vs.some(v => v === true)),
ltlAvailable: calc('ltlAvailable', vs => vs.some(v => v === true)),
canPublicNote: calc('canPublicNote', vs => vs.some(v => v === true)),
mentionLimit: calc('mentionLimit', vs => Math.max(...vs)),
canInvite: calc('canInvite', vs => vs.some(v => v === true)),
inviteLimit: calc('inviteLimit', vs => Math.max(...vs)),
inviteLimitCycle: calc('inviteLimitCycle', vs => Math.max(...vs)),

View File

@ -191,7 +191,7 @@ export class WebAuthnService {
if (cert[0] === 0x04) { // 前の実装ではいつも 0x04 で始まっていた
const halfLength = (cert.length - 1) / 2;
const cborMap = new Map<number, number | ArrayBufferLike>();
const cborMap = new Map<number, number | Uint8Array>();
cborMap.set(1, 2); // kty, EC2
cborMap.set(3, -7); // alg, ES256
cborMap.set(-1, 1); // crv, P256

View File

@ -160,6 +160,10 @@ export const packedRolePoliciesSchema = {
type: 'boolean',
optional: false, nullable: false,
},
mentionLimit: {
type: 'integer',
optional: false, nullable: false,
},
canInvite: {
type: 'boolean',
optional: false, nullable: false,

View File

@ -293,6 +293,7 @@ import * as ep___notes_translate from './endpoints/notes/translate.js';
import * as ep___notes_unrenote from './endpoints/notes/unrenote.js';
import * as ep___notes_userListTimeline from './endpoints/notes/user-list-timeline.js';
import * as ep___notifications_create from './endpoints/notifications/create.js';
import * as ep___notifications_flush from './endpoints/notifications/flush.js';
import * as ep___notifications_markAllAsRead from './endpoints/notifications/mark-all-as-read.js';
import * as ep___notifications_testNotification from './endpoints/notifications/test-notification.js';
import * as ep___pagePush from './endpoints/page-push.js';
@ -664,6 +665,7 @@ const $notes_translate: Provider = { provide: 'ep:notes/translate', useClass: ep
const $notes_unrenote: Provider = { provide: 'ep:notes/unrenote', useClass: ep___notes_unrenote.default };
const $notes_userListTimeline: Provider = { provide: 'ep:notes/user-list-timeline', useClass: ep___notes_userListTimeline.default };
const $notifications_create: Provider = { provide: 'ep:notifications/create', useClass: ep___notifications_create.default };
const $notifications_flush: Provider = { provide: 'ep:notifications/flush', useClass: ep___notifications_flush.default };
const $notifications_markAllAsRead: Provider = { provide: 'ep:notifications/mark-all-as-read', useClass: ep___notifications_markAllAsRead.default };
const $notifications_testNotification: Provider = { provide: 'ep:notifications/test-notification', useClass: ep___notifications_testNotification.default };
const $pagePush: Provider = { provide: 'ep:page-push', useClass: ep___pagePush.default };
@ -1039,6 +1041,7 @@ const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep__
$notes_unrenote,
$notes_userListTimeline,
$notifications_create,
$notifications_flush,
$notifications_markAllAsRead,
$notifications_testNotification,
$pagePush,
@ -1408,7 +1411,9 @@ const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep__
$notes_unrenote,
$notes_userListTimeline,
$notifications_create,
$notifications_flush,
$notifications_markAllAsRead,
$notifications_testNotification,
$pagePush,
$pages_create,
$pages_delete,

View File

@ -293,6 +293,7 @@ import * as ep___notes_translate from './endpoints/notes/translate.js';
import * as ep___notes_unrenote from './endpoints/notes/unrenote.js';
import * as ep___notes_userListTimeline from './endpoints/notes/user-list-timeline.js';
import * as ep___notifications_create from './endpoints/notifications/create.js';
import * as ep___notifications_flush from './endpoints/notifications/flush.js';
import * as ep___notifications_markAllAsRead from './endpoints/notifications/mark-all-as-read.js';
import * as ep___notifications_testNotification from './endpoints/notifications/test-notification.js';
import * as ep___pagePush from './endpoints/page-push.js';
@ -662,6 +663,7 @@ const eps = [
['notes/unrenote', ep___notes_unrenote],
['notes/user-list-timeline', ep___notes_userListTimeline],
['notifications/create', ep___notifications_create],
['notifications/flush', ep___notifications_flush],
['notifications/mark-all-as-read', ep___notifications_markAllAsRead],
['notifications/test-notification', ep___notifications_testNotification],
['page-push', ep___pagePush],

View File

@ -85,6 +85,12 @@ export const meta = {
id: '3ac74a84-8fd5-4bb0-870f-01804f82ce15',
},
cannotReplyToSpecifiedVisibilityNoteWithExtendedVisibility: {
message: 'You cannot reply to a specified visibility note with extended visibility.',
code: 'CANNOT_REPLY_TO_SPECIFIED_VISIBILITY_NOTE_WITH_EXTENDED_VISIBILITY',
id: 'ed940410-535c-4d5e-bfa3-af798671e93c',
},
cannotCreateAlreadyExpiredPoll: {
message: 'Poll is already expired.',
code: 'CANNOT_CREATE_ALREADY_EXPIRED_POLL',
@ -120,6 +126,12 @@ export const meta = {
code: 'CONTAINS_PROHIBITED_WORDS',
id: 'aa6e01d3-a85c-669d-758a-76aab43af334',
},
containsTooManyMentions: {
message: 'Cannot post because it exceeds the allowed number of mentions.',
code: 'CONTAINS_TOO_MANY_MENTIONS',
id: '4de0363a-3046-481b-9b0f-feff3e211025',
},
},
} as const;
@ -313,6 +325,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
throw new ApiError(meta.errors.cannotReplyToPureRenote);
} else if (!await this.noteEntityService.isVisibleForMe(reply, me.id)) {
throw new ApiError(meta.errors.cannotReplyToInvisibleNote);
} else if (reply.visibility === 'specified' && ps.visibility !== 'specified') {
throw new ApiError(meta.errors.cannotReplyToSpecifiedVisibilityNoteWithExtendedVisibility);
}
// Check blocking
@ -378,9 +392,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
} catch (e) {
// TODO: 他のErrorもここでキャッチしてエラーメッセージを当てるようにしたい
if (e instanceof IdentifiableError) {
if (e.id === '689ee33f-f97c-479a-ac49-1b9f8140af99') throw new ApiError(meta.errors.containsProhibitedWords);
if (e.id === '689ee33f-f97c-479a-ac49-1b9f8140af99') {
throw new ApiError(meta.errors.containsProhibitedWords);
} else if (e.id === '9f466dab-c856-48cd-9e65-ff90ff750580') {
throw new ApiError(meta.errors.containsTooManyMentions);
}
}
throw e;
}
});

View File

@ -0,0 +1,33 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { Injectable } from '@nestjs/common';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { NotificationService } from '@/core/NotificationService.js';
export const meta = {
tags: ['notifications', 'account'],
requireCredential: true,
kind: 'write:notifications',
} as const;
export const paramDef = {
type: 'object',
properties: {},
required: [],
} as const;
@Injectable()
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
constructor(
private notificationService: NotificationService,
) {
super(meta, paramDef, async (ps, me) => {
this.notificationService.flushAllNotifications(me.id);
});
}
}

View File

@ -176,6 +176,87 @@ describe('Note', () => {
assert.strictEqual(deleteRes.status, 204);
});
test('visibility: followersなートに対してフォロワーはリプライできる', async () => {
await api('/following/create', {
userId: alice.id,
}, bob);
const aliceNote = await api('/notes/create', {
text: 'direct note to bob',
visibility: 'followers',
}, alice);
assert.strictEqual(aliceNote.status, 200);
const replyId = aliceNote.body.createdNote.id;
const bobReply = await api('/notes/create', {
text: 'reply to alice note',
replyId,
}, bob);
assert.strictEqual(bobReply.status, 200);
assert.strictEqual(bobReply.body.createdNote.replyId, replyId);
await api('/following/delete', {
userId: alice.id,
}, bob);
});
test('visibility: followersなートに対してフォロワーでないユーザーがリプライしようとすると怒られる', async () => {
const aliceNote = await api('/notes/create', {
text: 'direct note to bob',
visibility: 'followers',
}, alice);
assert.strictEqual(aliceNote.status, 200);
const bobReply = await api('/notes/create', {
text: 'reply to alice note',
replyId: aliceNote.body.createdNote.id,
}, bob);
assert.strictEqual(bobReply.status, 400);
assert.strictEqual(bobReply.body.error.code, 'CANNOT_REPLY_TO_AN_INVISIBLE_NOTE');
});
test('visibility: specifiedなートに対してvisibility: specifiedで返信できる', async () => {
const aliceNote = await api('/notes/create', {
text: 'direct note to bob',
visibility: 'specified',
visibleUserIds: [bob.id],
}, alice);
assert.strictEqual(aliceNote.status, 200);
const bobReply = await api('/notes/create', {
text: 'reply to alice note',
replyId: aliceNote.body.createdNote.id,
visibility: 'specified',
visibleUserIds: [alice.id],
}, bob);
assert.strictEqual(bobReply.status, 200);
});
test('visibility: specifiedなートに対してvisibility: follwersで返信しようとすると怒られる', async () => {
const aliceNote = await api('/notes/create', {
text: 'direct note to bob',
visibility: 'specified',
visibleUserIds: [bob.id],
}, alice);
assert.strictEqual(aliceNote.status, 200);
const bobReply = await api('/notes/create', {
text: 'reply to alice note with visibility: followers',
replyId: aliceNote.body.createdNote.id,
visibility: 'followers',
}, bob);
assert.strictEqual(bobReply.status, 400);
assert.strictEqual(bobReply.body.error.code, 'CANNOT_REPLY_TO_SPECIFIED_VISIBILITY_NOTE_WITH_EXTENDED_VISIBILITY');
});
test('文字数ぎりぎりで怒られない', async () => {
const post = {
text: '!'.repeat(MAX_NOTE_TEXT_LENGTH), // 3000文字
@ -680,6 +761,171 @@ describe('Note', () => {
assert.strictEqual(note1.status, 400);
});
test('メンションの数が上限を超えるとエラーになる', async () => {
const res = await api('admin/roles/create', {
name: 'test',
description: '',
color: null,
iconUrl: null,
displayOrder: 0,
target: 'manual',
condFormula: {},
isAdministrator: false,
isModerator: false,
isPublic: false,
isExplorable: false,
asBadge: false,
canEditMembersByModerator: false,
policies: {
mentionLimit: {
useDefault: false,
priority: 1,
value: 0,
},
},
}, alice);
assert.strictEqual(res.status, 200);
await new Promise(x => setTimeout(x, 2));
const assign = await api('admin/roles/assign', {
userId: alice.id,
roleId: res.body.id,
}, alice);
assert.strictEqual(assign.status, 204);
await new Promise(x => setTimeout(x, 2));
const note = await api('/notes/create', {
text: '@bob potentially annoying text',
}, alice);
assert.strictEqual(note.status, 400);
assert.strictEqual(note.body.error.code, 'CONTAINS_TOO_MANY_MENTIONS');
await api('admin/roles/unassign', {
userId: alice.id,
roleId: res.body.id,
});
await api('admin/roles/delete', {
roleId: res.body.id,
}, alice);
});
test('ダイレクト投稿もエラーになる', async () => {
const res = await api('admin/roles/create', {
name: 'test',
description: '',
color: null,
iconUrl: null,
displayOrder: 0,
target: 'manual',
condFormula: {},
isAdministrator: false,
isModerator: false,
isPublic: false,
isExplorable: false,
asBadge: false,
canEditMembersByModerator: false,
policies: {
mentionLimit: {
useDefault: false,
priority: 1,
value: 0,
},
},
}, alice);
assert.strictEqual(res.status, 200);
await new Promise(x => setTimeout(x, 2));
const assign = await api('admin/roles/assign', {
userId: alice.id,
roleId: res.body.id,
}, alice);
assert.strictEqual(assign.status, 204);
await new Promise(x => setTimeout(x, 2));
const note = await api('/notes/create', {
text: 'potentially annoying text',
visibility: 'specified',
visibleUserIds: [ bob.id ],
}, alice);
assert.strictEqual(note.status, 400);
assert.strictEqual(note.body.error.code, 'CONTAINS_TOO_MANY_MENTIONS');
await api('admin/roles/unassign', {
userId: alice.id,
roleId: res.body.id,
});
await api('admin/roles/delete', {
roleId: res.body.id,
}, alice);
});
test('ダイレクトの宛先とメンションが同じ場合は重複してカウントしない', async () => {
const res = await api('admin/roles/create', {
name: 'test',
description: '',
color: null,
iconUrl: null,
displayOrder: 0,
target: 'manual',
condFormula: {},
isAdministrator: false,
isModerator: false,
isPublic: false,
isExplorable: false,
asBadge: false,
canEditMembersByModerator: false,
policies: {
mentionLimit: {
useDefault: false,
priority: 1,
value: 1,
},
},
}, alice);
assert.strictEqual(res.status, 200);
await new Promise(x => setTimeout(x, 2));
const assign = await api('admin/roles/assign', {
userId: alice.id,
roleId: res.body.id,
}, alice);
assert.strictEqual(assign.status, 204);
await new Promise(x => setTimeout(x, 2));
const note = await api('/notes/create', {
text: '@bob potentially annoying text',
visibility: 'specified',
visibleUserIds: [ bob.id ],
}, alice);
assert.strictEqual(note.status, 200);
await api('admin/roles/unassign', {
userId: alice.id,
roleId: res.body.id,
});
await api('admin/roles/delete', {
roleId: res.body.id,
}, alice);
});
});
describe('notes/delete', () => {

View File

@ -227,6 +227,46 @@ describe('Streaming', () => {
assert.strictEqual(fired, false);
});
/**
* TODO: 落ちる
* @see https://github.com/misskey-dev/misskey/issues/13474
test('visibility: specified なノートで visibleUserIds に自分が含まれているときそのノートへのリプライが流れてくる', async () => {
const chitoseToKyokoAndAyano = await post(chitose, { text: 'direct note from chitose to kyoko and ayano', visibility: 'specified', visibleUserIds: [kyoko.id, ayano.id] });
const fired = await waitFire(
ayano, 'homeTimeline', // ayano:home
() => api('notes/create', { text: 'direct reply from kyoko to chitose and ayano', replyId: chitoseToKyokoAndAyano.id, visibility: 'specified', visibleUserIds: [chitose.id, ayano.id] }, kyoko),
msg => msg.type === 'note' && msg.body.userId === kyoko.id,
);
assert.strictEqual(fired, true);
});
*/
test('visibility: specified な投稿に対するリプライで visibleUserIds が拡張されたとき、その拡張されたユーザーの HTL にはそのリプライが流れない', async () => {
const chitoseToKyoko = await post(chitose, { text: 'direct note from chitose to kyoko', visibility: 'specified', visibleUserIds: [kyoko.id] });
const fired = await waitFire(
ayano, 'homeTimeline', // ayano:home
() => api('notes/create', { text: 'direct reply from kyoko to chitose and ayano', replyId: chitoseToKyoko.id, visibility: 'specified', visibleUserIds: [chitose.id, ayano.id] }, kyoko),
msg => msg.type === 'note' && msg.body.userId === kyoko.id,
);
assert.strictEqual(fired, false);
});
test('visibility: specified な投稿に対するリプライで visibleUserIds が収縮されたとき、その収縮されたユーザーの HTL にはそのリプライが流れない', async () => {
const chitoseToKyokoAndAyano = await post(chitose, { text: 'direct note from chitose to kyoko and ayano', visibility: 'specified', visibleUserIds: [kyoko.id, ayano.id] });
const fired = await waitFire(
ayano, 'homeTimeline', // ayano:home
() => api('notes/create', { text: 'direct reply from kyoko to chitose', replyId: chitoseToKyokoAndAyano.id, visibility: 'specified', visibleUserIds: [chitose.id] }, kyoko),
msg => msg.type === 'note' && msg.body.userId === kyoko.id,
);
assert.strictEqual(fired, false);
});
test('withRenotes: false のときリノートが流れない', async () => {
const fired = await waitFire(
ayano, 'homeTimeline', // ayano:home

View File

@ -27,19 +27,19 @@
"@syuilo/aiscript": "0.17.0",
"@tabler/icons-webfont": "2.44.0",
"@twemoji/parser": "15.0.0",
"@vitejs/plugin-vue": "5.0.3",
"@vue/compiler-sfc": "3.4.18",
"@vitejs/plugin-vue": "5.0.4",
"@vue/compiler-sfc": "3.4.21",
"aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.1.2",
"astring": "1.8.6",
"broadcast-channel": "7.0.0",
"buraha": "0.0.1",
"canvas-confetti": "1.6.1",
"chart.js": "4.4.1",
"chart.js": "4.4.2",
"chartjs-adapter-date-fns": "3.0.0",
"chartjs-chart-matrix": "2.0.1",
"chartjs-plugin-gradient": "0.6.1",
"chartjs-plugin-zoom": "2.0.1",
"chromatic": "10.6.1",
"chromatic": "11.0.0",
"compare-versions": "6.1.0",
"cropperjs": "2.0.0-beta.4",
"date-fns": "2.30.0",
@ -57,13 +57,13 @@
"misskey-reversi": "workspace:*",
"photoswipe": "5.4.3",
"punycode": "2.3.1",
"rollup": "4.9.6",
"sanitize-html": "2.11.0",
"sass": "1.70.0",
"rollup": "4.12.0",
"sanitize-html": "2.12.1",
"sass": "1.71.1",
"shiki": "1.0.0-beta.3",
"strict-event-emitter-types": "2.0.0",
"textarea-caret": "3.1.0",
"three": "0.160.1",
"three": "0.161.0",
"throttle-debounce": "5.0.0",
"tinycolor2": "1.6.0",
"tsc-alias": "1.8.8",
@ -71,39 +71,39 @@
"typescript": "5.3.3",
"uuid": "9.0.1",
"v-code-diff": "1.7.2",
"vite": "5.1.0",
"vue": "3.4.18",
"vite": "5.1.4",
"vue": "3.4.21",
"vuedraggable": "next"
},
"devDependencies": {
"@misskey-dev/eslint-plugin": "1.0.0",
"@misskey-dev/summaly": "5.0.3",
"@storybook/addon-actions": "8.0.0-beta.2",
"@storybook/addon-essentials": "8.0.0-beta.2",
"@storybook/addon-interactions": "8.0.0-beta.2",
"@storybook/addon-links": "8.0.0-beta.2",
"@storybook/addon-mdx-gfm": "8.0.0-beta.2",
"@storybook/addon-storysource": "8.0.0-beta.2",
"@storybook/blocks": "8.0.0-beta.2",
"@storybook/components": "8.0.0-beta.2",
"@storybook/core-events": "8.0.0-beta.2",
"@storybook/manager-api": "8.0.0-beta.2",
"@storybook/preview-api": "8.0.0-beta.2",
"@storybook/react": "8.0.0-beta.2",
"@storybook/react-vite": "8.0.0-beta.2",
"@storybook/test": "8.0.0-beta.2",
"@storybook/theming": "8.0.0-beta.2",
"@storybook/types": "8.0.0-beta.2",
"@storybook/vue3": "8.0.0-beta.2",
"@storybook/vue3-vite": "8.0.0-beta.2",
"@storybook/addon-actions": "8.0.0-beta.6",
"@storybook/addon-essentials": "8.0.0-beta.6",
"@storybook/addon-interactions": "8.0.0-beta.6",
"@storybook/addon-links": "8.0.0-beta.6",
"@storybook/addon-mdx-gfm": "8.0.0-beta.6",
"@storybook/addon-storysource": "8.0.0-beta.6",
"@storybook/blocks": "8.0.0-beta.6",
"@storybook/components": "8.0.0-beta.6",
"@storybook/core-events": "8.0.0-beta.6",
"@storybook/manager-api": "8.0.0-beta.6",
"@storybook/preview-api": "8.0.0-beta.6",
"@storybook/react": "8.0.0-beta.6",
"@storybook/react-vite": "8.0.0-beta.6",
"@storybook/test": "8.0.0-beta.6",
"@storybook/theming": "8.0.0-beta.6",
"@storybook/types": "8.0.0-beta.6",
"@storybook/vue3": "8.0.0-beta.6",
"@storybook/vue3-vite": "8.0.0-beta.6",
"@testing-library/vue": "8.0.2",
"@types/escape-regexp": "0.0.3",
"@types/estree": "1.0.5",
"@types/matter-js": "0.19.6",
"@types/micromatch": "4.0.6",
"@types/node": "20.11.17",
"@types/punycode": "2.1.3",
"@types/sanitize-html": "2.9.5",
"@types/node": "20.11.22",
"@types/punycode": "2.1.4",
"@types/sanitize-html": "2.11.0",
"@types/throttle-debounce": "5.0.2",
"@types/tinycolor2": "1.4.6",
"@types/uuid": "9.0.8",
@ -111,25 +111,25 @@
"@typescript-eslint/eslint-plugin": "6.18.1",
"@typescript-eslint/parser": "6.18.1",
"@vitest/coverage-v8": "0.34.6",
"@vue/runtime-core": "3.4.18",
"@vue/runtime-core": "3.4.21",
"acorn": "8.11.3",
"cross-env": "7.0.3",
"cypress": "13.6.4",
"cypress": "13.6.6",
"eslint": "8.56.0",
"eslint-plugin-import": "2.29.1",
"eslint-plugin-vue": "9.20.1",
"eslint-plugin-vue": "9.22.0",
"fast-glob": "3.3.2",
"happy-dom": "10.0.3",
"happy-dom": "13.6.2",
"intersection-observer": "0.12.2",
"micromatch": "4.0.5",
"msw": "2.1.7",
"msw-storybook-addon": "2.0.0-beta.1",
"nodemon": "3.0.3",
"nodemon": "3.1.0",
"prettier": "3.2.5",
"react": "18.2.0",
"react-dom": "18.2.0",
"start-server-and-test": "2.0.3",
"storybook": "8.0.0-beta.2",
"storybook": "8.0.0-beta.6",
"storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme",
"vite-plugin-turbosnap": "1.0.3",
"vitest": "0.34.6",

View File

@ -108,12 +108,12 @@ import * as Misskey from 'misskey-js';
import XSection from '@/components/MkEmojiPicker.section.vue';
import {
emojilist,
unicodeEmojisMap,
emojiCharByCategory,
UnicodeEmojiDef,
unicodeEmojiCategories as categories,
getEmojiName,
CustomEmojiFolderTree,
getUnicodeEmoji,
} from '@/scripts/emojilist.js';
import MkRippleEffect from '@/components/MkRippleEffect.vue';
import * as os from '@/os.js';
@ -382,7 +382,7 @@ function getDef(emoji: string) {
if (emoji.includes(':')) {
return customEmojisMap.get(emoji.replace(/:/g, ''))!;
} else {
return unicodeEmojisMap.get(emoji)!;
return getUnicodeEmoji(emoji)!;
}
}

View File

@ -35,6 +35,7 @@ import { notificationTypes } from '@/const.js';
import { infoImageUrl } from '@/instance.js';
import { defaultStore } from '@/store.js';
import MkPullToRefresh from '@/components/MkPullToRefresh.vue';
import * as Misskey from 'misskey-js';
const props = defineProps<{
excludeTypes?: typeof notificationTypes[number][];
@ -75,17 +76,19 @@ function reload() {
});
}
let connection;
let connection: Misskey.ChannelConnection<Misskey.Channels['main']>;
onMounted(() => {
connection = useStream().useChannel('main');
connection.on('notification', onNotification);
connection.on('notificationFlushed', reload);
});
onActivated(() => {
pagingComponent.value?.reload();
connection = useStream().useChannel('main');
connection.on('notification', onNotification);
connection.on('notificationFlushed', reload);
});
onUnmounted(() => {

View File

@ -172,7 +172,7 @@ const emit = defineEmits<{
const textareaEl = shallowRef<HTMLTextAreaElement | null>(null);
const cwInputEl = shallowRef<HTMLInputElement | null>(null);
const hashtagsInputEl = shallowRef<HTMLInputElement | null>(null);
const visibilityButton = shallowRef<HTMLElement | null>(null);
const visibilityButton = shallowRef<HTMLElement>();
const posting = ref(false);
const posted = ref(false);
@ -461,6 +461,7 @@ function setVisibility() {
isSilenced: $i.isSilenced,
localOnly: localOnly.value,
src: visibilityButton.value,
...(props.reply ? { isReplyVisibilitySpecified: props.reply.visibility === 'specified' } : {}),
}, {
changeVisibility: v => {
visibility.value = v;

View File

@ -34,7 +34,7 @@ import { i18n } from '@/i18n.js';
import * as sound from '@/scripts/sound.js';
import { checkReactionPermissions } from '@/scripts/check-reaction-permissions.js';
import { customEmojisMap } from '@/custom-emojis.js';
import { unicodeEmojisMap } from '@/scripts/emojilist.js';
import { getUnicodeEmoji } from '@/scripts/emojilist.js';
const props = defineProps<{
reaction: string;
@ -52,7 +52,7 @@ const emit = defineEmits<{
const buttonEl = shallowRef<HTMLElement>();
const emojiName = computed(() => props.reaction.replace(/:/g, '').replace(/@\./, ''));
const emoji = computed(() => customEmojisMap.get(emojiName.value) ?? unicodeEmojisMap.get(props.reaction));
const emoji = computed(() => customEmojisMap.get(emojiName.value) ?? getUnicodeEmoji(props.reaction));
const canToggle = computed(() => {
return !props.reaction.match(/@\w/) && $i && emoji.value && checkReactionPermissions($i, props.note, emoji.value);

View File

@ -9,21 +9,21 @@ SPDX-License-Identifier: AGPL-3.0-only
<div :class="[$style.label, $style.item]">
{{ i18n.ts.visibility }}
</div>
<button key="public" :disabled="isSilenced" class="_button" :class="[$style.item, { [$style.active]: v === 'public' }]" data-index="1" @click="choose('public')">
<button key="public" :disabled="isSilenced || isReplyVisibilitySpecified" class="_button" :class="[$style.item, { [$style.active]: v === 'public' }]" data-index="1" @click="choose('public')">
<div :class="$style.icon"><i class="ti ti-world"></i></div>
<div :class="$style.body">
<span :class="$style.itemTitle">{{ i18n.ts._visibility.public }}</span>
<span :class="$style.itemDescription">{{ i18n.ts._visibility.publicDescription }}</span>
</div>
</button>
<button key="home" class="_button" :class="[$style.item, { [$style.active]: v === 'home' }]" data-index="2" @click="choose('home')">
<button key="home" :disabled="isReplyVisibilitySpecified" class="_button" :class="[$style.item, { [$style.active]: v === 'home' }]" data-index="2" @click="choose('home')">
<div :class="$style.icon"><i class="ti ti-home"></i></div>
<div :class="$style.body">
<span :class="$style.itemTitle">{{ i18n.ts._visibility.home }}</span>
<span :class="$style.itemDescription">{{ i18n.ts._visibility.homeDescription }}</span>
</div>
</button>
<button key="followers" class="_button" :class="[$style.item, { [$style.active]: v === 'followers' }]" data-index="3" @click="choose('followers')">
<button key="followers" :disabled="isReplyVisibilitySpecified" class="_button" :class="[$style.item, { [$style.active]: v === 'followers' }]" data-index="3" @click="choose('followers')">
<div :class="$style.icon"><i class="ti ti-lock"></i></div>
<div :class="$style.body">
<span :class="$style.itemTitle">{{ i18n.ts._visibility.followers }}</span>
@ -54,6 +54,7 @@ const props = withDefaults(defineProps<{
isSilenced: boolean;
localOnly: boolean;
src?: HTMLElement;
isReplyVisibilitySpecified?: boolean;
}>(), {
});

View File

@ -75,6 +75,7 @@ export const ROLE_POLICIES = [
'gtlAvailable',
'ltlAvailable',
'canPublicNote',
'mentionLimit',
'canInvite',
'inviteLimit',
'inviteLimitCycle',

View File

@ -160,6 +160,25 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</MkFolder>
<MkFolder v-if="matchQuery([i18n.ts._role._options.mentionMax, 'mentionLimit'])">
<template #label>{{ i18n.ts._role._options.mentionMax }}</template>
<template #suffix>
<span v-if="role.policies.mentionLimit.useDefault" :class="$style.useDefaultLabel">{{ i18n.ts._role.useBaseValue }}</span>
<span v-else>{{ role.policies.mentionLimit.value }}</span>
<span :class="$style.priorityIndicator"><i :class="getPriorityIcon(role.policies.mentionLimit)"></i></span>
</template>
<div class="_gaps">
<MkSwitch v-model="role.policies.mentionLimit.useDefault" :readonly="readonly">
<template #label>{{ i18n.ts._role.useBaseValue }}</template>
</MkSwitch>
<MkInput v-model="role.policies.mentionLimit.value" :disabled="role.policies.mentionLimit.useDefault" type="number" :readonly="readonly">
</MkInput>
<MkRange v-model="role.policies.mentionLimit.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
<template #label>{{ i18n.ts._role.priority }}</template>
</MkRange>
</div>
</MkFolder>
<MkFolder v-if="matchQuery([i18n.ts._role._options.canInvite, 'canInvite'])">
<template #label>{{ i18n.ts._role._options.canInvite }}</template>
<template #suffix>

View File

@ -48,6 +48,13 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkSwitch>
</MkFolder>
<MkFolder v-if="matchQuery([i18n.ts._role._options.mentionMax, 'mentionLimit'])">
<template #label>{{ i18n.ts._role._options.mentionMax }}</template>
<template #suffix>{{ policies.mentionLimit }}</template>
<MkInput v-model="policies.mentionLimit" type="number">
</MkInput>
</MkFolder>
<MkFolder v-if="matchQuery([i18n.ts._role._options.canInvite, 'canInvite'])">
<template #label>{{ i18n.ts._role._options.canInvite }}</template>
<template #suffix>{{ policies.canInvite ? i18n.ts.yes : i18n.ts.no }}</template>

View File

@ -35,6 +35,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<FormSection>
<div class="_gaps_m">
<FormLink @click="testNotification">{{ i18n.ts._notification.sendTestNotification }}</FormLink>
<FormLink @click="flushNotification">{{ i18n.ts._notification.flushNotification }}</FormLink>
</div>
</FormSection>
<FormSection>
@ -114,6 +115,17 @@ function testNotification(): void {
misskeyApi('notifications/test-notification');
}
async function flushNotification() {
const { canceled } = await os.confirm({
type: 'warning',
text: i18n.ts.resetAreYouSure,
});
if (canceled) return;
os.apiWithDialog('notifications/flush');
}
const headerActions = computed(() => []);
const headerTabs = computed(() => []);

View File

@ -20,8 +20,8 @@ export const emojilist: UnicodeEmojiDef[] = _emojilist.map(x => ({
category: unicodeEmojiCategories[x[2]],
}));
export const unicodeEmojisMap = new Map<string, UnicodeEmojiDef>(
emojilist.map(x => [x.char, x])
const unicodeEmojisMap = new Map<string, UnicodeEmojiDef>(
emojilist.map(x => [x.char, x]),
);
const _indexByChar = new Map<string, number>();
@ -39,6 +39,11 @@ for (let i = 0; i < emojilist.length; i++) {
export const emojiCharByCategory = _charGroupByCategory;
export function getUnicodeEmoji(char: string): UnicodeEmojiDef | null {
// Colorize it because emojilist.json assumes that
return unicodeEmojisMap.get(colorizeEmoji(char)) ?? null;
}
export function getEmojiName(char: string): string | null {
// Colorize it because emojilist.json assumes that
const idx = _indexByChar.get(colorizeEmoji(char));

View File

@ -9,6 +9,7 @@ import { onScrollBottom, onScrollTop } from '@/scripts/scroll.js';
describe('Scroll', () => {
describe('onScrollTop', () => {
/* (happy-dom)
test('Initial onScrollTop callback for connected elements', () => {
const { document } = new Window();
const div = document.createElement('div');
@ -21,6 +22,7 @@ describe('Scroll', () => {
assert.ok(called);
});
*/
test('No onScrollTop callback for disconnected elements', () => {
const { document } = new Window();
@ -35,11 +37,11 @@ describe('Scroll', () => {
});
describe('onScrollBottom', () => {
/* (happy-dom)
test('Initial onScrollBottom callback for connected elements', () => {
const { document } = new Window();
const div = document.createElement('div');
assert.strictEqual(div.scrollTop, 0);
(div as any).scrollHeight = 100; // happy-dom has no scrollHeight
document.body.append(div);
@ -48,12 +50,12 @@ describe('Scroll', () => {
assert.ok(called);
});
*/
test('No onScrollBottom callback for disconnected elements', () => {
const { document } = new Window();
const div = document.createElement('div');
assert.strictEqual(div.scrollTop, 0);
(div as any).scrollHeight = 100; // happy-dom has no scrollHeight
let called = false;
onScrollBottom(div as any as HTMLElement, () => called = true);

View File

@ -530,6 +530,7 @@ export type Channels = {
unreadNotification: (payload: Notification_2) => void;
unreadMention: (payload: Note['id']) => void;
readAllUnreadMentions: () => void;
notificationFlushed: () => void;
unreadSpecifiedNote: (payload: Note['id']) => void;
readAllUnreadSpecifiedNotes: () => void;
readAllAntennas: () => void;

View File

@ -38,8 +38,8 @@
"@microsoft/api-extractor": "7.39.1",
"@misskey-dev/eslint-plugin": "1.0.0",
"@swc/jest": "0.2.31",
"@types/jest": "29.5.11",
"@types/node": "20.11.17",
"@types/jest": "29.5.12",
"@types/node": "20.11.22",
"@typescript-eslint/eslint-plugin": "6.18.1",
"@typescript-eslint/parser": "6.18.1",
"eslint": "8.56.0",
@ -48,8 +48,8 @@
"jest-websocket-mock": "2.5.0",
"mock-socket": "9.3.1",
"ncp": "2.0.0",
"nodemon": "3.0.3",
"tsd": "0.30.4",
"nodemon": "3.1.0",
"tsd": "0.30.7",
"typescript": "5.3.3"
},
"files": [

View File

@ -3195,6 +3195,17 @@ declare module '../api.js' {
credential?: string | null,
): Promise<SwitchCaseResponseType<E, P>>;
/**
* No description provided.
*
* **Credential required**: *Yes* / **Permission**: *write:notifications*
*/
request<E extends 'notifications/flush', P extends Endpoints[E]['req']>(
endpoint: E,
params: P,
credential?: string | null,
): Promise<SwitchCaseResponseType<E, P>>;
/**
* No description provided.
*

View File

@ -841,6 +841,7 @@ export type Endpoints = {
'notes/unrenote': { req: NotesUnrenoteRequest; res: EmptyResponse };
'notes/user-list-timeline': { req: NotesUserListTimelineRequest; res: NotesUserListTimelineResponse };
'notifications/create': { req: NotificationsCreateRequest; res: EmptyResponse };
'notifications/flush': { req: EmptyRequest; res: EmptyResponse };
'notifications/mark-all-as-read': { req: EmptyRequest; res: EmptyResponse };
'notifications/test-notification': { req: EmptyRequest; res: EmptyResponse };
'page-push': { req: PagePushRequest; res: EmptyResponse };

View File

@ -2770,6 +2770,15 @@ export type paths = {
*/
post: operations['notifications/create'];
};
'/notifications/flush': {
/**
* notifications/flush
* @description No description provided.
*
* **Credential required**: *Yes* / **Permission**: *write:notifications*
*/
post: operations['notifications/flush'];
};
'/notifications/mark-all-as-read': {
/**
* notifications/mark-all-as-read
@ -4643,6 +4652,7 @@ export type components = {
gtlAvailable: boolean;
ltlAvailable: boolean;
canPublicNote: boolean;
mentionLimit: number;
canInvite: boolean;
inviteLimit: number;
inviteLimitCycle: number;
@ -22056,6 +22066,50 @@ export type operations = {
};
};
};
/**
* notifications/flush
* @description No description provided.
*
* **Credential required**: *Yes* / **Permission**: *write:notifications*
*/
'notifications/flush': {
responses: {
/** @description OK (without any results) */
204: {
content: never;
};
/** @description Client error */
400: {
content: {
'application/json': components['schemas']['Error'];
};
};
/** @description Authentication error */
401: {
content: {
'application/json': components['schemas']['Error'];
};
};
/** @description Forbidden error */
403: {
content: {
'application/json': components['schemas']['Error'];
};
};
/** @description I'm Ai */
418: {
content: {
'application/json': components['schemas']['Error'];
};
};
/** @description Internal server error */
500: {
content: {
'application/json': components['schemas']['Error'];
};
};
};
};
/**
* notifications/mark-all-as-read
* @description No description provided.

View File

@ -40,6 +40,7 @@ export type Channels = {
unreadNotification: (payload: Notification) => void;
unreadMention: (payload: Note['id']) => void;
readAllUnreadMentions: () => void;
notificationFlushed: () => void;
unreadSpecifiedNote: (payload: Note['id']) => void;
readAllUnreadSpecifiedNotes: () => void;
readAllAntennas: () => void;

View File

@ -19,7 +19,7 @@
"@typescript/lib-webworker": "npm:@types/serviceworker@0.0.67",
"eslint": "8.56.0",
"eslint-plugin-import": "2.29.1",
"nodemon": "3.0.3",
"nodemon": "3.1.0",
"typescript": "5.3.3"
},
"type": "module"

File diff suppressed because it is too large Load Diff