1
0
mirror of https://github.com/misskey-dev/misskey.git synced 2026-05-06 14:05:47 +02:00

Compare commits

..

20 Commits

Author SHA1 Message Date
Kagami Sascha Rosylight
0b0a416566 Merge remote-tracking branch 'origin/develop' into misskey-js 2023-03-25 07:58:26 +01:00
Kagami Sascha Rosylight
9044fa5d1a Merge branch 'develop' into misskey-js 2023-03-19 10:26:30 +01:00
Kagami Sascha Rosylight
3bb343e2fc Merge branch 'develop' into misskey-js 2023-03-17 11:11:21 +01:00
Kagami Sascha Rosylight
f2fd8bfac1 Update Dockerfile 2023-03-16 23:05:48 +01:00
Kagami Sascha Rosylight
1602ad843a Update pnpm-lock.yaml 2023-03-16 22:42:46 +01:00
Kagami Sascha Rosylight
e68236bd84 Update package.json 2023-03-16 22:35:52 +01:00
Kagami Sascha Rosylight
447b6f9e5f upgrade @types/node 2023-03-16 22:30:12 +01:00
Kagami Sascha Rosylight
746bc322b7 types too 2023-03-16 22:24:59 +01:00
Kagami Sascha Rosylight
a6aee82fcf upgrade jest 2023-03-16 22:23:52 +01:00
Kagami Sascha Rosylight
6095b33ab2 fix vite build 2023-03-16 22:13:42 +01:00
Kagami Sascha Rosylight
5ac094e51b fix lint errors 2023-03-16 21:38:20 +01:00
Kagami Sascha Rosylight
bdf013d547 Update pnpm-lock.yaml 2023-03-16 21:35:58 +01:00
Kagami Sascha Rosylight
96a2dda153 Update package.json 2023-03-16 21:32:16 +01:00
Kagami Sascha Rosylight
d69d2c8e8d Update test-misskey-js.yml 2023-03-16 21:24:22 +01:00
Kagami Sascha Rosylight
8736bb42f2 Update test-misskey-js.yml 2023-03-16 21:22:39 +01:00
Kagami Sascha Rosylight
effd78dc98 skip pnpm/action-setup? 2023-03-16 21:18:32 +01:00
Kagami Sascha Rosylight
634ce0fa49 Update misskey-js.api.md 2023-03-16 21:18:08 +01:00
Kagami Sascha Rosylight
6e6a5222cd enable corepack first 2023-03-16 21:14:55 +01:00
Kagami Sascha Rosylight
ce5a9630ca update workflows 2023-03-16 21:13:21 +01:00
Kagami Sascha Rosylight
d123722616 chore: integrate misskey-js as a workspace item 2023-03-16 21:07:16 +01:00
3069 changed files with 83809 additions and 344342 deletions

View File

@@ -1,223 +0,0 @@
#━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# Misskey configuration
#━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# ┌────────────────────────┐
#───┘ Initial Setup Password └─────────────────────────────────────────────────────
# Password to initiate setting up admin account.
# It will not be used after the initial setup is complete.
#
# Be sure to change this when you set up Misskey via the Internet.
#
# The provider of the service who sets up Misskey on behalf of the customer should
# set this value to something unique when generating the Misskey config file,
# and provide it to the customer.
setupPassword: example_password_please_change_this_or_you_will_get_hacked
# ┌─────┐
#───┘ URL └─────────────────────────────────────────────────────
# Final accessible URL seen by a user.
url: 'http://misskey.local'
# ONCE YOU HAVE STARTED THE INSTANCE, DO NOT CHANGE THE
# URL SETTINGS AFTER THAT!
# ┌───────────────────────┐
#───┘ Port and TLS settings └───────────────────────────────────
#
# Misskey requires a reverse proxy to support HTTPS connections.
#
# +----- https://example.tld/ ------------+
# +------+ |+-------------+ +----------------+|
# | User | ---> || Proxy (443) | ---> | Misskey (3000) ||
# +------+ |+-------------+ +----------------+|
# +---------------------------------------+
#
# You need to set up a reverse proxy. (e.g. nginx)
# An encrypted connection with HTTPS is highly recommended
# because tokens may be transferred in GET requests.
# The port that your Misskey server should listen on.
port: 61812
# ┌──────────────────────────┐
#───┘ PostgreSQL configuration └────────────────────────────────
db:
host: db
port: 5432
# Database name
db: misskey
# Auth
user: postgres
pass: postgres
# Whether disable Caching queries
#disableCache: true
# Extra Connection options
#extra:
# ssl: true
dbReplications: false
# You can configure any number of replicas here
#dbSlaves:
# -
# host:
# port:
# db:
# user:
# pass:
# -
# host:
# port:
# db:
# user:
# pass:
# ┌─────────────────────┐
#───┘ Redis configuration └─────────────────────────────────────
redis:
host: redis
port: 6379
#family: 0 # 0=Both, 4=IPv4, 6=IPv6
#pass: example-pass
#prefix: example-prefix
#db: 1
#redisForPubsub:
# host: redis
# port: 6379
# #family: 0 # 0=Both, 4=IPv4, 6=IPv6
# #pass: example-pass
# #prefix: example-prefix
# #db: 1
#redisForJobQueue:
# host: redis
# port: 6379
# #family: 0 # 0=Both, 4=IPv4, 6=IPv6
# #pass: example-pass
# #prefix: example-prefix
# #db: 1
#redisForTimelines:
# host: redis
# port: 6379
# #family: 0 # 0=Both, 4=IPv4, 6=IPv6
# #pass: example-pass
# #prefix: example-prefix
# #db: 1
#redisForReactions:
# host: redis
# port: 6379
# #family: 0 # 0=Both, 4=IPv4, 6=IPv6
# #pass: example-pass
# #prefix: example-prefix
# #db: 1
# ┌───────────────────────────┐
#───┘ MeiliSearch configuration └─────────────────────────────
#meilisearch:
# host: meilisearch
# port: 7700
# apiKey: ''
# ssl: true
# index: ''
# ┌───────────────┐
#───┘ ID generation └───────────────────────────────────────────
# You can select the ID generation method.
# You don't usually need to change this setting, but you can
# change it according to your preferences.
# Available methods:
# aid ... Short, Millisecond accuracy
# aidx ... Millisecond accuracy
# meid ... Similar to ObjectID, Millisecond accuracy
# ulid ... Millisecond accuracy
# objectid ... This is left for backward compatibility
# ONCE YOU HAVE STARTED THE INSTANCE, DO NOT CHANGE THE
# ID SETTINGS AFTER THAT!
id: 'aidx'
# ┌────────────────┐
#───┘ Error tracking └──────────────────────────────────────────
# Sentry is available for error tracking.
# See the Sentry documentation for more details on options.
#sentryForBackend:
# enableNodeProfiling: true
# options:
# dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0'
#sentryForFrontend:
# vueIntegration:
# tracingOptions:
# trackComponents: true
# browserTracingIntegration:
# replayIntegration:
# options:
# dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0'
# ┌─────────────────────┐
#───┘ Other configuration └─────────────────────────────────────
# Whether disable HSTS
#disableHsts: true
# Number of worker processes
#clusterLimit: 1
# Job concurrency per worker
# deliverJobConcurrency: 128
# inboxJobConcurrency: 16
# Job rate limiter
# deliverJobPerSec: 128
# inboxJobPerSec: 32
# Job attempts
# deliverJobMaxAttempts: 12
# inboxJobMaxAttempts: 8
# IP address family used for outgoing request (ipv4, ipv6 or dual)
#outgoingAddressFamily: ipv4
# Proxy for HTTP/HTTPS
#proxy: http://127.0.0.1:3128
proxyBypassHosts:
- api.deepl.com
- api-free.deepl.com
- www.recaptcha.net
- hcaptcha.com
- challenges.cloudflare.com
# Proxy for SMTP/SMTPS
#proxySmtp: http://127.0.0.1:3128 # use HTTP/1.1 CONNECT
#proxySmtp: socks4://127.0.0.1:1080 # use SOCKS4
#proxySmtp: socks5://127.0.0.1:1080 # use SOCKS5
# Media Proxy
#mediaProxy: https://example.com/proxy
allowedPrivateNetworks: [
'127.0.0.1/32'
]
# Upload or download file size limits (bytes)
#maxFileSize: 262144000

View File

@@ -1,11 +1,4 @@
# misskey settings
# MISSKEY_URL=https://example.tld/
# db settings
POSTGRES_PASSWORD=example-misskey-pass
# DATABASE_PASSWORD=${POSTGRES_PASSWORD}
POSTGRES_USER=example-misskey-user
# DATABASE_USER=${POSTGRES_USER}
POSTGRES_DB=misskey
# DATABASE_DB=${POSTGRES_DB}
DATABASE_URL="postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}"

View File

@@ -6,7 +6,6 @@
#───┘ URL └─────────────────────────────────────────────────────
# Final accessible URL seen by a user.
# You can set url from an environment variable instead.
url: https://example.tld/
# ONCE YOU HAVE STARTED THE INSTANCE, DO NOT CHANGE THE
@@ -39,11 +38,9 @@ db:
port: 5432
# Database name
# You can set db from an environment variable instead.
db: misskey
# Auth
# You can set user and pass from environment variables instead.
user: example-misskey-user
pass: example-misskey-pass
@@ -54,23 +51,6 @@ db:
#extra:
# ssl: true
dbReplications: false
# You can configure any number of replicas here
#dbSlaves:
# -
# host:
# port:
# db:
# user:
# pass:
# -
# host:
# port:
# db:
# user:
# pass:
# ┌─────────────────────┐
#───┘ Redis configuration └─────────────────────────────────────
@@ -82,69 +62,15 @@ redis:
#prefix: example-prefix
#db: 1
#redisForPubsub:
# host: redis
# port: 6379
# #family: 0 # 0=Both, 4=IPv4, 6=IPv6
# #pass: example-pass
# #prefix: example-prefix
# #db: 1
# ┌─────────────────────────────┐
#───┘ Elasticsearch configuration └─────────────────────────────
#redisForJobQueue:
# host: redis
# port: 6379
# #family: 0 # 0=Both, 4=IPv4, 6=IPv6
# #pass: example-pass
# #prefix: example-prefix
# #db: 1
#redisForTimelines:
# host: redis
# port: 6379
# #family: 0 # 0=Both, 4=IPv4, 6=IPv6
# #pass: example-pass
# #prefix: example-prefix
# #db: 1
#redisForReactions:
# host: redis
# port: 6379
# #family: 0 # 0=Both, 4=IPv4, 6=IPv6
# #pass: example-pass
# #prefix: example-prefix
# #db: 1
# ┌───────────────────────────────┐
#───┘ Fulltext search configuration └─────────────────────────────
# These are the setting items for the full-text search provider.
fulltextSearch:
# You can select the ID generation method.
# - sqlLike (default)
# Use SQL-like search.
# This is a standard feature of PostgreSQL, so no special extensions are required.
# - sqlPgroonga
# Use pgroonga.
# You need to install pgroonga and configure it as a PostgreSQL extension.
# In addition to the above, you need to create a pgroonga index on the text column of the note table.
# see: https://pgroonga.github.io/tutorial/
# - meilisearch
# Use Meilisearch.
# You need to install Meilisearch and configure.
provider: sqlLike
# For Meilisearch settings.
# If you select "meilisearch" for "fulltextSearch.provider", it must be set.
# You can set scope to local (default value) or global
# (include notes from remote).
#meilisearch:
# host: meilisearch
# port: 7700
# apiKey: ''
# ssl: true
# index: ''
# scope: local
#elasticsearch:
# host: localhost
# port: 9200
# ssl: false
# user:
# pass:
# ┌───────────────┐
#───┘ ID generation └───────────────────────────────────────────
@@ -155,7 +81,6 @@ fulltextSearch:
# Available methods:
# aid ... Short, Millisecond accuracy
# aidx ... Millisecond accuracy
# meid ... Similar to ObjectID, Millisecond accuracy
# ulid ... Millisecond accuracy
# objectid ... This is left for backward compatibility
@@ -163,27 +88,7 @@ fulltextSearch:
# ONCE YOU HAVE STARTED THE INSTANCE, DO NOT CHANGE THE
# ID SETTINGS AFTER THAT!
id: 'aidx'
# ┌────────────────┐
#───┘ Error tracking └──────────────────────────────────────────
# Sentry is available for error tracking.
# See the Sentry documentation for more details on options.
#sentryForBackend:
# enableNodeProfiling: true
# options:
# dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0'
#sentryForFrontend:
# vueIntegration:
# tracingOptions:
# trackComponents: true
# browserTracingIntegration:
# replayIntegration:
# options:
# dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0'
id: 'aid'
# ┌─────────────────────┐
#───┘ Other configuration └─────────────────────────────────────
@@ -200,7 +105,7 @@ id: 'aidx'
# Job rate limiter
# deliverJobPerSec: 128
# inboxJobPerSec: 32
# inboxJobPerSec: 16
# Job attempts
# deliverJobMaxAttempts: 12
@@ -227,22 +132,15 @@ proxyBypassHosts:
# Media Proxy
#mediaProxy: https://example.com/proxy
# For security reasons, uploading attachments from the intranet is prohibited,
# but exceptions can be made from the following settings. Default value is "undefined".
# Read changelog to learn more (Improvements of 12.90.0 (2021/09/04)).
# Proxy remote files (default: false)
#proxyRemoteFiles: true
# Sign to ActivityPub GET request (default: true)
signToActivityPubGet: true
#allowedPrivateNetworks: [
# '127.0.0.1/32'
#]
# Upload or download file size limits (bytes)
#maxFileSize: 262144000
# Log settings
# logging:
# sql:
# # Outputs query parameters during SQL execution to the log.
# # default: false
# enableQueryParamLogging: false
# # Disable query truncation. If set to true, the full text of the query will be output to the log.
# # default: false
# disableQueryTruncation: false

View File

@@ -2,77 +2,6 @@
# Misskey configuration
#━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# ┌──────────────────────────────┐
#───┘ a boring but important thing └────────────────────────────
#
# First of all, let me tell you a story that may possibly be
# boring to you and possibly important to you.
#
# Misskey is licensed under the AGPLv3 license. This license is
# known to be often misunderstood. Please read the following
# instructions carefully and select the appropriate option so
# that you do not negligently cause a license violation.
#
# --------
# Option 1: If you host Misskey AS-IS (without any changes to
# the source code. forks are not included).
#
# Step 1: Congratulations! You don't need to do anything.
# --------
# Option 2: If you have made changes to the source code (forks
# are included) and publish a Git repository of source
# code. There should be no access restrictions on
# this repository. Strictly speaking, it doesn't have
# to be a Git repository, but you'll probably use Git!
#
# Step 1: Build and run the Misskey server first.
# Step 2: Open <https://your.misskey.example/admin/settings> in
# your browser with the administrator account.
# Step 3: Enter the URL of your Git repository in the
# "Repository URL" field.
# --------
# Option 3: If neither of the above applies to you.
# (In this case, the source code should be published
# on the Misskey interface. IT IS NOT ENOUGH TO
# DISCLOSE THE SOURCE CODE WHEN A USER REQUESTS IT BY
# E-MAIL OR OTHER MEANS. If you are not satisfied
# with this, it is recommended that you read the
# license again carefully. Anyway, enabling this
# option will automatically generate and publish a
# tarball at build time, protecting you from
# inadvertent license violations. (There is no legal
# guarantee, of course.) The tarball will generated
# from the root directory of your codebase. So it is
# also recommended to check <built/tarball> directory
# once after building and before activating the server
# to avoid ACCIDENTAL LEAKING OF SENSITIVE INFORMATION.
# To prevent certain files from being included in the
# tarball, add a glob pattern after line 15 in
# <scripts/tarball.mjs>. DO NOT FORGET TO BUILD AFTER
# ENABLING THIS OPTION!)
#
# Step 1: Uncomment the following line.
#
# publishTarballInsteadOfProvideRepositoryUrl: true
# ┌────────────────────────┐
#───┘ Initial Setup Password └─────────────────────────────────────────────────────
# Password to initiate setting up admin account.
# It will not be used after the initial setup is complete.
#
# Be sure to change this when you set up Misskey via the Internet.
#
# The provider of the service who sets up Misskey on behalf of the customer should
# set this value to something unique when generating the Misskey config file,
# and provide it to the customer.
#
# setupPassword: example_password_please_change_this_or_you_will_get_hacked
# ┌─────┐
#───┘ URL └─────────────────────────────────────────────────────
@@ -101,58 +30,6 @@ url: https://example.tld/
# The port that your Misskey server should listen on.
port: 3000
# You can also use UNIX domain socket.
# socket: /path/to/misskey.sock
# chmodSocket: '777'
# Proxy trust settings
#
# Specifies the IP addresses that Misskey will use as trusted
# reverse proxies (e.g., nginx, Cloudflare). This affects how
# Misskey determines the source IP for each request and is used
# for important rate limiting and security features. If the value
# is not set correctly, Misskey may use the IP address of the
# reverse proxy instead of the actual source IP, which may lead to
# unintended rate limiting or security vulnerabilities.
# By default, the loopback network and private network address
# ranges shown below are trusted.
# If you are using a single reverse proxy and it is on the same
# machine or the same private network as Misskey, it is unlikely you
# need to change this setting, and the default setting is fine.
# Also, if you are using multiple reverse proxy servers and they are
# all on the same private network as Misskey, the default setting
# is fine.
# However, if you are using a reverse proxy server that accesses
# Misskey web servers and streaming servers via public IP addresses
# (for example, Cloudflare), you must set this variable.
# When changing this setting, you can use one of the following values:
#
# - true: Trust all proxies
# - false: Do not trust any proxies
# - IP address, IP address range, or array of them: Trust hops that
# match the specified criteria.
# - Integer: Trust the nth hop from the front-facing proxy server as
# the client.
# For more information on how to configure this setting, please refer
# to the Fastify documentation:
# https://fastify.dev/docs/latest/Reference/Server/#trustproxy
#
# Note that if this variable is set, it overrides the default range,
# so if you have both an external reverse proxy and a proxy on the
# local host, you must include both IPs (or IP ranges).
#
#trustProxy:
# - '10.0.0.0/8'
# - '172.16.0.0/12'
# - '192.168.0.0/16'
# - '127.0.0.1/32'
# - '::1/128'
# - 'fc00::/7'
# # Example: If you are using some external reverse proxies like CDNs,
# # you may need to add the CDN IP ranges here.
# # If you're using Cloudflare, you can find IP Ranges at:
# # https://www.cloudflare.com/ips/
# ┌──────────────────────────┐
#───┘ PostgreSQL configuration └────────────────────────────────
@@ -174,23 +51,6 @@ db:
#extra:
# ssl: true
dbReplications: false
# You can configure any number of replicas here
#dbSlaves:
# -
# host:
# port:
# db:
# user:
# pass:
# -
# host:
# port:
# db:
# user:
# pass:
# ┌─────────────────────┐
#───┘ Redis configuration └─────────────────────────────────────
@@ -201,80 +61,16 @@ redis:
#pass: example-pass
#prefix: example-prefix
#db: 1
# You can specify more ioredis options...
#username: example-username
#redisForPubsub:
# ┌─────────────────────────────┐
#───┘ Elasticsearch configuration └─────────────────────────────
#elasticsearch:
# host: localhost
# port: 6379
# #family: 0 # 0=Both, 4=IPv4, 6=IPv6
# #pass: example-pass
# #prefix: example-prefix
# #db: 1
# # You can specify more ioredis options...
# #username: example-username
#redisForJobQueue:
# host: localhost
# port: 6379
# #family: 0 # 0=Both, 4=IPv4, 6=IPv6
# #pass: example-pass
# #prefix: example-prefix
# #db: 1
# # You can specify more ioredis options...
# #username: example-username
#redisForTimelines:
# host: localhost
# port: 6379
# #family: 0 # 0=Both, 4=IPv4, 6=IPv6
# #pass: example-pass
# #prefix: example-prefix
# #db: 1
# # You can specify more ioredis options...
# #username: example-username
#redisForReactions:
# host: localhost
# port: 6379
# #family: 0 # 0=Both, 4=IPv4, 6=IPv6
# #pass: example-pass
# #prefix: example-prefix
# #db: 1
# # You can specify more ioredis options...
# #username: example-username
# ┌───────────────────────────────┐
#───┘ Fulltext search configuration └─────────────────────────────
# These are the setting items for the full-text search provider.
fulltextSearch:
# You can select the ID generation method.
# - sqlLike (default)
# Use SQL-like search.
# This is a standard feature of PostgreSQL, so no special extensions are required.
# - sqlPgroonga
# Use pgroonga.
# You need to install pgroonga and configure it as a PostgreSQL extension.
# In addition to the above, you need to create a pgroonga index on the text column of the note table.
# see: https://pgroonga.github.io/tutorial/
# - meilisearch
# Use Meilisearch.
# You need to install Meilisearch and configure.
provider: sqlLike
# For Meilisearch settings.
# If you select "meilisearch" for "fulltextSearch.provider", it must be set.
# You can set scope to local (default value) or global
# (include notes from remote).
#meilisearch:
# host: localhost
# port: 7700
# apiKey: ''
# ssl: true
# index: ''
# scope: local
# port: 9200
# ssl: false
# user:
# pass:
# ┌───────────────┐
#───┘ ID generation └───────────────────────────────────────────
@@ -285,7 +81,6 @@ fulltextSearch:
# Available methods:
# aid ... Short, Millisecond accuracy
# aidx ... Millisecond accuracy
# meid ... Similar to ObjectID, Millisecond accuracy
# ulid ... Millisecond accuracy
# objectid ... This is left for backward compatibility
@@ -293,27 +88,7 @@ fulltextSearch:
# ONCE YOU HAVE STARTED THE INSTANCE, DO NOT CHANGE THE
# ID SETTINGS AFTER THAT!
id: 'aidx'
# ┌────────────────┐
#───┘ Error tracking └──────────────────────────────────────────
# Sentry is available for error tracking.
# See the Sentry documentation for more details on options.
#sentryForBackend:
# enableNodeProfiling: true
# options:
# dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0'
#sentryForFrontend:
# vueIntegration:
# tracingOptions:
# trackComponents: true
# browserTracingIntegration:
# replayIntegration:
# options:
# dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0'
id: 'aid'
# ┌─────────────────────┐
#───┘ Other configuration └─────────────────────────────────────
@@ -321,31 +96,20 @@ id: 'aidx'
# Whether disable HSTS
#disableHsts: true
# Enable internal IP-based rate limiting (default: true)
# To configure them in reverse proxy instead, set this to false.
#enableIpRateLimit: true
# Number of worker processes
#clusterLimit: 1
# Job concurrency per worker
#deliverJobConcurrency: 128
#inboxJobConcurrency: 16
#relationshipJobConcurrency: 16
# What's relationshipJob?:
# Follow, unfollow, block and unblock(ings) while following-imports, etc. or account migrations.
# deliverJobConcurrency: 128
# inboxJobConcurrency: 16
# Job rate limiter
#deliverJobPerSec: 128
#inboxJobPerSec: 32
#relationshipJobPerSec: 64
# deliverJobPerSec: 128
# inboxJobPerSec: 16
# Job attempts
#deliverJobMaxAttempts: 12
#inboxJobMaxAttempts: 8
# Local address used for outgoing requests
#outgoingAddress: 127.0.0.1
# deliverJobMaxAttempts: 12
# inboxJobMaxAttempts: 8
# IP address family used for outgoing request (ipv4, ipv6 or dual)
#outgoingAddressFamily: ipv4
@@ -371,31 +135,22 @@ proxyBypassHosts:
# * Perform image compression (on a different server resource than the main process)
#mediaProxy: https://example.com/proxy
# Proxy remote files (default: false)
# Proxy remote files by this instance or mediaProxy to prevent remote files from running in remote domains.
#proxyRemoteFiles: true
# Movie Thumbnail Generation URL
# There is no reference implementation.
# For example, Misskey will point to the following URL:
# https://example.com/thumbnail.webp?thumbnail=1&url=https%3A%2F%2Fstorage.example.com%2Fpath%2Fto%2Fvideo.mp4
#videoThumbnailGenerator: https://example.com
# For security reasons, uploading attachments from the intranet is prohibited,
# but exceptions can be made from the following settings. Default value is "undefined".
# Read changelog to learn more (Improvements of 12.90.0 (2021/09/04)).
# Sign to ActivityPub GET request (default: true)
signToActivityPubGet: true
#allowedPrivateNetworks: [
# '127.0.0.1/32'
#]
# Upload or download file size limits (bytes)
#maxFileSize: 262144000
# PID File of master process
#pidFile: /tmp/misskey.pid
# Log settings
# logging:
# sql:
# # Outputs query parameters during SQL execution to the log.
# # default: false
# enableQueryParamLogging: false
# # Disable query truncation. If set to true, the full text of the query will be output to the log.
# # default: false
# disableQueryTruncation: false

View File

@@ -1 +1 @@
FROM mcr.microsoft.com/devcontainers/javascript-node:4.0.3-24-trixie
FROM mcr.microsoft.com/devcontainers/javascript-node:0-18

View File

@@ -1,24 +1,20 @@
{
"name": "Misskey",
"dockerComposeFile": "compose.yml",
"dockerComposeFile": "docker-compose.yml",
"service": "app",
"workspaceFolder": "/workspace",
"features": {
"ghcr.io/devcontainers/features/node:1": {
"version": "22.15.0"
},
"ghcr.io/devcontainers-extra/features/pnpm:2": {
"version": "10.10.0"
}
"ghcr.io/devcontainers-contrib/features/pnpm:2": {}
},
"forwardPorts": [3000],
"postCreateCommand": "/bin/bash .devcontainer/init.sh",
"postCreateCommand": "sudo chmod 755 .devcontainer/init.sh && .devcontainer/init.sh",
"customizations": {
"vscode": {
"extensions": [
"editorconfig.editorconfig",
"dbaeumer.vscode-eslint",
"Vue.volar",
"Vue.vscode-typescript-vue-plugin",
"Orta.vscode-jest",
"dbaeumer.vscode-eslint",
"mrmlnc.vscode-json5"

View File

@@ -51,23 +51,6 @@ db:
#extra:
# ssl: true
dbReplications: false
# You can configure any number of replicas here
#dbSlaves:
# -
# host:
# port:
# db:
# user:
# pass:
# -
# host:
# port:
# db:
# user:
# pass:
# ┌─────────────────────┐
#───┘ Redis configuration └─────────────────────────────────────
@@ -79,47 +62,15 @@ redis:
#prefix: example-prefix
#db: 1
#redisForPubsub:
# host: redis
# port: 6379
# #family: 0 # 0=Both, 4=IPv4, 6=IPv6
# #pass: example-pass
# #prefix: example-prefix
# #db: 1
# ┌─────────────────────────────┐
#───┘ Elasticsearch configuration └─────────────────────────────
#redisForJobQueue:
# host: redis
# port: 6379
# #family: 0 # 0=Both, 4=IPv4, 6=IPv6
# #pass: example-pass
# #prefix: example-prefix
# #db: 1
#redisForTimelines:
# host: redis
# port: 6379
# #family: 0 # 0=Both, 4=IPv4, 6=IPv6
# #pass: example-pass
# #prefix: example-prefix
# #db: 1
#redisForReactions:
# host: redis
# port: 6379
# #family: 0 # 0=Both, 4=IPv4, 6=IPv6
# #pass: example-pass
# #prefix: example-prefix
# #db: 1
# ┌───────────────────────────┐
#───┘ MeiliSearch configuration └─────────────────────────────
#meilisearch:
# host: meilisearch
# port: 7700
# apiKey: ''
# ssl: true
# index: ''
#elasticsearch:
# host: localhost
# port: 9200
# ssl: false
# user:
# pass:
# ┌───────────────┐
#───┘ ID generation └───────────────────────────────────────────
@@ -130,7 +81,6 @@ redis:
# Available methods:
# aid ... Short, Millisecond accuracy
# aidx ... Millisecond accuracy
# meid ... Similar to ObjectID, Millisecond accuracy
# ulid ... Millisecond accuracy
# objectid ... This is left for backward compatibility
@@ -138,27 +88,7 @@ redis:
# ONCE YOU HAVE STARTED THE INSTANCE, DO NOT CHANGE THE
# ID SETTINGS AFTER THAT!
id: 'aidx'
# ┌────────────────┐
#───┘ Error tracking └──────────────────────────────────────────
# Sentry is available for error tracking.
# See the Sentry documentation for more details on options.
#sentryForBackend:
# enableNodeProfiling: true
# options:
# dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0'
#sentryForFrontend:
# vueIntegration:
# tracingOptions:
# trackComponents: true
# browserTracingIntegration:
# replayIntegration:
# options:
# dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0'
id: 'aid'
# ┌─────────────────────┐
#───┘ Other configuration └─────────────────────────────────────
@@ -175,7 +105,7 @@ id: 'aidx'
# Job rate limiter
# deliverJobPerSec: 128
# inboxJobPerSec: 32
# inboxJobPerSec: 16
# Job attempts
# deliverJobMaxAttempts: 12
@@ -202,6 +132,12 @@ proxyBypassHosts:
# Media Proxy
#mediaProxy: https://example.com/proxy
# Proxy remote files (default: false)
#proxyRemoteFiles: true
# Sign to ActivityPub GET request (default: true)
signToActivityPubGet: true
allowedPrivateNetworks: [
'127.0.0.1/32'
]

View File

@@ -1,12 +1,13 @@
version: '3.8'
services:
app:
build:
build:
context: .
dockerfile: Dockerfile
volumes:
- ../:/workspace:cached
- node_modules:/workspace/node_modules
command: sleep infinity
@@ -28,7 +29,7 @@ services:
db:
restart: unless-stopped
image: postgres:18-alpine
image: postgres:15-alpine
networks:
- internal_network
environment:
@@ -45,7 +46,6 @@ services:
volumes:
postgres-data:
redis-data:
node_modules:
networks:
internal_network:

View File

@@ -2,14 +2,10 @@
set -xe
sudo chown node node_modules
sudo apt-get update
sudo apt-get -y install libgtk2.0-0 libgtk-3-0 libgbm-dev libnotify-dev libnss3 libxss1 libasound2 libxtst6 xauth xvfb
git config --global --add safe.directory /workspace
sudo chown -R node /workspace
git submodule update --init
pnpm config set store-dir /home/node/.local/share/pnpm/store
pnpm install --frozen-lockfile
cp .devcontainer/devcontainer.yml .config/default.yml
pnpm build
pnpm migrate
pnpm exec cypress install

View File

@@ -6,13 +6,14 @@
Dockerfile
build/
built/
src-js/
db/
.devcontainer/compose.yml
docker-compose.yml
elasticsearch/
node_modules/
packages/*/node_modules
redis/
files/
misskey-assets/
fluent-emojis/
.pnp.*
@@ -28,4 +29,4 @@ fluent-emojis/
.idea/
packages/*/.vscode/
packages/backend/test/compose.yml
packages/backend/test/docker-compose.yml

View File

@@ -6,14 +6,6 @@ indent_size = 2
charset = utf-8
insert_final_newline = true
end_of_line = lf
trim_trailing_whitespace = true
[*.md]
trim_trailing_whitespace = false
[*.{yml,yaml}]
indent_style = space
[packages/backend/migration/*.js]
indent_style = space
indent_size = 4

3
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,3 @@
# These are supported funding model platforms
patreon: syuilo

43
.github/ISSUE_TEMPLATE/01_bug-report.md vendored Normal file
View File

@@ -0,0 +1,43 @@
---
name: 🐛 Bug Report
about: Create a report to help us improve
title: ''
labels: âš ī¸bug?
assignees: ''
---
<!--
Thanks for reporting!
First, in order to avoid duplicate Issues, please search to see if the problem you found has already been reported.
Also, If you are NOT owner/admin of server, PLEASE DONT REPORT SERVER SPECIFIC ISSUES TO HERE! (e.g. feature XXX is not working in misskey.example) Please try with another misskey servers, and if your issue is only reproducible with specific server, contact your server's owner/admin first.
-->
## 💡 Summary
<!-- Tell us what the bug is -->
## đŸĨ° Expected Behavior
<!--- Tell us what should happen -->
## đŸ¤Ŧ Actual Behavior
<!--
Tell us what happens instead of the expected behavior.
Please include errors from the developer console and/or server log files if you have access to them.
-->
## 📝 Steps to Reproduce
1.
2.
3.
## 📌 Environment
<!-- Tell us where on the platform it happens -->
Misskey version:
Your OS:
Your browser:

View File

@@ -1,97 +0,0 @@
name: 🐛 Bug Report
description: Create a report to help us improve
labels: ["âš ī¸bug?"]
body:
- type: markdown
attributes:
value: |
Thanks for reporting!
First, in order to avoid duplicate Issues, please search to see if the problem you found has already been reported.
Also, If you are NOT owner/admin of server, PLEASE DONT REPORT SERVER SPECIFIC ISSUES TO HERE! (e.g. feature XXX is not working in misskey.example) Please try with another misskey servers, and if your issue is only reproducible with specific server, contact your server's owner/admin first.
- type: textarea
attributes:
label: 💡 Summary
description: Tell us what the bug is
validations:
required: true
- type: textarea
attributes:
label: đŸĨ° Expected Behavior
description: Tell us what should happen
validations:
required: true
- type: textarea
attributes:
label: đŸ¤Ŧ Actual Behavior
description: |
Tell us what happens instead of the expected behavior.
Please include errors from the developer console and/or server log files if you have access to them.
validations:
required: true
- type: textarea
attributes:
label: 📝 Steps to Reproduce
placeholder: |
1.
2.
3.
validations:
required: false
- type: textarea
attributes:
label: đŸ’ģ Frontend Environment
description: |
Tell us where on the platform it happens
DO NOT WRITE "latest". Please provide the specific version.
Examples:
* Model and OS of the device(s): MacBook Pro (14inch, 2021), macOS Ventura 13.4
* Browser: Chrome 113.0.5672.126
* Server URL: misskey.example.com
* Misskey: 2026.x.x
value: |
* Model and OS of the device(s):
* Browser:
* Server URL:
* Misskey:
render: markdown
validations:
required: false
- type: textarea
attributes:
label: 🛰 Backend Environment (for server admin)
description: |
Tell us where on the platform it happens
DO NOT WRITE "latest". Please provide the specific version.
If you are using a managed service, put that after the version.
Examples:
* Installation Method or Hosting Service: docker compose, k8s/docker, systemd, "Misskey install shell script", development environment
* Misskey: 2026.x.x
* Node: 20.x.x
* PostgreSQL: 18.x.x
* Redis: 7.x.x
* OS and Architecture: Ubuntu 24.04.2 LTS aarch64
value: |
* Installation Method or Hosting Service:
* Misskey:
* Node:
* PostgreSQL:
* Redis:
* OS and Architecture:
render: markdown
validations:
required: false
- type: checkboxes
attributes:
label: Do you want to address this bug yourself?
options:
- label: Yes, I will patch the bug myself and send a pull request

View File

@@ -0,0 +1,12 @@
---
name: ✨ Feature Request
about: Suggest an idea for this project
title: ''
labels: ✨Feature
assignees: ''
---
## Summary
<!-- Tell us what the suggestion is -->

View File

@@ -1,22 +0,0 @@
name: ✨ Feature Request
description: Suggest an idea for this project
labels: ["✨Feature"]
body:
- type: textarea
attributes:
label: Summary
description: Tell us what the suggestion is
validations:
required: true
- type: textarea
attributes:
label: Purpose
description: Describe the specific problem or need you think this feature will solve, and who it will help.
validations:
required: true
- type: checkboxes
attributes:
label: Do you want to implement this feature yourself?
options:
- label: Yes, I will implement this by myself and send a pull request

View File

@@ -1,8 +1,7 @@
contact_links:
- name: đŸ‘Ē Misskey Forum
url: https://forum.misskey.io/
about: Ask questions and share knowledge
- name: đŸ’Ŧ Misskey official Discord
url: https://discord.gg/Wp8gVStHW3
about: Chat freely about Misskey
# äģŽ
- name: đŸ’Ŧ Start discussion
url: https://github.com/misskey-dev/misskey/discussions
about: The official forum to join conversation and ask question

View File

@@ -10,36 +10,23 @@ updates:
schedule:
interval: daily
open-pull-requests-limit: 0
# Add only the root, not each workspace item
# https://github.com/dependabot/dependabot-core/issues/4993#issuecomment-1289133027
- package-ecosystem: npm
directory: "/"
schedule:
interval: daily
open-pull-requests-limit: 0
# List dependencies required to be updated together, sharing the same version numbers.
# Those who simply have the common owner (e.g. @fastify) don't need to be listed.
groups:
aws-sdk:
patterns:
- "@aws-sdk/*"
nestjs:
patterns:
- "@nestjs/*"
slacc:
patterns:
- "slacc-*"
storybook:
patterns:
- "storybook*"
- "@storybook/*"
swc-core:
patterns:
- "@swc/core*"
typescript-eslint:
patterns:
- "@typescript-eslint/*"
tensorflow:
patterns:
- "@tensorflow/*"
- package-ecosystem: npm
directory: "/packages/backend"
schedule:
interval: daily
open-pull-requests-limit: 0
- package-ecosystem: npm
directory: "/packages/frontend"
schedule:
interval: daily
open-pull-requests-limit: 0
- package-ecosystem: npm
directory: "/packages/sw"
schedule:
interval: daily
open-pull-requests-limit: 0

40
.github/labeler.yml vendored
View File

@@ -1,34 +1,12 @@
'packages/backend':
- any:
- changed-files:
- any-glob-to-any-file: ['packages/backend/**/*']
'âš™ī¸Server':
- packages/backend/**/*
'packages/backend:test':
- any:
- changed-files:
- any-glob-to-any-file: ['packages/backend/test/**/*', 'packages/backend/test-federation/**/*']
'đŸ–Ĩī¸Client':
- packages/frontend/**/*
'packages/frontend':
- any:
- changed-files:
- any-glob-to-any-file: ['packages/frontend/**/*']
'đŸ§ĒTest':
- cypress/**/*
- packages/backend/test/**/*
'packages/frontend:test':
- any:
- changed-files:
- any-glob-to-any-file: ['cypress/**/*']
'packages/sw':
- any:
- changed-files:
- any-glob-to-any-file: ['packages/sw/**/*']
'packages/misskey-js':
- any:
- changed-files:
- any-glob-to-any-file: ['packages/misskey-js/**/*']
'packages/misskey-js:test':
- any:
- changed-files:
- any-glob-to-any-file: ['packages/misskey-js/test/**/*', 'packages/misskey-js/test-d/**/*']
'â€ŧī¸ wrong locales':
- any: ['locales/*.yml', '!locales/ja-JP.yml']

View File

@@ -1 +0,0 @@
22.15.0

View File

@@ -1,7 +1,5 @@
url: 'http://misskey.local'
setupPassword: example_password_please_change_this_or_you_will_get_hacked
# ロãƒŧã‚ĢãƒĢでテ゚トするときãĢポãƒŧトをčĸĢらãĒいようãĢするためデフりãƒĢトぎもぎとは変える(äģĨ下同じ)
port: 61812
@@ -14,6 +12,4 @@ db:
redis:
host: 127.0.0.1
port: 56312
id: aidx
proxyRemoteFiles: true
id: aid

View File

@@ -19,6 +19,5 @@ https://github.com/misskey-dev/misskey/blob/develop/CONTRIBUTING.md
## Checklist
- [ ] Read the [contribution guide](https://github.com/misskey-dev/misskey/blob/develop/CONTRIBUTING.md)
- [ ] Test working in a local environment
- [ ] (If needed) Add story of storybook
- [ ] (If needed) Update CHANGELOG.md
- [ ] (If possible) Add tests

10
.github/reviewer-lottery.yml vendored Normal file
View File

@@ -0,0 +1,10 @@
groups:
- name: devs
reviewers: 2
internal_reviewers: 1
usernames:
- syuilo
- acid-chicken
- EbiseLutica
- rinsuki
- tamaina

View File

@@ -1,14 +1,7 @@
name: API report (misskey.js)
name: API report
on: [push, pull_request]
on:
push:
paths:
- packages/misskey-js/**
- .github/workflows/api-misskey-js.yml
pull_request:
paths:
- packages/misskey-js/**
- .github/workflows/api-misskey-js.yml
jobs:
report:
@@ -16,15 +9,14 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v6.0.1
uses: actions/checkout@v3.3.0
- name: Setup pnpm
uses: pnpm/action-setup@v4.2.0
- run: corepack enable
- name: Setup Node.js
uses: actions/setup-node@v6.1.0
uses: actions/setup-node@v3.6.0
with:
node-version-file: '.node-version'
node-version: 18.x
cache: 'pnpm'
- name: Install dependencies

View File

@@ -1,43 +0,0 @@
name: Check the description in CHANGELOG.md
on:
pull_request:
branches:
- master
- develop
jobs:
check-changelog:
runs-on: ubuntu-latest
steps:
- name: Checkout head
uses: actions/checkout@v6.0.1
- name: Setup Node.js
uses: actions/setup-node@v6.1.0
with:
node-version-file: '.node-version'
- name: Checkout base
run: |
mkdir _base
cp -r .git _base/.git
cd _base
git fetch --depth 1 origin ${{ github.base_ref }}
git checkout origin/${{ github.base_ref }} CHANGELOG.md
- name: Copy to Checker directory for CHANGELOG-base.md
run: cp _base/CHANGELOG.md scripts/changelog-checker/CHANGELOG-base.md
- name: Copy to Checker directory for CHANGELOG-head.md
run: cp CHANGELOG.md scripts/changelog-checker/CHANGELOG-head.md
- name: diff
continue-on-error: true
run: diff -u CHANGELOG-base.md CHANGELOG-head.md
working-directory: scripts/changelog-checker
- name: Setup Checker
run: npm install
working-directory: scripts/changelog-checker
- name: Run Checker
run: npm run run
working-directory: scripts/changelog-checker

View File

@@ -1,139 +0,0 @@
name: Check Misskey JS autogen
on:
pull_request_target:
branches:
- master
- develop
- improve-misskey-js-autogen-check
paths:
- packages/backend/**
jobs:
# pull_request_target safety: permissions: read-all, and there are no secrets used in this job
generate-misskey-js:
runs-on: ubuntu-latest
permissions:
contents: read
if: ${{ github.event.pull_request.mergeable == null || github.event.pull_request.mergeable == true }}
steps:
- name: checkout
uses: actions/checkout@v6.0.1
with:
submodules: true
persist-credentials: false
ref: refs/pull/${{ github.event.pull_request.number }}/merge
- name: setup pnpm
uses: pnpm/action-setup@v4
- name: setup node
id: setup-node
uses: actions/setup-node@v6.1.0
with:
node-version-file: '.node-version'
cache: pnpm
- name: install dependencies
run: pnpm i --frozen-lockfile
# generate api.json
- name: Copy Config
run: cp .config/example.yml .config/default.yml
- name: Build
run: pnpm build
- name: Generate API JSON
run: pnpm --filter backend generate-api-json
# build misskey js
- name: Build misskey-js
run: |-
cp packages/backend/built/api.json packages/misskey-js/generator/api.json
pnpm run --filter misskey-js-type-generator generate
# packages/misskey-js/generator/built/autogen
- name: Upload Generated
uses: actions/upload-artifact@v6
with:
name: generated-misskey-js
path: packages/misskey-js/generator/built/autogen
# pull_request_target safety: permissions: read-all, and no user codes are executed
get-actual-misskey-js:
runs-on: ubuntu-latest
permissions:
contents: read
if: ${{ github.event.pull_request.mergeable == null || github.event.pull_request.mergeable == true }}
steps:
- name: checkout
uses: actions/checkout@v6.0.1
with:
submodules: true
persist-credentials: false
ref: refs/pull/${{ github.event.pull_request.number }}/merge
- name: Upload From Merged
uses: actions/upload-artifact@v6
with:
name: actual-misskey-js
path: packages/misskey-js/src/autogen
# pull_request_target safety: nothing is cloned from repository
comment-misskey-js-autogen:
runs-on: ubuntu-latest
needs: [generate-misskey-js, get-actual-misskey-js]
permissions:
pull-requests: write
steps:
- name: download generated-misskey-js
uses: actions/download-artifact@v7
with:
name: generated-misskey-js
path: misskey-js-generated
- name: download actual-misskey-js
uses: actions/download-artifact@v7
with:
name: actual-misskey-js
path: misskey-js-actual
- name: check misskey-js changes
id: check-changes
run: |
diff -r -u --label=generated --label=on-tree ./misskey-js-generated ./misskey-js-actual > misskey-js.diff || true
if [ -s misskey-js.diff ]; then
echo "changes=true" >> $GITHUB_OUTPUT
else
echo "changes=false" >> $GITHUB_OUTPUT
fi
- name: Print full diff
run: cat ./misskey-js.diff
- name: send message
if: steps.check-changes.outputs.changes == 'true'
uses: thollander/actions-comment-pull-request@v3
with:
comment-tag: check-misskey-js-autogen
message: |-
Thank you for sending us a great Pull Request! 👍
Please regenerate misskey-js type definitions! 🙏
example:
```sh
pnpm run build-misskey-js-with-types
```
- name: send message
if: steps.check-changes.outputs.changes == 'false'
uses: thollander/actions-comment-pull-request@v3
with:
comment-tag: check-misskey-js-autogen
mode: delete
message: "Thank you!"
create_if_not_exists: false
- name: Make failure if changes are detected
if: steps.check-changes.outputs.changes == 'true'
run: exit 1

View File

@@ -1,29 +0,0 @@
name: Check Misskey JS version
on:
push:
branches: [ develop ]
paths:
- packages/misskey-js/package.json
- package.json
- .github/workflows/check-misskey-js-version.yml
pull_request:
branches: [ develop ]
paths:
- packages/misskey-js/package.json
- package.json
- .github/workflows/check-misskey-js-version.yml
jobs:
check-version:
# ãƒĢãƒŧトぎ package.json と packages/misskey-js/package.json ぎバãƒŧã‚¸ãƒ§ãƒŗãŒä¸€č‡´ã—ãĻいるかをįĸēčĒã™ã‚‹
name: Check version
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6.0.1
- name: Check version
run: |
if [ "$(jq -r '.version' package.json)" != "$(jq -r '.version' packages/misskey-js/package.json)" ]; then
echo "Version mismatch!"
exit 1
fi

View File

@@ -1,81 +0,0 @@
name: Check SPDX-License-Identifier
on:
push:
branches:
- master
- develop
pull_request:
jobs:
check-spdx-license-id:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6.0.1
- name: Check
run: |
counter=0
search() {
local directory="$1"
find "$directory" -type f \
'(' \
-name "*.cjs" -and -not -name '*.config.cjs' -o \
-name "*.html" -o \
-name "*.js" -and -not -name '*.config.js' -o \
-name "*.mjs" -and -not -name '*.config.mjs' -o \
-name "*.scss" -o \
-name "*.ts" -and -not -name '*.config.ts' -o \
-name "*.vue" \
')' -and \
-not -name '*eslint*'
}
check() {
local file="$1"
if ! (
grep -q "SPDX-FileCopyrightText: syuilo and misskey-project" "$file" ||
grep -q "SPDX-License-Identifier: AGPL-3.0-only" "$file"
); then
echo "Missing: $file"
((counter++))
fi
}
directories=(
"cypress/e2e"
"packages/backend/migration"
"packages/backend/src"
"packages/backend/test"
"packages/frontend-shared/@types"
"packages/frontend-shared/js"
"packages/frontend-builder"
"packages/frontend/.storybook"
"packages/frontend/@types"
"packages/frontend/lib"
"packages/frontend/public"
"packages/frontend/src"
"packages/frontend/test"
"packages/frontend-embed/@types"
"packages/frontend-embed/src"
"packages/icons-subsetter/src"
"packages/misskey-bubble-game/src"
"packages/misskey-reversi/src"
"packages/sw/src"
"scripts"
)
for directory in "${directories[@]}"; do
for file in $(search $directory); do
check "$file"
done
done
if [ $counter -gt 0 ]; then
echo "SPDX-License-Identifier is missing in $counter files."
exit 1
else
echo "SPDX-License-Identifier is certainly described in all target files!"
exit 0
fi

View File

@@ -10,7 +10,7 @@ jobs:
check_copyright_year:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6.0.1
- uses: actions/checkout@v3.2.0
- run: |
if [ "$(grep Copyright COPYING | sed -e 's/.*2014-\([0-9]*\) .*/\1/g')" -ne "$(date +%Y)" ]; then
echo "Please change copyright year!"

View File

@@ -1,84 +0,0 @@
name: deploy-test-environment
on:
issue_comment:
types: [created]
workflow_dispatch:
inputs:
repository:
description: 'Repository to deploy (optional, use the repository where this workflow is stored by default)'
required: false
default: ''
branch_or_hash:
description: 'Branch or Commit hash to deploy (optional, use the branch where this workflow is stored by default)'
required: false
default: ''
wait_time:
description: 'Time to wait in seconds (optional, 1800 seconds by default)'
required: false
default: ''
jobs:
get-pr-ref:
runs-on: ubuntu-latest
if: github.event_name == 'issue_comment' && github.event.issue.pull_request && startsWith(github.event.comment.body, '/preview')
outputs:
is-allowed-user: ${{ steps.check-allowed-users.outputs.is-allowed-user }}
pr-ref: ${{ steps.get-ref.outputs.pr-ref }}
wait_time: ${{ steps.get-wait-time.outputs.wait_time }}
steps:
- name: Checkout
uses: actions/checkout@v6.0.1
- name: Check allowed users
id: check-allowed-users
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ORG_ID: ${{ github.repository_owner_id }}
COMMENT_AUTHOR: ${{ github.event.comment.user.login }}
run: |
MEMBERSHIP_STATUS=$(curl -s -H "Authorization: Bearer $GITHUB_TOKEN" \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"https://api.github.com/organizations/$ORG_ID/public_members/$COMMENT_AUTHOR" \
-o /dev/null -w '%{http_code}\n' -s)
if [ "$MEMBERSHIP_STATUS" -eq 204 ]; then
echo "is-allowed-user=true" > $GITHUB_OUTPUT
else
echo "is-allowed-user=false" > $GITHUB_OUTPUT
fi
- name: Get PR ref
id: get-ref
run: |
PR_REF="refs/pull/${{ github.event.issue.number }}/head"
echo "pr-ref=$PR_REF" >> $GITHUB_OUTPUT
- name: Extract wait time
id: get-wait-time
env:
COMMENT_BODY: ${{ github.event.comment.body }}
run: |
WAIT_TIME=$(echo "$COMMENT_BODY" | grep -oP '(?<=/preview\s)\d+' || echo "1800")
echo "wait_time=$WAIT_TIME" > $GITHUB_OUTPUT
deploy-test-environment-pr-comment:
needs: get-pr-ref
if: needs.get-pr-ref.outputs.is-allowed-user == 'true'
uses: joinmisskey/misskey-tga/.github/workflows/deploy-test-environment.yml@main
with:
repository: ${{ github.repository }}
branch_or_hash: ${{ needs.get-pr-ref.outputs.pr-ref }}
wait_time: ${{ needs.get-pr-ref.outputs.wait_time }}
secrets:
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
deploy-test-environment-wd:
if: github.event_name == 'workflow_dispatch'
uses: joinmisskey/misskey-tga/.github/workflows/deploy-test-environment.yml@main
with:
repository: ${{ inputs.repository || github.repository }}
branch_or_hash: ${{ inputs.branch_or_hash || github.ref_name }}
wait_time: ${{ inputs.wait_time || '1800' }}
secrets:
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}

View File

@@ -6,83 +6,38 @@ on:
- develop
workflow_dispatch:
env:
REGISTRY_IMAGE: misskey/misskey
jobs:
# see https://docs.docker.com/build/ci/github-actions/multi-platform/#distribute-build-across-multiple-runners
build:
name: Build
push_to_registry:
name: Push Docker image to Docker Hub
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
platform:
- linux/amd64
- linux/arm64
if: github.repository == 'misskey-dev/misskey'
steps:
- name: Prepare
run: |
platform=${{ matrix.platform }}
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
- name: Check out the repo
uses: actions/checkout@v6.0.1
uses: actions/checkout@v3.3.0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
id: buildx
uses: docker/setup-buildx-action@v2.3.0
with:
platforms: linux/amd64,linux/arm64
- name: Docker meta
id: meta
uses: docker/metadata-action@v4
with:
images: misskey/misskey
- name: Log in to Docker Hub
uses: docker/login-action@v3
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push by digest
id: build
uses: docker/build-push-action@v6
- name: Build and Push to Docker Hub
uses: docker/build-push-action@v4
with:
builder: ${{ steps.buildx.outputs.name }}
context: .
push: true
platforms: ${{ matrix.platform }}
platforms: ${{ steps.buildx.outputs.platforms }}
provenance: false
tags: misskey/misskey:develop
labels: develop
cache-from: type=gha
cache-to: type=gha,mode=max
outputs: type=image,name=${{ env.REGISTRY_IMAGE }},push-by-digest=true,name-canonical=true,push=true
- name: Export digest
run: |
mkdir -p /tmp/digests
digest="${{ steps.build.outputs.digest }}"
touch "/tmp/digests/${digest#sha256:}"
- name: Upload digest
uses: actions/upload-artifact@v6
with:
name: digests-${{ env.PLATFORM_PAIR }}
path: /tmp/digests/*
if-no-files-found: error
retention-days: 1
merge:
runs-on: ubuntu-latest
needs:
- build
steps:
- name: Download digests
uses: actions/download-artifact@v7
with:
path: /tmp/digests
pattern: digests-*
merge-multiple: true
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Create manifest list and push
working-directory: /tmp/digests
run: |
docker buildx imagetools create --tag ${{ env.REGISTRY_IMAGE }}:develop \
$(printf '${{ env.REGISTRY_IMAGE }}@sha256:%s ' *)
- name: Inspect image
run: |
docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}:develop

View File

@@ -5,101 +5,45 @@ on:
types: [published]
workflow_dispatch:
env:
REGISTRY_IMAGE: misskey/misskey
TAGS: |
type=edge
type=ref,event=pr
type=ref,event=branch
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
jobs:
# see https://docs.docker.com/build/ci/github-actions/multi-platform/#distribute-build-across-multiple-runners
build:
name: Build
push_to_registry:
name: Push Docker image to Docker Hub
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
platform:
- linux/amd64
- linux/arm64
steps:
- name: Prepare
run: |
platform=${{ matrix.platform }}
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
- name: Check out the repo
uses: actions/checkout@v6.0.1
uses: actions/checkout@v3.3.0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
id: buildx
uses: docker/setup-buildx-action@v2.3.0
with:
platforms: linux/amd64,linux/arm64
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
uses: docker/metadata-action@v4
with:
images: ${{ env.REGISTRY_IMAGE }}
tags: ${{ env.TAGS }}
images: misskey/misskey
tags: |
type=edge
type=ref,event=pr
type=ref,event=branch
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
- name: Log in to Docker Hub
uses: docker/login-action@v3
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and Push to Docker Hub
id: build
uses: docker/build-push-action@v6
uses: docker/build-push-action@v4
with:
builder: ${{ steps.buildx.outputs.name }}
context: .
push: true
platforms: ${{ matrix.platform }}
platforms: ${{ steps.buildx.outputs.platforms }}
provenance: false
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
outputs: type=image,name=${{ env.REGISTRY_IMAGE }},push-by-digest=true,name-canonical=true,push=true
- name: Export digest
run: |
mkdir -p /tmp/digests
digest="${{ steps.build.outputs.digest }}"
touch "/tmp/digests/${digest#sha256:}"
- name: Upload digest
uses: actions/upload-artifact@v6
with:
name: digests-${{ env.PLATFORM_PAIR }}
path: /tmp/digests/*
if-no-files-found: error
retention-days: 1
merge:
runs-on: ubuntu-latest
needs:
- build
steps:
- name: Download digests
uses: actions/download-artifact@v7
with:
path: /tmp/digests
pattern: digests-*
merge-multiple: true
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY_IMAGE }}
tags: ${{ env.TAGS }}
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Create manifest list and push
working-directory: /tmp/digests
run: |
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
$(printf '${{ env.REGISTRY_IMAGE }}@sha256:%s ' *)
- name: Inspect image
run: |
docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}:${{ steps.meta.outputs.version }}

View File

@@ -11,43 +11,20 @@ on:
jobs:
dockle:
runs-on: ubuntu-latest
env:
DOCKER_CONTENT_TRUST: 1
DOCKLE_VERSION: 0.4.15
steps:
- uses: actions/checkout@v6.0.1
- name: Download and install dockle v${{ env.DOCKLE_VERSION }}
run: |
set -eux
curl -L -o dockle.deb "https://github.com/goodwithtech/dockle/releases/download/v${DOCKLE_VERSION}/dockle_${DOCKLE_VERSION}_Linux-64bit.deb"
- uses: actions/checkout@v3.2.0
- run: |
curl -L -o dockle.deb "https://github.com/goodwithtech/dockle/releases/download/v0.4.10/dockle_0.4.10_Linux-64bit.deb"
sudo dpkg -i dockle.deb
- name: Build web image (docker build)
run: |
set -eux
docker build -t "misskey-web:ci" .
docker image ls
- name: Mount tmpfs for Dockle tar
env:
TMPFS_SIZE: 8G
run: |
set -eux
sudo mkdir -p /mnt/dockle-tmp
sudo mount -t tmpfs -o size=${{ env.TMPFS_SIZE }} tmpfs /mnt/dockle-tmp
free -h
df -h
- name: Save image tar into tmpfs
run: |
set -eux
docker save misskey-web:ci -o /mnt/dockle-tmp/misskey-web.tar
ls -lh /mnt/dockle-tmp/misskey-web.tar
- name: Run Dockle Scan (tar input)
run: |
set -eux
dockle --exit-code 1 --input /mnt/dockle-tmp/misskey-web.tar
- run: |
cp .config/docker_example.env .config/docker.env
cp ./docker-compose.yml.example ./docker-compose.yml
- run: |
docker compose up -d web
docker tag "$(docker compose images web | awk 'OFS=":" {print $4}' | tail -n +2)" misskey-web:latest
- run: |
cmd="dockle --exit-code 1 misskey-web:latest ${image_name}"
echo "> ${cmd}"
eval "${cmd}"

View File

@@ -1,67 +0,0 @@
# this name is used in report-api-diff.yml so be careful when change name
name: Get api.json from Misskey
on:
pull_request:
branches:
- master
- develop
paths:
- packages/backend/**
- .github/workflows/get-api-diff.yml
jobs:
get-from-misskey:
runs-on: ubuntu-latest
permissions:
contents: read
strategy:
matrix:
api-json-name: [api-base.json, api-head.json]
include:
- api-json-name: api-base.json
ref: ${{ github.base_ref }}
- api-json-name: api-head.json
ref: refs/pull/${{ github.event.number }}/merge
steps:
- uses: actions/checkout@v6.0.1
with:
ref: ${{ matrix.ref }}
submodules: true
- name: Setup pnpm
uses: pnpm/action-setup@v4.2.0
- name: Use Node.js
uses: actions/setup-node@v6.1.0
with:
node-version-file: '.node-version'
cache: 'pnpm'
- run: pnpm i --frozen-lockfile
- name: Check pnpm-lock.yaml
run: git diff --exit-code pnpm-lock.yaml
- name: Copy Configure
run: cp .config/example.yml .config/default.yml
- name: Build
run: pnpm build
- name: Generate API JSON
run: pnpm --filter backend generate-api-json
- name: Copy API.json
run: cp packages/backend/built/api.json ${{ matrix.api-json-name }}
- name: Upload Artifact
uses: actions/upload-artifact@v6
with:
name: api-artifact-${{ matrix.api-json-name }}
path: ${{ matrix.api-json-name }}
save-pr-number:
runs-on: ubuntu-latest
steps:
- name: Save PR number
env:
PR_NUMBER: ${{ github.event.number }}
run: |
echo "$PR_NUMBER" > ./pr_number
- uses: actions/upload-artifact@v6
with:
name: api-artifact-pr-number
path: pr_number

View File

@@ -1,87 +0,0 @@
# this name is used in report-backend-memory.yml so be careful when change name
name: Get backend memory usage
on:
pull_request:
branches:
- master
- develop
paths:
- packages/backend/**
- packages/misskey-js/**
- .github/workflows/get-backend-memory.yml
jobs:
get-memory-usage:
runs-on: ubuntu-latest
permissions:
contents: read
strategy:
matrix:
memory-json-name: [memory-base.json, memory-head.json]
include:
- memory-json-name: memory-base.json
ref: ${{ github.base_ref }}
- memory-json-name: memory-head.json
ref: refs/pull/${{ github.event.number }}/merge
services:
postgres:
image: postgres:18
ports:
- 54312:5432
env:
POSTGRES_DB: test-misskey
POSTGRES_HOST_AUTH_METHOD: trust
redis:
image: redis:7
ports:
- 56312:6379
steps:
- uses: actions/checkout@v6.0.1
with:
ref: ${{ matrix.ref }}
submodules: true
- name: Setup pnpm
uses: pnpm/action-setup@v4.2.0
- name: Use Node.js
uses: actions/setup-node@v6.1.0
with:
node-version-file: '.node-version'
cache: 'pnpm'
- run: pnpm i --frozen-lockfile
- name: Check pnpm-lock.yaml
run: git diff --exit-code pnpm-lock.yaml
- name: Copy Configure
run: cp .github/misskey/test.yml .config/default.yml
- name: Compile Configure
run: pnpm compile-config
- name: Build
run: pnpm build
- name: Run migrations
run: pnpm --filter backend migrate
- name: Measure memory usage
run: |
# Start the server and measure memory usage
node packages/backend/scripts/measure-memory.mjs > ${{ matrix.memory-json-name }}
- name: Upload Artifact
uses: actions/upload-artifact@v6
with:
name: memory-artifact-${{ matrix.memory-json-name }}
path: ${{ matrix.memory-json-name }}
save-pr-number:
runs-on: ubuntu-latest
permissions: {}
steps:
- name: Save PR number
env:
PR_NUMBER: ${{ github.event.number }}
run: |
echo "$PR_NUMBER" > ./pr_number
- uses: actions/upload-artifact@v6
with:
name: memory-artifact-pr-number
path: pr_number

View File

@@ -11,6 +11,6 @@ jobs:
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: actions/labeler@v6
- uses: actions/labeler@v4
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"

View File

@@ -5,47 +5,25 @@ on:
branches:
- master
- develop
paths:
- packages/backend/**
- packages/frontend/**
- packages/frontend-shared/**
- packages/frontend-builder/**
- packages/frontend-embed/**
- packages/icons-subsetter/**
- packages/sw/**
- packages/misskey-js/**
- packages/misskey-bubble-game/**
- packages/misskey-reversi/**
- packages/shared/eslint.config.js
- .github/workflows/lint.yml
pull_request:
paths:
- packages/backend/**
- packages/frontend/**
- packages/frontend-shared/**
- packages/frontend-builder/**
- packages/frontend-embed/**
- packages/icons-subsetter/**
- packages/sw/**
- packages/misskey-js/**
- packages/misskey-bubble-game/**
- packages/misskey-reversi/**
- packages/shared/eslint.config.js
- .github/workflows/lint.yml
jobs:
pnpm_install:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6.0.1
- uses: actions/checkout@v3.3.0
with:
fetch-depth: 0
submodules: true
- name: Setup pnpm
uses: pnpm/action-setup@v4.2.0
- uses: actions/setup-node@v6.1.0
- uses: pnpm/action-setup@v2
with:
node-version-file: '.node-version'
version: 7
run_install: false
- uses: actions/setup-node@v3.6.0
with:
node-version: 18.x
cache: 'pnpm'
- run: corepack enable
- run: pnpm i --frozen-lockfile
lint:
@@ -57,36 +35,24 @@ jobs:
workspace:
- backend
- frontend
- frontend-shared
- frontend-builder
- frontend-embed
- icons-subsetter
- sw
- misskey-js
- misskey-bubble-game
- misskey-reversi
env:
eslint-cache-version: v1
eslint-cache-path: ${{ github.workspace }}/node_modules/.cache/eslint-${{ matrix.workspace }}
steps:
- uses: actions/checkout@v6.0.1
- uses: actions/checkout@v3.3.0
with:
fetch-depth: 0
submodules: true
- name: Setup pnpm
uses: pnpm/action-setup@v4.2.0
- uses: actions/setup-node@v6.1.0
- uses: pnpm/action-setup@v2
with:
node-version-file: '.node-version'
version: 7
run_install: false
- uses: actions/setup-node@v3.6.0
with:
node-version: 18.x
cache: 'pnpm'
- run: corepack enable
- run: pnpm i --frozen-lockfile
- name: Restore eslint cache
uses: actions/cache@v4.3.0
with:
path: ${{ env.eslint-cache-path }}
key: eslint-${{ env.eslint-cache-version }}-${{ matrix.workspace }}-${{ hashFiles('**/pnpm-lock.yaml') }}-${{ github.ref_name }}-${{ github.sha }}
restore-keys: eslint-${{ env.eslint-cache-version }}-${{ matrix.workspace }}-${{ hashFiles('**/pnpm-lock.yaml') }}-
- run: pnpm --filter ${{ matrix.workspace }} run eslint --cache --cache-location ${{ env.eslint-cache-path }} --cache-strategy content
- run: pnpm --filter ${{ matrix.workspace }} run eslint
typecheck:
needs: [pnpm_install]
@@ -96,20 +62,20 @@ jobs:
matrix:
workspace:
- backend
- frontend
- sw
- misskey-js
steps:
- uses: actions/checkout@v6.0.1
- uses: actions/checkout@v3.3.0
with:
fetch-depth: 0
submodules: true
- name: Setup pnpm
uses: pnpm/action-setup@v4.2.0
- uses: actions/setup-node@v6.1.0
- uses: pnpm/action-setup@v2
with:
node-version-file: '.node-version'
version: 7
run_install: false
- uses: actions/setup-node@v3.6.0
with:
node-version: 18.x
cache: 'pnpm'
- run: corepack enable
- run: pnpm i --frozen-lockfile
- run: pnpm --filter "${{ matrix.workspace }}^..." run build
- run: pnpm --filter ${{ matrix.workspace }} run typecheck

View File

@@ -1,33 +0,0 @@
name: Lint
on:
push:
paths:
- packages/i18n/**
- locales/**
- .github/workflows/locale.yml
pull_request:
paths:
- packages/i18n/**
- locales/**
- .github/workflows/locale.yml
jobs:
locale_verify:
runs-on: ubuntu-latest
continue-on-error: true
steps:
- uses: actions/checkout@v6.0.1
with:
fetch-depth: 0
submodules: true
- name: Setup pnpm
uses: pnpm/action-setup@v4.2.0
- uses: actions/setup-node@v6.1.0
with:
node-version-file: ".node-version"
cache: "pnpm"
- run: pnpm i --frozen-lockfile
- run: pnpm --filter i18n build
- name: Verify Locales
working-directory: ./packages/i18n
run: pnpm run verify

36
.github/workflows/ok-to-test.yml vendored Normal file
View File

@@ -0,0 +1,36 @@
# If someone with write access comments "/ok-to-test" on a pull request, emit a repository_dispatch event
name: Ok To Test
on:
issue_comment:
types: [created]
jobs:
ok-to-test:
runs-on: ubuntu-latest
# Only run for PRs, not issue comments
if: ${{ github.event.issue.pull_request }}
steps:
# Generate a GitHub App installation access token from an App ID and private key
# To create a new GitHub App:
# https://developer.github.com/apps/building-github-apps/creating-a-github-app/
# See app.yml for an example app manifest
- name: Generate token
id: generate_token
uses: tibdex/github-app-token@v1
with:
app_id: ${{ secrets.DEPLOYBOT_APP_ID }}
private_key: ${{ secrets.DEPLOYBOT_PRIVATE_KEY }}
- name: Slash Command Dispatch
uses: peter-evans/slash-command-dispatch@v1
env:
TOKEN: ${{ steps.generate_token.outputs.token }}
with:
token: ${{ env.TOKEN }} # GitHub App installation access token
# token: ${{ secrets.PERSONAL_ACCESS_TOKEN }} # PAT or OAuth token will also work
reaction-token: ${{ secrets.GITHUB_TOKEN }}
issue-type: pull-request
commands: deploy
named-args: true
permission: write

View File

@@ -1,38 +0,0 @@
name: On Release Created (Publish misskey-js)
on:
release:
types: [created]
workflow_dispatch:
jobs:
publish-misskey-js:
name: Publish misskey-js
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v6.0.1
with:
submodules: true
- name: Setup pnpm
uses: pnpm/action-setup@v4.2.0
- name: Use Node.js
uses: actions/setup-node@v6.1.0
with:
node-version-file: '.node-version'
cache: 'pnpm'
# see https://docs.github.com/actions/use-cases-and-examples/publishing-packages/publishing-nodejs-packages#publishing-packages-to-the-npm-registry
registry-url: 'https://registry.npmjs.org'
- name: Publish package
run: |
pnpm i --frozen-lockfile
pnpm build
pnpm --filter misskey-js publish --access public --no-git-checks --provenance
env:
NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }}
NPM_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }}

92
.github/workflows/pr-preview-deploy.yml vendored Normal file
View File

@@ -0,0 +1,92 @@
# Run secret-dependent integration tests only after /deploy approval
on:
repository_dispatch:
types: [deploy-command]
name: Deploy preview environment
jobs:
# Repo owner has commented /deploy on a (fork-based) pull request
deploy-preview-environment:
runs-on: ubuntu-latest
if:
github.event.client_payload.slash_command.sha != '' &&
contains(github.event.client_payload.pull_request.head.sha, github.event.client_payload.slash_command.sha)
steps:
- uses: actions/github-script@v6.3.3
id: check-id
env:
number: ${{ github.event.client_payload.pull_request.number }}
job: ${{ github.job }}
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
result-encoding: string
script: |
const { data: pull } = await github.rest.pulls.get({
...context.repo,
pull_number: process.env.number
});
const ref = pull.head.sha;
const { data: checks } = await github.rest.checks.listForRef({
...context.repo,
ref
});
const check = checks.check_runs.filter(c => c.name === process.env.job);
return check[0].id;
- uses: actions/github-script@v6.3.3
env:
check_id: ${{ steps.check-id.outputs.result }}
details_url: ${{ github.server_url }}/${{ github.repository }}/runs/${{ github.run_id }}
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
await github.rest.checks.update({
...context.repo,
check_run_id: process.env.check_id,
status: 'in_progress',
details_url: process.env.details_url
});
# Check out merge commit
- name: Fork based /deploy checkout
uses: actions/checkout@v3.3.0
with:
ref: 'refs/pull/${{ github.event.client_payload.pull_request.number }}/merge'
# <insert integration tests needing secrets>
- name: Context
uses: okteto/context@latest
with:
token: ${{ secrets.OKTETO_TOKEN }}
- name: Deploy preview environment
uses: ikuradon/deploy-preview@latest
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
name: pr-${{ github.event.client_payload.pull_request.number }}-syuilo
timeout: 15m
# Update check run called "integration-fork"
- uses: actions/github-script@v6.3.3
id: update-check-run
if: ${{ always() }}
env:
# Conveniently, job.status maps to https://developer.github.com/v3/checks/runs/#update-a-check-run
conclusion: ${{ job.status }}
check_id: ${{ steps.check-id.outputs.result }}
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const { data: result } = await github.rest.checks.update({
...context.repo,
check_run_id: process.env.check_id,
status: 'completed',
conclusion: process.env.conclusion
});
return result;

View File

@@ -0,0 +1,54 @@
# file: .github/workflows/preview-closed.yaml
on:
pull_request:
types:
- closed
name: Destroy preview environment
jobs:
destroy-preview-environment:
runs-on: ubuntu-latest
steps:
- uses: actions/github-script@v6.3.3
id: check-conclusion
env:
number: ${{ github.event.number }}
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
result-encoding: string
script: |
const { data: pull } = await github.rest.pulls.get({
...context.repo,
pull_number: process.env.number
});
const ref = pull.head.sha;
const { data: checks } = await github.rest.checks.listForRef({
...context.repo,
ref
});
const check = checks.check_runs.filter(c => c.name === 'deploy-preview-environment');
if (check.length === 0) {
return;
}
const { data: result } = await github.rest.checks.get({
...context.repo,
check_run_id: check[0].id,
});
return result.conclusion;
- name: Context
if: steps.check-conclusion.outputs.result == 'success'
uses: okteto/context@latest
with:
token: ${{ secrets.OKTETO_TOKEN }}
- name: Destroy preview environment
if: steps.check-conclusion.outputs.result == 'success'
uses: okteto/destroy-preview@latest
with:
name: pr-${{ github.event.number }}-syuilo

View File

@@ -1,48 +0,0 @@
name: "Release Manager: sync changelog with PR"
on:
push:
branches:
- develop
paths:
- 'CHANGELOG.md'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
permissions:
contents: write
issues: write
pull-requests: write
jobs:
edit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
# headが$GITHUB_REF_NAME, baseが$STABLE_BRANCHかつopenぎPRを1つ取垗
- name: Get PR
run: |
echo "pr_number=$(gh pr list --limit 1 --search "head:$GITHUB_REF_NAME base:$STABLE_BRANCH is:open" --json number --jq '.[] | .number')" >> $GITHUB_OUTPUT
id: get_pr
env:
STABLE_BRANCH: ${{ vars.STABLE_BRANCH }}
- name: Get target version
if: steps.get_pr.outputs.pr_number != ''
uses: misskey-dev/release-manager-actions/.github/actions/get-target-version@v2
id: v
# CHANGELOG.mdぎ内厚を取垗
- name: Get changelog
if: steps.get_pr.outputs.pr_number != ''
uses: misskey-dev/release-manager-actions/.github/actions/get-changelog@v2
with:
version: ${{ steps.v.outputs.target_version }}
id: changelog
# PRぎnotesを更新
- name: Update PR
if: steps.get_pr.outputs.pr_number != ''
run: |
gh pr edit "$PR_NUMBER" --body "$CHANGELOG"
env:
PR_NUMBER: ${{ steps.get_pr.outputs.pr_number }}
CHANGELOG: ${{ steps.changelog.outputs.changelog }}

View File

@@ -1,136 +0,0 @@
name: "Release Manager [Dispatch]"
on:
workflow_dispatch:
inputs:
## Specify the type of the next release.
#version_increment_type:
# type: choice
# description: 'VERSION INCREMENT TYPE'
# default: 'patch'
# required: false
# options:
# - 'major'
# - 'minor'
# - 'patch'
merge:
type: boolean
description: 'MERGE RELEASE BRANCH TO MAIN'
default: false
start-rc:
type: boolean
description: 'Start Release Candidate'
default: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
permissions:
contents: write
issues: write
pull-requests: write
jobs:
get-pr:
runs-on: ubuntu-latest
outputs:
pr_number: ${{ steps.get_pr.outputs.pr_number }}
steps:
- uses: actions/checkout@v6
# headが$GITHUB_REF_NAME, baseが$STABLE_BRANCHかつopenぎPRを1つ取垗
- name: Get PRs
run: |
echo "pr_number=$(gh pr list --limit 1 --search "head:$GITHUB_REF_NAME base:$STABLE_BRANCH is:open" --json number --jq '.[] | .number')" >> $GITHUB_OUTPUT
id: get_pr
env:
STABLE_BRANCH: ${{ vars.STABLE_BRANCH }}
merge:
uses: misskey-dev/release-manager-actions/.github/workflows/merge.yml@v2
needs: get-pr
if: ${{ needs.get-pr.outputs.pr_number != '' && inputs.merge == true }}
with:
pr_number: ${{ needs.get-pr.outputs.pr_number }}
user: 'github-actions[bot]'
package_jsons_to_rewrite: ${{ vars.PACKAGE_JSONS_TO_REWRITE }}
# Text to prepend to the changelog
# The first line must be `## Unreleased`
changes_template: |
## Unreleased
### General
-
### Client
-
### Server
-
use_external_app_to_release: ${{ vars.USE_RELEASE_APP == 'true' }}
indent: ${{ vars.INDENT }}
secrets:
RELEASE_APP_ID: ${{ secrets.RELEASE_APP_ID }}
RELEASE_APP_PRIVATE_KEY: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
create-prerelease:
uses: misskey-dev/release-manager-actions/.github/workflows/create-prerelease.yml@v2
needs: get-pr
if: ${{ needs.get-pr.outputs.pr_number != '' && inputs.merge != true }}
with:
pr_number: ${{ needs.get-pr.outputs.pr_number }}
user: 'github-actions[bot]'
package_jsons_to_rewrite: ${{ vars.PACKAGE_JSONS_TO_REWRITE }}
use_external_app_to_release: ${{ vars.USE_RELEASE_APP == 'true' }}
indent: ${{ vars.INDENT }}
draft_prerelease_channel: alpha
ready_start_prerelease_channel: beta
prerelease_channel: ${{ inputs.start-rc && 'rc' || '' }}
reset_number_on_channel_change: true
secrets:
RELEASE_APP_ID: ${{ secrets.RELEASE_APP_ID }}
RELEASE_APP_PRIVATE_KEY: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
create-target:
uses: misskey-dev/release-manager-actions/.github/workflows/create-target.yml@v2
needs: get-pr
if: ${{ needs.get-pr.outputs.pr_number == '' }}
with:
user: 'github-actions[bot]'
# The script for version increment.
# process.env.CURRENT_VERSION: The current version.
#
# Misskey calender versioning (yyyy.MM.patch) example
version_increment_script: |
const now = new Date();
const year = now.toLocaleDateString('en-US', { year: 'numeric', timeZone: 'Asia/Tokyo' });
const month = now.toLocaleDateString('en-US', { month: 'numeric', timeZone: 'Asia/Tokyo' });
const [major, minor, _patch] = process.env.CURRENT_VERSION.split('.');
const patch = Number(_patch.split('-')[0]);
if (Number.isNaN(patch)) {
console.error('Invalid patch version', year, month, process.env.CURRENT_VERSION, major, minor, _patch);
throw new Error('Invalid patch version');
}
if (year !== major || month !== minor) {
return `${year}.${month}.0`;
} else {
return `${major}.${minor}.${patch + 1}`;
}
##Semver example
#version_increment_script: |
# const [major, minor, patch] = process.env.CURRENT_VERSION.split('.');
# if ("${{ inputs.version_increment_type }}" === "major") {
# return `${Number(major) + 1}.0.0`;
# } else if ("${{ inputs.version_increment_type }}" === "minor") {
# return `${major}.${Number(minor) + 1}.0`;
# } else {
# return `${major}.${minor}.${Number(patch) + 1}`;
# }
package_jsons_to_rewrite: ${{ vars.PACKAGE_JSONS_TO_REWRITE }}
use_external_app_to_release: ${{ vars.USE_RELEASE_APP == 'true' }}
indent: ${{ vars.INDENT }}
stable_branch: ${{ vars.STABLE_BRANCH }}
draft_prerelease_channel: alpha
secrets:
RELEASE_APP_ID: ${{ secrets.RELEASE_APP_ID }}
RELEASE_APP_PRIVATE_KEY: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}

View File

@@ -1,104 +0,0 @@
name: Report API Diff
on:
workflow_run:
types: [completed]
workflows:
- Get api.json from Misskey # get-api-diff.yml
jobs:
compare-diff:
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion == 'success' }}
permissions:
pull-requests: write
# api-artifact
steps:
- name: Download artifact
uses: actions/github-script@v8.0.0
with:
script: |
const fs = require('fs');
let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: context.payload.workflow_run.id,
});
let matchArtifacts = allArtifacts.data.artifacts.filter((artifact) => {
return artifact.name.startsWith("api-artifact-") || artifact.name == "api-artifact"
});
await Promise.all(matchArtifacts.map(async (artifact) => {
let download = await github.rest.actions.downloadArtifact({
owner: context.repo.owner,
repo: context.repo.repo,
artifact_id: artifact.id,
archive_format: 'zip',
});
await fs.promises.writeFile(`${process.env.GITHUB_WORKSPACE}/${artifact.name}.zip`, Buffer.from(download.data));
}));
- name: Extract all artifacts
run: |
find . -mindepth 1 -maxdepth 1 -type f -name '*.zip' -exec unzip {} -d artifacts ';'
ls -la
- name: Load PR Number
id: load-pr-num
run: echo "pr-number=$(cat artifacts/pr_number)" >> "$GITHUB_OUTPUT"
- name: Output base
run: cat ./artifacts/api-base.json
- name: Output head
run: cat ./artifacts/api-head.json
- name: Arrange json files
run: |
jq '.' ./artifacts/api-base.json > ./api-base.json
jq '.' ./artifacts/api-head.json > ./api-head.json
- name: Get diff of 2 files
run: diff -u --label=base --label=head ./api-base.json ./api-head.json | cat > api.json.diff
- name: Get full diff
run: diff --label=base --label=head --new-line-format='+%L' --old-line-format='-%L' --unchanged-line-format=' %L' ./api-base.json ./api-head.json | cat > api-full.json.diff
- name: Echo full diff
run: cat ./api-full.json.diff
- name: Upload full diff to Artifact
uses: actions/upload-artifact@v6
with:
name: api-artifact
path: |
api-full.json.diff
api-base.json
api-head.json
- id: out-diff
name: Build diff Comment
run: |
HEADER="こぎPRãĢよるapi.jsonãŽåˇŽåˆ†"
FOOTER="[Get diff files from Workflow Page](https://github.com/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID})"
DIFF_BYTES="$(stat ./api.json.diff -c '%s' | tr -d '\n')"
echo "$HEADER" > ./output.md
if (( "$DIFF_BYTES" <= 1 )); then
echo 'åˇŽåˆ†ã¯ã‚ã‚Šãžã›ã‚“ã€‚' >> ./output.md
else
echo '<details>' >> ./output.md
echo '<summary>åˇŽåˆ†ã¯ã“ãĄã‚‰</summary>' >> ./output.md
echo >> ./output.md
echo '```diff' >> ./output.md
cat ./api.json.diff >> ./output.md
echo '```' >> ./output.md
echo '</details>' >> .output.md
fi
echo "$FOOTER" >> ./output.md
- uses: thollander/actions-comment-pull-request@v3
with:
pr-number: ${{ steps.load-pr-num.outputs.pr-number }}
comment-tag: show_diff
file-path: ./output.md
- name: Tell error to PR
uses: thollander/actions-comment-pull-request@v3
if: failure() && steps.load-pr-num.outputs.pr-number
with:
pr-number: ${{ steps.load-pr-num.outputs.pr-number }}
comment-tag: show_diff_error
message: |
api.jsonãŽåˇŽåˆ†äŊœæˆä¸­ãĢエナãƒŧがį™ēį”Ÿã—ãžã—ãŸã€‚čŠŗį´°ã¯[Workflowぎログ](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})をįĸēčĒã—ãĻください。

View File

@@ -1,122 +0,0 @@
name: Report backend memory
on:
workflow_run:
types: [completed]
workflows:
- Get backend memory usage # get-backend-memory.yml
jobs:
compare-memory:
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion == 'success' }}
permissions:
pull-requests: write
steps:
- name: Download artifact
uses: actions/github-script@v8.0.0
with:
script: |
const fs = require('fs');
let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: context.payload.workflow_run.id,
});
let matchArtifacts = allArtifacts.data.artifacts.filter((artifact) => {
return artifact.name.startsWith("memory-artifact-") || artifact.name == "memory-artifact"
});
await Promise.all(matchArtifacts.map(async (artifact) => {
let download = await github.rest.actions.downloadArtifact({
owner: context.repo.owner,
repo: context.repo.repo,
artifact_id: artifact.id,
archive_format: 'zip',
});
await fs.promises.writeFile(`${process.env.GITHUB_WORKSPACE}/${artifact.name}.zip`, Buffer.from(download.data));
}));
- name: Extract all artifacts
run: |
find . -mindepth 1 -maxdepth 1 -type f -name '*.zip' -exec unzip {} -d artifacts ';'
ls -la artifacts/
- name: Load PR Number
id: load-pr-num
run: echo "pr-number=$(cat artifacts/pr_number)" >> "$GITHUB_OUTPUT"
- name: Output base
run: cat ./artifacts/memory-base.json
- name: Output head
run: cat ./artifacts/memory-head.json
- name: Compare memory usage
id: compare
run: |
BASE_MEMORY=$(cat ./artifacts/memory-base.json)
HEAD_MEMORY=$(cat ./artifacts/memory-head.json)
BASE_RSS=$(echo "$BASE_MEMORY" | jq -r '.memory.rss // 0')
HEAD_RSS=$(echo "$HEAD_MEMORY" | jq -r '.memory.rss // 0')
# Calculate difference
if [ "$BASE_RSS" -gt 0 ] && [ "$HEAD_RSS" -gt 0 ]; then
DIFF=$((HEAD_RSS - BASE_RSS))
DIFF_PERCENT=$(echo "scale=2; ($DIFF * 100) / $BASE_RSS" | bc)
# Convert to MB for readability
BASE_MB=$(echo "scale=2; $BASE_RSS / 1048576" | bc)
HEAD_MB=$(echo "scale=2; $HEAD_RSS / 1048576" | bc)
DIFF_MB=$(echo "scale=2; $DIFF / 1048576" | bc)
echo "base_mb=$BASE_MB" >> "$GITHUB_OUTPUT"
echo "head_mb=$HEAD_MB" >> "$GITHUB_OUTPUT"
echo "diff_mb=$DIFF_MB" >> "$GITHUB_OUTPUT"
echo "diff_percent=$DIFF_PERCENT" >> "$GITHUB_OUTPUT"
echo "has_data=true" >> "$GITHUB_OUTPUT"
# Determine if this is a significant change (more than 5% increase)
if [ "$(echo "$DIFF_PERCENT > 5" | bc)" -eq 1 ]; then
echo "significant_increase=true" >> "$GITHUB_OUTPUT"
else
echo "significant_increase=false" >> "$GITHUB_OUTPUT"
fi
else
echo "has_data=false" >> "$GITHUB_OUTPUT"
fi
- id: build-comment
name: Build memory comment
run: |
HEADER="## Backend Memory Usage Comparison"
FOOTER="[See workflow logs for details](https://github.com/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID})"
echo "$HEADER" > ./output.md
echo >> ./output.md
if [ "${{ steps.compare.outputs.has_data }}" == "true" ]; then
echo "| Metric | base | head | Diff |" >> ./output.md
echo "|--------|------|------|------|" >> ./output.md
echo "| RSS | ${{ steps.compare.outputs.base_mb }} MB | ${{ steps.compare.outputs.head_mb }} MB | ${{ steps.compare.outputs.diff_mb }} MB (${{ steps.compare.outputs.diff_percent }}%) |" >> ./output.md
echo >> ./output.md
if [ "${{ steps.compare.outputs.significant_increase }}" == "true" ]; then
echo "âš ī¸ **Warning**: Memory usage has increased by more than 5%. Please verify this is not an unintended change." >> ./output.md
echo >> ./output.md
fi
else
echo "Could not retrieve memory usage data." >> ./output.md
echo >> ./output.md
fi
echo "$FOOTER" >> ./output.md
- uses: thollander/actions-comment-pull-request@v3
with:
pr-number: ${{ steps.load-pr-num.outputs.pr-number }}
comment-tag: show_memory_diff
file-path: ./output.md
- name: Tell error to PR
uses: thollander/actions-comment-pull-request@v3
if: failure() && steps.load-pr-num.outputs.pr-number
with:
pr-number: ${{ steps.load-pr-num.outputs.pr-number }}
comment-tag: show_memory_diff_error
message: |
An error occurred while comparing backend memory usage. See [workflow logs](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}) for details.

View File

@@ -1,51 +0,0 @@
name: Request release review
on:
issue_comment:
types: [created]
jobs:
reply:
if: github.event.comment.body == '/request-release-review'
runs-on: ubuntu-latest
permissions:
contents: read
issues: write
pull-requests: write
steps:
- name: Reply
uses: actions/github-script@v8
with:
script: |
const body = `To dev team (@misskey-dev/dev):
ãƒĒãƒĒãƒŧã‚šãŒææĄˆã•ã‚ŒãĻいぞす :rocket:
GOぎ場合はapprove、NO GOãŽå ´åˆã¯ããŽæ—¨ã‚ŗãƒĄãƒŗãƒˆã‚’ãŠéĄ˜ã„ã„ãŸã—ãžã™ã€‚
判断ãĢã‚ãŸãŖãĻč€ƒæ…Žã™ãšãčĻŗį‚šã¯ã€
- やり掋したことはãĒいかīŧŸ
- CHANGELOGは過不čļŗãĒいかīŧŸ
- バãƒŧã‚¸ãƒ§ãƒŗãĢå•éĄŒã¯ãĒいかīŧŸ(æœˆčˇ¨ã„ã§ã„ã‚‹ãŽãĢ更新åŋ˜ã‚ŒãĻいるãĒお)
- å†č€ƒã™ãšãä앿§˜ãƒģåŽŸčŖ…ã¯ãĒいかīŧŸ
- ベãƒŧã‚ŋį‰ˆã‚’æ¤œč¨ŧしたã‚ĩãƒŧバãƒŧã‹ã‚‰ä¸å…ˇåˆãŽå ąå‘Šį­‰ã¯ä¸ŠãŒãŖãĻãĒいかīŧŸ
- (ã‚ģキãƒĨãƒĒãƒ†ã‚ŖãŽäŋŽæ­Ŗã‚„重čρãĒバグäŋŽæ­ŖãĒおぎため)ãƒĒãƒĒãƒŧ゚をæ€Ĩã„ã æ–šãŒč‰¯ã„ã‹īŧŸãã†ã§ã¯ãĒいかīŧŸ
- ActionsがčŊãĄãĻいãĒいかīŧŸ
ãĒおが挙げられぞす。
ご協力ありがとうございぞす :sparkles:
`
const issue_number = context.payload.issue ? context.payload.issue.number : (context.payload.pull_request && context.payload.pull_request.number)
if (!issue_number) {
console.log('No issue or PR number found in payload; skipping')
} else {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number,
body,
})
}

13
.github/workflows/reviewer_lottery.yml vendored Normal file
View File

@@ -0,0 +1,13 @@
name: "Reviewer lottery"
on:
pull_request_target:
types: [opened, ready_for_review, reopened]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: uesteibar/reviewer-lottery@v2
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -1,108 +0,0 @@
name: Storybook
on:
push:
branches:
- master
- develop
pull_request_target:
branches-ignore:
# Since pull requests targets master mostly is the "develop" branch.
# Storybook CI is checked on the "push" event of "develop" branch so it would cause a duplicate build.
# This is a waste of chromatic build quota, so we don't run storybook CI on pull requests targets master.
- master
jobs:
build:
# Chromatic is not likely to be available for fork repositories, so we disable for fork repositories.
if: github.repository == 'misskey-dev/misskey'
runs-on: ubuntu-latest
env:
NODE_OPTIONS: "--max_old_space_size=7168"
steps:
- uses: actions/checkout@v6.0.1
if: github.event_name != 'pull_request_target'
with:
fetch-depth: 0
submodules: true
- uses: actions/checkout@v6.0.1
if: github.event_name == 'pull_request_target'
with:
fetch-depth: 0
submodules: true
ref: "refs/pull/${{ github.event.number }}/merge"
- name: Checkout actual HEAD
if: github.event_name == 'pull_request_target'
run: git checkout "$(git rev-list --parents -n1 HEAD | cut -d" " -f3)"
- name: Setup pnpm
uses: pnpm/action-setup@v4.2.0
- name: Use Node.js
uses: actions/setup-node@v6.1.0
with:
node-version-file: '.node-version'
cache: 'pnpm'
- run: pnpm i --frozen-lockfile
- name: Check pnpm-lock.yaml
run: git diff --exit-code pnpm-lock.yaml
- name: Build dependent packages
run: pnpm -F misskey-js -F misskey-bubble-game -F misskey-reversi build
- name: Build storybook
run: pnpm --filter frontend build-storybook
- name: Publish to Chromatic
if: github.event_name != 'pull_request_target' && github.ref == 'refs/heads/master'
run: pnpm --filter frontend chromatic --exit-once-uploaded -d storybook-static
env:
CHROMATIC_PROJECT_TOKEN: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
- name: Publish to Chromatic
if: github.event_name != 'pull_request_target' && github.ref != 'refs/heads/master'
id: chromatic_push
run: |
DIFF="${{ github.event.before }} HEAD"
if [ "$DIFF" = "0000000000000000000000000000000000000000 HEAD" ]; then
DIFF="HEAD"
fi
CHROMATIC_PARAMETER="$(node packages/frontend/.storybook/changes.js $(git diff-tree --no-commit-id --name-only -r $(echo "$DIFF") | xargs))"
if [ "$CHROMATIC_PARAMETER" = " --skip" ]; then
echo "skip=true" >> $GITHUB_OUTPUT
fi
if pnpm --filter frontend chromatic -d storybook-static $(echo "$CHROMATIC_PARAMETER"); then
echo "success=true" >> $GITHUB_OUTPUT
else
echo "success=false" >> $GITHUB_OUTPUT
fi
env:
CHROMATIC_PROJECT_TOKEN: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
- name: Publish to Chromatic
if: github.event_name == 'pull_request_target'
id: chromatic_pull_request
run: |
CHROMATIC_PARAMETER="$(node packages/frontend/.storybook/changes.js $(git diff --name-only origin/${GITHUB_BASE_REF}...origin/${GITHUB_HEAD_REF} | xargs))"
if [ "$CHROMATIC_PARAMETER" = " --skip" ]; then
echo "skip=true" >> $GITHUB_OUTPUT
fi
BRANCH="${{ github.event.pull_request.head.user.login }}:$GITHUB_HEAD_REF"
if [ "$BRANCH" = "misskey-dev:$GITHUB_HEAD_REF" ]; then
BRANCH="$GITHUB_HEAD_REF"
fi
pnpm --filter frontend chromatic --exit-once-uploaded -d storybook-static --branch-name "$BRANCH" $(echo "$CHROMATIC_PARAMETER")
env:
CHROMATIC_PROJECT_TOKEN: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
- name: Notify that Chromatic detects changes
uses: actions/github-script@v8.0.0
if: github.event_name != 'pull_request_target' && steps.chromatic_push.outputs.success == 'false'
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
github.rest.repos.createCommitComment({
owner: context.repo.owner,
repo: context.repo.repo,
commit_sha: context.sha,
body: 'Chromatic detects changes. Please [review the changes on Chromatic](https://www.chromatic.com/builds?appId=6428f7d7b962f0b79f97d6e4).'
})
- name: Upload Artifacts
uses: actions/upload-artifact@v6
with:
name: storybook
path: packages/frontend/storybook-static

View File

@@ -5,98 +5,44 @@ on:
branches:
- master
- develop
paths:
- packages/backend/**
# for permissions
- packages/misskey-js/**
- .github/workflows/test-backend.yml
- .github/misskey/test.yml
pull_request:
paths:
- packages/backend/**
# for permissions
- packages/misskey-js/**
- .github/workflows/test-backend.yml
- .github/misskey/test.yml
workflow_dispatch:
inputs:
force_ffmpeg_cache_update:
description: 'Force update ffmpeg cache'
required: false
default: false
type: boolean
jobs:
unit:
name: Unit tests (backend)
jest:
runs-on: ubuntu-latest
strategy:
matrix:
node-version-file:
- .node-version
- .github/min.node-version
node-version: [18.x]
services:
postgres:
image: postgres:18
image: postgres:13
ports:
- 54312:5432
env:
POSTGRES_DB: test-misskey
POSTGRES_HOST_AUTH_METHOD: trust
redis:
image: redis:7
image: redis:6
ports:
- 56312:6379
meilisearch:
image: getmeili/meilisearch:v1.3.4
ports:
- 57712:7700
env:
MEILI_NO_ANALYTICS: true
MEILI_ENV: development
steps:
- uses: actions/checkout@v6.0.1
- uses: actions/checkout@v3.3.0
with:
submodules: true
- name: Setup pnpm
uses: pnpm/action-setup@v4.2.0
- name: Get current date
id: current-date
run: echo "today=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT
- name: Setup and Restore ffmpeg/ffprobe Cache
id: cache-ffmpeg
uses: actions/cache@v4
- name: Install pnpm
uses: pnpm/action-setup@v2
with:
path: |
/usr/local/bin/ffmpeg
/usr/local/bin/ffprobe
# daily cache
key: ${{ runner.os }}-ffmpeg-${{ steps.current-date.outputs.today }}
restore-keys: |
${{ runner.os }}-ffmpeg-${{ steps.current-date.outputs.today }}
- name: Install FFmpeg
if: steps.cache-ffmpeg.outputs.cache-hit != 'true' || github.event.inputs.force_ffmpeg_cache_update == true
run: |
for i in {1..3}; do
echo "Attempt $i: Installing FFmpeg..."
curl -s -L https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz -o ffmpeg.tar.xz && \
tar -xf ffmpeg.tar.xz && \
mv ffmpeg-*-static/ffmpeg /usr/local/bin/ && \
mv ffmpeg-*-static/ffprobe /usr/local/bin/ && \
rm -rf ffmpeg.tar.xz ffmpeg-*-static/ && \
break || sleep 10
if [ $i -eq 3 ]; then
echo "Failed to install FFmpeg after 3 attempts"
exit 1
fi
done
- name: Use Node.js
uses: actions/setup-node@v6.1.0
version: 7
run_install: false
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3.6.0
with:
node-version-file: ${{ matrix.node-version-file }}
node-version: ${{ matrix.node-version }}
cache: 'pnpm'
- run: corepack enable
- run: pnpm i --frozen-lockfile
- name: Check pnpm-lock.yaml
run: git diff --exit-code pnpm-lock.yaml
@@ -105,102 +51,9 @@ jobs:
- name: Build
run: pnpm build
- name: Test
run: pnpm --filter backend test-and-coverage
- name: Upload to Codecov
uses: codecov/codecov-action@v5
run: pnpm jest-and-coverage
- name: Upload Coverage
uses: codecov/codecov-action@v3
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./packages/backend/coverage/coverage-final.json
e2e:
name: E2E tests (backend)
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
node-version-file:
- .node-version
- .github/min.node-version
services:
postgres:
image: postgres:18
ports:
- 54312:5432
env:
POSTGRES_DB: test-misskey
POSTGRES_HOST_AUTH_METHOD: trust
redis:
image: redis:7
ports:
- 56312:6379
steps:
- uses: actions/checkout@v6.0.1
with:
submodules: true
- name: Setup pnpm
uses: pnpm/action-setup@v4.2.0
- name: Use Node.js
uses: actions/setup-node@v6.1.0
with:
node-version-file: ${{ matrix.node-version-file }}
cache: 'pnpm'
- run: pnpm i --frozen-lockfile
- name: Check pnpm-lock.yaml
run: git diff --exit-code pnpm-lock.yaml
- name: Copy Configure
run: cp .github/misskey/test.yml .config
- name: Build
run: pnpm build
- name: Test
run: pnpm --filter backend test-and-coverage:e2e
- name: Upload to Codecov
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./packages/backend/coverage/coverage-final.json
migration:
name: Migration tests (backend)
runs-on: ubuntu-latest
strategy:
matrix:
node-version-file:
- .node-version
#- .github/min.node-version
services:
postgres:
image: postgres:18
ports:
- 54312:5432
env:
POSTGRES_DB: test-misskey
POSTGRES_HOST_AUTH_METHOD: trust
steps:
- uses: actions/checkout@v6.0.1
with:
submodules: true
- name: Setup pnpm
uses: pnpm/action-setup@v4.2.0
- name: Get current date
id: current-date
run: echo "today=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT
- name: Use Node.js
uses: actions/setup-node@v6.1.0
with:
node-version-file: ${{ matrix.node-version-file }}
cache: 'pnpm'
- run: pnpm i --frozen-lockfile
- name: Check pnpm-lock.yaml
run: git diff --exit-code pnpm-lock.yaml
- name: Copy Configure
run: cp .github/misskey/test.yml .config
- name: Build
run: pnpm build
- name: Run migrations
run: MISSKEY_CONFIG_YML=test.yml pnpm --filter backend migrate
- name: Check no migrations are remaining
run: MISSKEY_CONFIG_YML=test.yml pnpm --filter backend check-migrations

View File

@@ -1,111 +0,0 @@
name: Test (federation)
on:
push:
branches:
- master
- develop
paths:
- packages/backend/**
- packages/misskey-js/**
- .github/workflows/test-federation.yml
pull_request:
paths:
- packages/backend/**
- packages/misskey-js/**
- .github/workflows/test-federation.yml
workflow_dispatch:
inputs:
force_ffmpeg_cache_update:
description: 'Force update ffmpeg cache'
required: false
default: false
type: boolean
jobs:
test:
name: Federation test
runs-on: ubuntu-latest
strategy:
matrix:
node-version-file:
- .node-version
- .github/min.node-version
steps:
- uses: actions/checkout@v6
with:
submodules: true
- name: Setup pnpm
uses: pnpm/action-setup@v4.2.0
- name: Get current date
id: current-date
run: echo "today=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT
- name: Setup and Restore ffmpeg/ffprobe Cache
id: cache-ffmpeg
uses: actions/cache@v4
with:
path: |
/usr/local/bin/ffmpeg
/usr/local/bin/ffprobe
# daily cache
key: ${{ runner.os }}-ffmpeg-${{ steps.current-date.outputs.today }}
restore-keys: |
${{ runner.os }}-ffmpeg-${{ steps.current-date.outputs.today }}
- name: Install FFmpeg
if: steps.cache-ffmpeg.outputs.cache-hit != 'true' || github.event.inputs.force_ffmpeg_cache_update == true
run: |
for i in {1..3}; do
echo "Attempt $i: Installing FFmpeg..."
curl -s -L https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz -o ffmpeg.tar.xz && \
tar -xf ffmpeg.tar.xz && \
mv ffmpeg-*-static/ffmpeg /usr/local/bin/ && \
mv ffmpeg-*-static/ffprobe /usr/local/bin/ && \
rm -rf ffmpeg.tar.xz ffmpeg-*-static/ && \
break || sleep 10
if [ $i -eq 3 ]; then
echo "Failed to install FFmpeg after 3 attempts"
exit 1
fi
done
- name: Use Node.js
uses: actions/setup-node@v6.1.0
with:
node-version-file: ${{ matrix.node-version-file }}
cache: 'pnpm'
- name: Build Misskey
run: |
pnpm i --frozen-lockfile
pnpm build
- name: Setup
run: |
echo "NODE_VERSION=$(cat ${{ matrix.node-version-file }})" >> $GITHUB_ENV
cd packages/backend/test-federation
bash ./setup.sh
sudo chmod 644 ./certificates/*.test.key
- name: Start servers
id: start_servers
continue-on-error: true
# https://github.com/docker/compose/issues/1294#issuecomment-374847206
run: |
cd packages/backend/test-federation
docker compose up -d --scale tester=0
- name: Print start_servers error
if: ${{ steps.start_servers.outcome == 'failure' }}
run: |
cd packages/backend/test-federation
docker compose logs | tail -n 300
exit 1
- name: Test
run: |
cd packages/backend/test-federation
docker compose run --no-deps tester
- name: Log
if: always()
run: |
cd packages/backend/test-federation
docker compose logs
- name: Stop servers
if: always()
run: |
cd packages/backend/test-federation
docker compose down

View File

@@ -5,39 +5,31 @@ on:
branches:
- master
- develop
paths:
- packages/frontend/**
# for permissions
- packages/misskey-js/**
# for e2e
- packages/backend/**
- .github/workflows/test-frontend.yml
- .github/misskey/test.yml
pull_request:
paths:
- packages/frontend/**
# for permissions
- packages/misskey-js/**
# for e2e
- packages/backend/**
- .github/workflows/test-frontend.yml
- .github/misskey/test.yml
jobs:
vitest:
name: Unit tests (frontend)
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18.x]
steps:
- uses: actions/checkout@v6.0.1
- uses: actions/checkout@v3.3.0
with:
submodules: true
- name: Setup pnpm
uses: pnpm/action-setup@v4.2.0
- name: Use Node.js
uses: actions/setup-node@v6.1.0
- name: Install pnpm
uses: pnpm/action-setup@v2
with:
node-version-file: '.node-version'
version: 7
run_install: false
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3.6.0
with:
node-version: ${{ matrix.node-version }}
cache: 'pnpm'
- run: corepack enable
- run: pnpm i --frozen-lockfile
- name: Check pnpm-lock.yaml
run: git diff --exit-code pnpm-lock.yaml
@@ -48,35 +40,35 @@ jobs:
- name: Test
run: pnpm --filter frontend test-and-coverage
- name: Upload Coverage
uses: codecov/codecov-action@v5
uses: codecov/codecov-action@v3
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./packages/frontend/coverage/coverage-final.json
e2e:
name: E2E tests (frontend)
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
node-version: [18.x]
browser: [chrome]
services:
postgres:
image: postgres:18
image: postgres:13
ports:
- 54312:5432
env:
POSTGRES_DB: test-misskey
POSTGRES_HOST_AUTH_METHOD: trust
redis:
image: redis:7
image: redis:6
ports:
- 56312:6379
steps:
- uses: actions/checkout@v6.0.1
- uses: actions/checkout@v3.3.0
with:
submodules: true
# https://github.com/cypress-io/cypress-docker-images/issues/150
@@ -85,13 +77,17 @@ jobs:
# if: ${{ matrix.browser == 'firefox' }}
#- uses: browser-actions/setup-firefox@latest
# if: ${{ matrix.browser == 'firefox' }}
- name: Setup pnpm
uses: pnpm/action-setup@v4.2.0
- name: Use Node.js
uses: actions/setup-node@v6.1.0
- name: Install pnpm
uses: pnpm/action-setup@v2
with:
node-version-file: '.node-version'
version: 7
run_install: false
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3.6.0
with:
node-version: ${{ matrix.node-version }}
cache: 'pnpm'
- run: corepack enable
- run: pnpm i --frozen-lockfile
- name: Copy Configure
run: cp .github/misskey/test.yml .config
@@ -105,20 +101,19 @@ jobs:
- name: Cypress install
run: pnpm exec cypress install
- name: Cypress run
uses: cypress-io/github-action@v6
timeout-minutes: 15
uses: cypress-io/github-action@v5
with:
install: false
start: pnpm start:test
wait-on: 'http://localhost:61812'
headed: true
headless: false
browser: ${{ matrix.browser }}
- uses: actions/upload-artifact@v6
- uses: actions/upload-artifact@v2
if: failure()
with:
name: ${{ matrix.browser }}-cypress-screenshots
path: cypress/screenshots
- uses: actions/upload-artifact@v6
- uses: actions/upload-artifact@v2
if: always()
with:
name: ${{ matrix.browser }}-cypress-videos

View File

@@ -6,31 +6,29 @@ name: Test (misskey.js)
on:
push:
branches: [ develop ]
paths:
- packages/misskey-js/**
- .github/workflows/test-misskey-js.yml
pull_request:
branches: [ develop ]
paths:
- packages/misskey-js/**
- .github/workflows/test-misskey-js.yml
jobs:
test:
name: Unit tests (misskey.js)
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18.x]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
steps:
- name: Checkout
uses: actions/checkout@v6.0.1
uses: actions/checkout@v3.3.0
- name: Setup pnpm
uses: pnpm/action-setup@v4.2.0
- run: corepack enable
- name: Setup Node.js
uses: actions/setup-node@v6.1.0
- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3.6.0
with:
node-version-file: '.node-version'
node-version: ${{ matrix.node-version }}
cache: 'pnpm'
- name: Install dependencies
@@ -48,7 +46,7 @@ jobs:
CI: true
- name: Upload Coverage
uses: codecov/codecov-action@v5
uses: codecov/codecov-action@v3
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./packages/misskey-js/coverage/coverage-final.json

View File

@@ -1,35 +0,0 @@
name: Test (production install and build)
on:
push:
branches:
- master
- develop
pull_request:
env:
NODE_ENV: production
jobs:
production:
name: Production build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6.0.1
with:
submodules: true
- name: Setup pnpm
uses: pnpm/action-setup@v4.2.0
- name: Use Node.js
uses: actions/setup-node@v6.1.0
with:
node-version-file: '.node-version'
cache: 'pnpm'
- run: pnpm i --frozen-lockfile
- name: Check pnpm-lock.yaml
run: git diff --exit-code pnpm-lock.yaml
- name: Copy Configure
run: cp .github/misskey/test.yml .config/default.yml
- name: Build
run: pnpm build

View File

@@ -1,40 +0,0 @@
name: api.json validation
on:
push:
branches:
- master
- develop
paths:
- packages/backend/**
- .github/workflows/validate-api-json.yml
pull_request:
paths:
- packages/backend/**
- .github/workflows/validate-api-json.yml
jobs:
validate-api-json:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6.0.1
with:
submodules: true
- name: Setup pnpm
uses: pnpm/action-setup@v4.2.0
- name: Use Node.js
uses: actions/setup-node@v6.1.0
with:
node-version-file: '.node-version'
cache: 'pnpm'
- name: Install Redocly CLI
run: npm i -g @redocly/cli
- run: pnpm i --frozen-lockfile
- name: Check pnpm-lock.yaml
run: git diff --exit-code pnpm-lock.yaml
- name: Copy Configure
run: cp .config/example.yml .config/default.yml
- name: Build and generate
run: pnpm build && pnpm --filter backend generate-api-json
- name: Validation
run: npx @redocly/cli lint --extends=minimal ./packages/backend/built/api.json

22
.gitignore vendored
View File

@@ -35,22 +35,16 @@ coverage
!/.config/example.yml
!/.config/docker_example.yml
!/.config/docker_example.env
!/.config/cypress-devcontainer.yml
docker-compose.yml
./compose.yml
.devcontainer/compose.yml
!/.devcontainer/compose.yml
!/.devcontainer/docker-compose.yml
# misskey
/build
built
built-test
js-built
src-js
/data
/.cache-loader
/db
/meili_data
/elasticsearch
npm-debug.log
*.pem
run.bat
@@ -62,15 +56,6 @@ api-docs.json
/files
ormconfig.json
temp
/packages/frontend/src/**/*.stories.ts
tsdoc-metadata.json
misskey-assets
# Vite temporary files
vite.config.js.timestamp-*
vite.config.ts.timestamp-*
vite.config.local-dev.js.timestamp-*
vite.config.local-dev.ts.timestamp-*
# blender backups
*.blend1
@@ -78,6 +63,3 @@ vite.config.local-dev.ts.timestamp-*
*.blend3
*.blend4
*.blend5
# VSCode addon
.favorites.json

3
.gitmodules vendored
View File

@@ -1,3 +1,6 @@
[submodule "misskey-assets"]
path = misskey-assets
url = https://github.com/misskey-dev/assets.git
[submodule "fluent-emojis"]
path = fluent-emojis
url = https://github.com/misskey-dev/emojis.git

View File

@@ -1 +1 @@
22.15.0
v18.13.0

3
.npmrc
View File

@@ -1,3 +0,0 @@
engine-strict = true
save-exact = true
shell-emulator = true

View File

@@ -3,7 +3,9 @@
"editorconfig.editorconfig",
"dbaeumer.vscode-eslint",
"Vue.volar",
"Vue.vscode-typescript-vue-plugin",
"Orta.vscode-jest",
"dbaeumer.vscode-eslint",
"mrmlnc.vscode-json5"
]
}

28
.vscode/settings.json vendored
View File

@@ -1,20 +1,10 @@
{
"search.exclude": {
"**/node_modules": true
},
"typescript.tsdk": "node_modules/typescript/lib",
"typescript.enablePromptUseWorkspaceTsdk": true,
"files.associations": {
"*.test.ts": "typescript"
},
"jest.runMode": "on-demand",
"jest.virtualFolders": [
{ "name": "backend unit", "jestCommandLine": "pnpm -F backend run test" },
{ "name": "backend e2e", "jestCommandLine": "pnpm -F backend run test:e2e"},
{ "name": "misskey-js", "jestCommandLine": "pnpm -F misskey-js run jest" }
],
"editor.codeActionsOnSave": {
"source.fixAll": "explicit"
},
"editor.formatOnSave": false
}
"search.exclude": {
"**/node_modules": true
},
"typescript.tsdk": "node_modules/typescript/lib",
"files.associations": {
"*.test.ts": "typescript"
},
"jest.autoRun": "off"
}

File diff suppressed because it is too large Load Diff

View File

@@ -2,131 +2,45 @@
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, caste, color, religion, or sexual
identity and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
Examples of behavior that contributes to creating a positive environment include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the overall
community
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior include:
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery, and sexual attention or advances of
any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email address,
without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
## Enforcement Responsibilities
## Our Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
<syuilotan@yahoo.co.jp>.
All complaints will be reviewed and investigated promptly and fairly.
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at syuilotan@yahoo.co.jp. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series of
actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or permanent
ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within the
community.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.1, available at
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
Community Impact Guidelines were inspired by
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
For answers to common questions about this code of conduct, see the FAQ at
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
[https://www.contributor-covenant.org/translations][translations].
[homepage]: https://www.contributor-covenant.org
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
[Mozilla CoC]: https://github.com/mozilla/diversity
[FAQ]: https://www.contributor-covenant.org/faq
[translations]: https://www.contributor-covenant.org/translations
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

View File

@@ -1,7 +1,7 @@
# Contribution guide
We're glad you're interested in contributing Misskey! In this document you will find the information you need to contribute to the project.
> [!NOTE]
> **Note**
> This project uses Japanese as its major language, **but you do not need to translate and write the Issues/PRs in Japanese.**
> Also, you might receive comments on your Issue/PR in Japanese, but you do not need to reply to them in Japanese as well.\
> The accuracy of machine translation into Japanese is not high, so it will be easier for us to understand if you write it in the original language.
@@ -15,33 +15,18 @@ Before creating an issue, please check the following:
- To avoid duplication, please search for similar issues before creating a new issue.
- Do not use Issues to ask questions or troubleshooting.
- Issues should only be used to feature requests, suggestions, and bug tracking.
- Please ask questions or troubleshooting in [GitHub Discussions](https://github.com/misskey-dev/misskey/discussions) or [Discord](https://discord.gg/Wp8gVStHW3).
- Please ask questions or troubleshooting in ~~the [Misskey Forum](https://forum.misskey.io/)~~ [GitHub Discussions](https://github.com/misskey-dev/misskey/discussions) or [Discord](https://discord.gg/Wp8gVStHW3).
> [!WARNING]
> **Warning**
> Do not close issues that are about to be resolved. It should remain open until a commit that actually resolves it is merged.
### Recommended discussing before implementation
We welcome your proposal.
## Before implementation
When you want to add a feature or fix a bug, **first have the design and policy reviewed in an Issue** (if it is not there, please make one). Without this step, there is a high possibility that the PR will not be merged even if it is implemented.
At this point, you also need to clarify the goals of the PR you will create, and make sure that the other members of the team are aware of them.
PRs that do not have a clear set of do's and don'ts tend to be bloated and difficult to review.
Also, when you start implementation, assign yourself to the Issue (if you cannot do it yourself, ask Committer to assign you).
By expressing your intention to work on the Issue, you can prevent conflicts in the work.
To the Committers: you should not assign someone on it before the Final Decision.
### How issues are triaged
The Committers may:
* close an issue that is not reproducible on latest stable release,
* merge an issue into another issue,
* split an issue into multiple issues,
* or re-open that has been closed for some reason which is not applicable anymore.
@syuilo reserves the Final Decision rights including whether the project will implement feature and how to implement, these rights are not always exercised.
Also, when you start implementation, assign yourself to the Issue (if you cannot do it yourself, ask another member to assign you). By expressing your intention to work the Issue, you can prevent conflicts in the work.
## Well-known branches
- **`master`** branch is tracking the latest release and used for production purposes.
@@ -52,45 +37,25 @@ The Committers may:
## Creating a PR
Thank you for your PR! Before creating a PR, please check the following:
- If possible, prefix the title with a keyword that identifies the type of this PR, as shown below.
- `fix` / `refactor` / `feat` / `enhance` / `perf` / `chore` etc
- Also, make sure that the granularity of this PR is appropriate. Please do not include more than one type of change or interest in a single PR.
- `fix` / `refactor` / `feat` / `enhance` / `perf` / `chore` etc
- Also, make sure that the granularity of this PR is appropriate. Please do not include more than one type of change or interest in a single PR.
- If there is an Issue which will be resolved by this PR, please include a reference to the Issue in the text.
- Please add the summary of the changes to [`CHANGELOG.md`](/CHANGELOG.md). However, this is not necessary for changes that do not affect the users, such as refactoring.
- Check if there are any documents that need to be created or updated due to this change.
- If you have added a feature or fixed a bug, please add a test case if possible.
- Please make sure that tests and Lint are passed in advance.
- You can run it with `pnpm test` and `pnpm lint`. [See more info](#testing)
- You can run it with `pnpm test` and `pnpm lint`. [See more info](#testing)
- If this PR includes UI changes, please attach a screenshot in the text.
Thanks for your cooperation 🤗
### Additional things for ActivityPub payload changes
*This section is specific to misskey-dev implementation. Other fork or implementation may take different way. A significant difference is that non-"misskey-dev" extension is not described in the misskey-hub's document.*
If PR includes changes to ActivityPub payload, please reflect it in [misskey-hub's document](https://github.com/misskey-dev/misskey-hub-next/blob/master/content/ns.md) by sending PR.
The name of purporsed extension property (referred as "extended property" in later) to ActivityPub shall be prefixed by `_misskey_`. (i.e. `_misskey_quote`)
The extended property in `packages/backend/src/core/activitypub/type.ts` **must** be declared as optional because ActivityPub payloads that comes from older Misskey or other implementation may not contain it.
The extended property must be included in the context definition. Context is defined in `packages/backend/src/core/activitypub/misc/contexts.ts`.
The key shall be same as the name of extended property, and the value shall be same as "short IRI".
"Short IRI" is defined in misskey-hub's document, but usually takes form of `misskey:<name of extended property>`. (i.e. `misskey:_misskey_quote`)
One should not add property that has defined before by other implementation, or add custom variant value to "well-known" property.
## Reviewers guide
Be willing to comment on the good points and not just the things you want fixed đŸ’¯
čĒ­ã‚“ã§ãŠãã¨ã„ã„ã‚„ã¤
- https://blog.lacolaco.net/posts/1e2cf439b3c2/
- https://konifar-zatsu.hatenadiary.jp/entry/2024/11/05/192421
### Review perspective
- Scope
- Are the goals of the PR clear?
- Is the granularity of the PR appropriate?
- Are the goals of the PR clear?
- Is the granularity of the PR appropriate?
- Security
- Does merging this PR create a vulnerability?
- Performance
@@ -101,22 +66,6 @@ Be willing to comment on the good points and not just the things you want fixed
- Are there any omissions or gaps?
- Does it check for anomalies?
## Security Advisory
### For reporter
Thank you for your reporting!
If you can also create a patch to fix the vulnerability, please create a PR on the private fork.
> [!note]
> There is a GitHub bug that prevents merging if a PR not following the develop branch of upstream, so please keep follow the develop branch.
### For misskey-dev member
äŋŽæ­ŖPRがdevelopãĢčŋŊ垓されãĻいãĒいとマãƒŧジできãĒいぎで、マãƒŧジできãĒã‹ãŖãŸã‚‰
> Could you merge or rebase onto upstream develop branch?
ãĒおとäŧãˆã‚‹ã€‚
## Deploy
The `/deploy` command by issue comment can be used to deploy the contents of a PR to the preview environment.
```
@@ -128,7 +77,7 @@ An actual domain will be assigned so you can test the federation.
## Release
### Release Instructions
1. Commit version changes in the `develop` branch ([package.json](package.json))
1. Commit version changes in the `develop` branch ([package.json](https://github.com/misskey-dev/misskey/blob/develop/package.json))
2. Create a release PR.
- Into `master` from `develop` branch.
- The title must be in the format `Release: x.y.z`.
@@ -139,7 +88,7 @@ An actual domain will be assigned so you can test the federation.
- The target branch must be `master`
- The tag name must be the version
> [!NOTE]
> **Note**
> Why this instruction is necessary:
> - To perform final QA checks
> - To distribute responsibility
@@ -152,30 +101,26 @@ You can improve our translations with your Crowdin account.
Your changes in Crowdin are automatically submitted as a PR (with the title "New Crowdin translations") to the repository.
The owner [@syuilo](https://github.com/syuilo) merges the PR into the develop branch before the next release.
If your language is not listed in Crowdin, please open an issue. We will add it to Crowdin.
For newly added languages, once the translation progress per language exceeds 70%, it will be officially introduced into Misskey and made available to users.
If your language is not listed in Crowdin, please open an issue.
![Crowdin](https://d322cqt584bo4o.cloudfront.net/misskey/localized.svg)
## Development
### Setup
Before developing, you have to set up environment. Misskey requires Redis, PostgreSQL, and FFmpeg.
During development, it is useful to use the
You would want to install Meilisearch to experiment related features. Technically, meilisearch is not strict requirement, but some features and tests require it.
```
pnpm dev
```
There are a few ways to proceed.
command.
#### Use system-wide software
You could install them in system-wide (such as from package manager).
#### Use `docker compose`
You could obtain middleware container by typing `docker compose -f $PROJECT_ROOT/compose.local-db.yml up -d`.
#### Use Devcontainer
Devcontainer also has necessary setting. This method can be done by connecting from VSCode.
- Server-side source files and automatically builds them if they are modified. Automatically start the server process(es).
- Vite HMR (just the `vite` command) is available. The behavior may be different from production.
- Service Worker is watched by esbuild.
### Dev Container
Instead of running `pnpm` locally, you can use Dev Container to set up your development environment.
To use Dev Container, open the project directory on VSCode with Dev Containers installed.
To use Dev Container, open the project directory on VSCode with Dev Containers installed.
**Note:** If you are using Windows, please clone the repository with WSL. Using Git for Windows will result in broken files due to the difference in how newlines are handled.
It will run the following command automatically inside the container.
@@ -187,66 +132,38 @@ pnpm build
pnpm migrate
```
After finishing the migration, you can proceed.
After finishing the migration, run the `pnpm dev` command to start the development server.
### Start developing
During development, it is useful to use the
```
``` bash
pnpm dev
```
command.
- Server-side source files and automatically builds them if they are modified. Automatically start the server process(es).
- Service Worker is watched by esbuild.
- Vite HMR (just the `vite` command) is available. The behavior may be different from production.
- Vite runs behind the backend (the backend will proxy Vite at /vite and /embed_vite except for websocket used for HMR).
- You can see Misskey by accessing `http://localhost:3000` (Replace `3000` with the port configured with `port` in .config/default.yml).
## Testing
You can run non-backend tests by executing following commands:
```sh
pnpm --filter frontend test
pnpm --filter misskey-js test
- Test codes are located in [`/packages/backend/test`](/packages/backend/test).
### Run test
Create a config file.
```
Backend tests require manual preparation of servers. See the next section for more on this.
### Backend
There are three types of test codes for the backend:
- Unit tests: [`/packages/backend/test/unit`](/packages/backend/test/unit)
- Single-server E2E tests: [`/packages/backend/test/e2e`](/packages/backend/test/e2e)
- Multiple-server E2E tests: [`/packages/backend/test-federation`](/packages/backend/test-federation)
#### Running Unit Tests or Single-server E2E Tests
1. Create a config file:
```sh
cp .github/misskey/test.yml .config/
```
2. Start DB and Redis servers for testing:
```sh
docker compose -f packages/backend/test/compose.yml up
Prepare DB/Redis for testing.
```
Instead, you can prepare an empty (data can be erased) DB and edit `.config/test.yml` appropriately.
3. Run all tests:
```sh
pnpm --filter backend test # unit tests
pnpm --filter backend test:e2e # single-server E2E tests
docker compose -f packages/backend/test/docker-compose.yml up
```
If you want to run a specific test, run as a following command:
```sh
pnpm --filter backend test -- packages/backend/test/unit/activitypub.ts
pnpm --filter backend test:e2e -- packages/backend/test/e2e/nodeinfo.ts
Alternatively, prepare an empty (data can be erased) DB and edit `.config/test.yml`.
Run all test.
```
pnpm test
```
#### Running Multiple-server E2E Tests
See [`/packages/backend/test-federation/README.md`](/packages/backend/test-federation/README.md).
#### Run specify test
```
pnpm jest -- foo.ts
```
## Environment Variable
- `MISSKEY_CONFIG_YML`: Specify the file path of config.yml instead of default.yml (e.g. `2nd.yml`).
- `MISSKEY_WEBFINGER_USE_HTTP`: If it's set true, WebFinger requests will be http instead of https, useful for testing federation between servers in localhost. NEVER USE IN PRODUCTION.
### e2e tests
TODO
## Continuous integration
Misskey uses GitHub Actions for executing automated tests.
@@ -258,12 +175,6 @@ Misskey uses Vue(v3) as its front-end framework.
- **When creating a new component, please use the Composition API (with [setup sugar](https://v3.vuejs.org/api/sfc-script-setup.html) and [ref sugar](https://github.com/vuejs/rfcs/discussions/369)) instead of the Options API.**
- Some of the existing components are implemented in the Options API, but it is an old implementation. Refactors that migrate those components to the Composition API are also welcome.
## Tabler Icons
ã‚ĸã‚¤ã‚ŗãƒŗã¯ã€Production Build時ãĢäŊŋį”¨ã•ã‚ŒãĻいãĒいもぎが削除されるようãĢãĒãŖãĻいぞす。
**ã‚ĸã‚¤ã‚ŗãƒŗã‚’å‹•įš„ãĢč¨­åŽšã™ã‚‹éš›ãĢは、 `ti-${someVal}` ぎようãĒ、ã‚ĸã‚¤ã‚ŗãƒŗåãŽãŋã‚’å‹•įš„ãĢå¤‰åŒ–ã•ã›ã‚‹åŽŸčŖ…ã‚’čĄŒã‚ãĒいでください。**
åŋ…ず `ti-xxx` ぎようãĒ厌全ãĒクナ゚名をåĢめるようãĢしãĻください。
## nirax
niraxは、MisskeyでäŊŋį”¨ã—ãĻいるã‚ĒãƒĒジナãƒĢãŽãƒ•ãƒ­ãƒŗãƒˆã‚¨ãƒŗãƒ‰ãƒĢãƒŧãƒ†ã‚Ŗãƒŗã‚°ã‚ˇã‚šãƒ†ãƒ ã§ã™ã€‚
**vue-routerからåŊąéŸŋを多大ãĢ受けãĻいるぎで、ぞずはvue-routerãĢついãĻå­Ļãļことをお勧めしぞす。**
@@ -271,7 +182,7 @@ niraxは、MisskeyでäŊŋį”¨ã—ãĻいるã‚ĒãƒĒジナãƒĢãŽãƒ•ãƒ­ãƒŗãƒˆã‚¨ãƒŗãƒ‰
### ãƒĢãƒŧãƒˆåŽšįžŠ
ãƒĢãƒŧãƒˆåŽšįžŠã¯ã€äģĨ下ぎåŊĸåŧãŽã‚Ēブジェクトぎ配列です。
```ts
``` ts
{
name?: string;
path: string;
@@ -279,11 +190,12 @@ niraxは、MisskeyでäŊŋį”¨ã—ãĻいるã‚ĒãƒĒジナãƒĢãŽãƒ•ãƒ­ãƒŗãƒˆã‚¨ãƒŗãƒ‰
query?: Record<string, string>;
loginRequired?: boolean;
hash?: string;
globalCacheKey?: string;
children?: RouteDef[];
}
```
> [!WARNING]
> **Warning**
> įžįŠļ、ãƒĢãƒŧãƒˆã¯åŽšįžŠã•ã‚ŒãŸé †ãĢčŠ•äžĄã•ã‚Œãžã™ã€‚
> たとえば、`/foo/:id`ãƒĢãƒŧãƒˆåŽšįžŠãŽæŦĄãĢ`/foo/bar`ãƒĢãƒŧãƒˆåŽšįžŠãŒã•ã‚ŒãĻã„ãŸå ´åˆã€åžŒč€…ãŒãƒžãƒƒãƒã™ã‚‹ã“ã¨ã¯ã‚ã‚Šãžã›ã‚“ã€‚
@@ -291,201 +203,7 @@ niraxは、MisskeyでäŊŋį”¨ã—ãĻいるã‚ĒãƒĒジナãƒĢãŽãƒ•ãƒ­ãƒŗãƒˆã‚¨ãƒŗãƒ‰
vue-routerとぎ最大ぎ違いは、niraxã¯č¤‡æ•°ãŽãƒĢãƒŧã‚ŋãƒŧãŒå­˜åœ¨ã™ã‚‹ã“ã¨ã‚’č¨ąå¯ã—ãĻã„ã‚‹į‚šã§ã™ã€‚
これãĢより、ã‚ĸプãƒĒ内ã‚Ļã‚Ŗãƒŗãƒ‰ã‚Ļでブナã‚Ļã‚ļとは個åˆĨãĢãƒĢãƒŧãƒ†ã‚Ŗãƒŗã‚°ã™ã‚‹ã“ã¨ãĒおが可čƒŊãĢãĒりぞす。
## Storybook
Misskey uses [Storybook](https://storybook.js.org/) for UI development.
### Setup & Run
#### Setup
```bash
pnpm --filter misskey-js build
```
#### Run
```bash
pnpm --filter frontend storybook-dev
```
### Usage
When you create a new component (in this example, `MyComponent.vue`), the story file (`MyComponent.stories.ts`) will be automatically generated by the `.storybook/generate.js` script.
You can override the default story by creating a impl story file (`MyComponent.stories.impl.ts`).
```ts
/* eslint-disable @typescript-eslint/explicit-function-return-type */
import { StoryObj } from '@storybook/vue3';
import MyComponent from './MyComponent.vue';
export const Default = {
render(args) {
return {
components: {
MyComponent,
},
setup() {
return {
args,
};
},
computed: {
props() {
return {
...this.args,
};
},
},
template: '<MyComponent v-bind="props" />',
};
},
args: {
foo: 'bar',
},
parameters: {
layout: 'centered',
},
} satisfies StoryObj<typeof MyComponent>;
```
If you want to opt-out from the automatic generation, create a `MyComponent.stories.impl.ts` file and add the following line to the file.
```ts
import MyComponent from './MyComponent.vue';
void MyComponent;
```
You can override the component meta by creating a meta story file (`MyComponent.stories.meta.ts`).
```ts
export const argTypes = {
scale: {
control: {
type: 'range',
min: 1,
max: 4,
},
},
};
```
Also, you can use msw to mock API requests in the storybook. Creating a `MyComponent.stories.msw.ts` file to define the mock handlers.
```ts
import { HttpResponse, http } from 'msw';
export const handlers = [
http.post('/api/notes/timeline', ({ request }) => {
return HttpResponse.json([]);
}),
];
```
Don't forget to re-run the `.storybook/generate.js` script after adding, editing, or removing the above files.
## Nest
### Nest Service Circular dependency / Nestでã‚ĩãƒŧビ゚ぎåžĒį’°å‚į…§ã§ã‚¨ãƒŠãƒŧがčĩˇããŸå ´åˆ
#### forwardRef
ãžãšã¯į°Ąå˜ãĢ`forwardRef`をčŠĻしãĻãŋる
```typescript
export class FooService {
constructor(
@Inject(forwardRef(() => BarService))
private barService: BarService
) {
}
}
```
#### OnModuleInit
できãĒければ`OnModuleInit`をäŊŋう
```typescript
import { Injectable, OnModuleInit } from '@nestjs/common';
import { ModuleRef } from '@nestjs/core';
import { BarService } from '@/core/BarService';
@Injectable()
export class FooService implements OnModuleInit {
private barService: BarService // constructorからį§ģ動しãĻくる
constructor(
private moduleRef: ModuleRef,
) {
}
async onModuleInit() {
this.barService = this.moduleRef.get(BarService.name);
}
public async niceMethod() {
return await this.barService.incredibleMethod({ hoge: 'fuga' });
}
}
```
##### Service Unit Test
テ゚トで`onModuleInit`をå‘ŧãŗå‡ēすåŋ…čĻãŒã‚ã‚‹
```typescript
// import ...
describe('test', () => {
let app: TestingModule;
let fooService: FooService; // for test case
let barService: BarService; // for test case
beforeEach(async () => {
app = await Test.createTestingModule({
imports: ...,
providers: [
FooService,
{ // mockする (mockはåŋ…須ではãĒいかもしれãĒい)
provide: BarService,
useFactory: () => ({
incredibleMethod: jest.fn(),
}),
},
{ // ProvideãĢする
provide: BarService.name,
useExisting: BarService,
},
],
})
.useMocker(...
.compile();
fooService = app.get<FooService>(FooService);
barService = app.get<BarService>(BarService) as jest.Mocked<BarService>;
// onModuleInitã‚’åŽŸčĄŒã™ã‚‹
await fooService.onModuleInit();
});
test('nice', () => {
await fooService.niceMethod();
expect(barService.incredibleMethod).toHaveBeenCalled();
expect(barService.incredibleMethod.mock.lastCall![0])
.toEqual({ hoge: 'fuga' });
});
})
```
## Notes
### MisskeyãŽãƒ‰ãƒĄã‚¤ãƒŗå›ē有ぎæĻ‚åŋĩは`Mi`をprefixする
䞋えばGoogleがč‡Ēį¤žã‚ĩãƒŧビ゚をMap、Earth、DriveではãĒくGoogle Map、Google Earth、Google DriveぎようãĢå‘Ŋ名するぎと同じ
ã‚ŗãƒŧド上でMisskeyãŽãƒ‰ãƒĄã‚¤ãƒŗå›ē有ぎæĻ‚åŋĩãĢは`Mi`をprefixすることで、äģ–ãŽãƒ‰ãƒĄã‚¤ãƒŗãŽåŒæ§˜ãŽæĻ‚åŋĩとåŒēåˆĨできるãģã‹ã€åå‰ãŽčĄįĒã‚’é˜˛ãã€‚
ãŸã ã—ã€æ–‡č„ˆä¸ŠMisskeyãŽã‚‚ãŽã‚’æŒ‡ã™ã“ã¨ãŒæ˜Žã‚‰ã‹ã§ã‚ã‚Šã€åå‰ãŽčĄįĒãŽæã‚ŒãŒãĒã„å ´åˆã¯ã€ä¸€æ™‚įš„ãĒロãƒŧã‚ĢãƒĢ変数ãĢé™ãŖãĻ`Mi`ã‚’įœį•ĨしãĻもよい。
### Misskey.jsãŽåž‹į”Ÿæˆ
```bash
pnpm build-misskey-js-with-types
```
### How to resolve conflictions occurred at pnpm-lock.yaml?
Just execute `pnpm` to fix it.
@@ -581,6 +299,27 @@ pnpm dlx typeorm migration:generate -d ormconfig.js -o <migration name>
- į”ŸæˆåžŒã€ãƒ•ã‚Ąã‚¤ãƒĢをmigration下ãĢį§ģしãĻください
- äŊœæˆã•れた゚クãƒĒプトは不åŋ…čρãĒ変更をåĢむため除åŽģしãĻください
### JSON SchemaぎobjectでanyOfをäŊŋうとき
JSON Schemaで、objectãĢ寞しãĻanyOfをäŊŋう場合、anyOfぎ中でpropertiesã‚’åŽšįžŠã—ãĒいこと。
バãƒĒデãƒŧã‚ˇãƒ§ãƒŗãŒåŠšã‹ãĒいため。īŧˆSchemaTypeもそぎようãĢäŊœã‚‰ã‚ŒãĻおり、objectぎanyOf内ぎpropertiesは捨ãĻられぞすīŧ‰
https://github.com/misskey-dev/misskey/pull/10082
テキ゚トhogeãŠã‚ˆãŗfugaãĢついãĻã€į‰‡æ–šã‚’åŋ…é ˆã¨ã—ã¤ã¤ä¸Ąæ–šãŽæŒ‡åŽšã‚‚ã‚ã‚Šã†ã‚‹å ´åˆ:
```
export const paramDef = {
type: 'object',
properties: {
hoge: { type: 'string', minLength: 1 },
fuga: { type: 'string', minLength: 1 },
},
anyOf: [
{ required: ['hoge'] },
{ required: ['fuga'] },
],
} as const;
```
### ã‚ŗãƒã‚¯ã‚ˇãƒ§ãƒŗãĢは`markRaw`せよ
**VueãŽã‚ŗãƒŗãƒãƒŧãƒãƒŗãƒˆãŽdataã‚Ēãƒ—ã‚ˇãƒ§ãƒŗã¨ã—ãĻ**misskey.jsãŽã‚ŗãƒã‚¯ã‚ˇãƒ§ãƒŗã‚’č¨­åŽšã™ã‚‹ã¨ãã€åŋ…ず`markRaw`でナップしãĻãã ã•ã„ã€‚ã‚¤ãƒŗã‚šã‚ŋãƒŗã‚šãŒä¸åŋ…čρãĢãƒĒã‚ĸã‚¯ãƒ†ã‚Ŗãƒ–åŒ–ã•ã‚Œã‚‹ã“ã¨ã§ã€misskey.js内ぎå‡Ļį†ã§ä¸å…ˇåˆãŒį™ēį”Ÿã™ã‚‹ã¨ã¨ã‚‚ãĢ、パフりãƒŧãƒžãƒŗã‚šä¸ŠãŽå•éĄŒãĢã‚‚įš‹ãŒã‚‹ã€‚ãĒお、Composition APIをäŊŋう場合はこぎ限りではãĒい(ãƒĒã‚ĸã‚¯ãƒ†ã‚Ŗãƒ–åŒ–ã¯ãƒžãƒ‹ãƒĨã‚ĸãƒĢãĒため)。
@@ -594,47 +333,3 @@ marginã¯ããŽã‚ŗãƒŗãƒãƒŧãƒãƒŗãƒˆã‚’äŊŋã†å´ãŒč¨­åŽšã™ã‚‹
## そぎäģ–
### HTMLぎクナ゚名で follow という単čĒžã¯äŊŋわãĒい
åēƒå‘Šãƒ–ロッã‚ĢãƒŧでčĒ¤ãŖãĻブロックされる
### indexã¨ã„ã†ãƒ•ã‚Ąã‚¤ãƒĢ名をäŊŋうãĒ
ESMã§ã¯ãƒ‡ã‚ŖãƒŦクトãƒĒã‚¤ãƒŗãƒãƒŧトはå샿­ĸされãĻã„ã‚‹ãŽã¨ã€ãƒ‡ã‚ŖãƒŦクトãƒĒã‚¤ãƒŗãƒãƒŧãƒˆã›ãšã¨ã‚‚ãƒ•ã‚Ąã‚¤ãƒĢ名が index だとäŊ•故か一部ぎナイブナãƒĒīŧŸã§ãƒ‡ã‚ŖãƒŦクトãƒĒã‚¤ãƒŗãƒãƒŧトだとčĻ‹åšã•ã‚ŒãĻエナãƒŧãĢãĒる
## CSS Recipe
### Lighten CSS vars
``` css
color: hsl(from var(--MI_THEME-accent) h s calc(l + 10));
```
### Darken CSS vars
``` css
color: hsl(from var(--MI_THEME-accent) h s calc(l - 10));
```
### Add alpha to CSS vars
``` css
color: color(from var(--MI_THEME-accent) srgb r g b / 0.5);
```
## č€ƒãˆæ–š
### DRYãĢ囚われるãĒ
åŋ…čρãĒぎは一čˆŦ化ではãĒくæŠŊčąĄåŒ–ã¨č€ƒãˆãžã™ã€‚
ᛞäŋĄã›ãšã€čĒ¤ãŖãŸãƒģ不åŋ…čρãĒå…ąé€šåŒ–ã¯éŋけ、それがč‡Ēį„ļã ã¨æ„Ÿã˜ã‚‹å ´åˆã¯é‡č¤‡ã•ã›ã‚‹å‹‡æ°—ã‚’æŒãĄãžã—ã‚‡ã†ã€‚
### Misskeyã‚’č¤‡é›‘ãĢしãĒã„åŽŸčŖ…
ãã‚ŒãŒã„ãã‚‰č¤‡é›‘ã§ã‚ãŖãĻも、Misskeyå›ēæœ‰ãŽã‚ŗãƒŗãƒ†ã‚­ã‚šãƒˆã¨é–ĸåŋƒãŒåˆ†é›ĸされãĻいる(もしくはäē‹åŽŸä¸Šåˆ†é›ĸされãĻいるとčĻ‹åšã™ã“ã¨ãŒã§ãã‚‹)åŽŸčŖ…ã§ã‚ã‚Œã°ã€ãã‚Œã¯MisskeyãŽã‚ŗãƒŧドベãƒŧ゚ãĢå¯žã™ã‚‹č¤‡é›‘æ€§ãĢåŊąéŸŋを与えãĒã„ã¨č€ƒãˆãžã™ã€‚
䞋えるãĒら、VueやAiScriptã¨ã„ãŖãŸMisskeyがäŊŋį”¨ã—ãĻいるナイブナãƒĒãŽå†…éƒ¨åŽŸčŖ…ãŒã„ãã‚‰č¤‡é›‘ã ãŖãŸã¨ã—ãĻも、「それをäŊŋį”¨ã—ãĻいるからMisskeyãŽåŽŸčŖ…ã¯č¤‡é›‘ã§ã‚ã‚‹ã€ã¨ã„ã†ã“ã¨ãĢはãĒらãĒいぎと同じです。
MisskeyãŽãƒ‰ãƒĄã‚¤ãƒŗįŸĨč­˜ã‹ã‚‰é–ĸåŋƒãŒåˆ†é›ĸされãĻいるということは、MisskeyãŽåŽŸčŖ…ãĢついãĻč€ƒãˆã‚‹æ™‚ãĢãã‚Œã‚‰ãŽå†…éƒ¨åŽŸčŖ…ã‚’č€ƒæ…Žã™ã‚‹åŋ…čĻãŒį„Ąãã€čĒįŸĨč˛ čˇã‚’åĸ—やさãĒいからです。
ぞた重čρãĒį‚šã¯ã€ããŽåŽŸčŖ…ãŒã€MisskeyãƒĒポジトãƒĒぎ外部ãĢあるかãƒģ内部ãĢあるかということや、MisskeyãŒãƒĄãƒŗãƒ†ãƒŠãƒŗã‚šã™ã‚‹ã‚‚ãŽã‹ãƒģįŦŦä¸‰č€…ãŒãƒĄãƒŗãƒ†ãƒŠãƒŗã‚šã™ã‚‹ã‚‚ãŽã‹ã¨ã„ãŖãŸã“ã¨ã¯č¤‡é›‘æ€§ã‚’č€ƒãˆã‚‹ä¸Šã§ã¯ãģã¨ã‚“ãŠį„ĄčĻ–ã§ãã‚‹ã¨ã„ã†į‚šã§ã™ã€‚
ã‚‚ãĄã‚ã‚“ããŽåŽŸčŖ…ãŒMisskeyãƒĒポジトãƒĒãĢあり、MisskeyãŒãƒĄãƒŗãƒ†ãƒŠãƒŗã‚šã—ãĒければãĒらãĒいもぎは、äŋåŽˆãŽã‚ŗã‚šãƒˆã¯ã‹ã‹ã‚Šãžã™ã€‚
しかし、MisskeyぎæœŦčŗĒįš„ãĒ設計ãƒģåŽŸčŖ…ã¨ã„ã†čĻŗį‚šã§čĻ‹ãŸã¨ãã¯ã€ããŽåŽŸčŖ…ã¯åŽŸčŗĒįš„ãĢ外部ナイブナãƒĒぎようãĢæŒ¯ã‚‹čˆžã„ãžã™ã€‚
æ›č¨€ã™ã‚Œã°ã€ŒãŸãžãŸãžMisskeyぎ開į™ēč€…ã¨åŒã˜äēēãŸãĄãŒãƒĄãƒŗãƒ†ãƒŠãƒŗã‚šã—ãĻいるし、たぞたぞMisskeyぎãƒĒポジトãƒĒ内ãĢįŊŽã„ãĻあるだけぎ外部ナイブナãƒĒ」です。
ããŽãŸã‚ã€åŽŸčŖ…ã‚’ãĒるずくMisskeyãŽãƒ‰ãƒĄã‚¤ãƒŗįŸĨč­˜ã‹ã‚‰į‹ŦįĢ‹ã—ãŸã‚‚ãŽãĢすれば、MisskeyãŽã‚ŗãƒŧドベãƒŧã‚šãŽč¤‡é›‘æ€§ã‚’ä¸Šã’ã‚‹ã“ã¨ãĒく抟čƒŊåŽŸčŖ…ã‚’čĄŒã†ã“ã¨ãŒã§ãã€ãŠåž—ã§ã‚ã‚‹ã¨č¨€ãˆãžã™ã€‚
ã‚‚ãĄã‚ã‚“ãã‚ŒãĢã“ã ã‚ãŖãĻ、äē›į´°ãĒåŽŸčŖ…ã§ã‚‚ããŽã‚ˆã†ãĢ分é›ĸしãĻã—ãžã†ã¨ã‹ãˆãŖãĻčĒįŸĨč˛ čˇãŒåĸ—ãˆãŸã‚Šã€åŽŸčŖ…é‡ãŒåĸ—えãĻãƒĄãƒĒãƒƒãƒˆã‚’ãƒ‡ãƒĄãƒĒãƒƒãƒˆãŒä¸Šå›žã‚‹å ´åˆã‚‚ã‚ã‚‹ãŽã§ã€ã‚ąãƒŧã‚šãƒã‚¤ã‚ąãƒŧ゚ではありぞす。

View File

@@ -1,5 +1,5 @@
Unless otherwise stated this repository is
Copyright Š 2014-2026 syuilo and contributors
Copyright Š 2014-2023 syuilo and contributers
And is distributed under The GNU Affero General Public License Version 3, you should have received a copy of the license file as LICENSE.

View File

@@ -1,6 +1,6 @@
# syntax = docker/dockerfile:1.4
ARG NODE_VERSION=22.15.0-bookworm
ARG NODE_VERSION=18.13.0-bullseye
# build assets & compile TypeScript
@@ -14,32 +14,24 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
&& apt-get install -yqq --no-install-recommends \
build-essential
RUN corepack enable
WORKDIR /misskey
COPY --link ["pnpm-lock.yaml", "pnpm-workspace.yaml", "package.json", "./"]
COPY --link ["scripts", "./scripts"]
COPY --link ["patches", "./patches"]
COPY --link ["packages/backend/package.json", "./packages/backend/"]
COPY --link ["packages/frontend-shared/package.json", "./packages/frontend-shared/"]
COPY --link ["packages/frontend/package.json", "./packages/frontend/"]
COPY --link ["packages/frontend-embed/package.json", "./packages/frontend-embed/"]
COPY --link ["packages/frontend-builder/package.json", "./packages/frontend-builder/"]
COPY --link ["packages/i18n/package.json", "./packages/i18n/"]
COPY --link ["packages/icons-subsetter/package.json", "./packages/icons-subsetter/"]
COPY --link ["packages/sw/package.json", "./packages/sw/"]
COPY --link ["packages/misskey-js/package.json", "./packages/misskey-js/"]
COPY --link ["packages/misskey-reversi/package.json", "./packages/misskey-reversi/"]
COPY --link ["packages/misskey-bubble-game/package.json", "./packages/misskey-bubble-game/"]
ARG NODE_ENV=production
RUN node -e "console.log(JSON.parse(require('node:fs').readFileSync('./package.json')).packageManager)" | xargs npm install -g
RUN --mount=type=cache,target=/root/.local/share/pnpm/store,sharing=locked \
pnpm i --frozen-lockfile --aggregate-output
COPY --link . ./
ARG NODE_ENV=production
RUN git submodule update --init
RUN pnpm build
RUN rm -rf .git/
@@ -52,19 +44,13 @@ RUN apt-get update \
&& apt-get install -yqq --no-install-recommends \
build-essential
RUN corepack enable
WORKDIR /misskey
COPY --link ["pnpm-lock.yaml", "pnpm-workspace.yaml", "package.json", "./"]
COPY --link ["scripts", "./scripts"]
COPY --link ["patches", "./patches"]
COPY --link ["packages/backend/package.json", "./packages/backend/"]
COPY --link ["packages/misskey-js/package.json", "./packages/misskey-js/"]
COPY --link ["packages/misskey-reversi/package.json", "./packages/misskey-reversi/"]
COPY --link ["packages/misskey-bubble-game/package.json", "./packages/misskey-bubble-game/"]
ARG NODE_ENV=production
RUN node -e "console.log(JSON.parse(require('node:fs').readFileSync('./package.json')).packageManager)" | xargs npm install -g
RUN --mount=type=cache,target=/root/.local/share/pnpm/store,sharing=locked \
pnpm i --frozen-lockfile --aggregate-output
@@ -76,38 +62,25 @@ ARG GID="991"
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
ffmpeg tini curl libjemalloc-dev libjemalloc2 \
&& ln -s /usr/lib/$(uname -m)-linux-gnu/libjemalloc.so.2 /usr/local/lib/libjemalloc.so \
ffmpeg tini curl \
&& corepack enable \
&& groupadd -g "${GID}" misskey \
&& useradd -l -u "${UID}" -g "${GID}" -m -d /misskey misskey \
&& find / -type d -path /sys -prune -o -type d -path /proc -prune -o -type f -perm /u+s -ignore_readdir_race -exec chmod u-s {} \; \
&& find / -type d -path /sys -prune -o -type d -path /proc -prune -o -type f -perm /g+s -ignore_readdir_race -exec chmod g-s {} \; \
&& find / -type d -path /proc -prune -o -type f -perm /u+s -ignore_readdir_race -exec chmod u-s {} \; \
&& find / -type d -path /proc -prune -o -type f -perm /g+s -ignore_readdir_race -exec chmod g-s {} \; \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists
# add package.json to add pnpm
COPY ./package.json ./package.json
RUN node -e "console.log(JSON.parse(require('node:fs').readFileSync('./package.json')).packageManager)" | xargs npm install -g
USER misskey
WORKDIR /misskey
COPY --chown=misskey:misskey --from=target-builder /misskey/node_modules ./node_modules
COPY --chown=misskey:misskey --from=target-builder /misskey/packages/backend/node_modules ./packages/backend/node_modules
COPY --chown=misskey:misskey --from=target-builder /misskey/packages/misskey-js/node_modules ./packages/misskey-js/node_modules
COPY --chown=misskey:misskey --from=target-builder /misskey/packages/misskey-reversi/node_modules ./packages/misskey-reversi/node_modules
COPY --chown=misskey:misskey --from=target-builder /misskey/packages/misskey-bubble-game/node_modules ./packages/misskey-bubble-game/node_modules
COPY --chown=misskey:misskey --from=native-builder /misskey/built ./built
COPY --chown=misskey:misskey --from=native-builder /misskey/packages/misskey-js/built ./packages/misskey-js/built
COPY --chown=misskey:misskey --from=native-builder /misskey/packages/misskey-reversi/built ./packages/misskey-reversi/built
COPY --chown=misskey:misskey --from=native-builder /misskey/packages/misskey-bubble-game/built ./packages/misskey-bubble-game/built
COPY --chown=misskey:misskey --from=native-builder /misskey/packages/backend/built ./packages/backend/built
COPY --chown=misskey:misskey --from=native-builder /misskey/packages/backend/src-js ./packages/backend/src-js
COPY --chown=misskey:misskey --from=native-builder /misskey/packages/i18n/built ./packages/i18n/built
COPY --chown=misskey:misskey --from=native-builder /misskey/fluent-emojis /misskey/fluent-emojis
COPY --chown=misskey:misskey . ./
ENV LD_PRELOAD=/usr/local/lib/libjemalloc.so
ENV NODE_ENV=production
HEALTHCHECK --interval=5s --retries=20 CMD ["/bin/bash", "/misskey/healthcheck.sh"]
ENTRYPOINT ["/usr/bin/tini", "--"]

View File

@@ -1,18 +1,16 @@
<div align="center">
<a href="https://misskey-hub.net">
<img src="./assets/title_float.svg" alt="Misskey logo" style="border-radius:50%" width="300"/>
<img src="./assets/title_float.svg" alt="Misskey logo" style="border-radius:50%" width="400"/>
</a>
**🌎 **Misskey** is an open source, federated social media platform that's free forever! 🚀**
[Learn more](https://misskey-hub.net/)
**🌎 **[Misskey](https://misskey-hub.net/)** is an open source, decentralized social media platform that's free forever! 🚀**
---
<a href="https://misskey-hub.net/servers/">
<a href="https://misskey-hub.net/instances.html">
<img src="https://custom-icon-badges.herokuapp.com/badge/find_an-instance-acea31?logoColor=acea31&style=for-the-badge&logo=misskey&labelColor=363B40" alt="find an instance"/></a>
<a href="https://misskey-hub.net/docs/for-admin/install/guides/">
<a href="https://misskey-hub.net/docs/install.html">
<img src="https://custom-icon-badges.herokuapp.com/badge/create_an-instance-FBD53C?logoColor=FBD53C&style=for-the-badge&logo=server&labelColor=363B40" alt="create an instance"/></a>
<a href="./CONTRIBUTING.md">
@@ -23,31 +21,39 @@
<a href="https://www.patreon.com/syuilo">
<img src="https://custom-icon-badges.herokuapp.com/badge/become_a-patron-F96854?logoColor=F96854&style=for-the-badge&logo=patreon&labelColor=363B40" alt="become a patron"/></a>
---
[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/misskey-dev/misskey)
<a href="https://flatt.tech/oss/gmo/trampoline" target="_blank"><img src="https://flatt.tech/assets/images/badges/gmo-oss.svg" height="24px"/></a>
[![codecov](https://codecov.io/gh/misskey-dev/misskey/branch/develop/graph/badge.svg?token=R6IQZ3QJOL)](https://codecov.io/gh/misskey-dev/misskey)
</div>
## Thanks
<div>
<a href="https://sentry.io/"><img src="https://github.com/misskey-dev/misskey/assets/4439005/98576556-222f-467a-94be-e98dbda1d852" height="30" alt="Sentry" /></a>
<a href="https://xn--931a.moe/"><img src="https://github.com/misskey-dev/misskey/blob/develop/assets/ai.png?raw=true" align="right" height="320px"/></a>
Thanks to [Sentry](https://sentry.io/) for providing the error tracking platform that helps us catch unexpected errors.
## ✨ Features
- **ActivityPub support**\
Not on Misskey? No problem! Not only can Misskey instances talk to each other, but you can make friends with people on other networks like Mastodon and Pixelfed!
- **Reactions**\
You can add emoji reactions to any post! No longer are you bound by a like button, show everyone exactly how you feel with the tap of a button.
- **Drive**\
With Misskey's built in drive, you get cloud storage right in your social media, where you can upload any files, make folders, and find media from posts you've made!
- **Rich Web UI**\
Misskey has a rich and easy to use Web UI!
It is highly customizable, from changing the layout and adding widgets to making custom themes.
Furthermore, plugins can be created using AiScript, an original programming language.
- And much more...
<a href="https://www.chromatic.com/"><img src="https://user-images.githubusercontent.com/321738/84662277-e3db4f80-af1b-11ea-88f5-91d67a5e59f6.png" height="30" alt="Chromatic" /></a>
</div>
Thanks to [Chromatic](https://www.chromatic.com/) for providing the visual testing platform that helps us review UI changes and catch visual regressions.
<div style="clear: both;"></div>
<a href="https://about.codecov.io/for/open-source/"><img src="https://about.codecov.io/wp-content/themes/codecov/assets/brand/sentry-cobranding/logos/codecov-by-sentry-logo.svg" height="30" alt="Codecov" /></a>
## Documentation
Thanks to [Codecov](https://about.codecov.io/for/open-source/) for providing the code coverage platform that helps us improve our test coverage.
Misskey Documentation can be found at [Misskey Hub](https://misskey-hub.net/), some of the links and graphics above also lead to specific portions of it.
<a href="https://crowdin.com/"><img src="https://user-images.githubusercontent.com/20679825/230709597-1299a011-171a-4294-a91e-355a9b37c672.svg" height="30" alt="Crowdin" /></a>
Thanks to [Crowdin](https://crowdin.com/) for providing the localization platform that helps us translate Misskey into many languages.
<a href="https://hub.docker.com/"><img src="https://user-images.githubusercontent.com/20679825/230148221-f8e73a32-a49b-47c3-9029-9a15c3824f92.png" height="30" alt="Docker" /></a>
Thanks to [Docker](https://hub.docker.com/) for providing the container platform that helps us run Misskey in production.
## Sponsors
<div align="center">
<a class="rss3" title="RSS3" href="https://rss3.io/" target="_blank"><img src="https://rss3.mypinata.cloud/ipfs/QmUG6H3Z7D5P511shn7sB4CPmpjH5uZWu4m5mWX7U3Gqbu" alt="RSS3" height="60"></a>
</div>

View File

@@ -6,7 +6,6 @@ Also, the later tasks are more indefinite and are subject to change as developme
This is the phase we are at now. We need to make a high-maintenance environment that can withstand future development.
- ~~Make the number of type errors zero (backend)~~ → Done âœ”ī¸
- ~~Make the number of type errors zero (frontend)~~ → Done âœ”ī¸
- Improve CI
- ~~Fix tests~~ → Done âœ”ī¸
- Fix random test failures - https://github.com/misskey-dev/misskey/issues/7985 and https://github.com/misskey-dev/misskey/issues/7986
@@ -23,7 +22,7 @@ This is the phase we are at now. We need to make a high-maintenance environment
Once Phase 1 is complete and an environment conducive to the development of a stable system is in place, the implementation of new functions can begin gradually.
- Improve features for moderation
- ~~OAuth2 support https://github.com/misskey-dev/misskey/issues/8262~~ → Done âœ”ī¸
- OAuth2 support https://github.com/misskey-dev/misskey/issues/8262
- GraphQL support?
## (3) Improve scalability

View File

@@ -1,20 +1,9 @@
# Reporting Security Issues
If you discover a security issue in Misskey, please report it by **[this form](https://github.com/misskey-dev/misskey/security/advisories/new)**.
If you discover a security issue in Misskey, please report it by sending an
email to [syuilotan@yahoo.co.jp](mailto:syuilotan@yahoo.co.jp).
This will allow us to assess the risk, and make a fix available before we add a
bug report to the GitHub repository.
Thanks for helping make Misskey safe for everyone.
> [!note]
> CNA [requires](https://www.cve.org/ResourcesSupport/AllResources/CNARules#section_5-2_Description) that CVEs include a description in English for inclusion in the CVE Catalog.
>
> When creating a security advisory, all content must be written in English (it is acceptable to include a non-English description along with the English one).
## When create a patch
If you can also create a patch to fix the vulnerability, please create a PR on the private fork.
> [!note]
> There is a GitHub bug that prevents merging if a PR not following the develop branch of upstream, so please keep follow the develop branch.

BIN
assets/about/drive.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

BIN
assets/about/post.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 317 KiB

BIN
assets/about/reaction.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
assets/about/ui.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

BIN
assets/ss/explore.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 238 KiB

BIN
assets/ss/user.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

View File

@@ -72,75 +72,25 @@ db:
#extra:
# ssl: true
dbReplications: false
# You can configure any number of replicas here
#dbSlaves:
# -
# host:
# port:
# db:
# user:
# pass:
# -
# host:
# port:
# db:
# user:
# pass:
# ┌─────────────────────┐
#───┘ Redis configuration └─────────────────────────────────────
redis:
host: localhost
port: 6379
#family: 0 # 0=Both, 4=IPv4, 6=IPv6
#pass: example-pass
#prefix: example-prefix
#db: 1
#redisForPubsub:
# ┌─────────────────────────────┐
#───┘ Elasticsearch configuration └─────────────────────────────
#elasticsearch:
# host: localhost
# port: 6379
# #family: 0 # 0=Both, 4=IPv4, 6=IPv6
# #pass: example-pass
# #prefix: example-prefix
# #db: 1
#redisForJobQueue:
# host: localhost
# port: 6379
# #family: 0 # 0=Both, 4=IPv4, 6=IPv6
# #pass: example-pass
# #prefix: example-prefix
# #db: 1
#redisForTimelines:
# host: redis
# port: 6379
# #family: 0 # 0=Both, 4=IPv4, 6=IPv6
# #pass: example-pass
# #prefix: example-prefix
# #db: 1
#redisForReactions:
# host: redis
# port: 6379
# #family: 0 # 0=Both, 4=IPv4, 6=IPv6
# #pass: example-pass
# #prefix: example-prefix
# #db: 1
# ┌───────────────────────────┐
#───┘ MeiliSearch configuration └─────────────────────────────
#meilisearch:
# host: localhost
# port: 7700
# apiKey: ''
# ssl: true
# index: ''
# port: 9200
# ssl: false
# user:
# pass:
# ┌───────────────┐
#───┘ ID generation └───────────────────────────────────────────
@@ -151,7 +101,6 @@ redis:
# Available methods:
# aid ... Short, Millisecond accuracy
# aidx ... Millisecond accuracy
# meid ... Similar to ObjectID, Millisecond accuracy
# ulid ... Millisecond accuracy
# objectid ... This is left for backward compatibility
@@ -159,28 +108,7 @@ redis:
# ONCE YOU HAVE STARTED THE INSTANCE, DO NOT CHANGE THE
# ID SETTINGS AFTER THAT!
id: "aidx"
# ┌────────────────┐
#───┘ Error tracking └──────────────────────────────────────────
# Sentry is available for error tracking.
# See the Sentry documentation for more details on options.
#sentryForBackend:
# enableNodeProfiling: true
# options:
# dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0'
#sentryForFrontend:
# vueIntegration:
# tracingOptions:
# trackComponents: true
# browserTracingIntegration:
# replayIntegration:
# options:
# dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0'
id: "aid"
# ┌─────────────────────┐
#───┘ Other configuration └─────────────────────────────────────
@@ -196,7 +124,7 @@ id: "aidx"
# Job rate limiter
# deliverJobPerSec: 128
# inboxJobPerSec: 32
# inboxJobPerSec: 16
# Job attempts
# deliverJobMaxAttempts: 12
@@ -221,6 +149,9 @@ id: "aidx"
# Media Proxy
#mediaProxy: https://example.com/proxy
# Sign to ActivityPub GET request (default: true)
signToActivityPubGet: true
#allowedPrivateNetworks: [
# '127.0.0.1/32'
#]

View File

@@ -27,7 +27,7 @@ spec:
ports:
- containerPort: 3000
- name: postgres
image: postgres:18-alpine
image: postgres:14-alpine
env:
- name: POSTGRES_USER
value: "example-misskey-user"
@@ -38,7 +38,7 @@ spec:
ports:
- containerPort: 5432
- name: redis
image: redis:7-alpine
image: redis:alpine
ports:
- containerPort: 6379
volumes:

View File

@@ -1,40 +0,0 @@
# こぎconfigは、 dockerでMisskeyæœŦäŊ“ã‚’čĩˇå‹•せず、 redisとpostgresql ãĒおだけをčĩˇå‹•しぞす
services:
redis:
restart: always
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- ./redis:/data
healthcheck:
test: "redis-cli ping"
interval: 5s
retries: 20
db:
restart: always
image: postgres:18-alpine
ports:
- "5432:5432"
env_file:
- .config/docker.env
volumes:
- ./db:/var/lib/postgresql
healthcheck:
test: "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"
interval: 5s
retries: 20
# meilisearch:
# restart: always
# image: getmeili/meilisearch:v1.3.4
# environment:
# - MEILI_NO_ANALYTICS=true
# - MEILI_ENV=production
# env_file:
# - .config/meilisearch.env
# volumes:
# - ./meili_data:/meili_data

View File

@@ -1,98 +0,0 @@
services:
web:
build: .
restart: always
links:
- db
- redis
# - mcaptcha
# - meilisearch
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
ports:
- "3000:3000"
networks:
- internal_network
- external_network
# env_file:
# - .config/docker.env
volumes:
- ./files:/misskey/files
- ./.config:/misskey/.config:ro
redis:
restart: always
image: redis:7-alpine
networks:
- internal_network
volumes:
- ./redis:/data
healthcheck:
test: "redis-cli ping"
interval: 5s
retries: 20
db:
restart: always
image: postgres:18-alpine
networks:
- internal_network
env_file:
- .config/docker.env
volumes:
- ./db:/var/lib/postgresql
healthcheck:
test: "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"
interval: 5s
retries: 20
# mcaptcha:
# restart: always
# image: mcaptcha/mcaptcha:latest
# networks:
# internal_network:
# external_network:
# aliases:
# - localhost
# ports:
# - 7493:7493
# env_file:
# - .config/docker.env
# environment:
# PORT: 7493
# MCAPTCHA_redis_URL: "redis://mcaptcha_redis/"
# depends_on:
# db:
# condition: service_healthy
# mcaptcha_redis:
# condition: service_healthy
#
# mcaptcha_redis:
# image: mcaptcha/cache:latest
# networks:
# - internal_network
# healthcheck:
# test: "redis-cli ping"
# interval: 5s
# retries: 20
# meilisearch:
# restart: always
# image: getmeili/meilisearch:v1.3.4
# environment:
# - MEILI_NO_ANALYTICS=true
# - MEILI_ENV=production
# env_file:
# - .config/meilisearch.env
# networks:
# - internal_network
# volumes:
# - ./meili_data:/meili_data
networks:
internal_network:
internal: true
external_network:

View File

@@ -2,6 +2,11 @@ import { defineConfig } from 'cypress'
export default defineConfig({
e2e: {
// We've imported your old cypress plugins here.
// You may want to clean this up later by importing these.
setupNodeEvents(on, config) {
return require('./cypress/plugins/index.js')(on, config)
},
baseUrl: 'http://localhost:61812',
},
})

View File

@@ -1,8 +1,3 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
describe('Before setup instance', () => {
beforeEach(() => {
cy.resetState();
@@ -23,7 +18,6 @@ describe('Before setup instance', () => {
cy.intercept('POST', '/api/admin/accounts/create').as('signup');
cy.get('[data-cy-admin-initial-password] input').type('example_password_please_change_this_or_you_will_get_hacked');
cy.get('[data-cy-admin-username] input').type('admin');
cy.get('[data-cy-admin-password] input').type('admin1234');
cy.get('[data-cy-admin-ok]').click();
@@ -31,14 +25,6 @@ describe('Before setup instance', () => {
// ãĒぜか動かãĒい
//cy.wait('@signup').should('have.property', 'response.statusCode');
cy.wait('@signup');
cy.intercept('POST', '/api/admin/update-meta').as('update-meta');
cy.get('[data-cy-next]').click();
cy.get('[data-cy-server-name] input').type('Testskey');
cy.get('[data-cy-server-setup-wizard-apply]').click();
cy.wait('@update-meta');
});
});
@@ -66,20 +52,12 @@ describe('After setup instance', () => {
cy.intercept('POST', '/api/signup').as('signup');
cy.get('[data-cy-signup]').click();
cy.get('[data-cy-signup-rules-continue]').should('be.disabled');
cy.get('[data-cy-signup-rules-notes-agree] [data-cy-switch-toggle]').click();
cy.get('[data-cy-modal-dialog-ok]').click();
cy.get('[data-cy-signup-rules-continue]').should('not.be.disabled');
cy.get('[data-cy-signup-rules-continue]').click();
cy.get('[data-cy-signup-submit]').should('be.disabled');
cy.get('[data-cy-signup-username] input').type('alice');
cy.get('[data-cy-signup-submit]').should('be.disabled');
cy.get('[data-cy-signup-password] input').type('alice1234');
cy.get('[data-cy-signup-submit]').should('be.disabled');
cy.get('[data-cy-signup-password-retype] input').type('alice1234');
cy.get('[data-cy-signup-submit]').should('be.disabled');
cy.get('[data-cy-signup-invitation-code] input').type('test-invitation-code');
cy.get('[data-cy-signup-submit]').should('not.be.disabled');
cy.get('[data-cy-signup-submit]').click();
@@ -93,12 +71,6 @@ describe('After setup instance', () => {
// ãƒĻãƒŧã‚ļãƒŧåãŒé‡č¤‡ã—ãĻいる場合ぎ挙動įĸēčĒ
cy.get('[data-cy-signup]').click();
cy.get('[data-cy-signup-rules-continue]').should('be.disabled');
cy.get('[data-cy-signup-rules-notes-agree] [data-cy-switch-toggle]').click();
cy.get('[data-cy-modal-dialog-ok]').click();
cy.get('[data-cy-signup-rules-continue]').should('not.be.disabled');
cy.get('[data-cy-signup-rules-continue]').click();
cy.get('[data-cy-signup-username] input').type('alice');
cy.get('[data-cy-signup-password] input').type('alice1234');
cy.get('[data-cy-signup-password-retype] input').type('alice1234');
@@ -130,16 +102,11 @@ describe('After user signup', () => {
it('signin', () => {
cy.visitHome();
cy.intercept('POST', '/api/signin-flow').as('signin');
cy.intercept('POST', '/api/signin').as('signin');
cy.get('[data-cy-signin]').click();
cy.get('[data-cy-signin-page-input]').should('be.visible', { timeout: 1000 });
// EnterキãƒŧでįļščĄŒã§ãã‚‹ã‹ãŽįĸēčĒã‚‚å…ŧねる
cy.get('[data-cy-signin-username] input').type('alice{enter}');
cy.get('[data-cy-signin-page-password]').should('be.visible', { timeout: 10000 });
// EnterキãƒŧでįļščĄŒã§ãã‚‹ã‹ãŽįĸēčĒã‚‚å…ŧねる
cy.get('[data-cy-signin-username] input').type('alice');
// Enterキãƒŧでã‚ĩã‚¤ãƒŗã‚¤ãƒŗã§ãã‚‹ã‹ãŽįĸēčĒã‚‚å…ŧねる
cy.get('[data-cy-signin-password] input').type('alice1234{enter}');
cy.wait('@signin');
@@ -154,9 +121,8 @@ describe('After user signup', () => {
cy.visitHome();
cy.get('[data-cy-signin]').click();
cy.get('[data-cy-signin-page-input]').should('be.visible', { timeout: 1000 });
cy.get('[data-cy-signin-username] input').type('alice{enter}');
cy.get('[data-cy-signin-username] input').type('alice');
cy.get('[data-cy-signin-password] input').type('alice1234{enter}');
// TODO: cypressãĢブナã‚Ļã‚ļãŽč¨€čĒžæŒ‡åŽšã§ãã‚‹æŠŸčƒŊãŒåŽŸčŖ…ã•ã‚ŒæŦĄįŦŦ英čĒžãŽãŋテ゚トするようãĢする
cy.contains(/ã‚ĸã‚Ģã‚ĻãƒŗãƒˆãŒå‡įĩã•れãĻいぞす|This account has been suspended due to/gi);
@@ -183,67 +149,15 @@ describe('After user signed in', () => {
});
it('successfully loads', () => {
// 襨į¤ēãĢ時間がかかるぎでデフりãƒĢãƒˆį§’æ•°ã ã¨ã‚ŋイムã‚ĸã‚Ļトする
cy.get('[data-cy-user-setup-continue]', { timeout: 30000 }).should('be.visible');
cy.get('[data-cy-open-post-form]').should('be.visible');
});
it('account setup wizard', () => {
// 襨į¤ēãĢ時間がかかるぎでデフりãƒĢãƒˆį§’æ•°ã ã¨ã‚ŋイムã‚ĸã‚Ļトする
cy.get('[data-cy-user-setup-continue]', { timeout: 30000 }).click();
cy.get('[data-cy-user-setup-user-name] input').type('ありす');
cy.get('[data-cy-user-setup-user-description] textarea').type('ãģげ');
// TODO: ã‚ĸã‚¤ã‚ŗãƒŗč¨­åŽšãƒ†ã‚šãƒˆ
cy.get('[data-cy-user-setup-continue]').click();
// ãƒ—ãƒŠã‚¤ãƒã‚ˇãƒŧč¨­åŽš
cy.get('[data-cy-user-setup-continue]').click();
// フりロãƒŧぱキップ
cy.get('[data-cy-user-setup-continue]').click();
// ãƒ—ãƒƒã‚ˇãƒĨ通įŸĨč¨­åŽšã¯ã‚šã‚­ãƒƒãƒ—
cy.get('[data-cy-user-setup-continue]').click();
cy.get('[data-cy-user-setup-continue]').click();
});
});
describe('After user setup', () => {
beforeEach(() => {
cy.resetState();
// ã‚¤ãƒŗã‚šã‚ŋãƒŗã‚šåˆæœŸã‚ģットã‚ĸップ
cy.registerUser('admin', 'pass', true);
// ãƒĻãƒŧã‚ļãƒŧäŊœæˆ
cy.registerUser('alice', 'alice1234');
cy.login('alice', 'alice1234');
// ã‚ĸã‚Ģã‚ĻãƒŗãƒˆåˆæœŸč¨­åŽšã‚Ļã‚Ŗã‚ļãƒŧド
// 襨į¤ēãĢ時間がかかるぎでデフりãƒĢãƒˆį§’æ•°ã ã¨ã‚ŋイムã‚ĸã‚Ļトする
cy.get('[data-cy-user-setup] [data-cy-modal-window-close]', { timeout: 30000 }).click();
cy.get('[data-cy-modal-dialog-ok]').click();
});
afterEach(() => {
// テ゚トįĩ‚äē†į›´å‰ãĢペãƒŧã‚¸éˇį§ģするようãĒãƒ†ã‚šãƒˆã‚ąãƒŧ゚(䞋えばã‚ĸã‚Ģã‚ĻãƒŗãƒˆäŊœæˆ)だと、たãļんCypressぎバグでブナã‚Ļã‚ļぎ内厚がæŦĄãŽãƒ†ã‚šãƒˆã‚ąãƒŧ゚ãĢåŧ•きįļ™ãŒã‚ŒãĻしぞう(䞋えばã‚ĸã‚Ģã‚ĻãƒŗãƒˆãŒäŊœæˆã—įĩ‚ã‚ãŖãŸæŽĩ階からテ゚トが始ぞる)。
// waitをå…Ĩã‚Œã‚‹ã“ã¨ã§ãã‚Œã‚’é˜˛æ­ĸできる
cy.wait(1000);
});
it('note', () => {
cy.get('[data-cy-open-post-form]').should('be.visible');
cy.get('[data-cy-open-post-form]').click();
cy.get('[data-cy-post-form-text]').type('Hello, Misskey!');
cy.get('[data-cy-open-post-form-submit]').click();
cy.contains('Hello, Misskey!', { timeout: 15000 });
cy.contains('Hello, Misskey!');
});
it('open note form with hotkey', () => {

View File

@@ -1,35 +0,0 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
describe('Router transition', () => {
describe('Redirect', () => {
// ã‚ĩãƒŧバぎ初期化。ãƒĢãƒŧトぎテ゚トãĢé–ĸしãĻは各describeごとãĢ1åēĻã ã‘åŽŸčĄŒã§ååˆ†ã ã¨æ€ã†īŧˆäŊŋいぞわした斚が旊いīŧ‰
before(() => {
cy.resetState();
// ã‚¤ãƒŗã‚šã‚ŋãƒŗã‚šåˆæœŸã‚ģットã‚ĸップ
cy.registerUser('admin', 'pass', true);
// ãƒĻãƒŧã‚ļãƒŧäŊœæˆ
cy.registerUser('alice', 'alice1234');
cy.login('alice', 'alice1234');
// ã‚ĸã‚Ģã‚ĻãƒŗãƒˆåˆæœŸč¨­åŽšã‚Ļã‚Ŗã‚ļãƒŧド
// 襨į¤ēãĢ時間がかかるぎでデフりãƒĢãƒˆį§’æ•°ã ã¨ã‚ŋイムã‚ĸã‚Ļトする
cy.get('[data-cy-user-setup] [data-cy-modal-window-close]', { timeout: 30000 }).click();
cy.wait(500);
cy.get('[data-cy-modal-dialog-ok]').click();
});
it('redirect to user profile', () => {
// テ゚トぎためだけãĢį”¨æ„ã•ã‚ŒãŸãƒĒダイãƒŦã‚¯ãƒˆį”¨ãƒĢãƒŧトãĢéŖ›ãļ
cy.visit('/redirect-test');
// ãƒ—ãƒ­ãƒ•ã‚ŖãƒŧãƒĢペãƒŧジぎURLであることをįĸēčĒã™ã‚‹
cy.url().should('include', '/@alice')
});
});
});

View File

@@ -1,9 +1,3 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
/* flaky
describe('After user signed in', () => {
beforeEach(() => {
cy.resetState();
@@ -16,10 +10,6 @@ describe('After user signed in', () => {
cy.registerUser('alice', 'alice1234');
cy.login('alice', 'alice1234');
// ã‚ĸã‚Ģã‚ĻãƒŗãƒˆåˆæœŸč¨­åŽšã‚Ļã‚Ŗã‚ļãƒŧド
cy.get('[data-cy-user-setup] [data-cy-modal-window-close]').click();
cy.get('[data-cy-modal-dialog-ok]').click();
});
afterEach(() => {
@@ -29,26 +19,26 @@ describe('After user signed in', () => {
});
it('widget edit toggle is visible', () => {
cy.get('[data-cy-widget-edit]').should('be.visible');
cy.get('.mk-widget-edit').should('be.visible');
});
it('widget select should be visible in edit mode', () => {
cy.get('[data-cy-widget-edit]').click();
cy.get('[data-cy-widget-select]').should('be.visible');
cy.get('.mk-widget-edit').click();
cy.get('.mk-widget-select').should('be.visible');
});
it('first widget should be removed', () => {
cy.get('[data-cy-widget-edit]').click();
cy.get('.mk-widget-edit').click();
cy.get('[data-cy-customize-container]:first-child [data-cy-customize-container-remove]._button').click();
cy.get('[data-cy-customize-container]').should('have.length', 2);
});
function buildWidgetTest(widgetName) {
it(`${widgetName} widget should get added`, () => {
cy.get('[data-cy-widget-edit]').click();
cy.get('[data-cy-widget-select] select').select(widgetName, { force: true });
cy.get('.mk-widget-edit').click();
cy.get('.mk-widget-select select').select(widgetName, { force: true });
cy.get('[data-cy-bg]._modalBg[data-cy-transparent]').click({ multiple: true, force: true });
cy.get('[data-cy-widget-add]').click({ force: true });
cy.get('.mk-widget-add').click({ force: true });
cy.get(`[data-cy-mkw-${widgetName}]`).should('exist');
});
}
@@ -73,4 +63,3 @@ describe('After user signed in', () => {
buildWidgetTest('aiscript');
buildWidgetTest('aichan');
});
*/

22
cypress/plugins/index.js Normal file
View File

@@ -0,0 +1,22 @@
/// <reference types="cypress" />
// ***********************************************************
// This example plugins/index.js can be used to load plugins
//
// You can change the location of this file or turn off loading
// the plugins file with the 'pluginsFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/plugins-guide
// ***********************************************************
// This function is called when a project is opened or re-opened (e.g. due to
// the project's config changing)
/**
* @type {Cypress.PluginConfig}
*/
// eslint-disable-next-line no-unused-vars
module.exports = (on, config) => {
// `on` is used to hook into various events Cypress emits
// `config` is the resolved Cypress config
}

View File

@@ -30,13 +30,9 @@ Cypress.Commands.add('visitHome', () => {
})
Cypress.Commands.add('resetState', () => {
// iframe.contentWindow.indexedDB.deleteDatabase() がchromeぎバグでäŊŋį”¨ã§ããĒいため、indexedDBã‚’į„ĄåŠšåŒ–ã—ãĻいる。
// see https://github.com/misskey-dev/misskey/issues/13605#issuecomment-2053652123
/*
cy.window().then(win => {
cy.window(win => {
win.indexedDB.deleteDatabase('keyval-store');
});
*/
cy.request('POST', '/api/reset-db', {}).as('reset');
cy.get('@reset').its('status').should('equal', 204);
cy.reload(true);
@@ -48,19 +44,16 @@ Cypress.Commands.add('registerUser', (username, password, isAdmin = false) => {
cy.request('POST', route, {
username: username,
password: password,
...(isAdmin ? { setupPassword: 'example_password_please_change_this_or_you_will_get_hacked' } : {}),
}).its('body').as(username);
});
Cypress.Commands.add('login', (username, password) => {
cy.visitHome();
cy.intercept('POST', '/api/signin-flow').as('signin');
cy.intercept('POST', '/api/signin').as('signin');
cy.get('[data-cy-signin]').click();
cy.get('[data-cy-signin-page-input]').should('be.visible', { timeout: 1000 });
cy.get('[data-cy-signin-username] input').type(`${username}{enter}`);
cy.get('[data-cy-signin-page-password]').should('be.visible', { timeout: 10000 });
cy.get('[data-cy-signin-username] input').type(username);
cy.get('[data-cy-signin-password] input').type(`${password}{enter}`);
cy.wait('@signin').as('signedIn');

View File

@@ -21,8 +21,6 @@ import './commands'
Cypress.on('uncaught:exception', (err, runnable) => {
if ([
'The source image cannot be decoded',
// Chrome
'ResizeObserver loop limit exceeded',

View File

@@ -1,19 +0,0 @@
declare global {
namespace Cypress {
interface Chainable {
login(username: string, password: string): Chainable<void>;
registerUser(
username: string,
password: string,
isAdmin?: boolean
): Chainable<void>;
resetState(): Chainable<void>;
visitHome(): Chainable<void>;
}
}
}
export {}

View File

@@ -1,8 +0,0 @@
{
"compilerOptions": {
"lib": ["dom", "es5"],
"target": "es5",
"types": ["cypress", "node"]
},
"include": ["./**/*.ts"]
}

View File

@@ -0,0 +1,65 @@
version: "3"
services:
web:
build: .
restart: always
links:
- db
- redis
# - es
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
ports:
- "3000:3000"
networks:
- internal_network
- external_network
volumes:
- ./files:/misskey/files
- ./.config:/misskey/.config:ro
redis:
restart: always
image: redis:7-alpine
networks:
- internal_network
volumes:
- ./redis:/data
healthcheck:
test: "redis-cli ping"
interval: 5s
retries: 20
db:
restart: always
image: postgres:15-alpine
networks:
- internal_network
env_file:
- .config/docker.env
volumes:
- ./db:/var/lib/postgresql/data
healthcheck:
test: "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"
interval: 5s
retries: 20
# es:
# restart: always
# image: docker.elastic.co/elasticsearch/elasticsearch-oss:6.4.2
# environment:
# - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
# - "TAKE_FILE_OWNERSHIP=111"
# networks:
# - internal_network
# volumes:
# - ./elasticsearch:/usr/share/elasticsearch/data
networks:
internal_network:
internal: true
external_network:

25
docs/DONATORS.md Normal file
View File

@@ -0,0 +1,25 @@
DONATORS
========
The list of people who have sent donation for Misskey.
(In random order, honorific titles are omitted.)
* らãĩぁ
* äŋēæ§˜
* ãĒぎうり
* ゚ãƒĢãƒĄ https://surume.tk/
* 藍
* éŸŗčˆš https://otofune.me/
* aqz https://misskey.xyz/aqz
* kotodu "č™šį„Ąå‰ĩäŊœä¸­"
* Maya Minatsuki
* Knzk https://knzk.me/@Knzk
* ã­ã˜ã‚Šã‚ã•ãŗ https://knzk.me/@y
* NCLS https://knzk.me/@imncls]
* こじぞ @skoji@sandbox.skoji.jp
:heart: Thanks for donating, guys!
---
If your name is missing, please contact us!

65
gulpfile.js Normal file
View File

@@ -0,0 +1,65 @@
/**
* Gulp tasks
*/
const fs = require('fs');
const gulp = require('gulp');
const replace = require('gulp-replace');
const terser = require('gulp-terser');
const cssnano = require('gulp-cssnano');
const locales = require('./locales');
const meta = require('./package.json');
gulp.task('copy:backend:views', () =>
gulp.src('./packages/backend/src/server/web/views/**/*').pipe(gulp.dest('./packages/backend/built/server/web/views'))
);
gulp.task('copy:frontend:fonts', () =>
gulp.src('./packages/frontend/node_modules/three/examples/fonts/**/*').pipe(gulp.dest('./built/_frontend_dist_/fonts/'))
);
gulp.task('copy:frontend:tabler-icons', () =>
gulp.src('./packages/frontend/node_modules/@tabler/icons-webfont/**/*').pipe(gulp.dest('./built/_frontend_dist_/tabler-icons/'))
);
gulp.task('copy:frontend:locales', cb => {
fs.mkdirSync('./built/_frontend_dist_/locales', { recursive: true });
const v = { '_version_': meta.version };
for (const [lang, locale] of Object.entries(locales)) {
fs.writeFileSync(`./built/_frontend_dist_/locales/${lang}.${meta.version}.json`, JSON.stringify({ ...locale, ...v }), 'utf-8');
}
cb();
});
gulp.task('build:backend:script', () => {
return gulp.src(['./packages/backend/src/server/web/boot.js', './packages/backend/src/server/web/bios.js', './packages/backend/src/server/web/cli.js'])
.pipe(replace('LANGS', JSON.stringify(Object.keys(locales))))
.pipe(terser({
toplevel: true
}))
.pipe(gulp.dest('./packages/backend/built/server/web/'));
});
gulp.task('build:backend:style', () => {
return gulp.src(['./packages/backend/src/server/web/style.css', './packages/backend/src/server/web/bios.css', './packages/backend/src/server/web/cli.css'])
.pipe(cssnano({
zindex: false
}))
.pipe(gulp.dest('./packages/backend/built/server/web/'));
});
gulp.task('build', gulp.parallel(
'copy:frontend:locales', 'copy:backend:views', 'build:backend:script', 'build:backend:style', 'copy:frontend:fonts', 'copy:frontend:tabler-icons'
));
gulp.task('default', gulp.task('build'));
gulp.task('watch', () => {
gulp.watch([
'./packages/*/src/**/*',
], { ignoreInitial: false }, gulp.task('build'));
});

View File

@@ -1,7 +1,4 @@
#!/bin/bash
# SPDX-FileCopyrightText: syuilo and misskey-project
# SPDX-License-Identifier: AGPL-3.0-only
PORT=$(grep '^port:' /misskey/.config/default.yml | awk 'NR==1{print $2; exit}')
curl -Sfso/dev/null "http://localhost:${PORT}/healthz"
curl -s -S -o /dev/null "http://localhost:${PORT}"

View File

@@ -1,54 +0,0 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
/* eslint-disable @typescript-eslint/explicit-function-return-type */
import { action } from 'storybook/actions';
import type { StoryObj } from '@storybook/vue3';
import { HttpResponse, http } from 'msw';
import { abuseUserReport } from '../packages/frontend/.storybook/fakes.js';
import { commonHandlers } from '../packages/frontend/.storybook/mocks.js';
import MkAbuseReport from './MkAbuseReport.vue';
export const Default = {
render(args) {
return {
components: {
MkAbuseReport,
},
setup() {
return {
args,
};
},
computed: {
props() {
return {
...this.args,
};
},
events() {
return {
resolved: action('resolved'),
};
},
},
template: '<MkAbuseReport v-bind="props" v-on="events" />',
};
},
args: {
report: abuseUserReport(),
},
parameters: {
layout: 'fullscreen',
msw: {
handlers: [
...commonHandlers,
http.post('/api/admin/resolve-abuse-user-report', async ({ request }) => {
action('POST /api/admin/resolve-abuse-user-report')(await request.json());
return HttpResponse.json({});
}),
],
},
},
} satisfies StoryObj<typeof MkAbuseReport>;

View File

@@ -1,232 +0,0 @@
<!--
SPDX-FileCopyrightText: syuilo and misskey-project
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<canvas ref="canvasEl" style="display: block; width: 100%; height: 100%; pointer-events: none;"></canvas>
</template>
<script lang="ts" setup>
import { onMounted, onUnmounted, useTemplateRef } from 'vue';
import isChromatic from 'chromatic/isChromatic';
import { initShaderProgram } from '@/utility/webgl.js';
const VERTEX_SHADER = `#version 300 es
in vec2 position;
out vec2 in_uv;
void main() {
in_uv = (position + 1.0) / 2.0;
gl_Position = vec4(position, 0.0, 1.0);
}
`;
const FRAGMENT_SHADER = `#version 300 es
precision mediump float;
const float PI = 3.141592653589793;
const float TWO_PI = 6.283185307179586;
const float HALF_PI = 1.5707963267948966;
in vec2 in_uv;
uniform vec2 in_resolution;
uniform float u_scale;
uniform float u_time;
uniform float u_seed;
uniform float u_angle;
uniform float u_radius;
uniform vec3 u_color;
uniform vec2 u_ripplePositions[16];
uniform float u_rippleRadiuses[16];
out vec4 out_color;
float getRipple(vec2 uv) {
float strength = 0.0;
float thickness = 0.05;
for (int i = 0; i < 16; i++) {
if (u_rippleRadiuses[i] <= 0.0) continue;
float d = distance(uv, u_ripplePositions[i]);
// フチ
if (d < u_rippleRadiuses[i] + thickness && d > u_rippleRadiuses[i] - thickness) {
float gradate = abs(d - u_rippleRadiuses[i] + thickness) / thickness;
strength += (1.0 - u_rippleRadiuses[i]) * gradate;
}
// 内側
if (d < u_rippleRadiuses[i] + thickness) {
strength += 0.25 * (1.0 - u_rippleRadiuses[i]);
}
}
return strength;
}
void main() {
float x_ratio = min(in_resolution.x / in_resolution.y, 1.0);
float y_ratio = min(in_resolution.y / in_resolution.x, 1.0);
float angle = -(u_angle * PI);
vec2 centeredUv = (in_uv - vec2(0.5, 0.5)) * vec2(x_ratio, y_ratio);
vec2 rotatedUV = vec2(
centeredUv.x * cos(angle) - centeredUv.y * sin(angle),
centeredUv.x * sin(angle) + centeredUv.y * cos(angle)
);
vec2 uv = rotatedUV;
float time = u_time * 0.00025;
float size = 1.0 / u_scale;
float size_half = size / 2.0;
float modX = mod(uv.x, size);
float modY = mod(uv.y, size);
vec2 pixelated_uv = vec2(
(size * (floor((uv.x - 0.5 - size) / size) + 0.5)),
(size * (floor((uv.y - 0.5 - size) / size) + 0.5))
) + vec2(0.5 + size, 0.5 + size);
float strength = getRipple(pixelated_uv);
float opacity = min(max(strength, 0.0), 1.0);
float threshold = ((u_radius / 2.0) / u_scale);
if (length(vec2(modX - size_half, modY - size_half)) < threshold) {
out_color = vec4(u_color.r, u_color.g, u_color.b, opacity);
//out_color = vec4(1.0);
return;
}
// debug
//float a = min(max(getRipple(uv), 0.0), 1.0);
//out_color = vec4(u_color.r, u_color.g, u_color.b, (opacity + a) / 2.0);
out_color = vec4(0.0, 0.0, 0.0, 0.0);
}
`;
const canvasEl = useTemplateRef('canvasEl');
const props = withDefaults(defineProps<{
scale?: number;
}>(), {
scale: 48,
});
let handle: ReturnType<typeof window['requestAnimationFrame']> | null = null;
onMounted(() => {
const canvas = canvasEl.value!;
let width = canvas.offsetWidth;
let height = canvas.offsetHeight;
canvas.width = width;
canvas.height = height;
const maybeGl = canvas.getContext('webgl2', { preserveDrawingBuffer: false, alpha: true, premultipliedAlpha: false, antialias: true });
if (maybeGl == null) return;
const gl = maybeGl;
const VERTICES = new Float32Array([-1, -1, -1, 1, 1, 1, -1, -1, 1, 1, 1, -1]);
const vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, VERTICES, gl.STATIC_DRAW);
//gl.clearColor(0.0, 0.0, 0.0, 0.0);
//gl.clear(gl.COLOR_BUFFER_BIT);
const shaderProgram = initShaderProgram(gl, VERTEX_SHADER, FRAGMENT_SHADER);
gl.useProgram(shaderProgram);
const positionLocation = gl.getAttribLocation(shaderProgram, 'position');
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(positionLocation);
const in_resolution = gl.getUniformLocation(shaderProgram, 'in_resolution');
gl.uniform2fv(in_resolution, [canvas.width, canvas.height]);
const u_time = gl.getUniformLocation(shaderProgram, 'u_time');
const u_seed = gl.getUniformLocation(shaderProgram, 'u_seed');
const u_scale = gl.getUniformLocation(shaderProgram, 'u_scale');
const u_angle = gl.getUniformLocation(shaderProgram, 'u_angle');
const u_radius = gl.getUniformLocation(shaderProgram, 'u_radius');
const u_color = gl.getUniformLocation(shaderProgram, 'u_color');
gl.uniform1f(u_seed, Math.random() * 1000);
gl.uniform1f(u_scale, props.scale);
gl.uniform1f(u_angle, 0.0);
gl.uniform1f(u_radius, 0.15);
gl.uniform3fv(u_color, [0.5, 1.0, 0]);
if (isChromatic()) {
gl.uniform1f(u_time, 0);
gl.drawArrays(gl.TRIANGLES, 0, 6);
} else {
let ripples = [] as { position: [number, number]; startTime: number; }[];
const LIFE_TIME = 1000 * 4;
function render(timeStamp: number) {
let sizeChanged = false;
if (Math.abs(height - canvas.offsetHeight) > 2) {
height = canvas.offsetHeight;
canvas.height = height;
sizeChanged = true;
}
if (Math.abs(width - canvas.offsetWidth) > 2) {
width = canvas.offsetWidth;
canvas.width = width;
sizeChanged = true;
}
if (sizeChanged && gl) {
gl.uniform2fv(in_resolution, [width, height]);
gl.viewport(0, 0, width, height);
}
gl.uniform1f(u_time, timeStamp);
if (Math.random() < 0.01 && ripples.length < 16) {
ripples.push({ position: [(Math.random() * 2) - 1, (Math.random() * 2) - 1], startTime: timeStamp });
}
for (let i = 0; i < 16; i++) {
const o = gl.getUniformLocation(shaderProgram, `u_ripplePositions[${i.toString()}]`);
const r = gl.getUniformLocation(shaderProgram, `u_rippleRadiuses[${i.toString()}]`);
const ripple = ripples[i];
if (ripple == null) {
gl.uniform2f(o, 0, 0);
gl.uniform1f(r, 0.0);
continue;
}
const delta = timeStamp - ripple.startTime;
gl.uniform2f(o, ripple.position[0], ripple.position[1]);
gl.uniform1f(r, delta / LIFE_TIME);
}
ripples = ripples.filter(r => (timeStamp - r.startTime) < LIFE_TIME);
if (ripples.length === 0) {
ripples.push({ position: [(Math.random() * 2) - 1, (Math.random() * 2) - 1], startTime: timeStamp });
}
gl.drawArrays(gl.TRIANGLES, 0, 6);
handle = window.requestAnimationFrame(render);
}
handle = window.requestAnimationFrame(render);
}
});
onUnmounted(() => {
if (handle) {
window.cancelAnimationFrame(handle);
}
// TODO: WebGLãƒĒã‚Ŋãƒŧã‚šãŽč§Ŗæ”ž
});
</script>
<style lang="scss" module>
</style>

View File

@@ -1,190 +0,0 @@
<!--
SPDX-FileCopyrightText: syuilo and misskey-project
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<canvas ref="canvasEl" style="display: block; width: 100%; height: 100%; pointer-events: none;"></canvas>
</template>
<script lang="ts" setup>
import { onMounted, onUnmounted, useTemplateRef } from 'vue';
import isChromatic from 'chromatic/isChromatic';
import { GLSL_LIB_SNOISE, initShaderProgram } from '@/utility/webgl.js';
const VERTEX_SHADER = `#version 300 es
in vec2 position;
out vec2 in_uv;
void main() {
in_uv = (position + 1.0) / 2.0;
gl_Position = vec4(position, 0.0, 1.0);
}
`;
const FRAGMENT_SHADER = `#version 300 es
precision mediump float;
const float PI = 3.141592653589793;
const float TWO_PI = 6.283185307179586;
const float HALF_PI = 1.5707963267948966;
${GLSL_LIB_SNOISE}
in vec2 in_uv;
uniform vec2 in_resolution;
uniform float u_scale;
uniform float u_time;
uniform float u_seed;
uniform float u_angle;
uniform float u_radius;
uniform vec3 u_color;
out vec4 out_color;
void main() {
float x_ratio = min(in_resolution.x / in_resolution.y, 1.0);
float y_ratio = min(in_resolution.y / in_resolution.x, 1.0);
float size = 1.0 / u_scale;
float size_half = size / 2.0;
float angle = -(u_angle * PI);
vec2 centeredUv = (in_uv - vec2(0.5, 0.5)) * vec2(x_ratio, y_ratio);
vec2 rotatedUV = vec2(
centeredUv.x * cos(angle) - centeredUv.y * sin(angle),
centeredUv.x * sin(angle) + centeredUv.y * cos(angle)
);
vec2 uv = rotatedUV;
float modX = mod(uv.x, size);
float modY = mod(uv.y, size);
vec2 pixelated_uv = vec2(
(size * (floor((uv.x - 0.5 - size) / size) + 0.5)),
(size * (floor((uv.y - 0.5 - size) / size) + 0.5))
) + vec2(0.5 + size, 0.5 + size);
float time = u_time * 0.00025;
float noiseAScale = 1.0;
float noiseAX = (pixelated_uv.x + u_seed) * (u_scale / noiseAScale);
float noiseAY = (pixelated_uv.y + u_seed) * (u_scale / noiseAScale);
float noiseA = snoise(vec3(noiseAX, noiseAY, time * 2.0));
float noiseBScale = 32.0;
float noiseBX = (pixelated_uv.x + u_seed) * (u_scale / noiseBScale);
float noiseBY = (pixelated_uv.y + u_seed) * (u_scale / noiseBScale);
float noiseB = snoise(vec3(noiseBX, noiseBY, time));
float strength = 0.0;
strength += noiseA * 0.2;
strength += noiseB * 0.8;
float opacity = min(max(strength, 0.0), 1.0);
float threshold = ((u_radius / 2.0) / u_scale);
if (length(vec2(modX - size_half, modY - size_half)) < threshold) {
out_color = vec4(u_color.r, u_color.g, u_color.b, opacity);
return;
}
out_color = vec4(0.0, 0.0, 0.0, 0.0);
}
`;
const canvasEl = useTemplateRef('canvasEl');
const props = withDefaults(defineProps<{
scale?: number;
}>(), {
scale: 48,
});
let handle: ReturnType<typeof window['requestAnimationFrame']> | null = null;
onMounted(() => {
const canvas = canvasEl.value!;
let width = canvas.offsetWidth;
let height = canvas.offsetHeight;
canvas.width = width;
canvas.height = height;
const maybeGl = canvas.getContext('webgl2', { preserveDrawingBuffer: false, alpha: true, premultipliedAlpha: false, antialias: true });
if (maybeGl == null) return;
const gl = maybeGl;
const VERTICES = new Float32Array([-1, -1, -1, 1, 1, 1, -1, -1, 1, 1, 1, -1]);
const vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, VERTICES, gl.STATIC_DRAW);
//gl.clearColor(0.0, 0.0, 0.0, 0.0);
//gl.clear(gl.COLOR_BUFFER_BIT);
const shaderProgram = initShaderProgram(gl, VERTEX_SHADER, FRAGMENT_SHADER);
gl.useProgram(shaderProgram);
const positionLocation = gl.getAttribLocation(shaderProgram, 'position');
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(positionLocation);
const in_resolution = gl.getUniformLocation(shaderProgram, 'in_resolution');
gl.uniform2fv(in_resolution, [canvas.width, canvas.height]);
const u_time = gl.getUniformLocation(shaderProgram, 'u_time');
const u_seed = gl.getUniformLocation(shaderProgram, 'u_seed');
const u_scale = gl.getUniformLocation(shaderProgram, 'u_scale');
const u_angle = gl.getUniformLocation(shaderProgram, 'u_angle');
const u_radius = gl.getUniformLocation(shaderProgram, 'u_radius');
const u_color = gl.getUniformLocation(shaderProgram, 'u_color');
gl.uniform1f(u_seed, Math.random() * 1000);
gl.uniform1f(u_scale, props.scale);
gl.uniform1f(u_angle, 0.0);
gl.uniform1f(u_radius, 0.15);
gl.uniform3fv(u_color, [0.5, 1.0, 0]);
if (isChromatic()) {
gl.uniform1f(u_time, 0);
gl.drawArrays(gl.TRIANGLES, 0, 6);
} else {
function render(timeStamp: number) {
let sizeChanged = false;
if (Math.abs(height - canvas.offsetHeight) > 2) {
height = canvas.offsetHeight;
canvas.height = height;
sizeChanged = true;
}
if (Math.abs(width - canvas.offsetWidth) > 2) {
width = canvas.offsetWidth;
canvas.width = width;
sizeChanged = true;
}
if (sizeChanged && gl) {
gl.uniform2fv(in_resolution, [width, height]);
gl.viewport(0, 0, width, height);
}
gl.uniform1f(u_time, timeStamp);
gl.drawArrays(gl.TRIANGLES, 0, 6);
handle = window.requestAnimationFrame(render);
}
handle = window.requestAnimationFrame(render);
}
});
onUnmounted(() => {
if (handle) {
window.cancelAnimationFrame(handle);
}
// TODO: WebGLãƒĒã‚Ŋãƒŧã‚šãŽč§Ŗæ”ž
});
</script>
<style lang="scss" module>
</style>

View File

@@ -1 +0,0 @@
äŊŋわれãĒくãĒãŖãŸã‘ãŠæļˆã™ãŽã¯å‹ŋäŊ“ãĒい(将æĨäŊŋえるかもしれãĒい)ã‚ŗãƒŧドをå…ĨれãĻおくとこ

View File

@@ -2,7 +2,6 @@
_lang_: "Ø§Ų„ØšØąØ¨ŲŠØŠ"
headlineMisskey: "Ø´Ø¨ŲƒØŠ Ų…ØąØĒØ¨ØˇØŠ Ø¨Ø§Ų„Ų…Ų„Ø§Ø­Ø¸Ø§ØĒ"
introMisskey: "Ø§Ų‡Ų„Ø§ Ø¨Ųƒ! Ų…ŲŠØŗŲƒŲŠ Ų‡Ųˆ Ų…Ų†ØĩØŠ ØĒØ¯ŲˆŲŠŲ† Ų…ØĩØēØą Ų„Ø§ Ų…ØąŲƒØ˛ŲŠØŠ ŲˆŲ…ŲØĒŲˆØ­ØŠ Ø§Ų„Ų…ØĩØ¯Øą.\nŲŠŲ…ŲƒŲ†Ųƒ Ų…Ø´Ø§ØąŲƒØŠ \"Ų…Ų„Ø§Ø­Ø¸Ø§ØĒ\" ØšŲ† Ų…Ø§ ؊ØŦØąŲŠ Ø­ŲˆŲ„ŲƒØŒ ؈ØĨØŽØ¨Ø§Øą Ø§Ų„ØŦŲ…ŲŠØš ØšŲ† Ų†ŲØŗŲƒ 📡\nØĒØŗŲ…Ø­ Ų„Ųƒ \"Ø§Ų„Ø§Ų†ŲØšØ§Ų„Ø§ØĒ\" بØĒØšØ¨ŲŠØą ØšŲ† Ø´ØšŲˆØąŲƒ Ø­ŲˆŲ„ Ų…Ų„Ø§Ø­Ø¸Ø§ØĒ Ø§Ų„ØĸØŽØąŲŠŲ† 👍\nØ§ŲƒØĒØ´Ų ØšØ§Ų„Ų…Ų‹Ø§ ØŦØ¯ŲŠØ¯Ų‹Ø§ 🚀"
poweredByMisskeyDescription: "{name} Ų‡Ųˆ ØĨØ­Ø¯Ų‰ Ø§Ų„ØŽŲØ¯Ų…Ø§ØĒ Ø§Ų„ØĒ؊ ØĒØŗØĒØŽØ¯Ų… Ø§Ų„Ų…Ų†ØĩØŠ ؅؁ØĒŲˆØ­ØŠ Ø§Ų„Ų…ØĩØ¯Øą <b>Ų…ŲŠØŗŲƒŲŠ</b> (ŲŠØ´Ø§Øą ØĨŲ„ŲŠŲ‡ ŲƒŲ…ØĢŲŠŲ„ Ų…ŲŠØŗŲƒŲŠ)"
monthAndDay: "{day}/{month}"
search: "Ø§Ų„Ø¨Ø­ØĢ"
notifications: "Ø§Ų„ØĨØ´ØšØ§ØąØ§ØĒ"
@@ -20,7 +19,6 @@ noNotes: "Ų„Ų… ŲŠŲØšØĢØą ØšŲ„Ų‰ ØŖŲŠØŠ Ų…Ų„Ø§Ø­Ø¸Ø§ØĒ"
noNotifications: "Ų„ŲŠØŗ Ų‡Ų†Ø§Ųƒ ØŖŲŠØŠ Ø§Ø´ØšØ§ØąØ§ØĒ"
instance: "Ų…ØĢŲŠŲ„ Ø§Ų„ØŽØ§Ø¯Ų…"
settings: "Ø§Ų„Ø§ØšØ¯Ø§Ø¯Ø§ØĒ"
notificationSettings: "ØĨؚداداØĒ Ø§Ų„ØĨØ´ØšØ§ØąØ§ØĒ"
basicSettings: "Ø§Ų„Ø§ØšØ¯Ø§Ø¯Ø§ØĒ Ø§Ų„ØŖØŗØ§ØŗŲŠØŠ"
otherSettings: "ØĨؚداداØĒ ØŖØŽØąŲ‰"
openInWindow: "Ø§ŲØĒØ­ ؁؊ Ų†Ø§ŲØ°ØŠ ØŦØ¯ŲŠØ¯ØŠ"
@@ -41,23 +39,16 @@ unfavorite: "ØĨØ˛Ø§Ų„ØŠ Ų…Ų† Ø§Ų„Ų…ŲØļŲ„ØŠ"
favorited: "ØŖŲØļ؊؁ ØĨŲ„Ų‰ Ø§Ų„Ų…ŲØļŲ„ØŠ."
alreadyFavorited: "ØĒŲ…ØĒ ØĨØļØ§ŲØĒŲ‡ Ø¨Ø§Ų„ŲØšŲ„ ØĨŲ„Ų‰ Ø§Ų„Ų…ŲØļŲ„ØŠ."
cantFavorite: "ØĒØšØ°ØąØĒ Ø§Ų„ØĨØļØ§ŲØŠ ØĨŲ„Ų‰ Ø§Ų„Ų…ŲØļŲ„ØŠ."
pin: "ØĢبØĒŲ‡Ø§ ØšŲ„Ų‰ Ø§Ų„ØĩŲØ­ØŠ Ø§Ų„Ø´ØŽØĩŲŠØŠ"
unpin: "ŲŲƒŲ‡Ø§ Ų…Ų† Ų…Ų„ŲŲƒ Ø§Ų„Ø´ØŽØĩ؊"
pin: "Ø¯Ø¨Ų‘ØŗŲ‡Ø§ ØšŲ„Ų‰ Ø§Ų„ØĩŲØ­ØŠ Ø§Ų„Ø´ØŽØĩŲŠØŠ"
unpin: "ØŖŲ„Øē ØĒØ¯Ø¨ŲŠØŗŲ‡Ø§ Ų…Ų† Ų…Ų„ŲŲƒ Ø§Ų„Ø´ØŽØĩ؊"
copyContent: "Ø§Ų†ØŗØŽ Ø§Ų„Ų…Ø­ØĒŲˆŲ‰"
copyLink: "Ø§Ų†ØŗØŽ Ø§Ų„ØąØ§Ø¨Øˇ"
delete: "Ø­Ø°Ų"
deleteAndEdit: "ØĨØ˛Ø§Ų„ØŠ ؈ØĨؚاد؊ Ø§Ų„ØĩŲŠØ§ØēØŠ"
deleteAndEditConfirm: "ØŖŲ…ØĒØŖŲƒØ¯ Ų…Ų† Ø­Ø°Ų Ø§Ų„Ų…Ų„Ø§Ø­Ø¸ØŠØŸ ØŗØĒŲŲ‚Ø¯ ŲƒŲ„ Ų…Ø´Ø§ØąŲƒØ§ØĒŲ‡Ø§ØŒ ŲˆØ§Ų„ØĒŲØ§ØšŲ„Ø§ØĒ، ŲˆØ§Ų„ØąØ¯ŲˆØ¯ ØšŲ„ŲŠŲ‡Ø§."
addToList: "ØŖØļ؁؇ ØĨŲ„Ų‰ Ų‚Ø§ØĻŲ…ØŠ"
addToAntenna: "ØŖØļ؁ ØĨŲ„Ų‰ Ų‡ŲˆØ§ØĻ؊"
sendMessage: "ØŖØąØŗŲ„ ØąØŗØ§Ų„ØŠ"
copyRSS: "Ø§Ų†ØŗØŽ ØąØ§Ø¨Øˇ RSS"
copyUsername: "Ø§Ų†ØŗØŽ Ø§ØŗŲ… Ø§Ų„Ų…ØŗØĒØŽØ¯Ų…"
copyUserId: "Ø§Ų†ØŗØŽ Ų…ØšØąŲ Ø§Ų„Ų…ØŗØĒØŽØ¯Ų…"
copyNoteId: "Ø§Ų†ØŗØŽ Ų…ØšØąŲ Ø§Ų„Ų…Ų„Ø§Ø­Ø¸ØŠ"
copyFileId: "Ø§Ų†ØŗØŽ Ų…ØšØąŲ‘Ų Ø§Ų„Ų…Ų„Ų"
copyFolderId: "Ø§Ų†ØŗØŽ Ų…ØšØąŲ‘Ų Ø§Ų„Ų…ØŦŲ„Ø¯"
copyProfileUrl: "Ø§Ų†ØŗØŽ ØąØ§Ø¨Øˇ Ø§Ų„Ų…Ų„Ų Ø§Ų„Ø´ØŽØĩ؊"
searchUser: "ابحØĢ ØšŲ† Ų…ØŗØĒØŽØ¯Ų…ŲŠŲ†"
reply: "ØąØ¯"
loadMore: "ØšØąØļ Ø§Ų„Ų…Ø˛ŲŠØ¯"
@@ -68,7 +59,7 @@ receiveFollowRequest: "ØĒŲ„Ų‚ŲŠØĒ ØˇŲ„Ø¨ Ų…ØĒابؚ؊"
followRequestAccepted: "Ų‚ŲØ¨Ų„ ØˇŲ„Ø¨ Ø§Ų„Ų…ØĒابؚ؊"
mention: "ØŖØ´Øą Ø§Ų„Ų‰"
mentions: "Ø§Ų„ØĨØ´Ø§ØąØ§ØĒ"
directNotes: "ØąØŗØ§Ų„ØŠ ؎اØĩØŠ"
directNotes: "Ø§Ų„Ų…Ų„Ø§Ø­Ø¸Ø§ØĒ Ø§Ų„Ų…Ø¨Ø§Ø´ØąØŠ"
importAndExport: "ØĨØŗØĒŲˆØąØ¯ / ØĩØ¯Øą"
import: "Ø§ØŗØĒŲŠØąØ§Ø¯"
export: "ØĒØĩØ¯ŲŠØą"
@@ -110,27 +101,23 @@ renoted: "ØŖŲØšŲŠØ¯ Ų†Ø´ØąŲ‡"
cantRenote: "Ų„Ø§ ŲŠŲ…ŲƒŲ† ØĨؚاد؊ Ų†Ø´Øą Ø§Ų„Ų…Ų„Ø§Ø­Ø¸ØŠ"
cantReRenote: "Ų„Ø§ ŲŠŲ…ŲƒŲ†Ųƒ ØĨؚاد؊ Ų†Ø´Øą Ų…Ų„Ø§Ø­Ø¸ØŠ Ų…ØšØ§Ø¯ Ų†Ø´ØąŲ‡Ø§"
quote: "Ø§Ų‚ØĒØ¨Øŗ"
inChannelRenote: "ØĨؚاد؊ Ų†Ø´Øą ؁؊ Ų‚Ų†Ø§ØŠ"
inChannelQuote: "Ø§Ų‚ØĒØ¨Ø§Øŗ ؁؊ Ų‚Ų†Ø§ØŠ"
pinnedNote: "Ų…Ų„Ø§Ø­Ø¸ØŠ Ų…ØĢبØĒØŠ"
pinned: "ØĢبØĒŲ‡Ø§ ØšŲ„Ų‰ Ø§Ų„ØĩŲØ­ØŠ Ø§Ų„Ø´ØŽØĩŲŠØŠ"
pinnedNote: "Ų…Ų„Ø§Ø­Ø¸ØŠ Ų…Ø¯Ø¨ØŗØŠ"
pinned: "Ø¯Ø¨Ų‘ØŗŲ‡Ø§ ØšŲ„Ų‰ Ø§Ų„ØĩŲØ­ØŠ Ø§Ų„Ø´ØŽØĩŲŠØŠ"
you: "ØŖŲ†ØĒ"
clickToShow: "اØļØēØˇ Ų„Ų„ØšØąØļ"
sensitive: "Ų…Ø­ØĒŲˆŲ‰ Ø­ØŗØ§Øŗ"
add: "ØĨØļØ§ŲØŠ"
reaction: "Ø§Ų„ØĒŲØ§ØšŲ„Ø§ØĒ"
reactions: "Ø§Ų„ØĒŲØ§ØšŲ„Ø§ØĒ"
reactionSetting: "Ø§Ų„ØĒŲØ§ØšŲ„Ø§ØĒ Ø§Ų„Ų…ØąØ§Ø¯ ØšØąØļŲ‡Ø§ ؁؊ Ų…Ų†ØĒŲ‚ŲŠ Ø§Ų„ØĒŲØ§ØšŲ„Ø§ØĒ."
reactionSettingDescription2: "Ø§ØŗØ­Ø¨ Ų„ØĒØąØĒŲŠØ¨ ، Ø§Ų†Ų‚Øą Ų„Ų„Ø­Ø°Ų ، Ø§ØŗØĒØŽØ¯Ų… \"+\" Ų„Ų„ØĨØļØ§ŲØŠ."
rememberNoteVisibility: "ØĒØ°ŲƒØą ØĨؚدادØĒ Ų…Ø¯Ų‰ ØąØ¤ŲŠØŠ Ø§Ų„Ų…Ų„Ø§Ø­Ø¸Ø§ØĒ"
attachCancel: "ØŖØ˛Ų„ Ø§Ų„Ų…ØąŲŲ‚"
deleteFile: "Ø­ŲØ°Ų Ø§Ų„Ų…Ų„Ų"
markAsSensitive: "ØšŲ„Ų‘Ų…Ų‡ ŲƒŲ…Ø­ØĒŲˆŲ‰ Ø­ØŗØ§Øŗ"
unmarkAsSensitive: "ØŖŲ„Øē ØĒØšŲŠŲŠŲ†Ų‡ ŲƒŲ…Ø­ØĒŲˆŲ‰ Ø­ØŗØ§Øŗ"
enterFileName: "Ø§Ø¯ØŽŲ„ Ø§ØŗŲ… Ø§Ų„Ų…Ų„Ų"
mute: "Ø§ŲƒØĒŲ…"
unmute: "ØĨŲ„ØēØ§ØĄ Ø§Ų„ŲƒØĒŲ…"
renoteMute: "Ø§ŲƒØĒŲ… ØĨؚاد؊ Ø§Ų„Ų†Ø´Øą"
renoteUnmute: "Ø§ØąŲØš Ø§Ų„ŲƒØĒŲ… ØšŲ† ØĨؚاد؊ Ø§Ų„Ų†Ø´Øą"
block: "احØŦب"
unblock: "ØĨŲ„ØēØ§ØĄ Ø§Ų„Ø­ØŦب"
suspend: "ØšŲ„ŲŲ‚"
@@ -140,10 +127,7 @@ unblockConfirm: "ØŖŲ…ØĒØŖŲƒØ¯ Ų…Ų† ØĨŲ„ØēØ§ØĄ Ø­ØŦب Ų‡Ø°Ø§ Ø§Ų„Ø­ØŗØ§Ø¨ØŸ"
suspendConfirm: "ØŖŲ…ØĒØŖŲƒØ¯ Ų…Ų† ØĒØšŲ„ŲŠŲ‚ Ø§Ų„Ø­ØŗØ§Ø¨ØŸ"
unsuspendConfirm: "ØŖŲ…ØĒØŖŲƒØ¯ Ų…Ų† ØĨŲ„ØēØ§ØĄ ØĒØšŲ„ŲŠŲ‚ØŸ"
selectList: "ا؎ØĒØą Ų‚Ø§ØĻŲ…ØŠ"
editList: "ØšØ¯Ų‘Ų„ Ø§Ų„Ų‚Ø§ØĻŲ…ØŠ"
selectChannel: "ا؎ØĒØą Ų‚Ų†Ø§ØŠ"
selectAntenna: "ا؎ØĒØą Ų‡ŲˆØ§ØĻŲŠŲ‹Ø§"
editAntenna: "ØšØ¯Ų‘Ų„ Ø§Ų„Ų‡ŲˆØ§ØĻ؊"
selectWidget: "ا؎ØĒØą ŲˆØ¯ØŦØŠ"
editWidgets: "ØšØ¯Ų‘Ų„ Ø§Ų„ŲˆØ¯ØŦاØĒ"
editWidgetsExit: "ØĒŲ…"
@@ -155,7 +139,6 @@ emojiUrl: "ØąØ§Ø¨Øˇ Ø§Ų„ØĨŲŠŲ…ŲˆØŦ؊"
addEmoji: "ØĨØļØ§ŲØŠ ØĨŲŠŲ…ŲˆØŦ؊"
settingGuide: "Ø§Ų„ØĨؚداداØĒ Ø§Ų„Ų…ØŗØĒØ­ØŗŲ†ØŠ"
cacheRemoteFiles: "ØŽØ˛Ų† Ų…Ø¤Ų‚ØĒا Ø§Ų„Ų…Ų„ŲØ§ØĒ Ø§Ų„Ø¨ØšŲŠØ¯ØŠ"
cacheRemoteFilesDescription: "ØĨذا ØšŲØˇŲ„ Ų‡Ø°Ø§ Ø§Ų„ØĨؚداد، ØŗØĒŲØ­Ų…Ų„ Ø§Ų„Ų…Ų„ŲØ§ØĒ Ų…Ų† Ø§Ų„Ų…ØĢŲŠŲ„ Ø§Ų„Ø¨ØšŲŠØ¯ØŒ Ų‡Ø°Ø§ ØŗŲŠŲ‚Ų„Ų„ Ų…Ų† Ø§Ų„Ų…ØŗØ§Ø­ØŠ Ø§Ų„Ų…ØŗØĒØēŲ„ØŠ ØšŲ„Ų‰ Ø§Ų„Ų‚ØąØĩ Ų„ŲƒŲ† ØŗŲŠØ˛ŲŠØ¯ Ø­ØŦŲ… ØĒØ¯ŲŲ‚ Ø§Ų„Ø¨ŲŠØ§Ų†Ø§ØĒ ŲˆŲ‡Ø°Ø§ Ų„ØŖŲ† Ø§Ų„ØĩŲˆØą Ø§Ų„Ų…ØĩØēØąØŠ Ų„Ų† ØĒŲˆŲ„Ų‘Ø¯."
flagAsBot: "ØšŲ„Ų‘Ų…Ų‡ ŲƒØ­ØŗØ§Ø¨ ØĸŲ„ŲŠ"
flagAsBotDescription: "ŲØšŲ‘Ų„ Ų‡Ø°Ø§ Ø§Ų„ØŽŲŠØ§Øą ØĨذا ŲƒØ§Ų† Ų‡Ø°Ø§ Ø§Ų„Ø­ØŗØ§Ø¨ ŲŠŲØ¯Ø§Øą ØšØ¨Øą Ø¨ØąŲ…ØŦŲŠØŠ. ØĨذا ŲŲØšŲ„ ŲØŗŲŠŲƒŲˆŲ† Ø¨Ų…ØĢاب؊ ØšŲ„Ø§Ų…ØŠ Ų„Ų„Ų…ØˇŲˆØąŲŠŲ† Ø§Ų„ØĸØŽØąŲŠŲ† Ų„ØĒØŦŲ†Ø¨ ØŗŲ„Ø§ØŗŲ„ Ų„Ø§ Ų…ØĒŲ†Ø§Ų‡ŲŠØŠ Ų…Ų† Ø§Ų„ØĒŲØ§ØšŲ„ Ø¨ŲŠŲ† Ø­ØŗØ§Ø¨Ø§ØĒ Ø§Ų„ØĸŲ„ŲŠØŠ ؈ØļØ¨Øˇ ØŖŲ†Ø¸Ų…ØŠ Ų…ŲŠØŗŲƒŲŠ Ų„Ų„ØĒØšØ§Ų…Ų„ Ų…Øš Ų‡Ø°Ø§ Ø§Ų„Ø­ØŗØ§Ø¨ ؃ØĸŲ„ŲŠ."
flagAsCat: "ØšŲ„Ų‘Ų… Ų‡Ø°Ø§ Ø§Ų„Ø­ØŗØ§Ø¨ ŲƒØ­ØŗØ§Ø¨ Ų‚Øˇ"
@@ -214,7 +197,8 @@ blockedUsers: "Ø§Ų„Ø­ØŗØ§Ø¨Ø§ØĒ Ø§Ų„Ų…Ø­ØŦŲˆØ¨ØŠ"
noUsers: "Ų„ŲŠØŗ Ų‡Ų†Ø§Ųƒ Ų…ØŗØĒØŽØ¯Ų…ŲˆŲ†"
editProfile: "ØĒØšØ¯ŲŠŲ„ Ø§Ų„Ų…Ų„Ų Ø§Ų„ØĒØšØąŲŠŲŲŠ"
noteDeleteConfirm: "Ų‡Ų„ ØĒØąŲŠØ¯ Ø­Ø°Ų Ų‡Ø°Ų‡ Ø§Ų„Ų…Ų„Ø§Ø­Ø¸ØŠØŸ"
pinLimitExceeded: "Ų„Ø§ ŲŠŲ…ŲƒŲ†Ųƒ ØĒØĢØ¨ŲŠØĒ Ø§Ų„Ų…Ų„Ø§Ø­Ø¸Ø§ØĒ بؚد Ø§Ų„ØĸŲ†."
pinLimitExceeded: "Ų„Ø§ ŲŠŲ…ŲƒŲ†Ųƒ ØĒØ¯Ø¨ŲŠØŗ Ø§Ų„Ų…Ų„Ø§Ø­Ø¸Ø§ØĒ بؚد Ø§Ų„ØĸŲ†."
intro: "Ų„Ų‚Ø¯ Ø§Ų†ØĒŲ‡ØĒ ØšŲ…Ų„ŲŠØŠ ØĒŲ†ØĩŲŠØ¨ Misskey. Ø§Ų„ØąØŦØ§ØĄ ØĨŲ†Ø´Ø§ØĄ Ø­ØŗØ§Ø¨ ØĨØ¯Ø§ØąŲŠ."
done: "ØĒŲ…Ų‘"
processing: "Ø§Ų„Ų…ØšØ§Ų„ØŦØŠ ØŦØ§ØąŲŠØŠ"
preview: "Ų…ØšØ§ŲŠŲ†ØŠ"
@@ -250,6 +234,7 @@ removeAreYouSure: "Ų…ØĒØŖŲƒØ¯ Ų…Ų† ØŖŲ†Ųƒ ØĒØąŲŠØ¯ Ø­Ø°Ų {x}؟"
deleteAreYouSure: "Ų…ØĒØŖŲƒØ¯ Ų…Ų† ØŖŲ†Ųƒ ØĒØąŲŠØ¯ Ø­Ø°Ų {x}؟"
resetAreYouSure: "Ų‡Ų„ ØĒØąŲŠØ¯ ØĨؚاد؊ Ø§Ų„ØĒØšŲŠŲŠŲ†ØŸ"
saved: "Ø­ŲŲØ¸"
messaging: "Ø§Ų„Ų…Ø­Ø§Ø¯ØĢØŠ"
upload: "Ø§ØąŲØš"
keepOriginalUploading: "Ø§Ø¨Ų‚ Ø§Ų„ØĩŲˆØąØŠ Ø§Ų„ØŖØĩŲ„ŲŠØŠ"
keepOriginalUploadingDescription: "ŲŠØ­ŲØ¸ Ø§Ų„ØĩŲˆØą Ø§Ų„Ų…ØąŲŲˆØšØŠ ØšŲ„Ų‰ Ø­Ø§Ų„ØĒŲ‡Ø§ Ø§Ų„ØŖØĩŲ„ŲŠØŠØŒ ŲˆØ§Ų† ØšØˇŲ‘Ų„ ØŗØĒŲˆŲ„Ø¯ Ų†ØŗØŽØŠ Ų…ØŽØĩØĩØŠ Ų…Ų† Ø§Ų„ØĩŲˆØąØŠ."
@@ -262,18 +247,15 @@ uploadFromUrlMayTakeTime: "ØŗŲŠØŗØĒØēØąŲ‚ بؚØļ Ø§Ų„ŲˆŲ‚ØĒ Ų„Ø§ØĒŲ…Ø§Ų… Ø§Ų„Øą
explore: "Ø§ØŗØĒŲƒØ´Ø§Ų"
messageRead: "Ų…Ų‚ØąŲˆØĄØŠ"
noMoreHistory: "Ų„Ø§ ؊؈ØŦد Ø§Ų„Ų…Ø˛ŲŠØ¯ Ų…Ų† Ø§Ų„ØĒØ§ØąŲŠØŽ"
startMessaging: "Ø§Ø¨Ø¯ØŖ Ų…Ø­Ø§Ø¯ØĢØŠ"
nUsersRead: "Ų‚ØąØŖŲ‡ {n}"
agreeTo: "Ø§ŲˆØ§ŲŲ‚ ØšŲ„Ų‰ {0}"
agree: "ØŖŲ‚Ø¨Ų„"
agreeBelow: "ØŖŲ‚Ø¨Ų„ Ų…Ø§ ŲŠŲ„ŲŠ"
basicNotesBeforeCreateAccount: "Ų…Ų„Ø§Ø­Ø¸Ø§ØĒ Ų…Ų‡Ų…ØŠ"
termsOfService: "Ø´ØąŲˆØˇ Ø§Ų„ØŽØ¯Ų…ØŠ"
tos: "Ø´ØąŲˆØˇ Ø§Ų„ØŽØ¯Ų…ØŠ"
start: "Ø§Ų„Ø¨Ø¯Ø§ŲŠØŠ"
home: "Ø§Ų„ØąØĻŲŠØŗŲŠ"
remoteUserCaution: "Ų‡Ø°Ų‡ Ø§Ų„Ų…ØšŲ„ŲˆŲ…Ø§ØĒ Ų‚Ø¯ Ų„Ø§ ØĒŲƒŲˆŲ† Ų…ŲƒØĒŲ…Ų„ØŠ Ø¨Ų…Ø§ ØŖŲ† Ø§Ų„Ų…ØŗØĒØŽØ¯Ų… Ų…Ų† Ų…ØĢŲŠŲ„ Ø¨ØšŲŠØ¯."
activity: "Ø§Ų„Ų†Ø´Ø§Øˇ"
images: "ØĩŲˆØą"
image: "ØĩŲˆØą"
images: "Ø§Ų„ØĩŲˆØą"
birthday: "ØĒØ§ØąŲŠØŽ Ø§Ų„Ų…ŲŠŲ„Ø§Ø¯"
yearsOld: "{age} ØŗŲ†ØŠ"
registeredDate: "Ø§Ų†ØļŲ… ؁؊"
@@ -310,7 +292,7 @@ copyUrl: "Ø§Ų†ØŗØŽ Ø§Ų„ØąØ§Ø¨Øˇ"
rename: "ØĨؚاد؊ Ø§Ų„ØĒØŗŲ…ŲŠØŠ"
avatar: "Ø§Ų„ØĩŲˆØąØŠ Ø§Ų„ØąŲ…Ø˛ŲŠØŠ"
banner: "Ø§Ų„ØĩŲˆØąØŠ Ø§Ų„ØąØŖØŗŲŠØŠ"
displayOfSensitiveMedia: "ØšØąØļ Ø§Ų„Ų…Ø­ØĒŲˆŲ‰ Ø§Ų„Ø­ØŗØ§Øŗ"
nsfw: "Ų…Ø­ØĒŲˆŲ‰ Ø­ØŗØ§Øŗ"
whenServerDisconnected: "ØšŲ†Ø¯ ŲŲ‚Ø¯Ø§Ų† Ø§Ų„Ø§ØĒØĩØ§Ų„ Ø¨Ø§Ų„ØŽØ§Ø¯Ų…"
disconnectedFromServer: "Ų‚ŲØˇŲØš Ø§Ų„ØĨØĒØĩØ§Ų„ Ø¨Ø§Ų„ØŽØ§Ø¯Ų…"
reload: "Ø§Ų†ØšØ´"
@@ -340,25 +322,25 @@ enableLocalTimeline: "ØĒŲØšŲŠŲ„ Ø§Ų„ØŽŲŠØˇ Ø§Ų„Ų…Ø­Ų„ŲŠ"
enableGlobalTimeline: "ØĒŲØšŲŠŲ„ Ø§Ų„ØŽŲŠØˇ Ø§Ų„Ø˛Ų…Ų†ŲŠ Ø§Ų„Ø´Ø§Ų…Ų„"
disablingTimelinesInfo: "ØŗŲŠØĒŲ…ŲƒŲ† Ø§Ų„Ų…Ø¯ŲŠØąŲˆŲ† ŲˆØ§Ų„Ų…Ø´ØąŲŲˆŲ† Ų…Ų† Ø§Ų„ŲˆØĩŲˆŲ„ ØĨŲ„Ų‰ ŲƒŲ„ Ø§Ų„ØŽŲŠŲˆØˇ Ø§Ų„Ø˛Ų…Ų†ŲŠØŠ Ø­ØĒŲ‰ ؈ØĨŲ† Ų„Ų… ØĒŲØšŲ‘Ų„."
registration: "ØĨŲ†Ø´Ø§ØĄ Ø­ØŗØ§Ø¨"
enableRegistration: "ØĒŲØšŲŠŲ„ ØĨŲ†Ø´Ø§ØĄ Ø§Ų„Ø­ØŗØ§Ø¨Ø§ØĒ Ø§Ų„ØŦØ¯ŲŠØ¯ØŠ"
invite: "Ø¯ØšŲˆØŠ"
driveCapacityPerLocalAccount: "Ø­ØĩØŠ Ø§Ų„ØĒØŽØ˛ŲŠŲ† Ų„ŲƒŲ„ Ų…ØŗØĒØŽØ¯Ų… Ų…Ø­Ų„ŲŠ"
driveCapacityPerRemoteAccount: "Ø­ØĩØŠ Ø§Ų„ØĒØŽØ˛ŲŠŲ† Ų„ŲƒŲ„ Ų…ØŗØĒØŽØ¯Ų… Ø¨ØšŲŠØ¯"
inMb: "Ø¨Ø§Ų„Ų…ŲŠØēØ§Ø¨Ø§ŲŠØĒ"
iconUrl: "ØąØ§Ø¨Øˇ Ø§Ų„ØŖŲŠŲ‚ŲˆŲ†ØŠ"
bannerUrl: "ØąØ§Ø¨Øˇ ØĩŲˆØąØŠ Ø§Ų„Ų„Ø§ŲØĒØŠ"
backgroundImageUrl: "ØąØ§Ø¨Øˇ ØĩŲˆØąØŠ Ø§Ų„ØŽŲ„ŲŲŠØŠ"
basicInfo: "Ø§Ų„Ų…ØšŲ„ŲˆŲ…Ø§ØĒ Ø§Ų„ØŖØŗØ§ØŗŲŠØŠ "
pinnedUsers: "Ø§Ų„Ų…ØŗØĒØŽØ¯Ų…ŲˆŲ† Ø§Ų„Ų…ØĢبØĒŲˆŲ†"
pinnedUsersDescription: "Ų‚Ø§ØĻŲ…ØŠ Ø§Ų„Ų…ØŗØĒØŽØ¯Ų…ŲŠŲ† Ø§Ų„Ų…ØĢبØĒŲŠŲ† ؁؊ Ų„ØŗØ§Ų† \"Ø§ØŗØĒŲƒØ´Ų\" ، اØŦØšŲ„ ŲƒŲ„ Ø§ØŗŲ… Ų…ØŗØĒØŽØ¯Ų… ؁؊ ØŗØˇØą Ų„ŲˆØ­Ø¯Ų‡."
pinnedPages: "Ø§Ų„ØĩŲØ­Ø§ØĒ Ø§Ų„Ų…ØĢبØĒØŠ"
pinnedPagesDescription: "ØŖØ¯ØŽŲ„ Ų…ØŗØ§Øą Ø§Ų„ØĩŲØ­Ø§ØĒ Ø§Ų„ØĒ؊ ØĒØąŲŠØ¯ ØĒØĢØ¨ŲŠØĒŲ‡Ø§ ؁؊ ØŖØšŲ„Ų‰ Ų‡Ø°Ø§ Ø§Ų„Ų…ŲˆŲ‚ØšØŒ اØŦØšŲ„ ŲƒŲ„ Ų…ØŗØ§Øą ؁؊ ØŗØˇØą Ų„ŲˆØ­Ø¯Ų‡."
pinnedClipId: "Ų…ØšØąŲ‘Ų Ø§Ų„Ų…Ø´Ø¨Ųƒ Ø§Ų„Ų…ØĢبØĒ"
pinnedNotes: "Ų…Ų„Ø§Ø­Ø¸ØŠ Ų…ØĢبØĒØŠ"
pinnedUsers: "Ø§Ų„Ų…ØŗØĒØŽØ¯Ų…ŲˆŲ† Ø§Ų„Ų…Ø¯Ø¨ØŗŲˆŲ†"
pinnedUsersDescription: "Ų‚Ø§ØĻŲ…ØŠ Ø§Ų„Ų…ØŗØĒØŽØ¯Ų…ŲŠŲ† Ø§Ų„Ų…Ø¯Ø¨ØŗŲŠŲ† ؁؊ Ų„ØŗØ§Ų† \"Ø§ØŗØĒŲƒØ´Ų\" ، اØŦØšŲ„ ŲƒŲ„ Ø§ØŗŲ… Ų…ØŗØĒØŽØ¯Ų… ؁؊ ØŗØˇØą Ų„ŲˆØ­Ø¯Ų‡."
pinnedPages: "Ø§Ų„ØĩŲØ­Ø§ØĒ Ø§Ų„Ų…Ø¯Ø¨ØŗØŠ"
pinnedPagesDescription: "ØŖØ¯ØŽŲ„ Ų…ØŗØ§Øą Ø§Ų„ØĩŲØ­Ø§ØĒ Ø§Ų„ØĒ؊ ØĒØąŲŠØ¯ ØĒØ¯Ø¨ŲŠØŗŲ‡Ø§ ؁؊ ØŖØšŲ„Ų‰ Ų‡Ø°Ø§ Ø§Ų„Ų…ŲˆŲ‚ØšØŒ اØŦØšŲ„ ŲƒŲ„ Ų…ØŗØ§Øą ؁؊ ØŗØˇØą Ų„ŲˆØ­Ø¯Ų‡."
pinnedClipId: "Ų…ØšØąŲ‘Ų Ø§Ų„Ų…Ø´Ø¨Ųƒ Ø§Ų„Ų…Ø¯Ø¨Øŗ"
pinnedNotes: "Ų…Ų„Ø§Ø­Ø¸ØŠ Ų…Ø¯Ø¨ØŗØŠ"
hcaptcha: "hCaptcha"
enableHcaptcha: "ŲØšŲ‘Ų„ hCaptcha"
hcaptchaSiteKey: "؅؁ØĒاح Ø§Ų„Ų…ŲˆŲ‚Øš"
hcaptchaSecretKey: "Ø§Ų„Ų…ŲØĒاح Ø§Ų„ØŗØąŲŠ"
mcaptchaSiteKey: "؅؁ØĒاح Ø§Ų„Ų…ŲˆŲ‚Øš"
mcaptchaSecretKey: "Ø§Ų„Ų…ŲØĒاح Ø§Ų„ØŗØąŲŠ"
recaptcha: "reCAPTCHA"
enableRecaptcha: "ØĒŲ…ŲƒŲŠŲ† reCAPTCHA"
recaptchaSiteKey: "؅؁ØĒاح Ø§Ų„Ų…ŲˆŲ‚Øš"
@@ -375,7 +357,6 @@ antennaExcludeKeywords: "Ø§Ų„ŲƒŲ„Ų…Ø§ØĒ Ø§Ų„Ų…ŲØĒØ§Ø­ŲŠØŠ Ø§Ų„Ų…ØŗØĒØĢŲ†Ø§ØŠ"
antennaKeywordsDescription: "Ø§ŲØĩŲ„ Ø¨ŲŠŲ†Ų‡Ų… Ø¨Ų…ØŗØ§ŲØŠ Ų„Ø§ØŗØĒØŽØ¯Ø§Ų… Ų…ØšØ§Ų…Ų„ \"؈\" ØŖŲˆ Ø¨ØŗØˇØą Ų„Ø§ØŗØĒØŽØ¯Ø§Ų… Ų…ØšØ§Ų…Ų„ \"ØŖŲˆ\""
notifyAntenna: "Ų†Ø¨Ų‡Ų†ŲŠ بØĩŲˆŲ„ Ų…Ų„Ø§Ø­Ø¸Ø§ØĒ ØŦØ¯ŲŠØ¯ØŠ"
withFileAntenna: "Ų…Ų„Ø§Ø­Ø¸Ø§ØĒ ØĒØ­ŲˆŲŠ Ų…Ų„ŲØ§ØĒ ŲŲ‚Øˇ"
enableServiceworker: "ŲØšŲ‘Ų„ ØĨØąØŗØ§Ų„ Ø§Ų„ØĨØ´ØšØ§ØąØ§ØĒ Ų„Ų„Ų…ØĒØĩŲØ­"
antennaUsersDescription: "Ø§ŲƒØĒب Ø§ØŗŲ… Ų…ØŗØĒØŽØ¯Ų… Ų„ŲƒŲ„ ØŗØˇØą"
caseSensitive: "Ø­ØŗØ§ØŗŲŠØŠ Ø­Ø§Ų„ØŠ Ø§Ų„ØŖØ­ØąŲ"
withReplies: "Ø¨Ø§Ų„ØąØ¯ŲˆØ¯"
@@ -398,15 +379,11 @@ about: "ØšŲ†"
aboutMisskey: "ØšŲ† Misskey"
administrator: "Ø§Ų„Ų…Ø¯ŲŠØą"
token: "Ø§Ų„ØąŲ…Ø˛ Ø§Ų„Ų…Ų…ŲŠØ˛"
2fa: "Ø§Ų„Ø§ØŗØĒ؊ØĢØ§Ų‚ Ø¨ØšØ§Ų…Ų„ŲŽŲŠŲ’Ų†"
totp: "ØĒØˇØ¨ŲŠŲ‚ Ø§ØŗØĒ؊ØĢØ§Ų‚"
moderator: "Ų…Ø´ØąŲŲ"
moderation: "Ø§Ų„ØĨØ´ØąØ§Ų"
nUsersMentioned: "{n} Ų…ØŗØĒØŽØ¯Ų…ŲŠŲ† ØŖŲØ´ŲŠØą ØĨŲ„ŲŠŲ‡Ų…"
securityKeyAndPasskey: "Ø§Ų„ØŖŲ…Ų† ŲˆŲ…ŲØ§ØĒŲŠØ­ Ø§Ų„ØŖŲ…Ø§Ų†"
securityKey: "؅؁ØĒاح Ø§Ų„ØŖŲ…Ø§Ų†"
lastUsed: "ØĸØŽØą Ø§ØŗØĒØŽØ¯Ø§Ų…"
lastUsedAt: "ØĸØŽØą Ø§ØŗØĒØŽØ¯Ø§Ų…: {t}"
unregister: "ØĨŲ„ØēØ§ØĄ Ø§Ų„ØĒØŗØŦŲŠŲ„"
passwordLessLogin: "؄ؐØŦ ؅ؐ؆ Ø¯ŲˆŲ† ŲƒŲ„Ų…ØŠ ØŗØąŲŠØŠ"
resetPassword: "ØŖØšØ¯ ØĒØšŲŠŲŠŲ† ŲƒŲ„Ų…ØĒ؃ Ø§Ų„ØŗØąŲŠØŠ"
@@ -416,6 +393,7 @@ share: "Ø´Ø§ØąŲŲƒ"
notFound: "ØēŲŠØą Ų…ŲˆØŦŲˆØ¯"
notFoundDescription: "ØĒØšØ°Øą Ø§Ų„ØšØĢŲˆØą ØšŲ„Ų‰ ØĩŲØ­ØŠ ŲŠŲ‚ŲˆØ¯ ØĨŲ„ŲŠŲ‡Ø§ Ų‡Ø°Ø§ Ø§Ų„ØąØ§Ø¨Øˇ."
uploadFolder: "Ø§Ų„Ų…ØŦŲ„Ø¯ Ø§Ų„Ø§ŲØĒØąØ§Øļ؊ Ų„Ų„ØąŲØš"
cacheClear: "Ų…ØŗØ­ Ø°Ø§ŲƒØąØŠ Ø§Ų„ØĒØŽØ˛ŲŠŲ† Ø§Ų„Ų…Ø¤Ų‚ØĒ"
markAsReadAllNotifications: "؈ØļØš ØŦŲ…ŲŠØš Ø§Ų„ØĨØ´ØšØ§ØąØ§ØĒ ŲƒØŖŲ†Ų‡Ø§ Ų…Ų‚ØąŲˆØĄØŠ"
markAsReadAllUnreadNotes: "ØšŲ„Ų‘Ų… ØŦŲ…ŲŠØš Ø§Ų„Ų…Ų„Ø§Ø­Ø¸Ø§ØĒ ŲƒŲ…Ų‚ØąŲˆØĄØŠ"
markAsReadAllTalkMessages: "ØšŲ„Ų‘Ų… ØŦŲ…ŲŠØš Ø§Ų„ØąØŗØ§ØĻŲ„ ŲƒŲ…Ų‚ØąŲˆØĄØŠ"
@@ -433,6 +411,8 @@ retype: "ØŖØšØ¯ Ø§Ų„ŲƒØĒاب؊"
noteOf: "Ų…Ų„Ø§Ø­Ø¸Ø§ØĒ {user}"
quoteAttached: "Ø§ŲŲ‚ØĒŲØ¨ØŗŲŽ"
quoteQuestion: "ØŖØĒØąŲŠØ¯ ØĒØļŲ…ŲŠŲ†Ų‡Ø§ ŲƒØ§Ų‚ØĒØ¨Ø§Øŗ"
noMessagesYet: "Ų„ŲŠØŗ Ų‡Ų†Ø§Ųƒ ØąØŗØ§ØĻŲ„ بؚد"
newMessageExists: "Ų„Ų‚Ø¯ ØĒŲ„Ų‚ŲŠØĒ ØąØŗØ§Ų„ØŠ ØŦØ¯ŲŠØ¯ØŠ"
onlyOneFileCanBeAttached: "ŲŠŲ…ŲƒŲ†Ųƒ ØĨØąŲØ§Ų‚ ؅؄؁ ŲˆØ§Ø­Ø¯ Ø¨Ø§Ų„ØąØŗØ§Ų„ØŠ"
signinRequired: "ØąØŦØ§ØĄŲ‹ ؄ؐØŦ"
invitations: "Ø¯ØšŲˆØŠ"
@@ -454,8 +434,6 @@ or: "ØŖŲˆ"
language: "Ø§Ų„Ų„ØēØŠ"
uiLanguage: "Ų„ØēØŠ ŲˆØ§ØŦŲ‡ØŠ Ø§Ų„Ų…ØŗØĒØŽØ¯Ų…"
aboutX: "ØšŲ† {x}"
emojiStyle: "Ų†Ų…Øˇ Ø§Ų„ŲˆØŦŲˆŲ‡ Ø§Ų„ØĒØšØ¨ŲŠØąŲŠØŠ"
showNoteActionsOnlyHover: "ØŖØ¸Ų‡Øą Ø§Ų„ØĨØŦØąØ§ØĄØ§ØĒ ØšŲ†Ø¯ Ø§Ų„ØĒŲ…ØąŲŠØą ŲŲˆŲ‚ Ø§Ų„Ų…Ų„Ø§Ø­Ø¸ØŠ"
noHistory: "Ø§Ų„ØŗØŦŲ„ ŲØ§ØąØē"
signinHistory: "ØĒØ§ØąŲŠØŽ ØĒØŗØŦŲŠŲ„ Ø§Ų„Ø¯ØŽŲˆŲ„"
doing: "Ø§Ų†ØĒØ¸Øą Ų„Ø­Ø¸ØŠ"
@@ -466,7 +444,6 @@ createAccount: "ØŖŲ†Ø´ØĻ Ø­ØŗØ§Ø¨Ų‹Ø§"
existingAccount: "Ø§Ų„Ø­ØŗØ§Ø¨Ø§ØĒ Ø§Ų„Ų…ŲˆØŦŲˆØ¯ØŠ"
regenerate: "ØŖØšŲØ¯ Ø§Ų„ØĒŲˆŲ„ŲŠØ¯"
fontSize: "Ø­ØŦŲ… Ø§Ų„ØŽØˇ"
limitTo: "ØŗŲ‚ŲŲ‡Ų Ų„Ų€{x}"
noFollowRequests: "Ų„ŲŠØŗ Ų„Ø¯ŲŠŲƒ ØˇŲ„Ø¨Ø§ØĒ Ų…ØĒابؚ؊ Ų…ØšŲ„Ų‚ØŠ"
openImageInNewTab: "ØĨ؁ØĒØ­ Ø§Ų„ØĩŲˆØąØŠ بØĩŲØ­ØŠ ØŦØ¯ŲŠØ¯ØŠ"
dashboard: "Ų„ŲˆØ­ØŠ Ø§Ų„ØĒØ­ŲƒŲ…"
@@ -488,16 +465,13 @@ objectStoragePrefix: "Ø§Ų„Ø¨Ø§Ø¯ØĻØŠ"
objectStoragePrefixDesc: "ØŗØĒŲØ­ŲØ¸ Ø§Ų„Ų…Ų„ŲØ§ØĒ ؁؊ Ų…ØŦŲ„Ø¯Ø§ØĒ ØĒØ­ŲˆŲŠ Ø§ØŗŲ…Ø§ØĄŲ‡Ø§ Ų‡Ø°Ų‡ Ø§Ų„Ø¨Ø§Ø¯ØĻØŠ."
objectStorageEndpoint: "Ų†Ų‚ØˇØŠ Ø§Ų„Ų†Ų‡Ø§ŲŠØŠ"
objectStorageRegion: "Ø§Ų„Ų…Ų†ØˇŲ‚ØŠ"
objectStorageRegionDesc: "حدد Ų…Ų†ØˇŲ‚ØŠ Ų…ØĢŲ„ \"xx-east-1\". ØĨذا ŲƒØ§Ų†ØĒ ØŽØ¯Ų…ØĒ؃ Ų„Ø§ ØĒŲ…ŲŠØ˛ Ø¨ŲŠŲ† Ø§Ų„Ų…Ų†Ø§ØˇŲ‚ Ø§ØŗØĒØŽØ¯Ų… \"us-east-1\" ØŖŲˆ اØĒØąŲƒŲ‡Ø§ ŲØ§ØąØēØŠ ØĨذا ŲƒŲ†ØĒ ØĒØŗØĒØŽØ¯Ų… Ų…ØĒØēŲŠØąØ§ØĒ Ø§Ų„Ø¨ŲŠØĻØŠ ØŖŲˆ Ų…Ų„ŲØ§ØĒ ØļØ¨Øˇ AWS."
objectStorageUseSSL: "Ø§ØŗØĒØŽØ¯Ų… SSL"
objectStorageUseSSLDesc: "ØšØˇŲ„ Ų‡Ø°Ø§ Ø§Ų„ØŽŲŠØ§Øą ØĨذا Ų„Ų… ØĒØąØ¯ Ø§ØŗØĒØŽØ¯Ø§Ų… API ØšØ¨Øą HTTPS"
objectStorageUseProxy: "اØĒØĩŲ„ ØšØ¨Øą ŲˆŲƒŲŠŲ„"
objectStorageUseProxyDesc: "ØšØˇŲ„ Ų‡Ø°Ø§ Ø§Ų„ØŽŲŠØ§Øą ØĨذا Ų„Ų… ØĒØąØ¯ Ø§ØŗØĒØŽØ¯Ø§Ų… API ØšØ¨Øą ŲˆŲƒŲŠŲ„"
objectStorageSetPublicRead: "ØšŲŠŲ†Ų‡Ø§ ؃\"ØšŲ„Ų†ŲŠØŠ\" ØšŲ†Ø¯ Ø§Ų„ØąŲØš"
serverLogs: "ØŗØŦŲ„Ø§ØĒ Ø§Ų„ØŽØ§Ø¯Ų…"
deleteAll: "Ø­Ø°Ų Ø§Ų„ŲƒŲ„"
showFixedPostForm: "ØŖØ¸Ų‡Øą Ų†Ų…ŲˆØ°ØŦ Ø§Ų„ŲƒØĒاب؊ ؁؊ ØŖØšŲ„Ų‰ Ø§Ų„ØĩŲØ­ØŠ"
showFixedPostFormInChannel: "ØŖØ¸Ų‡Øą Ų†Ų…ŲˆØ°ØŦ Ø§Ų„ŲƒØĒاب؊ ؁؊ ØŖØšŲ„Ų‰ Ø§Ų„ØŽØˇ Ø§Ų„Ø˛Ų…Ų†ŲŠ (Ų‚Ų†ŲˆØ§ØĒ)"
newNoteRecived: "Ų‡Ų†Ø§Ųƒ Ų…Ų„Ø§Ø­Ø¸Ø§ØĒ ØŦØ¯ŲŠØ¯ØŠ"
sounds: "Ø§Ų„ØąŲ†Ø§ØĒ"
sound: "Ø§Ų„ØąŲ†Ø§ØĒ"
@@ -532,12 +506,9 @@ userSuspended: "ØšŲŲ„Ų‚ Ų‡Ø°Ø§ Ø§Ų„Ų…ØŗØĒØŽØ¯Ų…."
userSilenced: "ŲƒŲØĒŲ… Ų‡Ø°Ø§ Ø§Ų„Ų…ØŗØĒØŽØ¯Ų…."
yourAccountSuspendedTitle: "Ų‡Ø°Ø§ Ø§Ų„Ø­ØŗØ§Ø¨ Ų…ØšŲ„Ų‚"
yourAccountSuspendedDescription: "ØšŲŲ„Ų‚ Ø§Ų„Ø­ØŗØ§Ø¨ Ø¨ØŗØ¨Ø¨ Ø§Ų†ØĒŲ‡Ø§Ųƒ Ø´ØąŲˆØˇ ØŽØ¯Ų…ØŠ Ø§Ų„Ų…ØĢŲŠŲ„ ؈ Ų…Ø§ Ø´Ø§Ø¨Ų‡. ØĨذا ØŖØąØ¯ØĒ Ų…ØšØąŲØŠ Ø§Ų„ØĒ؁ØĩŲŠŲ„ ØĒŲˆØ§ØĩŲ„ Ų…Øš Ų…Ø¯ŲŠØą Ø§Ų„Ų…ØĢŲŠŲ„. ØąØŦØ§ØĄŲ‹ Ų„Ø§ ØĒŲ†Ø´ØĻ Ø­ØŗØ§Ø¨ ØŦØ¯ŲŠØ¯."
accountDeleted: "Ø­ŲØ°Ų Ø§Ų„Ø­ØŗØ§Ø¨"
accountDeletedDescription: "Ø­ŲØ°Ų Ų‡Ø°Ø§ Ø§Ų„Ø­ØŗØ§Ø¨."
menu: "Ø§Ų„Ų‚Ø§ØĻŲ…ØŠ"
divider: "ŲØ§ØĩŲ„"
addItem: "ØĨØļØ§ŲØŠ ØšŲ†ØĩØą"
rearrange: "ØŖØšØ¯ Ø§Ų„ØĒØąØĒŲŠØ¨"
relays: "Ø§Ų„Ų…ŲØąŲŽØ­Ų„Ø§ØĒ"
addRelay: "ØĨØļØ§ŲØŠ Ų…ŲØąØ­Ų‘Ų„"
inboxUrl: "ØąØ§Ø¨Øˇ ØĩŲ†Ø¯ŲˆŲ‚ Ø§Ų„ŲˆØ§ØąØ¯"
@@ -560,8 +531,6 @@ author: "Ø§Ų„ŲƒØ§ØĒب"
leaveConfirm: "Ų„Ø¯ŲŠŲƒ ØĒØēŲŠŲŠØąØ§ØĒ ØēŲŠØą Ų…Ø­ŲŲˆØ¸ØŠ. ØŖØĒØąŲŠØ¯ Ø§Ų„Ų…ØĒابؚ؊ Ø¯ŲˆŲ† Ø­ŲØ¸Ų‡Ø§ØŸ"
manage: "ØĨØ¯Ø§ØąØŠ "
plugins: "Ø§Ų„ØĨØļØ§ŲØ§ØĒ"
preferencesBackups: "Ø§Ų„Ų†ŲØŗØŽ Ø§Ų„Ø§Ø­ØĒŲŠØ§ØˇŲŠØŠ Ų„Ų„ØĨؚداداØĒ"
useBlurEffectForModal: "Ø§ØŗØĒØŽØ¯Ų… ØĒØŖØĢŲŠØą Ø§Ų„ØˇŲ…Øŗ ؁؊ Ø§Ų„Ų…Ø´ØąŲˆØˇ"
useFullReactionPicker: "Ø§ØŗØĒØŽØ¯Ų… Ø§Ų„Ø­ØŦŲ… Ø§Ų„ŲƒØ§Ų…Ų„ Ų„Ų…Ų†ØĒŲ‚ŲŠ Ø§Ų„ØĒŲØ§ØšŲ„Ø§ØĒ"
width: "Ø§Ų„ØšØąØļ"
height: "Ø§Ų„ØĨØąØĒŲØ§Øš"
@@ -620,7 +589,10 @@ abuseReported: "ØŖŲØąØŗŲ„ Ø§Ų„Ø¨Ų„Ø§Øē، Ø´ŲƒØąŲ‹Ø§ Ų„Ųƒ"
reporter: "Ø§Ų„Ų…ŲØ¨Ų„Ų‘Øē"
reporteeOrigin: "ØŖØĩŲ„ Ø§Ų„Ø¨Ų„Ø§Øē"
reporterOrigin: "ØŖØĩŲ„ Ø§Ų„Ų…ŲØ¨Ų„Ų‘Øē"
forwardReport: "؈ØŦŲ‘Ų‡ Ø§Ų„Ø¨Ų„Ø§Øē ØĨŲ„Ų‰ Ø§Ų„Ų…ØĢŲŠŲ„ Ø§Ų„Ø¨ØšŲŠØ¯"
forwardReportIsAnonymous: "؁؊ Ø§Ų„Ų…ØĢŲŠŲ„ Ø§Ų„Ø¨ØšŲŠØ¯ ØŗŲŠØ¸Ų‡Øą Ø§Ų„Ų…Ø¨Ų„Ų‘Øē ŲƒØ­ØŗØ§Ø¨ Ų…ØŦŲ‡ŲˆŲ„."
send: "ØŖØąØŗŲ„"
abuseMarkAsResolved: "ØšŲ„Ų‘Ų… Ø§Ų„Ø¨Ų„Ø§Øē ŲƒŲ…Ø­Ų„ŲˆŲ„"
openInNewTab: "Ø§ŲØĒØ­ ؁؊ Ų„ØŗØ§Ų† ØŦØ¯ŲŠØ¯"
defaultNavigationBehaviour: "ØŗŲ„ŲˆŲƒ Ø§Ų„Ų…Ų„Ø§Ø­ØŠ Ø§Ų„Ø§ŲØĒØąØ§Øļ؊"
editTheseSettingsMayBreakAccount: "ØĒØšØ¯ŲŠŲ„ Ų‡Ø°Ų‡ Ø§Ų„ØĨؚداداØĒ Ų‚Ø¯ ŲŠØŗØ¨Ø¨ ØšØˇØ¨Ų‹Ø§ Ų„Ø­ØŗØ§Ø¨Ųƒ"
@@ -634,9 +606,7 @@ clip: "Ų…ŲØ´Ø¨Ųƒ"
createNew: "ØŖŲ†Ø´ŲØĻ ØŦØ¯ŲŠØ¯"
optional: "ا؎ØĒŲŠØ§ØąŲŠ"
createNewClip: "ØŖŲ†Ø´ØĻ Ų…ŲØ´Ø¨ŲƒŲŽØ§ ØŦØ¯ŲŠØ¯Ų‹Ø§"
confirmToUnclipAlreadyClippedNote: "Ų‡Ø°Ų‡ Ø§Ų„Ų…Ų„Ø§Ø­Ø¸ØŠ ØĒŲ†ØĒŲ…ŲŠ Ų„Ų„Ų…Ø´Ø¨Ųƒ {name} ØŗŲ„ŲŲ‹Ø§ØŒ ØŖØĒØąŲŠØ¯ Ø­Ø°ŲŲ‡Ø§ Ų…Ų†Ų‡â¸Ž"
public: "ØšŲ„Ų†ŲŠ"
private: "؎اØĩ"
i18nInfo: "؊ØĒØąØŦŲ… Ų…ØĒØˇŲˆØšŲˆŲ† Ų…ŲŠØŗŲƒŲŠ ØĨŲ„Ų‰ ؚد؊ Ų„ØēاØĒ، ŲŠŲ…ŲƒŲ†Ųƒ Ø§Ų„Ų…ØŗØ§ØšØ¯ØŠ ØšØ¨Øą {link}"
manageAccessTokens: "ØĨØ¯Ø§ØąØŠ ØąŲ…ŲˆØ˛ Ø§Ų„ŲˆØĩŲˆŲ„"
accountInfo: "Ų…ØšŲ„ŲˆŲ…Ø§ØĒ Ø§Ų„Ø­ØŗØ§Ø¨"
@@ -657,7 +627,6 @@ driveFilesCount: "ؚدد Ø§Ų„Ų…Ų„ŲØ§ØĒ ؁؊ Ų‚ØąØĩ Ø§Ų„ØĒØŽØ˛ŲŠŲ†"
driveUsage: "Ø§Ų„Ų…ØŗØĒØēŲ„ Ų…Ų† Ų‚ØąØĩ Ø§Ų„ØĒØŽØ˛ŲŠŲ†"
noCrawle: "Ø§ØąŲØļ ŲŲ‡ØąØŗØŠ Ø˛Ø§Ø­Ų Ø§Ų„ŲˆŲŠØ¨"
noCrawleDescription: "ŲŠØˇŲ„Ø¨ Ų…Ų† Ų…Ø­ØąŲƒØ§ØĒ Ø§Ų„Ø¨Ø­ØĢ ØŖŲ„Ų‘Ø§ ŲŠŲŲŲ‡ØąØŗŲˆØ§ Ų…Ų„ŲŲƒ Ø§Ų„Ø´ØŽØĩ؊ ŲˆŲ…Ų„Ø§Ø­Ø¸Ø§ØĒ ؈ØĩŲØ­Ø§ØĒ؃ ŲˆŲ…Ø§ Ø´Ø§Ø¨Ų‡."
lockedAccountInfo: "ØŗØĒŲƒŲˆŲ† Ų‡Ø°Ų‡ Ø§Ų„Ų…Ų„Ø§Ø­Ø¸ØŠ Ų…ØąØĻŲŠØŠ Ų„Ų„ØŦŲ…ŲŠØš Ų…Ø§Ų„Ų… ØĒحدد Ų…ØąØĻØĒŲŠŲ‡Ø§ ØĨŲ„Ų‰ \"Ų„Ų„Ų…ØĒØ§Ø¨ØšŲŠŲ† ŲŲ‚Øˇ\""
alwaysMarkSensitive: "ØšŲ„Ų‘Ų… Ø§ŲØĒØąØ§ØļŲŠŲ‹Ø§ ØŦŲ…ŲŠØš Ų…Ų„Ø§Ø­Ø¸Ø§ØĒ؊ ŲƒØ°Ø§ØĒ Ų…Ø­ØĒŲˆŲ‰ Ø­ØŗØ§Øŗ"
loadRawImages: "Ø­Ų…Ų‘Ų„ Ø§Ų„ØĩŲˆØą Ø§Ų„ØŖØĩŲ„ŲŠØŠ Ø¨Ø¯Ų„Ų‹Ø§ Ų…Ų† Ø§Ų„Ų…ØĩØēØąØ§ØĒ"
disableShowingAnimatedImages: "Ų„Ø§ ØĒØ´ØēŲ‘Ų„ Ø§Ų„ØĩŲˆØą Ø§Ų„Ų…ØĒØ­ØąŲƒØŠ"
@@ -671,12 +640,10 @@ contact: "Ø§Ų„ØĒŲˆØ§ØĩŲ„"
useSystemFont: "Ø§ØŗØĒØŽØ¯Ų… Ø§Ų„ØŽØˇ Ø§Ų„Ø§ŲØĒØąØ§ØļŲŠØŠ Ų„Ų„Ų†Ø¸Ø§Ų…"
clips: "Ų…Ø´Ø§Ø¨Ųƒ"
experimentalFeatures: "Ų…ŲŠŲ‘Ø˛Ø§ØĒ ا؎ØĒØ¨Ø§ØąŲŠØŠ"
experimental: "ا؎ØĒØ¨Ø§ØąŲŠ"
developer: "Ø§Ų„Ų…ØˇŲˆØą"
makeExplorable: "ØŖØ¸Ų‡Øą Ø§Ų„Ø­ØŗØ§Ø¨ ؁؊ ØĩŲØ­ØŠ \"Ø§ØŗØĒŲƒØ´Ø§Ų\""
makeExplorableDescription: "بØĒØšØˇŲŠŲ„ Ų‡Ø°Ø§ Ø§Ų„ØŽŲŠØ§Øą Ų„Ų† ŲŠØ¸Ų‡Øą Ø­ØŗØ§Ø¨Ųƒ ؁؊ ØĩŲØ­ØŠ \"Ø§ØŗØĒŲƒØ´Ø§Ų\""
left: "ŲŠØŗØ§Øą"
center: "ŲˆØŗØˇ"
showGapBetweenNotesInTimeline: "ØŖØ¸Ų‡Øą ؁ØŦŲˆØ§ØĒ Ø¨ŲŠŲ† Ø§Ų„Ų…Ø´Ø§ØąŲƒØ§ØĒ ؁؊ Ø§Ų„ØŽŲŠØˇ Ø§Ų„Ø˛Ų…Ų†ŲŠ"
wide: "ØšØąŲŠØļ"
narrow: "ØąŲŲŠØš"
reloadToApplySetting: "ØŗŲŠŲØˇØ¨Ų‚ Ų‡Ø°Ø§ Ø§Ų„ØĨؚداد بؚد ØĨؚاد؊ ØĒØ­Ų…ŲŠŲ„ Ø§Ų„ØĩŲØ­ØŠØŒ ØŖØĒØąŲŠØ¯ ØĨؚاد؊ ØĒØ­Ų…ŲŠŲ„Ų‡Ø§ Ø§Ų„ØĸŲ†ØŸ"
@@ -694,7 +661,6 @@ accentColor: "ØˇØ§Ø¨Øš Ų„ŲˆŲ†ŲŠ"
textColor: "Ų„ŲˆŲ† Ø§Ų„Ų†Øĩ"
saveAs: "Ø§Ø­ŲØ¸ ŲƒŲ€..."
advanced: "Ų…ØĒŲ‚Ø¯Ų…"
advancedSettings: "ØĨؚداداØĒ Ų…ØĒŲ‚Ø¯Ų…ØŠ"
value: "Ø§Ų„Ų‚ŲŠŲ…ØŠ"
createdAt: "ØŖŲŲ†Ø´ØĻ ؁؊"
updatedAt: "Ø­ŲØ¯Ų‘ØĢ ؁؊"
@@ -714,7 +680,6 @@ editCode: "Ø­ØąØą Ø§Ų„Ø´ŲØąØŠ"
apply: "ØĒØˇØ¨ŲŠŲ‚"
receiveAnnouncementFromInstance: "Ø§ØŗØĒŲ„Ų… ØĨØ´ØšØ§ØąØ§ØĒ Ų…Ų† Ų‡Ø°Ø§ Ø§Ų„Ų…ØĢŲŠŲ„"
emailNotification: "ØĨØ´ØšØ§ØąØ§ØĒ Ø§Ų„Ø¨ØąŲŠØ¯ Ø§Ų„ŲƒØĒØąŲˆŲ†ŲŠ"
publish: "ØšŲ„Ų†ŲŠ"
inChannelSearch: "ابحØĢ ØšŲ† Ų‚Ų†Ø§ØŠ"
useReactionPickerForContextMenu: "Ø§ŲØĒØ­ Ų…Ų†ØĒŲ‚ŲŠ Ø§Ų„ØĒŲØ§ØšŲ„Ø§ØĒ ØšŲ†Ø¯ Ø§Ų„Ų†Ų‚Øą Ø¨Ø§Ų„Ø˛Øą Ø§Ų„ØŖŲŠŲ…Ų†"
typingUsers: "{users} ؊؃ØĒب(ŲˆŲ†)..."
@@ -727,7 +692,7 @@ unlikeConfirm: "ØŖØĒØąŲŠØ¯ ØĨŲ„ØēØ§ØĄ ØĨØšØŦØ§Ø¨ŲƒØŸ"
fullView: "Ų…Ų„ØĄ Ø§Ų„Ø´Ø§Ø´ØŠ"
quitFullView: "Ø§ØŽØąØŦ Ų…Ų† ؈ØļØš Ų…Ų„ØĄ Ų„Ų„Ø´Ø§Ø´ØŠ"
addDescription: "ØŖØļ؁ ؈ØĩŲŲ‹Ø§"
userPagePinTip: "Ų„ØšØąØļ Ų…Ų„Ø§Ø­Ø¸ØŠ Ų‡Ų†Ø§ ا؎ØĒØą \"ØĢبØĒŲ‡Ø§ ØšŲ„Ų‰ Ø§Ų„ØĩŲØ­ØŠ Ø§Ų„Ø´ØŽØĩŲŠØŠ\" Ų…Ų† Ų‚Ø§ØĻŲ…ØŠ ØĒŲ„Ųƒ Ø§Ų„Ų…Ų„Ø§Ø­Ø¸ØŠ."
userPagePinTip: "Ų„ØšØąØļ Ų…Ų„Ø§Ø­Ø¸ØŠ Ų‡Ų†Ø§ ا؎ØĒØą \"Ø¯Ø¨ØŗŲ‡Ø§ ØšŲ„Ų‰ Ø§Ų„ØĩŲØ­ØŠ Ø§Ų„Ø´ØŽØĩŲŠØŠ\" Ų…Ų† Ų‚Ø§ØĻŲ…ØŠ ØĒŲ„Ųƒ Ø§Ų„Ų…Ų„Ø§Ø­Ø¸ØŠ."
notSpecifiedMentionWarning: "؁؊ Ø§Ų„Ų…Ų„Ø§Ø­Ø¸ØŠ Ø°ŲƒØą Ų„Ų…ØŗØĒØŽØ¯Ų…ŲŠŲ† Ų„Ų† ŲŠØŗØĒŲ„Ų…ŲˆŲ‡Ø§."
info: "ØšŲ†"
userInfo: "Ų…ØšŲ„ŲˆŲ…Ø§ØĒ Ø§Ų„Ų…ØŗØĒØŽØ¯Ų…"
@@ -754,14 +719,12 @@ noMaintainerInformationWarning: "Ų„Ų… ØĒŲØļØ¨Øˇ Ų…ØšŲ„ŲˆŲ…Ø§ØĒ Ø§Ų„Ų…Ø¯ŲŠØą"
noBotProtectionWarning: "Ų„Ų… ØĒØļØ¨Øˇ Ø§Ų„Ø­Ų…Ø§ŲŠØŠ Ų…Ų† Ø§Ų„Ø­ØŗØ§Ø¨Ø§ØĒ Ø§Ų„ØĸŲ„ŲŠØŠ"
configure: "اØļØ¨Øˇ"
postToGallery: "Ø§Ų†Ø´Øą ؁؊ Ø§Ų„Ų…ØšØąØļ"
postToHashtag: "Ø§Ų†Ø´Øą Ø¨Ų‡Ø°Ø§ Ø§Ų„ŲˆØŗŲ…"
gallery: "Ø§Ų„Ų…ØšØąØļ"
recentPosts: "Ø§Ų„Ų…Ø´Ø§ØąŲƒØ§ØĒ Ø§Ų„Ø­Ø¯ŲŠØĢØŠ"
popularPosts: "Ø§Ų„Ų…Ø´Ø§ØąŲƒØ§ØĒ Ø§Ų„Ų…ØĒØ¯Ø§ŲˆŲ„ØŠ"
shareWithNote: "Ø´Ø§ØąŲƒŲ‡ ؁؊ Ų…Ų„Ø§Ø­Ø¸ØŠ"
ads: "Ø§Ų„ØĨØšŲ„Ø§Ų†Ø§ØĒ"
expiration: "ŲŠŲ†ØĒŲ‡ŲŠ Ø§ØŗØĒØˇŲ„Ø§Øš Ø§Ų„ØąØŖŲŠ ؁؊"
startingperiod: "Ø§Ø¨Ø¯ØŖ"
memo: "ØĒØ°ŲƒŲŠØą"
priority: "Ø§Ų„ØŖŲˆŲ„ŲˆŲŠØŠ"
high: "ØšØ§Ų„ŲŠØŠ"
@@ -787,18 +750,13 @@ translate: "ØĒØąØŦŲ…"
translatedFrom: "ØĒŲØąØŦŲ… Ų…Ų† {x}"
accountDeletionInProgress: "Ø­Ø°Ų Ø§Ų„Ø­ØŗØ§Ø¨ ØŦØ§ØąŲ"
usernameInfo: "Ø§Ų„Ø§ØŗŲ… Ø§Ų„Ø°ŲŠ ŲŠŲ…ŲŠØ˛Ųƒ ØšŲ† Ø¨Ø§ŲŲŠ Ų…ØŗØĒØŽØ¯Ų…ŲŠ Ų‡Ø°Ø§ Ø§Ų„ØŽØ§Ø¯Ų…ØŒ ŲŠŲ…ŲƒŲ†Ųƒ Ø§ØŗØĒØŽØ¯Ø§Ų… Ø§Ų„Ø­ØąŲˆŲ Ø§Ų„Ų„Ø§ØĒŲŠŲ†ŲŠØŠ (a~z, A~Z) ŲˆØ§Ų„ØŖØąŲ‚Ø§Ų… (0~9) ŲˆØ§Ų„Ø´ØąØˇØŠ Ø§Ų„ØŗŲŲ„ŲŠØŠ (_). Ų„Ø§ ŲŠŲ…ŲƒŲ†Ųƒ ØĒØēŲŠŲŠØąŲ‡ بؚد ØĒØŗØŦŲŠŲ„Ų‡."
devMode: "؈ØļØš Ø§Ų„Ų…ŲØˇŲˆŲ‘Øą"
keepCw: "ØŖØ¨Ų‚Ų ØšŲ„Ų‰ ØĒØ­Ø°ŲŠØąØ§ØĒ Ø§Ų„Ų…Ø­ØĒŲˆŲ‰"
pubSub: "Ø­ØŗØ§Ø¨Ø§ØĒ Pub/Sub"
lastCommunication: "ØĸØŽØą ØĒŲˆØ§ØĩŲ„"
resolved: "ØšŲˆŲ„ØŦ"
unresolved: "Ų„Ų… ŲŠØšØ§Ų„ØŦ"
breakFollow: "ØĨŲ„ØēØ§ØĄ Ø§Ų„Ø§Ø´ØĒØąØ§Ųƒ"
breakFollowConfirm: "ØŖŲ…ØĒØŖŲƒØ¯ Ų…Ų† ØĨØ˛Ø§Ų„ØŠ Ø§Ų„Ų…ØĒØ§Ø¨ŲØš ؟"
itsOn: "Ų…ŲØšŲ‘Ų„"
itsOff: "Ų…ØšØˇŲ‘Ų„"
on: "Ų…ŲØšŲ„"
off: "Ų…ØšØˇŲ„"
emailRequiredForSignup: "ØšŲ†ŲˆØ§Ų† Ø§Ų„Ø¨ØąŲŠØ¯ Ø§Ų„ØĨŲ„ŲƒØĒØąŲˆŲ†ŲŠ ØĨŲ„Ø˛Ø§Ų…ŲŠ Ų„Ų„ØĒØŗØŦŲŠŲ„"
unread: "ØēŲŠØą Ų…Ų‚ØąŲˆØĄØŠ"
filter: "ØąØ´Ų‘Ø­"
@@ -809,7 +767,8 @@ makeReactionsPublicDescription: "Ų‡Ø°Ø§ ØŗŲŠØŦØšŲ„ Ų‚Ø§ØĻŲ…ØŠ ØĒŲØ§ØšŲ„Ø§ØĒ؃
classic: "ØĒŲ‚Ų„ŲŠØ¯ŲŠ"
muteThread: "Ø§ŲƒØĒŲ… Ø§Ų„Ų†Ų‚Ø§Ø´"
unmuteThread: "Ø§ØąŲØš Ø§Ų„ŲƒØĒŲ… ØšŲ† Ø§Ų„Ų†Ų‚Ø§Ø´"
continueThread: "Ø§ØšØąØļ Ø¨Ų‚ŲŠØŠ Ø§Ų„Ų†Ų‚Ø§Ø´"
ffVisibility: "Ų…ØąØĻŲŠØŠ Ø§Ų„Ų…ØĒØ§Ø¨ŲØšŲŠŲ†/Ø§Ų„Ų…ØĒØ§Ø¨ŲŽØšŲŠŲ†"
ffVisibilityDescription: "ŲŠØŗŲ…Ø­ Ų„Ųƒ بØĒØ­Ø¯ŲŠØ¯ Ų…Ų† ŲŠŲ…ŲƒŲ†Ų‡Ų… ØąØ¤ŲŠØŠ Ų…ØĒØ§Ø¨ŲØšŲŠŲƒ ŲˆŲ…ØĒØ§Ø¨ŲŽØšŲŠŲƒ."
deleteAccountConfirm: "ØŗŲŠØ­Ø°Ų Ø­ØŗØ§Ø¨Ųƒ Ų†Ų‡Ø§ØĻŲŠŲ‹Ø§ØŒ ØŖØĒØąŲŠØ¯ Ø§Ų„Ų…ØĒابؚ؊؟"
incorrectPassword: "ŲƒŲ„Ų…ØŠ Ø§Ų„ØŗØą ØŽØ§ØˇØĻØŠ."
voteConfirm: "Ų…ØĒŲŠŲ‚Ų‘ŲŲ† Ų…Ų† ØĒØĩ؈؊ØĒ؃ Ų„Ų€ {choice}؟"
@@ -831,301 +790,23 @@ tenMinutes: "10 Ø¯Ų‚Ø§ØĻŲ‚"
oneHour: "ØŗØ§ØšØŠ"
oneDay: "ŲŠŲˆŲ…"
oneWeek: "ØŖØŗØ¨ŲˆØš"
oneMonth: "Ø´Ų‡Øą"
failedToFetchAccountInformation: "ØĒØšØ°Øą ØŦŲ„Ø¨ Ų…ØšŲ„ŲˆŲ…Ø§ØĒ Ø§Ų„Ø­ØŗØ§Ø¨"
cropImage: "Ø§Ų‚ØĒØĩاØĩ Ø§Ų„ØĩŲˆØąØŠ"
cropImageAsk: "ØŖØĒØąŲŠØ¯ Ø§Ų‚ØĒØĩاØĩ Ų‡Ø°Ų‡ Ø§Ų„ØĩŲˆØąØŠ"
cropYes: "Ø§Ų‚ØĒØĩ"
cropNo: "Ø§ØŗØĒØŽØ¯Ų…Ų‡Ø§ ŲƒŲ…Ø§ Ų‡ŲŠ"
file: "Ø§Ų„Ų…Ų„ŲØ§ØĒ"
recentNHours: "ØĸØŽØą {n} ØŗØ§ØšØŠ"
recentNDays: "ØĸØŽØą {n} ØŖŲŠØ§Ų…"
noEmailServerWarning: "ØŽØ§Ø¯Ų… Ø§Ų„Ø¨ØąŲŠØ¯ ØēŲŠØą Ų…ØļØ¨ŲˆØˇ."
thereIsUnresolvedAbuseReportWarning: "ØĒ؈ØŦد Ø¨Ų„Ø§ØēاØĒ ØēŲŠØą Ų…ØšØ§Ų„ØŦØŠ."
recommended: "Ų…Ų‚ØĒØąØ­"
check: "Ø§Ų„ØĒØ­Ų‚Ų‚"
driveCapOverrideLabel: "ØēŲŠŲ‘Øą Ø­ØŦŲ… Ų‚ØąØĩ Ø§Ų„ØĒØŽØ˛ŲŠŲ† Ų„Ų‡Ø°Ø§ Ø§Ų„Ų…ØŗØĒØŽØ¯Ų…"
driveCapOverrideCaption: "ØŖØšØ¯ Ø§Ų„Ø­ØŦŲ… ØĨŲ„Ų‰ Ø§Ų„Ų‚ŲŠŲ…ØŠ Ø§Ų„Ø§ŲØĒØąØ§ØļŲŠØŠ بØĨØ¯ØŽØ§Ų„ 0 ØŖŲˆ ØŖŲ‚Ų„."
requireAdminForView: "Ų„Ø§ØŗØĒØšØąØ§Øļ Ų‡Ø°Ų‡ Ø§Ų„ØĩŲØ­ØŠ ؈ØŦب ØšŲ„ŲŠŲƒ Ø§Ų„ŲˆŲ„ŲˆØŦ ŲƒŲ…Ø¯ŲŠØą."
isSystemAccount: "Ø­ØŗØ§Ø¨ ØŖŲ†Ø´ØŖŲ‡ Ø§Ų„Ų†Ø¸Ø§Ų… ŲˆŲŠŲØ¯Ø§Øą Ų…Ų† Ų‚ŲØ¨Ų„Ų‡."
typeToConfirm: "ØŖØ¯ØŽŲ„ {x} Ų„Ų„ØĒØŖŲƒŲŠØ¯"
deleteAccount: "Ø§Ø­Ø°Ų Ø§Ų„Ø­ØŗØ§Ø¨"
document: "Ø§Ų„ØĒ؈ØĢŲŠŲ‚"
numberOfPageCache: "ؚدد Ø§Ų„ØĩŲØ­Ø§ØĒ Ø§Ų„Ų…ØŽØ˛Ų†ØŠ Ų…Ø¤Ų‚ØĒŲ‹Ø§"
numberOfPageCacheDescription: "ØąŲØš Ø§Ų„ØąŲ‚Ų… ØŗŲŠØŗØ­Ų† ØĒØŦØąØ¨ØŠ Ø§Ų„Ų…ØŗØĒØŽØ¯Ų… Ų„ŲƒŲ† ØŗŲŠØąŲØš Ø§ØŗØĒŲ‡Ų„Ø§Ųƒ Ø§Ų„Ø°Ø§ŲƒØąØŠ."
logoutConfirm: "ØŖØĒØąŲŠØ¯ Ø§Ų„ØŽØąŲˆØŦ؟"
lastActiveDate: "ØĸØŽØą Ø§ØŗØĒØŽØ¯Ø§Ų…"
statusbar: "Ø´ØąŲŠØˇ Ø§Ų„Ø­Ø§Ų„ØŠ"
pleaseSelect: "حدد ØŽŲŠØ§ØąŲ‹Ø§"
reverse: "Ø§Ų‚Ų„Ø¨"
colored: "Ų…Ų„ŲˆŲ‘Ų†"
refreshInterval: "Ų…Ų‡Ų„ØŠ Ø§Ų„ØĒØ­Ø¯ŲŠØĢ"
label: "Ø§Ų„ØĒØŗŲ…ŲŠØŠ"
type: "Ų†ŲˆØš"
speed: "ØŗØąØšØŠ"
slow: "Ø¨ØˇŲŠØĄ"
fast: "ØŗØąŲŠØš"
sensitiveMediaDetection: "Ø§Ų„ØĒØšØąŲ ØšŲ„Ų‰ Ø§Ų„Ų…Ø­ØĒŲˆŲ‰ Ø§Ų„Ø­ØŗØ§Øŗ"
localOnly: "Ø§Ų„Ų…Ø­Ų„ŲŠ ŲŲ‚Øˇ"
remoteOnly: "Ø¨ŲØšØ¯ŲŠ ŲŲ‚Øˇ"
failedToUpload: "ŲØ´Ų„ Ø§Ų„ØąŲØš"
cannotUploadBecauseInappropriate: "ØĒØšØ°Øą ØąŲØš Ø§Ų„Ų…Ų„Ų Ų„ŲˆØŦŲˆØ¯ Ų…Ø­ØĒŲˆŲ‰ Ø­ØŗØ§Øŗ ŲŲŠŲ‡."
cannotUploadBecauseNoFreeSpace: "ØĒØšØ°Øą ØąŲØš Ø§Ų„Ų…Ų„Ų Ų„Ų†Ų‚Øĩ Ų…ØŗØ§Ø­ØŠ Ø§Ų„ØĒØŽØ˛ŲŠŲ†."
cannotUploadBecauseExceedsFileSizeLimit: "ØĒØšØ°Øą ØąŲØš Ø§Ų„Ų…Ų„Ų Ø¨ØŗØ¨Ø¨ ØĒØŦØ§ŲˆØ˛ Ø­ØŦŲ…Ų‡ Ų„Ų„Ø­Ø¯ Ø§Ų„Ų…ØŗŲ…ŲˆØ­"
beta: "Ø¨ŲŠØĒا"
enableAutoSensitive: "ØĒØšŲŠŲŠŲ† ØĒŲ„Ų‚Ø§ØĻ؊ ŲƒŲ…Ø­ØĒŲˆŲ‰ Ø­ØŗØ§Øŗ NSFW"
enableAutoSensitiveDescription: "ØšŲ†Ø¯ Ø§Ų„Ø§ØŗØĒØˇØ§ØšØŠ ŲŠØŗŲ…Ø­ Ø¨Ø§ŲƒØĒØ´Ø§Ų Ø§Ų„Ų…Ø­ØĒŲˆŲ‰ Ø­ØŗØ§Øŗ NSFW ØĒŲ„Ų‚Ø§ØĻŲŠŲ‹Ø§ ؁؊ Ø§Ų„ŲˆØŗØ§ØĻØˇ Ø¨Ø§ØŗØĒØŽØ¯Ø§Ų… ØĒØšŲ„Ų… Ø§Ų„ØĸŲ„ØŠ ŲˆŲˆØŗŲ…Ų‡Ø§ ØĒØ¨ØšŲ‹Ø§ Ų„Ø°Ų„Ųƒ. Ų‚Ø¯ ŲŠŲƒŲˆŲ† Ų‡Ø°Ø§ Ø§Ų„ØŽŲŠØ§Øą Ų…ŲØšŲ„Ø§ Ų…Ų† ØŦŲ‡ØŠ Ø§Ų„ØŽØ§Ø¯Ų… ŲˆØŗŲŠØšŲ…Ų„ Ø­ØĒŲ‰ ŲˆØ§Ų† ØšŲØˇŲ„."
activeEmailValidationDescription: "؊ØĒØ­Ų‚Ų‚ Ų…Ų† Øĩح؊ ØšŲ†ŲˆØ§Ų† Ø§Ų„Ø¨ØąŲŠØ¯ Ø§Ų„ØĨŲ„ŲƒØĒØąŲˆŲ†ŲŠ Ø¨Ø´ŲƒŲ„ ØŖŲƒØĢØą Ø­Ø˛Ų…Ų‹Ø§ ŲˆØ°Ų„Ųƒ ØšØ¨Øą ØĒØ­Ø¯ŲŠØ¯ Ų…Ø§ ØĨذا ŲƒØ§Ų† ØšŲ†ŲˆØ§Ų† Ø¨ØąŲŠØ¯ ØĨŲ„ŲƒØĒØąŲˆŲ†ŲŠ Ų…Ø¤Ų‚ØĒ ؈ØĨŲ…ŲƒØ§Ų†ŲŠØŠ Ø§Ų„ØĒŲˆØ§ØĩŲ„ Ų…ØšŲ‡. ØĨذا Ų„Ų… ŲŠØ­Ø¯Ø¯ Ų‡Ø°Ø§ Ø§Ų„ØŽŲŠØ§Øą ŲØŗŲŠØĒØ­Ų‚Ų‚ Ų…Ų† Ų†ØŗŲ‚ ØšŲ†ŲˆØ§Ų† Ø§Ų„Ø¨ØąŲŠØ¯ Ø§Ų„ØĨŲ„ŲƒØĒØąŲˆŲ†ŲŠ."
navbar: "Ø´ØąŲŠØˇ Ø§Ų„ØĒŲ†Ų‚Ų„"
shuffle: "ØŽŲ„Øˇ"
account: "Ø§Ų„Ø­ØŗØ§Ø¨Ø§ØĒ"
move: "ØŖŲ†Ų‚Ų„"
pushNotification: "ØĨØąØŗØ§Ų„ Ø§Ų„ØĨØ´ØšØ§ØąØ§ØĒ"
subscribePushNotification: "ŲØšŲ‘Ų„ ØĨØąØŗØ§Ų„ Ø§Ų„ØĨØ´ØšØ§ØąØ§ØĒ"
unsubscribePushNotification: "ØšØˇŲ„ ØĨØąØŗØ§Ų„ Ø§Ų„ØĨØ´ØšØ§ØąØ§ØĒ"
pushNotificationAlreadySubscribed: "ØĨØąØŗØ§Ų„ Ø§Ų„ØĨØ´ØšØ§ØąØ§ØĒ Ų…ŲØšŲ„ ØŗŲ„ŲŲ‹Ø§"
pushNotificationNotSupported: "Ų…ØĒØĩŲØ­Ųƒ Ų„Ø§ ŲŠØ¯ØšŲ… ØĨØąØŗØ§Ų„ Ø§Ų„ØĨØ´ØšØ§ØąØ§ØĒ ØŖŲˆ Ø§Ų„Ų…ØĢŲŠŲ„ Ų„Ø§ ŲŠØ¯ØšŲ…Ų‡Ø§."
sendPushNotificationReadMessage: "Ø§Ø­Ø°Ų Ø§Ų„ØĨØ´ØšØ§ØąØ§ØĒ ŲŲˆØą Ų‚ØąØ§ØĄØĒŲ‡Ø§"
sendPushNotificationReadMessageCaption: "Ų‡Ø°Ø§ Ų‚Ø¯ ŲŠØ˛ŲŠØ¯ Ų…Ų† Ų…ØšØ¯Ų„ Ø§ØŗØĒŲ‡Ų„Ø§Ųƒ Ø§Ų„ØˇØ§Ų‚ØŠ Ų„ØŦŲ‡Ø§Ø˛Ųƒ."
windowMaximize: "Ø§Ų…Ų„ØŖ Ø§Ų„Ø´Ø§Ø´ØŠ"
windowRestore: "Ø§ØŗØĒØąØŦاؚ"
caption: "Ø§Ų„ØĒØšŲ„ŲŠŲ‚ Ø§Ų„ØĒ؈ØļŲŠØ­ŲŠ"
loggedInAsBot: "ŲˆØ§Ų„ØŦ ؃ØĸŲ„ŲŠ"
tools: "ØŖØ¯ŲˆØ§ØĒ"
cannotLoad: "ØĒØšØ°Øą Ø§Ų„ØĒØ­Ų…ŲŠŲ„"
numberOfProfileView: "Ų…Ø´Ø§Ų‡Ø¯Ø§ØĒ Ø§Ų„Ų…Ų„Ų Ø§Ų„Ø´ØŽØĩ؊"
like: "ØŖØšØŦØ¨Ų†ŲŠ"
unlike: "ØŖŲ„Øēؐ Ø§Ų„ØĨØšØŦاب"
numberOfLikes: "Ø§Ų„ØĨØšØŦاباØĒ"
show: "Ø§Ų„Ų…Ø¸Ų‡Øą"
neverShow: "Ų„Ø§ ØĒØ¸Ų‡ØąŲ‡ Ų…ØŦØ¯Ø¯Ų‹Ø§"
remindMeLater: "ØąØ¨Ų…Ø§ Ų„Ø§Ø­Ų‚Ø§"
didYouLikeMisskey: "Ų‡Ų„ ØŖØšØŦØ¨Ųƒ Ų…ŲŠØŗŲƒŲŠØŸ"
pleaseDonate: "ŲŠØŗØĒØŽØ¯Ų… {host} Ø§Ų„Ø¨ØąŲ…ØŦŲŠØŠ Ø§Ų„Ø­ØąØŠ Ų…ŲŠØŗŲƒŲŠ. Ų†ØĒŲ…Ų†Ų‰ ØŖŲ† ØĒØĒØ¨ØąØšŲˆØ§ Ų„Ų„Ų…Ø´ØąŲˆØš Ų…Ų…Ø§ ØŗŲŠØŗŲ…Ø­ Ų„Ų†Ø§ Ų…ØĒابؚ؊ ØĒØˇŲˆŲŠØąŲ‡!"
roles: "Ø§Ų„ØŖØ¯ŲˆØ§Øą"
role: "Ø§Ų„Ø¯ŲˆØą"
noRole: "Ų„Ų… ŲŠŲØšØĢØą ØšŲ„Ų‰ Ø¯ŲˆØą"
normalUser: "Ų…ØŗØĒØŽØ¯Ų… ØšØ§Ø¯ŲŠ"
undefined: "ØēŲŠØą Ų…ØšØąŲ‘Ų"
assign: "ØŖØŗŲ†Ø¯"
unassign: "ØŖŲ„Øē Ø§Ų„ØĨØŗŲ†Ø§Ø¯"
color: "Ø§Ų„Ų„ŲˆŲ†"
manageCustomEmojis: "ØĨØ¯Ø§ØąØŠ Ø§Ų„ØĨŲŠŲ…ŲˆØŦ؊ Ø§Ų„Ų…ØŽØĩØĩØŠ"
youCannotCreateAnymore: "؈ØĩŲ„ØĒ Ų„ØŗŲ‚Ų Ø§Ų„ØĨŲ†Ø´Ø§ØĄ."
cannotPerformTemporary: "ØēŲŠØą Ų…ØĒاح Ų…Ø¤Ų‚ØĒØ§Ų‹"
invalidParamError: "Ų…ØšØ§Ų…Ų„Ø§ØĒ ØēŲŠØą ØĩØ§Ų„Ø­ØŠ"
permissionDeniedError: "ØąŲŲØļØŠ Ø§Ų„ØšŲ…Ų„ŲŠØŠ"
preset: "ØĨؚداداØĒ Ų…ØŗØ¨Ų‚ØŠ"
selectFromPresets: "ا؎ØĒØą Ų…Ų† Ø§Ų„ØĨؚداداØĒ Ø§Ų„Ų…ØŗØ¨Ų‚ØŠ"
achievements: "Ø§Ų„ØĨŲ†ØŦØ§Ø˛Ø§ØĒ"
gotInvalidResponseError: "Ø§ØŗØĒØŦاب؊ ØēŲŠØą Ų…ØĒŲˆŲ‚ØšØŠ Ų…Ų† Ø§Ų„ØŽØ§Ø¯Ų…"
gotInvalidResponseErrorDescription: "؊ØĒØšØ°Øą Ø§Ų„ŲˆØĩŲˆŲ„ ØĨŲ„Ų‰ Ø§Ų„ØŽØ§Ø¯Ų… ØŖŲˆØŖŲ†Ų‡ ŲŠŲØĩØ§Ų†ØŒ ØąØŦØ§ØĄŲ‹ Ø­Ø§ŲˆŲ„ Ų„Ø§Ø­Ų‚Ų‹Ø§."
thisPostMayBeAnnoying: "Ų‡Ø°Ø§ Ų‚Ø¯ ŲŠØ˛ØšØŦ Ø§Ų„ØĸØŽØąŲŠŲ†."
thisPostMayBeAnnoyingHome: "ØŖŲ†Ø´Øą ؁؊ Ø§Ų„ØŽØˇ Ø§Ų„Ø˛Ų…Ų†ŲŠ Ø§Ų„ØąØĻŲŠØŗ"
thisPostMayBeAnnoyingCancel: "ØŖŲ„Øēؐ"
internalServerError: "ØŽØˇØŖ Ø¯Ø§ØŽŲ„ŲŠ ؁؊ Ø§Ų„ØŽØ§Ø¯Ų…"
internalServerErrorDescription: "ŲˆØ§ØŦŲ‡ Ø§Ų„ØŽØ§Ø¯Ų… ØŽØˇØŖ Øē؊ Ų…ØĒŲˆŲ‚Øš."
copyErrorInfo: "Ø§Ų†ØŗØŽ ØĒŲØ§ØĩŲŠŲ„ Ø§Ų„ØŽØˇØŖ"
joinThisServer: "ØŗØŦŲ„ ؁؊ Ų‡Ø°Ø§ Ø§Ų„Ų…ØĢŲŠŲ„"
exploreOtherServers: "اؚØĢØą ØšŲ„Ų‰ Ų…ØĢŲŠŲ„ ØĸØŽØą"
disableFederationOk: "ØšØˇŲ‘Ų„"
invitationRequiredToRegister: "Ų‡Ø°Ø§ Ø§Ų„Ų…ØĢŲŠŲ„ Ų„Ų„Ų…Ø¯ØšŲˆŲŠŲ† ŲŲ‚Øˇ. Ų„ØĒØŗØŦŲŠŲ„ ŲŲŠŲ‡ ØĒØ­ØĒاØŦ ØąŲ…Ø˛Ų‹Ø§ ØĩØ§Ų„Ø­Ų‹Ø§."
postToTheChannel: "Ø§Ų†Ø´Øą ؁؊ Ų‚Ų†Ø§ØŠ"
cannotBeChangedLater: "Ų„Ø§ ŲŠŲ…ŲƒŲ† ØĒØēŲŠŲŠØąŲ‡ Ų„Ø§Ø­Ų‚Ų‹Ø§."
reactionAcceptance: "Ų‚Ø¨ŲˆŲ„ Ø§Ų„ØĒŲØ§ØšŲ„Ø§ØĒ"
rolesAssignedToMe: "Ø§Ų„ØŖØ¯ŲˆØ§Øą Ø§Ų„Ų…ØŗŲ†Ø¯ØŠ ØĨŲ„ŲŠ"
resetPasswordConfirm: "Ų‡Ų„ ØĒØąŲŠØ¯ ØĨؚاد؊ ØĒØšŲŠŲŠŲ† ŲƒŲ„Ų…ØŠ Ø§Ų„ØŗØąØŸ"
license: "Ø§Ų„ØąØŽØĩØŠ"
unfavoriteConfirm: "ØŖØĒØąŲŠØ¯ ØĨØ˛Ø§Ų„ØĒŲ‡Ø§ Ų…Ų† Ø§Ų„Ų…ŲØļŲ„ØŠØŸ"
reactionsDisplaySize: "Ø­ØŦŲ… Ø§Ų„ØĒŲØ§ØšŲ„Ø§ØĒ"
limitWidthOfReaction: "ØĒØĩØēŲŠØą Ø­ØŦŲ… Ø§Ų„ØĒŲØ§ØšŲ„Ø§ØĒ"
noteIdOrUrl: "Ų…ØšØąŲ Ø§Ų„Ų…Ų„Ø§Ø­Ø¸ØŠ ØŖŲˆ ØąØ§Ø¨ØˇŲ‡Ø§"
video: "ŲŲŠØ¯ŲŠŲˆ"
videos: "ŲŲŠØ¯ŲŠŲˆŲ‡Ø§ØĒ"
dataSaver: "Ų…ŲˆŲØą Ø§Ų„Ø¨ŲŠØ§Ų†Ø§ØĒ"
accountMigration: "ØĒØąØ­ŲŠŲ„ Ø§Ų„Ø­ØŗØ§Ø¨"
accountMoved: "Ų†Ų‚Ų„ Ų‡Ø°Ø§ Ø§Ų„Ų…ØŗØĒØŽØ¯Ų… Ø­ØŗØ§Ø¨Ų‡:"
accountMovedShort: "ØąŲØ­Ų„ Ų‡Ø°Ø§ Ø§Ų„Ø­ØŗØ§Ø¨."
operationForbidden: "ØšŲ…Ų„ŲŠØŠ Ų…Ų…Ų†ŲˆØšØŠ"
forceShowAds: "ØŖØ¸Ų‡Øą Ø§Ų„ØĨØšŲ„Ø§Ų†Ø§ØĒ Ø§Ų„ØĒØŦØ§ØąŲŠØŠ داØĻŲ…Ø§"
reactionsList: "Ø§Ų„ØĒŲØ§ØšŲ„Ø§ØĒ"
renotesList: "ØĨؚاداØĒ Ø§Ų„Ų†Ø´Øą"
notificationDisplay: "ØĨØ´ØšØ§ØąØ§ØĒ"
leftTop: "ØŖØšŲ„Ų‰ Ø§Ų„ŲŠØŗØ§Øą"
rightTop: "ØŖØšŲ„Ų‰ Ø§Ų„ŲŠŲ…ŲŠŲ†"
leftBottom: "ØŖØŗŲŲ„ Ø§Ų„ŲŠØŗØ§Øą"
rightBottom: "ØŖØŗŲŲ„ Ø§Ų„ŲŠŲ…ŲŠŲ†"
stackAxis: "اØĒØŦØ§Ų‡ Ø§Ų„ØĒŲƒØ¯ŲŠØŗ"
vertical: "ØšŲ…ŲˆØ¯ŲŠ"
horizontal: "ØŦØ§Ų†Ø¨ŲŠ"
position: "Ø§Ų„Ų…ŲˆØļØš"
serverRules: "Ų‚ŲˆØ§Ų†ŲŠŲ† Ø§Ų„ØŽØ§Ø¯Ų…"
pleaseConfirmBelowBeforeSignup: "ØąØŦØ§ØĄŲ‹ ŲˆØ§ŲŲ‚ ØšŲ„Ų‰ Ų…Ø§ ŲŠŲ„ŲŠ Ų‚Ø¨Ų„ Ø§Ų„ØĒØŗØŦŲŠŲ„."
pleaseAgreeAllToContinue: "Ų„Ų„Ų…ØĒابؚ؊ ŲˆØ§ŲŲ‚ ØšŲ„Ų‰ Ø§Ų„Ø­Ų‚ŲˆŲ„ ØŖØšŲ„Ø§Ų‡."
continue: "Ų…ØĒابؚ؊"
preservedUsernames: "ØŖØŗŲ…Ø§ØĄ Ø§Ų„Ų…ØŗØĒØŽØ¯Ų…ŲŠŲ† Ø§Ų„Ų…Ø­ØŦŲˆØ˛ØŠ"
preservedUsernamesDescription: "Ų‚Ø§ØĻŲ…ØŠ Ø¨ØŖØŗŲ…Ø§ØĄ Ø§Ų„Ų…ØŗØĒØŽØ¯Ų…ŲŠŲ† Ø§Ų„Ų…Ø­ØŦŲˆØ˛ØŠ ŲƒŲ„ŲŒ ؁؊ ØŗØˇØą. Ų„Ų† ŲŠŲŲ‚Ø¨Ų„ Ø§Ų„ØĒØŗØŦŲŠŲ„ Ø¨Ų‡Ø°Ų‡ Ø§Ų„ØŖØŗŲ…Ø§ØĄ ŲˆØŗØĒØ¨Ų‚Ų‰ Ų…Ø­ØĩŲˆØąØŠ ØšŲ„Ų‰ Ø§Ų„ØĒØŗØŦŲŠŲ„ Ø§Ų„ŲŠØ¯ŲˆŲŠ Ø¨ŲˆØ§ØŗØˇØŠ Ø§Ų„Ų…Ø¯ŲŠØąŲŠŲ†. Ų„Ų† ؊ØĒØŖØĢØą Ø§Ų„Ų…ØŗØĒØŽØ¯Ų…ŲˆŲ† Ø§Ų„Ø°ŲŠŲ† ŲŠŲ…Ų„ŲƒŲˆŲ† Ų‡Ø°Ų‡ Ø§Ų„ØŖØŗŲ…Ø§ØĄ ØŗŲ„ŲŲ‹Ø§."
createNoteFromTheFile: "ØŖŲ†Ø´ØĻ Ų…Ų„Ø§Ø­Ø¸ØŠ Ų…Ų† Ų‡Ø°Ø§ Ø§Ų„Ų…Ų„Ų"
archive: "Ø§Ų„ØŖØąØ´ŲŠŲ"
channelArchiveConfirmTitle: "ØŖØĒØąŲŠØ¯ ØŖØąØ´ŲØĒ {name}؟"
channelArchiveConfirmDescription: "Ų„Ų† ŲŠŲ…ŲƒŲ†Ųƒ Ų†Ø´Øą Ų…Ų„Ø§Ø­Ø¸Ø§ØĒ ؁؊ Ø§Ų„Ų‚Ų†Ø§ØŠ Ø§Ų„Ų…ØŖØąØ´ŲØŠ ŲˆŲ„Ų† ØĒØ¸Ų‡Øą ؁؊ Ų‚Ø§ØĻŲ…ØŠ Ø§Ų„Ų‚Ų†ŲˆØ§ØĒ ŲˆŲ„Ø§ ؁؊ Ų†ØĒاØĻØŦ Ø§Ų„Ø¨Ø­ØĢ."
thisChannelArchived: "ØŖŲØąØ´ŲØĒ Ų‡Ø°Ų‡ Ø§Ų„Ų‚Ų†Ø§ØŠ."
displayOfNote: "ØšØąØļ Ø§Ų„Ų…Ų„Ø§Ø­Ø¸ØŠ"
initialAccountSetting: "ØĨؚداد Ø§Ų„Ų…Ų„Ų Ø§Ų„Ø´ØŽØĩ؊"
youFollowing: "Ų…ØĒØ§Ø¨ŲŽØš"
preventAiLearning: "Ų…Ų†Øš Ø§ØŗØĒØŽØ¯Ø§Ų… Ø§Ų„Ø¨ŲŠØ§Ų†Ø§ØĒ ؁؊ ØĒØšŲ„ŲŠŲ… Ø§Ų„ØĸŲ„ØŠ"
options: "ØŽŲŠØ§ØąØ§ØĒ"
specifyUser: "Ų…ØŗØĒØŽØ¯Ų… Ų…Ø­Ø¯Ø¯"
failedToPreviewUrl: "ØĒØĒØšØ°Øą Ø§Ų„Ų…ØšØ§ŲŠŲ†ØŠ"
update: "Ø­Ø¯ŲŲ‘ØĢ"
rolesThatCanBeUsedThisEmojiAsReaction: "Ø§Ų„ØŖØ¯ŲˆØ§Øą Ø§Ų„ØĒ؊ ŲŠŲØŗŲ…Ø­ Ų„ØŖØĩØ­Ø§Ø¨Ų‡Ø§ Ø§ØŗØĒØŽØ¯Ø§Ų… Ų‡Ø°Ø§ اØĨŲŠŲ…ŲˆØŦ؊ ؁؊ Ø§Ų„Ų„ØĒŲØ§ØšŲ„"
rolesThatCanBeUsedThisEmojiAsReactionEmptyDescription: "ØĨذا Ų„Ų… ØĒحدد Ø¯ŲˆØąŲ‹Ø§ ŲŠŲ…ŲƒŲ† Ų„Ų„ØŦŲ…ŲŠØš Ø§ØŗØĒØŽØ¯Ø§Ų… Ų‡Ø°Ø§ Ø§Ų„ØĨŲŠŲ…ŲˆØŦ؊ ؁؊ Ø§Ų„ØĒŲØ§ØšŲ„."
rolesThatCanBeUsedThisEmojiAsReactionPublicRoleWarn: "؊ØŦب ØŖŲ† ØĒŲƒŲˆŲ† Ø§Ų„ØŖØ¯ŲˆØ§Øą ØšŲ„Ų†ŲŠØŠ."
cancelReactionConfirm: "ØŖØĒØąŲŠØ¯ Ø­Ø°Ų ØĒŲØ§ØšŲ„ŲƒØŸ"
changeReactionConfirm: "ØŖØĒØąŲŠØ¯ ØĒØšØ¯ŲŠŲ„ ØĒŲØ§ØšŲ„ŲƒØŸ"
later: "Ų„Ø§Ø­Ų‚Ø§Ų‹"
goToMisskey: "Ų„Ų…ŲŠØŗŲƒŲŠ"
additionalEmojiDictionary: "Ų‚ŲˆØ§Ų…ŲŠØŗ ØĨŲŠŲ…ŲˆØŦ؊ ØĨØļØ§ŲŲŠØŠ"
installed: "Ų…ŲØĢبØĒ"
enableServerMachineStats: "Ų†Ø´Øą ØĨØ­ØĩاØĻŲŠØ§ØĒ ØšØĒاد Ø§Ų„ØŽØ§Ø¯Ų…"
turnOffToImprovePerformance: "ØĒŲØšŲŠŲ„Ų‡ Ų‚Ø¯ ŲŠØ˛ŲŠØ¯ Ø§Ų„ØŖØ¯Ø§ØĄ."
createInviteCode: "ŲˆŲ„ŲŲ‘Ø¯ Ø¯ØšŲˆØŠ"
inviteCodeCreated: "ŲˆŲ„ŲŲ‘Ø¯ØĒ Ø¯ØšŲˆØŠ"
inviteLimitExceeded: "؈ØĩŲ„ØĒŲŽ Ų„Ø­Ø¯ ؚدد Ø§Ų„Ø¯ØšŲˆØ§ØĒ Ø§Ų„Ų…ØŗŲ…ŲˆØ­ Ų„Ųƒ ØĒŲˆŲ„ŲŠØ¯Ų‡Ø§."
createLimitRemaining: "حد ؚدد Ø§Ų„Ø¯ØšŲˆØ§ØĒ: {limit} Ø¯ØšŲˆØŠ"
expirationDate: "ØĒØ§ØąŲŠØŽ Ø§Ų†ØĒŲ‡Ø§ØĄ Ø§Ų„ØĩŲ„Ø§Ø­ŲŠØŠ"
noExpirationDate: "Ų„Ø§ Ų†Ų‡Ø§ŲŠØŠ Ų„ØĩŲ„Ø§Ø­ŲŠØĒŲ‡Ø§"
inviteCodeUsedAt: "Ø§ŲØŗØĒØŽØ¯Ų… ØąŲ…Ø˛ Ø§Ų„Ø¯ØšŲˆØŠ ؁؊"
registeredUserUsingInviteCode: "Ø§ŲØŗØĒØŽØ¯Ų… ØąŲ…Ø˛ Ø§Ų„Ø¯ØšŲˆØŠ"
unused: "ØēŲŠØą Ų…ØŗØĒØšŲ…ŲŽŲ„"
expired: "Ų…Ų†ØĒŲ‡ŲŠØŠ ØĩŲ„Ø§Ø­ŲŠØĒŲ‡"
icon: "Ø§Ų„ØĩŲˆØąØŠ Ø§Ų„ØąŲ…Ø˛ŲŠØŠ"
replies: "ØąØ¯"
renotes: "ØŖØšØ¯ Ø§Ų„Ų†Ø´Øą"
sourceCode: "Ø§Ų„Ø´ŲØąØŠ Ø§Ų„Ų…ØĩØ¯ØąŲŠØŠ"
flip: "Ø§Ų‚Ų„Ø¨"
lastNDays: "ØĸØŽØą {n} ØŖŲŠØ§Ų…"
surrender: "ØŖŲ„Øēؐ"
postForm: "ØŖŲ†Ø´ØĻ Ų…Ų„Ø§Ø­Ø¸ØŠ"
information: "ØšŲ†"
inMinutes: "د"
inDays: "؊"
widgets: "Ø§Ų„ØĒØˇØ¨ŲŠŲ‚Ø§ØĒ Ø§Ų„Ų…ŲØĩØēŲ‘ØąØŠ"
presets: "ØĨؚداداØĒ Ų…ØŗØ¨Ų‚ØŠ"
_imageEditing:
_vars:
filename: "Ø§ØŗŲ… Ø§Ų„Ų…Ų„Ų"
_imageFrameEditor:
font: "Ø§Ų„ØŽØˇ"
fontSerif: "Serif"
fontSansSerif: "Sans Serif"
_chat:
invitations: "Ø¯ØšŲˆØŠ"
noHistory: "Ø§Ų„ØŗØŦŲ„ ŲØ§ØąØē"
members: "Ø§Ų„ØŖØšØļØ§ØĄ"
home: "Ø§Ų„ØąØĻŲŠØŗŲŠ"
send: "ØŖØąØŗŲ„"
_delivery:
stop: "Ų…ŲØšŲ„Ų‘Ų‚"
_initialAccountSetting:
accountCreated: "Ų†ØŦØ­ ØĨŲ†Ø´Ø§ØĄ Ø­ØŗØ§Ø¨Ųƒ!"
letsStartAccountSetup: "ØĨذا ŲƒŲ†ØĒ ØŦØ¯ŲŠØ¯Ų‹Ø§ Ų„Ų†ØšØ¯Ų‘ Ø­ØŗØ§Ø¨Ųƒ Ø§Ų„Ø´ØŽØĩ؊."
letsFillYourProfile: "ØŖŲˆŲ„Ų‹Ø§ Ų„Ų†ØšØ¯ Ų…Ų„ŲŲƒ Ø§Ų„Ø´ØŽØĩ؊."
profileSetting: "ØĨؚداداØĒ Ø§Ų„Ų…Ų„Ų Ø§Ų„Ø´ØŽØĩ؊"
privacySetting: "ØĨؚداداØĒ Ø§Ų„ØŽØĩ؈ØĩŲŠØŠ"
theseSettingsCanEditLater: "ŲŠŲ…ŲƒŲ†Ųƒ ØĒØēŲŠŲŠØą Ų‡Ø°Ų‡ Ø§Ų„ØĨؚداداØĒ Ų„Ø§Ø­Ų‚Ų‹Ø§."
skipAreYouSure: "ØŖØĒØąŲŠØ¯ ØĒØŽØˇŲŠ ØĨؚداد Ø§Ų„Ų…Ų„Ų Ø§Ų„Ø´ØŽØĩŲŠØŸ"
laterAreYouSure: "ØŖØĒØąŲŠØ¯ ØĨؚداد Ø§Ų„Ų…Ų„Ų Ø§Ų„Ø´ØŽØĩ؊ Ų„Ø§Ø­Ų‚Ų‹Ø§ØŸ"
_serverRules:
description: "Ų…ØŦŲ…ŲˆØšØŠ Ų…Ų† Ø§Ų„Ų‚ŲˆØ§ØšØ¯ Ų„ØšØąØļŲ‡Ø§ ØšŲ†Ø¯ Ø§Ų„ØĒØŗØŦŲŠŲ„ØŒ Ų…Ų† Ø§Ų„Ų…ØŗØĒØ­ØŗŲ† ؃ØĒاب؊ Ų…Ų„ØŽØĩŲ Ų„Ų„Ø´ØąŲˆØˇ Ø§Ų„ØŽØ¯Ų…ØŠ."
_accountMigration:
moveFrom: "Ø§Ų†Ų‚Ų„ Ø­ØŗØ§Ø¨Ų‹Ø§ ØĸØŽØą Ų„Ų‡Ø°Ø§ Ø§Ų„Ø­ØŗØ§Ø¨"
moveFromLabel: "Ø§Ų„Ø­ØŗØ§Ø¨ Ø§Ų„ØŖØĩŲ„ŲŠ #{n}"
moveTo: "Ø§Ų†Ų‚Ų„ Ų‡Ø°Ø§ Ø§Ų„Ø­ØŗØ§Ø¨ Ų„Ø­ØŗØ§Ø¨ ØĸØŽØą"
moveToLabel: "Ø§Ų„Ø­ØŗØ§Ø¨ Ø§Ų„ŲˆØŦŲ‡ØŠ:"
moveCannotBeUndone: "Ų„Ø§ ŲŠŲ…ŲƒŲ† Ø§Ų„ØĒØąØ§ØŦØš ØšŲ† Ų†Ų‚Ų„ Ø§Ų„Ø­ØŗØ§Ø¨."
movedTo: "Ø§Ų„Ø­ØŗØ§Ø¨ Ø§Ų„ŲˆØŦŲ‡ØŠ:"
_achievements:
_types:
_notes1:
description: "Ø§Ų†Ø´Øą Ų…Ų„Ø§Ø­Ø¸ØĒ؃ Ø§Ų„ØŖŲˆŲ„Ų‰"
flavor: "ØĒŲ…ØĒØš Ø¨Ø§ØŗØĒØŽØ¯Ø§Ų… Ų…ŲŠØŗŲƒŲŠ!"
_notes10:
title: "بؚØļ Ø§Ų„Ų…Ų„Ø§Ø­Ø¸Ø§ØĒ"
description: "Ø§Ų†Ø´Øą 10 Ų…Ų„Ø§Ø­Ø¸Ø§ØĒ"
_notes100:
title: "؃ØĢŲŠØą Ų…Ų† Ø§Ų„Ų…Ų„Ø§Ø­Ø¸Ø§ØĒ"
description: "Ø§Ų†Ø´Øą 100 Ų…Ų„Ø§Ø­Ø¸ØŠ"
_notes500:
description: "Ø§Ų†Ø´Øą 500 Ų…Ų„Ø§Ø­Ø¸ØŠ"
_notes1000:
title: "ØŦØ¨Ų„ Ų…Ų„Ø§Ø­Ø¸Ø§ØĒ"
description: "Ø§Ų†Ø´Øą 1000 Ų…Ų„Ø§Ø­Ø¸ØŠ"
_notes5000:
description: "Ø§Ų†Ø´Øą 5000 Ų…Ų„Ø§Ø­Ø¸ØŠ"
_notes10000:
description: "Ø§Ų†Ø´Øą 10000 Ų…Ų„Ø§Ø­Ø¸ØŠ"
_notes20000:
title: "ØŖØąŲŠØ¯...Ų…Ų„Ø§Ø­Ø¸Ø§ØĒ...ØŖŲƒØĢØą"
description: "Ø§Ų†Ø´Øą 20000 Ų…Ų„Ø§Ø­Ø¸ØŠ"
_notes30000:
title: "Ų…Ų„Ø§Ø­Ø¸Ø§ØĒ ŲˆŲ…Ų„Ø§Ø­Ø¸Ø§ØĒ ŲˆŲ…Ų„Ø§Ø­Ø¸Ø§ØĒ"
description: "Ø§Ų†Ø´Øą 30000 Ų…Ų„Ø§Ø­Ø¸ØŠ"
_notes40000:
title: "Ų…ØĩŲ†Øš Ų…Ų„Ø§Ø­Ø¸Ø§ØĒ"
description: "Ø§Ų†Ø´Øą 40000 Ų…Ų„Ø§Ø­Ø¸ØŠ"
_notes50000:
title: "ŲƒŲˆŲƒØ¨ Ų…Ų„Ø§Ø­Ø¸Ø§ØĒ"
description: "Ø§Ų†Ø´Øą 50000 Ų…Ų„Ø§Ø­Ø¸ØŠ"
_notes60000:
title: "Ų†ØŦŲ… Ų…Ų„Ø§Ø­Ø¸Ø§ØĒ"
description: "Ø§Ų†Ø´Øą 60000 Ų…Ų„Ø§Ø­Ø¸ØŠ"
_notes70000:
title: "ØĢŲ‚Ø¨ ØŖØŗŲˆØ¯ Ų„Ų„Ų…Ų„Ø§Ø­Ø¸Ø§ØĒ"
description: "Ø§Ų†Ø´Øą 70000 Ų…Ų„Ø§Ø­Ø¸ØŠ"
_notes80000:
title: "Ų…ØŦØąØŠ Ų…Ų„Ø§Ø­Ø¸Ø§ØĒ"
description: "Ø§Ų†Ø´Øą 80000 Ų…Ų„Ø§Ø­Ø¸ØŠ"
_notes90000:
title: "ŲƒŲˆŲ’Ų† Ų…Ų„Ø§Ø­Ø¸Ø§ØĒ"
description: "Ø§Ų†Ø´Øą 90000 Ų…Ų„Ø§Ø­Ø¸ØŠ"
_notes100000:
title: "ŲƒŲ„ Ų…Ų„Ø§Ø­Ø¸Ø§ØĒ؃ Ų„Ų†Ø§"
description: "Ø§Ų†Ø´Øą 100000 Ų…Ų„Ø§Ø­Ø¸ØŠ"
flavor: "Ø­Ų‚Ų‹Ø§ Ų„Ø¯ŲŠŲƒ Ø§Ų„ŲƒØĢŲŠØą Ų…Ų† Ø§Ų„Ų‚ØĩØĩ"
_login3:
title: "Ų…Ø¨ØĒØ¯ØŖ I"
_noteFavorited1:
description: "؁Øļ؋ؐ؄ Ų…Ų„Ø§Ø­Ø¸ØĒ؃ Ø§Ų„ØŖŲˆŲ„Ų‰"
_myNoteFavorited1:
title: "ØŗØ§ØšŲ Ų„Ų„Ų†ØŦŲˆŲ…"
description: "ØŖØšØŦب Ø´ØŽØĩ ØĸØŽØą بØĨØ­Ø¯Ų‰ Ų…Ų„Ø§Ø­Ø¸Ø§ØĒ؃"
_profileFilled:
title: "Ų…ØŗØĒؚد"
description: "ØŖØšØ¯Ų‘ Ø­ØŗØ§Ø¨Ųƒ"
_markedAsCat:
title: "ØŖŲ†Ø§ Ų‚Øˇ"
_role:
new: "Ø¯ŲˆØą ØŦØ¯ŲŠØ¯"
edit: "Ø­ØąØą Ø§Ų„ØŖØ¯ŲˆØ§Øą"
name: "Ø§ØŗŲ… Ø§Ų„Ø¯ŲˆØą"
description: "؈Øĩ؁ Ø§Ų„Ø¯ŲˆØą"
permission: "ØŖØ°ŲˆŲ†Ø§ØĒ Ø§Ų„Ø¯ŲˆØą"
assignTarget: "Ų†ŲˆØš Ø§Ų„ØĨØŗŲ†Ø§Ø¯"
condition: "Ø§Ų„Ø´ØąØˇ"
options: "ØŽŲŠØ§ØąØ§ØĒ"
policies: "Ø§Ų„ØŗŲŠØ§ØŗØŠ Ø§Ų„ØšØ§Ų…ØŠ"
priority: "Ø§Ų„ØŖŲˆŲ„ŲˆŲŠØŠ"
_priority:
low: "Ų…Ų†ØŽŲØļØŠ"
middle: "Ų…ØĒŲˆØŗØˇ"
high: "ØšØ§Ų„ŲŠØŠ"
_options:
canManageCustomEmojis: "ØĨØ¯Ø§ØąØŠ Ø§Ų„ØĨŲŠŲ…ŲˆØŦ؊ Ø§Ų„Ų…ØŽØĩØĩØŠ"
pinMax: "حد ؚدد Ø§Ų„Ų…Ų„Ø§Ø­Ø¸Ø§ØĒ Ø§Ų„Ų…ØĢبØĒØŠ"
_condition:
isLocal: "Ų…ØŗØĒØŽØ¯Ų… Ų…Ø­Ų„ŲŠ"
isRemote: "Ų…ØŗØĒØŽØ¯Ų… Ø¨ØšŲŠØ¯"
_emailUnavailable:
used: "Ų‡Ø°Ø§ Ø§Ų„Ø¨ØąŲŠØ¯ Ø§Ų„ØĨŲ„ŲƒØĒØąŲˆŲ†ŲŠ Ų…ØŗØĒØŽØ¯Ų…"
format: "Øĩ؊ØēØŠ Ø§Ų„Ø¨ØąŲŠØ¯ Ø§Ų„ØĨŲ„ŲƒØĒØąŲˆŲ†ŲŠ ØēŲŠØą ØĩØ§Ų„Ø­ØŠ"
@@ -1168,10 +849,6 @@ _plugin:
install: "ØĢØ¨Ų‘ØĒ ØĨØļØ§ŲØ§ØĒ"
installWarn: "ØąØŦØ§ØĄŲ‹ Ų„Ø§ ØĒØĢبØĒ ØĨØļØ§ŲØ§ØĒ ØēŲŠØą Ų…ŲˆØĢŲˆŲ‚ØŠ."
manage: "ØĨØ¯Ø§ØąØŠ Ø§Ų„ØĨØļØ§ŲØ§ØĒ"
viewSource: "Ø§Ø¸Ų‡Øą Ø§Ų„Ų…ØĩØ¯Øą"
_preferencesBackups:
createdAt: "ØĒŲ… ØĨŲ†Ø´Ø§Ø¤Ų‡: {date} {time}"
updatedAt: "ØĸØŽØą ØĒØ­Ø¯ŲŠØĢ: {date} {time}"
_registry:
scope: "Ø§Ų„Ø­ŲŠŲ‘Ø˛"
key: "؅؁ØĒاح"
@@ -1187,6 +864,10 @@ _aboutMisskey:
donate: "ØĒØ¨ØąØš Ų„Ų…ŲŠØŗŲƒŲŠ"
morePatrons: "Ų†Ø­Ų† Ų†Ų‚Ø¯Øą Ø§Ų„Ø¯ØšŲ… Ø§Ų„Ø°ŲŠ Ų‚Ø¯Ų…Ų‡ Ø§Ų„ØšØ¯ŲŠØ¯ Ų…Ų† Ø§Ų„ØŖØ´ØŽØ§Øĩ Ø§Ų„Ø°ŲŠŲ† Ų„Ų… Ų†Ø°ŲƒØąŲ‡Ų…. Ø´ŲƒØąŲ‹Ø§ Ų„ŲƒŲ… đŸĨ°"
patrons: "Ø§Ų„Ø¯Ø§ØšŲ…ŲˆŲ†"
_nsfw:
respect: "Ø§ØŽŲ Ø§Ų„ŲˆØŗØ§ØĻØˇ ذاØĒ Ø§Ų„Ų…Ø­ØĒŲˆŲ‰ Ø§Ų„Ø­ØŗØ§Øŗ"
ignore: "Ø§ØšØąØļ Ø§Ų„ŲˆØŗØ§ØĻØˇ ذاØĒ Ø§Ų„Ų…Ø­ØĒŲˆŲ‰ Ø§Ų„Ø­ØŗØ§Øŗ"
force: "Ø§ØŽŲ ŲƒŲ„ Ø§Ų„ŲˆØŗØ§ØĻØˇ"
_instanceTicker:
none: "Ų„Ø§ ØĒØ¸Ų‡ØąŲ‡ بØĒاØĒŲ‹Ø§"
remote: "ØŖØ¸Ų‡Øą Ų„Ų„Ų…ØŗØĒØŽØ¯Ų…ŲŠŲ† Ø§Ų„Ø¨ŲØšØ§Ø¯"
@@ -1212,6 +893,11 @@ _wordMute:
muteWords: "Ø§Ų„ŲƒŲ„Ų…Ø§ØĒ Ø§Ų„Ų…Ø­Ø¸ŲˆØąØŠ"
muteWordsDescription: "Ø§ŲØĩŲ„ Ø¨ŲŠŲ†Ų‡Ų… Ø¨Ų…ØŗØ§ŲØŠ Ų„Ø§ØŗØĒØŽØ¯Ø§Ų… Ų…ØšØ§Ų…Ų„ \"؈\" ØŖŲˆ Ø¨ØŗØˇØą Ų„Ø§ØŗØĒØŽØ¯Ø§Ų… Ų…ØšØ§Ų…Ų„ \"ØŖŲˆ\"."
muteWordsDescription2: "احØĩØą Ø§Ų„ŲƒŲ„Ų…Ø§ØĒ Ø§Ų„Ų…ŲØĒØ§Ø­ŲŠØŠ Ø¨ŲŠŲ† Ø¨ŲŠŲ† Ø´ØąØˇØĒŲŠŲ† Ų…Ø§ØĻŲ„ØĒŲŠŲ† Ų„Ø§ØŗØĒØŽØ¯Ø§Ų…Ų‡Ø§ ؃ØĒØšØ§Ø¨ŲŠØą Ų†Ų…ØˇŲŠØŠ"
softDescription: "Ø§ØŽŲ Ø§Ų„Ų…Ų„Ø§Ø­Ø¸Ø§ØĒ Ø§Ų„ØĒ؊ ØĒØŗØĒ؈؁ Ø§Ų„Ø´ØąŲˆØˇ Ų…Ų† Ø§Ų„ØŽŲŠØˇ Ø§Ų„Ø˛Ų…Ų†ŲŠ."
hardDescription: "Ø§ØŽŲ Ø§Ų„Ų…Ų„Ø§Ø­Ø¸Ø§ØĒ Ø§Ų„ØĒ؊ ØĒØŗØĒ؈؁ Ø§Ų„Ø´ØąŲˆØˇ Ų…Ų† Ø§Ų„ØŽŲŠØˇ Ø§Ų„Ø˛Ų…Ų†ŲŠ.Ø¨Ø§Ų„ØĨØļØ§ŲØŠ ØĨŲ„Ų‰ ØŖŲ† Ų‡Ø°Ų‡ Ø§Ų„Ų…Ų„Ø§Ø­Ø¸Ø§ØĒ ØŗØĒØ¨Ų‚Ų‰ Ų…ØŽŲŲŠØŠ Ø­ØĒŲ‰ ؈ØĨŲ† ØĒØēŲŠØąØĒ Ø§Ų„Ø´ØąŲˆØˇ."
soft: "Ų„ŲŠŲ†ØŠ"
hard: "Ų‚Ø§ØŗŲŠØŠ"
mutedNotes: "Ø§Ų„Ų…Ų„Ø§Ø­Ø¸Ø§ØĒ Ø§Ų„Ų…ŲƒØĒŲˆŲ…ØŠ"
_instanceMute:
instanceMuteDescription: "Ų‡Ø°Ų‡ ØŗŲŠØ­ØŦب ŲƒŲ„ Ų…Ų„Ø§Ø­Ø¸Ø§ØĒ Ø§Ų„ØŽŲˆØ§Ø¯Ų… Ø§Ų„Ų…Ø­ØŦŲˆØ¨ØŠ ŲˆŲ…Ø´Ø§ØąŲƒØ§ØĒŲ‡Ø§ ŲˆØ§Ų„ØąØ¯ŲˆØ¯ ØšŲ„Ų‰ ØĒŲ„Ųƒ Ø§Ų„Ų…Ų„Ø§Ø­Ø¸Ø§ØĒ Ø­ØĒŲ‰ ؈ØĨŲ† ŲƒØ§Ų†ØĒ Ų…Ų† ØŽØ§Ø¯Ų… ØēŲŠØą Ų…Ø­ØŦŲˆØ¨."
instanceMuteDescription2: "Ų…Ø¯ØŽŲ„ØŠ Ų„ŲƒŲ„ ØŗØˇØą"
@@ -1249,6 +935,7 @@ _theme:
shadow: "Ø§Ų„Ø¸Ų„"
navBg: "ØŽŲ„ŲŲŠØŠ Ø§Ų„Ø´ØąŲŠØˇ Ø§Ų„ØŦØ§Ų†Ø¨ŲŠ"
navFg: "Ų†Øĩ Ø§Ų„Ø´ØąŲŠØˇ Ø§Ų„ØŦØ§Ų†Ø¨ŲŠ"
navHoverFg: "Ų†Øĩ Ø§Ų„Ø´ØąŲŠØˇ Ø§Ų„ØŦØ§Ų†Ø¨ŲŠ (ØšŲ†Ø¯ Ø§Ų„ØĒŲ…ØąŲŠØą ŲŲˆŲ‚Ų‡)"
link: "ØąØ§Ø¨Øˇ"
hashtag: "ŲˆØŗŲ…"
mention: "ØŖØ´Øą Ø§Ų„Ų‰"
@@ -1263,11 +950,17 @@ _theme:
buttonBg: "ØŽŲ„ŲŲŠØŠ Ø§Ų„ØŖØ˛ØąØ§Øą"
buttonHoverBg: "ØŽŲ„ŲŲŠØŠ Ø§Ų„ØŖØ˛ØąØ§Øą (ØšŲ†Ø¯ Ø§Ų„ØĒŲ…ØąŲŠØą ŲŲˆŲ‚Ų‡Ø§)"
inputBorder: "Ø­ŲˆØ§Ų Ø­Ų‚Ų„ Ø§Ų„ØĨØ¯ØŽØ§Ų„"
listItemHoverBg: "ØŽŲ„ŲŲŠØŠ ØšŲ†Ø§ØĩØą Ø§Ų„Ų‚Ø§ØĻŲ…ØŠ (ØšŲ†Ø¯ Ø§Ų„ØĒŲ…ØąŲŠØą ŲŲˆŲ‚Ų‡Ø§)"
driveFolderBg: "ØŽŲ„ŲŲŠØŠ Ų…ØŦŲ„Ø¯ Ų‚ØąØĩ Ø§Ų„ØĒØŽØ˛ŲŠŲ†"
messageBg: "ØŽŲ„ŲŲŠØŠ Ø§Ų„Ų…Ø­Ø§Ø¯ØĢØŠ"
_sfx:
note: "Ø§Ų„Ų…Ų„Ø§Ø­Ø¸Ø§ØĒ"
noteMy: "Ų…Ų„Ø§Ø­Ø¸ØĒ؊"
notification: "Ø§Ų„ØĨØ´ØšØ§ØąØ§ØĒ"
chat: "Ø§Ų„Ų…Ø­Ø§Ø¯ØĢØŠ"
chatBg: "Ø§Ų„Ų…Ø­Ø§Ø¯ØĢØŠ (Ø§Ų„ØŽŲ„ŲŲŠØŠ)"
antenna: "Ø§Ų„Ų‡ŲˆØ§ØĻŲŠØ§ØĒ"
channel: "ØĨØ´ØšØ§ØąØ§ØĒ Ø§Ų„Ų‚Ų†Ø§ØĒ"
_ago:
future: "Ø§Ų„Ų…ØŗØĒŲ‚Ø¨ŲŽŲ„"
justNow: "Ø§Ų„Ų„Ø­Ø¸ØŠ"
@@ -1284,6 +977,29 @@ _time:
minute: "د"
hour: "ØŗØ§"
day: "؊"
_tutorial:
title: "؃؊؁ ØĒØŗØĒØŽØ¯Ų… Misskey"
step1_1: "Ų…ØąØ­Ø¨Ų‹Ø§!"
step1_2: "ØĒØ¯ØšŲ‰ Ų‡Ø°Ų‡ Ø§Ų„ØĩŲØ­ØŠ 'Ø§Ų„ØŽŲŠØˇ Ø§Ų„Ø˛Ų…Ų†ŲŠ' ŲˆŲ‡ŲŠ ØĒØ­ŲˆŲŠ Ų…Ų„Ø§Ø­Ø¸Ø§ØĒ Ø§Ų„ØŖØ´ØŽØ§Øĩ Ø§Ų„Ø°ŲŠ ØĒØĒØ§Ø¨ØšŲ‡Ų… Ų…ØąØĒب؊ Ø­ØŗØ¨ ØĒØ§ØąŲŠØŽ Ų†Ø´ØąŲ‡Ø§."
step1_3: "ØŽŲŠØˇŲƒ Ø§Ų„Ø˛Ų…Ų†ŲŠ ŲØ§ØąØē Ø­Ø§Ų„ŲŠŲ‹Ø§ Ø¨Ų…Ø§ ØŖŲ†Ųƒ Ų„Ø§ ØĒØĒابؚ ØŖŲŠ Ø´ØŽØĩ ŲˆŲ„Ų… ØĒŲ†Ø´Øą ØŖŲŠ Ų…Ų„Ø§Ø­Ø¸ØŠ."
step2_1: "Ų„Ų†Ų†Ų‡ŲŠ ØĨؚداد Ų…Ų„ŲŲƒ Ø§Ų„Ø´ØŽØĩ؊ Ų‚Ø¨Ų„ ؃ØĒاب؊ Ų…Ų„Ø§Ø­Ø¸ØŠ ØŖŲˆ Ų…ØĒابؚ؊ ØŖØ´ØŽØ§Øĩ."
step2_2: "ØŖØšØˇØ§ØĄ Ų…ØšŲ„ŲˆŲ…Ø§ØĒ ØšŲ† Ø´ØŽØĩ؊ØĒ؃ ŲŠŲ…Ų†Ø­ Ų…Ų† Ų„Ų‡ Ų†ŲØŗ ØĨŲ‡ØĒŲ…Ø§Ų…Ø§ØĒ؃ ŲØąØĩØŠ Ų…ØĒابؚØĒ؃ ŲˆØ§Ų„ØĒŲØ§ØšŲ„ Ų…Øš Ų…Ų„Ø§Ø­Ø¸Ø§ØĒ؃."
step3_1: "Ų‡Ų„ ØŖŲ†Ų‡ŲŠØĒ ØĨؚداد Ø­ØŗØ§Ø¨ŲƒØŸ"
step3_2: "ØĨذا ØĒØ§Ų„ŲŠŲ‹Ø§ Ų„ØĒŲ†Ø´Øą Ų…Ų„Ø§Ø­Ø¸ØŠ. ØŖŲ†Ų‚Øą ØšŲ„Ų‰ ØŖŲŠŲ‚ŲˆŲ†ØŠ Ø§Ų„Ų‚Ų„Ų… ؁؊ ØŖØšŲ„Ų‰ Ø§Ų„Ø´Ø§Ø´ØŠ"
step3_3: "Ø§Ų…Ų„ØŖ Ø§Ų„Ų†Ų…ŲˆØ°ØŦ ŲˆØ§Ų†Ų‚Øą Ø§Ų„Ø˛ØąŲ‘ Ø§Ų„Ų…ŲˆØŦŲˆØ¯ ؁؊ ØŖØšŲ„Ų‰ Ø§Ų„ŲŠŲ…ŲŠŲ† Ų„Ų„ØĨØąØŗØ§Ų„."
step3_4: "Ų„ŲŠØŗ Ų„Ø¯ŲŠŲƒ Ų…Ø§ ØĒŲ‚ŲˆŲ„Ų‡ØŸ ØĨذا Ø§ŲƒØĒب \"Ø¨Ø¯ØŖØĒŲ Ø§ØŗØĒØŽØ¯Ų… Ų…ŲŠØŗŲƒŲŠ\"."
step4_1: "Ų‡Ų„ Ų†Ø´ØąØĒ Ų…Ų„Ø§Ø­Ø¸ØĒ؃ Ø§Ų„ØŖŲˆŲ„Ų‰ØŸ"
step4_2: "Ų…ØąØ­Ų‰! ŲŠŲ…ŲƒŲ†Ųƒ Ø§Ų„ØĸŲ† ØąØ¤ŲŠØŠ Ų…Ų„Ø§Ø­Ø¸ØĒ؃ ؁؊ Ø§Ų„ØŽŲŠØˇ Ø§Ų„Ø˛Ų…Ų†ŲŠ."
step5_1: "ŲˆØ§Ų„ØĸŲ†ØŒ Ų„Ų†ØŦØšŲ„ Ø§Ų„ØŽŲŠØˇ Ø§Ų„Ø˛Ų…Ų†ŲŠ ØŖŲƒØĢØą Ø­ŲŠŲˆŲŠØŠ ŲˆØ°Ų„Ųƒ Ø¨Ų…ØĒابؚ؊ بؚØļ Ø§Ų„Ų…ØŗØĒØŽØ¯Ų…ŲŠŲ†."
step5_2: "ØĒØšØąØļ ØĩŲØ­ØŠ {features} Ø§Ų„Ų…Ų„Ø§Ø­Ø¸Ø§ØĒ Ø§Ų„Ų…ØĒØ¯Ø§ŲˆŲ„ØŠ ؁؊ Ų‡Ø°Ø§ Ø§Ų„Ų…ØĢŲŠŲ„ ؈؊ØĒŲŠØ­ Ų„Ųƒ {Explore} Ø§Ų„ØšØĢŲˆØą ØšŲ„Ų‰ Ø§Ų„Ų…ØŗØĒØŽØ¯Ų…ŲŠŲ† Ø§Ų„ØąØ§ØĻØ¯ŲŠŲ†. اؚØĢØą ØšŲ„Ų‰ Ø§Ų„ØŖØ´ØŽØ§Øĩ Ø§Ų„Ø°ŲŠŲ† ؊ØĢŲŠØąŲˆŲ† ØĨŲ‡ØĒŲ…Ø§Ų…Ųƒ ؈ØĒØ§Ø¨ØšŲ‡Ų…!"
step5_3: "Ų„Ų…ØĒابؚ؊ Ų…ØŗØĒØŽØ¯Ų…ŲŠŲ† Ø§Ø¯ØŽŲ„ ؅؄؁؇؅ Ø§Ų„Ø´ØŽØĩ؊ Ø¨Ø§Ų„Ų†Ų‚Øą ØšŲ„Ų‰ ØĩŲˆØąØĒŲ‡Ų… Ø§Ų„Ø´ØŽØĩŲŠØŠ ØĢŲ… اØļØēØˇ Ø˛Øą 'ØĒابؚ'."
step5_4: "ØĨذا ŲƒØ§Ų† Ų„Ø¯Ų‰ Ø§Ų„Ų…ØŗØĒØŽØ¯Ų… ØąŲ…Ø˛ ؂؁؄ بØŦŲˆØ§Øą Ø§ØŗŲ…Ų‡ ، ؈ØŦب ØšŲ„ŲŠŲƒ Ø§Ų†ØĒØ¸Ø§ØąŲ‡ Ų„ŲŠŲ‚Ø¨Ų„ ØˇŲ„Ø¨ Ø§Ų„Ų…ØĒابؚ؊ ŲŠØ¯ŲˆŲŠŲ‹Ø§."
step6_1: "Ø§Ų„ØĸŲ† ØŗØĒØĒŲ…ŲƒŲ† Ų…Ų† ØąØ¤ŲŠØŠ Ų…Ų„Ø§Ø­Ø¸Ø§ØĒ Ø§Ų„Ų…ØŗØĒØŽØ¯Ų…ŲŠŲ† Ø§Ų„Ų…ØĒØ§Ø¨ŲŽØšŲŠŲ† ؁؊ Ø§Ų„ØŽŲŠØˇ Ø§Ų„Ø˛Ų…Ų†ŲŠ."
step6_2: "ŲŠŲ…ŲƒŲ†Ųƒ Ø§Ų„ØĒŲØ§ØšŲ„ Ø¨ØŗØąØšØŠ Ų…Øš Ø§Ų„Ų…Ų„Ø§Ø­Ø¸Ø§ØĒ ØšŲ† ØˇØąŲŠŲ‚ ØĨØļØ§ŲØŠ \"ØĒŲØ§ØšŲ„\"."
step6_3: "Ų„ØĨØļØ§ŲØŠ ØĒŲØ§ØšŲ„ Ų„Ų…Ų„Ø§Ø­Ø¸ØŠ ، Ø§Ų†Ų‚Øą ŲŲˆŲ‚ ØšŲ„Ø§Ų…ØŠ \"+\" ØŖØŗŲŲ„ Ų„Ų„Ų…Ų„Ø§Ø­Ø¸ØŠ ŲˆØ§ØŽØĒØą Ø§Ų„ØĨŲŠŲ…ŲˆØŦ؊ Ø§Ų„Ų…ØˇŲ„ŲˆØ¨."
step7_1: "Ų…Ø¨Ø§ØąŲƒ ! ØŖŲ†Ų‡ŲŠØĒ Ø§Ų„Ø¯ŲˆØąØŠ Ø§Ų„ØĒØšŲ„ŲŠŲ…ŲŠØŠ Ø§Ų„ØŖØŗØ§ØŗŲŠØŠ Ų„Ø§ØŗØĒØŽØ¯Ø§Ų… Ų…ŲŠØŗŲƒŲŠ."
step7_2: "ØĨذا ØŖØąØ¯ØĒ Ų…ØšØąŲØŠ Ø§Ų„Ų…Ø˛ŲŠØ¯ ØšŲ† Ų…ŲŠØŗŲƒŲŠ Ø˛Øą {help}."
step7_3: "Ø­Ø¸Ų‹Ø§ ØŗØšŲŠØ¯Ų‹Ø§ ŲˆØ§ØŗØĒŲ…ØĒØš Ø¨ŲˆŲ‚ØĒ؃ Ų…Øš Ų…ŲŠØŗŲƒŲŠ! 🚀"
_2fa:
alreadyRegistered: "ØŗØŦŲ„ØĒ ØŗŲ„ŲŲ‹Ø§ ØŦŲ‡Ø§Ø˛Ų‹Ø§ Ų„Ų„Ø§ØŗØĒ؊ØĢØ§Ų‚ Ø¨ØšØ§Ų…Ų„ŲŠŲ†."
step1: "ØŖŲˆŲ„Ų‹Ø§ ØĢØ¨Ų‘ØĒ ØĒØˇØ¨ŲŠŲ‚ Ø§ØŗØĒ؊ØĢØ§Ų‚ ØšŲ„Ų‰ ØŦŲ‡Ø§Ø˛Ųƒ (Ų…ØĢŲ„ {a} ؈{b})."
@@ -1322,7 +1038,6 @@ _permissions:
"read:gallery": "Ø§ØšØąØļ Ø§Ų„Ų…ØšØąØļ"
"write:gallery": "ØšØ¯Ų‘Ų„ Ø§Ų„Ų…ØšØąØļ"
"read:gallery-likes": "ŲŠØšØąØļ Ų…Ø§ ØŖØšØŦØ¨Ųƒ Ų…Ų† Ų…Ø´Ø§ØąŲƒØ§ØĒ Ø§Ų„Ų…ØšØąØļ"
"write:chat": "Ø§ŲƒØĒب ØŖŲˆ Ø§Ø­Ø°Ų ØąØŗØ§ØĻŲ„ Ų…Ø­Ø§Ø¯ØĢØŠ"
_auth:
shareAccess: "ØŖØĒØąŲŠØ¯ Ø§Ų„ØĒ؁؈؊Øļ Ų„Ų€ \"{name}\" Ø¨Ø§Ų„ŲˆØĩŲˆŲ„ Ų„Ø­ØŗØ§Ø¨ŲƒØŸ"
shareAccessAsk: "Ų‡Ų„ ØĒØŽŲˆŲ„ Ų„Ų‡Ø°Ø§ Ø§Ų„ØĒØˇØ¨ŲŠŲ‚ Ø§Ų„ŲˆØĩŲˆŲ„ Ų„Ø­ØŗØ§Ø¨ŲƒØŸ"
@@ -1362,7 +1077,6 @@ _widgets:
onlineUsers: "Ø§Ų„Ų…ØĒŲ‘ØĩŲ„ŲˆŲ†"
jobQueue: "Ų‚Ø§ØĻŲ…ØŠ Ø§Ų„Ø§Ų†ØĒØ¸Ø§Øą"
serverMetric: "ØĨØ­ØĩاØĻŲŠØ§ØĒ Ø§Ų„ØŽØ§Ø¯Ų…"
userList: "Ų‚Ø§ØĻŲ…ØŠ Ø§Ų„Ų…ØŗØĒØŽØ¯Ų…ŲŠŲ†"
_userList:
chooseList: "ا؎ØĒØą Ų‚Ø§ØĻŲ…ØŠ"
_cw:
@@ -1405,9 +1119,6 @@ _postForm:
replyPlaceholder: "ØąØ¯ ØšŲ„Ų‰ Ų‡Ø°Ų‡ Ø§Ų„Ų…Ų„Ø§Ø­Ø¸ØŠâ€Ļ"
quotePlaceholder: "Ø§Ų‚ØĒØ¨Øŗ Ų‡Ø°Ų‡ Ø§Ų„Ų…Ų„Ø§Ø­Ø¸ØŠâ€Ļ"
channelPlaceholder: "Ø§Ų†Ø´Øą ؁؊ Ų‚Ų†Ø§ØŠ..."
_howToUse:
visibility_title: "Ø§Ų„Ø¸Ų‡ŲˆØą"
menu_title: "Ø§Ų„Ų‚Ø§ØĻŲ…ØŠ"
_placeholders:
a: "Ų…Ø§ Ø§Ų„Ø°ŲŠ ØĒŲ†ŲˆŲŠ ŲØšŲ„Ų‡ØŸ"
b: "Ų…Ø§Ø°Ø§ ŲŠØ­Ø¯ØĢ Ø­ŲˆŲ„Ųƒ ؟"
@@ -1429,8 +1140,6 @@ _profile:
changeBanner: "ØēŲŠŲ‘Øą Ø§Ų„Ų„Ø§ŲØĒØŠ"
_exportOrImport:
allNotes: "ŲƒŲ„ Ø§Ų„Ų…Ų„Ø§Ø­Ø¸Ø§ØĒ"
favoritedNotes: " Ø§Ų„Ų…Ų„Ø§Ø­Ø¸Ø§ØĒ Ø§Ų„Ų…ŲØļŲ„ØŠ"
clips: "Ų…ŲØ´Ø¨Ųƒ"
followingList: "Ø§Ų„Ų…ØĒØ§Ø¨ŲŽØšŲˆŲ†"
muteList: "Ø§Ų„Ų…ØŗØĒØŽØ¯Ų…ŲˆŲ† Ø§Ų„Ų…ŲƒØĒŲˆŲ…ŲˆŲ†"
blockingList: "Ø§Ų„Ų…ØŗØĒØŽØ¯Ų…ŲˆŲ† Ø§Ų„Ų…Ø­ØŦŲˆØ¨ŲˆŲ†"
@@ -1449,8 +1158,6 @@ _charts:
notesTotal: "ØĨØŦŲ…Ø§Ų„ŲŠ Ø§Ų„Ų…Ų„Ø§Ø­Ø¸Ø§ØĒ"
filesIncDec: "ØĒØ¨Ø§ŲŠŲ† ؚدد Ø§Ų„Ų…Ų„ŲØ§ØĒ"
filesTotal: "Ø§Ų„ØšØ¯Ø¯ Ø§Ų„ØĨØŦŲ…Ø§Ų„ŲŠ Ų„Ų„Ų…Ų„ŲØ§ØĒ"
storageUsageIncDec: "Ø§Ų„ØĒØ¨Ø§ŲŠŲ† ؁؊ Ø§ØŗØĒØēŲ„Ø§Ų„ Ų…ØŗØ§Ø­ØŠ Ø§Ų„ØĒØŽØ˛ŲŠŲ†"
storageUsageTotal: "اØŦŲ…Ø§Ų„ŲŠ Ų…ØŗØ§Ø­ØŠ Ø§Ų„ØĒØŽØ˛ŲŠŲ† Ø§Ų„Ų…ØŗØĒØēŲ„ØŠ"
_instanceCharts:
requests: "Ø§Ų„ØˇŲ„Ø¨Ø§ØĒ"
users: "ØĒØ¨Ø§ŲŠŲ† ؚدد Ø§Ų„Ų…ØŗØĒØŽØ¯Ų…ŲŠŲ†"
@@ -1475,6 +1182,9 @@ _pages:
newPage: "ØŖŲ†Ø´ØĻ ØĩŲØ­ØŠ ØŦØ¯ŲŠØ¯ØŠ"
editPage: "ØšØ¯Ų‘Ų„ Ø§Ų„ØĩŲØ­ØŠ"
readPage: "Ų†ŲØ´Ų‘Øˇ ØšØąØļ Ø§Ų„Ų…ØĩØ¯Øą"
created: "Ų†ØŦØ­ ØĨŲ†Ø´Ø§ØĄ Ø§Ų„ØĩŲØ­ØŠ"
updated: "Ų†ØŦØ­ ØĒØšØ¯ŲŠŲ„ Ø§Ų„ØĩŲØ­ØŠ"
deleted: "Ų†ØŦØ­ Ø­Ø°Ų Ø§Ų„ØĩŲØ­ØŠ"
pageSetting: "ØĨؚداداØĒ Ø§Ų„ØĩŲØ­ØŠ"
nameAlreadyExists: "ØąØ§Ø¨Øˇ Ø§Ų„ØĩŲØ­ØŠ Ų…ŲˆØŦŲˆØ¯ Ų…ØŗØ¨Ų‚Ų‹Ø§"
invalidNameTitle: "ØąØ§Ø¨Øˇ Ø§Ų„ØĩŲØ­ØŠ Ų„ŲŠØŗ ØĩØ§Ų„Ø­Ų‹Ø§"
@@ -1493,7 +1203,7 @@ _pages:
url: "ØąØ§Ø¨Øˇ Ø§Ų„ØĩŲØ­ØŠ"
summary: "Ų…Ų„ØŽØĩ Ø§Ų„ØĩŲØ­ØŠ"
alignCenter: "ØĒŲˆØŗŲŠØˇ Ø§Ų„ØšŲ†Ø§ØĩØą"
hideTitleWhenPinned: "Ø§ØŽŲ ØšŲ†ŲˆØ§Ų† Ø§Ų„ØĩŲØ­ØŠ ØšŲ†Ø¯ ØĒØĢØ¨ŲŠØĒŲ‡Ø§ ؁؊ ؅؄؁ Ø§Ų„Ø´ØŽØĩ؊"
hideTitleWhenPinned: "Ø§ØŽŲ ØšŲ†ŲˆØ§Ų† Ø§Ų„ØĩŲØ­ØŠ ØšŲ†Ø¯ ØĒØ¯Ø¨ŲŠØŗŲ‡Ø§ ؁؊ ؅؄؁ Ø§Ų„Ø´ØŽØĩ؊"
font: "Ø§Ų„ØŽØˇ"
fontSerif: "Serif"
fontSansSerif: "Sans Serif"
@@ -1508,7 +1218,7 @@ _pages:
text: "Ų†Øĩ"
textarea: "Ø­Ų‚Ų„ Ų†Øĩ؊"
section: "Ų‚ØŗŲ…"
image: "ØĩŲˆØą"
image: "Ø§Ų„ØĩŲˆØą"
button: "Ø˛ØąŲ‘"
note: "Ų…Ų„Ø§Ø­Ø¸ØŠ Ų…ØļŲ…Ų‘Ų†ØŠ"
_note:
@@ -1523,101 +1233,48 @@ _notification:
fileUploaded: "Ų†ØŦØ­ ØąŲØš Ø§Ų„Ų…Ų„Ų"
youGotMention: "{name} ØŖØ´Ø§Øą ØĨŲ„ŲŠŲƒ"
youGotReply: "ØąØ¯Ų‘ ØšŲ„ŲŠŲƒ {name}"
youGotQuote: "Ø§Ų‚ØĒØ¨Øŗ {name} Ų…Ų†Ø´ŲˆØąŲƒ"
youRenoted: "ØŖØšØ§Ø¯ {name} Ų†Ø´Øą Ų…Ų†Ø´ŲˆØąŲƒ"
youGotQuote: "Ø§Ų‚ØĒØ¨Øŗ Ų…Ų†Ųƒ {name}"
youRenoted: "ØĨؚادØĒ Ų†Ø´Øą Ų…Ų† {name}"
youWereFollowed: "؊ØĒØ§Ø¨ØšŲƒ"
youReceivedFollowRequest: "ØĒŲ„Ų‚ŲŠØĒŲŽ ØˇŲ„Ø¨ Ų…ØĒابؚ؊"
yourFollowRequestAccepted: "Ų‚ŲØ¨Ų„ ØˇŲ„Ø¨ Ø§Ų„Ų…ØĒابؚ؊"
pollEnded: "Ø§Ų†ØĒŲ‡Ų‰ Ø§Ų„Ø§ØŗØĒØˇŲ„Ø§Øš"
pollEnded: "Ø¸Ų‡ØąØĒ Ų†ØĒاØĻØŦ Ø§Ų„Ø§ØŗØĒØˇŲ„Ø§Øš"
unreadAntennaNote: "Ų‡ŲˆØ§ØĻ؊ {name}"
_types:
all: "Ø§Ų„ŲƒŲ„"
follow: "Ų…ØĒØ§Ø¨ŲØšŲˆŲ† ØŦدد"
mention: "Ø§Ų„ØĨØ´Ø§ØąØ§ØĒ"
reply: "Ø§Ų„ØąØ¯ŲˆØ¯"
renote: "ØŖØšØ§Ø¯ Ø§Ų„Ų†Ø´Øą"
renote: "ØŖØšØ¯ Ø§Ų„Ų†Ø´Øą"
quote: "Ø§Ų„Ø§Ų‚ØĒØ¨Ø§ØŗØ§ØĒ"
reaction: "Ø§Ų„ØĒŲØ§ØšŲ„"
receiveFollowRequest: "ØˇŲ„Ø¨Ø§ØĒ Ø§Ų„Ų…ØĒابؚ؊"
reaction: "Ø§Ų„ØĒŲØ§ØšŲ„Ø§ØĒ"
receiveFollowRequest: "ØˇŲ„Ø¨Ø§ØĒ Ø§Ų„Ų…ØĒابؚ؊ Ø§Ų„Ų…ØĒŲ„Ų‚Ø§ØŠ"
followRequestAccepted: "ØˇŲ„Ø¨Ø§ØĒ Ø§Ų„Ų…ØĒابؚ؊ Ø§Ų„Ų…Ų‚Ø¨ŲˆŲ„ØŠ"
login: "؄ؐØŦ"
app: "ØĨØ´ØšØ§ØąØ§ØĒ Ø§Ų„ØĒØˇØ¨ŲŠŲ‚Ø§ØĒ Ø§Ų„Ų…ØąØĒØ¨ØˇØŠ"
_actions:
followBack: "ØĒØ§Ø¨ØšŲƒ Ø¨Ø§Ų„Ų…ØĢŲ„"
reply: "ØąØ¯"
renote: "ØŖØšØ¯ Ø§Ų„Ų†Ø´Øą"
_deck:
alwaysShowMainColumn: "ØŖØ¸Ų‡Øą Ø§Ų„ØšŲ…ŲˆØ¯ Ø§Ų„ØŖØŗØ§ØŗŲŠ داØĻŲ…Ų‹Ø§"
columnAlign: "Ų…Ø­Ø§Ø°Ø§ØŠ Ø§Ų„ØŖØšŲ…Ø¯ØŠ"
addColumn: "ØĨØļØ§ŲØŠ ØšŲ…ŲˆØ¯"
swapLeft: "Ø§Ų„ØĒØ­ØąŲŠŲƒ ØĨŲ„Ų‰ Ø§Ų„ŲŠØŗØ§Øą"
swapRight: "Ø§Ų„ØĒØ­ØąŲŠŲƒ ØĨŲ„Ų‰ Ø§Ų„ŲŠŲ…ŲŠŲ†"
swapUp: "Ø§Ų„ØĒØ­ØąŲŠŲƒ ØĨŲ„Ų‰ Ø§Ų„ØŖØšŲ„Ų‰"
swapDown: "Ø§Ų„ØĒØ­ØąŲŠŲƒ ØĨŲ„Ų‰ Ø§Ų„ØŖØŗŲŲ„"
profile: "Ø­ØŗØ§Ø¨ŲŠ Ø§Ų„Ø´ØŽØĩ؊"
newProfile: "؅؄؁ ØĒØšØąŲŠŲŲŠ ØŦØ¯ŲŠØ¯"
deleteProfile: "Ø­Ø°Ų Ø§Ų„Ų…Ų„Ų Ø§Ų„ØĒØšØąŲŠŲŲŠ"
alwaysShowMainColumn: "ØŖØ¸Ų‡Øą Ø§Ų„ØšŲ…ŲˆØ¯ Ø§Ų„ØąØĻŲŠØŗŲŠ داØĻŲ…Ų‹Ø§"
columnAlign: "Ø­Ø§Ø°Ų Ø§Ų„ØŖØšŲ…Ø¯ØŠ"
addColumn: "ØŖØļ؁ ØšŲ…ŲˆØ¯Ų‹Ø§"
swapLeft: "Ø­ØąŲ‘Ųƒ Ų„Ų„ŲŠØŗØ§Øą"
swapRight: "Ø­ØąŲ‘Ųƒ Ų„Ų„ŲŠŲ…ŲŠŲ†"
swapUp: "Ø­ØąŲ‘Ųƒ Ų„ØŖØšŲ„Ų‰"
swapDown: "Ø­ØąŲ‘Ųƒ Ų„ØŖØŗŲŲ„"
profile: "Ø§Ų„Ų…Ų„Ų Ø§Ų„Ø´ØŽØĩ؊"
_columns:
main: "Ø§Ų„ØąØĻŲŠØŗŲŠØŠ"
widgets: "Ø§Ų„ØĒØˇØ¨ŲŠŲ‚Ø§ØĒ Ø§Ų„Ų…ŲØĩØēŲ‘ØąØŠ"
main: "Ø§Ų„ØąØĻŲŠØŗŲŠ"
widgets: "Ø§Ų„ŲˆØ¯ØŦاØĒ"
notifications: "Ø§Ų„ØĨØ´ØšØ§ØąØ§ØĒ"
tl: "Ø§Ų„ØŽØˇ Ø§Ų„Ø˛Ų…Ų†ŲŠ"
tl: "Ø§Ų„ØŽŲŠØˇ Ø§Ų„Ø˛Ų…Ų†ŲŠ"
antenna: "Ø§Ų„Ų‡ŲˆØ§ØĻŲŠØ§ØĒ"
list: "Ø§Ų„Ų‚ŲˆØ§ØĻŲ…"
channel: "Ø§Ų„Ų‚Ų†ŲˆØ§ØĒ"
mentions: "Ø§Ų„ØĨØ´Ø§ØąØ§ØĒ"
direct: "Ų…Ø¨Ø§Ø´ØąØŠ"
_webhookSettings:
name: "Ø§Ų„Ø§ØŗŲ…"
active: "Ų…ŲŲØšŲ‘Ų„"
_events:
reaction: "ØšŲ†Ø¯ Ø§Ų„ØĒŲØ§ØšŲ„"
_abuseReport:
_notificationRecipient:
_recipientType:
mail: "Ø§Ų„Ø¨ØąŲŠØ¯ Ø§Ų„ØĨŲ„ŲƒØĒØąŲˆŲ†ŲŠ "
_moderationLogTypes:
suspend: "ØšŲ„ŲŲ‚"
deleteDriveFile: "Ø­ŲØ°Ų Ø§Ų„Ų…Ų„Ų"
deleteNote: "Ø­ŲØ°ŲØĒ Ø§Ų„Ų…Ų„Ø§Ø­Ø¸ØŠ"
createGlobalAnnouncement: "ØŖŲŲ†Ø´ØĻ ØĨØšŲ„Ø§Ų† ØšØ§Ų…"
createUserAnnouncement: "ØŖŲŲ†Ø´ØĻ ØĨØšŲ„Ø§Ų† Ų…ØŗØĒØŽØ¯Ų…"
updateGlobalAnnouncement: "Ø­ŲØ¯ØĢ ØĨØšŲ„Ø§Ų† ØšØ§Ų…"
updateUserAnnouncement: "Ø­ŲØ¯ØĢ ØĨØšŲ„Ø§Ų† Ų…ØŗØĒØŽØ¯Ų…"
resetPassword: "ØŖØšØ¯ ØĒØšŲŠŲŠŲ† ŲƒŲ„Ų…ØĒ؃ Ø§Ų„ØŗØąŲŠØŠ"
createInvitation: "ŲˆŲ„ŲŲ‘Ø¯ Ø¯ØšŲˆØŠ"
_reversi:
total: "Ø§Ų„Ų…ØŦŲ…ŲˆØš"
lookingForPlayer: "ŲŠØ¨Ø­ØĢ ØšŲ† ØŽØĩŲ…..."
gameCanceled: "ØŖŲŲ„Øē؊ØĒ Ø§Ų„Ų„ØšØ¨ØŠ."
opponentHasSettingsChanged: "ØēŲŠŲŽØą Ø§Ų„ØŽØĩŲ… ØĨؚدادØĒŲ‡."
showBoardLabels: "Ø§ØšØąØļ ØĒØąŲ‚ŲŠŲ… Ø§Ų„Øĩ؁؈؁ ŲˆØ§Ų„ØŖØšŲ…Ø¯ØŠ ØšŲ„Ų‰ Ø§Ų„Ų„ŲˆØ­"
useAvatarAsStone: "Ø­ŲˆŲŽŲ„ Ø§Ų„Ø­ØŦØ§ØąØŠ ØĨŲ„Ų‰ ØĩŲˆØą Ų…ØŗØĒØŽØ¯Ų…ŲŠŲ†"
_offlineScreen:
title: "ØēŲŠØą Ų…ØĒØĩŲ„ - ؊ØĒØšØ°Øą Ø§Ų„Ø§ØĒØĩØ§Ų„ Ø¨Ø§Ų„ØŽØ§Ø¯Ų…"
header: "؊ØĒØšØ°Øą Ø§Ų„Ø§ØĒØĩØ§Ų„ Ø¨Ø§Ų„ØŽØ§Ø¯Ų…"
_remoteLookupErrors:
_noSuchObject:
title: "ØēŲŠØą Ų…ŲˆØŦŲˆØ¯"
_search:
searchScopeAll: "Ø§Ų„ŲƒŲ„"
searchScopeLocal: "Ø§Ų„Ų…Ø­Ų„ŲŠ"
searchScopeUser: "Ų…ØŗØĒØŽØ¯Ų… Ų…Ø­Ø¯Ø¯"
_watermarkEditor:
opacity: "Ø§Ų„Ø´ŲØ§ŲŲŠØŠ"
scale: "Ø§Ų„Ø­ØŦŲ…"
text: "Ų†Øĩ"
position: "Ø§Ų„Ų…ŲˆØļØš"
type: "Ų†ŲˆØš"
image: "ØĩŲˆØą"
advanced: "Ų…ØĒŲ‚Ø¯Ų…"
_imageEffector:
_fxProps:
scale: "Ø§Ų„Ø­ØŦŲ…"
size: "Ø§Ų„Ø­ØŦŲ…"
offset: "Ø§Ų„Ų…ŲˆØļØš"
color: "Ø§Ų„Ų„ŲˆŲ†"
opacity: "Ø§Ų„Ø´ŲØ§ŲŲŠØŠ"
_qr:
showTabTitle: "Ø§Ų„Ų…Ø¸Ų‡Øą"
raw: "Ų†Øĩ"
name: "Ø§Ų„ØĨØŗŲ…"
active: "Ų…ŲØšŲ‘Ų„"

View File

@@ -2,7 +2,6 @@
_lang_: "āĻŦāĻžāĻ‚āϞāĻž"
headlineMisskey: "āύ⧋āϟ āĻŦā§āϝāĻžāĻŦāĻšāĻžāϰ āĻ•āϰ⧇ āϏāĻ‚āϝ⧁āĻ•ā§āϤ āύ⧇āϟāĻ“ā§ŸāĻžāĻ°ā§āĻ•"
introMisskey: "āĻ¸ā§āĻŦāĻžāĻ—āϤāĻŽ! āĻŽāĻŋāϏāĻ•āĻŋ āĻāĻ•āϟāĻŋ āĻ“āĻĒ⧇āύ āϏ⧋āĻ°ā§āϏ, āĻĄāĻŋāϏ⧇āĻ¨ā§āĻŸā§āϰāĻžāϞāĻžāχāϜāĻĄ āĻŽāĻžāχāĻ•ā§āϰ⧋āĻŦā§āϞāĻ—āĻŋāĻ‚ āĻĒāϰāĻŋāώ⧇āĻŦāĻžāĨ¤ \n\"āύ⧋āϟ\" āϤ⧈āϰāĻŋāϰ āĻŽāĻžāĻ§ā§āϝāĻŽā§‡ āϝāĻž āϘāϟāϛ⧇ āϤāĻž āϏāĻŦāĻžāϰ āϏāĻžāĻĨ⧇ āĻļā§‡ā§ŸāĻžāϰ āĻ•āϰ⧁āύ 📡\n\"āϰāĻŋāĻ…ā§āϝāĻžāĻ•āĻļāύ\" āϗ⧁āϞāĻŋāϰ āĻŽāĻžāĻ§ā§āϝāĻŽā§‡ āϝ⧇āϕ⧋āύ⧋ āύ⧋āϟ āϏāĻŽā§āĻĒāĻ°ā§āϕ⧇ āφāĻĒāύāĻžāϰ āĻ…āύ⧁āĻ­ā§‚āϤāĻŋ āĻŦā§āϝāĻžāĻ•ā§āϤ āĻ•āϰāϤ⧇ āĻĒāĻžāϰ⧇āύ 👍\nāĻāĻ•āϟāĻŋ āύāϤ⧁āύ āĻĻ⧁āύāĻŋ⧟āĻž āϘ⧁āϰ⧇ āĻĻ⧇āϖ⧁āύ 🚀\n"
poweredByMisskeyDescription: "{name} āĻšāϞ āĻ“āĻĒ⧇āύ āϏ⧋āĻ°ā§āϏ āĻĒā§āĻ˛ā§āϝāĻžāϟāĻĢāĻ°ā§āĻŽ <b>Misskey</b>-āĻāϰ āϏāĻžāĻ°ā§āĻ­āĻžāϰāϗ⧁āϞāĻŋāϰ āĻāĻ•āϟāĻŋ⧎"
monthAndDay: "{day}/{month}"
search: "āϖ⧁āρāϜ⧁āύ"
notifications: "āĻŦāĻŋāĻœā§āĻžāĻĒā§āϤāĻŋ"
@@ -13,14 +12,12 @@ fetchingAsApObject: "āĻĢ⧇āĻĄāĻŋāĻ­āĻžāĻ°ā§āϏ āĻĨ⧇āϕ⧇ āĻ–āĻŦāϰ āφāύ
ok: "āĻ āĻŋāĻ•"
gotIt: "āĻŦ⧁āĻā§‡āĻ›āĻŋ"
cancel: "āĻŦāĻžāϤāĻŋāϞ"
noThankYou: "āύāĻž, āϧāĻ¨ā§āϝāĻŦāĻžāĻĻ"
enterUsername: "āχāωāϜāĻžāϰāύ⧇āĻŽ āϞāĻŋāϖ⧁āύ"
renotedBy: "{user} āϰāĻŋāύ⧋āϟ āĻ•āϰ⧇āϛ⧇āύ"
noNotes: "āϕ⧋āύ āύ⧋āϟ āύ⧇āχ"
noNotifications: "āϕ⧋āύ⧋ āĻŦāĻŋāĻœā§āĻžāĻĒā§āϤāĻŋ āύ⧇āχ"
instance: "āχāĻ¨ā§āϏāĻŸā§āϝāĻžāĻ¨ā§āϏ"
settings: "āϏ⧇āϟāĻŋāĻ‚āϏ"
notificationSettings: "āĻŦāĻŋāĻœā§āĻžāĻĒā§āϤāĻŋāϰ āϏ⧇āϟāĻŋāĻ‚āϏ"
basicSettings: "āϏāĻžāϧāĻžāϰāĻŖ āϏ⧇āϟāĻŋāĻ‚āϏ"
otherSettings: "āĻ…āĻ¨ā§āϝāĻžāĻ¨ā§āϝ āϏ⧇āϟāĻŋāĻ‚āϏ"
openInWindow: "āύāϤ⧁āύ āωāχāĻ¨ā§āĻĄā§‹āϤ⧇ āϖ⧁āϞāĻž"
@@ -45,20 +42,12 @@ pin: "āĻĒāĻŋāύ āĻ•āϰāĻž"
unpin: "āĻĒāĻŋāύ āϏāϰāĻžāύ"
copyContent: "āĻŦāĻŋāώ⧟āĻŦāĻ¸ā§āϤ⧁ āĻ•āĻĒāĻŋ āĻ•āϰ⧁āύ"
copyLink: "āϞāĻŋāĻ™ā§āĻ• āĻ•āĻĒāĻŋ āĻ•āϰ⧁āύ"
copyLinkRenote: "āϰāĻŋāύ⧋āϟ āϞāĻŋāĻ™ā§āĻ• āĻ•āĻĒāĻŋ āĻ•āϰ⧁āύ"
delete: "āĻŽā§āϛ⧁āύ"
deleteAndEdit: "āĻŽā§āϛ⧁āύ āĻāĻŦāĻ‚ āϏāĻŽā§āĻĒāĻžāĻĻāύāĻž āĻ•āϰ⧁āύ"
deleteAndEditConfirm: "āφāĻĒāύāĻŋ āĻ•āĻŋ āĻāχ āύ⧋āϟāϟāĻŋ āĻŽā§āϛ⧇ āĻāϟāĻŋ āϏāĻŽā§āĻĒāĻžāĻĻāύāĻž āĻ•āϰāĻžāϰ āĻŦāĻŋāώāϝāĻŧ⧇ āύāĻŋāĻļā§āϚāĻŋāϤ? āφāĻĒāύāĻŋ āĻāϟāĻŋāϰ āϏāĻŽāĻ¸ā§āϤ āϰāĻŋāĻ…ā§āϝāĻžāĻ•āĻļāύ, āϰāĻŋāύ⧋āϟ āĻāĻŦāĻ‚ āϜāĻŦāĻžāĻŦ āĻšāĻžāϰāĻžāĻŦ⧇āύāĨ¤"
addToList: "āϞāĻŋāĻ¸ā§āϟ āĻ āϝ⧋āĻ— āĻ•āϰ⧁āύ"
addToAntenna: "āĻ…ā§āϝāĻžāĻ¨ā§āĻŸā§‡āύāĻž āĻ āϝ⧋āĻ— āĻ•āϰ⧁āύ"
sendMessage: "āĻāĻ•āϟāĻŋ āĻŦāĻžāĻ°ā§āϤāĻž āĻĒāĻžāĻ āĻžāύ"
copyRSS: "RSS āĻ•āĻĒāĻŋ āĻ•āϰ⧁āύ"
copyUsername: "āĻŦā§āϝāĻŦāĻšāĻžāϰāĻ•āĻžāϰ⧀āϰ āύāĻžāĻŽ āĻ•āĻĒāĻŋ āĻ•āϰ⧁āύ"
copyUserId: "āĻŦā§āϝāĻŦāĻšāĻžāϰāĻ•āĻžāϰ⧀āϰ ID āĻ•āĻĒāĻŋ āĻ•āϰ⧁āύ"
copyNoteId: "āύ⧋āĻŸā§‡āϰ ID āĻ•āĻĒāĻŋ āĻ•āϰ⧁āύ"
copyFileId: "āĻĢāĻžāχāϞ ID āĻ•āĻĒāĻŋ āĻ•āϰ⧁āύ"
copyFolderId: "āĻĢā§‹āĻ˛ā§āĻĄāĻžāϰ ID āĻ•āĻĒāĻŋ āĻ•āϰ⧁āύ"
copyProfileUrl: "āĻĒā§āϰ⧋āĻĢāĻžāχāϞ URL āĻ•āĻĒāĻŋ āĻ•āϰ⧁āύ"
searchUser: "āĻŦā§āϝāĻŦāĻšāĻžāϰāĻ•āĻžāϰ⧀ āϖ⧁āρāϜ⧁āύ..."
reply: "āϜāĻŦāĻžāĻŦ"
loadMore: "āφāϰāĻ“ āĻĻ⧇āϖ⧁āύ"
@@ -111,8 +100,6 @@ renoted: "āϰāĻŋāύ⧋āϟ āĻ•āϰāĻž āĻšā§Ÿā§‡āϛ⧇"
cantRenote: "āĻāχ āύ⧋āϟāϟāĻŋ āϰāĻŋāύ⧋āϟ āĻ•āϰāĻž āϝāĻžāĻŦ⧇ āύāĻžāĨ¤"
cantReRenote: "āϰāĻŋāύ⧋āϟāϕ⧇ āϰāĻŋāύ⧋āϟ āĻ•āϰāĻž āϝāĻžāĻŦ⧇ āύāĻžāĨ¤"
quote: "āωāĻĻā§āϧ⧃āϤāĻŋ"
inChannelRenote: "āĻšā§āϝāĻžāύ⧇āϞ⧇ āϰāĻŋāύ⧋āϟ"
inChannelQuote: "āĻšā§āϝāĻžāύ⧇āϞ⧇ āωāĻĻā§āϧ⧃āϤāĻŋ"
pinnedNote: "āĻĒāĻŋāύ āĻ•āϰāĻž āύ⧋āϟ"
pinned: "āĻĒāĻŋāύ āĻ•āϰāĻž"
you: "āφāĻĒāύāĻŋ"
@@ -121,10 +108,7 @@ sensitive: "āϏāĻ‚āĻŦ⧇āĻĻāύāĻļā§€āϞ āĻŦāĻŋāώ⧟āĻŦāĻ¸ā§āϤ⧁"
add: "āϝ⧁āĻ•ā§āϤ āĻ•āϰ⧁āύ"
reaction: "āĻĒā§āϰāϤāĻŋāĻ•ā§āϰāĻŋāϝāĻŧāĻž"
reactions: "āĻĒā§āϰāϤāĻŋāĻ•ā§āϰāĻŋāϝāĻŧāĻž"
emojiPicker: "āχāĻŽā§‹āϜāĻŋ āĻĒāĻŋāĻ•āĻžāϰ"
pinnedEmojisForReactionSettingDescription: "āϰāĻŋāĻ…ā§āϝāĻžāĻ•āĻļāύ āĻĻā§‡ā§ŸāĻžāϰ āϏāĻŽāϝāĻŧ āφāĻĒāύāĻŋ āχāĻŽā§‹āϜāĻŋāϟāĻŋāϕ⧇ āĻĒāĻŋāύ āĻ•āϰāĻž āĻāĻŦāĻ‚ āĻĒā§āϰāĻĻāĻ°ā§āĻļāĻŋāϤ āĻšāĻ“āϝāĻŧāĻžāϰ āϜāĻ¨ā§āϝ āϏ⧇āϟ āĻ•āϰāϤ⧇ āĻĒāĻžāϰ⧇āύāĨ¤"
pinnedEmojisSettingDescription: "āχāĻŽā§‹āϜāĻŋ āχāύāĻĒ⧁āϟ āĻĻā§‡ā§ŸāĻžāϰ āϏāĻŽāϝāĻŧ āφāĻĒāύāĻŋ āχāĻŽā§‹āϜāĻŋāϟāĻŋāϕ⧇ āĻĒāĻŋāύ āĻ•āϰāĻž āĻāĻŦāĻ‚ āĻĒā§āϰāĻĻāĻ°ā§āĻļāĻŋāϤ āĻšāĻ“āϝāĻŧāĻžāϰ āϜāĻ¨ā§āϝ āϏ⧇āϟ āĻ•āϰāϤ⧇ āĻĒāĻžāϰ⧇āύāĨ¤"
emojiPickerDisplay: "āĻĒāĻŋāĻ•āĻžāϰ āĻĄāĻŋāϏāĻĒā§āϞ⧇"
reactionSetting: "āϰāĻŋāĻ…ā§āϝāĻžāĻ•āĻļāύ āĻĒāĻŋāĻ•āĻžāϰ⧇ āϝ⧇āϏāĻ•āϞ āĻĒā§āϰāϤāĻŋāĻ•ā§āϰāĻŋ⧟āĻž āĻĻ⧇āĻ–āĻžāύ⧋ āĻšāĻŦ⧇"
reactionSettingDescription2: "āĻĒ⧁āύāϰāĻžāϝāĻŧ āϏāĻžāϜāĻžāϤ⧇ āĻŸā§‡āύ⧇ āφāύ⧁āύ, āĻŽā§āĻ›āϤ⧇ āĻ•ā§āϞāĻŋāĻ• āĻ•āϰ⧁āύ, āϝ⧋āĻ— āĻ•āϰāϤ⧇ + āϟāĻŋāĻĒ⧁āύāĨ¤"
rememberNoteVisibility: "āύ⧋āĻŸā§‡āϰ āĻĻ⧃āĻļā§āϝāĻŽāĻžāĻ¨ā§āϝāϤāĻžāϰ āϏ⧇āϟāĻŋāĻ‚āϏ āĻŽāύ⧇ āϰāĻžāϖ⧁āύ"
attachCancel: "āĻ…ā§āϝāĻžāϟāĻžāϚāĻŽā§‡āĻ¨ā§āϟ āϏāϰāĻžāύ "
@@ -215,6 +199,7 @@ noUsers: "āϕ⧋āύ āĻŦā§āϝāĻžāĻŦāĻšāĻžāϰāĻ•āĻžāϰ⧀ āύ⧇āχ"
editProfile: "āĻĒā§āϰ⧋āĻĢāĻžāχāϞ āϏāĻŽā§āĻĒāĻžāĻĻāύāĻž āĻ•āϰ⧁āύ"
noteDeleteConfirm: "āφāĻĒāύāĻŋ āĻ•āĻŋ āύ⧋āϟ āĻĄāĻŋāϞāĻŋāϟ āĻ•āϰāĻžāϰ āĻŦā§āϝāĻžāĻĒāĻžāϰ⧇ āύāĻŋāĻļā§āϚāĻŋāϤ?"
pinLimitExceeded: "āφāĻĒāύāĻŋ āφāϰ āϕ⧋āύ āύ⧋āϟ āĻĒāĻŋāύ āĻ•āϰāϤ⧇ āĻĒāĻžāϰāĻŦ⧇āύ āύāĻž"
intro: "Misskey āĻāϰ āχāĻ¨ā§āϏāϟāϞ⧇āĻļāύ āϏāĻŽā§āĻĒāĻ¨ā§āύ āĻšā§Ÿā§‡āϛ⧇īŧāĻĻ⧟āĻž āĻ•āϰ⧇ āĻ…ā§āϝāĻžāĻĄāĻŽāĻŋāύ āχāωāϜāĻžāϰ āϤ⧈āϰāĻŋ āĻ•āϰ⧁āύāĨ¤"
done: "āϏāĻŽā§āĻĒāĻ¨ā§āύ"
processing: "āĻĒā§āϰāĻ•ā§āϰāĻŋ⧟āĻžāϧ⧀āύ..."
preview: "āĻĒā§‚āĻ°ā§āĻŦāϰ⧂āĻĒ āĻĻ⧇āϖ⧁āύ"
@@ -251,6 +236,7 @@ removeAreYouSure: "āφāĻĒāύāĻŋ āĻ•āĻŋ \"{x}\" āϏāϰāĻžāύ⧋āϰ āĻŦā§āϝāĻž
deleteAreYouSure: "āφāĻĒāύāĻŋ āĻ•āĻŋ \"{x}\" āϏāϰāĻžāύ⧋āϰ āĻŦā§āϝāĻžāĻĒāĻžāϰ⧇ āύāĻŋāĻļā§āϚāĻŋāϤ?"
resetAreYouSure: "āϰāĻŋāϏ⧇āϟ āĻ•āϰāĻžāϰ āĻŦā§āϝāĻžāĻĒāĻžāϰ⧇ āύāĻŋāĻļā§āϚāĻŋāϤ?"
saved: "āϏāĻ‚āϰāĻ•ā§āώāĻŋāϤ āĻšā§Ÿā§‡āϛ⧇"
messaging: "āĻšā§āϝāĻžāϟ"
upload: "āφāĻĒāϞ⧋āĻĄ"
keepOriginalUploading: "āφāϏāϞ āĻ›āĻŦāĻŋ āϰāĻžāϖ⧁āύ"
keepOriginalUploadingDescription: "āĻ›āĻŦāĻŋāϟāĻŋ āφāĻĒāϞ⧋āĻĄ āĻ•āϰāĻžāϰ āϏāĻŽāϝāĻŧ āφāϏāϞ āϏāĻ‚āĻ¸ā§āĻ•āϰāĻŖāϟāĻŋ āϰāĻžāϖ⧁āύāĨ¤ āĻ…āĻĒāĻļāύāϟāĻŋ āĻŦāĻ¨ā§āϧ āĻĨāĻžāĻ•āϞ⧇, āφāĻĒāϞ⧋āĻĄā§‡āϰ āϏāĻŽāϝāĻŧ āĻ“āϝāĻŧ⧇āĻŦ āĻĒā§āϰāĻ•āĻžāĻļāύāĻžāϰ āϜāĻ¨ā§āϝ āĻ›āĻŦāĻŋ āĻŦā§āϰāĻžāωāϜāĻžāϰ⧇ āϤ⧈āϰāĻŋ āĻ•āϰāĻž āĻšāĻŦ⧇āĨ¤"
@@ -263,14 +249,15 @@ uploadFromUrlMayTakeTime: "URL āĻšāϤ⧇ āφāĻĒāϞ⧋āĻĄ āĻšāϤ⧇ āĻ•āĻŋāϛ⧁
explore: "āϘ⧁āϰ⧇ āĻĻ⧇āϖ⧁āύ"
messageRead: "āĻĒāĻĄāĻŧāĻž"
noMoreHistory: "āφāϰ āϕ⧋āύ āχāϤāĻŋāĻšāĻžāϏ āύ⧇āχ"
startMessaging: "āĻšā§āϝāĻžāϟ āĻļ⧁āϰ⧁ āĻ•āϰ⧁āύ"
nUsersRead: "{n} āϜāύ āĻĒā§œā§‡āϛ⧇āύ"
agreeTo: "{0} āĻāϰ āĻĒā§āϰāϤāĻŋ āφāĻŽāĻŋ āϏāĻŽā§āĻŽāϤ"
tos: "āĻĒāϰāĻŋāώ⧇āĻŦāĻžāϰ āĻļāĻ°ā§āϤāĻžāĻĻāĻŋ"
start: "āĻļ⧁āϰ⧁ āĻ•āϰ⧁āύ"
home: "āĻŽā§‚āϞ āĻĒāĻžāϤāĻž"
remoteUserCaution: "āĻāχ āĻŦā§āϝāĻžāĻŦāĻšāĻžāϰāĻ•āĻžāϰ⧀ āϰāĻŋāĻŽā§‹āϟ āχāĻ¨ā§āϏāĻŸā§āϝāĻžāĻ¨ā§āϏ⧇āϰ, āύāĻŋāĻŽā§āύāĻ•ā§āϤ āϤāĻĨā§āϝ āĻ…āϏāĻŽā§āĻĒā§‚āĻ°ā§āĻŖ āĻšāϤ⧇ āĻĒāĻžāϰ⧇āĨ¤"
activity: "āĻ•āĻžāĻ°ā§āϝāĻ•āϞāĻžāĻĒ"
images: "āĻ›āĻŦāĻŋ"
image: "āĻ›āĻŦāĻŋ"
birthday: "āϜāĻ¨ā§āĻŽāĻĻāĻŋāύ"
yearsOld: "{age} āĻŦāĻ›āϰ"
registeredDate: "āϝ⧋āĻ—āĻĻāĻžāύ⧇āϰ āϤāĻžāϰāĻŋāĻ–"
@@ -307,6 +294,7 @@ copyUrl: "URL āĻ•āĻĒāĻŋ āĻ•āϰ⧁āύ"
rename: "āĻĒ⧁āύāσāύāĻžāĻŽāĻ•āϰāĻŖ"
avatar: "āĻĒā§āϰ⧋āĻĢāĻžāχāϞ āĻ›āĻŦāĻŋ"
banner: "āĻŦā§āϝāĻžāύāĻžāϰ"
nsfw: "āϏāĻ‚āĻŦ⧇āĻĻāύāĻļā§€āϞ āĻŦāĻŋāώ⧟āĻŦāĻ¸ā§āϤ⧁"
whenServerDisconnected: "āϏāĻžāĻ°ā§āĻ­āĻžāϰ⧇āϰ āϏāĻžāĻĨ⧇ āϏāĻ‚āϝ⧋āĻ— āĻŦāĻŋāĻšā§āĻ›āĻŋāĻ¨ā§āύ āĻšāϝāĻŧ⧇ āϗ⧇āϞ⧇"
disconnectedFromServer: "āϏāĻžāĻ°ā§āĻ­āĻžāϰ āĻĨ⧇āϕ⧇ āϏāĻ‚āϝ⧋āĻ— āĻŦāĻŋāĻšā§āĻ›āĻŋāĻ¨ā§āύ āĻšāϝāĻŧ⧇āϛ⧇"
reload: "āφāĻŦāĻžāϰ āϞ⧋āĻĄ āĻ•āϰ⧁āύ"
@@ -336,10 +324,12 @@ enableLocalTimeline: "āĻ¸ā§āĻĨāĻžāĻ¨ā§€ā§Ÿ āϟāĻžāχāĻŽāϞāĻžāχāύ āϚāĻžāϞ
enableGlobalTimeline: "āĻ—ā§āϞ⧋āĻŦāĻžāϞ āϟāĻžāχāĻŽāϞāĻžāχāύ āϚāĻžāϞ⧁ āĻ•āϰ⧁āύ"
disablingTimelinesInfo: "āφāĻĒāύāĻŋ āĻāχ āϟāĻžāχāĻŽāϞāĻžāχāύāϗ⧁āϞāĻŋ āĻŦāĻ¨ā§āϧ āĻ•āϰāϞ⧇āĻ“ āĻĒā§āϰāĻļāĻžāϏāĻ• āĻāĻŦāĻ‚ āĻŽāĻĄāĻžāϰ⧇āϟāϰāϰāĻž āĻāχ āϟāĻžāχāĻŽāϞāĻžāχāύāϗ⧁āϞāĻŋ āĻŦā§āϝāĻžāĻŦāĻšāĻžāϰ āĻ•āϰāϤ⧇ āĻĒāĻžāϰāĻŦ⧇"
registration: "āύāĻŋāĻŦāĻ¨ā§āϧāύ"
enableRegistration: "āύāϤ⧁āύ āĻŦā§āϝāĻžāĻŦāĻšāĻžāϰāĻ•āĻžāϰ⧀ āύāĻŋāĻŦāĻ¨ā§āϧāύ āϚāĻžāϞ⧁ āĻ•āϰ⧁āύ"
invite: "āφāĻŽāĻ¨ā§āĻ¤ā§āϰāĻŖ"
driveCapacityPerLocalAccount: "āĻĒā§āϰāĻ¤ā§āϝ⧇āĻ• āĻ¸ā§āĻĨāĻžāĻ¨ā§€ā§Ÿ āĻŦā§āϝāĻžāĻŦāĻšāĻžāϰāĻ•āĻžāϰ⧀āϰ āϜāĻ¨ā§āϝ āĻĄā§āϰāĻžāχāϭ⧇āϰ āϜāĻžā§ŸāĻ—āĻž"
driveCapacityPerRemoteAccount: "āĻĒā§āϰāĻ¤ā§āϝ⧇āĻ• āϰāĻŋāĻŽā§‹āϟ āĻŦā§āϝāĻžāĻŦāĻšāĻžāϰāĻ•āĻžāϰ⧀āϰ āϜāĻ¨ā§āϝ āĻĄā§āϰāĻžāχāϭ⧇āϰ āϜāĻžā§ŸāĻ—āĻž"
inMb: "āĻŽā§‡āĻ—āĻžāĻŦāĻžāχāĻŸā§‡ āϞāĻŋāϖ⧁āύ"
iconUrl: "āφāχāĻ•āύ⧇āϰ URL (āĻĢā§āϝāĻžāĻ­āĻŋāĻ•āύ, āχāĻ¤ā§āϝāĻžāĻĻāĻŋ)"
bannerUrl: "āĻŦā§āϝāĻžāύāĻžāϰ āĻ›āĻŦāĻŋāϰ URL"
backgroundImageUrl: "āĻĒāϟāĻ­ā§‚āĻŽāĻŋāϰ āϚāĻŋāĻ¤ā§āϰ⧇āϰ URL"
basicInfo: "āφāĻĒāύāĻžāϰ āĻŦā§āϝāĻ•ā§āϤāĻŋāĻ—āϤ āϤāĻĨā§āϝ"
@@ -353,8 +343,6 @@ hcaptcha: "hCaptcha"
enableHcaptcha: "hCaptcha āϚāĻžāϞ⧁ āĻ•āϰ⧁āύ"
hcaptchaSiteKey: "āϏāĻžāχāϟ āϕ⧀"
hcaptchaSecretKey: "āϏāĻŋāĻ•ā§āϰ⧇āϟ āϕ⧀"
mcaptchaSiteKey: "āϏāĻžāχāϟ āϕ⧀"
mcaptchaSecretKey: "āϏāĻŋāĻ•ā§āϰ⧇āϟ āϕ⧀"
recaptcha: "reCAPTCHA"
enableRecaptcha: "reCAPTCHA āϚāĻžāϞ⧁ āĻ•āϰ⧁āύ"
recaptchaSiteKey: "āϏāĻžāχāϟ āϕ⧀"
@@ -407,6 +395,7 @@ share: "āĻļā§‡ā§ŸāĻžāϰ"
notFound: "āĻĒāĻžāĻ“ā§ŸāĻž āϝāĻžā§ŸāύāĻŋ"
notFoundDescription: "āĻāχ URL-āĻāϰ āϏāĻžāĻĨ⧇ āϏāĻŽā§āĻĒāĻ°ā§āĻ•āĻŋāϤ āϕ⧋āύ⧋ āĻĒ⧃āĻˇā§āĻ āĻž āύ⧇āχāĨ¤"
uploadFolder: "āφāĻĒāϞ⧋āĻĄā§‡āϰ āϜāĻ¨ā§āϝ āĻĄāĻŋāĻĢāĻ˛ā§āϟ āĻĢā§‹āĻ˛ā§āĻĄāĻžāϰ"
cacheClear: "āĻ•ā§āϝāĻžāĻļ āĻĒāϰāĻŋāĻˇā§āĻ•āĻžāϰ āĻ•āϰ⧁āύ"
markAsReadAllNotifications: "āϏāĻŽāĻ¸ā§āϤ āĻŦāĻŋāĻœā§āĻžāĻĒā§āϤāĻŋāϗ⧁āϞāĻŋ āĻĒāĻ āĻŋāϤ āĻšāĻŋāϏāĻžāĻŦ⧇ āϚāĻŋāĻšā§āύāĻŋāϤ āĻ•āϰ⧁āύ"
markAsReadAllUnreadNotes: "āϏāĻŽāĻ¸ā§āϤ āύ⧋āϟāϗ⧁āϞāĻŋ āĻĒāĻ āĻŋāϤ āĻšāĻŋāϏāĻžāĻŦ⧇ āϚāĻŋāĻšā§āύāĻŋāϤ āĻ•āϰ⧁āύ"
markAsReadAllTalkMessages: "āϏāĻŽāĻ¸ā§āϤ āĻŽā§‡āϏ⧇āϜ āĻĒāĻ āĻŋāϤ āĻšāĻŋāϏāĻžāĻŦ⧇ āϚāĻŋāĻšā§āύāĻŋāϤ āĻ•āϰ⧁āύ"
@@ -424,6 +413,8 @@ retype: "āĻĒ⧁āύāσ āĻĒā§āϰāĻŦ⧇āĻļ"
noteOf: "{user} āĻāϰ āύ⧋āϟ"
quoteAttached: "āωāĻĻā§āϧ⧃āϤ"
quoteQuestion: "āωāĻĻā§āϧ⧃āϤāĻŋ āĻšāĻŋāϏāĻžāĻŦ⧇ āϏāĻ‚āϝ⧁āĻ•ā§āϤ āĻ•āϰāĻŦ⧇āύ?"
noMessagesYet: "āϕ⧋āύ āĻŽā§‡āϏ⧇āϜ āύ⧇āχ"
newMessageExists: "āύāϤ⧁āύ āĻŽā§‡āϏ⧇āϜ āĻĒā§‡ā§Ÿā§‡āϛ⧇āύ"
onlyOneFileCanBeAttached: "āφāĻĒāύāĻŋ āĻŽā§‡āϏ⧇āĻœā§‡āϰ āϏāĻžāĻĨ⧇ āϏāĻ°ā§āĻŦā§‹āĻšā§āϚ āĻāĻ•āϟāĻŋ āĻĢāĻžāχāϞ āϝ⧁āĻ•ā§āϤ āĻ•āϰāϤ⧇ āĻĒāĻžāϰāĻŦ⧇āύ"
signinRequired: "āĻĻāϝāĻŧāĻž āĻ•āϰ⧇ āϞāĻ— āχāύ āĻ•āϰ⧁āύ"
invitations: "āφāĻŽāĻ¨ā§āĻ¤ā§āϰāĻŖ"
@@ -445,6 +436,7 @@ or: "āĻ…āĻĨāĻŦāĻž"
language: "āĻ­āĻžāώāĻž"
uiLanguage: "UI āĻāϰ āĻ­āĻžāώāĻž"
aboutX: "{x} āϏāĻŽā§āĻĒāĻ°ā§āϕ⧇"
disableDrawer: "āĻĄā§āϰāϝāĻŧāĻžāϰ āĻŽā§‡āύ⧁ āĻĒā§āϰāĻĻāĻ°ā§āĻļāύ āĻ•āϰāĻŦ⧇āύ āύāĻž"
noHistory: "āϕ⧋āύ⧋ āχāϤāĻŋāĻšāĻžāϏ āύ⧇āχ"
signinHistory: "āĻĒā§āϰāĻŦ⧇āĻļ āĻ•āϰāĻžāϰ āχāϤāĻŋāĻšāĻžāϏ"
doing: "āĻĒā§āϰāĻ•ā§āϰāĻŋ⧟āĻž āĻ•āϰāϛ⧇..."
@@ -618,7 +610,10 @@ abuseReported: "āφāĻĒāύāĻžāϰ āĻ…āĻ­āĻŋāϝ⧋āĻ—āϟāĻŋ āĻĻāĻžāĻ–āĻŋāϞ āĻ•āϰ
reporter: "āĻ…āĻ­āĻŋāϝ⧋āĻ—āĻ•āĻžāϰ⧀"
reporteeOrigin: "āĻ…āĻ­āĻŋāϝ⧋āĻ—āϟāĻŋāϰ āĻ‰ā§ŽāϏ"
reporterOrigin: "āĻ…āĻ­āĻŋāϝ⧋āĻ—āĻ•āĻžāϰ⧀āϰ āĻ‰ā§ŽāϏ"
forwardReport: "āϰāĻŋāĻŽā§‹āϟ āχāĻ¨ā§āϏāĻ¤ā§āϝāĻžāĻ¨ā§āϏ⧇ āĻ…āĻ­āĻŋāϝ⧋āĻ—āϟāĻŋ āĻĒāĻžāĻ āĻžāύ"
forwardReportIsAnonymous: "āφāĻĒāύāĻžāϰ āϤāĻĨā§āϝ āϰāĻŋāĻŽā§‹āϟ āχāĻ¨ā§āϏāĻ¤ā§āϝāĻžāĻ¨ā§āϏ⧇ āĻĒāĻžāĻ āĻžāύ⧋ āĻšāĻŦ⧇ āύāĻž āĻāĻŦāĻ‚ āĻāĻ•āϟāĻŋ āĻŦ⧇āύāĻžāĻŽā§€ āϏāĻŋāĻ¸ā§āĻŸā§‡āĻŽ āĻ…ā§āϝāĻžāĻ•āĻžāωāĻ¨ā§āϟ āĻšāĻŋāϏāĻžāĻŦ⧇ āĻĒā§āϰāĻĻāĻ°ā§āĻļāĻŋāϤ āĻšāĻŦ⧇āĨ¤"
send: "āĻĒāĻžāĻ āĻžāύ"
abuseMarkAsResolved: "āĻ…āĻ­āĻŋāϝ⧋āĻ—āϟāĻŋāϕ⧇ āϏāĻŽāĻžāϧāĻžāĻ•ā§ƒāϤ āĻšāĻŋāϏāĻžāĻŦ⧇ āϚāĻŋāĻšā§āύāĻŋāϤ āĻ•āϰ⧁āύ"
openInNewTab: "āύāϤ⧁āύ āĻŸā§āϝāĻžāĻŦ⧇ āϖ⧁āϞ⧁āύ"
openInSideView: "āϏāĻžāχāĻĄ āĻ­āĻŋāωāϤ⧇ āϖ⧁āϞ⧁āύ"
defaultNavigationBehaviour: "āĻĄāĻŋāĻĢāĻ˛ā§āϟ āύ⧇āĻ­āĻŋāϗ⧇āĻļāύ"
@@ -634,7 +629,6 @@ createNew: "āύāϤ⧁āύ"
optional: "āĻĒā§āϰāϝāĻŧā§‹āϜāĻ¨ā§€ā§Ÿ āύ⧟"
createNewClip: "āύāϤ⧁āύ āĻ•ā§āϞāĻŋāĻĒ āϤ⧈āϰāĻŋ āĻ•āϰ⧁āύ"
public: "āϏāĻ°ā§āĻŦāϜāύ⧀āύ"
private: "āĻŦā§āϝāĻžāĻ•ā§āϤāĻŋāĻ—āϤ"
i18nInfo: "Misskey āĻ¸ā§āĻŦ⧇āĻšā§āĻ›āĻžāϏ⧇āĻŦāĻ•āĻĻ⧇āϰ āĻĻā§āĻŦāĻžāϰāĻž āĻŦāĻŋāĻ­āĻŋāĻ¨ā§āύ āĻ­āĻžāώāĻžāϝāĻŧ āĻ…āύ⧁āĻŦāĻžāĻĻ āĻ•āϰāĻž āĻšāĻšā§āϛ⧇āĨ¤ āφāĻĒāύāĻŋ {link} āĻ āĻ—āĻŋā§Ÿā§‡ āĻ…āύ⧁āĻŦāĻžāĻĻ⧇ āϏāĻšāϝ⧋āĻ—āĻŋāϤāĻž āĻ•āϰāϤ⧇ āĻĒāĻžāϰ⧇āύāĨ¤"
manageAccessTokens: "āĻ…ā§āϝāĻžāĻ•ā§āϏ⧇āϏ āĻŸā§‹āϕ⧇āύ āĻĒāϰāĻŋāϚāĻžāϞāύāĻž āĻ•āϰ⧁āύ"
accountInfo: "āĻ…ā§āϝāĻžāĻ•āĻžāωāĻ¨ā§āĻŸā§‡āϰ āϤāĻĨā§āϝ"
@@ -672,6 +666,7 @@ experimentalFeatures: "āĻĒāϰ⧀āĻ•ā§āώāĻžāĻŽā§‚āϞāĻ• āĻŦ⧈āĻļāĻŋāĻˇā§āϟ
developer: "āĻĄā§‡āϭ⧇āϞāĻĒāĻžāϰ"
makeExplorable: "āĻ…ā§āϝāĻžāĻ•āĻžāωāĻ¨ā§āϟ \"āϘ⧁āϰ⧇ āĻĻ⧇āϖ⧁āύ\" āĻĒ⧃āĻˇā§āĻ āĻžā§Ÿ āĻĻ⧇āĻ–āĻžāύ"
makeExplorableDescription: "āφāĻĒāύāĻŋ āĻāϟāĻŋ āĻŦāĻ¨ā§āϧ āĻ•āϰāϞ⧇, āφāĻĒāύāĻžāϰ āĻ…ā§āϝāĻžāĻ•āĻžāωāĻ¨ā§āϟ \"āϘ⧁āϰ⧇ āĻĻ⧇āϖ⧁āύ\" āĻĒ⧃āĻˇā§āĻ āĻžā§Ÿ āĻĒā§āϰāĻĻāĻ°ā§āĻļāĻŋāϤ āĻšāĻŦ⧇ āύāĻžāĨ¤"
showGapBetweenNotesInTimeline: "āϟāĻžāχāĻŽāϞāĻžāχāύ āĻāĻŦāĻ‚ āύ⧋āĻŸā§‡āϰ āĻŽāĻžāĻā§‡ āĻĢāĻžāĻ•āĻž āϜāĻžā§ŸāĻ—āĻž āϰāĻžāϖ⧁āύ"
duplicate: "āĻĒā§āϰāϤāĻŋāϰ⧂āĻĒ"
left: "āĻŦāĻžāĻŽ"
center: "āĻŽāĻžāĻāĻ–āĻžāύ"
@@ -801,6 +796,8 @@ makeReactionsPublicDescription: "āφāĻĒāύāĻžāϰ āĻĒā§‚āĻ°ā§āĻŦāĻŦāĻ°ā§āϤ⧀
classic: "āĻ•ā§āϞāĻžāϏāĻŋāĻ•"
muteThread: "āĻĨā§āϰ⧇āĻĄ āĻŽāĻŋāωāϟ āĻ•āϰ⧁āύ"
unmuteThread: "āĻĨā§āϰ⧇āĻĄ āφāύāĻŽāĻŋāωāϟ āĻ•āϰ⧁āύ"
ffVisibility: "āĻ…āύ⧁āϏāϰāĻŖ/āĻ…āύ⧁āϏāϰāĻŖāĻ•āĻžāϰ⧀āĻĻ⧇āϰ āĻĻ⧃āĻļā§āϝāĻŽāĻžāĻ¨ā§āϝāϤāĻž"
ffVisibilityDescription: "āφāĻĒāύāĻŋ āĻ•āĻžāϕ⧇ āĻ…āύ⧁āϏāϰāĻŖ āĻ•āϰ⧇āύ āĻāĻŦāĻ‚ āϕ⧇ āφāĻĒāύāĻžāϕ⧇ āĻ…āύ⧁āϏāϰāĻŖ āĻ•āϰ⧇, āϏ⧇āϟāĻž āĻ•āĻžāϰāĻž āĻĻ⧇āĻ–āϤ⧇ āĻĒāĻžāĻŦ⧇ āϤāĻž āύāĻŋāĻ°ā§āϧāĻžāϰāĻŖ āĻ•āϰ⧇āĨ¤"
continueThread: "āφāϰ⧋ āĻĨā§āϰ⧇āĻĄ āĻĻ⧇āϖ⧁āύ"
deleteAccountConfirm: "āφāĻĒāύāĻžāϰ āĻ…ā§āϝāĻžāĻ•āĻžāωāĻ¨ā§āϟ āĻŽā§āϛ⧇ āĻĢ⧇āϞāĻž āĻšāĻŦ⧇āĨ¤ āĻ āĻŋāĻ• āφāϛ⧇?"
incorrectPassword: "āφāĻĒāύāĻžāϰ āĻĻ⧇āĻ“ā§ŸāĻž āĻĒāĻžāϏāĻ“āϝāĻŧāĻžāĻ°ā§āĻĄāϟāĻŋ āϭ⧁āϞāĨ¤"
@@ -839,36 +836,6 @@ account: "āĻ…ā§āϝāĻžāĻ•āĻžāωāĻ¨ā§āϟāϗ⧁āϞāĻŋ"
like: "āĻĒāĻ›āĻ¨ā§āĻĻ āĻ•āϰāĻž"
show: "āĻĒā§āϰāĻĻāĻ°ā§āĻļāύ"
color: "āϰāĻ‚"
horizontal: "āĻĒāĻžāĻļ⧇"
youFollowing: "āĻ…āύ⧁āϏāϰāĻŖ āĻ•āϰāĻž āĻšāĻšā§āϛ⧇"
icon: "āĻĒā§āϰ⧋āĻĢāĻžāχāϞ āĻ›āĻŦāĻŋ"
replies: "āϜāĻŦāĻžāĻŦ"
renotes: "āϰāĻŋāύ⧋āϟ"
sourceCode: "āϏ⧋āĻ°ā§āϏ āϕ⧋āĻĄ"
flip: "āωāĻ˛ā§āϟāĻžāύ"
postForm: "āύ⧋āϟ āϞāĻŋāϖ⧁āύ"
information: "āφāĻĒāύāĻžāϰ āϏāĻŽā§āĻĒāĻ°ā§āϕ⧇"
inMinutes: "āĻŽāĻŋāύāĻŋāϟ"
inDays: "āĻĻāĻŋāύ"
widgets: "āωāχāĻœā§‡āϟāϗ⧁āϞāĻŋ"
_imageEditing:
_vars:
filename: "āĻĢāĻžāχāϞ⧇āϰ āύāĻžāĻŽ"
_imageFrameEditor:
header: "āĻšā§‡āĻĄāĻžāϰ"
font: "āĻĢāĻ¨ā§āϟ"
fontSerif: "āϏ⧇āϰāĻŋāĻĢ"
fontSansSerif: "āĻ¸ā§āϝāĻžāĻ¨ā§āϏ āϏ⧇āϰāĻŋāĻĢ"
_chat:
invitations: "āφāĻŽāĻ¨ā§āĻ¤ā§āϰāĻŖ"
noHistory: "āϕ⧋āύ⧋ āχāϤāĻŋāĻšāĻžāϏ āύ⧇āχ"
members: "āϏāĻĻāĻ¸ā§āϝāĻŦ⧃āĻ¨ā§āĻĻ"
home: "āĻŽā§‚āϞ āĻĒāĻžāϤāĻž"
send: "āĻĒāĻžāĻ āĻžāύ"
_delivery:
stop: "āĻ¸ā§āĻĨāĻ—āĻŋāϤ āĻ•āϰāĻž āĻšā§Ÿā§‡āϛ⧇"
_type:
none: "āĻĒā§āϰāĻ•āĻžāĻļ āĻ•āϰāĻž āĻšāĻšā§āϛ⧇"
_role:
priority: "āĻ…āĻ—ā§āϰāĻžāϧāĻŋāĻ•āĻžāϰ"
_priority:
@@ -918,7 +885,6 @@ _plugin:
install: "āĻĒā§āϞāĻžāĻ—āχāύ āχāĻ¨ā§āϏāϟāϞ āĻ•āϰ⧁āύ"
installWarn: "āĻ…āĻŦāĻŋāĻļā§āĻŦāĻ¸ā§āϤ āĻĒā§āϞāĻžāĻ—āχāύ āχāύāĻ¸ā§āϟāϞ āĻ•āϰāĻŦ⧇āύ āύāĻžāĨ¤"
manage: "āĻĒā§āϞāĻžāĻ—āχāύ āĻŽā§āϝāĻžāύ⧇āϜ āĻ•āϰ⧁āύ"
viewSource: "āĻ‰ā§ŽāϏ āĻĻ⧇āϖ⧁āύ"
_registry:
scope: "āĻ¸ā§āϕ⧋āĻĒ"
key: "āϕ⧀"
@@ -934,6 +900,10 @@ _aboutMisskey:
donate: "Misskey āϤ⧇ āĻĻāĻžāύ āĻ•āϰ⧁āύ"
morePatrons: "āφāϰāĻ“ āĻ…āύ⧇āϕ⧇ āφāĻŽāĻžāĻĻ⧇āϰ āϏāĻžāĻšāĻžāĻ¯ā§āϝ āĻ•āϰāϛ⧇āύāĨ¤ āϤāĻžāĻĻ⧇āϰ āϏāĻŦāĻžāχāϕ⧇ āϧāĻ¨ā§āϝāĻŦāĻžāĻĻ đŸĨ°"
patrons: "āϏāĻŽāĻ°ā§āĻĨāύāĻ•āĻžāϰ⧀"
_nsfw:
respect: "āĻ¸ā§āĻĒāĻ°ā§āĻļāĻ•āĻžāϤāϰ āĻŽāĻŋāĻĄāĻŋ⧟āĻž āϞ⧁āĻ•āĻžāύ"
ignore: "āĻ¸ā§āĻĒāĻ°ā§āĻļāĻ•āĻžāϤāϰ āĻŽāĻŋāĻĄāĻŋ⧟āĻž āϞ⧁āĻ•āĻžāĻŦ⧇āύ āύāĻž"
force: "āϏāĻ•āϞ āĻŽāĻŋāĻĄāĻŋ⧟āĻž āϞ⧁āĻ•āĻžāύ"
_instanceTicker:
none: "āĻĻ⧇āĻ–āĻžāĻŦ⧇āύ āύāĻž"
remote: "āϰāĻŋāĻŽā§‹āϟ āĻŦā§āϝāĻžāĻŦāĻšāĻžāϰāĻ•āĻžāϰ⧀āĻĻ⧇āϰ āϜāĻ¨ā§āϝ āĻĻ⧇āĻ–āĻžāύ"
@@ -961,6 +931,11 @@ _wordMute:
muteWords: "āύāĻŋāσāĻļāĻŦā§āĻĻ āĻ•āϰāĻž āĻļāĻŦā§āĻĻāϗ⧁āϞāĻŋ"
muteWordsDescription: "āĻ¸ā§āĻĒ⧇āϏ āĻĻāĻŋāϝāĻŧ⧇ āφāϞāĻžāĻĻāĻž āĻ•āϰāϞ⧇ AND āĻļāĻ°ā§āϤ āϤ⧈āϰāĻŋ āĻšāĻŦ⧇ āĻāĻŦāĻ‚ āφāϞāĻžāĻĻāĻž āϞāĻžāχāύ⧇ āϞāĻŋāĻ–āϞ⧇ OR āĻļāĻ°ā§āϤ āϤ⧈āϰāĻŋ āĻšāĻŦ⧇āĨ¤"
muteWordsDescription2: "āϰ⧇āϗ⧁āϞāĻžāϰ āĻāĻ•ā§āϏāĻĒā§āϰ⧇āĻļāύ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāϤ⧇ āĻ¸ā§āĻ˛ā§āϝāĻžāĻļ āĻĻāĻŋāϝāĻŧ⧇ āϕ⧀āĻ“āϝāĻŧāĻžāĻ°ā§āĻĄāϕ⧇ āϘāĻŋāϰ⧇ āϰāĻžāϖ⧁āύāĨ¤"
softDescription: "āϟāĻžāχāĻŽāϞāĻžāχāύ āĻĨ⧇āϕ⧇ āύāĻŋāĻ°ā§āĻĻāĻŋāĻˇā§āϟ āĻļāĻ°ā§āϤāĻžāύ⧁āϝāĻžā§Ÿā§€ āύ⧋āϟ āϞ⧁āĻ•āĻŋāϝāĻŧ⧇ āϰāĻžāϖ⧇āĨ¤"
hardDescription: "āύāĻŋāĻ°ā§āĻĻāĻŋāĻˇā§āϟ āĻļāĻ°ā§āϤāĻžāύ⧁āϝāĻžā§Ÿā§€ āύ⧋āϟāϗ⧁āϞāĻŋāϕ⧇ āϟāĻžāχāĻŽāϞāĻžāχāύ āĻĨ⧇āϕ⧇ āĻŦāĻžāĻĻ āĻĻā§‡ā§ŸāĨ¤ āφāĻĒāύāĻŋ āĻļāĻ°ā§āϤ āĻĒāϰāĻŋāĻŦāĻ°ā§āϤāύ āĻ•āϰāϞ⧇āĻ“ āϝ⧇ āύ⧋āϟāϗ⧁āϞāĻŋ āϝ⧋āĻ— āĻ•āϰāĻž āĻšāϝāĻŧāύāĻŋ āϏ⧇āϗ⧁āϞāĻŋ āĻŦāĻžāĻĻ āĻĻ⧇āĻ“āϝāĻŧāĻž āĻšāĻŦ⧇āĨ¤"
soft: "āύāĻŽāĻ¨ā§€ā§Ÿ"
hard: "āĻ•āĻ ā§‹āϰ"
mutedNotes: "āĻŽāĻŋāωāϟ āĻ•āϰāĻž āύ⧋āϟāϗ⧁āϞāĻŋ"
_instanceMute:
instanceMuteDescription: "āĻ•āύāĻĢāĻŋāĻ—āĻžāϰ āĻ•āϰāĻž āχāĻ¨ā§āϏāĻŸā§āϝāĻžāĻ¨ā§āϏ⧇āϰ āϏāĻŦ āύ⧋āϟ āĻāĻŦāĻ‚ āϰāĻŋāύ⧋āϟ āĻŽāĻŋāωāϟ āĻ•āϰ⧁āύ, āĻŽāĻŋāωāϟ āĻ•āϰāĻž āχāĻ¨ā§āϏāĻŸā§āϝāĻžāĻ¨ā§āϏ⧇āϰ āĻŦā§āϝāĻŦāĻšāĻžāϰāĻ•āĻžāϰ⧀āĻĻ⧇āϰ āωāĻ¤ā§āϤāϰ āϏāĻšāĨ¤"
instanceMuteDescription2: "āĻĒā§āϰāϤāĻŋāϟāĻŋāϕ⧇ āφāϞāĻžāĻĻāĻž āϞāĻžāχāύ⧇ āϞāĻŋāϖ⧁āύ"
@@ -1007,6 +982,7 @@ _theme:
header: "āĻšā§‡āĻĄāĻžāϰ"
navBg: "āϏāĻžāχāĻĄāĻŦāĻžāϰ⧇āϰ āĻĒāϟāĻ­ā§‚āĻŽāĻŋ"
navFg: "āϏāĻžāχāĻĄāĻŦāĻžāϰ⧇āϰ āĻĒāĻžāĻ ā§āϝ"
navHoverFg: "āϏāĻžāχāĻĄāĻŦāĻžāϰ⧇āϰ āĻĒāĻžāĻ ā§āϝ (āĻšāĻ­āĻžāϰ)"
navActive: "āϏāĻžāχāĻĄāĻŦāĻžāϰ⧇āϰ āĻĒāĻžāĻ ā§āϝ (āĻ…ā§āϝāĻžāĻ•āϟāĻŋāĻ­)"
navIndicator: "āϏāĻžāχāĻĄāĻŦāĻžāϰ⧇āϰ āχāύāĻĄāĻŋāϕ⧇āϟāϰ"
link: "āϞāĻŋāĻ‚āĻ•"
@@ -1023,18 +999,30 @@ _theme:
infoFg: "āϤāĻĨā§āϝ⧇āϰ āĻĒāĻžāĻ ā§āϝ"
infoWarnBg: "āĻ“ā§ŸāĻžāĻ°ā§āύāĻŋāĻ‚ āĻāϰ āĻĒāϟāĻ­ā§‚āĻŽāĻŋ"
infoWarnFg: "āĻ“ā§ŸāĻžāĻ°ā§āύāĻŋāĻ‚ āĻāϰ āĻĒāĻžāĻ ā§āϝ"
cwBg: "CW āĻŦāĻžāϟāύ⧇āϰ āĻĒāϟāĻ­ā§‚āĻŽāĻŋ"
cwFg: "CW āĻŦāĻžāϟāύ⧇āϰ āĻĒāĻžāĻ ā§āϝ"
cwHoverBg: "CW āĻŦāĻžāϟāύ⧇āϰ āĻĒāϟāĻ­ā§‚āĻŽāĻŋ (āĻšāĻ­āĻžāϰ)"
toastBg: "āĻŦāĻŋāĻœā§āĻžāĻĒā§āϤāĻŋāϰ āĻĒāϟāĻ­ā§‚āĻŽāĻŋ"
toastFg: "āĻŦāĻŋāĻœā§āĻžāĻĒā§āϤāĻŋāϰ āĻĒāĻžāĻ ā§āϝ"
buttonBg: "āĻŦāĻžāϟāύ⧇āϰ āĻĒāϟāĻ­ā§‚āĻŽāĻŋ"
buttonHoverBg: "āĻŦāĻžāϟāύ⧇āϰ āĻĒāϟāĻ­ā§‚āĻŽāĻŋ (āĻšāĻ­āĻžāϰ)"
inputBorder: "āχāύāĻĒ⧁āϟ āĻĢāĻŋāĻ˛ā§āĻĄā§‡āϰ āĻŦāĻ°ā§āĻĄāĻžāϰ"
listItemHoverBg: "āϞāĻŋāĻ¸ā§āϟ āφāχāĻŸā§‡āĻŽā§‡āϰ āĻĒāϟāĻ­ā§‚āĻŽāĻŋ (āĻšā§‹āĻ­āĻžāϰ)"
driveFolderBg: "āĻĄā§āϰāĻžāχāĻ­ āĻĢā§‹āĻ˛ā§āĻĄāĻžāϰ⧇āϰ āĻĒāϟāĻ­ā§‚āĻŽāĻŋ"
wallpaperOverlay: "āĻ“āϝāĻŧāĻžāϞāĻĒ⧇āĻĒāĻžāϰ āĻ“āĻ­āĻžāϰāϞ⧇"
badge: "āĻŦā§āϝāĻžāϜ"
messageBg: "āĻšā§āϝāĻžāĻŸā§‡āϰ āĻĒāϟāĻ­ā§‚āĻŽāĻŋ"
accentDarken: "āĻ…ā§āϝāĻžāĻ•āϏ⧇āĻ¨ā§āϟ (āĻ—āĻžā§)"
accentLighten: "āĻ…ā§āϝāĻžāĻ•āϏ⧇āĻ¨ā§āϟ (āĻšāĻžāĻ˛ā§āĻ•āĻž)"
fgHighlighted: "āĻšāĻžāχāϞāĻžāχāϟ āĻ•āϰāĻž āĻĒāĻžāĻ ā§āϝ"
_sfx:
note: "āύ⧋āϟāϗ⧁āϞāĻŋ"
noteMy: "āύ⧋āϟ (āφāĻĒāύāĻžāϰ)"
notification: "āĻŦāĻŋāĻœā§āĻžāĻĒā§āϤāĻŋ"
chat: "āĻšā§āϝāĻžāϟ"
chatBg: "āĻšā§āϝāĻžāϟ (āĻŦā§āϝāĻžāĻ•āĻ—ā§āϰāĻžāωāĻ¨ā§āĻĄ)"
antenna: "āĻ…ā§āϝāĻžāĻ¨ā§āĻŸā§‡āύāĻžāϗ⧁āϞāĻŋ"
channel: "āĻšā§āϝāĻžāύ⧇āϞ⧇āϰ āĻŦāĻŋāĻœā§āĻžāĻĒā§āϤāĻŋ"
_ago:
future: "āĻ­āĻŦāĻŋāĻˇā§āĻ¯ā§Ž"
justNow: "āĻāχāĻŽāĻžāĻ¤ā§āϰ"
@@ -1051,14 +1039,37 @@ _time:
minute: "āĻŽāĻŋāύāĻŋāϟ"
hour: "āϘāĻŖā§āϟāĻž"
day: "āĻĻāĻŋāύ"
_tutorial:
title: "Misskey āĻ•āĻŋāĻ­āĻžāĻŦ⧇ āĻŦā§āϝāĻžāĻŦāĻšāĻžāϰ āĻ•āϰāĻŦ⧇āύ"
step1_1: "āĻ¸ā§āĻŦāĻžāĻ—āϤāĻŽ!"
step1_2: "āĻāχ āĻ¸ā§āĻ•ā§āϰ⧀āύāϟāĻŋāϕ⧇ \"āϟāĻžāχāĻŽāϞāĻžāχāύ\" āĻŦāϞāĻž āĻšāϝāĻŧ āĻāĻŦāĻ‚ āĻ•āĻžāϞāĻžāύ⧁āĻ•ā§āϰāĻŽāĻŋāĻ• āĻ•ā§āϰāĻŽā§‡ āφāĻĒāύāĻžāϰ āĻāĻŦāĻ‚ āφāĻĒāύāĻŋ āϝāĻžāĻĻ⧇āϰ \"āĻ…āύ⧁āϏāϰāĻŖ āĻ•āϰ⧇āύ\" āϤāĻžāĻĻ⧇āϰ \"āύ⧋āϟāϗ⧁āϞāĻŋ\" āĻĻ⧇āĻ–āĻžāϝāĻŧ⧎"
step1_3: "āφāĻĒāύāĻŋ āφāĻĒāύāĻžāϰ āϟāĻžāχāĻŽāϞāĻžāχāύ⧇ āĻ•āĻŋāϛ⧁ āĻĻ⧇āĻ–āϤ⧇ āĻĒāĻžāĻŦ⧇āύ āύāĻž āĻ•āĻžāϰāĻŖ āφāĻĒāύāĻŋ āĻāĻ–āύāĻ“ āϕ⧋āύ⧋ āύ⧋āϟ āĻĒā§‹āĻ¸ā§āϟ āĻ•āϰ⧇āύāύāĻŋ āĻāĻŦāĻ‚ āφāĻĒāύāĻŋ āĻ•āĻžāωāϕ⧇ āĻ…āύ⧁āϏāϰāĻŖ āĻ•āϰāϛ⧇āύ āύāĻžā§ˇ"
step2_1: "āύ⧋āϟ āϤ⧈āϰāĻŋ āĻ•āϰāĻžāϰ āφāϗ⧇ āĻŦāĻž āĻ•āĻžāωāϕ⧇ āĻ…āύ⧁āϏāϰāĻŖ āĻ•āϰāĻžāϰ āφāϗ⧇ āĻĒā§āϰāĻĨāĻŽā§‡ āφāĻĒāύāĻžāϰ āĻĒā§āϰ⧋āĻĢāĻžāχāϞāϟāĻŋ āϏāĻŽā§āĻĒā§‚āĻ°ā§āĻŖ āĻ•āϰ⧁āύāĨ¤"
step2_2: "āφāĻĒāύāĻŋ āϕ⧇ āϤāĻž āϜāĻžāύāĻž āĻ…āύ⧇āĻ• āϞ⧋āϕ⧇āϰ āϜāĻ¨ā§āϝ āφāĻĒāύāĻžāϰ āύ⧋āϟāϗ⧁āϞāĻŋ āĻĻ⧇āĻ–āĻž āĻāĻŦāĻ‚ āĻ…āύ⧁āϏāϰāĻŖ āĻ•āϰāĻžāϕ⧇ āϏāĻšāϜ āĻ•āϰ⧇ āϤ⧋āϞ⧇⧎"
step3_1: "āφāĻĒāύāĻŋ āĻ•āĻŋ āϏāĻĢāϞāĻ­āĻžāĻŦ⧇ āφāĻĒāύāĻžāϰ āĻĒā§āϰ⧋āĻĢāĻžāχāϞ āϏ⧇āϟ āφāĻĒ āĻ•āϰ⧇āϛ⧇āύ?"
step3_2: "āĻāĻ–āύ, āĻ•āĻŋāϛ⧁ āύ⧋āϟ āĻĒā§‹āĻ¸ā§āϟ āĻ•āϰāĻžāϰ āĻšā§‡āĻˇā§āϟāĻž āĻ•āϰ⧁āύāĨ¤ āĻĒā§‹āĻ¸ā§āϟ āĻĢāĻ°ā§āĻŽ āϖ⧁āϞāϤ⧇ āĻĒ⧇āĻ¨ā§āϏāĻŋāϞ āϚāĻŋāĻšā§āύāϝ⧁āĻ•ā§āϤ āĻŦāĻžāϟāύ⧇ āĻ•ā§āϞāĻŋāĻ• āĻ•āϰ⧁āύāĨ¤"
step3_3: "āĻŦāĻŋāώāϝāĻŧāĻŦāĻ¸ā§āϤ⧁ āϞ⧇āĻ–āĻžāϰ āĻĒāϰ⧇, āφāĻĒāύāĻŋ āĻĢāĻ°ā§āĻŽā§‡āϰ āωāĻĒāϰ⧇āϰ āĻĄāĻžāύāĻĻāĻŋāϕ⧇āϰ āĻŦāĻžāϟāύ⧇ āĻ•ā§āϞāĻŋāĻ• āĻ•āϰ⧇ āĻĒā§‹āĻ¸ā§āϟ āĻ•āϰāϤ⧇ āĻĒāĻžāϰ⧇āύāĨ¤"
step3_4: "āĻĒā§‹āĻ¸ā§āϟ āĻ•āϰāĻžāϰ āĻŽāϤ āĻ•āĻŋāϛ⧁ āĻŽāύ⧇ āĻĒāϰāϛ⧇ āύāĻž? \"āφāĻŽāĻŋ āĻŽāĻŋāϏāĻ•āĻŋ āϏ⧇āϟ āφāĻĒ āĻ•āϰāĻ›āĻŋ\" āĻŦāϞāϞ⧇ āϕ⧇āĻŽāύ āĻšāϝāĻŧ?"
step4_1: "āĻĒā§‹āĻ¸ā§āϟ āĻ•āϰ⧇āϛ⧇āύ?"
step4_2: "āϏāĻžāĻŦāĻžāĻļ! āĻāĻ–āύ āφāĻĒāύāĻžāϰ āύ⧋āϟ āϟāĻžāχāĻŽāϞāĻžāχāύ⧇ āĻĻ⧇āĻ–āĻž āϝāĻžāĻŦ⧇āĨ¤"
step5_1: "āĻāĻ–āύ āĻ…āĻ¨ā§āϝāĻĻ⧇āϰāϕ⧇ āĻ…āύ⧁āϏāϰāĻŖ āĻ•āϰ⧇ āφāĻĒāύāĻžāϰ āϟāĻžāχāĻŽāϞāĻžāχāύāϕ⧇ āĻĒā§āϰāĻžāĻŖāĻŦāĻ¨ā§āϤ āĻ•āϰ⧇ āϤ⧁āϞ⧁āύāĨ¤"
step5_2: "āφāĻĒāύāĻŋ {featured}-āĻ āϜāύāĻĒā§āϰāĻŋāϝāĻŧ āύ⧋āϟāϗ⧁āϞāĻŋ āĻĻ⧇āĻ–āϤ⧇ āĻĒāĻžāϰ⧇āύ, āϝāĻžāϤ⧇ āφāĻĒāύāĻŋ āϝ⧇ āĻŦā§āϝāĻ•ā§āϤāĻŋāϕ⧇ āĻĒāĻ›āĻ¨ā§āĻĻ āĻ•āϰ⧇āύ āϤāĻžāϕ⧇ āĻŦ⧇āϛ⧇ āύāĻŋāϤ⧇ āĻāĻŦāĻ‚ āĻ…āύ⧁āϏāϰāĻŖ āĻ•āϰāϤ⧇ āĻĒāĻžāϰ⧇āύ, āĻ…āĻĨāĻŦāĻž {explore}-āĻ āϜāύāĻĒā§āϰāĻŋāϝāĻŧ āĻŦā§āϝāĻŦāĻšāĻžāϰāĻ•āĻžāϰ⧀āĻĻ⧇āϰ āĻĻ⧇āĻ–āϤ⧇ āĻĒāĻžāϰ⧇āύ⧎"
step5_3: "āĻāĻ•āϜāύ āĻŦā§āϝāĻŦāĻšāĻžāϰāĻ•āĻžāϰ⧀āϕ⧇ āĻ…āύ⧁āϏāϰāĻŖ āĻ•āϰāϤ⧇, āĻŦā§āϝāĻŦāĻšāĻžāϰāĻ•āĻžāϰ⧀āϰ āφāχāĻ•āύ⧇ āĻ•ā§āϞāĻŋāĻ• āĻ•āϰ⧁āύ āĻāĻŦāĻ‚ āĻŦā§āϝāĻŦāĻšāĻžāϰāĻ•āĻžāϰ⧀āϰ āĻĒ⧃āĻˇā§āĻ āĻžāϤ⧇ \"āĻ…āύ⧁āϏāϰāĻŖ āĻ•āϰ⧁āύ\" āĻŦāĻžāϟāύ⧇ āĻ•ā§āϞāĻŋāĻ• āĻ•āϰ⧁āύāĨ¤"
step5_4: "āϝāĻĻāĻŋ āĻŦā§āϝāĻŦāĻšāĻžāϰāĻ•āĻžāϰ⧀āϰ āύāĻžāĻŽā§‡āϰ āĻĒāĻžāĻļ⧇ āĻāĻ•āϟāĻŋ āϞāĻ• āφāχāĻ•āύ āĻĨāĻžāϕ⧇ āϤāĻžāĻšāϞ⧇ āφāĻĒāύāĻžāϰ āĻ…āύ⧁āϏāϰāϪ⧇āϰ āĻ…āύ⧁āϰ⧋āϧ āĻ—ā§āϰāĻšāĻŖ āĻ•āϰāĻžāϰ āϜāĻ¨ā§āϝ āϤāĻžāϰāĻž āĻ•āĻŋāϛ⧁ āϏāĻŽāϝāĻŧ āύāĻŋāϤ⧇ āĻĒāĻžāϰ⧇āĨ¤"
step6_1: "āϏāĻŦāĻ•āĻŋāϛ⧁ āĻ āĻŋāĻ• āĻĨāĻžāĻ•āϞ⧇ āφāĻĒāύāĻŋ āϟāĻžāχāĻŽāϞāĻžāχāύ⧇ āĻ…āĻ¨ā§āϝ āĻŦā§āϝāĻŦāĻšāĻžāϰāĻ•āĻžāϰ⧀āĻĻ⧇āϰ āύ⧋āϟ āĻĻ⧇āĻ–āϤ⧇ āĻĒāĻžāĻŦ⧇āύāĨ¤"
step6_2: "āφāĻĒāύāĻŋ āϏāĻšāĻœā§‡āχ āφāĻĒāύāĻžāϰ āĻĒā§āϰāϤāĻŋāĻ•ā§āϰāĻŋāϝāĻŧāĻž āϜāĻžāύāĻžāϤ⧇ āĻ…āĻ¨ā§āϝ āĻŦā§āϝāĻ•ā§āϤāĻŋāϰ āύ⧋āĻŸā§‡ \"āϰāĻŋāĻ…ā§āϝāĻžāĻ•āĻļāύ\" āϝ⧋āĻ— āĻ•āϰāϤ⧇ āĻĒāĻžāϰ⧇āύāĨ¤"
step6_3: "āĻāĻ•āϟāĻŋ āϰāĻŋāĻ…ā§āϝāĻžāĻ•āĻļāύ āϝ⧋āĻ— āĻ•āϰāϤ⧇, āύ⧋āĻŸā§‡ \"+\" āϚāĻŋāĻšā§āύ⧇ āĻ•ā§āϞāĻŋāĻ• āĻ•āϰ⧁āύ āĻāĻŦāĻ‚ āφāĻĒāύāĻžāϰ āĻĒāĻ›āĻ¨ā§āĻĻ⧇āϰ āϰāĻŋāĻ…ā§āϝāĻžāĻ•āĻļāύ āύāĻŋāĻ°ā§āĻŦāĻžāϚāύ āĻ•āϰ⧁āύāĨ¤"
step7_1: "āĻ…āĻ­āĻŋāύāĻ¨ā§āĻĻāύ! āφāĻĒāύāĻŋ āĻāĻ–āύ Misskey-āϰ āĻĒā§āϰāĻžāĻĨāĻŽāĻŋāĻ• āϟāĻŋāωāĻŸā§‹āϰāĻŋāϝāĻŧāĻžāϞāϟāĻŋ āĻļ⧇āώ āĻ•āϰ⧇āϛ⧇āύāĨ¤"
step7_2: "āφāĻĒāύāĻŋ āϝāĻĻāĻŋ Misskey āϏāĻŽā§āĻĒāĻ°ā§āϕ⧇ āφāϰāĻ“ āϜāĻžāύāϤ⧇ āϚāĻžāύ, āϤāĻžāĻšāϞ⧇ {help} āĻ āĻĻ⧇āϖ⧁āύāĨ¤"
step7_3: "āĻāĻ–āύ Misskey āωāĻĒāĻ­ā§‹āĻ— āĻ•āϰ⧁āύ 🚀"
_2fa:
alreadyRegistered: "āφāĻĒāύāĻŋ āχāϤāĻŋāĻŽāĻ§ā§āϝ⧇ āĻāĻ•āϟāĻŋ 2-āĻĢā§āϝāĻžāĻ•ā§āϟāϰ āĻ…āĻĨ⧇āύāϟāĻŋāϕ⧇āĻļāύ āĻĄāĻŋāĻ­āĻžāχāϏ āύāĻŋāĻŦāĻ¨ā§āϧāύ āĻ•āϰ⧇āϛ⧇āύ⧎"
step1: "āĻĒā§āϰāĻĨāĻŽā§‡, āφāĻĒāύāĻžāϰ āĻĄāĻŋāĻ­āĻžāχāϏ⧇ {a} āĻŦāĻž {b} āĻāϰ āĻŽāϤ⧋ āĻāĻ•āϟāĻŋ āĻ…āĻĨ⧇āύāϟāĻŋāϕ⧇āĻļāύ āĻ…ā§āϝāĻžāĻĒ āχāύāĻ¸ā§āϟāϞ āĻ•āϰ⧁āύ⧎"
step2: "āĻāϰāĻĒāϰ⧇, āĻ…ā§āϝāĻžāĻĒ⧇āϰ āϏāĻžāĻšāĻžāĻ¯ā§āϝ⧇ āĻĒā§āϰāĻĻāĻ°ā§āĻļāĻŋāϤ QR āϕ⧋āĻĄāϟāĻŋ āĻ¸ā§āĻ•ā§āϝāĻžāύ āĻ•āϰ⧁āύāĨ¤"
step2Url: "āĻĄā§‡āĻ¸ā§āĻ•āϟāĻĒ āĻ…ā§āϝāĻžāĻĒ⧇, āύāĻŋāĻŽā§āύāϞāĻŋāĻ–āĻŋāϤ URL āϞāĻŋāϖ⧁āύ:"
step3: "āĻ…ā§āϝāĻžāĻĒ⧇ āĻĒā§āϰāĻĻāĻ°ā§āĻļāĻŋāϤ āĻŸā§‹āϕ⧇āύāϟāĻŋ āϞāĻŋāϖ⧁āύ āĻāĻŦāĻ‚ āφāĻĒāύāĻžāϰ āĻ•āĻžāϜ āĻļ⧇āώāĨ¤"
step4: "āφāĻĒāύāĻžāϕ⧇ āĻāĻ–āύ āĻĨ⧇āϕ⧇ āϞāĻ— āχāύ āĻ•āϰāĻžāϰ āϏāĻŽāϝāĻŧ, āĻāχāĻ­āĻžāĻŦ⧇ āĻŸā§‹āϕ⧇āύ āϞāĻŋāĻ–āϤ⧇ āĻšāĻŦ⧇āĨ¤"
securityKeyInfo: "āφāĻĒāύāĻŋ āĻāĻ•āϟāĻŋ āĻšāĻžāĻ°ā§āĻĄāĻ“āϝāĻŧā§āϝāĻžāϰ āϏāĻŋāĻ•āĻŋāωāϰāĻŋāϟāĻŋ āϕ⧀ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧇ āϞāĻ— āχāύ āĻ•āϰāϤ⧇ āĻĒāĻžāϰ⧇āύ āϝāĻž FIDO2 āĻŦāĻž āĻĄāĻŋāĻ­āĻžāχāϏ⧇āϰ āĻĢāĻŋāĻ™ā§āĻ—āĻžāϰāĻĒā§āϰāĻŋāĻ¨ā§āϟ āϏ⧇āĻ¨ā§āϏāϰ āĻŦāĻž āĻĒāĻŋāύ āϏāĻŽāĻ°ā§āĻĨāύ āĻ•āϰ⧇⧎"
renewTOTPCancel: "āύāĻž, āϧāĻ¨ā§āϝāĻŦāĻžāĻĻ"
_permissions:
"read:account": "āĻ…ā§āϝāĻžāĻ•āĻžāωāĻ¨ā§āĻŸā§‡āϰ āϤāĻĨā§āϝ āĻĻ⧇āϖ⧁āύ"
"write:account": "āĻ…ā§āϝāĻžāĻ•āĻžāωāĻ¨ā§āĻŸā§‡āϰ āϤāĻĨā§āϝ āϏāĻŽā§āĻĒāĻžāĻĻāύ āĻ•āϰ⧁āύ"
@@ -1092,7 +1103,6 @@ _permissions:
"write:gallery": "āĻ—ā§āϝāĻžāϞāĻžāϰ⧀ āϏāĻŽā§āĻĒāĻžāĻĻāύāĻž āĻ•āϰ⧁āύ"
"read:gallery-likes": "āĻ—ā§āϝāĻžāϞāĻžāϰ⧀āϰ āĻĒāĻ›āĻ¨ā§āĻĻāϗ⧁āϞāĻŋ āĻĻ⧇āϖ⧁āύ"
"write:gallery-likes": "āĻ—ā§āϝāĻžāϞāĻžāϰ⧀āϰ āĻĒāĻ›āĻ¨ā§āĻĻāϗ⧁āϞāĻŋ āϏāĻŽā§āĻĒāĻžāĻĻāύāĻž āĻ•āϰ⧁āύ"
"write:chat": "āĻšā§āϝāĻžāϟāϗ⧁āϞāĻŋ āϏāĻŽā§āĻĒāĻžāĻĻāύāĻž āĻ•āϰ⧁āύ"
_auth:
shareAccess: "\"{name}\" āϕ⧇ āĻ…ā§āϝāĻžāĻ•āĻžāωāĻ¨ā§āĻŸā§‡āϰ āĻ…ā§āϝāĻžāĻ•ā§āϏ⧇āϏ āĻĻāĻŋāĻŦ⧇āύ?"
shareAccessAsk: "āĻ…ā§āϝāĻžāĻĒā§āϞāĻŋāϕ⧇āĻļāύāϟāĻŋāϕ⧇ āĻ…ā§āϝāĻžāĻ•āĻžāωāĻ¨ā§āĻŸā§‡āϰ āĻ…ā§āϝāĻžāĻ•ā§āϏ⧇āϏ āĻĻāĻŋāĻŦ⧇āύ?"
@@ -1177,9 +1187,6 @@ _postForm:
replyPlaceholder: "āύ⧋āϟāϟāĻŋāϰ āϜāĻŦāĻžāĻŦ āĻĻāĻŋāύ..."
quotePlaceholder: "āύ⧋āϟāϟāĻŋāϕ⧇ āωāĻĻā§āϧ⧃āϤ āĻ•āϰ⧁āύ..."
channelPlaceholder: "āĻšā§āϝāĻžāύ⧇āϞ⧇ āĻĒā§‹āĻ¸ā§āϟ āĻ•āϰ⧁āύ..."
_howToUse:
visibility_title: "āĻĻ⧃āĻļā§āϝāĻŽāĻžāύāϤāĻž"
menu_title: "āĻŽā§‡āύ⧁"
_placeholders:
a: "āφāĻĒāύāĻŋ āĻāĻ–āύ āĻ•āĻŋ āĻ•āϰāϛ⧇āύ?"
b: "āφāĻĒāύāĻžāϰ āφāĻļ⧇ āĻĒāĻžāĻļ⧇ āĻ•āĻŋ āĻšāĻšā§āϛ⧇?"
@@ -1201,7 +1208,6 @@ _profile:
changeBanner: "āĻŦā§āϝāĻžāύāĻžāϰ āĻĒāϰāĻŋāĻŦāĻ°ā§āϤāύ āĻ•āϰ⧁āύ"
_exportOrImport:
allNotes: "āϏāĻ•āϞ āύ⧋āϟ"
clips: "āĻ•ā§āϞāĻŋāĻĒ"
followingList: "āĻ…āύ⧁āϏāϰāĻŖ āĻ•āϰāĻž āĻšāĻšā§āϛ⧇"
muteList: "āĻŽāĻŋāωāϟ"
blockingList: "āĻŦā§āϞāĻ•"
@@ -1249,6 +1255,9 @@ _pages:
newPage: "āύāϤ⧁āύ āĻĒ⧃āĻˇā§āĻ āĻž āĻŦāĻžāύāĻžāύ"
editPage: "āĻĒ⧃āĻˇā§āĻ āĻžāϟāĻŋ āϏāĻŽā§āĻĒāĻžāĻĻāύāĻž āĻ•āϰ⧁āύ"
readPage: "āĻ‰ā§ŽāϏ āĻĻ⧇āĻ–āϛ⧇āύ"
created: "āĻĒ⧃āĻˇā§āĻ āĻž āϤ⧈āϰāĻŋ āĻ•āϰāĻž āĻšā§Ÿā§‡āϛ⧇"
updated: "āĻĒ⧃āĻˇā§āĻ āĻž āϏāĻŽā§āĻĒāĻžāĻĻāύāĻž āĻ•āϰāĻž āĻšā§Ÿā§‡āϛ⧇"
deleted: "āĻĒ⧃āĻˇā§āĻ āĻž āĻŽā§āϛ⧇ āĻĢ⧇āϞāĻž āĻšā§Ÿā§‡āϛ⧇"
pageSetting: "āĻĒ⧃āĻˇā§āĻ āĻžāϰ āϏ⧇āϟāĻŋāĻ‚āϏ"
nameAlreadyExists: "āĻĒ⧃āĻˇā§āĻ āĻžāϰ URLāϟāĻŋ āχāϤāĻŋāĻŽāĻ§ā§āϝ⧇āχ āĻŦā§āϝāĻžāĻŦāĻšāĻžāϰ āĻ•āϰāĻž āĻšā§Ÿā§‡āϛ⧇"
invalidNameTitle: "āĻĒ⧃āĻˇā§āĻ āĻžāϰ URL āĻ…āĻŦ⧈āϧ"
@@ -1317,7 +1326,6 @@ _notification:
pollEnded: "āĻĒā§‹āϞ āĻļ⧇āώ"
receiveFollowRequest: "āĻĒā§āϰāĻžāĻĒā§āϤ āĻ…āύ⧁āϏāϰāϪ⧇āϰ āĻ…āύ⧁āϰ⧋āϧāϏāĻŽā§‚āĻš"
followRequestAccepted: "āĻ—ā§ƒāĻšā§€āϤ āĻ…āύ⧁āϏāϰāϪ⧇āϰ āĻ…āύ⧁āϰ⧋āϧāϏāĻŽā§‚āĻš"
login: "āĻĒā§āϰāĻŦ⧇āĻļ āĻ•āϰ⧁āύ"
app: "āϞāĻŋāĻ™ā§āĻ• āĻ•āϰāĻž āĻ…ā§āϝāĻžāĻĒ āĻĨ⧇āϕ⧇ āĻŦāĻŋāĻœā§āĻžāĻĒā§āϤāĻŋ"
_actions:
followBack: "āĻĢāϞ⧋ āĻŦā§āϝāĻžāĻ• āĻ•āϰ⧇āϛ⧇"
@@ -1348,34 +1356,4 @@ _deck:
_webhookSettings:
name: "āύāĻžāĻŽ"
active: "āϚāĻžāϞ⧁"
_abuseReport:
_notificationRecipient:
_recipientType:
mail: "āχāĻŽā§‡āχāϞ"
_moderationLogTypes:
suspend: "āĻ¸ā§āĻĨāĻ—āĻŋāϤ āĻ•āϰāĻž"
resetPassword: "āĻĒāĻžāϏāĻ“āϝāĻŧāĻžāĻ°ā§āĻĄ āϰāĻŋāϏ⧇āϟ āĻ•āϰ⧁āύ"
_reversi:
total: "āĻŽā§‹āϟ"
_remoteLookupErrors:
_noSuchObject:
title: "āĻĒāĻžāĻ“ā§ŸāĻž āϝāĻžā§ŸāύāĻŋ"
_search:
searchScopeAll: "āϏāĻŦāϗ⧁āϞ⧋"
searchScopeLocal: "āĻ¸ā§āĻĨāĻžāύ⧀āϝāĻŧ"
_watermarkEditor:
opacity: "āĻ…āĻ¸ā§āĻŦāĻšā§āĻ›āϤāĻž"
scale: "āφāĻ•āĻžāϰ"
text: "āϞ⧇āĻ–āĻž"
image: "āĻ›āĻŦāĻŋ"
advanced: "āωāĻ¨ā§āύāϤ"
_imageEffector:
_fxProps:
scale: "āφāĻ•āĻžāϰ"
size: "āφāĻ•āĻžāϰ"
color: "āϰāĻ‚"
opacity: "āĻ…āĻ¸ā§āĻŦāĻšā§āĻ›āϤāĻž"
lightness: "āωāĻœā§āĻœā§āĻŦāϞ āĻ•āϰ⧁āύ"
_qr:
showTabTitle: "āĻĒā§āϰāĻĻāĻ°ā§āĻļāύ"
raw: "āϞ⧇āĻ–āĻž"

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