diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 0c301b2..e267ff2 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -9913,6 +9913,9 @@ rm_redo_error_callback(void *arg)
 	XLogRecord *record = (XLogRecord *) arg;
 	StringInfoData buf;
 
+	/* use the right domain to translate the errcontext() calls */
+	set_errtextdomain();
+
 	initStringInfo(&buf);
 	RmgrTable[record->xl_rmid].rm_desc(&buf,
 									   record->xl_info,
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index 1fffe1c..59ff1c9 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -914,6 +914,9 @@ sql_function_parse_error_callback(void *arg)
 {
 	parse_error_callback_arg *callback_arg = (parse_error_callback_arg *) arg;
 
+	/* use the right domain to translate the errcontext() calls */
+	set_errtextdomain();
+
 	/* See if it's a syntax error; if so, transpose to CREATE FUNCTION */
 	if (!function_parse_error_transpose(callback_arg->prosrc))
 	{
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 95fec8d..8533914 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -1743,6 +1743,9 @@ CopyFromErrorCallback(void *arg)
 {
 	CopyState	cstate = (CopyState) arg;
 
+	/* use the right domain to translate the errcontext() calls */
+	set_errtextdomain();
+
 	if (cstate->binary)
 	{
 		/* can't usefully display the data */
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index ae8d374..7643733 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -1294,6 +1294,9 @@ sql_exec_error_callback(void *arg)
 	if (fcache == NULL || fcache->fname == NULL)
 		return;
 
+	/* use the right domain to translate the errcontext() calls */
+	set_errtextdomain();
+
 	/*
 	 * If there is a syntax error position, convert to internal syntax error
 	 */
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index 5e4ae42..fd43106 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -2154,7 +2154,10 @@ _SPI_error_callback(void *arg)
 		internalerrquery(query);
 	}
 	else
+	{
+		set_errtextdomain();
 		errcontext("SQL statement \"%s\"", query);
+	}
 }
 
 /*
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 344ebb7..c747bb6 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -4350,6 +4350,9 @@ sql_inline_error_callback(void *arg)
 	inline_error_callback_arg *callback_arg = (inline_error_callback_arg *) arg;
 	int			syntaxerrposition;
 
+	/* use the right domain to translate the errcontext() calls */
+	set_errtextdomain();
+
 	/* If it's a syntax error, convert to internal syntax error report */
 	syntaxerrposition = geterrposition();
 	if (syntaxerrposition > 0)
diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c
index 5314954..dcbc275 100644
--- a/src/backend/parser/parse_type.c
+++ b/src/backend/parser/parse_type.c
@@ -644,6 +644,9 @@ pts_error_callback(void *arg)
 {
 	const char *str = (const char *) arg;
 
+	/* use the right domain to translate the errcontext() calls */
+	set_errtextdomain();
+
 	errcontext("invalid type name \"%s\"", str);
 
 	/*
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index 613d754..6630755 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -2849,6 +2849,9 @@ shared_buffer_write_error_callback(void *arg)
 {
 	volatile BufferDesc *bufHdr = (volatile BufferDesc *) arg;
 
+	/* use the right domain to translate the errcontext() calls */
+	set_errtextdomain();
+
 	/* Buffer is pinned, so we can read the tag without locking the spinlock */
 	if (bufHdr != NULL)
 	{
@@ -2868,6 +2871,9 @@ local_buffer_write_error_callback(void *arg)
 {
 	volatile BufferDesc *bufHdr = (volatile BufferDesc *) arg;
 
+	/* use the right domain to translate the errcontext() calls */
+	set_errtextdomain();
+
 	if (bufHdr != NULL)
 	{
 		char	   *path = relpathbackend(bufHdr->tag.rnode, MyBackendId,
diff --git a/src/backend/tsearch/ts_locale.c b/src/backend/tsearch/ts_locale.c
index 5b42a93..b2511bf 100644
--- a/src/backend/tsearch/ts_locale.c
+++ b/src/backend/tsearch/ts_locale.c
@@ -166,6 +166,9 @@ tsearch_readline_callback(void *arg)
 {
 	tsearch_readline_state *stp = (tsearch_readline_state *) arg;
 
+	/* use the right domain to translate the errcontext() calls */
+	set_errtextdomain();
+
 	/*
 	 * We can't include the text of the config line for errors that occur
 	 * during t_readline() itself.	This is only partly a consequence of our
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index 239ac19..3d17036 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -395,6 +395,7 @@ errfinish(int dummy,...)
 	int			elevel = edata->elevel;
 	MemoryContext oldcontext;
 	ErrorContextCallback *econtext;
+	const char *olddomain;
 
 	recursion_depth++;
 	CHECK_STACK_DEPTH();
@@ -410,10 +411,23 @@ errfinish(int dummy,...)
 	 * functions will be treated as recursive errors --- this ensures we will
 	 * avoid infinite recursion (see errstart).
 	 */
+	olddomain = edata->domain;
 	for (econtext = error_context_stack;
 		 econtext != NULL;
 		 econtext = econtext->previous)
+	{
+		/*
+		 * Callback can set the text domain, so restore it after the call.
+		 * This isn't strictly necessary, every callback should set the domain
+		 * explicitly if the messages are translatable to begin with. But
+		 * just in case a callback forgets to do it (perhaps because it's a
+		 * an external module that was written before version 9.2, where
+		 * errtextdomain() was added), let's be consistent and always use the
+		 * domain specified in errstart.
+		 */
 		(*econtext->callback) (econtext->arg);
+		edata->domain = olddomain;
+	}
 
 	/*
 	 * If ERROR (not more nor less) we pass it off to the current handler.
@@ -980,6 +994,34 @@ errcontext(const char *fmt,...)
 
 
 /*
+ * errtextdomain --- set the message domain used for errcontext calls
+ *
+ * This is used in error callback functions, before calling errcontext(). It
+ * sets the message domain that will be used to translate the messages in
+ * the errcontext() call(s) that follow.  Normally we set the domain in
+ * errstart(), but an error context callback often resides in a different
+ * module than where the error originates and where errstart() called, so the
+ * text domain needs to be set explicitly in the callback.
+ *
+ * Actually, this affects not only errcontext() calls but also errmsg(),
+ * errdetail() etc, but errcontext() is the only function that is normally
+ * called at a distance from the original ereport() site.
+ */
+int
+errtextdomain(const char *domain)
+{
+	ErrorData  *edata = &errordata[errordata_stack_depth];
+
+	/* we don't bother incrementing recursion_depth */
+	CHECK_STACK_DEPTH();
+
+	edata->domain = domain;
+
+	return 0;					/* return value does not matter */
+}
+
+
+/*
  * errhidestmt --- optionally suppress STATEMENT: field of log entry
  *
  * This should be called if the message text already includes the statement.
diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h
index 7b5bcfa..a78b949 100644
--- a/src/include/utils/elog.h
+++ b/src/include/utils/elog.h
@@ -178,6 +178,8 @@ errcontext(const char *fmt,...)
    the supplied arguments. */
 __attribute__((format(PG_PRINTF_ATTRIBUTE, 1, 2)));
 
+extern int errtextdomain(const char *domain);
+
 extern int	errhidestmt(bool hide_stmt);
 
 extern int	errfunction(const char *funcname);
@@ -227,6 +229,15 @@ typedef struct ErrorContextCallback
 
 extern PGDLLIMPORT ErrorContextCallback *error_context_stack;
 
+/*
+ * This is a convenience macro to set text-domain to the default for the
+ * current source file. This should be called in every error callback
+ * function, before calling errcontext(), to make sure the right gettext
+ * domain is used to translate the context message.
+ */
+#define set_errtextdomain()	\
+	errtextdomain(TEXTDOMAIN)
+
 
 /*----------
  * API for catching ereport(ERROR) exits.  Use these macros like so:
diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c
index 7a92f3d..22006e3 100644
--- a/src/pl/plperl/plperl.c
+++ b/src/pl/plperl/plperl.c
@@ -3649,7 +3649,10 @@ plperl_exec_callback(void *arg)
 	char	   *procname = (char *) arg;
 
 	if (procname)
+	{
+		set_errtextdomain();
 		errcontext("PL/Perl function \"%s\"", procname);
+	}
 }
 
 /*
@@ -3661,7 +3664,10 @@ plperl_compile_callback(void *arg)
 	char	   *procname = (char *) arg;
 
 	if (procname)
+	{
+		set_errtextdomain();
 		errcontext("compilation of PL/Perl function \"%s\"", procname);
+	}
 }
 
 /*
@@ -3670,6 +3676,7 @@ plperl_compile_callback(void *arg)
 static void
 plperl_inline_callback(void *arg)
 {
+	set_errtextdomain();
 	errcontext("PL/Perl anonymous code block");
 }
 
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index d43b8e0..8e53f04 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -908,8 +908,11 @@ plpgsql_compile_error_callback(void *arg)
 	}
 
 	if (plpgsql_error_funcname)
+	{
+		set_errtextdomain();
 		errcontext("compilation of PL/pgSQL function \"%s\" near line %d",
 				   plpgsql_error_funcname, plpgsql_latest_lineno());
+	}
 }
 
 
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index a385b9a..a5b9866 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -786,6 +786,9 @@ plpgsql_exec_error_callback(void *arg)
 	if (estate->err_text == raise_skip_msg)
 		return;
 
+	/* use the right domain to translate the errcontext() calls */
+	set_errtextdomain();
+
 	if (estate->err_text != NULL)
 	{
 		/*
diff --git a/src/pl/plpython/plpy_exec.c b/src/pl/plpython/plpy_exec.c
index 280d3ed..0356d56 100644
--- a/src/pl/plpython/plpy_exec.c
+++ b/src/pl/plpython/plpy_exec.c
@@ -458,7 +458,10 @@ plpython_return_error_callback(void *arg)
 	PLyExecutionContext *exec_ctx = PLy_current_execution_context();
 
 	if (exec_ctx->curr_proc)
+	{
+		set_errtextdomain();
 		errcontext("while creating return value");
+	}
 }
 
 static PyObject *
@@ -786,7 +789,10 @@ plpython_trigger_error_callback(void *arg)
 	PLyExecutionContext *exec_ctx = PLy_current_execution_context();
 
 	if (exec_ctx->curr_proc)
+	{
+		set_errtextdomain();
 		errcontext("while modifying trigger row");
+	}
 }
 
 /* execute Python code, propagate Python errors to the backend */
diff --git a/src/pl/plpython/plpy_main.c b/src/pl/plpython/plpy_main.c
index c126db9..da7c8ed 100644
--- a/src/pl/plpython/plpy_main.c
+++ b/src/pl/plpython/plpy_main.c
@@ -343,13 +343,17 @@ plpython_error_callback(void *arg)
 	PLyExecutionContext *exec_ctx = PLy_current_execution_context();
 
 	if (exec_ctx->curr_proc)
+	{
+		set_errtextdomain();
 		errcontext("PL/Python function \"%s\"",
 				   PLy_procedure_name(exec_ctx->curr_proc));
+	}
 }
 
 static void
 plpython_inline_error_callback(void *arg)
 {
+	set_errtextdomain();
 	errcontext("PL/Python anonymous code block");
 }
 
