From 48c3d2d170b58cb27f5bca9784aac93adca3bc3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A4=EC=A2=85=EA=B7=BC?= <137996885+hi2242@users.noreply.github.com> Date: Sat, 17 Oct 2026 01:22:09 +0900 Subject: [PATCH] fix(website): use Noto Sans KR for Korean text (#3821) Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com> --- scripts/test-website.py | 10 ++++++++++ scripts/website_i18n.py | 8 +++++++- website/README.md | 7 +++++++ website/public/assets/styles.css | 10 +++++----- website/style-guide.md | 4 +++- 5 files changed, 32 insertions(+), 7 deletions(-) diff --git a/scripts/test-website.py b/scripts/test-website.py index 513139ff5f..0b60cff7e4 100644 --- a/scripts/test-website.py +++ b/scripts/test-website.py @@ -13,6 +13,7 @@ import unittest from unittest.mock import patch from http.server import ThreadingHTTPServer from urllib.error import HTTPError +from urllib.parse import urlsplit from urllib.request import urlopen import xml.etree.ElementTree as ET @@ -561,6 +562,15 @@ class WebsiteTranslationChecks(unittest.TestCase): parser = VERIFY.HtmlReferences() parser.feed(translated) self.assertEqual(parser.lang, "en" if parser.translation_fallback else code) + font_stylesheets = [ + node.attrs["href"] for node in Document(translated).nodes + if node.tag == "link" and node.attrs.get("rel") == "stylesheet" + and urlsplit(node.attrs.get("href", "")).netloc == "fonts.googleapis.com" + ] + self.assertEqual(len(font_stylesheets), int(parser.lang == "ko")) + if font_stylesheets: + self.assertIn("family=Noto+Sans+KR:wght@100..900", font_stylesheets[0]) + self.assertIn("&display=swap", font_stylesheets[0]) if code != "en" and not parser.translation_fallback: self.assertEqual(parser.direction, language["dir"]) if parser.translation_fallback: diff --git a/scripts/website_i18n.py b/scripts/website_i18n.py index 0f93546f01..bfcf4bb17a 100644 --- a/scripts/website_i18n.py +++ b/scripts/website_i18n.py @@ -545,7 +545,13 @@ def render_page(root, source_path, language, messages, available, figures, page_ source = replace_language_markup(source, root, relative, code, page_languages) runtime = {text: messages[("runtime", text)] for text in read_runtime(root, source_path)} payload = json.dumps(runtime, ensure_ascii=False).replace("<", "\\u003c").replace("&", "\\u0026") - source = source.replace("", f'\n ') + # Only Korean editions request the font stylesheet; English fallbacks keep + # their system fonts. The variable face also supplies the site's 650 weight. + font_stylesheet = ( + '\n ' + if code == "ko" else "" + ) + source = source.replace("", f'{font_stylesheet}\n ') return source diff --git a/website/README.md b/website/README.md index 2fb5436eb8..b29774aa18 100644 --- a/website/README.md +++ b/website/README.md @@ -14,6 +14,13 @@ downloads and check the site. There is no frontend framework, bundler or npm dependency. A build generates all editions in `website/_site/`. The resulting files work as ordinary static files; language switching needs no JavaScript. +Korean editions load the variable Noto Sans KR webfont through Google Fonts +(`fonts.googleapis.com` and `fonts.gstatic.com`) so the preferred Korean font +does not depend on fonts installed on the reader's PC. Other editions keep +system fonts. Text remains visible while the font loads (`display=swap`), and +local Korean fonts are used if Google Fonts is unavailable. The stylesheet is +added during translation rendering; generated `_site/` pages are not authored. + [style-guide.md](style-guide.md) explains the writing, page structure, figures, visual system and review process. Read it before adding or revising a page. diff --git a/website/public/assets/styles.css b/website/public/assets/styles.css index 6644a461e7..687e136d8a 100644 --- a/website/public/assets/styles.css +++ b/website/public/assets/styles.css @@ -2930,7 +2930,7 @@ html[dir="rtl"] .brand-name { } } -/* Select local fonts with the appropriate CJK glyph forms. */ +/* Select fonts with the appropriate CJK glyph forms. */ :lang(ja) { --font-sans: var(--font-latin), "Hiragino Sans", "Hiragino Kaku Gothic ProN", @@ -2957,11 +2957,11 @@ html[dir="rtl"] .brand-name { } :lang(ko) { --font-sans: - var(--font-latin), "Apple SD Gothic Neo", "Malgun Gothic", "Noto Sans KR", - "Noto Sans CJK KR", sans-serif; + "Noto Sans KR", "Noto Sans CJK KR", "Apple SD Gothic Neo", "Malgun Gothic", + var(--font-latin), sans-serif; --font-mono: - var(--font-code), "Apple SD Gothic Neo", "Malgun Gothic", "Noto Sans KR", - "Noto Sans CJK KR", monospace; + var(--font-code), "Noto Sans KR", "Noto Sans CJK KR", "Apple SD Gothic Neo", + "Malgun Gothic", monospace; } :lang(en) { --font-sans: var(--font-latin), sans-serif; diff --git a/website/style-guide.md b/website/style-guide.md index ec84f04f45..0c92e152c4 100644 --- a/website/style-guide.md +++ b/website/style-guide.md @@ -256,7 +256,9 @@ Embedded SVGs inherit the page's `color-scheme`; canvas demos listen for that differs from the system preference. Use the system sans-serif stack for prose and the shared monospace stack for -code. The content container is at most 1080px wide, with article text limited to +code. Korean editions use Noto Sans KR from Google Fonts, with local Korean +fonts as fallbacks; code retains the shared monospace fonts for Latin glyphs. +The content container is at most 1080px wide, with article text limited to 760px. The header has its own 1320px container so navigation and utility controls fit together. At 1100px and below, navigation moves to a separate row. Reuse the existing heading sizes, line heights, section spacing and component classes.