3 个版本 (破坏性更新)
0.9.0 | 2019 年 3 月 3 日 |
---|---|
0.8.0 | 2019 年 1 月 2 日 |
0.7.0 | 2018 年 2 月 7 日 |
#29 在 国际化 (i18n)
每月下载量 64,194
在 51 个库中 使用 51 个 (直接使用 14 个)
79KB
1K SLoC
UNIC: Rust 的 Unicode 和国际化库
https://github.com/open-i18n/rust-unic
UNIC 是一个开发 Rust 编程语言组件的项目,以提供高质量、易于使用的 Unicode 和国际化数据和算法的 crate。换句话说,它类似于 ICU,但完全用 Rust 编写,大部分在 safe 模式下,但在可能的情况下也受益于 unsafe 模式的性能提升。
请参阅 UNIC 变更日志 了解最新版本详细信息。
项目目标
UNIC 的目标是提供访问 Unicode 和国际化所有级别的功能,从 Unicode 字符属性开始,到处理文本的 Unicode 算法,以及基于 Unicode Common Locale 数据库 (CLDR) 的更高级(基于区域设置)过程。
根据 Unicode/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 数据版本。违反这个假设可能导致不一致性和难以发现的错误。在合并仓库中,在开发过程中以及跨组件(集成)测试中,都有可能达到更好的完整性。
-
按需付费。小型组件(基本包),仅依赖于它们所需的内容,允许用户仅将其项目消费的内容引入项目。
-
共享启动。大量扩展 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》或 https://apache.ac.cn/licenses/LICENSE-2.0)
- MIT 许可证(《LICENSE-MIT》或 http://opensource.org/licenses/MIT)
。
贡献
除非您明确声明,否则根据 Apache-2.0 许可证定义的,任何有意提交以包含在您的工作中的贡献,都应如上所述双许可,而不附加任何额外的条款或条件。
行为准则
UNIC 项目遵循 Rust 行为准则。您可以在 CODE_OF_CONDUCT.md 或在线 https://rust-lang.net.cn/conduct.html 中找到其副本。
lib.rs
:
UNIC — Unicode Emoji — 表情符号字符属性
unic
的组件:Rust 的 Unicode 和国际化包。