Repository navigation
Add Simplified Chinese to the browser extension - #84
Merged
Merged
Conversation
The admin app gained Simplified Chinese in #71, and this extension's README promised the admin app's set, so a browser reporting zh-CN met a Chinese dashboard beside an English popup. zh.ts carries the 69 keys and joins SUPPORTED_LANGUAGES, whose table map is typed over the list, so a language registered without a table fails the typecheck instead of serving undefined. The language is the browser's, read by its primary subtag, so zh-CN, zh-SG and zh-Hans all resolve to zh. A script variant the set does not carry, zh-TW or zh-HK, also resolves to zh and reads Simplified: the extension carries one Chinese table and has no picker. detectLanguage() now says so, and a test pins it as a decision rather than an accident. The parity suites iterate SUPPORTED_LANGUAGES instead of naming id and sv, so this locale and any later one are covered the day they register, and the "accepts en, id, sv" case is replaced by every-language acceptance plus an assertion on the exact array. A new popup case renders with navigator.language set to zh-CN and asserts the Chinese labels, which is the whole path from the browser to the strings on screen. CHANGELOG.md gains the entry under Unreleased, RELEASE.md gains the locale step and the Chinese clip check on the two narrowest surfaces.
The README enumerated three languages and claimed they mirrored the admin app, which needed an edit for every locale on either side and read as false the moment one landed on only one side. It points at SUPPORTED_LANGUAGES now, which is also where a new locale is registered, and the test summary line no longer counts the languages it covers.
createTranslateFn passed a string replacement to String#replace, so "$&",
"$'", "$`" and "$1" in a parameter expanded instead of printing.
error.validation is a bare {message} passthrough for server text, so a
server message such as "Invalid character '$' in slug" rendered as
"Invalid character ' in slug". The admin app's createTranslateFn already
passes a replacer function for this reason; the extension now does too.
Review of #78. The extension routed zh-TW, zh-HK and zh-Hant to the Simplified table by primary subtag. chrome.i18n keeps zh_CN and zh_TW apart and falls back to default_locale instead of crossing scripts, and the extension has no picker to undo the choice. detectLanguage() now reads the script Intl.Locale#maximize() implies: zh-CN and zh-SG write Hans and read the zh table, while zh-TW, zh-HK, zh-MO and any Hant tag fall back to English. zh-Hans-HK names its script and stays Simplified. popup.html and options.html ship lang="en", so Chinese labels sat under an English tag. Screen readers chose an English voice and browsers chose the Han fallback font from the system default. Both entry points now set <html lang> (zh-Hans for the zh table), and the options page titles its tab with options.title instead of the hardcoded "shrtnr settings". tests/setup.ts exports setBrowserLanguage() beside the en-US pin it overrides, and the CHANGELOG entry drops the blank line that made the Unreleased list loose.
Review of #78. - The exact-array assertion on SUPPORTED_LANGUAGES becomes equality with the admin app's list, which backs the README's "same set as the admin app" and fails when either side registers a locale alone. - getTranslations(lang) is asserted to return the table module by reference, restoring the strength of the toBe(id) case the PR replaced with a _lang check. The duplicate _lang loop is gone. - The invalid-URL and save-failure cases split into it.each over every language (not the key) and over translated (not English), so the default-language rule stays in the translated list. - Options gets a zh-CN render case. It builds its own t, apart from the popup's, and nothing covered it. - popup.test.tsx uses setBrowserLanguage() instead of its own descriptor.
Review of #78. en, id and sv write your-shrtnr, shrtnr-anda and din-shrtnr in the base URL placeholder and the invalid-URL example. zh wrote a bare shrtnr.example.com, which reads as a real host to paste. It now writes ni-de-shrtnr, in the pinyin style of popup.slug.placeholder. A test pins the cue for every table and checks the invalid-URL example quotes the placeholder host. form.testOkCreateOnly joined two clauses with a 顿号, which separates list items, so it read as a list. It now carries the English contrast: 可以创建链接,但不能读取链接.
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
shrtnr | 87c7253 | Nov 03 2026, 01:43 AM |
DennisAlund
commented
Nov 3, 2026
DennisAlund
left a comment
Member
Author
There was a problem hiding this comment.
Code review (xhigh). 13 findings: 11 inline, 2 below because they sit outside the diff. Only the first inline finding changes what users see: CLDR sends several script-less Chinese region tags (zh-US, zh-GB, zh-AU, ...) to Traditional, so those browsers read English. The rest are test hygiene, cleanup, altitude and convention.
Checked: tsc --noEmit clean and vitest run 181 passing in browser-extensions/. Intl.Locale#maximize() behavior tested in Node 24 (ICU 78, CLDR 48).
Outside the diff
browser-extensions/src/options/Options.tsx:60:trebuilt on every render. Options callscreateTranslateFn(detectLanguage())in the component body, so since this PR every state change (loading→ready, shortcut resolved, saved, each ConfigForm re-render) constructs and maximizes anIntl.Localeagain. Popup memoizes the same value (Popup.tsx:105). UseuseMemo(..., []), a module-level constant, or the entry-point prop suggested onoptions/index.tsx:9.src/pages/layout.tsx:32: altitude: the admin app still tags the same tablelang="zh". The PR's reason forzh-Hans(screen-reader voice, Han font fallback) applies equally to the admin app, which renders the same Simplified table. On a system whose default Han font is Traditional or Japanese, the dashboard renders Simplified strings with the wrong glyph forms while the popup beside it is correct. The script mapping belongs beside the shared language registry, not in the extension alone.
Review of #84. The extension now tags its zh table zh-Hans so screen readers and Han font fallback match the Simplified strings. The admin app renders the same table and still emitted <html lang="zh">, so on a system whose default Han font is Traditional or Japanese the dashboard drew Simplified strings in the wrong glyph forms. documentLang() sits beside SUPPORTED_LANGUAGES in src/i18n and the layout uses it. The layout used to pass the same value to adminClientScriptPath(). It now keeps the language code for the script URL and the tagged value for the attribute. admin-assets.test.ts caught the first draft serving the English script to a zh viewer.
Review of #84. The extension's tests imported ../../src/i18n, so its CI job and store release depended on admin files under a tsconfig and install they were never checked against. The extension now imports nothing from outside browser-extensions/. src/__tests__/unit/extension-i18n-parity.test.ts imports the extension's i18n module instead. The root suite has every dependency installed. It checks three things: - the language set, compared as a set so a reorder does not fail it; - the <html lang> tag per language; - one interpolation contract run against both createTranslateFn copies ($ sequences, English fallback, missing key). The contract stands in for sharing the function. Sharing it would put admin source in the extension bundle, the coupling the move removes. The extension's i18n/index.ts drops its DOM dependency so the root typecheck accepts it: detectLanguage() reads navigator through globalThis, and applyDocumentLanguage() moves to i18n/document.ts.
Review of #84. detectLanguage() took the script from CLDR likely subtags, which send zh-US, zh-GB, zh-AU, zh-ID, zh-TH, zh-PH and zh-VN to Hant. Those browsers read English though the docs promised Simplified outside Taiwan, Hong Kong and Macau. The mapping also shifts between browser versions. The rule is now explicit: a script subtag wins, Chinese in TW, HK and MO is Traditional, and Chinese anywhere else is Simplified. matchLanguageTag() matches a table registered by language and script ("zh-Hant") before the bare-language table. A Traditional table now plugs in as the comment describes. Tests run the matcher against a list holding "zh-Hant" to pin that path before the table exists.
Review of #84. - Each entry point detects the language once and hands it to Popup or Options as a prop. <html lang>, the tab title and the strings on screen now come from one value instead of two calls. - Options memoizes t like Popup. It rebuilt t on every render, which since this PR also parsed an Intl.Locale each time. A test counts the navigator.language reads per mount. - document-language.test.tsx unmounts what each entry point renders, which cleanup() does not track. It also seeds <html lang> with a sentinel, so the English cases prove the entry point wrote "en". - Test titles drop their em dashes (CLAUDE.md writing rules).
Review of #84. "只有创建权限的密钥就足以缩短链接" mixed 只有…才 ("only if") with 就足以. It could read as "only a create-scoped key qualifies", which contradicts the next sentence about adding read for QR codes. It now reads "仅具备创建权限的密钥即可缩短链接", matching the English "A key with the create scope is enough".
Member
Author
|
The two review findings outside the diff:
|
DennisAlund
added a commit
that referenced
this pull request
Nov 3, 2026
…tches Patch release for the fixes merged since 0.43.0: the chart axis formats labels with Intl instead of string glue (#83), the admin's Chinese pages declare lang="zh-Hans" (#84), and hono and zod pick up published advisory fixes (#82) alongside the dependency upgrade (#81). The spec embeds info.version, so the bump refreshes x-spec-hash in all three SDKs. The emitted spec differs from 0.43.0 in info.version only: spec change does not affect SDK surface; bump hash to record review.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Takes over #78 by @myml and addresses the review on it. The first two commits are #78's commits cherry-picked, authored by the contributor. Two docs conflicts with
mainwere resolved in the cherry-pick:mainhad since condensed the README and pointed RELEASE.md atSUPPORTED_LANGUAGES. Both resolutions keepmain's wording plus #78's additions.What #78 adds
src/i18n/zh.tswith the popup, form, error and options strings, registered inSUPPORTED_LANGUAGES. The popup and options page follownavigator.language.Review findings addressed
$sequences in a parameter expand as replacement patternscreateTranslateFnpasses a replacer function, as the admin app does (469ab01).<html lang="en">on Chinese text; English tab title<html lang>(zh-Hansfor the zh table). Options titles its tab withoptions.title(7afb05a).zh-TW/zh-HK/zh-Hantrouted to SimplifieddetectLanguage()reads the scriptIntl.Locale#maximize()implies. Traditional tags fall back to English, aschrome.i18ndoes.zh-Hans-HKstays Simplified (7afb05a).SUPPORTED_LANGUAGES(b332f70).b332f70).main. The cherry-pick keepsmain's wording.getTranslationscheck weakened to_lang_langloop removed (b332f70).zh-CNcase asserts theshrtnr 设置heading (b332f70).browserLanguage()duplicatedsetBrowserLanguage()exported fromtests/setup.tsbeside theen-USpin (7afb05a).ni-de-shrtnr.example.com, pinned for every table by a test (fb1e079).可以创建链接,但不能读取链接(fb1e079).7afb05a).translatedrule re-derived inlineit.each(SUPPORTED_LANGUAGES)andit.each(translated)(b332f70).Second review round
zh-US,zh-GBand others to Traditional0c9cc1c: fixed rule. A script subtag wins, and TW/HK/MO are Traditional.zh-Hantpath the matcher cannot take0c9cc1c:matchLanguageTag()matches language plus script first.dfaba99: the parity checks move to the root suite, and the extension imports nothing outside its folder.dfaba99: sorted comparison.createTranslateFnduplicateddfaba99: one interpolation contract runs against both copies in the root suite.lang="zh"aa226b2:documentLang()insrc/i18n, and the layout uses it.tper renderdd08720: the entry point passeslangas a prop, and Options memoizest.dd08720:afterEachunmount and a sentinel seed.dd0872087c7253This PR now touches the admin app: zh pages emit
<html lang="zh-Hans">.Tests
Extension: 10 files, 190 pass.
yarn typecheckclean.yarn buildandyarn verify-buildpass for both targets.Root:
yarn test109 files, 1690 pass.tsc --noEmitclean (it now also typechecks the extension's i18n module).yarn e2e38 pass.Each commit passes the extension suite, both typechecks and the touched root suites on its own.
Mutation checks: these fail the suite when broken:
zhis dropped.The region rule,
matchLanguageTag, thelangprop, the Options memo and the admindocumentLangeach failed before their fix.Not run in a real browser. RELEASE.md step 4 covers the locale check before a tag.
Supersedes #78.