@scoutello/i18n-magic 0.16.0 → 0.19.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 (64) hide show
  1. package/README.md +207 -19
  2. package/dist/cli.d.ts +2 -0
  3. package/dist/cli.d.ts.map +1 -0
  4. package/dist/cli.js +101 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/commands/check-missing.d.ts +1 -0
  7. package/dist/commands/check-missing.d.ts.map +1 -0
  8. package/dist/commands/check-missing.js +13 -0
  9. package/dist/commands/check-missing.js.map +1 -0
  10. package/dist/commands/clean.d.ts +1 -0
  11. package/dist/commands/clean.d.ts.map +1 -0
  12. package/dist/commands/clean.js +82 -0
  13. package/dist/commands/clean.js.map +1 -0
  14. package/dist/commands/create-pruned-namespace-automated.d.ts +20 -0
  15. package/dist/commands/create-pruned-namespace-automated.d.ts.map +1 -0
  16. package/dist/commands/create-pruned-namespace-automated.js +98 -0
  17. package/dist/commands/create-pruned-namespace-automated.js.map +1 -0
  18. package/dist/commands/create-pruned-namespace.d.ts +3 -0
  19. package/dist/commands/create-pruned-namespace.d.ts.map +1 -0
  20. package/dist/commands/create-pruned-namespace.js +123 -0
  21. package/dist/commands/create-pruned-namespace.js.map +1 -0
  22. package/dist/commands/replace.d.ts +1 -0
  23. package/dist/commands/replace.d.ts.map +1 -0
  24. package/dist/commands/replace.js +58 -0
  25. package/dist/commands/replace.js.map +1 -0
  26. package/dist/commands/scan.d.ts +1 -0
  27. package/dist/commands/scan.d.ts.map +1 -0
  28. package/dist/commands/scan.js +70 -0
  29. package/dist/commands/scan.js.map +1 -0
  30. package/dist/commands/sync-locales.d.ts +1 -0
  31. package/dist/commands/sync-locales.d.ts.map +1 -0
  32. package/dist/commands/sync-locales.js +78 -0
  33. package/dist/commands/sync-locales.js.map +1 -0
  34. package/dist/i18n-magic.cjs.development.js +348 -130
  35. package/dist/i18n-magic.cjs.development.js.map +1 -1
  36. package/dist/i18n-magic.cjs.production.min.js +1 -1
  37. package/dist/i18n-magic.cjs.production.min.js.map +1 -1
  38. package/dist/i18n-magic.esm.js +339 -130
  39. package/dist/i18n-magic.esm.js.map +1 -1
  40. package/dist/index.d.ts +11 -1
  41. package/dist/index.d.ts.map +1 -0
  42. package/dist/index.js +22 -9
  43. package/dist/index.js.map +1 -0
  44. package/dist/lib/languges.d.ts +1 -0
  45. package/dist/lib/languges.d.ts.map +1 -0
  46. package/dist/lib/languges.js +146 -0
  47. package/dist/lib/languges.js.map +1 -0
  48. package/dist/lib/types.d.ts +7 -1
  49. package/dist/lib/types.d.ts.map +1 -0
  50. package/dist/lib/types.js +3 -0
  51. package/dist/lib/types.js.map +1 -0
  52. package/dist/lib/utils.d.ts +20 -1
  53. package/dist/lib/utils.d.ts.map +1 -0
  54. package/dist/lib/utils.js +264 -0
  55. package/dist/lib/utils.js.map +1 -0
  56. package/package.json +38 -14
  57. package/src/cli.ts +117 -0
  58. package/src/commands/clean.ts +4 -1
  59. package/src/commands/create-pruned-namespace-automated.ts +165 -0
  60. package/src/commands/create-pruned-namespace.ts +168 -0
  61. package/src/commands/scan.ts +12 -0
  62. package/src/index.ts +23 -113
  63. package/src/lib/types.ts +7 -1
  64. package/src/lib/utils.ts +59 -2
package/src/cli.ts ADDED
@@ -0,0 +1,117 @@
1
+ import { Command } from "commander"
2
+ import dotenv from "dotenv"
3
+ import OpenAI from "openai"
4
+ import { checkMissing } from "./commands/check-missing"
5
+ import { removeUnusedKeys } from "./commands/clean"
6
+ import { createPrunedNamespace } from "./commands/create-pruned-namespace"
7
+ import { replaceTranslation } from "./commands/replace"
8
+ import { translateMissing } from "./commands/scan"
9
+ import { syncLocales } from "./commands/sync-locales"
10
+ import type { CommandType, Configuration } from "./lib/types"
11
+ import { loadConfig } from "./lib/utils"
12
+
13
+ const program = new Command()
14
+
15
+ program
16
+ .name("i18n-magic")
17
+ .description(
18
+ "CLI to help you manage your locales JSON with translations, replacements, etc. with OpenAI.",
19
+ )
20
+ .version("0.2.0")
21
+ .option("-c, --config <path>", "path to config file")
22
+ .option("-e, --env <path>", "path to .env file")
23
+
24
+ const commands: CommandType[] = [
25
+ {
26
+ name: "scan",
27
+ description:
28
+ "Scan for missing translations, get prompted for each, translate it to the other locales and save it to the JSON file.",
29
+ action: translateMissing,
30
+ },
31
+ {
32
+ name: "replace",
33
+ description:
34
+ "Replace a translation based on the key, and translate it to the other locales and save it to the JSON file.",
35
+ action: replaceTranslation,
36
+ },
37
+ {
38
+ name: "check-missing",
39
+ description:
40
+ "Check if there are any missing translations. Useful for a CI/CD pipeline or husky hook.",
41
+ action: checkMissing,
42
+ },
43
+ {
44
+ name: "sync",
45
+ description:
46
+ "Sync the translations from the default locale to the other locales. Useful for a CI/CD pipeline or husky hook.",
47
+ action: syncLocales,
48
+ },
49
+ {
50
+ name: "clean",
51
+ description:
52
+ "Remove unused translations from all locales. Useful for a CI/CD pipeline or husky hook.",
53
+ action: removeUnusedKeys,
54
+ },
55
+ {
56
+ name: "prune",
57
+ description: "Create a pruned namespace from the other namespaces.",
58
+ action: createPrunedNamespace,
59
+ },
60
+ ]
61
+
62
+ for (const command of commands) {
63
+ const cmd = program.command(command.name).description(command.description)
64
+
65
+ // Add key option to replace command
66
+ if (command.name === "replace") {
67
+ cmd
68
+ .option("-k, --key <key>", "translation key to replace")
69
+ .allowExcessArguments(true)
70
+ .argument("[key]", "translation key to replace")
71
+ }
72
+
73
+ cmd.action(async (arg, options) => {
74
+ const res = dotenv.config({
75
+ path: program.opts().env || ".env",
76
+ })
77
+
78
+ const config: Configuration = await loadConfig({
79
+ configPath: program.opts().config,
80
+ })
81
+
82
+ const isGemini = (config.model as string)?.includes("gemini")
83
+
84
+ // Get API key from environment or config
85
+ const openaiKey = res.parsed.OPENAI_API_KEY || config.OPENAI_API_KEY
86
+ const geminiKey = res.parsed.GEMINI_API_KEY || config.GEMINI_API_KEY
87
+
88
+ // Select appropriate key based on model type
89
+ const key = isGemini ? geminiKey : openaiKey
90
+
91
+ if (!key) {
92
+ const keyType = isGemini ? "GEMINI_API_KEY" : "OPENAI_API_KEY"
93
+ console.error(
94
+ `Please provide a${isGemini ? " Gemini" : "n OpenAI"} API key in your .env file or config, called ${keyType}.`,
95
+ )
96
+ process.exit(1)
97
+ }
98
+
99
+ const openai = new OpenAI({
100
+ apiKey: key,
101
+ ...(isGemini && {
102
+ baseURL: "https://generativelanguage.googleapis.com/v1beta/openai/",
103
+ }),
104
+ })
105
+
106
+ // For replace command, check for key in argument or option
107
+ if (command.name === "replace") {
108
+ // If key is provided as positional argument, use that first
109
+ const keyToUse = typeof arg === "string" ? arg : options.key
110
+ command.action({ ...config, openai }, keyToUse)
111
+ } else {
112
+ command.action({ ...config, openai })
113
+ }
114
+ })
115
+ }
116
+
117
+ program.parse(process.argv)
@@ -26,7 +26,10 @@ export const removeUnusedKeys = async (config: Configuration) => {
26
26
  })
27
27
 
28
28
  // Find all files to scan
29
- const files = await glob([...globPatterns, "!**/node_modules/**"])
29
+ const allPatterns = globPatterns.map((pattern) =>
30
+ typeof pattern === "string" ? pattern : pattern.pattern,
31
+ )
32
+ const files = await glob([...allPatterns, "!**/node_modules/**"])
30
33
 
31
34
  // Extract all translation keys from the codebase
32
35
  const extractedKeys = []
@@ -0,0 +1,165 @@
1
+ import glob from "fast-glob"
2
+ import { Parser } from "i18next-scanner"
3
+ import fs from "node:fs"
4
+ import type { Configuration } from "../lib/types"
5
+ import {
6
+ getPureKey,
7
+ loadLocalesFile,
8
+ removeDuplicatesFromArray,
9
+ writeLocalesFile,
10
+ } from "../lib/utils"
11
+
12
+ export interface PruneOptions {
13
+ sourceNamespace: string
14
+ newNamespace: string
15
+ globPatterns: string[]
16
+ }
17
+
18
+ export interface PruneResult {
19
+ locale: string
20
+ keyCount: number
21
+ success: boolean
22
+ error?: string
23
+ }
24
+
25
+ export interface PruneResponse {
26
+ success: boolean
27
+ message: string
28
+ keysCount: number
29
+ results?: PruneResult[]
30
+ }
31
+
32
+ export const createPrunedNamespaceAutomated = async (
33
+ config: Configuration,
34
+ options: PruneOptions,
35
+ ): Promise<PruneResponse> => {
36
+ const { namespaces, loadPath, savePath, locales, defaultNamespace } = config
37
+ const { sourceNamespace, newNamespace, globPatterns } = options
38
+
39
+ // Validate inputs
40
+ if (!namespaces.includes(sourceNamespace)) {
41
+ throw new Error(
42
+ `Source namespace '${sourceNamespace}' not found in configuration`,
43
+ )
44
+ }
45
+
46
+ if (namespaces.includes(newNamespace)) {
47
+ throw new Error(`Namespace '${newNamespace}' already exists`)
48
+ }
49
+
50
+ console.log(
51
+ `Creating pruned namespace '${newNamespace}' from '${sourceNamespace}'`,
52
+ )
53
+ console.log(`Using glob patterns: ${globPatterns.join(", ")}`)
54
+
55
+ // Extract keys from files matching the glob patterns
56
+ const parser = new Parser({
57
+ nsSeparator: false,
58
+ keySeparator: false,
59
+ })
60
+
61
+ const files = await glob([...globPatterns, "!**/node_modules/**"])
62
+ console.log(`Found ${files.length} files to scan`)
63
+
64
+ const extractedKeys = []
65
+
66
+ for (const file of files) {
67
+ const content = fs.readFileSync(file, "utf-8")
68
+ parser.parseFuncFromString(content, { list: ["t"] }, (key: string) => {
69
+ extractedKeys.push(key)
70
+ })
71
+ }
72
+
73
+ const uniqueExtractedKeys = removeDuplicatesFromArray(extractedKeys)
74
+ console.log(`Found ${uniqueExtractedKeys.length} unique translation keys`)
75
+
76
+ // Filter keys that belong to the source namespace
77
+ const relevantKeys = []
78
+
79
+ for (const key of uniqueExtractedKeys) {
80
+ const pureKey = getPureKey(
81
+ key,
82
+ sourceNamespace,
83
+ sourceNamespace === defaultNamespace,
84
+ )
85
+
86
+ if (pureKey) {
87
+ relevantKeys.push(pureKey)
88
+ }
89
+ }
90
+
91
+ console.log(
92
+ `Found ${relevantKeys.length} keys from namespace '${sourceNamespace}'`,
93
+ )
94
+
95
+ if (relevantKeys.length === 0) {
96
+ console.log("No relevant keys found. Exiting...")
97
+ return {
98
+ success: false,
99
+ message: "No relevant keys found",
100
+ keysCount: 0,
101
+ }
102
+ }
103
+
104
+ // Get translations from source namespace and create new namespace files
105
+ const results: PruneResult[] = []
106
+
107
+ for (const locale of locales) {
108
+ try {
109
+ // Load source namespace translations
110
+ const sourceTranslations = await loadLocalesFile(
111
+ loadPath,
112
+ locale,
113
+ sourceNamespace,
114
+ )
115
+
116
+ // Create new namespace with only the keys used in the glob pattern files
117
+ const newNamespaceTranslations: Record<string, string> = {}
118
+
119
+ for (const key of relevantKeys) {
120
+ if (sourceTranslations[key]) {
121
+ newNamespaceTranslations[key] = sourceTranslations[key]
122
+ }
123
+ }
124
+
125
+ // Write the new namespace file
126
+ await writeLocalesFile(
127
+ savePath,
128
+ locale,
129
+ newNamespace,
130
+ newNamespaceTranslations,
131
+ )
132
+
133
+ const keyCount = Object.keys(newNamespaceTranslations).length
134
+ console.log(
135
+ `Created pruned namespace '${newNamespace}' for locale '${locale}' with ${keyCount} keys`,
136
+ )
137
+
138
+ results.push({
139
+ locale,
140
+ keyCount,
141
+ success: true,
142
+ })
143
+ } catch (error) {
144
+ console.error(
145
+ `Error creating pruned namespace for locale '${locale}':`,
146
+ error,
147
+ )
148
+ results.push({
149
+ locale,
150
+ keyCount: 0,
151
+ success: false,
152
+ error: error instanceof Error ? error.message : String(error),
153
+ })
154
+ }
155
+ }
156
+
157
+ console.log(`✅ Successfully created pruned namespace '${newNamespace}'`)
158
+
159
+ return {
160
+ success: true,
161
+ message: `Created pruned namespace '${newNamespace}' with ${relevantKeys.length} keys`,
162
+ keysCount: relevantKeys.length,
163
+ results,
164
+ }
165
+ }
@@ -0,0 +1,168 @@
1
+ import glob from "fast-glob"
2
+ import { Parser } from "i18next-scanner"
3
+ import fs from "node:fs"
4
+ import prompts from "prompts"
5
+ import type { Configuration } from "../lib/types"
6
+ import {
7
+ getPureKey,
8
+ loadLocalesFile,
9
+ removeDuplicatesFromArray,
10
+ writeLocalesFile,
11
+ } from "../lib/utils"
12
+
13
+ export const createPrunedNamespace = async (config: Configuration) => {
14
+ const { namespaces, loadPath, savePath, locales, defaultNamespace } = config
15
+
16
+ // Step 1: Ask for source namespace
17
+ const sourceNamespaceResponse = await prompts({
18
+ type: "select",
19
+ name: "value",
20
+ message: "Select source namespace to create pruned version from:",
21
+ choices: namespaces.map((namespace) => ({
22
+ title: namespace,
23
+ value: namespace,
24
+ })),
25
+ onState: (state) => {
26
+ if (state.aborted) {
27
+ process.nextTick(() => {
28
+ process.exit(0)
29
+ })
30
+ }
31
+ },
32
+ })
33
+
34
+ const sourceNamespace = sourceNamespaceResponse.value
35
+
36
+ // Step 2: Ask for new namespace name
37
+ const newNamespaceResponse = await prompts({
38
+ type: "text",
39
+ name: "value",
40
+ message: "Enter the name for the new namespace:",
41
+ validate: (value) => {
42
+ if (!value) return "Namespace name cannot be empty"
43
+ if (namespaces.includes(value)) return "Namespace already exists"
44
+ return true
45
+ },
46
+ onState: (state) => {
47
+ if (state.aborted) {
48
+ process.nextTick(() => {
49
+ process.exit(0)
50
+ })
51
+ }
52
+ },
53
+ })
54
+
55
+ const newNamespace = newNamespaceResponse.value
56
+
57
+ // Step 3: Ask for glob patterns to find relevant keys
58
+ const allPatterns = config.globPatterns.map((pattern) =>
59
+ typeof pattern === "string" ? pattern : pattern.pattern,
60
+ )
61
+ const globPatternsResponse = await prompts({
62
+ type: "list",
63
+ name: "value",
64
+ message: "Enter glob patterns to find relevant keys (comma separated):",
65
+ initial: allPatterns.join(","),
66
+ separator: ",",
67
+ onState: (state) => {
68
+ if (state.aborted) {
69
+ process.nextTick(() => {
70
+ process.exit(0)
71
+ })
72
+ }
73
+ },
74
+ })
75
+
76
+ const selectedGlobPatterns = globPatternsResponse.value
77
+
78
+ console.log(
79
+ `Finding keys used in files matching: ${selectedGlobPatterns.join(", ")}`,
80
+ )
81
+
82
+ // Extract keys from files matching the glob patterns
83
+ const parser = new Parser({
84
+ nsSeparator: false,
85
+ keySeparator: false,
86
+ })
87
+
88
+ const files = await glob([...selectedGlobPatterns, "!**/node_modules/**"])
89
+ console.log(`Found ${files.length} files to scan`)
90
+
91
+ const extractedKeys = []
92
+
93
+ for (const file of files) {
94
+ const content = fs.readFileSync(file, "utf-8")
95
+ parser.parseFuncFromString(content, { list: ["t"] }, (key: string) => {
96
+ extractedKeys.push(key)
97
+ })
98
+ }
99
+
100
+ const uniqueExtractedKeys = removeDuplicatesFromArray(extractedKeys)
101
+ console.log(`Found ${uniqueExtractedKeys.length} unique translation keys`)
102
+
103
+ // Filter keys that belong to the source namespace
104
+ const relevantKeys = []
105
+
106
+ for (const key of uniqueExtractedKeys) {
107
+ const pureKey = getPureKey(
108
+ key,
109
+ sourceNamespace,
110
+ sourceNamespace === defaultNamespace,
111
+ )
112
+
113
+ if (pureKey) {
114
+ relevantKeys.push(pureKey)
115
+ }
116
+ }
117
+
118
+ console.log(
119
+ `Found ${relevantKeys.length} keys from namespace '${sourceNamespace}'`,
120
+ )
121
+
122
+ if (relevantKeys.length === 0) {
123
+ console.log("No relevant keys found. Exiting...")
124
+ return
125
+ }
126
+
127
+ // Get translations from source namespace and create new namespace files
128
+ for (const locale of locales) {
129
+ try {
130
+ // Load source namespace translations
131
+ const sourceTranslations = await loadLocalesFile(
132
+ loadPath,
133
+ locale,
134
+ sourceNamespace,
135
+ )
136
+
137
+ // Create new namespace with only the keys used in the glob pattern files
138
+ const newNamespaceTranslations: Record<string, string> = {}
139
+
140
+ for (const key of relevantKeys) {
141
+ if (sourceTranslations[key]) {
142
+ newNamespaceTranslations[key] = sourceTranslations[key]
143
+ }
144
+ }
145
+
146
+ // Write the new namespace file
147
+ await writeLocalesFile(
148
+ savePath,
149
+ locale,
150
+ newNamespace,
151
+ newNamespaceTranslations,
152
+ )
153
+
154
+ console.log(
155
+ `Created pruned namespace '${newNamespace}' for locale '${locale}' with ${
156
+ Object.keys(newNamespaceTranslations).length
157
+ } keys`,
158
+ )
159
+ } catch (error) {
160
+ console.error(
161
+ `Error creating pruned namespace for locale '${locale}':`,
162
+ error,
163
+ )
164
+ }
165
+ }
166
+
167
+ console.log(`✅ Successfully created pruned namespace '${newNamespace}'`)
168
+ }
@@ -1,3 +1,4 @@
1
+ import console from "console"
1
2
  import type { Configuration } from "../lib/types"
2
3
  import {
3
4
  checkAllKeysExist,
@@ -7,6 +8,7 @@ import {
7
8
  translateKey,
8
9
  writeLocalesFile,
9
10
  } from "../lib/utils"
11
+ import { removeUnusedKeys } from "./clean"
10
12
 
11
13
  export const translateMissing = async (config: Configuration) => {
12
14
  const {
@@ -18,8 +20,18 @@ export const translateMissing = async (config: Configuration) => {
18
20
  context,
19
21
  openai,
20
22
  disableTranslation,
23
+ autoClear,
21
24
  } = config
22
25
 
26
+ // Run clean command first if autoClear is enabled
27
+ if (autoClear) {
28
+ console.log("🧹 Auto-clearing unused translations before scanning...")
29
+ await removeUnusedKeys(config)
30
+ console.log(
31
+ "✅ Auto-clear completed. Now scanning for missing translations...\n",
32
+ )
33
+ }
34
+
23
35
  const newKeys = await getMissingKeys(config)
24
36
 
25
37
  if (newKeys.length === 0) {
package/src/index.ts CHANGED
@@ -1,113 +1,23 @@
1
- import { Command } from "commander"
2
- import dotenv from "dotenv"
3
- import OpenAI from "openai"
4
- import { checkMissing } from "./commands/check-missing"
5
- import { removeUnusedKeys } from "./commands/clean"
6
- import { replaceTranslation } from "./commands/replace"
7
- import { translateMissing } from "./commands/scan"
8
- import { syncLocales } from "./commands/sync-locales"
9
- import type { CommandType, Configuration } from "./lib/types"
10
- import { loadConfig } from "./lib/utils"
11
-
12
- // Only run CLI initialization when this file is executed directly
13
-
14
- const program = new Command()
15
-
16
- program
17
- .name("i18n-magic")
18
- .description(
19
- "CLI to help you manage your locales JSON with translations, replacements, etc. with OpenAI.",
20
- )
21
- .version("0.2.0")
22
- .option("-c, --config <path>", "path to config file")
23
- .option("-e, --env <path>", "path to .env file")
24
-
25
- const commands: CommandType[] = [
26
- {
27
- name: "scan",
28
- description:
29
- "Scan for missing translations, get prompted for each, translate it to the other locales and save it to the JSON file.",
30
- action: translateMissing,
31
- },
32
- {
33
- name: "replace",
34
- description:
35
- "Replace a translation based on the key, and translate it to the other locales and save it to the JSON file.",
36
- action: replaceTranslation,
37
- },
38
- {
39
- name: "check-missing",
40
- description:
41
- "Check if there are any missing translations. Useful for a CI/CD pipeline or husky hook.",
42
- action: checkMissing,
43
- },
44
- {
45
- name: "sync",
46
- description:
47
- "Sync the translations from the default locale to the other locales. Useful for a CI/CD pipeline or husky hook.",
48
- action: syncLocales,
49
- },
50
- {
51
- name: "clean",
52
- description:
53
- "Remove unused translations from all locales. Useful for a CI/CD pipeline or husky hook.",
54
- action: removeUnusedKeys,
55
- },
56
- ]
57
-
58
- for (const command of commands) {
59
- const cmd = program.command(command.name).description(command.description)
60
-
61
- // Add key option to replace command
62
- if (command.name === "replace") {
63
- cmd
64
- .option("-k, --key <key>", "translation key to replace")
65
- .allowExcessArguments(true)
66
- .argument("[key]", "translation key to replace")
67
- }
68
-
69
- cmd.action(async (arg, options) => {
70
- const res = dotenv.config({
71
- path: program.opts().env || ".env",
72
- })
73
-
74
- const config: Configuration = await loadConfig({
75
- configPath: program.opts().config,
76
- })
77
-
78
- const isGemini = (config.model as string)?.includes("gemini")
79
-
80
- // Get API key from environment or config
81
- const openaiKey = res.parsed.OPENAI_API_KEY || config.OPENAI_API_KEY
82
- const geminiKey = res.parsed.GEMINI_API_KEY || config.GEMINI_API_KEY
83
-
84
- // Select appropriate key based on model type
85
- const key = isGemini ? geminiKey : openaiKey
86
-
87
- if (!key) {
88
- const keyType = isGemini ? "GEMINI_API_KEY" : "OPENAI_API_KEY"
89
- console.error(
90
- `Please provide a${isGemini ? " Gemini" : "n OpenAI"} API key in your .env file or config, called ${keyType}.`,
91
- )
92
- process.exit(1)
93
- }
94
-
95
- const openai = new OpenAI({
96
- apiKey: key,
97
- ...(isGemini && {
98
- baseURL: "https://generativelanguage.googleapis.com/v1beta/openai/",
99
- }),
100
- })
101
-
102
- // For replace command, check for key in argument or option
103
- if (command.name === "replace") {
104
- // If key is provided as positional argument, use that first
105
- const keyToUse = typeof arg === "string" ? arg : options.key
106
- command.action({ ...config, openai }, keyToUse)
107
- } else {
108
- command.action({ ...config, openai })
109
- }
110
- })
111
- }
112
-
113
- program.parse(process.argv)
1
+ // Export command functions for programmatic usage
2
+ export { checkMissing } from "./commands/check-missing"
3
+ export { removeUnusedKeys } from "./commands/clean"
4
+ export { createPrunedNamespace } from "./commands/create-pruned-namespace"
5
+ export { createPrunedNamespaceAutomated } from "./commands/create-pruned-namespace-automated"
6
+ export { replaceTranslation } from "./commands/replace"
7
+ export { translateMissing } from "./commands/scan"
8
+ export { syncLocales } from "./commands/sync-locales"
9
+
10
+ // Export utility functions
11
+ export { loadConfig } from "./lib/utils"
12
+
13
+ // Export types
14
+ export type {
15
+ PruneOptions,
16
+ PruneResponse,
17
+ PruneResult,
18
+ } from "./commands/create-pruned-namespace-automated"
19
+ export type {
20
+ CommandType,
21
+ Configuration,
22
+ GlobPatternConfig,
23
+ } from "./lib/types"
package/src/lib/types.ts CHANGED
@@ -7,6 +7,11 @@ type Model =
7
7
  | "gemini-2.0-flash"
8
8
  | "gemini-2.0-flash-lite"
9
9
 
10
+ export interface GlobPatternConfig {
11
+ pattern: string
12
+ namespaces: string[]
13
+ }
14
+
10
15
  export interface Configuration {
11
16
  loadPath:
12
17
  | string
@@ -22,9 +27,10 @@ export interface Configuration {
22
27
  defaultNamespace: string
23
28
  namespaces: string[]
24
29
  locales: string[]
25
- globPatterns: string[]
30
+ globPatterns: (string | GlobPatternConfig)[]
26
31
  context?: string
27
32
  disableTranslation?: boolean
33
+ autoClear?: boolean
28
34
  OPENAI_API_KEY?: string
29
35
  GEMINI_API_KEY?: string
30
36
  model?: Model