29个版本

新版本 0.10.1 2024年8月19日
0.9.4 2024年4月22日
0.9.1 2024年3月11日
0.8.0 2022年8月8日
0.5.13 2020年7月24日

#7 in 模板引擎

Download history 3117/week @ 2024-05-03 3399/week @ 2024-05-10 3161/week @ 2024-05-17 4155/week @ 2024-05-24 5370/week @ 2024-05-31 4477/week @ 2024-06-07 5256/week @ 2024-06-14 4595/week @ 2024-06-21 4702/week @ 2024-06-28 4800/week @ 2024-07-05 4905/week @ 2024-07-12 5382/week @ 2024-07-19 7447/week @ 2024-07-26 7726/week @ 2024-08-02 7706/week @ 2024-08-09 7920/week @ 2024-08-16

32,040 每月下载量
用于 26 个crate (12 直接使用)

MIT/Apache

67KB
952

Fluent Templates:一个高级Fluent API。

Build & Test crates.io Help Wanted Lines Of Code Documentation

fluent-templates 允许您轻松地将 Fluent 本地化集成到您的Rust应用程序或库中。它通过提供一个高级“加载器”API来实现,该API根据简单的语言协商加载fluent字符串,以及一个 FluentLoader 结构体,它是一个对 Loader 无感知的容器类型,它提供了对流行的模板引擎(如handlebars或tera)的可选特质实现,允许您在模板中使用本地化而无需样板代码。

加载器

目前这个crate提供了两种不同类型的加载器,覆盖了两个主要用例。

  • static_loader! — 一个过程宏,它将在 编译时 将您的fluent资源加载到您的二进制文件中,并创建一个新的 StaticLoader 静态变量,允许您访问本地化。 static_loader! 在您想要本地化应用程序并希望将fluent资源与二进制文件一起发布时非常有用。

  • ArcLoader — 一个结构体,它使用 Arc 作为其后端存储在 运行时 加载您的fluent资源。 ArcLoader 在您希望在运行时更改和/或更新本地化,或者如果您正在编写一个希望在自己的应用程序中提供fluent本地化的开发工具(如静态网站生成器)时非常有用。

static_loader!

使用 fluent-templates 最简单的方法是使用 static_loader! 程序宏,它将创建一个新的 StaticLoader 静态变量。

基本示例

fluent_templates::static_loader! {
    // Declare our `StaticLoader` named `LOCALES`.
    static LOCALES = {
        // The directory of localisations and fluent resources.
        locales: "./tests/locales",
        // The language to falback on if something is not present.
        fallback_language: "en-US",
        // Optional: A fluent resource that is shared with every locale.
        core_locales: "./tests/locales/core.ftl",
    };
}

自定义示例

您还可以在初始化时修改每个 FluentBundle,以便能够更改配置或从 Rust 中添加资源。

use fluent_bundle::FluentResource;
use fluent_templates::static_loader;
use once_cell::sync::Lazy;

static_loader! {
    // Declare our `StaticLoader` named `LOCALES`.
    static LOCALES = {
        // The directory of localisations and fluent resources.
        locales: "./tests/locales",
        // The language to falback on if something is not present.
        fallback_language: "en-US",
        // Optional: A fluent resource that is shared with every locale.
        core_locales: "./tests/locales/core.ftl",
        // Optional: A function that is run over each fluent bundle.
        customise: |bundle| {
            // Since this will be called for each locale bundle and
            // `FluentResource`s need to be either `&'static` or behind an
            // `Arc` it's recommended you use lazily initialised
            // static variables.
            static CRATE_VERSION_FTL: Lazy<FluentResource> = Lazy::new(|| {
                let ftl_string = String::from(
                    concat!("-crate-version = {}", env!("CARGO_PKG_VERSION"))
                );

                FluentResource::try_new(ftl_string).unwrap()
            });

            bundle.add_resource(&CRATE_VERSION_FTL);
        }
    };
}

区域设置目录

fluent-templates 将收集所有匹配有效 Unicode 语言标识符 的子目录,并将这些目录中找到的所有 fluent 文件捆绑在一起,并将这些资源映射到相应的标识符。 fluent-templates 将按需递归遍历每个语言目录,并尊重任何存在的 .gitignore.ignore 文件。

示例布局

locales
├── core.ftl
├── en-US
│   └── main.ftl
├── fr
│   └── main.ftl
├── zh-CN
│   └── main.ftl
└── zh-TW
    └── main.ftl

查找 fluent 资源

您可以使用 Loader 特性来 lookup 给定的 fluent 资源,并使用 lookup_with_args 提供所需任何额外的参数。

示例

 # In `locales/en-US/main.ftl`
 hello-world = Hello World!
 greeting = Hello { $name }!

 # In `locales/fr/main.ftl`
 hello-world = Bonjour le monde!
 greeting = Bonjour { $name }!

 # In `locales/de/main.ftl`
 hello-world = Hallo Welt!
 greeting = Hallo { $name }!
use std::collections::HashMap;

use unic_langid::{LanguageIdentifier, langid};
use fluent_templates::{Loader, static_loader};

const US_ENGLISH: LanguageIdentifier = langid!("en-US");
const FRENCH: LanguageIdentifier = langid!("fr");
const GERMAN: LanguageIdentifier = langid!("de");

static_loader! {
    static LOCALES = {
        locales: "./tests/locales",
        fallback_language: "en-US",
        // Removes unicode isolating marks around arguments, you typically
        // should only set to false when testing.
        customise: |bundle| bundle.set_use_isolating(false),
    };
}

fn main() {
    assert_eq!("Hello World!", LOCALES.lookup(&US_ENGLISH, "hello-world"));
    assert_eq!("Bonjour le monde!", LOCALES.lookup(&FRENCH, "hello-world"));
    assert_eq!("Hallo Welt!", LOCALES.lookup(&GERMAN, "hello-world"));

    let args = {
        let mut map = HashMap::new();
        map.insert(String::from("name"), "Alice".into());
        map
    };

    assert_eq!("Hello Alice!", LOCALES.lookup_with_args(&US_ENGLISH, "greeting", &args));
    assert_eq!("Bonjour Alice!", LOCALES.lookup_with_args(&FRENCH, "greeting", &args));
    assert_eq!("Hallo Alice!", LOCALES.lookup_with_args(&GERMAN, "greeting", &args));
}

Tera

使用 tera 功能,您可以像 Tera 函数一样使用 FluentLoader。它接受一个指向 fluent 资源的 key 参数和一个表示要获取该键的语言的 lang。您可以选择将额外参数作为资源参数传递给函数。fluent-templates 将自动将 Tera 的 snake_case 参数键转换为 fluent 优先的 kebab-case 参数。

fluent-templates = { version = "*", features = ["tera"] }
use fluent_templates::{FluentLoader, static_loader};

static_loader! {
    static LOCALES = {
        locales: "./tests/locales",
        fallback_language: "en-US",
        // Removes unicode isolating marks around arguments, you typically
        // should only set to false when testing.
        customise: |bundle| bundle.set_use_isolating(false),
    };
}

fn main() {
    let mut tera = tera::Tera::default();
    let ctx = tera::Context::default();
    tera.register_function("fluent", FluentLoader::new(&*LOCALES));
    assert_eq!(
        "Hello World!",
        tera.render_str(r#"{{ fluent(key="hello-world", lang="en-US") }}"#, &ctx).unwrap()
    );
    assert_eq!(
        "Hello Alice!",
        tera.render_str(r#"{{ fluent(key="greeting", lang="en-US", name="Alice") }}"#, &ctx).unwrap()
    );
}

Handlebars

在 handlebars 中,fluent-templates 将在渲染时读取您的 handlebars::Context 中的 lang 字段。

fluent-templates = { version = "*", features = ["handlebars"] }
use fluent_templates::{FluentLoader, static_loader};

static_loader! {
    static LOCALES = {
        locales: "./tests/locales",
        fallback_language: "en-US",
        // Removes unicode isolating marks around arguments, you typically
        // should only set to false when testing.
        customise: |bundle| bundle.set_use_isolating(false),
    };
}

fn main() {
    let mut handlebars = handlebars::Handlebars::new();
    handlebars.register_helper("fluent", Box::new(FluentLoader::new(&*LOCALES)));
    let data = serde_json::json!({"lang": "zh-CN"});
    assert_eq!("Hello World!", handlebars.render_template(r#"{{fluent "hello-world"}}"#, &data).unwrap());
    assert_eq!("Hello Alice!", handlebars.render_template(r#"{{fluent "greeting" name="Alice"}}"#, &data).unwrap());
}

Handlebars 辅助器语法。

提供的主要辅助器是 {{fluent}} 辅助器。如果您有以下 Fluent 文件

foo-bar = "foo bar"
placeholder = this has a placeholder { $variable }
placeholder2 = this has { $variable1 } { $variable2 }

您可以使用以下方式在模板中包含字符串

<!-- will render "foo bar" -->
{{fluent "foo-bar"}}
<!-- will render "this has a placeholder baz" -->
{{fluent "placeholder" variable="baz"}}

您还可以使用 {{fluentparam}} 辅助器来指定 变量,特别是当您需要它们为多行时。

{{#fluent "placeholder2"}}
    {{#fluentparam "variable1"}}
        first line
        second line
    {{/fluentparam}}
    {{#fluentparam "variable2"}}
        first line
        second line
    {{/fluentparam}}
{{/fluent}}

常见问题解答

为什么参数的值周围有额外的字符?

这些被称为“Unicode Isolating Marks”,用于允许文本双向。您可以通过将 FluentBundle::set_isolating_marks 设置为 false 来禁用此功能。

static_loader! {
    static LOCALES = {
        locales: "./tests/locales",
        fallback_language: "en-US",
        // Removes unicode isolating marks around arguments.
        customise: |bundle| bundle.set_use_isolating(false),
    };
}

依赖关系

~5–15MB
~181K SLoC