Skip to content

Commit 1770253

Browse files
committed
Transform getLocale() and other APIs
1 parent 9d30b42 commit 1770253

File tree

4 files changed

+133
-14
lines changed

4 files changed

+133
-14
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ In transform mode, calls to this function are replaced with `undefined`.
5959
Return a promise that is resolved when the next set of templates are loaded and
6060
available for rendering. Applications in runtime mode should always `await localeReady()` before rendering.
6161

62-
In transform mode, calls to this function are replaced with `undefined`.
62+
In transform mode, calls to this function are replaced with
63+
`Promise.resolve(undefined)`.
6364

6465
### `msg(id: string, template, ...args): string|TemplateResult`
6566

src/outputters/transform.ts

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export function transformOutput(
5252
}
5353
opts.outDir = pathLib.join(outRoot, '/', locale);
5454
program.emit(undefined, undefined, undefined, undefined, {
55-
before: [litLocalizeTransform(translations, program)],
55+
before: [litLocalizeTransform(translations, locale, program)],
5656
});
5757
}
5858
}
@@ -62,10 +62,11 @@ export function transformOutput(
6262
*/
6363
export function litLocalizeTransform(
6464
translations: Map<string, Message> | undefined,
65+
locale: string,
6566
program: ts.Program
6667
): ts.TransformerFactory<ts.SourceFile> {
6768
return (context) => {
68-
const transformer = new Transformer(context, translations, program);
69+
const transformer = new Transformer(context, translations, locale, program);
6970
return (file) => ts.visitNode(file, transformer.boundVisitNode);
7071
};
7172
}
@@ -76,26 +77,32 @@ export function litLocalizeTransform(
7677
class Transformer {
7778
private context: ts.TransformationContext;
7879
private translations: Map<string, Message> | undefined;
80+
private locale: string;
7981
private typeChecker: ts.TypeChecker;
8082
boundVisitNode = this.visitNode.bind(this);
8183

8284
constructor(
8385
context: ts.TransformationContext,
8486
translations: Map<string, Message> | undefined,
87+
locale: string,
8588
program: ts.Program
8689
) {
8790
this.context = context;
8891
this.translations = translations;
92+
this.locale = locale;
8993
this.typeChecker = program.getTypeChecker();
9094
}
9195

9296
/**
9397
* Top-level delegating visitor for all nodes.
9498
*/
9599
visitNode(node: ts.Node): ts.VisitResult<ts.Node> {
100+
// msg('greeting', 'hello') -> 'hola'
96101
if (isMsgCall(node, this.typeChecker)) {
97102
return this.replaceMsgCall(node);
98103
}
104+
105+
// html`<b>${msg('greeting', 'hello')}</b>` -> html`<b>hola</b>`
99106
if (isLitExpression(node)) {
100107
// If an html-tagged template literal embeds a msg call, we want to
101108
// collapse the result of that msg call into the parent template.
@@ -105,9 +112,38 @@ class Transformer {
105112
)
106113
);
107114
}
115+
116+
// import ... from 'lit-localize' -> (removed)
108117
if (this.isLitLocalizeImport(node)) {
109118
return undefined;
110119
}
120+
121+
// configureLocalization(...) -> undefined
122+
if (
123+
this.isCallToTaggedFunction(node, '_LIT_LOCALIZE_CONFIGURE_LOCALIZATION_')
124+
) {
125+
return ts.createIdentifier('undefined');
126+
}
127+
128+
// getLocale() -> "es-419"
129+
if (this.isCallToTaggedFunction(node, '_LIT_LOCALIZE_GET_LOCALE_')) {
130+
return ts.createStringLiteral(this.locale);
131+
}
132+
133+
// setLocale("es-419") -> undefined
134+
if (this.isCallToTaggedFunction(node, '_LIT_LOCALIZE_SET_LOCALE_')) {
135+
return ts.createIdentifier('undefined');
136+
}
137+
138+
// localeReady() -> Promise.resolve(undefined)
139+
if (this.isCallToTaggedFunction(node, '_LIT_LOCALIZE_LOCALE_READY_')) {
140+
return ts.createCall(
141+
ts.createPropertyAccess(ts.createIdentifier('Promise'), 'resolve'),
142+
[],
143+
[ts.createIdentifier('undefined')]
144+
);
145+
}
146+
111147
return ts.visitEachChild(node, this.boundVisitNode, this.context);
112148
}
113149

@@ -358,6 +394,22 @@ class Transformer {
358394
}
359395
return false;
360396
}
397+
398+
/**
399+
* Return whether the given node is call to a function which is is "tagged"
400+
* with the given special identifying property (e.g. "_LIT_LOCALIZE_MSG_").
401+
*/
402+
isCallToTaggedFunction(
403+
node: ts.Node,
404+
tagProperty: string
405+
): node is ts.CallExpression {
406+
if (!ts.isCallExpression(node)) {
407+
return false;
408+
}
409+
const type = this.typeChecker.getTypeAtLocation(node.expression);
410+
const props = this.typeChecker.getPropertiesOfType(type);
411+
return props.some((prop) => prop.escapedName === tagProperty);
412+
}
361413
}
362414

363415
/**

src/tests/transform.unit.test.ts

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ function checkTransform(
3030
inputTs: string,
3131
expectedJs: string,
3232
messages: Message[],
33-
autoImport = true
33+
autoImport = true,
34+
locale = 'en'
3435
) {
3536
if (autoImport) {
3637
// Rather than fuss with imports in all the test cases, this little hack
@@ -54,7 +55,7 @@ function checkTransform(
5455
// them here, so it's a waste of time.
5556
options.typeRoots = [];
5657
const result = compileTsFragment(inputTs, options, cache, (program) => ({
57-
before: [litLocalizeTransform(makeMessageIdMap(messages), program)],
58+
before: [litLocalizeTransform(makeMessageIdMap(messages), locale, program)],
5859
}));
5960

6061
let formattedExpected = prettier.format(expectedJs, {parser: 'typescript'});
@@ -321,3 +322,62 @@ test('exclude different msg function', (t) => {
321322
false
322323
);
323324
});
325+
326+
test('configureLocalization() -> undefined', (t) => {
327+
checkTransform(
328+
t,
329+
`import {configureLocalization} from './lib_client/index.js';
330+
configureLocalization({
331+
sourceLocale: 'en',
332+
targetLocales: ['es-419'],
333+
loadLocale: (locale: string) => import(\`/\${locale}.js\`),
334+
});`,
335+
`undefined;`,
336+
[],
337+
true
338+
);
339+
});
340+
341+
test('getLocale() -> "es-419"', (t) => {
342+
checkTransform(
343+
t,
344+
`import {getLocale} from './lib_client/index.js';
345+
getLocale();`,
346+
`"en";`,
347+
[],
348+
true,
349+
'en'
350+
);
351+
352+
checkTransform(
353+
t,
354+
`import {getLocale} from './lib_client/index.js';
355+
getLocale();`,
356+
`"es-419";`,
357+
[],
358+
true,
359+
'es-419'
360+
);
361+
});
362+
363+
test('setLocale() -> undefined', (t) => {
364+
checkTransform(
365+
t,
366+
`import {setLocale} from './lib_client/index.js';
367+
setLocale("es-419");`,
368+
`undefined;`,
369+
[],
370+
true
371+
);
372+
});
373+
374+
test('localeReady() -> Promise.resolve(undefined)', (t) => {
375+
checkTransform(
376+
t,
377+
`import {localeReady} from './lib_client/index.js';
378+
localeReady().then(() => console.log('ok'))`,
379+
`Promise.resolve(undefined).then(() => console.log('ok'))`,
380+
[],
381+
true
382+
);
383+
});

src_client/index.ts

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -93,26 +93,32 @@ let loading = new Deferred<void>();
9393
* Set runtime configuration parameters for lit-localize. This function must be
9494
* called before using any other lit-localize function.
9595
*/
96-
export function configureLocalization(config: Configuration) {
96+
export const configureLocalization: ((config: Configuration) => void) & {
97+
_LIT_LOCALIZE_CONFIGURE_LOCALIZATION_?: never;
98+
} = (config: Configuration) => {
9799
activeLocale = sourceLocale = config.sourceLocale;
98100
validLocales = new Set(config.targetLocales);
99101
validLocales.add(config.sourceLocale);
100102
loadLocale = config.loadLocale;
101-
}
103+
};
102104

103105
/**
104106
* Return the active locale code. Returns empty string if lit-localize has not
105107
* yet been configured.
106108
*/
107-
export function getLocale(): string {
109+
export const getLocale: (() => string) & {
110+
_LIT_LOCALIZE_GET_LOCALE_?: never;
111+
} = () => {
108112
return activeLocale;
109-
}
113+
};
110114

111115
/**
112116
* Set the active locale code, and begin loading templates for that locale using
113117
* the `loadLocale` function that was passed to `configureLocalization`.
114118
*/
115-
export function setLocale(newLocale: string): void {
119+
export const setLocale: ((newLocale: string) => void) & {
120+
_LIT_LOCALIZE_SET_LOCALE_?: never;
121+
} = (newLocale: string) => {
116122
if (
117123
newLocale === activeLocale ||
118124
!validLocales ||
@@ -147,15 +153,15 @@ export function setLocale(newLocale: string): void {
147153
}
148154
);
149155
}
150-
}
156+
};
151157

152158
/**
153159
* Return a promise that is resolved when the next set of templates are loaded
154160
* and available for rendering.
155161
*/
156-
export function localeReady(): Promise<void> {
157-
return loading.promise;
158-
}
162+
export const localeReady: (() => Promise<void>) & {
163+
_LIT_LOCALIZE_LOCALE_READY_?: never;
164+
} = () => loading.promise;
159165

160166
/**
161167
* Make a string or lit-html template localizable.

0 commit comments

Comments
 (0)