#本地化 #翻译 #国际化

无需 std typed-i18n

一个类型安全的国际化系统

2 个版本

0.6.1 2024年6月28日
0.6.0 2024年6月18日

#64 in 国际化(i18n)

MIT 许可证

26KB
109

Codecov Dependency status crates.io Downloads Github stars License

crate typed-i18n

将语言文件和枚举转换为类型安全的国际化系统。

基本用法

Yaml 语言文件:(也支持 json 和 lrc,更多见下文)

hello_world:
  en: Hello World
  de: Hallo Welt
hello_you:
  en: Hello %{name}

代码

#[derive(Copy, Clone, TypedI18N)]
#[typed_i18n(filename = "example.yaml")]
#[typed_i18n(builder = "mixed_str", prefix = "str_")]
enum Language { En, De }

生成的代码

impl Language {
    fn str_hello_world(self) -> &'static str;
    fn str_hello_you(self, name: &str) -> String;
}

用法

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());
  }
}

也支持全局存储的语言,详见下文 global

更多构建器

不同的生成器会添加不同的代码

#[typed_i18n(builder = "static_str", prefix = "sta_")]
#[typed_i18n(builder = "String")]

生成的代码

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;
}

其他输出类型

也支持字符串之外的类型

#in addition to the messages above
hello_with_icon:
  en: Hello %{name}*{icon}
#[typed_i18n(builder = "HtmlBuilder", input = "Html", prefix = "html_")]

生成的代码

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;
}

请参见 示例,其中包含 HtmlBuilder 用于 yew::Html 实现的示例。

输入

字段

  • filename:翻译文件的路径,相对于 crate 根目录(必需)。
  • separator:用于合并树路径,默认:_
  • global:用于全局存储的语言,详见下文 global,默认:不使用。

示例

#[typed_i18n(filename= "example.yaml",separator= "_")]

消息可以提供如下:

  • yaml(如上例所示)
  • json
  • lrc

json

{"hello_world": {"en": "Hello World"}}

yaml 和 json 文件可能有一个 _version: 2 条目,以与其他国际化工具兼容。(其他数字是错误,省略它是可以的。)

如果 yaml 和 json 文件有一个更深的树,则使用分隔符将段连接成一个函数名。

示例

hello:
  world:
    en: Hello World
  you:
    en: Hello %{name}

使用分隔符 _,它将产生与上述示例相同的名称。这可能有助于您组织消息。

由于名称是函数名,因此不允许使用 . 作为分隔符,但您可以使用 ·(U+00B7)。

lrc

; lines starting with `;` or `/` will be skipped
#hello_world
  en Hello World

输出

字段

  • builder:实现 Builder 的项目的名称,或一个特殊值。
  • prefix:此构建器生成的所有函数的前缀,默认:空字符串。
  • str_conversion:如何转换 str 参数,默认 ref
  • input:输入类型(对于已类型化输入),默认:无输入。
  • input_conversion:如何将参数转换为输入类型,默认:into

builder

必须是特殊值或实现了 Builder 的类型。

所有没有输入类型的构建器都将跳过所有针对该类型的消息。

以下所有构建器都是内置的。对于这些,不得设置 input。始终可用

  • static_str:所有不带参数的消息的返回类型为 &'static str。所有其他都将被跳过。
  • mixed_str:所有不带参数的消息的返回类型为 &'static str,所有其他将具有返回类型 String
  • String
  • Cow<'static, str>
  • _:函数将泛型化构建器类型。这有时并不有用,例如,当在函数的结果上调用 .into().as_ref() 时。

除了 static_str_ 外,都需要 alloc 功能(默认启用)以及 StringCow 在作用域内。

有关自定义类型构建器的信息,请参阅上面的示例和在 示例目录 中的示例。

prefix

builder 的所有函数都将由前缀和消息名称组成。

使用相同的前缀或重叠的名称会导致具有相同名称的函数,从而引起编译错误。这不会由 derive 宏检查。

str_conversion

所有字符串(普通)%{param} 参数的转换

  • ref(默认):fn str_hello_you(self, name: &str) -> String
  • as_reffn str_hello_you<S1: AsRef<str>>(self, name: S1) -> String

input

类型化 *{param} 参数的输入类型。

所有没有 input 的构建器将静默地跳过所有具有类型化参数的消息。

使用 _ 创建输入类型的泛型函数。可能会导致与泛型构建器类型相同的问题。

input_conversion

如何转换输入类型

  • value:输入类型作为参数:fn html_hello_with_icon(self, name: &str, icon: Input) -> <HtmlBuilder as BuilderFromValue>::Output
  • into(默认):可以转换为输入类型的任何东西:fn html_hello_with_icon<T1: Into<Input>>(self, name: &str, icon: T1) -> <HtmlBuilder as BuilderFromValue>::Output
  • ref:输入类型的引用:fn html_hello_with_icon(self, name: &str, icon: &Input) -> <HtmlBuilder as BuilderFromRef>::Output
  • as_ref:可以转换为输入类型引用的任何东西:fn html_hello_with_icon<T1: AsRef<Input>>(self, name: &str, icon: T1) -> <HtmlBuilder as BuilderFromRef>::Output

对于 valueinto 来说,构建器必须也实现 BuilderFromValue

对于 refas_ref 来说,构建器必须也实现 BuilderFromRef

语言

枚举值可以被注解为

  • name:消息文件中语言的名称,默认为 snake_case 中的值名称。
  • fallback:一个由空格和/或逗号分隔的语言名称列表,它定义了在消息缺失时应使用哪种语言。默认:按列表顺序的所有语言(不一定按数字顺序)。
  • default:用于 全局 存储。只能有一个语言是默认的。

示例

#[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,
}

全局

可以生成全局语言存储。

代码

#[derive(Copy, Clone, TypedI18N)]
#[typed_i18n(filename = "example.yaml", global = "atomic")]
#[typed_i18n(builder = "static_str")]
enum Language { En, #[typed_i18n(default = "true")] De }

生成的代码

impl Language {
    fn global() -> Self;
    fn set_global(self);
}

示例用法

// de is the default
assert_eq!(Language::global().hello_world(), "Hallo Welt");
Language::En.set_global();
assert_eq!(Language::global().hello_world(), "Hello world");

默认语言(要么标记为如此,参见上面的示例,要么是第一个)最初存储为全局语言。

目前 atomic 是唯一的实现,它使用 AtomicU8 来存储语言。转换不依赖于枚举的表示。在超过 256 种语言的情况下使用 AtomicUsize

特性

  • alloc,默认启用:为StringCow<'static, str>提供Builder实现,也支持mixed_str

库始终为no_std

依赖项

~3.5–4.5MB
~91K SLoC