lingo.dev 0.87.14 → 0.88.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +169 -6
- package/build/cli.cjs +63 -7
- package/build/cli.cjs.map +1 -1
- package/build/cli.mjs +62 -6
- package/build/cli.mjs.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,13 +1,176 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<a href="https://lingo.dev">
|
|
3
|
+
<img src="https://raw.githubusercontent.com/lingodotdev/lingo.dev/main/content/banner.dark.png" width="100%" alt="Lingo.dev" />
|
|
4
|
+
</a>
|
|
5
|
+
</p>
|
|
2
6
|
|
|
3
|
-
|
|
7
|
+
<p align="center">
|
|
8
|
+
<strong>⚡️ AI-powered open-source CLI for web & mobile localization.</strong>
|
|
9
|
+
</p>
|
|
4
10
|
|
|
5
|
-
|
|
11
|
+
<br />
|
|
12
|
+
|
|
13
|
+
<p align="center">
|
|
14
|
+
<a href="https://docs.lingo.dev">Docs</a> •
|
|
15
|
+
<a href="https://github.com/lingodotdev/lingo.dev/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22">Contribute</a> •
|
|
16
|
+
<a href="#-github-action">GitHub Action</a> •
|
|
17
|
+
<a href="#">Star the repo</a>
|
|
18
|
+
</p>
|
|
19
|
+
|
|
20
|
+
<p align="center">
|
|
21
|
+
<a href="https://github.com/lingodotdev/lingo.dev/actions/workflows/release.yml">
|
|
22
|
+
<img src="https://github.com/lingodotdev/lingo.dev/actions/workflows/release.yml/badge.svg" alt="Release" />
|
|
23
|
+
</a>
|
|
24
|
+
<a href="https://github.com/lingodotdev/lingo.dev/blob/main/LICENSE.md">
|
|
25
|
+
<img src="https://img.shields.io/github/license/lingodotdev/lingo.dev" alt="License" />
|
|
26
|
+
</a>
|
|
27
|
+
<a href="https://github.com/lingodotdev/lingo.dev/commits/main">
|
|
28
|
+
<img src="https://img.shields.io/github/last-commit/lingodotdev/lingo.dev" alt="Last Commit" />
|
|
29
|
+
</a>
|
|
30
|
+
</p>
|
|
31
|
+
|
|
32
|
+
<br />
|
|
33
|
+
|
|
34
|
+
Lingo.dev is a community-driven, open-source CLI for AI-powered web and mobile app localization.
|
|
35
|
+
|
|
36
|
+
Lingo.dev is designed to produce authentic translations instantly, eliminating manual work and management overhead. As a result, teams do accurate localization 100x faster, shipping features to more happy users worldwide. It can be used with your own LLM or with Lingo.dev-managed Localization Engine.
|
|
37
|
+
|
|
38
|
+
> **Little-known fact:** Lingo.dev began as a small project at a student hackathon back in 2023! Many iterations later, we got accepted into Y Combinator in 2024, and we're now hiring! Interested in building the next-gen localization tools? Send your CV to careers@lingo.dev! 🚀
|
|
39
|
+
|
|
40
|
+
## 📑 In This Guide
|
|
41
|
+
|
|
42
|
+
- [Quickstart](#-quickstart) - Get started in minutes
|
|
43
|
+
- [Caching](#-caching-with-i18nlock) - Optimize translation updates
|
|
44
|
+
- [GitHub Action](#-github-action) - Automate localization in CI/CD
|
|
45
|
+
- [Features](#-supercharged-features) - What makes Lingo.dev powerful
|
|
46
|
+
- [Documentation](#-documentation) - Detailed guides and references
|
|
47
|
+
- [Contribute](#-contribute) - Join our community
|
|
48
|
+
|
|
49
|
+
## 💫 Quickstart
|
|
50
|
+
|
|
51
|
+
Lingo.dev CLI is designed to work with both your own LLM, and Lingo.dev-managed Localization Engine built on top of latest SOTA (state-of-the-art) LLMs.
|
|
52
|
+
|
|
53
|
+
### Using Your Own LLM (BYOK or Bring Your Own Key)
|
|
54
|
+
|
|
55
|
+
1. Create an `i18n.json` configuration file:
|
|
56
|
+
|
|
57
|
+
```json
|
|
58
|
+
{
|
|
59
|
+
"version": 1.5,
|
|
60
|
+
"provider": {
|
|
61
|
+
"id": "anthropic",
|
|
62
|
+
"model": "claude-3-7-sonnet-latest",
|
|
63
|
+
"prompt": "You're translating text from {source} to {target}."
|
|
64
|
+
},
|
|
65
|
+
"locale": {
|
|
66
|
+
"source": "en",
|
|
67
|
+
"targets": ["es", "fr", "de"]
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
2. Set your API key as an environment variable:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
export ANTHROPIC_API_KEY=your_anthropic_api_key
|
|
76
|
+
# or for OpenAI
|
|
77
|
+
export OPENAI_API_KEY=your_openai_api_key
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
3. Run the localization:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
npx lingo.dev@latest i18n
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Using Lingo.dev Cloud
|
|
87
|
+
|
|
88
|
+
Oftentimes, production-grade apps require features like translation memory, glossary support, and localization quality assurance. Also, sometimes, you want an expert to decide for you which LLM provider and model to use, and to update the model automatically when new ones are released. Lingo.dev is a managed Localization Engine that provides these features:
|
|
89
|
+
|
|
90
|
+
1. Create an `i18n.json` configuration file (without provider node):
|
|
91
|
+
|
|
92
|
+
```json
|
|
93
|
+
{
|
|
94
|
+
"version": 1.5,
|
|
95
|
+
"locale": {
|
|
96
|
+
"source": "en",
|
|
97
|
+
"targets": ["es", "fr", "de"]
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
2. Authenticate with Lingo.dev:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
npx lingo.dev@latest auth --login
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
3. Run localization:
|
|
6
109
|
|
|
7
110
|
```bash
|
|
8
|
-
|
|
111
|
+
npx lingo.dev@latest i18n
|
|
9
112
|
```
|
|
10
113
|
|
|
11
|
-
|
|
114
|
+
## 📖 Documentation
|
|
115
|
+
|
|
116
|
+
For detailed guides and API references, visit the [documentation](https://lingo.dev/go/docs).
|
|
117
|
+
|
|
118
|
+
## 🔒 Caching with `i18n.lock`
|
|
119
|
+
|
|
120
|
+
Lingo.dev uses an `i18n.lock` file to track content checksums, ensuring only changed text gets translated. This improves:
|
|
121
|
+
|
|
122
|
+
- ⚡️ **Speed**: Skip already translated content
|
|
123
|
+
- 🔄 **Consistency**: Prevent unnecessary retranslations
|
|
124
|
+
- 💰 **Cost**: No billing for repeated translations
|
|
125
|
+
|
|
126
|
+
## 🤖 GitHub Action
|
|
127
|
+
|
|
128
|
+
Lingo.dev offers a GitHub Action to automate localization in your CI/CD pipeline. Here's a basic setup:
|
|
129
|
+
|
|
130
|
+
```yaml
|
|
131
|
+
- uses: lingodotdev/lingo.dev@main
|
|
132
|
+
with:
|
|
133
|
+
api-key: ${{ secrets.LINGODOTDEV_API_KEY }}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
This action runs `lingo.dev i18n` on every push, keeping your translations up-to-date automatically.
|
|
137
|
+
|
|
138
|
+
For pull request mode and other configuration options, visit our [GitHub Action documentation](https://docs.lingo.dev/ci-action/gha).
|
|
139
|
+
|
|
140
|
+
## ⚡️ Lingo.dev's Superpowers
|
|
141
|
+
|
|
142
|
+
- 🔥 **Instant integration**: Works with your codebase in minutes
|
|
143
|
+
- 🔄 **CI/CD Automation**: Set it and forget it
|
|
144
|
+
- 🌍 **Global reach**: Ship to users everywhere
|
|
145
|
+
- 🧠 **AI-powered**: Uses latest language models for natural translations
|
|
146
|
+
- 📊 **Format-agnostic**: JSON, YAML, CSV, Markdown, Android, iOS, and many more
|
|
147
|
+
- 🔍 **Clean diffs**: Preserves your file structure exactly
|
|
148
|
+
- ⚡️ **Lightning-fast**: Translations in seconds, not days
|
|
149
|
+
- 🔄 **Always synced**: Automatically updates when content changes
|
|
150
|
+
- 🌟 **Human quality**: Translations that don't sound robotic
|
|
151
|
+
- 👨💻 **Built by devs, for devs**: We use it ourselves daily
|
|
152
|
+
- 📈 **Grows with you**: From side project to enterprise scale
|
|
153
|
+
|
|
154
|
+
## 🤝 Contribute
|
|
155
|
+
|
|
156
|
+
Lingo.dev is community-driven, so we welcome all contributions!
|
|
157
|
+
|
|
158
|
+
Have an idea for a new feature? Create a GitHub issue!
|
|
159
|
+
|
|
160
|
+
Want to contribute? Create a pull request!
|
|
161
|
+
|
|
162
|
+
## 🌐 Readme in other languages
|
|
163
|
+
|
|
164
|
+
- [English](https://github.com/lingodotdev/lingo.dev)
|
|
165
|
+
- [Spanish](/readme/es.md)
|
|
166
|
+
- [French](/readme/fr.md)
|
|
167
|
+
- [Russian](/readme/ru.md)
|
|
168
|
+
- [German](/readme/de.md)
|
|
169
|
+
- [Chinese](/readme/zh-Hans.md)
|
|
170
|
+
- [Korean](/readme/ko.md)
|
|
171
|
+
- [Japanese](/readme/ja.md)
|
|
172
|
+
- [Italian](/readme/it.md)
|
|
173
|
+
- [Arabic](/readme/ar.md)
|
|
174
|
+
- [Hindi](/readme/hi.md)
|
|
12
175
|
|
|
13
|
-
[
|
|
176
|
+
Don't see your language? Just add a new language code to the [`i18n.json`](./i18n.json) file and open a PR.
|
package/build/cli.cjs
CHANGED
|
@@ -865,6 +865,9 @@ function getBuckets(i18nConfig) {
|
|
|
865
865
|
if (bucketEntry.lockedKeys) {
|
|
866
866
|
config.lockedKeys = bucketEntry.lockedKeys;
|
|
867
867
|
}
|
|
868
|
+
if (bucketEntry.lockedPatterns) {
|
|
869
|
+
config.lockedPatterns = bucketEntry.lockedPatterns;
|
|
870
|
+
}
|
|
868
871
|
return config;
|
|
869
872
|
});
|
|
870
873
|
return result;
|
|
@@ -3139,7 +3142,7 @@ function md5(input2) {
|
|
|
3139
3142
|
|
|
3140
3143
|
var fenceRegex = /([ \t]*)(^>\s*)?```([\s\S]*?)```/gm;
|
|
3141
3144
|
var inlineCodeRegex = /(?<!`)`([^`\r\n]+?)`(?!`)/g;
|
|
3142
|
-
var imageRegex = /([ \t]*)(^>\s*)?!\[[^\]]*?\]\([
|
|
3145
|
+
var imageRegex = /([ \t]*)(^>\s*)?!\[[^\]]*?\]\(([^()]*(\([^()]*\)[^()]*)*)\)/gm;
|
|
3143
3146
|
function ensureSurroundingImageNewlines(_content) {
|
|
3144
3147
|
let found = false;
|
|
3145
3148
|
let content = _content;
|
|
@@ -3278,8 +3281,57 @@ function createMdxSectionsSplit2Loader() {
|
|
|
3278
3281
|
});
|
|
3279
3282
|
}
|
|
3280
3283
|
|
|
3284
|
+
// src/cli/loaders/mdx2/locked-patterns.ts
|
|
3285
|
+
function extractLockedPatterns(content, patterns = []) {
|
|
3286
|
+
let finalContent = content;
|
|
3287
|
+
const lockedPlaceholders = {};
|
|
3288
|
+
if (!patterns || patterns.length === 0) {
|
|
3289
|
+
return { content: finalContent, lockedPlaceholders };
|
|
3290
|
+
}
|
|
3291
|
+
for (const patternStr of patterns) {
|
|
3292
|
+
try {
|
|
3293
|
+
const pattern = new RegExp(patternStr, "gm");
|
|
3294
|
+
const matches = Array.from(finalContent.matchAll(pattern));
|
|
3295
|
+
for (const match of matches) {
|
|
3296
|
+
const matchedText = match[0];
|
|
3297
|
+
const matchHash = md5(matchedText);
|
|
3298
|
+
const placeholder = `---LOCKED-PATTERN-${matchHash}---`;
|
|
3299
|
+
lockedPlaceholders[placeholder] = matchedText;
|
|
3300
|
+
finalContent = finalContent.replace(matchedText, placeholder);
|
|
3301
|
+
}
|
|
3302
|
+
} catch (error) {
|
|
3303
|
+
console.warn(`Invalid regex pattern: ${patternStr}`);
|
|
3304
|
+
}
|
|
3305
|
+
}
|
|
3306
|
+
return {
|
|
3307
|
+
content: finalContent,
|
|
3308
|
+
lockedPlaceholders
|
|
3309
|
+
};
|
|
3310
|
+
}
|
|
3311
|
+
function createMdxLockedPatternsLoader(defaultPatterns) {
|
|
3312
|
+
return createLoader({
|
|
3313
|
+
async pull(locale, input2, initCtx, originalLocale) {
|
|
3314
|
+
const patterns = defaultPatterns || [];
|
|
3315
|
+
const { content } = extractLockedPatterns(input2 || "", patterns);
|
|
3316
|
+
return content;
|
|
3317
|
+
},
|
|
3318
|
+
async push(locale, data, originalInput, originalLocale, pullInput, pullOutput) {
|
|
3319
|
+
const patterns = defaultPatterns || [];
|
|
3320
|
+
if (!pullInput) {
|
|
3321
|
+
return data;
|
|
3322
|
+
}
|
|
3323
|
+
const { lockedPlaceholders } = extractLockedPatterns(pullInput, patterns);
|
|
3324
|
+
let result = data;
|
|
3325
|
+
for (const [placeholder, original] of Object.entries(lockedPlaceholders)) {
|
|
3326
|
+
result = result.replaceAll(placeholder, original);
|
|
3327
|
+
}
|
|
3328
|
+
return result;
|
|
3329
|
+
}
|
|
3330
|
+
});
|
|
3331
|
+
}
|
|
3332
|
+
|
|
3281
3333
|
// src/cli/loaders/index.ts
|
|
3282
|
-
function createBucketLoader(bucketType, bucketPathPattern, options, lockedKeys) {
|
|
3334
|
+
function createBucketLoader(bucketType, bucketPathPattern, options, lockedKeys, lockedPatterns) {
|
|
3283
3335
|
switch (bucketType) {
|
|
3284
3336
|
default:
|
|
3285
3337
|
throw new Error(`Unsupported bucket type: ${bucketType}`);
|
|
@@ -3349,6 +3401,7 @@ function createBucketLoader(bucketType, bucketPathPattern, options, lockedKeys)
|
|
|
3349
3401
|
bucketPathPattern
|
|
3350
3402
|
}),
|
|
3351
3403
|
createMdxCodePlaceholderLoader(),
|
|
3404
|
+
createMdxLockedPatternsLoader(lockedPatterns),
|
|
3352
3405
|
createMdxFrontmatterSplitLoader(),
|
|
3353
3406
|
createMdxSectionsSplit2Loader(),
|
|
3354
3407
|
createLocalizableMdxDocumentLoader(),
|
|
@@ -3673,13 +3726,14 @@ function withExponentialBackoff(fn, maxAttempts = 3, baseDelay = 1e3) {
|
|
|
3673
3726
|
}
|
|
3674
3727
|
|
|
3675
3728
|
// src/cli/utils/observability.ts
|
|
3676
|
-
var _posthognode = require('posthog-node');
|
|
3729
|
+
var _posthognode = require('posthog-node'); var _posthognode2 = _interopRequireDefault(_posthognode);
|
|
3730
|
+
var { PostHog } = _posthognode2.default;
|
|
3677
3731
|
async function trackEvent(distinctId, event, properties) {
|
|
3678
3732
|
if (process.env.DO_NOT_TRACK) {
|
|
3679
3733
|
return;
|
|
3680
3734
|
}
|
|
3681
3735
|
try {
|
|
3682
|
-
const posthog = new
|
|
3736
|
+
const posthog = new PostHog("phc_eR0iSoQufBxNY36k0f0T15UvHJdTfHlh8rJcxsfhfXk", {
|
|
3683
3737
|
host: "https://eu.i.posthog.com",
|
|
3684
3738
|
flushAt: 1,
|
|
3685
3739
|
flushInterval: 0
|
|
@@ -3927,7 +3981,8 @@ var i18n_default = new (0, _interactivecommander.Command)().command("i18n").desc
|
|
|
3927
3981
|
defaultLocale: sourceLocale,
|
|
3928
3982
|
injectLocale: bucket.injectLocale
|
|
3929
3983
|
},
|
|
3930
|
-
bucket.lockedKeys
|
|
3984
|
+
bucket.lockedKeys,
|
|
3985
|
+
bucket.lockedPatterns
|
|
3931
3986
|
);
|
|
3932
3987
|
bucketLoader.setDefaultLocale(sourceLocale);
|
|
3933
3988
|
await bucketLoader.init();
|
|
@@ -4113,7 +4168,8 @@ var i18n_default = new (0, _interactivecommander.Command)().command("i18n").desc
|
|
|
4113
4168
|
defaultLocale: sourceLocale,
|
|
4114
4169
|
injectLocale: bucket.injectLocale
|
|
4115
4170
|
},
|
|
4116
|
-
bucket.lockedKeys
|
|
4171
|
+
bucket.lockedKeys,
|
|
4172
|
+
bucket.lockedPatterns
|
|
4117
4173
|
);
|
|
4118
4174
|
bucketLoader.setDefaultLocale(sourceLocale);
|
|
4119
4175
|
await bucketLoader.init();
|
|
@@ -5724,7 +5780,7 @@ function validateParams2(i18nConfig, flags) {
|
|
|
5724
5780
|
// package.json
|
|
5725
5781
|
var package_default = {
|
|
5726
5782
|
name: "lingo.dev",
|
|
5727
|
-
version: "0.
|
|
5783
|
+
version: "0.88.0",
|
|
5728
5784
|
description: "Lingo.dev CLI",
|
|
5729
5785
|
private: false,
|
|
5730
5786
|
publishConfig: {
|