diff options
author | HiDeoo | 2024-06-09 20:04:12 +0200 |
---|---|---|
committer | GitHub | 2024-06-09 20:04:12 +0200 |
commit | 0b8a843936bd8506ac228608b07c54a76a7add19 (patch) | |
tree | ab374a11d0f0c220a7b77bba7eda46722d01d00f | |
parent | e6c0467100eaa76515d6dd1ee7485a457143f47a (diff) | |
download | IT.starlight-0b8a843936bd8506ac228608b07c54a76a7add19.tar.gz IT.starlight-0b8a843936bd8506ac228608b07c54a76a7add19.tar.bz2 IT.starlight-0b8a843936bd8506ac228608b07c54a76a7add19.zip |
Improve locale text direction detection mechanism (#1987)
Co-authored-by: Chris Swithinbank <357379+delucis@users.noreply.github.com>
-rw-r--r-- | .changeset/olive-oranges-poke.md | 5 | ||||
-rw-r--r-- | packages/starlight/__tests__/basics/i18n.test.ts | 50 | ||||
-rw-r--r-- | packages/starlight/utils/i18n.ts | 29 |
3 files changed, 81 insertions, 3 deletions
diff --git a/.changeset/olive-oranges-poke.md b/.changeset/olive-oranges-poke.md new file mode 100644 index 00000000..f9b1c2d8 --- /dev/null +++ b/.changeset/olive-oranges-poke.md @@ -0,0 +1,5 @@ +--- +'@astrojs/starlight': patch +--- + +Fixes issues with the locale text direction detection mechanism in some environments like WebContainers or Bun. diff --git a/packages/starlight/__tests__/basics/i18n.test.ts b/packages/starlight/__tests__/basics/i18n.test.ts index f609e528..527783b9 100644 --- a/packages/starlight/__tests__/basics/i18n.test.ts +++ b/packages/starlight/__tests__/basics/i18n.test.ts @@ -1,4 +1,4 @@ -import { assert, describe, expect, test } from 'vitest'; +import { assert, describe, expect, test, vi } from 'vitest'; import config from 'virtual:starlight/user-config'; import { processI18nConfig, pickLang } from '../../utils/i18n'; import type { AstroConfig } from 'astro'; @@ -250,6 +250,54 @@ describe('processI18nConfig', () => { }); }); +describe('getLocaleDir', () => { + test('uses the Node.js implementation to figure out the text direction', () => { + const { starlightConfig } = processI18nConfig( + config, + getAstroI18nTestConfig({ + defaultLocale: 'en', + locales: ['en'], + }) + ); + + expect(starlightConfig.defaultLocale.dir).toBe('ltr'); + }); + + test('uses `getTextInfo()` when `textInfo` is not available', async () => { + // @ts-expect-error - `getTextInfo` is not typed but is available in some non-v8 based environments. + vi.spyOn(global.Intl, 'Locale').mockImplementation(() => ({ + getTextInfo: () => ({ direction: 'rtl' }), + })); + + const { starlightConfig } = processI18nConfig( + config, + getAstroI18nTestConfig({ + defaultLocale: 'en', + locales: ['en'], + }) + ); + + expect(starlightConfig.defaultLocale.dir).toBe('rtl'); + }); + + test('fallbacks to a list of well-known RTL languages when `textInfo` and `getTextInfo()` are not available', async () => { + // @ts-expect-error - We are simulating the absence of `textInfo` and `getTextInfo()`. + vi.spyOn(global.Intl, 'Locale').mockImplementation((tag) => ({ language: tag })); + + const { starlightConfig } = processI18nConfig( + config, + getAstroI18nTestConfig({ + defaultLocale: 'en', + locales: ['en', 'fa'], + }) + ); + + expect(starlightConfig.defaultLocale.dir).toBe('ltr'); + expect(starlightConfig.locales?.root?.dir).toBe('ltr'); + expect(starlightConfig.locales?.fa?.dir).toBe('rtl'); + }); +}); + function getAstroI18nTestConfig(i18nConfig: AstroUserConfig['i18n']): AstroConfig['i18n'] { return { ...i18nConfig, diff --git a/packages/starlight/utils/i18n.ts b/packages/starlight/utils/i18n.ts index cb7c052a..be334ecb 100644 --- a/packages/starlight/utils/i18n.ts +++ b/packages/starlight/utils/i18n.ts @@ -6,6 +6,15 @@ import type { StarlightConfig } from './user-config'; export const BuiltInDefaultLocale = { ...getLocaleInfo('en'), lang: 'en' }; /** + * A list of well-known right-to-left languages used as a fallback when determining the text + * direction of a locale is not supported by the `Intl.Locale` API in the current environment. + * + * @see getLocaleDir() + * @see https://en.wikipedia.org/wiki/IETF_language_tag#List_of_common_primary_language_subtags + */ +const wellKnownRTL = ['ar', 'fa', 'he', 'prs', 'ps', 'syc', 'ug', 'ur']; + +/** * Processes the Astro and Starlight i18n configurations to generate/update them accordingly: * * - If no Astro and Starlight i18n configurations are provided, the built-in default locale is @@ -150,8 +159,7 @@ function getLocaleInfo(lang: string) { if (!label || lang === label) throw new Error('Label not found.'); return { label: label[0]?.toLocaleUpperCase(locale) + label.slice(1), - // @ts-expect-error - `textInfo` is not part of the `Intl.Locale` type but is available in Node.js 18.0.0+. - dir: locale.textInfo.direction as 'ltr' | 'rtl', + dir: getLocaleDir(locale), }; } catch (error) { throw new AstroError( @@ -162,6 +170,23 @@ function getLocaleInfo(lang: string) { } /** + * Returns the direction of the passed locale. + * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/getTextInfo + */ +function getLocaleDir(locale: Intl.Locale): 'ltr' | 'rtl' { + if ('textInfo' in locale) { + // @ts-expect-error - `textInfo` is not typed but is available in v8 based environments. + return locale.textInfo.direction; + } else if ('getTextInfo' in locale) { + // @ts-expect-error - `getTextInfo` is not typed but is available in some non-v8 based environments. + return locale.getTextInfo().direction; + } + // Firefox does not support `textInfo` or `getTextInfo` yet so we fallback to a well-known list + // of right-to-left languages. + return wellKnownRTL.includes(locale.language) ? 'rtl' : 'ltr'; +} + +/** * Get the string for the passed language from a dictionary object. * * TODO: Make this clever. Currently a simple key look-up, but should use |