Make language non-optional with english as default
This commit is contained in:
parent
67e9313b91
commit
d025854457
39
src/library/text/lang.rs
Normal file
39
src/library/text/lang.rs
Normal file
@ -0,0 +1,39 @@
|
||||
use crate::eval::Value;
|
||||
use crate::geom::Dir;
|
||||
|
||||
/// A natural language.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
pub struct Lang([u8; 2]);
|
||||
|
||||
impl Lang {
|
||||
/// The code for the english language.
|
||||
pub const ENGLISH: Self = Self(*b"en");
|
||||
|
||||
/// Construct a language from a two-byte ISO 639-1 code.
|
||||
pub fn from_str(iso: &str) -> Option<Self> {
|
||||
let mut bytes: [u8; 2] = iso.as_bytes().try_into().ok()?;
|
||||
bytes.make_ascii_lowercase();
|
||||
Some(Self(bytes))
|
||||
}
|
||||
|
||||
/// Return the language code as a string slice.
|
||||
pub fn as_str(&self) -> &str {
|
||||
std::str::from_utf8(&self.0).unwrap_or_default()
|
||||
}
|
||||
|
||||
/// The default direction for the language.
|
||||
pub fn dir(&self) -> Dir {
|
||||
match self.as_str() {
|
||||
"ar" | "dv" | "fa" | "he" | "ks" | "pa" | "ps" | "sd" | "ug" | "ur"
|
||||
| "yi" => Dir::RTL,
|
||||
_ => Dir::LTR,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
castable! {
|
||||
Lang,
|
||||
Expected: "string",
|
||||
Value::Str(string) => Self::from_str(&string)
|
||||
.ok_or("expected two letter language code")?,
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
//! Text handling and paragraph layout.
|
||||
|
||||
mod deco;
|
||||
mod lang;
|
||||
mod link;
|
||||
mod par;
|
||||
mod quotes;
|
||||
@ -8,6 +9,7 @@ mod raw;
|
||||
mod shaping;
|
||||
|
||||
pub use deco::*;
|
||||
pub use lang::*;
|
||||
pub use link::*;
|
||||
pub use par::*;
|
||||
pub use quotes::*;
|
||||
@ -64,8 +66,7 @@ impl TextNode {
|
||||
pub const BOTTOM_EDGE: TextEdge = TextEdge::Metric(VerticalFontMetric::Baseline);
|
||||
|
||||
/// An ISO 639-1 language code.
|
||||
#[property(referenced)]
|
||||
pub const LANG: Option<Lang> = None;
|
||||
pub const LANG: Lang = Lang::ENGLISH;
|
||||
/// The direction for text and inline objects. When `auto`, the direction is
|
||||
/// automatically inferred from the language.
|
||||
#[property(resolve)]
|
||||
@ -257,32 +258,6 @@ castable! {
|
||||
}),
|
||||
}
|
||||
|
||||
/// A natural language.
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||
pub struct Lang(EcoString);
|
||||
|
||||
impl Lang {
|
||||
/// The default direction for the language.
|
||||
pub fn dir(&self) -> Dir {
|
||||
match self.0.as_str() {
|
||||
"ar" | "dv" | "fa" | "he" | "ks" | "pa" | "ps" | "sd" | "ug" | "ur"
|
||||
| "yi" => Dir::RTL,
|
||||
_ => Dir::LTR,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the language code as a string slice.
|
||||
pub fn as_str(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
castable! {
|
||||
Lang,
|
||||
Expected: "string",
|
||||
Value::Str(string) => Self(string.to_lowercase()),
|
||||
}
|
||||
|
||||
/// The direction of text and inline objects in their line.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||
pub struct HorizontalDir(pub Dir);
|
||||
@ -301,10 +276,7 @@ impl Resolve for Smart<HorizontalDir> {
|
||||
|
||||
fn resolve(self, styles: StyleChain) -> Self::Output {
|
||||
match self {
|
||||
Smart::Auto => match styles.get(TextNode::LANG) {
|
||||
Some(lang) => lang.dir(),
|
||||
None => Dir::LTR,
|
||||
},
|
||||
Smart::Auto => styles.get(TextNode::LANG).dir(),
|
||||
Smart::Custom(dir) => dir.0,
|
||||
}
|
||||
}
|
||||
|
@ -408,11 +408,7 @@ fn collect<'a>(
|
||||
if styles.get(TextNode::SMART_QUOTES) {
|
||||
// TODO: Also get region.
|
||||
let lang = styles.get(TextNode::LANG);
|
||||
let quotes = lang
|
||||
.as_ref()
|
||||
.map(|lang| Quotes::from_lang(lang.as_str(), ""))
|
||||
.unwrap_or_default();
|
||||
|
||||
let quotes = Quotes::from_lang(lang.as_str(), "");
|
||||
let peeked = iter.peek().and_then(|(child, _)| match child {
|
||||
ParChild::Text(text) => text.chars().next(),
|
||||
ParChild::Quote(_) => Some('"'),
|
||||
@ -750,7 +746,7 @@ fn breakpoints<'a>(p: &'a Preparation) -> Breakpoints<'a> {
|
||||
end: 0,
|
||||
mandatory: false,
|
||||
hyphenate: p.get_shared(TextNode::HYPHENATE),
|
||||
lang: p.get_shared(TextNode::LANG).map(Option::as_ref),
|
||||
lang: p.get_shared(TextNode::LANG),
|
||||
}
|
||||
}
|
||||
|
||||
@ -773,7 +769,7 @@ struct Breakpoints<'a> {
|
||||
/// Whether to hyphenate if it's the same for all children.
|
||||
hyphenate: Option<bool>,
|
||||
/// The text language if it's the same for all children.
|
||||
lang: Option<Option<&'a Lang>>,
|
||||
lang: Option<Lang>,
|
||||
}
|
||||
|
||||
impl Iterator for Breakpoints<'_> {
|
||||
@ -831,9 +827,9 @@ impl Breakpoints<'_> {
|
||||
|
||||
/// The text language at the given offset.
|
||||
fn lang_at(&self, offset: usize) -> Option<hypher::Lang> {
|
||||
let lang = self.lang.unwrap_or_else(|| {
|
||||
let lang = self.lang.or_else(|| {
|
||||
let shaped = self.p.find(offset)?.text()?;
|
||||
shaped.styles.get(TextNode::LANG).as_ref()
|
||||
Some(shaped.styles.get(TextNode::LANG))
|
||||
})?;
|
||||
|
||||
let bytes = lang.as_str().as_bytes().try_into().ok()?;
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
@ -6,13 +6,13 @@
|
||||
#set page(width: auto)
|
||||
#grid(
|
||||
columns: (70pt, 60pt),
|
||||
text(lang: "en")[Warm welcomes to Typst.],
|
||||
[Warm welcomes to Typst.],
|
||||
text(lang: "el")[διαμερίσματα. \ λατρευτός],
|
||||
)
|
||||
|
||||
---
|
||||
// Test disabling hyphenation for short passages.
|
||||
#set text(lang: "en", hyphenate: true)
|
||||
#set text(hyphenate: true)
|
||||
|
||||
Welcome to wonderful experiences. \
|
||||
Welcome to `wonderful` experiences. \
|
||||
@ -20,14 +20,14 @@ Welcome to #text(hyphenate: false)[wonderful] experiences. \
|
||||
Welcome to wonde#text(hyphenate: false)[rf]ul experiences. \
|
||||
|
||||
// Test enabling hyphenation for short passages.
|
||||
#set text(lang: "en", hyphenate: false)
|
||||
#set text(hyphenate: false)
|
||||
Welcome to wonderful experiences. \
|
||||
Welcome to wo#text(hyphenate: true)[nd]erful experiences. \
|
||||
|
||||
---
|
||||
// Hyphenate between shape runs.
|
||||
#set page(width: 80pt)
|
||||
#set text(lang: "en", hyphenate: true)
|
||||
#set text(hyphenate: true)
|
||||
It's a #emph[Tree]beard.
|
||||
|
||||
---
|
||||
@ -46,5 +46,5 @@ It's a #emph[Tree]beard.
|
||||
// do that. The test passes if there's just one hyphenation between
|
||||
// "net" and "works".
|
||||
#set page(width: 70pt)
|
||||
#set text(lang: "en", hyphenate: true)
|
||||
#set text(hyphenate: true)
|
||||
#h(6pt) networks, the rest.
|
||||
|
@ -1,7 +1,6 @@
|
||||
|
||||
---
|
||||
#set page(width: 180pt)
|
||||
#set text(lang: "en")
|
||||
#set par(
|
||||
justify: true,
|
||||
indent: 14pt,
|
||||
|
@ -1,6 +1,6 @@
|
||||
#set page(width: auto, height: auto)
|
||||
#set par(leading: 4pt, justify: true)
|
||||
#set text(lang: "en", family: "Latin Modern Roman")
|
||||
#set text(family: "Latin Modern Roman")
|
||||
|
||||
#let story = [
|
||||
In olden times when wishing still helped one, there lived a king whose
|
||||
|
@ -4,7 +4,7 @@
|
||||
// Test hanging punctuation.
|
||||
#set page(width: 130pt, margins: 15pt)
|
||||
#set par(justify: true, linebreaks: "simple")
|
||||
#set text(lang: "en", size: 9pt)
|
||||
#set text(size: 9pt)
|
||||
#rect(fill: rgb(repr(teal) + "00"), width: 100%)[
|
||||
This is a little bit of text that builds up to
|
||||
hang-ing hyphens and dash---es and then, you know,
|
||||
|
@ -24,12 +24,10 @@
|
||||
|
||||
---
|
||||
// Test single pair of quotes.
|
||||
#set text(lang: "en")
|
||||
""
|
||||
|
||||
---
|
||||
// Test sentences with numbers and apostrophes.
|
||||
#set text(lang: "en")
|
||||
The 5'11" 'quick' brown fox jumps over the "lazy" dog's ear.
|
||||
|
||||
He said "I'm a big fella."
|
||||
@ -40,7 +38,6 @@ The 5\'11\" 'quick\' brown fox jumps over the \"lazy" dog\'s ear.
|
||||
|
||||
---
|
||||
// Test turning smart quotes off.
|
||||
#set text(lang: "en")
|
||||
He's told some books contain questionable "example text".
|
||||
|
||||
#set text(smart-quotes: false)
|
||||
@ -48,7 +45,6 @@ He's told some books contain questionable "example text".
|
||||
|
||||
---
|
||||
// Test changing properties within text.
|
||||
#set text(lang: "en")
|
||||
"She suddenly started speaking french: #text(lang: "fr")['Je suis une banane.']" Roman told me.
|
||||
|
||||
Some people's thought on this would be #text(smart-quotes: false)["strange."]
|
||||
|
Loading…
x
Reference in New Issue
Block a user