From 3c48eee2964539ffb2e9a1792ab015367b9a85d4 Mon Sep 17 00:00:00 2001 From: kazuya kawaguchi Date: Wed, 12 Nov 2025 18:23:19 +0900 Subject: [PATCH 1/3] fix: wrong default options --- src/h3.ts | 131 +++++++++++++++++++------------------------------- src/hono.ts | 131 +++++++++++++++++--------------------------------- src/http.ts | 41 ++++++++++------ src/node.ts | 122 ++++++++++++++++++++-------------------------- src/web.ts | 136 ++++++++++++++++++++-------------------------------- 5 files changed, 223 insertions(+), 338 deletions(-) diff --git a/src/h3.ts b/src/h3.ts index 5a6198c..e855d08 100644 --- a/src/h3.ts +++ b/src/h3.ts @@ -14,10 +14,15 @@ import { parseDefaultHeader, validateLocale } from './http.ts' -import { pathLanguageParser } from './shared.ts' import type { H3Event } from 'h3' -import type { CookieOptions, HeaderOptions, PathOptions, QueryOptions } from './http.ts' +import type { + CookieLocaleOptions, + CookieOptions, + HeaderOptions, + PathOptions, + QueryOptions +} from './http.ts' /** * get languages from header @@ -40,20 +45,17 @@ import type { CookieOptions, HeaderOptions, PathOptions, QueryOptions } from './ * ``` * * @param event - The {@link H3Event | H3} event - * @param options.name - The header name, which is as default `accept-language`. - * @param options.parser - The parser function, which is as default {@linkcode parseDefaultHeader}. If you are specifying more than one in your own format, you need a parser. + * @param options - The {@link HeaderOptions | header options} object. `name` option is `accept-language` as default. * * @returns The array of language tags, if you use `accept-language` header and `*` (any language) or empty string is detected, return an empty array. */ -export function getHeaderLanguages( - event: H3Event, - { name = ACCEPT_LANGUAGE_HEADER, parser = parseDefaultHeader }: HeaderOptions = {} -): string[] { +export function getHeaderLanguages(event: H3Event, options: HeaderOptions = {}): string[] { + const { name = ACCEPT_LANGUAGE_HEADER } = options const getter = () => { const headers = getHeaders(event) return headers[name] } - return getHeaderLanguagesWithGetter(getter, { name, parser }) + return getHeaderLanguagesWithGetter(getter, options) } /** @@ -77,16 +79,12 @@ export function getHeaderLanguages( * ``` * * @param event - The {@link H3Event | H3} event - * @param options.name - The header name, which is as default `accept-language`. - * @param options.parser - The parser function, which is as default {@linkcode parseDefaultHeader}. If you are specifying more than one in your own format, you need a parser. + * @param options - The {@link HeaderOptions | header options} object * * @returns The **first language tag** of header, if header is not exists, or `*` (any language), return empty string. */ -export function getHeaderLanguage( - event: H3Event, - { name = ACCEPT_LANGUAGE_HEADER, parser = parseDefaultHeader }: HeaderOptions = {} -): string { - return getHeaderLanguages(event, { name, parser })[0] || '' +export function getHeaderLanguage(event: H3Event, options: HeaderOptions = {}): string { + return getHeaderLanguages(event, options)[0] || '' } /** @@ -109,19 +107,15 @@ export function getHeaderLanguage( * ``` * * @param event - The {@link H3Event | H3} event - * @param options.name - The header name, which is as default `accept-language`. - * @param options.parser - The parser function, which is as default {@linkcode parseDefaultHeader}. If you are specifying more than one in your own format, you need a parser. + * @param options - The {@link HeaderOptions | header options} object * * @throws {RangeError} Throws the {@linkcode RangeError} if header are not a well-formed BCP 47 language tag. * * @returns The locales that wrapped from header, if you use `accept-language` header and `*` (any language) or empty string is detected, return an empty array. */ -export function getHeaderLocales( - event: H3Event, - { name = ACCEPT_LANGUAGE_HEADER, parser = parseDefaultHeader }: HeaderOptions = {} -): Intl.Locale[] { +export function getHeaderLocales(event: H3Event, options: HeaderOptions = {}): Intl.Locale[] { // @ts-expect-error -- FIXME: this type error needs to be fixed - return mapToLocaleFromLanguageTag(getHeaderLanguages, event, { name, parser }) + return mapToLocaleFromLanguageTag(getHeaderLanguages, event, options) } /** @@ -130,17 +124,16 @@ export function getHeaderLocales( * @description wrap language tags with {@link Intl.Locale | locale}, languages tags will be parsed from `accept-language` header as default. Unlike {@link getHeaderLocales}, this function does not throw an error if the locale cannot be obtained, this function returns `null`. * * @param event - The {@link H3Event | H3} event - * @param options.name - The header name, which is as default `accept-language`. - * @param options.parser - The parser function, which is as default {@linkcode parseDefaultHeader}. If you are specifying more than one in your own format, you need a parser. + * @param options - The {@link HeaderOptions | header options} object * * @returns The locales that wrapped from header, if you use `accept-language` header and `*` (any language) or empty string is detected, return an empty array. if header are not a well-formed BCP 47 language tag, return `null`. */ export function tryHeaderLocales( event: H3Event, - { name = ACCEPT_LANGUAGE_HEADER, parser = parseDefaultHeader }: HeaderOptions = {} + options: HeaderOptions = {} ): Intl.Locale[] | null { try { - return getHeaderLocales(event, { name, parser }) + return getHeaderLocales(event, options) } catch { return null } @@ -166,9 +159,7 @@ export function tryHeaderLocales( * ``` * * @param event - The {@link H3Event | H3} event - * @param options.lang - The default language tag, Optional. default value is `en-US`. You must specify the language tag with the {@link https://datatracker.ietf.org/doc/html/rfc4646#section-2.1 | BCP 47 syntax}. - * @param options.name - The header name, which is as default `accept-language`. - * @param options.parser - The parser function, which is as default {@linkcode parseDefaultHeader}. If you are specifying more than one in your own format, you need a parser. + * @param options - The {@link HeaderOptions | header options} object. `lang` option is `en-US` as default, you must specify the language tag with the {@link https://datatracker.ietf.org/doc/html/rfc4646#section-2.1 | BCP 47 syntax}. `name` option is `accept-language` as default, and `parser` option is {@linkcode parseDefaultHeader} as default. * * @throws {RangeError} Throws the {@linkcode RangeError} if `lang` option or header are not a well-formed BCP 47 language tag. * @@ -176,12 +167,13 @@ export function tryHeaderLocales( */ export function getHeaderLocale( event: H3Event, - { + options: HeaderOptions & { lang?: string } = {} +): Intl.Locale { + const { lang = DEFAULT_LANG_TAG, name = ACCEPT_LANGUAGE_HEADER, parser = parseDefaultHeader - }: HeaderOptions & { lang?: string } = {} -): Intl.Locale { + } = options return getLocaleWithGetter(() => getHeaderLanguages(event, { name, parser })[0] || lang) } @@ -191,22 +183,16 @@ export function getHeaderLocale( * @description wrap language tag with {@link Intl.Locale | locale}, languages tags will be parsed from `accept-language` header as default. Unlike {@link getHeaderLocale}, this function does not throw an error if the locale cannot be obtained, this function returns `null`. * * @param event - The {@link H3Event | H3} event - * @param options.lang - The default language tag, Optional. default value is `en-US`. You must specify the language tag with the {@link https://datatracker.ietf.org/doc/html/rfc4646#section-2.1 | BCP 47 syntax}. - * @param options.name - The header name, which is as default `accept-language`. - * @param options.parser - The parser function, which is as default {@linkcode parseDefaultHeader}. If you are specifying more than one in your own format, you need a parser. + * @param options - The {@link HeaderOptions | header options} object * * @returns The first locale that resolved from header string. if you use `accept-language` header and `*` (any language) or empty string is detected, return `en-US`. if header are not a well-formed BCP 47 language tag, return `null`. */ export function tryHeaderLocale( event: H3Event, - { - lang = DEFAULT_LANG_TAG, - name = ACCEPT_LANGUAGE_HEADER, - parser = parseDefaultHeader - }: HeaderOptions & { lang?: string } = {} + options: HeaderOptions & { lang?: string } = {} ): Intl.Locale | null { try { - return getHeaderLocale(event, { lang, name, parser }) + return getHeaderLocale(event, options) } catch { return null } @@ -230,17 +216,14 @@ export function tryHeaderLocale( * ``` * * @param event - The {@link H3Event | H3} event - * @param options.lang - The default language tag, default is `en-US`. You must specify the language tag with the {@link https://datatracker.ietf.org/doc/html/rfc4646#section-2.1 | BCP 47 syntax}. - * @param options.name - The cookie name, default is `i18n_locale` + * @param options - The {@link CookieLocaleOptions | cookie locale options}, `lang` option is `en-US` as default, you must specify the language tag with the {@link https://datatracker.ietf.org/doc/html/rfc4646#section-2.1 | BCP 47 syntax}. `name` option is `i18n_locale` as default. * * @throws {RangeError} Throws a {@linkcode RangeError} if `lang` option or cookie name value are not a well-formed BCP 47 language tag. * * @returns The locale that resolved from cookie */ -export function getCookieLocale( - event: H3Event, - { lang = DEFAULT_LANG_TAG, name = DEFAULT_COOKIE_NAME } = {} -): Intl.Locale { +export function getCookieLocale(event: H3Event, options: CookieLocaleOptions = {}): Intl.Locale { + const { lang = DEFAULT_LANG_TAG, name = DEFAULT_COOKIE_NAME } = options return getLocaleWithGetter(() => getCookie(event, name) || lang) } @@ -250,17 +233,16 @@ export function getCookieLocale( * @description Unlike {@linkcode getCookieLocale}, this function does not throw an error if the locale cannot be obtained, this function returns `null`. * * @param event - The {@link H3Event | H3} event - * @param options.lang - The default language tag, default is `en-US`. You must specify the language tag with the {@link https://datatracker.ietf.org/doc/html/rfc4646#section-2.1 | BCP 47 syntax}. - * @param options.name - The cookie name, default is `i18n_locale` + * @param options - The {@link CookieLocaleOptions | cookie locale options} * * @returns The locale that resolved from cookie. if `lang` option or cookie name value are not a well-formed BCP 47 language tag, return `null`. */ export function tryCookieLocale( event: H3Event, - { lang = DEFAULT_LANG_TAG, name = DEFAULT_COOKIE_NAME } = {} + options: CookieLocaleOptions = {} ): Intl.Locale | null { try { - return getCookieLocale(event, { lang, name }) + return getCookieLocale(event, options) } catch { return null } @@ -284,35 +266,32 @@ export function tryCookieLocale( * * @param event - The {@link H3Event | H3} event * @param locale - The locale value - * @param options - The cookie options, `name` option is `i18n_locale` as default, and `path` option is `/` as default. + * @param options - The {@link CookieOptions | cookie options}, `name` option is `i18n_locale` as default * * @throws {SyntaxError} Throws the {@linkcode SyntaxError} if `locale` is invalid. */ export function setCookieLocale( event: H3Event, locale: string | Intl.Locale, - options: CookieOptions = { name: DEFAULT_COOKIE_NAME } // eslint-disable-line unicorn/no-object-as-default-parameter -- NOTE: allow + options: CookieOptions = {} ): void { + const { name = DEFAULT_COOKIE_NAME } = options validateLocale(locale) - setCookie(event, options.name!, locale.toString(), options) + setCookie(event, name, locale.toString(), options) } /** * get the locale from the path * * @param event - the {@link H3Event | H3} event - * @param options.lang - the language tag, which is as default `'en-US'`. optional - * @param options.parser - the path language parser, default {@linkcode pathLanguageParser}, optional + * @param options - the {@link PathOptions | path options} object * * @throws {RangeError} Throws the {@linkcode RangeError} if the language in the path, that is not a well-formed BCP 47 language tag. * * @returns The locale that resolved from path */ -export function getPathLocale( - event: H3Event, - { lang = DEFAULT_LANG_TAG, parser = pathLanguageParser }: PathOptions = {} -): Intl.Locale { - return _getPathLocale(getRequestURL(event), { lang, parser }) +export function getPathLocale(event: H3Event, options: PathOptions = {}): Intl.Locale { + return _getPathLocale(getRequestURL(event), options) } /** @@ -321,17 +300,13 @@ export function getPathLocale( * @description Unlike {@linkcode getPathLocale}, this function does not throw an error if the locale cannot be obtained, this function returns `null`. * * @param event - the {@link H3Event | H3} event - * @param options.lang - the language tag, which is as default `'en-US'`. optional - * @param options.parser - the path language parser, default {@linkcode pathLanguageParser}, optional + * @param options - the {@link PathOptions | path options} object * * @returns The locale that resolved from path. if the language in the path, that is not a well-formed BCP 47 language tag, return `null`. */ -export function tryPathLocale( - event: H3Event, - { lang = DEFAULT_LANG_TAG, parser = pathLanguageParser }: PathOptions = {} -): Intl.Locale | null { +export function tryPathLocale(event: H3Event, options: PathOptions = {}): Intl.Locale | null { try { - return getPathLocale(event, { lang, parser }) + return getPathLocale(event, options) } catch { return null } @@ -341,17 +316,14 @@ export function tryPathLocale( * get the locale from the query * * @param event - the {@link H3Event | H3} event - * @param options.lang - the language tag, which is as default `'en-US'`. optional - * @param options.name - the query param name, default `'locale'`. optional + * @param options - The {@link QueryOptions | query options}, `lang` option is `en-US` as default, `name` option is `locale` as default. * * @throws {RangeError} Throws the {@linkcode RangeError} if the language in the query, that is not a well-formed BCP 47 language tag. * * @returns The locale that resolved from query */ -export function getQueryLocale( - event: H3Event, - { lang = DEFAULT_LANG_TAG, name = 'locale' }: QueryOptions = {} -): Intl.Locale { +export function getQueryLocale(event: H3Event, options: QueryOptions = {}): Intl.Locale { + const { lang = DEFAULT_LANG_TAG, name = 'locale' } = options return _getQueryLocale(getRequestURL(event), { lang, name }) } @@ -361,15 +333,12 @@ export function getQueryLocale( * @description Unlike {@linkcode getQueryLocale}, this function does not throw an error if the locale cannot be obtained, this function returns `null`. * * @param event - the {@link H3Event | H3} event - * @param options.lang - the language tag, which is as default `'en-US'`. optional - * @param options.name - the query param name, default `'locale'`. optional + * @param options - The {@link QueryOptions | query options}, `lang` option is `en-US` as default, `name` option is `locale` as default. * * @returns The locale that resolved from query. if the language in the query, that is not a well-formed BCP 47 language tag, return `null`. */ -export function tryQueryLocale( - event: H3Event, - { lang = DEFAULT_LANG_TAG, name = 'locale' }: QueryOptions = {} -): Intl.Locale | null { +export function tryQueryLocale(event: H3Event, options: QueryOptions = {}): Intl.Locale | null { + const { lang = DEFAULT_LANG_TAG, name = 'locale' } = options try { return getQueryLocale(event, { lang, name }) } catch { diff --git a/src/hono.ts b/src/hono.ts index 58fca90..9e5fbfa 100644 --- a/src/hono.ts +++ b/src/hono.ts @@ -14,10 +14,9 @@ import { parseDefaultHeader, validateLocale } from './http.ts' -import { pathLanguageParser } from './shared.ts' import type { Context } from 'hono' -import type { HeaderOptions, PathOptions, QueryOptions } from './http.ts' +import type { CookieLocaleOptions, HeaderOptions, PathOptions, QueryOptions } from './http.ts' type CookieOptions = Parameters[3] & { name?: string } @@ -42,19 +41,13 @@ type CookieOptions = Parameters[3] & { name?: string } * ``` * * @param context - A {@link Context | Hono} context - * @param options.name - A header name, which is as default `accept-language`. - * @param options.parser - A parser function, which is as default {@linkcode parseDefaultHeader}. If you are specifying more than one in your own format, you need a parser. + * @param options - The {@link HeaderOptions | header options} object. `name` option is `accept-language` as default. * * @returns An array of language tags, if you use `accept-language` header and `*` (any language) or empty string is detected, return an empty array. */ -export function getHeaderLanguages( - context: Context, - { name = ACCEPT_LANGUAGE_HEADER, parser = parseDefaultHeader }: HeaderOptions = {} -): string[] { - return getHeaderLanguagesWithGetter(() => context.req.header(name), { - name, - parser - }) +export function getHeaderLanguages(context: Context, options: HeaderOptions = {}): string[] { + const { name = ACCEPT_LANGUAGE_HEADER } = options + return getHeaderLanguagesWithGetter(() => context.req.header(name), options) } /** @@ -78,16 +71,12 @@ export function getHeaderLanguages( * ``` * * @param context - A {@link Context | Hono} context - * @param options.name - A header name, which is as default `accept-language`. - * @param options.parser - A parser function, which is as default {@linkcode parseDefaultHeader}. If you are specifying more than one in your own format, you need a parser. + * @param options - The {@link HeaderOptions | header options} object * * @returns A **first language tag** of header, if header is not exists, or `*` (any language), return empty string. */ -export function getHeaderLanguage( - context: Context, - { name = ACCEPT_LANGUAGE_HEADER, parser = parseDefaultHeader }: HeaderOptions = {} -): string { - return getHeaderLanguages(context, { name, parser })[0] || '' +export function getHeaderLanguage(context: Context, options: HeaderOptions = {}): string { + return getHeaderLanguages(context, options)[0] || '' } /** @@ -111,22 +100,15 @@ export function getHeaderLanguage( * ``` * * @param context - A {@link Context | Hono} context - * @param options.name - A header name, which is as default `accept-language`. - * @param options.parser - A parser function, which is as default {@linkcode parseDefaultHeader}. If you are specifying more than one in your own format, you need a parser. + * @param options - The {@link HeaderOptions | header options} object * * @throws {RangeError} Throws the {@linkcode RangeError} if header are not a well-formed BCP 47 language tag. * * @returns Some locales that wrapped from header, if you use `accept-language` header and `*` (any language) or empty string is detected, return an empty array. */ -export function getHeaderLocales( - context: Context, - { name = ACCEPT_LANGUAGE_HEADER, parser = parseDefaultHeader }: HeaderOptions = {} -): Intl.Locale[] { +export function getHeaderLocales(context: Context, options: HeaderOptions = {}): Intl.Locale[] { // @ts-expect-error -- FIXME: this type error needs to be fixed - return mapToLocaleFromLanguageTag(getHeaderLanguages, context, { - name, - parser - }) + return mapToLocaleFromLanguageTag(getHeaderLanguages, context, options) } /** @@ -135,17 +117,16 @@ export function getHeaderLocales( * @description wrap language tags with {@link Intl.Locale | locale}, languages tags will be parsed from `accept-language` header as default. Unlike {@link getHeaderLocales}, this function does not throw an error if the locale cannot be obtained, this function returns `null`. * * @param context - A {@link Context | Hono} context - * @param options.name - A header name, which is as default `accept-language`. - * @param options.parser - A parser function, which is as default {@linkcode parseDefaultHeader}. If you are specifying more than one in your own format, you need a parser. + * @param options - The {@link HeaderOptions | header options} object * * @returns Some locales that wrapped from header, if you use `accept-language` header and `*` (any language) or empty string is detected, return an empty array. if header are not a well-formed BCP 47 language tag, return `null`. */ export function tryHeaderLocales( context: Context, - { name = ACCEPT_LANGUAGE_HEADER, parser = parseDefaultHeader }: HeaderOptions = {} + options: HeaderOptions = {} ): Intl.Locale[] | null { try { - return getHeaderLocales(context, { name, parser }) + return getHeaderLocales(context, options) } catch { return null } @@ -172,9 +153,7 @@ export function tryHeaderLocales( * ``` * * @param context - A {@link Context | Hono} context - * @param options.lang - A default language tag, Optional. default value is `en-US`. You must specify the language tag with the {@link https://datatracker.ietf.org/doc/html/rfc4646#section-2.1 | BCP 47 syntax}. - * @param options.name - A header name, which is as default `accept-language`. - * @param options.parser - A parser function, which is as default {@linkcode parseDefaultHeader}. If you are specifying more than one in your own format, you need a parser. + * @param options - The {@link HeaderOptions | header options} object. `lang` option is `en-US` as default, you must specify the language tag with the {@link https://datatracker.ietf.org/doc/html/rfc4646#section-2.1 | BCP 47 syntax}. `name` option is `accept-language` as default, and `parser` option is {@linkcode parseDefaultHeader} as default. * * @throws {RangeError} Throws the {@linkcode RangeError} if `lang` option or header are not a well-formed BCP 47 language tag. * @@ -182,12 +161,13 @@ export function tryHeaderLocales( */ export function getHeaderLocale( context: Context, - { + options: HeaderOptions & { lang?: string } = {} +): Intl.Locale { + const { lang = DEFAULT_LANG_TAG, name = ACCEPT_LANGUAGE_HEADER, parser = parseDefaultHeader - }: HeaderOptions & { lang?: string } = {} -): Intl.Locale { + } = options return getLocaleWithGetter(() => getHeaderLanguages(context, { name, parser })[0] || lang) } @@ -197,22 +177,16 @@ export function getHeaderLocale( * @description wrap language tag with {@link Intl.Locale | locale}, languages tags will be parsed from `accept-language` header as default. Unlike {@link getHeaderLocale}, this function does not throw an error if the locale cannot be obtained, this function returns `null`. * * @param context - A {@link Context | Hono} context - * @param options.lang - A default language tag, Optional. default value is `en-US`. You must specify the language tag with the {@link https://datatracker.ietf.org/doc/html/rfc4646#section-2.1 | BCP 47 syntax}. - * @param options.name - A header name, which is as default `accept-language`. - * @param options.parser - A parser function, which is as default {@linkcode parseDefaultHeader}. If you are specifying more than one in your own format, you need a parser. + * @param options - The {@link HeaderOptions | header options} object * * @returns A first locale that resolved from header string. if you use `accept-language` header and `*` (any language) or empty string is detected, return `en-US`. if `lang` option or header are not a well-formed BCP 47 language tag, return `null`. */ export function tryHeaderLocale( context: Context, - { - lang = DEFAULT_LANG_TAG, - name = ACCEPT_LANGUAGE_HEADER, - parser = parseDefaultHeader - }: HeaderOptions & { lang?: string } = {} + options: HeaderOptions & { lang?: string } = {} ): Intl.Locale | null { try { - return getHeaderLocale(context, { lang, name, parser }) + return getHeaderLocale(context, options) } catch { return null } @@ -237,17 +211,14 @@ export function tryHeaderLocale( * ``` * * @param context - A {@link Context | Hono} context - * @param options.lang - A default language tag, default is `en-US`. You must specify the language tag with the {@link https://datatracker.ietf.org/doc/html/rfc4646#section-2.1 | BCP 47 syntax}. - * @param options.name - A cookie name, default is `i18n_locale` + * @param options - The {@link CookieLocaleOptions | cookie locale options}, `lang` option is `en-US` as default, you must specify the language tag with the {@link https://datatracker.ietf.org/doc/html/rfc4646#section-2.1 | BCP 47 syntax}. `name` option is `i18n_locale` as default. * * @throws {RangeError} Throws a {@linkcode RangeError} if `lang` option or cookie name value are not a well-formed BCP 47 language tag. * * @returns The locale that resolved from cookie */ -export function getCookieLocale( - context: Context, - { lang = DEFAULT_LANG_TAG, name = DEFAULT_COOKIE_NAME } = {} -): Intl.Locale { +export function getCookieLocale(context: Context, options: CookieLocaleOptions = {}): Intl.Locale { + const { lang = DEFAULT_LANG_TAG, name = DEFAULT_COOKIE_NAME } = options return getLocaleWithGetter(() => getCookie(context, name) || lang) } @@ -257,17 +228,16 @@ export function getCookieLocale( * @description Unlike {@linkcode getCookieLocale}, this function does not throw an error if the locale cannot be obtained, this function returns `null`. * * @param context - A {@link Context | Hono} context - * @param options.lang - A default language tag, default is `en-US`. You must specify the language tag with the {@link https://datatracker.ietf.org/doc/html/rfc4646#section-2.1 | BCP 47 syntax}. - * @param options.name - A cookie name, default is `i18n_locale` + * @param options - The {@link CookieLocaleOptions | cookie locale options} * * @returns The locale that resolved from cookie, if `lang` option or cookie name value are not a well-formed BCP 47 language tag, return `null`. */ export function tryCookieLocale( context: Context, - { lang = DEFAULT_LANG_TAG, name = DEFAULT_COOKIE_NAME } = {} + options: CookieLocaleOptions = {} ): Intl.Locale | null { try { - return getCookieLocale(context, { lang, name }) + return getCookieLocale(context, options) } catch { return null } @@ -292,35 +262,32 @@ export function tryCookieLocale( * * @param context - A {@link Context | Hono} context * @param locale - A locale value - * @param options - A cookie options, `name` option is `i18n_locale` as default, and `path` option is `/` as default. + * @param options - The {@link CookieOptions | cookie options}, `name` option is `i18n_locale` as default * * @throws {SyntaxError} Throws the {@linkcode SyntaxError} if `locale` is invalid. */ export function setCookieLocale( context: Context, locale: string | Intl.Locale, - options: CookieOptions = { name: DEFAULT_COOKIE_NAME } // eslint-disable-line unicorn/no-object-as-default-parameter -- NOTE: allow + options: CookieOptions = {} ): void { + const { name = DEFAULT_COOKIE_NAME } = options validateLocale(locale) - setCookie(context, options.name!, locale.toString(), options) + setCookie(context, name, locale.toString(), options) } /** * get the locale from the path * * @param context - A {@link Context | Hono} context - * @param options.lang - A language tag, which is as default `'en-US'`. optional - * @param options.parser - the path language parser, default {@linkcode pathLanguageParser}, optional + * @param options - the {@link PathOptions | path options} object * * @throws {RangeError} Throws the {@linkcode RangeError} if the language in the path, that is not a well-formed BCP 47 language tag. * * @returns The locale that resolved from path */ -export function getPathLocale( - context: Context, - { lang = DEFAULT_LANG_TAG, parser = pathLanguageParser }: PathOptions = {} -): Intl.Locale { - return _getPathLocale(new URL(context.req.url), { lang, parser }) +export function getPathLocale(context: Context, options: PathOptions = {}): Intl.Locale { + return _getPathLocale(new URL(context.req.url), options) } /** @@ -329,17 +296,13 @@ export function getPathLocale( * @description Unlike {@linkcode getPathLocale}, this function does not throw an error if the locale cannot be obtained, this function returns `null`. * * @param context - A {@link Context | Hono} context - * @param options.lang - A language tag, which is as default `'en-US'`. optional - * @param options.parser - the path language parser, default {@linkcode pathLanguageParser}, optional + * @param options - the {@link PathOptions | path options} object * * @returns The locale that resolved from path. if the language in the path, that is not a well-formed BCP 47 language tag, return `null`. */ -export function tryPathLocale( - context: Context, - { lang = DEFAULT_LANG_TAG, parser = pathLanguageParser }: PathOptions = {} -): Intl.Locale | null { +export function tryPathLocale(context: Context, options: PathOptions = {}): Intl.Locale | null { try { - return getPathLocale(context, { lang, parser }) + return getPathLocale(context, options) } catch { return null } @@ -349,17 +312,14 @@ export function tryPathLocale( * get the locale from the query * * @param context - A {@link Context | Hono} context - * @param options.lang - A language tag, which is as default `'en-US'`. optional - * @param options.name - A query param name, default `'locale'`. optional + * @param options - The {@link QueryOptions | query options}, `lang` option is `en-US` as default, `name` option is `locale` as default. * * @throws {RangeError} Throws the {@linkcode RangeError} if the language in the query, that is not a well-formed BCP 47 language tag. * * @returns The locale that resolved from query */ -export function getQueryLocale( - context: Context, - { lang = DEFAULT_LANG_TAG, name = 'locale' }: QueryOptions = {} -): Intl.Locale { +export function getQueryLocale(context: Context, options: QueryOptions = {}): Intl.Locale { + const { lang = DEFAULT_LANG_TAG, name = 'locale' } = options return _getQueryLocale(new URL(context.req.url), { lang, name }) } @@ -369,15 +329,12 @@ export function getQueryLocale( * @description Unlike {@linkcode getQueryLocale}, this function does not throw an error if the locale cannot be obtained, this function returns `null`. * * @param context - A {@link Context | Hono} context - * @param options.lang - A language tag, which is as default `'en-US'`. optional - * @param options.name - A query param name, default `'locale'`. optional + * @param options - The {@link QueryOptions | query options}, `lang` option is `en-US` as default, `name` option is `locale` as default. * * @returns The locale that resolved from query. if the language in the query, that is not a well-formed BCP 47 language tag, return `null`. */ -export function tryQueryLocale( - context: Context, - { lang = DEFAULT_LANG_TAG, name = 'locale' }: QueryOptions = {} -): Intl.Locale | null { +export function tryQueryLocale(context: Context, options: QueryOptions = {}): Intl.Locale | null { + const { lang = DEFAULT_LANG_TAG, name = 'locale' } = options try { return getQueryLocale(context, { lang, name }) } catch { diff --git a/src/http.ts b/src/http.ts index c7bb9d3..5a66fc0 100644 --- a/src/http.ts +++ b/src/http.ts @@ -125,6 +125,20 @@ export type CookieOptions = CookieSerializeOptions & { name?: string } +/** + * Cookie locale options type + */ +export type CookieLocaleOptions = { + /** + * The default language tag + */ + lang?: string + /** + * Cookie name + */ + name?: string +} + /** * Header options type */ @@ -154,13 +168,15 @@ export function parseDefaultHeader(input: string): string[] { * get languages from header with getter function * * @param getter - the header string getter function + * @param options - the {@link HeaderOptions | header options} * * @returns The array of language tags */ export function getHeaderLanguagesWithGetter( getter: () => string | null | undefined, - { name = ACCEPT_LANGUAGE_HEADER, parser = parseDefaultHeader }: HeaderOptions = {} + options: HeaderOptions = {} ): string[] { + const { name = ACCEPT_LANGUAGE_HEADER, parser = parseDefaultHeader } = options const langString = getter() return langString ? name === ACCEPT_LANGUAGE_HEADER @@ -247,15 +263,12 @@ export type PathOptions = { * get the language from the path * * @param path - the target path - * @param options.lang - the language tag, which is as default `'en-US'`. optional - * @param options.parser - the path language parser, optional + * @param options - the {@link PathOptions | path options} object * * @returns the language that is parsed by the path language parser, if the language is not detected, return a `options.lang` value */ -export function getPathLanguage( - path: string | URL, - { lang = DEFAULT_LANG_TAG, parser = pathLanguageParser }: PathOptions = {} -): string { +export function getPathLanguage(path: string | URL, options: PathOptions = {}): string { + const { lang = DEFAULT_LANG_TAG, parser = pathLanguageParser } = options return (parser || pathLanguageParser)(path) || lang } @@ -263,18 +276,14 @@ export function getPathLanguage( * get the locale from the path * * @param path - the target path - * @param options.lang - the language tag, which is as default `'en-US'`. optional - * @param options.parser - the path language parser, optional + * @param options - the {@link PathOptions | path options} object * * @throws {RangeError} Throws the {@link RangeError} if the language in the path, that is not a well-formed BCP 47 language tag. * * @returns The locale that resolved from path */ -export function getPathLocale( - path: string | URL, - { lang = DEFAULT_LANG_TAG, parser = pathLanguageParser }: PathOptions = {} -): Intl.Locale { - return new Intl.Locale(getPathLanguage(path, { lang, parser })) +export function getPathLocale(path: string | URL, options: PathOptions = {}): Intl.Locale { + return new Intl.Locale(getPathLanguage(path, options)) } function getURLSearchParams(input: string | URL | URLSearchParams): URLSearchParams { @@ -322,6 +331,7 @@ export function getQueryLanguage( * get the locale from the query * * @param query - the target query + * @param options - The {@link QueryOptions | query options} * @param options.lang - the language tag, which is as default `'en-US'`. optional * @param options.name - the query param name, default `'locale'`. optional * @@ -331,7 +341,8 @@ export function getQueryLanguage( */ export function getQueryLocale( query: string | URL | URLSearchParams, - { lang = DEFAULT_LANG_TAG, name = 'locale' }: QueryOptions = {} + options: QueryOptions = {} ): Intl.Locale { + const { lang = DEFAULT_LANG_TAG, name = 'locale' } = options return new Intl.Locale(getQueryLanguage(query, { lang, name })) } diff --git a/src/node.ts b/src/node.ts index ef4e910..f6e10de 100644 --- a/src/node.ts +++ b/src/node.ts @@ -16,10 +16,16 @@ import { parseDefaultHeader, validateLocale } from './http.ts' -import { normalizeLanguageName, pathLanguageParser } from './shared.ts' +import { normalizeLanguageName } from './shared.ts' import type { IncomingMessage, OutgoingMessage } from 'node:http' -import type { CookieOptions, HeaderOptions, PathOptions, QueryOptions } from './http.ts' +import type { + CookieLocaleOptions, + CookieOptions, + HeaderOptions, + PathOptions, + QueryOptions +} from './http.ts' /** * get languages from header @@ -42,17 +48,17 @@ import type { CookieOptions, HeaderOptions, PathOptions, QueryOptions } from './ * ``` * * @param request - The {@link IncomingMessage | request} - * @param options.name - The header name, which is as default `accept-language`. - * @param options.parser - The parser function, which is as default {@linkcode parseDefaultHeader}. If you are specifying more than one in your own format, you need a parser. + * @param options - The {@link HeaderOptions | header options} object. `name` option is `accept-language` as default. * * @returns The array of language tags, if you use `accept-language` header and `*` (any language) or empty string is detected, return an empty array. */ export function getHeaderLanguages( request: IncomingMessage, - { name = ACCEPT_LANGUAGE_HEADER, parser = parseDefaultHeader }: HeaderOptions = {} + options: HeaderOptions = {} ): string[] { + const { name = ACCEPT_LANGUAGE_HEADER } = options const getter = () => request.headers[name] as string | undefined - return getHeaderLanguagesWithGetter(getter, { name, parser }) + return getHeaderLanguagesWithGetter(getter, options) } /** @@ -76,16 +82,12 @@ export function getHeaderLanguages( * ``` * * @param request - The {@link IncomingMessage | request} - * @param options.name - The header name, which is as default `accept-language`. - * @param options.parser - The parser function, which is as default {@linkcode parseDefaultHeader}. If you are specifying more than one in your own format, you need a parser. + * @param options - The {@link HeaderOptions | header options} object * * @returns The **first language tag** of header, if header is not exists, or `*` (any language), return empty string. */ -export function getHeaderLanguage( - request: IncomingMessage, - { name = ACCEPT_LANGUAGE_HEADER, parser = parseDefaultHeader }: HeaderOptions = {} -): string { - return getHeaderLanguages(request, { name, parser })[0] || '' +export function getHeaderLanguage(request: IncomingMessage, options: HeaderOptions = {}): string { + return getHeaderLanguages(request, options)[0] || '' } /** @@ -109,8 +111,7 @@ export function getHeaderLanguage( * ``` * * @param request - The {@link IncomingMessage | request} - * @param options.name - The header name, which is as default `accept-language`. - * @param options.parser - The parser function, which is as default {@linkcode parseDefaultHeader}. If you are specifying more than one in your own format, you need a parser. + * @param options - The {@link HeaderOptions | header options} object * * @throws {RangeError} Throws the {@linkcode RangeError} if header are not a well-formed BCP 47 language tag. * @@ -118,13 +119,10 @@ export function getHeaderLanguage( */ export function getHeaderLocales( request: IncomingMessage, - { name = ACCEPT_LANGUAGE_HEADER, parser = parseDefaultHeader }: HeaderOptions = {} + options: HeaderOptions = {} ): Intl.Locale[] { // @ts-expect-error -- FIXME: this type error needs to be fixed - return mapToLocaleFromLanguageTag(getHeaderLanguages, request, { - name, - parser - }) + return mapToLocaleFromLanguageTag(getHeaderLanguages, request, options) } /** @@ -133,17 +131,16 @@ export function getHeaderLocales( * @description wrap language tags with {@link Intl.Locale | locale}, languages tags will be parsed from `accept-language` header as default. Unlike {@link getHeaderLocales}, this function does not throw an error if the locale cannot be obtained, this function returns `null`. * * @param request - The {@link IncomingMessage | request} - * @param options.name - The header name, which is as default `accept-language`. - * @param options.parser - The parser function, which is as default {@linkcode parseDefaultHeader}. If you are specifying more than one in your own format, you need a parser. + * @param options - The {@link HeaderOptions | header options} object * * @returns The locales that wrapped from header, if you use `accept-language` header and `*` (any language) or empty string is detected, return an empty array. if header are not a well-formed BCP 47 language tag, return `null`. */ export function tryHeaderLocales( request: IncomingMessage, - { name = ACCEPT_LANGUAGE_HEADER, parser = parseDefaultHeader }: HeaderOptions = {} + options: HeaderOptions = {} ): Intl.Locale[] | null { try { - return getHeaderLocales(request, { name, parser }) + return getHeaderLocales(request, options) } catch { return null } @@ -170,9 +167,7 @@ export function tryHeaderLocales( * ``` * * @param request - The {@link IncomingMessage | request} - * @param options.lang - The default language tag, Optional. default value is `en-US`. You must specify the language tag with the {@link https://datatracker.ietf.org/doc/html/rfc4646#section-2.1 | BCP 47 syntax}. - * @param options.name - The header name, which is as default `accept-language`. - * @param options.parser - The parser function, which is as default {@linkcode parseDefaultHeader}. If you are specifying more than one in your own format, you need a parser. + * @param options - The {@link HeaderOptions | header options} object. `lang` option is `en-US` as default, you must specify the language tag with the {@link https://datatracker.ietf.org/doc/html/rfc4646#section-2.1 | BCP 47 syntax}. `name` option is `accept-language` as default, and `parser` option is {@linkcode parseDefaultHeader} as default. * * @throws {RangeError} Throws the {@linkcode RangeError} if `lang` option or header are not a well-formed BCP 47 language tag. * @@ -180,12 +175,13 @@ export function tryHeaderLocales( */ export function getHeaderLocale( request: IncomingMessage, - { + options: HeaderOptions & { lang?: string } = {} +): Intl.Locale { + const { lang = DEFAULT_LANG_TAG, name = ACCEPT_LANGUAGE_HEADER, parser = parseDefaultHeader - }: HeaderOptions & { lang?: string } = {} -): Intl.Locale { + } = options return getLocaleWithGetter(() => getHeaderLanguages(request, { name, parser })[0] || lang) } @@ -195,22 +191,16 @@ export function getHeaderLocale( * @description wrap language tag with {@link Intl.Locale | locale}, languages tags will be parsed from `accept-language` header as default. Unlike {@link getHeaderLocale}, this function does not throw an error if the locale cannot be obtained, this function returns `null`. * * @param request - The {@link IncomingMessage | request} - * @param options.lang - The default language tag, Optional. default value is `en-US`. You must specify the language tag with the {@link https://datatracker.ietf.org/doc/html/rfc4646#section-2.1 | BCP 47 syntax}. - * @param options.name - The header name, which is as default `accept-language`. - * @param options.parser - The parser function, which is as default {@linkcode parseDefaultHeader}. If you are specifying more than one in your own format, you need a parser. + * @param options - The {@link HeaderOptions | header options} object * * @returns The first locale that resolved from header string. if you use `accept-language` header and `*` (any language) or empty string is detected, return `en-US`. if `lang` option or header are not a well-formed BCP 47 language tag, return `null`. */ export function tryHeaderLocale( request: IncomingMessage, - { - lang = DEFAULT_LANG_TAG, - name = ACCEPT_LANGUAGE_HEADER, - parser = parseDefaultHeader - }: HeaderOptions & { lang?: string } = {} + options: HeaderOptions & { lang?: string } = {} ): Intl.Locale | null { try { - return getHeaderLocale(request, { lang, name, parser }) + return getHeaderLocale(request, options) } catch { return null } @@ -234,8 +224,7 @@ export function tryHeaderLocale( * ``` * * @param request - The {@link IncomingMessage | request} - * @param options.lang - The default language tag, default is `en-US`. You must specify the language tag with the {@link https://datatracker.ietf.org/doc/html/rfc4646#section-2.1 | BCP 47 syntax}. - * @param options.name - The cookie name, default is `i18n_locale` + * @param options - The {@link CookieLocaleOptions | cookie locale options}, `lang` option is `en-US` as default, you must specify the language tag with the {@link https://datatracker.ietf.org/doc/html/rfc4646#section-2.1 | BCP 47 syntax}. `name` option is `i18n_locale` as default. * * @throws {RangeError} Throws a {@linkcode RangeError} if `lang` option or cookie name value are not a well-formed BCP 47 language tag. * @@ -243,8 +232,9 @@ export function tryHeaderLocale( */ export function getCookieLocale( request: IncomingMessage, - { lang = DEFAULT_LANG_TAG, name = DEFAULT_COOKIE_NAME } = {} + options: CookieLocaleOptions = {} ): Intl.Locale { + const { lang = DEFAULT_LANG_TAG, name = DEFAULT_COOKIE_NAME } = options const getter = () => { const cookieRaw = request.headers.cookie const cookie = parse(cookieRaw || '') @@ -259,17 +249,16 @@ export function getCookieLocale( * @description Unlike {@link getCookieLocale}, this function does not throw an error if the locale cannot be obtained, this function returns `null`. * * @param request - The {@link IncomingMessage | request} - * @param options.lang - The default language tag, default is `en-US`. You must specify the language tag with the {@link https://datatracker.ietf.org/doc/html/rfc4646#section-2.1 | BCP 47 syntax}. - * @param options.name - The cookie name, default is `i18n_locale` + * @param options - The {@link CookieLocaleOptions | cookie locale options} * * @returns The locale that resolved from cookie. if `lang` option or cookie name value are not a well-formed BCP 47 language tag, return `null`. */ export function tryCookieLocale( request: IncomingMessage, - { lang = DEFAULT_LANG_TAG, name = DEFAULT_COOKIE_NAME } = {} + options: CookieLocaleOptions = {} ): Intl.Locale | null { try { - return getCookieLocale(request, { lang, name }) + return getCookieLocale(request, options) } catch { return null } @@ -293,18 +282,19 @@ export function tryCookieLocale( * * @param response - The {@link OutgoingMessage | response} * @param locale - The locale value - * @param options - The cookie options, `name` option is `i18n_locale` as default, and `path` option is `/` as default. + * @param options - The {@link CookieOptions | cookie options}, `name` option is `i18n_locale` as default * * @throws {SyntaxError} Throws the {@linkcode SyntaxError} if `locale` is invalid. */ export function setCookieLocale( response: OutgoingMessage, locale: string | Intl.Locale, - options: CookieOptions = { name: DEFAULT_COOKIE_NAME } // eslint-disable-line unicorn/no-object-as-default-parameter -- NOTE: allow + options: CookieOptions = {} ): void { + const { name = DEFAULT_COOKIE_NAME } = options validateLocale(locale) - const setCookies = getExistCookies(options.name!, () => response.getHeader('set-cookie')) - const target = serialize(options.name!, locale.toString(), { + const setCookies = getExistCookies(name, () => response.getHeader('set-cookie')) + const target = serialize(name, locale.toString(), { path: '/', ...options }) @@ -344,18 +334,14 @@ function getURL(request: IncomingMessage): URL { * get the locale from the path * * @param request - the {@link IncomingMessage | request} - * @param options.lang - the language tag, which is as default `'en-US'`. optional - * @param options.parser - the path language parser, default {@linkcode pathLanguageParser}, optional + * @param options - the {@link PathOptions | path options} object * * @throws {RangeError} Throws the {@linkcode RangeError} if the language in the path, that is not a well-formed BCP 47 language tag. * * @returns The locale that resolved from path */ -export function getPathLocale( - request: IncomingMessage, - { lang = DEFAULT_LANG_TAG, parser = pathLanguageParser }: PathOptions = {} -): Intl.Locale { - return _getPathLocale(getURL(request), { lang, parser }) +export function getPathLocale(request: IncomingMessage, options: PathOptions = {}): Intl.Locale { + return _getPathLocale(getURL(request), options) } /** @@ -364,17 +350,16 @@ export function getPathLocale( * @description Unlike {@linkcode getPathLocale}, this function does not throw an error if the locale cannot be obtained, this function returns `null`. * * @param request - the {@link IncomingMessage | request} - * @param options.lang - the language tag, which is as default `'en-US'`. optional - * @param options.parser - the path language parser, default {@linkcode pathLanguageParser}, optional + * @param options - the {@link PathOptions | path options} object * * @returns The locale that resolved from path. if the language in the path, that is not a well-formed BCP 47 language tag, return `null`. */ export function tryPathLocale( request: IncomingMessage, - { lang = DEFAULT_LANG_TAG, parser = pathLanguageParser }: PathOptions = {} + options: PathOptions = {} ): Intl.Locale | null { try { - return getPathLocale(request, { lang, parser }) + return getPathLocale(request, options) } catch { return null } @@ -384,17 +369,14 @@ export function tryPathLocale( * get the locale from the query * * @param request - the {@link IncomingMessage | request} - * @param options.lang - the language tag, which is as default `'en-US'`. optional - * @param options.name - the query param name, default `'locale'`. optional + * @param options - The {@link QueryOptions | query options}, `lang` option is `en-US` as default, `name` option is `locale` as default. * * @throws {RangeError} Throws the {@linkcode RangeError} if the language in the query, that is not a well-formed BCP 47 language tag. * * @returns The locale that resolved from query */ -export function getQueryLocale( - request: IncomingMessage, - { lang = DEFAULT_LANG_TAG, name = 'locale' }: QueryOptions = {} -): Intl.Locale { +export function getQueryLocale(request: IncomingMessage, options: QueryOptions = {}): Intl.Locale { + const { lang = DEFAULT_LANG_TAG, name = 'locale' } = options return _getQueryLocale(getURL(request), { lang, name }) } @@ -404,15 +386,15 @@ export function getQueryLocale( * @description Unlike {@linkcode getQueryLocale}, this function does not throw an error if the locale cannot be obtained, this function returns `null`. * * @param request - the {@link IncomingMessage | request} - * @param options.lang - the language tag, which is as default `'en-US'`. optional - * @param options.name - the query param name, default `'locale'`. optional + * @param options - The {@link QueryOptions | query options}, `lang` option is `en-US` as default, `name` option is `locale` as default. * * @returns The locale that resolved from query. if the language in the query, that is not a well-formed BCP 47 language tag, return `null`. */ export function tryQueryLocale( request: IncomingMessage, - { lang = DEFAULT_LANG_TAG, name = 'locale' }: QueryOptions = {} + options: QueryOptions = {} ): Intl.Locale | null { + const { lang = DEFAULT_LANG_TAG, name = 'locale' } = options try { return getQueryLocale(request, { lang, name }) } catch { diff --git a/src/web.ts b/src/web.ts index 5fc344a..2f356a5 100644 --- a/src/web.ts +++ b/src/web.ts @@ -15,9 +15,14 @@ import { parseDefaultHeader, validateLocale } from './http.ts' -import { pathLanguageParser } from './shared.ts' -import type { CookieOptions, HeaderOptions, PathOptions, QueryOptions } from './http.ts' +import type { + CookieLocaleOptions, + CookieOptions, + HeaderOptions, + PathOptions, + QueryOptions +} from './http.ts' /** * get languages from header @@ -40,17 +45,14 @@ import type { CookieOptions, HeaderOptions, PathOptions, QueryOptions } from './ * ``` * * @param request - The {@link Request | request} - * @param options.name - The header name, which is as default `accept-language`. - * @param options.parser - The parser function, which is as default {@linkcode parseDefaultHeader}. If you are specifying more than one in your own format, you need a parser. + * @param options - The {@link HeaderOptions | header options} object. `name` option is `accept-language` as default. * * @returns The array of language tags, if you use `accept-language` header and `*` (any language) or empty string is detected, return an empty array. */ -export function getHeaderLanguages( - request: Request, - { name = ACCEPT_LANGUAGE_HEADER, parser = parseDefaultHeader }: HeaderOptions = {} -): string[] { +export function getHeaderLanguages(request: Request, options: HeaderOptions = {}): string[] { + const { name = ACCEPT_LANGUAGE_HEADER } = options const getter = () => request.headers.get(name) - return getHeaderLanguagesWithGetter(getter, { name, parser }) + return getHeaderLanguagesWithGetter(getter, options) } /** @@ -74,16 +76,12 @@ export function getHeaderLanguages( * ``` * * @param request - The {@link Request | request} - * @param options.name - The header name, which is as default `accept-language`. - * @param options.parser - The parser function, which is as default {@linkcode parseDefaultHeader}. If you are specifying more than one in your own format, you need a parser. + * @param options - The {@link HeaderOptions | header options} object * * @returns The **first language tag** of header, if header is not exists, or `*` (any language), return empty string. */ -export function getHeaderLanguage( - request: Request, - { name = ACCEPT_LANGUAGE_HEADER, parser = parseDefaultHeader }: HeaderOptions = {} -): string { - return getHeaderLanguages(request, { name, parser })[0] || '' +export function getHeaderLanguage(request: Request, options: HeaderOptions = {}): string { + return getHeaderLanguages(request, options)[0] || '' } /** @@ -108,22 +106,15 @@ export function getHeaderLanguage( * ``` * * @param request - The {@link Request | request} - * @param options.name - The header name, which is as default `accept-language`. - * @param options.parser - The parser function, which is as default {@linkcode parseDefaultHeader}. If you are specifying more than one in your own format, you need a parser. + * @param options - The {@link HeaderOptions | header options} object * * @throws {RangeError} Throws the {@linkcode RangeError} if header are not a well-formed BCP 47 language tag. * * @returns The locales that wrapped from header, if you use `accept-language` header and `*` (any language) or empty string is detected, return an empty array. */ -export function getHeaderLocales( - request: Request, - { name = ACCEPT_LANGUAGE_HEADER, parser = parseDefaultHeader }: HeaderOptions = {} -): Intl.Locale[] { +export function getHeaderLocales(request: Request, options: HeaderOptions = {}): Intl.Locale[] { // @ts-expect-error -- FIXME: this type error needs to be fixed - return mapToLocaleFromLanguageTag(getHeaderLanguages, request, { - name, - parser - }) + return mapToLocaleFromLanguageTag(getHeaderLanguages, request, options) } /** @@ -132,17 +123,16 @@ export function getHeaderLocales( * @description wrap language tags with {@link Intl.Locale | locale}, languages tags will be parsed from `accept-language` header as default. Unlike {@link getHeaderLocales}, this function does not throw an error if the locale cannot be obtained, this function returns `null`. * * @param request - The {@link Request | request} - * @param options.name - The header name, which is as default `accept-language`. - * @param options.parser - The parser function, which is as default {@linkcode parseDefaultHeader}. If you are specifying more than one in your own format, you need a parser. + * @param options - The {@link HeaderOptions | header options} object * * @returns The locales that wrapped from header, if you use `accept-language` header and `*` (any language) or empty string is detected, return an empty array. if header are not a well-formed BCP 47 language tag, return `null`. */ export function tryHeaderLocales( request: Request, - { name = ACCEPT_LANGUAGE_HEADER, parser = parseDefaultHeader }: HeaderOptions = {} + options: HeaderOptions = {} ): Intl.Locale[] | null { try { - return getHeaderLocales(request, { name, parser }) + return getHeaderLocales(request, options) } catch { return null } @@ -168,9 +158,7 @@ export function tryHeaderLocales( * }) * * @param request - The {@link Request | request} - * @param options.lang - The default language tag, Optional. default value is `en-US`. You must specify the language tag with the {@link https://datatracker.ietf.org/doc/html/rfc4646#section-2.1 | BCP 47 syntax}. - * @param options.name - The header name, which is as default `accept-language`. - * @param options.parser - The parser function, which is as default {@linkcode parseDefaultHeader}. If you are specifying more than one in your own format, you need a parser. + * @param options - The {@link HeaderOptions | header options} object. `lang` option is `en-US` as default, you must specify the language tag with the {@link https://datatracker.ietf.org/doc/html/rfc4646#section-2.1 | BCP 47 syntax}. `name` option is `accept-language` as default, and `parser` option is {@linkcode parseDefaultHeader} as default. * * @throws {RangeError} Throws the {@linkcode RangeError} if `lang` option or header are not a well-formed BCP 47 language tag. * @@ -178,12 +166,13 @@ export function tryHeaderLocales( */ export function getHeaderLocale( request: Request, - { + options: HeaderOptions & { lang?: string } = {} +): Intl.Locale { + const { lang = DEFAULT_LANG_TAG, name = ACCEPT_LANGUAGE_HEADER, parser = parseDefaultHeader - }: HeaderOptions & { lang?: string } = {} -): Intl.Locale { + } = options return getLocaleWithGetter(() => getHeaderLanguages(request, { name, parser })[0] || lang) } @@ -193,22 +182,16 @@ export function getHeaderLocale( * @description wrap language tag with {@link Intl.Locale | locale}, languages tags will be parsed from `accept-language` header as default. Unlike {@link getHeaderLocale}, this function does not throw an error if the locale cannot be obtained, this function returns `null`. * * @param request - The {@link Request | request} - * @param options.lang - The default language tag, Optional. default value is `en-US`. You must specify the language tag with the {@link https://datatracker.ietf.org/doc/html/rfc4646#section-2.1 | BCP 47 syntax}. - * @param options.name - The header name, which is as default `accept-language`. - * @param options.parser - The parser function, which is as default {@linkcode parseDefaultHeader}. If you are specifying more than one in your own format, you need a parser. + * @param options - The {@link HeaderOptions | header options} object * * @returns The first locale that resolved from header string. if you use `accept-language` header and `*` (any language) or empty string is detected, return `en-US`. if `lang` option or header are not a well-formed BCP 47 language tag, return `null`. */ export function tryHeaderLocale( request: Request, - { - lang = DEFAULT_LANG_TAG, - name = ACCEPT_LANGUAGE_HEADER, - parser = parseDefaultHeader - }: HeaderOptions & { lang?: string } = {} + options: HeaderOptions & { lang?: string } = {} ): Intl.Locale | null { try { - return getHeaderLocale(request, { lang, name, parser }) + return getHeaderLocale(request, options) } catch { return null } @@ -233,17 +216,14 @@ export function tryHeaderLocale( * ``` * * @param request - The {@link Request | request} - * @param options.lang - The default language tag, default is `en-US`. You must specify the language tag with the {@link https://datatracker.ietf.org/doc/html/rfc4646#section-2.1 | BCP 47 syntax}. - * @param options.name - The cookie name, default is `i18n_locale` + * @param options - The {@link CookieLocaleOptions | cookie locale options}, `lang` option is `en-US` as default, you must specify the language tag with the {@link https://datatracker.ietf.org/doc/html/rfc4646#section-2.1 | BCP 47 syntax}. `name` option is `i18n_locale` as default. * * @throws {RangeError} Throws a {@linkcode RangeError} if `lang` option or cookie name value are not a well-formed BCP 47 language tag. * * @returns The locale that resolved from cookie */ -export function getCookieLocale( - request: Request, - { lang = DEFAULT_LANG_TAG, name = DEFAULT_COOKIE_NAME } = {} -): Intl.Locale { +export function getCookieLocale(request: Request, options: CookieLocaleOptions = {}): Intl.Locale { + const { lang = DEFAULT_LANG_TAG, name = DEFAULT_COOKIE_NAME } = options const getter = () => { const cookieRaw = request.headers.get('cookie') const cookie = parse(cookieRaw || '') @@ -258,17 +238,16 @@ export function getCookieLocale( * @description Unlike {@linkcode getCookieLocale}, this function does not throw an error if the locale cannot be obtained, this function returns `null`. * * @param request - The {@link Request | request} - * @param options.lang - The default language tag, default is `en-US`. You must specify the language tag with the {@link https://datatracker.ietf.org/doc/html/rfc4646#section-2.1 | BCP 47 syntax}. - * @param options.name - The cookie name, default is `i18n_locale` + * @param options - The {@link CookieLocaleOptions | cookie locale options} * * @returns The locale that resolved from cookie. if `lang` option or cookie name value are not a well-formed BCP 47 language tag, return `null`. */ export function tryCookieLocale( request: Request, - { lang = DEFAULT_LANG_TAG, name = DEFAULT_COOKIE_NAME } = {} + options: CookieLocaleOptions = {} ): Intl.Locale | null { try { - return getCookieLocale(request, { lang, name }) + return getCookieLocale(request, options) } catch { return null } @@ -296,18 +275,19 @@ export function tryCookieLocale( * * @param response - The {@link Response | response} * @param locale - The locale value - * @param options - The cookie options, `name` option is `i18n_locale` as default, and `path` option is `/` as default. + * @param options - The {@link CookieOptions | cookie options}, `name` option is `i18n_locale` as default * * @throws {SyntaxError} Throws the {@linkcode SyntaxError} if `locale` is invalid. */ export function setCookieLocale( response: Response, locale: string | Intl.Locale, - options: CookieOptions = { name: DEFAULT_COOKIE_NAME } // eslint-disable-line unicorn/no-object-as-default-parameter -- NOTE: allow + options: CookieOptions = {} ): void { + const { name = DEFAULT_COOKIE_NAME } = options validateLocale(locale) - const setCookies = getExistCookies(options.name!, () => response.headers.getSetCookie()) - const target = serialize(options.name!, locale.toString(), { + const setCookies = getExistCookies(name, () => response.headers.getSetCookie()) + const target = serialize(name, locale.toString(), { path: '/', ...options }) @@ -318,18 +298,14 @@ export function setCookieLocale( * get the locale from the path * * @param request - the {@link Request | request} - * @param options.lang - the language tag, which is as default `'en-US'`. optional - * @param options.parser - the path language parser, default {@linkcode pathLanguageParser}, optional + * @param options - the {@link PathOptions | path options} object * * @throws {RangeError} Throws the {@linkcode RangeError} if the language in the path, that is not a well-formed BCP 47 language tag. * * @returns The locale that resolved from path */ -export function getPathLocale( - request: Request, - { lang = DEFAULT_LANG_TAG, parser = pathLanguageParser }: PathOptions = {} -): Intl.Locale { - return _getPathLocale(new URL(request.url), { lang, parser }) +export function getPathLocale(request: Request, options: PathOptions = {}): Intl.Locale { + return _getPathLocale(new URL(request.url), options) } /** @@ -338,17 +314,13 @@ export function getPathLocale( * @description Unlike {@linkcode getPathLocale}, this function does not throw an error if the locale cannot be obtained, this function returns `null`. * * @param request - the {@link Request | request} - * @param options.lang - the language tag, which is as default `'en-US'`. optional - * @param options.parser - the path language parser, default {@linkcode pathLanguageParser}, optional + * @param options - the {@link PathOptions | path options} object * * @returns The locale that resolved from path. if the language in the path, that is not a well-formed BCP 47 language tag, return `null`. */ -export function tryPathLocale( - request: Request, - { lang = DEFAULT_LANG_TAG, parser = pathLanguageParser }: PathOptions = {} -): Intl.Locale | null { +export function tryPathLocale(request: Request, options: PathOptions = {}): Intl.Locale | null { try { - return getPathLocale(request, { lang, parser }) + return getPathLocale(request, options) } catch { return null } @@ -358,17 +330,14 @@ export function tryPathLocale( * get the locale from the query * * @param request - the {@link Request | request} - * @param options.lang - the language tag, which is as default `'en-US'`. optional - * @param options.name - the query param name, default `'locale'`. optional + * @param options - The {@link QueryOptions | query options}, `lang` option is `en-US` as default, `name` option is `locale` as default. * * @throws {RangeError} Throws the {@linkcode RangeError} if the language in the query, that is not a well-formed BCP 47 language tag. * * @returns The locale that resolved from query */ -export function getQueryLocale( - request: Request, - { lang = DEFAULT_LANG_TAG, name = 'locale' }: QueryOptions = {} -): Intl.Locale { +export function getQueryLocale(request: Request, options: QueryOptions = {}): Intl.Locale { + const { lang = DEFAULT_LANG_TAG, name = 'locale' } = options return _getQueryLocale(new URL(request.url), { lang, name }) } @@ -378,15 +347,12 @@ export function getQueryLocale( * @description Unlike {@linkcode getQueryLocale}, this function does not throw an error if the locale cannot be obtained, this function returns `null`. * * @param request - the {@link Request | request} - * @param options.lang - the language tag, which is as default `'en-US'`. optional - * @param options.name - the query param name, default `'locale'`. optional + * @param options - The {@link QueryOptions | query options}, `lang` option is `en-US` as default, `name` option is `locale` as default. * * @returns The locale that resolved from query. if the language in the query, that is not a well-formed BCP 47 language tag, return `null`. */ -export function tryQueryLocale( - request: Request, - { lang = DEFAULT_LANG_TAG, name = 'locale' }: QueryOptions = {} -): Intl.Locale | null { +export function tryQueryLocale(request: Request, options: QueryOptions = {}): Intl.Locale | null { + const { lang = DEFAULT_LANG_TAG, name = 'locale' } = options try { return getQueryLocale(request, { lang, name }) } catch { From 263527670304cfb972c29507acc798f2fa12a401 Mon Sep 17 00:00:00 2001 From: kazuya kawaguchi Date: Wed, 12 Nov 2025 18:26:18 +0900 Subject: [PATCH 2/3] fix --- deno/http.ts | 41 +++++++++------ deno/shared.ts | 1 - deno/web.ts | 136 ++++++++++++++++++------------------------------ scripts/deno.ts | 1 - 4 files changed, 77 insertions(+), 102 deletions(-) diff --git a/deno/http.ts b/deno/http.ts index c7bb9d3..5a66fc0 100644 --- a/deno/http.ts +++ b/deno/http.ts @@ -125,6 +125,20 @@ export type CookieOptions = CookieSerializeOptions & { name?: string } +/** + * Cookie locale options type + */ +export type CookieLocaleOptions = { + /** + * The default language tag + */ + lang?: string + /** + * Cookie name + */ + name?: string +} + /** * Header options type */ @@ -154,13 +168,15 @@ export function parseDefaultHeader(input: string): string[] { * get languages from header with getter function * * @param getter - the header string getter function + * @param options - the {@link HeaderOptions | header options} * * @returns The array of language tags */ export function getHeaderLanguagesWithGetter( getter: () => string | null | undefined, - { name = ACCEPT_LANGUAGE_HEADER, parser = parseDefaultHeader }: HeaderOptions = {} + options: HeaderOptions = {} ): string[] { + const { name = ACCEPT_LANGUAGE_HEADER, parser = parseDefaultHeader } = options const langString = getter() return langString ? name === ACCEPT_LANGUAGE_HEADER @@ -247,15 +263,12 @@ export type PathOptions = { * get the language from the path * * @param path - the target path - * @param options.lang - the language tag, which is as default `'en-US'`. optional - * @param options.parser - the path language parser, optional + * @param options - the {@link PathOptions | path options} object * * @returns the language that is parsed by the path language parser, if the language is not detected, return a `options.lang` value */ -export function getPathLanguage( - path: string | URL, - { lang = DEFAULT_LANG_TAG, parser = pathLanguageParser }: PathOptions = {} -): string { +export function getPathLanguage(path: string | URL, options: PathOptions = {}): string { + const { lang = DEFAULT_LANG_TAG, parser = pathLanguageParser } = options return (parser || pathLanguageParser)(path) || lang } @@ -263,18 +276,14 @@ export function getPathLanguage( * get the locale from the path * * @param path - the target path - * @param options.lang - the language tag, which is as default `'en-US'`. optional - * @param options.parser - the path language parser, optional + * @param options - the {@link PathOptions | path options} object * * @throws {RangeError} Throws the {@link RangeError} if the language in the path, that is not a well-formed BCP 47 language tag. * * @returns The locale that resolved from path */ -export function getPathLocale( - path: string | URL, - { lang = DEFAULT_LANG_TAG, parser = pathLanguageParser }: PathOptions = {} -): Intl.Locale { - return new Intl.Locale(getPathLanguage(path, { lang, parser })) +export function getPathLocale(path: string | URL, options: PathOptions = {}): Intl.Locale { + return new Intl.Locale(getPathLanguage(path, options)) } function getURLSearchParams(input: string | URL | URLSearchParams): URLSearchParams { @@ -322,6 +331,7 @@ export function getQueryLanguage( * get the locale from the query * * @param query - the target query + * @param options - The {@link QueryOptions | query options} * @param options.lang - the language tag, which is as default `'en-US'`. optional * @param options.name - the query param name, default `'locale'`. optional * @@ -331,7 +341,8 @@ export function getQueryLanguage( */ export function getQueryLocale( query: string | URL | URLSearchParams, - { lang = DEFAULT_LANG_TAG, name = 'locale' }: QueryOptions = {} + options: QueryOptions = {} ): Intl.Locale { + const { lang = DEFAULT_LANG_TAG, name = 'locale' } = options return new Intl.Locale(getQueryLanguage(query, { lang, name })) } diff --git a/deno/shared.ts b/deno/shared.ts index 67f50b1..a608f9d 100644 --- a/deno/shared.ts +++ b/deno/shared.ts @@ -61,7 +61,6 @@ export function toLocale(val: string | Intl.Locale): Intl.Locale { */ export function validateLangTag(lang: string): boolean { try { - // @ts-ignore NOTE: https://github.com/microsoft/TypeScript/pull/56079 Intl.getCanonicalLocales(lang) return true } catch { diff --git a/deno/web.ts b/deno/web.ts index d774fef..7bb8363 100644 --- a/deno/web.ts +++ b/deno/web.ts @@ -15,9 +15,14 @@ import { parseDefaultHeader, validateLocale } from './http.ts' -import { pathLanguageParser } from './shared.ts' -import type { CookieOptions, HeaderOptions, PathOptions, QueryOptions } from './http.ts' +import type { + CookieLocaleOptions, + CookieOptions, + HeaderOptions, + PathOptions, + QueryOptions +} from './http.ts' /** * get languages from header @@ -40,17 +45,14 @@ import type { CookieOptions, HeaderOptions, PathOptions, QueryOptions } from './ * ``` * * @param request - The {@link Request | request} - * @param options.name - The header name, which is as default `accept-language`. - * @param options.parser - The parser function, which is as default {@linkcode parseDefaultHeader}. If you are specifying more than one in your own format, you need a parser. + * @param options - The {@link HeaderOptions | header options} object. `name` option is `accept-language` as default. * * @returns The array of language tags, if you use `accept-language` header and `*` (any language) or empty string is detected, return an empty array. */ -export function getHeaderLanguages( - request: Request, - { name = ACCEPT_LANGUAGE_HEADER, parser = parseDefaultHeader }: HeaderOptions = {} -): string[] { +export function getHeaderLanguages(request: Request, options: HeaderOptions = {}): string[] { + const { name = ACCEPT_LANGUAGE_HEADER } = options const getter = () => request.headers.get(name) - return getHeaderLanguagesWithGetter(getter, { name, parser }) + return getHeaderLanguagesWithGetter(getter, options) } /** @@ -74,16 +76,12 @@ export function getHeaderLanguages( * ``` * * @param request - The {@link Request | request} - * @param options.name - The header name, which is as default `accept-language`. - * @param options.parser - The parser function, which is as default {@linkcode parseDefaultHeader}. If you are specifying more than one in your own format, you need a parser. + * @param options - The {@link HeaderOptions | header options} object * * @returns The **first language tag** of header, if header is not exists, or `*` (any language), return empty string. */ -export function getHeaderLanguage( - request: Request, - { name = ACCEPT_LANGUAGE_HEADER, parser = parseDefaultHeader }: HeaderOptions = {} -): string { - return getHeaderLanguages(request, { name, parser })[0] || '' +export function getHeaderLanguage(request: Request, options: HeaderOptions = {}): string { + return getHeaderLanguages(request, options)[0] || '' } /** @@ -108,22 +106,15 @@ export function getHeaderLanguage( * ``` * * @param request - The {@link Request | request} - * @param options.name - The header name, which is as default `accept-language`. - * @param options.parser - The parser function, which is as default {@linkcode parseDefaultHeader}. If you are specifying more than one in your own format, you need a parser. + * @param options - The {@link HeaderOptions | header options} object * * @throws {RangeError} Throws the {@linkcode RangeError} if header are not a well-formed BCP 47 language tag. * * @returns The locales that wrapped from header, if you use `accept-language` header and `*` (any language) or empty string is detected, return an empty array. */ -export function getHeaderLocales( - request: Request, - { name = ACCEPT_LANGUAGE_HEADER, parser = parseDefaultHeader }: HeaderOptions = {} -): Intl.Locale[] { +export function getHeaderLocales(request: Request, options: HeaderOptions = {}): Intl.Locale[] { // @ts-expect-error -- FIXME: this type error needs to be fixed - return mapToLocaleFromLanguageTag(getHeaderLanguages, request, { - name, - parser - }) + return mapToLocaleFromLanguageTag(getHeaderLanguages, request, options) } /** @@ -132,17 +123,16 @@ export function getHeaderLocales( * @description wrap language tags with {@link Intl.Locale | locale}, languages tags will be parsed from `accept-language` header as default. Unlike {@link getHeaderLocales}, this function does not throw an error if the locale cannot be obtained, this function returns `null`. * * @param request - The {@link Request | request} - * @param options.name - The header name, which is as default `accept-language`. - * @param options.parser - The parser function, which is as default {@linkcode parseDefaultHeader}. If you are specifying more than one in your own format, you need a parser. + * @param options - The {@link HeaderOptions | header options} object * * @returns The locales that wrapped from header, if you use `accept-language` header and `*` (any language) or empty string is detected, return an empty array. if header are not a well-formed BCP 47 language tag, return `null`. */ export function tryHeaderLocales( request: Request, - { name = ACCEPT_LANGUAGE_HEADER, parser = parseDefaultHeader }: HeaderOptions = {} + options: HeaderOptions = {} ): Intl.Locale[] | null { try { - return getHeaderLocales(request, { name, parser }) + return getHeaderLocales(request, options) } catch { return null } @@ -168,9 +158,7 @@ export function tryHeaderLocales( * }) * * @param request - The {@link Request | request} - * @param options.lang - The default language tag, Optional. default value is `en-US`. You must specify the language tag with the {@link https://datatracker.ietf.org/doc/html/rfc4646#section-2.1 | BCP 47 syntax}. - * @param options.name - The header name, which is as default `accept-language`. - * @param options.parser - The parser function, which is as default {@linkcode parseDefaultHeader}. If you are specifying more than one in your own format, you need a parser. + * @param options - The {@link HeaderOptions | header options} object. `lang` option is `en-US` as default, you must specify the language tag with the {@link https://datatracker.ietf.org/doc/html/rfc4646#section-2.1 | BCP 47 syntax}. `name` option is `accept-language` as default, and `parser` option is {@linkcode parseDefaultHeader} as default. * * @throws {RangeError} Throws the {@linkcode RangeError} if `lang` option or header are not a well-formed BCP 47 language tag. * @@ -178,12 +166,13 @@ export function tryHeaderLocales( */ export function getHeaderLocale( request: Request, - { + options: HeaderOptions & { lang?: string } = {} +): Intl.Locale { + const { lang = DEFAULT_LANG_TAG, name = ACCEPT_LANGUAGE_HEADER, parser = parseDefaultHeader - }: HeaderOptions & { lang?: string } = {} -): Intl.Locale { + } = options return getLocaleWithGetter(() => getHeaderLanguages(request, { name, parser })[0] || lang) } @@ -193,22 +182,16 @@ export function getHeaderLocale( * @description wrap language tag with {@link Intl.Locale | locale}, languages tags will be parsed from `accept-language` header as default. Unlike {@link getHeaderLocale}, this function does not throw an error if the locale cannot be obtained, this function returns `null`. * * @param request - The {@link Request | request} - * @param options.lang - The default language tag, Optional. default value is `en-US`. You must specify the language tag with the {@link https://datatracker.ietf.org/doc/html/rfc4646#section-2.1 | BCP 47 syntax}. - * @param options.name - The header name, which is as default `accept-language`. - * @param options.parser - The parser function, which is as default {@linkcode parseDefaultHeader}. If you are specifying more than one in your own format, you need a parser. + * @param options - The {@link HeaderOptions | header options} object * * @returns The first locale that resolved from header string. if you use `accept-language` header and `*` (any language) or empty string is detected, return `en-US`. if `lang` option or header are not a well-formed BCP 47 language tag, return `null`. */ export function tryHeaderLocale( request: Request, - { - lang = DEFAULT_LANG_TAG, - name = ACCEPT_LANGUAGE_HEADER, - parser = parseDefaultHeader - }: HeaderOptions & { lang?: string } = {} + options: HeaderOptions & { lang?: string } = {} ): Intl.Locale | null { try { - return getHeaderLocale(request, { lang, name, parser }) + return getHeaderLocale(request, options) } catch { return null } @@ -233,17 +216,14 @@ export function tryHeaderLocale( * ``` * * @param request - The {@link Request | request} - * @param options.lang - The default language tag, default is `en-US`. You must specify the language tag with the {@link https://datatracker.ietf.org/doc/html/rfc4646#section-2.1 | BCP 47 syntax}. - * @param options.name - The cookie name, default is `i18n_locale` + * @param options - The {@link CookieLocaleOptions | cookie locale options}, `lang` option is `en-US` as default, you must specify the language tag with the {@link https://datatracker.ietf.org/doc/html/rfc4646#section-2.1 | BCP 47 syntax}. `name` option is `i18n_locale` as default. * * @throws {RangeError} Throws a {@linkcode RangeError} if `lang` option or cookie name value are not a well-formed BCP 47 language tag. * * @returns The locale that resolved from cookie */ -export function getCookieLocale( - request: Request, - { lang = DEFAULT_LANG_TAG, name = DEFAULT_COOKIE_NAME } = {} -): Intl.Locale { +export function getCookieLocale(request: Request, options: CookieLocaleOptions = {}): Intl.Locale { + const { lang = DEFAULT_LANG_TAG, name = DEFAULT_COOKIE_NAME } = options const getter = () => { const cookieRaw = request.headers.get('cookie') const cookie = parse(cookieRaw || '') @@ -258,17 +238,16 @@ export function getCookieLocale( * @description Unlike {@linkcode getCookieLocale}, this function does not throw an error if the locale cannot be obtained, this function returns `null`. * * @param request - The {@link Request | request} - * @param options.lang - The default language tag, default is `en-US`. You must specify the language tag with the {@link https://datatracker.ietf.org/doc/html/rfc4646#section-2.1 | BCP 47 syntax}. - * @param options.name - The cookie name, default is `i18n_locale` + * @param options - The {@link CookieLocaleOptions | cookie locale options} * * @returns The locale that resolved from cookie. if `lang` option or cookie name value are not a well-formed BCP 47 language tag, return `null`. */ export function tryCookieLocale( request: Request, - { lang = DEFAULT_LANG_TAG, name = DEFAULT_COOKIE_NAME } = {} + options: CookieLocaleOptions = {} ): Intl.Locale | null { try { - return getCookieLocale(request, { lang, name }) + return getCookieLocale(request, options) } catch { return null } @@ -296,18 +275,19 @@ export function tryCookieLocale( * * @param response - The {@link Response | response} * @param locale - The locale value - * @param options - The cookie options, `name` option is `i18n_locale` as default, and `path` option is `/` as default. + * @param options - The {@link CookieOptions | cookie options}, `name` option is `i18n_locale` as default * * @throws {SyntaxError} Throws the {@linkcode SyntaxError} if `locale` is invalid. */ export function setCookieLocale( response: Response, locale: string | Intl.Locale, - options: CookieOptions = { name: DEFAULT_COOKIE_NAME } // eslint-disable-line unicorn/no-object-as-default-parameter -- NOTE: allow + options: CookieOptions = {} ): void { + const { name = DEFAULT_COOKIE_NAME } = options validateLocale(locale) - const setCookies = getExistCookies(options.name!, () => response.headers.getSetCookie()) - const target = serialize(options.name!, locale.toString(), { + const setCookies = getExistCookies(name, () => response.headers.getSetCookie()) + const target = serialize(name, locale.toString(), { path: '/', ...options }) @@ -318,18 +298,14 @@ export function setCookieLocale( * get the locale from the path * * @param request - the {@link Request | request} - * @param options.lang - the language tag, which is as default `'en-US'`. optional - * @param options.parser - the path language parser, default {@linkcode pathLanguageParser}, optional + * @param options - the {@link PathOptions | path options} object * * @throws {RangeError} Throws the {@linkcode RangeError} if the language in the path, that is not a well-formed BCP 47 language tag. * * @returns The locale that resolved from path */ -export function getPathLocale( - request: Request, - { lang = DEFAULT_LANG_TAG, parser = pathLanguageParser }: PathOptions = {} -): Intl.Locale { - return _getPathLocale(new URL(request.url), { lang, parser }) +export function getPathLocale(request: Request, options: PathOptions = {}): Intl.Locale { + return _getPathLocale(new URL(request.url), options) } /** @@ -338,17 +314,13 @@ export function getPathLocale( * @description Unlike {@linkcode getPathLocale}, this function does not throw an error if the locale cannot be obtained, this function returns `null`. * * @param request - the {@link Request | request} - * @param options.lang - the language tag, which is as default `'en-US'`. optional - * @param options.parser - the path language parser, default {@linkcode pathLanguageParser}, optional + * @param options - the {@link PathOptions | path options} object * * @returns The locale that resolved from path. if the language in the path, that is not a well-formed BCP 47 language tag, return `null`. */ -export function tryPathLocale( - request: Request, - { lang = DEFAULT_LANG_TAG, parser = pathLanguageParser }: PathOptions = {} -): Intl.Locale | null { +export function tryPathLocale(request: Request, options: PathOptions = {}): Intl.Locale | null { try { - return getPathLocale(request, { lang, parser }) + return getPathLocale(request, options) } catch { return null } @@ -358,17 +330,14 @@ export function tryPathLocale( * get the locale from the query * * @param request - the {@link Request | request} - * @param options.lang - the language tag, which is as default `'en-US'`. optional - * @param options.name - the query param name, default `'locale'`. optional + * @param options - The {@link QueryOptions | query options}, `lang` option is `en-US` as default, `name` option is `locale` as default. * * @throws {RangeError} Throws the {@linkcode RangeError} if the language in the query, that is not a well-formed BCP 47 language tag. * * @returns The locale that resolved from query */ -export function getQueryLocale( - request: Request, - { lang = DEFAULT_LANG_TAG, name = 'locale' }: QueryOptions = {} -): Intl.Locale { +export function getQueryLocale(request: Request, options: QueryOptions = {}): Intl.Locale { + const { lang = DEFAULT_LANG_TAG, name = 'locale' } = options return _getQueryLocale(new URL(request.url), { lang, name }) } @@ -378,15 +347,12 @@ export function getQueryLocale( * @description Unlike {@linkcode getQueryLocale}, this function does not throw an error if the locale cannot be obtained, this function returns `null`. * * @param request - the {@link Request | request} - * @param options.lang - the language tag, which is as default `'en-US'`. optional - * @param options.name - the query param name, default `'locale'`. optional + * @param options - The {@link QueryOptions | query options}, `lang` option is `en-US` as default, `name` option is `locale` as default. * * @returns The locale that resolved from query. if the language in the query, that is not a well-formed BCP 47 language tag, return `null`. */ -export function tryQueryLocale( - request: Request, - { lang = DEFAULT_LANG_TAG, name = 'locale' }: QueryOptions = {} -): Intl.Locale | null { +export function tryQueryLocale(request: Request, options: QueryOptions = {}): Intl.Locale | null { + const { lang = DEFAULT_LANG_TAG, name = 'locale' } = options try { return getQueryLocale(request, { lang, name }) } catch { diff --git a/scripts/deno.ts b/scripts/deno.ts index f735259..a2e44a7 100644 --- a/scripts/deno.ts +++ b/scripts/deno.ts @@ -11,7 +11,6 @@ const TARGETS = [ 'index.ts', 'locale.ts', 'shared.ts', - 'shim.d.ts', 'types.ts', 'web.ts' ] From ed83cdac6bc5c24e4601c2a4783dc91e7fc6506c Mon Sep 17 00:00:00 2001 From: kazuya kawaguchi Date: Wed, 12 Nov 2025 18:28:11 +0900 Subject: [PATCH 3/3] fix: typo --- deno/shared.ts | 2 +- src/shared.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/deno/shared.ts b/deno/shared.ts index a608f9d..eafa0bd 100644 --- a/deno/shared.ts +++ b/deno/shared.ts @@ -92,7 +92,7 @@ export function parseAcceptLanguage(value: string): string[] { * ```ts * const oldLangName = 'en_US' * const langTag = normalizeLanguageName(oldLangName) - * conosle.log(langTag) // en-US + * console.log(langTag) // en-US * ``` * * @param langName - The target language name diff --git a/src/shared.ts b/src/shared.ts index a608f9d..eafa0bd 100644 --- a/src/shared.ts +++ b/src/shared.ts @@ -92,7 +92,7 @@ export function parseAcceptLanguage(value: string): string[] { * ```ts * const oldLangName = 'en_US' * const langTag = normalizeLanguageName(oldLangName) - * conosle.log(langTag) // en-US + * console.log(langTag) // en-US * ``` * * @param langName - The target language name