2 releases
0.6.1 | Jun 28, 2024 |
---|---|
0.6.0 | Jun 18, 2024 |
#69 in Internationalization (i18n)
26KB
109 lines
crate typed-i18n
Convert a language file and an enum into a type safe i18n system.
Basic Usage
Yaml language file: (json and lrc is also supported, more below)
hello_world:
en: Hello World
de: Hallo Welt
hello_you:
en: Hello %{name}
Code:
#[derive(Copy, Clone, TypedI18N)]
#[typed_i18n(filename = "example.yaml")]
#[typed_i18n(builder = "mixed_str", prefix = "str_")]
enum Language { En, De }
Generated code:
impl Language {
fn str_hello_world(self) -> &'static str;
fn str_hello_you(self, name: &str) -> String;
}
Usage:
fn print_hello(language: Language, name: Option<&str>){
if let Some(name) = name {
println!("{}", language.str_hello_you(name));
} else {
println!("{}", language.str_hello_world());
}
}
A global stored language is also supported, see global below.
More builders
Different generators add different code:
#[typed_i18n(builder = "static_str", prefix = "sta_")]
#[typed_i18n(builder = "String")]
Generated code:
impl Language {
fn sta_hello_world(self) -> &'static str;
// The static_str builder skips all parameterized messages
fn hello_world(self) -> String;
fn hello_you(self, name: &str) -> String;
}
Other output types
Also types other than strings are possible:
#in addition to the messages above
hello_with_icon:
en: Hello %{name}*{icon}
#[typed_i18n(builder = "HtmlBuilder", input = "Html", prefix = "html_")]
Generated code:
impl Language {
fn html_hello_world(self) -> <HtmlBuilder as Builder>::Output;
fn html_hello_you(self, name: &str) -> <HtmlBuilder as Builder>::Output;
fn html_hello_with_icon<T1: Into<Html>>(
self,
name: &str,
icon: T1,
) -> <HtmlBuilder as Builder>::Output;
}
See examples for a HtmlBuilder
for yew::Html
implementation.
Input
Fields:
filename
: the path to the translations, relative to the crate root (required).separator
: used for combining paths of a tree, default:_
.global
: used for a global stored language, see global below, default: not used.
Example:
#[typed_i18n(filename = "example.yaml", separator = "_")]
The messages can be supplied in
- yaml (as seen in the examples above)
- json
- lrc
json:
{"hello_world": {"en": "Hello World"}}
The yaml and json files may have a _version: 2
entry, to be compatible
with other i18n tools. (Other numbers are an error, omitting it is ok.)
The yaml and json files may have a deeper tree. In that case the separator is used to join the segments into a function name.
Example:
hello:
world:
en: Hello World
you:
en: Hello %{name}
With the separator _
it will result in the same names as the examples above.
This may help you structure the messages.
Since the name is a function name a .
as a separator is not allowed, but you can use ·
(U+00B7).
lrc:
; lines starting with `;` or `/` will be skipped
#hello_world
en Hello World
Output
Fields:
builder
: The name of an item which implementsBuilder
, or a special value.prefix
: Prefix for all functions generated by this builder, default: the empty string.str_conversion
: How to convert str parameters, defaultref
.input
: Type of the input (for typed inputs), default: no input.input_conversion
: How to convert the parameter into the input type, default:into
.
builder
Must be either a special value or a type for which Builder
is implemented.
All builders without a input type will skip all messages for it.
All the builders below are built-in. The input
must not be set for these. Always available:
static_str
: all messages without parameters have the return type&'static str
. All others are skipped.mixed_str
: all messages without parameters have the return type&'static str
all others will have the return typeString
.String
Cow<'static, str>
_
: The functions will be generic over the builder type. This is sometimes not helpful, e.g. when.into()
or.as_ref()
will be called on the result of the function.
All except static_str
and _
require the feature alloc
(enabled by default) and that String
or Cow
is in scope.
To learn about custom type builder see the example above and in the examples directory.
prefix
All functions of this builder
will be composed of the prefix and the message name.
Using identical prefixes or overlapping names will result in functions with identical names and thus result in a compile error. This will not be checked by the derive macro.
str_conversion
The conversion for all string (normal) %{param}
parameters:
ref
(default):fn str_hello_you(self, name: &str) -> String
.as_ref
:fn str_hello_you<S1: AsRef<str>>(self, name: S1) -> String
.
input
Type of the input for typed *{param}
parameters.
All builders without input
will silently skip all messages with typed parameters.
With _
a generic function over the input type is created. May lead to the same problems as
a generic builder type.
input_conversion
How to convert the types inputs:
value
: The input type as parameter:fn html_hello_with_icon(self, name: &str, icon: Input) -> <HtmlBuilder as BuilderFromValue>::Output
into
(default): Something that can be converted into the input type:fn html_hello_with_icon<T1: Into<Input>>(self, name: &str, icon: T1) -> <HtmlBuilder as BuilderFromValue>::Output
ref
: A reference to the input type:fn html_hello_with_icon(self, name: &str, icon: &Input) -> <HtmlBuilder as BuilderFromRef>::Output
as_ref
: Something that can be converted into a reference to the input type:fn html_hello_with_icon<T1: AsRef<Input>>(self, name: &str, icon: T1) -> <HtmlBuilder as BuilderFromRef>::Output
For value
and into
to work the builder must also implement BuilderFromValue
.
For ref
and as_ref
to work the builder must also implement BuilderFromRef
.
Language
The enum values can be annotated with:
name
: The name of the language in the messages file, defaults to the name of the value insnake_case
.fallback
: A space and/or comma separated list of language names which defines which language should be used when a message is missing. Default: all languages in listing order (not necessary in numerical order).default
: Is used for a global storage. Only one language may be the default.
Example:
#[derive(Copy, Clone, TypedI18N)]
#[typed_i18n(filename = "example.yaml")]
#[typed_i18n(builder = "mixed_str", prefix = "str_")]
enum Language {
De,
#[typed_i18n(name = "en")]
English,
#[typed_i18n(fallback = "en, de")]
EnAu,
}
Global
It is possible to generate a global language storage.
Code:
#[derive(Copy, Clone, TypedI18N)]
#[typed_i18n(filename = "example.yaml", global = "atomic")]
#[typed_i18n(builder = "static_str")]
enum Language { En, #[typed_i18n(default = "true")] De }
Generated code:
impl Language {
fn global() -> Self;
fn set_global(self);
}
Example usage:
// de is the default
assert_eq!(Language::global().hello_world(), "Hallo Welt");
Language::En.set_global();
assert_eq!(Language::global().hello_world(), "Hello world");
The default language (either marked as such, see example above, or the first one) is initially stored as the global language.
Currently atomic
is the only implementation, which uses an AtomicU8
to store the language. The conversion does not depend on the representation of the enum.
In case of more than 256 languages an AtomicUsize
is used.
Features
alloc
, enabled by default: Provide Builder implementations forString
andCow<'static, str>
, also supportmixed_str
.
The library is always no_std
.
Dependencies
~3.5–4.5MB
~90K SLoC