@lingo.dev/_compiler 0.2.4 → 0.3.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/build/index.cjs CHANGED
@@ -4,7 +4,7 @@ var _unplugin = require('unplugin');
4
4
  // package.json
5
5
  var package_default = {
6
6
  name: "@lingo.dev/_compiler",
7
- version: "0.2.4",
7
+ version: "0.3.1",
8
8
  description: "Lingo.dev Compiler",
9
9
  private: false,
10
10
  publishConfig: {
@@ -46,6 +46,8 @@ var package_default = {
46
46
  "@babel/parser": "^7.26.7",
47
47
  "@babel/traverse": "^7.27.4",
48
48
  "@babel/types": "^7.26.7",
49
+ "@lingo.dev/_sdk": "workspace:*",
50
+ "@openrouter/ai-sdk-provider": "^0.7.1",
49
51
  ai: "^4.2.10",
50
52
  dedent: "^1.6.0",
51
53
  dotenv: "^16.4.5",
@@ -53,11 +55,13 @@ var package_default = {
53
55
  ini: "^5.0.0",
54
56
  lodash: "^4.17.21",
55
57
  "object-hash": "^3.0.0",
58
+ "ollama-ai-provider": "^1.2.0",
56
59
  prettier: "^3.4.2",
57
60
  unplugin: "^2.1.2",
58
61
  vitest: "^2.1.4",
59
62
  zod: "^3.24.1"
60
- }
63
+ },
64
+ packageManager: "pnpm@9.12.3"
61
65
  };
62
66
 
63
67
  // src/index.ts
@@ -830,10 +834,9 @@ var LCPCache = class {
830
834
  }
831
835
  // merge dictionary with current cache, sort files, entries and locales to minimize diffs
832
836
  static _mergeLocaleDictionary(currentCache, dictionary, lcp) {
833
- const files = _lodash2.default.call(void 0, dictionary.files).mapValues((file, fileName) => ({
834
- ...file,
835
- entries: _lodash2.default.call(void 0, file.entries).mapValues((entry, entryName) => {
836
- const cachedEntry = _nullishCoalesce(_lodash2.default.get(currentCache, ["files", fileName, "entries", entryName]), () => ( {}));
837
+ const newCache = _lodash2.default.cloneDeep(currentCache);
838
+ for (const [fileName, fileData] of Object.entries(dictionary.files)) {
839
+ for (const [entryName, entryValue] of Object.entries(fileData.entries)) {
837
840
  const hash = _lodash2.default.get(lcp, [
838
841
  "files",
839
842
  fileName,
@@ -841,19 +844,33 @@ var LCPCache = class {
841
844
  entryName,
842
845
  "hash"
843
846
  ]);
844
- const cachedEntryContent = cachedEntry.hash === hash ? cachedEntry.content : {};
845
- const content = _lodash2.default.call(void 0, {
846
- ...cachedEntryContent,
847
- [dictionary.locale]: entry
847
+ const existingEntry = _lodash2.default.get(newCache, [
848
+ "files",
849
+ fileName,
850
+ "entries",
851
+ entryName
852
+ ]) || {
853
+ content: {},
854
+ hash
855
+ };
856
+ const mergedContent = _lodash2.default.call(void 0, {
857
+ ...existingEntry.content,
858
+ [dictionary.locale]: entryValue
848
859
  }).toPairs().sortBy([0]).fromPairs().value();
849
- return { content, hash };
850
- }).toPairs().sortBy([0]).fromPairs().value()
851
- })).toPairs().sortBy([0]).fromPairs().value();
852
- const newCache = {
860
+ _lodash2.default.set(newCache, ["files", fileName, "entries", entryName], {
861
+ content: mergedContent,
862
+ hash
863
+ });
864
+ }
865
+ }
866
+ const sortedFiles = _lodash2.default.call(void 0, newCache.files).toPairs().sortBy([0]).map(([fileName, file]) => {
867
+ const sortedEntries = _lodash2.default.call(void 0, file.entries).toPairs().sortBy([0]).fromPairs().value();
868
+ return [fileName, { entries: sortedEntries }];
869
+ }).fromPairs().value();
870
+ return {
853
871
  version: dictionary.version,
854
- files
872
+ files: sortedFiles
855
873
  };
856
- return newCache;
857
874
  }
858
875
  // extract dictionary from cache for given locale, validate entry hash from LCP schema
859
876
  static _extractLocaleDictionary(cache, locale, lcp) {
@@ -926,7 +943,10 @@ var LCPCache = class {
926
943
  // src/lib/lcp/api/index.ts
927
944
  var _groq = require('@ai-sdk/groq');
928
945
  var _google = require('@ai-sdk/google');
946
+ var _aisdkprovider = require('@openrouter/ai-sdk-provider');
947
+ var _ollamaaiprovider = require('ollama-ai-provider');
929
948
  var _ai = require('ai');
949
+ var __sdk = require('@lingo.dev/_sdk');
930
950
 
931
951
 
932
952
  // src/utils/locales.ts
@@ -949,7 +969,16 @@ function getLocaleModel(localeModels, sourceLocale, targetLocale) {
949
969
  ];
950
970
  const modelKey = localeKeys.find((key) => localeModels.hasOwnProperty(key));
951
971
  if (modelKey) {
952
- const [provider, model] = _optionalChain([localeModels, 'access', _29 => _29[modelKey], 'optionalAccess', _30 => _30.split, 'call', _31 => _31(":")]);
972
+ const value = localeModels[modelKey];
973
+ const firstColonIndex = _optionalChain([value, 'optionalAccess', _29 => _29.indexOf, 'call', _30 => _30(":")]);
974
+ if (value && firstColonIndex !== -1 && firstColonIndex !== void 0) {
975
+ const provider2 = value.substring(0, firstColonIndex);
976
+ const model2 = value.substring(firstColonIndex + 1);
977
+ if (provider2 && model2) {
978
+ return { provider: provider2, model: model2 };
979
+ }
980
+ }
981
+ const [provider, model] = _optionalChain([value, 'optionalAccess', _31 => _31.split, 'call', _32 => _32(":")]) || [];
953
982
  if (provider && model) {
954
983
  return { provider, model };
955
984
  }
@@ -962,7 +991,7 @@ var prompt_default = (args) => {
962
991
  return getUserSystemPrompt(args) || getBuiltInSystemPrompt(args);
963
992
  };
964
993
  function getUserSystemPrompt(args) {
965
- const userPrompt = _optionalChain([args, 'access', _32 => _32.prompt, 'optionalAccess', _33 => _33.trim, 'call', _34 => _34(), 'optionalAccess', _35 => _35.replace, 'call', _36 => _36("{SOURCE_LOCALE}", args.sourceLocale), 'optionalAccess', _37 => _37.replace, 'call', _38 => _38("{TARGET_LOCALE}", args.targetLocale)]);
994
+ const userPrompt = _optionalChain([args, 'access', _33 => _33.prompt, 'optionalAccess', _34 => _34.trim, 'call', _35 => _35(), 'optionalAccess', _36 => _36.replace, 'call', _37 => _37("{SOURCE_LOCALE}", args.sourceLocale), 'optionalAccess', _38 => _38.replace, 'call', _39 => _39("{TARGET_LOCALE}", args.targetLocale)]);
966
995
  if (userPrompt) {
967
996
  console.log("\u2728 Compiler is using user-defined prompt.");
968
997
  return userPrompt;
@@ -1172,7 +1201,7 @@ function getKeyFromEnv(envVarName) {
1172
1201
  path.default.resolve(process.cwd(), ".env.development")
1173
1202
  ]
1174
1203
  });
1175
- return _optionalChain([result, 'optionalAccess', _39 => _39.parsed, 'optionalAccess', _40 => _40[envVarName]]);
1204
+ return _optionalChain([result, 'optionalAccess', _40 => _40.parsed, 'optionalAccess', _41 => _41[envVarName]]);
1176
1205
  }
1177
1206
  function getKeyFromRc(rcPath) {
1178
1207
  const rc = getRc();
@@ -1188,6 +1217,15 @@ function getGroqKeyFromRc() {
1188
1217
  function getGroqKeyFromEnv() {
1189
1218
  return getKeyFromEnv("GROQ_API_KEY");
1190
1219
  }
1220
+ function getLingoDotDevKeyFromEnv() {
1221
+ return getKeyFromEnv("LINGODOTDEV_API_KEY");
1222
+ }
1223
+ function getLingoDotDevKeyFromRc() {
1224
+ return getKeyFromRc("auth.apiKey");
1225
+ }
1226
+ function getLingoDotDevKey() {
1227
+ return getLingoDotDevKeyFromEnv() || getLingoDotDevKeyFromRc();
1228
+ }
1191
1229
  function getGoogleKey() {
1192
1230
  return getGoogleKeyFromEnv() || getGoogleKeyFromRc();
1193
1231
  }
@@ -1197,6 +1235,15 @@ function getGoogleKeyFromRc() {
1197
1235
  function getGoogleKeyFromEnv() {
1198
1236
  return getKeyFromEnv("GOOGLE_API_KEY");
1199
1237
  }
1238
+ function getOpenRouterKey() {
1239
+ return getOpenRouterKeyFromEnv() || getOpenRouterKeyFromRc();
1240
+ }
1241
+ function getOpenRouterKeyFromRc() {
1242
+ return getKeyFromRc("llm.openrouterApiKey");
1243
+ }
1244
+ function getOpenRouterKeyFromEnv() {
1245
+ return getKeyFromEnv("OPENROUTER_API_KEY");
1246
+ }
1200
1247
 
1201
1248
  // src/lib/lcp/api/index.ts
1202
1249
 
@@ -1222,6 +1269,22 @@ var providerDetails = {
1222
1269
  apiKeyConfigKey: "llm.googleApiKey",
1223
1270
  getKeyLink: "https://ai.google.dev/",
1224
1271
  docsLink: "https://ai.google.dev/gemini-api/docs/troubleshooting"
1272
+ },
1273
+ openrouter: {
1274
+ name: "OpenRouter",
1275
+ apiKeyEnvVar: "OPENROUTER_API_KEY",
1276
+ apiKeyConfigKey: "llm.openrouterApiKey",
1277
+ getKeyLink: "https://openrouter.ai",
1278
+ docsLink: "https://openrouter.ai/docs"
1279
+ },
1280
+ ollama: {
1281
+ name: "Ollama",
1282
+ apiKeyEnvVar: void 0,
1283
+ // Ollama doesn't require an API key
1284
+ apiKeyConfigKey: void 0,
1285
+ // Ollama doesn't require an API key
1286
+ getKeyLink: "https://ollama.com/download",
1287
+ docsLink: "https://github.com/ollama/ollama/tree/main/docs"
1225
1288
  }
1226
1289
  };
1227
1290
 
@@ -1308,59 +1371,118 @@ var LCPAPI = class {
1308
1371
  };
1309
1372
  return dictionary;
1310
1373
  }
1311
- static async _translateChunk(models, sourceDictionary, sourceLocale, targetLocale) {
1312
- const { provider, model } = getLocaleModel(
1313
- models,
1314
- sourceLocale,
1315
- targetLocale
1316
- );
1317
- if (!provider || !model) {
1374
+ static _createLingoDotDevEngine() {
1375
+ if (isRunningInCIOrDocker()) {
1376
+ const apiKeyFromEnv = getLingoDotDevKeyFromEnv();
1377
+ if (!apiKeyFromEnv) {
1378
+ this._failMissingLLMKeyCi("lingo.dev");
1379
+ }
1380
+ }
1381
+ const apiKey = getLingoDotDevKey();
1382
+ if (!apiKey) {
1318
1383
  throw new Error(
1319
- `\u26A0\uFE0F Locale "${targetLocale}" is not configured. Add provider and model for this locale to your config, e.g., "groq:llama3-8b-8192".`
1384
+ "\u26A0\uFE0F Lingo.dev API key not found. Please set LINGODOTDEV_API_KEY environment variable or configure it user-wide."
1320
1385
  );
1321
1386
  }
1322
- try {
1323
- const aiModel = this._createAiModel(provider, model, targetLocale);
1324
- console.log(
1325
- `\u2728 Using model "${model}" from "${provider}" to translate from "${sourceLocale}" to "${targetLocale}"`
1326
- );
1327
- const response = await _ai.generateText.call(void 0, {
1328
- model: aiModel,
1329
- messages: [
1387
+ console.log(`Creating Lingo.dev client`);
1388
+ return new (0, __sdk.LingoDotDevEngine)({
1389
+ apiKey
1390
+ });
1391
+ }
1392
+ static async _translateChunk(models, sourceDictionary, sourceLocale, targetLocale) {
1393
+ if (models === "lingo.dev") {
1394
+ try {
1395
+ const lingoDotDevEngine = this._createLingoDotDevEngine();
1396
+ console.log(
1397
+ `\u2728 Using Lingo.dev Engine to localize from "${sourceLocale}" to "${targetLocale}"`
1398
+ );
1399
+ const result = await lingoDotDevEngine.localizeObject(
1400
+ sourceDictionary,
1330
1401
  {
1331
- role: "system",
1332
- content: prompt_default({ sourceLocale, targetLocale })
1333
- },
1334
- ...shots_default.flatMap((shotsTuple) => [
1402
+ sourceLocale,
1403
+ targetLocale
1404
+ }
1405
+ );
1406
+ return result;
1407
+ } catch (error) {
1408
+ this._failLLMFailureLocal(
1409
+ "lingo.dev",
1410
+ targetLocale,
1411
+ error instanceof Error ? error.message : "Unknown error"
1412
+ );
1413
+ throw error;
1414
+ }
1415
+ } else {
1416
+ const { provider, model } = getLocaleModel(
1417
+ models,
1418
+ sourceLocale,
1419
+ targetLocale
1420
+ );
1421
+ if (!provider || !model) {
1422
+ throw new Error(
1423
+ _dedent2.default`
1424
+ 🚫 Lingo.dev Localization Engine Not Configured!
1425
+
1426
+ The "models" parameter is missing or incomplete in your Lingo.dev configuration.
1427
+
1428
+ 👉 To fix this, set the "models" parameter to either:
1429
+ • "lingo.dev" (for the default engine)
1430
+ • a map of locale-to-model, e.g. { "models": { "en:es": "openai:gpt-3.5-turbo" } }
1431
+
1432
+ Example:
1433
+ {
1434
+ // ...
1435
+ "models": "lingo.dev"
1436
+ }
1437
+
1438
+ For more details, see: https://lingo.dev/compiler
1439
+ To get help, join our Discord: https://lingo.dev/go/discord
1440
+ `
1441
+ );
1442
+ }
1443
+ try {
1444
+ const aiModel = this._createAiModel(provider, model, targetLocale);
1445
+ console.log(
1446
+ `\u2139\uFE0F Using raw LLM API ("${provider}":"${model}") to translate from "${sourceLocale}" to "${targetLocale}"`
1447
+ );
1448
+ const response = await _ai.generateText.call(void 0, {
1449
+ model: aiModel,
1450
+ messages: [
1335
1451
  {
1336
- role: "user",
1337
- content: obj2xml(shotsTuple[0])
1452
+ role: "system",
1453
+ content: prompt_default({ sourceLocale, targetLocale })
1338
1454
  },
1455
+ ...shots_default.flatMap((shotsTuple) => [
1456
+ {
1457
+ role: "user",
1458
+ content: obj2xml(shotsTuple[0])
1459
+ },
1460
+ {
1461
+ role: "assistant",
1462
+ content: obj2xml(shotsTuple[1])
1463
+ }
1464
+ ]),
1339
1465
  {
1340
- role: "assistant",
1341
- content: obj2xml(shotsTuple[1])
1466
+ role: "user",
1467
+ content: obj2xml(sourceDictionary)
1342
1468
  }
1343
- ]),
1344
- {
1345
- role: "user",
1346
- content: obj2xml(sourceDictionary)
1347
- }
1348
- ]
1349
- });
1350
- console.log("Response text received for", targetLocale);
1351
- let responseText = response.text;
1352
- responseText = responseText.substring(
1353
- responseText.indexOf("<"),
1354
- responseText.lastIndexOf(">") + 1
1355
- );
1356
- return xml2obj(responseText);
1357
- } catch (error) {
1358
- this._failLLMFailureLocal(
1359
- provider,
1360
- targetLocale,
1361
- error instanceof Error ? error.message : "Unknown error"
1362
- );
1363
- throw error;
1469
+ ]
1470
+ });
1471
+ console.log("Response text received for", targetLocale);
1472
+ let responseText = response.text;
1473
+ responseText = responseText.substring(
1474
+ responseText.indexOf("<"),
1475
+ responseText.lastIndexOf(">") + 1
1476
+ );
1477
+ return xml2obj(responseText);
1478
+ } catch (error) {
1479
+ this._failLLMFailureLocal(
1480
+ provider,
1481
+ targetLocale,
1482
+ error instanceof Error ? error.message : "Unknown error"
1483
+ );
1484
+ throw error;
1485
+ }
1364
1486
  }
1365
1487
  }
1366
1488
  /**
@@ -1374,7 +1496,7 @@ var LCPAPI = class {
1374
1496
  */
1375
1497
  static _createAiModel(providerId, modelId, targetLocale) {
1376
1498
  switch (providerId) {
1377
- case "groq":
1499
+ case "groq": {
1378
1500
  if (isRunningInCIOrDocker()) {
1379
1501
  const groqFromEnv = getGroqKeyFromEnv();
1380
1502
  if (!groqFromEnv) {
@@ -1391,7 +1513,8 @@ var LCPAPI = class {
1391
1513
  `Creating Groq client for ${targetLocale} using model ${modelId}`
1392
1514
  );
1393
1515
  return _groq.createGroq.call(void 0, { apiKey: groqKey })(modelId);
1394
- case "google":
1516
+ }
1517
+ case "google": {
1395
1518
  if (isRunningInCIOrDocker()) {
1396
1519
  const googleFromEnv = getGoogleKeyFromEnv();
1397
1520
  if (!googleFromEnv) {
@@ -1408,10 +1531,38 @@ var LCPAPI = class {
1408
1531
  `Creating Google Generative AI client for ${targetLocale} using model ${modelId}`
1409
1532
  );
1410
1533
  return _google.createGoogleGenerativeAI.call(void 0, { apiKey: googleKey })(modelId);
1411
- default:
1534
+ }
1535
+ case "openrouter": {
1536
+ if (isRunningInCIOrDocker()) {
1537
+ const openRouterFromEnv = getOpenRouterKeyFromEnv();
1538
+ if (!openRouterFromEnv) {
1539
+ this._failMissingLLMKeyCi(providerId);
1540
+ }
1541
+ }
1542
+ const openRouterKey = getOpenRouterKey();
1543
+ if (!openRouterKey) {
1544
+ throw new Error(
1545
+ "\u26A0\uFE0F OpenRouter API key not found. Please set OPENROUTER_API_KEY environment variable or configure it user-wide."
1546
+ );
1547
+ }
1548
+ console.log(
1549
+ `Creating OpenRouter client for ${targetLocale} using model ${modelId}`
1550
+ );
1551
+ return _aisdkprovider.createOpenRouter.call(void 0, {
1552
+ apiKey: openRouterKey
1553
+ })(modelId);
1554
+ }
1555
+ case "ollama": {
1556
+ console.log(
1557
+ `Creating Ollama client for ${targetLocale} using model ${modelId} at default Ollama address`
1558
+ );
1559
+ return _ollamaaiprovider.createOllama.call(void 0, )(modelId);
1560
+ }
1561
+ default: {
1412
1562
  throw new Error(
1413
1563
  `\u26A0\uFE0F Provider "${providerId}" for locale "${targetLocale}" is not supported. Only "groq" and "google" providers are supported at the moment.`
1414
1564
  );
1565
+ }
1415
1566
  }
1416
1567
  }
1417
1568
  /**
@@ -1500,7 +1651,7 @@ var LCPAPI = class {
1500
1651
  This error comes from the ${details.name} API, please check their documentation for more details: ${details.docsLink}
1501
1652
 
1502
1653
  ⭐️ Also:
1503
- 1. Did you set ${details.apiKeyEnvVar} environment variable correctly?
1654
+ 1. Did you set ${details.apiKeyEnvVar ? `${details.apiKeyEnvVar}` : "the provider API key"} environment variable correctly ${!details.apiKeyEnvVar ? "(if required)" : ""}?
1504
1655
  2. Did you reach any limits of your ${details.name} account?
1505
1656
  3. If you have questions, feature requests, or would like to contribute, join our Discord: https://lingo.dev/go/discord
1506
1657
 
@@ -1514,31 +1665,39 @@ var LCPAPI = class {
1514
1665
 
1515
1666
  // src/lib/lcp/server.ts
1516
1667
  var LCPServer = (_class = class {
1517
- static __initStatic() {this.isLoading = false}
1668
+ static __initStatic() {this.dictionariesCache = null}
1669
+ static __initStatic2() {this.inFlightPromise = null}
1518
1670
  static async loadDictionaries(params) {
1519
- while (this.isLoading) {
1520
- await new Promise(function(resolve3) {
1521
- setTimeout(resolve3, 500);
1522
- });
1671
+ if (this.dictionariesCache) {
1672
+ return this.dictionariesCache;
1523
1673
  }
1524
- this.isLoading = true;
1525
- const targetLocales = _lodash2.default.uniq([
1526
- ...params.targetLocales,
1527
- params.sourceLocale
1528
- ]);
1529
- const dictionaries = await Promise.all(
1530
- targetLocales.map(
1531
- (targetLocale) => this.loadDictionaryForLocale({ ...params, targetLocale })
1532
- )
1533
- );
1534
- const result = _lodash2.default.fromPairs(
1535
- targetLocales.map((targetLocale, index) => [
1536
- targetLocale,
1537
- dictionaries[index]
1538
- ])
1539
- );
1540
- this.isLoading = false;
1541
- return result;
1674
+ if (this.inFlightPromise) {
1675
+ return this.inFlightPromise;
1676
+ }
1677
+ this.inFlightPromise = (async () => {
1678
+ try {
1679
+ const targetLocales = _lodash2.default.uniq([
1680
+ ...params.targetLocales,
1681
+ params.sourceLocale
1682
+ ]);
1683
+ const dictionaries = await Promise.all(
1684
+ targetLocales.map(
1685
+ (targetLocale) => this.loadDictionaryForLocale({ ...params, targetLocale })
1686
+ )
1687
+ );
1688
+ const result = _lodash2.default.fromPairs(
1689
+ targetLocales.map((targetLocale, index) => [
1690
+ targetLocale,
1691
+ dictionaries[index]
1692
+ ])
1693
+ );
1694
+ this.dictionariesCache = result;
1695
+ return result;
1696
+ } finally {
1697
+ this.inFlightPromise = null;
1698
+ }
1699
+ })();
1700
+ return this.inFlightPromise;
1542
1701
  }
1543
1702
  static async loadDictionaryForLocale(params) {
1544
1703
  const sourceDictionary = this._extractSourceDictionary(
@@ -1703,14 +1862,14 @@ var LCPServer = (_class = class {
1703
1862
  const sourceFile = _lodash2.default.get(sourceDictionary.files, fileName);
1704
1863
  const targetFile = _lodash2.default.get(targetDictionary.files, fileName);
1705
1864
  const entries = removeEmptyEntries ? _lodash2.default.pickBy(
1706
- _optionalChain([sourceFile, 'optionalAccess', _41 => _41.entries]) || {},
1707
- (value) => _optionalChain([String, 'call', _42 => _42(value || ""), 'optionalAccess', _43 => _43.trim, 'optionalCall', _44 => _44(), 'optionalAccess', _45 => _45.length]) > 0
1708
- ) : _optionalChain([sourceFile, 'optionalAccess', _46 => _46.entries]) || {};
1865
+ _optionalChain([sourceFile, 'optionalAccess', _42 => _42.entries]) || {},
1866
+ (value) => _optionalChain([String, 'call', _43 => _43(value || ""), 'optionalAccess', _44 => _44.trim, 'optionalCall', _45 => _45(), 'optionalAccess', _46 => _46.length]) > 0
1867
+ ) : _optionalChain([sourceFile, 'optionalAccess', _47 => _47.entries]) || {};
1709
1868
  return [
1710
1869
  fileName,
1711
1870
  {
1712
1871
  ...targetFile,
1713
- entries: _lodash2.default.merge(_optionalChain([targetFile, 'optionalAccess', _47 => _47.entries]) || {}, entries)
1872
+ entries: _lodash2.default.merge({}, _optionalChain([targetFile, 'optionalAccess', _48 => _48.entries]) || {}, entries)
1714
1873
  }
1715
1874
  ];
1716
1875
  }).fromPairs().value();
@@ -1727,7 +1886,7 @@ var LCPServer = (_class = class {
1727
1886
  0
1728
1887
  );
1729
1888
  }
1730
- }, _class.__initStatic(), _class);
1889
+ }, _class.__initStatic(), _class.__initStatic2(), _class);
1731
1890
 
1732
1891
  // src/utils/invokations.ts
1733
1892
 
@@ -1916,7 +2075,7 @@ function jsxFragmentMutation(payload) {
1916
2075
  var jsxHtmlLangMutation = createCodeMutation((payload) => {
1917
2076
  _traverse2.default.call(void 0, payload.ast, {
1918
2077
  JSXElement: (path7) => {
1919
- if (_optionalChain([getJsxElementName, 'call', _48 => _48(path7), 'optionalAccess', _49 => _49.toLowerCase, 'call', _50 => _50()]) === "html") {
2078
+ if (_optionalChain([getJsxElementName, 'call', _49 => _49(path7), 'optionalAccess', _50 => _50.toLowerCase, 'call', _51 => _51()]) === "html") {
1920
2079
  const mode = getModuleExecutionMode(payload.ast, payload.params.rsc);
1921
2080
  const packagePath = mode === "client" ? ModuleId.ReactClient : ModuleId.ReactRSC;
1922
2081
  const lingoHtmlComponentImport = getOrCreateImport(payload.ast, {
@@ -2499,6 +2658,10 @@ var keyCheckers = {
2499
2658
  google: {
2500
2659
  checkEnv: getGoogleKeyFromEnv,
2501
2660
  checkRc: getGoogleKeyFromRc
2661
+ },
2662
+ "lingo.dev": {
2663
+ checkEnv: getLingoDotDevKeyFromEnv,
2664
+ checkRc: getLingoDotDevKeyFromRc
2502
2665
  }
2503
2666
  };
2504
2667
  var unplugin = _unplugin.createUnplugin.call(void 0,
@@ -2506,26 +2669,31 @@ var unplugin = _unplugin.createUnplugin.call(void 0,
2506
2669
  console.log("\u2139\uFE0F Starting Lingo.dev compiler...");
2507
2670
  const params = _lodash2.default.defaults(_params, defaultParams);
2508
2671
  if (!isRunningInCIOrDocker()) {
2509
- validateLLMKeyDetails(params.models);
2510
- }
2511
- const invalidLocales = getInvalidLocales(
2512
- params.models,
2513
- params.sourceLocale,
2514
- params.targetLocales
2515
- );
2516
- if (invalidLocales.length > 0) {
2517
- console.log(_dedent2.default`
2518
- \n
2519
- ⚠️ Lingo.dev Localization Compiler requires LLM model setup for the following locales: ${invalidLocales.join(", ")}.
2520
-
2521
- ⭐️ Next steps:
2522
- 1. Refer to documentation for help: https://docs.lingo.dev/
2523
- 2. If you want to use a different LLM, raise an issue in our open-source repo: https://lingo.dev/go/gh
2524
- 3. If you have questions, feature requests, or would like to contribute, join our Discord: https://lingo.dev/go/discord
2525
-
2526
-
2527
- `);
2528
- process.exit(1);
2672
+ if (params.models === "lingo.dev") {
2673
+ validateLLMKeyDetails(["lingo.dev"]);
2674
+ } else {
2675
+ const configuredProviders = getConfiguredProviders(params.models);
2676
+ validateLLMKeyDetails(configuredProviders);
2677
+ const invalidLocales = getInvalidLocales(
2678
+ params.models,
2679
+ params.sourceLocale,
2680
+ params.targetLocales
2681
+ );
2682
+ if (invalidLocales.length > 0) {
2683
+ console.log(_dedent2.default`
2684
+ \n
2685
+ ⚠️ Lingo.dev Localization Compiler requires LLM model setup for the following locales: ${invalidLocales.join(", ")}.
2686
+
2687
+ ⭐️ Next steps:
2688
+ 1. Refer to documentation for help: https://docs.lingo.dev/
2689
+ 2. If you want to use a different LLM, raise an issue in our open-source repo: https://lingo.dev/go/gh
2690
+ 3. If you have questions, feature requests, or would like to contribute, join our Discord: https://lingo.dev/go/discord
2691
+
2692
+
2693
+ `);
2694
+ process.exit(1);
2695
+ }
2696
+ }
2529
2697
  }
2530
2698
  LCPCache.ensureDictionaryFile({
2531
2699
  sourceRoot: params.sourceRoot,
@@ -2553,6 +2721,7 @@ var unplugin = _unplugin.createUnplugin.call(void 0,
2553
2721
  lingoDir: params.lingoDir
2554
2722
  });
2555
2723
  const dictionary = dictionaries[moduleInfo.params.locale];
2724
+ console.log(JSON.stringify(dictionary, null, 2));
2556
2725
  return {
2557
2726
  code: `export default ${JSON.stringify(dictionary, null, 2)}`
2558
2727
  };
@@ -2621,10 +2790,12 @@ var src_default = {
2621
2790
  return config2;
2622
2791
  }
2623
2792
  };
2624
- function validateLLMKeyDetails(models) {
2625
- const configuredProviders = _lodash2.default.chain(Object.values(models)).map((modelString) => modelString.split(":")[0]).filter(Boolean).uniq().filter(
2793
+ function getConfiguredProviders(models) {
2794
+ return _lodash2.default.chain(Object.values(models)).map((modelString) => modelString.split(":")[0]).filter(Boolean).uniq().filter(
2626
2795
  (providerId) => providerDetails.hasOwnProperty(providerId) && keyCheckers.hasOwnProperty(providerId)
2627
2796
  ).value();
2797
+ }
2798
+ function validateLLMKeyDetails(configuredProviders) {
2628
2799
  if (configuredProviders.length === 0) {
2629
2800
  return;
2630
2801
  }
package/build/index.d.cts CHANGED
@@ -8,7 +8,7 @@ type CompilerParams = {
8
8
  rsc: boolean;
9
9
  useDirective: boolean;
10
10
  debug: boolean;
11
- models: Record<string, string>;
11
+ models: "lingo.dev" | Record<string, string>;
12
12
  };
13
13
  declare const defaultParams: CompilerParams;
14
14
 
package/build/index.d.ts CHANGED
@@ -8,7 +8,7 @@ type CompilerParams = {
8
8
  rsc: boolean;
9
9
  useDirective: boolean;
10
10
  debug: boolean;
11
- models: Record<string, string>;
11
+ models: "lingo.dev" | Record<string, string>;
12
12
  };
13
13
  declare const defaultParams: CompilerParams;
14
14
 
package/build/index.mjs CHANGED
@@ -4,7 +4,7 @@ import { createUnplugin } from "unplugin";
4
4
  // package.json
5
5
  var package_default = {
6
6
  name: "@lingo.dev/_compiler",
7
- version: "0.2.4",
7
+ version: "0.3.1",
8
8
  description: "Lingo.dev Compiler",
9
9
  private: false,
10
10
  publishConfig: {
@@ -46,6 +46,8 @@ var package_default = {
46
46
  "@babel/parser": "^7.26.7",
47
47
  "@babel/traverse": "^7.27.4",
48
48
  "@babel/types": "^7.26.7",
49
+ "@lingo.dev/_sdk": "workspace:*",
50
+ "@openrouter/ai-sdk-provider": "^0.7.1",
49
51
  ai: "^4.2.10",
50
52
  dedent: "^1.6.0",
51
53
  dotenv: "^16.4.5",
@@ -53,11 +55,13 @@ var package_default = {
53
55
  ini: "^5.0.0",
54
56
  lodash: "^4.17.21",
55
57
  "object-hash": "^3.0.0",
58
+ "ollama-ai-provider": "^1.2.0",
56
59
  prettier: "^3.4.2",
57
60
  unplugin: "^2.1.2",
58
61
  vitest: "^2.1.4",
59
62
  zod: "^3.24.1"
60
- }
63
+ },
64
+ packageManager: "pnpm@9.12.3"
61
65
  };
62
66
 
63
67
  // src/index.ts
@@ -830,10 +834,9 @@ var LCPCache = class {
830
834
  }
831
835
  // merge dictionary with current cache, sort files, entries and locales to minimize diffs
832
836
  static _mergeLocaleDictionary(currentCache, dictionary, lcp) {
833
- const files = _3(dictionary.files).mapValues((file, fileName) => ({
834
- ...file,
835
- entries: _3(file.entries).mapValues((entry, entryName) => {
836
- const cachedEntry = _3.get(currentCache, ["files", fileName, "entries", entryName]) ?? {};
837
+ const newCache = _3.cloneDeep(currentCache);
838
+ for (const [fileName, fileData] of Object.entries(dictionary.files)) {
839
+ for (const [entryName, entryValue] of Object.entries(fileData.entries)) {
837
840
  const hash = _3.get(lcp, [
838
841
  "files",
839
842
  fileName,
@@ -841,19 +844,33 @@ var LCPCache = class {
841
844
  entryName,
842
845
  "hash"
843
846
  ]);
844
- const cachedEntryContent = cachedEntry.hash === hash ? cachedEntry.content : {};
845
- const content = _3({
846
- ...cachedEntryContent,
847
- [dictionary.locale]: entry
847
+ const existingEntry = _3.get(newCache, [
848
+ "files",
849
+ fileName,
850
+ "entries",
851
+ entryName
852
+ ]) || {
853
+ content: {},
854
+ hash
855
+ };
856
+ const mergedContent = _3({
857
+ ...existingEntry.content,
858
+ [dictionary.locale]: entryValue
848
859
  }).toPairs().sortBy([0]).fromPairs().value();
849
- return { content, hash };
850
- }).toPairs().sortBy([0]).fromPairs().value()
851
- })).toPairs().sortBy([0]).fromPairs().value();
852
- const newCache = {
860
+ _3.set(newCache, ["files", fileName, "entries", entryName], {
861
+ content: mergedContent,
862
+ hash
863
+ });
864
+ }
865
+ }
866
+ const sortedFiles = _3(newCache.files).toPairs().sortBy([0]).map(([fileName, file]) => {
867
+ const sortedEntries = _3(file.entries).toPairs().sortBy([0]).fromPairs().value();
868
+ return [fileName, { entries: sortedEntries }];
869
+ }).fromPairs().value();
870
+ return {
853
871
  version: dictionary.version,
854
- files
872
+ files: sortedFiles
855
873
  };
856
- return newCache;
857
874
  }
858
875
  // extract dictionary from cache for given locale, validate entry hash from LCP schema
859
876
  static _extractLocaleDictionary(cache, locale, lcp) {
@@ -926,7 +943,10 @@ var LCPCache = class {
926
943
  // src/lib/lcp/api/index.ts
927
944
  import { createGroq } from "@ai-sdk/groq";
928
945
  import { createGoogleGenerativeAI } from "@ai-sdk/google";
946
+ import { createOpenRouter } from "@openrouter/ai-sdk-provider";
947
+ import { createOllama } from "ollama-ai-provider";
929
948
  import { generateText } from "ai";
949
+ import { LingoDotDevEngine } from "@lingo.dev/_sdk";
930
950
  import _6 from "lodash";
931
951
 
932
952
  // src/utils/locales.ts
@@ -949,7 +969,16 @@ function getLocaleModel(localeModels, sourceLocale, targetLocale) {
949
969
  ];
950
970
  const modelKey = localeKeys.find((key) => localeModels.hasOwnProperty(key));
951
971
  if (modelKey) {
952
- const [provider, model] = localeModels[modelKey]?.split(":");
972
+ const value = localeModels[modelKey];
973
+ const firstColonIndex = value?.indexOf(":");
974
+ if (value && firstColonIndex !== -1 && firstColonIndex !== void 0) {
975
+ const provider2 = value.substring(0, firstColonIndex);
976
+ const model2 = value.substring(firstColonIndex + 1);
977
+ if (provider2 && model2) {
978
+ return { provider: provider2, model: model2 };
979
+ }
980
+ }
981
+ const [provider, model] = value?.split(":") || [];
953
982
  if (provider && model) {
954
983
  return { provider, model };
955
984
  }
@@ -1188,6 +1217,15 @@ function getGroqKeyFromRc() {
1188
1217
  function getGroqKeyFromEnv() {
1189
1218
  return getKeyFromEnv("GROQ_API_KEY");
1190
1219
  }
1220
+ function getLingoDotDevKeyFromEnv() {
1221
+ return getKeyFromEnv("LINGODOTDEV_API_KEY");
1222
+ }
1223
+ function getLingoDotDevKeyFromRc() {
1224
+ return getKeyFromRc("auth.apiKey");
1225
+ }
1226
+ function getLingoDotDevKey() {
1227
+ return getLingoDotDevKeyFromEnv() || getLingoDotDevKeyFromRc();
1228
+ }
1191
1229
  function getGoogleKey() {
1192
1230
  return getGoogleKeyFromEnv() || getGoogleKeyFromRc();
1193
1231
  }
@@ -1197,6 +1235,15 @@ function getGoogleKeyFromRc() {
1197
1235
  function getGoogleKeyFromEnv() {
1198
1236
  return getKeyFromEnv("GOOGLE_API_KEY");
1199
1237
  }
1238
+ function getOpenRouterKey() {
1239
+ return getOpenRouterKeyFromEnv() || getOpenRouterKeyFromRc();
1240
+ }
1241
+ function getOpenRouterKeyFromRc() {
1242
+ return getKeyFromRc("llm.openrouterApiKey");
1243
+ }
1244
+ function getOpenRouterKeyFromEnv() {
1245
+ return getKeyFromEnv("OPENROUTER_API_KEY");
1246
+ }
1200
1247
 
1201
1248
  // src/lib/lcp/api/index.ts
1202
1249
  import dedent2 from "dedent";
@@ -1222,6 +1269,22 @@ var providerDetails = {
1222
1269
  apiKeyConfigKey: "llm.googleApiKey",
1223
1270
  getKeyLink: "https://ai.google.dev/",
1224
1271
  docsLink: "https://ai.google.dev/gemini-api/docs/troubleshooting"
1272
+ },
1273
+ openrouter: {
1274
+ name: "OpenRouter",
1275
+ apiKeyEnvVar: "OPENROUTER_API_KEY",
1276
+ apiKeyConfigKey: "llm.openrouterApiKey",
1277
+ getKeyLink: "https://openrouter.ai",
1278
+ docsLink: "https://openrouter.ai/docs"
1279
+ },
1280
+ ollama: {
1281
+ name: "Ollama",
1282
+ apiKeyEnvVar: void 0,
1283
+ // Ollama doesn't require an API key
1284
+ apiKeyConfigKey: void 0,
1285
+ // Ollama doesn't require an API key
1286
+ getKeyLink: "https://ollama.com/download",
1287
+ docsLink: "https://github.com/ollama/ollama/tree/main/docs"
1225
1288
  }
1226
1289
  };
1227
1290
 
@@ -1308,59 +1371,118 @@ var LCPAPI = class {
1308
1371
  };
1309
1372
  return dictionary;
1310
1373
  }
1311
- static async _translateChunk(models, sourceDictionary, sourceLocale, targetLocale) {
1312
- const { provider, model } = getLocaleModel(
1313
- models,
1314
- sourceLocale,
1315
- targetLocale
1316
- );
1317
- if (!provider || !model) {
1374
+ static _createLingoDotDevEngine() {
1375
+ if (isRunningInCIOrDocker()) {
1376
+ const apiKeyFromEnv = getLingoDotDevKeyFromEnv();
1377
+ if (!apiKeyFromEnv) {
1378
+ this._failMissingLLMKeyCi("lingo.dev");
1379
+ }
1380
+ }
1381
+ const apiKey = getLingoDotDevKey();
1382
+ if (!apiKey) {
1318
1383
  throw new Error(
1319
- `\u26A0\uFE0F Locale "${targetLocale}" is not configured. Add provider and model for this locale to your config, e.g., "groq:llama3-8b-8192".`
1384
+ "\u26A0\uFE0F Lingo.dev API key not found. Please set LINGODOTDEV_API_KEY environment variable or configure it user-wide."
1320
1385
  );
1321
1386
  }
1322
- try {
1323
- const aiModel = this._createAiModel(provider, model, targetLocale);
1324
- console.log(
1325
- `\u2728 Using model "${model}" from "${provider}" to translate from "${sourceLocale}" to "${targetLocale}"`
1326
- );
1327
- const response = await generateText({
1328
- model: aiModel,
1329
- messages: [
1387
+ console.log(`Creating Lingo.dev client`);
1388
+ return new LingoDotDevEngine({
1389
+ apiKey
1390
+ });
1391
+ }
1392
+ static async _translateChunk(models, sourceDictionary, sourceLocale, targetLocale) {
1393
+ if (models === "lingo.dev") {
1394
+ try {
1395
+ const lingoDotDevEngine = this._createLingoDotDevEngine();
1396
+ console.log(
1397
+ `\u2728 Using Lingo.dev Engine to localize from "${sourceLocale}" to "${targetLocale}"`
1398
+ );
1399
+ const result = await lingoDotDevEngine.localizeObject(
1400
+ sourceDictionary,
1330
1401
  {
1331
- role: "system",
1332
- content: prompt_default({ sourceLocale, targetLocale })
1333
- },
1334
- ...shots_default.flatMap((shotsTuple) => [
1402
+ sourceLocale,
1403
+ targetLocale
1404
+ }
1405
+ );
1406
+ return result;
1407
+ } catch (error) {
1408
+ this._failLLMFailureLocal(
1409
+ "lingo.dev",
1410
+ targetLocale,
1411
+ error instanceof Error ? error.message : "Unknown error"
1412
+ );
1413
+ throw error;
1414
+ }
1415
+ } else {
1416
+ const { provider, model } = getLocaleModel(
1417
+ models,
1418
+ sourceLocale,
1419
+ targetLocale
1420
+ );
1421
+ if (!provider || !model) {
1422
+ throw new Error(
1423
+ dedent2`
1424
+ 🚫 Lingo.dev Localization Engine Not Configured!
1425
+
1426
+ The "models" parameter is missing or incomplete in your Lingo.dev configuration.
1427
+
1428
+ 👉 To fix this, set the "models" parameter to either:
1429
+ • "lingo.dev" (for the default engine)
1430
+ • a map of locale-to-model, e.g. { "models": { "en:es": "openai:gpt-3.5-turbo" } }
1431
+
1432
+ Example:
1433
+ {
1434
+ // ...
1435
+ "models": "lingo.dev"
1436
+ }
1437
+
1438
+ For more details, see: https://lingo.dev/compiler
1439
+ To get help, join our Discord: https://lingo.dev/go/discord
1440
+ `
1441
+ );
1442
+ }
1443
+ try {
1444
+ const aiModel = this._createAiModel(provider, model, targetLocale);
1445
+ console.log(
1446
+ `\u2139\uFE0F Using raw LLM API ("${provider}":"${model}") to translate from "${sourceLocale}" to "${targetLocale}"`
1447
+ );
1448
+ const response = await generateText({
1449
+ model: aiModel,
1450
+ messages: [
1335
1451
  {
1336
- role: "user",
1337
- content: obj2xml(shotsTuple[0])
1452
+ role: "system",
1453
+ content: prompt_default({ sourceLocale, targetLocale })
1338
1454
  },
1455
+ ...shots_default.flatMap((shotsTuple) => [
1456
+ {
1457
+ role: "user",
1458
+ content: obj2xml(shotsTuple[0])
1459
+ },
1460
+ {
1461
+ role: "assistant",
1462
+ content: obj2xml(shotsTuple[1])
1463
+ }
1464
+ ]),
1339
1465
  {
1340
- role: "assistant",
1341
- content: obj2xml(shotsTuple[1])
1466
+ role: "user",
1467
+ content: obj2xml(sourceDictionary)
1342
1468
  }
1343
- ]),
1344
- {
1345
- role: "user",
1346
- content: obj2xml(sourceDictionary)
1347
- }
1348
- ]
1349
- });
1350
- console.log("Response text received for", targetLocale);
1351
- let responseText = response.text;
1352
- responseText = responseText.substring(
1353
- responseText.indexOf("<"),
1354
- responseText.lastIndexOf(">") + 1
1355
- );
1356
- return xml2obj(responseText);
1357
- } catch (error) {
1358
- this._failLLMFailureLocal(
1359
- provider,
1360
- targetLocale,
1361
- error instanceof Error ? error.message : "Unknown error"
1362
- );
1363
- throw error;
1469
+ ]
1470
+ });
1471
+ console.log("Response text received for", targetLocale);
1472
+ let responseText = response.text;
1473
+ responseText = responseText.substring(
1474
+ responseText.indexOf("<"),
1475
+ responseText.lastIndexOf(">") + 1
1476
+ );
1477
+ return xml2obj(responseText);
1478
+ } catch (error) {
1479
+ this._failLLMFailureLocal(
1480
+ provider,
1481
+ targetLocale,
1482
+ error instanceof Error ? error.message : "Unknown error"
1483
+ );
1484
+ throw error;
1485
+ }
1364
1486
  }
1365
1487
  }
1366
1488
  /**
@@ -1374,7 +1496,7 @@ var LCPAPI = class {
1374
1496
  */
1375
1497
  static _createAiModel(providerId, modelId, targetLocale) {
1376
1498
  switch (providerId) {
1377
- case "groq":
1499
+ case "groq": {
1378
1500
  if (isRunningInCIOrDocker()) {
1379
1501
  const groqFromEnv = getGroqKeyFromEnv();
1380
1502
  if (!groqFromEnv) {
@@ -1391,7 +1513,8 @@ var LCPAPI = class {
1391
1513
  `Creating Groq client for ${targetLocale} using model ${modelId}`
1392
1514
  );
1393
1515
  return createGroq({ apiKey: groqKey })(modelId);
1394
- case "google":
1516
+ }
1517
+ case "google": {
1395
1518
  if (isRunningInCIOrDocker()) {
1396
1519
  const googleFromEnv = getGoogleKeyFromEnv();
1397
1520
  if (!googleFromEnv) {
@@ -1408,10 +1531,38 @@ var LCPAPI = class {
1408
1531
  `Creating Google Generative AI client for ${targetLocale} using model ${modelId}`
1409
1532
  );
1410
1533
  return createGoogleGenerativeAI({ apiKey: googleKey })(modelId);
1411
- default:
1534
+ }
1535
+ case "openrouter": {
1536
+ if (isRunningInCIOrDocker()) {
1537
+ const openRouterFromEnv = getOpenRouterKeyFromEnv();
1538
+ if (!openRouterFromEnv) {
1539
+ this._failMissingLLMKeyCi(providerId);
1540
+ }
1541
+ }
1542
+ const openRouterKey = getOpenRouterKey();
1543
+ if (!openRouterKey) {
1544
+ throw new Error(
1545
+ "\u26A0\uFE0F OpenRouter API key not found. Please set OPENROUTER_API_KEY environment variable or configure it user-wide."
1546
+ );
1547
+ }
1548
+ console.log(
1549
+ `Creating OpenRouter client for ${targetLocale} using model ${modelId}`
1550
+ );
1551
+ return createOpenRouter({
1552
+ apiKey: openRouterKey
1553
+ })(modelId);
1554
+ }
1555
+ case "ollama": {
1556
+ console.log(
1557
+ `Creating Ollama client for ${targetLocale} using model ${modelId} at default Ollama address`
1558
+ );
1559
+ return createOllama()(modelId);
1560
+ }
1561
+ default: {
1412
1562
  throw new Error(
1413
1563
  `\u26A0\uFE0F Provider "${providerId}" for locale "${targetLocale}" is not supported. Only "groq" and "google" providers are supported at the moment.`
1414
1564
  );
1565
+ }
1415
1566
  }
1416
1567
  }
1417
1568
  /**
@@ -1500,7 +1651,7 @@ var LCPAPI = class {
1500
1651
  This error comes from the ${details.name} API, please check their documentation for more details: ${details.docsLink}
1501
1652
 
1502
1653
  ⭐️ Also:
1503
- 1. Did you set ${details.apiKeyEnvVar} environment variable correctly?
1654
+ 1. Did you set ${details.apiKeyEnvVar ? `${details.apiKeyEnvVar}` : "the provider API key"} environment variable correctly ${!details.apiKeyEnvVar ? "(if required)" : ""}?
1504
1655
  2. Did you reach any limits of your ${details.name} account?
1505
1656
  3. If you have questions, feature requests, or would like to contribute, join our Discord: https://lingo.dev/go/discord
1506
1657
 
@@ -1514,31 +1665,39 @@ var LCPAPI = class {
1514
1665
 
1515
1666
  // src/lib/lcp/server.ts
1516
1667
  var LCPServer = class {
1517
- static isLoading = false;
1668
+ static dictionariesCache = null;
1669
+ static inFlightPromise = null;
1518
1670
  static async loadDictionaries(params) {
1519
- while (this.isLoading) {
1520
- await new Promise(function(resolve3) {
1521
- setTimeout(resolve3, 500);
1522
- });
1671
+ if (this.dictionariesCache) {
1672
+ return this.dictionariesCache;
1523
1673
  }
1524
- this.isLoading = true;
1525
- const targetLocales = _7.uniq([
1526
- ...params.targetLocales,
1527
- params.sourceLocale
1528
- ]);
1529
- const dictionaries = await Promise.all(
1530
- targetLocales.map(
1531
- (targetLocale) => this.loadDictionaryForLocale({ ...params, targetLocale })
1532
- )
1533
- );
1534
- const result = _7.fromPairs(
1535
- targetLocales.map((targetLocale, index) => [
1536
- targetLocale,
1537
- dictionaries[index]
1538
- ])
1539
- );
1540
- this.isLoading = false;
1541
- return result;
1674
+ if (this.inFlightPromise) {
1675
+ return this.inFlightPromise;
1676
+ }
1677
+ this.inFlightPromise = (async () => {
1678
+ try {
1679
+ const targetLocales = _7.uniq([
1680
+ ...params.targetLocales,
1681
+ params.sourceLocale
1682
+ ]);
1683
+ const dictionaries = await Promise.all(
1684
+ targetLocales.map(
1685
+ (targetLocale) => this.loadDictionaryForLocale({ ...params, targetLocale })
1686
+ )
1687
+ );
1688
+ const result = _7.fromPairs(
1689
+ targetLocales.map((targetLocale, index) => [
1690
+ targetLocale,
1691
+ dictionaries[index]
1692
+ ])
1693
+ );
1694
+ this.dictionariesCache = result;
1695
+ return result;
1696
+ } finally {
1697
+ this.inFlightPromise = null;
1698
+ }
1699
+ })();
1700
+ return this.inFlightPromise;
1542
1701
  }
1543
1702
  static async loadDictionaryForLocale(params) {
1544
1703
  const sourceDictionary = this._extractSourceDictionary(
@@ -1710,7 +1869,7 @@ var LCPServer = class {
1710
1869
  fileName,
1711
1870
  {
1712
1871
  ...targetFile,
1713
- entries: _7.merge(targetFile?.entries || {}, entries)
1872
+ entries: _7.merge({}, targetFile?.entries || {}, entries)
1714
1873
  }
1715
1874
  ];
1716
1875
  }).fromPairs().value();
@@ -2499,6 +2658,10 @@ var keyCheckers = {
2499
2658
  google: {
2500
2659
  checkEnv: getGoogleKeyFromEnv,
2501
2660
  checkRc: getGoogleKeyFromRc
2661
+ },
2662
+ "lingo.dev": {
2663
+ checkEnv: getLingoDotDevKeyFromEnv,
2664
+ checkRc: getLingoDotDevKeyFromRc
2502
2665
  }
2503
2666
  };
2504
2667
  var unplugin = createUnplugin(
@@ -2506,26 +2669,31 @@ var unplugin = createUnplugin(
2506
2669
  console.log("\u2139\uFE0F Starting Lingo.dev compiler...");
2507
2670
  const params = _11.defaults(_params, defaultParams);
2508
2671
  if (!isRunningInCIOrDocker()) {
2509
- validateLLMKeyDetails(params.models);
2510
- }
2511
- const invalidLocales = getInvalidLocales(
2512
- params.models,
2513
- params.sourceLocale,
2514
- params.targetLocales
2515
- );
2516
- if (invalidLocales.length > 0) {
2517
- console.log(dedent3`
2518
- \n
2519
- ⚠️ Lingo.dev Localization Compiler requires LLM model setup for the following locales: ${invalidLocales.join(", ")}.
2520
-
2521
- ⭐️ Next steps:
2522
- 1. Refer to documentation for help: https://docs.lingo.dev/
2523
- 2. If you want to use a different LLM, raise an issue in our open-source repo: https://lingo.dev/go/gh
2524
- 3. If you have questions, feature requests, or would like to contribute, join our Discord: https://lingo.dev/go/discord
2525
-
2526
-
2527
- `);
2528
- process.exit(1);
2672
+ if (params.models === "lingo.dev") {
2673
+ validateLLMKeyDetails(["lingo.dev"]);
2674
+ } else {
2675
+ const configuredProviders = getConfiguredProviders(params.models);
2676
+ validateLLMKeyDetails(configuredProviders);
2677
+ const invalidLocales = getInvalidLocales(
2678
+ params.models,
2679
+ params.sourceLocale,
2680
+ params.targetLocales
2681
+ );
2682
+ if (invalidLocales.length > 0) {
2683
+ console.log(dedent3`
2684
+ \n
2685
+ ⚠️ Lingo.dev Localization Compiler requires LLM model setup for the following locales: ${invalidLocales.join(", ")}.
2686
+
2687
+ ⭐️ Next steps:
2688
+ 1. Refer to documentation for help: https://docs.lingo.dev/
2689
+ 2. If you want to use a different LLM, raise an issue in our open-source repo: https://lingo.dev/go/gh
2690
+ 3. If you have questions, feature requests, or would like to contribute, join our Discord: https://lingo.dev/go/discord
2691
+
2692
+
2693
+ `);
2694
+ process.exit(1);
2695
+ }
2696
+ }
2529
2697
  }
2530
2698
  LCPCache.ensureDictionaryFile({
2531
2699
  sourceRoot: params.sourceRoot,
@@ -2553,6 +2721,7 @@ var unplugin = createUnplugin(
2553
2721
  lingoDir: params.lingoDir
2554
2722
  });
2555
2723
  const dictionary = dictionaries[moduleInfo.params.locale];
2724
+ console.log(JSON.stringify(dictionary, null, 2));
2556
2725
  return {
2557
2726
  code: `export default ${JSON.stringify(dictionary, null, 2)}`
2558
2727
  };
@@ -2621,10 +2790,12 @@ var src_default = {
2621
2790
  return config2;
2622
2791
  }
2623
2792
  };
2624
- function validateLLMKeyDetails(models) {
2625
- const configuredProviders = _11.chain(Object.values(models)).map((modelString) => modelString.split(":")[0]).filter(Boolean).uniq().filter(
2793
+ function getConfiguredProviders(models) {
2794
+ return _11.chain(Object.values(models)).map((modelString) => modelString.split(":")[0]).filter(Boolean).uniq().filter(
2626
2795
  (providerId) => providerDetails.hasOwnProperty(providerId) && keyCheckers.hasOwnProperty(providerId)
2627
2796
  ).value();
2797
+ }
2798
+ function validateLLMKeyDetails(configuredProviders) {
2628
2799
  if (configuredProviders.length === 0) {
2629
2800
  return;
2630
2801
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lingo.dev/_compiler",
3
- "version": "0.2.4",
3
+ "version": "0.3.1",
4
4
  "description": "Lingo.dev Compiler",
5
5
  "private": false,
6
6
  "publishConfig": {
@@ -35,6 +35,7 @@
35
35
  "@babel/parser": "^7.26.7",
36
36
  "@babel/traverse": "^7.27.4",
37
37
  "@babel/types": "^7.26.7",
38
+ "@openrouter/ai-sdk-provider": "^0.7.1",
38
39
  "ai": "^4.2.10",
39
40
  "dedent": "^1.6.0",
40
41
  "dotenv": "^16.4.5",
@@ -42,10 +43,12 @@
42
43
  "ini": "^5.0.0",
43
44
  "lodash": "^4.17.21",
44
45
  "object-hash": "^3.0.0",
46
+ "ollama-ai-provider": "^1.2.0",
45
47
  "prettier": "^3.4.2",
46
48
  "unplugin": "^2.1.2",
47
49
  "vitest": "^2.1.4",
48
- "zod": "^3.24.1"
50
+ "zod": "^3.24.1",
51
+ "@lingo.dev/_sdk": "0.9.1"
49
52
  },
50
53
  "scripts": {
51
54
  "dev": "tsup --watch",