langsys-js-typescript 0.2.3 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/README.md +10 -10
- package/dist/index.d.mts +36 -4
- package/dist/index.d.ts +36 -4
- package/dist/index.js +80 -41
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +80 -42
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
import { IntlMessageFormat } from 'intl-messageformat';
|
|
2
2
|
|
|
3
|
+
// src/locale.ts
|
|
4
|
+
function canonicalizeLocale(locale) {
|
|
5
|
+
if (!locale || typeof locale !== "string") return locale;
|
|
6
|
+
const cleaned = locale.trim().replace(/_/g, "-");
|
|
7
|
+
try {
|
|
8
|
+
const [canonical] = Intl.getCanonicalLocales(cleaned);
|
|
9
|
+
return canonical ?? cleaned;
|
|
10
|
+
} catch {
|
|
11
|
+
return cleaned.split("-").map((part, i) => {
|
|
12
|
+
if (i === 0) return part.toLowerCase();
|
|
13
|
+
if (part.length === 4) return part[0].toUpperCase() + part.slice(1).toLowerCase();
|
|
14
|
+
if (part.length === 2 || /^\d{3}$/.test(part)) return part.toUpperCase();
|
|
15
|
+
return part.toLowerCase();
|
|
16
|
+
}).join("-");
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function maximizedLangScript(locale) {
|
|
20
|
+
try {
|
|
21
|
+
const max = new Intl.Locale(canonicalizeLocale(locale)).maximize();
|
|
22
|
+
return `${max.language}-${max.script ?? ""}`;
|
|
23
|
+
} catch {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
3
28
|
// src/signal.ts
|
|
4
29
|
function createSignal(initial) {
|
|
5
30
|
let value = initial;
|
|
@@ -91,7 +116,7 @@ var LangsysAppAPIClass = class {
|
|
|
91
116
|
return await this.get("authorize-project/[projectid]");
|
|
92
117
|
}
|
|
93
118
|
async getTranslations(locale) {
|
|
94
|
-
return await this.get("translations", { project_id: this.config.projectid, locale });
|
|
119
|
+
return await this.get("translations", { project_id: this.config.projectid, locale: canonicalizeLocale(locale) });
|
|
95
120
|
}
|
|
96
121
|
/**
|
|
97
122
|
* Register phrases and/or content blocks via the current unified endpoint.
|
|
@@ -237,24 +262,37 @@ var config = {
|
|
|
237
262
|
function isICU(template) {
|
|
238
263
|
return ICU_PATTERN.test(template);
|
|
239
264
|
}
|
|
240
|
-
var ICU_PATTERN = /\{[^{}]+,\s*(plural|select|selectordinal|number|date|time)\s
|
|
265
|
+
var ICU_PATTERN = /\{[^{}]+,\s*(plural|select|selectordinal|number|date|time)\s*[,}]/;
|
|
241
266
|
function interpolate(template, params, locale) {
|
|
242
267
|
if (isICU(template)) {
|
|
243
268
|
try {
|
|
244
|
-
return new IntlMessageFormat(template, locale
|
|
269
|
+
return new IntlMessageFormat(template, locale || "en").format(params);
|
|
245
270
|
} catch {
|
|
246
|
-
return simpleInterpolate(template, params);
|
|
271
|
+
return simpleInterpolate(template, params, locale);
|
|
247
272
|
}
|
|
248
273
|
}
|
|
249
|
-
return simpleInterpolate(template, params);
|
|
274
|
+
return simpleInterpolate(template, params, locale);
|
|
250
275
|
}
|
|
251
|
-
function simpleInterpolate(template, params) {
|
|
276
|
+
function simpleInterpolate(template, params, locale) {
|
|
252
277
|
return template.replace(/\{([^{},]+)\}/g, (match, rawKey) => {
|
|
253
278
|
const key = rawKey.trim();
|
|
254
279
|
if (!(key in params)) return match;
|
|
255
280
|
const value = params[key];
|
|
256
281
|
if (value === void 0 || value === null) return match;
|
|
257
|
-
if (value instanceof Date)
|
|
282
|
+
if (value instanceof Date) {
|
|
283
|
+
try {
|
|
284
|
+
return new Intl.DateTimeFormat(locale || "en", { dateStyle: "medium" }).format(value);
|
|
285
|
+
} catch {
|
|
286
|
+
return value.toISOString();
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
if (typeof value === "number" || typeof value === "bigint") {
|
|
290
|
+
try {
|
|
291
|
+
return new Intl.NumberFormat(locale || "en").format(value);
|
|
292
|
+
} catch {
|
|
293
|
+
return String(value);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
258
296
|
return String(value);
|
|
259
297
|
});
|
|
260
298
|
}
|
|
@@ -280,7 +318,7 @@ var Translations = class {
|
|
|
280
318
|
});
|
|
281
319
|
this.config = config2;
|
|
282
320
|
this.debug = new Logger(!!config2.debug);
|
|
283
|
-
this.locale = config2.baseLocale || "";
|
|
321
|
+
this.locale = canonicalizeLocale(config2.baseLocale || "");
|
|
284
322
|
this.tSignal = createSignal(this.buildTFn());
|
|
285
323
|
sTranslations.subscribe(() => this.tSignal.set(this.buildTFn()));
|
|
286
324
|
currentlyLoadedLocale.subscribe(() => this.tSignal.set(this.buildTFn()));
|
|
@@ -421,7 +459,7 @@ var Translations = class {
|
|
|
421
459
|
this.config = config2;
|
|
422
460
|
if (!this.config.projectid || !this.config.key) return;
|
|
423
461
|
if (this.config.debug) this.debug.debugEnabled = this.config.debug;
|
|
424
|
-
if (config2.baseLocale) this.locale = config2.baseLocale;
|
|
462
|
+
if (config2.baseLocale) this.locale = canonicalizeLocale(config2.baseLocale);
|
|
425
463
|
this.debug.log("TRANSLATION SETUP INITIATED", this.config);
|
|
426
464
|
if (typeof window !== "undefined") {
|
|
427
465
|
if (this.isFirstClientRun && this.missingTokens.length > 0) {
|
|
@@ -449,6 +487,7 @@ var Translations = class {
|
|
|
449
487
|
*/
|
|
450
488
|
markLoaded(locale) {
|
|
451
489
|
if (!locale) return;
|
|
490
|
+
locale = canonicalizeLocale(locale);
|
|
452
491
|
this.locale = locale;
|
|
453
492
|
this.lastLoaded[locale] = (/* @__PURE__ */ new Date()).getTime() / 1e3;
|
|
454
493
|
this.readyResolve();
|
|
@@ -460,6 +499,7 @@ var Translations = class {
|
|
|
460
499
|
async change(locale, force = false, skipFetch = false) {
|
|
461
500
|
if (!locale) return false;
|
|
462
501
|
if (!this.config.projectid || !this.config.key) return false;
|
|
502
|
+
locale = canonicalizeLocale(locale);
|
|
463
503
|
if (skipFetch) {
|
|
464
504
|
this.debug.log("Using pre-fetched translations for locale", locale);
|
|
465
505
|
this.locale = locale;
|
|
@@ -628,11 +668,11 @@ var LangsysAppClass = class {
|
|
|
628
668
|
}
|
|
629
669
|
if (!UserLocaleStore?.subscribe || typeof UserLocaleStore.get !== "function") {
|
|
630
670
|
this.debug.error(
|
|
631
|
-
"LangsysApp.init missing UserLocaleStore \u2014 pass any object satisfying LocaleSource (get + subscribe). createSignal('en-
|
|
671
|
+
"LangsysApp.init missing UserLocaleStore \u2014 pass any object satisfying LocaleSource (get + subscribe). createSignal('en-US') works if you have nothing of your own."
|
|
632
672
|
);
|
|
633
673
|
return { status: false, errors: ["Missing UserLocaleStore"] };
|
|
634
674
|
}
|
|
635
|
-
baseLocale = baseLocale
|
|
675
|
+
baseLocale = canonicalizeLocale(baseLocale);
|
|
636
676
|
if (!emulateFailureToLoad) {
|
|
637
677
|
this.config = {
|
|
638
678
|
projectid,
|
|
@@ -671,8 +711,9 @@ var LangsysAppClass = class {
|
|
|
671
711
|
if (initialTranslations2 && initialTranslationsLocale) {
|
|
672
712
|
this.Translations.markLoaded(initialTranslationsLocale);
|
|
673
713
|
}
|
|
674
|
-
this.config.sUserLocale.subscribe((
|
|
714
|
+
this.config.sUserLocale.subscribe((rawLocale) => {
|
|
675
715
|
if (!validateResponse.status) return;
|
|
716
|
+
const locale = canonicalizeLocale(rawLocale);
|
|
676
717
|
this.getLocalesData(locale);
|
|
677
718
|
this.debug.log("SUBSCRIBING TO USER LOCALE STORE");
|
|
678
719
|
this.translationsLoadingPromise = this.Translations.change(locale, false);
|
|
@@ -680,7 +721,7 @@ var LangsysAppClass = class {
|
|
|
680
721
|
return validateResponse;
|
|
681
722
|
}
|
|
682
723
|
async getDialCodes(inLocale) {
|
|
683
|
-
const locale =
|
|
724
|
+
const locale = this.resolveLocale(inLocale);
|
|
684
725
|
if (this.dialCodes.length && this.dialCodesLocale === locale) return this.dialCodes;
|
|
685
726
|
const route = `countries/dial-codes/${locale}`;
|
|
686
727
|
const response = await LangsysAppAPI.get(route);
|
|
@@ -693,7 +734,7 @@ var LangsysAppClass = class {
|
|
|
693
734
|
return this.dialCodes;
|
|
694
735
|
}
|
|
695
736
|
async getCurrencies(inLocale) {
|
|
696
|
-
const locale =
|
|
737
|
+
const locale = this.resolveLocale(inLocale);
|
|
697
738
|
if (this.currencies.length && this.currenciesLocale === locale) return this.currencies;
|
|
698
739
|
const route = `currencies/${locale}`;
|
|
699
740
|
const response = await LangsysAppAPI.get(route);
|
|
@@ -706,7 +747,7 @@ var LangsysAppClass = class {
|
|
|
706
747
|
return this.currencies;
|
|
707
748
|
}
|
|
708
749
|
async getCountries(inLocale) {
|
|
709
|
-
const locale =
|
|
750
|
+
const locale = this.resolveLocale(inLocale);
|
|
710
751
|
if (this.countries.length && this.countriesLocale === locale) return this.countries;
|
|
711
752
|
const route = `countries/${locale}`;
|
|
712
753
|
const response = await LangsysAppAPI.get(route);
|
|
@@ -720,7 +761,7 @@ var LangsysAppClass = class {
|
|
|
720
761
|
}
|
|
721
762
|
async getCountryName(forCountryCode, inLocale) {
|
|
722
763
|
if (!forCountryCode) return "";
|
|
723
|
-
const locale =
|
|
764
|
+
const locale = this.resolveLocale(inLocale);
|
|
724
765
|
if (!this.countries.length || this.countriesLocale !== locale) await this.getCountries(inLocale);
|
|
725
766
|
const country = this.countries.find((c) => c.code.toLowerCase() === forCountryCode.toLowerCase())?.label;
|
|
726
767
|
if (!country) this.debug.warn("getCountryName failed to match", forCountryCode);
|
|
@@ -728,14 +769,14 @@ var LangsysAppClass = class {
|
|
|
728
769
|
}
|
|
729
770
|
async getCurrencyName(forCurrencyCode, inLocale) {
|
|
730
771
|
if (!forCurrencyCode) return "";
|
|
731
|
-
const locale =
|
|
772
|
+
const locale = this.resolveLocale(inLocale);
|
|
732
773
|
if (!this.currencies.length || this.currenciesLocale !== locale) await this.getCurrencies(inLocale);
|
|
733
774
|
const currency = this.currencies.find((c) => c.code?.toLowerCase() === forCurrencyCode.toLowerCase())?.name;
|
|
734
775
|
if (!currency) this.debug.warn("getCurrencyName failed to match", forCurrencyCode);
|
|
735
776
|
return currency || forCurrencyCode;
|
|
736
777
|
}
|
|
737
778
|
async getLocalesFormat(format = "", inLocale) {
|
|
738
|
-
const locale =
|
|
779
|
+
const locale = this.resolveLocale(inLocale);
|
|
739
780
|
const route = `locales/${locale}` + (format ? `/${format}` : "");
|
|
740
781
|
const response = await LangsysAppAPI.get(route);
|
|
741
782
|
if (response.errors || !response.status) {
|
|
@@ -756,7 +797,7 @@ var LangsysAppClass = class {
|
|
|
756
797
|
return await this.getLocalesFormat("flat", inLocale);
|
|
757
798
|
}
|
|
758
799
|
async getLocalesData(inLocale, forceRefresh = false) {
|
|
759
|
-
const locale =
|
|
800
|
+
const locale = this.resolveLocale(inLocale);
|
|
760
801
|
if (this.locales?.[locale]?.length && !forceRefresh) return this.locales[locale];
|
|
761
802
|
this.locales[locale] = await this.getLocalesFormat("data", inLocale);
|
|
762
803
|
return this.locales[locale];
|
|
@@ -766,15 +807,16 @@ var LangsysAppClass = class {
|
|
|
766
807
|
}
|
|
767
808
|
async getLocaleNameWithLookup(forLocale, shortName = false, inLocale) {
|
|
768
809
|
if (!forLocale) return "";
|
|
769
|
-
const locale =
|
|
810
|
+
const locale = this.resolveLocale(inLocale);
|
|
770
811
|
await this.getLocalesData(locale);
|
|
771
812
|
return this.getLocaleName(forLocale, shortName, locale);
|
|
772
813
|
}
|
|
773
814
|
getLocaleName(forLocale, shortName = false, inLocale) {
|
|
774
|
-
const locale =
|
|
815
|
+
const locale = this.resolveLocale(inLocale);
|
|
816
|
+
const target = canonicalizeLocale(forLocale).toLowerCase();
|
|
775
817
|
let name = "";
|
|
776
818
|
this.locales[locale]?.every((loc) => {
|
|
777
|
-
if (loc.code ===
|
|
819
|
+
if (loc.code.toLowerCase() === target) {
|
|
778
820
|
name = shortName ? loc.lang_name : loc.locale_name;
|
|
779
821
|
return false;
|
|
780
822
|
}
|
|
@@ -800,7 +842,7 @@ var LangsysAppClass = class {
|
|
|
800
842
|
const bestMatch = this.findBestLocaleMatch(userLocales, supportedLocales);
|
|
801
843
|
if (bestMatch) return bestMatch;
|
|
802
844
|
}
|
|
803
|
-
return
|
|
845
|
+
return canonicalizeLocale(userLocales[0]);
|
|
804
846
|
}
|
|
805
847
|
getUserLanguagePreferences(acceptLanguageHeader) {
|
|
806
848
|
if (acceptLanguageHeader) {
|
|
@@ -837,30 +879,26 @@ var LangsysAppClass = class {
|
|
|
837
879
|
}
|
|
838
880
|
findBestLocaleMatch(userLocales, supportedLocales) {
|
|
839
881
|
if (!userLocales.length || !supportedLocales.length) return null;
|
|
840
|
-
const
|
|
882
|
+
const supported = supportedLocales.map((l) => canonicalizeLocale(l));
|
|
841
883
|
for (const userLocale of userLocales) {
|
|
842
|
-
const
|
|
843
|
-
if (
|
|
884
|
+
const user = canonicalizeLocale(userLocale);
|
|
885
|
+
if (supported.includes(user)) return user;
|
|
844
886
|
}
|
|
845
887
|
for (const userLocale of userLocales) {
|
|
846
|
-
const
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
888
|
+
const userKey = maximizedLangScript(userLocale);
|
|
889
|
+
if (!userKey) continue;
|
|
890
|
+
const match = supported.find((s) => maximizedLangScript(s) === userKey);
|
|
891
|
+
if (match) return match;
|
|
850
892
|
}
|
|
851
893
|
return null;
|
|
852
894
|
}
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
return
|
|
860
|
-
}
|
|
861
|
-
getLanguageCode(locale) {
|
|
862
|
-
if (!locale || typeof locale !== "string") return "";
|
|
863
|
-
return locale.split(/[-_]/)[0].toLowerCase();
|
|
895
|
+
/**
|
|
896
|
+
* Resolve the effective locale for a data-fetching helper: explicit
|
|
897
|
+
* argument, else the user's current locale, else the base locale —
|
|
898
|
+
* always in BCP 47 canonical form so cache keys and API routes agree.
|
|
899
|
+
*/
|
|
900
|
+
resolveLocale(inLocale) {
|
|
901
|
+
return canonicalizeLocale(inLocale || this.config.sUserLocale.get() || this.config.baseLocale);
|
|
864
902
|
}
|
|
865
903
|
};
|
|
866
904
|
var LangsysApp = new LangsysAppClass();
|
|
@@ -1486,6 +1524,6 @@ var t = ((phrase, ...rest) => {
|
|
|
1486
1524
|
});
|
|
1487
1525
|
var tSignal = LangsysApp.Translations.tSignal;
|
|
1488
1526
|
|
|
1489
|
-
export { LangsysApp, LangsysAppAPI, Logger, PHRASE_MARKER_ATTR, Phrase, Translate, Translations, createSignal, currentlyLoadedLocale, encodeRichText, generateCustomId, getValue, interpolate, isContentBlockKnown, isEmpty, isICU, logger, markupTokenValues, md5, persist, reconstitute, registerContentBlock, sTranslations, stripSentinels, t, tSignal, tokenizeElement };
|
|
1527
|
+
export { LangsysApp, LangsysAppAPI, Logger, PHRASE_MARKER_ATTR, Phrase, Translate, Translations, canonicalizeLocale, createSignal, currentlyLoadedLocale, encodeRichText, generateCustomId, getValue, interpolate, isContentBlockKnown, isEmpty, isICU, logger, markupTokenValues, md5, persist, reconstitute, registerContentBlock, sTranslations, stripSentinels, t, tSignal, tokenizeElement };
|
|
1490
1528
|
//# sourceMappingURL=index.mjs.map
|
|
1491
1529
|
//# sourceMappingURL=index.mjs.map
|