@playcraft/adsdk 1.0.18-beta.1 → 1.0.18-beta.2

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/esm/index.js CHANGED
@@ -443,6 +443,196 @@ function removeWechatGuide() {
443
443
  }
444
444
  }
445
445
 
446
+ // src/language.ts
447
+ function readQuerystring(key) {
448
+ try {
449
+ if (typeof window === "undefined" || !window.location) return void 0;
450
+ const search = window.location.search || "";
451
+ if (!search) return void 0;
452
+ if (typeof URLSearchParams !== "undefined") {
453
+ const params = new URLSearchParams(search);
454
+ return params.get(key) || void 0;
455
+ }
456
+ const re = new RegExp("[?&]" + key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + "=([^&#]*)");
457
+ const m = re.exec(search);
458
+ return m ? decodeURIComponent(m[1]) : void 0;
459
+ } catch (e) {
460
+ return void 0;
461
+ }
462
+ }
463
+ function readLocalStorage(key) {
464
+ try {
465
+ if (typeof localStorage === "undefined") return void 0;
466
+ return localStorage.getItem(key) || void 0;
467
+ } catch (e) {
468
+ return void 0;
469
+ }
470
+ }
471
+ function readCookie(name) {
472
+ try {
473
+ if (typeof document === "undefined" || !document.cookie) return void 0;
474
+ const re = new RegExp("(?:^|; )" + name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + "=([^;]*)");
475
+ const m = re.exec(document.cookie);
476
+ return m ? decodeURIComponent(m[1]) : void 0;
477
+ } catch (e) {
478
+ return void 0;
479
+ }
480
+ }
481
+ function readNavigatorLanguages() {
482
+ try {
483
+ if (typeof navigator === "undefined") return [];
484
+ const list = [];
485
+ const langs = navigator.languages;
486
+ if (Array.isArray(langs)) list.push(...langs);
487
+ if (navigator.language) list.push(navigator.language);
488
+ const userLang = navigator.userLanguage || navigator.browserLanguage;
489
+ if (userLang) list.push(userLang);
490
+ return list.filter(Boolean);
491
+ } catch (e) {
492
+ return [];
493
+ }
494
+ }
495
+ function readHtmlTag() {
496
+ try {
497
+ if (typeof document === "undefined" || !document.documentElement) return void 0;
498
+ return document.documentElement.lang || void 0;
499
+ } catch (e) {
500
+ return void 0;
501
+ }
502
+ }
503
+ function detectCandidates() {
504
+ return [
505
+ readQuerystring("lng"),
506
+ readLocalStorage("i18nextLng"),
507
+ readCookie("i18next"),
508
+ ...readNavigatorLanguages(),
509
+ readHtmlTag()
510
+ ];
511
+ }
512
+ var SUPPORTED_LANG_CODES = [
513
+ "en",
514
+ "zh",
515
+ "ja",
516
+ "ko",
517
+ "de",
518
+ "fr",
519
+ "es",
520
+ "pt",
521
+ "it",
522
+ "hi",
523
+ "bn",
524
+ "ur",
525
+ "ar",
526
+ "tr",
527
+ "ru",
528
+ "ms",
529
+ "id"
530
+ ];
531
+ function normalizeLangTag(tag) {
532
+ if (!tag || typeof tag !== "string") return void 0;
533
+ const primary = tag.split(/[-_]/)[0].toLowerCase();
534
+ return SUPPORTED_LANG_CODES.indexOf(primary) >= 0 ? primary : void 0;
535
+ }
536
+ function getLanguageCode() {
537
+ const candidates = detectCandidates();
538
+ for (const tag of candidates) {
539
+ const code = normalizeLangTag(tag);
540
+ if (code) return code;
541
+ }
542
+ return "en";
543
+ }
544
+ var LANGUAGE_MAP = {
545
+ // 英语
546
+ "en": "EN",
547
+ // 中文(简体)
548
+ "zh": "CN_S",
549
+ "zh-cn": "CN_S",
550
+ "zh-sg": "CN_S",
551
+ "zh-my": "CN_S",
552
+ "zh-hans": "CN_S",
553
+ "zh-hans-cn": "CN_S",
554
+ "zh-hans-sg": "CN_S",
555
+ "zh-hans-my": "CN_S",
556
+ // 中文(繁体)
557
+ "zh-tw": "CN_T",
558
+ "zh-hk": "CN_T",
559
+ "zh-mo": "CN_T",
560
+ "zh-hant": "CN_T",
561
+ "zh-hant-tw": "CN_T",
562
+ "zh-hant-hk": "CN_T",
563
+ "zh-hant-mo": "CN_T",
564
+ // 德语
565
+ "de": "DE",
566
+ // 法语
567
+ "fr": "FR",
568
+ // 西班牙语
569
+ "es": "ES",
570
+ // 日语
571
+ "ja": "JA",
572
+ // 韩语
573
+ "ko": "KO",
574
+ // 葡萄牙语
575
+ "pt": "PT",
576
+ // 意大利语
577
+ "it": "IT",
578
+ // 印地语
579
+ "hi": "HI",
580
+ // 孟加拉语
581
+ "bn": "BN",
582
+ // 乌尔都语
583
+ "ur": "UR",
584
+ // 阿拉伯语
585
+ "ar": "AR",
586
+ // 土耳其语
587
+ "tr": "TR",
588
+ // 俄语
589
+ "ru": "RU",
590
+ // 马来语
591
+ "ms": "MS",
592
+ // 印尼语
593
+ "id": "ID"
594
+ };
595
+ var SUPPORTED_PROJECT_LANGUAGES = /* @__PURE__ */ new Set([
596
+ "EN",
597
+ "CN_S",
598
+ "CN_T",
599
+ "DE",
600
+ "FR",
601
+ "ES",
602
+ "JA",
603
+ "KO",
604
+ "PT",
605
+ "IT",
606
+ "HI",
607
+ "BN",
608
+ "UR",
609
+ "AR",
610
+ "TR",
611
+ "RU",
612
+ "MS",
613
+ "IN",
614
+ "ID"
615
+ ]);
616
+ function resolveLanguageTag(tag) {
617
+ if (!tag || typeof tag !== "string") return void 0;
618
+ const normalized = tag.trim();
619
+ if (!normalized) return void 0;
620
+ const projectCode = normalized.toUpperCase().replace(/-/g, "_");
621
+ if (SUPPORTED_PROJECT_LANGUAGES.has(projectCode)) return projectCode;
622
+ const lower = normalized.toLowerCase();
623
+ if (LANGUAGE_MAP[lower]) return LANGUAGE_MAP[lower];
624
+ const primary = lower.split(/[-_]/)[0];
625
+ return LANGUAGE_MAP[primary];
626
+ }
627
+ function getLanguage() {
628
+ const candidates = detectCandidates();
629
+ for (const tag of candidates) {
630
+ const code = resolveLanguageTag(tag);
631
+ if (code) return code;
632
+ }
633
+ return "EN";
634
+ }
635
+
446
636
  // src/theme-config.ts
447
637
  function extractDefaultsFromSchema(schema) {
448
638
  if (!schema || typeof schema !== "object") return void 0;
@@ -461,13 +651,6 @@ function extractDefaultsFromSchema(schema) {
461
651
  }
462
652
  return void 0;
463
653
  }
464
- function getLanguageCode() {
465
- try {
466
- return (navigator.language || "en").split("-")[0].toLowerCase();
467
- } catch (e) {
468
- return "en";
469
- }
470
- }
471
654
  function lookupI18nRuntime(runtime, key, langCode) {
472
655
  var _a, _b;
473
656
  const map = runtime[key];
@@ -533,6 +716,7 @@ function setConfig(configList, config) {
533
716
  }
534
717
  if (i18nRecord) {
535
718
  const langCode = getLanguageCode();
719
+ console.log("detect target langCode", langCode);
536
720
  resolveI18nRefs(mergedConfig, i18nRecord, langCode);
537
721
  }
538
722
  resolveImagePaths(mergedConfig, assetRecordByPath);
@@ -1338,6 +1522,31 @@ var _sdk = class _sdk {
1338
1522
  static isGoogle() {
1339
1523
  return _sdk.getCurChannel() === "google";
1340
1524
  }
1525
+ /**
1526
+ * 获取当前用户语言的 i18n 简码。
1527
+ * 多源探测(querystring → localStorage → cookie → navigator → html tag)。
1528
+ *
1529
+ * @returns i18n 简码:en | zh | ja | ko | de | fr | es | pt | it | hi | bn | ur | ar | tr | ru | ms | id,默认 'en'
1530
+ *
1531
+ * @example
1532
+ * const langCode = sdk.getLanguageCode(); // 'zh'
1533
+ */
1534
+ static getLanguageCode() {
1535
+ return getLanguageCode();
1536
+ }
1537
+ /**
1538
+ * 获取当前用户语言的业务代号。
1539
+ * 多源探测(querystring → localStorage → cookie → navigator → html tag),
1540
+ * 并映射到业务语言代号(区分简繁中文)。
1541
+ *
1542
+ * @returns 业务代号:EN | CN_S | CN_T | DE | FR | ES | JA | KO | PT | IT | HI | BN | UR | AR | TR | RU | MS | IN | ID,默认 'EN'
1543
+ *
1544
+ * @example
1545
+ * const lang = sdk.getLanguage(); // 'CN_S'
1546
+ */
1547
+ static getLanguage() {
1548
+ return getLanguage();
1549
+ }
1341
1550
  /**
1342
1551
  * 设置主题配置,支持传入多份 JSON5 配置。
1343
1552
  * 每一份配置都可以是 JSON Schema(带 $schema 字段),会自动提取其中的 default 值。
@@ -1400,7 +1609,7 @@ var _sdk = class _sdk {
1400
1609
  }
1401
1610
  };
1402
1611
  /** Current version of the SDK */
1403
- _sdk.version = "1.0.18-beta.1";
1612
+ _sdk.version = "1.0.18-beta.2";
1404
1613
  /** Current maximum width of the playable ad container in pixels */
1405
1614
  _sdk.maxWidth = Math.floor(window.innerWidth);
1406
1615
  /** Current maximum height of the playable ad container in pixels */
@@ -1430,6 +1639,8 @@ export {
1430
1639
  sdk as default,
1431
1640
  disableCustomCursor,
1432
1641
  enableCustomCursor,
1642
+ getLanguage,
1643
+ getLanguageCode,
1433
1644
  hideWechatGuide,
1434
1645
  removeWechatGuide,
1435
1646
  sdk,
@@ -681,6 +681,196 @@
681
681
  }
682
682
  }
683
683
 
684
+ // src/language.ts
685
+ function readQuerystring(key) {
686
+ try {
687
+ if (typeof window === "undefined" || !window.location) return void 0;
688
+ const search = window.location.search || "";
689
+ if (!search) return void 0;
690
+ if (typeof URLSearchParams !== "undefined") {
691
+ const params = new URLSearchParams(search);
692
+ return params.get(key) || void 0;
693
+ }
694
+ const re = new RegExp("[?&]" + key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + "=([^&#]*)");
695
+ const m = re.exec(search);
696
+ return m ? decodeURIComponent(m[1]) : void 0;
697
+ } catch (e) {
698
+ return void 0;
699
+ }
700
+ }
701
+ function readLocalStorage(key) {
702
+ try {
703
+ if (typeof localStorage === "undefined") return void 0;
704
+ return localStorage.getItem(key) || void 0;
705
+ } catch (e) {
706
+ return void 0;
707
+ }
708
+ }
709
+ function readCookie(name) {
710
+ try {
711
+ if (typeof document === "undefined" || !document.cookie) return void 0;
712
+ const re = new RegExp("(?:^|; )" + name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + "=([^;]*)");
713
+ const m = re.exec(document.cookie);
714
+ return m ? decodeURIComponent(m[1]) : void 0;
715
+ } catch (e) {
716
+ return void 0;
717
+ }
718
+ }
719
+ function readNavigatorLanguages() {
720
+ try {
721
+ if (typeof navigator === "undefined") return [];
722
+ const list = [];
723
+ const langs = navigator.languages;
724
+ if (Array.isArray(langs)) list.push(...langs);
725
+ if (navigator.language) list.push(navigator.language);
726
+ const userLang = navigator.userLanguage || navigator.browserLanguage;
727
+ if (userLang) list.push(userLang);
728
+ return list.filter(Boolean);
729
+ } catch (e) {
730
+ return [];
731
+ }
732
+ }
733
+ function readHtmlTag() {
734
+ try {
735
+ if (typeof document === "undefined" || !document.documentElement) return void 0;
736
+ return document.documentElement.lang || void 0;
737
+ } catch (e) {
738
+ return void 0;
739
+ }
740
+ }
741
+ function detectCandidates() {
742
+ return [
743
+ readQuerystring("lng"),
744
+ readLocalStorage("i18nextLng"),
745
+ readCookie("i18next"),
746
+ ...readNavigatorLanguages(),
747
+ readHtmlTag()
748
+ ];
749
+ }
750
+ var SUPPORTED_LANG_CODES = [
751
+ "en",
752
+ "zh",
753
+ "ja",
754
+ "ko",
755
+ "de",
756
+ "fr",
757
+ "es",
758
+ "pt",
759
+ "it",
760
+ "hi",
761
+ "bn",
762
+ "ur",
763
+ "ar",
764
+ "tr",
765
+ "ru",
766
+ "ms",
767
+ "id"
768
+ ];
769
+ function normalizeLangTag(tag) {
770
+ if (!tag || typeof tag !== "string") return void 0;
771
+ const primary = tag.split(/[-_]/)[0].toLowerCase();
772
+ return SUPPORTED_LANG_CODES.indexOf(primary) >= 0 ? primary : void 0;
773
+ }
774
+ function getLanguageCode() {
775
+ const candidates = detectCandidates();
776
+ for (const tag of candidates) {
777
+ const code = normalizeLangTag(tag);
778
+ if (code) return code;
779
+ }
780
+ return "en";
781
+ }
782
+ var LANGUAGE_MAP = {
783
+ // 英语
784
+ "en": "EN",
785
+ // 中文(简体)
786
+ "zh": "CN_S",
787
+ "zh-cn": "CN_S",
788
+ "zh-sg": "CN_S",
789
+ "zh-my": "CN_S",
790
+ "zh-hans": "CN_S",
791
+ "zh-hans-cn": "CN_S",
792
+ "zh-hans-sg": "CN_S",
793
+ "zh-hans-my": "CN_S",
794
+ // 中文(繁体)
795
+ "zh-tw": "CN_T",
796
+ "zh-hk": "CN_T",
797
+ "zh-mo": "CN_T",
798
+ "zh-hant": "CN_T",
799
+ "zh-hant-tw": "CN_T",
800
+ "zh-hant-hk": "CN_T",
801
+ "zh-hant-mo": "CN_T",
802
+ // 德语
803
+ "de": "DE",
804
+ // 法语
805
+ "fr": "FR",
806
+ // 西班牙语
807
+ "es": "ES",
808
+ // 日语
809
+ "ja": "JA",
810
+ // 韩语
811
+ "ko": "KO",
812
+ // 葡萄牙语
813
+ "pt": "PT",
814
+ // 意大利语
815
+ "it": "IT",
816
+ // 印地语
817
+ "hi": "HI",
818
+ // 孟加拉语
819
+ "bn": "BN",
820
+ // 乌尔都语
821
+ "ur": "UR",
822
+ // 阿拉伯语
823
+ "ar": "AR",
824
+ // 土耳其语
825
+ "tr": "TR",
826
+ // 俄语
827
+ "ru": "RU",
828
+ // 马来语
829
+ "ms": "MS",
830
+ // 印尼语
831
+ "id": "ID"
832
+ };
833
+ var SUPPORTED_PROJECT_LANGUAGES = /* @__PURE__ */ new Set([
834
+ "EN",
835
+ "CN_S",
836
+ "CN_T",
837
+ "DE",
838
+ "FR",
839
+ "ES",
840
+ "JA",
841
+ "KO",
842
+ "PT",
843
+ "IT",
844
+ "HI",
845
+ "BN",
846
+ "UR",
847
+ "AR",
848
+ "TR",
849
+ "RU",
850
+ "MS",
851
+ "IN",
852
+ "ID"
853
+ ]);
854
+ function resolveLanguageTag(tag) {
855
+ if (!tag || typeof tag !== "string") return void 0;
856
+ const normalized = tag.trim();
857
+ if (!normalized) return void 0;
858
+ const projectCode = normalized.toUpperCase().replace(/-/g, "_");
859
+ if (SUPPORTED_PROJECT_LANGUAGES.has(projectCode)) return projectCode;
860
+ const lower = normalized.toLowerCase();
861
+ if (LANGUAGE_MAP[lower]) return LANGUAGE_MAP[lower];
862
+ const primary = lower.split(/[-_]/)[0];
863
+ return LANGUAGE_MAP[primary];
864
+ }
865
+ function getLanguage() {
866
+ const candidates = detectCandidates();
867
+ for (const tag of candidates) {
868
+ const code = resolveLanguageTag(tag);
869
+ if (code) return code;
870
+ }
871
+ return "EN";
872
+ }
873
+
684
874
  // src/theme-config.ts
685
875
  function extractDefaultsFromSchema(schema) {
686
876
  if (!schema || typeof schema !== "object") return void 0;
@@ -699,13 +889,6 @@
699
889
  }
700
890
  return void 0;
701
891
  }
702
- function getLanguageCode() {
703
- try {
704
- return (navigator.language || "en").split("-")[0].toLowerCase();
705
- } catch (e) {
706
- return "en";
707
- }
708
- }
709
892
  function lookupI18nRuntime(runtime, key, langCode) {
710
893
  var _a, _b;
711
894
  const map = runtime[key];
@@ -771,6 +954,7 @@
771
954
  }
772
955
  if (i18nRecord) {
773
956
  const langCode = getLanguageCode();
957
+ console.log("detect target langCode", langCode);
774
958
  resolveI18nRefs(mergedConfig, i18nRecord, langCode);
775
959
  }
776
960
  resolveImagePaths(mergedConfig, assetRecordByPath);
@@ -1576,6 +1760,31 @@
1576
1760
  static isGoogle() {
1577
1761
  return _sdk.getCurChannel() === "google";
1578
1762
  }
1763
+ /**
1764
+ * 获取当前用户语言的 i18n 简码。
1765
+ * 多源探测(querystring → localStorage → cookie → navigator → html tag)。
1766
+ *
1767
+ * @returns i18n 简码:en | zh | ja | ko | de | fr | es | pt | it | hi | bn | ur | ar | tr | ru | ms | id,默认 'en'
1768
+ *
1769
+ * @example
1770
+ * const langCode = sdk.getLanguageCode(); // 'zh'
1771
+ */
1772
+ static getLanguageCode() {
1773
+ return getLanguageCode();
1774
+ }
1775
+ /**
1776
+ * 获取当前用户语言的业务代号。
1777
+ * 多源探测(querystring → localStorage → cookie → navigator → html tag),
1778
+ * 并映射到业务语言代号(区分简繁中文)。
1779
+ *
1780
+ * @returns 业务代号:EN | CN_S | CN_T | DE | FR | ES | JA | KO | PT | IT | HI | BN | UR | AR | TR | RU | MS | IN | ID,默认 'EN'
1781
+ *
1782
+ * @example
1783
+ * const lang = sdk.getLanguage(); // 'CN_S'
1784
+ */
1785
+ static getLanguage() {
1786
+ return getLanguage();
1787
+ }
1579
1788
  /**
1580
1789
  * 设置主题配置,支持传入多份 JSON5 配置。
1581
1790
  * 每一份配置都可以是 JSON Schema(带 $schema 字段),会自动提取其中的 default 值。
@@ -1638,7 +1847,7 @@
1638
1847
  }
1639
1848
  };
1640
1849
  /** Current version of the SDK */
1641
- _sdk.version = "1.0.18-beta.1";
1850
+ _sdk.version = "1.0.18-beta.2";
1642
1851
  /** Current maximum width of the playable ad container in pixels */
1643
1852
  _sdk.maxWidth = Math.floor(window.innerWidth);
1644
1853
  /** Current maximum height of the playable ad container in pixels */
package/dist/index.d.mts CHANGED
@@ -262,6 +262,27 @@ declare class sdk {
262
262
  * 判断是否是 Google
263
263
  */
264
264
  static isGoogle(): boolean;
265
+ /**
266
+ * 获取当前用户语言的 i18n 简码。
267
+ * 多源探测(querystring → localStorage → cookie → navigator → html tag)。
268
+ *
269
+ * @returns i18n 简码:en | zh | ja | ko | de | fr | es | pt | it | hi | bn | ur | ar | tr | ru | ms | id,默认 'en'
270
+ *
271
+ * @example
272
+ * const langCode = sdk.getLanguageCode(); // 'zh'
273
+ */
274
+ static getLanguageCode(): string;
275
+ /**
276
+ * 获取当前用户语言的业务代号。
277
+ * 多源探测(querystring → localStorage → cookie → navigator → html tag),
278
+ * 并映射到业务语言代号(区分简繁中文)。
279
+ *
280
+ * @returns 业务代号:EN | CN_S | CN_T | DE | FR | ES | JA | KO | PT | IT | HI | BN | UR | AR | TR | RU | MS | IN | ID,默认 'EN'
281
+ *
282
+ * @example
283
+ * const lang = sdk.getLanguage(); // 'CN_S'
284
+ */
285
+ static getLanguage(): string;
265
286
  /**
266
287
  * 设置主题配置,支持传入多份 JSON5 配置。
267
288
  * 每一份配置都可以是 JSON Schema(带 $schema 字段),会自动提取其中的 default 值。
@@ -336,4 +357,31 @@ declare function hideWechatGuide(): void;
336
357
  */
337
358
  declare function removeWechatGuide(): void;
338
359
 
339
- export { type CursorOptions, type I18nRecord, sdk as default, disableCustomCursor, enableCustomCursor, hideWechatGuide, removeWechatGuide, sdk, showWechatGuide };
360
+ /**
361
+ * 语言检测模块(零外部依赖)。
362
+ *
363
+ * 提供两个层级的语言获取:
364
+ * - getLanguageCode() → i18n 简码(en / zh / ja / ko …),用于 i18n.runtime 查表
365
+ * - getLanguage() → 业务语言代号(EN / CN_S / CN_T / DE …),用于游戏内资源选择
366
+ *
367
+ * 探测优先级(与 i18next-browser-languagedetector 保持一致):
368
+ * 1. URL querystring (?lng=xx)
369
+ * 2. localStorage (i18nextLng)
370
+ * 3. cookie (i18next)
371
+ * 4. navigator.languages / navigator.language
372
+ * 5. <html lang="xx">
373
+ */
374
+ /**
375
+ * 多源探测当前用户语言,并归一化为 i18n 简码。
376
+ *
377
+ * @returns i18n 简码:en | zh | ja | ko | de | fr | es | pt | it | hi | bn | ur | ar | tr | ru | ms | id,默认 'en'
378
+ */
379
+ declare function getLanguageCode(): string;
380
+ /**
381
+ * 多源探测当前用户语言,返回业务语言代号。
382
+ *
383
+ * @returns 业务代号:EN | CN_S | CN_T | DE | FR | ES | JA | KO | PT | IT | HI | BN | UR | AR | TR | RU | MS | IN | ID,默认 'EN'
384
+ */
385
+ declare function getLanguage(): string;
386
+
387
+ export { type CursorOptions, type I18nRecord, sdk as default, disableCustomCursor, enableCustomCursor, getLanguage, getLanguageCode, hideWechatGuide, removeWechatGuide, sdk, showWechatGuide };
package/dist/index.d.ts CHANGED
@@ -262,6 +262,27 @@ declare class sdk {
262
262
  * 判断是否是 Google
263
263
  */
264
264
  static isGoogle(): boolean;
265
+ /**
266
+ * 获取当前用户语言的 i18n 简码。
267
+ * 多源探测(querystring → localStorage → cookie → navigator → html tag)。
268
+ *
269
+ * @returns i18n 简码:en | zh | ja | ko | de | fr | es | pt | it | hi | bn | ur | ar | tr | ru | ms | id,默认 'en'
270
+ *
271
+ * @example
272
+ * const langCode = sdk.getLanguageCode(); // 'zh'
273
+ */
274
+ static getLanguageCode(): string;
275
+ /**
276
+ * 获取当前用户语言的业务代号。
277
+ * 多源探测(querystring → localStorage → cookie → navigator → html tag),
278
+ * 并映射到业务语言代号(区分简繁中文)。
279
+ *
280
+ * @returns 业务代号:EN | CN_S | CN_T | DE | FR | ES | JA | KO | PT | IT | HI | BN | UR | AR | TR | RU | MS | IN | ID,默认 'EN'
281
+ *
282
+ * @example
283
+ * const lang = sdk.getLanguage(); // 'CN_S'
284
+ */
285
+ static getLanguage(): string;
265
286
  /**
266
287
  * 设置主题配置,支持传入多份 JSON5 配置。
267
288
  * 每一份配置都可以是 JSON Schema(带 $schema 字段),会自动提取其中的 default 值。
@@ -336,4 +357,31 @@ declare function hideWechatGuide(): void;
336
357
  */
337
358
  declare function removeWechatGuide(): void;
338
359
 
339
- export { type CursorOptions, type I18nRecord, sdk as default, disableCustomCursor, enableCustomCursor, hideWechatGuide, removeWechatGuide, sdk, showWechatGuide };
360
+ /**
361
+ * 语言检测模块(零外部依赖)。
362
+ *
363
+ * 提供两个层级的语言获取:
364
+ * - getLanguageCode() → i18n 简码(en / zh / ja / ko …),用于 i18n.runtime 查表
365
+ * - getLanguage() → 业务语言代号(EN / CN_S / CN_T / DE …),用于游戏内资源选择
366
+ *
367
+ * 探测优先级(与 i18next-browser-languagedetector 保持一致):
368
+ * 1. URL querystring (?lng=xx)
369
+ * 2. localStorage (i18nextLng)
370
+ * 3. cookie (i18next)
371
+ * 4. navigator.languages / navigator.language
372
+ * 5. <html lang="xx">
373
+ */
374
+ /**
375
+ * 多源探测当前用户语言,并归一化为 i18n 简码。
376
+ *
377
+ * @returns i18n 简码:en | zh | ja | ko | de | fr | es | pt | it | hi | bn | ur | ar | tr | ru | ms | id,默认 'en'
378
+ */
379
+ declare function getLanguageCode(): string;
380
+ /**
381
+ * 多源探测当前用户语言,返回业务语言代号。
382
+ *
383
+ * @returns 业务代号:EN | CN_S | CN_T | DE | FR | ES | JA | KO | PT | IT | HI | BN | UR | AR | TR | RU | MS | IN | ID,默认 'EN'
384
+ */
385
+ declare function getLanguage(): string;
386
+
387
+ export { type CursorOptions, type I18nRecord, sdk as default, disableCustomCursor, enableCustomCursor, getLanguage, getLanguageCode, hideWechatGuide, removeWechatGuide, sdk, showWechatGuide };
package/dist/index.js CHANGED
@@ -23,6 +23,8 @@ __export(index_exports, {
23
23
  default: () => sdk,
24
24
  disableCustomCursor: () => disableCustomCursor,
25
25
  enableCustomCursor: () => enableCustomCursor,
26
+ getLanguage: () => getLanguage,
27
+ getLanguageCode: () => getLanguageCode,
26
28
  hideWechatGuide: () => hideWechatGuide,
27
29
  removeWechatGuide: () => removeWechatGuide,
28
30
  sdk: () => sdk,
@@ -475,6 +477,196 @@ function removeWechatGuide() {
475
477
  }
476
478
  }
477
479
 
480
+ // src/language.ts
481
+ function readQuerystring(key) {
482
+ try {
483
+ if (typeof window === "undefined" || !window.location) return void 0;
484
+ const search = window.location.search || "";
485
+ if (!search) return void 0;
486
+ if (typeof URLSearchParams !== "undefined") {
487
+ const params = new URLSearchParams(search);
488
+ return params.get(key) || void 0;
489
+ }
490
+ const re = new RegExp("[?&]" + key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + "=([^&#]*)");
491
+ const m = re.exec(search);
492
+ return m ? decodeURIComponent(m[1]) : void 0;
493
+ } catch (e) {
494
+ return void 0;
495
+ }
496
+ }
497
+ function readLocalStorage(key) {
498
+ try {
499
+ if (typeof localStorage === "undefined") return void 0;
500
+ return localStorage.getItem(key) || void 0;
501
+ } catch (e) {
502
+ return void 0;
503
+ }
504
+ }
505
+ function readCookie(name) {
506
+ try {
507
+ if (typeof document === "undefined" || !document.cookie) return void 0;
508
+ const re = new RegExp("(?:^|; )" + name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + "=([^;]*)");
509
+ const m = re.exec(document.cookie);
510
+ return m ? decodeURIComponent(m[1]) : void 0;
511
+ } catch (e) {
512
+ return void 0;
513
+ }
514
+ }
515
+ function readNavigatorLanguages() {
516
+ try {
517
+ if (typeof navigator === "undefined") return [];
518
+ const list = [];
519
+ const langs = navigator.languages;
520
+ if (Array.isArray(langs)) list.push(...langs);
521
+ if (navigator.language) list.push(navigator.language);
522
+ const userLang = navigator.userLanguage || navigator.browserLanguage;
523
+ if (userLang) list.push(userLang);
524
+ return list.filter(Boolean);
525
+ } catch (e) {
526
+ return [];
527
+ }
528
+ }
529
+ function readHtmlTag() {
530
+ try {
531
+ if (typeof document === "undefined" || !document.documentElement) return void 0;
532
+ return document.documentElement.lang || void 0;
533
+ } catch (e) {
534
+ return void 0;
535
+ }
536
+ }
537
+ function detectCandidates() {
538
+ return [
539
+ readQuerystring("lng"),
540
+ readLocalStorage("i18nextLng"),
541
+ readCookie("i18next"),
542
+ ...readNavigatorLanguages(),
543
+ readHtmlTag()
544
+ ];
545
+ }
546
+ var SUPPORTED_LANG_CODES = [
547
+ "en",
548
+ "zh",
549
+ "ja",
550
+ "ko",
551
+ "de",
552
+ "fr",
553
+ "es",
554
+ "pt",
555
+ "it",
556
+ "hi",
557
+ "bn",
558
+ "ur",
559
+ "ar",
560
+ "tr",
561
+ "ru",
562
+ "ms",
563
+ "id"
564
+ ];
565
+ function normalizeLangTag(tag) {
566
+ if (!tag || typeof tag !== "string") return void 0;
567
+ const primary = tag.split(/[-_]/)[0].toLowerCase();
568
+ return SUPPORTED_LANG_CODES.indexOf(primary) >= 0 ? primary : void 0;
569
+ }
570
+ function getLanguageCode() {
571
+ const candidates = detectCandidates();
572
+ for (const tag of candidates) {
573
+ const code = normalizeLangTag(tag);
574
+ if (code) return code;
575
+ }
576
+ return "en";
577
+ }
578
+ var LANGUAGE_MAP = {
579
+ // 英语
580
+ "en": "EN",
581
+ // 中文(简体)
582
+ "zh": "CN_S",
583
+ "zh-cn": "CN_S",
584
+ "zh-sg": "CN_S",
585
+ "zh-my": "CN_S",
586
+ "zh-hans": "CN_S",
587
+ "zh-hans-cn": "CN_S",
588
+ "zh-hans-sg": "CN_S",
589
+ "zh-hans-my": "CN_S",
590
+ // 中文(繁体)
591
+ "zh-tw": "CN_T",
592
+ "zh-hk": "CN_T",
593
+ "zh-mo": "CN_T",
594
+ "zh-hant": "CN_T",
595
+ "zh-hant-tw": "CN_T",
596
+ "zh-hant-hk": "CN_T",
597
+ "zh-hant-mo": "CN_T",
598
+ // 德语
599
+ "de": "DE",
600
+ // 法语
601
+ "fr": "FR",
602
+ // 西班牙语
603
+ "es": "ES",
604
+ // 日语
605
+ "ja": "JA",
606
+ // 韩语
607
+ "ko": "KO",
608
+ // 葡萄牙语
609
+ "pt": "PT",
610
+ // 意大利语
611
+ "it": "IT",
612
+ // 印地语
613
+ "hi": "HI",
614
+ // 孟加拉语
615
+ "bn": "BN",
616
+ // 乌尔都语
617
+ "ur": "UR",
618
+ // 阿拉伯语
619
+ "ar": "AR",
620
+ // 土耳其语
621
+ "tr": "TR",
622
+ // 俄语
623
+ "ru": "RU",
624
+ // 马来语
625
+ "ms": "MS",
626
+ // 印尼语
627
+ "id": "ID"
628
+ };
629
+ var SUPPORTED_PROJECT_LANGUAGES = /* @__PURE__ */ new Set([
630
+ "EN",
631
+ "CN_S",
632
+ "CN_T",
633
+ "DE",
634
+ "FR",
635
+ "ES",
636
+ "JA",
637
+ "KO",
638
+ "PT",
639
+ "IT",
640
+ "HI",
641
+ "BN",
642
+ "UR",
643
+ "AR",
644
+ "TR",
645
+ "RU",
646
+ "MS",
647
+ "IN",
648
+ "ID"
649
+ ]);
650
+ function resolveLanguageTag(tag) {
651
+ if (!tag || typeof tag !== "string") return void 0;
652
+ const normalized = tag.trim();
653
+ if (!normalized) return void 0;
654
+ const projectCode = normalized.toUpperCase().replace(/-/g, "_");
655
+ if (SUPPORTED_PROJECT_LANGUAGES.has(projectCode)) return projectCode;
656
+ const lower = normalized.toLowerCase();
657
+ if (LANGUAGE_MAP[lower]) return LANGUAGE_MAP[lower];
658
+ const primary = lower.split(/[-_]/)[0];
659
+ return LANGUAGE_MAP[primary];
660
+ }
661
+ function getLanguage() {
662
+ const candidates = detectCandidates();
663
+ for (const tag of candidates) {
664
+ const code = resolveLanguageTag(tag);
665
+ if (code) return code;
666
+ }
667
+ return "EN";
668
+ }
669
+
478
670
  // src/theme-config.ts
479
671
  function extractDefaultsFromSchema(schema) {
480
672
  if (!schema || typeof schema !== "object") return void 0;
@@ -493,13 +685,6 @@ function extractDefaultsFromSchema(schema) {
493
685
  }
494
686
  return void 0;
495
687
  }
496
- function getLanguageCode() {
497
- try {
498
- return (navigator.language || "en").split("-")[0].toLowerCase();
499
- } catch (e) {
500
- return "en";
501
- }
502
- }
503
688
  function lookupI18nRuntime(runtime, key, langCode) {
504
689
  var _a, _b;
505
690
  const map = runtime[key];
@@ -565,6 +750,7 @@ function setConfig(configList, config) {
565
750
  }
566
751
  if (i18nRecord) {
567
752
  const langCode = getLanguageCode();
753
+ console.log("detect target langCode", langCode);
568
754
  resolveI18nRefs(mergedConfig, i18nRecord, langCode);
569
755
  }
570
756
  resolveImagePaths(mergedConfig, assetRecordByPath);
@@ -1370,6 +1556,31 @@ var _sdk = class _sdk {
1370
1556
  static isGoogle() {
1371
1557
  return _sdk.getCurChannel() === "google";
1372
1558
  }
1559
+ /**
1560
+ * 获取当前用户语言的 i18n 简码。
1561
+ * 多源探测(querystring → localStorage → cookie → navigator → html tag)。
1562
+ *
1563
+ * @returns i18n 简码:en | zh | ja | ko | de | fr | es | pt | it | hi | bn | ur | ar | tr | ru | ms | id,默认 'en'
1564
+ *
1565
+ * @example
1566
+ * const langCode = sdk.getLanguageCode(); // 'zh'
1567
+ */
1568
+ static getLanguageCode() {
1569
+ return getLanguageCode();
1570
+ }
1571
+ /**
1572
+ * 获取当前用户语言的业务代号。
1573
+ * 多源探测(querystring → localStorage → cookie → navigator → html tag),
1574
+ * 并映射到业务语言代号(区分简繁中文)。
1575
+ *
1576
+ * @returns 业务代号:EN | CN_S | CN_T | DE | FR | ES | JA | KO | PT | IT | HI | BN | UR | AR | TR | RU | MS | IN | ID,默认 'EN'
1577
+ *
1578
+ * @example
1579
+ * const lang = sdk.getLanguage(); // 'CN_S'
1580
+ */
1581
+ static getLanguage() {
1582
+ return getLanguage();
1583
+ }
1373
1584
  /**
1374
1585
  * 设置主题配置,支持传入多份 JSON5 配置。
1375
1586
  * 每一份配置都可以是 JSON Schema(带 $schema 字段),会自动提取其中的 default 值。
@@ -1432,7 +1643,7 @@ var _sdk = class _sdk {
1432
1643
  }
1433
1644
  };
1434
1645
  /** Current version of the SDK */
1435
- _sdk.version = "1.0.18-beta.1";
1646
+ _sdk.version = "1.0.18-beta.2";
1436
1647
  /** Current maximum width of the playable ad container in pixels */
1437
1648
  _sdk.maxWidth = Math.floor(window.innerWidth);
1438
1649
  /** Current maximum height of the playable ad container in pixels */
@@ -1462,6 +1673,8 @@ window.PlayableSDK = sdk;
1462
1673
  0 && (module.exports = {
1463
1674
  disableCustomCursor,
1464
1675
  enableCustomCursor,
1676
+ getLanguage,
1677
+ getLanguageCode,
1465
1678
  hideWechatGuide,
1466
1679
  removeWechatGuide,
1467
1680
  sdk,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@playcraft/adsdk",
3
- "version": "1.0.18-beta.1",
3
+ "version": "1.0.18-beta.2",
4
4
  "description": "统一的 Playable SDK,支持 MRAID、Google、Facebook、Vungle、BigoAds 等多广告渠道",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/esm/index.js",