diff --git a/packages/client/src/components/MkNoteSub.vue b/packages/client/src/components/MkNoteSub.vue
index 30c27e623..9ae773bfb 100644
--- a/packages/client/src/components/MkNoteSub.vue
+++ b/packages/client/src/components/MkNoteSub.vue
@@ -6,7 +6,7 @@
 			<XNoteHeader class="header" :note="note" :mini="true"/>
 			<div class="body">
 				<p v-if="note.cw != null" class="cw">
-					<Mfm v-if="note.cw != ''" class="text" :text="note.cw" :author="note.user" :i="$i" :custom-emojis="note.emojis" />
+					<Mfm v-if="note.cw != ''" class="text" :text="note.cw" :author="note.user" :i="$i" :custom-emojis="note.emojis"/>
 					<XCwButton v-model="showContent" :note="note"/>
 				</p>
 				<div v-show="note.cw == null || showContent" class="content">
@@ -19,7 +19,7 @@
 		<MkNoteSub v-for="reply in replies" :key="reply.id" :note="reply" class="reply" :detail="true" :depth="depth + 1"/>
 	</template>
 	<div v-else class="more">
-		<MkA class="text _link" :to="notePage(note)">{{ $ts.continueThread }} <i class="fas fa-angle-double-right"></i></MkA>
+		<MkA class="text _link" :to="notePage(note)">{{ i18n.ts.continueThread }} <i class="fas fa-angle-double-right"></i></MkA>
 	</div>
 </div>
 </template>
@@ -27,11 +27,12 @@
 <script lang="ts" setup>
 import { } from 'vue';
 import * as misskey from 'misskey-js';
-import { notePage } from '@/filters/note';
 import XNoteHeader from './note-header.vue';
 import MkNoteSubNoteContent from './sub-note-content.vue';
 import XCwButton from './cw-button.vue';
+import { notePage } from '@/filters/note';
 import * as os from '@/os';
+import { i18n } from '@/i18n';
 
 const props = withDefaults(defineProps<{
 	note: misskey.entities.Note;
@@ -49,7 +50,7 @@ let replies: misskey.entities.Note[] = $ref([]);
 if (props.detail) {
 	os.api('notes/children', {
 		noteId: props.note.id,
-		limit: 5
+		limit: 5,
 	}).then(res => {
 		replies = res;
 	});
diff --git a/packages/client/src/components/abuse-report.vue b/packages/client/src/components/abuse-report.vue
index 2b89eef85..8c25df110 100644
--- a/packages/client/src/components/abuse-report.vue
+++ b/packages/client/src/components/abuse-report.vue
@@ -9,7 +9,7 @@
 			</div>
 		</MkA>
 		<MkKeyValue class="_formBlock">
-			<template #key>{{ $ts.registeredDate }}</template>
+			<template #key>{{ i18n.ts.registeredDate }}</template>
 			<template #value>{{ new Date(report.targetUser.createdAt).toLocaleString() }} (<MkTime :time="report.targetUser.createdAt"/>)</template>
 		</MkKeyValue>
 	</div>
@@ -18,18 +18,18 @@
 			<Mfm :text="report.comment"/>
 		</div>
 		<hr/>
-		<div>{{ $ts.reporter }}: <MkAcct :user="report.reporter"/></div>
+		<div>{{ i18n.ts.reporter }}: <MkAcct :user="report.reporter"/></div>
 		<div v-if="report.assignee">
-			{{ $ts.moderator }}:
+			{{ i18n.ts.moderator }}:
 			<MkAcct :user="report.assignee"/>
 		</div>
 		<div><MkTime :time="report.createdAt"/></div>
 		<div class="action">
 			<MkSwitch v-model="forward" :disabled="report.targetUser.host == null || report.resolved">
-				{{ $ts.forwardReport }}
-				<template #caption>{{ $ts.forwardReportIsAnonymous }}</template>
+				{{ i18n.ts.forwardReport }}
+				<template #caption>{{ i18n.ts.forwardReportIsAnonymous }}</template>
 			</MkSwitch>
-			<MkButton v-if="!report.resolved" primary @click="resolve">{{ $ts.abuseMarkAsResolved }}</MkButton>
+			<MkButton v-if="!report.resolved" primary @click="resolve">{{ i18n.ts.abuseMarkAsResolved }}</MkButton>
 		</div>
 	</div>
 </div>
@@ -41,6 +41,7 @@ import MkSwitch from '@/components/form/switch.vue';
 import MkKeyValue from '@/components/key-value.vue';
 import { acct, userPage } from '@/filters/user';
 import * as os from '@/os';
+import { i18n } from '@/i18n';
 
 const props = defineProps<{
 	report: any;
diff --git a/packages/client/src/components/cropper-dialog.vue b/packages/client/src/components/cropper-dialog.vue
index a8bde6ea0..c320b21d7 100644
--- a/packages/client/src/components/cropper-dialog.vue
+++ b/packages/client/src/components/cropper-dialog.vue
@@ -9,7 +9,7 @@
 	@ok="ok()"
 	@closed="$emit('closed')"
 >
-	<template #header>{{ $ts.cropImage }}</template>
+	<template #header>{{ i18n.ts.cropImage }}</template>
 	<template #default="{ width, height }">
 		<div class="mk-cropper-dialog" :style="`--vw: ${width}px; --vh: ${height}px;`">
 			<Transition name="fade">
@@ -36,6 +36,7 @@ import { $i } from '@/account';
 import { defaultStore } from '@/store';
 import { apiUrl, url } from '@/config';
 import { query } from '@/scripts/url';
+import { i18n } from '@/i18n';
 
 const emit = defineEmits<{
 	(ev: 'ok', cropped: misskey.entities.DriveFile): void;
diff --git a/packages/client/src/components/form/checkbox.vue b/packages/client/src/components/form/checkbox.vue
index fadb770ae..fb5c82bb4 100644
--- a/packages/client/src/components/form/checkbox.vue
+++ b/packages/client/src/components/form/checkbox.vue
@@ -9,7 +9,7 @@
 		:disabled="disabled"
 		@keydown.enter="toggle"
 	>
-	<span ref="button" v-adaptive-border v-tooltip="checked ? $ts.itsOn : $ts.itsOff" class="button" @click.prevent="toggle">
+	<span ref="button" v-adaptive-border v-tooltip="checked ? i18n.ts.itsOn : i18n.ts.itsOff" class="button" @click.prevent="toggle">
 		<i class="check fas fa-check"></i>
 	</span>
 	<span class="label">
@@ -24,6 +24,7 @@
 import { toRefs, Ref } from 'vue';
 import * as os from '@/os';
 import Ripple from '@/components/ripple.vue';
+import { i18n } from '@/i18n';
 
 const props = defineProps<{
 	modelValue: boolean | Ref<boolean>;
diff --git a/packages/client/src/components/form/input.vue b/packages/client/src/components/form/input.vue
index 2a03d6a5d..1c9fee8c7 100644
--- a/packages/client/src/components/form/input.vue
+++ b/packages/client/src/components/form/input.vue
@@ -29,7 +29,7 @@
 	</div>
 	<div class="caption"><slot name="caption"></slot></div>
 
-	<MkButton v-if="manualSave && changed" primary class="save" @click="updated"><i class="fas fa-check"></i> {{ $ts.save }}</MkButton>
+	<MkButton v-if="manualSave && changed" primary class="save" @click="updated"><i class="fas fa-check"></i> {{ i18n.ts.save }}</MkButton>
 </div>
 </template>
 
@@ -38,6 +38,7 @@ import { onMounted, onUnmounted, nextTick, ref, watch, computed, toRefs } from '
 import { debounce } from 'throttle-debounce';
 import MkButton from '@/components/ui/button.vue';
 import { useInterval } from '@/scripts/use-interval';
+import { i18n } from '@/i18n';
 
 const props = defineProps<{
 	modelValue: string | number;
diff --git a/packages/client/src/components/form/select.vue b/packages/client/src/components/form/select.vue
index 78282dfdc..7a1f11aa7 100644
--- a/packages/client/src/components/form/select.vue
+++ b/packages/client/src/components/form/select.vue
@@ -22,7 +22,7 @@
 	</div>
 	<div class="caption"><slot name="caption"></slot></div>
 
-	<MkButton v-if="manualSave && changed" primary @click="updated"><i class="fas fa-save"></i> {{ $ts.save }}</MkButton>
+	<MkButton v-if="manualSave && changed" primary @click="updated"><i class="fas fa-save"></i> {{ i18n.ts.save }}</MkButton>
 </div>
 </template>
 
@@ -31,6 +31,7 @@ import { onMounted, onUnmounted, nextTick, ref, watch, computed, toRefs, VNode,
 import MkButton from '@/components/ui/button.vue';
 import * as os from '@/os';
 import { useInterval } from '@/scripts/use-interval';
+import { i18n } from '@/i18n';
 
 const props = defineProps<{
 	modelValue: string;
diff --git a/packages/client/src/components/form/switch.vue b/packages/client/src/components/form/switch.vue
index fead16355..1ed00ae65 100644
--- a/packages/client/src/components/form/switch.vue
+++ b/packages/client/src/components/form/switch.vue
@@ -9,7 +9,7 @@
 		:disabled="disabled"
 		@keydown.enter="toggle"
 	>
-	<span ref="button" v-tooltip="checked ? $ts.itsOn : $ts.itsOff" class="button" @click.prevent="toggle">
+	<span ref="button" v-tooltip="checked ? i18n.ts.itsOn : i18n.ts.itsOff" class="button" @click.prevent="toggle">
 		<div class="knob"></div>
 	</span>
 	<span class="label">
@@ -23,6 +23,7 @@
 <script lang="ts" setup>
 import { toRefs, Ref } from 'vue';
 import * as os from '@/os';
+import { i18n } from '@/i18n';
 
 const props = defineProps<{
 	modelValue: boolean | Ref<boolean>;
diff --git a/packages/client/src/components/form/textarea.vue b/packages/client/src/components/form/textarea.vue
index c9ba9b97a..cb502a422 100644
--- a/packages/client/src/components/form/textarea.vue
+++ b/packages/client/src/components/form/textarea.vue
@@ -2,7 +2,8 @@
 <div class="adhpbeos">
 	<div class="label" @click="focus"><slot name="label"></slot></div>
 	<div class="input" :class="{ disabled, focused, tall, pre }">
-		<textarea ref="inputEl"
+		<textarea
+			ref="inputEl"
 			v-model="v"
 			v-adaptive-border
 			:class="{ code, _monospace: code }"
@@ -21,14 +22,15 @@
 	</div>
 	<div class="caption"><slot name="caption"></slot></div>
 
-	<MkButton v-if="manualSave && changed" primary class="save" @click="updated"><i class="fas fa-save"></i> {{ $ts.save }}</MkButton>
+	<MkButton v-if="manualSave && changed" primary class="save" @click="updated"><i class="fas fa-save"></i> {{ i18n.ts.save }}</MkButton>
 </div>
 </template>
 
 <script lang="ts">
 import { defineComponent, onMounted, onUnmounted, nextTick, ref, watch, computed, toRefs } from 'vue';
-import MkButton from '@/components/ui/button.vue';
 import { debounce } from 'throttle-debounce';
+import MkButton from '@/components/ui/button.vue';
+import { i18n } from '@/i18n';
 
 export default defineComponent({
 	components: {
@@ -37,66 +39,66 @@ export default defineComponent({
 
 	props: {
 		modelValue: {
-			required: true
+			required: true,
 		},
 		type: {
 			type: String,
-			required: false
+			required: false,
 		},
 		required: {
 			type: Boolean,
-			required: false
+			required: false,
 		},
 		readonly: {
 			type: Boolean,
-			required: false
+			required: false,
 		},
 		disabled: {
 			type: Boolean,
-			required: false
+			required: false,
 		},
 		pattern: {
 			type: String,
-			required: false
+			required: false,
 		},
 		placeholder: {
 			type: String,
-			required: false
+			required: false,
 		},
 		autofocus: {
 			type: Boolean,
 			required: false,
-			default: false
+			default: false,
 		},
 		autocomplete: {
-			required: false
+			required: false,
 		},
 		spellcheck: {
-			required: false
+			required: false,
 		},
 		code: {
 			type: Boolean,
-			required: false
+			required: false,
 		},
 		tall: {
 			type: Boolean,
 			required: false,
-			default: false
+			default: false,
 		},
 		pre: {
 			type: Boolean,
 			required: false,
-			default: false
+			default: false,
 		},
 		debounce: {
 			type: Boolean,
 			required: false,
-			default: false
+			default: false,
 		},
 		manualSave: {
 			type: Boolean,
 			required: false,
-			default: false
+			default: false,
 		},
 	},
 
diff --git a/packages/client/src/components/global/error.vue b/packages/client/src/components/global/error.vue
index 98b96fb41..4e2ba07d3 100644
--- a/packages/client/src/components/global/error.vue
+++ b/packages/client/src/components/global/error.vue
@@ -2,14 +2,15 @@
 <transition :name="$store.state.animation ? 'zoom' : ''" appear>
 	<div class="mjndxjcg">
 		<img src="https://xn--931a.moe/assets/error.jpg" class="_ghost"/>
-		<p><i class="fas fa-exclamation-triangle"></i> {{ $ts.somethingHappened }}</p>
-		<MkButton class="button" @click="() => $emit('retry')">{{ $ts.retry }}</MkButton>
+		<p><i class="fas fa-exclamation-triangle"></i> {{ i18n.ts.somethingHappened }}</p>
+		<MkButton class="button" @click="() => $emit('retry')">{{ i18n.ts.retry }}</MkButton>
 	</div>
 </transition>
 </template>
 
 <script lang="ts" setup>
 import MkButton from '@/components/ui/button.vue';
+import { i18n } from '@/i18n';
 </script>
 
 <style lang="scss" scoped>
diff --git a/packages/client/src/components/instance-stats.vue b/packages/client/src/components/instance-stats.vue
index 1a811c2d8..65465dd9a 100644
--- a/packages/client/src/components/instance-stats.vue
+++ b/packages/client/src/components/instance-stats.vue
@@ -4,29 +4,29 @@
 		<div class="body">
 			<div class="selects" style="display: flex;">
 				<MkSelect v-model="chartSrc" style="margin: 0; flex: 1;">
-					<optgroup :label="$ts.federation">
-						<option value="federation">{{ $ts._charts.federation }}</option>
-						<option value="ap-request">{{ $ts._charts.apRequest }}</option>
+					<optgroup :label="i18n.ts.federation">
+						<option value="federation">{{ i18n.ts._charts.federation }}</option>
+						<option value="ap-request">{{ i18n.ts._charts.apRequest }}</option>
 					</optgroup>
-					<optgroup :label="$ts.users">
-						<option value="users">{{ $ts._charts.usersIncDec }}</option>
-						<option value="users-total">{{ $ts._charts.usersTotal }}</option>
-						<option value="active-users">{{ $ts._charts.activeUsers }}</option>
+					<optgroup :label="i18n.ts.users">
+						<option value="users">{{ i18n.ts._charts.usersIncDec }}</option>
+						<option value="users-total">{{ i18n.ts._charts.usersTotal }}</option>
+						<option value="active-users">{{ i18n.ts._charts.activeUsers }}</option>
 					</optgroup>
-					<optgroup :label="$ts.notes">
-						<option value="notes">{{ $ts._charts.notesIncDec }}</option>
-						<option value="local-notes">{{ $ts._charts.localNotesIncDec }}</option>
-						<option value="remote-notes">{{ $ts._charts.remoteNotesIncDec }}</option>
-						<option value="notes-total">{{ $ts._charts.notesTotal }}</option>
+					<optgroup :label="i18n.ts.notes">
+						<option value="notes">{{ i18n.ts._charts.notesIncDec }}</option>
+						<option value="local-notes">{{ i18n.ts._charts.localNotesIncDec }}</option>
+						<option value="remote-notes">{{ i18n.ts._charts.remoteNotesIncDec }}</option>
+						<option value="notes-total">{{ i18n.ts._charts.notesTotal }}</option>
 					</optgroup>
-					<optgroup :label="$ts.drive">
-						<option value="drive-files">{{ $ts._charts.filesIncDec }}</option>
-						<option value="drive">{{ $ts._charts.storageUsageIncDec }}</option>
+					<optgroup :label="i18n.ts.drive">
+						<option value="drive-files">{{ i18n.ts._charts.filesIncDec }}</option>
+						<option value="drive">{{ i18n.ts._charts.storageUsageIncDec }}</option>
 					</optgroup>
 				</MkSelect>
 				<MkSelect v-model="chartSpan" style="margin: 0 0 0 10px;">
-					<option value="hour">{{ $ts.perHour }}</option>
-					<option value="day">{{ $ts.perDay }}</option>
+					<option value="hour">{{ i18n.ts.perHour }}</option>
+					<option value="day">{{ i18n.ts.perDay }}</option>
 				</MkSelect>
 			</div>
 			<div class="chart">
@@ -71,6 +71,7 @@ import MkSelect from '@/components/form/select.vue';
 import MkChart from '@/components/chart.vue';
 import { useChartTooltip } from '@/scripts/use-chart-tooltip';
 import * as os from '@/os';
+import { i18n } from '@/i18n';
 
 Chart.register(
 	ArcElement,
diff --git a/packages/client/src/components/key-value.vue b/packages/client/src/components/key-value.vue
index 3d665e159..586f7a3f9 100644
--- a/packages/client/src/components/key-value.vue
+++ b/packages/client/src/components/key-value.vue
@@ -5,7 +5,7 @@
 	</div>
 	<div class="value">
 		<slot name="value"></slot>
-		<button v-if="copy" v-tooltip="$ts.copy" class="_textButton" style="margin-left: 0.5em;" @click="copy_"><i class="far fa-copy"></i></button>
+		<button v-if="copy" v-tooltip="i18n.ts.copy" class="_textButton" style="margin-left: 0.5em;" @click="copy_"><i class="far fa-copy"></i></button>
 	</div>
 </div>
 </template>
@@ -14,6 +14,7 @@
 import { } from 'vue';
 import copyToClipboard from '@/scripts/copy-to-clipboard';
 import * as os from '@/os';
+import { i18n } from '@/i18n';
 
 const props = withDefaults(defineProps<{
 	copy?: string | null;
diff --git a/packages/client/src/components/note-detailed.vue b/packages/client/src/components/note-detailed.vue
index 85bffca4a..1e0625b6c 100644
--- a/packages/client/src/components/note-detailed.vue
+++ b/packages/client/src/components/note-detailed.vue
@@ -14,7 +14,7 @@
 	<div v-if="isRenote" class="renote">
 		<MkAvatar class="avatar" :user="note.user"/>
 		<i class="fas fa-retweet"></i>
-		<I18n :src="$ts.renotedBy" tag="span">
+		<I18n :src="i18n.ts.renotedBy" tag="span">
 			<template #user>
 				<MkA v-user-preview="note.userId" class="name" :to="userPage(note.user)">
 					<MkUserName :user="note.user"/>
@@ -54,7 +54,7 @@
 				</p>
 				<div v-show="appearNote.cw == null || showContent" class="content">
 					<div class="text">
-						<span v-if="appearNote.isHidden" style="opacity: 0.5">({{ $ts.private }})</span>
+						<span v-if="appearNote.isHidden" style="opacity: 0.5">({{ i18n.ts.private }})</span>
 						<MkA v-if="appearNote.replyId" class="reply" :to="`/notes/${appearNote.replyId}`"><i class="fas fa-reply"></i></MkA>
 						<Mfm v-if="appearNote.text" :text="appearNote.text" :author="appearNote.user" :i="$i" :custom-emojis="appearNote.emojis"/>
 						<a v-if="appearNote.renote != null" class="rp">RN:</a>
@@ -103,7 +103,7 @@
 	<MkNoteSub v-for="note in replies" :key="note.id" :note="note" class="reply" :detail="true"/>
 </div>
 <div v-else class="_panel muted" @click="muted = false">
-	<I18n :src="$ts.userSaysSomething" tag="small">
+	<I18n :src="i18n.ts.userSaysSomething" tag="small">
 		<template #name>
 			<MkA v-user-preview="appearNote.userId" class="name" :to="userPage(appearNote.user)">
 				<MkUserName :user="appearNote.user"/>
diff --git a/packages/client/src/components/notes.vue b/packages/client/src/components/notes.vue
index 41bec5a57..e351a76eb 100644
--- a/packages/client/src/components/notes.vue
+++ b/packages/client/src/components/notes.vue
@@ -3,7 +3,7 @@
 	<template #empty>
 		<div class="_fullinfo">
 			<img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
-			<div>{{ $ts.noNotes }}</div>
+			<div>{{ i18n.ts.noNotes }}</div>
 		</div>
 	</template>
 
@@ -21,8 +21,8 @@
 import { ref } from 'vue';
 import XNote from '@/components/note.vue';
 import XList from '@/components/date-separated-list.vue';
-import MkPagination from '@/components/ui/pagination.vue';
-import { Paging } from '@/components/ui/pagination.vue';
+import MkPagination, { Paging } from '@/components/ui/pagination.vue';
+import { i18n } from '@/i18n';
 
 const props = defineProps<{
 	pagination: Paging;
diff --git a/packages/client/src/components/notification.vue b/packages/client/src/components/notification.vue
index 10cbe2090..9589970a4 100644
--- a/packages/client/src/components/notification.vue
+++ b/packages/client/src/components/notification.vue
@@ -61,10 +61,10 @@
 			<Mfm :text="getNoteSummary(notification.note)" :plain="true" :nowrap="!full" :custom-emojis="notification.note.emojis"/>
 			<i class="fas fa-quote-right"></i>
 		</MkA>
-		<span v-if="notification.type === 'follow'" class="text" style="opacity: 0.6;">{{ $ts.youGotNewFollower }}<div v-if="full"><MkFollowButton :user="notification.user" :full="true"/></div></span>
-		<span v-if="notification.type === 'followRequestAccepted'" class="text" style="opacity: 0.6;">{{ $ts.followRequestAccepted }}</span>
-		<span v-if="notification.type === 'receiveFollowRequest'" class="text" style="opacity: 0.6;">{{ $ts.receiveFollowRequest }}<div v-if="full && !followRequestDone"><button class="_textButton" @click="acceptFollowRequest()">{{ $ts.accept }}</button> | <button class="_textButton" @click="rejectFollowRequest()">{{ $ts.reject }}</button></div></span>
-		<span v-if="notification.type === 'groupInvited'" class="text" style="opacity: 0.6;">{{ $ts.groupInvited }}: <b>{{ notification.invitation.group.name }}</b><div v-if="full && !groupInviteDone"><button class="_textButton" @click="acceptGroupInvitation()">{{ $ts.accept }}</button> | <button class="_textButton" @click="rejectGroupInvitation()">{{ $ts.reject }}</button></div></span>
+		<span v-if="notification.type === 'follow'" class="text" style="opacity: 0.6;">{{ i18n.ts.youGotNewFollower }}<div v-if="full"><MkFollowButton :user="notification.user" :full="true"/></div></span>
+		<span v-if="notification.type === 'followRequestAccepted'" class="text" style="opacity: 0.6;">{{ i18n.ts.followRequestAccepted }}</span>
+		<span v-if="notification.type === 'receiveFollowRequest'" class="text" style="opacity: 0.6;">{{ i18n.ts.receiveFollowRequest }}<div v-if="full && !followRequestDone"><button class="_textButton" @click="acceptFollowRequest()">{{ i18n.ts.accept }}</button> | <button class="_textButton" @click="rejectFollowRequest()">{{ i18n.ts.reject }}</button></div></span>
+		<span v-if="notification.type === 'groupInvited'" class="text" style="opacity: 0.6;">{{ i18n.ts.groupInvited }}: <b>{{ notification.invitation.group.name }}</b><div v-if="full && !groupInviteDone"><button class="_textButton" @click="acceptGroupInvitation()">{{ i18n.ts.accept }}</button> | <button class="_textButton" @click="rejectGroupInvitation()">{{ i18n.ts.reject }}</button></div></span>
 		<span v-if="notification.type === 'app'" class="text">
 			<Mfm :text="notification.body" :nowrap="!full"/>
 		</span>
diff --git a/packages/client/src/components/notifications.vue b/packages/client/src/components/notifications.vue
index eb19ad488..baac2fdca 100644
--- a/packages/client/src/components/notifications.vue
+++ b/packages/client/src/components/notifications.vue
@@ -3,7 +3,7 @@
 	<template #empty>
 		<div class="_fullinfo">
 			<img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
-			<div>{{ $ts.noNotifications }}</div>
+			<div>{{ i18n.ts.noNotifications }}</div>
 		</div>
 	</template>
 
@@ -26,6 +26,7 @@ import XNote from '@/components/note.vue';
 import * as os from '@/os';
 import { stream } from '@/stream';
 import { $i } from '@/account';
+import { i18n } from '@/i18n';
 
 const props = defineProps<{
 	includeTypes?: typeof notificationTypes[number][];
diff --git a/packages/client/src/components/poll-editor.vue b/packages/client/src/components/poll-editor.vue
index a068aca79..6c1a4cc89 100644
--- a/packages/client/src/components/poll-editor.vue
+++ b/packages/client/src/components/poll-editor.vue
@@ -1,7 +1,7 @@
 <template>
 <div class="zmdxowus">
 	<p v-if="choices.length < 2" class="caution">
-		<i class="fas fa-exclamation-triangle"></i>{{ $ts._poll.noOnlyOneChoice }}
+		<i class="fas fa-exclamation-triangle"></i>{{ i18n.ts._poll.noOnlyOneChoice }}
 	</p>
 	<ul>
 		<li v-for="(choice, i) in choices" :key="i">
@@ -12,34 +12,34 @@
 			</button>
 		</li>
 	</ul>
-	<MkButton v-if="choices.length < 10" class="add" @click="add">{{ $ts.add }}</MkButton>
-	<MkButton v-else class="add" disabled>{{ $ts._poll.noMore }}</MkButton>
-	<MkSwitch v-model="multiple">{{ $ts._poll.canMultipleVote }}</MkSwitch>
+	<MkButton v-if="choices.length < 10" class="add" @click="add">{{ i18n.ts.add }}</MkButton>
+	<MkButton v-else class="add" disabled>{{ i18n.ts._poll.noMore }}</MkButton>
+	<MkSwitch v-model="multiple">{{ i18n.ts._poll.canMultipleVote }}</MkSwitch>
 	<section>
 		<div>
 			<MkSelect v-model="expiration" small>
-				<template #label>{{ $ts._poll.expiration }}</template>
-				<option value="infinite">{{ $ts._poll.infinite }}</option>
-				<option value="at">{{ $ts._poll.at }}</option>
-				<option value="after">{{ $ts._poll.after }}</option>
+				<template #label>{{ i18n.ts._poll.expiration }}</template>
+				<option value="infinite">{{ i18n.ts._poll.infinite }}</option>
+				<option value="at">{{ i18n.ts._poll.at }}</option>
+				<option value="after">{{ i18n.ts._poll.after }}</option>
 			</MkSelect>
 			<section v-if="expiration === 'at'">
 				<MkInput v-model="atDate" small type="date" class="input">
-					<template #label>{{ $ts._poll.deadlineDate }}</template>
+					<template #label>{{ i18n.ts._poll.deadlineDate }}</template>
 				</MkInput>
 				<MkInput v-model="atTime" small type="time" class="input">
-					<template #label>{{ $ts._poll.deadlineTime }}</template>
+					<template #label>{{ i18n.ts._poll.deadlineTime }}</template>
 				</MkInput>
 			</section>
 			<section v-else-if="expiration === 'after'">
 				<MkInput v-model="after" small type="number" class="input">
-					<template #label>{{ $ts._poll.duration }}</template>
+					<template #label>{{ i18n.ts._poll.duration }}</template>
 				</MkInput>
 				<MkSelect v-model="unit" small>
-					<option value="second">{{ $ts._time.second }}</option>
-					<option value="minute">{{ $ts._time.minute }}</option>
-					<option value="hour">{{ $ts._time.hour }}</option>
-					<option value="day">{{ $ts._time.day }}</option>
+					<option value="second">{{ i18n.ts._time.second }}</option>
+					<option value="minute">{{ i18n.ts._time.minute }}</option>
+					<option value="hour">{{ i18n.ts._time.hour }}</option>
+					<option value="day">{{ i18n.ts._time.day }}</option>
 				</MkSelect>
 			</section>
 		</div>
@@ -55,6 +55,7 @@ import MkSwitch from './form/switch.vue';
 import MkButton from './ui/button.vue';
 import { formatDateTimeString } from '@/scripts/format-time-string';
 import { addTime } from '@/scripts/time';
+import { i18n } from '@/i18n';
 
 const props = defineProps<{
 	modelValue: {
diff --git a/packages/client/src/components/poll.vue b/packages/client/src/components/poll.vue
index 35f87325d..d90af1cfe 100644
--- a/packages/client/src/components/poll.vue
+++ b/packages/client/src/components/poll.vue
@@ -13,97 +13,77 @@
 	<p v-if="!readOnly">
 		<span>{{ $t('_poll.totalVotes', { n: total }) }}</span>
 		<span> · </span>
-		<a v-if="!closed && !isVoted" @click="showResult = !showResult">{{ showResult ? $ts._poll.vote : $ts._poll.showResult }}</a>
-		<span v-if="isVoted">{{ $ts._poll.voted }}</span>
-		<span v-else-if="closed">{{ $ts._poll.closed }}</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>
+		<span v-else-if="closed">{{ i18n.ts._poll.closed }}</span>
 		<span v-if="remaining > 0"> · {{ timer }}</span>
 	</p>
 </div>
 </template>
 
-<script lang="ts">
-import { computed, defineComponent, onUnmounted, ref, toRef } from 'vue';
+<script lang="ts" setup>
+import { computed, onUnmounted, ref, toRef } from 'vue';
+import * as misskey from 'misskey-js';
 import { sum } from '@/scripts/array';
 import { pleaseLogin } from '@/scripts/please-login';
 import * as os from '@/os';
 import { i18n } from '@/i18n';
 import { useInterval } from '@/scripts/use-interval';
 
-export default defineComponent({
-	props: {
-		note: {
-			type: Object,
-			required: true,
-		},
-		readOnly: {
-			type: Boolean,
-			required: false,
-			default: false,
-		},
-	},
+const props = defineProps<{
+	note: misskey.entities.Note;
+	readOnly?: boolean;
+}>();
 
-	setup(props) {
-		const remaining = ref(-1);
+const remaining = ref(-1);
 
-		const total = computed(() => sum(props.note.poll.choices.map(x => x.votes)));
-		const closed = computed(() => remaining.value === 0);
-		const isVoted = computed(() => !props.note.poll.multiple && props.note.poll.choices.some(c => c.isVoted));
-		const timer = computed(() => i18n.t(
-			remaining.value >= 86400 ? '_poll.remainingDays' :
-			remaining.value >= 3600 ? '_poll.remainingHours' :
-			remaining.value >= 60 ? '_poll.remainingMinutes' : '_poll.remainingSeconds', {
-				s: Math.floor(remaining.value % 60),
-				m: Math.floor(remaining.value / 60) % 60,
-				h: Math.floor(remaining.value / 3600) % 24,
-				d: Math.floor(remaining.value / 86400),
-			}));
+const total = computed(() => sum(props.note.poll.choices.map(x => x.votes)));
+const closed = computed(() => remaining.value === 0);
+const isVoted = computed(() => !props.note.poll.multiple && props.note.poll.choices.some(c => c.isVoted));
+const timer = computed(() => i18n.t(
+	remaining.value >= 86400 ? '_poll.remainingDays' :
+	remaining.value >= 3600 ? '_poll.remainingHours' :
+	remaining.value >= 60 ? '_poll.remainingMinutes' : '_poll.remainingSeconds', {
+		s: Math.floor(remaining.value % 60),
+		m: Math.floor(remaining.value / 60) % 60,
+		h: Math.floor(remaining.value / 3600) % 24,
+		d: Math.floor(remaining.value / 86400),
+	}));
 
-		const showResult = ref(props.readOnly || isVoted.value);
+const showResult = ref(props.readOnly || isVoted.value);
 
-		// 期限付きアンケート
-		if (props.note.poll.expiresAt) {
-			const tick = () => {
-				remaining.value = Math.floor(Math.max(new Date(props.note.poll.expiresAt).getTime() - Date.now(), 0) / 1000);
-				if (remaining.value === 0) {
-					showResult.value = true;
-				}
-			};
-
-			useInterval(tick, 3000, {
-				immediate: true,
-				afterMounted: false,
-			});
+// 期限付きアンケート
+if (props.note.poll.expiresAt) {
+	const tick = () => {
+		remaining.value = Math.floor(Math.max(new Date(props.note.poll.expiresAt).getTime() - Date.now(), 0) / 1000);
+		if (remaining.value === 0) {
+			showResult.value = true;
 		}
+	};
 
-		const vote = async (id) => {
-			pleaseLogin();
+	useInterval(tick, 3000, {
+		immediate: true,
+		afterMounted: false,
+	});
+}
 
-			if (props.readOnly || closed.value || isVoted.value) return;
+const vote = async (id) => {
+	pleaseLogin();
 
-			const { canceled } = await os.confirm({
-				type: 'question',
-				text: i18n.t('voteConfirm', { choice: props.note.poll.choices[id].text }),
-			});
-			if (canceled) return;
+	if (props.readOnly || closed.value || isVoted.value) return;
 
-			await os.api('notes/polls/vote', {
-				noteId: props.note.id,
-				choice: id,
-			});
-			if (!showResult.value) showResult.value = !props.note.poll.multiple;
-		};
+	const { canceled } = await os.confirm({
+		type: 'question',
+		text: i18n.t('voteConfirm', { choice: props.note.poll.choices[id].text }),
+	});
+	if (canceled) return;
 
-		return {
-			remaining,
-			showResult,
-			total,
-			isVoted,
-			closed,
-			timer,
-			vote,
-		};
-	},
-});
+	await os.api('notes/polls/vote', {
+		noteId: props.note.id,
+		choice: id,
+	});
+	if (!showResult.value) showResult.value = !props.note.poll.multiple;
+};
 </script>
 
 <style lang="scss" scoped>
diff --git a/packages/client/src/components/remote-caution.vue b/packages/client/src/components/remote-caution.vue
index 130a0249b..e9461197c 100644
--- a/packages/client/src/components/remote-caution.vue
+++ b/packages/client/src/components/remote-caution.vue
@@ -1,8 +1,10 @@
 <template>
-<div class="jmgmzlwq _block"><i class="fas fa-exclamation-triangle" style="margin-right: 8px;"></i>{{ $ts.remoteUserCaution }}<a class="link" :href="href" rel="nofollow noopener" target="_blank">{{ $ts.showOnRemote }}</a></div>
+<div class="jmgmzlwq _block"><i class="fas fa-exclamation-triangle" style="margin-right: 8px;"></i>{{ i18n.ts.remoteUserCaution }}<a class="link" :href="href" rel="nofollow noopener" target="_blank">{{ i18n.ts.showOnRemote }}</a></div>
 </template>
 
 <script lang="ts" setup>
+import { i18n } from '@/i18n';
+
 defineProps<{
 	href: string;
 }>();
diff --git a/packages/client/src/components/signin-dialog.vue b/packages/client/src/components/signin-dialog.vue
index 848b11fad..ec68668a7 100644
--- a/packages/client/src/components/signin-dialog.vue
+++ b/packages/client/src/components/signin-dialog.vue
@@ -1,11 +1,12 @@
 <template>
-<XModalWindow ref="dialog"
+<XModalWindow
+	ref="dialog"
 	:width="370"
 	:height="400"
 	@close="onClose"
 	@closed="emit('closed')"
 >
-	<template #header>{{ $ts.login }}</template>
+	<template #header>{{ i18n.ts.login }}</template>
 
 	<MkSignin :auto-set="autoSet" :message="message" @login="onLogin"/>
 </XModalWindow>
@@ -13,15 +14,16 @@
 
 <script lang="ts" setup>
 import { } from 'vue';
-import XModalWindow from '@/components/ui/modal-window.vue';
 import MkSignin from './signin.vue';
+import XModalWindow from '@/components/ui/modal-window.vue';
+import { i18n } from '@/i18n';
 
 const props = withDefaults(defineProps<{
 	autoSet?: boolean;
 	message?: string,
 }>(), {
 	autoSet: false,
-	message: ''
+	message: '',
 });
 
 const emit = defineEmits<{
diff --git a/packages/client/src/components/signup-dialog.vue b/packages/client/src/components/signup-dialog.vue
index 6dad9257a..c5f933f6b 100644
--- a/packages/client/src/components/signup-dialog.vue
+++ b/packages/client/src/components/signup-dialog.vue
@@ -1,11 +1,12 @@
 <template>
-<XModalWindow ref="dialog"
+<XModalWindow
+	ref="dialog"
 	:width="366"
 	:height="500"
 	@close="dialog.close()"
 	@closed="$emit('closed')"
 >
-	<template #header>{{ $ts.signup }}</template>
+	<template #header>{{ i18n.ts.signup }}</template>
 
 	<div class="_monolithic_">
 		<div class="_section">
@@ -17,8 +18,9 @@
 
 <script lang="ts" setup>
 import { } from 'vue';
-import XModalWindow from '@/components/ui/modal-window.vue';
 import XSignup from './signup.vue';
+import XModalWindow from '@/components/ui/modal-window.vue';
+import { i18n } from '@/i18n';
 
 const props = withDefaults(defineProps<{
 	autoSet?: boolean;
diff --git a/packages/client/src/components/signup.vue b/packages/client/src/components/signup.vue
index c35d65d5d..f8e39985b 100644
--- a/packages/client/src/components/signup.vue
+++ b/packages/client/src/components/signup.vue
@@ -1,65 +1,65 @@
 <template>
 <form class="qlvuhzng _formRoot" autocomplete="new-password" @submit.prevent="onSubmit">
 	<MkInput v-if="instance.disableRegistration" v-model="invitationCode" class="_formBlock" type="text" :spellcheck="false" required>
-		<template #label>{{ $ts.invitationCode }}</template>
+		<template #label>{{ i18n.ts.invitationCode }}</template>
 		<template #prefix><i class="fas fa-key"></i></template>
 	</MkInput>
 	<MkInput v-model="username" class="_formBlock" type="text" pattern="^[a-zA-Z0-9_]{1,20}$" :spellcheck="false" required data-cy-signup-username @update:modelValue="onChangeUsername">
-		<template #label>{{ $ts.username }} <div v-tooltip:dialog="$ts.usernameInfo" class="_button _help"><i class="far fa-question-circle"></i></div></template>
+		<template #label>{{ i18n.ts.username }} <div v-tooltip:dialog="i18n.ts.usernameInfo" class="_button _help"><i class="far fa-question-circle"></i></div></template>
 		<template #prefix>@</template>
 		<template #suffix>@{{ host }}</template>
 		<template #caption>
-			<span v-if="usernameState === 'wait'" style="color:#999"><i class="fas fa-spinner fa-pulse fa-fw"></i> {{ $ts.checking }}</span>
-			<span v-else-if="usernameState === 'ok'" style="color: var(--success)"><i class="fas fa-check fa-fw"></i> {{ $ts.available }}</span>
-			<span v-else-if="usernameState === 'unavailable'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.unavailable }}</span>
-			<span v-else-if="usernameState === 'error'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.error }}</span>
-			<span v-else-if="usernameState === 'invalid-format'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.usernameInvalidFormat }}</span>
-			<span v-else-if="usernameState === 'min-range'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.tooShort }}</span>
-			<span v-else-if="usernameState === 'max-range'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.tooLong }}</span>
+			<span v-if="usernameState === 'wait'" style="color:#999"><i class="fas fa-spinner fa-pulse fa-fw"></i> {{ i18n.ts.checking }}</span>
+			<span v-else-if="usernameState === 'ok'" style="color: var(--success)"><i class="fas fa-check fa-fw"></i> {{ i18n.ts.available }}</span>
+			<span v-else-if="usernameState === 'unavailable'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ i18n.ts.unavailable }}</span>
+			<span v-else-if="usernameState === 'error'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ i18n.ts.error }}</span>
+			<span v-else-if="usernameState === 'invalid-format'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ i18n.ts.usernameInvalidFormat }}</span>
+			<span v-else-if="usernameState === 'min-range'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ i18n.ts.tooShort }}</span>
+			<span v-else-if="usernameState === 'max-range'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ i18n.ts.tooLong }}</span>
 		</template>
 	</MkInput>
 	<MkInput v-if="instance.emailRequiredForSignup" v-model="email" class="_formBlock" :debounce="true" type="email" :spellcheck="false" required data-cy-signup-email @update:modelValue="onChangeEmail">
-		<template #label>{{ $ts.emailAddress }} <div v-tooltip:dialog="$ts._signup.emailAddressInfo" class="_button _help"><i class="far fa-question-circle"></i></div></template>
+		<template #label>{{ i18n.ts.emailAddress }} <div v-tooltip:dialog="i18n.ts._signup.emailAddressInfo" class="_button _help"><i class="far fa-question-circle"></i></div></template>
 		<template #prefix><i class="fas fa-envelope"></i></template>
 		<template #caption>
-			<span v-if="emailState === 'wait'" style="color:#999"><i class="fas fa-spinner fa-pulse fa-fw"></i> {{ $ts.checking }}</span>
-			<span v-else-if="emailState === 'ok'" style="color: var(--success)"><i class="fas fa-check fa-fw"></i> {{ $ts.available }}</span>
-			<span v-else-if="emailState === 'unavailable:used'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts._emailUnavailable.used }}</span>
-			<span v-else-if="emailState === 'unavailable:format'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts._emailUnavailable.format }}</span>
-			<span v-else-if="emailState === 'unavailable:disposable'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts._emailUnavailable.disposable }}</span>
-			<span v-else-if="emailState === 'unavailable:mx'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts._emailUnavailable.mx }}</span>
-			<span v-else-if="emailState === 'unavailable:smtp'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts._emailUnavailable.smtp }}</span>
-			<span v-else-if="emailState === 'unavailable'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.unavailable }}</span>
-			<span v-else-if="emailState === 'error'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.error }}</span>
+			<span v-if="emailState === 'wait'" style="color:#999"><i class="fas fa-spinner fa-pulse fa-fw"></i> {{ i18n.ts.checking }}</span>
+			<span v-else-if="emailState === 'ok'" style="color: var(--success)"><i class="fas fa-check fa-fw"></i> {{ i18n.ts.available }}</span>
+			<span v-else-if="emailState === 'unavailable:used'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ i18n.ts._emailUnavailable.used }}</span>
+			<span v-else-if="emailState === 'unavailable:format'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ i18n.ts._emailUnavailable.format }}</span>
+			<span v-else-if="emailState === 'unavailable:disposable'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ i18n.ts._emailUnavailable.disposable }}</span>
+			<span v-else-if="emailState === 'unavailable:mx'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ i18n.ts._emailUnavailable.mx }}</span>
+			<span v-else-if="emailState === 'unavailable:smtp'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ i18n.ts._emailUnavailable.smtp }}</span>
+			<span v-else-if="emailState === 'unavailable'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ i18n.ts.unavailable }}</span>
+			<span v-else-if="emailState === 'error'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ i18n.ts.error }}</span>
 		</template>
 	</MkInput>
 	<MkInput v-model="password" class="_formBlock" type="password" autocomplete="new-password" required data-cy-signup-password @update:modelValue="onChangePassword">
-		<template #label>{{ $ts.password }}</template>
+		<template #label>{{ i18n.ts.password }}</template>
 		<template #prefix><i class="fas fa-lock"></i></template>
 		<template #caption>
-			<span v-if="passwordStrength == 'low'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.weakPassword }}</span>
-			<span v-if="passwordStrength == 'medium'" style="color: var(--warn)"><i class="fas fa-check fa-fw"></i> {{ $ts.normalPassword }}</span>
-			<span v-if="passwordStrength == 'high'" style="color: var(--success)"><i class="fas fa-check fa-fw"></i> {{ $ts.strongPassword }}</span>
+			<span v-if="passwordStrength == 'low'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ i18n.ts.weakPassword }}</span>
+			<span v-if="passwordStrength == 'medium'" style="color: var(--warn)"><i class="fas fa-check fa-fw"></i> {{ i18n.ts.normalPassword }}</span>
+			<span v-if="passwordStrength == 'high'" style="color: var(--success)"><i class="fas fa-check fa-fw"></i> {{ i18n.ts.strongPassword }}</span>
 		</template>
 	</MkInput>
 	<MkInput v-model="retypedPassword" class="_formBlock" type="password" autocomplete="new-password" required data-cy-signup-password-retype @update:modelValue="onChangePasswordRetype">
-		<template #label>{{ $ts.password }} ({{ $ts.retype }})</template>
+		<template #label>{{ i18n.ts.password }} ({{ i18n.ts.retype }})</template>
 		<template #prefix><i class="fas fa-lock"></i></template>
 		<template #caption>
-			<span v-if="passwordRetypeState == 'match'" style="color: var(--success)"><i class="fas fa-check fa-fw"></i> {{ $ts.passwordMatched }}</span>
-			<span v-if="passwordRetypeState == 'not-match'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.passwordNotMatched }}</span>
+			<span v-if="passwordRetypeState == 'match'" style="color: var(--success)"><i class="fas fa-check fa-fw"></i> {{ i18n.ts.passwordMatched }}</span>
+			<span v-if="passwordRetypeState == 'not-match'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ i18n.ts.passwordNotMatched }}</span>
 		</template>
 	</MkInput>
 	<MkSwitch v-if="instance.tosUrl" v-model="ToSAgreement" class="_formBlock tou">
-		<I18n :src="$ts.agreeTo">
+		<I18n :src="i18n.ts.agreeTo">
 			<template #0>
-				<a :href="instance.tosUrl" class="_link" target="_blank">{{ $ts.tos }}</a>
+				<a :href="instance.tosUrl" class="_link" target="_blank">{{ i18n.ts.tos }}</a>
 			</template>
 		</I18n>
 	</MkSwitch>
 	<MkCaptcha v-if="instance.enableHcaptcha" ref="hcaptcha" v-model="hCaptchaResponse" class="_formBlock captcha" provider="hcaptcha" :sitekey="instance.hcaptchaSiteKey"/>
 	<MkCaptcha v-if="instance.enableRecaptcha" ref="recaptcha" v-model="reCaptchaResponse" class="_formBlock captcha" provider="recaptcha" :sitekey="instance.recaptchaSiteKey"/>
-	<MkButton class="_formBlock" type="submit" :disabled="shouldDisableSubmitting" gradate data-cy-signup-submit>{{ $ts.start }}</MkButton>
+	<MkButton class="_formBlock" type="submit" :disabled="shouldDisableSubmitting" gradate data-cy-signup-submit>{{ i18n.ts.start }}</MkButton>
 </form>
 </template>
 
diff --git a/packages/client/src/components/sub-note-content.vue b/packages/client/src/components/sub-note-content.vue
index d6a37d07b..25ab883f4 100644
--- a/packages/client/src/components/sub-note-content.vue
+++ b/packages/client/src/components/sub-note-content.vue
@@ -1,8 +1,8 @@
 <template>
 <div class="wrmlmaau" :class="{ collapsed }">
 	<div class="body">
-		<span v-if="note.isHidden" style="opacity: 0.5">({{ $ts.private }})</span>
-		<span v-if="note.deletedAt" style="opacity: 0.5">({{ $ts.deleted }})</span>
+		<span v-if="note.isHidden" style="opacity: 0.5">({{ i18n.ts.private }})</span>
+		<span v-if="note.deletedAt" style="opacity: 0.5">({{ i18n.ts.deleted }})</span>
 		<MkA v-if="note.replyId" class="reply" :to="`/notes/${note.replyId}`"><i class="fas fa-reply"></i></MkA>
 		<Mfm v-if="note.text" :text="note.text" :author="note.user" :i="$i" :custom-emojis="note.emojis"/>
 		<MkA v-if="note.renoteId" class="rp" :to="`/notes/${note.renoteId}`">RN: ...</MkA>
@@ -12,20 +12,21 @@
 		<XMediaList :media-list="note.files"/>
 	</details>
 	<details v-if="note.poll">
-		<summary>{{ $ts.poll }}</summary>
+		<summary>{{ i18n.ts.poll }}</summary>
 		<XPoll :note="note"/>
 	</details>
 	<button v-if="collapsed" class="fade _button" @click="collapsed = false">
-		<span>{{ $ts.showMore }}</span>
+		<span>{{ i18n.ts.showMore }}</span>
 	</button>
 </div>
 </template>
 
 <script lang="ts" setup>
 import { } from 'vue';
+import * as misskey from 'misskey-js';
 import XPoll from './poll.vue';
 import XMediaList from './media-list.vue';
-import * as misskey from 'misskey-js';
+import { i18n } from '@/i18n';
 
 const props = defineProps<{
 	note: misskey.entities.Note;
diff --git a/packages/client/src/components/ui/menu.vue b/packages/client/src/components/ui/menu.vue
index 6d1a2cc77..d1bb3212f 100644
--- a/packages/client/src/components/ui/menu.vue
+++ b/packages/client/src/components/ui/menu.vue
@@ -46,7 +46,7 @@
 			</button>
 		</template>
 		<span v-if="items2.length === 0" class="none item">
-			<span>{{ $ts.none }}</span>
+			<span>{{ i18n.ts.none }}</span>
 		</span>
 	</div>
 	<div v-if="childMenu" class="child">
@@ -61,6 +61,8 @@ import { focusPrev, focusNext } from '@/scripts/focus';
 import FormSwitch from '@/components/form/switch.vue';
 import { MenuItem, InnerMenuItem, MenuPending, MenuAction } from '@/types/menu';
 import * as os from '@/os';
+import { i18n } from '@/i18n';
+
 const XChild = defineAsyncComponent(() => import('./menu.child.vue'));
 
 const props = defineProps<{
diff --git a/packages/client/src/components/ui/pagination.vue b/packages/client/src/components/ui/pagination.vue
index a03c2b3a1..063dc2d47 100644
--- a/packages/client/src/components/ui/pagination.vue
+++ b/packages/client/src/components/ui/pagination.vue
@@ -8,7 +8,7 @@
 		<slot name="empty">
 			<div class="_fullinfo">
 				<img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
-				<div>{{ $ts.nothing }}</div>
+				<div>{{ i18n.ts.nothing }}</div>
 			</div>
 		</slot>
 	</div>
@@ -16,14 +16,14 @@
 	<div v-else ref="rootEl">
 		<div v-show="pagination.reversed && more" key="_more_" class="cxiknjgy _gap">
 			<MkButton v-if="!moreFetching" class="button" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }" primary @click="fetchMoreAhead">
-				{{ $ts.loadMore }}
+				{{ i18n.ts.loadMore }}
 			</MkButton>
 			<MkLoading v-else class="loading"/>
 		</div>
 		<slot :items="items"></slot>
 		<div v-show="!pagination.reversed && more" key="_more_" class="cxiknjgy _gap">
 			<MkButton v-if="!moreFetching" v-appear="($store.state.enableInfiniteScroll && !disableAutoLoad) ? fetchMore : null" class="button" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }" primary @click="fetchMore">
-				{{ $ts.loadMore }}
+				{{ i18n.ts.loadMore }}
 			</MkButton>
 			<MkLoading v-else class="loading"/>
 		</div>
@@ -37,6 +37,7 @@ import * as misskey from 'misskey-js';
 import * as os from '@/os';
 import { onScrollTop, isTopVisible, getScrollPosition, getScrollContainer } from '@/scripts/scroll';
 import MkButton from '@/components/ui/button.vue';
+import { i18n } from '@/i18n';
 
 const SECOND_FETCH_LIMIT = 30;
 
diff --git a/packages/client/src/components/updated.vue b/packages/client/src/components/updated.vue
index 375ac0dbb..1c1e5f4ae 100644
--- a/packages/client/src/components/updated.vue
+++ b/packages/client/src/components/updated.vue
@@ -1,10 +1,10 @@
 <template>
 <MkModal ref="modal" :z-priority="'middle'" @click="$refs.modal.close()" @closed="$emit('closed')">
 	<div class="ewlycnyt">
-		<div class="title"><MkSparkle>{{ $ts.misskeyUpdated }}</MkSparkle></div>
+		<div class="title"><MkSparkle>{{ i18n.ts.misskeyUpdated }}</MkSparkle></div>
 		<div class="version">✨{{ version }}🚀</div>
-		<MkButton full @click="whatIsNew">{{ $ts.whatIsNew }}</MkButton>
-		<MkButton class="gotIt" primary full @click="$refs.modal.close()">{{ $ts.gotIt }}</MkButton>
+		<MkButton full @click="whatIsNew">{{ i18n.ts.whatIsNew }}</MkButton>
+		<MkButton class="gotIt" primary full @click="$refs.modal.close()">{{ i18n.ts.gotIt }}</MkButton>
 	</div>
 </MkModal>
 </template>
@@ -15,8 +15,9 @@ import MkModal from '@/components/ui/modal.vue';
 import MkButton from '@/components/ui/button.vue';
 import MkSparkle from '@/components/sparkle.vue';
 import { version } from '@/config';
+import { i18n } from '@/i18n';
 
-const modal = ref();
+const modal = ref<InstanceType<typeof MkModal>>();
 
 const whatIsNew = () => {
 	modal.value.close();
diff --git a/packages/client/src/components/url-preview.vue b/packages/client/src/components/url-preview.vue
index e15d28a38..df4b0e53b 100644
--- a/packages/client/src/components/url-preview.vue
+++ b/packages/client/src/components/url-preview.vue
@@ -1,6 +1,6 @@
 <template>
 <div v-if="playerEnabled" class="player" :style="`padding: ${(player.height || 0) / (player.width || 1) * 100}% 0 0`">
-	<button class="disablePlayer" :title="$ts.disablePlayer" @click="playerEnabled = false"><i class="fas fa-times"></i></button>
+	<button class="disablePlayer" :title="i18n.ts.disablePlayer" @click="playerEnabled = false"><i class="fas fa-times"></i></button>
 	<iframe :src="player.url + (player.url.match(/\?/) ? '&autoplay=1&auto_play=1' : '?autoplay=1&auto_play=1')" :width="player.width || '100%'" :heigth="player.height || 250" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen/>
 </div>
 <div v-else-if="tweetId && tweetExpanded" ref="twitter" class="twitter">
@@ -10,7 +10,7 @@
 	<transition :name="$store.state.animation ? 'zoom' : ''" mode="out-in">
 		<component :is="self ? 'MkA' : 'a'" v-if="!fetching" class="link" :class="{ compact }" :[attr]="self ? url.substr(local.length) : url" rel="nofollow noopener" :target="target" :title="url">
 			<div v-if="thumbnail" class="thumbnail" :style="`background-image: url('${thumbnail}')`">
-				<button v-if="!playerEnabled && player.url" class="_button" :title="$ts.enablePlayer" @click.prevent="playerEnabled = true"><i class="fas fa-play-circle"></i></button>
+				<button v-if="!playerEnabled && player.url" class="_button" :title="i18n.ts.enablePlayer" @click.prevent="playerEnabled = true"><i class="fas fa-play-circle"></i></button>
 			</div>
 			<article>
 				<header>
@@ -26,7 +26,7 @@
 	</transition>
 	<div v-if="tweetId" class="expandTweet">
 		<a @click="tweetExpanded = true">
-			<i class="fab fa-twitter"></i> {{ $ts.expandTweet }}
+			<i class="fab fa-twitter"></i> {{ i18n.ts.expandTweet }}
 		</a>
 	</div>
 </div>
@@ -35,6 +35,7 @@
 <script lang="ts" setup>
 import { onMounted, onUnmounted } from 'vue';
 import { url as local, lang } from '@/config';
+import { i18n } from '@/i18n';
 
 const props = withDefaults(defineProps<{
 	url: string;
diff --git a/packages/client/src/components/user-info.vue b/packages/client/src/components/user-info.vue
index 6a25d412f..1cd275a6d 100644
--- a/packages/client/src/components/user-info.vue
+++ b/packages/client/src/components/user-info.vue
@@ -10,17 +10,17 @@
 		<div v-if="user.description" class="mfm">
 			<Mfm :text="user.description" :author="user" :i="$i" :custom-emojis="user.emojis"/>
 		</div>
-		<span v-else style="opacity: 0.7;">{{ $ts.noAccountDescription }}</span>
+		<span v-else style="opacity: 0.7;">{{ i18n.ts.noAccountDescription }}</span>
 	</div>
 	<div class="status">
 		<div>
-			<p>{{ $ts.notes }}</p><span>{{ user.notesCount }}</span>
+			<p>{{ i18n.ts.notes }}</p><span>{{ user.notesCount }}</span>
 		</div>
 		<div>
-			<p>{{ $ts.following }}</p><span>{{ user.followingCount }}</span>
+			<p>{{ i18n.ts.following }}</p><span>{{ user.followingCount }}</span>
 		</div>
 		<div>
-			<p>{{ $ts.followers }}</p><span>{{ user.followersCount }}</span>
+			<p>{{ i18n.ts.followers }}</p><span>{{ user.followersCount }}</span>
 		</div>
 	</div>
 	<MkFollowButton v-if="$i && user.id != $i.id" class="koudoku-button" :user="user" mini/>
@@ -31,6 +31,7 @@
 import * as misskey from 'misskey-js';
 import MkFollowButton from './follow-button.vue';
 import { userPage } from '@/filters/user';
+import { i18n } from '@/i18n';
 
 defineProps<{
 	user: misskey.entities.UserDetailed;
diff --git a/packages/client/src/components/user-list.vue b/packages/client/src/components/user-list.vue
index 3e273721c..fe30d371f 100644
--- a/packages/client/src/components/user-list.vue
+++ b/packages/client/src/components/user-list.vue
@@ -3,7 +3,7 @@
 	<template #empty>
 		<div class="_fullinfo">
 			<img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
-			<div>{{ $ts.noUsers }}</div>
+			<div>{{ i18n.ts.noUsers }}</div>
 		</div>
 	</template>
 
@@ -18,9 +18,9 @@
 <script lang="ts" setup>
 import { ref } from 'vue';
 import MkUserInfo from '@/components/user-info.vue';
-import MkPagination from '@/components/ui/pagination.vue';
-import { Paging } from '@/components/ui/pagination.vue';
+import MkPagination, { Paging } from '@/components/ui/pagination.vue';
 import { userPage } from '@/filters/user';
+import { i18n } from '@/i18n';
 
 const props = defineProps<{
 	pagination: Paging;
diff --git a/packages/client/src/components/user-select-dialog.vue b/packages/client/src/components/user-select-dialog.vue
index 972d35348..4d8e427a7 100644
--- a/packages/client/src/components/user-select-dialog.vue
+++ b/packages/client/src/components/user-select-dialog.vue
@@ -1,5 +1,6 @@
 <template>
-<XModalWindow ref="dialogEl"
+<XModalWindow
+	ref="dialogEl"
 	:with-ok-button="true"
 	:ok-button-disabled="selected == null"
 	@click="cancel()"
@@ -7,16 +8,16 @@
 	@ok="ok()"
 	@closed="$emit('closed')"
 >
-	<template #header>{{ $ts.selectUser }}</template>
+	<template #header>{{ i18n.ts.selectUser }}</template>
 	<div class="tbhwbxda">
 		<div class="form">
 			<FormSplit :min-width="170">
 				<MkInput v-model="username" :autofocus="true" @update:modelValue="search">
-					<template #label>{{ $ts.username }}</template>
+					<template #label>{{ i18n.ts.username }}</template>
 					<template #prefix>@</template>
 				</MkInput>
 				<MkInput v-model="host" @update:modelValue="search">
-					<template #label>{{ $ts.host }}</template>
+					<template #label>{{ i18n.ts.host }}</template>
 					<template #prefix>@</template>
 				</MkInput>
 			</FormSplit>
@@ -32,7 +33,7 @@
 				</div>
 			</div>
 			<div v-else class="empty">
-				<span>{{ $ts.noUsers }}</span>
+				<span>{{ i18n.ts.noUsers }}</span>
 			</div>
 		</div>
 		<div v-if="username == '' && host == ''" class="recent">
@@ -58,6 +59,7 @@ import FormSplit from '@/components/form/split.vue';
 import XModalWindow from '@/components/ui/modal-window.vue';
 import * as os from '@/os';
 import { defaultStore } from '@/store';
+import { i18n } from '@/i18n';
 
 const emit = defineEmits<{
 	(ev: 'ok', selected: misskey.entities.UserDetailed): void;
@@ -81,7 +83,7 @@ const search = () => {
 		username: username,
 		host: host,
 		limit: 10,
-		detail: false
+		detail: false,
 	}).then(_users => {
 		users = _users;
 	});
diff --git a/packages/client/src/components/visibility-picker.vue b/packages/client/src/components/visibility-picker.vue
index 7fe55858c..f4830cd2c 100644
--- a/packages/client/src/components/visibility-picker.vue
+++ b/packages/client/src/components/visibility-picker.vue
@@ -4,37 +4,37 @@
 		<button key="public" class="_button" :class="{ active: v === 'public' }" data-index="1" @click="choose('public')">
 			<div><i class="fas fa-globe"></i></div>
 			<div>
-				<span>{{ $ts._visibility.public }}</span>
-				<span>{{ $ts._visibility.publicDescription }}</span>
+				<span>{{ i18n.ts._visibility.public }}</span>
+				<span>{{ i18n.ts._visibility.publicDescription }}</span>
 			</div>
 		</button>
 		<button key="home" class="_button" :class="{ active: v === 'home' }" data-index="2" @click="choose('home')">
 			<div><i class="fas fa-home"></i></div>
 			<div>
-				<span>{{ $ts._visibility.home }}</span>
-				<span>{{ $ts._visibility.homeDescription }}</span>
+				<span>{{ i18n.ts._visibility.home }}</span>
+				<span>{{ i18n.ts._visibility.homeDescription }}</span>
 			</div>
 		</button>
 		<button key="followers" class="_button" :class="{ active: v === 'followers' }" data-index="3" @click="choose('followers')">
 			<div><i class="fas fa-unlock"></i></div>
 			<div>
-				<span>{{ $ts._visibility.followers }}</span>
-				<span>{{ $ts._visibility.followersDescription }}</span>
+				<span>{{ i18n.ts._visibility.followers }}</span>
+				<span>{{ i18n.ts._visibility.followersDescription }}</span>
 			</div>
 		</button>
 		<button key="specified" :disabled="localOnly" class="_button" :class="{ active: v === 'specified' }" data-index="4" @click="choose('specified')">
 			<div><i class="fas fa-envelope"></i></div>
 			<div>
-				<span>{{ $ts._visibility.specified }}</span>
-				<span>{{ $ts._visibility.specifiedDescription }}</span>
+				<span>{{ i18n.ts._visibility.specified }}</span>
+				<span>{{ i18n.ts._visibility.specifiedDescription }}</span>
 			</div>
 		</button>
 		<div class="divider"></div>
 		<button key="localOnly" class="_button localOnly" :class="{ active: localOnly }" data-index="5" @click="localOnly = !localOnly">
 			<div><i class="fas fa-biohazard"></i></div>
 			<div>
-				<span>{{ $ts._visibility.localOnly }}</span>
-				<span>{{ $ts._visibility.localOnlyDescription }}</span>
+				<span>{{ i18n.ts._visibility.localOnly }}</span>
+				<span>{{ i18n.ts._visibility.localOnlyDescription }}</span>
 			</div>
 			<div><i :class="localOnly ? 'fas fa-toggle-on' : 'fas fa-toggle-off'"></i></div>
 		</button>
@@ -46,6 +46,7 @@
 import { nextTick, watch } from 'vue';
 import * as misskey from 'misskey-js';
 import MkModal from '@/components/ui/modal.vue';
+import { i18n } from '@/i18n';
 
 const modal = $ref<InstanceType<typeof MkModal>>();
 
diff --git a/packages/client/src/components/widgets.vue b/packages/client/src/components/widgets.vue
index 0a9769e19..54d4c57af 100644
--- a/packages/client/src/components/widgets.vue
+++ b/packages/client/src/components/widgets.vue
@@ -3,11 +3,11 @@
 	<template v-if="edit">
 		<header>
 			<MkSelect v-model="widgetAdderSelected" style="margin-bottom: var(--margin)" class="mk-widget-select">
-				<template #label>{{ $ts.selectWidget }}</template>
+				<template #label>{{ i18n.ts.selectWidget }}</template>
 				<option v-for="widget in widgetDefs" :key="widget" :value="widget">{{ i18n.t(`_widgets.${widget}`) }}</option>
 			</MkSelect>
-			<MkButton inline primary class="mk-widget-add" @click="addWidget"><i class="fas fa-plus"></i> {{ $ts.add }}</MkButton>
-			<MkButton inline @click="$emit('exit')">{{ $ts.close }}</MkButton>
+			<MkButton inline primary class="mk-widget-add" @click="addWidget"><i class="fas fa-plus"></i> {{ i18n.ts.add }}</MkButton>
+			<MkButton inline @click="$emit('exit')">{{ i18n.ts.close }}</MkButton>
 		</header>
 		<XDraggable
 			v-model="widgets_"
diff --git a/packages/client/src/pages/about.federation.vue b/packages/client/src/pages/about.federation.vue
index 00ca44eec..8d9390872 100644
--- a/packages/client/src/pages/about.federation.vue
+++ b/packages/client/src/pages/about.federation.vue
@@ -3,35 +3,35 @@
 	<div class="query">
 		<MkInput v-model="host" :debounce="true" class="">
 			<template #prefix><i class="fas fa-search"></i></template>
-			<template #label>{{ $ts.host }}</template>
+			<template #label>{{ i18n.ts.host }}</template>
 		</MkInput>
 		<FormSplit style="margin-top: var(--margin);">
 			<MkSelect v-model="state">
-				<template #label>{{ $ts.state }}</template>
-				<option value="all">{{ $ts.all }}</option>
-				<option value="federating">{{ $ts.federating }}</option>
-				<option value="subscribing">{{ $ts.subscribing }}</option>
-				<option value="publishing">{{ $ts.publishing }}</option>
-				<option value="suspended">{{ $ts.suspended }}</option>
-				<option value="blocked">{{ $ts.blocked }}</option>
-				<option value="notResponding">{{ $ts.notResponding }}</option>
+				<template #label>{{ i18n.ts.state }}</template>
+				<option value="all">{{ i18n.ts.all }}</option>
+				<option value="federating">{{ i18n.ts.federating }}</option>
+				<option value="subscribing">{{ i18n.ts.subscribing }}</option>
+				<option value="publishing">{{ i18n.ts.publishing }}</option>
+				<option value="suspended">{{ i18n.ts.suspended }}</option>
+				<option value="blocked">{{ i18n.ts.blocked }}</option>
+				<option value="notResponding">{{ i18n.ts.notResponding }}</option>
 			</MkSelect>
 			<MkSelect v-model="sort">
-				<template #label>{{ $ts.sort }}</template>
-				<option value="+pubSub">{{ $ts.pubSub }} ({{ $ts.descendingOrder }})</option>
-				<option value="-pubSub">{{ $ts.pubSub }} ({{ $ts.ascendingOrder }})</option>
-				<option value="+notes">{{ $ts.notes }} ({{ $ts.descendingOrder }})</option>
-				<option value="-notes">{{ $ts.notes }} ({{ $ts.ascendingOrder }})</option>
-				<option value="+users">{{ $ts.users }} ({{ $ts.descendingOrder }})</option>
-				<option value="-users">{{ $ts.users }} ({{ $ts.ascendingOrder }})</option>
-				<option value="+following">{{ $ts.following }} ({{ $ts.descendingOrder }})</option>
-				<option value="-following">{{ $ts.following }} ({{ $ts.ascendingOrder }})</option>
-				<option value="+followers">{{ $ts.followers }} ({{ $ts.descendingOrder }})</option>
-				<option value="-followers">{{ $ts.followers }} ({{ $ts.ascendingOrder }})</option>
-				<option value="+caughtAt">{{ $ts.registeredAt }} ({{ $ts.descendingOrder }})</option>
-				<option value="-caughtAt">{{ $ts.registeredAt }} ({{ $ts.ascendingOrder }})</option>
-				<option value="+lastCommunicatedAt">{{ $ts.lastCommunication }} ({{ $ts.descendingOrder }})</option>
-				<option value="-lastCommunicatedAt">{{ $ts.lastCommunication }} ({{ $ts.ascendingOrder }})</option>
+				<template #label>{{ i18n.ts.sort }}</template>
+				<option value="+pubSub">{{ i18n.ts.pubSub }} ({{ i18n.ts.descendingOrder }})</option>
+				<option value="-pubSub">{{ i18n.ts.pubSub }} ({{ i18n.ts.ascendingOrder }})</option>
+				<option value="+notes">{{ i18n.ts.notes }} ({{ i18n.ts.descendingOrder }})</option>
+				<option value="-notes">{{ i18n.ts.notes }} ({{ i18n.ts.ascendingOrder }})</option>
+				<option value="+users">{{ i18n.ts.users }} ({{ i18n.ts.descendingOrder }})</option>
+				<option value="-users">{{ i18n.ts.users }} ({{ i18n.ts.ascendingOrder }})</option>
+				<option value="+following">{{ i18n.ts.following }} ({{ i18n.ts.descendingOrder }})</option>
+				<option value="-following">{{ i18n.ts.following }} ({{ i18n.ts.ascendingOrder }})</option>
+				<option value="+followers">{{ i18n.ts.followers }} ({{ i18n.ts.descendingOrder }})</option>
+				<option value="-followers">{{ i18n.ts.followers }} ({{ i18n.ts.ascendingOrder }})</option>
+				<option value="+caughtAt">{{ i18n.ts.registeredAt }} ({{ i18n.ts.descendingOrder }})</option>
+				<option value="-caughtAt">{{ i18n.ts.registeredAt }} ({{ i18n.ts.ascendingOrder }})</option>
+				<option value="+lastCommunicatedAt">{{ i18n.ts.lastCommunication }} ({{ i18n.ts.descendingOrder }})</option>
+				<option value="-lastCommunicatedAt">{{ i18n.ts.lastCommunication }} ({{ i18n.ts.ascendingOrder }})</option>
 			</MkSelect>
 		</FormSplit>
 	</div>
diff --git a/packages/client/src/pages/about.vue b/packages/client/src/pages/about.vue
index e482c4ca6..ff50ba845 100644
--- a/packages/client/src/pages/about.vue
+++ b/packages/client/src/pages/about.vue
@@ -13,7 +13,7 @@
 			</div>
 
 			<MkKeyValue class="_formBlock">
-				<template #key>{{ $ts.description }}</template>
+				<template #key>{{ i18n.ts.description }}</template>
 				<template #value>{{ $instance.description }}</template>
 			</MkKeyValue>
 
@@ -22,33 +22,33 @@
 					<template #key>Misskey</template>
 					<template #value>{{ version }}</template>
 				</MkKeyValue>
-				<FormLink to="/about-misskey">{{ $ts.aboutMisskey }}</FormLink>
+				<FormLink to="/about-misskey">{{ i18n.ts.aboutMisskey }}</FormLink>
 			</FormSection>
 
 			<FormSection>
 				<FormSplit>
 					<MkKeyValue class="_formBlock">
-						<template #key>{{ $ts.administrator }}</template>
+						<template #key>{{ i18n.ts.administrator }}</template>
 						<template #value>{{ $instance.maintainerName }}</template>
 					</MkKeyValue>
 					<MkKeyValue class="_formBlock">
-						<template #key>{{ $ts.contact }}</template>
+						<template #key>{{ i18n.ts.contact }}</template>
 						<template #value>{{ $instance.maintainerEmail }}</template>
 					</MkKeyValue>
 				</FormSplit>
-				<FormLink v-if="$instance.tosUrl" :to="$instance.tosUrl" class="_formBlock" external>{{ $ts.tos }}</FormLink>
+				<FormLink v-if="$instance.tosUrl" :to="$instance.tosUrl" class="_formBlock" external>{{ i18n.ts.tos }}</FormLink>
 			</FormSection>
 
 			<FormSuspense :p="initStats">
 				<FormSection>
-					<template #label>{{ $ts.statistics }}</template>
+					<template #label>{{ i18n.ts.statistics }}</template>
 					<FormSplit>
 						<MkKeyValue class="_formBlock">
-							<template #key>{{ $ts.users }}</template>
+							<template #key>{{ i18n.ts.users }}</template>
 							<template #value>{{ number(stats.originalUsersCount) }}</template>
 						</MkKeyValue>
 						<MkKeyValue class="_formBlock">
-							<template #key>{{ $ts.notes }}</template>
+							<template #key>{{ i18n.ts.notes }}</template>
 							<template #value>{{ number(stats.originalNotesCount) }}</template>
 						</MkKeyValue>
 					</FormSplit>
diff --git a/packages/client/src/pages/admin/abuses.vue b/packages/client/src/pages/admin/abuses.vue
index 11cf284b2..9c718ab9e 100644
--- a/packages/client/src/pages/admin/abuses.vue
+++ b/packages/client/src/pages/admin/abuses.vue
@@ -7,31 +7,31 @@
 				<div class="_content">
 					<div class="inputs" style="display: flex;">
 						<MkSelect v-model="state" style="margin: 0; flex: 1;">
-							<template #label>{{ $ts.state }}</template>
-							<option value="all">{{ $ts.all }}</option>
-							<option value="unresolved">{{ $ts.unresolved }}</option>
-							<option value="resolved">{{ $ts.resolved }}</option>
+							<template #label>{{ i18n.ts.state }}</template>
+							<option value="all">{{ i18n.ts.all }}</option>
+							<option value="unresolved">{{ i18n.ts.unresolved }}</option>
+							<option value="resolved">{{ i18n.ts.resolved }}</option>
 						</MkSelect>
 						<MkSelect v-model="targetUserOrigin" style="margin: 0; flex: 1;">
-							<template #label>{{ $ts.reporteeOrigin }}</template>
-							<option value="combined">{{ $ts.all }}</option>
-							<option value="local">{{ $ts.local }}</option>
-							<option value="remote">{{ $ts.remote }}</option>
+							<template #label>{{ i18n.ts.reporteeOrigin }}</template>
+							<option value="combined">{{ i18n.ts.all }}</option>
+							<option value="local">{{ i18n.ts.local }}</option>
+							<option value="remote">{{ i18n.ts.remote }}</option>
 						</MkSelect>
 						<MkSelect v-model="reporterOrigin" style="margin: 0; flex: 1;">
-							<template #label>{{ $ts.reporterOrigin }}</template>
-							<option value="combined">{{ $ts.all }}</option>
-							<option value="local">{{ $ts.local }}</option>
-							<option value="remote">{{ $ts.remote }}</option>
+							<template #label>{{ i18n.ts.reporterOrigin }}</template>
+							<option value="combined">{{ i18n.ts.all }}</option>
+							<option value="local">{{ i18n.ts.local }}</option>
+							<option value="remote">{{ i18n.ts.remote }}</option>
 						</MkSelect>
 					</div>
 					<!-- TODO
 			<div class="inputs" style="display: flex; padding-top: 1.2em;">
 				<MkInput v-model="searchUsername" style="margin: 0; flex: 1;" type="text" :spellcheck="false">
-					<span>{{ $ts.username }}</span>
+					<span>{{ i18n.ts.username }}</span>
 				</MkInput>
 				<MkInput v-model="searchHost" style="margin: 0; flex: 1;" type="text" :spellcheck="false" :disabled="pagination.params().origin === 'local'">
-					<span>{{ $ts.host }}</span>
+					<span>{{ i18n.ts.host }}</span>
 				</MkInput>
 			</div>
 			-->
diff --git a/packages/client/src/pages/admin/bot-protection.vue b/packages/client/src/pages/admin/bot-protection.vue
index d316f973b..cf2cdea92 100644
--- a/packages/client/src/pages/admin/bot-protection.vue
+++ b/packages/client/src/pages/admin/bot-protection.vue
@@ -3,7 +3,7 @@
 	<FormSuspense :p="init">
 		<div class="_formRoot">
 			<FormRadios v-model="provider" class="_formBlock">
-				<option :value="null">{{ $ts.none }} ({{ $ts.notRecommended }})</option>
+				<option :value="null">{{ i18n.ts.none }} ({{ i18n.ts.notRecommended }})</option>
 				<option value="hcaptcha">hCaptcha</option>
 				<option value="recaptcha">reCAPTCHA</option>
 			</FormRadios>
@@ -11,33 +11,33 @@
 			<template v-if="provider === 'hcaptcha'">
 				<FormInput v-model="hcaptchaSiteKey" class="_formBlock">
 					<template #prefix><i class="fas fa-key"></i></template>
-					<template #label>{{ $ts.hcaptchaSiteKey }}</template>
+					<template #label>{{ i18n.ts.hcaptchaSiteKey }}</template>
 				</FormInput>
 				<FormInput v-model="hcaptchaSecretKey" class="_formBlock">
 					<template #prefix><i class="fas fa-key"></i></template>
-					<template #label>{{ $ts.hcaptchaSecretKey }}</template>
+					<template #label>{{ i18n.ts.hcaptchaSecretKey }}</template>
 				</FormInput>
 				<FormSlot class="_formBlock">
-					<template #label>{{ $ts.preview }}</template>
+					<template #label>{{ i18n.ts.preview }}</template>
 					<MkCaptcha provider="hcaptcha" :sitekey="hcaptchaSiteKey || '10000000-ffff-ffff-ffff-000000000001'"/>
 				</FormSlot>
 			</template>
 			<template v-else-if="provider === 'recaptcha'">
 				<FormInput v-model="recaptchaSiteKey" class="_formBlock">
 					<template #prefix><i class="fas fa-key"></i></template>
-					<template #label>{{ $ts.recaptchaSiteKey }}</template>
+					<template #label>{{ i18n.ts.recaptchaSiteKey }}</template>
 				</FormInput>
 				<FormInput v-model="recaptchaSecretKey" class="_formBlock">
 					<template #prefix><i class="fas fa-key"></i></template>
-					<template #label>{{ $ts.recaptchaSecretKey }}</template>
+					<template #label>{{ i18n.ts.recaptchaSecretKey }}</template>
 				</FormInput>
 				<FormSlot v-if="recaptchaSiteKey" class="_formBlock">
-					<template #label>{{ $ts.preview }}</template>
+					<template #label>{{ i18n.ts.preview }}</template>
 					<MkCaptcha provider="recaptcha" :sitekey="recaptchaSiteKey"/>
 				</FormSlot>
 			</template>
 
-			<FormButton primary @click="save"><i class="fas fa-save"></i> {{ $ts.save }}</FormButton>
+			<FormButton primary @click="save"><i class="fas fa-save"></i> {{ i18n.ts.save }}</FormButton>
 		</div>
 	</FormSuspense>
 </div>
@@ -52,6 +52,7 @@ import FormSuspense from '@/components/form/suspense.vue';
 import FormSlot from '@/components/form/slot.vue';
 import * as os from '@/os';
 import { fetchInstance } from '@/instance';
+import { i18n } from '@/i18n';
 
 const MkCaptcha = defineAsyncComponent(() => import('@/components/captcha.vue'));
 
diff --git a/packages/client/src/pages/admin/emoji-edit-dialog.vue b/packages/client/src/pages/admin/emoji-edit-dialog.vue
index d482fa49e..af91044e7 100644
--- a/packages/client/src/pages/admin/emoji-edit-dialog.vue
+++ b/packages/client/src/pages/admin/emoji-edit-dialog.vue
@@ -1,5 +1,6 @@
 <template>
-<XModalWindow ref="dialog"
+<XModalWindow
+	ref="dialog"
 	:width="370"
 	:with-ok-button="true"
 	@close="$refs.dialog.close()"
@@ -12,16 +13,16 @@
 		<div class="yigymqpb _section">
 			<img :src="emoji.url" class="img"/>
 			<MkInput v-model="name" class="_formBlock">
-				<template #label>{{ $ts.name }}</template>
+				<template #label>{{ i18n.ts.name }}</template>
 			</MkInput>
 			<MkInput v-model="category" class="_formBlock" :datalist="categories">
-				<template #label>{{ $ts.category }}</template>
+				<template #label>{{ i18n.ts.category }}</template>
 			</MkInput>
 			<MkInput v-model="aliases" class="_formBlock">
-				<template #label>{{ $ts.tags }}</template>
-				<template #caption>{{ $ts.setMultipleBySeparatingWithSpace }}</template>
+				<template #label>{{ i18n.ts.tags }}</template>
+				<template #caption>{{ i18n.ts.setMultipleBySeparatingWithSpace }}</template>
 			</MkInput>
-			<MkButton danger @click="del()"><i class="fas fa-trash-alt"></i> {{ $ts.delete }}</MkButton>
+			<MkButton danger @click="del()"><i class="fas fa-trash-alt"></i> {{ i18n.ts.delete }}</MkButton>
 		</div>
 	</div>
 </XModalWindow>
@@ -70,7 +71,7 @@ async function update() {
 			name,
 			category,
 			aliases: aliases.split(' '),
-		}
+		},
 	});
 
 	dialog.close();
@@ -84,10 +85,10 @@ async function del() {
 	if (canceled) return;
 
 	os.api('admin/emoji/delete', {
-		id: props.emoji.id
+		id: props.emoji.id,
 	}).then(() => {
 		emit('done', {
-			deleted: true
+			deleted: true,
 		});
 		dialog.close();
 	});
diff --git a/packages/client/src/pages/admin/emojis.vue b/packages/client/src/pages/admin/emojis.vue
index 5ed2b1478..51ecd1b79 100644
--- a/packages/client/src/pages/admin/emojis.vue
+++ b/packages/client/src/pages/admin/emojis.vue
@@ -7,7 +7,7 @@
 				<div v-if="tab === 'local'" class="local">
 					<MkInput v-model="query" :debounce="true" type="search">
 						<template #prefix><i class="fas fa-search"></i></template>
-						<template #label>{{ $ts.search }}</template>
+						<template #label>{{ i18n.ts.search }}</template>
 					</MkInput>
 					<MkSwitch v-model="selectMode" style="margin: 8px 0;">
 						<template #label>Select mode</template>
@@ -21,7 +21,7 @@
 						<MkButton inline danger @click="delBulk">Delete</MkButton>
 					</div>
 					<MkPagination ref="emojisPaginationComponent" :pagination="pagination">
-						<template #empty><span>{{ $ts.noCustomEmojis }}</span></template>
+						<template #empty><span>{{ i18n.ts.noCustomEmojis }}</span></template>
 						<template #default="{items}">
 							<div class="ldhfsamy">
 								<button v-for="emoji in items" :key="emoji.id" class="emoji _panel _button" :class="{ selected: selectedEmojis.includes(emoji.id) }" @click="selectMode ? toggleSelect(emoji) : edit(emoji)">
@@ -40,14 +40,14 @@
 					<FormSplit>
 						<MkInput v-model="queryRemote" :debounce="true" type="search">
 							<template #prefix><i class="fas fa-search"></i></template>
-							<template #label>{{ $ts.search }}</template>
+							<template #label>{{ i18n.ts.search }}</template>
 						</MkInput>
 						<MkInput v-model="host" :debounce="true">
-							<template #label>{{ $ts.host }}</template>
+							<template #label>{{ i18n.ts.host }}</template>
 						</MkInput>
 					</FormSplit>
 					<MkPagination :pagination="remotePagination">
-						<template #empty><span>{{ $ts.noCustomEmojis }}</span></template>
+						<template #empty><span>{{ i18n.ts.noCustomEmojis }}</span></template>
 						<template #default="{items}">
 							<div class="ldhfsamy">
 								<div v-for="emoji in items" :key="emoji.id" class="emoji _panel _button" @click="remoteMenu(emoji, $event)">
diff --git a/packages/client/src/pages/admin/files.vue b/packages/client/src/pages/admin/files.vue
index dd309180a..2614a6b8d 100644
--- a/packages/client/src/pages/admin/files.vue
+++ b/packages/client/src/pages/admin/files.vue
@@ -7,13 +7,13 @@
 				<div>
 					<div class="inputs" style="display: flex; gap: var(--margin); flex-wrap: wrap;">
 						<MkSelect v-model="origin" style="margin: 0; flex: 1;">
-							<template #label>{{ $ts.instance }}</template>
-							<option value="combined">{{ $ts.all }}</option>
-							<option value="local">{{ $ts.local }}</option>
-							<option value="remote">{{ $ts.remote }}</option>
+							<template #label>{{ i18n.ts.instance }}</template>
+							<option value="combined">{{ i18n.ts.all }}</option>
+							<option value="local">{{ i18n.ts.local }}</option>
+							<option value="remote">{{ i18n.ts.remote }}</option>
 						</MkSelect>
 						<MkInput v-model="searchHost" :debounce="true" type="search" style="margin: 0; flex: 1;" :disabled="pagination.params.origin === 'local'">
-							<template #label>{{ $ts.host }}</template>
+							<template #label>{{ i18n.ts.host }}</template>
 						</MkInput>
 					</div>
 					<div class="inputs" style="display: flex; gap: var(--margin); flex-wrap: wrap; padding-top: 1.2em;">
diff --git a/packages/client/src/pages/admin/index.vue b/packages/client/src/pages/admin/index.vue
index 2ff55d351..154b85480 100644
--- a/packages/client/src/pages/admin/index.vue
+++ b/packages/client/src/pages/admin/index.vue
@@ -7,10 +7,10 @@
 					<img :src="$instance.iconUrl || '/favicon.ico'" alt="" class="icon"/>
 				</div>
 
-				<MkInfo v-if="thereIsUnresolvedAbuseReport" warn class="info">{{ $ts.thereIsUnresolvedAbuseReportWarning }} <MkA to="/admin/abuses" class="_link">{{ $ts.check }}</MkA></MkInfo>
-				<MkInfo v-if="noMaintainerInformation" warn class="info">{{ $ts.noMaintainerInformationWarning }} <MkA to="/admin/settings" class="_link">{{ $ts.configure }}</MkA></MkInfo>
-				<MkInfo v-if="noBotProtection" warn class="info">{{ $ts.noBotProtectionWarning }} <MkA to="/admin/security" class="_link">{{ $ts.configure }}</MkA></MkInfo>
-				<MkInfo v-if="noEmailServer" warn class="info">{{ $ts.noEmailServerWarning }} <MkA to="/admin/email-settings" class="_link">{{ $ts.configure }}</MkA></MkInfo>
+				<MkInfo v-if="thereIsUnresolvedAbuseReport" warn class="info">{{ i18n.ts.thereIsUnresolvedAbuseReportWarning }} <MkA to="/admin/abuses" class="_link">{{ i18n.ts.check }}</MkA></MkInfo>
+				<MkInfo v-if="noMaintainerInformation" warn class="info">{{ i18n.ts.noMaintainerInformationWarning }} <MkA to="/admin/settings" class="_link">{{ i18n.ts.configure }}</MkA></MkInfo>
+				<MkInfo v-if="noBotProtection" warn class="info">{{ i18n.ts.noBotProtectionWarning }} <MkA to="/admin/security" class="_link">{{ i18n.ts.configure }}</MkA></MkInfo>
+				<MkInfo v-if="noEmailServer" warn class="info">{{ i18n.ts.noEmailServerWarning }} <MkA to="/admin/email-settings" class="_link">{{ i18n.ts.configure }}</MkA></MkInfo>
 
 				<MkSuperMenu :def="menuDef" :grid="currentPage?.route.name == null"></MkSuperMenu>
 			</div>
diff --git a/packages/client/src/pages/admin/integrations.discord.vue b/packages/client/src/pages/admin/integrations.discord.vue
index 9fdc51a6c..eff318dc1 100644
--- a/packages/client/src/pages/admin/integrations.discord.vue
+++ b/packages/client/src/pages/admin/integrations.discord.vue
@@ -2,7 +2,7 @@
 <FormSuspense :p="init">
 	<div class="_formRoot">
 		<FormSwitch v-model="enableDiscordIntegration" class="_formBlock">
-			<template #label>{{ $ts.enable }}</template>
+			<template #label>{{ i18n.ts.enable }}</template>
 		</FormSwitch>
 
 		<template v-if="enableDiscordIntegration">
@@ -19,7 +19,7 @@
 			</FormInput>
 		</template>
 
-		<FormButton primary class="_formBlock" @click="save"><i class="fas fa-save"></i> {{ $ts.save }}</FormButton>
+		<FormButton primary class="_formBlock" @click="save"><i class="fas fa-save"></i> {{ i18n.ts.save }}</FormButton>
 	</div>
 </FormSuspense>
 </template>
@@ -33,6 +33,7 @@ import FormInfo from '@/components/ui/info.vue';
 import FormSuspense from '@/components/form/suspense.vue';
 import * as os from '@/os';
 import { fetchInstance } from '@/instance';
+import { i18n } from '@/i18n';
 
 let uri: string = $ref('');
 let enableDiscordIntegration: boolean = $ref(false);
diff --git a/packages/client/src/pages/admin/integrations.github.vue b/packages/client/src/pages/admin/integrations.github.vue
index b10ccb839..f6e56e1ed 100644
--- a/packages/client/src/pages/admin/integrations.github.vue
+++ b/packages/client/src/pages/admin/integrations.github.vue
@@ -2,7 +2,7 @@
 <FormSuspense :p="init">
 	<div class="_formRoot">
 		<FormSwitch v-model="enableGithubIntegration" class="_formBlock">
-			<template #label>{{ $ts.enable }}</template>
+			<template #label>{{ i18n.ts.enable }}</template>
 		</FormSwitch>
 
 		<template v-if="enableGithubIntegration">
@@ -19,7 +19,7 @@
 			</FormInput>
 		</template>
 
-		<FormButton primary class="_formBlock" @click="save"><i class="fas fa-save"></i> {{ $ts.save }}</FormButton>
+		<FormButton primary class="_formBlock" @click="save"><i class="fas fa-save"></i> {{ i18n.ts.save }}</FormButton>
 	</div>
 </FormSuspense>
 </template>
@@ -33,6 +33,7 @@ import FormInfo from '@/components/ui/info.vue';
 import FormSuspense from '@/components/form/suspense.vue';
 import * as os from '@/os';
 import { fetchInstance } from '@/instance';
+import { i18n } from '@/i18n';
 
 let uri: string = $ref('');
 let enableGithubIntegration: boolean = $ref(false);
diff --git a/packages/client/src/pages/admin/integrations.twitter.vue b/packages/client/src/pages/admin/integrations.twitter.vue
index 11b5fd86b..48da4648f 100644
--- a/packages/client/src/pages/admin/integrations.twitter.vue
+++ b/packages/client/src/pages/admin/integrations.twitter.vue
@@ -2,7 +2,7 @@
 <FormSuspense :p="init">
 	<div class="_formRoot">
 		<FormSwitch v-model="enableTwitterIntegration" class="_formBlock">
-			<template #label>{{ $ts.enable }}</template>
+			<template #label>{{ i18n.ts.enable }}</template>
 		</FormSwitch>
 
 		<template v-if="enableTwitterIntegration">
@@ -19,7 +19,7 @@
 			</FormInput>
 		</template>
 
-		<FormButton primary class="_formBlock" @click="save"><i class="fas fa-save"></i> {{ $ts.save }}</FormButton>
+		<FormButton primary class="_formBlock" @click="save"><i class="fas fa-save"></i> {{ i18n.ts.save }}</FormButton>
 	</div>
 </FormSuspense>
 </template>
@@ -33,6 +33,7 @@ import FormInfo from '@/components/ui/info.vue';
 import FormSuspense from '@/components/form/suspense.vue';
 import * as os from '@/os';
 import { fetchInstance } from '@/instance';
+import { i18n } from '@/i18n';
 
 let uri: string = $ref('');
 let enableTwitterIntegration: boolean = $ref(false);
diff --git a/packages/client/src/pages/admin/queue.chart.vue b/packages/client/src/pages/admin/queue.chart.vue
index c213037b6..186a22c43 100644
--- a/packages/client/src/pages/admin/queue.chart.vue
+++ b/packages/client/src/pages/admin/queue.chart.vue
@@ -33,7 +33,7 @@
 				<span style="margin-left: 8px; opacity: 0.7;">({{ number(job[1]) }} jobs)</span>
 			</div>
 		</div>
-		<span v-else style="opacity: 0.5;">{{ $ts.noJobs }}</span>
+		<span v-else style="opacity: 0.5;">{{ i18n.ts.noJobs }}</span>
 	</div>
 </div>
 </template>
@@ -44,6 +44,7 @@ import XChart from './queue.chart.chart.vue';
 import number from '@/filters/number';
 import * as os from '@/os';
 import { stream } from '@/stream';
+import { i18n } from '@/i18n';
 
 const connection = markRaw(stream.useChannel('queueStats'));
 
diff --git a/packages/client/src/pages/admin/users.vue b/packages/client/src/pages/admin/users.vue
index c6755672f..6bc569f24 100644
--- a/packages/client/src/pages/admin/users.vue
+++ b/packages/client/src/pages/admin/users.vue
@@ -7,36 +7,36 @@
 				<div class="users">
 					<div class="inputs">
 						<MkSelect v-model="sort" style="flex: 1;">
-							<template #label>{{ $ts.sort }}</template>
-							<option value="-createdAt">{{ $ts.registeredDate }} ({{ $ts.ascendingOrder }})</option>
-							<option value="+createdAt">{{ $ts.registeredDate }} ({{ $ts.descendingOrder }})</option>
-							<option value="-updatedAt">{{ $ts.lastUsed }} ({{ $ts.ascendingOrder }})</option>
-							<option value="+updatedAt">{{ $ts.lastUsed }} ({{ $ts.descendingOrder }})</option>
+							<template #label>{{ i18n.ts.sort }}</template>
+							<option value="-createdAt">{{ i18n.ts.registeredDate }} ({{ i18n.ts.ascendingOrder }})</option>
+							<option value="+createdAt">{{ i18n.ts.registeredDate }} ({{ i18n.ts.descendingOrder }})</option>
+							<option value="-updatedAt">{{ i18n.ts.lastUsed }} ({{ i18n.ts.ascendingOrder }})</option>
+							<option value="+updatedAt">{{ i18n.ts.lastUsed }} ({{ i18n.ts.descendingOrder }})</option>
 						</MkSelect>
 						<MkSelect v-model="state" style="flex: 1;">
-							<template #label>{{ $ts.state }}</template>
-							<option value="all">{{ $ts.all }}</option>
-							<option value="available">{{ $ts.normal }}</option>
-							<option value="admin">{{ $ts.administrator }}</option>
-							<option value="moderator">{{ $ts.moderator }}</option>
-							<option value="silenced">{{ $ts.silence }}</option>
-							<option value="suspended">{{ $ts.suspend }}</option>
+							<template #label>{{ i18n.ts.state }}</template>
+							<option value="all">{{ i18n.ts.all }}</option>
+							<option value="available">{{ i18n.ts.normal }}</option>
+							<option value="admin">{{ i18n.ts.administrator }}</option>
+							<option value="moderator">{{ i18n.ts.moderator }}</option>
+							<option value="silenced">{{ i18n.ts.silence }}</option>
+							<option value="suspended">{{ i18n.ts.suspend }}</option>
 						</MkSelect>
 						<MkSelect v-model="origin" style="flex: 1;">
-							<template #label>{{ $ts.instance }}</template>
-							<option value="combined">{{ $ts.all }}</option>
-							<option value="local">{{ $ts.local }}</option>
-							<option value="remote">{{ $ts.remote }}</option>
+							<template #label>{{ i18n.ts.instance }}</template>
+							<option value="combined">{{ i18n.ts.all }}</option>
+							<option value="local">{{ i18n.ts.local }}</option>
+							<option value="remote">{{ i18n.ts.remote }}</option>
 						</MkSelect>
 					</div>
 					<div class="inputs">
 						<MkInput v-model="searchUsername" style="flex: 1;" type="text" :spellcheck="false" @update:modelValue="$refs.users.reload()">
 							<template #prefix>@</template>
-							<template #label>{{ $ts.username }}</template>
+							<template #label>{{ i18n.ts.username }}</template>
 						</MkInput>
 						<MkInput v-model="searchHost" style="flex: 1;" type="text" :spellcheck="false" :disabled="pagination.params.origin === 'local'" @update:modelValue="$refs.users.reload()">
 							<template #prefix>@</template>
-							<template #label>{{ $ts.host }}</template>
+							<template #label>{{ i18n.ts.host }}</template>
 						</MkInput>
 					</div>
 
diff --git a/packages/client/src/pages/channel-editor.vue b/packages/client/src/pages/channel-editor.vue
index 0fa1f6951..5b5d0454f 100644
--- a/packages/client/src/pages/channel-editor.vue
+++ b/packages/client/src/pages/channel-editor.vue
@@ -4,22 +4,22 @@
 	<MkSpacer :content-max="700">
 		<div class="_formRoot">
 			<MkInput v-model="name" class="_formBlock">
-				<template #label>{{ $ts.name }}</template>
+				<template #label>{{ i18n.ts.name }}</template>
 			</MkInput>
 
 			<MkTextarea v-model="description" class="_formBlock">
-				<template #label>{{ $ts.description }}</template>
+				<template #label>{{ i18n.ts.description }}</template>
 			</MkTextarea>
 
 			<div class="banner">
-				<MkButton v-if="bannerId == null" @click="setBannerImage"><i class="fas fa-plus"></i> {{ $ts._channel.setBanner }}</MkButton>
+				<MkButton v-if="bannerId == null" @click="setBannerImage"><i class="fas fa-plus"></i> {{ i18n.ts._channel.setBanner }}</MkButton>
 				<div v-else-if="bannerUrl">
 					<img :src="bannerUrl" style="width: 100%;"/>
-					<MkButton @click="removeBannerImage()"><i class="fas fa-trash-alt"></i> {{ $ts._channel.removeBanner }}</MkButton>
+					<MkButton @click="removeBannerImage()"><i class="fas fa-trash-alt"></i> {{ i18n.ts._channel.removeBanner }}</MkButton>
 				</div>
 			</div>
 			<div class="_formBlock">
-				<MkButton primary @click="save()"><i class="fas fa-save"></i> {{ channelId ? $ts.save : $ts.create }}</MkButton>
+				<MkButton primary @click="save()"><i class="fas fa-save"></i> {{ channelId ? i18n.ts.save : i18n.ts.create }}</MkButton>
 			</div>
 		</div>
 	</MkSpacer>
diff --git a/packages/client/src/pages/channel.vue b/packages/client/src/pages/channel.vue
index 1443a9b64..4a3c97cda 100644
--- a/packages/client/src/pages/channel.vue
+++ b/packages/client/src/pages/channel.vue
@@ -13,8 +13,8 @@
 				</div>
 				<div :style="{ backgroundImage: channel.bannerUrl ? `url(${channel.bannerUrl})` : null }" class="banner">
 					<div class="status">
-						<div><i class="fas fa-users fa-fw"></i><I18n :src="$ts._channel.usersCount" tag="span" style="margin-left: 4px;"><template #n><b>{{ channel.usersCount }}</b></template></I18n></div>
-						<div><i class="fas fa-pencil-alt fa-fw"></i><I18n :src="$ts._channel.notesCount" tag="span" style="margin-left: 4px;"><template #n><b>{{ channel.notesCount }}</b></template></I18n></div>
+						<div><i class="fas fa-users fa-fw"></i><I18n :src="i18n.ts._channel.usersCount" tag="span" style="margin-left: 4px;"><template #n><b>{{ channel.usersCount }}</b></template></I18n></div>
+						<div><i class="fas fa-pencil-alt fa-fw"></i><I18n :src="i18n.ts._channel.notesCount" tag="span" style="margin-left: 4px;"><template #n><b>{{ channel.notesCount }}</b></template></I18n></div>
 					</div>
 					<div class="fade"></div>
 				</div>
diff --git a/packages/client/src/pages/explore.users.vue b/packages/client/src/pages/explore.users.vue
index 8af37ab52..ed5ee3b79 100644
--- a/packages/client/src/pages/explore.users.vue
+++ b/packages/client/src/pages/explore.users.vue
@@ -1,32 +1,32 @@
 <template>
 <MkSpacer :content-max="1200">
 	<MkTab v-model="origin" style="margin-bottom: var(--margin);">
-		<option value="local">{{ $ts.local }}</option>
-		<option value="remote">{{ $ts.remote }}</option>
+		<option value="local">{{ i18n.ts.local }}</option>
+		<option value="remote">{{ i18n.ts.remote }}</option>
 	</MkTab>
 	<div v-if="origin === 'local'">
 		<template v-if="tag == null">
 			<MkFolder class="_gap" persist-key="explore-pinned-users">
-				<template #header><i class="fas fa-bookmark fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.pinnedUsers }}</template>
+				<template #header><i class="fas fa-bookmark fa-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.pinnedUsers }}</template>
 				<XUserList :pagination="pinnedUsers"/>
 			</MkFolder>
 			<MkFolder class="_gap" persist-key="explore-popular-users">
-				<template #header><i class="fas fa-chart-line fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.popularUsers }}</template>
+				<template #header><i class="fas fa-chart-line fa-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.popularUsers }}</template>
 				<XUserList :pagination="popularUsers"/>
 			</MkFolder>
 			<MkFolder class="_gap" persist-key="explore-recently-updated-users">
-				<template #header><i class="fas fa-comment-alt fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.recentlyUpdatedUsers }}</template>
+				<template #header><i class="fas fa-comment-alt fa-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.recentlyUpdatedUsers }}</template>
 				<XUserList :pagination="recentlyUpdatedUsers"/>
 			</MkFolder>
 			<MkFolder class="_gap" persist-key="explore-recently-registered-users">
-				<template #header><i class="fas fa-plus fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.recentlyRegisteredUsers }}</template>
+				<template #header><i class="fas fa-plus fa-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.recentlyRegisteredUsers }}</template>
 				<XUserList :pagination="recentlyRegisteredUsers"/>
 			</MkFolder>
 		</template>
 	</div>
 	<div v-else>
 		<MkFolder ref="tagsEl" :foldable="true" :expanded="false" class="_gap">
-			<template #header><i class="fas fa-hashtag fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.popularTags }}</template>
+			<template #header><i class="fas fa-hashtag fa-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.popularTags }}</template>
 
 			<div class="vxjfqztj">
 				<MkA v-for="tag in tagsLocal" :key="'local:' + tag.tag" :to="`/explore/tags/${tag.tag}`" class="local">{{ tag.tag }}</MkA>
@@ -41,15 +41,15 @@
 
 		<template v-if="tag == null">
 			<MkFolder class="_gap">
-				<template #header><i class="fas fa-chart-line fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.popularUsers }}</template>
+				<template #header><i class="fas fa-chart-line fa-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.popularUsers }}</template>
 				<XUserList :pagination="popularUsersF"/>
 			</MkFolder>
 			<MkFolder class="_gap">
-				<template #header><i class="fas fa-comment-alt fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.recentlyUpdatedUsers }}</template>
+				<template #header><i class="fas fa-comment-alt fa-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.recentlyUpdatedUsers }}</template>
 				<XUserList :pagination="recentlyUpdatedUsersF"/>
 			</MkFolder>
 			<MkFolder class="_gap">
-				<template #header><i class="fas fa-rocket fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.recentlyDiscoveredUsers }}</template>
+				<template #header><i class="fas fa-rocket fa-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.recentlyDiscoveredUsers }}</template>
 				<XUserList :pagination="recentlyRegisteredUsersF"/>
 			</MkFolder>
 		</template>
diff --git a/packages/client/src/pages/explore.vue b/packages/client/src/pages/explore.vue
index 059e94d1d..4725278ab 100644
--- a/packages/client/src/pages/explore.vue
+++ b/packages/client/src/pages/explore.vue
@@ -13,12 +13,12 @@
 				<div>
 					<MkInput v-model="searchQuery" :debounce="true" type="search" class="_formBlock">
 						<template #prefix><i class="fas fa-search"></i></template>
-						<template #label>{{ $ts.searchUser }}</template>
+						<template #label>{{ i18n.ts.searchUser }}</template>
 					</MkInput>
 					<MkRadios v-model="searchOrigin" class="_formBlock">
-						<option value="combined">{{ $ts.all }}</option>
-						<option value="local">{{ $ts.local }}</option>
-						<option value="remote">{{ $ts.remote }}</option>
+						<option value="combined">{{ i18n.ts.all }}</option>
+						<option value="local">{{ i18n.ts.local }}</option>
+						<option value="remote">{{ i18n.ts.remote }}</option>
 					</MkRadios>
 				</div>
 
diff --git a/packages/client/src/pages/favorites.vue b/packages/client/src/pages/favorites.vue
index 6f75d68de..203dc4a51 100644
--- a/packages/client/src/pages/favorites.vue
+++ b/packages/client/src/pages/favorites.vue
@@ -6,7 +6,7 @@
 			<template #empty>
 				<div class="_fullinfo">
 					<img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
-					<div>{{ $ts.noNotes }}</div>
+					<div>{{ i18n.ts.noNotes }}</div>
 				</div>
 			</template>
 
diff --git a/packages/client/src/pages/follow-requests.vue b/packages/client/src/pages/follow-requests.vue
index 1f4dc9e93..f87bcbf24 100644
--- a/packages/client/src/pages/follow-requests.vue
+++ b/packages/client/src/pages/follow-requests.vue
@@ -6,7 +6,7 @@
 			<template #empty>
 				<div class="_fullinfo">
 					<img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
-					<div>{{ $ts.noFollowRequests }}</div>
+					<div>{{ i18n.ts.noFollowRequests }}</div>
 				</div>
 			</template>
 			<template #default="{items}">
diff --git a/packages/client/src/pages/gallery/edit.vue b/packages/client/src/pages/gallery/edit.vue
index f8a5d54f7..2e0e5063e 100644
--- a/packages/client/src/pages/gallery/edit.vue
+++ b/packages/client/src/pages/gallery/edit.vue
@@ -4,27 +4,27 @@
 	<MkSpacer :content-max="800" :margin-min="16" :margin-max="32">
 		<FormSuspense :p="init">
 			<FormInput v-model="title">
-				<template #label>{{ $ts.title }}</template>
+				<template #label>{{ i18n.ts.title }}</template>
 			</FormInput>
 
 			<FormTextarea v-model="description" :max="500">
-				<template #label>{{ $ts.description }}</template>
+				<template #label>{{ i18n.ts.description }}</template>
 			</FormTextarea>
 
 			<div class="">
 				<div v-for="file in files" :key="file.id" class="wqugxsfx" :style="{ backgroundImage: file ? `url(${ file.thumbnailUrl })` : null }">
 					<div class="name">{{ file.name }}</div>
-					<button v-tooltip="$ts.remove" class="remove _button" @click="remove(file)"><i class="fas fa-times"></i></button>
+					<button v-tooltip="i18n.ts.remove" class="remove _button" @click="remove(file)"><i class="fas fa-times"></i></button>
 				</div>
-				<FormButton primary @click="selectFile"><i class="fas fa-plus"></i> {{ $ts.attachFile }}</FormButton>
+				<FormButton primary @click="selectFile"><i class="fas fa-plus"></i> {{ i18n.ts.attachFile }}</FormButton>
 			</div>
 
-			<FormSwitch v-model="isSensitive">{{ $ts.markAsSensitive }}</FormSwitch>
+			<FormSwitch v-model="isSensitive">{{ i18n.ts.markAsSensitive }}</FormSwitch>
 
-			<FormButton v-if="postId" primary @click="save"><i class="fas fa-save"></i> {{ $ts.save }}</FormButton>
-			<FormButton v-else primary @click="save"><i class="fas fa-save"></i> {{ $ts.publish }}</FormButton>
+			<FormButton v-if="postId" primary @click="save"><i class="fas fa-save"></i> {{ i18n.ts.save }}</FormButton>
+			<FormButton v-else primary @click="save"><i class="fas fa-save"></i> {{ i18n.ts.publish }}</FormButton>
 
-			<FormButton v-if="postId" danger @click="del"><i class="fas fa-trash-alt"></i> {{ $ts.delete }}</FormButton>
+			<FormButton v-if="postId" danger @click="del"><i class="fas fa-trash-alt"></i> {{ i18n.ts.delete }}</FormButton>
 		</FormSuspense>
 	</MkSpacer>
 </MkStickyContainer>
diff --git a/packages/client/src/pages/gallery/index.vue b/packages/client/src/pages/gallery/index.vue
index 6b406af74..fba8b96bd 100644
--- a/packages/client/src/pages/gallery/index.vue
+++ b/packages/client/src/pages/gallery/index.vue
@@ -5,7 +5,7 @@
 		<div class="_root">
 			<div v-if="tab === 'explore'">
 				<MkFolder class="_gap">
-					<template #header><i class="fas fa-clock"></i>{{ $ts.recentPosts }}</template>
+					<template #header><i class="fas fa-clock"></i>{{ i18n.ts.recentPosts }}</template>
 					<MkPagination v-slot="{items}" :pagination="recentPostsPagination" :disable-auto-load="true">
 						<div class="vfpdbgtk">
 							<MkGalleryPostPreview v-for="post in items" :key="post.id" :post="post" class="post"/>
@@ -13,7 +13,7 @@
 					</MkPagination>
 				</MkFolder>
 				<MkFolder class="_gap">
-					<template #header><i class="fas fa-fire-alt"></i>{{ $ts.popularPosts }}</template>
+					<template #header><i class="fas fa-fire-alt"></i>{{ i18n.ts.popularPosts }}</template>
 					<MkPagination v-slot="{items}" :pagination="popularPostsPagination" :disable-auto-load="true">
 						<div class="vfpdbgtk">
 							<MkGalleryPostPreview v-for="post in items" :key="post.id" :post="post" class="post"/>
@@ -29,7 +29,7 @@
 				</MkPagination>
 			</div>
 			<div v-else-if="tab === 'my'">
-				<MkA to="/gallery/new" class="_link" style="margin: 16px;"><i class="fas fa-plus"></i> {{ $ts.postToGallery }}</MkA>
+				<MkA to="/gallery/new" class="_link" style="margin: 16px;"><i class="fas fa-plus"></i> {{ i18n.ts.postToGallery }}</MkA>
 				<MkPagination v-slot="{items}" :pagination="myPostsPagination">
 					<div class="vfpdbgtk">
 						<MkGalleryPostPreview v-for="post in items" :key="post.id" :post="post" class="post"/>
diff --git a/packages/client/src/pages/gallery/post.vue b/packages/client/src/pages/gallery/post.vue
index e87a541e9..a3a11d9a6 100644
--- a/packages/client/src/pages/gallery/post.vue
+++ b/packages/client/src/pages/gallery/post.vue
@@ -18,13 +18,13 @@
 						</div>
 						<div class="actions">
 							<div class="like">
-								<MkButton v-if="post.isLiked" v-tooltip="$ts._gallery.unlike" class="button" primary @click="unlike()"><i class="fas fa-heart"></i><span v-if="post.likedCount > 0" class="count">{{ post.likedCount }}</span></MkButton>
-								<MkButton v-else v-tooltip="$ts._gallery.like" class="button" @click="like()"><i class="far fa-heart"></i><span v-if="post.likedCount > 0" class="count">{{ post.likedCount }}</span></MkButton>
+								<MkButton v-if="post.isLiked" v-tooltip="i18n.ts._gallery.unlike" class="button" primary @click="unlike()"><i class="fas fa-heart"></i><span v-if="post.likedCount > 0" class="count">{{ post.likedCount }}</span></MkButton>
+								<MkButton v-else v-tooltip="i18n.ts._gallery.like" class="button" @click="like()"><i class="far fa-heart"></i><span v-if="post.likedCount > 0" class="count">{{ post.likedCount }}</span></MkButton>
 							</div>
 							<div class="other">
-								<button v-if="$i && $i.id === post.user.id" v-tooltip="$ts.edit" v-click-anime class="_button" @click="edit"><i class="fas fa-pencil-alt fa-fw"></i></button>
-								<button v-tooltip="$ts.shareWithNote" v-click-anime class="_button" @click="shareWithNote"><i class="fas fa-retweet fa-fw"></i></button>
-								<button v-tooltip="$ts.share" v-click-anime class="_button" @click="share"><i class="fas fa-share-alt fa-fw"></i></button>
+								<button v-if="$i && $i.id === post.user.id" v-tooltip="i18n.ts.edit" v-click-anime class="_button" @click="edit"><i class="fas fa-pencil-alt fa-fw"></i></button>
+								<button v-tooltip="i18n.ts.shareWithNote" v-click-anime class="_button" @click="shareWithNote"><i class="fas fa-retweet fa-fw"></i></button>
+								<button v-tooltip="i18n.ts.share" v-click-anime class="_button" @click="share"><i class="fas fa-share-alt fa-fw"></i></button>
 							</div>
 						</div>
 						<div class="user">
@@ -38,7 +38,7 @@
 					</div>
 					<MkAd :prefer="['horizontal', 'horizontal-big']"/>
 					<MkContainer :max-height="300" :foldable="true" class="other">
-						<template #header><i class="fas fa-clock"></i> {{ $ts.recentPosts }}</template>
+						<template #header><i class="fas fa-clock"></i> {{ i18n.ts.recentPosts }}</template>
 						<MkPagination v-slot="{items}" :pagination="otherPostsPagination">
 							<div class="sdrarzaf">
 								<MkGalleryPostPreview v-for="post in items" :key="post.id" :post="post" class="post"/>
diff --git a/packages/client/src/pages/instance-info.vue b/packages/client/src/pages/instance-info.vue
index d4d338b12..eaa9b2b31 100644
--- a/packages/client/src/pages/instance-info.vue
+++ b/packages/client/src/pages/instance-info.vue
@@ -5,51 +5,51 @@
 		<div v-if="tab === 'overview'" class="_formRoot">
 			<div class="fnfelxur">
 				<img :src="instance.iconUrl || instance.faviconUrl" alt="" class="icon"/>
-				<span class="name">{{ instance.name || `(${$ts.unknown})` }}</span>
+				<span class="name">{{ instance.name || `(${i18n.ts.unknown})` }}</span>
 			</div>
 			<MkKeyValue :copy="host" oneline style="margin: 1em 0;">
 				<template #key>Host</template>
 				<template #value><span class="_monospace"><MkLink :url="`https://${host}`">{{ host }}</MkLink></span></template>
 			</MkKeyValue>
 			<MkKeyValue oneline style="margin: 1em 0;">
-				<template #key>{{ $ts.software }}</template>
-				<template #value><span class="_monospace">{{ instance.softwareName || `(${$ts.unknown})` }} / {{ instance.softwareVersion || `(${$ts.unknown})` }}</span></template>
+				<template #key>{{ i18n.ts.software }}</template>
+				<template #value><span class="_monospace">{{ instance.softwareName || `(${i18n.ts.unknown})` }} / {{ instance.softwareVersion || `(${i18n.ts.unknown})` }}</span></template>
 			</MkKeyValue>
 			<MkKeyValue oneline style="margin: 1em 0;">
-				<template #key>{{ $ts.administrator }}</template>
-				<template #value>{{ instance.maintainerName || `(${$ts.unknown})` }} ({{ instance.maintainerEmail || `(${$ts.unknown})` }})</template>
+				<template #key>{{ i18n.ts.administrator }}</template>
+				<template #value>{{ instance.maintainerName || `(${i18n.ts.unknown})` }} ({{ instance.maintainerEmail || `(${i18n.ts.unknown})` }})</template>
 			</MkKeyValue>
 			<MkKeyValue>
-				<template #key>{{ $ts.description }}</template>
+				<template #key>{{ i18n.ts.description }}</template>
 				<template #value>{{ instance.description }}</template>
 			</MkKeyValue>
 
 			<FormSection v-if="iAmModerator">
 				<template #label>Moderation</template>
-				<FormSwitch v-model="suspended" class="_formBlock" @update:modelValue="toggleSuspend">{{ $ts.stopActivityDelivery }}</FormSwitch>
-				<FormSwitch v-model="isBlocked" class="_formBlock" @update:modelValue="toggleBlock">{{ $ts.blockThisInstance }}</FormSwitch>
+				<FormSwitch v-model="suspended" class="_formBlock" @update:modelValue="toggleSuspend">{{ i18n.ts.stopActivityDelivery }}</FormSwitch>
+				<FormSwitch v-model="isBlocked" class="_formBlock" @update:modelValue="toggleBlock">{{ i18n.ts.blockThisInstance }}</FormSwitch>
 				<MkButton @click="refreshMetadata"><i class="fas fa-refresh"></i> Refresh metadata</MkButton>
 			</FormSection>
 
 			<FormSection>
 				<MkKeyValue oneline style="margin: 1em 0;">
-					<template #key>{{ $ts.registeredAt }}</template>
+					<template #key>{{ i18n.ts.registeredAt }}</template>
 					<template #value><MkTime mode="detail" :time="instance.caughtAt"/></template>
 				</MkKeyValue>
 				<MkKeyValue oneline style="margin: 1em 0;">
-					<template #key>{{ $ts.updatedAt }}</template>
+					<template #key>{{ i18n.ts.updatedAt }}</template>
 					<template #value><MkTime mode="detail" :time="instance.infoUpdatedAt"/></template>
 				</MkKeyValue>
 				<MkKeyValue oneline style="margin: 1em 0;">
-					<template #key>{{ $ts.latestRequestSentAt }}</template>
+					<template #key>{{ i18n.ts.latestRequestSentAt }}</template>
 					<template #value><MkTime v-if="instance.latestRequestSentAt" :time="instance.latestRequestSentAt"/><span v-else>N/A</span></template>
 				</MkKeyValue>
 				<MkKeyValue oneline style="margin: 1em 0;">
-					<template #key>{{ $ts.latestStatus }}</template>
+					<template #key>{{ i18n.ts.latestStatus }}</template>
 					<template #value>{{ instance.latestStatus ? instance.latestStatus : 'N/A' }}</template>
 				</MkKeyValue>
 				<MkKeyValue oneline style="margin: 1em 0;">
-					<template #key>{{ $ts.latestRequestReceivedAt }}</template>
+					<template #key>{{ i18n.ts.latestRequestReceivedAt }}</template>
 					<template #value><MkTime v-if="instance.latestRequestReceivedAt" :time="instance.latestRequestReceivedAt"/><span v-else>N/A</span></template>
 				</MkKeyValue>
 			</FormSection>
@@ -78,17 +78,17 @@
 			<div class="cmhjzshl">
 				<div class="selects">
 					<MkSelect v-model="chartSrc" style="margin: 0 10px 0 0; flex: 1;">
-						<option value="instance-requests">{{ $ts._instanceCharts.requests }}</option>
-						<option value="instance-users">{{ $ts._instanceCharts.users }}</option>
-						<option value="instance-users-total">{{ $ts._instanceCharts.usersTotal }}</option>
-						<option value="instance-notes">{{ $ts._instanceCharts.notes }}</option>
-						<option value="instance-notes-total">{{ $ts._instanceCharts.notesTotal }}</option>
-						<option value="instance-ff">{{ $ts._instanceCharts.ff }}</option>
-						<option value="instance-ff-total">{{ $ts._instanceCharts.ffTotal }}</option>
-						<option value="instance-drive-usage">{{ $ts._instanceCharts.cacheSize }}</option>
-						<option value="instance-drive-usage-total">{{ $ts._instanceCharts.cacheSizeTotal }}</option>
-						<option value="instance-drive-files">{{ $ts._instanceCharts.files }}</option>
-						<option value="instance-drive-files-total">{{ $ts._instanceCharts.filesTotal }}</option>
+						<option value="instance-requests">{{ i18n.ts._instanceCharts.requests }}</option>
+						<option value="instance-users">{{ i18n.ts._instanceCharts.users }}</option>
+						<option value="instance-users-total">{{ i18n.ts._instanceCharts.usersTotal }}</option>
+						<option value="instance-notes">{{ i18n.ts._instanceCharts.notes }}</option>
+						<option value="instance-notes-total">{{ i18n.ts._instanceCharts.notesTotal }}</option>
+						<option value="instance-ff">{{ i18n.ts._instanceCharts.ff }}</option>
+						<option value="instance-ff-total">{{ i18n.ts._instanceCharts.ffTotal }}</option>
+						<option value="instance-drive-usage">{{ i18n.ts._instanceCharts.cacheSize }}</option>
+						<option value="instance-drive-usage-total">{{ i18n.ts._instanceCharts.cacheSizeTotal }}</option>
+						<option value="instance-drive-files">{{ i18n.ts._instanceCharts.files }}</option>
+						<option value="instance-drive-files-total">{{ i18n.ts._instanceCharts.filesTotal }}</option>
 					</MkSelect>
 				</div>
 				<div class="charts">
diff --git a/packages/client/src/pages/mfm-cheat-sheet.vue b/packages/client/src/pages/mfm-cheat-sheet.vue
index 0b5dae996..bd8ae4e0b 100644
--- a/packages/client/src/pages/mfm-cheat-sheet.vue
+++ b/packages/client/src/pages/mfm-cheat-sheet.vue
@@ -3,11 +3,11 @@
 	<template #header><MkPageHeader/></template>
 	<MkSpacer :content-max="800">
 		<div class="mwysmxbg">
-			<div>{{ $ts._mfm.intro }}</div>
+			<div>{{ i18n.ts._mfm.intro }}</div>
 			<div class="section _block">
-				<div class="title">{{ $ts._mfm.mention }}</div>
+				<div class="title">{{ i18n.ts._mfm.mention }}</div>
 				<div class="content">
-					<p>{{ $ts._mfm.mentionDescription }}</p>
+					<p>{{ i18n.ts._mfm.mentionDescription }}</p>
 					<div class="preview">
 						<Mfm :text="preview_mention"/>
 						<MkTextarea v-model="preview_mention"><template #label>MFM</template></MkTextarea>
@@ -15,9 +15,9 @@
 				</div>
 			</div>
 			<div class="section _block">
-				<div class="title">{{ $ts._mfm.hashtag }}</div>
+				<div class="title">{{ i18n.ts._mfm.hashtag }}</div>
 				<div class="content">
-					<p>{{ $ts._mfm.hashtagDescription }}</p>
+					<p>{{ i18n.ts._mfm.hashtagDescription }}</p>
 					<div class="preview">
 						<Mfm :text="preview_hashtag"/>
 						<MkTextarea v-model="preview_hashtag"><template #label>MFM</template></MkTextarea>
@@ -25,9 +25,9 @@
 				</div>
 			</div>
 			<div class="section _block">
-				<div class="title">{{ $ts._mfm.url }}</div>
+				<div class="title">{{ i18n.ts._mfm.url }}</div>
 				<div class="content">
-					<p>{{ $ts._mfm.urlDescription }}</p>
+					<p>{{ i18n.ts._mfm.urlDescription }}</p>
 					<div class="preview">
 						<Mfm :text="preview_url"/>
 						<MkTextarea v-model="preview_url"><template #label>MFM</template></MkTextarea>
@@ -35,9 +35,9 @@
 				</div>
 			</div>
 			<div class="section _block">
-				<div class="title">{{ $ts._mfm.link }}</div>
+				<div class="title">{{ i18n.ts._mfm.link }}</div>
 				<div class="content">
-					<p>{{ $ts._mfm.linkDescription }}</p>
+					<p>{{ i18n.ts._mfm.linkDescription }}</p>
 					<div class="preview">
 						<Mfm :text="preview_link"/>
 						<MkTextarea v-model="preview_link"><template #label>MFM</template></MkTextarea>
@@ -45,9 +45,9 @@
 				</div>
 			</div>
 			<div class="section _block">
-				<div class="title">{{ $ts._mfm.emoji }}</div>
+				<div class="title">{{ i18n.ts._mfm.emoji }}</div>
 				<div class="content">
-					<p>{{ $ts._mfm.emojiDescription }}</p>
+					<p>{{ i18n.ts._mfm.emojiDescription }}</p>
 					<div class="preview">
 						<Mfm :text="preview_emoji"/>
 						<MkTextarea v-model="preview_emoji"><template #label>MFM</template></MkTextarea>
@@ -55,9 +55,9 @@
 				</div>
 			</div>
 			<div class="section _block">
-				<div class="title">{{ $ts._mfm.bold }}</div>
+				<div class="title">{{ i18n.ts._mfm.bold }}</div>
 				<div class="content">
-					<p>{{ $ts._mfm.boldDescription }}</p>
+					<p>{{ i18n.ts._mfm.boldDescription }}</p>
 					<div class="preview">
 						<Mfm :text="preview_bold"/>
 						<MkTextarea v-model="preview_bold"><template #label>MFM</template></MkTextarea>
@@ -65,9 +65,9 @@
 				</div>
 			</div>
 			<div class="section _block">
-				<div class="title">{{ $ts._mfm.small }}</div>
+				<div class="title">{{ i18n.ts._mfm.small }}</div>
 				<div class="content">
-					<p>{{ $ts._mfm.smallDescription }}</p>
+					<p>{{ i18n.ts._mfm.smallDescription }}</p>
 					<div class="preview">
 						<Mfm :text="preview_small"/>
 						<MkTextarea v-model="preview_small"><template #label>MFM</template></MkTextarea>
@@ -75,9 +75,9 @@
 				</div>
 			</div>
 			<div class="section _block">
-				<div class="title">{{ $ts._mfm.quote }}</div>
+				<div class="title">{{ i18n.ts._mfm.quote }}</div>
 				<div class="content">
-					<p>{{ $ts._mfm.quoteDescription }}</p>
+					<p>{{ i18n.ts._mfm.quoteDescription }}</p>
 					<div class="preview">
 						<Mfm :text="preview_quote"/>
 						<MkTextarea v-model="preview_quote"><template #label>MFM</template></MkTextarea>
@@ -85,9 +85,9 @@
 				</div>
 			</div>
 			<div class="section _block">
-				<div class="title">{{ $ts._mfm.center }}</div>
+				<div class="title">{{ i18n.ts._mfm.center }}</div>
 				<div class="content">
-					<p>{{ $ts._mfm.centerDescription }}</p>
+					<p>{{ i18n.ts._mfm.centerDescription }}</p>
 					<div class="preview">
 						<Mfm :text="preview_center"/>
 						<MkTextarea v-model="preview_center"><template #label>MFM</template></MkTextarea>
@@ -95,9 +95,9 @@
 				</div>
 			</div>
 			<div class="section _block">
-				<div class="title">{{ $ts._mfm.inlineCode }}</div>
+				<div class="title">{{ i18n.ts._mfm.inlineCode }}</div>
 				<div class="content">
-					<p>{{ $ts._mfm.inlineCodeDescription }}</p>
+					<p>{{ i18n.ts._mfm.inlineCodeDescription }}</p>
 					<div class="preview">
 						<Mfm :text="preview_inlineCode"/>
 						<MkTextarea v-model="preview_inlineCode"><template #label>MFM</template></MkTextarea>
@@ -105,9 +105,9 @@
 				</div>
 			</div>
 			<div class="section _block">
-				<div class="title">{{ $ts._mfm.blockCode }}</div>
+				<div class="title">{{ i18n.ts._mfm.blockCode }}</div>
 				<div class="content">
-					<p>{{ $ts._mfm.blockCodeDescription }}</p>
+					<p>{{ i18n.ts._mfm.blockCodeDescription }}</p>
 					<div class="preview">
 						<Mfm :text="preview_blockCode"/>
 						<MkTextarea v-model="preview_blockCode"><template #label>MFM</template></MkTextarea>
@@ -115,9 +115,9 @@
 				</div>
 			</div>
 			<div class="section _block">
-				<div class="title">{{ $ts._mfm.inlineMath }}</div>
+				<div class="title">{{ i18n.ts._mfm.inlineMath }}</div>
 				<div class="content">
-					<p>{{ $ts._mfm.inlineMathDescription }}</p>
+					<p>{{ i18n.ts._mfm.inlineMathDescription }}</p>
 					<div class="preview">
 						<Mfm :text="preview_inlineMath"/>
 						<MkTextarea v-model="preview_inlineMath"><template #label>MFM</template></MkTextarea>
@@ -126,9 +126,9 @@
 			</div>
 			<!-- deprecated
 		<div class="section _block">
-			<div class="title">{{ $ts._mfm.search }}</div>
+			<div class="title">{{ i18n.ts._mfm.search }}</div>
 			<div class="content">
-				<p>{{ $ts._mfm.searchDescription }}</p>
+				<p>{{ i18n.ts._mfm.searchDescription }}</p>
 				<div class="preview">
 					<Mfm :text="preview_search"/>
 					<MkTextarea v-model="preview_search"><template #label>MFM</template></MkTextarea>
@@ -137,9 +137,9 @@
 		</div>
 		-->
 			<div class="section _block">
-				<div class="title">{{ $ts._mfm.flip }}</div>
+				<div class="title">{{ i18n.ts._mfm.flip }}</div>
 				<div class="content">
-					<p>{{ $ts._mfm.flipDescription }}</p>
+					<p>{{ i18n.ts._mfm.flipDescription }}</p>
 					<div class="preview">
 						<Mfm :text="preview_flip"/>
 						<MkTextarea v-model="preview_flip"><template #label>MFM</template></MkTextarea>
@@ -147,9 +147,9 @@
 				</div>
 			</div>
 			<div class="section _block">
-				<div class="title">{{ $ts._mfm.font }}</div>
+				<div class="title">{{ i18n.ts._mfm.font }}</div>
 				<div class="content">
-					<p>{{ $ts._mfm.fontDescription }}</p>
+					<p>{{ i18n.ts._mfm.fontDescription }}</p>
 					<div class="preview">
 						<Mfm :text="preview_font"/>
 						<MkTextarea v-model="preview_font"><template #label>MFM</template></MkTextarea>
@@ -157,9 +157,9 @@
 				</div>
 			</div>
 			<div class="section _block">
-				<div class="title">{{ $ts._mfm.x2 }}</div>
+				<div class="title">{{ i18n.ts._mfm.x2 }}</div>
 				<div class="content">
-					<p>{{ $ts._mfm.x2Description }}</p>
+					<p>{{ i18n.ts._mfm.x2Description }}</p>
 					<div class="preview">
 						<Mfm :text="preview_x2"/>
 						<MkTextarea v-model="preview_x2"><template #label>MFM</template></MkTextarea>
@@ -167,9 +167,9 @@
 				</div>
 			</div>
 			<div class="section _block">
-				<div class="title">{{ $ts._mfm.x3 }}</div>
+				<div class="title">{{ i18n.ts._mfm.x3 }}</div>
 				<div class="content">
-					<p>{{ $ts._mfm.x3Description }}</p>
+					<p>{{ i18n.ts._mfm.x3Description }}</p>
 					<div class="preview">
 						<Mfm :text="preview_x3"/>
 						<MkTextarea v-model="preview_x3"><template #label>MFM</template></MkTextarea>
@@ -177,9 +177,9 @@
 				</div>
 			</div>
 			<div class="section _block">
-				<div class="title">{{ $ts._mfm.x4 }}</div>
+				<div class="title">{{ i18n.ts._mfm.x4 }}</div>
 				<div class="content">
-					<p>{{ $ts._mfm.x4Description }}</p>
+					<p>{{ i18n.ts._mfm.x4Description }}</p>
 					<div class="preview">
 						<Mfm :text="preview_x4"/>
 						<MkTextarea v-model="preview_x4"><template #label>MFM</template></MkTextarea>
@@ -187,9 +187,9 @@
 				</div>
 			</div>
 			<div class="section _block">
-				<div class="title">{{ $ts._mfm.blur }}</div>
+				<div class="title">{{ i18n.ts._mfm.blur }}</div>
 				<div class="content">
-					<p>{{ $ts._mfm.blurDescription }}</p>
+					<p>{{ i18n.ts._mfm.blurDescription }}</p>
 					<div class="preview">
 						<Mfm :text="preview_blur"/>
 						<MkTextarea v-model="preview_blur"><template #label>MFM</template></MkTextarea>
@@ -197,9 +197,9 @@
 				</div>
 			</div>
 			<div class="section _block">
-				<div class="title">{{ $ts._mfm.jelly }}</div>
+				<div class="title">{{ i18n.ts._mfm.jelly }}</div>
 				<div class="content">
-					<p>{{ $ts._mfm.jellyDescription }}</p>
+					<p>{{ i18n.ts._mfm.jellyDescription }}</p>
 					<div class="preview">
 						<Mfm :text="preview_jelly"/>
 						<MkTextarea v-model="preview_jelly"><template #label>MFM</template></MkTextarea>
@@ -207,9 +207,9 @@
 				</div>
 			</div>
 			<div class="section _block">
-				<div class="title">{{ $ts._mfm.tada }}</div>
+				<div class="title">{{ i18n.ts._mfm.tada }}</div>
 				<div class="content">
-					<p>{{ $ts._mfm.tadaDescription }}</p>
+					<p>{{ i18n.ts._mfm.tadaDescription }}</p>
 					<div class="preview">
 						<Mfm :text="preview_tada"/>
 						<MkTextarea v-model="preview_tada"><template #label>MFM</template></MkTextarea>
@@ -217,9 +217,9 @@
 				</div>
 			</div>
 			<div class="section _block">
-				<div class="title">{{ $ts._mfm.jump }}</div>
+				<div class="title">{{ i18n.ts._mfm.jump }}</div>
 				<div class="content">
-					<p>{{ $ts._mfm.jumpDescription }}</p>
+					<p>{{ i18n.ts._mfm.jumpDescription }}</p>
 					<div class="preview">
 						<Mfm :text="preview_jump"/>
 						<MkTextarea v-model="preview_jump"><template #label>MFM</template></MkTextarea>
@@ -227,9 +227,9 @@
 				</div>
 			</div>
 			<div class="section _block">
-				<div class="title">{{ $ts._mfm.bounce }}</div>
+				<div class="title">{{ i18n.ts._mfm.bounce }}</div>
 				<div class="content">
-					<p>{{ $ts._mfm.bounceDescription }}</p>
+					<p>{{ i18n.ts._mfm.bounceDescription }}</p>
 					<div class="preview">
 						<Mfm :text="preview_bounce"/>
 						<MkTextarea v-model="preview_bounce"><template #label>MFM</template></MkTextarea>
@@ -237,9 +237,9 @@
 				</div>
 			</div>
 			<div class="section _block">
-				<div class="title">{{ $ts._mfm.spin }}</div>
+				<div class="title">{{ i18n.ts._mfm.spin }}</div>
 				<div class="content">
-					<p>{{ $ts._mfm.spinDescription }}</p>
+					<p>{{ i18n.ts._mfm.spinDescription }}</p>
 					<div class="preview">
 						<Mfm :text="preview_spin"/>
 						<MkTextarea v-model="preview_spin"><template #label>MFM</template></MkTextarea>
@@ -247,9 +247,9 @@
 				</div>
 			</div>
 			<div class="section _block">
-				<div class="title">{{ $ts._mfm.shake }}</div>
+				<div class="title">{{ i18n.ts._mfm.shake }}</div>
 				<div class="content">
-					<p>{{ $ts._mfm.shakeDescription }}</p>
+					<p>{{ i18n.ts._mfm.shakeDescription }}</p>
 					<div class="preview">
 						<Mfm :text="preview_shake"/>
 						<MkTextarea v-model="preview_shake"><template #label>MFM</template></MkTextarea>
@@ -257,9 +257,9 @@
 				</div>
 			</div>
 			<div class="section _block">
-				<div class="title">{{ $ts._mfm.twitch }}</div>
+				<div class="title">{{ i18n.ts._mfm.twitch }}</div>
 				<div class="content">
-					<p>{{ $ts._mfm.twitchDescription }}</p>
+					<p>{{ i18n.ts._mfm.twitchDescription }}</p>
 					<div class="preview">
 						<Mfm :text="preview_twitch"/>
 						<MkTextarea v-model="preview_twitch"><template #label>MFM</template></MkTextarea>
@@ -267,9 +267,9 @@
 				</div>
 			</div>
 			<div class="section _block">
-				<div class="title">{{ $ts._mfm.rainbow }}</div>
+				<div class="title">{{ i18n.ts._mfm.rainbow }}</div>
 				<div class="content">
-					<p>{{ $ts._mfm.rainbowDescription }}</p>
+					<p>{{ i18n.ts._mfm.rainbowDescription }}</p>
 					<div class="preview">
 						<Mfm :text="preview_rainbow"/>
 						<MkTextarea v-model="preview_rainbow"><template #label>MFM</template></MkTextarea>
@@ -277,9 +277,9 @@
 				</div>
 			</div>
 			<div class="section _block">
-				<div class="title">{{ $ts._mfm.sparkle }}</div>
+				<div class="title">{{ i18n.ts._mfm.sparkle }}</div>
 				<div class="content">
-					<p>{{ $ts._mfm.sparkleDescription }}</p>
+					<p>{{ i18n.ts._mfm.sparkleDescription }}</p>
 					<div class="preview">
 						<Mfm :text="preview_sparkle"/>
 						<MkTextarea v-model="preview_sparkle"><span>MFM</span></MkTextarea>
@@ -287,9 +287,9 @@
 				</div>
 			</div>
 			<div class="section _block">
-				<div class="title">{{ $ts._mfm.rotate }}</div>
+				<div class="title">{{ i18n.ts._mfm.rotate }}</div>
 				<div class="content">
-					<p>{{ $ts._mfm.rotateDescription }}</p>
+					<p>{{ i18n.ts._mfm.rotateDescription }}</p>
 					<div class="preview">
 						<Mfm :text="preview_rotate"/>
 						<MkTextarea v-model="preview_rotate"><span>MFM</span></MkTextarea>
@@ -297,9 +297,9 @@
 				</div>
 			</div>
 			<div class="section _block">
-				<div class="title">{{ $ts._mfm.plain }}</div>
+				<div class="title">{{ i18n.ts._mfm.plain }}</div>
 				<div class="content">
-					<p>{{ $ts._mfm.plainDescription }}</p>
+					<p>{{ i18n.ts._mfm.plainDescription }}</p>
 					<div class="preview">
 						<Mfm :text="preview_plain"/>
 						<MkTextarea v-model="preview_plain"><span>MFM</span></MkTextarea>
diff --git a/packages/client/src/pages/my-antennas/editor.vue b/packages/client/src/pages/my-antennas/editor.vue
index 9470257c6..32027846c 100644
--- a/packages/client/src/pages/my-antennas/editor.vue
+++ b/packages/client/src/pages/my-antennas/editor.vue
@@ -2,44 +2,44 @@
 <div class="shaynizk">
 	<div class="form">
 		<MkInput v-model="name" class="_formBlock">
-			<template #label>{{ $ts.name }}</template>
+			<template #label>{{ i18n.ts.name }}</template>
 		</MkInput>
 		<MkSelect v-model="src" class="_formBlock">
-			<template #label>{{ $ts.antennaSource }}</template>
-			<option value="all">{{ $ts._antennaSources.all }}</option>
-			<!--<option value="home">{{ $ts._antennaSources.homeTimeline }}</option>-->
-			<option value="users">{{ $ts._antennaSources.users }}</option>
-			<!--<option value="list">{{ $ts._antennaSources.userList }}</option>-->
-			<!--<option value="group">{{ $ts._antennaSources.userGroup }}</option>-->
+			<template #label>{{ i18n.ts.antennaSource }}</template>
+			<option value="all">{{ i18n.ts._antennaSources.all }}</option>
+			<!--<option value="home">{{ i18n.ts._antennaSources.homeTimeline }}</option>-->
+			<option value="users">{{ i18n.ts._antennaSources.users }}</option>
+			<!--<option value="list">{{ i18n.ts._antennaSources.userList }}</option>-->
+			<!--<option value="group">{{ i18n.ts._antennaSources.userGroup }}</option>-->
 		</MkSelect>
 		<MkSelect v-if="src === 'list'" v-model="userListId" class="_formBlock">
-			<template #label>{{ $ts.userList }}</template>
+			<template #label>{{ i18n.ts.userList }}</template>
 			<option v-for="list in userLists" :key="list.id" :value="list.id">{{ list.name }}</option>
 		</MkSelect>
 		<MkSelect v-else-if="src === 'group'" v-model="userGroupId" class="_formBlock">
-			<template #label>{{ $ts.userGroup }}</template>
+			<template #label>{{ i18n.ts.userGroup }}</template>
 			<option v-for="group in userGroups" :key="group.id" :value="group.id">{{ group.name }}</option>
 		</MkSelect>
 		<MkTextarea v-else-if="src === 'users'" v-model="users" class="_formBlock">
-			<template #label>{{ $ts.users }}</template>
-			<template #caption>{{ $ts.antennaUsersDescription }} <button class="_textButton" @click="addUser">{{ $ts.addUser }}</button></template>
+			<template #label>{{ i18n.ts.users }}</template>
+			<template #caption>{{ i18n.ts.antennaUsersDescription }} <button class="_textButton" @click="addUser">{{ i18n.ts.addUser }}</button></template>
 		</MkTextarea>
-		<MkSwitch v-model="withReplies" class="_formBlock">{{ $ts.withReplies }}</MkSwitch>
+		<MkSwitch v-model="withReplies" class="_formBlock">{{ i18n.ts.withReplies }}</MkSwitch>
 		<MkTextarea v-model="keywords" class="_formBlock">
-			<template #label>{{ $ts.antennaKeywords }}</template>
-			<template #caption>{{ $ts.antennaKeywordsDescription }}</template>
+			<template #label>{{ i18n.ts.antennaKeywords }}</template>
+			<template #caption>{{ i18n.ts.antennaKeywordsDescription }}</template>
 		</MkTextarea>
 		<MkTextarea v-model="excludeKeywords" class="_formBlock">
-			<template #label>{{ $ts.antennaExcludeKeywords }}</template>
-			<template #caption>{{ $ts.antennaKeywordsDescription }}</template>
+			<template #label>{{ i18n.ts.antennaExcludeKeywords }}</template>
+			<template #caption>{{ i18n.ts.antennaKeywordsDescription }}</template>
 		</MkTextarea>
-		<MkSwitch v-model="caseSensitive" class="_formBlock">{{ $ts.caseSensitive }}</MkSwitch>
-		<MkSwitch v-model="withFile" class="_formBlock">{{ $ts.withFileAntenna }}</MkSwitch>
-		<MkSwitch v-model="notify" class="_formBlock">{{ $ts.notifyAntenna }}</MkSwitch>
+		<MkSwitch v-model="caseSensitive" class="_formBlock">{{ i18n.ts.caseSensitive }}</MkSwitch>
+		<MkSwitch v-model="withFile" class="_formBlock">{{ i18n.ts.withFileAntenna }}</MkSwitch>
+		<MkSwitch v-model="notify" class="_formBlock">{{ i18n.ts.notifyAntenna }}</MkSwitch>
 	</div>
 	<div class="actions">
-		<MkButton inline primary @click="saveAntenna()"><i class="fas fa-save"></i> {{ $ts.save }}</MkButton>
-		<MkButton v-if="antenna.id != null" inline danger @click="deleteAntenna()"><i class="fas fa-trash"></i> {{ $ts.delete }}</MkButton>
+		<MkButton inline primary @click="saveAntenna()"><i class="fas fa-save"></i> {{ i18n.ts.save }}</MkButton>
+		<MkButton v-if="antenna.id != null" inline danger @click="deleteAntenna()"><i class="fas fa-trash"></i> {{ i18n.ts.delete }}</MkButton>
 	</div>
 </div>
 </template>
diff --git a/packages/client/src/pages/my-clips/index.vue b/packages/client/src/pages/my-clips/index.vue
index ac5a3578f..5e5454b72 100644
--- a/packages/client/src/pages/my-clips/index.vue
+++ b/packages/client/src/pages/my-clips/index.vue
@@ -1,17 +1,19 @@
-<template><MkStickyContainer>
+<template>
+<MkStickyContainer>
 	<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
-		<MkSpacer :content-max="700">
-	<div class="qtcaoidl">
-		<MkButton primary class="add" @click="create"><i class="fas fa-plus"></i> {{ $ts.add }}</MkButton>
+	<MkSpacer :content-max="700">
+		<div class="qtcaoidl">
+			<MkButton primary class="add" @click="create"><i class="fas fa-plus"></i> {{ i18n.ts.add }}</MkButton>
 
-		<MkPagination v-slot="{items}" ref="pagingComponent" :pagination="pagination" class="list">
-			<MkA v-for="item in items" :key="item.id" :to="`/clips/${item.id}`" class="item _panel _gap">
-				<b>{{ item.name }}</b>
-				<div v-if="item.description" class="description">{{ item.description }}</div>
-			</MkA>
-		</MkPagination>
-	</div>
-</MkSpacer></MkStickyContainer>
+			<MkPagination v-slot="{items}" ref="pagingComponent" :pagination="pagination" class="list">
+				<MkA v-for="item in items" :key="item.id" :to="`/clips/${item.id}`" class="item _panel _gap">
+					<b>{{ item.name }}</b>
+					<div v-if="item.description" class="description">{{ item.description }}</div>
+				</MkA>
+			</MkPagination>
+		</div>
+	</MkSpacer>
+</MkStickyContainer>
 </template>
 
 <script lang="ts" setup>
diff --git a/packages/client/src/pages/my-lists/index.vue b/packages/client/src/pages/my-lists/index.vue
index 03b638151..144cba684 100644
--- a/packages/client/src/pages/my-lists/index.vue
+++ b/packages/client/src/pages/my-lists/index.vue
@@ -1,17 +1,19 @@
-<template><MkStickyContainer>
+<template>
+<MkStickyContainer>
 	<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
-		<MkSpacer :content-max="700">
-	<div class="qkcjvfiv">
-		<MkButton primary class="add" @click="create"><i class="fas fa-plus"></i> {{ $ts.createList }}</MkButton>
+	<MkSpacer :content-max="700">
+		<div class="qkcjvfiv">
+			<MkButton primary class="add" @click="create"><i class="fas fa-plus"></i> {{ i18n.ts.createList }}</MkButton>
 
-		<MkPagination v-slot="{items}" ref="pagingComponent" :pagination="pagination" class="lists _content">
-			<MkA v-for="list in items" :key="list.id" class="list _panel" :to="`/my/lists/${ list.id }`">
-				<div class="name">{{ list.name }}</div>
-				<MkAvatars :user-ids="list.userIds"/>
-			</MkA>
-		</MkPagination>
-	</div>
-</MkSpacer></MkStickyContainer>
+			<MkPagination v-slot="{items}" ref="pagingComponent" :pagination="pagination" class="lists _content">
+				<MkA v-for="list in items" :key="list.id" class="list _panel" :to="`/my/lists/${ list.id }`">
+					<div class="name">{{ list.name }}</div>
+					<MkAvatars :user-ids="list.userIds"/>
+				</MkA>
+			</MkPagination>
+		</div>
+	</MkSpacer>
+</MkStickyContainer>
 </template>
 
 <script lang="ts" setup>
diff --git a/packages/client/src/pages/my-lists/list.vue b/packages/client/src/pages/my-lists/list.vue
index 892878ae8..8b23c667d 100644
--- a/packages/client/src/pages/my-lists/list.vue
+++ b/packages/client/src/pages/my-lists/list.vue
@@ -1,42 +1,44 @@
-<template><MkStickyContainer>
+<template>
+<MkStickyContainer>
 	<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
-		<MkSpacer :content-max="700">
-	<div class="mk-list-page">
-		<transition :name="$store.state.animation ? 'zoom' : ''" mode="out-in">
-			<div v-if="list" class="_section">
-				<div class="_content">
-					<MkButton inline @click="addUser()">{{ $ts.addUser }}</MkButton>
-					<MkButton inline @click="renameList()">{{ $ts.rename }}</MkButton>
-					<MkButton inline @click="deleteList()">{{ $ts.delete }}</MkButton>
+	<MkSpacer :content-max="700">
+		<div class="mk-list-page">
+			<transition :name="$store.state.animation ? 'zoom' : ''" mode="out-in">
+				<div v-if="list" class="_section">
+					<div class="_content">
+						<MkButton inline @click="addUser()">{{ i18n.ts.addUser }}</MkButton>
+						<MkButton inline @click="renameList()">{{ i18n.ts.rename }}</MkButton>
+						<MkButton inline @click="deleteList()">{{ i18n.ts.delete }}</MkButton>
+					</div>
 				</div>
-			</div>
-		</transition>
+			</transition>
 
-		<transition :name="$store.state.animation ? 'zoom' : ''" mode="out-in">
-			<div v-if="list" class="_section members _gap">
-				<div class="_title">{{ $ts.members }}</div>
-				<div class="_content">
-					<div class="users">
-						<div v-for="user in users" :key="user.id" class="user _panel">
-							<MkAvatar :user="user" class="avatar" :show-indicator="true"/>
-							<div class="body">
-								<MkUserName :user="user" class="name"/>
-								<MkAcct :user="user" class="acct"/>
-							</div>
-							<div class="action">
-								<button class="_button" @click="removeUser(user)"><i class="fas fa-times"></i></button>
+			<transition :name="$store.state.animation ? 'zoom' : ''" mode="out-in">
+				<div v-if="list" class="_section members _gap">
+					<div class="_title">{{ i18n.ts.members }}</div>
+					<div class="_content">
+						<div class="users">
+							<div v-for="user in users" :key="user.id" class="user _panel">
+								<MkAvatar :user="user" class="avatar" :show-indicator="true"/>
+								<div class="body">
+									<MkUserName :user="user" class="name"/>
+									<MkAcct :user="user" class="acct"/>
+								</div>
+								<div class="action">
+									<button class="_button" @click="removeUser(user)"><i class="fas fa-times"></i></button>
+								</div>
 							</div>
 						</div>
 					</div>
 				</div>
-			</div>
-		</transition>
-	</div>
-</MkSpacer></MkStickyContainer>
+			</transition>
+		</div>
+	</MkSpacer>
+</MkStickyContainer>
 </template>
 
 <script lang="ts" setup>
-import { computed, defineComponent, watch } from 'vue';
+import { computed, watch } from 'vue';
 import MkButton from '@/components/ui/button.vue';
 import * as os from '@/os';
 import { mainRouter } from '@/router';
diff --git a/packages/client/src/pages/not-found.vue b/packages/client/src/pages/not-found.vue
index a819cce96..253ecdb23 100644
--- a/packages/client/src/pages/not-found.vue
+++ b/packages/client/src/pages/not-found.vue
@@ -2,7 +2,7 @@
 <div class="ipledcug">
 	<div class="_fullinfo">
 		<img src="https://xn--931a.moe/assets/not-found.jpg" class="_ghost"/>
-		<div>{{ $ts.notFoundDescription }}</div>
+		<div>{{ i18n.ts.notFoundDescription }}</div>
 	</div>
 </div>
 </template>
diff --git a/packages/client/src/pages/note.vue b/packages/client/src/pages/note.vue
index 5e153482d..240a0d4eb 100644
--- a/packages/client/src/pages/note.vue
+++ b/packages/client/src/pages/note.vue
@@ -16,7 +16,7 @@
 							<XNoteDetailed :key="note.id" v-model:note="note" class="note"/>
 						</div>
 						<div v-if="clips && clips.length > 0" class="_content clips _gap">
-							<div class="title">{{ $ts.clip }}</div>
+							<div class="title">{{ i18n.ts.clip }}</div>
 							<MkA v-for="item in clips" :key="item.id" :to="`/clips/${item.id}`" class="item _panel _gap">
 								<b>{{ item.name }}</b>
 								<div v-if="item.description" class="description">{{ item.description }}</div>
diff --git a/packages/client/src/pages/page.vue b/packages/client/src/pages/page.vue
index c60b7069e..02fd61f58 100644
--- a/packages/client/src/pages/page.vue
+++ b/packages/client/src/pages/page.vue
@@ -18,12 +18,12 @@
 					</div>
 					<div class="actions">
 						<div class="like">
-							<MkButton v-if="page.isLiked" v-tooltip="$ts._pages.unlike" class="button" primary @click="unlike()"><i class="fas fa-heart"></i><span v-if="page.likedCount > 0" class="count">{{ page.likedCount }}</span></MkButton>
-							<MkButton v-else v-tooltip="$ts._pages.like" class="button" @click="like()"><i class="far fa-heart"></i><span v-if="page.likedCount > 0" class="count">{{ page.likedCount }}</span></MkButton>
+							<MkButton v-if="page.isLiked" v-tooltip="i18n.ts._pages.unlike" class="button" primary @click="unlike()"><i class="fas fa-heart"></i><span v-if="page.likedCount > 0" class="count">{{ page.likedCount }}</span></MkButton>
+							<MkButton v-else v-tooltip="i18n.ts._pages.like" class="button" @click="like()"><i class="far fa-heart"></i><span v-if="page.likedCount > 0" class="count">{{ page.likedCount }}</span></MkButton>
 						</div>
 						<div class="other">
-							<button v-tooltip="$ts.shareWithNote" v-click-anime class="_button" @click="shareWithNote"><i class="fas fa-retweet fa-fw"></i></button>
-							<button v-tooltip="$ts.share" v-click-anime class="_button" @click="share"><i class="fas fa-share-alt fa-fw"></i></button>
+							<button v-tooltip="i18n.ts.shareWithNote" v-click-anime class="_button" @click="shareWithNote"><i class="fas fa-retweet fa-fw"></i></button>
+							<button v-tooltip="i18n.ts.share" v-click-anime class="_button" @click="share"><i class="fas fa-share-alt fa-fw"></i></button>
 						</div>
 					</div>
 					<div class="user">
@@ -35,21 +35,21 @@
 						<MkFollowButton v-if="!$i || $i.id != page.user.id" :user="page.user" :inline="true" :transparent="false" :full="true" large class="koudoku"/>
 					</div>
 					<div class="links">
-						<MkA :to="`/@${username}/pages/${pageName}/view-source`" class="link">{{ $ts._pages.viewSource }}</MkA>
+						<MkA :to="`/@${username}/pages/${pageName}/view-source`" class="link">{{ i18n.ts._pages.viewSource }}</MkA>
 						<template v-if="$i && $i.id === page.userId">
-							<MkA :to="`/pages/edit/${page.id}`" class="link">{{ $ts._pages.editThisPage }}</MkA>
-							<button v-if="$i.pinnedPageId === page.id" class="link _textButton" @click="pin(false)">{{ $ts.unpin }}</button>
-							<button v-else class="link _textButton" @click="pin(true)">{{ $ts.pin }}</button>
+							<MkA :to="`/pages/edit/${page.id}`" class="link">{{ i18n.ts._pages.editThisPage }}</MkA>
+							<button v-if="$i.pinnedPageId === page.id" class="link _textButton" @click="pin(false)">{{ i18n.ts.unpin }}</button>
+							<button v-else class="link _textButton" @click="pin(true)">{{ i18n.ts.pin }}</button>
 						</template>
 					</div>
 				</div>
 				<div class="footer">
-					<div><i class="far fa-clock"></i> {{ $ts.createdAt }}: <MkTime :time="page.createdAt" mode="detail"/></div>
-					<div v-if="page.createdAt != page.updatedAt"><i class="far fa-clock"></i> {{ $ts.updatedAt }}: <MkTime :time="page.updatedAt" mode="detail"/></div>
+					<div><i class="far fa-clock"></i> {{ i18n.ts.createdAt }}: <MkTime :time="page.createdAt" mode="detail"/></div>
+					<div v-if="page.createdAt != page.updatedAt"><i class="far fa-clock"></i> {{ i18n.ts.updatedAt }}: <MkTime :time="page.updatedAt" mode="detail"/></div>
 				</div>
 				<MkAd :prefer="['horizontal', 'horizontal-big']"/>
 				<MkContainer :max-height="300" :foldable="true" class="other">
-					<template #header><i class="fas fa-clock"></i> {{ $ts.recentPosts }}</template>
+					<template #header><i class="fas fa-clock"></i> {{ i18n.ts.recentPosts }}</template>
 					<MkPagination v-slot="{items}" :pagination="otherPostsPagination">
 						<MkPagePreview v-for="page in items" :key="page.id" :page="page" class="_gap"/>
 					</MkPagination>
diff --git a/packages/client/src/pages/registry.keys.vue b/packages/client/src/pages/registry.keys.vue
index e58077905..ae500386d 100644
--- a/packages/client/src/pages/registry.keys.vue
+++ b/packages/client/src/pages/registry.keys.vue
@@ -4,11 +4,11 @@
 	<MkSpacer :content-max="600" :margin-min="16">
 		<FormSplit>
 			<MkKeyValue class="_formBlock">
-				<template #key>{{ $ts._registry.domain }}</template>
-				<template #value>{{ $ts.system }}</template>
+				<template #key>{{ i18n.ts._registry.domain }}</template>
+				<template #value>{{ i18n.ts.system }}</template>
 			</MkKeyValue>
 			<MkKeyValue class="_formBlock">
-				<template #key>{{ $ts._registry.scope }}</template>
+				<template #key>{{ i18n.ts._registry.scope }}</template>
 				<template #value>{{ scope.join('/') }}</template>
 			</MkKeyValue>
 		</FormSplit>
diff --git a/packages/client/src/pages/registry.value.vue b/packages/client/src/pages/registry.value.vue
index 9deb31e4a..562f9df88 100644
--- a/packages/client/src/pages/registry.value.vue
+++ b/packages/client/src/pages/registry.value.vue
@@ -2,36 +2,36 @@
 <MkStickyContainer>
 	<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
 	<MkSpacer :content-max="600" :margin-min="16">
-		<FormInfo warn>{{ $ts.editTheseSettingsMayBreakAccount }}</FormInfo>
+		<FormInfo warn>{{ i18n.ts.editTheseSettingsMayBreakAccount }}</FormInfo>
 
 		<template v-if="value">
 			<FormSplit>
 				<MkKeyValue class="_formBlock">
-					<template #key>{{ $ts._registry.domain }}</template>
-					<template #value>{{ $ts.system }}</template>
+					<template #key>{{ i18n.ts._registry.domain }}</template>
+					<template #value>{{ i18n.ts.system }}</template>
 				</MkKeyValue>
 				<MkKeyValue class="_formBlock">
-					<template #key>{{ $ts._registry.scope }}</template>
+					<template #key>{{ i18n.ts._registry.scope }}</template>
 					<template #value>{{ scope.join('/') }}</template>
 				</MkKeyValue>
 				<MkKeyValue class="_formBlock">
-					<template #key>{{ $ts._registry.key }}</template>
+					<template #key>{{ i18n.ts._registry.key }}</template>
 					<template #value>{{ key }}</template>
 				</MkKeyValue>
 			</FormSplit>
 			
 			<FormTextarea v-model="valueForEditor" tall class="_formBlock _monospace">
-				<template #label>{{ $ts.value }} (JSON)</template>
+				<template #label>{{ i18n.ts.value }} (JSON)</template>
 			</FormTextarea>
 
-			<MkButton class="_formBlock" primary @click="save"><i class="fas fa-save"></i> {{ $ts.save }}</MkButton>
+			<MkButton class="_formBlock" primary @click="save"><i class="fas fa-save"></i> {{ i18n.ts.save }}</MkButton>
 
 			<MkKeyValue class="_formBlock">
-				<template #key>{{ $ts.updatedAt }}</template>
+				<template #key>{{ i18n.ts.updatedAt }}</template>
 				<template #value><MkTime :time="value.updatedAt" mode="detail"/></template>
 			</MkKeyValue>
 
-			<MkButton danger @click="del"><i class="fas fa-trash"></i> {{ $ts.delete }}</MkButton>
+			<MkButton danger @click="del"><i class="fas fa-trash"></i> {{ i18n.ts.delete }}</MkButton>
 		</template>
 	</MkSpacer>
 </MkStickyContainer>
diff --git a/packages/client/src/pages/settings/email.vue b/packages/client/src/pages/settings/email.vue
index 9d2afd6a6..1dae233a0 100644
--- a/packages/client/src/pages/settings/email.vue
+++ b/packages/client/src/pages/settings/email.vue
@@ -1,39 +1,39 @@
 <template>
 <div class="_formRoot">
 	<FormSection>
-		<template #label>{{ $ts.emailAddress }}</template>
+		<template #label>{{ i18n.ts.emailAddress }}</template>
 		<FormInput v-model="emailAddress" type="email" manual-save>
 			<template #prefix><i class="fas fa-envelope"></i></template>
-			<template v-if="$i.email && !$i.emailVerified" #caption>{{ $ts.verificationEmailSent }}</template>
-			<template v-else-if="emailAddress === $i.email && $i.emailVerified" #caption><i class="fas fa-check" style="color: var(--success);"></i> {{ $ts.emailVerified }}</template>
+			<template v-if="$i.email && !$i.emailVerified" #caption>{{ i18n.ts.verificationEmailSent }}</template>
+			<template v-else-if="emailAddress === $i.email && $i.emailVerified" #caption><i class="fas fa-check" style="color: var(--success);"></i> {{ i18n.ts.emailVerified }}</template>
 		</FormInput>
 	</FormSection>
 
 	<FormSection>
 		<FormSwitch :model-value="$i.receiveAnnouncementEmail" @update:modelValue="onChangeReceiveAnnouncementEmail">
-			{{ $ts.receiveAnnouncementFromInstance }}
+			{{ i18n.ts.receiveAnnouncementFromInstance }}
 		</FormSwitch>
 	</FormSection>
 
 	<FormSection>
-		<template #label>{{ $ts.emailNotification }}</template>
+		<template #label>{{ i18n.ts.emailNotification }}</template>
 		<FormSwitch v-model="emailNotification_mention" class="_formBlock">
-			{{ $ts._notification._types.mention }}
+			{{ i18n.ts._notification._types.mention }}
 		</FormSwitch>
 		<FormSwitch v-model="emailNotification_reply" class="_formBlock">
-			{{ $ts._notification._types.reply }}
+			{{ i18n.ts._notification._types.reply }}
 		</FormSwitch>
 		<FormSwitch v-model="emailNotification_quote" class="_formBlock">
-			{{ $ts._notification._types.quote }}
+			{{ i18n.ts._notification._types.quote }}
 		</FormSwitch>
 		<FormSwitch v-model="emailNotification_follow" class="_formBlock">
-			{{ $ts._notification._types.follow }}
+			{{ i18n.ts._notification._types.follow }}
 		</FormSwitch>
 		<FormSwitch v-model="emailNotification_receiveFollowRequest" class="_formBlock">
-			{{ $ts._notification._types.receiveFollowRequest }}
+			{{ i18n.ts._notification._types.receiveFollowRequest }}
 		</FormSwitch>
 		<FormSwitch v-model="emailNotification_groupInvited" class="_formBlock">
-			{{ $ts._notification._types.groupInvited }}
+			{{ i18n.ts._notification._types.groupInvited }}
 		</FormSwitch>
 	</FormSection>
 </div>
diff --git a/packages/client/src/pages/settings/import-export.vue b/packages/client/src/pages/settings/import-export.vue
index d48dab9f8..13c2b7fc8 100644
--- a/packages/client/src/pages/settings/import-export.vue
+++ b/packages/client/src/pages/settings/import-export.vue
@@ -1,69 +1,69 @@
 <template>
 <div class="_formRoot">
 	<FormSection>
-		<template #label>{{ $ts._exportOrImport.allNotes }}</template>
+		<template #label>{{ i18n.ts._exportOrImport.allNotes }}</template>
 		<FormFolder>
-			<template #label>{{ $ts.export }}</template>
+			<template #label>{{ i18n.ts.export }}</template>
 			<template #icon><i class="fas fa-download"></i></template>
-			<MkButton primary :class="$style.button" inline @click="exportNotes()"><i class="fas fa-download"></i> {{ $ts.export }}</MkButton>
+			<MkButton primary :class="$style.button" inline @click="exportNotes()"><i class="fas fa-download"></i> {{ i18n.ts.export }}</MkButton>
 		</FormFolder>
 	</FormSection>
 	<FormSection>
-		<template #label>{{ $ts._exportOrImport.followingList }}</template>
+		<template #label>{{ i18n.ts._exportOrImport.followingList }}</template>
 		<FormFolder class="_formBlock">
-			<template #label>{{ $ts.export }}</template>
+			<template #label>{{ i18n.ts.export }}</template>
 			<template #icon><i class="fas fa-download"></i></template>
 			<FormSwitch v-model="excludeMutingUsers" class="_formBlock">
-				{{ $ts._exportOrImport.excludeMutingUsers }}
+				{{ i18n.ts._exportOrImport.excludeMutingUsers }}
 			</FormSwitch>
 			<FormSwitch v-model="excludeInactiveUsers" class="_formBlock">
-				{{ $ts._exportOrImport.excludeInactiveUsers }}
+				{{ i18n.ts._exportOrImport.excludeInactiveUsers }}
 			</FormSwitch>
-			<MkButton primary :class="$style.button" inline @click="exportFollowing()"><i class="fas fa-download"></i> {{ $ts.export }}</MkButton>
+			<MkButton primary :class="$style.button" inline @click="exportFollowing()"><i class="fas fa-download"></i> {{ i18n.ts.export }}</MkButton>
 		</FormFolder>
 		<FormFolder class="_formBlock">
-			<template #label>{{ $ts.import }}</template>
+			<template #label>{{ i18n.ts.import }}</template>
 			<template #icon><i class="fas fa-upload"></i></template>
-			<MkButton primary :class="$style.button" inline @click="importFollowing($event)"><i class="fas fa-upload"></i> {{ $ts.import }}</MkButton>
+			<MkButton primary :class="$style.button" inline @click="importFollowing($event)"><i class="fas fa-upload"></i> {{ i18n.ts.import }}</MkButton>
 		</FormFolder>
 	</FormSection>
 	<FormSection>
-		<template #label>{{ $ts._exportOrImport.userLists }}</template>
+		<template #label>{{ i18n.ts._exportOrImport.userLists }}</template>
 		<FormFolder class="_formBlock">
-			<template #label>{{ $ts.export }}</template>
+			<template #label>{{ i18n.ts.export }}</template>
 			<template #icon><i class="fas fa-download"></i></template>
-			<MkButton primary :class="$style.button" inline @click="exportUserLists()"><i class="fas fa-download"></i> {{ $ts.export }}</MkButton>
+			<MkButton primary :class="$style.button" inline @click="exportUserLists()"><i class="fas fa-download"></i> {{ i18n.ts.export }}</MkButton>
 		</FormFolder>
 		<FormFolder class="_formBlock">
-			<template #label>{{ $ts.import }}</template>
+			<template #label>{{ i18n.ts.import }}</template>
 			<template #icon><i class="fas fa-upload"></i></template>
-			<MkButton primary :class="$style.button" inline @click="importUserLists($event)"><i class="fas fa-upload"></i> {{ $ts.import }}</MkButton>
+			<MkButton primary :class="$style.button" inline @click="importUserLists($event)"><i class="fas fa-upload"></i> {{ i18n.ts.import }}</MkButton>
 		</FormFolder>
 	</FormSection>
 	<FormSection>
-		<template #label>{{ $ts._exportOrImport.muteList }}</template>
+		<template #label>{{ i18n.ts._exportOrImport.muteList }}</template>
 		<FormFolder class="_formBlock">
-			<template #label>{{ $ts.export }}</template>
+			<template #label>{{ i18n.ts.export }}</template>
 			<template #icon><i class="fas fa-download"></i></template>
-			<MkButton primary :class="$style.button" inline @click="exportMuting()"><i class="fas fa-download"></i> {{ $ts.export }}</MkButton>
+			<MkButton primary :class="$style.button" inline @click="exportMuting()"><i class="fas fa-download"></i> {{ i18n.ts.export }}</MkButton>
 		</FormFolder>
 		<FormFolder class="_formBlock">
-			<template #label>{{ $ts.import }}</template>
+			<template #label>{{ i18n.ts.import }}</template>
 			<template #icon><i class="fas fa-upload"></i></template>
-			<MkButton primary :class="$style.button" inline @click="importMuting($event)"><i class="fas fa-upload"></i> {{ $ts.import }}</MkButton>
+			<MkButton primary :class="$style.button" inline @click="importMuting($event)"><i class="fas fa-upload"></i> {{ i18n.ts.import }}</MkButton>
 		</FormFolder>
 	</FormSection>
 	<FormSection>
-		<template #label>{{ $ts._exportOrImport.blockingList }}</template>
+		<template #label>{{ i18n.ts._exportOrImport.blockingList }}</template>
 		<FormFolder class="_formBlock">
-			<template #label>{{ $ts.export }}</template>
+			<template #label>{{ i18n.ts.export }}</template>
 			<template #icon><i class="fas fa-download"></i></template>
-			<MkButton primary :class="$style.button" inline @click="exportBlocking()"><i class="fas fa-download"></i> {{ $ts.export }}</MkButton>
+			<MkButton primary :class="$style.button" inline @click="exportBlocking()"><i class="fas fa-download"></i> {{ i18n.ts.export }}</MkButton>
 		</FormFolder>
 		<FormFolder class="_formBlock">
-			<template #label>{{ $ts.import }}</template>
+			<template #label>{{ i18n.ts.import }}</template>
 			<template #icon><i class="fas fa-upload"></i></template>
-			<MkButton primary :class="$style.button" inline @click="importBlocking($event)"><i class="fas fa-upload"></i> {{ $ts.import }}</MkButton>
+			<MkButton primary :class="$style.button" inline @click="importBlocking($event)"><i class="fas fa-upload"></i> {{ i18n.ts.import }}</MkButton>
 		</FormFolder>
 	</FormSection>
 </div>
diff --git a/packages/client/src/pages/settings/index.vue b/packages/client/src/pages/settings/index.vue
index 8964333b3..0ca91d0eb 100644
--- a/packages/client/src/pages/settings/index.vue
+++ b/packages/client/src/pages/settings/index.vue
@@ -6,7 +6,7 @@
 			<div class="body">
 				<div v-if="!narrow || currentPage?.route.name == null" class="nav">
 					<div class="baaadecd">
-						<MkInfo v-if="emailNotConfigured" warn class="info">{{ $ts.emailNotConfiguredWarning }} <MkA to="/settings/email" class="_link">{{ $ts.configure }}</MkA></MkInfo>
+						<MkInfo v-if="emailNotConfigured" warn class="info">{{ i18n.ts.emailNotConfiguredWarning }} <MkA to="/settings/email" class="_link">{{ i18n.ts.configure }}</MkA></MkInfo>
 						<MkSuperMenu :def="menuDef" :grid="currentPage?.route.name == null"></MkSuperMenu>
 					</div>
 				</div>
diff --git a/packages/client/src/pages/settings/mute-block.vue b/packages/client/src/pages/settings/mute-block.vue
index 397a0c815..495d52d4d 100644
--- a/packages/client/src/pages/settings/mute-block.vue
+++ b/packages/client/src/pages/settings/mute-block.vue
@@ -1,12 +1,12 @@
 <template>
 <div class="_formRoot">
 	<MkTab v-model="tab" style="margin-bottom: var(--margin);">
-		<option value="mute">{{ $ts.mutedUsers }}</option>
-		<option value="block">{{ $ts.blockedUsers }}</option>
+		<option value="mute">{{ i18n.ts.mutedUsers }}</option>
+		<option value="block">{{ i18n.ts.blockedUsers }}</option>
 	</MkTab>
 	<div v-if="tab === 'mute'">
 		<MkPagination :pagination="mutingPagination" class="muting">
-			<template #empty><FormInfo>{{ $ts.noUsers }}</FormInfo></template>
+			<template #empty><FormInfo>{{ i18n.ts.noUsers }}</FormInfo></template>
 			<template #default="{items}">
 				<FormLink v-for="mute in items" :key="mute.id" :to="userPage(mute.mutee)">
 					<MkAcct :user="mute.mutee"/>
@@ -16,7 +16,7 @@
 	</div>
 	<div v-if="tab === 'block'">
 		<MkPagination :pagination="blockingPagination" class="blocking">
-			<template #empty><FormInfo>{{ $ts.noUsers }}</FormInfo></template>
+			<template #empty><FormInfo>{{ i18n.ts.noUsers }}</FormInfo></template>
 			<template #default="{items}">
 				<FormLink v-for="block in items" :key="block.id" :to="userPage(block.blockee)">
 					<MkAcct :user="block.blockee"/>
diff --git a/packages/client/src/pages/settings/privacy.vue b/packages/client/src/pages/settings/privacy.vue
index be9e34cdf..45a0358a9 100644
--- a/packages/client/src/pages/settings/privacy.vue
+++ b/packages/client/src/pages/settings/privacy.vue
@@ -1,54 +1,54 @@
 <template>
 <div class="_formRoot">
-	<FormSwitch v-model="isLocked" class="_formBlock" @update:modelValue="save()">{{ $ts.makeFollowManuallyApprove }}<template #caption>{{ $ts.lockedAccountInfo }}</template></FormSwitch>
-	<FormSwitch v-if="isLocked" v-model="autoAcceptFollowed" class="_formBlock" @update:modelValue="save()">{{ $ts.autoAcceptFollowed }}</FormSwitch>
+	<FormSwitch v-model="isLocked" class="_formBlock" @update:modelValue="save()">{{ i18n.ts.makeFollowManuallyApprove }}<template #caption>{{ i18n.ts.lockedAccountInfo }}</template></FormSwitch>
+	<FormSwitch v-if="isLocked" v-model="autoAcceptFollowed" class="_formBlock" @update:modelValue="save()">{{ i18n.ts.autoAcceptFollowed }}</FormSwitch>
 
 	<FormSwitch v-model="publicReactions" class="_formBlock" @update:modelValue="save()">
-		{{ $ts.makeReactionsPublic }}
-		<template #caption>{{ $ts.makeReactionsPublicDescription }}</template>
+		{{ i18n.ts.makeReactionsPublic }}
+		<template #caption>{{ i18n.ts.makeReactionsPublicDescription }}</template>
 	</FormSwitch>
 		
 	<FormSelect v-model="ffVisibility" class="_formBlock" @update:modelValue="save()">
-		<template #label>{{ $ts.ffVisibility }}</template>
-		<option value="public">{{ $ts._ffVisibility.public }}</option>
-		<option value="followers">{{ $ts._ffVisibility.followers }}</option>
-		<option value="private">{{ $ts._ffVisibility.private }}</option>
-		<template #caption>{{ $ts.ffVisibilityDescription }}</template>
+		<template #label>{{ i18n.ts.ffVisibility }}</template>
+		<option value="public">{{ i18n.ts._ffVisibility.public }}</option>
+		<option value="followers">{{ i18n.ts._ffVisibility.followers }}</option>
+		<option value="private">{{ i18n.ts._ffVisibility.private }}</option>
+		<template #caption>{{ i18n.ts.ffVisibilityDescription }}</template>
 	</FormSelect>
 		
 	<FormSwitch v-model="hideOnlineStatus" class="_formBlock" @update:modelValue="save()">
-		{{ $ts.hideOnlineStatus }}
-		<template #caption>{{ $ts.hideOnlineStatusDescription }}</template>
+		{{ i18n.ts.hideOnlineStatus }}
+		<template #caption>{{ i18n.ts.hideOnlineStatusDescription }}</template>
 	</FormSwitch>
 	<FormSwitch v-model="noCrawle" class="_formBlock" @update:modelValue="save()">
-		{{ $ts.noCrawle }}
-		<template #caption>{{ $ts.noCrawleDescription }}</template>
+		{{ i18n.ts.noCrawle }}
+		<template #caption>{{ i18n.ts.noCrawleDescription }}</template>
 	</FormSwitch>
 	<FormSwitch v-model="isExplorable" class="_formBlock" @update:modelValue="save()">
-		{{ $ts.makeExplorable }}
-		<template #caption>{{ $ts.makeExplorableDescription }}</template>
+		{{ i18n.ts.makeExplorable }}
+		<template #caption>{{ i18n.ts.makeExplorableDescription }}</template>
 	</FormSwitch>
 
 	<FormSection>
-		<FormSwitch v-model="rememberNoteVisibility" class="_formBlock" @update:modelValue="save()">{{ $ts.rememberNoteVisibility }}</FormSwitch>
+		<FormSwitch v-model="rememberNoteVisibility" class="_formBlock" @update:modelValue="save()">{{ i18n.ts.rememberNoteVisibility }}</FormSwitch>
 		<FormFolder v-if="!rememberNoteVisibility" class="_formBlock">
-			<template #label>{{ $ts.defaultNoteVisibility }}</template>
-			<template v-if="defaultNoteVisibility === 'public'" #suffix>{{ $ts._visibility.public }}</template>
-			<template v-else-if="defaultNoteVisibility === 'home'" #suffix>{{ $ts._visibility.home }}</template>
-			<template v-else-if="defaultNoteVisibility === 'followers'" #suffix>{{ $ts._visibility.followers }}</template>
-			<template v-else-if="defaultNoteVisibility === 'specified'" #suffix>{{ $ts._visibility.specified }}</template>
+			<template #label>{{ i18n.ts.defaultNoteVisibility }}</template>
+			<template v-if="defaultNoteVisibility === 'public'" #suffix>{{ i18n.ts._visibility.public }}</template>
+			<template v-else-if="defaultNoteVisibility === 'home'" #suffix>{{ i18n.ts._visibility.home }}</template>
+			<template v-else-if="defaultNoteVisibility === 'followers'" #suffix>{{ i18n.ts._visibility.followers }}</template>
+			<template v-else-if="defaultNoteVisibility === 'specified'" #suffix>{{ i18n.ts._visibility.specified }}</template>
 
 			<FormSelect v-model="defaultNoteVisibility" class="_formBlock">
-				<option value="public">{{ $ts._visibility.public }}</option>
-				<option value="home">{{ $ts._visibility.home }}</option>
-				<option value="followers">{{ $ts._visibility.followers }}</option>
-				<option value="specified">{{ $ts._visibility.specified }}</option>
+				<option value="public">{{ i18n.ts._visibility.public }}</option>
+				<option value="home">{{ i18n.ts._visibility.home }}</option>
+				<option value="followers">{{ i18n.ts._visibility.followers }}</option>
+				<option value="specified">{{ i18n.ts._visibility.specified }}</option>
 			</FormSelect>
-			<FormSwitch v-model="defaultNoteLocalOnly" class="_formBlock">{{ $ts._visibility.localOnly }}</FormSwitch>
+			<FormSwitch v-model="defaultNoteLocalOnly" class="_formBlock">{{ i18n.ts._visibility.localOnly }}</FormSwitch>
 		</FormFolder>
 	</FormSection>
 
-	<FormSwitch v-model="keepCw" class="_formBlock" @update:modelValue="save()">{{ $ts.keepCw }}</FormSwitch>
+	<FormSwitch v-model="keepCw" class="_formBlock" @update:modelValue="save()">{{ i18n.ts.keepCw }}</FormSwitch>
 </div>
 </template>
 
diff --git a/packages/client/src/pages/settings/reaction.vue b/packages/client/src/pages/settings/reaction.vue
index 382e1b081..170124eac 100644
--- a/packages/client/src/pages/settings/reaction.vue
+++ b/packages/client/src/pages/settings/reaction.vue
@@ -1,7 +1,7 @@
 <template>
 <div class="_formRoot">
 	<FromSlot class="_formBlock">
-		<template #label>{{ $ts.reactionSettingDescription }}</template>
+		<template #label>{{ i18n.ts.reactionSettingDescription }}</template>
 		<div v-panel style="border-radius: 6px;">
 			<XDraggable v-model="reactions" class="zoaiodol" :item-key="item => item" animation="150" delay="100" delay-on-touch-only="true">
 				<template #item="{element}">
@@ -14,17 +14,17 @@
 				</template>
 			</XDraggable>
 		</div>
-		<template #caption>{{ $ts.reactionSettingDescription2 }} <button class="_textButton" @click="preview">{{ $ts.preview }}</button></template>
+		<template #caption>{{ i18n.ts.reactionSettingDescription2 }} <button class="_textButton" @click="preview">{{ i18n.ts.preview }}</button></template>
 	</FromSlot>
 
 	<FormRadios v-model="reactionPickerSize" class="_formBlock">
-		<template #label>{{ $ts.size }}</template>
-		<option :value="1">{{ $ts.small }}</option>
-		<option :value="2">{{ $ts.medium }}</option>
-		<option :value="3">{{ $ts.large }}</option>
+		<template #label>{{ i18n.ts.size }}</template>
+		<option :value="1">{{ i18n.ts.small }}</option>
+		<option :value="2">{{ i18n.ts.medium }}</option>
+		<option :value="3">{{ i18n.ts.large }}</option>
 	</FormRadios>
 	<FormRadios v-model="reactionPickerWidth" class="_formBlock">
-		<template #label>{{ $ts.numberOfColumn }}</template>
+		<template #label>{{ i18n.ts.numberOfColumn }}</template>
 		<option :value="1">5</option>
 		<option :value="2">6</option>
 		<option :value="3">7</option>
@@ -32,22 +32,22 @@
 		<option :value="5">9</option>
 	</FormRadios>
 	<FormRadios v-model="reactionPickerHeight" class="_formBlock">
-		<template #label>{{ $ts.height }}</template>
-		<option :value="1">{{ $ts.small }}</option>
-		<option :value="2">{{ $ts.medium }}</option>
-		<option :value="3">{{ $ts.large }}</option>
-		<option :value="4">{{ $ts.large }}+</option>
+		<template #label>{{ i18n.ts.height }}</template>
+		<option :value="1">{{ i18n.ts.small }}</option>
+		<option :value="2">{{ i18n.ts.medium }}</option>
+		<option :value="3">{{ i18n.ts.large }}</option>
+		<option :value="4">{{ i18n.ts.large }}+</option>
 	</FormRadios>
 
 	<FormSwitch v-model="reactionPickerUseDrawerForMobile" class="_formBlock">
-		{{ $ts.useDrawerReactionPickerForMobile }}
-		<template #caption>{{ $ts.needReloadToApply }}</template>
+		{{ i18n.ts.useDrawerReactionPickerForMobile }}
+		<template #caption>{{ i18n.ts.needReloadToApply }}</template>
 	</FormSwitch>
 
 	<FormSection>
 		<div style="display: flex; gap: var(--margin); flex-wrap: wrap;">
-			<FormButton inline @click="preview"><i class="fas fa-eye"></i> {{ $ts.preview }}</FormButton>
-			<FormButton inline danger @click="setDefault"><i class="fas fa-undo"></i> {{ $ts.default }}</FormButton>
+			<FormButton inline @click="preview"><i class="fas fa-eye"></i> {{ i18n.ts.preview }}</FormButton>
+			<FormButton inline danger @click="setDefault"><i class="fas fa-undo"></i> {{ i18n.ts.default }}</FormButton>
 		</div>
 	</FormSection>
 </div>
diff --git a/packages/client/src/pages/settings/theme.vue b/packages/client/src/pages/settings/theme.vue
index 667155019..201a7fa8e 100644
--- a/packages/client/src/pages/settings/theme.vue
+++ b/packages/client/src/pages/settings/theme.vue
@@ -5,8 +5,8 @@
 			<div class="toggleWrapper">
 				<input id="dn" v-model="darkMode" type="checkbox" class="dn"/>
 				<label for="dn" class="toggle">
-					<span class="before">{{ $ts.light }}</span>
-					<span class="after">{{ $ts.dark }}</span>
+					<span class="before">{{ i18n.ts.light }}</span>
+					<span class="after">{{ i18n.ts.dark }}</span>
 					<span class="toggle__handler">
 						<span class="crater crater--1"></span>
 						<span class="crater crater--2"></span>
@@ -22,28 +22,28 @@
 			</div>
 		</div>
 		<div class="sync">
-			<FormSwitch v-model="syncDeviceDarkMode">{{ $ts.syncDeviceDarkMode }}</FormSwitch>
+			<FormSwitch v-model="syncDeviceDarkMode">{{ i18n.ts.syncDeviceDarkMode }}</FormSwitch>
 		</div>
 	</div>
 
 	<div class="selects _formBlock">
 		<FormSelect v-model="lightThemeId" large class="select">
-			<template #label>{{ $ts.themeForLightMode }}</template>
+			<template #label>{{ i18n.ts.themeForLightMode }}</template>
 			<template #prefix><i class="fas fa-sun"></i></template>
-			<optgroup :label="$ts.lightThemes">
+			<optgroup :label="i18n.ts.lightThemes">
 				<option v-for="x in lightThemes" :key="x.id" :value="x.id">{{ x.name }}</option>
 			</optgroup>
-			<optgroup :label="$ts.darkThemes">
+			<optgroup :label="i18n.ts.darkThemes">
 				<option v-for="x in darkThemes" :key="x.id" :value="x.id">{{ x.name }}</option>
 			</optgroup>
 		</FormSelect>
 		<FormSelect v-model="darkThemeId" large class="select">
-			<template #label>{{ $ts.themeForDarkMode }}</template>
+			<template #label>{{ i18n.ts.themeForDarkMode }}</template>
 			<template #prefix><i class="fas fa-moon"></i></template>
-			<optgroup :label="$ts.darkThemes">
+			<optgroup :label="i18n.ts.darkThemes">
 				<option v-for="x in darkThemes" :key="x.id" :value="x.id">{{ x.name }}</option>
 			</optgroup>
-			<optgroup :label="$ts.lightThemes">
+			<optgroup :label="i18n.ts.lightThemes">
 				<option v-for="x in lightThemes" :key="x.id" :value="x.id">{{ x.name }}</option>
 			</optgroup>
 		</FormSelect>
@@ -51,15 +51,15 @@
 
 	<FormSection>
 		<div class="_formLinksGrid">
-			<FormLink to="/settings/theme/manage"><template #icon><i class="fas fa-folder-open"></i></template>{{ $ts._theme.manage }}<template #suffix>{{ themesCount }}</template></FormLink>
-			<FormLink to="https://assets.misskey.io/theme/list" external><template #icon><i class="fas fa-globe"></i></template>{{ $ts._theme.explore }}</FormLink>
-			<FormLink to="/settings/theme/install"><template #icon><i class="fas fa-download"></i></template>{{ $ts._theme.install }}</FormLink>
-			<FormLink to="/theme-editor"><template #icon><i class="fas fa-paint-roller"></i></template>{{ $ts._theme.make }}</FormLink>
+			<FormLink to="/settings/theme/manage"><template #icon><i class="fas fa-folder-open"></i></template>{{ i18n.ts._theme.manage }}<template #suffix>{{ themesCount }}</template></FormLink>
+			<FormLink to="https://assets.misskey.io/theme/list" external><template #icon><i class="fas fa-globe"></i></template>{{ i18n.ts._theme.explore }}</FormLink>
+			<FormLink to="/settings/theme/install"><template #icon><i class="fas fa-download"></i></template>{{ i18n.ts._theme.install }}</FormLink>
+			<FormLink to="/theme-editor"><template #icon><i class="fas fa-paint-roller"></i></template>{{ i18n.ts._theme.make }}</FormLink>
 		</div>
 	</FormSection>
 
-	<FormButton v-if="wallpaper == null" class="_formBlock" @click="setWallpaper">{{ $ts.setWallpaper }}</FormButton>
-	<FormButton v-else class="_formBlock" @click="wallpaper = null">{{ $ts.removeWallpaper }}</FormButton>
+	<FormButton v-if="wallpaper == null" class="_formBlock" @click="setWallpaper">{{ i18n.ts.setWallpaper }}</FormButton>
+	<FormButton v-else class="_formBlock" @click="wallpaper = null">{{ i18n.ts.removeWallpaper }}</FormButton>
 </div>
 </template>
 
diff --git a/packages/client/src/pages/share.vue b/packages/client/src/pages/share.vue
index 8984823b6..6c3ce339c 100644
--- a/packages/client/src/pages/share.vue
+++ b/packages/client/src/pages/share.vue
@@ -16,7 +16,7 @@
 				class="_panel"
 				@posted="state = 'posted'"
 			/>
-			<MkButton v-else-if="state === 'posted'" primary class="close" @click="close()">{{ $ts.close }}</MkButton>
+			<MkButton v-else-if="state === 'posted'" primary class="close" @click="close()">{{ i18n.ts.close }}</MkButton>
 		</div>
 	</section>
 </div>
@@ -25,7 +25,7 @@
 <script lang="ts" setup>
 // SPECIFICATION: https://misskey-hub.net/docs/features/share-form.html
 
-import { defineComponent } from 'vue';
+import { } from 'vue';
 import { noteVisibilities } from 'misskey-js';
 import * as Acct from 'misskey-js/built/acct';
 import * as Misskey from 'misskey-js';
diff --git a/packages/client/src/pages/timeline.tutorial.vue b/packages/client/src/pages/timeline.tutorial.vue
index 432d28c60..f69bf5810 100644
--- a/packages/client/src/pages/timeline.tutorial.vue
+++ b/packages/client/src/pages/timeline.tutorial.vue
@@ -1,52 +1,52 @@
 <template>
 <div class="_card tbkwesmv">
-	<div class="_title"><i class="fas fa-info-circle"></i> {{ $ts._tutorial.title }}</div>
+	<div class="_title"><i class="fas fa-info-circle"></i> {{ i18n.ts._tutorial.title }}</div>
 	<div v-if="tutorial === 0" class="_content">
-		<div>{{ $ts._tutorial.step1_1 }}</div>
-		<div>{{ $ts._tutorial.step1_2 }}</div>
-		<div>{{ $ts._tutorial.step1_3 }}</div>
+		<div>{{ i18n.ts._tutorial.step1_1 }}</div>
+		<div>{{ i18n.ts._tutorial.step1_2 }}</div>
+		<div>{{ i18n.ts._tutorial.step1_3 }}</div>
 	</div>
 	<div v-else-if="tutorial === 1" class="_content">
-		<div>{{ $ts._tutorial.step2_1 }}</div>
-		<div>{{ $ts._tutorial.step2_2 }}</div>
-		<MkA class="_link" to="/settings/profile">{{ $ts.editProfile }}</MkA>
+		<div>{{ i18n.ts._tutorial.step2_1 }}</div>
+		<div>{{ i18n.ts._tutorial.step2_2 }}</div>
+		<MkA class="_link" to="/settings/profile">{{ i18n.ts.editProfile }}</MkA>
 	</div>
 	<div v-else-if="tutorial === 2" class="_content">
-		<div>{{ $ts._tutorial.step3_1 }}</div>
-		<div>{{ $ts._tutorial.step3_2 }}</div>
-		<div>{{ $ts._tutorial.step3_3 }}</div>
-		<small>{{ $ts._tutorial.step3_4 }}</small>
+		<div>{{ i18n.ts._tutorial.step3_1 }}</div>
+		<div>{{ i18n.ts._tutorial.step3_2 }}</div>
+		<div>{{ i18n.ts._tutorial.step3_3 }}</div>
+		<small>{{ i18n.ts._tutorial.step3_4 }}</small>
 	</div>
 	<div v-else-if="tutorial === 3" class="_content">
-		<div>{{ $ts._tutorial.step4_1 }}</div>
-		<div>{{ $ts._tutorial.step4_2 }}</div>
+		<div>{{ i18n.ts._tutorial.step4_1 }}</div>
+		<div>{{ i18n.ts._tutorial.step4_2 }}</div>
 	</div>
 	<div v-else-if="tutorial === 4" class="_content">
-		<div>{{ $ts._tutorial.step5_1 }}</div>
-		<I18n :src="$ts._tutorial.step5_2" tag="div">
+		<div>{{ i18n.ts._tutorial.step5_1 }}</div>
+		<I18n :src="i18n.ts._tutorial.step5_2" tag="div">
 			<template #featured>
-				<MkA class="_link" to="/featured">{{ $ts.featured }}</MkA>
+				<MkA class="_link" to="/featured">{{ i18n.ts.featured }}</MkA>
 			</template>
 			<template #explore>
-				<MkA class="_link" to="/explore">{{ $ts.explore }}</MkA>
+				<MkA class="_link" to="/explore">{{ i18n.ts.explore }}</MkA>
 			</template>
 		</I18n>
-		<div>{{ $ts._tutorial.step5_3 }}</div>
-		<small>{{ $ts._tutorial.step5_4 }}</small>
+		<div>{{ i18n.ts._tutorial.step5_3 }}</div>
+		<small>{{ i18n.ts._tutorial.step5_4 }}</small>
 	</div>
 	<div v-else-if="tutorial === 5" class="_content">
-		<div>{{ $ts._tutorial.step6_1 }}</div>
-		<div>{{ $ts._tutorial.step6_2 }}</div>
-		<div>{{ $ts._tutorial.step6_3 }}</div>
+		<div>{{ i18n.ts._tutorial.step6_1 }}</div>
+		<div>{{ i18n.ts._tutorial.step6_2 }}</div>
+		<div>{{ i18n.ts._tutorial.step6_3 }}</div>
 	</div>
 	<div v-else-if="tutorial === 6" class="_content">
-		<div>{{ $ts._tutorial.step7_1 }}</div>
-		<I18n :src="$ts._tutorial.step7_2" tag="div">
+		<div>{{ i18n.ts._tutorial.step7_1 }}</div>
+		<I18n :src="i18n.ts._tutorial.step7_2" tag="div">
 			<template #help>
-				<a href="https://misskey-hub.net/help.html" target="_blank" class="_link">{{ $ts.help }}</a>
+				<a href="https://misskey-hub.net/help.html" target="_blank" class="_link">{{ i18n.ts.help }}</a>
 			</template>
 		</I18n>
-		<div>{{ $ts._tutorial.step7_3 }}</div>
+		<div>{{ i18n.ts._tutorial.step7_3 }}</div>
 	</div>
 
 	<div class="_footer navigation">
@@ -59,8 +59,8 @@
 				<i class="fas fa-chevron-right"></i>
 			</button>
 		</div>
-		<MkButton v-if="tutorial === 6" class="ok" primary @click="tutorial = -1"><i class="fas fa-check"></i> {{ $ts.gotIt }}</MkButton>
-		<MkButton v-else class="ok" primary @click="tutorial++"><i class="fas fa-check"></i> {{ $ts.next }}</MkButton>
+		<MkButton v-if="tutorial === 6" class="ok" primary @click="tutorial = -1"><i class="fas fa-check"></i> {{ i18n.ts.gotIt }}</MkButton>
+		<MkButton v-else class="ok" primary @click="tutorial++"><i class="fas fa-check"></i> {{ i18n.ts.next }}</MkButton>
 	</div>
 </div>
 </template>
@@ -69,10 +69,11 @@
 import { computed } from 'vue';
 import MkButton from '@/components/ui/button.vue';
 import { defaultStore } from '@/store';
+import { i18n } from '@/i18n';
 
 const tutorial = computed({
 	get() { return defaultStore.reactiveState.tutorial.value || 0; },
-	set(value) { defaultStore.set('tutorial', value); }
+	set(value) { defaultStore.set('tutorial', value); },
 });
 </script>
 
diff --git a/packages/client/src/pages/timeline.vue b/packages/client/src/pages/timeline.vue
index 40eb85ff4..f62ab95b5 100644
--- a/packages/client/src/pages/timeline.vue
+++ b/packages/client/src/pages/timeline.vue
@@ -6,7 +6,7 @@
 			<XTutorial v-if="$store.reactiveState.tutorial.value != -1" class="tutorial _block"/>
 			<XPostForm v-if="$store.reactiveState.showFixedPostForm.value" class="post-form _block" fixed/>
 
-			<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 _block">
 				<XTimeline
 					ref="tl" :key="src"
diff --git a/packages/client/src/pages/user-info.vue b/packages/client/src/pages/user-info.vue
index fd24ec284..9154c7348 100644
--- a/packages/client/src/pages/user-info.vue
+++ b/packages/client/src/pages/user-info.vue
@@ -57,15 +57,15 @@
 
 					<div class="_formBlock">
 						<MkKeyValue v-if="user.host" oneline style="margin: 1em 0;">
-							<template #key>{{ $ts.instanceInfo }}</template>
+							<template #key>{{ i18n.ts.instanceInfo }}</template>
 							<template #value><MkA :to="`/instance-info/${user.host}`" class="_link">{{ user.host }} <i class="fas fa-angle-right"></i></MkA></template>
 						</MkKeyValue>
 						<MkKeyValue v-else oneline style="margin: 1em 0;">
-							<template #key>{{ $ts.instanceInfo }}</template>
+							<template #key>{{ i18n.ts.instanceInfo }}</template>
 							<template #value>(Local user)</template>
 						</MkKeyValue>
 						<MkKeyValue oneline style="margin: 1em 0;">
-							<template #key>{{ $ts.updatedAt }}</template>
+							<template #key>{{ i18n.ts.updatedAt }}</template>
 							<template #value><MkTime v-if="user.lastFetchedAt" mode="detail" :time="user.lastFetchedAt"/><span v-else>N/A</span></template>
 						</MkKeyValue>
 						<MkKeyValue v-if="ap" oneline style="margin: 1em 0;">
@@ -74,7 +74,7 @@
 						</MkKeyValue>
 					</div>
 
-					<FormButton v-if="user.host != null" class="_formBlock" @click="updateRemoteUser"><i class="fas fa-sync"></i> {{ $ts.updateRemoteUser }}</FormButton>
+					<FormButton v-if="user.host != null" class="_formBlock" @click="updateRemoteUser"><i class="fas fa-sync"></i> {{ i18n.ts.updateRemoteUser }}</FormButton>
 
 					<FormFolder class="_formBlock">
 						<template #label>Raw</template>
@@ -85,13 +85,13 @@
 				</FormSection>
 			</div>
 			<div v-else-if="tab === 'moderation'" class="_formRoot">
-				<FormSwitch v-if="user.host == null && $i.isAdmin && (moderator || !user.isAdmin)" v-model="moderator" class="_formBlock" @update:modelValue="toggleModerator">{{ $ts.moderator }}</FormSwitch>
-				<FormSwitch v-model="silenced" class="_formBlock" @update:modelValue="toggleSilence">{{ $ts.silence }}</FormSwitch>
-				<FormSwitch v-model="suspended" class="_formBlock" @update:modelValue="toggleSuspend">{{ $ts.suspend }}</FormSwitch>
-				{{ $ts.reflectMayTakeTime }}
+				<FormSwitch v-if="user.host == null && $i.isAdmin && (moderator || !user.isAdmin)" v-model="moderator" class="_formBlock" @update:modelValue="toggleModerator">{{ i18n.ts.moderator }}</FormSwitch>
+				<FormSwitch v-model="silenced" class="_formBlock" @update:modelValue="toggleSilence">{{ i18n.ts.silence }}</FormSwitch>
+				<FormSwitch v-model="suspended" class="_formBlock" @update:modelValue="toggleSuspend">{{ i18n.ts.suspend }}</FormSwitch>
+				{{ i18n.ts.reflectMayTakeTime }}
 				<div class="_formBlock">
-					<FormButton v-if="user.host == null && iAmModerator" inline style="margin-right: 8px;" @click="resetPassword"><i class="fas fa-key"></i> {{ $ts.resetPassword }}</FormButton>
-					<FormButton v-if="$i.isAdmin" inline danger @click="deleteAccount">{{ $ts.deleteAccount }}</FormButton>
+					<FormButton v-if="user.host == null && iAmModerator" inline style="margin-right: 8px;" @click="resetPassword"><i class="fas fa-key"></i> {{ i18n.ts.resetPassword }}</FormButton>
+					<FormButton v-if="$i.isAdmin" inline danger @click="deleteAccount">{{ i18n.ts.deleteAccount }}</FormButton>
 				</div>
 				<FormTextarea v-model="moderationNote" manual-save class="_formBlock">
 					<template #label>Moderation note</template>
@@ -128,7 +128,7 @@
 				<div class="cmhjzshm">
 					<div class="selects">
 						<MkSelect v-model="chartSrc" style="margin: 0 10px 0 0; flex: 1;">
-							<option value="per-user-notes">{{ $ts.notes }}</option>
+							<option value="per-user-notes">{{ i18n.ts.notes }}</option>
 						</MkSelect>
 					</div>
 					<div class="charts">
diff --git a/packages/client/src/pages/user-list-timeline.vue b/packages/client/src/pages/user-list-timeline.vue
index 593db1dea..c1f4569a0 100644
--- a/packages/client/src/pages/user-list-timeline.vue
+++ b/packages/client/src/pages/user-list-timeline.vue
@@ -2,7 +2,7 @@
 <MkStickyContainer>
 	<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
 	<div ref="rootEl" v-size="{ min: [800] }" class="eqqrhokj">
-		<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 _block">
 			<XTimeline
 				ref="tlEl" :key="listId"
diff --git a/packages/client/src/pages/user/home.vue b/packages/client/src/pages/user/home.vue
index c28079bb3..fa72a995b 100644
--- a/packages/client/src/pages/user/home.vue
+++ b/packages/client/src/pages/user/home.vue
@@ -3,8 +3,8 @@
 	<div ref="rootEl" v-size="{ max: [500] }" class="ftskorzw" :class="{ wide: !narrow }">
 		<div class="main">
 			<!-- TODO -->
-			<!-- <div class="punished" v-if="user.isSuspended"><i class="fas fa-exclamation-triangle" style="margin-right: 8px;"></i> {{ $ts.userSuspended }}</div> -->
-			<!-- <div class="punished" v-if="user.isSilenced"><i class="fas fa-exclamation-triangle" style="margin-right: 8px;"></i> {{ $ts.userSilenced }}</div> -->
+			<!-- <div class="punished" v-if="user.isSuspended"><i class="fas fa-exclamation-triangle" style="margin-right: 8px;"></i> {{ i18n.ts.userSuspended }}</div> -->
+			<!-- <div class="punished" v-if="user.isSilenced"><i class="fas fa-exclamation-triangle" style="margin-right: 8px;"></i> {{ i18n.ts.userSilenced }}</div> -->
 
 			<div class="profile">
 				<MkRemoteCaution v-if="user.host != null" :href="user.url" class="warn"/>
@@ -17,13 +17,13 @@
 							<MkUserName class="name" :user="user" :nowrap="true"/>
 							<div class="bottom">
 								<span class="username"><MkAcct :user="user" :detail="true"/></span>
-								<span v-if="user.isAdmin" :title="$ts.isAdmin" style="color: var(--badge);"><i class="fas fa-bookmark"></i></span>
-								<span v-if="!user.isAdmin && user.isModerator" :title="$ts.isModerator" style="color: var(--badge);"><i class="far fa-bookmark"></i></span>
-								<span v-if="user.isLocked" :title="$ts.isLocked"><i class="fas fa-lock"></i></span>
-								<span v-if="user.isBot" :title="$ts.isBot"><i class="fas fa-robot"></i></span>
+								<span v-if="user.isAdmin" :title="i18n.ts.isAdmin" style="color: var(--badge);"><i class="fas fa-bookmark"></i></span>
+								<span v-if="!user.isAdmin && user.isModerator" :title="i18n.ts.isModerator" style="color: var(--badge);"><i class="far fa-bookmark"></i></span>
+								<span v-if="user.isLocked" :title="i18n.ts.isLocked"><i class="fas fa-lock"></i></span>
+								<span v-if="user.isBot" :title="i18n.ts.isBot"><i class="fas fa-robot"></i></span>
 							</div>
 						</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 v-if="$i" class="actions">
 							<button class="menu _button" @click="menu"><i class="fas fa-ellipsis-h"></i></button>
 							<MkFollowButton v-if="$i.id != user.id" :user="user" :inline="true" :transparent="false" :full="true" class="koudoku"/>
@@ -34,27 +34,27 @@
 						<MkUserName :user="user" :nowrap="false" class="name"/>
 						<div class="bottom">
 							<span class="username"><MkAcct :user="user" :detail="true"/></span>
-							<span v-if="user.isAdmin" :title="$ts.isAdmin" style="color: var(--badge);"><i class="fas fa-bookmark"></i></span>
-							<span v-if="!user.isAdmin && user.isModerator" :title="$ts.isModerator" style="color: var(--badge);"><i class="far fa-bookmark"></i></span>
-							<span v-if="user.isLocked" :title="$ts.isLocked"><i class="fas fa-lock"></i></span>
-							<span v-if="user.isBot" :title="$ts.isBot"><i class="fas fa-robot"></i></span>
+							<span v-if="user.isAdmin" :title="i18n.ts.isAdmin" style="color: var(--badge);"><i class="fas fa-bookmark"></i></span>
+							<span v-if="!user.isAdmin && user.isModerator" :title="i18n.ts.isModerator" style="color: var(--badge);"><i class="far fa-bookmark"></i></span>
+							<span v-if="user.isLocked" :title="i18n.ts.isLocked"><i class="fas fa-lock"></i></span>
+							<span v-if="user.isBot" :title="i18n.ts.isBot"><i class="fas fa-robot"></i></span>
 						</div>
 					</div>
 					<div class="description">
 						<Mfm v-if="user.description" :text="user.description" :is-note="false" :author="user" :i="$i" :custom-emojis="user.emojis"/>
-						<p v-else class="empty">{{ $ts.noAccountDescription }}</p>
+						<p v-else class="empty">{{ i18n.ts.noAccountDescription }}</p>
 					</div>
 					<div class="fields system">
 						<dl v-if="user.location" class="field">
-							<dt class="name"><i class="fas fa-map-marker fa-fw"></i> {{ $ts.location }}</dt>
+							<dt class="name"><i class="fas fa-map-marker fa-fw"></i> {{ i18n.ts.location }}</dt>
 							<dd class="value">{{ user.location }}</dd>
 						</dl>
 						<dl v-if="user.birthday" class="field">
-							<dt class="name"><i class="fas fa-birthday-cake fa-fw"></i> {{ $ts.birthday }}</dt>
+							<dt class="name"><i class="fas fa-birthday-cake fa-fw"></i> {{ i18n.ts.birthday }}</dt>
 							<dd class="value">{{ user.birthday.replace('-', '/').replace('-', '/') }} ({{ $t('yearsOld', { age }) }})</dd>
 						</dl>
 						<dl class="field">
-							<dt class="name"><i class="fas fa-calendar-alt fa-fw"></i> {{ $ts.registeredDate }}</dt>
+							<dt class="name"><i class="fas fa-calendar-alt fa-fw"></i> {{ i18n.ts.registeredDate }}</dt>
 							<dd class="value">{{ new Date(user.createdAt).toLocaleString() }} (<MkTime :time="user.createdAt"/>)</dd>
 						</dl>
 					</div>
@@ -71,15 +71,15 @@
 					<div class="status">
 						<MkA v-click-anime :to="userPage(user)" :class="{ active: page === 'index' }">
 							<b>{{ number(user.notesCount) }}</b>
-							<span>{{ $ts.notes }}</span>
+							<span>{{ i18n.ts.notes }}</span>
 						</MkA>
 						<MkA v-click-anime :to="userPage(user, 'following')" :class="{ active: page === 'following' }">
 							<b>{{ number(user.followingCount) }}</b>
-							<span>{{ $ts.following }}</span>
+							<span>{{ i18n.ts.following }}</span>
 						</MkA>
 						<MkA v-click-anime :to="userPage(user, 'followers')" :class="{ active: page === 'followers' }">
 							<b>{{ number(user.followersCount) }}</b>
-							<span>{{ $ts.followers }}</span>
+							<span>{{ i18n.ts.followers }}</span>
 						</MkA>
 					</div>
 				</div>
@@ -89,7 +89,7 @@
 				<div v-if="user.pinnedNotes.length > 0" class="_gap">
 					<XNote v-for="note in user.pinnedNotes" :key="note.id" class="note _block" :note="note" :pinned="true"/>
 				</div>
-				<MkInfo v-else-if="$i && $i.id === user.id">{{ $ts.userPagePinTip }}</MkInfo>
+				<MkInfo v-else-if="$i && $i.id === user.id">{{ i18n.ts.userPagePinTip }}</MkInfo>
 				<template v-if="narrow">
 					<XPhotos :key="user.id" :user="user"/>
 					<XActivity :key="user.id" :user="user" style="margin-top: var(--margin);"/>
diff --git a/packages/client/src/pages/user/index.timeline.vue b/packages/client/src/pages/user/index.timeline.vue
index 1bcc0a1b8..0345978b8 100644
--- a/packages/client/src/pages/user/index.timeline.vue
+++ b/packages/client/src/pages/user/index.timeline.vue
@@ -2,9 +2,9 @@
 <MkStickyContainer>
 	<template #header>
 		<MkTab v-model="include" :class="$style.tab">
-			<option :value="null">{{ $ts.notes }}</option>
-			<option value="replies">{{ $ts.notesAndReplies }}</option>
-			<option value="files">{{ $ts.withFiles }}</option>
+			<option :value="null">{{ i18n.ts.notes }}</option>
+			<option value="replies">{{ i18n.ts.notesAndReplies }}</option>
+			<option value="files">{{ i18n.ts.withFiles }}</option>
 		</MkTab>
 	</template>
 	<XNotes :no-gap="true" :pagination="pagination"/>
@@ -17,6 +17,7 @@ import * as misskey from 'misskey-js';
 import XNotes from '@/components/notes.vue';
 import MkTab from '@/components/tab.vue';
 import * as os from '@/os';
+import { i18n } from '@/i18n';
 
 const props = defineProps<{
 	user: misskey.entities.UserDetailed;
diff --git a/packages/client/src/ui/_common_/navbar-for-mobile.vue b/packages/client/src/ui/_common_/navbar-for-mobile.vue
index f2521cfc7..2bac239d4 100644
--- a/packages/client/src/ui/_common_/navbar-for-mobile.vue
+++ b/packages/client/src/ui/_common_/navbar-for-mobile.vue
@@ -9,30 +9,30 @@
 		</div>
 		<div class="middle">
 			<MkA v-click-anime class="item index" active-class="active" to="/" exact>
-				<i class="icon fas fa-home fa-fw"></i><span class="text">{{ $ts.timeline }}</span>
+				<i class="icon fas fa-home fa-fw"></i><span class="text">{{ i18n.ts.timeline }}</span>
 			</MkA>
 			<template v-for="item in menu">
 				<div v-if="item === '-'" class="divider"></div>
 				<component :is="navbarItemDef[item].to ? 'MkA' : 'button'" v-else-if="navbarItemDef[item] && (navbarItemDef[item].show !== false)" v-click-anime class="item _button" :class="[item, { active: navbarItemDef[item].active }]" active-class="active" :to="navbarItemDef[item].to" v-on="navbarItemDef[item].action ? { click: navbarItemDef[item].action } : {}">
-					<i class="icon fa-fw" :class="navbarItemDef[item].icon"></i><span class="text">{{ $ts[navbarItemDef[item].title] }}</span>
+					<i class="icon fa-fw" :class="navbarItemDef[item].icon"></i><span class="text">{{ i18n.ts[navbarItemDef[item].title] }}</span>
 					<span v-if="navbarItemDef[item].indicated" class="indicator"><i class="icon fas fa-circle"></i></span>
 				</component>
 			</template>
 			<div class="divider"></div>
 			<MkA v-if="$i.isAdmin || $i.isModerator" v-click-anime class="item" active-class="active" to="/admin">
-				<i class="icon fas fa-door-open fa-fw"></i><span class="text">{{ $ts.controlPanel }}</span>
+				<i class="icon fas fa-door-open fa-fw"></i><span class="text">{{ i18n.ts.controlPanel }}</span>
 			</MkA>
 			<button v-click-anime class="item _button" @click="more">
-				<i class="icon fa fa-ellipsis-h fa-fw"></i><span class="text">{{ $ts.more }}</span>
+				<i class="icon fa fa-ellipsis-h fa-fw"></i><span class="text">{{ i18n.ts.more }}</span>
 				<span v-if="otherMenuItemIndicated" class="indicator"><i class="icon fas fa-circle"></i></span>
 			</button>
 			<MkA v-click-anime class="item" active-class="active" to="/settings">
-				<i class="icon fas fa-cog fa-fw"></i><span class="text">{{ $ts.settings }}</span>
+				<i class="icon fas fa-cog fa-fw"></i><span class="text">{{ i18n.ts.settings }}</span>
 			</MkA>
 		</div>
 		<div class="bottom">
 			<button class="item _button post" data-cy-open-post-form @click="os.post">
-				<i class="icon fas fa-pencil-alt fa-fw"></i><span class="text">{{ $ts.note }}</span>
+				<i class="icon fas fa-pencil-alt fa-fw"></i><span class="text">{{ i18n.ts.note }}</span>
 			</button>
 			<button v-click-anime class="item _button account" @click="openAccountMenu">
 				<MkAvatar :user="$i" class="avatar"/><MkAcct class="text" :user="$i"/>
diff --git a/packages/client/src/ui/_common_/stream-indicator.vue b/packages/client/src/ui/_common_/stream-indicator.vue
index 5e811e1b8..a855de8ab 100644
--- a/packages/client/src/ui/_common_/stream-indicator.vue
+++ b/packages/client/src/ui/_common_/stream-indicator.vue
@@ -1,9 +1,9 @@
 <template>
 <div v-if="hasDisconnected && $store.state.serverDisconnectedBehavior === 'quiet'" class="nsbbhtug" @click="resetDisconnected">
-	<div>{{ $ts.disconnectedFromServer }}</div>
+	<div>{{ i18n.ts.disconnectedFromServer }}</div>
 	<div class="command">
-		<button class="_textButton" @click="reload">{{ $ts.reload }}</button>
-		<button class="_textButton">{{ $ts.doNothing }}</button>
+		<button class="_textButton" @click="reload">{{ i18n.ts.reload }}</button>
+		<button class="_textButton">{{ i18n.ts.doNothing }}</button>
 	</div>
 </div>
 </template>
@@ -11,6 +11,7 @@
 <script lang="ts" setup>
 import { onUnmounted } from 'vue';
 import { stream } from '@/stream';
+import { i18n } from '@/i18n';
 
 let hasDisconnected = $ref(false);
 
diff --git a/packages/client/src/ui/_common_/upload.vue b/packages/client/src/ui/_common_/upload.vue
index f3703d0e8..8324e9e75 100644
--- a/packages/client/src/ui/_common_/upload.vue
+++ b/packages/client/src/ui/_common_/upload.vue
@@ -6,7 +6,7 @@
 			<div class="top">
 				<p class="name"><i class="fas fa-spinner fa-pulse"></i>{{ ctx.name }}</p>
 				<p class="status">
-					<span v-if="ctx.progressValue === undefined" class="initing">{{ $ts.waiting }}<MkEllipsis/></span>
+					<span v-if="ctx.progressValue === undefined" class="initing">{{ i18n.ts.waiting }}<MkEllipsis/></span>
 					<span v-if="ctx.progressValue !== undefined" class="kb">{{ String(Math.floor(ctx.progressValue / 1024)).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1,') }}<i>KB</i> / {{ String(Math.floor(ctx.progressMax / 1024)).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1,') }}<i>KB</i></span>
 					<span v-if="ctx.progressValue !== undefined" class="percentage">{{ Math.floor((ctx.progressValue / ctx.progressMax) * 100) }}</span>
 				</p>
@@ -21,6 +21,7 @@
 import { } from 'vue';
 import * as os from '@/os';
 import { uploads } from '@/scripts/upload';
+import { i18n } from '@/i18n';
 
 const zIndex = os.claimZIndex('high');
 </script>
diff --git a/packages/client/src/widgets/activity.vue b/packages/client/src/widgets/activity.vue
index 7252d6540..97e354e1b 100644
--- a/packages/client/src/widgets/activity.vue
+++ b/packages/client/src/widgets/activity.vue
@@ -1,6 +1,6 @@
 <template>
 <MkContainer :show-header="widgetProps.showHeader" :naked="widgetProps.transparent" class="mkw-activity">
-	<template #header><i class="fas fa-chart-simple"></i>{{ $ts._widgets.activity }}</template>
+	<template #header><i class="fas fa-chart-simple"></i>{{ i18n.ts._widgets.activity }}</template>
 	<template #func><button class="_button" @click="toggleView()"><i class="fas fa-sort"></i></button></template>
 
 	<div>
@@ -22,6 +22,7 @@ import { GetFormResultType } from '@/scripts/form';
 import * as os from '@/os';
 import MkContainer from '@/components/ui/container.vue';
 import { $i } from '@/account';
+import { i18n } from '@/i18n';
 
 const name = 'activity';
 
diff --git a/packages/client/src/widgets/aiscript.vue b/packages/client/src/widgets/aiscript.vue
index 9fed292a6..6ae1a9877 100644
--- a/packages/client/src/widgets/aiscript.vue
+++ b/packages/client/src/widgets/aiscript.vue
@@ -1,6 +1,6 @@
 <template>
 <MkContainer :show-header="widgetProps.showHeader" class="mkw-aiscript">
-	<template #header><i class="fas fa-terminal"></i>{{ $ts._widgets.aiscript }}</template>
+	<template #header><i class="fas fa-terminal"></i>{{ i18n.ts._widgets.aiscript }}</template>
 
 	<div class="uylguesu _monospace">
 		<textarea v-model="widgetProps.script" placeholder="(1 + 1)"></textarea>
@@ -14,13 +14,14 @@
 
 <script lang="ts" setup>
 import { onMounted, onUnmounted, ref, watch } from 'vue';
-import { GetFormResultType } from '@/scripts/form';
+import { AiScript, parse, utils } from '@syuilo/aiscript';
 import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
+import { GetFormResultType } from '@/scripts/form';
 import * as os from '@/os';
 import MkContainer from '@/components/ui/container.vue';
-import { AiScript, parse, utils } from '@syuilo/aiscript';
 import { createAiScriptEnv } from '@/scripts/aiscript/api';
 import { $i } from '@/account';
+import { i18n } from '@/i18n';
 
 const name = 'aiscript';
 
@@ -88,7 +89,7 @@ const run = async () => {
 				}); break;
 				default: break;
 			}
-		}
+		},
 	});
 
 	let ast;
diff --git a/packages/client/src/widgets/calendar.vue b/packages/client/src/widgets/calendar.vue
index 3a0dc8970..99bd36e2f 100644
--- a/packages/client/src/widgets/calendar.vue
+++ b/packages/client/src/widgets/calendar.vue
@@ -11,19 +11,19 @@
 	</div>
 	<div class="info">
 		<div>
-			<p>{{ $ts.today }}: <b>{{ dayP.toFixed(1) }}%</b></p>
+			<p>{{ i18n.ts.today }}: <b>{{ dayP.toFixed(1) }}%</b></p>
 			<div class="meter">
 				<div class="val" :style="{ width: `${dayP}%` }"></div>
 			</div>
 		</div>
 		<div>
-			<p>{{ $ts.thisMonth }}: <b>{{ monthP.toFixed(1) }}%</b></p>
+			<p>{{ i18n.ts.thisMonth }}: <b>{{ monthP.toFixed(1) }}%</b></p>
 			<div class="meter">
 				<div class="val" :style="{ width: `${monthP}%` }"></div>
 			</div>
 		</div>
 		<div>
-			<p>{{ $ts.thisYear }}: <b>{{ yearP.toFixed(1) }}%</b></p>
+			<p>{{ i18n.ts.thisYear }}: <b>{{ yearP.toFixed(1) }}%</b></p>
 			<div class="meter">
 				<div class="val" :style="{ width: `${yearP}%` }"></div>
 			</div>
diff --git a/packages/client/src/widgets/federation.vue b/packages/client/src/widgets/federation.vue
index ac87cdac2..9619aa53e 100644
--- a/packages/client/src/widgets/federation.vue
+++ b/packages/client/src/widgets/federation.vue
@@ -1,6 +1,6 @@
 <template>
 <MkContainer :show-header="widgetProps.showHeader" :foldable="foldable" :scrollable="scrollable" class="mkw-federation">
-	<template #header><i class="fas fa-globe"></i>{{ $ts._widgets.federation }}</template>
+	<template #header><i class="fas fa-globe"></i>{{ i18n.ts._widgets.federation }}</template>
 
 	<div class="wbrkwalb">
 		<MkLoading v-if="fetching"/>
@@ -26,6 +26,7 @@ import MkContainer from '@/components/ui/container.vue';
 import MkMiniChart from '@/components/mini-chart.vue';
 import * as os from '@/os';
 import { useInterval } from '@/scripts/use-interval';
+import { i18n } from '@/i18n';
 
 const name = 'federation';
 
diff --git a/packages/client/src/widgets/memo.vue b/packages/client/src/widgets/memo.vue
index 8670cb2ba..14cc2a4e4 100644
--- a/packages/client/src/widgets/memo.vue
+++ b/packages/client/src/widgets/memo.vue
@@ -1,21 +1,22 @@
 <template>
 <MkContainer :show-header="widgetProps.showHeader" class="mkw-memo">
-	<template #header><i class="fas fa-sticky-note"></i>{{ $ts._widgets.memo }}</template>
+	<template #header><i class="fas fa-sticky-note"></i>{{ i18n.ts._widgets.memo }}</template>
 
 	<div class="otgbylcu">
-		<textarea v-model="text" :placeholder="$ts.placeholder" @input="onChange"></textarea>
-		<button :disabled="!changed" class="_buttonPrimary" @click="saveMemo">{{ $ts.save }}</button>
+		<textarea v-model="text" :placeholder="i18n.ts.placeholder" @input="onChange"></textarea>
+		<button :disabled="!changed" class="_buttonPrimary" @click="saveMemo">{{ i18n.ts.save }}</button>
 	</div>
 </MkContainer>
 </template>
 
 <script lang="ts" setup>
 import { onMounted, onUnmounted, reactive, ref, watch } from 'vue';
-import { GetFormResultType } from '@/scripts/form';
 import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
+import { GetFormResultType } from '@/scripts/form';
 import * as os from '@/os';
 import MkContainer from '@/components/ui/container.vue';
 import { defaultStore } from '@/store';
+import { i18n } from '@/i18n';
 
 const name = 'memo';
 
diff --git a/packages/client/src/widgets/notifications.vue b/packages/client/src/widgets/notifications.vue
index 18c546ee7..05b24e5cb 100644
--- a/packages/client/src/widgets/notifications.vue
+++ b/packages/client/src/widgets/notifications.vue
@@ -1,6 +1,6 @@
 <template>
 <MkContainer :style="`height: ${widgetProps.height}px;`" :show-header="widgetProps.showHeader" :scrollable="true" class="mkw-notifications">
-	<template #header><i class="fas fa-bell"></i>{{ $ts.notifications }}</template>
+	<template #header><i class="fas fa-bell"></i>{{ i18n.ts.notifications }}</template>
 	<template #func><button class="_button" @click="configureNotification()"><i class="fas fa-cog"></i></button></template>
 
 	<div>
@@ -10,12 +10,13 @@
 </template>
 
 <script lang="ts" setup>
-import { GetFormResultType } from '@/scripts/form';
+import { defineAsyncComponent } from 'vue';
 import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
+import { GetFormResultType } from '@/scripts/form';
 import MkContainer from '@/components/ui/container.vue';
 import XNotifications from '@/components/notifications.vue';
 import * as os from '@/os';
-import { defineAsyncComponent } from 'vue';
+import { i18n } from '@/i18n';
 
 const name = 'notifications';
 
@@ -57,7 +58,7 @@ const configureNotification = () => {
 			const { includingTypes } = res;
 			widgetProps.includingTypes = includingTypes;
 			save();
-		}
+		},
 	}, 'closed');
 };
 
diff --git a/packages/client/src/widgets/online-users.vue b/packages/client/src/widgets/online-users.vue
index 4122a8265..e9ab79b11 100644
--- a/packages/client/src/widgets/online-users.vue
+++ b/packages/client/src/widgets/online-users.vue
@@ -1,6 +1,6 @@
 <template>
 <div class="mkw-onlineUsers" :class="{ _panel: !widgetProps.transparent, pad: !widgetProps.transparent }">
-	<I18n v-if="onlineUsersCount" :src="$ts.onlineUsersCount" text-tag="span" class="text">
+	<I18n v-if="onlineUsersCount" :src="i18n.ts.onlineUsersCount" text-tag="span" class="text">
 		<template #n><b>{{ onlineUsersCount }}</b></template>
 	</I18n>
 </div>
@@ -12,6 +12,7 @@ import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExp
 import { GetFormResultType } from '@/scripts/form';
 import * as os from '@/os';
 import { useInterval } from '@/scripts/use-interval';
+import { i18n } from '@/i18n';
 
 const name = 'onlineUsers';
 
diff --git a/packages/client/src/widgets/photos.vue b/packages/client/src/widgets/photos.vue
index 5d9b9e298..41bec5dc5 100644
--- a/packages/client/src/widgets/photos.vue
+++ b/packages/client/src/widgets/photos.vue
@@ -1,11 +1,12 @@
 <template>
 <MkContainer :show-header="widgetProps.showHeader" :naked="widgetProps.transparent" :class="$style.root" :data-transparent="widgetProps.transparent ? true : null" class="mkw-photos">
-	<template #header><i class="fas fa-camera"></i>{{ $ts._widgets.photos }}</template>
+	<template #header><i class="fas fa-camera"></i>{{ i18n.ts._widgets.photos }}</template>
 
 	<div class="">
 		<MkLoading v-if="fetching"/>
 		<div v-else :class="$style.stream">
-			<div v-for="(image, i) in images" :key="i"
+			<div
+				v-for="(image, i) in images" :key="i"
 				:class="$style.img"
 				:style="`background-image: url(${thumbnail(image)})`"
 			></div>
@@ -16,13 +17,14 @@
 
 <script lang="ts" setup>
 import { onMounted, onUnmounted, reactive, ref } from 'vue';
-import { GetFormResultType } from '@/scripts/form';
 import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
+import { GetFormResultType } from '@/scripts/form';
 import { stream } from '@/stream';
 import { getStaticImageUrl } from '@/scripts/get-static-image-url';
 import * as os from '@/os';
 import MkContainer from '@/components/ui/container.vue';
 import { defaultStore } from '@/store';
+import { i18n } from '@/i18n';
 
 const name = 'photos';
 
@@ -70,7 +72,7 @@ const thumbnail = (image: any): string => {
 
 os.api('drive/stream', {
 	type: 'image/*',
-	limit: 9
+	limit: 9,
 }).then(res => {
 	images.value = res;
 	fetching.value = false;
diff --git a/packages/client/src/widgets/server-metric/index.vue b/packages/client/src/widgets/server-metric/index.vue
index 9e86b811d..3350620ad 100644
--- a/packages/client/src/widgets/server-metric/index.vue
+++ b/packages/client/src/widgets/server-metric/index.vue
@@ -1,6 +1,6 @@
 <template>
 <MkContainer :show-header="widgetProps.showHeader" :naked="widgetProps.transparent">
-	<template #header><i class="fas fa-server"></i>{{ $ts._widgets.serverMetric }}</template>
+	<template #header><i class="fas fa-server"></i>{{ i18n.ts._widgets.serverMetric }}</template>
 	<template #func><button class="_button" @click="toggleView()"><i class="fas fa-sort"></i></button></template>
 
 	<div v-if="meta" class="mkw-serverMetric">
@@ -15,16 +15,17 @@
 
 <script lang="ts" setup>
 import { onMounted, onUnmounted, ref } from 'vue';
-import { GetFormResultType } from '@/scripts/form';
 import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from '../widget';
-import MkContainer from '@/components/ui/container.vue';
 import XCpuMemory from './cpu-mem.vue';
 import XNet from './net.vue';
 import XCpu from './cpu.vue';
 import XMemory from './mem.vue';
 import XDisk from './disk.vue';
+import MkContainer from '@/components/ui/container.vue';
+import { GetFormResultType } from '@/scripts/form';
 import * as os from '@/os';
 import { stream } from '@/stream';
+import { i18n } from '@/i18n';
 
 const name = 'serverMetric';
 
diff --git a/packages/client/src/widgets/slideshow.vue b/packages/client/src/widgets/slideshow.vue
index c28631216..e317b8ab9 100644
--- a/packages/client/src/widgets/slideshow.vue
+++ b/packages/client/src/widgets/slideshow.vue
@@ -2,7 +2,7 @@
 <div class="kvausudm _panel mkw-slideshow" :style="{ height: widgetProps.height + 'px' }">
 	<div @click="choose">
 		<p v-if="widgetProps.folderId == null">
-			{{ $ts.folder }}
+			{{ i18n.ts.folder }}
 		</p>
 		<p v-if="widgetProps.folderId != null && images.length === 0 && !fetching">{{ $t('no-image') }}</p>
 		<div ref="slideA" class="slide a"></div>
@@ -17,6 +17,7 @@ import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExp
 import { GetFormResultType } from '@/scripts/form';
 import * as os from '@/os';
 import { useInterval } from '@/scripts/use-interval';
+import { i18n } from '@/i18n';
 
 const name = 'slideshow';
 
diff --git a/packages/client/src/widgets/trends.vue b/packages/client/src/widgets/trends.vue
index 0f34ea634..8d11b69cb 100644
--- a/packages/client/src/widgets/trends.vue
+++ b/packages/client/src/widgets/trends.vue
@@ -1,6 +1,6 @@
 <template>
 <MkContainer :show-header="widgetProps.showHeader" class="mkw-trends">
-	<template #header><i class="fas fa-hashtag"></i>{{ $ts._widgets.trends }}</template>
+	<template #header><i class="fas fa-hashtag"></i>{{ i18n.ts._widgets.trends }}</template>
 
 	<div class="wbrkwala">
 		<MkLoading v-if="fetching"/>
@@ -25,6 +25,7 @@ import MkContainer from '@/components/ui/container.vue';
 import MkMiniChart from '@/components/mini-chart.vue';
 import * as os from '@/os';
 import { useInterval } from '@/scripts/use-interval';
+import { i18n } from '@/i18n';
 
 const name = 'hashtags';