locon 1.4.6 → 1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,9 +1,7 @@
1
1
  "use strict";
2
- var __create = Object.create;
3
2
  var __defProp = Object.defineProperty;
4
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
6
  var __export = (target, all) => {
9
7
  for (var name in all)
@@ -17,38 +15,590 @@ var __copyProps = (to, from, except, desc) => {
17
15
  }
18
16
  return to;
19
17
  };
20
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
- // If the importer is in node compatibility mode or this is not an ESM
22
- // file that has been converted to a CommonJS file using a Babel-
23
- // compatible transform (i.e. "__esModule" has not been set), then set
24
- // "default" to the CommonJS "module.exports" for node compatibility.
25
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
- mod
27
- ));
28
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
- var locon_exports = {};
30
- __export(locon_exports, {
31
- LText: () => import_LText.default,
19
+
20
+ // src/locon/index.tsx
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ LText: () => LText_default,
32
24
  LoconContext: () => LoconContext,
33
- default: () => locon_default,
34
- useLocon: () => import_useLocon.default
25
+ RTL_LANGUAGES: () => RTL_LANGUAGES,
26
+ RTL_SCRIPTS: () => RTL_SCRIPTS,
27
+ applyRTL: () => applyRTL,
28
+ createTranslator: () => createTranslator_default,
29
+ default: () => index_default,
30
+ getSystemLanguage: () => getSystemLanguage_default,
31
+ intlLocale: () => intlLocale_default,
32
+ isRtlLocale: () => isRtlLocale,
33
+ resolveLocale: () => resolveLocale_default,
34
+ useLocon: () => useLocon_default
35
35
  });
36
- module.exports = __toCommonJS(locon_exports);
37
- var import_jsx_runtime = require("react/jsx-runtime");
36
+ module.exports = __toCommonJS(index_exports);
37
+ var import_react2 = require("react");
38
+
39
+ // src/locon/utils/pluralCategory.ts
40
+ var between = (x, from, to) => x >= from && x <= to;
41
+ var oneIfIntegerOne = ({ i, v }) => i === 1 && v === 0 ? "one" : "other";
42
+ var oneIfOne = ({ n }) => n === 1 ? "one" : "other";
43
+ var oneIfZeroOrOne = ({ i }) => i === 0 || i === 1 ? "one" : "other";
44
+ var onlyOther = () => "other";
45
+ var eastSlavic = ({ i, v }) => {
46
+ if (v !== 0) return "other";
47
+ if (i % 10 === 1 && i % 100 !== 11) return "one";
48
+ if (between(i % 10, 2, 4) && !between(i % 100, 12, 14)) return "few";
49
+ return "many";
50
+ };
51
+ var southSlavic = ({ i, v }) => {
52
+ if (v === 0 && i % 10 === 1 && i % 100 !== 11) return "one";
53
+ if (v === 0 && between(i % 10, 2, 4) && !between(i % 100, 12, 14)) return "few";
54
+ return "other";
55
+ };
56
+ var westSlavic = ({ i, v }) => {
57
+ if (i === 1 && v === 0) return "one";
58
+ if (between(i, 2, 4) && v === 0) return "few";
59
+ return v !== 0 ? "many" : "other";
60
+ };
61
+ var RULES = {
62
+ ru: eastSlavic,
63
+ uk: eastSlavic,
64
+ be: eastSlavic,
65
+ pl: ({ i, v }) => {
66
+ if (v !== 0) return "other";
67
+ if (i === 1) return "one";
68
+ if (between(i % 10, 2, 4) && !between(i % 100, 12, 14)) return "few";
69
+ return "many";
70
+ },
71
+ cs: westSlavic,
72
+ sk: westSlavic,
73
+ hr: southSlavic,
74
+ sr: southSlavic,
75
+ bs: southSlavic,
76
+ sl: ({ i, v }) => {
77
+ if (v !== 0) return "few";
78
+ if (i % 100 === 1) return "one";
79
+ if (i % 100 === 2) return "two";
80
+ if (between(i % 100, 3, 4)) return "few";
81
+ return "other";
82
+ },
83
+ lt: ({ n, v }) => {
84
+ if (v !== 0) return "many";
85
+ if (n % 10 === 1 && !between(n % 100, 11, 19)) return "one";
86
+ if (between(n % 10, 2, 9) && !between(n % 100, 11, 19)) return "few";
87
+ return "other";
88
+ },
89
+ lv: ({ n, v }) => {
90
+ if (v === 0 && (n % 10 === 0 || between(n % 100, 11, 19))) return "zero";
91
+ if (v === 0 && n % 10 === 1 && n % 100 !== 11) return "one";
92
+ return "other";
93
+ },
94
+ ro: ({ n, i, v }) => {
95
+ if (i === 1 && v === 0) return "one";
96
+ if (v !== 0 || n === 0 || n !== 1 && between(n % 100, 1, 19)) return "few";
97
+ return "other";
98
+ },
99
+ ar: ({ n, v }) => {
100
+ if (v !== 0) return "other";
101
+ if (n === 0) return "zero";
102
+ if (n === 1) return "one";
103
+ if (n === 2) return "two";
104
+ if (between(n % 100, 3, 10)) return "few";
105
+ if (between(n % 100, 11, 99)) return "many";
106
+ return "other";
107
+ },
108
+ he: ({ i, v }) => {
109
+ if (i === 1 && v === 0 || i === 0 && v !== 0) return "one";
110
+ if (i === 2 && v === 0) return "two";
111
+ return "other";
112
+ },
113
+ ga: ({ n, v }) => {
114
+ if (v !== 0) return "other";
115
+ if (n === 1) return "one";
116
+ if (n === 2) return "two";
117
+ if (between(n, 3, 6)) return "few";
118
+ if (between(n, 7, 10)) return "many";
119
+ return "other";
120
+ },
121
+ fr: oneIfZeroOrOne,
122
+ pt: oneIfZeroOrOne,
123
+ hi: ({ n, i }) => i === 0 || n === 1 ? "one" : "other",
124
+ bn: ({ n, i }) => i === 0 || n === 1 ? "one" : "other",
125
+ fa: ({ n, i }) => i === 0 || n === 1 ? "one" : "other",
126
+ es: oneIfOne,
127
+ tr: oneIfOne,
128
+ el: oneIfOne,
129
+ hu: oneIfOne,
130
+ ka: oneIfOne,
131
+ kk: oneIfOne,
132
+ az: oneIfOne,
133
+ uz: oneIfOne,
134
+ bg: oneIfOne,
135
+ sq: oneIfOne,
136
+ ja: onlyOther,
137
+ zh: onlyOther,
138
+ ko: onlyOther,
139
+ vi: onlyOther,
140
+ th: onlyOther,
141
+ id: onlyOther,
142
+ ms: onlyOther,
143
+ my: onlyOther,
144
+ km: onlyOther,
145
+ lo: onlyOther
146
+ };
147
+ function operands(count) {
148
+ const n = Math.abs(count);
149
+ const fraction = String(n).split(".")[1] ?? "";
150
+ return { n, i: Math.floor(n), v: fraction.length };
151
+ }
152
+ function fallbackCategory(count, tag) {
153
+ const language = tag.split("-")[0]?.toLowerCase() ?? "";
154
+ const rule = RULES[language] ?? oneIfIntegerOne;
155
+ return rule(operands(count));
156
+ }
157
+ function pluralCategory(count, locale) {
158
+ const tag = locale.replace(/_/g, "-");
159
+ if (typeof Intl !== "undefined" && typeof Intl.PluralRules === "function") {
160
+ try {
161
+ return new Intl.PluralRules(tag).select(count);
162
+ } catch {
163
+ }
164
+ }
165
+ return fallbackCategory(count, tag);
166
+ }
167
+
168
+ // src/locon/utils/createTranslator.ts
169
+ var reverseIndexCache = /* @__PURE__ */ new WeakMap();
170
+ function getReverseIndex(assets, locale) {
171
+ let perLocale = reverseIndexCache.get(assets);
172
+ if (!perLocale) {
173
+ perLocale = /* @__PURE__ */ new Map();
174
+ reverseIndexCache.set(assets, perLocale);
175
+ }
176
+ const cached = perLocale.get(locale);
177
+ if (cached) {
178
+ return cached;
179
+ }
180
+ const index = /* @__PURE__ */ new Map();
181
+ const localeAssets = assets[locale];
182
+ if (localeAssets) {
183
+ for (const key of Object.keys(localeAssets)) {
184
+ const value = localeAssets[key];
185
+ if (value !== void 0 && !index.has(value)) {
186
+ index.set(value, key);
187
+ }
188
+ }
189
+ }
190
+ perLocale.set(locale, index);
191
+ return index;
192
+ }
193
+ function interpolate(value, params) {
194
+ if (!params) {
195
+ return value;
196
+ }
197
+ return value.replace(
198
+ /\{(\w+)\}/g,
199
+ (match, token) => params[token] === void 0 ? match : String(params[token])
200
+ );
201
+ }
202
+ var PLURAL_SUFFIX = /_(zero|one|two|few|many|other)$/;
203
+ function createTranslator({ assets, locale, defaultLocale = "en", projectLocale }) {
204
+ const resolvedProjectLocale = projectLocale || defaultLocale;
205
+ const read = (fromLocale, key) => {
206
+ const value = assets[fromLocale]?.[key];
207
+ return value === void 0 || value === "" ? void 0 : value;
208
+ };
209
+ const keyFor = (input, targetLocale) => {
210
+ if (assets[targetLocale]?.[input] !== void 0 || assets[defaultLocale]?.[input] !== void 0 || assets[resolvedProjectLocale]?.[input] !== void 0) {
211
+ return input;
212
+ }
213
+ return getReverseIndex(assets, resolvedProjectLocale).get(input) ?? getReverseIndex(assets, defaultLocale).get(input) ?? input;
214
+ };
215
+ return function l(assetKey, options) {
216
+ if (!assetKey) {
217
+ return "";
218
+ }
219
+ const targetLocale = options?.locale || locale;
220
+ const resolvedKey = keyFor(assetKey, targetLocale);
221
+ const key = options?.count === void 0 ? resolvedKey : resolvedKey.replace(PLURAL_SUFFIX, "");
222
+ const params = options?.count === void 0 ? options?.params : { ...options.params, count: options.count };
223
+ for (const candidateLocale of [targetLocale, defaultLocale, resolvedProjectLocale]) {
224
+ const candidateKeys = options?.count === void 0 ? [key] : [`${key}_${pluralCategory(options.count, candidateLocale)}`, `${key}_other`, key];
225
+ for (const candidateKey of candidateKeys) {
226
+ const value = read(candidateLocale, candidateKey);
227
+ if (value !== void 0) {
228
+ return interpolate(value, params);
229
+ }
230
+ }
231
+ }
232
+ return interpolate(options?.fallback ?? assetKey, params);
233
+ };
234
+ }
235
+ var createTranslator_default = createTranslator;
236
+
237
+ // src/locon/utils/getSystemLanguage.ts
238
+ function isModuleNotFound(error) {
239
+ const candidate = error;
240
+ if (candidate?.code === "MODULE_NOT_FOUND") {
241
+ return true;
242
+ }
243
+ return /cannot find module|requiring unknown module|could not be found|unable to resolve/i.test(
244
+ candidate?.message ?? ""
245
+ );
246
+ }
247
+ function matchLocale(deviceTags, availableLocales) {
248
+ const parse = (locale) => {
249
+ const normalized = locale.toLowerCase().replace(/_/g, "-");
250
+ const subtags = normalized.split("-");
251
+ const extensionIndex = subtags.findIndex((subtag, index) => index > 0 && subtag.length === 1);
252
+ const coreSubtags = subtags.slice(1, extensionIndex < 0 ? void 0 : extensionIndex);
253
+ const explicitScript = coreSubtags.find((subtag) => /^[a-z]{4}$/.test(subtag));
254
+ const region = coreSubtags.find((subtag) => /^[a-z]{2}$|^\d{3}$/.test(subtag));
255
+ const chineseRegionScript = subtags[0] === "zh" && region ? ["tw", "hk", "mo"].includes(region) ? "hant" : ["cn", "sg", "my"].includes(region) ? "hans" : void 0 : void 0;
256
+ let script = explicitScript ?? chineseRegionScript;
257
+ if (!script) {
258
+ try {
259
+ script = new Intl.Locale(normalized).maximize().script?.toLowerCase();
260
+ } catch {
261
+ }
262
+ }
263
+ return {
264
+ normalized,
265
+ language: subtags[0],
266
+ // Script is normally the second subtag. Looking until the first
267
+ // extension also handles legacy extlang-shaped tags without confusing a
268
+ // four-character variant beginning with a digit for a script. Likely
269
+ // subtags distinguish region-only tags such as zh-TW and zh-CN.
270
+ explicitScript,
271
+ script
272
+ };
273
+ };
274
+ const available = availableLocales.map((locale) => ({ locale, ...parse(locale) }));
275
+ for (const tag of deviceTags) {
276
+ if (!tag) {
277
+ continue;
278
+ }
279
+ const requested = parse(tag);
280
+ const exact = available.find((entry) => entry.normalized === requested.normalized);
281
+ if (exact) {
282
+ return exact.locale;
283
+ }
284
+ const languageEntries = available.filter((entry) => entry.language === requested.language);
285
+ const availableScripts = new Set(languageEntries.map((entry) => entry.script).filter(Boolean));
286
+ const scriptMatters = Boolean(requested.explicitScript || availableScripts.size > 1);
287
+ const sameScript = scriptMatters && requested.script ? available.find((entry) => entry.language === requested.language && entry.script === requested.script) : void 0;
288
+ if (sameScript) {
289
+ return sameScript.locale;
290
+ }
291
+ const language = available.find((entry) => entry.normalized === requested.language);
292
+ if (language) {
293
+ return language.locale;
294
+ }
295
+ const sameLanguage = available.find((entry) => entry.language === requested.language);
296
+ if (sameLanguage) {
297
+ return sameLanguage.locale;
298
+ }
299
+ }
300
+ return null;
301
+ }
302
+ function tagsFromReactNativeLocalize() {
303
+ try {
304
+ const RNLocalize = require("react-native-localize");
305
+ if (typeof RNLocalize?.getLocales === "function") {
306
+ return RNLocalize.getLocales().flatMap(
307
+ (locale) => [locale.languageTag, locale.languageCode].filter(Boolean)
308
+ );
309
+ }
310
+ } catch (error) {
311
+ if (!isModuleNotFound(error)) {
312
+ console.warn("[locon] react-native-localize failed:", error);
313
+ }
314
+ }
315
+ return [];
316
+ }
317
+ function tagsFromExpoLocalization() {
318
+ try {
319
+ const Localization = require("expo-localization");
320
+ if (typeof Localization?.getLocales === "function") {
321
+ return Localization.getLocales().flatMap(
322
+ (locale) => [locale.languageTag, locale.languageCode].filter(Boolean)
323
+ );
324
+ }
325
+ } catch (error) {
326
+ if (!isModuleNotFound(error)) {
327
+ console.warn("[locon] expo-localization failed:", error);
328
+ }
329
+ }
330
+ return [];
331
+ }
332
+ function tagsFromNativeModules() {
333
+ try {
334
+ const { NativeModules, Platform } = require("react-native");
335
+ const tags = [];
336
+ if (Platform?.OS === "ios") {
337
+ const appleLanguages = NativeModules?.SettingsManager?.settings?.AppleLanguages;
338
+ if (Array.isArray(appleLanguages)) {
339
+ tags.push(...appleLanguages);
340
+ }
341
+ }
342
+ const localeIdentifier = NativeModules?.I18nManager?.localeIdentifier;
343
+ if (typeof localeIdentifier === "string") {
344
+ tags.push(localeIdentifier);
345
+ }
346
+ return tags;
347
+ } catch {
348
+ return [];
349
+ }
350
+ }
351
+ function tagsFromIntl() {
352
+ try {
353
+ const systemLocale = Intl.DateTimeFormat().resolvedOptions().locale;
354
+ return systemLocale ? [systemLocale] : [];
355
+ } catch {
356
+ return [];
357
+ }
358
+ }
359
+ function getSystemLanguage(availableLocales) {
360
+ if (!availableLocales?.length) {
361
+ return null;
362
+ }
363
+ const sources = [tagsFromExpoLocalization, tagsFromReactNativeLocalize, tagsFromNativeModules, tagsFromIntl];
364
+ for (const source of sources) {
365
+ const match = matchLocale(source(), availableLocales);
366
+ if (match) {
367
+ return match;
368
+ }
369
+ }
370
+ return null;
371
+ }
372
+ var getSystemLanguage_default = getSystemLanguage;
373
+
374
+ // src/locon/utils/resolveLocale.ts
375
+ function resolveLocale({
376
+ assets,
377
+ currentLocale,
378
+ defaultLocale = "en",
379
+ autodetect = true,
380
+ systemLocale
381
+ }) {
382
+ if (currentLocale) {
383
+ return currentLocale;
384
+ }
385
+ if (!autodetect) {
386
+ return defaultLocale;
387
+ }
388
+ const detected = systemLocale === void 0 ? getSystemLanguage_default(Object.keys(assets)) : systemLocale;
389
+ return detected ?? defaultLocale;
390
+ }
391
+ var resolveLocale_default = resolveLocale;
392
+
393
+ // src/locon/utils/rtl.ts
394
+ var RTL_LANGUAGES = /* @__PURE__ */ new Set([
395
+ "ar",
396
+ // Arabic
397
+ "arc",
398
+ // Aramaic
399
+ "ckb",
400
+ // Central Kurdish (Sorani)
401
+ "dv",
402
+ // Divehi
403
+ "fa",
404
+ // Persian
405
+ "glk",
406
+ // Gilaki
407
+ "he",
408
+ // Hebrew
409
+ "khw",
410
+ // Khowar
411
+ "ks",
412
+ // Kashmiri (Perso-Arabic)
413
+ "lrc",
414
+ // Northern Luri
415
+ "mzn",
416
+ // Mazanderani
417
+ "nqo",
418
+ // N'Ko
419
+ "pnb",
420
+ // Western Punjabi
421
+ "ps",
422
+ // Pashto
423
+ "sd",
424
+ // Sindhi (Perso-Arabic)
425
+ "syr",
426
+ // Syriac
427
+ "ug",
428
+ // Uyghur
429
+ "ur",
430
+ // Urdu
431
+ "yi"
432
+ // Yiddish
433
+ ]);
434
+ var RTL_SCRIPTS = /* @__PURE__ */ new Set([
435
+ "adlm",
436
+ "arab",
437
+ "aran",
438
+ "armi",
439
+ "avst",
440
+ "chrs",
441
+ "cprt",
442
+ "elym",
443
+ "hatr",
444
+ "hebr",
445
+ "hung",
446
+ "khar",
447
+ "lydi",
448
+ "mand",
449
+ "mani",
450
+ "mend",
451
+ "merc",
452
+ "mero",
453
+ "narb",
454
+ "nbat",
455
+ "nkoo",
456
+ "orkh",
457
+ "ougr",
458
+ "palm",
459
+ "phli",
460
+ "phlp",
461
+ "phnx",
462
+ "prti",
463
+ "rohg",
464
+ "samr",
465
+ "sarb",
466
+ "sogd",
467
+ "sogo",
468
+ "syrc",
469
+ "thaa",
470
+ "yezi"
471
+ ]);
472
+ var lastRequestedRTL;
473
+ function rtlFromIntl(locale) {
474
+ try {
475
+ const likelyLocale = new Intl.Locale(locale.replace(/_/g, "-")).maximize();
476
+ const localeWithDirection = likelyLocale;
477
+ const direction = localeWithDirection.getTextInfo?.().direction ?? localeWithDirection.textInfo?.direction;
478
+ if (direction === "rtl") {
479
+ return true;
480
+ }
481
+ if (direction === "ltr") {
482
+ return false;
483
+ }
484
+ const likelyScript = likelyLocale.script?.toLowerCase();
485
+ return likelyScript && RTL_SCRIPTS.has(likelyScript) ? true : void 0;
486
+ } catch {
487
+ return void 0;
488
+ }
489
+ }
490
+ function isRtlLocale(locale) {
491
+ if (!locale) {
492
+ return false;
493
+ }
494
+ const subtags = locale.split(/[-_]/);
495
+ const languageCode = subtags[0]?.toLowerCase();
496
+ const extensionIndex = subtags.findIndex((subtag, index) => index > 0 && subtag.length === 1);
497
+ const coreSubtags = subtags.slice(1, extensionIndex < 0 ? void 0 : extensionIndex);
498
+ const scriptCode = coreSubtags.find((subtag) => /^[a-z]{4}$/i.test(subtag))?.toLowerCase();
499
+ if (scriptCode) {
500
+ return RTL_SCRIPTS.has(scriptCode) || rtlFromIntl(locale) === true;
501
+ }
502
+ const runtimeDirection = rtlFromIntl(locale);
503
+ if (runtimeDirection !== void 0) {
504
+ return runtimeDirection;
505
+ }
506
+ return Boolean(languageCode && RTL_LANGUAGES.has(languageCode));
507
+ }
508
+ function applyRTL(locale) {
509
+ const shouldBeRTL = isRtlLocale(locale);
510
+ try {
511
+ const { I18nManager } = require("react-native");
512
+ if (!I18nManager || typeof I18nManager.forceRTL !== "function") {
513
+ return false;
514
+ }
515
+ I18nManager.allowRTL?.(true);
516
+ const renderedRTL = Boolean(I18nManager.isRTL);
517
+ const effectiveRequestedRTL = lastRequestedRTL ?? renderedRTL;
518
+ if (effectiveRequestedRTL !== shouldBeRTL) {
519
+ I18nManager.forceRTL(shouldBeRTL);
520
+ }
521
+ lastRequestedRTL = shouldBeRTL;
522
+ return renderedRTL !== shouldBeRTL;
523
+ } catch {
524
+ return false;
525
+ }
526
+ }
527
+
528
+ // src/locon/components/LText.tsx
529
+ var import_react_native = require("react-native");
530
+
531
+ // src/locon/hooks/useLocon.ts
38
532
  var import_react = require("react");
39
- var import_getSystemLanguage = __toESM(require("./utils/getSystemLanguage"));
40
- var import_LText = __toESM(require("./components/LText"));
41
- var import_useLocon = __toESM(require("./hooks/useLocon"));
42
- const LoconContext = (0, import_react.createContext)({
533
+ function useLocon() {
534
+ const locon = (0, import_react.useContext)(LoconContext);
535
+ return locon;
536
+ }
537
+ var useLocon_default = useLocon;
538
+
539
+ // src/locon/components/LText.tsx
540
+ var import_jsx_runtime = require("react/jsx-runtime");
541
+ function LText({ children, assetKey, params, count, locale, ...props }) {
542
+ const { l } = useLocon_default();
543
+ const value = l(assetKey || children, { params, count, locale, fallback: assetKey ? children : void 0 });
544
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native.Text, { ...props, children: value });
545
+ }
546
+ var LText_default = LText;
547
+
548
+ // src/locon/utils/intlLocale.ts
549
+ function intlLocale(locale, extensions = {}) {
550
+ const normalizedLocale = locale.replace(/_/g, "-");
551
+ const requested = [];
552
+ if (extensions.calendar) {
553
+ requested.push(["ca", extensions.calendar]);
554
+ }
555
+ if (extensions.numbering) {
556
+ requested.push(["nu", extensions.numbering]);
557
+ }
558
+ if (!requested.length) {
559
+ return normalizedLocale;
560
+ }
561
+ const subtags = normalizedLocale.split("-");
562
+ const isSingleton = (subtag) => subtag.length === 1;
563
+ const privateUseIndex = subtags.findIndex((subtag, index) => index > 0 && subtag.toLowerCase() === "x");
564
+ const unicodeIndex = subtags.findIndex(
565
+ (subtag, index) => index > 0 && (privateUseIndex < 0 || index < privateUseIndex) && subtag.toLowerCase() === "u"
566
+ );
567
+ if (unicodeIndex >= 0) {
568
+ const nextSingleton = subtags.findIndex((subtag, index) => index > unicodeIndex && isSingleton(subtag));
569
+ const unicodeEnd = nextSingleton < 0 ? subtags.length : nextSingleton;
570
+ const existingKeys = new Set(
571
+ subtags.slice(unicodeIndex + 1, unicodeEnd).filter((subtag) => subtag.length === 2).map((subtag) => subtag.toLowerCase())
572
+ );
573
+ const additions = requested.flatMap(([key, value]) => existingKeys.has(key) ? [] : [key, value]);
574
+ if (!additions.length) {
575
+ return normalizedLocale;
576
+ }
577
+ subtags.splice(unicodeEnd, 0, ...additions);
578
+ return subtags.join("-");
579
+ }
580
+ const insertionIndex = privateUseIndex < 0 ? subtags.length : privateUseIndex;
581
+ subtags.splice(insertionIndex, 0, "u", ...requested.flat());
582
+ return subtags.join("-");
583
+ }
584
+ var intlLocale_default = intlLocale;
585
+
586
+ // src/locon/index.tsx
587
+ var import_jsx_runtime2 = require("react/jsx-runtime");
588
+ var fallbackTranslator = createTranslator_default({ assets: {}, locale: "en" });
589
+ var LoconContext = (0, import_react2.createContext)({
43
590
  assets: {},
591
+ locales: [],
44
592
  currentLocale: "en",
45
593
  defaultLocale: "en",
594
+ projectLocale: "en",
595
+ systemLocale: null,
596
+ isRTL: false,
46
597
  autodetect: true,
47
598
  setLocale: () => {
48
599
  },
49
- // Default implementation
50
- l: (assetKey) => assetKey
51
- // Default implementation
600
+ l: fallbackTranslator,
601
+ lIn: (locale, assetKey, options) => fallbackTranslator(assetKey, { ...options, locale })
52
602
  });
53
603
  function Locon({
54
604
  children,
@@ -59,82 +609,89 @@ function Locon({
59
609
  autodetect = true
60
610
  }) {
61
611
  const resolvedProjectLocale = projectLocale || defaultLocale;
62
- const [currentLocale, setCurrentLocale] = (0, import_react.useState)(() => {
63
- if (currentLocaleProps) {
64
- return currentLocaleProps;
65
- }
66
- if (autodetect) {
67
- const systemLocale = (0, import_getSystemLanguage.default)(Object.keys(assets));
68
- if (systemLocale && assets[systemLocale]) {
69
- return systemLocale;
70
- }
71
- }
72
- return defaultLocale;
73
- });
74
- const getAssets = (0, import_react.useCallback)(() => {
75
- const currentAssets = assets[currentLocale] ?? assets[defaultLocale];
76
- return currentAssets ?? {};
77
- }, [assets, currentLocale, defaultLocale]);
78
- const getAsset = (0, import_react.useCallback)(
79
- (assetKey) => {
80
- const assetsObj = getAssets();
81
- return assetsObj[assetKey] ?? "";
82
- },
83
- [getAssets]
612
+ const locales = (0, import_react2.useMemo)(() => Object.keys(assets), [assets]);
613
+ const systemLocale = (0, import_react2.useMemo)(() => autodetect ? getSystemLanguage_default(locales) : null, [autodetect, locales]);
614
+ const [currentLocale, setCurrentLocale] = (0, import_react2.useState)(
615
+ () => resolveLocale_default({ assets, currentLocale: currentLocaleProps, defaultLocale, autodetect, systemLocale })
84
616
  );
85
- const setCurrentLocaleHandler = (0, import_react.useCallback)(
617
+ const setCurrentLocaleHandler = (0, import_react2.useCallback)(
86
618
  (locale) => {
619
+ if (locale === null) {
620
+ setCurrentLocale(resolveLocale_default({ assets, defaultLocale, autodetect, systemLocale }));
621
+ return;
622
+ }
87
623
  if (assets[locale]) {
88
624
  setCurrentLocale(locale);
89
625
  } else {
90
626
  console.warn(`[locon] Locale "${locale}" not found in assets.`);
91
627
  }
92
628
  },
93
- [assets]
629
+ [assets, systemLocale, defaultLocale, autodetect]
94
630
  );
95
- const getAssetByValue = (0, import_react.useCallback)(
96
- (assetValue, locale = resolvedProjectLocale) => {
97
- const currentAssets = assets[locale];
98
- if (!currentAssets) {
99
- return void 0;
100
- }
101
- const assetKey = Object.keys(currentAssets).find((key) => currentAssets[key] === assetValue);
102
- return assetKey ? getAsset(assetKey) : void 0;
103
- },
104
- [assets, getAsset, resolvedProjectLocale]
631
+ const l = (0, import_react2.useMemo)(
632
+ () => createTranslator_default({
633
+ assets,
634
+ locale: currentLocale,
635
+ defaultLocale,
636
+ projectLocale: resolvedProjectLocale
637
+ }),
638
+ [assets, currentLocale, defaultLocale, resolvedProjectLocale]
105
639
  );
106
- const l = (0, import_react.useCallback)(
107
- (assetKey) => {
108
- if (!assetKey) {
109
- return "";
110
- }
111
- const asset = getAsset(assetKey) || getAssetByValue(assetKey) || getAssetByValue(assetKey, defaultLocale);
112
- return asset ?? assetKey;
113
- },
114
- [getAsset, getAssetByValue, defaultLocale]
640
+ const lIn = (0, import_react2.useCallback)(
641
+ (locale, assetKey, options) => l(assetKey, { ...options, locale }),
642
+ [l]
115
643
  );
116
- (0, import_react.useEffect)(() => {
117
- if (currentLocaleProps) {
118
- setCurrentLocale((locale) => locale === currentLocaleProps ? locale : currentLocaleProps);
644
+ (0, import_react2.useEffect)(() => {
645
+ if (currentLocaleProps === void 0) {
646
+ return;
119
647
  }
120
- }, [currentLocaleProps]);
121
- const value = (0, import_react.useMemo)(
648
+ if (currentLocaleProps === null) {
649
+ setCurrentLocale(resolveLocale_default({ assets, defaultLocale, autodetect, systemLocale }));
650
+ return;
651
+ }
652
+ setCurrentLocale((locale) => locale === currentLocaleProps ? locale : currentLocaleProps);
653
+ }, [assets, currentLocaleProps, systemLocale, defaultLocale, autodetect]);
654
+ const value = (0, import_react2.useMemo)(
122
655
  () => ({
123
656
  assets,
657
+ locales,
124
658
  currentLocale,
125
659
  defaultLocale,
660
+ projectLocale: resolvedProjectLocale,
661
+ systemLocale,
662
+ isRTL: isRtlLocale(currentLocale),
126
663
  autodetect,
127
664
  l,
665
+ lIn,
128
666
  setLocale: setCurrentLocaleHandler
129
667
  }),
130
- [assets, currentLocale, defaultLocale, autodetect, l, setCurrentLocaleHandler]
668
+ [
669
+ assets,
670
+ locales,
671
+ currentLocale,
672
+ defaultLocale,
673
+ resolvedProjectLocale,
674
+ systemLocale,
675
+ autodetect,
676
+ l,
677
+ lIn,
678
+ setCurrentLocaleHandler
679
+ ]
131
680
  );
132
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LoconContext, { value, children });
681
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(LoconContext, { value, children });
133
682
  }
134
- var locon_default = Locon;
683
+ var index_default = Locon;
135
684
  // Annotate the CommonJS export names for ESM import in node:
136
685
  0 && (module.exports = {
137
686
  LText,
138
687
  LoconContext,
688
+ RTL_LANGUAGES,
689
+ RTL_SCRIPTS,
690
+ applyRTL,
691
+ createTranslator,
692
+ getSystemLanguage,
693
+ intlLocale,
694
+ isRtlLocale,
695
+ resolveLocale,
139
696
  useLocon
140
697
  });