1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
//! # 🔞
//!
//! `r18` is a crate intends to simplify the internationalisation of Rust
//! projects.
//!
//! `MSRV >= 1.70.0 (stable)`
//!
//! ## Usage
//!
//! Add `r18` as your project dependency:
//!
//! ```toml
//! [dependencies]
//! r18 = "*"
//! ```
//! Create a `JSON` translation file whose filename follows
//! [IETF BCP 47](https://www.wikiwand.com/en/IETF_language_tag)
//! language tag, like below:
//!
//! ```json
//! // PATH: ./tr/zh-CN.json
//! {
//! "Hello, {}": "你好,{}"
//! }
//! ```
//!
//! Then add [`init`] to the global scope of your code with
//! the directory where translation files in (in following example is `./tr`).
//!
//! ```ignore
//! r18::init!("tr");
//! ```
//!
//! After initialising the `r18`, use [`auto_detect`] to detect locale and load
//! translation model automatically.
//! If you want, you can use [`set_locale`] to set locale manually.
//! After above process, use [`tr`] to get your text which has been translated.
//!
//! ```ignore
//! r18::init!("tr");
//!
//! fn main() {
//! r18::auto_detect!(); // get locale & set
//!
//! let name = "ho-229";
//! println!("{}", r18::tr!("Hello, {}", name));
//!
//! // reset locale to disable translation
//! r18::set_locale!("");
//! assert_eq!("Hello, ho-229", r18::tr!("Hello, {}", name));
//! }
//! ```
//!
//! ### Fallback Configuration
//!
//! Sometimes your translation may not fully match the user's locale,
//! but usually, this doesn't mean that your translations cannot be used.
//! In that case, we need the fallback feature.
//!
//! By default, if the translation does not match the user's locale,
//! `r18` will fallback to the translation which is the same language
//! by the highest alphabetical order.
//!
//! You can also specify a fallback translation for a language in `config.json`
//! which placed with other translation files.
//!
//! eg.
//! ```json
//! {
//! "fallback": {
//! "zh": "zh-TW"
//! }
//! }
//! ```
use std::sync::{Mutex, OnceLock};
#[doc(hidden)]
pub use dynfmt::{Format, SimpleCurlyFormat};
#[doc(hidden)]
pub use oxilangtag::{LanguageTag, LanguageTagParseError};
#[doc(hidden)]
pub use phf;
pub use r18_proc_macros::init;
#[doc(hidden)]
pub use sys_locale::get_locale;
mod_use::mod_use!(macros);
#[doc(hidden)]
pub struct Locale {
pub name: &'static str,
pub translate: phf::Map<&'static str, &'static str>,
}
#[doc(hidden)]
pub static CURRENT_LOCALE: OnceLock<Mutex<Option<&'static Locale>>> = OnceLock::new();
/// Translate content with the locale setting and given prefix.
///
/// We recommend using [`tr!`] instead of [`translate`] for translate your
/// content.
pub fn translate(prefix: impl AsRef<str>, content: &str) -> &str {
let locale = CURRENT_LOCALE
.get_or_init(|| Mutex::new(None))
.lock()
.unwrap();
let Some(locale) = *locale else {
return content;
};
match locale
.translate
.get(format!("{} {}", prefix.as_ref(), content).as_str())
{
Some(tr) => tr,
None => content,
}
}