@randajan/say 1.0.2 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -34,21 +34,23 @@ var Say = class _Say extends Function {
34
34
  * @param {object} opts
35
35
  * @param {string[]} opts.langs
36
36
  * @param {Record<string, string[]>} opts.translations
37
- * @param {string[]} opts.defaultLang
37
+ * @param {string} opts.defaultLang
38
38
  * @param {Say|null} opts.parent
39
39
  */
40
40
  constructor({ defaultLang = null, langs = [], translations = {}, parent = null } = {}) {
41
41
  super();
42
+ const brothers = [];
42
43
  langs = langs ?? [];
43
44
  translations = translations ?? {};
44
45
  defaultLang = defaultLang ?? langs[0];
45
- const _say = (...a) => _say.say(...a);
46
+ const _say = (phraseId, lang) => _say.say(phraseId, lang);
46
47
  (0, import_props.solids)(_say, {
47
48
  defaultLang,
48
49
  langs,
49
50
  langIndex: makeLangIndex(langs),
50
51
  translations,
51
- parent
52
+ parent,
53
+ brothers
52
54
  });
53
55
  return Object.setPrototypeOf(_say, new.target.prototype);
54
56
  }
@@ -63,16 +65,45 @@ var Say = class _Say extends Function {
63
65
  }
64
66
  return arr[i];
65
67
  }
66
- say(phraseId, lang) {
67
- const effectiveLang = lang ?? this.defaultLang;
68
- const here = this._lookupHere(phraseId, effectiveLang);
69
- if (here != null) {
70
- return here;
68
+ _lookupBrothers(phraseId, lang, seen = /* @__PURE__ */ new WeakSet()) {
69
+ const { brothers } = this;
70
+ if (!brothers.length) {
71
+ return;
71
72
  }
72
- if (this.parent) {
73
- return this.parent.say(phraseId, effectiveLang);
73
+ for (const bro of brothers) {
74
+ const r = bro._lookup(phraseId, lang, false, seen);
75
+ if (r != null) {
76
+ return r;
77
+ }
74
78
  }
75
- throw new Error(`Phrase '${phraseId}' not found (lang '${effectiveLang}').`);
79
+ }
80
+ _lookupParent(phraseId, lang, seen = /* @__PURE__ */ new WeakSet()) {
81
+ const { parent } = this;
82
+ return parent?._lookup(phraseId, lang, false, seen);
83
+ }
84
+ _lookup(phraseId, lang, throwError = true, seen = /* @__PURE__ */ new WeakSet()) {
85
+ if (seen.has(this)) {
86
+ return;
87
+ } else {
88
+ seen.add(this);
89
+ }
90
+ const el = lang ?? this.defaultLang;
91
+ const vh = this._lookupHere(phraseId, el);
92
+ if (vh != null) {
93
+ return vh;
94
+ }
95
+ const vb = this._lookupBrothers(phraseId, el, seen);
96
+ if (vb != null) {
97
+ return vb;
98
+ }
99
+ const vp = this._lookupParent(phraseId, el, seen);
100
+ if (vp != null) {
101
+ return vp;
102
+ }
103
+ if (!throwError) {
104
+ return;
105
+ }
106
+ throw new Error(`Phrase '${phraseId}' not found (lang '${el}').`);
76
107
  }
77
108
  bindLang(lang) {
78
109
  return this.extend({ defaultLang: lang });
@@ -83,23 +114,29 @@ var Say = class _Say extends Function {
83
114
  } else if (!defaultLang) {
84
115
  defaultLang = this.defaultLang;
85
116
  }
86
- console.log({ defaultLang, langs, translations }, { ...this });
87
117
  return new _Say({ defaultLang, langs, translations, parent: this });
88
118
  }
89
- has(phraseId, lang) {
90
- try {
91
- this.say(phraseId, lang);
92
- return true;
93
- } catch {
94
- return false;
119
+ append(brother) {
120
+ if (brother === this) {
121
+ throw new Error(`Say.append(children) can't accept itself`);
122
+ }
123
+ if (brother === this.parent) {
124
+ throw new Error(`Say.append(children) can't accept own parent`);
95
125
  }
126
+ if (!(brother instanceof _Say)) {
127
+ throw new Error(`Say.append(children) must by instanceof Say()`);
128
+ }
129
+ this.brothers.push(brother);
130
+ return this;
131
+ }
132
+ has(phraseId, lang) {
133
+ return this._lookup(phraseId, lang, false) != null;
96
134
  }
97
135
  sayOr(phraseId, fallback, lang) {
98
- try {
99
- return this.say(phraseId, lang);
100
- } catch {
101
- return fallback;
102
- }
136
+ return this._lookup(phraseId, lang, false) ?? fallback;
137
+ }
138
+ say(phraseId, lang) {
139
+ return this.sayOr(phraseId, `{${phraseId}}`, lang);
103
140
  }
104
141
  };
105
142
  var index_default = Say;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/index.js"],
4
- "sourcesContent": ["import { solids } from \"@randajan/props\";\n\nconst makeLangIndex = (langs = []) => {\n const idx = {};\n for (let i = 0; i < langs.length; i++) idx[langs[i]] = i;\n return idx;\n};\n\nexport class Say extends Function {\n /**\n * @param {object} opts\n * @param {string[]} opts.langs\n * @param {Record<string, string[]>} opts.translations\n * @param {string[]} opts.defaultLang\n * @param {Say|null} opts.parent\n */\n constructor({ defaultLang = null, langs = [], translations = {}, parent = null } = {}) {\n super();\n\n langs = langs ?? [];\n translations = translations ?? {};\n defaultLang = defaultLang ?? langs[0];\n\n // Important: keep prototype = Say so instance methods work\n const _say = (...a) => _say.say(...a);\n\n solids(_say, {\n defaultLang,\n langs,\n langIndex: makeLangIndex(langs),\n translations,\n parent\n });\n\n return Object.setPrototypeOf(_say, new.target.prototype);\n }\n\n _lookupHere(phraseId, lang) {\n const i = this.langIndex[lang];\n if (i == null) { return; }\n const arr = this.translations?.[phraseId];\n if (!Array.isArray(arr)) { return; }\n return arr[i];\n }\n\n say(phraseId, lang) {\n const effectiveLang = lang ?? this.defaultLang;\n const here = this._lookupHere(phraseId, effectiveLang);\n\n if (here != null) { return here; }\n if (this.parent) { return this.parent.say(phraseId, effectiveLang); }\n\n throw new Error(`Phrase '${phraseId}' not found (lang '${effectiveLang}').`);\n }\n\n bindLang(lang) {\n return this.extend({defaultLang:lang});\n }\n\n extend({ defaultLang, langs, translations} = {}) {\n if (!langs) { langs = this.langs; }\n else if (!defaultLang) { defaultLang = this.defaultLang; }\n\n console.log({ defaultLang, langs, translations }, {...this});\n return new Say({defaultLang, langs, translations, parent:this});\n }\n\n has(phraseId, lang) {\n try {\n this.say(phraseId, lang);\n return true;\n } catch {\n return false;\n }\n }\n\n sayOr(phraseId, fallback, lang) {\n try {\n return this.say(phraseId, lang);\n } catch {\n return fallback;\n }\n }\n}\n\nexport default Say;\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAuB;AAEvB,IAAM,gBAAgB,CAAC,QAAQ,CAAC,MAAM;AAClC,QAAM,MAAM,CAAC;AACb,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,IAAK,KAAI,MAAM,CAAC,CAAC,IAAI;AACvD,SAAO;AACX;AAEO,IAAM,MAAN,MAAM,aAAY,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQ9B,YAAY,EAAE,cAAc,MAAM,QAAQ,CAAC,GAAG,eAAe,CAAC,GAAG,SAAS,KAAK,IAAI,CAAC,GAAG;AACnF,UAAM;AAEN,YAAQ,SAAS,CAAC;AAClB,mBAAe,gBAAgB,CAAC;AAChC,kBAAc,eAAe,MAAM,CAAC;AAGpC,UAAM,OAAO,IAAI,MAAM,KAAK,IAAI,GAAG,CAAC;AAEpC,6BAAO,MAAM;AAAA,MACT;AAAA,MACA;AAAA,MACA,WAAW,cAAc,KAAK;AAAA,MAC9B;AAAA,MACA;AAAA,IACJ,CAAC;AAED,WAAO,OAAO,eAAe,MAAM,WAAW,SAAS;AAAA,EAC3D;AAAA,EAEA,YAAY,UAAU,MAAM;AACxB,UAAM,IAAI,KAAK,UAAU,IAAI;AAC7B,QAAI,KAAK,MAAM;AAAE;AAAA,IAAQ;AACzB,UAAM,MAAM,KAAK,eAAe,QAAQ;AACxC,QAAI,CAAC,MAAM,QAAQ,GAAG,GAAG;AAAE;AAAA,IAAQ;AACnC,WAAO,IAAI,CAAC;AAAA,EAChB;AAAA,EAEA,IAAI,UAAU,MAAM;AAChB,UAAM,gBAAgB,QAAQ,KAAK;AACnC,UAAM,OAAO,KAAK,YAAY,UAAU,aAAa;AAErD,QAAI,QAAQ,MAAM;AAAE,aAAO;AAAA,IAAM;AACjC,QAAI,KAAK,QAAQ;AAAE,aAAO,KAAK,OAAO,IAAI,UAAU,aAAa;AAAA,IAAG;AAEpE,UAAM,IAAI,MAAM,WAAW,QAAQ,sBAAsB,aAAa,KAAK;AAAA,EAC/E;AAAA,EAEA,SAAS,MAAM;AACX,WAAO,KAAK,OAAO,EAAC,aAAY,KAAI,CAAC;AAAA,EACzC;AAAA,EAEA,OAAO,EAAE,aAAa,OAAO,aAAY,IAAI,CAAC,GAAG;AAC7C,QAAI,CAAC,OAAO;AAAE,cAAQ,KAAK;AAAA,IAAO,WACzB,CAAC,aAAa;AAAE,oBAAc,KAAK;AAAA,IAAa;AAEzD,YAAQ,IAAI,EAAE,aAAa,OAAO,aAAa,GAAG,EAAC,GAAG,KAAI,CAAC;AAC3D,WAAO,IAAI,KAAI,EAAC,aAAa,OAAO,cAAc,QAAO,KAAI,CAAC;AAAA,EAClE;AAAA,EAEA,IAAI,UAAU,MAAM;AAChB,QAAI;AACA,WAAK,IAAI,UAAU,IAAI;AACvB,aAAO;AAAA,IACX,QAAQ;AACJ,aAAO;AAAA,IACX;AAAA,EACJ;AAAA,EAEA,MAAM,UAAU,UAAU,MAAM;AAC5B,QAAI;AACA,aAAO,KAAK,IAAI,UAAU,IAAI;AAAA,IAClC,QAAQ;AACJ,aAAO;AAAA,IACX;AAAA,EACJ;AACJ;AAEA,IAAO,gBAAQ;",
4
+ "sourcesContent": ["import { solids } from \"@randajan/props\";\n\nconst makeLangIndex = (langs = []) => {\n const idx = {};\n for (let i = 0; i < langs.length; i++) idx[langs[i]] = i;\n return idx;\n};\n\nexport class Say extends Function {\n /**\n * @param {object} opts\n * @param {string[]} opts.langs\n * @param {Record<string, string[]>} opts.translations\n * @param {string} opts.defaultLang\n * @param {Say|null} opts.parent\n */\n constructor({ defaultLang = null, langs = [], translations = {}, parent = null } = {}) {\n super();\n\n const brothers = [];\n langs = langs ?? [];\n translations = translations ?? {};\n defaultLang = defaultLang ?? langs[0];\n\n // Important: keep prototype = Say so instance methods work\n const _say = (phraseId, lang) => _say.say(phraseId, lang);\n\n solids(_say, {\n defaultLang,\n langs,\n langIndex: makeLangIndex(langs),\n translations,\n parent,\n brothers\n });\n\n return Object.setPrototypeOf(_say, new.target.prototype);\n }\n\n _lookupHere(phraseId, lang) {\n const i = this.langIndex[lang];\n if (i == null) { return; }\n const arr = this.translations?.[phraseId];\n if (!Array.isArray(arr)) { return; }\n return arr[i];\n }\n\n _lookupBrothers(phraseId, lang, seen=new WeakSet()) {\n const { brothers } = this;\n if (!brothers.length) { return; }\n for (const bro of brothers) {\n const r = bro._lookup(phraseId, lang, false, seen);\n if (r != null) { return r; }\n }\n }\n\n _lookupParent(phraseId, lang, seen=new WeakSet()) {\n const { parent } = this;\n return parent?._lookup(phraseId, lang, false, seen);\n }\n \n _lookup(phraseId, lang, throwError=true, seen=new WeakSet()) {\n if (seen.has(this)) { return; } else { seen.add(this); }\n\n const el = lang ?? this.defaultLang;\n const vh = this._lookupHere(phraseId, el);\n if (vh != null) { return vh; }\n\n const vb = this._lookupBrothers(phraseId, el, seen);\n if (vb != null) { return vb; }\n\n const vp = this._lookupParent(phraseId, el, seen);\n if (vp != null) { return vp; }\n\n if (!throwError) { return; }\n throw new Error(`Phrase '${phraseId}' not found (lang '${el}').`);\n }\n\n bindLang(lang) {\n return this.extend({defaultLang:lang});\n }\n\n extend({ defaultLang, langs, translations} = {}) {\n if (!langs) { langs = this.langs; }\n else if (!defaultLang) { defaultLang = this.defaultLang; }\n return new Say({defaultLang, langs, translations, parent:this});\n }\n \n append(brother) {\n if (brother === this) {\n throw new Error(`Say.append(children) can't accept itself`);\n }\n if (brother === this.parent) {\n throw new Error(`Say.append(children) can't accept own parent`);\n }\n if (!(brother instanceof Say)) {\n throw new Error(`Say.append(children) must by instanceof Say()`);\n }\n this.brothers.push(brother);\n return this;\n }\n\n has(phraseId, lang) {\n return this._lookup(phraseId, lang, false) != null;\n }\n\n sayOr(phraseId, fallback, lang) {\n return this._lookup(phraseId, lang, false) ?? fallback;\n }\n\n say(phraseId, lang) {\n return this.sayOr(phraseId, `{${phraseId}}`, lang);\n }\n}\n\nexport default Say;\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -10,21 +10,23 @@ var Say = class _Say extends Function {
10
10
  * @param {object} opts
11
11
  * @param {string[]} opts.langs
12
12
  * @param {Record<string, string[]>} opts.translations
13
- * @param {string[]} opts.defaultLang
13
+ * @param {string} opts.defaultLang
14
14
  * @param {Say|null} opts.parent
15
15
  */
16
16
  constructor({ defaultLang = null, langs = [], translations = {}, parent = null } = {}) {
17
17
  super();
18
+ const brothers = [];
18
19
  langs = langs ?? [];
19
20
  translations = translations ?? {};
20
21
  defaultLang = defaultLang ?? langs[0];
21
- const _say = (...a) => _say.say(...a);
22
+ const _say = (phraseId, lang) => _say.say(phraseId, lang);
22
23
  solids(_say, {
23
24
  defaultLang,
24
25
  langs,
25
26
  langIndex: makeLangIndex(langs),
26
27
  translations,
27
- parent
28
+ parent,
29
+ brothers
28
30
  });
29
31
  return Object.setPrototypeOf(_say, new.target.prototype);
30
32
  }
@@ -39,16 +41,45 @@ var Say = class _Say extends Function {
39
41
  }
40
42
  return arr[i];
41
43
  }
42
- say(phraseId, lang) {
43
- const effectiveLang = lang ?? this.defaultLang;
44
- const here = this._lookupHere(phraseId, effectiveLang);
45
- if (here != null) {
46
- return here;
44
+ _lookupBrothers(phraseId, lang, seen = /* @__PURE__ */ new WeakSet()) {
45
+ const { brothers } = this;
46
+ if (!brothers.length) {
47
+ return;
47
48
  }
48
- if (this.parent) {
49
- return this.parent.say(phraseId, effectiveLang);
49
+ for (const bro of brothers) {
50
+ const r = bro._lookup(phraseId, lang, false, seen);
51
+ if (r != null) {
52
+ return r;
53
+ }
50
54
  }
51
- throw new Error(`Phrase '${phraseId}' not found (lang '${effectiveLang}').`);
55
+ }
56
+ _lookupParent(phraseId, lang, seen = /* @__PURE__ */ new WeakSet()) {
57
+ const { parent } = this;
58
+ return parent?._lookup(phraseId, lang, false, seen);
59
+ }
60
+ _lookup(phraseId, lang, throwError = true, seen = /* @__PURE__ */ new WeakSet()) {
61
+ if (seen.has(this)) {
62
+ return;
63
+ } else {
64
+ seen.add(this);
65
+ }
66
+ const el = lang ?? this.defaultLang;
67
+ const vh = this._lookupHere(phraseId, el);
68
+ if (vh != null) {
69
+ return vh;
70
+ }
71
+ const vb = this._lookupBrothers(phraseId, el, seen);
72
+ if (vb != null) {
73
+ return vb;
74
+ }
75
+ const vp = this._lookupParent(phraseId, el, seen);
76
+ if (vp != null) {
77
+ return vp;
78
+ }
79
+ if (!throwError) {
80
+ return;
81
+ }
82
+ throw new Error(`Phrase '${phraseId}' not found (lang '${el}').`);
52
83
  }
53
84
  bindLang(lang) {
54
85
  return this.extend({ defaultLang: lang });
@@ -59,23 +90,29 @@ var Say = class _Say extends Function {
59
90
  } else if (!defaultLang) {
60
91
  defaultLang = this.defaultLang;
61
92
  }
62
- console.log({ defaultLang, langs, translations }, { ...this });
63
93
  return new _Say({ defaultLang, langs, translations, parent: this });
64
94
  }
65
- has(phraseId, lang) {
66
- try {
67
- this.say(phraseId, lang);
68
- return true;
69
- } catch {
70
- return false;
95
+ append(brother) {
96
+ if (brother === this) {
97
+ throw new Error(`Say.append(children) can't accept itself`);
98
+ }
99
+ if (brother === this.parent) {
100
+ throw new Error(`Say.append(children) can't accept own parent`);
71
101
  }
102
+ if (!(brother instanceof _Say)) {
103
+ throw new Error(`Say.append(children) must by instanceof Say()`);
104
+ }
105
+ this.brothers.push(brother);
106
+ return this;
107
+ }
108
+ has(phraseId, lang) {
109
+ return this._lookup(phraseId, lang, false) != null;
72
110
  }
73
111
  sayOr(phraseId, fallback, lang) {
74
- try {
75
- return this.say(phraseId, lang);
76
- } catch {
77
- return fallback;
78
- }
112
+ return this._lookup(phraseId, lang, false) ?? fallback;
113
+ }
114
+ say(phraseId, lang) {
115
+ return this.sayOr(phraseId, `{${phraseId}}`, lang);
79
116
  }
80
117
  };
81
118
  var index_default = Say;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/index.js"],
4
- "sourcesContent": ["import { solids } from \"@randajan/props\";\n\nconst makeLangIndex = (langs = []) => {\n const idx = {};\n for (let i = 0; i < langs.length; i++) idx[langs[i]] = i;\n return idx;\n};\n\nexport class Say extends Function {\n /**\n * @param {object} opts\n * @param {string[]} opts.langs\n * @param {Record<string, string[]>} opts.translations\n * @param {string[]} opts.defaultLang\n * @param {Say|null} opts.parent\n */\n constructor({ defaultLang = null, langs = [], translations = {}, parent = null } = {}) {\n super();\n\n langs = langs ?? [];\n translations = translations ?? {};\n defaultLang = defaultLang ?? langs[0];\n\n // Important: keep prototype = Say so instance methods work\n const _say = (...a) => _say.say(...a);\n\n solids(_say, {\n defaultLang,\n langs,\n langIndex: makeLangIndex(langs),\n translations,\n parent\n });\n\n return Object.setPrototypeOf(_say, new.target.prototype);\n }\n\n _lookupHere(phraseId, lang) {\n const i = this.langIndex[lang];\n if (i == null) { return; }\n const arr = this.translations?.[phraseId];\n if (!Array.isArray(arr)) { return; }\n return arr[i];\n }\n\n say(phraseId, lang) {\n const effectiveLang = lang ?? this.defaultLang;\n const here = this._lookupHere(phraseId, effectiveLang);\n\n if (here != null) { return here; }\n if (this.parent) { return this.parent.say(phraseId, effectiveLang); }\n\n throw new Error(`Phrase '${phraseId}' not found (lang '${effectiveLang}').`);\n }\n\n bindLang(lang) {\n return this.extend({defaultLang:lang});\n }\n\n extend({ defaultLang, langs, translations} = {}) {\n if (!langs) { langs = this.langs; }\n else if (!defaultLang) { defaultLang = this.defaultLang; }\n\n console.log({ defaultLang, langs, translations }, {...this});\n return new Say({defaultLang, langs, translations, parent:this});\n }\n\n has(phraseId, lang) {\n try {\n this.say(phraseId, lang);\n return true;\n } catch {\n return false;\n }\n }\n\n sayOr(phraseId, fallback, lang) {\n try {\n return this.say(phraseId, lang);\n } catch {\n return fallback;\n }\n }\n}\n\nexport default Say;\n"],
5
- "mappings": ";AAAA,SAAS,cAAc;AAEvB,IAAM,gBAAgB,CAAC,QAAQ,CAAC,MAAM;AAClC,QAAM,MAAM,CAAC;AACb,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,IAAK,KAAI,MAAM,CAAC,CAAC,IAAI;AACvD,SAAO;AACX;AAEO,IAAM,MAAN,MAAM,aAAY,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQ9B,YAAY,EAAE,cAAc,MAAM,QAAQ,CAAC,GAAG,eAAe,CAAC,GAAG,SAAS,KAAK,IAAI,CAAC,GAAG;AACnF,UAAM;AAEN,YAAQ,SAAS,CAAC;AAClB,mBAAe,gBAAgB,CAAC;AAChC,kBAAc,eAAe,MAAM,CAAC;AAGpC,UAAM,OAAO,IAAI,MAAM,KAAK,IAAI,GAAG,CAAC;AAEpC,WAAO,MAAM;AAAA,MACT;AAAA,MACA;AAAA,MACA,WAAW,cAAc,KAAK;AAAA,MAC9B;AAAA,MACA;AAAA,IACJ,CAAC;AAED,WAAO,OAAO,eAAe,MAAM,WAAW,SAAS;AAAA,EAC3D;AAAA,EAEA,YAAY,UAAU,MAAM;AACxB,UAAM,IAAI,KAAK,UAAU,IAAI;AAC7B,QAAI,KAAK,MAAM;AAAE;AAAA,IAAQ;AACzB,UAAM,MAAM,KAAK,eAAe,QAAQ;AACxC,QAAI,CAAC,MAAM,QAAQ,GAAG,GAAG;AAAE;AAAA,IAAQ;AACnC,WAAO,IAAI,CAAC;AAAA,EAChB;AAAA,EAEA,IAAI,UAAU,MAAM;AAChB,UAAM,gBAAgB,QAAQ,KAAK;AACnC,UAAM,OAAO,KAAK,YAAY,UAAU,aAAa;AAErD,QAAI,QAAQ,MAAM;AAAE,aAAO;AAAA,IAAM;AACjC,QAAI,KAAK,QAAQ;AAAE,aAAO,KAAK,OAAO,IAAI,UAAU,aAAa;AAAA,IAAG;AAEpE,UAAM,IAAI,MAAM,WAAW,QAAQ,sBAAsB,aAAa,KAAK;AAAA,EAC/E;AAAA,EAEA,SAAS,MAAM;AACX,WAAO,KAAK,OAAO,EAAC,aAAY,KAAI,CAAC;AAAA,EACzC;AAAA,EAEA,OAAO,EAAE,aAAa,OAAO,aAAY,IAAI,CAAC,GAAG;AAC7C,QAAI,CAAC,OAAO;AAAE,cAAQ,KAAK;AAAA,IAAO,WACzB,CAAC,aAAa;AAAE,oBAAc,KAAK;AAAA,IAAa;AAEzD,YAAQ,IAAI,EAAE,aAAa,OAAO,aAAa,GAAG,EAAC,GAAG,KAAI,CAAC;AAC3D,WAAO,IAAI,KAAI,EAAC,aAAa,OAAO,cAAc,QAAO,KAAI,CAAC;AAAA,EAClE;AAAA,EAEA,IAAI,UAAU,MAAM;AAChB,QAAI;AACA,WAAK,IAAI,UAAU,IAAI;AACvB,aAAO;AAAA,IACX,QAAQ;AACJ,aAAO;AAAA,IACX;AAAA,EACJ;AAAA,EAEA,MAAM,UAAU,UAAU,MAAM;AAC5B,QAAI;AACA,aAAO,KAAK,IAAI,UAAU,IAAI;AAAA,IAClC,QAAQ;AACJ,aAAO;AAAA,IACX;AAAA,EACJ;AACJ;AAEA,IAAO,gBAAQ;",
4
+ "sourcesContent": ["import { solids } from \"@randajan/props\";\n\nconst makeLangIndex = (langs = []) => {\n const idx = {};\n for (let i = 0; i < langs.length; i++) idx[langs[i]] = i;\n return idx;\n};\n\nexport class Say extends Function {\n /**\n * @param {object} opts\n * @param {string[]} opts.langs\n * @param {Record<string, string[]>} opts.translations\n * @param {string} opts.defaultLang\n * @param {Say|null} opts.parent\n */\n constructor({ defaultLang = null, langs = [], translations = {}, parent = null } = {}) {\n super();\n\n const brothers = [];\n langs = langs ?? [];\n translations = translations ?? {};\n defaultLang = defaultLang ?? langs[0];\n\n // Important: keep prototype = Say so instance methods work\n const _say = (phraseId, lang) => _say.say(phraseId, lang);\n\n solids(_say, {\n defaultLang,\n langs,\n langIndex: makeLangIndex(langs),\n translations,\n parent,\n brothers\n });\n\n return Object.setPrototypeOf(_say, new.target.prototype);\n }\n\n _lookupHere(phraseId, lang) {\n const i = this.langIndex[lang];\n if (i == null) { return; }\n const arr = this.translations?.[phraseId];\n if (!Array.isArray(arr)) { return; }\n return arr[i];\n }\n\n _lookupBrothers(phraseId, lang, seen=new WeakSet()) {\n const { brothers } = this;\n if (!brothers.length) { return; }\n for (const bro of brothers) {\n const r = bro._lookup(phraseId, lang, false, seen);\n if (r != null) { return r; }\n }\n }\n\n _lookupParent(phraseId, lang, seen=new WeakSet()) {\n const { parent } = this;\n return parent?._lookup(phraseId, lang, false, seen);\n }\n \n _lookup(phraseId, lang, throwError=true, seen=new WeakSet()) {\n if (seen.has(this)) { return; } else { seen.add(this); }\n\n const el = lang ?? this.defaultLang;\n const vh = this._lookupHere(phraseId, el);\n if (vh != null) { return vh; }\n\n const vb = this._lookupBrothers(phraseId, el, seen);\n if (vb != null) { return vb; }\n\n const vp = this._lookupParent(phraseId, el, seen);\n if (vp != null) { return vp; }\n\n if (!throwError) { return; }\n throw new Error(`Phrase '${phraseId}' not found (lang '${el}').`);\n }\n\n bindLang(lang) {\n return this.extend({defaultLang:lang});\n }\n\n extend({ defaultLang, langs, translations} = {}) {\n if (!langs) { langs = this.langs; }\n else if (!defaultLang) { defaultLang = this.defaultLang; }\n return new Say({defaultLang, langs, translations, parent:this});\n }\n \n append(brother) {\n if (brother === this) {\n throw new Error(`Say.append(children) can't accept itself`);\n }\n if (brother === this.parent) {\n throw new Error(`Say.append(children) can't accept own parent`);\n }\n if (!(brother instanceof Say)) {\n throw new Error(`Say.append(children) must by instanceof Say()`);\n }\n this.brothers.push(brother);\n return this;\n }\n\n has(phraseId, lang) {\n return this._lookup(phraseId, lang, false) != null;\n }\n\n sayOr(phraseId, fallback, lang) {\n return this._lookup(phraseId, lang, false) ?? fallback;\n }\n\n say(phraseId, lang) {\n return this.sayOr(phraseId, `{${phraseId}}`, lang);\n }\n}\n\nexport default Say;\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@randajan/say",
3
- "version": "1.0.2",
3
+ "version": "1.1.0",
4
4
  "description": "Tiny, chainable phrase lookup helper for simple localization with fallbacks.",
5
5
  "repository": "randajan/say",
6
6
  "type": "module",