glotfile 0.1.1 → 0.2.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 CHANGED
@@ -8,7 +8,7 @@ to make.
8
8
 
9
9
  - **One source of truth** — every string and translation lives in `glotfile.json`, committed alongside your code. Versioning, review, and rollback come from git. For large catalogs, run `glotfile split` to store the catalog as a `glotfile/` directory with one file per locale — so a one-locale change is a one-file `git diff` instead of a multi-megabyte one. The in-app experience is identical.
10
10
  - **Local web UI** — run one command, edit in the browser, changes save straight back to the file.
11
- - **AI translation** — fill in missing languages with Anthropic, OpenAI, or AWS Bedrock, using per-key context, a glossary, and screenshots.
11
+ - **AI translation** — fill in missing languages with Anthropic, OpenAI, AWS Bedrock, or OpenRouter, using per-key context, a glossary, and screenshots.
12
12
  - **Export anywhere** — generate Flutter ARB, Laravel PHP, i18next JSON, gettext `.po`, and Apple `.stringsdict` from the same source.
13
13
 
14
14
  ---
@@ -181,9 +181,9 @@ in the project directory. For example:
181
181
  ANTHROPIC_API_KEY=sk-ant-...
182
182
  ```
183
183
 
184
- Three providers are supported — Anthropic (default), OpenAI, and AWS Bedrock
185
- (Amazon Nova, Claude, and Meta Llama). For the full setup of each — required
186
- env vars, model ids, regions, and the optional SDKs to install — see
184
+ Four providers are supported — Anthropic (default), OpenAI, AWS Bedrock
185
+ (Amazon Nova, Claude, and Meta Llama), and OpenRouter. For the full setup of
186
+ each — required env vars, model ids, regions, and the optional SDKs to install — see
187
187
  **[docs/ai-providers.md](docs/ai-providers.md)**.
188
188
 
189
189
  What the translator does for you:
@@ -277,7 +277,7 @@ var init_schema = __esm({
277
277
  PLURAL_CATEGORIES = ["zero", "one", "two", "few", "many", "other"];
278
278
  EXACT_SELECTOR_RE = /^=\d+$/;
279
279
  LOCALE_CASES = ["lower-hyphen", "lower-underscore", "bcp47-hyphen", "bcp47-underscore"];
280
- PROVIDERS = ["anthropic", "openai", "bedrock"];
280
+ PROVIDERS = ["anthropic", "openai", "bedrock", "openrouter"];
281
281
  GlotfileError = class extends Error {
282
282
  };
283
283
  }
@@ -1768,6 +1768,17 @@ var init_anthropic = __esm({
1768
1768
 
1769
1769
  // src/server/ai/openai.ts
1770
1770
  import { createRequire } from "module";
1771
+ function loadOpenAIClient(opts) {
1772
+ const require2 = createRequire(import.meta.url);
1773
+ let OpenAICtor;
1774
+ try {
1775
+ const mod = require2("openai");
1776
+ OpenAICtor = mod.OpenAI ?? mod.default ?? mod;
1777
+ } catch {
1778
+ throw new Error("The OpenAI SDK is required for this provider. Install it: npm i openai");
1779
+ }
1780
+ return new OpenAICtor(opts);
1781
+ }
1771
1782
  var OpenAIProvider;
1772
1783
  var init_openai = __esm({
1773
1784
  "src/server/ai/openai.ts"() {
@@ -1784,15 +1795,7 @@ var init_openai = __esm({
1784
1795
  if (!process.env.OPENAI_API_KEY) {
1785
1796
  throw new Error("OPENAI_API_KEY is not set. AI translation requires it; every other feature works offline.");
1786
1797
  }
1787
- const require2 = createRequire(import.meta.url);
1788
- let OpenAICtor;
1789
- try {
1790
- const mod = require2("openai");
1791
- OpenAICtor = mod.OpenAI ?? mod.default ?? mod;
1792
- } catch {
1793
- throw new Error('Provider "openai" requires the OpenAI SDK. Install it: npm i openai');
1794
- }
1795
- this.client = new OpenAICtor({ baseURL: config.endpoint ?? void 0 });
1798
+ this.client = loadOpenAIClient({ baseURL: config.endpoint ?? void 0 });
1796
1799
  }
1797
1800
  config;
1798
1801
  client;
@@ -1985,6 +1988,34 @@ var init_bedrock = __esm({
1985
1988
  }
1986
1989
  });
1987
1990
 
1991
+ // src/server/ai/openrouter.ts
1992
+ function openRouterClientOptions(config) {
1993
+ const apiKey = process.env.OPENROUTER_API_KEY;
1994
+ if (!apiKey) {
1995
+ throw new Error("OPENROUTER_API_KEY is not set. AI translation requires it; every other feature works offline.");
1996
+ }
1997
+ return {
1998
+ apiKey,
1999
+ baseURL: config.endpoint ?? OPENROUTER_BASE_URL,
2000
+ defaultHeaders: { "HTTP-Referer": OPENROUTER_REFERER, "X-Title": OPENROUTER_TITLE }
2001
+ };
2002
+ }
2003
+ var OPENROUTER_BASE_URL, OPENROUTER_REFERER, OPENROUTER_TITLE, OpenRouterProvider;
2004
+ var init_openrouter = __esm({
2005
+ "src/server/ai/openrouter.ts"() {
2006
+ "use strict";
2007
+ init_openai();
2008
+ OPENROUTER_BASE_URL = "https://openrouter.ai/api/v1";
2009
+ OPENROUTER_REFERER = "https://www.npmjs.com/package/glotfile";
2010
+ OPENROUTER_TITLE = "glotfile";
2011
+ OpenRouterProvider = class extends OpenAIProvider {
2012
+ constructor(config, client) {
2013
+ super(config, client ?? loadOpenAIClient(openRouterClientOptions(config)));
2014
+ }
2015
+ };
2016
+ }
2017
+ });
2018
+
1988
2019
  // src/server/ai/index.ts
1989
2020
  function makeProvider(config) {
1990
2021
  const ai = config.ai;
@@ -1995,8 +2026,10 @@ function makeProvider(config) {
1995
2026
  return new OpenAIProvider(ai);
1996
2027
  case "bedrock":
1997
2028
  return new BedrockProvider(ai);
2029
+ case "openrouter":
2030
+ return new OpenRouterProvider(ai);
1998
2031
  default:
1999
- throw new Error(`Unknown AI provider "${String(ai.provider)}". Supported: anthropic, openai, bedrock.`);
2032
+ throw new Error(`Unknown AI provider "${String(ai.provider)}". Supported: anthropic, openai, bedrock, openrouter.`);
2000
2033
  }
2001
2034
  }
2002
2035
  var init_ai = __esm({
@@ -2005,6 +2038,7 @@ var init_ai = __esm({
2005
2038
  init_anthropic();
2006
2039
  init_openai();
2007
2040
  init_bedrock();
2041
+ init_openrouter();
2008
2042
  }
2009
2043
  });
2010
2044
 
@@ -83,7 +83,7 @@ function isPluralForm(key) {
83
83
  return PLURAL_CATEGORIES.includes(key) || EXACT_SELECTOR_RE.test(key);
84
84
  }
85
85
  var LOCALE_CASES = ["lower-hyphen", "lower-underscore", "bcp47-hyphen", "bcp47-underscore"];
86
- var PROVIDERS = ["anthropic", "openai", "bedrock"];
86
+ var PROVIDERS = ["anthropic", "openai", "bedrock", "openrouter"];
87
87
  var GlotfileError = class extends Error {
88
88
  };
89
89
  function isObject(v) {
@@ -2517,6 +2517,17 @@ var AnthropicProvider = class {
2517
2517
 
2518
2518
  // src/server/ai/openai.ts
2519
2519
  import { createRequire } from "module";
2520
+ function loadOpenAIClient(opts) {
2521
+ const require2 = createRequire(import.meta.url);
2522
+ let OpenAICtor;
2523
+ try {
2524
+ const mod = require2("openai");
2525
+ OpenAICtor = mod.OpenAI ?? mod.default ?? mod;
2526
+ } catch {
2527
+ throw new Error("The OpenAI SDK is required for this provider. Install it: npm i openai");
2528
+ }
2529
+ return new OpenAICtor(opts);
2530
+ }
2520
2531
  var OpenAIProvider = class {
2521
2532
  constructor(config, client) {
2522
2533
  this.config = config;
@@ -2527,15 +2538,7 @@ var OpenAIProvider = class {
2527
2538
  if (!process.env.OPENAI_API_KEY) {
2528
2539
  throw new Error("OPENAI_API_KEY is not set. AI translation requires it; every other feature works offline.");
2529
2540
  }
2530
- const require2 = createRequire(import.meta.url);
2531
- let OpenAICtor;
2532
- try {
2533
- const mod = require2("openai");
2534
- OpenAICtor = mod.OpenAI ?? mod.default ?? mod;
2535
- } catch {
2536
- throw new Error('Provider "openai" requires the OpenAI SDK. Install it: npm i openai');
2537
- }
2538
- this.client = new OpenAICtor({ baseURL: config.endpoint ?? void 0 });
2541
+ this.client = loadOpenAIClient({ baseURL: config.endpoint ?? void 0 });
2539
2542
  }
2540
2543
  config;
2541
2544
  client;
@@ -2718,6 +2721,27 @@ var BedrockProvider = class {
2718
2721
  }
2719
2722
  };
2720
2723
 
2724
+ // src/server/ai/openrouter.ts
2725
+ var OPENROUTER_BASE_URL = "https://openrouter.ai/api/v1";
2726
+ var OPENROUTER_REFERER = "https://www.npmjs.com/package/glotfile";
2727
+ var OPENROUTER_TITLE = "glotfile";
2728
+ function openRouterClientOptions(config) {
2729
+ const apiKey = process.env.OPENROUTER_API_KEY;
2730
+ if (!apiKey) {
2731
+ throw new Error("OPENROUTER_API_KEY is not set. AI translation requires it; every other feature works offline.");
2732
+ }
2733
+ return {
2734
+ apiKey,
2735
+ baseURL: config.endpoint ?? OPENROUTER_BASE_URL,
2736
+ defaultHeaders: { "HTTP-Referer": OPENROUTER_REFERER, "X-Title": OPENROUTER_TITLE }
2737
+ };
2738
+ }
2739
+ var OpenRouterProvider = class extends OpenAIProvider {
2740
+ constructor(config, client) {
2741
+ super(config, client ?? loadOpenAIClient(openRouterClientOptions(config)));
2742
+ }
2743
+ };
2744
+
2721
2745
  // src/server/ai/index.ts
2722
2746
  function makeProvider(config) {
2723
2747
  const ai = config.ai;
@@ -2728,8 +2752,10 @@ function makeProvider(config) {
2728
2752
  return new OpenAIProvider(ai);
2729
2753
  case "bedrock":
2730
2754
  return new BedrockProvider(ai);
2755
+ case "openrouter":
2756
+ return new OpenRouterProvider(ai);
2731
2757
  default:
2732
- throw new Error(`Unknown AI provider "${String(ai.provider)}". Supported: anthropic, openai, bedrock.`);
2758
+ throw new Error(`Unknown AI provider "${String(ai.provider)}". Supported: anthropic, openai, bedrock, openrouter.`);
2733
2759
  }
2734
2760
  }
2735
2761