391 lines
22 KiB
HTML
391 lines
22 KiB
HTML
<!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 -->
|
|
|
|
|
|
<!-- Provide site root and default themes to javascript -->
|
|
<script>
|
|
const path_to_root = "../";
|
|
const default_light_theme = "light";
|
|
const default_dark_theme = "navy";
|
|
</script>
|
|
<!-- Start loading toc.js asap -->
|
|
<script src="../toc.js"></script>
|
|
</head>
|
|
<body>
|
|
<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 = sidebar === 'visible';
|
|
html.classList.remove('sidebar-visible');
|
|
html.classList.add("sidebar-" + sidebar);
|
|
</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. (Shortkey: s)" 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/master/src/diagnostics/translation.md" title="Suggest an edit" aria-label="Suggest an 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">
|
|
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
|
</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>.<attribute-name></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>&'static str</code> with the
|
|
identifier of the Fluent message (sometimes with an additional <code>&'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<'tcx></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<FluentBundle></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>
|
|
|
|
|
|
</div>
|
|
</body>
|
|
</html>
|