-
Notifications
You must be signed in to change notification settings - Fork 2
/
main.js
379 lines (346 loc) · 12.6 KB
/
main.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
'use babel'
let franc = null
let Promise = null
let CompositeDisposable = null
let Range = null
let UserSupportHelper = null
let Language = null
let TranslatorPlusDictionary = null
let TranslatorPlusDictionaryView = null
let NotificationView = null
let ExternalApis = null
let MicrosoftTranslatorClient = null
let Dejizo = null
let Configuration = null
let isInitialized = false
function initialize() {
if (isInitialized) return
// Initialize modules
franc = require('franc')
UserSupportHelper = require('atom-user-support-helper')
Promise = require('bluebird')
const atom = require('atom')
CompositeDisposable = atom.CompositeDisposable
Range = atom.Range
// Initialize the local modules
Language = require('./language')
TranslatorPlusDictionary = require('./translator-plus-dictionary')
TranslatorPlusDictionaryView = require('./translator-plus-dictionary-view')
ExternalApis = require('./external-apis')
NotificationView = require('./notification-view')
MicrosoftTranslatorClient = require('./microsoft-translator-client')
Dejizo = require('./dejizo')
Configuration = require('./configuration')
isInitialized = true
}
function getCodeFromName(name) {
const lang = Language.getFromName(name)
if (lang) return lang.code
else return lang
}
function getNameFromCode(code) {
const lang = Language.getFromCode(code)
if (lang) return lang.name
else return lang
}
export default {
config: {
microsoftTranslatorClientId: {
title: 'Client ID of Microsoft Translator API',
type: 'string',
default: ''
},
microsoftTranslatorClientSecret: {
title: 'Client Secret of Microsoft Translator API',
type: 'string',
default: ''
},
languages: {
title: "Languages used in this package",
description: "https://github.com/wooorm/franc/blob/master/Supported-Languages.md is a list of languages names.",
type: 'array',
default: ['eng', 'jpn'],
items: {
type: 'string'
}
},
primaryLanguage: {
title: "The Primary Language",
type: 'string',
default: 'jpn'
},
secondaryLanguage: {
title: "The Secondary Language",
type: 'string',
default: 'eng'
}
},
activate() {
initialize()
// Initialize the field
this.subscriptions = new CompositeDisposable()
this.languages = []
this.francOptions = { whitelist: [], minLength: 1 }
this.translatorPlusDictionary = new TranslatorPlusDictionary()
this.notificationView = new NotificationView()
this.views = new Map()
// Initialize languages
this.updateLanguages()
// Add Microsoft Translator
const microsoftTranslatorClientId = atom.config.get('translator-plus-dictionary.microsoftTranslatorClientId')
const microsoftTranslatorClientSecret = atom.config.get('translator-plus-dictionary.microsoftTranslatorClientSecret')
this.microsoftTranslatorClient = new MicrosoftTranslatorClient(microsoftTranslatorClientId, microsoftTranslatorClientSecret)
this.translatorPlusDictionary.translators.push(this.microsoftTranslatorClient)
// Add Dejizo
const dejizo = new Dejizo()
this.translatorPlusDictionary.dictionaries.push(dejizo)
// Load external APIs
const apis = new ExternalApis()
apis.load(this.translatorPlusDictionary)
// Wire translatorPlusDictionary and NotificationView
this.subscriptions.add(
this.translatorPlusDictionary.onStarted((x) => { this.notificationView.started(x) })
)
this.subscriptions.add(
this.translatorPlusDictionary.onFinished((x) => { this.notificationView.finished(x) })
)
this.subscriptions.add(
this.translatorPlusDictionary.onFailed((x) => { this.notificationView.failed(x) })
)
// Register the commands
this.subscriptions.add(
atom.commands.add('atom-text-editor', { 'translator-plus-dictionary:translate': () => this.translate() })
)
this.subscriptions.add(
atom.commands.add('atom-text-editor', { 'translator-plus-dictionary:close-all': () => this.closeAll() })
)
this.subscriptions.add(
atom.commands.add('body', { 'translator-plus-dictionary:configure': () => this.configure() })
)
// Observe the configuration
this.subscriptions.add(
atom.config.observe(Configuration.getKey(), (value) => {
this.microsoftTranslatorClient.setClientIdAndSecret(
value.microsoftTranslatorClientId,
value.microsoftTranslatorClientSecret
)
this.updateLanguages()
})
)
// Configure user-support-helper
this.configureUserSupportHelper()
},
configureUserSupportHelper() {
this.helper = new UserSupportHelper()
// Add configuration settings
const panel = this.helper.getInteractiveConfigurationPanel()
panel.add(Configuration.getKey('microsoftTranslatorClientId'), {
type: 'input',
name: 'Client ID of Microsoft Translator API',
detail: '<a href="https://www.microsoft.com/en-us/translator/getstarted.aspx">This page</a> helps you to understand how to obtain the client ID.',
validate: (result) => { return (result.length !== 0) ? true: 'too short' }
})
panel.add(Configuration.getKey('microsoftTranslatorClientSecret'), {
type: 'input',
name: 'Client Secret of Microsoft Translator API',
validate: (result) => { return (result.length !== 0) ? true: 'too short' }
})
panel.add(Configuration.getKey('languages'), {
type: 'multipleList',
name: 'Languages used in this package',
default: [],
choices: Language.list.map((elem) => { return elem.name }),
map: (names) => { return names.map((name) => { return getCodeFromName(name) }) },
inverseMap: (codes) => { return codes.map((code) => { return getNameFromCode(code) }) },
validate: (result) => {
if (result.length >= 2) {
return true
} else {
return "More than 2 language are required"
}
}
})
panel.add(Configuration.getKey('primaryLanguage'), {
type: 'list',
name: 'The primary language',
message: "The recommended value is your native language.",
default: '',
choices: [],
map: (name) => { return getCodeFromName(name) },
inverseMap: (code) => { return getNameFromCode(code) }
})
panel.add(Configuration.getKey('secondaryLanguage'), {
type: 'list',
name: 'The secondary language',
message: "The recommended value is 'eng'.",
default: 'eng',
choices: ['eng'],
map: (name) => { return getCodeFromName(name) },
inverseMap: (code) => { return getNameFromCode(code) }
})
},
consumeStatusBar(statusBar) {
// Add a tile to the status bar
this.statusBarTile = statusBar.addLeftTile({
item: this.notificationView.statusBar,
priority: 100
})
},
deactivate() {
this.views.forEach((view) => view.destory())
this.subscriptions.dispose()
if (this.statusBarTile) {
this.statusBarTile.destory()
}
this.notificationView.destory()
this.helper.destroy()
},
serialize() {},
closeView(editor, view) {
for (let i = 0; i < this.views.get(editor).length; i++) {
if (this.views.get(editor)[i] === view) {
this.views.get(editor).slice(i, 1)
}
}
view.destroy()
if (this.views.get(editor).length === 0) {
editor.getElement().classList.remove('translator-plus-dictionary-active')
}
},
closeAll() {
const editor = atom.workspace.getActiveTextEditor()
if (!editor || !this.views.has(editor)) return
for (const v of this.views.get(editor)) {
v.destroy()
}
this.views.delete(editor)
editor.getElement().classList.remove('translator-plus-dictionary-active')
},
configure() {
const panel = this.helper.getInteractiveConfigurationPanel()
return panel.show((id, configValues) => {
switch (id) {
case null:
return { id: Configuration.getKey("microsoftTranslatorClientId") }
case Configuration.getKey("microsoftTranslatorClientId"):
return { id: Configuration.getKey("microsoftTranslatorClientSecret") }
case Configuration.getKey("microsoftTranslatorClientSecret"):
return { id: Configuration.getKey("languages") }
case Configuration.getKey("languages"):
return {
id: Configuration.getKey("primaryLanguage"),
choices: configValues.get(Configuration.getKey("languages")).map((code) => {
return getNameFromCode(code)
}),
}
case Configuration.getKey("primaryLanguage"):
return {
id: Configuration.getKey("secondaryLanguage"),
choices: configValues.get(Configuration.getKey("languages")).map((code) => {
return getNameFromCode(code)
}),
finished: true
}
default:
return null
}
}).then((result) => {
atom.config.set(Configuration.getKey("configured"), true)
return result
})
},
configureIfNeeded() {
const isSetClientIdAndSecret =
(
Configuration.getValueWithoutDefault("microsoftTranslatorClientId") &&
Configuration.getValueWithoutDefault("microsoftTranslatorClientSecret")
)
const isSetLanguage =
(
Configuration.getValueWithoutDefault("languages") ||
Configuration.getValueWithoutDefault("secondaryLanguage") ||
Configuration.getValueWithoutDefault("secondaryLanguage")
)
const alreadyConfigured = Configuration.getValue("configured") || false
if (isSetClientIdAndSecret && (isSetLanguage || alreadyConfigured)) {
return new Promise((resolve) => resolve())
} else {
return this.configure()
}
},
translate() {
this.configureIfNeeded().then(
() => {
// Execute after configuration
const editor = atom.workspace.getActiveTextEditor()
if (!editor) return
atom.workspace.getActiveTextEditor().getElement().classList.add('translator-plus-dictionary-active')
let targets = []
// Get texts from selected texts
for (const range of editor.getSelectedBufferRanges()) {
if ((range.start.row === range.end.row) && (range.start.column === range.end.column)) continue
text = editor.getTextInRange(range)
targets.push({
range: range,
text: text
})
}
if (targets.length === 0) {
// Get words from cursor positions if no text are selected
for (const cursor of editor.getCursors()) {
const beginWord = cursor.getBeginningOfCurrentWordBufferPosition()
const endWord = cursor.getEndOfCurrentWordBufferPosition()
const range = new Range(beginWord, endWord)
targets.push({
range: range,
text: editor.getTextInRange(range)
})
}
}
for (const target of targets) {
// Decide the language of the texts by using frac
const from = franc(target.text, this.francOptions)
const lang = Language.getFromCode(from)
target.from = lang
// Decide the output language by using the primary and secondary languages
if (this.primaryLanguage === lang) {
target.to = this.secondaryLanguage
} else {
target.to = this.primaryLanguage
}
// Generate a view
const view = new TranslatorPlusDictionaryView(
editor, target, this.languages, this.translatorPlusDictionary
)
// Add to views
if (!this.views.has(editor)) {
this.views.set(editor, [])
}
this.views.get(editor).push(view)
view.onClosed(() => { this.closeView(editor, view) })
}
}
)
},
updateLanguages() {
const languageCodes = atom.config.get('translator-plus-dictionary.languages')
this.languages = []
languageCodes.forEach((code) => {
language = Language.getFromCode(code)
if (language === null) {
// TODO notify anerror
return
}
// Store the usable languages information in the form of Language class instances
this.languages.push(language)
this.francOptions.whitelist.push(code)
})
if (this.languages.length === 0) {
// Use all languages detected by franc if there is no `languages` configuration.
this.languages = Language.list
this.francOptions.whiteList = Language.list.map((e) => { return e.code })
}
this.primaryLanguage = Language.getFromCode(atom.config.get('translator-plus-dictionary.primaryLanguage'))
this.secondaryLanguage = Language.getFromCode(atom.config.get('translator-plus-dictionary.secondaryLanguage'))
}
}