@tramvai/module-client-hints 7.2.2 → 7.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/README.md +18 -1
- package/lib/browser/language.browser.js +19 -0
- package/lib/browser/language.d.ts +4 -0
- package/lib/browser.js +1 -1
- package/lib/server/language.d.ts +8 -0
- package/lib/server/language.es.js +36 -0
- package/lib/server/language.js +44 -0
- package/lib/server.es.js +1 -1
- package/lib/server.js +1 -0
- package/lib/shared/providers.browser.browser.js +2 -0
- package/lib/shared/providers.server.es.js +2 -0
- package/lib/shared/providers.server.js +2 -0
- package/lib/tokens.browser.js +8 -1
- package/lib/tokens.d.ts +9 -0
- package/lib/tokens.es.js +8 -1
- package/lib/tokens.js +8 -0
- package/package.json +9 -9
package/README.md
CHANGED
|
@@ -225,7 +225,7 @@ const App = () => {
|
|
|
225
225
|
|
|
226
226
|
Object as a result of parsing user-agent headers or string with [@tinkoff/user-agent](../libs/user-agent.md). Parsing happens only on server-side and parsed info is reused on client-side. If header info is unavailable then takes user-agent and parse it with ua-parser-js lib.
|
|
227
227
|
|
|
228
|
-
|
|
228
|
+
#### Example
|
|
229
229
|
|
|
230
230
|
Here userAgent is used to determine the mobile device.
|
|
231
231
|
|
|
@@ -260,6 +260,23 @@ export const IS_MOBILE_BANNER = createToken<boolean>('IS_MOBILE_BANNER');
|
|
|
260
260
|
export class BannerModule {}
|
|
261
261
|
```
|
|
262
262
|
|
|
263
|
+
### `USER_LANGUAGE_TOKEN`:
|
|
264
|
+
|
|
265
|
+
`USER_LANGUAGE_TOKEN` is responsible for determining the user's preferred languages based on this algorithm:
|
|
266
|
+
|
|
267
|
+
- **Server:** Parses `Accept-Language` header from the HTTP request
|
|
268
|
+
- **Client:** Reads `navigator.languages` array
|
|
269
|
+
|
|
270
|
+
Example `Accept-Language` header:
|
|
271
|
+
|
|
272
|
+
```
|
|
273
|
+
Accept-Language: en-US,en;q=0.9,ru;q=0.8
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
Parsed to: `['en-US', 'en', 'ru']`
|
|
277
|
+
|
|
278
|
+
The language detection algorithm checks these values against `availableLanguages` to find the best match.
|
|
279
|
+
|
|
263
280
|
## Env variables
|
|
264
281
|
|
|
265
282
|
### TRAMVAI_USER_AGENT_CACHE_MAX
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import uniq from '@tinkoff/utils/array/uniq';
|
|
2
|
+
import { provide } from '@tramvai/core';
|
|
3
|
+
import { USER_LANGUAGE_TOKEN } from '../tokens.browser.js';
|
|
4
|
+
|
|
5
|
+
const browserUserLanguageProvider = provide({
|
|
6
|
+
provide: USER_LANGUAGE_TOKEN,
|
|
7
|
+
useFactory: () => {
|
|
8
|
+
if (typeof navigator === 'undefined' || !navigator.languages) {
|
|
9
|
+
return ['ru'];
|
|
10
|
+
}
|
|
11
|
+
const languages = Array.from(navigator.languages).map((lang) => {
|
|
12
|
+
// Normalize language codes to ISO-639-1
|
|
13
|
+
return lang.toLowerCase().split('-')[0];
|
|
14
|
+
});
|
|
15
|
+
return uniq(languages);
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
export { browserUserLanguageProvider };
|
package/lib/browser.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { declareModule } from '@tramvai/core';
|
|
2
2
|
import { browserProviders } from './shared/providers.browser.browser.js';
|
|
3
|
-
export { PARSER_CLIENT_HINTS_ENABLED, USER_AGENT_PARSER_EXTENSIONS_TOKEN, USER_AGENT_TOKEN } from './tokens.browser.js';
|
|
3
|
+
export { PARSER_CLIENT_HINTS_ENABLED, USER_AGENT_PARSER_EXTENSIONS_TOKEN, USER_AGENT_TOKEN, USER_LANGUAGE_TOKEN } from './tokens.browser.js';
|
|
4
4
|
export { displayMode, fromClientHints, isRetina, isSupposed } from './shared/stores/mediaCheckers.browser.js';
|
|
5
5
|
export { useDisplayMode, useFromClientHints, useIsRetina, useIsSupposed, useMedia } from './shared/stores/mediaSelectors.browser.js';
|
|
6
6
|
export { MediaStore, setMedia } from './shared/stores/media.browser.js';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare const serverUserLanguageProvider: import("@tramvai/core").Provider<{
|
|
2
|
+
requestManager: import("@tramvai/tokens-common").RequestManager & {
|
|
3
|
+
__type?: "base token" | undefined;
|
|
4
|
+
};
|
|
5
|
+
}, string[] & {
|
|
6
|
+
__type?: "base token" | undefined;
|
|
7
|
+
}>;
|
|
8
|
+
//# sourceMappingURL=language.d.ts.map
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import uniq from '@tinkoff/utils/array/uniq';
|
|
2
|
+
import { provide } from '@tramvai/core';
|
|
3
|
+
import { REQUEST_MANAGER_TOKEN } from '@tramvai/tokens-common';
|
|
4
|
+
import { USER_LANGUAGE_TOKEN } from '../tokens.es.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Parse Accept-Language header into array of language codes, normalized to ISO-639-1
|
|
8
|
+
* Format: "en-US,en;q=0.9,ru;q=0.8" => ["en", "ru"]
|
|
9
|
+
*/
|
|
10
|
+
function parseAcceptLanguage(header) {
|
|
11
|
+
return header
|
|
12
|
+
.split(',')
|
|
13
|
+
.filter(Boolean)
|
|
14
|
+
.map((langAndQuality) => {
|
|
15
|
+
const [lang, qualityStr = 'q=1.0'] = langAndQuality.trim().split(';');
|
|
16
|
+
const [_, quality] = qualityStr.split('=');
|
|
17
|
+
return { lang: lang.trim(), quality: parseFloat(quality) };
|
|
18
|
+
})
|
|
19
|
+
.sort((a, b) => b.quality - a.quality)
|
|
20
|
+
.map((item) => item.lang.toLowerCase().split('-')[0]);
|
|
21
|
+
}
|
|
22
|
+
const serverUserLanguageProvider = provide({
|
|
23
|
+
provide: USER_LANGUAGE_TOKEN,
|
|
24
|
+
useFactory: ({ requestManager }) => {
|
|
25
|
+
const acceptLanguage = requestManager.getHeader('accept-language');
|
|
26
|
+
if (typeof acceptLanguage !== 'string') {
|
|
27
|
+
return ['ru'];
|
|
28
|
+
}
|
|
29
|
+
return uniq(parseAcceptLanguage(acceptLanguage));
|
|
30
|
+
},
|
|
31
|
+
deps: {
|
|
32
|
+
requestManager: REQUEST_MANAGER_TOKEN,
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
export { serverUserLanguageProvider };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var uniq = require('@tinkoff/utils/array/uniq');
|
|
6
|
+
var core = require('@tramvai/core');
|
|
7
|
+
var tokensCommon = require('@tramvai/tokens-common');
|
|
8
|
+
var tokens = require('../tokens.js');
|
|
9
|
+
|
|
10
|
+
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
11
|
+
|
|
12
|
+
var uniq__default = /*#__PURE__*/_interopDefaultLegacy(uniq);
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Parse Accept-Language header into array of language codes, normalized to ISO-639-1
|
|
16
|
+
* Format: "en-US,en;q=0.9,ru;q=0.8" => ["en", "ru"]
|
|
17
|
+
*/
|
|
18
|
+
function parseAcceptLanguage(header) {
|
|
19
|
+
return header
|
|
20
|
+
.split(',')
|
|
21
|
+
.filter(Boolean)
|
|
22
|
+
.map((langAndQuality) => {
|
|
23
|
+
const [lang, qualityStr = 'q=1.0'] = langAndQuality.trim().split(';');
|
|
24
|
+
const [_, quality] = qualityStr.split('=');
|
|
25
|
+
return { lang: lang.trim(), quality: parseFloat(quality) };
|
|
26
|
+
})
|
|
27
|
+
.sort((a, b) => b.quality - a.quality)
|
|
28
|
+
.map((item) => item.lang.toLowerCase().split('-')[0]);
|
|
29
|
+
}
|
|
30
|
+
const serverUserLanguageProvider = core.provide({
|
|
31
|
+
provide: tokens.USER_LANGUAGE_TOKEN,
|
|
32
|
+
useFactory: ({ requestManager }) => {
|
|
33
|
+
const acceptLanguage = requestManager.getHeader('accept-language');
|
|
34
|
+
if (typeof acceptLanguage !== 'string') {
|
|
35
|
+
return ['ru'];
|
|
36
|
+
}
|
|
37
|
+
return uniq__default["default"](parseAcceptLanguage(acceptLanguage));
|
|
38
|
+
},
|
|
39
|
+
deps: {
|
|
40
|
+
requestManager: tokensCommon.REQUEST_MANAGER_TOKEN,
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
exports.serverUserLanguageProvider = serverUserLanguageProvider;
|
package/lib/server.es.js
CHANGED
|
@@ -2,7 +2,7 @@ import { declareModule, provide } from '@tramvai/core';
|
|
|
2
2
|
import { serverProviders } from './shared/providers.server.es.js';
|
|
3
3
|
import { serverUserAgentProviders } from './server/userAgent.es.js';
|
|
4
4
|
import { PARSER_CLIENT_HINTS_ENABLED } from './tokens.es.js';
|
|
5
|
-
export { PARSER_CLIENT_HINTS_ENABLED, USER_AGENT_PARSER_EXTENSIONS_TOKEN, USER_AGENT_TOKEN } from './tokens.es.js';
|
|
5
|
+
export { PARSER_CLIENT_HINTS_ENABLED, USER_AGENT_PARSER_EXTENSIONS_TOKEN, USER_AGENT_TOKEN, USER_LANGUAGE_TOKEN } from './tokens.es.js';
|
|
6
6
|
export { displayMode, fromClientHints, isRetina, isSupposed } from './shared/stores/mediaCheckers.es.js';
|
|
7
7
|
export { useDisplayMode, useFromClientHints, useIsRetina, useIsSupposed, useMedia } from './shared/stores/mediaSelectors.es.js';
|
|
8
8
|
export { MediaStore, setMedia } from './shared/stores/media.es.js';
|
package/lib/server.js
CHANGED
|
@@ -28,6 +28,7 @@ const ClientHintsModule = /* @__PURE__ */ core.declareModule({
|
|
|
28
28
|
exports.PARSER_CLIENT_HINTS_ENABLED = tokens.PARSER_CLIENT_HINTS_ENABLED;
|
|
29
29
|
exports.USER_AGENT_PARSER_EXTENSIONS_TOKEN = tokens.USER_AGENT_PARSER_EXTENSIONS_TOKEN;
|
|
30
30
|
exports.USER_AGENT_TOKEN = tokens.USER_AGENT_TOKEN;
|
|
31
|
+
exports.USER_LANGUAGE_TOKEN = tokens.USER_LANGUAGE_TOKEN;
|
|
31
32
|
exports.displayMode = mediaCheckers.displayMode;
|
|
32
33
|
exports.fromClientHints = mediaCheckers.fromClientHints;
|
|
33
34
|
exports.isRetina = mediaCheckers.isRetina;
|
|
@@ -3,12 +3,14 @@ import { commandLineListTokens } from '@tramvai/core';
|
|
|
3
3
|
import { CONTEXT_TOKEN, STORE_TOKEN } from '@tramvai/tokens-common';
|
|
4
4
|
import { COOKIE_MANAGER_TOKEN } from '@tramvai/tokens-cookie';
|
|
5
5
|
import { matchMediaCommand } from '../browser/matchMedia.browser.js';
|
|
6
|
+
import { browserUserLanguageProvider } from '../browser/language.browser.js';
|
|
6
7
|
import { USER_AGENT_TOKEN } from '../tokens.browser.js';
|
|
7
8
|
import { UserAgentStore } from './stores/userAgent.browser.js';
|
|
8
9
|
import { commonProviders } from './providers.browser.js';
|
|
9
10
|
|
|
10
11
|
const browserProviders = [
|
|
11
12
|
...commonProviders,
|
|
13
|
+
browserUserLanguageProvider,
|
|
12
14
|
provide({
|
|
13
15
|
provide: commandLineListTokens.clear,
|
|
14
16
|
multi: true,
|
|
@@ -3,10 +3,12 @@ import { commandLineListTokens } from '@tramvai/core';
|
|
|
3
3
|
import { CONTEXT_TOKEN } from '@tramvai/tokens-common';
|
|
4
4
|
import { COOKIE_MANAGER_TOKEN } from '@tramvai/tokens-cookie';
|
|
5
5
|
import { readMediaCommand } from '../server/readMedia.es.js';
|
|
6
|
+
import { serverUserLanguageProvider } from '../server/language.es.js';
|
|
6
7
|
import { commonProviders } from './providers.es.js';
|
|
7
8
|
|
|
8
9
|
const serverProviders = [
|
|
9
10
|
...commonProviders,
|
|
11
|
+
serverUserLanguageProvider,
|
|
10
12
|
provide({
|
|
11
13
|
provide: commandLineListTokens.resolveUserDeps,
|
|
12
14
|
multi: true,
|
|
@@ -7,10 +7,12 @@ var core = require('@tramvai/core');
|
|
|
7
7
|
var tokensCommon = require('@tramvai/tokens-common');
|
|
8
8
|
var tokensCookie = require('@tramvai/tokens-cookie');
|
|
9
9
|
var readMedia = require('../server/readMedia.js');
|
|
10
|
+
var language = require('../server/language.js');
|
|
10
11
|
var providers = require('./providers.js');
|
|
11
12
|
|
|
12
13
|
const serverProviders = [
|
|
13
14
|
...providers.commonProviders,
|
|
15
|
+
language.serverUserLanguageProvider,
|
|
14
16
|
dippy.provide({
|
|
15
17
|
provide: core.commandLineListTokens.resolveUserDeps,
|
|
16
18
|
multi: true,
|
package/lib/tokens.browser.js
CHANGED
|
@@ -3,5 +3,12 @@ import { createToken } from '@tinkoff/dippy';
|
|
|
3
3
|
const USER_AGENT_TOKEN = createToken('userAgent');
|
|
4
4
|
const USER_AGENT_PARSER_EXTENSIONS_TOKEN = createToken('userAgentParserExtensions', { multi: true });
|
|
5
5
|
const PARSER_CLIENT_HINTS_ENABLED = createToken('client-hints parserClientHints enabled');
|
|
6
|
+
/**
|
|
7
|
+
* Token for user language preferences
|
|
8
|
+
* On server: parsed from Accept-Language header
|
|
9
|
+
* On client: from navigator.languages
|
|
10
|
+
* Returns array of language codes in order of preference and normalized to ISO-639-1
|
|
11
|
+
*/
|
|
12
|
+
const USER_LANGUAGE_TOKEN = createToken('user language');
|
|
6
13
|
|
|
7
|
-
export { PARSER_CLIENT_HINTS_ENABLED, USER_AGENT_PARSER_EXTENSIONS_TOKEN, USER_AGENT_TOKEN };
|
|
14
|
+
export { PARSER_CLIENT_HINTS_ENABLED, USER_AGENT_PARSER_EXTENSIONS_TOKEN, USER_AGENT_TOKEN, USER_LANGUAGE_TOKEN };
|
package/lib/tokens.d.ts
CHANGED
|
@@ -10,4 +10,13 @@ export declare const PARSER_CLIENT_HINTS_ENABLED: (false & {
|
|
|
10
10
|
}) | (true & {
|
|
11
11
|
__type?: "base token" | undefined;
|
|
12
12
|
});
|
|
13
|
+
/**
|
|
14
|
+
* Token for user language preferences
|
|
15
|
+
* On server: parsed from Accept-Language header
|
|
16
|
+
* On client: from navigator.languages
|
|
17
|
+
* Returns array of language codes in order of preference and normalized to ISO-639-1
|
|
18
|
+
*/
|
|
19
|
+
export declare const USER_LANGUAGE_TOKEN: string[] & {
|
|
20
|
+
__type?: "base token" | undefined;
|
|
21
|
+
};
|
|
13
22
|
//# sourceMappingURL=tokens.d.ts.map
|
package/lib/tokens.es.js
CHANGED
|
@@ -3,5 +3,12 @@ import { createToken } from '@tinkoff/dippy';
|
|
|
3
3
|
const USER_AGENT_TOKEN = createToken('userAgent');
|
|
4
4
|
const USER_AGENT_PARSER_EXTENSIONS_TOKEN = createToken('userAgentParserExtensions', { multi: true });
|
|
5
5
|
const PARSER_CLIENT_HINTS_ENABLED = createToken('client-hints parserClientHints enabled');
|
|
6
|
+
/**
|
|
7
|
+
* Token for user language preferences
|
|
8
|
+
* On server: parsed from Accept-Language header
|
|
9
|
+
* On client: from navigator.languages
|
|
10
|
+
* Returns array of language codes in order of preference and normalized to ISO-639-1
|
|
11
|
+
*/
|
|
12
|
+
const USER_LANGUAGE_TOKEN = createToken('user language');
|
|
6
13
|
|
|
7
|
-
export { PARSER_CLIENT_HINTS_ENABLED, USER_AGENT_PARSER_EXTENSIONS_TOKEN, USER_AGENT_TOKEN };
|
|
14
|
+
export { PARSER_CLIENT_HINTS_ENABLED, USER_AGENT_PARSER_EXTENSIONS_TOKEN, USER_AGENT_TOKEN, USER_LANGUAGE_TOKEN };
|
package/lib/tokens.js
CHANGED
|
@@ -7,7 +7,15 @@ var dippy = require('@tinkoff/dippy');
|
|
|
7
7
|
const USER_AGENT_TOKEN = dippy.createToken('userAgent');
|
|
8
8
|
const USER_AGENT_PARSER_EXTENSIONS_TOKEN = dippy.createToken('userAgentParserExtensions', { multi: true });
|
|
9
9
|
const PARSER_CLIENT_HINTS_ENABLED = dippy.createToken('client-hints parserClientHints enabled');
|
|
10
|
+
/**
|
|
11
|
+
* Token for user language preferences
|
|
12
|
+
* On server: parsed from Accept-Language header
|
|
13
|
+
* On client: from navigator.languages
|
|
14
|
+
* Returns array of language codes in order of preference and normalized to ISO-639-1
|
|
15
|
+
*/
|
|
16
|
+
const USER_LANGUAGE_TOKEN = dippy.createToken('user language');
|
|
10
17
|
|
|
11
18
|
exports.PARSER_CLIENT_HINTS_ENABLED = PARSER_CLIENT_HINTS_ENABLED;
|
|
12
19
|
exports.USER_AGENT_PARSER_EXTENSIONS_TOKEN = USER_AGENT_PARSER_EXTENSIONS_TOKEN;
|
|
13
20
|
exports.USER_AGENT_TOKEN = USER_AGENT_TOKEN;
|
|
21
|
+
exports.USER_LANGUAGE_TOKEN = USER_LANGUAGE_TOKEN;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tramvai/module-client-hints",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.3.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "lib/server.js",
|
|
6
6
|
"module": "lib/server.es.js",
|
|
@@ -20,21 +20,21 @@
|
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"@tinkoff/env-validators": "0.6.1",
|
|
23
|
-
"@tinkoff/user-agent": "0.9.
|
|
23
|
+
"@tinkoff/user-agent": "0.9.9",
|
|
24
24
|
"@tinkoff/utils": "^2.1.2",
|
|
25
|
-
"@tramvai/module-metrics": "7.
|
|
25
|
+
"@tramvai/module-metrics": "7.3.0",
|
|
26
26
|
"@tramvai/safe-strings": "0.10.1",
|
|
27
|
-
"@tramvai/tokens-child-app": "7.
|
|
28
|
-
"@tramvai/tokens-common": "7.
|
|
29
|
-
"@tramvai/tokens-cookie": "7.
|
|
30
|
-
"@tramvai/tokens-render": "7.
|
|
27
|
+
"@tramvai/tokens-child-app": "7.3.0",
|
|
28
|
+
"@tramvai/tokens-common": "7.3.0",
|
|
29
|
+
"@tramvai/tokens-cookie": "7.3.0",
|
|
30
|
+
"@tramvai/tokens-render": "7.3.0",
|
|
31
31
|
"user-agent-data-types": "^0.3.1"
|
|
32
32
|
},
|
|
33
33
|
"peerDependencies": {
|
|
34
34
|
"@tinkoff/dippy": "0.13.1",
|
|
35
35
|
"@tinkoff/logger": "^0.10.516",
|
|
36
|
-
"@tramvai/core": "7.
|
|
37
|
-
"@tramvai/state": "7.
|
|
36
|
+
"@tramvai/core": "7.3.0",
|
|
37
|
+
"@tramvai/state": "7.3.0",
|
|
38
38
|
"react": ">=16.14.0",
|
|
39
39
|
"react-dom": ">=16.14.0",
|
|
40
40
|
"tslib": "^2.4.0"
|