diff --git a/.config/example.yml b/.config/example.yml index 8e881c1879..9d20e44a0b 100644 --- a/.config/example.yml +++ b/.config/example.yml @@ -113,14 +113,9 @@ id: 'aid' reservedUsernames: - root - admin + - administrator + - me - system - - test - - proxy - - relay - - mod - - moderator - - info - - information # Whether disable HSTS #disableHsts: true @@ -152,6 +147,7 @@ reservedUsernames: #proxy: http://127.0.0.1:3128 #proxyBypassHosts: [ +# 'web.kaiteki.app', # 'example.com', # '192.0.2.8' #] diff --git a/README.md b/README.md index af117ac4aa..d05cb55db8 100644 --- a/README.md +++ b/README.md @@ -27,9 +27,9 @@ - Notable differences: - Improved UI/UX (especially on mobile) - Improved notifications - - Fediverse account migration - Improved instance security - Improved accessibility + - Improved threads - Recommended Instances timeline - OCR image captioning - New and improved Groups diff --git a/locales/ca-ES.yml b/locales/ca-ES.yml index 3de877dbe1..24e4b6d7d1 100644 --- a/locales/ca-ES.yml +++ b/locales/ca-ES.yml @@ -1,7 +1,7 @@ _lang_: "Català" headlineMisskey: "Una xarxa social de codi obert, descentralitzada i gratuita per\ \ sempre \U0001F680" -introMisskey: "Benvinguts! Calckey es una plataforma social de codi obert, descentralitzada\ +introMisskey: "Benvinguts! Calckey es una plataforma social de codi obert, descentralitzada\ \ i gratuita per sempre! \U0001F680" monthAndDay: "{day}/{month}" search: "Cercar" @@ -15,43 +15,43 @@ gotIt: "Ho he entès!" cancel: "Cancel·lar" enterUsername: "Introdueix el teu nom d'usuari" renotedBy: "Resignat per {user}" -noNotes: "Cap nota" +noNotes: "Cap publicació" noNotifications: "Cap notificació" -instance: "Instàncies" +instance: "Instància" settings: "Preferències" basicSettings: "Configuració bàsica" -otherSettings: "Configuració avançada" -openInWindow: "Obrir en una nova finestra" +otherSettings: "Altres opcions" +openInWindow: "Obrir en una finestra nova" profile: "Perfil" timeline: "Línia de temps" noAccountDescription: "Aquest usuari encara no ha escrit la seva biografia." login: "Iniciar sessió" -loggingIn: "Identificant-se" -logout: "Tancar la sessió" +loggingIn: "Iniciant sessió" +logout: "Tancar sessió" signup: "Registrar-se" uploading: "Pujant..." save: "Desar" users: "Usuaris" addUser: "Afegir un usuari" -favorite: "Afegir a preferits" +favorite: "Afegir a favorits" favorites: "Favorits" -unfavorite: "Eliminar dels preferits" -favorited: "Afegit als preferits." -alreadyFavorited: "Ja s'ha afegit als preferits." -cantFavorite: "No s'ha pogut afegir als preferits." +unfavorite: "Eliminar de favorits" +favorited: "Afegit a favorits." +alreadyFavorited: "Ja s'ha afegit a favorits." +cantFavorite: "No s'ha pogut afegir a favorits." pin: "Fixar al perfil" -unpin: "Para de fixar del perfil" -copyContent: "Copiar el contingut" -copyLink: "Copiar l'enllaç" -delete: "Eliminar" -deleteAndEdit: "Esborrar i editar" -deleteAndEditConfirm: "Estàs segur que vols suprimir aquesta nota i editar-la? Perdràs\ - \ totes les reaccions, notes i respostes." -addToList: "Afegir a una llista" +unpin: "Deixar de fixar al perfil" +copyContent: "Còpia el contingut" +copyLink: "Còpia l'enllaç" +delete: "Esborra" +deleteAndEdit: "Esborrar i edita" +deleteAndEditConfirm: "Estàs segur que vols esborrar aquesta nota i editar-la? Perdràs\ + \ totes les reaccions, resignats i respostes." +addToList: "Afegir a la llista" sendMessage: "Enviar un missatge" -copyUsername: "Copiar nom d'usuari" -searchUser: "Cercar usuaris" -reply: "Respondre" +copyUsername: "Còpia nom d'usuari" +searchUser: "Cercar un usuari" +reply: "Respon" loadMore: "Carregar més" showMore: "Veure més" youGotNewFollower: "t'ha seguit" @@ -60,21 +60,21 @@ followRequestAccepted: "Sol·licitud de seguiment acceptada" mention: "Menció" mentions: "Mencions" directNotes: "Missatges directes" -importAndExport: "Importar / Exportar" +importAndExport: "Importar / Exportar Dades" import: "Importar" export: "Exportar" files: "Fitxers" -download: "Baixar" -driveFileDeleteConfirm: "Estàs segur que vols suprimir el fitxer \"{name}\"? Les notes\ - \ associades a aquest fitxer adjunt també se suprimiran." +download: "Descarregar" +driveFileDeleteConfirm: "Estàs segur que vols suprimir el fitxer \"{name}\"? Les publicacions\ + \ associades a aquest fitxer adjunt també es suprimiran." unfollowConfirm: "Estàs segur que vols deixar de seguir {name}?" exportRequested: "Has sol·licitat una exportació. Això pot trigar una estona. S'afegirà\ - \ a la teva unitat un cop completat." + \ al teu Disc un cop completada." importRequested: "Has sol·licitat una importació. Això pot trigar una estona." lists: "Llistes" noLists: "No tens cap llista" -note: "Post" -notes: "Posts" +note: "Publicació" +notes: "Publicacions" following: "Seguint" followers: "Seguidors" followsYou: "Et segueix" @@ -83,7 +83,7 @@ manageLists: "Gestionar les llistes" error: "Error" somethingHappened: "S'ha produït un error" retry: "Torna-ho a intentar" -pageLoadError: "S'ha produït un error en carregar la pàgina" +pageLoadError: "Alguna cosa a sortit malament al carregar la pàgina." pageLoadErrorDescription: "Això normalment es deu a errors de xarxa o a la memòria\ \ cau del navegador. Prova d'esborrar la memòria cau i torna-ho a provar després\ \ d'esperar una estona." @@ -100,13 +100,13 @@ followRequests: "Sol·licituds de seguiment" unfollow: "Deixar de seguir" followRequestPending: "Sol·licituds de seguiment pendents" enterEmoji: "Introduir un emoji" -renote: "Renotar" -unrenote: "Anul·lar renota" -renoted: "Renotat." -cantRenote: "Aquesta publicació no pot ser renotada." -cantReRenote: "Impossible renotar una renota." +renote: "Impulsà" +unrenote: "Anul·lar impuls" +renoted: "Impulsat." +cantRenote: "Aquesta publicació no pot ser impulsada." +cantReRenote: "No es pot impulsar un impuls." quote: "Citar" -pinnedNote: "Nota fixada" +pinnedNote: "Publicació fixada" pinned: "Fixar al perfil" you: "Tu" clickToShow: "Fes clic per mostrar" @@ -116,7 +116,7 @@ reaction: "Reaccions" reactionSetting: "Reaccions a mostrar al selector de reaccions" reactionSettingDescription2: "Arrossega per reordenar, fes clic per suprimir, prem\ \ \"+\" per afegir." -rememberNoteVisibility: "Recorda la configuració de visibilitat de les notes" +rememberNoteVisibility: "Recorda la configuració de visibilitat de les publicacions" attachCancel: "Eliminar el fitxer adjunt" markAsSensitive: "Marcar com a NSFW" unmarkAsSensitive: "Deixar de marcar com a sensible" @@ -130,7 +130,7 @@ unsuspend: "Deixa de suspendre" instances: "Instàncies" remove: "Eliminar" nsfw: "NSFW" -pinnedNotes: "Nota fixada" +pinnedNotes: "Publicació fixada" userList: "Llistes" smtpUser: "Nom d'usuari" smtpPass: "Contrasenya" @@ -147,7 +147,7 @@ _mfm: _theme: keys: mention: "Menció" - renote: "Renotar" + renote: "Impulsar" _sfx: note: "Posts" notification: "Notificacions" @@ -191,12 +191,12 @@ _notification: _types: follow: "Seguint" mention: "Menció" - renote: "Renotar" + renote: "Impulsos" quote: "Citar" reaction: "Reaccions" _actions: reply: "Respondre" - renote: "Renotar" + renote: "Impulsos" _deck: _columns: notifications: "Notificacions" @@ -469,3 +469,209 @@ enableLocalTimeline: Activa la línea de temps local enableRecommendedTimeline: Activa la línea de temps de recomanats pinnedClipId: ID del clip que vols fixar hcaptcha: hCaptcha +manageAntennas: Gestiona les Antenes +name: Nom +notesAndReplies: Articles i respostes +silence: Posa en silenci +withFiles: Amb fitxers +popularUsers: Usuaris populars +exploreUsersCount: Hi han {count} usuaris +exploreFediverse: Explora el Fesiverse +popularTags: Etiquetes populars +about: Sobre +recentlyUpdatedUsers: Usuaris actius fa poc +recentlyRegisteredUsers: Usuaris registrats fa poc +recentlyDiscoveredUsers: Nous suaris descoberts +administrator: Administrador +token: Token +registerSecurityKey: Registra una clau de seguretat +securityKeyName: Nom clau +lastUsed: Feta servir per última vegada +unregister: Anul·lar el registre +passwordLessLogin: Identificació sense contrasenya +share: Comparteix +notFound: No s'ha trobat +newPasswordIs: La nova contrasenya és "{password}" +notFoundDescription: No es pot trobar cap pàgina que correspongui a aquesta adreça + URL. +uploadFolder: Carpeta per defecte per pujar arxius +cacheClear: Netejar la memòria cau +markAsReadAllNotifications: Marca totes les notificacions com llegides +markAsReadAllUnreadNotes: Marca totes les publicacions com a llegides +markAsReadAllTalkMessages: Marca tots els missatges com llegits +help: Ajuda +inputMessageHere: Escriu aquí el missatge +close: Tancar +group: Grup +groups: Grups +createGroup: Crea un grup +ownedGroups: Grups que et pertanyen +joinedGroups: Grups als que t'has unit +groupName: Nom del grup +members: Membres +transfer: Transferir +messagingWithUser: Conversa privada +title: Títol +text: Text +enable: Activar +next: Següent +retype: Torna a entrar +noteOf: Publicat per {user} +inviteToGroup: Invitar a un grup +quoteAttached: Cita +quoteQuestion: Adjuntar com a cita? +noMessagesYet: Encara no hi han missatges +signinRequired: Si us plau registrat o inicia sessió per continuar +invitations: Invitacions +invitationCode: Codi d'invitació +checking: Comprovant... +usernameInvalidFormat: Pots fer servir lletres en majúscules o minúscules, nombres + i guions baixos. +tooShort: Massa curt +tooLong: Massa llarg +weakPassword: Contrasenya amb seguretat feble +strongPassword: Contrasenya amb seguretat forta +passwordMatched: Coincidències +signinWith: Inicieu sessió com {x} +signinFailed: No es pot iniciar sessió. El nom d'usuari o la contrasenya són incorrectes. +or: O +language: Idioma +uiLanguage: Idioma de la interfície d'usuari +groupInvited: T'han invitat a un grup +aboutX: Sobre {x} +youHaveNoGroups: No tens grups +disableDrawer: No facis servir els menús amb estil de calaix +noHistory: No ha historial disponible +signinHistory: Historial d'inicis de sessió +disableAnimatedMfm: Desactiva les animacions amb MFM +doing: Processant... +category: Categoría +existingAccount: El compte ja existeix +regenerate: Regenerar +docSource: Font d'aquest document +createAccount: Crear compte +fontSize: Mida del text +noFollowRequests: No tens cap sol·licitud de seguiment per aprovar +openImageInNewTab: Obre les imatges en una pestanya nova +dashboard: Panell +local: Local +remote: Remot +total: Total +weekOverWeekChanges: Canvis d'ençà la passada setmana +dayOverDayChanges: Canvis d'ençà ahir +appearance: Aparença +clientSettings: Configuració del client +accountSettings: Configuració del compte +promotion: Promogut +promote: Promoure +numberOfDays: Nombre de dies +objectStorageBaseUrl: Adreça URL base +hideThisNote: Amaga aquest article +showFeaturedNotesInTimeline: Mostra els articles destacats a la línea de temps +objectStorage: Emmagatzematge d'objectes +useObjectStorage: Fes servir l'emmagatzema d'objectes +expandTweet: Amplia el tuit +themeEditor: Editor de temes +description: Descripció +leaveConfirm: Hi han canvis que no s'han desat. Els vols descartar? +manage: Administració +plugins: Afegits +preferencesBackups: Preferències de còpies de seguretat +undeck: Treure el Deck +useBlurEffectForModal: Fes servir efectes de difuminació en les finestres modals +useFullReactionPicker: Fes servir el selector de reaccions a tamany complert +deck: Deck +width: Amplada +generateAccessToken: Genera un token d'accés +medium: Mitja +small: Petit +permission: Permisos +enableAll: Activa tots +tokenRequested: Garantir accés al compte +pluginTokenRequestedDescription: Aquest afegit podrà fer servir els permisos configurats + aquí. +emailServer: Servidor de correu electrònic +notificationType: Tipus de notificació +edit: Editar +emailAddress: Adreça de Correu electrònic +smtpConfig: Configuració del servidor SMTP +smtpHost: Host +enableEmail: Activa la distribució de correu electrònic +smtpPort: Port +emailConfigInfo: Fet servir per confirmar les adreçats de correu electrònic al registrar-se + o si s'oblida la contrasenya +email: Correu electrònic +smtpSecure: Fes servir SSL/TLS implícit per connectar-se per SMTP +emptyToDisableSmtpAuth: Deixa el nom d'usuari i la contrasenya sense emplenar per + desactivar la verificació SMTP +smtpSecureInfo: Desactiva això quant facis servir STARTTLS +testEmail: Envia un correu electrònic de verificació +wordMute: Silenciar paraules +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:' +userSaysSomething: '{name} va dir alguna cosa' +instanceMute: Silenciar instàncies +logs: Registres +copy: Copiar +delayed: Retardat +metrics: Mètriques +overview: Vista general +database: Base de dades +regenerateLoginToken: Regenera el token d'inici de sessió +reduceUiAnimation: Redueix les animacions de la UI +messagingWithGroup: Conversa en grup +invites: Invitacions +unavailable: No disponible +newMessageExists: Tens nous missatges +onlyOneFileCanBeAttached: Només pots adjuntar un fitxer per missatge +normalPassword: Contrasenya amb seguretat mitjana +passwordNotMatched: No hi han coincidències +useOsNativeEmojis: Fes servir els emojis per defecte del Sistema Operatiu +joinOrCreateGroup: Fes que et convidin a un grup o crea el teu propi. +objectStorageBaseUrlDesc: "Es l'adreça URL que serveix com a referència. Específica\ + \ la adreça URL del CDN o Proxy si fas servir.\nPer fer servir S3 'https://.s3.amazonaws.com'\ + \ i per GCS o serveis semblants 'https://storage.googleapis.com/', etc." +height: Alçada +large: Gran +notificationSetting: Preferències de notificacions +makeActive: Activar +notificationSettingDesc: Tria el tipus de notificació que es veure. +notifyAntenna: Notificar noves articles +withFileAntenna: Només articles amb fitxers +enableServiceworker: Activa les notificacions push per al teu navegador +antennaUsersDescription: Escriu un nom d'usuari per línea +antennaInstancesDescription: Escriu la adreça d'una instància per línea +tags: Etiquetes +antennaSource: Font de la antena +antennaKeywords: Paraules claus a escolta +antennaExcludeKeywords: Paraules clau a excluir +antennaKeywordsDescription: Separades amb espais per fer una condició AND i amb una + línea nova per fer una condició OR. +caseSensitive: Sensible a majúscules i minúscules +withReplies: Inclou respostes +connectedTo: Aquest(s) compte(s) estan connectats +silenceConfirm: Segur que vols posa en silenci aquest usuari? +unsilence: Desfés posar en silenci +unsilenceConfirm: Segur que vols treure el silenci a aquest usuari? +aboutMisskey: Sobre Calckey +twoStepAuthentication: Autentificació de dos factors +moderator: Moderador +moderation: Moderació +available: Disponible +tapSecurityKey: Escriu la teva clau de seguretat +nUsersMentioned: Esmentat per {n} usuari(s) +securityKey: Clau de seguretat +resetPassword: Restablir contrasenya +describeFile: Afegeix un subtítol +enterFileDescription: Entra un subtítol +author: Autor +disableAll: Desactiva tots +userSaysSomethingReason: '{name} va dir {reason}' +display: Visualització +channel: Canals +create: Crear +useGlobalSetting: Fes servir els ajustos globals +useGlobalSettingDesc: Si s'activa, es faran servir els ajustos de notificacions del + teu compte. Si es desactiva , es poden fer configuracions individuals. +other: Altres diff --git a/locales/en-US.yml b/locales/en-US.yml index a11a404d6a..2feb2cd947 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -1237,6 +1237,14 @@ _mfm: sparkleDescription: "Gives content a sparkling particle effect." rotate: "Rotate" rotateDescription: "Turns content by a specified angle." + position: "Position" + positionDescription: "Move content by a specified amount." + scale: "Scale" + scaleDescription: "Scale content by a specified amount." + foreground: "Foreground color" + foregroundDescription: "Change the foreground color of text." + background: "Background color" + backgroundDescription: "Change the background color of text." plain: "Plain" plainDescription: "Deactivates the effects of all MFM contained within this MFM\ \ effect." diff --git a/locales/fi.yml b/locales/fi.yml new file mode 100644 index 0000000000..c9e5e06e08 --- /dev/null +++ b/locales/fi.yml @@ -0,0 +1,43 @@ +username: Käyttäjänimi +fetchingAsApObject: Hae Fedeversestä +gotIt: Selvä! +cancel: Peruuta +enterUsername: Anna käyttäjänimi +renotedBy: Buustannut {käyttäjä} +noNotes: Ei lähetyksiä +noNotifications: Ei ilmoituksia +instance: Instanssi +settings: Asetukset +basicSettings: Perusasetukset +otherSettings: Muut asetukset +openInWindow: Avaa ikkunaan +profile: Profiili +timeline: Aikajana +noAccountDescription: Käyttäjä ei ole vielä kirjoittanut kuvaustaan vielä. +login: Kirjaudu sisään +loggingIn: Kirjautuu sisään +logout: Kirjaudu ulos +uploading: Tallentaa ylös... +save: Tallenna +favorites: Kirjanmerkit +unfavorite: Poista kirjanmerkeistä +favorited: Lisätty kirjanmerkkeihin. +alreadyFavorited: Lisätty jo kirjanmerkkeihin. +cantFavorite: Ei voitu lisätä kirjanmerkkeihin. +pin: Kiinnitä profiiliin +unpin: Irroita profiilista +delete: Poista +forgotPassword: Unohtunut salasana +search: Etsi +notifications: Ilmoitukset +password: Salasana +ok: OK +noThankYou: Ei kiitos +signup: Rekisteröidy +users: Käyttäjät +addUser: Lisää käyttäjä +addInstance: Lisää instanssi +favorite: Lisää kirjanmerkkeihin +copyContent: Kopioi sisältö +deleteAndEdit: Poista ja muokkaa +copyLink: Kopioi linkki diff --git a/locales/ru-RU.yml b/locales/ru-RU.yml index 24878686bb..91d96fcc51 100644 --- a/locales/ru-RU.yml +++ b/locales/ru-RU.yml @@ -986,7 +986,7 @@ _registry: createKey: "Новый ключ" _aboutMisskey: about: "Calckey это форк Misskey, сделанный ThatOneCalculator, разработка которого\ - \ начал с 2022." + \ началась с 2022." contributors: "Основные соавторы" allContributors: "Все соавторы" source: "Исходный код" diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml index 78bf4f34cf..eb640b7dd4 100644 --- a/locales/zh-TW.yml +++ b/locales/zh-TW.yml @@ -64,7 +64,7 @@ import: "匯入" export: "匯出" files: "檔案" download: "下載" -driveFileDeleteConfirm: "確定要刪除檔案「{name}」嗎?使用此附件的貼文也會跟著消失。\n" +driveFileDeleteConfirm: "確定要刪除檔案「{name}」嗎?使用此附件的貼文也會跟著消失。" unfollowConfirm: "確定要取消追隨{name}嗎?" exportRequested: "已請求匯出。這可能會花一點時間。結束後檔案將會被放到雲端裡。" importRequested: "已請求匯入。這可能會花一點時間" @@ -291,7 +291,7 @@ emptyDrive: "雲端硬碟為空" emptyFolder: "資料夾為空" unableToDelete: "無法刪除" inputNewFileName: "輸入檔案名稱" -inputNewDescription: "請輸入新標題 " +inputNewDescription: "請輸入新標題" inputNewFolderName: "輸入新資料夾的名稱" circularReferenceFolder: "目標文件夾是您要移動的文件夾的子文件夾。" hasChildFilesOrFolders: "此文件夾不是空的,無法刪除。" @@ -324,7 +324,7 @@ yearX: "{year}年" pages: "頁面" integration: "整合" connectService: "己連結" -disconnectService: "己斷開 " +disconnectService: "己斷開" enableLocalTimeline: "開啟本地時間軸" enableGlobalTimeline: "啟用公開時間軸" disablingTimelinesInfo: "即使您關閉了時間線功能,管理員和協調人仍可以繼續使用,以方便您。" @@ -336,7 +336,7 @@ driveCapacityPerRemoteAccount: "每個非本地用戶的雲端容量" inMb: "以Mbps為單位" iconUrl: "圖像URL" bannerUrl: "橫幅圖像URL" -backgroundImageUrl: "背景圖片的來源網址 " +backgroundImageUrl: "背景圖片的來源網址" basicInfo: "基本資訊" pinnedUsers: "置頂用戶" pinnedUsersDescription: "在「發現」頁面中使用換行標記想要置頂的使用者。" @@ -490,7 +490,7 @@ useObjectStorage: "使用Object Storage" objectStorageBaseUrl: "Base URL" objectStorageBaseUrlDesc: "引用時的URL。如果您使用的是CDN或反向代理,请指定其URL,例如S3:“https://.s3.amazonaws.com”,GCS:“https://storage.googleapis.com/”" objectStorageBucket: "儲存空間(Bucket)" -objectStorageBucketDesc: "請指定您正在使用的服務的存儲桶名稱。 " +objectStorageBucketDesc: "請指定您正在使用的服務的存儲桶名稱。" objectStoragePrefix: "前綴" objectStoragePrefixDesc: "它存儲在此前綴目錄下。" objectStorageEndpoint: "端點(Endpoint)" @@ -560,8 +560,8 @@ disablePlayer: "關閉播放器" expandTweet: "展開推文" themeEditor: "主題編輯器" description: "描述" -describeFile: "添加標題 " -enterFileDescription: "輸入標題 " +describeFile: "添加標題" +enterFileDescription: "輸入標題" author: "作者" leaveConfirm: "有未保存的更改。要放棄嗎?" manage: "管理" @@ -865,7 +865,7 @@ driveCapOverrideLabel: "更改這個使用者的雲端硬碟容量上限" driveCapOverrideCaption: "如果指定0以下的值,就會被取消。" requireAdminForView: "必須以管理者帳號登入才可以檢視。" isSystemAccount: "由系統自動建立與管理的帳號。" -typeToConfirm: "要執行這項操作,請輸入 {x} " +typeToConfirm: "要執行這項操作,請輸入 {x}" deleteAccount: "刪除帳號" document: "文件" numberOfPageCache: "快取頁面數" @@ -876,7 +876,7 @@ statusbar: "狀態列" pleaseSelect: "請選擇" reverse: "翻轉" colored: "彩色" -refreshInterval: "更新間隔" +refreshInterval: "更新間隔 " label: "標籤" type: "類型" speed: "速度" @@ -895,7 +895,7 @@ activeEmailValidationDescription: "積極地驗證用戶的電子郵件地址, navbar: "導覽列" shuffle: "隨機" account: "帳戶" -move: "移動 " +move: "移動" customKaTeXMacro: "自定義 KaTeX 宏" customKaTeXMacroDescription: "使用宏來輕鬆的輸入數學表達式吧!宏的用法與 LaTeX 中的命令定義相同。你可以使用 \\newcommand{\\\ name}{content} 或 \\newcommand{\\name}[number of arguments]{content} 來輸入數學表達式。舉個例子,\\\ @@ -933,11 +933,11 @@ _accountDelete: inProgress: "正在刪除" _ad: back: "返回" - reduceFrequencyOfThisAd: "降低此廣告的頻率 " + reduceFrequencyOfThisAd: "降低此廣告的頻率" _forgotPassword: enterEmail: "請輸入您的帳戶註冊的電子郵件地址。 密碼重置連結將被發送到該電子郵件地址。" - ifNoEmail: "如果您還沒有註冊您的電子郵件地址,請聯繫管理員。 " - contactAdmin: "此實例不支持電子郵件,請聯繫您的管理員重置您的密碼。 " + ifNoEmail: "如果您還沒有註冊您的電子郵件地址,請聯繫管理員。" + contactAdmin: "此實例不支持電子郵件,請聯繫您的管理員重置您的密碼。" _gallery: my: "我的貼文" liked: "喜歡的貼文" @@ -1000,7 +1000,7 @@ _mfm: url: "URL" urlDescription: "可以展示URL位址。" link: "鏈接" - linkDescription: "您可以將特定範圍的文章與 URL 相關聯。 " + linkDescription: "您可以將特定範圍的文章與 URL 相關聯。" bold: "粗體" boldDescription: "可以將文字顯示为粗體来強調。" small: "縮小" @@ -1805,3 +1805,6 @@ migration: 遷移 homeTimeline: 主頁時間軸 swipeOnDesktop: 允許在桌面上進行手機式滑動 logoImageUrl: 圖標網址 +addInstance: 增加一個實例 +noInstances: 沒有實例 +flagSpeakAsCat: 像貓一樣地說話 diff --git a/package.json b/package.json index 96a66979e8..42b44031ca 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "calckey", - "version": "13.2.0-beta8", + "version": "13.2.0-rc", "codename": "aqua", "repository": { "type": "git", @@ -40,6 +40,8 @@ "@bull-board/ui": "^4.10.2", "@napi-rs/cli": "^2.15.0", "@tensorflow/tfjs": "^3.21.0", + "focus-trap": "^7.2.0", + "focus-trap-vue": "^4.0.1", "js-yaml": "4.1.0", "seedrandom": "^3.0.5" }, diff --git a/packages/backend/assets/favicon.ico b/packages/backend/assets/favicon.ico index f252ca8165..96effef17d 100644 Binary files a/packages/backend/assets/favicon.ico and b/packages/backend/assets/favicon.ico differ diff --git a/packages/backend/assets/favicon.png b/packages/backend/assets/favicon.png index ee67787513..0f482d7f04 100644 Binary files a/packages/backend/assets/favicon.png and b/packages/backend/assets/favicon.png differ diff --git a/packages/backend/assets/favicon.svg b/packages/backend/assets/favicon.svg index a77c6e22bb..675d09cc85 100644 --- a/packages/backend/assets/favicon.svg +++ b/packages/backend/assets/favicon.svg @@ -1,25 +1 @@ - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - \ No newline at end of file + diff --git a/packages/backend/assets/icons/192.png b/packages/backend/assets/icons/192.png index 9cdb656a57..536e01cc1f 100644 Binary files a/packages/backend/assets/icons/192.png and b/packages/backend/assets/icons/192.png differ diff --git a/packages/backend/assets/icons/512.png b/packages/backend/assets/icons/512.png index a830bfa8ad..6455268532 100644 Binary files a/packages/backend/assets/icons/512.png and b/packages/backend/assets/icons/512.png differ diff --git a/packages/backend/assets/inverse wordmark.png b/packages/backend/assets/inverse wordmark.png new file mode 100644 index 0000000000..6455268532 Binary files /dev/null and b/packages/backend/assets/inverse wordmark.png differ diff --git a/packages/backend/assets/inverse wordmark.svg b/packages/backend/assets/inverse wordmark.svg index 07e9ceeeec..59125fe7be 100644 --- a/packages/backend/assets/inverse wordmark.svg +++ b/packages/backend/assets/inverse wordmark.svg @@ -1,65 +1 @@ - - - - + diff --git a/packages/backend/assets/splash.png b/packages/backend/assets/splash.png index bd2de5c9fd..8c1da72fbf 100644 Binary files a/packages/backend/assets/splash.png and b/packages/backend/assets/splash.png differ diff --git a/packages/backend/package.json b/packages/backend/package.json index 888eb97402..ce0b2c2e18 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -26,7 +26,7 @@ "@bull-board/api": "^4.6.4", "@bull-board/koa": "^4.6.4", "@bull-board/ui": "^4.6.4", - "@calckey/megalodon": "5.1.24", + "@calckey/megalodon": "5.2.0", "@discordapp/twemoji": "14.0.2", "@elastic/elasticsearch": "7.17.0", "@koa/cors": "3.4.3", @@ -75,9 +75,11 @@ "koa": "2.13.4", "koa-body": "^6.0.1", "koa-bodyparser": "4.3.0", + "koa-favicon": "2.1.0", "koa-json-body": "5.3.0", "koa-logger": "3.2.1", "koa-mount": "4.0.0", + "koa-remove-trailing-slashes": "2.0.3", "koa-send": "5.0.1", "koa-slow": "2.1.0", "koa-views": "7.0.2", diff --git a/packages/backend/src/server/api/index.ts b/packages/backend/src/server/api/index.ts index b8e4231655..06b3ea4efa 100644 --- a/packages/backend/src/server/api/index.ts +++ b/packages/backend/src/server/api/index.ts @@ -20,6 +20,7 @@ import handler from "./api-handler.js"; import signup from "./private/signup.js"; import signin from "./private/signin.js"; import signupPending from "./private/signup-pending.js"; +import verifyEmail from "./private/verify-email.js"; import discord from "./service/discord.js"; import github from "./service/github.js"; import twitter from "./service/twitter.js"; @@ -177,6 +178,7 @@ for (const endpoint of [...endpoints, ...compatibility]) { router.post("/signup", signup); router.post("/signin", signin); router.post("/signup-pending", signupPending); +router.post("/verify-email", verifyEmail); router.use(discord.routes()); router.use(github.routes()); diff --git a/packages/backend/src/server/api/mastodon/endpoints/account.ts b/packages/backend/src/server/api/mastodon/endpoints/account.ts index 70bdb74f34..7490581937 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/account.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/account.ts @@ -91,6 +91,44 @@ export function apiAccountMastodon(router: Router): void { ctx.body = e.response.data; } }); + router.get("/v1/accounts/relationships", async (ctx) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + let users; + try { + // TODO: this should be body + let ids = ctx.request.query ? ctx.request.query["id[]"] : null; + if (typeof ids === "string") { + ids = [ids]; + } + users = ids; + relationshipModel.id = ids?.toString() || "1"; + if (!ids) { + ctx.body = [relationshipModel]; + return; + } + + let reqIds = []; + for (let i = 0; i < ids.length; i++) { + reqIds.push(convertId(ids[i], IdType.CalckeyId)); + } + + const data = await client.getRelationships(reqIds); + let resp = data.data; + for (let acctIdx = 0; acctIdx < resp.length; acctIdx++) { + resp[acctIdx].id = convertId(resp[acctIdx].id, IdType.MastodonId); + } + ctx.body = resp; + } catch (e: any) { + console.error(e); + let data = e.response.data; + data.users = users; + console.error(data); + ctx.status = 401; + ctx.body = data; + } + }); router.get<{ Params: { id: string } }>("/v1/accounts/:id", async (ctx) => { const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; const accessTokens = ctx.headers.authorization; @@ -340,44 +378,6 @@ export function apiAccountMastodon(router: Router): void { } }, ); - router.get("/v1/accounts/relationships", async (ctx) => { - const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; - const accessTokens = ctx.headers.authorization; - const client = getClient(BASE_URL, accessTokens); - let users; - try { - // TODO: this should be body - let ids = ctx.request.query ? ctx.request.query["id[]"] : null; - if (typeof ids === "string") { - ids = [ids]; - } - users = ids; - relationshipModel.id = ids?.toString() || "1"; - if (!ids) { - ctx.body = [relationshipModel]; - return; - } - - let reqIds = []; - for (let i = 0; i < ids.length; i++) { - reqIds.push(convertId(ids[i], IdType.CalckeyId)); - } - - const data = await client.getRelationships(reqIds); - let resp = data.data; - for (let acctIdx = 0; acctIdx < resp.length; acctIdx++) { - resp[acctIdx].id = convertId(resp[acctIdx].id, IdType.MastodonId); - } - ctx.body = resp; - } catch (e: any) { - console.error(e); - let data = e.response.data; - data.users = users; - console.error(data); - ctx.status = 401; - ctx.body = data; - } - }); router.get("/v1/bookmarks", async (ctx) => { const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; const accessTokens = ctx.headers.authorization; diff --git a/packages/backend/src/server/api/mastodon/endpoints/status.ts b/packages/backend/src/server/api/mastodon/endpoints/status.ts index fcfbd6aaaf..f7589569c9 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/status.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/status.ts @@ -388,7 +388,7 @@ export function statusModel( emojis: MastodonEntity.Emoji[], content: string, ) { - const now = Math.floor(new Date().getTime() / 1000); + const now = new Date().toISOString(); return { id: "9atm5frjhb", uri: "https://http.cat/404", // "" diff --git a/packages/backend/src/server/api/mastodon/endpoints/timeline.ts b/packages/backend/src/server/api/mastodon/endpoints/timeline.ts index ce3a4dc958..57e5d9bb02 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/timeline.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/timeline.ts @@ -211,7 +211,7 @@ export function apiTimelineMastodon(router: Router): void { const accessTokens = ctx.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { - const data = await client.createList((ctx.query as any).title); + const data = await client.createList((ctx.request.body as any).title); ctx.body = data.data; } catch (e: any) { console.error(e); @@ -227,7 +227,7 @@ export function apiTimelineMastodon(router: Router): void { const accessTokens = ctx.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { - const data = await client.updateList(ctx.params.id, ctx.query as any); + const data = await client.updateList(ctx.params.id, (ctx.request.body as any).title); ctx.body = data.data; } catch (e: any) { console.error(e); diff --git a/packages/backend/src/server/api/private/verify-email.ts b/packages/backend/src/server/api/private/verify-email.ts new file mode 100644 index 0000000000..e6c8295d18 --- /dev/null +++ b/packages/backend/src/server/api/private/verify-email.ts @@ -0,0 +1,38 @@ +import type Koa from "koa"; +import { Users, UserProfiles } from "@/models/index.js"; +import { publishMainStream } from "@/services/stream.js"; + +export default async (ctx: Koa.Context) => { + const body = ctx.request.body; + + const code = body["code"]; + + const profile = await UserProfiles.findOneByOrFail({ emailVerifyCode: code }); + + if (profile != null) { + ctx.body = "Verify succeeded!"; + + await UserProfiles.update( + { userId: profile.userId }, + { + emailVerified: true, + emailVerifyCode: null, + }, + ); + + publishMainStream( + profile.userId, + "meUpdated", + await Users.pack( + profile.userId, + { id: profile.userId }, + { + detail: true, + includeSecrets: true, + }, + ), + ); + } else { + ctx.throw(404); + } +}; diff --git a/packages/backend/src/server/index.ts b/packages/backend/src/server/index.ts index 7d274f7d28..8035dcdd7a 100644 --- a/packages/backend/src/server/index.ts +++ b/packages/backend/src/server/index.ts @@ -10,6 +10,7 @@ import Router from "@koa/router"; import mount from "koa-mount"; import koaLogger from "koa-logger"; import * as slow from "koa-slow"; + import { IsNull } from "typeorm"; import config from "@/config/index.js"; import Logger from "@/services/logger.js"; @@ -29,6 +30,7 @@ import proxyServer from "./proxy/index.js"; import webServer from "./web/index.js"; import { initializeStreamingServer } from "./api/streaming.js"; import { koaBody } from "koa-body"; +import removeTrailingSlash from "koa-remove-trailing-slashes"; import { v4 as uuid } from "uuid"; export const serverLogger = new Logger("server", "gray", false); @@ -37,12 +39,7 @@ export const serverLogger = new Logger("server", "gray", false); const app = new Koa(); app.proxy = true; -// Replace trailing slashes -app.use(async (ctx, next) => { - if (ctx.request.path !== "/" && ctx.request.path.endsWith("/")) - return ctx.redirect(ctx.request.path.replace(/\/$/, "")); - else await next(); -}); +app.use(removeTrailingSlash()); if (!["production", "test"].includes(process.env.NODE_ENV || "")) { // Logger @@ -127,40 +124,6 @@ router.get("/identicon/:x", async (ctx) => { ctx.body = fs.createReadStream(temp).on("close", () => cleanup()); }); -router.get("/verify-email/:code", async (ctx) => { - const profile = await UserProfiles.findOneBy({ - emailVerifyCode: ctx.params.code, - }); - - if (profile != null) { - ctx.body = "Verify succeeded!"; - ctx.status = 200; - - await UserProfiles.update( - { userId: profile.userId }, - { - emailVerified: true, - emailVerifyCode: null, - }, - ); - - publishMainStream( - profile.userId, - "meUpdated", - await Users.pack( - profile.userId, - { id: profile.userId }, - { - detail: true, - includeSecrets: true, - }, - ), - ); - } else { - ctx.status = 404; - } -}); - mastoRouter.get("/oauth/authorize", async (ctx) => { const { client_id, state, redirect_uri } = ctx.request.query; console.log(ctx.request.req); diff --git a/packages/backend/src/server/web/index.ts b/packages/backend/src/server/web/index.ts index 028170cd75..642a17d578 100644 --- a/packages/backend/src/server/web/index.ts +++ b/packages/backend/src/server/web/index.ts @@ -8,11 +8,13 @@ import { readFileSync } from "node:fs"; import Koa from "koa"; import Router from "@koa/router"; import send from "koa-send"; +import favicon from "koa-favicon"; import views from "koa-views"; import sharp from "sharp"; import { createBullBoard } from "@bull-board/api"; import { BullAdapter } from "@bull-board/api/bullAdapter.js"; import { KoaAdapter } from "@bull-board/koa"; + import { In, IsNull } from "typeorm"; import { fetchMeta } from "@/misc/fetch-meta.js"; import config from "@/config/index.js"; @@ -96,14 +98,8 @@ app.use( }), ); -// Favicon Router -app.use(async (ctx, next) => { - if (ctx.path != "/favicon.ico") return next(); - const meta = await fetchMeta(); - if (meta.iconUrl === "") - ctx.body = readFileSync(`${_dirname}/../../../assets/favicon.ico`); - else ctx.redirect(meta.iconUrl); -}); +// Serve favicon +app.use(favicon(`${_dirname}/../../../assets/favicon.ico`)); // Common request handler app.use(async (ctx, next) => { diff --git a/packages/backend/src/services/send-email.ts b/packages/backend/src/services/send-email.ts index 4c442a168c..7647b0db49 100644 --- a/packages/backend/src/services/send-email.ts +++ b/packages/backend/src/services/send-email.ts @@ -33,7 +33,6 @@ export async function sendEmail( } as any); try { - // TODO: htmlサニタイズ const info = await transporter.sendMail({ from: meta.email!, to: to, @@ -44,81 +43,23 @@ export async function sendEmail( ${subject} - - -
-
- -

${meta.name}

+ +
+
+ +

${meta.name}

-
-