@lang-tag/cli 0.9.4
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/LICENSE +21 -0
- package/README.md +136 -0
- package/cli/config.d.ts +191 -0
- package/cli/index.cjs +1492 -0
- package/cli/index.js +1474 -0
- package/cli/logger.d.ts +9 -0
- package/cli/template/base-app.mustache +69 -0
- package/cli/template/base-library.mustache +83 -0
- package/cli/template/placeholder.mustache +81 -0
- package/index.cjs +96 -0
- package/index.d.ts +169 -0
- package/index.js +96 -0
- package/package.json +64 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 TheTonsOfCode
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# Lang-tag: Component-Colocated Translation Management / Translation Engine Proxy
|
|
2
|
+
|
|
3
|
+
A professional solution for managing translations in modern JavaScript/TypeScript projects, especially those using component-based architectures. `lang-tag` simplifies internationalization by allowing you to define translation keys directly within the components where they are used. Translations become local, callable function objects with full TypeScript support, IntelliSense, and compile-time safety.
|
|
4
|
+
|
|
5
|
+
## Key Benefits
|
|
6
|
+
|
|
7
|
+
### Lightweight Core (~1KB)
|
|
8
|
+
|
|
9
|
+
The core is optimized for performance, with a bundle size of just **~1KB** ([check on Bundlephobia](https://bundlephobia.com/package/lang-tag)). It provides essential TypeScript types and minimal utilities to help you build a custom `lang-tag` setup tailored to your project.
|
|
10
|
+
|
|
11
|
+
- **Component-local translations** – define translations directly within components, avoiding scattered key structures
|
|
12
|
+
- **Light structure, full control** – only the translation object shape is enforced; naming, config, functions, and libraries are all up to you
|
|
13
|
+
- **Flexible library support** – integrate third-party packages effortlessly, with support for both classic mappings and fully customized `lang-tag` flows
|
|
14
|
+
|
|
15
|
+
### Effortless translation structure
|
|
16
|
+
|
|
17
|
+
Instead of manually managing centralized translation files, `lang-tag` lets you colocate keys within components and automatically organizes them into namespaces based on your project structure. For example, all components in `components/orders` or pages in `pages/order` share the `orders` namespace. You define a simple folder-to-namespace mapping once, and `lang-tag` handles merging and file organization—while you retain full control over how namespaces are merged.
|
|
18
|
+
|
|
19
|
+
> Set your rules, then let `lang-tag` do the rest.
|
|
20
|
+
|
|
21
|
+
### Advanced CLI (zero bundle impact)
|
|
22
|
+
|
|
23
|
+
Full functionality is available through an advanced CLI that keeps your application bundle size untouched:
|
|
24
|
+
|
|
25
|
+
- **Automatic translation collection** – `lang-tag collect` scans your project for translation tags and aggregates them into organized JSON files (e.g., `public/locales/en/common.json`), based on your configuration
|
|
26
|
+
- **Dynamic configuration updates** – `lang-tag regenerate-tags` automatically refreshes translation settings in your code, using rules defined in your configuration (e.g., mapping namespaces based on folder structure)
|
|
27
|
+
- **Third-party translation import** – `lang-tag import` detects and integrates translations from external libraries, adapting them to your project’s translation system
|
|
28
|
+
- **Watch mode** – `lang-tag watch` monitors your source files for changes and automatically re-collects/re-generates translations when needed
|
|
29
|
+
|
|
30
|
+
### Practical and Flexible Architecture
|
|
31
|
+
|
|
32
|
+
The solution provides:
|
|
33
|
+
- **Framework agnostic** – works with any JavaScript/TypeScript project and integrates easily with libraries like react-i18next
|
|
34
|
+
- **Library ecosystem support** - create reusable component libraries with embedded lang-tag translations that consuming lang-tag applications can easily import/integrate and override
|
|
35
|
+
- **Full TypeScript support** - complete type safety with IntelliSense for all translation keys and interpolation parameters
|
|
36
|
+
- **Flexible integration** - seamlessly integrates with existing i18n libraries (i18next, react-i18next) while maintaining your current translation workflow
|
|
37
|
+
- **Automation-first** - comprehensive CLI tools for collection, import, regeneration, and watch modes to streamline the entire translation workflow
|
|
38
|
+
|
|
39
|
+
## Core Concept
|
|
40
|
+
|
|
41
|
+
`lang-tag` allows translation management by enabling component-colocated translation definitions. This approach eliminates the traditional complexity of managing distributed translation files and hierarchical key structures, allowing developers to define translations directly where they are consumed.
|
|
42
|
+
|
|
43
|
+
### Component-Colocated Translation Pattern
|
|
44
|
+
|
|
45
|
+
Instead of maintaining separate translation files and complex key mappings, translations are defined inline within components:
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
// Component with colocated translations using custom i18n tag
|
|
49
|
+
import { i18n } from '../utils/i18n';
|
|
50
|
+
|
|
51
|
+
const translations = i18n({
|
|
52
|
+
greeting: 'Welcome {{name}} to our store!',
|
|
53
|
+
orderSummary: 'You have {{count}} items in your cart.',
|
|
54
|
+
actions: {
|
|
55
|
+
proceed: 'Proceed to Payment',
|
|
56
|
+
cancel: 'Cancel Order'
|
|
57
|
+
}
|
|
58
|
+
}, {
|
|
59
|
+
namespace: 'orders',
|
|
60
|
+
path: 'components.checkout'
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
function CheckoutComponent({ name, count }) {
|
|
64
|
+
const t = translations.useT();
|
|
65
|
+
|
|
66
|
+
return (
|
|
67
|
+
<div>
|
|
68
|
+
<h2>{t.greeting({ name })}</h2>
|
|
69
|
+
<p>{t.orderSummary({ count })}</p>
|
|
70
|
+
<div>
|
|
71
|
+
<button>{t.actions.proceed()}</button>
|
|
72
|
+
<button>{t.actions.cancel()}</button>
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Automated Translation Workflow
|
|
80
|
+
|
|
81
|
+
The `lang-tag` ecosystem provides tooling to transform colocated definitions into production-ready translation files:
|
|
82
|
+
|
|
83
|
+
**Collection & Organization**
|
|
84
|
+
- The `lang-tag collect` command discovers translation tags throughout your codebase
|
|
85
|
+
- Translations are organized into namespace-based JSON files (e.g., `public/locales/en/orders.json`)
|
|
86
|
+
- Hierarchical key structures can be automatically generated based on configuration rules (eg.: based on component paths)
|
|
87
|
+
|
|
88
|
+
**Dynamic Configuration Management**
|
|
89
|
+
- Configuration parameters can be automatically generated using `onConfigGeneration` rules
|
|
90
|
+
- Namespace and path assignments can be derived from custom logic (eg.: by file structure, component location)
|
|
91
|
+
- The `lang-tag regenerate-tags` command updates source code configurations dynamically
|
|
92
|
+
|
|
93
|
+
**Development-Time Optimization**
|
|
94
|
+
- Watch mode (`lang-tag watch`) provides real-time translation collection during development
|
|
95
|
+
- Changes to translation definitions trigger automatic regeneration of translation files
|
|
96
|
+
- Full TypeScript integration ensures compile-time validation of translation keys and parameters
|
|
97
|
+
|
|
98
|
+
### Enterprise Integration Capabilities
|
|
99
|
+
|
|
100
|
+
**Framework Agnostic Architecture**
|
|
101
|
+
- Core library provides building blocks (like `createCallableTranslations`) for creating custom tag functions
|
|
102
|
+
- Seamless integration with existing i18n libraries (i18next, react-i18next, etc.)
|
|
103
|
+
- Maintains compatibility with current translation workflows while enhancing developer experience
|
|
104
|
+
|
|
105
|
+
**Library Ecosystem Support**
|
|
106
|
+
- Component libraries can embed translations using `.lang-tag.exports.json` manifests
|
|
107
|
+
- The `lang-tag import` command automatically discovers and integrates library translations
|
|
108
|
+
- Consuming applications maintain full control over translation overrides and customization
|
|
109
|
+
|
|
110
|
+
**Type-Safe Translation Experience**
|
|
111
|
+
- Complete TypeScript support with IntelliSense for all translation keys
|
|
112
|
+
- Compile-time validation of interpolation parameters
|
|
113
|
+
- Callable translation objects provide intuitive API with full type inference
|
|
114
|
+
|
|
115
|
+
## Installation
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
npm install lang-tag
|
|
119
|
+
# or
|
|
120
|
+
yarn add lang-tag
|
|
121
|
+
# or
|
|
122
|
+
pnpm add lang-tag
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Documentation
|
|
126
|
+
|
|
127
|
+
For detailed setup, usage, and advanced features, please refer to the documentation:
|
|
128
|
+
|
|
129
|
+
- [Getting Started & Basic Usage](docs/getting-started.md)
|
|
130
|
+
- [CLI Usage](docs/cli-usage.md)
|
|
131
|
+
- [Advanced Features](docs/advanced-features.md)
|
|
132
|
+
- [Integrations](docs/integrations.md)
|
|
133
|
+
- [React-i18next Example](docs/react-i18n-example.md)
|
|
134
|
+
- [Library Support](docs/library-support.md)
|
|
135
|
+
- [API Reference](docs/api-reference.md)
|
|
136
|
+
- [Flexible Translation Definitions](docs/flexible-translations.md)
|
package/cli/config.d.ts
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { LangTagTranslationsConfig } from '../index.ts';
|
|
2
|
+
import { LangTagCLILogger } from './logger.ts';
|
|
3
|
+
export interface LangTagCLIConfig {
|
|
4
|
+
/**
|
|
5
|
+
* Tag name used to mark translations in code.
|
|
6
|
+
* @default 'lang'
|
|
7
|
+
*/
|
|
8
|
+
tagName: string;
|
|
9
|
+
/**
|
|
10
|
+
* Glob patterns specifying directories/files to include when searching for translations.
|
|
11
|
+
* @default ['src/** /*.{js,ts,jsx,tsx}']
|
|
12
|
+
*/
|
|
13
|
+
includes: string[];
|
|
14
|
+
/**
|
|
15
|
+
* Glob patterns specifying directories/files to exclude when searching for translations.
|
|
16
|
+
* @default ['node_modules', 'dist', 'build', '** /*.test.ts']
|
|
17
|
+
*/
|
|
18
|
+
excludes: string[];
|
|
19
|
+
/**
|
|
20
|
+
* Output directory for generated translation namespace files (e.g., common.json, errors.json).
|
|
21
|
+
* @default 'locales/en'
|
|
22
|
+
*/
|
|
23
|
+
outputDir: string;
|
|
24
|
+
collect?: {
|
|
25
|
+
/**
|
|
26
|
+
* @default 'common'
|
|
27
|
+
*/
|
|
28
|
+
defaultNamespace?: string;
|
|
29
|
+
/**
|
|
30
|
+
* When true, conflicts are not reported when two translation tags have the same path but identical values.
|
|
31
|
+
* This is useful for shared translations that appear in multiple files with the same content.
|
|
32
|
+
* @default true
|
|
33
|
+
*/
|
|
34
|
+
ignoreConflictsWithMatchingValues?: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* A function called when the collected translation configuration needs to be fixed or validated.
|
|
37
|
+
* Allows modification of the configuration before it's saved to the output files.
|
|
38
|
+
*/
|
|
39
|
+
onCollectConfigFix?: (event: LangTagCLICollectConfigFixEvent) => LangTagTranslationsConfig;
|
|
40
|
+
/**
|
|
41
|
+
* A function called when a single conflict is detected between translation tags.
|
|
42
|
+
* Allows custom resolution logic for handling individual conflicts.
|
|
43
|
+
* Return true to continue processing, false to stop execution.
|
|
44
|
+
*/
|
|
45
|
+
onConflictResolution?: (event: LangTagCLIConflictResolutionEvent) => Promise<void>;
|
|
46
|
+
/**
|
|
47
|
+
* A function called after all conflicts have been collected and processed.
|
|
48
|
+
* Allows custom logic to decide whether to continue or stop based on all conflicts.
|
|
49
|
+
* Return true to continue processing, false to stop execution.
|
|
50
|
+
*/
|
|
51
|
+
onCollectFinish?: (event: LangTagCLICollectFinishEvent) => void;
|
|
52
|
+
};
|
|
53
|
+
import: {
|
|
54
|
+
/**
|
|
55
|
+
* Output directory for generated files containing imported library tags.
|
|
56
|
+
* @default 'src/lang-libraries'
|
|
57
|
+
*/
|
|
58
|
+
dir: string;
|
|
59
|
+
/**
|
|
60
|
+
* The import statement used in generated library files to import the project's `lang` tag function.
|
|
61
|
+
* @default 'import { lang } from "@/my-lang-tag-path"'
|
|
62
|
+
*/
|
|
63
|
+
tagImportPath: string;
|
|
64
|
+
/**
|
|
65
|
+
* A function to customize the generated file name and export name for imported library tags.
|
|
66
|
+
* Allows controlling how imported tags are organized and named within the generated files.
|
|
67
|
+
*/
|
|
68
|
+
onImport: (params: LangTagCLIOnImportParams, actions: LangTagCLIOnImportActions) => void;
|
|
69
|
+
/**
|
|
70
|
+
* A function called after all lang-tags were imported
|
|
71
|
+
*/
|
|
72
|
+
onImportFinish?: () => void;
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* Determines the position of the translation argument in the `lang()` function.
|
|
76
|
+
* If `1`, translations are in the first argument (`lang(translations, options)`).
|
|
77
|
+
* If `2`, translations are in the second argument (`lang(options, translations)`).
|
|
78
|
+
* @default 1
|
|
79
|
+
*/
|
|
80
|
+
translationArgPosition: 1 | 2;
|
|
81
|
+
/**
|
|
82
|
+
* Primary language used for the library's translations.
|
|
83
|
+
* Affects default language settings when used in library mode.
|
|
84
|
+
* @default 'en'
|
|
85
|
+
*/
|
|
86
|
+
language: string;
|
|
87
|
+
/**
|
|
88
|
+
* Indicates whether this configuration is for a translation library.
|
|
89
|
+
* If true, generates an exports file (`.lang-tag.exports.json`) instead of locale files.
|
|
90
|
+
* @default false
|
|
91
|
+
*/
|
|
92
|
+
isLibrary: boolean;
|
|
93
|
+
/**
|
|
94
|
+
* Whether to flatten the translation keys. (Currently unused)
|
|
95
|
+
* @default false
|
|
96
|
+
*/
|
|
97
|
+
/**
|
|
98
|
+
* A function called for each found lang tag before processing.
|
|
99
|
+
* Allows dynamic modification of the tag's configuration (namespace, path, etc.)
|
|
100
|
+
* based on the file path or other context.
|
|
101
|
+
* If it returns `undefined`, the tag's configuration is not automatically generated or updated.
|
|
102
|
+
*/
|
|
103
|
+
onConfigGeneration: (params: LangTagCLIOnConfigGenerationParams) => LangTagTranslationsConfig | undefined;
|
|
104
|
+
debug?: boolean;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Parameters passed to the `onImport` configuration function.
|
|
108
|
+
*/
|
|
109
|
+
export interface LangTagCLIOnImportParams {
|
|
110
|
+
/** The name of the package from which the tag is being imported. */
|
|
111
|
+
packageName: string;
|
|
112
|
+
/** The relative path to the source file within the imported package. */
|
|
113
|
+
importedRelativePath: string;
|
|
114
|
+
/** The original variable name assigned to the lang tag in the source library file, if any. */
|
|
115
|
+
originalExportName: string | undefined;
|
|
116
|
+
/** Parsed JSON translation object from the imported tag. */
|
|
117
|
+
translations: Record<string, any>;
|
|
118
|
+
/** Configuration object associated with the imported tag. */
|
|
119
|
+
config: LangTagTranslationsConfig;
|
|
120
|
+
/** A mutable object that can be used to pass data between multiple `onImport` calls for the same generated file. */
|
|
121
|
+
fileGenerationData: any;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Actions that can be performed within the onImport callback.
|
|
125
|
+
*/
|
|
126
|
+
export interface LangTagCLIOnImportActions {
|
|
127
|
+
/** Sets the desired file for the generated import. */
|
|
128
|
+
setFile: (file: string) => void;
|
|
129
|
+
/** Sets the desired export name for the imported tag. */
|
|
130
|
+
setExportName: (name: string) => void;
|
|
131
|
+
/** Sets the configuration for the currently imported tag. */
|
|
132
|
+
setConfig: (config: LangTagTranslationsConfig) => void;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Parameters passed to the `onConfigGeneration` configuration function.
|
|
136
|
+
*/
|
|
137
|
+
export interface LangTagCLIOnConfigGenerationParams {
|
|
138
|
+
/** The absolute path to the source file being processed. */
|
|
139
|
+
fullPath: string;
|
|
140
|
+
/** The path of the source file relative to the project root (where the command was invoked). */
|
|
141
|
+
path: string;
|
|
142
|
+
/** True if the file being processed is located within the configured library import directory (`config.import.dir`). */
|
|
143
|
+
isImportedLibrary: boolean;
|
|
144
|
+
/** The configuration object extracted from the lang tag's options argument (e.g., `{ namespace: 'common', path: 'my.path' }`). */
|
|
145
|
+
config: LangTagTranslationsConfig;
|
|
146
|
+
}
|
|
147
|
+
type Validity = 'ok' | 'invalid-param-1' | 'invalid-param-2' | 'translations-not-found';
|
|
148
|
+
export interface LangTagCLIProcessedTag {
|
|
149
|
+
fullMatch: string;
|
|
150
|
+
parameter1Text: string;
|
|
151
|
+
parameter2Text?: string;
|
|
152
|
+
parameterTranslations: any;
|
|
153
|
+
parameterConfig?: any;
|
|
154
|
+
variableName?: string;
|
|
155
|
+
/** Character index in the whole text where the match starts */
|
|
156
|
+
index: number;
|
|
157
|
+
/** Line number (1-based) where the match was found */
|
|
158
|
+
line: number;
|
|
159
|
+
/** Column number (1-based) where the match starts in the line */
|
|
160
|
+
column: number;
|
|
161
|
+
validity: Validity;
|
|
162
|
+
}
|
|
163
|
+
export interface LangTagCLITagConflictInfo {
|
|
164
|
+
tag: LangTagCLIProcessedTag;
|
|
165
|
+
relativeFilePath: string;
|
|
166
|
+
value: any;
|
|
167
|
+
}
|
|
168
|
+
export interface LangTagCLIConflict {
|
|
169
|
+
path: string;
|
|
170
|
+
tagA: LangTagCLITagConflictInfo;
|
|
171
|
+
tagB: LangTagCLITagConflictInfo;
|
|
172
|
+
conflictType: 'path_overwrite' | 'type_mismatch';
|
|
173
|
+
}
|
|
174
|
+
export interface LangTagCLICollectConfigFixEvent {
|
|
175
|
+
config: LangTagTranslationsConfig;
|
|
176
|
+
langTagConfig: LangTagCLIConfig;
|
|
177
|
+
}
|
|
178
|
+
export interface LangTagCLIConflictResolutionEvent {
|
|
179
|
+
conflict: LangTagCLIConflict;
|
|
180
|
+
logger: LangTagCLILogger;
|
|
181
|
+
/** Breaks translation collection process */
|
|
182
|
+
exit(): void;
|
|
183
|
+
}
|
|
184
|
+
export interface LangTagCLICollectFinishEvent {
|
|
185
|
+
conflicts: LangTagCLIConflict[];
|
|
186
|
+
logger: LangTagCLILogger;
|
|
187
|
+
/** Breaks translation collection process */
|
|
188
|
+
exit(): void;
|
|
189
|
+
}
|
|
190
|
+
export declare const LANG_TAG_DEFAULT_CONFIG: LangTagCLIConfig;
|
|
191
|
+
export {};
|