Keep Latin families first and read only the tagged language for CJK variants

This commit is contained in:
ZacharyZhang-NY committed 2026-10-05 23:48:00 -04:00
1 parent a62771e2b4
commit ab028e80eb
3 files changed
+115 -60

No files matched your search

+44 -59
View File
@@ -1,20 +1,10 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd"> <!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig> <fontconfig>
<!-- CJK: give language-tagged text the Noto CJK variant that matches its <!-- CJK: fold the language tag onto one tag per Noto CJK variant -->
language. noto-fonts-cjk ships all five, Han glyph shapes differ between
them, and the generic assigns below would otherwise hand tagged CJK text
to families with no Han coverage, leaving the variant to a charset-scan
lottery. Chinese arrives under many tags (W3C recommends the zh-Hans and
zh-Hant script forms), so the tags are first folded onto one tag per
variant. Order is load-bearing: a bare zh and a bare zh-hant satisfy
the contains test of every one of their extensions, so both are pinned
by exact matches before the extension folds run. Cantonese under its
own primary tag folds to Hong Kong forms, except explicitly
Simplified Cantonese, which folds to SC. -->
<match target="pattern"> <match target="pattern">
<test name="lang" compare="eq"> <test name="lang" qual="first" compare="eq">
<string>zh</string> <string>zh</string>
</test> </test>
<edit name="lang" mode="assign"> <edit name="lang" mode="assign">
@@ -23,7 +13,7 @@
</match> </match>
<match target="pattern"> <match target="pattern">
<test name="lang" compare="eq"> <test name="lang" qual="first" compare="eq">
<string>zh-hant</string> <string>zh-hant</string>
</test> </test>
<edit name="lang" mode="assign"> <edit name="lang" mode="assign">
@@ -32,7 +22,7 @@
</match> </match>
<match target="pattern"> <match target="pattern">
<test name="lang" compare="contains"> <test name="lang" qual="first" compare="contains">
<string>zh-hant-hk</string> <string>zh-hant-hk</string>
</test> </test>
<edit name="lang" mode="assign"> <edit name="lang" mode="assign">
@@ -41,7 +31,7 @@
</match> </match>
<match target="pattern"> <match target="pattern">
<test name="lang" compare="contains"> <test name="lang" qual="first" compare="contains">
<string>zh-hant-mo</string> <string>zh-hant-mo</string>
</test> </test>
<edit name="lang" mode="assign"> <edit name="lang" mode="assign">
@@ -50,7 +40,7 @@
</match> </match>
<match target="pattern"> <match target="pattern">
<test name="lang" compare="contains"> <test name="lang" qual="first" compare="contains">
<string>zh-hant</string> <string>zh-hant</string>
</test> </test>
<edit name="lang" mode="assign"> <edit name="lang" mode="assign">
@@ -59,7 +49,7 @@
</match> </match>
<match target="pattern"> <match target="pattern">
<test name="lang" compare="contains"> <test name="lang" qual="first" compare="contains">
<string>zh-hans</string> <string>zh-hans</string>
</test> </test>
<edit name="lang" mode="assign"> <edit name="lang" mode="assign">
@@ -68,7 +58,7 @@
</match> </match>
<match target="pattern"> <match target="pattern">
<test name="lang" compare="contains"> <test name="lang" qual="first" compare="contains">
<string>zh-sg</string> <string>zh-sg</string>
</test> </test>
<edit name="lang" mode="assign"> <edit name="lang" mode="assign">
@@ -77,7 +67,7 @@
</match> </match>
<match target="pattern"> <match target="pattern">
<test name="lang" compare="contains"> <test name="lang" qual="first" compare="contains">
<string>zh-mo</string> <string>zh-mo</string>
</test> </test>
<edit name="lang" mode="assign"> <edit name="lang" mode="assign">
@@ -86,7 +76,7 @@
</match> </match>
<match target="pattern"> <match target="pattern">
<test name="lang" compare="eq"> <test name="lang" qual="first" compare="eq">
<string>yue</string> <string>yue</string>
</test> </test>
<edit name="lang" mode="assign"> <edit name="lang" mode="assign">
@@ -95,7 +85,7 @@
</match> </match>
<match target="pattern"> <match target="pattern">
<test name="lang" compare="contains"> <test name="lang" qual="first" compare="contains">
<string>yue-hans</string> <string>yue-hans</string>
</test> </test>
<edit name="lang" mode="assign"> <edit name="lang" mode="assign">
@@ -104,7 +94,7 @@
</match> </match>
<match target="pattern"> <match target="pattern">
<test name="lang" compare="contains"> <test name="lang" qual="first" compare="contains">
<string>yue-cn</string> <string>yue-cn</string>
</test> </test>
<edit name="lang" mode="assign"> <edit name="lang" mode="assign">
@@ -113,7 +103,7 @@
</match> </match>
<match target="pattern"> <match target="pattern">
<test name="lang" compare="contains"> <test name="lang" qual="first" compare="contains">
<string>yue</string> <string>yue</string>
</test> </test>
<edit name="lang" mode="assign"> <edit name="lang" mode="assign">
@@ -121,189 +111,184 @@
</edit> </edit>
</match> </match>
<!-- Then append that variant after the generic family it falls back from -->
<!-- Each variant is then prepended for its tag, ahead of the assigns below
because these test the generic names those assigns replace. Unlike the
Arabic rule further down, these carry a family test, so the prepend
lands just ahead of the matched generic and a concrete family the app
asked for stays in front of the variant. -->
<match target="pattern"> <match target="pattern">
<test name="lang" compare="contains"> <test name="lang" qual="first" compare="contains">
<string>zh-cn</string> <string>zh-cn</string>
</test> </test>
<test name="family"> <test name="family">
<string>sans-serif</string> <string>sans-serif</string>
</test> </test>
<edit name="family" mode="prepend" binding="strong"> <edit name="family" mode="append" binding="strong">
<string>Noto Sans CJK SC</string> <string>Noto Sans CJK SC</string>
</edit> </edit>
</match> </match>
<match target="pattern"> <match target="pattern">
<test name="lang" compare="contains"> <test name="lang" qual="first" compare="contains">
<string>zh-cn</string> <string>zh-cn</string>
</test> </test>
<test name="family"> <test name="family">
<string>serif</string> <string>serif</string>
</test> </test>
<edit name="family" mode="prepend" binding="strong"> <edit name="family" mode="append" binding="strong">
<string>Noto Serif CJK SC</string> <string>Noto Serif CJK SC</string>
</edit> </edit>
</match> </match>
<match target="pattern"> <match target="pattern">
<test name="lang" compare="contains"> <test name="lang" qual="first" compare="contains">
<string>zh-cn</string> <string>zh-cn</string>
</test> </test>
<test name="family"> <test name="family">
<string>monospace</string> <string>monospace</string>
</test> </test>
<edit name="family" mode="prepend" binding="strong"> <edit name="family" mode="append" binding="strong">
<string>Noto Sans Mono CJK SC</string> <string>Noto Sans Mono CJK SC</string>
</edit> </edit>
</match> </match>
<match target="pattern"> <match target="pattern">
<test name="lang" compare="contains"> <test name="lang" qual="first" compare="contains">
<string>zh-hk</string> <string>zh-hk</string>
</test> </test>
<test name="family"> <test name="family">
<string>sans-serif</string> <string>sans-serif</string>
</test> </test>
<edit name="family" mode="prepend" binding="strong"> <edit name="family" mode="append" binding="strong">
<string>Noto Sans CJK HK</string> <string>Noto Sans CJK HK</string>
</edit> </edit>
</match> </match>
<match target="pattern"> <match target="pattern">
<test name="lang" compare="contains"> <test name="lang" qual="first" compare="contains">
<string>zh-hk</string> <string>zh-hk</string>
</test> </test>
<test name="family"> <test name="family">
<string>serif</string> <string>serif</string>
</test> </test>
<edit name="family" mode="prepend" binding="strong"> <edit name="family" mode="append" binding="strong">
<string>Noto Serif CJK HK</string> <string>Noto Serif CJK HK</string>
</edit> </edit>
</match> </match>
<match target="pattern"> <match target="pattern">
<test name="lang" compare="contains"> <test name="lang" qual="first" compare="contains">
<string>zh-hk</string> <string>zh-hk</string>
</test> </test>
<test name="family"> <test name="family">
<string>monospace</string> <string>monospace</string>
</test> </test>
<edit name="family" mode="prepend" binding="strong"> <edit name="family" mode="append" binding="strong">
<string>Noto Sans Mono CJK HK</string> <string>Noto Sans Mono CJK HK</string>
</edit> </edit>
</match> </match>
<match target="pattern"> <match target="pattern">
<test name="lang" compare="contains"> <test name="lang" qual="first" compare="contains">
<string>zh-tw</string> <string>zh-tw</string>
</test> </test>
<test name="family"> <test name="family">
<string>sans-serif</string> <string>sans-serif</string>
</test> </test>
<edit name="family" mode="prepend" binding="strong"> <edit name="family" mode="append" binding="strong">
<string>Noto Sans CJK TC</string> <string>Noto Sans CJK TC</string>
</edit> </edit>
</match> </match>
<match target="pattern"> <match target="pattern">
<test name="lang" compare="contains"> <test name="lang" qual="first" compare="contains">
<string>zh-tw</string> <string>zh-tw</string>
</test> </test>
<test name="family"> <test name="family">
<string>serif</string> <string>serif</string>
</test> </test>
<edit name="family" mode="prepend" binding="strong"> <edit name="family" mode="append" binding="strong">
<string>Noto Serif CJK TC</string> <string>Noto Serif CJK TC</string>
</edit> </edit>
</match> </match>
<match target="pattern"> <match target="pattern">
<test name="lang" compare="contains"> <test name="lang" qual="first" compare="contains">
<string>zh-tw</string> <string>zh-tw</string>
</test> </test>
<test name="family"> <test name="family">
<string>monospace</string> <string>monospace</string>
</test> </test>
<edit name="family" mode="prepend" binding="strong"> <edit name="family" mode="append" binding="strong">
<string>Noto Sans Mono CJK TC</string> <string>Noto Sans Mono CJK TC</string>
</edit> </edit>
</match> </match>
<match target="pattern"> <match target="pattern">
<test name="lang" compare="contains"> <test name="lang" qual="first" compare="contains">
<string>ja</string> <string>ja</string>
</test> </test>
<test name="family"> <test name="family">
<string>sans-serif</string> <string>sans-serif</string>
</test> </test>
<edit name="family" mode="prepend" binding="strong"> <edit name="family" mode="append" binding="strong">
<string>Noto Sans CJK JP</string> <string>Noto Sans CJK JP</string>
</edit> </edit>
</match> </match>
<match target="pattern"> <match target="pattern">
<test name="lang" compare="contains"> <test name="lang" qual="first" compare="contains">
<string>ja</string> <string>ja</string>
</test> </test>
<test name="family"> <test name="family">
<string>serif</string> <string>serif</string>
</test> </test>
<edit name="family" mode="prepend" binding="strong"> <edit name="family" mode="append" binding="strong">
<string>Noto Serif CJK JP</string> <string>Noto Serif CJK JP</string>
</edit> </edit>
</match> </match>
<match target="pattern"> <match target="pattern">
<test name="lang" compare="contains"> <test name="lang" qual="first" compare="contains">
<string>ja</string> <string>ja</string>
</test> </test>
<test name="family"> <test name="family">
<string>monospace</string> <string>monospace</string>
</test> </test>
<edit name="family" mode="prepend" binding="strong"> <edit name="family" mode="append" binding="strong">
<string>Noto Sans Mono CJK JP</string> <string>Noto Sans Mono CJK JP</string>
</edit> </edit>
</match> </match>
<match target="pattern"> <match target="pattern">
<test name="lang" compare="contains"> <test name="lang" qual="first" compare="contains">
<string>ko</string> <string>ko</string>
</test> </test>
<test name="family"> <test name="family">
<string>sans-serif</string> <string>sans-serif</string>
</test> </test>
<edit name="family" mode="prepend" binding="strong"> <edit name="family" mode="append" binding="strong">
<string>Noto Sans CJK KR</string> <string>Noto Sans CJK KR</string>
</edit> </edit>
</match> </match>
<match target="pattern"> <match target="pattern">
<test name="lang" compare="contains"> <test name="lang" qual="first" compare="contains">
<string>ko</string> <string>ko</string>
</test> </test>
<test name="family"> <test name="family">
<string>serif</string> <string>serif</string>
</test> </test>
<edit name="family" mode="prepend" binding="strong"> <edit name="family" mode="append" binding="strong">
<string>Noto Serif CJK KR</string> <string>Noto Serif CJK KR</string>
</edit> </edit>
</match> </match>
<match target="pattern"> <match target="pattern">
<test name="lang" compare="contains"> <test name="lang" qual="first" compare="contains">
<string>ko</string> <string>ko</string>
</test> </test>
<test name="family"> <test name="family">
<string>monospace</string> <string>monospace</string>
</test> </test>
<edit name="family" mode="prepend" binding="strong"> <edit name="family" mode="append" binding="strong">
<string>Noto Sans Mono CJK KR</string> <string>Noto Sans Mono CJK KR</string>
</edit> </edit>
</match> </match>
+1 -1
View File
@@ -16,7 +16,7 @@ Progress source of truth for the `omarchycn` branches of `omarchy`, `omarchy-iso
- [x] T0 Branches and this list - [x] T0 Branches and this list
- [x] T1 China package sources (PRD 1): region state, `omarchy-region` reader, `default/region/china/pacman.sed`, applied by `omarchy-refresh-pacman` and `install/post-install/pacman.sh`; tests - [x] T1 China package sources (PRD 1): region state, `omarchy-region` reader, `default/region/china/pacman.sed`, applied by `omarchy-refresh-pacman` and `install/post-install/pacman.sh`; tests
- [x] T2 Region choice in the installer (PRD 2): `omarchy_prompt_region` in `setup-form.sh`, ISO configurator screen, orchestrator passes `OMARCHY_REGION`, `install/config/region.sh` records it - [x] T2 Region choice in the installer (PRD 2): `omarchy_prompt_region` in `setup-form.sh`, ISO configurator screen, orchestrator passes `OMARCHY_REGION`, `install/config/region.sh` records it
- [ ] T3 Input method (PRD 3): rime-ice-installer patch and PKGBUILD in omarchy-pkgs, IME packages in `omarchy-other.packages`, install-time package add, user step runs the installer from bundled assets, ISO build bundles assets and the package - [x] T3 Input method (PRD 3): rime-ice-installer patch and PKGBUILD in omarchy-pkgs, IME packages in `omarchy-other.packages`, install-time package add, user step runs the installer from bundled assets, ISO build bundles assets and the package
- [ ] T4 CJK fontconfig fix (PRD 4): rebuild PR 9693 on the current base - [ ] T4 CJK fontconfig fix (PRD 4): rebuild PR 9693 on the current base
- [ ] T5 AI (PRD 5): Dim and Kimi Code for every region (rebuild PR 9694), Claude Code third-party provider setup shown only in China - [ ] T5 AI (PRD 5): Dim and Kimi Code for every region (rebuild PR 9694), Claude Code third-party provider setup shown only in China
- [ ] T6 Developer mirrors (PRD 6): npm, PyPI/uv, Go, rustup, crates.io, RubyGems, Maven via system drop-ins under `default/region/china/` - [ ] T6 Developer mirrors (PRD 6): npm, PyPI/uv, Go, rustup, crates.io, RubyGems, Maven via system drop-ins under `default/region/china/`
+70
View File
@@ -0,0 +1,70 @@
#!/bin/bash
set -euo pipefail
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
if ! command -v fc-match >/dev/null || [[ $(fc-list : file) != *NotoSansCJK-Regular.ttc* ]]; then
skip "fontconfig CJK variants need fc-match and noto-fonts-cjk"
exit 0
fi
test_tmp=$(mktemp -d)
trap 'rm -rf "$test_tmp"' EXIT
{
echo '<?xml version="1.0"?><!DOCTYPE fontconfig SYSTEM "fonts.dtd"><fontconfig>'
echo "<dir>/usr/share/fonts</dir><cachedir>$test_tmp/cache</cachedir>"
for conf in /etc/fonts/conf.d/*.conf; do
if [[ ${conf##*/} == "50-omarchy.conf" ]]; then
echo "<include>$ROOT/default/fontconfig/conf.avail/50-omarchy.conf</include>"
else
echo "<include>$conf</include>"
fi
done
echo '</fontconfig>'
} >"$test_tmp/fonts.conf"
export FONTCONFIG_FILE="$test_tmp/fonts.conf"
fc-cache >/dev/null
declare -A latin=([sans-serif]="Liberation Sans" [serif]="Liberation Serif" [monospace]="JetBrainsMono Nerd Font")
declare -A cjk=([sans-serif]="Noto Sans CJK" [serif]="Noto Serif CJK" [monospace]="Noto Sans Mono CJK")
declare -A variant=(
[zh]=SC [zh-cn]=SC [zh-sg]=SC [zh-hans]=SC [zh-hans-cn]=SC [zh-hans-hk]=SC
[zh-tw]=TC [zh-hant]=TC [zh-hant-tw]=TC
[zh-hk]=HK [zh-mo]=HK [zh-hant-hk]=HK [zh-hant-mo]=HK
[yue]=HK [yue-hk]=HK [yue-hant]=HK [yue-hant-hk]=HK [yue-hant-cn]=HK
[yue-hans]=SC [yue-hans-cn]=SC [yue-hans-hk]=SC [yue-cn]=SC
[ja]=JP [ja-jp]=JP [ko]=KR [ko-kr]=KR
)
family() { LANG=$1 fc-match -f '%{family[0]}' "$2"; }
for locale in C.UTF-8 zh_CN.UTF-8; do
for tag in "${!variant[@]}"; do
for generic in sans-serif serif monospace; do
actual=$(family "$locale" "$generic:lang=$tag")
[[ $actual == "${latin[$generic]}" ]] || fail "$locale $generic:lang=$tag keeps Latin in ${latin[$generic]}" "$actual"
actual=$(family "$locale" "$generic:lang=$tag:charset=9aa8")
expected="${cjk[$generic]} ${variant[$tag]}"
[[ $actual == "$expected" ]] || fail "$locale $generic:lang=$tag sets Han in $expected" "$actual"
done
done
pass "$locale: tagged CJK keeps Omarchy's Latin families and sets Han in the tag's variant"
done
for generic in sans-serif serif monospace; do
actual=$(family zh_CN.UTF-8 "$generic")
[[ $actual == "${latin[$generic]}" ]] || fail "zh_CN untagged $generic keeps ${latin[$generic]}" "$actual"
actual=$(family zh_CN.UTF-8 "$generic:charset=9aa8")
[[ $actual == "${cjk[$generic]} SC" ]] || fail "zh_CN untagged $generic sets Han in SC" "$actual"
done
pass "a Chinese locale keeps Omarchy's Latin families and sets untagged Han in SC"
[[ $(LANG=C.UTF-8 fc-pattern --config "sans-serif:lang=ja" family) == *'"Noto Sans CJK JP"(s)'* ]] ||
fail "lang=ja binds its variant strongly, so the prefix check below can see one"
for tag in jam kok; do
actual=$(LANG=C.UTF-8 fc-pattern --config "sans-serif:lang=$tag" family)
[[ ! $actual =~ CJK\ [A-Z]+\"\(s\) ]] || fail "lang=$tag is not taken for Japanese or Korean" "$actual"
done
pass "languages that only share a prefix with ja or ko get no CJK variant"