From 9224b6635f17c4e38e4a35b4aa1a47f94e8a3d2e Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sat, 1 Apr 2023 14:01:57 +0900
Subject: [PATCH] refactor(frontend): remove $ts and $t

---
 packages/frontend/@types/vue.d.ts             |  6 ----
 .../frontend/src/components/MkContainer.vue   |  3 +-
 .../frontend/src/components/MkFormDialog.vue  | 14 ++++----
 packages/frontend/src/components/MkGoogle.vue |  3 +-
 .../frontend/src/components/MkMediaBanner.vue |  5 +--
 .../frontend/src/components/MkMediaVideo.vue  |  5 +--
 .../src/components/MkModalPageWindow.vue      |  2 +-
 packages/frontend/src/components/MkNote.vue   |  2 +-
 .../src/components/MkNoteDetailed.vue         |  2 +-
 packages/frontend/src/components/MkOmit.vue   |  3 +-
 packages/frontend/src/components/MkPoll.vue   |  4 +--
 .../frontend/src/components/MkPollEditor.vue  |  2 +-
 .../src/components/MkSubNoteContent.vue       |  2 +-
 .../src/components/MkTokenGenerateWindow.vue  |  8 ++---
 .../frontend/src/components/MkUserInfo.vue    |  2 +-
 .../frontend/src/components/MkUserPopup.vue   |  8 ++---
 .../frontend/src/components/form/suspense.vue |  6 ++--
 .../frontend/src/components/global/MkAd.vue   |  7 ++--
 packages/frontend/src/init.ts                 |  6 ----
 packages/frontend/src/pages/about.emojis.vue  |  6 ++--
 packages/frontend/src/pages/admin/relays.vue  |  2 +-
 packages/frontend/src/pages/announcements.vue |  2 +-
 .../frontend/src/pages/antenna-timeline.vue   |  2 +-
 packages/frontend/src/pages/auth.form.vue     | 28 ++++++++--------
 packages/frontend/src/pages/auth.vue          |  2 +-
 packages/frontend/src/pages/miauth.vue        |  8 ++---
 .../page-editor/els/page-editor.el.image.vue  |  3 +-
 .../page-editor/els/page-editor.el.note.vue   |  9 +++---
 .../page-editor/els/page-editor.el.text.vue   |  3 +-
 .../page-editor/page-editor.container.vue     |  6 ++--
 .../src/pages/page-editor/page-editor.vue     | 28 ++++++++--------
 packages/frontend/src/pages/settings/apps.vue |  2 +-
 .../frontend/src/pages/settings/sounds.vue    |  2 +-
 packages/frontend/src/pages/user/home.vue     |  2 +-
 .../src/pages/user/index.activity.vue         |  2 +-
 .../frontend/src/pages/user/index.photos.vue  |  4 +--
 .../frontend/src/pages/welcome.entrance.b.vue | 20 ++++++------
 .../frontend/src/pages/welcome.entrance.c.vue | 32 ++++++++++---------
 packages/frontend/src/pages/welcome.setup.vue |  8 ++---
 packages/frontend/src/ui/classic.header.vue   |  8 +++--
 packages/frontend/src/ui/classic.sidebar.vue  | 12 ++++---
 packages/frontend/src/ui/visitor/a.vue        |  8 +++--
 packages/frontend/src/ui/visitor/b.vue        | 19 +++++------
 packages/frontend/src/ui/visitor/header.vue   | 16 ++++++----
 packages/frontend/src/ui/visitor/kanban.vue   | 10 +++---
 .../frontend/src/widgets/WidgetCalendar.vue   |  8 ++---
 .../frontend/src/widgets/WidgetSlideshow.vue  |  2 +-
 .../frontend/src/widgets/WidgetTimeline.vue   |  2 +-
 .../frontend/src/widgets/WidgetTrends.vue     |  2 +-
 49 files changed, 183 insertions(+), 165 deletions(-)

diff --git a/packages/frontend/@types/vue.d.ts b/packages/frontend/@types/vue.d.ts
index d84f83b43a..4b7cb045f8 100644
--- a/packages/frontend/@types/vue.d.ts
+++ b/packages/frontend/@types/vue.d.ts
@@ -1,10 +1,4 @@
 /// <reference types="vue/macros-global" />
 
-import type { i18n } from '@/i18n';
-
 declare module 'vue' {
-	interface ComponentCustomProperties {
-		$t: typeof i18n['t'];
-		$ts: typeof i18n['ts'];
-	}
 }
diff --git a/packages/frontend/src/components/MkContainer.vue b/packages/frontend/src/components/MkContainer.vue
index e021cfbda9..1834224b8d 100644
--- a/packages/frontend/src/components/MkContainer.vue
+++ b/packages/frontend/src/components/MkContainer.vue
@@ -26,7 +26,7 @@
 		<div v-show="showBody" ref="content" :class="[$style.content, { [$style.omitted]: omitted }]">
 			<slot></slot>
 			<button v-if="omitted" :class="$style.fade" class="_button" @click="() => { ignoreOmit = true; omitted = false; }">
-				<span :class="$style.fadeLabel">{{ $ts.showMore }}</span>
+				<span :class="$style.fadeLabel">{{ i18n.ts.showMore }}</span>
 			</button>
 		</div>
 	</Transition>
@@ -36,6 +36,7 @@
 <script lang="ts">
 import { defineComponent } from 'vue';
 import { defaultStore } from '@/store';
+import { i18n } from '@/i18n';
 
 export default defineComponent({
 	props: {
diff --git a/packages/frontend/src/components/MkFormDialog.vue b/packages/frontend/src/components/MkFormDialog.vue
index 971bb806af..979df2e7c1 100644
--- a/packages/frontend/src/components/MkFormDialog.vue
+++ b/packages/frontend/src/components/MkFormDialog.vue
@@ -18,15 +18,15 @@
 		<div class="_gaps_m">
 			<template v-for="item in Object.keys(form).filter(item => !form[item].hidden)">
 				<MkInput v-if="form[item].type === 'number'" v-model="values[item]" type="number" :step="form[item].step || 1">
-					<template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ $ts.optional }})</span></template>
+					<template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ i18n.ts.optional }})</span></template>
 					<template v-if="form[item].description" #caption>{{ form[item].description }}</template>
 				</MkInput>
 				<MkInput v-else-if="form[item].type === 'string' && !form[item].multiline" v-model="values[item]" type="text">
-					<template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ $ts.optional }})</span></template>
+					<template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ i18n.ts.optional }})</span></template>
 					<template v-if="form[item].description" #caption>{{ form[item].description }}</template>
 				</MkInput>
 				<MkTextarea v-else-if="form[item].type === 'string' && form[item].multiline" v-model="values[item]">
-					<template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ $ts.optional }})</span></template>
+					<template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ i18n.ts.optional }})</span></template>
 					<template v-if="form[item].description" #caption>{{ form[item].description }}</template>
 				</MkTextarea>
 				<MkSwitch v-else-if="form[item].type === 'boolean'" v-model="values[item]">
@@ -34,15 +34,15 @@
 					<template v-if="form[item].description" #caption>{{ form[item].description }}</template>
 				</MkSwitch>
 				<MkSelect v-else-if="form[item].type === 'enum'" v-model="values[item]">
-					<template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ $ts.optional }})</span></template>
+					<template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ i18n.ts.optional }})</span></template>
 					<option v-for="item in form[item].enum" :key="item.value" :value="item.value">{{ item.label }}</option>
 				</MkSelect>
 				<MkRadios v-else-if="form[item].type === 'radio'" v-model="values[item]">
-					<template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ $ts.optional }})</span></template>
+					<template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ i18n.ts.optional }})</span></template>
 					<option v-for="item in form[item].options" :key="item.value" :value="item.value">{{ item.label }}</option>
 				</MkRadios>
 				<MkRange v-else-if="form[item].type === 'range'" v-model="values[item]" :min="form[item].min" :max="form[item].max" :step="form[item].step" :text-converter="form[item].textConverter">
-					<template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ $ts.optional }})</span></template>
+					<template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ i18n.ts.optional }})</span></template>
 					<template v-if="form[item].description" #caption>{{ form[item].description }}</template>
 				</MkRange>
 				<MkButton v-else-if="form[item].type === 'button'" @click="form[item].action($event, values)">
@@ -64,6 +64,7 @@ import MkRange from './MkRange.vue';
 import MkButton from './MkButton.vue';
 import MkRadios from './MkRadios.vue';
 import MkModalWindow from '@/components/MkModalWindow.vue';
+import { i18n } from '@/i18n';
 
 export default defineComponent({
 	components: {
@@ -93,6 +94,7 @@ export default defineComponent({
 	data() {
 		return {
 			values: {},
+			i18n,
 		};
 	},
 
diff --git a/packages/frontend/src/components/MkGoogle.vue b/packages/frontend/src/components/MkGoogle.vue
index 007728176e..227054d963 100644
--- a/packages/frontend/src/components/MkGoogle.vue
+++ b/packages/frontend/src/components/MkGoogle.vue
@@ -1,12 +1,13 @@
 <template>
 <div :class="$style.root">
 	<input v-model="query" :class="$style.input" type="search" :placeholder="q">
-	<button :class="$style.button" @click="search"><i class="ti ti-search"></i> {{ $ts.searchByGoogle }}</button>
+	<button :class="$style.button" @click="search"><i class="ti ti-search"></i> {{ i18n.ts.searchByGoogle }}</button>
 </div>
 </template>
 
 <script lang="ts" setup>
 import { ref } from 'vue';
+import { i18n } from '@/i18n';
 
 const props = defineProps<{
 	q: string;
diff --git a/packages/frontend/src/components/MkMediaBanner.vue b/packages/frontend/src/components/MkMediaBanner.vue
index c0401a6455..1576144f6b 100644
--- a/packages/frontend/src/components/MkMediaBanner.vue
+++ b/packages/frontend/src/components/MkMediaBanner.vue
@@ -2,8 +2,8 @@
 <div class="mk-media-banner">
 	<div v-if="media.isSensitive && hide" class="sensitive" @click="hide = false">
 		<span class="icon"><i class="ti ti-alert-triangle"></i></span>
-		<b>{{ $ts.sensitive }}</b>
-		<span>{{ $ts.clickToShow }}</span>
+		<b>{{ i18n.ts.sensitive }}</b>
+		<span>{{ i18n.ts.clickToShow }}</span>
 	</div>
 	<div v-else-if="media.type.startsWith('audio') && media.type !== 'audio/midi'" class="audio">
 		<VuePlyr :options="{ volume: 0.5 }">
@@ -33,6 +33,7 @@ import * as misskey from 'misskey-js';
 import VuePlyr from 'vue-plyr';
 import { ColdDeviceStorage } from '@/store';
 import 'vue-plyr/dist/vue-plyr.css';
+import { i18n } from '@/i18n';
 
 const props = withDefaults(defineProps<{
 	media: misskey.entities.DriveFile;
diff --git a/packages/frontend/src/components/MkMediaVideo.vue b/packages/frontend/src/components/MkMediaVideo.vue
index 979c3eed28..e02a7af09e 100644
--- a/packages/frontend/src/components/MkMediaVideo.vue
+++ b/packages/frontend/src/components/MkMediaVideo.vue
@@ -1,8 +1,8 @@
 <template>
 <div v-if="hide" class="icozogqfvdetwohsdglrbswgrejoxbdj" @click="hide = false">
 	<div>
-		<b><i class="ti ti-alert-triangle"></i> {{ $ts.sensitive }}</b>
-		<span>{{ $ts.clickToShow }}</span>
+		<b><i class="ti ti-alert-triangle"></i> {{ i18n.ts.sensitive }}</b>
+		<span>{{ i18n.ts.clickToShow }}</span>
 	</div>
 </div>
 <div v-else class="kkjnbbplepmiyuadieoenjgutgcmtsvu">
@@ -28,6 +28,7 @@ import * as misskey from 'misskey-js';
 import VuePlyr from 'vue-plyr';
 import { defaultStore } from '@/store';
 import 'vue-plyr/dist/vue-plyr.css';
+import { i18n } from '@/i18n';
 
 const props = defineProps<{
 	video: misskey.entities.DriveFile;
diff --git a/packages/frontend/src/components/MkModalPageWindow.vue b/packages/frontend/src/components/MkModalPageWindow.vue
index 68a3eda3d8..b38865f525 100644
--- a/packages/frontend/src/components/MkModalPageWindow.vue
+++ b/packages/frontend/src/components/MkModalPageWindow.vue
@@ -2,7 +2,7 @@
 <MkModal ref="modal" @click="$emit('click')" @closed="$emit('closed')">
 	<div ref="rootEl" class="hrmcaedk" :style="{ width: `${width}px`, height: (height ? `min(${height}px, 100%)` : '100%') }">
 		<div class="header" @contextmenu="onContextmenu">
-			<button v-if="history.length > 0" v-tooltip="$ts.goBack" class="_button" @click="back()"><i class="ti ti-arrow-left"></i></button>
+			<button v-if="history.length > 0" v-tooltip="i18n.ts.goBack" class="_button" @click="back()"><i class="ti ti-arrow-left"></i></button>
 			<span v-else style="display: inline-block; width: 20px"></span>
 			<span v-if="pageMetadata?.value" class="title">
 				<i v-if="pageMetadata?.value.icon" class="icon" :class="pageMetadata?.value.icon"></i>
diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue
index eb9793fcc1..700bbde6f0 100644
--- a/packages/frontend/src/components/MkNote.vue
+++ b/packages/frontend/src/components/MkNote.vue
@@ -57,7 +57,7 @@
 						<div v-if="translating || translation" :class="$style.translation">
 							<MkLoading v-if="translating" mini/>
 							<div v-else :class="$style.translated">
-								<b>{{ $t('translatedFrom', { x: translation.sourceLang }) }}: </b>
+								<b>{{ i18n.t('translatedFrom', { x: translation.sourceLang }) }}: </b>
 								<Mfm :text="translation.text" :author="appearNote.user" :i="$i" :emoji-urls="appearNote.emojis"/>
 							</div>
 						</div>
diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue
index 715fd3a9a8..67bdfd2258 100644
--- a/packages/frontend/src/components/MkNoteDetailed.vue
+++ b/packages/frontend/src/components/MkNoteDetailed.vue
@@ -70,7 +70,7 @@
 						<div v-if="translating || translation" class="translation">
 							<MkLoading v-if="translating" mini/>
 							<div v-else class="translated">
-								<b>{{ $t('translatedFrom', { x: translation.sourceLang }) }}: </b>
+								<b>{{ i18n.t('translatedFrom', { x: translation.sourceLang }) }}: </b>
 								<Mfm :text="translation.text" :author="appearNote.user" :i="$i" :emoji-urls="appearNote.emojis"/>
 							</div>
 						</div>
diff --git a/packages/frontend/src/components/MkOmit.vue b/packages/frontend/src/components/MkOmit.vue
index a806d92b22..9232ebb7c9 100644
--- a/packages/frontend/src/components/MkOmit.vue
+++ b/packages/frontend/src/components/MkOmit.vue
@@ -2,13 +2,14 @@
 <div ref="content" :class="[$style.content, { [$style.omitted]: omitted }]">
 	<slot></slot>
 	<button v-if="omitted" :class="$style.fade" class="_button" @click="() => { ignoreOmit = true; omitted = false; }">
-		<span :class="$style.fadeLabel">{{ $ts.showMore }}</span>
+		<span :class="$style.fadeLabel">{{ i18n.ts.showMore }}</span>
 	</button>
 </div>
 </template>
 
 <script lang="ts" setup>
 import { onMounted } from 'vue';
+import { i18n } from '@/i18n';
 
 const props = withDefaults(defineProps<{
 	maxHeight: number;
diff --git a/packages/frontend/src/components/MkPoll.vue b/packages/frontend/src/components/MkPoll.vue
index fcbd8ad351..0810061ff9 100644
--- a/packages/frontend/src/components/MkPoll.vue
+++ b/packages/frontend/src/components/MkPoll.vue
@@ -6,12 +6,12 @@
 			<span>
 				<template v-if="choice.isVoted"><i class="ti ti-check"></i></template>
 				<Mfm :text="choice.text" :plain="true"/>
-				<span v-if="showResult" class="votes">({{ $t('_poll.votesCount', { n: choice.votes }) }})</span>
+				<span v-if="showResult" class="votes">({{ i18n.t('_poll.votesCount', { n: choice.votes }) }})</span>
 			</span>
 		</li>
 	</ul>
 	<p v-if="!readOnly">
-		<span>{{ $t('_poll.totalVotes', { n: total }) }}</span>
+		<span>{{ i18n.t('_poll.totalVotes', { n: total }) }}</span>
 		<span> ยท </span>
 		<a v-if="!closed && !isVoted" @click="showResult = !showResult">{{ showResult ? i18n.ts._poll.vote : i18n.ts._poll.showResult }}</a>
 		<span v-if="isVoted">{{ i18n.ts._poll.voted }}</span>
diff --git a/packages/frontend/src/components/MkPollEditor.vue b/packages/frontend/src/components/MkPollEditor.vue
index 9567c58b99..471ec39169 100644
--- a/packages/frontend/src/components/MkPollEditor.vue
+++ b/packages/frontend/src/components/MkPollEditor.vue
@@ -5,7 +5,7 @@
 	</p>
 	<ul>
 		<li v-for="(choice, i) in choices" :key="i">
-			<MkInput class="input" small :model-value="choice" :placeholder="$t('_poll.choiceN', { n: i + 1 })" @update:model-value="onInput(i, $event)">
+			<MkInput class="input" small :model-value="choice" :placeholder="i18n.t('_poll.choiceN', { n: i + 1 })" @update:model-value="onInput(i, $event)">
 			</MkInput>
 			<button class="_button" @click="remove(i)">
 				<i class="ti ti-x"></i>
diff --git a/packages/frontend/src/components/MkSubNoteContent.vue b/packages/frontend/src/components/MkSubNoteContent.vue
index 5290129ee7..1ac7107aa7 100644
--- a/packages/frontend/src/components/MkSubNoteContent.vue
+++ b/packages/frontend/src/components/MkSubNoteContent.vue
@@ -8,7 +8,7 @@
 		<MkA v-if="note.renoteId" :class="$style.rp" :to="`/notes/${note.renoteId}`">RN: ...</MkA>
 	</div>
 	<details v-if="note.files.length > 0">
-		<summary>({{ $t('withNFiles', { n: note.files.length }) }})</summary>
+		<summary>({{ i18n.t('withNFiles', { n: note.files.length }) }})</summary>
 		<MkMediaList :media-list="note.files"/>
 	</details>
 	<details v-if="note.poll">
diff --git a/packages/frontend/src/components/MkTokenGenerateWindow.vue b/packages/frontend/src/components/MkTokenGenerateWindow.vue
index 6035c20d23..56be044405 100644
--- a/packages/frontend/src/components/MkTokenGenerateWindow.vue
+++ b/packages/frontend/src/components/MkTokenGenerateWindow.vue
@@ -10,7 +10,7 @@
 	@closed="$emit('closed')"
 	@ok="ok()"
 >
-	<template #header>{{ title || $ts.generateAccessToken }}</template>
+	<template #header>{{ title || i18n.ts.generateAccessToken }}</template>
 
 	<MkSpacer :margin-min="20" :margin-max="28">
 		<div class="_gaps_m">
@@ -19,15 +19,15 @@
 			</div>
 			<div>
 				<MkInput v-model="name">
-					<template #label>{{ $ts.name }}</template>
+					<template #label>{{ i18n.ts.name }}</template>
 				</MkInput>
 			</div>
-			<div><b>{{ $ts.permission }}</b></div>
+			<div><b>{{ i18n.ts.permission }}</b></div>
 			<div class="_buttons">
 				<MkButton inline @click="disableAll">{{ i18n.ts.disableAll }}</MkButton>
 				<MkButton inline @click="enableAll">{{ i18n.ts.enableAll }}</MkButton>
 			</div>
-			<MkSwitch v-for="kind in (initialPermissions || kinds)" :key="kind" v-model="permissions[kind]">{{ $t(`_permissions.${kind}`) }}</MkSwitch>
+			<MkSwitch v-for="kind in (initialPermissions || kinds)" :key="kind" v-model="permissions[kind]">{{ i18n.t(`_permissions.${kind}`) }}</MkSwitch>
 		</div>
 	</MkSpacer>
 </MkModalWindow>
diff --git a/packages/frontend/src/components/MkUserInfo.vue b/packages/frontend/src/components/MkUserInfo.vue
index 1660ca68fc..5086c1b319 100644
--- a/packages/frontend/src/components/MkUserInfo.vue
+++ b/packages/frontend/src/components/MkUserInfo.vue
@@ -6,7 +6,7 @@
 		<MkA class="name" :to="userPage(user)"><MkUserName :user="user" :nowrap="false"/></MkA>
 		<p class="username"><MkAcct :user="user"/></p>
 	</div>
-	<span v-if="$i && $i.id !== user.id && user.isFollowed" class="followed">{{ $ts.followsYou }}</span>
+	<span v-if="$i && $i.id !== user.id && user.isFollowed" class="followed">{{ i18n.ts.followsYou }}</span>
 	<div class="description">
 		<div v-if="user.description" class="mfm">
 			<Mfm :text="user.description" :author="user" :i="$i"/>
diff --git a/packages/frontend/src/components/MkUserPopup.vue b/packages/frontend/src/components/MkUserPopup.vue
index 73f42a4e68..8ca0355448 100644
--- a/packages/frontend/src/components/MkUserPopup.vue
+++ b/packages/frontend/src/components/MkUserPopup.vue
@@ -9,7 +9,7 @@
 	<div v-if="showing" :class="$style.root" class="_popup _shadow" :style="{ zIndex, top: top + 'px', left: left + 'px' }" @mouseover="() => { emit('mouseover'); }" @mouseleave="() => { emit('mouseleave'); }">
 		<div v-if="user != null">
 			<div :class="$style.banner" :style="user.bannerUrl ? `background-image: url(${user.bannerUrl})` : ''">
-				<span v-if="$i && $i.id != user.id && user.isFollowed" :class="$style.followed">{{ $ts.followsYou }}</span>
+				<span v-if="$i && $i.id != user.id && user.isFollowed" :class="$style.followed">{{ i18n.ts.followsYou }}</span>
 			</div>
 			<svg viewBox="0 0 128 128" :class="$style.avatarBack">
 				<g transform="matrix(1.6,0,0,1.6,-38.4,-51.2)">
@@ -27,15 +27,15 @@
 			</div>
 			<div :class="$style.status">
 				<div :class="$style.statusItem">
-					<div :class="$style.statusItemLabel">{{ $ts.notes }}</div>
+					<div :class="$style.statusItemLabel">{{ i18n.ts.notes }}</div>
 					<div>{{ number(user.notesCount) }}</div>
 				</div>
 				<div :class="$style.statusItem">
-					<div :class="$style.statusItemLabel">{{ $ts.following }}</div>
+					<div :class="$style.statusItemLabel">{{ i18n.ts.following }}</div>
 					<div>{{ number(user.followingCount) }}</div>
 				</div>
 				<div :class="$style.statusItem">
-					<div :class="$style.statusItemLabel">{{ $ts.followers }}</div>
+					<div :class="$style.statusItemLabel">{{ i18n.ts.followers }}</div>
 					<div>{{ number(user.followersCount) }}</div>
 				</div>
 			</div>
diff --git a/packages/frontend/src/components/form/suspense.vue b/packages/frontend/src/components/form/suspense.vue
index d4b36381a2..3a44c3da3d 100644
--- a/packages/frontend/src/components/form/suspense.vue
+++ b/packages/frontend/src/components/form/suspense.vue
@@ -8,8 +8,8 @@
 	</div>
 	<div v-else>
 		<div class="wszdbhzo">
-			<div><i class="ti ti-alert-triangle"></i> {{ $ts.somethingHappened }}</div>
-			<MkButton inline class="retry" @click="retry"><i class="ti ti-reload"></i> {{ $ts.retry }}</MkButton>
+			<div><i class="ti ti-alert-triangle"></i> {{ i18n.ts.somethingHappened }}</div>
+			<MkButton inline class="retry" @click="retry"><i class="ti ti-reload"></i> {{ i18n.ts.retry }}</MkButton>
 		</div>
 	</div>
 </Transition>
@@ -19,6 +19,7 @@
 import { defineComponent, PropType, ref, watch } from 'vue';
 import MkButton from '@/components/MkButton.vue';
 import { defaultStore } from '@/store';
+import { i18n } from '@/i18n';
 
 export default defineComponent({
 	components: {
@@ -74,6 +75,7 @@ export default defineComponent({
 			result,
 			retry,
 			defaultStore,
+			i18n,
 		};
 	},
 });
diff --git a/packages/frontend/src/components/global/MkAd.vue b/packages/frontend/src/components/global/MkAd.vue
index e0304c8bc5..b8f749bd1c 100644
--- a/packages/frontend/src/components/global/MkAd.vue
+++ b/packages/frontend/src/components/global/MkAd.vue
@@ -9,9 +9,9 @@
 	<div v-else :class="$style.menu">
 		<div :class="$style.menuContainer">
 			<div>Ads by {{ host }}</div>
-			<!--<MkButton class="button" primary>{{ $ts._ad.like }}</MkButton>-->
-			<MkButton v-if="chosen.ratio !== 0" :class="$style.menuButton" @click="reduceFrequency">{{ $ts._ad.reduceFrequencyOfThisAd }}</MkButton>
-			<button class="_textButton" @click="toggleMenu">{{ $ts._ad.back }}</button>
+			<!--<MkButton class="button" primary>{{ i18n.ts._ad.like }}</MkButton>-->
+			<MkButton v-if="chosen.ratio !== 0" :class="$style.menuButton" @click="reduceFrequency">{{ i18n.ts._ad.reduceFrequencyOfThisAd }}</MkButton>
+			<button class="_textButton" @click="toggleMenu">{{ i18n.ts._ad.back }}</button>
 		</div>
 	</div>
 </div>
@@ -26,6 +26,7 @@ import MkButton from '@/components/MkButton.vue';
 import { defaultStore } from '@/store';
 import * as os from '@/os';
 import { $i } from '@/account';
+import { i18n } from '@/i18n';
 
 type Ad = (typeof instance)['ads'][number];
 
diff --git a/packages/frontend/src/init.ts b/packages/frontend/src/init.ts
index 2a595f4cc4..26c5adfc70 100644
--- a/packages/frontend/src/init.ts
+++ b/packages/frontend/src/init.ts
@@ -198,12 +198,6 @@ if (_DEV_) {
 	app.config.performance = true;
 }
 
-// TODO: ๅปƒๆญข
-app.config.globalProperties = {
-	$t: i18n.t,
-	$ts: i18n.ts,
-};
-
 widgets(app);
 directives(app);
 components(app);
diff --git a/packages/frontend/src/pages/about.emojis.vue b/packages/frontend/src/pages/about.emojis.vue
index e0c5c084e1..d461430234 100644
--- a/packages/frontend/src/pages/about.emojis.vue
+++ b/packages/frontend/src/pages/about.emojis.vue
@@ -3,7 +3,7 @@
 	<MkButton v-if="$i && ($i.isModerator || $i.policies.canManageCustomEmojis)" primary link to="/custom-emojis-manager">{{ i18n.ts.manageCustomEmojis }}</MkButton>
 
 	<div class="query">
-		<MkInput v-model="q" class="" :placeholder="$ts.search">
+		<MkInput v-model="q" class="" :placeholder="i18n.ts.search">
 			<template #prefix><i class="ti ti-search"></i></template>
 		</MkInput>
 
@@ -15,14 +15,14 @@
 	</div>
 
 	<MkFoldableSection v-if="searchEmojis" class="emojis">
-		<template #header>{{ $ts.searchResult }}</template>
+		<template #header>{{ i18n.ts.searchResult }}</template>
 		<div class="zuvgdzyt">
 			<XEmoji v-for="emoji in searchEmojis" :key="emoji.name" class="emoji" :emoji="emoji"/>
 		</div>
 	</MkFoldableSection>
 	
 	<MkFoldableSection v-for="category in customEmojiCategories" v-once :key="category" class="emojis">
-		<template #header>{{ category || $ts.other }}</template>
+		<template #header>{{ category || i18n.ts.other }}</template>
 		<div class="zuvgdzyt">
 			<XEmoji v-for="emoji in customEmojis.filter(e => e.category === category)" :key="emoji.name" class="emoji" :emoji="emoji"/>
 		</div>
diff --git a/packages/frontend/src/pages/admin/relays.vue b/packages/frontend/src/pages/admin/relays.vue
index 55d33e0158..7ebcdfc583 100644
--- a/packages/frontend/src/pages/admin/relays.vue
+++ b/packages/frontend/src/pages/admin/relays.vue
@@ -9,7 +9,7 @@
 					<i v-if="relay.status === 'accepted'" class="ti ti-check icon accepted"></i>
 					<i v-else-if="relay.status === 'rejected'" class="ti ti-ban icon rejected"></i>
 					<i v-else class="ti ti-clock icon requesting"></i>
-					<span>{{ $t(`_relayStatus.${relay.status}`) }}</span>
+					<span>{{ i18n.t(`_relayStatus.${relay.status}`) }}</span>
 				</div>
 				<MkButton class="button" inline danger @click="remove(relay.inbox)"><i class="ti ti-trash"></i> {{ i18n.ts.remove }}</MkButton>
 			</div>
diff --git a/packages/frontend/src/pages/announcements.vue b/packages/frontend/src/pages/announcements.vue
index 526ea3a79a..16a0ee8373 100644
--- a/packages/frontend/src/pages/announcements.vue
+++ b/packages/frontend/src/pages/announcements.vue
@@ -10,7 +10,7 @@
 					<img v-if="announcement.imageUrl" :src="announcement.imageUrl"/>
 				</div>
 				<div v-if="$i && !announcement.isRead" class="footer">
-					<MkButton primary @click="read(items, announcement, i)"><i class="ti ti-check"></i> {{ $ts.gotIt }}</MkButton>
+					<MkButton primary @click="read(items, announcement, i)"><i class="ti ti-check"></i> {{ i18n.ts.gotIt }}</MkButton>
 				</div>
 			</section>
 		</MkPagination>
diff --git a/packages/frontend/src/pages/antenna-timeline.vue b/packages/frontend/src/pages/antenna-timeline.vue
index cf803d6c7f..62e8178af1 100644
--- a/packages/frontend/src/pages/antenna-timeline.vue
+++ b/packages/frontend/src/pages/antenna-timeline.vue
@@ -2,7 +2,7 @@
 <MkStickyContainer>
 	<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
 	<div ref="rootEl" v-hotkey.global="keymap" class="tqmomfks">
-		<div v-if="queue > 0" class="new"><button class="_buttonPrimary" @click="top()">{{ $ts.newNoteRecived }}</button></div>
+		<div v-if="queue > 0" class="new"><button class="_buttonPrimary" @click="top()">{{ i18n.ts.newNoteRecived }}</button></div>
 		<div class="tl">
 			<MkTimeline
 				ref="tlEl" :key="antennaId"
diff --git a/packages/frontend/src/pages/auth.form.vue b/packages/frontend/src/pages/auth.form.vue
index f8484185f5..40a6d782b0 100644
--- a/packages/frontend/src/pages/auth.form.vue
+++ b/packages/frontend/src/pages/auth.form.vue
@@ -1,25 +1,25 @@
 <template>
-	<section>
-		<div v-if="app.permission.length > 0">
-			<p>{{ $t('_auth.permission', { name }) }}</p>
-			<ul>
-				<li v-for="p in app.permission" :key="p">{{ $t(`_permissions.${p}`) }}</li>
-			</ul>
-		</div>
-		<div>{{ i18n.t('_auth.shareAccess', { name: `${name} (${app.id})` }) }}</div>
-		<div :class="$style.buttons">
-			<MkButton inline @click="cancel">{{ i18n.ts.cancel }}</MkButton>
-			<MkButton inline primary @click="accept">{{ i18n.ts.accept }}</MkButton>
-		</div>
-	</section>
+<section>
+	<div v-if="app.permission.length > 0">
+		<p>{{ i18n.t('_auth.permission', { name }) }}</p>
+		<ul>
+			<li v-for="p in app.permission" :key="p">{{ i18n.t(`_permissions.${p}`) }}</li>
+		</ul>
+	</div>
+	<div>{{ i18n.t('_auth.shareAccess', { name: `${name} (${app.id})` }) }}</div>
+	<div :class="$style.buttons">
+		<MkButton inline @click="cancel">{{ i18n.ts.cancel }}</MkButton>
+		<MkButton inline primary @click="accept">{{ i18n.ts.accept }}</MkButton>
+	</div>
+</section>
 </template>
 
 <script lang="ts" setup>
 import { } from 'vue';
+import { AuthSession } from 'misskey-js/built/entities';
 import MkButton from '@/components/MkButton.vue';
 import * as os from '@/os';
 import { i18n } from '@/i18n';
-import { AuthSession } from 'misskey-js/built/entities';
 
 const props = defineProps<{
 	session: AuthSession;
diff --git a/packages/frontend/src/pages/auth.vue b/packages/frontend/src/pages/auth.vue
index 4f8afb9ea2..2f40e7ded6 100644
--- a/packages/frontend/src/pages/auth.vue
+++ b/packages/frontend/src/pages/auth.vue
@@ -20,7 +20,7 @@
 				<h1>{{ i18n.ts._auth.denied }}</h1>
 			</div>
 			<div v-if="state == 'accepted' && session">
-				<h1>{{ session.app.isAuthorized ? $t('already-authorized') : i18n.ts.allowed }}</h1>
+				<h1>{{ session.app.isAuthorized ? i18n.t('already-authorized') : i18n.ts.allowed }}</h1>
 				<p v-if="session.app.callbackUrl">
 					{{ i18n.ts._auth.callback }}
 					<MkEllipsis/>
diff --git a/packages/frontend/src/pages/miauth.vue b/packages/frontend/src/pages/miauth.vue
index 915adff277..8e0624f555 100644
--- a/packages/frontend/src/pages/miauth.vue
+++ b/packages/frontend/src/pages/miauth.vue
@@ -1,6 +1,6 @@
 <template>
 <MkStickyContainer>
-	<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs" /></template>
+	<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
 	<MkSpacer :content-max="800">
 		<div v-if="$i">
 			<div v-if="state == 'waiting'">
@@ -15,13 +15,13 @@
 			</div>
 			<div v-else>
 				<div v-if="_permissions.length > 0">
-					<p v-if="name">{{ $t('_auth.permission', { name }) }}</p>
+					<p v-if="name">{{ i18n.t('_auth.permission', { name }) }}</p>
 					<p v-else>{{ i18n.ts._auth.permissionAsk }}</p>
 					<ul>
-						<li v-for="p in _permissions" :key="p">{{ $t(`_permissions.${p}`) }}</li>
+						<li v-for="p in _permissions" :key="p">{{ i18n.t(`_permissions.${p}`) }}</li>
 					</ul>
 				</div>
-				<div v-if="name">{{ $t('_auth.shareAccess', { name }) }}</div>
+				<div v-if="name">{{ i18n.t('_auth.shareAccess', { name }) }}</div>
 				<div v-else>{{ i18n.ts._auth.shareAccessAsk }}</div>
 				<div :class="$style.buttons">
 					<MkButton inline @click="deny">{{ i18n.ts.cancel }}</MkButton>
diff --git a/packages/frontend/src/pages/page-editor/els/page-editor.el.image.vue b/packages/frontend/src/pages/page-editor/els/page-editor.el.image.vue
index fe230ad095..ffeb8ba285 100644
--- a/packages/frontend/src/pages/page-editor/els/page-editor.el.image.vue
+++ b/packages/frontend/src/pages/page-editor/els/page-editor.el.image.vue
@@ -1,7 +1,7 @@
 <template>
 <!-- eslint-disable vue/no-mutating-props -->
 <XContainer :draggable="true" @remove="() => $emit('remove')">
-	<template #header><i class="ti ti-photo"></i> {{ $ts._pages.blocks.image }}</template>
+	<template #header><i class="ti ti-photo"></i> {{ i18n.ts._pages.blocks.image }}</template>
 	<template #func>
 		<button @click="choose()">
 			<i class="ti ti-folder"></i>
@@ -20,6 +20,7 @@ import { onMounted } from 'vue';
 import XContainer from '../page-editor.container.vue';
 import MkDriveFileThumbnail from '@/components/MkDriveFileThumbnail.vue';
 import * as os from '@/os';
+import { i18n } from '@/i18n';
 
 const props = defineProps<{
 	modelValue: any
diff --git a/packages/frontend/src/pages/page-editor/els/page-editor.el.note.vue b/packages/frontend/src/pages/page-editor/els/page-editor.el.note.vue
index cc39d2c412..a388a8d0c1 100644
--- a/packages/frontend/src/pages/page-editor/els/page-editor.el.note.vue
+++ b/packages/frontend/src/pages/page-editor/els/page-editor.el.note.vue
@@ -1,14 +1,14 @@
 <template>
 <!-- eslint-disable vue/no-mutating-props -->
 <XContainer :draggable="true" @remove="() => $emit('remove')">
-	<template #header><i class="ti ti-note"></i> {{ $ts._pages.blocks.note }}</template>
+	<template #header><i class="ti ti-note"></i> {{ i18n.ts._pages.blocks.note }}</template>
 
 	<section style="padding: 0 16px 0 16px;">
 		<MkInput v-model="id">
-			<template #label>{{ $ts._pages.blocks._note.id }}</template>
-			<template #caption>{{ $ts._pages.blocks._note.idDescription }}</template>
+			<template #label>{{ i18n.ts._pages.blocks._note.id }}</template>
+			<template #caption>{{ i18n.ts._pages.blocks._note.idDescription }}</template>
 		</MkInput>
-		<MkSwitch v-model="props.modelValue.detailed"><span>{{ $ts._pages.blocks._note.detailed }}</span></MkSwitch>
+		<MkSwitch v-model="props.modelValue.detailed"><span>{{ i18n.ts._pages.blocks._note.detailed }}</span></MkSwitch>
 
 		<MkNote v-if="note && !props.modelValue.detailed" :key="note.id + ':normal'" v-model:note="note" style="margin-bottom: 16px;"/>
 		<MkNoteDetailed v-if="note && props.modelValue.detailed" :key="note.id + ':detail'" v-model:note="note" style="margin-bottom: 16px;"/>
@@ -25,6 +25,7 @@ import MkSwitch from '@/components/MkSwitch.vue';
 import MkNote from '@/components/MkNote.vue';
 import MkNoteDetailed from '@/components/MkNoteDetailed.vue';
 import * as os from '@/os';
+import { i18n } from '@/i18n';
 
 const props = defineProps<{
 	modelValue: any
diff --git a/packages/frontend/src/pages/page-editor/els/page-editor.el.text.vue b/packages/frontend/src/pages/page-editor/els/page-editor.el.text.vue
index ee494b7574..bf21ae3c67 100644
--- a/packages/frontend/src/pages/page-editor/els/page-editor.el.text.vue
+++ b/packages/frontend/src/pages/page-editor/els/page-editor.el.text.vue
@@ -1,7 +1,7 @@
 <template>
 <!-- eslint-disable vue/no-mutating-props -->
 <XContainer :draggable="true" @remove="() => $emit('remove')">
-	<template #header><i class="ti ti-align-left"></i> {{ $ts._pages.blocks.text }}</template>
+	<template #header><i class="ti ti-align-left"></i> {{ i18n.ts._pages.blocks.text }}</template>
 
 	<section class="vckmsadr">
 		<textarea v-model="text"></textarea>
@@ -13,6 +13,7 @@
 /* eslint-disable vue/no-mutating-props */
 import { watch } from 'vue';
 import XContainer from '../page-editor.container.vue';
+import { i18n } from '@/i18n';
 
 const props = defineProps<{
 	modelValue: any
diff --git a/packages/frontend/src/pages/page-editor/page-editor.container.vue b/packages/frontend/src/pages/page-editor/page-editor.container.vue
index 15cdda5efb..dd733403af 100644
--- a/packages/frontend/src/pages/page-editor/page-editor.container.vue
+++ b/packages/frontend/src/pages/page-editor/page-editor.container.vue
@@ -16,8 +16,8 @@
 			</button>
 		</div>
 	</header>
-	<p v-show="showBody" v-if="error != null" class="error">{{ $t('_pages.script.typeError', { slot: error.arg + 1, expect: $t(`script.types.${error.expect}`), actual: $t(`script.types.${error.actual}`) }) }}</p>
-	<p v-show="showBody" v-if="warn != null" class="warn">{{ $t('_pages.script.thereIsEmptySlot', { slot: warn.slot + 1 }) }}</p>
+	<p v-show="showBody" v-if="error != null" class="error">{{ i18n.t('_pages.script.typeError', { slot: error.arg + 1, expect: i18n.t(`script.types.${error.expect}`), actual: i18n.t(`script.types.${error.actual}`) }) }}</p>
+	<p v-show="showBody" v-if="warn != null" class="warn">{{ i18n.t('_pages.script.thereIsEmptySlot', { slot: warn.slot + 1 }) }}</p>
 	<div v-show="showBody" class="body">
 		<slot></slot>
 	</div>
@@ -26,6 +26,7 @@
 
 <script lang="ts">
 import { defineComponent } from 'vue';
+import { i18n } from '@/i18n';
 
 export default defineComponent({
 	props: {
@@ -54,6 +55,7 @@ export default defineComponent({
 	data() {
 		return {
 			showBody: this.expanded,
+			i18n,
 		};
 	},
 	methods: {
diff --git a/packages/frontend/src/pages/page-editor/page-editor.vue b/packages/frontend/src/pages/page-editor/page-editor.vue
index c4b37c91c6..bcf30e23a7 100644
--- a/packages/frontend/src/pages/page-editor/page-editor.vue
+++ b/packages/frontend/src/pages/page-editor/page-editor.vue
@@ -3,42 +3,42 @@
 	<template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template>
 	<MkSpacer :content-max="700">
 		<div class="jqqmcavi">
-			<MkButton v-if="pageId" class="button" inline link :to="`/@${ author.username }/pages/${ currentName }`"><i class="ti ti-external-link"></i> {{ $ts._pages.viewPage }}</MkButton>
-			<MkButton v-if="!readonly" inline primary class="button" @click="save"><i class="ti ti-device-floppy"></i> {{ $ts.save }}</MkButton>
-			<MkButton v-if="pageId" inline class="button" @click="duplicate"><i class="ti ti-copy"></i> {{ $ts.duplicate }}</MkButton>
-			<MkButton v-if="pageId && !readonly" inline class="button" danger @click="del"><i class="ti ti-trash"></i> {{ $ts.delete }}</MkButton>
+			<MkButton v-if="pageId" class="button" inline link :to="`/@${ author.username }/pages/${ currentName }`"><i class="ti ti-external-link"></i> {{ i18n.ts._pages.viewPage }}</MkButton>
+			<MkButton v-if="!readonly" inline primary class="button" @click="save"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton>
+			<MkButton v-if="pageId" inline class="button" @click="duplicate"><i class="ti ti-copy"></i> {{ i18n.ts.duplicate }}</MkButton>
+			<MkButton v-if="pageId && !readonly" inline class="button" danger @click="del"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton>
 		</div>
 
 		<div v-if="tab === 'settings'">
 			<div class="_gaps_m">
 				<MkInput v-model="title">
-					<template #label>{{ $ts._pages.title }}</template>
+					<template #label>{{ i18n.ts._pages.title }}</template>
 				</MkInput>
 
 				<MkInput v-model="summary">
-					<template #label>{{ $ts._pages.summary }}</template>
+					<template #label>{{ i18n.ts._pages.summary }}</template>
 				</MkInput>
 
 				<MkInput v-model="name">
 					<template #prefix>{{ url }}/@{{ author.username }}/pages/</template>
-					<template #label>{{ $ts._pages.url }}</template>
+					<template #label>{{ i18n.ts._pages.url }}</template>
 				</MkInput>
 
-				<MkSwitch v-model="alignCenter">{{ $ts._pages.alignCenter }}</MkSwitch>
+				<MkSwitch v-model="alignCenter">{{ i18n.ts._pages.alignCenter }}</MkSwitch>
 
 				<MkSelect v-model="font">
-					<template #label>{{ $ts._pages.font }}</template>
-					<option value="serif">{{ $ts._pages.fontSerif }}</option>
-					<option value="sans-serif">{{ $ts._pages.fontSansSerif }}</option>
+					<template #label>{{ i18n.ts._pages.font }}</template>
+					<option value="serif">{{ i18n.ts._pages.fontSerif }}</option>
+					<option value="sans-serif">{{ i18n.ts._pages.fontSansSerif }}</option>
 				</MkSelect>
 
-				<MkSwitch v-model="hideTitleWhenPinned">{{ $ts._pages.hideTitleWhenPinned }}</MkSwitch>
+				<MkSwitch v-model="hideTitleWhenPinned">{{ i18n.ts._pages.hideTitleWhenPinned }}</MkSwitch>
 
 				<div class="eyeCatch">
-					<MkButton v-if="eyeCatchingImageId == null && !readonly" @click="setEyeCatchingImage"><i class="ti ti-plus"></i> {{ $ts._pages.eyeCatchingImageSet }}</MkButton>
+					<MkButton v-if="eyeCatchingImageId == null && !readonly" @click="setEyeCatchingImage"><i class="ti ti-plus"></i> {{ i18n.ts._pages.eyeCatchingImageSet }}</MkButton>
 					<div v-else-if="eyeCatchingImage">
 						<img :src="eyeCatchingImage.url" :alt="eyeCatchingImage.name" style="max-width: 100%;"/>
-						<MkButton v-if="!readonly" @click="removeEyeCatchingImage()"><i class="ti ti-trash"></i> {{ $ts._pages.eyeCatchingImageRemove }}</MkButton>
+						<MkButton v-if="!readonly" @click="removeEyeCatchingImage()"><i class="ti ti-trash"></i> {{ i18n.ts._pages.eyeCatchingImageRemove }}</MkButton>
 					</div>
 				</div>
 			</div>
diff --git a/packages/frontend/src/pages/settings/apps.vue b/packages/frontend/src/pages/settings/apps.vue
index 861414cef8..955d812154 100644
--- a/packages/frontend/src/pages/settings/apps.vue
+++ b/packages/frontend/src/pages/settings/apps.vue
@@ -24,7 +24,7 @@
 					<details>
 						<summary>{{ i18n.ts.details }}</summary>
 						<ul>
-							<li v-for="p in token.permission" :key="p">{{ $t(`_permissions.${p}`) }}</li>
+							<li v-for="p in token.permission" :key="p">{{ i18n.t(`_permissions.${p}`) }}</li>
 						</ul>
 					</details>
 					<div class="actions">
diff --git a/packages/frontend/src/pages/settings/sounds.vue b/packages/frontend/src/pages/settings/sounds.vue
index 006a2377d4..8855a275c6 100644
--- a/packages/frontend/src/pages/settings/sounds.vue
+++ b/packages/frontend/src/pages/settings/sounds.vue
@@ -8,7 +8,7 @@
 		<template #label>{{ i18n.ts.sounds }}</template>
 		<div class="_gaps_s">
 			<MkFolder v-for="type in Object.keys(sounds)" :key="type">
-				<template #label>{{ $t('_sfx.' + type) }}</template>
+				<template #label>{{ i18n.t('_sfx.' + type) }}</template>
 				<template #suffix>{{ sounds[type].type ?? i18n.ts.none }}</template>
 
 				<XSound :type="sounds[type].type" :volume="sounds[type].volume" @update="(res) => updated(type, res)"/>
diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue
index c45c0600a7..1ce0bedbe3 100644
--- a/packages/frontend/src/pages/user/home.vue
+++ b/packages/frontend/src/pages/user/home.vue
@@ -57,7 +57,7 @@
 						</dl>
 						<dl v-if="user.birthday" class="field">
 							<dt class="name"><i class="ti ti-cake ti-fw"></i> {{ i18n.ts.birthday }}</dt>
-							<dd class="value">{{ user.birthday.replace('-', '/').replace('-', '/') }} ({{ $t('yearsOld', { age }) }})</dd>
+							<dd class="value">{{ user.birthday.replace('-', '/').replace('-', '/') }} ({{ i18n.t('yearsOld', { age }) }})</dd>
 						</dl>
 						<dl class="field">
 							<dt class="name"><i class="ti ti-calendar ti-fw"></i> {{ i18n.ts.registeredDate }}</dt>
diff --git a/packages/frontend/src/pages/user/index.activity.vue b/packages/frontend/src/pages/user/index.activity.vue
index 8ff3374446..2d9ee85bc4 100644
--- a/packages/frontend/src/pages/user/index.activity.vue
+++ b/packages/frontend/src/pages/user/index.activity.vue
@@ -1,7 +1,7 @@
 <template>
 <MkContainer>
 	<template #icon><i class="ti ti-chart-line"></i></template>
-	<template #header>{{ $ts.activity }}</template>
+	<template #header>{{ i18n.ts.activity }}</template>
 	<template #func="{ buttonStyleClass }">
 		<button class="_button" :class="buttonStyleClass" @click="showMenu">
 			<i class="ti ti-dots"></i>
diff --git a/packages/frontend/src/pages/user/index.photos.vue b/packages/frontend/src/pages/user/index.photos.vue
index 85f6591eee..e710dab2d9 100644
--- a/packages/frontend/src/pages/user/index.photos.vue
+++ b/packages/frontend/src/pages/user/index.photos.vue
@@ -1,7 +1,7 @@
 <template>
 <MkContainer :max-height="300" :foldable="true">
 	<template #icon><i class="ti ti-photo"></i></template>
-	<template #header>{{ $ts.images }}</template>
+	<template #header>{{ i18n.ts.images }}</template>
 	<div :class="$style.root">
 		<MkLoading v-if="fetching"/>
 		<div v-if="!fetching && images.length > 0" :class="$style.stream">
@@ -14,7 +14,7 @@
 				<ImgWithBlurhash :hash="image.file.blurhash" :src="thumbnail(image.file)" :title="image.file.name"/>
 			</MkA>
 		</div>
-		<p v-if="!fetching && images.length == 0" :class="$style.empty">{{ $ts.nothing }}</p>
+		<p v-if="!fetching && images.length == 0" :class="$style.empty">{{ i18n.ts.nothing }}</p>
 	</div>
 </MkContainer>
 </template>
diff --git a/packages/frontend/src/pages/welcome.entrance.b.vue b/packages/frontend/src/pages/welcome.entrance.b.vue
index 7c7dfcc850..03bf174710 100644
--- a/packages/frontend/src/pages/welcome.entrance.b.vue
+++ b/packages/frontend/src/pages/welcome.entrance.b.vue
@@ -10,22 +10,22 @@
 			</h1>
 			<div class="about">
 				<!-- eslint-disable-next-line vue/no-v-html -->
-				<div class="desc" v-html="meta.description || $ts.headlineMisskey"></div>
+				<div class="desc" v-html="meta.description || i18n.ts.headlineMisskey"></div>
 			</div>
 			<div class="action">
-				<MkButton class="signup" inline gradate @click="signup()">{{ $ts.signup }}</MkButton>
-				<MkButton class="signin" inline @click="signin()">{{ $ts.login }}</MkButton>
+				<MkButton class="signup" inline gradate @click="signup()">{{ i18n.ts.signup }}</MkButton>
+				<MkButton class="signin" inline @click="signin()">{{ i18n.ts.login }}</MkButton>
 			</div>
 			<div v-if="onlineUsersCount && stats" class="status">
 				<div>
-					<I18n :src="$ts.nUsers" text-tag="span" class="users">
+					<I18n :src="i18n.ts.nUsers" text-tag="span" class="users">
 						<template #n><b>{{ number(stats.originalUsersCount) }}</b></template>
 					</I18n>
-					<I18n :src="$ts.nNotes" text-tag="span" class="notes">
+					<I18n :src="i18n.ts.nNotes" text-tag="span" class="notes">
 						<template #n><b>{{ number(stats.originalNotesCount) }}</b></template>
 					</I18n>
 				</div>
-				<I18n :src="$ts.onlineUsersCount" text-tag="span" class="online">
+				<I18n :src="i18n.ts.onlineUsersCount" text-tag="span" class="online">
 					<template #n><b>{{ onlineUsersCount }}</b></template>
 				</I18n>
 			</div>
@@ -47,6 +47,7 @@ import MkFeaturedPhotos from '@/components/MkFeaturedPhotos.vue';
 import { host, instanceName } from '@/config';
 import * as os from '@/os';
 import number from '@/filters/number';
+import { i18n } from '@/i18n';
 
 export default defineComponent({
 	components: {
@@ -64,6 +65,7 @@ export default defineComponent({
 			stats: null,
 			tags: [],
 			onlineUsersCount: null,
+			i18n,
 		};
 	},
 
@@ -103,19 +105,19 @@ export default defineComponent({
 
 		showMenu(ev) {
 			os.popupMenu([{
-				text: this.$t('aboutX', { x: instanceName }),
+				text: i18n.t('aboutX', { x: instanceName }),
 				icon: 'ti ti-info-circle',
 				action: () => {
 					os.pageWindow('/about');
 				},
 			}, {
-				text: this.$ts.aboutMisskey,
+				text: i18n.ts.aboutMisskey,
 				icon: 'ti ti-info-circle',
 				action: () => {
 					os.pageWindow('/about-misskey');
 				},
 			}, null, {
-				text: this.$ts.help,
+				text: i18n.ts.help,
 				icon: 'ti ti-question-circle',
 				action: () => {
 					window.open('https://misskey-hub.net/help.md', '_blank');
diff --git a/packages/frontend/src/pages/welcome.entrance.c.vue b/packages/frontend/src/pages/welcome.entrance.c.vue
index f566d1a56a..eca4e5764d 100644
--- a/packages/frontend/src/pages/welcome.entrance.c.vue
+++ b/packages/frontend/src/pages/welcome.entrance.c.vue
@@ -22,22 +22,22 @@
 					</h1>
 					<div class="about">
 						<!-- eslint-disable-next-line vue/no-v-html -->
-						<div class="desc" v-html="meta.description || $ts.headlineMisskey"></div>
+						<div class="desc" v-html="meta.description || i18n.ts.headlineMisskey"></div>
 					</div>
 					<div class="action">
-						<MkButton inline gradate @click="signup()">{{ $ts.signup }}</MkButton>
-						<MkButton inline @click="signin()">{{ $ts.login }}</MkButton>
+						<MkButton inline gradate @click="signup()">{{ i18n.ts.signup }}</MkButton>
+						<MkButton inline @click="signin()">{{ i18n.ts.login }}</MkButton>
 					</div>
 					<div v-if="onlineUsersCount && stats" class="status">
 						<div>
-							<I18n :src="$ts.nUsers" text-tag="span" class="users">
+							<I18n :src="i18n.ts.nUsers" text-tag="span" class="users">
 								<template #n><b>{{ number(stats.originalUsersCount) }}</b></template>
 							</I18n>
-							<I18n :src="$ts.nNotes" text-tag="span" class="notes">
+							<I18n :src="i18n.ts.nNotes" text-tag="span" class="notes">
 								<template #n><b>{{ number(stats.originalNotesCount) }}</b></template>
 							</I18n>
 						</div>
-						<I18n :src="$ts.onlineUsersCount" text-tag="span" class="online">
+						<I18n :src="i18n.ts.onlineUsersCount" text-tag="span" class="online">
 							<template #n><b>{{ onlineUsersCount }}</b></template>
 						</I18n>
 					</div>
@@ -45,10 +45,10 @@
 				</div>
 			</div>
 			<nav class="nav">
-				<MkA to="/announcements">{{ $ts.announcements }}</MkA>
-				<MkA to="/explore">{{ $ts.explore }}</MkA>
-				<MkA to="/channels">{{ $ts.channel }}</MkA>
-				<MkA to="/featured">{{ $ts.featured }}</MkA>
+				<MkA to="/announcements">{{ i18n.ts.announcements }}</MkA>
+				<MkA to="/explore">{{ i18n.ts.explore }}</MkA>
+				<MkA to="/channels">{{ i18n.ts.channel }}</MkA>
+				<MkA to="/featured">{{ i18n.ts.featured }}</MkA>
 			</nav>
 		</div>
 	</div>
@@ -58,15 +58,16 @@
 <script lang="ts">
 import { defineComponent } from 'vue';
 import { toUnicode } from 'punycode/';
+import XTimeline from './welcome.timeline.vue';
 import XSigninDialog from '@/components/MkSigninDialog.vue';
 import XSignupDialog from '@/components/MkSignupDialog.vue';
 import MkButton from '@/components/MkButton.vue';
 import MkNote from '@/components/MkNote.vue';
 import MkFeaturedPhotos from '@/components/MkFeaturedPhotos.vue';
-import XTimeline from './welcome.timeline.vue';
 import { host, instanceName } from '@/config';
 import * as os from '@/os';
 import number from '@/filters/number';
+import { i18n } from '@/i18n';
 
 export default defineComponent({
 	components: {
@@ -84,6 +85,7 @@ export default defineComponent({
 			stats: null,
 			tags: [],
 			onlineUsersCount: null,
+			i18n,
 		};
 	},
 
@@ -123,22 +125,22 @@ export default defineComponent({
 
 		showMenu(ev) {
 			os.popupMenu([{
-				text: this.$t('aboutX', { x: instanceName }),
+				text: i18n.t('aboutX', { x: instanceName }),
 				icon: 'ti ti-info-circle',
 				action: () => {
 					os.pageWindow('/about');
 				},
 			}, {
-				text: this.$ts.aboutMisskey,
+				text: i18n.ts.aboutMisskey,
 				icon: 'ti ti-info-circle',
 				action: () => {
 					os.pageWindow('/about-misskey');
 				},
 			}, null, {
-				text: this.$ts.help,
+				text: i18n.ts.help,
 				icon: 'ti ti-question-circle',
 				action: () => {
-					window.open(`https://misskey-hub.net/help.md`, '_blank');
+					window.open('https://misskey-hub.net/help.md', '_blank');
 				},
 			}], ev.currentTarget ?? ev.target);
 		},
diff --git a/packages/frontend/src/pages/welcome.setup.vue b/packages/frontend/src/pages/welcome.setup.vue
index 8b43fa368b..212d156a83 100644
--- a/packages/frontend/src/pages/welcome.setup.vue
+++ b/packages/frontend/src/pages/welcome.setup.vue
@@ -2,19 +2,19 @@
 <form class="mk-setup" @submit.prevent="submit()">
 	<h1>Welcome to Misskey!</h1>
 	<div class="_gaps_m">
-		<p>{{ $ts.intro }}</p>
+		<p>{{ i18n.ts.intro }}</p>
 		<MkInput v-model="username" pattern="^[a-zA-Z0-9_]{1,20}$" :spellcheck="false" required data-cy-admin-username>
-			<template #label>{{ $ts.username }}</template>
+			<template #label>{{ i18n.ts.username }}</template>
 			<template #prefix>@</template>
 			<template #suffix>@{{ host }}</template>
 		</MkInput>
 		<MkInput v-model="password" type="password" data-cy-admin-password>
-			<template #label>{{ $ts.password }}</template>
+			<template #label>{{ i18n.ts.password }}</template>
 			<template #prefix><i class="ti ti-lock"></i></template>
 		</MkInput>
 		<div class="bottom">
 			<MkButton gradate type="submit" :disabled="submitting" data-cy-admin-ok>
-				{{ submitting ? $ts.processing : $ts.done }}<MkEllipsis v-if="submitting"/>
+				{{ submitting ? i18n.ts.processing : i18n.ts.done }}<MkEllipsis v-if="submitting"/>
 			</MkButton>
 		</div>
 	</div>
diff --git a/packages/frontend/src/ui/classic.header.vue b/packages/frontend/src/ui/classic.header.vue
index 1a4d1c454f..5e632c16d0 100644
--- a/packages/frontend/src/ui/classic.header.vue
+++ b/packages/frontend/src/ui/classic.header.vue
@@ -5,7 +5,7 @@
 			<button v-click-anime class="item _button instance" @click="openInstanceMenu">
 				<img :src="instance.iconUrl ?? instance.faviconUrl ?? '/favicon.ico'" class="_ghost"/>
 			</button>
-			<MkA v-click-anime v-tooltip="$ts.timeline" class="item index" active-class="active" to="/" exact>
+			<MkA v-click-anime v-tooltip="i18n.ts.timeline" class="item index" active-class="active" to="/" exact>
 				<i class="ti ti-home ti-fw"></i>
 			</MkA>
 			<template v-for="item in menu">
@@ -16,7 +16,7 @@
 				</component>
 			</template>
 			<div class="divider"></div>
-			<MkA v-if="$i.isAdmin || $i.isModerator" v-click-anime v-tooltip="$ts.controlPanel" class="item" active-class="active" to="/admin" :behavior="settingsWindowed ? 'modalWindow' : null">
+			<MkA v-if="$i.isAdmin || $i.isModerator" v-click-anime v-tooltip="i18n.ts.controlPanel" class="item" active-class="active" to="/admin" :behavior="settingsWindowed ? 'modalWindow' : null">
 				<i class="ti ti-dashboard ti-fw"></i>
 			</MkA>
 			<button v-click-anime class="item _button" @click="more">
@@ -25,7 +25,7 @@
 			</button>
 		</div>
 		<div class="right">
-			<MkA v-click-anime v-tooltip="$ts.settings" class="item" active-class="active" to="/settings" :behavior="settingsWindowed ? 'modalWindow' : null">
+			<MkA v-click-anime v-tooltip="i18n.ts.settings" class="item" active-class="active" to="/settings" :behavior="settingsWindowed ? 'modalWindow' : null">
 				<i class="ti ti-settings ti-fw"></i>
 			</MkA>
 			<button v-click-anime class="item _button account" @click="openAccountMenu">
@@ -52,6 +52,7 @@ import MkButton from '@/components/MkButton.vue';
 import { mainRouter } from '@/router';
 import { defaultStore } from '@/store';
 import { instance } from '@/instance';
+import { i18n } from '@/i18n';
 
 export default defineComponent({
 	components: {
@@ -68,6 +69,7 @@ export default defineComponent({
 			defaultStore,
 			instance,
 			$i,
+			i18n,
 		};
 	},
 
diff --git a/packages/frontend/src/ui/classic.sidebar.vue b/packages/frontend/src/ui/classic.sidebar.vue
index 10aa615b76..42ea5cd785 100644
--- a/packages/frontend/src/ui/classic.sidebar.vue
+++ b/packages/frontend/src/ui/classic.sidebar.vue
@@ -5,12 +5,12 @@
 	</button>
 	<div class="post" data-cy-open-post-form @click="post">
 		<MkButton class="button" gradate full rounded>
-			<i class="ti ti-pencil ti-fw"></i><span v-if="!iconOnly" class="text">{{ $ts.note }}</span>
+			<i class="ti ti-pencil ti-fw"></i><span v-if="!iconOnly" class="text">{{ i18n.ts.note }}</span>
 		</MkButton>
 	</div>
 	<div class="divider"></div>
 	<MkA v-click-anime class="item index" active-class="active" to="/" exact>
-		<i class="ti ti-home ti-fw"></i><span class="text">{{ $ts.timeline }}</span>
+		<i class="ti ti-home ti-fw"></i><span class="text">{{ i18n.ts.timeline }}</span>
 	</MkA>
 	<template v-for="item in menu">
 		<div v-if="item === '-'" class="divider"></div>
@@ -21,14 +21,14 @@
 	</template>
 	<div class="divider"></div>
 	<MkA v-if="$i.isAdmin || $i.isModerator" v-click-anime class="item" active-class="active" to="/admin" :behavior="settingsWindowed ? 'modalWindow' : null">
-		<i class="ti ti-dashboard ti-fw"></i><span class="text">{{ $ts.controlPanel }}</span>
+		<i class="ti ti-dashboard ti-fw"></i><span class="text">{{ i18n.ts.controlPanel }}</span>
 	</MkA>
 	<button v-click-anime class="item _button" @click="more">
-		<i class="ti ti-dots ti-fw"></i><span class="text">{{ $ts.more }}</span>
+		<i class="ti ti-dots ti-fw"></i><span class="text">{{ i18n.ts.more }}</span>
 		<span v-if="otherNavItemIndicated" class="indicator"><i class="_indicatorCircle"></i></span>
 	</button>
 	<MkA v-click-anime class="item" active-class="active" to="/settings" :behavior="settingsWindowed ? 'modalWindow' : null">
-		<i class="ti ti-settings ti-fw"></i><span class="text">{{ $ts.settings }}</span>
+		<i class="ti ti-settings ti-fw"></i><span class="text">{{ i18n.ts.settings }}</span>
 	</MkA>
 	<div class="divider"></div>
 	<div class="about">
@@ -53,6 +53,7 @@ import { mainRouter } from '@/router';
 //import MisskeyLogo from '@assets/client/misskey.svg';
 import { defaultStore } from '@/store';
 import { instance } from '@/instance';
+import { i18n } from '@/i18n';
 
 export default defineComponent({
 	components: {
@@ -71,6 +72,7 @@ export default defineComponent({
 			defaultStore,
 			instance,
 			$i,
+			i18n,
 		};
 	},
 
diff --git a/packages/frontend/src/ui/visitor/a.vue b/packages/frontend/src/ui/visitor/a.vue
index 1ac5decf42..4761036075 100644
--- a/packages/frontend/src/ui/visitor/a.vue
+++ b/packages/frontend/src/ui/visitor/a.vue
@@ -5,11 +5,11 @@
 			<h1 v-if="meta"><img v-if="meta.logoImageUrl" class="logo" :src="meta.logoImageUrl"><span v-else class="text">{{ instanceName }}</span></h1>
 			<div v-if="meta" class="about">
 				<!-- eslint-disable-next-line vue/no-v-html -->
-				<div class="desc" v-html="meta.description || $ts.introMisskey"></div>
+				<div class="desc" v-html="meta.description || i18n.ts.introMisskey"></div>
 			</div>
 			<div class="action">
-				<button class="_button primary" @click="signup()">{{ $ts.signup }}</button>
-				<button class="_button" @click="signin()">{{ $ts.login }}</button>
+				<button class="_button primary" @click="signup()">{{ i18n.ts.signup }}</button>
+				<button class="_button" @click="signin()">{{ i18n.ts.login }}</button>
 			</div>
 		</div>
 	</div>
@@ -45,6 +45,7 @@ import MkButton from '@/components/MkButton.vue';
 import { defaultStore, ColdDeviceStorage } from '@/store';
 import { mainRouter } from '@/router';
 import { instance } from '@/instance';
+import { i18n } from '@/i18n';
 
 const DESKTOP_THRESHOLD = 1100;
 
@@ -69,6 +70,7 @@ export default defineComponent({
 			isDesktop: window.innerWidth >= DESKTOP_THRESHOLD,
 			defaultStore,
 			instance,
+			i18n,
 		};
 	},
 
diff --git a/packages/frontend/src/ui/visitor/b.vue b/packages/frontend/src/ui/visitor/b.vue
index fd13439326..5287a670c5 100644
--- a/packages/frontend/src/ui/visitor/b.vue
+++ b/packages/frontend/src/ui/visitor/b.vue
@@ -35,18 +35,18 @@
 
 	<Transition :name="'tray'">
 		<div v-if="showMenu" class="menu">
-			<MkA to="/" class="link" active-class="active"><i class="ti ti-home icon"></i>{{ $ts.home }}</MkA>
-			<MkA v-if="isTimelineAvailable" to="/timeline" class="link" active-class="active"><i class="ti ti-message icon"></i>{{ $ts.timeline }}</MkA>
-			<MkA to="/explore" class="link" active-class="active"><i class="ti ti-hash icon"></i>{{ $ts.explore }}</MkA>
-			<MkA to="/announcements" class="link" active-class="active"><i class="ti ti-speakerphone icon"></i>{{ $ts.announcements }}</MkA>
-			<MkA to="/channels" class="link" active-class="active"><i class="ti ti-device-tv icon"></i>{{ $ts.channel }}</MkA>
+			<MkA to="/" class="link" active-class="active"><i class="ti ti-home icon"></i>{{ i18n.ts.home }}</MkA>
+			<MkA v-if="isTimelineAvailable" to="/timeline" class="link" active-class="active"><i class="ti ti-message icon"></i>{{ i18n.ts.timeline }}</MkA>
+			<MkA to="/explore" class="link" active-class="active"><i class="ti ti-hash icon"></i>{{ i18n.ts.explore }}</MkA>
+			<MkA to="/announcements" class="link" active-class="active"><i class="ti ti-speakerphone icon"></i>{{ i18n.ts.announcements }}</MkA>
+			<MkA to="/channels" class="link" active-class="active"><i class="ti ti-device-tv icon"></i>{{ i18n.ts.channel }}</MkA>
 			<div class="divider"></div>
-			<MkA to="/pages" class="link" active-class="active"><i class="ti ti-news icon"></i>{{ $ts.pages }}</MkA>
+			<MkA to="/pages" class="link" active-class="active"><i class="ti ti-news icon"></i>{{ i18n.ts.pages }}</MkA>
 			<MkA to="/play" class="link" active-class="active"><i class="ti ti-player-play icon"></i>Play</MkA>
-			<MkA to="/gallery" class="link" active-class="active"><i class="ti ti-icons icon"></i>{{ $ts.gallery }}</MkA>
+			<MkA to="/gallery" class="link" active-class="active"><i class="ti ti-icons icon"></i>{{ i18n.ts.gallery }}</MkA>
 			<div class="action">
-				<button class="_buttonPrimary" @click="signup()">{{ $ts.signup }}</button>
-				<button class="_button" @click="signin()">{{ $ts.login }}</button>
+				<button class="_buttonPrimary" @click="signup()">{{ i18n.ts.signup }}</button>
+				<button class="_button" @click="signin()">{{ i18n.ts.login }}</button>
 			</div>
 		</div>
 	</Transition>
@@ -65,6 +65,7 @@ import XSignupDialog from '@/components/MkSignupDialog.vue';
 import { ColdDeviceStorage, defaultStore } from '@/store';
 import { mainRouter } from '@/router';
 import { PageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata';
+import { i18n } from '@/i18n';
 
 const DESKTOP_THRESHOLD = 1100;
 
diff --git a/packages/frontend/src/ui/visitor/header.vue b/packages/frontend/src/ui/visitor/header.vue
index aaa7e77e90..7de81f6431 100644
--- a/packages/frontend/src/ui/visitor/header.vue
+++ b/packages/frontend/src/ui/visitor/header.vue
@@ -2,14 +2,14 @@
 <div class="sqxihjet">
 	<div v-if="narrow === false" class="wide">
 		<div class="content">
-			<MkA to="/" class="link" active-class="active"><i class="ti ti-home icon"></i>{{ $ts.home }}</MkA>
-			<MkA v-if="isTimelineAvailable" to="/timeline" class="link" active-class="active"><i class="ti ti-message icon"></i>{{ $ts.timeline }}</MkA>
-			<MkA to="/explore" class="link" active-class="active"><i class="ti ti-hash icon"></i>{{ $ts.explore }}</MkA>
-			<MkA to="/channels" class="link" active-class="active"><i class="ti ti-device-tv icon"></i>{{ $ts.channel }}</MkA>
+			<MkA to="/" class="link" active-class="active"><i class="ti ti-home icon"></i>{{ i18n.ts.home }}</MkA>
+			<MkA v-if="isTimelineAvailable" to="/timeline" class="link" active-class="active"><i class="ti ti-message icon"></i>{{ i18n.ts.timeline }}</MkA>
+			<MkA to="/explore" class="link" active-class="active"><i class="ti ti-hash icon"></i>{{ i18n.ts.explore }}</MkA>
+			<MkA to="/channels" class="link" active-class="active"><i class="ti ti-device-tv icon"></i>{{ i18n.ts.channel }}</MkA>
 			<div class="right">
-				<button class="_button search" @click="search()"><i class="ti ti-search icon"></i><span>{{ $ts.search }}</span></button>
-				<button class="_buttonPrimary signup" @click="signup()">{{ $ts.signup }}</button>
-				<button class="_button login" @click="signin()">{{ $ts.login }}</button>
+				<button class="_button search" @click="search()"><i class="ti ti-search icon"></i><span>{{ i18n.ts.search }}</span></button>
+				<button class="_buttonPrimary signup" @click="signup()">{{ i18n.ts.signup }}</button>
+				<button class="_button login" @click="signin()">{{ i18n.ts.login }}</button>
 			</div>
 		</div>
 	</div>
@@ -28,6 +28,7 @@ import XSignupDialog from '@/components/MkSignupDialog.vue';
 import * as os from '@/os';
 import { instance } from '@/instance';
 import { mainRouter } from '@/router';
+import { i18n } from '@/i18n';
 
 export default defineComponent({
 	data() {
@@ -35,6 +36,7 @@ export default defineComponent({
 			narrow: null,
 			showMenu: false,
 			isTimelineAvailable: instance.policies.ltlAvailable || instance.policies.gtlAvailable,
+			i18n,
 		};
 	},
 
diff --git a/packages/frontend/src/ui/visitor/kanban.vue b/packages/frontend/src/ui/visitor/kanban.vue
index 25d925e67f..ce7fcfe944 100644
--- a/packages/frontend/src/ui/visitor/kanban.vue
+++ b/packages/frontend/src/ui/visitor/kanban.vue
@@ -9,14 +9,14 @@
 			</h1>
 			<template v-if="full">
 				<div v-if="meta" class="about">
-					<div class="desc" v-html="meta.description || $ts.introMisskey"></div>
+					<div class="desc" v-html="meta.description || i18n.ts.introMisskey"></div>
 				</div>
 				<div class="action">
-					<button class="_buttonPrimary" @click="signup()">{{ $ts.signup }}</button>
-					<button class="_button" @click="signin()">{{ $ts.login }}</button>
+					<button class="_buttonPrimary" @click="signup()">{{ i18n.ts.signup }}</button>
+					<button class="_button" @click="signin()">{{ i18n.ts.login }}</button>
 				</div>
 				<div class="announcements panel">
-					<header>{{ $ts.announcements }}</header>
+					<header>{{ i18n.ts.announcements }}</header>
 					<MkPagination v-slot="{items}" :pagination="announcements" class="list">
 						<section v-for="announcement in items" :key="announcement.id" class="item">
 							<div class="title">{{ announcement.title }}</div>
@@ -46,6 +46,7 @@ import XSigninDialog from '@/components/MkSigninDialog.vue';
 import XSignupDialog from '@/components/MkSignupDialog.vue';
 import MkButton from '@/components/MkButton.vue';
 import { instance } from '@/instance';
+import { i18n } from '@/i18n';
 
 export default defineComponent({
 	components: {
@@ -83,6 +84,7 @@ export default defineComponent({
 				limit: 10,
 			},
 			instance,
+			i18n,
 		};
 	},
 
diff --git a/packages/frontend/src/widgets/WidgetCalendar.vue b/packages/frontend/src/widgets/WidgetCalendar.vue
index de2e4b179d..58d0732263 100644
--- a/packages/frontend/src/widgets/WidgetCalendar.vue
+++ b/packages/frontend/src/widgets/WidgetCalendar.vue
@@ -2,11 +2,11 @@
 <div :class="[$style.root, { _panel: !widgetProps.transparent }]" data-cy-mkw-calendar>
 	<div :class="[$style.calendar, { [$style.isHoliday]: isHoliday }]">
 		<p :class="$style.monthAndYear">
-			<span :class="$style.year">{{ $t('yearX', { year }) }}</span>
-			<span :class="$style.month">{{ $t('monthX', { month }) }}</span>
+			<span :class="$style.year">{{ i18n.t('yearX', { year }) }}</span>
+			<span :class="$style.month">{{ i18n.t('monthX', { month }) }}</span>
 		</p>
-		<p v-if="month === 1 && day === 1" class="day">๐ŸŽ‰{{ $t('dayX', { day }) }}<span style="display: inline-block; transform: scaleX(-1);">๐ŸŽ‰</span></p>
-		<p v-else :class="$style.day">{{ $t('dayX', { day }) }}</p>
+		<p v-if="month === 1 && day === 1" class="day">๐ŸŽ‰{{ i18n.t('dayX', { day }) }}<span style="display: inline-block; transform: scaleX(-1);">๐ŸŽ‰</span></p>
+		<p v-else :class="$style.day">{{ i18n.t('dayX', { day }) }}</p>
 		<p :class="$style.weekDay">{{ weekDay }}</p>
 	</div>
 	<div :class="$style.info">
diff --git a/packages/frontend/src/widgets/WidgetSlideshow.vue b/packages/frontend/src/widgets/WidgetSlideshow.vue
index 22a0024271..915e7aaaf4 100644
--- a/packages/frontend/src/widgets/WidgetSlideshow.vue
+++ b/packages/frontend/src/widgets/WidgetSlideshow.vue
@@ -4,7 +4,7 @@
 		<p v-if="widgetProps.folderId == null">
 			{{ i18n.ts.folder }}
 		</p>
-		<p v-if="widgetProps.folderId != null && images.length === 0 && !fetching">{{ $t('no-image') }}</p>
+		<p v-if="widgetProps.folderId != null && images.length === 0 && !fetching">{{ i18n.t('no-image') }}</p>
 		<div ref="slideA" class="slide a"></div>
 		<div ref="slideB" class="slide b"></div>
 	</div>
diff --git a/packages/frontend/src/widgets/WidgetTimeline.vue b/packages/frontend/src/widgets/WidgetTimeline.vue
index 0f6f25b0a9..71ee75f6cb 100644
--- a/packages/frontend/src/widgets/WidgetTimeline.vue
+++ b/packages/frontend/src/widgets/WidgetTimeline.vue
@@ -10,7 +10,7 @@
 	</template>
 	<template #header>
 		<button class="_button" @click="choose">
-			<span>{{ widgetProps.src === 'list' ? widgetProps.list.name : widgetProps.src === 'antenna' ? widgetProps.antenna.name : $t('_timelines.' + widgetProps.src) }}</span>
+			<span>{{ widgetProps.src === 'list' ? widgetProps.list.name : widgetProps.src === 'antenna' ? widgetProps.antenna.name : i18n.t('_timelines.' + widgetProps.src) }}</span>
 			<i :class="menuOpened ? 'ti ti-chevron-up' : 'ti ti-chevron-down'" style="margin-left: 8px;"></i>
 		</button>
 	</template>
diff --git a/packages/frontend/src/widgets/WidgetTrends.vue b/packages/frontend/src/widgets/WidgetTrends.vue
index a53f270df5..01450a7ab5 100644
--- a/packages/frontend/src/widgets/WidgetTrends.vue
+++ b/packages/frontend/src/widgets/WidgetTrends.vue
@@ -9,7 +9,7 @@
 			<div v-for="stat in stats" :key="stat.tag">
 				<div class="tag">
 					<MkA class="a" :to="`/tags/${ encodeURIComponent(stat.tag) }`" :title="stat.tag">#{{ stat.tag }}</MkA>
-					<p>{{ $t('nUsersMentioned', { n: stat.usersCount }) }}</p>
+					<p>{{ i18n.t('nUsersMentioned', { n: stat.usersCount }) }}</p>
 				</div>
 				<MkMiniChart class="chart" :src="stat.chart"/>
 			</div>