使用旧的 Rust 2015
0.4.0 |
|
---|---|
0.2.0 |
|
0.1.1 |
|
0.1.0 |
|
#24 in #unic
3KB
UNIC: Rust 的 Unicode 和国际化 Crates
https://github.com/open-i18n/rust-unic
UNIC 是一个开发 Rust 编程语言组件的项目,旨在提供高质量、易于使用的 Unicode 和国际化数据与算法的 crate。换句话说,它就像 Rust 的 ICU,完全用 Rust 编写,大部分在 safe 模式下,但在可能的情况下也受益于 unsafe 模式的性能提升。
请参阅 UNIC 更新日志,了解最新发布详情。
项目目标
UNIC 的目标是提供对 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-ucd
:Unicode 字符数据库(《UAX#44》)。 -
unic-bidi
:Unicode 双向算法(《UAX#9》)。 -
unic-normal
:Unicode 规范化形式(《UAX#15》)。 -
unic-segment
:Unicode 文本分段算法(《UAX#29》)。 -
unic-idna
:Unicode IDNA 兼容处理(《UTS#46》)。 -
unic-emoji
:Unicode 表情符号(《UTS#51》)。
应用
unic-cli
:UNIC 命令行工具
代码组织:组合仓库
以下是一些将这些组件组合在一起的原因
-
更快的开发。实现新的Unicode/i18n组件通常依赖于其他(较低级别)组件,而它们又可能需要调整——暴露新API、修复错误等,这些都可以在更少的周期和更短的时间内进行开发、测试和审查。
-
实施完整性。多个组件之间的依赖意味着组件需要在某种程度上相互一致。许多由较小部分组成的Unicode算法假定算法的所有部分都使用相同的Unicode数据版本。违反这个假设可能导致不一致和难以捕捉的错误。在联合仓库中,在开发过程中以及进行跨组件(集成)测试时,可以更好地达到完整性。
-
按需付费。小型组件(基本crate),它们仅依赖于所需内容,使用户只需在其项目中引入所需的内容。
-
共享启动。大量扩展Unicode/i18n功能依赖于将源Unicode/locale数据转换为目标编程语言的格式化格式。在联合仓库中,更容易维护这些启动工具、扩展覆盖范围,并使用更高效的数据结构。
文档
- 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的扩展还将添加更多内容...)](https://github.com/behnam/rust-unic/blob/41cd2b01e2e51a2b8d7d32fd13b14c0c092ac014/examples/)
许可证
许可证为以下之一
- Apache许可证第2版(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。