diff --git a/package/gluon-web/src/template_parser.c b/package/gluon-web/src/template_parser.c
index b69a99b392187f0606906cb87b7d8199ce033040..3ba45488ca65fde10d6c016f49aa61740eea133d 100644
--- a/package/gluon-web/src/template_parser.c
+++ b/package/gluon-web/src/template_parser.c
@@ -72,15 +72,15 @@ struct template_parser {
 
 /* leading and trailing code for different types */
 static const char *const gen_code[][2] = {
-	[T_TYPE_INIT]     = {NULL,              NULL},
-	[T_TYPE_TEXT]     = {"write('",         "')"},
-	[T_TYPE_COMMENT]  = {NULL,              NULL},
-	[T_TYPE_EXPR]     = {"write(tostring(", " or ''))"},
-	[T_TYPE_INCLUDE]  = {"include('",       "')"},
-	[T_TYPE_I18N]     = {"write('",         "')"},
-	[T_TYPE_I18N_RAW] = {"write('",         "')"},
-	[T_TYPE_CODE]     = {NULL,              " "},
-	[T_TYPE_EOF]      = {NULL,              NULL},
+	[T_TYPE_INIT]     = {NULL,                        NULL},
+	[T_TYPE_TEXT]     = {"write('",                   "')"},
+	[T_TYPE_COMMENT]  = {NULL,                        NULL},
+	[T_TYPE_EXPR]     = {"write(tostring(",           " or ''))"},
+	[T_TYPE_INCLUDE]  = {"include('",                 "')"},
+	[T_TYPE_I18N]     = {"write(pcdata(translate('",  "')))"},
+	[T_TYPE_I18N_RAW] = {"write(translate('",         "'))"},
+	[T_TYPE_CODE]     = {NULL,                        " "},
+	[T_TYPE_EOF]      = {NULL,                        NULL},
 };
 
 static struct template_parser * template_init(struct template_parser *parser)
@@ -241,6 +241,28 @@ static void template_code(struct template_parser *parser, const char *e)
 	parser->cur_chunk.e = e;
 }
 
+static void luastr_escape(struct template_buffer *out, const char *s, const char *e)
+{
+	for (const char *ptr = s; ptr < e; ptr++) {
+		switch (*ptr) {
+		case '\\':
+			buf_append(out, "\\\\", 2);
+			break;
+
+		case '\'':
+			buf_append(out, "\\\'", 2);
+			break;
+
+		case '\n':
+			buf_append(out, "\\n", 2);
+			break;
+
+		default:
+			buf_putchar(out, *ptr);
+		}
+	}
+}
+
 static struct template_buffer * template_format_chunk(struct template_parser *parser)
 {
 	const char *p;
@@ -266,7 +288,10 @@ static struct template_buffer * template_format_chunk(struct template_parser *pa
 
 		switch (c->type) {
 		case T_TYPE_TEXT:
-			luastr_escape(buf, c->s, c->e - c->s, false);
+		case T_TYPE_INCLUDE:
+		case T_TYPE_I18N:
+		case T_TYPE_I18N_RAW:
+			luastr_escape(buf, c->s, c->e);
 			break;
 
 		case T_TYPE_EXPR:
@@ -275,18 +300,6 @@ static struct template_buffer * template_format_chunk(struct template_parser *pa
 				parser->line += (*p == '\n');
 			break;
 
-		case T_TYPE_INCLUDE:
-			luastr_escape(buf, c->s, c->e - c->s, false);
-			break;
-
-		case T_TYPE_I18N:
-			luastr_translate(buf, c->s, c->e - c->s, true);
-			break;
-
-		case T_TYPE_I18N_RAW:
-			luastr_translate(buf, c->s, c->e - c->s, false);
-			break;
-
 		case T_TYPE_CODE:
 			buf_append(buf, c->s, c->e - c->s);
 			for (p = c->s; p < c->e; p++)
diff --git a/package/gluon-web/src/template_utils.c b/package/gluon-web/src/template_utils.c
index 7bd0f7098cb61bb0e9733747e3a4ec9f7d787fec..8a4cecfee8d7c7597525a30aebb646fc07f6367e 100644
--- a/package/gluon-web/src/template_utils.c
+++ b/package/gluon-web/src/template_utils.c
@@ -66,7 +66,7 @@ static bool buf_grow(struct template_buffer *buf, size_t len)
 }
 
 /* put one char into buffer object */
-static bool buf_putchar(struct template_buffer *buf, char c)
+bool buf_putchar(struct template_buffer *buf, char c)
 {
 	if (!buf_grow(buf, 1))
 		return false;
@@ -311,53 +311,3 @@ char * pcdata(const char *s, size_t l, size_t *outl)
 	*outl = buf_length(buf);
 	return buf_destroy(buf);
 }
-
-void luastr_escape(struct template_buffer *out, const char *s, size_t l, bool escape_xml)
-{
-	int esl;
-	char esq[8];
-	const char *ptr;
-
-	for (ptr = s; ptr < (s + l); ptr++) {
-		switch (*ptr) {
-		case '\\':
-			buf_append(out, "\\\\", 2);
-			break;
-
-		case '\'':
-			if (escape_xml)
-				buf_append(out, "&#39;", 5);
-			else
-				buf_append(out, "\\\'", 2);
-			break;
-
-		case '\n':
-			buf_append(out, "\\n", 2);
-			break;
-
-		case '"':
-		case '&':
-		case '<':
-		case '>':
-			if (escape_xml) {
-				esl = snprintf(esq, sizeof(esq), "&#%i;", *ptr);
-				buf_append(out, esq, esl);
-				break;
-			}
-
-		default:
-			buf_putchar(out, *ptr);
-		}
-	}
-}
-
-void luastr_translate(struct template_buffer *out, const char *s, size_t l, bool escape_xml)
-{
-	char *tr;
-	size_t trlen;
-
-	if (lmo_translate(s, l, &tr, &trlen))
-		luastr_escape(out, tr, trlen, escape_xml);
-	else
-		luastr_escape(out, s, l, escape_xml);
-}
diff --git a/package/gluon-web/src/template_utils.h b/package/gluon-web/src/template_utils.h
index a094d344fb5016e5be464ea985f415c5cb94edb6..5708bfd3724a3eb75be267fb9319198a426bc9d2 100644
--- a/package/gluon-web/src/template_utils.h
+++ b/package/gluon-web/src/template_utils.h
@@ -32,6 +32,7 @@ struct template_buffer {
 };
 
 struct template_buffer * buf_init(size_t size);
+bool buf_putchar(struct template_buffer *buf, char c);
 bool buf_append(struct template_buffer *buf, const char *s, size_t len);
 char * buf_destroy(struct template_buffer *buf);
 
@@ -44,7 +45,4 @@ static inline size_t buf_length(struct template_buffer *buf)
 
 char * pcdata(const char *s, size_t l, size_t *outl);
 
-void luastr_escape(struct template_buffer *out, const char *s, size_t l, bool escape_xml);
-void luastr_translate(struct template_buffer *out, const char *s, size_t l, bool escape_xml);
-
 #endif