Repository: typst/typst
File: crates/typst/src/text/smartquote.rs (Rust)

Code before:
        let region = region.as_ref().map(Region::as_str);

        let default = ("‘", "’", "“", "”");
        let low_high = ("‚", "‘", "„", "“");

        let (single_open, single_close, double_open, double_close) = match lang.as_str() {
            "de" if matches!(region, Some("CH" | "LI")) => match alternative {
                false => ("‹", "›", "«", "»"),
                true => low_high,
            },
            "cs" | "da" | "de" | "sk" | "sl" if alternative => ("›", "‹", "»", "«"),
            "cs" | "de" | "et" | "is" | "lt" | "lv" | "sk" | "sl" => low_high,
            "da" => ("‘", "’", "“", "”"),
            "fr" | "ru" if alternative => default,
            "fr" => ("‹\u{00A0}", "\u{00A0}›", "«\u{00A0}", "\u{00A0}»"),
            "fi" | "sv" if alternative => ("’", "’", "»", "»"),
            "bs" | "fi" | "sv" => ("’", "’", "”", "”"),
            "it" if alternative => default,
            "la" if alternative => ("“", "”", "«\u{202F}", "\u{202F}»"),
            "it" | "la" => ("“", "”", "«", "»"),
            "es" if matches!(region, Some("ES") | None) => ("“", "”", "«", "»"),
            "hu" | "pl" | "ro" => ("’", "’", "„", "”"),
            "no" | "nb" | "nn" if alternative => low_high,
            "ru" | "no" | "nb" | "nn" | "ua" => ("’", "’", "«", "»"),
            "gr" => ("‘", "’", "«", "»"),
            "he" => ("\u{05F3}", "\u{05F3}", "\u{05F4}", "\u{05F4}"),
            _ if lang.dir() == Dir::RTL => ("’", "‘", "”", "“"),
            _ => default,
        };

        fn inner_or_default<'s>(
            quotes: Smart<&'s SmartQuoteDict>,
            f: impl FnOnce(&'s SmartQuoteDict) -> Smart<&'s SmartQuoteSet>,
            default: [&'s str; 2],
        ) -> [&'s str; 2] {
            match quotes.and_then(f) {
                Smart::Auto => default,
                Smart::Custom(SmartQuoteSet { open, close }) => {
                    [open, close].map(|s| s.as_str())
                }
            }
        }

        let quotes = quotes.as_ref();
        let [single_open, single_close] =
            inner_or_default(quotes, |q| q.single.as_ref(), [single_open, single_close]);
        let [double_open, double_close] =
            inner_or_default(quotes, |q| q.double.as_ref(), [double_open, double_close]);

        Self {
            single_open,

Review comment (suggestion):
See my comment above, I believe the "normal" matching opening/closing quotes would be most appropriate here (unicode mirroring rules should already take care of making them appear the right way around to my understanding).

Code after:
        let region = region.as_ref().map(Region::as_str);

        let default = ("‘", "’", "“", "”");
        let low_high = ("‚", "‘", "„", "“");

        let (single_open, single_close, double_open, double_close) = match lang.as_str() {
            "de" if matches!(region, Some("CH" | "LI")) => match alternative {
                false => ("‹", "›", "«", "»"),
                true => low_high,
            },
            "cs" | "da" | "de" | "sk" | "sl" if alternative => ("›", "‹", "»", "«"),
            "cs" | "de" | "et" | "is" | "lt" | "lv" | "sk" | "sl" => low_high,
            "da" => ("‘", "’", "“", "”"),
            "fr" | "ru" if alternative => default,
            "fr" => ("‹\u{00A0}", "\u{00A0}›", "«\u{00A0}", "\u{00A0}»"),
            "fi" | "sv" if alternative => ("’", "’", "»", "»"),
            "bs" | "fi" | "sv" => ("’", "’", "”", "”"),
            "it" if alternative => default,
            "la" if alternative => ("“", "”", "«\u{202F}", "\u{202F}»"),
            "it" | "la" => ("“", "”", "«", "»"),
            "es" if matches!(region, Some("ES") | None) => ("“", "”", "«", "»"),
            "hu" | "pl" | "ro" => ("’", "’", "„", "”"),
            "no" | "nb" | "nn" if alternative => low_high,
            "ru" | "no" | "nb" | "nn" | "ua" => ("’", "’", "«", "»"),
            "gr" => ("‘", "’", "«", "»"),
            "he" => ("’", "’", "”", "”"),
            _ if lang.dir() == Dir::RTL => ("’", "‘", "”", "“"),
            _ => default,
        };

        fn inner_or_default<'s>(
            quotes: Smart<&'s SmartQuoteDict>,
            f: impl FnOnce(&'s SmartQuoteDict) -> Smart<&'s SmartQuoteSet>,
            default: [&'s str; 2],
        ) -> [&'s str; 2] {
            match quotes.and_then(f) {
                Smart::Auto => default,
                Smart::Custom(SmartQuoteSet { open, close }) => {
                    [open, close].map(|s| s.as_str())
                }
            }
        }

        let quotes = quotes.as_ref();
        let [single_open, single_close] =
            inner_or_default(quotes, |q| q.single.as_ref(), [single_open, single_close]);
        let [double_open, double_close] =
            inner_or_default(quotes, |q| q.double.as_ref(), [double_open, double_close]);

        Self {
            single_open,