summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortamaina <tamaina@hotmail.co.jp>2020-05-23 13:19:31 +0900
committerGitHub <noreply@github.com>2020-05-23 13:19:31 +0900
commit3963ed8ff7488a0e2f7f3876461f909e6be08e25 (patch)
treed019d1d294e8ca757479dc638f977ae10b5f44da
parentfix(i18n): Fix missing translation (diff)
downloadmisskey-3963ed8ff7488a0e2f7f3876461f909e6be08e25.tar.gz
misskey-3963ed8ff7488a0e2f7f3876461f909e6be08e25.tar.bz2
misskey-3963ed8ff7488a0e2f7f3876461f909e6be08e25.zip
feat(client): 翻訳をIndexedDBに保存・プッシュ通知を翻訳 (#6396)
* wip * tabun ok * better msg * oops * fix lint * Update gulpfile.ts Co-authored-by: Acid Chicken (硫酸鶏) <root@acid-chicken.com> * Update src/client/scripts/set-i18n-contexts.ts Co-authored-by: Acid Chicken (硫酸鶏) <root@acid-chicken.com> * refactor Co-authored-by: acid-chicken <root@acid-chicken.com> * ✨ * wip * fix lint * たぶんおk * fix flush * Translate Notification * remove console.log * fix * add notifications * remove san * wip * ok * :v: * Update src/prelude/array.ts Co-authored-by: Acid Chicken (硫酸鶏) <root@acid-chicken.com> * wip * i18n refactor * Update init.ts * :v: Co-authored-by: Acid Chicken (硫酸鶏) <root@acid-chicken.com> Co-authored-by: syuilo <syuilotan@yahoo.co.jp>
-rw-r--r--gulpfile.ts6
-rw-r--r--locales/ja-JP.yml16
-rw-r--r--package.json2
-rw-r--r--src/client/app.vue3
-rw-r--r--src/client/components/captcha.vue2
-rw-r--r--src/client/components/cw-button.vue3
-rw-r--r--src/client/components/date-separated-list.vue3
-rw-r--r--src/client/components/dialog.vue3
-rw-r--r--src/client/components/drive-window.vue3
-rw-r--r--src/client/components/drive.file.vue3
-rw-r--r--src/client/components/drive.folder.vue3
-rw-r--r--src/client/components/drive.nav-folder.vue3
-rw-r--r--src/client/components/drive.vue3
-rw-r--r--src/client/components/emoji-picker.vue3
-rw-r--r--src/client/components/error.vue2
-rw-r--r--src/client/components/follow-button.vue3
-rw-r--r--src/client/components/google.vue2
-rw-r--r--src/client/components/image-viewer.vue3
-rw-r--r--src/client/components/instance-stats.vue3
-rw-r--r--src/client/components/media-banner.vue2
-rw-r--r--src/client/components/media-image.vue2
-rw-r--r--src/client/components/media-video.vue2
-rw-r--r--src/client/components/mention.vue2
-rw-r--r--src/client/components/note.vue2
-rw-r--r--src/client/components/notes.vue3
-rw-r--r--src/client/components/notification.vue6
-rw-r--r--src/client/components/notifications.vue3
-rw-r--r--src/client/components/page/page.post.vue2
-rw-r--r--src/client/components/page/page.vue3
-rw-r--r--src/client/components/poll-editor.vue2
-rw-r--r--src/client/components/poll.vue2
-rw-r--r--src/client/components/post-form-attaches.vue3
-rw-r--r--src/client/components/post-form.vue3
-rw-r--r--src/client/components/reaction-icon.vue2
-rw-r--r--src/client/components/reaction-picker.vue3
-rw-r--r--src/client/components/reactions-viewer.details.vue2
-rw-r--r--src/client/components/remote-caution.vue2
-rw-r--r--src/client/components/signin-dialog.vue3
-rwxr-xr-xsrc/client/components/signin.vue3
-rw-r--r--src/client/components/signup-dialog.vue3
-rw-r--r--src/client/components/signup.vue3
-rw-r--r--src/client/components/stream-indicator.vue2
-rw-r--r--src/client/components/sub-note-content.vue2
-rw-r--r--src/client/components/time.vue2
-rw-r--r--src/client/components/uploader.vue2
-rw-r--r--src/client/components/url-preview-popup.vue3
-rw-r--r--src/client/components/url-preview.vue3
-rw-r--r--src/client/components/user-list.vue3
-rw-r--r--src/client/components/user-menu.vue3
-rw-r--r--src/client/components/user-preview.vue3
-rw-r--r--src/client/components/user-select.vue3
-rw-r--r--src/client/components/users-dialog.vue3
-rw-r--r--src/client/components/visibility-chooser.vue2
-rw-r--r--src/client/components/window.vue3
-rw-r--r--src/client/config.ts5
-rw-r--r--src/client/db.ts68
-rw-r--r--src/client/i18n.ts12
-rw-r--r--src/client/init.ts44
-rw-r--r--src/client/pages/about-misskey.vue3
-rw-r--r--src/client/pages/about.vue3
-rw-r--r--src/client/pages/announcements.vue3
-rw-r--r--src/client/pages/auth.form.vue2
-rwxr-xr-xsrc/client/pages/auth.vue2
-rw-r--r--src/client/pages/doc.vue3
-rw-r--r--src/client/pages/explore.vue3
-rw-r--r--src/client/pages/follow.vue3
-rw-r--r--src/client/pages/index.welcome.entrance.vue3
-rw-r--r--src/client/pages/index.welcome.setup.vue2
-rw-r--r--src/client/pages/instance/announcements.vue3
-rw-r--r--src/client/pages/instance/federation.instance.vue3
-rw-r--r--src/client/pages/instance/federation.vue3
-rw-r--r--src/client/pages/instance/index.vue3
-rw-r--r--src/client/pages/instance/queue.queue.vue3
-rw-r--r--src/client/pages/instance/queue.vue3
-rw-r--r--src/client/pages/instance/relays.vue3
-rw-r--r--src/client/pages/instance/settings.vue3
-rw-r--r--src/client/pages/instance/users.user.vue3
-rw-r--r--src/client/pages/messaging/index.vue3
-rw-r--r--src/client/pages/messaging/messaging-room.form.vue2
-rw-r--r--src/client/pages/messaging/messaging-room.message.vue2
-rw-r--r--src/client/pages/messaging/messaging-room.vue3
-rw-r--r--src/client/pages/miauth.vue2
-rw-r--r--src/client/pages/my-antennas/index.antenna.vue3
-rw-r--r--src/client/pages/my-groups/group.vue3
-rw-r--r--src/client/pages/my-lists/list.vue3
-rw-r--r--src/client/pages/my-settings/2fa.vue2
-rw-r--r--src/client/pages/my-settings/api.vue2
-rw-r--r--src/client/pages/my-settings/drive.vue3
-rw-r--r--src/client/pages/my-settings/import-export.vue3
-rw-r--r--src/client/pages/my-settings/integration.vue3
-rw-r--r--src/client/pages/my-settings/mute-block.vue3
-rw-r--r--src/client/pages/my-settings/privacy.vue3
-rw-r--r--src/client/pages/my-settings/profile.vue3
-rw-r--r--src/client/pages/my-settings/reaction.vue3
-rw-r--r--src/client/pages/my-settings/security.vue3
-rw-r--r--src/client/pages/not-found.vue3
-rw-r--r--src/client/pages/note.vue2
-rw-r--r--src/client/pages/page-editor/els/page-editor.el.button.vue3
-rw-r--r--src/client/pages/page-editor/els/page-editor.el.canvas.vue3
-rw-r--r--src/client/pages/page-editor/els/page-editor.el.counter.vue3
-rw-r--r--src/client/pages/page-editor/els/page-editor.el.if.vue3
-rw-r--r--src/client/pages/page-editor/els/page-editor.el.image.vue3
-rw-r--r--src/client/pages/page-editor/els/page-editor.el.number-input.vue3
-rw-r--r--src/client/pages/page-editor/els/page-editor.el.post.vue3
-rw-r--r--src/client/pages/page-editor/els/page-editor.el.radio-button.vue2
-rw-r--r--src/client/pages/page-editor/els/page-editor.el.section.vue3
-rw-r--r--src/client/pages/page-editor/els/page-editor.el.switch.vue3
-rw-r--r--src/client/pages/page-editor/els/page-editor.el.text-input.vue3
-rw-r--r--src/client/pages/page-editor/els/page-editor.el.text.vue3
-rw-r--r--src/client/pages/page-editor/els/page-editor.el.textarea-input.vue3
-rw-r--r--src/client/pages/page-editor/els/page-editor.el.textarea.vue3
-rw-r--r--src/client/pages/page-editor/page-editor.container.vue3
-rw-r--r--src/client/pages/page-editor/page-editor.script-block.vue3
-rw-r--r--src/client/pages/page-editor/page-editor.vue3
-rw-r--r--src/client/pages/pages.vue2
-rw-r--r--src/client/pages/preferences/index.vue22
-rw-r--r--src/client/pages/preferences/sidebar.vue3
-rw-r--r--src/client/pages/preferences/theme.vue3
-rw-r--r--src/client/pages/room/room.vue3
-rw-r--r--src/client/pages/scratchpad.vue3
-rw-r--r--src/client/pages/share.vue3
-rw-r--r--src/client/pages/user/follow-list.vue3
-rw-r--r--src/client/pages/user/index.photos.vue2
-rw-r--r--src/client/scripts/compose-notification.ts97
-rw-r--r--src/client/scripts/set-i18n-contexts.ts18
-rw-r--r--src/client/sw.ts (renamed from src/client/sw.js)13
-rw-r--r--src/client/tsconfig.json5
-rw-r--r--src/client/widgets/activity.chart.vue2
-rw-r--r--src/client/widgets/activity.vue2
-rw-r--r--src/client/widgets/calendar.vue2
-rw-r--r--src/client/widgets/memo.vue2
-rw-r--r--src/client/widgets/notifications.vue2
-rw-r--r--src/client/widgets/photos.vue2
-rw-r--r--src/client/widgets/rss.vue2
-rw-r--r--src/client/widgets/timeline.vue2
-rw-r--r--src/client/widgets/trends.vue2
-rw-r--r--src/misc/get-note-summary.ts14
-rw-r--r--src/prelude/array.ts12
-rw-r--r--src/server/web/index.ts3
-rw-r--r--src/server/web/views/flush.pug32
-rw-r--r--src/services/push-notification.ts7
-rw-r--r--webpack.config.ts2
-rw-r--r--yarn.lock10
143 files changed, 290 insertions, 433 deletions
diff --git a/gulpfile.ts b/gulpfile.ts
index 262c0a5030..880adb51de 100644
--- a/gulpfile.ts
+++ b/gulpfile.ts
@@ -11,7 +11,7 @@ const cleanCSS = require('gulp-clean-css');
const sass = require('gulp-dart-sass');
const fiber = require('fibers');
-const locales = require('./locales');
+const locales: { [x: string]: any } = require('./locales');
const meta = require('./package.json');
gulp.task('build:ts', () => {
@@ -31,8 +31,10 @@ gulp.task('build:copy:views', () =>
gulp.task('build:copy:locales', cb => {
fs.mkdirSync('./built/client/assets/locales', { recursive: true });
+ const v = { '_version_': meta.version };
+
for (const [lang, locale] of Object.entries(locales)) {
- fs.writeFileSync(`./built/client/assets/locales/${lang}.${meta.version}.json`, JSON.stringify(locale), 'utf-8');
+ fs.writeFileSync(`./built/client/assets/locales/${lang}.${meta.version}.json`, JSON.stringify({ ...locale, ...v }), 'utf-8');
}
cb();
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index ab293eb89d..3c7dc6640b 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -507,6 +507,8 @@ addRelay: "リレーの追加"
inboxUrl: "inboxのURL"
addedRelays: "追加済みのリレー"
serviceworkerInfo: "プッシュ通知を行うには有効する必要があります。"
+deletedNote: "削除された投稿"
+invisibleNote: "非公開の投稿"
_theme:
explore: "テーマを探す"
@@ -1102,3 +1104,17 @@ _relayStatus:
requesting: "承認待ち"
accepted: "承認済み"
rejected: "拒否済み"
+
+_notification:
+ fileUploaded: "ファイルがアップロードされました"
+ youGotMention: "{name}からのメンション"
+ youGotReply: "{name}からのリプライ"
+ youGotQuote: "{name}による引用"
+ youRenoted: "{name}がRenoteしました"
+ youGotPoll: "{name}が投票しました"
+ youGotMessagingMessageFromUser: "{name}からのチャットがあります"
+ youGotMessagingMessageFromGroup: "{name}のチャットがあります"
+ youWereFollowed: "フォローされました"
+ youReceivedFollowRequest: "フォローリクエストが来ました"
+ yourFollowRequestAccepted: "フォローリクエストが承認されました"
+ youWereInvitedToGroup: "グループに招待されました"
diff --git a/package.json b/package.json
index 15c1478dbd..d34394d138 100644
--- a/package.json
+++ b/package.json
@@ -125,6 +125,7 @@
"css-loader": "3.5.3",
"cssnano": "4.1.10",
"dateformat": "3.0.3",
+ "deep-entries": "3.1.0",
"diskusage": "1.1.3",
"double-ended-queue": "2.1.0-0",
"escape-regexp": "0.0.1",
@@ -151,6 +152,7 @@
"http-proxy-agent": "4.0.1",
"http-signature": "1.3.4",
"https-proxy-agent": "5.0.0",
+ "idb-keyval": "3.2.0",
"insert-text-at-cursor": "0.3.0",
"is-root": "2.1.0",
"is-svg": "4.2.1",
diff --git a/src/client/app.vue b/src/client/app.vue
index 5e7396205b..8e192d4633 100644
--- a/src/client/app.vue
+++ b/src/client/app.vue
@@ -136,15 +136,12 @@ import { faGripVertical, faChevronLeft, faHashtag, faBroadcastTower, faFireAlt,
import { faBell, faEnvelope, faLaugh, faComments } from '@fortawesome/free-regular-svg-icons';
import { ResizeObserver } from '@juggle/resize-observer';
import { v4 as uuid } from 'uuid';
-import i18n from './i18n';
import { host, instanceName } from './config';
import { search } from './scripts/search';
const DESKTOP_THRESHOLD = 1100;
export default Vue.extend({
- i18n,
-
components: {
XClock: () => import('./components/header-clock.vue').then(m => m.default),
MkButton: () => import('./components/ui/button.vue').then(m => m.default),
diff --git a/src/client/components/captcha.vue b/src/client/components/captcha.vue
index 6b1ee6f0b2..1a894d9350 100644
--- a/src/client/components/captcha.vue
+++ b/src/client/components/captcha.vue
@@ -7,7 +7,6 @@
<script lang="ts">
import Vue from 'vue';
-import i18n from '../i18n';
type Captcha = {
render(container: string | Node, options: {
@@ -31,7 +30,6 @@ declare global {
}
export default Vue.extend({
- i18n,
props: {
provider: {
type: String,
diff --git a/src/client/components/cw-button.vue b/src/client/components/cw-button.vue
index 4516e5210c..07a44d970f 100644
--- a/src/client/components/cw-button.vue
+++ b/src/client/components/cw-button.vue
@@ -7,13 +7,10 @@
<script lang="ts">
import Vue from 'vue';
-import i18n from '../i18n';
import { length } from 'stringz';
import { concat } from '../../prelude/array';
export default Vue.extend({
- i18n,
-
props: {
value: {
type: Boolean,
diff --git a/src/client/components/date-separated-list.vue b/src/client/components/date-separated-list.vue
index b80c6494ed..a27e9a05a2 100644
--- a/src/client/components/date-separated-list.vue
+++ b/src/client/components/date-separated-list.vue
@@ -15,11 +15,8 @@
<script lang="ts">
import Vue from 'vue';
import { faAngleUp, faAngleDown } from '@fortawesome/free-solid-svg-icons';
-import i18n from '../i18n';
export default Vue.extend({
- i18n,
-
props: {
items: {
type: Array,
diff --git a/src/client/components/dialog.vue b/src/client/components/dialog.vue
index da8e54684b..58115b47a2 100644
--- a/src/client/components/dialog.vue
+++ b/src/client/components/dialog.vue
@@ -57,11 +57,8 @@ import MkInput from './ui/input.vue';
import MkSelect from './ui/select.vue';
import MkSignin from './signin.vue';
import parseAcct from '../../misc/acct/parse';
-import i18n from '../i18n';
export default Vue.extend({
- i18n,
-
components: {
MkButton,
MkInput,
diff --git a/src/client/components/drive-window.vue b/src/client/components/drive-window.vue
index d63881c0ed..c42cb66617 100644
--- a/src/client/components/drive-window.vue
+++ b/src/client/components/drive-window.vue
@@ -12,13 +12,10 @@
<script lang="ts">
import Vue from 'vue';
-import i18n from '../i18n';
import XDrive from './drive.vue';
import XWindow from './window.vue';
export default Vue.extend({
- i18n,
-
components: {
XDrive,
XWindow,
diff --git a/src/client/components/drive.file.vue b/src/client/components/drive.file.vue
index a547abf9a0..1b24c61df5 100644
--- a/src/client/components/drive.file.vue
+++ b/src/client/components/drive.file.vue
@@ -32,7 +32,6 @@
<script lang="ts">
import Vue from 'vue';
import { faEye, faEyeSlash } from '@fortawesome/free-regular-svg-icons';
-import i18n from '../i18n';
import copyToClipboard from '../scripts/copy-to-clipboard';
//import updateAvatar from '../api/update-avatar';
//import updateBanner from '../api/update-banner';
@@ -40,8 +39,6 @@ import XFileThumbnail from './drive-file-thumbnail.vue';
import { faDownload, faLink, faICursor, faTrashAlt } from '@fortawesome/free-solid-svg-icons';
export default Vue.extend({
- i18n,
-
components: {
XFileThumbnail
},
diff --git a/src/client/components/drive.folder.vue b/src/client/components/drive.folder.vue
index b778acc77f..9e80653194 100644
--- a/src/client/components/drive.folder.vue
+++ b/src/client/components/drive.folder.vue
@@ -28,11 +28,8 @@
<script lang="ts">
import Vue from 'vue';
import { faFolder, faFolderOpen } from '@fortawesome/free-regular-svg-icons';
-import i18n from '../i18n';
export default Vue.extend({
- i18n,
-
props: {
folder: {
type: Object,
diff --git a/src/client/components/drive.nav-folder.vue b/src/client/components/drive.nav-folder.vue
index 0689faecd2..9e805a5e93 100644
--- a/src/client/components/drive.nav-folder.vue
+++ b/src/client/components/drive.nav-folder.vue
@@ -15,11 +15,8 @@
<script lang="ts">
import Vue from 'vue';
import { faCloud } from '@fortawesome/free-solid-svg-icons';
-import i18n from '../i18n';
export default Vue.extend({
- i18n,
-
props: {
folder: {
type: Object,
diff --git a/src/client/components/drive.vue b/src/client/components/drive.vue
index 08c7097a8f..65eb1cb816 100644
--- a/src/client/components/drive.vue
+++ b/src/client/components/drive.vue
@@ -48,7 +48,6 @@
<script lang="ts">
import Vue from 'vue';
import { faAngleRight } from '@fortawesome/free-solid-svg-icons';
-import i18n from '../i18n';
import XNavFolder from './drive.nav-folder.vue';
import XFolder from './drive.folder.vue';
import XFile from './drive.file.vue';
@@ -56,8 +55,6 @@ import XUploader from './uploader.vue';
import MkButton from './ui/button.vue';
export default Vue.extend({
- i18n,
-
components: {
XNavFolder,
XFolder,
diff --git a/src/client/components/emoji-picker.vue b/src/client/components/emoji-picker.vue
index 868a6125c4..7871b438c9 100644
--- a/src/client/components/emoji-picker.vue
+++ b/src/client/components/emoji-picker.vue
@@ -64,7 +64,6 @@
<script lang="ts">
import Vue from 'vue';
-import i18n from '../i18n';
import { emojilist } from '../../misc/emojilist';
import { getStaticImageUrl } from '../scripts/get-static-image-url';
import { faAsterisk, faLeaf, faUtensils, faFutbol, faCity, faDice, faGlobe, faHistory, faUser } from '@fortawesome/free-solid-svg-icons';
@@ -73,8 +72,6 @@ import { groupByX } from '../../prelude/array';
import XPopup from './popup.vue';
export default Vue.extend({
- i18n,
-
components: {
XPopup,
},
diff --git a/src/client/components/error.vue b/src/client/components/error.vue
index dd9de43c16..fea81305ed 100644
--- a/src/client/components/error.vue
+++ b/src/client/components/error.vue
@@ -9,11 +9,9 @@
<script lang="ts">
import Vue from 'vue';
import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';
-import i18n from '../i18n';
import MkButton from './ui/button.vue';
export default Vue.extend({
- i18n,
components: {
MkButton,
},
diff --git a/src/client/components/follow-button.vue b/src/client/components/follow-button.vue
index 23cb0cd945..7967c0e159 100644
--- a/src/client/components/follow-button.vue
+++ b/src/client/components/follow-button.vue
@@ -30,12 +30,9 @@
<script lang="ts">
import Vue from 'vue';
-import i18n from '../i18n';
import { faSpinner, faPlus, faMinus, faHourglassHalf } from '@fortawesome/free-solid-svg-icons';
export default Vue.extend({
- i18n,
-
props: {
user: {
type: Object,
diff --git a/src/client/components/google.vue b/src/client/components/google.vue
index 01dcf24bf8..de96cbd16a 100644
--- a/src/client/components/google.vue
+++ b/src/client/components/google.vue
@@ -8,10 +8,8 @@
<script lang="ts">
import Vue from 'vue';
import { faSearch } from '@fortawesome/free-solid-svg-icons';
-import i18n from '../i18n';
export default Vue.extend({
- i18n,
props: ['q'],
data() {
return {
diff --git a/src/client/components/image-viewer.vue b/src/client/components/image-viewer.vue
index 3359b600da..c78112b988 100644
--- a/src/client/components/image-viewer.vue
+++ b/src/client/components/image-viewer.vue
@@ -6,12 +6,9 @@
<script lang="ts">
import Vue from 'vue';
-import i18n from '../i18n';
import XModal from './modal.vue';
export default Vue.extend({
- i18n,
-
components: {
XModal,
},
diff --git a/src/client/components/instance-stats.vue b/src/client/components/instance-stats.vue
index 378e9ce391..552e3523f7 100644
--- a/src/client/components/instance-stats.vue
+++ b/src/client/components/instance-stats.vue
@@ -125,7 +125,6 @@
import Vue from 'vue';
import { faChartBar, faUser, faPencilAlt } from '@fortawesome/free-solid-svg-icons';
import Chart from 'chart.js';
-import i18n from '../i18n';
import MkSelect from './ui/select.vue';
const chartLimit = 90;
@@ -140,8 +139,6 @@ const alpha = (hex, a) => {
};
export default Vue.extend({
- i18n,
-
components: {
MkSelect
},
diff --git a/src/client/components/media-banner.vue b/src/client/components/media-banner.vue
index 088c11fab7..0f746d4340 100644
--- a/src/client/components/media-banner.vue
+++ b/src/client/components/media-banner.vue
@@ -28,10 +28,8 @@
<script lang="ts">
import Vue from 'vue';
import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';
-import i18n from '../i18n';
export default Vue.extend({
- i18n,
props: {
media: {
type: Object,
diff --git a/src/client/components/media-image.vue b/src/client/components/media-image.vue
index 6c33b657ff..6d1b5345de 100644
--- a/src/client/components/media-image.vue
+++ b/src/client/components/media-image.vue
@@ -21,12 +21,10 @@
<script lang="ts">
import Vue from 'vue';
import { faExclamationTriangle, faEyeSlash } from '@fortawesome/free-solid-svg-icons';
-import i18n from '../i18n';
import { getStaticImageUrl } from '../scripts/get-static-image-url';
import ImageViewer from './image-viewer.vue';
export default Vue.extend({
- i18n,
props: {
image: {
type: Object,
diff --git a/src/client/components/media-video.vue b/src/client/components/media-video.vue
index d9b4415cbf..a5e06bfaa9 100644
--- a/src/client/components/media-video.vue
+++ b/src/client/components/media-video.vue
@@ -23,10 +23,8 @@
import Vue from 'vue';
import { faPlayCircle } from '@fortawesome/free-regular-svg-icons';
import { faExclamationTriangle, faEyeSlash } from '@fortawesome/free-solid-svg-icons';
-import i18n from '../i18n';
export default Vue.extend({
- i18n,
props: {
video: {
type: Object,
diff --git a/src/client/components/mention.vue b/src/client/components/mention.vue
index 06dcf12887..8c939f839a 100644
--- a/src/client/components/mention.vue
+++ b/src/client/components/mention.vue
@@ -16,12 +16,10 @@
<script lang="ts">
import Vue from 'vue';
-import i18n from '../i18n';
import { toUnicode } from 'punycode';
import { host as localHost } from '../config';
export default Vue.extend({
- i18n,
props: {
username: {
type: String,
diff --git a/src/client/components/note.vue b/src/client/components/note.vue
index fd895ad5ae..6e513a4b2a 100644
--- a/src/client/components/note.vue
+++ b/src/client/components/note.vue
@@ -93,7 +93,6 @@ import { faBolt, faTimes, faBullhorn, faStar, faLink, faExternalLinkSquareAlt, f
import { faCopy, faTrashAlt, faEdit, faEye, faEyeSlash } from '@fortawesome/free-regular-svg-icons';
import { parse } from '../../mfm/parse';
import { sum, unique } from '../../prelude/array';
-import i18n from '../i18n';
import XSub from './note.sub.vue';
import XNoteHeader from './note-header.vue';
import XNotePreview from './note-preview.vue';
@@ -109,7 +108,6 @@ import { url } from '../config';
import copyToClipboard from '../scripts/copy-to-clipboard';
export default Vue.extend({
- i18n,
components: {
XSub,
diff --git a/src/client/components/notes.vue b/src/client/components/notes.vue
index 0cf4dee2dd..515bc58e2e 100644
--- a/src/client/components/notes.vue
+++ b/src/client/components/notes.vue
@@ -29,15 +29,12 @@
<script lang="ts">
import Vue from 'vue';
-import i18n from '../i18n';
import paging from '../scripts/paging';
import XNote from './note.vue';
import XList from './date-separated-list.vue';
import MkButton from './ui/button.vue';
export default Vue.extend({
- i18n,
-
components: {
XNote, XList, MkButton
},
diff --git a/src/client/components/notification.vue b/src/client/components/notification.vue
index d3ebc8f179..de233d14ac 100644
--- a/src/client/components/notification.vue
+++ b/src/client/components/notification.vue
@@ -61,13 +61,11 @@
import Vue from 'vue';
import { faIdCardAlt, faPlus, faQuoteLeft, faQuoteRight, faRetweet, faReply, faAt, faCheck, faPollH } from '@fortawesome/free-solid-svg-icons';
import { faClock } from '@fortawesome/free-regular-svg-icons';
-import getNoteSummary from '../../misc/get-note-summary';
+import noteSummary from '../../misc/get-note-summary';
import XReactionIcon from './reaction-icon.vue';
import MkFollowButton from './follow-button.vue';
-import i18n from '../i18n';
export default Vue.extend({
- i18n,
components: {
XReactionIcon, MkFollowButton
},
@@ -89,7 +87,7 @@ export default Vue.extend({
},
data() {
return {
- getNoteSummary,
+ getNoteSummary: (text: string) => noteSummary(text, this.$root.i18n.messages[this.$root.i18n.locale]),
followRequestDone: false,
groupInviteDone: false,
faIdCardAlt, faPlus, faQuoteLeft, faQuoteRight, faRetweet, faReply, faAt, faClock, faCheck, faPollH
diff --git a/src/client/components/notifications.vue b/src/client/components/notifications.vue
index ecf5268983..3ed198a04c 100644
--- a/src/client/components/notifications.vue
+++ b/src/client/components/notifications.vue
@@ -18,15 +18,12 @@
<script lang="ts">
import Vue from 'vue';
-import i18n from '../i18n';
import paging from '../scripts/paging';
import XNotification from './notification.vue';
import XList from './date-separated-list.vue';
import XNote from './note.vue';
export default Vue.extend({
- i18n,
-
components: {
XNotification,
XList,
diff --git a/src/client/components/page/page.post.vue b/src/client/components/page/page.post.vue
index 6f79374f34..da5bc8bfab 100644
--- a/src/client/components/page/page.post.vue
+++ b/src/client/components/page/page.post.vue
@@ -8,13 +8,11 @@
<script lang="ts">
import Vue from 'vue';
import { faCheck, faPaperPlane } from '@fortawesome/free-solid-svg-icons';
-import i18n from '../../i18n';
import MkTextarea from '../ui/textarea.vue';
import MkButton from '../ui/button.vue';
import { apiUrl } from '../../config';
export default Vue.extend({
- i18n,
components: {
MkTextarea,
MkButton,
diff --git a/src/client/components/page/page.vue b/src/client/components/page/page.vue
index e3b04d7fd6..b3cc01ec22 100644
--- a/src/client/components/page/page.vue
+++ b/src/client/components/page/page.vue
@@ -9,14 +9,11 @@ import Vue from 'vue';
import { parse } from '@syuilo/aiscript';
import { faHeart as faHeartS } from '@fortawesome/free-solid-svg-icons';
import { faHeart } from '@fortawesome/free-regular-svg-icons';
-import i18n from '../../i18n';
import XBlock from './page.block.vue';
import { Hpml } from '../../scripts/hpml/evaluator';
import { url } from '../../config';
export default Vue.extend({
- i18n,
-
components: {
XBlock
},
diff --git a/src/client/components/poll-editor.vue b/src/client/components/poll-editor.vue
index 91c7dab598..0687e999b5 100644
--- a/src/client/components/poll-editor.vue
+++ b/src/client/components/poll-editor.vue
@@ -51,7 +51,6 @@
<script lang="ts">
import Vue from 'vue';
import { faExclamationTriangle, faTimes } from '@fortawesome/free-solid-svg-icons';
-import i18n from '../i18n';
import { erase } from '../../prelude/array';
import { addTime } from '../../prelude/time';
import { formatDateTimeString } from '../../misc/format-time-string';
@@ -61,7 +60,6 @@ import MkSwitch from './ui/switch.vue';
import MkButton from './ui/button.vue';
export default Vue.extend({
- i18n,
components: {
MkInput,
MkSelect,
diff --git a/src/client/components/poll.vue b/src/client/components/poll.vue
index c748b6b099..e0c42cd008 100644
--- a/src/client/components/poll.vue
+++ b/src/client/components/poll.vue
@@ -24,11 +24,9 @@
<script lang="ts">
import Vue from 'vue';
import { faCheck } from '@fortawesome/free-solid-svg-icons';
-import i18n from '../i18n';
import { sum } from '../../prelude/array';
export default Vue.extend({
- i18n,
props: {
note: {
type: Object,
diff --git a/src/client/components/post-form-attaches.vue b/src/client/components/post-form-attaches.vue
index d9c0653617..2415bf28ec 100644
--- a/src/client/components/post-form-attaches.vue
+++ b/src/client/components/post-form-attaches.vue
@@ -14,15 +14,12 @@
<script lang="ts">
import Vue from 'vue';
-import i18n from '../i18n';
import * as XDraggable from 'vuedraggable';
import { faTimesCircle, faEye, faEyeSlash } from '@fortawesome/free-regular-svg-icons';
import { faExclamationTriangle, faICursor } from '@fortawesome/free-solid-svg-icons';
import XFileThumbnail from './drive-file-thumbnail.vue'
export default Vue.extend({
- i18n,
-
components: {
XDraggable,
XFileThumbnail
diff --git a/src/client/components/post-form.vue b/src/client/components/post-form.vue
index 05faea5146..cdb61f51d5 100644
--- a/src/client/components/post-form.vue
+++ b/src/client/components/post-form.vue
@@ -57,7 +57,6 @@ import { faEyeSlash, faLaughSquint } from '@fortawesome/free-regular-svg-icons';
import insertTextAtCursor from 'insert-text-at-cursor';
import { length } from 'stringz';
import { toASCII } from 'punycode';
-import i18n from '../i18n';
import MkVisibilityChooser from './visibility-chooser.vue';
import MkUserSelect from './user-select.vue';
import XNotePreview from './note-preview.vue';
@@ -70,8 +69,6 @@ import { formatTimeString } from '../../misc/format-time-string';
import { selectDriveFile } from '../scripts/select-drive-file';
export default Vue.extend({
- i18n,
-
components: {
XNotePreview,
XUploader: () => import('./uploader.vue').then(m => m.default),
diff --git a/src/client/components/reaction-icon.vue b/src/client/components/reaction-icon.vue
index 3c6d56b80a..fe2b528368 100644
--- a/src/client/components/reaction-icon.vue
+++ b/src/client/components/reaction-icon.vue
@@ -4,9 +4,7 @@
<script lang="ts">
import Vue from 'vue';
-import i18n from '../i18n';
export default Vue.extend({
- i18n,
props: {
reaction: {
type: String,
diff --git a/src/client/components/reaction-picker.vue b/src/client/components/reaction-picker.vue
index 99b27ad9c9..e331410c39 100644
--- a/src/client/components/reaction-picker.vue
+++ b/src/client/components/reaction-picker.vue
@@ -11,14 +11,11 @@
<script lang="ts">
import Vue from 'vue';
-import i18n from '../i18n';
import { emojiRegex } from '../../misc/emoji-regex';
import XReactionIcon from './reaction-icon.vue';
import XPopup from './popup.vue';
export default Vue.extend({
- i18n,
-
components: {
XPopup,
XReactionIcon,
diff --git a/src/client/components/reactions-viewer.details.vue b/src/client/components/reactions-viewer.details.vue
index ea2523a11f..67c8b261be 100644
--- a/src/client/components/reactions-viewer.details.vue
+++ b/src/client/components/reactions-viewer.details.vue
@@ -20,10 +20,8 @@
<script lang="ts">
import Vue from 'vue';
-import i18n from '../i18n';
export default Vue.extend({
- i18n,
props: {
reaction: {
type: String,
diff --git a/src/client/components/remote-caution.vue b/src/client/components/remote-caution.vue
index 95b37d3053..21af9f766a 100644
--- a/src/client/components/remote-caution.vue
+++ b/src/client/components/remote-caution.vue
@@ -5,10 +5,8 @@
<script lang="ts">
import Vue from 'vue';
import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';
-import i18n from '../i18n';
export default Vue.extend({
- i18n,
props: {
href: {
type: String,
diff --git a/src/client/components/signin-dialog.vue b/src/client/components/signin-dialog.vue
index a356c3ccdb..98b75e627c 100644
--- a/src/client/components/signin-dialog.vue
+++ b/src/client/components/signin-dialog.vue
@@ -7,13 +7,10 @@
<script lang="ts">
import Vue from 'vue';
-import i18n from '../i18n';
import XWindow from './window.vue';
import MkSignin from './signin.vue';
export default Vue.extend({
- i18n,
-
components: {
MkSignin,
XWindow,
diff --git a/src/client/components/signin.vue b/src/client/components/signin.vue
index dc73ad8a0f..a7653b17b0 100755
--- a/src/client/components/signin.vue
+++ b/src/client/components/signin.vue
@@ -49,13 +49,10 @@ import { faLock, faGavel } from '@fortawesome/free-solid-svg-icons';
import { faTwitter, faDiscord, faGithub } from '@fortawesome/free-brands-svg-icons';
import MkButton from './ui/button.vue';
import MkInput from './ui/input.vue';
-import i18n from '../i18n';
import { apiUrl, host } from '../config';
import { byteify, hexify } from '../scripts/2fa';
export default Vue.extend({
- i18n,
-
components: {
MkButton,
MkInput,
diff --git a/src/client/components/signup-dialog.vue b/src/client/components/signup-dialog.vue
index 4db79af512..eff1f79c48 100644
--- a/src/client/components/signup-dialog.vue
+++ b/src/client/components/signup-dialog.vue
@@ -7,13 +7,10 @@
<script lang="ts">
import Vue from 'vue';
-import i18n from '../i18n';
import XWindow from './window.vue';
import XSignup from './signup.vue';
export default Vue.extend({
- i18n,
-
components: {
XSignup,
XWindow,
diff --git a/src/client/components/signup.vue b/src/client/components/signup.vue
index acb6a745ab..ff1932b42d 100644
--- a/src/client/components/signup.vue
+++ b/src/client/components/signup.vue
@@ -53,15 +53,12 @@ import Vue from 'vue';
import { faLock, faExclamationTriangle, faSpinner, faCheck, faKey } from '@fortawesome/free-solid-svg-icons';
const getPasswordStrength = require('syuilo-password-strength');
import { toUnicode } from 'punycode';
-import i18n from '../i18n';
import { host, url } from '../config';
import MkButton from './ui/button.vue';
import MkInput from './ui/input.vue';
import MkSwitch from './ui/switch.vue';
export default Vue.extend({
- i18n,
-
components: {
MkButton,
MkInput,
diff --git a/src/client/components/stream-indicator.vue b/src/client/components/stream-indicator.vue
index dd7a5d07c1..ec00f4cbfe 100644
--- a/src/client/components/stream-indicator.vue
+++ b/src/client/components/stream-indicator.vue
@@ -10,10 +10,8 @@
<script lang="ts">
import Vue from 'vue';
-import i18n from '../i18n';
export default Vue.extend({
- i18n,
data() {
return {
hasDisconnected: false,
diff --git a/src/client/components/sub-note-content.vue b/src/client/components/sub-note-content.vue
index e60c197442..a14c832ea8 100644
--- a/src/client/components/sub-note-content.vue
+++ b/src/client/components/sub-note-content.vue
@@ -21,12 +21,10 @@
<script lang="ts">
import Vue from 'vue';
import { faReply } from '@fortawesome/free-solid-svg-icons';
-import i18n from '../i18n';
import XPoll from './poll.vue';
import XMediaList from './media-list.vue';
export default Vue.extend({
- i18n,
components: {
XPoll,
XMediaList,
diff --git a/src/client/components/time.vue b/src/client/components/time.vue
index 6d092cf4f8..2a871d6d81 100644
--- a/src/client/components/time.vue
+++ b/src/client/components/time.vue
@@ -8,10 +8,8 @@
<script lang="ts">
import Vue from 'vue';
-import i18n from '../i18n';
export default Vue.extend({
- i18n,
props: {
time: {
type: [Date, String],
diff --git a/src/client/components/uploader.vue b/src/client/components/uploader.vue
index 4ceb5e2877..6ebdf123b1 100644
--- a/src/client/components/uploader.vue
+++ b/src/client/components/uploader.vue
@@ -21,13 +21,11 @@
<script lang="ts">
import Vue from 'vue';
-import i18n from '../i18n';
import { apiUrl } from '../config';
//import getMD5 from '../../scripts/get-md5';
import { faSpinner } from '@fortawesome/free-solid-svg-icons';
export default Vue.extend({
- i18n,
data() {
return {
uploads: [],
diff --git a/src/client/components/url-preview-popup.vue b/src/client/components/url-preview-popup.vue
index acd9b1aa9a..52731296cb 100644
--- a/src/client/components/url-preview-popup.vue
+++ b/src/client/components/url-preview-popup.vue
@@ -6,12 +6,9 @@
<script lang="ts">
import Vue from 'vue';
-import i18n from '../i18n';
import MkUrlPreview from './url-preview.vue';
export default Vue.extend({
- i18n,
-
components: {
MkUrlPreview
},
diff --git a/src/client/components/url-preview.vue b/src/client/components/url-preview.vue
index c2dd0038be..d77cfafd1e 100644
--- a/src/client/components/url-preview.vue
+++ b/src/client/components/url-preview.vue
@@ -32,12 +32,9 @@
<script lang="ts">
import Vue from 'vue';
import { faPlayCircle } from '@fortawesome/free-regular-svg-icons';
-import i18n from '../i18n';
import { url as local, lang } from '../config';
export default Vue.extend({
- i18n,
-
props: {
url: {
type: String,
diff --git a/src/client/components/user-list.vue b/src/client/components/user-list.vue
index bde3af6906..7a9cd58a48 100644
--- a/src/client/components/user-list.vue
+++ b/src/client/components/user-list.vue
@@ -31,14 +31,11 @@
<script lang="ts">
import Vue from 'vue';
-import i18n from '../i18n';
import paging from '../scripts/paging';
import MkContainer from './ui/container.vue';
import MkFollowButton from './follow-button.vue';
export default Vue.extend({
- i18n,
-
components: {
MkContainer,
MkFollowButton,
diff --git a/src/client/components/user-menu.vue b/src/client/components/user-menu.vue
index a2275197d8..25937fb3c0 100644
--- a/src/client/components/user-menu.vue
+++ b/src/client/components/user-menu.vue
@@ -6,15 +6,12 @@
import Vue from 'vue';
import { faAt, faListUl, faEye, faEyeSlash, faBan, faPencilAlt, faComments, faUsers, faMicrophoneSlash } from '@fortawesome/free-solid-svg-icons';
import { faSnowflake, faEnvelope } from '@fortawesome/free-regular-svg-icons';
-import i18n from '../i18n';
import XMenu from './menu.vue';
import copyToClipboard from '../scripts/copy-to-clipboard';
import { host } from '../config';
import getAcct from '../../misc/acct/render';
export default Vue.extend({
- i18n,
-
components: {
XMenu
},
diff --git a/src/client/components/user-preview.vue b/src/client/components/user-preview.vue
index 89150eaacc..8c8eee2a34 100644
--- a/src/client/components/user-preview.vue
+++ b/src/client/components/user-preview.vue
@@ -28,13 +28,10 @@
<script lang="ts">
import Vue from 'vue';
-import i18n from '../i18n';
import parseAcct from '../../misc/acct/parse';
import MkFollowButton from './follow-button.vue';
export default Vue.extend({
- i18n,
-
components: {
MkFollowButton
},
diff --git a/src/client/components/user-select.vue b/src/client/components/user-select.vue
index a82626652d..9b4a68ddb3 100644
--- a/src/client/components/user-select.vue
+++ b/src/client/components/user-select.vue
@@ -21,14 +21,11 @@
<script lang="ts">
import Vue from 'vue';
-import i18n from '../i18n';
import { faTimes, faCheck } from '@fortawesome/free-solid-svg-icons';
import MkInput from './ui/input.vue';
import XWindow from './window.vue';
export default Vue.extend({
- i18n,
-
components: {
MkInput,
XWindow,
diff --git a/src/client/components/users-dialog.vue b/src/client/components/users-dialog.vue
index 9d0c5e4251..0e0cc36c2a 100644
--- a/src/client/components/users-dialog.vue
+++ b/src/client/components/users-dialog.vue
@@ -31,13 +31,10 @@
<script lang="ts">
import Vue from 'vue';
import { faTimes } from '@fortawesome/free-solid-svg-icons';
-import i18n from '../i18n';
import paging from '../scripts/paging';
import XModal from './modal.vue';
export default Vue.extend({
- i18n,
-
components: {
XModal,
},
diff --git a/src/client/components/visibility-chooser.vue b/src/client/components/visibility-chooser.vue
index dc7b41e286..0f7e37a088 100644
--- a/src/client/components/visibility-chooser.vue
+++ b/src/client/components/visibility-chooser.vue
@@ -37,11 +37,9 @@
import Vue from 'vue';
import { faGlobe, faUnlock, faHome } from '@fortawesome/free-solid-svg-icons';
import { faEnvelope } from '@fortawesome/free-regular-svg-icons';
-import i18n from '../i18n';
import XPopup from './popup.vue';
export default Vue.extend({
- i18n,
components: {
XPopup
},
diff --git a/src/client/components/window.vue b/src/client/components/window.vue
index 0b2ba248bf..db13985181 100644
--- a/src/client/components/window.vue
+++ b/src/client/components/window.vue
@@ -20,12 +20,9 @@
<script lang="ts">
import Vue from 'vue';
import { faTimes, faCheck } from '@fortawesome/free-solid-svg-icons';
-import i18n from '../i18n';
import XModal from './modal.vue';
export default Vue.extend({
- i18n,
-
components: {
XModal,
},
diff --git a/src/client/config.ts b/src/client/config.ts
index 0d4a96964e..f71647a05c 100644
--- a/src/client/config.ts
+++ b/src/client/config.ts
@@ -1,3 +1,6 @@
+import { clientDb, entries } from './db';
+import { fromEntries } from '../prelude/array';
+
declare const _LANGS_: string[];
declare const _VERSION_: string;
declare const _ENV_: string;
@@ -12,7 +15,7 @@ export const apiUrl = url + '/api';
export const wsUrl = url.replace('http://', 'ws://').replace('https://', 'wss://') + '/streaming';
export const lang = localStorage.getItem('lang');
export const langs = _LANGS_;
-export const locale = JSON.parse(localStorage.getItem('locale'));
+export const getLocale = async () => fromEntries((await entries(clientDb.i18n)) as [string, string][]);
export const version = _VERSION_;
export const env = _ENV_;
export const instanceName = siteName === 'Misskey' ? null : siteName;
diff --git a/src/client/db.ts b/src/client/db.ts
new file mode 100644
index 0000000000..3000a0c968
--- /dev/null
+++ b/src/client/db.ts
@@ -0,0 +1,68 @@
+import { Store } from 'idb-keyval';
+// Provide functions from idb-keyval
+export { get, set, del, clear, keys } from 'idb-keyval';
+
+//#region Construct DB
+export const clientDb = {
+ i18n: new Store('MisskeyClient', 'i18n')
+};
+//#endregion
+
+//#region Provide some tool functions
+function openTransaction(store: Store, mode: IDBTransactionMode): Promise<IDBTransaction>{
+ return store._dbp.then(db => db.transaction(store.storeName, mode));
+}
+
+export function entries(store: Store): Promise<[IDBValidKey, unknown][]> {
+ const entries: [IDBValidKey, unknown][] = [];
+
+ return store._withIDBStore('readonly', store => {
+ store.openCursor().onsuccess = function () {
+ if (!this.result) return;
+ entries.push([this.result.key, this.result.value]);
+ this.result.continue();
+ };
+ }).then(() => entries);
+}
+
+export async function bulkGet(keys: IDBValidKey[], store: Store): Promise<[IDBValidKey, unknown][]> {
+ const valPromises: Promise<[IDBValidKey, unknown]>[] = [];
+
+ const tx = await openTransaction(store, 'readwrite');
+ const st = tx.objectStore(store.storeName);
+ for (const key of keys) {
+ valPromises.push(new Promise((resolve, reject) => {
+ const getting = st.get(key);
+ getting.onsuccess = function (e) {
+ return resolve([key, this.result]);
+ };
+ getting.onerror = function (e) {
+ return reject(this.error);
+ };
+ }));
+ }
+ return new Promise((resolve, reject) => {
+ tx.oncomplete = () => resolve(Promise.all(valPromises));
+ tx.abort = tx.onerror = () => reject(tx.error);
+ });
+}
+
+export async function bulkSet(map: [IDBValidKey, any][], store: Store): Promise<void> {
+ const tx = await openTransaction(store, 'readwrite');
+ const st = tx.objectStore(store.storeName);
+ for (const [key, value] of map) {
+ st.put(value, key);
+ }
+ return new Promise((resolve, reject) => {
+ tx.oncomplete = () => resolve();
+ tx.abort = tx.onerror = () => reject(tx.error);
+ });
+}
+
+export function count(store: Store): Promise<number> {
+ let req: IDBRequest<number>;
+ return store._withIDBStore('readonly', store => {
+ req = store.count();
+ }).then(() => req.result);
+}
+//#endregion
diff --git a/src/client/i18n.ts b/src/client/i18n.ts
deleted file mode 100644
index 05d319fbaf..0000000000
--- a/src/client/i18n.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import Vue from 'vue';
-import VueI18n from 'vue-i18n';
-import { lang, locale } from './config';
-
-Vue.use(VueI18n);
-
-export default new VueI18n({
- locale: lang,
- messages: {
- [lang]: locale
- }
-});
diff --git a/src/client/init.ts b/src/client/init.ts
index 500092061a..e2772502f6 100644
--- a/src/client/init.ts
+++ b/src/client/init.ts
@@ -7,13 +7,13 @@ import Vuex from 'vuex';
import VueMeta from 'vue-meta';
import PortalVue from 'portal-vue';
import VAnimateCss from 'v-animate-css';
+import VueI18n from 'vue-i18n';
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
-import i18n from './i18n';
import VueHotkey from './scripts/hotkey';
import App from './app.vue';
import MiOS from './mios';
-import { version, langs, instanceName } from './config';
+import { version, langs, instanceName, getLocale } from './config';
import PostFormDialog from './components/post-form-dialog.vue';
import Dialog from './components/dialog.vue';
import Menu from './components/menu.vue';
@@ -21,12 +21,15 @@ import { router } from './router';
import { applyTheme, lightTheme } from './theme';
import { isDeviceDarkmode } from './scripts/is-device-darkmode';
import createStore from './store';
+import { clientDb, get, count } from './db';
+import { setI18nContexts } from './scripts/set-i18n-contexts';
Vue.use(Vuex);
Vue.use(VueHotkey);
Vue.use(VueMeta);
Vue.use(PortalVue);
Vue.use(VAnimateCss);
+Vue.use(VueI18n);
Vue.component('fa', FontAwesomeIcon);
require('./directives');
@@ -96,27 +99,6 @@ if (isMobile || window.innerWidth <= 1024) {
head.appendChild(viewport);
}
-//#region Fetch locale data
-const cachedLocale = localStorage.getItem('locale');
-
-if (cachedLocale == null) {
- fetch(`/assets/locales/${lang}.${version}.json`)
- .then(response => response.json()).then(locale => {
- localStorage.setItem('locale', JSON.stringify(locale));
- i18n.locale = lang;
- i18n.setLocaleMessage(lang, locale);
- });
-} else {
- // TODO: 古い時だけ更新
- setTimeout(() => {
- fetch(`/assets/locales/${lang}.${version}.json`)
- .then(response => response.json()).then(locale => {
- localStorage.setItem('locale', JSON.stringify(locale));
- });
- }, 1000 * 5);
-}
-//#endregion
-
//#region Set lang attr
const html = document.documentElement;
html.setAttribute('lang', lang);
@@ -167,6 +149,18 @@ os.init(async () => {
});
//#endregion
+ //#region Fetch locale data
+ const i18n = new VueI18n();
+
+ await count(clientDb.i18n).then(async n => {
+ if (n === 0) return setI18nContexts(lang, version, i18n);
+ if ((await get('_version_', clientDb.i18n) !== version)) return setI18nContexts(lang, version, i18n, true);
+
+ i18n.locale = lang;
+ i18n.setLocaleMessage(lang, await getLocale());
+ });
+ //#endregion
+
if ('Notification' in window && store.getters.isSignedIn) {
// 許可を得ていなかったらリクエスト
if (Notification.permission === 'default') {
@@ -176,6 +170,7 @@ os.init(async () => {
const app = new Vue({
store: store,
+ i18n,
metaInfo: {
title: null,
titleTemplate: title => title ? `${title} | ${(instanceName || 'Misskey')}` : (instanceName || 'Misskey')
@@ -183,7 +178,8 @@ os.init(async () => {
data() {
return {
stream: os.stream,
- isMobile: isMobile
+ isMobile: isMobile,
+ i18n // TODO: 消せないか考える SEE: https://github.com/syuilo/misskey/pull/6396#discussion_r429511030
};
},
methods: {
diff --git a/src/client/pages/about-misskey.vue b/src/client/pages/about-misskey.vue
index 84cd5d5e9c..2c4a257b15 100644
--- a/src/client/pages/about-misskey.vue
+++ b/src/client/pages/about-misskey.vue
@@ -63,12 +63,9 @@
import Vue from 'vue';
import { faInfoCircle } from '@fortawesome/free-solid-svg-icons';
import { version } from '../config';
-import i18n from '../i18n';
import MkLink from '../components/link.vue';
export default Vue.extend({
- i18n,
-
components: {
MkLink
},
diff --git a/src/client/pages/about.vue b/src/client/pages/about.vue
index a3a4b6ac73..25fb0ca13e 100644
--- a/src/client/pages/about.vue
+++ b/src/client/pages/about.vue
@@ -25,12 +25,9 @@
import Vue from 'vue';
import { faInfoCircle } from '@fortawesome/free-solid-svg-icons';
import { version } from '../config';
-import i18n from '../i18n';
import MkInstanceStats from '../components/instance-stats.vue';
export default Vue.extend({
- i18n,
-
metaInfo() {
return {
title: this.$t('instance') as string
diff --git a/src/client/pages/announcements.vue b/src/client/pages/announcements.vue
index 5c6d4f58af..089475ed60 100644
--- a/src/client/pages/announcements.vue
+++ b/src/client/pages/announcements.vue
@@ -21,13 +21,10 @@
<script lang="ts">
import Vue from 'vue';
import { faCheck, faBroadcastTower } from '@fortawesome/free-solid-svg-icons';
-import i18n from '../i18n';
import MkPagination from '../components/ui/pagination.vue';
import MkButton from '../components/ui/button.vue';
export default Vue.extend({
- i18n,
-
metaInfo() {
return {
title: this.$t('announcements') as string
diff --git a/src/client/pages/auth.form.vue b/src/client/pages/auth.form.vue
index e6f61f52f1..c5a9b769ac 100644
--- a/src/client/pages/auth.form.vue
+++ b/src/client/pages/auth.form.vue
@@ -23,11 +23,9 @@
<script lang="ts">
import Vue from 'vue';
-import i18n from '../i18n';
import MkButton from '../components/ui/button.vue';
export default Vue.extend({
- i18n,
components: {
MkButton
},
diff --git a/src/client/pages/auth.vue b/src/client/pages/auth.vue
index e025924fe0..5c40842da1 100755
--- a/src/client/pages/auth.vue
+++ b/src/client/pages/auth.vue
@@ -30,12 +30,10 @@
<script lang="ts">
import Vue from 'vue';
-import i18n from '../i18n';
import XForm from './auth.form.vue';
import MkSignin from '../components/signin.vue';
export default Vue.extend({
- i18n,
components: {
XForm,
MkSignin,
diff --git a/src/client/pages/doc.vue b/src/client/pages/doc.vue
index 7c4f7ebccf..e4c4ef5c6c 100644
--- a/src/client/pages/doc.vue
+++ b/src/client/pages/doc.vue
@@ -19,7 +19,6 @@ import Vue from 'vue';
import { faFileAlt } from '@fortawesome/free-solid-svg-icons'
import MarkdownIt from 'markdown-it';
import MarkdownItAnchor from 'markdown-it-anchor';
-import i18n from '../i18n';
import { url, lang } from '../config';
import MkLink from '../components/link.vue';
@@ -32,8 +31,6 @@ markdown.use(MarkdownItAnchor, {
});
export default Vue.extend({
- i18n,
-
metaInfo() {
return {
title: this.title,
diff --git a/src/client/pages/explore.vue b/src/client/pages/explore.vue
index 7ff4b5ed60..39904846cf 100644
--- a/src/client/pages/explore.vue
+++ b/src/client/pages/explore.vue
@@ -57,13 +57,10 @@
import Vue from 'vue';
import { faChartLine, faPlus, faHashtag, faRocket } from '@fortawesome/free-solid-svg-icons';
import { faBookmark, faCommentAlt } from '@fortawesome/free-regular-svg-icons';
-import i18n from '../i18n';
import XUserList from '../components/user-list.vue';
import MkContainer from '../components/ui/container.vue';
export default Vue.extend({
- i18n,
-
metaInfo() {
return {
title: this.$t('explore') as string
diff --git a/src/client/pages/follow.vue b/src/client/pages/follow.vue
index d765259737..8659763bb7 100644
--- a/src/client/pages/follow.vue
+++ b/src/client/pages/follow.vue
@@ -5,11 +5,8 @@
<script lang="ts">
import Vue from 'vue';
-import i18n from '../i18n';
export default Vue.extend({
- i18n,
-
created() {
const acct = new URL(location.href).searchParams.get('acct');
if (acct == null) return;
diff --git a/src/client/pages/index.welcome.entrance.vue b/src/client/pages/index.welcome.entrance.vue
index a9343e87cc..9bb2e85fc3 100644
--- a/src/client/pages/index.welcome.entrance.vue
+++ b/src/client/pages/index.welcome.entrance.vue
@@ -20,12 +20,9 @@ import XSigninDialog from '../components/signin-dialog.vue';
import XSignupDialog from '../components/signup-dialog.vue';
import MkButton from '../components/ui/button.vue';
import XNotes from '../components/notes.vue';
-import i18n from '../i18n';
import { host } from '../config';
export default Vue.extend({
- i18n,
-
components: {
MkButton,
XNotes,
diff --git a/src/client/pages/index.welcome.setup.vue b/src/client/pages/index.welcome.setup.vue
index 6d08f5b5d4..9a66a4dffb 100644
--- a/src/client/pages/index.welcome.setup.vue
+++ b/src/client/pages/index.welcome.setup.vue
@@ -25,10 +25,8 @@ import { faLock } from '@fortawesome/free-solid-svg-icons';
import MkButton from '../components/ui/button.vue';
import MkInput from '../components/ui/input.vue';
import { host } from '../config';
-import i18n from '../i18n';
export default Vue.extend({
- i18n,
components: {
MkButton,
diff --git a/src/client/pages/instance/announcements.vue b/src/client/pages/instance/announcements.vue
index 2889cf8cce..0e11e2932e 100644
--- a/src/client/pages/instance/announcements.vue
+++ b/src/client/pages/instance/announcements.vue
@@ -28,14 +28,11 @@
import Vue from 'vue';
import { faBroadcastTower, faPlus } from '@fortawesome/free-solid-svg-icons';
import { faSave, faTrashAlt } from '@fortawesome/free-regular-svg-icons';
-import i18n from '../../i18n';
import MkButton from '../../components/ui/button.vue';
import MkInput from '../../components/ui/input.vue';
import MkTextarea from '../../components/ui/textarea.vue';
export default Vue.extend({
- i18n,
-
metaInfo() {
return {
title: this.$t('announcements') as string
diff --git a/src/client/pages/instance/federation.instance.vue b/src/client/pages/instance/federation.instance.vue
index 08f4d1b4fb..6b6352a151 100644
--- a/src/client/pages/instance/federation.instance.vue
+++ b/src/client/pages/instance/federation.instance.vue
@@ -120,7 +120,6 @@
<script lang="ts">
import Vue from 'vue';
import Chart from 'chart.js';
-import i18n from '../../i18n';
import { faTimes, faCrosshairs, faCloudDownloadAlt, faCloudUploadAlt, faUsers, faPencilAlt, faFileImage, faDatabase, faTrafficLight, faLongArrowAltUp, faLongArrowAltDown, faMinusCircle, faTrashAlt } from '@fortawesome/free-solid-svg-icons';
import XWindow from '../../components/window.vue';
import MkUsersDialog from '../../components/users-dialog.vue';
@@ -141,8 +140,6 @@ const alpha = hex => {
};
export default Vue.extend({
- i18n,
-
components: {
XWindow,
MkSelect,
diff --git a/src/client/pages/instance/federation.vue b/src/client/pages/instance/federation.vue
index 5babc60453..77819235d7 100644
--- a/src/client/pages/instance/federation.vue
+++ b/src/client/pages/instance/federation.vue
@@ -62,7 +62,6 @@
<script lang="ts">
import Vue from 'vue';
import { faGlobe, faCircle, faExchangeAlt, faCaretDown, faCaretUp, faTrafficLight } from '@fortawesome/free-solid-svg-icons';
-import i18n from '../../i18n';
import MkButton from '../../components/ui/button.vue';
import MkInput from '../../components/ui/input.vue';
import MkSelect from '../../components/ui/select.vue';
@@ -70,8 +69,6 @@ import MkPagination from '../../components/ui/pagination.vue';
import MkInstanceInfo from './federation.instance.vue';
export default Vue.extend({
- i18n,
-
metaInfo() {
return {
title: this.$t('federation') as string
diff --git a/src/client/pages/instance/index.vue b/src/client/pages/instance/index.vue
index 1d90aa5537..d21f8d455e 100644
--- a/src/client/pages/instance/index.vue
+++ b/src/client/pages/instance/index.vue
@@ -107,7 +107,6 @@ import MkButton from '../../components/ui/button.vue';
import MkSelect from '../../components/ui/select.vue';
import MkInput from '../../components/ui/input.vue';
import { version, url } from '../../config';
-import i18n from '../../i18n';
const alpha = (hex, a) => {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)!;
@@ -118,8 +117,6 @@ const alpha = (hex, a) => {
};
export default Vue.extend({
- i18n,
-
metaInfo() {
return {
title: this.$t('instance') as string
diff --git a/src/client/pages/instance/queue.queue.vue b/src/client/pages/instance/queue.queue.vue
index 7f0fc7d2bc..1649d1e172 100644
--- a/src/client/pages/instance/queue.queue.vue
+++ b/src/client/pages/instance/queue.queue.vue
@@ -25,7 +25,6 @@
<script lang="ts">
import Vue from 'vue';
import Chart from 'chart.js';
-import i18n from '../../i18n';
const alpha = (hex, a) => {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)!;
@@ -36,8 +35,6 @@ const alpha = (hex, a) => {
};
export default Vue.extend({
- i18n,
-
props: {
domain: {
required: true
diff --git a/src/client/pages/instance/queue.vue b/src/client/pages/instance/queue.vue
index c4892e88db..7a2204e519 100644
--- a/src/client/pages/instance/queue.vue
+++ b/src/client/pages/instance/queue.vue
@@ -21,13 +21,10 @@
import Vue from 'vue';
import { faExchangeAlt } from '@fortawesome/free-solid-svg-icons';
import { faTrashAlt } from '@fortawesome/free-regular-svg-icons';
-import i18n from '../../i18n';
import MkButton from '../../components/ui/button.vue';
import XQueue from './queue.queue.vue';
export default Vue.extend({
- i18n,
-
metaInfo() {
return {
title: `${this.$t('jobQueue')} | ${this.$t('instance')}`
diff --git a/src/client/pages/instance/relays.vue b/src/client/pages/instance/relays.vue
index 9b523bd0ec..dd18867b6a 100644
--- a/src/client/pages/instance/relays.vue
+++ b/src/client/pages/instance/relays.vue
@@ -28,13 +28,10 @@
import Vue from 'vue';
import { faPlus, faProjectDiagram } from '@fortawesome/free-solid-svg-icons';
import { faSave, faTrashAlt } from '@fortawesome/free-regular-svg-icons';
-import i18n from '../../i18n';
import MkButton from '../../components/ui/button.vue';
import MkInput from '../../components/ui/input.vue';
export default Vue.extend({
- i18n,
-
metaInfo() {
return {
title: this.$t('relays') as string
diff --git a/src/client/pages/instance/settings.vue b/src/client/pages/instance/settings.vue
index afd6d4cc6d..0436e87804 100644
--- a/src/client/pages/instance/settings.vue
+++ b/src/client/pages/instance/settings.vue
@@ -210,12 +210,9 @@ import MkSwitch from '../../components/ui/switch.vue';
import MkInfo from '../../components/ui/info.vue';
import MkUserSelect from '../../components/user-select.vue';
import { url } from '../../config';
-import i18n from '../../i18n';
import getAcct from '../../../misc/acct/render';
export default Vue.extend({
- i18n,
-
metaInfo() {
return {
title: this.$t('instance') as string
diff --git a/src/client/pages/instance/users.user.vue b/src/client/pages/instance/users.user.vue
index 1fb064f7f0..25f0260637 100644
--- a/src/client/pages/instance/users.user.vue
+++ b/src/client/pages/instance/users.user.vue
@@ -39,12 +39,9 @@ import { faTimes, faBookmark, faKey, faSync, faMicrophoneSlash, faExternalLinkSq
import { faSnowflake, faTrashAlt, faBookmark as farBookmark } from '@fortawesome/free-regular-svg-icons';
import MkButton from '../../components/ui/button.vue';
import MkSwitch from '../../components/ui/switch.vue';
-import i18n from '../../i18n';
import Progress from '../../scripts/loading';
export default Vue.extend({
- i18n,
-
components: {
MkButton,
MkSwitch,
diff --git a/src/client/pages/messaging/index.vue b/src/client/pages/messaging/index.vue
index 7a55004cbf..7ee782c4a9 100644
--- a/src/client/pages/messaging/index.vue
+++ b/src/client/pages/messaging/index.vue
@@ -42,14 +42,11 @@
<script lang="ts">
import Vue from 'vue';
import { faUser, faUsers, faComments, faPlus } from '@fortawesome/free-solid-svg-icons';
-import i18n from '../../i18n';
import getAcct from '../../../misc/acct/render';
import MkButton from '../../components/ui/button.vue';
import MkUserSelect from '../../components/user-select.vue';
export default Vue.extend({
- i18n,
-
components: {
MkButton
},
diff --git a/src/client/pages/messaging/messaging-room.form.vue b/src/client/pages/messaging/messaging-room.form.vue
index 0cd3dfcc85..be47efd2ce 100644
--- a/src/client/pages/messaging/messaging-room.form.vue
+++ b/src/client/pages/messaging/messaging-room.form.vue
@@ -27,12 +27,10 @@ import Vue from 'vue';
import { faPaperPlane, faPhotoVideo, faLaughSquint } from '@fortawesome/free-solid-svg-icons';
import insertTextAtCursor from 'insert-text-at-cursor';
import * as autosize from 'autosize';
-import i18n from '../../i18n';
import { formatTimeString } from '../../../misc/format-time-string';
import { selectFile } from '../../scripts/select-file';
export default Vue.extend({
- i18n,
components: {
XUploader: () => import('../../components/uploader.vue').then(m => m.default),
},
diff --git a/src/client/pages/messaging/messaging-room.message.vue b/src/client/pages/messaging/messaging-room.message.vue
index 67756572ff..58e1e54ad8 100644
--- a/src/client/pages/messaging/messaging-room.message.vue
+++ b/src/client/pages/messaging/messaging-room.message.vue
@@ -38,13 +38,11 @@
<script lang="ts">
import Vue from 'vue';
-import i18n from '../../i18n';
import { parse } from '../../../mfm/parse';
import { unique } from '../../../prelude/array';
import MkUrlPreview from '../../components/url-preview.vue';
export default Vue.extend({
- i18n,
components: {
MkUrlPreview
},
diff --git a/src/client/pages/messaging/messaging-room.vue b/src/client/pages/messaging/messaging-room.vue
index 317ad087fe..e97d5532ac 100644
--- a/src/client/pages/messaging/messaging-room.vue
+++ b/src/client/pages/messaging/messaging-room.vue
@@ -37,7 +37,6 @@
<script lang="ts">
import Vue from 'vue';
import { faArrowCircleDown, faFlag, faUsers, faInfoCircle } from '@fortawesome/free-solid-svg-icons';
-import i18n from '../../i18n';
import XList from '../../components/date-separated-list.vue';
import XMessage from './messaging-room.message.vue';
import XForm from './messaging-room.form.vue';
@@ -45,8 +44,6 @@ import { url } from '../../config';
import parseAcct from '../../../misc/acct/parse';
export default Vue.extend({
- i18n,
-
components: {
XMessage,
XForm,
diff --git a/src/client/pages/miauth.vue b/src/client/pages/miauth.vue
index 0e170af11a..15cde8bc25 100644
--- a/src/client/pages/miauth.vue
+++ b/src/client/pages/miauth.vue
@@ -40,12 +40,10 @@
<script lang="ts">
import Vue from 'vue';
-import i18n from '../i18n';
import MkSignin from '../components/signin.vue';
import MkButton from '../components/ui/button.vue';
export default Vue.extend({
- i18n,
components: {
MkSignin,
MkButton,
diff --git a/src/client/pages/my-antennas/index.antenna.vue b/src/client/pages/my-antennas/index.antenna.vue
index 2a9aebbcbf..6435e4fc9a 100644
--- a/src/client/pages/my-antennas/index.antenna.vue
+++ b/src/client/pages/my-antennas/index.antenna.vue
@@ -48,7 +48,6 @@
<script lang="ts">
import Vue from 'vue';
import { faSave, faTrash } from '@fortawesome/free-solid-svg-icons';
-import i18n from '../../i18n';
import MkButton from '../../components/ui/button.vue';
import MkInput from '../../components/ui/input.vue';
import MkTextarea from '../../components/ui/textarea.vue';
@@ -58,8 +57,6 @@ import MkUserSelect from '../../components/user-select.vue';
import getAcct from '../../../misc/acct/render';
export default Vue.extend({
- i18n,
-
components: {
MkButton, MkInput, MkTextarea, MkSelect, MkSwitch
},
diff --git a/src/client/pages/my-groups/group.vue b/src/client/pages/my-groups/group.vue
index c8170a2a57..0132bc2c33 100644
--- a/src/client/pages/my-groups/group.vue
+++ b/src/client/pages/my-groups/group.vue
@@ -41,14 +41,11 @@
<script lang="ts">
import Vue from 'vue';
import { faTimes, faUsers } from '@fortawesome/free-solid-svg-icons';
-import i18n from '../../i18n';
import Progress from '../../scripts/loading';
import MkButton from '../../components/ui/button.vue';
import MkUserSelect from '../../components/user-select.vue';
export default Vue.extend({
- i18n,
-
metaInfo() {
return {
title: this.group ? `${this.group.name} | ${this.$t('manageGroups')}` : this.$t('manageGroups')
diff --git a/src/client/pages/my-lists/list.vue b/src/client/pages/my-lists/list.vue
index cf85a80ccb..7052c55160 100644
--- a/src/client/pages/my-lists/list.vue
+++ b/src/client/pages/my-lists/list.vue
@@ -40,14 +40,11 @@
<script lang="ts">
import Vue from 'vue';
import { faTimes, faListUl } from '@fortawesome/free-solid-svg-icons';
-import i18n from '../../i18n';
import Progress from '../../scripts/loading';
import MkButton from '../../components/ui/button.vue';
import MkUserSelect from '../../components/user-select.vue';
export default Vue.extend({
- i18n,
-
metaInfo() {
return {
title: this.list ? `${this.list.name} | ${this.$t('manageLists')}` : this.$t('manageLists')
diff --git a/src/client/pages/my-settings/2fa.vue b/src/client/pages/my-settings/2fa.vue
index 6ceca21fe6..58ba03c41c 100644
--- a/src/client/pages/my-settings/2fa.vue
+++ b/src/client/pages/my-settings/2fa.vue
@@ -65,7 +65,6 @@
<script lang="ts">
import Vue from 'vue';
import { faLock } from '@fortawesome/free-solid-svg-icons';
-import i18n from '../../i18n';
import { hostname } from '../../config';
import { byteify, hexify, stringify } from '../../scripts/2fa';
import MkButton from '../../components/ui/button.vue';
@@ -74,7 +73,6 @@ import MkInput from '../../components/ui/input.vue';
import MkSwitch from '../../components/ui/switch.vue';
export default Vue.extend({
- i18n,
components: {
MkButton, MkInfo, MkInput, MkSwitch
},
diff --git a/src/client/pages/my-settings/api.vue b/src/client/pages/my-settings/api.vue
index f394c826de..79b459fb5e 100644
--- a/src/client/pages/my-settings/api.vue
+++ b/src/client/pages/my-settings/api.vue
@@ -13,12 +13,10 @@
<script lang="ts">
import Vue from 'vue';
import { faKey, faSyncAlt } from '@fortawesome/free-solid-svg-icons';
-import i18n from '../../i18n';
import MkButton from '../../components/ui/button.vue';
import MkInput from '../../components/ui/input.vue';
export default Vue.extend({
- i18n,
components: {
MkButton, MkInput
},
diff --git a/src/client/pages/my-settings/drive.vue b/src/client/pages/my-settings/drive.vue
index c3d2d1dc2d..7612c5011f 100644
--- a/src/client/pages/my-settings/drive.vue
+++ b/src/client/pages/my-settings/drive.vue
@@ -13,12 +13,9 @@ import Vue from 'vue';
import { faCloud, faFolderOpen } from '@fortawesome/free-solid-svg-icons';
import { faClock, faEyeSlash, faTrashAlt } from '@fortawesome/free-regular-svg-icons';
import MkButton from '../../components/ui/button.vue';
-import i18n from '../../i18n';
import { selectDriveFolder } from '../../scripts/select-drive-folder';
export default Vue.extend({
- i18n,
-
components: {
MkButton,
},
diff --git a/src/client/pages/my-settings/import-export.vue b/src/client/pages/my-settings/import-export.vue
index 4795741189..cc148d48d4 100644
--- a/src/client/pages/my-settings/import-export.vue
+++ b/src/client/pages/my-settings/import-export.vue
@@ -21,12 +21,9 @@ import Vue from 'vue';
import { faDownload, faUpload, faBoxes } from '@fortawesome/free-solid-svg-icons';
import MkButton from '../../components/ui/button.vue';
import MkSelect from '../../components/ui/select.vue';
-import i18n from '../../i18n';
import { apiUrl } from '../../config';
export default Vue.extend({
- i18n,
-
components: {
MkButton,
MkSelect,
diff --git a/src/client/pages/my-settings/integration.vue b/src/client/pages/my-settings/integration.vue
index 3dd7783f12..2d6e57e22c 100644
--- a/src/client/pages/my-settings/integration.vue
+++ b/src/client/pages/my-settings/integration.vue
@@ -29,13 +29,10 @@
import Vue from 'vue';
import { faShareAlt } from '@fortawesome/free-solid-svg-icons';
import { faTwitter, faDiscord, faGithub } from '@fortawesome/free-brands-svg-icons';
-import i18n from '../../i18n';
import { apiUrl } from '../../config';
import MkButton from '../../components/ui/button.vue';
export default Vue.extend({
- i18n,
-
components: {
MkButton
},
diff --git a/src/client/pages/my-settings/mute-block.vue b/src/client/pages/my-settings/mute-block.vue
index 03cf4aacc8..8eb43a6e29 100644
--- a/src/client/pages/my-settings/mute-block.vue
+++ b/src/client/pages/my-settings/mute-block.vue
@@ -34,11 +34,8 @@
import Vue from 'vue';
import { faBan } from '@fortawesome/free-solid-svg-icons';
import MkPagination from '../../components/ui/pagination.vue';
-import i18n from '../../i18n';
export default Vue.extend({
- i18n,
-
components: {
MkPagination,
},
diff --git a/src/client/pages/my-settings/privacy.vue b/src/client/pages/my-settings/privacy.vue
index 7ac9062d88..527ac9ea37 100644
--- a/src/client/pages/my-settings/privacy.vue
+++ b/src/client/pages/my-settings/privacy.vue
@@ -24,11 +24,8 @@ import Vue from 'vue';
import { faLock } from '@fortawesome/free-solid-svg-icons';
import MkSelect from '../../components/ui/select.vue';
import MkSwitch from '../../components/ui/switch.vue';
-import i18n from '../../i18n';
export default Vue.extend({
- i18n,
-
components: {
MkSelect,
MkSwitch,
diff --git a/src/client/pages/my-settings/profile.vue b/src/client/pages/my-settings/profile.vue
index b168c89ec0..16bba7a270 100644
--- a/src/client/pages/my-settings/profile.vue
+++ b/src/client/pages/my-settings/profile.vue
@@ -62,13 +62,10 @@ import MkButton from '../../components/ui/button.vue';
import MkInput from '../../components/ui/input.vue';
import MkTextarea from '../../components/ui/textarea.vue';
import MkSwitch from '../../components/ui/switch.vue';
-import i18n from '../../i18n';
import { host } from '../../config';
import { selectFile } from '../../scripts/select-file';
export default Vue.extend({
- i18n,
-
components: {
MkButton,
MkInput,
diff --git a/src/client/pages/my-settings/reaction.vue b/src/client/pages/my-settings/reaction.vue
index 68c481707c..ef4f6f6723 100644
--- a/src/client/pages/my-settings/reaction.vue
+++ b/src/client/pages/my-settings/reaction.vue
@@ -21,13 +21,10 @@ import { faUndo } from '@fortawesome/free-solid-svg-icons';
import MkInput from '../../components/ui/input.vue';
import MkButton from '../../components/ui/button.vue';
import MkReactionPicker from '../../components/reaction-picker.vue';
-import i18n from '../../i18n';
import { emojiRegexWithCustom } from '../../../misc/emoji-regex';
import { defaultSettings } from '../../store';
export default Vue.extend({
- i18n,
-
components: {
MkInput,
MkButton,
diff --git a/src/client/pages/my-settings/security.vue b/src/client/pages/my-settings/security.vue
index ba670b2f68..dc77ca12c5 100644
--- a/src/client/pages/my-settings/security.vue
+++ b/src/client/pages/my-settings/security.vue
@@ -11,11 +11,8 @@
import Vue from 'vue';
import { faLock } from '@fortawesome/free-solid-svg-icons';
import MkButton from '../../components/ui/button.vue';
-import i18n from '../../i18n';
export default Vue.extend({
- i18n,
-
components: {
MkButton,
},
diff --git a/src/client/pages/not-found.vue b/src/client/pages/not-found.vue
index 9608e07786..7f4c46c23d 100644
--- a/src/client/pages/not-found.vue
+++ b/src/client/pages/not-found.vue
@@ -15,11 +15,8 @@
<script lang="ts">
import Vue from 'vue';
import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';
-import i18n from '../i18n';
export default Vue.extend({
- i18n,
-
metaInfo() {
return {
title: this.$t('notFound') as string
diff --git a/src/client/pages/note.vue b/src/client/pages/note.vue
index 37c66833e9..48629a4ebe 100644
--- a/src/client/pages/note.vue
+++ b/src/client/pages/note.vue
@@ -29,14 +29,12 @@
<script lang="ts">
import Vue from 'vue';
import { faChevronUp, faChevronDown } from '@fortawesome/free-solid-svg-icons';
-import i18n from '../i18n';
import Progress from '../scripts/loading';
import XNote from '../components/note.vue';
import XNotes from '../components/notes.vue';
import MkRemoteCaution from '../components/remote-caution.vue';
export default Vue.extend({
- i18n,
metaInfo() {
return {
title: this.$t('note') as string
diff --git a/src/client/pages/page-editor/els/page-editor.el.button.vue b/src/client/pages/page-editor/els/page-editor.el.button.vue
index 9ca9fe06f3..9821201666 100644
--- a/src/client/pages/page-editor/els/page-editor.el.button.vue
+++ b/src/client/pages/page-editor/els/page-editor.el.button.vue
@@ -40,15 +40,12 @@
<script lang="ts">
import Vue from 'vue';
import { faBolt } from '@fortawesome/free-solid-svg-icons';
-import i18n from '../../../i18n';
import XContainer from '../page-editor.container.vue';
import MkSelect from '../../../components/ui/select.vue';
import MkInput from '../../../components/ui/input.vue';
import MkSwitch from '../../../components/ui/switch.vue';
export default Vue.extend({
- i18n,
-
components: {
XContainer, MkSelect, MkInput, MkSwitch
},
diff --git a/src/client/pages/page-editor/els/page-editor.el.canvas.vue b/src/client/pages/page-editor/els/page-editor.el.canvas.vue
index 4977318919..a499207806 100644
--- a/src/client/pages/page-editor/els/page-editor.el.canvas.vue
+++ b/src/client/pages/page-editor/els/page-editor.el.canvas.vue
@@ -13,13 +13,10 @@
<script lang="ts">
import Vue from 'vue';
import { faPaintBrush, faMagic } from '@fortawesome/free-solid-svg-icons';
-import i18n from '../../../i18n';
import XContainer from '../page-editor.container.vue';
import MkInput from '../../../components/ui/input.vue';
export default Vue.extend({
- i18n,
-
components: {
XContainer, MkInput
},
diff --git a/src/client/pages/page-editor/els/page-editor.el.counter.vue b/src/client/pages/page-editor/els/page-editor.el.counter.vue
index d9a4ddddee..f439f3e6ff 100644
--- a/src/client/pages/page-editor/els/page-editor.el.counter.vue
+++ b/src/client/pages/page-editor/els/page-editor.el.counter.vue
@@ -13,13 +13,10 @@
<script lang="ts">
import Vue from 'vue';
import { faBolt, faMagic } from '@fortawesome/free-solid-svg-icons';
-import i18n from '../../../i18n';
import XContainer from '../page-editor.container.vue';
import MkInput from '../../../components/ui/input.vue';
export default Vue.extend({
- i18n,
-
components: {
XContainer, MkInput
},
diff --git a/src/client/pages/page-editor/els/page-editor.el.if.vue b/src/client/pages/page-editor/els/page-editor.el.if.vue
index 0449b9cf2b..53cb9e2aee 100644
--- a/src/client/pages/page-editor/els/page-editor.el.if.vue
+++ b/src/client/pages/page-editor/els/page-editor.el.if.vue
@@ -28,13 +28,10 @@
import Vue from 'vue';
import { v4 as uuid } from 'uuid';
import { faPlus, faQuestion } from '@fortawesome/free-solid-svg-icons';
-import i18n from '../../../i18n';
import XContainer from '../page-editor.container.vue';
import MkSelect from '../../../components/ui/select.vue';
export default Vue.extend({
- i18n,
-
components: {
XContainer, MkSelect
},
diff --git a/src/client/pages/page-editor/els/page-editor.el.image.vue b/src/client/pages/page-editor/els/page-editor.el.image.vue
index e22701e5c0..dd690da6f1 100644
--- a/src/client/pages/page-editor/els/page-editor.el.image.vue
+++ b/src/client/pages/page-editor/els/page-editor.el.image.vue
@@ -17,14 +17,11 @@
import Vue from 'vue';
import { faPencilAlt } from '@fortawesome/free-solid-svg-icons';
import { faImage, faFolderOpen } from '@fortawesome/free-regular-svg-icons';
-import i18n from '../../../i18n';
import XContainer from '../page-editor.container.vue';
import MkFileThumbnail from '../../../components/drive-file-thumbnail.vue';
import { selectDriveFile } from '../../../scripts/select-drive-file';
export default Vue.extend({
- i18n,
-
components: {
XContainer, MkFileThumbnail
},
diff --git a/src/client/pages/page-editor/els/page-editor.el.number-input.vue b/src/client/pages/page-editor/els/page-editor.el.number-input.vue
index 76dd254464..62d2e1bf8a 100644
--- a/src/client/pages/page-editor/els/page-editor.el.number-input.vue
+++ b/src/client/pages/page-editor/els/page-editor.el.number-input.vue
@@ -13,13 +13,10 @@
<script lang="ts">
import Vue from 'vue';
import { faBolt, faMagic } from '@fortawesome/free-solid-svg-icons';
-import i18n from '../../../i18n';
import XContainer from '../page-editor.container.vue';
import MkInput from '../../../components/ui/input.vue';
export default Vue.extend({
- i18n,
-
components: {
XContainer, MkInput
},
diff --git a/src/client/pages/page-editor/els/page-editor.el.post.vue b/src/client/pages/page-editor/els/page-editor.el.post.vue
index 2c6ce24e95..06dea51c1f 100644
--- a/src/client/pages/page-editor/els/page-editor.el.post.vue
+++ b/src/client/pages/page-editor/els/page-editor.el.post.vue
@@ -13,15 +13,12 @@
<script lang="ts">
import Vue from 'vue';
import { faPaperPlane } from '@fortawesome/free-regular-svg-icons';
-import i18n from '../../../i18n';
import XContainer from '../page-editor.container.vue';
import MkTextarea from '../../../components/ui/textarea.vue';
import MkInput from '../../../components/ui/input.vue';
import MkSwitch from '../../../components/ui/switch.vue';
export default Vue.extend({
- i18n,
-
components: {
XContainer, MkTextarea, MkInput, MkSwitch
},
diff --git a/src/client/pages/page-editor/els/page-editor.el.radio-button.vue b/src/client/pages/page-editor/els/page-editor.el.radio-button.vue
index 8d404ec0df..34a9366d62 100644
--- a/src/client/pages/page-editor/els/page-editor.el.radio-button.vue
+++ b/src/client/pages/page-editor/els/page-editor.el.radio-button.vue
@@ -14,13 +14,11 @@
<script lang="ts">
import Vue from 'vue';
import { faBolt, faMagic } from '@fortawesome/free-solid-svg-icons';
-import i18n from '../../../i18n';
import XContainer from '../page-editor.container.vue';
import MkTextarea from '../../../components/ui/textarea.vue';
import MkInput from '../../../components/ui/input.vue';
export default Vue.extend({
- i18n,
components: {
XContainer, MkTextarea, MkInput
},
diff --git a/src/client/pages/page-editor/els/page-editor.el.section.vue b/src/client/pages/page-editor/els/page-editor.el.section.vue
index a32cf9c753..e89a8b840c 100644
--- a/src/client/pages/page-editor/els/page-editor.el.section.vue
+++ b/src/client/pages/page-editor/els/page-editor.el.section.vue
@@ -21,12 +21,9 @@ import Vue from 'vue';
import { v4 as uuid } from 'uuid';
import { faPlus, faPencilAlt } from '@fortawesome/free-solid-svg-icons';
import { faStickyNote } from '@fortawesome/free-regular-svg-icons';
-import i18n from '../../../i18n';
import XContainer from '../page-editor.container.vue';
export default Vue.extend({
- i18n,
-
components: {
XContainer
},
diff --git a/src/client/pages/page-editor/els/page-editor.el.switch.vue b/src/client/pages/page-editor/els/page-editor.el.switch.vue
index 8f169c3d23..5055da4f6f 100644
--- a/src/client/pages/page-editor/els/page-editor.el.switch.vue
+++ b/src/client/pages/page-editor/els/page-editor.el.switch.vue
@@ -13,14 +13,11 @@
<script lang="ts">
import Vue from 'vue';
import { faBolt, faMagic } from '@fortawesome/free-solid-svg-icons';
-import i18n from '../../../i18n';
import XContainer from '../page-editor.container.vue';
import MkSwitch from '../../../components/ui/switch.vue';
import MkInput from '../../../components/ui/input.vue';
export default Vue.extend({
- i18n,
-
components: {
XContainer, MkSwitch, MkInput
},
diff --git a/src/client/pages/page-editor/els/page-editor.el.text-input.vue b/src/client/pages/page-editor/els/page-editor.el.text-input.vue
index 7c9e3d6a0e..bd5fb37617 100644
--- a/src/client/pages/page-editor/els/page-editor.el.text-input.vue
+++ b/src/client/pages/page-editor/els/page-editor.el.text-input.vue
@@ -13,13 +13,10 @@
<script lang="ts">
import Vue from 'vue';
import { faBolt, faMagic } from '@fortawesome/free-solid-svg-icons';
-import i18n from '../../../i18n';
import XContainer from '../page-editor.container.vue';
import MkInput from '../../../components/ui/input.vue';
export default Vue.extend({
- i18n,
-
components: {
XContainer, MkInput
},
diff --git a/src/client/pages/page-editor/els/page-editor.el.text.vue b/src/client/pages/page-editor/els/page-editor.el.text.vue
index c6722236eb..a50b1113bd 100644
--- a/src/client/pages/page-editor/els/page-editor.el.text.vue
+++ b/src/client/pages/page-editor/els/page-editor.el.text.vue
@@ -11,12 +11,9 @@
<script lang="ts">
import Vue from 'vue';
import { faAlignLeft } from '@fortawesome/free-solid-svg-icons';
-import i18n from '../../../i18n';
import XContainer from '../page-editor.container.vue';
export default Vue.extend({
- i18n,
-
components: {
XContainer
},
diff --git a/src/client/pages/page-editor/els/page-editor.el.textarea-input.vue b/src/client/pages/page-editor/els/page-editor.el.textarea-input.vue
index 8081e706bc..33c49c705b 100644
--- a/src/client/pages/page-editor/els/page-editor.el.textarea-input.vue
+++ b/src/client/pages/page-editor/els/page-editor.el.textarea-input.vue
@@ -13,14 +13,11 @@
<script lang="ts">
import Vue from 'vue';
import { faBolt, faMagic } from '@fortawesome/free-solid-svg-icons';
-import i18n from '../../../i18n';
import XContainer from '../page-editor.container.vue';
import MkTextarea from '../../../components/ui/textarea.vue';
import MkInput from '../../../components/ui/input.vue';
export default Vue.extend({
- i18n,
-
components: {
XContainer, MkTextarea, MkInput
},
diff --git a/src/client/pages/page-editor/els/page-editor.el.textarea.vue b/src/client/pages/page-editor/els/page-editor.el.textarea.vue
index d31da5dfa3..e2e8848ccf 100644
--- a/src/client/pages/page-editor/els/page-editor.el.textarea.vue
+++ b/src/client/pages/page-editor/els/page-editor.el.textarea.vue
@@ -11,12 +11,9 @@
<script lang="ts">
import Vue from 'vue';
import { faAlignLeft } from '@fortawesome/free-solid-svg-icons';
-import i18n from '../../../i18n';
import XContainer from '../page-editor.container.vue';
export default Vue.extend({
- i18n,
-
components: {
XContainer
},
diff --git a/src/client/pages/page-editor/page-editor.container.vue b/src/client/pages/page-editor/page-editor.container.vue
index 5bc9744671..3fa09f5600 100644
--- a/src/client/pages/page-editor/page-editor.container.vue
+++ b/src/client/pages/page-editor/page-editor.container.vue
@@ -28,11 +28,8 @@
import Vue from 'vue';
import { faBars, faAngleUp, faAngleDown } from '@fortawesome/free-solid-svg-icons';
import { faTrashAlt } from '@fortawesome/free-regular-svg-icons';
-import i18n from '../../i18n';
export default Vue.extend({
- i18n,
-
props: {
expanded: {
type: Boolean,
diff --git a/src/client/pages/page-editor/page-editor.script-block.vue b/src/client/pages/page-editor/page-editor.script-block.vue
index 9eafd5daa0..f3270f02e3 100644
--- a/src/client/pages/page-editor/page-editor.script-block.vue
+++ b/src/client/pages/page-editor/page-editor.script-block.vue
@@ -59,14 +59,11 @@
import Vue from 'vue';
import { faPencilAlt, faPlug } from '@fortawesome/free-solid-svg-icons';
import { v4 as uuid } from 'uuid';
-import i18n from '../../i18n';
import XContainer from './page-editor.container.vue';
import MkTextarea from '../../components/ui/textarea.vue';
import { isLiteralBlock, funcDefs, blockDefs } from '../../scripts/hpml/index';
export default Vue.extend({
- i18n,
-
components: {
XContainer, MkTextarea
},
diff --git a/src/client/pages/page-editor/page-editor.vue b/src/client/pages/page-editor/page-editor.vue
index 4437c7716d..2beb2df389 100644
--- a/src/client/pages/page-editor/page-editor.vue
+++ b/src/client/pages/page-editor/page-editor.vue
@@ -91,7 +91,6 @@ import PrismEditor from 'vue-prism-editor';
import { faICursor, faPlus, faMagic, faCog, faCode, faExternalLinkSquareAlt } from '@fortawesome/free-solid-svg-icons';
import { faSave, faStickyNote, faTrashAlt } from '@fortawesome/free-regular-svg-icons';
import { v4 as uuid } from 'uuid';
-import i18n from '../../i18n';
import XVariable from './page-editor.script-block.vue';
import XBlocks from './page-editor.blocks.vue';
import MkTextarea from '../../components/ui/textarea.vue';
@@ -107,8 +106,6 @@ import { collectPageVars } from '../../scripts/collect-page-vars';
import { selectDriveFile } from '../../scripts/select-drive-file';
export default Vue.extend({
- i18n,
-
components: {
XDraggable, XVariable, XBlocks, MkTextarea, MkContainer, MkButton, MkSelect, MkSwitch, MkInput, PrismEditor
},
diff --git a/src/client/pages/pages.vue b/src/client/pages/pages.vue
index dd3d09db08..a261785715 100644
--- a/src/client/pages/pages.vue
+++ b/src/client/pages/pages.vue
@@ -28,14 +28,12 @@
import Vue from 'vue';
import { faPlus, faEdit } from '@fortawesome/free-solid-svg-icons';
import { faStickyNote, faHeart } from '@fortawesome/free-regular-svg-icons';
-import i18n from '../i18n';
import MkPagePreview from '../components/page-preview.vue';
import MkPagination from '../components/ui/pagination.vue';
import MkButton from '../components/ui/button.vue';
import MkContainer from '../components/ui/container.vue';
export default Vue.extend({
- i18n,
components: {
MkPagePreview, MkPagination, MkButton, MkContainer
},
diff --git a/src/client/pages/preferences/index.vue b/src/client/pages/preferences/index.vue
index 9f4bb67956..14d22bf02d 100644
--- a/src/client/pages/preferences/index.vue
+++ b/src/client/pages/preferences/index.vue
@@ -99,8 +99,8 @@ import MkRadio from '../../components/ui/radio.vue';
import MkRange from '../../components/ui/range.vue';
import XTheme from './theme.vue';
import XSidebar from './sidebar.vue';
-import i18n from '../../i18n';
import { langs } from '../../config';
+import { clientDb, set } from '../../db';
const sounds = [
null,
@@ -120,8 +120,6 @@ const sounds = [
];
export default Vue.extend({
- i18n,
-
metaInfo() {
return {
title: this.$t('settings') as string
@@ -228,9 +226,23 @@ export default Vue.extend({
watch: {
lang() {
+ const dialog = this.$root.dialog({
+ type: 'waiting',
+ iconOnly: true
+ });
+
localStorage.setItem('lang', this.lang);
- localStorage.removeItem('locale');
- location.reload();
+
+ return set('_version_', `changeLang-${(new Date()).toJSON()}`, clientDb.i18n)
+ .then(() => location.reload())
+ .catch(() => {
+ dialog.close();
+ this.$root.dialog({
+ type: 'error',
+ iconOnly: true,
+ autoClose: true
+ });
+ });
},
fontSize() {
diff --git a/src/client/pages/preferences/sidebar.vue b/src/client/pages/preferences/sidebar.vue
index 2dced10e7b..34c9916cf5 100644
--- a/src/client/pages/preferences/sidebar.vue
+++ b/src/client/pages/preferences/sidebar.vue
@@ -19,12 +19,9 @@ import Vue from 'vue';
import { faListUl, faSave, faRedo } from '@fortawesome/free-solid-svg-icons';
import MkButton from '../../components/ui/button.vue';
import MkTextarea from '../../components/ui/textarea.vue';
-import i18n from '../../i18n';
import { defaultDeviceUserSettings } from '../../store';
export default Vue.extend({
- i18n,
-
components: {
MkButton,
MkTextarea,
diff --git a/src/client/pages/preferences/theme.vue b/src/client/pages/preferences/theme.vue
index f35b5d6ed8..2111fa2248 100644
--- a/src/client/pages/preferences/theme.vue
+++ b/src/client/pages/preferences/theme.vue
@@ -87,14 +87,11 @@ import MkButton from '../../components/ui/button.vue';
import MkSelect from '../../components/ui/select.vue';
import MkSwitch from '../../components/ui/switch.vue';
import MkTextarea from '../../components/ui/textarea.vue';
-import i18n from '../../i18n';
import { Theme, builtinThemes, applyTheme, validateTheme } from '../../theme';
import { selectFile } from '../../scripts/select-file';
import { isDeviceDarkmode } from '../../scripts/is-device-darkmode';
export default Vue.extend({
- i18n,
-
components: {
MkInput,
MkButton,
diff --git a/src/client/pages/room/room.vue b/src/client/pages/room/room.vue
index 6ede771c56..cf6850526f 100644
--- a/src/client/pages/room/room.vue
+++ b/src/client/pages/room/room.vue
@@ -59,7 +59,6 @@
<script lang="ts">
import Vue from 'vue';
-import i18n from '../../i18n';
import { Room } from '../../scripts/room/room';
import parseAcct from '../../../misc/acct/parse';
import XPreview from './preview.vue';
@@ -74,8 +73,6 @@ import { selectFile } from '../../scripts/select-file';
let room: Room;
export default Vue.extend({
- i18n,
-
components: {
XPreview,
MkButton,
diff --git a/src/client/pages/scratchpad.vue b/src/client/pages/scratchpad.vue
index 622c40398f..81d4e60459 100644
--- a/src/client/pages/scratchpad.vue
+++ b/src/client/pages/scratchpad.vue
@@ -28,14 +28,11 @@ import "prismjs";
import 'prismjs/themes/prism-okaidia.css';
import PrismEditor from 'vue-prism-editor';
import { AiScript, parse, utils, values } from '@syuilo/aiscript';
-import i18n from '../i18n';
import MkContainer from '../components/ui/container.vue';
import MkButton from '../components/ui/button.vue';
import { createAiScriptEnv } from '../scripts/create-aiscript-env';
export default Vue.extend({
- i18n,
-
metaInfo() {
return {
title: this.$t('scratchpad') as string
diff --git a/src/client/pages/share.vue b/src/client/pages/share.vue
index 566650e309..153de76801 100644
--- a/src/client/pages/share.vue
+++ b/src/client/pages/share.vue
@@ -18,13 +18,10 @@
<script lang="ts">
import Vue from 'vue';
import { faShareAlt } from '@fortawesome/free-solid-svg-icons';
-import i18n from '../i18n';
import PostFormDialog from '../components/post-form-dialog.vue';
import MkButton from '../components/ui/button.vue';
export default Vue.extend({
- i18n,
-
metaInfo() {
return {
title: this.$t('share') as string
diff --git a/src/client/pages/user/follow-list.vue b/src/client/pages/user/follow-list.vue
index 3da0b8359d..666e2d04fe 100644
--- a/src/client/pages/user/follow-list.vue
+++ b/src/client/pages/user/follow-list.vue
@@ -19,13 +19,10 @@
<script lang="ts">
import Vue from 'vue';
import parseAcct from '../../../misc/acct/parse';
-import i18n from '../../i18n';
import MkFollowButton from '../../components/follow-button.vue';
import MkPagination from '../../components/ui/pagination.vue';
export default Vue.extend({
- i18n,
-
components: {
MkPagination,
MkFollowButton,
diff --git a/src/client/pages/user/index.photos.vue b/src/client/pages/user/index.photos.vue
index 07b4db0a93..83a2618403 100644
--- a/src/client/pages/user/index.photos.vue
+++ b/src/client/pages/user/index.photos.vue
@@ -14,11 +14,9 @@
<script lang="ts">
import Vue from 'vue';
-import i18n from '../../i18n';
import { getStaticImageUrl } from '../../scripts/get-static-image-url';
export default Vue.extend({
- i18n,
props: ['user'],
data() {
return {
diff --git a/src/client/scripts/compose-notification.ts b/src/client/scripts/compose-notification.ts
index bf32552506..29eb515bfe 100644
--- a/src/client/scripts/compose-notification.ts
+++ b/src/client/scripts/compose-notification.ts
@@ -1,58 +1,95 @@
import getNoteSummary from '../../misc/get-note-summary';
import getUserName from '../../misc/get-user-name';
+import { clientDb, get, bulkGet } from '../db';
+import { fromEntries } from '../../prelude/array';
-type Notification = {
- title: string;
- body: string;
- icon: string;
- onclick?: any;
-};
+const getTranslation = (text: string): Promise<string> => get(text, clientDb.i18n);
-// TODO: i18n
+export default async function(type, data): Promise<[string, NotificationOptions]> {
+ const contexts = ['deletedNote', 'invisibleNote', 'withNFiles', '_cw.poll'];
+ const locale = fromEntries(await bulkGet(contexts, clientDb.i18n) as [string, string][]);
-export default function(type, data): Notification {
switch (type) {
- case 'driveFileCreated':
- return {
- title: 'File uploaded',
+ case 'driveFileCreated': // TODO (Server Side)
+ return [await getTranslation('_notification.fileUploaded'), {
body: data.name,
icon: data.url
- };
-
+ }];
case 'notification':
switch (data.type) {
case 'mention':
- return {
- title: `${getUserName(data.user)}:`,
- body: getNoteSummary(data),
+ return [(await getTranslation('_notification.youGotMention')).replace('{name}', getUserName(data.user)), {
+ body: getNoteSummary(data.note, locale),
icon: data.user.avatarUrl
- };
+ }];
case 'reply':
- return {
- title: `You got reply from ${getUserName(data.user)}:`,
- body: getNoteSummary(data),
+ return [(await getTranslation('_notification.youGotReply')).replace('{name}', getUserName(data.user)), {
+ body: getNoteSummary(data.note, locale),
+ icon: data.user.avatarUrl
+ }];
+
+ case 'renote':
+ return [(await getTranslation('_notification.youRenoted')).replace('{name}', getUserName(data.user)), {
+ body: getNoteSummary(data.note, locale),
icon: data.user.avatarUrl
- };
+ }];
case 'quote':
- return {
- title: `${getUserName(data.user)}:`,
- body: getNoteSummary(data),
+ return [(await getTranslation('_notification.youGotQuote')).replace('{name}', getUserName(data.user)), {
+ body: getNoteSummary(data.note, locale),
icon: data.user.avatarUrl
- };
+ }];
case 'reaction':
- return {
- title: `${getUserName(data.user)}: ${data.reaction}:`,
- body: getNoteSummary(data.note),
+ return [`${data.reaction} ${getUserName(data.user)}`, {
+ body: getNoteSummary(data.note, locale),
+ icon: data.user.avatarUrl
+ }];
+
+ case 'pollVote':
+ return [(await getTranslation('_notification.youGotPoll')).replace('{name}', getUserName(data.user)), {
+ body: getNoteSummary(data.note, locale),
+ icon: data.user.avatarUrl
+ }];
+
+ case 'follow':
+ return [await getTranslation('_notification.youWereFollowed'), {
+ body: getUserName(data.user),
icon: data.user.avatarUrl
- };
+ }];
+
+ case 'receiveFollowRequest':
+ return [await getTranslation('_notification.youReceivedFollowRequest'), {
+ body: getUserName(data.user),
+ icon: data.user.avatarUrl
+ }];
+
+ case 'followRequestAccepted':
+ return [await getTranslation('_notification.yourFollowRequestAccepted'), {
+ body: getUserName(data.user),
+ icon: data.user.avatarUrl
+ }];
+
+ case 'groupInvited':
+ return [await getTranslation('_notification.youWereInvitedToGroup'), {
+ body: data.group.name
+ }];
default:
return null;
}
-
+ case 'unreadMessagingMessage':
+ if (data.groupId === null) {
+ return [(await getTranslation('_notification.youGotMessagingMessageFromUser')).replace('{name}', getUserName(data.user)), {
+ icon: data.user.avatarUrl,
+ tag: `messaging:user:${data.user.id}`
+ }];
+ }
+ return [(await getTranslation('_notification.youGotMessagingMessageFromGroup')).replace('{name}', data.group.name), {
+ icon: data.user.avatarUrl,
+ tag: `messaging:group:${data.group.id}`
+ }];
default:
return null;
}
diff --git a/src/client/scripts/set-i18n-contexts.ts b/src/client/scripts/set-i18n-contexts.ts
new file mode 100644
index 0000000000..2eb76047f1
--- /dev/null
+++ b/src/client/scripts/set-i18n-contexts.ts
@@ -0,0 +1,18 @@
+import VueI18n from 'vue-i18n';
+import { clientDb, clear, bulkSet } from '../db';
+import { deepEntries, delimitEntry } from 'deep-entries';
+import { fromEntries } from '../../prelude/array';
+
+export function setI18nContexts(lang: string, version: string, i18n: VueI18n, cleardb = false) {
+ return Promise.all([
+ cleardb ? clear(clientDb.i18n) : Promise.resolve(),
+ fetch(`/assets/locales/${lang}.${version}.json`)
+ ])
+ .then(([, response]) => response.json())
+ .then(locale => {
+ const flatLocaleEntries = deepEntries(locale, delimitEntry) as [string, string][];
+ bulkSet(flatLocaleEntries, clientDb.i18n);
+ i18n.locale = lang;
+ i18n.setLocaleMessage(lang, fromEntries(flatLocaleEntries));
+ });
+}
diff --git a/src/client/sw.js b/src/client/sw.ts
index 68e43429ac..341198852e 100644
--- a/src/client/sw.js
+++ b/src/client/sw.ts
@@ -1,6 +1,7 @@
/**
* Service Worker
*/
+declare var self: ServiceWorkerGlobalScope;
import composeNotification from './scripts/compose-notification';
@@ -14,7 +15,7 @@ const apiUrl = `${location.origin}/api/`;
self.addEventListener('install', ev => {
console.info('installed');
- ev.waitUntil(
+ ev.waitUntil(
caches.open(cacheName)
.then(cache => {
return cache.addAll([
@@ -22,7 +23,7 @@ self.addEventListener('install', ev => {
]);
})
.then(() => self.skipWaiting())
- );
+ );
});
self.addEventListener('activate', ev => {
@@ -55,16 +56,12 @@ self.addEventListener('push', ev => {
// クライアント取得
ev.waitUntil(self.clients.matchAll({
includeUncontrolled: true
- }).then(clients => {
+ }).then(async clients => {
// クライアントがあったらストリームに接続しているということなので通知しない
if (clients.length != 0) return;
const { type, body } = ev.data.json();
- const n = composeNotification(type, body);
- return self.registration.showNotification(n.title, {
- body: n.body,
- icon: n.icon,
- });
+ return self.registration.showNotification(...(await composeNotification(type, body)));
}));
});
diff --git a/src/client/tsconfig.json b/src/client/tsconfig.json
index 3ec0271f63..aac0d1bfe7 100644
--- a/src/client/tsconfig.json
+++ b/src/client/tsconfig.json
@@ -21,6 +21,11 @@
"typeRoots": [
"node_modules/@types",
"src/@types"
+ ],
+ "lib": [
+ "esnext",
+ "dom",
+ "webworker"
]
},
"compileOnSave": false,
diff --git a/src/client/widgets/activity.chart.vue b/src/client/widgets/activity.chart.vue
index 0278e02ae7..2b70493552 100644
--- a/src/client/widgets/activity.chart.vue
+++ b/src/client/widgets/activity.chart.vue
@@ -26,7 +26,6 @@
<script lang="ts">
import Vue from 'vue';
-import i18n from '../i18n';
function dragListen(fn) {
window.addEventListener('mousemove', fn);
@@ -41,7 +40,6 @@ function dragClear(fn) {
}
export default Vue.extend({
- i18n,
props: ['data'],
data() {
return {
diff --git a/src/client/widgets/activity.vue b/src/client/widgets/activity.vue
index 6c32642bb6..4fdd81ae52 100644
--- a/src/client/widgets/activity.vue
+++ b/src/client/widgets/activity.vue
@@ -19,7 +19,6 @@
import { faChartBar, faSort } from '@fortawesome/free-solid-svg-icons';
import MkContainer from '../components/ui/container.vue';
import define from './define';
-import i18n from '../i18n';
import XCalendar from './activity.calendar.vue';
import XChart from './activity.chart.vue';
@@ -30,7 +29,6 @@ export default define({
view: 0
})
}).extend({
- i18n,
components: {
MkContainer,
XCalendar,
diff --git a/src/client/widgets/calendar.vue b/src/client/widgets/calendar.vue
index c041734a4d..328e6bc62f 100644
--- a/src/client/widgets/calendar.vue
+++ b/src/client/widgets/calendar.vue
@@ -33,7 +33,6 @@
<script lang="ts">
import define from './define';
-import i18n from '../i18n';
export default define({
name: 'calendar',
@@ -41,7 +40,6 @@ export default define({
design: 0
})
}).extend({
- i18n,
data() {
return {
now: new Date(),
diff --git a/src/client/widgets/memo.vue b/src/client/widgets/memo.vue
index 3c170adc4e..cdc716b9fa 100644
--- a/src/client/widgets/memo.vue
+++ b/src/client/widgets/memo.vue
@@ -15,7 +15,6 @@
import { faStickyNote } from '@fortawesome/free-solid-svg-icons';
import MkContainer from '../components/ui/container.vue';
import define from './define';
-import i18n from '../i18n';
export default define({
name: 'memo',
@@ -23,7 +22,6 @@ export default define({
compact: false
})
}).extend({
- i18n,
components: {
MkContainer
diff --git a/src/client/widgets/notifications.vue b/src/client/widgets/notifications.vue
index 9c1bddb2ee..39fc8a9361 100644
--- a/src/client/widgets/notifications.vue
+++ b/src/client/widgets/notifications.vue
@@ -15,7 +15,6 @@ import { faBell } from '@fortawesome/free-solid-svg-icons';
import MkContainer from '../components/ui/container.vue';
import XNotifications from '../components/notifications.vue';
import define from './define';
-import i18n from '../i18n';
const basisSteps = [25, 50, 75, 100]
const previewHeights = [200, 300, 400, 500]
@@ -27,7 +26,6 @@ export default define({
basisStep: 0
})
}).extend({
- i18n,
components: {
MkContainer,
diff --git a/src/client/widgets/photos.vue b/src/client/widgets/photos.vue
index 1deb6de62d..6e4e43a565 100644
--- a/src/client/widgets/photos.vue
+++ b/src/client/widgets/photos.vue
@@ -20,7 +20,6 @@
import { faCamera } from '@fortawesome/free-solid-svg-icons';
import MkContainer from '../components/ui/container.vue';
import define from './define';
-import i18n from '../i18n';
import { getStaticImageUrl } from '../scripts/get-static-image-url';
export default define({
@@ -29,7 +28,6 @@ export default define({
design: 0,
})
}).extend({
- i18n,
components: {
MkContainer,
},
diff --git a/src/client/widgets/rss.vue b/src/client/widgets/rss.vue
index 61c1e23b6e..4e57281e9f 100644
--- a/src/client/widgets/rss.vue
+++ b/src/client/widgets/rss.vue
@@ -18,7 +18,6 @@
import { faRssSquare, faCog } from '@fortawesome/free-solid-svg-icons';
import MkContainer from '../components/ui/container.vue';
import define from './define';
-import i18n from '../i18n';
export default define({
name: 'rss',
@@ -27,7 +26,6 @@ export default define({
url: 'http://feeds.afpbb.com/rss/afpbb/afpbbnews'
})
}).extend({
- i18n,
components: {
MkContainer
},
diff --git a/src/client/widgets/timeline.vue b/src/client/widgets/timeline.vue
index 55f78f985f..6331311828 100644
--- a/src/client/widgets/timeline.vue
+++ b/src/client/widgets/timeline.vue
@@ -27,7 +27,6 @@ import { faComments } from '@fortawesome/free-regular-svg-icons';
import MkContainer from '../components/ui/container.vue';
import XTimeline from '../components/timeline.vue';
import define from './define';
-import i18n from '../i18n';
const basisSteps = [25, 50, 75, 100]
const previewHeights = [200, 300, 400, 500]
@@ -41,7 +40,6 @@ export default define({
basisStep: 0
})
}).extend({
- i18n,
components: {
MkContainer,
diff --git a/src/client/widgets/trends.vue b/src/client/widgets/trends.vue
index 690383d1f9..61f5bfbd32 100644
--- a/src/client/widgets/trends.vue
+++ b/src/client/widgets/trends.vue
@@ -23,7 +23,6 @@
import { faHashtag } from '@fortawesome/free-solid-svg-icons';
import MkContainer from '../components/ui/container.vue';
import define from './define';
-import i18n from '../i18n';
import XChart from './trends.chart.vue';
export default define({
@@ -32,7 +31,6 @@ export default define({
compact: false
})
}).extend({
- i18n,
components: {
MkContainer, XChart
},
diff --git a/src/misc/get-note-summary.ts b/src/misc/get-note-summary.ts
index e3458cb189..c23306ab11 100644
--- a/src/misc/get-note-summary.ts
+++ b/src/misc/get-note-summary.ts
@@ -2,13 +2,13 @@
* 投稿を表す文字列を取得します。
* @param {*} note (packされた)投稿
*/
-const summarize = (note: any): string => {
+const summarize = (note: any, locale: any): string => {
if (note.deletedAt) {
- return '(削除された投稿)';
+ return `(${locale['deletedNote']})`;
}
if (note.isHidden) {
- return '(非公開の投稿)';
+ return `(${locale['invisibleNote']})`;
}
let summary = '';
@@ -22,18 +22,18 @@ const summarize = (note: any): string => {
// ファイルが添付されているとき
if ((note.files || []).length != 0) {
- summary += ` (${note.files.length}つのファイル)`;
+ summary += ` (${locale['withNFiles'].replace('{n}', note.files.length)})`;
}
// 投票が添付されているとき
if (note.poll) {
- summary += ' (投票)';
+ summary += ` (${locale._cw?.poll || locale['_cw.poll']})`;
}
// 返信のとき
if (note.replyId) {
if (note.reply) {
- summary += `\n\nRE: ${summarize(note.reply)}`;
+ summary += `\n\nRE: ${summarize(note.reply, locale)}`;
} else {
summary += '\n\nRE: ...';
}
@@ -42,7 +42,7 @@ const summarize = (note: any): string => {
// Renoteのとき
if (note.renoteId) {
if (note.renote) {
- summary += `\n\nRN: ${summarize(note.renote)}`;
+ summary += `\n\nRN: ${summarize(note.renote, locale)}`;
} else {
summary += '\n\nRN: ...';
}
diff --git a/src/prelude/array.ts b/src/prelude/array.ts
index f4d684d574..9e1dfead53 100644
--- a/src/prelude/array.ts
+++ b/src/prelude/array.ts
@@ -130,7 +130,17 @@ export function cumulativeSum(xs: number[]): number[] {
}
// Object.fromEntries()
-export function fromEntries(xs: [string, any][]): { [x: string]: any; } {
+export function fromEntries<T extends readonly (readonly [PropertyKey, any])[]>(xs: T):
+ T[number] extends infer U
+ ?
+ (
+ U extends readonly any[]
+ ? (x: { [_ in U[0]]: U[1] }) => any
+ : never
+ ) extends (x: infer V) => any
+ ? V
+ : never
+ : never {
return xs.reduce((obj, [k, v]) => Object.assign(obj, { [k]: v }), {} as { [x: string]: any; });
}
diff --git a/src/server/web/index.ts b/src/server/web/index.ts
index 3da86944d7..5bb052a693 100644
--- a/src/server/web/index.ts
+++ b/src/server/web/index.ts
@@ -245,7 +245,8 @@ router.get('/notes/:note', async ctx => {
const meta = await fetchMeta();
await ctx.render('note', {
note: _note,
- summary: getNoteSummary(_note),
+ // TODO: Let locale changeable by instance setting
+ summary: getNoteSummary(_note, locales['ja-JP']),
instanceName: meta.name || 'Misskey',
icon: meta.iconUrl
});
diff --git a/src/server/web/views/flush.pug b/src/server/web/views/flush.pug
index f279c23605..59fed1f15d 100644
--- a/src/server/web/views/flush.pug
+++ b/src/server/web/views/flush.pug
@@ -1,20 +1,38 @@
doctype html
html
+ #msg
script.
- localStorage.removeItem('locale');
+ const msg = document.getElementById('msg');
try {
- navigator.serviceWorker.controller.postMessage('clear');
+ localStorage.clear();
+ message('localStorage cleared');
- navigator.serviceWorker.getRegistrations().then(registrations => {
- return Promise.all(registrations.map(registration => registration.unregister()));
- }).then(() => {
- location = '/';
- });
+ const delidb = indexedDB.deleteDatabase('MisskeyClient');
+ delidb.onsuccess = () => message('indexedDB cleared');
+
+ if (navigator.serviceWorker.controller) {
+ navigator.serviceWorker.controller.postMessage('clear');
+ navigator.serviceWorker.getRegistrations()
+ .then(registrations => {
+ return Promise.all(registrations.map(registration => registration.unregister()));
+ })
+ .then(() => {
+ message('Success Flush! Please reopen Misskey.\n成功しました。Misskeyを開き直してください。');
+ })
+ .catch(e => { throw Error(e) });
+ } else {
+ message('Success Flush! Please reopen Misskey.\n成功しました。Misskeyを開き直してください。');
+ }
} catch (e) {
console.error(e);
+ message(`${e}¥n¥nFlush Failed. Please reopen Misskey.\n失敗しました。Misskeyを開き直してください。`);
setTimeout(() => {
location = '/';
}, 10000)
}
+
+ function message(text) {
+ msg.insertAdjacentHTML('beforeend', `<p>[${(new Date()).toString()}] ${text.replace(/¥n/g,'<br>')}</p>`)
+ }
diff --git a/src/services/push-notification.ts b/src/services/push-notification.ts
index f0d9c4e22c..d0a0c04d62 100644
--- a/src/services/push-notification.ts
+++ b/src/services/push-notification.ts
@@ -2,8 +2,13 @@ import * as push from 'web-push';
import config from '../config';
import { SwSubscriptions } from '../models';
import { fetchMeta } from '../misc/fetch-meta';
+import { PackedNotification } from '../models/repositories/notification';
+import { PackedMessagingMessage } from '../models/repositories/messaging-message';
-export default async function(userId: string, type: string, body?: any) {
+type notificationType = 'notification' | 'unreadMessagingMessage';
+type notificationBody = PackedNotification | PackedMessagingMessage;
+
+export default async function(userId: string, type: notificationType, body: notificationBody) {
const meta = await fetchMeta();
if (!meta.enableServiceWorker || meta.swPublicKey == null || meta.swPrivateKey == null) return;
diff --git a/webpack.config.ts b/webpack.config.ts
index 64cf4c8581..fa364e603a 100644
--- a/webpack.config.ts
+++ b/webpack.config.ts
@@ -34,7 +34,7 @@ const postcss = {
module.exports = {
entry: {
app: './src/client/init.ts',
- sw: './src/client/sw.js'
+ sw: './src/client/sw.ts'
},
module: {
rules: [{
diff --git a/yarn.lock b/yarn.lock
index c10ddb19c2..4ea29f6725 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2819,6 +2819,11 @@ decompress-response@^4.2.0:
dependencies:
mimic-response "^2.0.0"
+deep-entries@3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/deep-entries/-/deep-entries-3.1.0.tgz#e456aa791d01b045641c75e41e170c0c95a9d472"
+ integrity sha512-pCpcCqx/hclnT2e4mMlM9geG8XIaxWN+yNKJHHwu1FZyYKErKU/fPztYYSk2HwnqRPf55cDEXraV6MLv8I5FrA==
+
deep-eql@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-3.0.1.tgz#dfc9404400ad1c8fe023e7da1df1c147c4b444df"
@@ -4488,6 +4493,11 @@ icss-utils@^4.0.0, icss-utils@^4.1.1:
dependencies:
postcss "^7.0.14"
+idb-keyval@3.2.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/idb-keyval/-/idb-keyval-3.2.0.tgz#cbbf354deb5684b6cdc84376294fc05932845bd6"
+ integrity sha512-slx8Q6oywCCSfKgPgL0sEsXtPVnSbTLWpyiDcu6msHOyKOLari1TD1qocXVCft80umnkk3/Qqh3lwoFt8T/BPQ==
+
ieee754@1.1.13, ieee754@^1.1.13, ieee754@^1.1.4:
version "1.1.13"
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84"