blob: 708516d32072da239035fe76892406477dd2bfee [file] [log] [blame] [edit]
<!DOCTYPE HTML>
<html lang="en" class="light sidebar-visible" dir="ltr">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>Translation - Rust Compiler Development Guide</title>
<!-- Custom HTML head -->
<meta name="description" content="A guide to developing the Rust compiler (rustc)">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#ffffff">
<link rel="icon" href="../favicon.svg">
<link rel="shortcut icon" href="../favicon.png">
<link rel="stylesheet" href="../css/variables.css">
<link rel="stylesheet" href="../css/general.css">
<link rel="stylesheet" href="../css/chrome.css">
<link rel="stylesheet" href="../css/print.css" media="print">
<!-- Fonts -->
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
<link rel="stylesheet" href="../fonts/fonts.css">
<!-- Highlight.js Stylesheets -->
<link rel="stylesheet" id="highlight-css" href="../highlight.css">
<link rel="stylesheet" id="tomorrow-night-css" href="../tomorrow-night.css">
<link rel="stylesheet" id="ayu-highlight-css" href="../ayu-highlight.css">
<!-- Custom theme stylesheets -->
<link rel="stylesheet" href="../pagetoc.css">
<!-- Provide site root and default themes to javascript -->
<script>
const path_to_root = "../";
const default_light_theme = "light";
const default_dark_theme = "navy";
window.path_to_searchindex_js = "../searchindex.js";
</script>
<!-- Start loading toc.js asap -->
<script src="../toc.js"></script>
</head>
<body>
<div id="mdbook-help-container">
<div id="mdbook-help-popup">
<h2 class="mdbook-help-title">Keyboard shortcuts</h2>
<div>
<p>Press <kbd></kbd> or <kbd></kbd> to navigate between chapters</p>
<p>Press <kbd>S</kbd> or <kbd>/</kbd> to search in the book</p>
<p>Press <kbd>?</kbd> to show this help</p>
<p>Press <kbd>Esc</kbd> to hide this help</p>
</div>
</div>
</div>
<div id="body-container">
<!-- Work around some values being stored in localStorage wrapped in quotes -->
<script>
try {
let theme = localStorage.getItem('mdbook-theme');
let sidebar = localStorage.getItem('mdbook-sidebar');
if (theme.startsWith('"') && theme.endsWith('"')) {
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
}
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
}
} catch (e) { }
</script>
<!-- Set the theme before any content is loaded, prevents flash -->
<script>
const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme;
let theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
const html = document.documentElement;
html.classList.remove('light')
html.classList.add(theme);
html.classList.add("js");
</script>
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
<!-- Hide / unhide sidebar before it is displayed -->
<script>
let sidebar = null;
const sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
if (document.body.clientWidth >= 1080) {
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
sidebar = sidebar || 'visible';
} else {
sidebar = 'hidden';
sidebar_toggle.checked = false;
}
if (sidebar === 'visible') {
sidebar_toggle.checked = true;
} else {
html.classList.remove('sidebar-visible');
}
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<!-- populated by js -->
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
<noscript>
<iframe class="sidebar-iframe-outer" src="../toc.html"></iframe>
</noscript>
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
<div class="sidebar-resize-indicator"></div>
</div>
</nav>
<div id="page-wrapper" class="page-wrapper">
<div class="page">
<div id="menu-bar-hover-placeholder"></div>
<div id="menu-bar" class="menu-bar sticky">
<div class="left-buttons">
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
<i class="fa fa-bars"></i>
</label>
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
<i class="fa fa-paint-brush"></i>
</button>
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
<li role="none"><button role="menuitem" class="theme" id="default_theme">Auto</button></li>
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
</ul>
<button id="search-toggle" class="icon-button" type="button" title="Search (`/`)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="/ s" aria-controls="searchbar">
<i class="fa fa-search"></i>
</button>
</div>
<h1 class="menu-title">Rust Compiler Development Guide</h1>
<div class="right-buttons">
<a href="../print.html" title="Print this book" aria-label="Print this book">
<i id="print-button" class="fa fa-print"></i>
</a>
<a href="https://github.com/rust-lang/rustc-dev-guide" title="Git repository" aria-label="Git repository">
<i id="git-repository-button" class="fa fa-github"></i>
</a>
<a href="https://github.com/rust-lang/rustc-dev-guide/edit/main/src/diagnostics/translation.md" title="Suggest an edit" aria-label="Suggest an edit" rel="edit">
<i id="git-edit-button" class="fa fa-edit"></i>
</a>
</div>
</div>
<div id="search-wrapper" class="hidden">
<form id="searchbar-outer" class="searchbar-outer">
<div class="search-wrapper">
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
<div class="spinner-wrapper">
<i class="fa fa-spinner fa-spin"></i>
</div>
</div>
</form>
<div id="searchresults-outer" class="searchresults-outer hidden">
<div id="searchresults-header" class="searchresults-header"></div>
<ul id="searchresults">
</ul>
</div>
</div>
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
<script>
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
});
</script>
<div id="content" class="content">
<main>
<h1 id="translation"><a class="header" href="#translation">Translation</a></h1>
<div class="warning">
rustc's current diagnostics translation infrastructure (as of
<!-- date-check --> October 2024
) unfortunately causes some friction for compiler contributors, and the current
infrastructure is mostly pending a redesign that better addresses needs of both
compiler contributors and translation teams. Note that there is no current
active redesign proposals (as of
<!-- date-check --> October 2024
)!
<p>Please see the tracking issue <a href="https://github.com/rust-lang/rust/issues/132181">https://github.com/rust-lang/rust/issues/132181</a>
for status updates.</p>
<p>We have downgraded the internal lints <code>untranslatable_diagnostic</code> and
<code>diagnostic_outside_of_impl</code>. Those internal lints previously required new code
to use the current translation infrastructure. However, because the translation
infra is waiting for a yet-to-be-proposed redesign and thus rework, we are not
mandating usage of current translation infra. Use the infra if you <em>want to</em> or
otherwise makes the code cleaner, but otherwise sidestep the translation infra
if you need more flexibility.</p>
</div>
<p>rustc's diagnostic infrastructure supports translatable diagnostics using
<a href="https://projectfluent.org">Fluent</a>.</p>
<h2 id="writing-translatable-diagnostics"><a class="header" href="#writing-translatable-diagnostics">Writing translatable diagnostics</a></h2>
<p>There are two ways of writing translatable diagnostics:</p>
<ol>
<li>For simple diagnostics, using a diagnostic (or subdiagnostic) derive.
("Simple" diagnostics being those that don't require a lot of logic in
deciding to emit subdiagnostics and can therefore be represented as
diagnostic structs). See <a href="./diagnostic-structs.html">the diagnostic and subdiagnostic structs
documentation</a>.</li>
<li>Using typed identifiers with <code>Diag</code> APIs (in
<code>Diagnostic</code> or <code>Subdiagnostic</code> or <code>LintDiagnostic</code> implementations).</li>
</ol>
<p>When adding or changing a translatable diagnostic,
you don't need to worry about the translations.
Only updating the original English message is required.
Currently,
each crate which defines translatable diagnostics has its own Fluent resource,
which is a file named <code>messages.ftl</code>,
located in the root of the crate
(such as<code>compiler/rustc_expand/messages.ftl</code>).</p>
<h2 id="fluent"><a class="header" href="#fluent">Fluent</a></h2>
<p>Fluent is built around the idea of "asymmetric localization", which aims to
decouple the expressiveness of translations from the grammar of the source
language (English in rustc's case). Prior to translation, rustc's diagnostics
relied heavily on interpolation to build the messages shown to the users.
Interpolated strings are hard to translate because writing a natural-sounding
translation might require more, less, or just different interpolation than the
English string, all of which would require changes to the compiler's source
code to support.</p>
<p>Diagnostic messages are defined in Fluent resources. A combined set of Fluent
resources for a given locale (e.g. <code>en-US</code>) is known as Fluent bundle.</p>
<pre><code class="language-fluent">typeck_address_of_temporary_taken = cannot take address of a temporary
</code></pre>
<p>In the above example, <code>typeck_address_of_temporary_taken</code> is the identifier for
a Fluent message and corresponds to the diagnostic message in English. Other
Fluent resources can be written which would correspond to a message in another
language. Each diagnostic therefore has at least one Fluent message.</p>
<pre><code class="language-fluent">typeck_address_of_temporary_taken = cannot take address of a temporary
.label = temporary value
</code></pre>
<p>By convention, diagnostic messages for subdiagnostics are specified as
"attributes" on Fluent messages (additional related messages, denoted by the
<code>.&lt;attribute-name&gt;</code> syntax). In the above example, <code>label</code> is an attribute of
<code>typeck_address_of_temporary_taken</code> which corresponds to the message for the
label added to this diagnostic.</p>
<p>Diagnostic messages often interpolate additional context into the message shown
to the user, such as the name of a type or of a variable. Additional context to
Fluent messages is provided as an "argument" to the diagnostic.</p>
<pre><code class="language-fluent">typeck_struct_expr_non_exhaustive =
cannot create non-exhaustive {$what} using struct expression
</code></pre>
<p>In the above example, the Fluent message refers to an argument named <code>what</code>
which is expected to exist (how arguments are provided to diagnostics is
discussed in detail later).</p>
<p>You can consult the <a href="https://projectfluent.org">Fluent</a> documentation for other usage examples of Fluent
and its syntax.</p>
<h3 id="guideline-for-message-naming"><a class="header" href="#guideline-for-message-naming">Guideline for message naming</a></h3>
<p>Usually, fluent uses <code>-</code> for separating words inside a message name. However,
<code>_</code> is accepted by fluent as well. As <code>_</code> fits Rust's use cases better, due to
the identifiers on the Rust side using <code>_</code> as well, inside rustc, <code>-</code> is not
allowed for separating words, and instead <code>_</code> is recommended. The only exception
is for leading <code>-</code>s, for message names like <code>-passes_see_issue</code>.</p>
<h3 id="guidelines-for-writing-translatable-messages"><a class="header" href="#guidelines-for-writing-translatable-messages">Guidelines for writing translatable messages</a></h3>
<p>For a message to be translatable into different languages, all of the
information required by any language must be provided to the diagnostic as an
argument (not just the information required in the English message).</p>
<p>As the compiler team gain more experience writing diagnostics that have all of
the information necessary to be translated into different languages, this page
will be updated with more guidance. For now, the <a href="https://projectfluent.org">Fluent</a> documentation has
excellent examples of translating messages into different locales and the
information that needs to be provided by the code to do so.</p>
<h3 id="compile-time-validation-and-typed-identifiers"><a class="header" href="#compile-time-validation-and-typed-identifiers">Compile-time validation and typed identifiers</a></h3>
<p>rustc's <code>fluent_messages</code> macro performs compile-time validation of Fluent
resources and generates code to make it easier to refer to Fluent messages in
diagnostics.</p>
<p>Compile-time validation of Fluent resources will emit any parsing errors
from Fluent resources while building the compiler, preventing invalid Fluent
resources from causing panics in the compiler. Compile-time validation also
emits an error if multiple Fluent messages have the same identifier.</p>
<h2 id="internals"><a class="header" href="#internals">Internals</a></h2>
<p>Various parts of rustc's diagnostic internals are modified in order to support
translation.</p>
<h3 id="messages"><a class="header" href="#messages">Messages</a></h3>
<p>All of rustc's traditional diagnostic APIs (e.g. <code>struct_span_err</code> or <code>note</code>)
take any message that can be converted into a <code>DiagMessage</code> (or
<code>SubdiagMessage</code>).</p>
<p><a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_error_messages/enum.DiagMessage.html"><code>rustc_error_messages::DiagMessage</code></a> can represent legacy non-translatable
diagnostic messages and translatable messages. Non-translatable messages are
just <code>String</code>s. Translatable messages are just a <code>&amp;'static str</code> with the
identifier of the Fluent message (sometimes with an additional <code>&amp;'static str</code>
with an attribute).</p>
<p><code>DiagMessage</code> never needs to be interacted with directly:
<code>DiagMessage</code> constants are created for each diagnostic message in a
Fluent resource (described in more detail below), or <code>DiagMessage</code>s will
either be created in the macro-generated code of a diagnostic derive.</p>
<p><code>rustc_error_messages::SubdiagMessage</code> is similar, it can correspond to a
legacy non-translatable diagnostic message or the name of an attribute to a
Fluent message. Translatable <code>SubdiagMessage</code>s must be combined with a
<code>DiagMessage</code> (using <code>DiagMessage::with_subdiagnostic_message</code>) to
be emitted (an attribute name on its own is meaningless without a corresponding
message identifier, which is what <code>DiagMessage</code> provides).</p>
<p>Both <code>DiagMessage</code> and <code>SubdiagMessage</code> implement <code>Into</code> for any
type that can be converted into a string, and converts these into
non-translatable diagnostics - this keeps all existing diagnostic calls
working.</p>
<h3 id="arguments"><a class="header" href="#arguments">Arguments</a></h3>
<p>Additional context for Fluent messages which are interpolated into message
contents needs to be provided to translatable diagnostics.</p>
<p>Diagnostics have a <code>set_arg</code> function that can be used to provide this
additional context to a diagnostic.</p>
<p>Arguments have both a name (e.g. "what" in the earlier example) and a value.
Argument values are represented using the <code>DiagArgValue</code> type, which is
just a string or a number. rustc types can implement <code>IntoDiagArg</code> with
conversion into a string or a number, and common types like <code>Ty&lt;'tcx&gt;</code> already
have such implementations.</p>
<p><code>set_arg</code> calls are handled transparently by diagnostic derives but need to be
added manually when using diagnostic builder APIs.</p>
<h3 id="loading"><a class="header" href="#loading">Loading</a></h3>
<p>rustc makes a distinction between the "fallback bundle" for <code>en-US</code> that is used
by default and when another locale is missing a message; and the primary fluent
bundle which is requested by the user.</p>
<p>Diagnostic emitters implement the <code>Emitter</code> trait which has two functions for
accessing the fallback and primary fluent bundles (<code>fallback_fluent_bundle</code> and
<code>fluent_bundle</code> respectively).</p>
<p><code>Emitter</code> also has member functions with default implementations for performing
translation of a <code>DiagMessage</code> using the results of
<code>fallback_fluent_bundle</code> and <code>fluent_bundle</code>.</p>
<p>All of the emitters in rustc load the fallback Fluent bundle lazily, only
reading Fluent resources and parsing them when an error message is first being
translated (for performance reasons - it doesn't make sense to do this if no
error is being emitted). <code>rustc_error_messages::fallback_fluent_bundle</code> returns
a <code>std::lazy::Lazy&lt;FluentBundle&gt;</code> which is provided to emitters and evaluated
in the first call to <code>Emitter::fallback_fluent_bundle</code>.</p>
<p>The primary Fluent bundle (for the user's desired locale) is expected to be
returned by <code>Emitter::fluent_bundle</code>. This bundle is used preferentially when
translating messages, the fallback bundle is only used if the primary bundle is
missing a message or not provided.</p>
<p>There are no locale bundles distributed with the compiler,
but mechanisms are implemented for loading them.</p>
<ul>
<li><code>-Ztranslate-additional-ftl</code> can be used to load a specific resource as the
primary bundle for testing purposes.</li>
<li><code>-Ztranslate-lang</code> can be provided a language identifier (something like
<code>en-US</code>) and will load any Fluent resources found in
<code>$sysroot/share/locale/$locale/</code> directory (both the user provided
sysroot and any sysroot candidates).</li>
</ul>
<p>Primary bundles are not currently loaded lazily and if requested will be loaded
at the start of compilation regardless of whether an error occurs. Lazily
loading primary bundles is possible if it can be assumed that loading a bundle
won't fail. Bundle loading can fail if a requested locale is missing, Fluent
files are malformed, or a message is duplicated in multiple resources.</p>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="../diagnostics/diagnostic-structs.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="../diagnostics/lintstore.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
<div style="clear: both"></div>
</nav>
</div>
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
<a rel="prev" href="../diagnostics/diagnostic-structs.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="../diagnostics/lintstore.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
</nav>
</div>
<script>
window.playground_copyable = true;
</script>
<script src="../elasticlunr.min.js"></script>
<script src="../mark.min.js"></script>
<script src="../searcher.js"></script>
<script src="../clipboard.min.js"></script>
<script src="../highlight.js"></script>
<script src="../book.js"></script>
<!-- Custom JS scripts -->
<script src="../mermaid.min.js"></script>
<script src="../mermaid-init.js"></script>
<script src="../pagetoc.js"></script>
</div>
</body>
</html>