diff --git a/docs/reference/plugin/external-link-icon.md b/docs/reference/plugin/external-link-icon.md index 1918cb47ae..d7435abfd1 100644 --- a/docs/reference/plugin/external-link-icon.md +++ b/docs/reference/plugin/external-link-icon.md @@ -12,6 +12,43 @@ This plugin has been integrated into the default theme. npm i -D @vuepress/plugin-external-link-icon@next ``` +## Options + +### locales + +- Type: `Record` + +- Details: + + The a11y text of the external link icon in different locales. + + If this option is not specified, it will fallback to default text. + +- Example: + +```js +module.exports = { + plugins: [ + [ + '@vuepress/plugin-external-link-icon', + { + locales: { + '/': { + openInNewWindow: 'open in new window', + }, + '/zh/': { + openInNewWindow: '在新窗口打开', + }, + }, + }, + ], + ], +} +``` + +- Also see: + - [Guide > I18n](../../guide/i18n.md) + ## Frontmatter ### externalLinkIcon diff --git a/docs/zh/reference/plugin/external-link-icon.md b/docs/zh/reference/plugin/external-link-icon.md index 508d2e4022..c75b9db1b5 100644 --- a/docs/zh/reference/plugin/external-link-icon.md +++ b/docs/zh/reference/plugin/external-link-icon.md @@ -12,6 +12,43 @@ npm i -D @vuepress/plugin-external-link-icon@next ``` +## 配置项 + +### locales + +- 类型: `Record` + +- 详情: + + 外部链接图标在不同 locales 下的 A11y 文字。 + + 如果没有指定该配置项,它会降级使用默认文字。 + +- 示例: + +```js +module.exports = { + plugins: [ + [ + '@vuepress/plugin-external-link-icon', + { + locales: { + '/': { + openInNewWindow: 'open in new window', + }, + '/zh/': { + openInNewWindow: '在新窗口打开', + }, + }, + }, + ], + ], +} +``` + +- 参考: + - [指南 > 多语言支持](../../guide/i18n.md) + ## Frontmatter ### externalLinkIcon diff --git a/packages/@vuepress/plugin-external-link-icon/src/client/clientAppEnhance.ts b/packages/@vuepress/plugin-external-link-icon/src/client/clientAppEnhance.ts index 504bb1716e..42eb611f99 100644 --- a/packages/@vuepress/plugin-external-link-icon/src/client/clientAppEnhance.ts +++ b/packages/@vuepress/plugin-external-link-icon/src/client/clientAppEnhance.ts @@ -1,6 +1,13 @@ import { defineClientAppEnhance } from '@vuepress/client' +import { h } from 'vue' +import type { ExternalLinkIconLocales } from '../shared' import { ExternalLinkIcon } from './components/ExternalLinkIcon' +declare const __EXTERNAL_LINK_ICON_LOCALES__: ExternalLinkIconLocales + +const locales = __EXTERNAL_LINK_ICON_LOCALES__ + export default defineClientAppEnhance(({ app }) => { - app.component('ExternalLinkIcon', ExternalLinkIcon) + // wrap the `` component with plugin options + app.component('ExternalLinkIcon', h(ExternalLinkIcon, { locales })) }) diff --git a/packages/@vuepress/plugin-external-link-icon/src/client/components/ExternalLinkIcon.ts b/packages/@vuepress/plugin-external-link-icon/src/client/components/ExternalLinkIcon.ts index bfe428d93d..8ffbff59f2 100644 --- a/packages/@vuepress/plugin-external-link-icon/src/client/components/ExternalLinkIcon.ts +++ b/packages/@vuepress/plugin-external-link-icon/src/client/components/ExternalLinkIcon.ts @@ -1,5 +1,7 @@ -import { h } from 'vue' -import type { FunctionalComponent } from 'vue' +import { useRouteLocale } from '@vuepress/client' +import { computed, defineComponent, h } from 'vue' +import type { PropType } from 'vue' +import type { ExternalLinkIconLocales } from '../../shared' import '../styles/vars.css' import '../styles/external-link-icon.css' @@ -30,7 +32,35 @@ const svg = h( ] ) -export const ExternalLinkIcon: FunctionalComponent = (_, { slots }) => - h('span', [svg, slots.default?.()]) +export const ExternalLinkIcon = defineComponent({ + name: 'ExternalLinkIcon', -ExternalLinkIcon.displayName = 'ExternalLinkIcon' + props: { + locales: { + type: Object as PropType, + required: false, + default: () => ({}), + }, + }, + + setup(props) { + const routeLocale = useRouteLocale() + const locale = computed( + () => + props.locales[routeLocale.value] ?? { + openInNewWindow: 'open in new window', + } + ) + return () => + h('span', [ + svg, + h( + 'span', + { + class: 'external-link-icon-sr-only', + }, + locale.value.openInNewWindow + ), + ]) + }, +}) diff --git a/packages/@vuepress/plugin-external-link-icon/src/client/styles/external-link-icon.css b/packages/@vuepress/plugin-external-link-icon/src/client/styles/external-link-icon.css index e04d3f9e0d..3343aee5d5 100644 --- a/packages/@vuepress/plugin-external-link-icon/src/client/styles/external-link-icon.css +++ b/packages/@vuepress/plugin-external-link-icon/src/client/styles/external-link-icon.css @@ -5,3 +5,16 @@ vertical-align: middle; top: -1px; } + +.external-link-icon-sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border-width: 0; + user-select: none; +} diff --git a/packages/@vuepress/plugin-external-link-icon/src/node/externalLinkIconPlugin.ts b/packages/@vuepress/plugin-external-link-icon/src/node/externalLinkIconPlugin.ts index ff5bc7d394..e4a86a60ba 100644 --- a/packages/@vuepress/plugin-external-link-icon/src/node/externalLinkIconPlugin.ts +++ b/packages/@vuepress/plugin-external-link-icon/src/node/externalLinkIconPlugin.ts @@ -1,12 +1,24 @@ import type { Plugin } from '@vuepress/core' import type { MarkdownEnv } from '@vuepress/markdown' import { path } from '@vuepress/utils' +import type { ExternalLinkIconLocales } from '../shared' -export type ExternalLinkIconPluginOptions = Record +/** + * Options for @vuepress/plugin-external-link-icon + */ +export type ExternalLinkIconPluginOptions = { + locales?: ExternalLinkIconLocales +} -export const externalLinkIconPlugin: Plugin = { +export const externalLinkIconPlugin: Plugin = ({ + locales = {}, +}) => ({ name: '@vuepress/plugin-external-link-icon', + define: { + __EXTERNAL_LINK_ICON_LOCALES__: locales, + }, + clientAppEnhanceFiles: path.resolve( __dirname, '../client/clientAppEnhance.js' @@ -44,4 +56,4 @@ export const externalLinkIconPlugin: Plugin = { return result } }, -} +}) diff --git a/packages/@vuepress/plugin-external-link-icon/src/shared/index.ts b/packages/@vuepress/plugin-external-link-icon/src/shared/index.ts new file mode 100644 index 0000000000..c9f6f047dc --- /dev/null +++ b/packages/@vuepress/plugin-external-link-icon/src/shared/index.ts @@ -0,0 +1 @@ +export * from './types' diff --git a/packages/@vuepress/plugin-external-link-icon/src/shared/types.ts b/packages/@vuepress/plugin-external-link-icon/src/shared/types.ts new file mode 100644 index 0000000000..d1d214bcb2 --- /dev/null +++ b/packages/@vuepress/plugin-external-link-icon/src/shared/types.ts @@ -0,0 +1,5 @@ +import type { LocaleConfig } from '@vuepress/shared' + +export type ExternalLinkIconLocales = LocaleConfig<{ + openInNewWindow: string +}> diff --git a/packages/@vuepress/plugin-external-link-icon/tsconfig.cjs.json b/packages/@vuepress/plugin-external-link-icon/tsconfig.cjs.json index 8ae90821dc..bcb0719155 100644 --- a/packages/@vuepress/plugin-external-link-icon/tsconfig.cjs.json +++ b/packages/@vuepress/plugin-external-link-icon/tsconfig.cjs.json @@ -5,5 +5,5 @@ "rootDir": "./src", "outDir": "./lib" }, - "include": ["./src/node"] + "include": ["./src/node", "./src/shared"] } diff --git a/packages/@vuepress/plugin-external-link-icon/tsconfig.esm.json b/packages/@vuepress/plugin-external-link-icon/tsconfig.esm.json index 91cd8e8620..98610e4b94 100644 --- a/packages/@vuepress/plugin-external-link-icon/tsconfig.esm.json +++ b/packages/@vuepress/plugin-external-link-icon/tsconfig.esm.json @@ -5,5 +5,5 @@ "rootDir": "./src", "outDir": "./lib" }, - "include": ["./src/client"] + "include": ["./src/client", "./src/shared"] } diff --git a/packages/@vuepress/theme-default/src/client/clientAppEnhance.ts b/packages/@vuepress/theme-default/src/client/clientAppEnhance.ts index a96b5e90c4..33b5cf8c6d 100644 --- a/packages/@vuepress/theme-default/src/client/clientAppEnhance.ts +++ b/packages/@vuepress/theme-default/src/client/clientAppEnhance.ts @@ -3,7 +3,6 @@ import { h } from 'vue' import Badge from './components/global/Badge.vue' import CodeGroup from './components/global/CodeGroup' import CodeGroupItem from './components/global/CodeGroupItem.vue' -import ExternalLinkIcon from './components/global/ExternalLinkIcon.vue' import { useScrollPromise } from './composables' import './styles/index.scss' @@ -13,10 +12,6 @@ export default defineClientAppEnhance(({ app, router }) => { app.component('CodeGroup', CodeGroup) app.component('CodeGroupItem', CodeGroupItem) - // override the `` provided by @vuepress/plugin-external-link-icon - delete app._context.components.ExternalLinkIcon - app.component('ExternalLinkIcon', ExternalLinkIcon) - // compat with @vuepress/plugin-docsearch and @vuepress/plugin-search app.component('NavbarSearch', () => { const SearchComponent = diff --git a/packages/@vuepress/theme-default/src/client/components/global/ExternalLinkIcon.vue b/packages/@vuepress/theme-default/src/client/components/global/ExternalLinkIcon.vue deleted file mode 100644 index b01897a6ee..0000000000 --- a/packages/@vuepress/theme-default/src/client/components/global/ExternalLinkIcon.vue +++ /dev/null @@ -1,12 +0,0 @@ - - - diff --git a/packages/@vuepress/theme-default/src/client/styles/index.scss b/packages/@vuepress/theme-default/src/client/styles/index.scss index 5e3046926a..7ee77d3a6e 100644 --- a/packages/@vuepress/theme-default/src/client/styles/index.scss +++ b/packages/@vuepress/theme-default/src/client/styles/index.scss @@ -13,7 +13,6 @@ @use 'navbar-dropdown'; @use 'page'; @use 'sidebar'; -@use 'sr-only'; @use 'toc'; @use 'transitions'; diff --git a/packages/@vuepress/theme-default/src/client/styles/sr-only.scss b/packages/@vuepress/theme-default/src/client/styles/sr-only.scss deleted file mode 100644 index 33e25b30eb..0000000000 --- a/packages/@vuepress/theme-default/src/client/styles/sr-only.scss +++ /dev/null @@ -1,12 +0,0 @@ -.sr-only { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - overflow: hidden; - clip: rect(0, 0, 0, 0); - white-space: nowrap; - border-width: 0; - user-select: none; -} diff --git a/packages/@vuepress/theme-default/src/node/defaultTheme.ts b/packages/@vuepress/theme-default/src/node/defaultTheme.ts index e789ef752d..df6312a24f 100644 --- a/packages/@vuepress/theme-default/src/node/defaultTheme.ts +++ b/packages/@vuepress/theme-default/src/node/defaultTheme.ts @@ -12,6 +12,7 @@ import { resolveContainerPluginOptionsForCodeGroup, resolveContainerPluginOptionsForCodeGroupItem, resolveContainerPluginOptionsForDetails, + resolveExternalLinkIconPluginOptions, resolveGitPluginOptions, resolveMediumZoomPluginOptions, } from './utils' @@ -108,7 +109,10 @@ export const defaultTheme: Theme = ( '@vuepress/container', resolveContainerPluginOptionsForCodeGroupItem(themePlugins), ], - ['@vuepress/external-link-icon', themePlugins.externalLinkIcon !== false], + [ + '@vuepress/external-link-icon', + resolveExternalLinkIconPluginOptions(themePlugins, localeOptions), + ], ['@vuepress/git', resolveGitPluginOptions(themePlugins, localeOptions)], ['@vuepress/medium-zoom', resolveMediumZoomPluginOptions(themePlugins)], ['@vuepress/nprogress', themePlugins.nprogress !== false], diff --git a/packages/@vuepress/theme-default/src/node/utils/index.ts b/packages/@vuepress/theme-default/src/node/utils/index.ts index 17cd5b4e68..9e8de92dd5 100644 --- a/packages/@vuepress/theme-default/src/node/utils/index.ts +++ b/packages/@vuepress/theme-default/src/node/utils/index.ts @@ -1,5 +1,6 @@ export * from './assignDefaultLocaleOptions' export * from './resolveActiveHeaderLinksPluginOptions' export * from './resolveContainerPluginOptions' +export * from './resolveExternalLinkIconPluginOptions' export * from './resolveGitPluginOptions' export * from './resolveMediumZoomPluginOptions' diff --git a/packages/@vuepress/theme-default/src/node/utils/resolveExternalLinkIconPluginOptions.ts b/packages/@vuepress/theme-default/src/node/utils/resolveExternalLinkIconPluginOptions.ts new file mode 100644 index 0000000000..18304a889b --- /dev/null +++ b/packages/@vuepress/theme-default/src/node/utils/resolveExternalLinkIconPluginOptions.ts @@ -0,0 +1,27 @@ +import type { ExternalLinkIconPluginOptions } from '@vuepress/plugin-external-link-icon' +import type { DefaultThemeData, DefaultThemePluginsOptions } from '../../shared' + +/** + * Resolve options for @vuepress/plugin-external-link-icon + */ +export const resolveExternalLinkIconPluginOptions = ( + themePlugins: DefaultThemePluginsOptions, + localeOptions: DefaultThemeData +): ExternalLinkIconPluginOptions | boolean => { + if (themePlugins?.externalLinkIcon === false) { + return false + } + + return { + locales: Object.entries(localeOptions.locales || {}).reduce( + (result, [key, value]) => { + result[key] = { + openInNewWindow: + value.openInNewWindow ?? localeOptions.openInNewWindow, + } + return result + }, + {} + ), + } +}