@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.
Files changed (94) hide show
  1. package/README.md +54 -53
  2. package/localization/.localization.cache.json +3100 -908
  3. package/localization/aa.json +23 -16
  4. package/localization/af.json +19 -12
  5. package/localization/agq.json +23 -16
  6. package/localization/ak.json +23 -16
  7. package/localization/am.json +23 -16
  8. package/localization/ar.json +19 -12
  9. package/localization/as.json +23 -16
  10. package/localization/asa.json +23 -16
  11. package/localization/ast.json +16 -9
  12. package/localization/az.json +17 -10
  13. package/localization/ba.json +23 -16
  14. package/localization/bas.json +23 -16
  15. package/localization/be.json +23 -16
  16. package/localization/bem.json +23 -16
  17. package/localization/bez.json +22 -15
  18. package/localization/bg.json +18 -11
  19. package/localization/bm.json +17 -10
  20. package/localization/bn.json +20 -13
  21. package/localization/bo.json +23 -16
  22. package/localization/br.json +18 -11
  23. package/localization/brx.json +23 -16
  24. package/localization/bs.json +20 -13
  25. package/localization/byn.json +23 -16
  26. package/localization/ca.json +16 -9
  27. package/localization/ccp.json +23 -16
  28. package/localization/cd-RU.json +16 -9
  29. package/localization/ceb.json +21 -14
  30. package/localization/cgg.json +22 -15
  31. package/localization/chr.json +23 -16
  32. package/localization/co.json +22 -15
  33. package/localization/config.json +1 -1
  34. package/localization/cs.json +18 -11
  35. package/localization/cu-RU.json +23 -16
  36. package/localization/da.json +14 -7
  37. package/localization/de-AT.json +19 -12
  38. package/localization/de-CH.json +19 -12
  39. package/localization/de-DE.json +18 -11
  40. package/localization/dua.json +23 -16
  41. package/localization/dv.json +23 -16
  42. package/localization/dz.json +23 -16
  43. package/localization/ebu.json +23 -16
  44. package/localization/en.json +9 -2
  45. package/localization/es-ES.json +17 -10
  46. package/localization/es-MX.json +18 -11
  47. package/localization/et.json +20 -13
  48. package/localization/eu.json +20 -13
  49. package/localization/fr-CA.json +15 -8
  50. package/localization/fr-CH.json +15 -8
  51. package/localization/fr-FR.json +15 -8
  52. package/localization/gsw.json +20 -13
  53. package/localization/hi.json +19 -12
  54. package/localization/hr.json +18 -11
  55. package/localization/hy.json +21 -14
  56. package/localization/ja.json +18 -11
  57. package/localization/km.json +21 -14
  58. package/localization/ksf.json +23 -16
  59. package/localization/ku.json +22 -15
  60. package/localization/kw.json +23 -16
  61. package/localization/my.json +22 -15
  62. package/localization/nl.json +18 -11
  63. package/localization/prs.json +18 -11
  64. package/localization/reference.js +9 -1
  65. package/localization/ru.json +14 -7
  66. package/localization/sq.json +19 -12
  67. package/localization/swc.json +21 -14
  68. package/localization/th.json +15 -8
  69. package/localization/tzm-Latn-.json +23 -16
  70. package/localization/uk.json +14 -7
  71. package/localization/vi.json +17 -10
  72. package/localization/zh-Hans.json +17 -10
  73. package/localization/zh-Hant.json +18 -11
  74. package/package.json +4 -3
  75. package/src/commands/list-models.js +6 -0
  76. package/src/{translate.js → commands/translate.js} +124 -139
  77. package/src/{consts.js → lib/consts.js} +12 -0
  78. package/src/{io.js → lib/io.js} +1 -1
  79. package/src/{logging.js → lib/logging.js} +3 -3
  80. package/src/{options.js → lib/options.js} +1 -1
  81. package/src/lib/reference-loader.js +91 -0
  82. package/src/{utils.js → lib/utils.js} +15 -0
  83. package/src/localizer/localize.js +3 -4
  84. package/src/main.mjs +98 -49
  85. package/src/providers/anthropic.mjs +38 -2
  86. package/src/providers/openai.mjs +45 -2
  87. package/src/shutdown.js +1 -1
  88. /package/{bin.mjs → alt.mjs} +0 -0
  89. /package/src/{assert.js → lib/assert.js} +0 -0
  90. /package/src/{cache.js → lib/cache.js} +0 -0
  91. /package/src/{config.js → lib/config.js} +0 -0
  92. /package/src/{context-keys.js → lib/context-keys.js} +0 -0
  93. /package/src/{logo.js → lib/logo.js} +0 -0
  94. /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(fileURLToPath(import.meta.url))
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
- program
86
- .command('translate', { isDefault: true }) // This makes it the default command
87
- .action(async () => {
88
- const options = program.opts()
89
- initLogFromOptions({ options, log })
90
- await runTranslation({ appState, options, log })
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
- program.parse(process.argv)
80
+ const runCommand = async function() {
81
+ const command = this.name()
82
+ const options = this.opts()
94
83
 
95
- await printLogo({
96
- fontsSrcDir: path.resolve(__dirname, '../assets/figlet-fonts/'),
97
- tagline: p.description,
98
- log
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 getTranslationRequestDetails({ messages, apiKey, log }) {
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: 'claude-3-7-sonnet-20250219',
45
+ model,
10
46
  max_tokens: 1024,
11
47
  messages: messages.map(m => ({ role: 'user', content: m })),
12
48
  },
@@ -2,11 +2,54 @@ export function name() {
2
2
  return 'OpenAI'
3
3
  }
4
4
 
5
- export function getTranslationRequestDetails({ messages, apiKey, log }) {
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: 'gpt-4-turbo',
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
@@ -1,4 +1,4 @@
1
- import { rmDir, writeJsonFile } from './io.js'
1
+ import { rmDir, writeJsonFile } from './lib/io.js'
2
2
 
3
3
  export function shutdown(appState, kill) {
4
4
  const { log, errors, tmpDir, filesToWrite } = appState
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes