20个版本

新版本 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日

模板引擎 中排名第552

Download history 3078/week @ 2024-05-03 3365/week @ 2024-05-10 3139/week @ 2024-05-17 4092/week @ 2024-05-24 5334/week @ 2024-05-31 4450/week @ 2024-06-07 5174/week @ 2024-06-14 4574/week @ 2024-06-21 4680/week @ 2024-06-28 4809/week @ 2024-07-05 4909/week @ 2024-07-12 5387/week @ 2024-07-19 7434/week @ 2024-07-26 7707/week @ 2024-08-02 7699/week @ 2024-08-09 7958/week @ 2024-08-16

每月下载量32,035
4 个crate使用(通过 fluent-templates

MIT/Apache

14KB
241 代码行

Fluent Templates:一个高级Fluent API。

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

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

加载器

目前,这个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 功能,您可以将 FluentLoader 用作 Tera 函数。它接受一个指向 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),
    };
}

依赖关系

~1–9MB
~81K SLoC