From 5bb5f9671f6b0c9a4246968d54016828f5c2ec77 Mon Sep 17 00:00:00 2001 From: kazuya kawaguchi Date: Sun, 8 Oct 2023 02:36:37 +0900 Subject: [PATCH] feat: type utilites for unicode language id (#20) * feat: type utilites for unicode language id * updates * fix: unicode language id implementation * fix * fix --- bun.lockb | Bin 141998 -> 158753 bytes mod.ts | 4 - package.json | 9 +- playground/bun/index.ts | 5 +- playground/bun/package.json | 2 +- playground/deno/main.ts | 2 + playground/node/package.json | 2 +- src/locale.test-d.ts | 280 +++++++++++++++++++++++++++++ src/locale.ts | 337 +++++++++++++++++++++++++++++++++++ src/types.ts | 50 ++++++ tsconfig.json | 10 +- vitest.config.ts | 3 + 12 files changed, 690 insertions(+), 14 deletions(-) delete mode 100644 mod.ts create mode 100644 src/locale.test-d.ts create mode 100644 src/locale.ts create mode 100644 src/types.ts diff --git a/bun.lockb b/bun.lockb index 6536f8915f71b7f87bdbdaa053c2e7c88e52c82c..39df61b1560d3491f5668fd3958917a3687fa25e 100755 GIT binary patch delta 38050 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 x)W;-5>BEz1i! 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 { diff --git a/playground/node/package.json b/playground/node/package.json index 517efaa..07be27c 100644 --- a/playground/node/package.json +++ b/playground/node/package.json @@ -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", diff --git a/src/locale.test-d.ts b/src/locale.test-d.ts new file mode 100644 index 0000000..caeaaf1 --- /dev/null +++ b/src/locale.test-d.ts @@ -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>().toMatchTypeOf() + // 2 + expectTypeOf>().toMatchTypeOf() + // 3 + expectTypeOf>().toMatchTypeOf() + // 4 + expectTypeOf>().toMatchTypeOf< + false + >() + // 5 + expectTypeOf>().toMatchTypeOf< + true + >() + // 8 + expectTypeOf>() + .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>().toMatchTypeOf< + ['ja', never] + >() + // 3 chars + expectTypeOf>().toMatchTypeOf< + ['jpn', never] + >() + // 7 chars + expectTypeOf>().toMatchTypeOf<['english', never]>() + // 'root' is special (4 chars) + expectTypeOf>().toMatchTypeOf<['root', never]>() + // upper case + expectTypeOf>().toMatchTypeOf< + ['JA', never] + >() + // mixied case + expectTypeOf>().toMatchTypeOf< + ['Ja', never] + >() + + /** + * Failed cases + */ + + // empty + expectTypeOf>().toMatchTypeOf< + [never, 1] + >() + // no-alphabet + expectTypeOf>().toMatchTypeOf< + [never, 2] + >() + // never + expectTypeOf>().toMatchTypeOf< + [never, 1] + >() + // range + expectTypeOf>().toMatchTypeOf< + [never, 3] + >() + expectTypeOf>().toMatchTypeOf< + [never, 3] + >() + // not string + expectTypeOf>().toMatchTypeOf() +}) + +test('ParseScriptSubtag', () => { + /** + * Success cases + */ + + // 4 chars + expectTypeOf>().toMatchTypeOf< + ['kana', never] + >() + + // empty + expectTypeOf>().toMatchTypeOf< + [never, never] + >() + + // upper case + expectTypeOf>().toMatchTypeOf< + ['Kana', never] + >() + + /** + * Failed cases + */ + + // no-alphabet + expectTypeOf>().toMatchTypeOf< + [never, 4] + >() + // range + expectTypeOf>().toMatchTypeOf< + [never, 5] + >() + expectTypeOf>().toMatchTypeOf< + [never, 5] + >() + // not string + expectTypeOf>().toMatchTypeOf< + never + >() +}) + +test('ParseRegionSubtag', () => { + /** + * Success cases + */ + + // 2 chars (alpha) + expectTypeOf>().toMatchTypeOf< + ['jp', never] + >() + // 3 chars (digit) + expectTypeOf>().toMatchTypeOf< + ['012', never] + >() + // empty + expectTypeOf>().toMatchTypeOf< + [never, never] + >() + // upper case + expectTypeOf>().toMatchTypeOf< + ['JP', never] + >() + + /** + * Failed cases + */ + + // no all-alphabet + expectTypeOf>().toMatchTypeOf< + [never, 6] + >() + // no all-digits + expectTypeOf>().toMatchTypeOf< + [never, 6] + >() + // range + expectTypeOf>().toMatchTypeOf< + [never, 7] + >() + expectTypeOf>().toMatchTypeOf< + [never, 7] + >() + expectTypeOf>().toMatchTypeOf< + [never, 7] + >() + expectTypeOf>().toMatchTypeOf< + [never, 7] + >() + // not string + expectTypeOf>().toMatchTypeOf< + never + >() +}) + +test('ParseVariantsSubtag', () => { + /** + * Success cases + */ + + // 3 chars, all digits + expectTypeOf>().toMatchTypeOf< + [['123'], never] + >() + // 3 chars, first digit and alphabets + expectTypeOf>().toMatchTypeOf< + [['1ab'], never] + >() + // 5 chars, all alphabets + expectTypeOf>().toMatchTypeOf< + [['abcde'], never] + >() + // 7 chars, alphabets and digits + expectTypeOf>().toMatchTypeOf< + [['ab12cde', 'abcde123'], never] + >() + + /** + * Failed cases + */ + + // range 1 + expectTypeOf>().toMatchTypeOf< + [[], never] + >() + // range 2 + expectTypeOf>().toMatchTypeOf< + [[], never] + >() + // range 4 + expectTypeOf>().toMatchTypeOf< + [[], never] + >() + // range 9 + expectTypeOf>().toMatchTypeOf< + [[], never] + >() + + // 3 chars, first alphabet and digits + expectTypeOf>().toMatchTypeOf< + [[], never] + >() + // 3 chars, all alphabets + expectTypeOf>().toMatchTypeOf< + [[], never] + >() + + // not string + expectTypeOf>().toMatchTypeOf< + [[], never] + >() +}) + +test('ParseUnicodeLangugageId', () => { + /** + * Success cases + */ + expectTypeOf>().toMatchTypeOf< + [{ lang: 'ja'; script: 'Kana'; region: 'jp'; variants: ['jauer'] }, never] + >() + + /** Erros */ + expectTypeOf>().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', + ], + ] + >() +}) diff --git a/src/locale.ts b/src/locale.ts new file mode 100644 index 0000000..cd73685 --- /dev/null +++ b/src/locale.ts @@ -0,0 +1,337 @@ +import type { + All, + Concat, + Filter, + First, + Includes, + IsNever, + Length, + Push, + Shift, + Split, + StringToArray, + TupleToUnion, + UnionToTuple, +} from './types.ts' + +export interface UnicodeLocaleId { + lang: UnicodeLanguageId + extensions: Array< + UnicodeExtension | TransformedExtension | PuExtension | OtherExtension + > +} + +export interface UnicodeLanguageId { + lang: string + script?: string + region?: string + variants: string[] +} + +type Alphabets = + | 'a' + | 'b' + | 'c' + | 'd' + | 'e' + | 'f' + | 'g' + | 'h' + | 'i' + | 'j' + | 'k' + | 'l' + | 'm' + | 'n' + | 'o' + | 'p' + | 'q' + | 'r' + | 's' + | 't' + | 'u' + | 'v' + | 'w' + | 'x' + | 'y' + | 'z' +type Digits = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' +type Alpha = TupleToUnion< + Concat, UnionToTuple>> +> +type AlphaNumber = TupleToUnion< + Concat, UnionToTuple> +> + +export type CheckRange< + T extends unknown[], + Indexes extends number[], +> = Includes> extends true ? true : false + +// deno-fmt-ignore +export type ValidCharacters< + T extends unknown[], + UnionChars = Alphabets, // default alphabets + Target = First, + Rest extends unknown[] = Shift, +> = IsNever extends false + ? [Includes, Target>, ...ValidCharacters] + : [] + +export const localeErrors = /* @__PURE__ */ { + 1: 'missing unicode language subtag', + 2: 'malformed unicode language subtag', + 3: 'requires 2-3 or 5-8 alphabet lower characters', + 4: 'malformed unicode script subtag', + 5: 'unicode script subtag requires 4 alphabet lower characters', + 6: 'malformed unicode region subtag', + 7: 'unicode region subtag requires 2 alphabet lower characters or 3 digits', + 8: 'duplicate unicode variant subtag', + 1024: 'Unexpected error', +} as const + +/** + * parse unicode language id + * https://unicode.org/reports/tr35/#unicode_language_id + */ +export type ParseUnicodeLanguageId< + T extends string, + ErrorMsg extends Record = typeof localeErrors, + S extends unknown[] = Split, + Lang extends [string, number] = ParseLangSubtag>, + Rest1 extends unknown[] = Shift, + Script extends [string, number] = ParseScriptSubtag>, + Rest2 extends unknown[] = Shift, + Region extends [string, number] = ParseRegionSubtag>, + Rest3 extends unknown[] = Shift, + Variants extends [string[], number | never] = ParseVariantsSubtag< + Rest3 + >, + Errors extends unknown[] = Filter<[ + ErrorMsg[Lang[1]], + ErrorMsg[Script[1]], + ErrorMsg[Region[1]], + ErrorMsg[Variants[1]], + ], never>, +> = [ + { + lang: Lang[0] + script: Script[0] + region: Region[0] + variants: Variants[0] + }, + Length extends 0 ? never : Errors, +] + +/** + * parse unicode language subtag + * https://unicode.org/reports/tr35/#unicode_language_subtag + */ +// deno-fmt-ignore +export type ParseLangSubtag< + T, + R extends [string, number] = IsNever extends true + ? [never, 1] // missing + : T extends '' + ? [never, 1] // missing + : T extends 'root' + ? ['root', never] // 'root' is special case + : T extends string + ? ParseUnicodeLanguageSubtag + : never // unexpected +> = R + +/** + * parse unicode language subtag (EBNF: = alpha{2,3} | alpha{5,8};) + * https://unicode.org/reports/tr35/#unicode_language_subtag + */ +// TODO: Check if the language subtag is in CLDR +// deno-fmt-ignore +export type ParseUnicodeLanguageSubtag< + T extends string, + Chars extends unknown[] = StringToArray, +> = CheckRange extends true + ? Includes, false> extends true // check if all chars are alphabets + ? [never, 2] // malformed + : [T, never] + : [never, 3] // require characters length + +/** + * parse unicode script subtag + * https://unicode.org/reports/tr35/#unicode_script_subtag + */ +// deno-fmt-ignore +export type ParseScriptSubtag< + T, + R extends [string, number] = IsNever extends true + ? [never, never] // missing + : T extends '' + ? [never, never] // missing + : T extends string + ? ParseUnicodeScriptSubtag + : never // unexpected +> = R + +/** + * paser unicode script subtag (EBNF: = alpha{4};) + * https://unicode.org/reports/tr35/#unicode_script_subtag + */ +// TODO: Check if the script subtag is in CLDR +// deno-fmt-ignore +export type ParseUnicodeScriptSubtag< +T extends string, +Chars extends unknown[] = StringToArray, +> = CheckRange extends true + ? Includes, false> extends true // check if all chars are alphabets + ? [never, 4] // malformed + : [T, never] + : [never, 5] // require characters length + +/** + * parse unicode region subtag + * https://unicode.org/reports/tr35/#unicode_region_subtag + */ +// deno-fmt-ignore +export type ParseRegionSubtag< + T, + R extends [string, number] = IsNever extends true + ? [never, never] // missing + : T extends '' + ? [never, never] // missing + : T extends string + ? ParseUnicodeRegionSubtag + : never, // unexpected +> = R + +/** + * parse unicode region subtag (= (alpha{2} | digit{3}) ;) + * https://unicode.org/reports/tr35/#unicode_region_subtag + */ +// TODO: Check if the region subtag is in CLDR +// deno-fmt-ignore +export type ParseUnicodeRegionSubtag< + T extends string, + Chars extends unknown[] = StringToArray, + HasAlphabetsOnly = All, true>, + HasDigitsOnly = All, true>, +> = CheckRange extends true + ? Length extends 2 + ? HasAlphabetsOnly extends true + ? [T, never] + : HasDigitsOnly extends true + ? [never, 7] // require characters length + : [never, 6] // malformed + : Length extends 3 + ? HasDigitsOnly extends true + ? [T, never] + : HasAlphabetsOnly extends true + ? [never, 7] // require characters length + : [never, 6] // malformed + : [never, 7] // require characters length + : [never, 7] // require characters length + +/** + * parse unicode variant subtag + * https://unicode.org/reports/tr35/#unicode_variant_subtag + */ +export type ParseVariantsSubtag< + T extends unknown[], + R extends [string[], number | never] = _ParseVariantsSubtag, +> = R + +// deno-fmt-ignore +type _ParseVariantsSubtag< + T extends unknown[] = [], + Accumrator extends [string[], number | never] = [[], never], + HasVariants = Length extends 0 ? false : true, + Target = First, + Variant extends string = HasVariants extends true + ? Target extends string ? Target : never + : never, + VariantSubTag = ParseUnicodeVariantsSubtag extends [infer Tag, never] ? Tag : never, + Rest extends unknown[] = Shift, + Duplicate = IsNever extends false + ? Includes extends true ? true : false + : false, + VariantStr extends string = VariantSubTag extends string ? VariantSubTag : never, + > = IsNever extends false + ? [[...Accumrator[0]], Accumrator[1]] + : Duplicate extends true + ? [[...Accumrator[0]], 8] + : IsNever extends true + ? [[...Accumrator[0]], never] + : _ParseVariantsSubtag], Accumrator[1]]> + +/** + * parse unicode variant subtag (= (alphanum{5,8} | digit alphanum{3}) ;) + * https://unicode.org/reports/tr35/#unicode_variant_subtag + */ +// deno-fmt-ignore +type ParseUnicodeVariantsSubtag< + T extends string, + Chars extends unknown[] = StringToArray, + FirstChar = First, + RemainChars extends unknown[]= Shift, +> = Length extends 3 + ? All, true> extends true // check digit at first char + ? All, true> extends true// check alphanum at remain chars + ? [T, never] + : [never, never] // ignore + : [never, never] // ignore + : Length extends 4 + ? [never, never] // ignore + : CheckRange extends true + ? All, true> extends true// capture alphanum + ? [T, never] + : [never, never] // ignore + : [never, never] // ignore + +export type KV = [string, string] | [string] + +export interface Extension { + type: string +} + +export interface UnicodeExtension extends Extension { + type: 'u' + keywords: KV[] + attributes: string[] +} + +export interface TransformedExtension extends Extension { + type: 't' + fields: KV[] + lang?: UnicodeLanguageId +} +export interface PuExtension extends Extension { + type: 'x' + value: string +} + +export interface OtherExtension extends Extension { + type: + | 'a' + | 'b' + | 'c' + | 'd' + | 'e' + | 'f' + | 'g' + | 'h' + | 'i' + | 'j' + | 'k' + | 'l' + | 'm' + | 'n' + | 'o' + | 'p' + | 'q' + | 'r' + | 's' + | 'v' + | 'w' + | 'y' + | 'z' + value: string +} diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..3d533f7 --- /dev/null +++ b/src/types.ts @@ -0,0 +1,50 @@ +export type IsNever = [T] extends [never] ? true : false +export type Split = string extends S + ? string[] + : S extends `${infer A}${SEP}${infer B}` + ? [A, ...(B extends '' ? [] : Split)] + : SEP extends '' ? [] + : [S] +export type Shift = T extends [unknown, ...infer U] ? U + : never +export type First = T extends [infer A, ...infer rest] ? A + : never +export type Last = [unknown, ...T][T['length']] +export type Length = T['length'] +export type IsEqual = (() => T extends X ? 1 : 2) extends + (() => T extends Y ? 1 : 2) ? true : false +export type All = T extends [infer L, ...infer R] + ? IsEqual extends true ? All + : false + : true +export type Push = [...T, U] +export type Includes = T extends [infer A, ...infer B] + ? IsEqual extends true ? true : Includes + : false +export type Tuple = readonly unknown[] +export type Concat = [...T, ...U] +export type Filter = T extends [infer R, ...infer Rest] + ? [R] extends [F] ? Filter + : [R, ...Filter] + : [] + +export type UnionToIntersection = ( + U extends unknown ? (arg: U) => 0 : never +) extends (arg: infer I) => 0 ? I + : never +export type LastInUnion = UnionToIntersection< + U extends unknown ? (x: U) => 0 : never +> extends (x: infer L) => 0 ? L + : never +export type UnionToTuple> = [U] extends [never] ? [] + : [...UnionToTuple>, Last] +export type TupleToUnion = T extends + Array ? R + : never + +export type StringToUnion = T extends + `${infer Letter}${infer Rest}` ? Letter | StringToUnion + : never + +export type StringToArray = T extends + `${infer Letter}${infer Rest}` ? [Letter, ...StringToArray] : [] diff --git a/tsconfig.json b/tsconfig.json index 9da47a2..6d8ee1a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,7 +4,7 @@ /* Projects */ // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ - // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ @@ -12,7 +12,11 @@ /* Language and Environment */ "target": "ESNext", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ - // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "lib": [ + // "ESNext", + // "DOM", + // "DOM.Iterable" + // ], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ // "jsx": "preserve", /* Specify what JSX code is generated. */ // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ @@ -59,7 +63,7 @@ // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ // "outDir": "./", /* Specify an output folder for all emitted files. */ // "removeComments": true, /* Disable emitting comments. */ - // "noEmit": true, /* Disable emitting files from a compilation. */ + // "noEmit": true, /* Disable emitting files from a compilation. */ // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ diff --git a/vitest.config.ts b/vitest.config.ts index a93210a..c7d146f 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -3,5 +3,8 @@ import { defineConfig } from 'vitest/config' export default defineConfig({ test: { includeSource: ['src/**/*.{js,ts}'], + // typecheck: { + // ignoreSourceErrors: true, + // }, }, })