diff --git a/src/build/i18n.ts b/src/build/i18n.ts
index cb71a19394c4f72f72be94fff7c81c5908775a20..98b5413a2782849412c8b1135e228c13a19bf7f6 100644
--- a/src/build/i18n.ts
+++ b/src/build/i18n.ts
@@ -7,7 +7,7 @@ import locale from '../../locales';
 export default class Replacer {
 	private lang: string;
 
-	public pattern = /%i18n:(.+?)%/g;
+	public pattern = /%i18n:([a-z_\-@\.\!]+?)%/g;
 
 	constructor(lang: string) {
 		this.lang = lang;
@@ -53,11 +53,15 @@ export default class Replacer {
 		}
 	}
 
-	public replacement(ctx, match, a, b, c) {
+	public replacement(ctx, match, key) {
 		const client = 'misskey/src/client/app/';
 		let name = null;
 
-		let key = a || b || c;
+		const shouldEscape = key[0] == '!';
+		if (shouldEscape) {
+			key = key.substr(1);
+		}
+
 		if (key[0] == '@') {
 			name = ctx.src.substr(ctx.src.indexOf(client) + client.length);
 			key = key.substr(1);
@@ -65,6 +69,10 @@ export default class Replacer {
 
 		if (ctx && ctx.lang) this.lang = ctx.lang;
 
-		return this.get(name, key).replace(/'/g, '\\x27').replace(/"/g, '\\x22');
+		const txt = this.get(name, key);
+
+		return shouldEscape
+			? txt.replace(/'/g, '\\x27').replace(/"/g, '\\x22')
+			: txt.replace(/"/g, '"');
 	}
 }
diff --git a/src/client/app/ch/tags/channel.tag b/src/client/app/ch/tags/channel.tag
index c0561c9b9209672a2a35647a1bd64e849171e276..74b1a9ba19c8085c074b23a7d9bc37e0b2f3d17c 100644
--- a/src/client/app/ch/tags/channel.tag
+++ b/src/client/app/ch/tags/channel.tag
@@ -252,7 +252,7 @@
 		<button @click="selectFile">%fa:upload%%i18n:ch.tags.mk-channel-form.upload%</button>
 		<button @click="drive">%fa:cloud%%i18n:ch.tags.mk-channel-form.drive%</button>
 		<button :class="{ wait: wait }" ref="submit" disabled={ wait || (refs.text.value.length == 0) } @click="note">
-			<template v-if="!wait">%fa:paper-plane%</template>{ wait ? '%i18n:ch.tags.mk-channel-form.posting%' : '%i18n:ch.tags.mk-channel-form.note%' }<mk-ellipsis v-if="wait"/>
+			<template v-if="!wait">%fa:paper-plane%</template>{ wait ? '%i18n:!ch.tags.mk-channel-form.posting%' : '%i18n:!ch.tags.mk-channel-form.note%' }<mk-ellipsis v-if="wait"/>
 		</button>
 	</div>
 	<mk-uploader ref="uploader"/>
diff --git a/src/client/app/ch/tags/index.tag b/src/client/app/ch/tags/index.tag
index 88df2ec45d901e8531e8972aa9798ce4202f8f7d..529b83b2c769c3053fd3c9d1dd5cb9df98d5dc7d 100644
--- a/src/client/app/ch/tags/index.tag
+++ b/src/client/app/ch/tags/index.tag
@@ -25,7 +25,7 @@
 		});
 
 		this.n = () => {
-			const title = window.prompt('%i18n:ch.tags.mk-index.channel-title%');
+			const title = window.prompt('%i18n:!ch.tags.mk-index.channel-title%');
 
 			this.$root.$data.os.api('channels/create', {
 				title: title
diff --git a/src/client/app/common/scripts/check-for-update.ts b/src/client/app/common/scripts/check-for-update.ts
index 81c1eb9812e0e9dec63b36e21ef5c18afffd5421..20ce64ea8539fd70a3a35658135833b5fb734c06 100644
--- a/src/client/app/common/scripts/check-for-update.ts
+++ b/src/client/app/common/scripts/check-for-update.ts
@@ -23,7 +23,7 @@ export default async function(mios: MiOS, force = false, silent = false) {
 		}
 
 		if (!silent) {
-			alert('%i18n:common.update-available%'.replace('{newer}', newer).replace('{current}', current));
+			alert('%i18n:!common.update-available%'.replace('{newer}', newer).replace('{current}', current));
 		}
 
 		return newer;
diff --git a/src/client/app/common/scripts/streaming/home.ts b/src/client/app/common/scripts/streaming/home.ts
index e085801e15f0c92309919ff240e81faad151805d..73f2c5302c7db457fc69f644b4a281043e3085fd 100644
--- a/src/client/app/common/scripts/streaming/home.ts
+++ b/src/client/app/common/scripts/streaming/home.ts
@@ -30,7 +30,7 @@ export class HomeStream extends Stream {
 		// トークンが再生成されたとき
 		// このままではAPIが利用できないので強制的にサインアウトさせる
 		this.on('my_token_regenerated', () => {
-			alert('%i18n:common.my-token-regenerated%');
+			alert('%i18n:!common.my-token-regenerated%');
 			os.signout();
 		});
 	}
diff --git a/src/client/app/common/views/components/connect-failed.troubleshooter.vue b/src/client/app/common/views/components/connect-failed.troubleshooter.vue
index b0a9c87e789f3d5af61df6c5763f0108f37e0684..fffabe5a30bd3efa2ed367d82ba2564599735aff 100644
--- a/src/client/app/common/views/components/connect-failed.troubleshooter.vue
+++ b/src/client/app/common/views/components/connect-failed.troubleshooter.vue
@@ -7,21 +7,21 @@
 				<template v-if="network">%fa:check%</template>
 				<template v-if="!network">%fa:times%</template>
 			</template>
-			{{ network == null ? '%i18n:@checking-network%' : '%i18n:@network%' }}<mk-ellipsis v-if="network == null"/>
+			{{ network == null ? '%i18n:!@checking-network%' : '%i18n:!@network%' }}<mk-ellipsis v-if="network == null"/>
 		</p>
 		<p v-if="network == true" :data-wip="internet == null">
 			<template v-if="internet != null">
 				<template v-if="internet">%fa:check%</template>
 				<template v-if="!internet">%fa:times%</template>
 			</template>
-			{{ internet == null ? '%i18n:@checking-internet%' : '%i18n:@internet%' }}<mk-ellipsis v-if="internet == null"/>
+			{{ internet == null ? '%i18n:!@checking-internet%' : '%i18n:!@internet%' }}<mk-ellipsis v-if="internet == null"/>
 		</p>
 		<p v-if="internet == true" :data-wip="server == null">
 			<template v-if="server != null">
 				<template v-if="server">%fa:check%</template>
 				<template v-if="!server">%fa:times%</template>
 			</template>
-			{{ server == null ? '%i18n:@checking-server%' : '%i18n:@server%' }}<mk-ellipsis v-if="server == null"/>
+			{{ server == null ? '%i18n:!@checking-server%' : '%i18n:!@server%' }}<mk-ellipsis v-if="server == null"/>
 		</p>
 	</div>
 	<p v-if="!end">%i18n:@finding%<mk-ellipsis/></p>
diff --git a/src/client/app/common/views/components/connect-failed.vue b/src/client/app/common/views/components/connect-failed.vue
index d90c8756b8d3e3f5d8693d4bb9d02427257a7602..96857c87a17cfc9dfafb9b4e615a37bee8f3c8c4 100644
--- a/src/client/app/common/views/components/connect-failed.vue
+++ b/src/client/app/common/views/components/connect-failed.vue
@@ -3,9 +3,9 @@
 	<img src="data:image/jpeg;base64,%base64:/assets/error.jpg%" alt=""/>
 	<h1>%i18n:@title%</h1>
 	<p class="text">
-		{{ '%i18n:@description%'.substr(0, '%i18n:@description%'.indexOf('{')) }}
-		<a @click="reload">{{ '%i18n:@description%'.match(/\{(.+?)\}/)[1] }}</a>
-		{{ '%i18n:@description%'.substr('%i18n:@description%'.indexOf('}') + 1) }}
+		{{ '%i18n:!@description%'.substr(0, '%i18n:!@description%'.indexOf('{')) }}
+		<a @click="reload">{{ '%i18n:!@description%'.match(/\{(.+?)\}/)[1] }}</a>
+		{{ '%i18n:!@description%'.substr('%i18n:!@description%'.indexOf('}') + 1) }}
 	</p>
 	<button v-if="!troubleshooting" @click="troubleshooting = true">%i18n:@troubleshoot%</button>
 	<x-troubleshooter v-if="troubleshooting"/>
diff --git a/src/client/app/common/views/components/messaging-room.vue b/src/client/app/common/views/components/messaging-room.vue
index ca5416a218e25b2af6661af66635bf4d6fae43dd..38202d75819a893593e47f7265a1a93e58e5058a 100644
--- a/src/client/app/common/views/components/messaging-room.vue
+++ b/src/client/app/common/views/components/messaging-room.vue
@@ -8,7 +8,7 @@
 		<p class="empty" v-if="!init && messages.length == 0">%fa:info-circle%%i18n:@empty%</p>
 		<p class="no-history" v-if="!init && messages.length > 0 && !existMoreMessages">%fa:flag%%i18n:@no-history%</p>
 		<button class="more" :class="{ fetching: fetchingMoreMessages }" v-if="existMoreMessages" @click="fetchMoreMessages" :disabled="fetchingMoreMessages">
-			<template v-if="fetchingMoreMessages">%fa:spinner .pulse .fw%</template>{{ fetchingMoreMessages ? '%i18n:common.loading%' : '%i18n:@more%' }}
+			<template v-if="fetchingMoreMessages">%fa:spinner .pulse .fw%</template>{{ fetchingMoreMessages ? '%i18n:!common.loading%' : '%i18n:!@more%' }}
 		</button>
 		<template v-for="(message, i) in _messages">
 			<x-message :message="message" :key="message.id"/>
@@ -172,7 +172,7 @@ export default Vue.extend({
 				});
 			} else if (message.userId != (this as any).os.i.id) {
 				// Notify
-				this.notify('%i18n:@new-message%');
+				this.notify('%i18n:!@new-message%');
 			}
 		},
 
diff --git a/src/client/app/common/views/components/poll-editor.vue b/src/client/app/common/views/components/poll-editor.vue
index fa1897f435907bff2eaca69b2b109589bda1e10d..189172679b8e3b3d76291b7ed9c727022b51b0c2 100644
--- a/src/client/app/common/views/components/poll-editor.vue
+++ b/src/client/app/common/views/components/poll-editor.vue
@@ -5,7 +5,7 @@
 	</p>
 	<ul ref="choices">
 		<li v-for="(choice, i) in choices">
-			<input :value="choice" @input="onInput(i, $event)" :placeholder="'%i18n:@choice-n%'.replace('{}', i + 1)">
+			<input :value="choice" @input="onInput(i, $event)" :placeholder="'%i18n:!@choice-n%'.replace('{}', i + 1)">
 			<button @click="remove(i)" title="%i18n:@remove%">
 				%fa:times%
 			</button>
diff --git a/src/client/app/common/views/components/poll.vue b/src/client/app/common/views/components/poll.vue
index fd0ebc465677fab65c381e25b5a5d814c6d8a944..1834d4ddc22e15aef24a1766afda92e0852cf9b7 100644
--- a/src/client/app/common/views/components/poll.vue
+++ b/src/client/app/common/views/components/poll.vue
@@ -1,19 +1,19 @@
 <template>
 <div class="mk-poll" :data-is-voted="isVoted">
 	<ul>
-		<li v-for="choice in poll.choices" :key="choice.id" @click="vote(choice.id)" :class="{ voted: choice.voted }" :title="!isVoted ? '%i18n:@vote-to%'.replace('{}', choice.text) : ''">
+		<li v-for="choice in poll.choices" :key="choice.id" @click="vote(choice.id)" :class="{ voted: choice.voted }" :title="!isVoted ? '%i18n:!@vote-to%'.replace('{}', choice.text) : ''">
 			<div class="backdrop" :style="{ 'width': (showResult ? (choice.votes / total * 100) : 0) + '%' }"></div>
 			<span>
 				<template v-if="choice.isVoted">%fa:check%</template>
 				<span>{{ choice.text }}</span>
-				<span class="votes" v-if="showResult">({{ '%i18n:@vote-count%'.replace('{}', choice.votes) }})</span>
+				<span class="votes" v-if="showResult">({{ '%i18n:!@vote-count%'.replace('{}', choice.votes) }})</span>
 			</span>
 		</li>
 	</ul>
 	<p v-if="total > 0">
-		<span>{{ '%i18n:@total-users%'.replace('{}', total) }}</span>
+		<span>{{ '%i18n:!@total-users%'.replace('{}', total) }}</span>
 		<span>・</span>
-		<a v-if="!isVoted" @click="toggleShowResult">{{ showResult ? '%i18n:@vote%' : '%i18n:@show-result%' }}</a>
+		<a v-if="!isVoted" @click="toggleShowResult">{{ showResult ? '%i18n:!@vote%' : '%i18n:!@show-result%' }}</a>
 		<span v-if="isVoted">%i18n:@voted%</span>
 	</p>
 </div>
diff --git a/src/client/app/common/views/components/reaction-picker.vue b/src/client/app/common/views/components/reaction-picker.vue
index 631ed131331d4edca3d3a75887d4a5d5e0715027..267eeb3a1431084a77fbd5ffa2b8f7561fc731dd 100644
--- a/src/client/app/common/views/components/reaction-picker.vue
+++ b/src/client/app/common/views/components/reaction-picker.vue
@@ -22,7 +22,7 @@
 import Vue from 'vue';
 import * as anime from 'animejs';
 
-const placeholder = '%i18n:@choose-reaction%';
+const placeholder = '%i18n:!@choose-reaction%';
 
 export default Vue.extend({
 	props: ['note', 'source', 'compact', 'cb'],
diff --git a/src/client/app/common/views/components/signin.vue b/src/client/app/common/views/components/signin.vue
index 4b114eb750e0b5644485351859bd4c1cdd5aa63e..25f90a2f13c28e13e5f7724583a1b8eb2f00804c 100644
--- a/src/client/app/common/views/components/signin.vue
+++ b/src/client/app/common/views/components/signin.vue
@@ -9,7 +9,7 @@
 	<label class="token" v-if="user && user.twoFactorEnabled">
 		<input v-model="token" type="number" placeholder="%i18n:@token%" required/>%fa:lock%
 	</label>
-	<button type="submit" :disabled="signing">{{ signing ? '%i18n:@signing-in%' : '%i18n:@signin%' }}</button>
+	<button type="submit" :disabled="signing">{{ signing ? '%i18n:!@signing-in%' : '%i18n:!@signin%' }}</button>
 	もしくは <a :href="`${apiUrl}/signin/twitter`">Twitterでログイン</a>
 </form>
 </template>
diff --git a/src/client/app/common/views/components/signup.vue b/src/client/app/common/views/components/signup.vue
index b998e6f42ac9dc658daefc26b222801c2df0eb47..33a559ff8fb41457bddcb3993b295a5118b75ca6 100644
--- a/src/client/app/common/views/components/signup.vue
+++ b/src/client/app/common/views/components/signup.vue
@@ -127,7 +127,7 @@ export default Vue.extend({
 					location.href = '/';
 				});
 			}).catch(() => {
-				alert('%i18n:@some-error%');
+				alert('%i18n:!@some-error%');
 
 				(window as any).grecaptcha.reset();
 				this.recaptchaed = false;
diff --git a/src/client/app/common/views/components/time.vue b/src/client/app/common/views/components/time.vue
index 6e0d2b0dcb00955e73a7c3be0b649acfdf73fc3b..533958697c728215175ce05e14eed98dbb8726a9 100644
--- a/src/client/app/common/views/components/time.vue
+++ b/src/client/app/common/views/components/time.vue
@@ -44,16 +44,16 @@ export default Vue.extend({
 			const time = this._time;
 			const ago = (this.now.getTime() - time.getTime()) / 1000/*ms*/;
 			return (
-				ago >= 31536000 ? '%i18n:common.time.years_ago%'  .replace('{}', (~~(ago / 31536000)).toString()) :
-				ago >= 2592000  ? '%i18n:common.time.months_ago%' .replace('{}', (~~(ago / 2592000)).toString()) :
-				ago >= 604800   ? '%i18n:common.time.weeks_ago%'  .replace('{}', (~~(ago / 604800)).toString()) :
-				ago >= 86400    ? '%i18n:common.time.days_ago%'   .replace('{}', (~~(ago / 86400)).toString()) :
-				ago >= 3600     ? '%i18n:common.time.hours_ago%'  .replace('{}', (~~(ago / 3600)).toString()) :
-				ago >= 60       ? '%i18n:common.time.minutes_ago%'.replace('{}', (~~(ago / 60)).toString()) :
-				ago >= 10       ? '%i18n:common.time.seconds_ago%'.replace('{}', (~~(ago % 60)).toString()) :
-				ago >= 0        ? '%i18n:common.time.just_now%' :
-				ago <  0        ? '%i18n:common.time.future%' :
-				'%i18n:common.time.unknown%');
+				ago >= 31536000 ? '%i18n:!common.time.years_ago%'  .replace('{}', (~~(ago / 31536000)).toString()) :
+				ago >= 2592000  ? '%i18n:!common.time.months_ago%' .replace('{}', (~~(ago / 2592000)).toString()) :
+				ago >= 604800   ? '%i18n:!common.time.weeks_ago%'  .replace('{}', (~~(ago / 604800)).toString()) :
+				ago >= 86400    ? '%i18n:!common.time.days_ago%'   .replace('{}', (~~(ago / 86400)).toString()) :
+				ago >= 3600     ? '%i18n:!common.time.hours_ago%'  .replace('{}', (~~(ago / 3600)).toString()) :
+				ago >= 60       ? '%i18n:!common.time.minutes_ago%'.replace('{}', (~~(ago / 60)).toString()) :
+				ago >= 10       ? '%i18n:!common.time.seconds_ago%'.replace('{}', (~~(ago % 60)).toString()) :
+				ago >= 0        ? '%i18n:!common.time.just_now%' :
+				ago <  0        ? '%i18n:!common.time.future%' :
+				'%i18n:!common.time.unknown%');
 		}
 	},
 	created() {
diff --git a/src/client/app/common/views/components/twitter-setting.vue b/src/client/app/common/views/components/twitter-setting.vue
index 77788290f6e6c55c823969571ea2caf70c846e03..6ca1037aba4b4538407d2e728d285b2a55695d6b 100644
--- a/src/client/app/common/views/components/twitter-setting.vue
+++ b/src/client/app/common/views/components/twitter-setting.vue
@@ -3,7 +3,7 @@
 	<p>%i18n:@description%<a :href="`${docsUrl}/link-to-twitter`" target="_blank">%i18n:@detail%</a></p>
 	<p class="account" v-if="os.i.twitter" :title="`Twitter ID: ${os.i.twitter.userId}`">%i18n:@connected-to%: <a :href="`https://twitter.com/${os.i.twitter.screenName}`" target="_blank">@{{ os.i.twitter.screenName }}</a></p>
 	<p>
-		<a :href="`${apiUrl}/connect/twitter`" target="_blank" @click.prevent="connect">{{ os.i.twitter ? '%i18n:@reconnect%' : '%i18n:@connect%' }}</a>
+		<a :href="`${apiUrl}/connect/twitter`" target="_blank" @click.prevent="connect">{{ os.i.twitter ? '%i18n:!@reconnect%' : '%i18n:!@connect%' }}</a>
 		<span v-if="os.i.twitter"> or </span>
 		<a :href="`${apiUrl}/disconnect/twitter`" target="_blank" v-if="os.i.twitter" @click.prevent="disconnect">%i18n:@disconnect%</a>
 	</p>
diff --git a/src/client/app/common/views/widgets/broadcast.vue b/src/client/app/common/views/widgets/broadcast.vue
index 6e11ec29e30af29b18cf521c4486eca0fdcfa769..96d1d0ef3ac68bf70eb0daf4c75c32d2f5381a50 100644
--- a/src/client/app/common/views/widgets/broadcast.vue
+++ b/src/client/app/common/views/widgets/broadcast.vue
@@ -14,7 +14,7 @@
 		</svg>
 	</div>
 	<p class="fetching" v-if="fetching">%i18n:@fetching%<mk-ellipsis/></p>
-	<h1 v-if="!fetching">{{ broadcasts.length == 0 ? '%i18n:@no-broadcasts%' : broadcasts[i].title }}</h1>
+	<h1 v-if="!fetching">{{ broadcasts.length == 0 ? '%i18n:!@no-broadcasts%' : broadcasts[i].title }}</h1>
 	<p v-if="!fetching">
 		<span v-if="broadcasts.length != 0" v-html="broadcasts[i].text"></span>
 		<template v-if="broadcasts.length == 0">%i18n:@have-a-nice-day%</template>
diff --git a/src/client/app/common/views/widgets/donation.vue b/src/client/app/common/views/widgets/donation.vue
index 0b0145fa0bab3d176c694aa5b19cb88b5c3cd1ef..6b5a6697edd4878fe16b77312035566e59db6778 100644
--- a/src/client/app/common/views/widgets/donation.vue
+++ b/src/client/app/common/views/widgets/donation.vue
@@ -3,9 +3,9 @@
 	<article>
 		<h1>%fa:heart%%i18n:@title%</h1>
 		<p>
-			{{ '%i18n:@text%'.substr(0, '%i18n:@text%'.indexOf('{')) }}
+			{{ '%i18n:!@text%'.substr(0, '%i18n:!@text%'.indexOf('{')) }}
 			<a href="https://syuilo.com">@syuilo</a>
-			{{ '%i18n:@text%'.substr('%i18n:@text%'.indexOf('}') + 1) }}
+			{{ '%i18n:!@text%'.substr('%i18n:!@text%'.indexOf('}') + 1) }}
 		</p>
 	</article>
 </div>
diff --git a/src/client/app/desktop/views/components/calendar.vue b/src/client/app/desktop/views/components/calendar.vue
index 7f0052af5c904ddb5bc8928f4dc88910fa85244e..a99b48d195b2f3832bc636d5882f14854673fe24 100644
--- a/src/client/app/desktop/views/components/calendar.vue
+++ b/src/client/app/desktop/views/components/calendar.vue
@@ -2,7 +2,7 @@
 <div class="mk-calendar" :data-melt="design == 4 || design == 5">
 	<template v-if="design == 0 || design == 1">
 		<button @click="prev" title="%i18n:@prev%">%fa:chevron-circle-left%</button>
-		<p class="title">{{ '%i18n:@title%'.replace('{1}', year).replace('{2}', month) }}</p>
+		<p class="title">{{ '%i18n:!@title%'.replace('{1}', year).replace('{2}', month) }}</p>
 		<button @click="next" title="%i18n:@next%">%fa:chevron-circle-right%</button>
 	</template>
 
@@ -21,7 +21,7 @@
 			:data-is-out-of-range="isOutOfRange(i + 1)"
 			:data-is-donichi="isDonichi(i + 1)"
 			@click="go(i + 1)"
-			:title="isOutOfRange(i + 1) ? null : '%i18n:@go%'"
+			:title="isOutOfRange(i + 1) ? null : '%i18n:!@go%'"
 		>
 			<div>{{ i + 1 }}</div>
 		</div>
@@ -58,13 +58,13 @@ export default Vue.extend({
 			month: new Date().getMonth() + 1,
 			selected: new Date(),
 			weekdayText: [
-				'%i18n:common.weekday-short.sunday%',
-				'%i18n:common.weekday-short.monday%',
-				'%i18n:common.weekday-short.tuesday%',
-				'%i18n:common.weekday-short.wednesday%',
-				'%i18n:common.weekday-short.thursday%',
-				'%i18n:common.weekday-short.friday%',
-				'%i18n:common.weekday-short.satruday%'
+				'%i18n:!common.weekday-short.sunday%',
+				'%i18n:!common.weekday-short.monday%',
+				'%i18n:!common.weekday-short.tuesday%',
+				'%i18n:!common.weekday-short.wednesday%',
+				'%i18n:!common.weekday-short.thursday%',
+				'%i18n:!common.weekday-short.friday%',
+				'%i18n:!common.weekday-short.satruday%'
 			]
 		};
 	},
diff --git a/src/client/app/desktop/views/components/drive.file.vue b/src/client/app/desktop/views/components/drive.file.vue
index 5ad81322bf6f91616debe18000b884b98d37a185..d79cb6c09c6c35cfe1ec0049006197b39fe67303 100644
--- a/src/client/app/desktop/views/components/drive.file.vue
+++ b/src/client/app/desktop/views/components/drive.file.vue
@@ -64,46 +64,46 @@ export default Vue.extend({
 			this.isContextmenuShowing = true;
 			contextmenu(e, [{
 				type: 'item',
-				text: '%i18n:@contextmenu.rename%',
+				text: '%i18n:!@contextmenu.rename%',
 				icon: '%fa:i-cursor%',
 				onClick: this.rename
 			}, {
 				type: 'item',
-				text: '%i18n:@contextmenu.copy-url%',
+				text: '%i18n:!@contextmenu.copy-url%',
 				icon: '%fa:link%',
 				onClick: this.copyUrl
 			}, {
 				type: 'link',
 				href: `${this.file.url}?download`,
-				text: '%i18n:@contextmenu.download%',
+				text: '%i18n:!@contextmenu.download%',
 				icon: '%fa:download%',
 			}, {
 				type: 'divider',
 			}, {
 				type: 'item',
-				text: '%i18n:common.delete%',
+				text: '%i18n:!common.delete%',
 				icon: '%fa:R trash-alt%',
 				onClick: this.deleteFile
 			}, {
 				type: 'divider',
 			}, {
 				type: 'nest',
-				text: '%i18n:@contextmenu.else-files%',
+				text: '%i18n:!@contextmenu.else-files%',
 				menu: [{
 					type: 'item',
-					text: '%i18n:@contextmenu.set-as-avatar%',
+					text: '%i18n:!@contextmenu.set-as-avatar%',
 					onClick: this.setAsAvatar
 				}, {
 					type: 'item',
-					text: '%i18n:@contextmenu.set-as-banner%',
+					text: '%i18n:!@contextmenu.set-as-banner%',
 					onClick: this.setAsBanner
 				}]
 			}, {
 				type: 'nest',
-				text: '%i18n:@contextmenu.open-in-app%',
+				text: '%i18n:!@contextmenu.open-in-app%',
 				menu: [{
 					type: 'item',
-					text: '%i18n:@contextmenu.add-app%...',
+					text: '%i18n:!@contextmenu.add-app%...',
 					onClick: this.addApp
 				}]
 			}], {
@@ -141,8 +141,8 @@ export default Vue.extend({
 
 		rename() {
 			(this as any).apis.input({
-				title: '%i18n:@contextmenu.rename-file%',
-				placeholder: '%i18n:@contextmenu.input-new-file-name%',
+				title: '%i18n:!@contextmenu.rename-file%',
+				placeholder: '%i18n:!@contextmenu.input-new-file-name%',
 				default: this.file.name,
 				allowEmpty: false
 			}).then(name => {
@@ -157,9 +157,9 @@ export default Vue.extend({
 			copyToClipboard(this.file.url);
 			(this as any).apis.dialog({
 				title: '%fa:check%%i18n:@contextmenu.copied%',
-				text: '%i18n:@contextmenu.copied-url-to-clipboard%',
+				text: '%i18n:!@contextmenu.copied-url-to-clipboard%',
 				actions: [{
-					text: '%i18n:common.ok%'
+					text: '%i18n:!common.ok%'
 				}]
 			});
 		},
diff --git a/src/client/app/desktop/views/components/drive.folder.vue b/src/client/app/desktop/views/components/drive.folder.vue
index 16f474f4e0acc8443319f45790865722188ec57f..0761ffb1a1fa219054c5de1696210889eba880ab 100644
--- a/src/client/app/desktop/views/components/drive.folder.vue
+++ b/src/client/app/desktop/views/components/drive.folder.vue
@@ -54,26 +54,26 @@ export default Vue.extend({
 			this.isContextmenuShowing = true;
 			contextmenu(e, [{
 				type: 'item',
-				text: '%i18n:@contextmenu.move-to-this-folder%',
+				text: '%i18n:!@contextmenu.move-to-this-folder%',
 				icon: '%fa:arrow-right%',
 				onClick: this.go
 			}, {
 				type: 'item',
-				text: '%i18n:@contextmenu.show-in-new-window%',
+				text: '%i18n:!@contextmenu.show-in-new-window%',
 				icon: '%fa:R window-restore%',
 				onClick: this.newWindow
 			}, {
 				type: 'divider',
 			}, {
 				type: 'item',
-				text: '%i18n:@contextmenu.rename%',
+				text: '%i18n:!@contextmenu.rename%',
 				icon: '%fa:i-cursor%',
 				onClick: this.rename
 			}, {
 				type: 'divider',
 			}, {
 				type: 'item',
-				text: '%i18n:common.delete%',
+				text: '%i18n:!common.delete%',
 				icon: '%fa:R trash-alt%',
 				onClick: this.deleteFolder
 			}], {
@@ -159,15 +159,15 @@ export default Vue.extend({
 					switch (err) {
 						case 'detected-circular-definition':
 							(this as any).apis.dialog({
-								title: '%fa:exclamation-triangle%%i18n:@unable-to-process%',
-								text: '%i18n:@circular-reference-detected%',
+								title: '%fa:exclamation-triangle%%i18n:!@unable-to-process%',
+								text: '%i18n:!@circular-reference-detected%',
 								actions: [{
-									text: '%i18n:common.ok%'
+									text: '%i18n:!common.ok%'
 								}]
 							});
 							break;
 						default:
-							alert('%i18n:@unhandled-error% ' + err);
+							alert('%i18n:!@unhandled-error% ' + err);
 					}
 				});
 			}
@@ -199,8 +199,8 @@ export default Vue.extend({
 
 		rename() {
 			(this as any).apis.input({
-				title: '%i18n:@contextmenu.rename-folder%',
-				placeholder: '%i18n:@contextmenu.input-new-folder-name%',
+				title: '%i18n:!@contextmenu.rename-folder%',
+				placeholder: '%i18n:!@contextmenu.input-new-folder-name%',
 				default: this.folder.name
 			}).then(name => {
 				(this as any).api('drive/folders/update', {
diff --git a/src/client/app/desktop/views/components/drive.nav-folder.vue b/src/client/app/desktop/views/components/drive.nav-folder.vue
index 40f620875e042c9edf51f2477294c2c74048b3c3..71b2e419d98518c82e3ed20c5de6e73fd6ab94f6 100644
--- a/src/client/app/desktop/views/components/drive.nav-folder.vue
+++ b/src/client/app/desktop/views/components/drive.nav-folder.vue
@@ -8,7 +8,7 @@
 	@drop.stop="onDrop"
 >
 	<template v-if="folder == null">%fa:cloud%</template>
-	<span>{{ folder == null ? '%i18n:@drive%' : folder.name }}</span>
+	<span>{{ folder == null ? '%i18n:!@drive%' : folder.name }}</span>
 </div>
 </template>
 
diff --git a/src/client/app/desktop/views/components/drive.vue b/src/client/app/desktop/views/components/drive.vue
index abdf1338b4c074f6cd2f4aa8321a7cb5125c517a..5e91048d19fc4afe9246be8d5c1bda436ed906e0 100644
--- a/src/client/app/desktop/views/components/drive.vue
+++ b/src/client/app/desktop/views/components/drive.vue
@@ -138,17 +138,17 @@ export default Vue.extend({
 		onContextmenu(e) {
 			contextmenu(e, [{
 				type: 'item',
-				text: '%i18n:@contextmenu.create-folder%',
+				text: '%i18n:!@contextmenu.create-folder%',
 				icon: '%fa:R folder%',
 				onClick: this.createFolder
 			}, {
 				type: 'item',
-				text: '%i18n:@contextmenu.upload%',
+				text: '%i18n:!@contextmenu.upload%',
 				icon: '%fa:upload%',
 				onClick: this.selectLocalFile
 			}, {
 				type: 'item',
-				text: '%i18n:@contextmenu.url-upload%',
+				text: '%i18n:!@contextmenu.url-upload%',
 				icon: '%fa:cloud-upload-alt%',
 				onClick: this.urlUpload
 			}]);
@@ -306,15 +306,15 @@ export default Vue.extend({
 					switch (err) {
 						case 'detected-circular-definition':
 							(this as any).apis.dialog({
-								title: '%fa:exclamation-triangle%%i18n:@unable-to-process%',
-								text: '%i18n:@circular-reference-detected%',
+								title: '%fa:exclamation-triangle%%i18n:!@unable-to-process%',
+								text: '%i18n:!@circular-reference-detected%',
 								actions: [{
-									text: '%i18n:common.ok%'
+									text: '%i18n:!common.ok%'
 								}]
 							});
 							break;
 						default:
-							alert('%i18n:@unhandled-error% ' + err);
+							alert('%i18n:!@unhandled-error% ' + err);
 					}
 				});
 			}
@@ -327,8 +327,8 @@ export default Vue.extend({
 
 		urlUpload() {
 			(this as any).apis.input({
-				title: '%i18n:@url-upload%',
-				placeholder: '%i18n:@url-of-file%'
+				title: '%i18n:!@url-upload%',
+				placeholder: '%i18n:!@url-of-file%'
 			}).then(url => {
 				(this as any).api('drive/files/upload_from_url', {
 					url: url,
@@ -337,9 +337,9 @@ export default Vue.extend({
 
 				(this as any).apis.dialog({
 					title: '%fa:check%%i18n:@url-upload-requested%',
-					text: '%i18n:@may-take-time%',
+					text: '%i18n:!@may-take-time%',
 					actions: [{
-						text: '%i18n:common.ok%'
+						text: '%i18n:!common.ok%'
 					}]
 				});
 			});
@@ -347,8 +347,8 @@ export default Vue.extend({
 
 		createFolder() {
 			(this as any).apis.input({
-				title: '%i18n:@create-folder%',
-				placeholder: '%i18n:@folder-name%'
+				title: '%i18n:!@create-folder%',
+				placeholder: '%i18n:!@folder-name%'
 			}).then(name => {
 				(this as any).api('drive/folders/create', {
 					name: name,
diff --git a/src/client/app/desktop/views/components/notes.note.vue b/src/client/app/desktop/views/components/notes.note.vue
index 90f9e98a5c8e5744a7176bfeca4f602c5c45246c..326ec4dc8906dee72eef0261e8c3b92b65a1f5de 100644
--- a/src/client/app/desktop/views/components/notes.note.vue
+++ b/src/client/app/desktop/views/components/notes.note.vue
@@ -9,9 +9,9 @@
 				<img class="avatar" :src="`${note.user.avatarUrl}?thumbnail&size=32`" alt="avatar"/>
 			</router-link>
 			%fa:retweet%
-			<span>{{ '%i18n:@reposted-by%'.substr(0, '%i18n:@reposted-by%'.indexOf('{')) }}</span>
+			<span>{{ '%i18n:!@reposted-by%'.substr(0, '%i18n:!@reposted-by%'.indexOf('{')) }}</span>
 			<a class="name" :href="note.user | userPage" v-user-preview="note.userId">{{ note.user | userName }}</a>
-			<span>{{ '%i18n:@reposted-by%'.substr('%i18n:@reposted-by%'.indexOf('}') + 1) }}</span>
+			<span>{{ '%i18n:!@reposted-by%'.substr('%i18n:!@reposted-by%'.indexOf('}') + 1) }}</span>
 		</p>
 		<mk-time :time="note.createdAt"/>
 	</div>
diff --git a/src/client/app/desktop/views/components/notifications.vue b/src/client/app/desktop/views/components/notifications.vue
index c1d4c561e8fe98614dde201548d1cca803319178..413a87755a5038b8b594fb211b403941b759d782 100644
--- a/src/client/app/desktop/views/components/notifications.vue
+++ b/src/client/app/desktop/views/components/notifications.vue
@@ -93,7 +93,7 @@
 		</template>
 	</div>
 	<button class="more" :class="{ fetching: fetchingMoreNotifications }" v-if="moreNotifications" @click="fetchMoreNotifications" :disabled="fetchingMoreNotifications">
-		<template v-if="fetchingMoreNotifications">%fa:spinner .pulse .fw%</template>{{ fetchingMoreNotifications ? '%i18n:common.loading%' : '%i18n:@more%' }}
+		<template v-if="fetchingMoreNotifications">%fa:spinner .pulse .fw%</template>{{ fetchingMoreNotifications ? '%i18n:!common.loading%' : '%i18n:!@more%' }}
 	</button>
 	<p class="empty" v-if="notifications.length == 0 && !fetching">%i18n:@empty%</p>
 	<p class="loading" v-if="fetching">%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p>
diff --git a/src/client/app/desktop/views/components/post-form-window.vue b/src/client/app/desktop/views/components/post-form-window.vue
index 18bb39f9bce26dbf97d1cd9222b714e333ea7d64..1f0fbff760d3e01a885357025cc3902c45483837 100644
--- a/src/client/app/desktop/views/components/post-form-window.vue
+++ b/src/client/app/desktop/views/components/post-form-window.vue
@@ -4,8 +4,8 @@
 		<span :class="$style.icon" v-if="geo">%fa:map-marker-alt%</span>
 		<span v-if="!reply">%i18n:@note%</span>
 		<span v-if="reply">%i18n:@reply%</span>
-		<span :class="$style.count" v-if="media.length != 0">{{ '%i18n:@attaches%'.replace('{}', media.length) }}</span>
-		<span :class="$style.count" v-if="uploadings.length != 0">{{ '%i18n:@uploading-media%'.replace('{}', uploadings.length) }}<mk-ellipsis/></span>
+		<span :class="$style.count" v-if="media.length != 0">{{ '%i18n:!@attaches%'.replace('{}', media.length) }}</span>
+		<span :class="$style.count" v-if="uploadings.length != 0">{{ '%i18n:!@uploading-media%'.replace('{}', uploadings.length) }}<mk-ellipsis/></span>
 	</span>
 
 	<mk-note-preview v-if="reply" :class="$style.notePreview" :note="reply"/>
diff --git a/src/client/app/desktop/views/components/post-form.vue b/src/client/app/desktop/views/components/post-form.vue
index 6071bbe31f5ce9bf94b48c0422472655965b24fc..ebb01930885713deda6af898f18bb29907264e6b 100644
--- a/src/client/app/desktop/views/components/post-form.vue
+++ b/src/client/app/desktop/views/components/post-form.vue
@@ -28,9 +28,9 @@
 	<button class="kao" title="%i18n:@insert-a-kao%" @click="kao">%fa:R smile%</button>
 	<button class="poll" title="%i18n:@create-poll%" @click="poll = true">%fa:chart-pie%</button>
 	<button class="geo" title="位置情報を添付する" @click="geo ? removeGeo() : setGeo()">%fa:map-marker-alt%</button>
-	<p class="text-count" :class="{ over: text.length > 1000 }">{{ '%i18n:@text-remain%'.replace('{}', 1000 - text.length) }}</p>
+	<p class="text-count" :class="{ over: text.length > 1000 }">{{ '%i18n:!@text-remain%'.replace('{}', 1000 - text.length) }}</p>
 	<button :class="{ posting }" class="submit" :disabled="!canPost" @click="post">
-		{{ posting ? '%i18n:@posting%' : submitText }}<mk-ellipsis v-if="posting"/>
+		{{ posting ? '%i18n:!@posting%' : submitText }}<mk-ellipsis v-if="posting"/>
 	</button>
 	<input ref="file" type="file" accept="image/*" multiple="multiple" tabindex="-1" @change="onChangeFile"/>
 	<div class="dropzone" v-if="draghover"></div>
@@ -69,17 +69,17 @@ export default Vue.extend({
 		},
 		placeholder(): string {
 			return this.renote
-				? '%i18n:@quote-placeholder%'
+				? '%i18n:!@quote-placeholder%'
 				: this.reply
-					? '%i18n:@reply-placeholder%'
-					: '%i18n:@note-placeholder%';
+					? '%i18n:!@reply-placeholder%'
+					: '%i18n:!@note-placeholder%';
 		},
 		submitText(): string {
 			return this.renote
-				? '%i18n:@renote%'
+				? '%i18n:!@renote%'
 				: this.reply
-					? '%i18n:@reply%'
-					: '%i18n:@note%';
+					? '%i18n:!@reply%'
+					: '%i18n:!@note%';
 		},
 		canPost(): boolean {
 			return !this.posting && (this.text.length != 0 || this.files.length != 0 || this.poll || this.renote);
@@ -236,16 +236,16 @@ export default Vue.extend({
 				this.deleteDraft();
 				this.$emit('posted');
 				(this as any).apis.notify(this.renote
-					? '%i18n:@reposted%'
+					? '%i18n:!@reposted%'
 					: this.reply
-						? '%i18n:@replied%'
-						: '%i18n:@posted%');
+						? '%i18n:!@replied%'
+						: '%i18n:!@posted%');
 			}).catch(err => {
 				(this as any).apis.notify(this.renote
-					? '%i18n:@renote-failed%'
+					? '%i18n:!@renote-failed%'
 					: this.reply
-						? '%i18n:@reply-failed%'
-						: '%i18n:@note-failed%');
+						? '%i18n:!@reply-failed%'
+						: '%i18n:!@note-failed%');
 			}).then(() => {
 				this.posting = false;
 			});
diff --git a/src/client/app/desktop/views/components/renote-form.vue b/src/client/app/desktop/views/components/renote-form.vue
index 1f947a71dede2321e76d4fdb1f9ad096abb267b2..daae5df5e96a9e957ebce616ad5df0d5efa46cad 100644
--- a/src/client/app/desktop/views/components/renote-form.vue
+++ b/src/client/app/desktop/views/components/renote-form.vue
@@ -5,7 +5,7 @@
 		<footer>
 			<a class="quote" v-if="!quote" @click="onQuote">%i18n:@quote%</a>
 			<button class="cancel" @click="cancel">%i18n:@cancel%</button>
-			<button class="ok" @click="ok" :disabled="wait">{{ wait ? '%i18n:@reposting%' : '%i18n:@renote%' }}</button>
+			<button class="ok" @click="ok" :disabled="wait">{{ wait ? '%i18n:!@reposting%' : '%i18n:!@renote%' }}</button>
 		</footer>
 	</template>
 	<template v-if="quote">
@@ -32,9 +32,9 @@ export default Vue.extend({
 				renoteId: this.note.id
 			}).then(data => {
 				this.$emit('posted');
-				(this as any).apis.notify('%i18n:@success%');
+				(this as any).apis.notify('%i18n:!@success%');
 			}).catch(err => {
-				(this as any).apis.notify('%i18n:@failure%');
+				(this as any).apis.notify('%i18n:!@failure%');
 			}).then(() => {
 				this.wait = false;
 			});
diff --git a/src/client/app/desktop/views/components/repost-form.vue b/src/client/app/desktop/views/components/repost-form.vue
new file mode 100644
index 0000000000000000000000000000000000000000..d5b16967572c575746be176bf9be9465f8adb32b
--- /dev/null
+++ b/src/client/app/desktop/views/components/repost-form.vue
@@ -0,0 +1,131 @@
+<template>
+<div class="mk-renote-form">
+	<mk-note-preview :note="note"/>
+	<template v-if="!quote">
+		<footer>
+			<a class="quote" v-if="!quote" @click="onQuote">%i18n:desktop.tags.mk-renote-form.quote%</a>
+			<button class="cancel" @click="cancel">%i18n:desktop.tags.mk-renote-form.cancel%</button>
+			<button class="ok" @click="ok" :disabled="wait">{{ wait ? '%i18n:!desktop.tags.mk-renote-form.reposting%' : '%i18n:!desktop.tags.mk-renote-form.renote%' }}</button>
+		</footer>
+	</template>
+	<template v-if="quote">
+		<mk-post-form ref="form" :renote="note" @posted="onChildFormPosted"/>
+	</template>
+</div>
+</template>
+
+<script lang="ts">
+import Vue from 'vue';
+
+export default Vue.extend({
+	props: ['note'],
+	data() {
+		return {
+			wait: false,
+			quote: false
+		};
+	},
+	methods: {
+		ok() {
+			this.wait = true;
+			(this as any).api('notes/create', {
+				renoteId: this.note.id
+			}).then(data => {
+				this.$emit('posted');
+				(this as any).apis.notify('%i18n:!desktop.tags.mk-renote-form.success%');
+			}).catch(err => {
+				(this as any).apis.notify('%i18n:!desktop.tags.mk-renote-form.failure%');
+			}).then(() => {
+				this.wait = false;
+			});
+		},
+		cancel() {
+			this.$emit('canceled');
+		},
+		onQuote() {
+			this.quote = true;
+
+			this.$nextTick(() => {
+				(this.$refs.form as any).focus();
+			});
+		},
+		onChildFormPosted() {
+			this.$emit('posted');
+		}
+	}
+});
+</script>
+
+<style lang="stylus" scoped>
+@import '~const.styl'
+
+.mk-renote-form
+
+	> .mk-note-preview
+		margin 16px 22px
+
+	> footer
+		height 72px
+		background lighten($theme-color, 95%)
+
+		> .quote
+			position absolute
+			bottom 16px
+			left 28px
+			line-height 40px
+
+		button
+			display block
+			position absolute
+			bottom 16px
+			cursor pointer
+			padding 0
+			margin 0
+			width 120px
+			height 40px
+			font-size 1em
+			outline none
+			border-radius 4px
+
+			&:focus
+				&:after
+					content ""
+					pointer-events none
+					position absolute
+					top -5px
+					right -5px
+					bottom -5px
+					left -5px
+					border 2px solid rgba($theme-color, 0.3)
+					border-radius 8px
+
+		> .cancel
+			right 148px
+			color #888
+			background linear-gradient(to bottom, #ffffff 0%, #f5f5f5 100%)
+			border solid 1px #e2e2e2
+
+			&:hover
+				background linear-gradient(to bottom, #f9f9f9 0%, #ececec 100%)
+				border-color #dcdcdc
+
+			&:active
+				background #ececec
+				border-color #dcdcdc
+
+		> .ok
+			right 16px
+			font-weight bold
+			color $theme-color-foreground
+			background linear-gradient(to bottom, lighten($theme-color, 25%) 0%, lighten($theme-color, 10%) 100%)
+			border solid 1px lighten($theme-color, 15%)
+
+			&:hover
+				background linear-gradient(to bottom, lighten($theme-color, 8%) 0%, darken($theme-color, 8%) 100%)
+				border-color $theme-color
+
+			&:active
+				background $theme-color
+				border-color $theme-color
+
+</style>
diff --git a/src/client/app/desktop/views/components/settings.2fa.vue b/src/client/app/desktop/views/components/settings.2fa.vue
index 0809dd798c1012349d9e60e9cbadd79f64da319a..99b6cb947c7114c987dbfd843ce89cdb98c84909 100644
--- a/src/client/app/desktop/views/components/settings.2fa.vue
+++ b/src/client/app/desktop/views/components/settings.2fa.vue
@@ -34,7 +34,7 @@ export default Vue.extend({
 	methods: {
 		register() {
 			(this as any).apis.input({
-				title: '%i18n:@enter-password%',
+				title: '%i18n:!@enter-password%',
 				type: 'password'
 			}).then(password => {
 				(this as any).api('i/2fa/register', {
@@ -47,13 +47,13 @@ export default Vue.extend({
 
 		unregister() {
 			(this as any).apis.input({
-				title: '%i18n:@enter-password%',
+				title: '%i18n:!@enter-password%',
 				type: 'password'
 			}).then(password => {
 				(this as any).api('i/2fa/unregister', {
 					password: password
 				}).then(() => {
-					(this as any).apis.notify('%i18n:@unregistered%');
+					(this as any).apis.notify('%i18n:!@unregistered%');
 					(this as any).os.i.twoFactorEnabled = false;
 				});
 			});
@@ -63,10 +63,10 @@ export default Vue.extend({
 			(this as any).api('i/2fa/done', {
 				token: this.token
 			}).then(() => {
-				(this as any).apis.notify('%i18n:@success%');
+				(this as any).apis.notify('%i18n:!@success%');
 				(this as any).os.i.twoFactorEnabled = true;
 			}).catch(() => {
-				(this as any).apis.notify('%i18n:@failed%');
+				(this as any).apis.notify('%i18n:!@failed%');
 			});
 		}
 	}
diff --git a/src/client/app/desktop/views/components/settings.api.vue b/src/client/app/desktop/views/components/settings.api.vue
index 6ae29cb58aa0fab642d276ca4e728b77e10c486b..a43c6e8ea6381db451edb841bdaf26bb813263bd 100644
--- a/src/client/app/desktop/views/components/settings.api.vue
+++ b/src/client/app/desktop/views/components/settings.api.vue
@@ -15,7 +15,7 @@ export default Vue.extend({
 	methods: {
 		regenerateToken() {
 			(this as any).apis.input({
-				title: '%i18n:@enter-password%',
+				title: '%i18n:!@enter-password%',
 				type: 'password'
 			}).then(password => {
 				(this as any).api('i/regenerate_token', {
diff --git a/src/client/app/desktop/views/components/settings.password.vue b/src/client/app/desktop/views/components/settings.password.vue
index 39896daf67ce058d753b92c34ab1d9211d746576..9e89bc0f6e871fd859da52ac0b29abf5e3f09519 100644
--- a/src/client/app/desktop/views/components/settings.password.vue
+++ b/src/client/app/desktop/views/components/settings.password.vue
@@ -11,21 +11,21 @@ export default Vue.extend({
 	methods: {
 		reset() {
 			(this as any).apis.input({
-				title: '%i18n:@enter-current-password%',
+				title: '%i18n:!@enter-current-password%',
 				type: 'password'
 			}).then(currentPassword => {
 				(this as any).apis.input({
-					title: '%i18n:@enter-new-password%',
+					title: '%i18n:!@enter-new-password%',
 					type: 'password'
 				}).then(newPassword => {
 					(this as any).apis.input({
-						title: '%i18n:@enter-new-password-again%',
+						title: '%i18n:!@enter-new-password-again%',
 						type: 'password'
 					}).then(newPassword2 => {
 						if (newPassword !== newPassword2) {
 							(this as any).apis.dialog({
 								title: null,
-								text: '%i18n:@not-match%',
+								text: '%i18n:!@not-match%',
 								actions: [{
 									text: 'OK'
 								}]
@@ -36,7 +36,7 @@ export default Vue.extend({
 							currentPasword: currentPassword,
 							newPassword: newPassword
 						}).then(() => {
-							(this as any).apis.notify('%i18n:@changed%');
+							(this as any).apis.notify('%i18n:!@changed%');
 						});
 					});
 				});
diff --git a/src/client/app/desktop/views/pages/selectdrive.vue b/src/client/app/desktop/views/pages/selectdrive.vue
index c846f2418f637a9f44b8f2ca6d8bc036e2549d69..7a00896640f014c15317ceab37d8c4478da0cbc4 100644
--- a/src/client/app/desktop/views/pages/selectdrive.vue
+++ b/src/client/app/desktop/views/pages/selectdrive.vue
@@ -29,7 +29,7 @@ export default Vue.extend({
 		}
 	},
 	mounted() {
-		document.title = '%i18n:@title%';
+		document.title = '%i18n:!@title%';
 	},
 	methods: {
 		onSelected(file) {
diff --git a/src/client/app/desktop/views/widgets/channel.vue b/src/client/app/desktop/views/widgets/channel.vue
index 975058b0e205a94b989ae289a9fa6aa9b4d7e410..7e96f8ee3d169aa4f122de3bc2ac0f07266dd2f8 100644
--- a/src/client/app/desktop/views/widgets/channel.vue
+++ b/src/client/app/desktop/views/widgets/channel.vue
@@ -1,7 +1,7 @@
 <template>
 <div class="mkw-channel">
 	<template v-if="!props.compact">
-		<p class="title">%fa:tv%{{ channel ? channel.title : '%i18n:@title%' }}</p>
+		<p class="title">%fa:tv%{{ channel ? channel.title : '%i18n:!@title%' }}</p>
 		<button @click="settings" title="%i18n:@settings%">%fa:cog%</button>
 	</template>
 	<p class="get-started" v-if="props.channel == null">%i18n:@get-started%</p>
diff --git a/src/client/app/init.ts b/src/client/app/init.ts
index 2fb8f15cf3897d26130e5c2dd2568c3dd9a2e84e..990933ec0e844ad5dc687d8bfcdc416a625f1283 100644
--- a/src/client/app/init.ts
+++ b/src/client/app/init.ts
@@ -69,7 +69,7 @@ html.setAttribute('lang', lang);
 const head = document.getElementsByTagName('head')[0];
 const meta = document.createElement('meta');
 meta.setAttribute('name', 'description');
-meta.setAttribute('content', '%i18n:common.misskey%');
+meta.setAttribute('content', '%i18n:!common.misskey%');
 head.appendChild(meta);
 //#endregion
 
diff --git a/src/client/app/mobile/views/components/drive.vue b/src/client/app/mobile/views/components/drive.vue
index ec1b983d0b220ffd6a7ac3ed785e03ae2911527a..7aa666e1bbca774476712a76daa85323e995f016 100644
--- a/src/client/app/mobile/views/components/drive.vue
+++ b/src/client/app/mobile/views/components/drive.vue
@@ -32,7 +32,7 @@
 		<div class="files" v-if="files.length > 0">
 			<x-file v-for="file in files" :key="file.id" :file="file"/>
 			<button class="more" v-if="moreFiles" @click="fetchMoreFiles">
-				{{ fetchingMoreFiles ? '%i18n:common.loading%' : '%i18n:@load-more%' }}
+				{{ fetchingMoreFiles ? '%i18n:!common.loading%' : '%i18n:!@load-more%' }}
 			</button>
 		</div>
 		<div class="empty" v-if="files.length == 0 && folders.length == 0 && !fetching">
diff --git a/src/client/app/mobile/views/components/follow-button.vue b/src/client/app/mobile/views/components/follow-button.vue
index a6b5cf0556d8970023b879bc9a192566af847a27..5d6b8ebf84d39e5be42f278c41dadadd48c0d958 100644
--- a/src/client/app/mobile/views/components/follow-button.vue
+++ b/src/client/app/mobile/views/components/follow-button.vue
@@ -7,7 +7,7 @@
 	<template v-if="!wait && user.isFollowing">%fa:minus%</template>
 	<template v-if="!wait && !user.isFollowing">%fa:plus%</template>
 	<template v-if="wait">%fa:spinner .pulse .fw%</template>
-	{{ user.isFollowing ? '%i18n:@unfollow%' : '%i18n:@follow%' }}
+	{{ user.isFollowing ? '%i18n:!@unfollow%' : '%i18n:!@follow%' }}
 </button>
 </template>
 
diff --git a/src/client/app/mobile/views/components/note.vue b/src/client/app/mobile/views/components/note.vue
index cde8b6c2ce891a5741819cc05c4e1967a9460da0..cccb8875b47246f79583568a08a8f8b8a82cd619 100644
--- a/src/client/app/mobile/views/components/note.vue
+++ b/src/client/app/mobile/views/components/note.vue
@@ -9,9 +9,9 @@
 				<img class="avatar" :src="`${note.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
 			</router-link>
 			%fa:retweet%
-			<span>{{ '%i18n:@reposted-by%'.substr(0, '%i18n:@reposted-by%'.indexOf('{')) }}</span>
+			<span>{{ '%i18n:!@reposted-by%'.substr(0, '%i18n:!@reposted-by%'.indexOf('{')) }}</span>
 			<router-link class="name" :to="note.user | userPage">{{ note.user | userName }}</router-link>
-			<span>{{ '%i18n:@reposted-by%'.substr('%i18n:@reposted-by%'.indexOf('}') + 1) }}</span>
+			<span>{{ '%i18n:!@reposted-by%'.substr('%i18n:!@reposted-by%'.indexOf('}') + 1) }}</span>
 		</p>
 		<mk-time :time="note.createdAt"/>
 	</div>
diff --git a/src/client/app/mobile/views/components/notifications.vue b/src/client/app/mobile/views/components/notifications.vue
index 231ce2c1be55aed7e2ac426ab0e25ec8e7a7859c..ad43a27b9846dc97ab2b343189ad2118af361acc 100644
--- a/src/client/app/mobile/views/components/notifications.vue
+++ b/src/client/app/mobile/views/components/notifications.vue
@@ -11,7 +11,7 @@
 	</div>
 	<button class="more" v-if="moreNotifications" @click="fetchMoreNotifications" :disabled="fetchingMoreNotifications">
 		<template v-if="fetchingMoreNotifications">%fa:spinner .pulse .fw%</template>
-		{{ fetchingMoreNotifications ? '%i18n:common.loading%' : '%i18n:@more%' }}
+		{{ fetchingMoreNotifications ? '%i18n:!common.loading%' : '%i18n:!@more%' }}
 	</button>
 	<p class="empty" v-if="notifications.length == 0 && !fetching">%i18n:@empty%</p>
 	<p class="fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p>
diff --git a/src/client/app/mobile/views/components/post-form.vue b/src/client/app/mobile/views/components/post-form.vue
index 4b038fd2b61da6c7e739e5688add0b9f5a885643..861e8653ba5743268d7d36fbdfc2f634dca98b5c 100644
--- a/src/client/app/mobile/views/components/post-form.vue
+++ b/src/client/app/mobile/views/components/post-form.vue
@@ -5,12 +5,12 @@
 		<div>
 			<span class="text-count" :class="{ over: text.length > 1000 }">{{ 1000 - text.length }}</span>
 			<span class="geo" v-if="geo">%fa:map-marker-alt%</span>
-			<button class="submit" :disabled="posting" @click="post">{{ reply ? '返信' : '%i18n:@submit%' }}</button>
+			<button class="submit" :disabled="posting" @click="post">{{ reply ? '返信' : '%i18n:!@submit%' }}</button>
 		</div>
 	</header>
 	<div class="form">
 		<mk-note-preview v-if="reply" :note="reply"/>
-		<textarea v-model="text" ref="text" :disabled="posting" :placeholder="reply ? '%i18n:@reply-placeholder%' : '%i18n:@note-placeholder%'"></textarea>
+		<textarea v-model="text" ref="text" :disabled="posting" :placeholder="reply ? '%i18n:!@reply-placeholder%' : '%i18n:!@note-placeholder%'"></textarea>
 		<div class="attaches" v-show="files.length != 0">
 			<x-draggable class="files" :list="files" :options="{ animation: 150 }">
 				<div class="file" v-for="file in files" :key="file.id">
diff --git a/src/client/app/mobile/views/components/ui.nav.vue b/src/client/app/mobile/views/components/ui.nav.vue
index 2bab5fc7b1d89f7d17a49c40ec60c258475e47ca..68cdacb3b51baea334d58c79e08ec5cbaa5953fc 100644
--- a/src/client/app/mobile/views/components/ui.nav.vue
+++ b/src/client/app/mobile/views/components/ui.nav.vue
@@ -92,7 +92,7 @@ export default Vue.extend({
 	},
 	methods: {
 		search() {
-			const query = window.prompt('%i18n:@search%');
+			const query = window.prompt('%i18n:!@search%');
 			if (query == null || query == '') return;
 			this.$router.push('/search?q=' + encodeURIComponent(query));
 		},
diff --git a/src/client/app/mobile/views/components/user-timeline.vue b/src/client/app/mobile/views/components/user-timeline.vue
index 7cca6a55ac55376ff852efe511f8576e6a477bde..40b3be035e628b2ddf46c3a0039e7d3f2e251f44 100644
--- a/src/client/app/mobile/views/components/user-timeline.vue
+++ b/src/client/app/mobile/views/components/user-timeline.vue
@@ -6,7 +6,7 @@
 		</div>
 		<div class="empty" v-if="!fetching && notes.length == 0">
 			%fa:R comments%
-			{{ withMedia ? '%i18n:@no-notes-with-media%' : '%i18n:@no-notes%' }}
+			{{ withMedia ? '%i18n:!@no-notes-with-media%' : '%i18n:!@no-notes%' }}
 		</div>
 		<button v-if="!fetching && existMore" @click="more" :disabled="moreFetching" slot="tail">
 			<span v-if="!moreFetching">%i18n:@load-more%</span>
diff --git a/src/client/app/mobile/views/pages/followers.vue b/src/client/app/mobile/views/pages/followers.vue
index b1543b7cdaa0ca017c25258b2fed81cfb42662c8..f3c75f71e93e7496773a11d7549b0f495ade12f6 100644
--- a/src/client/app/mobile/views/pages/followers.vue
+++ b/src/client/app/mobile/views/pages/followers.vue
@@ -2,7 +2,7 @@
 <mk-ui>
 	<template slot="header" v-if="!fetching">
 		<img :src="`${user.avatarUrl}?thumbnail&size=64`" alt="">
-		{{ '%i18n:@followers-of%'.replace('{}', name) }}
+		{{ '%i18n:!@followers-of%'.replace('{}', name) }}
 	</template>
 	<mk-users-list
 		v-if="!fetching"
@@ -52,7 +52,7 @@ export default Vue.extend({
 				this.user = user;
 				this.fetching = false;
 
-				document.title = '%i18n:@followers-of%'.replace('{}', this.name) + ' | Misskey';
+				document.title = '%i18n:!@followers-of%'.replace('{}', this.name) + ' | Misskey';
 			});
 		},
 		onLoaded() {
diff --git a/src/client/app/mobile/views/pages/following.vue b/src/client/app/mobile/views/pages/following.vue
index 8dbdd5dba4f6d18d7ef4312627548cf84caea250..88368ff778e33fff0170d4025225c1606ec893fc 100644
--- a/src/client/app/mobile/views/pages/following.vue
+++ b/src/client/app/mobile/views/pages/following.vue
@@ -2,7 +2,7 @@
 <mk-ui>
 	<template slot="header" v-if="!fetching">
 		<img :src="`${user.avatarUrl}?thumbnail&size=64`" alt="">
-		{{ '%i18n:@following-of%'.replace('{}', name) }}
+		{{ '%i18n:!@following-of%'.replace('{}', name) }}
 	</template>
 	<mk-users-list
 		v-if="!fetching"
@@ -51,7 +51,7 @@ export default Vue.extend({
 				this.user = user;
 				this.fetching = false;
 
-				document.title = '%i18n:@followers-of%'.replace('{}', this.name) + ' | Misskey';
+				document.title = '%i18n:!@followers-of%'.replace('{}', this.name) + ' | Misskey';
 			});
 		},
 		onLoaded() {
diff --git a/src/client/app/mobile/views/pages/notifications.vue b/src/client/app/mobile/views/pages/notifications.vue
index 5ae94590cc58ea0c68eeb2e782ceb0f8c11935d8..cd2b633676252dc5ba017f552520be428297fdf4 100644
--- a/src/client/app/mobile/views/pages/notifications.vue
+++ b/src/client/app/mobile/views/pages/notifications.vue
@@ -19,7 +19,7 @@ export default Vue.extend({
 	},
 	methods: {
 		fn() {
-			const ok = window.confirm('%i18n:@read-all%');
+			const ok = window.confirm('%i18n:!@read-all%');
 			if (!ok) return;
 
 			(this as any).api('notifications/markAsRead_all');
diff --git a/src/client/app/mobile/views/pages/profile-setting.vue b/src/client/app/mobile/views/pages/profile-setting.vue
index 846be59951e31206651b71647006ab4ec1a2b4cd..59da71c67d9ce80cb2aedf23024c5eb50ace7080 100644
--- a/src/client/app/mobile/views/pages/profile-setting.vue
+++ b/src/client/app/mobile/views/pages/profile-setting.vue
@@ -72,7 +72,7 @@ export default Vue.extend({
 					avatarId: file.id
 				}).then(() => {
 					this.avatarSaving = false;
-					alert('%i18n:@avatar-saved%');
+					alert('%i18n:!@avatar-saved%');
 				});
 			});
 		},
@@ -86,7 +86,7 @@ export default Vue.extend({
 					bannerId: file.id
 				}).then(() => {
 					this.bannerSaving = false;
-					alert('%i18n:@banner-saved%');
+					alert('%i18n:!@banner-saved%');
 				});
 			});
 		},
@@ -100,7 +100,7 @@ export default Vue.extend({
 				birthday: this.birthday || null
 			}).then(() => {
 				this.saving = false;
-				alert('%i18n:@saved%');
+				alert('%i18n:!@saved%');
 			});
 		}
 	}
diff --git a/src/client/app/mobile/views/pages/search.vue b/src/client/app/mobile/views/pages/search.vue
index 2ea2c73384f0ba6b266defd1215ec44b5f65663b..31035f666a9885982d30c5d313bc4a24d418e7dd 100644
--- a/src/client/app/mobile/views/pages/search.vue
+++ b/src/client/app/mobile/views/pages/search.vue
@@ -3,7 +3,7 @@
 	<span slot="header">%fa:search% {{ q }}</span>
 	<main v-if="!fetching">
 		<mk-notes :class="$style.notes" :notes="notes">
-			<span v-if="notes.length == 0">{{ '%i18n:@empty%'.replace('{}', q) }}</span>
+			<span v-if="notes.length == 0">{{ '%i18n:!@empty%'.replace('{}', q) }}</span>
 			<button v-if="existMore" @click="more" :disabled="fetching" slot="tail">
 				<span v-if="!fetching">%i18n:@load-more%</span>
 				<span v-if="fetching">%i18n:common.loading%<mk-ellipsis/></span>
diff --git a/src/client/app/mobile/views/pages/selectdrive.vue b/src/client/app/mobile/views/pages/selectdrive.vue
index e8e256cae8c5375b0fec24129d43cb39d67a49a0..741559ed0b4613f6a2a6ac945ae4eb8ee7d3e064 100644
--- a/src/client/app/mobile/views/pages/selectdrive.vue
+++ b/src/client/app/mobile/views/pages/selectdrive.vue
@@ -25,7 +25,7 @@ export default Vue.extend({
 		}
 	},
 	mounted() {
-		document.title = '%i18n:@title%';
+		document.title = '%i18n:!@title%';
 	},
 	methods: {
 		onSelected(file) {
diff --git a/src/client/app/mobile/views/pages/settings.vue b/src/client/app/mobile/views/pages/settings.vue
index 36f7f09b845168b6f6769c59ee90e952d9fc1f36..8ae087749f5782ef3654d1dec5d864754ffe984c 100644
--- a/src/client/app/mobile/views/pages/settings.vue
+++ b/src/client/app/mobile/views/pages/settings.vue
@@ -2,7 +2,7 @@
 <mk-ui>
 	<span slot="header">%fa:cog%%i18n:@settings%</span>
 	<div :class="$style.content">
-		<p v-html="'%i18n:@signed-in-as%'.replace('{}', '<b>' + name + '</b>')"></p>
+		<p v-html="'%i18n:!@signed-in-as%'.replace('{}', '<b>' + name + '</b>')"></p>
 		<ul>
 			<li><router-link to="./settings/profile">%fa:user%%i18n:@profile%%fa:angle-right%</router-link></li>
 			<li><router-link to="./settings/twitter">%fa:B twitter%%i18n:@twitter%%fa:angle-right%</router-link></li>