diff --git a/packages/client/src/pages/admin/settings.vue b/packages/client/src/pages/admin/settings.vue
index f2970d0459..6dc30fe50b 100644
--- a/packages/client/src/pages/admin/settings.vue
+++ b/packages/client/src/pages/admin/settings.vue
@@ -3,104 +3,104 @@
 	<FormSuspense :p="init">
 		<div class="_formRoot">
 			<FormInput v-model="name" class="_formBlock">
-				<template #label>{{ $ts.instanceName }}</template>
+				<template #label>{{ i18n.ts.instanceName }}</template>
 			</FormInput>
 
 			<FormTextarea v-model="description" class="_formBlock">
-				<template #label>{{ $ts.instanceDescription }}</template>
+				<template #label>{{ i18n.ts.instanceDescription }}</template>
 			</FormTextarea>
 
 			<FormInput v-model="tosUrl" class="_formBlock">
 				<template #prefix><i class="fas fa-link"></i></template>
-				<template #label>{{ $ts.tosUrl }}</template>
+				<template #label>{{ i18n.ts.tosUrl }}</template>
 			</FormInput>
 
 			<FormSplit :min-width="300">
 				<FormInput v-model="maintainerName" class="_formBlock">
-					<template #label>{{ $ts.maintainerName }}</template>
+					<template #label>{{ i18n.ts.maintainerName }}</template>
 				</FormInput>
 
 				<FormInput v-model="maintainerEmail" type="email" class="_formBlock">
 					<template #prefix><i class="fas fa-envelope"></i></template>
-					<template #label>{{ $ts.maintainerEmail }}</template>
+					<template #label>{{ i18n.ts.maintainerEmail }}</template>
 				</FormInput>
 			</FormSplit>
 
 			<FormTextarea v-model="pinnedUsers" class="_formBlock">
-				<template #label>{{ $ts.pinnedUsers }}</template>
-				<template #caption>{{ $ts.pinnedUsersDescription }}</template>
+				<template #label>{{ i18n.ts.pinnedUsers }}</template>
+				<template #caption>{{ i18n.ts.pinnedUsersDescription }}</template>
 			</FormTextarea>
 
 			<FormSection>
 				<FormSwitch v-model="enableRegistration" class="_formBlock">
-					<template #label>{{ $ts.enableRegistration }}</template>
+					<template #label>{{ i18n.ts.enableRegistration }}</template>
 				</FormSwitch>
 
 				<FormSwitch v-model="emailRequiredForSignup" class="_formBlock">
-					<template #label>{{ $ts.emailRequiredForSignup }}</template>
+					<template #label>{{ i18n.ts.emailRequiredForSignup }}</template>
 				</FormSwitch>
 			</FormSection>
 
 			<FormSection>
-				<FormSwitch v-model="enableLocalTimeline" class="_formBlock">{{ $ts.enableLocalTimeline }}</FormSwitch>
-				<FormSwitch v-model="enableGlobalTimeline" class="_formBlock">{{ $ts.enableGlobalTimeline }}</FormSwitch>
-				<FormInfo class="_formBlock">{{ $ts.disablingTimelinesInfo }}</FormInfo>
+				<FormSwitch v-model="enableLocalTimeline" class="_formBlock">{{ i18n.ts.enableLocalTimeline }}</FormSwitch>
+				<FormSwitch v-model="enableGlobalTimeline" class="_formBlock">{{ i18n.ts.enableGlobalTimeline }}</FormSwitch>
+				<FormInfo class="_formBlock">{{ i18n.ts.disablingTimelinesInfo }}</FormInfo>
 			</FormSection>
 
 			<FormSection>
-				<template #label>{{ $ts.theme }}</template>
+				<template #label>{{ i18n.ts.theme }}</template>
 
 				<FormInput v-model="iconUrl" class="_formBlock">
 					<template #prefix><i class="fas fa-link"></i></template>
-					<template #label>{{ $ts.iconUrl }}</template>
+					<template #label>{{ i18n.ts.iconUrl }}</template>
 				</FormInput>
 
 				<FormInput v-model="bannerUrl" class="_formBlock">
 					<template #prefix><i class="fas fa-link"></i></template>
-					<template #label>{{ $ts.bannerUrl }}</template>
+					<template #label>{{ i18n.ts.bannerUrl }}</template>
 				</FormInput>
 
 				<FormInput v-model="backgroundImageUrl" class="_formBlock">
 					<template #prefix><i class="fas fa-link"></i></template>
-					<template #label>{{ $ts.backgroundImageUrl }}</template>
+					<template #label>{{ i18n.ts.backgroundImageUrl }}</template>
 				</FormInput>
 
 				<FormInput v-model="themeColor" class="_formBlock">
 					<template #prefix><i class="fas fa-palette"></i></template>
-					<template #label>{{ $ts.themeColor }}</template>
+					<template #label>{{ i18n.ts.themeColor }}</template>
 					<template #caption>#RRGGBB</template>
 				</FormInput>
 
 				<FormTextarea v-model="defaultLightTheme" class="_formBlock">
-					<template #label>{{ $ts.instanceDefaultLightTheme }}</template>
-					<template #caption>{{ $ts.instanceDefaultThemeDescription }}</template>
+					<template #label>{{ i18n.ts.instanceDefaultLightTheme }}</template>
+					<template #caption>{{ i18n.ts.instanceDefaultThemeDescription }}</template>
 				</FormTextarea>
 
 				<FormTextarea v-model="defaultDarkTheme" class="_formBlock">
-					<template #label>{{ $ts.instanceDefaultDarkTheme }}</template>
-					<template #caption>{{ $ts.instanceDefaultThemeDescription }}</template>
+					<template #label>{{ i18n.ts.instanceDefaultDarkTheme }}</template>
+					<template #caption>{{ i18n.ts.instanceDefaultThemeDescription }}</template>
 				</FormTextarea>
 			</FormSection>
 
 			<FormSection>
-				<template #label>{{ $ts.files }}</template>
+				<template #label>{{ i18n.ts.files }}</template>
 
 				<FormSwitch v-model="cacheRemoteFiles" class="_formBlock">
-					<template #label>{{ $ts.cacheRemoteFiles }}</template>
-					<template #caption>{{ $ts.cacheRemoteFilesDescription }}</template>
+					<template #label>{{ i18n.ts.cacheRemoteFiles }}</template>
+					<template #caption>{{ i18n.ts.cacheRemoteFilesDescription }}</template>
 				</FormSwitch>
 
 				<FormSplit :min-width="280">
 					<FormInput v-model="localDriveCapacityMb" type="number" class="_formBlock">
-						<template #label>{{ $ts.driveCapacityPerLocalAccount }}</template>
+						<template #label>{{ i18n.ts.driveCapacityPerLocalAccount }}</template>
 						<template #suffix>MB</template>
-						<template #caption>{{ $ts.inMb }}</template>
+						<template #caption>{{ i18n.ts.inMb }}</template>
 					</FormInput>
 
 					<FormInput v-model="remoteDriveCapacityMb" type="number" :disabled="!cacheRemoteFiles" class="_formBlock">
-						<template #label>{{ $ts.driveCapacityPerRemoteAccount }}</template>
+						<template #label>{{ i18n.ts.driveCapacityPerRemoteAccount }}</template>
 						<template #suffix>MB</template>
-						<template #caption>{{ $ts.inMb }}</template>
+						<template #caption>{{ i18n.ts.inMb }}</template>
 					</FormInput>
 				</FormSplit>
 			</FormSection>
@@ -109,8 +109,8 @@
 				<template #label>ServiceWorker</template>
 
 				<FormSwitch v-model="enableServiceWorker" class="_formBlock">
-					<template #label>{{ $ts.enableServiceworker }}</template>
-					<template #caption>{{ $ts.serviceworkerInfo }}</template>
+					<template #label>{{ i18n.ts.enableServiceworker }}</template>
+					<template #caption>{{ i18n.ts.serviceworkerInfo }}</template>
 				</FormSwitch>
 
 				<template v-if="enableServiceWorker">
@@ -142,8 +142,8 @@
 </MkSpacer>
 </template>
 
-<script lang="ts">
-import { defineComponent } from 'vue';
+<script lang="ts" setup>
+import { } from 'vue';
 import FormSwitch from '@/components/form/switch.vue';
 import FormInput from '@/components/form/input.vue';
 import FormTextarea from '@/components/form/textarea.vue';
@@ -154,119 +154,103 @@ import FormSuspense from '@/components/form/suspense.vue';
 import * as os from '@/os';
 import * as symbols from '@/symbols';
 import { fetchInstance } from '@/instance';
+import { i18n } from '@/i18n';
 
-export default defineComponent({
-	components: {
-		FormSwitch,
-		FormInput,
-		FormSuspense,
-		FormTextarea,
-		FormInfo,
-		FormSection,
-		FormSplit,
-	},
+let name: string | null = $ref(null);
+let description: string | null = $ref(null);
+let tosUrl: string | null = $ref(null);
+let maintainerName: string | null = $ref(null);
+let maintainerEmail: string | null = $ref(null);
+let iconUrl: string | null = $ref(null);
+let bannerUrl: string | null = $ref(null);
+let backgroundImageUrl: string | null = $ref(null);
+let themeColor: any = $ref(null);
+let defaultLightTheme: any = $ref(null);
+let defaultDarkTheme: any = $ref(null);
+let enableLocalTimeline: boolean = $ref(false);
+let enableGlobalTimeline: boolean = $ref(false);
+let pinnedUsers: string = $ref('');
+let cacheRemoteFiles: boolean = $ref(false);
+let localDriveCapacityMb: any = $ref(0);
+let remoteDriveCapacityMb: any = $ref(0);
+let enableRegistration: boolean = $ref(false);
+let emailRequiredForSignup: boolean = $ref(false);
+let enableServiceWorker: boolean = $ref(false);
+let swPublicKey: any = $ref(null);
+let swPrivateKey: any = $ref(null);
+let deeplAuthKey: string = $ref('');
+let deeplIsPro: boolean = $ref(false);
 
-	emits: ['info'],
+async function init() {
+	const meta = await os.api('admin/meta');
+	name = meta.name;
+	description = meta.description;
+	tosUrl = meta.tosUrl;
+	iconUrl = meta.iconUrl;
+	bannerUrl = meta.bannerUrl;
+	backgroundImageUrl = meta.backgroundImageUrl;
+	themeColor = meta.themeColor;
+	defaultLightTheme = meta.defaultLightTheme;
+	defaultDarkTheme = meta.defaultDarkTheme;
+	maintainerName = meta.maintainerName;
+	maintainerEmail = meta.maintainerEmail;
+	enableLocalTimeline = !meta.disableLocalTimeline;
+	enableGlobalTimeline = !meta.disableGlobalTimeline;
+	pinnedUsers = meta.pinnedUsers.join('\n');
+	cacheRemoteFiles = meta.cacheRemoteFiles;
+	localDriveCapacityMb = meta.driveCapacityPerLocalUserMb;
+	remoteDriveCapacityMb = meta.driveCapacityPerRemoteUserMb;
+	enableRegistration = !meta.disableRegistration;
+	emailRequiredForSignup = meta.emailRequiredForSignup;
+	enableServiceWorker = meta.enableServiceWorker;
+	swPublicKey = meta.swPublickey;
+	swPrivateKey = meta.swPrivateKey;
+	deeplAuthKey = meta.deeplAuthKey;
+	deeplIsPro = meta.deeplIsPro;
+}
 
-	data() {
-		return {
-			[symbols.PAGE_INFO]: {
-				title: this.$ts.general,
-				icon: 'fas fa-cog',
-				bg: 'var(--bg)',
-				actions: [{
-					asFullButton: true,
-					icon: 'fas fa-check',
-					text: this.$ts.save,
-					handler: this.save,
-				}],
-			},
-			name: null,
-			description: null,
-			tosUrl: null as string | null,
-			maintainerName: null,
-			maintainerEmail: null,
-			iconUrl: null,
-			bannerUrl: null,
-			backgroundImageUrl: null,
-			themeColor: null,
-			defaultLightTheme: null,
-			defaultDarkTheme: null,
-			enableLocalTimeline: false,
-			enableGlobalTimeline: false,
-			pinnedUsers: '',
-			cacheRemoteFiles: false,
-			localDriveCapacityMb: 0,
-			remoteDriveCapacityMb: 0,
-			enableRegistration: false,
-			emailRequiredForSignup: false,
-			enableServiceWorker: false,
-			swPublicKey: null,
-			swPrivateKey: null,
-			deeplAuthKey: '',
-			deeplIsPro: false,
-		}
-	},
+function save() {
+	os.apiWithDialog('admin/update-meta', {
+		name,
+		description,
+		tosUrl,
+		iconUrl,
+		bannerUrl,
+		backgroundImageUrl,
+		themeColor: themeColor === '' ? null : themeColor,
+		defaultLightTheme: defaultLightTheme === '' ? null : defaultLightTheme,
+		defaultDarkTheme: defaultDarkTheme === '' ? null : defaultDarkTheme,
+		maintainerName,
+		maintainerEmail,
+		disableLocalTimeline: !enableLocalTimeline,
+		disableGlobalTimeline: !enableGlobalTimeline,
+		pinnedUsers: pinnedUsers.split('\n'),
+		cacheRemoteFiles,
+		localDriveCapacityMb: parseInt(localDriveCapacityMb, 10),
+		remoteDriveCapacityMb: parseInt(remoteDriveCapacityMb, 10),
+		disableRegistration: !enableRegistration,
+		emailRequiredForSignup,
+		enableServiceWorker,
+		swPublicKey,
+		swPrivateKey,
+		deeplAuthKey,
+		deeplIsPro,
+	}).then(() => {
+		fetchInstance();
+	});
+}
 
-	methods: {
-		async init() {
-			const meta = await os.api('admin/meta');
-			this.name = meta.name;
-			this.description = meta.description;
-			this.tosUrl = meta.tosUrl;
-			this.iconUrl = meta.iconUrl;
-			this.bannerUrl = meta.bannerUrl;
-			this.backgroundImageUrl = meta.backgroundImageUrl;
-			this.themeColor = meta.themeColor;
-			this.defaultLightTheme = meta.defaultLightTheme;
-			this.defaultDarkTheme = meta.defaultDarkTheme;
-			this.maintainerName = meta.maintainerName;
-			this.maintainerEmail = meta.maintainerEmail;
-			this.enableLocalTimeline = !meta.disableLocalTimeline;
-			this.enableGlobalTimeline = !meta.disableGlobalTimeline;
-			this.pinnedUsers = meta.pinnedUsers.join('\n');
-			this.cacheRemoteFiles = meta.cacheRemoteFiles;
-			this.localDriveCapacityMb = meta.driveCapacityPerLocalUserMb;
-			this.remoteDriveCapacityMb = meta.driveCapacityPerRemoteUserMb;
-			this.enableRegistration = !meta.disableRegistration;
-			this.emailRequiredForSignup = meta.emailRequiredForSignup;
-			this.enableServiceWorker = meta.enableServiceWorker;
-			this.swPublicKey = meta.swPublickey;
-			this.swPrivateKey = meta.swPrivateKey;
-			this.deeplAuthKey = meta.deeplAuthKey;
-			this.deeplIsPro = meta.deeplIsPro;
-		},
-
-		save() {
-			os.apiWithDialog('admin/update-meta', {
-				name: this.name,
-				description: this.description,
-				tosUrl: this.tosUrl,
-				iconUrl: this.iconUrl,
-				bannerUrl: this.bannerUrl,
-				backgroundImageUrl: this.backgroundImageUrl,
-				themeColor: this.themeColor === '' ? null : this.themeColor,
-				defaultLightTheme: this.defaultLightTheme === '' ? null : this.defaultLightTheme,
-				defaultDarkTheme: this.defaultDarkTheme === '' ? null : this.defaultDarkTheme,
-				maintainerName: this.maintainerName,
-				maintainerEmail: this.maintainerEmail,
-				disableLocalTimeline: !this.enableLocalTimeline,
-				disableGlobalTimeline: !this.enableGlobalTimeline,
-				pinnedUsers: this.pinnedUsers.split('\n'),
-				cacheRemoteFiles: this.cacheRemoteFiles,
-				localDriveCapacityMb: parseInt(this.localDriveCapacityMb, 10),
-				remoteDriveCapacityMb: parseInt(this.remoteDriveCapacityMb, 10),
-				disableRegistration: !this.enableRegistration,
-				emailRequiredForSignup: this.emailRequiredForSignup,
-				enableServiceWorker: this.enableServiceWorker,
-				swPublicKey: this.swPublicKey,
-				swPrivateKey: this.swPrivateKey,
-				deeplAuthKey: this.deeplAuthKey,
-				deeplIsPro: this.deeplIsPro,
-			}).then(() => {
-				fetchInstance();
-			});
-		}
+defineExpose({
+	[symbols.PAGE_INFO]: {
+		title: i18n.ts.general,
+		icon: 'fas fa-cog',
+		bg: 'var(--bg)',
+		actions: [{
+			asFullButton: true,
+			icon: 'fas fa-check',
+			text: i18n.ts.save,
+			handler: save,
+		}],
 	}
 });
 </script>