probably-china 1.1.0 → 1.1.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.
Files changed (3) hide show
  1. package/README.md +8 -4
  2. package/dist/index.js +94 -37
  3. package/package.json +5 -2
package/README.md CHANGED
@@ -5,10 +5,14 @@
5
5
 
6
6
  This is a simple package for the browser environment that makes a quick guess
7
7
  to see if the current webpage is running in China, or if the user is from China,
8
- based on the available browser info. It currently uses two strategies:
9
-
10
- 1. Check if the current browser favors simplified Chinese or related languages.
11
- 2. Check if the current timezone is one of the Chinese timezones.
8
+ based on the available browser info. It currently uses the following strategies:
9
+
10
+ 1. Check if the current timezone is one of the Chinese timezones.
11
+ 2. Check if the current browser favors simplified Chinese or related languages
12
+ (unless a Taiwanese language tag comes first).
13
+ 3. Check if the user agent is an in-app browser or a browser that is mostly used in China
14
+ (such as WeChat, QQ, Alipay, Douyin, or HarmonyOS).
15
+ 4. Check if the user comes from a Chinese website (such as Baidu or Zhihu).
12
16
 
13
17
  The use case for this package is not to avoid showing information that are sensitive in China.
14
18
 
package/dist/index.js CHANGED
@@ -1,37 +1,94 @@
1
-
2
- const detectChinaLanguage = ["zh-CN", "zh-CHS", "zh-Hans", "zh-HK", "zh-MO"];
3
- const detectChinaTimeZone = ["Asia/Shanghai", "Asia/Urumqi", "Asia/Hong_Kong", "Asia/Macau"];
4
-
5
- function checkTimeZone() {
6
- if(typeof Intl !== "undefined") {
7
- const timeZone = new Intl.DateTimeFormat().resolvedOptions().timeZone;
8
- return detectChinaTimeZone.indexOf(timeZone) >= 0;
9
- } else {
10
- // legacy browser
11
- const str = new Date().toString();
12
- return str.indexOf("\u4e2d") > 0 || str.indexOf("\u9999") > 0;
13
- }
14
- }
15
-
16
- function checkChinaLanguages() {
17
- if(navigator.languages) { // https://caniuse.com/mdn-api_navigator_languages
18
- for(let i = 0; i < navigator.languages.length; i++) {
19
- const l = navigator.languages[i];
20
- // Some Taiwanese users might also have "zh-CN" in their language list.
21
- // We use the following criteria to eliminate such false-positive,
22
- // so that it returns false if "zh-TW" comes first in the language list.
23
- if(l === "zh-TW") return false;
24
- if(detectChinaLanguage.indexOf(l) >= 0) return true;
25
- }
26
- return false;
27
- }
28
- // legacy browser
29
- return detectChinaLanguage.indexOf(navigator.language) >= 0;
30
- }
31
-
32
- /**
33
- * A lucky guess that we are probably in China or facing a Chinese user.
34
- */
35
- export const probablyChina = checkTimeZone() || checkChinaLanguages();
36
-
37
- export default probablyChina;
1
+ const detectChinaRegion = ["cn", "hk", "mo"];
2
+ const detectChinaTimeZone = [
3
+ "Asia/Shanghai", "Asia/Urumqi", "Asia/Hong_Kong", "Asia/Macau",
4
+ // Legacy aliases, which may be returned as-is in some environments
5
+ "PRC", "Asia/Chongqing", "Asia/Chungking", "Asia/Harbin", "Asia/Kashgar", "Asia/Macao", "Hongkong",
6
+ ];
7
+
8
+ // In-app browsers or browsers that are mostly used in China only.
9
+ // Browsers with a large user base outside China (such as UC, Mi, HeyTap, vivo or Huawei) are intentionally excluded.
10
+ const detectChinaUserAgent = new RegExp([
11
+ "MicroMessenger", "MQQBrowser", "QQ/", "Quark", "baiduboxapp", "BIDUBrowser", "AlipayClient", "DingTalk",
12
+ "Weibo", "aweme", "NewsArticle", "Feishu", "SogouMobileBrowser", "MetaSr", "360SE", "360EE",
13
+ "2345Explorer", "LBBROWSER", "OpenHarmony", "ArkWeb",
14
+ ].join("|"));
15
+
16
+ const detectChinaReferrer = /^https?:\/\/([^/]+\.)?(baidu\.com|so\.com|sogou\.com|sm\.cn|cn\.bing\.com|weixin\.qq\.com|weixin110\.qq\.com|zhihu\.com|weibo\.com|weibo\.cn|xiaohongshu\.com|douyin\.com|bilibili\.com)(:\d+)?(\/|$)/i;
17
+
18
+ function checkTimeZone() {
19
+ if(typeof Intl !== "undefined") {
20
+ const timeZone = new Intl.DateTimeFormat().resolvedOptions().timeZone;
21
+ return detectChinaTimeZone.indexOf(timeZone) >= 0;
22
+ } else {
23
+ // legacy browser
24
+ const str = new Date().toString();
25
+ return str.indexOf("中") > 0 || str.indexOf("香") > 0;
26
+ }
27
+ }
28
+
29
+ /**
30
+ * Classify a language tag.
31
+ * @param {string} tag
32
+ * @returns {boolean | undefined} `true` for China, `false` for Taiwan, `undefined` if undecided.
33
+ */
34
+ function classifyLanguage(tag) {
35
+ // Old browsers (such as Safari < 10) report tags in lower case.
36
+ const parts = tag.toLowerCase().split("-");
37
+ if(parts[0] !== "zh") return undefined;
38
+
39
+ // Legacy Microsoft tags
40
+ if(parts[1] === "chs") return true;
41
+ if(parts[1] === "cht") return false;
42
+
43
+ let script = "", region = "";
44
+ for(let i = 1; i < parts.length; i++) {
45
+ if(parts[i].length === 4) script = parts[i];
46
+ else if(parts[i].length === 2) region = parts[i];
47
+ }
48
+
49
+ // Region takes precedence, so for example "zh-Hans-SG" is not China.
50
+ if(region === "tw") return false;
51
+ if(detectChinaRegion.indexOf(region) >= 0) return true;
52
+ if(region) return undefined;
53
+
54
+ // No region; "zh" and "zh-Hans" maximize to "zh-Hans-CN", while "zh-Hant" maximizes to "zh-Hant-TW".
55
+ if(script === "hant") return false;
56
+ return true;
57
+ }
58
+
59
+ function checkChinaLanguages() {
60
+ const list = [];
61
+ if(navigator.languages) { // https://caniuse.com/mdn-api_navigator_languages
62
+ for(let i = 0; i < navigator.languages.length; i++) list.push(navigator.languages[i]);
63
+ } else if(navigator.language) {
64
+ // legacy browser
65
+ list.push(navigator.language);
66
+ }
67
+ // The browser UI locale, which in some browsers (such as Firefox) can differ from the content language list.
68
+ if(typeof Intl !== "undefined") list.push(new Intl.DateTimeFormat().resolvedOptions().locale);
69
+
70
+ for(let i = 0; i < list.length; i++) {
71
+ // Some Taiwanese users might also have "zh-CN" in their language list.
72
+ // The first decisive tag wins, so that it returns false if "zh-TW" comes first in the language list.
73
+ const result = classifyLanguage(list[i]);
74
+ if(result !== undefined) return result;
75
+ }
76
+ return false;
77
+ }
78
+
79
+ function checkUserAgent() {
80
+ // False positive: navigator.userAgent exists in early Opera versions, but the compat data only records it since Opera 10.5.
81
+ // eslint-disable-next-line compat/compat
82
+ return detectChinaUserAgent.test(navigator.userAgent || "");
83
+ }
84
+
85
+ function checkReferrer() {
86
+ return typeof document !== "undefined" && detectChinaReferrer.test(document.referrer);
87
+ }
88
+
89
+ /**
90
+ * A lucky guess that we are probably in China or facing a Chinese user.
91
+ */
92
+ export const probablyChina = checkTimeZone() || checkChinaLanguages() || checkUserAgent() || checkReferrer();
93
+
94
+ export default probablyChina;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "probably-china",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
4
4
  "description": "A lucky guess that we are probably in China or facing a Chinese user.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -8,7 +8,10 @@
8
8
  "China"
9
9
  ],
10
10
  "author": "Mu-Tsun Tsai <don.m.t.tsai@gmail.com> (https://github.com/MuTsunTsai)",
11
- "repository": "github:MuTsunTsai/probably-china",
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "github:MuTsunTsai/probably-china"
14
+ },
12
15
  "license": "MIT",
13
16
  "files": [
14
17
  "dist"