better-translation 0.2.2 → 0.2.3

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/dist/ai.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { d as TranslateFn } from "./types-DxcBf0RB.mjs";
1
+ import { c as TranslateFn } from "./types-C3EkBdeb.mjs";
2
2
  import { generateText } from "ai";
3
3
 
4
4
  //#region src/ai.d.ts
@@ -10,6 +10,8 @@ interface CreateAiTranslateOptions {
10
10
  prompt?: string;
11
11
  /** Optional temperature forwarded to the selected model provider. */
12
12
  temperature?: number;
13
+ /** Maximum number of per-message translation requests to run at once. */
14
+ concurrency?: number;
13
15
  }
14
16
  declare function createAiTranslate(options?: CreateAiTranslateOptions): TranslateFn;
15
17
  //#endregion
package/dist/ai.d.mts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"ai.d.mts","names":[],"sources":["../src/ai.ts"],"mappings":";;;;KAKK,OAAA,GAAU,UAAU,QAAQ,YAAA;AAAA,UAEhB,wBAAA;EAFZ;EAIH,KAAA,GAAQ,OAAO;;EAEf,MAAA;EAN2C;EAQ3C,WAAA;AAAA;AAAA,iBAGc,iBAAA,CAAkB,OAAA,GAAS,wBAAA,GAAgC,WAAW"}
1
+ {"version":3,"file":"ai.d.mts","names":[],"sources":["../src/ai.ts"],"mappings":";;;;KAMK,OAAA,GAAU,UAAU,QAAQ,YAAA;AAAA,UAEhB,wBAAA;EAFZ;EAIH,KAAA,GAAQ,OAAO;;EAEf,MAAA;EAN2C;EAQ3C,WAAA;EANuC;EAQvC,WAAA;AAAA;AAAA,iBAGc,iBAAA,CAAkB,OAAA,GAAS,wBAAA,GAAgC,WAAW"}
package/dist/ai.mjs CHANGED
@@ -1,12 +1,23 @@
1
1
  //#region src/ai.ts
2
2
  const DEFAULT_GATEWAY_MODEL = "openai/gpt-5.5";
3
+ const DEFAULT_CONCURRENCY = 10;
3
4
  function createAiTranslate(options = {}) {
4
5
  return async (messages, locale) => {
5
6
  const result = {};
6
- for (const message of messages) result[message.id] = await translateMessage(message, locale, options);
7
+ const concurrency = normalizeConcurrency(options.concurrency);
8
+ await Promise.all(Array.from({ length: Math.min(concurrency, messages.length) }, async (_, workerIndex) => {
9
+ for (let index = workerIndex; index < messages.length; index += concurrency) {
10
+ const message = messages[index];
11
+ result[message.id] = await translateMessage(message, locale, options);
12
+ }
13
+ }));
7
14
  return result;
8
15
  };
9
16
  }
17
+ function normalizeConcurrency(concurrency = DEFAULT_CONCURRENCY) {
18
+ if (!Number.isFinite(concurrency)) return DEFAULT_CONCURRENCY;
19
+ return Math.max(1, Math.floor(concurrency));
20
+ }
10
21
  async function translateMessage(message, locale, options) {
11
22
  return (await translateWithAi(message, locale, options)).trim() || message.text;
12
23
  }
@@ -29,7 +40,7 @@ ${locale}
29
40
 
30
41
  ## Output Contract
31
42
  Return only the translated text for the provided source message.
32
- Do not include the message id, labels, explanations, markdown, code fences, or surrounding quotes.
43
+ Do not include the lookup id, labels, explanations, markdown, code fences, or surrounding quotes.
33
44
  Use the message context when provided.`].join("\n\n");
34
45
  }
35
46
  function createUserPrompt(message, locale) {
package/dist/ai.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"ai.mjs","names":[],"sources":["../src/ai.ts"],"sourcesContent":["import type { generateText } from \"ai\"\n\nimport type { TranslateFn, TranslateMessage } from \"./types.js\"\n\nconst DEFAULT_GATEWAY_MODEL = \"openai/gpt-5.5\"\ntype AiModel = Parameters<typeof generateText>[0][\"model\"]\n\nexport interface CreateAiTranslateOptions {\n /** AI SDK model value. Defaults to a Vercel AI Gateway model string. */\n model?: AiModel\n /** Primary translation brief for product, tone, glossary, or domain instructions. */\n prompt?: string\n /** Optional temperature forwarded to the selected model provider. */\n temperature?: number\n}\n\nexport function createAiTranslate(options: CreateAiTranslateOptions = {}): TranslateFn {\n return async (messages, locale) => {\n const result: Record<string, string> = {}\n\n for (const message of messages) {\n result[message.id] = await translateMessage(message, locale, options)\n }\n\n return result\n }\n}\n\nasync function translateMessage(message: TranslateMessage, locale: string, options: CreateAiTranslateOptions) {\n const translated = (await translateWithAi(message, locale, options)).trim()\n\n return translated || message.text\n}\n\nasync function translateWithAi(message: TranslateMessage, locale: string, options: CreateAiTranslateOptions) {\n const { generateText } = await import(\"ai\")\n const { text } = await generateText({\n model: options.model ?? DEFAULT_GATEWAY_MODEL,\n system: createSystemPrompt(locale, options.prompt),\n prompt: createUserPrompt(message, locale),\n ...(options.temperature === undefined ? {} : { temperature: options.temperature }),\n })\n\n return text\n}\n\nfunction createSystemPrompt(locale: string, prompt?: string) {\n const translationBrief = prompt?.trim() || \"Translate the provided UI messages as concise, natural application UI copy.\"\n\n return [\n `## Translation Brief\n${translationBrief}\n\n## Target Locale\n${locale}\n\n## Output Contract\nReturn only the translated text for the provided source message.\nDo not include the message id, labels, explanations, markdown, code fences, or surrounding quotes.\nUse the message context when provided.`,\n ].join(\"\\n\\n\")\n}\n\nfunction createUserPrompt(message: TranslateMessage, locale: string) {\n return JSON.stringify({\n targetLocale: locale,\n message: {\n id: message.id,\n text: message.text,\n context: message.meta.context,\n },\n })\n}\n"],"mappings":";AAIA,MAAM,wBAAwB;AAY9B,SAAgB,kBAAkB,UAAoC,CAAC,GAAgB;CACrF,OAAO,OAAO,UAAU,WAAW;EACjC,MAAM,SAAiC,CAAC;EAExC,KAAK,MAAM,WAAW,UACpB,OAAO,QAAQ,MAAM,MAAM,iBAAiB,SAAS,QAAQ,OAAO;EAGtE,OAAO;CACT;AACF;AAEA,eAAe,iBAAiB,SAA2B,QAAgB,SAAmC;CAG5G,QAFoB,MAAM,gBAAgB,SAAS,QAAQ,OAAO,GAAG,KAErD,KAAK,QAAQ;AAC/B;AAEA,eAAe,gBAAgB,SAA2B,QAAgB,SAAmC;CAC3G,MAAM,EAAE,iBAAiB,MAAM,OAAO;CACtC,MAAM,EAAE,SAAS,MAAM,aAAa;EAClC,OAAO,QAAQ,SAAS;EACxB,QAAQ,mBAAmB,QAAQ,QAAQ,MAAM;EACjD,QAAQ,iBAAiB,SAAS,MAAM;EACxC,GAAI,QAAQ,gBAAgB,KAAA,IAAY,CAAC,IAAI,EAAE,aAAa,QAAQ,YAAY;CAClF,CAAC;CAED,OAAO;AACT;AAEA,SAAS,mBAAmB,QAAgB,QAAiB;CAG3D,OAAO,CACL;EAHuB,QAAQ,KAAK,KAAK,8EAI1B;;;EAGjB,OAAO;;;;;uCAMP,EAAE,KAAK,MAAM;AACf;AAEA,SAAS,iBAAiB,SAA2B,QAAgB;CACnE,OAAO,KAAK,UAAU;EACpB,cAAc;EACd,SAAS;GACP,IAAI,QAAQ;GACZ,MAAM,QAAQ;GACd,SAAS,QAAQ,KAAK;EACxB;CACF,CAAC;AACH"}
1
+ {"version":3,"file":"ai.mjs","names":[],"sources":["../src/ai.ts"],"sourcesContent":["import type { generateText } from \"ai\"\n\nimport type { TranslateFn, TranslateMessage } from \"./types.js\"\n\nconst DEFAULT_GATEWAY_MODEL = \"openai/gpt-5.5\"\nconst DEFAULT_CONCURRENCY = 10\ntype AiModel = Parameters<typeof generateText>[0][\"model\"]\n\nexport interface CreateAiTranslateOptions {\n /** AI SDK model value. Defaults to a Vercel AI Gateway model string. */\n model?: AiModel\n /** Primary translation brief for product, tone, glossary, or domain instructions. */\n prompt?: string\n /** Optional temperature forwarded to the selected model provider. */\n temperature?: number\n /** Maximum number of per-message translation requests to run at once. */\n concurrency?: number\n}\n\nexport function createAiTranslate(options: CreateAiTranslateOptions = {}): TranslateFn {\n return async (messages, locale) => {\n const result: Record<string, string> = {}\n const concurrency = normalizeConcurrency(options.concurrency)\n\n await Promise.all(\n Array.from({ length: Math.min(concurrency, messages.length) }, async (_, workerIndex) => {\n for (let index = workerIndex; index < messages.length; index += concurrency) {\n const message = messages[index]!\n result[message.id] = await translateMessage(message, locale, options)\n }\n }),\n )\n\n return result\n }\n}\n\nfunction normalizeConcurrency(concurrency = DEFAULT_CONCURRENCY) {\n if (!Number.isFinite(concurrency)) return DEFAULT_CONCURRENCY\n return Math.max(1, Math.floor(concurrency))\n}\n\nasync function translateMessage(message: TranslateMessage, locale: string, options: CreateAiTranslateOptions) {\n const translated = (await translateWithAi(message, locale, options)).trim()\n\n return translated || message.text\n}\n\nasync function translateWithAi(message: TranslateMessage, locale: string, options: CreateAiTranslateOptions) {\n const { generateText } = await import(\"ai\")\n const { text } = await generateText({\n model: options.model ?? DEFAULT_GATEWAY_MODEL,\n system: createSystemPrompt(locale, options.prompt),\n prompt: createUserPrompt(message, locale),\n ...(options.temperature === undefined ? {} : { temperature: options.temperature }),\n })\n\n return text\n}\n\nfunction createSystemPrompt(locale: string, prompt?: string) {\n const translationBrief = prompt?.trim() || \"Translate the provided UI messages as concise, natural application UI copy.\"\n\n return [\n `## Translation Brief\n${translationBrief}\n\n## Target Locale\n${locale}\n\n## Output Contract\nReturn only the translated text for the provided source message.\nDo not include the lookup id, labels, explanations, markdown, code fences, or surrounding quotes.\nUse the message context when provided.`,\n ].join(\"\\n\\n\")\n}\n\nfunction createUserPrompt(message: TranslateMessage, locale: string) {\n return JSON.stringify({\n targetLocale: locale,\n message: {\n id: message.id,\n text: message.text,\n context: message.meta.context,\n },\n })\n}\n"],"mappings":";AAIA,MAAM,wBAAwB;AAC9B,MAAM,sBAAsB;AAc5B,SAAgB,kBAAkB,UAAoC,CAAC,GAAgB;CACrF,OAAO,OAAO,UAAU,WAAW;EACjC,MAAM,SAAiC,CAAC;EACxC,MAAM,cAAc,qBAAqB,QAAQ,WAAW;EAE5D,MAAM,QAAQ,IACZ,MAAM,KAAK,EAAE,QAAQ,KAAK,IAAI,aAAa,SAAS,MAAM,EAAE,GAAG,OAAO,GAAG,gBAAgB;GACvF,KAAK,IAAI,QAAQ,aAAa,QAAQ,SAAS,QAAQ,SAAS,aAAa;IAC3E,MAAM,UAAU,SAAS;IACzB,OAAO,QAAQ,MAAM,MAAM,iBAAiB,SAAS,QAAQ,OAAO;GACtE;EACF,CAAC,CACH;EAEA,OAAO;CACT;AACF;AAEA,SAAS,qBAAqB,cAAc,qBAAqB;CAC/D,IAAI,CAAC,OAAO,SAAS,WAAW,GAAG,OAAO;CAC1C,OAAO,KAAK,IAAI,GAAG,KAAK,MAAM,WAAW,CAAC;AAC5C;AAEA,eAAe,iBAAiB,SAA2B,QAAgB,SAAmC;CAG5G,QAFoB,MAAM,gBAAgB,SAAS,QAAQ,OAAO,GAAG,KAErD,KAAK,QAAQ;AAC/B;AAEA,eAAe,gBAAgB,SAA2B,QAAgB,SAAmC;CAC3G,MAAM,EAAE,iBAAiB,MAAM,OAAO;CACtC,MAAM,EAAE,SAAS,MAAM,aAAa;EAClC,OAAO,QAAQ,SAAS;EACxB,QAAQ,mBAAmB,QAAQ,QAAQ,MAAM;EACjD,QAAQ,iBAAiB,SAAS,MAAM;EACxC,GAAI,QAAQ,gBAAgB,KAAA,IAAY,CAAC,IAAI,EAAE,aAAa,QAAQ,YAAY;CAClF,CAAC;CAED,OAAO;AACT;AAEA,SAAS,mBAAmB,QAAgB,QAAiB;CAG3D,OAAO,CACL;EAHuB,QAAQ,KAAK,KAAK,8EAI1B;;;EAGjB,OAAO;;;;;uCAMP,EAAE,KAAK,MAAM;AACf;AAEA,SAAS,iBAAiB,SAA2B,QAAgB;CACnE,OAAO,KAAK,UAAU;EACpB,cAAc;EACd,SAAS;GACP,IAAI,QAAQ;GACZ,MAAM,QAAQ;GACd,SAAS,QAAQ,KAAK;EACxB;CACF,CAAC;AACH"}
@@ -12,7 +12,7 @@ function getMessageIdentity(message, meta) {
12
12
  const serializedMeta = serializeMeta(meta);
13
13
  return serializedMeta ? `${message}\0${serializedMeta}` : message;
14
14
  }
15
- /** Generates the stable hashed id used to store and look up a translated message. */
15
+ /** Generates the stable hashed lookup id used to store and look up a translated message. */
16
16
  function getMessageId(message, meta) {
17
17
  const value = getMessageIdentity(message, meta);
18
18
  let hash = 2166136261;
@@ -29,4 +29,4 @@ function getCallMessageId(message, options) {
29
29
  //#endregion
30
30
  export { getMessageId as n, serializeMeta as r, getCallMessageId as t };
31
31
 
32
- //# sourceMappingURL=message-id-B8zJjADZ.mjs.map
32
+ //# sourceMappingURL=message-id-DGUI8H5w.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"message-id-DGUI8H5w.mjs","names":[],"sources":["../src/message-id.ts"],"sourcesContent":["import type { TranslateOptions } from \"./types.js\"\n\nexport function normalizeMeta(meta?: TranslateOptions): TranslateOptions {\n if (!meta) return {}\n\n return Object.fromEntries(\n Object.entries(meta)\n .filter(([key, value]) => key !== \"id\" && value !== undefined)\n .sort(([a], [b]) => a.localeCompare(b)),\n ) as TranslateOptions\n}\n\nexport function serializeMeta(meta?: TranslateOptions) {\n if (!meta) return \"\"\n\n const normalized = normalizeMeta(meta)\n return Object.keys(normalized).length > 0 ? JSON.stringify(normalized) : \"\"\n}\n\nexport function getMessageIdentity(message: string, meta?: TranslateOptions) {\n const serializedMeta = serializeMeta(meta)\n return serializedMeta ? `${message}\\0${serializedMeta}` : message\n}\n\n/** Generates the stable hashed lookup id used to store and look up a translated message. */\nexport function getMessageId(message: string, meta?: TranslateOptions) {\n const value = getMessageIdentity(message, meta)\n let hash = 2166136261\n\n for (let i = 0; i < value.length; i++) {\n hash ^= value.charCodeAt(i)\n hash = Math.imul(hash, 16777619)\n }\n\n return `m_${(hash >>> 0).toString(36)}`\n}\n\n/** Resolves the lookup id for function-style `t()` calls, preferring an explicit `options.id`. */\nexport function getCallMessageId(message: string, options?: TranslateOptions) {\n return options?.id ?? getMessageId(message, options)\n}\n"],"mappings":";AAEA,SAAgB,cAAc,MAA2C;CACvE,IAAI,CAAC,MAAM,OAAO,CAAC;CAEnB,OAAO,OAAO,YACZ,OAAO,QAAQ,IAAI,EAChB,QAAQ,CAAC,KAAK,WAAW,QAAQ,QAAQ,UAAU,KAAA,CAAS,EAC5D,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC,CAC1C;AACF;AAEA,SAAgB,cAAc,MAAyB;CACrD,IAAI,CAAC,MAAM,OAAO;CAElB,MAAM,aAAa,cAAc,IAAI;CACrC,OAAO,OAAO,KAAK,UAAU,EAAE,SAAS,IAAI,KAAK,UAAU,UAAU,IAAI;AAC3E;AAEA,SAAgB,mBAAmB,SAAiB,MAAyB;CAC3E,MAAM,iBAAiB,cAAc,IAAI;CACzC,OAAO,iBAAiB,GAAG,QAAQ,IAAI,mBAAmB;AAC5D;;AAGA,SAAgB,aAAa,SAAiB,MAAyB;CACrE,MAAM,QAAQ,mBAAmB,SAAS,IAAI;CAC9C,IAAI,OAAO;CAEX,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;EACrC,QAAQ,MAAM,WAAW,CAAC;EAC1B,OAAO,KAAK,KAAK,MAAM,QAAQ;CACjC;CAEA,OAAO,MAAM,SAAS,GAAG,SAAS,EAAE;AACtC;;AAGA,SAAgB,iBAAiB,SAAiB,SAA4B;CAC5E,OAAO,SAAS,MAAM,aAAa,SAAS,OAAO;AACrD"}
@@ -1,4 +1,4 @@
1
- import { u as RuntimeMessages } from "./types-DxcBf0RB.mjs";
1
+ import { s as RuntimeMessages } from "./types-C3EkBdeb.mjs";
2
2
 
3
3
  //#region src/messages.d.ts
4
4
  declare const locales: readonly string[];
package/dist/react.d.mts CHANGED
@@ -1,10 +1,10 @@
1
- import { p as TranslateOptions } from "./types-DxcBf0RB.mjs";
1
+ import { u as TranslateOptions } from "./types-C3EkBdeb.mjs";
2
2
  import { ReactNode } from "react";
3
3
 
4
4
  //#region src/react.d.ts
5
5
  /** Props for `TranslateProvider`. */
6
6
  interface TranslateProviderProps {
7
- /** Flattened locale message map keyed by stable message id. */
7
+ /** Flattened locale message map keyed by stable lookup id. */
8
8
  messages: Record<string, string>;
9
9
  /** React subtree that should have access to translations. */
10
10
  children: ReactNode;
@@ -28,7 +28,7 @@ type VarProps = {
28
28
  declare function Var(props: VarProps): import("react/jsx-runtime").JSX.Element;
29
29
  /** Props for `T`. */
30
30
  interface TProps {
31
- /** Explicit stable id to use instead of hashing the rendered source text, whether provided manually or by a transform. */
31
+ /** Explicit stable lookup id to use instead of hashing the rendered source text, whether provided manually or by a transform. */
32
32
  id?: string;
33
33
  /** Extra disambiguating context for translators and custom grouping. */
34
34
  context?: string;
package/dist/react.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { n as getMessageId, t as getCallMessageId } from "./message-id-B8zJjADZ.mjs";
1
+ import { n as getMessageId, t as getCallMessageId } from "./message-id-DGUI8H5w.mjs";
2
2
  import { Children, createContext, isValidElement, use, useMemo } from "react";
3
3
  import { Fragment, jsx } from "react/jsx-runtime";
4
4
  //#region src/react.tsx
@@ -1 +1 @@
1
- {"version":3,"file":"react.mjs","names":[],"sources":["../src/react.tsx"],"sourcesContent":["import { Children, createContext, isValidElement, use, useMemo, type ReactNode } from \"react\"\n\nimport type { TranslateOptions } from \"./types.js\"\n\nimport { getCallMessageId, getMessageId } from \"./message-id.js\"\n\ninterface TranslateContextValue {\n messages: Record<string, string>\n}\n\nconst TranslateContext = createContext<TranslateContextValue>({ messages: {} })\n\n/** Props for `TranslateProvider`. */\nexport interface TranslateProviderProps {\n /** Flattened locale message map keyed by stable message id. */\n messages: Record<string, string>\n /** React subtree that should have access to translations. */\n children: ReactNode\n}\n\n/** Provides translated messages to React components below it. */\nexport function TranslateProvider({ messages, children }: TranslateProviderProps) {\n const value = useMemo(() => ({ messages }), [messages])\n return <TranslateContext.Provider value={value}>{children}</TranslateContext.Provider>\n}\n\n/** Returns the raw locale message map from the current provider. */\nexport function useMessages() {\n return use(TranslateContext).messages\n}\n\ntype MessageValues = Record<string, unknown>\ntype TranslateFn = (message: string, valuesOrOptions?: MessageValues | TranslateOptions, options?: TranslateOptions) => string\n\n/** Returns a translator function for text used in props, labels, and other non-JSX positions. */\nexport function useT(): TranslateFn {\n const { messages } = use(TranslateContext)\n return useMemo<TranslateFn>(\n () => (message, valuesOrOptions, options) => {\n const values = isTranslateOptions(valuesOrOptions) ? undefined : normalizeValues(valuesOrOptions)\n const resolvedOptions = isTranslateOptions(valuesOrOptions) ? valuesOrOptions : options\n const template = messages[getCallMessageId(message, resolvedOptions)] ?? message\n if (!values) return template\n return template.replace(/\\{(\\w+)\\}/g, (_, name: string) => values[name] ?? `{${name}}`)\n },\n [messages],\n )\n}\n\n/** Props for `Var`. */\nexport type VarProps = {\n /** Optional shorthand child form, normalized at build time for simple identifiers. */\n children?: ReactNode\n} & Record<string, ReactNode | undefined>\n\n/** Marks a runtime value for placeholder interpolation inside `<T>` content. */\nexport function Var(props: VarProps) {\n return <>{getRuntimeVarEntry(props)?.value ?? props.children}</>\n}\n\n/** Props for `T`. */\nexport interface TProps {\n /** Explicit stable id to use instead of hashing the rendered source text, whether provided manually or by a transform. */\n id?: string\n /** Extra disambiguating context for translators and custom grouping. */\n context?: string\n /** Source-language JSX content to translate. */\n children?: ReactNode\n}\n\n/** Renders translated JSX content and supports placeholders through `<Var>`. */\nexport function T({ id, context, children }: TProps) {\n const { messages } = use(TranslateContext)\n const resolvedMeta = context ? { context } : undefined\n const runtimeContent = useMemo(() => extractRuntimeContent(children), [children])\n const template = messages[id ?? (runtimeContent.message ? getMessageId(runtimeContent.message, resolvedMeta) : \"\")]\n const vars = template?.includes(\"{\") ? runtimeContent.vars : undefined\n const interpolated = useMemo(() => interpolate(template, vars), [template, vars])\n\n if (!template) return <>{children}</>\n if (!vars) return <>{template}</>\n if (!interpolated.length) return <>{children}</>\n return <>{interpolated}</>\n}\n\nfunction interpolate(template?: string, vars?: Record<string, ReactNode>): ReactNode[] {\n if (!template || !vars) return []\n const result: ReactNode[] = []\n const re = /\\{(\\w+)\\}/g\n let lastIndex = 0\n let match: RegExpExecArray | null\n\n while ((match = re.exec(template)) !== null) {\n if (match.index > lastIndex) result.push(template.slice(lastIndex, match.index))\n result.push(vars[match[1]!] ?? `{${match[1]}}`)\n lastIndex = re.lastIndex\n }\n\n if (lastIndex < template.length) result.push(template.slice(lastIndex))\n return result\n}\n\nfunction extractRuntimeContent(children: ReactNode) {\n const parts: string[] = []\n const vars: Record<string, ReactNode> = {}\n\n Children.forEach(children, (child) => {\n if (typeof child === \"string\" || typeof child === \"number\") {\n parts.push(String(child))\n return\n }\n\n if (isValidElement<VarProps>(child) && child.type === Var) {\n const entry = getRuntimeVarEntry(child.props)\n if (entry) {\n parts.push(`{${entry.name}}`)\n vars[entry.name] = entry.value\n }\n }\n })\n\n return {\n message: parts.join(\"\").replace(/\\s+/g, \" \").trim(),\n vars: Object.keys(vars).length > 0 ? vars : undefined,\n }\n}\n\nfunction getRuntimeVarEntry(props: VarProps) {\n if (typeof props.name === \"string\" && props.children !== undefined) {\n return { name: props.name, value: props.children }\n }\n\n const entries = Object.entries(props).filter(([key]) => key !== \"children\")\n if (entries.length !== 1) return undefined\n\n const [name, value] = entries[0]!\n return { name, value }\n}\n\nfunction isTranslateOptions(value?: MessageValues | TranslateOptions): value is TranslateOptions {\n if (!value || Array.isArray(value)) return false\n return Object.keys(value).every((key) => key === \"id\" || key === \"context\")\n}\n\nfunction normalizeValues(values?: MessageValues) {\n if (!values) return undefined\n const entries = Object.entries(values).map(([name, value]) => [name, String(value)] as const)\n return entries.length > 0 ? Object.fromEntries(entries) : undefined\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"react.mjs","names":[],"sources":["../src/react.tsx"],"sourcesContent":["import { Children, createContext, isValidElement, use, useMemo, type ReactNode } from \"react\"\n\nimport type { TranslateOptions } from \"./types.js\"\n\nimport { getCallMessageId, getMessageId } from \"./message-id.js\"\n\ninterface TranslateContextValue {\n messages: Record<string, string>\n}\n\nconst TranslateContext = createContext<TranslateContextValue>({ messages: {} })\n\n/** Props for `TranslateProvider`. */\nexport interface TranslateProviderProps {\n /** Flattened locale message map keyed by stable lookup id. */\n messages: Record<string, string>\n /** React subtree that should have access to translations. */\n children: ReactNode\n}\n\n/** Provides translated messages to React components below it. */\nexport function TranslateProvider({ messages, children }: TranslateProviderProps) {\n const value = useMemo(() => ({ messages }), [messages])\n return <TranslateContext.Provider value={value}>{children}</TranslateContext.Provider>\n}\n\n/** Returns the raw locale message map from the current provider. */\nexport function useMessages() {\n return use(TranslateContext).messages\n}\n\ntype MessageValues = Record<string, unknown>\ntype TranslateFn = (message: string, valuesOrOptions?: MessageValues | TranslateOptions, options?: TranslateOptions) => string\n\n/** Returns a translator function for text used in props, labels, and other non-JSX positions. */\nexport function useT(): TranslateFn {\n const { messages } = use(TranslateContext)\n return useMemo<TranslateFn>(\n () => (message, valuesOrOptions, options) => {\n const values = isTranslateOptions(valuesOrOptions) ? undefined : normalizeValues(valuesOrOptions)\n const resolvedOptions = isTranslateOptions(valuesOrOptions) ? valuesOrOptions : options\n const template = messages[getCallMessageId(message, resolvedOptions)] ?? message\n if (!values) return template\n return template.replace(/\\{(\\w+)\\}/g, (_, name: string) => values[name] ?? `{${name}}`)\n },\n [messages],\n )\n}\n\n/** Props for `Var`. */\nexport type VarProps = {\n /** Optional shorthand child form, normalized at build time for simple identifiers. */\n children?: ReactNode\n} & Record<string, ReactNode | undefined>\n\n/** Marks a runtime value for placeholder interpolation inside `<T>` content. */\nexport function Var(props: VarProps) {\n return <>{getRuntimeVarEntry(props)?.value ?? props.children}</>\n}\n\n/** Props for `T`. */\nexport interface TProps {\n /** Explicit stable lookup id to use instead of hashing the rendered source text, whether provided manually or by a transform. */\n id?: string\n /** Extra disambiguating context for translators and custom grouping. */\n context?: string\n /** Source-language JSX content to translate. */\n children?: ReactNode\n}\n\n/** Renders translated JSX content and supports placeholders through `<Var>`. */\nexport function T({ id, context, children }: TProps) {\n const { messages } = use(TranslateContext)\n const resolvedMeta = context ? { context } : undefined\n const runtimeContent = useMemo(() => extractRuntimeContent(children), [children])\n const template = messages[id ?? (runtimeContent.message ? getMessageId(runtimeContent.message, resolvedMeta) : \"\")]\n const vars = template?.includes(\"{\") ? runtimeContent.vars : undefined\n const interpolated = useMemo(() => interpolate(template, vars), [template, vars])\n\n if (!template) return <>{children}</>\n if (!vars) return <>{template}</>\n if (!interpolated.length) return <>{children}</>\n return <>{interpolated}</>\n}\n\nfunction interpolate(template?: string, vars?: Record<string, ReactNode>): ReactNode[] {\n if (!template || !vars) return []\n const result: ReactNode[] = []\n const re = /\\{(\\w+)\\}/g\n let lastIndex = 0\n let match: RegExpExecArray | null\n\n while ((match = re.exec(template)) !== null) {\n if (match.index > lastIndex) result.push(template.slice(lastIndex, match.index))\n result.push(vars[match[1]!] ?? `{${match[1]}}`)\n lastIndex = re.lastIndex\n }\n\n if (lastIndex < template.length) result.push(template.slice(lastIndex))\n return result\n}\n\nfunction extractRuntimeContent(children: ReactNode) {\n const parts: string[] = []\n const vars: Record<string, ReactNode> = {}\n\n Children.forEach(children, (child) => {\n if (typeof child === \"string\" || typeof child === \"number\") {\n parts.push(String(child))\n return\n }\n\n if (isValidElement<VarProps>(child) && child.type === Var) {\n const entry = getRuntimeVarEntry(child.props)\n if (entry) {\n parts.push(`{${entry.name}}`)\n vars[entry.name] = entry.value\n }\n }\n })\n\n return {\n message: parts.join(\"\").replace(/\\s+/g, \" \").trim(),\n vars: Object.keys(vars).length > 0 ? vars : undefined,\n }\n}\n\nfunction getRuntimeVarEntry(props: VarProps) {\n if (typeof props.name === \"string\" && props.children !== undefined) {\n return { name: props.name, value: props.children }\n }\n\n const entries = Object.entries(props).filter(([key]) => key !== \"children\")\n if (entries.length !== 1) return undefined\n\n const [name, value] = entries[0]!\n return { name, value }\n}\n\nfunction isTranslateOptions(value?: MessageValues | TranslateOptions): value is TranslateOptions {\n if (!value || Array.isArray(value)) return false\n return Object.keys(value).every((key) => key === \"id\" || key === \"context\")\n}\n\nfunction normalizeValues(values?: MessageValues) {\n if (!values) return undefined\n const entries = Object.entries(values).map(([name, value]) => [name, String(value)] as const)\n return entries.length > 0 ? Object.fromEntries(entries) : undefined\n}\n"],"mappings":"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"}
package/dist/server.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { p as TranslateOptions } from "./types-DxcBf0RB.mjs";
1
+ import { u as TranslateOptions } from "./types-C3EkBdeb.mjs";
2
2
 
3
3
  //#region src/server.d.ts
4
4
  type MessageValues = Record<string, unknown>;
package/dist/server.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { t as getCallMessageId } from "./message-id-B8zJjADZ.mjs";
1
+ import { t as getCallMessageId } from "./message-id-DGUI8H5w.mjs";
2
2
  //#region src/server.ts
3
3
  /** Creates a lightweight server-side translator from a loaded message map. */
4
4
  function createTranslator(messages) {
@@ -31,22 +31,22 @@ interface ManifestEntry {
31
31
  /** Source locations that contributed to this manifest entry. */
32
32
  sources: MessageSource[];
33
33
  }
34
- /** In-memory manifest keyed by stable message id. */
34
+ /** In-memory manifest keyed by stable lookup id. */
35
35
  type MessageManifest = Record<string, ManifestEntry>;
36
- /** Private on-disk manifest keyed by stable message id. */
36
+ /** Private on-disk manifest keyed by stable lookup id. */
37
37
  type MessageManifestFile = MessageManifest;
38
- /** Flat runtime message map keyed by stable message id. */
38
+ /** Flat runtime message map keyed by stable lookup id. */
39
39
  type RuntimeMessages = Record<string, string>;
40
40
  /** Extra metadata that can influence translation and message grouping. */
41
41
  interface TranslateOptions {
42
- /** Explicit stable message id for direct runtime lookups, whether provided manually or by a transform. */
42
+ /** Explicit stable lookup id for direct runtime lookups, whether provided manually or by a transform. */
43
43
  id?: string;
44
44
  /** Extra disambiguating context for translators and custom ids. */
45
45
  context?: string;
46
46
  }
47
47
  /** A full message payload passed to the translate callback. */
48
48
  interface TranslateMessage {
49
- /** Stable message id used for locale file keys and translation results. */
49
+ /** Stable lookup id used for locale file keys and translation results. */
50
50
  id: string;
51
51
  /** Source-language text that should be translated. */
52
52
  text: string;
@@ -59,22 +59,6 @@ interface TranslateMessage {
59
59
  }
60
60
  /** User-provided translation function used to fill missing locale entries. */
61
61
  type TranslateFn = (messages: TranslateMessage[], locale: string) => Promise<Record<string, string>>;
62
- /** Stores messages in a remote backend. Deprecated: use `runtime.type = "remote"` instead. */
63
- interface BetterTranslateRemoteStorageOptions {
64
- /** Selects remote storage. */
65
- type: "remote";
66
- /** Optional remote backend URL. */
67
- url?: string;
68
- }
69
- /** Writes locale JSON files into the app repository or deployed artifact. Deprecated: use `runtime.type = "local"` instead. */
70
- interface BetterTranslateLocalStorageOptions {
71
- /** Selects local file storage. */
72
- type: "local";
73
- /** Output directory where locale JSON files are written. */
74
- output?: string;
75
- }
76
- /** Controls where translated locale artifacts are written or synced. */
77
- type BetterTranslateStorageOptions = BetterTranslateRemoteStorageOptions | BetterTranslateLocalStorageOptions;
78
62
  /** Writes locale files into the app and loads them through Vite. */
79
63
  interface BetterTranslateLocalRuntimeOptions {
80
64
  /** Selects local runtime artifacts. */
@@ -85,6 +69,8 @@ interface BetterTranslateLocalRuntimeOptions {
85
69
  output?: string;
86
70
  /** Public URL prefix used by the generated loader for `target: "public"`. */
87
71
  basePath?: string;
72
+ /** Custom translation function used for messages missing from non-default locales. */
73
+ translate?: TranslateFn;
88
74
  }
89
75
  /** Loads locale files from an external translation service. */
90
76
  interface BetterTranslateRemoteRuntimeOptions {
@@ -93,14 +79,22 @@ interface BetterTranslateRemoteRuntimeOptions {
93
79
  /** Remote translation service URL. */
94
80
  endpoint?: string;
95
81
  /** Remote project identifier. */
96
- projectId?: string;
82
+ projectId: string;
83
+ /** Project API key used by the Vite plugin to sync Manifests. Falls back to `BETTER_TRANSLATION_API_KEY`. */
84
+ apiKey?: string;
85
+ /** Branch to read from, or `"auto"` to infer it from the environment. */
86
+ branch?: "auto" | (string & {});
87
+ /** Local development behavior for remote runtime mode. */
88
+ dev?: {
89
+ /** Avoid platform reads and writes during local development. */offline?: boolean;
90
+ };
97
91
  }
98
92
  /** Controls where locale artifacts live and how the virtual runtime loader reads them. */
99
93
  type BetterTranslateRuntimeOptions = BetterTranslateLocalRuntimeOptions | BetterTranslateRemoteRuntimeOptions;
100
94
  /** Runtime metadata emitted by the plugin for server-side loaders. */
101
95
  interface BetterTranslateRuntimeConfig {
102
96
  /** Runtime backend configured for locale artifacts. */
103
- runtime: BetterTranslateRuntimeOptions;
97
+ runtime: BetterTranslateLocalRuntimeOptions | Omit<BetterTranslateRemoteRuntimeOptions, "apiKey">;
104
98
  /** Locale code treated as the source language. */
105
99
  defaultLocale: string;
106
100
  /** All locale codes emitted by the plugin. */
@@ -120,11 +114,7 @@ interface BetterTranslatePluginOptions {
120
114
  logging?: boolean;
121
115
  /** Runtime backend configuration. */
122
116
  runtime?: BetterTranslateRuntimeOptions;
123
- /** Storage backend configuration. Deprecated: use `runtime` instead. */
124
- storage?: BetterTranslateStorageOptions;
125
- /** Custom translation function used for messages missing from non-default locales. */
126
- translate?: TranslateFn;
127
117
  }
128
118
  //#endregion
129
- export { BetterTranslateRemoteStorageOptions as a, BetterTranslateStorageOptions as c, TranslateFn as d, TranslateMessage as f, BetterTranslateRemoteRuntimeOptions as i, MessageManifestFile as l, BetterTranslateLocalStorageOptions as n, BetterTranslateRuntimeConfig as o, TranslateOptions as p, BetterTranslatePluginOptions as r, BetterTranslateRuntimeOptions as s, BetterTranslateLocalRuntimeOptions as t, RuntimeMessages as u };
130
- //# sourceMappingURL=types-DxcBf0RB.d.mts.map
119
+ export { BetterTranslateRuntimeOptions as a, TranslateFn as c, BetterTranslateRuntimeConfig as i, TranslateMessage as l, BetterTranslatePluginOptions as n, MessageManifestFile as o, BetterTranslateRemoteRuntimeOptions as r, RuntimeMessages as s, BetterTranslateLocalRuntimeOptions as t, TranslateOptions as u };
120
+ //# sourceMappingURL=types-C3EkBdeb.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types-C3EkBdeb.d.mts","names":[],"sources":["../src/types.ts"],"mappings":";;UAeiB,aAAA;EAkBZ;EAhBH,IAAA;EAoB4B;EAlB5B,IAAA;EA0BsB;EAxBtB,MAAA;EAoBA;EAlBA,IAAA;EAoBA;EAlBA,MAAA;EAoBS;EAlBT,OAAA;EAkBsB;EAhBtB,SAAA;EAoByB;EAlBzB,KAAA;EAkB4B;EAhB5B,GAAA;AAAA;;UAIe,aAAA;EAegC;EAb/C,cAAA;EAgBU;EAdV,IAAA,EAAM,gBAAA;;EAEN,YAAA;EAYkC;EAVlC,OAAA,EAAS,aAAa;AAAA;;KAIZ,eAAA,GAAkB,MAAM,SAAS,aAAA;AA2DpC;AAAA,KAxDG,mBAAA,GAAsB,eAAe;;KAGrC,eAAA,GAAkB,MAAM;;UAiDnB,gBAAA;EA2Cf;EAzCA,EAAA;EA6CA;EA3CA,OAAO;AAAA;;UAIQ,gBAAA;EA6CN;EA3CT,EAAA;EAgDuC;EA9CvC,IAAA;EA8C0C;EA5C1C,IAAA,EAAM,gBAAA;EA+CS;EA7Cf,YAAA;;EAEA,OAAA,EAAS,aAAa;AAAA;;KAIZ,WAAA,IAAe,QAAA,EAAU,gBAAA,IAAoB,MAAA,aAAmB,OAAA,CAAQ,MAAA;;UAGnE,kCAAA;EAsCN;EApCT,IAAA;EAoCmD;EAlCnD,MAAA;EAsCA;EApCA,MAAA;EAoCO;EAlCP,QAAA;EAsC2C;EApC3C,SAAA,GAAY,WAAW;AAAA;;UAIR,mCAAA;EAsCf;EApCA,IAAA;EAwCA;EAtCA,QAAA;EAwCU;EAtCV,SAAA;EAsCuC;EApCvC,MAAA;;EAEA,MAAA;;EAEA,GAAA;oEAEE,OAAA;EAAA;AAAA;;KAKQ,6BAAA,GAAgC,kCAAA,GAAqC,mCAAmC;;UAGnG,4BAAA;;EAEf,OAAA,EAAS,kCAAA,GAAqC,IAAA,CAAK,mCAAA;;EAEnD,aAAA;;EAEA,OAAA;AAAA;;UAIe,4BAAA;;EAEf,OAAA;;EAEA,aAAA;;EAEA,OAAA;;EAEA,SAAA;;EAEA,OAAA;;EAEA,OAAA,GAAU,6BAA6B;AAAA"}
package/dist/vite.d.mts CHANGED
@@ -1,9 +1,9 @@
1
- import { a as BetterTranslateRemoteStorageOptions, c as BetterTranslateStorageOptions, d as TranslateFn, f as TranslateMessage, i as BetterTranslateRemoteRuntimeOptions, l as MessageManifestFile, n as BetterTranslateLocalStorageOptions, o as BetterTranslateRuntimeConfig, p as TranslateOptions, r as BetterTranslatePluginOptions, s as BetterTranslateRuntimeOptions, t as BetterTranslateLocalRuntimeOptions, u as RuntimeMessages } from "./types-DxcBf0RB.mjs";
1
+ import { a as BetterTranslateRuntimeOptions, c as TranslateFn, i as BetterTranslateRuntimeConfig, l as TranslateMessage, n as BetterTranslatePluginOptions, o as MessageManifestFile, r as BetterTranslateRemoteRuntimeOptions, s as RuntimeMessages, t as BetterTranslateLocalRuntimeOptions, u as TranslateOptions } from "./types-C3EkBdeb.mjs";
2
2
  import { Plugin } from "vite";
3
3
 
4
4
  //#region src/plugin.d.ts
5
5
  /** Scans source files for translatable messages and keeps locale JSON files in sync. */
6
6
  declare function betterTranslation(options: BetterTranslatePluginOptions): Plugin;
7
7
  //#endregion
8
- export { type BetterTranslateLocalRuntimeOptions, type BetterTranslateLocalStorageOptions, type BetterTranslatePluginOptions, type BetterTranslateRemoteRuntimeOptions, type BetterTranslateRemoteStorageOptions, type BetterTranslateRuntimeConfig, type BetterTranslateRuntimeOptions, type BetterTranslateStorageOptions, type MessageManifestFile, type RuntimeMessages, type TranslateFn, type TranslateMessage, type TranslateOptions, betterTranslation };
8
+ export { type BetterTranslateLocalRuntimeOptions, type BetterTranslatePluginOptions, type BetterTranslateRemoteRuntimeOptions, type BetterTranslateRuntimeConfig, type BetterTranslateRuntimeOptions, type MessageManifestFile, type RuntimeMessages, type TranslateFn, type TranslateMessage, type TranslateOptions, betterTranslation };
9
9
  //# sourceMappingURL=vite.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"vite.d.mts","names":[],"sources":["../src/plugin.ts"],"mappings":";;;;;iBA8DgB,iBAAA,CAAkB,OAAA,EAAS,4BAAA,GAA+B,MAAM"}
1
+ {"version":3,"file":"vite.d.mts","names":[],"sources":["../src/plugin.ts"],"mappings":";;;;;iBAkEgB,iBAAA,CAAkB,OAAA,EAAS,4BAAA,GAA+B,MAAM"}
package/dist/vite.mjs CHANGED
@@ -1,4 +1,5 @@
1
- import { n as getMessageId, r as serializeMeta, t as getCallMessageId } from "./message-id-B8zJjADZ.mjs";
1
+ import { n as getMessageId, r as serializeMeta, t as getCallMessageId } from "./message-id-DGUI8H5w.mjs";
2
+ import { execFileSync } from "node:child_process";
2
3
  import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
3
4
  import { dirname, relative, resolve } from "node:path";
4
5
  import { normalizePath } from "vite";
@@ -30,9 +31,9 @@ function saveCache(path, cache) {
30
31
  if (existsSync(path) && readFileSync(path, "utf-8") === next) return;
31
32
  writeFileSync(path, next);
32
33
  }
33
- /** Builds the cache key used to distinguish translations by stable message id and locale. */
34
- function getCacheKey(messageId, locale) {
35
- return `${messageId}\0${locale}`;
34
+ /** Builds the cache key used to distinguish translations by stable lookup id and locale. */
35
+ function getCacheKey(lookupId, locale) {
36
+ return `${lookupId}\0${locale}`;
36
37
  }
37
38
  //#endregion
38
39
  //#region src/extractor.ts
@@ -313,11 +314,14 @@ function getRuntimeConfigPath(root, dir) {
313
314
  const PREFIX = "\x1B[36m[better-translation]\x1B[0m";
314
315
  const DIM = "\x1B[2m";
315
316
  const RESET = "\x1B[0m";
316
- const YELLOW = "\x1B[33m";
317
317
  const BOLD = "\x1B[1m";
318
318
  const CYAN = "\x1B[36m";
319
- const REMOTE_API_BASE_URL = "https://better-translation.com";
320
- const REMOTE_STUB = `${YELLOW}stub${RESET}`;
319
+ const REMOTE_API_BASE_URL = "https://better-translation.dev";
320
+ const DEFAULT_REMOTE_API_KEY_ENV = "BETTER_TRANSLATION_API_KEY";
321
+ const DEFAULT_REMOTE_BRANCH = "main";
322
+ const DEFAULT_CACHE_DIR = ".cache/better-translation";
323
+ const DEFAULT_TRANSLATION_CACHE_FILE = `${DEFAULT_CACHE_DIR}/cache.json`;
324
+ const DEFAULT_REMOTE_OFFLINE_OUTPUT_DIR = `${DEFAULT_CACHE_DIR}/runtime`;
321
325
  const DEFAULT_ROOT_DIR = "src";
322
326
  const DEFAULT_SCAN_EXTENSIONS = [
323
327
  ".ts",
@@ -345,39 +349,66 @@ function formatLocales(locales) {
345
349
  }
346
350
  /** Scans source files for translatable messages and keeps locale JSON files in sync. */
347
351
  function betterTranslation(options) {
348
- const { locales, defaultLocale = locales[0] ?? "en", rootDir = DEFAULT_ROOT_DIR, cacheFile = ".cache/better-translation.json", logging = true, runtime, storage, translate } = options;
349
- const configuredRuntime = normalizeRuntimeOptions(runtime, storage);
352
+ const { locales, defaultLocale = locales[0] ?? "en", rootDir = DEFAULT_ROOT_DIR, cacheFile = DEFAULT_TRANSLATION_CACHE_FILE, logging = true, runtime } = options;
353
+ const configuredRuntime = normalizeRuntimeOptions(runtime);
350
354
  const manifest = {};
351
355
  const fileMessages = /* @__PURE__ */ new Map();
352
356
  let cache = createEmptyCache();
353
357
  let resolvedRuntime = configuredRuntime;
354
- let usesLocalStorage = configuredRuntime.type === "local";
358
+ let usesLocalStorage = shouldUseLocalStorage(configuredRuntime, false);
359
+ let resolvedTranslate = configuredRuntime.type === "local" ? configuredRuntime.translate : void 0;
355
360
  let localesDir = configuredRuntime.type === "local" ? configuredRuntime.output ?? "src/lib/bt" : DEFAULT_LOCAL_OUTPUT_DIR;
356
361
  let publicBasePath = configuredRuntime.type === "local" && configuredRuntime.target === "public" ? configuredRuntime.basePath ?? "/bt" : "/bt";
357
362
  let remoteUrl = configuredRuntime.type === "remote" ? configuredRuntime.endpoint ?? REMOTE_API_BASE_URL : REMOTE_API_BASE_URL;
358
363
  let root = "";
359
364
  let isDev = false;
360
365
  let translateTimer = null;
361
- let warnedRemoteTranslateStub = false;
362
- let warnedRemoteSyncStub = false;
366
+ let remoteSyncTimer = null;
367
+ let lastSyncedRemoteManifestSignature = null;
363
368
  let sourceRoots = [];
364
369
  function log(message) {
365
370
  if (logging) console.log(message);
366
371
  }
367
- async function remoteTranslate(messages, _locale) {
368
- if (!warnedRemoteTranslateStub) {
369
- warnedRemoteTranslateStub = true;
370
- log(`${PREFIX} ${REMOTE_STUB} remote translate via ${DIM}${remoteUrl}${RESET} not implemented yet`);
371
- }
372
- return Object.fromEntries(messages.map((message) => [message.id, message.text]));
373
- }
374
372
  async function syncRemote() {
375
- if (!warnedRemoteSyncStub) {
376
- warnedRemoteSyncStub = true;
377
- log(`${PREFIX} ${REMOTE_STUB} remote locale sync via ${DIM}${remoteUrl}${RESET} not implemented yet`);
373
+ if (resolvedRuntime.type !== "remote") return;
374
+ const payload = {
375
+ defaultLocale,
376
+ locales,
377
+ messages: buildMessageManifest()
378
+ };
379
+ const signature = JSON.stringify(payload);
380
+ if (lastSyncedRemoteManifestSignature === signature) return;
381
+ const apiKey = resolveRemoteSyncApiKey();
382
+ const target = formatRemoteManifestTarget(resolvedRuntime, remoteUrl);
383
+ if (!apiKey) throw new Error([
384
+ `${PREFIX} remote Manifest sync requires a Project API key`,
385
+ `set ${DEFAULT_REMOTE_API_KEY_ENV} or pass runtime.apiKey in the Vite plugin config`,
386
+ `target: ${target}`
387
+ ].join("\n"));
388
+ let response;
389
+ try {
390
+ response = await fetch(target, {
391
+ method: "POST",
392
+ headers: {
393
+ authorization: `Bearer ${apiKey}`,
394
+ "content-type": "application/json"
395
+ },
396
+ body: JSON.stringify(payload)
397
+ });
398
+ } catch (error) {
399
+ throw new Error(formatRemoteSyncNetworkError(target, error));
378
400
  }
401
+ const body = await response.text();
402
+ if (!response.ok) throw new Error(formatRemoteSyncError(response, target, body));
403
+ const messageCount = parseRemoteSyncResult(body)?.messageCount ?? Object.keys(payload.messages).length;
404
+ log(`${PREFIX} ${BOLD}Synced${RESET} ${CYAN}${messageCount}${RESET} ${messageCount === 1 ? "Message" : "Messages"} -> ${CYAN}${formatRuntime(resolvedRuntime)}${RESET}`);
405
+ lastSyncedRemoteManifestSignature = signature;
406
+ }
407
+ function resolveRemoteSyncApiKey() {
408
+ const explicitApiKey = resolvedRuntime.type === "remote" ? resolvedRuntime.apiKey?.trim() : null;
409
+ if (explicitApiKey) return explicitApiKey;
410
+ return process.env[DEFAULT_REMOTE_API_KEY_ENV]?.trim() || null;
379
411
  }
380
- const resolvedTranslate = translate ?? (usesLocalStorage ? void 0 : remoteTranslate);
381
412
  function buildMessageManifest() {
382
413
  return Object.fromEntries(Object.entries(manifest).map(([id, entry]) => [id, {
383
414
  defaultMessage: entry.defaultMessage,
@@ -397,7 +428,7 @@ function betterTranslation(options) {
397
428
  }
398
429
  function getRuntimeConfig() {
399
430
  return {
400
- runtime: resolvedRuntime,
431
+ runtime: getRuntimeConfigRuntime(resolvedRuntime),
401
432
  defaultLocale,
402
433
  locales
403
434
  };
@@ -475,12 +506,15 @@ function betterTranslation(options) {
475
506
  }
476
507
  for (const id of Object.keys(manifest)) {
477
508
  if (Object.hasOwn(messages, id)) continue;
478
- if (Object.hasOwn(existingMessages, id)) {
479
- messages[id] = existingMessages[id];
509
+ const entry = manifest[id];
510
+ const existingMessage = existingMessages[id];
511
+ const cachedMessage = getFreshCachedMessage(id, locale);
512
+ if (existingMessage !== void 0 && !isUntranslatedLocaleValue(existingMessage, entry)) {
513
+ messages[id] = existingMessage;
480
514
  continue;
481
515
  }
482
- const cachedMessage = cache.entries[getCacheKey(id, locale)]?.translation;
483
516
  if (cachedMessage !== void 0) messages[id] = cachedMessage;
517
+ else if (shouldWriteDefaultLocaleFallback()) messages[id] = entry.defaultMessage;
484
518
  }
485
519
  return messages;
486
520
  }
@@ -495,16 +529,19 @@ function betterTranslation(options) {
495
529
  for (const locale of locales) {
496
530
  if (locale === defaultLocale) continue;
497
531
  const existingMessages = readLocaleMessages(locale);
498
- for (const [id, entry] of Object.entries(manifest)) if (!Object.hasOwn(existingMessages, id) && !Object.hasOwn(cache.entries, getCacheKey(id, locale))) {
499
- const misses = missingByLocale.get(locale) ?? [];
500
- misses.push({
501
- id,
502
- text: entry.defaultMessage,
503
- meta: entry.meta,
504
- placeholders: entry.placeholders,
505
- sources: entry.sources
506
- });
507
- missingByLocale.set(locale, misses);
532
+ for (const [id, entry] of Object.entries(manifest)) {
533
+ const existingMessage = existingMessages[id];
534
+ if (!(existingMessage !== void 0 && !isUntranslatedLocaleValue(existingMessage, entry)) && getFreshCachedMessage(id, locale) === void 0) {
535
+ const misses = missingByLocale.get(locale) ?? [];
536
+ misses.push({
537
+ id,
538
+ text: entry.defaultMessage,
539
+ meta: entry.meta,
540
+ placeholders: entry.placeholders,
541
+ sources: entry.sources
542
+ });
543
+ missingByLocale.set(locale, misses);
544
+ }
508
545
  }
509
546
  }
510
547
  return missingByLocale;
@@ -546,10 +583,12 @@ function betterTranslation(options) {
546
583
  if (totalMisses === 0) return false;
547
584
  const missLocales = [...missingByLocale.keys()];
548
585
  log(`${PREFIX} ${BOLD}Translating${RESET} ${CYAN}${totalMisses}${RESET} ${totalMisses === 1 ? "Message" : "Messages"} -> ${CYAN}${formatLocales(missLocales)}${RESET}`);
586
+ let translatedCount = 0;
549
587
  for (const [locale, misses] of missingByLocale) {
550
588
  const result = await resolvedTranslate(misses, locale);
551
589
  for (const miss of misses) {
552
- const translated = result[miss.id] ?? miss.text;
590
+ const translated = result[miss.id]?.trim();
591
+ if (!translated) continue;
553
592
  cache.entries[getCacheKey(miss.id, locale)] = {
554
593
  sourceText: miss.text,
555
594
  meta: miss.meta,
@@ -557,10 +596,26 @@ function betterTranslation(options) {
557
596
  translation: translated,
558
597
  timestamp: Date.now()
559
598
  };
599
+ translatedCount += 1;
560
600
  }
561
601
  }
602
+ log(`${PREFIX} ${BOLD}Translated${RESET} ${CYAN}${translatedCount}${RESET}/${CYAN}${totalMisses}${RESET} ${totalMisses === 1 ? "Message" : "Messages"} -> ${CYAN}${formatLocales(missLocales)}${RESET}`);
562
603
  return true;
563
604
  }
605
+ function getFreshCachedMessage(id, locale) {
606
+ const entry = manifest[id];
607
+ const cachedMessage = cache.entries[getCacheKey(id, locale)];
608
+ if (!entry || !cachedMessage) return void 0;
609
+ if (cachedMessage.sourceText !== entry.defaultMessage) return void 0;
610
+ if (serializeMeta(cachedMessage.meta) !== serializeMeta(entry.meta)) return void 0;
611
+ return cachedMessage.translation;
612
+ }
613
+ function isUntranslatedLocaleValue(value, entry) {
614
+ return resolvedTranslate !== void 0 && value.trim() === entry.defaultMessage.trim();
615
+ }
616
+ function shouldWriteDefaultLocaleFallback() {
617
+ return resolvedTranslate !== void 0 || isRemoteOfflineDev(resolvedRuntime, isDev);
618
+ }
564
619
  function scheduleDevTranslation() {
565
620
  if (!resolvedTranslate) return;
566
621
  if (!isDev) return;
@@ -571,6 +626,13 @@ function betterTranslation(options) {
571
626
  writePrivateManifest();
572
627
  }, 1e3);
573
628
  }
629
+ function scheduleDevRemoteSync() {
630
+ if (usesLocalStorage || !isDev) return;
631
+ if (remoteSyncTimer) clearTimeout(remoteSyncTimer);
632
+ remoteSyncTimer = setTimeout(() => {
633
+ syncRemote().catch((error) => console.error(error instanceof Error ? error.message : error));
634
+ }, 1e3);
635
+ }
574
636
  function removeFileMessages(file) {
575
637
  const previous = fileMessages.get(file);
576
638
  if (!previous) return false;
@@ -648,6 +710,7 @@ function betterTranslation(options) {
648
710
  return relative(root, file).replaceAll("\\", "/");
649
711
  }
650
712
  function createVirtualMessagesModule() {
713
+ if (isRemoteOfflineDev(resolvedRuntime, isDev)) return createModuleMessagesModule(locales, (locale) => `/${normalizePath(toRootRelativePath(getLocalePath(locale)))}`);
651
714
  if (resolvedRuntime.type === "remote") return createRemoteMessagesModule(resolvedRuntime, locales, remoteUrl);
652
715
  if (resolvedRuntime.target === "public") return createPublicMessagesModule(locales, publicBasePath);
653
716
  return createModuleMessagesModule(locales, (locale) => `/${normalizePath(toRootRelativePath(getLocalePath(locale)))}`);
@@ -662,8 +725,9 @@ function betterTranslation(options) {
662
725
  root = config.root;
663
726
  isDev = config.command === "serve";
664
727
  resolvedRuntime = resolveRuntimeOptions(configuredRuntime, config);
665
- usesLocalStorage = resolvedRuntime.type === "local";
666
- localesDir = resolvedRuntime.type === "local" ? resolvedRuntime.output : DEFAULT_LOCAL_OUTPUT_DIR;
728
+ usesLocalStorage = shouldUseLocalStorage(resolvedRuntime, isDev);
729
+ resolvedTranslate = resolvedRuntime.type === "local" ? resolvedRuntime.translate : void 0;
730
+ localesDir = getRuntimeOutputDir(resolvedRuntime, isDev);
667
731
  publicBasePath = resolvedRuntime.type === "local" && resolvedRuntime.target === "public" ? resolvedRuntime.basePath ?? "/bt" : "/bt";
668
732
  remoteUrl = resolvedRuntime.type === "remote" ? resolvedRuntime.endpoint ?? REMOTE_API_BASE_URL : REMOTE_API_BASE_URL;
669
733
  sourceRoots = (Array.isArray(rootDir) ? rootDir : [rootDir]).map((dir) => resolve(root, dir));
@@ -675,7 +739,7 @@ function betterTranslation(options) {
675
739
  load(id) {
676
740
  if (id === RESOLVED_VIRTUAL_MESSAGES_MODULE_ID) return createVirtualMessagesModule();
677
741
  },
678
- buildStart() {
742
+ async buildStart() {
679
743
  cache = loadCache(resolve(root, cacheFile));
680
744
  scanAllSourceFiles();
681
745
  if (usesLocalStorage && !isDev) {
@@ -691,13 +755,16 @@ function betterTranslation(options) {
691
755
  },
692
756
  configureServer(server) {
693
757
  server.watcher.add(sourceRoots);
758
+ server.httpServer?.once("listening", () => scheduleDevRemoteSync());
694
759
  const syncFileFromDisk = (file) => {
695
760
  if (!shouldScanFile(file) || !existsSync(file)) return;
696
761
  applySyncResult(syncSourceCode(file, readFileSync(file, "utf-8")), { scheduleTranslation: true });
762
+ scheduleDevRemoteSync();
697
763
  };
698
764
  const removeFileFromManifest = (file) => {
699
765
  if (!shouldScanFile(file)) return;
700
766
  applySyncResult(removeTrackedFile(file), { scheduleTranslation: true });
767
+ scheduleDevRemoteSync();
701
768
  };
702
769
  server.watcher.on("add", syncFileFromDisk);
703
770
  server.watcher.on("change", syncFileFromDisk);
@@ -721,7 +788,7 @@ function betterTranslation(options) {
721
788
  if (usesLocalStorage) {
722
789
  if (!isDev) assertGeneratedFilesCommitted();
723
790
  assertLocalBuildTranslationsComplete();
724
- } else await translateMissingMessages();
791
+ } else if (isDev) await translateMissingMessages();
725
792
  if (!usesLocalStorage) await syncRemote();
726
793
  },
727
794
  closeBundle() {
@@ -733,30 +800,49 @@ function betterTranslation(options) {
733
800
  function formatLocaleIssue(locale, label, ids) {
734
801
  return `- ${locale}: ${label} (${ids.slice(0, 5).map((id) => JSON.stringify(id)).join(", ")}${ids.length > 5 ? `, ... ${ids.length - 5} more` : ""})`;
735
802
  }
736
- function normalizeRuntimeOptions(runtime, storage) {
803
+ function shouldUseLocalStorage(runtime, isDev) {
804
+ return runtime.type === "local" || isRemoteOfflineDev(runtime, isDev);
805
+ }
806
+ function isRemoteOfflineDev(runtime, isDev) {
807
+ return runtime.type === "remote" && isDev && runtime.dev?.offline === true;
808
+ }
809
+ function getRuntimeConfigRuntime(runtime) {
810
+ if (runtime.type === "local") return runtime;
811
+ return {
812
+ type: "remote",
813
+ projectId: runtime.projectId,
814
+ ...runtime.endpoint === void 0 ? {} : { endpoint: runtime.endpoint },
815
+ ...runtime.branch === void 0 ? {} : { branch: runtime.branch },
816
+ ...runtime.dev === void 0 ? {} : { dev: runtime.dev }
817
+ };
818
+ }
819
+ function getRuntimeOutputDir(runtime, isDev) {
820
+ if (runtime.type === "local") return runtime.output;
821
+ if (isRemoteOfflineDev(runtime, isDev)) return DEFAULT_REMOTE_OFFLINE_OUTPUT_DIR;
822
+ return DEFAULT_LOCAL_OUTPUT_DIR;
823
+ }
824
+ function normalizeRuntimeOptions(runtime) {
737
825
  if (runtime) return runtime.type === "local" ? {
738
826
  ...runtime,
739
827
  target: runtime.target ?? "module"
740
828
  } : runtime;
741
- if (!storage) return {
742
- type: "local",
743
- target: "module"
744
- };
745
- if (storage.type === "remote") return {
746
- type: "remote",
747
- endpoint: storage.url
748
- };
749
829
  return {
750
830
  type: "local",
751
- target: "module",
752
- output: storage.output
831
+ target: "module"
753
832
  };
754
833
  }
755
834
  function resolveRuntimeOptions(runtime, config) {
756
- if (runtime.type === "remote") return {
757
- ...runtime,
758
- endpoint: runtime.endpoint ?? REMOTE_API_BASE_URL
759
- };
835
+ if (runtime.type === "remote") {
836
+ const projectId = typeof runtime.projectId === "string" ? runtime.projectId.trim() : "";
837
+ if (!projectId) throw new Error(`${PREFIX} remote runtime requires a projectId`);
838
+ return {
839
+ ...runtime,
840
+ projectId,
841
+ endpoint: runtime.endpoint ?? REMOTE_API_BASE_URL,
842
+ branch: resolveBranch(runtime, config.root),
843
+ dev: { offline: runtime.dev?.offline ?? false }
844
+ };
845
+ }
760
846
  const target = runtime.target ?? "module";
761
847
  if (target === "module") return {
762
848
  ...runtime,
@@ -780,7 +866,63 @@ function inferPublicBasePath(outputPath, publicDir) {
780
866
  return `/${relativeToPublic}`.replace(/\/$/, "");
781
867
  }
782
868
  function formatRuntime(runtime) {
783
- return runtime.type === "local" ? `Local/${runtime.target ?? "module"}` : "Remote";
869
+ return runtime.type === "local" ? `Local/${runtime.target ?? "module"}` : `Remote/${runtime.branch ?? "auto"}`;
870
+ }
871
+ function resolveBranch(runtime, root) {
872
+ if (runtime.branch && runtime.branch !== "auto") return runtime.branch;
873
+ const envBranch = process.env.BETTER_TRANSLATION_BRANCH?.trim();
874
+ if (envBranch) return envBranch;
875
+ const providerBranch = process.env.VERCEL_GIT_COMMIT_REF?.trim();
876
+ if (providerBranch) return providerBranch;
877
+ return readCurrentGitBranch(root) ?? DEFAULT_REMOTE_BRANCH;
878
+ }
879
+ function readCurrentGitBranch(root) {
880
+ try {
881
+ return execFileSync("git", ["branch", "--show-current"], {
882
+ cwd: root,
883
+ encoding: "utf-8",
884
+ stdio: [
885
+ "ignore",
886
+ "pipe",
887
+ "ignore"
888
+ ]
889
+ }).trim() || null;
890
+ } catch {
891
+ return null;
892
+ }
893
+ }
894
+ function formatRemoteManifestTarget(runtime, endpoint) {
895
+ return `${endpoint.replace(/\/$/, "")}/api/projects/${encodeURIComponent(runtime.projectId)}/branches/${encodeURIComponent(runtime.branch ?? DEFAULT_REMOTE_BRANCH)}/manifest`;
896
+ }
897
+ function parseRemoteSyncResult(body) {
898
+ try {
899
+ const parsed = JSON.parse(body);
900
+ if (typeof parsed !== "object" || parsed === null) return null;
901
+ return { messageCount: "messageCount" in parsed && typeof parsed.messageCount === "number" ? parsed.messageCount : void 0 };
902
+ } catch {
903
+ return null;
904
+ }
905
+ }
906
+ function formatRemoteSyncError(response, target, body) {
907
+ const details = formatRemoteSyncResponseDetails(body);
908
+ return [
909
+ `${PREFIX} remote Manifest sync failed with ${response.status} ${response.statusText || "HTTP error"}`,
910
+ `target: ${target}`,
911
+ details ? `response: ${details}` : null
912
+ ].filter((line) => line !== null).join("\n");
913
+ }
914
+ function formatRemoteSyncNetworkError(target, error) {
915
+ const message = error instanceof Error ? error.message : String(error);
916
+ return [
917
+ `${PREFIX} remote Manifest sync could not reach the hosted service`,
918
+ `target: ${target}`,
919
+ `error: ${message}`
920
+ ].join("\n");
921
+ }
922
+ function formatRemoteSyncResponseDetails(body) {
923
+ const details = body.trim().replace(/\s+/g, " ");
924
+ if (!details) return "";
925
+ return details.length > 500 ? `${details.slice(0, 500)}...` : details;
784
926
  }
785
927
  function createModuleMessagesModule(locales, getImportPath) {
786
928
  const cases = locales.map((locale) => ` case ${JSON.stringify(locale)}:
@@ -815,13 +957,14 @@ function createPublicMessagesModule(locales, basePath) {
815
957
  }
816
958
  function createRemoteMessagesModule(runtime, locales, endpoint) {
817
959
  const normalizedEndpoint = endpoint.replace(/\/$/, "");
818
- const projectPath = runtime.projectId ? `/projects/${encodeURIComponent(runtime.projectId)}` : "";
960
+ const projectPath = `/projects/${encodeURIComponent(runtime.projectId)}`;
961
+ const branchPath = `/branches/${encodeURIComponent(runtime.branch ?? DEFAULT_REMOTE_BRANCH)}`;
819
962
  return [
820
963
  `export const locales = ${JSON.stringify(locales)}`,
821
964
  "",
822
965
  "export async function loadMessages(locale) {",
823
966
  " assertKnownLocale(locale)",
824
- ` const response = await fetch(\`${normalizedEndpoint}${projectPath}/locales/\${encodeURIComponent(locale)}.json\`)`,
967
+ ` const response = await fetch(\`${normalizedEndpoint}${projectPath}${branchPath}/locales/\${encodeURIComponent(locale)}.json\`)`,
825
968
  " if (!response.ok) throw new Error(`Failed to load locale: ${locale}`)",
826
969
  " return response.json()",
827
970
  "}",
package/dist/vite.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"vite.mjs","names":[],"sources":["../src/cache.ts","../src/extractor.ts","../src/runtime-config.ts","../src/plugin.ts"],"sourcesContent":["import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\"\nimport { dirname } from \"node:path\"\n\nimport type { TranslationCache } from \"./types.js\"\n\nconst CURRENT_VERSION = 1\n\nexport function createEmptyCache(): TranslationCache {\n return { version: CURRENT_VERSION, entries: {} }\n}\n\n/** Loads the translation cache from disk, resetting it when the schema version changes. */\nexport function loadCache(path: string): TranslationCache {\n if (!existsSync(path)) return createEmptyCache()\n try {\n const data = JSON.parse(readFileSync(path, \"utf-8\")) as TranslationCache\n if (data.version !== CURRENT_VERSION) return createEmptyCache()\n return data\n } catch {\n return createEmptyCache()\n }\n}\n\n/** Persists the translation cache so future runs can reuse existing translations. */\nexport function saveCache(path: string, cache: TranslationCache) {\n const dir = dirname(path)\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true })\n const next = JSON.stringify(cache, null, 2)\n if (existsSync(path) && readFileSync(path, \"utf-8\") === next) return\n writeFileSync(path, next)\n}\n\n/** Builds the cache key used to distinguish translations by stable message id and locale. */\nexport function getCacheKey(messageId: string, locale: string) {\n return `${messageId}\\0${locale}`\n}\n","import type { Argument, JSXChild, StringLiteral } from \"oxc-parser\"\nimport { parseSync, Visitor } from \"oxc-parser\"\n\nimport type { ExtractedMessage, MessageSource, TranslateOptions } from \"./types.js\"\n\nimport { getCallMessageId, getMessageId } from \"./message-id.js\"\n\ninterface Markers {\n call: string[]\n component: string[]\n logging: boolean\n}\n\nexport interface SourceEdit {\n start: number\n end: number\n replacement: string\n}\n\nexport interface SourceAnalysis {\n parsed: boolean\n messages: ExtractedMessage[]\n edits: SourceEdit[]\n}\n\n/** Extracts messages and source edits from a file in one coordinated parse pass. */\nexport function analyzeSourceFile(code: string, filename: string, markers: Markers): SourceAnalysis {\n const messages: ExtractedMessage[] = []\n const edits: SourceEdit[] = []\n const result = parseSync(filename, code)\n if (result.errors.length > 0) return { parsed: false, messages, edits }\n const lineStarts = getLineStarts(code)\n\n const visitor = new Visitor({\n CallExpression(node) {\n if (\n node.callee.type === \"Identifier\" &&\n markers.call.includes(node.callee.name) &&\n node.arguments.length >= 1 &&\n isStringLiteral(node.arguments[0]!)\n ) {\n const value = (node.arguments[0] as StringLiteral).value\n const meta = getCallMetaArgument(node.arguments)\n const id = getCallMessageId(value, meta)\n messages.push({\n id,\n defaultMessage: value,\n meta: meta ?? {},\n placeholders: extractPlaceholdersFromMessage(value),\n source: createSource({\n filename,\n lineStarts,\n marker: node.callee.name,\n kind: \"call\",\n start: node.start,\n end: node.end,\n }),\n })\n\n const callOptionsEdit = createCallOptionsEdit(code, node.arguments, id)\n if (callOptionsEdit) edits.push(callOptionsEdit)\n }\n },\n\n JSXElement(node) {\n const opening = node.openingElement\n if (\n opening.name.type === \"JSXIdentifier\" &&\n opening.name.name === \"Var\" &&\n (opening.attributes as Array<unknown>).length === 0\n ) {\n const identifier = getVarChildIdentifier(node.children)\n if (identifier) {\n edits.push({\n start: node.start,\n end: node.end,\n replacement: `<Var ${identifier}={${identifier}} />`,\n })\n }\n }\n\n if (opening.name.type !== \"JSXIdentifier\") return\n if (!markers.component.includes(opening.name.name)) return\n\n const extraction = extractJSXChildren(node.children)\n if (!extraction.valid) {\n if (markers.logging) {\n console.warn(`[better-translation] Non-static <${opening.name.name}> in ${filename}, skipping`)\n }\n return\n }\n\n const context = getJSXStringAttribute(opening.attributes, \"context\")\n const meta = context ? { context } : undefined\n const id = getJSXStringAttribute(opening.attributes, \"id\") ?? getMessageId(extraction.message, meta)\n messages.push({\n id,\n defaultMessage: extraction.message,\n meta: meta ?? {},\n placeholders: extraction.placeholders,\n source: createSource({\n filename,\n lineStarts,\n marker: opening.name.name,\n kind: \"component\",\n start: node.start,\n end: node.end,\n }),\n })\n\n if (!hasJSXAttribute(opening.attributes as Array<unknown>, \"id\")) {\n edits.push({\n start: opening.name.end,\n end: opening.name.end,\n replacement: ` id=\"${id}\"`,\n })\n }\n },\n })\n\n visitor.visit(result.program)\n return { parsed: true, messages, edits }\n}\n\nfunction isStringLiteral(node: Argument): node is StringLiteral {\n return node.type === \"Literal\" && typeof (node as StringLiteral).value === \"string\"\n}\n\nfunction getMetaArgument(node?: Argument) {\n if (!node) return undefined\n if (isStringLiteral(node)) return { context: node.value }\n\n if (node.type !== \"ObjectExpression\") return undefined\n\n const meta: TranslateOptions = {}\n\n for (const property of node.properties as Array<{\n type: string\n key?: { type: string; name?: string; value?: unknown }\n value?: Argument\n }>) {\n if (\n (property.type === \"ObjectProperty\" || property.type === \"Property\") &&\n ((property.key?.type === \"Identifier\" && (property.key.name === \"context\" || property.key.name === \"id\")) ||\n (property.key?.type === \"Literal\" && (property.key.value === \"context\" || property.key.value === \"id\"))) &&\n property.value &&\n isStringLiteral(property.value)\n ) {\n const key = property.key?.type === \"Identifier\" ? property.key.name : property.key?.value\n if (key === \"context\" || key === \"id\") meta[key] = property.value.value\n }\n }\n\n return Object.keys(meta).length > 0 ? meta : undefined\n}\n\nfunction getCallMetaArgument(args: Argument[]) {\n return getMetaArgument(isTranslateOptionsArgument(args[1]) ? args[1] : args[2])\n}\n\nfunction createCallOptionsEdit(code: string, args: Argument[], id: string) {\n const valuesArg = args[1]\n const optionsArg = isTranslateOptionsArgument(valuesArg) ? valuesArg : args[2]\n\n if (!optionsArg) {\n return {\n start: (valuesArg ?? args[0]!).end,\n end: (valuesArg ?? args[0]!).end,\n replacement: `, { id: ${JSON.stringify(id)} }`,\n }\n }\n\n if (isStringLiteral(optionsArg)) {\n const contextValue = code.slice(optionsArg.start, optionsArg.end)\n return {\n start: optionsArg.start,\n end: optionsArg.end,\n replacement: `{ id: ${JSON.stringify(id)}, context: ${contextValue} }`,\n }\n }\n\n if (optionsArg.type !== \"ObjectExpression\") return undefined\n if (hasObjectProperty(optionsArg, \"id\")) return undefined\n\n const objectSource = code.slice(optionsArg.start, optionsArg.end)\n const innerSource = objectSource.slice(1, -1)\n const replacement =\n innerSource.trim().length > 0 ? `{ id: ${JSON.stringify(id)},${innerSource} }` : `{ id: ${JSON.stringify(id)} }`\n\n return {\n start: optionsArg.start,\n end: optionsArg.end,\n replacement,\n }\n}\n\nfunction isTranslateOptionsArgument(node?: Argument) {\n if (!node) return false\n if (isStringLiteral(node)) return true\n if (node.type !== \"ObjectExpression\") return false\n\n return (node.properties as Array<unknown>).every((entry) => {\n const property = entry as\n | {\n type?: string\n key?: { type?: string; name?: string; value?: unknown }\n }\n | undefined\n\n if (property?.type !== \"ObjectProperty\" && property?.type !== \"Property\") return false\n\n return (\n (property.key?.type === \"Identifier\" && (property.key.name === \"context\" || property.key.name === \"id\")) ||\n (property.key?.type === \"Literal\" && (property.key.value === \"context\" || property.key.value === \"id\"))\n )\n })\n}\n\nfunction getJSXStringAttribute(attributes: Array<unknown>, name: string) {\n for (const attr of attributes as Array<{\n type: string\n name?: { type: string; name?: string }\n value?: { type: string; value?: unknown } | null\n }>) {\n if (\n attr.type === \"JSXAttribute\" &&\n attr.name?.type === \"JSXIdentifier\" &&\n attr.name.name === name &&\n attr.value?.type === \"Literal\" &&\n typeof attr.value.value === \"string\"\n ) {\n return attr.value.value as string\n }\n }\n}\n\nfunction hasJSXAttribute(attributes: Array<unknown>, name: string) {\n return attributes.some((attr) => {\n const attribute = attr as\n | {\n type?: string\n name?: { type?: string; name?: string }\n }\n | undefined\n\n return attribute?.type === \"JSXAttribute\" && attribute.name?.type === \"JSXIdentifier\" && attribute.name.name === name\n })\n}\n\nfunction hasObjectProperty(node: { properties: Array<unknown> }, name: string) {\n return node.properties.some((entry) => {\n const property = entry as\n | {\n type?: string\n key?: { type?: string; name?: string; value?: unknown }\n }\n | undefined\n\n if (property?.type !== \"ObjectProperty\" && property?.type !== \"Property\") return false\n return (\n (property.key?.type === \"Identifier\" && property.key.name === name) ||\n (property.key?.type === \"Literal\" && property.key.value === name)\n )\n })\n}\n\nfunction extractPlaceholdersFromMessage(message: string) {\n const names = new Set<string>()\n for (const match of message.matchAll(/\\{(\\w+)\\}/g)) {\n if (match[1]) names.add(match[1])\n }\n return [...names]\n}\n\nfunction createSource({\n filename,\n lineStarts,\n marker,\n kind,\n start,\n end,\n}: {\n filename: string\n lineStarts: number[]\n marker: string\n kind: MessageSource[\"kind\"]\n start: number\n end: number\n}): MessageSource {\n const startPosition = getPosition(start, lineStarts)\n const endPosition = getPosition(end, lineStarts)\n\n return {\n file: filename,\n kind,\n marker,\n line: startPosition.line,\n column: startPosition.column,\n endLine: endPosition.line,\n endColumn: endPosition.column,\n start,\n end,\n }\n}\n\nfunction getLineStarts(code: string) {\n const starts = [0]\n for (let i = 0; i < code.length; i++) {\n if (code[i] === \"\\n\") starts.push(i + 1)\n }\n return starts\n}\n\nfunction getPosition(offset: number, lineStarts: number[]) {\n let low = 0\n let high = lineStarts.length - 1\n\n while (low <= high) {\n const mid = Math.floor((low + high) / 2)\n const lineStart = lineStarts[mid]!\n const nextLineStart = lineStarts[mid + 1] ?? Number.POSITIVE_INFINITY\n\n if (offset < lineStart) {\n high = mid - 1\n continue\n }\n\n if (offset >= nextLineStart) {\n low = mid + 1\n continue\n }\n\n return { line: mid + 1, column: offset - lineStart + 1 }\n }\n\n const lastLine = lineStarts.length - 1\n const lastStart = lineStarts[lastLine] ?? 0\n return { line: lastLine + 1, column: offset - lastStart + 1 }\n}\n\ninterface ExtractionResult {\n message: string\n placeholders: string[]\n valid: boolean\n}\n\nfunction extractJSXChildren(children: Array<JSXChild>): ExtractionResult {\n const parts: string[] = []\n const placeholders: string[] = []\n\n for (const child of children) {\n switch (child.type) {\n case \"JSXText\":\n parts.push(child.value)\n break\n\n case \"JSXElement\": {\n const name = child.openingElement.name\n if (name.type !== \"JSXIdentifier\" || name.name !== \"Var\") {\n return { message: \"\", placeholders: [], valid: false }\n }\n\n const varName = getVarPlaceholderName(child)\n if (!varName) return { message: \"\", placeholders: [], valid: false }\n\n placeholders.push(varName)\n parts.push(`{${varName}}`)\n break\n }\n\n case \"JSXExpressionContainer\":\n if (child.expression.type !== \"JSXEmptyExpression\") {\n return { message: \"\", placeholders: [], valid: false }\n }\n break\n\n default:\n break\n }\n }\n\n const message = parts.join(\"\").replace(/\\s+/g, \" \").trim()\n return { message, placeholders, valid: message.length > 0 }\n}\n\nfunction getVarPlaceholderName(node: { openingElement: { attributes: Array<unknown> }; children: Array<JSXChild> }) {\n const explicitName = getJSXStringAttribute(node.openingElement.attributes as Array<unknown>, \"name\")\n if (explicitName) return explicitName\n\n const customPropName = getSingleJSXAttributeName(node.openingElement.attributes as Array<unknown>)\n if (customPropName) return customPropName\n\n return getVarChildIdentifier(node.children)\n}\n\nfunction getSingleJSXAttributeName(attributes: Array<unknown>) {\n const keys = attributes.flatMap((attr) => {\n const attribute = attr as\n | {\n type?: string\n name?: { type?: string; name?: string }\n }\n | undefined\n\n if (attribute?.type !== \"JSXAttribute\" || attribute.name?.type !== \"JSXIdentifier\") return []\n return [attribute.name.name]\n })\n\n return keys.length === 1 ? keys[0] : undefined\n}\n\nfunction getVarChildIdentifier(children: Array<JSXChild>) {\n const meaningfulChildren = children.filter((child) => !(child.type === \"JSXText\" && child.value.trim().length === 0))\n if (meaningfulChildren.length !== 1) return undefined\n\n const [child] = meaningfulChildren\n if (!child || child.type !== \"JSXExpressionContainer\" || child.expression.type !== \"Identifier\") return undefined\n return child.expression.name\n}\n","import { existsSync, readFileSync } from \"node:fs\"\nimport { dirname, resolve } from \"node:path\"\nimport { fileURLToPath } from \"node:url\"\n\nimport type { BetterTranslateRuntimeConfig } from \"./types.js\"\n\nexport const DEFAULT_LOCAL_OUTPUT_DIR = \"src/lib/bt\"\nexport const DEFAULT_PUBLIC_OUTPUT_SUBDIR = \"bt\"\nexport const DEFAULT_PUBLIC_BASE_PATH = \"/bt\"\nexport const RUNTIME_CONFIG_FILENAME = \"runtime.json\"\nexport const COMMON_RUNTIME_CONFIG_DIRS = [\n DEFAULT_LOCAL_OUTPUT_DIR,\n `public/${DEFAULT_PUBLIC_OUTPUT_SUBDIR}`,\n `assets/${DEFAULT_LOCAL_OUTPUT_DIR}`,\n]\n\nexport function getRuntimeConfigPath(root: string, dir: string) {\n return resolve(root, dir, RUNTIME_CONFIG_FILENAME)\n}\n\nexport function getRuntimeConfigCandidatePaths(importMetaUrl: string, dirs = COMMON_RUNTIME_CONFIG_DIRS) {\n return getSearchBaseDirs(importMetaUrl).flatMap((baseDir) => dirs.map((dir) => resolve(baseDir, dir, RUNTIME_CONFIG_FILENAME)))\n}\n\nexport function getLocalConfigCandidatePaths(dir: string, importMetaUrl: string, fileName: string) {\n return getSearchBaseDirs(importMetaUrl).map((baseDir) => resolve(baseDir, dir, fileName))\n}\n\nexport function readRuntimeConfigFromPaths(paths: string[]) {\n for (const path of dedupe(paths)) {\n if (!existsSync(path)) continue\n try {\n return JSON.parse(readFileSync(path, \"utf-8\")) as BetterTranslateRuntimeConfig\n } catch {\n continue\n }\n }\n return null\n}\n\nfunction dedupe(paths: string[]) {\n return [...new Set(paths)]\n}\n\nfunction getSearchBaseDirs(importMetaUrl: string) {\n const currentDir = dirname(fileURLToPath(importMetaUrl))\n const candidates = [...collectParentDirs(process.cwd()), ...collectParentDirs(currentDir)]\n return dedupe(candidates)\n}\n\nfunction collectParentDirs(startDir: string, maxDepth = 6) {\n const dirs: string[] = []\n let currentDir = startDir\n\n for (let i = 0; i <= maxDepth; i++) {\n dirs.push(currentDir)\n const parentDir = dirname(currentDir)\n if (parentDir === currentDir) break\n currentDir = parentDir\n }\n\n return dirs\n}\n","import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from \"node:fs\"\nimport { dirname, relative, resolve } from \"node:path\"\nimport { normalizePath, type Plugin, type ResolvedConfig } from \"vite\"\n\nimport type {\n BetterTranslatePluginOptions,\n BetterTranslateRuntimeOptions,\n BetterTranslateStorageOptions,\n BetterTranslateRuntimeConfig,\n ExtractedMessage,\n ManifestEntry,\n MessageManifest,\n MessageManifestFile,\n MessageSource,\n RuntimeMessages,\n TranslateMessage,\n TranslationCache,\n} from \"./types.js\"\n\nimport { createEmptyCache, getCacheKey, loadCache, saveCache } from \"./cache.js\"\nimport { analyzeSourceFile } from \"./extractor.js\"\nimport { serializeMeta } from \"./message-id.js\"\nimport {\n DEFAULT_LOCAL_OUTPUT_DIR,\n DEFAULT_PUBLIC_BASE_PATH,\n DEFAULT_PUBLIC_OUTPUT_SUBDIR,\n getRuntimeConfigPath,\n} from \"./runtime-config.js\"\n\nconst PREFIX = \"\\x1b[36m[better-translation]\\x1b[0m\"\nconst DIM = \"\\x1b[2m\"\nconst RESET = \"\\x1b[0m\"\nconst YELLOW = \"\\x1b[33m\"\nconst BOLD = \"\\x1b[1m\"\nconst CYAN = \"\\x1b[36m\"\nconst REMOTE_API_BASE_URL = \"https://better-translation.com\"\nconst REMOTE_STUB = `${YELLOW}stub${RESET}`\nconst DEFAULT_ROOT_DIR = \"src\"\nconst DEFAULT_SCAN_EXTENSIONS = [\".ts\", \".tsx\", \".js\", \".jsx\"]\nconst VIRTUAL_MESSAGES_MODULE_ID = \"better-translation/messages\"\nconst RESOLVED_VIRTUAL_MESSAGES_MODULE_ID = `\\0${VIRTUAL_MESSAGES_MODULE_ID}`\nconst CALL_MARKERS = [\"t\", \"useT\"]\nconst COMPONENT_MARKERS = [\"T\"]\nconst PRIVATE_MANIFEST_FILENAME = \"manifest.json\"\nconst LOCALES_SUBDIR = \"locales\"\nconst GITIGNORE_FILENAME = \".gitignore\"\nconst GITIGNORE_CONTENTS = [\"# Generated by better-translation/vite\", \"manifest.json\", \"\"].join(\"\\n\")\n\ninterface SyncResult {\n manifestChanged: boolean\n localeMessagesChanged: boolean\n}\n\nfunction formatLocale(locale: string) {\n return locale.toUpperCase()\n}\n\nfunction formatLocales(locales: string[]) {\n return locales.map(formatLocale).join(\", \")\n}\n\n/** Scans source files for translatable messages and keeps locale JSON files in sync. */\nexport function betterTranslation(options: BetterTranslatePluginOptions): Plugin {\n const {\n locales,\n defaultLocale = locales[0] ?? \"en\",\n rootDir = DEFAULT_ROOT_DIR,\n cacheFile = \".cache/better-translation.json\",\n logging = true,\n runtime,\n storage,\n translate,\n } = options\n const configuredRuntime = normalizeRuntimeOptions(runtime, storage)\n const manifest: MessageManifest = {}\n const fileMessages = new Map<string, ExtractedMessage[]>()\n let cache: TranslationCache = createEmptyCache()\n let resolvedRuntime = configuredRuntime\n let usesLocalStorage = configuredRuntime.type === \"local\"\n let localesDir =\n configuredRuntime.type === \"local\" ? (configuredRuntime.output ?? DEFAULT_LOCAL_OUTPUT_DIR) : DEFAULT_LOCAL_OUTPUT_DIR\n let publicBasePath =\n configuredRuntime.type === \"local\" && configuredRuntime.target === \"public\"\n ? (configuredRuntime.basePath ?? DEFAULT_PUBLIC_BASE_PATH)\n : DEFAULT_PUBLIC_BASE_PATH\n let remoteUrl = configuredRuntime.type === \"remote\" ? (configuredRuntime.endpoint ?? REMOTE_API_BASE_URL) : REMOTE_API_BASE_URL\n let root = \"\"\n let isDev = false\n let translateTimer: ReturnType<typeof setTimeout> | null = null\n let warnedRemoteTranslateStub = false\n let warnedRemoteSyncStub = false\n let sourceRoots: string[] = []\n\n function log(message: string) {\n if (logging) console.log(message)\n }\n\n async function remoteTranslate(messages: TranslateMessage[], _locale: string) {\n if (!warnedRemoteTranslateStub) {\n warnedRemoteTranslateStub = true\n log(`${PREFIX} ${REMOTE_STUB} remote translate via ${DIM}${remoteUrl}${RESET} not implemented yet`)\n }\n return Object.fromEntries(messages.map((message) => [message.id, message.text])) as Record<string, string>\n }\n\n async function syncRemote() {\n if (!warnedRemoteSyncStub) {\n warnedRemoteSyncStub = true\n log(`${PREFIX} ${REMOTE_STUB} remote locale sync via ${DIM}${remoteUrl}${RESET} not implemented yet`)\n }\n }\n\n const resolvedTranslate = translate ?? (usesLocalStorage ? undefined : remoteTranslate)\n\n function buildMessageManifest(): MessageManifestFile {\n return Object.fromEntries(\n Object.entries(manifest).map(([id, entry]) => [\n id,\n {\n defaultMessage: entry.defaultMessage,\n meta: entry.meta,\n placeholders: entry.placeholders,\n sources: entry.sources,\n },\n ]),\n )\n }\n\n function shouldScanFile(id: string) {\n const cleanId = id.split(\"?\", 1)[0] ?? id\n if (cleanId.includes(\"node_modules\")) return false\n const extension = DEFAULT_SCAN_EXTENSIONS.find((ext) => cleanId.endsWith(ext))\n if (!extension) return false\n return sourceRoots.some(\n (sourceRoot) => cleanId === sourceRoot || cleanId.startsWith(`${sourceRoot}/`) || cleanId.startsWith(`${sourceRoot}\\\\`),\n )\n }\n\n function getPrivateManifestPath() {\n return resolve(root, localesDir, PRIVATE_MANIFEST_FILENAME)\n }\n\n function getRuntimeConfig(): BetterTranslateRuntimeConfig {\n return {\n runtime: resolvedRuntime,\n defaultLocale,\n locales,\n }\n }\n\n function writeRuntimeConfig() {\n if (!usesLocalStorage) return\n\n const runtimeConfig = JSON.stringify(getRuntimeConfig(), null, 2) + \"\\n\"\n const path = getRuntimeConfigPath(root, localesDir)\n const dir = dirname(path)\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true })\n writeFileIfChanged(path, runtimeConfig)\n }\n\n function getLocalesDirPath() {\n return resolve(root, localesDir, LOCALES_SUBDIR)\n }\n\n function getLocalePath(locale: string) {\n return resolve(getLocalesDirPath(), `${locale}.json`)\n }\n\n function readLocaleMessages(locale: string): RuntimeMessages {\n const path = getLocalePath(locale)\n if (!existsSync(path)) return {}\n\n try {\n const input = JSON.parse(readFileSync(path, \"utf-8\")) as unknown\n return normalizeLocaleMessages(input)\n } catch {\n return {}\n }\n }\n\n function writePrivateManifest() {\n if (!usesLocalStorage) return\n const path = getPrivateManifestPath()\n const dir = dirname(path)\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true })\n writeFileIfChanged(path, JSON.stringify(buildMessageManifest(), null, 2) + \"\\n\")\n }\n\n function assertFileContents(path: string, expected: string, label: string) {\n if (!existsSync(path)) {\n throw new Error(`${PREFIX} missing committed ${label} at ${relative(root, path)}`)\n }\n const actual = readFileSync(path, \"utf-8\")\n if (actual !== expected) {\n throw new Error(\n [\n `${PREFIX} committed ${label} is out of date`,\n `expected committed file at ${relative(root, path)} to match the generated output`,\n `run the dev workflow to regenerate locale artifacts and commit the result`,\n ].join(\"\\n\"),\n )\n }\n }\n\n function assertJsonFileContents(path: string, expected: unknown, label: string) {\n if (!existsSync(path)) {\n throw new Error(`${PREFIX} missing committed ${label} at ${relative(root, path)}`)\n }\n\n let actual: unknown\n try {\n actual = JSON.parse(readFileSync(path, \"utf-8\")) as unknown\n } catch {\n throw new Error(`${PREFIX} committed ${label} is invalid JSON at ${relative(root, path)}`)\n }\n\n if (JSON.stringify(actual) !== JSON.stringify(expected)) {\n throw new Error(\n [\n `${PREFIX} committed ${label} is out of date`,\n `expected committed file at ${relative(root, path)} to match the generated output`,\n `run the dev workflow to regenerate locale artifacts and commit the result`,\n ].join(\"\\n\"),\n )\n }\n }\n\n function writeGitignore() {\n if (!usesLocalStorage) return\n const path = resolve(root, localesDir, GITIGNORE_FILENAME)\n const dir = dirname(path)\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true })\n writeFileIfChanged(path, GITIGNORE_CONTENTS)\n }\n\n function assertGeneratedFilesCommitted() {\n if (!usesLocalStorage) return\n assertJsonFileContents(getRuntimeConfigPath(root, localesDir), getRuntimeConfig(), \"runtime config\")\n assertFileContents(resolve(root, localesDir, GITIGNORE_FILENAME), GITIGNORE_CONTENTS, \"generated .gitignore\")\n }\n\n function buildLocalLocaleMessages(locale: string): RuntimeMessages {\n const existingMessages = readLocaleMessages(locale)\n const messages: RuntimeMessages = {}\n\n if (locale === defaultLocale) {\n for (const [id, entry] of Object.entries(manifest)) {\n messages[id] = entry.defaultMessage\n }\n return messages\n }\n\n for (const id of Object.keys(manifest)) {\n if (Object.hasOwn(messages, id)) continue\n if (Object.hasOwn(existingMessages, id)) {\n const existingMessage = existingMessages[id]!\n messages[id] = existingMessage\n continue\n }\n const cachedMessage = cache.entries[getCacheKey(id, locale)]?.translation\n if (cachedMessage !== undefined) messages[id] = cachedMessage\n }\n return messages\n }\n\n function writeLocaleFilesToDisk() {\n if (!usesLocalStorage) return\n const dir = getLocalesDirPath()\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true })\n for (const locale of locales) {\n writeFileIfChanged(resolve(dir, `${locale}.json`), JSON.stringify(buildLocalLocaleMessages(locale), null, 2) + \"\\n\")\n }\n }\n\n function getMissingMessagesByLocale() {\n const missingByLocale = new Map<string, TranslateMessage[]>()\n\n for (const locale of locales) {\n if (locale === defaultLocale) continue\n const existingMessages = readLocaleMessages(locale)\n for (const [id, entry] of Object.entries(manifest)) {\n if (!Object.hasOwn(existingMessages, id) && !Object.hasOwn(cache.entries, getCacheKey(id, locale))) {\n const misses = missingByLocale.get(locale) ?? []\n misses.push({\n id,\n text: entry.defaultMessage,\n meta: entry.meta,\n placeholders: entry.placeholders,\n sources: entry.sources,\n })\n missingByLocale.set(locale, misses)\n }\n }\n }\n\n return missingByLocale\n }\n\n function assertLocalBuildTranslationsComplete() {\n const expectedIds = new Set(Object.keys(manifest))\n const issues: string[] = []\n\n for (const locale of locales) {\n const localePath = getLocalePath(locale)\n if (!existsSync(localePath)) {\n issues.push(`- ${locale}: missing file at ${relative(root, localePath)}`)\n continue\n }\n\n const localeMessages = readLocaleMessages(locale)\n const missingIds = [...expectedIds].filter((id) => !Object.hasOwn(localeMessages, id))\n const orphanIds = Object.keys(localeMessages).filter((id) => !expectedIds.has(id))\n\n if (locale === defaultLocale) {\n const staleIds = [...expectedIds].filter((id) => localeMessages[id] !== manifest[id]!.defaultMessage)\n if (missingIds.length > 0) issues.push(formatLocaleIssue(locale, \"missing\", missingIds))\n if (orphanIds.length > 0) issues.push(formatLocaleIssue(locale, \"orphaned\", orphanIds))\n if (staleIds.length > 0) issues.push(formatLocaleIssue(locale, \"outdated default messages\", staleIds))\n continue\n }\n\n if (missingIds.length > 0) issues.push(formatLocaleIssue(locale, \"missing\", missingIds))\n if (orphanIds.length > 0) issues.push(formatLocaleIssue(locale, \"orphaned\", orphanIds))\n }\n\n if (issues.length === 0) return\n\n throw new Error(\n [\n `${PREFIX} committed locale artifacts are out of sync for local production build`,\n `local production builds are check-only and never regenerate locale files`,\n `run the dev workflow to regenerate locale artifacts and commit the result`,\n ...issues,\n ].join(\"\\n\"),\n )\n }\n\n async function translateMissingMessages() {\n if (!resolvedTranslate) return false\n const missingByLocale = getMissingMessagesByLocale()\n\n const totalMisses = [...missingByLocale.values()].reduce((count, misses) => count + misses.length, 0)\n if (totalMisses === 0) return false\n\n const missLocales = [...missingByLocale.keys()]\n log(\n `${PREFIX} ${BOLD}Translating${RESET} ${CYAN}${totalMisses}${RESET} ${totalMisses === 1 ? \"Message\" : \"Messages\"} -> ${CYAN}${formatLocales(missLocales)}${RESET}`,\n )\n\n for (const [locale, misses] of missingByLocale) {\n const result = await resolvedTranslate(misses, locale)\n\n for (const miss of misses) {\n const translated = result[miss.id] ?? miss.text\n cache.entries[getCacheKey(miss.id, locale)] = {\n sourceText: miss.text,\n meta: miss.meta,\n locale,\n translation: translated,\n timestamp: Date.now(),\n }\n }\n }\n\n return true\n }\n\n function scheduleDevTranslation() {\n if (!resolvedTranslate) return\n if (!isDev) return\n if (translateTimer) clearTimeout(translateTimer)\n translateTimer = setTimeout(async () => {\n const translated = await translateMissingMessages()\n if (translated) saveCache(resolve(root, cacheFile), cache)\n writeLocaleFilesToDisk()\n writePrivateManifest()\n }, 1000)\n }\n\n function removeFileMessages(file: string) {\n const previous = fileMessages.get(file)\n if (!previous) return false\n\n for (const message of previous) {\n const entry = manifest[message.id]\n if (!entry) continue\n entry.sources = entry.sources.filter((source) => !isSameSource(source, message.source))\n if (entry.sources.length === 0) delete manifest[message.id]\n }\n\n fileMessages.delete(file)\n return true\n }\n\n function syncFileMessages(file: string, messages: ExtractedMessage[]): SyncResult {\n const previousMessages = fileMessages.get(file) ?? []\n const nextEntries = groupMessagesById(messages)\n for (const [id, entry] of Object.entries(nextEntries)) {\n const existing = manifest[id]\n if (existing && !hasSameMessageShape(existing, entry)) {\n throw new Error(formatCollisionError(id, existing, entry))\n }\n }\n\n removeFileMessages(file)\n for (const [id, entry] of Object.entries(nextEntries)) {\n if (!manifest[id]) {\n manifest[id] = entry\n continue\n }\n for (const source of entry.sources) {\n if (!manifest[id]!.sources.some((existingSource) => isSameSource(existingSource, source))) {\n manifest[id]!.sources.push(source)\n }\n }\n }\n\n if (messages.length > 0) fileMessages.set(file, messages)\n return {\n manifestChanged:\n previousMessages.length !== messages.length ||\n previousMessages.some((message, index) => !isSameExtractedMessage(message, messages[index])),\n localeMessagesChanged:\n previousMessages.length !== messages.length ||\n previousMessages.some((message, index) => !hasSameMessageShape(message, messages[index]!)),\n }\n }\n\n function syncSourceCode(file: string, code: string) {\n const analysis = analyzeSourceFile(code, file, {\n call: CALL_MARKERS,\n component: COMPONENT_MARKERS,\n logging,\n })\n if (!analysis.parsed) return null\n return syncFileMessages(\n file,\n analysis.messages.map((message) => ({\n ...message,\n source: {\n ...message.source,\n file: toRootRelativePath(message.source.file),\n },\n })),\n )\n }\n\n function removeTrackedFile(file: string): SyncResult {\n const hadPreviousMessages = removeFileMessages(file)\n return {\n manifestChanged: hadPreviousMessages,\n localeMessagesChanged: hadPreviousMessages,\n }\n }\n\n function applySyncResult(syncResult: SyncResult | null, options: { scheduleTranslation: boolean }) {\n if (!syncResult) return\n if (syncResult.localeMessagesChanged) {\n writeLocaleFilesToDisk()\n writePrivateManifest()\n if (options.scheduleTranslation) scheduleDevTranslation()\n return\n }\n if (syncResult.manifestChanged) writePrivateManifest()\n }\n\n function scanAllSourceFiles() {\n for (const id of Object.keys(manifest)) delete manifest[id]\n fileMessages.clear()\n\n for (const sourceRoot of sourceRoots) {\n if (!existsSync(sourceRoot)) continue\n for (const file of collectScanFiles(sourceRoot).sort()) {\n const code = readFileSync(file, \"utf-8\")\n syncSourceCode(file, code)\n }\n }\n }\n\n function toRootRelativePath(file: string) {\n return relative(root, file).replaceAll(\"\\\\\", \"/\")\n }\n\n function createVirtualMessagesModule() {\n if (resolvedRuntime.type === \"remote\") return createRemoteMessagesModule(resolvedRuntime, locales, remoteUrl)\n if (resolvedRuntime.target === \"public\") return createPublicMessagesModule(locales, publicBasePath)\n return createModuleMessagesModule(locales, (locale) => `/${normalizePath(toRootRelativePath(getLocalePath(locale)))}`)\n }\n\n return {\n name: \"better-translation-extract\",\n enforce: \"pre\",\n\n config() {\n return {\n ssr: {\n noExternal: [\"better-translation\"],\n },\n }\n },\n\n configResolved(config) {\n root = config.root\n isDev = config.command === \"serve\"\n resolvedRuntime = resolveRuntimeOptions(configuredRuntime, config)\n usesLocalStorage = resolvedRuntime.type === \"local\"\n localesDir = resolvedRuntime.type === \"local\" ? resolvedRuntime.output! : DEFAULT_LOCAL_OUTPUT_DIR\n publicBasePath =\n resolvedRuntime.type === \"local\" && resolvedRuntime.target === \"public\"\n ? (resolvedRuntime.basePath ?? DEFAULT_PUBLIC_BASE_PATH)\n : DEFAULT_PUBLIC_BASE_PATH\n remoteUrl = resolvedRuntime.type === \"remote\" ? (resolvedRuntime.endpoint ?? REMOTE_API_BASE_URL) : REMOTE_API_BASE_URL\n sourceRoots = (Array.isArray(rootDir) ? rootDir : [rootDir]).map((dir) => resolve(root, dir))\n log(\n `${PREFIX} Locales: ${CYAN}${formatLocales(locales)}${RESET} | Default: ${CYAN}${formatLocale(defaultLocale)}${RESET} | Runtime: ${CYAN}${formatRuntime(resolvedRuntime)}${RESET} | Out Dir: ${DIM}${usesLocalStorage ? localesDir : \"n/a\"}${RESET} | Roots: ${DIM}${(Array.isArray(rootDir) ? rootDir : [rootDir]).join(\", \")}${RESET}`,\n )\n },\n\n resolveId(id) {\n if (id === VIRTUAL_MESSAGES_MODULE_ID) return RESOLVED_VIRTUAL_MESSAGES_MODULE_ID\n },\n\n load(id) {\n if (id === RESOLVED_VIRTUAL_MESSAGES_MODULE_ID) return createVirtualMessagesModule()\n },\n\n buildStart() {\n cache = loadCache(resolve(root, cacheFile))\n scanAllSourceFiles()\n if (usesLocalStorage && !isDev) {\n assertGeneratedFilesCommitted()\n assertLocalBuildTranslationsComplete()\n return\n }\n writeRuntimeConfig()\n writeGitignore()\n writeLocaleFilesToDisk()\n writePrivateManifest()\n\n if (isDev) scheduleDevTranslation()\n },\n\n configureServer(server) {\n server.watcher.add(sourceRoots)\n\n const syncFileFromDisk = (file: string) => {\n if (!shouldScanFile(file) || !existsSync(file)) return\n applySyncResult(syncSourceCode(file, readFileSync(file, \"utf-8\")), { scheduleTranslation: true })\n }\n\n const removeFileFromManifest = (file: string) => {\n if (!shouldScanFile(file)) return\n applySyncResult(removeTrackedFile(file), { scheduleTranslation: true })\n }\n\n server.watcher.on(\"add\", syncFileFromDisk)\n server.watcher.on(\"change\", syncFileFromDisk)\n server.watcher.on(\"unlink\", removeFileFromManifest)\n },\n\n transform(code, id) {\n const cleanId = id.split(\"?\", 1)[0] ?? id\n if (!shouldScanFile(cleanId)) return\n\n const analysis = analyzeSourceFile(code, cleanId, {\n call: CALL_MARKERS,\n component: COMPONENT_MARKERS,\n logging,\n })\n\n if (analysis.edits.length === 0) return\n return {\n code: applyEdits(code, analysis.edits),\n map: null,\n }\n },\n\n async generateBundle() {\n if (usesLocalStorage) {\n if (!isDev) {\n assertGeneratedFilesCommitted()\n }\n assertLocalBuildTranslationsComplete()\n } else {\n await translateMissingMessages()\n }\n\n if (!usesLocalStorage) {\n await syncRemote()\n }\n },\n\n closeBundle() {\n if (usesLocalStorage && !isDev) return\n saveCache(resolve(root, cacheFile), cache)\n },\n }\n}\n\nfunction formatLocaleIssue(locale: string, label: string, ids: string[]) {\n const preview = ids\n .slice(0, 5)\n .map((id) => JSON.stringify(id))\n .join(\", \")\n const suffix = ids.length > 5 ? `, ... ${ids.length - 5} more` : \"\"\n return `- ${locale}: ${label} (${preview}${suffix})`\n}\n\nfunction normalizeRuntimeOptions(\n runtime: BetterTranslateRuntimeOptions | undefined,\n storage: BetterTranslateStorageOptions | undefined,\n): BetterTranslateRuntimeOptions {\n if (runtime) return runtime.type === \"local\" ? { ...runtime, target: runtime.target ?? \"module\" } : runtime\n if (!storage) return { type: \"local\", target: \"module\" }\n if (storage.type === \"remote\") return { type: \"remote\", endpoint: storage.url }\n return { type: \"local\", target: \"module\", output: storage.output }\n}\n\nfunction resolveRuntimeOptions(runtime: BetterTranslateRuntimeOptions, config: ResolvedConfig): BetterTranslateRuntimeOptions {\n if (runtime.type === \"remote\") return { ...runtime, endpoint: runtime.endpoint ?? REMOTE_API_BASE_URL }\n\n const target = runtime.target ?? \"module\"\n if (target === \"module\") {\n return {\n ...runtime,\n target,\n output: runtime.output ?? DEFAULT_LOCAL_OUTPUT_DIR,\n }\n }\n\n if (!config.publicDir) {\n throw new Error(`${PREFIX} runtime target \"public\" requires Vite publicDir to be enabled`)\n }\n\n const output = runtime.output ?? normalizePath(relative(config.root, resolve(config.publicDir, DEFAULT_PUBLIC_OUTPUT_SUBDIR)))\n const outputPath = resolve(config.root, output)\n const publicBasePath = runtime.basePath ?? inferPublicBasePath(outputPath, config.publicDir)\n\n return {\n ...runtime,\n target,\n output,\n basePath: publicBasePath,\n }\n}\n\nfunction inferPublicBasePath(outputPath: string, publicDir: string) {\n const relativeToPublic = normalizePath(relative(publicDir, outputPath))\n if (relativeToPublic.startsWith(\"..\")) {\n throw new Error(`${PREFIX} runtime target \"public\" output must be inside Vite publicDir unless basePath is provided`)\n }\n return `/${relativeToPublic}`.replace(/\\/$/, \"\")\n}\n\nfunction formatRuntime(runtime: BetterTranslateRuntimeOptions) {\n return runtime.type === \"local\" ? `Local/${runtime.target ?? \"module\"}` : \"Remote\"\n}\n\nfunction createModuleMessagesModule(locales: string[], getImportPath: (locale: string) => string) {\n const cases = locales\n .map(\n (locale) => ` case ${JSON.stringify(locale)}:\n return (await import(${JSON.stringify(getImportPath(locale))})).default`,\n )\n .join(\"\\n\")\n\n return [\n `export const locales = ${JSON.stringify(locales)}`,\n \"\",\n \"export async function loadMessages(locale) {\",\n \" switch (locale) {\",\n cases,\n \" default:\",\n \" throw new Error(`Unknown locale: ${locale}`)\",\n \" }\",\n \"}\",\n \"\",\n ].join(\"\\n\")\n}\n\nfunction createPublicMessagesModule(locales: string[], basePath: string) {\n const normalizedBasePath = basePath.replace(/\\/$/, \"\")\n return [\n `export const locales = ${JSON.stringify(locales)}`,\n \"\",\n \"export async function loadMessages(locale) {\",\n \" assertKnownLocale(locale)\",\n ` const response = await fetch(\\`${normalizedBasePath}/locales/\\${encodeURIComponent(locale)}.json\\`)`,\n \" if (!response.ok) throw new Error(`Failed to load locale: ${locale}`)\",\n \" return response.json()\",\n \"}\",\n \"\",\n createKnownLocaleAssertion(locales),\n ].join(\"\\n\")\n}\n\nfunction createRemoteMessagesModule(\n runtime: Extract<BetterTranslateRuntimeOptions, { type: \"remote\" }>,\n locales: string[],\n endpoint: string,\n) {\n const normalizedEndpoint = endpoint.replace(/\\/$/, \"\")\n const projectPath = runtime.projectId ? `/projects/${encodeURIComponent(runtime.projectId)}` : \"\"\n return [\n `export const locales = ${JSON.stringify(locales)}`,\n \"\",\n \"export async function loadMessages(locale) {\",\n \" assertKnownLocale(locale)\",\n ` const response = await fetch(\\`${normalizedEndpoint}${projectPath}/locales/\\${encodeURIComponent(locale)}.json\\`)`,\n \" if (!response.ok) throw new Error(`Failed to load locale: ${locale}`)\",\n \" return response.json()\",\n \"}\",\n \"\",\n createKnownLocaleAssertion(locales),\n ].join(\"\\n\")\n}\n\nfunction createKnownLocaleAssertion(locales: string[]) {\n return [\n `const knownLocales = new Set(${JSON.stringify(locales)})`,\n \"\",\n \"function assertKnownLocale(locale) {\",\n \" if (!knownLocales.has(locale)) throw new Error(`Unknown locale: ${locale}`)\",\n \"}\",\n \"\",\n ].join(\"\\n\")\n}\n\nfunction normalizeLocaleMessages(input: unknown): RuntimeMessages {\n if (isRuntimeMessages(input)) return input\n if (\n typeof input === \"object\" &&\n input !== null &&\n \"messages\" in input &&\n typeof input.messages === \"object\" &&\n input.messages !== null\n ) {\n return Object.fromEntries(\n Object.entries(input.messages).flatMap(([id, entry]) =>\n typeof entry === \"object\" && entry !== null && \"translation\" in entry && typeof entry.translation === \"string\"\n ? [[id, entry.translation]]\n : [],\n ),\n ) as RuntimeMessages\n }\n return {}\n}\n\nfunction writeFileIfChanged(path: string, contents: string) {\n if (existsSync(path) && readFileSync(path, \"utf-8\") === contents) return false\n writeFileSync(path, contents)\n return true\n}\n\nfunction collectScanFiles(root: string) {\n const files: string[] = []\n for (const entry of readdirSync(root, { withFileTypes: true })) {\n const path = resolve(root, entry.name)\n if (entry.isDirectory()) {\n if (entry.name === \"node_modules\") continue\n files.push(...collectScanFiles(path))\n continue\n }\n files.push(path)\n }\n return files\n}\n\nfunction isRuntimeMessages(input: unknown): input is RuntimeMessages {\n return typeof input === \"object\" && input !== null && Object.values(input).every((value) => typeof value === \"string\")\n}\n\nfunction applyEdits(code: string, edits: Array<{ start: number; end: number; replacement: string }>) {\n let transformed = code\n for (const edit of [...edits].sort((a, b) => b.start - a.start)) {\n transformed = `${transformed.slice(0, edit.start)}${edit.replacement}${transformed.slice(edit.end)}`\n }\n return transformed\n}\n\nfunction groupMessagesById(messages: ExtractedMessage[]): MessageManifest {\n const grouped: MessageManifest = {}\n\n for (const message of messages) {\n const existing = grouped[message.id]\n if (existing && !hasSameMessageShape(existing, message)) {\n throw new Error(formatCollisionError(message.id, existing, message))\n }\n if (!existing) {\n grouped[message.id] = {\n defaultMessage: message.defaultMessage,\n meta: message.meta,\n placeholders: message.placeholders,\n sources: [message.source],\n }\n continue\n }\n if (!existing.sources.some((source) => isSameSource(source, message.source))) {\n existing.sources.push(message.source)\n }\n }\n\n return grouped\n}\n\nfunction hasSameMessageShape(\n existing: Pick<ManifestEntry, \"defaultMessage\" | \"meta\" | \"placeholders\">,\n incoming: Pick<ManifestEntry, \"defaultMessage\" | \"meta\" | \"placeholders\"> | ExtractedMessage,\n) {\n return (\n existing.defaultMessage === incoming.defaultMessage &&\n serializeMeta(existing.meta) === serializeMeta(incoming.meta) &&\n JSON.stringify(existing.placeholders) === JSON.stringify(incoming.placeholders)\n )\n}\n\nfunction isSameSource(left: MessageSource, right: MessageSource) {\n return (\n left.file === right.file &&\n left.kind === right.kind &&\n left.marker === right.marker &&\n left.start === right.start &&\n left.end === right.end\n )\n}\n\nfunction isSameExtractedMessage(left: ExtractedMessage, right?: ExtractedMessage) {\n if (!right) return false\n return hasSameMessageShape(left, right) && isSameSource(left.source, right.source)\n}\n\nfunction formatCollisionError(\n id: string,\n existing: Pick<ManifestEntry, \"defaultMessage\" | \"meta\" | \"placeholders\" | \"sources\">,\n incoming: Pick<ManifestEntry, \"defaultMessage\" | \"meta\" | \"placeholders\" | \"sources\"> | ExtractedMessage,\n) {\n const existingSources = formatSources(existing.sources)\n const incomingSources = formatSources(\"source\" in incoming ? [incoming.source] : incoming.sources)\n return [\n `${PREFIX} conflicting message definition for ${BOLD}\"${id}\"${RESET}`,\n `existing: ${JSON.stringify({ defaultMessage: existing.defaultMessage, meta: existing.meta, placeholders: existing.placeholders })}`,\n `existing sources: ${existingSources}`,\n `incoming: ${JSON.stringify({ defaultMessage: incoming.defaultMessage, meta: incoming.meta, placeholders: incoming.placeholders })}`,\n `incoming sources: ${incomingSources}`,\n ].join(\"\\n\")\n}\n\nfunction formatSources(sources: MessageSource[]) {\n return sources.map((source) => `${source.file}:${source.line}:${source.column}`).join(\", \")\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"vite.mjs","names":[],"sources":["../src/cache.ts","../src/extractor.ts","../src/runtime-config.ts","../src/plugin.ts"],"sourcesContent":["import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\"\nimport { dirname } from \"node:path\"\n\nimport type { TranslationCache } from \"./types.js\"\n\nconst CURRENT_VERSION = 1\n\nexport function createEmptyCache(): TranslationCache {\n return { version: CURRENT_VERSION, entries: {} }\n}\n\n/** Loads the translation cache from disk, resetting it when the schema version changes. */\nexport function loadCache(path: string): TranslationCache {\n if (!existsSync(path)) return createEmptyCache()\n try {\n const data = JSON.parse(readFileSync(path, \"utf-8\")) as TranslationCache\n if (data.version !== CURRENT_VERSION) return createEmptyCache()\n return data\n } catch {\n return createEmptyCache()\n }\n}\n\n/** Persists the translation cache so future runs can reuse existing translations. */\nexport function saveCache(path: string, cache: TranslationCache) {\n const dir = dirname(path)\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true })\n const next = JSON.stringify(cache, null, 2)\n if (existsSync(path) && readFileSync(path, \"utf-8\") === next) return\n writeFileSync(path, next)\n}\n\n/** Builds the cache key used to distinguish translations by stable lookup id and locale. */\nexport function getCacheKey(lookupId: string, locale: string) {\n return `${lookupId}\\0${locale}`\n}\n","import type { Argument, JSXChild, StringLiteral } from \"oxc-parser\"\nimport { parseSync, Visitor } from \"oxc-parser\"\n\nimport type { ExtractedMessage, MessageSource, TranslateOptions } from \"./types.js\"\n\nimport { getCallMessageId, getMessageId } from \"./message-id.js\"\n\ninterface Markers {\n call: string[]\n component: string[]\n logging: boolean\n}\n\nexport interface SourceEdit {\n start: number\n end: number\n replacement: string\n}\n\nexport interface SourceAnalysis {\n parsed: boolean\n messages: ExtractedMessage[]\n edits: SourceEdit[]\n}\n\n/** Extracts messages and source edits from a file in one coordinated parse pass. */\nexport function analyzeSourceFile(code: string, filename: string, markers: Markers): SourceAnalysis {\n const messages: ExtractedMessage[] = []\n const edits: SourceEdit[] = []\n const result = parseSync(filename, code)\n if (result.errors.length > 0) return { parsed: false, messages, edits }\n const lineStarts = getLineStarts(code)\n\n const visitor = new Visitor({\n CallExpression(node) {\n if (\n node.callee.type === \"Identifier\" &&\n markers.call.includes(node.callee.name) &&\n node.arguments.length >= 1 &&\n isStringLiteral(node.arguments[0]!)\n ) {\n const value = (node.arguments[0] as StringLiteral).value\n const meta = getCallMetaArgument(node.arguments)\n const id = getCallMessageId(value, meta)\n messages.push({\n id,\n defaultMessage: value,\n meta: meta ?? {},\n placeholders: extractPlaceholdersFromMessage(value),\n source: createSource({\n filename,\n lineStarts,\n marker: node.callee.name,\n kind: \"call\",\n start: node.start,\n end: node.end,\n }),\n })\n\n const callOptionsEdit = createCallOptionsEdit(code, node.arguments, id)\n if (callOptionsEdit) edits.push(callOptionsEdit)\n }\n },\n\n JSXElement(node) {\n const opening = node.openingElement\n if (\n opening.name.type === \"JSXIdentifier\" &&\n opening.name.name === \"Var\" &&\n (opening.attributes as Array<unknown>).length === 0\n ) {\n const identifier = getVarChildIdentifier(node.children)\n if (identifier) {\n edits.push({\n start: node.start,\n end: node.end,\n replacement: `<Var ${identifier}={${identifier}} />`,\n })\n }\n }\n\n if (opening.name.type !== \"JSXIdentifier\") return\n if (!markers.component.includes(opening.name.name)) return\n\n const extraction = extractJSXChildren(node.children)\n if (!extraction.valid) {\n if (markers.logging) {\n console.warn(`[better-translation] Non-static <${opening.name.name}> in ${filename}, skipping`)\n }\n return\n }\n\n const context = getJSXStringAttribute(opening.attributes, \"context\")\n const meta = context ? { context } : undefined\n const id = getJSXStringAttribute(opening.attributes, \"id\") ?? getMessageId(extraction.message, meta)\n messages.push({\n id,\n defaultMessage: extraction.message,\n meta: meta ?? {},\n placeholders: extraction.placeholders,\n source: createSource({\n filename,\n lineStarts,\n marker: opening.name.name,\n kind: \"component\",\n start: node.start,\n end: node.end,\n }),\n })\n\n if (!hasJSXAttribute(opening.attributes as Array<unknown>, \"id\")) {\n edits.push({\n start: opening.name.end,\n end: opening.name.end,\n replacement: ` id=\"${id}\"`,\n })\n }\n },\n })\n\n visitor.visit(result.program)\n return { parsed: true, messages, edits }\n}\n\nfunction isStringLiteral(node: Argument): node is StringLiteral {\n return node.type === \"Literal\" && typeof (node as StringLiteral).value === \"string\"\n}\n\nfunction getMetaArgument(node?: Argument) {\n if (!node) return undefined\n if (isStringLiteral(node)) return { context: node.value }\n\n if (node.type !== \"ObjectExpression\") return undefined\n\n const meta: TranslateOptions = {}\n\n for (const property of node.properties as Array<{\n type: string\n key?: { type: string; name?: string; value?: unknown }\n value?: Argument\n }>) {\n if (\n (property.type === \"ObjectProperty\" || property.type === \"Property\") &&\n ((property.key?.type === \"Identifier\" && (property.key.name === \"context\" || property.key.name === \"id\")) ||\n (property.key?.type === \"Literal\" && (property.key.value === \"context\" || property.key.value === \"id\"))) &&\n property.value &&\n isStringLiteral(property.value)\n ) {\n const key = property.key?.type === \"Identifier\" ? property.key.name : property.key?.value\n if (key === \"context\" || key === \"id\") meta[key] = property.value.value\n }\n }\n\n return Object.keys(meta).length > 0 ? meta : undefined\n}\n\nfunction getCallMetaArgument(args: Argument[]) {\n return getMetaArgument(isTranslateOptionsArgument(args[1]) ? args[1] : args[2])\n}\n\nfunction createCallOptionsEdit(code: string, args: Argument[], id: string) {\n const valuesArg = args[1]\n const optionsArg = isTranslateOptionsArgument(valuesArg) ? valuesArg : args[2]\n\n if (!optionsArg) {\n return {\n start: (valuesArg ?? args[0]!).end,\n end: (valuesArg ?? args[0]!).end,\n replacement: `, { id: ${JSON.stringify(id)} }`,\n }\n }\n\n if (isStringLiteral(optionsArg)) {\n const contextValue = code.slice(optionsArg.start, optionsArg.end)\n return {\n start: optionsArg.start,\n end: optionsArg.end,\n replacement: `{ id: ${JSON.stringify(id)}, context: ${contextValue} }`,\n }\n }\n\n if (optionsArg.type !== \"ObjectExpression\") return undefined\n if (hasObjectProperty(optionsArg, \"id\")) return undefined\n\n const objectSource = code.slice(optionsArg.start, optionsArg.end)\n const innerSource = objectSource.slice(1, -1)\n const replacement =\n innerSource.trim().length > 0 ? `{ id: ${JSON.stringify(id)},${innerSource} }` : `{ id: ${JSON.stringify(id)} }`\n\n return {\n start: optionsArg.start,\n end: optionsArg.end,\n replacement,\n }\n}\n\nfunction isTranslateOptionsArgument(node?: Argument) {\n if (!node) return false\n if (isStringLiteral(node)) return true\n if (node.type !== \"ObjectExpression\") return false\n\n return (node.properties as Array<unknown>).every((entry) => {\n const property = entry as\n | {\n type?: string\n key?: { type?: string; name?: string; value?: unknown }\n }\n | undefined\n\n if (property?.type !== \"ObjectProperty\" && property?.type !== \"Property\") return false\n\n return (\n (property.key?.type === \"Identifier\" && (property.key.name === \"context\" || property.key.name === \"id\")) ||\n (property.key?.type === \"Literal\" && (property.key.value === \"context\" || property.key.value === \"id\"))\n )\n })\n}\n\nfunction getJSXStringAttribute(attributes: Array<unknown>, name: string) {\n for (const attr of attributes as Array<{\n type: string\n name?: { type: string; name?: string }\n value?: { type: string; value?: unknown } | null\n }>) {\n if (\n attr.type === \"JSXAttribute\" &&\n attr.name?.type === \"JSXIdentifier\" &&\n attr.name.name === name &&\n attr.value?.type === \"Literal\" &&\n typeof attr.value.value === \"string\"\n ) {\n return attr.value.value as string\n }\n }\n}\n\nfunction hasJSXAttribute(attributes: Array<unknown>, name: string) {\n return attributes.some((attr) => {\n const attribute = attr as\n | {\n type?: string\n name?: { type?: string; name?: string }\n }\n | undefined\n\n return attribute?.type === \"JSXAttribute\" && attribute.name?.type === \"JSXIdentifier\" && attribute.name.name === name\n })\n}\n\nfunction hasObjectProperty(node: { properties: Array<unknown> }, name: string) {\n return node.properties.some((entry) => {\n const property = entry as\n | {\n type?: string\n key?: { type?: string; name?: string; value?: unknown }\n }\n | undefined\n\n if (property?.type !== \"ObjectProperty\" && property?.type !== \"Property\") return false\n return (\n (property.key?.type === \"Identifier\" && property.key.name === name) ||\n (property.key?.type === \"Literal\" && property.key.value === name)\n )\n })\n}\n\nfunction extractPlaceholdersFromMessage(message: string) {\n const names = new Set<string>()\n for (const match of message.matchAll(/\\{(\\w+)\\}/g)) {\n if (match[1]) names.add(match[1])\n }\n return [...names]\n}\n\nfunction createSource({\n filename,\n lineStarts,\n marker,\n kind,\n start,\n end,\n}: {\n filename: string\n lineStarts: number[]\n marker: string\n kind: MessageSource[\"kind\"]\n start: number\n end: number\n}): MessageSource {\n const startPosition = getPosition(start, lineStarts)\n const endPosition = getPosition(end, lineStarts)\n\n return {\n file: filename,\n kind,\n marker,\n line: startPosition.line,\n column: startPosition.column,\n endLine: endPosition.line,\n endColumn: endPosition.column,\n start,\n end,\n }\n}\n\nfunction getLineStarts(code: string) {\n const starts = [0]\n for (let i = 0; i < code.length; i++) {\n if (code[i] === \"\\n\") starts.push(i + 1)\n }\n return starts\n}\n\nfunction getPosition(offset: number, lineStarts: number[]) {\n let low = 0\n let high = lineStarts.length - 1\n\n while (low <= high) {\n const mid = Math.floor((low + high) / 2)\n const lineStart = lineStarts[mid]!\n const nextLineStart = lineStarts[mid + 1] ?? Number.POSITIVE_INFINITY\n\n if (offset < lineStart) {\n high = mid - 1\n continue\n }\n\n if (offset >= nextLineStart) {\n low = mid + 1\n continue\n }\n\n return { line: mid + 1, column: offset - lineStart + 1 }\n }\n\n const lastLine = lineStarts.length - 1\n const lastStart = lineStarts[lastLine] ?? 0\n return { line: lastLine + 1, column: offset - lastStart + 1 }\n}\n\ninterface ExtractionResult {\n message: string\n placeholders: string[]\n valid: boolean\n}\n\nfunction extractJSXChildren(children: Array<JSXChild>): ExtractionResult {\n const parts: string[] = []\n const placeholders: string[] = []\n\n for (const child of children) {\n switch (child.type) {\n case \"JSXText\":\n parts.push(child.value)\n break\n\n case \"JSXElement\": {\n const name = child.openingElement.name\n if (name.type !== \"JSXIdentifier\" || name.name !== \"Var\") {\n return { message: \"\", placeholders: [], valid: false }\n }\n\n const varName = getVarPlaceholderName(child)\n if (!varName) return { message: \"\", placeholders: [], valid: false }\n\n placeholders.push(varName)\n parts.push(`{${varName}}`)\n break\n }\n\n case \"JSXExpressionContainer\":\n if (child.expression.type !== \"JSXEmptyExpression\") {\n return { message: \"\", placeholders: [], valid: false }\n }\n break\n\n default:\n break\n }\n }\n\n const message = parts.join(\"\").replace(/\\s+/g, \" \").trim()\n return { message, placeholders, valid: message.length > 0 }\n}\n\nfunction getVarPlaceholderName(node: { openingElement: { attributes: Array<unknown> }; children: Array<JSXChild> }) {\n const explicitName = getJSXStringAttribute(node.openingElement.attributes as Array<unknown>, \"name\")\n if (explicitName) return explicitName\n\n const customPropName = getSingleJSXAttributeName(node.openingElement.attributes as Array<unknown>)\n if (customPropName) return customPropName\n\n return getVarChildIdentifier(node.children)\n}\n\nfunction getSingleJSXAttributeName(attributes: Array<unknown>) {\n const keys = attributes.flatMap((attr) => {\n const attribute = attr as\n | {\n type?: string\n name?: { type?: string; name?: string }\n }\n | undefined\n\n if (attribute?.type !== \"JSXAttribute\" || attribute.name?.type !== \"JSXIdentifier\") return []\n return [attribute.name.name]\n })\n\n return keys.length === 1 ? keys[0] : undefined\n}\n\nfunction getVarChildIdentifier(children: Array<JSXChild>) {\n const meaningfulChildren = children.filter((child) => !(child.type === \"JSXText\" && child.value.trim().length === 0))\n if (meaningfulChildren.length !== 1) return undefined\n\n const [child] = meaningfulChildren\n if (!child || child.type !== \"JSXExpressionContainer\" || child.expression.type !== \"Identifier\") return undefined\n return child.expression.name\n}\n","import { existsSync, readFileSync } from \"node:fs\"\nimport { dirname, resolve } from \"node:path\"\nimport { fileURLToPath } from \"node:url\"\n\nimport type { BetterTranslateRuntimeConfig } from \"./types.js\"\n\nexport const DEFAULT_LOCAL_OUTPUT_DIR = \"src/lib/bt\"\nexport const DEFAULT_PUBLIC_OUTPUT_SUBDIR = \"bt\"\nexport const DEFAULT_PUBLIC_BASE_PATH = \"/bt\"\nexport const RUNTIME_CONFIG_FILENAME = \"runtime.json\"\nexport const COMMON_RUNTIME_CONFIG_DIRS = [\n DEFAULT_LOCAL_OUTPUT_DIR,\n `public/${DEFAULT_PUBLIC_OUTPUT_SUBDIR}`,\n `assets/${DEFAULT_LOCAL_OUTPUT_DIR}`,\n]\n\nexport function getRuntimeConfigPath(root: string, dir: string) {\n return resolve(root, dir, RUNTIME_CONFIG_FILENAME)\n}\n\nexport function getRuntimeConfigCandidatePaths(importMetaUrl: string, dirs = COMMON_RUNTIME_CONFIG_DIRS) {\n return getSearchBaseDirs(importMetaUrl).flatMap((baseDir) => dirs.map((dir) => resolve(baseDir, dir, RUNTIME_CONFIG_FILENAME)))\n}\n\nexport function getLocalConfigCandidatePaths(dir: string, importMetaUrl: string, fileName: string) {\n return getSearchBaseDirs(importMetaUrl).map((baseDir) => resolve(baseDir, dir, fileName))\n}\n\nexport function readRuntimeConfigFromPaths(paths: string[]) {\n for (const path of dedupe(paths)) {\n if (!existsSync(path)) continue\n try {\n return JSON.parse(readFileSync(path, \"utf-8\")) as BetterTranslateRuntimeConfig\n } catch {\n continue\n }\n }\n return null\n}\n\nfunction dedupe(paths: string[]) {\n return [...new Set(paths)]\n}\n\nfunction getSearchBaseDirs(importMetaUrl: string) {\n const currentDir = dirname(fileURLToPath(importMetaUrl))\n const candidates = [...collectParentDirs(process.cwd()), ...collectParentDirs(currentDir)]\n return dedupe(candidates)\n}\n\nfunction collectParentDirs(startDir: string, maxDepth = 6) {\n const dirs: string[] = []\n let currentDir = startDir\n\n for (let i = 0; i <= maxDepth; i++) {\n dirs.push(currentDir)\n const parentDir = dirname(currentDir)\n if (parentDir === currentDir) break\n currentDir = parentDir\n }\n\n return dirs\n}\n","import { execFileSync } from \"node:child_process\"\nimport { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from \"node:fs\"\nimport { dirname, relative, resolve } from \"node:path\"\nimport { normalizePath, type Plugin, type ResolvedConfig } from \"vite\"\n\nimport type {\n BetterTranslatePluginOptions,\n BetterTranslateRuntimeOptions,\n BetterTranslateRuntimeConfig,\n ExtractedMessage,\n ManifestEntry,\n MessageManifest,\n MessageManifestFile,\n MessageSource,\n RuntimeMessages,\n TranslateFn,\n TranslateMessage,\n TranslationCache,\n} from \"./types.js\"\n\nimport { createEmptyCache, getCacheKey, loadCache, saveCache } from \"./cache.js\"\nimport { analyzeSourceFile } from \"./extractor.js\"\nimport { serializeMeta } from \"./message-id.js\"\nimport {\n DEFAULT_LOCAL_OUTPUT_DIR,\n DEFAULT_PUBLIC_BASE_PATH,\n DEFAULT_PUBLIC_OUTPUT_SUBDIR,\n getRuntimeConfigPath,\n} from \"./runtime-config.js\"\n\nconst PREFIX = \"\\x1b[36m[better-translation]\\x1b[0m\"\nconst DIM = \"\\x1b[2m\"\nconst RESET = \"\\x1b[0m\"\nconst BOLD = \"\\x1b[1m\"\nconst CYAN = \"\\x1b[36m\"\nconst REMOTE_API_BASE_URL = \"https://better-translation.dev\"\nconst DEFAULT_REMOTE_API_KEY_ENV = \"BETTER_TRANSLATION_API_KEY\"\nconst DEFAULT_REMOTE_BRANCH = \"main\"\nconst DEFAULT_CACHE_DIR = \".cache/better-translation\"\nconst DEFAULT_TRANSLATION_CACHE_FILE = `${DEFAULT_CACHE_DIR}/cache.json`\nconst DEFAULT_REMOTE_OFFLINE_OUTPUT_DIR = `${DEFAULT_CACHE_DIR}/runtime`\nconst DEFAULT_ROOT_DIR = \"src\"\nconst DEFAULT_SCAN_EXTENSIONS = [\".ts\", \".tsx\", \".js\", \".jsx\"]\nconst VIRTUAL_MESSAGES_MODULE_ID = \"better-translation/messages\"\nconst RESOLVED_VIRTUAL_MESSAGES_MODULE_ID = `\\0${VIRTUAL_MESSAGES_MODULE_ID}`\nconst CALL_MARKERS = [\"t\", \"useT\"]\nconst COMPONENT_MARKERS = [\"T\"]\nconst PRIVATE_MANIFEST_FILENAME = \"manifest.json\"\nconst LOCALES_SUBDIR = \"locales\"\nconst GITIGNORE_FILENAME = \".gitignore\"\nconst GITIGNORE_CONTENTS = [\"# Generated by better-translation/vite\", \"manifest.json\", \"\"].join(\"\\n\")\n\ninterface SyncResult {\n manifestChanged: boolean\n localeMessagesChanged: boolean\n}\n\nfunction formatLocale(locale: string) {\n return locale.toUpperCase()\n}\n\nfunction formatLocales(locales: string[]) {\n return locales.map(formatLocale).join(\", \")\n}\n\n/** Scans source files for translatable messages and keeps locale JSON files in sync. */\nexport function betterTranslation(options: BetterTranslatePluginOptions): Plugin {\n const {\n locales,\n defaultLocale = locales[0] ?? \"en\",\n rootDir = DEFAULT_ROOT_DIR,\n cacheFile = DEFAULT_TRANSLATION_CACHE_FILE,\n logging = true,\n runtime,\n } = options\n const configuredRuntime = normalizeRuntimeOptions(runtime)\n const manifest: MessageManifest = {}\n const fileMessages = new Map<string, ExtractedMessage[]>()\n let cache: TranslationCache = createEmptyCache()\n let resolvedRuntime = configuredRuntime\n let usesLocalStorage = shouldUseLocalStorage(configuredRuntime, false)\n let resolvedTranslate: TranslateFn | undefined = configuredRuntime.type === \"local\" ? configuredRuntime.translate : undefined\n let localesDir =\n configuredRuntime.type === \"local\" ? (configuredRuntime.output ?? DEFAULT_LOCAL_OUTPUT_DIR) : DEFAULT_LOCAL_OUTPUT_DIR\n let publicBasePath =\n configuredRuntime.type === \"local\" && configuredRuntime.target === \"public\"\n ? (configuredRuntime.basePath ?? DEFAULT_PUBLIC_BASE_PATH)\n : DEFAULT_PUBLIC_BASE_PATH\n let remoteUrl = configuredRuntime.type === \"remote\" ? (configuredRuntime.endpoint ?? REMOTE_API_BASE_URL) : REMOTE_API_BASE_URL\n let root = \"\"\n let isDev = false\n let translateTimer: ReturnType<typeof setTimeout> | null = null\n let remoteSyncTimer: ReturnType<typeof setTimeout> | null = null\n let lastSyncedRemoteManifestSignature: string | null = null\n let sourceRoots: string[] = []\n\n function log(message: string) {\n if (logging) console.log(message)\n }\n\n async function syncRemote() {\n if (resolvedRuntime.type !== \"remote\") return\n\n const payload = {\n defaultLocale,\n locales,\n messages: buildMessageManifest(),\n }\n const signature = JSON.stringify(payload)\n if (lastSyncedRemoteManifestSignature === signature) return\n\n const apiKey = resolveRemoteSyncApiKey()\n const target = formatRemoteManifestTarget(resolvedRuntime, remoteUrl)\n if (!apiKey) {\n throw new Error(\n [\n `${PREFIX} remote Manifest sync requires a Project API key`,\n `set ${DEFAULT_REMOTE_API_KEY_ENV} or pass runtime.apiKey in the Vite plugin config`,\n `target: ${target}`,\n ].join(\"\\n\"),\n )\n }\n\n let response: Response\n try {\n response = await fetch(target, {\n method: \"POST\",\n headers: {\n authorization: `Bearer ${apiKey}`,\n \"content-type\": \"application/json\",\n },\n body: JSON.stringify(payload),\n })\n } catch (error) {\n throw new Error(formatRemoteSyncNetworkError(target, error))\n }\n\n const body = await response.text()\n\n if (!response.ok) {\n throw new Error(formatRemoteSyncError(response, target, body))\n }\n\n const result = parseRemoteSyncResult(body)\n const messageCount = result?.messageCount ?? Object.keys(payload.messages).length\n log(\n `${PREFIX} ${BOLD}Synced${RESET} ${CYAN}${messageCount}${RESET} ${messageCount === 1 ? \"Message\" : \"Messages\"} -> ${CYAN}${formatRuntime(resolvedRuntime)}${RESET}`,\n )\n lastSyncedRemoteManifestSignature = signature\n }\n\n function resolveRemoteSyncApiKey() {\n const explicitApiKey = resolvedRuntime.type === \"remote\" ? resolvedRuntime.apiKey?.trim() : null\n if (explicitApiKey) return explicitApiKey\n\n const envApiKey = process.env[DEFAULT_REMOTE_API_KEY_ENV]?.trim()\n return envApiKey || null\n }\n\n function buildMessageManifest(): MessageManifestFile {\n return Object.fromEntries(\n Object.entries(manifest).map(([id, entry]) => [\n id,\n {\n defaultMessage: entry.defaultMessage,\n meta: entry.meta,\n placeholders: entry.placeholders,\n sources: entry.sources,\n },\n ]),\n )\n }\n\n function shouldScanFile(id: string) {\n const cleanId = id.split(\"?\", 1)[0] ?? id\n if (cleanId.includes(\"node_modules\")) return false\n const extension = DEFAULT_SCAN_EXTENSIONS.find((ext) => cleanId.endsWith(ext))\n if (!extension) return false\n return sourceRoots.some(\n (sourceRoot) => cleanId === sourceRoot || cleanId.startsWith(`${sourceRoot}/`) || cleanId.startsWith(`${sourceRoot}\\\\`),\n )\n }\n\n function getPrivateManifestPath() {\n return resolve(root, localesDir, PRIVATE_MANIFEST_FILENAME)\n }\n\n function getRuntimeConfig(): BetterTranslateRuntimeConfig {\n return {\n runtime: getRuntimeConfigRuntime(resolvedRuntime),\n defaultLocale,\n locales,\n }\n }\n\n function writeRuntimeConfig() {\n if (!usesLocalStorage) return\n\n const runtimeConfig = JSON.stringify(getRuntimeConfig(), null, 2) + \"\\n\"\n const path = getRuntimeConfigPath(root, localesDir)\n const dir = dirname(path)\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true })\n writeFileIfChanged(path, runtimeConfig)\n }\n\n function getLocalesDirPath() {\n return resolve(root, localesDir, LOCALES_SUBDIR)\n }\n\n function getLocalePath(locale: string) {\n return resolve(getLocalesDirPath(), `${locale}.json`)\n }\n\n function readLocaleMessages(locale: string): RuntimeMessages {\n const path = getLocalePath(locale)\n if (!existsSync(path)) return {}\n\n try {\n const input = JSON.parse(readFileSync(path, \"utf-8\")) as unknown\n return normalizeLocaleMessages(input)\n } catch {\n return {}\n }\n }\n\n function writePrivateManifest() {\n if (!usesLocalStorage) return\n const path = getPrivateManifestPath()\n const dir = dirname(path)\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true })\n writeFileIfChanged(path, JSON.stringify(buildMessageManifest(), null, 2) + \"\\n\")\n }\n\n function assertFileContents(path: string, expected: string, label: string) {\n if (!existsSync(path)) {\n throw new Error(`${PREFIX} missing committed ${label} at ${relative(root, path)}`)\n }\n const actual = readFileSync(path, \"utf-8\")\n if (actual !== expected) {\n throw new Error(\n [\n `${PREFIX} committed ${label} is out of date`,\n `expected committed file at ${relative(root, path)} to match the generated output`,\n `run the dev workflow to regenerate locale artifacts and commit the result`,\n ].join(\"\\n\"),\n )\n }\n }\n\n function assertJsonFileContents(path: string, expected: unknown, label: string) {\n if (!existsSync(path)) {\n throw new Error(`${PREFIX} missing committed ${label} at ${relative(root, path)}`)\n }\n\n let actual: unknown\n try {\n actual = JSON.parse(readFileSync(path, \"utf-8\")) as unknown\n } catch {\n throw new Error(`${PREFIX} committed ${label} is invalid JSON at ${relative(root, path)}`)\n }\n\n if (JSON.stringify(actual) !== JSON.stringify(expected)) {\n throw new Error(\n [\n `${PREFIX} committed ${label} is out of date`,\n `expected committed file at ${relative(root, path)} to match the generated output`,\n `run the dev workflow to regenerate locale artifacts and commit the result`,\n ].join(\"\\n\"),\n )\n }\n }\n\n function writeGitignore() {\n if (!usesLocalStorage) return\n const path = resolve(root, localesDir, GITIGNORE_FILENAME)\n const dir = dirname(path)\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true })\n writeFileIfChanged(path, GITIGNORE_CONTENTS)\n }\n\n function assertGeneratedFilesCommitted() {\n if (!usesLocalStorage) return\n assertJsonFileContents(getRuntimeConfigPath(root, localesDir), getRuntimeConfig(), \"runtime config\")\n assertFileContents(resolve(root, localesDir, GITIGNORE_FILENAME), GITIGNORE_CONTENTS, \"generated .gitignore\")\n }\n\n function buildLocalLocaleMessages(locale: string): RuntimeMessages {\n const existingMessages = readLocaleMessages(locale)\n const messages: RuntimeMessages = {}\n\n if (locale === defaultLocale) {\n for (const [id, entry] of Object.entries(manifest)) {\n messages[id] = entry.defaultMessage\n }\n return messages\n }\n\n for (const id of Object.keys(manifest)) {\n if (Object.hasOwn(messages, id)) continue\n const entry = manifest[id]!\n const existingMessage = existingMessages[id]\n const cachedMessage = getFreshCachedMessage(id, locale)\n\n if (existingMessage !== undefined && !isUntranslatedLocaleValue(existingMessage, entry)) {\n messages[id] = existingMessage\n continue\n }\n\n if (cachedMessage !== undefined) messages[id] = cachedMessage\n else if (shouldWriteDefaultLocaleFallback()) messages[id] = entry.defaultMessage\n }\n return messages\n }\n\n function writeLocaleFilesToDisk() {\n if (!usesLocalStorage) return\n const dir = getLocalesDirPath()\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true })\n for (const locale of locales) {\n writeFileIfChanged(resolve(dir, `${locale}.json`), JSON.stringify(buildLocalLocaleMessages(locale), null, 2) + \"\\n\")\n }\n }\n\n function getMissingMessagesByLocale() {\n const missingByLocale = new Map<string, TranslateMessage[]>()\n\n for (const locale of locales) {\n if (locale === defaultLocale) continue\n const existingMessages = readLocaleMessages(locale)\n for (const [id, entry] of Object.entries(manifest)) {\n const existingMessage = existingMessages[id]\n const hasExistingMessage = existingMessage !== undefined && !isUntranslatedLocaleValue(existingMessage, entry)\n if (!hasExistingMessage && getFreshCachedMessage(id, locale) === undefined) {\n const misses = missingByLocale.get(locale) ?? []\n misses.push({\n id,\n text: entry.defaultMessage,\n meta: entry.meta,\n placeholders: entry.placeholders,\n sources: entry.sources,\n })\n missingByLocale.set(locale, misses)\n }\n }\n }\n\n return missingByLocale\n }\n\n function assertLocalBuildTranslationsComplete() {\n const expectedIds = new Set(Object.keys(manifest))\n const issues: string[] = []\n\n for (const locale of locales) {\n const localePath = getLocalePath(locale)\n if (!existsSync(localePath)) {\n issues.push(`- ${locale}: missing file at ${relative(root, localePath)}`)\n continue\n }\n\n const localeMessages = readLocaleMessages(locale)\n const missingIds = [...expectedIds].filter((id) => !Object.hasOwn(localeMessages, id))\n const orphanIds = Object.keys(localeMessages).filter((id) => !expectedIds.has(id))\n\n if (locale === defaultLocale) {\n const staleIds = [...expectedIds].filter((id) => localeMessages[id] !== manifest[id]!.defaultMessage)\n if (missingIds.length > 0) issues.push(formatLocaleIssue(locale, \"missing\", missingIds))\n if (orphanIds.length > 0) issues.push(formatLocaleIssue(locale, \"orphaned\", orphanIds))\n if (staleIds.length > 0) issues.push(formatLocaleIssue(locale, \"outdated default messages\", staleIds))\n continue\n }\n\n if (missingIds.length > 0) issues.push(formatLocaleIssue(locale, \"missing\", missingIds))\n if (orphanIds.length > 0) issues.push(formatLocaleIssue(locale, \"orphaned\", orphanIds))\n }\n\n if (issues.length === 0) return\n\n throw new Error(\n [\n `${PREFIX} committed locale artifacts are out of sync for local production build`,\n `local production builds are check-only and never regenerate locale files`,\n `run the dev workflow to regenerate locale artifacts and commit the result`,\n ...issues,\n ].join(\"\\n\"),\n )\n }\n\n async function translateMissingMessages() {\n if (!resolvedTranslate) return false\n const missingByLocale = getMissingMessagesByLocale()\n\n const totalMisses = [...missingByLocale.values()].reduce((count, misses) => count + misses.length, 0)\n if (totalMisses === 0) return false\n\n const missLocales = [...missingByLocale.keys()]\n log(\n `${PREFIX} ${BOLD}Translating${RESET} ${CYAN}${totalMisses}${RESET} ${totalMisses === 1 ? \"Message\" : \"Messages\"} -> ${CYAN}${formatLocales(missLocales)}${RESET}`,\n )\n\n let translatedCount = 0\n for (const [locale, misses] of missingByLocale) {\n const result = await resolvedTranslate(misses, locale)\n\n for (const miss of misses) {\n const translated = result[miss.id]?.trim()\n if (!translated) continue\n cache.entries[getCacheKey(miss.id, locale)] = {\n sourceText: miss.text,\n meta: miss.meta,\n locale,\n translation: translated,\n timestamp: Date.now(),\n }\n translatedCount += 1\n }\n }\n\n log(\n `${PREFIX} ${BOLD}Translated${RESET} ${CYAN}${translatedCount}${RESET}/${CYAN}${totalMisses}${RESET} ${totalMisses === 1 ? \"Message\" : \"Messages\"} -> ${CYAN}${formatLocales(missLocales)}${RESET}`,\n )\n\n return true\n }\n\n function getFreshCachedMessage(id: string, locale: string) {\n const entry = manifest[id]\n const cachedMessage = cache.entries[getCacheKey(id, locale)]\n if (!entry || !cachedMessage) return undefined\n if (cachedMessage.sourceText !== entry.defaultMessage) return undefined\n if (serializeMeta(cachedMessage.meta) !== serializeMeta(entry.meta)) return undefined\n return cachedMessage.translation\n }\n\n function isUntranslatedLocaleValue(value: string, entry: Pick<ManifestEntry, \"defaultMessage\">) {\n return resolvedTranslate !== undefined && value.trim() === entry.defaultMessage.trim()\n }\n\n function shouldWriteDefaultLocaleFallback() {\n return resolvedTranslate !== undefined || isRemoteOfflineDev(resolvedRuntime, isDev)\n }\n\n function scheduleDevTranslation() {\n if (!resolvedTranslate) return\n if (!isDev) return\n if (translateTimer) clearTimeout(translateTimer)\n translateTimer = setTimeout(async () => {\n const translated = await translateMissingMessages()\n if (translated) saveCache(resolve(root, cacheFile), cache)\n writeLocaleFilesToDisk()\n writePrivateManifest()\n }, 1000)\n }\n\n function scheduleDevRemoteSync() {\n if (usesLocalStorage || !isDev) return\n if (remoteSyncTimer) clearTimeout(remoteSyncTimer)\n remoteSyncTimer = setTimeout(() => {\n void syncRemote().catch((error) => console.error(error instanceof Error ? error.message : error))\n }, 1000)\n }\n\n function removeFileMessages(file: string) {\n const previous = fileMessages.get(file)\n if (!previous) return false\n\n for (const message of previous) {\n const entry = manifest[message.id]\n if (!entry) continue\n entry.sources = entry.sources.filter((source) => !isSameSource(source, message.source))\n if (entry.sources.length === 0) delete manifest[message.id]\n }\n\n fileMessages.delete(file)\n return true\n }\n\n function syncFileMessages(file: string, messages: ExtractedMessage[]): SyncResult {\n const previousMessages = fileMessages.get(file) ?? []\n const nextEntries = groupMessagesById(messages)\n for (const [id, entry] of Object.entries(nextEntries)) {\n const existing = manifest[id]\n if (existing && !hasSameMessageShape(existing, entry)) {\n throw new Error(formatCollisionError(id, existing, entry))\n }\n }\n\n removeFileMessages(file)\n for (const [id, entry] of Object.entries(nextEntries)) {\n if (!manifest[id]) {\n manifest[id] = entry\n continue\n }\n for (const source of entry.sources) {\n if (!manifest[id]!.sources.some((existingSource) => isSameSource(existingSource, source))) {\n manifest[id]!.sources.push(source)\n }\n }\n }\n\n if (messages.length > 0) fileMessages.set(file, messages)\n return {\n manifestChanged:\n previousMessages.length !== messages.length ||\n previousMessages.some((message, index) => !isSameExtractedMessage(message, messages[index])),\n localeMessagesChanged:\n previousMessages.length !== messages.length ||\n previousMessages.some((message, index) => !hasSameMessageShape(message, messages[index]!)),\n }\n }\n\n function syncSourceCode(file: string, code: string) {\n const analysis = analyzeSourceFile(code, file, {\n call: CALL_MARKERS,\n component: COMPONENT_MARKERS,\n logging,\n })\n if (!analysis.parsed) return null\n return syncFileMessages(\n file,\n analysis.messages.map((message) => ({\n ...message,\n source: {\n ...message.source,\n file: toRootRelativePath(message.source.file),\n },\n })),\n )\n }\n\n function removeTrackedFile(file: string): SyncResult {\n const hadPreviousMessages = removeFileMessages(file)\n return {\n manifestChanged: hadPreviousMessages,\n localeMessagesChanged: hadPreviousMessages,\n }\n }\n\n function applySyncResult(syncResult: SyncResult | null, options: { scheduleTranslation: boolean }) {\n if (!syncResult) return\n if (syncResult.localeMessagesChanged) {\n writeLocaleFilesToDisk()\n writePrivateManifest()\n if (options.scheduleTranslation) scheduleDevTranslation()\n return\n }\n if (syncResult.manifestChanged) writePrivateManifest()\n }\n\n function scanAllSourceFiles() {\n for (const id of Object.keys(manifest)) delete manifest[id]\n fileMessages.clear()\n\n for (const sourceRoot of sourceRoots) {\n if (!existsSync(sourceRoot)) continue\n for (const file of collectScanFiles(sourceRoot).sort()) {\n const code = readFileSync(file, \"utf-8\")\n syncSourceCode(file, code)\n }\n }\n }\n\n function toRootRelativePath(file: string) {\n return relative(root, file).replaceAll(\"\\\\\", \"/\")\n }\n\n function createVirtualMessagesModule() {\n if (isRemoteOfflineDev(resolvedRuntime, isDev)) {\n return createModuleMessagesModule(locales, (locale) => `/${normalizePath(toRootRelativePath(getLocalePath(locale)))}`)\n }\n if (resolvedRuntime.type === \"remote\") return createRemoteMessagesModule(resolvedRuntime, locales, remoteUrl)\n if (resolvedRuntime.target === \"public\") return createPublicMessagesModule(locales, publicBasePath)\n return createModuleMessagesModule(locales, (locale) => `/${normalizePath(toRootRelativePath(getLocalePath(locale)))}`)\n }\n\n return {\n name: \"better-translation-extract\",\n enforce: \"pre\",\n\n config() {\n return {\n ssr: {\n noExternal: [\"better-translation\"],\n },\n }\n },\n\n configResolved(config) {\n root = config.root\n isDev = config.command === \"serve\"\n resolvedRuntime = resolveRuntimeOptions(configuredRuntime, config)\n usesLocalStorage = shouldUseLocalStorage(resolvedRuntime, isDev)\n resolvedTranslate = resolvedRuntime.type === \"local\" ? resolvedRuntime.translate : undefined\n localesDir = getRuntimeOutputDir(resolvedRuntime, isDev)\n publicBasePath =\n resolvedRuntime.type === \"local\" && resolvedRuntime.target === \"public\"\n ? (resolvedRuntime.basePath ?? DEFAULT_PUBLIC_BASE_PATH)\n : DEFAULT_PUBLIC_BASE_PATH\n remoteUrl = resolvedRuntime.type === \"remote\" ? (resolvedRuntime.endpoint ?? REMOTE_API_BASE_URL) : REMOTE_API_BASE_URL\n sourceRoots = (Array.isArray(rootDir) ? rootDir : [rootDir]).map((dir) => resolve(root, dir))\n log(\n `${PREFIX} Locales: ${CYAN}${formatLocales(locales)}${RESET} | Default: ${CYAN}${formatLocale(defaultLocale)}${RESET} | Runtime: ${CYAN}${formatRuntime(resolvedRuntime)}${RESET} | Out Dir: ${DIM}${usesLocalStorage ? localesDir : \"n/a\"}${RESET} | Roots: ${DIM}${(Array.isArray(rootDir) ? rootDir : [rootDir]).join(\", \")}${RESET}`,\n )\n },\n\n resolveId(id) {\n if (id === VIRTUAL_MESSAGES_MODULE_ID) return RESOLVED_VIRTUAL_MESSAGES_MODULE_ID\n },\n\n load(id) {\n if (id === RESOLVED_VIRTUAL_MESSAGES_MODULE_ID) return createVirtualMessagesModule()\n },\n\n async buildStart() {\n cache = loadCache(resolve(root, cacheFile))\n scanAllSourceFiles()\n if (usesLocalStorage && !isDev) {\n assertGeneratedFilesCommitted()\n assertLocalBuildTranslationsComplete()\n return\n }\n writeRuntimeConfig()\n writeGitignore()\n writeLocaleFilesToDisk()\n writePrivateManifest()\n\n if (isDev) scheduleDevTranslation()\n },\n\n configureServer(server) {\n server.watcher.add(sourceRoots)\n server.httpServer?.once(\"listening\", () => scheduleDevRemoteSync())\n\n const syncFileFromDisk = (file: string) => {\n if (!shouldScanFile(file) || !existsSync(file)) return\n applySyncResult(syncSourceCode(file, readFileSync(file, \"utf-8\")), { scheduleTranslation: true })\n scheduleDevRemoteSync()\n }\n\n const removeFileFromManifest = (file: string) => {\n if (!shouldScanFile(file)) return\n applySyncResult(removeTrackedFile(file), { scheduleTranslation: true })\n scheduleDevRemoteSync()\n }\n\n server.watcher.on(\"add\", syncFileFromDisk)\n server.watcher.on(\"change\", syncFileFromDisk)\n server.watcher.on(\"unlink\", removeFileFromManifest)\n },\n\n transform(code, id) {\n const cleanId = id.split(\"?\", 1)[0] ?? id\n if (!shouldScanFile(cleanId)) return\n\n const analysis = analyzeSourceFile(code, cleanId, {\n call: CALL_MARKERS,\n component: COMPONENT_MARKERS,\n logging,\n })\n\n if (analysis.edits.length === 0) return\n return {\n code: applyEdits(code, analysis.edits),\n map: null,\n }\n },\n\n async generateBundle() {\n if (usesLocalStorage) {\n if (!isDev) {\n assertGeneratedFilesCommitted()\n }\n assertLocalBuildTranslationsComplete()\n } else if (isDev) {\n await translateMissingMessages()\n }\n\n if (!usesLocalStorage) {\n await syncRemote()\n }\n },\n\n closeBundle() {\n if (usesLocalStorage && !isDev) return\n saveCache(resolve(root, cacheFile), cache)\n },\n }\n}\n\nfunction formatLocaleIssue(locale: string, label: string, ids: string[]) {\n const preview = ids\n .slice(0, 5)\n .map((id) => JSON.stringify(id))\n .join(\", \")\n const suffix = ids.length > 5 ? `, ... ${ids.length - 5} more` : \"\"\n return `- ${locale}: ${label} (${preview}${suffix})`\n}\n\nfunction shouldUseLocalStorage(runtime: BetterTranslateRuntimeOptions, isDev: boolean) {\n return runtime.type === \"local\" || isRemoteOfflineDev(runtime, isDev)\n}\n\nfunction isRemoteOfflineDev(runtime: BetterTranslateRuntimeOptions, isDev: boolean) {\n return runtime.type === \"remote\" && isDev && runtime.dev?.offline === true\n}\n\nfunction getRuntimeConfigRuntime(runtime: BetterTranslateRuntimeOptions): BetterTranslateRuntimeConfig[\"runtime\"] {\n if (runtime.type === \"local\") return runtime\n\n return {\n type: \"remote\",\n projectId: runtime.projectId,\n ...(runtime.endpoint === undefined ? {} : { endpoint: runtime.endpoint }),\n ...(runtime.branch === undefined ? {} : { branch: runtime.branch }),\n ...(runtime.dev === undefined ? {} : { dev: runtime.dev }),\n }\n}\n\nfunction getRuntimeOutputDir(runtime: BetterTranslateRuntimeOptions, isDev: boolean) {\n if (runtime.type === \"local\") return runtime.output!\n if (isRemoteOfflineDev(runtime, isDev)) return DEFAULT_REMOTE_OFFLINE_OUTPUT_DIR\n return DEFAULT_LOCAL_OUTPUT_DIR\n}\n\nfunction normalizeRuntimeOptions(runtime: BetterTranslateRuntimeOptions | undefined): BetterTranslateRuntimeOptions {\n if (runtime) return runtime.type === \"local\" ? { ...runtime, target: runtime.target ?? \"module\" } : runtime\n return { type: \"local\", target: \"module\" }\n}\n\nfunction resolveRuntimeOptions(runtime: BetterTranslateRuntimeOptions, config: ResolvedConfig): BetterTranslateRuntimeOptions {\n if (runtime.type === \"remote\") {\n const projectId = typeof runtime.projectId === \"string\" ? runtime.projectId.trim() : \"\"\n if (!projectId) {\n throw new Error(`${PREFIX} remote runtime requires a projectId`)\n }\n return {\n ...runtime,\n projectId,\n endpoint: runtime.endpoint ?? REMOTE_API_BASE_URL,\n branch: resolveBranch(runtime, config.root),\n dev: {\n offline: runtime.dev?.offline ?? false,\n },\n }\n }\n\n const target = runtime.target ?? \"module\"\n if (target === \"module\") {\n return {\n ...runtime,\n target,\n output: runtime.output ?? DEFAULT_LOCAL_OUTPUT_DIR,\n }\n }\n\n if (!config.publicDir) {\n throw new Error(`${PREFIX} runtime target \"public\" requires Vite publicDir to be enabled`)\n }\n\n const output = runtime.output ?? normalizePath(relative(config.root, resolve(config.publicDir, DEFAULT_PUBLIC_OUTPUT_SUBDIR)))\n const outputPath = resolve(config.root, output)\n const publicBasePath = runtime.basePath ?? inferPublicBasePath(outputPath, config.publicDir)\n\n return {\n ...runtime,\n target,\n output,\n basePath: publicBasePath,\n }\n}\n\nfunction inferPublicBasePath(outputPath: string, publicDir: string) {\n const relativeToPublic = normalizePath(relative(publicDir, outputPath))\n if (relativeToPublic.startsWith(\"..\")) {\n throw new Error(`${PREFIX} runtime target \"public\" output must be inside Vite publicDir unless basePath is provided`)\n }\n return `/${relativeToPublic}`.replace(/\\/$/, \"\")\n}\n\nfunction formatRuntime(runtime: BetterTranslateRuntimeOptions) {\n return runtime.type === \"local\" ? `Local/${runtime.target ?? \"module\"}` : `Remote/${runtime.branch ?? \"auto\"}`\n}\n\nfunction resolveBranch(runtime: Pick<Extract<BetterTranslateRuntimeOptions, { type: \"remote\" }>, \"branch\">, root: string) {\n if (runtime.branch && runtime.branch !== \"auto\") return runtime.branch\n\n const envBranch = process.env.BETTER_TRANSLATION_BRANCH?.trim()\n if (envBranch) return envBranch\n\n const providerBranch = process.env.VERCEL_GIT_COMMIT_REF?.trim()\n if (providerBranch) return providerBranch\n\n const gitBranch = readCurrentGitBranch(root)\n return gitBranch ?? DEFAULT_REMOTE_BRANCH\n}\n\nfunction readCurrentGitBranch(root: string) {\n try {\n const branch = execFileSync(\"git\", [\"branch\", \"--show-current\"], {\n cwd: root,\n encoding: \"utf-8\",\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n }).trim()\n return branch || null\n } catch {\n return null\n }\n}\n\nfunction formatRemoteManifestTarget(runtime: Extract<BetterTranslateRuntimeOptions, { type: \"remote\" }>, endpoint: string) {\n return `${endpoint.replace(/\\/$/, \"\")}/api/projects/${encodeURIComponent(runtime.projectId)}/branches/${encodeURIComponent(runtime.branch ?? DEFAULT_REMOTE_BRANCH)}/manifest`\n}\n\nfunction parseRemoteSyncResult(body: string) {\n try {\n const parsed = JSON.parse(body) as unknown\n if (typeof parsed !== \"object\" || parsed === null) return null\n const messageCount = \"messageCount\" in parsed && typeof parsed.messageCount === \"number\" ? parsed.messageCount : undefined\n return { messageCount }\n } catch {\n return null\n }\n}\n\nfunction formatRemoteSyncError(response: Response, target: string, body: string) {\n const details = formatRemoteSyncResponseDetails(body)\n return [\n `${PREFIX} remote Manifest sync failed with ${response.status} ${response.statusText || \"HTTP error\"}`,\n `target: ${target}`,\n details ? `response: ${details}` : null,\n ]\n .filter((line) => line !== null)\n .join(\"\\n\")\n}\n\nfunction formatRemoteSyncNetworkError(target: string, error: unknown) {\n const message = error instanceof Error ? error.message : String(error)\n return [`${PREFIX} remote Manifest sync could not reach the hosted service`, `target: ${target}`, `error: ${message}`].join(\n \"\\n\",\n )\n}\n\nfunction formatRemoteSyncResponseDetails(body: string) {\n const details = body.trim().replace(/\\s+/g, \" \")\n if (!details) return \"\"\n return details.length > 500 ? `${details.slice(0, 500)}...` : details\n}\n\nfunction createModuleMessagesModule(locales: string[], getImportPath: (locale: string) => string) {\n const cases = locales\n .map(\n (locale) => ` case ${JSON.stringify(locale)}:\n return (await import(${JSON.stringify(getImportPath(locale))})).default`,\n )\n .join(\"\\n\")\n\n return [\n `export const locales = ${JSON.stringify(locales)}`,\n \"\",\n \"export async function loadMessages(locale) {\",\n \" switch (locale) {\",\n cases,\n \" default:\",\n \" throw new Error(`Unknown locale: ${locale}`)\",\n \" }\",\n \"}\",\n \"\",\n ].join(\"\\n\")\n}\n\nfunction createPublicMessagesModule(locales: string[], basePath: string) {\n const normalizedBasePath = basePath.replace(/\\/$/, \"\")\n return [\n `export const locales = ${JSON.stringify(locales)}`,\n \"\",\n \"export async function loadMessages(locale) {\",\n \" assertKnownLocale(locale)\",\n ` const response = await fetch(\\`${normalizedBasePath}/locales/\\${encodeURIComponent(locale)}.json\\`)`,\n \" if (!response.ok) throw new Error(`Failed to load locale: ${locale}`)\",\n \" return response.json()\",\n \"}\",\n \"\",\n createKnownLocaleAssertion(locales),\n ].join(\"\\n\")\n}\n\nfunction createRemoteMessagesModule(\n runtime: Extract<BetterTranslateRuntimeOptions, { type: \"remote\" }>,\n locales: string[],\n endpoint: string,\n) {\n const normalizedEndpoint = endpoint.replace(/\\/$/, \"\")\n const projectPath = `/projects/${encodeURIComponent(runtime.projectId)}`\n const branchPath = `/branches/${encodeURIComponent(runtime.branch ?? DEFAULT_REMOTE_BRANCH)}`\n return [\n `export const locales = ${JSON.stringify(locales)}`,\n \"\",\n \"export async function loadMessages(locale) {\",\n \" assertKnownLocale(locale)\",\n ` const response = await fetch(\\`${normalizedEndpoint}${projectPath}${branchPath}/locales/\\${encodeURIComponent(locale)}.json\\`)`,\n \" if (!response.ok) throw new Error(`Failed to load locale: ${locale}`)\",\n \" return response.json()\",\n \"}\",\n \"\",\n createKnownLocaleAssertion(locales),\n ].join(\"\\n\")\n}\n\nfunction createKnownLocaleAssertion(locales: string[]) {\n return [\n `const knownLocales = new Set(${JSON.stringify(locales)})`,\n \"\",\n \"function assertKnownLocale(locale) {\",\n \" if (!knownLocales.has(locale)) throw new Error(`Unknown locale: ${locale}`)\",\n \"}\",\n \"\",\n ].join(\"\\n\")\n}\n\nfunction normalizeLocaleMessages(input: unknown): RuntimeMessages {\n if (isRuntimeMessages(input)) return input\n if (\n typeof input === \"object\" &&\n input !== null &&\n \"messages\" in input &&\n typeof input.messages === \"object\" &&\n input.messages !== null\n ) {\n return Object.fromEntries(\n Object.entries(input.messages).flatMap(([id, entry]) =>\n typeof entry === \"object\" && entry !== null && \"translation\" in entry && typeof entry.translation === \"string\"\n ? [[id, entry.translation]]\n : [],\n ),\n ) as RuntimeMessages\n }\n return {}\n}\n\nfunction writeFileIfChanged(path: string, contents: string) {\n if (existsSync(path) && readFileSync(path, \"utf-8\") === contents) return false\n writeFileSync(path, contents)\n return true\n}\n\nfunction collectScanFiles(root: string) {\n const files: string[] = []\n for (const entry of readdirSync(root, { withFileTypes: true })) {\n const path = resolve(root, entry.name)\n if (entry.isDirectory()) {\n if (entry.name === \"node_modules\") continue\n files.push(...collectScanFiles(path))\n continue\n }\n files.push(path)\n }\n return files\n}\n\nfunction isRuntimeMessages(input: unknown): input is RuntimeMessages {\n return typeof input === \"object\" && input !== null && Object.values(input).every((value) => typeof value === \"string\")\n}\n\nfunction applyEdits(code: string, edits: Array<{ start: number; end: number; replacement: string }>) {\n let transformed = code\n for (const edit of [...edits].sort((a, b) => b.start - a.start)) {\n transformed = `${transformed.slice(0, edit.start)}${edit.replacement}${transformed.slice(edit.end)}`\n }\n return transformed\n}\n\nfunction groupMessagesById(messages: ExtractedMessage[]): MessageManifest {\n const grouped: MessageManifest = {}\n\n for (const message of messages) {\n const existing = grouped[message.id]\n if (existing && !hasSameMessageShape(existing, message)) {\n throw new Error(formatCollisionError(message.id, existing, message))\n }\n if (!existing) {\n grouped[message.id] = {\n defaultMessage: message.defaultMessage,\n meta: message.meta,\n placeholders: message.placeholders,\n sources: [message.source],\n }\n continue\n }\n if (!existing.sources.some((source) => isSameSource(source, message.source))) {\n existing.sources.push(message.source)\n }\n }\n\n return grouped\n}\n\nfunction hasSameMessageShape(\n existing: Pick<ManifestEntry, \"defaultMessage\" | \"meta\" | \"placeholders\">,\n incoming: Pick<ManifestEntry, \"defaultMessage\" | \"meta\" | \"placeholders\"> | ExtractedMessage,\n) {\n return (\n existing.defaultMessage === incoming.defaultMessage &&\n serializeMeta(existing.meta) === serializeMeta(incoming.meta) &&\n JSON.stringify(existing.placeholders) === JSON.stringify(incoming.placeholders)\n )\n}\n\nfunction isSameSource(left: MessageSource, right: MessageSource) {\n return (\n left.file === right.file &&\n left.kind === right.kind &&\n left.marker === right.marker &&\n left.start === right.start &&\n left.end === right.end\n )\n}\n\nfunction isSameExtractedMessage(left: ExtractedMessage, right?: ExtractedMessage) {\n if (!right) return false\n return hasSameMessageShape(left, right) && isSameSource(left.source, right.source)\n}\n\nfunction formatCollisionError(\n id: string,\n existing: Pick<ManifestEntry, \"defaultMessage\" | \"meta\" | \"placeholders\" | \"sources\">,\n incoming: Pick<ManifestEntry, \"defaultMessage\" | \"meta\" | \"placeholders\" | \"sources\"> | ExtractedMessage,\n) {\n const existingSources = formatSources(existing.sources)\n const incomingSources = formatSources(\"source\" in incoming ? [incoming.source] : incoming.sources)\n return [\n `${PREFIX} conflicting message definition for ${BOLD}\"${id}\"${RESET}`,\n `existing: ${JSON.stringify({ defaultMessage: existing.defaultMessage, meta: existing.meta, placeholders: existing.placeholders })}`,\n `existing sources: ${existingSources}`,\n `incoming: ${JSON.stringify({ defaultMessage: incoming.defaultMessage, meta: incoming.meta, placeholders: incoming.placeholders })}`,\n `incoming sources: ${incomingSources}`,\n ].join(\"\\n\")\n}\n\nfunction formatSources(sources: MessageSource[]) {\n return sources.map((source) => `${source.file}:${source.line}:${source.column}`).join(\", \")\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "better-translation",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "description": "Vite plugin and runtime helpers for Better Translation.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -1 +0,0 @@
1
- {"version":3,"file":"message-id-B8zJjADZ.mjs","names":[],"sources":["../src/message-id.ts"],"sourcesContent":["import type { TranslateOptions } from \"./types.js\"\n\nexport function normalizeMeta(meta?: TranslateOptions): TranslateOptions {\n if (!meta) return {}\n\n return Object.fromEntries(\n Object.entries(meta)\n .filter(([key, value]) => key !== \"id\" && value !== undefined)\n .sort(([a], [b]) => a.localeCompare(b)),\n ) as TranslateOptions\n}\n\nexport function serializeMeta(meta?: TranslateOptions) {\n if (!meta) return \"\"\n\n const normalized = normalizeMeta(meta)\n return Object.keys(normalized).length > 0 ? JSON.stringify(normalized) : \"\"\n}\n\nexport function getMessageIdentity(message: string, meta?: TranslateOptions) {\n const serializedMeta = serializeMeta(meta)\n return serializedMeta ? `${message}\\0${serializedMeta}` : message\n}\n\n/** Generates the stable hashed id used to store and look up a translated message. */\nexport function getMessageId(message: string, meta?: TranslateOptions) {\n const value = getMessageIdentity(message, meta)\n let hash = 2166136261\n\n for (let i = 0; i < value.length; i++) {\n hash ^= value.charCodeAt(i)\n hash = Math.imul(hash, 16777619)\n }\n\n return `m_${(hash >>> 0).toString(36)}`\n}\n\n/** Resolves the lookup id for function-style `t()` calls, preferring an explicit `options.id`. */\nexport function getCallMessageId(message: string, options?: TranslateOptions) {\n return options?.id ?? getMessageId(message, options)\n}\n"],"mappings":";AAEA,SAAgB,cAAc,MAA2C;CACvE,IAAI,CAAC,MAAM,OAAO,CAAC;CAEnB,OAAO,OAAO,YACZ,OAAO,QAAQ,IAAI,EAChB,QAAQ,CAAC,KAAK,WAAW,QAAQ,QAAQ,UAAU,KAAA,CAAS,EAC5D,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC,CAC1C;AACF;AAEA,SAAgB,cAAc,MAAyB;CACrD,IAAI,CAAC,MAAM,OAAO;CAElB,MAAM,aAAa,cAAc,IAAI;CACrC,OAAO,OAAO,KAAK,UAAU,EAAE,SAAS,IAAI,KAAK,UAAU,UAAU,IAAI;AAC3E;AAEA,SAAgB,mBAAmB,SAAiB,MAAyB;CAC3E,MAAM,iBAAiB,cAAc,IAAI;CACzC,OAAO,iBAAiB,GAAG,QAAQ,IAAI,mBAAmB;AAC5D;;AAGA,SAAgB,aAAa,SAAiB,MAAyB;CACrE,MAAM,QAAQ,mBAAmB,SAAS,IAAI;CAC9C,IAAI,OAAO;CAEX,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;EACrC,QAAQ,MAAM,WAAW,CAAC;EAC1B,OAAO,KAAK,KAAK,MAAM,QAAQ;CACjC;CAEA,OAAO,MAAM,SAAS,GAAG,SAAS,EAAE;AACtC;;AAGA,SAAgB,iBAAiB,SAAiB,SAA4B;CAC5E,OAAO,SAAS,MAAM,aAAa,SAAS,OAAO;AACrD"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"types-DxcBf0RB.d.mts","names":[],"sources":["../src/types.ts"],"mappings":";;UAeiB,aAAA;EAkBZ;EAhBH,IAAA;EAoB4B;EAlB5B,IAAA;EA0BsB;EAxBtB,MAAA;EAoBA;EAlBA,IAAA;EAoBA;EAlBA,MAAA;EAoBS;EAlBT,OAAA;EAkBsB;EAhBtB,SAAA;EAoByB;EAlBzB,KAAA;EAkB4B;EAhB5B,GAAA;AAAA;;UAIe,aAAA;EAegC;EAb/C,cAAA;EAgBU;EAdV,IAAA,EAAM,gBAAA;;EAEN,YAAA;EAYkC;EAVlC,OAAA,EAAS,aAAa;AAAA;;KAIZ,eAAA,GAAkB,MAAM,SAAS,aAAA;AA2DpC;AAAA,KAxDG,mBAAA,GAAsB,eAAe;;KAGrC,eAAA,GAAkB,MAAM;AA6FpC;AAAA,UA5CiB,gBAAA;;EAEf,EAAA;EA4CA;EA1CA,OAAO;AAAA;;UAIQ,gBAAA;EA4CP;EA1CR,EAAA;EA8CkD;EA5ClD,IAAA;EA4CkD;EA1ClD,IAAA,EAAM,gBAAA;EA8CN;EA5CA,YAAA;EA8CS;EA5CT,OAAA,EAAS,aAAa;AAAA;;KAIZ,WAAA,IAAe,QAAA,EAAU,gBAAA,IAAoB,MAAA,aAAmB,OAAA,CAAQ,MAAA;;UAGnE,mCAAA;EA4CA;EA1Cf,IAAA;;EAEA,GAAG;AAAA;;UAIY,kCAAA;EA0Cf;EAxCA,IAAA;EAwCO;EAtCP,MAAM;AAAA;;KAII,6BAAA,GAAgC,mCAAA,GAAsC,kCAAkC;;UAGnG,kCAAA;EAmDQ;EAjDvB,IAAA;EAmCA;EAjCA,MAAA;EAqCA;EAnCA,MAAA;EAuCA;EArCA,QAAA;AAAA;;UAIe,mCAAA;EAuCf;EArCA,IAAA;EAqCuB;EAnCvB,QAAA;;EAEA,SAAA;AAAA;;KAIU,6BAAA,GAAgC,kCAAA,GAAqC,mCAAmC;;UAGnG,4BAAA;;EAEf,OAAA,EAAS,6BAA6B;;EAEtC,aAAA;;EAEA,OAAA;AAAA;;UAIe,4BAAA;;EAEf,OAAA;;EAEA,aAAA;;EAEA,OAAA;;EAEA,SAAA;;EAEA,OAAA;;EAEA,OAAA,GAAU,6BAAA;;EAEV,OAAA,GAAU,6BAAA;;EAEV,SAAA,GAAY,WAAA;AAAA"}