Compare commits
197 Commits
Author | SHA1 | Date | |
---|---|---|---|
72d9a46156 | |||
683a85ecc0 | |||
89047fd66d | |||
8ca27a4480 | |||
77d2d84e05 | |||
3bde4285eb | |||
15efbfb244 | |||
55a57db958 | |||
91f624c900 | |||
d753489e49 | |||
36dd7c82db | |||
b00db74216 | |||
cf9f85eb2a | |||
bfa8414289 | |||
58dc9c9544 | |||
ba95ce18a8 | |||
ac10c40f31 | |||
de8b40c80f | |||
60fe69728d | |||
557607e842 | |||
40ac9de728 | |||
a725cd1946 | |||
b648706756 | |||
20f2e9fc6d | |||
858ced0a53 | |||
c9ca8f777f | |||
0d0be31527 | |||
96305a088c | |||
22e30b44b9 | |||
48d0e2fa5f | |||
0489291815 | |||
7a412500e1 | |||
dffaa72a88 | |||
152d61f906 | |||
b44ded5fc9 | |||
99d77cc843 | |||
bc385eec2d | |||
2c6b9403b3 | |||
89cdc59fe5 | |||
3e04e1ccea | |||
25f491a65f | |||
7fdde157e0 | |||
27f30a449a | |||
dc66a0cd66 | |||
f302ecd1e2 | |||
1afc2a227a | |||
ab594d1dfe | |||
647d32f506 | |||
1bc109b42c | |||
6e02e2a6b3 | |||
3372baad6c | |||
e9b3e3877a | |||
c84c5fbae7 | |||
2e7ab91f48 | |||
92c41c003a | |||
53688cdcdc | |||
70f864ea8b | |||
5dd94e551a | |||
afc1ed9897 | |||
df7562f98d | |||
1c7e799164 | |||
107a3f99f4 | |||
7e3cf4b4ad | |||
afdb23ff57 | |||
0ce64f8c33 | |||
2cd6ccb85c | |||
3dcf5374c2 | |||
1c7d5f3f64 | |||
bb0cb0a866 | |||
362dc29057 | |||
8af0218e4c | |||
09af9968b5 | |||
7d0b819c5a | |||
4bbb7eded3 | |||
72ea9e5522 | |||
743e5d947d | |||
f257853906 | |||
32983d3829 | |||
bdef33e88d | |||
a4a4194586 | |||
e36c538dc0 | |||
982a218bf2 | |||
f7aeca4c7c | |||
4b81bc864f | |||
a52cc62c04 | |||
81f69bff9e | |||
d9b2aa1880 | |||
3bddd986a0 | |||
d00712d817 | |||
d756fdbebf | |||
b2f2c74605 | |||
1407cd71b5 | |||
3dd3a7238d | |||
afc834ae4b | |||
357f190ce3 | |||
ab92762320 | |||
04942c8477 | |||
9c60f51e0d | |||
956ae2ac46 | |||
f2ed813337 | |||
7d9a7d1a3c | |||
2f67b6fb47 | |||
7444dde93e | |||
e62fad7bc6 | |||
2c6bad2501 | |||
6952bb2eca | |||
610972dbde | |||
926049055a | |||
db5f6b60df | |||
7be7b9d9fd | |||
366855c4cc | |||
e950beeeab | |||
5515b39f6c | |||
f5d0599e4d | |||
8fd7914f0f | |||
24ef98eb01 | |||
b3dd6cf001 | |||
6d4b474cb1 | |||
0a8876a3ae | |||
9a83a48331 | |||
7ed50b90bd | |||
b6fd5d7282 | |||
33243e7176 | |||
e8439679a5 | |||
06124dbbd5 | |||
857940f402 | |||
bcb04924ff | |||
2b6aeb9ebc | |||
648e4538fb | |||
cd4b3777da | |||
4acf506b7e | |||
543ca348bc | |||
03375412f1 | |||
0c7a9b6827 | |||
c54bc3c176 | |||
90973bedf4 | |||
757f70162e | |||
3047410b35 | |||
9d0d4c5eae | |||
cab5b4d601 | |||
517b6e25d9 | |||
94d394c29e | |||
a52099d175 | |||
f1cb7b862d | |||
0863e5d379 | |||
55dcd25df1 | |||
f3155ea180 | |||
2c5162671c | |||
fc8aeb5a66 | |||
995cf503eb | |||
0e49c11a4c | |||
0367c37b0a | |||
e0b9fe5e5d | |||
a4726e683b | |||
3b10e93efe | |||
02b07c1b5b | |||
5e54751bd4 | |||
34f1dc238d | |||
3c0f4bdf35 | |||
a68d370ad0 | |||
debb27ed30 | |||
97fbb857fb | |||
93f13ffc8e | |||
4d9dc93d46 | |||
22cd4c5d16 | |||
6883994b22 | |||
852a312753 | |||
afea0baaa8 | |||
2c477660a0 | |||
16a1cd3c9d | |||
db23dd2e01 | |||
eaa1ded5ba | |||
393e14196a | |||
8b005307ea | |||
c915ac0e72 | |||
1752bb896e | |||
9c8fa026f3 | |||
e93db6324a | |||
a10708bffa | |||
e65a7a142f | |||
72e0b2d470 | |||
43fb0f36a7 | |||
cf3533221b | |||
de522ebe14 | |||
2ace891dde | |||
e212d9f991 | |||
739f3f84d0 | |||
5e206c86be | |||
1ab37011ea | |||
b6e02f63cd | |||
60e10d4efa | |||
95ba7e43b1 | |||
9e5a2e5b17 | |||
dbbc416095 | |||
a479ad357c | |||
b1c12abb7c | |||
17d1a1d7bd |
1
.gitignore
vendored
1
.gitignore
vendored
@ -17,3 +17,4 @@ api-docs.json
|
||||
/mongo
|
||||
/elasticsearch
|
||||
*.code-workspace
|
||||
yarn.lock
|
||||
|
12
Dockerfile
12
Dockerfile
@ -8,18 +8,20 @@ WORKDIR /misskey
|
||||
|
||||
FROM base AS builder
|
||||
|
||||
RUN unlink /usr/bin/free
|
||||
RUN apk add --no-cache \
|
||||
gcc \
|
||||
g++ \
|
||||
libc-dev \
|
||||
python \
|
||||
autoconf \
|
||||
automake \
|
||||
file \
|
||||
g++ \
|
||||
gcc \
|
||||
libc-dev \
|
||||
libtool \
|
||||
make \
|
||||
nasm \
|
||||
pkgconfig \
|
||||
libtool \
|
||||
procps \
|
||||
python \
|
||||
zlib-dev
|
||||
RUN npm i -g node-gyp
|
||||
|
||||
|
18
README.md
18
README.md
@ -73,46 +73,50 @@ Please see [Contribution guide](./CONTRIBUTING.md).
|
||||
----------------------------------------------------------------
|
||||
<!-- PATREON_START -->
|
||||
<table><tr>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/12190916/fb7fa7983c14425f890369535b1506a4/1?token-time=2145916800&token-hash=Zeh1u6l_Vmgoy8A1eT1Sltea-_SZSq8t8uOWDRZRh94%3D" alt="weep"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/13376668/71f3cf87ec6c4393a44b1b9df5ee3d12/1?token-time=2145916800&token-hash=7pSmWqgMfMSJHVIEcNsuuQoKeU3TRluew5p0EGTzWA4%3D" alt="Arctic"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/12731202/0995c46cdcb54153ab5f073f5869b70a/1?token-time=2145916800&token-hash=Yd60FK_SWfQO56SeiJpy1tDHOnCV4xdEywQe8gn5_Wo%3D" alt="negao"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/12913507/f7181eacafe8469a93033d85f5969c29/2?token-time=2145916800&token-hash=mgPdX9TqZxEg4TTPuc477dxhIgYk9246qafjWZEqZ7g%3D" alt="Melilot"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/3384329/8b713330cb27404ea6e9fac50ff96efe/1?token-time=2145916800&token-hash=0eu4-m1gTWA9PhptVZt6rdKcusqcD7RB87rJT23VVFI%3D" alt="べすれい"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/12021162/963128bb8d14476dbd8407943db8f31a/1?token-time=2145916800&token-hash=GgJ_NmUB6_nnRNLVGUWjV-WX91On7BOu59LKncYV9fE%3D" alt="gutfuckllc"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/11357794/923ce94cd8c44ba788ee931907881839/1?token-time=2145916800&token-hash=I8lJVM8LeW6TSo5W6uIIRZ42cw83zp1wK_FsbzY0mcQ%3D" alt="mydarkstar"></td>
|
||||
<td><img src="https://c8.patreon.com/2/100/12718187" alt="Peter G."></td>
|
||||
</tr><tr>
|
||||
<td><a href="https://www.patreon.com/weepjp">weep</a></td>
|
||||
<td><a href="https://www.patreon.com/user?u=13376668">Arctic</a></td>
|
||||
<td><a href="https://www.patreon.com/negao">negao</a></td>
|
||||
<td><a href="https://www.patreon.com/user?u=12913507">Melilot</a></td>
|
||||
<td><a href="https://www.patreon.com/user?u=3384329">べすれい</a></td>
|
||||
<td><a href="https://www.patreon.com/gutfuckllc">gutfuckllc</a></td>
|
||||
<td><a href="https://www.patreon.com/mydarkstar">mydarkstar</a></td>
|
||||
<td><a href="https://www.patreon.com/user?u=12718187">Peter G.</a></td>
|
||||
</tr></table>
|
||||
<table><tr>
|
||||
<td><img src="https://c8.patreon.com/2/100/12718187" alt="Peter G."></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/13039004/509d0c412eb14ae08d6a812a3054f7d6/1?token-time=2145916800&token-hash=zwSu01tOtn5xTUucDZHuPsCxF2HBEMVs9ROJKTlEV_o%3D" alt="nemu"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/5881381/6235ca5d3fb04c8e95ef5b4ff2abcc18/3?token-time=2145916800&token-hash=qsdn0-e6yLaLI6hUX9JAkyTR6a5UdnSp7T1foniBvGQ%3D" alt="YUKIMOCHI"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/8241184/39e18850e87a449e9c9a71acb3310ebd/2?token-time=2145916800&token-hash=iUXOQzRyJDv3PJxwS7Mjwg1459dzh2trOq6NFtXu_OM%3D" alt="Acid Chicken"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/13034746/c711c7f58e204ecfbc2fd646bc8a4eee/1?token-time=2145916800&token-hash=UERBN4OyP7Nh5XwwdDg0N0IE5cD6_qUQMO81Z5Wizso%3D" alt="Hiratake"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/10789744/97175095d8f04c0f86225ff47cb98d40/1?token-time=2145916800&token-hash=P4BIzCX2I1CkEP66ottfhsC8Wr6BUSamjA-vq3pLqFI%3D" alt="Naoki Hirayama"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/4503830/ccf2cc867ea64de0b524bb2e24b9a1cb/1?token-time=2145916800&token-hash=S1zP0QyLU52Dqq6dtc9qNYyWfW86XrYHiR4NMbeOrnA%3D" alt="dansup"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/619786/32cf01444db24e578cd1982c197f6fc6/1?token-time=2145916800&token-hash=tB1e_r8RlZ5sFL0KV_e8dugapxatNBRK1Z3h67TO1g8%3D" alt="Gargron"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/5731881/4b6038e6cda34c04b83a5fcce3806a93/1?token-time=2145916800&token-hash=VZUtwrjQa8Jml4twCjHYQQZ64wHEY4oIlGl7Kc-VYUQ%3D" alt="Nokotaro Takeda"></td>
|
||||
</tr><tr>
|
||||
<td><a href="https://www.patreon.com/user?u=12718187">Peter G.</a></td>
|
||||
<td><a href="https://www.patreon.com/user?u=13039004">nemu</a></td>
|
||||
<td><a href="https://www.patreon.com/yukimochi">YUKIMOCHI</a></td>
|
||||
<td><a href="https://www.patreon.com/acid_chicken">Acid Chicken</a></td>
|
||||
<td><a href="https://www.patreon.com/hiratake">Hiratake</a></td>
|
||||
<td><a href="https://www.patreon.com/spinlock">Naoki Hirayama</a></td>
|
||||
<td><a href="https://www.patreon.com/dansup">dansup</a></td>
|
||||
<td><a href="https://www.patreon.com/mastodon">Gargron</a></td>
|
||||
<td><a href="https://www.patreon.com/takenoko">Nokotaro Takeda</a></td>
|
||||
</tr></table>
|
||||
<table><tr>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/619786/32cf01444db24e578cd1982c197f6fc6/1?token-time=2145916800&token-hash=tB1e_r8RlZ5sFL0KV_e8dugapxatNBRK1Z3h67TO1g8%3D" alt="Gargron"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/5731881/4b6038e6cda34c04b83a5fcce3806a93/1?token-time=2145916800&token-hash=VZUtwrjQa8Jml4twCjHYQQZ64wHEY4oIlGl7Kc-VYUQ%3D" alt="Nokotaro Takeda"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/12531784/93a45137841849329ba692da92ac7c60/1?token-time=2145916800&token-hash=tMosUojzUYJCH_3t--tvYA-SMCyrS__hzSndyaRSnbo%3D" alt="Takashi Shibuya"></td>
|
||||
</tr><tr>
|
||||
<td><a href="https://www.patreon.com/mastodon">Gargron</a></td>
|
||||
<td><a href="https://www.patreon.com/takenoko">Nokotaro Takeda</a></td>
|
||||
<td><a href="https://www.patreon.com/user?u=12531784">Takashi Shibuya</a></td>
|
||||
</tr></table>
|
||||
|
||||
**Last updated:** Fri, 23 Nov 2018 14:09:04 UTC
|
||||
**Last updated:** Tue, 27 Nov 2018 06:24:05 UTC
|
||||
<!-- PATREON_END -->
|
||||
|
||||
:four_leaf_clover: Copyright
|
||||
|
@ -26,6 +26,7 @@ common:
|
||||
close: "閉じる"
|
||||
do-not-copy-paste: "ここにコードを入力したり張り付けたりしないでください。アカウントが不正利用される可能性があります。"
|
||||
load-more: "もっと読み込む"
|
||||
enter-password: "パスワードを入力してください"
|
||||
got-it: "わかった"
|
||||
customization-tips:
|
||||
title: "カスタマイズのヒント"
|
||||
@ -457,6 +458,10 @@ common/views/components/profile-editor.vue:
|
||||
saved: "プロフィールを保存しました"
|
||||
uploading: "アップロード中"
|
||||
upload-failed: "アップロードに失敗しました"
|
||||
email: "メール設定"
|
||||
email-address: "メールアドレス"
|
||||
email-verified: "メールアドレスが確認されました"
|
||||
email-not-verified: "メールアドレスが確認されていません。メールボックスをご確認ください。"
|
||||
common/views/widgets/broadcast.vue:
|
||||
fetching: "確認中"
|
||||
no-broadcasts: "お知らせはありません"
|
||||
@ -731,6 +736,8 @@ desktop/views/components/settings.vue:
|
||||
note-visibility: "投稿の公開範囲"
|
||||
default-note-visibility: "デフォルトの公開範囲"
|
||||
remember-note-visibility: "投稿の公開範囲を記憶する"
|
||||
web-search-engine: "ウェブ検索エンジン"
|
||||
web-search-engine-desc: "例: https://www.google.com/?#q={{query}}"
|
||||
auto-popout: "ウィンドウの自動ポップアウト"
|
||||
auto-popout-desc: "ウィンドウが開かれるとき、ポップアウト(ブラウザ外に切り離す)可能なら自動でポップアウトします。この設定はブラウザに記憶されます。"
|
||||
advanced: "詳細設定"
|
||||
@ -966,17 +973,17 @@ admin/views/instance.vue:
|
||||
recaptcha-site-key: "reCAPTCHA site key"
|
||||
recaptcha-secret-key: "reCAPTCHA secret key"
|
||||
twitter-integration-config: "Twitter連携の設定"
|
||||
twitter-integration-info: "コールバックURLは /api/tw/cb に設定します。"
|
||||
twitter-integration-info: "コールバックURLは {url} に設定します。"
|
||||
enable-twitter-integration: "Twitter連携を有効にする"
|
||||
twitter-integration-consumer-key: "Consumer key"
|
||||
twitter-integration-consumer-secret: "Consumer secret"
|
||||
github-integration-config: "GitHub連携の設定"
|
||||
github-integration-info: "コールバックURLは /api/gh/cb に設定します。"
|
||||
github-integration-info: "コールバックURLは {url} に設定します。"
|
||||
enable-github-integration: "GitHub連携を有効にする"
|
||||
github-integration-client-id: "Client ID"
|
||||
github-integration-client-secret: "Client Secret"
|
||||
discord-integration-config: "Discord連携の設定"
|
||||
discord-integration-info: "コールバックURLは /api/dc/cb に設定します。"
|
||||
discord-integration-info: "コールバックURLは {url} に設定します。"
|
||||
enable-discord-integration: "Discord連携を有効にする"
|
||||
discord-integration-client-id: "Client ID"
|
||||
discord-integration-client-secret: "Client Secret"
|
||||
@ -997,6 +1004,15 @@ admin/views/instance.vue:
|
||||
external-user-recommendation-engine-desc: "例: https://vinayaka.distsn.org/cgi-bin/vinayaka-user-match-misskey-api.cgi?{{host}}+{{user}}+{{limit}}+{{offset}}"
|
||||
external-user-recommendation-timeout: "タイムアウト"
|
||||
external-user-recommendation-timeout-desc: "ミリ秒単位 (例: 300000)"
|
||||
email-config: "メールサーバーの設定"
|
||||
email-config-info: "メールアドレス確認やパスワードリセットの際に使われます。"
|
||||
enable-email: "メール配信を有効にする"
|
||||
email: "メールアドレス"
|
||||
smtp-use-ssl: "SMTPサーバーはSSLを使用"
|
||||
smtp-host: "SMTPホスト"
|
||||
smtp-port: "SMTPポート"
|
||||
smtp-user: "SMTPユーザー"
|
||||
smtp-pass: "SMTPパスワード"
|
||||
admin/views/charts.vue:
|
||||
title: "チャート"
|
||||
per-day: "1日ごと"
|
||||
@ -1371,6 +1387,8 @@ mobile/views/pages/settings.vue:
|
||||
note-visibility: "投稿の公開範囲"
|
||||
default-note-visibility: "デフォルトの公開範囲"
|
||||
remember-note-visibility: "投稿の公開範囲を記憶する"
|
||||
web-search-engine: "ウェブ検索エンジン"
|
||||
web-search-engine-desc: "例: https://www.google.com/?#q={{query}}"
|
||||
disable-via-mobile: "「モバイルからの投稿」フラグを付けない"
|
||||
load-raw-images: "添付された画像を高画質で表示する"
|
||||
load-remote-media: "リモートサーバーのメディアを表示する"
|
||||
|
@ -26,6 +26,7 @@ common:
|
||||
close: "Schließen"
|
||||
do-not-copy-paste: "ここにコードを入力したり張り付けたりしないでください。アカウントが不正利用される可能性があります。"
|
||||
load-more: "もっと読み込む"
|
||||
enter-password: "パスワードを入力してください"
|
||||
got-it: "Verstanden!"
|
||||
customization-tips:
|
||||
title: "Anpassung-Tipps"
|
||||
@ -457,6 +458,10 @@ common/views/components/profile-editor.vue:
|
||||
saved: "プロフィールを保存しました"
|
||||
uploading: "アップロード中"
|
||||
upload-failed: "アップロードに失敗しました"
|
||||
email: "メール設定"
|
||||
email-address: "メールアドレス"
|
||||
email-verified: "メールアドレスが確認されました"
|
||||
email-not-verified: "メールアドレスが確認されていません。メールボックスをご確認ください。"
|
||||
common/views/widgets/broadcast.vue:
|
||||
fetching: "Laden"
|
||||
no-broadcasts: "Keine Broadcasts"
|
||||
@ -731,6 +736,8 @@ desktop/views/components/settings.vue:
|
||||
note-visibility: "投稿の公開範囲"
|
||||
default-note-visibility: "デフォルトの公開範囲"
|
||||
remember-note-visibility: "投稿の公開範囲を記憶する"
|
||||
web-search-engine: "ウェブ検索エンジン"
|
||||
web-search-engine-desc: "例: https://www.google.com/?#q={{query}}"
|
||||
auto-popout: "Automatische Pop-out Fenster"
|
||||
auto-popout-desc: "Pop-out ein offenes Fenster wenn möglich. Diese Einstellung wird im Browser gespeichert."
|
||||
advanced: "Erweiterte Einstellungen"
|
||||
@ -966,17 +973,17 @@ admin/views/instance.vue:
|
||||
recaptcha-site-key: "reCAPTCHA site key"
|
||||
recaptcha-secret-key: "reCAPTCHA secret key"
|
||||
twitter-integration-config: "Twitter連携の設定"
|
||||
twitter-integration-info: "コールバックURLは /api/tw/cb に設定します。"
|
||||
twitter-integration-info: "コールバックURLは {url} に設定します。"
|
||||
enable-twitter-integration: "Twitter連携を有効にする"
|
||||
twitter-integration-consumer-key: "Consumer key"
|
||||
twitter-integration-consumer-secret: "Consumer secret"
|
||||
github-integration-config: "GitHub連携の設定"
|
||||
github-integration-info: "コールバックURLは /api/gh/cb に設定します。"
|
||||
github-integration-info: "コールバックURLは {url} に設定します。"
|
||||
enable-github-integration: "GitHub連携を有効にする"
|
||||
github-integration-client-id: "Client ID"
|
||||
github-integration-client-secret: "Client Secret"
|
||||
discord-integration-config: "Discord連携の設定"
|
||||
discord-integration-info: "コールバックURLは /api/dc/cb に設定します。"
|
||||
discord-integration-info: "コールバックURLは {url} に設定します。"
|
||||
enable-discord-integration: "Discord連携を有効にする"
|
||||
discord-integration-client-id: "Client ID"
|
||||
discord-integration-client-secret: "Client Secret"
|
||||
@ -997,6 +1004,15 @@ admin/views/instance.vue:
|
||||
external-user-recommendation-engine-desc: "例: https://vinayaka.distsn.org/cgi-bin/vinayaka-user-match-misskey-api.cgi?{{host}}+{{user}}+{{limit}}+{{offset}}"
|
||||
external-user-recommendation-timeout: "タイムアウト"
|
||||
external-user-recommendation-timeout-desc: "ミリ秒単位 (例: 300000)"
|
||||
email-config: "メールサーバーの設定"
|
||||
email-config-info: "メールアドレス確認やパスワードリセットの際に使われます。"
|
||||
enable-email: "メール配信を有効にする"
|
||||
email: "メールアドレス"
|
||||
smtp-use-ssl: "SMTPサーバーはSSLを使用"
|
||||
smtp-host: "SMTPホスト"
|
||||
smtp-port: "SMTPポート"
|
||||
smtp-user: "SMTPユーザー"
|
||||
smtp-pass: "SMTPパスワード"
|
||||
admin/views/charts.vue:
|
||||
title: "チャート"
|
||||
per-day: "1日ごと"
|
||||
@ -1371,6 +1387,8 @@ mobile/views/pages/settings.vue:
|
||||
note-visibility: "投稿の公開範囲"
|
||||
default-note-visibility: "デフォルトの公開範囲"
|
||||
remember-note-visibility: "投稿の公開範囲を記憶する"
|
||||
web-search-engine: "ウェブ検索エンジン"
|
||||
web-search-engine-desc: "例: https://www.google.com/?#q={{query}}"
|
||||
disable-via-mobile: "「モバイルからの投稿」フラグを付けない"
|
||||
load-raw-images: "添付された画像を高画質で表示する"
|
||||
load-remote-media: "リモートサーバーのメディアを表示する"
|
||||
|
@ -26,6 +26,7 @@ common:
|
||||
close: "Close"
|
||||
do-not-copy-paste: "Please do not enter or paste the code here. Account may be compromised."
|
||||
load-more: "Load more"
|
||||
enter-password: "パスワードを入力してください"
|
||||
got-it: "Got it!"
|
||||
customization-tips:
|
||||
title: "Customization tips"
|
||||
@ -90,9 +91,9 @@ common:
|
||||
specified: "Direct"
|
||||
specified-desc: "Post to specified users only"
|
||||
private: "Private"
|
||||
local-public: "Local (Public)"
|
||||
local-home: "Local (Home)"
|
||||
local-followers: "Local (Followers)"
|
||||
local-public: "Public (Only local)"
|
||||
local-home: "Home (Only local)"
|
||||
local-followers: "Followers (Only local)"
|
||||
note-placeholders:
|
||||
a: "What are you doing?"
|
||||
b: "What's happening?"
|
||||
@ -208,7 +209,7 @@ common/views/components/games/reversi/reversi.index.vue:
|
||||
invitations: "You’ve got an invitation!"
|
||||
my-games: "My game"
|
||||
all-games: "All games"
|
||||
enter-username: "Enter a username"
|
||||
enter-username: "Please enter username"
|
||||
game-state:
|
||||
ended: "Finished"
|
||||
playing: "In Progress"
|
||||
@ -219,7 +220,7 @@ common/views/components/games/reversi/reversi.room.vue:
|
||||
black-or-white: "Black/White"
|
||||
black-is: "Black is {}"
|
||||
rules: "Rules"
|
||||
is-llotheo: "The lesser side wins"
|
||||
is-llotheo: "The lesser one wins(Llotheo)"
|
||||
looped-map: "Looped map"
|
||||
can-put-everywhere: "Can put everywhere"
|
||||
settings-of-the-bot: "Bot settings"
|
||||
@ -433,8 +434,8 @@ common/views/components/visibility-chooser.vue:
|
||||
private: "Private"
|
||||
local-public: "Local (Public)"
|
||||
local-public-desc: "Do not publish to remote"
|
||||
local-home: "Local (Home)"
|
||||
local-followers: "Local (Followers)"
|
||||
local-home: "Home (Only local)"
|
||||
local-followers: "Followers (Only local)"
|
||||
common/views/components/trends.vue:
|
||||
count: "{} users mentioned"
|
||||
empty: "No popular hashtag trends"
|
||||
@ -445,20 +446,24 @@ common/views/components/profile-editor.vue:
|
||||
location: "Location"
|
||||
description: "About me"
|
||||
birthday: "Birthday"
|
||||
avatar: "Avatar"
|
||||
avatar: "Icon"
|
||||
banner: "Banner"
|
||||
is-cat: "This account is a Cat"
|
||||
is-bot: "This account is a Bot"
|
||||
is-locked: "Follower requests require approval"
|
||||
careful-bot: "Follower requests from bots require approval"
|
||||
advanced: "Advanced"
|
||||
advanced: "Other"
|
||||
privacy: "Privacy"
|
||||
save: "Update profile"
|
||||
save: "Save"
|
||||
saved: "Profile updated successfully"
|
||||
uploading: "Uploading"
|
||||
upload-failed: "Failed to upload"
|
||||
email: "Email settings"
|
||||
email-address: "Email Address"
|
||||
email-verified: "Your email has been verified."
|
||||
email-not-verified: "Email address is not confirmed. Please check your inbox."
|
||||
common/views/widgets/broadcast.vue:
|
||||
fetching: "Fetching"
|
||||
fetching: "Checking"
|
||||
no-broadcasts: "No announcements"
|
||||
have-a-nice-day: "Have a nice day!"
|
||||
next: "Next"
|
||||
@ -731,6 +736,8 @@ desktop/views/components/settings.vue:
|
||||
note-visibility: "Post visibility"
|
||||
default-note-visibility: "Default visibility"
|
||||
remember-note-visibility: "Remember post visibility"
|
||||
web-search-engine: "Web search engine"
|
||||
web-search-engine-desc: "Example: https://www.google.com/?#q={{query}}"
|
||||
auto-popout: "Auto pop-out window"
|
||||
auto-popout-desc: "If it's possible, pop-out display will be used instead of opening a new window. This setting is stored in your browser."
|
||||
advanced: "Advanced settings"
|
||||
@ -966,17 +973,17 @@ admin/views/instance.vue:
|
||||
recaptcha-site-key: "reCAPTCHA site key"
|
||||
recaptcha-secret-key: "reCAPTCHA secret key"
|
||||
twitter-integration-config: "Settings of connecting to Twitter"
|
||||
twitter-integration-info: "The callback URL is set on /api/tw/cb."
|
||||
twitter-integration-info: "The callback URL is set on {url}."
|
||||
enable-twitter-integration: "Enable connection to Twitter"
|
||||
twitter-integration-consumer-key: "Consumer key"
|
||||
twitter-integration-consumer-secret: "Consumer Secret"
|
||||
github-integration-config: "Setting of connecting to GitHub"
|
||||
github-integration-info: "The callback URL is set on /api/gh/cb."
|
||||
github-integration-info: "The callback URL is set on {url}."
|
||||
enable-github-integration: "Enable connection to GitHub"
|
||||
github-integration-client-id: "Client ID"
|
||||
github-integration-client-secret: "Client Secret"
|
||||
discord-integration-config: "Setting of Discord Integration"
|
||||
discord-integration-info: "The callback URL is set on /api/dc/cb."
|
||||
discord-integration-info: "The callback URL is set on {url}."
|
||||
enable-discord-integration: "Enable Discord connection"
|
||||
discord-integration-client-id: "Client ID"
|
||||
discord-integration-client-secret: "Client Secret"
|
||||
@ -997,6 +1004,15 @@ admin/views/instance.vue:
|
||||
external-user-recommendation-engine-desc: "Example : https://vinayaka.distsn.org/cgi-bin/vinayaka-user-match-misskey-api.cgi?{{host}}+{{user}}+{{limit}}+{{offset}}"
|
||||
external-user-recommendation-timeout: "Timeout"
|
||||
external-user-recommendation-timeout-desc: "Number of milliseconds (ex. 300,000)"
|
||||
email-config: "Email server settings"
|
||||
email-config-info: "Used to confirm email and password reset etc."
|
||||
enable-email: "Enable email delivery"
|
||||
email: "Email Address"
|
||||
smtp-use-ssl: "Use SSL for SMTP server"
|
||||
smtp-host: "SMTP Host"
|
||||
smtp-port: "SMTP Port"
|
||||
smtp-user: "SMTP User"
|
||||
smtp-pass: "SMTP Password"
|
||||
admin/views/charts.vue:
|
||||
title: "Chart"
|
||||
per-day: "per Day"
|
||||
@ -1371,6 +1387,8 @@ mobile/views/pages/settings.vue:
|
||||
note-visibility: "Post visibility"
|
||||
default-note-visibility: "Default visibility"
|
||||
remember-note-visibility: "Remember post visibility"
|
||||
web-search-engine: "Web search engine"
|
||||
web-search-engine-desc: "Example: https://www.google.com/?#q={{query}}"
|
||||
disable-via-mobile: "Don't mark the post as 'from mobile'"
|
||||
load-raw-images: "Show attached images in original quality"
|
||||
load-remote-media: "Show media from a remote server"
|
||||
|
@ -26,6 +26,7 @@ common:
|
||||
close: "Cerrar"
|
||||
do-not-copy-paste: "Por favor no copies código aquí. Tu cuenta puede resultar comprometida."
|
||||
load-more: "もっと読み込む"
|
||||
enter-password: "パスワードを入力してください"
|
||||
got-it: "¡Listo!"
|
||||
customization-tips:
|
||||
title: "Consejos de personalización"
|
||||
@ -457,6 +458,10 @@ common/views/components/profile-editor.vue:
|
||||
saved: "プロフィールを保存しました"
|
||||
uploading: "アップロード中"
|
||||
upload-failed: "アップロードに失敗しました"
|
||||
email: "メール設定"
|
||||
email-address: "メールアドレス"
|
||||
email-verified: "メールアドレスが確認されました"
|
||||
email-not-verified: "メールアドレスが確認されていません。メールボックスをご確認ください。"
|
||||
common/views/widgets/broadcast.vue:
|
||||
fetching: "Recuperando"
|
||||
no-broadcasts: "Sin emisión"
|
||||
@ -731,6 +736,8 @@ desktop/views/components/settings.vue:
|
||||
note-visibility: "投稿の公開範囲"
|
||||
default-note-visibility: "デフォルトの公開範囲"
|
||||
remember-note-visibility: "投稿の公開範囲を記憶する"
|
||||
web-search-engine: "ウェブ検索エンジン"
|
||||
web-search-engine-desc: "例: https://www.google.com/?#q={{query}}"
|
||||
auto-popout: "Ventana emergente automática"
|
||||
auto-popout-desc: "Muestra una ventana emergente si es posible. Esta configuración depende del navegador."
|
||||
advanced: "Configuración avanzada"
|
||||
@ -966,17 +973,17 @@ admin/views/instance.vue:
|
||||
recaptcha-site-key: "reCAPTCHA site key"
|
||||
recaptcha-secret-key: "reCAPTCHA secret key"
|
||||
twitter-integration-config: "Twitter連携の設定"
|
||||
twitter-integration-info: "コールバックURLは /api/tw/cb に設定します。"
|
||||
twitter-integration-info: "コールバックURLは {url} に設定します。"
|
||||
enable-twitter-integration: "Twitter連携を有効にする"
|
||||
twitter-integration-consumer-key: "Consumer key"
|
||||
twitter-integration-consumer-secret: "Consumer secret"
|
||||
github-integration-config: "GitHub連携の設定"
|
||||
github-integration-info: "コールバックURLは /api/gh/cb に設定します。"
|
||||
github-integration-info: "コールバックURLは {url} に設定します。"
|
||||
enable-github-integration: "GitHub連携を有効にする"
|
||||
github-integration-client-id: "Client ID"
|
||||
github-integration-client-secret: "Client Secret"
|
||||
discord-integration-config: "Discord連携の設定"
|
||||
discord-integration-info: "コールバックURLは /api/dc/cb に設定します。"
|
||||
discord-integration-info: "コールバックURLは {url} に設定します。"
|
||||
enable-discord-integration: "Discord連携を有効にする"
|
||||
discord-integration-client-id: "Client ID"
|
||||
discord-integration-client-secret: "Client Secret"
|
||||
@ -997,6 +1004,15 @@ admin/views/instance.vue:
|
||||
external-user-recommendation-engine-desc: "例: https://vinayaka.distsn.org/cgi-bin/vinayaka-user-match-misskey-api.cgi?{{host}}+{{user}}+{{limit}}+{{offset}}"
|
||||
external-user-recommendation-timeout: "タイムアウト"
|
||||
external-user-recommendation-timeout-desc: "ミリ秒単位 (例: 300000)"
|
||||
email-config: "メールサーバーの設定"
|
||||
email-config-info: "メールアドレス確認やパスワードリセットの際に使われます。"
|
||||
enable-email: "メール配信を有効にする"
|
||||
email: "メールアドレス"
|
||||
smtp-use-ssl: "SMTPサーバーはSSLを使用"
|
||||
smtp-host: "SMTPホスト"
|
||||
smtp-port: "SMTPポート"
|
||||
smtp-user: "SMTPユーザー"
|
||||
smtp-pass: "SMTPパスワード"
|
||||
admin/views/charts.vue:
|
||||
title: "チャート"
|
||||
per-day: "1日ごと"
|
||||
@ -1371,6 +1387,8 @@ mobile/views/pages/settings.vue:
|
||||
note-visibility: "投稿の公開範囲"
|
||||
default-note-visibility: "デフォルトの公開範囲"
|
||||
remember-note-visibility: "投稿の公開範囲を記憶する"
|
||||
web-search-engine: "ウェブ検索エンジン"
|
||||
web-search-engine-desc: "例: https://www.google.com/?#q={{query}}"
|
||||
disable-via-mobile: "「モバイルからの投稿」フラグを付けない"
|
||||
load-raw-images: "添付された画像を高画質で表示する"
|
||||
load-remote-media: "リモートサーバーのメディアを表示する"
|
||||
|
@ -18,7 +18,7 @@ common:
|
||||
ui-desc: "どのようなUIが使いやすいかは人それぞれです。だから、Misskeyは自由度の高いUIを持っています。レイアウトやデザインを調整したり、カスタマイズ可能な様々なウィジェットを配置したりして、自分だけのホームを作ってください。"
|
||||
drive: "Drive"
|
||||
drive-desc: "以前投稿したことのある画像をまた投稿したくなったことはありませんか?もしくは、アップロードしたファイルをフォルダ分けして整理したくなったことはありませんか?Misskeyの根幹に組み込まれたドライブ機能によってそれらが解決します。ファイルの共有も簡単です。"
|
||||
outro: "他にもMisskeyにしかない機能はまだまだあるので、ぜひあなた自身の目で確かめてください。Misskeyは分散型SNSなので、このインスタンスが気に入らなければ他のインスタンスを試すこともできます。それでは、GLHF!"
|
||||
outro: "Découvrez vous-même les fonctionnalités de Misskey. Étant donné que Misskey est un réseaux social fédéré, vous pouvez essayer d’autres instances afin de trouver vos ami·e·s si la présente instance ne vous correspond pas. Bonne chance et amusez-vous bien !"
|
||||
adblock:
|
||||
detected: "Veuillez désactiver votre bloqueur de publicités"
|
||||
warning: "<strong>Misskey n’utilise pas de publicités</strong>, mais quelques options peuvent être non disponibles ou fonctionneraient mal si un bloqueur de publicités est activé."
|
||||
@ -26,6 +26,7 @@ common:
|
||||
close: "Fermer"
|
||||
do-not-copy-paste: "Veuillez ne pas entrer ou coller le code ici. Le compte peut être compromis."
|
||||
load-more: "Charger plus"
|
||||
enter-password: "パスワードを入力してください"
|
||||
got-it: "J’ai compris !"
|
||||
customization-tips:
|
||||
title: "Conseils de personnalisation"
|
||||
@ -457,6 +458,10 @@ common/views/components/profile-editor.vue:
|
||||
saved: "Profil mis à jour avec succès"
|
||||
uploading: "En cours d'envoi …"
|
||||
upload-failed: "Échec de l'envoi"
|
||||
email: "メール設定"
|
||||
email-address: "メールアドレス"
|
||||
email-verified: "メールアドレスが確認されました"
|
||||
email-not-verified: "メールアドレスが確認されていません。メールボックスをご確認ください。"
|
||||
common/views/widgets/broadcast.vue:
|
||||
fetching: "Récupération"
|
||||
no-broadcasts: "Aucune annonce"
|
||||
@ -731,6 +736,8 @@ desktop/views/components/settings.vue:
|
||||
note-visibility: "Visibilité de la publication"
|
||||
default-note-visibility: "Visibilité par défaut"
|
||||
remember-note-visibility: "Se souvenir du mode de visibilité de la publication"
|
||||
web-search-engine: "ウェブ検索エンジン"
|
||||
web-search-engine-desc: "例: https://www.google.com/?#q={{query}}"
|
||||
auto-popout: "Fenêtre contextuelle automatique"
|
||||
auto-popout-desc: "ウィンドウが開かれるとき、ポップアウト(ブラウザ外に切り離す)可能なら自動でポップアウトします。この設定はブラウザに記憶されます。"
|
||||
advanced: "Paramètres avancés"
|
||||
@ -966,17 +973,17 @@ admin/views/instance.vue:
|
||||
recaptcha-site-key: "Clé reCAPTCHA du site"
|
||||
recaptcha-secret-key: "Clé secrète reCAPTCHA"
|
||||
twitter-integration-config: "Paramètres de connexion à Twitter"
|
||||
twitter-integration-info: "L’URL callback est définit sur /api/tw/cb"
|
||||
twitter-integration-info: "コールバックURLは {url} に設定します。"
|
||||
enable-twitter-integration: "Activer la connection à Twitter"
|
||||
twitter-integration-consumer-key: "Clé du consommateur"
|
||||
twitter-integration-consumer-secret: "Secret du consommateur"
|
||||
github-integration-config: "Paramètres d’authentification GitHub"
|
||||
github-integration-info: "L’URL callback est définit sur /api/gh/cb"
|
||||
github-integration-info: "コールバックURLは {url} に設定します。"
|
||||
enable-github-integration: "Activer l’authentification avec Github"
|
||||
github-integration-client-id: "ID client"
|
||||
github-integration-client-secret: "Secret client"
|
||||
discord-integration-config: "Paramètres d’authentification Discord"
|
||||
discord-integration-info: "L’URL callback est définit sur /api/dc/cb"
|
||||
discord-integration-info: "コールバックURLは {url} に設定します。"
|
||||
enable-discord-integration: "Activer l’authentification avec Discord"
|
||||
discord-integration-client-id: "ID client"
|
||||
discord-integration-client-secret: "Secret client"
|
||||
@ -992,11 +999,20 @@ admin/views/instance.vue:
|
||||
save: "Sauvegarder"
|
||||
saved: "Enregistré"
|
||||
user-recommendation-config: "Utilisateur·rice·s"
|
||||
enable-external-user-recommendation: "外部ユーザーレコメンデーションを有効にする"
|
||||
enable-external-user-recommendation: "Activer la recommandation des utilisateur·trice·s distant·e·s"
|
||||
external-user-recommendation-engine: "Moteur"
|
||||
external-user-recommendation-engine-desc: "Exemple : https://vinayaka.distsn.org/cgi-bin/vinayaka-user-match-misskey-api.cgi?{{host}}+{{user}}+{{limit}}+{{offset}}"
|
||||
external-user-recommendation-timeout: "Délai d’expiration"
|
||||
external-user-recommendation-timeout-desc: "En millisecondes (par exemple : 300000)"
|
||||
email-config: "メールサーバーの設定"
|
||||
email-config-info: "メールアドレス確認やパスワードリセットの際に使われます。"
|
||||
enable-email: "メール配信を有効にする"
|
||||
email: "メールアドレス"
|
||||
smtp-use-ssl: "SMTPサーバーはSSLを使用"
|
||||
smtp-host: "SMTPホスト"
|
||||
smtp-port: "SMTPポート"
|
||||
smtp-user: "SMTPユーザー"
|
||||
smtp-pass: "SMTPパスワード"
|
||||
admin/views/charts.vue:
|
||||
title: "Graph"
|
||||
per-day: "par jour"
|
||||
@ -1023,12 +1039,12 @@ admin/views/charts.vue:
|
||||
network-time: "Temps de réponse"
|
||||
network-usage: "Traffic"
|
||||
admin/views/users.vue:
|
||||
operation: "操作"
|
||||
username-or-userid: "ユーザー名またはユーザーID"
|
||||
user-not-found: "ユーザーが見つかりません"
|
||||
lookup: "照会"
|
||||
reset-password: "パスワードをリセット"
|
||||
password-updated: "パスワードは現在「{password}」です"
|
||||
operation: "Actions"
|
||||
username-or-userid: "Nom d’utilisateur·rice ou ID utilisateur"
|
||||
user-not-found: "Utilisateur·rice non trouvé·e"
|
||||
lookup: "Recherche"
|
||||
reset-password: "Réinitialiser mot de passe"
|
||||
password-updated: "Le mot de passe est « {password} »"
|
||||
suspend: "Suspendre"
|
||||
suspended: "Suspendu·e avec succès."
|
||||
unsuspend: "Suspension levée"
|
||||
@ -1038,20 +1054,20 @@ admin/views/users.vue:
|
||||
unverify: "Ôter la vérification du compte"
|
||||
unverified: "Ce compte n'est plus vérifié"
|
||||
users:
|
||||
title: "ユーザー"
|
||||
title: "Utilisateur·rice·s"
|
||||
sort:
|
||||
title: "ソート"
|
||||
createdAtAsc: "登録日時が古い順"
|
||||
createdAtDesc: "登録日時が新しい順"
|
||||
updatedAtAsc: "更新日時が古い順"
|
||||
title: "Trier par"
|
||||
createdAtAsc: "Date d’inscription (Ascendant)"
|
||||
createdAtDesc: "Date d’inscription (Descendant)"
|
||||
updatedAtAsc: "Mis à jour récemment (Ascendant)"
|
||||
updatedAtDesc: "更新日時が新しい順"
|
||||
origin:
|
||||
title: "オリジン"
|
||||
combined: "ローカル+リモート"
|
||||
local: "ローカル"
|
||||
remote: "リモート"
|
||||
createdAt: "登録日時"
|
||||
updatedAt: "更新日時"
|
||||
title: "Origine"
|
||||
combined: "Locaux + distants"
|
||||
local: "Locaux"
|
||||
remote: "Distants"
|
||||
createdAt: "Créé le"
|
||||
updatedAt: "Mis à jour le"
|
||||
admin/views/moderators.vue:
|
||||
add-moderator:
|
||||
title: "Ajout d’un modérateur"
|
||||
@ -1371,6 +1387,8 @@ mobile/views/pages/settings.vue:
|
||||
note-visibility: "Visibilité de la publication"
|
||||
default-note-visibility: "Visibilité par défaut"
|
||||
remember-note-visibility: "Se souvenir du mode de visibilité de la publication"
|
||||
web-search-engine: "ウェブ検索エンジン"
|
||||
web-search-engine-desc: "例: https://www.google.com/?#q={{query}}"
|
||||
disable-via-mobile: "Ne pas mentionner que ma publication provient d'un 'périphérique mobile'"
|
||||
load-raw-images: "Afficher les photos jointes en haute qualité"
|
||||
load-remote-media: "Afficher les médias sur le serveur distant"
|
||||
|
@ -5,7 +5,7 @@
|
||||
const fs = require('fs');
|
||||
const yaml = require('js-yaml');
|
||||
|
||||
const langs = ['de-DE', 'en-US', 'fr-FR', 'ja-JP', 'ja-KS', 'pl-PL', 'es-ES', 'nl-NL', 'zh-CN'];
|
||||
const langs = ['de-DE', 'en-US', 'fr-FR', 'ja-JP', 'ja-KS', 'pl-PL', 'es-ES', 'nl-NL', 'zh-CN', 'ko-KR'];
|
||||
|
||||
const loadLocale = lang => yaml.safeLoad(fs.readFileSync(`${__dirname}/${lang}.yml`, 'utf-8'));
|
||||
const locales = langs.map(lang => ({ [lang]: loadLocale(lang) }));
|
||||
|
@ -26,6 +26,7 @@ common:
|
||||
close: "閉じる"
|
||||
do-not-copy-paste: "ここにコードを入力したり張り付けたりしないでください。アカウントが不正利用される可能性があります。"
|
||||
load-more: "もっと読み込む"
|
||||
enter-password: "パスワードを入力してください"
|
||||
got-it: "わかった"
|
||||
customization-tips:
|
||||
title: "カスタマイズのヒント"
|
||||
@ -457,6 +458,10 @@ common/views/components/profile-editor.vue:
|
||||
saved: "プロフィールを保存しました"
|
||||
uploading: "アップロード中"
|
||||
upload-failed: "アップロードに失敗しました"
|
||||
email: "メール設定"
|
||||
email-address: "メールアドレス"
|
||||
email-verified: "メールアドレスが確認されました"
|
||||
email-not-verified: "メールアドレスが確認されていません。メールボックスをご確認ください。"
|
||||
common/views/widgets/broadcast.vue:
|
||||
fetching: "確認中"
|
||||
no-broadcasts: "お知らせはありません"
|
||||
@ -731,6 +736,8 @@ desktop/views/components/settings.vue:
|
||||
note-visibility: "投稿の公開範囲"
|
||||
default-note-visibility: "デフォルトの公開範囲"
|
||||
remember-note-visibility: "投稿の公開範囲を記憶する"
|
||||
web-search-engine: "ウェブ検索エンジン"
|
||||
web-search-engine-desc: "例: https://www.google.com/?#q={{query}}"
|
||||
auto-popout: "ウィンドウの自動ポップアウト"
|
||||
auto-popout-desc: "ウィンドウが開かれるとき、ポップアウト(ブラウザ外に切り離す)可能なら自動でポップアウトします。この設定はブラウザに記憶されます。"
|
||||
advanced: "詳細設定"
|
||||
@ -966,17 +973,17 @@ admin/views/instance.vue:
|
||||
recaptcha-site-key: "reCAPTCHA site key"
|
||||
recaptcha-secret-key: "reCAPTCHA secret key"
|
||||
twitter-integration-config: "Twitter連携の設定"
|
||||
twitter-integration-info: "コールバックURLは /api/tw/cb に設定します。"
|
||||
twitter-integration-info: "コールバックURLは {url} に設定します。"
|
||||
enable-twitter-integration: "Twitter連携を有効にする"
|
||||
twitter-integration-consumer-key: "Consumer key"
|
||||
twitter-integration-consumer-secret: "Consumer secret"
|
||||
github-integration-config: "GitHub連携の設定"
|
||||
github-integration-info: "コールバックURLは /api/gh/cb に設定します。"
|
||||
github-integration-info: "コールバックURLは {url} に設定します。"
|
||||
enable-github-integration: "GitHub連携を有効にする"
|
||||
github-integration-client-id: "Client ID"
|
||||
github-integration-client-secret: "Client Secret"
|
||||
discord-integration-config: "Discord連携の設定"
|
||||
discord-integration-info: "コールバックURLは /api/dc/cb に設定します。"
|
||||
discord-integration-info: "コールバックURLは {url} に設定します。"
|
||||
enable-discord-integration: "Discord連携を有効にする"
|
||||
discord-integration-client-id: "Client ID"
|
||||
discord-integration-client-secret: "Client Secret"
|
||||
@ -997,6 +1004,15 @@ admin/views/instance.vue:
|
||||
external-user-recommendation-engine-desc: "例: https://vinayaka.distsn.org/cgi-bin/vinayaka-user-match-misskey-api.cgi?{{host}}+{{user}}+{{limit}}+{{offset}}"
|
||||
external-user-recommendation-timeout: "タイムアウト"
|
||||
external-user-recommendation-timeout-desc: "ミリ秒単位 (例: 300000)"
|
||||
email-config: "メールサーバーの設定"
|
||||
email-config-info: "メールアドレス確認やパスワードリセットの際に使われます。"
|
||||
enable-email: "メール配信を有効にする"
|
||||
email: "メールアドレス"
|
||||
smtp-use-ssl: "SMTPサーバーはSSLを使用"
|
||||
smtp-host: "SMTPホスト"
|
||||
smtp-port: "SMTPポート"
|
||||
smtp-user: "SMTPユーザー"
|
||||
smtp-pass: "SMTPパスワード"
|
||||
admin/views/charts.vue:
|
||||
title: "チャート"
|
||||
per-day: "1日ごと"
|
||||
@ -1371,6 +1387,8 @@ mobile/views/pages/settings.vue:
|
||||
note-visibility: "投稿の公開範囲"
|
||||
default-note-visibility: "デフォルトの公開範囲"
|
||||
remember-note-visibility: "投稿の公開範囲を記憶する"
|
||||
web-search-engine: "ウェブ検索エンジン"
|
||||
web-search-engine-desc: "例: https://www.google.com/?#q={{query}}"
|
||||
disable-via-mobile: "「モバイルからの投稿」フラグを付けない"
|
||||
load-raw-images: "添付された画像を高画質で表示する"
|
||||
load-remote-media: "リモートサーバーのメディアを表示する"
|
||||
|
@ -26,6 +26,7 @@ common:
|
||||
close: "閉じる"
|
||||
do-not-copy-paste: "ここにコードを入力したり張り付けたりしないでください。アカウントが不正利用される可能性があります。"
|
||||
load-more: "もっと読み込む"
|
||||
enter-password: "パスワードを入力してください"
|
||||
|
||||
got-it: "わかった"
|
||||
customization-tips:
|
||||
@ -503,6 +504,10 @@ common/views/components/profile-editor.vue:
|
||||
saved: "プロフィールを保存しました"
|
||||
uploading: "アップロード中"
|
||||
upload-failed: "アップロードに失敗しました"
|
||||
email: "メール設定"
|
||||
email-address: "メールアドレス"
|
||||
email-verified: "メールアドレスが確認されました"
|
||||
email-not-verified: "メールアドレスが確認されていません。メールボックスをご確認ください。"
|
||||
|
||||
common/views/widgets/broadcast.vue:
|
||||
fetching: "確認中"
|
||||
@ -824,6 +829,8 @@ desktop/views/components/settings.vue:
|
||||
note-visibility: "投稿の公開範囲"
|
||||
default-note-visibility: "デフォルトの公開範囲"
|
||||
remember-note-visibility: "投稿の公開範囲を記憶する"
|
||||
web-search-engine: "ウェブ検索エンジン"
|
||||
web-search-engine-desc: "例: https://www.google.com/?#q={{query}}"
|
||||
auto-popout: "ウィンドウの自動ポップアウト"
|
||||
auto-popout-desc: "ウィンドウが開かれるとき、ポップアウト(ブラウザ外に切り離す)可能なら自動でポップアウトします。この設定はブラウザに記憶されます。"
|
||||
advanced: "詳細設定"
|
||||
@ -1092,17 +1099,17 @@ admin/views/instance.vue:
|
||||
recaptcha-site-key: "reCAPTCHA site key"
|
||||
recaptcha-secret-key: "reCAPTCHA secret key"
|
||||
twitter-integration-config: "Twitter連携の設定"
|
||||
twitter-integration-info: "コールバックURLは /api/tw/cb に設定します。"
|
||||
twitter-integration-info: "コールバックURLは {url} に設定します。"
|
||||
enable-twitter-integration: "Twitter連携を有効にする"
|
||||
twitter-integration-consumer-key: "Consumer key"
|
||||
twitter-integration-consumer-secret: "Consumer secret"
|
||||
github-integration-config: "GitHub連携の設定"
|
||||
github-integration-info: "コールバックURLは /api/gh/cb に設定します。"
|
||||
github-integration-info: "コールバックURLは {url} に設定します。"
|
||||
enable-github-integration: "GitHub連携を有効にする"
|
||||
github-integration-client-id: "Client ID"
|
||||
github-integration-client-secret: "Client Secret"
|
||||
discord-integration-config: "Discord連携の設定"
|
||||
discord-integration-info: "コールバックURLは /api/dc/cb に設定します。"
|
||||
discord-integration-info: "コールバックURLは {url} に設定します。"
|
||||
enable-discord-integration: "Discord連携を有効にする"
|
||||
discord-integration-client-id: "Client ID"
|
||||
discord-integration-client-secret: "Client Secret"
|
||||
@ -1123,6 +1130,16 @@ admin/views/instance.vue:
|
||||
external-user-recommendation-engine-desc: "例: https://vinayaka.distsn.org/cgi-bin/vinayaka-user-match-misskey-api.cgi?{{host}}+{{user}}+{{limit}}+{{offset}}"
|
||||
external-user-recommendation-timeout: "タイムアウト"
|
||||
external-user-recommendation-timeout-desc: "ミリ秒単位 (例: 300000)"
|
||||
email-config: "メールサーバーの設定"
|
||||
email-config-info: "メールアドレス確認やパスワードリセットの際に使われます。"
|
||||
enable-email: "メール配信を有効にする"
|
||||
email: "メールアドレス"
|
||||
smtp-secure: "SMTP接続に暗黙的なSSL/TLSを使用する"
|
||||
smtp-secure-info: "STARTTLS使用時はオフにします。"
|
||||
smtp-host: "SMTPホスト"
|
||||
smtp-port: "SMTPポート"
|
||||
smtp-user: "SMTPユーザー"
|
||||
smtp-pass: "SMTPパスワード"
|
||||
|
||||
admin/views/charts.vue:
|
||||
title: "チャート"
|
||||
@ -1564,6 +1581,8 @@ mobile/views/pages/settings.vue:
|
||||
note-visibility: "投稿の公開範囲"
|
||||
default-note-visibility: "デフォルトの公開範囲"
|
||||
remember-note-visibility: "投稿の公開範囲を記憶する"
|
||||
web-search-engine: "ウェブ検索エンジン"
|
||||
web-search-engine-desc: "例: https://www.google.com/?#q={{query}}"
|
||||
disable-via-mobile: "「モバイルからの投稿」フラグを付けない"
|
||||
load-raw-images: "添付された画像を高画質で表示する"
|
||||
load-remote-media: "リモートサーバーのメディアを表示する"
|
||||
|
@ -26,6 +26,7 @@ common:
|
||||
close: "さいなら"
|
||||
do-not-copy-paste: "ここにコードを入力したり張り付けたりせんといてください。アカウントが不正利用されるかも分からん。知らんけど。"
|
||||
load-more: "もっと読み込む"
|
||||
enter-password: "パスワードを入力してください"
|
||||
got-it: "ほい"
|
||||
customization-tips:
|
||||
title: "カスタマイズのヒント"
|
||||
@ -457,6 +458,10 @@ common/views/components/profile-editor.vue:
|
||||
saved: "プロフィールを保存したで"
|
||||
uploading: "アップロードしとります"
|
||||
upload-failed: "これアップロードでけへんわ"
|
||||
email: "メール設定"
|
||||
email-address: "メールアドレス"
|
||||
email-verified: "メールアドレスが確認されました"
|
||||
email-not-verified: "メールアドレスが確認されていません。メールボックスをご確認ください。"
|
||||
common/views/widgets/broadcast.vue:
|
||||
fetching: "見てみるわ…"
|
||||
no-broadcasts: "お知らせはあらへんで"
|
||||
@ -731,6 +736,8 @@ desktop/views/components/settings.vue:
|
||||
note-visibility: "投稿の公開範囲"
|
||||
default-note-visibility: "もとからの公開範囲"
|
||||
remember-note-visibility: "投稿の公開範囲おぼえといて"
|
||||
web-search-engine: "ウェブ検索エンジン"
|
||||
web-search-engine-desc: "例: https://www.google.com/?#q={{query}}"
|
||||
auto-popout: "ウィンドウの自動ポップアウト"
|
||||
auto-popout-desc: "ウィンドウが開かれるとき、ポップアウト(ブラウザ外に切り離す)可能なら自動でポップアウトすんで。この設定はブラウザに記憶されんで。"
|
||||
advanced: "もっと設定"
|
||||
@ -966,17 +973,17 @@ admin/views/instance.vue:
|
||||
recaptcha-site-key: "reCAPTCHA site key"
|
||||
recaptcha-secret-key: "reCAPTCHA secret key"
|
||||
twitter-integration-config: "Twitter連携の設定"
|
||||
twitter-integration-info: "コールバックURLは /api/tw/cb に設定します。"
|
||||
twitter-integration-info: "コールバックURLは {url} に設定します。"
|
||||
enable-twitter-integration: "Twitter連携を有効にする"
|
||||
twitter-integration-consumer-key: "Consumer key"
|
||||
twitter-integration-consumer-secret: "Consumer secret"
|
||||
github-integration-config: "GitHub連携の設定"
|
||||
github-integration-info: "コールバックURLは /api/gh/cb に設定します。"
|
||||
github-integration-info: "コールバックURLは {url} に設定します。"
|
||||
enable-github-integration: "GitHub連携を有効にする"
|
||||
github-integration-client-id: "Client ID"
|
||||
github-integration-client-secret: "Client Secret"
|
||||
discord-integration-config: "Discord連携の設定"
|
||||
discord-integration-info: "コールバックURLは /api/dc/cb に設定します。"
|
||||
discord-integration-info: "コールバックURLは {url} に設定します。"
|
||||
enable-discord-integration: "Discord連携を有効にする"
|
||||
discord-integration-client-id: "Client ID"
|
||||
discord-integration-client-secret: "Client Secret"
|
||||
@ -997,6 +1004,15 @@ admin/views/instance.vue:
|
||||
external-user-recommendation-engine-desc: "例: https://vinayaka.distsn.org/cgi-bin/vinayaka-user-match-misskey-api.cgi?{{host}}+{{user}}+{{limit}}+{{offset}}"
|
||||
external-user-recommendation-timeout: "タイムアウト"
|
||||
external-user-recommendation-timeout-desc: "ミリ秒単位 (例: 300000)"
|
||||
email-config: "メールサーバーの設定"
|
||||
email-config-info: "メールアドレス確認やパスワードリセットの際に使われます。"
|
||||
enable-email: "メール配信を有効にする"
|
||||
email: "メールアドレス"
|
||||
smtp-use-ssl: "SMTPサーバーはSSLを使用"
|
||||
smtp-host: "SMTPホスト"
|
||||
smtp-port: "SMTPポート"
|
||||
smtp-user: "SMTPユーザー"
|
||||
smtp-pass: "SMTPパスワード"
|
||||
admin/views/charts.vue:
|
||||
title: "チャート"
|
||||
per-day: "1日ごと"
|
||||
@ -1371,6 +1387,8 @@ mobile/views/pages/settings.vue:
|
||||
note-visibility: "投稿の公開範囲"
|
||||
default-note-visibility: "もとからの公開範囲"
|
||||
remember-note-visibility: "投稿の公開範囲おぼえといて"
|
||||
web-search-engine: "ウェブ検索エンジン"
|
||||
web-search-engine-desc: "例: https://www.google.com/?#q={{query}}"
|
||||
disable-via-mobile: "「モバイルからの投稿」フラグなんて要らんわ"
|
||||
load-raw-images: "添付された画像もべっぴんさんのままにしといてな"
|
||||
load-remote-media: "東京とか、リモートサーバーのメディアも見せてや"
|
||||
|
2330
locales/ko-KR.yml
2330
locales/ko-KR.yml
File diff suppressed because it is too large
Load Diff
@ -26,6 +26,7 @@ common:
|
||||
close: "閉じる"
|
||||
do-not-copy-paste: "ここにコードを入力したり張り付けたりしないでください。アカウントが不正利用される可能性があります。"
|
||||
load-more: "もっと読み込む"
|
||||
enter-password: "パスワードを入力してください"
|
||||
got-it: "わかった"
|
||||
customization-tips:
|
||||
title: "カスタマイズのヒント"
|
||||
@ -457,6 +458,10 @@ common/views/components/profile-editor.vue:
|
||||
saved: "プロフィールを保存しました"
|
||||
uploading: "アップロード中"
|
||||
upload-failed: "アップロードに失敗しました"
|
||||
email: "メール設定"
|
||||
email-address: "メールアドレス"
|
||||
email-verified: "メールアドレスが確認されました"
|
||||
email-not-verified: "メールアドレスが確認されていません。メールボックスをご確認ください。"
|
||||
common/views/widgets/broadcast.vue:
|
||||
fetching: "Bezig met ophalen"
|
||||
no-broadcasts: "Geen uitzendingen"
|
||||
@ -731,6 +736,8 @@ desktop/views/components/settings.vue:
|
||||
note-visibility: "投稿の公開範囲"
|
||||
default-note-visibility: "デフォルトの公開範囲"
|
||||
remember-note-visibility: "投稿の公開範囲を記憶する"
|
||||
web-search-engine: "ウェブ検索エンジン"
|
||||
web-search-engine-desc: "例: https://www.google.com/?#q={{query}}"
|
||||
auto-popout: "Venster automatisch uitvouwen"
|
||||
auto-popout-desc: "Venster uitvouwen, indien mogelijk. Deze instelling wordt opgeslagen in je browser."
|
||||
advanced: "Geavanceerde instellingen"
|
||||
@ -966,17 +973,17 @@ admin/views/instance.vue:
|
||||
recaptcha-site-key: "reCAPTCHA site key"
|
||||
recaptcha-secret-key: "reCAPTCHA secret key"
|
||||
twitter-integration-config: "Twitter連携の設定"
|
||||
twitter-integration-info: "コールバックURLは /api/tw/cb に設定します。"
|
||||
twitter-integration-info: "コールバックURLは {url} に設定します。"
|
||||
enable-twitter-integration: "Twitter連携を有効にする"
|
||||
twitter-integration-consumer-key: "Consumer key"
|
||||
twitter-integration-consumer-secret: "Consumer secret"
|
||||
github-integration-config: "GitHub連携の設定"
|
||||
github-integration-info: "コールバックURLは /api/gh/cb に設定します。"
|
||||
github-integration-info: "コールバックURLは {url} に設定します。"
|
||||
enable-github-integration: "GitHub連携を有効にする"
|
||||
github-integration-client-id: "Client ID"
|
||||
github-integration-client-secret: "Client Secret"
|
||||
discord-integration-config: "Discord連携の設定"
|
||||
discord-integration-info: "コールバックURLは /api/dc/cb に設定します。"
|
||||
discord-integration-info: "コールバックURLは {url} に設定します。"
|
||||
enable-discord-integration: "Discord連携を有効にする"
|
||||
discord-integration-client-id: "Client ID"
|
||||
discord-integration-client-secret: "Client Secret"
|
||||
@ -997,6 +1004,15 @@ admin/views/instance.vue:
|
||||
external-user-recommendation-engine-desc: "例: https://vinayaka.distsn.org/cgi-bin/vinayaka-user-match-misskey-api.cgi?{{host}}+{{user}}+{{limit}}+{{offset}}"
|
||||
external-user-recommendation-timeout: "タイムアウト"
|
||||
external-user-recommendation-timeout-desc: "ミリ秒単位 (例: 300000)"
|
||||
email-config: "メールサーバーの設定"
|
||||
email-config-info: "メールアドレス確認やパスワードリセットの際に使われます。"
|
||||
enable-email: "メール配信を有効にする"
|
||||
email: "メールアドレス"
|
||||
smtp-use-ssl: "SMTPサーバーはSSLを使用"
|
||||
smtp-host: "SMTPホスト"
|
||||
smtp-port: "SMTPポート"
|
||||
smtp-user: "SMTPユーザー"
|
||||
smtp-pass: "SMTPパスワード"
|
||||
admin/views/charts.vue:
|
||||
title: "チャート"
|
||||
per-day: "1日ごと"
|
||||
@ -1371,6 +1387,8 @@ mobile/views/pages/settings.vue:
|
||||
note-visibility: "投稿の公開範囲"
|
||||
default-note-visibility: "デフォルトの公開範囲"
|
||||
remember-note-visibility: "投稿の公開範囲を記憶する"
|
||||
web-search-engine: "ウェブ検索エンジン"
|
||||
web-search-engine-desc: "例: https://www.google.com/?#q={{query}}"
|
||||
disable-via-mobile: "Zonder 'mobiele berichten'"
|
||||
load-raw-images: "添付された画像を高画質で表示する"
|
||||
load-remote-media: "リモートサーバーのメディアを表示する"
|
||||
|
@ -26,6 +26,7 @@ common:
|
||||
close: "Lukk"
|
||||
do-not-copy-paste: "ここにコードを入力したり張り付けたりしないでください。アカウントが不正利用される可能性があります。"
|
||||
load-more: "もっと読み込む"
|
||||
enter-password: "パスワードを入力してください"
|
||||
got-it: "Skjønner!"
|
||||
customization-tips:
|
||||
title: "カスタマイズのヒント"
|
||||
@ -457,6 +458,10 @@ common/views/components/profile-editor.vue:
|
||||
saved: "プロフィールを保存しました"
|
||||
uploading: "アップロード中"
|
||||
upload-failed: "アップロードに失敗しました"
|
||||
email: "メール設定"
|
||||
email-address: "メールアドレス"
|
||||
email-verified: "メールアドレスが確認されました"
|
||||
email-not-verified: "メールアドレスが確認されていません。メールボックスをご確認ください。"
|
||||
common/views/widgets/broadcast.vue:
|
||||
fetching: "Henter"
|
||||
no-broadcasts: "お知らせはありません"
|
||||
@ -731,6 +736,8 @@ desktop/views/components/settings.vue:
|
||||
note-visibility: "投稿の公開範囲"
|
||||
default-note-visibility: "デフォルトの公開範囲"
|
||||
remember-note-visibility: "投稿の公開範囲を記憶する"
|
||||
web-search-engine: "ウェブ検索エンジン"
|
||||
web-search-engine-desc: "例: https://www.google.com/?#q={{query}}"
|
||||
auto-popout: "ウィンドウの自動ポップアウト"
|
||||
auto-popout-desc: "ウィンドウが開かれるとき、ポップアウト(ブラウザ外に切り離す)可能なら自動でポップアウトします。この設定はブラウザに記憶されます。"
|
||||
advanced: "Avanserte innstillinger"
|
||||
@ -966,17 +973,17 @@ admin/views/instance.vue:
|
||||
recaptcha-site-key: "reCAPTCHA site key"
|
||||
recaptcha-secret-key: "reCAPTCHA secret key"
|
||||
twitter-integration-config: "Twitter連携の設定"
|
||||
twitter-integration-info: "コールバックURLは /api/tw/cb に設定します。"
|
||||
twitter-integration-info: "コールバックURLは {url} に設定します。"
|
||||
enable-twitter-integration: "Twitter連携を有効にする"
|
||||
twitter-integration-consumer-key: "Consumer key"
|
||||
twitter-integration-consumer-secret: "Consumer secret"
|
||||
github-integration-config: "GitHub連携の設定"
|
||||
github-integration-info: "コールバックURLは /api/gh/cb に設定します。"
|
||||
github-integration-info: "コールバックURLは {url} に設定します。"
|
||||
enable-github-integration: "GitHub連携を有効にする"
|
||||
github-integration-client-id: "Client ID"
|
||||
github-integration-client-secret: "Client Secret"
|
||||
discord-integration-config: "Discord連携の設定"
|
||||
discord-integration-info: "コールバックURLは /api/dc/cb に設定します。"
|
||||
discord-integration-info: "コールバックURLは {url} に設定します。"
|
||||
enable-discord-integration: "Discord連携を有効にする"
|
||||
discord-integration-client-id: "Client ID"
|
||||
discord-integration-client-secret: "Client Secret"
|
||||
@ -997,6 +1004,15 @@ admin/views/instance.vue:
|
||||
external-user-recommendation-engine-desc: "例: https://vinayaka.distsn.org/cgi-bin/vinayaka-user-match-misskey-api.cgi?{{host}}+{{user}}+{{limit}}+{{offset}}"
|
||||
external-user-recommendation-timeout: "タイムアウト"
|
||||
external-user-recommendation-timeout-desc: "ミリ秒単位 (例: 300000)"
|
||||
email-config: "メールサーバーの設定"
|
||||
email-config-info: "メールアドレス確認やパスワードリセットの際に使われます。"
|
||||
enable-email: "メール配信を有効にする"
|
||||
email: "メールアドレス"
|
||||
smtp-use-ssl: "SMTPサーバーはSSLを使用"
|
||||
smtp-host: "SMTPホスト"
|
||||
smtp-port: "SMTPポート"
|
||||
smtp-user: "SMTPユーザー"
|
||||
smtp-pass: "SMTPパスワード"
|
||||
admin/views/charts.vue:
|
||||
title: "チャート"
|
||||
per-day: "1日ごと"
|
||||
@ -1371,6 +1387,8 @@ mobile/views/pages/settings.vue:
|
||||
note-visibility: "投稿の公開範囲"
|
||||
default-note-visibility: "デフォルトの公開範囲"
|
||||
remember-note-visibility: "投稿の公開範囲を記憶する"
|
||||
web-search-engine: "ウェブ検索エンジン"
|
||||
web-search-engine-desc: "例: https://www.google.com/?#q={{query}}"
|
||||
disable-via-mobile: "「モバイルからの投稿」フラグを付けない"
|
||||
load-raw-images: "添付された画像を高画質で表示する"
|
||||
load-remote-media: "リモートサーバーのメディアを表示する"
|
||||
|
@ -7,14 +7,14 @@ common:
|
||||
about-title: "⭐ Fediwersum"
|
||||
about: "Dziękujemy za znalezienie Misskey. Misskey jest <b>zdecentralizowaną platformą mikroblogową</b> powstałą na Ziemi. Ponieważ działa ona w Fediwersum (uniwersum, w którego skład wchodzi wiele sieci społecznościowych), jest ona połączona z innymi platformami społecznościowymi. Spróbujesz odpocząć od zatłoczoneo miasta i zanurzyć się w nowym Internecie?"
|
||||
intro:
|
||||
title: "Misskeyって?"
|
||||
title: "Czym jest Misskey?"
|
||||
about: "Misskeyはオープンソースの<b>分散型マイクロブログSNS</b>です。リッチで高度にカスタマイズできるUI、投稿へのリアクション、ファイルを一元管理できるドライブなど、先進的な機能を揃えています。また、Fediverseと呼ばれるネットワークに接続できるため、他のSNSともやり取りできます。例えば、あなたが何か投稿すると、その投稿はMisskeyだけでなく他のSNSにも伝わります。ちょうどある惑星から他の惑星に電波を発信している様子をイメージしてください。"
|
||||
features: "特徴"
|
||||
rich-contents: "投稿"
|
||||
rich-contents-desc: "自分の考え、話題の出来事、皆と共有したいことについて発信してください。必要であれば、様々な構文を使って投稿を装飾したり、好きな画像、動画などのファイルやアンケートを添付することもできます。"
|
||||
reaction: "リアクション"
|
||||
reaction: "Reakcje"
|
||||
reaction-desc: "あなたの気持ちを伝える最も簡単な方法です。Misskeyは、他のユーザーの投稿に様々なリアクションを付けることができます。いちどMisskeyのリアクション機能を体験してしまうと、もう「いいね」の概念しか存在しないSNSには戻れなくなるかもしれません。"
|
||||
ui: "インターフェース"
|
||||
ui: "Interfejs"
|
||||
ui-desc: "どのようなUIが使いやすいかは人それぞれです。だから、Misskeyは自由度の高いUIを持っています。レイアウトやデザインを調整したり、カスタマイズ可能な様々なウィジェットを配置したりして、自分だけのホームを作ってください。"
|
||||
drive: "ドライブ"
|
||||
drive-desc: "以前投稿したことのある画像をまた投稿したくなったことはありませんか?もしくは、アップロードしたファイルをフォルダ分けして整理したくなったことはありませんか?Misskeyの根幹に組み込まれたドライブ機能によってそれらが解決します。ファイルの共有も簡単です。"
|
||||
@ -26,6 +26,7 @@ common:
|
||||
close: "Zamknij"
|
||||
do-not-copy-paste: "ここにコードを入力したり張り付けたりしないでください。アカウントが不正利用される可能性があります。"
|
||||
load-more: "もっと読み込む"
|
||||
enter-password: "パスワードを入力してください"
|
||||
got-it: "Rozumiem!"
|
||||
customization-tips:
|
||||
title: "Wskazówki o dostosowywaniu"
|
||||
@ -192,8 +193,8 @@ common/views/components/games/reversi/reversi.vue:
|
||||
waiting-for: "Oczekiwanie na {}"
|
||||
cancel: "Anuluj"
|
||||
common/views/components/games/reversi/reversi.game.vue:
|
||||
surrender: "投了"
|
||||
surrendered: "投了により"
|
||||
surrender: "Poddaj się"
|
||||
surrendered: "Przez poddanie się"
|
||||
is-llotheo: "石の少ない方が勝ち(ロセオ)"
|
||||
looped-map: "ループマップ"
|
||||
can-put-everywhere: "どこでも置けるモード"
|
||||
@ -293,8 +294,8 @@ common/views/components/theme.vue:
|
||||
import-by-code: "またはコードをペースト"
|
||||
theme-name-required: "テーマ名は必須です。"
|
||||
common/views/components/cw-button.vue:
|
||||
hide: "隠す"
|
||||
show: "もっと見る"
|
||||
hide: "Ukryj"
|
||||
show: "Pokaż więcej"
|
||||
common/views/components/messaging.vue:
|
||||
search-user: "Znajdź użytkownika"
|
||||
you: "Ty"
|
||||
@ -350,7 +351,7 @@ common/views/components/reaction-picker.vue:
|
||||
choose-reaction: "Wybierz reakcję"
|
||||
common/views/components/emoji-picker.vue:
|
||||
custom-emoji: "カスタム絵文字"
|
||||
people: "人"
|
||||
people: "Ludzie"
|
||||
animals-and-nature: "動物&自然"
|
||||
food-and-drink: "食べ物&飲み物"
|
||||
activity: "アクティビティ"
|
||||
@ -366,8 +367,8 @@ common/views/components/signin.vue:
|
||||
signin: "Zaloguj"
|
||||
or: "または"
|
||||
signin-with-twitter: "Zaloguj się za pomocą Twittera"
|
||||
signin-with-github: "GitHubでログイン"
|
||||
signin-with-discord: "Discordでログイン"
|
||||
signin-with-github: "Zaloguj się za pomocą GitHuba"
|
||||
signin-with-discord: "Zaloguj się za pomocą Discorda"
|
||||
login-failed: "ログインできませんでした。ユーザー名とパスワードを確認してください。"
|
||||
common/views/components/signup.vue:
|
||||
invitation-code: "招待コード"
|
||||
@ -457,6 +458,10 @@ common/views/components/profile-editor.vue:
|
||||
saved: "プロフィールを保存しました"
|
||||
uploading: "アップロード中"
|
||||
upload-failed: "アップロードに失敗しました"
|
||||
email: "メール設定"
|
||||
email-address: "メールアドレス"
|
||||
email-verified: "メールアドレスが確認されました"
|
||||
email-not-verified: "メールアドレスが確認されていません。メールボックスをご確認ください。"
|
||||
common/views/widgets/broadcast.vue:
|
||||
fetching: "Sprawdzanie"
|
||||
no-broadcasts: "Brak transmisji"
|
||||
@ -731,6 +736,8 @@ desktop/views/components/settings.vue:
|
||||
note-visibility: "投稿の公開範囲"
|
||||
default-note-visibility: "デフォルトの公開範囲"
|
||||
remember-note-visibility: "投稿の公開範囲を記憶する"
|
||||
web-search-engine: "ウェブ検索エンジン"
|
||||
web-search-engine-desc: "例: https://www.google.com/?#q={{query}}"
|
||||
auto-popout: "Automatycznie pojawiające się okna"
|
||||
auto-popout-desc: "ウィンドウが開かれるとき、ポップアウト(ブラウザ外に切り離す)可能なら自動でポップアウトします。この設定はブラウザに記憶されます。"
|
||||
advanced: "Ustawienia zaawansowane"
|
||||
@ -932,7 +939,7 @@ admin/views/index.vue:
|
||||
moderators: "モデレーター"
|
||||
users: "ユーザー"
|
||||
federation: "連合"
|
||||
announcements: "お知らせ"
|
||||
announcements: "Ogłoszenia"
|
||||
hashtags: "ハッシュタグ"
|
||||
back-to-misskey: "Misskeyに戻る"
|
||||
admin/views/dashboard.vue:
|
||||
@ -966,17 +973,17 @@ admin/views/instance.vue:
|
||||
recaptcha-site-key: "reCAPTCHA site key"
|
||||
recaptcha-secret-key: "reCAPTCHA secret key"
|
||||
twitter-integration-config: "Twitter連携の設定"
|
||||
twitter-integration-info: "コールバックURLは /api/tw/cb に設定します。"
|
||||
twitter-integration-info: "コールバックURLは {url} に設定します。"
|
||||
enable-twitter-integration: "Twitter連携を有効にする"
|
||||
twitter-integration-consumer-key: "Consumer key"
|
||||
twitter-integration-consumer-secret: "Consumer secret"
|
||||
github-integration-config: "GitHub連携の設定"
|
||||
github-integration-info: "コールバックURLは /api/gh/cb に設定します。"
|
||||
github-integration-info: "コールバックURLは {url} に設定します。"
|
||||
enable-github-integration: "GitHub連携を有効にする"
|
||||
github-integration-client-id: "Client ID"
|
||||
github-integration-client-secret: "Client Secret"
|
||||
discord-integration-config: "Discord連携の設定"
|
||||
discord-integration-info: "コールバックURLは /api/dc/cb に設定します。"
|
||||
discord-integration-info: "コールバックURLは {url} に設定します。"
|
||||
enable-discord-integration: "Discord連携を有効にする"
|
||||
discord-integration-client-id: "Client ID"
|
||||
discord-integration-client-secret: "Client Secret"
|
||||
@ -997,6 +1004,15 @@ admin/views/instance.vue:
|
||||
external-user-recommendation-engine-desc: "例: https://vinayaka.distsn.org/cgi-bin/vinayaka-user-match-misskey-api.cgi?{{host}}+{{user}}+{{limit}}+{{offset}}"
|
||||
external-user-recommendation-timeout: "タイムアウト"
|
||||
external-user-recommendation-timeout-desc: "ミリ秒単位 (例: 300000)"
|
||||
email-config: "メールサーバーの設定"
|
||||
email-config-info: "メールアドレス確認やパスワードリセットの際に使われます。"
|
||||
enable-email: "メール配信を有効にする"
|
||||
email: "メールアドレス"
|
||||
smtp-use-ssl: "SMTPサーバーはSSLを使用"
|
||||
smtp-host: "SMTPホスト"
|
||||
smtp-port: "SMTPポート"
|
||||
smtp-user: "SMTPユーザー"
|
||||
smtp-pass: "SMTPパスワード"
|
||||
admin/views/charts.vue:
|
||||
title: "チャート"
|
||||
per-day: "1日ごと"
|
||||
@ -1077,7 +1093,7 @@ admin/views/emoji.vue:
|
||||
are-you-sure: "「$1」を削除しますか?"
|
||||
removed: "削除しました"
|
||||
admin/views/announcements.vue:
|
||||
announcements: "お知らせ"
|
||||
announcements: "Ogłoszenia"
|
||||
save: "保存"
|
||||
remove: "削除"
|
||||
add: "追加"
|
||||
@ -1097,10 +1113,10 @@ desktop/views/pages/welcome.vue:
|
||||
signin-button: "Zaloguj się"
|
||||
signup-button: "Zarejestruj się"
|
||||
timeline: "Oś czasu"
|
||||
announcements: "お知らせ"
|
||||
photos: "最近の画像"
|
||||
announcements: "Ogłoszenia"
|
||||
photos: "Ostatnie obrazy"
|
||||
powered-by-misskey: "Oparto o <b>Misskey</b>."
|
||||
info: "情報"
|
||||
info: "Informacje"
|
||||
desktop/views/pages/drive.vue:
|
||||
title: "Dysk Misskey"
|
||||
desktop/views/pages/home-customize.vue:
|
||||
@ -1154,7 +1170,7 @@ desktop/views/pages/user/user.header.vue:
|
||||
following: "Śledzeni"
|
||||
followers: "Śledzący"
|
||||
is-bot: "To konto jest botem"
|
||||
years-old: "{age}歳"
|
||||
years-old: "{age} lat"
|
||||
year: "年"
|
||||
month: "月"
|
||||
day: "日"
|
||||
@ -1223,10 +1239,10 @@ mobile/views/components/media-video.vue:
|
||||
click-to-show: "Naciśnij aby wyświetlić"
|
||||
common/views/components/follow-button.vue:
|
||||
following: "フォロー中"
|
||||
follow: "フォロー"
|
||||
follow: "Śledź"
|
||||
request-pending: "フォロー許可待ち"
|
||||
follow-processing: "フォロー処理中"
|
||||
follow-request: "フォロー申請"
|
||||
follow-request: "Poproś o śledzenie"
|
||||
mobile/views/components/friends-maker.vue:
|
||||
title: "Zacznij śledzić ludzi takich jak Ty"
|
||||
empty: "Nie znaleziono podobnych użytkowników."
|
||||
@ -1371,6 +1387,8 @@ mobile/views/pages/settings.vue:
|
||||
note-visibility: "投稿の公開範囲"
|
||||
default-note-visibility: "デフォルトの公開範囲"
|
||||
remember-note-visibility: "投稿の公開範囲を記憶する"
|
||||
web-search-engine: "ウェブ検索エンジン"
|
||||
web-search-engine-desc: "例: https://www.google.com/?#q={{query}}"
|
||||
disable-via-mobile: "Nie oznaczaj wpisów jako „wysłane z telefonu”"
|
||||
load-raw-images: "Wyświetlaj załączone zdjęcia w wysokiej jakości"
|
||||
load-remote-media: "Wyświetlaj zawartość multimedialną ze zdalnych serwerów"
|
||||
@ -1414,7 +1432,7 @@ mobile/views/pages/user.vue:
|
||||
unmute: "ミュート解除"
|
||||
block: "ブロック"
|
||||
unblock: "ブロック解除"
|
||||
years-old: "{age}歳"
|
||||
years-old: "{age} lat"
|
||||
mobile/views/pages/user/home.vue:
|
||||
recent-notes: "Ostatnie wpisy"
|
||||
images: "Zdjęcia"
|
||||
|
@ -26,6 +26,7 @@ common:
|
||||
close: "Fechar"
|
||||
do-not-copy-paste: "Por favor, não digite ou copie o código aqui. A conta pode ser comprometida."
|
||||
load-more: "もっと読み込む"
|
||||
enter-password: "パスワードを入力してください"
|
||||
got-it: "Entendi!"
|
||||
customization-tips:
|
||||
title: "Dicas de personalização"
|
||||
@ -457,6 +458,10 @@ common/views/components/profile-editor.vue:
|
||||
saved: "プロフィールを保存しました"
|
||||
uploading: "アップロード中"
|
||||
upload-failed: "アップロードに失敗しました"
|
||||
email: "メール設定"
|
||||
email-address: "メールアドレス"
|
||||
email-verified: "メールアドレスが確認されました"
|
||||
email-not-verified: "メールアドレスが確認されていません。メールボックスをご確認ください。"
|
||||
common/views/widgets/broadcast.vue:
|
||||
fetching: "確認中"
|
||||
no-broadcasts: "お知らせはありません"
|
||||
@ -731,6 +736,8 @@ desktop/views/components/settings.vue:
|
||||
note-visibility: "投稿の公開範囲"
|
||||
default-note-visibility: "デフォルトの公開範囲"
|
||||
remember-note-visibility: "投稿の公開範囲を記憶する"
|
||||
web-search-engine: "ウェブ検索エンジン"
|
||||
web-search-engine-desc: "例: https://www.google.com/?#q={{query}}"
|
||||
auto-popout: "ウィンドウの自動ポップアウト"
|
||||
auto-popout-desc: "ウィンドウが開かれるとき、ポップアウト(ブラウザ外に切り離す)可能なら自動でポップアウトします。この設定はブラウザに記憶されます。"
|
||||
advanced: "詳細設定"
|
||||
@ -966,17 +973,17 @@ admin/views/instance.vue:
|
||||
recaptcha-site-key: "reCAPTCHA site key"
|
||||
recaptcha-secret-key: "reCAPTCHA secret key"
|
||||
twitter-integration-config: "Twitter連携の設定"
|
||||
twitter-integration-info: "コールバックURLは /api/tw/cb に設定します。"
|
||||
twitter-integration-info: "コールバックURLは {url} に設定します。"
|
||||
enable-twitter-integration: "Twitter連携を有効にする"
|
||||
twitter-integration-consumer-key: "Consumer key"
|
||||
twitter-integration-consumer-secret: "Consumer secret"
|
||||
github-integration-config: "GitHub連携の設定"
|
||||
github-integration-info: "コールバックURLは /api/gh/cb に設定します。"
|
||||
github-integration-info: "コールバックURLは {url} に設定します。"
|
||||
enable-github-integration: "GitHub連携を有効にする"
|
||||
github-integration-client-id: "Client ID"
|
||||
github-integration-client-secret: "Client Secret"
|
||||
discord-integration-config: "Discord連携の設定"
|
||||
discord-integration-info: "コールバックURLは /api/dc/cb に設定します。"
|
||||
discord-integration-info: "コールバックURLは {url} に設定します。"
|
||||
enable-discord-integration: "Discord連携を有効にする"
|
||||
discord-integration-client-id: "Client ID"
|
||||
discord-integration-client-secret: "Client Secret"
|
||||
@ -997,6 +1004,15 @@ admin/views/instance.vue:
|
||||
external-user-recommendation-engine-desc: "例: https://vinayaka.distsn.org/cgi-bin/vinayaka-user-match-misskey-api.cgi?{{host}}+{{user}}+{{limit}}+{{offset}}"
|
||||
external-user-recommendation-timeout: "タイムアウト"
|
||||
external-user-recommendation-timeout-desc: "ミリ秒単位 (例: 300000)"
|
||||
email-config: "メールサーバーの設定"
|
||||
email-config-info: "メールアドレス確認やパスワードリセットの際に使われます。"
|
||||
enable-email: "メール配信を有効にする"
|
||||
email: "メールアドレス"
|
||||
smtp-use-ssl: "SMTPサーバーはSSLを使用"
|
||||
smtp-host: "SMTPホスト"
|
||||
smtp-port: "SMTPポート"
|
||||
smtp-user: "SMTPユーザー"
|
||||
smtp-pass: "SMTPパスワード"
|
||||
admin/views/charts.vue:
|
||||
title: "チャート"
|
||||
per-day: "1日ごと"
|
||||
@ -1371,6 +1387,8 @@ mobile/views/pages/settings.vue:
|
||||
note-visibility: "投稿の公開範囲"
|
||||
default-note-visibility: "デフォルトの公開範囲"
|
||||
remember-note-visibility: "投稿の公開範囲を記憶する"
|
||||
web-search-engine: "ウェブ検索エンジン"
|
||||
web-search-engine-desc: "例: https://www.google.com/?#q={{query}}"
|
||||
disable-via-mobile: "「モバイルからの投稿」フラグを付けない"
|
||||
load-raw-images: "添付された画像を高画質で表示する"
|
||||
load-remote-media: "リモートサーバーのメディアを表示する"
|
||||
|
@ -7,61 +7,62 @@ common:
|
||||
about-title: "Мы — ⭐ fediverse"
|
||||
about: "Спасибо, что нашли Misskey. Misskey — это <b>децентрализованная платформа для микроблоггинга</b> родом с планеты Земля. Поскольку она существует внутри Fediverse (вселенной различных социальных платформ), она связана с другими платформами. Отдохните от шума большого города — и познакомьтесь с новым интернетом."
|
||||
intro:
|
||||
title: "Misskeyって?"
|
||||
about: "Misskeyはオープンソースの<b>分散型マイクロブログSNS</b>です。リッチで高度にカスタマイズできるUI、投稿へのリアクション、ファイルを一元管理できるドライブなど、先進的な機能を揃えています。また、Fediverseと呼ばれるネットワークに接続できるため、他のSNSともやり取りできます。例えば、あなたが何か投稿すると、その投稿はMisskeyだけでなく他のSNSにも伝わります。ちょうどある惑星から他の惑星に電波を発信している様子をイメージしてください。"
|
||||
features: "特徴"
|
||||
rich-contents: "投稿"
|
||||
rich-contents-desc: "自分の考え、話題の出来事、皆と共有したいことについて発信してください。必要であれば、様々な構文を使って投稿を装飾したり、好きな画像、動画などのファイルやアンケートを添付することもできます。"
|
||||
reaction: "リアクション"
|
||||
reaction-desc: "あなたの気持ちを伝える最も簡単な方法です。Misskeyは、他のユーザーの投稿に様々なリアクションを付けることができます。いちどMisskeyのリアクション機能を体験してしまうと、もう「いいね」の概念しか存在しないSNSには戻れなくなるかもしれません。"
|
||||
ui: "インターフェース"
|
||||
ui-desc: "どのようなUIが使いやすいかは人それぞれです。だから、Misskeyは自由度の高いUIを持っています。レイアウトやデザインを調整したり、カスタマイズ可能な様々なウィジェットを配置したりして、自分だけのホームを作ってください。"
|
||||
drive: "ドライブ"
|
||||
drive-desc: "以前投稿したことのある画像をまた投稿したくなったことはありませんか?もしくは、アップロードしたファイルをフォルダ分けして整理したくなったことはありませんか?Misskeyの根幹に組み込まれたドライブ機能によってそれらが解決します。ファイルの共有も簡単です。"
|
||||
outro: "他にもMisskeyにしかない機能はまだまだあるので、ぜひあなた自身の目で確かめてください。Misskeyは分散型SNSなので、このインスタンスが気に入らなければ他のインスタンスを試すこともできます。それでは、GLHF!"
|
||||
title: "Что такое Misskey?"
|
||||
about: "Misskey - это <b>децентрализованный сервис микроблогинга</b> с открытым исходным кодом. Он имеет такие функции, как: навороченный, полностью настраиваемый пользовательский интерфейс, множество реакций на посты, бесплатное хранилище файлов с интегрированной системой управления и ещё куча передовых фишек. А ещё сетевая система под названием “Fediverse” позволяет нам общаться с пользователями других социальных сетей. Например, если ты что-нибудь запостишь, то твой пост будет отослан не только в Misskey, но ещё и mastodon. Просто представь, что планета посылает микроволны на другую планету для коммуникации."
|
||||
features: "Особенности"
|
||||
rich-contents: "Посты"
|
||||
rich-contents-desc: "Просто выложи свою идею, актуальные темы и всё, что тебе хочется показать миру. Ты можешь декорировать свои слова, прикреплять свои любимые картинки, отправлять файлы с фильмами и создать голосование - это те вещи, которые ты можешь сделать с помощью Misskey!"
|
||||
reaction: "Реакции"
|
||||
reaction-desc: "Самый лёгкий способ выразить свои эмоции. Misskey позволяет добавлять различные виды реакций к постам других людей. Эмоциональный опыт из Misskey никогда не появится в других социальных сетях, позволяющих только жать “лайки”."
|
||||
ui: "Интерфейс"
|
||||
ui-desc: "Нет такого интерфейса, понравившегося всем. Поэтому у Misskey имеется пользовательский интерфейс, широко настраиваемый под ваши вкусы. Создай себе уникальную домашнюю страницу редактируя, подстраивая оформление ленты и размещая виджеты, которые тоже можно кастомизировать."
|
||||
drive: "Хранилище файлов"
|
||||
drive-desc: "Хотите запостить картинку, которую уже отправляли ранее? Хочется сортировать, переименовать и создать папку для ваших выложенных файлов? Тогда Misskey Drive - это лучшее решение для вас. Очень лёгкий способ делиться своими файлами онлайн."
|
||||
outro: "Попробуйте будущие, уникальные для Misskey функции своими глазами! Если чувствуете, что это не в вашем вкусе, то попробуйте другие инстанции, ведь Misskey - это децентрализованная социальная сеть, так что ты можешь с лёгкостью найти себе товарищей. И наконец, GLHF!"
|
||||
adblock:
|
||||
detected: "広告ブロッカーを無効にしてください"
|
||||
warning: "<strong>Misskeyは広告を掲載していません</strong>が、広告をブロックする機能が有効だと一部の機能が利用できなかったり、不具合が発生する場合があります。"
|
||||
application-authorization: "アプリの連携"
|
||||
close: "閉じる"
|
||||
do-not-copy-paste: "ここにコードを入力したり張り付けたりしないでください。アカウントが不正利用される可能性があります。"
|
||||
load-more: "もっと読み込む"
|
||||
detected: "Пожалуйста, отключите блокировщик рекламы."
|
||||
warning: "Некоторые функции перестанут быть доступными или будут работать неправильно, если включена блокировка рекламы. <strong>В Misskey нет рекламы</strong>."
|
||||
application-authorization: "Авторизация приложений"
|
||||
close: "Закрыть"
|
||||
do-not-copy-paste: "Пожалуйста, не вводите и не вставляйте сюда код. Аккаунту может угрожать опасность."
|
||||
load-more: "Загрузить больше"
|
||||
enter-password: "パスワードを入力してください"
|
||||
got-it: "わかった"
|
||||
customization-tips:
|
||||
title: "カスタマイズのヒント"
|
||||
paragraph: "<p>ホームのカスタマイズでは、ウィジェットを追加/削除したり、ドラッグ&ドロップして並べ替えたりすることができます。</p><p>一部のウィジェットは、<strong><strong>右</strong>クリック</strong>することで表示を変更することができます。</p><p>ウィジェットを削除するには、ヘッダーの<strong>「ゴミ箱」</strong>と書かれたエリアにウィジェットをドラッグ&ドロップします。</p><p>カスタマイズを終了するには、右上の「完了」をクリックします。</p>"
|
||||
gotit: "Got it!"
|
||||
notification:
|
||||
file-uploaded: "ファイルがアップロードされました"
|
||||
message-from: "{}さんからメッセージ:"
|
||||
reversi-invited: "対局への招待があります"
|
||||
reversi-invited-by: "{}さんから"
|
||||
file-uploaded: "Файл отправлен!"
|
||||
message-from: "Сообщение от {}:"
|
||||
reversi-invited: "Приглашён в игру"
|
||||
reversi-invited-by: "Был приглашён {}:"
|
||||
notified-by: "{}さんから"
|
||||
reply-from: "{}さんから返信:"
|
||||
quoted-by: "{}さんが引用:"
|
||||
reply-from: "Ответ от {}:"
|
||||
quoted-by: "Цитировано {}:"
|
||||
time:
|
||||
unknown: "なぞのじかん"
|
||||
future: "未来"
|
||||
just_now: "たった今"
|
||||
seconds_ago: "{}秒前"
|
||||
minutes_ago: "{}分前"
|
||||
hours_ago: "{}時間前"
|
||||
days_ago: "{}日前"
|
||||
weeks_ago: "{}週間前"
|
||||
months_ago: "{}ヶ月前"
|
||||
years_ago: "{}年前"
|
||||
month-and-day: "{month}月 {day}日"
|
||||
trash: "ゴミ箱"
|
||||
drive: "ドライブ"
|
||||
messaging: "トーク"
|
||||
unknown: "неизвестно"
|
||||
future: "сейчас"
|
||||
just_now: "сейчас"
|
||||
seconds_ago: "{} секунд назад"
|
||||
minutes_ago: "{} минут назад"
|
||||
hours_ago: "{} часов назад"
|
||||
days_ago: "{} дней назад"
|
||||
weeks_ago: "{} недель назад"
|
||||
months_ago: "{} месяцев назад"
|
||||
years_ago: "{} лет назад"
|
||||
month-and-day: "{day}.{month}"
|
||||
trash: "Мусорное ведро"
|
||||
drive: "Drive"
|
||||
messaging: "Чат"
|
||||
weekday-short:
|
||||
sunday: "日"
|
||||
monday: "月"
|
||||
tuesday: "火"
|
||||
wednesday: "水"
|
||||
thursday: "木"
|
||||
friday: "金"
|
||||
saturday: "土"
|
||||
sunday: "Вс"
|
||||
monday: "Пн"
|
||||
tuesday: "Вт"
|
||||
wednesday: "Ср"
|
||||
thursday: "Чт"
|
||||
friday: "Пт"
|
||||
saturday: "Сб"
|
||||
weekday:
|
||||
sunday: "日曜日"
|
||||
monday: "月曜日"
|
||||
@ -111,60 +112,60 @@ common:
|
||||
show-reversi-board-labels: "リバーシのボードの行と列のラベルを表示"
|
||||
use-contrast-reversi-stones: "リバーシのアイコンにコントラストを付ける"
|
||||
verified-user: "公式アカウント"
|
||||
disable-animated-mfm: "投稿内の動きのあるテキストを無効にする"
|
||||
always-show-nsfw: "常に閲覧注意のメディアを表示する"
|
||||
always-mark-nsfw: "常にメディアを閲覧注意として投稿"
|
||||
disable-animated-mfm: "Отключить анимированный текст в постах"
|
||||
always-show-nsfw: "Всегда показывать NSFW контент"
|
||||
always-mark-nsfw: "Всегда помечать посты с медиафайлами как NSFW"
|
||||
show-full-acct: "ユーザー名のホストを省略しない"
|
||||
show-via: "viaを表示する"
|
||||
reduce-motion: "UIの動きを減らす"
|
||||
this-setting-is-this-device-only: "このデバイスのみ"
|
||||
use-os-default-emojis: "OS標準の絵文字を使用"
|
||||
do-not-use-in-production: 'これは開発ビルドです。本番環境で使用しないでください。'
|
||||
show-via: "показать через"
|
||||
reduce-motion: "Уменьшить движение в интерфейсе"
|
||||
this-setting-is-this-device-only: "Только для этого устройства"
|
||||
use-os-default-emojis: "Использовать стандартные Emoji из ОС"
|
||||
do-not-use-in-production: 'Эта сборка для разработчиков. Не используйте в продакшне.'
|
||||
is-remote-user: "このユーザー情報はコピーです。"
|
||||
is-remote-post: "この投稿情報はコピーです。"
|
||||
view-on-remote: "正確な情報を見る"
|
||||
renoted-by: "{user}がRenote"
|
||||
error:
|
||||
title: '問題が発生しました'
|
||||
retry: 'やり直す'
|
||||
title: 'Что-то пошло не так :('
|
||||
retry: 'Повторить'
|
||||
reversi:
|
||||
drawn: "引き分け"
|
||||
my-turn: "あなたのターンです"
|
||||
opponent-turn: "相手のターンです"
|
||||
turn-of: "{name}のターンです"
|
||||
past-turn-of: "{name}のターン"
|
||||
won: "{name}の勝ち"
|
||||
black: "黒"
|
||||
white: "白"
|
||||
total: "合計"
|
||||
this-turn: "{count}ターン目"
|
||||
drawn: "Ничья"
|
||||
my-turn: "Ваш ход"
|
||||
opponent-turn: "Ход оппонента"
|
||||
turn-of: "Ход {name}"
|
||||
past-turn-of: "Ход {name}"
|
||||
won: "{name} победил"
|
||||
black: "Чёрный"
|
||||
white: "Белый"
|
||||
total: "Всего"
|
||||
this-turn: "Ход {count}"
|
||||
widgets:
|
||||
analog-clock: "アナログ時計"
|
||||
profile: "プロフィール"
|
||||
calendar: "カレンダー"
|
||||
timemachine: "カレンダー(タイムマシン)"
|
||||
activity: "アクティビティ"
|
||||
rss: "RSSリーダー"
|
||||
memo: "付箋"
|
||||
trends: "トレンド"
|
||||
photo-stream: "フォトストリーム"
|
||||
analog-clock: "Аналоговые часы"
|
||||
profile: "Профиль"
|
||||
calendar: "Календарь"
|
||||
timemachine: "Календарь (машина времени)"
|
||||
activity: "Активность"
|
||||
rss: "Ридер RSS"
|
||||
memo: "Заметка"
|
||||
trends: "Популярное"
|
||||
photo-stream: "Фотопоток"
|
||||
posts-monitor: "投稿チャート"
|
||||
slideshow: "スライドショー"
|
||||
version: "バージョン"
|
||||
slideshow: "Слайдшоу"
|
||||
version: "Версия"
|
||||
broadcast: "ブロードキャスト"
|
||||
notifications: "通知"
|
||||
users: "おすすめユーザー"
|
||||
polls: "アンケート"
|
||||
notifications: "Уведомления"
|
||||
users: "Рекомендованные пользователи"
|
||||
polls: "Голосования"
|
||||
post-form: "投稿フォーム"
|
||||
server: "サーバー情報"
|
||||
server: "Информация о сервере"
|
||||
donation: "寄付のお願い"
|
||||
nav: "ナビゲーション"
|
||||
tips: "ヒント"
|
||||
hashtags: "ハッシュタグ"
|
||||
dev: "アプリの作成に失敗しました。再度お試しください。"
|
||||
ai-chan-kawaii: "藍ちゃかわいい"
|
||||
hashtags: "Хэштеги"
|
||||
dev: "Не удалось создать приложение. Пожалуйста, попробуйте ещё раз."
|
||||
ai-chan-kawaii: "Ai-chan kawaii!"
|
||||
auth/views/form.vue:
|
||||
share-access: "<i>{name}</i>があなたのアカウントにアクセスすることを許可しますか?"
|
||||
share-access: "Вы разрешаете <i>{name}</i> получить доступ к вашему аккаунту?"
|
||||
permission-ask: "このアプリは次の権限を要求しています:"
|
||||
account-read: "アカウントの情報を見る。"
|
||||
account-write: "アカウントの情報を操作する。"
|
||||
@ -210,28 +211,28 @@ common/views/components/games/reversi/reversi.index.vue:
|
||||
all-games: "みんなの対局"
|
||||
enter-username: "ユーザー名を入力してください"
|
||||
game-state:
|
||||
ended: "終了"
|
||||
playing: "進行中"
|
||||
ended: "Завершено"
|
||||
playing: "В процессе"
|
||||
common/views/components/games/reversi/reversi.room.vue:
|
||||
settings-of-the-game: "ゲームの設定"
|
||||
settings-of-the-game: "Настройки игры"
|
||||
choose-map: "マップを選択"
|
||||
random: "ランダム"
|
||||
black-or-white: "先手/後手"
|
||||
black-is: "{}が黒"
|
||||
rules: "ルール"
|
||||
random: "Случайно"
|
||||
black-or-white: "Чёрные/Белые"
|
||||
black-is: "{} ходит чёрными"
|
||||
rules: "Правила"
|
||||
is-llotheo: "石の少ない方が勝ち(ロセオ)"
|
||||
looped-map: "ループマップ"
|
||||
can-put-everywhere: "どこでも置けるモード"
|
||||
settings-of-the-bot: "Botの設定"
|
||||
this-game-is-started-soon: "ゲームは数秒後に開始されます"
|
||||
waiting-for-other: "相手の準備が完了するのを待っています"
|
||||
settings-of-the-bot: "Настройки бота"
|
||||
this-game-is-started-soon: "Игра вот-вот начнётся"
|
||||
waiting-for-other: "Ожидание оппонента"
|
||||
waiting-for-me: "あなたの準備が完了するのを待っています"
|
||||
waiting-for-both: "準備中"
|
||||
cancel: "キャンセル"
|
||||
ready: "準備完了"
|
||||
cancel: "Отмена"
|
||||
ready: "Готов"
|
||||
cancel-ready: "準備続行"
|
||||
common/views/components/connect-failed.vue:
|
||||
title: "サーバーに接続できません"
|
||||
title: "Невозможно подключиться к серверу"
|
||||
description: "インターネット回線に問題があるか、サーバーがダウンまたはメンテナンスしている可能性があります。しばらくしてから{再度お試し}ください。"
|
||||
thanks: "いつもMisskeyをご利用いただきありがとうございます。"
|
||||
troubleshoot: "トラブルシュート"
|
||||
@ -457,6 +458,10 @@ common/views/components/profile-editor.vue:
|
||||
saved: "プロフィールを保存しました"
|
||||
uploading: "アップロード中"
|
||||
upload-failed: "アップロードに失敗しました"
|
||||
email: "メール設定"
|
||||
email-address: "メールアドレス"
|
||||
email-verified: "メールアドレスが確認されました"
|
||||
email-not-verified: "メールアドレスが確認されていません。メールボックスをご確認ください。"
|
||||
common/views/widgets/broadcast.vue:
|
||||
fetching: "確認中"
|
||||
no-broadcasts: "お知らせはありません"
|
||||
@ -731,6 +736,8 @@ desktop/views/components/settings.vue:
|
||||
note-visibility: "投稿の公開範囲"
|
||||
default-note-visibility: "デフォルトの公開範囲"
|
||||
remember-note-visibility: "投稿の公開範囲を記憶する"
|
||||
web-search-engine: "ウェブ検索エンジン"
|
||||
web-search-engine-desc: "例: https://www.google.com/?#q={{query}}"
|
||||
auto-popout: "ウィンドウの自動ポップアウト"
|
||||
auto-popout-desc: "ウィンドウが開かれるとき、ポップアウト(ブラウザ外に切り離す)可能なら自動でポップアウトします。この設定はブラウザに記憶されます。"
|
||||
advanced: "詳細設定"
|
||||
@ -966,17 +973,17 @@ admin/views/instance.vue:
|
||||
recaptcha-site-key: "reCAPTCHA site key"
|
||||
recaptcha-secret-key: "reCAPTCHA secret key"
|
||||
twitter-integration-config: "Twitter連携の設定"
|
||||
twitter-integration-info: "コールバックURLは /api/tw/cb に設定します。"
|
||||
twitter-integration-info: "コールバックURLは {url} に設定します。"
|
||||
enable-twitter-integration: "Twitter連携を有効にする"
|
||||
twitter-integration-consumer-key: "Consumer key"
|
||||
twitter-integration-consumer-secret: "Consumer secret"
|
||||
github-integration-config: "GitHub連携の設定"
|
||||
github-integration-info: "コールバックURLは /api/gh/cb に設定します。"
|
||||
github-integration-info: "コールバックURLは {url} に設定します。"
|
||||
enable-github-integration: "GitHub連携を有効にする"
|
||||
github-integration-client-id: "Client ID"
|
||||
github-integration-client-secret: "Client Secret"
|
||||
discord-integration-config: "Discord連携の設定"
|
||||
discord-integration-info: "コールバックURLは /api/dc/cb に設定します。"
|
||||
discord-integration-info: "コールバックURLは {url} に設定します。"
|
||||
enable-discord-integration: "Discord連携を有効にする"
|
||||
discord-integration-client-id: "Client ID"
|
||||
discord-integration-client-secret: "Client Secret"
|
||||
@ -997,6 +1004,15 @@ admin/views/instance.vue:
|
||||
external-user-recommendation-engine-desc: "例: https://vinayaka.distsn.org/cgi-bin/vinayaka-user-match-misskey-api.cgi?{{host}}+{{user}}+{{limit}}+{{offset}}"
|
||||
external-user-recommendation-timeout: "タイムアウト"
|
||||
external-user-recommendation-timeout-desc: "ミリ秒単位 (例: 300000)"
|
||||
email-config: "メールサーバーの設定"
|
||||
email-config-info: "メールアドレス確認やパスワードリセットの際に使われます。"
|
||||
enable-email: "メール配信を有効にする"
|
||||
email: "メールアドレス"
|
||||
smtp-use-ssl: "SMTPサーバーはSSLを使用"
|
||||
smtp-host: "SMTPホスト"
|
||||
smtp-port: "SMTPポート"
|
||||
smtp-user: "SMTPユーザー"
|
||||
smtp-pass: "SMTPパスワード"
|
||||
admin/views/charts.vue:
|
||||
title: "チャート"
|
||||
per-day: "1日ごと"
|
||||
@ -1371,6 +1387,8 @@ mobile/views/pages/settings.vue:
|
||||
note-visibility: "投稿の公開範囲"
|
||||
default-note-visibility: "デフォルトの公開範囲"
|
||||
remember-note-visibility: "投稿の公開範囲を記憶する"
|
||||
web-search-engine: "ウェブ検索エンジン"
|
||||
web-search-engine-desc: "例: https://www.google.com/?#q={{query}}"
|
||||
disable-via-mobile: "「モバイルからの投稿」フラグを付けない"
|
||||
load-raw-images: "添付された画像を高画質で表示する"
|
||||
load-remote-media: "リモートサーバーのメディアを表示する"
|
||||
|
@ -26,6 +26,7 @@ common:
|
||||
close: "关闭"
|
||||
do-not-copy-paste: "请不要在这里输入或粘贴代码。您帐户可能会受到损害。"
|
||||
load-more: "加载更多"
|
||||
enter-password: "パスワードを入力してください"
|
||||
got-it: "没问题"
|
||||
customization-tips:
|
||||
title: "客制化提示"
|
||||
@ -457,6 +458,10 @@ common/views/components/profile-editor.vue:
|
||||
saved: "更新配置文件成功"
|
||||
uploading: "正在上传"
|
||||
upload-failed: "上传失败"
|
||||
email: "メール設定"
|
||||
email-address: "メールアドレス"
|
||||
email-verified: "メールアドレスが確認されました"
|
||||
email-not-verified: "メールアドレスが確認されていません。メールボックスをご確認ください。"
|
||||
common/views/widgets/broadcast.vue:
|
||||
fetching: "确认中"
|
||||
no-broadcasts: "没有公告"
|
||||
@ -731,6 +736,8 @@ desktop/views/components/settings.vue:
|
||||
note-visibility: "帖子可见性"
|
||||
default-note-visibility: "默认可见性"
|
||||
remember-note-visibility: "记住帖子可见性"
|
||||
web-search-engine: "ウェブ検索エンジン"
|
||||
web-search-engine-desc: "例: https://www.google.com/?#q={{query}}"
|
||||
auto-popout: "自动弹出窗口"
|
||||
auto-popout-desc: "如果可用,将使用弹出显示而不是打开新窗口。 此设置存储在浏览器中。"
|
||||
advanced: "更多设置"
|
||||
@ -966,17 +973,17 @@ admin/views/instance.vue:
|
||||
recaptcha-site-key: "reCAPTCHA site key"
|
||||
recaptcha-secret-key: "reCAPTCHA secret key"
|
||||
twitter-integration-config: "连接到Twitter的设置"
|
||||
twitter-integration-info: "回调 URL 设置为 /api/tw/cb"
|
||||
twitter-integration-info: "设置返回的URL{url}。"
|
||||
enable-twitter-integration: "启用连接到Twitter"
|
||||
twitter-integration-consumer-key: "Consumer key"
|
||||
twitter-integration-consumer-secret: "Consumer Secret"
|
||||
github-integration-config: "连接到GitHub设置"
|
||||
github-integration-info: "回调 URL 设置为 /api/gh/cb"
|
||||
github-integration-info: "设置返回的URL{url}。"
|
||||
enable-github-integration: "启用连接到GitHub"
|
||||
github-integration-client-id: "Client ID"
|
||||
github-integration-client-secret: "Client Secret"
|
||||
discord-integration-config: "设置 Discord Integration"
|
||||
discord-integration-info: "回调 URL 设置为 /api/dc/cb"
|
||||
discord-integration-info: "设置返回的URL{url}。"
|
||||
enable-discord-integration: "启用 Discord 连接"
|
||||
discord-integration-client-id: "Client ID"
|
||||
discord-integration-client-secret: "Client Secret"
|
||||
@ -991,12 +998,21 @@ admin/views/instance.vue:
|
||||
invite: "邀请"
|
||||
save: "保存"
|
||||
saved: "保存完毕"
|
||||
user-recommendation-config: "おすすめユーザー"
|
||||
enable-external-user-recommendation: "外部ユーザーレコメンデーションを有効にする"
|
||||
external-user-recommendation-engine: "エンジン"
|
||||
external-user-recommendation-engine-desc: "例: https://vinayaka.distsn.org/cgi-bin/vinayaka-user-match-misskey-api.cgi?{{host}}+{{user}}+{{limit}}+{{offset}}"
|
||||
external-user-recommendation-timeout: "タイムアウト"
|
||||
external-user-recommendation-timeout-desc: "ミリ秒単位 (例: 300000)"
|
||||
user-recommendation-config: "推荐用户"
|
||||
enable-external-user-recommendation: "启用外部用户推荐"
|
||||
external-user-recommendation-engine: "引擎"
|
||||
external-user-recommendation-engine-desc: "例如: https://vinayaka.distsn.org/cgi-bin/vinayaka-user-match-misskey-api.cgi?{{host}}+{{user}}+{{limit}}+{{offset}}"
|
||||
external-user-recommendation-timeout: "超时"
|
||||
external-user-recommendation-timeout-desc: "单位为毫秒 (例如:300000)"
|
||||
email-config: "メールサーバーの設定"
|
||||
email-config-info: "メールアドレス確認やパスワードリセットの際に使われます。"
|
||||
enable-email: "メール配信を有効にする"
|
||||
email: "メールアドレス"
|
||||
smtp-use-ssl: "SMTPサーバーはSSLを使用"
|
||||
smtp-host: "SMTPホスト"
|
||||
smtp-port: "SMTPポート"
|
||||
smtp-user: "SMTPユーザー"
|
||||
smtp-pass: "SMTPパスワード"
|
||||
admin/views/charts.vue:
|
||||
title: "历史记录"
|
||||
per-day: "每天"
|
||||
@ -1024,11 +1040,11 @@ admin/views/charts.vue:
|
||||
network-usage: "网络流量"
|
||||
admin/views/users.vue:
|
||||
operation: "操作"
|
||||
username-or-userid: "ユーザー名またはユーザーID"
|
||||
user-not-found: "ユーザーが見つかりません"
|
||||
lookup: "照会"
|
||||
reset-password: "パスワードをリセット"
|
||||
password-updated: "パスワードは現在「{password}」です"
|
||||
username-or-userid: "用户名或用户ID"
|
||||
user-not-found: "用户不存在"
|
||||
lookup: "订阅"
|
||||
reset-password: "密码重置"
|
||||
password-updated: "密码为「{password}」"
|
||||
suspend: "被冻结"
|
||||
suspended: "成功冻结用户"
|
||||
unsuspend: "已解除冻结"
|
||||
@ -1038,20 +1054,20 @@ admin/views/users.vue:
|
||||
unverify: "解除账户认证"
|
||||
unverified: "该帐户未经认证"
|
||||
users:
|
||||
title: "ユーザー"
|
||||
title: "用户"
|
||||
sort:
|
||||
title: "ソート"
|
||||
createdAtAsc: "登録日時が古い順"
|
||||
createdAtDesc: "登録日時が新しい順"
|
||||
updatedAtAsc: "更新日時が古い順"
|
||||
updatedAtDesc: "更新日時が新しい順"
|
||||
title: "排序"
|
||||
createdAtAsc: "注册时间从旧到新"
|
||||
createdAtDesc: "注册时间从新到旧"
|
||||
updatedAtAsc: "更新时间从旧到新"
|
||||
updatedAtDesc: "更新时间从新到旧"
|
||||
origin:
|
||||
title: "オリジン"
|
||||
combined: "ローカル+リモート"
|
||||
local: "ローカル"
|
||||
remote: "リモート"
|
||||
createdAt: "登録日時"
|
||||
updatedAt: "更新日時"
|
||||
title: "源自"
|
||||
combined: "本地+远程"
|
||||
local: "本地"
|
||||
remote: "远程"
|
||||
createdAt: "注册日期"
|
||||
updatedAt: "最后更新"
|
||||
admin/views/moderators.vue:
|
||||
add-moderator:
|
||||
title: "注册版主"
|
||||
@ -1371,6 +1387,8 @@ mobile/views/pages/settings.vue:
|
||||
note-visibility: "帖子可见性"
|
||||
default-note-visibility: "默认可见性"
|
||||
remember-note-visibility: "记住帖子可见性"
|
||||
web-search-engine: "ウェブ検索エンジン"
|
||||
web-search-engine-desc: "例: https://www.google.com/?#q={{query}}"
|
||||
disable-via-mobile: "不要将帖子标记为“来自手机”"
|
||||
load-raw-images: "以原始质量显示附加图像"
|
||||
load-remote-media: "显示来自远程服务器的媒体"
|
||||
|
16
package.json
16
package.json
@ -1,8 +1,8 @@
|
||||
{
|
||||
"name": "misskey",
|
||||
"author": "syuilo <i@syuilo.com>",
|
||||
"version": "10.57.1",
|
||||
"clientVersion": "2.0.12082",
|
||||
"version": "10.59.2",
|
||||
"clientVersion": "2.0.12277",
|
||||
"codename": "nighthike",
|
||||
"main": "./built/index.js",
|
||||
"private": true,
|
||||
@ -64,6 +64,7 @@
|
||||
"@types/mongodb": "3.1.14",
|
||||
"@types/ms": "0.7.30",
|
||||
"@types/node": "10.12.10",
|
||||
"@types/nodemailer": "4.6.5",
|
||||
"@types/oauth": "0.9.1",
|
||||
"@types/parsimmon": "1.10.0",
|
||||
"@types/portscanner": "2.1.0",
|
||||
@ -88,7 +89,7 @@
|
||||
"@types/ws": "6.0.1",
|
||||
"animejs": "2.2.0",
|
||||
"apexcharts": "2.2.3",
|
||||
"autobind-decorator": "2.2.1",
|
||||
"autobind-decorator": "2.3.1",
|
||||
"autosize": "4.0.2",
|
||||
"autwh": "0.1.0",
|
||||
"bcryptjs": "2.4.3",
|
||||
@ -166,6 +167,7 @@
|
||||
"ms": "2.1.1",
|
||||
"nan": "2.11.1",
|
||||
"nested-property": "0.0.7",
|
||||
"nodemailer": "4.7.0",
|
||||
"nprogress": "0.2.0",
|
||||
"object-assign-deep": "0.4.0",
|
||||
"on-build-webpack": "0.1.0",
|
||||
@ -201,13 +203,13 @@
|
||||
"stylus": "0.54.5",
|
||||
"stylus-loader": "3.0.2",
|
||||
"summaly": "2.2.0",
|
||||
"systeminformation": "3.49.3",
|
||||
"systeminformation": "3.51.3",
|
||||
"syuilo-password-strength": "0.0.1",
|
||||
"terser-webpack-plugin": "1.1.0",
|
||||
"textarea-caret": "3.1.0",
|
||||
"tinycolor2": "1.4.1",
|
||||
"tmp": "0.0.33",
|
||||
"ts-loader": "5.3.0",
|
||||
"ts-loader": "5.3.1",
|
||||
"ts-node": "7.0.1",
|
||||
"tslint": "5.10.0",
|
||||
"typescript": "3.1.6",
|
||||
@ -224,7 +226,7 @@
|
||||
"vue-js-modal": "1.3.26",
|
||||
"vue-loader": "15.4.2",
|
||||
"vue-marquee-text-component": "1.1.0",
|
||||
"vue-router": "3.0.1",
|
||||
"vue-router": "3.0.2",
|
||||
"vue-style-loader": "4.1.2",
|
||||
"vue-svg-inline-loader": "1.2.2",
|
||||
"vue-template-compiler": "2.5.17",
|
||||
@ -233,7 +235,7 @@
|
||||
"vuex": "3.0.1",
|
||||
"vuex-persistedstate": "2.5.4",
|
||||
"web-push": "3.3.3",
|
||||
"webfinger.js": "2.6.6",
|
||||
"webfinger.js": "2.7.0",
|
||||
"webpack": "4.26.0",
|
||||
"webpack-cli": "3.1.2",
|
||||
"websocket": "1.0.28",
|
||||
|
@ -12,11 +12,15 @@
|
||||
<section class="fit-bottom">
|
||||
<header><fa :icon="faHeadset"/> {{ $t('maintainer-config') }}</header>
|
||||
<ui-input v-model="maintainerName">{{ $t('maintainer-name') }}</ui-input>
|
||||
<ui-input v-model="maintainerEmail" type="email"><i slot="icon"><fa :icon="['far', 'envelope']"/></i>{{ $t('maintainer-email') }}</ui-input>
|
||||
<ui-input v-model="maintainerEmail" type="email"><i slot="icon"><fa :icon="farEnvelope"/></i>{{ $t('maintainer-email') }}</ui-input>
|
||||
</section>
|
||||
<section class="fit-top fit-bottom">
|
||||
<ui-input v-model="maxNoteTextLength">{{ $t('max-note-text-length') }}</ui-input>
|
||||
</section>
|
||||
<section>
|
||||
<ui-switch v-model="disableRegistration">{{ $t('disable-registration') }}</ui-switch>
|
||||
<ui-switch v-model="disableLocalTimeline">{{ $t('disable-local-timeline') }}</ui-switch>
|
||||
</section>
|
||||
<section class="fit-bottom">
|
||||
<header><fa icon="cloud"/> {{ $t('drive-config') }}</header>
|
||||
<ui-switch v-model="cacheRemoteFiles">{{ $t('cache-remote-files') }}<span slot="desc">{{ $t('cache-remote-files-desc') }}</span></ui-switch>
|
||||
@ -37,10 +41,18 @@
|
||||
<ui-info warn>{{ $t('proxy-account-warn') }}</ui-info>
|
||||
</section>
|
||||
<section>
|
||||
<ui-switch v-model="disableRegistration">{{ $t('disable-registration') }}</ui-switch>
|
||||
</section>
|
||||
<section>
|
||||
<ui-switch v-model="disableLocalTimeline">{{ $t('disable-local-timeline') }}</ui-switch>
|
||||
<header><fa :icon="farEnvelope"/> {{ $t('email-config') }}</header>
|
||||
<ui-switch v-model="enableEmail">{{ $t('enable-email') }}<span slot="desc">{{ $t('email-config-info') }}</span></ui-switch>
|
||||
<ui-input v-model="email" type="email" :disabled="!enableEmail">{{ $t('email') }}</ui-input>
|
||||
<ui-horizon-group inputs>
|
||||
<ui-input v-model="smtpHost" :disabled="!enableEmail">{{ $t('smtp-host') }}</ui-input>
|
||||
<ui-input v-model="smtpPort" type="number" :disabled="!enableEmail">{{ $t('smtp-port') }}</ui-input>
|
||||
</ui-horizon-group>
|
||||
<ui-horizon-group inputs>
|
||||
<ui-input v-model="smtpUser" :disabled="!enableEmail">{{ $t('smtp-user') }}</ui-input>
|
||||
<ui-input v-model="smtpPass" :disabled="!enableEmail">{{ $t('smtp-pass') }}</ui-input>
|
||||
</ui-horizon-group>
|
||||
<ui-switch v-model="smtpSecure" :disabled="!enableEmail">{{ $t('smtp-secure') }}<span slot="desc">{{ $t('smtp-secure-info') }}</span></ui-switch>
|
||||
</section>
|
||||
<section>
|
||||
<header>summaly Proxy</header>
|
||||
@ -69,7 +81,7 @@
|
||||
<div slot="title"><fa :icon="['fab', 'twitter']"/> {{ $t('twitter-integration-config') }}</div>
|
||||
<section>
|
||||
<ui-switch v-model="enableTwitterIntegration">{{ $t('enable-twitter-integration') }}</ui-switch>
|
||||
<ui-info>{{ $t('twitter-integration-info') }}</ui-info>
|
||||
<ui-info>{{ $t('twitter-integration-info', { url: `${url}/api/tw/cb` }) }}</ui-info>
|
||||
<ui-input v-model="twitterConsumerKey" :disabled="!enableTwitterIntegration"><i slot="icon"><fa icon="key"/></i>{{ $t('twitter-integration-consumer-key') }}</ui-input>
|
||||
<ui-input v-model="twitterConsumerSecret" :disabled="!enableTwitterIntegration"><i slot="icon"><fa icon="key"/></i>{{ $t('twitter-integration-consumer-secret') }}</ui-input>
|
||||
<ui-button @click="updateMeta">{{ $t('save') }}</ui-button>
|
||||
@ -80,7 +92,7 @@
|
||||
<div slot="title"><fa :icon="['fab', 'github']"/> {{ $t('github-integration-config') }}</div>
|
||||
<section>
|
||||
<ui-switch v-model="enableGithubIntegration">{{ $t('enable-github-integration') }}</ui-switch>
|
||||
<ui-info>{{ $t('github-integration-info') }}</ui-info>
|
||||
<ui-info>{{ $t('github-integration-info', { url: `${url}/api/gh/cb` }) }}</ui-info>
|
||||
<ui-input v-model="githubClientId" :disabled="!enableGithubIntegration"><i slot="icon"><fa icon="key"/></i>{{ $t('github-integration-client-id') }}</ui-input>
|
||||
<ui-input v-model="githubClientSecret" :disabled="!enableGithubIntegration"><i slot="icon"><fa icon="key"/></i>{{ $t('github-integration-client-secret') }}</ui-input>
|
||||
<ui-button @click="updateMeta">{{ $t('save') }}</ui-button>
|
||||
@ -91,7 +103,7 @@
|
||||
<div slot="title"><fa :icon="['fab', 'discord']"/> {{ $t('discord-integration-config') }}</div>
|
||||
<section>
|
||||
<ui-switch v-model="enableDiscordIntegration">{{ $t('enable-discord-integration') }}</ui-switch>
|
||||
<ui-info>{{ $t('discord-integration-info') }}</ui-info>
|
||||
<ui-info>{{ $t('discord-integration-info', { url: `${url}/api/dc/cb` }) }}</ui-info>
|
||||
<ui-input v-model="discordClientId" :disabled="!enableDiscordIntegration"><i slot="icon"><fa icon="key"/></i>{{ $t('discord-integration-client-id') }}</ui-input>
|
||||
<ui-input v-model="discordClientSecret" :disabled="!enableDiscordIntegration"><i slot="icon"><fa icon="key"/></i>{{ $t('discord-integration-client-secret') }}</ui-input>
|
||||
<ui-button @click="updateMeta">{{ $t('save') }}</ui-button>
|
||||
@ -103,15 +115,17 @@
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import i18n from '../../i18n';
|
||||
import { host } from '../../config';
|
||||
import { url, host } from '../../config';
|
||||
import { toUnicode } from 'punycode';
|
||||
import { faHeadset, faShieldAlt, faGhost, faUserPlus } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faEnvelope as farEnvelope } from '@fortawesome/free-regular-svg-icons';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n('admin/views/instance.vue'),
|
||||
|
||||
data() {
|
||||
return {
|
||||
url,
|
||||
host: toUnicode(host),
|
||||
maintainerName: null,
|
||||
maintainerEmail: null,
|
||||
@ -143,7 +157,14 @@ export default Vue.extend({
|
||||
externalUserRecommendationEngine: null,
|
||||
externalUserRecommendationTimeout: null,
|
||||
summalyProxy: null,
|
||||
faHeadset, faShieldAlt, faGhost, faUserPlus
|
||||
enableEmail: false,
|
||||
email: null,
|
||||
smtpSecure: false,
|
||||
smtpHost: null,
|
||||
smtpPort: null,
|
||||
smtpUser: null,
|
||||
smtpPass: null,
|
||||
faHeadset, faShieldAlt, faGhost, faUserPlus, farEnvelope
|
||||
};
|
||||
},
|
||||
|
||||
@ -176,6 +197,13 @@ export default Vue.extend({
|
||||
this.externalUserRecommendationEngine = meta.externalUserRecommendationEngine;
|
||||
this.externalUserRecommendationTimeout = meta.externalUserRecommendationTimeout;
|
||||
this.summalyProxy = meta.summalyProxy;
|
||||
this.enableEmail = meta.enableEmail;
|
||||
this.email = meta.email;
|
||||
this.smtpSecure = meta.smtpSecure;
|
||||
this.smtpHost = meta.smtpHost;
|
||||
this.smtpPort = meta.smtpPort;
|
||||
this.smtpUser = meta.smtpUser;
|
||||
this.smtpPass = meta.smtpPass;
|
||||
});
|
||||
},
|
||||
|
||||
@ -221,7 +249,14 @@ export default Vue.extend({
|
||||
enableExternalUserRecommendation: this.enableExternalUserRecommendation,
|
||||
externalUserRecommendationEngine: this.externalUserRecommendationEngine,
|
||||
externalUserRecommendationTimeout: parseInt(this.externalUserRecommendationTimeout, 10),
|
||||
summalyProxy: this.summalyProxy
|
||||
summalyProxy: this.summalyProxy,
|
||||
enableEmail: this.enableEmail,
|
||||
email: this.email,
|
||||
smtpSecure: this.smtpSecure,
|
||||
smtpHost: this.smtpHost,
|
||||
smtpPort: parseInt(this.smtpPort, 10),
|
||||
smtpUser: this.smtpUser,
|
||||
smtpPass: this.smtpPass
|
||||
}).then(() => {
|
||||
this.$root.alert({
|
||||
type: 'success',
|
||||
|
@ -2,7 +2,7 @@ const faces = [
|
||||
'(=^・・^=)',
|
||||
'v(\'ω\')v',
|
||||
'🐡( \'-\' 🐡 )フグパンチ!!!!',
|
||||
'🖕(´・_・`)🖕',
|
||||
'✌️(´・_・`)✌️',
|
||||
'(。>﹏<。)',
|
||||
'(Δ・x・Δ)'
|
||||
];
|
||||
|
@ -78,9 +78,10 @@ export default (opts: Opts = {}) => ({
|
||||
urls(): string[] {
|
||||
if (this.appearNote.text) {
|
||||
const ast = parse(this.appearNote.text);
|
||||
// TODO: 再帰的にURL要素がないか調べる
|
||||
return unique(ast
|
||||
.filter(t => (t.type == 'url' || t.type == 'link') && !t.silent)
|
||||
.map(t => t.url));
|
||||
.filter(t => ((t.name == 'url' || t.name == 'link') && t.props.url && !t.props.silent))
|
||||
.map(t => t.props.url));
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ export default function(me, settings, note) {
|
||||
}
|
||||
}
|
||||
|
||||
if (!isMyNote && note.text && settings.mutedWords.some(q => !q.some(word => !note.text.includes(word)))) {
|
||||
if (!isMyNote && note.text && settings.mutedWords.some(q => q.length > 0 && !q.some(word => !note.text.includes(word)))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -50,15 +50,13 @@
|
||||
</div>
|
||||
|
||||
<div class="player" v-if="game.isEnded">
|
||||
<div>
|
||||
<button @click="logPos = 0" :disabled="logPos == 0"><fa icon="angle-double-left"/></button>
|
||||
<button @click="logPos--" :disabled="logPos == 0"><fa icon="angle-left"/></button>
|
||||
</div>
|
||||
<span>{{ logPos }} / {{ logs.length }}</span>
|
||||
<div>
|
||||
<button @click="logPos++" :disabled="logPos == logs.length"><fa icon="angle-right"/></button>
|
||||
<button @click="logPos = logs.length" :disabled="logPos == logs.length"><fa icon="angle-double-right"/></button>
|
||||
</div>
|
||||
<ui-horizon-group>
|
||||
<ui-button @click="logPos = 0" :disabled="logPos == 0"><fa :icon="faAngleDoubleLeft"/></ui-button>
|
||||
<ui-button @click="logPos--" :disabled="logPos == 0"><fa :icon="faAngleLeft"/></ui-button>
|
||||
<ui-button @click="logPos++" :disabled="logPos == logs.length"><fa :icon="faAngleRight"/></ui-button>
|
||||
<ui-button @click="logPos = logs.length" :disabled="logPos == logs.length"><fa :icon="faAngleDoubleRight"/></ui-button>
|
||||
</ui-horizon-group>
|
||||
</div>
|
||||
|
||||
<div class="info">
|
||||
@ -75,6 +73,7 @@ import i18n from '../../../../../i18n';
|
||||
import * as CRC32 from 'crc-32';
|
||||
import Reversi, { Color } from '../../../../../../../games/reversi/core';
|
||||
import { url } from '../../../../../config';
|
||||
import { faAngleDoubleLeft, faAngleLeft, faAngleRight, faAngleDoubleRight } from '@fortawesome/free-solid-svg-icons';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n('common/views/components/games/reversi/reversi.game.vue'),
|
||||
@ -99,7 +98,8 @@ export default Vue.extend({
|
||||
o: null as Reversi,
|
||||
logs: [],
|
||||
logPos: 0,
|
||||
pollingClock: null
|
||||
pollingClock: null,
|
||||
faAngleDoubleLeft, faAngleLeft, faAngleRight, faAngleDoubleRight
|
||||
};
|
||||
},
|
||||
|
||||
@ -449,7 +449,9 @@ export default Vue.extend({
|
||||
padding-bottom 16px
|
||||
|
||||
> .player
|
||||
padding-bottom 32px
|
||||
padding 0 16px 32px 16px
|
||||
margin 0 auto
|
||||
max-width 500px
|
||||
|
||||
> span
|
||||
display inline-block
|
||||
|
@ -22,7 +22,10 @@ export default Vue.extend({
|
||||
},
|
||||
methods: {
|
||||
search() {
|
||||
window.open(`https://www.google.com/?#q=${this.query}`, '_blank');
|
||||
const engine = this.$store.state.settings.webSearchEngine ||
|
||||
'https://www.google.com/?#q={{query}}';
|
||||
const url = engine.replace('{{query}}', this.query)
|
||||
window.open(url, '_blank');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -180,6 +180,7 @@ export default Vue.extend({
|
||||
padding 8px 16px
|
||||
width 100%
|
||||
color var(--popupFg)
|
||||
white-space nowrap
|
||||
|
||||
&:hover
|
||||
color var(--primaryForeground)
|
||||
|
@ -51,8 +51,8 @@ export default Vue.extend({
|
||||
if (this.message.text) {
|
||||
const ast = parse(this.message.text);
|
||||
return unique(ast
|
||||
.filter(t => (t.type == 'url' || t.type == 'link') && !t.silent)
|
||||
.map(t => t.url));
|
||||
.filter(t => ((t.name == 'url' || t.name == 'link') && t.props.url && !t.silent))
|
||||
.map(t => t.props.url));
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
@ -111,6 +111,14 @@ export default Vue.component('misskey-flavored-markdown', {
|
||||
}, genEl(token.children));
|
||||
}
|
||||
|
||||
case 'center': {
|
||||
return [createElement('div', {
|
||||
attrs: {
|
||||
style: 'text-align:center;'
|
||||
}
|
||||
}, genEl(token.children))];
|
||||
}
|
||||
|
||||
case 'motion': {
|
||||
motionCount++;
|
||||
const isLong = getTextCount(token.children) > 10 || getChildrenCount(token.children) > 5;
|
||||
|
@ -29,7 +29,7 @@ export default Vue.extend({
|
||||
|
||||
>>> .quote
|
||||
margin 8px
|
||||
padding 6px 12px
|
||||
padding 6px 0 6px 12px
|
||||
color var(--mfmQuote)
|
||||
border-left solid 3px var(--mfmQuoteLine)
|
||||
|
||||
@ -38,7 +38,7 @@ export default Vue.extend({
|
||||
margin 0 0.5em
|
||||
font-size 80%
|
||||
color #525252
|
||||
background #f8f8f8
|
||||
background rgba(0, 0, 0, 0.05)
|
||||
border-radius 2px
|
||||
|
||||
>>> pre > code
|
||||
|
@ -72,7 +72,7 @@ export default Vue.extend({
|
||||
|
||||
methods: {
|
||||
save() {
|
||||
this._mutedWords = this.mutedWords.split('\n').map(line => line.split(' '));
|
||||
this._mutedWords = this.mutedWords.split('\n').map(line => line.split(' ').filter(x => x != ''));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -66,6 +66,19 @@
|
||||
<ui-switch v-model="carefulBot" @change="save(false)">{{ $t('careful-bot') }}</ui-switch>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section v-if="enableEmail">
|
||||
<header>{{ $t('email') }}</header>
|
||||
|
||||
<div>
|
||||
<template v-if="$store.state.i.email != null">
|
||||
<ui-info v-if="$store.state.i.emailVerified">{{ $t('email-verified') }}</ui-info>
|
||||
<ui-info v-else warn>{{ $t('email-not-verified') }}</ui-info>
|
||||
</template>
|
||||
<ui-input v-model="email" type="email"><span>{{ $t('email-address') }}</span></ui-input>
|
||||
<ui-button @click="updateEmail()">{{ $t('save') }}</ui-button>
|
||||
</div>
|
||||
</section>
|
||||
</ui-card>
|
||||
</template>
|
||||
|
||||
@ -77,9 +90,12 @@ import { toUnicode } from 'punycode';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n('common/views/components/profile-editor.vue'),
|
||||
|
||||
data() {
|
||||
return {
|
||||
host: toUnicode(host),
|
||||
enableEmail: false,
|
||||
email: null,
|
||||
name: null,
|
||||
username: null,
|
||||
location: null,
|
||||
@ -113,7 +129,11 @@ export default Vue.extend({
|
||||
},
|
||||
|
||||
created() {
|
||||
this.name = this.$store.state.i.name || '';
|
||||
this.$root.getMeta().then(meta => {
|
||||
this.enableEmail = meta.enableEmail;
|
||||
});
|
||||
this.email = this.$store.state.i.email;
|
||||
this.name = this.$store.state.i.name;
|
||||
this.username = this.$store.state.i.username;
|
||||
this.location = this.$store.state.i.profile.location;
|
||||
this.description = this.$store.state.i.description;
|
||||
@ -179,8 +199,8 @@ export default Vue.extend({
|
||||
location: this.location || null,
|
||||
description: this.description || null,
|
||||
birthday: this.birthday || null,
|
||||
avatarId: this.avatarId,
|
||||
bannerId: this.bannerId,
|
||||
avatarId: this.avatarId || undefined,
|
||||
bannerId: this.bannerId || undefined,
|
||||
isCat: !!this.isCat,
|
||||
isBot: !!this.isBot,
|
||||
isLocked: !!this.isLocked,
|
||||
@ -199,6 +219,18 @@ export default Vue.extend({
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
updateEmail() {
|
||||
this.$input({
|
||||
title: this.$t('@.enter-password'),
|
||||
type: 'password'
|
||||
}).then(password => {
|
||||
this.$root.api('i/update_email', {
|
||||
password: password,
|
||||
email: this.email == '' ? null : this.email
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -67,7 +67,8 @@ export default Vue.extend({
|
||||
username: this.username,
|
||||
password: this.password,
|
||||
token: this.user && this.user.twoFactorEnabled ? this.token : undefined
|
||||
}, true).then(() => {
|
||||
}, true).then(res => {
|
||||
localStorage.setItem('i', res.i);
|
||||
location.reload();
|
||||
}).catch(() => {
|
||||
alert(this.$t('login-failed'));
|
||||
|
@ -79,6 +79,10 @@ export default Vue.extend({
|
||||
|
||||
*
|
||||
pointer-events none
|
||||
user-select none
|
||||
|
||||
&:disabled
|
||||
opacity 0.7
|
||||
|
||||
&:focus
|
||||
&:after
|
||||
@ -107,30 +111,30 @@ export default Vue.extend({
|
||||
color var(--text)
|
||||
background var(--buttonBg)
|
||||
|
||||
&:hover
|
||||
&:not(:disabled):hover
|
||||
background var(--buttonHoverBg)
|
||||
|
||||
&:active
|
||||
&:not(:disabled):active
|
||||
background var(--buttonActiveBg)
|
||||
|
||||
&.primary
|
||||
color var(--primaryForeground)
|
||||
background var(--primary)
|
||||
|
||||
&:hover
|
||||
&:not(:disabled):hover
|
||||
background var(--primaryLighten5)
|
||||
|
||||
&:active
|
||||
&:not(:disabled):active
|
||||
background var(--primaryDarken5)
|
||||
|
||||
&:not(.fill)
|
||||
color var(--primary)
|
||||
background none
|
||||
|
||||
&:hover
|
||||
&:not(:disabled):hover
|
||||
color var(--primaryDarken5)
|
||||
|
||||
&:active
|
||||
&:not(:disabled):active
|
||||
background var(--primaryAlpha03)
|
||||
|
||||
</style>
|
||||
|
@ -22,6 +22,7 @@ export default Vue.extend({
|
||||
<style lang="stylus" scoped>
|
||||
.ui-card
|
||||
margin 16px
|
||||
max-width 850px
|
||||
color var(--faceText)
|
||||
background var(--face)
|
||||
border-radius var(--round)
|
||||
|
@ -123,7 +123,7 @@ export default Vue.extend({
|
||||
> span
|
||||
display block
|
||||
line-height 20px
|
||||
color currentColor
|
||||
color var(--text)
|
||||
transition inherit
|
||||
|
||||
> p
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="mk-note-detail" :title="title">
|
||||
<div class="mk-note-detail" :title="title" tabindex="-1">
|
||||
<button
|
||||
class="read-more"
|
||||
v-if="appearNote.reply && appearNote.reply.replyId && conversation.length == 0"
|
||||
@ -63,18 +63,18 @@
|
||||
<footer>
|
||||
<span class="app" v-if="note.app && $store.state.settings.showVia">via <b>{{ note.app.name }}</b></span>
|
||||
<mk-reactions-viewer :note="appearNote"/>
|
||||
<button class="replyButton" @click="reply" :title="$t('reply')">
|
||||
<button class="replyButton" @click="reply()" :title="$t('reply')">
|
||||
<template v-if="appearNote.reply"><fa icon="reply-all"/></template>
|
||||
<template v-else><fa icon="reply"/></template>
|
||||
<p class="count" v-if="appearNote.repliesCount > 0">{{ appearNote.repliesCount }}</p>
|
||||
</button>
|
||||
<button class="renoteButton" @click="renote" :title="$t('renote')">
|
||||
<button class="renoteButton" @click="renote()" :title="$t('renote')">
|
||||
<fa icon="retweet"/><p class="count" v-if="appearNote.renoteCount > 0">{{ appearNote.renoteCount }}</p>
|
||||
</button>
|
||||
<button class="reactionButton" :class="{ reacted: appearNote.myReaction != null }" @click="react" ref="reactButton" :title="$t('add-reaction')">
|
||||
<button class="reactionButton" :class="{ reacted: appearNote.myReaction != null }" @click="react()" ref="reactButton" :title="$t('add-reaction')">
|
||||
<fa icon="plus"/><p class="count" v-if="appearNote.reactions_count > 0">{{ appearNote.reactions_count }}</p>
|
||||
</button>
|
||||
<button @click="menu" ref="menuButton">
|
||||
<button @click="menu()" ref="menuButton">
|
||||
<fa icon="ellipsis-h"/>
|
||||
</button>
|
||||
</footer>
|
||||
@ -88,23 +88,18 @@
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import i18n from '../../../i18n';
|
||||
import parse from '../../../../../mfm/parse';
|
||||
|
||||
import MkPostFormWindow from './post-form-window.vue';
|
||||
import MkRenoteFormWindow from './renote-form-window.vue';
|
||||
import MkNoteMenu from '../../../common/views/components/note-menu.vue';
|
||||
import MkReactionPicker from '../../../common/views/components/reaction-picker.vue';
|
||||
import XSub from './note.sub.vue';
|
||||
import { sum, unique } from '../../../../../prelude/array';
|
||||
import noteSubscriber from '../../../common/scripts/note-subscriber';
|
||||
import noteMixin from '../../../common/scripts/note-mixin';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n('desktop/views/components/note-detail.vue'),
|
||||
|
||||
components: {
|
||||
XSub
|
||||
},
|
||||
|
||||
mixins: [noteSubscriber('note')],
|
||||
mixins: [noteMixin(), noteSubscriber('note')],
|
||||
|
||||
props: {
|
||||
note: {
|
||||
@ -118,47 +113,12 @@ export default Vue.extend({
|
||||
|
||||
data() {
|
||||
return {
|
||||
showContent: false,
|
||||
conversation: [],
|
||||
conversationFetching: false,
|
||||
replies: []
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
isRenote(): boolean {
|
||||
return (this.note.renote &&
|
||||
this.note.text == null &&
|
||||
this.note.fileIds.length == 0 &&
|
||||
this.note.poll == null);
|
||||
},
|
||||
|
||||
appearNote(): any {
|
||||
return this.isRenote ? this.note.renote : this.note;
|
||||
},
|
||||
|
||||
reactionsCount(): number {
|
||||
return this.appearNote.reactionCounts
|
||||
? sum(Object.values(this.appearNote.reactionCounts))
|
||||
: 0;
|
||||
},
|
||||
|
||||
title(): string {
|
||||
return new Date(this.appearNote.createdAt).toLocaleString();
|
||||
},
|
||||
|
||||
urls(): string[] {
|
||||
if (this.appearNote.text) {
|
||||
const ast = parse(this.appearNote.text);
|
||||
return unique(ast
|
||||
.filter(t => (t.type == 'url' || t.type == 'link') && !t.silent)
|
||||
.map(t => t.url));
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
// Get replies
|
||||
if (!this.compact) {
|
||||
@ -169,24 +129,6 @@ export default Vue.extend({
|
||||
this.replies = replies;
|
||||
});
|
||||
}
|
||||
|
||||
// Draw map
|
||||
if (this.appearNote.geo) {
|
||||
const shouldShowMap = this.$store.getters.isSignedIn ? this.$store.state.settings.showMaps : true;
|
||||
if (shouldShowMap) {
|
||||
this.$root.os.getGoogleMaps().then(maps => {
|
||||
const uluru = new maps.LatLng(this.appearNote.geo.coordinates[1], this.appearNote.geo.coordinates[0]);
|
||||
const map = new maps.Map(this.$refs.map, {
|
||||
center: uluru,
|
||||
zoom: 15
|
||||
});
|
||||
new maps.Marker({
|
||||
position: uluru,
|
||||
map: map
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
@ -200,32 +142,6 @@ export default Vue.extend({
|
||||
this.conversationFetching = false;
|
||||
this.conversation = conversation.reverse();
|
||||
});
|
||||
},
|
||||
|
||||
reply() {
|
||||
this.$root.new(MkPostFormWindow, {
|
||||
reply: this.appearNote
|
||||
});
|
||||
},
|
||||
|
||||
renote() {
|
||||
this.$root.new(MkRenoteFormWindow, {
|
||||
note: this.appearNote
|
||||
});
|
||||
},
|
||||
|
||||
react() {
|
||||
this.$root.new(MkReactionPicker, {
|
||||
source: this.$refs.reactButton,
|
||||
note: this.appearNote
|
||||
});
|
||||
},
|
||||
|
||||
menu() {
|
||||
this.$root.new(MkNoteMenu, {
|
||||
source: this.$refs.menuButton,
|
||||
note: this.appearNote
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -91,6 +91,11 @@
|
||||
</ui-select>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<header>{{ $t('web-search-engine') }}</header>
|
||||
<ui-input v-model="webSearchEngine">{{ $t('web-search-engine') }}<span slot="desc">{{ $t('web-search-engine-desc') }}</span></ui-input>
|
||||
</section>
|
||||
</ui-card>
|
||||
|
||||
<ui-card class="web" v-show="page == 'web'">
|
||||
@ -177,6 +182,7 @@
|
||||
</optgroup>
|
||||
</ui-select>
|
||||
<div class="none ui info">
|
||||
<div>Current: <i>{{ this.currentLanguage }}</i></div>
|
||||
<p><fa icon="info-circle"/>{{ $t('language-desc') }}</p>
|
||||
</div>
|
||||
</section>
|
||||
@ -345,6 +351,7 @@ export default Vue.extend({
|
||||
meta: null,
|
||||
version,
|
||||
langs,
|
||||
currentLanguage: 'Unknown',
|
||||
latestVersion: undefined,
|
||||
checkingForUpdate: false
|
||||
};
|
||||
@ -455,6 +462,11 @@ export default Vue.extend({
|
||||
set(value) { this.$store.dispatch('settings/set', { key: 'defaultNoteVisibility', value }); }
|
||||
},
|
||||
|
||||
webSearchEngine: {
|
||||
get() { return this.$store.state.settings.webSearchEngine; },
|
||||
set(value) { this.$store.dispatch('settings/set', { key: 'webSearchEngine', value }); }
|
||||
},
|
||||
|
||||
showReplyTarget: {
|
||||
get() { return this.$store.state.settings.showReplyTarget; },
|
||||
set(value) { this.$store.dispatch('settings/set', { key: 'showReplyTarget', value }); }
|
||||
@ -544,6 +556,12 @@ export default Vue.extend({
|
||||
this.$root.getMeta().then(meta => {
|
||||
this.meta = meta;
|
||||
});
|
||||
|
||||
try {
|
||||
const locale = JSON.parse(localStorage.getItem('locale') || "{}");
|
||||
const localeKey = localStorage.getItem('localeKey');
|
||||
this.currentLanguage = `${locale.meta.lang} (${localeKey})`;
|
||||
} catch { }
|
||||
},
|
||||
methods: {
|
||||
readAllUnreadNotes() {
|
||||
|
@ -54,7 +54,25 @@ export default Vue.extend({
|
||||
padding 16px
|
||||
|
||||
> button
|
||||
display block
|
||||
margin-bottom 16px
|
||||
color var(--primaryForeground)
|
||||
background var(--primary)
|
||||
width 100%
|
||||
border-radius 38px
|
||||
user-select none
|
||||
cursor pointer
|
||||
padding 0 16px
|
||||
min-width 100px
|
||||
line-height 38px
|
||||
font-size 14px
|
||||
font-weight 700
|
||||
|
||||
&:hover
|
||||
background var(--primaryLighten10)
|
||||
|
||||
&:active
|
||||
background var(--primaryDarken10)
|
||||
|
||||
> a
|
||||
display block
|
||||
|
@ -10,7 +10,7 @@
|
||||
<span>{{ name }}</span>
|
||||
</span>
|
||||
|
||||
<div class="editor" style="padding:0 12px" v-if="edit">
|
||||
<div class="editor" style="padding:12px" v-if="edit">
|
||||
<ui-switch v-model="column.isMediaOnly" @change="onChangeSettings">{{ $t('is-media-only') }}</ui-switch>
|
||||
<ui-switch v-model="column.isMediaView" @change="onChangeSettings">{{ $t('is-media-view') }}</ui-switch>
|
||||
</div>
|
||||
|
@ -151,18 +151,20 @@ export default Vue.extend({
|
||||
&:hover
|
||||
color var(--desktopTimelineSrcHover)
|
||||
|
||||
> .empty
|
||||
display block
|
||||
margin 0 auto
|
||||
padding 32px
|
||||
max-width 400px
|
||||
text-align center
|
||||
color #999
|
||||
> .mk-notes
|
||||
|
||||
> [data-icon]
|
||||
> .empty
|
||||
display block
|
||||
margin-bottom 16px
|
||||
font-size 3em
|
||||
color #ccc
|
||||
margin 0 auto
|
||||
padding 32px
|
||||
max-width 400px
|
||||
text-align center
|
||||
color var(--text)
|
||||
|
||||
> [data-icon]
|
||||
display block
|
||||
margin-bottom 16px
|
||||
font-size 3em
|
||||
color var(--faceHeaderText);
|
||||
|
||||
</style>
|
||||
|
@ -371,7 +371,6 @@ export default Vue.extend({
|
||||
> .main
|
||||
grid-row 1
|
||||
grid-column 1 / 3
|
||||
border-top solid 5px var(--primary)
|
||||
|
||||
> div
|
||||
padding 32px
|
||||
|
@ -190,8 +190,8 @@ export default class MiOS extends EventEmitter {
|
||||
this.store.dispatch('mergeMe', freshData);
|
||||
});
|
||||
} else {
|
||||
// Get token from cookie
|
||||
const i = (document.cookie.match(/i=(!\w+)/) || [null, null])[1];
|
||||
// Get token from cookie or localStorage
|
||||
const i = (document.cookie.match(/i=(!\w+)/) || [null, null])[1] || localStorage.getItem('i');
|
||||
|
||||
fetchme(i, me => {
|
||||
if (me) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="mk-note-detail">
|
||||
<div class="mk-note-detail" tabindex="-1">
|
||||
<button
|
||||
class="more"
|
||||
v-if="appearNote.reply && appearNote.reply.replyId && conversation.length == 0"
|
||||
@ -61,18 +61,18 @@
|
||||
</div>
|
||||
<footer>
|
||||
<mk-reactions-viewer :note="appearNote"/>
|
||||
<button @click="reply" :title="$t('title')">
|
||||
<button @click="reply()" :title="$t('title')">
|
||||
<template v-if="appearNote.reply"><fa icon="reply-all"/></template>
|
||||
<template v-else><fa icon="reply"/></template>
|
||||
<p class="count" v-if="appearNote.repliesCount > 0">{{ appearNote.repliesCount }}</p>
|
||||
</button>
|
||||
<button @click="renote" title="Renote">
|
||||
<button @click="renote()" title="Renote">
|
||||
<fa icon="retweet"/><p class="count" v-if="appearNote.renoteCount > 0">{{ appearNote.renoteCount }}</p>
|
||||
</button>
|
||||
<button :class="{ reacted: appearNote.myReaction != null }" @click="react" ref="reactButton" :title="$t('title')">
|
||||
<button :class="{ reacted: appearNote.myReaction != null }" @click="react()" ref="reactButton" :title="$t('title')">
|
||||
<fa icon="plus"/><p class="count" v-if="appearNote.reactions_count > 0">{{ appearNote.reactions_count }}</p>
|
||||
</button>
|
||||
<button @click="menu" ref="menuButton">
|
||||
<button @click="menu()" ref="menuButton">
|
||||
<fa icon="ellipsis-h"/>
|
||||
</button>
|
||||
</footer>
|
||||
@ -86,21 +86,18 @@
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import i18n from '../../../i18n';
|
||||
import parse from '../../../../../mfm/parse';
|
||||
|
||||
import MkNoteMenu from '../../../common/views/components/note-menu.vue';
|
||||
import MkReactionPicker from '../../../common/views/components/reaction-picker.vue';
|
||||
import XSub from './note.sub.vue';
|
||||
import { sum, unique } from '../../../../../prelude/array';
|
||||
import noteSubscriber from '../../../common/scripts/note-subscriber';
|
||||
import noteMixin from '../../../common/scripts/note-mixin';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n('mobile/views/components/note-detail.vue'),
|
||||
|
||||
components: {
|
||||
XSub
|
||||
},
|
||||
|
||||
mixins: [noteSubscriber('note')],
|
||||
mixins: [noteMixin(), noteSubscriber('note')],
|
||||
|
||||
props: {
|
||||
note: {
|
||||
@ -114,43 +111,12 @@ export default Vue.extend({
|
||||
|
||||
data() {
|
||||
return {
|
||||
showContent: false,
|
||||
conversation: [],
|
||||
conversationFetching: false,
|
||||
replies: []
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
isRenote(): boolean {
|
||||
return (this.note.renote &&
|
||||
this.note.text == null &&
|
||||
this.note.fileIds.length == 0 &&
|
||||
this.note.poll == null);
|
||||
},
|
||||
|
||||
appearNote(): any {
|
||||
return this.isRenote ? this.note.renote : this.note;
|
||||
},
|
||||
|
||||
reactionsCount(): number {
|
||||
return this.appearNote.reactionCounts
|
||||
? sum(Object.values(this.appearNote.reactionCounts))
|
||||
: 0;
|
||||
},
|
||||
|
||||
urls(): string[] {
|
||||
if (this.appearNote.text) {
|
||||
const ast = parse(this.appearNote.text);
|
||||
return unique(ast
|
||||
.filter(t => (t.type == 'url' || t.type == 'link') && !t.silent)
|
||||
.map(t => t.url));
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
// Get replies
|
||||
if (!this.compact) {
|
||||
@ -161,24 +127,6 @@ export default Vue.extend({
|
||||
this.replies = replies;
|
||||
});
|
||||
}
|
||||
|
||||
// Draw map
|
||||
if (this.appearNote.geo) {
|
||||
const shouldShowMap = this.$store.getters.isSignedIn ? this.$store.state.settings.showMaps : true;
|
||||
if (shouldShowMap) {
|
||||
this.$root.os.getGoogleMaps().then(maps => {
|
||||
const uluru = new maps.LatLng(this.appearNote.geo.coordinates[1], this.appearNote.geo.coordinates[0]);
|
||||
const map = new maps.Map(this.$refs.map, {
|
||||
center: uluru,
|
||||
zoom: 15
|
||||
});
|
||||
new maps.Marker({
|
||||
position: uluru,
|
||||
map: map
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
@ -192,35 +140,6 @@ export default Vue.extend({
|
||||
this.conversationFetching = false;
|
||||
this.conversation = conversation.reverse();
|
||||
});
|
||||
},
|
||||
|
||||
reply() {
|
||||
this.$post({
|
||||
reply: this.appearNote
|
||||
});
|
||||
},
|
||||
|
||||
renote() {
|
||||
this.$post({
|
||||
renote: this.appearNote
|
||||
});
|
||||
},
|
||||
|
||||
react() {
|
||||
this.$root.new(MkReactionPicker, {
|
||||
source: this.$refs.reactButton,
|
||||
note: this.appearNote,
|
||||
compact: true,
|
||||
big: true
|
||||
});
|
||||
},
|
||||
|
||||
menu() {
|
||||
this.$root.new(MkNoteMenu, {
|
||||
source: this.$refs.menuButton,
|
||||
note: this.appearNote,
|
||||
compact: true
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -86,6 +86,11 @@
|
||||
</ui-select>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<header>{{ $t('web-search-engine') }}</header>
|
||||
<ui-input v-model="webSearchEngine">{{ $t('web-search-engine') }}<span slot="desc">{{ $t('web-search-engine-desc') }}</span></ui-input>
|
||||
</section>
|
||||
</ui-card>
|
||||
|
||||
<x-drive-settings/>
|
||||
@ -113,7 +118,8 @@
|
||||
<option v-for="x in langs" :value="x[0]" :key="x[0]">{{ x[1] }}</option>
|
||||
</optgroup>
|
||||
</ui-select>
|
||||
<span><fa icon="info-circle"/> {{ $t('lang-tip') }}</span>
|
||||
<div>Current: <i>{{ this.currentLanguage }}</i></div>
|
||||
<p><fa icon="info-circle"/> {{ $t('lang-tip') }}</p>
|
||||
</section>
|
||||
</ui-card>
|
||||
|
||||
@ -220,6 +226,7 @@ export default Vue.extend({
|
||||
version,
|
||||
codename,
|
||||
langs,
|
||||
currentLanguage: 'Unknown',
|
||||
latestVersion: undefined,
|
||||
checkingForUpdate: false
|
||||
};
|
||||
@ -365,6 +372,19 @@ export default Vue.extend({
|
||||
get() { return this.$store.state.settings.defaultNoteVisibility; },
|
||||
set(value) { this.$store.dispatch('settings/set', { key: 'defaultNoteVisibility', value }); }
|
||||
},
|
||||
|
||||
webSearchEngine: {
|
||||
get() { return this.$store.state.settings.webSearchEngine; },
|
||||
set(value) { this.$store.dispatch('settings/set', { key: 'webSearchEngine', value }); }
|
||||
},
|
||||
},
|
||||
|
||||
created() {
|
||||
try {
|
||||
const locale = JSON.parse(localStorage.getItem('locale') || "{}");
|
||||
const localeKey = localStorage.getItem('localeKey');
|
||||
this.currentLanguage = `${locale.meta.lang} (${localeKey})`;
|
||||
} catch { }
|
||||
},
|
||||
|
||||
mounted() {
|
||||
|
@ -334,6 +334,7 @@ main
|
||||
max-width 680px
|
||||
margin 0 auto
|
||||
padding 8px
|
||||
color var(--text)
|
||||
|
||||
@media (min-width 500px)
|
||||
padding 16px
|
||||
|
@ -306,6 +306,7 @@ export default Vue.extend({
|
||||
padding 16px 0
|
||||
border solid 2px rgba(0, 0, 0, 0.1)
|
||||
border-radius 8px
|
||||
color var(--text)
|
||||
|
||||
> *
|
||||
margin 0 16px
|
||||
|
@ -11,11 +11,3 @@ if (!('fetch' in window)) {
|
||||
'Your browser (or your OS) seems outdated. ' +
|
||||
'To run Misskey, please update your browser to latest version or try other browsers.');
|
||||
}
|
||||
|
||||
// Check whether cookie enabled
|
||||
if (!navigator.cookieEnabled) {
|
||||
alert(
|
||||
'Misskeyを利用するにはCookieを有効にしてください。' +
|
||||
'\n\n' +
|
||||
'To use Misskey, please enable Cookie.');
|
||||
}
|
||||
|
@ -35,6 +35,7 @@ const defaultSettings = {
|
||||
iLikeSushi: false,
|
||||
rememberNoteVisibility: false,
|
||||
defaultNoteVisibility: 'public',
|
||||
webSearchEngine: 'https://www.google.com/?#q={{query}}',
|
||||
mutedWords: [],
|
||||
games: {
|
||||
reversi: {
|
||||
@ -129,6 +130,7 @@ export default (os: MiOS) => new Vuex.Store({
|
||||
logout(ctx) {
|
||||
ctx.commit('updateI', null);
|
||||
document.cookie = `i=; domain=${hostname}; expires=Thu, 01 Jan 1970 00:00:01 GMT;`;
|
||||
localStorage.removeItem('i');
|
||||
},
|
||||
|
||||
mergeMe(ctx, me) {
|
||||
|
@ -4,6 +4,7 @@
|
||||
"start_url": "/",
|
||||
"display": "standalone",
|
||||
"background_color": "#313a42",
|
||||
"theme_color": "#fb4e4e",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/assets/icons/16.png",
|
||||
@ -34,6 +35,11 @@
|
||||
"src": "/assets/icons/256.png",
|
||||
"sizes": "256x256",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/assets/icons/512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"share_target": {
|
||||
|
@ -45,6 +45,12 @@ export default (tokens: Node[], mentionedRemoteUsers: INote['mentionedRemoteUser
|
||||
return pre;
|
||||
},
|
||||
|
||||
center(token) {
|
||||
const el = doc.createElement('div');
|
||||
dive(token.children).forEach(child => el.appendChild(child));
|
||||
return el;
|
||||
},
|
||||
|
||||
emoji(token) {
|
||||
return doc.createTextNode(token.props.emoji ? token.props.emoji : `:${token.props.name}:`);
|
||||
},
|
||||
|
@ -41,11 +41,12 @@ export default (source: string): Node[] => {
|
||||
}
|
||||
|
||||
function isBlockNode(node: Node): boolean {
|
||||
return ['blockCode', 'quote', 'title'].includes(node.name);
|
||||
return ['blockCode', 'center', 'quote', 'title'].includes(node.name);
|
||||
}
|
||||
|
||||
/**
|
||||
* ブロック要素の前後にある改行を削除します(ブロック要素自体が改行の役割も果たすため、余計に改行されてしまうため)
|
||||
* ブロック要素の前後にある改行を削除します
|
||||
* (ブロック要素自体が改行の役割を果たすため、余計に改行されてしまう)
|
||||
* @param nodes
|
||||
*/
|
||||
const removeNeedlessLineBreaks = (nodes: Node[]) => {
|
||||
|
@ -29,6 +29,32 @@ function makeNodeWithChildren(name: string, children: Node[], props?: any): Node
|
||||
return _makeNode(name, children, props);
|
||||
}
|
||||
|
||||
function getTrailingPosition(x: string): number {
|
||||
const brackets = [
|
||||
['(', ')'],
|
||||
['「', '」'],
|
||||
];
|
||||
const pendingBrackets = [] as any;
|
||||
const end = x.split('').findIndex(char => {
|
||||
const closeMatch = brackets.map(x => x[1]).indexOf(char);
|
||||
const openMatch = brackets.map(x => x[0]).indexOf(char);
|
||||
if (closeMatch != -1) {
|
||||
if (pendingBrackets[closeMatch] > 0) {
|
||||
pendingBrackets[closeMatch]--;
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
} else if (openMatch != -1) {
|
||||
pendingBrackets[openMatch] = (pendingBrackets[openMatch] || 0) + 1;
|
||||
return false;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
return end > 0 ? end : x.length;
|
||||
}
|
||||
|
||||
const newline = P((input, i) => {
|
||||
if (i == 0 || input[i] == '\n' || input[i - 1] == '\n') {
|
||||
return P.makeSuccess(i, null);
|
||||
@ -53,6 +79,7 @@ const mfm = P.createLanguage({
|
||||
r.math,
|
||||
r.search,
|
||||
r.title,
|
||||
r.center,
|
||||
r.text
|
||||
).atLeast(1),
|
||||
|
||||
@ -65,6 +92,7 @@ const mfm = P.createLanguage({
|
||||
r.mention,
|
||||
r.hashtag,
|
||||
r.emoji,
|
||||
r.math,
|
||||
r.text
|
||||
).atLeast(1).tryParse(x))),
|
||||
//#endregion
|
||||
@ -87,11 +115,30 @@ const mfm = P.createLanguage({
|
||||
.map(x => makeNodeWithChildren('bold', P.alt(
|
||||
r.mention,
|
||||
r.hashtag,
|
||||
r.url,
|
||||
r.link,
|
||||
r.emoji,
|
||||
r.text
|
||||
).atLeast(1).tryParse(x))),
|
||||
//#endregion
|
||||
|
||||
//#region Center
|
||||
center: r =>
|
||||
P.regexp(/<center>([\s\S]+?)<\/center>/, 1)
|
||||
.map(x => makeNodeWithChildren('center', P.alt(
|
||||
r.big,
|
||||
r.bold,
|
||||
r.motion,
|
||||
r.mention,
|
||||
r.hashtag,
|
||||
r.emoji,
|
||||
r.math,
|
||||
r.url,
|
||||
r.link,
|
||||
r.text
|
||||
).atLeast(1).tryParse(x))),
|
||||
//#endregion
|
||||
|
||||
//#region Emoji
|
||||
emoji: r =>
|
||||
P.alt(
|
||||
@ -112,9 +159,11 @@ const mfm = P.createLanguage({
|
||||
const text = input.substr(i);
|
||||
const match = text.match(/^#([^\s\.,!\?#]+)/i);
|
||||
if (!match) return P.makeFailure(i, 'not a hashtag');
|
||||
if (match[1].match(/[0-9]+/)) return P.makeFailure(i, 'not a hashtag');
|
||||
if (input[i - 1] != '\n' && input[i - 1] != ' ' && input[i - 1] != null) return P.makeFailure(i, 'require space before "#"');
|
||||
return P.makeSuccess(i + match[0].length, makeNode('hashtag', { hashtag: match[1] }));
|
||||
let hashtag = match[1];
|
||||
hashtag = hashtag.substr(0, getTrailingPosition(hashtag));
|
||||
if (hashtag.match(/^[0-9]+$/)) return P.makeFailure(i, 'not a hashtag');
|
||||
if (!['\n', ' ', ' ', '(', '「', null, undefined].includes(input[i - 1])) return P.makeFailure(i, 'require space before "#"');
|
||||
return P.makeSuccess(i + ('#' + hashtag).length, makeNode('hashtag', { hashtag: hashtag }));
|
||||
}),
|
||||
//#endregion
|
||||
|
||||
@ -181,6 +230,9 @@ const mfm = P.createLanguage({
|
||||
r.mention,
|
||||
r.hashtag,
|
||||
r.emoji,
|
||||
r.url,
|
||||
r.link,
|
||||
r.math,
|
||||
r.text
|
||||
).atLeast(1).tryParse(x))),
|
||||
//#endregion
|
||||
@ -246,23 +298,7 @@ const mfm = P.createLanguage({
|
||||
const match = text.match(/^https?:\/\/[\w\/:%#@\$&\?!\(\)\[\]~\.,=\+\-]+/);
|
||||
if (!match) return P.makeFailure(i, 'not a url');
|
||||
let url = match[0];
|
||||
let pendingBracket = 0;
|
||||
const end = url.split('').findIndex(char => {
|
||||
if (char == ')') {
|
||||
if (pendingBracket > 0) {
|
||||
pendingBracket--;
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
} else if (char == '(') {
|
||||
pendingBracket++;
|
||||
return false;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
if (end > 0) url = url.substr(0, end);
|
||||
url = url.substr(0, getTrailingPosition(url));
|
||||
if (url.endsWith('.')) url = url.substr(0, url.lastIndexOf('.'));
|
||||
if (url.endsWith(',')) url = url.substr(0, url.lastIndexOf(','));
|
||||
return P.makeSuccess(i + url.length, url);
|
||||
|
@ -6,15 +6,24 @@ export default function(file: IDriveFile, thumbnail = false): string {
|
||||
|
||||
if (file.metadata.withoutChunks) {
|
||||
if (thumbnail) {
|
||||
return file.metadata.thumbnailUrl || file.metadata.url;
|
||||
return file.metadata.thumbnailUrl || file.metadata.webpublicUrl || file.metadata.url;
|
||||
} else {
|
||||
return file.metadata.url;
|
||||
return file.metadata.webpublicUrl || file.metadata.url;
|
||||
}
|
||||
} else {
|
||||
if (thumbnail) {
|
||||
return `${config.drive_url}/${file._id}?thumbnail`;
|
||||
} else {
|
||||
return `${config.drive_url}/${file._id}`;
|
||||
return `${config.drive_url}/${file._id}?web`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function getOriginalUrl(file: IDriveFile) {
|
||||
if (file.metadata && file.metadata.url) {
|
||||
return file.metadata.url;
|
||||
}
|
||||
|
||||
const accessKey = file.metadata ? file.metadata.accessKey : null;
|
||||
return `${config.drive_url}/${file._id}${accessKey ? '?original=' + accessKey : ''}`;
|
||||
}
|
||||
|
29
src/models/drive-file-webpublic.ts
Normal file
29
src/models/drive-file-webpublic.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import * as mongo from 'mongodb';
|
||||
import monkDb, { nativeDbConn } from '../db/mongodb';
|
||||
|
||||
const DriveFileWebpublic = monkDb.get<IDriveFileWebpublic>('driveFileWebpublics.files');
|
||||
DriveFileWebpublic.createIndex('metadata.originalId', { sparse: true, unique: true });
|
||||
export default DriveFileWebpublic;
|
||||
|
||||
export const DriveFileWebpublicChunk = monkDb.get('driveFileWebpublics.chunks');
|
||||
|
||||
export const getDriveFileWebpublicBucket = async (): Promise<mongo.GridFSBucket> => {
|
||||
const db = await nativeDbConn();
|
||||
const bucket = new mongo.GridFSBucket(db, {
|
||||
bucketName: 'driveFileWebpublics'
|
||||
});
|
||||
return bucket;
|
||||
};
|
||||
|
||||
export type IMetadata = {
|
||||
originalId: mongo.ObjectID;
|
||||
};
|
||||
|
||||
export type IDriveFileWebpublic = {
|
||||
_id: mongo.ObjectID;
|
||||
uploadDate: Date;
|
||||
md5: string;
|
||||
filename: string;
|
||||
contentType: string;
|
||||
metadata: IMetadata;
|
||||
};
|
@ -3,7 +3,7 @@ const deepcopy = require('deepcopy');
|
||||
import { pack as packFolder } from './drive-folder';
|
||||
import monkDb, { nativeDbConn } from '../db/mongodb';
|
||||
import isObjectId from '../misc/is-objectid';
|
||||
import getDriveFileUrl from '../misc/get-drive-file-url';
|
||||
import getDriveFileUrl, { getOriginalUrl } from '../misc/get-drive-file-url';
|
||||
|
||||
const DriveFile = monkDb.get<IDriveFile>('driveFiles.files');
|
||||
DriveFile.createIndex('md5');
|
||||
@ -28,21 +28,48 @@ export type IMetadata = {
|
||||
_user: any;
|
||||
folderId: mongo.ObjectID;
|
||||
comment: string;
|
||||
|
||||
/**
|
||||
* リモートインスタンスから取得した場合の元URL
|
||||
*/
|
||||
uri?: string;
|
||||
|
||||
/**
|
||||
* URL for web(生成されている場合) or original
|
||||
* * オブジェクトストレージを利用している or リモートサーバーへの直リンクである 場合のみ
|
||||
*/
|
||||
url?: string;
|
||||
|
||||
/**
|
||||
* URL for thumbnail (thumbnailがなければなし)
|
||||
* * オブジェクトストレージを利用している or リモートサーバーへの直リンクである 場合のみ
|
||||
*/
|
||||
thumbnailUrl?: string;
|
||||
|
||||
/**
|
||||
* URL for original (web用が生成されてない場合はurlがoriginalを指す)
|
||||
* * オブジェクトストレージを利用している or リモートサーバーへの直リンクである 場合のみ
|
||||
*/
|
||||
webpublicUrl?: string;
|
||||
|
||||
accessKey?: string;
|
||||
|
||||
src?: string;
|
||||
deletedAt?: Date;
|
||||
|
||||
/**
|
||||
* このファイルの中身データがMongoDB内に保存されているのか否か
|
||||
* このファイルの中身データがMongoDB内に保存されていないか否か
|
||||
* オブジェクトストレージを利用している or リモートサーバーへの直リンクである
|
||||
* な場合は false になります
|
||||
* な場合は true になります
|
||||
*/
|
||||
withoutChunks?: boolean;
|
||||
|
||||
storage?: string;
|
||||
storageProps?: any;
|
||||
|
||||
/***
|
||||
* ObjectStorage の格納先の情報
|
||||
*/
|
||||
storageProps?: IStorageProps;
|
||||
isSensitive?: boolean;
|
||||
|
||||
/**
|
||||
@ -56,6 +83,25 @@ export type IMetadata = {
|
||||
isRemote?: boolean;
|
||||
};
|
||||
|
||||
export type IStorageProps = {
|
||||
/**
|
||||
* ObjectStorage key for original
|
||||
*/
|
||||
key: string;
|
||||
|
||||
/***
|
||||
* ObjectStorage key for thumbnail (thumbnailがなければなし)
|
||||
*/
|
||||
thumbnailKey?: string;
|
||||
|
||||
/***
|
||||
* ObjectStorage key for webpublic (webpublicがなければなし)
|
||||
*/
|
||||
webpublicKey?: string;
|
||||
|
||||
id?: string;
|
||||
};
|
||||
|
||||
export type IDriveFile = {
|
||||
_id: mongo.ObjectID;
|
||||
uploadDate: Date;
|
||||
@ -83,7 +129,8 @@ export function validateFileName(name: string): boolean {
|
||||
export const packMany = (
|
||||
files: any[],
|
||||
options?: {
|
||||
detail: boolean
|
||||
detail?: boolean
|
||||
self?: boolean,
|
||||
}
|
||||
) => {
|
||||
return Promise.all(files.map(f => pack(f, options)));
|
||||
@ -95,11 +142,13 @@ export const packMany = (
|
||||
export const pack = (
|
||||
file: any,
|
||||
options?: {
|
||||
detail: boolean
|
||||
detail?: boolean,
|
||||
self?: boolean,
|
||||
}
|
||||
) => new Promise<any>(async (resolve, reject) => {
|
||||
const opts = Object.assign({
|
||||
detail: false
|
||||
detail: false,
|
||||
self: false
|
||||
}, options);
|
||||
|
||||
let _file: any;
|
||||
@ -165,5 +214,9 @@ export const pack = (
|
||||
delete _target.isRemote;
|
||||
delete _target._user;
|
||||
|
||||
if (opts.self) {
|
||||
_target.url = getOriginalUrl(_file);
|
||||
}
|
||||
|
||||
resolve(_target);
|
||||
});
|
||||
|
@ -214,4 +214,12 @@ export type IMeta = {
|
||||
enableExternalUserRecommendation?: boolean;
|
||||
externalUserRecommendationEngine?: string;
|
||||
externalUserRecommendationTimeout?: number;
|
||||
|
||||
enableEmail?: boolean;
|
||||
email?: string;
|
||||
smtpSecure?: boolean;
|
||||
smtpHost?: string;
|
||||
smtpPort?: number;
|
||||
smtpUser?: string;
|
||||
smtpPass?: string;
|
||||
};
|
||||
|
@ -38,6 +38,8 @@ type IUserBase = {
|
||||
bannerId: mongo.ObjectID;
|
||||
avatarUrl?: string;
|
||||
bannerUrl?: string;
|
||||
avatarColor?: any;
|
||||
bannerColor?: any;
|
||||
wallpaperId: mongo.ObjectID;
|
||||
wallpaperUrl?: string;
|
||||
data: any;
|
||||
@ -76,6 +78,8 @@ export interface ILocalUser extends IUserBase {
|
||||
host: null;
|
||||
keypair: string;
|
||||
email: string;
|
||||
emailVerified?: boolean;
|
||||
emailVerifyCode?: string;
|
||||
password: string;
|
||||
token: string;
|
||||
twitter: {
|
||||
@ -97,9 +101,6 @@ export interface ILocalUser extends IUserBase {
|
||||
username: string;
|
||||
discriminator: string;
|
||||
};
|
||||
line: {
|
||||
userId: string;
|
||||
};
|
||||
profile: {
|
||||
location: string;
|
||||
birthday: string; // 'YYYY-MM-DD'
|
||||
@ -284,6 +285,7 @@ export const pack = (
|
||||
delete _user._id;
|
||||
|
||||
delete _user.usernameLower;
|
||||
delete _user.emailVerifyCode;
|
||||
|
||||
if (_user.host == null) {
|
||||
// Remove private properties
|
||||
@ -304,11 +306,11 @@ export const pack = (
|
||||
delete _user.discord.refreshToken;
|
||||
delete _user.discord.expiresDate;
|
||||
}
|
||||
delete _user.line;
|
||||
|
||||
// Visible via only the official client
|
||||
if (!opts.includeSecrets) {
|
||||
delete _user.email;
|
||||
delete _user.emailVerified;
|
||||
delete _user.settings;
|
||||
delete _user.clientSettings;
|
||||
}
|
||||
|
@ -212,13 +212,17 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise<IU
|
||||
const bannerId = banner ? banner._id : null;
|
||||
const avatarUrl = getDriveFileUrl(avatar, true);
|
||||
const bannerUrl = getDriveFileUrl(banner, false);
|
||||
const avatarColor = avatar && avatar.metadata.properties.avgColor ? avatar.metadata.properties.avgColor : null;
|
||||
const bannerColor = banner && avatar.metadata.properties.avgColor ? banner.metadata.properties.avgColor : null;
|
||||
|
||||
await User.update({ _id: user._id }, {
|
||||
$set: {
|
||||
avatarId,
|
||||
bannerId,
|
||||
avatarUrl,
|
||||
bannerUrl
|
||||
bannerUrl,
|
||||
avatarColor,
|
||||
bannerColor
|
||||
}
|
||||
});
|
||||
|
||||
@ -226,6 +230,8 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise<IU
|
||||
user.bannerId = bannerId;
|
||||
user.avatarUrl = avatarUrl;
|
||||
user.bannerUrl = bannerUrl;
|
||||
user.avatarColor = avatarColor;
|
||||
user.bannerColor = bannerColor;
|
||||
//#endregion
|
||||
|
||||
await updateFeatured(user._id).catch(err => console.log(err));
|
||||
@ -306,6 +312,8 @@ export async function updatePerson(uri: string, resolver?: Resolver, hint?: obje
|
||||
bannerId: banner ? banner._id : null,
|
||||
avatarUrl: getDriveFileUrl(avatar, true),
|
||||
bannerUrl: getDriveFileUrl(banner, false),
|
||||
avatarColor: avatar && avatar.metadata.properties.avgColor ? avatar.metadata.properties.avgColor : null,
|
||||
bannerColor: banner && banner.metadata.properties.avgColor ? banner.metadata.properties.avgColor : null,
|
||||
description: htmlToMFM(person.summary),
|
||||
followersCount,
|
||||
followingCount,
|
||||
|
@ -4,21 +4,24 @@ import config from '../../../config';
|
||||
import { ILocalUser } from '../../../models/user';
|
||||
|
||||
export default function(ctx: Koa.Context, user: ILocalUser, redirect = false) {
|
||||
const expires = 1000 * 60 * 60 * 24 * 365; // One Year
|
||||
ctx.cookies.set('i', user.token, {
|
||||
path: '/',
|
||||
domain: config.hostname,
|
||||
// SEE: https://github.com/koajs/koa/issues/974
|
||||
// When using a SSL proxy it should be configured to add the "X-Forwarded-Proto: https" header
|
||||
secure: config.url.startsWith('https'),
|
||||
httpOnly: false,
|
||||
expires: new Date(Date.now() + expires),
|
||||
maxAge: expires
|
||||
});
|
||||
|
||||
if (redirect) {
|
||||
//#region Cookie
|
||||
const expires = 1000 * 60 * 60 * 24 * 365; // One Year
|
||||
ctx.cookies.set('i', user.token, {
|
||||
path: '/',
|
||||
domain: config.hostname,
|
||||
// SEE: https://github.com/koajs/koa/issues/974
|
||||
// When using a SSL proxy it should be configured to add the "X-Forwarded-Proto: https" header
|
||||
secure: config.url.startsWith('https'),
|
||||
httpOnly: false,
|
||||
expires: new Date(Date.now() + expires),
|
||||
maxAge: expires
|
||||
});
|
||||
//#endregion
|
||||
|
||||
ctx.redirect(config.url);
|
||||
} else {
|
||||
ctx.status = 204;
|
||||
ctx.body = { i: user.token };
|
||||
ctx.status = 200;
|
||||
}
|
||||
}
|
||||
|
@ -228,7 +228,56 @@ export const meta = {
|
||||
desc: {
|
||||
'ja-JP': '外部ユーザーレコメンデーションのタイムアウト (ミリ秒)'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
enableEmail: {
|
||||
validator: $.bool.optional,
|
||||
desc: {
|
||||
'ja-JP': 'メール配信を有効にするか否か'
|
||||
}
|
||||
},
|
||||
|
||||
email: {
|
||||
validator: $.str.optional.nullable,
|
||||
desc: {
|
||||
'ja-JP': 'メール配信する際に利用するメールアドレス'
|
||||
}
|
||||
},
|
||||
|
||||
smtpSecure: {
|
||||
validator: $.bool.optional,
|
||||
desc: {
|
||||
'ja-JP': 'SMTPサーバがSSLを使用しているか否か'
|
||||
}
|
||||
},
|
||||
|
||||
smtpHost: {
|
||||
validator: $.str.optional.nullable,
|
||||
desc: {
|
||||
'ja-JP': 'SMTPサーバのホスト'
|
||||
}
|
||||
},
|
||||
|
||||
smtpPort: {
|
||||
validator: $.num.optional.nullable,
|
||||
desc: {
|
||||
'ja-JP': 'SMTPサーバのポート'
|
||||
}
|
||||
},
|
||||
|
||||
smtpUser: {
|
||||
validator: $.str.optional.nullable,
|
||||
desc: {
|
||||
'ja-JP': 'SMTPサーバのユーザー名'
|
||||
}
|
||||
},
|
||||
|
||||
smtpPass: {
|
||||
validator: $.str.optional.nullable,
|
||||
desc: {
|
||||
'ja-JP': 'SMTPサーバのパスワード'
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
@ -359,6 +408,34 @@ export default define(meta, (ps) => new Promise(async (res, rej) => {
|
||||
set.externalUserRecommendationTimeout = ps.externalUserRecommendationTimeout;
|
||||
}
|
||||
|
||||
if (ps.enableEmail !== undefined) {
|
||||
set.enableEmail = ps.enableEmail;
|
||||
}
|
||||
|
||||
if (ps.email !== undefined) {
|
||||
set.email = ps.email;
|
||||
}
|
||||
|
||||
if (ps.smtpSecure !== undefined) {
|
||||
set.smtpSecure = ps.smtpSecure;
|
||||
}
|
||||
|
||||
if (ps.smtpHost !== undefined) {
|
||||
set.smtpHost = ps.smtpHost;
|
||||
}
|
||||
|
||||
if (ps.smtpPort !== undefined) {
|
||||
set.smtpPort = ps.smtpPort;
|
||||
}
|
||||
|
||||
if (ps.smtpUser !== undefined) {
|
||||
set.smtpUser = ps.smtpUser;
|
||||
}
|
||||
|
||||
if (ps.smtpPass !== undefined) {
|
||||
set.smtpPass = ps.smtpPass;
|
||||
}
|
||||
|
||||
await Meta.update({}, {
|
||||
$set: set
|
||||
}, { upsert: true });
|
||||
|
@ -3,6 +3,8 @@ import define from '../../define';
|
||||
import driveChart from '../../../../chart/drive';
|
||||
|
||||
export const meta = {
|
||||
stability: 'stable',
|
||||
|
||||
desc: {
|
||||
'ja-JP': 'ドライブのチャートを取得します。'
|
||||
},
|
||||
|
@ -3,6 +3,8 @@ import define from '../../define';
|
||||
import federationChart from '../../../../chart/federation';
|
||||
|
||||
export const meta = {
|
||||
stability: 'stable',
|
||||
|
||||
desc: {
|
||||
'ja-JP': 'フェデレーションのチャートを取得します。'
|
||||
},
|
||||
|
@ -3,6 +3,8 @@ import define from '../../define';
|
||||
import hashtagChart from '../../../../chart/hashtag';
|
||||
|
||||
export const meta = {
|
||||
stability: 'stable',
|
||||
|
||||
desc: {
|
||||
'ja-JP': 'ハッシュタグごとのチャートを取得します。'
|
||||
},
|
||||
|
@ -3,6 +3,8 @@ import define from '../../define';
|
||||
import networkChart from '../../../../chart/network';
|
||||
|
||||
export const meta = {
|
||||
stability: 'stable',
|
||||
|
||||
desc: {
|
||||
'ja-JP': 'ネットワークのチャートを取得します。'
|
||||
},
|
||||
|
@ -3,6 +3,8 @@ import define from '../../define';
|
||||
import notesChart from '../../../../chart/notes';
|
||||
|
||||
export const meta = {
|
||||
stability: 'stable',
|
||||
|
||||
desc: {
|
||||
'ja-JP': '投稿のチャートを取得します。'
|
||||
},
|
||||
|
@ -4,6 +4,8 @@ import perUserDriveChart from '../../../../../chart/per-user-drive';
|
||||
import ID, { transform } from '../../../../../misc/cafy-id';
|
||||
|
||||
export const meta = {
|
||||
stability: 'stable',
|
||||
|
||||
desc: {
|
||||
'ja-JP': 'ユーザーごとのドライブのチャートを取得します。'
|
||||
},
|
||||
|
@ -4,6 +4,8 @@ import perUserFollowingChart from '../../../../../chart/per-user-following';
|
||||
import ID, { transform } from '../../../../../misc/cafy-id';
|
||||
|
||||
export const meta = {
|
||||
stability: 'stable',
|
||||
|
||||
desc: {
|
||||
'ja-JP': 'ユーザーごとのフォロー/フォロワーのチャートを取得します。'
|
||||
},
|
||||
|
@ -4,6 +4,8 @@ import perUserNotesChart from '../../../../../chart/per-user-notes';
|
||||
import ID, { transform } from '../../../../../misc/cafy-id';
|
||||
|
||||
export const meta = {
|
||||
stability: 'stable',
|
||||
|
||||
desc: {
|
||||
'ja-JP': 'ユーザーごとの投稿のチャートを取得します。'
|
||||
},
|
||||
|
@ -4,6 +4,8 @@ import perUserReactionsChart from '../../../../../chart/per-user-reactions';
|
||||
import ID, { transform } from '../../../../../misc/cafy-id';
|
||||
|
||||
export const meta = {
|
||||
stability: 'stable',
|
||||
|
||||
desc: {
|
||||
'ja-JP': 'ユーザーごとの被リアクション数のチャートを取得します。'
|
||||
},
|
||||
|
@ -3,6 +3,8 @@ import define from '../../define';
|
||||
import usersChart from '../../../../chart/users';
|
||||
|
||||
export const meta = {
|
||||
stability: 'stable',
|
||||
|
||||
desc: {
|
||||
'ja-JP': 'ユーザーのチャートを取得します。'
|
||||
},
|
||||
|
@ -77,5 +77,5 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => {
|
||||
sort: sort
|
||||
});
|
||||
|
||||
res(await packMany(files));
|
||||
res(await packMany(files, { detail: false, self: true }));
|
||||
}));
|
||||
|
@ -32,6 +32,6 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => {
|
||||
if (file === null) {
|
||||
res({ file: null });
|
||||
} else {
|
||||
res({ file: await pack(file) });
|
||||
res({ file: await pack(file, { self: true }) });
|
||||
}
|
||||
}));
|
||||
|
@ -74,7 +74,7 @@ export default define(meta, (ps, user, app, file, cleanup) => new Promise(async
|
||||
|
||||
cleanup();
|
||||
|
||||
res(pack(driveFile));
|
||||
res(pack(driveFile, { self: true }));
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
|
||||
|
@ -31,5 +31,5 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => {
|
||||
'metadata.folderId': ps.folderId
|
||||
});
|
||||
|
||||
res(await Promise.all(files.map(file => pack(file))));
|
||||
res(await Promise.all(files.map(file => pack(file, { self: true }))));
|
||||
}));
|
||||
|
@ -41,7 +41,8 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => {
|
||||
|
||||
// Serialize
|
||||
const _file = await pack(file, {
|
||||
detail: true
|
||||
detail: true,
|
||||
self: true
|
||||
});
|
||||
|
||||
res(_file);
|
||||
|
@ -111,7 +111,7 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => {
|
||||
});
|
||||
|
||||
// Serialize
|
||||
const fileObj = await pack(file);
|
||||
const fileObj = await pack(file, { self: true });
|
||||
|
||||
// Response
|
||||
res(fileObj);
|
||||
|
@ -50,5 +50,5 @@ export const meta = {
|
||||
};
|
||||
|
||||
export default define(meta, (ps, user) => new Promise(async (res, rej) => {
|
||||
res(pack(await uploadFromUrl(ps.url, user, ps.folderId, null, ps.isSensitive, ps.force)));
|
||||
res(pack(await uploadFromUrl(ps.url, user, ps.folderId, null, ps.isSensitive, ps.force), { self: true }));
|
||||
}));
|
||||
|
@ -65,5 +65,5 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => {
|
||||
sort: sort
|
||||
});
|
||||
|
||||
res(await packMany(files));
|
||||
res(await packMany(files, { self: true }));
|
||||
}));
|
||||
|
@ -1,4 +1,4 @@
|
||||
import User, { pack } from '../../../models/user';
|
||||
import { pack } from '../../../models/user';
|
||||
import define from '../define';
|
||||
|
||||
export const meta = {
|
||||
|
100
src/server/api/endpoints/i/update_email.ts
Normal file
100
src/server/api/endpoints/i/update_email.ts
Normal file
@ -0,0 +1,100 @@
|
||||
import $ from 'cafy';
|
||||
import User, { pack } from '../../../../models/user';
|
||||
import { publishMainStream } from '../../../../stream';
|
||||
import define from '../../define';
|
||||
import * as nodemailer from 'nodemailer';
|
||||
import fetchMeta from '../../../../misc/fetch-meta';
|
||||
import rndstr from 'rndstr';
|
||||
import config from '../../../../config';
|
||||
const ms = require('ms');
|
||||
import * as bcrypt from 'bcryptjs';
|
||||
|
||||
export const meta = {
|
||||
requireCredential: true,
|
||||
|
||||
secure: true,
|
||||
|
||||
limit: {
|
||||
duration: ms('1hour'),
|
||||
max: 3
|
||||
},
|
||||
|
||||
params: {
|
||||
password: {
|
||||
validator: $.str
|
||||
},
|
||||
|
||||
email: {
|
||||
validator: $.str.optional.nullable
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
export default define(meta, (ps, user) => new Promise(async (res, rej) => {
|
||||
// Compare password
|
||||
const same = await bcrypt.compare(ps.password, user.password);
|
||||
|
||||
if (!same) {
|
||||
return rej('incorrect password');
|
||||
}
|
||||
|
||||
await User.update(user._id, {
|
||||
$set: {
|
||||
email: ps.email,
|
||||
emailVerified: false,
|
||||
emailVerifyCode: null
|
||||
}
|
||||
});
|
||||
|
||||
// Serialize
|
||||
const iObj = await pack(user._id, user, {
|
||||
detail: true,
|
||||
includeSecrets: true
|
||||
});
|
||||
|
||||
// Send response
|
||||
res(iObj);
|
||||
|
||||
// Publish meUpdated event
|
||||
publishMainStream(user._id, 'meUpdated', iObj);
|
||||
|
||||
if (ps.email != null) {
|
||||
const code = rndstr('a-z0-9', 16);
|
||||
|
||||
await User.update(user._id, {
|
||||
$set: {
|
||||
emailVerifyCode: code
|
||||
}
|
||||
});
|
||||
|
||||
const meta = await fetchMeta();
|
||||
|
||||
const enableAuth = meta.smtpUser != null && meta.smtpUser !== '';
|
||||
|
||||
const transporter = nodemailer.createTransport({
|
||||
host: meta.smtpHost,
|
||||
port: meta.smtpPort,
|
||||
secure: meta.smtpSecure,
|
||||
ignoreTLS: !enableAuth,
|
||||
auth: enableAuth ? {
|
||||
user: meta.smtpUser,
|
||||
pass: meta.smtpPass
|
||||
} : undefined
|
||||
});
|
||||
|
||||
const link = `${config.url}/verify-email/${code}`;
|
||||
|
||||
transporter.sendMail({
|
||||
from: meta.email,
|
||||
to: ps.email,
|
||||
subject: meta.name,
|
||||
text: `To verify email, please click this link: ${link}`
|
||||
}, (error, info) => {
|
||||
if (error) {
|
||||
return console.error(error);
|
||||
}
|
||||
|
||||
console.log('Message sent: %s', info.messageId);
|
||||
});
|
||||
}
|
||||
}));
|
@ -66,16 +66,12 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => {
|
||||
swPublickey: config.sw ? config.sw.public_key : null,
|
||||
bannerUrl: instance.bannerUrl,
|
||||
maxNoteTextLength: instance.maxNoteTextLength,
|
||||
|
||||
emojis: emojis,
|
||||
enableEmail: instance.enableEmail,
|
||||
|
||||
enableTwitterIntegration: instance.enableTwitterIntegration,
|
||||
enableGithubIntegration: instance.enableGithubIntegration,
|
||||
enableDiscordIntegration: instance.enableDiscordIntegration,
|
||||
|
||||
enableExternalUserRecommendation: instance.enableExternalUserRecommendation,
|
||||
externalUserRecommendationEngine: instance.externalUserRecommendationEngine,
|
||||
externalUserRecommendationTimeout: instance.externalUserRecommendationTimeout
|
||||
};
|
||||
|
||||
if (ps.detail) {
|
||||
@ -107,7 +103,16 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => {
|
||||
response.githubClientSecret = instance.githubClientSecret;
|
||||
response.discordClientId = instance.discordClientId;
|
||||
response.discordClientSecret = instance.discordClientSecret;
|
||||
response.enableExternalUserRecommendation = instance.enableExternalUserRecommendation;
|
||||
response.externalUserRecommendationEngine = instance.externalUserRecommendationEngine;
|
||||
response.externalUserRecommendationTimeout = instance.externalUserRecommendationTimeout;
|
||||
response.summalyProxy = instance.summalyProxy;
|
||||
response.email = instance.email;
|
||||
response.smtpSecure = instance.smtpSecure;
|
||||
response.smtpHost = instance.smtpHost;
|
||||
response.smtpPort = instance.smtpPort;
|
||||
response.smtpUser = instance.smtpUser;
|
||||
response.smtpPass = instance.smtpPass;
|
||||
}
|
||||
|
||||
res(response);
|
||||
|
@ -70,84 +70,6 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => {
|
||||
|
||||
users = users.concat(otherUsers);
|
||||
}
|
||||
|
||||
if (users.length < ps.limit) {
|
||||
const otherUsers = await User
|
||||
.find({
|
||||
_id: { $nin: users.map(u => u._id) },
|
||||
host: null,
|
||||
usernameLower: new RegExp(escapeRegexp(ps.query.replace('@', '').toLowerCase()))
|
||||
}, {
|
||||
limit: ps.limit - users.length
|
||||
});
|
||||
|
||||
users = users.concat(otherUsers);
|
||||
}
|
||||
|
||||
if (users.length < ps.limit && !ps.localOnly) {
|
||||
const otherUsers = await User
|
||||
.find({
|
||||
_id: { $nin: users.map(u => u._id) },
|
||||
host: { $ne: null },
|
||||
usernameLower: new RegExp(escapeRegexp(ps.query.replace('@', '').toLowerCase()))
|
||||
}, {
|
||||
limit: ps.limit - users.length
|
||||
});
|
||||
|
||||
users = users.concat(otherUsers);
|
||||
}
|
||||
}
|
||||
|
||||
if (users.length < ps.limit) {
|
||||
const otherUsers = await User
|
||||
.find({
|
||||
_id: { $nin: users.map(u => u._id) },
|
||||
host: null,
|
||||
name: new RegExp('^' + escapeRegexp(ps.query.toLowerCase()))
|
||||
}, {
|
||||
limit: ps.limit - users.length
|
||||
});
|
||||
|
||||
users = users.concat(otherUsers);
|
||||
}
|
||||
|
||||
if (users.length < ps.limit && !ps.localOnly) {
|
||||
const otherUsers = await User
|
||||
.find({
|
||||
_id: { $nin: users.map(u => u._id) },
|
||||
host: { $ne: null },
|
||||
name: new RegExp('^' + escapeRegexp(ps.query.toLowerCase()))
|
||||
}, {
|
||||
limit: ps.limit - users.length
|
||||
});
|
||||
|
||||
users = users.concat(otherUsers);
|
||||
}
|
||||
|
||||
if (users.length < ps.limit) {
|
||||
const otherUsers = await User
|
||||
.find({
|
||||
_id: { $nin: users.map(u => u._id) },
|
||||
host: null,
|
||||
name: new RegExp(escapeRegexp(ps.query.toLowerCase()))
|
||||
}, {
|
||||
limit: ps.limit - users.length
|
||||
});
|
||||
|
||||
users = users.concat(otherUsers);
|
||||
}
|
||||
|
||||
if (users.length < ps.limit && !ps.localOnly) {
|
||||
const otherUsers = await User
|
||||
.find({
|
||||
_id: { $nin: users.map(u => u._id) },
|
||||
host: { $ne: null },
|
||||
name: new RegExp(escapeRegexp(ps.query.toLowerCase()))
|
||||
}, {
|
||||
limit: ps.limit - users.length
|
||||
});
|
||||
|
||||
users = users.concat(otherUsers);
|
||||
}
|
||||
|
||||
// Serialize
|
||||
|
@ -19,6 +19,12 @@ app.use(cors({
|
||||
origin: '*'
|
||||
}));
|
||||
|
||||
// No caching
|
||||
app.use(async (ctx, next) => {
|
||||
ctx.set('Cache-Control', 'private, max-age=0, must-revalidate');
|
||||
await next();
|
||||
});
|
||||
|
||||
app.use(bodyParser({
|
||||
// リクエストが multipart/form-data でない限りはJSONだと見なす
|
||||
detectJSON: ctx => !ctx.is('multipart/form-data')
|
||||
|
@ -3,6 +3,7 @@ import * as send from 'koa-send';
|
||||
import * as mongodb from 'mongodb';
|
||||
import DriveFile, { getDriveFileBucket } from '../../models/drive-file';
|
||||
import DriveFileThumbnail, { getDriveFileThumbnailBucket } from '../../models/drive-file-thumbnail';
|
||||
import DriveFileWebpublic, { getDriveFileWebpublicBucket } from '../../models/drive-file-webpublic';
|
||||
|
||||
const assets = `${__dirname}/../../server/file/assets/`;
|
||||
|
||||
@ -41,6 +42,11 @@ export default async function(ctx: Koa.Context) {
|
||||
}
|
||||
|
||||
const sendRaw = async () => {
|
||||
if (file.metadata && file.metadata.accessKey && file.metadata.accessKey != ctx.query['original']) {
|
||||
ctx.status = 403;
|
||||
return;
|
||||
}
|
||||
|
||||
const bucket = await getDriveFileBucket();
|
||||
const readable = bucket.openDownloadStream(fileId);
|
||||
readable.on('error', commonReadableHandlerGenerator(ctx));
|
||||
@ -60,6 +66,19 @@ export default async function(ctx: Koa.Context) {
|
||||
} else {
|
||||
await sendRaw();
|
||||
}
|
||||
} else if ('web' in ctx.query) {
|
||||
const web = await DriveFileWebpublic.findOne({
|
||||
'metadata.originalId': fileId
|
||||
});
|
||||
|
||||
if (web != null) {
|
||||
ctx.set('Content-Type', file.contentType);
|
||||
|
||||
const bucket = await getDriveFileWebpublicBucket();
|
||||
ctx.body = bucket.openDownloadStream(web._id);
|
||||
} else {
|
||||
await sendRaw();
|
||||
}
|
||||
} else {
|
||||
if ('download' in ctx.query) {
|
||||
ctx.set('Content-Disposition', 'attachment');
|
||||
|
@ -20,6 +20,7 @@ import config from '../config';
|
||||
import networkChart from '../chart/network';
|
||||
import apiServer from './api';
|
||||
import { sum } from '../prelude/array';
|
||||
import User from '../models/user';
|
||||
|
||||
// Init app
|
||||
const app = new Koa();
|
||||
@ -59,6 +60,29 @@ const router = new Router();
|
||||
router.use(activityPub.routes());
|
||||
router.use(webFinger.routes());
|
||||
|
||||
router.get('/verify-email/:code', async ctx => {
|
||||
const user = await User.findOne({ emailVerifyCode: ctx.params.code });
|
||||
|
||||
if (user != null) {
|
||||
ctx.body = 'Verify succeeded!';
|
||||
ctx.status = 200;
|
||||
|
||||
User.update({ _id: user._id }, {
|
||||
$set: {
|
||||
emailVerified: true,
|
||||
emailVerifyCode: null
|
||||
}
|
||||
});
|
||||
} else {
|
||||
ctx.status = 404;
|
||||
}
|
||||
});
|
||||
|
||||
// Return 404 for other .well-known
|
||||
router.all('/.well-known/*', async ctx => {
|
||||
ctx.status = 404;
|
||||
});
|
||||
|
||||
// Register router
|
||||
app.use(router.routes());
|
||||
|
||||
|
@ -16,6 +16,7 @@ import { publishMainStream, publishDriveStream } from '../../stream';
|
||||
import { isLocalUser, IUser, IRemoteUser } from '../../models/user';
|
||||
import delFile from './delete-file';
|
||||
import config from '../../config';
|
||||
import { getDriveFileWebpublicBucket } from '../../models/drive-file-webpublic';
|
||||
import { getDriveFileThumbnailBucket } from '../../models/drive-file-thumbnail';
|
||||
import driveChart from '../../chart/drive';
|
||||
import perUserDriveChart from '../../chart/per-user-drive';
|
||||
@ -23,7 +24,71 @@ import fetchMeta from '../../misc/fetch-meta';
|
||||
|
||||
const log = debug('misskey:drive:add-file');
|
||||
|
||||
async function save(path: string, name: string, type: string, hash: string, size: number, metadata: any): Promise<IDriveFile> {
|
||||
/***
|
||||
* Save file
|
||||
* @param path Path for original
|
||||
* @param name Name for original
|
||||
* @param type Content-Type for original
|
||||
* @param hash Hash for original
|
||||
* @param size Size for original
|
||||
* @param metadata
|
||||
*/
|
||||
async function save(path: string, name: string, type: string, hash: string, size: number, metadata: IMetadata): Promise<IDriveFile> {
|
||||
// #region webpublic
|
||||
let webpublic: Buffer;
|
||||
let webpublicExt = 'jpg';
|
||||
let webpublicType = 'image/jpeg';
|
||||
|
||||
if (!metadata.uri) { // from local instance
|
||||
log(`creating web image`);
|
||||
|
||||
if (['image/jpeg'].includes(type)) {
|
||||
webpublic = await sharp(path)
|
||||
.resize(2048, 2048, {
|
||||
fit: 'inside',
|
||||
withoutEnlargement: true
|
||||
})
|
||||
.rotate()
|
||||
.jpeg({
|
||||
quality: 85,
|
||||
progressive: true
|
||||
})
|
||||
.toBuffer();
|
||||
} else if (['image/webp'].includes(type)) {
|
||||
webpublic = await sharp(path)
|
||||
.resize(2048, 2048, {
|
||||
fit: 'inside',
|
||||
withoutEnlargement: true
|
||||
})
|
||||
.rotate()
|
||||
.webp({
|
||||
quality: 85
|
||||
})
|
||||
.toBuffer();
|
||||
|
||||
webpublicExt = 'webp';
|
||||
webpublicType = 'image/webp';
|
||||
} else if (['image/png'].includes(type)) {
|
||||
webpublic = await sharp(path)
|
||||
.resize(2048, 2048, {
|
||||
fit: 'inside',
|
||||
withoutEnlargement: true
|
||||
})
|
||||
.rotate()
|
||||
.png()
|
||||
.toBuffer();
|
||||
|
||||
webpublicExt = 'png';
|
||||
webpublicType = 'image/png';
|
||||
} else {
|
||||
log(`web image not created (not an image)`);
|
||||
}
|
||||
} else {
|
||||
log(`web image not created (from remote)`);
|
||||
}
|
||||
// #endregion webpublic
|
||||
|
||||
// #region thumbnail
|
||||
let thumbnail: Buffer;
|
||||
let thumbnailExt = 'jpg';
|
||||
let thumbnailType = 'image/jpeg';
|
||||
@ -53,10 +118,9 @@ async function save(path: string, name: string, type: string, hash: string, size
|
||||
thumbnailExt = 'png';
|
||||
thumbnailType = 'image/png';
|
||||
}
|
||||
// #endregion thumbnail
|
||||
|
||||
if (config.drive && config.drive.storage == 'minio') {
|
||||
const minio = new Minio.Client(config.drive.config);
|
||||
|
||||
let [ext] = (name.match(/\.([a-zA-Z0-9_-]+)$/) || ['']);
|
||||
|
||||
if (ext === '') {
|
||||
@ -66,33 +130,41 @@ async function save(path: string, name: string, type: string, hash: string, size
|
||||
}
|
||||
|
||||
const key = `${config.drive.prefix}/${uuid.v4()}${ext}`;
|
||||
const webpublicKey = `${config.drive.prefix}/${uuid.v4()}.${webpublicExt}`;
|
||||
const thumbnailKey = `${config.drive.prefix}/${uuid.v4()}.${thumbnailExt}`;
|
||||
|
||||
log(`uploading original: ${key}`);
|
||||
const uploads = [
|
||||
upload(key, fs.createReadStream(path), type)
|
||||
];
|
||||
|
||||
if (webpublic) {
|
||||
log(`uploading webpublic: ${webpublicKey}`);
|
||||
uploads.push(upload(webpublicKey, webpublic, webpublicType));
|
||||
}
|
||||
|
||||
if (thumbnail) {
|
||||
log(`uploading thumbnail: ${thumbnailKey}`);
|
||||
uploads.push(upload(thumbnailKey, thumbnail, thumbnailType));
|
||||
}
|
||||
|
||||
await Promise.all(uploads);
|
||||
|
||||
const baseUrl = config.drive.baseUrl
|
||||
|| `${ config.drive.config.useSSL ? 'https' : 'http' }://${ config.drive.config.endPoint }${ config.drive.config.port ? `:${config.drive.config.port}` : '' }/${ config.drive.bucket }`;
|
||||
|
||||
await minio.putObject(config.drive.bucket, key, fs.createReadStream(path), size, {
|
||||
'Content-Type': type,
|
||||
'Cache-Control': 'max-age=31536000, immutable'
|
||||
});
|
||||
|
||||
if (thumbnail) {
|
||||
await minio.putObject(config.drive.bucket, thumbnailKey, thumbnail, size, {
|
||||
'Content-Type': thumbnailType,
|
||||
'Cache-Control': 'max-age=31536000, immutable'
|
||||
});
|
||||
}
|
||||
|
||||
Object.assign(metadata, {
|
||||
withoutChunks: true,
|
||||
storage: 'minio',
|
||||
storageProps: {
|
||||
key: key,
|
||||
thumbnailKey: thumbnailKey
|
||||
webpublicKey: webpublic ? webpublicKey : null,
|
||||
thumbnailKey: thumbnail ? thumbnailKey : null,
|
||||
},
|
||||
url: `${ baseUrl }/${ key }`,
|
||||
webpublicUrl: webpublic ? `${ baseUrl }/${ webpublicKey }` : null,
|
||||
thumbnailUrl: thumbnail ? `${ baseUrl }/${ thumbnailKey }` : null
|
||||
});
|
||||
} as IMetadata);
|
||||
|
||||
const file = await DriveFile.insert({
|
||||
length: size,
|
||||
@ -105,29 +177,55 @@ async function save(path: string, name: string, type: string, hash: string, size
|
||||
|
||||
return file;
|
||||
} else {
|
||||
// Get MongoDB GridFS bucket
|
||||
const bucket = await getDriveFileBucket();
|
||||
// #region store original
|
||||
const originalDst = await getDriveFileBucket();
|
||||
|
||||
const file = await new Promise<IDriveFile>((resolve, reject) => {
|
||||
const writeStream = bucket.openUploadStream(name, {
|
||||
// web用(Exif削除済み)がある場合はオリジナルにアクセス制限
|
||||
if (webpublic) metadata.accessKey = uuid.v4();
|
||||
|
||||
const originalFile = await new Promise<IDriveFile>((resolve, reject) => {
|
||||
const writeStream = originalDst.openUploadStream(name, {
|
||||
contentType: type,
|
||||
metadata
|
||||
});
|
||||
|
||||
writeStream.once('finish', resolve);
|
||||
writeStream.on('error', reject);
|
||||
|
||||
fs.createReadStream(path).pipe(writeStream);
|
||||
});
|
||||
|
||||
log(`original stored to ${originalFile._id}`);
|
||||
// #endregion store original
|
||||
|
||||
// #region store webpublic
|
||||
if (webpublic) {
|
||||
const webDst = await getDriveFileWebpublicBucket();
|
||||
|
||||
const webFile = await new Promise<IDriveFile>((resolve, reject) => {
|
||||
const writeStream = webDst.openUploadStream(name, {
|
||||
contentType: webpublicType,
|
||||
metadata: {
|
||||
originalId: originalFile._id
|
||||
}
|
||||
});
|
||||
|
||||
writeStream.once('finish', resolve);
|
||||
writeStream.on('error', reject);
|
||||
writeStream.end(webpublic);
|
||||
});
|
||||
|
||||
log(`web stored ${webFile._id}`);
|
||||
}
|
||||
// #endregion store webpublic
|
||||
|
||||
if (thumbnail) {
|
||||
const thumbnailBucket = await getDriveFileThumbnailBucket();
|
||||
|
||||
await new Promise<IDriveFile>((resolve, reject) => {
|
||||
const tuhmFile = await new Promise<IDriveFile>((resolve, reject) => {
|
||||
const writeStream = thumbnailBucket.openUploadStream(name, {
|
||||
contentType: thumbnailType,
|
||||
metadata: {
|
||||
originalId: file._id
|
||||
originalId: originalFile._id
|
||||
}
|
||||
});
|
||||
|
||||
@ -135,12 +233,23 @@ async function save(path: string, name: string, type: string, hash: string, size
|
||||
writeStream.on('error', reject);
|
||||
writeStream.end(thumbnail);
|
||||
});
|
||||
|
||||
log(`thumbnail stored ${tuhmFile._id}`);
|
||||
}
|
||||
|
||||
return file;
|
||||
return originalFile;
|
||||
}
|
||||
}
|
||||
|
||||
async function upload(key: string, stream: fs.ReadStream | Buffer, type: string) {
|
||||
const minio = new Minio.Client(config.drive.config);
|
||||
|
||||
await minio.putObject(config.drive.bucket, key, stream, null, {
|
||||
'Content-Type': type,
|
||||
'Cache-Control': 'max-age=31536000, immutable'
|
||||
});
|
||||
}
|
||||
|
||||
async function deleteOldFile(user: IRemoteUser) {
|
||||
const oldFile = await DriveFile.findOne({
|
||||
_id: {
|
||||
|
@ -4,6 +4,7 @@ import DriveFileThumbnail, { DriveFileThumbnailChunk } from '../../models/drive-
|
||||
import config from '../../config';
|
||||
import driveChart from '../../chart/drive';
|
||||
import perUserDriveChart from '../../chart/per-user-drive';
|
||||
import DriveFileWebpublic, { DriveFileWebpublicChunk } from '../../models/drive-file-webpublic';
|
||||
|
||||
export default async function(file: IDriveFile, isExpired = false) {
|
||||
if (file.metadata.storage == 'minio') {
|
||||
@ -20,6 +21,11 @@ export default async function(file: IDriveFile, isExpired = false) {
|
||||
const thumbnailObj = file.metadata.storageProps.thumbnailKey ? file.metadata.storageProps.thumbnailKey : `${config.drive.prefix}/${file.metadata.storageProps.id}-thumbnail`;
|
||||
await minio.removeObject(config.drive.bucket, thumbnailObj);
|
||||
}
|
||||
|
||||
if (file.metadata.webpublicUrl) {
|
||||
const webpublicObj = file.metadata.storageProps.webpublicKey ? file.metadata.storageProps.webpublicKey : `${config.drive.prefix}/${file.metadata.storageProps.id}-original`;
|
||||
await minio.removeObject(config.drive.bucket, webpublicObj);
|
||||
}
|
||||
}
|
||||
|
||||
// チャンクをすべて削除
|
||||
@ -48,6 +54,20 @@ export default async function(file: IDriveFile, isExpired = false) {
|
||||
}
|
||||
//#endregion
|
||||
|
||||
//#region Web公開用もあれば削除
|
||||
const webpublic = await DriveFileWebpublic.findOne({
|
||||
'metadata.originalId': file._id
|
||||
});
|
||||
|
||||
if (webpublic) {
|
||||
await DriveFileWebpublicChunk.remove({
|
||||
files_id: webpublic._id
|
||||
});
|
||||
|
||||
await DriveFileWebpublic.remove({ _id: webpublic._id });
|
||||
}
|
||||
//#endregion
|
||||
|
||||
// 統計を更新
|
||||
driveChart.update(file, false);
|
||||
perUserDriveChart.update(file, false);
|
||||
|
@ -192,7 +192,7 @@ describe('API', () => {
|
||||
password: 'foo'
|
||||
});
|
||||
|
||||
expect(res).have.status(204);
|
||||
expect(res).have.status(200);
|
||||
}));
|
||||
});
|
||||
|
||||
|
83
test/mfm.ts
83
test/mfm.ts
@ -186,6 +186,14 @@ describe('Text', () => {
|
||||
], tokens);
|
||||
});
|
||||
|
||||
it('with text (zenkaku)', () => {
|
||||
const tokens = analyze('こんにちは #世界');
|
||||
assert.deepEqual([
|
||||
text('こんにちは '),
|
||||
node('hashtag', { hashtag: '世界' })
|
||||
], tokens);
|
||||
});
|
||||
|
||||
it('ignore comma and period', () => {
|
||||
const tokens = analyze('Foo #bar, baz #piyo.');
|
||||
assert.deepEqual([
|
||||
@ -205,12 +213,67 @@ describe('Text', () => {
|
||||
], tokens);
|
||||
});
|
||||
|
||||
it('allow including number', () => {
|
||||
const tokens = analyze('#foo123');
|
||||
assert.deepEqual([
|
||||
node('hashtag', { hashtag: 'foo123' }),
|
||||
], tokens);
|
||||
});
|
||||
|
||||
it('with brackets', () => {
|
||||
const tokens1 = analyze('(#foo)');
|
||||
assert.deepEqual([
|
||||
text('('),
|
||||
node('hashtag', { hashtag: 'foo' }),
|
||||
text(')'),
|
||||
], tokens1);
|
||||
|
||||
const tokens2 = analyze('「#foo」');
|
||||
assert.deepEqual([
|
||||
text('「'),
|
||||
node('hashtag', { hashtag: 'foo' }),
|
||||
text('」'),
|
||||
], tokens2);
|
||||
});
|
||||
|
||||
it('with mixed brackets', () => {
|
||||
const tokens = analyze('「#foo(bar)」');
|
||||
assert.deepEqual([
|
||||
text('「'),
|
||||
node('hashtag', { hashtag: 'foo(bar)' }),
|
||||
text('」'),
|
||||
], tokens);
|
||||
});
|
||||
|
||||
it('with brackets (space before)', () => {
|
||||
const tokens1 = analyze('(bar #foo)');
|
||||
assert.deepEqual([
|
||||
text('(bar '),
|
||||
node('hashtag', { hashtag: 'foo' }),
|
||||
text(')'),
|
||||
], tokens1);
|
||||
|
||||
const tokens2 = analyze('「bar #foo」');
|
||||
assert.deepEqual([
|
||||
text('「bar '),
|
||||
node('hashtag', { hashtag: 'foo' }),
|
||||
text('」'),
|
||||
], tokens2);
|
||||
});
|
||||
|
||||
it('disallow number only', () => {
|
||||
const tokens = analyze('#123');
|
||||
assert.deepEqual([
|
||||
text('#123'),
|
||||
], tokens);
|
||||
});
|
||||
|
||||
it('disallow number only (with brackets)', () => {
|
||||
const tokens = analyze('(#123)');
|
||||
assert.deepEqual([
|
||||
text('(#123)'),
|
||||
], tokens);
|
||||
});
|
||||
});
|
||||
|
||||
describe('quote', () => {
|
||||
@ -417,6 +480,15 @@ describe('Text', () => {
|
||||
], tokens);
|
||||
});
|
||||
|
||||
it('simple (with silent flag)', () => {
|
||||
const tokens = analyze('?[foo](https://example.com)');
|
||||
assert.deepEqual([
|
||||
nodeWithChildren('link', [
|
||||
text('foo')
|
||||
], { url: 'https://example.com', silent: true })
|
||||
], tokens);
|
||||
});
|
||||
|
||||
it('in text', () => {
|
||||
const tokens = analyze('before[foo](https://example.com)after');
|
||||
assert.deepEqual([
|
||||
@ -600,6 +672,17 @@ describe('Text', () => {
|
||||
], tokens);
|
||||
});
|
||||
});
|
||||
|
||||
describe('center', () => {
|
||||
it('simple', () => {
|
||||
const tokens = analyze('<center>foo</center>');
|
||||
assert.deepEqual([
|
||||
nodeWithChildren('center', [
|
||||
text('foo')
|
||||
]),
|
||||
], tokens);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('toHtml', () => {
|
||||
|
Reference in New Issue
Block a user