From 1e92327210890ffbb0e3cde155f030a8c7e8af32 Mon Sep 17 00:00:00 2001 From: kazuya kawaguchi Date: Fri, 22 Sep 2023 09:31:56 +0900 Subject: [PATCH] feat: add `getAcceptLanguage` (#5) --- README.md | 1 + src/h3.test.ts | 91 +++++++++++++++++++++++++++++++++++------------- src/h3.ts | 11 ++++++ src/node.test.ts | 28 +++++++++++++++ src/node.ts | 13 ++++++- src/web.test.ts | 20 +++++++++++ src/web.ts | 13 ++++++- 7 files changed, 151 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 40ad2ee..7bb78d6 100644 --- a/README.md +++ b/README.md @@ -132,6 +132,7 @@ You can do `import { ... } from '@intlify/utils'` the above utilities ### HTTP - `getAcceptLanguages` +- `getAcceptLanguage` - `getLocale` - `getCookieLocale` - `setCookieLocale` diff --git a/src/h3.test.ts b/src/h3.test.ts index 495c981..222ace6 100644 --- a/src/h3.test.ts +++ b/src/h3.test.ts @@ -2,6 +2,7 @@ import { beforeEach, describe, expect, test } from 'vitest' import { createApp, eventHandler, toNodeListener } from 'h3' import supertest from 'supertest' import { + getAcceptLanguage, getAcceptLanguages, getCookieLocale, getLocale, @@ -14,7 +15,7 @@ import type { SuperTest, Test } from 'supertest' describe('getAcceptLanguages', () => { test('basic', () => { - const eventMock = { + const mockEvent = { node: { req: { method: 'GET', @@ -24,11 +25,11 @@ describe('getAcceptLanguages', () => { }, }, } as H3Event - expect(getAcceptLanguages(eventMock)).toEqual(['en-US', 'en', 'ja']) + expect(getAcceptLanguages(mockEvent)).toEqual(['en-US', 'en', 'ja']) }) test('any language', () => { - const eventMock = { + const mockEvent = { node: { req: { method: 'GET', @@ -38,11 +39,11 @@ describe('getAcceptLanguages', () => { }, }, } as H3Event - expect(getAcceptLanguages(eventMock)).toEqual([]) + expect(getAcceptLanguages(mockEvent)).toEqual([]) }) test('empty', () => { - const eventMock = { + const mockEvent = { node: { req: { method: 'GET', @@ -50,13 +51,55 @@ describe('getAcceptLanguages', () => { }, }, } as H3Event - expect(getAcceptLanguages(eventMock)).toEqual([]) + expect(getAcceptLanguages(mockEvent)).toEqual([]) + }) +}) + +describe('getAcceptLanguage', () => { + test('basic', () => { + const mockEvent = { + node: { + req: { + method: 'GET', + headers: { + 'accept-language': 'en-US,en;q=0.9,ja;q=0.8', + }, + }, + }, + } as H3Event + expect(getAcceptLanguage(mockEvent)).toEqual('en-US') + }) + + test('any language', () => { + const mockEvent = { + node: { + req: { + method: 'GET', + headers: { + 'accept-language': '*', + }, + }, + }, + } as H3Event + expect(getAcceptLanguage(mockEvent)).toEqual('') + }) + + test('empty', () => { + const mockEvent = { + node: { + req: { + method: 'GET', + headers: {}, + }, + }, + } as H3Event + expect(getAcceptLanguage(mockEvent)).toEqual('') }) }) describe('getLocale', () => { test('basic', () => { - const eventMock = { + const mockEvent = { node: { req: { method: 'GET', @@ -66,7 +109,7 @@ describe('getLocale', () => { }, }, } as H3Event - const locale = getLocale(eventMock) + const locale = getLocale(mockEvent) expect(locale.baseName).toEqual('en-US') expect(locale.language).toEqual('en') @@ -74,7 +117,7 @@ describe('getLocale', () => { }) test('accept-language is any language', () => { - const eventMock = { + const mockEvent = { node: { req: { method: 'GET', @@ -84,13 +127,13 @@ describe('getLocale', () => { }, }, } as H3Event - const locale = getLocale(eventMock) + const locale = getLocale(mockEvent) expect(locale.baseName).toEqual(DEFAULT_LANG_TAG) }) test('specify default language', () => { - const eventMock = { + const mockEvent = { node: { req: { method: 'GET', @@ -100,13 +143,13 @@ describe('getLocale', () => { }, }, } as H3Event - const locale = getLocale(eventMock, 'ja-JP') + const locale = getLocale(mockEvent, 'ja-JP') expect(locale.baseName).toEqual('ja-JP') }) test('RangeError', () => { - const eventMock = { + const mockEvent = { node: { req: { method: 'GET', @@ -117,13 +160,13 @@ describe('getLocale', () => { }, } as H3Event - expect(() => getLocale(eventMock, 'ja-JP')).toThrowError(RangeError) + expect(() => getLocale(mockEvent, 'ja-JP')).toThrowError(RangeError) }) }) describe('getCookieLocale', () => { test('basic', () => { - const eventMock = { + const mockEvent = { node: { req: { method: 'GET', @@ -133,7 +176,7 @@ describe('getCookieLocale', () => { }, }, } as H3Event - const locale = getCookieLocale(eventMock) + const locale = getCookieLocale(mockEvent) expect(locale.baseName).toEqual('ja-US') expect(locale.language).toEqual('ja') @@ -141,7 +184,7 @@ describe('getCookieLocale', () => { }) test('cookie is empty', () => { - const eventMock = { + const mockEvent = { node: { req: { method: 'GET', @@ -149,13 +192,13 @@ describe('getCookieLocale', () => { }, }, } as H3Event - const locale = getCookieLocale(eventMock) + const locale = getCookieLocale(mockEvent) expect(locale.baseName).toEqual(DEFAULT_LANG_TAG) }) test('specify default language', () => { - const eventMock = { + const mockEvent = { node: { req: { method: 'GET', @@ -163,13 +206,13 @@ describe('getCookieLocale', () => { }, }, } as H3Event - const locale = getCookieLocale(eventMock, { lang: 'ja-JP' }) + const locale = getCookieLocale(mockEvent, { lang: 'ja-JP' }) expect(locale.baseName).toEqual('ja-JP') }) test('specify cookie name', () => { - const eventMock = { + const mockEvent = { node: { req: { method: 'GET', @@ -179,13 +222,13 @@ describe('getCookieLocale', () => { }, }, } as H3Event - const locale = getCookieLocale(eventMock, { name: 'intlify_locale' }) + const locale = getCookieLocale(mockEvent, { name: 'intlify_locale' }) expect(locale.baseName).toEqual('fr-FR') }) test('RangeError', () => { - const eventMock = { + const mockEvent = { node: { req: { method: 'GET', @@ -196,7 +239,7 @@ describe('getCookieLocale', () => { }, } as H3Event - expect(() => getCookieLocale(eventMock, { name: 'intlify_locale' })) + expect(() => getCookieLocale(mockEvent, { name: 'intlify_locale' })) .toThrowError(RangeError) }) }) diff --git a/src/h3.ts b/src/h3.ts index 96152f9..7bd3687 100644 --- a/src/h3.ts +++ b/src/h3.ts @@ -40,6 +40,17 @@ export function getAcceptLanguages(event: H3Event): string[] { return getAcceptLanguagesWithGetter(getter) } +/** + * get accept language + * + * @param {H3Event} event The {@link H3Event | H3} event + * + * @returns {string} The **first language tag** of `accept-language` header, if `accept-language` header is not exists, or `*` (any language), return empty string. + */ +export function getAcceptLanguage(event: H3Event): string { + return getAcceptLanguages(event)[0] || '' +} + /** * get locale * diff --git a/src/node.test.ts b/src/node.test.ts index 3f89609..bfabe6a 100644 --- a/src/node.test.ts +++ b/src/node.test.ts @@ -1,6 +1,7 @@ import { describe, expect, test } from 'vitest' import supertest from 'supertest' import { + getAcceptLanguage, getAcceptLanguages, getCookieLocale, getLocale, @@ -36,6 +37,33 @@ describe('getAcceptLanguages', () => { }) }) +describe('getAcceptLanguage', () => { + test('basic', () => { + const mockRequest = { + headers: { + 'accept-language': 'en-US,en;q=0.9,ja;q=0.8', + }, + } as IncomingMessage + expect(getAcceptLanguage(mockRequest)).toBe('en-US') + }) + + test('any language', () => { + const mockRequest = { + headers: { + 'accept-language': '*', + }, + } as IncomingMessage + expect(getAcceptLanguage(mockRequest)).toBe('') + }) + + test('empty', () => { + const mockRequest = { + headers: {}, + } as IncomingMessage + expect(getAcceptLanguage(mockRequest)).toBe('') + }) +}) + describe('getLocale', () => { test('basic', () => { const mockRequest = { diff --git a/src/node.ts b/src/node.ts index af28ba3..e7fde86 100644 --- a/src/node.ts +++ b/src/node.ts @@ -32,11 +32,22 @@ import type { CookieOptions } from './http.ts' * * @returns {Array} The array of language tags, if `*` (any language) or empty string is detected, return an empty array. */ -export function getAcceptLanguages(req: IncomingMessage) { +export function getAcceptLanguages(req: IncomingMessage): string[] { const getter = () => req.headers['accept-language'] return getAcceptLanguagesWithGetter(getter) } +/** + * get accept language + * + * @param {IncomingMessage} request The {@link IncomingMessage | request} + * + * @returns {string} The **first language tag** of `accept-language` header, if `accept-language` header is not exists, or `*` (any language), return empty string. + */ +export function getAcceptLanguage(req: IncomingMessage): string { + return getAcceptLanguages(req)[0] || '' +} + /** * get locale * diff --git a/src/web.test.ts b/src/web.test.ts index d5ddb1c..077d860 100644 --- a/src/web.test.ts +++ b/src/web.test.ts @@ -1,5 +1,6 @@ import { describe, expect, test } from 'vitest' import { + getAcceptLanguage, getAcceptLanguages, getCookieLocale, getLocale, @@ -26,6 +27,25 @@ describe('getAcceptLanguages', () => { }) }) +describe('getAcceptLanguage', () => { + test('basic', () => { + const mockRequest = new Request('https://example.com') + mockRequest.headers.set('accept-language', 'en-US,en;q=0.9,ja;q=0.8') + expect(getAcceptLanguage(mockRequest)).toBe('en-US') + }) + + test('any language', () => { + const mockRequest = new Request('https://example.com') + mockRequest.headers.set('accept-language', '*') + expect(getAcceptLanguage(mockRequest)).toBe('') + }) + + test('empty', () => { + const mockRequest = new Request('https://example.com') + expect(getAcceptLanguage(mockRequest)).toBe('') + }) +}) + describe('getLocale', () => { test('basic', () => { const mockRequest = new Request('https://example.com') diff --git a/src/web.ts b/src/web.ts index 382cb45..9bacf01 100644 --- a/src/web.ts +++ b/src/web.ts @@ -33,11 +33,22 @@ import type { CookieOptions } from './http.ts' * * @returns {Array} The array of language tags, if `*` (any language) or empty string is detected, return an empty array. */ -export function getAcceptLanguages(req: Request) { +export function getAcceptLanguages(req: Request): string[] { const getter = () => req.headers.get('accept-language') return getAcceptLanguagesWithGetter(getter) } +/** + * get accept language + * + * @param {Request} request The {@link Request | request} + * + * @returns {string} The **first language tag** of `accept-language` header, if `accept-language` header is not exists, or `*` (any language), return empty string. + */ +export function getAcceptLanguage(req: Request): string { + return getAcceptLanguages(req)[0] || '' +} + /** * get locale *