Skip to content

Commit

Permalink
feat: type utilites for unicode language id (#20)
Browse files Browse the repository at this point in the history
* feat: type utilites for unicode language id

* updates

* fix: unicode language id implementation

* fix

* fix
  • Loading branch information
kazupon committed Oct 7, 2023
1 parent 3549031 commit 5bb5f96
Show file tree
Hide file tree
Showing 12 changed files with 690 additions and 14 deletions.
Binary file modified bun.lockb
Binary file not shown.
4 changes: 0 additions & 4 deletions mod.ts

This file was deleted.

9 changes: 6 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,9 @@
"lint": "deno lint",
"format": "deno fmt",
"build": "unbuild",
"test": "vitest run",
"test": "npm run test:typecheck && npm run test:unit",
"test:unit": "vitest run",
"test:typecheck": "vitest typecheck --run",
"test:coverage": "npm test -- --reporter verbose --coverage",
"play:browser": "npm run -w example-browser dev",
"play:node": "npm run -w example-node dev",
Expand All @@ -84,7 +86,8 @@
"devDependencies": {
"@types/node": "^20.6.0",
"@types/supertest": "^2.0.12",
"@vitest/coverage-v8": "^0.34.4",
"@vitest/coverage-v8": "^1.0.0-beta.1",
"bun-types": "latest",
"bumpp": "^9.2.0",
"cookie-es": "^1.0.0",
"gh-changelogen": "^0.2.8",
Expand All @@ -93,7 +96,7 @@
"supertest": "^6.3.3",
"typescript": "^5.2.2",
"unbuild": "^2.0.0",
"vitest": "^0.34.4"
"vitest": "^1.0.0-beta.1"
},
"workspaces": [
"playground/*"
Expand Down
5 changes: 3 additions & 2 deletions playground/bun/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { getLocale } from '@intlify/utils/web'
import { getHeaderLocale } from '@intlify/utils'

const port = 8124
// @ts-ignore: this is example
Bun.serve({
port,
fetch(req: Request) {
const locale = getLocale(req)
const locale = getHeaderLocale(req)
return new Response(`detect locale: ${locale.toString()}`)
},
})
Expand Down
2 changes: 1 addition & 1 deletion playground/bun/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@
"typescript": "^5.0.0"
},
"dependencies": {
"@intlify/utils": "npm:@intlify/utils-edge@0.2.0-28254719.6209414"
"@intlify/utils": "npm:@intlify/utils-edge@0.5.0-28266797.0fa17f3"
}
}
2 changes: 2 additions & 0 deletions playground/deno/main.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// @ts-ignore: this is example
import { getHeaderLanguages } from 'https://esm.sh/@intlify/utils/web'

const port = 8125
// @ts-ignore: this is example
Deno.serve({
port,
}, (req: Request) => {
Expand Down
2 changes: 1 addition & 1 deletion playground/node/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"dev": "npx tsx index.ts"
},
"dependencies": {
"@intlify/utils": "npm:@intlify/utils-edge@0.2.0-28254719.6209414"
"@intlify/utils": "npm:@intlify/utils-edge@0.5.0-28266797.0fa17f3"
},
"devDependencies": {
"@types/node": "^20.6.0",
Expand Down
280 changes: 280 additions & 0 deletions src/locale.test-d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,280 @@
import { expectTypeOf, test } from 'vitest'

import type {
CheckRange,
ParseLangSubtag,
ParseRegionSubtag,
ParseScriptSubtag,
ParseUnicodeLanguageId,
ParseVariantsSubtag,
} from './locale.ts'

test('CheckRange', () => {
type Indexes = [2, 3, 5, 6, 7, 8]
// 0
expectTypeOf<CheckRange<[], Indexes>>().toMatchTypeOf<false>()
// 2
expectTypeOf<CheckRange<['e', 'n'], Indexes>>().toMatchTypeOf<true>()
// 3
expectTypeOf<CheckRange<['e', 'n', 'g'], Indexes>>().toMatchTypeOf<true>()
// 4
expectTypeOf<CheckRange<['e', 'n', 'g', 'l'], Indexes>>().toMatchTypeOf<
false
>()
// 5
expectTypeOf<CheckRange<['j', 'a', 'p', 'a', 'n'], Indexes>>().toMatchTypeOf<
true
>()
// 8
expectTypeOf<CheckRange<['c', 'h', 'i', 'l', 'a', 'n', 'd', '1'], Indexes>>()
.toMatchTypeOf<
true
>()
// 9
expectTypeOf<
CheckRange<['c', 'h', 'i', 'l', 'a', 'n', 'd', 'a', 'b'], Indexes>
>()
.toMatchTypeOf<
false
>()

expectTypeOf<
CheckRange<['1', '2', '3', '4'], [4]>
>()
.toMatchTypeOf<
true
>()
expectTypeOf<
CheckRange<['1', '2', '3'], [4]>
>()
.toMatchTypeOf<
false
>()
})

test('ParseLangSubtag', () => {
/**
* Success cases
*/

// 2 chars
expectTypeOf<ParseLangSubtag<'ja'>>().toMatchTypeOf<
['ja', never]
>()
// 3 chars
expectTypeOf<ParseLangSubtag<'jpn'>>().toMatchTypeOf<
['jpn', never]
>()
// 7 chars
expectTypeOf<ParseLangSubtag<'english'>>().toMatchTypeOf<['english', never]>()
// 'root' is special (4 chars)
expectTypeOf<ParseLangSubtag<'root'>>().toMatchTypeOf<['root', never]>()
// upper case
expectTypeOf<ParseLangSubtag<'JA'>>().toMatchTypeOf<
['JA', never]
>()
// mixied case
expectTypeOf<ParseLangSubtag<'Ja'>>().toMatchTypeOf<
['Ja', never]
>()

/**
* Failed cases
*/

// empty
expectTypeOf<ParseLangSubtag<''>>().toMatchTypeOf<
[never, 1]
>()
// no-alphabet
expectTypeOf<ParseLangSubtag<'11'>>().toMatchTypeOf<
[never, 2]
>()
// never
expectTypeOf<ParseLangSubtag<never>>().toMatchTypeOf<
[never, 1]
>()
// range
expectTypeOf<ParseLangSubtag<'abcd'>>().toMatchTypeOf<
[never, 3]
>()
expectTypeOf<ParseLangSubtag<'abcdefghj'>>().toMatchTypeOf<
[never, 3]
>()
// not string
expectTypeOf<ParseLangSubtag<1>>().toMatchTypeOf<never>()
})

test('ParseScriptSubtag', () => {
/**
* Success cases
*/

// 4 chars
expectTypeOf<ParseScriptSubtag<'kana'>>().toMatchTypeOf<
['kana', never]
>()

// empty
expectTypeOf<ParseScriptSubtag<''>>().toMatchTypeOf<
[never, never]
>()

// upper case
expectTypeOf<ParseScriptSubtag<'Kana'>>().toMatchTypeOf<
['Kana', never]
>()

/**
* Failed cases
*/

// no-alphabet
expectTypeOf<ParseScriptSubtag<'1111'>>().toMatchTypeOf<
[never, 4]
>()
// range
expectTypeOf<ParseScriptSubtag<'lat'>>().toMatchTypeOf<
[never, 5]
>()
expectTypeOf<ParseScriptSubtag<'arabi'>>().toMatchTypeOf<
[never, 5]
>()
// not string
expectTypeOf<ParseScriptSubtag<1>>().toMatchTypeOf<
never
>()
})

test('ParseRegionSubtag', () => {
/**
* Success cases
*/

// 2 chars (alpha)
expectTypeOf<ParseRegionSubtag<'jp'>>().toMatchTypeOf<
['jp', never]
>()
// 3 chars (digit)
expectTypeOf<ParseRegionSubtag<'012'>>().toMatchTypeOf<
['012', never]
>()
// empty
expectTypeOf<ParseRegionSubtag<''>>().toMatchTypeOf<
[never, never]
>()
// upper case
expectTypeOf<ParseLangSubtag<'JP'>>().toMatchTypeOf<
['JP', never]
>()

/**
* Failed cases
*/

// no all-alphabet
expectTypeOf<ParseRegionSubtag<'j1'>>().toMatchTypeOf<
[never, 6]
>()
// no all-digits
expectTypeOf<ParseRegionSubtag<'12j'>>().toMatchTypeOf<
[never, 6]
>()
// range
expectTypeOf<ParseRegionSubtag<'j'>>().toMatchTypeOf<
[never, 7]
>()
expectTypeOf<ParseRegionSubtag<'12'>>().toMatchTypeOf<
[never, 7]
>()
expectTypeOf<ParseRegionSubtag<'jpn'>>().toMatchTypeOf<
[never, 7]
>()
expectTypeOf<ParseRegionSubtag<'9a23'>>().toMatchTypeOf<
[never, 7]
>()
// not string
expectTypeOf<ParseRegionSubtag<1>>().toMatchTypeOf<
never
>()
})

test('ParseVariantsSubtag', () => {
/**
* Success cases
*/

// 3 chars, all digits
expectTypeOf<ParseVariantsSubtag<['123']>>().toMatchTypeOf<
[['123'], never]
>()
// 3 chars, first digit and alphabets
expectTypeOf<ParseVariantsSubtag<['1ab']>>().toMatchTypeOf<
[['1ab'], never]
>()
// 5 chars, all alphabets
expectTypeOf<ParseVariantsSubtag<['abcde']>>().toMatchTypeOf<
[['abcde'], never]
>()
// 7 chars, alphabets and digits
expectTypeOf<ParseVariantsSubtag<['ab12cde', 'abcde123']>>().toMatchTypeOf<
[['ab12cde', 'abcde123'], never]
>()

/**
* Failed cases
*/

// range 1
expectTypeOf<ParseVariantsSubtag<['1']>>().toMatchTypeOf<
[[], never]
>()
// range 2
expectTypeOf<ParseVariantsSubtag<['12']>>().toMatchTypeOf<
[[], never]
>()
// range 4
expectTypeOf<ParseVariantsSubtag<['1234']>>().toMatchTypeOf<
[[], never]
>()
// range 9
expectTypeOf<ParseVariantsSubtag<['123456789']>>().toMatchTypeOf<
[[], never]
>()

// 3 chars, first alphabet and digits
expectTypeOf<ParseVariantsSubtag<['a12']>>().toMatchTypeOf<
[[], never]
>()
// 3 chars, all alphabets
expectTypeOf<ParseVariantsSubtag<['abc']>>().toMatchTypeOf<
[[], never]
>()

// not string
expectTypeOf<ParseVariantsSubtag<[1]>>().toMatchTypeOf<
[[], never]
>()
})

test('ParseUnicodeLangugageId', () => {
/**
* Success cases
*/
expectTypeOf<ParseUnicodeLanguageId<'ja-Kana-jp-jauer'>>().toMatchTypeOf<
[{ lang: 'ja'; script: 'Kana'; region: 'jp'; variants: ['jauer'] }, never]
>()

/** Erros */
expectTypeOf<ParseUnicodeLanguageId<'a-ana-p-jauer-jauer'>>().toMatchTypeOf<
[
{ lang: never; script: never; region: never; variants: ['jauer'] },
[
'requires 2-3 or 5-8 alphabet lower characters',
'unicode script subtag requires 4 alphabet lower characters',
'unicode region subtag requires 2 alphabet lower characters or 3 digits',
'duplicate unicode variant subtag',
],
]
>()
})
Loading

0 comments on commit 5bb5f96

Please sign in to comment.