aboutsummaryrefslogtreecommitdiff
path: root/README.md
blob: 677ac1cd726b6c31cb8a5ef5b31cf81db275f9d1 (plain) (blame)
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
# "Shake Hand!" 

> Let different languages shake hands with each other! 🤝

This is a **purely compile-time** Rust internationalization library. **All** localized strings are fully embedded into the binary at compile time, with only minimal runtime overhead.

# How to Use?

## 1. Add Dependency

Add the following to your `Cargo.toml`:

```toml
shakehand = "0.1"
```

## 2. Write Configuration Files

Create a directory under your project to serve as the root for translations, e.g. `./locale/`.
Create any `toml` file inside this directory, e.g. `./locale/global.toml`, representing a translation file.

In the file, group translations by language section. The key is the translation key, and the value is the translated text.
Text with parameters uses `%{parameter_name}` as placeholders, where the parameter name directly maps to the generated function's parameter name:

```toml
[en]
world = "world"
greeting = "Hello, %{someone}!"

[zh_CN]
world = "世界"
greeting = "你好,%{someone}!"
```

## 3. Load Translations

In your Rust code, use `shakehand::locale!` to hardcode the entire directory into a module:

```rust
pub mod translation {
    shakehand::locale!("./locale", fallback = "en");
}
```

The generated module will contain:
- A `Languages` enum listing all languages
- A `set_lang` function for switching the current language
- A unit struct `Global` (named after `global.toml`), whose associated functions are the translations for each key

## 4. Call Translations

Call translations just like ordinary functions, passing parameters by placeholder name:

```rust
use crate::translation::{Global, Languages, set_lang};

fn main() {
    set_lang(Languages::en);
    let greeting = Global::greeting("World");
    println!("{}", greeting);  // Hello, World!

    set_lang(Languages::zh_CN);
    let greeting = Global::greeting("世界");
    println!("{}", greeting);  // 你好,世界!
}
```

You can also pass the return value of another translation as a parameter, since each parameterless translation function returns a `&'static str`, completely allocation-free:

```rust
let greeting = Global::greeting(Global::world());
```

## 5. Fallback Chain (Optional)

You can configure a **fallback chain** in your `Cargo.toml` under `[package.metadata.shakehand]`.

When a language has no translation for a key, the system will automatically walk the chain
 to find the nearest language that has one.

```toml
[package.metadata.shakehand]
# For any language not explicitly listed, fall back to English
fallback.other = "en"

# Specific fallback rules
fallback.zh_HK = "zh_CN"   # Hong Kong Trad -> Simplified Chinese
fallback.zh_TW = "zh_CN"   # Taiwan Trad -> Simplified Chinese
fallback.zh_CN = "en"      # Simplified Chinese -> English
fallback.it = "fr"         # Italian -> French
```

The chain resolution is **at runtime via a generated `FallbackSolver`**:
- Each language has a `try_fallback_once()` step defined in a compile-time generated match.
- Translation functions loop through the chain until a language with the value is found.
- `fallback.other` is the ultimate fallback (falls back to `locale!`'s `fallback` parameter if unset).
- All languages in the chain are automatically added to the `Languages` enum, even if they
  have no translations in the locale files.


# Contributing

Directly open a PR to the [repository](https://github.com/catilgrass/shakehand) and mention [@Weicao-CatilGrass](https://github.com/Weicao-CatilGrass).



# License

MIT or Apache 2.0