Awesome
cargo-i18n
![dependency status](https://deps.rs/repo/github/kellpossible/cargo-i18n/status.svg)
This crate is a Cargo sub-command cargo i18n
which can be used to extract and
build, and verify localization resources at compile time for your crate. The
i18n-embed library has been created to
allow you to conveniently embed these localizations into your application or
library, and have them selected at runtime. Different systems can be used simultaneously.
i18n-embed
supports both the following localization systems:
You can install this tool using the command: cargo install cargo-i18n
.
The cargo i18n
command reads the configuration file (by default called i18n.toml
) in the root directory of your crate, and then proceeds to extract localization resources from your source files, and build them.
The i18n-build library contains most of the implementation for this tool. It has been published separately to allow its direct use within project build scripts if required.
Projects Using cargo-i18n
or i18n-embed
- The source code for this project, which localizes itself.
age
file encryptionavalanche-report
coster
(work in progress) self-hosted web application.
Usage with Fluent
Fluent support is now available in i18n-embed. See the examples for that crate, and the documentation for i18n-embed for example of how to use it.
See also the fl!() macro for a convenient compile-time checked way to access fluent messages when using i18n-embed
.
Currently there are no validations performed by the cargo-i18n
tool when using the fluent
localization system, however there are some planned (see tracking issue #31). If you have any more ideas for this, please feel free to contribute to the issue discussion.
Usage with Gettext
This is an example for how to use the cargo-i18n
tool, and i18n-embed
the gettext
localization tool system. Please note that the gettext
localization system is technically inferior to fluent
in a number of ways, however there are always legacy reasons, and the developer/translator ecosystem around gettext
is more mature.
Firstly, ensure you have the required utilities installed on your system. See Gettext System Requirements and install the necessary utilities and commands for the localization system you will be using.
Defining Localized Strings
You will need to ensure that your strings in your source code that you want localized are using the tr!()
macro from the tr crate.
You can add comments to your strings which will be available to translators to add context, and ensure that they understand what the string is for.
For example:
use tr::tr;
fn example(file: String) {
let my_string = tr!(
// {0} is a file path
// Example message: Printing this file: "file.doc"
"Printing this file: \"{0}\"",
file
);
}
Minimal Configuration
You will need to create an i18n.toml
configuration in the root directory of your crate. A minimal configuration for a binary crate to be localized to Spanish and Japanese using the gettext
system would be:
# (Required) The language identifier of the language used in the
# source code for gettext system, and the primary fallback language
# (for which all strings must be present) when using the fluent
# system.
fallback_language = "en"
[gettext]
# (Required) The languages that the software will be translated into.
target_languages = ["es", "ja"]
# (Required) Path to the output directory, relative to `i18n.toml` of the crate
# being localized.
output_dir = "i18n"
See Configuration for a description of all the available configuration options.
Running cargo i18n
Open your command line/terminal and navigate to your crate directory, and run cargo i18n
. You may be prompted to enter some email addresses to use for contact points for each of the language's po
files. At the end there should be a new directory in your crate called i18n
, and inside will be pot
, po
and mo
directories.
The pot
directory contains pot
files which were extracted from your source code using the xtr
tool, and there should be a single pot
file with the name of your crate in here too, which is the result of merging all the other pot
files.
The po
directory contains the language specific message files.
The mo
directory contains the compiled messages, which will later be embedded into your application.
At this point it could be a good idea to add the following to your crate's .gitignore
(if you are using git):
/i18n/pot
/i18n/mo
If you want your crate to be able to build without requiring this tool to be present on the system, then you can leave the /i18n/mo
directory out of the .gitignore
, and commit the files inside.
Embedding Translations
Now that you have compiled your translations, you can embed them within your application. For this purpose the i18n-embed crate was created.
Add the following to your Cargo.toml
dependencies:
[dependencies]
i18n-embed = "VERSION"
A minimal example for how to embed the compiled translations into your application could be:
use i18n_embed::{DesktopLanguageRequester,
gettext::gettext_language_loader};
use rust_embed::RustEmbed;
#[derive(RustEmbed)]
#[folder = "i18n/mo"] // path to the compiled localization resources
struct Translations;
fn main() {
let translations = Translations {};
let language_loader = gettext_language_loader!();
// Use the language requester for the desktop platform (linux, windows, mac).
// There is also a requester available for the web-sys WASM platform called
// WebLanguageRequester, or you can implement your own.
let requested_languages = DesktopLanguageRequester::requested_languages();
i18n_embed::select(&language_loader, &translations, &requested_languages);
// continue with your application
}
You can see the i18n-embed documentation for more detailed examples of how this library can be used.
Distributing to Translators
Now you need to send of the po
files to your translators, or provide them access to edit them. Some desktop tools which can be used for the translation include:
Or you could also consider setting up a translation management website for your project to allow translators to edit translations without requiring them to interact with source control or mess around with sending files and installing applications. Some examples:
Self Hosted:
Cloud:
- poeditor - free for open source projects, currently being used for this project.
- crowdin - free for popular open source projects.
Updating Translations
Once you have some updated po
files back from translators, or you want to update the po
files with new or edited strings, all you need to do is run cargo i18n
to update the po
files, and recompile updated mo
files, then rebuild your application with cargo build
.
For some projects using build scripts, with complex pipelines, and with continuous integration, you may want to look into using the i18n-build for automation as an alternative to the cargo i18n
command line tool.
Configuration
Available configuration options for i18n.toml
:
# (Required) The language identifier of the language used in the
# source code for gettext system, and the primary fallback language
# (for which all strings must be present) when using the fluent
# system.
fallback_language = "en-US"
# (Optional) Specify which subcrates to perform localization within. If the
# subcrate has its own `i18n.toml` then, it will have its localization
# performed independently (rather than being incorporated into the parent
# project's localization).
subcrates = ["subcrate1", "subcrate2"]
# (Optional) Use the gettext localization system.
[gettext]
# (Required) The languages that the software will be translated into.
target_languages = ["es", "ru", "cz"]
# (Required) Path to the output directory, relative to `i18n.toml` of the crate
# being localized.
output_dir = "i18n"
# (Optional) The reporting address for msgid bugs. This is the email address or
# URL to which the translators shall report bugs in the untranslated
# strings.
msg_bugs_address = "example@example.com"
# (Optional) Set the copyright holder for the generated files.
copyright_holder = "You?"
# (Optional) If this crate is being localized as a subcrate, store the final
# localization artifacts (the module pot and mo files) with the parent crate's
# output. Currently crates which contain subcrates with duplicate names are not
# supported. By default this is false.
extract_to_parent = false
# (Optional) If a subcrate has extract_to_parent set to true, then merge the
# output pot file of that subcrate into this crate's pot file. By default this
# is false.
collate_extracted_subcrates = false
# (Optional) How much message location information to include in the output.
# If the type is ‘full’ (the default), it generates the lines with both file
# name and line number: ‘#: filename:line’. If it is ‘file’, the line number
# part is omitted: ‘#: filename’. If it is ‘never’, nothing is generated.
# [possible values: full, file, never].
add_location = "full"
# (Optional) Whether or not to perform string extraction using the `xtr` tool.
xtr = true
# (Optional )Path to where the pot files will be written to by `xtr` command,
# and were they will be read from by the `msginit` and `msgmerge` commands. By
# default this is `output_dir/pot`.
pot_dir = "i18n/pot"
# (Optional) Path to where the po files will be stored/edited with the
# `msgmerge` and `msginit` commands, and where they will be read from with the
# `msgfmt` command. By default this is `output_dir/po`.
po_dir = "i18n/po"
# (Optional) Path to where the mo files will be written to by the `msgfmt`
# command. By default this is `output_dir/mo`.
mo_dir = "i18n/mo"
# (Optional) Enable the `--use-fuzzy` option for the `msgfmt` command. By
# default this is false. If your .po file are copied from another project, you
# may need to enable it.
use_fuzzy = false
# (Optional) Use the fluent localization system.
[fluent]
# (Required) The path to the assets directory.
# The paths inside the assets directory should be structured like so:
# `assets_dir/{language}/{domain}.ftl`
assets_dir = "i18n"
System Requirements
Gettext Requirements
Using the gettext
localization system with this tool requires you to have gettext installed on your system.
The msginit
, msgfmt
, msgmerge
and msgcat
commands all need to be installed and present in your path.
You also need to ensure that you have the xtr
string extraction command installed, which can be achieved using cargo install xtr
.
Contributing
Pull-requests are welcome, but for design changes it is preferred that you create a GitHub issue first to discuss it before implementation. You can also contribute to the localization of this tool via:
Or you can also use your favourite po
editor directly to help with localizing the files located in i18n/po and i18n-build/i18n/po.
To add a new language, you can make a request via a GitHub issue, or submit a pull request adding the new locale to i18n.toml and generating the associated new po
files using cargo i18n
.
Translations of this README.md file are also welcome, and can be submitted via pull request. Just name it README.md.lang
, where lang
is the locale code (see List of ISO 639-1 codes).