diff --git a/src/client/components/abuse-report-window.vue b/src/client/components/abuse-report-window.vue
index c550e1e85..98578ee44 100644
--- a/src/client/components/abuse-report-window.vue
+++ b/src/client/components/abuse-report-window.vue
@@ -2,7 +2,7 @@
 <XWindow ref="window" :initial-width="400" :initial-height="500" :can-resize="true" @closed="$emit('closed')">
 	<template #header>
 		<Fa :icon="faExclamationCircle" style="margin-right: 0.5em;"/>
-		<I18n src="reportAbuseOf" tag="span">
+		<I18n :src="$ts.reportAbuseOf" tag="span">
 			<template #name>
 				<b><MkAcct :user="user"/></b>
 			</template>
@@ -12,14 +12,14 @@
 		<div class="_section">
 			<div class="_content">
 				<MkTextarea v-model:value="comment">
-					<span>{{ $t('details') }}</span>
-					<template #desc>{{ $t('fillAbuseReportDescription') }}</template>
+					<span>{{ $ts.details }}</span>
+					<template #desc>{{ $ts.fillAbuseReportDescription }}</template>
 				</MkTextarea>
 			</div>
 		</div>
 		<div class="_section">
 			<div class="_content">
-				<MkButton @click="send" primary full :disabled="comment.length === 0">{{ $t('send') }}</MkButton>
+				<MkButton @click="send" primary full :disabled="comment.length === 0">{{ $ts.send }}</MkButton>
 			</div>
 		</div>
 	</div>
@@ -69,7 +69,7 @@ export default defineComponent({
 			}, undefined, res => {
 				os.dialog({
 					type: 'success',
-					text: this.$t('abuseReported')
+					text: this.$ts.abuseReported
 				});
 				this.$refs.window.close();
 			});
diff --git a/src/client/components/autocomplete.vue b/src/client/components/autocomplete.vue
index c3eaa36ef..ae742610f 100644
--- a/src/client/components/autocomplete.vue
+++ b/src/client/components/autocomplete.vue
@@ -8,7 +8,7 @@
 			</span>
 			<span class="username">@{{ acct(user) }}</span>
 		</li>
-		<li @click="chooseUser()" @keydown="onKeydown" tabindex="-1" class="choose">{{ $t('selectUser') }}</li>
+		<li @click="chooseUser()" @keydown="onKeydown" tabindex="-1" class="choose">{{ $ts.selectUser }}</li>
 	</ol>
 	<ol class="hashtags" ref="suggests" v-if="hashtags.length > 0">
 		<li v-for="hashtag in hashtags" @click="complete(type, hashtag)" @keydown="onKeydown" tabindex="-1">
diff --git a/src/client/components/captcha.vue b/src/client/components/captcha.vue
index f37f39556..a6445ecea 100644
--- a/src/client/components/captcha.vue
+++ b/src/client/components/captcha.vue
@@ -1,6 +1,6 @@
 <template>
 <div>
-	<span v-if="!available">{{ $t('waiting') }}<MkEllipsis/></span>
+	<span v-if="!available">{{ $ts.waiting }}<MkEllipsis/></span>
 	<div ref="captcha"></div>
 </div>
 </template>
diff --git a/src/client/components/channel-follow-button.vue b/src/client/components/channel-follow-button.vue
index c59c319fd..17770a6f3 100644
--- a/src/client/components/channel-follow-button.vue
+++ b/src/client/components/channel-follow-button.vue
@@ -6,14 +6,14 @@
 >
 	<template v-if="!wait">
 		<template v-if="isFollowing">
-			<span v-if="full">{{ $t('unfollow') }}</span><Fa :icon="faMinus"/>
+			<span v-if="full">{{ $ts.unfollow }}</span><Fa :icon="faMinus"/>
 		</template>
 		<template v-else>
-			<span v-if="full">{{ $t('follow') }}</span><Fa :icon="faPlus"/>
+			<span v-if="full">{{ $ts.follow }}</span><Fa :icon="faPlus"/>
 		</template>
 	</template>
 	<template v-else>
-		<span v-if="full">{{ $t('processing') }}</span><Fa :icon="faSpinner" pulse fixed-width/>
+		<span v-if="full">{{ $ts.processing }}</span><Fa :icon="faSpinner" pulse fixed-width/>
 	</template>
 </button>
 </template>
diff --git a/src/client/components/channel-preview.vue b/src/client/components/channel-preview.vue
index 241fbe4f6..e222ad7ae 100644
--- a/src/client/components/channel-preview.vue
+++ b/src/client/components/channel-preview.vue
@@ -6,7 +6,7 @@
 		<div class="status">
 			<div>
 				<Fa :icon="faUsers" fixed-width/>
-				<I18n src="_channel.usersCount" tag="span" style="margin-left: 4px;">
+				<I18n :src="$ts._channel.usersCount" tag="span" style="margin-left: 4px;">
 					<template #n>
 						<b>{{ channel.usersCount }}</b>
 					</template>
@@ -14,7 +14,7 @@
 			</div>
 			<div>
 				<Fa :icon="faPencilAlt" fixed-width/>
-				<I18n src="_channel.notesCount" tag="span" style="margin-left: 4px;">
+				<I18n :src="$ts._channel.notesCount" tag="span" style="margin-left: 4px;">
 					<template #n>
 						<b>{{ channel.notesCount }}</b>
 					</template>
@@ -27,7 +27,7 @@
 	</article>
 	<footer>
 		<span v-if="channel.lastNotedAt">
-			{{ $t('updatedAt') }}: <MkTime :time="channel.lastNotedAt"/>
+			{{ $ts.updatedAt }}: <MkTime :time="channel.lastNotedAt"/>
 		</span>
 	</footer>
 </MkA>
diff --git a/src/client/components/cw-button.vue b/src/client/components/cw-button.vue
index d052c410d..d2336085a 100644
--- a/src/client/components/cw-button.vue
+++ b/src/client/components/cw-button.vue
@@ -1,6 +1,6 @@
 <template>
 <button class="nrvgflfu _button" @click="toggle">
-	<b>{{ value ? $t('_cw.hide') : $t('_cw.show') }}</b>
+	<b>{{ value ? $ts._cw.hide : $ts._cw.show }}</b>
 	<span v-if="!value">{{ label }}</span>
 </button>
 </template>
@@ -27,7 +27,7 @@ export default defineComponent({
 			return concat([
 				this.note.text ? [this.$t('_cw.chars', { count: length(this.note.text) })] : [],
 				this.note.files && this.note.files.length !== 0 ? [this.$t('_cw.files', { count: this.note.files.length }) ] : [],
-				this.note.poll != null ? [this.$t('poll')] : []
+				this.note.poll != null ? [this.$ts.poll] : []
 			] as string[][]).join(' / ');
 		}
 	},
diff --git a/src/client/components/dialog.vue b/src/client/components/dialog.vue
index f8d700202..a34f7f794 100644
--- a/src/client/components/dialog.vue
+++ b/src/client/components/dialog.vue
@@ -26,8 +26,8 @@
 			</template>
 		</MkSelect>
 		<div class="buttons" v-if="(showOkButton || showCancelButton) && !actions">
-			<MkButton inline @click="ok" v-if="showOkButton" primary :autofocus="!input && !select">{{ (showCancelButton || input || select) ? $t('ok') : $t('gotIt') }}</MkButton>
-			<MkButton inline @click="cancel" v-if="showCancelButton || input || select">{{ $t('cancel') }}</MkButton>
+			<MkButton inline @click="ok" v-if="showOkButton" primary :autofocus="!input && !select">{{ (showCancelButton || input || select) ? $ts.ok : $ts.gotIt }}</MkButton>
+			<MkButton inline @click="cancel" v-if="showCancelButton || input || select">{{ $ts.cancel }}</MkButton>
 		</div>
 		<div class="buttons" v-if="actions">
 			<MkButton v-for="action in actions" inline @click="() => { action.callback(); close(); }" :primary="action.primary" :key="action.text">{{ action.text }}</MkButton>
diff --git a/src/client/components/drive-select-dialog.vue b/src/client/components/drive-select-dialog.vue
index 5221bdbb6..f9a402545 100644
--- a/src/client/components/drive-select-dialog.vue
+++ b/src/client/components/drive-select-dialog.vue
@@ -10,7 +10,7 @@
 	@closed="$emit('closed')"
 >
 	<template #header>
-		{{ multiple ? ((type === 'file') ? $t('selectFiles') : $t('selectFolders')) : ((type === 'file') ? $t('selectFile') : $t('selectFolder')) }}
+		{{ multiple ? ((type === 'file') ? $ts.selectFiles : $ts.selectFolders) : ((type === 'file') ? $ts.selectFile : $ts.selectFolder) }}
 		<span v-if="selected.length > 0" style="margin-left: 8px; opacity: 0.5;">({{ number(selected.length) }})</span>
 	</template>
 	<XDrive :multiple="multiple" @changeSelection="onChangeSelection" @selected="ok()" :select="type"/>
diff --git a/src/client/components/drive-window.vue b/src/client/components/drive-window.vue
index 0806fc180..43f07ebe7 100644
--- a/src/client/components/drive-window.vue
+++ b/src/client/components/drive-window.vue
@@ -6,7 +6,7 @@
 	@closed="$emit('closed')"
 >
 	<template #header>
-		{{ $t('drive') }}
+		{{ $ts.drive }}
 	</template>
 	<XDrive :initial-folder="initialFolder"/>
 </XWindow>
diff --git a/src/client/components/drive.file.vue b/src/client/components/drive.file.vue
index 81e314a72..ddee81261 100644
--- a/src/client/components/drive.file.vue
+++ b/src/client/components/drive.file.vue
@@ -10,15 +10,15 @@
 >
 	<div class="label" v-if="$i.avatarId == file.id">
 		<img src="/assets/label.svg"/>
-		<p>{{ $t('avatar') }}</p>
+		<p>{{ $ts.avatar }}</p>
 	</div>
 	<div class="label" v-if="$i.bannerId == file.id">
 		<img src="/assets/label.svg"/>
-		<p>{{ $t('banner') }}</p>
+		<p>{{ $ts.banner }}</p>
 	</div>
 	<div class="label red" v-if="file.isSensitive">
 		<img src="/assets/label-red.svg"/>
-		<p>{{ $t('nsfw') }}</p>
+		<p>{{ $ts.nsfw }}</p>
 	</div>
 
 	<MkDriveFileThumbnail class="thumbnail" :file="file" fit="contain"/>
@@ -82,26 +82,26 @@ export default defineComponent({
 	methods: {
 		getMenu() {
 			return [{
-				text: this.$t('rename'),
+				text: this.$ts.rename,
 				icon: faICursor,
 				action: this.rename
 			}, {
-				text: this.file.isSensitive ? this.$t('unmarkAsSensitive') : this.$t('markAsSensitive'),
+				text: this.file.isSensitive ? this.$ts.unmarkAsSensitive : this.$ts.markAsSensitive,
 				icon: this.file.isSensitive ? faEye : faEyeSlash,
 				action: this.toggleSensitive
 			}, null, {
-				text: this.$t('copyUrl'),
+				text: this.$ts.copyUrl,
 				icon: faLink,
 				action: this.copyUrl
 			}, {
 				type: 'a',
 				href: this.file.url,
 				target: '_blank',
-				text: this.$t('download'),
+				text: this.$ts.download,
 				icon: faDownload,
 				download: this.file.name
 			}, null, {
-				text: this.$t('delete'),
+				text: this.$ts.delete,
 				icon: faTrashAlt,
 				danger: true,
 				action: this.deleteFile
@@ -137,9 +137,9 @@ export default defineComponent({
 
 		rename() {
 			os.dialog({
-				title: this.$t('renameFile'),
+				title: this.$ts.renameFile,
 				input: {
-					placeholder: this.$t('inputNewFileName'),
+					placeholder: this.$ts.inputNewFileName,
 					default: this.file.name,
 					allowEmpty: false
 				}
diff --git a/src/client/components/drive.folder.vue b/src/client/components/drive.folder.vue
index 79ae789f7..745b6018d 100644
--- a/src/client/components/drive.folder.vue
+++ b/src/client/components/drive.folder.vue
@@ -20,7 +20,7 @@
 		{{ folder.name }}
 	</p>
 	<p class="upload" v-if="$store.state.uploadFolder == folder.id">
-		{{ $t('uploadFolder') }}
+		{{ $ts.uploadFolder }}
 	</p>
 	<button v-if="selectMode" class="checkbox _button" :class="{ checked: isSelected }" @click.prevent.stop="checkboxClicked"></button>
 </div>
@@ -155,14 +155,14 @@ export default defineComponent({
 					switch (err) {
 						case 'detected-circular-definition':
 							os.dialog({
-								title: this.$t('unableToProcess'),
-								text: this.$t('circularReferenceFolder')
+								title: this.$ts.unableToProcess,
+								text: this.$ts.circularReferenceFolder
 							});
 							break;
 						default:
 							os.dialog({
 								type: 'error',
-								text: this.$t('somethingHappened')
+								text: this.$ts.somethingHappened
 							});
 					}
 				});
@@ -195,9 +195,9 @@ export default defineComponent({
 
 		rename() {
 			os.dialog({
-				title: this.$t('renameFolder'),
+				title: this.$ts.renameFolder,
 				input: {
-					placeholder: this.$t('inputNewFolderName'),
+					placeholder: this.$ts.inputNewFolderName,
 					default: this.folder.name
 				}
 			}).then(({ canceled, result: name }) => {
@@ -221,14 +221,14 @@ export default defineComponent({
 					case 'b0fc8a17-963c-405d-bfbc-859a487295e1':
 						os.dialog({
 							type: 'error',
-							title: this.$t('unableToDelete'),
-							text: this.$t('hasChildFilesOrFolders')
+							title: this.$ts.unableToDelete,
+							text: this.$ts.hasChildFilesOrFolders
 						});
 						break;
 					default:
 						os.dialog({
 							type: 'error',
-							text: this.$t('unableToDelete')
+							text: this.$ts.unableToDelete
 						});
 				}
 			});
@@ -240,7 +240,7 @@ export default defineComponent({
 
 		onContextmenu(e) {
 			os.contextMenu([{
-				text: this.$t('openInWindow'),
+				text: this.$ts.openInWindow,
 				icon: faWindowRestore,
 				action: () => {
 					os.popup(import('./drive-window.vue'), {
@@ -249,11 +249,11 @@ export default defineComponent({
 					}, 'closed');
 				}
 			}, null, {
-				text: this.$t('rename'),
+				text: this.$ts.rename,
 				icon: faICursor,
 				action: this.rename
 			}, null, {
-				text: this.$t('delete'),
+				text: this.$ts.delete,
 				icon: faTrashAlt,
 				danger: true,
 				action: this.deleteFolder
diff --git a/src/client/components/drive.nav-folder.vue b/src/client/components/drive.nav-folder.vue
index 16ebc433f..881330529 100644
--- a/src/client/components/drive.nav-folder.vue
+++ b/src/client/components/drive.nav-folder.vue
@@ -8,7 +8,7 @@
 	@drop.stop="onDrop"
 >
 	<i v-if="folder == null"><Fa :icon="faCloud"/></i>
-	<span>{{ folder == null ? $t('drive') : folder.name }}</span>
+	<span>{{ folder == null ? $ts.drive : folder.name }}</span>
 </div>
 </template>
 
diff --git a/src/client/components/drive.vue b/src/client/components/drive.vue
index a743249b9..64e4d51e2 100644
--- a/src/client/components/drive.vue
+++ b/src/client/components/drive.vue
@@ -24,18 +24,18 @@
 				<XFolder v-for="f in folders" :key="f.id" class="folder" :folder="f" :select-mode="select === 'folder'" :is-selected="selectedFolders.some(x => x.id === f.id)" @chosen="chooseFolder"/>
 				<!-- SEE: https://stackoverflow.com/questions/18744164/flex-box-align-last-row-to-grid -->
 				<div class="padding" v-for="(n, i) in 16" :key="i"></div>
-				<MkButton ref="moreFolders" v-if="moreFolders">{{ $t('loadMore') }}</MkButton>
+				<MkButton ref="moreFolders" v-if="moreFolders">{{ $ts.loadMore }}</MkButton>
 			</div>
 			<div class="files" ref="filesContainer" v-show="files.length > 0">
 				<XFile v-for="file in files" :key="file.id" class="file" :file="file" :select-mode="select === 'file'" :is-selected="selectedFiles.some(x => x.id === file.id)" @chosen="chooseFile"/>
 				<!-- SEE: https://stackoverflow.com/questions/18744164/flex-box-align-last-row-to-grid -->
 				<div class="padding" v-for="(n, i) in 16" :key="i"></div>
-				<MkButton ref="loadMoreFiles" @click="fetchMoreFiles" v-show="moreFiles">{{ $t('loadMore') }}</MkButton>
+				<MkButton ref="loadMoreFiles" @click="fetchMoreFiles" v-show="moreFiles">{{ $ts.loadMore }}</MkButton>
 			</div>
 			<div class="empty" v-if="files.length == 0 && folders.length == 0 && !fetching">
 				<p v-if="draghover">{{ $t('empty-draghover') }}</p>
-				<p v-if="!draghover && folder == null"><strong>{{ $t('emptyDrive') }}</strong><br/>{{ $t('empty-drive-description') }}</p>
-				<p v-if="!draghover && folder != null">{{ $t('emptyFolder') }}</p>
+				<p v-if="!draghover && folder == null"><strong>{{ $ts.emptyDrive }}</strong><br/>{{ $t('empty-drive-description') }}</p>
+				<p v-if="!draghover && folder != null">{{ $ts.emptyFolder }}</p>
 			</div>
 		</div>
 		<MkLoading v-if="fetching"/>
@@ -277,14 +277,14 @@ export default defineComponent({
 					switch (err) {
 						case 'detected-circular-definition':
 							os.dialog({
-								title: this.$t('unableToProcess'),
-								text: this.$t('circularReferenceFolder')
+								title: this.$ts.unableToProcess,
+								text: this.$ts.circularReferenceFolder
 							});
 							break;
 						default:
 							os.dialog({
 								type: 'error',
-								text: this.$t('somethingHappened')
+								text: this.$ts.somethingHappened
 							});
 					}
 				});
@@ -298,9 +298,9 @@ export default defineComponent({
 
 		urlUpload() {
 			os.dialog({
-				title: this.$t('uploadFromUrl'),
+				title: this.$ts.uploadFromUrl,
 				input: {
-					placeholder: this.$t('uploadFromUrlDescription')
+					placeholder: this.$ts.uploadFromUrlDescription
 				}
 			}).then(({ canceled, result: url }) => {
 				if (canceled) return;
@@ -310,17 +310,17 @@ export default defineComponent({
 				});
 
 				os.dialog({
-					title: this.$t('uploadFromUrlRequested'),
-					text: this.$t('uploadFromUrlMayTakeTime')
+					title: this.$ts.uploadFromUrlRequested,
+					text: this.$ts.uploadFromUrlMayTakeTime
 				});
 			});
 		},
 
 		createFolder() {
 			os.dialog({
-				title: this.$t('createFolder'),
+				title: this.$ts.createFolder,
 				input: {
-					placeholder: this.$t('folderName')
+					placeholder: this.$ts.folderName
 				}
 			}).then(({ canceled, result: name }) => {
 				if (canceled) return;
@@ -335,9 +335,9 @@ export default defineComponent({
 
 		renameFolder(folder) {
 			os.dialog({
-				title: this.$t('renameFolder'),
+				title: this.$ts.renameFolder,
 				input: {
-					placeholder: this.$t('inputNewFolderName'),
+					placeholder: this.$ts.inputNewFolderName,
 					default: folder.name
 				}
 			}).then(({ canceled, result: name }) => {
@@ -363,14 +363,14 @@ export default defineComponent({
 					case 'b0fc8a17-963c-405d-bfbc-859a487295e1':
 						os.dialog({
 							type: 'error',
-							title: this.$t('unableToDelete'),
-							text: this.$t('hasChildFilesOrFolders')
+							title: this.$ts.unableToDelete,
+							text: this.$ts.hasChildFilesOrFolders
 						});
 						break;
 					default:
 						os.dialog({
 							type: 'error',
-							text: this.$t('unableToDelete')
+							text: this.$ts.unableToDelete
 						});
 					}
 			});
@@ -602,29 +602,29 @@ export default defineComponent({
 
 		getMenu() {
 			return [{
-				text: this.$t('addFile'),
+				text: this.$ts.addFile,
 				type: 'label'
 			}, {
-				text: this.$t('upload'),
+				text: this.$ts.upload,
 				icon: faUpload,
 				action: () => { this.selectLocalFile(); }
 			}, {
-				text: this.$t('fromUrl'),
+				text: this.$ts.fromUrl,
 				icon: faLink,
 				action: () => { this.urlUpload(); }
 			}, null, {
-				text: this.folder ? this.folder.name : this.$t('drive'),
+				text: this.folder ? this.folder.name : this.$ts.drive,
 				type: 'label'
 			}, this.folder ? {
-				text: this.$t('renameFolder'),
+				text: this.$ts.renameFolder,
 				icon: faICursor,
 				action: () => { this.renameFolder(this.folder); }
 			} : undefined, this.folder ? {
-				text: this.$t('deleteFolder'),
+				text: this.$ts.deleteFolder,
 				icon: faTrashAlt,
 				action: () => { this.deleteFolder(this.folder); }
 			} : undefined, {
-				text: this.$t('createFolder'),
+				text: this.$ts.createFolder,
 				icon: faFolderPlus,
 				action: () => { this.createFolder(); }
 			}];
diff --git a/src/client/components/emoji-picker.vue b/src/client/components/emoji-picker.vue
index a9fa35708..996dc7067 100644
--- a/src/client/components/emoji-picker.vue
+++ b/src/client/components/emoji-picker.vue
@@ -1,7 +1,7 @@
 <template>
 <MkModal ref="modal" :src="src" @click="$refs.modal.close()" @closed="$emit('closed')">
 	<div class="omfetrab _popup" :class="['w' + width, 'h' + height, { big }]">
-		<input ref="search" class="search" :class="{ filled: q != null && q != '' }" v-model.trim="q" :placeholder="$t('search')" @paste.stop="paste" @keyup.enter="done()">
+		<input ref="search" class="search" :class="{ filled: q != null && q != '' }" v-model.trim="q" :placeholder="$ts.search" @paste.stop="paste" @keyup.enter="done()">
 		<div class="emojis">
 			<section class="result">
 				<div v-if="searchResultCustom.length > 0">
@@ -43,7 +43,7 @@
 				</section>
 
 				<section>
-					<header class="_acrylic"><Fa :icon="faClock" fixed-width/> {{ $t('recentUsed') }}</header>
+					<header class="_acrylic"><Fa :icon="faClock" fixed-width/> {{ $ts.recentUsed }}</header>
 					<div>
 						<button v-for="emoji in $store.state.recentlyUsedEmojis"
 							class="_button"
@@ -59,7 +59,7 @@
 			</div>
 
 			<section v-for="category in customEmojiCategories" :key="'custom:' + category" class="custom">
-				<header class="_acrylic" v-appear="() => visibleCategories[category] = true">{{ category || $t('other') }}</header>
+				<header class="_acrylic" v-appear="() => visibleCategories[category] = true">{{ category || $ts.other }}</header>
 				<div v-if="visibleCategories[category]">
 					<button v-for="emoji in customEmojis.filter(e => e.category === category)"
 						class="_button"
diff --git a/src/client/components/follow-button.vue b/src/client/components/follow-button.vue
index 588e10119..1234649e5 100644
--- a/src/client/components/follow-button.vue
+++ b/src/client/components/follow-button.vue
@@ -6,23 +6,23 @@
 >
 	<template v-if="!wait">
 		<template v-if="hasPendingFollowRequestFromYou && user.isLocked">
-			<span v-if="full">{{ $t('followRequestPending') }}</span><Fa :icon="faHourglassHalf"/>
+			<span v-if="full">{{ $ts.followRequestPending }}</span><Fa :icon="faHourglassHalf"/>
 		</template>
 		<template v-else-if="hasPendingFollowRequestFromYou && !user.isLocked"> <!-- つまりリモートフォローの場合。 -->
-			<span v-if="full">{{ $t('processing') }}</span><Fa :icon="faSpinner" pulse/>
+			<span v-if="full">{{ $ts.processing }}</span><Fa :icon="faSpinner" pulse/>
 		</template>
 		<template v-else-if="isFollowing">
-			<span v-if="full">{{ $t('unfollow') }}</span><Fa :icon="faMinus"/>
+			<span v-if="full">{{ $ts.unfollow }}</span><Fa :icon="faMinus"/>
 		</template>
 		<template v-else-if="!isFollowing && user.isLocked">
-			<span v-if="full">{{ $t('followRequest') }}</span><Fa :icon="faPlus"/>
+			<span v-if="full">{{ $ts.followRequest }}</span><Fa :icon="faPlus"/>
 		</template>
 		<template v-else-if="!isFollowing && !user.isLocked">
-			<span v-if="full">{{ $t('follow') }}</span><Fa :icon="faPlus"/>
+			<span v-if="full">{{ $ts.follow }}</span><Fa :icon="faPlus"/>
 		</template>
 	</template>
 	<template v-else>
-		<span v-if="full">{{ $t('processing') }}</span><Fa :icon="faSpinner" pulse fixed-width/>
+		<span v-if="full">{{ $ts.processing }}</span><Fa :icon="faSpinner" pulse fixed-width/>
 	</template>
 </button>
 </template>
diff --git a/src/client/components/form-dialog.vue b/src/client/components/form-dialog.vue
index add6b230d..7620abcd7 100644
--- a/src/client/components/form-dialog.vue
+++ b/src/client/components/form-dialog.vue
@@ -15,15 +15,15 @@
 	<FormBase class="xkpnjxcv">
 		<template v-for="item in Object.keys(form).filter(item => !form[item].hidden)">
 			<FormInput v-if="form[item].type === 'number'" v-model:value="values[item]" type="number" :step="form[item].step || 1">
-				<span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ $t('optional') }})</span>
+				<span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ $ts.optional }})</span>
 				<template v-if="form[item].description" #desc>{{ form[item].description }}</template>
 			</FormInput>
 			<FormInput v-else-if="form[item].type === 'string' && !form[item].multiline" v-model:value="values[item]" type="text">
-				<span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ $t('optional') }})</span>
+				<span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ $ts.optional }})</span>
 				<template v-if="form[item].description" #desc>{{ form[item].description }}</template>
 			</FormInput>
 			<FormTextarea v-else-if="form[item].type === 'string' && form[item].multiline" v-model:value="values[item]">
-				<span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ $t('optional') }})</span>
+				<span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ $ts.optional }})</span>
 				<template v-if="form[item].description" #desc>{{ form[item].description }}</template>
 			</FormTextarea>
 			<FormSwitch v-else-if="form[item].type === 'boolean'" v-model:value="values[item]">
@@ -31,11 +31,11 @@
 				<template v-if="form[item].description" #desc>{{ form[item].description }}</template>
 			</FormSwitch>
 			<FormSelect v-else-if="form[item].type === 'enum'" v-model:value="values[item]">
-				<template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ $t('optional') }})</span></template>
+				<template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ $ts.optional }})</span></template>
 				<option v-for="item in form[item].enum" :value="item.value" :key="item.value">{{ item.label }}</option>
 			</FormSelect>
 			<FormRange v-else-if="form[item].type === 'range'" v-model:value="values[item]" :min="form[item].mim" :max="form[item].max" :step="form[item].step">
-				<template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ $t('optional') }})</span></template>
+				<template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ $ts.optional }})</span></template>
 				<template v-if="form[item].description" #desc>{{ form[item].description }}</template>
 			</FormRange>
 			<FormButton v-else-if="form[item].type === 'button'" @click="form[item].action($event, values)">
diff --git a/src/client/components/form/input.vue b/src/client/components/form/input.vue
index 89551a5fc..c8c22e95c 100644
--- a/src/client/components/form/input.vue
+++ b/src/client/components/form/input.vue
@@ -44,7 +44,7 @@
 		</datalist>
 		<div class="suffix" ref="suffixEl"><slot name="suffix"></slot></div>
 	</div>
-	<button class="save _textButton" v-if="save && changed" @click="() => { changed = false; save(); }">{{ $t('save') }}</button>
+	<button class="save _textButton" v-if="save && changed" @click="() => { changed = false; save(); }">{{ $ts.save }}</button>
 	<div class="_formCaption"><slot name="desc"></slot></div>
 </div>
 </template>
diff --git a/src/client/components/form/pagination.vue b/src/client/components/form/pagination.vue
index 7dcaedf9b..df65d9684 100644
--- a/src/client/components/form/pagination.vue
+++ b/src/client/components/form/pagination.vue
@@ -6,7 +6,7 @@
 		<slot name="empty"></slot>
 	</div>
 	<FormButton v-show="more" class="button" @click="fetchMore" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }" primary>
-		<template v-if="!moreFetching">{{ $t('loadMore') }}</template>
+		<template v-if="!moreFetching">{{ $ts.loadMore }}</template>
 		<template v-if="moreFetching"><MkLoading inline/></template>
 	</FormButton>
 </FormGroup>
diff --git a/src/client/components/form/textarea.vue b/src/client/components/form/textarea.vue
index d84b48197..711cd5012 100644
--- a/src/client/components/form/textarea.vue
+++ b/src/client/components/form/textarea.vue
@@ -14,7 +14,7 @@
 			@blur="focused = false"
 		></textarea>
 	</div>
-	<button class="save _textButton" v-if="save && changed" @click="() => { changed = false; save(); }">{{ $t('save') }}</button>
+	<button class="save _textButton" v-if="save && changed" @click="() => { changed = false; save(); }">{{ $ts.save }}</button>
 	<div class="_formCaption"><slot name="desc"></slot></div>
 </div>
 </template>
diff --git a/src/client/components/global/a.vue b/src/client/components/global/a.vue
index e93600301..671245fec 100644
--- a/src/client/components/global/a.vue
+++ b/src/client/components/global/a.vue
@@ -58,31 +58,31 @@ export default defineComponent({
 				text: this.to,
 			}, {
 				icon: faWindowMaximize,
-				text: this.$t('openInWindow'),
+				text: this.$ts.openInWindow,
 				action: () => {
 					os.pageWindow(this.to);
 				}
 			}, this.sideViewHook ? {
 				icon: faColumns,
-				text: this.$t('openInSideView'),
+				text: this.$ts.openInSideView,
 				action: () => {
 					this.sideViewHook(this.to);
 				}
 			} : undefined, {
 				icon: faExpandAlt,
-				text: this.$t('showInPage'),
+				text: this.$ts.showInPage,
 				action: () => {
 					this.$router.push(this.to);
 				}
 			}, null, {
 				icon: faExternalLinkAlt,
-				text: this.$t('openInNewTab'),
+				text: this.$ts.openInNewTab,
 				action: () => {
 					window.open(this.to, '_blank');
 				}
 			}, {
 				icon: faLink,
-				text: this.$t('copyLink'),
+				text: this.$ts.copyLink,
 				action: () => {
 					copyToClipboard(`${url}${this.to}`);
 				}
diff --git a/src/client/components/global/error.vue b/src/client/components/global/error.vue
index e4c76faa3..b78973ff8 100644
--- a/src/client/components/global/error.vue
+++ b/src/client/components/global/error.vue
@@ -2,8 +2,8 @@
 <transition :name="$store.state.animation ? 'zoom' : ''" appear>
 	<div class="mjndxjcg">
 		<img src="https://xn--931a.moe/assets/error.jpg" class="_ghost"/>
-		<p><Fa :icon="faExclamationTriangle"/> {{ $t('somethingHappened') }}</p>
-		<MkButton @click="() => $emit('retry')" class="button">{{ $t('retry') }}</MkButton>
+		<p><Fa :icon="faExclamationTriangle"/> {{ $ts.somethingHappened }}</p>
+		<MkButton @click="() => $emit('retry')" class="button">{{ $ts.retry }}</MkButton>
 	</div>
 </transition>
 </template>
diff --git a/src/client/components/global/i18n.ts b/src/client/components/global/i18n.ts
index 603c07ca9..b1142caf9 100644
--- a/src/client/components/global/i18n.ts
+++ b/src/client/components/global/i18n.ts
@@ -1,15 +1,37 @@
-import { h, Fragment, defineComponent } from 'vue';
-import type { SetupContext, VNodeChild, RenderFunction } from 'vue';
+import { h, defineComponent } from 'vue';
 
 export default defineComponent({
 	props: {
 		src: {
 			type: String,
-			required: true
+			required: true,
+		},
+		tag: {
+			type: String,
+			required: false,
+			default: 'span',
 		},
 	},
 	render() {
-		// TODO
-		return h('span', this.src);
+		let str = this.src;
+		const parsed = [] as (string | { arg: string; })[];
+		while (true) {
+			const nextBracketOpen = str.indexOf('{');
+			const nextBracketClose = str.indexOf('}');
+
+			if (nextBracketOpen === -1) {
+				parsed.push(str);
+				break;
+			} else {
+				if (nextBracketOpen > 0) parsed.push(str.substr(0, nextBracketOpen));
+				parsed.push({
+					arg: str.substring(nextBracketOpen + 1, nextBracketClose)
+				});
+			}
+
+			str = str.substr(nextBracketClose + 1);
+		}
+
+		return h(this.tag, parsed.map(x => typeof x === 'string' ? x : this.$slots[x.arg]()));
 	}
 });
diff --git a/src/client/components/global/time.vue b/src/client/components/global/time.vue
index 544746c24..6a330a230 100644
--- a/src/client/components/global/time.vue
+++ b/src/client/components/global/time.vue
@@ -44,9 +44,9 @@ export default defineComponent({
 				ago >= 3600     ? this.$t('_ago.hoursAgo',   { n: (~~(ago / 3600)).toString() }) :
 				ago >= 60       ? this.$t('_ago.minutesAgo', { n: (~~(ago / 60)).toString() }) :
 				ago >= 10       ? this.$t('_ago.secondsAgo', { n: (~~(ago % 60)).toString() }) :
-				ago >= -1       ? this.$t('_ago.justNow') :
-				ago <  -1       ? this.$t('_ago.future') :
-				this.$t('_ago.unknown'));
+				ago >= -1       ? this.$ts._ago.justNow :
+				ago <  -1       ? this.$ts._ago.future :
+				this.$ts._ago.unknown);
 		}
 	},
 	created() {
diff --git a/src/client/components/google.vue b/src/client/components/google.vue
index a63a1476a..fe692a925 100644
--- a/src/client/components/google.vue
+++ b/src/client/components/google.vue
@@ -1,7 +1,7 @@
 <template>
 <div class="mk-google">
 	<input type="search" v-model="query" :placeholder="q">
-	<button @click="search"><Fa :icon="faSearch"/> {{ $t('search') }}</button>
+	<button @click="search"><Fa :icon="faSearch"/> {{ $ts.search }}</button>
 </div>
 </template>
 
diff --git a/src/client/components/instance-stats.vue b/src/client/components/instance-stats.vue
index 652cedceb..e9b496180 100644
--- a/src/client/components/instance-stats.vue
+++ b/src/client/components/instance-stats.vue
@@ -3,80 +3,80 @@
 	<div class="stats" v-if="info">
 		<div class="_panel">
 			<div>
-				<b><Fa :icon="faUser"/>{{ $t('users') }}</b>
-				<small>{{ $t('local') }}</small>
+				<b><Fa :icon="faUser"/>{{ $ts.users }}</b>
+				<small>{{ $ts.local }}</small>
 			</div>
 			<div>
 				<dl class="total">
-					<dt>{{ $t('total') }}</dt>
+					<dt>{{ $ts.total }}</dt>
 					<dd>{{ number(info.originalUsersCount) }}</dd>
 				</dl>
 				<dl class="diff" :class="{ inc: usersLocalDoD > 0 }">
-					<dt>{{ $t('dayOverDayChanges') }}</dt>
+					<dt>{{ $ts.dayOverDayChanges }}</dt>
 					<dd>{{ number(usersLocalDoD) }}</dd>
 				</dl>
 				<dl class="diff" :class="{ inc: usersLocalWoW > 0 }">
-					<dt>{{ $t('weekOverWeekChanges') }}</dt>
+					<dt>{{ $ts.weekOverWeekChanges }}</dt>
 					<dd>{{ number(usersLocalWoW) }}</dd>
 				</dl>
 			</div>
 		</div>
 		<div class="_panel">
 			<div>
-				<b><Fa :icon="faUser"/>{{ $t('users') }}</b>
-				<small>{{ $t('remote') }}</small>
+				<b><Fa :icon="faUser"/>{{ $ts.users }}</b>
+				<small>{{ $ts.remote }}</small>
 			</div>
 			<div>
 				<dl class="total">
-					<dt>{{ $t('total') }}</dt>
+					<dt>{{ $ts.total }}</dt>
 					<dd>{{ number((info.usersCount - info.originalUsersCount)) }}</dd>
 				</dl>
 				<dl class="diff" :class="{ inc: usersRemoteDoD > 0 }">
-					<dt>{{ $t('dayOverDayChanges') }}</dt>
+					<dt>{{ $ts.dayOverDayChanges }}</dt>
 					<dd>{{ number(usersRemoteDoD) }}</dd>
 				</dl>
 				<dl class="diff" :class="{ inc: usersRemoteWoW > 0 }">
-					<dt>{{ $t('weekOverWeekChanges') }}</dt>
+					<dt>{{ $ts.weekOverWeekChanges }}</dt>
 					<dd>{{ number(usersRemoteWoW) }}</dd>
 				</dl>
 			</div>
 		</div>
 		<div class="_panel">
 			<div>
-				<b><Fa :icon="faPencilAlt"/>{{ $t('notes') }}</b>
-				<small>{{ $t('local') }}</small>
+				<b><Fa :icon="faPencilAlt"/>{{ $ts.notes }}</b>
+				<small>{{ $ts.local }}</small>
 			</div>
 			<div>
 				<dl class="total">
-					<dt>{{ $t('total') }}</dt>
+					<dt>{{ $ts.total }}</dt>
 					<dd>{{ number(info.originalNotesCount) }}</dd>
 				</dl>
 				<dl class="diff" :class="{ inc: notesLocalDoD > 0 }">
-					<dt>{{ $t('dayOverDayChanges') }}</dt>
+					<dt>{{ $ts.dayOverDayChanges }}</dt>
 					<dd>{{ number(notesLocalDoD) }}</dd>
 				</dl>
 				<dl class="diff" :class="{ inc: notesLocalWoW > 0 }">
-					<dt>{{ $t('weekOverWeekChanges') }}</dt>
+					<dt>{{ $ts.weekOverWeekChanges }}</dt>
 					<dd>{{ number(notesLocalWoW) }}</dd>
 				</dl>
 			</div>
 		</div>
 		<div class="_panel">
 			<div>
-				<b><Fa :icon="faPencilAlt"/>{{ $t('notes') }}</b>
-				<small>{{ $t('remote') }}</small>
+				<b><Fa :icon="faPencilAlt"/>{{ $ts.notes }}</b>
+				<small>{{ $ts.remote }}</small>
 			</div>
 			<div>
 				<dl class="total">
-					<dt>{{ $t('total') }}</dt>
+					<dt>{{ $ts.total }}</dt>
 					<dd>{{ number((info.notesCount - info.originalNotesCount)) }}</dd>
 				</dl>
 				<dl class="diff" :class="{ inc: notesRemoteDoD > 0 }">
-					<dt>{{ $t('dayOverDayChanges') }}</dt>
+					<dt>{{ $ts.dayOverDayChanges }}</dt>
 					<dd>{{ number(notesRemoteDoD) }}</dd>
 				</dl>
 				<dl class="diff" :class="{ inc: notesRemoteWoW > 0 }">
-					<dt>{{ $t('weekOverWeekChanges') }}</dt>
+					<dt>{{ $ts.weekOverWeekChanges }}</dt>
 					<dd>{{ number(notesRemoteWoW) }}</dd>
 				</dl>
 			</div>
@@ -84,35 +84,35 @@
 	</div>
 
 	<section class="_card">
-		<div class="_title" style="position: relative;"><Fa :icon="faChartBar"/> {{ $t('statistics') }}<button @click="fetchChart" class="_button" style="position: absolute; right: 0; bottom: 0; top: 0; padding: inherit;"><Fa :icon="faSync"/></button></div>
+		<div class="_title" style="position: relative;"><Fa :icon="faChartBar"/> {{ $ts.statistics }}<button @click="fetchChart" class="_button" style="position: absolute; right: 0; bottom: 0; top: 0; padding: inherit;"><Fa :icon="faSync"/></button></div>
 		<div class="_content" style="margin-top: -8px;">
 			<div class="selects" style="display: flex;">
 				<MkSelect v-model:value="chartSrc" style="margin: 0; flex: 1;">
-					<optgroup :label="$t('federation')">
-						<option value="federation-instances">{{ $t('_charts.federationInstancesIncDec') }}</option>
-						<option value="federation-instances-total">{{ $t('_charts.federationInstancesTotal') }}</option>
+					<optgroup :label="$ts.federation">
+						<option value="federation-instances">{{ $ts._charts.federationInstancesIncDec }}</option>
+						<option value="federation-instances-total">{{ $ts._charts.federationInstancesTotal }}</option>
 					</optgroup>
-					<optgroup :label="$t('users')">
-						<option value="users">{{ $t('_charts.usersIncDec') }}</option>
-						<option value="users-total">{{ $t('_charts.usersTotal') }}</option>
-						<option value="active-users">{{ $t('_charts.activeUsers') }}</option>
+					<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>
-					<optgroup :label="$t('notes')">
-						<option value="notes">{{ $t('_charts.notesIncDec') }}</option>
-						<option value="local-notes">{{ $t('_charts.localNotesIncDec') }}</option>
-						<option value="remote-notes">{{ $t('_charts.remoteNotesIncDec') }}</option>
-						<option value="notes-total">{{ $t('_charts.notesTotal') }}</option>
+					<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>
-					<optgroup :label="$t('drive')">
-						<option value="drive-files">{{ $t('_charts.filesIncDec') }}</option>
-						<option value="drive-files-total">{{ $t('_charts.filesTotal') }}</option>
-						<option value="drive">{{ $t('_charts.storageUsageIncDec') }}</option>
-						<option value="drive-total">{{ $t('_charts.storageUsageTotal') }}</option>
+					<optgroup :label="$ts.drive">
+						<option value="drive-files">{{ $ts._charts.filesIncDec }}</option>
+						<option value="drive-files-total">{{ $ts._charts.filesTotal }}</option>
+						<option value="drive">{{ $ts._charts.storageUsageIncDec }}</option>
+						<option value="drive-total">{{ $ts._charts.storageUsageTotal }}</option>
 					</optgroup>
 				</MkSelect>
 				<MkSelect v-model:value="chartSpan" style="margin: 0;">
-					<option value="hour">{{ $t('perHour') }}</option>
-					<option value="day">{{ $t('perDay') }}</option>
+					<option value="hour">{{ $ts.perHour }}</option>
+					<option value="day">{{ $ts.perDay }}</option>
 				</MkSelect>
 			</div>
 			<canvas ref="chart"></canvas>
diff --git a/src/client/components/launch-pad.vue b/src/client/components/launch-pad.vue
index 24af78247..f732b675e 100644
--- a/src/client/components/launch-pad.vue
+++ b/src/client/components/launch-pad.vue
@@ -18,7 +18,7 @@
 		<div class="sub">
 			<MkA to="/docs" @click.passive="close()">
 				<Fa :icon="faQuestionCircle" class="icon"/>
-				<div class="text">{{ $t('help') }}</div>
+				<div class="text">{{ $ts.help }}</div>
 			</MkA>
 			<MkA to="/about" @click.passive="close()">
 				<Fa :icon="faInfoCircle" class="icon"/>
@@ -26,7 +26,7 @@
 			</MkA>
 			<MkA to="/about-misskey" @click.passive="close()">
 				<Fa :icon="faInfoCircle" class="icon"/>
-				<div class="text">{{ $t('aboutMisskey') }}</div>
+				<div class="text">{{ $ts.aboutMisskey }}</div>
 			</MkA>
 		</div>
 	</div>
diff --git a/src/client/components/media-banner.vue b/src/client/components/media-banner.vue
index d90d1186e..a2e1776d0 100644
--- a/src/client/components/media-banner.vue
+++ b/src/client/components/media-banner.vue
@@ -2,8 +2,8 @@
 <div class="mk-media-banner">
 	<div class="sensitive" v-if="media.isSensitive && hide" @click="hide = false">
 		<span class="icon"><Fa :icon="faExclamationTriangle"/></span>
-		<b>{{ $t('sensitive') }}</b>
-		<span>{{ $t('clickToShow') }}</span>
+		<b>{{ $ts.sensitive }}</b>
+		<span>{{ $ts.clickToShow }}</span>
 	</div>
 	<div class="audio" v-else-if="media.type.startsWith('audio') && media.type !== 'audio/midi'">
 		<audio class="audio"
diff --git a/src/client/components/media-image.vue b/src/client/components/media-image.vue
index 55d334987..9f3898ec5 100644
--- a/src/client/components/media-image.vue
+++ b/src/client/components/media-image.vue
@@ -3,8 +3,8 @@
 	<ImgWithBlurhash class="bg" :hash="image.blurhash" :title="image.name"/>
 	<div class="text">
 		<div>
-			<b><Fa :icon="faExclamationTriangle"/> {{ $t('sensitive') }}</b>
-			<span>{{ $t('clickToShow') }}</span>
+			<b><Fa :icon="faExclamationTriangle"/> {{ $ts.sensitive }}</b>
+			<span>{{ $ts.clickToShow }}</span>
 		</div>
 	</div>
 </div>
diff --git a/src/client/components/media-video.vue b/src/client/components/media-video.vue
index 78b011157..378894720 100644
--- a/src/client/components/media-video.vue
+++ b/src/client/components/media-video.vue
@@ -1,8 +1,8 @@
 <template>
 <div class="icozogqfvdetwohsdglrbswgrejoxbdj" v-if="hide" @click="hide = false">
 	<div>
-		<b><Fa :icon="faExclamationTriangle"/> {{ $t('sensitive') }}</b>
-		<span>{{ $t('clickToShow') }}</span>
+		<b><Fa :icon="faExclamationTriangle"/> {{ $ts.sensitive }}</b>
+		<span>{{ $ts.clickToShow }}</span>
 	</div>
 </div>
 <div class="kkjnbbplepmiyuadieoenjgutgcmtsvu" v-else>
diff --git a/src/client/components/mention.vue b/src/client/components/mention.vue
index 69ea99b87..12a620af9 100644
--- a/src/client/components/mention.vue
+++ b/src/client/components/mention.vue
@@ -1,6 +1,6 @@
 <template>
 <MkA class="ldlomzub" :class="{ isMe }" :to="url" v-user-preview="canonical" v-if="url.startsWith('/')">
-	<span class="me" v-if="isMe">{{ $t('you') }}</span>
+	<span class="me" v-if="isMe">{{ $ts.you }}</span>
 	<span class="main">
 		<span class="username">@{{ username }}</span>
 		<span class="host" v-if="(host != localHost) || $store.state.showFullAcct">@{{ toUnicode(host) }}</span>
diff --git a/src/client/components/note.vue b/src/client/components/note.vue
index 6d5750451..2d87cb29a 100644
--- a/src/client/components/note.vue
+++ b/src/client/components/note.vue
@@ -10,13 +10,13 @@
 >
 	<XSub v-for="note in conversation" class="reply-to-more" :key="note.id" :note="note"/>
 	<XSub :note="appearNote.reply" class="reply-to" v-if="appearNote.reply"/>
-	<div class="info" v-if="pinned"><Fa :icon="faThumbtack"/> {{ $t('pinnedNote') }}</div>
-	<div class="info" v-if="appearNote._prId_"><Fa :icon="faBullhorn"/> {{ $t('promotion') }}<button class="_textButton hide" @click="readPromo()">{{ $t('hideThisNote') }} <Fa :icon="faTimes"/></button></div>
-	<div class="info" v-if="appearNote._featuredId_"><Fa :icon="faBolt"/> {{ $t('featured') }}</div>
+	<div class="info" v-if="pinned"><Fa :icon="faThumbtack"/> {{ $ts.pinnedNote }}</div>
+	<div class="info" v-if="appearNote._prId_"><Fa :icon="faBullhorn"/> {{ $ts.promotion }}<button class="_textButton hide" @click="readPromo()">{{ $ts.hideThisNote }} <Fa :icon="faTimes"/></button></div>
+	<div class="info" v-if="appearNote._featuredId_"><Fa :icon="faBolt"/> {{ $ts.featured }}</div>
 	<div class="renote" v-if="isRenote">
 		<MkAvatar class="avatar" :user="note.user"/>
 		<Fa :icon="faRetweet"/>
-		<I18n src="renotedBy" tag="span">
+		<I18n :src="$ts.renotedBy" tag="span">
 			<template #user>
 				<MkA class="name" :to="userPage(note.user)" v-user-preview="note.userId">
 					<MkUserName :user="note.user"/>
@@ -48,7 +48,7 @@
 				</p>
 				<div class="content" v-show="appearNote.cw == null || showContent">
 					<div class="text">
-						<span v-if="appearNote.isHidden" style="opacity: 0.5">({{ $t('private') }})</span>
+						<span v-if="appearNote.isHidden" style="opacity: 0.5">({{ $ts.private }})</span>
 						<MkA class="reply" v-if="appearNote.replyId" :to="`/notes/${appearNote.replyId}`"><Fa :icon="faReply"/></MkA>
 						<Mfm v-if="appearNote.text" :text="appearNote.text" :author="appearNote.user" :i="$i" :custom-emojis="appearNote.emojis"/>
 						<a class="rp" v-if="appearNote.renote != null">RN:</a>
@@ -90,7 +90,7 @@
 	<XSub 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="userSaysSomething" tag="small">
+	<I18n :src="$ts.userSaysSomething" tag="small">
 		<template #name>
 			<MkA class="name" :to="userPage(appearNote.user)" v-user-preview="appearNote.userId">
 				<MkUserName :user="appearNote.user"/>
@@ -469,7 +469,7 @@ export default defineComponent({
 			pleaseLogin();
 			this.blur();
 			os.modalMenu([{
-				text: this.$t('renote'),
+				text: this.$ts.renote,
 				icon: faRetweet,
 				action: () => {
 					os.api('notes/create', {
@@ -477,7 +477,7 @@ export default defineComponent({
 					});
 				}
 			}, {
-				text: this.$t('quote'),
+				text: this.$ts.quote,
 				icon: faQuoteRight,
 				action: () => {
 					os.post({
@@ -495,18 +495,18 @@ export default defineComponent({
 			}, undefined, (res: any) => {
 				os.dialog({
 					type: 'success',
-					text: this.$t('renoted'),
+					text: this.$ts.renoted,
 				});
 			}, (e: Error) => {
 				if (e.id === 'b5c90186-4ab0-49c8-9bba-a1f76c282ba4') {
 					os.dialog({
 						type: 'error',
-						text: this.$t('cantRenote'),
+						text: this.$ts.cantRenote,
 					});
 				} else if (e.id === 'fd4cc33e-2a37-48dd-99cc-9b806eb2031a') {
 					os.dialog({
 						type: 'error',
-						text: this.$t('cantReRenote'),
+						text: this.$ts.cantReRenote,
 					});
 				}
 			});
@@ -553,18 +553,18 @@ export default defineComponent({
 			}, undefined, (res: any) => {
 				os.dialog({
 					type: 'success',
-					text: this.$t('favorited'),
+					text: this.$ts.favorited,
 				});
 			}, (e: Error) => {
 				if (e.id === 'a402c12b-34dd-41d2-97d8-4d2ffd96a1a6') {
 					os.dialog({
 						type: 'error',
-						text: this.$t('alreadyFavorited'),
+						text: this.$ts.alreadyFavorited,
 					});
 				} else if (e.id === '6dd26674-e060-4816-909a-45ba3f4da458') {
 					os.dialog({
 						type: 'error',
-						text: this.$t('cantFavorite'),
+						text: this.$ts.cantFavorite,
 					});
 				}
 			});
@@ -573,7 +573,7 @@ export default defineComponent({
 		del() {
 			os.dialog({
 				type: 'warning',
-				text: this.$t('noteDeleteConfirm'),
+				text: this.$ts.noteDeleteConfirm,
 				showCancelButton: true
 			}).then(({ canceled }) => {
 				if (canceled) return;
@@ -587,7 +587,7 @@ export default defineComponent({
 		delEdit() {
 			os.dialog({
 				type: 'warning',
-				text: this.$t('deleteAndEditConfirm'),
+				text: this.$ts.deleteAndEditConfirm,
 				showCancelButton: true
 			}).then(({ canceled }) => {
 				if (canceled) return;
@@ -621,15 +621,15 @@ export default defineComponent({
 
 				menu = [{
 					icon: faCopy,
-					text: this.$t('copyContent'),
+					text: this.$ts.copyContent,
 					action: this.copyContent
 				}, {
 					icon: faLink,
-					text: this.$t('copyLink'),
+					text: this.$ts.copyLink,
 					action: this.copyLink
 				}, (this.appearNote.url || this.appearNote.uri) ? {
 					icon: faExternalLinkSquareAlt,
-					text: this.$t('showOnRemote'),
+					text: this.$ts.showOnRemote,
 					action: () => {
 						window.open(this.appearNote.url || this.appearNote.uri, '_blank');
 					}
@@ -637,41 +637,41 @@ export default defineComponent({
 				null,
 				statePromise.then(state => state.isFavorited ? {
 					icon: faStar,
-					text: this.$t('unfavorite'),
+					text: this.$ts.unfavorite,
 					action: () => this.toggleFavorite(false)
 				} : {
 					icon: faStar,
-					text: this.$t('favorite'),
+					text: this.$ts.favorite,
 					action: () => this.toggleFavorite(true)
 				}),
 				{
 					icon: faPaperclip,
-					text: this.$t('clip'),
+					text: this.$ts.clip,
 					action: () => this.clip()
 				},
 				(this.appearNote.userId != this.$i.id) ? statePromise.then(state => state.isWatching ? {
 					icon: faEyeSlash,
-					text: this.$t('unwatch'),
+					text: this.$ts.unwatch,
 					action: () => this.toggleWatch(false)
 				} : {
 					icon: faEye,
-					text: this.$t('watch'),
+					text: this.$ts.watch,
 					action: () => this.toggleWatch(true)
 				}) : undefined,
 				this.appearNote.userId == this.$i.id ? (this.$i.pinnedNoteIds || []).includes(this.appearNote.id) ? {
 					icon: faThumbtack,
-					text: this.$t('unpin'),
+					text: this.$ts.unpin,
 					action: () => this.togglePin(false)
 				} : {
 					icon: faThumbtack,
-					text: this.$t('pin'),
+					text: this.$ts.pin,
 					action: () => this.togglePin(true)
 				} : undefined,
 				...(this.$i.isModerator || this.$i.isAdmin ? [
 					null,
 					{
 						icon: faBullhorn,
-						text: this.$t('promote'),
+						text: this.$ts.promote,
 						action: this.promote
 					}]
 					: []
@@ -680,7 +680,7 @@ export default defineComponent({
 					null,
 					{
 						icon: faExclamationCircle,
-						text: this.$t('reportAbuse'),
+						text: this.$ts.reportAbuse,
 						action: () => {
 							const u = `${url}/notes/${this.appearNote.id}`;
 							os.popup(import('@/components/abuse-report-window.vue'), {
@@ -695,12 +695,12 @@ export default defineComponent({
 					null,
 					this.appearNote.userId == this.$i.id ? {
 						icon: faEdit,
-						text: this.$t('deleteAndEdit'),
+						text: this.$ts.deleteAndEdit,
 						action: this.delEdit
 					} : undefined,
 					{
 						icon: faTrashAlt,
-						text: this.$t('delete'),
+						text: this.$ts.delete,
 						danger: true,
 						action: this.del
 					}]
@@ -710,15 +710,15 @@ export default defineComponent({
 			} else {
 				menu = [{
 					icon: faCopy,
-					text: this.$t('copyContent'),
+					text: this.$ts.copyContent,
 					action: this.copyContent
 				}, {
 					icon: faLink,
-					text: this.$t('copyLink'),
+					text: this.$ts.copyLink,
 					action: this.copyLink
 				}, (this.appearNote.url || this.appearNote.uri) ? {
 					icon: faExternalLinkSquareAlt,
-					text: this.$t('showOnRemote'),
+					text: this.$ts.showOnRemote,
 					action: () => {
 						window.open(this.appearNote.url || this.appearNote.uri, '_blank');
 					}
@@ -760,7 +760,7 @@ export default defineComponent({
 		showRenoteMenu(viaKeyboard = false) {
 			if (!this.isMyRenote) return;
 			os.modalMenu([{
-				text: this.$t('unrenote'),
+				text: this.$ts.unrenote,
 				icon: faTrashAlt,
 				danger: true,
 				action: () => {
@@ -795,7 +795,7 @@ export default defineComponent({
 				if (e.id === '72dab508-c64d-498f-8740-a8eec1ba385a') {
 					os.dialog({
 						type: 'error',
-						text: this.$t('pinLimitExceeded')
+						text: this.$ts.pinLimitExceeded
 					});
 				}
 			});
@@ -805,22 +805,22 @@ export default defineComponent({
 			const clips = await os.api('clips/list');
 			os.modalMenu([{
 				icon: faPlus,
-				text: this.$t('createNew'),
+				text: this.$ts.createNew,
 				action: async () => {
-					const { canceled, result } = await os.form(this.$t('createNewClip'), {
+					const { canceled, result } = await os.form(this.$ts.createNewClip, {
 						name: {
 							type: 'string',
-							label: this.$t('name')
+							label: this.$ts.name
 						},
 						description: {
 							type: 'string',
 							required: false,
 							multiline: true,
-							label: this.$t('description')
+							label: this.$ts.description
 						},
 						isPublic: {
 							type: 'boolean',
-							label: this.$t('public'),
+							label: this.$ts.public,
 							default: false
 						}
 					});
@@ -841,7 +841,7 @@ export default defineComponent({
 
 		async promote() {
 			const { canceled, result: days } = await os.dialog({
-				title: this.$t('numberOfDays'),
+				title: this.$ts.numberOfDays,
 				input: { type: 'number' }
 			});
 
diff --git a/src/client/components/notes.vue b/src/client/components/notes.vue
index ee9668733..4deed33bb 100644
--- a/src/client/components/notes.vue
+++ b/src/client/components/notes.vue
@@ -2,14 +2,14 @@
 <div class="_list_">
 	<div class="_fullinfo" v-if="empty">
 		<img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
-		<div>{{ $t('noNotes') }}</div>
+		<div>{{ $ts.noNotes }}</div>
 	</div>
 
 	<MkError v-if="error" @retry="init()"/>
 
 	<div v-show="more && reversed" style="margin-bottom: var(--margin);">
 		<button class="_loadMore" @click="fetchMore" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }">
-			<template v-if="!moreFetching">{{ $t('loadMore') }}</template>
+			<template v-if="!moreFetching">{{ $ts.loadMore }}</template>
 			<template v-if="moreFetching"><MkLoading inline/></template>
 		</button>
 	</div>
@@ -20,7 +20,7 @@
 
 	<div v-show="more && !reversed" style="margin-top: var(--margin);">
 		<button class="_loadMore" v-appear="$store.state.enableInfiniteScroll ? fetchMore : null" @click="fetchMore" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }">
-			<template v-if="!moreFetching">{{ $t('loadMore') }}</template>
+			<template v-if="!moreFetching">{{ $ts.loadMore }}</template>
 			<template v-if="moreFetching"><MkLoading inline/></template>
 		</button>
 	</div>
diff --git a/src/client/components/notification-setting-window.vue b/src/client/components/notification-setting-window.vue
index e6d109e3a..1c19de9ff 100644
--- a/src/client/components/notification-setting-window.vue
+++ b/src/client/components/notification-setting-window.vue
@@ -8,17 +8,17 @@
 	@close="$refs.dialog.close()"
 	@closed="$emit('closed')"
 >
-	<template #header>{{ $t('notificationSetting') }}</template>
+	<template #header>{{ $ts.notificationSetting }}</template>
 	<div v-if="showGlobalToggle" class="_section">
 		<MkSwitch v-model:value="useGlobalSetting">
-			{{ $t('useGlobalSetting') }}
-			<template #desc>{{ $t('useGlobalSettingDesc') }}</template>
+			{{ $ts.useGlobalSetting }}
+			<template #desc>{{ $ts.useGlobalSettingDesc }}</template>
 		</MkSwitch>
 	</div>
 	<div v-if="!useGlobalSetting" class="_section">
-		<MkInfo>{{ $t('notificationSettingDesc') }}</MkInfo>
-		<MkButton inline @click="disableAll">{{ $t('disableAll') }}</MkButton>
-		<MkButton inline @click="enableAll">{{ $t('enableAll') }}</MkButton>
+		<MkInfo>{{ $ts.notificationSettingDesc }}</MkInfo>
+		<MkButton inline @click="disableAll">{{ $ts.disableAll }}</MkButton>
+		<MkButton inline @click="enableAll">{{ $ts.enableAll }}</MkButton>
 		<MkSwitch v-for="type in notificationTypes" :key="type" v-model:value="typesMap[type]">{{ $t(`_notification._types.${type}`) }}</MkSwitch>
 	</div>
 </XModalWindow>
diff --git a/src/client/components/notification.vue b/src/client/components/notification.vue
index c85457a56..0c6be82bb 100644
--- a/src/client/components/notification.vue
+++ b/src/client/components/notification.vue
@@ -46,10 +46,10 @@
 			<Mfm :text="getNoteSummary(notification.note)" :plain="true" :nowrap="!full" :custom-emojis="notification.note.emojis"/>
 			<Fa :icon="faQuoteRight"/>
 		</MkA>
-		<span v-if="notification.type === 'follow'" class="text" style="opacity: 0.6;">{{ $t('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;">{{ $t('followRequestAccepted') }}</span>
-		<span v-if="notification.type === 'receiveFollowRequest'" class="text" style="opacity: 0.6;">{{ $t('receiveFollowRequest') }}<div v-if="full && !followRequestDone"><button class="_textButton" @click="acceptFollowRequest()">{{ $t('accept') }}</button> | <button class="_textButton" @click="rejectFollowRequest()">{{ $t('reject') }}</button></div></span>
-		<span v-if="notification.type === 'groupInvited'" class="text" style="opacity: 0.6;">{{ $t('groupInvited') }}: <b>{{ notification.invitation.group.name }}</b><div v-if="full && !groupInviteDone"><button class="_textButton" @click="acceptGroupInvitation()">{{ $t('accept') }}</button> | <button class="_textButton" @click="rejectGroupInvitation()">{{ $t('reject') }}</button></div></span>
+		<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 === 'app'" class="text">
 			<Mfm :text="notification.body" :nowrap="!full"/>
 		</span>
diff --git a/src/client/components/notifications.vue b/src/client/components/notifications.vue
index 03db8c755..9759cc239 100644
--- a/src/client/components/notifications.vue
+++ b/src/client/components/notifications.vue
@@ -6,11 +6,11 @@
 	</XList>
 
 	<button class="_loadMore" v-appear="$store.state.enableInfiniteScroll ? fetchMore : null" @click="fetchMore" v-show="more" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }">
-		<template v-if="!moreFetching">{{ $t('loadMore') }}</template>
+		<template v-if="!moreFetching">{{ $ts.loadMore }}</template>
 		<template v-if="moreFetching"><MkLoading inline/></template>
 	</button>
 
-	<p class="empty" v-if="empty">{{ $t('noNotifications') }}</p>
+	<p class="empty" v-if="empty">{{ $ts.noNotifications }}</p>
 
 	<MkError v-if="error" @retry="init()"/>
 </div>
diff --git a/src/client/components/page-window.vue b/src/client/components/page-window.vue
index 7033c872e..5ea0e0801 100644
--- a/src/client/components/page-window.vue
+++ b/src/client/components/page-window.vue
@@ -90,29 +90,29 @@ export default defineComponent({
 				text: this.path,
 			}, {
 				icon: faExpandAlt,
-				text: this.$t('showInPage'),
+				text: this.$ts.showInPage,
 				action: this.expand
 			}, this.sideViewHook ? {
 				icon: faColumns,
-				text: this.$t('openInSideView'),
+				text: this.$ts.openInSideView,
 				action: () => {
 					this.sideViewHook(this.path);
 					this.$refs.window.close();
 				}
 			} : undefined, {
 				icon: faExternalLinkAlt,
-				text: this.$t('popout'),
+				text: this.$ts.popout,
 				action: this.popout
 			}, null, {
 				icon: faExternalLinkAlt,
-				text: this.$t('openInNewTab'),
+				text: this.$ts.openInNewTab,
 				action: () => {
 					window.open(this.url, '_blank');
 					this.$refs.window.close();
 				}
 			}, {
 				icon: faLink,
-				text: this.$t('copyLink'),
+				text: this.$ts.copyLink,
 				action: () => {
 					copyToClipboard(this.url);
 				}
diff --git a/src/client/components/poll-editor.vue b/src/client/components/poll-editor.vue
index 8d7e16387..49454f23c 100644
--- a/src/client/components/poll-editor.vue
+++ b/src/client/components/poll-editor.vue
@@ -1,7 +1,7 @@
 <template>
 <div class="zmdxowus">
 	<p class="caution" v-if="choices.length < 2">
-		<Fa :icon="faExclamationTriangle"/>{{ $t('_poll.noOnlyOneChoice') }}
+		<Fa :icon="faExclamationTriangle"/>{{ $ts._poll.noOnlyOneChoice }}
 	</p>
 	<ul ref="choices">
 		<li v-for="(choice, i) in choices" :key="i">
@@ -13,34 +13,34 @@
 			</button>
 		</li>
 	</ul>
-	<MkButton class="add" v-if="choices.length < 10" @click="add">{{ $t('add') }}</MkButton>
-	<MkButton class="add" v-else disabled>{{ $t('_poll.noMore') }}</MkButton>
+	<MkButton class="add" v-if="choices.length < 10" @click="add">{{ $ts.add }}</MkButton>
+	<MkButton class="add" v-else disabled>{{ $ts._poll.noMore }}</MkButton>
 	<section>
-		<MkSwitch v-model:value="multiple">{{ $t('_poll.canMultipleVote') }}</MkSwitch>
+		<MkSwitch v-model:value="multiple">{{ $ts._poll.canMultipleVote }}</MkSwitch>
 		<div>
 			<MkSelect v-model:value="expiration">
-				<template #label>{{ $t('_poll.expiration') }}</template>
-				<option value="infinite">{{ $t('_poll.infinite') }}</option>
-				<option value="at">{{ $t('_poll.at') }}</option>
-				<option value="after">{{ $t('_poll.after') }}</option>
+				<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>
 			</MkSelect>
 			<section v-if="expiration === 'at'">
 				<MkInput v-model:value="atDate" type="date" class="input">
-					<span>{{ $t('_poll.deadlineDate') }}</span>
+					<span>{{ $ts._poll.deadlineDate }}</span>
 				</MkInput>
 				<MkInput v-model:value="atTime" type="time" class="input">
-					<span>{{ $t('_poll.deadlineTime') }}</span>
+					<span>{{ $ts._poll.deadlineTime }}</span>
 				</MkInput>
 			</section>
 			<section v-if="expiration === 'after'">
 				<MkInput v-model:value="after" type="number" class="input">
-					<span>{{ $t('_poll.duration') }}</span>
+					<span>{{ $ts._poll.duration }}</span>
 				</MkInput>
 				<MkSelect v-model:value="unit">
-					<option value="second">{{ $t('_time.second') }}</option>
-					<option value="minute">{{ $t('_time.minute') }}</option>
-					<option value="hour">{{ $t('_time.hour') }}</option>
-					<option value="day">{{ $t('_time.day') }}</option>
+					<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>
 				</MkSelect>
 			</section>
 		</div>
diff --git a/src/client/components/poll.vue b/src/client/components/poll.vue
index 071e3d539..7f13e135a 100644
--- a/src/client/components/poll.vue
+++ b/src/client/components/poll.vue
@@ -13,9 +13,9 @@
 	<p>
 		<span>{{ $t('_poll.totalVotes', { n: total }) }}</span>
 		<span> · </span>
-		<a v-if="!closed && !isVoted" @click="toggleShowResult">{{ showResult ? $t('_poll.vote') : $t('_poll.showResult') }}</a>
-		<span v-if="isVoted">{{ $t('_poll.voted') }}</span>
-		<span v-else-if="closed">{{ $t('_poll.closed') }}</span>
+		<a v-if="!closed && !isVoted" @click="toggleShowResult">{{ 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>
 		<span v-if="remaining > 0"> · {{ timer }}</span>
 	</p>
 </div>
diff --git a/src/client/components/post-form-attaches.vue b/src/client/components/post-form-attaches.vue
index 4a14112b1..230c944de 100644
--- a/src/client/components/post-form-attaches.vue
+++ b/src/client/components/post-form-attaches.vue
@@ -77,7 +77,7 @@ export default defineComponent({
 		},
 		async rename(file) {
 			const { canceled, result } = await os.dialog({
-				title: this.$t('enterFileName'),
+				title: this.$ts.enterFileName,
 				input: {
 					default: file.name
 				},
@@ -95,15 +95,15 @@ export default defineComponent({
 		showFileMenu(file, ev: MouseEvent) {
 			if (this.menu) return;
 			this.menu = os.modalMenu([{
-				text: this.$t('renameFile'),
+				text: this.$ts.renameFile,
 				icon: faICursor,
 				action: () => { this.rename(file) }
 			}, {
-				text: file.isSensitive ? this.$t('unmarkAsSensitive') : this.$t('markAsSensitive'),
+				text: file.isSensitive ? this.$ts.unmarkAsSensitive : this.$ts.markAsSensitive,
 				icon: file.isSensitive ? faEyeSlash : faEye,
 				action: () => { this.toggleSensitive(file) }
 			}, {
-				text: this.$t('attachCancel'),
+				text: this.$ts.attachCancel,
 				icon: faTimesCircle,
 				action: () => { this.detachMedia(file.id) }
 			}], ev.currentTarget || ev.target).then(() => this.menu = null);
diff --git a/src/client/components/post-form.vue b/src/client/components/post-form.vue
index 086e32a71..19773b3b6 100644
--- a/src/client/components/post-form.vue
+++ b/src/client/components/post-form.vue
@@ -11,7 +11,7 @@
 		<div>
 			<span class="text-count" :class="{ over: trimmedLength(text) > max }">{{ max - trimmedLength(text) }}</span>
 			<span class="local-only" v-if="localOnly"><Fa :icon="faBiohazard"/></span>
-			<button class="_button visibility" @click="setVisibility" ref="visibilityButton" v-tooltip="$t('visibility')" :disabled="channel != null">
+			<button class="_button visibility" @click="setVisibility" ref="visibilityButton" v-tooltip="$ts.visibility" :disabled="channel != null">
 				<span v-if="visibility === 'public'"><Fa :icon="faGlobe"/></span>
 				<span v-if="visibility === 'home'"><Fa :icon="faHome"/></span>
 				<span v-if="visibility === 'followers'"><Fa :icon="faUnlock"/></span>
@@ -23,9 +23,9 @@
 	<div class="form" :class="{ fixed }">
 		<XNotePreview class="preview" v-if="reply" :note="reply"/>
 		<XNotePreview class="preview" v-if="renote" :note="renote"/>
-		<div class="with-quote" v-if="quoteId"><Fa icon="quote-left"/> {{ $t('quoteAttached') }}<button @click="quoteId = null"><Fa icon="times"/></button></div>
+		<div class="with-quote" v-if="quoteId"><Fa icon="quote-left"/> {{ $ts.quoteAttached }}<button @click="quoteId = null"><Fa icon="times"/></button></div>
 		<div v-if="visibility === 'specified'" class="to-specified">
-			<span style="margin-right: 8px;">{{ $t('recipient') }}</span>
+			<span style="margin-right: 8px;">{{ $ts.recipient }}</span>
 			<div class="visibleUsers">
 				<span v-for="u in visibleUsers" :key="u.id">
 					<MkAcct :user="u"/>
@@ -34,17 +34,17 @@
 				<button @click="addVisibleUser" class="_buttonPrimary"><Fa :icon="faPlus" fixed-width/></button>
 			</div>
 		</div>
-		<input v-show="useCw" ref="cw" class="cw" v-model="cw" :placeholder="$t('annotation')" @keydown="onKeydown">
+		<input v-show="useCw" ref="cw" class="cw" v-model="cw" :placeholder="$ts.annotation" @keydown="onKeydown">
 		<textarea v-model="text" class="text" :class="{ withCw: useCw }" ref="text" :disabled="posting" :placeholder="placeholder" @keydown="onKeydown" @paste="onPaste"></textarea>
 		<XPostFormAttaches class="attaches" :files="files" @updated="updateFiles" @detach="detachFile" @changeSensitive="updateFileSensitive" @changeName="updateFileName"/>
 		<XPollEditor v-if="poll" :poll="poll" @destroyed="poll = null" @updated="onPollUpdate"/>
 		<footer>
-			<button class="_button" @click="chooseFileFrom" v-tooltip="$t('attachFile')"><Fa :icon="faPhotoVideo"/></button>
-			<button class="_button" @click="togglePoll" :class="{ active: poll }" v-tooltip="$t('poll')"><Fa :icon="faPollH"/></button>
-			<button class="_button" @click="useCw = !useCw" :class="{ active: useCw }" v-tooltip="$t('useCw')"><Fa :icon="faEyeSlash"/></button>
-			<button class="_button" @click="insertMention" v-tooltip="$t('mention')"><Fa :icon="faAt"/></button>
-			<button class="_button" @click="insertEmoji" v-tooltip="$t('emoji')"><Fa :icon="faLaughSquint"/></button>
-			<button class="_button" @click="showActions" v-tooltip="$t('plugin')" v-if="postFormActions.length > 0"><Fa :icon="faPlug"/></button>
+			<button class="_button" @click="chooseFileFrom" v-tooltip="$ts.attachFile"><Fa :icon="faPhotoVideo"/></button>
+			<button class="_button" @click="togglePoll" :class="{ active: poll }" v-tooltip="$ts.poll"><Fa :icon="faPollH"/></button>
+			<button class="_button" @click="useCw = !useCw" :class="{ active: useCw }" v-tooltip="$ts.useCw"><Fa :icon="faEyeSlash"/></button>
+			<button class="_button" @click="insertMention" v-tooltip="$ts.mention"><Fa :icon="faAt"/></button>
+			<button class="_button" @click="insertEmoji" v-tooltip="$ts.emoji"><Fa :icon="faLaughSquint"/></button>
+			<button class="_button" @click="showActions" v-tooltip="$ts.plugin" v-if="postFormActions.length > 0"><Fa :icon="faPlug"/></button>
 		</footer>
 	</div>
 </div>
@@ -164,19 +164,19 @@ export default defineComponent({
 
 		placeholder(): string {
 			if (this.renote) {
-				return this.$t('_postForm.quotePlaceholder');
+				return this.$ts._postForm.quotePlaceholder;
 			} else if (this.reply) {
-				return this.$t('_postForm.replyPlaceholder');
+				return this.$ts._postForm.replyPlaceholder;
 			} else if (this.channel) {
-				return this.$t('_postForm.channelPlaceholder');
+				return this.$ts._postForm.channelPlaceholder;
 			} else {
 				const xs = [
-					this.$t('_postForm._placeholders.a'),
-					this.$t('_postForm._placeholders.b'),
-					this.$t('_postForm._placeholders.c'),
-					this.$t('_postForm._placeholders.d'),
-					this.$t('_postForm._placeholders.e'),
-					this.$t('_postForm._placeholders.f')
+					this.$ts._postForm._placeholders.a,
+					this.$ts._postForm._placeholders.b,
+					this.$ts._postForm._placeholders.c,
+					this.$ts._postForm._placeholders.d,
+					this.$ts._postForm._placeholders.e,
+					this.$ts._postForm._placeholders.f
 				];
 				return xs[Math.floor(Math.random() * xs.length)];
 			}
@@ -184,10 +184,10 @@ export default defineComponent({
 
 		submitText(): string {
 			return this.renote
-				? this.$t('quote')
+				? this.$ts.quote
 				: this.reply
-					? this.$t('reply')
-					: this.$t('note');
+					? this.$ts.reply
+					: this.$ts.note;
 		},
 
 		canPost(): boolean {
@@ -352,7 +352,7 @@ export default defineComponent({
 		},
 
 		chooseFileFrom(ev) {
-			selectFile(ev.currentTarget || ev.target, this.$t('attachFile'), true).then(files => {
+			selectFile(ev.currentTarget || ev.target, this.$ts.attachFile, true).then(files => {
 				for (const file of files) {
 					this.files.push(file);
 				}
@@ -452,7 +452,7 @@ export default defineComponent({
 
 				os.dialog({
 					type: 'info',
-					text: this.$t('quoteQuestion'),
+					text: this.$ts.quoteQuestion,
 					showCancelButton: true
 				}).then(({ canceled }) => {
 					if (canceled) {
diff --git a/src/client/components/remote-caution.vue b/src/client/components/remote-caution.vue
index dfb1d4eb5..3af601f62 100644
--- a/src/client/components/remote-caution.vue
+++ b/src/client/components/remote-caution.vue
@@ -1,5 +1,5 @@
 <template>
-<div class="jmgmzlwq _panel"><Fa :icon="faExclamationTriangle" style="margin-right: 8px;"/>{{ $t('remoteUserCaution') }}<a :href="href" rel="nofollow noopener" target="_blank">{{ $t('showOnRemote') }}</a></div>
+<div class="jmgmzlwq _panel"><Fa :icon="faExclamationTriangle" style="margin-right: 8px;"/>{{ $ts.remoteUserCaution }}<a :href="href" rel="nofollow noopener" target="_blank">{{ $ts.showOnRemote }}</a></div>
 </template>
 
 <script lang="ts">
diff --git a/src/client/components/sidebar.vue b/src/client/components/sidebar.vue
index 584093f32..b2c0283a4 100644
--- a/src/client/components/sidebar.vue
+++ b/src/client/components/sidebar.vue
@@ -15,7 +15,7 @@
 					<MkAvatar :user="$i" class="avatar"/><MkAcct class="text" :user="$i"/>
 				</button>
 				<MkA class="item index" active-class="active" to="/" exact>
-					<Fa :icon="faHome" fixed-width/><span class="text">{{ $t('timeline') }}</span>
+					<Fa :icon="faHome" fixed-width/><span class="text">{{ $ts.timeline }}</span>
 				</MkA>
 				<template v-for="item in menu">
 					<div v-if="item === '-'" class="divider"></div>
@@ -26,14 +26,14 @@
 				</template>
 				<div class="divider"></div>
 				<button class="item _button" :class="{ active: $route.path === '/instance' || $route.path.startsWith('/instance/') }" v-if="$i.isAdmin || $i.isModerator" @click="oepnInstanceMenu">
-					<Fa :icon="faServer" fixed-width/><span class="text">{{ $t('instance') }}</span>
+					<Fa :icon="faServer" fixed-width/><span class="text">{{ $ts.instance }}</span>
 				</button>
 				<button class="item _button" @click="more">
-					<Fa :icon="faEllipsisH" fixed-width/><span class="text">{{ $t('more') }}</span>
+					<Fa :icon="faEllipsisH" fixed-width/><span class="text">{{ $ts.more }}</span>
 					<i v-if="otherNavItemIndicated"><Fa :icon="faCircle"/></i>
 				</button>
 				<MkA class="item" active-class="active" to="/settings">
-					<Fa :icon="faCog" fixed-width/><span class="text">{{ $t('settings') }}</span>
+					<Fa :icon="faCog" fixed-width/><span class="text">{{ $ts.settings }}</span>
 				</MkA>
 			</div>
 		</nav>
@@ -121,7 +121,7 @@ export default defineComponent({
 			if (this.searching) return;
 
 			os.dialog({
-				title: this.$t('search'),
+				title: this.$ts.search,
 				input: true
 			}).then(async ({ canceled, result: query }) => {
 				if (canceled || query == null || query === '') return;
@@ -145,18 +145,18 @@ export default defineComponent({
 
 			os.modalMenu([...[{
 				type: 'link',
-				text: this.$t('profile'),
+				text: this.$ts.profile,
 				to: `/@${ this.$i.username }`,
 				avatar: this.$i,
 			}, null, ...accountItems, {
 				icon: faPlus,
-				text: this.$t('addAcount'),
+				text: this.$ts.addAcount,
 				action: () => {
 					os.modalMenu([{
-						text: this.$t('existingAcount'),
+						text: this.$ts.existingAcount,
 						action: () => { this.addAcount(); },
 					}, {
-						text: this.$t('createAccount'),
+						text: this.$ts.createAccount,
 						action: () => { this.createAccount(); },
 					}], ev.currentTarget || ev.target);
 				},
@@ -168,57 +168,57 @@ export default defineComponent({
 		oepnInstanceMenu(ev) {
 			os.modalMenu([{
 				type: 'link',
-				text: this.$t('dashboard'),
+				text: this.$ts.dashboard,
 				to: '/instance',
 				icon: faTachometerAlt,
 			}, null, this.$i.isAdmin ? {
 				type: 'link',
-				text: this.$t('settings'),
+				text: this.$ts.settings,
 				to: '/instance/settings',
 				icon: faCog,
 			} : undefined, {
 				type: 'link',
-				text: this.$t('customEmojis'),
+				text: this.$ts.customEmojis,
 				to: '/instance/emojis',
 				icon: faLaugh,
 			}, {
 				type: 'link',
-				text: this.$t('users'),
+				text: this.$ts.users,
 				to: '/instance/users',
 				icon: faUsers,
 			}, {
 				type: 'link',
-				text: this.$t('files'),
+				text: this.$ts.files,
 				to: '/instance/files',
 				icon: faCloud,
 			}, {
 				type: 'link',
-				text: this.$t('jobQueue'),
+				text: this.$ts.jobQueue,
 				to: '/instance/queue',
 				icon: faExchangeAlt,
 			}, {
 				type: 'link',
-				text: this.$t('federation'),
+				text: this.$ts.federation,
 				to: '/instance/federation',
 				icon: faGlobe,
 			}, {
 				type: 'link',
-				text: this.$t('relays'),
+				text: this.$ts.relays,
 				to: '/instance/relays',
 				icon: faProjectDiagram,
 			}, {
 				type: 'link',
-				text: this.$t('announcements'),
+				text: this.$ts.announcements,
 				to: '/instance/announcements',
 				icon: faBroadcastTower,
 			}, {
 				type: 'link',
-				text: this.$t('abuseReports'),
+				text: this.$ts.abuseReports,
 				to: '/instance/abuses',
 				icon: faExclamationCircle,
 			}, {
 				type: 'link',
-				text: this.$t('logs'),
+				text: this.$ts.logs,
 				to: '/instance/logs',
 				icon: faStream,
 			}], ev.currentTarget || ev.target);
diff --git a/src/client/components/signin-dialog.vue b/src/client/components/signin-dialog.vue
index 5726cf6e5..2edd10f53 100644
--- a/src/client/components/signin-dialog.vue
+++ b/src/client/components/signin-dialog.vue
@@ -5,7 +5,7 @@
 	@close="$refs.dialog.close()"
 	@closed="$emit('closed')"
 >
-	<template #header>{{ $t('login') }}</template>
+	<template #header>{{ $ts.login }}</template>
 
 	<MkSignin :auto-set="autoSet" @login="onLogin"/>
 </XModalWindow>
diff --git a/src/client/components/signin.vue b/src/client/components/signin.vue
index 57c390191..3384fff96 100755
--- a/src/client/components/signin.vue
+++ b/src/client/components/signin.vue
@@ -4,37 +4,37 @@
 		<div class="avatar" :style="{ backgroundImage: user ? `url('${ user.avatarUrl }')` : null }" v-show="withAvatar"></div>
 		<div class="normal-signin" v-if="!totpLogin">
 			<MkInput v-model:value="username" type="text" pattern="^[a-zA-Z0-9_]+$" spellcheck="false" autofocus required @update:value="onUsernameChange">
-				<span>{{ $t('username') }}</span>
+				<span>{{ $ts.username }}</span>
 				<template #prefix>@</template>
 				<template #suffix>@{{ host }}</template>
 			</MkInput>
 			<MkInput v-model:value="password" type="password" :with-password-toggle="true" v-if="!user || user && !user.usePasswordLessLogin" required>
-				<span>{{ $t('password') }}</span>
+				<span>{{ $ts.password }}</span>
 				<template #prefix><Fa :icon="faLock"/></template>
 			</MkInput>
-			<MkButton type="submit" primary :disabled="signing" style="margin: 0 auto;">{{ signing ? $t('loggingIn') : $t('login') }}</MkButton>
+			<MkButton type="submit" primary :disabled="signing" style="margin: 0 auto;">{{ signing ? $ts.loggingIn : $ts.login }}</MkButton>
 		</div>
 		<div class="2fa-signin" v-if="totpLogin" :class="{ securityKeys: user && user.securityKeys }">
 			<div v-if="user && user.securityKeys" class="twofa-group tap-group">
-				<p>{{ $t('tapSecurityKey') }}</p>
+				<p>{{ $ts.tapSecurityKey }}</p>
 				<MkButton @click="queryKey" v-if="!queryingKey">
-					{{ $t('retry') }}
+					{{ $ts.retry }}
 				</MkButton>
 			</div>
 			<div class="or-hr" v-if="user && user.securityKeys">
-				<p class="or-msg">{{ $t('or') }}</p>
+				<p class="or-msg">{{ $ts.or }}</p>
 			</div>
 			<div class="twofa-group totp-group">
-				<p style="margin-bottom:0;">{{ $t('twoStepAuthentication') }}</p>
+				<p style="margin-bottom:0;">{{ $ts.twoStepAuthentication }}</p>
 				<MkInput v-model:value="password" type="password" :with-password-toggle="true" v-if="user && user.usePasswordLessLogin" required>
-					<span>{{ $t('password') }}</span>
+					<span>{{ $ts.password }}</span>
 					<template #prefix><Fa :icon="faLock"/></template>
 				</MkInput>
 				<MkInput v-model:value="token" type="text" pattern="^[0-9]{6}$" autocomplete="off" spellcheck="false" required>
-					<span>{{ $t('token') }}</span>
+					<span>{{ $ts.token }}</span>
 					<template #prefix><Fa :icon="faGavel"/></template>
 				</MkInput>
-				<MkButton type="submit" :disabled="signing" primary style="margin: 0 auto;">{{ signing ? $t('loggingIn') : $t('login') }}</MkButton>
+				<MkButton type="submit" :disabled="signing" primary style="margin: 0 auto;">{{ signing ? $ts.loggingIn : $ts.login }}</MkButton>
 			</div>
 		</div>
 	</div>
@@ -153,7 +153,7 @@ export default defineComponent({
 				if (err === null) return;
 				os.dialog({
 					type: 'error',
-					text: this.$t('signinFailed')
+					text: this.$ts.signinFailed
 				});
 				this.signing = false;
 			});
@@ -174,7 +174,7 @@ export default defineComponent({
 					}).catch(() => {
 						os.dialog({
 							type: 'error',
-							text: this.$t('signinFailed')
+							text: this.$ts.signinFailed
 						});
 						this.challengeData = null;
 						this.totpLogin = false;
@@ -195,7 +195,7 @@ export default defineComponent({
 				}).catch(() => {
 					os.dialog({
 						type: 'error',
-						text: this.$t('loginFailed')
+						text: this.$ts.loginFailed
 					});
 					this.signing = false;
 				});
diff --git a/src/client/components/signup-dialog.vue b/src/client/components/signup-dialog.vue
index eb0c40523..74566fbce 100644
--- a/src/client/components/signup-dialog.vue
+++ b/src/client/components/signup-dialog.vue
@@ -5,7 +5,7 @@
 	@close="$refs.dialog.close()"
 	@closed="$emit('closed')"
 >
-	<template #header>{{ $t('signup') }}</template>
+	<template #header>{{ $ts.signup }}</template>
 
 	<div class="_section">
 		<XSignup :auto-set="autoSet" @signup="onSignup"/>
diff --git a/src/client/components/signup.vue b/src/client/components/signup.vue
index 5378ec38f..4e45b9a94 100644
--- a/src/client/components/signup.vue
+++ b/src/client/components/signup.vue
@@ -2,49 +2,49 @@
 <form class="mk-signup" @submit.prevent="onSubmit" :autocomplete="Math.random()">
 	<template v-if="meta">
 		<MkInput v-if="meta.disableRegistration" v-model:value="invitationCode" type="text" :autocomplete="Math.random()" spellcheck="false" required>
-			<span>{{ $t('invitationCode') }}</span>
+			<span>{{ $ts.invitationCode }}</span>
 			<template #prefix><Fa :icon="faKey"/></template>
 		</MkInput>
 		<MkInput v-model:value="username" type="text" pattern="^[a-zA-Z0-9_]{1,20}$" :autocomplete="Math.random()" spellcheck="false" required @update:value="onChangeUsername">
-			<span>{{ $t('username') }}</span>
+			<span>{{ $ts.username }}</span>
 			<template #prefix>@</template>
 			<template #suffix>@{{ host }}</template>
 			<template #desc>
-				<span v-if="usernameState == 'wait'" style="color:#999"><Fa :icon="faSpinner" pulse fixed-width/> {{ $t('checking') }}</span>
-				<span v-if="usernameState == 'ok'" style="color:#3CB7B5"><Fa :icon="faCheck" fixed-width/> {{ $t('available') }}</span>
-				<span v-if="usernameState == 'unavailable'" style="color:#FF1161"><Fa :icon="faExclamationTriangle" fixed-width/> {{ $t('unavailable') }}</span>
-				<span v-if="usernameState == 'error'" style="color:#FF1161"><Fa :icon="faExclamationTriangle" fixed-width/> {{ $t('error') }}</span>
-				<span v-if="usernameState == 'invalid-format'" style="color:#FF1161"><Fa :icon="faExclamationTriangle" fixed-width/> {{ $t('usernameInvalidFormat') }}</span>
-				<span v-if="usernameState == 'min-range'" style="color:#FF1161"><Fa :icon="faExclamationTriangle" fixed-width/> {{ $t('tooShort') }}</span>
-				<span v-if="usernameState == 'max-range'" style="color:#FF1161"><Fa :icon="faExclamationTriangle" fixed-width/> {{ $t('tooLong') }}</span>
+				<span v-if="usernameState == 'wait'" style="color:#999"><Fa :icon="faSpinner" pulse fixed-width/> {{ $ts.checking }}</span>
+				<span v-if="usernameState == 'ok'" style="color:#3CB7B5"><Fa :icon="faCheck" fixed-width/> {{ $ts.available }}</span>
+				<span v-if="usernameState == 'unavailable'" style="color:#FF1161"><Fa :icon="faExclamationTriangle" fixed-width/> {{ $ts.unavailable }}</span>
+				<span v-if="usernameState == 'error'" style="color:#FF1161"><Fa :icon="faExclamationTriangle" fixed-width/> {{ $ts.error }}</span>
+				<span v-if="usernameState == 'invalid-format'" style="color:#FF1161"><Fa :icon="faExclamationTriangle" fixed-width/> {{ $ts.usernameInvalidFormat }}</span>
+				<span v-if="usernameState == 'min-range'" style="color:#FF1161"><Fa :icon="faExclamationTriangle" fixed-width/> {{ $ts.tooShort }}</span>
+				<span v-if="usernameState == 'max-range'" style="color:#FF1161"><Fa :icon="faExclamationTriangle" fixed-width/> {{ $ts.tooLong }}</span>
 			</template>
 		</MkInput>
 		<MkInput v-model:value="password" type="password" :autocomplete="Math.random()" required @update:value="onChangePassword">
-			<span>{{ $t('password') }}</span>
+			<span>{{ $ts.password }}</span>
 			<template #prefix><Fa :icon="faLock"/></template>
 			<template #desc>
-				<p v-if="passwordStrength == 'low'" style="color:#FF1161"><Fa :icon="faExclamationTriangle" fixed-width/> {{ $t('weakPassword') }}</p>
-				<p v-if="passwordStrength == 'medium'" style="color:#3CB7B5"><Fa :icon="faCheck" fixed-width/> {{ $t('normalPassword') }}</p>
-				<p v-if="passwordStrength == 'high'" style="color:#3CB7B5"><Fa :icon="faCheck" fixed-width/> {{ $t('strongPassword') }}</p>
+				<p v-if="passwordStrength == 'low'" style="color:#FF1161"><Fa :icon="faExclamationTriangle" fixed-width/> {{ $ts.weakPassword }}</p>
+				<p v-if="passwordStrength == 'medium'" style="color:#3CB7B5"><Fa :icon="faCheck" fixed-width/> {{ $ts.normalPassword }}</p>
+				<p v-if="passwordStrength == 'high'" style="color:#3CB7B5"><Fa :icon="faCheck" fixed-width/> {{ $ts.strongPassword }}</p>
 			</template>
 		</MkInput>
 		<MkInput v-model:value="retypedPassword" type="password" :autocomplete="Math.random()" required @update:value="onChangePasswordRetype">
-			<span>{{ $t('password') }} ({{ $t('retype') }})</span>
+			<span>{{ $ts.password }} ({{ $ts.retype }})</span>
 			<template #prefix><Fa :icon="faLock"/></template>
 			<template #desc>
-				<p v-if="passwordRetypeState == 'match'" style="color:#3CB7B5"><Fa :icon="faCheck" fixed-width/> {{ $t('passwordMatched') }}</p>
-				<p v-if="passwordRetypeState == 'not-match'" style="color:#FF1161"><Fa :icon="faExclamationTriangle" fixed-width/> {{ $t('passwordNotMatched') }}</p>
+				<p v-if="passwordRetypeState == 'match'" style="color:#3CB7B5"><Fa :icon="faCheck" fixed-width/> {{ $ts.passwordMatched }}</p>
+				<p v-if="passwordRetypeState == 'not-match'" style="color:#FF1161"><Fa :icon="faExclamationTriangle" fixed-width/> {{ $ts.passwordNotMatched }}</p>
 			</template>
 		</MkInput>
 		<label v-if="meta.tosUrl" class="tou">
 			<input type="checkbox" v-model="ToSAgreement">
-			<I18n src="agreeTo">
-				<a :href="meta.tosUrl" class="_link" target="_blank">{{ $t('tos') }}</a>
+			<I18n :src="$ts.agreeTo">
+				<a :href="meta.tosUrl" class="_link" target="_blank">{{ $ts.tos }}</a>
 			</I18n>
 		</label>
 		<captcha v-if="meta.enableHcaptcha" class="captcha" provider="hcaptcha" ref="hcaptcha" v-model:value="hCaptchaResponse" :sitekey="meta.hcaptchaSiteKey"/>
 		<captcha v-if="meta.enableRecaptcha" class="captcha" provider="grecaptcha" ref="recaptcha" v-model:value="reCaptchaResponse" :sitekey="meta.recaptchaSiteKey"/>
-		<MkButton type="submit" :disabled="shouldDisableSubmitting" primary>{{ $t('start') }}</MkButton>
+		<MkButton type="submit" :disabled="shouldDisableSubmitting" primary>{{ $ts.start }}</MkButton>
 	</template>
 </form>
 </template>
@@ -195,7 +195,7 @@ export default defineComponent({
 
 				os.dialog({
 					type: 'error',
-					text: this.$t('somethingHappened')
+					text: this.$ts.somethingHappened
 				});
 			});
 		}
diff --git a/src/client/components/sub-note-content.vue b/src/client/components/sub-note-content.vue
index 04250dea5..ceb25e096 100644
--- a/src/client/components/sub-note-content.vue
+++ b/src/client/components/sub-note-content.vue
@@ -1,8 +1,8 @@
 <template>
 <div class="wrmlmaau">
 	<div class="body">
-		<span v-if="note.isHidden" style="opacity: 0.5">({{ $t('private') }})</span>
-		<span v-if="note.deletedAt" style="opacity: 0.5">({{ $t('deleted') }})</span>
+		<span v-if="note.isHidden" style="opacity: 0.5">({{ $ts.private }})</span>
+		<span v-if="note.deletedAt" style="opacity: 0.5">({{ $ts.deleted }})</span>
 		<MkA class="reply" v-if="note.replyId" :to="`/notes/${note.replyId}`"><Fa :icon="faReply"/></MkA>
 		<Mfm v-if="note.text" :text="note.text" :author="note.user" :i="$i" :custom-emojis="note.emojis"/>
 		<MkA class="rp" v-if="note.renoteId" :to="`/notes/${note.renoteId}`">RN: ...</MkA>
@@ -12,7 +12,7 @@
 		<XMediaList :media-list="note.files"/>
 	</details>
 	<details v-if="note.poll">
-		<summary>{{ $t('poll') }}</summary>
+		<summary>{{ $ts.poll }}</summary>
 		<XPoll :note="note"/>
 	</details>
 </div>
diff --git a/src/client/components/token-generate-window.vue b/src/client/components/token-generate-window.vue
index b46bb67b0..844c20eb0 100644
--- a/src/client/components/token-generate-window.vue
+++ b/src/client/components/token-generate-window.vue
@@ -9,17 +9,17 @@
 	@closed="$emit('closed')"
 	@ok="ok()"
 >
-	<template #header>{{ title || $t('generateAccessToken') }}</template>
+	<template #header>{{ title || $ts.generateAccessToken }}</template>
 	<div v-if="information" class="_section">
 		<MkInfo warn>{{ information }}</MkInfo>
 	</div>
 	<div class="_section">
-		<MkInput v-model:value="name">{{ $t('name') }}</MkInput>
+		<MkInput v-model:value="name">{{ $ts.name }}</MkInput>
 	</div>
 	<div class="_section">
-		<div style="margin-bottom: 16px;"><b>{{ $t('permission') }}</b></div>
-		<MkButton inline @click="disableAll">{{ $t('disableAll') }}</MkButton>
-		<MkButton inline @click="enableAll">{{ $t('enableAll') }}</MkButton>
+		<div style="margin-bottom: 16px;"><b>{{ $ts.permission }}</b></div>
+		<MkButton inline @click="disableAll">{{ $ts.disableAll }}</MkButton>
+		<MkButton inline @click="enableAll">{{ $ts.enableAll }}</MkButton>
 		<MkSwitch v-for="kind in (initialPermissions || kinds)" :key="kind" v-model:value="permissions[kind]">{{ $t(`_permissions.${kind}`) }}</MkSwitch>
 	</div>
 </XModalWindow>
diff --git a/src/client/components/ui/input.vue b/src/client/components/ui/input.vue
index dec4a0871..1c5222f8f 100644
--- a/src/client/components/ui/input.vue
+++ b/src/client/components/ui/input.vue
@@ -48,7 +48,7 @@
 		</datalist>
 		<div class="suffix" ref="suffixEl"><slot name="suffix"></slot></div>
 	</div>
-	<button class="save _textButton" v-if="save && changed" @click="() => { changed = false; save(); }">{{ $t('save') }}</button>
+	<button class="save _textButton" v-if="save && changed" @click="() => { changed = false; save(); }">{{ $ts.save }}</button>
 	<div class="desc _caption"><slot name="desc"></slot></div>
 </div>
 </template>
diff --git a/src/client/components/ui/menu.vue b/src/client/components/ui/menu.vue
index 1e585062f..c2033402e 100644
--- a/src/client/components/ui/menu.vue
+++ b/src/client/components/ui/menu.vue
@@ -35,7 +35,7 @@
 		</button>
 	</template>
 	<span v-if="_items.length === 0" class="none item">
-		<span>{{ $t('none') }}</span>
+		<span>{{ $ts.none }}</span>
 	</span>
 </div>
 </template>
diff --git a/src/client/components/ui/pagination.vue b/src/client/components/ui/pagination.vue
index 89da66e6a..d9588a037 100644
--- a/src/client/components/ui/pagination.vue
+++ b/src/client/components/ui/pagination.vue
@@ -6,7 +6,7 @@
 	</div>
 	<div class="more" v-show="more" key="_more_">
 		<MkButton class="button" v-appear="$store.state.enableInfiniteScroll ? fetchMore : null" @click="fetchMore" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }" primary>
-			<template v-if="!moreFetching">{{ $t('loadMore') }}</template>
+			<template v-if="!moreFetching">{{ $ts.loadMore }}</template>
 			<template v-if="moreFetching"><MkLoading inline/></template>
 		</MkButton>
 	</div>
diff --git a/src/client/components/ui/textarea.vue b/src/client/components/ui/textarea.vue
index d49d7e834..1032c10d1 100644
--- a/src/client/components/ui/textarea.vue
+++ b/src/client/components/ui/textarea.vue
@@ -14,7 +14,7 @@
 			@blur="focused = false"
 		></textarea>
 	</div>
-	<button class="save _textButton" v-if="save && changed" @click="() => { changed = false; save(); }">{{ $t('save') }}</button>
+	<button class="save _textButton" v-if="save && changed" @click="() => { changed = false; save(); }">{{ $ts.save }}</button>
 	<div class="desc _caption"><slot name="desc"></slot></div>
 </div>
 </template>
diff --git a/src/client/components/url-preview.vue b/src/client/components/url-preview.vue
index 90671a8f0..9d6b01d9e 100644
--- a/src/client/components/url-preview.vue
+++ b/src/client/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" @click="playerEnabled = false" :title="$t('disablePlayer')"><Fa icon="times"/></button>
+	<button class="disablePlayer" @click="playerEnabled = false" :title="$ts.disablePlayer"><Fa icon="times"/></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" class="twitter" ref="twitter">
@@ -10,7 +10,7 @@
 	<transition name="zoom" mode="out-in">
 		<component :is="self ? 'MkA' : 'a'" :class="{ compact }" :[attr]="self ? url.substr(local.length) : url" rel="nofollow noopener" :target="target" :title="url" v-if="!fetching">
 			<div class="thumbnail" v-if="thumbnail" :style="`background-image: url('${thumbnail}')`">
-				<button class="_button" v-if="!playerEnabled && player.url" @click.prevent="playerEnabled = true" :title="$t('enablePlayer')"><Fa :icon="faPlayCircle"/></button>
+				<button class="_button" v-if="!playerEnabled && player.url" @click.prevent="playerEnabled = true" :title="$ts.enablePlayer"><Fa :icon="faPlayCircle"/></button>
 			</div>
 			<article>
 				<header>
@@ -26,7 +26,7 @@
 	</transition>
 	<div class="expandTweet" v-if="tweetId">
 		<a @click="tweetExpanded = true">
-			<Fa :icon="faTwitter"/> {{ $t('expandTweet') }}
+			<Fa :icon="faTwitter"/> {{ $ts.expandTweet }}
 		</a>
 	</div>
 </div>
diff --git a/src/client/components/user-info.vue b/src/client/components/user-info.vue
index 6f3b891fa..711e36741 100644
--- a/src/client/components/user-info.vue
+++ b/src/client/components/user-info.vue
@@ -10,17 +10,17 @@
 		<div class="mfm" v-if="user.description">
 			<Mfm :text="user.description" :author="user" :i="$i" :custom-emojis="user.emojis"/>
 		</div>
-		<span v-else style="opacity: 0.7;">{{ $t('noAccountDescription') }}</span>
+		<span v-else style="opacity: 0.7;">{{ $ts.noAccountDescription }}</span>
 	</div>
 	<div class="status">
 		<div>
-			<p>{{ $t('notes') }}</p><span>{{ user.notesCount }}</span>
+			<p>{{ $ts.notes }}</p><span>{{ user.notesCount }}</span>
 		</div>
 		<div>
-			<p>{{ $t('following') }}</p><span>{{ user.followingCount }}</span>
+			<p>{{ $ts.following }}</p><span>{{ user.followingCount }}</span>
 		</div>
 		<div>
-			<p>{{ $t('followers') }}</p><span>{{ user.followersCount }}</span>
+			<p>{{ $ts.followers }}</p><span>{{ user.followersCount }}</span>
 		</div>
 	</div>
 	<MkFollowButton class="koudoku-button" v-if="$i && user.id != $i.id" :user="user" mini/>
diff --git a/src/client/components/user-list.vue b/src/client/components/user-list.vue
index 97abf746a..7a57ff716 100644
--- a/src/client/components/user-list.vue
+++ b/src/client/components/user-list.vue
@@ -3,13 +3,13 @@
 
 <div v-else class="efvhhmdq">
 	<div class="no-users" v-if="empty">
-		<p>{{ $t('noUsers') }}</p>
+		<p>{{ $ts.noUsers }}</p>
 	</div>
 	<div class="users">
 		<MkUserInfo class="user" v-for="user in users" :user="user" :key="user.id"/>
 	</div>
 	<button class="more" v-appear="$store.state.enableInfiniteScroll ? fetchMore : null" @click="fetchMore" :class="{ fetching: moreFetching }" v-show="more" :disabled="moreFetching">
-		<template v-if="moreFetching"><Fa icon="spinner" pulse fixed-width/></template>{{ moreFetching ? $t('loading') : $t('loadMore') }}
+		<template v-if="moreFetching"><Fa icon="spinner" pulse fixed-width/></template>{{ moreFetching ? $ts.loading : $ts.loadMore }}
 	</button>
 </div>
 </template>
diff --git a/src/client/components/user-preview.vue b/src/client/components/user-preview.vue
index d7c99dc3f..c4588e478 100644
--- a/src/client/components/user-preview.vue
+++ b/src/client/components/user-preview.vue
@@ -13,13 +13,13 @@
 			</div>
 			<div class="status">
 				<div>
-					<p>{{ $t('notes') }}</p><span>{{ user.notesCount }}</span>
+					<p>{{ $ts.notes }}</p><span>{{ user.notesCount }}</span>
 				</div>
 				<div>
-					<p>{{ $t('following') }}</p><span>{{ user.followingCount }}</span>
+					<p>{{ $ts.following }}</p><span>{{ user.followingCount }}</span>
 				</div>
 				<div>
-					<p>{{ $t('followers') }}</p><span>{{ user.followersCount }}</span>
+					<p>{{ $ts.followers }}</p><span>{{ user.followersCount }}</span>
 				</div>
 			</div>
 			<MkFollowButton class="koudoku-button" v-if="$i && user.id != $i.id" :user="user" mini/>
diff --git a/src/client/components/user-select-dialog.vue b/src/client/components/user-select-dialog.vue
index f977de293..c67c985fd 100644
--- a/src/client/components/user-select-dialog.vue
+++ b/src/client/components/user-select-dialog.vue
@@ -7,11 +7,11 @@
 	@ok="ok()"
 	@closed="$emit('closed')"
 >
-	<template #header>{{ $t('selectUser') }}</template>
+	<template #header>{{ $ts.selectUser }}</template>
 	<div class="tbhwbxda _section">
 		<div class="inputs">
-			<MkInput v-model:value="username" class="input" @update:value="search" ref="username"><span>{{ $t('username') }}</span><template #prefix>@</template></MkInput>
-			<MkInput v-model:value="host" class="input" @update:value="search"><span>{{ $t('host') }}</span><template #prefix>@</template></MkInput>
+			<MkInput v-model:value="username" class="input" @update:value="search" ref="username"><span>{{ $ts.username }}</span><template #prefix>@</template></MkInput>
+			<MkInput v-model:value="host" class="input" @update:value="search"><span>{{ $ts.host }}</span><template #prefix>@</template></MkInput>
 		</div>
 	</div>
 	<div class="tbhwbxda _section result" v-if="username != '' || host != ''" :class="{ hit: users.length > 0 }">
@@ -25,7 +25,7 @@
 			</div>
 		</div>
 		<div v-else class="empty">
-			<span>{{ $t('noUsers') }}</span>
+			<span>{{ $ts.noUsers }}</span>
 		</div>
 	</div>
 	<div class="tbhwbxda _section recent" v-if="username == '' && host == ''">
diff --git a/src/client/components/users-dialog.vue b/src/client/components/users-dialog.vue
index 46cbf69eb..014748adc 100644
--- a/src/client/components/users-dialog.vue
+++ b/src/client/components/users-dialog.vue
@@ -15,11 +15,11 @@
 		</MkA>
 	</div>
 	<button class="more _button" v-appear="$store.state.enableInfiniteScroll ? fetchMore : null" @click="fetchMore" v-show="more" :disabled="moreFetching">
-		<template v-if="!moreFetching">{{ $t('loadMore') }}</template>
+		<template v-if="!moreFetching">{{ $ts.loadMore }}</template>
 		<template v-if="moreFetching"><Fa :icon="faSpinner" pulse fixed-width/></template>
 	</button>
 
-	<p class="empty" v-if="empty">{{ $t('noUsers') }}</p>
+	<p class="empty" v-if="empty">{{ $ts.noUsers }}</p>
 
 	<MkError v-if="error" @retry="init()"/>
 </div>
diff --git a/src/client/components/visibility-picker.vue b/src/client/components/visibility-picker.vue
index f0cff5db6..e518f48d0 100644
--- a/src/client/components/visibility-picker.vue
+++ b/src/client/components/visibility-picker.vue
@@ -4,37 +4,37 @@
 		<button class="_button" @click="choose('public')" :class="{ active: v == 'public' }" data-index="1" key="public">
 			<div><Fa :icon="faGlobe"/></div>
 			<div>
-				<span>{{ $t('_visibility.public') }}</span>
-				<span>{{ $t('_visibility.publicDescription') }}</span>
+				<span>{{ $ts._visibility.public }}</span>
+				<span>{{ $ts._visibility.publicDescription }}</span>
 			</div>
 		</button>
 		<button class="_button" @click="choose('home')" :class="{ active: v == 'home' }" data-index="2" key="home">
 			<div><Fa :icon="faHome"/></div>
 			<div>
-				<span>{{ $t('_visibility.home') }}</span>
-				<span>{{ $t('_visibility.homeDescription') }}</span>
+				<span>{{ $ts._visibility.home }}</span>
+				<span>{{ $ts._visibility.homeDescription }}</span>
 			</div>
 		</button>
 		<button class="_button" @click="choose('followers')" :class="{ active: v == 'followers' }" data-index="3" key="followers">
 			<div><Fa :icon="faUnlock"/></div>
 			<div>
-				<span>{{ $t('_visibility.followers') }}</span>
-				<span>{{ $t('_visibility.followersDescription') }}</span>
+				<span>{{ $ts._visibility.followers }}</span>
+				<span>{{ $ts._visibility.followersDescription }}</span>
 			</div>
 		</button>
 		<button :disabled="localOnly" class="_button" @click="choose('specified')" :class="{ active: v == 'specified' }" data-index="4" key="specified">
 			<div><Fa :icon="faEnvelope"/></div>
 			<div>
-				<span>{{ $t('_visibility.specified') }}</span>
-				<span>{{ $t('_visibility.specifiedDescription') }}</span>
+				<span>{{ $ts._visibility.specified }}</span>
+				<span>{{ $ts._visibility.specifiedDescription }}</span>
 			</div>
 		</button>
 		<div class="divider"></div>
 		<button class="_button localOnly" @click="localOnly = !localOnly" :class="{ active: localOnly }" data-index="5" key="localOnly">
 			<div><Fa :icon="faBiohazard"/></div>
 			<div>
-				<span>{{ $t('_visibility.localOnly') }}</span>
-				<span>{{ $t('_visibility.localOnlyDescription') }}</span>
+				<span>{{ $ts._visibility.localOnly }}</span>
+				<span>{{ $ts._visibility.localOnlyDescription }}</span>
 			</div>
 			<div><Fa :icon="localOnly ? faToggleOn : faToggleOff" :key="localOnly"/></div>
 		</button>
diff --git a/src/client/i18n.ts b/src/client/i18n.ts
index ee09c4da3..aeecb58a3 100644
--- a/src/client/i18n.ts
+++ b/src/client/i18n.ts
@@ -21,6 +21,13 @@ export class I18n<T extends Record<string, any>> {
 	public t(key: string, args?: Record<string, any>): string {
 		try {
 			let str = key.split('.').reduce((o, i) => o[i], this.locale) as string;
+
+			if (_DEV_) {
+				if (!str.includes('{')) {
+					console.warn(`i18n: '${key}' has no any arg. so ref prop directly instead of call this method.`);
+				}
+			}
+
 			if (args) {
 				for (const [k, v] of Object.entries(args)) {
 					str = str.replace(`{${k}}`, v);
diff --git a/src/client/pages/_error_.vue b/src/client/pages/_error_.vue
index 96a1ece11..29a62d72b 100644
--- a/src/client/pages/_error_.vue
+++ b/src/client/pages/_error_.vue
@@ -3,8 +3,8 @@
 	<div class="_section">
 		<div class="mjndxjch _content">
 			<img src="https://xn--931a.moe/assets/error.jpg" class="_ghost"/>
-			<p><Fa :icon="faExclamationTriangle"/> {{ $t('pageLoadError') }}</p>
-			<p>{{ $t('pageLoadErrorDescription') }}</p>
+			<p><Fa :icon="faExclamationTriangle"/> {{ $ts.pageLoadError }}</p>
+			<p>{{ $ts.pageLoadErrorDescription }}</p>
 		</div>
 	</div>
 </transition>
@@ -22,7 +22,7 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: {
-				title: this.$t('error'),
+				title: this.$ts.error,
 				icon: faExclamationTriangle
 			},
 			faExclamationTriangle
diff --git a/src/client/pages/about-misskey.vue b/src/client/pages/about-misskey.vue
index b0f45892d..59d564afd 100644
--- a/src/client/pages/about-misskey.vue
+++ b/src/client/pages/about-misskey.vue
@@ -11,27 +11,27 @@
 			</div>
 		</section>
 		<section class="_formItem" style="text-align: center; padding: 0 16px;" @click="gravity">
-			{{ $t('_aboutMisskey.about') }}
+			{{ $ts._aboutMisskey.about }}
 		</section>
 		<FormGroup>
 			<FormLink to="https://github.com/syuilo/misskey" external>
 				<template #icon><Fa :icon="faCode"/></template>
-				{{ $t('_aboutMisskey.source') }}
+				{{ $ts._aboutMisskey.source }}
 				<template #suffix>GitHub</template>
 			</FormLink>
 			<FormLink to="https://crowdin.com/project/misskey" external>
 				<template #icon><Fa :icon="faLanguage"/></template>
-				{{ $t('_aboutMisskey.translation') }}
+				{{ $ts._aboutMisskey.translation }}
 				<template #suffix>Crowdin</template>
 			</FormLink>
 			<FormLink to="https://www.patreon.com/syuilo" external>
 				<template #icon><Fa :icon="faHandHoldingMedical"/></template>
-				{{ $t('_aboutMisskey.donate') }}
+				{{ $ts._aboutMisskey.donate }}
 				<template #suffix>Patreon</template>
 			</FormLink>
 		</FormGroup>
 		<FormGroup>
-			<template #label>{{ $t('_aboutMisskey.contributors') }}</template>
+			<template #label>{{ $ts._aboutMisskey.contributors }}</template>
 			<FormLink to="https://github.com/syuilo" external>@syuilo</FormLink>
 			<FormLink to="https://github.com/AyaMorisawa" external>@AyaMorisawa</FormLink>
 			<FormLink to="https://github.com/mei23" external>@mei23</FormLink>
@@ -40,12 +40,12 @@
 			<FormLink to="https://github.com/rinsuki" external>@rinsuki</FormLink>
 			<FormLink to="https://github.com/Xeltica" external>@Xeltica</FormLink>
 			<FormLink to="https://github.com/u1-liquid" external>@u1-liquid</FormLink>
-			<template #caption><MkLink url="https://github.com/syuilo/misskey/graphs/contributors">{{ $t('_aboutMisskey.allContributors') }}</MkLink></template>
+			<template #caption><MkLink url="https://github.com/syuilo/misskey/graphs/contributors">{{ $ts._aboutMisskey.allContributors }}</MkLink></template>
 		</FormGroup>
 		<FormGroup>
-			<template #label><Mfm text="[jelly ❤]"/> {{ $t('_aboutMisskey.patrons') }}</template>
+			<template #label><Mfm text="[jelly ❤]"/> {{ $ts._aboutMisskey.patrons }}</template>
 			<FormKeyValueView v-for="patron in patrons" :key="patron"><template #key>{{ patron }}</template></FormKeyValueView>
-			<template #caption>{{ $t('_aboutMisskey.morePatrons') }}</template>
+			<template #caption>{{ $ts._aboutMisskey.morePatrons }}</template>
 		</FormGroup>
 	</FormBase>
 </div>
@@ -115,7 +115,7 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: {
-				title: this.$t('aboutMisskey'),
+				title: this.$ts.aboutMisskey,
 				icon: null
 			},
 			version,
diff --git a/src/client/pages/about.vue b/src/client/pages/about.vue
index a7a277d86..8ddf96144 100644
--- a/src/client/pages/about.vue
+++ b/src/client/pages/about.vue
@@ -13,16 +13,16 @@
 
 	<FormGroup>
 		<FormKeyValueView>
-			<template #key>{{ $t('administrator') }}</template>
+			<template #key>{{ $ts.administrator }}</template>
 			<template #value>{{ meta.maintainerName }}</template>
 		</FormKeyValueView>
 		<FormKeyValueView>
-			<template #key>{{ $t('contact') }}</template>
+			<template #key>{{ $ts.contact }}</template>
 			<template #value>{{ meta.maintainerEmail }}</template>
 		</FormKeyValueView>
 	</FormGroup>
 
-	<FormLink v-if="meta.tosUrl" :to="meta.tosUrl" external>{{ $t('tos') }}</FormLink>
+	<FormLink v-if="meta.tosUrl" :to="meta.tosUrl" external>{{ $ts.tos }}</FormLink>
 </FormBase>
 </template>
 
@@ -47,7 +47,7 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: {
-				title: this.$t('instanceInfo'),
+				title: this.$ts.instanceInfo,
 				icon: faInfoCircle
 			},
 			version,
diff --git a/src/client/pages/announcements.vue b/src/client/pages/announcements.vue
index cb9cd7048..4fbb34756 100644
--- a/src/client/pages/announcements.vue
+++ b/src/client/pages/announcements.vue
@@ -8,7 +8,7 @@
 				<img v-if="announcement.imageUrl" :src="announcement.imageUrl"/>
 			</div>
 			<div class="_footer" v-if="$i && !announcement.isRead">
-				<MkButton @click="read(items, announcement, i)" primary><Fa :icon="faCheck"/> {{ $t('gotIt') }}</MkButton>
+				<MkButton @click="read(items, announcement, i)" primary><Fa :icon="faCheck"/> {{ $ts.gotIt }}</MkButton>
 			</div>
 		</section>
 	</MkPagination>
@@ -31,7 +31,7 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: {
-				title: this.$t('announcements'),
+				title: this.$ts.announcements,
 				icon: faBroadcastTower
 			},
 			pagination: {
diff --git a/src/client/pages/auth.form.vue b/src/client/pages/auth.form.vue
index dd5aa34e6..8b2adc3e0 100644
--- a/src/client/pages/auth.form.vue
+++ b/src/client/pages/auth.form.vue
@@ -7,14 +7,14 @@
 		<p class="description">{{ app.description }}</p>
 	</div>
 	<div class="_content">
-		<h2>{{ $t('_auth.permissionAsk') }}</h2>
+		<h2>{{ $ts._auth.permissionAsk }}</h2>
 		<ul>
 			<li v-for="p in app.permission" :key="p">{{ $t(`_permissions.${p}`) }}</li>
 		</ul>
 	</div>
 	<div class="_footer">
-		<MkButton @click="cancel" inline>{{ $t('cancel') }}</MkButton>
-		<MkButton @click="accept" inline primary>{{ $t('accept') }}</MkButton>
+		<MkButton @click="cancel" inline>{{ $ts.cancel }}</MkButton>
+		<MkButton @click="accept" inline primary>{{ $ts.accept }}</MkButton>
 	</div>
 </section>
 </template>
diff --git a/src/client/pages/auth.vue b/src/client/pages/auth.vue
index b8a8c009e..522bd4cdf 100755
--- a/src/client/pages/auth.vue
+++ b/src/client/pages/auth.vue
@@ -12,15 +12,15 @@
 		@accepted="accepted"
 	/>
 	<div class="denied" v-if="state == 'denied'">
-		<h1>{{ $t('_auth.denied') }}</h1>
+		<h1>{{ $ts._auth.denied }}</h1>
 	</div>
 	<div class="accepted" v-if="state == 'accepted'">
-		<h1>{{ session.app.isAuthorized ? this.$t('already-authorized') : this.$t('allowed') }}</h1>
-		<p v-if="session.app.callbackUrl">{{ $t('_auth.callback') }}<MkEllipsis/></p>
-		<p v-if="!session.app.callbackUrl">{{ $t('_auth.pleaseGoBack') }}</p>
+		<h1>{{ session.app.isAuthorized ? this.$t('already-authorized') : this.$ts.allowed }}</h1>
+		<p v-if="session.app.callbackUrl">{{ $ts._auth.callback }}<MkEllipsis/></p>
+		<p v-if="!session.app.callbackUrl">{{ $ts._auth.pleaseGoBack }}</p>
 	</div>
 	<div class="error" v-if="state == 'fetch-session-error'">
-		<p>{{ $t('somethingHappened') }}</p>
+		<p>{{ $ts.somethingHappened }}</p>
 	</div>
 </div>
 <div class="signin" v-else>
diff --git a/src/client/pages/channel-editor.vue b/src/client/pages/channel-editor.vue
index 02ce97374..983e76d50 100644
--- a/src/client/pages/channel-editor.vue
+++ b/src/client/pages/channel-editor.vue
@@ -2,20 +2,20 @@
 <div>
 	<div class="_section">
 		<div class="_content">
-			<MkInput v-model:value="name">{{ $t('name') }}</MkInput>
+			<MkInput v-model:value="name">{{ $ts.name }}</MkInput>
 
-			<MkTextarea v-model:value="description">{{ $t('description') }}</MkTextarea>
+			<MkTextarea v-model:value="description">{{ $ts.description }}</MkTextarea>
 
 			<div class="banner">
-				<MkButton v-if="bannerId == null" @click="setBannerImage"><Fa :icon="faPlus"/> {{ $t('_channel.setBanner') }}</MkButton>
+				<MkButton v-if="bannerId == null" @click="setBannerImage"><Fa :icon="faPlus"/> {{ $ts._channel.setBanner }}</MkButton>
 				<div v-else-if="bannerUrl">
 					<img :src="bannerUrl" style="width: 100%;"/>
-					<MkButton @click="removeBannerImage()"><Fa :icon="faTrashAlt"/> {{ $t('_channel.removeBanner') }}</MkButton>
+					<MkButton @click="removeBannerImage()"><Fa :icon="faTrashAlt"/> {{ $ts._channel.removeBanner }}</MkButton>
 				</div>
 			</div>
 		</div>
 		<div class="_footer">
-			<MkButton @click="save()" primary><Fa :icon="faSave"/> {{ channelId ? $t('save') : $t('create') }}</MkButton>
+			<MkButton @click="save()" primary><Fa :icon="faSave"/> {{ channelId ? $ts.save : $ts.create }}</MkButton>
 		</div>
 	</div>
 </div>
@@ -46,10 +46,10 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: computed(() => this.channelId ? {
-				title: this.$t('_channel.edit'),
+				title: this.$ts._channel.edit,
 				icon: faSatelliteDish,
 			} : {
-				title: this.$t('_channel.create'),
+				title: this.$ts._channel.create,
 				icon: faSatelliteDish,
 			}),
 			channel: null,
diff --git a/src/client/pages/channel.vue b/src/client/pages/channel.vue
index 2610ab100..00e9f8132 100644
--- a/src/client/pages/channel.vue
+++ b/src/client/pages/channel.vue
@@ -10,8 +10,8 @@
 		</div>
 		<div :style="{ backgroundImage: channel.bannerUrl ? `url(${channel.bannerUrl})` : null }" class="banner">
 			<div class="status">
-				<div><Fa :icon="faUsers" fixed-width/><I18n src="_channel.usersCount" tag="span" style="margin-left: 4px;"><template #n><b>{{ channel.usersCount }}</b></template></I18n></div>
-				<div><Fa :icon="faPencilAlt" fixed-width/><I18n src="_channel.notesCount" tag="span" style="margin-left: 4px;"><template #n><b>{{ channel.notesCount }}</b></template></I18n></div>
+				<div><Fa :icon="faUsers" fixed-width/><I18n :src="$ts._channel.usersCount" tag="span" style="margin-left: 4px;"><template #n><b>{{ channel.usersCount }}</b></template></I18n></div>
+				<div><Fa :icon="faPencilAlt" fixed-width/><I18n :src="$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/src/client/pages/channels.vue b/src/client/pages/channels.vue
index d1af46db5..fcedc28db 100644
--- a/src/client/pages/channels.vue
+++ b/src/client/pages/channels.vue
@@ -2,9 +2,9 @@
 <div>
 	<div class="_section" style="padding: 0;" v-if="$i">
 		<MkTab class="_content" v-model:value="tab">
-			<option value="featured"><Fa :icon="faFireAlt"/> {{ $t('_channel.featured') }}</option>
-			<option value="following"><Fa :icon="faHeart"/> {{ $t('_channel.following') }}</option>
-			<option value="owned"><Fa :icon="faEdit"/> {{ $t('_channel.owned') }}</option>
+			<option value="featured"><Fa :icon="faFireAlt"/> {{ $ts._channel.featured }}</option>
+			<option value="following"><Fa :icon="faHeart"/> {{ $ts._channel.following }}</option>
+			<option value="owned"><Fa :icon="faEdit"/> {{ $ts._channel.owned }}</option>
 		</MkTab>
 	</div>
 
@@ -47,7 +47,7 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: {
-				title: this.$t('channel'),
+				title: this.$ts.channel,
 				icon: faSatelliteDish,
 				action: {
 					icon: faPlus,
diff --git a/src/client/pages/clip.vue b/src/client/pages/clip.vue
index ae6fa6770..2b61813fd 100644
--- a/src/client/pages/clip.vue
+++ b/src/client/pages/clip.vue
@@ -81,24 +81,24 @@ export default defineComponent({
 		menu(ev) {
 			os.modalMenu([this.isOwned ? {
 				icon: faPencilAlt,
-				text: this.$t('edit'),
+				text: this.$ts.edit,
 				action: async () => {
 					const { canceled, result } = await os.form(this.clip.name, {
 						name: {
 							type: 'string',
-							label: this.$t('name'),
+							label: this.$ts.name,
 							default: this.clip.name
 						},
 						description: {
 							type: 'string',
 							required: false,
 							multiline: true,
-							label: this.$t('description'),
+							label: this.$ts.description,
 							default: this.clip.description
 						},
 						isPublic: {
 							type: 'boolean',
-							label: this.$t('public'),
+							label: this.$ts.public,
 							default: this.clip.isPublic
 						}
 					});
@@ -111,7 +111,7 @@ export default defineComponent({
 				}
 			} : undefined, this.isOwned ? {
 				icon: faTrashAlt,
-				text: this.$t('delete'),
+				text: this.$ts.delete,
 				danger: true,
 				action: async () => {
 					const { canceled } = await os.dialog({
diff --git a/src/client/pages/doc.vue b/src/client/pages/doc.vue
index fe9873c6a..27978ca99 100644
--- a/src/client/pages/doc.vue
+++ b/src/client/pages/doc.vue
@@ -6,7 +6,7 @@
 			<div v-html="body" class="qyqbqfal"></div>
 		</div>
 		<div class="_footer">
-			<MkLink :url="`https://github.com/syuilo/misskey/blob/master/src/docs/${doc}.ja-JP.md`" class="at">{{ $t('docSource') }}</MkLink>
+			<MkLink :url="`https://github.com/syuilo/misskey/blob/master/src/docs/${doc}.ja-JP.md`" class="at">{{ $ts.docSource }}</MkLink>
 		</div>
 	</main>
 </div>
diff --git a/src/client/pages/docs.vue b/src/client/pages/docs.vue
index 1ed7684a8..59d23efcb 100644
--- a/src/client/pages/docs.vue
+++ b/src/client/pages/docs.vue
@@ -21,7 +21,7 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: {
-				title: this.$t('help'),
+				title: this.$ts.help,
 				icon: faQuestionCircle
 			},
 			docs: [],
diff --git a/src/client/pages/drive.vue b/src/client/pages/drive.vue
index b7ed04e10..63b0e7ba7 100644
--- a/src/client/pages/drive.vue
+++ b/src/client/pages/drive.vue
@@ -18,7 +18,7 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: {
-				title: computed(() => this.folder ? this.folder.name : this.$t('drive')),
+				title: computed(() => this.folder ? this.folder.name : this.$ts.drive),
 				icon: faCloud,
 				action: {
 					icon: faEllipsisH,
diff --git a/src/client/pages/explore.vue b/src/client/pages/explore.vue
index 2920fcb01..3ab7dda24 100644
--- a/src/client/pages/explore.vue
+++ b/src/client/pages/explore.vue
@@ -1,7 +1,7 @@
 <template>
 <div class="lznhrdub">
 	<div class="_section">
-		<MkInput v-model:value="query" :debounce="true" type="search"><template #icon><Fa :icon="faSearch"/></template><span>{{ $t('searchUser') }}</span></MkInput>
+		<MkInput v-model:value="query" :debounce="true" type="search"><template #icon><Fa :icon="faSearch"/></template><span>{{ $ts.searchUser }}</span></MkInput>
 
 		<XUserList v-if="query" class="_vMargin" :pagination="searchPagination" ref="search"/>
 
@@ -12,30 +12,30 @@
 
 		<template v-if="tag == null">
 			<MkFolder class="_vMargin" persist-key="explore-pinned-users">
-				<template #header><Fa :icon="faBookmark" fixed-width style="margin-right: 0.5em;"/>{{ $t('pinnedUsers') }}</template>
+				<template #header><Fa :icon="faBookmark" fixed-width style="margin-right: 0.5em;"/>{{ $ts.pinnedUsers }}</template>
 				<XUserList :pagination="pinnedUsers"/>
 			</MkFolder>
 			<MkFolder class="_vMargin" persist-key="explore-popular-users">
-				<template #header><Fa :icon="faChartLine" fixed-width style="margin-right: 0.5em;"/>{{ $t('popularUsers') }}</template>
+				<template #header><Fa :icon="faChartLine" fixed-width style="margin-right: 0.5em;"/>{{ $ts.popularUsers }}</template>
 				<XUserList :pagination="popularUsers"/>
 			</MkFolder>
 			<MkFolder class="_vMargin" persist-key="explore-recently-updated-users">
-				<template #header><Fa :icon="faCommentAlt" fixed-width style="margin-right: 0.5em;"/>{{ $t('recentlyUpdatedUsers') }}</template>
+				<template #header><Fa :icon="faCommentAlt" fixed-width style="margin-right: 0.5em;"/>{{ $ts.recentlyUpdatedUsers }}</template>
 				<XUserList :pagination="recentlyUpdatedUsers"/>
 			</MkFolder>
 			<MkFolder class="_vMargin" persist-key="explore-recently-registered-users">
-				<template #header><Fa :icon="faPlus" fixed-width style="margin-right: 0.5em;"/>{{ $t('recentlyRegisteredUsers') }}</template>
+				<template #header><Fa :icon="faPlus" fixed-width style="margin-right: 0.5em;"/>{{ $ts.recentlyRegisteredUsers }}</template>
 				<XUserList :pagination="recentlyRegisteredUsers"/>
 			</MkFolder>
 		</template>
 	</div>
 	<div class="_section">
 		<div class="localfedi7 _panel _vMargin" v-if="tag == null" :style="{ backgroundImage: `url(/assets/fedi.jpg)` }">
-			<header><span>{{ $t('exploreFediverse') }}</span></header>
+			<header><span>{{ $ts.exploreFediverse }}</span></header>
 		</div>
 
 		<MkFolder :body-togglable="true" :expanded="false" ref="tags" class="_vMargin">
-			<template #header><Fa :icon="faHashtag" fixed-width style="margin-right: 0.5em;"/>{{ $t('popularTags') }}</template>
+			<template #header><Fa :icon="faHashtag" fixed-width style="margin-right: 0.5em;"/>{{ $ts.popularTags }}</template>
 
 			<div class="vxjfqztj">
 				<MkA v-for="tag in tagsLocal" :to="`/explore/tags/${tag.tag}`" :key="'local:' + tag.tag" class="local">{{ tag.tag }}</MkA>
@@ -50,15 +50,15 @@
 
 		<template v-if="tag == null">
 			<MkFolder class="_vMargin">
-				<template #header><Fa :icon="faChartLine" fixed-width style="margin-right: 0.5em;"/>{{ $t('popularUsers') }}</template>
+				<template #header><Fa :icon="faChartLine" fixed-width style="margin-right: 0.5em;"/>{{ $ts.popularUsers }}</template>
 				<XUserList :pagination="popularUsersF"/>
 			</MkFolder>
 			<MkFolder class="_vMargin">
-				<template #header><Fa :icon="faCommentAlt" fixed-width style="margin-right: 0.5em;"/>{{ $t('recentlyUpdatedUsers') }}</template>
+				<template #header><Fa :icon="faCommentAlt" fixed-width style="margin-right: 0.5em;"/>{{ $ts.recentlyUpdatedUsers }}</template>
 				<XUserList :pagination="recentlyUpdatedUsersF"/>
 			</MkFolder>
 			<MkFolder class="_vMargin">
-				<template #header><Fa :icon="faRocket" fixed-width style="margin-right: 0.5em;"/>{{ $t('recentlyDiscoveredUsers') }}</template>
+				<template #header><Fa :icon="faRocket" fixed-width style="margin-right: 0.5em;"/>{{ $ts.recentlyDiscoveredUsers }}</template>
 				<XUserList :pagination="recentlyRegisteredUsersF"/>
 			</MkFolder>
 		</template>
@@ -93,7 +93,7 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: {
-				title: this.$t('explore'),
+				title: this.$ts.explore,
 				icon: faHashtag
 			},
 			pinnedUsers: { endpoint: 'pinned-users' },
diff --git a/src/client/pages/favorites.vue b/src/client/pages/favorites.vue
index b207c4af0..35c594239 100644
--- a/src/client/pages/favorites.vue
+++ b/src/client/pages/favorites.vue
@@ -19,7 +19,7 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: {
-				title: this.$t('favorites'),
+				title: this.$ts.favorites,
 				icon: faStar
 			},
 			pagination: {
diff --git a/src/client/pages/featured.vue b/src/client/pages/featured.vue
index c09eadc94..c1e612ef8 100644
--- a/src/client/pages/featured.vue
+++ b/src/client/pages/featured.vue
@@ -18,7 +18,7 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: {
-				title: this.$t('featured'),
+				title: this.$ts.featured,
 				icon: faFireAlt
 			},
 			pagination: {
diff --git a/src/client/pages/follow-requests.vue b/src/client/pages/follow-requests.vue
index 3c4b94fb1..04d739088 100644
--- a/src/client/pages/follow-requests.vue
+++ b/src/client/pages/follow-requests.vue
@@ -4,7 +4,7 @@
 		<template #empty>
 			<div class="_fullinfo">
 				<img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
-				<div>{{ $t('noFollowRequests') }}</div>
+				<div>{{ $ts.noFollowRequests }}</div>
 			</div>
 		</template>
 		<template #default="{items}">
@@ -44,7 +44,7 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: {
-				title: this.$t('followRequests'),
+				title: this.$ts.followRequests,
 				icon: faUserClock,
 			},
 			pagination: {
diff --git a/src/client/pages/follow.vue b/src/client/pages/follow.vue
index b1ab7f9f6..f999f48c5 100644
--- a/src/client/pages/follow.vue
+++ b/src/client/pages/follow.vue
@@ -40,7 +40,7 @@ export default defineComponent({
 			});
 		}
 
-		os.promiseDialog(promise, null, null, this.$t('fetchingAsApObject'));
+		os.promiseDialog(promise, null, null, this.$ts.fetchingAsApObject);
 	},
 
 	methods: {
diff --git a/src/client/pages/instance/abuses.vue b/src/client/pages/instance/abuses.vue
index ed5390030..030f5ae15 100644
--- a/src/client/pages/instance/abuses.vue
+++ b/src/client/pages/instance/abuses.vue
@@ -4,31 +4,31 @@
 		<div class="_content">
 			<div class="inputs" style="display: flex;">
 				<MkSelect v-model:value="state" style="margin: 0; flex: 1;">
-					<template #label>{{ $t('state') }}</template>
-					<option value="all">{{ $t('all') }}</option>
-					<option value="unresolved">{{ $t('unresolved') }}</option>
-					<option value="resolved">{{ $t('resolved') }}</option>
+					<template #label>{{ $ts.state }}</template>
+					<option value="all">{{ $ts.all }}</option>
+					<option value="unresolved">{{ $ts.unresolved }}</option>
+					<option value="resolved">{{ $ts.resolved }}</option>
 				</MkSelect>
 				<MkSelect v-model:value="targetUserOrigin" style="margin: 0; flex: 1;">
-					<template #label>{{ $t('targetUserOrigin') }}</template>
-					<option value="combined">{{ $t('all') }}</option>
-					<option value="local">{{ $t('local') }}</option>
-					<option value="remote">{{ $t('remote') }}</option>
+					<template #label>{{ $ts.targetUserOrigin }}</template>
+					<option value="combined">{{ $ts.all }}</option>
+					<option value="local">{{ $ts.local }}</option>
+					<option value="remote">{{ $ts.remote }}</option>
 				</MkSelect>
 				<MkSelect v-model:value="reporterOrigin" style="margin: 0; flex: 1;">
-					<template #label>{{ $t('reporterOrigin') }}</template>
-					<option value="combined">{{ $t('all') }}</option>
-					<option value="local">{{ $t('local') }}</option>
-					<option value="remote">{{ $t('remote') }}</option>
+					<template #label>{{ $ts.reporterOrigin }}</template>
+					<option value="combined">{{ $ts.all }}</option>
+					<option value="local">{{ $ts.local }}</option>
+					<option value="remote">{{ $ts.remote }}</option>
 				</MkSelect>
 			</div>
 			<!-- TODO
 			<div class="inputs" style="display: flex; padding-top: 1.2em;">
 				<MkInput v-model:value="searchUsername" style="margin: 0; flex: 1;" type="text" spellcheck="false" @update:value="$refs.reports.reload()">
-					<span>{{ $t('username') }}</span>
+					<span>{{ $ts.username }}</span>
 				</MkInput>
 				<MkInput v-model:value="searchHost" style="margin: 0; flex: 1;" type="text" spellcheck="false" @update:value="$refs.reports.reload()" :disabled="pagination.params().origin === 'local'">
-					<span>{{ $t('host') }}</span>
+					<span>{{ $ts.host }}</span>
 				</MkInput>
 			</div>
 			-->
@@ -52,7 +52,7 @@
 					</div>
 					<div class="_footer">
 						<div v-if="report.assignee">Assignee: <MkAcct :user="report.assignee"/></div>
-						<MkButton @click="resolve(report)" primary v-if="!report.resolved">{{ $t('abuseMarkAsResolved') }}</MkButton>
+						<MkButton @click="resolve(report)" primary v-if="!report.resolved">{{ $ts.abuseMarkAsResolved }}</MkButton>
 					</div>
 				</div>
 			</MkPagination>
@@ -84,7 +84,7 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: {
-				title: this.$t('abuseReports'),
+				title: this.$ts.abuseReports,
 				icon: faExclamationCircle
 			},
 			searchUsername: '',
diff --git a/src/client/pages/instance/announcements.vue b/src/client/pages/instance/announcements.vue
index 0dd669d45..b859aae2e 100644
--- a/src/client/pages/instance/announcements.vue
+++ b/src/client/pages/instance/announcements.vue
@@ -2,22 +2,22 @@
 <div class="ztgjmzrw">
 	<div class="_section">
 		<div class="_content">
-			<MkButton @click="add()" primary style="margin: 0 auto 16px auto;"><Fa :icon="faPlus"/> {{ $t('add') }}</MkButton>
+			<MkButton @click="add()" primary style="margin: 0 auto 16px auto;"><Fa :icon="faPlus"/> {{ $ts.add }}</MkButton>
 			<section class="_card _vMargin announcements" v-for="announcement in announcements">
 				<div class="_content announcement">
 					<MkInput v-model:value="announcement.title">
-						<span>{{ $t('title') }}</span>
+						<span>{{ $ts.title }}</span>
 					</MkInput>
 					<MkTextarea v-model:value="announcement.text">
-						<span>{{ $t('text') }}</span>
+						<span>{{ $ts.text }}</span>
 					</MkTextarea>
 					<MkInput v-model:value="announcement.imageUrl">
-						<span>{{ $t('imageUrl') }}</span>
+						<span>{{ $ts.imageUrl }}</span>
 					</MkInput>
 					<p v-if="announcement.reads">{{ $t('nUsersRead', { n: announcement.reads }) }}</p>
 					<div class="buttons">
-						<MkButton class="button" inline @click="save(announcement)" primary><Fa :icon="faSave"/> {{ $t('save') }}</MkButton>
-						<MkButton class="button" inline @click="remove(announcement)"><Fa :icon="faTrashAlt"/> {{ $t('remove') }}</MkButton>
+						<MkButton class="button" inline @click="save(announcement)" primary><Fa :icon="faSave"/> {{ $ts.save }}</MkButton>
+						<MkButton class="button" inline @click="remove(announcement)"><Fa :icon="faTrashAlt"/> {{ $ts.remove }}</MkButton>
 					</div>
 				</div>
 			</section>
@@ -45,7 +45,7 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: {
-				title: this.$t('announcements'),
+				title: this.$ts.announcements,
 				icon: faBroadcastTower
 			},
 			announcements: [],
@@ -86,7 +86,7 @@ export default defineComponent({
 				os.api('admin/announcements/create', announcement).then(() => {
 					os.dialog({
 						type: 'success',
-						text: this.$t('saved')
+						text: this.$ts.saved
 					});
 				}).catch(e => {
 					os.dialog({
@@ -98,7 +98,7 @@ export default defineComponent({
 				os.api('admin/announcements/update', announcement).then(() => {
 					os.dialog({
 						type: 'success',
-						text: this.$t('saved')
+						text: this.$ts.saved
 					});
 				}).catch(e => {
 					os.dialog({
diff --git a/src/client/pages/instance/emoji-edit-dialog.vue b/src/client/pages/instance/emoji-edit-dialog.vue
index ed81f15f6..179057d51 100644
--- a/src/client/pages/instance/emoji-edit-dialog.vue
+++ b/src/client/pages/instance/emoji-edit-dialog.vue
@@ -10,13 +10,13 @@
 
 	<div class="yigymqpb _section">
 		<img :src="emoji.url" class="img"/>
-		<MkInput v-model:value="name"><span>{{ $t('name') }}</span></MkInput>
-		<MkInput v-model:value="category" :datalist="categories"><span>{{ $t('category') }}</span></MkInput>
+		<MkInput v-model:value="name"><span>{{ $ts.name }}</span></MkInput>
+		<MkInput v-model:value="category" :datalist="categories"><span>{{ $ts.category }}</span></MkInput>
 		<MkInput v-model:value="aliases">
-			<span>{{ $t('tags') }}</span>
-			<template #desc>{{ $t('setMultipleBySeparatingWithSpace') }}</template>
+			<span>{{ $ts.tags }}</span>
+			<template #desc>{{ $ts.setMultipleBySeparatingWithSpace }}</template>
 		</MkInput>
-		<MkButton danger @click="del()"><Fa :icon="faTrashAlt"/> {{ $t('delete') }}</MkButton>
+		<MkButton danger @click="del()"><Fa :icon="faTrashAlt"/> {{ $ts.delete }}</MkButton>
 	</div>
 </XModalWindow>
 </template>
diff --git a/src/client/pages/instance/emojis.vue b/src/client/pages/instance/emojis.vue
index 01ea0d7f8..6a51813bb 100644
--- a/src/client/pages/instance/emojis.vue
+++ b/src/client/pages/instance/emojis.vue
@@ -2,17 +2,17 @@
 <div class="mk-instance-emojis">
 	<div class="_section" style="padding: 0;">
 		<MkTab v-model:value="tab">
-			<option value="local">{{ $t('local') }}</option>
-			<option value="remote">{{ $t('remote') }}</option>
+			<option value="local">{{ $ts.local }}</option>
+			<option value="remote">{{ $ts.remote }}</option>
 		</MkTab>
 	</div>
 
 	<div class="_section">
 		<div class="local" v-if="tab === 'local'">
-			<MkButton primary @click="add" style="margin: 0 auto var(--margin) auto;"><Fa :icon="faPlus"/> {{ $t('addEmoji') }}</MkButton>
-			<MkInput v-model:value="query" :debounce="true" type="search"><template #icon><Fa :icon="faSearch"/></template><span>{{ $t('search') }}</span></MkInput>
+			<MkButton primary @click="add" style="margin: 0 auto var(--margin) auto;"><Fa :icon="faPlus"/> {{ $ts.addEmoji }}</MkButton>
+			<MkInput v-model:value="query" :debounce="true" type="search"><template #icon><Fa :icon="faSearch"/></template><span>{{ $ts.search }}</span></MkInput>
 			<MkPagination :pagination="pagination" ref="emojis">
-				<template #empty><span>{{ $t('noCustomEmojis') }}</span></template>
+				<template #empty><span>{{ $ts.noCustomEmojis }}</span></template>
 				<template #default="{items}">
 					<div class="emojis">
 						<button class="emoji _panel _button" v-for="emoji in items" :key="emoji.id" @click="edit(emoji)">
@@ -28,10 +28,10 @@
 		</div>
 
 		<div class="remote" v-else-if="tab === 'remote'">
-			<MkInput v-model:value="queryRemote" :debounce="true" type="search"><template #icon><Fa :icon="faSearch"/></template><span>{{ $t('search') }}</span></MkInput>
-			<MkInput v-model:value="host" :debounce="true"><span>{{ $t('host') }}</span></MkInput>
+			<MkInput v-model:value="queryRemote" :debounce="true" type="search"><template #icon><Fa :icon="faSearch"/></template><span>{{ $ts.search }}</span></MkInput>
+			<MkInput v-model:value="host" :debounce="true"><span>{{ $ts.host }}</span></MkInput>
 			<MkPagination :pagination="remotePagination" ref="remoteEmojis">
-				<template #empty><span>{{ $t('noCustomEmojis') }}</span></template>
+				<template #empty><span>{{ $ts.noCustomEmojis }}</span></template>
 				<template #default="{items}">
 					<div class="emojis">
 						<div class="emoji _panel _button" v-for="emoji in items" :key="emoji.id" @click="remoteMenu(emoji, $event)">
@@ -71,7 +71,7 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: {
-				title: this.$t('customEmojis'),
+				title: this.$ts.customEmojis,
 				icon: faLaugh,
 				action: {
 					icon: faPlus,
@@ -142,7 +142,7 @@ export default defineComponent({
 				type: 'label',
 				text: ':' + emoji.name + ':',
 			}, {
-				text: this.$t('import'),
+				text: this.$ts.import,
 				icon: faPlus,
 				action: () => { this.im(emoji) }
 			}], ev.currentTarget || ev.target);
diff --git a/src/client/pages/instance/federation.vue b/src/client/pages/instance/federation.vue
index f084feba1..fe8ce8c2f 100644
--- a/src/client/pages/instance/federation.vue
+++ b/src/client/pages/instance/federation.vue
@@ -2,38 +2,38 @@
 <div>
 	<div class="_section">
 		<div class="_content">
-			<MkInput v-model:value="host" :debounce="true"><span>{{ $t('host') }}</span></MkInput>
+			<MkInput v-model:value="host" :debounce="true"><span>{{ $ts.host }}</span></MkInput>
 			<div class="inputs" style="display: flex;">
 				<MkSelect v-model:value="state" style="margin: 0; flex: 1;">
-					<template #label>{{ $t('state') }}</template>
-					<option value="all">{{ $t('all') }}</option>
-					<option value="federating">{{ $t('federating') }}</option>
-					<option value="subscribing">{{ $t('subscribing') }}</option>
-					<option value="publishing">{{ $t('publishing') }}</option>
-					<option value="suspended">{{ $t('suspended') }}</option>
-					<option value="blocked">{{ $t('blocked') }}</option>
-					<option value="notResponding">{{ $t('notResponding') }}</option>
+					<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>
 				</MkSelect>
 				<MkSelect v-model:value="sort" style="margin: 0; flex: 1;">
-					<template #label>{{ $t('sort') }}</template>
-					<option value="+pubSub">{{ $t('pubSub') }} ({{ $t('descendingOrder') }})</option>
-					<option value="-pubSub">{{ $t('pubSub') }} ({{ $t('ascendingOrder') }})</option>
-					<option value="+notes">{{ $t('notes') }} ({{ $t('descendingOrder') }})</option>
-					<option value="-notes">{{ $t('notes') }} ({{ $t('ascendingOrder') }})</option>
-					<option value="+users">{{ $t('users') }} ({{ $t('descendingOrder') }})</option>
-					<option value="-users">{{ $t('users') }} ({{ $t('ascendingOrder') }})</option>
-					<option value="+following">{{ $t('following') }} ({{ $t('descendingOrder') }})</option>
-					<option value="-following">{{ $t('following') }} ({{ $t('ascendingOrder') }})</option>
-					<option value="+followers">{{ $t('followers') }} ({{ $t('descendingOrder') }})</option>
-					<option value="-followers">{{ $t('followers') }} ({{ $t('ascendingOrder') }})</option>
-					<option value="+caughtAt">{{ $t('caughtAt') }} ({{ $t('descendingOrder') }})</option>
-					<option value="-caughtAt">{{ $t('caughtAt') }} ({{ $t('ascendingOrder') }})</option>
-					<option value="+lastCommunicatedAt">{{ $t('lastCommunicatedAt') }} ({{ $t('descendingOrder') }})</option>
-					<option value="-lastCommunicatedAt">{{ $t('lastCommunicatedAt') }} ({{ $t('ascendingOrder') }})</option>
-					<option value="+driveUsage">{{ $t('driveUsage') }} ({{ $t('descendingOrder') }})</option>
-					<option value="-driveUsage">{{ $t('driveUsage') }} ({{ $t('ascendingOrder') }})</option>
-					<option value="+driveFiles">{{ $t('driveFiles') }} ({{ $t('descendingOrder') }})</option>
-					<option value="-driveFiles">{{ $t('driveFiles') }} ({{ $t('ascendingOrder') }})</option>
+					<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.caughtAt }} ({{ $ts.descendingOrder }})</option>
+					<option value="-caughtAt">{{ $ts.caughtAt }} ({{ $ts.ascendingOrder }})</option>
+					<option value="+lastCommunicatedAt">{{ $ts.lastCommunicatedAt }} ({{ $ts.descendingOrder }})</option>
+					<option value="-lastCommunicatedAt">{{ $ts.lastCommunicatedAt }} ({{ $ts.ascendingOrder }})</option>
+					<option value="+driveUsage">{{ $ts.driveUsage }} ({{ $ts.descendingOrder }})</option>
+					<option value="-driveUsage">{{ $ts.driveUsage }} ({{ $ts.ascendingOrder }})</option>
+					<option value="+driveFiles">{{ $ts.driveFiles }} ({{ $ts.descendingOrder }})</option>
+					<option value="-driveFiles">{{ $ts.driveFiles }} ({{ $ts.ascendingOrder }})</option>
 				</MkSelect>
 			</div>
 		</div>
@@ -79,7 +79,7 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: {
-				title: this.$t('federation'),
+				title: this.$ts.federation,
 				icon: faGlobe
 			},
 			host: '',
diff --git a/src/client/pages/instance/file-dialog.vue b/src/client/pages/instance/file-dialog.vue
index 79244349e..87fcdb59e 100644
--- a/src/client/pages/instance/file-dialog.vue
+++ b/src/client/pages/instance/file-dialog.vue
@@ -21,8 +21,8 @@
 		</div>
 		<div class="_section">
 			<div class="_content">
-				<MkButton full @click="showUser"><Fa :icon="faExternalLinkSquareAlt"/> {{ $t('user') }}</MkButton>
-				<MkButton full danger @click="del"><Fa :icon="faTrashAlt"/> {{ $t('delete') }}</MkButton>
+				<MkButton full @click="showUser"><Fa :icon="faExternalLinkSquareAlt"/> {{ $ts.user }}</MkButton>
+				<MkButton full danger @click="del"><Fa :icon="faTrashAlt"/> {{ $ts.delete }}</MkButton>
 			</div>
 		</div>
 		<div class="_section" v-if="info">
diff --git a/src/client/pages/instance/files.vue b/src/client/pages/instance/files.vue
index f0a8b69f8..f19e3fcd4 100644
--- a/src/client/pages/instance/files.vue
+++ b/src/client/pages/instance/files.vue
@@ -2,17 +2,17 @@
 <div class="xrmjdkdw">
 	<div class="_section">
 		<div class="_content">
-			<MkButton primary @click="clear()"><Fa :icon="faTrashAlt"/> {{ $t('clearCachedFiles') }}</MkButton>
+			<MkButton primary @click="clear()"><Fa :icon="faTrashAlt"/> {{ $ts.clearCachedFiles }}</MkButton>
 		</div>
 	</div>
 
 	<div class="_section lookup">
-		<div class="_title"><Fa :icon="faSearch"/> {{ $t('lookup') }}</div>
+		<div class="_title"><Fa :icon="faSearch"/> {{ $ts.lookup }}</div>
 		<div class="_content">
 			<MkInput class="target" v-model:value="q" type="text" @enter="find()">
-				<span>{{ $t('fileIdOrUrl') }}</span>
+				<span>{{ $ts.fileIdOrUrl }}</span>
 			</MkInput>
-			<MkButton @click="find()" primary><Fa :icon="faSearch"/> {{ $t('lookup') }}</MkButton>
+			<MkButton @click="find()" primary><Fa :icon="faSearch"/> {{ $ts.lookup }}</MkButton>
 		</div>
 	</div>
 
@@ -20,18 +20,18 @@
 		<div class="_content">
 			<div class="inputs" style="display: flex;">
 				<MkSelect v-model:value="origin" style="margin: 0; flex: 1;">
-					<template #label>{{ $t('instance') }}</template>
-					<option value="combined">{{ $t('all') }}</option>
-					<option value="local">{{ $t('local') }}</option>
-					<option value="remote">{{ $t('remote') }}</option>
+					<template #label>{{ $ts.instance }}</template>
+					<option value="combined">{{ $ts.all }}</option>
+					<option value="local">{{ $ts.local }}</option>
+					<option value="remote">{{ $ts.remote }}</option>
 				</MkSelect>
 				<MkInput v-model:value="searchHost" :debounce="true" type="search" style="margin: 0; flex: 1;" :disabled="pagination.params().origin === 'local'">
-					<span>{{ $t('host') }}</span>
+					<span>{{ $ts.host }}</span>
 				</MkInput>
 			</div>
 			<div class="inputs" style="display: flex; padding-top: 1.2em;">
 				<MkInput v-model:value="type" :debounce="true" type="search" style="margin: 0; flex: 1;">
-					<span>{{ $t('type') }}</span>
+					<span>{{ $ts.type }}</span>
 				</MkInput>
 			</div>
 			<MkPagination :pagination="pagination" #default="{items}" class="urempief" ref="files">
@@ -43,14 +43,14 @@
 						</div>
 						<div>
 							<MkAcct v-if="file.user" :user="file.user"/>
-							<div v-else>{{ $t('system') }}</div>
+							<div v-else>{{ $ts.system }}</div>
 						</div>
 						<div>
 							<span style="margin-right: 1em;">{{ file.type }}</span>
 							<span>{{ bytes(file.size) }}</span>
 						</div>
 						<div>
-							<span>{{ $t('registeredDate') }}: <MkTime :time="file.createdAt" mode="detail"/></span>
+							<span>{{ $ts.registeredDate }}: <MkTime :time="file.createdAt" mode="detail"/></span>
 						</div>
 					</div>
 				</button>
@@ -84,7 +84,7 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: {
-				title: this.$t('files'),
+				title: this.$ts.files,
 				icon: faCloud
 			},
 			q: null,
@@ -120,7 +120,7 @@ export default defineComponent({
 		clear() {
 			os.dialog({
 				type: 'warning',
-				text: this.$t('clearCachedFilesConfirm'),
+				text: this.$ts.clearCachedFilesConfirm,
 				showCancelButton: true
 			}).then(({ canceled }) => {
 				if (canceled) return;
@@ -142,7 +142,7 @@ export default defineComponent({
 				if (e.code === 'NO_SUCH_FILE') {
 					os.dialog({
 						type: 'error',
-						text: this.$t('notFound')
+						text: this.$ts.notFound
 					});
 				}
 			});
diff --git a/src/client/pages/instance/index.metrics.vue b/src/client/pages/instance/index.metrics.vue
index 4fd7e55ab..9d990c224 100644
--- a/src/client/pages/instance/index.metrics.vue
+++ b/src/client/pages/instance/index.metrics.vue
@@ -1,11 +1,11 @@
 <template>
 <div>
 	<MkFolder>
-		<template #header><Fa :icon="faHeartbeat"/> {{ $t('metrics') }}</template>
+		<template #header><Fa :icon="faHeartbeat"/> {{ $ts.metrics }}</template>
 		<div class="_section" style="padding: 0 var(--margin);">
 			<div class="_content">
 				<MkContainer :body-togglable="false" class="_vMargin">
-					<template #header><Fa :icon="faMicrochip"/>{{ $t('cpuAndMemory') }}</template>
+					<template #header><Fa :icon="faMicrochip"/>{{ $ts.cpuAndMemory }}</template>
 					<!--
 					<template #func>
 						<button class="_button" @click="resume" :disabled="!paused"><Fa :icon="faPlay"/></button>
@@ -28,7 +28,7 @@
 				</MkContainer>
 
 				<MkContainer :body-togglable="false" class="_vMargin">
-					<template #header><Fa :icon="faHdd"/> {{ $t('disk') }}</template>
+					<template #header><Fa :icon="faHdd"/> {{ $ts.disk }}</template>
 					<!--
 					<template #func>
 						<button class="_button" @click="resume" :disabled="!paused"><Fa :icon="faPlay"/></button>
@@ -51,7 +51,7 @@
 				</MkContainer>
 
 				<MkContainer :body-togglable="false" class="_vMargin">
-					<template #header><Fa :icon="faExchangeAlt"/> {{ $t('network') }}</template>
+					<template #header><Fa :icon="faExchangeAlt"/> {{ $ts.network }}</template>
 					<!--
 					<template #func>
 						<button class="_button" @click="resume" :disabled="!paused"><Fa :icon="faPlay"/></button>
@@ -75,11 +75,11 @@
 	</MkFolder>
 
 	<MkFolder>
-		<template #header><Fa :icon="faClipboardList"/> {{ $t('jobQueue') }}</template>
+		<template #header><Fa :icon="faClipboardList"/> {{ $ts.jobQueue }}</template>
 
 		<div class="vkyrmkwb" :style="{ gridTemplateRows: queueHeight }">
 			<MkContainer :body-togglable="false" :scrollable="true" :resize-base-el="() => $el">
-				<template #header><Fa :icon="faExclamationTriangle"/> {{ $t('delayed') }}</template>
+				<template #header><Fa :icon="faExclamationTriangle"/> {{ $ts.delayed }}</template>
 
 				<div class="_content">
 					<div class="_keyValue" v-for="job in jobs" :key="job[0]">
diff --git a/src/client/pages/instance/index.vue b/src/client/pages/instance/index.vue
index e9614acf7..e519ce743 100644
--- a/src/client/pages/instance/index.vue
+++ b/src/client/pages/instance/index.vue
@@ -1,13 +1,13 @@
 <template>
 <div v-if="meta" v-show="page === 'index'" class="xhexznfu _section">
 	<MkFolder>
-		<template #header><Fa :icon="faTachometerAlt"/> {{ $t('overview') }}</template>
+		<template #header><Fa :icon="faTachometerAlt"/> {{ $ts.overview }}</template>
 
 		<div class="sboqnrfi" :style="{ gridTemplateRows: overviewHeight }">
 			<MkInstanceStats :chart-limit="300" :detailed="true" class="_vMargin" ref="stats"/>
 
 			<MkContainer :body-togglable="true" class="_vMargin">
-				<template #header><Fa :icon="faInfoCircle"/>{{ $t('instanceInfo') }}</template>
+				<template #header><Fa :icon="faInfoCircle"/>{{ $ts.instanceInfo }}</template>
 
 				<div class="_content">
 					<div class="_keyValue"><b>Misskey</b><span>v{{ version }}</span></div>
@@ -20,7 +20,7 @@
 			</MkContainer>
 			
 			<MkContainer :body-togglable="true" :scrollable="true" class="_vMargin" style="height: 300px;">
-				<template #header><Fa :icon="faDatabase"/>{{ $t('database') }}</template>
+				<template #header><Fa :icon="faDatabase"/>{{ $ts.database }}</template>
 
 				<div class="_content" v-if="dbInfo">
 					<table style="border-collapse: collapse; width: 100%;">
@@ -42,7 +42,7 @@
 </div>
 <div v-if="page === 'logs'" class="_section">
 	<MkFolder>
-		<template #header><Fa :icon="faStream"/> {{ $t('logs') }}</template>
+		<template #header><Fa :icon="faStream"/> {{ $ts.logs }}</template>
 
 		<div class="_keyValue" v-for="log in modLogs">
 			<b>{{ log.type }}</b><span>by {{ log.user.username }}</span><MkTime :time="log.createdAt" style="opacity: 0.7;"/>
@@ -89,21 +89,21 @@ export default defineComponent({
 				tabs: [{
 					id: 'index',
 					title: null,
-					tooltip: this.$t('instance'),
+					tooltip: this.$ts.instance,
 					icon: faServer,
 					onClick: () => { this.page = 'index'; },
 					selected: computed(() => this.page === 'index')
 				}, {
 					id: 'metrics',
 					title: null,
-					tooltip: this.$t('metrics'),
+					tooltip: this.$ts.metrics,
 					icon: faHeartbeat,
 					onClick: () => { this.page = 'metrics'; },
 					selected: computed(() => this.page === 'metrics')
 				}, {
 					id: 'logs',
 					title: null,
-					tooltip: this.$t('logs'),
+					tooltip: this.$ts.logs,
 					icon: faStream,
 					onClick: () => { this.page = 'logs'; },
 					selected: computed(() => this.page === 'logs')
diff --git a/src/client/pages/instance/instance.vue b/src/client/pages/instance/instance.vue
index 6e626cb17..bcf18cfff 100644
--- a/src/client/pages/instance/instance.vue
+++ b/src/client/pages/instance/instance.vue
@@ -10,11 +10,11 @@
 		<div class="_table section">
 			<div class="_row">
 				<div class="_cell">
-					<div class="_label">{{ $t('software') }}</div>
+					<div class="_label">{{ $ts.software }}</div>
 					<div class="_data">{{ instance.softwareName || '?' }}</div>
 				</div>
 				<div class="_cell">
-					<div class="_label">{{ $t('version') }}</div>
+					<div class="_label">{{ $ts.version }}</div>
 					<div class="_data">{{ instance.softwareVersion || '?' }}</div>
 				</div>
 			</div>
@@ -22,77 +22,77 @@
 		<div class="_table data section">
 			<div class="_row">
 				<div class="_cell">
-					<div class="_label">{{ $t('registeredAt') }}</div>
+					<div class="_label">{{ $ts.registeredAt }}</div>
 					<div class="_data">{{ new Date(instance.caughtAt).toLocaleString() }} (<MkTime :time="instance.caughtAt"/>)</div>
 				</div>
 			</div>
 			<div class="_row">
 				<div class="_cell">
-					<div class="_label">{{ $t('following') }}</div>
+					<div class="_label">{{ $ts.following }}</div>
 					<button class="_data _textButton" @click="showFollowing()">{{ number(instance.followingCount) }}</button>
 				</div>
 				<div class="_cell">
-					<div class="_label">{{ $t('followers') }}</div>
+					<div class="_label">{{ $ts.followers }}</div>
 					<button class="_data _textButton" @click="showFollowers()">{{ number(instance.followersCount) }}</button>
 				</div>
 			</div>
 			<div class="_row">
 				<div class="_cell">
-					<div class="_label">{{ $t('users') }}</div>
+					<div class="_label">{{ $ts.users }}</div>
 					<button class="_data _textButton" @click="showUsers()">{{ number(instance.usersCount) }}</button>
 				</div>
 				<div class="_cell">
-					<div class="_label">{{ $t('notes') }}</div>
+					<div class="_label">{{ $ts.notes }}</div>
 					<div class="_data">{{ number(instance.notesCount) }}</div>
 				</div>
 			</div>
 			<div class="_row">
 				<div class="_cell">
-					<div class="_label">{{ $t('files') }}</div>
+					<div class="_label">{{ $ts.files }}</div>
 					<div class="_data">{{ number(instance.driveFiles) }}</div>
 				</div>
 				<div class="_cell">
-					<div class="_label">{{ $t('storageUsage') }}</div>
+					<div class="_label">{{ $ts.storageUsage }}</div>
 					<div class="_data">{{ bytes(instance.driveUsage) }}</div>
 				</div>
 			</div>
 			<div class="_row">
 				<div class="_cell">
-					<div class="_label">{{ $t('latestRequestSentAt') }}</div>
+					<div class="_label">{{ $ts.latestRequestSentAt }}</div>
 					<div class="_data"><MkTime v-if="instance.latestRequestSentAt" :time="instance.latestRequestSentAt"/><span v-else>N/A</span></div>
 				</div>
 				<div class="_cell">
-					<div class="_label">{{ $t('latestStatus') }}</div>
+					<div class="_label">{{ $ts.latestStatus }}</div>
 					<div class="_data">{{ instance.latestStatus ? instance.latestStatus : 'N/A' }}</div>
 				</div>
 			</div>
 			<div class="_row">
 				<div class="_cell">
-					<div class="_label">{{ $t('latestRequestReceivedAt') }}</div>
+					<div class="_label">{{ $ts.latestRequestReceivedAt }}</div>
 					<div class="_data"><MkTime v-if="instance.latestRequestReceivedAt" :time="instance.latestRequestReceivedAt"/><span v-else>N/A</span></div>
 				</div>
 			</div>
 		</div>
 		<div class="chart">
 			<div class="header">
-				<span class="label">{{ $t('charts') }}</span>
+				<span class="label">{{ $ts.charts }}</span>
 				<div class="selects">
 					<MkSelect v-model:value="chartSrc" style="margin: 0; flex: 1;">
-						<option value="requests">{{ $t('_instanceCharts.requests') }}</option>
-						<option value="users">{{ $t('_instanceCharts.users') }}</option>
-						<option value="users-total">{{ $t('_instanceCharts.usersTotal') }}</option>
-						<option value="notes">{{ $t('_instanceCharts.notes') }}</option>
-						<option value="notes-total">{{ $t('_instanceCharts.notesTotal') }}</option>
-						<option value="ff">{{ $t('_instanceCharts.ff') }}</option>
-						<option value="ff-total">{{ $t('_instanceCharts.ffTotal') }}</option>
-						<option value="drive-usage">{{ $t('_instanceCharts.cacheSize') }}</option>
-						<option value="drive-usage-total">{{ $t('_instanceCharts.cacheSizeTotal') }}</option>
-						<option value="drive-files">{{ $t('_instanceCharts.files') }}</option>
-						<option value="drive-files-total">{{ $t('_instanceCharts.filesTotal') }}</option>
+						<option value="requests">{{ $ts._instanceCharts.requests }}</option>
+						<option value="users">{{ $ts._instanceCharts.users }}</option>
+						<option value="users-total">{{ $ts._instanceCharts.usersTotal }}</option>
+						<option value="notes">{{ $ts._instanceCharts.notes }}</option>
+						<option value="notes-total">{{ $ts._instanceCharts.notesTotal }}</option>
+						<option value="ff">{{ $ts._instanceCharts.ff }}</option>
+						<option value="ff-total">{{ $ts._instanceCharts.ffTotal }}</option>
+						<option value="drive-usage">{{ $ts._instanceCharts.cacheSize }}</option>
+						<option value="drive-usage-total">{{ $ts._instanceCharts.cacheSizeTotal }}</option>
+						<option value="drive-files">{{ $ts._instanceCharts.files }}</option>
+						<option value="drive-files-total">{{ $ts._instanceCharts.filesTotal }}</option>
 					</MkSelect>
 					<MkSelect v-model:value="chartSpan" style="margin: 0;">
-						<option value="hour">{{ $t('perHour') }}</option>
-						<option value="day">{{ $t('perDay') }}</option>
+						<option value="hour">{{ $ts.perHour }}</option>
+						<option value="day">{{ $ts.perDay }}</option>
 					</MkSelect>
 				</div>
 			</div>
@@ -101,21 +101,21 @@
 			</div>
 		</div>
 		<div class="operations section">
-			<span class="label">{{ $t('operations') }}</span>
-			<MkSwitch v-model:value="isSuspended" class="switch">{{ $t('stopActivityDelivery') }}</MkSwitch>
-			<MkSwitch :value="isBlocked" class="switch" @update:value="changeBlock">{{ $t('blockThisInstance') }}</MkSwitch>
+			<span class="label">{{ $ts.operations }}</span>
+			<MkSwitch v-model:value="isSuspended" class="switch">{{ $ts.stopActivityDelivery }}</MkSwitch>
+			<MkSwitch :value="isBlocked" class="switch" @update:value="changeBlock">{{ $ts.blockThisInstance }}</MkSwitch>
 			<details>
-				<summary>{{ $t('deleteAllFiles') }}</summary>
-				<MkButton @click="deleteAllFiles()" style="margin: 0.5em 0 0.5em 0;"><Fa :icon="faTrashAlt"/> {{ $t('deleteAllFiles') }}</MkButton>
+				<summary>{{ $ts.deleteAllFiles }}</summary>
+				<MkButton @click="deleteAllFiles()" style="margin: 0.5em 0 0.5em 0;"><Fa :icon="faTrashAlt"/> {{ $ts.deleteAllFiles }}</MkButton>
 			</details>
 			<details>
-				<summary>{{ $t('removeAllFollowing') }}</summary>
-				<MkButton @click="removeAllFollowing()" style="margin: 0.5em 0 0.5em 0;"><Fa :icon="faMinusCircle"/> {{ $t('removeAllFollowing') }}</MkButton>
+				<summary>{{ $ts.removeAllFollowing }}</summary>
+				<MkButton @click="removeAllFollowing()" style="margin: 0.5em 0 0.5em 0;"><Fa :icon="faMinusCircle"/> {{ $ts.removeAllFollowing }}</MkButton>
 				<MkInfo warn>{{ $t('removeAllFollowingDescription', { host: instance.host }) }}</MkInfo>
 			</details>
 		</div>
 		<details class="metadata section">
-			<summary class="label">{{ $t('metadata') }}</summary>
+			<summary class="label">{{ $ts.metadata }}</summary>
 			<pre><code>{{ JSON.stringify(instance, null, 2) }}</code></pre>
 		</details>
 	</div>
@@ -442,7 +442,7 @@ export default defineComponent({
 
 		showFollowing() {
 			os.modal(MkUsersDialog, {
-				title: this.$t('instanceFollowing'),
+				title: this.$ts.instanceFollowing,
 				pagination: {
 					endpoint: 'federation/following',
 					limit: 10,
@@ -456,7 +456,7 @@ export default defineComponent({
 
 		showFollowers() {
 			os.modal(MkUsersDialog, {
-				title: this.$t('instanceFollowers'),
+				title: this.$ts.instanceFollowers,
 				pagination: {
 					endpoint: 'federation/followers',
 					limit: 10,
@@ -470,7 +470,7 @@ export default defineComponent({
 
 		showUsers() {
 			os.modal(MkUsersDialog, {
-				title: this.$t('instanceUsers'),
+				title: this.$ts.instanceUsers,
 				pagination: {
 					endpoint: 'federation/users',
 					limit: 10,
diff --git a/src/client/pages/instance/logs.vue b/src/client/pages/instance/logs.vue
index 5743deec6..73862e9da 100644
--- a/src/client/pages/instance/logs.vue
+++ b/src/client/pages/instance/logs.vue
@@ -2,16 +2,16 @@
 <div class="_section">
 	<div class="_inputs">
 		<MkInput v-model:value="logDomain" :debounce="true">
-			<span>{{ $t('domain') }}</span>
+			<span>{{ $ts.domain }}</span>
 		</MkInput>
 		<MkSelect v-model:value="logLevel">
-			<template #label>{{ $t('level') }}</template>
-			<option value="all">{{ $t('levels.all') }}</option>
-			<option value="info">{{ $t('levels.info') }}</option>
-			<option value="success">{{ $t('levels.success') }}</option>
-			<option value="warning">{{ $t('levels.warning') }}</option>
-			<option value="error">{{ $t('levels.error') }}</option>
-			<option value="debug">{{ $t('levels.debug') }}</option>
+			<template #label>{{ $ts.level }}</template>
+			<option value="all">{{ $ts.levels.all }}</option>
+			<option value="info">{{ $ts.levels.info }}</option>
+			<option value="success">{{ $ts.levels.success }}</option>
+			<option value="warning">{{ $ts.levels.warning }}</option>
+			<option value="error">{{ $ts.levels.error }}</option>
+			<option value="debug">{{ $ts.levels.debug }}</option>
 		</MkSelect>
 	</div>
 
@@ -24,7 +24,7 @@
 		</code>
 	</div>
 
-	<MkButton @click="deleteAllLogs()" primary><Fa :icon="faTrashAlt"/> {{ $t('deleteAll') }}</MkButton>
+	<MkButton @click="deleteAllLogs()" primary><Fa :icon="faTrashAlt"/> {{ $ts.deleteAll }}</MkButton>
 </div>
 </template>
 
@@ -49,7 +49,7 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: {
-				title: this.$t('serverLogs'),
+				title: this.$ts.serverLogs,
 				icon: faStream
 			},
 			logs: [],
diff --git a/src/client/pages/instance/queue.chart.vue b/src/client/pages/instance/queue.chart.vue
index 6a43d14a6..2a8259b9d 100644
--- a/src/client/pages/instance/queue.chart.vue
+++ b/src/client/pages/instance/queue.chart.vue
@@ -19,7 +19,7 @@
 				<span style="margin-left: 8px; opacity: 0.7;">({{ number(job[1]) }} jobs)</span>
 			</div>
 		</div>
-		<span v-else style="opacity: 0.5;">{{ $t('noJobs') }}</span>
+		<span v-else style="opacity: 0.5;">{{ $ts.noJobs }}</span>
 	</div>
 </section>
 </template>
diff --git a/src/client/pages/instance/queue.vue b/src/client/pages/instance/queue.vue
index e824e8ae2..b8ad87568 100644
--- a/src/client/pages/instance/queue.vue
+++ b/src/client/pages/instance/queue.vue
@@ -8,7 +8,7 @@
 	</XQueue>
 	<section class="_section">
 		<div class="_content">
-			<MkButton @click="clear()"><Fa :icon="faTrashAlt"/> {{ $t('clearQueue') }}</MkButton>
+			<MkButton @click="clear()"><Fa :icon="faTrashAlt"/> {{ $ts.clearQueue }}</MkButton>
 		</div>
 	</section>
 </div>
@@ -31,7 +31,7 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: {
-				title: this.$t('jobQueue'),
+				title: this.$ts.jobQueue,
 				icon: faExchangeAlt,
 			},
 			connection: os.stream.useSharedConnection('queueStats'),
@@ -56,8 +56,8 @@ export default defineComponent({
 		clear() {
 			os.dialog({
 				type: 'warning',
-				title: this.$t('clearQueueConfirmTitle'),
-				text: this.$t('clearQueueConfirmText'),
+				title: this.$ts.clearQueueConfirmTitle,
+				text: this.$ts.clearQueueConfirmText,
 				showCancelButton: true
 			}).then(({ canceled }) => {
 				if (canceled) return;
diff --git a/src/client/pages/instance/relays.vue b/src/client/pages/instance/relays.vue
index 885f86129..f1c98355d 100644
--- a/src/client/pages/instance/relays.vue
+++ b/src/client/pages/instance/relays.vue
@@ -1,21 +1,21 @@
 <template>
 <div class="relaycxt">
 	<section class="_section add">
-		<div class="_title"><Fa :icon="faPlus"/> {{ $t('addRelay') }}</div>
+		<div class="_title"><Fa :icon="faPlus"/> {{ $ts.addRelay }}</div>
 		<div class="_content">
 			<MkInput v-model:value="inbox">
-				<span>{{ $t('inboxUrl') }}</span>
+				<span>{{ $ts.inboxUrl }}</span>
 			</MkInput>
-			<MkButton @click="add(inbox)" primary><Fa :icon="faPlus"/> {{ $t('add') }}</MkButton>
+			<MkButton @click="add(inbox)" primary><Fa :icon="faPlus"/> {{ $ts.add }}</MkButton>
 		</div>
 	</section>
 
 	<section class="_section relays">
-		<div class="_title"><Fa :icon="faProjectDiagram"/> {{ $t('addedRelays') }}</div>
+		<div class="_title"><Fa :icon="faProjectDiagram"/> {{ $ts.addedRelays }}</div>
 		<div class="_content relay" v-for="relay in relays" :key="relay.inbox">
 			<div>{{ relay.inbox }}</div>
 			<div>{{ $t(`_relayStatus.${relay.status}`) }}</div>
-			<MkButton class="button" inline @click="remove(relay.inbox)"><Fa :icon="faTrashAlt"/> {{ $t('remove') }}</MkButton>
+			<MkButton class="button" inline @click="remove(relay.inbox)"><Fa :icon="faTrashAlt"/> {{ $ts.remove }}</MkButton>
 		</div>
 	</section>
 </div>
@@ -38,7 +38,7 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: {
-				title: this.$t('relays'),
+				title: this.$ts.relays,
 				icon: faProjectDiagram,
 			},
 			relays: [],
diff --git a/src/client/pages/instance/settings.vue b/src/client/pages/instance/settings.vue
index 62271b3eb..761044b01 100644
--- a/src/client/pages/instance/settings.vue
+++ b/src/client/pages/instance/settings.vue
@@ -1,110 +1,110 @@
 <template>
 <div v-if="meta" class="_section">
 	<section class="_card _vMargin">
-		<div class="_title"><Fa :icon="faInfoCircle"/> {{ $t('basicInfo') }}</div>
+		<div class="_title"><Fa :icon="faInfoCircle"/> {{ $ts.basicInfo }}</div>
 		<div class="_content">
-			<MkInput v-model:value="name">{{ $t('instanceName') }}</MkInput>
-			<MkTextarea v-model:value="description">{{ $t('instanceDescription') }}</MkTextarea>
-			<MkInput v-model:value="iconUrl"><template #icon><Fa :icon="faLink"/></template>{{ $t('iconUrl') }}</MkInput>
-			<MkInput v-model:value="bannerUrl"><template #icon><Fa :icon="faLink"/></template>{{ $t('bannerUrl') }}</MkInput>
-			<MkInput v-model:value="backgroundImageUrl"><template #icon><Fa :icon="faLink"/></template>{{ $t('backgroundImageUrl') }}</MkInput>
-			<MkInput v-model:value="logoImageUrl"><template #icon><Fa :icon="faLink"/></template>{{ $t('logoImageUrl') }}</MkInput>
-			<MkInput v-model:value="tosUrl"><template #icon><Fa :icon="faLink"/></template>{{ $t('tosUrl') }}</MkInput>
-			<MkInput v-model:value="maintainerName">{{ $t('maintainerName') }}</MkInput>
-			<MkInput v-model:value="maintainerEmail" type="email"><template #icon><Fa :icon="faEnvelope"/></template>{{ $t('maintainerEmail') }}</MkInput>
+			<MkInput v-model:value="name">{{ $ts.instanceName }}</MkInput>
+			<MkTextarea v-model:value="description">{{ $ts.instanceDescription }}</MkTextarea>
+			<MkInput v-model:value="iconUrl"><template #icon><Fa :icon="faLink"/></template>{{ $ts.iconUrl }}</MkInput>
+			<MkInput v-model:value="bannerUrl"><template #icon><Fa :icon="faLink"/></template>{{ $ts.bannerUrl }}</MkInput>
+			<MkInput v-model:value="backgroundImageUrl"><template #icon><Fa :icon="faLink"/></template>{{ $ts.backgroundImageUrl }}</MkInput>
+			<MkInput v-model:value="logoImageUrl"><template #icon><Fa :icon="faLink"/></template>{{ $ts.logoImageUrl }}</MkInput>
+			<MkInput v-model:value="tosUrl"><template #icon><Fa :icon="faLink"/></template>{{ $ts.tosUrl }}</MkInput>
+			<MkInput v-model:value="maintainerName">{{ $ts.maintainerName }}</MkInput>
+			<MkInput v-model:value="maintainerEmail" type="email"><template #icon><Fa :icon="faEnvelope"/></template>{{ $ts.maintainerEmail }}</MkInput>
 		</div>
 		<div class="_footer">
-			<MkButton primary @click="save(true)"><Fa :icon="faSave"/> {{ $t('save') }}</MkButton>
+			<MkButton primary @click="save(true)"><Fa :icon="faSave"/> {{ $ts.save }}</MkButton>
 		</div>
 	</section>
 
-	<MkInput v-model:value="pinnedClipId">{{ $t('pinnedClipId') }}</MkInput>
+	<MkInput v-model:value="pinnedClipId">{{ $ts.pinnedClipId }}</MkInput>
 
 	<section class="_card _vMargin">
 		<div class="_content">
-			<MkInput v-model:value="maxNoteTextLength" type="number" :save="() => save()"><template #icon><Fa :icon="faPencilAlt"/></template>{{ $t('maxNoteTextLength') }}</MkInput>
+			<MkInput v-model:value="maxNoteTextLength" type="number" :save="() => save()"><template #icon><Fa :icon="faPencilAlt"/></template>{{ $ts.maxNoteTextLength }}</MkInput>
 		</div>
 		<div class="_content">
-			<MkSwitch v-model:value="enableLocalTimeline" @update:value="save()">{{ $t('enableLocalTimeline') }}</MkSwitch>
-			<MkSwitch v-model:value="enableGlobalTimeline" @update:value="save()">{{ $t('enableGlobalTimeline') }}</MkSwitch>
-			<MkInfo>{{ $t('disablingTimelinesInfo') }}</MkInfo>
+			<MkSwitch v-model:value="enableLocalTimeline" @update:value="save()">{{ $ts.enableLocalTimeline }}</MkSwitch>
+			<MkSwitch v-model:value="enableGlobalTimeline" @update:value="save()">{{ $ts.enableGlobalTimeline }}</MkSwitch>
+			<MkInfo>{{ $ts.disablingTimelinesInfo }}</MkInfo>
 		</div>
 		<div class="_content">
-			<MkSwitch v-model:value="useStarForReactionFallback" @update:value="save()">{{ $t('useStarForReactionFallback') }}</MkSwitch>
+			<MkSwitch v-model:value="useStarForReactionFallback" @update:value="save()">{{ $ts.useStarForReactionFallback }}</MkSwitch>
 		</div>
 	</section>
 
 	<section class="_card _vMargin">
-		<div class="_title"><Fa :icon="faUser"/> {{ $t('registration') }}</div>
+		<div class="_title"><Fa :icon="faUser"/> {{ $ts.registration }}</div>
 		<div class="_content">
-			<MkSwitch v-model:value="enableRegistration" @update:value="save()">{{ $t('enableRegistration') }}</MkSwitch>
-			<MkButton v-if="!enableRegistration" @click="invite">{{ $t('invite') }}</MkButton>
+			<MkSwitch v-model:value="enableRegistration" @update:value="save()">{{ $ts.enableRegistration }}</MkSwitch>
+			<MkButton v-if="!enableRegistration" @click="invite">{{ $ts.invite }}</MkButton>
 		</div>
 	</section>
 
 	<section class="_card _vMargin">
-		<div class="_title"><Fa :icon="faShieldAlt"/> {{ $t('hcaptcha') }}</div>
+		<div class="_title"><Fa :icon="faShieldAlt"/> {{ $ts.hcaptcha }}</div>
 		<div class="_content">
-			<MkSwitch v-model:value="enableHcaptcha">{{ $t('enableHcaptcha') }}</MkSwitch>
+			<MkSwitch v-model:value="enableHcaptcha">{{ $ts.enableHcaptcha }}</MkSwitch>
 			<template v-if="enableHcaptcha">
-				<MkInput v-model:value="hcaptchaSiteKey" :disabled="!enableHcaptcha"><template #icon><Fa :icon="faKey"/></template>{{ $t('hcaptchaSiteKey') }}</MkInput>
-				<MkInput v-model:value="hcaptchaSecretKey" :disabled="!enableHcaptcha"><template #icon><Fa :icon="faKey"/></template>{{ $t('hcaptchaSecretKey') }}</MkInput>
+				<MkInput v-model:value="hcaptchaSiteKey" :disabled="!enableHcaptcha"><template #icon><Fa :icon="faKey"/></template>{{ $ts.hcaptchaSiteKey }}</MkInput>
+				<MkInput v-model:value="hcaptchaSecretKey" :disabled="!enableHcaptcha"><template #icon><Fa :icon="faKey"/></template>{{ $ts.hcaptchaSecretKey }}</MkInput>
 			</template>
 		</div>
 		<div class="_content" v-if="enableHcaptcha">
-			<header>{{ $t('preview') }}</header>
+			<header>{{ $ts.preview }}</header>
 			<captcha v-if="enableHcaptcha" provider="hcaptcha" :sitekey="hcaptchaSiteKey || '10000000-ffff-ffff-ffff-000000000001'"/>
 		</div>
 		<div class="_footer">
-			<MkButton primary @click="save(true)"><Fa :icon="faSave"/> {{ $t('save') }}</MkButton>
+			<MkButton primary @click="save(true)"><Fa :icon="faSave"/> {{ $ts.save }}</MkButton>
 		</div>
 	</section>
 
 	<section class="_card _vMargin">
-		<div class="_title"><Fa :icon="faShieldAlt"/> {{ $t('recaptcha') }}</div>
+		<div class="_title"><Fa :icon="faShieldAlt"/> {{ $ts.recaptcha }}</div>
 		<div class="_content">
-			<MkSwitch v-model:value="enableRecaptcha" ref="enableRecaptcha">{{ $t('enableRecaptcha') }}</MkSwitch>
+			<MkSwitch v-model:value="enableRecaptcha" ref="enableRecaptcha">{{ $ts.enableRecaptcha }}</MkSwitch>
 			<template v-if="enableRecaptcha">
-				<MkInput v-model:value="recaptchaSiteKey" :disabled="!enableRecaptcha"><template #icon><Fa :icon="faKey"/></template>{{ $t('recaptchaSiteKey') }}</MkInput>
-				<MkInput v-model:value="recaptchaSecretKey" :disabled="!enableRecaptcha"><template #icon><Fa :icon="faKey"/></template>{{ $t('recaptchaSecretKey') }}</MkInput>
+				<MkInput v-model:value="recaptchaSiteKey" :disabled="!enableRecaptcha"><template #icon><Fa :icon="faKey"/></template>{{ $ts.recaptchaSiteKey }}</MkInput>
+				<MkInput v-model:value="recaptchaSecretKey" :disabled="!enableRecaptcha"><template #icon><Fa :icon="faKey"/></template>{{ $ts.recaptchaSecretKey }}</MkInput>
 			</template>
 		</div>
 		<div class="_content" v-if="enableRecaptcha && recaptchaSiteKey">
-			<header>{{ $t('preview') }}</header>
+			<header>{{ $ts.preview }}</header>
 			<captcha v-if="enableRecaptcha" provider="grecaptcha" :sitekey="recaptchaSiteKey"/>
 		</div>
 		<div class="_footer">
-			<MkButton primary @click="save(true)"><Fa :icon="faSave"/> {{ $t('save') }}</MkButton>
+			<MkButton primary @click="save(true)"><Fa :icon="faSave"/> {{ $ts.save }}</MkButton>
 		</div>
 	</section>
 
 	<section class="_card _vMargin">
-		<div class="_title"><Fa :icon="faEnvelope" /> {{ $t('emailConfig') }}</div>
+		<div class="_title"><Fa :icon="faEnvelope" /> {{ $ts.emailConfig }}</div>
 		<div class="_content">
-			<MkSwitch v-model:value="enableEmail" @update:value="save()">{{ $t('enableEmail') }}<template #desc>{{ $t('emailConfigInfo') }}</template></MkSwitch>
-			<MkInput v-model:value="email" type="email" :disabled="!enableEmail">{{ $t('email') }}</MkInput>
-			<div><b>{{ $t('smtpConfig') }}</b></div>
+			<MkSwitch v-model:value="enableEmail" @update:value="save()">{{ $ts.enableEmail }}<template #desc>{{ $ts.emailConfigInfo }}</template></MkSwitch>
+			<MkInput v-model:value="email" type="email" :disabled="!enableEmail">{{ $ts.email }}</MkInput>
+			<div><b>{{ $ts.smtpConfig }}</b></div>
 			<div class="_inputs">
-				<MkInput v-model:value="smtpHost" :disabled="!enableEmail">{{ $t('smtpHost') }}</MkInput>
-				<MkInput v-model:value="smtpPort" type="number" :disabled="!enableEmail">{{ $t('smtpPort') }}</MkInput>
+				<MkInput v-model:value="smtpHost" :disabled="!enableEmail">{{ $ts.smtpHost }}</MkInput>
+				<MkInput v-model:value="smtpPort" type="number" :disabled="!enableEmail">{{ $ts.smtpPort }}</MkInput>
 			</div>
 			<div class="_inputs">
-				<MkInput v-model:value="smtpUser" :disabled="!enableEmail">{{ $t('smtpUser') }}</MkInput>
-				<MkInput v-model:value="smtpPass" type="password" :disabled="!enableEmail">{{ $t('smtpPass') }}</MkInput>
+				<MkInput v-model:value="smtpUser" :disabled="!enableEmail">{{ $ts.smtpUser }}</MkInput>
+				<MkInput v-model:value="smtpPass" type="password" :disabled="!enableEmail">{{ $ts.smtpPass }}</MkInput>
 			</div>
-			<MkInfo>{{ $t('emptyToDisableSmtpAuth') }}</MkInfo>
-			<MkSwitch v-model:value="smtpSecure" :disabled="!enableEmail">{{ $t('smtpSecure') }}<template #desc>{{ $t('smtpSecureInfo') }}</template></MkSwitch>
+			<MkInfo>{{ $ts.emptyToDisableSmtpAuth }}</MkInfo>
+			<MkSwitch v-model:value="smtpSecure" :disabled="!enableEmail">{{ $ts.smtpSecure }}<template #desc>{{ $ts.smtpSecureInfo }}</template></MkSwitch>
 			<div>
-			  <MkButton :disabled="!enableEmail" primary inline @click="save(true)"><Fa :icon="faSave"/> {{ $t('save') }}</MkButton>
-				<MkButton :disabled="!enableEmail" inline @click="testEmail()">{{ $t('testEmail') }}</MkButton>
+			  <MkButton :disabled="!enableEmail" primary inline @click="save(true)"><Fa :icon="faSave"/> {{ $ts.save }}</MkButton>
+				<MkButton :disabled="!enableEmail" inline @click="testEmail()">{{ $ts.testEmail }}</MkButton>
 			</div>
 		</div>
 	</section>
 
 	<section class="_card _vMargin">
-		<div class="_title"><Fa :icon="faBolt"/> {{ $t('serviceworker') }}</div>
+		<div class="_title"><Fa :icon="faBolt"/> {{ $ts.serviceworker }}</div>
 		<div class="_content">
-			<MkSwitch v-model:value="enableServiceWorker">{{ $t('enableServiceworker') }}<template #desc>{{ $t('serviceworkerInfo') }}</template></MkSwitch>
+			<MkSwitch v-model:value="enableServiceWorker">{{ $ts.enableServiceworker }}<template #desc>{{ $ts.serviceworkerInfo }}</template></MkSwitch>
 			<template v-if="enableServiceWorker">
 				<div class="_inputs">
 					<MkInput v-model:value="swPublicKey" :disabled="!enableServiceWorker"><template #icon><Fa :icon="faKey"/></template>Public key</MkInput>
@@ -113,100 +113,100 @@
 			</template>
 		</div>
 		<div class="_footer">
-			<MkButton primary @click="save(true)"><Fa :icon="faSave"/> {{ $t('save') }}</MkButton>
+			<MkButton primary @click="save(true)"><Fa :icon="faSave"/> {{ $ts.save }}</MkButton>
 		</div>
 	</section>
 
 	<section class="_card _vMargin">
-		<div class="_title"><Fa :icon="faThumbtack"/> {{ $t('pinnedUsers') }}</div>
+		<div class="_title"><Fa :icon="faThumbtack"/> {{ $ts.pinnedUsers }}</div>
 		<div class="_content">
 			<MkTextarea v-model:value="pinnedUsers">
-				<template #desc>{{ $t('pinnedUsersDescription') }} <button class="_textButton" @click="addPinUser">{{ $t('addUser') }}</button></template>
+				<template #desc>{{ $ts.pinnedUsersDescription }} <button class="_textButton" @click="addPinUser">{{ $ts.addUser }}</button></template>
 			</MkTextarea>
 		</div>
 		<div class="_footer">
-			<MkButton primary @click="save(true)"><Fa :icon="faSave"/> {{ $t('save') }}</MkButton>
+			<MkButton primary @click="save(true)"><Fa :icon="faSave"/> {{ $ts.save }}</MkButton>
 		</div>
 	</section>
 
 	<section class="_card _vMargin">
-		<div class="_title"><Fa :icon="faThumbtack"/> {{ $t('pinnedPages') }}</div>
+		<div class="_title"><Fa :icon="faThumbtack"/> {{ $ts.pinnedPages }}</div>
 		<div class="_content">
 			<MkTextarea v-model:value="pinnedPages">
-				<template #desc>{{ $t('pinnedPagesDescription') }}</template>
+				<template #desc>{{ $ts.pinnedPagesDescription }}</template>
 			</MkTextarea>
 		</div>
 		<div class="_footer">
-			<MkButton primary @click="save(true)"><Fa :icon="faSave"/> {{ $t('save') }}</MkButton>
+			<MkButton primary @click="save(true)"><Fa :icon="faSave"/> {{ $ts.save }}</MkButton>
 		</div>
 	</section>
 
 	<section class="_card _vMargin">
-		<div class="_title"><Fa :icon="faCloud"/> {{ $t('files') }}</div>
+		<div class="_title"><Fa :icon="faCloud"/> {{ $ts.files }}</div>
 		<div class="_content">
-			<MkSwitch v-model:value="cacheRemoteFiles">{{ $t('cacheRemoteFiles') }}<template #desc>{{ $t('cacheRemoteFilesDescription') }}</template></MkSwitch>
-			<MkSwitch v-model:value="proxyRemoteFiles">{{ $t('proxyRemoteFiles') }}<template #desc>{{ $t('proxyRemoteFilesDescription') }}</template></MkSwitch>
-			<MkInput v-model:value="localDriveCapacityMb" type="number">{{ $t('driveCapacityPerLocalAccount') }}<template #suffix>MB</template><template #desc>{{ $t('inMb') }}</template></MkInput>
-			<MkInput v-model:value="remoteDriveCapacityMb" type="number" :disabled="!cacheRemoteFiles">{{ $t('driveCapacityPerRemoteAccount') }}<template #suffix>MB</template><template #desc>{{ $t('inMb') }}</template></MkInput>
+			<MkSwitch v-model:value="cacheRemoteFiles">{{ $ts.cacheRemoteFiles }}<template #desc>{{ $ts.cacheRemoteFilesDescription }}</template></MkSwitch>
+			<MkSwitch v-model:value="proxyRemoteFiles">{{ $ts.proxyRemoteFiles }}<template #desc>{{ $ts.proxyRemoteFilesDescription }}</template></MkSwitch>
+			<MkInput v-model:value="localDriveCapacityMb" type="number">{{ $ts.driveCapacityPerLocalAccount }}<template #suffix>MB</template><template #desc>{{ $ts.inMb }}</template></MkInput>
+			<MkInput v-model:value="remoteDriveCapacityMb" type="number" :disabled="!cacheRemoteFiles">{{ $ts.driveCapacityPerRemoteAccount }}<template #suffix>MB</template><template #desc>{{ $ts.inMb }}</template></MkInput>
 		</div>
 		<div class="_footer">
-			<MkButton primary @click="save(true)"><Fa :icon="faSave"/> {{ $t('save') }}</MkButton>
+			<MkButton primary @click="save(true)"><Fa :icon="faSave"/> {{ $ts.save }}</MkButton>
 		</div>
 	</section>
 
 	<section class="_card _vMargin">
-		<div class="_title"><Fa :icon="faCloud"/> {{ $t('objectStorage') }}</div>
+		<div class="_title"><Fa :icon="faCloud"/> {{ $ts.objectStorage }}</div>
 		<div class="_content">
-			<MkSwitch v-model:value="useObjectStorage">{{ $t('useObjectStorage') }}</MkSwitch>
+			<MkSwitch v-model:value="useObjectStorage">{{ $ts.useObjectStorage }}</MkSwitch>
 			<template v-if="useObjectStorage">
-				<MkInput v-model:value="objectStorageBaseUrl" :disabled="!useObjectStorage">{{ $t('objectStorageBaseUrl') }}<template #desc>{{ $t('objectStorageBaseUrlDesc') }}</template></MkInput>
+				<MkInput v-model:value="objectStorageBaseUrl" :disabled="!useObjectStorage">{{ $ts.objectStorageBaseUrl }}<template #desc>{{ $ts.objectStorageBaseUrlDesc }}</template></MkInput>
 				<div class="_inputs">
-					<MkInput v-model:value="objectStorageBucket" :disabled="!useObjectStorage">{{ $t('objectStorageBucket') }}<template #desc>{{ $t('objectStorageBucketDesc') }}</template></MkInput>
-					<MkInput v-model:value="objectStoragePrefix" :disabled="!useObjectStorage">{{ $t('objectStoragePrefix') }}<template #desc>{{ $t('objectStoragePrefixDesc') }}</template></MkInput>
+					<MkInput v-model:value="objectStorageBucket" :disabled="!useObjectStorage">{{ $ts.objectStorageBucket }}<template #desc>{{ $ts.objectStorageBucketDesc }}</template></MkInput>
+					<MkInput v-model:value="objectStoragePrefix" :disabled="!useObjectStorage">{{ $ts.objectStoragePrefix }}<template #desc>{{ $ts.objectStoragePrefixDesc }}</template></MkInput>
 				</div>
-				<MkInput v-model:value="objectStorageEndpoint" :disabled="!useObjectStorage">{{ $t('objectStorageEndpoint') }}<template #desc>{{ $t('objectStorageEndpointDesc') }}</template></MkInput>
+				<MkInput v-model:value="objectStorageEndpoint" :disabled="!useObjectStorage">{{ $ts.objectStorageEndpoint }}<template #desc>{{ $ts.objectStorageEndpointDesc }}</template></MkInput>
 				<div class="_inputs">
-					<MkInput v-model:value="objectStorageRegion" :disabled="!useObjectStorage">{{ $t('objectStorageRegion') }}<template #desc>{{ $t('objectStorageRegionDesc') }}</template></MkInput>
+					<MkInput v-model:value="objectStorageRegion" :disabled="!useObjectStorage">{{ $ts.objectStorageRegion }}<template #desc>{{ $ts.objectStorageRegionDesc }}</template></MkInput>
 				</div>
 				<div class="_inputs">
 					<MkInput v-model:value="objectStorageAccessKey" :disabled="!useObjectStorage"><template #icon><Fa :icon="faKey"/></template>Access key</MkInput>
 					<MkInput v-model:value="objectStorageSecretKey" :disabled="!useObjectStorage"><template #icon><Fa :icon="faKey"/></template>Secret key</MkInput>
 				</div>
-				<MkSwitch v-model:value="objectStorageUseSSL" :disabled="!useObjectStorage">{{ $t('objectStorageUseSSL') }}<template #desc>{{ $t('objectStorageUseSSLDesc') }}</template></MkSwitch>
-				<MkSwitch v-model:value="objectStorageUseProxy" :disabled="!useObjectStorage">{{ $t('objectStorageUseProxy') }}<template #desc>{{ $t('objectStorageUseProxyDesc') }}</template></MkSwitch>
-				<MkSwitch v-model:value="objectStorageSetPublicRead" :disabled="!useObjectStorage">{{ $t('objectStorageSetPublicRead') }}</MkSwitch>
+				<MkSwitch v-model:value="objectStorageUseSSL" :disabled="!useObjectStorage">{{ $ts.objectStorageUseSSL }}<template #desc>{{ $ts.objectStorageUseSSLDesc }}</template></MkSwitch>
+				<MkSwitch v-model:value="objectStorageUseProxy" :disabled="!useObjectStorage">{{ $ts.objectStorageUseProxy }}<template #desc>{{ $ts.objectStorageUseProxyDesc }}</template></MkSwitch>
+				<MkSwitch v-model:value="objectStorageSetPublicRead" :disabled="!useObjectStorage">{{ $ts.objectStorageSetPublicRead }}</MkSwitch>
 			</template>
 		</div>
 		<div class="_footer">
-			<MkButton primary @click="save(true)"><Fa :icon="faSave"/> {{ $t('save') }}</MkButton>
+			<MkButton primary @click="save(true)"><Fa :icon="faSave"/> {{ $ts.save }}</MkButton>
 		</div>
 	</section>
 
 	<section class="_card _vMargin">
-		<div class="_title"><Fa :icon="faGhost"/> {{ $t('proxyAccount') }}</div>
+		<div class="_title"><Fa :icon="faGhost"/> {{ $ts.proxyAccount }}</div>
 		<div class="_content">
-			<MkInput :value="proxyAccount ? proxyAccount.username : null" disabled><template #prefix>@</template>{{ $t('proxyAccount') }}<template #desc>{{ $t('proxyAccountDescription') }}</template></MkInput>
-			<MkButton primary @click="chooseProxyAccount">{{ $t('chooseProxyAccount') }}</MkButton>
+			<MkInput :value="proxyAccount ? proxyAccount.username : null" disabled><template #prefix>@</template>{{ $ts.proxyAccount }}<template #desc>{{ $ts.proxyAccountDescription }}</template></MkInput>
+			<MkButton primary @click="chooseProxyAccount">{{ $ts.chooseProxyAccount }}</MkButton>
 		</div>
 	</section>
 
 	<section class="_card _vMargin">
-		<div class="_title"><Fa :icon="faBan"/> {{ $t('blockedInstances') }}</div>
+		<div class="_title"><Fa :icon="faBan"/> {{ $ts.blockedInstances }}</div>
 		<div class="_content">
 			<MkTextarea v-model:value="blockedHosts">
-				<template #desc>{{ $t('blockedInstancesDescription') }}</template>
+				<template #desc>{{ $ts.blockedInstancesDescription }}</template>
 			</MkTextarea>
 		</div>
 		<div class="_footer">
-			<MkButton primary @click="save(true)"><Fa :icon="faSave"/> {{ $t('save') }}</MkButton>
+			<MkButton primary @click="save(true)"><Fa :icon="faSave"/> {{ $ts.save }}</MkButton>
 		</div>
 	</section>
 
 	<section class="_card _vMargin">
-		<div class="_title"><Fa :icon="faShareAlt"/> {{ $t('integration') }}</div>
+		<div class="_title"><Fa :icon="faShareAlt"/> {{ $ts.integration }}</div>
 		<div class="_content">
 			<header><Fa :icon="faTwitter"/> Twitter</header>
-			<MkSwitch v-model:value="enableTwitterIntegration">{{ $t('enable') }}</MkSwitch>
+			<MkSwitch v-model:value="enableTwitterIntegration">{{ $ts.enable }}</MkSwitch>
 			<template v-if="enableTwitterIntegration">
 				<MkInfo>Callback URL: {{ `${url}/api/tw/cb` }}</MkInfo>
 				<MkInput v-model:value="twitterConsumerKey" :disabled="!enableTwitterIntegration"><template #icon><Fa :icon="faKey"/></template>Consumer Key</MkInput>
@@ -215,7 +215,7 @@
 		</div>
 		<div class="_content">
 			<header><Fa :icon="faGithub"/> GitHub</header>
-			<MkSwitch v-model:value="enableGithubIntegration">{{ $t('enable') }}</MkSwitch>
+			<MkSwitch v-model:value="enableGithubIntegration">{{ $ts.enable }}</MkSwitch>
 			<template v-if="enableGithubIntegration">
 				<MkInfo>Callback URL: {{ `${url}/api/gh/cb` }}</MkInfo>
 				<MkInput v-model:value="githubClientId" :disabled="!enableGithubIntegration"><template #icon><Fa :icon="faKey"/></template>Client ID</MkInput>
@@ -224,7 +224,7 @@
 		</div>
 		<div class="_content">
 			<header><Fa :icon="faDiscord"/> Discord</header>
-			<MkSwitch v-model:value="enableDiscordIntegration">{{ $t('enable') }}</MkSwitch>
+			<MkSwitch v-model:value="enableDiscordIntegration">{{ $ts.enable }}</MkSwitch>
 			<template v-if="enableDiscordIntegration">
 				<MkInfo>Callback URL: {{ `${url}/api/dc/cb` }}</MkInfo>
 				<MkInput v-model:value="discordClientId" :disabled="!enableDiscordIntegration"><template #icon><Fa :icon="faKey"/></template>Client ID</MkInput>
@@ -232,7 +232,7 @@
 			</template>
 		</div>
 		<div class="_footer">
-			<MkButton primary @click="save(true)"><Fa :icon="faSave"/> {{ $t('save') }}</MkButton>
+			<MkButton primary @click="save(true)"><Fa :icon="faSave"/> {{ $ts.save }}</MkButton>
 		</div>
 	</section>
 
@@ -240,7 +240,7 @@
 		<div class="_title"><Fa :icon="faArchway" /> Summaly Proxy</div>
 		<div class="_content">
 			<MkInput v-model:value="summalyProxy">URL</MkInput>
-			<MkButton primary @click="save(true)"><Fa :icon="faSave"/> {{ $t('save') }}</MkButton>
+			<MkButton primary @click="save(true)"><Fa :icon="faSave"/> {{ $ts.save }}</MkButton>
 		</div>
 	</section>
 </div>
@@ -274,7 +274,7 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: {
-				title: this.$t('instance'),
+				title: this.$ts.instance,
 				icon: faCog,
 			},
 			meta: null,
@@ -423,8 +423,8 @@ export default defineComponent({
 				os.dialog({
 					type: 'question', // warning だと間違って cancel するかもしれない
 					showCancelButton: true,
-					title: this.$t('settingGuide'),
-					text: this.$t('avoidMultiCaptchaConfirm'),
+					title: this.$ts.settingGuide,
+					text: this.$ts.avoidMultiCaptchaConfirm,
 				}).then(({ canceled }) => {
 					if (canceled) {
 						return;
@@ -440,8 +440,8 @@ export default defineComponent({
 				os.dialog({
 					type: 'question', // warning だと間違って cancel するかもしれない
 					showCancelButton: true,
-					title: this.$t('settingGuide'),
-					text: this.$t('avoidMultiCaptchaConfirm'),
+					title: this.$ts.settingGuide,
+					text: this.$ts.avoidMultiCaptchaConfirm,
 				}).then(({ canceled }) => {
 					if (canceled) {
 						return;
diff --git a/src/client/pages/instance/user-dialog.vue b/src/client/pages/instance/user-dialog.vue
index 810db8c50..b690826d2 100644
--- a/src/client/pages/instance/user-dialog.vue
+++ b/src/client/pages/instance/user-dialog.vue
@@ -24,17 +24,17 @@
 		</div>
 		<div class="_section">
 			<div class="_content">
-				<MkSwitch v-if="user.host == null && $i.isAdmin && (moderator || !user.isAdmin)" @update:value="toggleModerator" v-model:value="moderator">{{ $t('moderator') }}</MkSwitch>
-				<MkSwitch @update:value="toggleSilence" v-model:value="silenced">{{ $t('silence') }}</MkSwitch>
-				<MkSwitch @update:value="toggleSuspend" v-model:value="suspended">{{ $t('suspend') }}</MkSwitch>
+				<MkSwitch v-if="user.host == null && $i.isAdmin && (moderator || !user.isAdmin)" @update:value="toggleModerator" v-model:value="moderator">{{ $ts.moderator }}</MkSwitch>
+				<MkSwitch @update:value="toggleSilence" v-model:value="silenced">{{ $ts.silence }}</MkSwitch>
+				<MkSwitch @update:value="toggleSuspend" v-model:value="suspended">{{ $ts.suspend }}</MkSwitch>
 			</div>
 		</div>
 		<div class="_section">
 			<div class="_content">
-				<MkButton full @click="openProfile"><Fa :icon="faExternalLinkSquareAlt"/> {{ $t('profile') }}</MkButton>
-				<MkButton full v-if="user.host != null" @click="updateRemoteUser"><Fa :icon="faSync"/> {{ $t('updateRemoteUser') }}</MkButton>
-				<MkButton full @click="resetPassword"><Fa :icon="faKey"/> {{ $t('resetPassword') }}</MkButton>
-				<MkButton full @click="deleteAllFiles" danger><Fa :icon="faTrashAlt"/> {{ $t('deleteAllFiles') }}</MkButton>
+				<MkButton full @click="openProfile"><Fa :icon="faExternalLinkSquareAlt"/> {{ $ts.profile }}</MkButton>
+				<MkButton full v-if="user.host != null" @click="updateRemoteUser"><Fa :icon="faSync"/> {{ $ts.updateRemoteUser }}</MkButton>
+				<MkButton full @click="resetPassword"><Fa :icon="faKey"/> {{ $ts.resetPassword }}</MkButton>
+				<MkButton full @click="deleteAllFiles" danger><Fa :icon="faTrashAlt"/> {{ $ts.deleteAllFiles }}</MkButton>
 			</div>
 		</div>
 		<div class="_section">
@@ -139,7 +139,7 @@ export default defineComponent({
 			const confirm = await os.dialog({
 				type: 'warning',
 				showCancelButton: true,
-				text: v ? this.$t('silenceConfirm') : this.$t('unsilenceConfirm'),
+				text: v ? this.$ts.silenceConfirm : this.$ts.unsilenceConfirm,
 			});
 			if (confirm.canceled) {
 				this.silenced = !v;
@@ -153,7 +153,7 @@ export default defineComponent({
 			const confirm = await os.dialog({
 				type: 'warning',
 				showCancelButton: true,
-				text: v ? this.$t('suspendConfirm') : this.$t('unsuspendConfirm'),
+				text: v ? this.$ts.suspendConfirm : this.$ts.unsuspendConfirm,
 			});
 			if (confirm.canceled) {
 				this.suspended = !v;
@@ -172,7 +172,7 @@ export default defineComponent({
 			const confirm = await os.dialog({
 				type: 'warning',
 				showCancelButton: true,
-				text: this.$t('deleteAllFilesConfirm'),
+				text: this.$ts.deleteAllFilesConfirm,
 			});
 			if (confirm.canceled) return;
 			const process = async () => {
diff --git a/src/client/pages/instance/users.vue b/src/client/pages/instance/users.vue
index 1bf12196e..b2465991d 100644
--- a/src/client/pages/instance/users.vue
+++ b/src/client/pages/instance/users.vue
@@ -2,53 +2,53 @@
 <div class="mk-instance-users">
 	<div class="_section">
 		<div class="_content">
-			<MkButton inline primary @click="addUser()"><Fa :icon="faPlus"/> {{ $t('addUser') }}</MkButton>
+			<MkButton inline primary @click="addUser()"><Fa :icon="faPlus"/> {{ $ts.addUser }}</MkButton>
 		</div>
 	</div>
 
 	<div class="_section lookup">
-		<div class="_title"><Fa :icon="faSearch"/> {{ $t('lookup') }}</div>
+		<div class="_title"><Fa :icon="faSearch"/> {{ $ts.lookup }}</div>
 		<div class="_content">
 			<MkInput class="target" v-model:value="target" type="text" @enter="showUser()">
-				<span>{{ $t('usernameOrUserId') }}</span>
+				<span>{{ $ts.usernameOrUserId }}</span>
 			</MkInput>
-			<MkButton @click="showUser()" primary><Fa :icon="faSearch"/> {{ $t('lookup') }}</MkButton>
+			<MkButton @click="showUser()" primary><Fa :icon="faSearch"/> {{ $ts.lookup }}</MkButton>
 		</div>
 	</div>
 
 	<div class="_section users">
-		<div class="_title"><Fa :icon="faUsers"/> {{ $t('users') }}</div>
+		<div class="_title"><Fa :icon="faUsers"/> {{ $ts.users }}</div>
 		<div class="_content">
 			<div class="inputs" style="display: flex;">
 				<MkSelect v-model:value="sort" style="margin: 0; flex: 1;">
-					<template #label>{{ $t('sort') }}</template>
-					<option value="-createdAt">{{ $t('registeredDate') }} ({{ $t('ascendingOrder') }})</option>
-					<option value="+createdAt">{{ $t('registeredDate') }} ({{ $t('descendingOrder') }})</option>
-					<option value="-updatedAt">{{ $t('lastUsed') }} ({{ $t('ascendingOrder') }})</option>
-					<option value="+updatedAt">{{ $t('lastUsed') }} ({{ $t('descendingOrder') }})</option>
+					<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>
 				</MkSelect>
 				<MkSelect v-model:value="state" style="margin: 0; flex: 1;">
-					<template #label>{{ $t('state') }}</template>
-					<option value="all">{{ $t('all') }}</option>
-					<option value="available">{{ $t('normal') }}</option>
-					<option value="admin">{{ $t('administrator') }}</option>
-					<option value="moderator">{{ $t('moderator') }}</option>
-					<option value="silenced">{{ $t('silence') }}</option>
-					<option value="suspended">{{ $t('suspend') }}</option>
+					<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>
 				</MkSelect>
 				<MkSelect v-model:value="origin" style="margin: 0; flex: 1;">
-					<template #label>{{ $t('instance') }}</template>
-					<option value="combined">{{ $t('all') }}</option>
-					<option value="local">{{ $t('local') }}</option>
-					<option value="remote">{{ $t('remote') }}</option>
+					<template #label>{{ $ts.instance }}</template>
+					<option value="combined">{{ $ts.all }}</option>
+					<option value="local">{{ $ts.local }}</option>
+					<option value="remote">{{ $ts.remote }}</option>
 				</MkSelect>
 			</div>
 			<div class="inputs" style="display: flex; padding-top: 1.2em;">
 				<MkInput v-model:value="searchUsername" style="margin: 0; flex: 1;" type="text" spellcheck="false" @update:value="$refs.users.reload()">
-					<span>{{ $t('username') }}</span>
+					<span>{{ $ts.username }}</span>
 				</MkInput>
 				<MkInput v-model:value="searchHost" style="margin: 0; flex: 1;" type="text" spellcheck="false" @update:value="$refs.users.reload()" :disabled="pagination.params().origin === 'local'">
-					<span>{{ $t('host') }}</span>
+					<span>{{ $ts.host }}</span>
 				</MkInput>
 			</div>
 
@@ -65,10 +65,10 @@
 							<span class="punished" v-if="user.isSuspended"><Fa :icon="faSnowflake"/></span>
 						</header>
 						<div>
-							<span>{{ $t('lastUsed') }}: <MkTime v-if="user.updatedAt" :time="user.updatedAt" mode="detail"/></span>
+							<span>{{ $ts.lastUsed }}: <MkTime v-if="user.updatedAt" :time="user.updatedAt" mode="detail"/></span>
 						</div>
 						<div>
-							<span>{{ $t('registeredDate') }}: <MkTime :time="user.createdAt" mode="detail"/></span>
+							<span>{{ $ts.registeredDate }}: <MkTime :time="user.createdAt" mode="detail"/></span>
 						</div>
 					</div>
 				</button>
@@ -101,7 +101,7 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: {
-				title: this.$t('users'),
+				title: this.$ts.users,
 				icon: faUsers,
 				action: {
 					icon: faSearch,
@@ -153,7 +153,7 @@ export default defineComponent({
 					if (_notFound) {
 						os.dialog({
 							type: 'error',
-							text: this.$t('noSuchUser')
+							text: this.$ts.noSuchUser
 						});
 					} else {
 						_notFound = true;
@@ -185,13 +185,13 @@ export default defineComponent({
 
 		async addUser() {
 			const { canceled: canceled1, result: username } = await os.dialog({
-				title: this.$t('username'),
+				title: this.$ts.username,
 				input: true
 			});
 			if (canceled1) return;
 
 			const { canceled: canceled2, result: password } = await os.dialog({
-				title: this.$t('password'),
+				title: this.$ts.password,
 				input: { type: 'password' }
 			});
 			if (canceled2) return;
diff --git a/src/client/pages/mentions.vue b/src/client/pages/mentions.vue
index 396f22c0b..943a0e868 100644
--- a/src/client/pages/mentions.vue
+++ b/src/client/pages/mentions.vue
@@ -18,7 +18,7 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: {
-				title: this.$t('mentions'),
+				title: this.$ts.mentions,
 				icon: faAt
 			},
 			pagination: {
diff --git a/src/client/pages/messages.vue b/src/client/pages/messages.vue
index b8d715609..60621fcc1 100644
--- a/src/client/pages/messages.vue
+++ b/src/client/pages/messages.vue
@@ -18,7 +18,7 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: {
-				title: this.$t('directNotes'),
+				title: this.$ts.directNotes,
 				icon: faEnvelope
 			},
 			pagination: {
diff --git a/src/client/pages/messaging/index.vue b/src/client/pages/messaging/index.vue
index 3df15cbd5..08574fe5a 100644
--- a/src/client/pages/messaging/index.vue
+++ b/src/client/pages/messaging/index.vue
@@ -1,7 +1,7 @@
 <template>
 <div class="_section">
 	<div class="mk-messaging _content" v-size="{ max: [400] }">
-		<MkButton @click="start" primary class="start"><Fa :icon="faPlus"/> {{ $t('startMessaging') }}</MkButton>
+		<MkButton @click="start" primary class="start"><Fa :icon="faPlus"/> {{ $ts.startMessaging }}</MkButton>
 
 		<div class="history" v-if="messages.length > 0">
 			<MkA v-for="(message, i) in messages"
@@ -23,14 +23,14 @@
 						<MkTime :time="message.createdAt" class="time"/>
 					</header>
 					<div class="body">
-						<p class="text"><span class="me" v-if="isMe(message)">{{ $t('you') }}:</span>{{ message.text }}</p>
+						<p class="text"><span class="me" v-if="isMe(message)">{{ $ts.you }}:</span>{{ message.text }}</p>
 					</div>
 				</div>
 			</MkA>
 		</div>
 		<div class="_fullinfo" v-if="!fetching && messages.length == 0">
 			<img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
-			<div>{{ $t('noHistory') }}</div>
+			<div>{{ $ts.noHistory }}</div>
 		</div>
 		<MkLoading v-if="fetching"/>
 	</div>
@@ -53,7 +53,7 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: {
-				title: this.$t('messaging'),
+				title: this.$ts.messaging,
 				icon: faComments
 			},
 			fetching: true,
@@ -119,11 +119,11 @@ export default defineComponent({
 
 		start(ev) {
 			os.modalMenu([{
-				text: this.$t('messagingWithUser'),
+				text: this.$ts.messagingWithUser,
 				icon: faUser,
 				action: () => { this.startUser() }
 			}, {
-				text: this.$t('messagingWithGroup'),
+				text: this.$ts.messagingWithGroup,
 				icon: faUsers,
 				action: () => { this.startGroup() }
 			}], ev.currentTarget || ev.target);
@@ -141,14 +141,14 @@ export default defineComponent({
 			if (groups1.length === 0 && groups2.length === 0) {
 				os.dialog({
 					type: 'warning',
-					title: this.$t('youHaveNoGroups'),
-					text: this.$t('joinOrCreateGroup'),
+					title: this.$ts.youHaveNoGroups,
+					text: this.$ts.joinOrCreateGroup,
 				});
 				return;
 			}
 			const { canceled, result: group } = await os.dialog({
 				type: null,
-				title: this.$t('group'),
+				title: this.$ts.group,
 				select: {
 					items: groups1.concat(groups2).map(group => ({
 						value: group, text: group.name
diff --git a/src/client/pages/messaging/messaging-room.form.vue b/src/client/pages/messaging/messaging-room.form.vue
index ca7fa4905..e561cb3db 100644
--- a/src/client/pages/messaging/messaging-room.form.vue
+++ b/src/client/pages/messaging/messaging-room.form.vue
@@ -8,10 +8,10 @@
 		ref="text"
 		@keypress="onKeypress"
 		@paste="onPaste"
-		:placeholder="$t('inputMessageHere')"
+		:placeholder="$ts.inputMessageHere"
 	></textarea>
 	<div class="file" @click="file = null" v-if="file">{{ file.name }}</div>
-	<button class="send _button" @click="send" :disabled="!canSend || sending" :title="$t('send')">
+	<button class="send _button" @click="send" :disabled="!canSend || sending" :title="$ts.send">
 		<template v-if="!sending"><Fa :icon="faPaperPlane"/></template><template v-if="sending"><Fa icon="spinner .spin"/></template>
 	</button>
 	<button class="_button" @click="chooseFile"><Fa :icon="faPhotoVideo"/></button>
@@ -94,7 +94,7 @@ export default defineComponent({
 					const formatted = `${formatTimeString(new Date(file.lastModified), this.$store.state.pastedFileName).replace(/{{number}}/g, '1')}${ext}`;
 					const name = this.$store.state.pasteDialog
 						? await os.dialog({
-							title: this.$t('enterFileName'),
+							title: this.$ts.enterFileName,
 							input: {
 								default: formatted
 							},
@@ -107,7 +107,7 @@ export default defineComponent({
 				if (items[0].kind == 'file') {
 					os.dialog({
 						type: 'error',
-						text: this.$t('onlyOneFileCanBeAttached')
+						text: this.$ts.onlyOneFileCanBeAttached
 					});
 				}
 			}
@@ -132,7 +132,7 @@ export default defineComponent({
 				e.preventDefault();
 				os.dialog({
 					type: 'error',
-					text: this.$t('onlyOneFileCanBeAttached')
+					text: this.$ts.onlyOneFileCanBeAttached
 				});
 				return;
 			}
@@ -153,7 +153,7 @@ export default defineComponent({
 		},
 
 		chooseFile(e) {
-			selectFile(e.currentTarget || e.target, this.$t('selectFile'), false).then(file => {
+			selectFile(e.currentTarget || e.target, this.$ts.selectFile, false).then(file => {
 				this.file = file;
 			});
 		},
diff --git a/src/client/pages/messaging/messaging-room.message.vue b/src/client/pages/messaging/messaging-room.message.vue
index 0b6d9affa..d379c0c3c 100644
--- a/src/client/pages/messaging/messaging-room.message.vue
+++ b/src/client/pages/messaging/messaging-room.message.vue
@@ -3,7 +3,7 @@
 	<MkAvatar class="avatar" :user="message.user"/>
 	<div class="content">
 		<div class="balloon" :class="{ noText: message.text == null }" >
-			<button class="delete-button" v-if="isMe" :title="$t('delete')" @click="del">
+			<button class="delete-button" v-if="isMe" :title="$ts.delete" @click="del">
 				<img src="/assets/remove.png" alt="Delete"/>
 			</button>
 			<div class="content" v-if="!message.isDeleted">
@@ -16,17 +16,17 @@
 				</div>
 			</div>
 			<div class="content" v-else>
-				<p class="is-deleted">{{ $t('deleted') }}</p>
+				<p class="is-deleted">{{ $ts.deleted }}</p>
 			</div>
 		</div>
 		<div></div>
 		<MkUrlPreview v-for="url in urls" :url="url" :key="url" style="margin: 8px 0;"/>
 		<footer>
 			<template v-if="isGroup">
-				<span class="read" v-if="message.reads.length > 0">{{ $t('messageRead') }} {{ message.reads.length }}</span>
+				<span class="read" v-if="message.reads.length > 0">{{ $ts.messageRead }} {{ message.reads.length }}</span>
 			</template>
 			<template v-else>
-				<span class="read" v-if="isMe && message.isRead">{{ $t('messageRead') }}</span>
+				<span class="read" v-if="isMe && message.isRead">{{ $ts.messageRead }}</span>
 			</template>
 			<MkTime :time="message.createdAt"/>
 			<template v-if="message.is_edited"><Fa icon="pencil-alt"/></template>
diff --git a/src/client/pages/messaging/messaging-room.vue b/src/client/pages/messaging/messaging-room.vue
index 0a26b4926..7fdd0a201 100644
--- a/src/client/pages/messaging/messaging-room.vue
+++ b/src/client/pages/messaging/messaging-room.vue
@@ -6,10 +6,10 @@
 	<div class="_content mk-messaging-room">
 		<div class="body">
 			<MkLoading v-if="fetching"/>
-			<p class="empty" v-if="!fetching && messages.length == 0"><Fa :icon="faInfoCircle"/>{{ $t('noMessagesYet') }}</p>
-			<p class="no-history" v-if="!fetching && messages.length > 0 && !existMoreMessages"><Fa :icon="faFlag"/>{{ $t('noMoreHistory') }}</p>
+			<p class="empty" v-if="!fetching && messages.length == 0"><Fa :icon="faInfoCircle"/>{{ $ts.noMessagesYet }}</p>
+			<p class="no-history" v-if="!fetching && messages.length > 0 && !existMoreMessages"><Fa :icon="faFlag"/>{{ $ts.noMoreHistory }}</p>
 			<button class="more _button" ref="loadMore" :class="{ fetching: fetchingMoreMessages }" v-show="existMoreMessages" @click="fetchMoreMessages" :disabled="fetchingMoreMessages">
-				<template v-if="fetchingMoreMessages"><Fa icon="spinner" pulse fixed-width/></template>{{ fetchingMoreMessages ? $t('loading') : $t('loadMore') }}
+				<template v-if="fetchingMoreMessages"><Fa icon="spinner" pulse fixed-width/></template>{{ fetchingMoreMessages ? $ts.loading : $ts.loadMore }}
 			</button>
 			<XList class="messages" :items="messages" v-slot="{ item: message }" direction="up" reversed>
 				<XMessage :message="message" :is-group="group != null" :key="message.id"/>
@@ -18,7 +18,7 @@
 		<footer>
 			<transition name="fade">
 				<div class="new-message" v-show="showIndicator">
-					<button class="_buttonPrimary" @click="onIndicatorClick"><i><Fa :icon="faArrowCircleDown"/></i>{{ $t('newMessageExists') }}</button>
+					<button class="_buttonPrimary" @click="onIndicatorClick"><i><Fa :icon="faArrowCircleDown"/></i>{{ $ts.newMessageExists }}</button>
 				</div>
 			</transition>
 			<XForm v-if="!fetching" :user="user" :group="group" ref="form"/>
@@ -174,7 +174,7 @@ const Component = defineComponent({
 			} else if (e.dataTransfer.files.length > 1) {
 				os.dialog({
 					type: 'error',
-					text: this.$t('onlyOneFileCanBeAttached')
+					text: this.$ts.onlyOneFileCanBeAttached
 				});
 				return;
 			}
@@ -311,14 +311,14 @@ const Component = defineComponent({
 			const path = this.groupId ? `/my/messaging/group/${this.groupId}` : `/my/messaging/${this.userAcct}`;
 
 			os.modalMenu([this.inWindow ? undefined : {
-				text: this.$t('openInWindow'),
+				text: this.$ts.openInWindow,
 				icon: faWindowMaximize,
 				action: () => {
 					os.pageWindow(path);
 					this.$router.back();
 				},
 			}, this.inWindow ? undefined : {
-				text: this.$t('popout'),
+				text: this.$ts.popout,
 				icon: faExternalLinkAlt,
 				action: () => {
 					popout(path);
diff --git a/src/client/pages/mfm-cheat-sheet.vue b/src/client/pages/mfm-cheat-sheet.vue
index f1e9dc195..5bae1cb93 100644
--- a/src/client/pages/mfm-cheat-sheet.vue
+++ b/src/client/pages/mfm-cheat-sheet.vue
@@ -2,13 +2,13 @@
 <div class="mwysmxbg">
 	<div class="_section">
 		<div class="_content">
-			<p>{{ $t('_mfm.intro') }}</p>
+			<p>{{ $ts._mfm.intro }}</p>
 		</div>
 	</div>
 	<div class="_section">
-		<div class="_title">{{ $t('_mfm.mention') }}</div>
+		<div class="_title">{{ $ts._mfm.mention }}</div>
 		<div class="_content">
-			<p>{{ $t('_mfm.mentionDescription') }}</p>
+			<p>{{ $ts._mfm.mentionDescription }}</p>
 			<div class="preview _panel">
 				<Mfm :text="preview_mention"/>
 				<MkTextarea v-model:value="preview_mention"><span>MFM</span></MkTextarea>
@@ -16,9 +16,9 @@
 		</div>
 	</div>
 	<div class="_section">
-		<div class="_title">{{ $t('_mfm.hashtag') }}</div>
+		<div class="_title">{{ $ts._mfm.hashtag }}</div>
 		<div class="_content">
-			<p>{{ $t('_mfm.hashtagDescription') }}</p>
+			<p>{{ $ts._mfm.hashtagDescription }}</p>
 			<div class="preview _panel">
 				<Mfm :text="preview_hashtag"/>
 				<MkTextarea v-model:value="preview_hashtag"><span>MFM</span></MkTextarea>
@@ -26,9 +26,9 @@
 		</div>
 	</div>
 	<div class="_section">
-		<div class="_title">{{ $t('_mfm.url') }}</div>
+		<div class="_title">{{ $ts._mfm.url }}</div>
 		<div class="_content">
-			<p>{{ $t('_mfm.urlDescription') }}</p>
+			<p>{{ $ts._mfm.urlDescription }}</p>
 			<div class="preview _panel">
 				<Mfm :text="preview_url"/>
 				<MkTextarea v-model:value="preview_url"><span>MFM</span></MkTextarea>
@@ -36,9 +36,9 @@
 		</div>
 	</div>
 	<div class="_section">
-		<div class="_title">{{ $t('_mfm.link') }}</div>
+		<div class="_title">{{ $ts._mfm.link }}</div>
 		<div class="_content">
-			<p>{{ $t('_mfm.linkDescription') }}</p>
+			<p>{{ $ts._mfm.linkDescription }}</p>
 			<div class="preview _panel">
 				<Mfm :text="preview_link"/>
 				<MkTextarea v-model:value="preview_link"><span>MFM</span></MkTextarea>
@@ -46,9 +46,9 @@
 		</div>
 	</div>
 	<div class="_section">
-		<div class="_title">{{ $t('_mfm.emoji') }}</div>
+		<div class="_title">{{ $ts._mfm.emoji }}</div>
 		<div class="_content">
-			<p>{{ $t('_mfm.emojiDescription') }}</p>
+			<p>{{ $ts._mfm.emojiDescription }}</p>
 			<div class="preview _panel">
 				<Mfm :text="preview_emoji"/>
 				<MkTextarea v-model:value="preview_emoji"><span>MFM</span></MkTextarea>
@@ -56,9 +56,9 @@
 		</div>
 	</div>
 	<div class="_section">
-		<div class="_title">{{ $t('_mfm.bold') }}</div>
+		<div class="_title">{{ $ts._mfm.bold }}</div>
 		<div class="_content">
-			<p>{{ $t('_mfm.boldDescription') }}</p>
+			<p>{{ $ts._mfm.boldDescription }}</p>
 			<div class="preview _panel">
 				<Mfm :text="preview_bold"/>
 				<MkTextarea v-model:value="preview_bold"><span>MFM</span></MkTextarea>
@@ -66,9 +66,9 @@
 		</div>
 	</div>
 	<div class="_section">
-		<div class="_title">{{ $t('_mfm.small') }}</div>
+		<div class="_title">{{ $ts._mfm.small }}</div>
 		<div class="_content">
-			<p>{{ $t('_mfm.smallDescription') }}</p>
+			<p>{{ $ts._mfm.smallDescription }}</p>
 			<div class="preview _panel">
 				<Mfm :text="preview_small"/>
 				<MkTextarea v-model:value="preview_small"><span>MFM</span></MkTextarea>
@@ -76,9 +76,9 @@
 		</div>
 	</div>
 	<div class="_section">
-		<div class="_title">{{ $t('_mfm.quote') }}</div>
+		<div class="_title">{{ $ts._mfm.quote }}</div>
 		<div class="_content">
-			<p>{{ $t('_mfm.quoteDescription') }}</p>
+			<p>{{ $ts._mfm.quoteDescription }}</p>
 			<div class="preview _panel">
 				<Mfm :text="preview_quote"/>
 				<MkTextarea v-model:value="preview_quote"><span>MFM</span></MkTextarea>
@@ -86,9 +86,9 @@
 		</div>
 	</div>
 	<div class="_section">
-		<div class="_title">{{ $t('_mfm.center') }}</div>
+		<div class="_title">{{ $ts._mfm.center }}</div>
 		<div class="_content">
-			<p>{{ $t('_mfm.centerDescription') }}</p>
+			<p>{{ $ts._mfm.centerDescription }}</p>
 			<div class="preview _panel">
 				<Mfm :text="preview_center"/>
 				<MkTextarea v-model:value="preview_center"><span>MFM</span></MkTextarea>
@@ -96,9 +96,9 @@
 		</div>
 	</div>
 	<div class="_section">
-		<div class="_title">{{ $t('_mfm.inlineCode') }}</div>
+		<div class="_title">{{ $ts._mfm.inlineCode }}</div>
 		<div class="_content">
-			<p>{{ $t('_mfm.inlineCodeDescription') }}</p>
+			<p>{{ $ts._mfm.inlineCodeDescription }}</p>
 			<div class="preview _panel">
 				<Mfm :text="preview_inlineCode"/>
 				<MkTextarea v-model:value="preview_inlineCode"><span>MFM</span></MkTextarea>
@@ -106,9 +106,9 @@
 		</div>
 	</div>
 	<div class="_section">
-		<div class="_title">{{ $t('_mfm.blockCode') }}</div>
+		<div class="_title">{{ $ts._mfm.blockCode }}</div>
 		<div class="_content">
-			<p>{{ $t('_mfm.blockCodeDescription') }}</p>
+			<p>{{ $ts._mfm.blockCodeDescription }}</p>
 			<div class="preview _panel">
 				<Mfm :text="preview_blockCode"/>
 				<MkTextarea v-model:value="preview_blockCode"><span>MFM</span></MkTextarea>
@@ -116,9 +116,9 @@
 		</div>
 	</div>
 	<div class="_section">
-		<div class="_title">{{ $t('_mfm.inlineMath') }}</div>
+		<div class="_title">{{ $ts._mfm.inlineMath }}</div>
 		<div class="_content">
-			<p>{{ $t('_mfm.inlineMathDescription') }}</p>
+			<p>{{ $ts._mfm.inlineMathDescription }}</p>
 			<div class="preview _panel">
 				<Mfm :text="preview_inlineMath"/>
 				<MkTextarea v-model:value="preview_inlineMath"><span>MFM</span></MkTextarea>
@@ -126,9 +126,9 @@
 		</div>
 	</div>
 	<div class="_section">
-		<div class="_title">{{ $t('_mfm.search') }}</div>
+		<div class="_title">{{ $ts._mfm.search }}</div>
 		<div class="_content">
-			<p>{{ $t('_mfm.searchDescription') }}</p>
+			<p>{{ $ts._mfm.searchDescription }}</p>
 			<div class="preview _panel">
 				<Mfm :text="preview_search"/>
 				<MkTextarea v-model:value="preview_search"><span>MFM</span></MkTextarea>
@@ -136,9 +136,9 @@
 		</div>
 	</div>
 	<div class="_section">
-		<div class="_title">{{ $t('_mfm.flip') }}</div>
+		<div class="_title">{{ $ts._mfm.flip }}</div>
 		<div class="_content">
-			<p>{{ $t('_mfm.flipDescription') }}</p>
+			<p>{{ $ts._mfm.flipDescription }}</p>
 			<div class="preview _panel">
 				<Mfm :text="preview_flip"/>
 				<MkTextarea v-model:value="preview_flip"><span>MFM</span></MkTextarea>
@@ -146,9 +146,9 @@
 		</div>
 	</div>
 	<div class="_section">
-		<div class="_title">{{ $t('_mfm.jelly') }}</div>
+		<div class="_title">{{ $ts._mfm.jelly }}</div>
 		<div class="_content">
-			<p>{{ $t('_mfm.jellyDescription') }}</p>
+			<p>{{ $ts._mfm.jellyDescription }}</p>
 			<div class="preview _panel">
 				<Mfm :text="preview_jelly"/>
 				<MkTextarea v-model:value="preview_jelly"><span>MFM</span></MkTextarea>
@@ -156,9 +156,9 @@
 		</div>
 	</div>
 	<div class="_section">
-		<div class="_title">{{ $t('_mfm.tada') }}</div>
+		<div class="_title">{{ $ts._mfm.tada }}</div>
 		<div class="_content">
-			<p>{{ $t('_mfm.tadaDescription') }}</p>
+			<p>{{ $ts._mfm.tadaDescription }}</p>
 			<div class="preview _panel">
 				<Mfm :text="preview_tada"/>
 				<MkTextarea v-model:value="preview_tada"><span>MFM</span></MkTextarea>
@@ -166,9 +166,9 @@
 		</div>
 	</div>
 	<div class="_section">
-		<div class="_title">{{ $t('_mfm.jump') }}</div>
+		<div class="_title">{{ $ts._mfm.jump }}</div>
 		<div class="_content">
-			<p>{{ $t('_mfm.jumpDescription') }}</p>
+			<p>{{ $ts._mfm.jumpDescription }}</p>
 			<div class="preview _panel">
 				<Mfm :text="preview_jump"/>
 				<MkTextarea v-model:value="preview_jump"><span>MFM</span></MkTextarea>
@@ -176,9 +176,9 @@
 		</div>
 	</div>
 	<div class="_section">
-		<div class="_title">{{ $t('_mfm.bounce') }}</div>
+		<div class="_title">{{ $ts._mfm.bounce }}</div>
 		<div class="_content">
-			<p>{{ $t('_mfm.bounceDescription') }}</p>
+			<p>{{ $ts._mfm.bounceDescription }}</p>
 			<div class="preview _panel">
 				<Mfm :text="preview_bounce"/>
 				<MkTextarea v-model:value="preview_bounce"><span>MFM</span></MkTextarea>
@@ -186,9 +186,9 @@
 		</div>
 	</div>
 	<div class="_section">
-		<div class="_title">{{ $t('_mfm.spin') }}</div>
+		<div class="_title">{{ $ts._mfm.spin }}</div>
 		<div class="_content">
-			<p>{{ $t('_mfm.spinDescription') }}</p>
+			<p>{{ $ts._mfm.spinDescription }}</p>
 			<div class="preview _panel">
 				<Mfm :text="preview_spin"/>
 				<MkTextarea v-model:value="preview_spin"><span>MFM</span></MkTextarea>
@@ -196,9 +196,9 @@
 		</div>
 	</div>
 	<div class="_section">
-		<div class="_title">{{ $t('_mfm.shake') }}</div>
+		<div class="_title">{{ $ts._mfm.shake }}</div>
 		<div class="_content">
-			<p>{{ $t('_mfm.shakeDescription') }}</p>
+			<p>{{ $ts._mfm.shakeDescription }}</p>
 			<div class="preview _panel">
 				<Mfm :text="preview_shake"/>
 				<MkTextarea v-model:value="preview_shake"><span>MFM</span></MkTextarea>
@@ -206,9 +206,9 @@
 		</div>
 	</div>
 	<div class="_section">
-		<div class="_title">{{ $t('_mfm.twitch') }}</div>
+		<div class="_title">{{ $ts._mfm.twitch }}</div>
 		<div class="_content">
-			<p>{{ $t('_mfm.twitchDescription') }}</p>
+			<p>{{ $ts._mfm.twitchDescription }}</p>
 			<div class="preview _panel">
 				<Mfm :text="preview_twitch"/>
 				<MkTextarea v-model:value="preview_twitch"><span>MFM</span></MkTextarea>
@@ -231,22 +231,22 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: {
-				title: this.$t('_mfm.cheatSheet'),
+				title: this.$ts._mfm.cheatSheet,
 				icon: faQuestionCircle,
 			},
 			preview_mention: '@example',
 			preview_hashtag: '#test',
 			preview_url: `https://example.com`,
-			preview_link: `[${this.$t('_mfm.dummy')}](https://example.com)`,
+			preview_link: `[${this.$ts._mfm.dummy}](https://example.com)`,
 			preview_emoji: `:${this.$instance.emojis[0].name}:`,
-			preview_bold: `**${this.$t('_mfm.dummy')}**`,
-			preview_small: `<small>${this.$t('_mfm.dummy')}</small>`,
-			preview_center: `<center>${this.$t('_mfm.dummy')}</center>`,
+			preview_bold: `**${this.$ts._mfm.dummy}**`,
+			preview_small: `<small>${this.$ts._mfm.dummy}</small>`,
+			preview_center: `<center>${this.$ts._mfm.dummy}</center>`,
 			preview_inlineCode: '`<: "Hello, world!"`',
 			preview_blockCode: '```\n~ (#i, 100) {\n\t<: ? ((i % 15) = 0) "FizzBuzz"\n\t\t.? ((i % 3) = 0) "Fizz"\n\t\t.? ((i % 5) = 0) "Buzz"\n\t\t. i\n}\n```',
 			preview_inlineMath: '\\(x= \\frac{-b\' \\pm \\sqrt{(b\')^2-ac}}{a}\\)',
-			preview_quote: `> ${this.$t('_mfm.dummy')}`,
-			preview_search: `${this.$t('_mfm.dummy')} 検索`,
+			preview_quote: `> ${this.$ts._mfm.dummy}`,
+			preview_search: `${this.$ts._mfm.dummy} 検索`,
 			preview_jelly: `[jelly 🍮]`,
 			preview_tada: `[tada 🍮]`,
 			preview_jump: `[jump 🍮]`,
@@ -254,7 +254,7 @@ export default defineComponent({
 			preview_shake: `[shake 🍮]`,
 			preview_twitch: `[twitch 🍮]`,
 			preview_spin: `[spin 🍮] [spin.left 🍮] [spin.alternate 🍮]\n[spin.x 🍮] [spin.x,left 🍮] [spin.x,alternate 🍮]\n[spin.y 🍮] [spin.y,left 🍮] [spin.y,alternate 🍮]`,
-			preview_flip: `[flip ${this.$t('_mfm.dummy')}]\n[flip.v ${this.$t('_mfm.dummy')}]\n[flip.h,v ${this.$t('_mfm.dummy')}]`,
+			preview_flip: `[flip ${this.$ts._mfm.dummy}]\n[flip.v ${this.$ts._mfm.dummy}]\n[flip.h,v ${this.$ts._mfm.dummy}]`,
 		}
 	},
 });
diff --git a/src/client/pages/miauth.vue b/src/client/pages/miauth.vue
index 6cf5f39c5..6430588c4 100644
--- a/src/client/pages/miauth.vue
+++ b/src/client/pages/miauth.vue
@@ -7,27 +7,27 @@
 	</div>
 	<div class="denied _section" v-if="state == 'denied'">
 		<div class="_content">
-			<p>{{ $t('_auth.denied') }}</p>
+			<p>{{ $ts._auth.denied }}</p>
 		</div>
 	</div>
 	<div class="accepted _section" v-else-if="state == 'accepted'">
 		<div class="_content">
-			<p v-if="callback">{{ $t('_auth.callback') }}<MkEllipsis/></p>
-			<p v-else>{{ $t('_auth.pleaseGoBack') }}</p>
+			<p v-if="callback">{{ $ts._auth.callback }}<MkEllipsis/></p>
+			<p v-else>{{ $ts._auth.pleaseGoBack }}</p>
 		</div>
 	</div>
 	<div class="_section" v-else>
 		<div class="_title" v-if="name">{{ $t('_auth.shareAccess', { name: name }) }}</div>
-		<div class="_title" v-else>{{ $t('_auth.shareAccessAsk') }}</div>
+		<div class="_title" v-else>{{ $ts._auth.shareAccessAsk }}</div>
 		<div class="_content">
-			<p>{{ $t('_auth.permissionAsk') }}</p>
+			<p>{{ $ts._auth.permissionAsk }}</p>
 			<ul>
 				<li v-for="p in permission" :key="p">{{ $t(`_permissions.${p}`) }}</li>
 			</ul>
 		</div>
 		<div class="_footer">
-			<MkButton @click="deny" inline>{{ $t('cancel') }}</MkButton>
-			<MkButton @click="accept" inline primary>{{ $t('accept') }}</MkButton>
+			<MkButton @click="deny" inline>{{ $ts.cancel }}</MkButton>
+			<MkButton @click="accept" inline primary>{{ $ts.accept }}</MkButton>
 		</div>
 	</div>
 </div>
diff --git a/src/client/pages/my-antennas/index.antenna.vue b/src/client/pages/my-antennas/index.antenna.vue
index 509600590..b664af91b 100644
--- a/src/client/pages/my-antennas/index.antenna.vue
+++ b/src/client/pages/my-antennas/index.antenna.vue
@@ -3,44 +3,44 @@
 	<div class="_title" v-if="antenna.name">{{ antenna.name }}</div>
 	<div class="_content body">
 		<MkInput v-model:value="name">
-			<span>{{ $t('name') }}</span>
+			<span>{{ $ts.name }}</span>
 		</MkInput>
 		<MkSelect v-model:value="src">
-			<template #label>{{ $t('antennaSource') }}</template>
-			<option value="all">{{ $t('_antennaSources.all') }}</option>
-			<option value="home">{{ $t('_antennaSources.homeTimeline') }}</option>
-			<option value="users">{{ $t('_antennaSources.users') }}</option>
-			<option value="list">{{ $t('_antennaSources.userList') }}</option>
-			<option value="group">{{ $t('_antennaSources.userGroup') }}</option>
+			<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>
 		</MkSelect>
 		<MkSelect v-model:value="userListId" v-if="src === 'list'">
-			<template #label>{{ $t('userList') }}</template>
+			<template #label>{{ $ts.userList }}</template>
 			<option v-for="list in userLists" :value="list.id" :key="list.id">{{ list.name }}</option>
 		</MkSelect>
 		<MkSelect v-model:value="userGroupId" v-else-if="src === 'group'">
-			<template #label>{{ $t('userGroup') }}</template>
+			<template #label>{{ $ts.userGroup }}</template>
 			<option v-for="group in userGroups" :value="group.id" :key="group.id">{{ group.name }}</option>
 		</MkSelect>
 		<MkTextarea v-model:value="users" v-else-if="src === 'users'">
-			<span>{{ $t('users') }}</span>
-			<template #desc>{{ $t('antennaUsersDescription') }} <button class="_textButton" @click="addUser">{{ $t('addUser') }}</button></template>
+			<span>{{ $ts.users }}</span>
+			<template #desc>{{ $ts.antennaUsersDescription }} <button class="_textButton" @click="addUser">{{ $ts.addUser }}</button></template>
 		</MkTextarea>
-		<MkSwitch v-model:value="withReplies">{{ $t('withReplies') }}</MkSwitch>
+		<MkSwitch v-model:value="withReplies">{{ $ts.withReplies }}</MkSwitch>
 		<MkTextarea v-model:value="keywords">
-			<span>{{ $t('antennaKeywords') }}</span>
-			<template #desc>{{ $t('antennaKeywordsDescription') }}</template>
+			<span>{{ $ts.antennaKeywords }}</span>
+			<template #desc>{{ $ts.antennaKeywordsDescription }}</template>
 		</MkTextarea>
 		<MkTextarea v-model:value="excludeKeywords">
-			<span>{{ $t('antennaExcludeKeywords') }}</span>
-			<template #desc>{{ $t('antennaKeywordsDescription') }}</template>
+			<span>{{ $ts.antennaExcludeKeywords }}</span>
+			<template #desc>{{ $ts.antennaKeywordsDescription }}</template>
 		</MkTextarea>
-		<MkSwitch v-model:value="caseSensitive">{{ $t('caseSensitive') }}</MkSwitch>
-		<MkSwitch v-model:value="withFile">{{ $t('withFileAntenna') }}</MkSwitch>
-		<MkSwitch v-model:value="notify">{{ $t('notifyAntenna') }}</MkSwitch>
+		<MkSwitch v-model:value="caseSensitive">{{ $ts.caseSensitive }}</MkSwitch>
+		<MkSwitch v-model:value="withFile">{{ $ts.withFileAntenna }}</MkSwitch>
+		<MkSwitch v-model:value="notify">{{ $ts.notifyAntenna }}</MkSwitch>
 	</div>
 	<div class="_footer">
-		<MkButton inline @click="saveAntenna()" primary><Fa :icon="faSave"/> {{ $t('save') }}</MkButton>
-		<MkButton inline @click="deleteAntenna()" v-if="antenna.id != null"><Fa :icon="faTrash"/> {{ $t('delete') }}</MkButton>
+		<MkButton inline @click="saveAntenna()" primary><Fa :icon="faSave"/> {{ $ts.save }}</MkButton>
+		<MkButton inline @click="deleteAntenna()" v-if="antenna.id != null"><Fa :icon="faTrash"/> {{ $ts.delete }}</MkButton>
 	</div>
 </div>
 </template>
diff --git a/src/client/pages/my-antennas/index.vue b/src/client/pages/my-antennas/index.vue
index 20b1024c9..ebab025b4 100644
--- a/src/client/pages/my-antennas/index.vue
+++ b/src/client/pages/my-antennas/index.vue
@@ -1,6 +1,6 @@
 <template>
 <div class="ieepwinx _section">
-	<MkButton @click="create" primary class="add"><Fa :icon="faPlus"/> {{ $t('add') }}</MkButton>
+	<MkButton @click="create" primary class="add"><Fa :icon="faPlus"/> {{ $ts.add }}</MkButton>
 
 	<div class="_content">
 		<XAntenna v-if="draft" :antenna="draft" @created="onAntennaCreated" style="margin-bottom: var(--margin);"/>
@@ -29,7 +29,7 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: {
-				title: this.$t('manageAntennas'),
+				title: this.$ts.manageAntennas,
 				icon: faSatellite,
 				action: {
 					icon: faPlus,
diff --git a/src/client/pages/my-clips/index.vue b/src/client/pages/my-clips/index.vue
index ff7ef6282..b1fdb8e62 100644
--- a/src/client/pages/my-clips/index.vue
+++ b/src/client/pages/my-clips/index.vue
@@ -1,6 +1,6 @@
 <template>
 <div class="_section qtcaoidl">
-	<MkButton @click="create" primary class="add"><Fa :icon="faPlus"/> {{ $t('add') }}</MkButton>
+	<MkButton @click="create" primary class="add"><Fa :icon="faPlus"/> {{ $ts.add }}</MkButton>
 
 	<div class="_content">
 		<MkPagination :pagination="pagination" #default="{items}" ref="list" class="list">
@@ -29,7 +29,7 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: {
-				title: this.$t('clip'),
+				title: this.$ts.clip,
 				icon: faPaperclip,
 				action: {
 					icon: faPlus,
@@ -47,20 +47,20 @@ export default defineComponent({
 
 	methods: {
 		async create() {
-			const { canceled, result } = await os.form(this.$t('createNewClip'), {
+			const { canceled, result } = await os.form(this.$ts.createNewClip, {
 				name: {
 					type: 'string',
-					label: this.$t('name')
+					label: this.$ts.name
 				},
 				description: {
 					type: 'string',
 					required: false,
 					multiline: true,
-					label: this.$t('description')
+					label: this.$ts.description
 				},
 				isPublic: {
 					type: 'boolean',
-					label: this.$t('public'),
+					label: this.$ts.public,
 					default: false
 				}
 			});
diff --git a/src/client/pages/my-groups/group.vue b/src/client/pages/my-groups/group.vue
index 36fed3f9d..d0d83c9c0 100644
--- a/src/client/pages/my-groups/group.vue
+++ b/src/client/pages/my-groups/group.vue
@@ -3,17 +3,17 @@
 	<transition name="zoom" mode="out-in">
 		<div v-if="group" class="_section">
 			<div class="_content">
-				<MkButton inline @click="invite()">{{ $t('invite') }}</MkButton>
-				<MkButton inline @click="renameGroup()">{{ $t('rename') }}</MkButton>
-				<MkButton inline @click="transfer()">{{ $t('transfer') }}</MkButton>
-				<MkButton inline @click="deleteGroup()">{{ $t('delete') }}</MkButton>
+				<MkButton inline @click="invite()">{{ $ts.invite }}</MkButton>
+				<MkButton inline @click="renameGroup()">{{ $ts.rename }}</MkButton>
+				<MkButton inline @click="transfer()">{{ $ts.transfer }}</MkButton>
+				<MkButton inline @click="deleteGroup()">{{ $ts.delete }}</MkButton>
 			</div>
 		</div>
 	</transition>
 
 	<transition name="zoom" mode="out-in">
 		<div v-if="group" class="_section members _vMargin">
-			<div class="_title">{{ $t('members') }}</div>
+			<div class="_title">{{ $ts.members }}</div>
 			<div class="_content">
 				<div class="users">
 					<div class="user _panel" v-for="user in users" :key="user.id">
@@ -101,7 +101,7 @@ export default defineComponent({
 
 		async renameGroup() {
 			const { canceled, result: name } = await os.dialog({
-				title: this.$t('groupName'),
+				title: this.$ts.groupName,
 				input: {
 					default: this.group.name
 				}
diff --git a/src/client/pages/my-groups/index.vue b/src/client/pages/my-groups/index.vue
index fb3d9ccb3..99f7b1e68 100644
--- a/src/client/pages/my-groups/index.vue
+++ b/src/client/pages/my-groups/index.vue
@@ -2,15 +2,15 @@
 <div class="">
 	<div class="_section" style="padding: 0;">
 		<MkTab v-model:value="tab">
-			<option value="owned">{{ $t('ownedGroups') }}</option>
-			<option value="joined">{{ $t('joinedGroups') }}</option>
-			<option value="invites"><Fa :icon="faEnvelopeOpenText"/> {{ $t('invites') }}</option>
+			<option value="owned">{{ $ts.ownedGroups }}</option>
+			<option value="joined">{{ $ts.joinedGroups }}</option>
+			<option value="invites"><Fa :icon="faEnvelopeOpenText"/> {{ $ts.invites }}</option>
 		</MkTab>
 	</div>
 
 	<div class="_section">
 		<div class="_content" v-if="tab === 'owned'">
-			<MkButton @click="create" primary style="margin: 0 auto var(--margin) auto;"><Fa :icon="faPlus"/> {{ $t('createGroup') }}</MkButton>
+			<MkButton @click="create" primary style="margin: 0 auto var(--margin) auto;"><Fa :icon="faPlus"/> {{ $ts.createGroup }}</MkButton>
 
 			<MkPagination :pagination="ownedPagination" #default="{items}" ref="owned">
 				<div class="_card" v-for="group in items" :key="group.id">
@@ -35,8 +35,8 @@
 					<div class="_title">{{ invitation.group.name }}</div>
 					<div class="_content"><MkAvatars :user-ids="invitation.group.userIds"/></div>
 					<div class="_footer">
-						<MkButton @click="acceptInvite(invitation)" primary inline><Fa :icon="faCheck"/> {{ $t('accept') }}</MkButton>
-						<MkButton @click="rejectInvite(invitation)" primary inline><Fa :icon="faBan"/> {{ $t('reject') }}</MkButton>
+						<MkButton @click="acceptInvite(invitation)" primary inline><Fa :icon="faCheck"/> {{ $ts.accept }}</MkButton>
+						<MkButton @click="rejectInvite(invitation)" primary inline><Fa :icon="faBan"/> {{ $ts.reject }}</MkButton>
 					</div>
 				</div>
 			</MkPagination>
@@ -67,7 +67,7 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: {
-				title: this.$t('groups'),
+				title: this.$ts.groups,
 				icon: faUsers
 			},
 			tab: 'owned',
@@ -90,7 +90,7 @@ export default defineComponent({
 	methods: {
 		async create() {
 			const { canceled, result: name } = await os.dialog({
-				title: this.$t('groupName'),
+				title: this.$ts.groupName,
 				input: true
 			});
 			if (canceled) return;
diff --git a/src/client/pages/my-lists/index.vue b/src/client/pages/my-lists/index.vue
index edd4ff944..bb23be664 100644
--- a/src/client/pages/my-lists/index.vue
+++ b/src/client/pages/my-lists/index.vue
@@ -1,6 +1,6 @@
 <template>
 <div class="qkcjvfiv _section">
-	<MkButton @click="create" primary class="add"><Fa :icon="faPlus"/> {{ $t('createList') }}</MkButton>
+	<MkButton @click="create" primary class="add"><Fa :icon="faPlus"/> {{ $ts.createList }}</MkButton>
 
 	<MkPagination :pagination="pagination" #default="{items}" class="lists _content" ref="list">
 		<div class="list _panel" v-for="(list, i) in items" :key="list.id">
@@ -26,7 +26,7 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: {
-				title: this.$t('manageLists'),
+				title: this.$ts.manageLists,
 				icon: faListUl,
 				action: {
 					icon: faPlus,
@@ -44,7 +44,7 @@ export default defineComponent({
 	methods: {
 		async create() {
 			const { canceled, result: name } = await os.dialog({
-				title: this.$t('enterListName'),
+				title: this.$ts.enterListName,
 				input: true
 			});
 			if (canceled) return;
diff --git a/src/client/pages/my-lists/list.vue b/src/client/pages/my-lists/list.vue
index 4f352e05c..70c01dced 100644
--- a/src/client/pages/my-lists/list.vue
+++ b/src/client/pages/my-lists/list.vue
@@ -3,16 +3,16 @@
 	<transition name="zoom" mode="out-in">
 		<div v-if="list" class="_section">
 			<div class="_content">
-				<MkButton inline @click="addUser()">{{ $t('addUser') }}</MkButton>
-				<MkButton inline @click="renameList()">{{ $t('rename') }}</MkButton>
-				<MkButton inline @click="deleteList()">{{ $t('delete') }}</MkButton>
+				<MkButton inline @click="addUser()">{{ $ts.addUser }}</MkButton>
+				<MkButton inline @click="renameList()">{{ $ts.rename }}</MkButton>
+				<MkButton inline @click="deleteList()">{{ $ts.delete }}</MkButton>
 			</div>
 		</div>
 	</transition>
 
 	<transition name="zoom" mode="out-in">
 		<div v-if="list" class="_section members _vMargin">
-			<div class="_title">{{ $t('members') }}</div>
+			<div class="_title">{{ $ts.members }}</div>
 			<div class="_content">
 				<div class="users">
 					<div class="user _panel" v-for="user in users" :key="user.id">
@@ -102,7 +102,7 @@ export default defineComponent({
 
 		async renameList() {
 			const { canceled, result: name } = await os.dialog({
-				title: this.$t('enterListName'),
+				title: this.$ts.enterListName,
 				input: {
 					default: this.list.name
 				}
diff --git a/src/client/pages/not-found.vue b/src/client/pages/not-found.vue
index 0349b3c1b..ec707c2be 100644
--- a/src/client/pages/not-found.vue
+++ b/src/client/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>{{ $t('notFoundDescription') }}</div>
+		<div>{{ $ts.notFoundDescription }}</div>
 	</div>
 </div>
 </template>
@@ -16,7 +16,7 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: {
-				title: this.$t('notFound'),
+				title: this.$ts.notFound,
 				icon: faExclamationTriangle
 			},
 		}
diff --git a/src/client/pages/note.vue b/src/client/pages/note.vue
index 6ad6f2ba1..e2cd60a37 100644
--- a/src/client/pages/note.vue
+++ b/src/client/pages/note.vue
@@ -12,7 +12,7 @@
 				<XNote v-model:note="note" :key="note.id" :detail="true" class="_vMargin"/>
 			</div>
 			<div class="_content clips _vMargin" v-if="clips && clips.length > 0">
-				<div class="title">{{ $t('clip') }}</div>
+				<div class="title">{{ $ts.clip }}</div>
 				<MkA v-for="item in clips" :key="item.id" :to="`/clips/${item.id}`" class="item _panel _vMargin">
 					<b>{{ item.name }}</b>
 					<div v-if="item.description" class="description">{{ item.description }}</div>
@@ -60,7 +60,7 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: computed(() => this.note ? {
-				title: this.$t('note'),
+				title: this.$ts.note,
 				avatar: this.note.user,
 			} : null),
 			note: null,
diff --git a/src/client/pages/notifications.vue b/src/client/pages/notifications.vue
index 4dfcb3fa3..4b4f22e64 100644
--- a/src/client/pages/notifications.vue
+++ b/src/client/pages/notifications.vue
@@ -21,7 +21,7 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: {
-				title: this.$t('notifications'),
+				title: this.$ts.notifications,
 				icon: faBell
 			},
 		};
diff --git a/src/client/pages/page-editor/els/page-editor.el.button.vue b/src/client/pages/page-editor/els/page-editor.el.button.vue
index 7c6590457..846e3f644 100644
--- a/src/client/pages/page-editor/els/page-editor.el.button.vue
+++ b/src/client/pages/page-editor/els/page-editor.el.button.vue
@@ -1,37 +1,37 @@
 <template>
 <XContainer @remove="() => $emit('remove')" :draggable="true">
-	<template #header><Fa :icon="faBolt"/> {{ $t('_pages.blocks.button') }}</template>
+	<template #header><Fa :icon="faBolt"/> {{ $ts._pages.blocks.button }}</template>
 
 	<section class="xfhsjczc">
-		<MkInput v-model:value="value.text"><span>{{ $t('_pages.blocks._button.text') }}</span></MkInput>
-		<MkSwitch v-model:value="value.primary"><span>{{ $t('_pages.blocks._button.colored') }}</span></MkSwitch>
+		<MkInput v-model:value="value.text"><span>{{ $ts._pages.blocks._button.text }}</span></MkInput>
+		<MkSwitch v-model:value="value.primary"><span>{{ $ts._pages.blocks._button.colored }}</span></MkSwitch>
 		<MkSelect v-model:value="value.action">
-			<template #label>{{ $t('_pages.blocks._button.action') }}</template>
-			<option value="dialog">{{ $t('_pages.blocks._button._action.dialog') }}</option>
-			<option value="resetRandom">{{ $t('_pages.blocks._button._action.resetRandom') }}</option>
-			<option value="pushEvent">{{ $t('_pages.blocks._button._action.pushEvent') }}</option>
-			<option value="callAiScript">{{ $t('_pages.blocks._button._action.callAiScript') }}</option>
+			<template #label>{{ $ts._pages.blocks._button.action }}</template>
+			<option value="dialog">{{ $ts._pages.blocks._button._action.dialog }}</option>
+			<option value="resetRandom">{{ $ts._pages.blocks._button._action.resetRandom }}</option>
+			<option value="pushEvent">{{ $ts._pages.blocks._button._action.pushEvent }}</option>
+			<option value="callAiScript">{{ $ts._pages.blocks._button._action.callAiScript }}</option>
 		</MkSelect>
 		<template v-if="value.action === 'dialog'">
-			<MkInput v-model:value="value.content"><span>{{ $t('_pages.blocks._button._action._dialog.content') }}</span></MkInput>
+			<MkInput v-model:value="value.content"><span>{{ $ts._pages.blocks._button._action._dialog.content }}</span></MkInput>
 		</template>
 		<template v-else-if="value.action === 'pushEvent'">
-			<MkInput v-model:value="value.event"><span>{{ $t('_pages.blocks._button._action._pushEvent.event') }}</span></MkInput>
-			<MkInput v-model:value="value.message"><span>{{ $t('_pages.blocks._button._action._pushEvent.message') }}</span></MkInput>
+			<MkInput v-model:value="value.event"><span>{{ $ts._pages.blocks._button._action._pushEvent.event }}</span></MkInput>
+			<MkInput v-model:value="value.message"><span>{{ $ts._pages.blocks._button._action._pushEvent.message }}</span></MkInput>
 			<MkSelect v-model:value="value.var">
-				<template #label>{{ $t('_pages.blocks._button._action._pushEvent.variable') }}</template>
+				<template #label>{{ $ts._pages.blocks._button._action._pushEvent.variable }}</template>
 				<option :value="null">{{ $t('_pages.blocks._button._action._pushEvent.no-variable') }}</option>
 				<option v-for="v in hpml.getVarsByType()" :value="v.name">{{ v.name }}</option>
-				<optgroup :label="$t('_pages.script.pageVariables')">
+				<optgroup :label="$ts._pages.script.pageVariables">
 					<option v-for="v in hpml.getPageVarsByType()" :value="v">{{ v }}</option>
 				</optgroup>
-				<optgroup :label="$t('_pages.script.enviromentVariables')">
+				<optgroup :label="$ts._pages.script.enviromentVariables">
 					<option v-for="v in hpml.getEnvVarsByType()" :value="v">{{ v }}</option>
 				</optgroup>
 			</MkSelect>
 		</template>
 		<template v-else-if="value.action === 'callAiScript'">
-			<MkInput v-model:value="value.fn"><span>{{ $t('_pages.blocks._button._action._callAiScript.functionName') }}</span></MkInput>
+			<MkInput v-model:value="value.fn"><span>{{ $ts._pages.blocks._button._action._callAiScript.functionName }}</span></MkInput>
 		</template>
 	</section>
 </XContainer>
diff --git a/src/client/pages/page-editor/els/page-editor.el.canvas.vue b/src/client/pages/page-editor/els/page-editor.el.canvas.vue
index ff7e16064..83b7f376d 100644
--- a/src/client/pages/page-editor/els/page-editor.el.canvas.vue
+++ b/src/client/pages/page-editor/els/page-editor.el.canvas.vue
@@ -1,11 +1,11 @@
 <template>
 <XContainer @remove="() => $emit('remove')" :draggable="true">
-	<template #header><Fa :icon="faPaintBrush"/> {{ $t('_pages.blocks.canvas') }}</template>
+	<template #header><Fa :icon="faPaintBrush"/> {{ $ts._pages.blocks.canvas }}</template>
 
 	<section style="padding: 0 16px 0 16px;">
-		<MkInput v-model:value="value.name"><template #prefix><Fa :icon="faMagic"/></template><span>{{ $t('_pages.blocks._canvas.id') }}</span></MkInput>
-		<MkInput v-model:value="value.width" type="number"><span>{{ $t('_pages.blocks._canvas.width') }}</span><template #suffix>px</template></MkInput>
-		<MkInput v-model:value="value.height" type="number"><span>{{ $t('_pages.blocks._canvas.height') }}</span><template #suffix>px</template></MkInput>
+		<MkInput v-model:value="value.name"><template #prefix><Fa :icon="faMagic"/></template><span>{{ $ts._pages.blocks._canvas.id }}</span></MkInput>
+		<MkInput v-model:value="value.width" type="number"><span>{{ $ts._pages.blocks._canvas.width }}</span><template #suffix>px</template></MkInput>
+		<MkInput v-model:value="value.height" type="number"><span>{{ $ts._pages.blocks._canvas.height }}</span><template #suffix>px</template></MkInput>
 	</section>
 </XContainer>
 </template>
diff --git a/src/client/pages/page-editor/els/page-editor.el.counter.vue b/src/client/pages/page-editor/els/page-editor.el.counter.vue
index ae62c2fa8..81f046234 100644
--- a/src/client/pages/page-editor/els/page-editor.el.counter.vue
+++ b/src/client/pages/page-editor/els/page-editor.el.counter.vue
@@ -1,11 +1,11 @@
 <template>
 <XContainer @remove="() => $emit('remove')" :draggable="true">
-	<template #header><Fa :icon="faBolt"/> {{ $t('_pages.blocks.counter') }}</template>
+	<template #header><Fa :icon="faBolt"/> {{ $ts._pages.blocks.counter }}</template>
 
 	<section style="padding: 0 16px 0 16px;">
-		<MkInput v-model:value="value.name"><template #prefix><Fa :icon="faMagic"/></template><span>{{ $t('_pages.blocks._counter.name') }}</span></MkInput>
-		<MkInput v-model:value="value.text"><span>{{ $t('_pages.blocks._counter.text') }}</span></MkInput>
-		<MkInput v-model:value="value.inc" type="number"><span>{{ $t('_pages.blocks._counter.inc') }}</span></MkInput>
+		<MkInput v-model:value="value.name"><template #prefix><Fa :icon="faMagic"/></template><span>{{ $ts._pages.blocks._counter.name }}</span></MkInput>
+		<MkInput v-model:value="value.text"><span>{{ $ts._pages.blocks._counter.text }}</span></MkInput>
+		<MkInput v-model:value="value.inc" type="number"><span>{{ $ts._pages.blocks._counter.inc }}</span></MkInput>
 	</section>
 </XContainer>
 </template>
diff --git a/src/client/pages/page-editor/els/page-editor.el.if.vue b/src/client/pages/page-editor/els/page-editor.el.if.vue
index 2f1213853..8cc2f401c 100644
--- a/src/client/pages/page-editor/els/page-editor.el.if.vue
+++ b/src/client/pages/page-editor/els/page-editor.el.if.vue
@@ -1,6 +1,6 @@
 <template>
 <XContainer @remove="() => $emit('remove')" :draggable="true">
-	<template #header><Fa :icon="faQuestion"/> {{ $t('_pages.blocks.if') }}</template>
+	<template #header><Fa :icon="faQuestion"/> {{ $ts._pages.blocks.if }}</template>
 	<template #func>
 		<button @click="add()" class="_button">
 			<Fa :icon="faPlus"/>
@@ -9,12 +9,12 @@
 
 	<section class="romcojzs">
 		<MkSelect v-model:value="value.var">
-			<template #label>{{ $t('_pages.blocks._if.variable') }}</template>
+			<template #label>{{ $ts._pages.blocks._if.variable }}</template>
 			<option v-for="v in hpml.getVarsByType('boolean')" :value="v.name">{{ v.name }}</option>
-			<optgroup :label="$t('_pages.script.pageVariables')">
+			<optgroup :label="$ts._pages.script.pageVariables">
 				<option v-for="v in hpml.getPageVarsByType('boolean')" :value="v">{{ v }}</option>
 			</optgroup>
-			<optgroup :label="$t('_pages.script.enviromentVariables')">
+			<optgroup :label="$ts._pages.script.enviromentVariables">
 				<option v-for="v in hpml.getEnvVarsByType('boolean')" :value="v">{{ v }}</option>
 			</optgroup>
 		</MkSelect>
@@ -64,7 +64,7 @@ export default defineComponent({
 		async add() {
 			const { canceled, result: type } = await os.dialog({
 				type: null,
-				title: this.$t('_pages.chooseBlock'),
+				title: this.$ts._pages.chooseBlock,
 				select: {
 					groupedItems: this.getPageBlockList()
 				},
diff --git a/src/client/pages/page-editor/els/page-editor.el.image.vue b/src/client/pages/page-editor/els/page-editor.el.image.vue
index f5c2fe816..b711d0076 100644
--- a/src/client/pages/page-editor/els/page-editor.el.image.vue
+++ b/src/client/pages/page-editor/els/page-editor.el.image.vue
@@ -1,6 +1,6 @@
 <template>
 <XContainer @remove="() => $emit('remove')" :draggable="true">
-	<template #header><Fa :icon="faImage"/> {{ $t('_pages.blocks.image') }}</template>
+	<template #header><Fa :icon="faImage"/> {{ $ts._pages.blocks.image }}</template>
 	<template #func>
 		<button @click="choose()">
 			<Fa :icon="faFolderOpen"/>
diff --git a/src/client/pages/page-editor/els/page-editor.el.note.vue b/src/client/pages/page-editor/els/page-editor.el.note.vue
index 239ae3c01..7e6f8ca68 100644
--- a/src/client/pages/page-editor/els/page-editor.el.note.vue
+++ b/src/client/pages/page-editor/els/page-editor.el.note.vue
@@ -1,13 +1,13 @@
 <template>
 <XContainer @remove="() => $emit('remove')" :draggable="true">
-	<template #header><Fa :icon="faStickyNote"/> {{ $t('_pages.blocks.note') }}</template>
+	<template #header><Fa :icon="faStickyNote"/> {{ $ts._pages.blocks.note }}</template>
 
 	<section style="padding: 0 16px 0 16px;">
 		<MkInput v-model:value="id">
-			<span>{{ $t('_pages.blocks._note.id') }}</span>
-			<template #desc>{{ $t('_pages.blocks._note.idDescription') }}</template>
+			<span>{{ $ts._pages.blocks._note.id }}</span>
+			<template #desc>{{ $ts._pages.blocks._note.idDescription }}</template>
 		</MkInput>
-		<MkSwitch v-model:value="value.detailed"><span>{{ $t('_pages.blocks._note.detailed') }}</span></MkSwitch>
+		<MkSwitch v-model:value="value.detailed"><span>{{ $ts._pages.blocks._note.detailed }}</span></MkSwitch>
 
 		<XNote v-if="note" v-model:note="note" :key="note.id + ':' + (value.detailed ? 'detailed' : 'normal')" :detail="value.detailed" style="margin-bottom: 16px;"/>
 	</section>
diff --git a/src/client/pages/page-editor/els/page-editor.el.number-input.vue b/src/client/pages/page-editor/els/page-editor.el.number-input.vue
index 37b9ac90c..37a4d6031 100644
--- a/src/client/pages/page-editor/els/page-editor.el.number-input.vue
+++ b/src/client/pages/page-editor/els/page-editor.el.number-input.vue
@@ -1,11 +1,11 @@
 <template>
 <XContainer @remove="() => $emit('remove')" :draggable="true">
-	<template #header><Fa :icon="faBolt"/> {{ $t('_pages.blocks.numberInput') }}</template>
+	<template #header><Fa :icon="faBolt"/> {{ $ts._pages.blocks.numberInput }}</template>
 
 	<section style="padding: 0 16px 0 16px;">
-		<MkInput v-model:value="value.name"><template #prefix><Fa :icon="faMagic"/></template><span>{{ $t('_pages.blocks._numberInput.name') }}</span></MkInput>
-		<MkInput v-model:value="value.text"><span>{{ $t('_pages.blocks._numberInput.text') }}</span></MkInput>
-		<MkInput v-model:value="value.default" type="number"><span>{{ $t('_pages.blocks._numberInput.default') }}</span></MkInput>
+		<MkInput v-model:value="value.name"><template #prefix><Fa :icon="faMagic"/></template><span>{{ $ts._pages.blocks._numberInput.name }}</span></MkInput>
+		<MkInput v-model:value="value.text"><span>{{ $ts._pages.blocks._numberInput.text }}</span></MkInput>
+		<MkInput v-model:value="value.default" type="number"><span>{{ $ts._pages.blocks._numberInput.default }}</span></MkInput>
 	</section>
 </XContainer>
 </template>
diff --git a/src/client/pages/page-editor/els/page-editor.el.post.vue b/src/client/pages/page-editor/els/page-editor.el.post.vue
index 19c9c9d7d..3866f51c1 100644
--- a/src/client/pages/page-editor/els/page-editor.el.post.vue
+++ b/src/client/pages/page-editor/els/page-editor.el.post.vue
@@ -1,11 +1,11 @@
 <template>
 <XContainer @remove="() => $emit('remove')" :draggable="true">
-	<template #header><Fa :icon="faPaperPlane"/> {{ $t('_pages.blocks.post') }}</template>
+	<template #header><Fa :icon="faPaperPlane"/> {{ $ts._pages.blocks.post }}</template>
 
 	<section style="padding: 16px;">
-		<MkTextarea v-model:value="value.text">{{ $t('_pages.blocks._post.text') }}</MkTextarea>
-		<MkSwitch v-model:value="value.attachCanvasImage"><span>{{ $t('_pages.blocks._post.attachCanvasImage') }}</span></MkSwitch>
-		<MkInput v-if="value.attachCanvasImage" v-model:value="value.canvasId"><span>{{ $t('_pages.blocks._post.canvasId') }}</span></MkInput>
+		<MkTextarea v-model:value="value.text">{{ $ts._pages.blocks._post.text }}</MkTextarea>
+		<MkSwitch v-model:value="value.attachCanvasImage"><span>{{ $ts._pages.blocks._post.attachCanvasImage }}</span></MkSwitch>
+		<MkInput v-if="value.attachCanvasImage" v-model:value="value.canvasId"><span>{{ $ts._pages.blocks._post.canvasId }}</span></MkInput>
 	</section>
 </XContainer>
 </template>
diff --git a/src/client/pages/page-editor/els/page-editor.el.radio-button.vue b/src/client/pages/page-editor/els/page-editor.el.radio-button.vue
index e30a7d363..d1e8545b2 100644
--- a/src/client/pages/page-editor/els/page-editor.el.radio-button.vue
+++ b/src/client/pages/page-editor/els/page-editor.el.radio-button.vue
@@ -1,12 +1,12 @@
 <template>
 <XContainer @remove="() => $emit('remove')" :draggable="true">
-	<template #header><Fa :icon="faBolt"/> {{ $t('_pages.blocks.radioButton') }}</template>
+	<template #header><Fa :icon="faBolt"/> {{ $ts._pages.blocks.radioButton }}</template>
 
 	<section style="padding: 0 16px 16px 16px;">
-		<MkInput v-model:value="value.name"><template #prefix><Fa :icon="faMagic"/></template><span>{{ $t('_pages.blocks._radioButton.name') }}</span></MkInput>
-		<MkInput v-model:value="value.title"><span>{{ $t('_pages.blocks._radioButton.title') }}</span></MkInput>
-		<MkTextarea v-model:value="values"><span>{{ $t('_pages.blocks._radioButton.values') }}</span></MkTextarea>
-		<MkInput v-model:value="value.default"><span>{{ $t('_pages.blocks._radioButton.default') }}</span></MkInput>
+		<MkInput v-model:value="value.name"><template #prefix><Fa :icon="faMagic"/></template><span>{{ $ts._pages.blocks._radioButton.name }}</span></MkInput>
+		<MkInput v-model:value="value.title"><span>{{ $ts._pages.blocks._radioButton.title }}</span></MkInput>
+		<MkTextarea v-model:value="values"><span>{{ $ts._pages.blocks._radioButton.values }}</span></MkTextarea>
+		<MkInput v-model:value="value.default"><span>{{ $ts._pages.blocks._radioButton.default }}</span></MkInput>
 	</section>
 </XContainer>
 </template>
diff --git a/src/client/pages/page-editor/els/page-editor.el.section.vue b/src/client/pages/page-editor/els/page-editor.el.section.vue
index fdad14063..e08540351 100644
--- a/src/client/pages/page-editor/els/page-editor.el.section.vue
+++ b/src/client/pages/page-editor/els/page-editor.el.section.vue
@@ -75,7 +75,7 @@ export default defineComponent({
 		async add() {
 			const { canceled, result: type } = await os.dialog({
 				type: null,
-				title: this.$t('_pages.chooseBlock'),
+				title: this.$ts._pages.chooseBlock,
 				select: {
 					groupedItems: this.getPageBlockList()
 				},
diff --git a/src/client/pages/page-editor/els/page-editor.el.switch.vue b/src/client/pages/page-editor/els/page-editor.el.switch.vue
index 94ebda40b..261423993 100644
--- a/src/client/pages/page-editor/els/page-editor.el.switch.vue
+++ b/src/client/pages/page-editor/els/page-editor.el.switch.vue
@@ -1,11 +1,11 @@
 <template>
 <XContainer @remove="() => $emit('remove')" :draggable="true">
-	<template #header><Fa :icon="faBolt"/> {{ $t('_pages.blocks.switch') }}</template>
+	<template #header><Fa :icon="faBolt"/> {{ $ts._pages.blocks.switch }}</template>
 
 	<section class="kjuadyyj">
-		<MkInput v-model:value="value.name"><template #prefix><Fa :icon="faMagic"/></template><span>{{ $t('_pages.blocks._switch.name') }}</span></MkInput>
-		<MkInput v-model:value="value.text"><span>{{ $t('_pages.blocks._switch.text') }}</span></MkInput>
-		<MkSwitch v-model:value="value.default"><span>{{ $t('_pages.blocks._switch.default') }}</span></MkSwitch>
+		<MkInput v-model:value="value.name"><template #prefix><Fa :icon="faMagic"/></template><span>{{ $ts._pages.blocks._switch.name }}</span></MkInput>
+		<MkInput v-model:value="value.text"><span>{{ $ts._pages.blocks._switch.text }}</span></MkInput>
+		<MkSwitch v-model:value="value.default"><span>{{ $ts._pages.blocks._switch.default }}</span></MkSwitch>
 	</section>
 </XContainer>
 </template>
diff --git a/src/client/pages/page-editor/els/page-editor.el.text-input.vue b/src/client/pages/page-editor/els/page-editor.el.text-input.vue
index 90039a3c9..335d1ab40 100644
--- a/src/client/pages/page-editor/els/page-editor.el.text-input.vue
+++ b/src/client/pages/page-editor/els/page-editor.el.text-input.vue
@@ -1,11 +1,11 @@
 <template>
 <XContainer @remove="() => $emit('remove')" :draggable="true">
-	<template #header><Fa :icon="faBolt"/> {{ $t('_pages.blocks.textInput') }}</template>
+	<template #header><Fa :icon="faBolt"/> {{ $ts._pages.blocks.textInput }}</template>
 
 	<section style="padding: 0 16px 0 16px;">
-		<MkInput v-model:value="value.name"><template #prefix><Fa :icon="faMagic"/></template><span>{{ $t('_pages.blocks._textInput.name') }}</span></MkInput>
-		<MkInput v-model:value="value.text"><span>{{ $t('_pages.blocks._textInput.text') }}</span></MkInput>
-		<MkInput v-model:value="value.default" type="text"><span>{{ $t('_pages.blocks._textInput.default') }}</span></MkInput>
+		<MkInput v-model:value="value.name"><template #prefix><Fa :icon="faMagic"/></template><span>{{ $ts._pages.blocks._textInput.name }}</span></MkInput>
+		<MkInput v-model:value="value.text"><span>{{ $ts._pages.blocks._textInput.text }}</span></MkInput>
+		<MkInput v-model:value="value.default" type="text"><span>{{ $ts._pages.blocks._textInput.default }}</span></MkInput>
 	</section>
 </XContainer>
 </template>
diff --git a/src/client/pages/page-editor/els/page-editor.el.text.vue b/src/client/pages/page-editor/els/page-editor.el.text.vue
index fcce180f3..452eedba6 100644
--- a/src/client/pages/page-editor/els/page-editor.el.text.vue
+++ b/src/client/pages/page-editor/els/page-editor.el.text.vue
@@ -1,6 +1,6 @@
 <template>
 <XContainer @remove="() => $emit('remove')" :draggable="true">
-	<template #header><Fa :icon="faAlignLeft"/> {{ $t('_pages.blocks.text') }}</template>
+	<template #header><Fa :icon="faAlignLeft"/> {{ $ts._pages.blocks.text }}</template>
 
 	<section class="vckmsadr">
 		<textarea v-model="value.text"></textarea>
diff --git a/src/client/pages/page-editor/els/page-editor.el.textarea-input.vue b/src/client/pages/page-editor/els/page-editor.el.textarea-input.vue
index ea00860fe..afedbe6f6 100644
--- a/src/client/pages/page-editor/els/page-editor.el.textarea-input.vue
+++ b/src/client/pages/page-editor/els/page-editor.el.textarea-input.vue
@@ -1,11 +1,11 @@
 <template>
 <XContainer @remove="() => $emit('remove')" :draggable="true">
-	<template #header><Fa :icon="faBolt"/> {{ $t('_pages.blocks.textareaInput') }}</template>
+	<template #header><Fa :icon="faBolt"/> {{ $ts._pages.blocks.textareaInput }}</template>
 
 	<section style="padding: 0 16px 16px 16px;">
-		<MkInput v-model:value="value.name"><template #prefix><Fa :icon="faMagic"/></template><span>{{ $t('_pages.blocks._textareaInput.name') }}</span></MkInput>
-		<MkInput v-model:value="value.text"><span>{{ $t('_pages.blocks._textareaInput.text') }}</span></MkInput>
-		<MkTextarea v-model:value="value.default"><span>{{ $t('_pages.blocks._textareaInput.default') }}</span></MkTextarea>
+		<MkInput v-model:value="value.name"><template #prefix><Fa :icon="faMagic"/></template><span>{{ $ts._pages.blocks._textareaInput.name }}</span></MkInput>
+		<MkInput v-model:value="value.text"><span>{{ $ts._pages.blocks._textareaInput.text }}</span></MkInput>
+		<MkTextarea v-model:value="value.default"><span>{{ $ts._pages.blocks._textareaInput.default }}</span></MkTextarea>
 	</section>
 </XContainer>
 </template>
diff --git a/src/client/pages/page-editor/els/page-editor.el.textarea.vue b/src/client/pages/page-editor/els/page-editor.el.textarea.vue
index 38c901d79..e5f29b37d 100644
--- a/src/client/pages/page-editor/els/page-editor.el.textarea.vue
+++ b/src/client/pages/page-editor/els/page-editor.el.textarea.vue
@@ -1,6 +1,6 @@
 <template>
 <XContainer @remove="() => $emit('remove')" :draggable="true">
-	<template #header><Fa :icon="faAlignLeft"/> {{ $t('_pages.blocks.textarea') }}</template>
+	<template #header><Fa :icon="faAlignLeft"/> {{ $ts._pages.blocks.textarea }}</template>
 
 	<section class="ihymsbbe">
 		<textarea v-model="value.text"></textarea>
diff --git a/src/client/pages/page-editor/page-editor.script-block.vue b/src/client/pages/page-editor/page-editor.script-block.vue
index 34ba113b2..3fbcd1b19 100644
--- a/src/client/pages/page-editor/page-editor.script-block.vue
+++ b/src/client/pages/page-editor/page-editor.script-block.vue
@@ -8,7 +8,7 @@
 	</template>
 
 	<section v-if="value.type === null" class="pbglfege" @click="changeType()">
-		{{ $t('_pages.script.emptySlot') }}
+		{{ $ts._pages.script.emptySlot }}
 	</section>
 	<section v-else-if="value.type === 'text'" class="tbwccoaw">
 		<input v-model="value.value"/>
@@ -17,7 +17,7 @@
 		<textarea v-model="value.value"></textarea>
 	</section>
 	<section v-else-if="value.type === 'textList'" class="tbwccoaw">
-		<textarea v-model="value.value" :placeholder="$t('_pages.script.blocks._textList.info')"></textarea>
+		<textarea v-model="value.value" :placeholder="$ts._pages.script.blocks._textList.info"></textarea>
 	</section>
 	<section v-else-if="value.type === 'number'" class="tbwccoaw">
 		<input v-model="value.value" type="number"/>
@@ -25,13 +25,13 @@
 	<section v-else-if="value.type === 'ref'" class="hpdwcrvs">
 		<select v-model="value.value">
 			<option v-for="v in hpml.getVarsByType(getExpectedType ? getExpectedType() : null).filter(x => x.name !== name)" :value="v.name">{{ v.name }}</option>
-			<optgroup :label="$t('_pages.script.argVariables')">
+			<optgroup :label="$ts._pages.script.argVariables">
 				<option v-for="v in fnSlots" :value="v.name">{{ v.name }}</option>
 			</optgroup>
-			<optgroup :label="$t('_pages.script.pageVariables')">
+			<optgroup :label="$ts._pages.script.pageVariables">
 				<option v-for="v in hpml.getPageVarsByType(getExpectedType ? getExpectedType() : null)" :value="v">{{ v }}</option>
 			</optgroup>
-			<optgroup :label="$t('_pages.script.enviromentVariables')">
+			<optgroup :label="$ts._pages.script.enviromentVariables">
 				<option v-for="v in hpml.getEnvVarsByType(getExpectedType ? getExpectedType() : null)" :value="v">{{ v }}</option>
 			</optgroup>
 		</select>
@@ -41,7 +41,7 @@
 	</section>
 	<section v-else-if="value.type === 'fn'" class="" style="padding:0 16px 16px 16px;">
 		<MkTextarea v-model:value="slots">
-			<span>{{ $t('_pages.script.blocks._fn.slots') }}</span>
+			<span>{{ $ts._pages.script.blocks._fn.slots }}</span>
 			<template #desc>{{ $t('_pages.script.blocks._fn.slots-info') }}</template>
 		</MkTextarea>
 		<XV v-if="value.value.expression" v-model:value="value.value.expression" :title="$t(`_pages.script.blocks._fn.arg1`)" :get-expected-type="() => null" :hpml="hpml" :fn-slots="value.value.slots" :name="name"/>
@@ -214,7 +214,7 @@ export default defineComponent({
 		async changeType() {
 			const { canceled, result: type } = await os.dialog({
 				type: null,
-				title: this.$t('_pages.selectType'),
+				title: this.$ts._pages.selectType,
 				select: {
 					groupedItems: this.getScriptBlockList(this.getExpectedType ? this.getExpectedType() : null)
 				},
diff --git a/src/client/pages/page-editor/page-editor.vue b/src/client/pages/page-editor/page-editor.vue
index 24a868f36..45997dfd6 100644
--- a/src/client/pages/page-editor/page-editor.vue
+++ b/src/client/pages/page-editor/page-editor.vue
@@ -1,52 +1,52 @@
 <template>
 <div class="_section">
 	<div class="_content">
-		<MkA class="view" v-if="pageId" :to="`/@${ author.username }/pages/${ currentName }`"><Fa :icon="faExternalLinkSquareAlt"/> {{ $t('_pages.viewPage') }}</MkA>
+		<MkA class="view" v-if="pageId" :to="`/@${ author.username }/pages/${ currentName }`"><Fa :icon="faExternalLinkSquareAlt"/> {{ $ts._pages.viewPage }}</MkA>
 
 		<div class="buttons" style="margin: 16px 0;">
-			<MkButton inline @click="save" primary class="save"><Fa :icon="faSave"/> {{ $t('save') }}</MkButton>
-			<MkButton inline @click="duplicate" class="duplicate" v-if="pageId"><Fa :icon="faCopy"/> {{ $t('duplicate') }}</MkButton>
-			<MkButton inline @click="del" class="delete" v-if="pageId"><Fa :icon="faTrashAlt"/> {{ $t('delete') }}</MkButton>
+			<MkButton inline @click="save" primary class="save"><Fa :icon="faSave"/> {{ $ts.save }}</MkButton>
+			<MkButton inline @click="duplicate" class="duplicate" v-if="pageId"><Fa :icon="faCopy"/> {{ $ts.duplicate }}</MkButton>
+			<MkButton inline @click="del" class="delete" v-if="pageId"><Fa :icon="faTrashAlt"/> {{ $ts.delete }}</MkButton>
 		</div>
 
 		<MkContainer :body-togglable="true" :expanded="true" class="_vMargin">
-			<template #header><Fa :icon="faCog"/> {{ $t('_pages.pageSetting') }}</template>
+			<template #header><Fa :icon="faCog"/> {{ $ts._pages.pageSetting }}</template>
 			<div class="_section">
 				<MkInput v-model:value="title">
-					<span>{{ $t('_pages.title') }}</span>
+					<span>{{ $ts._pages.title }}</span>
 				</MkInput>
 
 				<MkInput v-model:value="summary">
-					<span>{{ $t('_pages.summary') }}</span>
+					<span>{{ $ts._pages.summary }}</span>
 				</MkInput>
 
 				<MkInput v-model:value="name">
 					<template #prefix>{{ url }}/@{{ author.username }}/pages/</template>
-					<span>{{ $t('_pages.url') }}</span>
+					<span>{{ $ts._pages.url }}</span>
 				</MkInput>
 
-				<MkSwitch v-model:value="alignCenter">{{ $t('_pages.alignCenter') }}</MkSwitch>
+				<MkSwitch v-model:value="alignCenter">{{ $ts._pages.alignCenter }}</MkSwitch>
 
 				<MkSelect v-model:value="font">
-					<template #label>{{ $t('_pages.font') }}</template>
-					<option value="serif">{{ $t('_pages.fontSerif') }}</option>
-					<option value="sans-serif">{{ $t('_pages.fontSansSerif') }}</option>
+					<template #label>{{ $ts._pages.font }}</template>
+					<option value="serif">{{ $ts._pages.fontSerif }}</option>
+					<option value="sans-serif">{{ $ts._pages.fontSansSerif }}</option>
 				</MkSelect>
 
-				<MkSwitch v-model:value="hideTitleWhenPinned">{{ $t('_pages.hideTitleWhenPinned') }}</MkSwitch>
+				<MkSwitch v-model:value="hideTitleWhenPinned">{{ $ts._pages.hideTitleWhenPinned }}</MkSwitch>
 
 				<div class="eyeCatch">
-					<MkButton v-if="eyeCatchingImageId == null && !readonly" @click="setEyeCatchingImage"><Fa :icon="faPlus"/> {{ $t('_pages.eyeCatchingImageSet') }}</MkButton>
+					<MkButton v-if="eyeCatchingImageId == null && !readonly" @click="setEyeCatchingImage"><Fa :icon="faPlus"/> {{ $ts._pages.eyeCatchingImageSet }}</MkButton>
 					<div v-else-if="eyeCatchingImage">
 						<img :src="eyeCatchingImage.url" :alt="eyeCatchingImage.name" style="max-width: 100%;"/>
-						<MkButton @click="removeEyeCatchingImage()" v-if="!readonly"><Fa :icon="faTrashAlt"/> {{ $t('_pages.eyeCatchingImageRemove') }}</MkButton>
+						<MkButton @click="removeEyeCatchingImage()" v-if="!readonly"><Fa :icon="faTrashAlt"/> {{ $ts._pages.eyeCatchingImageRemove }}</MkButton>
 					</div>
 				</div>
 			</div>
 		</MkContainer>
 
 		<MkContainer :body-togglable="true" :expanded="true" class="_vMargin">
-			<template #header><Fa :icon="faStickyNote"/> {{ $t('_pages.contents') }}</template>
+			<template #header><Fa :icon="faStickyNote"/> {{ $ts._pages.contents }}</template>
 			<div class="_section">
 				<XBlocks class="content" v-model:value="content" :hpml="hpml"/>
 
@@ -55,7 +55,7 @@
 		</MkContainer>
 
 		<MkContainer :body-togglable="true" class="_vMargin">
-			<template #header><Fa :icon="faMagic"/> {{ $t('_pages.variables') }}</template>
+			<template #header><Fa :icon="faMagic"/> {{ $ts._pages.variables }}</template>
 			<div class="qmuvgica">
 				<XDraggable tag="div" class="variables" v-show="variables.length > 0" v-model="variables" item-key="name" handle=".drag-handle" :group="{ name: 'variables' }" animation="150" swap-threshold="0.5">
 					<template #item="{element}">
@@ -76,7 +76,7 @@
 		</MkContainer>
 
 		<MkContainer :body-togglable="true" :expanded="true" class="_vMargin">
-			<template #header><Fa :icon="faCode"/> {{ $t('script') }}</template>
+			<template #header><Fa :icon="faCode"/> {{ $ts.script }}</template>
 			<div>
 				<MkTextarea class="_code" v-model:value="script"/>
 			</div>
@@ -135,10 +135,10 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: computed(() => this.initPageId ? {
-				title: this.$t('_pages.editPage'),
+				title: this.$ts._pages.editPage,
 				icon: faPencilAlt,
 			} : {
-				title: this.$t('_pages.newPage'),
+				title: this.$ts._pages.newPage,
 				icon: faPencilAlt,
 			}),
 			author: this.$i,
@@ -254,14 +254,14 @@ export default defineComponent({
 					if (err.info.param == 'name') {
 						os.dialog({
 							type: 'error',
-							title: this.$t('_pages.invalidNameTitle'),
-							text: this.$t('_pages.invalidNameText')
+							title: this.$ts._pages.invalidNameTitle,
+							text: this.$ts._pages.invalidNameText
 						});
 					}
 				} else if (err.code == 'NAME_ALREADY_EXISTS') {
 					os.dialog({
 						type: 'error',
-						text: this.$t('_pages.nameAlreadyExists')
+						text: this.$ts._pages.nameAlreadyExists
 					});
 				}
 			};
@@ -273,7 +273,7 @@ export default defineComponent({
 					this.currentName = this.name.trim();
 					os.dialog({
 						type: 'success',
-						text: this.$t('_pages.updated')
+						text: this.$ts._pages.updated
 					});
 				}).catch(onError);
 			} else {
@@ -283,7 +283,7 @@ export default defineComponent({
 					this.currentName = this.name.trim();
 					os.dialog({
 						type: 'success',
-						text: this.$t('_pages.created')
+						text: this.$ts._pages.created
 					});
 					this.$router.push(`/pages/edit/${this.pageId}`);
 				}).catch(onError);
@@ -302,7 +302,7 @@ export default defineComponent({
 				}).then(() => {
 					os.dialog({
 						type: 'success',
-						text: this.$t('_pages.deleted')
+						text: this.$ts._pages.deleted
 					});
 					this.$router.push(`/pages`);
 				});
@@ -317,7 +317,7 @@ export default defineComponent({
 				this.currentName = this.name.trim();
 				os.dialog({
 					type: 'success',
-					text: this.$t('_pages.created')
+					text: this.$ts._pages.created
 				});
 				this.$router.push(`/pages/edit/${this.pageId}`);
 			});
@@ -326,7 +326,7 @@ export default defineComponent({
 		async add() {
 			const { canceled, result: type } = await os.dialog({
 				type: null,
-				title: this.$t('_pages.chooseBlock'),
+				title: this.$ts._pages.chooseBlock,
 				select: {
 					groupedItems: this.getPageBlockList()
 				},
@@ -340,7 +340,7 @@ export default defineComponent({
 
 		async addVariable() {
 			let { canceled, result: name } = await os.dialog({
-				title: this.$t('_pages.enterVariableName'),
+				title: this.$ts._pages.enterVariableName,
 				input: {
 					type: 'text',
 				},
@@ -353,7 +353,7 @@ export default defineComponent({
 			if (this.hpml.isUsedName(name)) {
 				os.dialog({
 					type: 'error',
-					text: this.$t('_pages.variableNameIsAlreadyUsed')
+					text: this.$ts._pages.variableNameIsAlreadyUsed
 				});
 				return;
 			}
@@ -368,31 +368,31 @@ export default defineComponent({
 
 		getPageBlockList() {
 			return [{
-				label: this.$t('_pages.contentBlocks'),
+				label: this.$ts._pages.contentBlocks,
 				items: [
-					{ value: 'section', text: this.$t('_pages.blocks.section') },
-					{ value: 'text', text: this.$t('_pages.blocks.text') },
-					{ value: 'image', text: this.$t('_pages.blocks.image') },
-					{ value: 'textarea', text: this.$t('_pages.blocks.textarea') },
-					{ value: 'note', text: this.$t('_pages.blocks.note') },
-					{ value: 'canvas', text: this.$t('_pages.blocks.canvas') },
+					{ value: 'section', text: this.$ts._pages.blocks.section },
+					{ value: 'text', text: this.$ts._pages.blocks.text },
+					{ value: 'image', text: this.$ts._pages.blocks.image },
+					{ value: 'textarea', text: this.$ts._pages.blocks.textarea },
+					{ value: 'note', text: this.$ts._pages.blocks.note },
+					{ value: 'canvas', text: this.$ts._pages.blocks.canvas },
 				]
 			}, {
-				label: this.$t('_pages.inputBlocks'),
+				label: this.$ts._pages.inputBlocks,
 				items: [
-					{ value: 'button', text: this.$t('_pages.blocks.button') },
-					{ value: 'radioButton', text: this.$t('_pages.blocks.radioButton') },
-					{ value: 'textInput', text: this.$t('_pages.blocks.textInput') },
-					{ value: 'textareaInput', text: this.$t('_pages.blocks.textareaInput') },
-					{ value: 'numberInput', text: this.$t('_pages.blocks.numberInput') },
-					{ value: 'switch', text: this.$t('_pages.blocks.switch') },
-					{ value: 'counter', text: this.$t('_pages.blocks.counter') }
+					{ value: 'button', text: this.$ts._pages.blocks.button },
+					{ value: 'radioButton', text: this.$ts._pages.blocks.radioButton },
+					{ value: 'textInput', text: this.$ts._pages.blocks.textInput },
+					{ value: 'textareaInput', text: this.$ts._pages.blocks.textareaInput },
+					{ value: 'numberInput', text: this.$ts._pages.blocks.numberInput },
+					{ value: 'switch', text: this.$ts._pages.blocks.switch },
+					{ value: 'counter', text: this.$ts._pages.blocks.counter }
 				]
 			}, {
-				label: this.$t('_pages.specialBlocks'),
+				label: this.$ts._pages.specialBlocks,
 				items: [
-					{ value: 'if', text: this.$t('_pages.blocks.if') },
-					{ value: 'post', text: this.$t('_pages.blocks.post') }
+					{ value: 'if', text: this.$ts._pages.blocks.if },
+					{ value: 'post', text: this.$ts._pages.blocks.post }
 				]
 			}];
 		},
diff --git a/src/client/pages/page.vue b/src/client/pages/page.vue
index f2ccce67b..bd13b28e8 100644
--- a/src/client/pages/page.vue
+++ b/src/client/pages/page.vue
@@ -13,18 +13,18 @@
 	</div>
 	<div class="_section like">
 		<div class="_content">
-			<button class="_button" @click="unlike()" v-if="page.isLiked" :title="$t('_pages.unlike')"><Fa :icon="faHeartS"/></button>
-			<button class="_button" @click="like()" v-else :title="$t('_pages.like')"><Fa :icon="faHeartR"/></button>
+			<button class="_button" @click="unlike()" v-if="page.isLiked" :title="$ts._pages.unlike"><Fa :icon="faHeartS"/></button>
+			<button class="_button" @click="like()" v-else :title="$ts._pages.like"><Fa :icon="faHeartR"/></button>
 			<span class="count" v-if="page.likedCount > 0">{{ page.likedCount }}</span>
 		</div>
 	</div>
 	<div class="_section links">
 		<div class="_content">
-			<MkA :to="`./${page.name}/view-source`" class="link">{{ $t('_pages.viewSource') }}</MkA>
+			<MkA :to="`./${page.name}/view-source`" class="link">{{ $ts._pages.viewSource }}</MkA>
 			<template v-if="$i && $i.id === page.userId">
-				<MkA :to="`/pages/edit/${page.id}`" class="link">{{ $t('_pages.editThisPage') }}</MkA>
-				<button v-if="$i.pinnedPageId === page.id" @click="pin(false)" class="link _textButton">{{ $t('unpin') }}</button>
-				<button v-else @click="pin(true)" class="link _textButton">{{ $t('pin') }}</button>
+				<MkA :to="`/pages/edit/${page.id}`" class="link">{{ $ts._pages.editThisPage }}</MkA>
+				<button v-if="$i.pinnedPageId === page.id" @click="pin(false)" class="link _textButton">{{ $ts.unpin }}</button>
+				<button v-else @click="pin(true)" class="link _textButton">{{ $ts.pin }}</button>
 			</template>
 		</div>
 	</div>
diff --git a/src/client/pages/pages.vue b/src/client/pages/pages.vue
index 26dcddddf..29960351d 100644
--- a/src/client/pages/pages.vue
+++ b/src/client/pages/pages.vue
@@ -1,9 +1,9 @@
 <template>
 <div>
 	<MkTab v-model:value="tab" v-if="$i">
-		<option value="featured"><Fa :icon="faFireAlt"/> {{ $t('_pages.featured') }}</option>
-		<option value="my"><Fa :icon="faEdit"/> {{ $t('_pages.my') }}</option>
-		<option value="liked"><Fa :icon="faHeart"/> {{ $t('_pages.liked') }}</option>
+		<option value="featured"><Fa :icon="faFireAlt"/> {{ $ts._pages.featured }}</option>
+		<option value="my"><Fa :icon="faEdit"/> {{ $ts._pages.my }}</option>
+		<option value="liked"><Fa :icon="faHeart"/> {{ $ts._pages.liked }}</option>
 	</MkTab>
 
 	<div class="_section">
@@ -45,7 +45,7 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: {
-				title: this.$t('pages'),
+				title: this.$ts.pages,
 				icon: faStickyNote,
 				action: {
 					icon: faPlus,
diff --git a/src/client/pages/reversi/game.board.vue b/src/client/pages/reversi/game.board.vue
index 0c4b1505b..8ce232af0 100644
--- a/src/client/pages/reversi/game.board.vue
+++ b/src/client/pages/reversi/game.board.vue
@@ -1,6 +1,6 @@
 <template>
 <div class="xqnhankfuuilcwvhgsopeqncafzsquya">
-	<header><b><MkA :to="userPage(blackUser)"><MkUserName :user="blackUser"/></MkA></b>({{ $t('_reversi.black') }}) vs <b><MkA :to="userPage(whiteUser)"><MkUserName :user="whiteUser"/></MkA></b>({{ $t('_reversi.white') }})</header>
+	<header><b><MkA :to="userPage(blackUser)"><MkUserName :user="blackUser"/></MkA></b>({{ $ts._reversi.black }}) vs <b><MkA :to="userPage(whiteUser)"><MkUserName :user="whiteUser"/></MkA></b>({{ $ts._reversi.white }})</header>
 
 	<div style="overflow: hidden; line-height: 28px;">
 		<p class="turn" v-if="!iAmPlayer && !game.isEnded">
@@ -10,14 +10,14 @@
 		<p class="turn" v-if="logPos != logs.length">
 			<Mfm :key="'past-turn-of:' + turnUser().name" :text="$t('_reversi.pastTurnOf', { name: turnUser().name })" :plain="true" :custom-emojis="turnUser().emojis"/>
 		</p>
-		<p class="turn1" v-if="iAmPlayer && !game.isEnded && !isMyTurn()">{{ $t('_reversi.opponentTurn') }}<MkEllipsis/></p>
-		<p class="turn2" v-if="iAmPlayer && !game.isEnded && isMyTurn()" style="animation: tada 1s linear infinite both;">{{ $t('_reversi.myTurn') }}</p>
+		<p class="turn1" v-if="iAmPlayer && !game.isEnded && !isMyTurn()">{{ $ts._reversi.opponentTurn }}<MkEllipsis/></p>
+		<p class="turn2" v-if="iAmPlayer && !game.isEnded && isMyTurn()" style="animation: tada 1s linear infinite both;">{{ $ts._reversi.myTurn }}</p>
 		<p class="result" v-if="game.isEnded && logPos == logs.length">
 			<template v-if="game.winner">
 				<Mfm :key="'won'" :text="$t('_reversi.won', { name: game.winner.name })" :plain="true" :custom-emojis="game.winner.emojis"/>
-				<span v-if="game.surrendered != null"> ({{ $t('_reversi.surrendered') }})</span>
+				<span v-if="game.surrendered != null"> ({{ $ts._reversi.surrendered }})</span>
 			</template>
-			<template v-else>{{ $t('_reversi.drawn') }}</template>
+			<template v-else>{{ $ts._reversi.drawn }}</template>
 		</p>
 	</div>
 
@@ -54,10 +54,10 @@
 		</div>
 	</div>
 
-	<p class="status"><b>{{ $t('_reversi.turnCount', { count: logPos }) }}</b> {{ $t('_reversi.black') }}:{{ o.blackCount }} {{ $t('_reversi.white') }}:{{ o.whiteCount }} {{ $t('_reversi.total') }}:{{ o.blackCount + o.whiteCount }}</p>
+	<p class="status"><b>{{ $t('_reversi.turnCount', { count: logPos }) }}</b> {{ $ts._reversi.black }}:{{ o.blackCount }} {{ $ts._reversi.white }}:{{ o.whiteCount }} {{ $ts._reversi.total }}:{{ o.blackCount + o.whiteCount }}</p>
 
 	<div class="actions" v-if="!game.isEnded && iAmPlayer">
-		<MkButton @click="surrender" inline>{{ $t('_reversi.surrender') }}</MkButton>
+		<MkButton @click="surrender" inline>{{ $ts._reversi.surrender }}</MkButton>
 	</div>
 
 	<div class="player" v-if="game.isEnded">
@@ -72,9 +72,9 @@
 	</div>
 
 	<div class="info">
-		<p v-if="game.isLlotheo">{{ $t('_reversi.isLlotheo') }}</p>
-		<p v-if="game.loopedBoard">{{ $t('_reversi.loopedMap') }}</p>
-		<p v-if="game.canPutEverywhere">{{ $t('_reversi.canPutEverywhere') }}</p>
+		<p v-if="game.isLlotheo">{{ $ts._reversi.isLlotheo }}</p>
+		<p v-if="game.loopedBoard">{{ $ts._reversi.loopedMap }}</p>
+		<p v-if="game.canPutEverywhere">{{ $ts._reversi.canPutEverywhere }}</p>
 	</div>
 
 	<div class="watchers">
diff --git a/src/client/pages/reversi/game.setting.vue b/src/client/pages/reversi/game.setting.vue
index f23a5816f..404e560a0 100644
--- a/src/client/pages/reversi/game.setting.vue
+++ b/src/client/pages/reversi/game.setting.vue
@@ -3,13 +3,13 @@
 	<header><b><MkUserName :user="game.user1"/></b> vs <b><MkUserName :user="game.user2"/></b></header>
 
 	<div>
-		<p>{{ $t('_reversi.gameSettings') }}</p>
+		<p>{{ $ts._reversi.gameSettings }}</p>
 
 		<div class="card map _panel">
 			<header>
-				<select v-model="mapName" :placeholder="$t('_reversi.chooseBoard')" @change="onMapChange">
+				<select v-model="mapName" :placeholder="$ts._reversi.chooseBoard" @change="onMapChange">
 					<option label="-Custom-" :value="mapName" v-if="mapName == '-Custom-'"/>
-					<option :label="$t('random')" :value="null"/>
+					<option :label="$ts.random" :value="null"/>
 					<optgroup v-for="c in mapCategories" :key="c" :label="c">
 						<option v-for="m in Object.values(maps).filter(m => m.category == c)" :key="m.name" :label="m.name" :value="m.name">{{ m.name }}</option>
 					</optgroup>
@@ -29,20 +29,20 @@
 
 		<div class="card _panel">
 			<header>
-				<span>{{ $t('_reversi.blackOrWhite') }}</span>
+				<span>{{ $ts._reversi.blackOrWhite }}</span>
 			</header>
 
 			<div>
-				<MkRadio v-model="game.bw" value="random" @update:modelValue="updateSettings('bw')">{{ $t('random') }}</MkRadio>
+				<MkRadio v-model="game.bw" value="random" @update:modelValue="updateSettings('bw')">{{ $ts.random }}</MkRadio>
 				<MkRadio v-model="game.bw" :value="'1'" @update:modelValue="updateSettings('bw')">
-					<I18n src="_reversi.blackIs" tag="span">
+					<I18n :src="$ts._reversi.blackIs" tag="span">
 						<template #name>
 							<b><MkUserName :user="game.user1"/></b>
 						</template>
 					</I18n>
 				</MkRadio>
 				<MkRadio v-model="game.bw" :value="'2'" @update:modelValue="updateSettings('bw')">
-					<I18n src="_reversi.blackIs" tag="span">
+					<I18n :src="$ts._reversi.blackIs" tag="span">
 						<template #name>
 							<b><MkUserName :user="game.user2"/></b>
 						</template>
@@ -53,19 +53,19 @@
 
 		<div class="card _panel">
 			<header>
-				<span>{{ $t('_reversi.rules') }}</span>
+				<span>{{ $ts._reversi.rules }}</span>
 			</header>
 
 			<div>
-				<MkSwitch v-model:value="game.isLlotheo" @update:value="updateSettings('isLlotheo')">{{ $t('_reversi.isLlotheo') }}</MkSwitch>
-				<MkSwitch v-model:value="game.loopedBoard" @update:value="updateSettings('loopedBoard')">{{ $t('_reversi.loopedMap') }}</MkSwitch>
-				<MkSwitch v-model:value="game.canPutEverywhere" @update:value="updateSettings('canPutEverywhere')">{{ $t('_reversi.canPutEverywhere') }}</MkSwitch>
+				<MkSwitch v-model:value="game.isLlotheo" @update:value="updateSettings('isLlotheo')">{{ $ts._reversi.isLlotheo }}</MkSwitch>
+				<MkSwitch v-model:value="game.loopedBoard" @update:value="updateSettings('loopedBoard')">{{ $ts._reversi.loopedMap }}</MkSwitch>
+				<MkSwitch v-model:value="game.canPutEverywhere" @update:value="updateSettings('canPutEverywhere')">{{ $ts._reversi.canPutEverywhere }}</MkSwitch>
 			</div>
 		</div>
 
 		<div class="card form _panel" v-if="form">
 			<header>
-				<span>{{ $t('_reversi.botSettings') }}</span>
+				<span>{{ $ts._reversi.botSettings }}</span>
 			</header>
 
 			<div>
@@ -108,16 +108,16 @@
 
 	<footer class="_acrylic">
 		<p class="status">
-			<template v-if="isAccepted && isOpAccepted">{{ $t('_reversi.thisGameIsStartedSoon') }}<MkEllipsis/></template>
-			<template v-if="isAccepted && !isOpAccepted">{{ $t('_reversi.waitingForOther') }}<MkEllipsis/></template>
-			<template v-if="!isAccepted && isOpAccepted">{{ $t('_reversi.waitingForMe') }}</template>
-			<template v-if="!isAccepted && !isOpAccepted">{{ $t('_reversi.waitingBoth') }}<MkEllipsis/></template>
+			<template v-if="isAccepted && isOpAccepted">{{ $ts._reversi.thisGameIsStartedSoon }}<MkEllipsis/></template>
+			<template v-if="isAccepted && !isOpAccepted">{{ $ts._reversi.waitingForOther }}<MkEllipsis/></template>
+			<template v-if="!isAccepted && isOpAccepted">{{ $ts._reversi.waitingForMe }}</template>
+			<template v-if="!isAccepted && !isOpAccepted">{{ $ts._reversi.waitingBoth }}<MkEllipsis/></template>
 		</p>
 
 		<div class="actions">
-			<MkButton inline @click="exit">{{ $t('cancel') }}</MkButton>
-			<MkButton inline primary @click="accept" v-if="!isAccepted">{{ $t('_reversi.ready') }}</MkButton>
-			<MkButton inline primary @click="cancel" v-if="isAccepted">{{ $t('_reversi.cancelReady') }}</MkButton>
+			<MkButton inline @click="exit">{{ $ts.cancel }}</MkButton>
+			<MkButton inline primary @click="accept" v-if="!isAccepted">{{ $ts._reversi.ready }}</MkButton>
+			<MkButton inline primary @click="cancel" v-if="isAccepted">{{ $ts._reversi.cancelReady }}</MkButton>
 		</div>
 	</footer>
 </div>
diff --git a/src/client/pages/reversi/game.vue b/src/client/pages/reversi/game.vue
index 3887ff465..ba750f43c 100644
--- a/src/client/pages/reversi/game.vue
+++ b/src/client/pages/reversi/game.vue
@@ -27,7 +27,7 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: {
-				title: this.$t('_reversi.reversi'),
+				title: this.$ts._reversi.reversi,
 				icon: faGamepad
 			},
 			game: null,
diff --git a/src/client/pages/reversi/index.vue b/src/client/pages/reversi/index.vue
index d57b9ae20..4b5d783f9 100644
--- a/src/client/pages/reversi/index.vue
+++ b/src/client/pages/reversi/index.vue
@@ -1,14 +1,14 @@
 <template>
 <div class="bgvwxkhb" v-if="!matching">
-	<h1>Misskey {{ $t('_reversi.reversi') }}</h1>
+	<h1>Misskey {{ $ts._reversi.reversi }}</h1>
 
 	<div class="play">
-		<MkButton primary round @click="match" style="margin: var(--margin) auto 0 auto;">{{ $t('invite') }}</MkButton>
+		<MkButton primary round @click="match" style="margin: var(--margin) auto 0 auto;">{{ $ts.invite }}</MkButton>
 	</div>
 
 	<div class="_section">
 		<MkFolder v-if="invitations.length > 0">
-			<template #header>{{ $t('invitations') }}</template>
+			<template #header>{{ $ts.invitations }}</template>
 			<div class="nfcacttm">
 				<button class="invitation _panel _button" v-for="invitation in invitations" tabindex="-1" @click="accept(invitation)">
 					<MkAvatar class="avatar" :user="invitation.parent"/>
@@ -20,25 +20,25 @@
 		</MkFolder>
 
 		<MkFolder v-if="myGames.length > 0">
-			<template #header>{{ $t('_reversi.myGames') }}</template>
+			<template #header>{{ $ts._reversi.myGames }}</template>
 			<div class="knextgwz">
 				<MkA class="game _panel" v-for="g in myGames" tabindex="-1" :to="`/games/reversi/${g.id}`" :key="g.id">
 					<div class="players">
 						<MkAvatar class="avatar" :user="g.user1"/><b><MkUserName :user="g.user1"/></b> vs <b><MkUserName :user="g.user2"/></b><MkAvatar class="avatar" :user="g.user2"/>
 					</div>
-					<footer><span class="state" :class="{ playing: !g.isEnded }">{{ g.isEnded ? $t('_reversi.ended') : $t('_reversi.playing') }}</span><MkTime class="time" :time="g.createdAt"/></footer>
+					<footer><span class="state" :class="{ playing: !g.isEnded }">{{ g.isEnded ? $ts._reversi.ended : $ts._reversi.playing }}</span><MkTime class="time" :time="g.createdAt"/></footer>
 				</MkA>
 			</div>
 		</MkFolder>
 
 		<MkFolder v-if="games.length > 0">
-			<template #header>{{ $t('_reversi.allGames') }}</template>
+			<template #header>{{ $ts._reversi.allGames }}</template>
 			<div class="knextgwz">
 				<MkA class="game _panel" v-for="g in games" tabindex="-1" :to="`/games/reversi/${g.id}`" :key="g.id">
 					<div class="players">
 						<MkAvatar class="avatar" :user="g.user1"/><b><MkUserName :user="g.user1"/></b> vs <b><MkUserName :user="g.user2"/></b><MkAvatar class="avatar" :user="g.user2"/>
 					</div>
-					<footer><span class="state" :class="{ playing: !g.isEnded }">{{ g.isEnded ? $t('_reversi.ended') : $t('_reversi.playing') }}</span><MkTime class="time" :time="g.createdAt"/></footer>
+					<footer><span class="state" :class="{ playing: !g.isEnded }">{{ g.isEnded ? $ts._reversi.ended : $ts._reversi.playing }}</span><MkTime class="time" :time="g.createdAt"/></footer>
 				</MkA>
 			</div>
 		</MkFolder>
@@ -46,7 +46,7 @@
 </div>
 <div class="sazhgisb" v-else>
 	<h1>
-		<I18n src="waitingFor" tag="span">
+		<I18n :src="$ts.waitingFor" tag="span">
 			<template #x>
 				<b><MkUserName :user="matching"/></b>
 			</template>
@@ -54,7 +54,7 @@
 		<MkEllipsis/>
 	</h1>
 	<div class="cancel">
-		<MkButton inline round @click="cancel">{{ $t('cancel') }}</MkButton>
+		<MkButton inline round @click="cancel">{{ $ts.cancel }}</MkButton>
 	</div>
 </div>
 </template>
@@ -76,7 +76,7 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: {
-				title: this.$t('_reversi.reversi'),
+				title: this.$ts._reversi.reversi,
 				icon: faGamepad
 			},
 			games: [],
diff --git a/src/client/pages/room/room.vue b/src/client/pages/room/room.vue
index b60372ccb..25dfda1d1 100644
--- a/src/client/pages/room/room.vue
+++ b/src/client/pages/room/room.vue
@@ -8,7 +8,7 @@
 				<div v-for="k in Object.keys(selectedFurnitureInfo.props)" :key="k">
 					<p>{{ k }}</p>
 					<template v-if="selectedFurnitureInfo.props[k] === 'image'">
-						<MkButton @click="chooseImage(k, $event)">{{ $t('_rooms.chooseImage') }}</MkButton>
+						<MkButton @click="chooseImage(k, $event)">{{ $ts._rooms.chooseImage }}</MkButton>
 					</template>
 					<template v-else-if="selectedFurnitureInfo.props[k] === 'color'">
 						<input type="color" :value="selectedFurnitureProps ? selectedFurnitureProps[k] : null" @change="updateColor(k, $event)"/>
@@ -17,33 +17,33 @@
 			</template>
 		</div>
 		<div class="_content">
-			<MkButton inline @click="translate()" :primary="isTranslateMode"><Fa :icon="faArrowsAlt"/> {{ $t('_rooms.translate') }}</MkButton>
-			<MkButton inline @click="rotate()" :primary="isRotateMode"><Fa :icon="faUndo"/> {{ $t('_rooms.rotate') }}</MkButton>
-			<MkButton inline v-if="isTranslateMode || isRotateMode" @click="exit()"><Fa :icon="faBan"/> {{ $t('_rooms.exit') }}</MkButton>
+			<MkButton inline @click="translate()" :primary="isTranslateMode"><Fa :icon="faArrowsAlt"/> {{ $ts._rooms.translate }}</MkButton>
+			<MkButton inline @click="rotate()" :primary="isRotateMode"><Fa :icon="faUndo"/> {{ $ts._rooms.rotate }}</MkButton>
+			<MkButton inline v-if="isTranslateMode || isRotateMode" @click="exit()"><Fa :icon="faBan"/> {{ $ts._rooms.exit }}</MkButton>
 		</div>
 		<div class="_content">
-			<MkButton @click="remove()"><Fa :icon="faTrashAlt"/> {{ $t('_rooms.remove') }}</MkButton>
+			<MkButton @click="remove()"><Fa :icon="faTrashAlt"/> {{ $ts._rooms.remove }}</MkButton>
 		</div>
 	</div>
 
 	<div class="menu _section" v-if="isMyRoom">
 		<div class="_content">
-			<MkButton @click="add()"><Fa :icon="faBoxOpen"/> {{ $t('_rooms.addFurniture') }}</MkButton>
+			<MkButton @click="add()"><Fa :icon="faBoxOpen"/> {{ $ts._rooms.addFurniture }}</MkButton>
 		</div>
 		<div class="_content">
 			<MkSelect :value="roomType" @update:value="updateRoomType($event)">
-				<template #label>{{ $t('_rooms.roomType') }}</template>
-				<option value="default">{{ $t('_rooms._roomType.default') }}</option>
-				<option value="washitsu">{{ $t('_rooms._roomType.washitsu') }}</option>
+				<template #label>{{ $ts._rooms.roomType }}</template>
+				<option value="default">{{ $ts._rooms._roomType.default }}</option>
+				<option value="washitsu">{{ $ts._rooms._roomType.washitsu }}</option>
 			</MkSelect>
 			<label v-if="roomType === 'default'">
-				<span>{{ $t('_rooms.carpetColor') }}</span>
+				<span>{{ $ts._rooms.carpetColor }}</span>
 				<input type="color" :value="carpetColor" @change="updateCarpetColor($event)"/>
 			</label>
 		</div>
 		<div class="_content">
-			<MkButton inline :disabled="!changed" primary @click="save()"><Fa :icon="faSave"/> {{ $t('save') }}</MkButton>
-			<MkButton inline @click="clear()"><Fa :icon="faBroom"/> {{ $t('_rooms.clear') }}</MkButton>
+			<MkButton inline :disabled="!changed" primary @click="save()"><Fa :icon="faSave"/> {{ $ts.save }}</MkButton>
+			<MkButton inline @click="clear()"><Fa :icon="faBroom"/> {{ $ts._rooms.clear }}</MkButton>
 		</div>
 	</div>
 </div>
@@ -83,7 +83,7 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: computed(() => this.user ? {
-				title: this.$t('room'),
+				title: this.$ts.room,
 				avatar: this.user,
 			} : null),
 			user: null,
@@ -141,7 +141,7 @@ export default defineComponent({
 		if (this.changed) {
 			os.dialog({
 				type: 'warning',
-				text: this.$t('leaveConfirm'),
+				text: this.$ts.leaveConfirm,
 				showCancelButton: true
 			}).then(({ canceled }) => {
 				if (canceled) {
@@ -171,7 +171,7 @@ export default defineComponent({
 		async add() {
 			const { canceled, result: id } = await os.dialog({
 				type: null,
-				title: this.$t('_rooms.addFurniture'),
+				title: this.$ts._rooms.addFurniture,
 				select: {
 					items: storeItems.map(item => ({
 						value: item.id, text: this.$t('_rooms._furnitures.' + item.id)
@@ -208,7 +208,7 @@ export default defineComponent({
 		clear() {
 			os.dialog({
 				type: 'warning',
-				text: this.$t('_rooms.clearConfirm'),
+				text: this.$ts._rooms.clearConfirm,
 				showCancelButton: true
 			}).then(({ canceled }) => {
 				if (canceled) return;
diff --git a/src/client/pages/scratchpad.vue b/src/client/pages/scratchpad.vue
index 6dc7ae9c9..d55910e9d 100644
--- a/src/client/pages/scratchpad.vue
+++ b/src/client/pages/scratchpad.vue
@@ -6,14 +6,14 @@
 	</div>
 
 	<MkContainer :body-togglable="true">
-		<template #header><Fa fixed-width/>{{ $t('output') }}</template>
+		<template #header><Fa fixed-width/>{{ $ts.output }}</template>
 		<div class="bepmlvbi">
 			<div v-for="log in logs" class="log" :key="log.id" :class="{ print: log.print }">{{ log.text }}</div>
 		</div>
 	</MkContainer>
 
 	<section class="_section" style="margin-top: var(--margin);">
-		<div class="_content">{{ $t('scratchpadDescription') }}</div>
+		<div class="_content">{{ $ts.scratchpadDescription }}</div>
 	</section>
 </div>
 </template>
@@ -44,7 +44,7 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: {
-				title: this.$t('scratchpad'),
+				title: this.$ts.scratchpad,
 				icon: faTerminal,
 			},
 			code: '',
diff --git a/src/client/pages/settings/2fa.vue b/src/client/pages/settings/2fa.vue
index 8d7a9a383..e7f97725a 100644
--- a/src/client/pages/settings/2fa.vue
+++ b/src/client/pages/settings/2fa.vue
@@ -1,41 +1,41 @@
 <template>
 <section class="_card">
-	<div class="_title"><Fa :icon="faLock"/> {{ $t('twoStepAuthentication') }}</div>
+	<div class="_title"><Fa :icon="faLock"/> {{ $ts.twoStepAuthentication }}</div>
 	<div class="_content">
-		<MkButton v-if="!data && !$i.twoFactorEnabled" @click="register">{{ $t('_2fa.registerDevice') }}</MkButton>
+		<MkButton v-if="!data && !$i.twoFactorEnabled" @click="register">{{ $ts._2fa.registerDevice }}</MkButton>
 		<template v-if="$i.twoFactorEnabled">
-			<p>{{ $t('_2fa.alreadyRegistered') }}</p>
-			<MkButton @click="unregister">{{ $t('unregister') }}</MkButton>
+			<p>{{ $ts._2fa.alreadyRegistered }}</p>
+			<MkButton @click="unregister">{{ $ts.unregister }}</MkButton>
 
 			<template v-if="supportsCredentials">
 				<hr class="totp-method-sep">
 
-				<h2 class="heading">{{ $t('securityKey') }}</h2>
-				<p>{{ $t('_2fa.securityKeyInfo') }}</p>
+				<h2 class="heading">{{ $ts.securityKey }}</h2>
+				<p>{{ $ts._2fa.securityKeyInfo }}</p>
 				<div class="key-list">
 					<div class="key" v-for="key in $i.securityKeysList">
 						<h3>{{ key.name }}</h3>
-						<div class="last-used">{{ $t('lastUsed') }}<MkTime :time="key.lastUsed"/></div>
-						<MkButton @click="unregisterKey(key)">{{ $t('unregister') }}</MkButton>
+						<div class="last-used">{{ $ts.lastUsed }}<MkTime :time="key.lastUsed"/></div>
+						<MkButton @click="unregisterKey(key)">{{ $ts.unregister }}</MkButton>
 					</div>
 				</div>
 
-				<MkSwitch v-model:value="usePasswordLessLogin" @update:value="updatePasswordLessLogin" v-if="$i.securityKeysList.length > 0">{{ $t('passwordLessLogin') }}</MkSwitch>
+				<MkSwitch v-model:value="usePasswordLessLogin" @update:value="updatePasswordLessLogin" v-if="$i.securityKeysList.length > 0">{{ $ts.passwordLessLogin }}</MkSwitch>
 
-				<MkInfo warn v-if="registration && registration.error">{{ $t('error') }} {{ registration.error }}</MkInfo>
-				<MkButton v-if="!registration || registration.error" @click="addSecurityKey">{{ $t('_2fa.registerKey') }}</MkButton>
+				<MkInfo warn v-if="registration && registration.error">{{ $ts.error }} {{ registration.error }}</MkInfo>
+				<MkButton v-if="!registration || registration.error" @click="addSecurityKey">{{ $ts._2fa.registerKey }}</MkButton>
 
 				<ol v-if="registration && !registration.error">
 					<li v-if="registration.stage >= 0">
-						{{ $t('tapSecurityKey') }}
+						{{ $ts.tapSecurityKey }}
 						<Fa icon="spinner" pulse fixed-width v-if="registration.saving && registration.stage == 0" />
 					</li>
 					<li v-if="registration.stage >= 1">
 						<MkForm :disabled="registration.stage != 1 || registration.saving">
 							<MkInput v-model:value="keyName" :max="30">
-								<span>{{ $t('securityKeyName') }}</span>
+								<span>{{ $ts.securityKeyName }}</span>
 							</MkInput>
-							<MkButton @click="registerKey" :disabled="keyName.length == 0">{{ $t('registerSecurityKey') }}</MkButton>
+							<MkButton @click="registerKey" :disabled="keyName.length == 0">{{ $ts.registerSecurityKey }}</MkButton>
 							<Fa icon="spinner" pulse fixed-width v-if="registration.saving && registration.stage == 1" />
 						</MkForm>
 					</li>
@@ -45,7 +45,7 @@
 		<div v-if="data && !$i.twoFactorEnabled">
 			<ol style="margin: 0; padding: 0 0 0 1em;">
 				<li>
-					<I18n src="_2fa.step1" tag="span">
+					<I18n :src="$ts._2fa.step1" tag="span">
 						<template #a>
 							<a href="https://authy.com/" rel="noopener" target="_blank" class="_link">Authy</a>
 						</template>
@@ -54,13 +54,13 @@
 						</template>
 					</I18n>
 				</li>
-				<li>{{ $t('_2fa.step2') }}<br><img :src="data.qr"></li>
-				<li>{{ $t('_2fa.step3') }}<br>
-					<MkInput v-model:value="token" type="text" pattern="^[0-9]{6}$" autocomplete="off" spellcheck="false">{{ $t('token') }}</MkInput>
-					<MkButton primary @click="submit">{{ $t('done') }}</MkButton>
+				<li>{{ $ts._2fa.step2 }}<br><img :src="data.qr"></li>
+				<li>{{ $ts._2fa.step3 }}<br>
+					<MkInput v-model:value="token" type="text" pattern="^[0-9]{6}$" autocomplete="off" spellcheck="false">{{ $ts.token }}</MkInput>
+					<MkButton primary @click="submit">{{ $ts.done }}</MkButton>
 				</li>
 			</ol>
-			<MkInfo>{{ $t('_2fa.step4') }}</MkInfo>
+			<MkInfo>{{ $ts._2fa.step4 }}</MkInfo>
 		</div>
 	</div>
 </section>
@@ -91,7 +91,7 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: {
-				title: this.$t('twoStepAuthentication'),
+				title: this.$ts.twoStepAuthentication,
 				icon: faLock
 			},
 			data: null,
@@ -107,7 +107,7 @@ export default defineComponent({
 	methods: {
 		register() {
 			os.dialog({
-				title: this.$t('password'),
+				title: this.$ts.password,
 				input: {
 					type: 'password'
 				}
@@ -123,7 +123,7 @@ export default defineComponent({
 
 		unregister() {
 			os.dialog({
-				title: this.$t('password'),
+				title: this.$ts.password,
 				input: {
 					type: 'password'
 				}
@@ -173,7 +173,7 @@ export default defineComponent({
 
 		unregisterKey(key) {
 			os.dialog({
-				title: this.$t('password'),
+				title: this.$ts.password,
 				input: {
 					type: 'password'
 				}
@@ -193,7 +193,7 @@ export default defineComponent({
 
 		addSecurityKey() {
 			os.dialog({
-				title: this.$t('password'),
+				title: this.$ts.password,
 				input: {
 					type: 'password'
 				}
diff --git a/src/client/pages/settings/account-info.vue b/src/client/pages/settings/account-info.vue
index 2907b4b27..f231bea22 100644
--- a/src/client/pages/settings/account-info.vue
+++ b/src/client/pages/settings/account-info.vue
@@ -7,124 +7,124 @@
 
 	<FormGroup>
 		<FormKeyValueView>
-			<template #key>{{ $t('registeredDate') }}</template>
+			<template #key>{{ $ts.registeredDate }}</template>
 			<template #value><MkTime :time="$i.createdAt" mode="detail"/></template>
 		</FormKeyValueView>
 	</FormGroup>
 
 	<FormGroup v-if="stats">
-		<template #label>{{ $t('statistics') }}</template>
+		<template #label>{{ $ts.statistics }}</template>
 		<FormKeyValueView>
-			<template #key>{{ $t('notesCount') }}</template>
+			<template #key>{{ $ts.notesCount }}</template>
 			<template #value>{{ number(stats.notesCount) }}</template>
 		</FormKeyValueView>
 		<FormKeyValueView>
-			<template #key>{{ $t('repliesCount') }}</template>
+			<template #key>{{ $ts.repliesCount }}</template>
 			<template #value>{{ number(stats.repliesCount) }}</template>
 		</FormKeyValueView>
 		<FormKeyValueView>
-			<template #key>{{ $t('renotesCount') }}</template>
+			<template #key>{{ $ts.renotesCount }}</template>
 			<template #value>{{ number(stats.renotesCount) }}</template>
 		</FormKeyValueView>
 		<FormKeyValueView>
-			<template #key>{{ $t('repliedCount') }}</template>
+			<template #key>{{ $ts.repliedCount }}</template>
 			<template #value>{{ number(stats.repliedCount) }}</template>
 		</FormKeyValueView>
 		<FormKeyValueView>
-			<template #key>{{ $t('renotedCount') }}</template>
+			<template #key>{{ $ts.renotedCount }}</template>
 			<template #value>{{ number(stats.renotedCount) }}</template>
 		</FormKeyValueView>
 		<FormKeyValueView>
-			<template #key>{{ $t('pollVotesCount') }}</template>
+			<template #key>{{ $ts.pollVotesCount }}</template>
 			<template #value>{{ number(stats.pollVotesCount) }}</template>
 		</FormKeyValueView>
 		<FormKeyValueView>
-			<template #key>{{ $t('pollVotedCount') }}</template>
+			<template #key>{{ $ts.pollVotedCount }}</template>
 			<template #value>{{ number(stats.pollVotedCount) }}</template>
 		</FormKeyValueView>
 		<FormKeyValueView>
-			<template #key>{{ $t('sentReactionsCount') }}</template>
+			<template #key>{{ $ts.sentReactionsCount }}</template>
 			<template #value>{{ number(stats.sentReactionsCount) }}</template>
 		</FormKeyValueView>
 		<FormKeyValueView>
-			<template #key>{{ $t('receivedReactionsCount') }}</template>
+			<template #key>{{ $ts.receivedReactionsCount }}</template>
 			<template #value>{{ number(stats.receivedReactionsCount) }}</template>
 		</FormKeyValueView>
 		<FormKeyValueView>
-			<template #key>{{ $t('noteFavoritesCount') }}</template>
+			<template #key>{{ $ts.noteFavoritesCount }}</template>
 			<template #value>{{ number(stats.noteFavoritesCount) }}</template>
 		</FormKeyValueView>
 		<FormKeyValueView>
-			<template #key>{{ $t('followingCount') }}</template>
+			<template #key>{{ $ts.followingCount }}</template>
 			<template #value>{{ number(stats.followingCount) }}</template>
 		</FormKeyValueView>
 		<FormKeyValueView>
-			<template #key>{{ $t('followingCount') }} ({{ $t('local') }})</template>
+			<template #key>{{ $ts.followingCount }} ({{ $ts.local }})</template>
 			<template #value>{{ number(stats.localFollowingCount) }}</template>
 		</FormKeyValueView>
 		<FormKeyValueView>
-			<template #key>{{ $t('followingCount') }} ({{ $t('remote') }})</template>
+			<template #key>{{ $ts.followingCount }} ({{ $ts.remote }})</template>
 			<template #value>{{ number(stats.remoteFollowingCount) }}</template>
 		</FormKeyValueView>
 		<FormKeyValueView>
-			<template #key>{{ $t('followersCount') }}</template>
+			<template #key>{{ $ts.followersCount }}</template>
 			<template #value>{{ number(stats.followersCount) }}</template>
 		</FormKeyValueView>
 		<FormKeyValueView>
-			<template #key>{{ $t('followersCount') }} ({{ $t('local') }})</template>
+			<template #key>{{ $ts.followersCount }} ({{ $ts.local }})</template>
 			<template #value>{{ number(stats.localFollowersCount) }}</template>
 		</FormKeyValueView>
 		<FormKeyValueView>
-			<template #key>{{ $t('followersCount') }} ({{ $t('remote') }})</template>
+			<template #key>{{ $ts.followersCount }} ({{ $ts.remote }})</template>
 			<template #value>{{ number(stats.remoteFollowersCount) }}</template>
 		</FormKeyValueView>
 		<FormKeyValueView>
-			<template #key>{{ $t('pageLikesCount') }}</template>
+			<template #key>{{ $ts.pageLikesCount }}</template>
 			<template #value>{{ number(stats.pageLikesCount) }}</template>
 		</FormKeyValueView>
 		<FormKeyValueView>
-			<template #key>{{ $t('pageLikedCount') }}</template>
+			<template #key>{{ $ts.pageLikedCount }}</template>
 			<template #value>{{ number(stats.pageLikedCount) }}</template>
 		</FormKeyValueView>
 		<FormKeyValueView>
-			<template #key>{{ $t('driveFilesCount') }}</template>
+			<template #key>{{ $ts.driveFilesCount }}</template>
 			<template #value>{{ number(stats.driveFilesCount) }}</template>
 		</FormKeyValueView>
 		<FormKeyValueView>
-			<template #key>{{ $t('driveUsage') }}</template>
+			<template #key>{{ $ts.driveUsage }}</template>
 			<template #value>{{ bytes(stats.driveUsage) }}</template>
 		</FormKeyValueView>
 		<FormKeyValueView>
-			<template #key>{{ $t('reversiCount') }}</template>
+			<template #key>{{ $ts.reversiCount }}</template>
 			<template #value>{{ number(stats.reversiCount) }}</template>
 		</FormKeyValueView>
 	</FormGroup>
 
 	<FormGroup>
-		<template #label>{{ $t('other') }}</template>
+		<template #label>{{ $ts.other }}</template>
 		<FormKeyValueView>
 			<template #key>emailVerified</template>
-			<template #value>{{ $i.emailVerified ? $t('yes') : $t('no') }}</template>
+			<template #value>{{ $i.emailVerified ? $ts.yes : $ts.no }}</template>
 		</FormKeyValueView>
 		<FormKeyValueView>
 			<template #key>twoFactorEnabled</template>
-			<template #value>{{ $i.twoFactorEnabled ? $t('yes') : $t('no') }}</template>
+			<template #value>{{ $i.twoFactorEnabled ? $ts.yes : $ts.no }}</template>
 		</FormKeyValueView>
 		<FormKeyValueView>
 			<template #key>securityKeys</template>
-			<template #value>{{ $i.securityKeys ? $t('yes') : $t('no') }}</template>
+			<template #value>{{ $i.securityKeys ? $ts.yes : $ts.no }}</template>
 		</FormKeyValueView>
 		<FormKeyValueView>
 			<template #key>usePasswordLessLogin</template>
-			<template #value>{{ $i.usePasswordLessLogin ? $t('yes') : $t('no') }}</template>
+			<template #value>{{ $i.usePasswordLessLogin ? $ts.yes : $ts.no }}</template>
 		</FormKeyValueView>
 		<FormKeyValueView>
 			<template #key>isModerator</template>
-			<template #value>{{ $i.isModerator ? $t('yes') : $t('no') }}</template>
+			<template #value>{{ $i.isModerator ? $ts.yes : $ts.no }}</template>
 		</FormKeyValueView>
 		<FormKeyValueView>
 			<template #key>isAdmin</template>
-			<template #value>{{ $i.isAdmin ? $t('yes') : $t('no') }}</template>
+			<template #value>{{ $i.isAdmin ? $ts.yes : $ts.no }}</template>
 		</FormKeyValueView>
 	</FormGroup>
 </FormBase>
@@ -160,7 +160,7 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: {
-				title: this.$t('accountInfo'),
+				title: this.$ts.accountInfo,
 				icon: faInfoCircle
 			},
 			stats: null
diff --git a/src/client/pages/settings/api.vue b/src/client/pages/settings/api.vue
index 8e5e4fbc6..b02a33b3f 100644
--- a/src/client/pages/settings/api.vue
+++ b/src/client/pages/settings/api.vue
@@ -1,7 +1,7 @@
 <template>
 <FormBase>
-	<FormButton @click="generateToken" primary>{{ $t('generateAccessToken') }}</FormButton>
-	<FormLink to="/settings/apps">{{ $t('manageAccessTokens') }}</FormLink>
+	<FormButton @click="generateToken" primary>{{ $ts.generateAccessToken }}</FormButton>
+	<FormLink to="/settings/apps">{{ $ts.manageAccessTokens }}</FormLink>
 	<FormLink to="/api-console" :behavior="isDesktop ? 'window' : null">API console</FormLink>
 </FormBase>
 </template>
@@ -53,7 +53,7 @@ export default defineComponent({
 
 					os.dialog({
 						type: 'success',
-						title: this.$t('token'),
+						title: this.$ts.token,
 						text: token
 					});
 				},
diff --git a/src/client/pages/settings/apps.vue b/src/client/pages/settings/apps.vue
index 724a2e8d1..3eb31ab52 100644
--- a/src/client/pages/settings/apps.vue
+++ b/src/client/pages/settings/apps.vue
@@ -4,7 +4,7 @@
 		<template #empty>
 			<div class="_fullinfo">
 				<img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
-				<div>{{ $t('nothing') }}</div>
+				<div>{{ $ts.nothing }}</div>
 			</div>
 		</template>
 		<template #default="{items}">
@@ -14,18 +14,18 @@
 					<div class="name">{{ token.name }}</div>
 					<div class="description">{{ token.description }}</div>
 					<div class="_keyValue">
-						<div>{{ $t('installedDate') }}:</div>
+						<div>{{ $ts.installedDate }}:</div>
 						<div><MkTime :time="token.createdAt"/></div>
 					</div>
 					<div class="_keyValue">
-						<div>{{ $t('lastUsedDate') }}:</div>
+						<div>{{ $ts.lastUsedDate }}:</div>
 						<div><MkTime :time="token.lastUsedAt"/></div>
 					</div>
 					<div class="actions">
 						<button class="_button" @click="revoke(token)"><Fa :icon="faTrashAlt"/></button>
 					</div>
 					<details>
-						<summary>{{ $t('details') }}</summary>
+						<summary>{{ $ts.details }}</summary>
 						<ul>
 							<li v-for="p in token.permission" :key="p">{{ $t(`_permissions.${p}`) }}</li>
 						</ul>
@@ -59,7 +59,7 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: {
-				title: this.$t('installedApps'),
+				title: this.$ts.installedApps,
 				icon: faPlug,
 			},
 			pagination: {
diff --git a/src/client/pages/settings/deck.vue b/src/client/pages/settings/deck.vue
index c5833f146..4b672af82 100644
--- a/src/client/pages/settings/deck.vue
+++ b/src/client/pages/settings/deck.vue
@@ -4,18 +4,18 @@
 	<section class="_card _vMargin">
 		<div class="_title"><Fa :icon="faColumns"/> </div>
 		<div class="_content">
-			<div>{{ $t('defaultNavigationBehaviour') }}</div>
-			<MkSwitch v-model:value="navWindow">{{ $t('openInWindow') }}</MkSwitch>
+			<div>{{ $ts.defaultNavigationBehaviour }}</div>
+			<MkSwitch v-model:value="navWindow">{{ $ts.openInWindow }}</MkSwitch>
 		</div>
 		<div class="_content">
 			<MkSwitch v-model:value="alwaysShowMainColumn">
-				{{ $t('_deck.alwaysShowMainColumn') }}
+				{{ $ts._deck.alwaysShowMainColumn }}
 			</MkSwitch>
 		</div>
 		<div class="_content">
-			<div>{{ $t('_deck.columnAlign') }}</div>
-			<MkRadio v-model="columnAlign" value="left">{{ $t('left') }}</MkRadio>
-			<MkRadio v-model="columnAlign" value="center">{{ $t('center') }}</MkRadio>
+			<div>{{ $ts._deck.columnAlign }}</div>
+			<MkRadio v-model="columnAlign" value="left">{{ $ts.left }}</MkRadio>
+			<MkRadio v-model="columnAlign" value="center">{{ $ts.center }}</MkRadio>
 		</div>
 	</section>
 
@@ -60,7 +60,7 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: {
-				title: this.$t('deck'),
+				title: this.$ts.deck,
 				icon: faColumns
 			},
 			faImage, faCog,
diff --git a/src/client/pages/settings/drive.vue b/src/client/pages/settings/drive.vue
index 1b7cf600c..89c9b4bd6 100644
--- a/src/client/pages/settings/drive.vue
+++ b/src/client/pages/settings/drive.vue
@@ -1,9 +1,9 @@
 <template>
 <section class="uawsfosz _section">
-	<div class="_title"><Fa :icon="faCloud"/> {{ $t('drive') }}</div>
+	<div class="_title"><Fa :icon="faCloud"/> {{ $ts.drive }}</div>
 	<div class="_content">
-		<span>{{ $t('uploadFolder') }}: {{ uploadFolder ? uploadFolder.name : '-' }}</span>
-		<MkButton primary @click="chooseUploadFolder()"><Fa :icon="faFolderOpen"/> {{ $t('selectFolder') }}</MkButton>
+		<span>{{ $ts.uploadFolder }}: {{ uploadFolder ? uploadFolder.name : '-' }}</span>
+		<MkButton primary @click="chooseUploadFolder()"><Fa :icon="faFolderOpen"/> {{ $ts.selectFolder }}</MkButton>
 	</div>
 </section>
 </template>
diff --git a/src/client/pages/settings/email-address.vue b/src/client/pages/settings/email-address.vue
index e6e23144d..4aed9bf4c 100644
--- a/src/client/pages/settings/email-address.vue
+++ b/src/client/pages/settings/email-address.vue
@@ -2,12 +2,12 @@
 <FormBase>
 	<FormGroup>
 		<FormInput v-model:value="emailAddress" type="email">
-			{{ $t('emailAddress') }}
-			<template #desc v-if="$i.email && !$i.emailVerified">{{ $t('verificationEmailSent') }}</template>
-			<template #desc v-else-if="emailAddress === $i.email && $i.emailVerified">{{ $t('emailVerified') }}</template>
+			{{ $ts.emailAddress }}
+			<template #desc v-if="$i.email && !$i.emailVerified">{{ $ts.verificationEmailSent }}</template>
+			<template #desc v-else-if="emailAddress === $i.email && $i.emailVerified">{{ $ts.emailVerified }}</template>
 		</FormInput>
 	</FormGroup>
-	<FormButton @click="save" primary>{{ $t('save') }}</FormButton>
+	<FormButton @click="save" primary>{{ $ts.save }}</FormButton>
 </FormBase>
 </template>
 
@@ -34,7 +34,7 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: {
-				title: this.$t('emailAddress'),
+				title: this.$ts.emailAddress,
 				icon: faEnvelope
 			},
 			emailAddress: null,
@@ -54,7 +54,7 @@ export default defineComponent({
 	methods: {
 		save() {
 			os.dialog({
-				title: this.$t('password'),
+				title: this.$ts.password,
 				input: {
 					type: 'password'
 				}
diff --git a/src/client/pages/settings/email.vue b/src/client/pages/settings/email.vue
index 49a099673..830c041ba 100644
--- a/src/client/pages/settings/email.vue
+++ b/src/client/pages/settings/email.vue
@@ -1,11 +1,11 @@
 <template>
 <FormBase>
 	<FormGroup>
-		<template #label>{{ $t('emailAddress') }}</template>
+		<template #label>{{ $ts.emailAddress }}</template>
 		<FormLink to="/settings/email/address">
 			<template v-if="$i.email && !$i.emailVerified" #icon><Fa :icon="faExclamationTriangle" style="color: var(--warn);"/></template>
 			<template v-else-if="$i.email && $i.emailVerified" #icon><Fa :icon="faCheck" style="color: var(--success);"/></template>
-			{{ $i.email || $t('notSet') }}
+			{{ $i.email || $ts.notSet }}
 		</FormLink>
 	</FormGroup>
 </FormBase>
@@ -34,7 +34,7 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: {
-				title: this.$t('email'),
+				title: this.$ts.email,
 				icon: faEnvelope
 			},
 			faCog, faExclamationTriangle, faCheck
diff --git a/src/client/pages/settings/experimental-features.vue b/src/client/pages/settings/experimental-features.vue
index cc03afd1f..01692154e 100644
--- a/src/client/pages/settings/experimental-features.vue
+++ b/src/client/pages/settings/experimental-features.vue
@@ -32,7 +32,7 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: {
-				title: this.$t('experimentalFeatures'),
+				title: this.$ts.experimentalFeatures,
 				icon: faFlask
 			},
 			stats: null
diff --git a/src/client/pages/settings/general.vue b/src/client/pages/settings/general.vue
index 92622f451..e6bb1b8a7 100644
--- a/src/client/pages/settings/general.vue
+++ b/src/client/pages/settings/general.vue
@@ -1,12 +1,12 @@
 <template>
 <FormBase>
-	<FormSwitch v-model:value="showFixedPostForm">{{ $t('showFixedPostForm') }}</FormSwitch>
+	<FormSwitch v-model:value="showFixedPostForm">{{ $ts.showFixedPostForm }}</FormSwitch>
 
 	<FormSelect v-model:value="lang">
-		<template #label>{{ $t('uiLanguage') }}</template>
+		<template #label>{{ $ts.uiLanguage }}</template>
 		<option v-for="x in langs" :value="x[0]" :key="x[0]">{{ x[1] }}</option>
 		<template #caption>
-			<I18n src="i18nInfo" tag="span">
+			<I18n :src="$ts.i18nInfo" tag="span">
 				<template #link>
 					<MkLink url="https://crowdin.com/project/misskey">Crowdin</MkLink>
 				</template>
@@ -15,35 +15,35 @@
 	</FormSelect>
 
 	<FormGroup>
-		<template #label>{{ $t('behavior') }}</template>
-		<FormSwitch v-model:value="imageNewTab">{{ $t('openImageInNewTab') }}</FormSwitch>
-		<FormSwitch v-model:value="enableInfiniteScroll">{{ $t('enableInfiniteScroll') }}</FormSwitch>
-		<FormSwitch v-model:value="disablePagesScript">{{ $t('disablePagesScript') }}</FormSwitch>
+		<template #label>{{ $ts.behavior }}</template>
+		<FormSwitch v-model:value="imageNewTab">{{ $ts.openImageInNewTab }}</FormSwitch>
+		<FormSwitch v-model:value="enableInfiniteScroll">{{ $ts.enableInfiniteScroll }}</FormSwitch>
+		<FormSwitch v-model:value="disablePagesScript">{{ $ts.disablePagesScript }}</FormSwitch>
 	</FormGroup>
 
 	<FormSelect v-model:value="serverDisconnectedBehavior">
-		<template #label>{{ $t('whenServerDisconnected') }}</template>
-		<option value="reload">{{ $t('_serverDisconnectedBehavior.reload') }}</option>
-		<option value="dialog">{{ $t('_serverDisconnectedBehavior.dialog') }}</option>
-		<option value="quiet">{{ $t('_serverDisconnectedBehavior.quiet') }}</option>
+		<template #label>{{ $ts.whenServerDisconnected }}</template>
+		<option value="reload">{{ $ts._serverDisconnectedBehavior.reload }}</option>
+		<option value="dialog">{{ $ts._serverDisconnectedBehavior.dialog }}</option>
+		<option value="quiet">{{ $ts._serverDisconnectedBehavior.quiet }}</option>
 	</FormSelect>
 
 	<FormGroup>
-		<template #label>{{ $t('appearance') }}</template>
-		<FormSwitch v-model:value="disableAnimatedMfm">{{ $t('disableAnimatedMfm') }}</FormSwitch>
-		<FormSwitch v-model:value="reduceAnimation">{{ $t('reduceUiAnimation') }}</FormSwitch>
-		<FormSwitch v-model:value="useBlurEffectForModal">{{ $t('useBlurEffectForModal') }}</FormSwitch>
-		<FormSwitch v-model:value="showGapBetweenNotesInTimeline">{{ $t('showGapBetweenNotesInTimeline') }}</FormSwitch>
-		<FormSwitch v-model:value="loadRawImages">{{ $t('loadRawImages') }}</FormSwitch>
-		<FormSwitch v-model:value="disableShowingAnimatedImages">{{ $t('disableShowingAnimatedImages') }}</FormSwitch>
-		<FormSwitch v-model:value="useSystemFont">{{ $t('useSystemFont') }}</FormSwitch>
-		<FormSwitch v-model:value="useOsNativeEmojis">{{ $t('useOsNativeEmojis') }}
+		<template #label>{{ $ts.appearance }}</template>
+		<FormSwitch v-model:value="disableAnimatedMfm">{{ $ts.disableAnimatedMfm }}</FormSwitch>
+		<FormSwitch v-model:value="reduceAnimation">{{ $ts.reduceUiAnimation }}</FormSwitch>
+		<FormSwitch v-model:value="useBlurEffectForModal">{{ $ts.useBlurEffectForModal }}</FormSwitch>
+		<FormSwitch v-model:value="showGapBetweenNotesInTimeline">{{ $ts.showGapBetweenNotesInTimeline }}</FormSwitch>
+		<FormSwitch v-model:value="loadRawImages">{{ $ts.loadRawImages }}</FormSwitch>
+		<FormSwitch v-model:value="disableShowingAnimatedImages">{{ $ts.disableShowingAnimatedImages }}</FormSwitch>
+		<FormSwitch v-model:value="useSystemFont">{{ $ts.useSystemFont }}</FormSwitch>
+		<FormSwitch v-model:value="useOsNativeEmojis">{{ $ts.useOsNativeEmojis }}
 			<div><Mfm text="🍮🍦🍭🍩🍰🍫🍬🥞🍪" :key="useOsNativeEmojis"/></div>
 		</FormSwitch>
 	</FormGroup>
 
 	<FormRadios v-model="fontSize">
-		<template #desc>{{ $t('fontSize') }}</template>
+		<template #desc>{{ $ts.fontSize }}</template>
 		<option value="small"><span style="font-size: 14px;">Aa</span></option>
 		<option :value="null"><span style="font-size: 16px;">Aa</span></option>
 		<option value="large"><span style="font-size: 18px;">Aa</span></option>
@@ -51,34 +51,34 @@
 	</FormRadios>
 
 	<FormSelect v-model:value="instanceTicker">
-		<template #label>{{ $t('instanceTicker') }}</template>
-		<option value="none">{{ $t('_instanceTicker.none') }}</option>
-		<option value="remote">{{ $t('_instanceTicker.remote') }}</option>
-		<option value="always">{{ $t('_instanceTicker.always') }}</option>
+		<template #label>{{ $ts.instanceTicker }}</template>
+		<option value="none">{{ $ts._instanceTicker.none }}</option>
+		<option value="remote">{{ $ts._instanceTicker.remote }}</option>
+		<option value="always">{{ $ts._instanceTicker.always }}</option>
 	</FormSelect>
 
 	<FormSelect v-model:value="nsfw">
-		<template #label>{{ $t('nsfw') }}</template>
-		<option value="respect">{{ $t('_nsfw.respect') }}</option>
-		<option value="ignore">{{ $t('_nsfw.ignore') }}</option>
-		<option value="force">{{ $t('_nsfw.force') }}</option>
+		<template #label>{{ $ts.nsfw }}</template>
+		<option value="respect">{{ $ts._nsfw.respect }}</option>
+		<option value="ignore">{{ $ts._nsfw.ignore }}</option>
+		<option value="force">{{ $ts._nsfw.force }}</option>
 	</FormSelect>
 
 	<FormGroup>
-		<template #label>{{ $t('defaultNavigationBehaviour') }}</template>
-		<FormSwitch v-model:value="defaultSideView">{{ $t('openInSideView') }}</FormSwitch>
+		<template #label>{{ $ts.defaultNavigationBehaviour }}</template>
+		<FormSwitch v-model:value="defaultSideView">{{ $ts.openInSideView }}</FormSwitch>
 	</FormGroup>
 
 	<FormSelect v-model:value="chatOpenBehavior">
-		<template #label>{{ $t('chatOpenBehavior') }}</template>
-		<option value="page">{{ $t('showInPage') }}</option>
-		<option value="window">{{ $t('openInWindow') }}</option>
-		<option value="popout">{{ $t('popout') }}</option>
+		<template #label>{{ $ts.chatOpenBehavior }}</template>
+		<option value="page">{{ $ts.showInPage }}</option>
+		<option value="window">{{ $ts.openInWindow }}</option>
+		<option value="popout">{{ $ts.popout }}</option>
 	</FormSelect>
 
-	<FormLink to="/settings/deck">{{ $t('deck') }}</FormLink>
+	<FormLink to="/settings/deck">{{ $ts.deck }}</FormLink>
 
-	<FormButton @click="cacheClear()" danger>{{ $t('cacheClear') }}</FormButton>
+	<FormButton @click="cacheClear()" danger>{{ $ts.cacheClear }}</FormButton>
 </FormBase>
 </template>
 
@@ -116,7 +116,7 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: {
-				title: this.$t('general'),
+				title: this.$ts.general,
 				icon: faCogs
 			},
 			langs,
diff --git a/src/client/pages/settings/import-export.vue b/src/client/pages/settings/import-export.vue
index c081c9bb4..91020070a 100644
--- a/src/client/pages/settings/import-export.vue
+++ b/src/client/pages/settings/import-export.vue
@@ -1,16 +1,16 @@
 <template>
 <section class="_section">
-	<div class="_title"><Fa :icon="faBoxes"/> {{ $t('importAndExport') }}</div>
+	<div class="_title"><Fa :icon="faBoxes"/> {{ $ts.importAndExport }}</div>
 	<div class="_content">
 		<MkSelect v-model:value="exportTarget">
-			<option value="notes">{{ $t('_exportOrImport.allNotes') }}</option>
-			<option value="following">{{ $t('_exportOrImport.followingList') }}</option>
-			<option value="user-lists">{{ $t('_exportOrImport.userLists') }}</option>
-			<option value="mute">{{ $t('_exportOrImport.muteList') }}</option>
-			<option value="blocking">{{ $t('_exportOrImport.blockingList') }}</option>
+			<option value="notes">{{ $ts._exportOrImport.allNotes }}</option>
+			<option value="following">{{ $ts._exportOrImport.followingList }}</option>
+			<option value="user-lists">{{ $ts._exportOrImport.userLists }}</option>
+			<option value="mute">{{ $ts._exportOrImport.muteList }}</option>
+			<option value="blocking">{{ $ts._exportOrImport.blockingList }}</option>
 		</MkSelect>
-		<MkButton inline primary @click="doExport"><Fa :icon="faDownload"/> {{ $t('export') }}</MkButton>
-		<MkButton inline primary @click="doImport" :disabled="!['following', 'user-lists'].includes(exportTarget)"><Fa :icon="faUpload"/> {{ $t('import') }}</MkButton>
+		<MkButton inline primary @click="doExport"><Fa :icon="faDownload"/> {{ $ts.export }}</MkButton>
+		<MkButton inline primary @click="doImport" :disabled="!['following', 'user-lists'].includes(exportTarget)"><Fa :icon="faUpload"/> {{ $ts.import }}</MkButton>
 	</div>
 </section>
 </template>
@@ -48,7 +48,7 @@ export default defineComponent({
 			.then(() => {
 				os.dialog({
 					type: 'info',
-					text: this.$t('exportRequested')
+					text: this.$ts.exportRequested
 				});
 			}).catch((e: any) => {
 				os.dialog({
@@ -69,7 +69,7 @@ export default defineComponent({
 			}).then(() => {
 				os.dialog({
 					type: 'info',
-					text: this.$t('importRequested')
+					text: this.$ts.importRequested
 				});
 			}).catch((e: any) => {
 				os.dialog({
diff --git a/src/client/pages/settings/index.vue b/src/client/pages/settings/index.vue
index 209d70aae..b42a51112 100644
--- a/src/client/pages/settings/index.vue
+++ b/src/client/pages/settings/index.vue
@@ -2,33 +2,33 @@
 <div class="vvcocwet" :class="{ wide: !narrow }" ref="el">
 	<FormBase class="nav" v-if="!narrow || page == null" :force-wide="!narrow">
 		<FormGroup>
-			<template #label>{{ $t('basicSettings') }}</template>
-			<FormLink :active="page === 'profile'" replace to="/settings/profile"><template #icon><Fa :icon="faUser"/></template>{{ $t('profile') }}</FormLink>
-			<FormLink :active="page === 'privacy'" replace to="/settings/privacy"><template #icon><Fa :icon="faLockOpen"/></template>{{ $t('privacy') }}</FormLink>
-			<FormLink :active="page === 'reaction'" replace to="/settings/reaction"><template #icon><Fa :icon="faLaugh"/></template>{{ $t('reaction') }}</FormLink>
-			<FormLink :active="page === 'notifications'" replace to="/settings/notifications"><template #icon><Fa :icon="faBell"/></template>{{ $t('notifications') }}</FormLink>
-			<FormLink :active="page === 'email'" replace to="/settings/email"><template #icon><Fa :icon="faEnvelope"/></template>{{ $t('email') }}</FormLink>
-			<FormLink :active="page === 'integration'" replace to="/settings/integration"><template #icon><Fa :icon="faShareAlt"/></template>{{ $t('integration') }}</FormLink>
-			<FormLink :active="page === 'security'" replace to="/settings/security"><template #icon><Fa :icon="faLock"/></template>{{ $t('security') }}</FormLink>
+			<template #label>{{ $ts.basicSettings }}</template>
+			<FormLink :active="page === 'profile'" replace to="/settings/profile"><template #icon><Fa :icon="faUser"/></template>{{ $ts.profile }}</FormLink>
+			<FormLink :active="page === 'privacy'" replace to="/settings/privacy"><template #icon><Fa :icon="faLockOpen"/></template>{{ $ts.privacy }}</FormLink>
+			<FormLink :active="page === 'reaction'" replace to="/settings/reaction"><template #icon><Fa :icon="faLaugh"/></template>{{ $ts.reaction }}</FormLink>
+			<FormLink :active="page === 'notifications'" replace to="/settings/notifications"><template #icon><Fa :icon="faBell"/></template>{{ $ts.notifications }}</FormLink>
+			<FormLink :active="page === 'email'" replace to="/settings/email"><template #icon><Fa :icon="faEnvelope"/></template>{{ $ts.email }}</FormLink>
+			<FormLink :active="page === 'integration'" replace to="/settings/integration"><template #icon><Fa :icon="faShareAlt"/></template>{{ $ts.integration }}</FormLink>
+			<FormLink :active="page === 'security'" replace to="/settings/security"><template #icon><Fa :icon="faLock"/></template>{{ $ts.security }}</FormLink>
 		</FormGroup>
 		<FormGroup>
-			<template #label>{{ $t('clientSettings') }}</template>
-			<FormLink :active="page === 'general'" replace to="/settings/general"><template #icon><Fa :icon="faCogs"/></template>{{ $t('general') }}</FormLink>
-			<FormLink :active="page === 'theme'" replace to="/settings/theme"><template #icon><Fa :icon="faPalette"/></template>{{ $t('theme') }}</FormLink>
-			<FormLink :active="page === 'sidebar'" replace to="/settings/sidebar"><template #icon><Fa :icon="faListUl"/></template>{{ $t('sidebar') }}</FormLink>
-			<FormLink :active="page === 'sounds'" replace to="/settings/sounds"><template #icon><Fa :icon="faMusic"/></template>{{ $t('sounds') }}</FormLink>
-			<FormLink :active="page === 'plugins'" replace to="/settings/plugins"><template #icon><Fa :icon="faPlug"/></template>{{ $t('plugins') }}</FormLink>
+			<template #label>{{ $ts.clientSettings }}</template>
+			<FormLink :active="page === 'general'" replace to="/settings/general"><template #icon><Fa :icon="faCogs"/></template>{{ $ts.general }}</FormLink>
+			<FormLink :active="page === 'theme'" replace to="/settings/theme"><template #icon><Fa :icon="faPalette"/></template>{{ $ts.theme }}</FormLink>
+			<FormLink :active="page === 'sidebar'" replace to="/settings/sidebar"><template #icon><Fa :icon="faListUl"/></template>{{ $ts.sidebar }}</FormLink>
+			<FormLink :active="page === 'sounds'" replace to="/settings/sounds"><template #icon><Fa :icon="faMusic"/></template>{{ $ts.sounds }}</FormLink>
+			<FormLink :active="page === 'plugins'" replace to="/settings/plugins"><template #icon><Fa :icon="faPlug"/></template>{{ $ts.plugins }}</FormLink>
 		</FormGroup>
 		<FormGroup>
-			<template #label>{{ $t('otherSettings') }}</template>
-			<FormLink :active="page === 'import-export'" replace to="/settings/import-export"><template #icon><Fa :icon="faBoxes"/></template>{{ $t('importAndExport') }}</FormLink>
-			<FormLink :active="page === 'mute-block'" replace to="/settings/mute-block"><template #icon><Fa :icon="faBan"/></template>{{ $t('muteAndBlock') }}</FormLink>
-			<FormLink :active="page === 'word-mute'" replace to="/settings/word-mute"><template #icon><Fa :icon="faCommentSlash"/></template>{{ $t('wordMute') }}</FormLink>
+			<template #label>{{ $ts.otherSettings }}</template>
+			<FormLink :active="page === 'import-export'" replace to="/settings/import-export"><template #icon><Fa :icon="faBoxes"/></template>{{ $ts.importAndExport }}</FormLink>
+			<FormLink :active="page === 'mute-block'" replace to="/settings/mute-block"><template #icon><Fa :icon="faBan"/></template>{{ $ts.muteAndBlock }}</FormLink>
+			<FormLink :active="page === 'word-mute'" replace to="/settings/word-mute"><template #icon><Fa :icon="faCommentSlash"/></template>{{ $ts.wordMute }}</FormLink>
 			<FormLink :active="page === 'api'" replace to="/settings/api"><template #icon><Fa :icon="faKey"/></template>API</FormLink>
-			<FormLink :active="page === 'other'" replace to="/settings/other"><template #icon><Fa :icon="faEllipsisH"/></template>{{ $t('other') }}</FormLink>
+			<FormLink :active="page === 'other'" replace to="/settings/other"><template #icon><Fa :icon="faEllipsisH"/></template>{{ $ts.other }}</FormLink>
 		</FormGroup>
 		<FormGroup>
-			<FormButton @click="logout" danger>{{ $t('logout') }}</FormButton>
+			<FormButton @click="logout" danger>{{ $ts.logout }}</FormButton>
 		</FormGroup>
 	</FormBase>
 	<div class="main">
diff --git a/src/client/pages/settings/integration.vue b/src/client/pages/settings/integration.vue
index c10a90151..4516f490d 100644
--- a/src/client/pages/settings/integration.vue
+++ b/src/client/pages/settings/integration.vue
@@ -2,23 +2,23 @@
 <section class="_section">
 	<div class="_content" v-if="enableTwitterIntegration">
 		<header><Fa :icon="faTwitter"/> Twitter</header>
-		<p v-if="integrations.twitter">{{ $t('connectedTo') }}: <a :href="`https://twitter.com/${integrations.twitter.screenName}`" rel="nofollow noopener" target="_blank">@{{ integrations.twitter.screenName }}</a></p>
-		<MkButton v-if="integrations.twitter" @click="disconnectTwitter">{{ $t('disconnectSerice') }}</MkButton>
-		<MkButton v-else @click="connectTwitter">{{ $t('connectSerice') }}</MkButton>
+		<p v-if="integrations.twitter">{{ $ts.connectedTo }}: <a :href="`https://twitter.com/${integrations.twitter.screenName}`" rel="nofollow noopener" target="_blank">@{{ integrations.twitter.screenName }}</a></p>
+		<MkButton v-if="integrations.twitter" @click="disconnectTwitter">{{ $ts.disconnectSerice }}</MkButton>
+		<MkButton v-else @click="connectTwitter">{{ $ts.connectSerice }}</MkButton>
 	</div>
 
 	<div class="_content" v-if="enableDiscordIntegration">
 		<header><Fa :icon="faDiscord"/> Discord</header>
-		<p v-if="integrations.discord">{{ $t('connectedTo') }}: <a :href="`https://discordapp.com/users/${integrations.discord.id}`" rel="nofollow noopener" target="_blank">@{{ integrations.discord.username }}#{{ integrations.discord.discriminator }}</a></p>
-		<MkButton v-if="integrations.discord" @click="disconnectDiscord">{{ $t('disconnectSerice') }}</MkButton>
-		<MkButton v-else @click="connectDiscord">{{ $t('connectSerice') }}</MkButton>
+		<p v-if="integrations.discord">{{ $ts.connectedTo }}: <a :href="`https://discordapp.com/users/${integrations.discord.id}`" rel="nofollow noopener" target="_blank">@{{ integrations.discord.username }}#{{ integrations.discord.discriminator }}</a></p>
+		<MkButton v-if="integrations.discord" @click="disconnectDiscord">{{ $ts.disconnectSerice }}</MkButton>
+		<MkButton v-else @click="connectDiscord">{{ $ts.connectSerice }}</MkButton>
 	</div>
 
 	<div class="_content" v-if="enableGithubIntegration">
 		<header><Fa :icon="faGithub"/> GitHub</header>
-		<p v-if="integrations.github">{{ $t('connectedTo') }}: <a :href="`https://github.com/${integrations.github.login}`" rel="nofollow noopener" target="_blank">@{{ integrations.github.login }}</a></p>
-		<MkButton v-if="integrations.github" @click="disconnectGithub">{{ $t('disconnectSerice') }}</MkButton>
-		<MkButton v-else @click="connectGithub">{{ $t('connectSerice') }}</MkButton>
+		<p v-if="integrations.github">{{ $ts.connectedTo }}: <a :href="`https://github.com/${integrations.github.login}`" rel="nofollow noopener" target="_blank">@{{ integrations.github.login }}</a></p>
+		<MkButton v-if="integrations.github" @click="disconnectGithub">{{ $ts.disconnectSerice }}</MkButton>
+		<MkButton v-else @click="connectGithub">{{ $ts.connectSerice }}</MkButton>
 	</div>
 </section>
 </template>
@@ -41,7 +41,7 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: {
-				title: this.$t('integration'),
+				title: this.$ts.integration,
 				icon: faShareAlt
 			},
 			apiUrl,
diff --git a/src/client/pages/settings/mute-block.vue b/src/client/pages/settings/mute-block.vue
index 43e2c396b..3457e3161 100644
--- a/src/client/pages/settings/mute-block.vue
+++ b/src/client/pages/settings/mute-block.vue
@@ -1,12 +1,12 @@
 <template>
 <section class="rrfwjxfl _section">
 	<MkTab v-model:value="tab" style="margin-bottom: var(--margin);">
-		<option value="mute">{{ $t('mutedUsers') }}</option>
-		<option value="block">{{ $t('blockedUsers') }}</option>
+		<option value="mute">{{ $ts.mutedUsers }}</option>
+		<option value="block">{{ $ts.blockedUsers }}</option>
 	</MkTab>
 	<div class="_content" v-if="tab === 'mute'">
 		<MkPagination :pagination="mutingPagination" class="muting">
-			<template #empty><MkInfo>{{ $t('noUsers') }}</MkInfo></template>
+			<template #empty><MkInfo>{{ $ts.noUsers }}</MkInfo></template>
 			<template #default="{items}">
 				<div class="user" v-for="mute in items" :key="mute.id">
 					<MkA class="name" :to="userPage(mute.mutee)">
@@ -18,7 +18,7 @@
 	</div>
 	<div class="_content" v-if="tab === 'block'">
 		<MkPagination :pagination="blockingPagination" class="blocking">
-			<template #empty><MkInfo>{{ $t('noUsers') }}</MkInfo></template>
+			<template #empty><MkInfo>{{ $ts.noUsers }}</MkInfo></template>
 			<template #default="{items}">
 				<div class="user" v-for="block in items" :key="block.id">
 					<MkA class="name" :to="userPage(block.blockee)">
@@ -52,7 +52,7 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: {
-				title: this.$t('muteAndBlock'),
+				title: this.$ts.muteAndBlock,
 				icon: faBan
 			},
 			tab: 'mute',
diff --git a/src/client/pages/settings/notifications.vue b/src/client/pages/settings/notifications.vue
index db89e7cfe..c3b7ecae1 100644
--- a/src/client/pages/settings/notifications.vue
+++ b/src/client/pages/settings/notifications.vue
@@ -1,10 +1,10 @@
 <template>
 <FormBase>
-	<FormLink @click="configure">{{ $t('notificationSetting') }}</FormLink>
+	<FormLink @click="configure">{{ $ts.notificationSetting }}</FormLink>
 	<FormGroup>
-		<FormButton @click="readAllNotifications">{{ $t('markAsReadAllNotifications') }}</FormButton>
-		<FormButton @click="readAllUnreadNotes">{{ $t('markAsReadAllUnreadNotes') }}</FormButton>
-		<FormButton @click="readAllMessagingMessages">{{ $t('markAsReadAllTalkMessages') }}</FormButton>
+		<FormButton @click="readAllNotifications">{{ $ts.markAsReadAllNotifications }}</FormButton>
+		<FormButton @click="readAllUnreadNotes">{{ $ts.markAsReadAllUnreadNotes }}</FormButton>
+		<FormButton @click="readAllMessagingMessages">{{ $ts.markAsReadAllTalkMessages }}</FormButton>
 	</FormGroup>
 </FormBase>
 </template>
@@ -33,7 +33,7 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: {
-				title: this.$t('notifications'),
+				title: this.$ts.notifications,
 				icon: faBell
 			},
 			faCog
diff --git a/src/client/pages/settings/other.vue b/src/client/pages/settings/other.vue
index 599a1d853..1f7c8f7df 100644
--- a/src/client/pages/settings/other.vue
+++ b/src/client/pages/settings/other.vue
@@ -1,14 +1,14 @@
 <template>
 <FormBase>
 	<FormSwitch :value="$i.injectFeaturedNote" @update:value="onChangeInjectFeaturedNote">
-		{{ $t('showFeaturedNotesInTimeline') }}
+		{{ $ts.showFeaturedNotesInTimeline }}
 	</FormSwitch>
 
-	<FormLink to="/settings/account-info">{{ $t('accountInfo') }}</FormLink>
-	<FormLink to="/settings/experimental-features">{{ $t('experimentalFeatures') }}</FormLink>
+	<FormLink to="/settings/account-info">{{ $ts.accountInfo }}</FormLink>
+	<FormLink to="/settings/experimental-features">{{ $ts.experimentalFeatures }}</FormLink>
 
 	<FormGroup>
-		<template #label>{{ $t('developer') }}</template>
+		<template #label>{{ $ts.developer }}</template>
 		<FormSwitch v-model:value="debug" @update:value="changeDebug">
 			DEBUG MODE
 		</FormSwitch>
@@ -47,7 +47,7 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: {
-				title: this.$t('other'),
+				title: this.$ts.other,
 				icon: faEllipsisH
 			},
 			debug
diff --git a/src/client/pages/settings/plugins.vue b/src/client/pages/settings/plugins.vue
index 1fda97fc5..7f3734e34 100644
--- a/src/client/pages/settings/plugins.vue
+++ b/src/client/pages/settings/plugins.vue
@@ -1,41 +1,41 @@
 <template>
 <section class="_section">
-	<div class="_title"><Fa :icon="faPlug"/> {{ $t('plugins') }}</div>
+	<div class="_title"><Fa :icon="faPlug"/> {{ $ts.plugins }}</div>
 	<div class="_content">
 		<details>
-			<summary><Fa :icon="faDownload"/> {{ $t('install') }}</summary>
-			<MkInfo warn>{{ $t('pluginInstallWarn') }}</MkInfo>
+			<summary><Fa :icon="faDownload"/> {{ $ts.install }}</summary>
+			<MkInfo warn>{{ $ts.pluginInstallWarn }}</MkInfo>
 			<MkTextarea v-model:value="script" tall>
-				<span>{{ $t('script') }}</span>
+				<span>{{ $ts.script }}</span>
 			</MkTextarea>
-			<MkButton @click="install()" primary><Fa :icon="faSave"/> {{ $t('install') }}</MkButton>
+			<MkButton @click="install()" primary><Fa :icon="faSave"/> {{ $ts.install }}</MkButton>
 		</details>
 	</div>
 	<div class="_content">
 		<details>
-			<summary><Fa :icon="faFolderOpen"/> {{ $t('manage') }}</summary>
+			<summary><Fa :icon="faFolderOpen"/> {{ $ts.manage }}</summary>
 			<MkSelect v-model:value="selectedPluginId">
 				<option v-for="x in plugins" :value="x.id" :key="x.id">{{ x.name }}</option>
 			</MkSelect>
 			<template v-if="selectedPlugin">
 				<div style="margin: -8px 0 8px 0;">
-					<MkSwitch :value="selectedPlugin.active" @update:value="changeActive(selectedPlugin, $event)">{{ $t('makeActive') }}</MkSwitch>
+					<MkSwitch :value="selectedPlugin.active" @update:value="changeActive(selectedPlugin, $event)">{{ $ts.makeActive }}</MkSwitch>
 				</div>
 				<div class="_keyValue">
-					<div>{{ $t('version') }}:</div>
+					<div>{{ $ts.version }}:</div>
 					<div>{{ selectedPlugin.version }}</div>
 				</div>
 				<div class="_keyValue">
-					<div>{{ $t('author') }}:</div>
+					<div>{{ $ts.author }}:</div>
 					<div>{{ selectedPlugin.author }}</div>
 				</div>
 				<div class="_keyValue">
-					<div>{{ $t('description') }}:</div>
+					<div>{{ $ts.description }}:</div>
 					<div>{{ selectedPlugin.description }}</div>
 				</div>
 				<div style="margin-top: 8px;">
-					<MkButton @click="config()" inline v-if="selectedPlugin.config"><Fa :icon="faCog"/> {{ $t('settings') }}</MkButton>
-					<MkButton @click="uninstall()" inline><Fa :icon="faTrashAlt"/> {{ $t('uninstall') }}</MkButton>
+					<MkButton @click="config()" inline v-if="selectedPlugin.config"><Fa :icon="faCog"/> {{ $ts.settings }}</MkButton>
+					<MkButton @click="uninstall()" inline><Fa :icon="faTrashAlt"/> {{ $ts.uninstall }}</MkButton>
 				</div>
 			</template>
 		</details>
@@ -132,8 +132,8 @@ export default defineComponent({
 
 			const token = permissions == null || permissions.length === 0 ? null : await new Promise((res, rej) => {
 				os.popup(import('@/components/token-generate-window.vue'), {
-					title: this.$t('tokenRequested'),
-					information: this.$t('pluginTokenRequestedDescription'),
+					title: this.$ts.tokenRequested,
+					information: this.$ts.pluginTokenRequestedDescription,
 					initialName: name,
 					initialPermissions: permissions
 				}, {
diff --git a/src/client/pages/settings/privacy.vue b/src/client/pages/settings/privacy.vue
index 6ac87ee64..9c5fb0850 100644
--- a/src/client/pages/settings/privacy.vue
+++ b/src/client/pages/settings/privacy.vue
@@ -1,28 +1,28 @@
 <template>
 <FormBase>
 	<FormGroup>
-		<FormSwitch v-model:value="isLocked" @update:value="save()">{{ $t('makeFollowManuallyApprove') }}</FormSwitch>
-		<FormSwitch v-model:value="autoAcceptFollowed" :disabled="!isLocked" @update:value="save()">{{ $t('autoAcceptFollowed') }}</FormSwitch>
-		<template #caption>{{ $t('lockedAccountInfo') }}</template>
+		<FormSwitch v-model:value="isLocked" @update:value="save()">{{ $ts.makeFollowManuallyApprove }}</FormSwitch>
+		<FormSwitch v-model:value="autoAcceptFollowed" :disabled="!isLocked" @update:value="save()">{{ $ts.autoAcceptFollowed }}</FormSwitch>
+		<template #caption>{{ $ts.lockedAccountInfo }}</template>
 	</FormGroup>
 	<FormSwitch v-model:value="noCrawle" @update:value="save()">
-		{{ $t('noCrawle') }}
-		<template #desc>{{ $t('noCrawleDescription') }}</template>
+		{{ $ts.noCrawle }}
+		<template #desc>{{ $ts.noCrawleDescription }}</template>
 	</FormSwitch>
 	<FormSwitch v-model:value="isExplorable" @update:value="save()">
-		{{ $t('makeExplorable') }}
-		<template #desc>{{ $t('makeExplorableDescription') }}</template>
+		{{ $ts.makeExplorable }}
+		<template #desc>{{ $ts.makeExplorableDescription }}</template>
 	</FormSwitch>
-	<FormSwitch v-model:value="rememberNoteVisibility" @update:value="save()">{{ $t('rememberNoteVisibility') }}</FormSwitch>
+	<FormSwitch v-model:value="rememberNoteVisibility" @update:value="save()">{{ $ts.rememberNoteVisibility }}</FormSwitch>
 	<FormGroup v-if="!rememberNoteVisibility">
-		<template #label>{{ $t('defaultNoteVisibility') }}</template>
+		<template #label>{{ $ts.defaultNoteVisibility }}</template>
 		<FormSelect v-model:value="defaultNoteVisibility">
-			<option value="public">{{ $t('_visibility.public') }}</option>
-			<option value="home">{{ $t('_visibility.home') }}</option>
-			<option value="followers">{{ $t('_visibility.followers') }}</option>
-			<option value="specified">{{ $t('_visibility.specified') }}</option>
+			<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>
 		</FormSelect>
-		<FormSwitch v-model:value="defaultNoteLocalOnly">{{ $t('_visibility.localOnly') }}</FormSwitch>
+		<FormSwitch v-model:value="defaultNoteLocalOnly">{{ $ts._visibility.localOnly }}</FormSwitch>
 	</FormGroup>
 </FormBase>
 </template>
@@ -50,7 +50,7 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: {
-				title: this.$t('privacy'),
+				title: this.$ts.privacy,
 				icon: faLockOpen
 			},
 			isLocked: false,
diff --git a/src/client/pages/settings/profile.vue b/src/client/pages/settings/profile.vue
index a471af9c0..8c2c63e31 100644
--- a/src/client/pages/settings/profile.vue
+++ b/src/client/pages/settings/profile.vue
@@ -4,41 +4,41 @@
 		<div class="_formItem _formPanel llvierxe" :style="{ backgroundImage: $i.bannerUrl ? `url(${ $i.bannerUrl })` : null }">
 			<MkAvatar class="avatar" :user="$i"/>
 		</div>
-		<FormButton @click="changeAvatar" primary>{{ $t('_profile.changeAvatar') }}</FormButton>
-		<FormButton @click="changeBanner" primary>{{ $t('_profile.changeBanner') }}</FormButton>
+		<FormButton @click="changeAvatar" primary>{{ $ts._profile.changeAvatar }}</FormButton>
+		<FormButton @click="changeBanner" primary>{{ $ts._profile.changeBanner }}</FormButton>
 	</FormGroup>
 
 	<FormInput v-model:value="name" :max="30">
-		<span>{{ $t('_profile.name') }}</span>
+		<span>{{ $ts._profile.name }}</span>
 	</FormInput>
 
 	<FormTextarea v-model:value="description" :max="500">
-		<span>{{ $t('_profile.description') }}</span>
-		<template #desc>{{ $t('_profile.youCanIncludeHashtags') }}</template>
+		<span>{{ $ts._profile.description }}</span>
+		<template #desc>{{ $ts._profile.youCanIncludeHashtags }}</template>
 	</FormTextarea>
 
 	<FormInput v-model:value="location">
-		<span>{{ $t('location') }}</span>
+		<span>{{ $ts.location }}</span>
 		<template #prefix><Fa :icon="faMapMarkerAlt"/></template>
 	</FormInput>
 
 	<FormInput v-model:value="birthday" type="date">
-		<span>{{ $t('birthday') }}</span>
+		<span>{{ $ts.birthday }}</span>
 		<template #prefix><Fa :icon="faBirthdayCake"/></template>
 	</FormInput>
 
 	<FormGroup>
-		<FormButton @click="editMetadata" primary>{{ $t('_profile.metadataEdit') }}</FormButton>
-		<template #caption>{{ $t('_profile.metadataDescription') }}</template>
+		<FormButton @click="editMetadata" primary>{{ $ts._profile.metadataEdit }}</FormButton>
+		<template #caption>{{ $ts._profile.metadataDescription }}</template>
 	</FormGroup>
 
-	<FormSwitch v-model:value="isCat">{{ $t('flagAsCat') }}<template #desc>{{ $t('flagAsCatDescription') }}</template></FormSwitch>
+	<FormSwitch v-model:value="isCat">{{ $ts.flagAsCat }}<template #desc>{{ $ts.flagAsCatDescription }}</template></FormSwitch>
 
-	<FormSwitch v-model:value="isBot">{{ $t('flagAsBot') }}<template #desc>{{ $t('flagAsBotDescription') }}</template></FormSwitch>
+	<FormSwitch v-model:value="isBot">{{ $ts.flagAsBot }}<template #desc>{{ $ts.flagAsBotDescription }}</template></FormSwitch>
 
-	<FormSwitch v-model:value="alwaysMarkNsfw">{{ $t('alwaysMarkSensitive') }}</FormSwitch>
+	<FormSwitch v-model:value="alwaysMarkNsfw">{{ $ts.alwaysMarkSensitive }}</FormSwitch>
 
-	<FormButton @click="save(true)" primary><Fa :icon="faSave"/> {{ $t('save') }}</FormButton>
+	<FormButton @click="save(true)" primary><Fa :icon="faSave"/> {{ $ts.save }}</FormButton>
 </FormBase>
 </template>
 
@@ -73,7 +73,7 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: {
-				title: this.$t('profile'),
+				title: this.$ts.profile,
 				icon: faUser
 			},
 			host,
@@ -126,7 +126,7 @@ export default defineComponent({
 
 	methods: {
 		changeAvatar(e) {
-			selectFile(e.currentTarget || e.target, this.$t('avatar')).then(file => {
+			selectFile(e.currentTarget || e.target, this.$ts.avatar).then(file => {
 				os.api('i/update', {
 					avatarId: file.id,
 				});
@@ -134,7 +134,7 @@ export default defineComponent({
 		},
 
 		changeBanner(e) {
-			selectFile(e.currentTarget || e.target, this.$t('banner')).then(file => {
+			selectFile(e.currentTarget || e.target, this.$ts.banner).then(file => {
 				os.api('i/update', {
 					bannerId: file.id,
 				});
@@ -142,45 +142,45 @@ export default defineComponent({
 		},
 
 		async editMetadata() {
-			const { canceled, result } = await os.form(this.$t('_profile.metadata'), {
+			const { canceled, result } = await os.form(this.$ts._profile.metadata, {
 				fieldName0: {
 					type: 'string',
-					label: this.$t('_profile.metadataLabel') + ' 1',
+					label: this.$ts._profile.metadataLabel + ' 1',
 					default: this.fieldName0,
 				},
 				fieldValue0: {
 					type: 'string',
-					label: this.$t('_profile.metadataContent') + ' 1',
+					label: this.$ts._profile.metadataContent + ' 1',
 					default: this.fieldValue0,
 				},
 				fieldName1: {
 					type: 'string',
-					label: this.$t('_profile.metadataLabel') + ' 2',
+					label: this.$ts._profile.metadataLabel + ' 2',
 					default: this.fieldName1,
 				},
 				fieldValue1: {
 					type: 'string',
-					label: this.$t('_profile.metadataContent') + ' 2',
+					label: this.$ts._profile.metadataContent + ' 2',
 					default: this.fieldValue1,
 				},
 				fieldName2: {
 					type: 'string',
-					label: this.$t('_profile.metadataLabel') + ' 3',
+					label: this.$ts._profile.metadataLabel + ' 3',
 					default: this.fieldName2,
 				},
 				fieldValue2: {
 					type: 'string',
-					label: this.$t('_profile.metadataContent') + ' 3',
+					label: this.$ts._profile.metadataContent + ' 3',
 					default: this.fieldValue2,
 				},
 				fieldName3: {
 					type: 'string',
-					label: this.$t('_profile.metadataLabel') + ' 4',
+					label: this.$ts._profile.metadataLabel + ' 4',
 					default: this.fieldName3,
 				},
 				fieldValue3: {
 					type: 'string',
-					label: this.$t('_profile.metadataContent') + ' 4',
+					label: this.$ts._profile.metadataContent + ' 4',
 					default: this.fieldValue3,
 				},
 			});
diff --git a/src/client/pages/settings/reaction.vue b/src/client/pages/settings/reaction.vue
index 6f1a5c954..798ffb33d 100644
--- a/src/client/pages/settings/reaction.vue
+++ b/src/client/pages/settings/reaction.vue
@@ -1,7 +1,7 @@
 <template>
 <FormBase>
 	<div class="_formItem">
-		<div class="_formLabel">{{ $t('reactionSettingDescription') }}</div>
+		<div class="_formLabel">{{ $ts.reactionSettingDescription }}</div>
 		<div class="_formPanel">
 			<XDraggable class="zoaiodol" v-model="reactions" :item-key="item => item" animation="150" delay="100" delay-on-touch-only="true">
 				<template #item="{element}">
@@ -14,23 +14,23 @@
 				</template>
 			</XDraggable>
 		</div>
-		<div class="_formCaption">{{ $t('reactionSettingDescription2') }} <button class="_textButton" @click="preview">{{ $t('preview') }}</button></div>
+		<div class="_formCaption">{{ $ts.reactionSettingDescription2 }} <button class="_textButton" @click="preview">{{ $ts.preview }}</button></div>
 	</div>
 
 	<FormRadios v-model="reactionPickerWidth">
-		<template #desc>{{ $t('width') }}</template>
-		<option :value="1">{{ $t('small') }}</option>
-		<option :value="2">{{ $t('medium') }}</option>
-		<option :value="3">{{ $t('large') }}</option>
+		<template #desc>{{ $ts.width }}</template>
+		<option :value="1">{{ $ts.small }}</option>
+		<option :value="2">{{ $ts.medium }}</option>
+		<option :value="3">{{ $ts.large }}</option>
 	</FormRadios>
 	<FormRadios v-model="reactionPickerHeight">
-		<template #desc>{{ $t('height') }}</template>
-		<option :value="1">{{ $t('small') }}</option>
-		<option :value="2">{{ $t('medium') }}</option>
-		<option :value="3">{{ $t('large') }}</option>
+		<template #desc>{{ $ts.height }}</template>
+		<option :value="1">{{ $ts.small }}</option>
+		<option :value="2">{{ $ts.medium }}</option>
+		<option :value="3">{{ $ts.large }}</option>
 	</FormRadios>
-	<FormButton @click="preview"><Fa :icon="faEye"/> {{ $t('preview') }}</FormButton>
-	<FormButton danger @click="setDefault"><Fa :icon="faUndo"/> {{ $t('default') }}</FormButton>
+	<FormButton @click="preview"><Fa :icon="faEye"/> {{ $ts.preview }}</FormButton>
+	<FormButton danger @click="setDefault"><Fa :icon="faUndo"/> {{ $ts.default }}</FormButton>
 </FormBase>
 </template>
 
@@ -60,7 +60,7 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: {
-				title: this.$t('reaction'),
+				title: this.$ts.reaction,
 				icon: faLaugh,
 				action: {
 					icon: faEye,
@@ -97,7 +97,7 @@ export default defineComponent({
 
 		remove(reaction, ev) {
 			os.modalMenu([{
-				text: this.$t('remove'),
+				text: this.$ts.remove,
 				action: () => {
 					this.reactions = this.reactions.filter(x => x !== reaction)
 				}
@@ -114,7 +114,7 @@ export default defineComponent({
 		async setDefault() {
 			const { canceled } = await os.dialog({
 				type: 'warning',
-				text: this.$t('resetAreYouSure'),
+				text: this.$ts.resetAreYouSure,
 				showCancelButton: true
 			});
 			if (canceled) return;
diff --git a/src/client/pages/settings/security.vue b/src/client/pages/settings/security.vue
index 4d4a5edbf..cdb5705c0 100644
--- a/src/client/pages/settings/security.vue
+++ b/src/client/pages/settings/security.vue
@@ -1,10 +1,10 @@
 <template>
 <FormBase>
 	<X2fa/>
-	<FormLink to="/settings/2fa"><template #icon><Fa :icon="faMobileAlt"/></template>{{ $t('twoStepAuthentication') }}</FormLink>
-	<FormButton primary @click="change()">{{ $t('changePassword') }}</FormButton>
+	<FormLink to="/settings/2fa"><template #icon><Fa :icon="faMobileAlt"/></template>{{ $ts.twoStepAuthentication }}</FormLink>
+	<FormButton primary @click="change()">{{ $ts.changePassword }}</FormButton>
 	<FormPagination :pagination="pagination">
-		<template #label>{{ $t('signinHistory') }}</template>
+		<template #label>{{ $ts.signinHistory }}</template>
 		<template #default="{items}">
 			<div class="_formPanel timnmucd" v-for="item in items" :key="item.id">
 				<header>
@@ -17,8 +17,8 @@
 		</template>
 	</FormPagination>
 	<FormGroup>
-		<FormButton danger @click="regenerateToken"><Fa :icon="faSyncAlt"/> {{ $t('regenerateLoginToken') }}</FormButton>
-		<template #caption>{{ $t('regenerateLoginTokenDescription') }}</template>
+		<FormButton danger @click="regenerateToken"><Fa :icon="faSyncAlt"/> {{ $ts.regenerateLoginToken }}</FormButton>
+		<template #caption>{{ $ts.regenerateLoginTokenDescription }}</template>
 	</FormGroup>
 </FormBase>
 </template>
@@ -47,7 +47,7 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: {
-				title: this.$t('security'),
+				title: this.$ts.security,
 				icon: faLock
 			},
 			pagination: {
@@ -65,7 +65,7 @@ export default defineComponent({
 	methods: {
 		async change() {
 			const { canceled: canceled1, result: currentPassword } = await os.dialog({
-				title: this.$t('currentPassword'),
+				title: this.$ts.currentPassword,
 				input: {
 					type: 'password'
 				}
@@ -73,7 +73,7 @@ export default defineComponent({
 			if (canceled1) return;
 
 			const { canceled: canceled2, result: newPassword } = await os.dialog({
-				title: this.$t('newPassword'),
+				title: this.$ts.newPassword,
 				input: {
 					type: 'password'
 				}
@@ -81,7 +81,7 @@ export default defineComponent({
 			if (canceled2) return;
 
 			const { canceled: canceled3, result: newPassword2 } = await os.dialog({
-				title: this.$t('newPasswordRetype'),
+				title: this.$ts.newPasswordRetype,
 				input: {
 					type: 'password'
 				}
@@ -91,7 +91,7 @@ export default defineComponent({
 			if (newPassword !== newPassword2) {
 				os.dialog({
 					type: 'error',
-					text: this.$t('retypedNotMatch')
+					text: this.$ts.retypedNotMatch
 				});
 				return;
 			}
@@ -104,7 +104,7 @@ export default defineComponent({
 
 		regenerateToken() {
 			os.dialog({
-				title: this.$t('password'),
+				title: this.$ts.password,
 				input: {
 					type: 'password'
 				}
diff --git a/src/client/pages/settings/sidebar.vue b/src/client/pages/settings/sidebar.vue
index d835f57b5..7fba1b663 100644
--- a/src/client/pages/settings/sidebar.vue
+++ b/src/client/pages/settings/sidebar.vue
@@ -1,19 +1,19 @@
 <template>
 <FormBase>
 	<FormTextarea v-model:value="items" tall>
-		<span>{{ $t('sidebar') }}</span>
-		<template #desc><button class="_textButton" @click="addItem">{{ $t('addItem') }}</button></template>
+		<span>{{ $ts.sidebar }}</span>
+		<template #desc><button class="_textButton" @click="addItem">{{ $ts.addItem }}</button></template>
 	</FormTextarea>
 
 	<FormRadios v-model="sidebarDisplay">
-		<template #desc>{{ $t('display') }}</template>
-		<option value="full">{{ $t('_sidebar.full') }}</option>
-		<option value="icon">{{ $t('_sidebar.icon') }}</option>
-		<!-- <MkRadio v-model="sidebarDisplay" value="hide" disabled>{{ $t('_sidebar.hide') }}</MkRadio>--> <!-- TODO: サイドバーを完全に隠せるようにすると、別途ハンバーガーボタンのようなものをUIに表示する必要があり面倒 -->
+		<template #desc>{{ $ts.display }}</template>
+		<option value="full">{{ $ts._sidebar.full }}</option>
+		<option value="icon">{{ $ts._sidebar.icon }}</option>
+		<!-- <MkRadio v-model="sidebarDisplay" value="hide" disabled>{{ $ts._sidebar.hide }}</MkRadio>--> <!-- TODO: サイドバーを完全に隠せるようにすると、別途ハンバーガーボタンのようなものをUIに表示する必要があり面倒 -->
 	</FormRadios>
 
-	<FormButton @click="save()" primary><Fa :icon="faSave"/> {{ $t('save') }}</FormButton>
-	<FormButton @click="reset()" danger><Fa :icon="faRedo"/> {{ $t('default') }}</FormButton>
+	<FormButton @click="save()" primary><Fa :icon="faSave"/> {{ $ts.save }}</FormButton>
+	<FormButton @click="reset()" danger><Fa :icon="faRedo"/> {{ $ts.default }}</FormButton>
 </FormBase>
 </template>
 
@@ -43,7 +43,7 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: {
-				title: this.$t('sidebar'),
+				title: this.$ts.sidebar,
 				icon: faListUl
 			},
 			menuDef: sidebarDef,
@@ -73,12 +73,12 @@ export default defineComponent({
 			const menu = Object.keys(this.menuDef).filter(k => !this.$store.state.menu.includes(k));
 			const { canceled, result: item } = await os.dialog({
 				type: null,
-				title: this.$t('addItem'),
+				title: this.$ts.addItem,
 				select: {
 					items: [...menu.map(k => ({
 						value: k, text: this.$t(this.menuDef[k].title)
 					})), ...[{
-						value: '-', text: this.$t('divider')
+						value: '-', text: this.$ts.divider
 					}]]
 				},
 				showCancelButton: true
diff --git a/src/client/pages/settings/sounds.vue b/src/client/pages/settings/sounds.vue
index d254dc4cf..c53968da4 100644
--- a/src/client/pages/settings/sounds.vue
+++ b/src/client/pages/settings/sounds.vue
@@ -1,19 +1,19 @@
 <template>
 <FormBase>
 	<FormRange v-model:value="masterVolume" :min="0" :max="1" :step="0.05">
-		<template #label><Fa :icon="volumeIcon" :key="volumeIcon"/> {{ $t('masterVolume') }}</template>
+		<template #label><Fa :icon="volumeIcon" :key="volumeIcon"/> {{ $ts.masterVolume }}</template>
 	</FormRange>
 
 	<FormGroup>
-		<template #label>{{ $t('sounds') }}</template>
+		<template #label>{{ $ts.sounds }}</template>
 		<FormButton v-for="type in Object.keys(sounds)" :key="type" :center="false" @click="edit(type)">
 			{{ $t('_sfx.' + type) }}
-			<template #suffix>{{ sounds[type].type || $t('none') }}</template>
+			<template #suffix>{{ sounds[type].type || $ts.none }}</template>
 			<template #suffixIcon><Fa :icon="faChevronDown"/></template>
 		</FormButton>
 	</FormGroup>
 
-	<FormButton @click="reset()" danger><Fa :icon="faRedo"/> {{ $t('default') }}</FormButton>
+	<FormButton @click="reset()" danger><Fa :icon="faRedo"/> {{ $ts.default }}</FormButton>
 </FormBase>
 </template>
 
@@ -69,7 +69,7 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: {
-				title: this.$t('sounds'),
+				title: this.$ts.sounds,
 				icon: faMusic
 			},
 			sounds: {},
@@ -110,9 +110,9 @@ export default defineComponent({
 					type: 'enum',
 					enum: soundsTypes.map(x => ({
 						value: x,
-						label: x == null ? this.$t('none') : x,
+						label: x == null ? this.$ts.none : x,
 					})),
-					label: this.$t('sound'),
+					label: this.$ts.sound,
 					default: this.sounds[type].type,
 				},
 				volume: {
@@ -120,12 +120,12 @@ export default defineComponent({
 					mim: 0,
 					max: 1,
 					step: 0.05,
-					label: this.$t('volume'),
+					label: this.$ts.volume,
 					default: this.sounds[type].volume
 				},
 				listen: {
 					type: 'button',
-					content: this.$t('listen'),
+					content: this.$ts.listen,
 					action: (_, values) => {
 						playFile(values.type, values.volume);
 					}
diff --git a/src/client/pages/settings/theme.install.vue b/src/client/pages/settings/theme.install.vue
index 4ec976422..407b75537 100644
--- a/src/client/pages/settings/theme.install.vue
+++ b/src/client/pages/settings/theme.install.vue
@@ -2,12 +2,12 @@
 <FormBase>
 	<FormGroup>
 		<FormTextarea v-model:value="installThemeCode">
-			<span>{{ $t('_theme.code') }}</span>
+			<span>{{ $ts._theme.code }}</span>
 		</FormTextarea>
-		<FormButton @click="() => preview(installThemeCode)" :disabled="installThemeCode == null" inline><Fa :icon="faEye"/> {{ $t('preview') }}</FormButton>
+		<FormButton @click="() => preview(installThemeCode)" :disabled="installThemeCode == null" inline><Fa :icon="faEye"/> {{ $ts.preview }}</FormButton>
 	</FormGroup>
 
-	<FormButton @click="() => install(installThemeCode)" :disabled="installThemeCode == null" primary inline><Fa :icon="faCheck"/> {{ $t('install') }}</FormButton>
+	<FormButton @click="() => install(installThemeCode)" :disabled="installThemeCode == null" primary inline><Fa :icon="faCheck"/> {{ $ts.install }}</FormButton>
 </FormBase>
 </template>
 
@@ -42,7 +42,7 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: {
-				title: this.$t('_theme.install'),
+				title: this.$ts._theme.install,
 				icon: faDownload
 			},
 			installThemeCode: null,
@@ -63,21 +63,21 @@ export default defineComponent({
 			} catch (e) {
 				os.dialog({
 					type: 'error',
-					text: this.$t('_theme.invalid')
+					text: this.$ts._theme.invalid
 				});
 				return false;
 			}
 			if (!validateTheme(theme)) {
 				os.dialog({
 					type: 'error',
-					text: this.$t('_theme.invalid')
+					text: this.$ts._theme.invalid
 				});
 				return false;
 			}
 			if (ColdDeviceStorage.get('themes').some(t => t.id === theme.id)) {
 				os.dialog({
 					type: 'info',
-					text: this.$t('_theme.alreadyInstalled')
+					text: this.$ts._theme.alreadyInstalled
 				});
 				return false;
 			}
diff --git a/src/client/pages/settings/theme.manage.vue b/src/client/pages/settings/theme.manage.vue
index 9fa93aac5..6e3bd59a0 100644
--- a/src/client/pages/settings/theme.manage.vue
+++ b/src/client/pages/settings/theme.manage.vue
@@ -1,21 +1,21 @@
 <template>
 <FormBase>
 	<FormSelect v-model:value="selectedThemeId">
-		<template #label>{{ $t('installedThemes') }}</template>
+		<template #label>{{ $ts.installedThemes }}</template>
 		<option v-for="x in installedThemes" :value="x.id" :key="x.id">{{ x.name }}</option>
-		<optgroup :label="$t('builtinThemes')">
+		<optgroup :label="$ts.builtinThemes">
 			<option v-for="x in builtinThemes" :value="x.id" :key="x.id">{{ x.name }}</option>
 		</optgroup>
 	</FormSelect>
 	<template v-if="selectedTheme">
 		<FormInput readonly :value="selectedTheme.author">
-			<span>{{ $t('author') }}</span>
+			<span>{{ $ts.author }}</span>
 		</FormInput>
 		<FormTextarea readonly tall :value="selectedThemeCode">
-			<span>{{ $t('_theme.code') }}</span>
-			<template #desc><button @click="copyThemeCode()" class="_textButton">{{ $t('copy') }}</button></template>
+			<span>{{ $ts._theme.code }}</span>
+			<template #desc><button @click="copyThemeCode()" class="_textButton">{{ $ts.copy }}</button></template>
 		</FormTextarea>
-		<FormButton @click="uninstall()" danger v-if="!builtinThemes.some(t => t.id == selectedTheme.id)"><Fa :icon="faTrashAlt"/> {{ $t('uninstall') }}</FormButton>
+		<FormButton @click="uninstall()" danger v-if="!builtinThemes.some(t => t.id == selectedTheme.id)"><Fa :icon="faTrashAlt"/> {{ $ts.uninstall }}</FormButton>
 	</template>
 </FormBase>
 </template>
@@ -52,7 +52,7 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: {
-				title: this.$t('_theme.manage'),
+				title: this.$ts._theme.manage,
 				icon: faFolderOpen
 			},
 			installedThemes: ColdDeviceStorage.ref('themes'),
diff --git a/src/client/pages/settings/theme.vue b/src/client/pages/settings/theme.vue
index 1536d0cc0..e98a9f77b 100644
--- a/src/client/pages/settings/theme.vue
+++ b/src/client/pages/settings/theme.vue
@@ -1,20 +1,20 @@
 <template>
 <FormBase>
 	<FormSelect v-model:value="lightTheme" v-if="!darkMode">
-		<template #label>{{ $t('themeForLightMode') }}</template>
-		<optgroup :label="$t('lightThemes')">
+		<template #label>{{ $ts.themeForLightMode }}</template>
+		<optgroup :label="$ts.lightThemes">
 			<option v-for="x in lightThemes" :value="x.id" :key="x.id">{{ x.name }}</option>
 		</optgroup>
-		<optgroup :label="$t('darkThemes')">
+		<optgroup :label="$ts.darkThemes">
 			<option v-for="x in darkThemes" :value="x.id" :key="x.id">{{ x.name }}</option>
 		</optgroup>
 	</FormSelect>
 	<FormSelect v-model:value="darkTheme" v-else>
-		<template #label>{{ $t('themeForDarkMode') }}</template>
-		<optgroup :label="$t('darkThemes')">
+		<template #label>{{ $ts.themeForDarkMode }}</template>
+		<optgroup :label="$ts.darkThemes">
 			<option v-for="x in darkThemes" :value="x.id" :key="x.id">{{ x.name }}</option>
 		</optgroup>
-		<optgroup :label="$t('lightThemes')">
+		<optgroup :label="$ts.lightThemes">
 			<option v-for="x in lightThemes" :value="x.id" :key="x.id">{{ x.name }}</option>
 		</optgroup>
 	</FormSelect>
@@ -25,8 +25,8 @@
 				<div class="toggleWrapper">
 					<input type="checkbox" class="dn" id="dn" v-model="darkMode" :disabled="syncDeviceDarkMode"/>
 					<label for="dn" class="toggle">
-						<span class="before">{{ $t('light') }}</span>
-						<span class="after">{{ $t('dark') }}</span>
+						<span class="before">{{ $ts.light }}</span>
+						<span class="after">{{ $ts.dark }}</span>
 						<span class="toggle__handler">
 							<span class="crater crater--1"></span>
 							<span class="crater crater--2"></span>
@@ -42,20 +42,20 @@
 				</div>
 			</div>
 		</div>
-		<FormSwitch v-model:value="syncDeviceDarkMode">{{ $t('syncDeviceDarkMode') }}</FormSwitch>
+		<FormSwitch v-model:value="syncDeviceDarkMode">{{ $ts.syncDeviceDarkMode }}</FormSwitch>
 	</FormGroup>
 
-	<FormButton primary v-if="wallpaper == null" @click="setWallpaper">{{ $t('setWallpaper') }}</FormButton>
-	<FormButton primary v-else @click="wallpaper = null">{{ $t('removeWallpaper') }}</FormButton>
+	<FormButton primary v-if="wallpaper == null" @click="setWallpaper">{{ $ts.setWallpaper }}</FormButton>
+	<FormButton primary v-else @click="wallpaper = null">{{ $ts.removeWallpaper }}</FormButton>
 
 	<FormGroup>
-		<FormLink to="https://assets.msky.cafe/theme/list" external>{{ $t('_theme.explore') }}</FormLink>
-		<FormLink to="/theme-editor">{{ $t('_theme.make') }}</FormLink>
+		<FormLink to="https://assets.msky.cafe/theme/list" external>{{ $ts._theme.explore }}</FormLink>
+		<FormLink to="/theme-editor">{{ $ts._theme.make }}</FormLink>
 	</FormGroup>
 
-	<FormLink to="/settings/theme/install"><template #icon><Fa :icon="faDownload"/></template>{{ $t('_theme.install') }}</FormLink>
+	<FormLink to="/settings/theme/install"><template #icon><Fa :icon="faDownload"/></template>{{ $ts._theme.install }}</FormLink>
 
-	<FormLink to="/settings/theme/manage"><template #icon><Fa :icon="faFolderOpen"/></template>{{ $t('_theme.manage') }}</FormLink>
+	<FormLink to="/settings/theme/manage"><template #icon><Fa :icon="faFolderOpen"/></template>{{ $ts._theme.manage }}</FormLink>
 </FormBase>
 </template>
 
diff --git a/src/client/pages/settings/word-mute.vue b/src/client/pages/settings/word-mute.vue
index f02f9bf2f..38dbc2c62 100644
--- a/src/client/pages/settings/word-mute.vue
+++ b/src/client/pages/settings/word-mute.vue
@@ -1,31 +1,31 @@
 <template>
 <div>
 	<MkTab v-model:value="tab">
-		<option value="soft">{{ $t('_wordMute.soft') }}</option>
-		<option value="hard">{{ $t('_wordMute.hard') }}</option>
+		<option value="soft">{{ $ts._wordMute.soft }}</option>
+		<option value="hard">{{ $ts._wordMute.hard }}</option>
 	</MkTab>
 	<FormBase>
 		<div class="_formItem">
 			<div v-show="tab === 'soft'">
-				<MkInfo>{{ $t('_wordMute.softDescription') }}</MkInfo>
+				<MkInfo>{{ $ts._wordMute.softDescription }}</MkInfo>
 				<FormTextarea v-model:value="softMutedWords">
-					<span>{{ $t('_wordMute.muteWords') }}</span>
-					<template #desc>{{ $t('_wordMute.muteWordsDescription') }}<br>{{ $t('_wordMute.muteWordsDescription2') }}</template>
+					<span>{{ $ts._wordMute.muteWords }}</span>
+					<template #desc>{{ $ts._wordMute.muteWordsDescription }}<br>{{ $ts._wordMute.muteWordsDescription2 }}</template>
 				</FormTextarea>
 			</div>
 			<div v-show="tab === 'hard'">
-				<MkInfo>{{ $t('_wordMute.hardDescription') }}</MkInfo>
+				<MkInfo>{{ $ts._wordMute.hardDescription }}</MkInfo>
 				<FormTextarea v-model:value="hardMutedWords">
-					<span>{{ $t('_wordMute.muteWords') }}</span>
-					<template #desc>{{ $t('_wordMute.muteWordsDescription') }}<br>{{ $t('_wordMute.muteWordsDescription2') }}</template>
+					<span>{{ $ts._wordMute.muteWords }}</span>
+					<template #desc>{{ $ts._wordMute.muteWordsDescription }}<br>{{ $ts._wordMute.muteWordsDescription2 }}</template>
 				</FormTextarea>
 				<FormKeyValueView v-if="hardWordMutedNotesCount != null">
-					<template #key>{{ $t('_wordMute.mutedNotes') }}</template>
+					<template #key>{{ $ts._wordMute.mutedNotes }}</template>
 					<template #value>{{ number(hardWordMutedNotesCount) }}</template>
 				</FormKeyValueView>
 			</div>
 		</div>
-		<FormButton @click="save()" primary inline :disabled="!changed"><Fa :icon="faSave"/> {{ $t('save') }}</FormButton>
+		<FormButton @click="save()" primary inline :disabled="!changed"><Fa :icon="faSave"/> {{ $ts.save }}</FormButton>
 	</FormBase>
 </div>
 </template>
@@ -57,7 +57,7 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: {
-				title: this.$t('wordMute'),
+				title: this.$ts.wordMute,
 				icon: faCommentSlash
 			},
 			tab: 'soft',
diff --git a/src/client/pages/share.vue b/src/client/pages/share.vue
index d56307466..7912e5421 100644
--- a/src/client/pages/share.vue
+++ b/src/client/pages/share.vue
@@ -4,7 +4,7 @@
 		<div class="_title" v-if="title">{{ title }}</div>
 		<div class="_content">
 			<XPostForm v-if="!posted" fixed :instant="true" :initial-text="initialText" @posted="posted = true" class="_panel"/>
-			<MkButton v-else primary @click="close()">{{ $t('close') }}</MkButton>
+			<MkButton v-else primary @click="close()">{{ $ts.close }}</MkButton>
 		</div>
 		<div class="_footer" v-if="url">{{ url }}</div>
 	</section>
@@ -27,7 +27,7 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: {
-				title: this.$t('share'),
+				title: this.$ts.share,
 				icon: faShareAlt
 			},
 			title: null,
diff --git a/src/client/pages/theme-editor.vue b/src/client/pages/theme-editor.vue
index b2ce1b080..1f5e26037 100644
--- a/src/client/pages/theme-editor.vue
+++ b/src/client/pages/theme-editor.vue
@@ -3,24 +3,24 @@
 	<section class="_section">
 		<div class="_content">
 			<details>
-				<summary>{{ $t('import') }}</summary>
+				<summary>{{ $ts.import }}</summary>
 				<MkTextarea v-model:value="themeToImport">
-					{{ $t('_theme.importInfo') }}
+					{{ $ts._theme.importInfo }}
 				</MkTextarea>
-				<MkButton :disabled="!themeToImport.trim()" @click="importTheme">{{ $t('import') }}</MkButton>
+				<MkButton :disabled="!themeToImport.trim()" @click="importTheme">{{ $ts.import }}</MkButton>
 			</details>
 		</div>
 	</section>
 	<section class="_section">
 		<div class="_content _card _vMargin">
 			<div class="_content">
-				<MkInput v-model:value="name" required><span>{{ $t('name') }}</span></MkInput>
-				<MkInput v-model:value="author" required><span>{{ $t('author') }}</span></MkInput>
-				<MkTextarea v-model:value="description"><span>{{ $t('description') }}</span></MkTextarea>
+				<MkInput v-model:value="name" required><span>{{ $ts.name }}</span></MkInput>
+				<MkInput v-model:value="author" required><span>{{ $ts.author }}</span></MkInput>
+				<MkTextarea v-model:value="description"><span>{{ $ts.description }}</span></MkTextarea>
 				<div class="_inputs">
-					<div v-text="$t('_theme.base')" />
-					<MkRadio v-model="baseTheme" value="light">{{ $t('light') }}</MkRadio>
-					<MkRadio v-model="baseTheme" value="dark">{{ $t('dark') }}</MkRadio>
+					<div v-text="$ts._theme.base" />
+					<MkRadio v-model="baseTheme" value="light">{{ $ts.light }}</MkRadio>
+					<MkRadio v-model="baseTheme" value="dark">{{ $ts.dark }}</MkRadio>
 				</div>
 			</div>
 		</div>
@@ -29,8 +29,8 @@
 				<div class="item" v-for="([ k, v ], i) in theme" :key="k">
 					<div class="_inputs">
 						<div>
-							{{ k.startsWith('$') ? `${k} (${$t('_theme.constant')})` : $t('_theme.keys.' + k) }}
-							<button v-if="k.startsWith('$')" class="_button _link" @click="del(i)" v-text="$t('delete')" />
+							{{ k.startsWith('$') ? `${k} (${$ts._theme.constant})` : $t('_theme.keys.' + k) }}
+							<button v-if="k.startsWith('$')" class="_button _link" @click="del(i)" v-text="$ts.delete" />
 						</div>
 						<div>
 							<div class="type" @click="chooseType($event, i)">
@@ -46,7 +46,7 @@
 							<!-- ref const -->
 							<MkInput v-else-if="v.type === 'refConst'" v-model:value="v.key">
 								<template #prefix>$</template>
-								<span>{{ $t('name') }}</span>
+								<span>{{ $ts.name }}</span>
 							</MkInput>
 							<!-- ref props -->
 							<MkSelect class="select" v-else-if="v.type === 'refProp'" v-model:value="v.key">
@@ -55,12 +55,12 @@
 							<!-- func -->
 							<template v-else-if="v.type === 'func'">
 								<MkSelect class="select" v-model:value="v.name">
-									<template #label>{{ $t('_theme.funcKind') }}</template>
+									<template #label>{{ $ts._theme.funcKind }}</template>
 									<option v-for="n in ['alpha', 'darken', 'lighten']" :value="n" :key="n">{{ $t('_theme.' + n) }}</option>
 								</MkSelect>
-								<MkInput type="number" v-model:value="v.arg"><span>{{ $t('_theme.argument') }}</span></MkInput>
+								<MkInput type="number" v-model:value="v.arg"><span>{{ $ts._theme.argument }}</span></MkInput>
 								<MkSelect class="select" v-model:value="v.value">
-									<template #label>{{ $t('_theme.basedProp') }}</template>
+									<template #label>{{ $ts._theme.basedProp }}</template>
 									<option v-for="key in themeProps" :value="key" :key="key">{{ $t('_theme.keys.' + key) }}</option>
 								</MkSelect>
 							</template>
@@ -71,20 +71,20 @@
 						</div>
 					</div>
 				</div>
-				<MkButton primary @click="addConst">{{ $t('_theme.addConstant') }}</MkButton>
+				<MkButton primary @click="addConst">{{ $ts._theme.addConstant }}</MkButton>
 			</div>
 		</div>
 	</section>
 	<section class="_section">
 		<details class="_content">
-			<summary>{{ $t('sample') }}</summary>
+			<summary>{{ $ts.sample }}</summary>
 			<MkSample/>
 		</details>
 	</section>
 	<section class="_section">
 		<div class="_content">
-			<MkButton inline @click="preview">{{ $t('preview') }}</MkButton>
-			<MkButton inline primary :disabled="!name || !author" @click="save">{{ $t('save') }}</MkButton>
+			<MkButton inline @click="preview">{{ $ts.preview }}</MkButton>
+			<MkButton inline primary :disabled="!name || !author" @click="save">{{ $ts.save }}</MkButton>
 		</div>
 	</section>
 </div>
@@ -122,7 +122,7 @@ export default defineComponent({
 	data() {
 		return {
 			INFO: {
-				title: this.$t('themeEditor'),
+				title: this.$ts.themeEditor,
 				icon: faPalette,
 			},
 			theme: [] as ThemeViewModel,
@@ -177,7 +177,7 @@ export default defineComponent({
 		async confirm(): Promise<boolean> {
 			const { canceled } = await os.dialog({
 				type: 'warning',
-				text: this.$t('leaveConfirm'),
+				text: this.$ts.leaveConfirm,
 				showCancelButton: true
 			});
 			return !canceled;
@@ -203,7 +203,7 @@ export default defineComponent({
 	
 		async addConst() {
 			const { canceled, result } = await os.dialog({
-				title: this.$t('_theme.inputConstantName'),
+				title: this.$ts._theme.inputConstantName,
 				input: true
 			});
 			if (canceled) return;
@@ -238,7 +238,7 @@ export default defineComponent({
 
 			try {
 				const theme = JSON5.parse(this.themeToImport) as Theme;
-				if (!validateTheme(theme)) throw new Error(this.$t('_theme.invalid'));
+				if (!validateTheme(theme)) throw new Error(this.$ts._theme.invalid);
 
 				this.name = theme.name;
 				this.description = theme.desc || '';
@@ -260,9 +260,9 @@ export default defineComponent({
 
 		getTypeOf(v: ThemeValue) {
 			return v === null
-				? this.$t('_theme.defaultValue')
+				? this.$ts._theme.defaultValue
 				: typeof v === 'string'
-					? this.$t('_theme.color')
+					? this.$ts._theme.color
 					: this.$t('_theme.' + v.type);
 		},
 	
@@ -274,23 +274,23 @@ export default defineComponent({
 		showTypeMenu(e: MouseEvent) {
 			return new Promise<ThemeValue>((resolve) => {
 				os.modalMenu([{
-					text: this.$t('_theme.defaultValue'),
+					text: this.$ts._theme.defaultValue,
 					action: () => resolve(null),
 				}, {
-					text: this.$t('_theme.color'),
+					text: this.$ts._theme.color,
 					action: () => resolve('#000000'),
 				}, {
-					text: this.$t('_theme.func'),
+					text: this.$ts._theme.func,
 					action: () => resolve({
 						type: 'func', name: 'alpha', arg: 1, value: 'accent'
 					}),
 				}, {
-					text: this.$t('_theme.refProp'),
+					text: this.$ts._theme.refProp,
 					action: () => resolve({
 						type: 'refProp', key: 'accent',
 					}),
 				}, {
-					text: this.$t('_theme.refConst'),
+					text: this.$ts._theme.refConst,
 					action: () => resolve({
 						type: 'refConst', key: '',
 					}),
diff --git a/src/client/pages/timeline.tutorial.vue b/src/client/pages/timeline.tutorial.vue
index 60e71b097..203527ef0 100644
--- a/src/client/pages/timeline.tutorial.vue
+++ b/src/client/pages/timeline.tutorial.vue
@@ -1,52 +1,52 @@
 <template>
 <div class="_card tbkwesmv">
-	<div class="_title"><Fa :icon="faInfoCircle"/> {{ $t('_tutorial.title') }}</div>
+	<div class="_title"><Fa :icon="faInfoCircle"/> {{ $ts._tutorial.title }}</div>
 	<div class="_content" v-if="tutorial === 0">
-		<div>{{ $t('_tutorial.step1_1') }}</div>
-		<div>{{ $t('_tutorial.step1_2') }}</div>
-		<div>{{ $t('_tutorial.step1_3') }}</div>
+		<div>{{ $ts._tutorial.step1_1 }}</div>
+		<div>{{ $ts._tutorial.step1_2 }}</div>
+		<div>{{ $ts._tutorial.step1_3 }}</div>
 	</div>
 	<div class="_content" v-else-if="tutorial === 1">
-		<div>{{ $t('_tutorial.step2_1') }}</div>
-		<div>{{ $t('_tutorial.step2_2') }}</div>
-		<MkA class="_link" to="/settings/profile">{{ $t('editProfile') }}</MkA>
+		<div>{{ $ts._tutorial.step2_1 }}</div>
+		<div>{{ $ts._tutorial.step2_2 }}</div>
+		<MkA class="_link" to="/settings/profile">{{ $ts.editProfile }}</MkA>
 	</div>
 	<div class="_content" v-else-if="tutorial === 2">
-		<div>{{ $t('_tutorial.step3_1') }}</div>
-		<div>{{ $t('_tutorial.step3_2') }}</div>
-		<div>{{ $t('_tutorial.step3_3') }}</div>
-		<small>{{ $t('_tutorial.step3_4') }}</small>
+		<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>
 	<div class="_content" v-else-if="tutorial === 3">
-		<div>{{ $t('_tutorial.step4_1') }}</div>
-		<div>{{ $t('_tutorial.step4_2') }}</div>
+		<div>{{ $ts._tutorial.step4_1 }}</div>
+		<div>{{ $ts._tutorial.step4_2 }}</div>
 	</div>
 	<div class="_content" v-else-if="tutorial === 4">
-		<div>{{ $t('_tutorial.step5_1') }}</div>
-		<I18n src="_tutorial.step5_2" tag="div">
+		<div>{{ $ts._tutorial.step5_1 }}</div>
+		<I18n :src="$ts._tutorial.step5_2" tag="div">
 			<template #featured>
-				<MkA class="_link" to="/featured">{{ $t('featured') }}</MkA>
+				<MkA class="_link" to="/featured">{{ $ts.featured }}</MkA>
 			</template>
 			<template #explore>
-				<MkA class="_link" to="/explore">{{ $t('explore') }}</MkA>
+				<MkA class="_link" to="/explore">{{ $ts.explore }}</MkA>
 			</template>
 		</I18n>
-		<div>{{ $t('_tutorial.step5_3') }}</div>
-		<small>{{ $t('_tutorial.step5_4') }}</small>
+		<div>{{ $ts._tutorial.step5_3 }}</div>
+		<small>{{ $ts._tutorial.step5_4 }}</small>
 	</div>
 	<div class="_content" v-else-if="tutorial === 5">
-		<div>{{ $t('_tutorial.step6_1') }}</div>
-		<div>{{ $t('_tutorial.step6_2') }}</div>
-		<div>{{ $t('_tutorial.step6_3') }}</div>
+		<div>{{ $ts._tutorial.step6_1 }}</div>
+		<div>{{ $ts._tutorial.step6_2 }}</div>
+		<div>{{ $ts._tutorial.step6_3 }}</div>
 	</div>
 	<div class="_content" v-else-if="tutorial === 6">
-		<div>{{ $t('_tutorial.step7_1') }}</div>
-		<I18n src="_tutorial.step7_2" tag="div">
+		<div>{{ $ts._tutorial.step7_1 }}</div>
+		<I18n :src="$ts._tutorial.step7_2" tag="div">
 			<template #help>
-				<MkA class="_link" to="/docs">{{ $t('help') }}</MkA>
+				<MkA class="_link" to="/docs">{{ $ts.help }}</MkA>
 			</template>
 		</I18n>
-		<div>{{ $t('_tutorial.step7_3') }}</div>
+		<div>{{ $ts._tutorial.step7_3 }}</div>
 	</div>
 
 	<div class="_footer navigation">
@@ -59,8 +59,8 @@
 				<Fa :icon="faChevronRight"/>
 			</button>
 		</div>
-		<MkButton class="ok" @click="tutorial = -1" primary v-if="tutorial === 6"><Fa :icon="faCheck"/> {{ $t('gotIt') }}</MkButton>
-		<MkButton class="ok" @click="tutorial++" primary v-else><Fa :icon="faCheck"/> {{ $t('next') }}</MkButton>
+		<MkButton class="ok" @click="tutorial = -1" primary v-if="tutorial === 6"><Fa :icon="faCheck"/> {{ $ts.gotIt }}</MkButton>
+		<MkButton class="ok" @click="tutorial++" primary v-else><Fa :icon="faCheck"/> {{ $ts.next }}</MkButton>
 	</div>
 </div>
 </template>
diff --git a/src/client/pages/timeline.vue b/src/client/pages/timeline.vue
index e0309d46f..a5dd097b3 100644
--- a/src/client/pages/timeline.vue
+++ b/src/client/pages/timeline.vue
@@ -1,6 +1,6 @@
 <template>
 <div class="mk-home" v-hotkey.global="keymap">
-	<div class="new" v-if="queue > 0" :style="{ width: width + 'px' }"><button class="_buttonPrimary" @click="top()">{{ $t('newNoteRecived') }}</button></div>
+	<div class="new" v-if="queue > 0" :style="{ width: width + 'px' }"><button class="_buttonPrimary" @click="top()">{{ $ts.newNoteRecived }}</button></div>
 
 	<div class="_section">
 		<XTutorial v-if="$store.reactiveState.tutorial.value != -1" class="tutorial _content _vMargin"/>
@@ -53,7 +53,7 @@ export default defineComponent({
 				const tabs = [{
 					id: 'home',
 					title: null,
-					tooltip: this.$t('_timelines.home'),
+					tooltip: this.$ts._timelines.home,
 					icon: faHome,
 					onClick: () => { this.src = 'home'; this.saveSrc(); },
 					selected: computed(() => this.src === 'home')
@@ -63,7 +63,7 @@ export default defineComponent({
 					tabs.push({
 						id: 'local',
 						title: null,
-						tooltip: this.$t('_timelines.local'),
+						tooltip: this.$ts._timelines.local,
 						icon: faComments,
 						onClick: () => { this.src = 'local'; this.saveSrc(); },
 						selected: computed(() => this.src === 'local')
@@ -72,7 +72,7 @@ export default defineComponent({
 					tabs.push({
 						id: 'social',
 						title: null,
-						tooltip: this.$t('_timelines.social'),
+						tooltip: this.$ts._timelines.social,
 						icon: faShareAlt,
 						onClick: () => { this.src = 'social'; this.saveSrc(); },
 						selected: computed(() => this.src === 'social')
@@ -83,7 +83,7 @@ export default defineComponent({
 					tabs.push({
 						id: 'global',
 						title: null,
-						tooltip: this.$t('_timelines.global'),
+						tooltip: this.$ts._timelines.global,
 						icon: faGlobe,
 						onClick: () => { this.src = 'global'; this.saveSrc(); },
 						selected: computed(() => this.src === 'global')
diff --git a/src/client/pages/user/index.activity.vue b/src/client/pages/user/index.activity.vue
index 1f059146e..020d76c26 100644
--- a/src/client/pages/user/index.activity.vue
+++ b/src/client/pages/user/index.activity.vue
@@ -1,6 +1,6 @@
 <template>
 <MkContainer>
-	<template #header><Fa :icon="faChartBar" style="margin-right: 0.5em;"/>{{ $t('activity') }}</template>
+	<template #header><Fa :icon="faChartBar" style="margin-right: 0.5em;"/>{{ $ts.activity }}</template>
 
 	<div style="padding: 8px;">
 		<div ref="chart"></div>
diff --git a/src/client/pages/user/index.photos.vue b/src/client/pages/user/index.photos.vue
index 8cb72518b..d0c6f1934 100644
--- a/src/client/pages/user/index.photos.vue
+++ b/src/client/pages/user/index.photos.vue
@@ -1,6 +1,6 @@
 <template>
 <MkContainer>
-	<template #header><Fa :icon="faImage" style="margin-right: 0.5em;"/>{{ $t('images') }}</template>
+	<template #header><Fa :icon="faImage" style="margin-right: 0.5em;"/>{{ $ts.images }}</template>
 	<div class="ujigsodd">
 		<MkLoading v-if="fetching"/>
 		<div class="stream" v-if="!fetching && images.length > 0">
@@ -10,7 +10,7 @@
 				:to="notePage(image.note)"
 			></MkA>
 		</div>
-		<p class="empty" v-if="!fetching && images.length == 0">{{ $t('nothing') }}</p>
+		<p class="empty" v-if="!fetching && images.length == 0">{{ $ts.nothing }}</p>
 	</div>
 </MkContainer>
 </template>
diff --git a/src/client/pages/user/index.timeline.vue b/src/client/pages/user/index.timeline.vue
index cca5f9baa..f16297e21 100644
--- a/src/client/pages/user/index.timeline.vue
+++ b/src/client/pages/user/index.timeline.vue
@@ -1,9 +1,9 @@
 <template>
 <div>
 	<MkTab v-model:value="with_" class="_vMargin">
-		<option :value="null">{{ $t('notes') }}</option>
-		<option value="replies">{{ $t('notesAndReplies') }}</option>
-		<option value="files">{{ $t('withFiles') }}</option>
+		<option :value="null">{{ $ts.notes }}</option>
+		<option value="replies">{{ $ts.notesAndReplies }}</option>
+		<option value="files">{{ $ts.withFiles }}</option>
 	</MkTab>
 	<XNotes ref="timeline" class="_vMargin" :pagination="pagination" @before="$emit('before')" @after="e => $emit('after', e)"/>
 </div>
diff --git a/src/client/pages/user/index.vue b/src/client/pages/user/index.vue
index 2efc9b993..a1aad3cfa 100644
--- a/src/client/pages/user/index.vue
+++ b/src/client/pages/user/index.vue
@@ -13,36 +13,36 @@
 					<MkUserName :user="user" :nowrap="false" class="name"/>
 					<MkAcct :user="user" :detail="true" class="acct"/>
 				</div>
-				<div class="followed" v-if="$i && $i.id != user.id && user.isFollowed"><span>{{ $t('followsYou') }}</span></div>
+				<div class="followed" v-if="$i && $i.id != user.id && user.isFollowed"><span>{{ $ts.followsYou }}</span></div>
 				<div class="status">
 					<MkA :to="userPage(user)" :class="{ active: page === 'index' }">
 						<b>{{ number(user.notesCount) }}</b>
-						<span>{{ $t('notes') }}</span>
+						<span>{{ $ts.notes }}</span>
 					</MkA>
 					<MkA :to="userPage(user, 'following')" :class="{ active: page === 'following' }">
 						<b>{{ number(user.followingCount) }}</b>
-						<span>{{ $t('following') }}</span>
+						<span>{{ $ts.following }}</span>
 					</MkA>
 					<MkA :to="userPage(user, 'followers')" :class="{ active: page === 'followers' }">
 						<b>{{ number(user.followersCount) }}</b>
-						<span>{{ $t('followers') }}</span>
+						<span>{{ $ts.followers }}</span>
 					</MkA>
 				</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">{{ $t('noAccountDescription') }}</p>
+					<p v-else class="empty">{{ $ts.noAccountDescription }}</p>
 				</div>
 				<div class="fields system">
 					<dl class="field" v-if="user.location">
-						<dt class="name"><Fa :icon="faMapMarker" fixed-width/> {{ $t('location') }}</dt>
+						<dt class="name"><Fa :icon="faMapMarker" fixed-width/> {{ $ts.location }}</dt>
 						<dd class="value">{{ user.location }}</dd>
 					</dl>
 					<dl class="field" v-if="user.birthday">
-						<dt class="name"><Fa :icon="faBirthdayCake" fixed-width/> {{ $t('birthday') }}</dt>
+						<dt class="name"><Fa :icon="faBirthdayCake" fixed-width/> {{ $ts.birthday }}</dt>
 						<dd class="value">{{ user.birthday.replace('-', '/').replace('-', '/') }} ({{ $t('yearsOld', { age }) }})</dd>
 					</dl>
 					<dl class="field">
-						<dt class="name"><Fa :icon="faCalendarAlt" fixed-width/> {{ $t('registeredDate') }}</dt>
+						<dt class="name"><Fa :icon="faCalendarAlt" fixed-width/> {{ $ts.registeredDate }}</dt>
 						<dd class="value">{{ new Date(user.createdAt).toLocaleString() }} (<MkTime :time="user.createdAt"/>)</dd>
 					</dl>
 				</div>
@@ -63,15 +63,15 @@
 				<div class="nav _vMargin">
 					<MkA :to="userPage(user)" :class="{ active: page === 'index' }" class="link">
 						<Fa :icon="faCommentAlt" class="icon"/>
-						<span>{{ $t('notes') }}</span>
+						<span>{{ $ts.notes }}</span>
 					</MkA>
 					<MkA :to="userPage(user, 'clips')" :class="{ active: page === 'clips' }" class="link">
 						<Fa :icon="faPaperclip" class="icon"/>
-						<span>{{ $t('clips') }}</span>
+						<span>{{ $ts.clips }}</span>
 					</MkA>
 					<MkA :to="userPage(user, 'pages')" :class="{ active: page === 'pages' }" class="link">
 						<Fa :icon="faFileAlt" class="icon"/>
-						<span>{{ $t('pages') }}</span>
+						<span>{{ $ts.pages }}</span>
 					</MkA>
 					<div class="actions">
 						<button @click="menu" class="menu _button"><Fa :icon="faEllipsisH"/></button>
@@ -95,8 +95,8 @@
 	</div>
 	<div class="ftskorzw narrow _section" v-else-if="user && narrow === true" v-size="{ max: [500] }">
 		<!-- TODO -->
-		<!-- <div class="punished" v-if="user.isSuspended"><Fa :icon="faExclamationTriangle" style="margin-right: 8px;"/> {{ $t('userSuspended') }}</div> -->
-		<!-- <div class="punished" v-if="user.isSilenced"><Fa :icon="faExclamationTriangle" style="margin-right: 8px;"/> {{ $t('userSilenced') }}</div> -->
+		<!-- <div class="punished" v-if="user.isSuspended"><Fa :icon="faExclamationTriangle" style="margin-right: 8px;"/> {{ $ts.userSuspended }}</div> -->
+		<!-- <div class="punished" v-if="user.isSilenced"><Fa :icon="faExclamationTriangle" style="margin-right: 8px;"/> {{ $ts.userSilenced }}</div> -->
 
 		<div class="profile _content _vMargin">
 			<MkRemoteCaution v-if="user.host != null" :href="user.url" class="_vMargin"/>
@@ -109,13 +109,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="$t('isAdmin')" style="color: var(--badge);"><Fa :icon="faBookmark"/></span>
-							<span v-if="!user.isAdmin && user.isModerator" :title="$t('isModerator')" style="color: var(--badge);"><Fa :icon="farBookmark"/></span>
-							<span v-if="user.isLocked" :title="$t('isLocked')"><Fa :icon="faLock"/></span>
-							<span v-if="user.isBot" :title="$t('isBot')"><Fa :icon="faRobot"/></span>
+							<span v-if="user.isAdmin" :title="$ts.isAdmin" style="color: var(--badge);"><Fa :icon="faBookmark"/></span>
+							<span v-if="!user.isAdmin && user.isModerator" :title="$ts.isModerator" style="color: var(--badge);"><Fa :icon="farBookmark"/></span>
+							<span v-if="user.isLocked" :title="$ts.isLocked"><Fa :icon="faLock"/></span>
+							<span v-if="user.isBot" :title="$ts.isBot"><Fa :icon="faRobot"/></span>
 						</div>
 					</div>
-					<span class="followed" v-if="$i && $i.id != user.id && user.isFollowed">{{ $t('followsYou') }}</span>
+					<span class="followed" v-if="$i && $i.id != user.id && user.isFollowed">{{ $ts.followsYou }}</span>
 					<div class="actions" v-if="$i">
 						<button @click="menu" class="menu _button"><Fa :icon="faEllipsisH"/></button>
 						<MkFollowButton v-if="$i.id != user.id" :user="user" :inline="true" :transparent="false" :full="true" class="koudoku"/>
@@ -126,27 +126,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="$t('isAdmin')" style="color: var(--badge);"><Fa :icon="faBookmark"/></span>
-						<span v-if="!user.isAdmin && user.isModerator" :title="$t('isModerator')" style="color: var(--badge);"><Fa :icon="farBookmark"/></span>
-						<span v-if="user.isLocked" :title="$t('isLocked')"><Fa :icon="faLock"/></span>
-						<span v-if="user.isBot" :title="$t('isBot')"><Fa :icon="faRobot"/></span>
+						<span v-if="user.isAdmin" :title="$ts.isAdmin" style="color: var(--badge);"><Fa :icon="faBookmark"/></span>
+						<span v-if="!user.isAdmin && user.isModerator" :title="$ts.isModerator" style="color: var(--badge);"><Fa :icon="farBookmark"/></span>
+						<span v-if="user.isLocked" :title="$ts.isLocked"><Fa :icon="faLock"/></span>
+						<span v-if="user.isBot" :title="$ts.isBot"><Fa :icon="faRobot"/></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">{{ $t('noAccountDescription') }}</p>
+					<p v-else class="empty">{{ $ts.noAccountDescription }}</p>
 				</div>
 				<div class="fields system">
 					<dl class="field" v-if="user.location">
-						<dt class="name"><Fa :icon="faMapMarker" fixed-width/> {{ $t('location') }}</dt>
+						<dt class="name"><Fa :icon="faMapMarker" fixed-width/> {{ $ts.location }}</dt>
 						<dd class="value">{{ user.location }}</dd>
 					</dl>
 					<dl class="field" v-if="user.birthday">
-						<dt class="name"><Fa :icon="faBirthdayCake" fixed-width/> {{ $t('birthday') }}</dt>
+						<dt class="name"><Fa :icon="faBirthdayCake" fixed-width/> {{ $ts.birthday }}</dt>
 						<dd class="value">{{ user.birthday.replace('-', '/').replace('-', '/') }} ({{ $t('yearsOld', { age }) }})</dd>
 					</dl>
 					<dl class="field">
-						<dt class="name"><Fa :icon="faCalendarAlt" fixed-width/> {{ $t('registeredDate') }}</dt>
+						<dt class="name"><Fa :icon="faCalendarAlt" fixed-width/> {{ $ts.registeredDate }}</dt>
 						<dd class="value">{{ new Date(user.createdAt).toLocaleString() }} (<MkTime :time="user.createdAt"/>)</dd>
 					</dl>
 				</div>
@@ -163,15 +163,15 @@
 				<div class="status">
 					<MkA :to="userPage(user)" :class="{ active: page === 'index' }">
 						<b>{{ number(user.notesCount) }}</b>
-						<span>{{ $t('notes') }}</span>
+						<span>{{ $ts.notes }}</span>
 					</MkA>
 					<MkA :to="userPage(user, 'following')" :class="{ active: page === 'following' }">
 						<b>{{ number(user.followingCount) }}</b>
-						<span>{{ $t('following') }}</span>
+						<span>{{ $ts.following }}</span>
 					</MkA>
 					<MkA :to="userPage(user, 'followers')" :class="{ active: page === 'followers' }">
 						<b>{{ number(user.followersCount) }}</b>
-						<span>{{ $t('followers') }}</span>
+						<span>{{ $ts.followers }}</span>
 					</MkA>
 				</div>
 			</div>
@@ -180,15 +180,15 @@
 		<div class="nav _vMargin">
 			<MkA :to="userPage(user)" :class="{ active: page === 'index' }" class="link">
 				<Fa :icon="faCommentAlt" class="icon"/>
-				<span>{{ $t('notes') }}</span>
+				<span>{{ $ts.notes }}</span>
 			</MkA>
 			<MkA :to="userPage(user, 'clips')" :class="{ active: page === 'clips' }" class="link">
 				<Fa :icon="faPaperclip" class="icon"/>
-				<span>{{ $t('clips') }}</span>
+				<span>{{ $ts.clips }}</span>
 			</MkA>
 			<MkA :to="userPage(user, 'pages')" :class="{ active: page === 'pages' }" class="link">
 				<Fa :icon="faFileAlt" class="icon"/>
-				<span>{{ $t('pages') }}</span>
+				<span>{{ $ts.pages }}</span>
 			</MkA>
 		</div>
 
diff --git a/src/client/pages/welcome.entrance.vue b/src/client/pages/welcome.entrance.vue
index 550d9fe6e..13b389d65 100644
--- a/src/client/pages/welcome.entrance.vue
+++ b/src/client/pages/welcome.entrance.vue
@@ -11,13 +11,13 @@
 		</div>
 	</div>
 	<template v-if="meta.pinnedClipId">
-		<h2># {{ $t('pinnedNotes') }}</h2>
+		<h2># {{ $ts.pinnedNotes }}</h2>
 		<MkPagination :pagination="clipPagination" #default="{items}">
 			<XNote class="kmkqjgkl" v-for="note in items" :note="note" :key="note.id"/>
 		</MkPagination>
 	</template>
 	<template v-else>
-		<h2># {{ $t('featured') }}</h2>
+		<h2># {{ $ts.featured }}</h2>
 		<MkPagination :pagination="featuredPagination" #default="{items}">
 			<XNote class="kmkqjgkl" v-for="note in items" :note="note" :key="note.id"/>
 		</MkPagination>
diff --git a/src/client/pages/welcome.setup.vue b/src/client/pages/welcome.setup.vue
index d205d7976..bc994099a 100644
--- a/src/client/pages/welcome.setup.vue
+++ b/src/client/pages/welcome.setup.vue
@@ -2,18 +2,18 @@
 <form class="mk-setup" @submit.prevent="submit()">
 	<h1>Welcome to Misskey!</h1>
 	<div>
-		<p>{{ $t('intro') }}</p>
+		<p>{{ $ts.intro }}</p>
 		<MkInput v-model:value="username" pattern="^[a-zA-Z0-9_]{1,20}$" spellcheck="false" required>
-			<span>{{ $t('username') }}</span>
+			<span>{{ $ts.username }}</span>
 			<template #prefix>@</template>
 			<template #suffix>@{{ host }}</template>
 		</MkInput>
 		<MkInput v-model:value="password" type="password">
-			<span>{{ $t('password') }}</span>
+			<span>{{ $ts.password }}</span>
 			<template #prefix><Fa :icon="faLock"/></template>
 		</MkInput>
 		<footer>
-			<MkButton primary type="submit" :disabled="submitting">{{ submitting ? $t('processing') : $t('done') }}<MkEllipsis v-if="submitting"/></MkButton>
+			<MkButton primary type="submit" :disabled="submitting">{{ submitting ? $ts.processing : $ts.done }}<MkEllipsis v-if="submitting"/></MkButton>
 		</footer>
 	</div>
 </form>
@@ -59,7 +59,7 @@ export default defineComponent({
 
 				os.dialog({
 					type: 'error',
-					text: this.$t('somethingHappened')
+					text: this.$ts.somethingHappened
 				});
 			});
 		}
diff --git a/src/client/ui/_common_/stream-indicator.vue b/src/client/ui/_common_/stream-indicator.vue
index 23578bf62..8b1b4b567 100644
--- a/src/client/ui/_common_/stream-indicator.vue
+++ b/src/client/ui/_common_/stream-indicator.vue
@@ -1,9 +1,9 @@
 <template>
 <div class="nsbbhtug" v-if="hasDisconnected && $store.state.serverDisconnectedBehavior === 'quiet'" @click="resetDisconnected">
-	<div>{{ $t('disconnectedFromServer') }}</div>
+	<div>{{ $ts.disconnectedFromServer }}</div>
 	<div class="command">
-		<button class="_textButton" @click="reload">{{ $t('reload') }}</button>
-		<button class="_textButton">{{ $t('doNothing') }}</button>
+		<button class="_textButton" @click="reload">{{ $ts.reload }}</button>
+		<button class="_textButton">{{ $ts.doNothing }}</button>
 	</div>
 </div>
 </template>
diff --git a/src/client/ui/_common_/upload.vue b/src/client/ui/_common_/upload.vue
index 2ba2186f5..c1986737d 100644
--- a/src/client/ui/_common_/upload.vue
+++ b/src/client/ui/_common_/upload.vue
@@ -6,7 +6,7 @@
 			<div class="top">
 				<p class="name"><Fa :icon="faSpinner" pulse/>{{ ctx.name }}</p>
 				<p class="status">
-					<span class="initing" v-if="ctx.progressValue === undefined">{{ $t('waiting') }}<MkEllipsis/></span>
+					<span class="initing" v-if="ctx.progressValue === undefined">{{ $ts.waiting }}<MkEllipsis/></span>
 					<span class="kb" v-if="ctx.progressValue !== undefined">{{ 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 class="percentage" v-if="ctx.progressValue !== undefined">{{ Math.floor((ctx.progressValue / ctx.progressMax) * 100) }}</span>
 				</p>
diff --git a/src/client/ui/deck.vue b/src/client/ui/deck.vue
index a33e23410..557df3a80 100644
--- a/src/client/ui/deck.vue
+++ b/src/client/ui/deck.vue
@@ -148,7 +148,7 @@ export default defineComponent({
 			];
 
 			const { canceled, result: column } = await os.dialog({
-				title: this.$t('_deck.addColumn'),
+				title: this.$ts._deck.addColumn,
 				type: null,
 				select: {
 					items: columns.map(column => ({
diff --git a/src/client/ui/deck/antenna-column.vue b/src/client/ui/deck/antenna-column.vue
index 5fe1835c3..0b3ae3af5 100644
--- a/src/client/ui/deck/antenna-column.vue
+++ b/src/client/ui/deck/antenna-column.vue
@@ -48,7 +48,7 @@ export default defineComponent({
 	created() {
 		this.menu = [{
 			icon: faCog,
-			text: this.$t('selectAntenna'),
+			text: this.$ts.selectAntenna,
 			action: this.setAntenna
 		}];
 	},
@@ -63,7 +63,7 @@ export default defineComponent({
 		async setAntenna() {
 			const antennas = await os.api('antennas/list');
 			const { canceled, result: antenna } = await os.dialog({
-				title: this.$t('selectAntenna'),
+				title: this.$ts.selectAntenna,
 				type: null,
 				select: {
 					items: antennas.map(x => ({
diff --git a/src/client/ui/deck/column.vue b/src/client/ui/deck/column.vue
index b1b38199b..41dcc3e36 100644
--- a/src/client/ui/deck/column.vue
+++ b/src/client/ui/deck/column.vue
@@ -136,10 +136,10 @@ export default defineComponent({
 		getMenu() {
 			const items = [{
 				icon: faPencilAlt,
-				text: this.$t('rename'),
+				text: this.$ts.rename,
 				action: () => {
 					os.dialog({
-						title: this.$t('rename'),
+						title: this.$ts.rename,
 						input: {
 							default: this.column.name,
 							allowEmpty: false
@@ -151,43 +151,43 @@ export default defineComponent({
 				}
 			}, null, {
 				icon: faArrowLeft,
-				text: this.$t('_deck.swapLeft'),
+				text: this.$ts._deck.swapLeft,
 				action: () => {
 					swapLeftColumn(this.column.id);
 				}
 			}, {
 				icon: faArrowRight,
-				text: this.$t('_deck.swapRight'),
+				text: this.$ts._deck.swapRight,
 				action: () => {
 					swapRightColumn(this.column.id);
 				}
 			}, this.isStacked ? {
 				icon: faArrowUp,
-				text: this.$t('_deck.swapUp'),
+				text: this.$ts._deck.swapUp,
 				action: () => {
 					swapUpColumn(this.column.id);
 				}
 			} : undefined, this.isStacked ? {
 				icon: faArrowDown,
-				text: this.$t('_deck.swapDown'),
+				text: this.$ts._deck.swapDown,
 				action: () => {
 					swapDownColumn(this.column.id);
 				}
 			} : undefined, null, {
 				icon: faWindowRestore,
-				text: this.$t('_deck.stackLeft'),
+				text: this.$ts._deck.stackLeft,
 				action: () => {
 					stackLeftColumn(this.column.id);
 				}
 			}, this.isStacked ? {
 				icon: faWindowMaximize,
-				text: this.$t('_deck.popRight'),
+				text: this.$ts._deck.popRight,
 				action: () => {
 					popRightColumn(this.column.id);
 				}
 			} : undefined, null, {
 				icon: faTrashAlt,
-				text: this.$t('remove'),
+				text: this.$ts.remove,
 				action: () => {
 					removeColumn(this.column.id);
 				}
diff --git a/src/client/ui/deck/list-column.vue b/src/client/ui/deck/list-column.vue
index b12023614..364d8265e 100644
--- a/src/client/ui/deck/list-column.vue
+++ b/src/client/ui/deck/list-column.vue
@@ -48,7 +48,7 @@ export default defineComponent({
 	created() {
 		this.menu = [{
 			icon: faCog,
-			text: this.$t('selectList'),
+			text: this.$ts.selectList,
 			action: this.setList
 		}];
 	},
@@ -63,7 +63,7 @@ export default defineComponent({
 		async setList() {
 			const lists = await os.api('users/lists/list');
 			const { canceled, result: list } = await os.dialog({
-				title: this.$t('selectList'),
+				title: this.$ts.selectList,
 				type: null,
 				select: {
 					items: lists.map(x => ({
diff --git a/src/client/ui/deck/notifications-column.vue b/src/client/ui/deck/notifications-column.vue
index 871869590..0c7f72d9f 100644
--- a/src/client/ui/deck/notifications-column.vue
+++ b/src/client/ui/deck/notifications-column.vue
@@ -42,7 +42,7 @@ export default defineComponent({
 	created() {
 		this.menu = [{
 			icon: faCog,
-			text: this.$t('notificationSetting'),
+			text: this.$ts.notificationSetting,
 			action: () => {
 				os.popup(import('@/components/notification-setting-window.vue'), {
 					includingTypes: this.column.includingTypes,
diff --git a/src/client/ui/deck/tl-column.vue b/src/client/ui/deck/tl-column.vue
index 6b05f21c3..45b4c72fb 100644
--- a/src/client/ui/deck/tl-column.vue
+++ b/src/client/ui/deck/tl-column.vue
@@ -63,7 +63,7 @@ export default defineComponent({
 	created() {
 		this.menu = [{
 			icon: faCog,
-			text: this.$t('timeline'),
+			text: this.$ts.timeline,
 			action: this.setType
 		}];
 	},
@@ -81,17 +81,17 @@ export default defineComponent({
 	methods: {
 		async setType() {
 			const { canceled, result: src } = await os.dialog({
-				title: this.$t('timeline'),
+				title: this.$ts.timeline,
 				type: null,
 				select: {
 					items: [{
-						value: 'home', text: this.$t('_timelines.home')
+						value: 'home', text: this.$ts._timelines.home
 					}, {
-						value: 'local', text: this.$t('_timelines.local')
+						value: 'local', text: this.$ts._timelines.local
 					}, {
-						value: 'social', text: this.$t('_timelines.social')
+						value: 'social', text: this.$ts._timelines.social
 					}, {
-						value: 'global', text: this.$t('_timelines.global')
+						value: 'global', text: this.$ts._timelines.global
 					}]
 				},
 			});
diff --git a/src/client/ui/deck/widgets-column.vue b/src/client/ui/deck/widgets-column.vue
index 8f526f973..39d7dc1cb 100644
--- a/src/client/ui/deck/widgets-column.vue
+++ b/src/client/ui/deck/widgets-column.vue
@@ -6,11 +6,11 @@
 		<template v-if="edit">
 			<header>
 				<MkSelect v-model:value="widgetAdderSelected" style="margin-bottom: var(--margin)">
-					<template #label>{{ $t('selectWidget') }}</template>
+					<template #label>{{ $ts.selectWidget }}</template>
 					<option v-for="widget in widgets" :value="widget" :key="widget">{{ $t(`_widgets.${widget}`) }}</option>
 				</MkSelect>
-				<MkButton inline @click="addWidget" primary><Fa :icon="faPlus"/> {{ $t('add') }}</MkButton>
-				<MkButton inline @click="edit = false">{{ $t('close') }}</MkButton>
+				<MkButton inline @click="addWidget" primary><Fa :icon="faPlus"/> {{ $ts.add }}</MkButton>
+				<MkButton inline @click="edit = false">{{ $ts.close }}</MkButton>
 			</header>
 			<XDraggable
 				v-model="_widgets"
@@ -84,7 +84,7 @@ export default defineComponent({
 	created() {
 		this.menu = [{
 			icon: faCog,
-			text: this.$t('edit'),
+			text: this.$ts.edit,
 			action: () => {
 				this.edit = !this.edit;
 			}
diff --git a/src/client/ui/default.side.vue b/src/client/ui/default.side.vue
index dc9dabaec..b58c339f2 100644
--- a/src/client/ui/default.side.vue
+++ b/src/client/ui/default.side.vue
@@ -83,28 +83,28 @@ export default defineComponent({
 				text: this.path,
 			}, {
 				icon: faExpandAlt,
-				text: this.$t('showInPage'),
+				text: this.$ts.showInPage,
 				action: () => {
 					this.$router.push(this.path);
 					this.close();
 				}
 			}, {
 				icon: faWindowMaximize,
-				text: this.$t('openInWindow'),
+				text: this.$ts.openInWindow,
 				action: () => {
 					os.pageWindow(this.path);
 					this.close();
 				}
 			}, null, {
 				icon: faExternalLinkAlt,
-				text: this.$t('openInNewTab'),
+				text: this.$ts.openInNewTab,
 				action: () => {
 					window.open(this.url, '_blank');
 					this.close();
 				}
 			}, {
 				icon: faLink,
-				text: this.$t('copyLink'),
+				text: this.$ts.copyLink,
 				action: () => {
 					copyToClipboard(this.url);
 				}
diff --git a/src/client/ui/default.vue b/src/client/ui/default.vue
index 32a5e9959..46d02954b 100644
--- a/src/client/ui/default.vue
+++ b/src/client/ui/default.vue
@@ -215,13 +215,13 @@ export default defineComponent({
 				text: path,
 			}, {
 				icon: faColumns,
-				text: this.$t('openInSideView'),
+				text: this.$ts.openInSideView,
 				action: () => {
 					this.$refs.side.navigate(path);
 				}
 			}, {
 				icon: faWindowMaximize,
-				text: this.$t('openInWindow'),
+				text: this.$ts.openInWindow,
 				action: () => {
 					os.pageWindow(path);
 				}
diff --git a/src/client/ui/default.widgets.vue b/src/client/ui/default.widgets.vue
index d05b297ec..ec73c4277 100644
--- a/src/client/ui/default.widgets.vue
+++ b/src/client/ui/default.widgets.vue
@@ -20,11 +20,11 @@
 				</div>
 			</template>
 		</XDraggable>
-		<button @click="editMode = false" class="_textButton" style="font-size: 0.9em;"><Fa :icon="faCheck"/> {{ $t('editWidgetsExit') }}</button>
+		<button @click="editMode = false" class="_textButton" style="font-size: 0.9em;"><Fa :icon="faCheck"/> {{ $ts.editWidgetsExit }}</button>
 	</template>
 	<template v-else>
 		<component v-for="widget in widgets" class="_inContainer_ _forceContainerFull_" :is="`mkw-${widget.name}`" :key="widget.id" :widget="widget" @updateProps="saveWidget(widget.id, $event)"/>
-		<button @click="editMode = true" class="_textButton" style="font-size: 0.9em;"><Fa :icon="faPencilAlt"/> {{ $t('editWidgets') }}</button>
+		<button @click="editMode = true" class="_textButton" style="font-size: 0.9em;"><Fa :icon="faPencilAlt"/> {{ $ts.editWidgets }}</button>
 	</template>
 </div>
 </template>
@@ -76,7 +76,7 @@ export default defineComponent({
 		async addWidget() {
 			const { canceled, result: widget } = await os.dialog({
 				type: null,
-				title: this.$t('chooseWidget'),
+				title: this.$ts.chooseWidget,
 				select: {
 					items: widgets.map(widget => ({
 						value: widget,
diff --git a/src/client/ui/visitor/a.vue b/src/client/ui/visitor/a.vue
index 6f8c5efd6..669e06431 100644
--- a/src/client/ui/visitor/a.vue
+++ b/src/client/ui/visitor/a.vue
@@ -4,11 +4,11 @@
 		<div>
 			<h1 v-if="meta"><img class="logo" v-if="meta.logoImageUrl" :src="meta.logoImageUrl"><span v-else class="text">{{ instanceName }}</span></h1>
 			<div class="about" v-if="meta">
-				<div class="desc" v-html="meta.description || $t('introMisskey')"></div>
+				<div class="desc" v-html="meta.description || $ts.introMisskey"></div>
 			</div>
 			<div class="action">
-				<button class="_button primary" @click="signup()">{{ $t('signup') }}</button>
-				<button class="_button" @click="signin()">{{ $t('login') }}</button>
+				<button class="_button primary" @click="signup()">{{ $ts.signup }}</button>
+				<button class="_button" @click="signin()">{{ $ts.login }}</button>
 			</div>
 		</div>
 	</div>
diff --git a/src/client/ui/visitor/b.vue b/src/client/ui/visitor/b.vue
index 94d503c51..967cf0885 100644
--- a/src/client/ui/visitor/b.vue
+++ b/src/client/ui/visitor/b.vue
@@ -35,13 +35,13 @@
 
 	<transition name="tray">
 		<div v-if="showMenu" class="menu">
-			<MkA to="/" class="link" active-class="active"><Fa :icon="faHome" class="icon"/>{{ $t('home') }}</MkA>
-			<MkA to="/explore" class="link" active-class="active"><Fa :icon="faHashtag" class="icon"/>{{ $t('explore') }}</MkA>
-			<MkA to="/featured" class="link" active-class="active"><Fa :icon="faFireAlt" class="icon"/>{{ $t('featured') }}</MkA>
-			<MkA to="/channels" class="link" active-class="active"><Fa :icon="faSatelliteDish" class="icon"/>{{ $t('channel') }}</MkA>
+			<MkA to="/" class="link" active-class="active"><Fa :icon="faHome" class="icon"/>{{ $ts.home }}</MkA>
+			<MkA to="/explore" class="link" active-class="active"><Fa :icon="faHashtag" class="icon"/>{{ $ts.explore }}</MkA>
+			<MkA to="/featured" class="link" active-class="active"><Fa :icon="faFireAlt" class="icon"/>{{ $ts.featured }}</MkA>
+			<MkA to="/channels" class="link" active-class="active"><Fa :icon="faSatelliteDish" class="icon"/>{{ $ts.channel }}</MkA>
 			<div class="action">
-				<button class="_buttonPrimary" @click="signup()">{{ $t('signup') }}</button>
-				<button class="_button" @click="signin()">{{ $t('login') }}</button>
+				<button class="_buttonPrimary" @click="signup()">{{ $ts.signup }}</button>
+				<button class="_button" @click="signin()">{{ $ts.login }}</button>
 			</div>
 		</div>
 	</transition>
diff --git a/src/client/ui/visitor/header.vue b/src/client/ui/visitor/header.vue
index 808616463..9a7bf96fc 100644
--- a/src/client/ui/visitor/header.vue
+++ b/src/client/ui/visitor/header.vue
@@ -2,10 +2,10 @@
 <div class="sqxihjet">
 	<div class="wide" v-if="narrow === false">
 		<div class="content">
-			<MkA to="/" class="link" active-class="active"><Fa :icon="faHome" class="icon"/>{{ $t('home') }}</MkA>
-			<MkA to="/explore" class="link" active-class="active"><Fa :icon="faHashtag" class="icon"/>{{ $t('explore') }}</MkA>
-			<MkA to="/featured" class="link" active-class="active"><Fa :icon="faFireAlt" class="icon"/>{{ $t('featured') }}</MkA>
-			<MkA to="/channels" class="link" active-class="active"><Fa :icon="faSatelliteDish" class="icon"/>{{ $t('channel') }}</MkA>
+			<MkA to="/" class="link" active-class="active"><Fa :icon="faHome" class="icon"/>{{ $ts.home }}</MkA>
+			<MkA to="/explore" class="link" active-class="active"><Fa :icon="faHashtag" class="icon"/>{{ $ts.explore }}</MkA>
+			<MkA to="/featured" class="link" active-class="active"><Fa :icon="faFireAlt" class="icon"/>{{ $ts.featured }}</MkA>
+			<MkA to="/channels" class="link" active-class="active"><Fa :icon="faSatelliteDish" class="icon"/>{{ $ts.channel }}</MkA>
 			<div class="page active link" v-if="info">
 				<div class="title">
 					<Fa v-if="info.icon" :icon="info.icon" :key="info.icon" class="icon"/>
@@ -16,9 +16,9 @@
 				<button class="_button action" v-if="info.action" @click.stop="info.action.handler"><Fa :icon="info.action.icon" :key="info.action.icon"/></button>
 			</div>
 			<div class="right">
-				<button class="_button search" @click="search()"><Fa :icon="faSearch" class="icon"/><span>{{ $t('search') }}</span></button>
-				<button class="_buttonPrimary signup" @click="signup()">{{ $t('signup') }}</button>
-				<button class="_button login" @click="signin()">{{ $t('login') }}</button>
+				<button class="_button search" @click="search()"><Fa :icon="faSearch" class="icon"/><span>{{ $ts.search }}</span></button>
+				<button class="_buttonPrimary signup" @click="signup()">{{ $ts.signup }}</button>
+				<button class="_button login" @click="signin()">{{ $ts.login }}</button>
 			</div>
 		</div>
 	</div>
diff --git a/src/client/ui/visitor/kanban.vue b/src/client/ui/visitor/kanban.vue
index 6816589d2..6a4bcd1ad 100644
--- a/src/client/ui/visitor/kanban.vue
+++ b/src/client/ui/visitor/kanban.vue
@@ -8,14 +8,14 @@
 			</h1>
 			<template v-if="full">
 				<div class="about" v-if="meta">
-					<div class="desc" v-html="meta.description || $t('introMisskey')"></div>
+					<div class="desc" v-html="meta.description || $ts.introMisskey"></div>
 				</div>
 				<div class="action">
-					<button class="_buttonPrimary" @click="signup()">{{ $t('signup') }}</button>
-					<button class="_button" @click="signin()">{{ $t('login') }}</button>
+					<button class="_buttonPrimary" @click="signup()">{{ $ts.signup }}</button>
+					<button class="_button" @click="signin()">{{ $ts.login }}</button>
 				</div>
 				<div class="announcements panel">
-					<header>{{ $t('announcements') }}</header>
+					<header>{{ $ts.announcements }}</header>
 					<MkPagination :pagination="announcements" #default="{items}" class="list">
 						<section class="item" v-for="(announcement, i) in items" :key="announcement.id">
 							<div class="title">{{ announcement.title }}</div>
diff --git a/src/client/widgets/activity.vue b/src/client/widgets/activity.vue
index 334526bd2..a92644e0a 100644
--- a/src/client/widgets/activity.vue
+++ b/src/client/widgets/activity.vue
@@ -1,6 +1,6 @@
 <template>
 <MkContainer :show-header="props.showHeader" :naked="props.transparent">
-	<template #header><Fa :icon="faChartBar"/>{{ $t('_widgets.activity') }}</template>
+	<template #header><Fa :icon="faChartBar"/>{{ $ts._widgets.activity }}</template>
 	<template #func><button @click="toggleView()" class="_button"><Fa :icon="faSort"/></button></template>
 
 	<div>
diff --git a/src/client/widgets/calendar.vue b/src/client/widgets/calendar.vue
index d464f27ec..545072e87 100644
--- a/src/client/widgets/calendar.vue
+++ b/src/client/widgets/calendar.vue
@@ -10,19 +10,19 @@
 	</div>
 	<div class="info">
 		<div>
-			<p>{{ $t('today') }}: <b>{{ dayP.toFixed(1) }}%</b></p>
+			<p>{{ $ts.today }}: <b>{{ dayP.toFixed(1) }}%</b></p>
 			<div class="meter">
 				<div class="val" :style="{ width: `${dayP}%` }"></div>
 			</div>
 		</div>
 		<div>
-			<p>{{ $t('thisMonth') }}: <b>{{ monthP.toFixed(1) }}%</b></p>
+			<p>{{ $ts.thisMonth }}: <b>{{ monthP.toFixed(1) }}%</b></p>
 			<div class="meter">
 				<div class="val" :style="{ width: `${monthP}%` }"></div>
 			</div>
 		</div>
 		<div>
-			<p>{{ $t('thisYear') }}: <b>{{ yearP.toFixed(1) }}%</b></p>
+			<p>{{ $ts.thisYear }}: <b>{{ yearP.toFixed(1) }}%</b></p>
 			<div class="meter">
 				<div class="val" :style="{ width: `${yearP}%` }"></div>
 			</div>
@@ -80,13 +80,13 @@ export default defineComponent({
 			this.month = nm + 1;
 			this.day = nd;
 			this.weekDay = [
-				this.$t('_weekday.sunday'),
-				this.$t('_weekday.monday'),
-				this.$t('_weekday.tuesday'),
-				this.$t('_weekday.wednesday'),
-				this.$t('_weekday.thursday'),
-				this.$t('_weekday.friday'),
-				this.$t('_weekday.saturday')
+				this.$ts._weekday.sunday,
+				this.$ts._weekday.monday,
+				this.$ts._weekday.tuesday,
+				this.$ts._weekday.wednesday,
+				this.$ts._weekday.thursday,
+				this.$ts._weekday.friday,
+				this.$ts._weekday.saturday
 			][now.getDay()];
 
 			const dayNumer   = now.getTime() - new Date(ny, nm, nd).getTime();
diff --git a/src/client/widgets/federation.vue b/src/client/widgets/federation.vue
index 5cfa87e58..6e76bc40e 100644
--- a/src/client/widgets/federation.vue
+++ b/src/client/widgets/federation.vue
@@ -1,6 +1,6 @@
 <template>
 <MkContainer :show-header="props.showHeader" :body-togglable="bodyTogglable" :scrollable="scrollable">
-	<template #header><Fa :icon="faGlobe"/>{{ $t('_widgets.federation') }}</template>
+	<template #header><Fa :icon="faGlobe"/>{{ $ts._widgets.federation }}</template>
 
 	<div class="wbrkwalb">
 		<MkLoading v-if="fetching"/>
diff --git a/src/client/widgets/memo.vue b/src/client/widgets/memo.vue
index 9cde149f4..dab19cd16 100644
--- a/src/client/widgets/memo.vue
+++ b/src/client/widgets/memo.vue
@@ -1,10 +1,10 @@
 <template>
 <MkContainer :show-header="props.showHeader">
-	<template #header><Fa :icon="faStickyNote"/>{{ $t('_widgets.memo') }}</template>
+	<template #header><Fa :icon="faStickyNote"/>{{ $ts._widgets.memo }}</template>
 
 	<div class="otgbylcu">
-		<textarea v-model="text" :placeholder="$t('placeholder')" @input="onChange"></textarea>
-		<button @click="saveMemo" :disabled="!changed" class="_buttonPrimary">{{ $t('save') }}</button>
+		<textarea v-model="text" :placeholder="$ts.placeholder" @input="onChange"></textarea>
+		<button @click="saveMemo" :disabled="!changed" class="_buttonPrimary">{{ $ts.save }}</button>
 	</div>
 </MkContainer>
 </template>
diff --git a/src/client/widgets/notifications.vue b/src/client/widgets/notifications.vue
index 7937ffb0a..c3eb6ee79 100644
--- a/src/client/widgets/notifications.vue
+++ b/src/client/widgets/notifications.vue
@@ -1,6 +1,6 @@
 <template>
 <MkContainer :style="`height: ${props.height}px;`" :show-header="props.showHeader" :scrollable="true">
-	<template #header><Fa :icon="faBell"/>{{ $t('notifications') }}</template>
+	<template #header><Fa :icon="faBell"/>{{ $ts.notifications }}</template>
 	<template #func><button @click="configure()" class="_button"><Fa :icon="faCog"/></button></template>
 
 	<div>
diff --git a/src/client/widgets/photos.vue b/src/client/widgets/photos.vue
index 26b4487ad..ada15486b 100644
--- a/src/client/widgets/photos.vue
+++ b/src/client/widgets/photos.vue
@@ -1,6 +1,6 @@
 <template>
 <MkContainer :show-header="props.showHeader" :naked="props.transparent" :class="$style.root" :data-transparent="props.transparent ? true : null">
-	<template #header><Fa :icon="faCamera"/>{{ $t('_widgets.photos') }}</template>
+	<template #header><Fa :icon="faCamera"/>{{ $ts._widgets.photos }}</template>
 
 	<div class="">
 		<MkLoading v-if="fetching"/>
diff --git a/src/client/widgets/slideshow.vue b/src/client/widgets/slideshow.vue
index 0c1a2dc86..5d2e2b1eb 100644
--- a/src/client/widgets/slideshow.vue
+++ b/src/client/widgets/slideshow.vue
@@ -3,7 +3,7 @@
 	<div @click="choose">
 		<p v-if="props.folderId == null">
 			<template v-if="isCustomizeMode">{{ $t('folder-customize-mode') }}</template>
-			<template v-else>{{ $t('folder') }}</template>
+			<template v-else>{{ $ts.folder }}</template>
 		</p>
 		<p v-if="props.folderId != null && images.length === 0 && !fetching">{{ $t('no-image') }}</p>
 		<div ref="slideA" class="slide a"></div>
diff --git a/src/client/widgets/timeline.vue b/src/client/widgets/timeline.vue
index 2c98b013d..ee60790cb 100644
--- a/src/client/widgets/timeline.vue
+++ b/src/client/widgets/timeline.vue
@@ -90,19 +90,19 @@ export default defineComponent({
 				}
 			}));
 			os.modalMenu([{
-				text: this.$t('_timelines.home'),
+				text: this.$ts._timelines.home,
 				icon: faHome,
 				action: () => { this.setSrc('home') }
 			}, {
-				text: this.$t('_timelines.local'),
+				text: this.$ts._timelines.local,
 				icon: faComments,
 				action: () => { this.setSrc('local') }
 			}, {
-				text: this.$t('_timelines.social'),
+				text: this.$ts._timelines.social,
 				icon: faShareAlt,
 				action: () => { this.setSrc('social') }
 			}, {
-				text: this.$t('_timelines.global'),
+				text: this.$ts._timelines.global,
 				icon: faGlobe,
 				action: () => { this.setSrc('global') }
 			}, antennaItems.length > 0 ? null : undefined, ...antennaItems, listItems.length > 0 ? null : undefined, ...listItems], ev.currentTarget || ev.target).then(() => {
diff --git a/src/client/widgets/trends.vue b/src/client/widgets/trends.vue
index 9510bf205..3734573e3 100644
--- a/src/client/widgets/trends.vue
+++ b/src/client/widgets/trends.vue
@@ -1,6 +1,6 @@
 <template>
 <MkContainer :show-header="props.showHeader">
-	<template #header><Fa :icon="faHashtag"/>{{ $t('_widgets.trends') }}</template>
+	<template #header><Fa :icon="faHashtag"/>{{ $ts._widgets.trends }}</template>
 
 	<div class="wbrkwala">
 		<MkLoading v-if="fetching"/>