Skip to content

Commit 0097be5

Browse files
committed
feat: enhance pseudo-locale handling
1 parent 0e7e35b commit 0097be5

File tree

5 files changed

+50
-43
lines changed

5 files changed

+50
-43
lines changed

src/index.ts

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,39 @@
11
import { MessageFormatElement, parse, ParserOptions } from '@formatjs/icu-messageformat-parser'
2-
import { LoaderDefinition } from 'webpack'
32
import { validate } from 'schema-utils'
3+
import { LoaderDefinition } from 'webpack'
44

55
import { prehandle } from './prehandler'
6-
import { getPseudoLocale } from './pseudo-locale'
6+
import { getPseudoLocale, Handler } from './pseudo-locale'
77
import { Options, ResourceQuery, schemaOptions, schemaResourceQuery } from './types'
88

99
const loader: LoaderDefinition<Options> = function (source) {
1010
this.cacheable()
1111
const options = this.getOptions(schemaOptions)
12-
const pseudoLocale = options.pseudoLocale ?? getResourceQuery(this).pseudoLocale
1312
const compiled = compile(prehandle(options.format ?? 'default', JSON.parse(source)), {
1413
ignoreTag: options.ignoreTag,
1514
requiresOtherClause: options.requiresOtherClause,
1615
shouldParseSkeletons: options.shouldParseSkeletons,
17-
posthandler: typeof pseudoLocale === 'string' ? getPseudoLocale(pseudoLocale) : undefined,
16+
handlers: [
17+
// 1. Pseudo-locales
18+
getPseudoLocale(options.pseudoLocale ?? getResourceQuery(this).pseudoLocale),
19+
],
1820
})
1921
return JSON.stringify(Object.fromEntries(compiled))
2022
}
2123

2224
export default loader
2325

2426
interface CompileOptions extends ParserOptions {
25-
posthandler?(elements: MessageFormatElement[]): MessageFormatElement[]
27+
readonly handlers: readonly (Handler | undefined)[]
2628
}
2729

28-
function compile(messages: [string, string][], options: CompileOptions) {
29-
type ParsedEntry = readonly [string, MessageFormatElement[]]
30-
return messages.map(([key, message]): ParsedEntry => {
31-
const parsed = parse(message, options)
32-
return [key, options.posthandler?.(parsed) ?? parsed]
30+
function compile(messages: [string, string][], { handlers, ...options }: CompileOptions) {
31+
return messages.map(([key, message]): [string, MessageFormatElement[]] => {
32+
const elements = handlers.reduceRight<Iterable<MessageFormatElement>>(
33+
(elements, handler) => handler?.(elements) ?? elements,
34+
parse(message, options)
35+
)
36+
return [key, Array.from(elements)]
3337
})
3438
}
3539

src/pseudo-locale/ast.ts

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { createLiteralElement, isLiteralElement, MessageFormatElement } from '@formatjs/icu-messageformat-parser'
2+
23
import { Transformer } from './Transformer'
4+
import { Handler } from './types'
35

46
export const ACCENTED_MAP = new Transformer(
57
'\u0226\u0181\u0187\u1e12\u1e16\u0191\u0193\u0126\u012a\u0134\u0136\u013f\u1e3e\u0220\u01fe\u01a4\u024a\u0158\u015e\u0166\u016c\u1e7c\u1e86\u1e8a\u1e8e\u1e90',
@@ -13,32 +15,32 @@ export const FLIPPED_MAP = new Transformer(
1315
false
1416
)
1517

16-
export function createEnglishTransformer(brackets: string, transformer: Transformer) {
18+
export function createEnglishTransformer(brackets: string, transformer: Transformer): Handler {
1719
const [leftBracket, rightBracket] = brackets
20+
const handler = modifyLiteralElement(transformer.stringify)
1821
return function* (elements: Iterable<MessageFormatElement>) {
1922
yield createLiteralElement(leftBracket)
20-
yield* modifyLiteralElement(elements, transformer.stringify)
23+
yield* handler(elements)
2124
yield createLiteralElement(rightBracket)
2225
}
2326
}
2427

25-
export function* modifyLiteralElement(
26-
elements: Iterable<MessageFormatElement>,
27-
modifier: (input: string) => string
28-
): Iterable<MessageFormatElement> {
29-
for (const element of elements) {
30-
if (isLiteralElement(element)) {
31-
yield { ...element, value: modifier(element.value) }
32-
} else if ('options' in element) {
33-
const entries = Object.entries(element.options).map(([key, option]) => [
34-
key,
35-
{ value: Array.from(modifyLiteralElement(option.value, modifier)) },
36-
])
37-
yield { ...element, options: Object.fromEntries(entries) }
38-
} else if ('children' in element) {
39-
yield { ...element, children: Array.from(modifyLiteralElement(element.children, modifier)) }
40-
} else {
41-
yield element
28+
export function modifyLiteralElement(modifier: (input: string) => string): Handler {
29+
return function* handler(elements): Iterable<MessageFormatElement> {
30+
for (const element of elements) {
31+
if (isLiteralElement(element)) {
32+
yield { ...element, value: modifier(element.value) }
33+
} else if ('options' in element) {
34+
const entries = Object.entries(element.options).map(([key, option]) => [
35+
key,
36+
{ value: Array.from(handler(option.value)) },
37+
])
38+
yield { ...element, options: Object.fromEntries(entries) }
39+
} else if ('children' in element) {
40+
yield { ...element, children: Array.from(handler(element.children)) }
41+
} else {
42+
yield element
43+
}
4244
}
4345
}
4446
}

src/pseudo-locale/index.ts

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,14 @@
1-
import type { MessageFormatElement } from '@formatjs/icu-messageformat-parser'
21
import { createLiteralElement } from '@formatjs/icu-messageformat-parser'
2+
33
import { ACCENTED_MAP, createEnglishTransformer, FLIPPED_MAP, modifyLiteralElement } from './ast'
4+
import { Handler } from './types'
5+
6+
export { Handler }
47

5-
const locales: Record<string, (elements: Iterable<MessageFormatElement>) => Iterable<MessageFormatElement>> = {
6-
*'xx-LS'(elements) {
7-
yield* elements
8-
yield createLiteralElement('S'.repeat(25))
9-
},
10-
'xx-AC': (elements) => modifyLiteralElement(elements, (value) => value.toUpperCase()),
11-
*'xx-HA'(elements) {
12-
yield createLiteralElement('[javascript]')
13-
yield* elements
14-
},
8+
const locales: Record<string, Handler> = {
9+
'xx-LS': (elements) => [...elements, createLiteralElement('S'.repeat(25))],
10+
'xx-AC': modifyLiteralElement((value) => value.toUpperCase()),
11+
'xx-HA': (elements) => [createLiteralElement('[javascript]'), ...elements],
1512
'en-XA': createEnglishTransformer('\u005b\u005d', ACCENTED_MAP),
1613
'en-XB': createEnglishTransformer('\u202e\u202c', FLIPPED_MAP),
1714
}
@@ -20,7 +17,7 @@ export function getPseudoLocaleNames(): string[] {
2017
return Object.keys(locales)
2118
}
2219

23-
export function getPseudoLocale(name: string): (elements: MessageFormatElement[]) => MessageFormatElement[] {
24-
const handle = locales[name]
25-
return (elements) => Array.from(handle(elements))
20+
export function getPseudoLocale(name: string | undefined): Handler | undefined {
21+
if (name === undefined) return undefined
22+
return locales[name]
2623
}

src/pseudo-locale/types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import type { MessageFormatElement } from '@formatjs/icu-messageformat-parser'
2+
3+
export type Handler = (elements: Iterable<MessageFormatElement>) => Iterable<MessageFormatElement>

src/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { Schema } from 'schema-utils'
2+
23
import { getFormats } from './prehandler'
34
import { getPseudoLocaleNames } from './pseudo-locale'
45

0 commit comments

Comments
 (0)