Repository: ovh/cds
Pull request #5581 (2020-12-04 21:51:04)

refactor: keep only en language
Signed-off-by: Yvonnick Esnault <yvonnick.esnault@corp.ovh.com>


ovh-cds: 
CDS Report build-all-cds#15047.0 ✘  
* Build
  * Build and Package All ✔ 
  * Build Debpacker ✔ 
  * Build OS-Ansible-Inventory ✔ 
  * Build SMTPMock ✔ 
  * Docs -   
  * Lint UI Translate ✔ 
  * Test CLI ✔ 
  * Test Engine ✘  
  * Test SDK ✘  
  * Test SDK Rust ✔ 
  * Test UI ✔ 

sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!

[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug.png' alt='Bug' width='16' height='16' />](https://sonarcloud.io/project/issues?id=ovh_cds&pullRequest=5581&resolved=false&types=BUG) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=ovh_cds&pullRequest=5581&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=ovh_cds&pullRequest=5581&resolved=false&types=BUG)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability.png' alt='Vulnerability' width='16' height='16' />](https://sonarcloud.io/project/issues?id=ovh_cds&pullRequest=5581&resolved=false&types=VULNERABILITY) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=ovh_cds&pullRequest=5581&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=ovh_cds&pullRequest=5581&resolved=false&types=VULNERABILITY)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot.png' alt='Security Hotspot' width='16' height='16' />](https://sonarcloud.io/project/security_hotspots?id=ovh_cds&pullRequest=5581&resolved=false&types=SECURITY_HOTSPOT) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/security_hotspots?id=ovh_cds&pullRequest=5581&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=ovh_cds&pullRequest=5581&resolved=false&types=SECURITY_HOTSPOT)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell.png' alt='Code Smell' width='16' height='16' />](https://sonarcloud.io/project/issues?id=ovh_cds&pullRequest=5581&resolved=false&types=CODE_SMELL) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=ovh_cds&pullRequest=5581&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=ovh_cds&pullRequest=5581&resolved=false&types=CODE_SMELL)

[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo.png' alt='No Coverage information' width='16' height='16' />](https://sonarcloud.io/component_measures?id=ovh_cds&pullRequest=5581&metric=coverage&view=list) No Coverage information  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/NoDuplicationInfo.png' alt='No Duplication information' width='16' height='16' />](https://sonarcloud.io/component_measures?id=ovh_cds&pullRequest=5581&metric=duplicated_lines_density&view=list) No Duplication information


ovh-cds: 
CDS Report build-all-cds#15056.0 ✘  
* Build
  * Build and Package All ✔ 
  * Build Debpacker ✔ 
  * Build OS-Ansible-Inventory ✔ 
  * Build SMTPMock ✔ 
  * Docs -   
  * Lint UI Translate ✔ 
  * Test CLI ✔ 
  * Test Engine ✘  
  * Test SDK ✔ 
  * Test SDK Rust ✔ 
  * Test UI ✔ 

sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!

[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug.png' alt='Bug' width='16' height='16' />](https://sonarcloud.io/project/issues?id=ovh_cds&pullRequest=5581&resolved=false&types=BUG) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=ovh_cds&pullRequest=5581&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=ovh_cds&pullRequest=5581&resolved=false&types=BUG)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability.png' alt='Vulnerability' width='16' height='16' />](https://sonarcloud.io/project/issues?id=ovh_cds&pullRequest=5581&resolved=false&types=VULNERABILITY) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=ovh_cds&pullRequest=5581&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=ovh_cds&pullRequest=5581&resolved=false&types=VULNERABILITY)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot.png' alt='Security Hotspot' width='16' height='16' />](https://sonarcloud.io/project/security_hotspots?id=ovh_cds&pullRequest=5581&resolved=false&types=SECURITY_HOTSPOT) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/security_hotspots?id=ovh_cds&pullRequest=5581&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=ovh_cds&pullRequest=5581&resolved=false&types=SECURITY_HOTSPOT)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell.png' alt='Code Smell' width='16' height='16' />](https://sonarcloud.io/project/issues?id=ovh_cds&pullRequest=5581&resolved=false&types=CODE_SMELL) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=ovh_cds&pullRequest=5581&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=ovh_cds&pullRequest=5581&resolved=false&types=CODE_SMELL)

[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo.png' alt='No Coverage information' width='16' height='16' />](https://sonarcloud.io/component_measures?id=ovh_cds&pullRequest=5581&metric=coverage&view=list) No Coverage information  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/NoDuplicationInfo.png' alt='No Duplication information' width='16' height='16' />](https://sonarcloud.io/component_measures?id=ovh_cds&pullRequest=5581&metric=duplicated_lines_density&view=list) No Duplication information



Patch:
diff --git a/engine/api/application_import.go b/engine/api/application_import.go
index 4855b50d1..41b9fbdcf 100644
--- a/engine/api/application_import.go
+++ b/engine/api/application_import.go
@@ -63,13 +63,13 @@ func (api *API) postApplicationImportHandler() service.Handler {
 		defer tx.Rollback() // nolint
 
 		newApp, _, msgList, globalError := application.ParseAndImport(ctx, tx, api.Cache, *proj, eapp, application.ImportOptions{Force: force}, project.DecryptWithBuiltinKey, getAPIConsumer(ctx))
-		msgListString := translate(r, msgList)
+		msgListString := translate(msgList)
 		if globalError != nil {
 			globalError = sdk.WrapError(globalError, "Unable to import application %s", eapp.Name)
 			if sdk.ErrorIsUnknown(globalError) {
 				return globalError
 			}
-			sdkErr := sdk.ExtractHTTPError(globalError, r.Header.Get("Accept-Language"))
+			sdkErr := sdk.ExtractHTTPError(globalError)
 			return service.WriteJSON(w, append(msgListString, sdkErr.Message), sdkErr.Status)
 		}
 
diff --git a/engine/api/ascode.go b/engine/api/ascode.go
index 2b7b5be83..3e10e65d3 100644
--- a/engine/api/ascode.go
+++ b/engine/api/ascode.go
@@ -206,7 +206,7 @@ func (api *API) postPerformImportAsCodeHandler() service.Handler {
 		if err := workflowtemplate.UpdateTemplateInstanceWithWorkflow(ctx, api.mustDB(), *wrkflw, *consumer, wti); err != nil {
 			return err
 		}
-		msgListString := translate(r, allMsg)
+		msgListString := translate(allMsg)
 
 		tx, err := api.mustDB().Begin()
 		if err != nil {
diff --git a/engine/api/ascode/dao.go b/engine/api/ascode/dao.go
index 7497f9b19..043594dcc 100644
--- a/engine/api/ascode/dao.go
+++ b/engine/api/ascode/dao.go
@@ -99,7 +99,7 @@ func deleteEvent(db gorp.SqlExecutor, event *sdk.AsCodeEvent) error {
 // DeleteAsCodeEventByWorkflowID removes all as_code_event from workflow_id
 func DeleteAsCodeEventByWorkflowID(db gorp.SqlExecutor, id int64) error {
 	_, err := db.Exec("DELETE FROM as_code_events WHERE workflow_id = $1", id)
-	return sdk.WrapError(err, "unable to delete as_code_events with workflow_id %s", id)
+	return sdk.WrapError(err, "unable to delete as_code_events with workflow_id %d", id)
 }
 
 func DeleteEventsPipelineOnlyFromRepoName(ctx context.Context, db gorp.SqlExecutor, fromRepository string, pipID int64, pipName string) error {
diff --git a/engine/api/environment_import.go b/engine/api/environment_import.go
index fc870218b..41f351d0d 100644
--- a/engine/api/environment_import.go
+++ b/engine/api/environment_import.go
@@ -59,13 +59,13 @@ func (api *API) postEnvironmentImportHandler() service.Handler {
 		defer tx.Rollback() // nolint
 
 		_, _, msgList, globalError := environment.ParseAndImport(ctx, tx, *proj, data, environment.ImportOptions{Force: force}, project.DecryptWithBuiltinKey, getAPIConsumer(ctx))
-		msgListString := translate(r, msgList)
+		msgListString := translate(msgList)
 		if globalError != nil {
 			globalError = sdk.WrapError(globalError, "Unable to import environment %s", data.Name)
 			if sdk.ErrorIsUnknown(globalError) {
 				return globalError
 			}
-			sdkErr := sdk.ExtractHTTPError(globalError, r.Header.Get("Accept-Language"))
+			sdkErr := sdk.ExtractHTTPError(globalError)
 			return service.WriteJSON(w, append(msgListString, sdkErr.Message), sdkErr.Status)
 		}
 
@@ -136,7 +136,7 @@ func (api *API) importNewEnvironmentHandler() service.Handler {
 		close(msgChan)
 		<-done
 
-		msgListString := translate(r, allMsg)
+		msgListString := translate(allMsg)
 
 		if err := tx.Commit(); err != nil {
 			return sdk.WithStack(err)
@@ -209,7 +209,7 @@ func (api *API) importIntoEnvironmentHandler() service.Handler {
 		close(msgChan)
 		<-done
 
-		msgListString := translate(r, allMsg)
+		msgListString := translate(allMsg)
 
 		if err := tx.Commit(); err != nil {
 			return sdk.WithStack(err)
diff --git a/engine/api/pipeline_import.go b/engine/api/pipeline_import.go
index 55d4a0714..ce4689b15 100644
--- a/engine/api/pipeline_import.go
+++ b/engine/api/pipeline_import.go
@@ -87,13 +87,13 @@ func (api *API) importPipelineHandler() service.Handler {
 
 		pip, allMsg, globalError := pipeline.ParseAndImport(ctx, tx, api.Cache, *proj, data, getAPIConsumer(ctx),
 			pipeline.ImportOptions{Force: force})
-		msgListString := translate(r, allMsg)
+		msgListString := translate(allMsg)
 		if globalError != nil {
 			globalError = sdk.WrapError(globalError, "unable to import pipeline")
 			if sdk.ErrorIsUnknown(globalError) {
 				return globalError
 			}
-			sdkErr := sdk.ExtractHTTPError(globalError, r.Header.Get("Accept-Language"))
+			sdkErr := sdk.ExtractHTTPError(globalError)
 			return service.WriteJSON(w, append(msgListString, sdkErr.Message), sdkErr.Status)
 		}
 
@@ -147,7 +147,7 @@ func (api *API) putImportPipelineHandler() service.Handler {
 		defer tx.Rollback() // nolint
 
 		pip, allMsg, err := pipeline.ParseAndImport(ctx, tx, api.Cache, *proj, data, getAPIConsumer(ctx), pipeline.ImportOptions{Force: true, PipelineName: pipelineName})
-		msgListString := translate(r, allMsg)
+		msgListString := translate(allMsg)
 		if err != nil {
 			return sdk.NewErrorWithStack(err, sdk.NewErrorFrom(sdk.ErrInvalidPipeline, "unable to parse and import pipeline"))
 		}
diff --git a/engine/api/router_util.go b/engine/api/router_util.go
index 3e740cd84..f5ac83236 100644
--- a/engine/api/router_util.go
+++ b/engine/api/router_util.go
@@ -162,11 +162,10 @@ func requestVarInt(r *http.Request, s string) (int64, error) {
 	return id, nil
 }
 
-func translate(r *http.Request, msgList []sdk.Message) []string {
-	al := r.Header.Get("Accept-Language")
+func translate(msgList []sdk.Message) []string {
 	msgListString := []string{}
 	for _, m := range msgList {
-		s := m.String(al)
+		s := m.String()
 		if s != "" {
 			msgListString = append(msgListString, s)
 		}
diff --git a/engine/api/templates.go b/engine/api/templates.go
index 72638e156..d5f833181 100644
--- a/engine/api/templates.go
+++ b/engine/api/templates.go
@@ -480,7 +480,7 @@ func (api *API) postTemplateApplyHandler() service.Handler {
 			return err
 		}
 
-		msgStrings := translate(r, msgs)
+		msgStrings := translate(msgs)
 
 		log.Debug("postTemplateApplyHandler> importing the workflow %s from template %s", wkf.Name, wt.Slug)
 
@@ -976,7 +976,7 @@ func (api *API) postTemplatePushHandler() service.Handler {
 		w.Header().Add(sdk.ResponseTemplateGroupNameHeader, wt.Group.Name)
 		w.Header().Add(sdk.ResponseTemplateSlugHeader, wt.Slug)
 
-		return service.WriteJSON(w, translate(r, msgs), http.StatusOK)
+		return service.WriteJSON(w, translate(msgs), http.StatusOK)
 	}
 }
 
diff --git a/engine/api/workflow.go b/engine/api/workflow.go
index 6fae69f9c..0391b7aee 100644
--- a/engine/api/workflow.go
+++ b/engine/api/workflow.go
@@ -199,8 +199,7 @@ func (api *API) postWorkflowRetentionPolicyDryRun() service.Handler {
 		api.GoRoutines.Exec(api.Router.Background, "workflow-retention-dryrun", func(ctx context.Context) {
 			if err := purge.ApplyRetentionPolicyOnWorkflow(ctx, api.Cache, api.mustDBWithCtx(ctx), *wf, purge.MarkAsDeleteOptions{DryRun: true}, u.AuthentifiedUser); err != nil {
 				log.ErrorWithFields(ctx, log.Fields{"stack_trace": fmt.Sprintf("%+v", err)}, "%s", err)
-				al := r.Header.Get("Accept-Language")
-				httpErr := sdk.ExtractHTTPError(err, al)
+				httpErr := sdk.ExtractHTTPError(err)
 				event.PublishWorkflowRetentionDryRun(ctx, key, name, "ERROR", httpErr.Error(), nil, 0, u.AuthentifiedUser)
 			}
 		})
diff --git a/engine/api/workflow/dao_run.go b/engine/api/workflow/dao_run.go
index 392c849b3..edea56d4a 100644
--- a/engine/api/workflow/dao_run.go
+++ b/engine/api/workflow/dao_run.go
@@ -54,7 +54,6 @@ type LoadRunOptions struct {
 	WithVulnerabilities     bool
 	WithDeleted             bool
 	DisableDetailledNodeRun bool
-	Language                string
 }
 
 // insertWorkflowRun inserts in table "workflow_run""
@@ -955,11 +954,7 @@ func syncNodeRuns(db gorp.SqlExecutor, wr *sdk.WorkflowRun, loadOpts LoadRunOpti
 			}
 			wnr.Coverage = cov
 		}
-		var l = loadOpts.Language
-		if l == "" {
-			l = "en"
-		}
-		wnr.Translate(l)
+		wnr.Translate()
 		wr.WorkflowNodeRuns[wnr.WorkflowNodeID] = append(wr.WorkflowNodeRuns[wnr.WorkflowNodeID], *wnr)
 	}
 
diff --git a/engine/api/workflow/execute_node_run.go b/engine/api/workflow/execute_node_run.go
index d30ba7519..9713a5091 100644
--- a/engine/api/workflow/execute_node_run.go
+++ b/engine/api/workflow/execute_node_run.go
@@ -523,7 +523,7 @@ jobLoop:
 				msg := sdk.SpawnMsg{
 					ID: sdk.MsgSpawnInfoJobError.ID,
 				}
-				msg.Args = []interface{}{sdk.ExtractHTTPError(e, "").Error()}
+				msg.Args = []interface{}{sdk.ExtractHTTPError(e).Error()}
 				wjob.SpawnInfos = append(wjob.SpawnInfos, sdk.SpawnInfo{
 					APITime:     time.Now(),
 					Message:     msg,
diff --git a/engine/api/workflow/process_node.go b/engine/api/workflow/process_node.go
index f945af74a..05d186f8b 100644
--- a/engine/api/workflow/process_node.go
+++ b/engine/api/workflow/process_node.go
@@ -36,7 +36,7 @@ func processNodeTriggers(ctx context.Context, db gorpmapper.SqlExecutorWithTx, s
 			r1, _, errPwnr := processNodeRun(ctx, db, store, proj, wr, mapNodes, &t.ChildNode, int(parentSubNumber), parentNodeRun, nil, nil)
 			if errPwnr != nil {
 				log.Error(ctx, "processWorkflowRun> Unable to process node ID=%d: %s", t.ChildNode.ID, errPwnr)
-				AddWorkflowRunInfo(wr, sdk.SpawnMsgNew(*sdk.MsgWorkflowError, sdk.ExtractHTTPError(errPwnr, "")))
+				AddWorkflowRunInfo(wr, sdk.SpawnMsgNew(*sdk.MsgWorkflowError, sdk.ExtractHTTPError(errPwnr)))
 			}
 			report.Merge(ctx, r1)
 			continue
@@ -242,7 +242,7 @@ func processNode(ctx context.Context, db gorpmapper.SqlExecutorWithTx, store cac
 
 		vcsInf, errVcs = getVCSInfos(ctx, db, store, proj.Key, vcsServer, currentJobGitValues, app.Name, app.VCSServer, app.RepositoryFullname)
 		if errVcs != nil {
-			AddWorkflowRunInfo(wr, sdk.SpawnMsgNew(*sdk.MsgWorkflowError, sdk.ExtractHTTPError(errVcs, "")))
+			AddWorkflowRunInfo(wr, sdk.SpawnMsgNew(*sdk.MsgWorkflowError, sdk.ExtractHTTPError(errVcs)))
 			return nil, false, sdk.WrapError(errVcs, "unable to get git informations")
 		}
 	}
@@ -479,7 +479,7 @@ func computePayload(n *sdk.Node, hookEvent *sdk.WorkflowNodeRunHookEvent, manual
 func computeNodeContextBuildParameters(ctx context.Context, proj sdk.Project, wr *sdk.WorkflowRun, run *sdk.WorkflowNodeRun, n *sdk.Node, runContext nodeRunContext) {
 	nodeRunParams, errParam := getNodeRunBuildParameters(ctx, proj, wr, run, runContext)
 	if errParam != nil {
-		AddWorkflowRunInfo(wr, sdk.SpawnMsgNew(*sdk.MsgWorkflowError, sdk.ExtractHTTPError(errParam, "")))
+		AddWorkflowRunInfo(wr, sdk.SpawnMsgNew(*sdk.MsgWorkflowError, sdk.ExtractHTTPError(errParam)))
 		// if there an error -> display it in workflowRunInfo and not stop the launch
 		log.Error(ctx, "processNode> getNodeRunBuildParameters failed. Project:%s [#%d.%d]%s.%d with payload %v err:%v", proj.Name, wr.Number, run.SubNumber, wr.Workflow.Name, n.ID, run.Payload, errParam)
 	}
diff --git a/engine/api/workflow_import.go b/engine/api/workflow_import.go
index 38ce57306..ef8109531 100644
--- a/engine/api/workflow_import.go
+++ b/engine/api/workflow_import.go
@@ -138,10 +138,10 @@ func (api *API) postWorkflowImportHandler() service.Handler {
 		}
 
 		wrkflw, msgList, globalError := workflow.ParseAndImport(ctx, tx, api.Cache, *proj, wf, ew, getAPIConsumer(ctx), workflow.ImportOptions{Force: force})
-		msgListString := translate(r, msgList)
+		msgListString := translate(msgList)
 		if globalError != nil {
 			if len(msgListString) != 0 {
-				sdkErr := sdk.ExtractHTTPError(globalError, r.Header.Get("Accept-Language"))
+				sdkErr := sdk.ExtractHTTPError(globalError)
 				return service.WriteJSON(w, append(msgListString, sdkErr.Message), sdkErr.Status)
 			}
 			return sdk.WrapError(globalError, "Unable to import workflow %s", ew.GetName())
@@ -222,10 +222,10 @@ func (api *API) putWorkflowImportHandler() service.Handler {
 		defer tx.Rollback() //nolint
 
 		wrkflw, msgList, globalError := workflow.ParseAndImport(ctx, tx, api.Cache, *proj, wf, ew, u, workflow.ImportOptions{Force: true, WorkflowName: wfName})
-		msgListString := translate(r, msgList)
+		msgListString := translate(msgList)
 		if globalError != nil {
 			if len(msgListString) != 0 {
-				sdkErr := sdk.ExtractHTTPError(globalError, r.Header.Get("Accept-Language"))
+				sdkErr := sdk.ExtractHTTPError(globalError)
 				return service.WriteJSON(w, append(msgListString, sdkErr.Error()), sdkErr.Status)
 			}
 			return sdk.WrapError(globalError, "unable to import workflow %s", ew.GetName())
@@ -325,7 +325,7 @@ func (api *API) postWorkflowPushHandler() service.Handler {
 			return err
 		}
 
-		msgListString := translate(r, allMsg)
+		msgListString := translate(allMsg)
 
 		if wrkflw != nil {
 			w.Header().Add(sdk.ResponseWorkflowIDHeader, fmt.Sprintf("%d", wrkflw.ID))
diff --git a/engine/api/workflow_queue.go b/engine/api/workflow_queue.go
index d1f851cbe..c6c40d20c 100644
--- a/engine/api/workflow_queue.go
+++ b/engine/api/workflow_queue.go
@@ -716,7 +716,7 @@ func (api *API) postWorkflowJobStepStatusHandler() service.Handler {
 			log.Warning(ctx, "postWorkflowJobStepStatusHandler> Unable to load workflow run for event: %v", err)
 			return nil
 		}
-		nodeRun.Translate(r.Header.Get("Accept-Language"))
+		nodeRun.Translate()
 		eventsNotifs := notification.GetUserWorkflowEvents(ctx, api.mustDB(), api.Cache, wr.Workflow.ProjectID, wr.Workflow.ProjectKey, work.Name, wr.Workflow.Notifications, nil, nodeRun)
 		event.PublishWorkflowNodeRun(context.Background(), nodeRun, wr.Workflow, eventsNotifs)
 		return nil
diff --git a/engine/api/workflow_run.go b/engine/api/workflow_run.go
index 13dbe5d36..993954551 100644
--- a/engine/api/workflow_run.go
+++ b/engine/api/workflow_run.go
@@ -277,7 +277,7 @@ func (api *API) getLatestWorkflowRunHandler() service.Handler {
 		if err != nil {
 			return sdk.WrapError(err, "Unable to load last workflow run")
 		}
-		run.Translate(r.Header.Get("Accept-Language"))
+		run.Translate()
 		return service.WriteJSON(w, run, http.StatusOK)
 	}
 }
@@ -304,7 +304,6 @@ func (api *API) getWorkflowRunHandler() service.Handler {
 				WithArtifacts:           true,
 				WithLightTests:          true,
 				DisableDetailledNodeRun: !isService && withDetailledNodeRun != "true",
-				Language:                r.Header.Get("Accept-Language"),
 			},
 		)
 		if err != nil {
@@ -325,7 +324,7 @@ func (api *API) getWorkflowRunHandler() service.Handler {
 			}
 		}
 
-		run.Translate(r.Header.Get("Accept-Language"))
+		run.Translate()
 
 		return service.WriteJSON(w, run, http.StatusOK)
 	}
@@ -806,7 +805,7 @@ func (api *API) getWorkflowNodeRunHandler() service.Handler {
 			return sdk.WrapError(err, "Unable to load last workflow run")
 		}
 
-		run.Translate(r.Header.Get("Accept-Language"))
+		run.Translate()
 		return service.WriteJSON(w, run, http.StatusOK)
 	}
 }
@@ -1275,7 +1274,7 @@ func failInitWorkflowRun(ctx context.Context, db *gorp.DbMap, wfRun *sdk.Workflo
 			wfRun.Status = sdk.StatusNeverBuilt
 		}
 	} else {
-		httpErr := sdk.ExtractHTTPError(err, "")
+		httpErr := sdk.ExtractHTTPError(err)
 		isErrWithStack := sdk.IsErrorWithStack(err)
 		fields := log.Fields{}
 		if isErrWithStack {
@@ -1471,11 +1470,10 @@ func (api *API) getWorkflowNodeRunJobSpawnInfosHandler() service.Handler {
 			return sdk.WrapError(err, "cannot load spawn infos for node run job id %d", runJobID)
 		}
 
-		l := r.Header.Get("Accept-Language")
 		for ki, info := range spawnInfos {
 			if _, ok := sdk.Messages[info.Message.ID]; ok {
 				m := sdk.NewMessage(sdk.Messages[info.Message.ID], info.Message.Args...)
-				spawnInfos[ki].UserMessage = m.String(l)
+				spawnInfos[ki].UserMessage = m.String()
 			}
 		}
 		return service.WriteJSON(w, spawnInfos, http.StatusOK)
diff --git a/engine/cdn/redis/reader.go b/engine/cdn/redis/reader.go
index 23a0d21d7..f3d728a5a 100644
--- a/engine/cdn/redis/reader.go
+++ b/engine/cdn/redis/reader.go
@@ -163,6 +163,10 @@ func (r *Reader) loadMoreLines() error {
 }
 
 func (r *Reader) Read(p []byte) (n int, err error) {
+	if r.readEOF {
+		return 0, io.EOF
+	}
+
 	lengthToRead := len(p)
 
 	// If we don't have enough bytes in current buffer we will load some line from Redis
diff --git a/engine/cdn/redis/writer.go b/engine/cdn/redis/writer.go
index f502f0756..cda10e63d 100644
--- a/engine/cdn/redis/writer.go
+++ b/engine/cdn/redis/writer.go
@@ -2,6 +2,7 @@ package redis
 
 import (
 	"context"
+	"fmt"
 	"io"
 	"strconv"
 	"strings"
@@ -17,6 +18,7 @@ type Writer struct {
 	PrefixKey     string
 	currentScore  uint
 	currentBuffer []byte
+	closed        bool
 }
 
 // Add new item in cache + update last usage
@@ -30,6 +32,10 @@ func (w *Writer) add(score uint, value string) error {
 }
 
 func (w *Writer) Write(p []byte) (int, error) {
+	if w.closed {
+		return 0, fmt.Errorf("writer is closed")
+	}
+
 	// Append cnew buffer to current one
 	w.currentBuffer = append(w.currentBuffer, p...)
 
@@ -56,6 +62,7 @@ func (w *Writer) Write(p []byte) (int, error) {
 
 // Close will write the end of the buffer to store in case the last line is not ended by \n
 func (w *Writer) Close() error {
+	w.closed = true
 	if len(w.currentBuffer) > 0 {
 		if err := w.add(w.currentScore, string(w.currentBuffer)+"\n"); err != nil {
 			return err
diff --git a/engine/hatchery/swarm/swarm_util_create.go b/engine/hatchery/swarm/swarm_util_create.go
index e45662ee5..bb8a48832 100644
--- a/engine/hatchery/swarm/swarm_util_create.go
+++ b/engine/hatchery/swarm/swarm_util_create.go
@@ -136,7 +136,7 @@ checkImage:
 			next()
 			hatchery.SendSpawnInfo(ctx, h, spawnArgs.JobID, sdk.SpawnMsg{
 				ID:   sdk.MsgSpawnInfoHatcheryEndDockerPullErr.ID,
-				Args: []interface{}{h.Name(), cArgs.image, sdk.ExtractHTTPError(err, "").Error()},
+				Args: []interface{}{h.Name(), cArgs.image, sdk.ExtractHTTPError(err).Error()},
 			})
 			return sdk.WrapError(err, "Unable to pull image %s on %s", cArgs.image, dockerClient.name)
 		}
diff --git a/engine/service/http.go b/engine/service/http.go
index d65136426..6c0f7171e 100644
--- a/engine/service/http.go
+++ b/engine/service/http.go
@@ -110,8 +110,7 @@ type ErrorResponse struct {
 
 // WriteError is a helper function to return error in a language the called understand
 func WriteError(ctx context.Context, w http.ResponseWriter, r *http.Request, err error) {
-	al := r.Header.Get("Accept-Language")
-	httpErr := sdk.ExtractHTTPError(err, al)
+	httpErr := sdk.ExtractHTTPError(err)
 	isErrWithStack := sdk.IsErrorWithStack(err)
 
 	fields := log.Fields{}
diff --git a/engine/worker/cmd_cds_version_set.go b/engine/worker/cmd_cds_version_set.go
index 10ba25a18..cfc6982a3 100644
--- a/engine/worker/cmd_cds_version_set.go
+++ b/engine/worker/cmd_cds_version_set.go
@@ -85,7 +85,7 @@ func cdsVersionSetCmd() func(cmd *cobra.Command, args []string) {
 
 		if err := f(); err != nil {
 			if sdk.IsErrorWithStack(err) {
-				httpErr := sdk.ExtractHTTPError(err, "")
+				httpErr := sdk.ExtractHTTPError(err)
 				sdk.Exit("%v", httpErr.Error())
 			} else {
 				sdk.Exit("%v", err)
diff --git a/engine/worker/cmd_run.go b/engine/worker/cmd_run.go
index d2670e157..968df776a 100644
--- a/engine/worker/cmd_run.go
+++ b/engine/worker/cmd_run.go
@@ -68,7 +68,7 @@ func runCmd() func(cmd *cobra.Command, args []string) {
 			fields := log.Fields{}
 			if isErrWithStack {
 				fields["stack_trace"] = fmt.Sprintf("%+v", err)
-				fields["request_id"] = sdk.ExtractHTTPError(err, "").RequestID
+				fields["request_id"] = sdk.ExtractHTTPError(err).RequestID
 			}
 			log.ErrorWithFields(ctx, fields, "%v", err)
 			time.Sleep(2 * time.Second)
diff --git a/engine/worker/internal/http_server.go b/engine/worker/internal/http_server.go
index bd10e7b5f..35f5057fe 100644
--- a/engine/worker/internal/http_server.go
+++ b/engine/worker/internal/http_server.go
@@ -99,7 +99,6 @@ func writeJSON(w http.ResponseWriter, data interface{}, status int) {
 }
 
 func writeError(w http.ResponseWriter, r *http.Request, err error) {
-	al := r.Header.Get("Accept-Language")
-	sdkErr := sdk.ExtractHTTPError(err, al)
+	sdkErr := sdk.ExtractHTTPError(err)
 	writeJSON(w, sdkErr, sdkErr.Status)
 }
diff --git a/sdk/error.go b/sdk/error.go
index c81d06179..7ff54162e 100644
--- a/sdk/error.go
+++ b/sdk/error.go
@@ -10,7 +10,6 @@ import (
 	"strings"
 
 	"github.com/pkg/errors"
-	"golang.org/x/text/language"
 )
 
 // Existing CDS errors
@@ -386,189 +385,6 @@ var errorsAmericanEnglish = map[int]string{
 	ErrWebsocketUpgrade.ID:                              "Websocket upgrade required",
 }
 
-var errorsFrench = map[int]string{
-	ErrUnknownError.ID:                                  "erreur interne",
-	ErrActionAlreadyUpdated.ID:                          "le status de l'action a déjà été mis à jour",
-	ErrNoAction.ID:                                      "l'action n'existe pas",
-	ErrActionLoop.ID:                                    "la définition de l'action contient une boucle récursive",
-	ErrInvalidID.ID:                                     "l'ID doit être un nombre entier",
-	ErrInvalidProject.ID:                                "projet manquant",
-	ErrInvalidProjectKey.ID:                             "la clef de project doit uniquement contenir des lettres majuscules et des chiffres",
-	ErrProjectHasPipeline.ID:                            "le project contient un pipeline",
-	ErrProjectHasApplication.ID:                         "le project contient une application",
-	ErrUnauthorized.ID:                                  "authentification invalide",
-	ErrForbidden.ID:                                     "accès refusé",
-	ErrPipelineNotFound.ID:                              "le pipeline n'existe pas",
-	ErrPipelineNotAttached.ID:                           "le pipeline n'est pas lié à l'application",
-	ErrNoEnvironmentProvided.ID:                         "les pipelines de déploiement et de tests requièrent un environnement",
-	ErrEnvironmentProvided.ID:                           "une pipeline de build ne nécessite pas d'environnement",
-	ErrUnknownEnv.ID:                                    "environnement inconnu",
-	ErrEnvironmentExist.ID:                              "l'environnement existe",
-	ErrNoPipelineBuild.ID:                               "ce build n'existe pas",
-	ErrInvalidUsername.ID:                               "nom d'utilisateur invalide",
-	ErrUsernamePresent.ID:                               "le nom d'utilisateur est déjà présent",
-	ErrInvalidEmail.ID:                                  "addresse email invalide",
-	ErrGroupPresent.ID:                                  "le groupe est déjà présent",
-	ErrInvalidName.ID:                                   "le nom est invalide",
-	ErrInvalidUser.ID:                                   "mauvaise combinaison compte/mot de passe utilisateur",
-	ErrBuildArchived.ID:                                 "impossible de relancer ce build car il a été archivé",
-	ErrNoEnvironment.ID:                                 "l'environement n'existe pas",
-	ErrModelNameExist.ID:                                "le nom du modèle de worker est déjà utilisé",
-	ErrNoProject.ID:                                     "le projet n'existe pas",
-	ErrVariableExists.ID:                                "la variable existe déjà",
-	ErrInvalidGroupPattern.ID:                           "nom de groupe invalide '^[a-zA-Z0-9.-_-]{1,}$'",
-	ErrGroupExists.ID:                                   "le groupe existe déjà",
-	ErrNotEnoughAdmin.ID:                                "pas assez d'admin restant",
-	ErrInvalidProjectName.ID:                            "nom de project vide non autorisé",
-	ErrInvalidApplicationPattern.ID:                     "nom de l'application invalide '^[a-zA-Z0-9.-_-]{1,}$'",
-	ErrInvalidWorkerModelNamePattern.ID:                 "nom du worker model invalide '^[a-zA-Z0-9.-_-]{1,}$'",
-	ErrInvalidPipelinePattern.ID:                        "nom du pipeline invalide '^[a-zA-Z0-9.-_-]{1,}$'",
-	ErrNotFound.ID:                                      "la ressource n'existe pas",
-	ErrNoHook.ID:                                        "le hook n'existe pas",
-	ErrNoAttachedPipeline.ID:                            "le pipeline n'est pas lié à l'application",
-	ErrNoReposManager.ID:                                "le gestionnaire de dépôt n'existe pas",
-	ErrNoReposManagerAuth.ID:                            "connexion de CDS au gestionnaire de dépôt refusée, merci de contacter l'administrateur",
-	ErrNoReposManagerClientAuth.ID:                      "connexion au gestionnaire de dépôts refusée, merci de détacher et ré-attacher le repository manager sur votre projet CDS",
-	ErrRepoNotFound.ID:                                  "le dépôt n'existe pas",
-	ErrSecretStoreUnreachable.ID:                        "impossible de contacter vault",
-	ErrSecretKeyFetchFailed.ID:                          "erreur pendnat la récuperation de la clef de chiffrement",
-	ErrCommitsFetchFailed.ID:                            "impossible de retrouver les changements",
-	ErrInvalidGoPath.ID:                                 "le gopath n'est pas valide",
-	ErrInvalidSecretFormat.ID:                           "impossibe de dechiffrer le secret, format invalide",
-	ErrSessionNotFound.ID:                               "session invalide",
-	ErrNoPreviousSuccess.ID:                             "il n'y a aucune précédente version en succès pour ce pipeline",
-	ErrNoPermExecution.ID:                               "vous n'avez pas les droits d'éxécution",
-	ErrInvalidSecretValue.ID:                            "valeur du secret non specifiée",
-	ErrPipelineHasApplication.ID:                        "le pipeline est utilisé par une application",
-	ErrNoDirectSecretUse.ID:                             "l'utilisation du type de paramêtre 'password' est impossible",
-	ErrNoBranch.ID:                                      "la branche est introuvable dans le dépôt",
-	ErrLDAPConn.ID:                                      "erreur de connexion au serveur LDAP",
-	ErrServiceUnavailable.ID:                            "service temporairement indisponible ou en maintenance",
-	ErrParseUserNotification.ID:                         "notification non reconnue",
-	ErrNotSupportedUserNotification.ID:                  "notification non supportée",
-	ErrGroupNeedAdmin.ID:                                "il faut au moins 1 administrateur",
-	ErrGroupNeedWrite.ID:                                "il faut au moins 1 groupe avec les droits d'écriture",
-	ErrNoVariable.ID:                                    "la variable n'existe pas",
-	ErrPluginInvalid.ID:                                 "plugin non valide",
-	ErrApplicationExist.ID:                              "une application du même nom existe déjà",
-	ErrBranchNameNotProvided.ID:                         "le paramètre git.branch ou git.tag est obligatoire",
-	ErrInfiniteTriggerLoop.ID:                           "création d'une boucle de trigger infinie interdite",
-	ErrInvalidResetUser.ID:                              "mauvaise combinaison compte/mail utilisateur",
-	ErrUserConflict.ID:                                  "cet utilisateur existe deja",
-	ErrWrongRequest.ID:                                  "la requête est incorrecte",
-	ErrAlreadyExist.ID:                                  "conflit",
-	ErrInvalidType.ID:                                   "type non valide",
-	ErrParentApplicationAndPipelineMandatory.ID:         "application et pipeline parents obligatoires",
-	ErrNoParentBuildFound.ID:                            "aucun build parent n'a pu être trouvé",
-	ErrParameterExists.ID:                               "le paramètre existe déjà",
-	ErrNoHatchery.ID:                                    "La hatchery n'existe pas",
-	ErrInvalidWorkerStatus.ID:                           "Le status du worker est incorrect",
-	ErrInvalidToken.ID:                                  "Token non valide",
-	ErrAppBuildingPipelines.ID:                          "Impossible de supprimer l'application, il y a pipelines en cours",
-	ErrInvalidTimezone.ID:                               "Fuseau horaire invalide",
-	ErrEnvironmentCannotBeDeleted.ID:                    "L'environement ne peut etre supprimé. Il est encore utilisé.",
-	ErrInvalidPipeline.ID:                               "Pipeline invalide",
-	ErrKeyNotFound.ID:                                   "Clé introuvable",
-	ErrPipelineAlreadyExists.ID:                         "Le pipeline existe déjà",
-	ErrJobAlreadyBooked.ID:                              "Le job est déjà réservé",
-	ErrPipelineBuildNotFound.ID:                         "Le pipeline build n'a pu être trouvé",
-	ErrAlreadyTaken.ID:                                  "Ce job est déjà en cours de traitement par un autre worker",
-	ErrWorkflowNodeNotFound.ID:                          "Noeud de Workflow introuvable",
-	ErrWorkflowInvalidRoot.ID:                           "Racine de Workflow invalide",
-	ErrWorkflowNodeRef.ID:                               "Référence de noeud de workflow invalide",
-	ErrWorkflowInvalid.ID:                               "Workflow invalide",
-	ErrWorkflowNodeJoinNotFound.ID:                      "Jointure introuvable",
-	ErrInvalidJobRequirement.ID:                         "Pré-requis de Job invalide",
-	ErrNotImplemented.ID:                                "La fonctionnalité n'est pas implémentée",
-	ErrParameterNotExists.ID:                            "Ce paramètre n'existe pas",
-	ErrUnknownKeyType.ID:                                "Le type de clé n'est pas connu",
-	ErrInvalidKeyPattern.ID:                             "le nom de la clé doit respecter le pattern suivant; '^[a-zA-Z0-9.-_-]{1,}$'",
-	ErrWebhookConfigDoesNotMatch.ID:                     "la configuration du webhook ne correspond pas",
-	ErrPipelineUsedByWorkflow.ID:                        "Le pipeline est utilisé par un workflow",
-	ErrMethodNotAllowed.ID:                              "La méthode n'est pas autorisée",
-	ErrInvalidNodeNamePattern.ID:                        "Le nom du noeud du workflow doit respecter le pattern suivant; '^[a-zA-Z0-9.-_-]{1,}$'",
-	ErrWorkflowNodeParentNotRun.ID:                      "Il est interdit de lancer un noeuds si ses parents n'ont jamais été lancés",
-	ErrDefaultGroupPermission.ID:                        "Le groupe par défaut ne peut être utilisé qu'en lecture seule",
-	ErrLastGroupWithWriteRole.ID:                        "Le dernier groupe doit avoir les droits d'écriture",
-	ErrInvalidEmailDomain.ID:                            "Domaine invalide",
-	ErrWorkflowNodeRunJobNotFound.ID:                    "Job non trouvé",
-	ErrBuiltinKeyNotFound.ID:                            "Clé de chiffrage introuvable",
-	ErrStepNotFound.ID:                                  "Step introuvable",
-	ErrWorkerModelAlreadyBooked.ID:                      "Le modèle de worker est déjà réservé",
-	ErrConditionsNotOk.ID:                               "Impossible de démarrer ce pipeline car les conditions de lancement ne sont pas respectées",
-	ErrDownloadInvalidOS.ID:                             "OS invalide. L'OS doit être linux, darwin, freebsd ou windows",
-	ErrDownloadInvalidArch.ID:                           "Architecture invalide. L'architecture doit être 386, i386, i686, amd64, x86_64 ou arm (dépendant de l'OS)",
-	ErrDownloadInvalidName.ID:                           "Nom invalide",
-	ErrDownloadDoesNotExist.ID:                          "Le fichier n'existe pas",
-	ErrTokenNotFound.ID:                                 "Le token n'existe pas",
-	ErrWorkflowNotificationNodeRef.ID:                   "Une référence de noeud de workflow est invalide dans vos notifications (si vous souhaitez supprimer un pipeline vérifiez qu'il ne soit plus référencé dans la liste de vos notifications)",
-	ErrInvalidJobRequirementDuplicateModel.ID:           "Pré-requis de job invalides: vous ne pouvez pas séléctionnez plusieurs modèles de worker",
-	ErrInvalidJobRequirementDuplicateHostname.ID:        "Pré-requis de job invalides: vous ne pouvez pas séléctionnez plusieurs hostname",
-	ErrInvalidKeyName.ID:                                "Nom de clé invalide. Les clés d'application doivent être préfixées par 'app-', les clés d'environnement doivent être préfixées par 'env-'",
-	ErrRepoOperationTimeout.ID:                          "L'analyse du dépôt a pris trop de temps",
-	ErrInvalidGitBranch.ID:                              "Valeur git.branch invalide, vous ne pouvez pas avoir de valeur git.branch avec une string vide dans votre payload par défaut",
-	ErrInvalidFavoriteType.ID:                           "Type de favori invalide: doit être 'projet' ou 'workflow'",
-	ErrUnsupportedOSArchPlugin.ID:                       "OS/Architecture non supporté pour ce plugin",
-	ErrNoBroadcast.ID:                                   "Information invalide",
-	ErrBroadcastNotFound.ID:                             "Information non trouvée",
-	ErrInvalidPatternModel.ID:                           "Pattern de modèle de worker invalide: le nom, type et commande principale sont requis",
-	ErrWorkerModelNoAdmin.ID:                            "Accès refusé: vous n'êtes ni un administrateur CDS ni un administrateur du groupe pour lequel vous tentez de créer votre modèle",
-	ErrWorkerModelNoPattern.ID:                          "Accès refusé: vous devez obligatoirement sélectionner un pattern de script de configuration. Si vous souhaitez ajouter un pattern particulier, veuillez contacter un administrateur CDS",
-	ErrJobNotBooked.ID:                                  "Le job est déjà libéré",
-	ErrUserNotFound.ID:                                  "Utilisateur non trouvé",
-	ErrInvalidNumber.ID:                                 "Nombre non valide",
-	ErrKeyAlreadyExist.ID:                               "La clé existe déjà",
-	ErrPipelineNameImport.ID:                            "Le nom du pipeline dans le code ne correspond pas au nom du pipeline que vous voulez éditer",
-	ErrWorkflowNameImport.ID:                            "Le nom du workflow dans le code ne correspond pas au nom du workflow que vous voulez éditer",
-	ErrIconBadFormat.ID:                                 "Mauvais format d'icône, doit être une image",
-	ErrIconBadSize.ID:                                   "Taille de l'icône trop importante. (max 100Ko)",
-	ErrWorkflowConditionBadOperator.ID:                  "Opérateur de condition de lancement incorrect",
-	ErrColorBadFormat.ID:                                "Format de la couleur incorrect. Vous devez utiliser le format hexadécimal (exemple: #FFFF)",
-	ErrInvalidHookConfiguration.ID:                      "Configuration de hook invalide",
-	ErrInvalidData.ID:                                   "Impossible de valider les données",
-	ErrInvalidGroupAdmin.ID:                             "L'utilisateur n'est pas administrateur du groupe",
-	ErrInvalidGroupMember.ID:                            "L'utilisateur n'est pas membre du groupe",
-	ErrWorkflowNotGenerated.ID:                          "Le workflow n'a pas été généré par un template",
-	ErrInvalidNodeDefaultPayload.ID:                     "Le workflow est incorrect. Un payload par défaut ne peut pas être sur un pipeline autre que le premier du workflow",
-	ErrInvalidApplicationRepoStrategy.ID:                "La stratégie de dépôt (vcs) de l'application n'est pas correcte",
-	ErrWorkflowNodeRootUpdate.ID:                        "Impossible de mettre à jour ou supprimer le noeud racine du workflow",
-	ErrWorkflowAlreadyAsCode.ID:                         "Le workflow est déjà as-code ou il y a déjà une pull-request pour le transformer",
-	ErrNoDBMigrationID.ID:                               "Cet id n'existe pas dans la table gorp_migrations",
-	ErrCannotParseTemplate.ID:                           "Impossible de parser le modèle de workflow",
-	ErrGroupNotFoundInProject.ID:                        "Impossible d'ajouter ce groupe dans vos permissions de workflow car ce groupe n'est pas présent dans les permissions de votre projet",
-	ErrGroupNotFoundInWorkflow.ID:                       "Impossible d'ajouter ce groupe dans vos permissions de noeud du workflow car ce groupe n'est pas présent dans les permissions de votre workflow",
-	ErrWorkflowPermInsufficient.ID:                      "Impossible d'ajouter ce groupe dans vos permissions du workflow car ce groupe a des droits inférieurs (< RWX) à celui du workflow",
-	ErrApplicationUsedByWorkflow.ID:                     "L'application est utilisée par un workflow",
-	ErrLocked.ID:                                        "La ressource est verrouillée",
-	ErrInvalidJobRequirementWorkerModelPermission.ID:    "Pré-requis de job invalide: Modèle de worker inutilisable en raison des permissions",
-	ErrInvalidJobRequirementWorkerModelCapabilitites.ID: "Pré-requis de job invalide: Le modèle de worker ne dispose pas de binaires suffisants",
-	ErrMalformattedStep.ID:                              "Étape malformée",
-	ErrVCSUsedByApplication.ID:                          "Le gestionnaire de dépot est encore utilisé par une application",
-	ErrApplicationAsCodeOverride.ID:                     "Vous ne pouvez pas importer l'application depuis ce dépôt",
-	ErrPipelineAsCodeOverride.ID:                        "Vous ne pouvez pas importer le pipeline depuis ce dépôt",
-	ErrEnvironmentAsCodeOverride.ID:                     "Vous ne pouvez pas importer l'environment depuis ce dépôt",
-	ErrWorkflowAsCodeOverride.ID:                        "Vous ne pouvez pas importer le workflow depuis ce dépôt",
-	ErrProjectSecretDataUnknown.ID:                      "Donnée chiffrée non valide",
-	ErrApplicationMandatoryOnWorkflowAsCode.ID:          "Une application liée à un dépôt git est obligatoire à la racine du workflow",
-	ErrInvalidPayloadVariable.ID:                        "Le payload du workflow ne peut pas contenir de clés nommées cds.*",
-	ErrInvalidPassword.ID:                               "Votre valeur de type mot de passe n'est pas correct",
-	ErrRepositoryUsedByHook.ID:                          "Il y a encore un repository webhook sur ce dépôt",
-	ErrResourceNotInProject.ID:                          "La ressource n'est pas lié au projet",
-	ErrEnvironmentNotFound.ID:                           "L'environnement n'existe pas",
-	ErrIntegrationtNotFound.ID:                          "L'intégration n'existe pas",
-	ErrSignupDisabled.ID:                                "La création de compte est désactivée pour ce mode d'authentification.",
-	ErrBadBrokerConfiguration.ID:                        "Impossible de se connecter à votre intégration de type évènement. Veuillez vérifier votre configuration",
-	ErrInvalidJobRequirementNetworkAccess.ID:            "Pré-requis de job invalide: Le pré-requis network doit contenir un ':'. Exemple: golang.org:http, golang.org:443",
-	ErrWorkflowAsCodeResync.ID:                          "Impossible de resynchroniser un workflow en mode as-code",
-	ErrWorkflowNodeNameDuplicate.ID:                     "Vous ne pouvez pas avoir plusieurs fois le même nom de pipeline dans votre workflow",
-	ErrUnsupportedMediaType.ID:                          "Le format de la requête est invalide",
-	ErrNothingToPush.ID:                                 "Aucune modification à pousser",
-	ErrWorkerErrorCommand.ID:                            "Commande du worker en erreur",
-	ErrRepoAnalyzeFailed.ID:                             "L'analyse du repository a echoué",
-	ErrConflictData.ID:                                  "Donnée en conflit",
-	ErrWebsocketUpgrade.ID:                              "Websocket upgrade requis",
-}
-
 // Error type.
 type Error struct {
 	ID         int         `json:"id"`
@@ -610,24 +426,8 @@ func (e Error) printLight() string {
 	return message
 }
 
-func (e Error) Translate(al string) string {
-	acceptedLanguages, _, err := language.ParseAcceptLanguage(al)
-	if err != nil {
-		acceptedLanguages = []language.Tag{language.AmericanEnglish}
-	}
-
-	// try to get error message for accepted language and error ID, else use unknown error message
-	tag, _, _ := matcher.Match(acceptedLanguages...)
-	var msg string
-	var ok bool
-	switch tag {
-	case language.French:
-		msg, ok = errorsFrench[e.ID]
-	case language.AmericanEnglish:
-		msg, ok = errorsAmericanEnglish[e.ID]
-	default:
-		msg, ok = errorsAmericanEnglish[e.ID]
-	}
+func (e Error) Translate() string {
+	msg, ok := errorsAmericanEnglish[e.ID]
 	if !ok {
 		return errorsAmericanEnglish[ErrUnknownError.ID]
 	}
@@ -646,7 +446,7 @@ func NewErrorWithStack(root error, err error) error {
 	case Error:
 		errWithStack.httpError = e
 	default:
-		errWithStack.httpError = ExtractHTTPError(err, "")
+		errWithStack.httpError = ExtractHTTPError(err)
 	}
 
 	return errWithStack
@@ -756,7 +556,7 @@ func NewError(httpError Error, err error) error {
 	if e, ok := err.(*MultiError); ok {
 		var ss []string
 		for i := range *e {
-			ss = append(ss, ExtractHTTPError((*e)[i], "").printLight())
+			ss = append(ss, ExtractHTTPError((*e)[i]).printLight())
 		}
 		httpError.From = strings.Join(ss, ", ")
 	} else {
@@ -816,7 +616,7 @@ func WrapError(err error, format string, args ...interface{}) error {
 	if e, ok := err.(*MultiError); ok {
 		var ss []string
 		for i := range *e {
-			ss = append(ss, ExtractHTTPError((*e)[i], "").printLight())
+			ss = append(ss, ExtractHTTPError((*e)[i]).printLight())
 		}
 		httpError.From = strings.Join(ss, ", ")
 	}
@@ -848,7 +648,7 @@ func WithStack(err error) error {
 	// if it's a Error wrap it in error with stack
 	if e, ok := err.(Error); ok {
 		return errorWithStack{
-			root:      errors.New(e.Translate("")),
+			root:      errors.New(e.Translate()),
 			stack:     callers(),
 			httpError: e,
 		}
@@ -871,7 +671,7 @@ func WithData(err error, data interface{}) error {
 
 // ExtractHTTPError tries to recognize given error and return http error
 // with message in a language matching Accepted-Language.
-func ExtractHTTPError(source error, al string) Error {
+func ExtractHTTPError(source error) Error {
 	var httpError Error
 
 	// try to recognize http error from source
@@ -880,7 +680,7 @@ func ExtractHTTPError(source error, al string) Error {
 		httpError = ErrUnknownError
 		var ss []string
 		for i := range *e {
-			ss = append(ss, ExtractHTTPError((*e)[i], al).printLight())
+			ss = append(ss, ExtractHTTPError((*e)[i]).printLight())
 		}
 		httpError.Message = strings.Join(ss, ", ")
 	case errorWithStack:
@@ -899,7 +699,7 @@ func ExtractHTTPError(source error, al string) Error {
 	// if error's message is not empty do not override (custom message)
 	// else set message for given accepted languages.
 	if httpError.Message == "" {
-		httpError.Message = httpError.Translate(al)
+		httpError.Message = httpError.Translate()
 	}
 
 	return httpError
diff --git a/sdk/hatchery/starter.go b/sdk/hatchery/starter.go
index 7c3017584..70dfc8935 100644
--- a/sdk/hatchery/starter.go
+++ b/sdk/hatchery/starter.go
@@ -206,7 +206,7 @@ func spawnWorkerForJob(ctx context.Context, h Interface, j workerStarterRequest)
 		ctxSendSpawnInfo, next = telemetry.Span(ctxJob, "hatchery.QueueJobSendSpawnInfo", telemetry.Tag("status", "errSpawn"), telemetry.Tag("msg", sdk.MsgSpawnInfoHatcheryErrorSpawn.ID))
 		SendSpawnInfo(ctxSendSpawnInfo, h, j.id, sdk.SpawnMsg{
 			ID:   sdk.MsgSpawnInfoHatcheryErrorSpawn.ID,
-			Args: []interface{}{h.Service().Name, modelName, sdk.Round(time.Since(start), time.Second).String(), sdk.ExtractHTTPError(errSpawn, "").Error()},
+			Args: []interface{}{h.Service().Name, modelName, sdk.Round(time.Since(start), time.Second).String(), sdk.ExtractHTTPError(errSpawn).Error()},
 		})
 		log.Error(ctx, "hatchery %s cannot spawn worker %s for job %d: %v", h.Service().Name, modelName, j.id, errSpawn)
 		next()
diff --git a/sdk/messages.go b/sdk/messages.go
index b9ef3fd3d..dabe230e3 100644
--- a/sdk/messages.go
+++ b/sdk/messages.go
@@ -206,19 +206,8 @@ var (
 )
 
 //String returns formated string for the specified language
-func (m *Message) String(al string) string {
-	acceptedLanguages, _, err := language.ParseAcceptLanguage(al)
-	if err != nil {
-		return fmt.Sprintf(m.Format[EN], m.Args...)
-	}
-
-	t, _, _ := matcher.Match(acceptedLanguages...)
-	switch t {
-	case language.French, language.AmericanEnglish:
-		return fmt.Sprintf(m.Format[lang(t)], m.Args...)
-	default:
-		return fmt.Sprintf(m.Format[EN], m.Args...)
-	}
+func (m *Message) String() string {
+	return fmt.Sprintf(m.Format[EN], m.Args...)
 }
 
 // MessagesToError returns a translated slices of messages as an error
@@ -228,14 +217,14 @@ func MessagesToError(messages []Message) error {
 		if i != 0 {
 			s += "; "
 		}
-		s += err.String(language.AmericanEnglish.String())
+		s += err.String()
 	}
 	return errors.New(s)
 }
 
 // ErrorToMessage returns message from an error if possible
 func ErrorToMessage(err error) (Message, bool) {
-	cdsError := ExtractHTTPError(err, "EN")
+	cdsError := ExtractHTTPError(err)
 	switch cdsError.ID {
 	case ErrPipelineNotFound.ID:
 		return NewMessage(MsgWorkflowErrorBadPipelineName, cdsError.Data), true
diff --git a/sdk/repositories_operation.go b/sdk/repositories_operation.go
index 578a87801..3ccc6b8ef 100644
--- a/sdk/repositories_operation.go
+++ b/sdk/repositories_operation.go
@@ -40,7 +40,7 @@ func ToOperationError(err error) *OperationError {
 	if err == nil {
 		return nil
 	}
-	sdkError := ExtractHTTPError(err, "")
+	sdkError := ExtractHTTPError(err)
 	return &OperationError{
 		ID:         sdkError.ID,
 		Status:     sdkError.Status,
diff --git a/sdk/workflow_run.go b/sdk/workflow_run.go
index 13948b73c..c703791ff 100644
--- a/sdk/workflow_run.go
+++ b/sdk/workflow_run.go
@@ -198,11 +198,11 @@ func (w WorkflowRunVersion) IsValid() error {
 }
 
 // Translate translates messages in WorkflowNodeRun
-func (r *WorkflowRun) Translate(lang string) {
+func (r *WorkflowRun) Translate() {
 	for ki, info := range r.Infos {
 		if _, ok := Messages[info.Message.ID]; ok {
 			m := NewMessage(Messages[info.Message.ID], info.Message.Args...)
-			r.Infos[ki].UserMessage = m.String(lang)
+			r.Infos[ki].UserMessage = m.String()
 		}
 	}
 }
@@ -428,10 +428,10 @@ type WorkflowNodeTriggerRun struct {
 }
 
 // Translate translates messages in WorkflowNodeRun
-func (nr *WorkflowNodeRun) Translate(lang string) {
+func (nr *WorkflowNodeRun) Translate() {
 	for ks := range nr.Stages {
 		for kj := range nr.Stages[ks].RunJobs {
-			nr.Stages[ks].RunJobs[kj].Translate(lang)
+			nr.Stages[ks].RunJobs[kj].Translate()
 		}
 	}
 }
@@ -550,11 +550,11 @@ type WorkflowNodeJobRunBooked struct {
 }
 
 // Translate translates messages in WorkflowNodeJobRun
-func (wnjr *WorkflowNodeJobRun) Translate(lang string) {
+func (wnjr *WorkflowNodeJobRun) Translate() {
 	for ki, info := range wnjr.SpawnInfos {
 		if _, ok := Messages[info.Message.ID]; ok {
 			m := NewMessage(Messages[info.Message.ID], info.Message.Args...)
-			wnjr.SpawnInfos[ki].UserMessage = m.String(lang)
+			wnjr.SpawnInfos[ki].UserMessage = m.String()
 		}
 	}
 }
diff --git a/ui/src/app/app.component.spec.ts b/ui/src/app/app.component.spec.ts
index a2049bd8f..39339cf76 100644
--- a/ui/src/app/app.component.spec.ts
+++ b/ui/src/app/app.component.spec.ts
@@ -26,7 +26,6 @@ import { ApplicationService } from './service/application/application.service';
 import { AuthenticationService } from './service/authentication/authentication.service';
 import { BroadcastService } from './service/broadcast/broadcast.service';
 import { BroadcastStore } from './service/broadcast/broadcast.store';
-import { LanguageStore } from './service/language/language.store';
 import { NavbarService } from './service/navbar/navbar.service';
 import { NotificationService } from './service/notification/notification.service';
 import { PipelineService } from './service/pipeline/pipeline.service';
@@ -62,7 +61,6 @@ describe('App: CDS', () => {
                 ProjectStore,
                 EnvironmentService,
                 AuthenticationService,
-                LanguageStore,
                 ThemeStore,
                 NotificationService,
                 AppService,
diff --git a/ui/src/app/app.component.ts b/ui/src/app/app.component.ts
index 1021836e4..7b5c873aa 100644
--- a/ui/src/app/app.component.ts
+++ b/ui/src/app/app.component.ts
@@ -16,7 +16,6 @@ import { Subscription } from 'rxjs/Subscription';
 import * as format from 'string-format-obj';
 import { AppService } from './app.service';
 import { AuthentifiedUser } from './model/user.model';
-import { LanguageStore } from './service/language/language.store';
 import { NotificationService } from './service/notification/notification.service';
 import { HelpService, MonitoringService } from './service/services.module';
 import { ThemeStore } from './service/theme/theme.store';
@@ -58,7 +57,6 @@ export class AppComponent implements OnInit, OnDestroy {
 
     constructor(
         _translate: TranslateService,
-        private _language: LanguageStore,
         private _theme: ThemeStore,
         private _activatedRoute: ActivatedRoute,
         private _titleService: Title,
diff --git a/ui/src/app/service/language/language.interceptor.ts b/ui/src/app/service/language/language.interceptor.ts
deleted file mode 100644
index 8da94f444..000000000
--- a/ui/src/app/service/language/language.interceptor.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
-import { Injectable } from '@angular/core';
-import { Observable } from 'rxjs';
-import { LanguageStore } from './../language/language.store';
-
-@Injectable()
-export class LanguageInterceptor implements HttpInterceptor {
-  languageHeader: string;
-
-  constructor(
-    private _language: LanguageStore
-  ) {
-    this.languageHeader = 'en-US';
-
-    this._language.get().subscribe(l => {
-      if (l) {
-        this.languageHeader = l;
-      }
-    });
-  }
-
-  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
-    if (req.url.indexOf('assets/i18n') !== -1) {
-      return next.handle(req.clone());
-    }
-
-    return next.handle(req.clone({
-      setHeaders: { 'Accept-Language': this.languageHeader }
-    }));
-  }
-}
diff --git a/ui/src/app/service/language/language.store.ts b/ui/src/app/service/language/language.store.ts
deleted file mode 100644
index 67822aa2e..000000000
--- a/ui/src/app/service/language/language.store.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import { Injectable } from '@angular/core';
-import * as moment from 'moment';
-import { BehaviorSubject, Observable } from 'rxjs';
-
-@Injectable()
-export class LanguageStore {
-    private _language: BehaviorSubject<string> = new BehaviorSubject(null);
-
-    constructor() {
-        this._language.next('en')
-        moment.locale('en');
-    }
-
-    get() {
-        return new Observable<string>(fn => this._language.subscribe(fn));
-    }
-}
diff --git a/ui/src/app/service/services.module.ts b/ui/src/app/service/services.module.ts
index 3bf6ff6f3..f5a8dec06 100644
--- a/ui/src/app/service/services.module.ts
+++ b/ui/src/app/service/services.module.ts
@@ -34,8 +34,6 @@ import { HookService } from './hook/hook.service';
 import { ImportAsCodeService } from './import-as-code/import.service';
 import { IntegrationService } from './integration/integration.service';
 import { KeyService } from './keys/keys.service';
-import { LanguageInterceptor } from './language/language.interceptor';
-import { LanguageStore } from './language/language.store';
 import { MonitoringService } from './monitoring/monitoring.service';
 import { NavbarService } from './navbar/navbar.service';
 import { NotificationService } from './notification/notification.service';
@@ -104,7 +102,6 @@ export class ServicesModule {
                 BroadcastService,
                 BroadcastStore,
                 KeyService,
-                LanguageStore,
                 ThemeStore,
                 NavbarService,
                 NotificationService,
@@ -143,11 +140,6 @@ export class ServicesModule {
                     useClass: XSRFInterceptor,
                     multi: true
                 },
-                {
-                    provide: HTTP_INTERCEPTORS,
-                    useClass: LanguageInterceptor,
-                    multi: true
-                },
                 {
                     provide: HTTP_INTERCEPTORS,
                     useClass: LogoutInterceptor,
@@ -180,7 +172,6 @@ export {
     ImportAsCodeService,
     BroadcastStore,
     KeyService,
-    LanguageStore,
     ThemeStore,
     ParameterService,
     MonitoringService,


Test patch:
diff --git a/engine/api/ascode_test.go b/engine/api/ascode_test.go
index 32764412a..9bdad0cac 100644
--- a/engine/api/ascode_test.go
+++ b/engine/api/ascode_test.go
@@ -48,7 +48,7 @@ func writeError(w *http.Response, err error) (*http.Response, error) {
 	body := new(bytes.Buffer)
 	enc := json.NewEncoder(body)
 	w.Body = ioutil.NopCloser(body)
-	sdkErr := sdk.ExtractHTTPError(err, "")
+	sdkErr := sdk.ExtractHTTPError(err)
 	enc.Encode(sdkErr)
 	w.StatusCode = sdkErr.Status
 	return w, sdkErr
diff --git a/engine/api/pipeline/pipeline_importer_test.go b/engine/api/pipeline/pipeline_importer_test.go
index 869e0b607..0e8777f7c 100644
--- a/engine/api/pipeline/pipeline_importer_test.go
+++ b/engine/api/pipeline/pipeline_importer_test.go
@@ -45,7 +45,7 @@ func testImportUpdate(t *testing.T, db gorp.SqlExecutor, store cache.Store, tt t
 				done <- true
 				return
 			}
-			log.Debug("[TEST] %s >>> %s", tt.name, msg.String("en"))
+			log.Debug("[TEST] %s >>> %s", tt.name, msg.String())
 		}
 	}()
 
diff --git a/engine/api/workflow/process_node_test.go b/engine/api/workflow/process_node_test.go
index 1b2516091..71e56f90f 100644
--- a/engine/api/workflow/process_node_test.go
+++ b/engine/api/workflow/process_node_test.go
@@ -2776,7 +2776,7 @@ func writeError(w *http.Response, err error) (*http.Response, error) {
 	body := new(bytes.Buffer)
 	enc := json.NewEncoder(body)
 	w.Body = ioutil.NopCloser(body)
-	sdkErr := sdk.ExtractHTTPError(err, "")
+	sdkErr := sdk.ExtractHTTPError(err)
 	_ = enc.Encode(sdkErr) // nolint
 	w.StatusCode = sdkErr.Status
 	return w, sdkErr
diff --git a/engine/api/workflowtemplate/execute_test.go b/engine/api/workflowtemplate/execute_test.go
index f317718f4..3cec9cf1d 100644
--- a/engine/api/workflowtemplate/execute_test.go
+++ b/engine/api/workflowtemplate/execute_test.go
@@ -177,7 +177,7 @@ name: Environment-[[if .id]]`)),
 
 	_, err := workflowtemplate.Parse(tmpl)
 	assert.NotNil(t, err)
-	e := sdk.ExtractHTTPError(err, "")
+	e := sdk.ExtractHTTPError(err)
 	assert.Equal(t, sdk.ErrCannotParseTemplate.ID, e.ID)
 	errs := []sdk.WorkflowTemplateError{{
 		Type:    "workflow",
diff --git a/engine/cdn/redis/reader_test.go b/engine/cdn/redis/reader_test.go
new file mode 100644
index 000000000..035b20ba0
--- /dev/null
+++ b/engine/cdn/redis/reader_test.go
@@ -0,0 +1,58 @@
+package redis_test
+
+import (
+	"io"
+	"strings"
+	"testing"
+
+	"github.com/ovh/cds/engine/cache"
+	"github.com/ovh/cds/engine/cdn/redis"
+	"github.com/ovh/cds/engine/test"
+	"github.com/ovh/cds/sdk"
+	"github.com/stretchr/testify/require"
+)
+
+func TestReader_EOF(t *testing.T) {
+	cfg := test.LoadTestingConf(t, sdk.TypeCDN)
+
+	c, err := cache.New(cfg["redisHost"], cfg["redisPassword"], -1)
+	require.NoError(t, err)
+
+	cacheKey := cache.Key("test:cdn:item")
+
+	itemID := sdk.RandomString(10)
+
+	w := &redis.Writer{
+		Store:     c,
+		ItemID:    itemID,
+		PrefixKey: cacheKey,
+	}
+
+	r := &redis.Reader{
+		Store:     c,
+		ItemID:    itemID,
+		PrefixKey: cacheKey,
+		Format:    sdk.CDNReaderFormatJSON,
+	}
+
+	t.Logf("writings logs for fake item with id: %s", itemID)
+
+	// Insert lines in redis
+	_, err = io.Copy(w, strings.NewReader("this is the first line\nthis is the second line\n"))
+	require.NoError(t, err)
+
+	// Read all lines without closing the reader
+	buf1 := new(strings.Builder)
+	_, err = io.Copy(buf1, r)
+	require.NoError(t, err)
+	require.Equal(t, "[{\"number\":0,\"value\":\"this is the first line\\n\"},{\"number\":1,\"value\":\"this is the second line\\n\"}]", buf1.String())
+
+	// Insert a new line in redis
+	_, err = io.Copy(w, strings.NewReader("this is the third line\n"))
+	require.NoError(t, err)
+
+	// Try to read again from same reader should return an error
+	buf := make([]byte, 1)
+	_, err = r.Read(buf)
+	require.Error(t, err, "read should return EOF")
+}
diff --git a/engine/cdn/redis/writer_test.go b/engine/cdn/redis/writer_test.go
new file mode 100644
index 000000000..946a990ab
--- /dev/null
+++ b/engine/cdn/redis/writer_test.go
@@ -0,0 +1,36 @@
+package redis_test
+
+import (
+	"io"
+	"strings"
+	"testing"
+
+	"github.com/ovh/cds/engine/cache"
+	"github.com/ovh/cds/engine/cdn/redis"
+	"github.com/ovh/cds/engine/test"
+	"github.com/ovh/cds/sdk"
+	"github.com/stretchr/testify/require"
+)
+
+func TestWriter_Closed(t *testing.T) {
+	cfg := test.LoadTestingConf(t, sdk.TypeCDN)
+	c, err := cache.New(cfg["redisHost"], cfg["redisPassword"], -1)
+	require.NoError(t, err)
+
+	cacheKey := cache.Key("test:cdn:item")
+	itemID := sdk.RandomString(10)
+
+	w := &redis.Writer{
+		Store:     c,
+		ItemID:    itemID,
+		PrefixKey: cacheKey,
+	}
+
+	_, err = io.Copy(w, strings.NewReader("this is the first line\nthis is the second line\n"))
+	require.NoError(t, err)
+	require.NoError(t, w.Close())
+
+	_, err = io.Copy(w, strings.NewReader("this is the third line\n"))
+	require.Error(t, err)
+	require.Equal(t, "writer is closed", err.Error())
+}
diff --git a/sdk/error_test.go b/sdk/error_test.go
index 6ce03563f..233a5abdb 100644
--- a/sdk/error_test.go
+++ b/sdk/error_test.go
@@ -69,12 +69,12 @@ func TestNewError(t *testing.T) {
 	// print the error stack trace
 	t.Logf("%+v\n", err)
 
-	httpErr := ExtractHTTPError(err, "fr")
+	httpErr := ExtractHTTPError(err)
 
 	// print the http error
 	t.Log(httpErr)
 
-	assert.Equal(t, "la requête est incorrecte (from: this is an error generated from vendor)", httpErr.Error())
+	assert.Equal(t, "wrong request (from: this is an error generated from vendor)", httpErr.Error())
 }
 
 func TestWrapError(t *testing.T) {
@@ -118,23 +118,23 @@ func TestCause(t *testing.T) {
 func TestNewAdvancedError(t *testing.T) {
 	err := NewErrorFrom(ErrWrongRequest, "this is an error generated from vendor")
 	assert.Equal(t, "TestNewAdvancedError: wrong request (from: this is an error generated from vendor)", err.Error())
-	httpErr := ExtractHTTPError(err, "fr")
-	assert.Equal(t, "la requête est incorrecte (from: this is an error generated from vendor)", httpErr.Error())
+	httpErr := ExtractHTTPError(err)
+	assert.Equal(t, "wrong request (from: this is an error generated from vendor)", httpErr.Error())
 
 	err = WrapError(err, "Something no visible for http error")
 	assert.Equal(t, "TestNewAdvancedError: wrong request (from: this is an error generated from vendor) (caused by: Something no visible for http error: this is an error generated from vendor)", err.Error())
-	httpErr = ExtractHTTPError(err, "fr")
-	assert.Equal(t, "la requête est incorrecte (from: this is an error generated from vendor)", httpErr.Error())
+	httpErr = ExtractHTTPError(err)
+	assert.Equal(t, "wrong request (from: this is an error generated from vendor)", httpErr.Error())
 
 	err = NewError(ErrAlreadyTaken, err)
 	assert.Equal(t, "TestNewAdvancedError: This job is already taken by another worker (from: this is an error generated from vendor) (caused by: Something no visible for http error: this is an error generated from vendor)", err.Error())
-	httpErr = ExtractHTTPError(err, "fr")
-	assert.Equal(t, "Ce job est déjà en cours de traitement par un autre worker (from: this is an error generated from vendor)", httpErr.Error())
+	httpErr = ExtractHTTPError(err)
+	assert.Equal(t, "This job is already taken by another worker (from: this is an error generated from vendor)", httpErr.Error())
 
 	err = NewErrorWithStack(err, NewErrorFrom(ErrNotFound, "can't found this"))
 	assert.Equal(t, "TestNewAdvancedError: resource not found (from: can't found this) (caused by: Something no visible for http error: this is an error generated from vendor)", err.Error())
-	httpErr = ExtractHTTPError(err, "fr")
-	assert.Equal(t, "la ressource n'existe pas (from: can't found this)", httpErr.Error())
+	httpErr = ExtractHTTPError(err)
+	assert.Equal(t, "resource not found (from: can't found this)", httpErr.Error())
 }
 
 func TestMultiError(t *testing.T) {
@@ -150,23 +150,23 @@ func TestMultiError(t *testing.T) {
 	mError.Append(errFourth)
 
 	assert.Equal(t, "TestMultiError: internal server error (caused by: my first error), TestMultiError: wrong request (from: my second error), TestMultiError: internal server error (caused by: hidden info: my third error), TestMultiError: already exists (from: my fourth error)", mError.Error())
-	httpErr := ExtractHTTPError(mError, "fr")
-	assert.Equal(t, "erreur interne, la requête est incorrecte: my second error, erreur interne, conflit: my fourth error", httpErr.Error())
+	httpErr := ExtractHTTPError(mError)
+	assert.Equal(t, "internal server error, wrong request: my second error, internal server error, already exists: my fourth error", httpErr.Error())
 
 	wrappedErr := NewError(ErrWrongRequest, mError)
 	assert.Equal(t, "TestMultiError: wrong request (from: internal server error, wrong request: my second error, internal server error, already exists: my fourth error) (caused by: TestMultiError: internal server error (caused by: my first error), TestMultiError: wrong request (from: my second error), TestMultiError: internal server error (caused by: hidden info: my third error), TestMultiError: already exists (from: my fourth error))", wrappedErr.Error())
-	httpErr = ExtractHTTPError(wrappedErr, "fr")
-	assert.Equal(t, "la requête est incorrecte (from: internal server error, wrong request: my second error, internal server error, already exists: my fourth error)", httpErr.Error())
+	httpErr = ExtractHTTPError(wrappedErr)
+	assert.Equal(t, "wrong request (from: internal server error, wrong request: my second error, internal server error, already exists: my fourth error)", httpErr.Error())
 
 	stackErr := WithStack(mError)
 	assert.Equal(t, "TestMultiError: internal server error (from: internal server error, wrong request: my second error, internal server error, already exists: my fourth error) (caused by: TestMultiError: internal server error (caused by: my first error), TestMultiError: wrong request (from: my second error), TestMultiError: internal server error (caused by: hidden info: my third error), TestMultiError: already exists (from: my fourth error))", stackErr.Error())
-	httpErr = ExtractHTTPError(stackErr, "fr")
-	assert.Equal(t, "erreur interne (from: internal server error, wrong request: my second error, internal server error, already exists: my fourth error)", httpErr.Error())
+	httpErr = ExtractHTTPError(stackErr)
+	assert.Equal(t, "internal server error (from: internal server error, wrong request: my second error, internal server error, already exists: my fourth error)", httpErr.Error())
 
 	stackErr = WrapError(mError, "more info")
 	assert.Equal(t, "TestMultiError: internal server error (from: internal server error, wrong request: my second error, internal server error, already exists: my fourth error) (caused by: more info: TestMultiError: internal server error (caused by: my first error), TestMultiError: wrong request (from: my second error), TestMultiError: internal server error (caused by: hidden info: my third error), TestMultiError: already exists (from: my fourth error))", stackErr.Error())
-	httpErr = ExtractHTTPError(stackErr, "fr")
-	assert.Equal(t, "erreur interne (from: internal server error, wrong request: my second error, internal server error, already exists: my fourth error)", httpErr.Error())
+	httpErr = ExtractHTTPError(stackErr)
+	assert.Equal(t, "internal server error (from: internal server error, wrong request: my second error, internal server error, already exists: my fourth error)", httpErr.Error())
 }
 
 func Test_cause(t *testing.T) {