Skip to main content

lang_lib/
lib.rs

1//! A lightweight, high-performance localization library.
2//!
3//! `lang-lib` loads TOML language files, supports runtime locale switching,
4//! configurable file paths, and automatic fallback chains. It is designed to
5//! be dropped into any project without ceremony.
6//!
7//! # What This Crate Does Well
8//!
9//! - Keeps the runtime model simple: load files once, then translate by key.
10//! - Uses plain TOML so translators and developers can inspect files easily.
11//! - Works across platforms by resolving file paths with native path handling.
12//! - Fails predictably with typed errors when files are missing or invalid.
13//!
14//! # Quick Start
15//!
16//! ```rust,no_run
17//! use lang_lib::{t, Lang};
18//!
19//! // Configure once at startup
20//! Lang::set_path("locales");
21//! Lang::load("en").unwrap();
22//! Lang::load("es").unwrap();
23//! Lang::set_locale("en");
24//!
25//! // Translate anywhere
26//! let msg = t!("bad_password");
27//! let msg_es = t!("bad_password", "es");
28//! let msg_fb = t!("missing_key", fallback: "Default message");
29//! ```
30//! # Small Tutorial
31//!
32//! 1. Create a directory for locale files.
33//! 2. Add one TOML file per locale.
34//! 3. Load the locales your application needs at startup.
35//! 4. Set the active locale for the current process.
36//! 5. Use [`t!`] anywhere you need translated text.
37//!
38//! Example layout:
39//!
40//! ```text
41//! your-app/
42//! |- Cargo.toml
43//! |- src/
44//! |  \- main.rs
45//! \- locales/
46//!    |- en.toml
47//!    \- es.toml
48//! ```
49//!
50//!
51//! # File Format
52//!
53//! Language files are plain TOML, one key per line:
54//!
55//! ```toml
56//! bad_password = "Your password is incorrect."
57//! welcome_user = "Welcome back"
58//! not_found    = "The page you requested does not exist."
59//! ```
60//!
61//! Files are resolved as `{path}/{locale}.toml`.
62//!
63//! # Behavior Notes
64//!
65//! Lookup order is deterministic:
66//!
67//! 1. Requested locale, or the active locale when none is provided
68//! 2. Each configured fallback locale in order
69//! 3. The inline fallback passed to [`t!`]
70//! 4. The key itself
71//!
72//! Non-string TOML values are ignored on purpose. That keeps translation data
73//! flat and avoids surprising coercions at runtime.
74//!
75//! # Examples
76//!
77//! See the runnable example in `examples/basic.rs` for a complete setup using
78//! real locale files.
79//! See `examples/server.rs` for a server-oriented pattern that resolves a
80//! locale per request and passes it explicitly during translation.
81//! See `examples/axum_server.rs` for the same pattern inside a real HTTP
82//! handler using `axum`.
83//! See `examples/actix_server.rs` for the same pattern using `actix-web`.
84//! Locale names must be a single file stem such as `en`, `en-US`, or `pt_BR`.
85
86#![deny(missing_docs)]
87#![deny(unsafe_op_in_unsafe_fn)]
88#![deny(unused_must_use)]
89#![deny(unused_results)]
90#![deny(clippy::unwrap_used)]
91#![deny(clippy::expect_used)]
92#![deny(clippy::todo)]
93#![deny(clippy::unimplemented)]
94#![deny(clippy::print_stdout)]
95#![deny(clippy::print_stderr)]
96#![deny(clippy::dbg_macro)]
97#![deny(clippy::undocumented_unsafe_blocks)]
98#![deny(clippy::missing_safety_doc)]
99#![warn(clippy::pedantic)]
100#![allow(clippy::module_name_repetitions)]
101
102mod error;
103mod loader;
104mod request;
105mod store;
106
107pub use error::LangError;
108pub use request::{resolve_accept_language, resolve_accept_language_owned};
109pub use store::{Lang, Translator};
110
111/// Translates a key using the active locale.
112///
113/// Falls back through the fallback chain and finally returns the key itself
114/// if no translation is found anywhere.
115///
116/// # Examples
117///
118/// ```rust,no_run
119/// use lang_lib::t;
120///
121/// // Active locale
122/// let msg = t!("greeting");
123///
124/// // Specific locale
125/// let msg = t!("greeting", "es");
126///
127/// // Inline fallback
128/// let msg = t!("unknown_key", fallback: "Hello");
129/// ```
130#[macro_export]
131macro_rules! t {
132    ($key:expr) => {
133        $crate::Lang::translate($key, None, None)
134    };
135    ($key:expr, $locale:expr) => {
136        $crate::Lang::translate($key, Some($locale), None)
137    };
138    ($key:expr, fallback: $fallback:expr) => {
139        $crate::Lang::translate($key, None, Some($fallback))
140    };
141    ($key:expr, $locale:expr, fallback: $fallback:expr) => {
142        $crate::Lang::translate($key, Some($locale), Some($fallback))
143    };
144}