diff --git a/locales/ca-ES.yml b/locales/ca-ES.yml index 0bebd913df..4c9648739a 100644 --- a/locales/ca-ES.yml +++ b/locales/ca-ES.yml @@ -1179,7 +1179,7 @@ emptyToDisableSmtpAuth: Deixa el nom d'usuari i la contrasenya sense emplenar pe desactivar la verificació SMTP smtpSecureInfo: Desactiva això quant facis servir STARTTLS testEmail: Envia un correu electrònic de verificació -wordMute: Silenciar paraules +wordMute: Paraules i llenguatge silenciats regexpError: Error a la Expressió Regular regexpErrorDescription: 'Hi ha un error a la expressió regular a la línea {line} de la teva {tab} de paraules silenciades:' @@ -2040,6 +2040,13 @@ _wordMute: s'afegeixin a la línia de temps. A més, aquestes publicacions no s'afegiran a la línia de temps encara que es modifiquin les condicions. mutedNotes: Publicacions silenciades + muteLangsDescription2: Fes servir el codi del l'idioma. Per exemple en, fr, ja, + zh. + lang: Idioma + langDescription: Amagar les publicacions que coincideixin amb l'idioma a la línia + de temps. + muteLangs: Llenguatges silenciats + muteLangsDescription: Separar amb espais o línies no es per una condició OR. _auth: shareAccessAsk: Estàs segur que vols autoritzar aquesta aplicació per accedir al teu compte? @@ -2190,3 +2197,6 @@ detectPostLanguage: Detecta l'idioma automàticament i mostra un botó per els a indexableDescription: Permet al cercador intern mostrar els missatges públics indexable: Indexable languageForTranslation: Idioma de traducció d'articles +openServerInfo: Mostra la informació del servidor fent clic al símbol del servidor + en un missatge +vibrate: Activar vibracions diff --git a/locales/de-DE.yml b/locales/de-DE.yml index b2e7bda73c..08622ced1b 100644 --- a/locales/de-DE.yml +++ b/locales/de-DE.yml @@ -2213,3 +2213,5 @@ indexableDescription: Der integrierten Suche erlauben, Ihre öffentlichen Beitr anzuzeigen indexable: Indexierbar languageForTranslation: Übersetzungssprache veröffentlichen +openServerInfo: Anzeigen von Serverinformationen durch Anklicken des Server-Tickers + in einem Beitrag diff --git a/locales/fr-FR.yml b/locales/fr-FR.yml index ee112b7a1b..7ea66d7efb 100644 --- a/locales/fr-FR.yml +++ b/locales/fr-FR.yml @@ -724,8 +724,8 @@ no: "Non" driveFilesCount: "Nombre de fichiers dans le Drive" driveUsage: "Utilisation du Drive" noCrawle: "Refuser l'indexation par les robots" -noCrawleDescription: "Demandez aux moteurs de recherche de ne pas indexer votre page - de profil, vos publications, vos pages, etc." +noCrawleDescription: "Demandez aux moteurs de recherche externes de ne pas indexer + votre contenu." lockedAccountInfo: "À moins que vous ne définissiez la visibilité de votre publication sur \"Abonné-e-s\", vos publications sont visibles par tous, même si vous exigez que les demandes d'abonnement soient approuvées manuellement." @@ -2210,3 +2210,11 @@ confirm: Confirmer importZip: Importer ZIP exportZip: Exporter ZIP emojiPackCreator: Créateur de pack d’émoji +detectPostLanguage: Détecte automatiquement le langage et affiche un bouton de traduction + pour les langues étrangères +indexableDescription: Permettre à la recherche interne d’afficher vos publications + publiques +openServerInfo: Afficher les informations du serveur en cliquant sur le bandeau de + serveur d’une publication +indexable: Indexable +languageForTranslation: Langage post-traduction diff --git a/locales/id-ID.yml b/locales/id-ID.yml index 24a5bc25f0..da599a8fb3 100644 --- a/locales/id-ID.yml +++ b/locales/id-ID.yml @@ -637,7 +637,7 @@ emptyToDisableSmtpAuth: "Kosongkan nama pengguna dan kata sandi untuk menonaktif smtpSecure: "Gunakan SSL/TLS implisit untuk koneksi SMTP" smtpSecureInfo: "Matikan ini ketika menggunakan STARTTLS" testEmail: "Tes pengiriman surel" -wordMute: "Bisukan kata" +wordMute: "Bisukan kata dan bahasa" regexpError: "Kesalahan ekspresi reguler" regexpErrorDescription: "Galat terjadi pada baris {line} ekspresi reguler dari {tab} kata yang dibisukan:" @@ -1135,6 +1135,12 @@ _wordMute: soft: "Lembut" hard: "Keras" mutedNotes: "Postingan yang dibisukan" + muteLangsDescription2: Gunakan kode bahasa misalnya en, fr, ja, zh. + lang: Bahasa + langDescription: Sembunyikan postingan yang cocok dengan bahasa yang ditetapkan + dari timeline. + muteLangs: Bahasa yang dibisukan + muteLangsDescription: Pisahkan dengan spasi atau jeda baris untuk kondisi ATAU. _instanceMute: instanceMuteDescription: "Pengaturan ini akan membisukan postingan/pembagian apa saja dari server yang terdaftar, termasuk pengguna yang membalas pengguna lain @@ -2173,3 +2179,6 @@ detectPostLanguage: Deteksi bahasa secara otomatis dan tampilkan tombol terjemah indexableDescription: Perbolehkan pencarian di sini untuk menampilkan kiriman publikmu indexable: Dapat diindeks languageForTranslation: Bahasa terjemahan kiriman +openServerInfo: Tampilkan informasi server dengan mengeklik ticker server di sebuah + kiriman +vibrate: Putar getaran diff --git a/locales/it-IT.yml b/locales/it-IT.yml index f9199ee271..9ecb4ac304 100644 --- a/locales/it-IT.yml +++ b/locales/it-IT.yml @@ -2162,3 +2162,5 @@ detectPostLanguage: Riconosci la lingua automaticamente e mostra il bottone per indexableDescription: Mostra i tuoi post pubblici tramite il sistema di ricerca indexable: Indicizzabile languageForTranslation: Linguaggio di traduzione dei post +openServerInfo: Mostra informazioni sul server cliccando sul riquadro del server in + un post diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 9c78e1de52..2b82df1f8b 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -66,7 +66,7 @@ import: "インポート" export: "エクスポート" files: "ファイル" download: "ダウンロード" -driveFileDeleteConfirm: "ファイル「{name}」を削除しますか?これにより、このファイルが添付されている投稿も削除されます。" +driveFileDeleteConfirm: "ファイル「{name}」を削除しますか?これにより、添付ファイルとして含まれているすべての投稿から削除されます。" unfollowConfirm: "{name}さんのフォローを解除しますか?" exportRequested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、「ドライブ」に追加されます。" importRequested: "インポートをリクエストしました。これには時間がかかる場合があります。" diff --git a/locales/tr-TR.yml b/locales/tr-TR.yml index bda13aa909..709156fcf9 100644 --- a/locales/tr-TR.yml +++ b/locales/tr-TR.yml @@ -285,7 +285,7 @@ pinnedPagesDescription: Bu sunucunun üst kısmına sabitlemek istediğiniz Sayf yollarını satır sonundan ayırarak girin. enableHcaptcha: hCaptcha'yı Aktif Et notifyAntenna: Yeni gönderileri bildir -recentlyUpdatedUsers: En son aktif kullanıcılar +recentlyUpdatedUsers: En son aktif olan kullanıcılar about: Hakkında twoStepAuthentication: İki-adımlı doğrulama securityKeyName: Anahtar ismi diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml index 66983ae464..bc59ba62a5 100644 --- a/locales/zh-CN.yml +++ b/locales/zh-CN.yml @@ -486,15 +486,16 @@ hideThisNote: "隐藏这条帖子" showFeaturedNotesInTimeline: "在时间线上显示热门推荐" objectStorage: "对象存储" useObjectStorage: "使用对象存储" -objectStorageBaseUrl: "Base URL" -objectStorageBaseUrlDesc: "用于引用的 URL。如果您正在使用 CDN 或反向代理,请指定其 URL。\n例如S3:“https://.s3.amazonaws.com”,GCS:“https://storage.googleapis.com/”,其它同理。" +objectStorageBaseUrl: "根 URL" +objectStorageBaseUrlDesc: "用于引用的 URL。如果您正在使用 CDN 或反向代理,请指定其 URL。\n例如S3:\"https://.s3.amazonaws.com\"\ + ,GCS:\"https://storage.googleapis.com/\",其它同理。" objectStorageBucket: "存储桶" objectStorageBucketDesc: "请指定使用的对象存储服务的存储桶名称。" objectStoragePrefix: "前缀" objectStoragePrefixDesc: "文件将存储在此前缀的目录下。" -objectStorageEndpoint: "Endpoint" -objectStorageEndpointDesc: "如果您使用 AWS S3 请留空。否则请根据您使用的服务商的说明来进行设置,指定 Endpoint 形式为 - \"\" 或 \":\"。" +objectStorageEndpoint: "端点 (Endpoint)" +objectStorageEndpointDesc: "如果您使用 AWS S3 请留空。否则请根据您使用的服务商的说明来进行设置,指定端点 (Endpoint) + 形式为 \"\" 或 \":\"。" objectStorageRegion: "可用区" objectStorageRegionDesc: "指定一个可用区,例如 \"xx-east-1\"。 如果您的对象存储服务没有可用区概念,请将其留空或填写 \"\ us-east-1\"。\n对于 Cloudflare R2,可以填为 \"auto\"。" @@ -502,7 +503,7 @@ objectStorageUseSSL: "使用 SSL" objectStorageUseSSLDesc: "如果不使用 HTTPS 进行 API 连接,请关闭" objectStorageUseProxy: "使用代理" objectStorageUseProxyDesc: "如果您不使用代理进行 API 连接,请将其关闭" -objectStorageSetPublicRead: "上传时设置为 public-read" +objectStorageSetPublicRead: "上传时设置为 \"public-read\"" serverLogs: "服务器日志" deleteAll: "全部删除" showFixedPostForm: "在时间线顶部显示发帖框" @@ -599,7 +600,7 @@ emptyToDisableSmtpAuth: "留空用户名和密码以禁用 SMTP 验证" smtpSecure: "在 SMTP 连接中使用隐式 SSL / TLS" smtpSecureInfo: "使用 STARTTLS 时关闭" testEmail: "邮件发送测试" -wordMute: "文字过滤" +wordMute: "文字和语言过滤" regexpError: "正则表达式错误" regexpErrorDescription: "{tab} 文字过滤的第 {line} 行的正则表达式有错误:" instanceMute: "服务器静音" @@ -608,7 +609,7 @@ makeActive: "启用" display: "显示" copy: "复制" metrics: "指标" -overview: "服务器概况" +overview: "概况" logs: "日志" delayed: "滞后" database: "数据库" @@ -745,7 +746,7 @@ unlikeConfirm: "取消赞?" fullView: "全屏" quitFullView: "退出全屏" addDescription: "添加描述" -userPagePinTip: "在帖子的菜单中选择“置顶”,即可显示该条帖子。" +userPagePinTip: "在帖子的菜单中选择「置顶」,即可在此显示该条帖子。" notSpecifiedMentionWarning: "有未指定的提及" info: "关于" userInfo: "用户信息" @@ -805,7 +806,7 @@ accountDeletionInProgress: "正在删除账号" usernameInfo: "在服务器上唯一标识您的账号的名称。您可以使用字母 (a ~ z, A ~ Z)、数字 (0 ~ 9) 和下划线 (_)。用户名以后不能更改。" aiChanMode: "小蓝模式" keepCw: "保留内容警告" -pubSub: "推送 (Pub)/订阅 (Sub) 账号" +pubSub: "推送 (Pub) / 订阅 (Sub) 账号" lastCommunication: "最近通信" resolved: "已解决" unresolved: "未解决" @@ -1113,7 +1114,7 @@ _wordMute: muteLangs: "过滤语言" muteWordsDescription: "AND 条件用空格分隔,OR 条件用换行符分隔。" muteWordsDescription2: "将关键字用斜线括起来表示正则表达式。" - muteLangsDescription: "OR 条件用空格,换行符分隔" + muteLangsDescription: "OR 条件用空格或换行符分隔。" muteLangsDescription2: "使用语言代码。例: en, fr, ja, zh." softDescription: "隐藏时间线中指定条件的帖子。" langDescription: "从时间线中隐藏与设置语言匹配的帖子。" @@ -1839,8 +1840,8 @@ customMOTD: 自定义 MOTD(启动屏幕消息) sendPushNotificationReadMessageCaption: 会短暂显示 "{emptyPushNotificationMessage}" 的通知,如果启用,可能会增加您的设备的耗电量。 adminCustomCssWarn: 仅当您知道此设置的作用时才应使用它。输入不正确的值可能会导致每个人的客户端停止正常运行。请在用户设置中进行测试来确保您的 CSS 正常工作。 -customMOTDDescription: 自定义 MOTD(启动屏幕)消息,一行一个,每次用户加载/刷新页面时都会随机显示。 -customSplashIconsDescription: 用换行符隔开的自定义启动屏幕图标的 URL,在用户每次加载/重新载入页面时随机显示。请确保图片是在一个静态的 +customMOTDDescription: 自定义 MOTD(启动屏幕)消息,一行一个,每次用户加载 / 重新加载页面时都会随机显示。 +customSplashIconsDescription: 用换行符隔开的自定义启动屏幕图标的 URL,在用户每次加载 / 重新加载页面时随机显示。请确保图片是在一个静态的 URL 上,最好全部调整为 192x192 的大小。 recommendedInstancesDescription: 推荐的服务器一行一个,它们将出现在推荐时间线中。 splash: 启动画面 @@ -1863,7 +1864,7 @@ customSplashIcons: 自定义启动屏幕图标(urls) alt: 替代文字 pushNotificationNotSupported: 您的浏览器或者服务器不支持推送通知 showAds: 显示社区横幅 -enterSendsMessage: 按回车键发送信息(关闭则是 Ctrl + Retun 发送) +enterSendsMessage: 按回车键发送信息(关闭则是 Ctrl + Return 发送) recommendedInstances: 推荐服务器 updateAvailable: 可能有可用更新! swipeOnMobile: 允许在页面之间滑动 @@ -1976,7 +1977,7 @@ confirm: 确认 importZip: 导入 ZIP exportZip: 导出 ZIP emojiPackCreator: 表情包创建工具 -objectStorageS3ForcePathStyleDesc: 打开此选项可构建格式为 's3.amazonaws.com//' 而非 '.s3.amazonaws.com' +objectStorageS3ForcePathStyleDesc: 打开此选项可构建格式为 "s3.amazonaws.com//" 而非 ".s3.amazonaws.com" 的端点 URL。 objectStorageS3ForcePathStyle: 使用基于路径的端点 URL delete2fa: 禁用 2FA @@ -1989,3 +1990,5 @@ detectPostLanguage: 自动检测语言,并显示外文帖子的翻译按钮 indexableDescription: 允许内置搜索显示您的公开帖子 indexable: 可索引的 languageForTranslation: 帖子翻译语言 +vibrate: 播放振动 +openServerInfo: 点击帖子上的服务器滚动条时显示服务器信息 diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml index 9a1a0a75cc..d85893ca15 100644 --- a/locales/zh-TW.yml +++ b/locales/zh-TW.yml @@ -12,7 +12,7 @@ ok: "OK" gotIt: "知道了!" cancel: "取消" enterUsername: "輸入使用者名稱" -renotedBy: "{user} 轉傳了" +renotedBy: "{user} 轉發了" noNotes: "無貼文" noNotifications: "沒有通知" instance: "伺服器" @@ -292,7 +292,7 @@ inputNewFileName: "輸入檔案名稱" inputNewDescription: "請輸入新標題" inputNewFolderName: "輸入新資料夾的名稱" circularReferenceFolder: "目標文件夾是您要移動的文件夾的子文件夾。" -hasChildFilesOrFolders: "此文件夾不是空的,無法刪除。" +hasChildFilesOrFolders: "此資料夾不是空的,無法刪除。" copyUrl: "複製網址" rename: "重新命名" avatar: "大頭貼" @@ -325,7 +325,7 @@ connectService: "己連結" disconnectService: "己斷開" enableLocalTimeline: "開啟本地時間線" enableGlobalTimeline: "啟用公開時間線" -disablingTimelinesInfo: "即使您關閉了時間線功能,管理員和版主始終可以訪問所有的時間線。" +disablingTimelinesInfo: "即使您關閉了時間線功能,管理員和板主仍可訪問所有的時間線。" registration: "註冊" enableRegistration: "開啟新使用者註冊" invite: "邀請" @@ -773,7 +773,7 @@ gallery: "相簿" recentPosts: "最新貼文" popularPosts: "熱門的貼文" shareWithNote: "在貼文中分享" -ads: "廣告" +ads: "社群橫幅" expiration: "期限" memo: "備忘錄" priority: "優先級" @@ -801,7 +801,7 @@ translatedFrom: "從 {x} 翻譯" accountDeletionInProgress: "正在刪除帳戶" usernameInfo: "在伺服器上您的帳戶是唯一的識別名稱。您可以使用字母 (a ~ z, A ~ Z)、數字 (0 ~ 9) 和下底線 (_)。之後帳戶名是不能更改的。" aiChanMode: "小藍模式" -keepCw: "保持CW" +keepCw: "保持內容警告" pubSub: "Pub/Sub 帳戶" lastCommunication: "最近的通信" resolved: "已解決" @@ -1067,7 +1067,7 @@ _mfm: position: 位置 alwaysPlay: 自動播放所有MFM動畫 positionDescription: 按指定數量移動內容。 - advancedDescription: 如果禁用,則僅允許基本標記,除非正在播放 MFM 動畫 + advancedDescription: 如果停用,僅顯示基礎MFM及正在播放的MFM動畫 advanced: 高級MFM fade: 淡出 foreground: 文字顏色 @@ -1114,6 +1114,11 @@ _wordMute: soft: "軟性靜音" hard: "硬性靜音" mutedNotes: "已靜音的貼文" + muteLangsDescription2: '使用語言代碼。例: en, fr, ja, zh.' + lang: 語言 + langDescription: 將指定語言的貼文從時間線中隱藏。 + muteLangs: 被靜音的語言 + muteLangsDescription: OR條件以空格或換行進行分隔。 _instanceMute: instanceMuteDescription: "包括對被靜音伺服器上的用戶的回覆,被設定的伺服器上所有貼文及轉發都會被靜音。" instanceMuteDescription2: "設定時以換行進行分隔" @@ -1355,9 +1360,9 @@ _cw: files: "{count} 個檔案" _poll: noOnlyOneChoice: "至少需要兩個選項" - choiceN: "選擇{n}" + choiceN: "選項{n}" noMore: "沒辦法再添加選項了" - canMultipleVote: "可以多次投票" + canMultipleVote: "允許複選" expiration: "期限" infinite: "無期限" at: "結束時間" @@ -1366,7 +1371,7 @@ _poll: deadlineTime: "小時" duration: "時長" votesCount: "{n}票" - totalVotes: "一共{n}票" + totalVotes: "總計{n}票" vote: "投票" showResult: "顯示結果" voted: "已投票" @@ -1777,6 +1782,7 @@ _notification: reply: "回覆" renote: "轉發" reacted: 對您的貼文做出了反應 + renoted: 轉發了您的貼文 _deck: alwaysShowMainColumn: "總是顯示主欄" columnAlign: "對齊欄位" @@ -1823,13 +1829,13 @@ adminCustomCssWarn: 除非你知道它的作用,否則請不要使用此設定 CSS 正常工作。 showUpdates: Firefish 更新時顯示彈出視窗 recommendedInstances: 建議的伺服器 -caption: 自動字幕 +caption: 自動加上替代文字(alt) enterSendsMessage: 在 Messaging 中按 Return 發送消息 (如關閉則是 Ctrl + Return) migrationConfirm: "您確定要將你的帳戶遷移到 {account} 嗎? 一旦這樣做,你將無法復原,而你將無法再次正常使用您的帳戶。\n另外,請確保你已將此當前帳戶設置為您要遷移的帳戶。" customSplashIconsDescription: 每次用戶加載/重新加載頁面時,以換行符號分隔的自定啟動畫面圖標的網址將隨機顯示。請確保圖片位於靜態網址上,最好所有圖片解析度調整為 192x192。 accountMoved: '該使用者已遷移至新帳戶:' -showAds: 顯示廣告 +showAds: 顯示社群橫幅 noThankYou: 不用了,謝謝 selectInstance: 選擇伺服器 enableRecommendedTimeline: 啟用推薦時間線 @@ -1863,9 +1869,10 @@ silencedInstances: 已靜音的伺服器 silenced: 已靜音 _experiments: title: 試驗功能 + enablePostImports: 啟用匯入貼文的功能 findOtherInstance: 找找另一個伺服器 noGraze: 瀏覽器擴展 "Graze for Mastodon" 會與Firefish發生衝突,請停用該擴展。 -userSaysSomethingReasonRenote: '{name} 轉傳了包含 {reason} 的貼文' +userSaysSomethingReasonRenote: '{name} 轉發了包含 {reason} 的貼文' pushNotificationNotSupported: 你的瀏覽器或伺服器不支援推送通知 accessibility: 輔助功能 userSaysSomethingReasonReply: '{name} 回覆了包含 {reason} 的貼文' @@ -1909,7 +1916,7 @@ channelFederationWarn: 頻道功能尚未與聯邦宇宙連動 swipeOnMobile: 允許以滑動在頁面之間切換 sendPushNotificationReadMessage: 閱讀相關通知或消息後刪除推送通知 image: 圖片 -seperateRenoteQuote: 分別獨立的轉傳及引用按鈕 +seperateRenoteQuote: 分開轉發及引用的按鈕 clipsDesc: 摘錄就像一個可以分享的書籤。 你可以從每個貼文的菜單創建新摘錄或將貼文加入已有的摘錄。 noteId: 貼文 ID sendModMail: 發送審核通知 @@ -1919,7 +1926,7 @@ reactionPickerSkinTone: 首選表情符號膚色 indexFromDescription: 留空以索引每個貼文 preventAiLearning: 防止 AI 機器人抓取 preventAiLearningDescription: 請求第三方 AI 語言模型不要研究您上傳的內容,例如貼文和圖像。 -indexFrom: 從貼文 ID 開始的索引 +indexFrom: 建立此貼文ID以後的索引 isLocked: 該帳戶已獲得以下批准 isModerator: 板主 isAdmin: 管理員 @@ -1927,7 +1934,7 @@ isPatron: Firefish 項目贊助者 silencedWarning: 顯示此頁面是因為這些使用者來自您伺服器管理員已靜音的伺服器,因此他們可能是垃圾訊息。 signupsDisabled: 該伺服器上的註冊當前已被禁用,但您隨時可以在另一台伺服器上註冊!或是您有該伺服器的邀請碼,請在下面輸入。 showPopup: 通過彈出式視窗通知用戶 -showWithSparkles: 閃閃發光的顯示 +showWithSparkles: 讓標題閃閃發光 youHaveUnreadAnnouncements: 您有未讀的公告 donationLink: 連結到贊助頁面 neverShow: 不再顯示 @@ -1962,3 +1969,11 @@ emojiPackCreator: 表情包的作者 importZip: 匯入ZIP delete2fa: 停用二階段認證(2FA) confirm: 確認 +deletePasskeysConfirm: 此帳號的所有通行密鑰及安全密鑰將被完全刪除。此動作無法復原,是否繼續? +deletePasskeys: 刪除通行密鑰 +detectPostLanguage: 自動判定貼文的語言,並在外文貼文顯示翻譯按鈕 +indexableDescription: 允許內建搜尋引擎顯示您的公開貼文 +addRe: 在回覆有內容警告的貼文時,在標題前面加上 "re:" +vibrate: 播放振動 +openServerInfo: 點擊貼文中的伺服器名稱以顯示伺服器資訊 +languageForTranslation: 貼文翻譯語言 diff --git a/package.json b/package.json index 4f381e0689..721abfa93c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firefish", - "version": "1.0.5-dev14", + "version": "1.0.5-dev16", "codename": "aqua", "repository": { "type": "git", diff --git a/packages/backend/package.json b/packages/backend/package.json index 4e648ad30a..a5f0124c0b 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -145,7 +145,7 @@ }, "devDependencies": { "@swc/cli": "^0.1.62", - "@swc/core": "1.3.82", + "@swc/core": "1.3.78", "@types/adm-zip": "^0.5.0", "@types/bcryptjs": "2.4.2", "@types/escape-regexp": "0.0.1", diff --git a/packages/backend/src/mfm/from-html.ts b/packages/backend/src/mfm/from-html.ts index 7c956e9058..fbcf310252 100644 --- a/packages/backend/src/mfm/from-html.ts +++ b/packages/backend/src/mfm/from-html.ts @@ -110,9 +110,8 @@ export function fromHtml(html: string, hashtagNames?: string[]): string { } case "h1": { - text += "【"; appendChildren(node.childNodes); - text += "】\n"; + text += "\n"; break; } diff --git a/packages/backend/src/misc/get-reaction-emoji.ts b/packages/backend/src/misc/get-reaction-emoji.ts deleted file mode 100644 index 71521c4ae8..0000000000 --- a/packages/backend/src/misc/get-reaction-emoji.ts +++ /dev/null @@ -1,28 +0,0 @@ -export default function (reaction: string): string { - switch (reaction) { - case "like": - return "👍"; - case "love": - return "❤️"; - case "laugh": - return "😆"; - case "hmm": - return "🤔"; - case "surprise": - return "😮"; - case "congrats": - return "🎉"; - case "angry": - return "💢"; - case "confused": - return "😥"; - case "rip": - return "😇"; - case "pudding": - return "🍮"; - case "star": - return "⭐"; - default: - return reaction; - } -} diff --git a/packages/backend/src/misc/reaction-lib.ts b/packages/backend/src/misc/reaction-lib.ts index f8bec79c00..cad99a6411 100644 --- a/packages/backend/src/misc/reaction-lib.ts +++ b/packages/backend/src/misc/reaction-lib.ts @@ -6,21 +6,7 @@ import { IsNull } from "typeorm"; import { EmojiCache } from "@/misc/populate-emojis.js"; import type { Emoji } from "@/models/entities/emoji.js"; -const legacies = new Map([ - ["like", "👍"], - ["love", "❤️"], - ["laugh", "😆"], - ["hmm", "🤔"], - ["surprise", "😮"], - ["congrats", "🎉"], - ["angry", "💢"], - ["confused", "😥"], - ["rip", "😇"], - ["pudding", "🍮"], - ["star", "⭐"], -]); - -async function getFallbackReaction() { +export async function getFallbackReaction() { const meta = await fetchMeta(); const name = meta.defaultReaction; @@ -40,39 +26,17 @@ async function getFallbackReaction() { return { name, emoji }; } -export function convertLegacyReactions(reactions: Record) { - const _reactions = new Map(); - const decodedReactions = new Map(); +export function convertReactions(reactions: Record) { + const result = new Map(); for (const reaction in reactions) { if (reactions[reaction] <= 0) continue; - let decodedReaction; - if (decodedReactions.has(reaction)) { - decodedReaction = decodedReactions.get(reaction); - } else { - decodedReaction = decodeReaction(reaction); - decodedReactions.set(reaction, decodedReaction); - } - - const emoji = legacies.get(decodedReaction.reaction); - if (emoji) { - _reactions.set(emoji, (_reactions.get(emoji) || 0) + reactions[reaction]); - } else { - _reactions.set( - reaction, - (_reactions.get(reaction) || 0) + reactions[reaction], - ); - } + const decoded = decodeReaction(reaction).reaction; + result.set(decoded, (result.get(decoded) || 0) + reactions[reaction]); } - const _reactions2 = new Map(); - for (const [reaction, count] of _reactions) { - const decodedReaction = decodedReactions.get(reaction); - _reactions2.set(decodedReaction.reaction, count); - } - - return Object.fromEntries(_reactions2); + return Object.fromEntries(result); } export async function toDbReaction( @@ -83,9 +47,7 @@ export async function toDbReaction( const _reacterHost = toPunyNullable(reacterHost); - // Convert string-type reactions to unicode - const emoji = legacies.get(reaction) || (reaction === "♥️" ? "❤️" : null); - if (emoji) return { name: emoji, emoji: null }; + if (reaction === "♥️") return { name: "❤️", emoji: null }; // Allow unicode reactions const match = emojiRegex.exec(reaction); @@ -152,9 +114,3 @@ export function decodeReaction(str: string): DecodedReaction { host: undefined, }; } - -export function convertLegacyReaction(reaction: string): string { - const decoded = decodeReaction(reaction).reaction; - if (legacies.has(decoded)) return legacies.get(decoded)!; - return decoded; -} diff --git a/packages/backend/src/models/repositories/note-reaction.ts b/packages/backend/src/models/repositories/note-reaction.ts index 9597564fc8..d1e2696ae7 100644 --- a/packages/backend/src/models/repositories/note-reaction.ts +++ b/packages/backend/src/models/repositories/note-reaction.ts @@ -2,7 +2,7 @@ import { db } from "@/db/postgre.js"; import { NoteReaction } from "@/models/entities/note-reaction.js"; import { Notes, Users } from "../index.js"; import type { Packed } from "@/misc/schema.js"; -import { convertLegacyReaction } from "@/misc/reaction-lib.js"; +import { decodeReaction } from "@/misc/reaction-lib.js"; import type { User } from "@/models/entities/user.js"; export const NoteReactionRepository = db.getRepository(NoteReaction).extend({ @@ -27,7 +27,7 @@ export const NoteReactionRepository = db.getRepository(NoteReaction).extend({ id: reaction.id, createdAt: reaction.createdAt.toISOString(), user: await Users.pack(reaction.user ?? reaction.userId, me), - type: convertLegacyReaction(reaction.reaction), + type: decodeReaction(reaction.reaction).reaction, ...(opts.withNote ? { // may throw error diff --git a/packages/backend/src/models/repositories/note.ts b/packages/backend/src/models/repositories/note.ts index ee5b179760..62d99d3613 100644 --- a/packages/backend/src/models/repositories/note.ts +++ b/packages/backend/src/models/repositories/note.ts @@ -14,11 +14,7 @@ import { import type { Packed } from "@/misc/schema.js"; import { nyaize } from "@/misc/nyaize.js"; import { awaitAll } from "@/prelude/await-all.js"; -import { - convertLegacyReaction, - convertLegacyReactions, - decodeReaction, -} from "@/misc/reaction-lib.js"; +import { convertReactions, decodeReaction } from "@/misc/reaction-lib.js"; import type { NoteReaction } from "@/models/entities/note-reaction.js"; import { aggregateNoteEmojis, @@ -38,7 +34,7 @@ import { } from "@/db/scylla.js"; import { LocalFollowingsCache } from "@/misc/cache.js"; import { userByIdCache } from "@/services/user-cache.js"; -import { detect as detectLanguage_ } from "tinyld"; +import { detect as detectLanguage } from "tinyld"; export async function populatePoll( note: Note | ScyllaNote, @@ -136,7 +132,7 @@ async function populateMyReaction( if (_hint_?.myReactions) { const reaction = _hint_.myReactions.get(note.id); if (reaction) { - return convertLegacyReaction(reaction.reaction); + return decodeReaction(reaction.reaction).reaction; } else if (reaction === null) { return undefined; } @@ -161,7 +157,7 @@ async function populateMyReaction( } if (reaction) { - return convertLegacyReaction(reaction.reaction); + return decodeReaction(reaction.reaction).reaction; } return undefined; @@ -304,7 +300,8 @@ export const NoteRepository = db.getRepository(Note).extend({ host, ); - const lang = detectLanguage_(`${note.cw ?? ''}\n${note.text ?? ''}`) ?? "unknown" + const lang = + detectLanguage(`${note.cw ?? ""}\n${note.text ?? ""}`) ?? "unknown"; const reactionEmoji = await populateEmojis(reactionEmojiNames, host); const packed: Packed<"Note"> = await awaitAll({ id: note.id, @@ -321,7 +318,7 @@ export const NoteRepository = db.getRepository(Note).extend({ note.visibility === "specified" ? note.visibleUserIds : undefined, renoteCount: note.renoteCount, repliesCount: note.repliesCount, - reactions: convertLegacyReactions(note.reactions), + reactions: convertReactions(note.reactions), reactionEmojis: reactionEmoji, emojis: noteEmoji, tags: note.tags.length > 0 ? note.tags : undefined, diff --git a/packages/backend/src/remote/activitypub/models/note.ts b/packages/backend/src/remote/activitypub/models/note.ts index 84d38289f9..3da3321a7d 100644 --- a/packages/backend/src/remote/activitypub/models/note.ts +++ b/packages/backend/src/remote/activitypub/models/note.ts @@ -258,7 +258,7 @@ export async function createNote( // Quote let quote: Note | undefined | null; - if (note._misskey_quote || note.quoteUrl || note.quoteUri) { + if (note.quoteUrl || note.quoteUri) { const tryResolveNote = async ( uri: string, ): Promise< @@ -295,7 +295,7 @@ export async function createNote( }; const uris = unique( - [note._misskey_quote, note.quoteUrl, note.quoteUri].filter( + [note.quoteUrl, note.quoteUri].filter( (x): x is string => typeof x === "string", ), ); @@ -321,8 +321,6 @@ export async function createNote( typeof note.source?.content === "string" ) { text = note.source.content; - } else if (typeof note._misskey_content !== "undefined") { - text = note._misskey_content; } else if (typeof note.content === "string") { text = htmlToMfm(note.content, note.tag); } @@ -624,8 +622,6 @@ export async function updateNote(value: string | IObject, resolver?: Resolver) { typeof post.source?.content === "string" ) { text = post.source.content; - } else if (typeof post._misskey_content !== "undefined") { - text = post._misskey_content; } else if (typeof post.content === "string") { text = htmlToMfm(post.content, post.tag); } diff --git a/packages/backend/src/remote/activitypub/models/person.ts b/packages/backend/src/remote/activitypub/models/person.ts index 6a41958c12..5be2363afe 100644 --- a/packages/backend/src/remote/activitypub/models/person.ts +++ b/packages/backend/src/remote/activitypub/models/person.ts @@ -306,7 +306,7 @@ export async function createPerson( tags, isBot, isCat: (person as any).isCat === true, - speakAsCat: person.speakAsCat, + speakAsCat: (person as any).speakAsCat === true, isIndexable: person.indexable, }), )) as IRemoteUser; @@ -557,6 +557,7 @@ export async function updatePerson( tags, isBot: getApType(object) !== "Person", isCat: (person as any).isCat === true, + speakAsCat: (person as any).speakAsCat === true, isIndexable: person.indexable, isLocked: !!person.manuallyApprovesFollowers, movedToUri: person.movedTo || null, diff --git a/packages/backend/src/remote/activitypub/renderer/index.ts b/packages/backend/src/remote/activitypub/renderer/index.ts index bb5dcdfc19..7d2eb95288 100644 --- a/packages/backend/src/remote/activitypub/renderer/index.ts +++ b/packages/backend/src/remote/activitypub/renderer/index.ts @@ -35,13 +35,14 @@ export const renderActivity = (x: any): IActivity | null => { schema: "http://schema.org#", PropertyValue: "schema:PropertyValue", value: "schema:value", + // Firefish + firefish: "https://joinfirefish.org/ns#", + speakAsCat: "firefish:speakAsCat", // Misskey misskey: "https://misskey-hub.net/ns#", - _misskey_content: "misskey:_misskey_content", - _misskey_quote: "misskey:_misskey_quote", + _misskey_talk: "misskey:_misskey_talk", _misskey_reaction: "misskey:_misskey_reaction", _misskey_votes: "misskey:_misskey_votes", - _misskey_talk: "misskey:_misskey_talk", isCat: "misskey:isCat", // Fedibird fedibird: "http://fedibird.com/ns#", diff --git a/packages/backend/src/remote/activitypub/renderer/note.ts b/packages/backend/src/remote/activitypub/renderer/note.ts index 93a31c9908..40e3a102e6 100644 --- a/packages/backend/src/remote/activitypub/renderer/note.ts +++ b/packages/backend/src/remote/activitypub/renderer/note.ts @@ -1,4 +1,5 @@ import { In, IsNull } from "typeorm"; +import { detect as detectLanguage } from "tinyld"; import config from "@/config/index.js"; import type { Note, IMentionedRemoteUsers } from "@/models/entities/note.js"; import type { DriveFile } from "@/models/entities/drive-file.js"; @@ -157,6 +158,11 @@ export default async function renderNote( }), ); + const lang = detectLanguage(text); + const contentMap = lang ? { + [lang]: content + } : null; + const emojis = await getEmojis(note.emojis); const apemojis = emojis.map((emoji) => renderEmoji(emoji)); @@ -221,12 +227,11 @@ export default async function renderNote( attributedTo, summary, content, - _misskey_content: text, + contentMap, source: { content: text, mediaType: "text/x.misskeymarkdown", }, - _misskey_quote: quote, quoteUri: quote, quoteUrl: quote, published: note.createdAt.toISOString(), diff --git a/packages/backend/src/remote/activitypub/type.ts b/packages/backend/src/remote/activitypub/type.ts index ecaf6d6872..0e63f3ed55 100644 --- a/packages/backend/src/remote/activitypub/type.ts +++ b/packages/backend/src/remote/activitypub/type.ts @@ -14,6 +14,7 @@ export interface IObject { inReplyTo?: any; replies?: ICollection; content?: string; + contentMap?: obj; name?: string; startTime?: Date; endTime?: Date; @@ -134,7 +135,6 @@ export interface IPost extends IObject { content: string; mediaType: string; }; - _misskey_quote?: string; quoteUrl?: string; quoteUri?: string; _misskey_talk: boolean; @@ -146,7 +146,6 @@ export interface IQuestion extends IObject { content: string; mediaType: string; }; - _misskey_quote?: string; quoteUrl?: string; oneOf?: IQuestionChoice[]; anyOf?: IQuestionChoice[]; diff --git a/packages/backend/test/e2e/users.ts b/packages/backend/test/e2e/users.ts index 1090dc1cb5..86e254aebf 100644 --- a/packages/backend/test/e2e/users.ts +++ b/packages/backend/test/e2e/users.ts @@ -69,6 +69,7 @@ describe("ユーザー", () => { avatarBlurhash: user.avatarBlurhash, isBot: user.isBot, isCat: user.isCat, + speakAsCat: user.speakAsCat, instance: user.instance, emojis: user.emojis, onlineStatus: user.onlineStatus, @@ -401,6 +402,7 @@ describe("ユーザー", () => { assert.strictEqual(response.avatarBlurhash, null); assert.strictEqual(response.isBot, false); assert.strictEqual(response.isCat, false); + assert.strictEqual(response.speakAsCat, false); assert.strictEqual(response.instance, undefined); assert.deepStrictEqual(response.emojis, {}); assert.strictEqual(response.onlineStatus, "unknown"); @@ -538,6 +540,8 @@ describe("ユーザー", () => { { parameters: (): object => ({ isBot: false }) }, { parameters: (): object => ({ isCat: true }) }, { parameters: (): object => ({ isCat: false }) }, + { parameters: (): object => ({ speakAsCat: true }) }, + { parameters: (): object => ({ speakAsCat: false }) }, { parameters: (): object => ({ injectFeaturedNote: true }) }, { parameters: (): object => ({ injectFeaturedNote: false }) }, { parameters: (): object => ({ receiveAnnouncementEmail: true }) }, diff --git a/packages/backend/test/reaction-lib.ts b/packages/backend/test/reaction-lib.ts deleted file mode 100644 index 7c61dc76c2..0000000000 --- a/packages/backend/test/reaction-lib.ts +++ /dev/null @@ -1,83 +0,0 @@ -/* -import * as assert from 'assert'; - -import { toDbReaction } from '../src/misc/reaction-lib.js'; - -describe('toDbReaction', async () => { - it('既存の文字列リアクションはそのまま', async () => { - assert.strictEqual(await toDbReaction('like'), 'like'); - }); - - it('Unicodeプリンは寿司化不能とするため文字列化しない', async () => { - assert.strictEqual(await toDbReaction('🍮'), '🍮'); - }); - - it('プリン以外の既存のリアクションは文字列化する like', async () => { - assert.strictEqual(await toDbReaction('👍'), 'like'); - }); - - it('プリン以外の既存のリアクションは文字列化する love', async () => { - assert.strictEqual(await toDbReaction('❤️'), 'love'); - }); - - it('プリン以外の既存のリアクションは文字列化する love 異体字セレクタなし', async () => { - assert.strictEqual(await toDbReaction('❤'), 'love'); - }); - - it('プリン以外の既存のリアクションは文字列化する laugh', async () => { - assert.strictEqual(await toDbReaction('😆'), 'laugh'); - }); - - it('プリン以外の既存のリアクションは文字列化する hmm', async () => { - assert.strictEqual(await toDbReaction('🤔'), 'hmm'); - }); - - it('プリン以外の既存のリアクションは文字列化する surprise', async () => { - assert.strictEqual(await toDbReaction('😮'), 'surprise'); - }); - - it('プリン以外の既存のリアクションは文字列化する congrats', async () => { - assert.strictEqual(await toDbReaction('🎉'), 'congrats'); - }); - - it('プリン以外の既存のリアクションは文字列化する angry', async () => { - assert.strictEqual(await toDbReaction('💢'), 'angry'); - }); - - it('プリン以外の既存のリアクションは文字列化する confused', async () => { - assert.strictEqual(await toDbReaction('😥'), 'confused'); - }); - - it('プリン以外の既存のリアクションは文字列化する rip', async () => { - assert.strictEqual(await toDbReaction('😇'), 'rip'); - }); - - it('それ以外はUnicodeのまま', async () => { - assert.strictEqual(await toDbReaction('🍅'), '🍅'); - }); - - it('異体字セレクタ除去', async () => { - assert.strictEqual(await toDbReaction('㊗️'), '㊗'); - }); - - it('異体字セレクタ除去 必要なし', async () => { - assert.strictEqual(await toDbReaction('㊗'), '㊗'); - }); - - it('fallback - undefined', async () => { - assert.strictEqual(await toDbReaction(undefined), 'like'); - }); - - it('fallback - null', async () => { - assert.strictEqual(await toDbReaction(null), 'like'); - }); - - it('fallback - empty', async () => { - assert.strictEqual(await toDbReaction(''), 'like'); - }); - - it('fallback - unknown', async () => { - assert.strictEqual(await toDbReaction('unknown'), 'like'); - }); -}); -*/ diff --git a/packages/client/src/components/MkGoogle.vue b/packages/client/src/components/MkGoogle.vue index 4244bb22d4..c014607db9 100644 --- a/packages/client/src/components/MkGoogle.vue +++ b/packages/client/src/components/MkGoogle.vue @@ -22,7 +22,7 @@ const props = defineProps<{ const query = ref(props.q); const search = () => { - router.push(`/search/${query.value}`); + router.push(`/search?q=${query.value}`); }; diff --git a/packages/client/src/components/global/MkAvatar.vue b/packages/client/src/components/global/MkAvatar.vue index cf780009ad..5000dcb874 100644 --- a/packages/client/src/components/global/MkAvatar.vue +++ b/packages/client/src/components/global/MkAvatar.vue @@ -89,7 +89,7 @@ watch(