@i18n-micro/cli 0.0.0-stage → 2.0.1

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 (72) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +29 -2
  3. package/bin/i18n-micro.mjs +3 -0
  4. package/dist/chunks/check-CwSzATDI.mjs +175 -0
  5. package/dist/chunks/clean-Do9z6D5j.mjs +68 -0
  6. package/dist/chunks/clean-bQVjG_2M.mjs +1 -0
  7. package/dist/chunks/export-C2J-pagB.mjs +219 -0
  8. package/dist/chunks/export-usBaNIpp.mjs +1 -0
  9. package/dist/chunks/extract-CJ8UfrVJ.mjs +30 -0
  10. package/dist/chunks/format-6CtMgO-M.mjs +26 -0
  11. package/dist/chunks/http-whB7a5ye.mjs +100 -0
  12. package/dist/chunks/import-CnL8U6Za.mjs +99 -0
  13. package/dist/chunks/info-Lom_wHos.mjs +32 -0
  14. package/dist/chunks/json-BV_apCMu.mjs +439 -0
  15. package/dist/chunks/lint-DDGSQAXd.mjs +125 -0
  16. package/dist/chunks/nuxt-dKxG087V.mjs +366 -0
  17. package/dist/chunks/package-BejjFS6s.mjs +3 -0
  18. package/dist/chunks/project-DgWgr7pF.mjs +206 -0
  19. package/dist/chunks/protect-D1HIuxmJ.mjs +36 -0
  20. package/dist/chunks/pseudo-oXBaONgF.mjs +32 -0
  21. package/dist/chunks/rename-uJnBX86A.mjs +117 -0
  22. package/dist/chunks/runner-CsPPffV1.mjs +200 -0
  23. package/dist/chunks/search-Dj_Ynq_x.mjs +67 -0
  24. package/dist/chunks/source-Bx2lHSW7.mjs +744 -0
  25. package/dist/chunks/spec-DwveYr8X.mjs +507 -0
  26. package/dist/chunks/stats-BU-SmrVR.mjs +156 -0
  27. package/dist/chunks/sync-BY2EFEda.mjs +1 -0
  28. package/dist/chunks/sync-C0NVDabK.mjs +56 -0
  29. package/dist/chunks/sync-remote-y3ZATbF2.mjs +148 -0
  30. package/dist/chunks/text-to-i18n-DweavFLm.mjs +404 -0
  31. package/dist/chunks/translate-CmnmgAHX.mjs +543 -0
  32. package/dist/cli.mjs +4 -0
  33. package/dist/index.mjs +3 -0
  34. package/dist/src/cli.d.ts +1 -0
  35. package/dist/src/commands/check.d.ts +28 -0
  36. package/dist/src/commands/clean.d.ts +26 -0
  37. package/dist/src/commands/export.d.ts +10 -0
  38. package/dist/src/commands/extract.d.ts +6 -0
  39. package/dist/src/commands/format.d.ts +8 -0
  40. package/dist/src/commands/import.d.ts +8 -0
  41. package/dist/src/commands/info.d.ts +5 -0
  42. package/dist/src/commands/lint.d.ts +16 -0
  43. package/dist/src/commands/pseudo.d.ts +9 -0
  44. package/dist/src/commands/rename.d.ts +9 -0
  45. package/dist/src/commands/search.d.ts +20 -0
  46. package/dist/src/commands/stats.d.ts +20 -0
  47. package/dist/src/commands/sync-remote.d.ts +30 -0
  48. package/dist/src/commands/sync.d.ts +20 -0
  49. package/dist/src/commands/text-to-i18n.d.ts +10 -0
  50. package/dist/src/commands/translate.d.ts +14 -0
  51. package/dist/src/csv.d.ts +3 -0
  52. package/dist/src/errors.d.ts +22 -0
  53. package/dist/src/fs.d.ts +45 -0
  54. package/dist/src/glossary.d.ts +12 -0
  55. package/dist/src/help.d.ts +3 -0
  56. package/dist/src/http.d.ts +10 -0
  57. package/dist/src/index.d.ts +3 -0
  58. package/dist/src/json.d.ts +34 -0
  59. package/dist/src/lint-rules.d.ts +9 -0
  60. package/dist/src/log.d.ts +88 -0
  61. package/dist/src/nuxt.d.ts +66 -0
  62. package/dist/src/po.d.ts +14 -0
  63. package/dist/src/project.d.ts +64 -0
  64. package/dist/src/runner.d.ts +6 -0
  65. package/dist/src/source.d.ts +109 -0
  66. package/dist/src/spec.d.ts +57 -0
  67. package/dist/src/text-to-i18n.d.ts +46 -0
  68. package/dist/src/translate/ai.d.ts +2 -0
  69. package/dist/src/translate/drivers.d.ts +26 -0
  70. package/dist/src/translate/engine.d.ts +33 -0
  71. package/dist/src/translate/protect.d.ts +11 -0
  72. package/package.json +71 -3
@@ -0,0 +1,9 @@
1
+ export interface RenameArgs {
2
+ cwd?: string;
3
+ translationDir?: string;
4
+ from: string;
5
+ to: string;
6
+ ignore?: string;
7
+ dryRun?: boolean;
8
+ }
9
+ export default function run(args: RenameArgs): Promise<void>;
@@ -0,0 +1,20 @@
1
+ export interface SearchArgs {
2
+ cwd?: string;
3
+ translationDir?: string;
4
+ query?: string;
5
+ caseSensitive?: boolean;
6
+ onlyUnused?: boolean;
7
+ scope: 'global' | 'pages' | 'all';
8
+ limit?: number;
9
+ ignore?: string;
10
+ json?: boolean;
11
+ }
12
+ export interface SearchMatch {
13
+ scope: string;
14
+ key: string;
15
+ translations: Record<string, string | null>;
16
+ missingLocales: string[];
17
+ isUsed: boolean;
18
+ usages: string[];
19
+ }
20
+ export default function run(args: SearchArgs): Promise<void>;
@@ -0,0 +1,20 @@
1
+ export interface StatsArgs {
2
+ cwd?: string;
3
+ translationDir?: string;
4
+ html?: string;
5
+ json?: boolean;
6
+ baseRef?: string;
7
+ }
8
+ export interface LocaleCompletion {
9
+ code: string;
10
+ totalKeys: number;
11
+ translatedKeys: number;
12
+ completion: number;
13
+ }
14
+ export interface LocaleDelta {
15
+ code: string;
16
+ completionDelta: number;
17
+ translatedKeysDelta: number;
18
+ totalKeysDelta: number;
19
+ }
20
+ export default function run(args: StatsArgs): Promise<void>;
@@ -0,0 +1,30 @@
1
+ import { JsonObject, JsonValue } from '../json.js';
2
+ import { Project } from '../project.js';
3
+ export interface SyncRemoteArgs {
4
+ cwd?: string;
5
+ translationDir?: string;
6
+ pull?: boolean;
7
+ push?: boolean;
8
+ force?: boolean;
9
+ dryRun?: boolean;
10
+ }
11
+ export interface RemoteConfig {
12
+ type?: string;
13
+ url?: string;
14
+ path?: string;
15
+ token?: string;
16
+ auth?: {
17
+ username?: string;
18
+ password?: string;
19
+ };
20
+ }
21
+ export type LocaleTranslations = Record<string, JsonObject>;
22
+ interface PullPlan {
23
+ added: string[];
24
+ updated: string[];
25
+ conflicts: string[];
26
+ apply: Array<[code: string, key: string, value: JsonValue]>;
27
+ }
28
+ export declare function planPull(project: Project, remote: LocaleTranslations, force: boolean): PullPlan;
29
+ export default function run(args: SyncRemoteArgs): Promise<void>;
30
+ export {};
@@ -0,0 +1,20 @@
1
+ import { LocaleSet } from '../project.js';
2
+ export interface SyncArgs {
3
+ cwd?: string;
4
+ translationDir?: string;
5
+ ignore?: string;
6
+ dryRun?: boolean;
7
+ force?: boolean;
8
+ }
9
+ export interface ExtraKey {
10
+ scope: string;
11
+ key: string;
12
+ }
13
+ /** Keys of `locale` the default locale cannot answer, compared by path: `"a.b"` and `a: { b }` are the same key. */
14
+ export declare function extraKeys(reference: LocaleSet, locale: LocaleSet): ExtraKey[];
15
+ /**
16
+ * Removes what the default locale does not have. Missing keys stay missing: `''` would hide
17
+ * them from `fallbackLocale` and from `check`. A delete-only command, so it has the brakes of
18
+ * `clean`, and one more: a reference that is empty proves nothing.
19
+ */
20
+ export default function run(args: SyncArgs): Promise<void>;
@@ -0,0 +1,10 @@
1
+ export interface TextToI18nArgs {
2
+ cwd?: string;
3
+ translationFile?: string;
4
+ path?: string;
5
+ context?: string;
6
+ ignore?: string;
7
+ dryRun?: boolean;
8
+ interactive?: boolean;
9
+ }
10
+ export default function run(args: TextToI18nArgs): Promise<void>;
@@ -0,0 +1,14 @@
1
+ export interface TranslateArgs {
2
+ cwd?: string;
3
+ translationDir?: string;
4
+ service?: string;
5
+ token?: string;
6
+ options?: string;
7
+ locale?: string;
8
+ replace?: boolean;
9
+ dryRun?: boolean;
10
+ chunkSize?: number;
11
+ pluralSeparator: string;
12
+ glossaryFile?: string;
13
+ }
14
+ export default function run(args: TranslateArgs): Promise<void>;
@@ -0,0 +1,3 @@
1
+ export declare const stringifyCsv: (rows: string[][]) => string;
2
+ /** A quote out of place makes the rest of the file mean something else, so it stops the read. */
3
+ export declare function parseCsv(content: string, name?: string): string[][];
@@ -0,0 +1,22 @@
1
+ export interface CliErrorOptions {
2
+ code: string;
3
+ hints?: readonly string[];
4
+ details?: Record<string, string>;
5
+ cause?: unknown;
6
+ }
7
+ /** An expected failure: printed as message, details and hints, never as a stack trace. */
8
+ export declare class CliError extends Error {
9
+ readonly code: string;
10
+ readonly hints: readonly string[];
11
+ readonly details: Readonly<Record<string, string>>;
12
+ constructor(message: string, options: CliErrorOptions);
13
+ }
14
+ export declare const usageError: (message: string, hints?: readonly string[], details?: Record<string, string>) => CliError;
15
+ export declare const validationError: (message: string, hints?: readonly string[], details?: Record<string, string>) => CliError;
16
+ export declare const notFoundError: (message: string, hints?: readonly string[], details?: Record<string, string>) => CliError;
17
+ export declare const commandFailed: (message: string, hints?: readonly string[], details?: Record<string, string>) => CliError;
18
+ export declare const remoteConfigError: (message: string, hints?: readonly string[], details?: Record<string, string>) => CliError;
19
+ /** The command worked and found something wrong: exit code 1, not a failure of the tool. */
20
+ export declare const findingsError: (message: string, hints?: readonly string[], details?: Record<string, string>) => CliError;
21
+ /** nuxi's look: a red ` ERROR ` badge on a terminal, the clack error symbol in a log; hints after an arrow. */
22
+ export declare function reportError(error: unknown, debug: boolean): void;
@@ -0,0 +1,45 @@
1
+ import { JsonObject } from './json.js';
2
+ /** `path` from `from`, as a report, a key or a glob reads it: `/` on every platform. */
3
+ export declare const relativePath: (from: string, path: string) => string;
4
+ /** Whether `path` is `dir` or below it. `relative` gives an absolute path across Windows drives. */
5
+ export declare function isUnder(dir: string, path: string): boolean;
6
+ /** Every file under `dir` that `accept` wants, sorted, symlinks followed. A missing directory is an empty result. */
7
+ export declare function walkFiles(dir: string, accept?: (path: string) => boolean, skipDir?: (path: string) => boolean): string[];
8
+ export interface WriteOptions {
9
+ /** The old content does not matter: it is replaced, not edited. */
10
+ overwrite?: boolean;
11
+ }
12
+ /**
13
+ * Write a set of files all-or-nothing.
14
+ *
15
+ * Everything lands in temp files first, so a full disk, a bad path or a read-only file fails
16
+ * before the first real file changes. A rename that fails later puts back what the earlier ones
17
+ * replaced. Only killing the process can leave a mix: of whole files between two renames, and
18
+ * a cut one in a hard-linked target, see `commit`. The next save clears its temp files.
19
+ */
20
+ export declare function writeFilesAtomic(files: Array<[path: string, content: string | Buffer]>, options?: WriteOptions): void;
21
+ export declare const writeFileAtomic: (path: string, content: string | Buffer) => void;
22
+ export declare const stringifyJson: (value: unknown, indent?: number | string) => string;
23
+ /** JSON text has no raw newline in a string, so every one of them is a line ending. */
24
+ export declare const matchLineEnding: (previous: string | undefined, text: string) => string;
25
+ /** Saved the way the file was written, so an edit shows up as an edit and not as a whole-file diff. */
26
+ export declare function stringifyLike(previous: string | undefined, value: unknown): string;
27
+ /** Text that survives a save. Bytes that are not UTF-8 would come back as replacement characters. */
28
+ export declare function readUtf8(path: string): string;
29
+ /** For what is only read: a stray byte in a data file or a locale file costs nothing until a save. */
30
+ export declare const readLoose: (path: string) => string;
31
+ /** A BOM comes from Windows editors and Excel; `JSON.parse` stops at it. */
32
+ export declare const readText: (path: string) => string;
33
+ /**
34
+ * Read a JSON object strictly.
35
+ *
36
+ * A broken file must stop the command: treating it as empty is how the next save would
37
+ * replace somebody's translations with `{}`.
38
+ */
39
+ export declare function readJsonObject(path: string, options?: {
40
+ private?: boolean;
41
+ }): JsonObject;
42
+ export declare const readJsonIfExists: (path: string) => JsonObject;
43
+ /** `tests/**`, `*.spec.ts`. A glob without a slash matches at any depth. */
44
+ export declare function globToRegExp(glob: string): RegExp;
45
+ export declare const splitList: (value: string | undefined) => string[];
@@ -0,0 +1,12 @@
1
+ export interface GlossaryEntry {
2
+ source: string;
3
+ target: string;
4
+ from?: string;
5
+ to?: string;
6
+ }
7
+ export interface Glossary {
8
+ entries: GlossaryEntry[];
9
+ }
10
+ export declare function loadGlossary(path: string): Glossary;
11
+ /** Terms for one language pair, as the lines a translation prompt quotes. At most 100: a prompt has a budget. */
12
+ export declare function glossaryContext(glossary: Glossary, from: string, to: string): string | undefined;
@@ -0,0 +1,3 @@
1
+ import { CommandSpec } from './spec.js';
2
+ export declare function rootUsage(version: string): string;
3
+ export declare function commandUsage(name: string, spec: CommandSpec, version: string): string;
@@ -0,0 +1,10 @@
1
+ export interface RequestOptions extends Omit<RequestInit, 'signal'> {
2
+ timeoutMs?: number;
3
+ retries?: number;
4
+ retryDelayMs?: number;
5
+ }
6
+ export declare const request: (url: string, options?: RequestOptions) => Promise<Response>;
7
+ export declare function requestJson<T>(url: string, options?: RequestOptions): Promise<T>;
8
+ export declare const jsonBody: (value: unknown) => Pick<RequestInit, "body"> & {
9
+ headers: Record<string, string>;
10
+ };
@@ -0,0 +1,3 @@
1
+ export { runCli } from './runner.js';
2
+ export { COMMAND_GROUPS, commands } from './spec.js';
3
+ export type { ArgSpec, CommandGroup, CommandSpec } from './spec.js';
@@ -0,0 +1,34 @@
1
+ export type JsonValue = string | number | boolean | null | JsonObject | JsonValue[];
2
+ export interface JsonObject {
3
+ [key: string]: JsonValue;
4
+ }
5
+ export declare const isObject: (value: unknown) => value is JsonObject;
6
+ /** A lookup by a name from outside: `constructor` is not an entry of a table. */
7
+ export declare const ownValue: <T>(table: Record<string, T>, name: string) => T | undefined;
8
+ /**
9
+ * `{ a: { b: 'x' } }` -> `{ 'a.b': 'x' }`. Arrays and empty objects are leaves. No prototype:
10
+ * callers look keys up in the result, and `constructor` is a key like any other.
11
+ */
12
+ export declare function flatten(tree: JsonObject, prefix?: string, out?: Record<string, JsonValue>): Record<string, JsonValue>;
13
+ /** Only the string leaves: the part of a tree that is text to translate. */
14
+ export declare function flattenStrings(tree: JsonObject): Record<string, string>;
15
+ export declare function getPath(tree: JsonObject, path: string): JsonValue | undefined;
16
+ /**
17
+ * What writing `path` would destroy: a text above it, or the keys below it. Every write that
18
+ * comes from outside the file (import, pull, translation, generated key) asks this first.
19
+ */
20
+ export declare function pathConflict(tree: JsonObject, path: string): string | null;
21
+ /** What can go between quotes in a template and a script, and be stored. */
22
+ export declare const isWritableKey: (key: string) => boolean;
23
+ export declare function setPath(tree: JsonObject, path: string, value: JsonValue): void;
24
+ /** Remove one leaf and the parents it leaves empty. Everything else stays exactly as written. */
25
+ export declare function deletePath(tree: JsonObject, path: string): boolean;
26
+ /** The first key an object repeats. `JSON.parse` keeps the last and says nothing, and a save would lose the other. */
27
+ export declare function findDuplicateKey(text: string): string | null;
28
+ /** The merge and the writers skip this key, so it would vanish on a save. */
29
+ export declare function hasProtoKey(value: JsonValue): boolean;
30
+ /** The path of an integer `JSON.parse` could not hold exactly. */
31
+ export declare function findUnsafeInteger(value: JsonValue, path?: string): string | null;
32
+ /** `Map.groupBy`, which Node 20 does not have. */
33
+ export declare function groupBy<T, K>(items: T[], keyOf: (item: T) => K): Map<K, T[]>;
34
+ export declare function sortKeys(value: JsonValue): JsonValue;
@@ -0,0 +1,9 @@
1
+ import { LintRuleName } from './spec.js';
2
+ export interface LintRule {
3
+ name: string;
4
+ description: string;
5
+ isValid: (value: string) => boolean;
6
+ fix?: (value: string) => string;
7
+ }
8
+ /** Rules for what is wrong in any language. Whether a text ends in a period is not one of them. */
9
+ export declare const lintRules: Record<LintRuleName, Pick<LintRule, 'isValid' | 'fix'>>;
@@ -0,0 +1,88 @@
1
+ /** Anything printed from now on shows `***` instead. Shorter than a real key, it would only garble the text. */
2
+ export declare function addSecret(value: string | undefined): void;
3
+ /**
4
+ * A URL up to its query, split around the last `@` there: one in the query is no part of the address.
5
+ * Not a `.*` regex: that stops at a line end and leaves the password in the text.
6
+ */
7
+ export declare function urlParts(url: string): {
8
+ scheme: string;
9
+ credentials?: string;
10
+ host: string;
11
+ };
12
+ /** A URL somebody typed: what is after its path and before its host can be a key, whatever a server echoes back. */
13
+ export declare function addUrlSecrets(url: string | undefined): void;
14
+ /** A live terminal: a pipe or a CI log gets no header, spinner or timing, so its text stays the same run after run. */
15
+ export declare const isInteractive: (stream?: NodeJS.WriteStream) => boolean;
16
+ /** The symbols of @clack/prompts, which nuxi prints its log lines with. */
17
+ export declare const symbols: {
18
+ info: string;
19
+ success: string;
20
+ warn: string;
21
+ error: string;
22
+ step: string;
23
+ arrow: string;
24
+ frames: string[];
25
+ };
26
+ declare const SGR: {
27
+ readonly bold: readonly [1, 22];
28
+ readonly dim: readonly [2, 22];
29
+ readonly underline: readonly [4, 24];
30
+ readonly red: readonly [31, 39];
31
+ readonly green: readonly [32, 39];
32
+ readonly yellow: readonly [33, 39];
33
+ readonly blue: readonly [34, 39];
34
+ readonly magenta: readonly [35, 39];
35
+ readonly cyan: readonly [36, 39];
36
+ readonly gray: readonly [90, 39];
37
+ readonly black: readonly [30, 39];
38
+ readonly bgRed: readonly [41, 49];
39
+ readonly bgYellow: readonly [43, 49];
40
+ };
41
+ export type Style = keyof typeof SGR;
42
+ /** Always styled: the writer strips it again for a stream without colors. */
43
+ export declare const style: (names: Style | Style[], text: string) => string;
44
+ /** On stderr, and on a live terminal only: stdout stays clean for `--json` and pipes. */
45
+ export declare const spinner: {
46
+ start(text: string): void;
47
+ update(text: string): void;
48
+ /** Wipes the frame; the next tick draws it again under what was printed. */
49
+ clear(): void;
50
+ stop(): void;
51
+ };
52
+ /** `4.2ms`, `420ms`, `4.20s`, `42.0s`, `4m 02s`: the precision nuxi gives its timings. */
53
+ export declare function formatDuration(ms: number): string;
54
+ /** The word for a count: `pick(1, 'was', 'were')`. */
55
+ export declare const pick: (count: number, one: string, many: string) => string;
56
+ /** A count and its noun, in agreement: `1 key`, `2 keys`. */
57
+ export declare const plural: (count: number, one: string, many?: string) => string;
58
+ export declare const log: {
59
+ setSilent(value: boolean): void;
60
+ /** A bare line, for text that is part of a block. */
61
+ info(message: string): void;
62
+ /** A line of its own: what the command is doing or found. */
63
+ note(message: string): void;
64
+ success(message: string): void;
65
+ warn(message: string): void;
66
+ /** A blank line when a group or a table came last, once. */
67
+ gap(): string;
68
+ error(message: string): void;
69
+ /** Stderr as is: the error report builds its own lines. */
70
+ raw(message: string): void;
71
+ /** Machine-readable output: not muted by `--logLevel silent`. */
72
+ json(value: unknown): void;
73
+ section(title: string): void;
74
+ rows(rows: Array<[label: string, value: string | number]>): void;
75
+ /** A titled group with its count; cells of a row are aligned as a table. */
76
+ list(title: string, items: Array<string | string[]>): void;
77
+ };
78
+ /**
79
+ * One line of input. Closed stdin (CI, a pipe that ended) answers `null`, never hangs.
80
+ *
81
+ * A single reader for the whole run: readline buffers ahead, so a second one would miss
82
+ * lines a pipe already delivered.
83
+ */
84
+ export declare function ask(question: string): Promise<string | null>;
85
+ export declare function closePrompts(): void;
86
+ export declare function confirm(question: string): Promise<boolean>;
87
+ export declare function choose(question: string, options: string[]): Promise<string | null>;
88
+ export {};
@@ -0,0 +1,66 @@
1
+ export interface LocaleEntry {
2
+ code: string;
3
+ }
4
+ export interface ProjectConfig {
5
+ rootDir: string;
6
+ /** Where `pages/`, `components/` and friends live: `app/` on Nuxt 4. */
7
+ srcDir: string;
8
+ /** The project's own translation directory: the only one the CLI writes to. */
9
+ translationDir: string;
10
+ /** Lowest priority first, the project's own directory last. */
11
+ translationDirs: string[];
12
+ /** The ones the module reads top-level `{locale}.json` from, never `pages/`. */
13
+ additionalDirs: string[];
14
+ /** Every layer's root, the project's included. */
15
+ layerRoots: string[];
16
+ /** `dir.pages` of every layer. */
17
+ pagesDirs: string[];
18
+ /** Source folders outside the root that aliases and auto-imports reach into. */
19
+ extraSourceDirs: string[];
20
+ defaultLocale: string;
21
+ /** `DEFAULT_LOCALE` in the shell chose another locale than nuxt.config. */
22
+ defaultLocaleOverridden: boolean;
23
+ locales: LocaleEntry[];
24
+ disablePageLocales: boolean;
25
+ }
26
+ interface I18nOptions {
27
+ locales?: Array<string | (LocaleEntry & {
28
+ disabled?: boolean;
29
+ })>;
30
+ defaultLocale?: string;
31
+ translationDir?: string;
32
+ disablePageLocales?: boolean;
33
+ additionalTranslationDirs?: Array<string | {
34
+ path?: string;
35
+ }>;
36
+ }
37
+ interface LayerConfig {
38
+ rootDir?: string;
39
+ srcDir?: string;
40
+ dir?: {
41
+ pages?: string;
42
+ };
43
+ }
44
+ interface ResolvedNuxtConfig extends LayerConfig {
45
+ i18n?: I18nOptions;
46
+ modules?: unknown[];
47
+ alias?: Record<string, string>;
48
+ components?: unknown;
49
+ imports?: {
50
+ dirs?: unknown;
51
+ };
52
+ _layers?: ReadonlyArray<{
53
+ config?: LayerConfig;
54
+ }>;
55
+ }
56
+ export declare function findNuxtRoot(startDir: string): string | null;
57
+ /** A folder outside the root is still this app's code when an alias or auto-import points at it. */
58
+ export declare function extraSourceDirsOf(config: ResolvedNuxtConfig, rootDir: string): string[];
59
+ /**
60
+ * Resolved i18n configuration of the project at or above `cwd`.
61
+ *
62
+ * `loadNuxtConfig`, not `loadNuxt`: the CLI needs options and layers, and booting Nuxt runs
63
+ * every module of the app just to read them.
64
+ */
65
+ export declare function loadProjectConfig(cwd: string): Promise<ProjectConfig>;
66
+ export {};
@@ -0,0 +1,14 @@
1
+ /** The small part of gettext the CLI exchanges: a flat catalog of msgid -> msgstr, with msgctxt as a key prefix. */
2
+ export interface PoMessage {
3
+ key: string;
4
+ value: string;
5
+ /** The default-locale text: what a translator works from. */
6
+ source?: string;
7
+ }
8
+ export declare function compilePo(messages: PoMessage[]): string;
9
+ export interface PoCatalog {
10
+ messages: Record<string, string>;
11
+ /** Marked fuzzy: a guess nobody confirmed, not a translation. A translator's work all the same. */
12
+ fuzzy: Record<string, string>;
13
+ }
14
+ export declare function parsePo(content: string): PoCatalog;
@@ -0,0 +1,64 @@
1
+ import { JsonObject, JsonValue } from './json.js';
2
+ import { ProjectConfig } from './nuxt.js';
3
+ /** Not a word: a page may be called `global`. */
4
+ export declare const GLOBAL_SCOPE = "";
5
+ export interface LocaleLayer {
6
+ global: JsonObject;
7
+ pages: Record<string, JsonObject>;
8
+ }
9
+ /** `de.json` or `pages/about/de.json`: the one place a scope turns into a path. */
10
+ export declare const relativeFile: (code: string, scope: string, extension?: string) => string;
11
+ export declare class LocaleSet {
12
+ readonly inherited: LocaleLayer;
13
+ readonly own: LocaleLayer;
14
+ global: JsonObject;
15
+ pages: Record<string, JsonObject>;
16
+ modified: boolean;
17
+ constructor(merged: LocaleLayer, inherited: LocaleLayer, own: LocaleLayer);
18
+ get pageScopes(): string[];
19
+ /** The global scope first, then every page. */
20
+ get scopes(): string[];
21
+ tree(scope: string): JsonObject;
22
+ /** The project's file as it was read. */
23
+ ownTree(scope: string): JsonObject;
24
+ inheritedTree(scope: string): JsonObject;
25
+ /** The project's file as it stands now: edits in, layers out. */
26
+ fileTree(scope: string): JsonObject;
27
+ replaceTree(scope: string, tree: JsonObject): void;
28
+ strings(scope?: string): Record<string, string>;
29
+ leaves(scope?: string): Record<string, JsonValue>;
30
+ get(key: string, scope?: string): JsonValue | undefined;
31
+ /** What the app reads on `scope`: the page's value, then the global one. */
32
+ resolve(key: string, scope: string): JsonValue | undefined;
33
+ /** Writes `value`, or returns the key that blocks it: a write never destroys another value. */
34
+ set(key: string, value: JsonValue, scope?: string): string | null;
35
+ delete(key: string, scope?: string): void;
36
+ }
37
+ export interface SaveOptions {
38
+ /** Force an indentation; by default each file keeps its own. */
39
+ indent?: number;
40
+ /** Rewrite files whose content did not change: `format`. */
41
+ rewrite?: boolean;
42
+ sort?: boolean;
43
+ }
44
+ export declare class Project {
45
+ readonly config: ProjectConfig;
46
+ private readonly locales;
47
+ private constructor();
48
+ static load(options: {
49
+ cwd?: string;
50
+ translationDir?: string;
51
+ }): Promise<Project>;
52
+ get codes(): string[];
53
+ /** Locales to fill in from the default one. */
54
+ get targetCodes(): string[];
55
+ /** The comma list of a `--locale` flag, or every target locale. */
56
+ selectLocales(list: string[]): string[];
57
+ locale(code: string): LocaleSet;
58
+ get reference(): LocaleSet;
59
+ private filesOf;
60
+ /** The writes a save would make, for a caller that has other files to change in the same step. */
61
+ pendingFiles(options?: SaveOptions): Array<[string, string]>;
62
+ /** Write modified locales in one step, see `writeFilesAtomic`. Returns the paths written. */
63
+ save(options?: SaveOptions): string[];
64
+ }
@@ -0,0 +1,6 @@
1
+ import { ArgsDef, CommandDef } from 'citty';
2
+ export declare const packageName: string;
3
+ export declare const version: string;
4
+ export declare const main: CommandDef<ArgsDef>;
5
+ /** Run the CLI. Failures set `process.exitCode`; nothing here throws. */
6
+ export declare function runCli(rawArgs: string[]): Promise<void>;
@@ -0,0 +1,109 @@
1
+ import { ProjectConfig } from './nuxt.js';
2
+ export declare const NODE_TYPES: {
3
+ ELEMENT: number;
4
+ TEXT: number;
5
+ COMMENT: number;
6
+ INTERPOLATION: number;
7
+ ATTRIBUTE: number;
8
+ DIRECTIVE: number;
9
+ };
10
+ export declare const isCodeFile: (path: string) => boolean;
11
+ export interface KeyRef {
12
+ key: string;
13
+ file: string;
14
+ line: number;
15
+ /** The key's text in the file, without quotes. -1 when it came from text no parser read. */
16
+ start: number;
17
+ end: number;
18
+ /** `$has` asks whether a key exists: it does not need one to exist. */
19
+ guard: boolean;
20
+ /** Text after `$t(key, params, 'text')`. */
21
+ defaultText?: string;
22
+ /** Inside `<i18n-group>` the code holds only the part after this. */
23
+ keyPrefix?: string;
24
+ }
25
+ export interface DynamicKey {
26
+ file: string;
27
+ line: number;
28
+ expression: string;
29
+ /** Static text before the first interpolation: every key under it may be in use. */
30
+ prefix: string;
31
+ /** `<i18n-group prefix>`: a known prefix, not a key computed at runtime. */
32
+ group?: boolean;
33
+ /** A function that may not translate at all. Build output is full of them. */
34
+ guess?: boolean;
35
+ }
36
+ export interface FileScan {
37
+ refs: KeyRef[];
38
+ dynamic: DynamicKey[];
39
+ /** Strings that look like a key, and where each one is: a key can travel through a variable. */
40
+ literals: Map<string, string[]>;
41
+ /** Strings ending in a dot: `'errors.' + code`. */
42
+ prefixes: Set<string>;
43
+ /** Why this file could not be read completely. */
44
+ unproven: string[];
45
+ }
46
+ export interface AstNode {
47
+ type: string;
48
+ start?: number;
49
+ end?: number;
50
+ [key: string]: unknown;
51
+ }
52
+ export declare const lineOf: (content: string, offset: number) => number;
53
+ export declare function staticString(node: AstNode | undefined): string | null;
54
+ /**
55
+ * The syntax tree, or `null` when the code does not parse. A file with a recoverable error
56
+ * (a duplicate declaration, say) still gives a whole tree; `strict` refuses it, for telling
57
+ * an expression from a statement.
58
+ */
59
+ export declare function parseCode(code: string, lang?: string, strict?: boolean): unknown | null;
60
+ /** Every use of a translation key in one file, with positions. Comments do not count. */
61
+ export declare function scanFile(content: string, file: string): FileScan;
62
+ /** Route name Nuxt gives a page file: the name of its `locales/pages/<name>/` directory. */
63
+ export declare function pageNameOf(pagePath: string): string;
64
+ export declare function pageScopeOf(file: string, pagesRoot: string, content: string): string;
65
+ export interface ScopedRef extends KeyRef {
66
+ /** Route name when the file is a page. */
67
+ page: string | null;
68
+ /** A test or a fixture: proof of use, but no reason to add a key. */
69
+ test: boolean;
70
+ }
71
+ export interface ProjectScan {
72
+ refs: ScopedRef[];
73
+ dynamic: DynamicKey[];
74
+ literals: Map<string, string[]>;
75
+ prefixes: Set<string>;
76
+ /** Words of JSON and YAML files: keys can live in data that code feeds to `$t`. */
77
+ dataTokens: Set<string>;
78
+ dataFiles: string[];
79
+ unproven: Array<{
80
+ file: string;
81
+ reason: string;
82
+ }>;
83
+ files: number;
84
+ }
85
+ export interface ScanOptions {
86
+ /** Globs relative to the project root. */
87
+ ignore?: string[];
88
+ }
89
+ /** Evidence only, never saved: a stray byte in a notes file must not stop anything. */
90
+ export declare const dataTokensOf: (path: string) => string[];
91
+ /** Everything of the project a key can hide in, once each, whatever folder it lives in. */
92
+ export declare function projectFiles(config: ProjectConfig, options?: ScanOptions): string[];
93
+ export declare const isTestFile: (config: ProjectConfig, file: string) => boolean;
94
+ /**
95
+ * Whether a write may touch this source. The scan reads layers and alias targets to prove use;
96
+ * they belong to whoever else builds on them, and their locale files stay as they are.
97
+ */
98
+ export declare function isOwnSource(config: ProjectConfig, file: string): boolean;
99
+ export declare function scanProject(config: ProjectConfig, options?: ScanOptions): ProjectScan;
100
+ /**
101
+ * Whether a key may be read. One answer for `clean`, `check` and `search`.
102
+ *
103
+ * Any trace counts, not only a call: a string equal to the key can reach `$t` through a
104
+ * variable, a prop or a data file. A dead key that stays is a line of noise; a live key that
105
+ * goes is a broken page.
106
+ */
107
+ export declare function usageIndex(scan: ProjectScan): (key: string) => boolean;
108
+ /** Why "no source file uses this key" cannot be trusted in this project. Empty when it can. */
109
+ export declare function usageDoubts(scan: ProjectScan): string[];