Skip to content

Commit 16d18f8

Browse files
authored
fix(locale): complete locale id parser (#85)
1 parent 5ea00ef commit 16d18f8

2 files changed

Lines changed: 392 additions & 155 deletions

File tree

src/locale.test-d.ts

Lines changed: 122 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import type {
1818
ParseTransformedExtension,
1919
ParseUnicodeExtension,
2020
ParseUnicodeLanguageId,
21+
ParseUnicodeLocaleId,
2122
ParseVariantsSubtag
2223
} from './locale.ts'
2324

@@ -156,10 +157,10 @@ test('ParseVariantsSubtag', () => {
156157
* Success cases
157158
*/
158159

159-
// 3 chars, all digits
160-
expectTypeOf<ParseVariantsSubtag<['123']>>().toMatchTypeOf<[['123'], never, []]>()
161-
// 3 chars, first digit and alphabets
162-
expectTypeOf<ParseVariantsSubtag<['1ab']>>().toMatchTypeOf<[['1ab'], never, []]>()
160+
// 4 chars, digit + alphanum{3} (digit alphanum{3} per LDML spec)
161+
expectTypeOf<ParseVariantsSubtag<['1abc']>>().toMatchTypeOf<[['1abc'], never, []]>()
162+
// 4 chars, all digits (digit + digits{3})
163+
expectTypeOf<ParseVariantsSubtag<['1234']>>().toMatchTypeOf<[['1234'], never, []]>()
163164
// 5 chars, all alphabets
164165
expectTypeOf<ParseVariantsSubtag<['abcde']>>().toMatchTypeOf<[['abcde'], never, []]>()
165166
// 7 chars, alphabets and digits
@@ -175,15 +176,19 @@ test('ParseVariantsSubtag', () => {
175176
expectTypeOf<ParseVariantsSubtag<['1']>>().toMatchTypeOf<[[], never, ['1']]>()
176177
// range 2
177178
expectTypeOf<ParseVariantsSubtag<['12']>>().toMatchTypeOf<[[], never, ['12']]>()
178-
// range 4
179-
expectTypeOf<ParseVariantsSubtag<['1234']>>().toMatchTypeOf<[[], never, ['1234']]>()
179+
// range 3
180+
expectTypeOf<ParseVariantsSubtag<['123']>>().toMatchTypeOf<[[], never, ['123']]>()
180181
// range 9
181182
expectTypeOf<ParseVariantsSubtag<['123456789']>>().toMatchTypeOf<[[], never, ['123456789']]>()
182183

184+
// 3 chars, first digit and alphabets (too short for digit alphanum{3})
185+
expectTypeOf<ParseVariantsSubtag<['1ab']>>().toMatchTypeOf<[[], never, ['1ab']]>()
183186
// 3 chars, first alphabet and digits
184187
expectTypeOf<ParseVariantsSubtag<['a12']>>().toMatchTypeOf<[[], never, ['a12']]>()
185188
// 3 chars, all alphabets
186189
expectTypeOf<ParseVariantsSubtag<['abc']>>().toMatchTypeOf<[[], never, ['abc']]>()
190+
// 4 chars, first alphabet (not digit alphanum{3})
191+
expectTypeOf<ParseVariantsSubtag<['abcd']>>().toMatchTypeOf<[[], never, ['abcd']]>()
187192

188193
// not string
189194
expectTypeOf<ParseVariantsSubtag<[1]>>().toMatchTypeOf<[[], never, [1]]>()
@@ -203,6 +208,15 @@ test('ParseUnicodeLanguageId', () => {
203208
[{ lang: 'ja'; script: 'Kana'; region: 'jp'; variants: ['jauer'] }, never, []]
204209
>()
205210

211+
// script-only (unicode_language_id allows script subtag without language subtag)
212+
expectTypeOf<ParseUnicodeLanguageId<['Kana', 'JP']>>().toMatchTypeOf<
213+
[{ lang: never; script: 'Kana'; region: 'JP'; variants: [] }, never, []]
214+
>()
215+
// script-only without region
216+
expectTypeOf<ParseUnicodeLanguageId<['Latn']>>().toMatchTypeOf<
217+
[{ lang: never; script: 'Latn'; region: never; variants: [] }, never, []]
218+
>()
219+
206220
/** Errors */
207221
expectTypeOf<ParseUnicodeLanguageId<'a-ana-p-jauer-jauer'>>().toMatchTypeOf<
208222
[
@@ -327,6 +341,108 @@ test('ParsePuExtension', () => {
327341
expectTypeOf<ParsePuExtension<['1あ']>>().toMatchTypeOf<[never, 12, ['1あ']]>()
328342
})
329343

344+
test('ParseUnicodeLocaleId', () => {
345+
/**
346+
* Success cases
347+
*/
348+
349+
// simple language
350+
expectTypeOf<ParseUnicodeLocaleId<'ja'>>().toMatchTypeOf<
351+
[{ lang: { lang: 'ja'; variants: [] }; extensions: [] }, never, []]
352+
>()
353+
354+
// language with region
355+
expectTypeOf<ParseUnicodeLocaleId<'en-US'>>().toMatchTypeOf<
356+
[{ lang: { lang: 'en'; region: 'US'; variants: [] }; extensions: [] }, never, []]
357+
>()
358+
359+
// language with script and region
360+
expectTypeOf<ParseUnicodeLocaleId<'ja-Kana-JP'>>().toMatchTypeOf<
361+
[
362+
{
363+
lang: { lang: 'ja'; script: 'Kana'; region: 'JP'; variants: [] }
364+
extensions: []
365+
},
366+
never,
367+
[]
368+
]
369+
>()
370+
371+
// script-only
372+
expectTypeOf<ParseUnicodeLocaleId<'Kana-JP'>>().toMatchTypeOf<
373+
[
374+
{
375+
lang: { lang: never; script: 'Kana'; region: 'JP'; variants: [] }
376+
extensions: []
377+
},
378+
never,
379+
[]
380+
]
381+
>()
382+
383+
// language with unicode extension
384+
expectTypeOf<ParseUnicodeLocaleId<'en-US-u-co-standard'>>().toMatchTypeOf<
385+
[
386+
{
387+
lang: { lang: 'en'; region: 'US'; variants: [] }
388+
extensions: [{ type: 'u'; keywords: ['co', 'standard']; attributes: [] }]
389+
},
390+
never,
391+
[]
392+
]
393+
>()
394+
395+
// language with PU extension
396+
expectTypeOf<ParseUnicodeLocaleId<'en-US-x-private'>>().toMatchTypeOf<
397+
[
398+
{
399+
lang: { lang: 'en'; region: 'US'; variants: [] }
400+
extensions: [{ type: 'x'; value: 'private' }]
401+
},
402+
never,
403+
[]
404+
]
405+
>()
406+
407+
// language with transformed extension
408+
expectTypeOf<ParseUnicodeLocaleId<'en-US-t-ja-h0-hybrid'>>().toMatchTypeOf<
409+
[
410+
{
411+
lang: { lang: 'en'; region: 'US'; variants: [] }
412+
extensions: [
413+
{
414+
type: 't'
415+
lang: { lang: 'ja'; variants: [] }
416+
fields: [['h0', 'hybrid']]
417+
}
418+
]
419+
},
420+
never,
421+
[]
422+
]
423+
>()
424+
425+
// full locale with script, region, variant, and multiple extensions
426+
expectTypeOf<ParseUnicodeLocaleId<'ja-Kana-JP-jauer-u-co-standard-x-private'>>().toMatchTypeOf<
427+
[
428+
{
429+
lang: {
430+
lang: 'ja'
431+
script: 'Kana'
432+
region: 'JP'
433+
variants: ['jauer']
434+
}
435+
extensions: [
436+
{ type: 'u'; keywords: ['co', 'standard']; attributes: [] },
437+
{ type: 'x'; value: 'private' }
438+
]
439+
},
440+
never,
441+
[]
442+
]
443+
>()
444+
})
445+
330446
test('ParseOtherExtension', () => {
331447
/**
332448
* Success cases

0 commit comments

Comments
 (0)