diff --git a/crates/export/src/as_html.rs b/crates/export/src/as_html.rs
index 6ae767d09c..45f6ecafca 100644
--- a/crates/export/src/as_html.rs
+++ b/crates/export/src/as_html.rs
@@ -8,14 +8,18 @@ pub(crate) fn allowed(c: char) -> bool {
matches!(c, '\t' | '\n' | '\r') || !(c.is_ascii_control() || c == '\u{FFFE}' || c == '\u{FFFF}')
}
+/// The longest language tag the exports write. BCP 47 sets no maximum (35 characters is only
+/// the least a protocol must hold), so this bounds only what a PDF's `/Lang` can put in a file.
+const LONGEST_TAG: usize = 255;
+
/// `lang` when it is a well formed language tag (BCP 47, as a PDF's `/Lang` holds one): a primary
/// language of two or three letters, then subtags of one to eight letters or digits, joined by
-/// hyphens, 35 characters at most. Anything else names no language.
+/// hyphens, [`LONGEST_TAG`] characters at most. Anything else names no language.
pub(crate) fn language_tag(lang: Option<&str>) -> Option<&str> {
let tag = lang?.trim();
let mut subtags = tag.split('-');
let primary = subtags.next()?;
- let well_formed = tag.len() <= 35
+ let well_formed = tag.len() <= LONGEST_TAG
&& (2..=3).contains(&primary.len())
&& primary.bytes().all(|b| b.is_ascii_alphabetic())
&& subtags.all(|s| (1..=8).contains(&s.len()) && s.bytes().all(|b| b.is_ascii_alphanumeric()));
diff --git a/crates/export/src/tests.rs b/crates/export/src/tests.rs
index d62148aaf2..ba5f95f66d 100644
--- a/crates/export/src/tests.rs
+++ b/crates/export/src/tests.rs
@@ -260,6 +260,11 @@ fn word_and_html_take_the_documents_language() {
assert!(styles(Some("ar-EG")).contains(&defaults("")), "right-to-left text");
assert!(styles(Some("HE")).contains(""), "right-to-left text");
assert!(html_with_language(&pages, "Report", Some("bg-BG")).starts_with("\n\n"));
+ // A tag longer than 35 characters is still a tag.
+ let long = "en-US-u-ca-gregory-nu-latn-co-phonebk";
+ assert!(styles(Some(long)).contains(&format!("")), "{long}");
+ assert!(html_with_language(&pages, "Report", Some(long)).contains(&format!("")), "{long}");
+ let too_long = format!("en{}", "-abcdefgh".repeat(29));
for lang in [
None,
Some(""),
@@ -270,7 +275,7 @@ fn word_and_html_take_the_documents_language() {
Some("bg--BG"),
Some("bg-"),
Some("en-US-abcdefghi"),
- Some("en-abcdefgh-abcdefgh-abcdefgh-abcdefgh"),
+ Some(too_long.as_str()),
Some("en-\"x\""),
Some("1a-BG"),
Some("\u{431}\u{433}-BG"),