@intlayer/lingui 9.0.0-canary.1 → 9.0.0-canary.11

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.
@@ -12,15 +12,41 @@ let vite_intlayer = require("vite-intlayer");
12
12
  /**
13
13
  * Caller configurations for lingui.
14
14
  *
15
- * Lingui's `i18n._(id)` / `i18n.t` and `useLingui()`'s `_()` all resolve
16
- * translations at runtime through the intlayer `messages` dictionary. Since the
17
- * mapping is 1:1 (one catalog one `messages` dict) there is no namespace
18
- * splitting needed at the analyser level, so the caller list is empty for now.
15
+ * After `@lingui/babel-plugin-lingui-macro` / `@lingui/swc-plugin` compiles
16
+ * macros (`` t`Hello ${name}` `` `i18n._(id, values)`) the intlayer field
17
+ * usage analyser sees these method calls and records which top-level fields of
18
+ * the `messages` dictionary are consumed, enabling accurate pruning.
19
19
  *
20
- * Future: once `namespace: { from: 'self' }` is supported in the babel/swc
21
- * analyser, we can add callers for `useLingui()._(id)`.
20
+ * Two call forms are tracked:
21
+ * - `i18n._('home.title', values)` string id
22
+ * - `i18n._({ id: 'home.title', message: '...' }, values)` — descriptor
23
+ * - `i18n.t('home.title', values)` — alias for `_`
24
+ *
25
+ * Because lingui projects may also use hashed IDs (generated from the default
26
+ * message string) that cannot be statically mapped to dictionary keys, a
27
+ * separate `'all'`-mode caller is not needed here: when the first argument is
28
+ * not a static string or descriptor, the `'self'` handler falls back to `'all'`
29
+ * automatically.
22
30
  */
23
- const LINGUI_COMPAT_CALLERS = [];
31
+ const LINGUI_COMPAT_CALLERS = [{
32
+ callerName: "_",
33
+ importSources: ["@lingui/core", "@intlayer/lingui"],
34
+ matchAsMethod: true,
35
+ namespace: {
36
+ from: "fixed",
37
+ value: "messages"
38
+ },
39
+ translationFunction: "self"
40
+ }, {
41
+ callerName: "t",
42
+ importSources: ["@lingui/core", "@intlayer/lingui"],
43
+ matchAsMethod: true,
44
+ namespace: {
45
+ from: "fixed",
46
+ value: "messages"
47
+ },
48
+ translationFunction: "self"
49
+ }];
24
50
  /**
25
51
  * A Vite plugin for the lingui compat adapter.
26
52
  *
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["ANSIColors"],"sources":["../../../src/plugin/index.ts"],"sourcesContent":["import { join } from 'node:path';\nimport { runOnce } from '@intlayer/chokidar/utils';\nimport * as ANSIColors from '@intlayer/config/colors';\nimport { colorize, getAppLogger } from '@intlayer/config/logger';\nimport { getConfiguration } from '@intlayer/config/node';\nimport type { PluginOption } from 'vite';\nimport { type CompatCallerConfig, intlayer } from 'vite-intlayer';\n\n/**\n * Caller configurations for lingui.\n *\n * Lingui's `i18n._(id)` / `i18n.t` and `useLingui()`'s `_()` all resolve\n * translations at runtime through the intlayer `messages` dictionary. Since the\n * mapping is 1:1 (one catalog one `messages` dict) there is no namespace\n * splitting needed at the analyser level, so the caller list is empty for now.\n *\n * Future: once `namespace: { from: 'self' }` is supported in the babel/swc\n * analyser, we can add callers for `useLingui()._(id)`.\n */\nconst LINGUI_COMPAT_CALLERS: CompatCallerConfig[] = [];\n\n/**\n * A Vite plugin for the lingui compat adapter.\n *\n * Wraps `vite-intlayer` and adds resolve aliases so that both\n * `@lingui/core` **and** `@lingui/react` are redirected to\n * `@intlayer/lingui` at bundle time.\n *\n * @example\n * ```ts\n * // vite.config.ts\n * import { linguiVitePlugin } from '@intlayer/lingui/plugin';\n *\n * export default defineConfig({\n * plugins: [linguiVitePlugin()],\n * });\n * ```\n */\nexport const linguiVitePlugin = (\n options?: Parameters<typeof intlayer>[0]\n): PluginOption[] => {\n const intlayerConfig = getConfiguration();\n const appLogger = getAppLogger(intlayerConfig);\n\n runOnce(\n join(\n intlayerConfig.system.baseDir,\n '.intlayer',\n 'cache',\n 'intlayer-issues-invitation.lock'\n ),\n () => {\n appLogger([\n colorize(\n 'Please report any issues you met on GitHub:',\n ANSIColors.GREY\n ),\n colorize(\n 'https://github.com/aymericzip/intlayer/issues',\n ANSIColors.GREY_LIGHT\n ),\n ]);\n },\n {\n cacheTimeoutMs: 1000 * 60 * 60, // 1 hour\n }\n );\n\n const basePlugins = intlayer({\n ...options,\n compatCallers: [\n ...(options?.compatCallers ?? []),\n ...LINGUI_COMPAT_CALLERS,\n ],\n });\n\n const compatPlugin: PluginOption = {\n name: 'vite-lingui-compat-plugin',\n config: () => ({\n resolve: {\n alias: {\n '@lingui/core': '@intlayer/lingui',\n '@lingui/react': '@intlayer/lingui',\n },\n },\n }),\n };\n\n return [\n ...(Array.isArray(basePlugins) ? basePlugins : [basePlugins]),\n compatPlugin,\n ];\n};\n\nexport default linguiVitePlugin;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAmBA,MAAM,wBAA8C,CAAC;;;;;;;;;;;;;;;;;;AAmBrD,MAAa,oBACX,YACmB;CACnB,MAAM,6DAAkC;CACxC,MAAM,sDAAyB,cAAc;CAE7C,0DAEI,eAAe,OAAO,SACtB,aACA,SACA,iCACF,SACM;EACJ,UAAU,uCAEN,+CACAA,wBAAW,IACb,yCAEE,iDACAA,wBAAW,UACb,CACF,CAAC;CACH,GACA,EACE,gBAAgB,MAAO,KAAK,GAC9B,CACF;CAEA,MAAM,0CAAuB;EAC3B,GAAG;EACH,eAAe,CACb,GAAI,SAAS,iBAAiB,CAAC,GAC/B,GAAG,qBACL;CACF,CAAC;CAED,MAAM,eAA6B;EACjC,MAAM;EACN,eAAe,EACb,SAAS,EACP,OAAO;GACL,gBAAgB;GAChB,iBAAiB;EACnB,EACF,EACF;CACF;CAEA,OAAO,CACL,GAAI,MAAM,QAAQ,WAAW,IAAI,cAAc,CAAC,WAAW,GAC3D,YACF;AACF"}
1
+ {"version":3,"file":"index.cjs","names":["ANSIColors"],"sources":["../../../src/plugin/index.ts"],"sourcesContent":["import { join } from 'node:path';\nimport { runOnce } from '@intlayer/chokidar/utils';\nimport * as ANSIColors from '@intlayer/config/colors';\nimport { colorize, getAppLogger } from '@intlayer/config/logger';\nimport { getConfiguration } from '@intlayer/config/node';\nimport type { PluginOption } from 'vite';\nimport { type CompatCallerConfig, intlayer } from 'vite-intlayer';\n\n/**\n * Caller configurations for lingui.\n *\n * After `@lingui/babel-plugin-lingui-macro` / `@lingui/swc-plugin` compiles\n * macros (`` t`Hello ${name}` `` → `i18n._(id, values)`) the intlayer field\n * usage analyser sees these method calls and records which top-level fields of\n * the `messages` dictionary are consumed, enabling accurate pruning.\n *\n * Two call forms are tracked:\n * - `i18n._('home.title', values)` string id\n * - `i18n._({ id: 'home.title', message: '...' }, values)` descriptor\n * - `i18n.t('home.title', values)` — alias for `_`\n *\n * Because lingui projects may also use hashed IDs (generated from the default\n * message string) that cannot be statically mapped to dictionary keys, a\n * separate `'all'`-mode caller is not needed here: when the first argument is\n * not a static string or descriptor, the `'self'` handler falls back to `'all'`\n * automatically.\n */\nconst LINGUI_COMPAT_CALLERS: CompatCallerConfig[] = [\n {\n callerName: '_',\n importSources: ['@lingui/core', '@intlayer/lingui'],\n matchAsMethod: true,\n namespace: { from: 'fixed', value: 'messages' },\n translationFunction: 'self',\n },\n {\n callerName: 't',\n importSources: ['@lingui/core', '@intlayer/lingui'],\n matchAsMethod: true,\n namespace: { from: 'fixed', value: 'messages' },\n translationFunction: 'self',\n },\n];\n\n/**\n * A Vite plugin for the lingui compat adapter.\n *\n * Wraps `vite-intlayer` and adds resolve aliases so that both\n * `@lingui/core` **and** `@lingui/react` are redirected to\n * `@intlayer/lingui` at bundle time.\n *\n * @example\n * ```ts\n * // vite.config.ts\n * import { linguiVitePlugin } from '@intlayer/lingui/plugin';\n *\n * export default defineConfig({\n * plugins: [linguiVitePlugin()],\n * });\n * ```\n */\nexport const linguiVitePlugin = (\n options?: Parameters<typeof intlayer>[0]\n): PluginOption[] => {\n const intlayerConfig = getConfiguration();\n const appLogger = getAppLogger(intlayerConfig);\n\n runOnce(\n join(\n intlayerConfig.system.baseDir,\n '.intlayer',\n 'cache',\n 'intlayer-issues-invitation.lock'\n ),\n () => {\n appLogger([\n colorize(\n 'Please report any issues you met on GitHub:',\n ANSIColors.GREY\n ),\n colorize(\n 'https://github.com/aymericzip/intlayer/issues',\n ANSIColors.GREY_LIGHT\n ),\n ]);\n },\n {\n cacheTimeoutMs: 1000 * 60 * 60, // 1 hour\n }\n );\n\n const basePlugins = intlayer({\n ...options,\n compatCallers: [\n ...(options?.compatCallers ?? []),\n ...LINGUI_COMPAT_CALLERS,\n ],\n });\n\n const compatPlugin: PluginOption = {\n name: 'vite-lingui-compat-plugin',\n config: () => ({\n resolve: {\n alias: {\n '@lingui/core': '@intlayer/lingui',\n '@lingui/react': '@intlayer/lingui',\n },\n },\n }),\n };\n\n return [\n ...(Array.isArray(basePlugins) ? basePlugins : [basePlugins]),\n compatPlugin,\n ];\n};\n\nexport default linguiVitePlugin;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2BA,MAAM,wBAA8C,CAClD;CACE,YAAY;CACZ,eAAe,CAAC,gBAAgB,kBAAkB;CAClD,eAAe;CACf,WAAW;EAAE,MAAM;EAAS,OAAO;CAAW;CAC9C,qBAAqB;AACvB,GACA;CACE,YAAY;CACZ,eAAe,CAAC,gBAAgB,kBAAkB;CAClD,eAAe;CACf,WAAW;EAAE,MAAM;EAAS,OAAO;CAAW;CAC9C,qBAAqB;AACvB,CACF;;;;;;;;;;;;;;;;;;AAmBA,MAAa,oBACX,YACmB;CACnB,MAAM,6DAAkC;CACxC,MAAM,sDAAyB,cAAc;CAE7C,0DAEI,eAAe,OAAO,SACtB,aACA,SACA,iCACF,SACM;EACJ,UAAU,uCAEN,+CACAA,wBAAW,IACb,yCAEE,iDACAA,wBAAW,UACb,CACF,CAAC;CACH,GACA,EACE,gBAAgB,MAAO,KAAK,GAC9B,CACF;CAEA,MAAM,0CAAuB;EAC3B,GAAG;EACH,eAAe,CACb,GAAI,SAAS,iBAAiB,CAAC,GAC/B,GAAG,qBACL;CACF,CAAC;CAED,MAAM,eAA6B;EACjC,MAAM;EACN,eAAe,EACb,SAAS,EACP,OAAO;GACL,gBAAgB;GAChB,iBAAiB;EACnB,EACF,EACF;CACF;CAEA,OAAO,CACL,GAAI,MAAM,QAAQ,WAAW,IAAI,cAAc,CAAC,WAAW,GAC3D,YACF;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"useLingui.cjs","names":["LinguiContext","I18nClass"],"sources":["../../src/useLingui.ts"],"sourcesContent":["'use client';\n\nimport type { LocalesValues } from '@intlayer/types/module_augmentation';\nimport type { I18nContext } from '@lingui/react';\nimport { useContext, useMemo } from 'react';\nimport { useLocale } from 'react-intlayer';\nimport { I18nClass } from './I18nClass';\nimport { LinguiContext } from './LinguiContext';\n\n/**\n * Drop-in for `@lingui/react`'s `useLingui`.\n *\n * Returns `{ i18n, _, defaultComponent }` from the nearest `I18nProvider`.\n * When used outside a provider, derives a locale-aware `i18n` instance from\n * `react-intlayer`'s `useLocale()` so that Server Components and test contexts\n * work without an explicit provider.\n *\n * @example\n * ```tsx\n * const { _ } = useLingui();\n * return <h1>{_({ id: 'home.title', message: 'Welcome' })}</h1>;\n * ```\n */\nexport const useLingui = (): I18nContext => {\n const context = useContext(LinguiContext);\n const { locale } = useLocale();\n\n const derivedI18n = useMemo(() => {\n const instance = new I18nClass({ locale: locale as string });\n return {\n i18n: instance as unknown as I18nContext['i18n'],\n _: instance._.bind(instance) as I18nContext['_'],\n };\n }, [locale]);\n\n if (context) {\n return context;\n }\n\n return derivedI18n as I18nContext;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAuBA,MAAa,kBAA+B;CAC1C,MAAM,gCAAqBA,mCAAa;CACxC,MAAM,EAAE,yCAAqB;CAE7B,MAAM,uCAA4B;EAChC,MAAM,WAAW,IAAIC,4BAAU,EAAU,OAAiB,CAAC;EAC3D,OAAO;GACL,MAAM;GACN,GAAG,SAAS,EAAE,KAAK,QAAQ;EAC7B;CACF,GAAG,CAAC,MAAM,CAAC;CAEX,IAAI,SACF,OAAO;CAGT,OAAO;AACT"}
1
+ {"version":3,"file":"useLingui.cjs","names":["LinguiContext","I18nClass"],"sources":["../../src/useLingui.ts"],"sourcesContent":["'use client';\n\nimport type { I18nContext } from '@lingui/react';\nimport { useContext, useMemo } from 'react';\nimport { useLocale } from 'react-intlayer';\nimport { I18nClass } from './I18nClass';\nimport { LinguiContext } from './LinguiContext';\n\n/**\n * Drop-in for `@lingui/react`'s `useLingui`.\n *\n * Returns `{ i18n, _, defaultComponent }` from the nearest `I18nProvider`.\n * When used outside a provider, derives a locale-aware `i18n` instance from\n * `react-intlayer`'s `useLocale()` so that Server Components and test contexts\n * work without an explicit provider.\n *\n * @example\n * ```tsx\n * const { _ } = useLingui();\n * return <h1>{_({ id: 'home.title', message: 'Welcome' })}</h1>;\n * ```\n */\nexport const useLingui = (): I18nContext => {\n const context = useContext(LinguiContext);\n const { locale } = useLocale();\n\n const derivedI18n = useMemo(() => {\n const instance = new I18nClass({ locale: locale as string });\n return {\n i18n: instance as unknown as I18nContext['i18n'],\n _: instance._.bind(instance) as I18nContext['_'],\n };\n }, [locale]);\n\n if (context) {\n return context;\n }\n\n return derivedI18n as I18nContext;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAsBA,MAAa,kBAA+B;CAC1C,MAAM,gCAAqBA,mCAAa;CACxC,MAAM,EAAE,yCAAqB;CAE7B,MAAM,uCAA4B;EAChC,MAAM,WAAW,IAAIC,4BAAU,EAAU,OAAiB,CAAC;EAC3D,OAAO;GACL,MAAM;GACN,GAAG,SAAS,EAAE,KAAK,QAAQ;EAC7B;CACF,GAAG,CAAC,MAAM,CAAC;CAEX,IAAI,SACF,OAAO;CAGT,OAAO;AACT"}
@@ -1,36 +1,44 @@
1
1
  import { EventEmitter } from "./eventEmitter.mjs";
2
+ import { linguiMessageToIcu, navigateLinguiCatalog, unwrapLinguiCatalog } from "./linguiCatalog.mjs";
2
3
  import { getIntlayer } from "@intlayer/core/interpreter";
3
4
  import { resolveMessage } from "@intlayer/core/messageFormat";
5
+ import { getDictionaries } from "@intlayer/dictionaries-entry";
4
6
 
5
7
  //#region src/I18nClass.ts
6
8
  /**
7
- * Navigates a nested object by a dot-separated path.
9
+ * Keys of every intlayer dictionary available at runtime (the namespaces).
8
10
  *
9
- * Example: `navigatePath({home: {title: 'Hello'}}, 'home.title')` → `'Hello'`
11
+ * The bundler-injected `@intlayer/dictionaries-entry` exposes the full registry;
12
+ * outside a bundle (e.g. unit tests) it resolves to an empty map, in which case
13
+ * resolution falls back to the runtime catalogs loaded via `load()`.
10
14
  */
11
- const navigatePath = (object, path) => {
12
- if (!path) return object;
13
- const parts = path.split(".");
14
- let current = object;
15
- for (const part of parts) {
16
- if (current === null || current === void 0 || typeof current !== "object") return;
17
- current = current[part];
18
- }
19
- return current;
15
+ const getDictionaryKeys = () => {
16
+ try {
17
+ return Object.keys(getDictionaries());
18
+ } catch {
19
+ return [];
20
+ }
20
21
  };
21
22
  /**
22
- * Looks up a lingui message from the `messages` intlayer dictionary.
23
+ * Looks up a lingui message across every intlayer dictionary.
23
24
  *
24
- * The entire lingui catalog is stored in a single `messages` dictionary.
25
- * Dotted IDs (e.g. `'home.title'`) are resolved as nested paths within that
26
- * dictionary. Hash-style IDs (no dots) are direct top-level keys.
25
+ * lingui ids are flat, namespace-less keys (`'hero.title'`), but the matching
26
+ * content may live in any dictionary a single centralized catalog, or one of
27
+ * many namespaced catalogs (`home`, `shared`, …) produced by `syncJSON` from
28
+ * split lingui catalogs. Each dictionary is searched in turn, supporting both
29
+ * the flat/nested key shapes and the lingui `{ messages: {…} }` wrapper. The
30
+ * first match wins.
27
31
  */
28
- const lookupMessage = (id, locale) => {
29
- try {
30
- const value = navigatePath(getIntlayer("messages", locale), id);
31
- return typeof value === "string" ? value : void 0;
32
- } catch {
33
- return;
32
+ const lookupDictionaryMessage = (id, locale) => {
33
+ for (const key of getDictionaryKeys()) {
34
+ let dictionary;
35
+ try {
36
+ dictionary = getIntlayer(key, locale);
37
+ } catch {
38
+ continue;
39
+ }
40
+ const value = navigateLinguiCatalog(dictionary, id);
41
+ if (value !== void 0) return linguiMessageToIcu(value);
34
42
  }
35
43
  };
36
44
  /**
@@ -45,10 +53,21 @@ const lookupMessage = (id, locale) => {
45
53
  var I18nClass = class extends EventEmitter {
46
54
  _locale;
47
55
  _locales;
48
- constructor({ locale = "en", locales } = {}) {
56
+ /**
57
+ * Per-locale catalogs supplied at runtime via the constructor, `load()` or
58
+ * `loadAndActivate()`. Used as a fallback when a key is absent from the
59
+ * compiled intlayer `messages` dictionary, so an unmodified lingui codebase
60
+ * (which loads its compiled `.mjs`/`.json` catalogs) keeps working as a
61
+ * drop-in. For optimal bundle size, compile catalogs into intlayer
62
+ * dictionaries instead — see the dev warning emitted by `load()`.
63
+ */
64
+ _catalogs = {};
65
+ _loadFallbackWarned = false;
66
+ constructor({ locale = "en", locales, messages } = {}) {
49
67
  super();
50
68
  this._locale = typeof locale === "string" ? locale : "en";
51
69
  this._locales = locales;
70
+ if (messages) this.mergeAllCatalogs(messages);
52
71
  }
53
72
  get locale() {
54
73
  return this._locale;
@@ -57,11 +76,28 @@ var I18nClass = class extends EventEmitter {
57
76
  return this._locales;
58
77
  }
59
78
  get messages() {
60
- try {
61
- return getIntlayer("messages", this._locale);
62
- } catch {
63
- return {};
64
- }
79
+ const dictionary = {};
80
+ for (const key of getDictionaryKeys()) try {
81
+ Object.assign(dictionary, unwrapLinguiCatalog(getIntlayer(key, this._locale)));
82
+ } catch {}
83
+ return {
84
+ ...this._catalogs[this._locale] ?? {},
85
+ ...dictionary
86
+ };
87
+ }
88
+ /** Merges a single-locale catalog into the in-memory fallback store. */
89
+ mergeLocaleCatalog(locale, catalog) {
90
+ this._catalogs[locale] = {
91
+ ...this._catalogs[locale],
92
+ ...catalog
93
+ };
94
+ }
95
+ /**
96
+ * Merges a `{ [locale]: catalog }` map (lingui's `AllMessages`) into the
97
+ * in-memory fallback store.
98
+ */
99
+ mergeAllCatalogs(messages) {
100
+ for (const [locale, catalog] of Object.entries(messages)) if (catalog && typeof catalog === "object") this.mergeLocaleCatalog(locale, catalog);
65
101
  }
66
102
  /**
67
103
  * No-op: intlayer handles message compilation at build time.
@@ -71,16 +107,27 @@ var I18nClass = class extends EventEmitter {
71
107
  return this;
72
108
  }
73
109
  /**
74
- * No-op: messages are loaded automatically via intlayer dictionaries.
110
+ * Loads message catalogs into the runtime fallback store, supporting both
111
+ * lingui signatures: `load(locale, messages)` and `load(allMessages)`.
112
+ *
113
+ * These messages are a compatibility fallback — keys present in the compiled
114
+ * intlayer `messages` dictionary take precedence, and only those keys can be
115
+ * pruned/optimized at build time.
75
116
  */
76
- load(_localeOrAll, _messages) {
77
- console.warn("@intlayer/lingui: i18n.load() is a no-op messages are loaded automatically via intlayer dictionaries.");
117
+ load(localeOrAll, messages) {
118
+ if (typeof localeOrAll === "string") this.mergeLocaleCatalog(localeOrAll, messages ?? {});
119
+ else this.mergeAllCatalogs(localeOrAll);
120
+ if (!this._loadFallbackWarned) {
121
+ this._loadFallbackWarned = true;
122
+ console.warn("@intlayer/lingui: i18n.load() messages are used as a runtime fallback. For optimal bundle size, compile your catalogs into intlayer dictionaries instead of importing lingui locale files.");
123
+ }
78
124
  }
79
125
  /**
80
- * Activates the given locale (emits `'change'`).
81
- * The `messages` argument is ignored — intlayer handles the catalog.
126
+ * Loads the given catalog (fallback store) and activates the locale
127
+ * (emits `'change'`).
82
128
  */
83
- loadAndActivate({ locale, locales }) {
129
+ loadAndActivate({ locale, locales, messages }) {
130
+ if (messages) this.mergeLocaleCatalog(locale, messages);
84
131
  this.activate(locale, locales);
85
132
  }
86
133
  /**
@@ -91,6 +138,22 @@ var I18nClass = class extends EventEmitter {
91
138
  this._locales = locales;
92
139
  this.emit("change");
93
140
  }
141
+ /**
142
+ * Resolves the raw message template for an id (before interpolation).
143
+ *
144
+ * Resolution order:
145
+ * 1. compiled intlayer `messages` dictionary
146
+ * 2. runtime fallback catalog (constructor / `load()` / `loadAndActivate()`)
147
+ */
148
+ resolveTemplate(id) {
149
+ const fromDictionary = lookupDictionaryMessage(id, this._locale);
150
+ if (fromDictionary !== void 0) return fromDictionary;
151
+ const catalog = this._catalogs[this._locale];
152
+ if (catalog) {
153
+ const raw = navigateLinguiCatalog(catalog, id);
154
+ if (raw !== void 0) return linguiMessageToIcu(raw);
155
+ }
156
+ }
94
157
  _(descriptorOrId, values, options) {
95
158
  const isDescriptor = typeof descriptorOrId === "object" && descriptorOrId !== null;
96
159
  const id = isDescriptor ? descriptorOrId.id : descriptorOrId;
@@ -99,7 +162,7 @@ var I18nClass = class extends EventEmitter {
99
162
  ...descriptorOrId.values ?? {},
100
163
  ...values ?? {}
101
164
  } : values ?? {};
102
- return resolveMessage(lookupMessage(id, this._locale) ?? defaultMessage ?? id, resolvedValues, this._locale, "icu") ?? id;
165
+ return resolveMessage(this.resolveTemplate(id) ?? defaultMessage ?? id, resolvedValues, this._locale, "icu") ?? id;
103
166
  }
104
167
  /**
105
168
  * Alias for `_`. Provided for lingui API compatibility.
@@ -1 +1 @@
1
- {"version":3,"file":"I18nClass.mjs","names":[],"sources":["../../src/I18nClass.ts"],"sourcesContent":["import { getIntlayer } from '@intlayer/core/interpreter';\nimport { resolveMessage } from '@intlayer/core/messageFormat';\nimport type {\n DictionaryKeys,\n LocalesValues,\n} from '@intlayer/types/module_augmentation';\nimport type {\n AllMessages,\n Locale,\n Locales,\n MessageDescriptor,\n MessageId,\n MessageOptions,\n Messages,\n} from '@lingui/core';\nimport { EventEmitter } from './eventEmitter';\n\n/** Mirrors the unexported `Values` type from `@lingui/core`. */\ntype Values = Record<string, unknown>;\n\n/** Mirrors the unexported `MessageCompiler` type from `@lingui/core`. */\ntype MessageCompiler = (message: string) => unknown;\n\n/** Mirrors the unexported `Events` type from `@lingui/core`. */\ntype LinguiEvents = {\n change: () => void;\n missing: (event: { locale: Locale; id: MessageId }) => void;\n};\n\n/** Mirrors the unexported `I18nProps` type from `@lingui/core`. */\ntype I18nProps = {\n locale?: Locale;\n locales?: Locales;\n messages?: AllMessages;\n missing?: string | ((locale: string, id: string) => string);\n};\n\n/**\n * Navigates a nested object by a dot-separated path.\n *\n * Example: `navigatePath({home: {title: 'Hello'}}, 'home.title')` → `'Hello'`\n */\nconst navigatePath = (object: unknown, path: string): unknown => {\n if (!path) return object;\n const parts = path.split('.');\n let current: unknown = object;\n for (const part of parts) {\n if (\n current === null ||\n current === undefined ||\n typeof current !== 'object'\n ) {\n return undefined;\n }\n current = (current as Record<string, unknown>)[part];\n }\n return current;\n};\n\n/**\n * Looks up a lingui message from the `messages` intlayer dictionary.\n *\n * The entire lingui catalog is stored in a single `messages` dictionary.\n * Dotted IDs (e.g. `'home.title'`) are resolved as nested paths within that\n * dictionary. Hash-style IDs (no dots) are direct top-level keys.\n */\nconst lookupMessage = (\n id: string,\n locale: LocalesValues\n): string | undefined => {\n try {\n const dictionary = getIntlayer('messages' as DictionaryKeys, locale);\n const value = navigatePath(dictionary, id);\n return typeof value === 'string' ? value : undefined;\n } catch {\n return undefined;\n }\n};\n\n/**\n * Intlayer-backed implementation of `@lingui/core`'s `I18n` class.\n *\n * - Messages are read from the `messages` intlayer dictionary.\n * - ICU MessageFormat syntax is resolved via `@intlayer/core/messageFormat`.\n * - `load()` / `loadAndActivate()` / `setMessagesCompiler()` are no-ops —\n * messages are served by intlayer's compiled dictionaries.\n * - `activate(locale)` emits `'change'` so that React consumers re-render.\n */\nexport class I18nClass extends EventEmitter<LinguiEvents> {\n private _locale: string;\n private _locales?: Locales;\n\n constructor({ locale = 'en', locales }: I18nProps = {}) {\n super();\n this._locale = typeof locale === 'string' ? locale : 'en';\n this._locales = locales;\n }\n\n get locale(): string {\n return this._locale;\n }\n\n get locales(): Locales | undefined {\n return this._locales;\n }\n\n get messages(): Messages {\n try {\n return getIntlayer(\n 'messages' as DictionaryKeys,\n this._locale as LocalesValues\n ) as Messages;\n } catch {\n return {};\n }\n }\n\n /**\n * No-op: intlayer handles message compilation at build time.\n */\n setMessagesCompiler(_compiler: MessageCompiler): this {\n if (process.env.NODE_ENV === 'development') {\n console.warn(\n '@intlayer/lingui: i18n.setMessagesCompiler() is a no-op — ' +\n 'message compilation is handled at build time by intlayer.'\n );\n }\n return this;\n }\n\n /**\n * No-op: messages are loaded automatically via intlayer dictionaries.\n */\n load(_localeOrAll: Locale | AllMessages, _messages?: Messages): void {\n if (process.env.NODE_ENV === 'development') {\n console.warn(\n '@intlayer/lingui: i18n.load() is a no-op — ' +\n 'messages are loaded automatically via intlayer dictionaries.'\n );\n }\n }\n\n /**\n * Activates the given locale (emits `'change'`).\n * The `messages` argument is ignored — intlayer handles the catalog.\n */\n loadAndActivate({\n locale,\n locales,\n }: {\n locale: Locale;\n locales?: Locales;\n messages: Messages;\n }): void {\n this.activate(locale, locales);\n }\n\n /**\n * Sets the active locale and emits `'change'` to notify React consumers.\n */\n activate(locale: Locale, locales?: Locales): void {\n this._locale = locale;\n this._locales = locales;\n this.emit('change');\n }\n\n /**\n * Translates a message descriptor or a plain ID.\n *\n * Resolution order:\n * 1. `messages` intlayer dictionary (dotted path navigation)\n * 2. `descriptor.message` or `options.message` (original source string)\n * 3. The raw `id` as fallback\n */\n _(descriptor: MessageDescriptor): string;\n _(id: MessageId, values?: Values, options?: MessageOptions): string;\n _(\n descriptorOrId: MessageDescriptor | MessageId,\n values?: Values,\n options?: MessageOptions\n ): string {\n const isDescriptor =\n typeof descriptorOrId === 'object' && descriptorOrId !== null;\n const id = isDescriptor\n ? (descriptorOrId as MessageDescriptor).id\n : (descriptorOrId as MessageId);\n const defaultMessage = isDescriptor\n ? ((descriptorOrId as MessageDescriptor).message ?? options?.message)\n : options?.message;\n const resolvedValues: Values = isDescriptor\n ? {\n ...((descriptorOrId as MessageDescriptor).values ?? {}),\n ...(values ?? {}),\n }\n : (values ?? {});\n\n const rawValue = lookupMessage(id, this._locale as LocalesValues);\n const template = rawValue ?? defaultMessage ?? id;\n\n return (\n resolveMessage(\n template,\n resolvedValues as Record<string, string | number>,\n this._locale as LocalesValues,\n 'icu'\n ) ?? id\n );\n }\n\n /**\n * Alias for `_`. Provided for lingui API compatibility.\n */\n t = (\n descriptorOrId: MessageDescriptor | MessageId,\n values?: Values,\n options?: MessageOptions\n ): string => this._(descriptorOrId as MessageId, values, options);\n\n /**\n * @deprecated Use `Intl.DateTimeFormat` directly.\n */\n date(\n value?: string | Date | number,\n format?: Intl.DateTimeFormatOptions\n ): string {\n if (value === undefined || value === null) return '';\n const dateValue =\n value instanceof Date\n ? value\n : new Date(typeof value === 'string' ? value : value);\n return new Intl.DateTimeFormat(this._locale, format).format(dateValue);\n }\n\n /**\n * @deprecated Use `Intl.NumberFormat` directly.\n */\n number(value: number | bigint, format?: Intl.NumberFormatOptions): string {\n return new Intl.NumberFormat(this._locale, format).format(value);\n }\n}\n"],"mappings":";;;;;;;;;;AA0CA,MAAM,gBAAgB,QAAiB,SAA0B;CAC/D,IAAI,CAAC,MAAM,OAAO;CAClB,MAAM,QAAQ,KAAK,MAAM,GAAG;CAC5B,IAAI,UAAmB;CACvB,KAAK,MAAM,QAAQ,OAAO;EACxB,IACE,YAAY,QACZ,YAAY,UACZ,OAAO,YAAY,UAEnB;EAEF,UAAW,QAAoC;CACjD;CACA,OAAO;AACT;;;;;;;;AASA,MAAM,iBACJ,IACA,WACuB;CACvB,IAAI;EAEF,MAAM,QAAQ,aADK,YAAY,YAA8B,MACzB,GAAG,EAAE;EACzC,OAAO,OAAO,UAAU,WAAW,QAAQ;CAC7C,QAAQ;EACN;CACF;AACF;;;;;;;;;;AAWA,IAAa,YAAb,cAA+B,aAA2B;CACxD,AAAQ;CACR,AAAQ;CAER,YAAY,EAAE,SAAS,MAAM,YAAuB,CAAC,GAAG;EACtD,MAAM;EACN,KAAK,UAAU,OAAO,WAAW,WAAW,SAAS;EACrD,KAAK,WAAW;CAClB;CAEA,IAAI,SAAiB;EACnB,OAAO,KAAK;CACd;CAEA,IAAI,UAA+B;EACjC,OAAO,KAAK;CACd;CAEA,IAAI,WAAqB;EACvB,IAAI;GACF,OAAO,YACL,YACA,KAAK,OACP;EACF,QAAQ;GACN,OAAO,CAAC;EACV;CACF;;;;CAKA,oBAAoB,WAAkC;EAElD,QAAQ,KACN,qHAEF;EAEF,OAAO;CACT;;;;CAKA,KAAK,cAAoC,WAA4B;EAEjE,QAAQ,KACN,yGAEF;CAEJ;;;;;CAMA,gBAAgB,EACd,QACA,WAKO;EACP,KAAK,SAAS,QAAQ,OAAO;CAC/B;;;;CAKA,SAAS,QAAgB,SAAyB;EAChD,KAAK,UAAU;EACf,KAAK,WAAW;EAChB,KAAK,KAAK,QAAQ;CACpB;CAYA,EACE,gBACA,QACA,SACQ;EACR,MAAM,eACJ,OAAO,mBAAmB,YAAY,mBAAmB;EAC3D,MAAM,KAAK,eACN,eAAqC,KACrC;EACL,MAAM,iBAAiB,eACjB,eAAqC,WAAW,SAAS,UAC3D,SAAS;EACb,MAAM,iBAAyB,eAC3B;GACE,GAAK,eAAqC,UAAU,CAAC;GACrD,GAAI,UAAU,CAAC;EACjB,IACC,UAAU,CAAC;EAKhB,OACE,eAJe,cAAc,IAAI,KAAK,OAChB,KAAK,kBAAkB,IAK3C,gBACA,KAAK,SACL,KACF,KAAK;CAET;;;;CAKA,KACE,gBACA,QACA,YACW,KAAK,EAAE,gBAA6B,QAAQ,OAAO;;;;CAKhE,KACE,OACA,QACQ;EACR,IAAI,UAAU,UAAa,UAAU,MAAM,OAAO;EAClD,MAAM,YACJ,iBAAiB,OACb,QACA,IAAI,KAAK,OAAO,UAAU,WAAW,QAAQ,KAAK;EACxD,OAAO,IAAI,KAAK,eAAe,KAAK,SAAS,MAAM,CAAC,CAAC,OAAO,SAAS;CACvE;;;;CAKA,OAAO,OAAwB,QAA2C;EACxE,OAAO,IAAI,KAAK,aAAa,KAAK,SAAS,MAAM,CAAC,CAAC,OAAO,KAAK;CACjE;AACF"}
1
+ {"version":3,"file":"I18nClass.mjs","names":[],"sources":["../../src/I18nClass.ts"],"sourcesContent":["import { getIntlayer } from '@intlayer/core/interpreter';\nimport { resolveMessage } from '@intlayer/core/messageFormat';\nimport { getDictionaries } from '@intlayer/dictionaries-entry';\nimport type {\n DictionaryKeys,\n LocalesValues,\n} from '@intlayer/types/module_augmentation';\nimport type {\n AllMessages,\n Locale,\n Locales,\n MessageDescriptor,\n MessageId,\n MessageOptions,\n Messages,\n} from '@lingui/core';\nimport { EventEmitter } from './eventEmitter';\nimport {\n linguiMessageToIcu,\n navigateLinguiCatalog,\n unwrapLinguiCatalog,\n} from './linguiCatalog';\n\n/** Mirrors the unexported `Values` type from `@lingui/core`. */\ntype Values = Record<string, unknown>;\n\n/** Mirrors the unexported `MessageCompiler` type from `@lingui/core`. */\ntype MessageCompiler = (message: string) => unknown;\n\n/** Mirrors the unexported `Events` type from `@lingui/core`. */\ntype LinguiEvents = {\n change: () => void;\n missing: (event: { locale: Locale; id: MessageId }) => void;\n};\n\n/** Mirrors the unexported `I18nProps` type from `@lingui/core`. */\ntype I18nProps = {\n locale?: Locale;\n locales?: Locales;\n messages?: AllMessages;\n missing?: string | ((locale: string, id: string) => string);\n};\n\n/**\n * Keys of every intlayer dictionary available at runtime (the namespaces).\n *\n * The bundler-injected `@intlayer/dictionaries-entry` exposes the full registry;\n * outside a bundle (e.g. unit tests) it resolves to an empty map, in which case\n * resolution falls back to the runtime catalogs loaded via `load()`.\n */\nconst getDictionaryKeys = (): DictionaryKeys[] => {\n try {\n return Object.keys(getDictionaries()) as DictionaryKeys[];\n } catch {\n return [];\n }\n};\n\n/**\n * Looks up a lingui message across every intlayer dictionary.\n *\n * lingui ids are flat, namespace-less keys (`'hero.title'`), but the matching\n * content may live in any dictionary — a single centralized catalog, or one of\n * many namespaced catalogs (`home`, `shared`, …) produced by `syncJSON` from\n * split lingui catalogs. Each dictionary is searched in turn, supporting both\n * the flat/nested key shapes and the lingui `{ messages: {…} }` wrapper. The\n * first match wins.\n */\nconst lookupDictionaryMessage = (\n id: string,\n locale: LocalesValues\n): string | undefined => {\n for (const key of getDictionaryKeys()) {\n let dictionary: unknown;\n try {\n dictionary = getIntlayer(key, locale);\n } catch {\n continue;\n }\n const value = navigateLinguiCatalog(dictionary, id);\n if (value !== undefined) return linguiMessageToIcu(value);\n }\n return undefined;\n};\n\n/**\n * Intlayer-backed implementation of `@lingui/core`'s `I18n` class.\n *\n * - Messages are read from the `messages` intlayer dictionary.\n * - ICU MessageFormat syntax is resolved via `@intlayer/core/messageFormat`.\n * - `load()` / `loadAndActivate()` / `setMessagesCompiler()` are no-ops —\n * messages are served by intlayer's compiled dictionaries.\n * - `activate(locale)` emits `'change'` so that React consumers re-render.\n */\nexport class I18nClass extends EventEmitter<LinguiEvents> {\n private _locale: string;\n private _locales?: Locales;\n /**\n * Per-locale catalogs supplied at runtime via the constructor, `load()` or\n * `loadAndActivate()`. Used as a fallback when a key is absent from the\n * compiled intlayer `messages` dictionary, so an unmodified lingui codebase\n * (which loads its compiled `.mjs`/`.json` catalogs) keeps working as a\n * drop-in. For optimal bundle size, compile catalogs into intlayer\n * dictionaries instead — see the dev warning emitted by `load()`.\n */\n private _catalogs: Record<string, Messages> = {};\n private _loadFallbackWarned = false;\n\n constructor({ locale = 'en', locales, messages }: I18nProps = {}) {\n super();\n this._locale = typeof locale === 'string' ? locale : 'en';\n this._locales = locales;\n if (messages) this.mergeAllCatalogs(messages);\n }\n\n get locale(): string {\n return this._locale;\n }\n\n get locales(): Locales | undefined {\n return this._locales;\n }\n\n get messages(): Messages {\n // Merge every intlayer dictionary (namespace) for the active locale into a\n // single flat catalog, unwrapping the lingui `{ messages: {…} }` wrapper.\n const dictionary: Messages = {};\n for (const key of getDictionaryKeys()) {\n try {\n Object.assign(\n dictionary,\n unwrapLinguiCatalog(getIntlayer(key, this._locale as LocalesValues))\n );\n } catch {\n // Skip dictionaries that fail to resolve for this locale.\n }\n }\n // The compiled dictionaries win over the runtime fallback catalog.\n return { ...(this._catalogs[this._locale] ?? {}), ...dictionary };\n }\n\n /** Merges a single-locale catalog into the in-memory fallback store. */\n private mergeLocaleCatalog(locale: string, catalog: Messages) {\n this._catalogs[locale] = { ...this._catalogs[locale], ...catalog };\n }\n\n /**\n * Merges a `{ [locale]: catalog }` map (lingui's `AllMessages`) into the\n * in-memory fallback store.\n */\n private mergeAllCatalogs(messages: AllMessages) {\n for (const [locale, catalog] of Object.entries(messages)) {\n if (catalog && typeof catalog === 'object') {\n this.mergeLocaleCatalog(locale, catalog as Messages);\n }\n }\n }\n\n /**\n * No-op: intlayer handles message compilation at build time.\n */\n setMessagesCompiler(_compiler: MessageCompiler): this {\n if (process.env.NODE_ENV === 'development') {\n console.warn(\n '@intlayer/lingui: i18n.setMessagesCompiler() is a no-op — ' +\n 'message compilation is handled at build time by intlayer.'\n );\n }\n return this;\n }\n\n /**\n * Loads message catalogs into the runtime fallback store, supporting both\n * lingui signatures: `load(locale, messages)` and `load(allMessages)`.\n *\n * These messages are a compatibility fallback — keys present in the compiled\n * intlayer `messages` dictionary take precedence, and only those keys can be\n * pruned/optimized at build time.\n */\n load(localeOrAll: Locale | AllMessages, messages?: Messages): void {\n if (typeof localeOrAll === 'string') {\n this.mergeLocaleCatalog(localeOrAll, messages ?? {});\n } else {\n this.mergeAllCatalogs(localeOrAll);\n }\n\n if (process.env.NODE_ENV === 'development' && !this._loadFallbackWarned) {\n this._loadFallbackWarned = true;\n console.warn(\n '@intlayer/lingui: i18n.load() messages are used as a runtime ' +\n 'fallback. For optimal bundle size, compile your catalogs into ' +\n 'intlayer dictionaries instead of importing lingui locale files.'\n );\n }\n }\n\n /**\n * Loads the given catalog (fallback store) and activates the locale\n * (emits `'change'`).\n */\n loadAndActivate({\n locale,\n locales,\n messages,\n }: {\n locale: Locale;\n locales?: Locales;\n messages?: Messages;\n }): void {\n if (messages) this.mergeLocaleCatalog(locale, messages);\n this.activate(locale, locales);\n }\n\n /**\n * Sets the active locale and emits `'change'` to notify React consumers.\n */\n activate(locale: Locale, locales?: Locales): void {\n this._locale = locale;\n this._locales = locales;\n this.emit('change');\n }\n\n /**\n * Resolves the raw message template for an id (before interpolation).\n *\n * Resolution order:\n * 1. compiled intlayer `messages` dictionary\n * 2. runtime fallback catalog (constructor / `load()` / `loadAndActivate()`)\n */\n private resolveTemplate(id: string): string | undefined {\n const fromDictionary = lookupDictionaryMessage(\n id,\n this._locale as LocalesValues\n );\n if (fromDictionary !== undefined) return fromDictionary;\n\n const catalog = this._catalogs[this._locale];\n if (catalog) {\n const raw = navigateLinguiCatalog(catalog, id);\n if (raw !== undefined) return linguiMessageToIcu(raw);\n }\n\n return undefined;\n }\n\n /**\n * Translates a message descriptor or a plain ID.\n *\n * Resolution order:\n * 1. `messages` intlayer dictionary (flat dotted key or nested path)\n * 2. runtime fallback catalog loaded via `load()` / `loadAndActivate()`\n * 3. `descriptor.message` or `options.message` (original source string)\n * 4. The raw `id` as fallback\n */\n _(descriptor: MessageDescriptor): string;\n _(id: MessageId, values?: Values, options?: MessageOptions): string;\n _(\n descriptorOrId: MessageDescriptor | MessageId,\n values?: Values,\n options?: MessageOptions\n ): string {\n const isDescriptor =\n typeof descriptorOrId === 'object' && descriptorOrId !== null;\n const id = isDescriptor\n ? (descriptorOrId as MessageDescriptor).id\n : (descriptorOrId as MessageId);\n const defaultMessage = isDescriptor\n ? ((descriptorOrId as MessageDescriptor).message ?? options?.message)\n : options?.message;\n const resolvedValues: Values = isDescriptor\n ? {\n ...((descriptorOrId as MessageDescriptor).values ?? {}),\n ...(values ?? {}),\n }\n : (values ?? {});\n\n const rawValue = this.resolveTemplate(id);\n const template = rawValue ?? defaultMessage ?? id;\n\n return (\n resolveMessage(\n template,\n resolvedValues as Record<string, string | number>,\n this._locale as LocalesValues,\n 'icu'\n ) ?? id\n );\n }\n\n /**\n * Alias for `_`. Provided for lingui API compatibility.\n */\n t = (\n descriptorOrId: MessageDescriptor | MessageId,\n values?: Values,\n options?: MessageOptions\n ): string => this._(descriptorOrId as MessageId, values, options);\n\n /**\n * @deprecated Use `Intl.DateTimeFormat` directly.\n */\n date(\n value?: string | Date | number,\n format?: Intl.DateTimeFormatOptions\n ): string {\n if (value === undefined || value === null) return '';\n const dateValue =\n value instanceof Date\n ? value\n : new Date(typeof value === 'string' ? value : value);\n return new Intl.DateTimeFormat(this._locale, format).format(dateValue);\n }\n\n /**\n * @deprecated Use `Intl.NumberFormat` directly.\n */\n number(value: number | bigint, format?: Intl.NumberFormatOptions): string {\n return new Intl.NumberFormat(this._locale, format).format(value);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;AAkDA,MAAM,0BAA4C;CAChD,IAAI;EACF,OAAO,OAAO,KAAK,gBAAgB,CAAC;CACtC,QAAQ;EACN,OAAO,CAAC;CACV;AACF;;;;;;;;;;;AAYA,MAAM,2BACJ,IACA,WACuB;CACvB,KAAK,MAAM,OAAO,kBAAkB,GAAG;EACrC,IAAI;EACJ,IAAI;GACF,aAAa,YAAY,KAAK,MAAM;EACtC,QAAQ;GACN;EACF;EACA,MAAM,QAAQ,sBAAsB,YAAY,EAAE;EAClD,IAAI,UAAU,QAAW,OAAO,mBAAmB,KAAK;CAC1D;AAEF;;;;;;;;;;AAWA,IAAa,YAAb,cAA+B,aAA2B;CACxD,AAAQ;CACR,AAAQ;;;;;;;;;CASR,AAAQ,YAAsC,CAAC;CAC/C,AAAQ,sBAAsB;CAE9B,YAAY,EAAE,SAAS,MAAM,SAAS,aAAwB,CAAC,GAAG;EAChE,MAAM;EACN,KAAK,UAAU,OAAO,WAAW,WAAW,SAAS;EACrD,KAAK,WAAW;EAChB,IAAI,UAAU,KAAK,iBAAiB,QAAQ;CAC9C;CAEA,IAAI,SAAiB;EACnB,OAAO,KAAK;CACd;CAEA,IAAI,UAA+B;EACjC,OAAO,KAAK;CACd;CAEA,IAAI,WAAqB;EAGvB,MAAM,aAAuB,CAAC;EAC9B,KAAK,MAAM,OAAO,kBAAkB,GAClC,IAAI;GACF,OAAO,OACL,YACA,oBAAoB,YAAY,KAAK,KAAK,OAAwB,CAAC,CACrE;EACF,QAAQ,CAER;EAGF,OAAO;GAAE,GAAI,KAAK,UAAU,KAAK,YAAY,CAAC;GAAI,GAAG;EAAW;CAClE;;CAGA,AAAQ,mBAAmB,QAAgB,SAAmB;EAC5D,KAAK,UAAU,UAAU;GAAE,GAAG,KAAK,UAAU;GAAS,GAAG;EAAQ;CACnE;;;;;CAMA,AAAQ,iBAAiB,UAAuB;EAC9C,KAAK,MAAM,CAAC,QAAQ,YAAY,OAAO,QAAQ,QAAQ,GACrD,IAAI,WAAW,OAAO,YAAY,UAChC,KAAK,mBAAmB,QAAQ,OAAmB;CAGzD;;;;CAKA,oBAAoB,WAAkC;EAElD,QAAQ,KACN,qHAEF;EAEF,OAAO;CACT;;;;;;;;;CAUA,KAAK,aAAmC,UAA2B;EACjE,IAAI,OAAO,gBAAgB,UACzB,KAAK,mBAAmB,aAAa,YAAY,CAAC,CAAC;OAEnD,KAAK,iBAAiB,WAAW;EAGnC,IAA8C,CAAC,KAAK,qBAAqB;GACvE,KAAK,sBAAsB;GAC3B,QAAQ,KACN,4LAGF;EACF;CACF;;;;;CAMA,gBAAgB,EACd,QACA,SACA,YAKO;EACP,IAAI,UAAU,KAAK,mBAAmB,QAAQ,QAAQ;EACtD,KAAK,SAAS,QAAQ,OAAO;CAC/B;;;;CAKA,SAAS,QAAgB,SAAyB;EAChD,KAAK,UAAU;EACf,KAAK,WAAW;EAChB,KAAK,KAAK,QAAQ;CACpB;;;;;;;;CASA,AAAQ,gBAAgB,IAAgC;EACtD,MAAM,iBAAiB,wBACrB,IACA,KAAK,OACP;EACA,IAAI,mBAAmB,QAAW,OAAO;EAEzC,MAAM,UAAU,KAAK,UAAU,KAAK;EACpC,IAAI,SAAS;GACX,MAAM,MAAM,sBAAsB,SAAS,EAAE;GAC7C,IAAI,QAAQ,QAAW,OAAO,mBAAmB,GAAG;EACtD;CAGF;CAaA,EACE,gBACA,QACA,SACQ;EACR,MAAM,eACJ,OAAO,mBAAmB,YAAY,mBAAmB;EAC3D,MAAM,KAAK,eACN,eAAqC,KACrC;EACL,MAAM,iBAAiB,eACjB,eAAqC,WAAW,SAAS,UAC3D,SAAS;EACb,MAAM,iBAAyB,eAC3B;GACE,GAAK,eAAqC,UAAU,CAAC;GACrD,GAAI,UAAU,CAAC;EACjB,IACC,UAAU,CAAC;EAKhB,OACE,eAJe,KAAK,gBAAgB,EACd,KAAK,kBAAkB,IAK3C,gBACA,KAAK,SACL,KACF,KAAK;CAET;;;;CAKA,KACE,gBACA,QACA,YACW,KAAK,EAAE,gBAA6B,QAAQ,OAAO;;;;CAKhE,KACE,OACA,QACQ;EACR,IAAI,UAAU,UAAa,UAAU,MAAM,OAAO;EAClD,MAAM,YACJ,iBAAiB,OACb,QACA,IAAI,KAAK,OAAO,UAAU,WAAW,QAAQ,KAAK;EACxD,OAAO,IAAI,KAAK,eAAe,KAAK,SAAS,MAAM,CAAC,CAAC,OAAO,SAAS;CACvE;;;;CAKA,OAAO,OAAwB,QAA2C;EACxE,OAAO,IAAI,KAAK,aAAa,KAAK,SAAS,MAAM,CAAC,CAAC,OAAO,KAAK;CACjE;AACF"}
@@ -0,0 +1,119 @@
1
+ //#region src/linguiCatalog.ts
2
+ /**
3
+ * Helpers for reading lingui message catalogs.
4
+ *
5
+ * lingui catalogs are flat maps whose keys are dotted strings
6
+ * (`'results-table.bundleSize'`) rather than nested objects, and whose
7
+ * compiled (`.mjs`) values are token arrays rather than plain strings. These
8
+ * helpers bridge that format onto intlayer's ICU-based `resolveMessage`.
9
+ */
10
+ /**
11
+ * Reads a value from a catalog by id.
12
+ *
13
+ * lingui ids are flat keys that themselves contain dots
14
+ * (`'results-table.bundleSize'`), so the flat key is tried first. When that
15
+ * misses, the id is treated as a nested path (`'home.title'` →
16
+ * `catalog.home.title`) to also support genuinely nested intlayer
17
+ * dictionaries.
18
+ *
19
+ * @example
20
+ * navigateCatalog({ 'a.b': 'X' }, 'a.b'); // 'X' (flat key)
21
+ * navigateCatalog({ a: { b: 'X' } }, 'a.b'); // 'X' (nested path)
22
+ */
23
+ const navigateCatalog = (catalog, id) => {
24
+ if (!id) return catalog;
25
+ if (catalog === null || typeof catalog !== "object") return void 0;
26
+ const flatValue = catalog[id];
27
+ if (flatValue !== void 0) return flatValue;
28
+ if (!id.includes(".")) return void 0;
29
+ let current = catalog;
30
+ for (const part of id.split(".")) {
31
+ if (current === null || current === void 0 || typeof current !== "object") return;
32
+ current = current[part];
33
+ }
34
+ return current;
35
+ };
36
+ /**
37
+ * Returns the flat id→message map of a catalog, transparently unwrapping the
38
+ * lingui `{ messages: {…} }` wrapper when present.
39
+ *
40
+ * Compiled lingui catalogs (`.po`/`.mjs`/`.json`) nest the id→message map under
41
+ * a top-level `messages` key, and syncJSON-backed intlayer dictionaries
42
+ * preserve that wrapper (`{ messages: { 'hero.title': […] } }`). Centralized
43
+ * intlayer dictionaries instead store the map at the top level
44
+ * (`{ 'hero.title': '…' }`). Both shapes are supported.
45
+ */
46
+ const unwrapLinguiCatalog = (catalog) => {
47
+ if (!catalog || typeof catalog !== "object") return {};
48
+ const wrapped = catalog.messages;
49
+ if (wrapped && typeof wrapped === "object") return wrapped;
50
+ return catalog;
51
+ };
52
+ /**
53
+ * Reads a value from a catalog by id, accepting both the flat/nested shapes
54
+ * handled by {@link navigateCatalog} and the lingui `{ messages: {…} }` wrapper.
55
+ *
56
+ * The direct lookup is tried first so a centralized dictionary that genuinely
57
+ * stores content at the top level keeps working; only on a miss does it look
58
+ * inside the `messages` wrapper used by namespaced lingui catalogs.
59
+ */
60
+ const navigateLinguiCatalog = (catalog, id) => {
61
+ const direct = navigateCatalog(catalog, id);
62
+ if (direct !== void 0) return direct;
63
+ if (catalog && typeof catalog === "object") {
64
+ const wrapped = catalog.messages;
65
+ if (wrapped && typeof wrapped === "object") return navigateCatalog(wrapped, id);
66
+ }
67
+ };
68
+ /**
69
+ * Converts a single compiled lingui token to its ICU MessageFormat equivalent.
70
+ *
71
+ * Token shapes (from `@lingui/message-utils`):
72
+ * - `string` → literal text
73
+ * - `[name]` → `{name}`
74
+ * - `[name, 'number' | 'date' | 'time', format?]` → `{name, number, format}`
75
+ * - `[name, 'plural' | 'select' | 'selectordinal', options]` → ICU block,
76
+ * where each option value is itself a compiled sub-message.
77
+ */
78
+ const tokenToIcu = (token) => {
79
+ if (typeof token === "string") return token;
80
+ if (!Array.isArray(token)) return "";
81
+ const [name, type, format] = token;
82
+ if (type === void 0) return `{${String(name)}}`;
83
+ if (type === "plural" || type === "select" || type === "selectordinal") {
84
+ const options = format ?? {};
85
+ const segments = [];
86
+ let offsetSegment = "";
87
+ for (const [category, value] of Object.entries(options)) {
88
+ if (category === "offset") {
89
+ offsetSegment = `offset:${String(value)} `;
90
+ continue;
91
+ }
92
+ segments.push(`${category} {${linguiMessageToIcu(value)}}`);
93
+ }
94
+ return `{${String(name)}, ${type}, ${offsetSegment}${segments.join(" ")}}`;
95
+ }
96
+ return format !== void 0 ? `{${String(name)}, ${type}, ${String(format)}}` : `{${String(name)}, ${type}}`;
97
+ };
98
+ /**
99
+ * Converts a compiled lingui message into an ICU MessageFormat string that
100
+ * `@intlayer/core`'s `resolveMessage(…, 'icu')` can interpolate.
101
+ *
102
+ * Plain strings (uncompiled `.json` catalogs) pass through unchanged; token
103
+ * arrays (compiled `.mjs` catalogs) are flattened, including nested
104
+ * plural/select blocks and `{name}` placeholders.
105
+ *
106
+ * @example
107
+ * linguiMessageToIcu(['Bundle Size']); // 'Bundle Size'
108
+ * linguiMessageToIcu(['Hello ', ['name']]); // 'Hello {name}'
109
+ * linguiMessageToIcu('Already ICU {count}'); // 'Already ICU {count}'
110
+ */
111
+ const linguiMessageToIcu = (compiled) => {
112
+ if (typeof compiled === "string") return compiled;
113
+ if (!Array.isArray(compiled)) return String(compiled ?? "");
114
+ return compiled.map(tokenToIcu).join("");
115
+ };
116
+
117
+ //#endregion
118
+ export { linguiMessageToIcu, navigateCatalog, navigateLinguiCatalog, unwrapLinguiCatalog };
119
+ //# sourceMappingURL=linguiCatalog.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"linguiCatalog.mjs","names":[],"sources":["../../src/linguiCatalog.ts"],"sourcesContent":["/**\n * Helpers for reading lingui message catalogs.\n *\n * lingui catalogs are flat maps whose keys are dotted strings\n * (`'results-table.bundleSize'`) rather than nested objects, and whose\n * compiled (`.mjs`) values are token arrays rather than plain strings. These\n * helpers bridge that format onto intlayer's ICU-based `resolveMessage`.\n */\n\n/**\n * Reads a value from a catalog by id.\n *\n * lingui ids are flat keys that themselves contain dots\n * (`'results-table.bundleSize'`), so the flat key is tried first. When that\n * misses, the id is treated as a nested path (`'home.title'` →\n * `catalog.home.title`) to also support genuinely nested intlayer\n * dictionaries.\n *\n * @example\n * navigateCatalog({ 'a.b': 'X' }, 'a.b'); // 'X' (flat key)\n * navigateCatalog({ a: { b: 'X' } }, 'a.b'); // 'X' (nested path)\n */\nexport const navigateCatalog = (catalog: unknown, id: string): unknown => {\n if (!id) return catalog;\n if (catalog === null || typeof catalog !== 'object') return undefined;\n\n const flatValue = (catalog as Record<string, unknown>)[id];\n if (flatValue !== undefined) return flatValue;\n\n if (!id.includes('.')) return undefined;\n\n let current: unknown = catalog;\n for (const part of id.split('.')) {\n if (\n current === null ||\n current === undefined ||\n typeof current !== 'object'\n ) {\n return undefined;\n }\n current = (current as Record<string, unknown>)[part];\n }\n return current;\n};\n\n/**\n * Returns the flat id→message map of a catalog, transparently unwrapping the\n * lingui `{ messages: {…} }` wrapper when present.\n *\n * Compiled lingui catalogs (`.po`/`.mjs`/`.json`) nest the id→message map under\n * a top-level `messages` key, and syncJSON-backed intlayer dictionaries\n * preserve that wrapper (`{ messages: { 'hero.title': […] } }`). Centralized\n * intlayer dictionaries instead store the map at the top level\n * (`{ 'hero.title': '…' }`). Both shapes are supported.\n */\nexport const unwrapLinguiCatalog = (\n catalog: unknown\n): Record<string, unknown> => {\n if (!catalog || typeof catalog !== 'object') return {};\n const wrapped = (catalog as Record<string, unknown>).messages;\n if (wrapped && typeof wrapped === 'object') {\n return wrapped as Record<string, unknown>;\n }\n return catalog as Record<string, unknown>;\n};\n\n/**\n * Reads a value from a catalog by id, accepting both the flat/nested shapes\n * handled by {@link navigateCatalog} and the lingui `{ messages: {…} }` wrapper.\n *\n * The direct lookup is tried first so a centralized dictionary that genuinely\n * stores content at the top level keeps working; only on a miss does it look\n * inside the `messages` wrapper used by namespaced lingui catalogs.\n */\nexport const navigateLinguiCatalog = (\n catalog: unknown,\n id: string\n): unknown => {\n const direct = navigateCatalog(catalog, id);\n if (direct !== undefined) return direct;\n\n if (catalog && typeof catalog === 'object') {\n const wrapped = (catalog as Record<string, unknown>).messages;\n if (wrapped && typeof wrapped === 'object') {\n return navigateCatalog(wrapped, id);\n }\n }\n\n return undefined;\n};\n\n/**\n * Converts a single compiled lingui token to its ICU MessageFormat equivalent.\n *\n * Token shapes (from `@lingui/message-utils`):\n * - `string` → literal text\n * - `[name]` → `{name}`\n * - `[name, 'number' | 'date' | 'time', format?]` → `{name, number, format}`\n * - `[name, 'plural' | 'select' | 'selectordinal', options]` → ICU block,\n * where each option value is itself a compiled sub-message.\n */\nconst tokenToIcu = (token: unknown): string => {\n if (typeof token === 'string') return token;\n if (!Array.isArray(token)) return '';\n\n const [name, type, format] = token as [string, string?, unknown?];\n\n if (type === undefined) return `{${String(name)}}`;\n\n if (type === 'plural' || type === 'select' || type === 'selectordinal') {\n const options = (format ?? {}) as Record<string, unknown>;\n const segments: string[] = [];\n let offsetSegment = '';\n\n for (const [category, value] of Object.entries(options)) {\n if (category === 'offset') {\n offsetSegment = `offset:${String(value)} `;\n continue;\n }\n segments.push(`${category} {${linguiMessageToIcu(value)}}`);\n }\n\n return `{${String(name)}, ${type}, ${offsetSegment}${segments.join(' ')}}`;\n }\n\n return format !== undefined\n ? `{${String(name)}, ${type}, ${String(format)}}`\n : `{${String(name)}, ${type}}`;\n};\n\n/**\n * Converts a compiled lingui message into an ICU MessageFormat string that\n * `@intlayer/core`'s `resolveMessage(…, 'icu')` can interpolate.\n *\n * Plain strings (uncompiled `.json` catalogs) pass through unchanged; token\n * arrays (compiled `.mjs` catalogs) are flattened, including nested\n * plural/select blocks and `{name}` placeholders.\n *\n * @example\n * linguiMessageToIcu(['Bundle Size']); // 'Bundle Size'\n * linguiMessageToIcu(['Hello ', ['name']]); // 'Hello {name}'\n * linguiMessageToIcu('Already ICU {count}'); // 'Already ICU {count}'\n */\nexport const linguiMessageToIcu = (compiled: unknown): string => {\n if (typeof compiled === 'string') return compiled;\n if (!Array.isArray(compiled)) return String(compiled ?? '');\n return compiled.map(tokenToIcu).join('');\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAsBA,MAAa,mBAAmB,SAAkB,OAAwB;CACxE,IAAI,CAAC,IAAI,OAAO;CAChB,IAAI,YAAY,QAAQ,OAAO,YAAY,UAAU,OAAO;CAE5D,MAAM,YAAa,QAAoC;CACvD,IAAI,cAAc,QAAW,OAAO;CAEpC,IAAI,CAAC,GAAG,SAAS,GAAG,GAAG,OAAO;CAE9B,IAAI,UAAmB;CACvB,KAAK,MAAM,QAAQ,GAAG,MAAM,GAAG,GAAG;EAChC,IACE,YAAY,QACZ,YAAY,UACZ,OAAO,YAAY,UAEnB;EAEF,UAAW,QAAoC;CACjD;CACA,OAAO;AACT;;;;;;;;;;;AAYA,MAAa,uBACX,YAC4B;CAC5B,IAAI,CAAC,WAAW,OAAO,YAAY,UAAU,OAAO,CAAC;CACrD,MAAM,UAAW,QAAoC;CACrD,IAAI,WAAW,OAAO,YAAY,UAChC,OAAO;CAET,OAAO;AACT;;;;;;;;;AAUA,MAAa,yBACX,SACA,OACY;CACZ,MAAM,SAAS,gBAAgB,SAAS,EAAE;CAC1C,IAAI,WAAW,QAAW,OAAO;CAEjC,IAAI,WAAW,OAAO,YAAY,UAAU;EAC1C,MAAM,UAAW,QAAoC;EACrD,IAAI,WAAW,OAAO,YAAY,UAChC,OAAO,gBAAgB,SAAS,EAAE;CAEtC;AAGF;;;;;;;;;;;AAYA,MAAM,cAAc,UAA2B;CAC7C,IAAI,OAAO,UAAU,UAAU,OAAO;CACtC,IAAI,CAAC,MAAM,QAAQ,KAAK,GAAG,OAAO;CAElC,MAAM,CAAC,MAAM,MAAM,UAAU;CAE7B,IAAI,SAAS,QAAW,OAAO,IAAI,OAAO,IAAI,EAAE;CAEhD,IAAI,SAAS,YAAY,SAAS,YAAY,SAAS,iBAAiB;EACtE,MAAM,UAAW,UAAU,CAAC;EAC5B,MAAM,WAAqB,CAAC;EAC5B,IAAI,gBAAgB;EAEpB,KAAK,MAAM,CAAC,UAAU,UAAU,OAAO,QAAQ,OAAO,GAAG;GACvD,IAAI,aAAa,UAAU;IACzB,gBAAgB,UAAU,OAAO,KAAK,EAAE;IACxC;GACF;GACA,SAAS,KAAK,GAAG,SAAS,IAAI,mBAAmB,KAAK,EAAE,EAAE;EAC5D;EAEA,OAAO,IAAI,OAAO,IAAI,EAAE,IAAI,KAAK,IAAI,gBAAgB,SAAS,KAAK,GAAG,EAAE;CAC1E;CAEA,OAAO,WAAW,SACd,IAAI,OAAO,IAAI,EAAE,IAAI,KAAK,IAAI,OAAO,MAAM,EAAE,KAC7C,IAAI,OAAO,IAAI,EAAE,IAAI,KAAK;AAChC;;;;;;;;;;;;;;AAeA,MAAa,sBAAsB,aAA8B;CAC/D,IAAI,OAAO,aAAa,UAAU,OAAO;CACzC,IAAI,CAAC,MAAM,QAAQ,QAAQ,GAAG,OAAO,OAAO,YAAY,EAAE;CAC1D,OAAO,SAAS,IAAI,UAAU,CAAC,CAAC,KAAK,EAAE;AACzC"}
@@ -9,15 +9,43 @@ import { intlayer } from "vite-intlayer";
9
9
  /**
10
10
  * Caller configurations for lingui.
11
11
  *
12
- * Lingui's `i18n._(id)` / `i18n.t` and `useLingui()`'s `_()` all resolve
13
- * translations at runtime through the intlayer `messages` dictionary. Since the
14
- * mapping is 1:1 (one catalog one `messages` dict) there is no namespace
15
- * splitting needed at the analyser level, so the caller list is empty for now.
12
+ * After `@lingui/babel-plugin-lingui-macro` / `@lingui/swc-plugin` compiles
13
+ * macros (`` t`Hello ${name}` `` `i18n._(id, values)`) the intlayer field
14
+ * usage analyser sees these method calls and records which top-level fields of
15
+ * the `messages` dictionary are consumed, enabling accurate pruning.
16
16
  *
17
- * Future: once `namespace: { from: 'self' }` is supported in the babel/swc
18
- * analyser, we can add callers for `useLingui()._(id)`.
17
+ * Two call forms are tracked:
18
+ * - `i18n._('home.title', values)` string id
19
+ * - `i18n._({ id: 'home.title', message: '...' }, values)` — descriptor
20
+ * - `i18n.t('home.title', values)` — alias for `_`
21
+ *
22
+ * Because lingui projects may also use hashed IDs (generated from the default
23
+ * message string) that cannot be statically mapped to dictionary keys, a
24
+ * separate `'all'`-mode caller is not needed here: when the first argument is
25
+ * not a static string or descriptor, the `'self'` handler falls back to `'all'`
26
+ * automatically.
19
27
  */
20
- const LINGUI_COMPAT_CALLERS = [];
28
+ const LINGUI_COMPAT_CALLERS = [{
29
+ callerName: "_",
30
+ importSources: ["@lingui/core", "@intlayer/lingui"],
31
+ matchAsMethod: true,
32
+ namespace: {
33
+ from: "fixed",
34
+ value: "messages"
35
+ },
36
+ translationFunction: "self",
37
+ flatKey: true
38
+ }, {
39
+ callerName: "t",
40
+ importSources: ["@lingui/core", "@intlayer/lingui"],
41
+ matchAsMethod: true,
42
+ namespace: {
43
+ from: "fixed",
44
+ value: "messages"
45
+ },
46
+ translationFunction: "self",
47
+ flatKey: true
48
+ }];
21
49
  /**
22
50
  * A Vite plugin for the lingui compat adapter.
23
51
  *
@@ -28,14 +56,14 @@ const LINGUI_COMPAT_CALLERS = [];
28
56
  * @example
29
57
  * ```ts
30
58
  * // vite.config.ts
31
- * import { linguiVitePlugin } from '@intlayer/lingui/plugin';
59
+ * import { lingui } from '@intlayer/lingui/plugin';
32
60
  *
33
61
  * export default defineConfig({
34
- * plugins: [linguiVitePlugin()],
62
+ * plugins: [lingui()],
35
63
  * });
36
64
  * ```
37
65
  */
38
- const linguiVitePlugin = (options) => {
66
+ const lingui = (options) => {
39
67
  const intlayerConfig = getConfiguration();
40
68
  const appLogger = getAppLogger(intlayerConfig);
41
69
  runOnce(join(intlayerConfig.system.baseDir, ".intlayer", "cache", "intlayer-issues-invitation.lock"), () => {
@@ -54,7 +82,12 @@ const linguiVitePlugin = (options) => {
54
82
  };
55
83
  return [...Array.isArray(basePlugins) ? basePlugins : [basePlugins], compatPlugin];
56
84
  };
85
+ /**
86
+ * Backwards-compatible alias for {@link lingui}, matching the naming used by the
87
+ * other compat adapters' Vite plugins (e.g. `i18nextVitePlugin`).
88
+ */
89
+ const linguiVitePlugin = lingui;
57
90
 
58
91
  //#endregion
59
- export { linguiVitePlugin as default, linguiVitePlugin };
92
+ export { lingui as default, lingui, linguiVitePlugin };
60
93
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../../src/plugin/index.ts"],"sourcesContent":["import { join } from 'node:path';\nimport { runOnce } from '@intlayer/chokidar/utils';\nimport * as ANSIColors from '@intlayer/config/colors';\nimport { colorize, getAppLogger } from '@intlayer/config/logger';\nimport { getConfiguration } from '@intlayer/config/node';\nimport type { PluginOption } from 'vite';\nimport { type CompatCallerConfig, intlayer } from 'vite-intlayer';\n\n/**\n * Caller configurations for lingui.\n *\n * Lingui's `i18n._(id)` / `i18n.t` and `useLingui()`'s `_()` all resolve\n * translations at runtime through the intlayer `messages` dictionary. Since the\n * mapping is 1:1 (one catalog one `messages` dict) there is no namespace\n * splitting needed at the analyser level, so the caller list is empty for now.\n *\n * Future: once `namespace: { from: 'self' }` is supported in the babel/swc\n * analyser, we can add callers for `useLingui()._(id)`.\n */\nconst LINGUI_COMPAT_CALLERS: CompatCallerConfig[] = [];\n\n/**\n * A Vite plugin for the lingui compat adapter.\n *\n * Wraps `vite-intlayer` and adds resolve aliases so that both\n * `@lingui/core` **and** `@lingui/react` are redirected to\n * `@intlayer/lingui` at bundle time.\n *\n * @example\n * ```ts\n * // vite.config.ts\n * import { linguiVitePlugin } from '@intlayer/lingui/plugin';\n *\n * export default defineConfig({\n * plugins: [linguiVitePlugin()],\n * });\n * ```\n */\nexport const linguiVitePlugin = (\n options?: Parameters<typeof intlayer>[0]\n): PluginOption[] => {\n const intlayerConfig = getConfiguration();\n const appLogger = getAppLogger(intlayerConfig);\n\n runOnce(\n join(\n intlayerConfig.system.baseDir,\n '.intlayer',\n 'cache',\n 'intlayer-issues-invitation.lock'\n ),\n () => {\n appLogger([\n colorize(\n 'Please report any issues you met on GitHub:',\n ANSIColors.GREY\n ),\n colorize(\n 'https://github.com/aymericzip/intlayer/issues',\n ANSIColors.GREY_LIGHT\n ),\n ]);\n },\n {\n cacheTimeoutMs: 1000 * 60 * 60, // 1 hour\n }\n );\n\n const basePlugins = intlayer({\n ...options,\n compatCallers: [\n ...(options?.compatCallers ?? []),\n ...LINGUI_COMPAT_CALLERS,\n ],\n });\n\n const compatPlugin: PluginOption = {\n name: 'vite-lingui-compat-plugin',\n config: () => ({\n resolve: {\n alias: {\n '@lingui/core': '@intlayer/lingui',\n '@lingui/react': '@intlayer/lingui',\n },\n },\n }),\n };\n\n return [\n ...(Array.isArray(basePlugins) ? basePlugins : [basePlugins]),\n compatPlugin,\n ];\n};\n\nexport default linguiVitePlugin;\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAmBA,MAAM,wBAA8C,CAAC;;;;;;;;;;;;;;;;;;AAmBrD,MAAa,oBACX,YACmB;CACnB,MAAM,iBAAiB,iBAAiB;CACxC,MAAM,YAAY,aAAa,cAAc;CAE7C,QACE,KACE,eAAe,OAAO,SACtB,aACA,SACA,iCACF,SACM;EACJ,UAAU,CACR,SACE,+CACA,WAAW,IACb,GACA,SACE,iDACA,WAAW,UACb,CACF,CAAC;CACH,GACA,EACE,gBAAgB,MAAO,KAAK,GAC9B,CACF;CAEA,MAAM,cAAc,SAAS;EAC3B,GAAG;EACH,eAAe,CACb,GAAI,SAAS,iBAAiB,CAAC,GAC/B,GAAG,qBACL;CACF,CAAC;CAED,MAAM,eAA6B;EACjC,MAAM;EACN,eAAe,EACb,SAAS,EACP,OAAO;GACL,gBAAgB;GAChB,iBAAiB;EACnB,EACF,EACF;CACF;CAEA,OAAO,CACL,GAAI,MAAM,QAAQ,WAAW,IAAI,cAAc,CAAC,WAAW,GAC3D,YACF;AACF"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../src/plugin/index.ts"],"sourcesContent":["import { join } from 'node:path';\nimport { runOnce } from '@intlayer/chokidar/utils';\nimport * as ANSIColors from '@intlayer/config/colors';\nimport { colorize, getAppLogger } from '@intlayer/config/logger';\nimport { getConfiguration } from '@intlayer/config/node';\nimport type { PluginOption } from 'vite';\nimport { type CompatCallerConfig, intlayer } from 'vite-intlayer';\n\n/**\n * Caller configurations for lingui.\n *\n * After `@lingui/babel-plugin-lingui-macro` / `@lingui/swc-plugin` compiles\n * macros (`` t`Hello ${name}` `` → `i18n._(id, values)`) the intlayer field\n * usage analyser sees these method calls and records which top-level fields of\n * the `messages` dictionary are consumed, enabling accurate pruning.\n *\n * Two call forms are tracked:\n * - `i18n._('home.title', values)` — string id\n * - `i18n._({ id: 'home.title', message: '...' }, values)` descriptor\n * - `i18n.t('home.title', values)` — alias for `_`\n *\n * Because lingui projects may also use hashed IDs (generated from the default\n * message string) that cannot be statically mapped to dictionary keys, a\n * separate `'all'`-mode caller is not needed here: when the first argument is\n * not a static string or descriptor, the `'self'` handler falls back to `'all'`\n * automatically.\n */\nconst LINGUI_COMPAT_CALLERS: CompatCallerConfig[] = [\n {\n callerName: '_',\n importSources: ['@lingui/core', '@intlayer/lingui'],\n matchAsMethod: true,\n namespace: { from: 'fixed', value: 'messages' },\n translationFunction: 'self',\n // lingui catalogs are flat: `i18n._('results-table.bundleSize')` reads the\n // literal `'results-table.bundleSize'` key, so the whole id is the field.\n flatKey: true,\n },\n {\n callerName: 't',\n importSources: ['@lingui/core', '@intlayer/lingui'],\n matchAsMethod: true,\n namespace: { from: 'fixed', value: 'messages' },\n translationFunction: 'self',\n flatKey: true,\n },\n];\n\n/**\n * A Vite plugin for the lingui compat adapter.\n *\n * Wraps `vite-intlayer` and adds resolve aliases so that both\n * `@lingui/core` **and** `@lingui/react` are redirected to\n * `@intlayer/lingui` at bundle time.\n *\n * @example\n * ```ts\n * // vite.config.ts\n * import { lingui } from '@intlayer/lingui/plugin';\n *\n * export default defineConfig({\n * plugins: [lingui()],\n * });\n * ```\n */\nexport const lingui = (\n options?: Parameters<typeof intlayer>[0]\n): PluginOption[] => {\n const intlayerConfig = getConfiguration();\n const appLogger = getAppLogger(intlayerConfig);\n\n runOnce(\n join(\n intlayerConfig.system.baseDir,\n '.intlayer',\n 'cache',\n 'intlayer-issues-invitation.lock'\n ),\n () => {\n appLogger([\n colorize(\n 'Please report any issues you met on GitHub:',\n ANSIColors.GREY\n ),\n colorize(\n 'https://github.com/aymericzip/intlayer/issues',\n ANSIColors.GREY_LIGHT\n ),\n ]);\n },\n {\n cacheTimeoutMs: 1000 * 60 * 60, // 1 hour\n }\n );\n\n const basePlugins = intlayer({\n ...options,\n compatCallers: [\n ...(options?.compatCallers ?? []),\n ...LINGUI_COMPAT_CALLERS,\n ],\n });\n\n const compatPlugin: PluginOption = {\n name: 'vite-lingui-compat-plugin',\n config: () => ({\n resolve: {\n alias: {\n '@lingui/core': '@intlayer/lingui',\n '@lingui/react': '@intlayer/lingui',\n },\n },\n }),\n };\n\n return [\n ...(Array.isArray(basePlugins) ? basePlugins : [basePlugins]),\n compatPlugin,\n ];\n};\n\n/**\n * Backwards-compatible alias for {@link lingui}, matching the naming used by the\n * other compat adapters' Vite plugins (e.g. `i18nextVitePlugin`).\n */\nexport const linguiVitePlugin = lingui;\n\nexport default lingui;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AA2BA,MAAM,wBAA8C,CAClD;CACE,YAAY;CACZ,eAAe,CAAC,gBAAgB,kBAAkB;CAClD,eAAe;CACf,WAAW;EAAE,MAAM;EAAS,OAAO;CAAW;CAC9C,qBAAqB;CAGrB,SAAS;AACX,GACA;CACE,YAAY;CACZ,eAAe,CAAC,gBAAgB,kBAAkB;CAClD,eAAe;CACf,WAAW;EAAE,MAAM;EAAS,OAAO;CAAW;CAC9C,qBAAqB;CACrB,SAAS;AACX,CACF;;;;;;;;;;;;;;;;;;AAmBA,MAAa,UACX,YACmB;CACnB,MAAM,iBAAiB,iBAAiB;CACxC,MAAM,YAAY,aAAa,cAAc;CAE7C,QACE,KACE,eAAe,OAAO,SACtB,aACA,SACA,iCACF,SACM;EACJ,UAAU,CACR,SACE,+CACA,WAAW,IACb,GACA,SACE,iDACA,WAAW,UACb,CACF,CAAC;CACH,GACA,EACE,gBAAgB,MAAO,KAAK,GAC9B,CACF;CAEA,MAAM,cAAc,SAAS;EAC3B,GAAG;EACH,eAAe,CACb,GAAI,SAAS,iBAAiB,CAAC,GAC/B,GAAG,qBACL;CACF,CAAC;CAED,MAAM,eAA6B;EACjC,MAAM;EACN,eAAe,EACb,SAAS,EACP,OAAO;GACL,gBAAgB;GAChB,iBAAiB;EACnB,EACF,EACF;CACF;CAEA,OAAO,CACL,GAAI,MAAM,QAAQ,WAAW,IAAI,cAAc,CAAC,WAAW,GAC3D,YACF;AACF;;;;;AAMA,MAAa,mBAAmB"}
@@ -1 +1 @@
1
- {"version":3,"file":"useLingui.mjs","names":[],"sources":["../../src/useLingui.ts"],"sourcesContent":["'use client';\n\nimport type { LocalesValues } from '@intlayer/types/module_augmentation';\nimport type { I18nContext } from '@lingui/react';\nimport { useContext, useMemo } from 'react';\nimport { useLocale } from 'react-intlayer';\nimport { I18nClass } from './I18nClass';\nimport { LinguiContext } from './LinguiContext';\n\n/**\n * Drop-in for `@lingui/react`'s `useLingui`.\n *\n * Returns `{ i18n, _, defaultComponent }` from the nearest `I18nProvider`.\n * When used outside a provider, derives a locale-aware `i18n` instance from\n * `react-intlayer`'s `useLocale()` so that Server Components and test contexts\n * work without an explicit provider.\n *\n * @example\n * ```tsx\n * const { _ } = useLingui();\n * return <h1>{_({ id: 'home.title', message: 'Welcome' })}</h1>;\n * ```\n */\nexport const useLingui = (): I18nContext => {\n const context = useContext(LinguiContext);\n const { locale } = useLocale();\n\n const derivedI18n = useMemo(() => {\n const instance = new I18nClass({ locale: locale as string });\n return {\n i18n: instance as unknown as I18nContext['i18n'],\n _: instance._.bind(instance) as I18nContext['_'],\n };\n }, [locale]);\n\n if (context) {\n return context;\n }\n\n return derivedI18n as I18nContext;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAuBA,MAAa,kBAA+B;CAC1C,MAAM,UAAU,WAAW,aAAa;CACxC,MAAM,EAAE,WAAW,UAAU;CAE7B,MAAM,cAAc,cAAc;EAChC,MAAM,WAAW,IAAI,UAAU,EAAU,OAAiB,CAAC;EAC3D,OAAO;GACL,MAAM;GACN,GAAG,SAAS,EAAE,KAAK,QAAQ;EAC7B;CACF,GAAG,CAAC,MAAM,CAAC;CAEX,IAAI,SACF,OAAO;CAGT,OAAO;AACT"}
1
+ {"version":3,"file":"useLingui.mjs","names":[],"sources":["../../src/useLingui.ts"],"sourcesContent":["'use client';\n\nimport type { I18nContext } from '@lingui/react';\nimport { useContext, useMemo } from 'react';\nimport { useLocale } from 'react-intlayer';\nimport { I18nClass } from './I18nClass';\nimport { LinguiContext } from './LinguiContext';\n\n/**\n * Drop-in for `@lingui/react`'s `useLingui`.\n *\n * Returns `{ i18n, _, defaultComponent }` from the nearest `I18nProvider`.\n * When used outside a provider, derives a locale-aware `i18n` instance from\n * `react-intlayer`'s `useLocale()` so that Server Components and test contexts\n * work without an explicit provider.\n *\n * @example\n * ```tsx\n * const { _ } = useLingui();\n * return <h1>{_({ id: 'home.title', message: 'Welcome' })}</h1>;\n * ```\n */\nexport const useLingui = (): I18nContext => {\n const context = useContext(LinguiContext);\n const { locale } = useLocale();\n\n const derivedI18n = useMemo(() => {\n const instance = new I18nClass({ locale: locale as string });\n return {\n i18n: instance as unknown as I18nContext['i18n'],\n _: instance._.bind(instance) as I18nContext['_'],\n };\n }, [locale]);\n\n if (context) {\n return context;\n }\n\n return derivedI18n as I18nContext;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAsBA,MAAa,kBAA+B;CAC1C,MAAM,UAAU,WAAW,aAAa;CACxC,MAAM,EAAE,WAAW,UAAU;CAE7B,MAAM,cAAc,cAAc;EAChC,MAAM,WAAW,IAAI,UAAU,EAAU,OAAiB,CAAC;EAC3D,OAAO;GACL,MAAM;GACN,GAAG,SAAS,EAAE,KAAK,QAAQ;EAC7B;CACF,GAAG,CAAC,MAAM,CAAC;CAEX,IAAI,SACF,OAAO;CAGT,OAAO;AACT"}
@@ -33,44 +33,77 @@ type I18nProps = {
33
33
  declare class I18nClass extends EventEmitter<LinguiEvents> {
34
34
  private _locale;
35
35
  private _locales?;
36
+ /**
37
+ * Per-locale catalogs supplied at runtime via the constructor, `load()` or
38
+ * `loadAndActivate()`. Used as a fallback when a key is absent from the
39
+ * compiled intlayer `messages` dictionary, so an unmodified lingui codebase
40
+ * (which loads its compiled `.mjs`/`.json` catalogs) keeps working as a
41
+ * drop-in. For optimal bundle size, compile catalogs into intlayer
42
+ * dictionaries instead — see the dev warning emitted by `load()`.
43
+ */
44
+ private _catalogs;
45
+ private _loadFallbackWarned;
36
46
  constructor({
37
47
  locale,
38
- locales
48
+ locales,
49
+ messages
39
50
  }?: I18nProps);
40
51
  get locale(): string;
41
52
  get locales(): Locales | undefined;
42
53
  get messages(): Messages;
54
+ /** Merges a single-locale catalog into the in-memory fallback store. */
55
+ private mergeLocaleCatalog;
56
+ /**
57
+ * Merges a `{ [locale]: catalog }` map (lingui's `AllMessages`) into the
58
+ * in-memory fallback store.
59
+ */
60
+ private mergeAllCatalogs;
43
61
  /**
44
62
  * No-op: intlayer handles message compilation at build time.
45
63
  */
46
64
  setMessagesCompiler(_compiler: MessageCompiler): this;
47
65
  /**
48
- * No-op: messages are loaded automatically via intlayer dictionaries.
66
+ * Loads message catalogs into the runtime fallback store, supporting both
67
+ * lingui signatures: `load(locale, messages)` and `load(allMessages)`.
68
+ *
69
+ * These messages are a compatibility fallback — keys present in the compiled
70
+ * intlayer `messages` dictionary take precedence, and only those keys can be
71
+ * pruned/optimized at build time.
49
72
  */
50
- load(_localeOrAll: Locale | AllMessages, _messages?: Messages): void;
73
+ load(localeOrAll: Locale | AllMessages, messages?: Messages): void;
51
74
  /**
52
- * Activates the given locale (emits `'change'`).
53
- * The `messages` argument is ignored — intlayer handles the catalog.
75
+ * Loads the given catalog (fallback store) and activates the locale
76
+ * (emits `'change'`).
54
77
  */
55
78
  loadAndActivate({
56
79
  locale,
57
- locales
80
+ locales,
81
+ messages
58
82
  }: {
59
83
  locale: Locale;
60
84
  locales?: Locales;
61
- messages: Messages;
85
+ messages?: Messages;
62
86
  }): void;
63
87
  /**
64
88
  * Sets the active locale and emits `'change'` to notify React consumers.
65
89
  */
66
90
  activate(locale: Locale, locales?: Locales): void;
91
+ /**
92
+ * Resolves the raw message template for an id (before interpolation).
93
+ *
94
+ * Resolution order:
95
+ * 1. compiled intlayer `messages` dictionary
96
+ * 2. runtime fallback catalog (constructor / `load()` / `loadAndActivate()`)
97
+ */
98
+ private resolveTemplate;
67
99
  /**
68
100
  * Translates a message descriptor or a plain ID.
69
101
  *
70
102
  * Resolution order:
71
- * 1. `messages` intlayer dictionary (dotted path navigation)
72
- * 2. `descriptor.message` or `options.message` (original source string)
73
- * 3. The raw `id` as fallback
103
+ * 1. `messages` intlayer dictionary (flat dotted key or nested path)
104
+ * 2. runtime fallback catalog loaded via `load()` / `loadAndActivate()`
105
+ * 3. `descriptor.message` or `options.message` (original source string)
106
+ * 4. The raw `id` as fallback
74
107
  */
75
108
  _(descriptor: MessageDescriptor): string;
76
109
  _(id: MessageId, values?: Values, options?: MessageOptions): string;
@@ -1 +1 @@
1
- {"version":3,"file":"I18nClass.d.ts","names":[],"sources":["../../src/I18nClass.ts"],"mappings":";;;;;KAkBK,MAAA,GAAS,MAAM;AAH0B;AAAA,KAMzC,eAAA,IAAmB,OAAe;;KAGlC,YAAA;EACH,MAAA;EACA,OAAA,GAAU,KAAA;IAAS,MAAA,EAAQ,MAAA;IAAQ,EAAA,EAAI,SAAS;EAAA;AAAA;AALX;AAAA,KASlC,SAAA;EACH,MAAA,GAAS,MAAA;EACT,OAAA,GAAU,OAAA;EACV,QAAA,GAAW,WAAA;EACX,OAAA,cAAqB,MAAA,UAAgB,EAAA;AAAA;;;;;;AARa;AAAA;;;cA8DvC,SAAA,SAAkB,YAAA,CAAa,YAAA;EAAA,QAClC,OAAA;EAAA,QACA,QAAA;;IAEM,MAAA;IAAe;EAAA,IAAW,SAAA;EAAA,IAMpC,MAAA;EAAA,IAIA,OAAA,IAAW,OAAA;EAAA,IAIX,QAAA,IAAY,QAAA;EA1EhB;;;EAwFA,mBAAA,CAAoB,SAAA,EAAW,eAAA;EAtF/B;;;EAmGA,IAAA,CAAK,YAAA,EAAc,MAAA,GAAS,WAAA,EAAa,SAAA,GAAY,QAAA;EAnGN;AAsDjD;;;EA0DE,eAAA;IACE,MAAA;IACA;EAAA;IAEA,MAAA,EAAQ,MAAA;IACR,OAAA,GAAU,OAAA;IACV,QAAA,EAAU,QAAA;EAAA;EA9CI;;;EAsDhB,QAAA,CAAS,MAAA,EAAQ,MAAA,EAAQ,OAAA,GAAU,OAAA;EA3BkB;;;;;;;;EAyCrD,CAAA,CAAE,UAAA,EAAY,iBAAA;EACd,CAAA,CAAE,EAAA,EAAI,SAAA,EAAW,MAAA,GAAS,MAAA,EAAQ,OAAA,GAAU,cAAA;EAAlB;;;EAqC1B,CAAA,GACE,cAAA,EAAgB,iBAAA,GAAoB,SAAA,EACpC,MAAA,GAAS,MAAA,EACT,OAAA,GAAU,cAAA;EADD;;;EAOX,IAAA,CACE,KAAA,YAAiB,IAAA,WACjB,MAAA,GAAS,IAAA,CAAK,qBAAA;EAawB;;;EAAxC,MAAA,CAAO,KAAA,mBAAwB,MAAA,GAAS,IAAA,CAAK,mBAAA;AAAA"}
1
+ {"version":3,"file":"I18nClass.d.ts","names":[],"sources":["../../src/I18nClass.ts"],"mappings":";;;;;KAwBK,MAAA,GAAS,MAAM;AAR0B;AAAA,KAWzC,eAAA,IAAmB,OAAe;;KAGlC,YAAA;EACH,MAAA;EACA,OAAA,GAAU,KAAA;IAAS,MAAA,EAAQ,MAAA;IAAQ,EAAA,EAAI,SAAS;EAAA;AAAA;AALX;AAAA,KASlC,SAAA;EACH,MAAA,GAAS,MAAA;EACT,OAAA,GAAU,OAAA;EACV,QAAA,GAAW,WAAA;EACX,OAAA,cAAqB,MAAA,UAAgB,EAAA;AAAA;;;;;;AARa;AAAA;;;cA8DvC,SAAA,SAAkB,YAAA,CAAa,YAAA;EAAA,QAClC,OAAA;EAAA,QACA,QAAA;EAzDc;;;;;;;;EAAA,QAkEd,SAAA;EAAA,QACA,mBAAA;;IAEM,MAAA;IAAe,OAAA;IAAS;EAAA,IAAY,SAAA;EAAA,IAO9C,MAAA;EAAA,IAIA,OAAA,IAAW,OAAA;EAAA,IAIX,QAAA,IAAY,QAAA;EA7BK;EAAA,QAgDb,kBAAA;EAhDkC;;;;EAAA,QAwDlC,gBAAA;EA/BO;;;EA0Cf,mBAAA,CAAoB,SAAA,EAAW,eAAA;EAkBJ;;;;;;;;EAA3B,IAAA,CAAK,WAAA,EAAa,MAAA,GAAS,WAAA,EAAa,QAAA,GAAW,QAAA;EAqChB;;;;EAhBnC,eAAA;IACE,MAAA;IACA,OAAA;IACA;EAAA;IAEA,MAAA,EAAQ,MAAA;IACR,OAAA,GAAU,OAAA;IACV,QAAA,GAAW,QAAA;EAAA;EA+FM;;;EAtFnB,QAAA,CAAS,MAAA,EAAQ,MAAA,EAAQ,OAAA,GAAU,OAAA;EA1HM;;;;;;;EAAA,QAuIjC,eAAA;;;;;;;;;;EAyBR,CAAA,CAAE,UAAA,EAAY,iBAAA;EACd,CAAA,CAAE,EAAA,EAAI,SAAA,EAAW,MAAA,GAAS,MAAA,EAAQ,OAAA,GAAU,cAAA;EApIxC;;;EAyKJ,CAAA,GACE,cAAA,EAAgB,iBAAA,GAAoB,SAAA,EACpC,MAAA,GAAS,MAAA,EACT,OAAA,GAAU,cAAA;EAtIZ;;;EA4IA,IAAA,CACE,KAAA,YAAiB,IAAA,WACjB,MAAA,GAAS,IAAA,CAAK,qBAAA;EA5HE;;;EAyIlB,MAAA,CAAO,KAAA,mBAAwB,MAAA,GAAS,IAAA,CAAK,mBAAA;AAAA"}
@@ -0,0 +1,60 @@
1
+ //#region src/linguiCatalog.d.ts
2
+ /**
3
+ * Helpers for reading lingui message catalogs.
4
+ *
5
+ * lingui catalogs are flat maps whose keys are dotted strings
6
+ * (`'results-table.bundleSize'`) rather than nested objects, and whose
7
+ * compiled (`.mjs`) values are token arrays rather than plain strings. These
8
+ * helpers bridge that format onto intlayer's ICU-based `resolveMessage`.
9
+ */
10
+ /**
11
+ * Reads a value from a catalog by id.
12
+ *
13
+ * lingui ids are flat keys that themselves contain dots
14
+ * (`'results-table.bundleSize'`), so the flat key is tried first. When that
15
+ * misses, the id is treated as a nested path (`'home.title'` →
16
+ * `catalog.home.title`) to also support genuinely nested intlayer
17
+ * dictionaries.
18
+ *
19
+ * @example
20
+ * navigateCatalog({ 'a.b': 'X' }, 'a.b'); // 'X' (flat key)
21
+ * navigateCatalog({ a: { b: 'X' } }, 'a.b'); // 'X' (nested path)
22
+ */
23
+ declare const navigateCatalog: (catalog: unknown, id: string) => unknown;
24
+ /**
25
+ * Returns the flat id→message map of a catalog, transparently unwrapping the
26
+ * lingui `{ messages: {…} }` wrapper when present.
27
+ *
28
+ * Compiled lingui catalogs (`.po`/`.mjs`/`.json`) nest the id→message map under
29
+ * a top-level `messages` key, and syncJSON-backed intlayer dictionaries
30
+ * preserve that wrapper (`{ messages: { 'hero.title': […] } }`). Centralized
31
+ * intlayer dictionaries instead store the map at the top level
32
+ * (`{ 'hero.title': '…' }`). Both shapes are supported.
33
+ */
34
+ declare const unwrapLinguiCatalog: (catalog: unknown) => Record<string, unknown>;
35
+ /**
36
+ * Reads a value from a catalog by id, accepting both the flat/nested shapes
37
+ * handled by {@link navigateCatalog} and the lingui `{ messages: {…} }` wrapper.
38
+ *
39
+ * The direct lookup is tried first so a centralized dictionary that genuinely
40
+ * stores content at the top level keeps working; only on a miss does it look
41
+ * inside the `messages` wrapper used by namespaced lingui catalogs.
42
+ */
43
+ declare const navigateLinguiCatalog: (catalog: unknown, id: string) => unknown;
44
+ /**
45
+ * Converts a compiled lingui message into an ICU MessageFormat string that
46
+ * `@intlayer/core`'s `resolveMessage(…, 'icu')` can interpolate.
47
+ *
48
+ * Plain strings (uncompiled `.json` catalogs) pass through unchanged; token
49
+ * arrays (compiled `.mjs` catalogs) are flattened, including nested
50
+ * plural/select blocks and `{name}` placeholders.
51
+ *
52
+ * @example
53
+ * linguiMessageToIcu(['Bundle Size']); // 'Bundle Size'
54
+ * linguiMessageToIcu(['Hello ', ['name']]); // 'Hello {name}'
55
+ * linguiMessageToIcu('Already ICU {count}'); // 'Already ICU {count}'
56
+ */
57
+ declare const linguiMessageToIcu: (compiled: unknown) => string;
58
+ //#endregion
59
+ export { linguiMessageToIcu, navigateCatalog, navigateLinguiCatalog, unwrapLinguiCatalog };
60
+ //# sourceMappingURL=linguiCatalog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"linguiCatalog.d.ts","names":[],"sources":["../../src/linguiCatalog.ts"],"mappings":";;AAsBA;;;;AAA4D;AAiC5D;;;;AAES;AAiBT;;;;AAEY;AAmEZ;;;;AAAoD;cAzHvC,eAAA,GAAmB,OAAA,WAAkB,EAAU;;;;;;;;;;;cAiC/C,mBAAA,GACX,OAAA,cACC,MAAM;;;;;;;;;cAiBI,qBAAA,GACX,OAAA,WACA,EAAU;;;;;;;;;;;;;;cAmEC,kBAAA,GAAsB,QAAiB"}
@@ -12,14 +12,19 @@ import { intlayer } from "vite-intlayer";
12
12
  * @example
13
13
  * ```ts
14
14
  * // vite.config.ts
15
- * import { linguiVitePlugin } from '@intlayer/lingui/plugin';
15
+ * import { lingui } from '@intlayer/lingui/plugin';
16
16
  *
17
17
  * export default defineConfig({
18
- * plugins: [linguiVitePlugin()],
18
+ * plugins: [lingui()],
19
19
  * });
20
20
  * ```
21
21
  */
22
+ declare const lingui: (options?: Parameters<typeof intlayer>[0]) => PluginOption[];
23
+ /**
24
+ * Backwards-compatible alias for {@link lingui}, matching the naming used by the
25
+ * other compat adapters' Vite plugins (e.g. `i18nextVitePlugin`).
26
+ */
22
27
  declare const linguiVitePlugin: (options?: Parameters<typeof intlayer>[0]) => PluginOption[];
23
28
  //#endregion
24
- export { linguiVitePlugin as default, linguiVitePlugin };
29
+ export { lingui as default, lingui, linguiVitePlugin };
25
30
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/plugin/index.ts"],"mappings":";;;;;;AAsCA;;;;;;;;;;;;;AAEe;;cAFF,gBAAA,GACX,OAAA,GAAU,UAAA,QAAkB,QAAA,SAC3B,YAAA"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/plugin/index.ts"],"mappings":";;;;;;AAiEA;;;;;;;;;;;;;AAEe;AA0Df;cA5Da,MAAA,GACX,OAAA,GAAU,UAAA,QAAkB,QAAA,SAC3B,YAAA;;;;;cA0DU,gBAAA,GAAgB,OAAA,GA3DjB,UAAA,QAAkB,QAAA,SAC3B,YAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"useLingui.d.ts","names":[],"sources":["../../src/useLingui.ts"],"mappings":";;;;;AAuBA;;;;AAiBC;;;;;;;;cAjBY,SAAA,QAAgB,WAiB5B"}
1
+ {"version":3,"file":"useLingui.d.ts","names":[],"sources":["../../src/useLingui.ts"],"mappings":";;;;;AAsBA;;;;AAiBC;;;;;;;;cAjBY,SAAA,QAAgB,WAiB5B"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intlayer/lingui",
3
- "version": "9.0.0-canary.1",
3
+ "version": "9.0.0-canary.11",
4
4
  "private": false,
5
5
  "description": "lingui API adapter for intlayer — drop-in compatibility layer",
6
6
  "keywords": [
@@ -34,17 +34,15 @@
34
34
  "exports": {
35
35
  ".": {
36
36
  "types": "./dist/types/index.d.ts",
37
- "require": "./dist/cjs/index.cjs",
38
37
  "import": "./dist/esm/index.mjs"
39
38
  },
40
39
  "./plugin": {
41
40
  "types": "./dist/types/plugin/index.d.ts",
42
- "require": "./dist/cjs/plugin/index.cjs",
43
41
  "import": "./dist/esm/plugin/index.mjs"
44
42
  },
45
43
  "./package.json": "./package.json"
46
44
  },
47
- "main": "dist/cjs/index.cjs",
45
+ "main": "dist/esm/index.mjs",
48
46
  "module": "dist/esm/index.mjs",
49
47
  "types": "dist/types/index.d.ts",
50
48
  "typesVersions": {
@@ -76,18 +74,18 @@
76
74
  "typecheck": "tsc --noEmit --project tsconfig.types.json"
77
75
  },
78
76
  "dependencies": {
79
- "@intlayer/chokidar": "9.0.0-canary.1",
80
- "@intlayer/config": "9.0.0-canary.1",
81
- "@intlayer/core": "9.0.0-canary.1",
82
- "@intlayer/dictionaries-entry": "9.0.0-canary.1",
83
- "@intlayer/types": "9.0.0-canary.1",
84
- "react-intlayer": "9.0.0-canary.1",
85
- "vite-intlayer": "9.0.0-canary.1"
77
+ "@intlayer/chokidar": "9.0.0-canary.11",
78
+ "@intlayer/config": "9.0.0-canary.11",
79
+ "@intlayer/core": "9.0.0-canary.11",
80
+ "@intlayer/dictionaries-entry": "9.0.0-canary.11",
81
+ "@intlayer/types": "9.0.0-canary.11",
82
+ "react-intlayer": "9.0.0-canary.11",
83
+ "vite-intlayer": "9.0.0-canary.11"
86
84
  },
87
85
  "devDependencies": {
88
86
  "@lingui/core": "^6.3.0",
89
87
  "@lingui/react": "^6.3.0",
90
- "@types/node": "25.9.3",
88
+ "@types/node": "25.9.4",
91
89
  "@types/react": "19.1.8",
92
90
  "@utils/ts-config": "1.0.4",
93
91
  "@utils/ts-config-types": "1.0.4",
@@ -95,7 +93,7 @@
95
93
  "rimraf": "6.1.3",
96
94
  "tsdown": "0.22.2",
97
95
  "typescript": "6.0.3",
98
- "vite": "8.0.16",
96
+ "vite": "8.1.0",
99
97
  "vitest": "4.1.9"
100
98
  },
101
99
  "peerDependencies": {