poly-lexis 0.8.1 → 0.9.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.
- package/README.md +15 -1
- package/dist/cli/translations.js +47 -17
- package/dist/cli/translations.js.map +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +45 -14
- package/dist/index.js.map +1 -1
- package/dist/scripts/verify-translations.js +2 -1
- package/dist/scripts/verify-translations.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -150,7 +150,8 @@ poly-lexis uses a `.translationsrc.json` file in your project root for configura
|
|
|
150
150
|
"languages": ["en", "es", "fr", "de"],
|
|
151
151
|
"sourceLanguage": "en",
|
|
152
152
|
"typesOutputPath": "src/types/i18nTypes.ts",
|
|
153
|
-
"provider": "deepl"
|
|
153
|
+
"provider": "deepl",
|
|
154
|
+
"protectedTerms": ["MyBrand", "ProductName"]
|
|
154
155
|
}
|
|
155
156
|
```
|
|
156
157
|
|
|
@@ -161,6 +162,19 @@ poly-lexis uses a `.translationsrc.json` file in your project root for configura
|
|
|
161
162
|
- `sourceLanguage` - Source language for translations (default: `"en"`)
|
|
162
163
|
- `typesOutputPath` - Path to output TypeScript types (default: `src/types/i18nTypes.ts`)
|
|
163
164
|
- `provider` - Translation provider to use: `"deepl"` or `"google"` (default: `"deepl"`)
|
|
165
|
+
- `protectedTerms` - Words or phrases that should never be translated (default: `[]`)
|
|
166
|
+
|
|
167
|
+
### Protected Terms
|
|
168
|
+
|
|
169
|
+
Use `protectedTerms` to prevent specific words or phrases from being translated — useful for brand names, product names, or any term that must remain unchanged across all languages.
|
|
170
|
+
|
|
171
|
+
```json
|
|
172
|
+
{
|
|
173
|
+
"protectedTerms": ["Vandelay Industries", "MyProduct"]
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
These terms are replaced with placeholders before the text is sent to the translation API and restored afterwards, so the translation service never sees them.
|
|
164
178
|
|
|
165
179
|
### Environment Variables
|
|
166
180
|
|
package/dist/cli/translations.js
CHANGED
|
@@ -710,7 +710,8 @@ var init_types = __esm({
|
|
|
710
710
|
provider: "deepl",
|
|
711
711
|
useFallbackLanguages: true,
|
|
712
712
|
searchPaths: ["src", "app", "pages", "components"],
|
|
713
|
-
searchExtensions: [".ts", ".tsx", ".js", ".jsx", ".vue", ".svelte"]
|
|
713
|
+
searchExtensions: [".ts", ".tsx", ".js", ".jsx", ".vue", ".svelte"],
|
|
714
|
+
protectedTerms: []
|
|
714
715
|
};
|
|
715
716
|
DEFAULT_LANGUAGES = ["en", "fr", "it", "pl", "es", "pt", "de", "nl", "sv", "hu", "cs", "ja"];
|
|
716
717
|
}
|
|
@@ -1321,10 +1322,20 @@ import * as path5 from "path";
|
|
|
1321
1322
|
// src/translations/utils/deepl-translate-provider.ts
|
|
1322
1323
|
init_esm_shims();
|
|
1323
1324
|
init_language_fallback();
|
|
1324
|
-
function preserveVariables(text) {
|
|
1325
|
+
function preserveVariables(text, protectedTerms = []) {
|
|
1325
1326
|
const variableMap = /* @__PURE__ */ new Map();
|
|
1326
1327
|
let placeholderIndex = 0;
|
|
1327
|
-
|
|
1328
|
+
let result = text;
|
|
1329
|
+
for (const term of protectedTerms) {
|
|
1330
|
+
const escaped = term.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1331
|
+
result = result.replace(new RegExp(escaped, "g"), () => {
|
|
1332
|
+
const placeholder = `XXX_${placeholderIndex}_XXX`;
|
|
1333
|
+
variableMap.set(placeholder, term);
|
|
1334
|
+
placeholderIndex++;
|
|
1335
|
+
return placeholder;
|
|
1336
|
+
});
|
|
1337
|
+
}
|
|
1338
|
+
const textWithPlaceholders = result.replace(/\{\{([^}]+)\}\}/g, (match) => {
|
|
1328
1339
|
const placeholder = `XXX_${placeholderIndex}_XXX`;
|
|
1329
1340
|
variableMap.set(placeholder, match);
|
|
1330
1341
|
placeholderIndex++;
|
|
@@ -1356,7 +1367,7 @@ var DeepLTranslateProvider = class {
|
|
|
1356
1367
|
return this.isFreeApi ? "https://api-free.deepl.com/v2/translate" : "https://api.deepl.com/v2/translate";
|
|
1357
1368
|
}
|
|
1358
1369
|
async translate(options) {
|
|
1359
|
-
const { text, sourceLang, targetLang, apiKey, useFallbackLanguages = true } = options;
|
|
1370
|
+
const { text, sourceLang, targetLang, apiKey, useFallbackLanguages = true, protectedTerms = [] } = options;
|
|
1360
1371
|
if (!apiKey) {
|
|
1361
1372
|
throw new Error(
|
|
1362
1373
|
"DeepL API key is required. Set DEEPL_API_KEY environment variable or provide apiKey in options."
|
|
@@ -1370,7 +1381,7 @@ var DeepLTranslateProvider = class {
|
|
|
1370
1381
|
logLanguageFallback(sourceLangResult, "deepl");
|
|
1371
1382
|
resolvedSourceLang = sourceLangResult.resolvedLanguage;
|
|
1372
1383
|
}
|
|
1373
|
-
const { textWithPlaceholders, variableMap } = preserveVariables(text);
|
|
1384
|
+
const { textWithPlaceholders, variableMap } = preserveVariables(text, protectedTerms);
|
|
1374
1385
|
const body = {
|
|
1375
1386
|
text: [textWithPlaceholders],
|
|
1376
1387
|
target_lang: normalizeLanguageCode(targetLangResult.resolvedLanguage),
|
|
@@ -1420,10 +1431,20 @@ var DeepLTranslateProvider = class {
|
|
|
1420
1431
|
// src/translations/utils/google-translate-provider.ts
|
|
1421
1432
|
init_esm_shims();
|
|
1422
1433
|
init_language_fallback();
|
|
1423
|
-
function preserveVariables2(text) {
|
|
1434
|
+
function preserveVariables2(text, protectedTerms = []) {
|
|
1424
1435
|
const variableMap = /* @__PURE__ */ new Map();
|
|
1425
1436
|
let placeholderIndex = 0;
|
|
1426
|
-
|
|
1437
|
+
let result = text;
|
|
1438
|
+
for (const term of protectedTerms) {
|
|
1439
|
+
const escaped = term.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1440
|
+
result = result.replace(new RegExp(escaped, "g"), () => {
|
|
1441
|
+
const placeholder = `XXX_${placeholderIndex}_XXX`;
|
|
1442
|
+
variableMap.set(placeholder, term);
|
|
1443
|
+
placeholderIndex++;
|
|
1444
|
+
return placeholder;
|
|
1445
|
+
});
|
|
1446
|
+
}
|
|
1447
|
+
const textWithPlaceholders = result.replace(/\{\{([^}]+)\}\}/g, (match) => {
|
|
1427
1448
|
const placeholder = `XXX_${placeholderIndex}_XXX`;
|
|
1428
1449
|
variableMap.set(placeholder, match);
|
|
1429
1450
|
placeholderIndex++;
|
|
@@ -1445,7 +1466,7 @@ function restoreVariables2(text, variableMap) {
|
|
|
1445
1466
|
}
|
|
1446
1467
|
var GoogleTranslateProvider = class {
|
|
1447
1468
|
async translate(options) {
|
|
1448
|
-
const { text, sourceLang, targetLang, apiKey, useFallbackLanguages = true } = options;
|
|
1469
|
+
const { text, sourceLang, targetLang, apiKey, useFallbackLanguages = true, protectedTerms = [] } = options;
|
|
1449
1470
|
if (!apiKey) {
|
|
1450
1471
|
throw new Error(
|
|
1451
1472
|
"Google Translate API key is required. Set GOOGLE_TRANSLATE_API_KEY environment variable or provide apiKey in options."
|
|
@@ -1459,7 +1480,7 @@ var GoogleTranslateProvider = class {
|
|
|
1459
1480
|
logLanguageFallback(sourceLangResult, "google");
|
|
1460
1481
|
resolvedSourceLang = sourceLangResult.resolvedLanguage;
|
|
1461
1482
|
}
|
|
1462
|
-
const { textWithPlaceholders, variableMap } = preserveVariables2(text);
|
|
1483
|
+
const { textWithPlaceholders, variableMap } = preserveVariables2(text, protectedTerms);
|
|
1463
1484
|
const url = `https://translation.googleapis.com/language/translate/v2?key=${apiKey}`;
|
|
1464
1485
|
const sourceForGoogle = resolvedSourceLang?.includes("_") ? resolvedSourceLang.split("_")[0] : resolvedSourceLang;
|
|
1465
1486
|
const targetForGoogle = targetLangResult.resolvedLanguage.includes("_") ? targetLangResult.resolvedLanguage.split("_")[0] : targetLangResult.resolvedLanguage;
|
|
@@ -1514,14 +1535,15 @@ function setTranslationProvider(provider) {
|
|
|
1514
1535
|
function getTranslationProvider() {
|
|
1515
1536
|
return customProvider || defaultProvider;
|
|
1516
1537
|
}
|
|
1517
|
-
async function translateText(text, targetLang, sourceLang = "en", apiKey, useFallbackLanguages = true) {
|
|
1538
|
+
async function translateText(text, targetLang, sourceLang = "en", apiKey, useFallbackLanguages = true, protectedTerms = []) {
|
|
1518
1539
|
const provider = getTranslationProvider();
|
|
1519
1540
|
return provider.translate({
|
|
1520
1541
|
text,
|
|
1521
1542
|
sourceLang,
|
|
1522
1543
|
targetLang,
|
|
1523
1544
|
apiKey,
|
|
1524
|
-
useFallbackLanguages
|
|
1545
|
+
useFallbackLanguages,
|
|
1546
|
+
protectedTerms
|
|
1525
1547
|
});
|
|
1526
1548
|
}
|
|
1527
1549
|
|
|
@@ -1572,7 +1594,14 @@ async function addTranslationKey(projectRoot, options) {
|
|
|
1572
1594
|
targetTranslations[namespace] = {};
|
|
1573
1595
|
}
|
|
1574
1596
|
if (!targetTranslations[namespace][key] || targetTranslations[namespace][key].trim() === "") {
|
|
1575
|
-
const translated = await translateText(
|
|
1597
|
+
const translated = await translateText(
|
|
1598
|
+
value,
|
|
1599
|
+
lang,
|
|
1600
|
+
sourceLang,
|
|
1601
|
+
apiKey,
|
|
1602
|
+
config.useFallbackLanguages,
|
|
1603
|
+
config.protectedTerms
|
|
1604
|
+
);
|
|
1576
1605
|
targetTranslations[namespace][key] = translated;
|
|
1577
1606
|
const sorted = sortKeys(targetTranslations[namespace]);
|
|
1578
1607
|
writeTranslation(translationsPath, lang, namespace, sorted);
|
|
@@ -2003,7 +2032,8 @@ Processing language: ${language}`);
|
|
|
2003
2032
|
language,
|
|
2004
2033
|
config.sourceLanguage,
|
|
2005
2034
|
apiKey,
|
|
2006
|
-
config.useFallbackLanguages
|
|
2035
|
+
config.useFallbackLanguages,
|
|
2036
|
+
config.protectedTerms
|
|
2007
2037
|
);
|
|
2008
2038
|
console.log(` ${language.toUpperCase()}: "${translated}"`);
|
|
2009
2039
|
if (!dryRun) {
|
|
@@ -2370,8 +2400,8 @@ if (command === "find-unused") {
|
|
|
2370
2400
|
message: "Enter translation key (UPPERCASE_SNAKE_CASE):",
|
|
2371
2401
|
validate: (value2) => {
|
|
2372
2402
|
if (!value2.trim()) return "Key is required";
|
|
2373
|
-
if (!/^[A-Z0-9_]+$/.test(value2)) {
|
|
2374
|
-
return "Key
|
|
2403
|
+
if (!/^[A-Z0-9_]+$/.test(value2.toUpperCase())) {
|
|
2404
|
+
return "Key must be SNAKE_CASE (e.g., SAVE_CHANGES)";
|
|
2375
2405
|
}
|
|
2376
2406
|
return true;
|
|
2377
2407
|
},
|
|
@@ -2584,8 +2614,8 @@ if (command === "find-unused") {
|
|
|
2584
2614
|
message: "Enter translation key (UPPERCASE_SNAKE_CASE):",
|
|
2585
2615
|
validate: (value2) => {
|
|
2586
2616
|
if (!value2.trim()) return "Key is required";
|
|
2587
|
-
if (!/^[A-Z0-9_]+$/.test(value2)) {
|
|
2588
|
-
return "Key
|
|
2617
|
+
if (!/^[A-Z0-9_]+$/.test(value2.toUpperCase())) {
|
|
2618
|
+
return "Key must be SNAKE_CASE (e.g., SAVE_CHANGES)";
|
|
2589
2619
|
}
|
|
2590
2620
|
return true;
|
|
2591
2621
|
},
|