about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2023-02-22 15:14:22 +0000
committerbors <bors@rust-lang.org>2023-02-22 15:14:22 +0000
commitb869e84e581612f4a30a4bca63bd9e90e9a17003 (patch)
tree7d7a88d7eb4d06a9ef979efa1e4ecc805ed355b1 /compiler
parent3b4d6e080404560f63599deeb328dfa27fe081a6 (diff)
parent26255186e2e94e0fe62cfd0965662494b6aab27c (diff)
downloadrust-b869e84e581612f4a30a4bca63bd9e90e9a17003.tar.gz
rust-b869e84e581612f4a30a4bca63bd9e90e9a17003.zip
Auto merge of #103042 - davidtwco:translation-distributed-ftl, r=oli-obk
errors: generate typed identifiers in each crate

Instead of loading the Fluent resources for every crate in `rustc_error_messages`, each crate generates typed identifiers for its own diagnostics and creates a static which are pulled together in the `rustc_driver` crate and provided to the diagnostic emitter.

There are advantages and disadvantages to this change..

#### Advantages
- Changing a diagnostic now only recompiles the crate for that diagnostic and those crates that depend on it, rather than `rustc_error_messages` and all crates thereafter.
- This approach can be used to support first-party crates that want to supply translatable diagnostics (e.g. `rust-lang/thorin` in https://github.com/rust-lang/rust/pull/102612#discussion_r985372582, cc `@JhonnyBillM)`
- We can extend this a little so that tools built using rustc internals (like clippy or rustdoc) can add their own diagnostic resources (much more easily than those resources needing to be available to `rustc_error_messages`)

#### Disadvantages
- Crates can only refer to the diagnostic messages defined in the current crate (or those from dependencies), rather than all diagnostic messages.
- `rustc_driver` (or some other crate we create for this purpose) has to directly depend on *everything* that has error messages.
  - It already transitively depended on all these crates.

#### Pending work
- [x] I don't know how to make `rustc_codegen_gcc`'s translated diagnostics work with this approach - because `rustc_driver` can't depend on that crate and so can't get its resources to provide to the diagnostic emission. I don't really know how the alternative codegen backends are actually wired up to the compiler at all.
- [x] Update `triagebot.toml` to track the moved FTL files.

r? `@compiler-errors`
cc #100717
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_ast_lowering/locales/en-US.ftl (renamed from compiler/rustc_error_messages/locales/en-US/ast_lowering.ftl)0
-rw-r--r--compiler/rustc_ast_lowering/src/errors.rs2
-rw-r--r--compiler/rustc_ast_lowering/src/lib.rs13
-rw-r--r--compiler/rustc_ast_passes/locales/en-US.ftl (renamed from compiler/rustc_error_messages/locales/en-US/ast_passes.ftl)0
-rw-r--r--compiler/rustc_ast_passes/src/ast_validation.rs7
-rw-r--r--compiler/rustc_ast_passes/src/errors.rs6
-rw-r--r--compiler/rustc_ast_passes/src/feature_gate.rs2
-rw-r--r--compiler/rustc_ast_passes/src/lib.rs5
-rw-r--r--compiler/rustc_attr/locales/en-US.ftl (renamed from compiler/rustc_error_messages/locales/en-US/attr.ftl)0
-rw-r--r--compiler/rustc_attr/src/lib.rs5
-rw-r--r--compiler/rustc_attr/src/session_diagnostics.rs19
-rw-r--r--compiler/rustc_borrowck/locales/en-US.ftl (renamed from compiler/rustc_error_messages/locales/en-US/borrowck.ftl)0
-rw-r--r--compiler/rustc_borrowck/src/lib.rs5
-rw-r--r--compiler/rustc_borrowck/src/session_diagnostics.rs4
-rw-r--r--compiler/rustc_builtin_macros/locales/en-US.ftl (renamed from compiler/rustc_error_messages/locales/en-US/builtin_macros.ftl)0
-rw-r--r--compiler/rustc_builtin_macros/src/lib.rs4
-rw-r--r--compiler/rustc_codegen_cranelift/src/lib.rs5
-rw-r--r--compiler/rustc_codegen_gcc/locales/en-US.ftl (renamed from compiler/rustc_error_messages/locales/en-US/codegen_gcc.ftl)0
-rw-r--r--compiler/rustc_codegen_gcc/src/lib.rs9
-rw-r--r--compiler/rustc_codegen_llvm/locales/en-US.ftl (renamed from compiler/rustc_error_messages/locales/en-US/codegen_llvm.ftl)0
-rw-r--r--compiler/rustc_codegen_llvm/src/errors.rs7
-rw-r--r--compiler/rustc_codegen_llvm/src/lib.rs9
-rw-r--r--compiler/rustc_codegen_ssa/locales/en-US.ftl (renamed from compiler/rustc_error_messages/locales/en-US/codegen_ssa.ftl)0
-rw-r--r--compiler/rustc_codegen_ssa/src/errors.rs5
-rw-r--r--compiler/rustc_codegen_ssa/src/lib.rs4
-rw-r--r--compiler/rustc_codegen_ssa/src/traits/backend.rs4
-rw-r--r--compiler/rustc_const_eval/locales/en-US.ftl (renamed from compiler/rustc_error_messages/locales/en-US/const_eval.ftl)0
-rw-r--r--compiler/rustc_const_eval/src/errors.rs18
-rw-r--r--compiler/rustc_const_eval/src/lib.rs4
-rw-r--r--compiler/rustc_driver_impl/Cargo.toml21
-rw-r--r--compiler/rustc_driver_impl/locales/en-US.ftl (renamed from compiler/rustc_error_messages/locales/en-US/driver.ftl)0
-rw-r--r--compiler/rustc_driver_impl/src/lib.rs46
-rw-r--r--compiler/rustc_error_messages/locales/en-US.ftl1
-rw-r--r--compiler/rustc_error_messages/locales/en-US/compiletest.ftl5
-rw-r--r--compiler/rustc_error_messages/locales/en-US/errors.ftl13
-rw-r--r--compiler/rustc_error_messages/src/lib.rs44
-rw-r--r--compiler/rustc_errors/locales/en-US.ftl19
-rw-r--r--compiler/rustc_errors/src/diagnostic_impls.rs5
-rw-r--r--compiler/rustc_errors/src/json/tests.rs2
-rw-r--r--compiler/rustc_errors/src/lib.rs6
-rw-r--r--compiler/rustc_errors/src/translation.rs13
-rw-r--r--compiler/rustc_expand/locales/en-US.ftl (renamed from compiler/rustc_error_messages/locales/en-US/expand.ftl)0
-rw-r--r--compiler/rustc_expand/src/errors.rs14
-rw-r--r--compiler/rustc_expand/src/lib.rs5
-rw-r--r--compiler/rustc_expand/src/parse/tests.rs5
-rw-r--r--compiler/rustc_expand/src/tests.rs16
-rw-r--r--compiler/rustc_hir_analysis/locales/en-US.ftl (renamed from compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl)54
-rw-r--r--compiler/rustc_hir_analysis/src/errors.rs33
-rw-r--r--compiler/rustc_hir_analysis/src/lib.rs4
-rw-r--r--compiler/rustc_hir_typeck/locales/en-US.ftl (renamed from compiler/rustc_error_messages/locales/en-US/hir_typeck.ftl)44
-rw-r--r--compiler/rustc_hir_typeck/src/errors.rs20
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs3
-rw-r--r--compiler/rustc_hir_typeck/src/lib.rs8
-rw-r--r--compiler/rustc_incremental/locales/en-US.ftl (renamed from compiler/rustc_error_messages/locales/en-US/incremental.ftl)0
-rw-r--r--compiler/rustc_incremental/src/lib.rs5
-rw-r--r--compiler/rustc_infer/locales/en-US.ftl (renamed from compiler/rustc_error_messages/locales/en-US/infer.ftl)0
-rw-r--r--compiler/rustc_infer/src/errors/mod.rs35
-rw-r--r--compiler/rustc_infer/src/errors/note_and_explain.rs5
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/note.rs3
-rw-r--r--compiler/rustc_infer/src/lib.rs5
-rw-r--r--compiler/rustc_interface/locales/en-US.ftl (renamed from compiler/rustc_error_messages/locales/en-US/interface.ftl)0
-rw-r--r--compiler/rustc_interface/src/interface.rs2
-rw-r--r--compiler/rustc_interface/src/lib.rs5
-rw-r--r--compiler/rustc_interface/src/tests.rs2
-rw-r--r--compiler/rustc_interface/src/util.rs5
-rw-r--r--compiler/rustc_lint/locales/en-US.ftl (renamed from compiler/rustc_error_messages/locales/en-US/lint.ftl)0
-rw-r--r--compiler/rustc_lint/src/array_into_iter.rs6
-rw-r--r--compiler/rustc_lint/src/builtin.rs3
-rw-r--r--compiler/rustc_lint/src/errors.rs6
-rw-r--r--compiler/rustc_lint/src/levels.rs17
-rw-r--r--compiler/rustc_lint/src/lib.rs4
-rw-r--r--compiler/rustc_lint/src/lints.rs120
-rw-r--r--compiler/rustc_lint/src/non_fmt_panic.rs18
-rw-r--r--compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs2
-rw-r--r--compiler/rustc_lint/src/types.rs15
-rw-r--r--compiler/rustc_macros/src/diagnostics/diagnostic.rs4
-rw-r--r--compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs6
-rw-r--r--compiler/rustc_macros/src/diagnostics/fluent.rs449
-rw-r--r--compiler/rustc_macros/src/diagnostics/subdiagnostic.rs4
-rw-r--r--compiler/rustc_macros/src/lib.rs8
-rw-r--r--compiler/rustc_metadata/locales/en-US.ftl (renamed from compiler/rustc_error_messages/locales/en-US/metadata.ftl)0
-rw-r--r--compiler/rustc_metadata/src/errors.rs23
-rw-r--r--compiler/rustc_metadata/src/lib.rs4
-rw-r--r--compiler/rustc_middle/locales/en-US.ftl (renamed from compiler/rustc_error_messages/locales/en-US/middle.ftl)0
-rw-r--r--compiler/rustc_middle/src/lib.rs5
-rw-r--r--compiler/rustc_middle/src/ty/diagnostics.rs8
-rw-r--r--compiler/rustc_middle/src/ty/layout.rs7
-rw-r--r--compiler/rustc_mir_build/locales/en-US.ftl (renamed from compiler/rustc_error_messages/locales/en-US/mir_build.ftl)0
-rw-r--r--compiler/rustc_mir_build/src/errors.rs39
-rw-r--r--compiler/rustc_mir_build/src/lib.rs5
-rw-r--r--compiler/rustc_mir_dataflow/locales/en-US.ftl (renamed from compiler/rustc_error_messages/locales/en-US/mir_dataflow.ftl)0
-rw-r--r--compiler/rustc_mir_dataflow/src/lib.rs4
-rw-r--r--compiler/rustc_monomorphize/locales/en-US.ftl (renamed from compiler/rustc_error_messages/locales/en-US/monomorphize.ftl)0
-rw-r--r--compiler/rustc_monomorphize/src/errors.rs3
-rw-r--r--compiler/rustc_monomorphize/src/lib.rs4
-rw-r--r--compiler/rustc_parse/locales/en-US.ftl (renamed from compiler/rustc_error_messages/locales/en-US/parse.ftl)28
-rw-r--r--compiler/rustc_parse/src/errors.rs188
-rw-r--r--compiler/rustc_parse/src/lib.rs4
-rw-r--r--compiler/rustc_parse/src/parser/attr.rs19
-rw-r--r--compiler/rustc_parse/src/parser/diagnostics.rs6
-rw-r--r--compiler/rustc_parse/src/parser/pat.rs5
-rw-r--r--compiler/rustc_passes/locales/en-US.ftl (renamed from compiler/rustc_error_messages/locales/en-US/passes.ftl)0
-rw-r--r--compiler/rustc_passes/src/check_attr.rs10
-rw-r--r--compiler/rustc_passes/src/errors.rs92
-rw-r--r--compiler/rustc_passes/src/lib.rs4
-rw-r--r--compiler/rustc_plugin_impl/locales/en-US.ftl (renamed from compiler/rustc_error_messages/locales/en-US/plugin_impl.ftl)0
-rw-r--r--compiler/rustc_plugin_impl/src/lib.rs4
-rw-r--r--compiler/rustc_privacy/locales/en-US.ftl (renamed from compiler/rustc_error_messages/locales/en-US/privacy.ftl)0
-rw-r--r--compiler/rustc_privacy/src/errors.rs4
-rw-r--r--compiler/rustc_privacy/src/lib.rs4
-rw-r--r--compiler/rustc_query_system/locales/en-US.ftl (renamed from compiler/rustc_error_messages/locales/en-US/query_system.ftl)0
-rw-r--r--compiler/rustc_query_system/src/lib.rs5
-rw-r--r--compiler/rustc_resolve/locales/en-US.ftl (renamed from compiler/rustc_error_messages/locales/en-US/resolve.ftl)0
-rw-r--r--compiler/rustc_resolve/src/errors.rs14
-rw-r--r--compiler/rustc_resolve/src/lib.rs7
-rw-r--r--compiler/rustc_session/locales/en-US.ftl (renamed from compiler/rustc_error_messages/locales/en-US/session.ftl)0
-rw-r--r--compiler/rustc_session/src/lib.rs5
-rw-r--r--compiler/rustc_session/src/parse.rs6
-rw-r--r--compiler/rustc_session/src/session.rs8
-rw-r--r--compiler/rustc_symbol_mangling/locales/en-US.ftl (renamed from compiler/rustc_error_messages/locales/en-US/symbol_mangling.ftl)0
-rw-r--r--compiler/rustc_symbol_mangling/src/lib.rs4
-rw-r--r--compiler/rustc_trait_selection/locales/en-US.ftl (renamed from compiler/rustc_error_messages/locales/en-US/trait_selection.ftl)0
-rw-r--r--compiler/rustc_trait_selection/src/errors.rs11
-rw-r--r--compiler/rustc_trait_selection/src/lib.rs5
-rw-r--r--compiler/rustc_ty_utils/locales/en-US.ftl (renamed from compiler/rustc_error_messages/locales/en-US/ty_utils.ftl)0
-rw-r--r--compiler/rustc_ty_utils/src/errors.rs2
-rw-r--r--compiler/rustc_ty_utils/src/lib.rs4
127 files changed, 1042 insertions, 746 deletions
diff --git a/compiler/rustc_error_messages/locales/en-US/ast_lowering.ftl b/compiler/rustc_ast_lowering/locales/en-US.ftl
index 03c88c6c0eb..03c88c6c0eb 100644
--- a/compiler/rustc_error_messages/locales/en-US/ast_lowering.ftl
+++ b/compiler/rustc_ast_lowering/locales/en-US.ftl
diff --git a/compiler/rustc_ast_lowering/src/errors.rs b/compiler/rustc_ast_lowering/src/errors.rs
index 21c6a2d26f4..8b302ac2142 100644
--- a/compiler/rustc_ast_lowering/src/errors.rs
+++ b/compiler/rustc_ast_lowering/src/errors.rs
@@ -339,7 +339,7 @@ pub struct InclusiveRangeWithNoEnd {
 #[derive(Diagnostic, Clone, Copy)]
 #[diag(ast_lowering_trait_fn_async, code = "E0706")]
 #[note]
-#[note(note2)]
+#[note(ast_lowering_note2)]
 pub struct TraitFnAsync {
     #[primary_span]
     pub fn_span: Span,
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index b1b9344d253..a726fbb72e9 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -52,15 +52,20 @@ use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::sorted_map::SortedMap;
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 use rustc_data_structures::sync::Lrc;
-use rustc_errors::{DiagnosticArgFromDisplay, Handler, StashKey};
+use rustc_errors::{
+    DiagnosticArgFromDisplay, DiagnosticMessage, Handler, StashKey, SubdiagnosticMessage,
+};
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, LifetimeRes, Namespace, PartialRes, PerNS, Res};
 use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID};
 use rustc_hir::definitions::DefPathData;
 use rustc_hir::{ConstArg, GenericArg, ItemLocalId, ParamName, TraitCandidate};
 use rustc_index::vec::{Idx, IndexVec};
-use rustc_middle::span_bug;
-use rustc_middle::ty::{ResolverAstLowering, TyCtxt};
+use rustc_macros::fluent_messages;
+use rustc_middle::{
+    span_bug,
+    ty::{ResolverAstLowering, TyCtxt},
+};
 use rustc_session::parse::feature_err;
 use rustc_span::hygiene::MacroKind;
 use rustc_span::source_map::DesugaringKind;
@@ -87,6 +92,8 @@ mod lifetime_collector;
 mod pat;
 mod path;
 
+fluent_messages! { "../locales/en-US.ftl" }
+
 struct LoweringContext<'a, 'hir> {
     tcx: TyCtxt<'hir>,
     resolver: &'a mut ResolverAstLowering,
diff --git a/compiler/rustc_error_messages/locales/en-US/ast_passes.ftl b/compiler/rustc_ast_passes/locales/en-US.ftl
index 5f28839f136..5f28839f136 100644
--- a/compiler/rustc_error_messages/locales/en-US/ast_passes.ftl
+++ b/compiler/rustc_ast_passes/locales/en-US.ftl
diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs
index afa29a510d2..56977e45a1a 100644
--- a/compiler/rustc_ast_passes/src/ast_validation.rs
+++ b/compiler/rustc_ast_passes/src/ast_validation.rs
@@ -13,7 +13,7 @@ use rustc_ast::walk_list;
 use rustc_ast::*;
 use rustc_ast_pretty::pprust::{self, State};
 use rustc_data_structures::fx::FxHashMap;
-use rustc_errors::{error_code, fluent, pluralize, struct_span_err, Applicability};
+use rustc_errors::{error_code, pluralize, struct_span_err, Applicability};
 use rustc_macros::Subdiagnostic;
 use rustc_parse::validate_attr;
 use rustc_session::lint::builtin::{
@@ -30,6 +30,7 @@ use std::ops::{Deref, DerefMut};
 use thin_vec::thin_vec;
 
 use crate::errors::*;
+use crate::fluent_generated as fluent;
 
 const MORE_EXTERN: &str =
     "for more information, visit https://doc.rust-lang.org/std/keyword.extern.html";
@@ -1723,12 +1724,12 @@ pub(crate) enum ForbiddenLetReason {
     /// `let` is not valid and the source environment is not important
     GenericForbidden,
     /// A let chain with the `||` operator
-    #[note(not_supported_or)]
+    #[note(ast_passes_not_supported_or)]
     NotSupportedOr(#[primary_span] Span),
     /// A let chain with invalid parentheses
     ///
     /// For example, `let 1 = 1 && (expr && expr)` is allowed
     /// but `(let 1 = 1 && (let 1 = 1 && (let 1 = 1))) && let a = 1` is not
-    #[note(not_supported_parentheses)]
+    #[note(ast_passes_not_supported_parentheses)]
     NotSupportedParentheses(#[primary_span] Span),
 }
diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs
index 09e262452b1..69ce8daa455 100644
--- a/compiler/rustc_ast_passes/src/errors.rs
+++ b/compiler/rustc_ast_passes/src/errors.rs
@@ -50,7 +50,7 @@ pub struct InvalidLabel {
 pub struct InvalidVisibility {
     #[primary_span]
     pub span: Span,
-    #[label(implied)]
+    #[label(ast_passes_implied)]
     pub implied: Option<Span>,
     #[subdiagnostic]
     pub note: Option<InvalidVisibilityNote>,
@@ -58,9 +58,9 @@ pub struct InvalidVisibility {
 
 #[derive(Subdiagnostic)]
 pub enum InvalidVisibilityNote {
-    #[note(individual_impl_items)]
+    #[note(ast_passes_individual_impl_items)]
     IndividualImplItems,
-    #[note(individual_foreign_items)]
+    #[note(ast_passes_individual_foreign_items)]
     IndividualForeignItems,
 }
 
diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs
index 6f7ba7a5ad7..d69c84bf4d1 100644
--- a/compiler/rustc_ast_passes/src/feature_gate.rs
+++ b/compiler/rustc_ast_passes/src/feature_gate.rs
@@ -157,7 +157,7 @@ impl<'a> PostExpansionVisitor<'a> {
                 &self.sess.parse_sess,
                 sym::non_lifetime_binders,
                 non_lt_param_spans,
-                rustc_errors::fluent::ast_passes_forbidden_non_lifetime_param,
+                crate::fluent_generated::ast_passes_forbidden_non_lifetime_param,
             )
             .emit();
         }
diff --git a/compiler/rustc_ast_passes/src/lib.rs b/compiler/rustc_ast_passes/src/lib.rs
index bbcbecd8131..1be959b0de6 100644
--- a/compiler/rustc_ast_passes/src/lib.rs
+++ b/compiler/rustc_ast_passes/src/lib.rs
@@ -11,8 +11,13 @@
 #![feature(let_chains)]
 #![recursion_limit = "256"]
 
+use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage};
+use rustc_macros::fluent_messages;
+
 pub mod ast_validation;
 mod errors;
 pub mod feature_gate;
 pub mod node_count;
 pub mod show_span;
+
+fluent_messages! { "../locales/en-US.ftl" }
diff --git a/compiler/rustc_error_messages/locales/en-US/attr.ftl b/compiler/rustc_attr/locales/en-US.ftl
index a7f8c993d42..a7f8c993d42 100644
--- a/compiler/rustc_error_messages/locales/en-US/attr.ftl
+++ b/compiler/rustc_attr/locales/en-US.ftl
diff --git a/compiler/rustc_attr/src/lib.rs b/compiler/rustc_attr/src/lib.rs
index 4580ffcc6d8..5fede0a58ac 100644
--- a/compiler/rustc_attr/src/lib.rs
+++ b/compiler/rustc_attr/src/lib.rs
@@ -11,6 +11,9 @@
 #[macro_use]
 extern crate rustc_macros;
 
+use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage};
+use rustc_macros::fluent_messages;
+
 mod builtin;
 mod session_diagnostics;
 
@@ -22,3 +25,5 @@ pub use StabilityLevel::*;
 pub use rustc_ast::attr::*;
 
 pub(crate) use rustc_ast::HashStableContext;
+
+fluent_messages! { "../locales/en-US.ftl" }
diff --git a/compiler/rustc_attr/src/session_diagnostics.rs b/compiler/rustc_attr/src/session_diagnostics.rs
index 3ba7a3c5336..ee79545e304 100644
--- a/compiler/rustc_attr/src/session_diagnostics.rs
+++ b/compiler/rustc_attr/src/session_diagnostics.rs
@@ -2,11 +2,12 @@ use std::num::IntErrorKind;
 
 use rustc_ast as ast;
 use rustc_errors::{
-    error_code, fluent, Applicability, DiagnosticBuilder, ErrorGuaranteed, Handler, IntoDiagnostic,
+    error_code, Applicability, DiagnosticBuilder, ErrorGuaranteed, Handler, IntoDiagnostic,
 };
 use rustc_macros::Diagnostic;
 use rustc_span::{Span, Symbol};
 
+use crate::fluent_generated as fluent;
 use crate::UnsupportedLiteralReason;
 
 #[derive(Diagnostic)]
@@ -59,7 +60,7 @@ impl<'a> IntoDiagnostic<'a> for UnknownMetaItem<'_> {
         );
         diag.set_arg("item", self.item);
         diag.set_arg("expected", expected.join(", "));
-        diag.span_label(self.span, fluent::label);
+        diag.span_label(self.span, fluent::attr_label);
         diag
     }
 }
@@ -99,31 +100,31 @@ pub(crate) struct InvalidIssueString {
 // translatable.
 #[derive(Subdiagnostic)]
 pub(crate) enum InvalidIssueStringCause {
-    #[label(must_not_be_zero)]
+    #[label(attr_must_not_be_zero)]
     MustNotBeZero {
         #[primary_span]
         span: Span,
     },
 
-    #[label(empty)]
+    #[label(attr_empty)]
     Empty {
         #[primary_span]
         span: Span,
     },
 
-    #[label(invalid_digit)]
+    #[label(attr_invalid_digit)]
     InvalidDigit {
         #[primary_span]
         span: Span,
     },
 
-    #[label(pos_overflow)]
+    #[label(attr_pos_overflow)]
     PosOverflow {
         #[primary_span]
         span: Span,
     },
 
-    #[label(neg_overflow)]
+    #[label(attr_neg_overflow)]
     NegOverflow {
         #[primary_span]
         span: Span,
@@ -275,7 +276,7 @@ pub(crate) struct IncorrectReprFormatGeneric<'a> {
 
 #[derive(Subdiagnostic)]
 pub(crate) enum IncorrectReprFormatGenericCause<'a> {
-    #[suggestion(suggestion, code = "{name}({int})", applicability = "machine-applicable")]
+    #[suggestion(attr_suggestion, code = "{name}({int})", applicability = "machine-applicable")]
     Int {
         #[primary_span]
         span: Span,
@@ -287,7 +288,7 @@ pub(crate) enum IncorrectReprFormatGenericCause<'a> {
         int: u128,
     },
 
-    #[suggestion(suggestion, code = "{name}({symbol})", applicability = "machine-applicable")]
+    #[suggestion(attr_suggestion, code = "{name}({symbol})", applicability = "machine-applicable")]
     Symbol {
         #[primary_span]
         span: Span,
diff --git a/compiler/rustc_error_messages/locales/en-US/borrowck.ftl b/compiler/rustc_borrowck/locales/en-US.ftl
index a3b6b5e8138..a3b6b5e8138 100644
--- a/compiler/rustc_error_messages/locales/en-US/borrowck.ftl
+++ b/compiler/rustc_borrowck/locales/en-US.ftl
diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs
index 622b57c7b7f..42949143431 100644
--- a/compiler/rustc_borrowck/src/lib.rs
+++ b/compiler/rustc_borrowck/src/lib.rs
@@ -20,7 +20,7 @@ extern crate tracing;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::graph::dominators::Dominators;
 use rustc_data_structures::vec_map::VecMap;
-use rustc_errors::{Diagnostic, DiagnosticBuilder};
+use rustc_errors::{Diagnostic, DiagnosticBuilder, DiagnosticMessage, SubdiagnosticMessage};
 use rustc_hir as hir;
 use rustc_hir::def_id::LocalDefId;
 use rustc_index::bit_set::ChunkedBitSet;
@@ -28,6 +28,7 @@ use rustc_index::vec::IndexVec;
 use rustc_infer::infer::{
     DefiningAnchor, InferCtxt, NllRegionVariableOrigin, RegionVariableOrigin, TyCtxtInferExt,
 };
+use rustc_macros::fluent_messages;
 use rustc_middle::mir::{
     traversal, Body, ClearCrossCrate, Local, Location, Mutability, NonDivergingIntrinsic, Operand,
     Place, PlaceElem, PlaceRef, VarDebugInfoContents,
@@ -99,6 +100,8 @@ use places_conflict::{places_conflict, PlaceConflictBias};
 use region_infer::RegionInferenceContext;
 use renumber::RegionCtxt;
 
+fluent_messages! { "../locales/en-US.ftl" }
+
 // FIXME(eddyb) perhaps move this somewhere more centrally.
 #[derive(Debug)]
 struct Upvar<'tcx> {
diff --git a/compiler/rustc_borrowck/src/session_diagnostics.rs b/compiler/rustc_borrowck/src/session_diagnostics.rs
index 13199d03852..a3678929099 100644
--- a/compiler/rustc_borrowck/src/session_diagnostics.rs
+++ b/compiler/rustc_borrowck/src/session_diagnostics.rs
@@ -55,7 +55,7 @@ pub(crate) struct VarNeedNotMut {
 #[derive(Diagnostic)]
 #[diag(borrowck_var_cannot_escape_closure)]
 #[note]
-#[note(cannot_escape)]
+#[note(borrowck_cannot_escape)]
 pub(crate) struct FnMutError {
     #[primary_span]
     pub span: Span,
@@ -223,7 +223,7 @@ pub(crate) struct MoveBorrow<'a> {
     pub borrow_place: &'a str,
     pub value_place: &'a str,
     #[primary_span]
-    #[label(move_label)]
+    #[label(borrowck_move_label)]
     pub span: Span,
     #[label]
     pub borrow_span: Span,
diff --git a/compiler/rustc_error_messages/locales/en-US/builtin_macros.ftl b/compiler/rustc_builtin_macros/locales/en-US.ftl
index 4d088e27b36..4d088e27b36 100644
--- a/compiler/rustc_error_messages/locales/en-US/builtin_macros.ftl
+++ b/compiler/rustc_builtin_macros/locales/en-US.ftl
diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs
index 75cfac72384..8afb6e56069 100644
--- a/compiler/rustc_builtin_macros/src/lib.rs
+++ b/compiler/rustc_builtin_macros/src/lib.rs
@@ -21,8 +21,10 @@ extern crate tracing;
 
 use crate::deriving::*;
 
+use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage};
 use rustc_expand::base::{MacroExpanderFn, ResolverExpand, SyntaxExtensionKind};
 use rustc_expand::proc_macro::BangProcMacro;
+use rustc_macros::fluent_messages;
 use rustc_span::symbol::sym;
 
 mod alloc_error_handler;
@@ -54,6 +56,8 @@ pub mod proc_macro_harness;
 pub mod standard_library_imports;
 pub mod test_harness;
 
+fluent_messages! { "../locales/en-US.ftl" }
+
 pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) {
     let mut register = |name, kind| resolver.register_builtin_macro(name, kind);
     macro register_bang($($name:ident: $f:expr,)*) {
diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs
index c7fe382bac4..5ba568bfd6e 100644
--- a/compiler/rustc_codegen_cranelift/src/lib.rs
+++ b/compiler/rustc_codegen_cranelift/src/lib.rs
@@ -172,6 +172,11 @@ pub struct CraneliftCodegenBackend {
 }
 
 impl CodegenBackend for CraneliftCodegenBackend {
+    fn locale_resource(&self) -> &'static str {
+        // FIXME(rust-lang/rust#100717) - cranelift codegen backend is not yet translated
+        ""
+    }
+
     fn init(&self, sess: &Session) {
         use rustc_session::config::Lto;
         match sess.lto() {
diff --git a/compiler/rustc_error_messages/locales/en-US/codegen_gcc.ftl b/compiler/rustc_codegen_gcc/locales/en-US.ftl
index 6101b28ab0c..6101b28ab0c 100644
--- a/compiler/rustc_error_messages/locales/en-US/codegen_gcc.ftl
+++ b/compiler/rustc_codegen_gcc/locales/en-US.ftl
diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs
index 5ab87feb98b..44538b41528 100644
--- a/compiler/rustc_codegen_gcc/src/lib.rs
+++ b/compiler/rustc_codegen_gcc/src/lib.rs
@@ -73,7 +73,8 @@ use rustc_codegen_ssa::back::lto::{LtoModuleCodegen, SerializedModule, ThinModul
 use rustc_codegen_ssa::target_features::supported_target_features;
 use rustc_codegen_ssa::traits::{CodegenBackend, ExtraBackendMethods, ModuleBufferMethods, ThinBufferMethods, WriteBackendMethods};
 use rustc_data_structures::fx::FxHashMap;
-use rustc_errors::{ErrorGuaranteed, Handler};
+use rustc_errors::{DiagnosticMessage, ErrorGuaranteed, Handler, SubdiagnosticMessage};
+use rustc_macros::fluent_messages;
 use rustc_metadata::EncodedMetadata;
 use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
 use rustc_middle::ty::TyCtxt;
@@ -84,6 +85,8 @@ use rustc_span::Symbol;
 use rustc_span::fatal_error::FatalError;
 use tempfile::TempDir;
 
+fluent_messages! { "../locales/en-US.ftl" }
+
 pub struct PrintOnPanic<F: Fn() -> String>(pub F);
 
 impl<F: Fn() -> String> Drop for PrintOnPanic<F> {
@@ -100,6 +103,10 @@ pub struct GccCodegenBackend {
 }
 
 impl CodegenBackend for GccCodegenBackend {
+    fn locale_resource(&self) -> &'static str {
+        crate::DEFAULT_LOCALE_RESOURCE
+    }
+
     fn init(&self, sess: &Session) {
         if sess.lto() != Lto::No {
             sess.emit_warning(LTONotSupported {});
diff --git a/compiler/rustc_error_messages/locales/en-US/codegen_llvm.ftl b/compiler/rustc_codegen_llvm/locales/en-US.ftl
index e5df417370b..e5df417370b 100644
--- a/compiler/rustc_error_messages/locales/en-US/codegen_llvm.ftl
+++ b/compiler/rustc_codegen_llvm/locales/en-US.ftl
diff --git a/compiler/rustc_codegen_llvm/src/errors.rs b/compiler/rustc_codegen_llvm/src/errors.rs
index 81072edc475..bae88d94293 100644
--- a/compiler/rustc_codegen_llvm/src/errors.rs
+++ b/compiler/rustc_codegen_llvm/src/errors.rs
@@ -2,9 +2,10 @@ use std::borrow::Cow;
 use std::ffi::CString;
 use std::path::Path;
 
+use crate::fluent_generated as fluent;
 use rustc_data_structures::small_c_str::SmallCStr;
 use rustc_errors::{
-    fluent, DiagnosticBuilder, EmissionGuarantee, ErrorGuaranteed, Handler, IntoDiagnostic,
+    DiagnosticBuilder, EmissionGuarantee, ErrorGuaranteed, Handler, IntoDiagnostic,
 };
 use rustc_macros::{Diagnostic, Subdiagnostic};
 use rustc_span::Span;
@@ -27,9 +28,9 @@ pub(crate) struct UnknownCTargetFeature<'a> {
 
 #[derive(Subdiagnostic)]
 pub(crate) enum PossibleFeature<'a> {
-    #[help(possible_feature)]
+    #[help(codegen_llvm_possible_feature)]
     Some { rust_feature: &'a str },
-    #[help(consider_filing_feature_request)]
+    #[help(codegen_llvm_consider_filing_feature_request)]
     None,
 }
 
diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs
index 111d14b265c..c41e74c51a0 100644
--- a/compiler/rustc_codegen_llvm/src/lib.rs
+++ b/compiler/rustc_codegen_llvm/src/lib.rs
@@ -34,7 +34,8 @@ use rustc_codegen_ssa::traits::*;
 use rustc_codegen_ssa::ModuleCodegen;
 use rustc_codegen_ssa::{CodegenResults, CompiledModule};
 use rustc_data_structures::fx::FxHashMap;
-use rustc_errors::{ErrorGuaranteed, FatalError, Handler};
+use rustc_errors::{DiagnosticMessage, ErrorGuaranteed, FatalError, Handler, SubdiagnosticMessage};
+use rustc_macros::fluent_messages;
 use rustc_metadata::EncodedMetadata;
 use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
 use rustc_middle::ty::query::Providers;
@@ -83,6 +84,8 @@ mod type_of;
 mod va_arg;
 mod value;
 
+fluent_messages! { "../locales/en-US.ftl" }
+
 #[derive(Clone)]
 pub struct LlvmCodegenBackend(());
 
@@ -246,6 +249,10 @@ impl LlvmCodegenBackend {
 }
 
 impl CodegenBackend for LlvmCodegenBackend {
+    fn locale_resource(&self) -> &'static str {
+        crate::DEFAULT_LOCALE_RESOURCE
+    }
+
     fn init(&self, sess: &Session) {
         llvm_util::init(sess); // Make sure llvm is inited
     }
diff --git a/compiler/rustc_error_messages/locales/en-US/codegen_ssa.ftl b/compiler/rustc_codegen_ssa/locales/en-US.ftl
index 8fe5f8d50ab..8fe5f8d50ab 100644
--- a/compiler/rustc_error_messages/locales/en-US/codegen_ssa.ftl
+++ b/compiler/rustc_codegen_ssa/locales/en-US.ftl
diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs
index d81252653df..6dea7496fc3 100644
--- a/compiler/rustc_codegen_ssa/src/errors.rs
+++ b/compiler/rustc_codegen_ssa/src/errors.rs
@@ -1,8 +1,9 @@
 //! Errors emitted by codegen_ssa
 
 use crate::back::command::Command;
+use crate::fluent_generated as fluent;
 use rustc_errors::{
-    fluent, DiagnosticArgValue, DiagnosticBuilder, ErrorGuaranteed, Handler, IntoDiagnostic,
+    DiagnosticArgValue, DiagnosticBuilder, ErrorGuaranteed, Handler, IntoDiagnostic,
     IntoDiagnosticArg,
 };
 use rustc_macros::Diagnostic;
@@ -388,7 +389,7 @@ pub struct LinkerNotFound {
 #[derive(Diagnostic)]
 #[diag(codegen_ssa_unable_to_exe_linker)]
 #[note]
-#[note(command_note)]
+#[note(codegen_ssa_command_note)]
 pub struct UnableToExeLinker {
     pub linker_path: PathBuf,
     pub error: Error,
diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs
index 7d51cee307e..ebe9e50ffe6 100644
--- a/compiler/rustc_codegen_ssa/src/lib.rs
+++ b/compiler/rustc_codegen_ssa/src/lib.rs
@@ -25,7 +25,9 @@ extern crate rustc_middle;
 use rustc_ast as ast;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::sync::Lrc;
+use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage};
 use rustc_hir::def_id::CrateNum;
+use rustc_macros::fluent_messages;
 use rustc_middle::dep_graph::WorkProduct;
 use rustc_middle::middle::dependency_format::Dependencies;
 use rustc_middle::middle::exported_symbols::SymbolExportKind;
@@ -54,6 +56,8 @@ pub mod mono_item;
 pub mod target_features;
 pub mod traits;
 
+fluent_messages! { "../locales/en-US.ftl" }
+
 pub struct ModuleCodegen<M> {
     /// The name of the module. When the crate may be saved between
     /// compilations, incremental compilation requires that name be
diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs
index 5c35070ea66..64bebe50ddb 100644
--- a/compiler/rustc_codegen_ssa/src/traits/backend.rs
+++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs
@@ -57,6 +57,10 @@ impl<'tcx, T> Backend<'tcx> for T where
 }
 
 pub trait CodegenBackend {
+    /// Locale resources for diagnostic messages - a string the content of the Fluent resource.
+    /// Called before `init` so that all other functions are able to emit translatable diagnostics.
+    fn locale_resource(&self) -> &'static str;
+
     fn init(&self, _sess: &Session) {}
     fn print(&self, _req: PrintRequest, _sess: &Session) {}
     fn target_features(&self, _sess: &Session, _allow_unstable: bool) -> Vec<Symbol> {
diff --git a/compiler/rustc_error_messages/locales/en-US/const_eval.ftl b/compiler/rustc_const_eval/locales/en-US.ftl
index 33bb116d6fa..33bb116d6fa 100644
--- a/compiler/rustc_error_messages/locales/en-US/const_eval.ftl
+++ b/compiler/rustc_const_eval/locales/en-US.ftl
diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs
index 4b055076742..f8b7cc6d7e1 100644
--- a/compiler/rustc_const_eval/src/errors.rs
+++ b/compiler/rustc_const_eval/src/errors.rs
@@ -9,12 +9,12 @@ pub(crate) struct UnstableInStable {
     #[primary_span]
     pub span: Span,
     #[suggestion(
-        unstable_sugg,
+        const_eval_unstable_sugg,
         code = "#[rustc_const_unstable(feature = \"...\", issue = \"...\")]\n",
         applicability = "has-placeholders"
     )]
     #[suggestion(
-        bypass_sugg,
+        const_eval_bypass_sugg,
         code = "#[rustc_allow_const_fn_unstable({gate})]\n",
         applicability = "has-placeholders"
     )]
@@ -35,15 +35,15 @@ pub(crate) struct StaticAccessErr {
     #[primary_span]
     pub span: Span,
     pub kind: ConstContext,
-    #[note(teach_note)]
-    #[help(teach_help)]
+    #[note(const_eval_teach_note)]
+    #[help(const_eval_teach_help)]
     pub teach: Option<()>,
 }
 
 #[derive(Diagnostic)]
 #[diag(const_eval_raw_ptr_to_int)]
 #[note]
-#[note(note2)]
+#[note(const_eval_note2)]
 pub(crate) struct RawPtrToIntErr {
     #[primary_span]
     pub span: Span,
@@ -118,7 +118,7 @@ pub(crate) struct UnallowedMutableRefs {
     #[primary_span]
     pub span: Span,
     pub kind: ConstContext,
-    #[note(teach_note)]
+    #[note(const_eval_teach_note)]
     pub teach: Option<()>,
 }
 
@@ -128,7 +128,7 @@ pub(crate) struct UnallowedMutableRefsRaw {
     #[primary_span]
     pub span: Span,
     pub kind: ConstContext,
-    #[note(teach_note)]
+    #[note(const_eval_teach_note)]
     pub teach: Option<()>,
 }
 #[derive(Diagnostic)]
@@ -163,7 +163,7 @@ pub(crate) struct UnallowedHeapAllocations {
     #[label]
     pub span: Span,
     pub kind: ConstContext,
-    #[note(teach_note)]
+    #[note(const_eval_teach_note)]
     pub teach: Option<()>,
 }
 
@@ -184,7 +184,7 @@ pub(crate) struct InteriorMutableDataRefer {
     #[help]
     pub opt_help: Option<()>,
     pub kind: ConstContext,
-    #[note(teach_note)]
+    #[note(const_eval_teach_note)]
     pub teach: Option<()>,
 }
 
diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs
index 964efcc9062..fc6d61c79c2 100644
--- a/compiler/rustc_const_eval/src/lib.rs
+++ b/compiler/rustc_const_eval/src/lib.rs
@@ -34,10 +34,14 @@ pub mod interpret;
 pub mod transform;
 pub mod util;
 
+use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage};
+use rustc_macros::fluent_messages;
 use rustc_middle::ty;
 use rustc_middle::ty::query::Providers;
 use rustc_target::abi::InitKind;
 
+fluent_messages! { "../locales/en-US.ftl" }
+
 pub fn provide(providers: &mut Providers) {
     const_eval::provide(providers);
     providers.eval_to_const_value_raw = const_eval::eval_to_const_value_raw_provider;
diff --git a/compiler/rustc_driver_impl/Cargo.toml b/compiler/rustc_driver_impl/Cargo.toml
index cdec4f91277..7b59a52cffe 100644
--- a/compiler/rustc_driver_impl/Cargo.toml
+++ b/compiler/rustc_driver_impl/Cargo.toml
@@ -9,6 +9,27 @@ edition = "2021"
 tracing = { version = "0.1.35" }
 serde_json = "1.0.59"
 rustc_log = { path = "../rustc_log" }
+rustc_ast_lowering = { path = "../rustc_ast_lowering" }
+rustc_ast_passes = { path = "../rustc_ast_passes" }
+rustc_attr = { path = "../rustc_attr" }
+rustc_borrowck = { path = "../rustc_borrowck" }
+rustc_builtin_macros = { path = "../rustc_builtin_macros" }
+rustc_const_eval = { path = "../rustc_const_eval" }
+rustc_error_messages = { path = "../rustc_error_messages" }
+rustc_expand = { path = "../rustc_expand" }
+rustc_hir_typeck = { path = "../rustc_hir_typeck" }
+rustc_incremental = { path = "../rustc_incremental" }
+rustc_infer = { path = "../rustc_infer" }
+rustc_mir_build = { path = "../rustc_mir_build" }
+rustc_mir_dataflow = { path = "../rustc_mir_dataflow" }
+rustc_monomorphize = { path = "../rustc_monomorphize" }
+rustc_passes = { path = "../rustc_passes" }
+rustc_privacy = { path = "../rustc_privacy" }
+rustc_query_system = { path = "../rustc_query_system" }
+rustc_resolve = { path = "../rustc_resolve" }
+rustc_symbol_mangling = { path = "../rustc_symbol_mangling" }
+rustc_trait_selection = { path = "../rustc_trait_selection" }
+rustc_ty_utils = { path = "../rustc_ty_utils" }
 rustc_middle = { path = "../rustc_middle" }
 rustc_ast_pretty = { path = "../rustc_ast_pretty" }
 rustc_target = { path = "../rustc_target" }
diff --git a/compiler/rustc_error_messages/locales/en-US/driver.ftl b/compiler/rustc_driver_impl/locales/en-US.ftl
index f19b1ff6426..f19b1ff6426 100644
--- a/compiler/rustc_error_messages/locales/en-US/driver.ftl
+++ b/compiler/rustc_driver_impl/locales/en-US.ftl
diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs
index d7e9e00f3b6..54bcb154da2 100644
--- a/compiler/rustc_driver_impl/src/lib.rs
+++ b/compiler/rustc_driver_impl/src/lib.rs
@@ -23,11 +23,14 @@ use rustc_codegen_ssa::{traits::CodegenBackend, CodegenErrors, CodegenResults};
 use rustc_data_structures::profiling::{get_resident_set_size, print_time_passes_entry};
 use rustc_data_structures::sync::SeqCst;
 use rustc_errors::registry::{InvalidErrorCode, Registry};
-use rustc_errors::{ErrorGuaranteed, PResult, TerminalUrl};
+use rustc_errors::{
+    DiagnosticMessage, ErrorGuaranteed, PResult, SubdiagnosticMessage, TerminalUrl,
+};
 use rustc_feature::find_gated_cfg;
 use rustc_interface::util::{self, collect_crate_types, get_codegen_backend};
 use rustc_interface::{interface, Queries};
 use rustc_lint::LintStore;
+use rustc_macros::fluent_messages;
 use rustc_metadata::locator;
 use rustc_session::config::{nightly_options, CG_OPTIONS, Z_OPTIONS};
 use rustc_session::config::{ErrorOutputType, Input, OutputType, PrintRequest, TrimmedDefPaths};
@@ -61,6 +64,44 @@ use crate::session_diagnostics::{
     RLinkWrongFileType, RlinkNotAFile, RlinkUnableToRead,
 };
 
+fluent_messages! { "../locales/en-US.ftl" }
+
+pub static DEFAULT_LOCALE_RESOURCES: &[&str] = &[
+    // tidy-alphabetical-start
+    crate::DEFAULT_LOCALE_RESOURCE,
+    rustc_ast_lowering::DEFAULT_LOCALE_RESOURCE,
+    rustc_ast_passes::DEFAULT_LOCALE_RESOURCE,
+    rustc_attr::DEFAULT_LOCALE_RESOURCE,
+    rustc_borrowck::DEFAULT_LOCALE_RESOURCE,
+    rustc_builtin_macros::DEFAULT_LOCALE_RESOURCE,
+    rustc_codegen_ssa::DEFAULT_LOCALE_RESOURCE,
+    rustc_const_eval::DEFAULT_LOCALE_RESOURCE,
+    rustc_error_messages::DEFAULT_LOCALE_RESOURCE,
+    rustc_expand::DEFAULT_LOCALE_RESOURCE,
+    rustc_hir_analysis::DEFAULT_LOCALE_RESOURCE,
+    rustc_hir_typeck::DEFAULT_LOCALE_RESOURCE,
+    rustc_incremental::DEFAULT_LOCALE_RESOURCE,
+    rustc_infer::DEFAULT_LOCALE_RESOURCE,
+    rustc_interface::DEFAULT_LOCALE_RESOURCE,
+    rustc_lint::DEFAULT_LOCALE_RESOURCE,
+    rustc_metadata::DEFAULT_LOCALE_RESOURCE,
+    rustc_middle::DEFAULT_LOCALE_RESOURCE,
+    rustc_mir_build::DEFAULT_LOCALE_RESOURCE,
+    rustc_mir_dataflow::DEFAULT_LOCALE_RESOURCE,
+    rustc_monomorphize::DEFAULT_LOCALE_RESOURCE,
+    rustc_parse::DEFAULT_LOCALE_RESOURCE,
+    rustc_passes::DEFAULT_LOCALE_RESOURCE,
+    rustc_plugin_impl::DEFAULT_LOCALE_RESOURCE,
+    rustc_privacy::DEFAULT_LOCALE_RESOURCE,
+    rustc_query_system::DEFAULT_LOCALE_RESOURCE,
+    rustc_resolve::DEFAULT_LOCALE_RESOURCE,
+    rustc_session::DEFAULT_LOCALE_RESOURCE,
+    rustc_symbol_mangling::DEFAULT_LOCALE_RESOURCE,
+    rustc_trait_selection::DEFAULT_LOCALE_RESOURCE,
+    rustc_ty_utils::DEFAULT_LOCALE_RESOURCE,
+    // tidy-alphabetical-end
+];
+
 /// Exit status code used for successful compilation and help output.
 pub const EXIT_SUCCESS: i32 = 0;
 
@@ -218,6 +259,7 @@ fn run_compiler(
         output_file: ofile,
         output_dir: odir,
         file_loader,
+        locale_resources: DEFAULT_LOCALE_RESOURCES,
         lint_caps: Default::default(),
         parse_sess_created: None,
         register_lints: None,
@@ -1162,7 +1204,7 @@ static DEFAULT_HOOK: LazyLock<Box<dyn Fn(&panic::PanicInfo<'_>) + Sync + Send +
 /// hook.
 pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) {
     let fallback_bundle =
-        rustc_errors::fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false);
+        rustc_errors::fallback_fluent_bundle(crate::DEFAULT_LOCALE_RESOURCES.to_vec(), false);
     let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr(
         rustc_errors::ColorConfig::Auto,
         None,
diff --git a/compiler/rustc_error_messages/locales/en-US.ftl b/compiler/rustc_error_messages/locales/en-US.ftl
new file mode 100644
index 00000000000..e6292374448
--- /dev/null
+++ b/compiler/rustc_error_messages/locales/en-US.ftl
@@ -0,0 +1 @@
+# satisfy tidy lint by having a line in this file
diff --git a/compiler/rustc_error_messages/locales/en-US/compiletest.ftl b/compiler/rustc_error_messages/locales/en-US/compiletest.ftl
deleted file mode 100644
index 55061fbce7e..00000000000
--- a/compiler/rustc_error_messages/locales/en-US/compiletest.ftl
+++ /dev/null
@@ -1,5 +0,0 @@
-compiletest_example = this is an example message used in testing
-    .note = with a note
-    .help = with a help
-    .suggestion = with a suggestion
-    .label = with a label
diff --git a/compiler/rustc_error_messages/locales/en-US/errors.ftl b/compiler/rustc_error_messages/locales/en-US/errors.ftl
deleted file mode 100644
index 429bdd2777f..00000000000
--- a/compiler/rustc_error_messages/locales/en-US/errors.ftl
+++ /dev/null
@@ -1,13 +0,0 @@
-errors_target_invalid_address_space = invalid address space `{$addr_space}` for `{$cause}` in "data-layout": {$err}
-
-errors_target_invalid_bits = invalid {$kind} `{$bit}` for `{$cause}` in "data-layout": {$err}
-
-errors_target_missing_alignment = missing alignment for `{$cause}` in "data-layout"
-
-errors_target_invalid_alignment = invalid alignment for `{$cause}` in "data-layout": {$err}
-
-errors_target_inconsistent_architecture = inconsistent target specification: "data-layout" claims architecture is {$dl}-endian, while "target-endian" is `{$target}`
-
-errors_target_inconsistent_pointer_width = inconsistent target specification: "data-layout" claims pointers are {$pointer_size}-bit, while "target-pointer-width" is `{$target}`
-
-errors_target_invalid_bits_size = {$err}
diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs
index 579466ed366..40ed10e7165 100644
--- a/compiler/rustc_error_messages/src/lib.rs
+++ b/compiler/rustc_error_messages/src/lib.rs
@@ -34,47 +34,7 @@ use intl_memoizer::IntlLangMemoizer;
 pub use fluent_bundle::{self, types::FluentType, FluentArgs, FluentError, FluentValue};
 pub use unic_langid::{langid, LanguageIdentifier};
 
-// Generates `DEFAULT_LOCALE_RESOURCES` static and `fluent_generated` module.
-fluent_messages! {
-    // tidy-alphabetical-start
-    ast_lowering => "../locales/en-US/ast_lowering.ftl",
-    ast_passes => "../locales/en-US/ast_passes.ftl",
-    attr => "../locales/en-US/attr.ftl",
-    borrowck => "../locales/en-US/borrowck.ftl",
-    builtin_macros => "../locales/en-US/builtin_macros.ftl",
-    codegen_gcc => "../locales/en-US/codegen_gcc.ftl",
-    codegen_llvm => "../locales/en-US/codegen_llvm.ftl",
-    codegen_ssa => "../locales/en-US/codegen_ssa.ftl",
-    compiletest => "../locales/en-US/compiletest.ftl",
-    const_eval => "../locales/en-US/const_eval.ftl",
-    driver => "../locales/en-US/driver.ftl",
-    errors => "../locales/en-US/errors.ftl",
-    expand => "../locales/en-US/expand.ftl",
-    hir_analysis => "../locales/en-US/hir_analysis.ftl",
-    hir_typeck => "../locales/en-US/hir_typeck.ftl",
-    incremental => "../locales/en-US/incremental.ftl",
-    infer => "../locales/en-US/infer.ftl",
-    interface => "../locales/en-US/interface.ftl",
-    lint => "../locales/en-US/lint.ftl",
-    metadata => "../locales/en-US/metadata.ftl",
-    middle => "../locales/en-US/middle.ftl",
-    mir_build => "../locales/en-US/mir_build.ftl",
-    mir_dataflow => "../locales/en-US/mir_dataflow.ftl",
-    monomorphize => "../locales/en-US/monomorphize.ftl",
-    parse => "../locales/en-US/parse.ftl",
-    passes => "../locales/en-US/passes.ftl",
-    plugin_impl => "../locales/en-US/plugin_impl.ftl",
-    privacy => "../locales/en-US/privacy.ftl",
-    query_system => "../locales/en-US/query_system.ftl",
-    resolve => "../locales/en-US/resolve.ftl",
-    session => "../locales/en-US/session.ftl",
-    symbol_mangling => "../locales/en-US/symbol_mangling.ftl",
-    trait_selection => "../locales/en-US/trait_selection.ftl",
-    ty_utils => "../locales/en-US/ty_utils.ftl",
-    // tidy-alphabetical-end
-}
-
-pub use fluent_generated::{self as fluent, DEFAULT_LOCALE_RESOURCES};
+fluent_messages! { "../locales/en-US.ftl" }
 
 pub type FluentBundle = fluent_bundle::bundle::FluentBundle<FluentResource, IntlLangMemoizer>;
 
@@ -263,7 +223,7 @@ pub type LazyFallbackBundle = Lrc<Lazy<FluentBundle, impl FnOnce() -> FluentBund
 /// Return the default `FluentBundle` with standard "en-US" diagnostic messages.
 #[instrument(level = "trace")]
 pub fn fallback_fluent_bundle(
-    resources: &'static [&'static str],
+    resources: Vec<&'static str>,
     with_directionality_markers: bool,
 ) -> LazyFallbackBundle {
     Lrc::new(Lazy::new(move || {
diff --git a/compiler/rustc_errors/locales/en-US.ftl b/compiler/rustc_errors/locales/en-US.ftl
new file mode 100644
index 00000000000..dde1d6c0a81
--- /dev/null
+++ b/compiler/rustc_errors/locales/en-US.ftl
@@ -0,0 +1,19 @@
+errors_target_invalid_address_space =
+    invalid address space `{$addr_space}` for `{$cause}` in "data-layout": {$err}
+
+errors_target_invalid_bits =
+    invalid {$kind} `{$bit}` for `{$cause}` in "data-layout": {$err}
+
+errors_target_missing_alignment =
+    missing alignment for `{$cause}` in "data-layout"
+
+errors_target_invalid_alignment =
+    invalid alignment for `{$cause}` in "data-layout": {$err}
+
+errors_target_inconsistent_architecture =
+    inconsistent target specification: "data-layout" claims architecture is {$dl}-endian, while "target-endian" is `{$target}`
+
+errors_target_inconsistent_pointer_width =
+    inconsistent target specification: "data-layout" claims pointers are {$pointer_size}-bit, while "target-pointer-width" is `{$target}`
+
+errors_target_invalid_bits_size = {$err}
diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs
index 5ada85d04b0..d4ddd0c53bf 100644
--- a/compiler/rustc_errors/src/diagnostic_impls.rs
+++ b/compiler/rustc_errors/src/diagnostic_impls.rs
@@ -1,6 +1,5 @@
-use crate::{
-    fluent, DiagnosticArgValue, DiagnosticBuilder, Handler, IntoDiagnostic, IntoDiagnosticArg,
-};
+use crate::fluent_generated as fluent;
+use crate::{DiagnosticArgValue, DiagnosticBuilder, Handler, IntoDiagnostic, IntoDiagnosticArg};
 use rustc_ast as ast;
 use rustc_ast_pretty::pprust;
 use rustc_hir as hir;
diff --git a/compiler/rustc_errors/src/json/tests.rs b/compiler/rustc_errors/src/json/tests.rs
index f161532d3b7..671dc449eaa 100644
--- a/compiler/rustc_errors/src/json/tests.rs
+++ b/compiler/rustc_errors/src/json/tests.rs
@@ -46,7 +46,7 @@ fn test_positions(code: &str, span: (u32, u32), expected_output: SpanTestData) {
         let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
         sm.new_source_file(Path::new("test.rs").to_owned().into(), code.to_owned());
         let fallback_bundle =
-            crate::fallback_fluent_bundle(rustc_error_messages::DEFAULT_LOCALE_RESOURCES, false);
+            crate::fallback_fluent_bundle(vec![crate::DEFAULT_LOCALE_RESOURCE], false);
 
         let output = Arc::new(Mutex::new(Vec::new()));
         let je = JsonEmitter::new(
diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs
index 8c39feca88a..edec8cce92f 100644
--- a/compiler/rustc_errors/src/lib.rs
+++ b/compiler/rustc_errors/src/lib.rs
@@ -36,11 +36,11 @@ use rustc_data_structures::stable_hasher::StableHasher;
 use rustc_data_structures::sync::{self, Lock, Lrc};
 use rustc_data_structures::AtomicRef;
 pub use rustc_error_messages::{
-    fallback_fluent_bundle, fluent, fluent_bundle, DelayDm, DiagnosticMessage, FluentBundle,
+    fallback_fluent_bundle, fluent_bundle, DelayDm, DiagnosticMessage, FluentBundle,
     LanguageIdentifier, LazyFallbackBundle, MultiSpan, SpanLabel, SubdiagnosticMessage,
-    DEFAULT_LOCALE_RESOURCES,
 };
 pub use rustc_lint_defs::{pluralize, Applicability};
+use rustc_macros::fluent_messages;
 use rustc_span::source_map::SourceMap;
 use rustc_span::HashStableContext;
 use rustc_span::{Loc, Span};
@@ -76,6 +76,8 @@ pub use snippet::Style;
 pub type PErr<'a> = DiagnosticBuilder<'a, ErrorGuaranteed>;
 pub type PResult<'a, T> = Result<T, PErr<'a>>;
 
+fluent_messages! { "../locales/en-US.ftl" }
+
 // `PResult` is used a lot. Make sure it doesn't unintentionally get bigger.
 // (See also the comment on `DiagnosticBuilderInner`'s `diagnostic` field.)
 #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
diff --git a/compiler/rustc_errors/src/translation.rs b/compiler/rustc_errors/src/translation.rs
index addfc9726ca..1ac9b8e03c7 100644
--- a/compiler/rustc_errors/src/translation.rs
+++ b/compiler/rustc_errors/src/translation.rs
@@ -4,6 +4,7 @@ use crate::{DiagnosticArg, DiagnosticMessage, FluentBundle};
 use rustc_data_structures::sync::Lrc;
 use rustc_error_messages::FluentArgs;
 use std::borrow::Cow;
+use std::env;
 use std::error::Report;
 
 /// Convert diagnostic arguments (a rustc internal type that exists to implement
@@ -94,8 +95,16 @@ pub trait Translate {
                 // The primary bundle was present and translation succeeded
                 Some(Ok(t)) => t,
 
-                // Always yeet out for errors on debug
-                Some(Err(primary)) if cfg!(debug_assertions) => do yeet primary,
+                // Always yeet out for errors on debug (unless
+                // `RUSTC_TRANSLATION_NO_DEBUG_ASSERT` is set in the environment - this allows
+                // local runs of the test suites, of builds with debug assertions, to test the
+                // behaviour in a normal build).
+                Some(Err(primary))
+                    if cfg!(debug_assertions)
+                        && env::var("RUSTC_TRANSLATION_NO_DEBUG_ASSERT").is_err() =>
+                {
+                    do yeet primary
+                }
 
                 // If `translate_with_bundle` returns `Err` with the primary bundle, this is likely
                 // just that the primary bundle doesn't contain the message being translated or
diff --git a/compiler/rustc_error_messages/locales/en-US/expand.ftl b/compiler/rustc_expand/locales/en-US.ftl
index dbd80954382..dbd80954382 100644
--- a/compiler/rustc_error_messages/locales/en-US/expand.ftl
+++ b/compiler/rustc_expand/locales/en-US.ftl
diff --git a/compiler/rustc_expand/src/errors.rs b/compiler/rustc_expand/src/errors.rs
index 9b9697ab13d..d9b2b5f4802 100644
--- a/compiler/rustc_expand/src/errors.rs
+++ b/compiler/rustc_expand/src/errors.rs
@@ -65,7 +65,7 @@ pub(crate) struct MacroConstStability {
     #[primary_span]
     #[label]
     pub span: Span,
-    #[label(label2)]
+    #[label(expand_label2)]
     pub head_span: Span,
 }
 
@@ -75,7 +75,7 @@ pub(crate) struct MacroBodyStability {
     #[primary_span]
     #[label]
     pub span: Span,
-    #[label(label2)]
+    #[label(expand_label2)]
     pub head_span: Span,
 }
 
@@ -188,7 +188,7 @@ pub(crate) struct FeatureRemoved<'a> {
 }
 
 #[derive(Subdiagnostic)]
-#[note(reason)]
+#[note(expand_reason)]
 pub(crate) struct FeatureRemovedReason<'a> {
     pub reason: &'a str,
 }
@@ -223,12 +223,12 @@ pub(crate) struct MalformedFeatureAttribute {
 
 #[derive(Subdiagnostic)]
 pub(crate) enum MalformedFeatureAttributeHelp {
-    #[label(expected)]
+    #[label(expand_expected)]
     Label {
         #[primary_span]
         span: Span,
     },
-    #[suggestion(expected, code = "{suggestion}", applicability = "maybe-incorrect")]
+    #[suggestion(expand_expected, code = "{suggestion}", applicability = "maybe-incorrect")]
     Suggestion {
         #[primary_span]
         span: Span,
@@ -306,7 +306,7 @@ pub(crate) struct IncompleteParse<'a> {
     pub kind_name: &'a str,
 
     #[suggestion(
-        suggestion_add_semi,
+        expand_suggestion_add_semi,
         style = "verbose",
         code = ";",
         applicability = "maybe-incorrect"
@@ -340,7 +340,7 @@ pub(crate) struct ModuleInBlock {
 }
 
 #[derive(Subdiagnostic)]
-#[note(note)]
+#[note(expand_note)]
 pub(crate) struct ModuleInBlockName {
     #[primary_span]
     pub span: Span,
diff --git a/compiler/rustc_expand/src/lib.rs b/compiler/rustc_expand/src/lib.rs
index 89726856635..634e206e58a 100644
--- a/compiler/rustc_expand/src/lib.rs
+++ b/compiler/rustc_expand/src/lib.rs
@@ -20,6 +20,9 @@ extern crate tracing;
 
 extern crate proc_macro as pm;
 
+use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage};
+use rustc_macros::fluent_messages;
+
 mod placeholders;
 mod proc_macro_server;
 
@@ -60,3 +63,5 @@ mod tokenstream {
 mod mut_visit {
     mod tests;
 }
+
+fluent_messages! { "../locales/en-US.ftl" }
diff --git a/compiler/rustc_expand/src/parse/tests.rs b/compiler/rustc_expand/src/parse/tests.rs
index 0726d922c84..8b37728b60f 100644
--- a/compiler/rustc_expand/src/parse/tests.rs
+++ b/compiler/rustc_expand/src/parse/tests.rs
@@ -18,7 +18,10 @@ use rustc_span::{BytePos, FileName, Pos, Span};
 use std::path::PathBuf;
 
 fn sess() -> ParseSess {
-    ParseSess::new(FilePathMapping::empty())
+    ParseSess::new(
+        vec![crate::DEFAULT_LOCALE_RESOURCE, rustc_parse::DEFAULT_LOCALE_RESOURCE],
+        FilePathMapping::empty(),
+    )
 }
 
 /// Parses an item.
diff --git a/compiler/rustc_expand/src/tests.rs b/compiler/rustc_expand/src/tests.rs
index f80141403bf..14918d3c190 100644
--- a/compiler/rustc_expand/src/tests.rs
+++ b/compiler/rustc_expand/src/tests.rs
@@ -34,7 +34,10 @@ where
 
 /// Maps a string to tts, using a made-up filename.
 pub(crate) fn string_to_stream(source_str: String) -> TokenStream {
-    let ps = ParseSess::new(FilePathMapping::empty());
+    let ps = ParseSess::new(
+        vec![crate::DEFAULT_LOCALE_RESOURCE, rustc_parse::DEFAULT_LOCALE_RESOURCE],
+        FilePathMapping::empty(),
+    );
     source_file_to_stream(
         &ps,
         ps.source_map().new_source_file(PathBuf::from("bogofile").into(), source_str),
@@ -45,7 +48,10 @@ pub(crate) fn string_to_stream(source_str: String) -> TokenStream {
 
 /// Parses a string, returns a crate.
 pub(crate) fn string_to_crate(source_str: String) -> ast::Crate {
-    let ps = ParseSess::new(FilePathMapping::empty());
+    let ps = ParseSess::new(
+        vec![crate::DEFAULT_LOCALE_RESOURCE, rustc_parse::DEFAULT_LOCALE_RESOURCE],
+        FilePathMapping::empty(),
+    );
     with_error_checking_parse(source_str, &ps, |p| p.parse_crate_mod())
 }
 
@@ -127,8 +133,10 @@ fn test_harness(file_text: &str, span_labels: Vec<SpanLabel>, expected_output: &
     create_default_session_if_not_set_then(|_| {
         let output = Arc::new(Mutex::new(Vec::new()));
 
-        let fallback_bundle =
-            rustc_errors::fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false);
+        let fallback_bundle = rustc_errors::fallback_fluent_bundle(
+            vec![crate::DEFAULT_LOCALE_RESOURCE, rustc_parse::DEFAULT_LOCALE_RESOURCE],
+            false,
+        );
         let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty()));
         source_map.new_source_file(Path::new("test.rs").to_owned().into(), file_text.to_owned());
 
diff --git a/compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl b/compiler/rustc_hir_analysis/locales/en-US.ftl
index f8d28817fec..aa56e60ec75 100644
--- a/compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl
+++ b/compiler/rustc_hir_analysis/locales/en-US.ftl
@@ -33,20 +33,7 @@ hir_analysis_field_already_declared =
     .label = field already declared
     .previous_decl_label = `{$field_name}` first declared here
 
-hir_analysis_copy_impl_on_type_with_dtor =
-    the trait `Copy` may not be implemented for this type; the type has a destructor
-    .label = `Copy` not allowed on types with destructors
-
-hir_analysis_multiple_relaxed_default_bounds =
-    type parameter has more than one relaxed default bound, only one is supported
-
-hir_analysis_copy_impl_on_non_adt =
-    the trait `Copy` may not be implemented for this type
-    .label = type is not a structure or enumeration
-
-hir_analysis_trait_object_declared_with_no_traits =
-    at least one trait is required for an object type
-    .alias_span = this alias does not contain a trait
+hir_analysis_expected_used_symbol = expected `used`, `used(compiler)` or `used(linker)`
 
 hir_analysis_ambiguous_lifetime_bound =
     ambiguous lifetime bound, explicit lifetime bound required
@@ -68,6 +55,25 @@ hir_analysis_value_of_associated_struct_already_specified =
 hir_analysis_unconstrained_opaque_type = unconstrained opaque type
     .note = `{$name}` must be used in combination with a concrete type within the same {$what}
 
+hir_analysis_manual_implementation =
+    manual implementations of `{$trait_name}` are experimental
+    .label = manual implementations of `{$trait_name}` are experimental
+    .help = add `#![feature(unboxed_closures)]` to the crate attributes to enable
+
+hir_analysis_substs_on_overridden_impl = could not resolve substs on overridden impl
+
+hir_analysis_unused_extern_crate =
+    unused extern crate
+    .suggestion = remove it
+
+hir_analysis_extern_crate_not_idiomatic =
+    `extern crate` is not idiomatic in the new edition
+    .suggestion = convert it to a `{$msg_code}`
+
+hir_analysis_trait_object_declared_with_no_traits =
+    at least one trait is required for an object type
+    .alias_span = this alias does not contain a trait
+
 hir_analysis_missing_type_params =
     the type {$parameterCount ->
         [one] parameter
@@ -90,20 +96,16 @@ hir_analysis_missing_type_params =
     } to {$parameters}
     .note = because of the default `Self` reference, type parameters must be specified on object types
 
-hir_analysis_manual_implementation =
-    manual implementations of `{$trait_name}` are experimental
-    .label = manual implementations of `{$trait_name}` are experimental
-    .help = add `#![feature(unboxed_closures)]` to the crate attributes to enable
-
-hir_analysis_substs_on_overridden_impl = could not resolve substs on overridden impl
+hir_analysis_copy_impl_on_type_with_dtor =
+    the trait `Copy` may not be implemented for this type; the type has a destructor
+    .label = `Copy` not allowed on types with destructors
 
-hir_analysis_unused_extern_crate =
-    unused extern crate
-    .suggestion = remove it
+hir_analysis_multiple_relaxed_default_bounds =
+    type parameter has more than one relaxed default bound, only one is supported
 
-hir_analysis_extern_crate_not_idiomatic =
-    `extern crate` is not idiomatic in the new edition
-    .suggestion = convert it to a `{$msg_code}`
+hir_analysis_copy_impl_on_non_adt =
+    the trait `Copy` may not be implemented for this type
+    .label = type is not a structure or enumeration
 
 hir_analysis_const_impl_for_non_const_trait =
     const `impl` for trait `{$trait_name}` which is not marked with `#[const_trait]`
diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs
index f3c3c02a05f..be3ef03192c 100644
--- a/compiler/rustc_hir_analysis/src/errors.rs
+++ b/compiler/rustc_hir_analysis/src/errors.rs
@@ -1,7 +1,10 @@
 //! Errors emitted by `rustc_hir_analysis`.
 
-use rustc_errors::{error_code, Applicability, DiagnosticBuilder, ErrorGuaranteed, Handler};
-use rustc_errors::{IntoDiagnostic, MultiSpan};
+use crate::fluent_generated as fluent;
+use rustc_errors::{
+    error_code, Applicability, DiagnosticBuilder, ErrorGuaranteed, Handler, IntoDiagnostic,
+    MultiSpan,
+};
 use rustc_macros::{Diagnostic, LintDiagnostic};
 use rustc_middle::ty::Ty;
 use rustc_span::{symbol::Ident, Span, Symbol};
@@ -41,11 +44,11 @@ pub struct LifetimesOrBoundsMismatchOnTrait {
     #[primary_span]
     #[label]
     pub span: Span,
-    #[label(generics_label)]
+    #[label(hir_analysis_generics_label)]
     pub generics_span: Option<Span>,
-    #[label(where_label)]
+    #[label(hir_analysis_where_label)]
     pub where_span: Option<Span>,
-    #[label(bounds_label)]
+    #[label(hir_analysis_bounds_label)]
     pub bounds_span: Vec<Span>,
     pub item_kind: &'static str,
     pub ident: Ident,
@@ -57,7 +60,7 @@ pub struct AsyncTraitImplShouldBeAsync {
     #[primary_span]
     // #[label]
     pub span: Span,
-    #[label(trait_item_label)]
+    #[label(hir_analysis_trait_item_label)]
     pub trait_item_span: Option<Span>,
     pub method_name: Symbol,
 }
@@ -77,7 +80,7 @@ pub struct FieldAlreadyDeclared {
     #[primary_span]
     #[label]
     pub span: Span,
-    #[label(previous_decl_label)]
+    #[label(hir_analysis_previous_decl_label)]
     pub prev_span: Span,
 }
 
@@ -109,7 +112,7 @@ pub struct CopyImplOnNonAdt {
 pub struct TraitObjectDeclaredWithNoTraits {
     #[primary_span]
     pub span: Span,
-    #[label(alias_span)]
+    #[label(hir_analysis_alias_span)]
     pub trait_alias_span: Option<Span>,
 }
 
@@ -145,7 +148,7 @@ pub struct ValueOfAssociatedStructAlreadySpecified {
     #[primary_span]
     #[label]
     pub span: Span,
-    #[label(previous_bound_label)]
+    #[label(hir_analysis_previous_bound_label)]
     pub prev_span: Span,
     pub item_name: Ident,
     pub def_path: String,
@@ -175,7 +178,7 @@ impl<'a> IntoDiagnostic<'a> for MissingTypeParams {
     fn into_diagnostic(self, handler: &'a Handler) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
         let mut err = handler.struct_span_err_with_code(
             self.span,
-            rustc_errors::fluent::hir_analysis_missing_type_params,
+            fluent::hir_analysis_missing_type_params,
             error_code!(E0393),
         );
         err.set_arg("parameterCount", self.missing_type_params.len());
@@ -188,7 +191,7 @@ impl<'a> IntoDiagnostic<'a> for MissingTypeParams {
                 .join(", "),
         );
 
-        err.span_label(self.def_span, rustc_errors::fluent::label);
+        err.span_label(self.def_span, fluent::hir_analysis_label);
 
         let mut suggested = false;
         // Don't suggest setting the type params if there are some already: the order is
@@ -203,7 +206,7 @@ impl<'a> IntoDiagnostic<'a> for MissingTypeParams {
                 // least we can clue them to the correct syntax `Iterator<Type>`.
                 err.span_suggestion(
                     self.span,
-                    rustc_errors::fluent::suggestion,
+                    fluent::hir_analysis_suggestion,
                     format!(
                         "{}<{}>",
                         snippet,
@@ -219,10 +222,10 @@ impl<'a> IntoDiagnostic<'a> for MissingTypeParams {
             }
         }
         if !suggested {
-            err.span_label(self.span, rustc_errors::fluent::no_suggestion_label);
+            err.span_label(self.span, fluent::hir_analysis_no_suggestion_label);
         }
 
-        err.note(rustc_errors::fluent::note);
+        err.note(fluent::hir_analysis_note);
         err
     }
 }
@@ -274,7 +277,7 @@ pub struct ConstImplForNonConstTrait {
     pub local_trait_span: Option<Span>,
     #[note]
     pub marking: (),
-    #[note(adding)]
+    #[note(hir_analysis_adding)]
     pub adding: (),
 }
 
diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs
index 73a71376662..d54e569905f 100644
--- a/compiler/rustc_hir_analysis/src/lib.rs
+++ b/compiler/rustc_hir_analysis/src/lib.rs
@@ -99,9 +99,11 @@ pub mod structured_errors;
 mod variance;
 
 use rustc_errors::{struct_span_err, ErrorGuaranteed};
+use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage};
 use rustc_hir as hir;
 use rustc_hir::Node;
 use rustc_infer::infer::{InferOk, TyCtxtInferExt};
+use rustc_macros::fluent_messages;
 use rustc_middle::middle;
 use rustc_middle::ty::query::Providers;
 use rustc_middle::ty::{self, Ty, TyCtxt};
@@ -118,6 +120,8 @@ use std::ops::Not;
 use astconv::AstConv;
 use bounds::Bounds;
 
+fluent_messages! { "../locales/en-US.ftl" }
+
 fn require_c_abi_if_c_variadic(tcx: TyCtxt<'_>, decl: &hir::FnDecl<'_>, abi: Abi, span: Span) {
     const ERROR_HEAD: &str = "C-variadic function must have a compatible calling convention";
     const CONVENTIONS_UNSTABLE: &str = "`C`, `cdecl`, `win64`, `sysv64` or `efiapi`";
diff --git a/compiler/rustc_error_messages/locales/en-US/hir_typeck.ftl b/compiler/rustc_hir_typeck/locales/en-US.ftl
index 05ac8db0db8..adfcbc36a4d 100644
--- a/compiler/rustc_error_messages/locales/en-US/hir_typeck.ftl
+++ b/compiler/rustc_hir_typeck/locales/en-US.ftl
@@ -1,17 +1,26 @@
-hir_typeck_fru_note = this expression may have been misinterpreted as a `..` range expression
-hir_typeck_fru_expr = this expression does not end in a comma...
-hir_typeck_fru_expr2 = ... so this is interpreted as a `..` range expression, instead of functional record update syntax
-hir_typeck_fru_suggestion =
-    to set the remaining fields{$expr ->
-        [NONE]{""}
-        *[other] {" "}from `{$expr}`
-    }, separate the last named field with a comma
-
 hir_typeck_field_multiply_specified_in_initializer =
     field `{$ident}` specified more than once
     .label = used more than once
     .previous_use_label = first use of `{$ident}`
 
+hir_typeck_copy_impl_on_type_with_dtor =
+    the trait `Copy` may not be implemented for this type; the type has a destructor
+    .label = `Copy` not allowed on types with destructors
+
+hir_typeck_multiple_relaxed_default_bounds =
+    type parameter has more than one relaxed default bound, only one is supported
+
+hir_typeck_copy_impl_on_non_adt =
+    the trait `Copy` may not be implemented for this type
+    .label = type is not a structure or enumeration
+
+hir_typeck_trait_object_declared_with_no_traits =
+    at least one trait is required for an object type
+    .alias_span = this alias does not contain a trait
+
+hir_typeck_functional_record_update_on_non_struct =
+    functional record update syntax requires a struct
+
 hir_typeck_return_stmt_outside_of_fn_body =
     return statement outside of function body
     .encl_body_label = the return is part of this body...
@@ -26,9 +35,6 @@ hir_typeck_struct_expr_non_exhaustive =
 hir_typeck_method_call_on_unknown_type =
     the type of this value must be known to call a method on a raw pointer on it
 
-hir_typeck_functional_record_update_on_non_struct =
-    functional record update syntax requires a struct
-
 hir_typeck_address_of_temporary_taken = cannot take address of a temporary
     .label = temporary value
 
@@ -44,9 +50,6 @@ hir_typeck_missing_parentheses_in_range = can't call method `{$method_name}` on
 
 hir_typeck_add_missing_parentheses_in_range = you must surround the range in parentheses to call its `{$func_name}` function
 
-hir_typeck_op_trait_generic_params =
-    `{$method_name}` must not have any generic parameters
-
 hir_typeck_lang_start_incorrect_number_params = incorrect number of parameters for the `start` lang item
 hir_typeck_lang_start_incorrect_number_params_note_expected_count = the `start` lang item should have four parameters, but found {$found_param_count}
 
@@ -63,3 +66,14 @@ hir_typeck_help_set_edition_standalone = pass `--edition {$edition}` to `rustc`
 hir_typeck_note_edition_guide = for more on editions, read https://doc.rust-lang.org/edition-guide
 
 hir_typeck_convert_to_str = try converting the passed type into a `&str`
+
+hir_typeck_op_trait_generic_params = `{$method_name}` must not have any generic parameters
+
+hir_typeck_fru_note = this expression may have been misinterpreted as a `..` range expression
+hir_typeck_fru_expr = this expression does not end in a comma...
+hir_typeck_fru_expr2 = ... so this is interpreted as a `..` range expression, instead of functional record update syntax
+hir_typeck_fru_suggestion =
+    to set the remaining fields{$expr ->
+        [NONE]{""}
+        *[other] {" "}from `{$expr}`
+    }, separate the last named field with a comma
diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs
index 2c8979402b6..3eee2278dca 100644
--- a/compiler/rustc_hir_typeck/src/errors.rs
+++ b/compiler/rustc_hir_typeck/src/errors.rs
@@ -1,4 +1,5 @@
 //! Errors emitted by `rustc_hir_typeck`.
+use crate::fluent_generated as fluent;
 use rustc_errors::{AddToDiagnostic, Applicability, Diagnostic, MultiSpan, SubdiagnosticMessage};
 use rustc_macros::{Diagnostic, Subdiagnostic};
 use rustc_middle::ty::Ty;
@@ -14,7 +15,7 @@ pub struct FieldMultiplySpecifiedInInitializer {
     #[primary_span]
     #[label]
     pub span: Span,
-    #[label(previous_use_label)]
+    #[label(hir_typeck_previous_use_label)]
     pub prev_span: Span,
     pub ident: Ident,
 }
@@ -24,9 +25,9 @@ pub struct FieldMultiplySpecifiedInInitializer {
 pub struct ReturnStmtOutsideOfFnBody {
     #[primary_span]
     pub span: Span,
-    #[label(encl_body_label)]
+    #[label(hir_typeck_encl_body_label)]
     pub encl_body_span: Option<Span>,
-    #[label(encl_fn_label)]
+    #[label(hir_typeck_encl_fn_label)]
     pub encl_fn_span: Option<Span>,
 }
 
@@ -157,20 +158,17 @@ impl AddToDiagnostic for TypeMismatchFruTypo {
 
         // Only explain that `a ..b` is a range if it's split up
         if self.expr_span.between(self.fru_span).is_empty() {
-            diag.span_note(
-                self.expr_span.to(self.fru_span),
-                rustc_errors::fluent::hir_typeck_fru_note,
-            );
+            diag.span_note(self.expr_span.to(self.fru_span), fluent::hir_typeck_fru_note);
         } else {
             let mut multispan: MultiSpan = vec![self.expr_span, self.fru_span].into();
-            multispan.push_span_label(self.expr_span, rustc_errors::fluent::hir_typeck_fru_expr);
-            multispan.push_span_label(self.fru_span, rustc_errors::fluent::hir_typeck_fru_expr2);
-            diag.span_note(multispan, rustc_errors::fluent::hir_typeck_fru_note);
+            multispan.push_span_label(self.expr_span, fluent::hir_typeck_fru_expr);
+            multispan.push_span_label(self.fru_span, fluent::hir_typeck_fru_expr2);
+            diag.span_note(multispan, fluent::hir_typeck_fru_note);
         }
 
         diag.span_suggestion(
             self.expr_span.shrink_to_hi(),
-            rustc_errors::fluent::hir_typeck_fru_suggestion,
+            fluent::hir_typeck_fru_suggestion,
             ", ",
             Applicability::MaybeIncorrect,
         );
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
index 3539202d1ca..3f3323cc1af 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
@@ -1,9 +1,10 @@
 use super::FnCtxt;
 
 use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel};
+use crate::fluent_generated as fluent;
 use crate::method::probe::{IsSuggestion, Mode, ProbeScope};
 use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX};
-use rustc_errors::{fluent, Applicability, Diagnostic, MultiSpan};
+use rustc_errors::{Applicability, Diagnostic, MultiSpan};
 use rustc_hir as hir;
 use rustc_hir::def::{CtorKind, CtorOf, DefKind};
 use rustc_hir::lang_items::LangItem;
diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs
index 0204beb6fb8..57c6c155a4d 100644
--- a/compiler/rustc_hir_typeck/src/lib.rs
+++ b/compiler/rustc_hir_typeck/src/lib.rs
@@ -53,7 +53,10 @@ use crate::check::check_fn;
 use crate::coercion::DynamicCoerceMany;
 use crate::gather_locals::GatherLocalsVisitor;
 use rustc_data_structures::unord::UnordSet;
-use rustc_errors::{struct_span_err, DiagnosticId, ErrorGuaranteed, MultiSpan};
+use rustc_errors::{
+    struct_span_err, DiagnosticId, DiagnosticMessage, ErrorGuaranteed, MultiSpan,
+    SubdiagnosticMessage,
+};
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::intravisit::Visitor;
@@ -61,6 +64,7 @@ use rustc_hir::{HirIdMap, Node};
 use rustc_hir_analysis::astconv::AstConv;
 use rustc_hir_analysis::check::check_abi;
 use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
+use rustc_macros::fluent_messages;
 use rustc_middle::traits;
 use rustc_middle::ty::query::Providers;
 use rustc_middle::ty::{self, Ty, TyCtxt};
@@ -69,6 +73,8 @@ use rustc_session::Session;
 use rustc_span::def_id::{DefId, LocalDefId};
 use rustc_span::Span;
 
+fluent_messages! { "../locales/en-US.ftl" }
+
 #[macro_export]
 macro_rules! type_error_struct {
     ($session:expr, $span:expr, $typ:expr, $code:ident, $($message:tt)*) => ({
diff --git a/compiler/rustc_error_messages/locales/en-US/incremental.ftl b/compiler/rustc_incremental/locales/en-US.ftl
index 4852ee0d959..4852ee0d959 100644
--- a/compiler/rustc_error_messages/locales/en-US/incremental.ftl
+++ b/compiler/rustc_incremental/locales/en-US.ftl
diff --git a/compiler/rustc_incremental/src/lib.rs b/compiler/rustc_incremental/src/lib.rs
index 3c58cfa38f2..511e466c2ae 100644
--- a/compiler/rustc_incremental/src/lib.rs
+++ b/compiler/rustc_incremental/src/lib.rs
@@ -31,3 +31,8 @@ pub use persist::save_dep_graph;
 pub use persist::save_work_product_index;
 pub use persist::LoadResult;
 pub use persist::{build_dep_graph, load_dep_graph, DepGraphFuture};
+
+use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage};
+use rustc_macros::fluent_messages;
+
+fluent_messages! { "../locales/en-US.ftl" }
diff --git a/compiler/rustc_error_messages/locales/en-US/infer.ftl b/compiler/rustc_infer/locales/en-US.ftl
index c5b2b6c2d73..c5b2b6c2d73 100644
--- a/compiler/rustc_error_messages/locales/en-US/infer.ftl
+++ b/compiler/rustc_infer/locales/en-US.ftl
diff --git a/compiler/rustc_infer/src/errors/mod.rs b/compiler/rustc_infer/src/errors/mod.rs
index 0c2713fb1a7..7dccd0bb930 100644
--- a/compiler/rustc_infer/src/errors/mod.rs
+++ b/compiler/rustc_infer/src/errors/mod.rs
@@ -1,6 +1,6 @@
 use hir::GenericParamKind;
 use rustc_errors::{
-    fluent, AddToDiagnostic, Applicability, Diagnostic, DiagnosticMessage, DiagnosticStyledString,
+    AddToDiagnostic, Applicability, Diagnostic, DiagnosticMessage, DiagnosticStyledString,
     IntoDiagnosticArg, MultiSpan, SubdiagnosticMessage,
 };
 use rustc_hir as hir;
@@ -12,9 +12,10 @@ use rustc_span::symbol::kw;
 use rustc_span::Symbol;
 use rustc_span::{symbol::Ident, BytePos, Span};
 
-use crate::infer::error_reporting::nice_region_error::placeholder_error::Highlighted;
+use crate::fluent_generated as fluent;
 use crate::infer::error_reporting::{
     need_type_info::{GeneratorKindAsDiagArg, UnderspecifiedArgKind},
+    nice_region_error::placeholder_error::Highlighted,
     ObligationCauseAsDiagArg,
 };
 
@@ -26,9 +27,9 @@ pub struct OpaqueHiddenTypeDiag {
     #[primary_span]
     #[label]
     pub span: Span,
-    #[note(opaque_type)]
+    #[note(infer_opaque_type)]
     pub opaque_type: Span,
-    #[note(hidden_type)]
+    #[note(infer_hidden_type)]
     pub hidden_type: Span,
 }
 
@@ -768,11 +769,11 @@ impl<'tcx> ActualImplExplNotes<'tcx> {
 pub struct TraitPlaceholderMismatch<'tcx> {
     #[primary_span]
     pub span: Span,
-    #[label(label_satisfy)]
+    #[label(infer_label_satisfy)]
     pub satisfy_span: Option<Span>,
-    #[label(label_where)]
+    #[label(infer_label_where)]
     pub where_span: Option<Span>,
-    #[label(label_dup)]
+    #[label(infer_label_dup)]
     pub dup_span: Option<Span>,
     pub def_id: String,
     pub trait_def_id: String,
@@ -808,11 +809,11 @@ pub struct RelationshipHelp;
 #[diag(infer_trait_impl_diff)]
 pub struct TraitImplDiff {
     #[primary_span]
-    #[label(found)]
+    #[label(infer_found)]
     pub sp: Span,
-    #[label(expected)]
+    #[label(infer_expected)]
     pub trait_sp: Span,
-    #[note(expected_found)]
+    #[note(infer_expected_found)]
     pub note: (),
     #[subdiagnostic]
     pub param_help: ConsiderBorrowingParamHelp,
@@ -852,10 +853,10 @@ impl AddToDiagnostic for DynTraitConstraintSuggestion {
 #[derive(Diagnostic)]
 #[diag(infer_but_calling_introduces, code = "E0772")]
 pub struct ButCallingIntroduces {
-    #[label(label1)]
+    #[label(infer_label1)]
     pub param_ty_span: Span,
     #[primary_span]
-    #[label(label2)]
+    #[label(infer_label2)]
     pub cause_span: Span,
 
     pub has_param_name: bool,
@@ -913,15 +914,15 @@ impl AddToDiagnostic for MoreTargeted {
 pub struct ButNeedsToSatisfy {
     #[primary_span]
     pub sp: Span,
-    #[label(influencer)]
+    #[label(infer_influencer)]
     pub influencer_point: Span,
-    #[label(used_here)]
+    #[label(infer_used_here)]
     pub spans: Vec<Span>,
-    #[label(require)]
+    #[label(infer_require)]
     pub require_span_as_label: Option<Span>,
-    #[note(require)]
+    #[note(infer_require)]
     pub require_span_as_note: Option<Span>,
-    #[note(introduced_by_bound)]
+    #[note(infer_introduced_by_bound)]
     pub bound: Option<Span>,
 
     #[subdiagnostic]
diff --git a/compiler/rustc_infer/src/errors/note_and_explain.rs b/compiler/rustc_infer/src/errors/note_and_explain.rs
index cb96aeec5f3..ef543b1fb93 100644
--- a/compiler/rustc_infer/src/errors/note_and_explain.rs
+++ b/compiler/rustc_infer/src/errors/note_and_explain.rs
@@ -1,7 +1,6 @@
+use crate::fluent_generated as fluent;
 use crate::infer::error_reporting::nice_region_error::find_anon_type;
-use rustc_errors::{
-    self, fluent, AddToDiagnostic, Diagnostic, IntoDiagnosticArg, SubdiagnosticMessage,
-};
+use rustc_errors::{self, AddToDiagnostic, Diagnostic, IntoDiagnosticArg, SubdiagnosticMessage};
 use rustc_middle::ty::{self, TyCtxt};
 use rustc_span::{symbol::kw, Span};
 
diff --git a/compiler/rustc_infer/src/infer/error_reporting/note.rs b/compiler/rustc_infer/src/infer/error_reporting/note.rs
index e0e89158a58..7ffe1fd20b4 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/note.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/note.rs
@@ -2,10 +2,11 @@ use crate::errors::{
     note_and_explain, FullfillReqLifetime, LfBoundNotSatisfied, OutlivesBound, OutlivesContent,
     RefLongerThanData, RegionOriginNote, WhereClauseSuggestions,
 };
+use crate::fluent_generated as fluent;
 use crate::infer::error_reporting::{note_and_explain_region, TypeErrCtxt};
 use crate::infer::{self, SubregionOrigin};
 use rustc_errors::{
-    fluent, AddToDiagnostic, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, IntoDiagnostic,
+    AddToDiagnostic, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, IntoDiagnostic,
 };
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_middle::traits::ObligationCauseCode;
diff --git a/compiler/rustc_infer/src/lib.rs b/compiler/rustc_infer/src/lib.rs
index 4c119a44355..b03ecb59750 100644
--- a/compiler/rustc_infer/src/lib.rs
+++ b/compiler/rustc_infer/src/lib.rs
@@ -33,6 +33,11 @@ extern crate tracing;
 #[macro_use]
 extern crate rustc_middle;
 
+use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage};
+use rustc_macros::fluent_messages;
+
 mod errors;
 pub mod infer;
 pub mod traits;
+
+fluent_messages! { "../locales/en-US.ftl" }
diff --git a/compiler/rustc_error_messages/locales/en-US/interface.ftl b/compiler/rustc_interface/locales/en-US.ftl
index a7bc0e7af1f..a7bc0e7af1f 100644
--- a/compiler/rustc_error_messages/locales/en-US/interface.ftl
+++ b/compiler/rustc_interface/locales/en-US.ftl
diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs
index d504aea77d0..5e38ca034ac 100644
--- a/compiler/rustc_interface/src/interface.rs
+++ b/compiler/rustc_interface/src/interface.rs
@@ -222,6 +222,7 @@ pub struct Config {
     pub output_dir: Option<PathBuf>,
     pub output_file: Option<PathBuf>,
     pub file_loader: Option<Box<dyn FileLoader + Send + Sync>>,
+    pub locale_resources: &'static [&'static str],
 
     pub lint_caps: FxHashMap<lint::LintId, lint::Level>,
 
@@ -267,6 +268,7 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se
                 config.opts,
                 config.crate_cfg,
                 config.crate_check_cfg,
+                config.locale_resources,
                 config.file_loader,
                 CompilerIO {
                     input: config.input,
diff --git a/compiler/rustc_interface/src/lib.rs b/compiler/rustc_interface/src/lib.rs
index 82bc4770b6b..1abbe8d4fab 100644
--- a/compiler/rustc_interface/src/lib.rs
+++ b/compiler/rustc_interface/src/lib.rs
@@ -12,6 +12,9 @@
 #[macro_use]
 extern crate tracing;
 
+use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage};
+use rustc_macros::fluent_messages;
+
 mod callbacks;
 mod errors;
 pub mod interface;
@@ -27,3 +30,5 @@ pub use queries::Queries;
 
 #[cfg(test)]
 mod tests;
+
+fluent_messages! { "../locales/en-US.ftl" }
diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs
index bfb1e822a26..71a72036994 100644
--- a/compiler/rustc_interface/src/tests.rs
+++ b/compiler/rustc_interface/src/tests.rs
@@ -50,7 +50,7 @@ fn mk_session(matches: getopts::Matches) -> (Session, CfgSpecs) {
         output_file: None,
         temps_dir,
     };
-    let sess = build_session(sessopts, io, None, registry, Default::default(), None, None);
+    let sess = build_session(sessopts, io, None, registry, vec![], Default::default(), None, None);
     (sess, cfg)
 }
 
diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs
index 475d3601b52..e5d2fb2ea28 100644
--- a/compiler/rustc_interface/src/util.rs
+++ b/compiler/rustc_interface/src/util.rs
@@ -59,6 +59,7 @@ pub fn create_session(
     sopts: config::Options,
     cfg: FxHashSet<(String, Option<String>)>,
     check_cfg: CheckCfg,
+    locale_resources: &'static [&'static str],
     file_loader: Option<Box<dyn FileLoader + Send + Sync + 'static>>,
     io: CompilerIO,
     lint_caps: FxHashMap<lint::LintId, lint::Level>,
@@ -89,11 +90,15 @@ pub fn create_session(
         }
     };
 
+    let mut locale_resources = Vec::from(locale_resources);
+    locale_resources.push(codegen_backend.locale_resource());
+
     let mut sess = session::build_session(
         sopts,
         io,
         bundle,
         descriptions,
+        locale_resources,
         lint_caps,
         file_loader,
         target_override,
diff --git a/compiler/rustc_error_messages/locales/en-US/lint.ftl b/compiler/rustc_lint/locales/en-US.ftl
index b1e7cc69a80..b1e7cc69a80 100644
--- a/compiler/rustc_error_messages/locales/en-US/lint.ftl
+++ b/compiler/rustc_lint/locales/en-US.ftl
diff --git a/compiler/rustc_lint/src/array_into_iter.rs b/compiler/rustc_lint/src/array_into_iter.rs
index 3593f141df6..bccb0a94e98 100644
--- a/compiler/rustc_lint/src/array_into_iter.rs
+++ b/compiler/rustc_lint/src/array_into_iter.rs
@@ -1,5 +1,7 @@
-use crate::lints::{ArrayIntoIterDiag, ArrayIntoIterDiagSub};
-use crate::{LateContext, LateLintPass, LintContext};
+use crate::{
+    lints::{ArrayIntoIterDiag, ArrayIntoIterDiagSub},
+    LateContext, LateLintPass, LintContext,
+};
 use rustc_hir as hir;
 use rustc_middle::ty;
 use rustc_middle::ty::adjustment::{Adjust, Adjustment};
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index 726042479e9..5e90669d33a 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -20,6 +20,7 @@
 //! If you define a new `LateLintPass`, you will also need to add it to the
 //! `late_lint_methods!` invocation in `lib.rs`.
 
+use crate::fluent_generated as fluent;
 use crate::{
     errors::BuiltinEllpisisInclusiveRangePatterns,
     lints::{
@@ -50,7 +51,7 @@ use rustc_ast::{self as ast, *};
 use rustc_ast_pretty::pprust::{self, expr_to_string};
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::stack::ensure_sufficient_stack;
-use rustc_errors::{fluent, Applicability, DecorateLint, MultiSpan};
+use rustc_errors::{Applicability, DecorateLint, MultiSpan};
 use rustc_feature::{deprecated_attributes, AttributeGate, BuiltinAttribute, GateIssue, Stability};
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
diff --git a/compiler/rustc_lint/src/errors.rs b/compiler/rustc_lint/src/errors.rs
index f3ae2609186..9af5284df1e 100644
--- a/compiler/rustc_lint/src/errors.rs
+++ b/compiler/rustc_lint/src/errors.rs
@@ -1,6 +1,6 @@
+use crate::fluent_generated as fluent;
 use rustc_errors::{
-    fluent, AddToDiagnostic, Diagnostic, ErrorGuaranteed, Handler, IntoDiagnostic,
-    SubdiagnosticMessage,
+    AddToDiagnostic, Diagnostic, ErrorGuaranteed, Handler, IntoDiagnostic, SubdiagnosticMessage,
 };
 use rustc_macros::{Diagnostic, Subdiagnostic};
 use rustc_session::lint::Level;
@@ -116,7 +116,7 @@ impl IntoDiagnostic<'_> for CheckNameUnknown {
         let mut diag = handler.struct_err(fluent::lint_check_name_unknown);
         diag.code(rustc_errors::error_code!(E0602));
         if let Some(suggestion) = self.suggestion {
-            diag.help(fluent::help);
+            diag.help(fluent::lint_help);
             diag.set_arg("suggestion", suggestion);
         }
         diag.set_arg("lint_name", self.lint_name);
diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs
index cca36913dea..bc7488fab4a 100644
--- a/compiler/rustc_lint/src/levels.rs
+++ b/compiler/rustc_lint/src/levels.rs
@@ -1,13 +1,16 @@
-use crate::context::{CheckLintNameResult, LintStore};
-use crate::late::unerased_lint_store;
-use crate::lints::{
-    DeprecatedLintName, IgnoredUnlessCrateSpecified, OverruledAtributeLint, RenamedOrRemovedLint,
-    RenamedOrRemovedLintSuggestion, UnknownLint, UnknownLintSuggestion,
+use crate::{
+    context::{CheckLintNameResult, LintStore},
+    fluent_generated as fluent,
+    late::unerased_lint_store,
+    lints::{
+        DeprecatedLintName, IgnoredUnlessCrateSpecified, OverruledAtributeLint,
+        RenamedOrRemovedLint, RenamedOrRemovedLintSuggestion, UnknownLint, UnknownLintSuggestion,
+    },
 };
 use rustc_ast as ast;
 use rustc_ast_pretty::pprust;
 use rustc_data_structures::fx::FxHashMap;
-use rustc_errors::{fluent, DecorateLint, DiagnosticBuilder, DiagnosticMessage, MultiSpan};
+use rustc_errors::{DecorateLint, DiagnosticBuilder, DiagnosticMessage, MultiSpan};
 use rustc_hir as hir;
 use rustc_hir::intravisit::{self, Visitor};
 use rustc_hir::HirId;
@@ -983,7 +986,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
                     fluent::lint_unknown_gated_lint,
                     |lint| {
                         lint.set_arg("name", lint_id.lint.name_lower());
-                        lint.note(fluent::note);
+                        lint.note(fluent::lint_note);
                         add_feature_diagnostics(lint, &self.sess.parse_sess, feature);
                         lint
                     },
diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs
index 4ca37ef6850..2070ffea4d9 100644
--- a/compiler/rustc_lint/src/lib.rs
+++ b/compiler/rustc_lint/src/lib.rs
@@ -80,8 +80,10 @@ mod unused;
 pub use array_into_iter::ARRAY_INTO_ITER;
 
 use rustc_ast as ast;
+use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage};
 use rustc_hir as hir;
 use rustc_hir::def_id::LocalDefId;
+use rustc_macros::fluent_messages;
 use rustc_middle::ty::query::Providers;
 use rustc_middle::ty::TyCtxt;
 use rustc_session::lint::builtin::{
@@ -122,6 +124,8 @@ pub use rustc_session::lint::Level::{self, *};
 pub use rustc_session::lint::{BufferedEarlyLint, FutureIncompatibleInfo, Lint, LintId};
 pub use rustc_session::lint::{LintArray, LintPass};
 
+fluent_messages! { "../locales/en-US.ftl" }
+
 pub fn provide(providers: &mut Providers) {
     levels::provide(providers);
     expect::provide(providers);
diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs
index aa6e230dc6d..2d9aa9074be 100644
--- a/compiler/rustc_lint/src/lints.rs
+++ b/compiler/rustc_lint/src/lints.rs
@@ -2,9 +2,10 @@
 #![allow(rustc::diagnostic_outside_of_impl)]
 use std::num::NonZeroU32;
 
+use crate::fluent_generated as fluent;
 use rustc_errors::{
-    fluent, AddToDiagnostic, Applicability, DecorateLint, DiagnosticMessage,
-    DiagnosticStyledString, SuggestionStyle,
+    AddToDiagnostic, Applicability, DecorateLint, DiagnosticMessage, DiagnosticStyledString,
+    SuggestionStyle,
 };
 use rustc_hir::def_id::DefId;
 use rustc_macros::{LintDiagnostic, Subdiagnostic};
@@ -23,7 +24,7 @@ use crate::{
 #[diag(lint_array_into_iter)]
 pub struct ArrayIntoIterDiag<'a> {
     pub target: &'a str,
-    #[suggestion(use_iter_suggestion, code = "iter", applicability = "machine-applicable")]
+    #[suggestion(lint_use_iter_suggestion, code = "iter", applicability = "machine-applicable")]
     pub suggestion: Span,
     #[subdiagnostic]
     pub sub: Option<ArrayIntoIterDiagSub>,
@@ -31,12 +32,15 @@ pub struct ArrayIntoIterDiag<'a> {
 
 #[derive(Subdiagnostic)]
 pub enum ArrayIntoIterDiagSub {
-    #[suggestion(remove_into_iter_suggestion, code = "", applicability = "maybe-incorrect")]
+    #[suggestion(lint_remove_into_iter_suggestion, code = "", applicability = "maybe-incorrect")]
     RemoveIntoIter {
         #[primary_span]
         span: Span,
     },
-    #[multipart_suggestion(use_explicit_into_iter_suggestion, applicability = "maybe-incorrect")]
+    #[multipart_suggestion(
+        lint_use_explicit_into_iter_suggestion,
+        applicability = "maybe-incorrect"
+    )]
     UseExplicitIntoIter {
         #[suggestion_part(code = "IntoIterator::into_iter(")]
         start_span: Span,
@@ -163,13 +167,13 @@ pub struct BuiltinDeprecatedAttrLink<'a> {
 
 #[derive(Subdiagnostic)]
 pub enum BuiltinDeprecatedAttrLinkSuggestion<'a> {
-    #[suggestion(msg_suggestion, code = "", applicability = "machine-applicable")]
+    #[suggestion(lint_msg_suggestion, code = "", applicability = "machine-applicable")]
     Msg {
         #[primary_span]
         suggestion: Span,
         msg: &'a str,
     },
-    #[suggestion(default_suggestion, code = "", applicability = "machine-applicable")]
+    #[suggestion(lint_default_suggestion, code = "", applicability = "machine-applicable")]
     Default {
         #[primary_span]
         suggestion: Span,
@@ -201,9 +205,9 @@ pub struct BuiltinUnusedDocComment<'a> {
 
 #[derive(Subdiagnostic)]
 pub enum BuiltinUnusedDocCommentSub {
-    #[help(plain_help)]
+    #[help(lint_plain_help)]
     PlainHelp,
-    #[help(block_help)]
+    #[help(lint_block_help)]
     BlockHelp,
 }
 
@@ -242,7 +246,7 @@ impl<'a> DecorateLint<'a, ()> for BuiltinUngatedAsyncFnTrackCaller<'_> {
         self,
         diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>,
     ) -> &'b mut rustc_errors::DiagnosticBuilder<'a, ()> {
-        diag.span_label(self.label, fluent::label);
+        diag.span_label(self.label, fluent::lint_label);
         rustc_session::parse::add_feature_diagnostics(
             diag,
             &self.parse_sess,
@@ -337,7 +341,7 @@ impl AddToDiagnostic for BuiltinTypeAliasGenericBoundsSuggestion {
         ) -> rustc_errors::SubdiagnosticMessage,
     {
         diag.multipart_suggestion(
-            fluent::suggestion,
+            fluent::lint_suggestion,
             self.suggestions,
             Applicability::MachineApplicable,
         );
@@ -388,7 +392,7 @@ pub struct BuiltinExplicitOutlives {
 }
 
 #[derive(Subdiagnostic)]
-#[multipart_suggestion(suggestion)]
+#[multipart_suggestion(lint_suggestion)]
 pub struct BuiltinExplicitOutlivesSuggestion {
     #[suggestion_part(code = "")]
     pub spans: Vec<Span>,
@@ -407,11 +411,11 @@ pub struct BuiltinIncompleteFeatures {
 }
 
 #[derive(Subdiagnostic)]
-#[help(help)]
+#[help(lint_help)]
 pub struct BuiltinIncompleteFeaturesHelp;
 
 #[derive(Subdiagnostic)]
-#[note(note)]
+#[note(lint_note)]
 pub struct BuiltinIncompleteFeaturesNote {
     pub n: NonZeroU32,
 }
@@ -482,9 +486,9 @@ pub enum BuiltinClashingExtern<'a> {
     SameName {
         this: Symbol,
         orig: Symbol,
-        #[label(previous_decl_label)]
+        #[label(lint_previous_decl_label)]
         previous_decl_label: Span,
-        #[label(mismatch_label)]
+        #[label(lint_mismatch_label)]
         mismatch_label: Span,
         #[subdiagnostic]
         sub: BuiltinClashingExternSub<'a>,
@@ -493,9 +497,9 @@ pub enum BuiltinClashingExtern<'a> {
     DiffName {
         this: Symbol,
         orig: Symbol,
-        #[label(previous_decl_label)]
+        #[label(lint_previous_decl_label)]
         previous_decl_label: Span,
-        #[label(mismatch_label)]
+        #[label(lint_mismatch_label)]
         mismatch_label: Span,
         #[subdiagnostic]
         sub: BuiltinClashingExternSub<'a>,
@@ -571,7 +575,7 @@ pub struct SupertraitAsDerefTarget<'a> {
 }
 
 #[derive(Subdiagnostic)]
-#[label(label)]
+#[label(lint_label)]
 pub struct SupertraitAsDerefTargetLabel {
     #[primary_span]
     pub label: Span,
@@ -604,7 +608,7 @@ pub struct Expectation {
 }
 
 #[derive(Subdiagnostic)]
-#[note(rationale)]
+#[note(lint_rationale)]
 pub struct ExpectationNote {
     pub rationale: Symbol,
 }
@@ -625,13 +629,13 @@ pub struct ForLoopsOverFalliblesDiag<'a> {
 
 #[derive(Subdiagnostic)]
 pub enum ForLoopsOverFalliblesLoopSub<'a> {
-    #[suggestion(remove_next, code = ".by_ref()", applicability = "maybe-incorrect")]
+    #[suggestion(lint_remove_next, code = ".by_ref()", applicability = "maybe-incorrect")]
     RemoveNext {
         #[primary_span]
         suggestion: Span,
         recv_snip: String,
     },
-    #[multipart_suggestion(use_while_let, applicability = "maybe-incorrect")]
+    #[multipart_suggestion(lint_use_while_let, applicability = "maybe-incorrect")]
     UseWhileLet {
         #[suggestion_part(code = "while let {var}(")]
         start_span: Span,
@@ -642,14 +646,14 @@ pub enum ForLoopsOverFalliblesLoopSub<'a> {
 }
 
 #[derive(Subdiagnostic)]
-#[suggestion(use_question_mark, code = "?", applicability = "maybe-incorrect")]
+#[suggestion(lint_use_question_mark, code = "?", applicability = "maybe-incorrect")]
 pub struct ForLoopsOverFalliblesQuestionMark {
     #[primary_span]
     pub suggestion: Span,
 }
 
 #[derive(Subdiagnostic)]
-#[multipart_suggestion(suggestion, applicability = "maybe-incorrect")]
+#[multipart_suggestion(lint_suggestion, applicability = "maybe-incorrect")]
 pub struct ForLoopsOverFalliblesSuggestion<'a> {
     pub var: &'a str,
     #[suggestion_part(code = "if let {var}(")]
@@ -708,13 +712,13 @@ impl AddToDiagnostic for HiddenUnicodeCodepointsDiagSub {
         match self {
             HiddenUnicodeCodepointsDiagSub::Escape { spans } => {
                 diag.multipart_suggestion_with_style(
-                    fluent::suggestion_remove,
+                    fluent::lint_suggestion_remove,
                     spans.iter().map(|(_, span)| (*span, "".to_string())).collect(),
                     Applicability::MachineApplicable,
                     SuggestionStyle::HideCodeAlways,
                 );
                 diag.multipart_suggestion(
-                    fluent::suggestion_escape,
+                    fluent::lint_suggestion_escape,
                     spans
                         .into_iter()
                         .map(|(c, span)| {
@@ -737,8 +741,8 @@ impl AddToDiagnostic for HiddenUnicodeCodepointsDiagSub {
                         .collect::<Vec<String>>()
                         .join(", "),
                 );
-                diag.note(fluent::suggestion_remove);
-                diag.note(fluent::no_suggestion_note_escape);
+                diag.note(fluent::lint_suggestion_remove);
+                diag.note(fluent::lint_no_suggestion_note_escape);
             }
         }
     }
@@ -883,7 +887,7 @@ pub struct RenamedOrRemovedLint<'a> {
 }
 
 #[derive(Subdiagnostic)]
-#[suggestion(suggestion, code = "{replace}", applicability = "machine-applicable")]
+#[suggestion(lint_suggestion, code = "{replace}", applicability = "machine-applicable")]
 pub struct RenamedOrRemovedLintSuggestion<'a> {
     #[primary_span]
     pub suggestion: Span,
@@ -899,7 +903,7 @@ pub struct UnknownLint {
 }
 
 #[derive(Subdiagnostic)]
-#[suggestion(suggestion, code = "{replace}", applicability = "maybe-incorrect")]
+#[suggestion(lint_suggestion, code = "{replace}", applicability = "maybe-incorrect")]
 pub struct UnknownLintSuggestion {
     #[primary_span]
     pub suggestion: Span,
@@ -919,9 +923,9 @@ pub struct IgnoredUnlessCrateSpecified<'a> {
 #[note]
 #[help]
 pub struct CStringPtr {
-    #[label(as_ptr_label)]
+    #[label(lint_as_ptr_label)]
     pub as_ptr: Span,
-    #[label(unwrap_label)]
+    #[label(lint_unwrap_label)]
     pub unwrap: Span,
 }
 
@@ -952,7 +956,7 @@ pub struct ConfusableIdentifierPair {
 
 #[derive(LintDiagnostic)]
 #[diag(lint_mixed_script_confusables)]
-#[note(includes_note)]
+#[note(lint_includes_note)]
 #[note]
 pub struct MixedScriptConfusables {
     pub set: String,
@@ -972,17 +976,17 @@ impl<'a> DecorateLint<'a, ()> for NonFmtPanicUnused {
         diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>,
     ) -> &'b mut rustc_errors::DiagnosticBuilder<'a, ()> {
         diag.set_arg("count", self.count);
-        diag.note(fluent::note);
+        diag.note(fluent::lint_note);
         if let Some(span) = self.suggestion {
             diag.span_suggestion(
                 span.shrink_to_hi(),
-                fluent::add_args_suggestion,
+                fluent::lint_add_args_suggestion,
                 ", ...",
                 Applicability::HasPlaceholders,
             );
             diag.span_suggestion(
                 span.shrink_to_lo(),
-                fluent::add_fmt_suggestion,
+                fluent::lint_add_fmt_suggestion,
                 "\"{}\", ",
                 Applicability::MachineApplicable,
             );
@@ -1016,12 +1020,12 @@ pub struct NonCamelCaseType<'a> {
 
 #[derive(Subdiagnostic)]
 pub enum NonCamelCaseTypeSub {
-    #[label(label)]
+    #[label(lint_label)]
     Label {
         #[primary_span]
         span: Span,
     },
-    #[suggestion(suggestion, code = "{replace}", applicability = "maybe-incorrect")]
+    #[suggestion(lint_suggestion, code = "{replace}", applicability = "maybe-incorrect")]
     Suggestion {
         #[primary_span]
         span: Span,
@@ -1057,15 +1061,15 @@ impl AddToDiagnostic for NonSnakeCaseDiagSub {
     {
         match self {
             NonSnakeCaseDiagSub::Label { span } => {
-                diag.span_label(span, fluent::label);
+                diag.span_label(span, fluent::lint_label);
             }
             NonSnakeCaseDiagSub::Help => {
-                diag.help(fluent::help);
+                diag.help(fluent::lint_help);
             }
             NonSnakeCaseDiagSub::ConvertSuggestion { span, suggestion } => {
                 diag.span_suggestion(
                     span,
-                    fluent::convert_suggestion,
+                    fluent::lint_convert_suggestion,
                     suggestion,
                     Applicability::MaybeIncorrect,
                 );
@@ -1073,16 +1077,16 @@ impl AddToDiagnostic for NonSnakeCaseDiagSub {
             NonSnakeCaseDiagSub::RenameOrConvertSuggestion { span, suggestion } => {
                 diag.span_suggestion(
                     span,
-                    fluent::rename_or_convert_suggestion,
+                    fluent::lint_rename_or_convert_suggestion,
                     suggestion,
                     Applicability::MaybeIncorrect,
                 );
             }
             NonSnakeCaseDiagSub::SuggestionAndNote { span } => {
-                diag.note(fluent::cannot_convert_note);
+                diag.note(fluent::lint_cannot_convert_note);
                 diag.span_suggestion(
                     span,
-                    fluent::rename_suggestion,
+                    fluent::lint_rename_suggestion,
                     "",
                     Applicability::MaybeIncorrect,
                 );
@@ -1102,12 +1106,12 @@ pub struct NonUpperCaseGlobal<'a> {
 
 #[derive(Subdiagnostic)]
 pub enum NonUpperCaseGlobalSub {
-    #[label(label)]
+    #[label(lint_label)]
     Label {
         #[primary_span]
         span: Span,
     },
-    #[suggestion(suggestion, code = "{replace}", applicability = "maybe-incorrect")]
+    #[suggestion(lint_suggestion, code = "{replace}", applicability = "maybe-incorrect")]
     Suggestion {
         #[primary_span]
         span: Span,
@@ -1225,11 +1229,11 @@ impl AddToDiagnostic for OverflowingBinHexSign {
     {
         match self {
             OverflowingBinHexSign::Positive => {
-                diag.note(fluent::positive_note);
+                diag.note(fluent::lint_positive_note);
             }
             OverflowingBinHexSign::Negative => {
-                diag.note(fluent::negative_note);
-                diag.note(fluent::negative_becomes_note);
+                diag.note(fluent::lint_negative_note);
+                diag.note(fluent::lint_negative_becomes_note);
             }
         }
     }
@@ -1238,7 +1242,7 @@ impl AddToDiagnostic for OverflowingBinHexSign {
 #[derive(Subdiagnostic)]
 pub enum OverflowingBinHexSub<'a> {
     #[suggestion(
-        suggestion,
+        lint_suggestion,
         code = "{sans_suffix}{suggestion_ty}",
         applicability = "machine-applicable"
     )]
@@ -1248,7 +1252,7 @@ pub enum OverflowingBinHexSub<'a> {
         suggestion_ty: &'a str,
         sans_suffix: &'a str,
     },
-    #[help(help)]
+    #[help(lint_help)]
     Help { suggestion_ty: &'a str },
 }
 
@@ -1265,7 +1269,7 @@ pub struct OverflowingInt<'a> {
 }
 
 #[derive(Subdiagnostic)]
-#[help(help)]
+#[help(lint_help)]
 pub struct OverflowingIntHelp<'a> {
     pub suggestion_ty: &'a str,
 }
@@ -1317,13 +1321,13 @@ impl<'a> DecorateLint<'a, ()> for ImproperCTypes<'_> {
     ) -> &'b mut rustc_errors::DiagnosticBuilder<'a, ()> {
         diag.set_arg("ty", self.ty);
         diag.set_arg("desc", self.desc);
-        diag.span_label(self.label, fluent::label);
+        diag.span_label(self.label, fluent::lint_label);
         if let Some(help) = self.help {
             diag.help(help);
         }
         diag.note(self.note);
         if let Some(note) = self.span_note {
-            diag.span_note(note, fluent::note);
+            diag.span_note(note, fluent::lint_note);
         }
         diag
     }
@@ -1416,7 +1420,7 @@ pub struct UnusedDef<'a, 'b> {
 #[derive(Subdiagnostic)]
 pub enum UnusedDefSuggestion {
     #[suggestion(
-        suggestion,
+        lint_suggestion,
         style = "verbose",
         code = "let _ = ",
         applicability = "machine-applicable"
@@ -1460,13 +1464,13 @@ pub struct PathStatementDrop {
 
 #[derive(Subdiagnostic)]
 pub enum PathStatementDropSub {
-    #[suggestion(suggestion, code = "drop({snippet});", applicability = "machine-applicable")]
+    #[suggestion(lint_suggestion, code = "drop({snippet});", applicability = "machine-applicable")]
     Suggestion {
         #[primary_span]
         span: Span,
         snippet: String,
     },
-    #[help(help)]
+    #[help(lint_help)]
     Help {
         #[primary_span]
         span: Span,
@@ -1487,7 +1491,7 @@ pub struct UnusedDelim<'a> {
 }
 
 #[derive(Subdiagnostic)]
-#[multipart_suggestion(suggestion, applicability = "machine-applicable")]
+#[multipart_suggestion(lint_suggestion, applicability = "machine-applicable")]
 pub struct UnusedDelimSuggestion {
     #[suggestion_part(code = "{start_replace}")]
     pub start_span: Span,
diff --git a/compiler/rustc_lint/src/non_fmt_panic.rs b/compiler/rustc_lint/src/non_fmt_panic.rs
index 548f30ec972..5bb1abfd2ec 100644
--- a/compiler/rustc_lint/src/non_fmt_panic.rs
+++ b/compiler/rustc_lint/src/non_fmt_panic.rs
@@ -1,7 +1,7 @@
 use crate::lints::{NonFmtPanicBraces, NonFmtPanicUnused};
-use crate::{LateContext, LateLintPass, LintContext};
+use crate::{fluent_generated as fluent, LateContext, LateLintPass, LintContext};
 use rustc_ast as ast;
-use rustc_errors::{fluent, Applicability};
+use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_middle::lint::in_external_macro;
@@ -122,18 +122,18 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc
     #[allow(rustc::diagnostic_outside_of_impl)]
     cx.struct_span_lint(NON_FMT_PANICS, arg_span, fluent::lint_non_fmt_panic, |lint| {
         lint.set_arg("name", symbol);
-        lint.note(fluent::note);
-        lint.note(fluent::more_info_note);
+        lint.note(fluent::lint_note);
+        lint.note(fluent::lint_more_info_note);
         if !is_arg_inside_call(arg_span, span) {
             // No clue where this argument is coming from.
             return lint;
         }
         if arg_macro.map_or(false, |id| cx.tcx.is_diagnostic_item(sym::format_macro, id)) {
             // A case of `panic!(format!(..))`.
-            lint.note(fluent::supports_fmt_note);
+            lint.note(fluent::lint_supports_fmt_note);
             if let Some((open, close, _)) = find_delimiters(cx, arg_span) {
                 lint.multipart_suggestion(
-                    fluent::supports_fmt_suggestion,
+                    fluent::lint_supports_fmt_suggestion,
                     vec![
                         (arg_span.until(open.shrink_to_hi()), "".into()),
                         (close.until(arg_span.shrink_to_hi()), "".into()),
@@ -179,7 +179,7 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc
             if suggest_display {
                 lint.span_suggestion_verbose(
                     arg_span.shrink_to_lo(),
-                    fluent::display_suggestion,
+                    fluent::lint_display_suggestion,
                     "\"{}\", ",
                     fmt_applicability,
                 );
@@ -187,7 +187,7 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc
                 lint.set_arg("ty", ty);
                 lint.span_suggestion_verbose(
                     arg_span.shrink_to_lo(),
-                    fluent::debug_suggestion,
+                    fluent::lint_debug_suggestion,
                     "\"{:?}\", ",
                     fmt_applicability,
                 );
@@ -197,7 +197,7 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc
                 if let Some((open, close, del)) = find_delimiters(cx, span) {
                     lint.set_arg("already_suggested", suggest_display || suggest_debug);
                     lint.multipart_suggestion(
-                        fluent::panic_suggestion,
+                        fluent::lint_panic_suggestion,
                         if del == '(' {
                             vec![(span.until(open), "std::panic::panic_any".into())]
                         } else {
diff --git a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs
index 42442cfb190..883a56cb3ce 100644
--- a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs
+++ b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs
@@ -149,7 +149,7 @@ impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound {
 struct OpaqueHiddenInferredBoundLint<'tcx> {
     ty: Ty<'tcx>,
     proj_ty: Ty<'tcx>,
-    #[label(specifically)]
+    #[label(lint_specifically)]
     assoc_pred_span: Span,
     #[subdiagnostic]
     add_bound: Option<AddBound<'tcx>>,
diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs
index b7fd6a254d8..a7f2e9f6eb1 100644
--- a/compiler/rustc_lint/src/types.rs
+++ b/compiler/rustc_lint/src/types.rs
@@ -1,14 +1,17 @@
-use crate::lints::{
-    AtomicOrderingFence, AtomicOrderingLoad, AtomicOrderingStore, ImproperCTypes,
-    InvalidAtomicOrderingDiag, OnlyCastu8ToChar, OverflowingBinHex, OverflowingBinHexSign,
-    OverflowingBinHexSub, OverflowingInt, OverflowingIntHelp, OverflowingLiteral, OverflowingUInt,
-    RangeEndpointOutOfRange, UnusedComparisons, VariantSizeDifferencesDiag,
+use crate::{
+    fluent_generated as fluent,
+    lints::{
+        AtomicOrderingFence, AtomicOrderingLoad, AtomicOrderingStore, ImproperCTypes,
+        InvalidAtomicOrderingDiag, OnlyCastu8ToChar, OverflowingBinHex, OverflowingBinHexSign,
+        OverflowingBinHexSub, OverflowingInt, OverflowingIntHelp, OverflowingLiteral,
+        OverflowingUInt, RangeEndpointOutOfRange, UnusedComparisons, VariantSizeDifferencesDiag,
+    },
 };
 use crate::{LateContext, LateLintPass, LintContext};
 use rustc_ast as ast;
 use rustc_attr as attr;
 use rustc_data_structures::fx::FxHashSet;
-use rustc_errors::{fluent, DiagnosticMessage};
+use rustc_errors::DiagnosticMessage;
 use rustc_hir as hir;
 use rustc_hir::{is_range_literal, Expr, ExprKind, Node};
 use rustc_middle::ty::layout::{IntegerExt, LayoutOf, SizeSkeleton};
diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic.rs b/compiler/rustc_macros/src/diagnostics/diagnostic.rs
index 9ff94486404..12a954258d1 100644
--- a/compiler/rustc_macros/src/diagnostics/diagnostic.rs
+++ b/compiler/rustc_macros/src/diagnostics/diagnostic.rs
@@ -57,7 +57,7 @@ impl<'a> DiagnosticDerive<'a> {
                 }
                 Some(slug) => {
                     quote! {
-                        let mut #diag = #handler.struct_diagnostic(rustc_errors::fluent::#slug);
+                        let mut #diag = #handler.struct_diagnostic(crate::fluent_generated::#slug);
                     }
                 }
             };
@@ -149,7 +149,7 @@ impl<'a> LintDiagnosticDerive<'a> {
                 }
                 Some(slug) => {
                     quote! {
-                        rustc_errors::fluent::#slug.into()
+                        crate::fluent_generated::#slug.into()
                     }
                 }
             }
diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs
index 12bcd939bd6..46068f8c868 100644
--- a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs
+++ b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs
@@ -452,7 +452,7 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
                 Ok(quote! {
                     #diag.span_suggestions_with_style(
                         #span_field,
-                        rustc_errors::fluent::#slug,
+                        crate::fluent_generated::#slug,
                         #code_field,
                         #applicability,
                         #style
@@ -476,7 +476,7 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
         quote! {
             #diag.#fn_name(
                 #field_binding,
-                rustc_errors::fluent::#fluent_attr_identifier
+                crate::fluent_generated::#fluent_attr_identifier
             );
         }
     }
@@ -486,7 +486,7 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
     fn add_subdiagnostic(&self, kind: &Ident, fluent_attr_identifier: Path) -> TokenStream {
         let diag = &self.parent.diag;
         quote! {
-            #diag.#kind(rustc_errors::fluent::#fluent_attr_identifier);
+            #diag.#kind(crate::fluent_generated::#fluent_attr_identifier);
         }
     }
 
diff --git a/compiler/rustc_macros/src/diagnostics/fluent.rs b/compiler/rustc_macros/src/diagnostics/fluent.rs
index 08098c9bb2a..38c0f4895db 100644
--- a/compiler/rustc_macros/src/diagnostics/fluent.rs
+++ b/compiler/rustc_macros/src/diagnostics/fluent.rs
@@ -19,52 +19,9 @@ use std::{
     io::Read,
     path::{Path, PathBuf},
 };
-use syn::{
-    parse::{Parse, ParseStream},
-    parse_macro_input,
-    punctuated::Punctuated,
-    token, Ident, LitStr, Result,
-};
+use syn::{parse_macro_input, Ident, LitStr};
 use unic_langid::langid;
 
-struct Resource {
-    krate: Ident,
-    #[allow(dead_code)]
-    fat_arrow_token: token::FatArrow,
-    resource_path: LitStr,
-}
-
-impl Parse for Resource {
-    fn parse(input: ParseStream<'_>) -> Result<Self> {
-        Ok(Resource {
-            krate: input.parse()?,
-            fat_arrow_token: input.parse()?,
-            resource_path: input.parse()?,
-        })
-    }
-}
-
-struct Resources(Punctuated<Resource, token::Comma>);
-
-impl Parse for Resources {
-    fn parse(input: ParseStream<'_>) -> Result<Self> {
-        let mut resources = Punctuated::new();
-        loop {
-            if input.is_empty() || input.peek(token::Brace) {
-                break;
-            }
-            let value = input.parse()?;
-            resources.push_value(value);
-            if !input.peek(token::Comma) {
-                break;
-            }
-            let punct = input.parse()?;
-            resources.push_punct(punct);
-        }
-        Ok(Resources(resources))
-    }
-}
-
 /// Helper function for returning an absolute path for macro-invocation relative file paths.
 ///
 /// If the input is already absolute, then the input is returned. If the input is not absolute,
@@ -84,251 +41,285 @@ fn invocation_relative_path_to_absolute(span: Span, path: &str) -> PathBuf {
     }
 }
 
+/// Tokens to be returned when the macro cannot proceed.
+fn failed(crate_name: &Ident) -> proc_macro::TokenStream {
+    quote! {
+        pub static DEFAULT_LOCALE_RESOURCE: &'static str = "";
+
+        #[allow(non_upper_case_globals)]
+        #[doc(hidden)]
+        pub(crate) mod fluent_generated {
+            pub mod #crate_name {
+            }
+
+            pub mod _subdiag {
+                pub const help: crate::SubdiagnosticMessage =
+                    crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("help"));
+                pub const note: crate::SubdiagnosticMessage =
+                    crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("note"));
+                pub const warn: crate::SubdiagnosticMessage =
+                    crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("warn"));
+                pub const label: crate::SubdiagnosticMessage =
+                    crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("label"));
+                pub const suggestion: crate::SubdiagnosticMessage =
+                    crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("suggestion"));
+            }
+        }
+    }
+    .into()
+}
+
 /// See [rustc_macros::fluent_messages].
 pub(crate) fn fluent_messages(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
-    let resources = parse_macro_input!(input as Resources);
+    let crate_name = std::env::var("CARGO_PKG_NAME")
+        // If `CARGO_PKG_NAME` is missing, then we're probably running in a test, so use
+        // `no_crate`.
+        .unwrap_or_else(|_| "no_crate".to_string())
+        .replace("rustc_", "");
 
     // Cannot iterate over individual messages in a bundle, so do that using the
     // `FluentResource` instead. Construct a bundle anyway to find out if there are conflicting
     // messages in the resources.
     let mut bundle = FluentBundle::new(vec![langid!("en-US")]);
 
-    // Map of Fluent identifiers to the `Span` of the resource that defined them, used for better
-    // diagnostics.
-    let mut previous_defns = HashMap::new();
-
     // Set of Fluent attribute names already output, to avoid duplicate type errors - any given
     // constant created for a given attribute is the same.
     let mut previous_attrs = HashSet::new();
 
-    let mut includes = TokenStream::new();
-    let mut generated = TokenStream::new();
+    let resource_str = parse_macro_input!(input as LitStr);
+    let resource_span = resource_str.span().unwrap();
+    let relative_ftl_path = resource_str.value();
+    let absolute_ftl_path = invocation_relative_path_to_absolute(resource_span, &relative_ftl_path);
 
-    for res in resources.0 {
-        let krate_span = res.krate.span().unwrap();
-        let path_span = res.resource_path.span().unwrap();
+    let crate_name = Ident::new(&crate_name, resource_str.span());
 
-        let relative_ftl_path = res.resource_path.value();
-        let absolute_ftl_path =
-            invocation_relative_path_to_absolute(krate_span, &relative_ftl_path);
-        // As this macro also outputs an `include_str!` for this file, the macro will always be
-        // re-executed when the file changes.
-        let mut resource_file = match File::open(absolute_ftl_path) {
-            Ok(resource_file) => resource_file,
-            Err(e) => {
-                Diagnostic::spanned(path_span, Level::Error, "could not open Fluent resource")
-                    .note(e.to_string())
-                    .emit();
-                continue;
-            }
-        };
-        let mut resource_contents = String::new();
-        if let Err(e) = resource_file.read_to_string(&mut resource_contents) {
-            Diagnostic::spanned(path_span, Level::Error, "could not read Fluent resource")
+    // As this macro also outputs an `include_str!` for this file, the macro will always be
+    // re-executed when the file changes.
+    let mut resource_file = match File::open(absolute_ftl_path) {
+        Ok(resource_file) => resource_file,
+        Err(e) => {
+            Diagnostic::spanned(resource_span, Level::Error, "could not open Fluent resource")
                 .note(e.to_string())
                 .emit();
-            continue;
+            return failed(&crate_name);
         }
-        let resource = match FluentResource::try_new(resource_contents) {
-            Ok(resource) => resource,
-            Err((this, errs)) => {
-                Diagnostic::spanned(path_span, Level::Error, "could not parse Fluent resource")
-                    .help("see additional errors emitted")
-                    .emit();
-                for ParserError { pos, slice: _, kind } in errs {
-                    let mut err = kind.to_string();
-                    // Entirely unnecessary string modification so that the error message starts
-                    // with a lowercase as rustc errors do.
-                    err.replace_range(
-                        0..1,
-                        &err.chars().next().unwrap().to_lowercase().to_string(),
-                    );
+    };
+    let mut resource_contents = String::new();
+    if let Err(e) = resource_file.read_to_string(&mut resource_contents) {
+        Diagnostic::spanned(resource_span, Level::Error, "could not read Fluent resource")
+            .note(e.to_string())
+            .emit();
+        return failed(&crate_name);
+    }
+
+    let resource = match FluentResource::try_new(resource_contents) {
+        Ok(resource) => resource,
+        Err((this, errs)) => {
+            Diagnostic::spanned(resource_span, Level::Error, "could not parse Fluent resource")
+                .help("see additional errors emitted")
+                .emit();
+            for ParserError { pos, slice: _, kind } in errs {
+                let mut err = kind.to_string();
+                // Entirely unnecessary string modification so that the error message starts
+                // with a lowercase as rustc errors do.
+                err.replace_range(0..1, &err.chars().next().unwrap().to_lowercase().to_string());
 
-                    let line_starts: Vec<usize> = std::iter::once(0)
-                        .chain(
-                            this.source()
-                                .char_indices()
-                                .filter_map(|(i, c)| Some(i + 1).filter(|_| c == '\n')),
-                        )
-                        .collect();
-                    let line_start = line_starts
-                        .iter()
-                        .enumerate()
-                        .map(|(line, idx)| (line + 1, idx))
-                        .filter(|(_, idx)| **idx <= pos.start)
-                        .last()
-                        .unwrap()
-                        .0;
+                let line_starts: Vec<usize> = std::iter::once(0)
+                    .chain(
+                        this.source()
+                            .char_indices()
+                            .filter_map(|(i, c)| Some(i + 1).filter(|_| c == '\n')),
+                    )
+                    .collect();
+                let line_start = line_starts
+                    .iter()
+                    .enumerate()
+                    .map(|(line, idx)| (line + 1, idx))
+                    .filter(|(_, idx)| **idx <= pos.start)
+                    .last()
+                    .unwrap()
+                    .0;
 
-                    let snippet = Snippet {
-                        title: Some(Annotation {
-                            label: Some(&err),
-                            id: None,
+                let snippet = Snippet {
+                    title: Some(Annotation {
+                        label: Some(&err),
+                        id: None,
+                        annotation_type: AnnotationType::Error,
+                    }),
+                    footer: vec![],
+                    slices: vec![Slice {
+                        source: this.source(),
+                        line_start,
+                        origin: Some(&relative_ftl_path),
+                        fold: true,
+                        annotations: vec![SourceAnnotation {
+                            label: "",
                             annotation_type: AnnotationType::Error,
-                        }),
-                        footer: vec![],
-                        slices: vec![Slice {
-                            source: this.source(),
-                            line_start,
-                            origin: Some(&relative_ftl_path),
-                            fold: true,
-                            annotations: vec![SourceAnnotation {
-                                label: "",
-                                annotation_type: AnnotationType::Error,
-                                range: (pos.start, pos.end - 1),
-                            }],
+                            range: (pos.start, pos.end - 1),
                         }],
-                        opt: Default::default(),
-                    };
-                    let dl = DisplayList::from(snippet);
-                    eprintln!("{dl}\n");
-                }
-                continue;
+                    }],
+                    opt: Default::default(),
+                };
+                let dl = DisplayList::from(snippet);
+                eprintln!("{dl}\n");
             }
-        };
 
-        let mut constants = TokenStream::new();
-        let mut messagerefs = Vec::new();
-        for entry in resource.entries() {
-            let span = res.krate.span();
-            if let Entry::Message(Message { id: Identifier { name }, attributes, value, .. }) =
-                entry
-            {
-                let _ = previous_defns.entry(name.to_string()).or_insert(path_span);
+            return failed(&crate_name);
+        }
+    };
 
-                if name.contains('-') {
-                    Diagnostic::spanned(
-                        path_span,
-                        Level::Error,
-                        format!("name `{name}` contains a '-' character"),
-                    )
-                    .help("replace any '-'s with '_'s")
-                    .emit();
-                }
+    let mut constants = TokenStream::new();
+    let mut previous_defns = HashMap::new();
+    let mut message_refs = Vec::new();
+    for entry in resource.entries() {
+        if let Entry::Message(Message { id: Identifier { name }, attributes, value, .. }) = entry {
+            let _ = previous_defns.entry(name.to_string()).or_insert(resource_span);
+            if name.contains('-') {
+                Diagnostic::spanned(
+                    resource_span,
+                    Level::Error,
+                    format!("name `{name}` contains a '-' character"),
+                )
+                .help("replace any '-'s with '_'s")
+                .emit();
+            }
 
-                if let Some(Pattern { elements }) = value {
-                    for elt in elements {
-                        if let PatternElement::Placeable {
-                            expression:
-                                Expression::Inline(InlineExpression::MessageReference { id, .. }),
-                        } = elt
-                        {
-                            messagerefs.push((id.name, *name));
-                        }
+            if let Some(Pattern { elements }) = value {
+                for elt in elements {
+                    if let PatternElement::Placeable {
+                        expression:
+                            Expression::Inline(InlineExpression::MessageReference { id, .. }),
+                    } = elt
+                    {
+                        message_refs.push((id.name, *name));
                     }
                 }
+            }
 
-                // Require that the message name starts with the crate name
-                // `hir_typeck_foo_bar` (in `hir_typeck.ftl`)
-                // `const_eval_baz` (in `const_eval.ftl`)
-                // `const-eval-hyphen-having` => `hyphen_having` (in `const_eval.ftl`)
-                // The last case we error about above, but we want to fall back gracefully
-                // so that only the error is being emitted and not also one about the macro
-                // failing.
-                let crate_prefix = format!("{}_", res.krate);
+            // `typeck_foo_bar` => `foo_bar` (in `typeck.ftl`)
+            // `const_eval_baz` => `baz` (in `const_eval.ftl`)
+            // `const-eval-hyphen-having` => `hyphen_having` (in `const_eval.ftl`)
+            // The last case we error about above, but we want to fall back gracefully
+            // so that only the error is being emitted and not also one about the macro
+            // failing.
+            let crate_prefix = format!("{crate_name}_");
 
-                let snake_name = name.replace('-', "_");
-                if !snake_name.starts_with(&crate_prefix) {
+            let snake_name = name.replace('-', "_");
+            if !snake_name.starts_with(&crate_prefix) {
+                Diagnostic::spanned(
+                    resource_span,
+                    Level::Error,
+                    format!("name `{name}` does not start with the crate name"),
+                )
+                .help(format!(
+                    "prepend `{crate_prefix}` to the slug name: `{crate_prefix}{snake_name}`"
+                ))
+                .emit();
+            };
+            let snake_name = Ident::new(&snake_name, resource_str.span());
+
+            if !previous_attrs.insert(snake_name.clone()) {
+                continue;
+            }
+
+            let msg = format!("Constant referring to Fluent message `{name}` from `{crate_name}`");
+            constants.extend(quote! {
+                #[doc = #msg]
+                pub const #snake_name: crate::DiagnosticMessage =
+                    crate::DiagnosticMessage::FluentIdentifier(
+                        std::borrow::Cow::Borrowed(#name),
+                        None
+                    );
+            });
+
+            for Attribute { id: Identifier { name: attr_name }, .. } in attributes {
+                let snake_name = Ident::new(
+                    &format!("{}{}", &crate_prefix, &attr_name.replace('-', "_")),
+                    resource_str.span(),
+                );
+                if !previous_attrs.insert(snake_name.clone()) {
+                    continue;
+                }
+
+                if attr_name.contains('-') {
                     Diagnostic::spanned(
-                        path_span,
+                        resource_span,
                         Level::Error,
-                        format!("name `{name}` does not start with the crate name"),
+                        format!("attribute `{attr_name}` contains a '-' character"),
                     )
-                    .help(format!(
-                        "prepend `{crate_prefix}` to the slug name: `{crate_prefix}{snake_name}`"
-                    ))
+                    .help("replace any '-'s with '_'s")
                     .emit();
-                };
-
-                let snake_name = Ident::new(&snake_name, span);
+                }
 
+                let msg = format!(
+                    "Constant referring to Fluent message `{name}.{attr_name}` from `{crate_name}`"
+                );
                 constants.extend(quote! {
-                    pub const #snake_name: crate::DiagnosticMessage =
-                        crate::DiagnosticMessage::FluentIdentifier(
-                            std::borrow::Cow::Borrowed(#name),
-                            None
+                    #[doc = #msg]
+                    pub const #snake_name: crate::SubdiagnosticMessage =
+                        crate::SubdiagnosticMessage::FluentAttr(
+                            std::borrow::Cow::Borrowed(#attr_name)
                         );
                 });
-
-                for Attribute { id: Identifier { name: attr_name }, .. } in attributes {
-                    let snake_name = Ident::new(&attr_name.replace('-', "_"), span);
-                    if !previous_attrs.insert(snake_name.clone()) {
-                        continue;
-                    }
-
-                    if attr_name.contains('-') {
-                        Diagnostic::spanned(
-                            path_span,
-                            Level::Error,
-                            format!("attribute `{attr_name}` contains a '-' character"),
-                        )
-                        .help("replace any '-'s with '_'s")
-                        .emit();
-                    }
-
-                    constants.extend(quote! {
-                        pub const #snake_name: crate::SubdiagnosticMessage =
-                            crate::SubdiagnosticMessage::FluentAttr(
-                                std::borrow::Cow::Borrowed(#attr_name)
-                            );
-                    });
-                }
             }
         }
+    }
 
-        for (mref, name) in messagerefs.into_iter() {
-            if !previous_defns.contains_key(mref) {
-                Diagnostic::spanned(
-                    path_span,
-                    Level::Error,
-                    format!("referenced message `{mref}` does not exist (in message `{name}`)"),
-                )
-                .help(&format!("you may have meant to use a variable reference (`{{${mref}}}`)"))
-                .emit();
-            }
+    for (mref, name) in message_refs.into_iter() {
+        if !previous_defns.contains_key(mref) {
+            Diagnostic::spanned(
+                resource_span,
+                Level::Error,
+                format!("referenced message `{mref}` does not exist (in message `{name}`)"),
+            )
+            .help(&format!("you may have meant to use a variable reference (`{{${mref}}}`)"))
+            .emit();
         }
+    }
 
-        if let Err(errs) = bundle.add_resource(resource) {
-            for e in errs {
-                match e {
-                    FluentError::Overriding { kind, id } => {
-                        Diagnostic::spanned(
-                            path_span,
-                            Level::Error,
-                            format!("overrides existing {kind}: `{id}`"),
-                        )
-                        .span_help(previous_defns[&id], "previously defined in this resource")
-                        .emit();
-                    }
-                    FluentError::ResolverError(_) | FluentError::ParserError(_) => unreachable!(),
+    if let Err(errs) = bundle.add_resource(resource) {
+        for e in errs {
+            match e {
+                FluentError::Overriding { kind, id } => {
+                    Diagnostic::spanned(
+                        resource_span,
+                        Level::Error,
+                        format!("overrides existing {kind}: `{id}`"),
+                    )
+                    .emit();
                 }
+                FluentError::ResolverError(_) | FluentError::ParserError(_) => unreachable!(),
             }
         }
-
-        includes.extend(quote! { include_str!(#relative_ftl_path), });
-
-        generated.extend(constants);
     }
 
     quote! {
+        /// Raw content of Fluent resource for this crate, generated by `fluent_messages` macro,
+        /// imported by `rustc_driver` to include all crates' resources in one bundle.
+        pub static DEFAULT_LOCALE_RESOURCE: &'static str = include_str!(#relative_ftl_path);
+
         #[allow(non_upper_case_globals)]
         #[doc(hidden)]
-        pub mod fluent_generated {
-            pub static DEFAULT_LOCALE_RESOURCES: &'static [&'static str] = &[
-                #includes
-            ];
-
-            #generated
+        /// Auto-generated constants for type-checked references to Fluent messages.
+        pub(crate) mod fluent_generated {
+            #constants
 
+            /// Constants expected to exist by the diagnostic derive macros to use as default Fluent
+            /// identifiers for different subdiagnostic kinds.
             pub mod _subdiag {
+                /// Default for `#[help]`
                 pub const help: crate::SubdiagnosticMessage =
                     crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("help"));
+                /// Default for `#[note]`
                 pub const note: crate::SubdiagnosticMessage =
                     crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("note"));
+                /// Default for `#[warn]`
                 pub const warn: crate::SubdiagnosticMessage =
                     crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("warn"));
+                /// Default for `#[label]`
                 pub const label: crate::SubdiagnosticMessage =
                     crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("label"));
+                /// Default for `#[suggestion]`
                 pub const suggestion: crate::SubdiagnosticMessage =
                     crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("suggestion"));
             }
diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs
index 906e4c0b0e1..90660fc1f93 100644
--- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs
+++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs
@@ -512,7 +512,9 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
         let mut calls = TokenStream::new();
         for (kind, slug) in kind_slugs {
             let message = format_ident!("__message");
-            calls.extend(quote! { let #message = #f(#diag, rustc_errors::fluent::#slug.into()); });
+            calls.extend(
+                quote! { let #message = #f(#diag, crate::fluent_generated::#slug.into()); },
+            );
 
             let name = format_ident!("{}{}", if span_field.is_some() { "span_" } else { "" }, kind);
             let call = match kind {
diff --git a/compiler/rustc_macros/src/lib.rs b/compiler/rustc_macros/src/lib.rs
index d2cb6ee9f71..737500cc257 100644
--- a/compiler/rustc_macros/src/lib.rs
+++ b/compiler/rustc_macros/src/lib.rs
@@ -61,9 +61,7 @@ pub fn newtype_index(input: TokenStream) -> TokenStream {
 /// For example, given the following invocation of the macro..
 ///
 /// ```ignore (rust)
-/// fluent_messages! {
-///     typeck => "./typeck.ftl",
-/// }
+/// fluent_messages! { "./typeck.ftl" }
 /// ```
 /// ..where `typeck.ftl` has the following contents..
 ///
@@ -77,9 +75,7 @@ pub fn newtype_index(input: TokenStream) -> TokenStream {
 /// will generate the following code:
 ///
 /// ```ignore (rust)
-/// pub static DEFAULT_LOCALE_RESOURCES: &'static [&'static str] = &[
-///     include_str!("./typeck.ftl"),
-/// ];
+/// pub static DEFAULT_LOCALE_RESOURCE: &'static [&'static str] = include_str!("./typeck.ftl");
 ///
 /// mod fluent_generated {
 ///     mod typeck {
diff --git a/compiler/rustc_error_messages/locales/en-US/metadata.ftl b/compiler/rustc_metadata/locales/en-US.ftl
index 79b8b417257..79b8b417257 100644
--- a/compiler/rustc_error_messages/locales/en-US/metadata.ftl
+++ b/compiler/rustc_metadata/locales/en-US.ftl
diff --git a/compiler/rustc_metadata/src/errors.rs b/compiler/rustc_metadata/src/errors.rs
index c32686779fa..51b41b5f6a2 100644
--- a/compiler/rustc_metadata/src/errors.rs
+++ b/compiler/rustc_metadata/src/errors.rs
@@ -9,6 +9,7 @@ use rustc_session::config;
 use rustc_span::{sym, Span, Symbol};
 use rustc_target::spec::{PanicStrategy, TargetTriple};
 
+use crate::fluent_generated as fluent;
 use crate::locator::CrateFlavor;
 
 #[derive(Diagnostic)]
@@ -491,7 +492,7 @@ impl IntoDiagnostic<'_> for MultipleCandidates {
         self,
         handler: &'_ rustc_errors::Handler,
     ) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> {
-        let mut diag = handler.struct_err(rustc_errors::fluent::metadata_multiple_candidates);
+        let mut diag = handler.struct_err(fluent::metadata_multiple_candidates);
         diag.set_arg("crate_name", self.crate_name);
         diag.set_arg("flavor", self.flavor);
         diag.code(error_code!(E0464));
@@ -590,7 +591,7 @@ impl IntoDiagnostic<'_> for InvalidMetadataFiles {
         self,
         handler: &'_ rustc_errors::Handler,
     ) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> {
-        let mut diag = handler.struct_err(rustc_errors::fluent::metadata_invalid_meta_files);
+        let mut diag = handler.struct_err(fluent::metadata_invalid_meta_files);
         diag.set_arg("crate_name", self.crate_name);
         diag.set_arg("add_info", self.add_info);
         diag.code(error_code!(E0786));
@@ -619,7 +620,7 @@ impl IntoDiagnostic<'_> for CannotFindCrate {
         self,
         handler: &'_ rustc_errors::Handler,
     ) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> {
-        let mut diag = handler.struct_err(rustc_errors::fluent::metadata_cannot_find_crate);
+        let mut diag = handler.struct_err(fluent::metadata_cannot_find_crate);
         diag.set_arg("crate_name", self.crate_name);
         diag.set_arg("current_crate", self.current_crate);
         diag.set_arg("add_info", self.add_info);
@@ -630,32 +631,32 @@ impl IntoDiagnostic<'_> for CannotFindCrate {
             && self.locator_triple != TargetTriple::from_triple(config::host_triple())
         {
             if self.missing_core {
-                diag.note(rustc_errors::fluent::metadata_target_not_installed);
+                diag.note(fluent::metadata_target_not_installed);
             } else {
-                diag.note(rustc_errors::fluent::metadata_target_no_std_support);
+                diag.note(fluent::metadata_target_no_std_support);
             }
             // NOTE: this suggests using rustup, even though the user may not have it installed.
             // That's because they could choose to install it; or this may give them a hint which
             // target they need to install from their distro.
             if self.missing_core {
-                diag.help(rustc_errors::fluent::metadata_consider_downloading_target);
+                diag.help(fluent::metadata_consider_downloading_target);
             }
             // Suggest using #![no_std]. #[no_core] is unstable and not really supported anyway.
             // NOTE: this is a dummy span if `extern crate std` was injected by the compiler.
             // If it's not a dummy, that means someone added `extern crate std` explicitly and
             // `#![no_std]` won't help.
             if !self.missing_core && self.span.is_dummy() {
-                diag.note(rustc_errors::fluent::metadata_std_required);
+                diag.note(fluent::metadata_std_required);
             }
             if self.is_nightly_build {
-                diag.help(rustc_errors::fluent::metadata_consider_building_std);
+                diag.help(fluent::metadata_consider_building_std);
             }
         } else if self.crate_name == self.profiler_runtime {
-            diag.note(rustc_errors::fluent::metadata_compiler_missing_profiler);
+            diag.note(fluent::metadata_compiler_missing_profiler);
         } else if self.crate_name.as_str().starts_with("rustc_") {
-            diag.help(rustc_errors::fluent::metadata_install_missing_components);
+            diag.help(fluent::metadata_install_missing_components);
         }
-        diag.span_label(self.span, rustc_errors::fluent::metadata_cant_find_crate);
+        diag.span_label(self.span, fluent::metadata_cant_find_crate);
         diag
     }
 }
diff --git a/compiler/rustc_metadata/src/lib.rs b/compiler/rustc_metadata/src/lib.rs
index 1987f88e6b8..6f6d3731cea 100644
--- a/compiler/rustc_metadata/src/lib.rs
+++ b/compiler/rustc_metadata/src/lib.rs
@@ -30,6 +30,8 @@ extern crate rustc_data_structures;
 extern crate tracing;
 
 pub use rmeta::{provide, provide_extern};
+use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage};
+use rustc_macros::fluent_messages;
 
 mod dependency_format;
 mod foreign_modules;
@@ -44,3 +46,5 @@ pub mod locator;
 pub use fs::{emit_wrapper_file, METADATA_FILENAME};
 pub use native_libs::find_native_static_library;
 pub use rmeta::{encode_metadata, EncodedMetadata, METADATA_HEADER};
+
+fluent_messages! { "../locales/en-US.ftl" }
diff --git a/compiler/rustc_error_messages/locales/en-US/middle.ftl b/compiler/rustc_middle/locales/en-US.ftl
index 4f4e5c6a2c9..4f4e5c6a2c9 100644
--- a/compiler/rustc_error_messages/locales/en-US/middle.ftl
+++ b/compiler/rustc_middle/locales/en-US.ftl
diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs
index e6cd38c0f15..c33b9d84eb0 100644
--- a/compiler/rustc_middle/src/lib.rs
+++ b/compiler/rustc_middle/src/lib.rs
@@ -73,6 +73,9 @@ extern crate tracing;
 #[macro_use]
 extern crate smallvec;
 
+use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage};
+use rustc_macros::fluent_messages;
+
 #[cfg(test)]
 mod tests;
 
@@ -105,3 +108,5 @@ pub mod util {
 
 // Allows macros to refer to this crate as `::rustc_middle`
 extern crate self as rustc_middle;
+
+fluent_messages! { "../locales/en-US.ftl" }
diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs
index a029c1b209d..bdc58611092 100644
--- a/compiler/rustc_middle/src/ty/diagnostics.rs
+++ b/compiler/rustc_middle/src/ty/diagnostics.rs
@@ -612,3 +612,11 @@ impl<'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for MakeSuggestableFolder<'tcx> {
         c.try_super_fold_with(self)
     }
 }
+
+#[derive(Diagnostic)]
+#[diag(middle_const_not_used_in_type_alias)]
+pub(super) struct ConstNotUsedTraitAlias {
+    pub ct: String,
+    #[primary_span]
+    pub span: Span,
+}
diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs
index 993191ee96a..2d92f377041 100644
--- a/compiler/rustc_middle/src/ty/layout.rs
+++ b/compiler/rustc_middle/src/ty/layout.rs
@@ -1,3 +1,4 @@
+use crate::fluent_generated as fluent;
 use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags;
 use crate::ty::normalize_erasing_regions::NormalizationError;
 use crate::ty::{self, ReprOptions, Ty, TyCtxt, TypeVisitable};
@@ -182,16 +183,16 @@ impl IntoDiagnostic<'_, !> for LayoutError<'_> {
         match self {
             LayoutError::Unknown(ty) => {
                 diag.set_arg("ty", ty);
-                diag.set_primary_message(rustc_errors::fluent::middle_unknown_layout);
+                diag.set_primary_message(fluent::middle_unknown_layout);
             }
             LayoutError::SizeOverflow(ty) => {
                 diag.set_arg("ty", ty);
-                diag.set_primary_message(rustc_errors::fluent::middle_values_too_big);
+                diag.set_primary_message(fluent::middle_values_too_big);
             }
             LayoutError::NormalizationFailure(ty, e) => {
                 diag.set_arg("ty", ty);
                 diag.set_arg("failure_ty", e.get_type_for_failure());
-                diag.set_primary_message(rustc_errors::fluent::middle_cannot_be_normalized);
+                diag.set_primary_message(fluent::middle_cannot_be_normalized);
             }
         }
         diag
diff --git a/compiler/rustc_error_messages/locales/en-US/mir_build.ftl b/compiler/rustc_mir_build/locales/en-US.ftl
index f9bda721df3..f9bda721df3 100644
--- a/compiler/rustc_error_messages/locales/en-US/mir_build.ftl
+++ b/compiler/rustc_mir_build/locales/en-US.ftl
diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs
index ced251267d3..c1f6b8b59ce 100644
--- a/compiler/rustc_mir_build/src/errors.rs
+++ b/compiler/rustc_mir_build/src/errors.rs
@@ -1,9 +1,10 @@
-use crate::thir::pattern::deconstruct_pat::DeconstructedPat;
-use crate::thir::pattern::MatchCheckCtxt;
-use rustc_errors::Handler;
+use crate::{
+    fluent_generated as fluent,
+    thir::pattern::{deconstruct_pat::DeconstructedPat, MatchCheckCtxt},
+};
 use rustc_errors::{
     error_code, AddToDiagnostic, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
-    IntoDiagnostic, MultiSpan, SubdiagnosticMessage,
+    Handler, IntoDiagnostic, MultiSpan, SubdiagnosticMessage,
 };
 use rustc_hir::def::Res;
 use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
@@ -358,7 +359,7 @@ impl<'a> IntoDiagnostic<'a> for NonExhaustivePatternsTypeNotEmpty<'_, '_, '_> {
     fn into_diagnostic(self, handler: &'a Handler) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
         let mut diag = handler.struct_span_err_with_code(
             self.span,
-            rustc_errors::fluent::mir_build_non_exhaustive_patterns_type_not_empty,
+            fluent::mir_build_non_exhaustive_patterns_type_not_empty,
             error_code!(E0004),
         );
 
@@ -380,7 +381,7 @@ impl<'a> IntoDiagnostic<'a> for NonExhaustivePatternsTypeNotEmpty<'_, '_, '_> {
             let mut span: MultiSpan = def_span.into();
             span.push_span_label(def_span, "");
 
-            diag.span_note(span, rustc_errors::fluent::def_note);
+            diag.span_note(span, fluent::mir_build_def_note);
         }
 
         let is_variant_list_non_exhaustive = match self.ty.kind() {
@@ -391,14 +392,14 @@ impl<'a> IntoDiagnostic<'a> for NonExhaustivePatternsTypeNotEmpty<'_, '_, '_> {
         };
 
         if is_variant_list_non_exhaustive {
-            diag.note(rustc_errors::fluent::non_exhaustive_type_note);
+            diag.note(fluent::mir_build_non_exhaustive_type_note);
         } else {
-            diag.note(rustc_errors::fluent::type_note);
+            diag.note(fluent::mir_build_type_note);
         }
 
         if let ty::Ref(_, sub_ty, _) = self.ty.kind() {
             if !sub_ty.is_inhabited_from(self.cx.tcx, self.cx.module, self.cx.param_env) {
-                diag.note(rustc_errors::fluent::reference_note);
+                diag.note(fluent::mir_build_reference_note);
             }
         }
 
@@ -424,12 +425,12 @@ impl<'a> IntoDiagnostic<'a> for NonExhaustivePatternsTypeNotEmpty<'_, '_, '_> {
         if let Some((span, sugg)) = suggestion {
             diag.span_suggestion_verbose(
                 span,
-                rustc_errors::fluent::suggestion,
+                fluent::mir_build_suggestion,
                 sugg,
                 Applicability::HasPlaceholders,
             );
         } else {
-            diag.help(rustc_errors::fluent::help);
+            diag.help(fluent::mir_build_help);
         }
 
         diag
@@ -469,7 +470,7 @@ pub struct NonConstPath {
 pub struct UnreachablePattern {
     #[label]
     pub span: Option<Span>,
-    #[label(catchall_label)]
+    #[label(mir_build_catchall_label)]
     pub catchall: Option<Span>,
 }
 
@@ -493,7 +494,7 @@ pub struct LowerRangeBoundMustBeLessThanOrEqualToUpper {
     #[primary_span]
     #[label]
     pub span: Span,
-    #[note(teach_note)]
+    #[note(mir_build_teach_note)]
     pub teach: Option<()>,
 }
 
@@ -585,9 +586,9 @@ pub struct BorrowOfMovedValue<'tcx> {
     #[primary_span]
     pub span: Span,
     #[label]
-    #[label(occurs_because_label)]
+    #[label(mir_build_occurs_because_label)]
     pub binding_span: Span,
-    #[label(value_borrowed_label)]
+    #[label(mir_build_value_borrowed_label)]
     pub conflicts_ref: Vec<Span>,
     pub name: Ident,
     pub ty: Ty<'tcx>,
@@ -708,7 +709,7 @@ pub struct NontrivialStructuralMatch<'tcx> {
 #[diag(mir_build_overlapping_range_endpoints)]
 #[note]
 pub struct OverlappingRangeEndpoints<'tcx> {
-    #[label(range)]
+    #[label(mir_build_range)]
     pub range: Span,
     #[subdiagnostic]
     pub overlap: Vec<Overlap<'tcx>>,
@@ -788,7 +789,7 @@ pub(crate) struct PatternNotCovered<'s, 'tcx> {
     pub interpreted_as_const: Option<InterpretedAsConst>,
     #[subdiagnostic]
     pub adt_defined_here: Option<AdtDefinedHere<'tcx>>,
-    #[note(pattern_ty)]
+    #[note(mir_build_pattern_ty)]
     pub _p: (),
     pub pattern_ty: Ty<'tcx>,
     #[subdiagnostic]
@@ -823,10 +824,10 @@ impl<'tcx> AddToDiagnostic for AdtDefinedHere<'tcx> {
         let mut spans = MultiSpan::from(self.adt_def_span);
 
         for Variant { span } in self.variants {
-            spans.push_span_label(span, rustc_errors::fluent::mir_build_variant_defined_here);
+            spans.push_span_label(span, fluent::mir_build_variant_defined_here);
         }
 
-        diag.span_note(spans, rustc_errors::fluent::mir_build_adt_defined_here);
+        diag.span_note(spans, fluent::mir_build_adt_defined_here);
     }
 }
 
diff --git a/compiler/rustc_mir_build/src/lib.rs b/compiler/rustc_mir_build/src/lib.rs
index 94dae36154c..fbc130501f9 100644
--- a/compiler/rustc_mir_build/src/lib.rs
+++ b/compiler/rustc_mir_build/src/lib.rs
@@ -25,6 +25,11 @@ pub mod thir;
 
 use rustc_middle::ty::query::Providers;
 
+use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage};
+use rustc_macros::fluent_messages;
+
+fluent_messages! { "../locales/en-US.ftl" }
+
 pub fn provide(providers: &mut Providers) {
     providers.check_match = thir::pattern::check_match;
     providers.lit_to_const = thir::constant::lit_to_const;
diff --git a/compiler/rustc_error_messages/locales/en-US/mir_dataflow.ftl b/compiler/rustc_mir_dataflow/locales/en-US.ftl
index 98854152508..98854152508 100644
--- a/compiler/rustc_error_messages/locales/en-US/mir_dataflow.ftl
+++ b/compiler/rustc_mir_dataflow/locales/en-US.ftl
diff --git a/compiler/rustc_mir_dataflow/src/lib.rs b/compiler/rustc_mir_dataflow/src/lib.rs
index 3e382f500af..b1e03faff05 100644
--- a/compiler/rustc_mir_dataflow/src/lib.rs
+++ b/compiler/rustc_mir_dataflow/src/lib.rs
@@ -16,7 +16,9 @@ extern crate tracing;
 extern crate rustc_middle;
 
 use rustc_ast::MetaItem;
+use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage};
 use rustc_hir::def_id::DefId;
+use rustc_macros::fluent_messages;
 use rustc_middle::ty::{self, TyCtxt};
 use rustc_span::symbol::{sym, Symbol};
 
@@ -44,6 +46,8 @@ pub mod storage;
 pub mod un_derefer;
 pub mod value_analysis;
 
+fluent_messages! { "../locales/en-US.ftl" }
+
 pub(crate) mod indexes {
     pub(crate) use super::move_paths::MovePathIndex;
 }
diff --git a/compiler/rustc_error_messages/locales/en-US/monomorphize.ftl b/compiler/rustc_monomorphize/locales/en-US.ftl
index 6cea6a603f3..6cea6a603f3 100644
--- a/compiler/rustc_error_messages/locales/en-US/monomorphize.ftl
+++ b/compiler/rustc_monomorphize/locales/en-US.ftl
diff --git a/compiler/rustc_monomorphize/src/errors.rs b/compiler/rustc_monomorphize/src/errors.rs
index a53bd7e1fef..495a73490a2 100644
--- a/compiler/rustc_monomorphize/src/errors.rs
+++ b/compiler/rustc_monomorphize/src/errors.rs
@@ -1,5 +1,6 @@
 use std::path::PathBuf;
 
+use crate::fluent_generated as fluent;
 use rustc_errors::ErrorGuaranteed;
 use rustc_errors::IntoDiagnostic;
 use rustc_macros::{Diagnostic, LintDiagnostic};
@@ -44,7 +45,7 @@ impl IntoDiagnostic<'_> for UnusedGenericParamsHint {
         self,
         handler: &'_ rustc_errors::Handler,
     ) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> {
-        let mut diag = handler.struct_err(rustc_errors::fluent::monomorphize_unused_generic_params);
+        let mut diag = handler.struct_err(fluent::monomorphize_unused_generic_params);
         diag.set_span(self.span);
         for (span, name) in self.param_spans.into_iter().zip(self.param_names) {
             // FIXME: I can figure out how to do a label with a fluent string with a fixed message,
diff --git a/compiler/rustc_monomorphize/src/lib.rs b/compiler/rustc_monomorphize/src/lib.rs
index f88155e4fc7..f6b791f29c1 100644
--- a/compiler/rustc_monomorphize/src/lib.rs
+++ b/compiler/rustc_monomorphize/src/lib.rs
@@ -9,7 +9,9 @@ extern crate tracing;
 #[macro_use]
 extern crate rustc_middle;
 
+use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage};
 use rustc_hir::lang_items::LangItem;
+use rustc_macros::fluent_messages;
 use rustc_middle::traits;
 use rustc_middle::ty::adjustment::CustomCoerceUnsized;
 use rustc_middle::ty::query::{Providers, TyCtxtAt};
@@ -21,6 +23,8 @@ mod partitioning;
 mod polymorphize;
 mod util;
 
+fluent_messages! { "../locales/en-US.ftl" }
+
 fn custom_coerce_unsize_info<'tcx>(
     tcx: TyCtxtAt<'tcx>,
     source_ty: Ty<'tcx>,
diff --git a/compiler/rustc_error_messages/locales/en-US/parse.ftl b/compiler/rustc_parse/locales/en-US.ftl
index c9cf7b62071..a31b1f6ac1a 100644
--- a/compiler/rustc_error_messages/locales/en-US/parse.ftl
+++ b/compiler/rustc_parse/locales/en-US.ftl
@@ -93,6 +93,26 @@ parse_do_catch_syntax_removed = found removed `do catch` syntax
 parse_float_literal_requires_integer_part = float literals must have an integer part
     .suggestion = must have an integer part
 
+parse_invalid_int_literal_width = invalid width `{$width}` for integer literal
+    .help = valid widths are 8, 16, 32, 64 and 128
+
+parse_invalid_num_literal_base_prefix = invalid base prefix for number literal
+    .note = base prefixes (`0xff`, `0b1010`, `0o755`) are lowercase
+    .suggestion = try making the prefix lowercase
+
+parse_invalid_num_literal_suffix = invalid suffix `{$suffix}` for number literal
+    .label = invalid suffix `{$suffix}`
+    .help = the suffix must be one of the numeric types (`u32`, `isize`, `f32`, etc.)
+
+parse_invalid_float_literal_width = invalid width `{$width}` for float literal
+    .help = valid widths are 32 and 64
+
+parse_invalid_float_literal_suffix = invalid suffix `{$suffix}` for float literal
+    .label = invalid suffix `{$suffix}`
+    .help = valid suffixes are `f32` and `f64`
+
+parse_int_literal_too_large = integer literal is too large
+
 parse_missing_semicolon_before_array = expected `;`, found `[`
     .suggestion = consider adding `;` here
 
@@ -219,6 +239,14 @@ parse_struct_literal_not_allowed_here = struct literals are not allowed here
 
 parse_invalid_interpolated_expression = invalid interpolated expression
 
+parse_hexadecimal_float_literal_not_supported = hexadecimal float literal is not supported
+parse_octal_float_literal_not_supported = octal float literal is not supported
+parse_binary_float_literal_not_supported = binary float literal is not supported
+parse_not_supported = not supported
+
+parse_invalid_literal_suffix = suffixes on {$kind} literals are invalid
+    .label = invalid suffix `{$suffix}`
+
 parse_invalid_literal_suffix_on_tuple_index = suffixes on a tuple index are invalid
     .label = invalid suffix `{$suffix}`
     .tuple_exception_line_1 = `{$suffix}` is *temporarily* accepted on tuple index fields as it was incorrectly accepted on stable for a few releases
diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs
index 63bf864f2a8..c746a870964 100644
--- a/compiler/rustc_parse/src/errors.rs
+++ b/compiler/rustc_parse/src/errors.rs
@@ -2,13 +2,14 @@ use std::borrow::Cow;
 
 use rustc_ast::token::Token;
 use rustc_ast::{Path, Visibility};
-use rustc_errors::{fluent, AddToDiagnostic, Applicability, EmissionGuarantee, IntoDiagnostic};
+use rustc_errors::{AddToDiagnostic, Applicability, EmissionGuarantee, IntoDiagnostic};
 use rustc_macros::{Diagnostic, Subdiagnostic};
 use rustc_session::errors::ExprParenthesesNeeded;
 use rustc_span::edition::{Edition, LATEST_STABLE_EDITION};
 use rustc_span::symbol::Ident;
 use rustc_span::{Span, Symbol};
 
+use crate::fluent_generated as fluent;
 use crate::parser::TokenDescription;
 
 #[derive(Diagnostic)]
@@ -78,7 +79,7 @@ pub(crate) struct IncorrectSemicolon<'a> {
 #[diag(parse_incorrect_use_of_await)]
 pub(crate) struct IncorrectUseOfAwait {
     #[primary_span]
-    #[suggestion(parentheses_suggestion, code = "", applicability = "machine-applicable")]
+    #[suggestion(parse_parentheses_suggestion, code = "", applicability = "machine-applicable")]
     pub span: Span,
 }
 
@@ -87,7 +88,7 @@ pub(crate) struct IncorrectUseOfAwait {
 pub(crate) struct IncorrectAwait {
     #[primary_span]
     pub span: Span,
-    #[suggestion(postfix_suggestion, code = "{expr}.await{question_mark}")]
+    #[suggestion(parse_postfix_suggestion, code = "{expr}.await{question_mark}")]
     pub sugg_span: (Span, Applicability),
     pub expr: String,
     pub question_mark: &'static str,
@@ -140,7 +141,7 @@ pub(crate) struct InvalidComparisonOperator {
 #[derive(Subdiagnostic)]
 pub(crate) enum InvalidComparisonOperatorSub {
     #[suggestion(
-        use_instead,
+        parse_use_instead,
         style = "short",
         applicability = "machine-applicable",
         code = "{correct}"
@@ -151,7 +152,7 @@ pub(crate) enum InvalidComparisonOperatorSub {
         invalid: String,
         correct: String,
     },
-    #[label(spaceship_operator_invalid)]
+    #[label(parse_spaceship_operator_invalid)]
     Spaceship(#[primary_span] Span),
 }
 
@@ -169,14 +170,14 @@ pub(crate) struct InvalidLogicalOperator {
 #[derive(Subdiagnostic)]
 pub(crate) enum InvalidLogicalOperatorSub {
     #[suggestion(
-        use_amp_amp_for_conjunction,
+        parse_use_amp_amp_for_conjunction,
         style = "short",
         applicability = "machine-applicable",
         code = "&&"
     )]
     Conjunction(#[primary_span] Span),
     #[suggestion(
-        use_pipe_pipe_for_disjunction,
+        parse_use_pipe_pipe_for_disjunction,
         style = "short",
         applicability = "machine-applicable",
         code = "||"
@@ -262,14 +263,14 @@ pub(crate) struct UnexpectedTokenAfterLabel {
     #[primary_span]
     #[label(parse_unexpected_token_after_label)]
     pub span: Span,
-    #[suggestion(suggestion_remove_label, style = "verbose", code = "")]
+    #[suggestion(parse_suggestion_remove_label, style = "verbose", code = "")]
     pub remove_label: Option<Span>,
     #[subdiagnostic]
     pub enclose_in_block: Option<UnexpectedTokenAfterLabelSugg>,
 }
 
 #[derive(Subdiagnostic)]
-#[multipart_suggestion(suggestion_enclose_in_block, applicability = "machine-applicable")]
+#[multipart_suggestion(parse_suggestion_enclose_in_block, applicability = "machine-applicable")]
 pub(crate) struct UnexpectedTokenAfterLabelSugg {
     #[suggestion_part(code = "{{ ")]
     pub left: Span,
@@ -347,9 +348,9 @@ pub(crate) struct IfExpressionMissingThenBlock {
 
 #[derive(Subdiagnostic)]
 pub(crate) enum IfExpressionMissingThenBlockSub {
-    #[help(condition_possibly_unfinished)]
+    #[help(parse_condition_possibly_unfinished)]
     UnfinishedCondition(#[primary_span] Span),
-    #[help(add_then_block)]
+    #[help(parse_add_then_block)]
     AddThenBlock(#[primary_span] Span),
 }
 
@@ -364,9 +365,9 @@ pub(crate) struct IfExpressionLetSomeSub {
 #[diag(parse_if_expression_missing_condition)]
 pub(crate) struct IfExpressionMissingCondition {
     #[primary_span]
-    #[label(condition_label)]
+    #[label(parse_condition_label)]
     pub if_span: Span,
-    #[label(block_label)]
+    #[label(parse_block_label)]
     pub block_span: Span,
 }
 
@@ -404,10 +405,10 @@ pub(crate) struct OuterAttributeNotAllowedOnIfElse {
     #[primary_span]
     pub last: Span,
 
-    #[label(branch_label)]
+    #[label(parse_branch_label)]
     pub branch_span: Span,
 
-    #[label(ctx_label)]
+    #[label(parse_ctx_label)]
     pub ctx_span: Span,
     pub ctx: String,
 
@@ -427,9 +428,14 @@ pub(crate) struct MissingInInForLoop {
 #[derive(Subdiagnostic)]
 pub(crate) enum MissingInInForLoopSub {
     // Has been misleading, at least in the past (closed Issue #48492), thus maybe-incorrect
-    #[suggestion(use_in_not_of, style = "short", applicability = "maybe-incorrect", code = "in")]
+    #[suggestion(
+        parse_use_in_not_of,
+        style = "short",
+        applicability = "maybe-incorrect",
+        code = "in"
+    )]
     InNotOf(#[primary_span] Span),
-    #[suggestion(add_in, style = "short", applicability = "maybe-incorrect", code = " in ")]
+    #[suggestion(parse_add_in, style = "short", applicability = "maybe-incorrect", code = " in ")]
     AddIn(#[primary_span] Span),
 }
 
@@ -484,8 +490,8 @@ pub(crate) struct EqFieldInit {
 #[diag(parse_dotdotdot)]
 pub(crate) struct DotDotDot {
     #[primary_span]
-    #[suggestion(suggest_exclusive_range, applicability = "maybe-incorrect", code = "..")]
-    #[suggestion(suggest_inclusive_range, applicability = "maybe-incorrect", code = "..=")]
+    #[suggestion(parse_suggest_exclusive_range, applicability = "maybe-incorrect", code = "..")]
+    #[suggestion(parse_suggest_inclusive_range, applicability = "maybe-incorrect", code = "..=")]
     pub span: Span,
 }
 
@@ -525,10 +531,10 @@ pub(crate) struct UseEmptyBlockNotSemi {
 #[diag(parse_comparison_interpreted_as_generic)]
 pub(crate) struct ComparisonInterpretedAsGeneric {
     #[primary_span]
-    #[label(label_comparison)]
+    #[label(parse_label_comparison)]
     pub comparison: Span,
     pub r#type: Path,
-    #[label(label_args)]
+    #[label(parse_label_args)]
     pub args: Span,
     #[subdiagnostic]
     pub suggestion: ComparisonOrShiftInterpretedAsGenericSugg,
@@ -538,17 +544,17 @@ pub(crate) struct ComparisonInterpretedAsGeneric {
 #[diag(parse_shift_interpreted_as_generic)]
 pub(crate) struct ShiftInterpretedAsGeneric {
     #[primary_span]
-    #[label(label_comparison)]
+    #[label(parse_label_comparison)]
     pub shift: Span,
     pub r#type: Path,
-    #[label(label_args)]
+    #[label(parse_label_args)]
     pub args: Span,
     #[subdiagnostic]
     pub suggestion: ComparisonOrShiftInterpretedAsGenericSugg,
 }
 
 #[derive(Subdiagnostic)]
-#[multipart_suggestion(suggestion, applicability = "machine-applicable")]
+#[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")]
 pub(crate) struct ComparisonOrShiftInterpretedAsGenericSugg {
     #[suggestion_part(code = "(")]
     pub left: Span,
@@ -574,7 +580,7 @@ pub(crate) struct LeadingPlusNotSupported {
     #[label]
     pub span: Span,
     #[suggestion(
-        suggestion_remove_plus,
+        parse_suggestion_remove_plus,
         style = "verbose",
         code = "",
         applicability = "machine-applicable"
@@ -597,7 +603,7 @@ pub(crate) struct ParenthesesWithStructFields {
 }
 
 #[derive(Subdiagnostic)]
-#[multipart_suggestion(suggestion_braces_for_struct, applicability = "maybe-incorrect")]
+#[multipart_suggestion(parse_suggestion_braces_for_struct, applicability = "maybe-incorrect")]
 pub(crate) struct BracesForStructLiteral {
     #[suggestion_part(code = " {{ ")]
     pub first: Span,
@@ -606,7 +612,7 @@ pub(crate) struct BracesForStructLiteral {
 }
 
 #[derive(Subdiagnostic)]
-#[multipart_suggestion(suggestion_no_fields_for_fn, applicability = "maybe-incorrect")]
+#[multipart_suggestion(parse_suggestion_no_fields_for_fn, applicability = "maybe-incorrect")]
 pub(crate) struct NoFieldsForFnCall {
     #[suggestion_part(code = "")]
     pub fields: Vec<Span>,
@@ -643,7 +649,7 @@ pub(crate) struct ArrayBracketsInsteadOfSpaces {
 }
 
 #[derive(Subdiagnostic)]
-#[multipart_suggestion(suggestion, applicability = "maybe-incorrect")]
+#[multipart_suggestion(parse_suggestion, applicability = "maybe-incorrect")]
 pub(crate) struct ArrayBracketsInsteadOfSpacesSugg {
     #[suggestion_part(code = "[")]
     pub left: Span,
@@ -655,9 +661,9 @@ pub(crate) struct ArrayBracketsInsteadOfSpacesSugg {
 #[diag(parse_match_arm_body_without_braces)]
 pub(crate) struct MatchArmBodyWithoutBraces {
     #[primary_span]
-    #[label(label_statements)]
+    #[label(parse_label_statements)]
     pub statements: Span,
-    #[label(label_arrow)]
+    #[label(parse_label_arrow)]
     pub arrow: Span,
     pub num_statements: usize,
     #[subdiagnostic]
@@ -670,7 +676,7 @@ pub(crate) struct MatchArmBodyWithoutBraces {
 pub(crate) struct InclusiveRangeExtraEquals {
     #[primary_span]
     #[suggestion(
-        suggestion_remove_eq,
+        parse_suggestion_remove_eq,
         style = "short",
         code = "..=",
         applicability = "maybe-incorrect"
@@ -695,7 +701,7 @@ pub(crate) struct InclusiveRangeMatchArrow {
 pub(crate) struct InclusiveRangeNoEnd {
     #[primary_span]
     #[suggestion(
-        suggestion_open_range,
+        parse_suggestion_open_range,
         code = "..",
         applicability = "machine-applicable",
         style = "short"
@@ -705,7 +711,7 @@ pub(crate) struct InclusiveRangeNoEnd {
 
 #[derive(Subdiagnostic)]
 pub(crate) enum MatchArmBodyWithoutBracesSugg {
-    #[multipart_suggestion(suggestion_add_braces, applicability = "machine-applicable")]
+    #[multipart_suggestion(parse_suggestion_add_braces, applicability = "machine-applicable")]
     AddBraces {
         #[suggestion_part(code = "{{ ")]
         left: Span,
@@ -713,7 +719,7 @@ pub(crate) enum MatchArmBodyWithoutBracesSugg {
         right: Span,
     },
     #[suggestion(
-        suggestion_use_comma_not_semicolon,
+        parse_suggestion_use_comma_not_semicolon,
         code = ",",
         applicability = "machine-applicable"
     )]
@@ -733,7 +739,7 @@ pub(crate) struct StructLiteralNotAllowedHere {
 }
 
 #[derive(Subdiagnostic)]
-#[multipart_suggestion(suggestion, applicability = "machine-applicable")]
+#[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")]
 pub(crate) struct StructLiteralNotAllowedHereSugg {
     #[suggestion_part(code = "(")]
     pub left: Span,
@@ -755,9 +761,9 @@ pub(crate) struct InvalidLiteralSuffixOnTupleIndex {
     #[label]
     pub span: Span,
     pub suffix: Symbol,
-    #[help(tuple_exception_line_1)]
-    #[help(tuple_exception_line_2)]
-    #[help(tuple_exception_line_3)]
+    #[help(parse_tuple_exception_line_1)]
+    #[help(parse_tuple_exception_line_2)]
+    #[help(parse_tuple_exception_line_3)]
     pub exception: Option<()>,
 }
 
@@ -775,11 +781,11 @@ pub(crate) struct MismatchedClosingDelimiter {
     #[primary_span]
     pub spans: Vec<Span>,
     pub delimiter: String,
-    #[label(label_unmatched)]
+    #[label(parse_label_unmatched)]
     pub unmatched: Span,
-    #[label(label_opening_candidate)]
+    #[label(parse_label_opening_candidate)]
     pub opening_candidate: Option<Span>,
-    #[label(label_unclosed)]
+    #[label(parse_label_unclosed)]
     pub unclosed: Option<Span>,
 }
 
@@ -930,7 +936,7 @@ impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a, G> for ExpectedIdentifier {
         self,
         handler: &'a rustc_errors::Handler,
     ) -> rustc_errors::DiagnosticBuilder<'a, G> {
-        let token_descr = super::parser::TokenDescription::from_token(&self.token);
+        let token_descr = TokenDescription::from_token(&self.token);
 
         let mut diag = handler.struct_diagnostic(match token_descr {
             Some(TokenDescription::ReservedIdentifier) => {
@@ -976,7 +982,7 @@ impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a, G> for ExpectedSemi {
         self,
         handler: &'a rustc_errors::Handler,
     ) -> rustc_errors::DiagnosticBuilder<'a, G> {
-        let token_descr = super::parser::TokenDescription::from_token(&self.token);
+        let token_descr = TokenDescription::from_token(&self.token);
 
         let mut diag = handler.struct_diagnostic(match token_descr {
             Some(TokenDescription::ReservedIdentifier) => {
@@ -1025,7 +1031,7 @@ pub(crate) struct StructLiteralBodyWithoutPath {
 }
 
 #[derive(Subdiagnostic)]
-#[multipart_suggestion(suggestion, applicability = "has-placeholders")]
+#[multipart_suggestion(parse_suggestion, applicability = "has-placeholders")]
 pub(crate) struct StructLiteralBodyWithoutPathSugg {
     #[suggestion_part(code = "{{ SomeStruct ")]
     pub before: Span,
@@ -1043,7 +1049,7 @@ pub(crate) struct StructLiteralNeedingParens {
 }
 
 #[derive(Subdiagnostic)]
-#[multipart_suggestion(suggestion, applicability = "machine-applicable")]
+#[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")]
 pub(crate) struct StructLiteralNeedingParensSugg {
     #[suggestion_part(code = "(")]
     pub before: Span,
@@ -1070,7 +1076,7 @@ pub(crate) struct GenericParamsWithoutAngleBrackets {
 }
 
 #[derive(Subdiagnostic)]
-#[multipart_suggestion(suggestion, applicability = "machine-applicable")]
+#[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")]
 pub(crate) struct GenericParamsWithoutAngleBracketsSugg {
     #[suggestion_part(code = "<")]
     pub left: Span,
@@ -1091,7 +1097,7 @@ pub(crate) struct ComparisonOperatorsCannotBeChained {
     )]
     pub suggest_turbofish: Option<Span>,
     #[help(parse_sugg_turbofish_syntax)]
-    #[help(sugg_parentheses_for_function_args)]
+    #[help(parse_sugg_parentheses_for_function_args)]
     pub help_turbofish: Option<()>,
     #[subdiagnostic]
     pub chaining_sugg: Option<ComparisonOperatorsCannotBeChainedSugg>,
@@ -1100,7 +1106,7 @@ pub(crate) struct ComparisonOperatorsCannotBeChained {
 #[derive(Subdiagnostic)]
 pub(crate) enum ComparisonOperatorsCannotBeChainedSugg {
     #[suggestion(
-        sugg_split_comparison,
+        parse_sugg_split_comparison,
         style = "verbose",
         code = " && {middle_term}",
         applicability = "maybe-incorrect"
@@ -1110,7 +1116,7 @@ pub(crate) enum ComparisonOperatorsCannotBeChainedSugg {
         span: Span,
         middle_term: String,
     },
-    #[multipart_suggestion(sugg_parenthesize, applicability = "maybe-incorrect")]
+    #[multipart_suggestion(parse_sugg_parenthesize, applicability = "maybe-incorrect")]
     Parenthesize {
         #[suggestion_part(code = "(")]
         left: Span,
@@ -1130,7 +1136,7 @@ pub(crate) struct QuestionMarkInType {
 }
 
 #[derive(Subdiagnostic)]
-#[multipart_suggestion(suggestion, applicability = "machine-applicable")]
+#[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")]
 pub(crate) struct QuestionMarkInTypeSugg {
     #[suggestion_part(code = "Option<")]
     pub left: Span,
@@ -1148,7 +1154,7 @@ pub(crate) struct ParenthesesInForHead {
 }
 
 #[derive(Subdiagnostic)]
-#[multipart_suggestion(suggestion, applicability = "machine-applicable")]
+#[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")]
 pub(crate) struct ParenthesesInForHeadSugg {
     #[suggestion_part(code = "{left_snippet}")]
     pub left: Span,
@@ -1208,7 +1214,7 @@ pub(crate) struct ConstGenericWithoutBraces {
 }
 
 #[derive(Subdiagnostic)]
-#[multipart_suggestion(suggestion, applicability = "machine-applicable")]
+#[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")]
 pub(crate) struct ConstGenericWithoutBracesSugg {
     #[suggestion_part(code = "{{ ")]
     pub left: Span,
@@ -1228,7 +1234,7 @@ pub(crate) struct UnexpectedConstParamDeclaration {
 
 #[derive(Subdiagnostic)]
 pub(crate) enum UnexpectedConstParamDeclarationSugg {
-    #[multipart_suggestion(suggestion, applicability = "machine-applicable")]
+    #[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")]
     AddParam {
         #[suggestion_part(code = "<{snippet}>")]
         impl_generics: Span,
@@ -1237,7 +1243,7 @@ pub(crate) enum UnexpectedConstParamDeclarationSugg {
         snippet: String,
         ident: String,
     },
-    #[multipart_suggestion(suggestion, applicability = "machine-applicable")]
+    #[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")]
     AppendParam {
         #[suggestion_part(code = ", {snippet}")]
         impl_generics_end: Span,
@@ -1284,7 +1290,7 @@ pub(crate) struct FnPtrWithGenerics {
 }
 
 #[derive(Subdiagnostic)]
-#[multipart_suggestion(suggestion, applicability = "maybe-incorrect")]
+#[multipart_suggestion(parse_suggestion, applicability = "maybe-incorrect")]
 pub(crate) struct FnPtrWithGenericsSugg {
     #[suggestion_part(code = "{snippet}")]
     pub left: Span,
@@ -1325,16 +1331,16 @@ pub(crate) struct WhereClauseBeforeTupleStructBody {
     #[primary_span]
     #[label]
     pub span: Span,
-    #[label(name_label)]
+    #[label(parse_name_label)]
     pub name: Span,
-    #[label(body_label)]
+    #[label(parse_body_label)]
     pub body: Span,
     #[subdiagnostic]
     pub sugg: Option<WhereClauseBeforeTupleStructBodySugg>,
 }
 
 #[derive(Subdiagnostic)]
-#[multipart_suggestion(suggestion, applicability = "machine-applicable")]
+#[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")]
 pub(crate) struct WhereClauseBeforeTupleStructBodySugg {
     #[suggestion_part(code = "{snippet}")]
     pub left: Span,
@@ -1429,13 +1435,13 @@ pub(crate) enum MissingKeywordForItemDefinition {
 
 #[derive(Subdiagnostic)]
 pub(crate) enum AmbiguousMissingKwForItemSub {
-    #[suggestion(suggestion, applicability = "maybe-incorrect", code = "{snippet}!")]
+    #[suggestion(parse_suggestion, applicability = "maybe-incorrect", code = "{snippet}!")]
     SuggestMacro {
         #[primary_span]
         span: Span,
         snippet: String,
     },
-    #[help(help)]
+    #[help(parse_help)]
     HelpMacro,
 }
 
@@ -1443,9 +1449,9 @@ pub(crate) enum AmbiguousMissingKwForItemSub {
 #[diag(parse_missing_trait_in_trait_impl)]
 pub(crate) struct MissingTraitInTraitImpl {
     #[primary_span]
-    #[suggestion(suggestion_add_trait, code = " Trait ", applicability = "has-placeholders")]
+    #[suggestion(parse_suggestion_add_trait, code = " Trait ", applicability = "has-placeholders")]
     pub span: Span,
-    #[suggestion(suggestion_remove_for, code = "", applicability = "maybe-incorrect")]
+    #[suggestion(parse_suggestion_remove_for, code = "", applicability = "maybe-incorrect")]
     pub for_span: Span,
 }
 
@@ -1505,7 +1511,7 @@ pub(crate) struct ExternCrateNameWithDashes {
 }
 
 #[derive(Subdiagnostic)]
-#[multipart_suggestion(suggestion, applicability = "machine-applicable")]
+#[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")]
 pub(crate) struct ExternCrateNameWithDashesSugg {
     #[suggestion_part(code = "_")]
     pub dashes: Vec<Span>,
@@ -1726,10 +1732,15 @@ pub struct UnknownPrefix<'a> {
 
 #[derive(Subdiagnostic)]
 pub enum UnknownPrefixSugg {
-    #[suggestion(suggestion_br, code = "br", applicability = "maybe-incorrect", style = "verbose")]
+    #[suggestion(
+        parse_suggestion_br,
+        code = "br",
+        applicability = "maybe-incorrect",
+        style = "verbose"
+    )]
     UseBr(#[primary_span] Span),
     #[suggestion(
-        suggestion_whitespace,
+        parse_suggestion_whitespace,
         code = " ",
         applicability = "maybe-incorrect",
         style = "verbose"
@@ -1761,7 +1772,7 @@ pub struct UnknownTokenStart {
 
 #[derive(Subdiagnostic)]
 pub enum TokenSubstitution {
-    #[suggestion(sugg_quotes, code = "{suggestion}", applicability = "maybe-incorrect")]
+    #[suggestion(parse_sugg_quotes, code = "{suggestion}", applicability = "maybe-incorrect")]
     DirectedQuotes {
         #[primary_span]
         span: Span,
@@ -1769,7 +1780,7 @@ pub enum TokenSubstitution {
         ascii_str: &'static str,
         ascii_name: &'static str,
     },
-    #[suggestion(sugg_other, code = "{suggestion}", applicability = "maybe-incorrect")]
+    #[suggestion(parse_sugg_other, code = "{suggestion}", applicability = "maybe-incorrect")]
     Other {
         #[primary_span]
         span: Span,
@@ -1782,13 +1793,13 @@ pub enum TokenSubstitution {
 }
 
 #[derive(Subdiagnostic)]
-#[note(note_repeats)]
+#[note(parse_note_repeats)]
 pub struct UnknownTokenRepeat {
     pub repeats: usize,
 }
 
 #[derive(Subdiagnostic)]
-#[help(help_null)]
+#[help(parse_help_null)]
 pub struct UnknownTokenNull;
 
 #[derive(Diagnostic)]
@@ -1805,7 +1816,7 @@ pub enum UnescapeError {
     EscapeOnlyChar {
         #[primary_span]
         span: Span,
-        #[suggestion(escape, applicability = "machine-applicable", code = "{escaped_sugg}")]
+        #[suggestion(parse_escape, applicability = "machine-applicable", code = "{escaped_sugg}")]
         char_span: Span,
         escaped_sugg: String,
         escaped_msg: String,
@@ -1814,7 +1825,7 @@ pub enum UnescapeError {
     #[diag(parse_bare_cr)]
     BareCr {
         #[primary_span]
-        #[suggestion(escape, applicability = "machine-applicable", code = "\\r")]
+        #[suggestion(parse_escape, applicability = "machine-applicable", code = "\\r")]
         span: Span,
         double_quotes: bool,
     },
@@ -1854,7 +1865,12 @@ pub enum UnescapeError {
         #[primary_span]
         #[label]
         Span,
-        #[suggestion(terminate, code = "}}", applicability = "maybe-incorrect", style = "verbose")]
+        #[suggestion(
+            parse_terminate,
+            code = "}}",
+            applicability = "maybe-incorrect",
+            style = "verbose"
+        )]
         Span,
     ),
     #[diag(parse_no_brace_unicode_escape)]
@@ -1918,20 +1934,24 @@ pub enum UnescapeError {
 
 #[derive(Subdiagnostic)]
 pub enum MoreThanOneCharSugg {
-    #[suggestion(consider_normalized, code = "{normalized}", applicability = "machine-applicable")]
+    #[suggestion(
+        parse_consider_normalized,
+        code = "{normalized}",
+        applicability = "machine-applicable"
+    )]
     NormalizedForm {
         #[primary_span]
         span: Span,
         ch: String,
         normalized: String,
     },
-    #[suggestion(remove_non, code = "{ch}", applicability = "maybe-incorrect")]
+    #[suggestion(parse_remove_non, code = "{ch}", applicability = "maybe-incorrect")]
     RemoveNonPrinting {
         #[primary_span]
         span: Span,
         ch: String,
     },
-    #[suggestion(use_double_quotes, code = "{sugg}", applicability = "machine-applicable")]
+    #[suggestion(parse_use_double_quotes, code = "{sugg}", applicability = "machine-applicable")]
     Quotes {
         #[primary_span]
         span: Span,
@@ -1942,7 +1962,7 @@ pub enum MoreThanOneCharSugg {
 
 #[derive(Subdiagnostic)]
 pub enum MoreThanOneCharNote {
-    #[note(followed_by)]
+    #[note(parse_followed_by)]
     AllCombining {
         #[primary_span]
         span: Span,
@@ -1950,7 +1970,7 @@ pub enum MoreThanOneCharNote {
         len: usize,
         escaped_marks: String,
     },
-    #[note(non_printing)]
+    #[note(parse_non_printing)]
     NonPrinting {
         #[primary_span]
         span: Span,
@@ -1960,13 +1980,13 @@ pub enum MoreThanOneCharNote {
 
 #[derive(Subdiagnostic)]
 pub enum NoBraceUnicodeSub {
-    #[suggestion(use_braces, code = "{suggestion}", applicability = "maybe-incorrect")]
+    #[suggestion(parse_use_braces, code = "{suggestion}", applicability = "maybe-incorrect")]
     Suggestion {
         #[primary_span]
         span: Span,
         suggestion: String,
     },
-    #[help(format_of_unicode)]
+    #[help(parse_format_of_unicode)]
     Help,
 }
 
@@ -2042,9 +2062,9 @@ pub(crate) struct PatternOnWrongSideOfAt {
     #[suggestion(code = "{whole_pat}", applicability = "machine-applicable")]
     pub whole_span: Span,
     pub whole_pat: String,
-    #[label(label_pattern)]
+    #[label(parse_label_pattern)]
     pub pattern: Span,
-    #[label(label_binding)]
+    #[label(parse_label_binding)]
     pub binding: Span,
 }
 
@@ -2054,9 +2074,9 @@ pub(crate) struct PatternOnWrongSideOfAt {
 pub(crate) struct ExpectedBindingLeftOfAt {
     #[primary_span]
     pub whole_span: Span,
-    #[label(label_lhs)]
+    #[label(parse_label_lhs)]
     pub lhs: Span,
-    #[label(label_rhs)]
+    #[label(parse_label_rhs)]
     pub rhs: Span,
 }
 
@@ -2236,7 +2256,7 @@ pub(crate) struct NegativeBoundsNotSupported {
 
 #[derive(Subdiagnostic)]
 #[suggestion(
-    suggestion,
+    parse_suggestion,
     style = "tool-only",
     code = "{fixed}",
     applicability = "machine-applicable"
diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs
index b49a01d75ed..6f37e9758fc 100644
--- a/compiler/rustc_parse/src/lib.rs
+++ b/compiler/rustc_parse/src/lib.rs
@@ -19,6 +19,8 @@ use rustc_ast::{AttrItem, Attribute, MetaItem};
 use rustc_ast_pretty::pprust;
 use rustc_data_structures::sync::Lrc;
 use rustc_errors::{Applicability, Diagnostic, FatalError, Level, PResult};
+use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage};
+use rustc_macros::fluent_messages;
 use rustc_session::parse::ParseSess;
 use rustc_span::{FileName, SourceFile, Span};
 
@@ -34,6 +36,8 @@ pub mod validate_attr;
 
 mod errors;
 
+fluent_messages! { "../locales/en-US.ftl" }
+
 // A bunch of utility functions of the form `parse_<thing>_from_<source>`
 // where <thing> includes crate, expr, item, stmt, tts, and one that
 // uses a HOF to parse anything, and <source> includes file and
diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs
index 9fa657e725c..e3e7c63e344 100644
--- a/compiler/rustc_parse/src/parser/attr.rs
+++ b/compiler/rustc_parse/src/parser/attr.rs
@@ -1,10 +1,11 @@
 use crate::errors::{InvalidMetaItem, SuffixedLiteralInAttribute};
+use crate::fluent_generated as fluent;
 
 use super::{AttrWrapper, Capturing, FnParseMode, ForceCollect, Parser, PathStyle};
 use rustc_ast as ast;
 use rustc_ast::attr;
 use rustc_ast::token::{self, Delimiter, Nonterminal};
-use rustc_errors::{error_code, fluent, Diagnostic, IntoDiagnostic, PResult};
+use rustc_errors::{error_code, Diagnostic, IntoDiagnostic, PResult};
 use rustc_span::{sym, BytePos, Span};
 use std::convert::TryInto;
 use thin_vec::ThinVec;
@@ -68,10 +69,10 @@ impl<'a> Parser<'a> {
                             token::CommentKind::Block => OuterAttributeType::DocBlockComment,
                         },
                     ) {
-                        err.note(fluent::note);
+                        err.note(fluent::parse_note);
                         err.span_suggestion_verbose(
                             replacement_span,
-                            fluent::suggestion,
+                            fluent::parse_suggestion,
                             "",
                             rustc_errors::Applicability::MachineApplicable,
                         );
@@ -175,10 +176,10 @@ impl<'a> Parser<'a> {
             Ok(Some(item)) => {
                 // FIXME(#100717)
                 err.set_arg("item", item.kind.descr());
-                err.span_label(item.span, fluent::label_does_not_annotate_this);
+                err.span_label(item.span, fluent::parse_label_does_not_annotate_this);
                 err.span_suggestion_verbose(
                     replacement_span,
-                    fluent::sugg_change_inner_to_outer,
+                    fluent::parse_sugg_change_inner_to_outer,
                     match attr_type {
                         OuterAttributeType::Attribute => "",
                         OuterAttributeType::DocBlockComment => "*",
@@ -204,8 +205,8 @@ impl<'a> Parser<'a> {
                         attr_sp,
                         fluent::parse_inner_attr_not_permitted_after_outer_doc_comment,
                     );
-                    diag.span_label(attr_sp, fluent::label_attr)
-                        .span_label(prev_doc_comment_span, fluent::label_prev_doc_comment);
+                    diag.span_label(attr_sp, fluent::parse_label_attr)
+                        .span_label(prev_doc_comment_span, fluent::parse_label_prev_doc_comment);
                     diag
                 }
                 Some(InnerAttrForbiddenReason::AfterOuterAttribute { prev_outer_attr_sp }) => {
@@ -213,8 +214,8 @@ impl<'a> Parser<'a> {
                         attr_sp,
                         fluent::parse_inner_attr_not_permitted_after_outer_attr,
                     );
-                    diag.span_label(attr_sp, fluent::label_attr)
-                        .span_label(prev_outer_attr_sp, fluent::label_prev_attr);
+                    diag.span_label(attr_sp, fluent::parse_label_attr)
+                        .span_label(prev_outer_attr_sp, fluent::parse_label_prev_attr);
                     diag
                 }
                 Some(InnerAttrForbiddenReason::InCodeBlock) | None => {
diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs
index 46685dcc45d..d235b8a8176 100644
--- a/compiler/rustc_parse/src/parser/diagnostics.rs
+++ b/compiler/rustc_parse/src/parser/diagnostics.rs
@@ -18,6 +18,7 @@ use crate::errors::{
     UseEqInstead,
 };
 
+use crate::fluent_generated as fluent;
 use crate::lexer::UnmatchedBrace;
 use crate::parser;
 use rustc_ast as ast;
@@ -32,10 +33,9 @@ use rustc_ast::{
 use rustc_ast_pretty::pprust;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::{
-    fluent, Applicability, DiagnosticBuilder, DiagnosticMessage, FatalError, Handler, MultiSpan,
-    PResult,
+    pluralize, Applicability, Diagnostic, DiagnosticBuilder, DiagnosticMessage, ErrorGuaranteed,
+    FatalError, Handler, IntoDiagnostic, MultiSpan, PResult,
 };
-use rustc_errors::{pluralize, Diagnostic, ErrorGuaranteed, IntoDiagnostic};
 use rustc_session::errors::ExprParenthesesNeeded;
 use rustc_span::source_map::Spanned;
 use rustc_span::symbol::{kw, sym, Ident};
diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs
index 94f7031fad2..8e920f1c421 100644
--- a/compiler/rustc_parse/src/parser/pat.rs
+++ b/compiler/rustc_parse/src/parser/pat.rs
@@ -8,6 +8,7 @@ use crate::errors::{
     TrailingVertNotAllowed, UnexpectedLifetimeInPattern, UnexpectedVertVertBeforeFunctionParam,
     UnexpectedVertVertInPattern,
 };
+use crate::fluent_generated as fluent;
 use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole};
 use rustc_ast::mut_visit::{noop_visit_pat, MutVisitor};
 use rustc_ast::ptr::P;
@@ -17,9 +18,7 @@ use rustc_ast::{
     PatField, PatKind, Path, QSelf, RangeEnd, RangeSyntax,
 };
 use rustc_ast_pretty::pprust;
-use rustc_errors::{
-    fluent, Applicability, DiagnosticBuilder, ErrorGuaranteed, IntoDiagnostic, PResult,
-};
+use rustc_errors::{Applicability, DiagnosticBuilder, ErrorGuaranteed, IntoDiagnostic, PResult};
 use rustc_session::errors::ExprParenthesesNeeded;
 use rustc_span::source_map::{respan, Span, Spanned};
 use rustc_span::symbol::{kw, sym, Ident};
diff --git a/compiler/rustc_error_messages/locales/en-US/passes.ftl b/compiler/rustc_passes/locales/en-US.ftl
index 0c7e02912d4..0c7e02912d4 100644
--- a/compiler/rustc_error_messages/locales/en-US/passes.ftl
+++ b/compiler/rustc_passes/locales/en-US.ftl
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index 8ad4a5ef958..bb09dcbdd69 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -4,10 +4,10 @@
 //! conflicts between multiple such attributes attached to the same
 //! item.
 
-use crate::errors;
+use crate::{errors, fluent_generated as fluent};
 use rustc_ast::{ast, AttrStyle, Attribute, LitKind, MetaItemKind, MetaItemLit, NestedMetaItem};
 use rustc_data_structures::fx::FxHashMap;
-use rustc_errors::{fluent, Applicability, IntoDiagnosticArg, MultiSpan};
+use rustc_errors::{Applicability, IntoDiagnosticArg, MultiSpan};
 use rustc_expand::base::resolve_path;
 use rustc_feature::{AttributeDuplicates, AttributeType, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP};
 use rustc_hir as hir;
@@ -935,15 +935,15 @@ impl CheckAttrVisitor<'_> {
                             src.insert(1, '!');
                             err.span_suggestion_verbose(
                                 attr.span,
-                                fluent::suggestion,
+                                fluent::passes_suggestion,
                                 src,
                                 Applicability::MaybeIncorrect,
                             );
                         } else {
-                            err.span_help(attr.span, fluent::help);
+                            err.span_help(attr.span, fluent::passes_help);
                         }
                     }
-                    err.note(fluent::note);
+                    err.note(fluent::passes_note);
                     err
                 },
             );
diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs
index 68b098e3457..82fc3eeff94 100644
--- a/compiler/rustc_passes/src/errors.rs
+++ b/compiler/rustc_passes/src/errors.rs
@@ -3,6 +3,7 @@ use std::{
     path::{Path, PathBuf},
 };
 
+use crate::fluent_generated as fluent;
 use rustc_ast::Label;
 use rustc_errors::{
     error_code, Applicability, DiagnosticSymbolList, ErrorGuaranteed, IntoDiagnostic, MultiSpan,
@@ -261,7 +262,7 @@ pub struct DocKeywordConflict {
 pub struct DocInlineOnlyUse {
     #[label]
     pub attr_span: Span,
-    #[label(not_a_use_item_label)]
+    #[label(passes_not_a_use_item_label)]
     pub item_span: Option<Span>,
 }
 
@@ -300,7 +301,7 @@ pub struct DocTestUnknownAny {
 #[derive(LintDiagnostic)]
 #[diag(passes_doc_test_unknown_spotlight)]
 #[note]
-#[note(no_op_note)]
+#[note(passes_no_op_note)]
 pub struct DocTestUnknownSpotlight {
     pub path: String,
     #[suggestion(style = "short", applicability = "machine-applicable", code = "notable_trait")]
@@ -573,9 +574,9 @@ pub struct DebugVisualizerPlacement {
 
 #[derive(Diagnostic)]
 #[diag(passes_debug_visualizer_invalid)]
-#[note(note_1)]
-#[note(note_2)]
-#[note(note_3)]
+#[note(passes_note_1)]
+#[note(passes_note_2)]
+#[note(passes_note_3)]
 pub struct DebugVisualizerInvalid {
     #[primary_span]
     pub span: Span,
@@ -782,7 +783,7 @@ impl IntoDiagnostic<'_> for InvalidAttrAtCrateLevel {
         self,
         handler: &'_ rustc_errors::Handler,
     ) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> {
-        let mut diag = handler.struct_err(rustc_errors::fluent::passes_invalid_attr_at_crate_level);
+        let mut diag = handler.struct_err(fluent::passes_invalid_attr_at_crate_level);
         diag.set_span(self.span);
         diag.set_arg("name", self.name);
         // Only emit an error with a suggestion if we can create a string out
@@ -791,7 +792,7 @@ impl IntoDiagnostic<'_> for InvalidAttrAtCrateLevel {
             let replacement = src.replace("#!", "#");
             diag.span_suggestion_verbose(
                 self.span,
-                rustc_errors::fluent::suggestion,
+                fluent::passes_suggestion,
                 replacement,
                 rustc_errors::Applicability::MachineApplicable,
             );
@@ -917,17 +918,17 @@ impl<'a> IntoDiagnostic<'_> for BreakNonLoop<'a> {
     ) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> {
         let mut diag = handler.struct_span_err_with_code(
             self.span,
-            rustc_errors::fluent::passes_break_non_loop,
+            fluent::passes_break_non_loop,
             error_code!(E0571),
         );
         diag.set_arg("kind", self.kind);
-        diag.span_label(self.span, rustc_errors::fluent::label);
+        diag.span_label(self.span, fluent::passes_label);
         if let Some(head) = self.head {
-            diag.span_label(head, rustc_errors::fluent::label2);
+            diag.span_label(head, fluent::passes_label2);
         }
         diag.span_suggestion(
             self.span,
-            rustc_errors::fluent::suggestion,
+            fluent::passes_suggestion,
             self.suggestion,
             Applicability::MaybeIncorrect,
         );
@@ -945,7 +946,7 @@ impl<'a> IntoDiagnostic<'_> for BreakNonLoop<'a> {
                 _ => {
                     diag.span_suggestion(
                         self.break_expr_span,
-                        rustc_errors::fluent::break_expr_suggestion,
+                        fluent::passes_break_expr_suggestion,
                         label.ident,
                         Applicability::MaybeIncorrect,
                     );
@@ -962,7 +963,7 @@ pub struct ContinueLabeledBlock {
     #[primary_span]
     #[label]
     pub span: Span,
-    #[label(block_label)]
+    #[label(passes_block_label)]
     pub block_span: Span,
 }
 
@@ -972,7 +973,7 @@ pub struct BreakInsideClosure<'a> {
     #[primary_span]
     #[label]
     pub span: Span,
-    #[label(closure_label)]
+    #[label(passes_closure_label)]
     pub closure_span: Span,
     pub name: &'a str,
 }
@@ -983,7 +984,7 @@ pub struct BreakInsideAsyncBlock<'a> {
     #[primary_span]
     #[label]
     pub span: Span,
-    #[label(async_block_label)]
+    #[label(passes_async_block_label)]
     pub closure_span: Span,
     pub name: &'a str,
 }
@@ -1056,14 +1057,14 @@ impl IntoDiagnostic<'_> for NakedFunctionsAsmBlock {
     ) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> {
         let mut diag = handler.struct_span_err_with_code(
             self.span,
-            rustc_errors::fluent::passes_naked_functions_asm_block,
+            fluent::passes_naked_functions_asm_block,
             error_code!(E0787),
         );
         for span in self.multiple_asms.iter() {
-            diag.span_label(*span, rustc_errors::fluent::label_multiple_asm);
+            diag.span_label(*span, fluent::passes_label_multiple_asm);
         }
         for span in self.non_asms.iter() {
-            diag.span_label(*span, rustc_errors::fluent::label_non_asm);
+            diag.span_label(*span, fluent::passes_label_non_asm);
         }
         diag
     }
@@ -1122,9 +1123,9 @@ pub struct AttrOnlyInFunctions {
 pub struct MultipleRustcMain {
     #[primary_span]
     pub span: Span,
-    #[label(first)]
+    #[label(passes_first)]
     pub first: Span,
-    #[label(additional)]
+    #[label(passes_additional)]
     pub additional: Span,
 }
 
@@ -1135,7 +1136,7 @@ pub struct MultipleStartFunctions {
     pub span: Span,
     #[label]
     pub labeled: Span,
-    #[label(previous)]
+    #[label(passes_previous)]
     pub previous: Span,
 }
 
@@ -1180,7 +1181,7 @@ impl<'a> IntoDiagnostic<'a> for NoMainErr {
     ) -> rustc_errors::DiagnosticBuilder<'a, ErrorGuaranteed> {
         let mut diag = handler.struct_span_err_with_code(
             DUMMY_SP,
-            rustc_errors::fluent::passes_no_main_function,
+            fluent::passes_no_main_function,
             error_code!(E0601),
         );
         diag.set_arg("crate_name", self.crate_name);
@@ -1188,16 +1189,16 @@ impl<'a> IntoDiagnostic<'a> for NoMainErr {
         diag.set_arg("has_filename", self.has_filename);
         let note = if !self.non_main_fns.is_empty() {
             for &span in &self.non_main_fns {
-                diag.span_note(span, rustc_errors::fluent::here_is_main);
+                diag.span_note(span, fluent::passes_here_is_main);
             }
-            diag.note(rustc_errors::fluent::one_or_more_possible_main);
-            diag.help(rustc_errors::fluent::consider_moving_main);
+            diag.note(fluent::passes_one_or_more_possible_main);
+            diag.help(fluent::passes_consider_moving_main);
             // There were some functions named `main` though. Try to give the user a hint.
-            rustc_errors::fluent::main_must_be_defined_at_crate
+            fluent::passes_main_must_be_defined_at_crate
         } else if self.has_filename {
-            rustc_errors::fluent::consider_adding_main_to_file
+            fluent::passes_consider_adding_main_to_file
         } else {
-            rustc_errors::fluent::consider_adding_main_at_crate
+            fluent::passes_consider_adding_main_at_crate
         };
         if self.file_empty {
             diag.note(note);
@@ -1208,11 +1209,11 @@ impl<'a> IntoDiagnostic<'a> for NoMainErr {
 
         if let Some(main_def) = self.main_def_opt && main_def.opt_fn_def_id().is_none(){
             // There is something at `crate::main`, but it is not a function definition.
-            diag.span_label(main_def.span, rustc_errors::fluent::non_function_main);
+            diag.span_label(main_def.span, fluent::passes_non_function_main);
         }
 
         if self.add_teach_note {
-            diag.note(rustc_errors::fluent::teach_note);
+            diag.note(fluent::passes_teach_note);
         }
         diag
     }
@@ -1241,12 +1242,9 @@ impl IntoDiagnostic<'_> for DuplicateLangItem {
     ) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> {
         let mut diag = handler.struct_err_with_code(
             match self.duplicate {
-                Duplicate::Plain => rustc_errors::fluent::passes_duplicate_lang_item,
-
-                Duplicate::Crate => rustc_errors::fluent::passes_duplicate_lang_item_crate,
-                Duplicate::CrateDepends => {
-                    rustc_errors::fluent::passes_duplicate_lang_item_crate_depends
-                }
+                Duplicate::Plain => fluent::passes_duplicate_lang_item,
+                Duplicate::Crate => fluent::passes_duplicate_lang_item_crate,
+                Duplicate::CrateDepends => fluent::passes_duplicate_lang_item_crate_depends,
             },
             error_code!(E0152),
         );
@@ -1261,24 +1259,24 @@ impl IntoDiagnostic<'_> for DuplicateLangItem {
             diag.set_span(span);
         }
         if let Some(span) = self.first_defined_span {
-            diag.span_note(span, rustc_errors::fluent::first_defined_span);
+            diag.span_note(span, fluent::passes_first_defined_span);
         } else {
             if self.orig_dependency_of.is_empty() {
-                diag.note(rustc_errors::fluent::first_defined_crate);
+                diag.note(fluent::passes_first_defined_crate);
             } else {
-                diag.note(rustc_errors::fluent::first_defined_crate_depends);
+                diag.note(fluent::passes_first_defined_crate_depends);
             }
 
             if self.orig_is_local {
-                diag.note(rustc_errors::fluent::first_definition_local);
+                diag.note(fluent::passes_first_definition_local);
             } else {
-                diag.note(rustc_errors::fluent::first_definition_path);
+                diag.note(fluent::passes_first_definition_path);
             }
 
             if self.is_local {
-                diag.note(rustc_errors::fluent::second_definition_local);
+                diag.note(fluent::passes_second_definition_local);
             } else {
-                diag.note(rustc_errors::fluent::second_definition_path);
+                diag.note(fluent::passes_second_definition_path);
             }
         }
         diag
@@ -1389,7 +1387,7 @@ pub struct UselessStability {
     #[primary_span]
     #[label]
     pub span: Span,
-    #[label(item)]
+    #[label(passes_item)]
     pub item_sp: Span,
 }
 
@@ -1399,7 +1397,7 @@ pub struct InvalidStability {
     #[primary_span]
     #[label]
     pub span: Span,
-    #[label(item)]
+    #[label(passes_item)]
     pub item_sp: Span,
 }
 
@@ -1409,7 +1407,7 @@ pub struct CannotStabilizeDeprecated {
     #[primary_span]
     #[label]
     pub span: Span,
-    #[label(item)]
+    #[label(passes_item)]
     pub item_sp: Span,
 }
 
@@ -1419,7 +1417,7 @@ pub struct InvalidDeprecationVersion {
     #[primary_span]
     #[label]
     pub span: Span,
-    #[label(item)]
+    #[label(passes_item)]
     pub item_sp: Span,
 }
 
diff --git a/compiler/rustc_passes/src/lib.rs b/compiler/rustc_passes/src/lib.rs
index 6e621b7eb5e..0cb8424082c 100644
--- a/compiler/rustc_passes/src/lib.rs
+++ b/compiler/rustc_passes/src/lib.rs
@@ -18,6 +18,8 @@ extern crate rustc_middle;
 #[macro_use]
 extern crate tracing;
 
+use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage};
+use rustc_macros::fluent_messages;
 use rustc_middle::ty::query::Providers;
 
 mod check_attr;
@@ -40,6 +42,8 @@ pub mod stability;
 mod upvars;
 mod weak_lang_items;
 
+fluent_messages! { "../locales/en-US.ftl" }
+
 pub fn provide(providers: &mut Providers) {
     check_attr::provide(providers);
     check_const::provide(providers);
diff --git a/compiler/rustc_error_messages/locales/en-US/plugin_impl.ftl b/compiler/rustc_plugin_impl/locales/en-US.ftl
index 8db32a42c1d..8db32a42c1d 100644
--- a/compiler/rustc_error_messages/locales/en-US/plugin_impl.ftl
+++ b/compiler/rustc_plugin_impl/locales/en-US.ftl
diff --git a/compiler/rustc_plugin_impl/src/lib.rs b/compiler/rustc_plugin_impl/src/lib.rs
index 9ac27c65da8..3f03eef9ee3 100644
--- a/compiler/rustc_plugin_impl/src/lib.rs
+++ b/compiler/rustc_plugin_impl/src/lib.rs
@@ -11,11 +11,15 @@
 #![deny(rustc::untranslatable_diagnostic)]
 #![deny(rustc::diagnostic_outside_of_impl)]
 
+use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage};
 use rustc_lint::LintStore;
+use rustc_macros::fluent_messages;
 
 mod errors;
 pub mod load;
 
+fluent_messages! { "../locales/en-US.ftl" }
+
 /// Structure used to register plugins.
 ///
 /// A plugin registrar function takes an `&mut Registry` and should call
diff --git a/compiler/rustc_error_messages/locales/en-US/privacy.ftl b/compiler/rustc_privacy/locales/en-US.ftl
index a26d1b2b381..a26d1b2b381 100644
--- a/compiler/rustc_error_messages/locales/en-US/privacy.ftl
+++ b/compiler/rustc_privacy/locales/en-US.ftl
diff --git a/compiler/rustc_privacy/src/errors.rs b/compiler/rustc_privacy/src/errors.rs
index a6c95f1a815..72b53eefa08 100644
--- a/compiler/rustc_privacy/src/errors.rs
+++ b/compiler/rustc_privacy/src/errors.rs
@@ -57,7 +57,7 @@ pub struct InPublicInterfaceTraits<'a> {
     pub vis_descr: &'static str,
     pub kind: &'a str,
     pub descr: DiagnosticArgFromDisplay<'a>,
-    #[label(visibility_label)]
+    #[label(privacy_visibility_label)]
     pub vis_span: Span,
 }
 
@@ -71,7 +71,7 @@ pub struct InPublicInterface<'a> {
     pub vis_descr: &'static str,
     pub kind: &'a str,
     pub descr: DiagnosticArgFromDisplay<'a>,
-    #[label(visibility_label)]
+    #[label(privacy_visibility_label)]
     pub vis_span: Span,
 }
 
diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs
index 58dfca75c65..9e856bb8a08 100644
--- a/compiler/rustc_privacy/src/lib.rs
+++ b/compiler/rustc_privacy/src/lib.rs
@@ -16,11 +16,13 @@ use rustc_ast::MacroDef;
 use rustc_attr as attr;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_data_structures::intern::Interned;
+use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage};
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID};
 use rustc_hir::intravisit::{self, Visitor};
 use rustc_hir::{AssocItemKind, HirIdSet, ItemId, Node, PatKind};
+use rustc_macros::fluent_messages;
 use rustc_middle::bug;
 use rustc_middle::hir::nested_filter;
 use rustc_middle::middle::privacy::{EffectiveVisibilities, Level};
@@ -44,6 +46,8 @@ use errors::{
     UnnamedItemIsPrivate,
 };
 
+fluent_messages! { "../locales/en-US.ftl" }
+
 ////////////////////////////////////////////////////////////////////////////////
 /// Generic infrastructure used to implement specific visitors below.
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/compiler/rustc_error_messages/locales/en-US/query_system.ftl b/compiler/rustc_query_system/locales/en-US.ftl
index 870e824039c..870e824039c 100644
--- a/compiler/rustc_error_messages/locales/en-US/query_system.ftl
+++ b/compiler/rustc_query_system/locales/en-US.ftl
diff --git a/compiler/rustc_query_system/src/lib.rs b/compiler/rustc_query_system/src/lib.rs
index 623be668464..6cc4c9a7e1e 100644
--- a/compiler/rustc_query_system/src/lib.rs
+++ b/compiler/rustc_query_system/src/lib.rs
@@ -15,6 +15,9 @@ extern crate rustc_data_structures;
 #[macro_use]
 extern crate rustc_macros;
 
+use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage};
+use rustc_macros::fluent_messages;
+
 pub mod cache;
 pub mod dep_graph;
 mod error;
@@ -26,3 +29,5 @@ pub use error::HandleCycleError;
 pub use error::LayoutOfDepth;
 pub use error::QueryOverflow;
 pub use values::Value;
+
+fluent_messages! { "../locales/en-US.ftl" }
diff --git a/compiler/rustc_error_messages/locales/en-US/resolve.ftl b/compiler/rustc_resolve/locales/en-US.ftl
index 817bb83ed78..817bb83ed78 100644
--- a/compiler/rustc_error_messages/locales/en-US/resolve.ftl
+++ b/compiler/rustc_resolve/locales/en-US.ftl
diff --git a/compiler/rustc_resolve/src/errors.rs b/compiler/rustc_resolve/src/errors.rs
index 2c442774667..867363f4246 100644
--- a/compiler/rustc_resolve/src/errors.rs
+++ b/compiler/rustc_resolve/src/errors.rs
@@ -38,7 +38,7 @@ pub(crate) struct NameAlreadyUsedInParameterList {
     #[primary_span]
     #[label]
     pub(crate) span: Span,
-    #[label(first_use_of_name)]
+    #[label(resolve_first_use_of_name)]
     pub(crate) first_use_span: Span,
     pub(crate) name: Symbol,
 }
@@ -121,7 +121,7 @@ pub(crate) struct VariableBoundWithDifferentMode {
     #[primary_span]
     #[label]
     pub(crate) span: Span,
-    #[label(first_binding_span)]
+    #[label(resolve_first_binding_span)]
     pub(crate) first_binding_span: Span,
     pub(crate) variable_name: Symbol,
 }
@@ -293,7 +293,7 @@ pub(crate) struct BindingShadowsSomethingUnacceptable<'a> {
     pub(crate) article: &'a str,
     #[subdiagnostic]
     pub(crate) sub_suggestion: Option<BindingShadowsSomethingUnacceptableSuggestion>,
-    #[label(label_shadowed_binding)]
+    #[label(resolve_label_shadowed_binding)]
     pub(crate) shadowed_binding_span: Span,
     pub(crate) participle: &'a str,
     pub(crate) name: Symbol,
@@ -369,7 +369,7 @@ pub(crate) struct UnreachableLabel {
     #[label]
     pub(crate) span: Span,
     pub(crate) name: Symbol,
-    #[label(label_definition_span)]
+    #[label(resolve_label_definition_span)]
     pub(crate) definition_span: Span,
     #[subdiagnostic]
     pub(crate) sub_suggestion: Option<UnreachableLabelSubSuggestion>,
@@ -413,7 +413,7 @@ pub(crate) struct TraitImplMismatch {
     pub(crate) span: Span,
     pub(crate) name: Symbol,
     pub(crate) kind: String,
-    #[label(label_trait_item)]
+    #[label(resolve_label_trait_item)]
     pub(crate) trait_item_span: Span,
     pub(crate) trait_path: String,
     pub(crate) code: String,
@@ -434,9 +434,9 @@ pub(crate) struct TraitImplDuplicate {
     #[primary_span]
     #[label]
     pub(crate) span: Span,
-    #[label(old_span_label)]
+    #[label(resolve_old_span_label)]
     pub(crate) old_span: Span,
-    #[label(trait_item_span)]
+    #[label(resolve_trait_item_span)]
     pub(crate) trait_item_span: Span,
     pub(crate) name: Symbol,
 }
diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs
index 9fd7edeb5a1..237ddd0f761 100644
--- a/compiler/rustc_resolve/src/lib.rs
+++ b/compiler/rustc_resolve/src/lib.rs
@@ -28,7 +28,9 @@ use rustc_ast::{AngleBracketedArg, Crate, Expr, ExprKind, GenericArg, GenericArg
 use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet};
 use rustc_data_structures::intern::Interned;
 use rustc_data_structures::sync::{Lrc, MappedReadGuard};
-use rustc_errors::{Applicability, DiagnosticBuilder, ErrorGuaranteed};
+use rustc_errors::{
+    Applicability, DiagnosticBuilder, DiagnosticMessage, ErrorGuaranteed, SubdiagnosticMessage,
+};
 use rustc_expand::base::{DeriveResolutions, SyntaxExtension, SyntaxExtensionKind};
 use rustc_hir::def::Namespace::{self, *};
 use rustc_hir::def::{self, CtorOf, DefKind, DocLinkResMap, LifetimeRes, PartialRes, PerNS};
@@ -37,6 +39,7 @@ use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE};
 use rustc_hir::definitions::DefPathData;
 use rustc_hir::TraitCandidate;
 use rustc_index::vec::IndexVec;
+use rustc_macros::fluent_messages;
 use rustc_metadata::creader::{CStore, CrateLoader};
 use rustc_middle::metadata::ModChild;
 use rustc_middle::middle::privacy::EffectiveVisibilities;
@@ -77,6 +80,8 @@ mod late;
 mod macros;
 pub mod rustdoc;
 
+fluent_messages! { "../locales/en-US.ftl" }
+
 enum Weak {
     Yes,
     No,
diff --git a/compiler/rustc_error_messages/locales/en-US/session.ftl b/compiler/rustc_session/locales/en-US.ftl
index fe553edab42..fe553edab42 100644
--- a/compiler/rustc_error_messages/locales/en-US/session.ftl
+++ b/compiler/rustc_session/locales/en-US.ftl
diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs
index 39e871f532c..e1f1a5f6d2e 100644
--- a/compiler/rustc_session/src/lib.rs
+++ b/compiler/rustc_session/src/lib.rs
@@ -18,6 +18,9 @@ pub mod errors;
 #[macro_use]
 extern crate tracing;
 
+use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage};
+use rustc_macros::fluent_messages;
+
 pub mod cgu_reuse_tracker;
 pub mod utils;
 pub use lint::{declare_lint, declare_lint_pass, declare_tool_lint, impl_lint_pass};
@@ -39,6 +42,8 @@ pub mod output;
 
 pub use getopts;
 
+fluent_messages! { "../locales/en-US.ftl" }
+
 /// Requirements for a `StableHashingContext` to be used in this crate.
 /// This is a hack to allow using the `HashStable_Generic` derive macro
 /// instead of implementing everything in `rustc_middle`.
diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs
index cbdcc5581e5..4e8c3f73e29 100644
--- a/compiler/rustc_session/src/parse.rs
+++ b/compiler/rustc_session/src/parse.rs
@@ -226,8 +226,8 @@ pub struct ParseSess {
 
 impl ParseSess {
     /// Used for testing.
-    pub fn new(file_path_mapping: FilePathMapping) -> Self {
-        let fallback_bundle = fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false);
+    pub fn new(locale_resources: Vec<&'static str>, file_path_mapping: FilePathMapping) -> Self {
+        let fallback_bundle = fallback_fluent_bundle(locale_resources, false);
         let sm = Lrc::new(SourceMap::new(file_path_mapping));
         let handler = Handler::with_tty_emitter(
             ColorConfig::Auto,
@@ -265,7 +265,7 @@ impl ParseSess {
     }
 
     pub fn with_silent_emitter(fatal_note: Option<String>) -> Self {
-        let fallback_bundle = fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false);
+        let fallback_bundle = fallback_fluent_bundle(Vec::new(), false);
         let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
         let fatal_handler =
             Handler::with_tty_emitter(ColorConfig::Auto, false, None, None, None, fallback_bundle);
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index 3dc09854b3c..446ba63ed1c 100644
--- a/compiler/rustc_session/src/session.rs
+++ b/compiler/rustc_session/src/session.rs
@@ -1341,6 +1341,7 @@ pub fn build_session(
     io: CompilerIO,
     bundle: Option<Lrc<rustc_errors::FluentBundle>>,
     registry: rustc_errors::registry::Registry,
+    fluent_resources: Vec<&'static str>,
     driver_lint_caps: FxHashMap<lint::LintId, lint::Level>,
     file_loader: Option<Box<dyn FileLoader + Send + Sync + 'static>>,
     target_override: Option<Target>,
@@ -1385,7 +1386,7 @@ pub fn build_session(
     ));
 
     let fallback_bundle = fallback_fluent_bundle(
-        rustc_errors::DEFAULT_LOCALE_RESOURCES,
+        fluent_resources,
         sopts.unstable_opts.translate_directionality_markers,
     );
     let emitter = default_emitter(&sopts, registry, source_map.clone(), bundle, fallback_bundle);
@@ -1630,7 +1631,10 @@ pub enum IncrCompSession {
 }
 
 fn early_error_handler(output: config::ErrorOutputType) -> rustc_errors::Handler {
-    let fallback_bundle = fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false);
+    // FIXME(#100717): early errors aren't translated at the moment, so this is fine, but it will
+    // need to reference every crate that might emit an early error for translation to work.
+    let fallback_bundle =
+        fallback_fluent_bundle(vec![rustc_errors::DEFAULT_LOCALE_RESOURCE], false);
     let emitter: Box<dyn Emitter + sync::Send> = match output {
         config::ErrorOutputType::HumanReadable(kind) => {
             let (short, color_config) = kind.unzip();
diff --git a/compiler/rustc_error_messages/locales/en-US/symbol_mangling.ftl b/compiler/rustc_symbol_mangling/locales/en-US.ftl
index b7d48280f46..b7d48280f46 100644
--- a/compiler/rustc_error_messages/locales/en-US/symbol_mangling.ftl
+++ b/compiler/rustc_symbol_mangling/locales/en-US.ftl
diff --git a/compiler/rustc_symbol_mangling/src/lib.rs b/compiler/rustc_symbol_mangling/src/lib.rs
index d81722e59a6..d9ce7373483 100644
--- a/compiler/rustc_symbol_mangling/src/lib.rs
+++ b/compiler/rustc_symbol_mangling/src/lib.rs
@@ -100,8 +100,10 @@ extern crate rustc_middle;
 #[macro_use]
 extern crate tracing;
 
+use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage};
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
+use rustc_macros::fluent_messages;
 use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
 use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs;
 use rustc_middle::mir::mono::{InstantiationMode, MonoItem};
@@ -117,6 +119,8 @@ pub mod errors;
 pub mod test;
 pub mod typeid;
 
+fluent_messages! { "../locales/en-US.ftl" }
+
 /// This function computes the symbol name for the given `instance` and the
 /// given instantiating crate. That is, if you know that instance X is
 /// instantiated in crate Y, this is the symbol name this instance would have.
diff --git a/compiler/rustc_error_messages/locales/en-US/trait_selection.ftl b/compiler/rustc_trait_selection/locales/en-US.ftl
index 14eb4a5502d..14eb4a5502d 100644
--- a/compiler/rustc_error_messages/locales/en-US/trait_selection.ftl
+++ b/compiler/rustc_trait_selection/locales/en-US.ftl
diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs
index 4405537c645..df7c4df1868 100644
--- a/compiler/rustc_trait_selection/src/errors.rs
+++ b/compiler/rustc_trait_selection/src/errors.rs
@@ -1,4 +1,5 @@
-use rustc_errors::{fluent, ErrorGuaranteed, Handler, IntoDiagnostic};
+use crate::fluent_generated as fluent;
+use rustc_errors::{ErrorGuaranteed, Handler, IntoDiagnostic};
 use rustc_macros::Diagnostic;
 use rustc_middle::ty::{self, PolyTraitRef, Ty};
 use rustc_span::{Span, Symbol};
@@ -69,19 +70,19 @@ impl IntoDiagnostic<'_> for NegativePositiveConflict<'_> {
         diag.code(rustc_errors::error_code!(E0751));
         match self.negative_impl_span {
             Ok(span) => {
-                diag.span_label(span, fluent::negative_implementation_here);
+                diag.span_label(span, fluent::trait_selection_negative_implementation_here);
             }
             Err(cname) => {
-                diag.note(fluent::negative_implementation_in_crate);
+                diag.note(fluent::trait_selection_negative_implementation_in_crate);
                 diag.set_arg("negative_impl_cname", cname.to_string());
             }
         }
         match self.positive_impl_span {
             Ok(span) => {
-                diag.span_label(span, fluent::positive_implementation_here);
+                diag.span_label(span, fluent::trait_selection_positive_implementation_here);
             }
             Err(cname) => {
-                diag.note(fluent::positive_implementation_in_crate);
+                diag.note(fluent::trait_selection_positive_implementation_in_crate);
                 diag.set_arg("positive_impl_cname", cname.to_string());
             }
         }
diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs
index 6fa09410363..548b42cef43 100644
--- a/compiler/rustc_trait_selection/src/lib.rs
+++ b/compiler/rustc_trait_selection/src/lib.rs
@@ -36,7 +36,12 @@ extern crate rustc_middle;
 #[macro_use]
 extern crate smallvec;
 
+use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage};
+use rustc_macros::fluent_messages;
+
 pub mod errors;
 pub mod infer;
 pub mod solve;
 pub mod traits;
+
+fluent_messages! { "../locales/en-US.ftl" }
diff --git a/compiler/rustc_error_messages/locales/en-US/ty_utils.ftl b/compiler/rustc_ty_utils/locales/en-US.ftl
index abe65a0e3fe..abe65a0e3fe 100644
--- a/compiler/rustc_error_messages/locales/en-US/ty_utils.ftl
+++ b/compiler/rustc_ty_utils/locales/en-US.ftl
diff --git a/compiler/rustc_ty_utils/src/errors.rs b/compiler/rustc_ty_utils/src/errors.rs
index c05eeb353a8..ab3e62f0484 100644
--- a/compiler/rustc_ty_utils/src/errors.rs
+++ b/compiler/rustc_ty_utils/src/errors.rs
@@ -16,7 +16,7 @@ pub struct NeedsDropOverflow<'tcx> {
 pub struct GenericConstantTooComplex {
     #[primary_span]
     pub span: Span,
-    #[note(maybe_supported)]
+    #[note(ty_utils_maybe_supported)]
     pub maybe_supported: Option<()>,
     #[subdiagnostic]
     pub sub: GenericConstantTooComplexSub,
diff --git a/compiler/rustc_ty_utils/src/lib.rs b/compiler/rustc_ty_utils/src/lib.rs
index 0853de601b0..35f468aa952 100644
--- a/compiler/rustc_ty_utils/src/lib.rs
+++ b/compiler/rustc_ty_utils/src/lib.rs
@@ -15,6 +15,8 @@ extern crate rustc_middle;
 #[macro_use]
 extern crate tracing;
 
+use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage};
+use rustc_macros::fluent_messages;
 use rustc_middle::ty::query::Providers;
 
 mod abi;
@@ -31,6 +33,8 @@ pub mod representability;
 mod structural_match;
 mod ty;
 
+fluent_messages! { "../locales/en-US.ftl" }
+
 pub fn provide(providers: &mut Providers) {
     abi::provide(providers);
     assoc::provide(providers);