@@ -13,6 +13,206 @@ import {TemplateResult} from 'lit-html';
13
13
14
14
/* eslint-disable @typescript-eslint/no-explicit-any */
15
15
16
+ /**
17
+ * Configuration parameters for lit-localize when in runtime mode.
18
+ */
19
+ export interface RuntimeConfiguration {
20
+ /**
21
+ * Required locale code in which source templates in this project are written,
22
+ * and the initial active locale.
23
+ */
24
+ sourceLocale : string ;
25
+
26
+ /**
27
+ * Required locale codes that are supported by this project. Should not
28
+ * include the `sourceLocale` code.
29
+ */
30
+ targetLocales : Iterable < string > ;
31
+
32
+ /**
33
+ * Required function that returns a promise of the localized templates for the
34
+ * given locale code. For security, this function will only ever be called
35
+ * with a `locale` that is contained by `targetLocales`.
36
+ */
37
+ loadLocale : ( locale : string ) => Promise < LocaleModule > ;
38
+ }
39
+
40
+ /**
41
+ * Configuration parameters for lit-localize when in transform mode.
42
+ */
43
+ export interface TransformConfiguration {
44
+ /**
45
+ * Required locale code in which source templates in this project are written,
46
+ * and the active locale.
47
+ */
48
+ sourceLocale : string ;
49
+ }
50
+
51
+ /**
52
+ * The template-like types that can be passed to `msg`.
53
+ */
54
+ export type TemplateLike =
55
+ | string
56
+ | TemplateResult
57
+ | ( ( ...args : any [ ] ) => string )
58
+ | ( ( ...args : any [ ] ) => TemplateResult ) ;
59
+
60
+ /**
61
+ * A mapping from template ID to template.
62
+ */
63
+ export type TemplateMap = { [ id : string ] : TemplateLike } ;
64
+
65
+ /**
66
+ * The expected exports of a locale module.
67
+ */
68
+ export interface LocaleModule {
69
+ templates : TemplateMap ;
70
+ }
71
+
72
+ class Deferred < T > {
73
+ readonly promise : Promise < T > ;
74
+ private _resolve ! : ( value : T ) => void ;
75
+ private _reject ! : ( error : Error ) => void ;
76
+ settled = false ;
77
+
78
+ constructor ( ) {
79
+ this . promise = new Promise < T > ( ( resolve , reject ) => {
80
+ this . _resolve = resolve ;
81
+ this . _reject = reject ;
82
+ } ) ;
83
+ }
84
+
85
+ resolve ( value : T ) {
86
+ this . settled = true ;
87
+ this . _resolve ( value ) ;
88
+ }
89
+
90
+ reject ( error : Error ) {
91
+ this . settled = true ;
92
+ this . _reject ( error ) ;
93
+ }
94
+ }
95
+
96
+ let activeLocale = '' ;
97
+ let loadingLocale : string | undefined ;
98
+ let sourceLocale : string | undefined ;
99
+ let validLocales : Set < string > | undefined ;
100
+ let loadLocale : ( ( locale : string ) => Promise < LocaleModule > ) | undefined ;
101
+ let configured = false ;
102
+ let templates : TemplateMap | undefined ;
103
+ let loading = new Deferred < void > ( ) ;
104
+
105
+ /**
106
+ * Set configuration parameters for lit-localize when in runtime mode. Returns
107
+ * an object with functions:
108
+ *
109
+ * - `getLocale`: Return the active locale code.
110
+ * - `setLocale`: Set the active locale code.
111
+ *
112
+ * Throws if called more than once.
113
+ */
114
+ export function configureLocalization ( config : RuntimeConfiguration ) {
115
+ if ( configured === true ) {
116
+ throw new Error ( 'lit-localize can only be configured once' ) ;
117
+ }
118
+ configured = true ;
119
+ activeLocale = sourceLocale = config . sourceLocale ;
120
+ validLocales = new Set ( config . targetLocales ) ;
121
+ validLocales . add ( config . sourceLocale ) ;
122
+ loadLocale = config . loadLocale ;
123
+ return { getLocale, setLocale} ;
124
+ }
125
+
126
+ /**
127
+ * Set configuration parameters for lit-localize when in transform mode. Returns
128
+ * an object with function:
129
+ *
130
+ * - `getLocale`: Return the active locale code.
131
+ *
132
+ * Throws if called more than once.
133
+ */
134
+ export function configureTransformLocalization ( config : TransformConfiguration ) {
135
+ if ( configured === true ) {
136
+ throw new Error ( 'lit-localize can only be configured once' ) ;
137
+ }
138
+ configured = true ;
139
+ activeLocale = sourceLocale = config . sourceLocale ;
140
+ return { getLocale} ;
141
+ }
142
+
143
+ /**
144
+ * Return the active locale code.
145
+ */
146
+ function getLocale ( ) : string {
147
+ return activeLocale ;
148
+ }
149
+
150
+ /**
151
+ * Set the active locale code, and begin loading templates for that locale using
152
+ * the `loadLocale` function that was passed to `configureLocalization`. Returns
153
+ * a promise that resolves when the next locale is ready to be rendered.
154
+ *
155
+ * Note that if a second call to `setLocale` is made while the first requested
156
+ * locale is still loading, then the second call takes precedence, and the
157
+ * promise returned from the first call will resolve when second locale is
158
+ * ready. If you need to know whether a particular locale was loaded, check
159
+ * `getLocale` after the promise resolves.
160
+ *
161
+ * Throws if the given locale is not contained by the configured `sourceLocale`
162
+ * or `targetLocales`.
163
+ */
164
+ function setLocale ( newLocale : string ) : Promise < void > {
165
+ if ( ! configured || ! validLocales || ! loadLocale ) {
166
+ throw new Error ( 'Must call configureLocalization before setLocale' ) ;
167
+ }
168
+ if ( newLocale === loadingLocale ?? activeLocale ) {
169
+ return loading . promise ;
170
+ }
171
+ if ( ! validLocales . has ( newLocale ) ) {
172
+ throw new Error ( 'Invalid locale code' ) ;
173
+ }
174
+ loadingLocale = newLocale ;
175
+ if ( loading . settled ) {
176
+ loading = new Deferred ( ) ;
177
+ }
178
+ if ( newLocale === sourceLocale ) {
179
+ activeLocale = newLocale ;
180
+ loadingLocale = undefined ;
181
+ templates = undefined ;
182
+ loading . resolve ( ) ;
183
+ } else {
184
+ loadLocale ( newLocale ) . then (
185
+ ( mod ) => {
186
+ if ( newLocale === loadingLocale ) {
187
+ activeLocale = newLocale ;
188
+ loadingLocale = undefined ;
189
+ templates = mod . templates ;
190
+ loading . resolve ( ) ;
191
+ }
192
+ // Else another locale was requested in the meantime. Don't resolve or
193
+ // reject, because the newer load call is going to use the same promise.
194
+ // Note the user can call getLocale() after the promise resolves if they
195
+ // need to check if the locale is still the one they expected to load.
196
+ } ,
197
+ ( err ) => {
198
+ if ( newLocale === loadingLocale ) {
199
+ loading . reject ( err ) ;
200
+ }
201
+ }
202
+ ) ;
203
+ }
204
+ return loading . promise ;
205
+ }
206
+
207
+ /**
208
+ * Make a string or lit-html template localizable.
209
+ *
210
+ * @param id A project-wide unique identifier for this template.
211
+ * @param template A string, a lit-html template, or a function that returns
212
+ * either a string or lit-html template.
213
+ * @param args In the case that `template` is a function, it is invoked with
214
+ * the 3rd and onwards arguments to `msg`.
215
+ */
16
216
export function msg ( id : string , template : string ) : string ;
17
217
18
218
export function msg ( id : string , template : TemplateResult ) : TemplateResult ;
@@ -29,24 +229,19 @@ export function msg<F extends (...args: any[]) => TemplateResult>(
29
229
...params : Parameters < F >
30
230
) : TemplateResult ;
31
231
32
- /**
33
- * TODO(aomarks) This is a temporary stub implementation of the msg function. It
34
- * does not yet support actual localization, and is only used by the "transform"
35
- * output mode, since the user needs something to import.
36
- *
37
- * It may actually make more sense to move most of the generated code from
38
- * "runtime" mode into this library, so that users can
39
- * `import {msg} from 'lit-localize'` and tell it where to fetch translation
40
- * (this will make more sense after the planned revamp to support runtime async
41
- * locale loading and re-rendering).
42
- */
43
232
export function msg (
44
- _id : string ,
45
- template : string | TemplateResult | ( ( ) => string | TemplateResult ) ,
233
+ id : string ,
234
+ template : TemplateLike ,
46
235
...params : any [ ]
47
236
) : string | TemplateResult {
237
+ if ( templates ) {
238
+ const localized = templates [ id ] ;
239
+ if ( localized ) {
240
+ template = localized ;
241
+ }
242
+ }
48
243
if ( typeof template === 'function' ) {
49
- return ( template as any ) ( ...params ) ;
244
+ return template ( ...params ) ;
50
245
}
51
246
return template ;
52
247
}
0 commit comments