@drone1/alt 0.4.2 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -53
- package/localization/.localization.cache.json +3100 -908
- package/localization/aa.json +23 -16
- package/localization/af.json +19 -12
- package/localization/agq.json +23 -16
- package/localization/ak.json +23 -16
- package/localization/am.json +23 -16
- package/localization/ar.json +19 -12
- package/localization/as.json +23 -16
- package/localization/asa.json +23 -16
- package/localization/ast.json +16 -9
- package/localization/az.json +17 -10
- package/localization/ba.json +23 -16
- package/localization/bas.json +23 -16
- package/localization/be.json +23 -16
- package/localization/bem.json +23 -16
- package/localization/bez.json +22 -15
- package/localization/bg.json +18 -11
- package/localization/bm.json +17 -10
- package/localization/bn.json +20 -13
- package/localization/bo.json +23 -16
- package/localization/br.json +18 -11
- package/localization/brx.json +23 -16
- package/localization/bs.json +20 -13
- package/localization/byn.json +23 -16
- package/localization/ca.json +16 -9
- package/localization/ccp.json +23 -16
- package/localization/cd-RU.json +16 -9
- package/localization/ceb.json +21 -14
- package/localization/cgg.json +22 -15
- package/localization/chr.json +23 -16
- package/localization/co.json +22 -15
- package/localization/config.json +1 -1
- package/localization/cs.json +18 -11
- package/localization/cu-RU.json +23 -16
- package/localization/da.json +14 -7
- package/localization/de-AT.json +19 -12
- package/localization/de-CH.json +19 -12
- package/localization/de-DE.json +18 -11
- package/localization/dua.json +23 -16
- package/localization/dv.json +23 -16
- package/localization/dz.json +23 -16
- package/localization/ebu.json +23 -16
- package/localization/en.json +9 -2
- package/localization/es-ES.json +17 -10
- package/localization/es-MX.json +18 -11
- package/localization/et.json +20 -13
- package/localization/eu.json +20 -13
- package/localization/fr-CA.json +15 -8
- package/localization/fr-CH.json +15 -8
- package/localization/fr-FR.json +15 -8
- package/localization/gsw.json +20 -13
- package/localization/hi.json +19 -12
- package/localization/hr.json +18 -11
- package/localization/hy.json +21 -14
- package/localization/ja.json +18 -11
- package/localization/km.json +21 -14
- package/localization/ksf.json +23 -16
- package/localization/ku.json +22 -15
- package/localization/kw.json +23 -16
- package/localization/my.json +22 -15
- package/localization/nl.json +18 -11
- package/localization/prs.json +18 -11
- package/localization/reference.js +9 -1
- package/localization/ru.json +14 -7
- package/localization/sq.json +19 -12
- package/localization/swc.json +21 -14
- package/localization/th.json +15 -8
- package/localization/tzm-Latn-.json +23 -16
- package/localization/uk.json +14 -7
- package/localization/vi.json +17 -10
- package/localization/zh-Hans.json +17 -10
- package/localization/zh-Hant.json +18 -11
- package/package.json +4 -3
- package/src/commands/list-models.js +6 -0
- package/src/{translate.js → commands/translate.js} +124 -139
- package/src/{consts.js → lib/consts.js} +12 -0
- package/src/{io.js → lib/io.js} +1 -1
- package/src/{logging.js → lib/logging.js} +3 -3
- package/src/{options.js → lib/options.js} +1 -1
- package/src/lib/reference-loader.js +91 -0
- package/src/{utils.js → lib/utils.js} +15 -0
- package/src/localizer/localize.js +3 -4
- package/src/main.mjs +98 -49
- package/src/providers/anthropic.mjs +38 -2
- package/src/providers/openai.mjs +45 -2
- package/src/shutdown.js +1 -1
- /package/{bin.mjs → alt.mjs} +0 -0
- /package/src/{assert.js → lib/assert.js} +0 -0
- /package/src/{cache.js → lib/cache.js} +0 -0
- /package/src/{config.js → lib/config.js} +0 -0
- /package/src/{context-keys.js → lib/context-keys.js} +0 -0
- /package/src/{logo.js → lib/logo.js} +0 -0
- /package/src/{provider.js → lib/provider.js} +0 -0
package/src/main.mjs
CHANGED
|
@@ -3,25 +3,29 @@ import { program } from 'commander'
|
|
|
3
3
|
import { fileURLToPath } from 'url'
|
|
4
4
|
import { initLocalizer } from './localizer/localize.js'
|
|
5
5
|
import {
|
|
6
|
-
DEFAULT_CONFIG_FILENAME,
|
|
6
|
+
DEFAULT_CONFIG_FILENAME, DEFAULT_LLM_MODELS,
|
|
7
7
|
ENV_VARS,
|
|
8
8
|
LANGTAG_DEFAULT,
|
|
9
|
-
LOCALIZATION_SRC_DIR
|
|
10
|
-
} from './consts.js'
|
|
11
|
-
import { readJsonFile } from './io.js'
|
|
12
|
-
import { printLogo } from './logo.js'
|
|
13
|
-
import { createLog, initLogFromOptions } from './logging.js'
|
|
14
|
-
import { keyList, languageList } from './options.js'
|
|
15
|
-
import { runTranslation } from './translate.js'
|
|
9
|
+
LOCALIZATION_SRC_DIR
|
|
10
|
+
} from './lib/consts.js'
|
|
11
|
+
import { readJsonFile } from './lib/io.js'
|
|
12
|
+
import { printLogo } from './lib/logo.js'
|
|
13
|
+
import { createLog, initLogFromOptions } from './lib/logging.js'
|
|
14
|
+
import { keyList, languageList } from './lib/options.js'
|
|
15
|
+
import { runTranslation } from './commands/translate.js'
|
|
16
16
|
import { registerSignalHandlers } from './shutdown.js'
|
|
17
|
+
import { runListModels } from './commands/list-models.js'
|
|
17
18
|
|
|
18
|
-
const __dirname = path.dirname(
|
|
19
|
+
const __dirname = path.dirname(
|
|
20
|
+
fileURLToPath(import.meta.url)
|
|
21
|
+
)
|
|
19
22
|
|
|
20
23
|
// Main function
|
|
21
24
|
export async function run() {
|
|
22
|
-
const log = createLog()
|
|
25
|
+
const log = createLog(process.argv.includes('--dev'))
|
|
23
26
|
|
|
24
27
|
const appState = {
|
|
28
|
+
__dirname: path.dirname(fileURLToPath(import.meta.url)),
|
|
25
29
|
lang: null, // The app language, for output display (unrelated to translator)
|
|
26
30
|
tmpDir: null,
|
|
27
31
|
filesToWrite: {}, // Map of file path => JSON data to write
|
|
@@ -53,50 +57,95 @@ export async function run() {
|
|
|
53
57
|
log.I(` ${v.name.padEnd(37)} ${v.description}`)
|
|
54
58
|
})
|
|
55
59
|
})
|
|
56
|
-
.requiredOption('-r, --reference-file <path>', 'Path to reference JSONC file (default language)')
|
|
57
|
-
.option('-rl, --reference-language <language>', `The reference file's language; overrides any 'referenceLanguage' config setting`)
|
|
58
|
-
.option('-p, --provider <name>', `AI provider to use for translations (anthropic, openai); overrides any 'provider' config setting`)
|
|
59
|
-
.option('-o, --output-dir <path>', 'Output directory for localized files')
|
|
60
|
-
.option('-l, --target-languages <list>', `Comma-separated list of language codes; overrides any 'targetLanguages' config setting`, value => languageList(value, log))
|
|
61
|
-
.option('-k, --keys <list>', 'Comma-separated list of keys to process', keyList)
|
|
62
|
-
.option('-j, --reference-var-name <var name>', `The exported variable in the reference file, e.g. export default = {...} you'd use 'default'`, 'default')
|
|
63
|
-
.option('-f, --force', 'Force regeneration of all translations', false)
|
|
64
|
-
.option('-rtw, --realtime-writes', 'Write updates to disk immediately, rather than on shutdown', false)
|
|
65
|
-
.option('-m, --app-context-message <message>', `Description of your app to give context. Passed with each translation request; overrides any 'appContextMessage' config setting`)
|
|
66
|
-
.option('-y, --tty', 'Use tty/simple renderer; useful for CI', false)
|
|
67
|
-
.option('-c, --config-file <path>', `Path to config file; defaults to <output dir>/${DEFAULT_CONFIG_FILENAME}`)
|
|
68
|
-
.option('-x, --max-retries <integer>', 'Maximum retries on failure', 3)
|
|
69
|
-
.option('-n, --normalize-output-filenames', `Normalizes output filenames (to all lower-case); overrides any 'normalizeOutputFilenames' in config setting`, false)
|
|
70
|
-
.option('-v, --verbose', `Enables verbose spew`, false)
|
|
71
|
-
.option('-d, --debug', `Enables debug spew`, false)
|
|
72
|
-
.option('-t, --trace', `Enables trace spew`, false)
|
|
73
|
-
.option('--context-prefix <value>', `String to be prefixed to all keys to search for additional context, which are passed along to the AI for context`)
|
|
74
|
-
.option('--context-suffix <value>', `String to be suffixed to all keys to search for additional context, which are passed along to the AI for context`)
|
|
75
|
-
.option('--look-for-context-data', `If specified, ALT will pass any context data specified in the reference file to the AI provider for translation. At least one of --contextPrefix or --contextSuffix must be specified`, false)
|
|
76
|
-
.hook('preAction', (thisCommand) => {
|
|
77
|
-
const opts = thisCommand.opts()
|
|
78
|
-
if (opts.lookForContextData && !(opts.contextPrefix?.length || opts.contextSuffix?.length)) {
|
|
79
|
-
thisCommand.error('--lookForContextData requires at least 1 of --contextPrefix or --contextSuffix be defined and non-empty')
|
|
80
|
-
}
|
|
81
|
-
})
|
|
82
|
-
.action(async (thisCommand) => {
|
|
83
|
-
}) // Dummy action() for preAction
|
|
84
60
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
61
|
+
const SHARED_OPTIONS = {
|
|
62
|
+
'provider': {
|
|
63
|
+
flags: '-p, --provider <name>',
|
|
64
|
+
description: `AI provider to use for translations (anthropic, openai); overrides any 'provider' config setting`
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const addSharedOptions = ({ required, notRequired, program }) => {
|
|
69
|
+
required = (required || []).map(k => ({ k, f: 'requiredOption' }))
|
|
70
|
+
notRequired = (notRequired || []).map(k => ({ k, f: 'option' }))
|
|
71
|
+
;[
|
|
72
|
+
...required,
|
|
73
|
+
...notRequired
|
|
74
|
+
].forEach(({ k, f }) => {
|
|
75
|
+
const so = SHARED_OPTIONS[k]
|
|
76
|
+
program[f](so.flags, so.description, so?.defaultValue)
|
|
91
77
|
})
|
|
78
|
+
}
|
|
92
79
|
|
|
93
|
-
|
|
80
|
+
const runCommand = async function() {
|
|
81
|
+
const command = this.name()
|
|
82
|
+
const options = this.opts()
|
|
94
83
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
84
|
+
if (options.logo) {
|
|
85
|
+
await printLogo({
|
|
86
|
+
fontsSrcDir: path.resolve(__dirname, '../assets/figlet-fonts/'),
|
|
87
|
+
tagline: p.description,
|
|
88
|
+
log
|
|
89
|
+
})
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
initLogFromOptions({ options, log })
|
|
93
|
+
|
|
94
|
+
switch (command) {
|
|
95
|
+
case 'translate':
|
|
96
|
+
await runTranslation({ appState, options, log })
|
|
97
|
+
break
|
|
98
|
+
|
|
99
|
+
case 'list-models':
|
|
100
|
+
await runListModels({ appState, options, log })
|
|
101
|
+
break
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
addSharedOptions({
|
|
106
|
+
notRequired: [ 'provider' ],
|
|
107
|
+
program: program
|
|
108
|
+
.command('translate')
|
|
109
|
+
.requiredOption('-r, --reference-file <path>', 'Path to reference file of source strings to be translated. This file can be in .js, .mjs, .json, or .jsonc formats and is presumed to be' +
|
|
110
|
+
' in the reference language specified by --reference-language')
|
|
111
|
+
.option('-c, --config-file <path>', `Path to config file; defaults to <output dir>/${DEFAULT_CONFIG_FILENAME}`)
|
|
112
|
+
.option('-rl, --reference-language <language>', `The reference file's language; overrides any 'referenceLanguage' config setting`)
|
|
113
|
+
.option('-o, --output-dir <path>', 'Output directory for localized files')
|
|
114
|
+
.option('-l, --target-languages <list>', `Comma-separated list of language codes; overrides any 'targetLanguages' config setting`, value => languageList(value, log))
|
|
115
|
+
.option('-k, --keys <list>', 'Comma-separated list of keys to process', keyList)
|
|
116
|
+
.option('-R, --reference-exported-var-name <var name>', `For .js or .mjs reference files, this will be the exported variable, e.g. for 'export default = {...}' you'd use 'default' here, or 'data' for 'export const data = { ... }'. For .json or .jsonc reference files, this value is ignored.`, 'default')
|
|
117
|
+
.option('-m, --app-context-message <message>', `Description of your app to give context. Passed with each translation request; overrides any 'appContextMessage' config setting`)
|
|
118
|
+
.option('-f, --force', 'Force regeneration of all translations', false)
|
|
119
|
+
.option('-rtw, --realtime-writes', 'Write updates to disk immediately, rather than on shutdown', false)
|
|
120
|
+
.option('-y, --tty', 'Use tty/simple renderer; useful for CI', false)
|
|
121
|
+
.option('-M, --model <name>', `LLM model name to use; defaults are: ${Object.keys(DEFAULT_LLM_MODELS).map(p => `for "${p}": "${DEFAULT_LLM_MODELS[p]}"`).join(', ')}; use the 'list-models' command to view all models`)
|
|
122
|
+
.option('-x, --max-retries <integer>', 'Maximum retries on failure', 3)
|
|
123
|
+
.option('-n, --normalize-output-filenames', `Normalizes output filenames (to all lower-case); overrides any 'normalizeOutputFilenames' in config setting`, false)
|
|
124
|
+
.option('-N, --no-logo', `Suppress logo printout`, true) // NB: maps to options.logo, not options.noLogo
|
|
125
|
+
.option('-cp, --context-prefix <value>', `String to be prefixed to all keys to search for additional context, which are passed along to the AI for context`)
|
|
126
|
+
.option('-cs, --context-suffix <value>', `String to be suffixed to all keys to search for additional context, which are passed along to the AI for context`)
|
|
127
|
+
.option('-L, --look-for-context-data', `If specified, ALT will pass any context data specified in the reference file to the AI provider for translation. At least one of --contextPrefix or --contextSuffix must be specified`, false)
|
|
128
|
+
.option('-v, --verbose', `Enables verbose spew`, false)
|
|
129
|
+
.option('-d, --debug', `Enables debug spew`, false)
|
|
130
|
+
.option('-t, --trace', `Enables trace spew`, false)
|
|
131
|
+
.option('--dev', `Enable dev mode, which prints stack traces with errors`, false)
|
|
132
|
+
.hook('preAction', cmd => {
|
|
133
|
+
const opts = cmd.opts()
|
|
134
|
+
if (opts.lookForContextData && !(opts.contextPrefix?.length || opts.contextSuffix?.length)) {
|
|
135
|
+
cmd.error('--lookForContextData requires at least 1 of --contextPrefix or --contextSuffix be defined and non-empty')
|
|
136
|
+
}
|
|
137
|
+
})
|
|
138
|
+
.action(runCommand)
|
|
99
139
|
})
|
|
140
|
+
|
|
141
|
+
addSharedOptions({
|
|
142
|
+
required: [ 'provider' ],
|
|
143
|
+
program: program
|
|
144
|
+
.command('list-models')
|
|
145
|
+
.action(runCommand)
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
program.parse(process.argv)
|
|
100
149
|
} catch (error) {
|
|
101
150
|
log.E(error)
|
|
102
151
|
}
|
|
@@ -2,11 +2,47 @@ export function name() {
|
|
|
2
2
|
return 'Anthropic'
|
|
3
3
|
}
|
|
4
4
|
|
|
5
|
-
export function
|
|
5
|
+
export async function listModels(apiKey) {
|
|
6
|
+
let allModels = []
|
|
7
|
+
let hasMore = true
|
|
8
|
+
let lastId = null
|
|
9
|
+
|
|
10
|
+
while (hasMore) {
|
|
11
|
+
const url = new URL('https://api.anthropic.com/v1/models')
|
|
12
|
+
if (lastId) {
|
|
13
|
+
url.searchParams.append('after', lastId)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const response = await fetch(url, {
|
|
17
|
+
method: 'GET',
|
|
18
|
+
headers: {
|
|
19
|
+
'x-api-key': apiKey,
|
|
20
|
+
'anthropic-version': '2023-06-01'
|
|
21
|
+
}
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
const result = await response.json()
|
|
25
|
+
|
|
26
|
+
if (result.data && result.data.length > 0) {
|
|
27
|
+
allModels = [
|
|
28
|
+
...allModels,
|
|
29
|
+
...result.data
|
|
30
|
+
]
|
|
31
|
+
lastId = result.last_id
|
|
32
|
+
hasMore = result.has_more
|
|
33
|
+
} else {
|
|
34
|
+
hasMore = false
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return allModels
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function getTranslationRequestDetails({ model, messages, apiKey, log }) {
|
|
6
42
|
return {
|
|
7
43
|
url: 'https://api.anthropic.com/v1/messages',
|
|
8
44
|
params: {
|
|
9
|
-
model
|
|
45
|
+
model,
|
|
10
46
|
max_tokens: 1024,
|
|
11
47
|
messages: messages.map(m => ({ role: 'user', content: m })),
|
|
12
48
|
},
|
package/src/providers/openai.mjs
CHANGED
|
@@ -2,11 +2,54 @@ export function name() {
|
|
|
2
2
|
return 'OpenAI'
|
|
3
3
|
}
|
|
4
4
|
|
|
5
|
-
export function
|
|
5
|
+
export async function listModels(apiKey) {
|
|
6
|
+
let allModels = []
|
|
7
|
+
let hasMore = true
|
|
8
|
+
let lastId = null
|
|
9
|
+
|
|
10
|
+
while (hasMore) {
|
|
11
|
+
const url = new URL('https://api.openai.com/v1/models')
|
|
12
|
+
if (lastId) {
|
|
13
|
+
url.searchParams.append('after', lastId)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const response = await fetch(url, {
|
|
17
|
+
method: 'GET',
|
|
18
|
+
headers: {
|
|
19
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
20
|
+
'Content-Type': 'application/json'
|
|
21
|
+
}
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
const result = await response.json()
|
|
25
|
+
|
|
26
|
+
if (result.data && result.data.length > 0) {
|
|
27
|
+
allModels = [
|
|
28
|
+
...allModels,
|
|
29
|
+
...result.data
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
// Check if OpenAI has pagination indicators
|
|
33
|
+
// OpenAI might use different pagination methods
|
|
34
|
+
if (result.has_more && result.last_id) {
|
|
35
|
+
lastId = result.last_id
|
|
36
|
+
hasMore = true
|
|
37
|
+
} else {
|
|
38
|
+
hasMore = false
|
|
39
|
+
}
|
|
40
|
+
} else {
|
|
41
|
+
hasMore = false
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return { data: allModels }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function getTranslationRequestDetails({ model, messages, apiKey, log }) {
|
|
6
49
|
return {
|
|
7
50
|
url: 'https://api.openai.com/v1/chat/completions',
|
|
8
51
|
params: {
|
|
9
|
-
model
|
|
52
|
+
model,
|
|
10
53
|
messages: messages.map((m, idx) => {
|
|
11
54
|
return {
|
|
12
55
|
role: idx === messages.length - 1 ? 'user' : 'system',
|
package/src/shutdown.js
CHANGED
/package/{bin.mjs → alt.mjs}
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|