fix(i18n): localize the Link Routing description in browser settings (#9444)

The description was assembled from a bare template literal, so it stayed
English under every language pack. It now lives in the catalog as two entries —
description with a {{shortcut}} placeholder and descriptionBase for the
invert-on variant — following the copy-module pattern from #10991.

Co-authored-by: 조재중 <126754298+m-a-king@users.noreply.github.com>
This commit is contained in:
조재중
2026-08-04 02:57:08 -07:00
committed by Neil
parent 853c2043b9
commit ae3fa002c3
11 changed files with 188 additions and 21 deletions
@@ -4,7 +4,7 @@ import { SettingsSwitchRow } from './SettingsFormControls'
import {
getLinkRoutingModifierDescription,
getLinkRoutingModifierTitle
} from './browser-link-routing-modifier-copy'
} from './browser-link-routing-copy'
type BrowserLinkRoutingModifierSettingProps = {
settings: Pick<GlobalSettings, 'openLinksInApp' | 'openLinksInAppModifierInverts'>
@@ -2,7 +2,8 @@ import { useCallback, useEffect, useMemo, useRef, useState, type MutableRefObjec
import type { GlobalSettings } from '../../../../shared/types'
import { useAppStore } from '../../store'
import { matchesSettingsSearch } from './settings-search'
import { getBrowserPaneSearchEntries, getBrowserLinkRoutingDescription } from './browser-search'
import { getBrowserPaneSearchEntries } from './browser-search'
import { getBrowserLinkRoutingDescription } from './browser-link-routing-copy'
import { getBrowserUsePaneSearchEntries } from './browser-use-search'
import { getBrowserPaneCombinedSearchEntries } from './browser-pane-search'
import { BrowserHomePageSetting } from './BrowserHomePageSetting'
@@ -1,5 +1,30 @@
import { translate } from '@/i18n/i18n'
export function getBrowserLinkRoutingShortcutLabel(platform: { isMac: boolean }): string {
return platform.isMac ? '⇧⌘-click' : 'Shift+Ctrl+click'
}
// Why: "always" stops being true once inverting is on, so only then does the nested
// row take over the chord sentence — with it off this reads exactly as it always has.
// Each variant is a complete catalog entry: stitched suffixes break in locales whose
// sentence joining differs from English.
export function getBrowserLinkRoutingDescription(
platform: { isMac: boolean },
modifierInverts = false
): string {
if (modifierInverts) {
return translate(
'auto.components.settings.BrowserLinkRoutingSetting.descriptionBase',
"Open http(s) links in Orca's built-in browser — from the terminal, markdown, and the editor."
)
}
return translate(
'auto.components.settings.BrowserLinkRoutingSetting.description',
"Open http(s) links in Orca's built-in browser — from the terminal, markdown, and the editor. {{shortcut}} always uses your system browser.",
{ shortcut: getBrowserLinkRoutingShortcutLabel(platform) }
)
}
/**
* Title and description both name the destination the modifier reaches, which is
* the opposite of wherever Link Routing points. Kept out of the component so the
@@ -0,0 +1,55 @@
import { afterEach, describe, expect, it } from 'vitest'
import { i18n } from '@/i18n/i18n'
import { getBrowserLinkRoutingDescription } from './browser-link-routing-copy'
// The Link Routing description was a bare template literal, so it stayed English in every
// locale while its own title and the rest of the pane translated. It carries the platform
// shortcut label, so each locale must interpolate it rather than embed a hardcoded modifier.
const NON_ENGLISH_LOCALES = ['es', 'ja', 'ko', 'zh'] as const
const SHORTCUT_BY_PLATFORM = {
mac: '⇧⌘-click',
other: 'Shift+Ctrl+click'
} as const
describe('Link Routing description localization', () => {
afterEach(async () => {
await i18n.changeLanguage('en')
})
it('translates the description in every non-English locale on both platforms', async () => {
const english = {
mac: getBrowserLinkRoutingDescription({ isMac: true }),
other: getBrowserLinkRoutingDescription({ isMac: false })
}
for (const locale of NON_ENGLISH_LOCALES) {
await i18n.changeLanguage(locale)
for (const [platform, shortcut] of Object.entries(SHORTCUT_BY_PLATFORM)) {
const description = getBrowserLinkRoutingDescription({ isMac: platform === 'mac' })
expect(description, `${locale}/${platform} fell back to English`).not.toBe(
english[platform as keyof typeof english]
)
// The label is a key symbol, not prose: it stays literal in every locale.
expect(description, `${locale}/${platform} lost the shortcut label`).toContain(shortcut)
expect(description, `${locale}/${platform} leaked a placeholder`).not.toMatch(/\{\{.+?\}\}/)
}
}
})
it('keeps each platform label out of the other platform copy', async () => {
for (const locale of ['en', ...NON_ENGLISH_LOCALES]) {
await i18n.changeLanguage(locale)
expect(getBrowserLinkRoutingDescription({ isMac: true })).not.toContain(
SHORTCUT_BY_PLATFORM.other
)
expect(getBrowserLinkRoutingDescription({ isMac: false })).not.toContain(
SHORTCUT_BY_PLATFORM.mac
)
}
})
})
@@ -1,12 +1,25 @@
import { describe, expect, it } from 'vitest'
import { getBrowserLinkRoutingDescription, getBrowserPaneSearchEntries } from './browser-search'
import { beforeEach, describe, expect, it } from 'vitest'
import ko from '@/i18n/locales/ko.json'
import { i18n } from '@/i18n/i18n'
import { getBrowserPaneSearchEntries } from './browser-search'
import {
getBrowserLinkRoutingDescription,
getBrowserLinkRoutingShortcutLabel,
getLinkRoutingModifierDescription,
getLinkRoutingModifierTitle
} from './browser-link-routing-modifier-copy'
} from './browser-link-routing-copy'
describe('browser settings search copy', () => {
it('uses macOS shortcut keywords for Link Routing search metadata', () => {
it('uses macOS shortcut symbols for Link Routing copy and search metadata', () => {
expect(getBrowserLinkRoutingShortcutLabel({ isMac: true })).toBe('⇧⌘-click')
const description = getBrowserLinkRoutingDescription({ isMac: true })
expect(description).toContain('⇧⌘-click')
expect(description).not.toContain('Cmd/Ctrl')
// The copy is translated: a leaked `{{...}}` means the interpolation name drifted from the catalog.
expect(description).not.toMatch(/\{\{.+?\}\}/)
const linkRoutingEntry = getBrowserPaneSearchEntries({ isMac: true }).find(
(entry) => entry.title === 'Link Routing'
)
@@ -20,7 +33,14 @@ describe('browser settings search copy', () => {
expect(defaultZoomEntry?.keywords).toContain('zoom')
})
it('uses Ctrl shortcut keywords for Link Routing search metadata off macOS', () => {
it('uses Ctrl shortcut text for Link Routing copy and search metadata off macOS', () => {
expect(getBrowserLinkRoutingShortcutLabel({ isMac: false })).toBe('Shift+Ctrl+click')
const description = getBrowserLinkRoutingDescription({ isMac: false })
expect(description).toContain('Shift+Ctrl+click')
expect(description).not.toContain('Cmd/Ctrl')
expect(description).not.toMatch(/\{\{.+?\}\}/)
const linkRoutingEntry = getBrowserPaneSearchEntries({ isMac: false }).find(
(entry) => entry.title === 'Link Routing'
)
@@ -102,3 +122,61 @@ describe('browser link routing modifier copy', () => {
expect(entry?.keywords).toContain(getLinkRoutingModifierTitle(true))
})
})
// The bug this file guards: the Link Routing description was a bare template
// literal, so it stayed English in every locale. Asserting only "no {{...}} leaked"
// cannot catch that — the English literal has no placeholder either.
describe('Link Routing description localization', () => {
const KEY = 'auto.components.settings.BrowserLinkRoutingSetting.description'
const BASE_KEY = 'auto.components.settings.BrowserLinkRoutingSetting.descriptionBase'
beforeEach(async () => {
await i18n.changeLanguage('en')
})
it('renders the Korean copy with the shortcut interpolated', async () => {
const koCopy = (
ko.auto.components.settings.BrowserLinkRoutingSetting as unknown as Record<string, string>
)['description']
expect(koCopy).toBeTruthy()
expect(koCopy).toContain('{{shortcut}}')
i18n.addResourceBundle('ko', 'translation', ko, true, true)
await i18n.changeLanguage('ko')
const description = getBrowserLinkRoutingDescription({ isMac: true })
expect(description).toBe(koCopy.replace('{{shortcut}}', '⇧⌘-click'))
expect(description).not.toMatch(/\{\{.+?\}\}/)
// Fails when the copy is a hardcoded English literal.
expect(description).not.toContain("Orca's built-in browser")
// The entry title is localized too, so match on the description instead.
const entry = getBrowserPaneSearchEntries({ isMac: true }).find(
(item) => item.description === description
)
expect(entry).toBeDefined()
await i18n.changeLanguage('en')
expect(getBrowserLinkRoutingDescription({ isMac: true })).toContain("Orca's built-in browser")
})
it('renders the Korean copy for the invert-on variant', async () => {
const koBase = (
ko.auto.components.settings.BrowserLinkRoutingSetting as unknown as Record<string, string>
)['descriptionBase']
expect(koBase).toBeTruthy()
i18n.addResourceBundle('ko', 'translation', ko, true, true)
await i18n.changeLanguage('ko')
const description = getBrowserLinkRoutingDescription({ isMac: true }, true)
expect(description).toBe(koBase)
// Fails when the invert-on branch regresses to a hardcoded English literal.
expect(description).not.toContain("Orca's built-in browser")
})
it('uses the catalog key rather than an inline literal', () => {
expect(i18n.exists(KEY)).toBe(true)
expect(i18n.exists(BASE_KEY)).toBe(true)
})
})
@@ -2,9 +2,10 @@ import type { SettingsSearchEntry } from './settings-search'
import { translate } from '@/i18n/i18n'
import { translateSearchKeyword } from './settings-search-keywords'
import {
getBrowserLinkRoutingDescription,
getLinkRoutingModifierDescription,
getLinkRoutingModifierTitle
} from './browser-link-routing-modifier-copy'
} from './browser-link-routing-copy'
type BrowserShortcutPlatform = {
isMac: boolean
@@ -16,19 +17,6 @@ function getDefaultBrowserShortcutPlatform(): BrowserShortcutPlatform {
}
}
// Why: "always" stops being true once inverting is on, so only then does the nested
// row take over the chord sentence — with it off this reads exactly as it always has.
export function getBrowserLinkRoutingDescription(
platform: BrowserShortcutPlatform = getDefaultBrowserShortcutPlatform(),
modifierInverts = false
): string {
const base =
"Open http(s) links in Orca's built-in browser — from the terminal, markdown, and the editor."
return modifierInverts
? base
: `${base} ${platform.isMac ? '⇧⌘-click' : 'Shift+Ctrl+click'} always uses your system browser.`
}
export function getBrowserPaneSearchEntries(
platform: BrowserShortcutPlatform = getDefaultBrowserShortcutPlatform()
): SettingsSearchEntry[] {
+4
View File
@@ -9827,6 +9827,10 @@
"unpackedHelp": "Development builds must be updated from their source checkout.",
"legacyHelp": "Update this server manually once to enable remote updates."
},
"BrowserLinkRoutingSetting": {
"description": "Open http(s) links in Orca's built-in browser — from the terminal, markdown, and the editor. {{shortcut}} always uses your system browser.",
"descriptionBase": "Open http(s) links in Orca's built-in browser — from the terminal, markdown, and the editor."
},
"BrowserLinkRoutingModifierSetting": {
"titleSystem": "Hold Shift to open in your web browser",
"titleOrca": "Hold Shift to open in Orca",
+4
View File
@@ -9679,6 +9679,10 @@
"unpackedHelp": "Development builds must be updated from their source checkout.",
"legacyHelp": "Update this server manually once to enable remote updates."
},
"BrowserLinkRoutingSetting": {
"description": "Abre los enlaces http(s) del terminal, Markdown y el editor en el navegador integrado de Orca. {{shortcut}} siempre usa el navegador del sistema.",
"descriptionBase": "Abre los enlaces http(s) del terminal, Markdown y el editor en el navegador integrado de Orca."
},
"BrowserLinkRoutingModifierSetting": {
"titleSystem": "Hold Shift to open in your web browser",
"titleOrca": "Hold Shift to open in Orca",
+4
View File
@@ -9679,6 +9679,10 @@
"unpackedHelp": "開発ビルドはソースチェックアウトから更新する必要があります。",
"legacyHelp": "リモート更新を有効にするには、このサーバーを手動で一度更新してください。"
},
"BrowserLinkRoutingSetting": {
"description": "ターミナル、Markdown、エディターの http(s) リンクを Orca の組み込みブラウザで開きます。{{shortcut}} は常にシステム ブラウザを使用します。",
"descriptionBase": "ターミナル、Markdown、エディターの http(s) リンクを Orca の組み込みブラウザで開きます。"
},
"BrowserLinkRoutingModifierSetting": {
"titleSystem": "Hold Shift to open in your web browser",
"titleOrca": "Hold Shift to open in Orca",
+4
View File
@@ -9679,6 +9679,10 @@
"unpackedHelp": "개발 빌드는 소스 체크아웃에서 업데이트해야 합니다.",
"legacyHelp": "원격 업데이트를 활성화하려면 이 서버를 수동으로 한 번 업데이트하세요."
},
"BrowserLinkRoutingSetting": {
"description": "터미널, Markdown, 편집기의 http(s) 링크를 Orca 내장 브라우저에서 엽니다. {{shortcut}}을(를) 누르면 항상 시스템 브라우저를 사용합니다.",
"descriptionBase": "터미널, Markdown, 편집기의 http(s) 링크를 Orca 내장 브라우저에서 엽니다."
},
"BrowserLinkRoutingModifierSetting": {
"titleSystem": "Hold Shift to open in your web browser",
"titleOrca": "Hold Shift to open in Orca",
+4
View File
@@ -9691,6 +9691,10 @@
"unpackedHelp": "开发版本必须从其源代码检出进行更新。",
"legacyHelp": "手动更新此服务器一次以启用远程更新。"
},
"BrowserLinkRoutingSetting": {
"description": "在 Orca 内置浏览器中打开来自终端、Markdown 和编辑器的 http(s) 链接。{{shortcut}} 始终使用系统浏览器。",
"descriptionBase": "在 Orca 内置浏览器中打开来自终端、Markdown 和编辑器的 http(s) 链接。"
},
"BrowserLinkRoutingModifierSetting": {
"titleSystem": "Hold Shift to open in your web browser",
"titleOrca": "Hold Shift to open in Orca",