@@ -107,22 +107,68 @@ class ZeroWidthEncoder {
107
107
return text ;
108
108
}
109
109
}
110
- class LiveTranslatorEnabler {
111
- constructor ( options , callback ) {
110
+ class LiveTranslatorManager {
111
+ constructor ( options ) {
112
112
this . _enabled = false ;
113
- this . _persist = options . persist || false ;
114
113
this . _options = options ;
115
- this . _callback = callback ;
114
+ this . _zwEncoder = new ZeroWidthEncoder ( ) ;
115
+ // handle persistance
116
116
const savedRaw = localStorage . getItem ( 'live-translator-enabled' ) ;
117
- if ( this . _persist && savedRaw ) {
117
+ if ( this . _options . persist && savedRaw ) {
118
118
const saved = JSON . parse ( savedRaw ) ;
119
119
if ( typeof saved === 'boolean' ) {
120
120
this . toggle ( saved ) ;
121
121
}
122
122
}
123
+ // initialize UI
124
+ this . _enableButton = document . createElement ( 'button' ) ;
125
+ this . _indicator = document . createElement ( 'span' ) ;
126
+ const style = document . createElement ( 'style' ) ;
127
+ style . id = 'live-translator-plugin-style' ;
128
+ style . innerHTML = css ;
129
+ document . head . appendChild ( style ) ;
130
+ this . _enableButton . innerText = 'LT' ;
131
+ this . _enableButton . classList . add ( 'live-translator-enable-button' ) ;
132
+ this . _indicator . classList . add ( 'live-translator-enable-button-indicator' ) ;
133
+ this . _enableButton . appendChild ( this . _indicator ) ;
134
+ this . _enableButton . addEventListener ( 'click' , ( ) => {
135
+ this . toggle ( ) ;
136
+ this . refreshI18n ( ) ;
137
+ this . render ( ) ;
138
+ } ) ;
139
+ document . body . appendChild ( this . _enableButton ) ;
140
+ // initialize encode
141
+ const originalFormatter = this . _options . i18n . formatter ;
142
+ this . _options . i18n . formatter = {
143
+ interpolate ( message , values , path ) {
144
+ const meta = this . _zwEncoder . encode ( JSON . stringify ( {
145
+ message,
146
+ values,
147
+ path,
148
+ locale : this . _options . i18n . locale ,
149
+ } ) ) ;
150
+ const original = originalFormatter . interpolate ( message , values , path ) ;
151
+ return ( original && this . _enabled ) ? [ meta , ...original ] : original ;
152
+ } ,
153
+ } ;
154
+ // initialize decode & render
155
+ const throttler = ( 0 , throttle_1 . default ) ( ( ) => this . render ( ) , 800 ) ;
156
+ const observer = new MutationObserver ( throttler ) ;
157
+ observer . observe ( document . documentElement , {
158
+ subtree : true ,
159
+ attributes : true ,
160
+ characterData : true ,
161
+ childList : false ,
162
+ } ) ;
163
+ document . documentElement . addEventListener ( 'mousemove' , throttler ) ;
164
+ // render for the first time
165
+ this . refreshI18n ( ) ;
166
+ this . render ( ) ;
123
167
}
124
- enabled ( ) {
125
- return this . _enabled ;
168
+ refreshI18n ( ) {
169
+ const originalLocale = this . _options . i18n . locale ;
170
+ this . _options . i18n . locale = '' ;
171
+ this . _options . i18n . locale = originalLocale ;
126
172
}
127
173
toggle ( enable ) {
128
174
if ( enable !== undefined ) {
@@ -131,14 +177,59 @@ class LiveTranslatorEnabler {
131
177
else {
132
178
this . _enabled = ! this . _enabled ;
133
179
}
134
- if ( this . _persist ) {
180
+ if ( this . _options . persist ) {
135
181
localStorage . setItem ( 'live-translator-enabled' , JSON . stringify ( this . _enabled ) ) ;
136
182
}
137
- // Refresh translations to show immediately
138
- const originalLocale = this . _options . i18n . locale ;
139
- this . _options . i18n . locale = '' ;
140
- this . _options . i18n . locale = originalLocale ;
141
- this . _callback ( ) ;
183
+ }
184
+ render ( ) {
185
+ const badges = document . querySelectorAll ( '.live-translator-badge' ) ;
186
+ badges . forEach ( ( badge ) => {
187
+ badge . remove ( ) ;
188
+ } ) ;
189
+ this . _indicator . style . background = this . _enabled ? 'lightgreen' : 'red' ;
190
+ if ( ! this . _enabled ) {
191
+ return ;
192
+ }
193
+ const re = new RegExp ( `${ this . _zwEncoder . START } [${ this . _zwEncoder . ZERO } ${ this . _zwEncoder . ONE } ${ this . _zwEncoder . SPACE } ]+${ this . _zwEncoder . END } ` , 'gm' ) ;
194
+ const queue = [ document . documentElement ] ;
195
+ while ( queue . length > 0 ) {
196
+ const node = queue . pop ( ) ;
197
+ const badges = [ ] ;
198
+ const parent = node . parentElement ;
199
+ if ( node instanceof Text ) {
200
+ const matches = node . textContent . match ( re ) ;
201
+ for ( const match of matches !== null && matches !== void 0 ? matches : [ ] ) {
202
+ const meta = JSON . parse ( this . _zwEncoder . decode ( match ) ) ;
203
+ badges . push ( createBadge ( meta , this . _options ) ) ;
204
+ }
205
+ }
206
+ const attributes = ( node . attributes ? [ ...node . attributes ] : [ ] )
207
+ . map ( ( attribute ) => ( { attribute, match : attribute . value . match ( re ) } ) )
208
+ . filter ( ( { match } ) => ! ! match ) ;
209
+ for ( const { attribute, match } of attributes ) {
210
+ for ( const m of match ) {
211
+ const meta = JSON . parse ( this . _zwEncoder . decode ( m ) ) ;
212
+ badges . push ( createBadge ( meta , this . _options , attribute . name ) ) ;
213
+ }
214
+ }
215
+ if ( badges . length ) {
216
+ let container ;
217
+ if ( node . previousElementSibling && node . previousElementSibling . classList . contains ( 'live-translator-badge-container' ) ) {
218
+ container = node . previousElementSibling ;
219
+ }
220
+ else {
221
+ container = document . createElement ( 'span' ) ;
222
+ container . classList . add ( 'live-translator-badge-container' ) ;
223
+ parent . insertBefore ( container , node ) ;
224
+ }
225
+ for ( const badge of badges ) {
226
+ container . appendChild ( badge ) ;
227
+ }
228
+ }
229
+ for ( const child of node . childNodes ) {
230
+ queue . push ( child ) ;
231
+ }
232
+ }
142
233
}
143
234
}
144
235
const createBadge = ( meta , options , attribute ) => {
@@ -165,96 +256,6 @@ const createBadge = (meta, options, attribute) => {
165
256
exports . LiveTranslatorPlugin = {
166
257
install ( app , options ) {
167
258
console . log ( 'LiveTranslator is installed' ) ;
168
- // declare
169
- const enableButton = document . createElement ( 'button' ) ;
170
- const indicator = document . createElement ( 'span' ) ;
171
- const zw = new ZeroWidthEncoder ( ) ;
172
- const visualize = ( ) => {
173
- const badges = document . querySelectorAll ( '.live-translator-badge' ) ;
174
- console . log ( 'clearing' , badges . length , 'badges' ) ;
175
- badges . forEach ( ( badge ) => {
176
- badge . remove ( ) ;
177
- } ) ;
178
- indicator . style . background = ltEnabler . enabled ( ) ? 'lightgreen' : 'red' ;
179
- if ( ! ltEnabler . enabled ( ) ) {
180
- return ;
181
- }
182
- const re = new RegExp ( `${ zw . START } [${ zw . ZERO } ${ zw . ONE } ${ zw . SPACE } ]+${ zw . END } ` , 'gm' ) ;
183
- const queue = [ document . documentElement ] ;
184
- while ( queue . length > 0 ) {
185
- const node = queue . pop ( ) ;
186
- const badges = [ ] ;
187
- const parent = node . parentElement ;
188
- if ( node instanceof Text ) {
189
- const matches = node . textContent . match ( re ) ;
190
- for ( const match of matches !== null && matches !== void 0 ? matches : [ ] ) {
191
- const meta = JSON . parse ( zw . decode ( match ) ) ;
192
- badges . push ( createBadge ( meta , options ) ) ;
193
- }
194
- }
195
- const attributes = ( node . attributes ? [ ...node . attributes ] : [ ] )
196
- . map ( ( attribute ) => ( { attribute, match : attribute . value . match ( re ) } ) )
197
- . filter ( ( { match } ) => ! ! match ) ;
198
- for ( const { attribute, match } of attributes ) {
199
- for ( const m of match ) {
200
- const meta = JSON . parse ( zw . decode ( m ) ) ;
201
- badges . push ( createBadge ( meta , options , attribute . name ) ) ;
202
- }
203
- }
204
- if ( badges . length ) {
205
- let container ;
206
- if ( node . previousElementSibling && node . previousElementSibling . classList . contains ( 'live-translator-badge-container' ) ) {
207
- container = node . previousElementSibling ;
208
- }
209
- else {
210
- container = document . createElement ( 'span' ) ;
211
- container . classList . add ( 'live-translator-badge-container' ) ;
212
- parent . insertBefore ( container , node ) ;
213
- }
214
- for ( const badge of badges ) {
215
- container . appendChild ( badge ) ;
216
- }
217
- }
218
- for ( const child of node . childNodes ) {
219
- queue . push ( child ) ;
220
- }
221
- }
222
- } ;
223
- const ltEnabler = new LiveTranslatorEnabler ( options , visualize ) ;
224
- // bind & style UI
225
- const style = document . createElement ( 'style' ) ;
226
- style . id = 'live-translator-plugin-style' ;
227
- style . innerHTML = css ;
228
- document . head . appendChild ( style ) ;
229
- enableButton . innerText = 'LT' ;
230
- enableButton . classList . add ( 'live-translator-enable-button' ) ;
231
- indicator . classList . add ( 'live-translator-enable-button-indicator' ) ;
232
- enableButton . appendChild ( indicator ) ;
233
- enableButton . addEventListener ( 'click' , ( ) => ltEnabler . toggle ( ) ) ;
234
- document . body . appendChild ( enableButton ) ;
235
- // encode meta to translation strings
236
- const originalFormatter = options . i18n . formatter ;
237
- options . i18n . formatter = {
238
- interpolate ( message , values , path ) {
239
- const meta = zw . encode ( JSON . stringify ( {
240
- message,
241
- values,
242
- path,
243
- locale : options . i18n . locale ,
244
- } ) ) ;
245
- const original = originalFormatter . interpolate ( message , values , path ) ;
246
- return ( original && ltEnabler . enabled ( ) ) ? [ meta , ...original ] : original ;
247
- } ,
248
- } ;
249
- // decode & visualize meta
250
- const throttler = ( 0 , throttle_1 . default ) ( visualize , 800 ) ;
251
- const observer = new MutationObserver ( throttler ) ;
252
- observer . observe ( document . documentElement , {
253
- subtree : true ,
254
- attributes : true ,
255
- characterData : true ,
256
- childList : false ,
257
- } ) ;
258
- document . documentElement . addEventListener ( 'mousemove' , throttler ) ;
259
+ new LiveTranslatorManager ( options ) ;
259
260
} ,
260
261
} ;
0 commit comments