3 个版本 (破坏性更新)
0.9.0 | 2019 年 3 月 3 日 |
---|---|
0.8.0 | 2019 年 1 月 2 日 |
0.7.0 | 2018 年 2 月 7 日 |
#247 in 国际化(i18n)
387,537 每月下载量
用于 760 个包(《760 个直接使用”)
95KB
1.5K SLoC
UNIC:Rust 的 Unicode 和国际化包
https://github.com/open-i18n/rust-unic
UNIC 是一个旨在为 Rust 编程语言开发组件的项目,以提供高质量、易于使用的 Unicode 和国际化数据和算法包。换句话说,它就像 Rust 中的 ICU,完全用 Rust 编写,大部分在 safe 模式下,但在可能的情况下也受益于 unsafe 模式的性能提升。
请参阅 UNIC 更新日志 了解最新版本详细信息。
项目目标
UNIC 的目标是提供对所有 Unicode 和国际化功能层次的访问,从 Unicode 字符属性开始,到 Unicode 文本处理的 Unicode 算法,以及基于 Unicode Common Locale 数据库(CLDR)的更高级(基于区域)过程。
根据需要,还实现了其他标准和最佳实践,如 IETF RFC。
项目状态
目前 UNIC 正在积极开发中:API 在 master
分支上频繁更新,每次 0.x
版本之间都可能发生 API 破坏。请参阅 公开问题 了解计划中的更改。
我们预计 2018 年将发布 1.0 版本,之后将维护稳定的 API,可能每年更新一两次 API。
设计目标
-
UNIC 的主要目标是通过易于使用的 API 提供可靠的功能。因此,新添加的组件可能在性能方面没有得到很好的优化,但将提供足够的测试来展示符合标准,以及示例来展示用户如何使用它们来满足常见需求。
-
UNIC 组件的下一个重大目标是性能和低二进制和内存占用。特别是,优化对 ASCII 及其他常见用例的运行时将鼓励适应,而无需担心会减慢常规的开发过程。
-
在可能范围内,组件将保证提供一致的数据和算法。通过跨组件测试来捕捉实现之间的任何不一致,而不会减慢开发过程。
组件及其组织
UNIC 组件 采用层次结构组织,从 unic
根开始,包含 主要组件。每个主要组件反过来可以包含一些 次要组件。
主要组件的 API 是为库的最终用户设计的,预计将得到广泛的文档支持,并附带代码示例。
与主要组件相反,次要组件充当为高级组件提供数据和算法的提供者,其 API 预计将具有更高的性能,并提供多种访问数据的方式。
UNIC 超级箱子
unic
超级箱子是所有 UNIC(主要)组件的集合,提供了一种方便的方式访问所有功能,当需要所有或许多功能时,而不是逐个导入组件。此箱子确保所有导入的组件在算法和数据方面兼容。
主要代码示例和跨组件集成测试在此箱子下实现。
主要组件
-
unic-char
:Unicode 字符工具。 -
unic-normal
:Unicode 正规化形式 (UAX#15)。 -
unic-segment
:Unicode 文本分段算法 (UAX#29)。 -
unic-emoji
:Unicode 表情符号 (UTS#51)。
应用
unic-cli
: UNIC 命令行工具
代码组织:合并仓库
以下是一些选择合并仓库的理由:
-
更快的开发速度。实现新的 Unicode/i18n 组件通常需要依赖其他(较低级别)组件,而这些组件可能需要调整——暴露新的 API、修复错误等——这些可以在更短的周期和更短的时间内进行开发、测试和审查。
-
实现完整性。对其他组件的多个依赖意味着这些组件在某种程度上需要相互协调。许多 Unicode 算法由较小的算法组成,假设算法的所有部分都使用相同的 Unicode 数据版本。违反此假设可能导致不一致和难以捕捉的错误。在合并仓库中,在开发过程中以及在进行跨组件(集成)测试时,可以更好地达到完整性。
-
按需付费。小型组件(基本 crates),只交叉依赖它们需要的组件,允许用户只引入项目中所消耗的部分。
-
共享启动。大量扩展 Unicode/i18n 功能需要将源 Unicode/区域数据转换为目的地编程语言的格式化格式。在合并仓库中,更容易维护这些启动工具,扩展覆盖范围,并使用更高效的数据结构。
文档
- Unicode 和 Rust
- UNIC 版本
- UNIC Unicode API
- UNIC API 指南
- UNIC API 参考(由 docs.rs 自动生成)
如何使用 UNIC
在 Cargo.toml
[dependencies]
unic = "0.9.0" # This has Unicode 10.0.0 data and algorithms
以及在 main.rs
extern crate unic;
use unic::ucd::common::is_alphanumeric;
use unic::bidi::BidiInfo;
use unic::normal::StrNormalForm;
use unic::segment::{GraphemeIndices, Graphemes, WordBoundIndices, WordBounds, Words};
use unic::ucd::normal::compose;
use unic::ucd::{is_cased, Age, BidiClass, CharAge, CharBidiClass, StrBidiClass, UnicodeVersion};
fn main() {
// Age
assert_eq!(Age::of('A').unwrap().actual(), UnicodeVersion { major: 1, minor: 1, micro: 0 });
assert_eq!(Age::of('\u{A0000}'), None);
assert_eq!(
Age::of('\u{10FFFF}').unwrap().actual(),
UnicodeVersion { major: 2, minor: 0, micro: 0 }
);
if let Some(age) = '🦊'.age() {
assert_eq!(age.actual().major, 9);
assert_eq!(age.actual().minor, 0);
assert_eq!(age.actual().micro, 0);
}
// Bidi
let text = concat![
"א",
"ב",
"ג",
"a",
"b",
"c",
];
assert!(!text.has_bidi_explicit());
assert!(text.has_rtl());
assert!(text.has_ltr());
assert_eq!(text.chars().nth(0).unwrap().bidi_class(), BidiClass::RightToLeft);
assert!(!text.chars().nth(0).unwrap().is_ltr());
assert!(text.chars().nth(0).unwrap().is_rtl());
assert_eq!(text.chars().nth(3).unwrap().bidi_class(), BidiClass::LeftToRight);
assert!(text.chars().nth(3).unwrap().is_ltr());
assert!(!text.chars().nth(3).unwrap().is_rtl());
let bidi_info = BidiInfo::new(text, None);
assert_eq!(bidi_info.paragraphs.len(), 1);
let para = &bidi_info.paragraphs[0];
assert_eq!(para.level.number(), 1);
assert_eq!(para.level.is_rtl(), true);
let line = para.range.clone();
let display = bidi_info.reorder_line(para, line);
assert_eq!(
display,
concat![
"a",
"b",
"c",
"ג",
"ב",
"א",
]
);
// Case
assert_eq!(is_cased('A'), true);
assert_eq!(is_cased('א'), false);
// Normalization
assert_eq!(compose('A', '\u{030A}'), Some('Å'));
let s = "ÅΩ";
let c = s.nfc().collect::<String>();
assert_eq!(c, "ÅΩ");
// Segmentation
assert_eq!(
Graphemes::new("a\u{310}e\u{301}o\u{308}\u{332}").collect::<Vec<&str>>(),
&["a\u{310}", "e\u{301}", "o\u{308}\u{332}"]
);
assert_eq!(
Graphemes::new("a\r\nb🇺🇳🇮🇨").collect::<Vec<&str>>(),
&["a", "\r\n", "b", "🇺🇳", "🇮🇨"]
);
assert_eq!(
GraphemeIndices::new("a̐éö̲\r\n").collect::<Vec<(usize, &str)>>(),
&[(0, "a̐"), (3, "é"), (6, "ö̲"), (11, "\r\n")]
);
assert_eq!(
Words::new(
"The quick (\"brown\") fox can't jump 32.3 feet, right?",
|s: &&str| s.chars().any(is_alphanumeric),
).collect::<Vec<&str>>(),
&["The", "quick", "brown", "fox", "can't", "jump", "32.3", "feet", "right"]
);
assert_eq!(
WordBounds::new("The quick (\"brown\") fox").collect::<Vec<&str>>(),
&["The", " ", "quick", " ", "(", "\"", "brown", "\"", ")", " ", " ", "fox"]
);
assert_eq!(
WordBoundIndices::new("Brr, it's 29.3°F!").collect::<Vec<(usize, &str)>>(),
&[
(0, "Brr"),
(3, ","),
(4, " "),
(5, "it's"),
(9, " "),
(10, "29.3"),
(14, "°"),
(16, "F"),
(17, "!")
]
);
}
您可以在 examples
和 tests
目录下找到更多示例。(随着 UNIC 的扩展,还将添加更多内容...)
许可
以下任一许可下授权:
- Apache 许可证 2.0(LICENSE-APACHE 或 http://www.apache.org/licenses/LICENSE-2.0)
- MIT 许可证(LICENSE-MIT 或 http://opensource.org/licenses/MIT)
由您选择。
贡献
除非您明确声明,否则任何提交以供包含在作品中的贡献,根据 Apache-2.0 许可证定义,均应如上双许可,无需任何附加条款或条件。
行为准则
UNIC 项目遵循 Rust 行为准则。您可以在 CODE_OF_CONDUCT.md 或在线 https://www.rust-lang.net.cn/conduct.html 找到其副本。
lib.rs
:
UNIC — UCD — 分割属性"
unic
的组件:为 Rust 提供的 Unicode 和国际化 Crates。
从 Unicode 字符数据库 (UCD) 访问文本分割字符属性。