@nimpl/i18n 1.1.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/ClientTranslation.d.ts +12 -0
- package/I18nProvider.d.ts +9 -0
- package/I18nTransmitter.d.ts +9 -0
- package/LICENSE +21 -0
- package/README.md +45 -0
- package/ServerTranslation.d.ts +12 -0
- package/createTranslation.d.ts +7 -0
- package/dist/ClientTranslation.js +15 -0
- package/dist/I18nProvider.js +15 -0
- package/dist/I18nTransmitter.js +45 -0
- package/dist/ServerTranslation.js +14 -0
- package/dist/configuration/CacheHandler.js +19 -0
- package/dist/configuration/DataLoader.js +105 -0
- package/dist/configuration/createCacheServer.js +51 -0
- package/dist/configuration/getConfig.js +31 -0
- package/dist/configuration/types.js +2 -0
- package/dist/createTranslation.js +28 -0
- package/dist/getTranslation.js +33 -0
- package/dist/helpers/isPromise.js +10 -0
- package/dist/lib/ClientI18nContext.js +6 -0
- package/dist/lib/ClientI18nProvider.js +36 -0
- package/dist/lib/I18nContext.js +8 -0
- package/dist/lib/Translation.js +54 -0
- package/dist/lib/formatServerTranslate.js +18 -0
- package/dist/lib/getDictionary.js +9 -0
- package/dist/lib/injectQuery.js +17 -0
- package/dist/lib/parseEntities.js +7 -0
- package/dist/types.js +2 -0
- package/dist/useTranslation.js +33 -0
- package/dist/withI18n.js +31 -0
- package/env.d.ts +1 -0
- package/getTranslation.d.ts +7 -0
- package/package.json +77 -0
- package/types.d.ts +22 -0
- package/useTranslation.d.ts +7 -0
- package/withI18n.d.ts +2 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { type I18nOptions } from './types';
|
|
3
|
+
type ClientTranslationProps = {
|
|
4
|
+
term: string;
|
|
5
|
+
components?: {
|
|
6
|
+
[key: string]: JSX.Element;
|
|
7
|
+
};
|
|
8
|
+
query?: I18nOptions['query'];
|
|
9
|
+
removeUnusedQueries?: I18nOptions['removeUnusedQueries'];
|
|
10
|
+
};
|
|
11
|
+
declare const ClientTranslation: React.FC<ClientTranslationProps>;
|
|
12
|
+
export default ClientTranslation;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { type I18nTransmitterProps } from './I18nTransmitter';
|
|
3
|
+
type I18nProviderProps = {
|
|
4
|
+
lang: string;
|
|
5
|
+
children: React.ReactNode;
|
|
6
|
+
clientTerms?: I18nTransmitterProps['terms'];
|
|
7
|
+
};
|
|
8
|
+
declare const I18nProvider: React.FC<I18nProviderProps>;
|
|
9
|
+
export default I18nProvider;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { type I18nOptions } from './types';
|
|
3
|
+
export type I18nTransmitterProps = {
|
|
4
|
+
terms: (string | [string, I18nOptions])[];
|
|
5
|
+
children: React.ReactNode;
|
|
6
|
+
cleanThread?: boolean;
|
|
7
|
+
};
|
|
8
|
+
declare const I18nTransmitter: React.FC<I18nTransmitterProps>;
|
|
9
|
+
export default I18nTransmitter;
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 Alexander Savelyev <vordgi1@gmail.com>
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# @nimpl/i18n
|
|
2
|
+
|
|
3
|
+
i18n library designed primarily with server components in mind and maximum optimization (due to the transfer of logic to the assembly stage and/or server side).
|
|
4
|
+
|
|
5
|
+
## Why one more library?
|
|
6
|
+
|
|
7
|
+
Server components are a recent feature in React. Existing translation libraries are not yet well-optimized for them. If they support it, then only by disabling Next.js static optimization.
|
|
8
|
+
|
|
9
|
+
This library is an attempt to create a highly optimized solution exclusively using the current capabilities of React.js, Next.js and node.js.
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
Support for loading translations during page rendering (*instead of the application build step*), allowing for up-to-date translations with ISR or SSR;
|
|
14
|
+
|
|
15
|
+
Support for revalidation logic, i.e. you can specify how often translations should be updated (*or that they should not be updated at all*);
|
|
16
|
+
|
|
17
|
+
Optimized caching system - not a single extra request will be sent, even with parallel building (which is now enabled by default);
|
|
18
|
+
|
|
19
|
+
Passing only those translations to the client that are needed on the client;
|
|
20
|
+
|
|
21
|
+
Embedding parameters in client translations on the server;
|
|
22
|
+
|
|
23
|
+
Support for html entities.
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
**Using npm:**
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npm i @nimpl/i18n
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
**Using yarn:**
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
yarn add @nimpl/i18n
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Additionally
|
|
40
|
+
|
|
41
|
+
Create tasks with wishes, ideas, difficulties, etc. All of them will definitely be considered and thought over.
|
|
42
|
+
|
|
43
|
+
## License
|
|
44
|
+
|
|
45
|
+
[MIT](https://github.com/vordgi/nimpl-i18n/blob/main/LICENSE)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { type I18nOptions } from './types';
|
|
3
|
+
type ServerTranslationProps = {
|
|
4
|
+
term: string;
|
|
5
|
+
components?: {
|
|
6
|
+
[key: string]: JSX.Element;
|
|
7
|
+
};
|
|
8
|
+
query?: I18nOptions['query'];
|
|
9
|
+
removeUnusedQueries?: I18nOptions['removeUnusedQueries'];
|
|
10
|
+
};
|
|
11
|
+
declare const ServerTranslation: React.FC<ServerTranslationProps>;
|
|
12
|
+
export default ServerTranslation;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type I18nOptions } from './types';
|
|
2
|
+
type CreateTranslationReturnType = {
|
|
3
|
+
t: (term: string, opts?: I18nOptions) => string;
|
|
4
|
+
lang: string;
|
|
5
|
+
};
|
|
6
|
+
declare const createTranslation: (lang: string, namespace?: string) => Promise<CreateTranslationReturnType>;
|
|
7
|
+
export default createTranslation;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
'use client';
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
const react_1 = __importDefault(require("react"));
|
|
8
|
+
const Translation_1 = __importDefault(require("./lib/Translation"));
|
|
9
|
+
const useTranslation_1 = __importDefault(require("./useTranslation"));
|
|
10
|
+
const ClientTranslation = ({ term, components, query, removeUnusedQueries }) => {
|
|
11
|
+
const { t } = (0, useTranslation_1.default)();
|
|
12
|
+
const text = t(term, { query, removeUnusedQueries });
|
|
13
|
+
return (react_1.default.createElement(Translation_1.default, { term: term, text: text, components: components }));
|
|
14
|
+
};
|
|
15
|
+
exports.default = ClientTranslation;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const react_1 = __importDefault(require("react"));
|
|
7
|
+
const I18nContext_1 = require("./lib/I18nContext");
|
|
8
|
+
const I18nTransmitter_1 = __importDefault(require("./I18nTransmitter"));
|
|
9
|
+
const getDictionary_1 = __importDefault(require("./lib/getDictionary"));
|
|
10
|
+
const I18nProvider = async ({ children, lang, clientTerms = [] }) => {
|
|
11
|
+
const dictionary = await (0, getDictionary_1.default)(lang);
|
|
12
|
+
return (react_1.default.createElement(I18nContext_1.I18nContext.Provider, { value: { lang, dictionary } },
|
|
13
|
+
react_1.default.createElement(I18nTransmitter_1.default, { terms: clientTerms, cleanThread: true }, children)));
|
|
14
|
+
};
|
|
15
|
+
exports.default = I18nProvider;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const react_1 = __importDefault(require("react"));
|
|
7
|
+
const object_path_1 = __importDefault(require("object-path"));
|
|
8
|
+
const get_server_context_1 = __importDefault(require("next-impl-getters/get-server-context"));
|
|
9
|
+
const I18nContext_1 = require("./lib/I18nContext");
|
|
10
|
+
const ClientI18nProvider_1 = __importDefault(require("./lib/ClientI18nProvider"));
|
|
11
|
+
const formatServerTranslate_1 = __importDefault(require("./lib/formatServerTranslate"));
|
|
12
|
+
const formatServerTranslates = (result, targetKey, translates, opts = {}) => {
|
|
13
|
+
if (!translates)
|
|
14
|
+
return;
|
|
15
|
+
if (typeof translates === 'string') {
|
|
16
|
+
// eslint-disable-next-line no-param-reassign
|
|
17
|
+
result[targetKey] = (0, formatServerTranslate_1.default)({ term: targetKey, text: translates, parseEntities: true, ...opts });
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
Object.entries(translates).forEach(([subKey, translate]) => {
|
|
21
|
+
formatServerTranslates(result, `${targetKey}.${subKey}`, translate, opts);
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
const I18nTransmitter = async ({ terms, children, cleanThread }) => {
|
|
26
|
+
const context = (0, get_server_context_1.default)(I18nContext_1.I18nContext);
|
|
27
|
+
if (!context) {
|
|
28
|
+
throw new Error('Please, Init I18nProvider - https://github.com/vordgi/nimpl-i18n#server-components');
|
|
29
|
+
}
|
|
30
|
+
const { dictionary, lang } = context;
|
|
31
|
+
const result = {};
|
|
32
|
+
terms.forEach((term) => {
|
|
33
|
+
if (Array.isArray(term)) {
|
|
34
|
+
const [termKey, opts] = term;
|
|
35
|
+
const translates = object_path_1.default.get(dictionary, termKey);
|
|
36
|
+
formatServerTranslates(result, termKey, translates, opts);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
const translates = object_path_1.default.get(dictionary, term);
|
|
40
|
+
formatServerTranslates(result, term, translates);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
return (react_1.default.createElement(ClientI18nProvider_1.default, { lang: lang, translates: result, cleanThread: cleanThread }, children));
|
|
44
|
+
};
|
|
45
|
+
exports.default = I18nTransmitter;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const react_1 = __importDefault(require("react"));
|
|
7
|
+
const Translation_1 = __importDefault(require("./lib/Translation"));
|
|
8
|
+
const getTranslation_1 = __importDefault(require("./getTranslation"));
|
|
9
|
+
const ServerTranslation = ({ term, components, query, removeUnusedQueries }) => {
|
|
10
|
+
const { t } = (0, getTranslation_1.default)();
|
|
11
|
+
const text = t(term, { query, removeUnusedQueries });
|
|
12
|
+
return (react_1.default.createElement(Translation_1.default, { term: term, text: text, components: components }));
|
|
13
|
+
};
|
|
14
|
+
exports.default = ServerTranslation;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CacheHandler = void 0;
|
|
4
|
+
class CacheHandler {
|
|
5
|
+
cache;
|
|
6
|
+
constructor() {
|
|
7
|
+
this.cache = new Map();
|
|
8
|
+
}
|
|
9
|
+
async get(key) {
|
|
10
|
+
return this.cache.get(key);
|
|
11
|
+
}
|
|
12
|
+
async set(key, data) {
|
|
13
|
+
this.cache.set(key, data);
|
|
14
|
+
}
|
|
15
|
+
async has(key) {
|
|
16
|
+
return this.cache.has(key);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
exports.CacheHandler = CacheHandler;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const isPromise_1 = require("../helpers/isPromise");
|
|
4
|
+
const CacheHandler_1 = require("./CacheHandler");
|
|
5
|
+
class DataLoader {
|
|
6
|
+
cache = {};
|
|
7
|
+
loadTranslates;
|
|
8
|
+
revalidate;
|
|
9
|
+
cacheHandler = new CacheHandler_1.CacheHandler();
|
|
10
|
+
checkIsActual;
|
|
11
|
+
retryAttempts;
|
|
12
|
+
languages;
|
|
13
|
+
constructor(opts) {
|
|
14
|
+
this.loadTranslates = opts.load;
|
|
15
|
+
this.revalidate = opts.revalidate;
|
|
16
|
+
this.checkIsActual = opts.checkIsActual;
|
|
17
|
+
this.retryAttempts = opts.retryAttempts || 3;
|
|
18
|
+
this.languages = opts.languages || [];
|
|
19
|
+
}
|
|
20
|
+
async prolongCache(lang) {
|
|
21
|
+
const prevMeta = this.cache[lang];
|
|
22
|
+
if (prevMeta && !(0, isPromise_1.isPromise)(prevMeta)) {
|
|
23
|
+
prevMeta.lastUpdated = Date.now();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
async actualizeData(lang, lastLoadMeta) {
|
|
27
|
+
const isOutdatedCache = !lastLoadMeta || (this.revalidate !== undefined && this.revalidate !== false && (lastLoadMeta.lastUpdated + (1000 * this.revalidate) < Date.now()));
|
|
28
|
+
if (isOutdatedCache) {
|
|
29
|
+
this.cache[lang] = new Promise(async (resolve) => {
|
|
30
|
+
if (this.checkIsActual) {
|
|
31
|
+
const isActual = await this.callWithRetries(() => this.checkIsActual(lang, lastLoadMeta));
|
|
32
|
+
if (isActual) {
|
|
33
|
+
this.prolongCache(lang);
|
|
34
|
+
resolve(undefined);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
const fullData = await this.callWithRetries(() => this.loadTranslates(lang));
|
|
38
|
+
if (!fullData?.data) {
|
|
39
|
+
throw new Error(`No data key in load response for lang: ${lang}`);
|
|
40
|
+
}
|
|
41
|
+
this.cacheHandler.set(lang, fullData.data);
|
|
42
|
+
this.cache[lang] = {
|
|
43
|
+
...fullData.meta,
|
|
44
|
+
lastUpdated: Date.now(),
|
|
45
|
+
isRevalidated: false,
|
|
46
|
+
};
|
|
47
|
+
resolve(fullData.data);
|
|
48
|
+
});
|
|
49
|
+
const data = await this.cache[lang];
|
|
50
|
+
return data;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
async load(lang) {
|
|
54
|
+
if (!this.languages.includes(lang)) {
|
|
55
|
+
console.error(`Unknown language: ${lang}`);
|
|
56
|
+
return {};
|
|
57
|
+
}
|
|
58
|
+
const cache = this.cache;
|
|
59
|
+
try {
|
|
60
|
+
const lastLoadResult = cache[lang];
|
|
61
|
+
/** We are still loading */
|
|
62
|
+
if ((0, isPromise_1.isPromise)(lastLoadResult)) {
|
|
63
|
+
const data = await cache[lang];
|
|
64
|
+
return data;
|
|
65
|
+
}
|
|
66
|
+
/** Cache doesn't exist or it's outdated */
|
|
67
|
+
const actualData = await this.actualizeData(lang, lastLoadResult);
|
|
68
|
+
if (actualData)
|
|
69
|
+
return actualData;
|
|
70
|
+
}
|
|
71
|
+
catch (e) {
|
|
72
|
+
console.error(`Can\'t load actual data for lang ${lang}: `, e);
|
|
73
|
+
}
|
|
74
|
+
const isCacheExist = await this.cacheHandler.has(lang);
|
|
75
|
+
if (isCacheExist) {
|
|
76
|
+
return this.cacheHandler.get(lang);
|
|
77
|
+
}
|
|
78
|
+
throw new Error('Can\'t load data or read from cache');
|
|
79
|
+
}
|
|
80
|
+
async revalidateTag(lang) {
|
|
81
|
+
const prevMeta = this.cache[lang];
|
|
82
|
+
if (prevMeta && !(0, isPromise_1.isPromise)(prevMeta)) {
|
|
83
|
+
prevMeta.isRevalidated = true;
|
|
84
|
+
await this.load(lang);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
async callWithRetries(cb) {
|
|
88
|
+
let attempts = 0;
|
|
89
|
+
const call = async () => {
|
|
90
|
+
try {
|
|
91
|
+
attempts += 1;
|
|
92
|
+
return await cb();
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
if (attempts === this.retryAttempts) {
|
|
96
|
+
throw new Error('Can\'t load data');
|
|
97
|
+
}
|
|
98
|
+
console.warn('Can\'t load data, trying again...');
|
|
99
|
+
call();
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
return call();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
exports.default = DataLoader;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
7
|
+
const getConfig_1 = __importDefault(require("./getConfig"));
|
|
8
|
+
const http_1 = __importDefault(require("http"));
|
|
9
|
+
const DataLoader_1 = __importDefault(require("./DataLoader"));
|
|
10
|
+
async function createCacheServer(port) {
|
|
11
|
+
const serverSecret = crypto_1.default.randomBytes(32).toString('hex');
|
|
12
|
+
const config = await (0, getConfig_1.default)();
|
|
13
|
+
const dataLoader = new DataLoader_1.default(config);
|
|
14
|
+
const server = http_1.default.createServer(async (req, res) => {
|
|
15
|
+
try {
|
|
16
|
+
const url = new URL(req.url || '/', 'http://n');
|
|
17
|
+
const secret = url.searchParams.get('secret');
|
|
18
|
+
const lang = url.searchParams.get('lang');
|
|
19
|
+
const type = url.searchParams.get('type');
|
|
20
|
+
if (type === 'dev' && process.env.NODE_ENV === 'development') {
|
|
21
|
+
return res.end(JSON.stringify({ secret: serverSecret }));
|
|
22
|
+
}
|
|
23
|
+
if (secret !== serverSecret || !lang) {
|
|
24
|
+
return res.end();
|
|
25
|
+
}
|
|
26
|
+
const data = await dataLoader.load(lang);
|
|
27
|
+
res.end(JSON.stringify(data));
|
|
28
|
+
}
|
|
29
|
+
catch (e) {
|
|
30
|
+
console.log('error on data loading', e);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
await new Promise((resolve) => {
|
|
34
|
+
server.listen(+port, 'localhost', () => {
|
|
35
|
+
resolve(1);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
process.on('SIGINT', () => {
|
|
39
|
+
server.close();
|
|
40
|
+
});
|
|
41
|
+
process.on('SIGQUIT', () => {
|
|
42
|
+
server.close();
|
|
43
|
+
});
|
|
44
|
+
process.on('SIGTERM', () => {
|
|
45
|
+
server.close();
|
|
46
|
+
});
|
|
47
|
+
return {
|
|
48
|
+
secret: serverSecret,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
exports.default = createCacheServer;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const fs_1 = __importDefault(require("fs"));
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const url_1 = require("url");
|
|
9
|
+
const CONFIG_PATH = path_1.default.join(process.cwd(), 'nimpl-i18n.js');
|
|
10
|
+
// Crutch bypass of conversion by the assembler to require
|
|
11
|
+
const dynamicImport = new Function('p', 'return import(p)');
|
|
12
|
+
const getConfig = async () => {
|
|
13
|
+
try {
|
|
14
|
+
if (fs_1.default.existsSync(CONFIG_PATH)) {
|
|
15
|
+
const config = await dynamicImport((0, url_1.pathToFileURL)(CONFIG_PATH).href);
|
|
16
|
+
const { load, languages } = config.default;
|
|
17
|
+
if (!load) {
|
|
18
|
+
throw new Error(`Can't find loaderProvider - https://github.com/vordgi/nimpl-i18n#configuration`);
|
|
19
|
+
}
|
|
20
|
+
if (!languages) {
|
|
21
|
+
throw new Error(`Can't find loaderProvider - https://github.com/vordgi/nimpl-i18n#configuration`);
|
|
22
|
+
}
|
|
23
|
+
return config.default;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
//
|
|
28
|
+
}
|
|
29
|
+
throw new Error('Can\'t load config - https://github.com/vordgi/nimpl-i18n#configuration');
|
|
30
|
+
};
|
|
31
|
+
exports.default = getConfig;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const object_path_1 = __importDefault(require("object-path"));
|
|
7
|
+
const formatServerTranslate_1 = __importDefault(require("./lib/formatServerTranslate"));
|
|
8
|
+
const getDictionary_1 = __importDefault(require("./lib/getDictionary"));
|
|
9
|
+
const createTranslation = async (lang, namespace) => {
|
|
10
|
+
const dictionary = await (0, getDictionary_1.default)(lang);
|
|
11
|
+
const namespaceDictionary = namespace ? object_path_1.default.get(dictionary, namespace) : dictionary;
|
|
12
|
+
const t = (term, opts) => {
|
|
13
|
+
let termDictionary = namespaceDictionary;
|
|
14
|
+
let termNamespace = namespace;
|
|
15
|
+
let termKey = term;
|
|
16
|
+
if (term.includes(':')) {
|
|
17
|
+
[termNamespace, termKey] = term.split(':');
|
|
18
|
+
termDictionary = object_path_1.default.get(dictionary, termNamespace);
|
|
19
|
+
}
|
|
20
|
+
const translation = object_path_1.default.get(namespaceDictionary, termKey);
|
|
21
|
+
const fullTerm = `${termNamespace ? `${termNamespace}.` : ''}${termKey}`;
|
|
22
|
+
if (typeof translation !== 'string' || !translation)
|
|
23
|
+
return fullTerm;
|
|
24
|
+
return (0, formatServerTranslate_1.default)({ term: fullTerm, text: translation, parseEntities: true, ...opts });
|
|
25
|
+
};
|
|
26
|
+
return { t, lang };
|
|
27
|
+
};
|
|
28
|
+
exports.default = createTranslation;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const object_path_1 = __importDefault(require("object-path"));
|
|
7
|
+
const get_server_context_1 = __importDefault(require("next-impl-getters/get-server-context"));
|
|
8
|
+
const I18nContext_1 = require("./lib/I18nContext");
|
|
9
|
+
const formatServerTranslate_1 = __importDefault(require("./lib/formatServerTranslate"));
|
|
10
|
+
const getTranslation = (namespace) => {
|
|
11
|
+
const context = (0, get_server_context_1.default)(I18nContext_1.I18nContext);
|
|
12
|
+
if (!context) {
|
|
13
|
+
throw new Error('Please, Init I18nProvider - https://nimpl.tech/i18n/usage#i18nprovider');
|
|
14
|
+
}
|
|
15
|
+
const { dictionary, lang } = context;
|
|
16
|
+
const namespaceDictionary = namespace ? object_path_1.default.get(dictionary, namespace) : dictionary;
|
|
17
|
+
const t = (term, opts) => {
|
|
18
|
+
let termDictionary = namespaceDictionary;
|
|
19
|
+
let termNamespace = namespace;
|
|
20
|
+
let termKey = term;
|
|
21
|
+
if (term.includes(':')) {
|
|
22
|
+
[termNamespace, termKey] = term.split(':');
|
|
23
|
+
termDictionary = object_path_1.default.get(dictionary, termNamespace);
|
|
24
|
+
}
|
|
25
|
+
const translation = object_path_1.default.get(namespaceDictionary, termKey);
|
|
26
|
+
const fullTerm = `${termNamespace ? `${termNamespace}.` : ''}${termKey}`;
|
|
27
|
+
if (typeof translation !== 'string' || !translation)
|
|
28
|
+
return fullTerm;
|
|
29
|
+
return (0, formatServerTranslate_1.default)({ term: fullTerm, text: translation, parseEntities: true, ...opts });
|
|
30
|
+
};
|
|
31
|
+
return { t, lang };
|
|
32
|
+
};
|
|
33
|
+
exports.default = getTranslation;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isPromise = void 0;
|
|
4
|
+
const isPromise = (el) => {
|
|
5
|
+
if (el && typeof el === 'object' && 'then' in el) {
|
|
6
|
+
return true;
|
|
7
|
+
}
|
|
8
|
+
return false;
|
|
9
|
+
};
|
|
10
|
+
exports.isPromise = isPromise;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
'use client';
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
20
|
+
if (mod && mod.__esModule) return mod;
|
|
21
|
+
var result = {};
|
|
22
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
23
|
+
__setModuleDefault(result, mod);
|
|
24
|
+
return result;
|
|
25
|
+
};
|
|
26
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
27
|
+
const react_1 = __importStar(require("react"));
|
|
28
|
+
const ClientI18nContext_1 = require("./ClientI18nContext");
|
|
29
|
+
const ClientI18nProvider = ({ translates, children, lang, cleanThread }) => {
|
|
30
|
+
const prevTranslates = (0, react_1.useContext)(ClientI18nContext_1.ClientI18nContext);
|
|
31
|
+
if (cleanThread) {
|
|
32
|
+
Object.assign(translates, prevTranslates?.translates);
|
|
33
|
+
}
|
|
34
|
+
return (react_1.default.createElement(ClientI18nContext_1.ClientI18nContext.Provider, { value: { lang, translates } }, children));
|
|
35
|
+
};
|
|
36
|
+
exports.default = ClientI18nProvider;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.I18nContext = void 0;
|
|
7
|
+
const create_server_context_1 = __importDefault(require("next-impl-getters/create-server-context"));
|
|
8
|
+
exports.I18nContext = (0, create_server_context_1.default)(null);
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
/* eslint-disable no-console, react/no-array-index-key */
|
|
7
|
+
const react_1 = __importDefault(require("react"));
|
|
8
|
+
const Translation = ({ term, text, components }) => {
|
|
9
|
+
const parts = text.split(/<\/?[a-zA-Z0-9]+>|<[a-zA-Z0-9]+ ?\/>/gm).map((el, i) => react_1.default.createElement(react_1.default.Fragment, { key: `p-${i}` }, el));
|
|
10
|
+
if (components) {
|
|
11
|
+
const tags = text.match(/<\/?[a-zA-Z0-9]+>|<[a-zA-Z0-9]+ ?\/>/gm);
|
|
12
|
+
const openedTags = [];
|
|
13
|
+
tags?.forEach((tag, tagIndex) => {
|
|
14
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
15
|
+
const tagName = tag.match(/[a-zA-Z0-9]+/)[0];
|
|
16
|
+
if (tag.match(/<[a-zA-Z0-9]+ ?\/>/)) {
|
|
17
|
+
const component = components[tagName];
|
|
18
|
+
if (component) {
|
|
19
|
+
parts.splice(tagIndex + 1, 1, react_1.default.cloneElement(component, { key: `c-${tagIndex}` }, component.props.children));
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
console.warn(`Unknown component for term "${term}" - ${tagName}`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
else if (tag.match(/<\/[a-zA-Z0-9]+>/)) {
|
|
26
|
+
const openedTagIndex = openedTags.findIndex((i) => i.tag === tagName);
|
|
27
|
+
if (openedTagIndex !== -1) {
|
|
28
|
+
const lastOpenedIndex = openedTags.length - 1 - openedTagIndex;
|
|
29
|
+
const openedTagsLength = openedTags.length;
|
|
30
|
+
for (let i = openedTagsLength; i > lastOpenedIndex; i--) {
|
|
31
|
+
const targetIndex = i - 1;
|
|
32
|
+
const targetTag = openedTags[targetIndex];
|
|
33
|
+
const component = components[targetTag.tag];
|
|
34
|
+
if (component) {
|
|
35
|
+
const children = parts.slice(targetTag.position + 1, tagIndex + 1)
|
|
36
|
+
.filter((c) => Boolean(c.props.children));
|
|
37
|
+
parts.splice(targetTag.position + 1, // parts на 1 больше
|
|
38
|
+
tagIndex - targetTag.position, react_1.default.cloneElement(component, { key: `${tagIndex}-${targetIndex}` }, children.length ? children : component.props.children));
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
console.warn(`Unknown component for term "${term}" - ${targetTag}`);
|
|
42
|
+
}
|
|
43
|
+
openedTags.splice(targetIndex, 1);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
openedTags.push({ tag: tagName, position: tagIndex });
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
return (parts);
|
|
53
|
+
};
|
|
54
|
+
exports.default = Translation;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const html_entities_1 = require("html-entities");
|
|
7
|
+
const injectQuery_1 = __importDefault(require("./injectQuery"));
|
|
8
|
+
const formatServerTranslate = ({ term, text, removeUnusedQueries, query, parseEntities }) => {
|
|
9
|
+
let newTranslate = text;
|
|
10
|
+
if (query) {
|
|
11
|
+
newTranslate = (0, injectQuery_1.default)({ term, text: newTranslate, query, removeUnusedQueries });
|
|
12
|
+
}
|
|
13
|
+
if (parseEntities === undefined || parseEntities === true) {
|
|
14
|
+
newTranslate = (0, html_entities_1.decode)(newTranslate, { scope: 'strict' });
|
|
15
|
+
}
|
|
16
|
+
return newTranslate;
|
|
17
|
+
};
|
|
18
|
+
exports.default = formatServerTranslate;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const getDictionary = async (lang) => {
|
|
4
|
+
const origFetch = globalThis._nextOriginalFetch || fetch;
|
|
5
|
+
const dataResp = await origFetch(`http://localhost:${process.env.I18N_CACHE_PORT}/?secret=${process.env.I18N_CACHE_SECRET}&lang=${lang}`, { cache: 'no-cache' });
|
|
6
|
+
const data = await dataResp.json();
|
|
7
|
+
return data;
|
|
8
|
+
};
|
|
9
|
+
exports.default = getDictionary;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const injectQuery = ({ text, query, removeUnusedQueries }) => {
|
|
4
|
+
const newText = text.replace(/{{([a-zA-Z0-9_-]+)}}/gm, (matched, g1) => {
|
|
5
|
+
if (query[g1]) {
|
|
6
|
+
return query[g1].toString();
|
|
7
|
+
}
|
|
8
|
+
if (removeUnusedQueries) {
|
|
9
|
+
return '';
|
|
10
|
+
}
|
|
11
|
+
else {
|
|
12
|
+
return matched;
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
return newText;
|
|
16
|
+
};
|
|
17
|
+
exports.default = injectQuery;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const html_entities_1 = require("html-entities");
|
|
4
|
+
const parseEntities = (translate) => {
|
|
5
|
+
return (0, html_entities_1.decode)(translate, { scope: 'strict' });
|
|
6
|
+
};
|
|
7
|
+
exports.default = parseEntities;
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const react_1 = require("react");
|
|
7
|
+
const ClientI18nContext_1 = require("./lib/ClientI18nContext");
|
|
8
|
+
const injectQuery_1 = __importDefault(require("./lib/injectQuery"));
|
|
9
|
+
const useTranslation = (namespace) => {
|
|
10
|
+
const context = (0, react_1.useContext)(ClientI18nContext_1.ClientI18nContext);
|
|
11
|
+
if (!context) {
|
|
12
|
+
throw new Error('Please, Init I18nTransmitter for client components - https://nimpl.tech/i18n/usage#client-components');
|
|
13
|
+
}
|
|
14
|
+
const { lang, translates } = context;
|
|
15
|
+
const t = (term, opts) => {
|
|
16
|
+
let termKey;
|
|
17
|
+
if (term.includes(':')) {
|
|
18
|
+
termKey = term.replace(':', '.');
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
termKey = `${namespace ? `${namespace}.` : ''}${term}`;
|
|
22
|
+
}
|
|
23
|
+
const translation = translates[termKey];
|
|
24
|
+
if (!translation)
|
|
25
|
+
return termKey;
|
|
26
|
+
if (opts?.query) {
|
|
27
|
+
return (0, injectQuery_1.default)({ term, text: translation, query: opts.query, removeUnusedQueries: opts.removeUnusedQueries });
|
|
28
|
+
}
|
|
29
|
+
return translation;
|
|
30
|
+
};
|
|
31
|
+
return { t, lang };
|
|
32
|
+
};
|
|
33
|
+
exports.default = useTranslation;
|
package/dist/withI18n.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const createCacheServer_1 = __importDefault(require("./configuration/createCacheServer"));
|
|
7
|
+
const PORT = '24';
|
|
8
|
+
let cacheSecret = process.env.I18N_CACHE_SECRET;
|
|
9
|
+
const withI18n = async (phase) => {
|
|
10
|
+
if (!cacheSecret && process.env.NODE_ENV === 'development') {
|
|
11
|
+
try {
|
|
12
|
+
const devResp = await fetch(`http://localhost:${PORT}/?type=dev`);
|
|
13
|
+
const data = await devResp.json();
|
|
14
|
+
if (data) {
|
|
15
|
+
cacheSecret = data.secret;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
//
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
if ((phase === 'phase-development-server' || phase === 'phase-production-server') && !cacheSecret) {
|
|
23
|
+
const { secret } = await (0, createCacheServer_1.default)(PORT);
|
|
24
|
+
cacheSecret = secret;
|
|
25
|
+
}
|
|
26
|
+
process.env.I18N_CACHE_PORT = PORT;
|
|
27
|
+
if (cacheSecret)
|
|
28
|
+
process.env.I18N_CACHE_SECRET = cacheSecret;
|
|
29
|
+
return (nextConfig) => nextConfig;
|
|
30
|
+
};
|
|
31
|
+
exports.default = withI18n;
|
package/env.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/// <reference types="react/experimental" />
|
package/package.json
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@nimpl/i18n",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "i18n library for working with translations in server and client components",
|
|
5
|
+
"exports": {
|
|
6
|
+
"./ClientTranslation": {
|
|
7
|
+
"default": "./dist/ClientTranslation.js"
|
|
8
|
+
},
|
|
9
|
+
"./createTranslation": {
|
|
10
|
+
"default": "./dist/createTranslation.js"
|
|
11
|
+
},
|
|
12
|
+
"./getTranslation": {
|
|
13
|
+
"default": "./dist/getTranslation.js"
|
|
14
|
+
},
|
|
15
|
+
"./I18nProvider": {
|
|
16
|
+
"default": "./dist/I18nProvider.js"
|
|
17
|
+
},
|
|
18
|
+
"./I18nTransmitter": {
|
|
19
|
+
"default": "./dist/I18nTransmitter.js"
|
|
20
|
+
},
|
|
21
|
+
"./ServerTranslation": {
|
|
22
|
+
"default": "./dist/ServerTranslation.js"
|
|
23
|
+
},
|
|
24
|
+
"./types": {
|
|
25
|
+
"default": "./dist/types.js"
|
|
26
|
+
},
|
|
27
|
+
"./useTranslation": {
|
|
28
|
+
"default": "./dist/useTranslation.js"
|
|
29
|
+
},
|
|
30
|
+
"./withI18n": {
|
|
31
|
+
"default": "./dist/withI18n.js"
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"files": [
|
|
35
|
+
"dist",
|
|
36
|
+
"*.d.ts"
|
|
37
|
+
],
|
|
38
|
+
"scripts": {
|
|
39
|
+
"build": "tsc"
|
|
40
|
+
},
|
|
41
|
+
"keywords": [
|
|
42
|
+
"next",
|
|
43
|
+
"next.js",
|
|
44
|
+
"server components",
|
|
45
|
+
"react.js",
|
|
46
|
+
"i18n",
|
|
47
|
+
"internationalization",
|
|
48
|
+
"localization",
|
|
49
|
+
"l10n",
|
|
50
|
+
"intl"
|
|
51
|
+
],
|
|
52
|
+
"repository": {
|
|
53
|
+
"type": "git",
|
|
54
|
+
"url": "git://github.com/vordgi/nimpl-i18n.git"
|
|
55
|
+
},
|
|
56
|
+
"author": {
|
|
57
|
+
"name": "Savelyev Alexander",
|
|
58
|
+
"email": "vordgi1@gmail.com",
|
|
59
|
+
"url": "https://github.com/vordgi/"
|
|
60
|
+
},
|
|
61
|
+
"license": "MIT",
|
|
62
|
+
"devDependencies": {
|
|
63
|
+
"@types/node": "16.11.12",
|
|
64
|
+
"@types/object-path": "0.11.4",
|
|
65
|
+
"@types/react": "18.2.43",
|
|
66
|
+
"react": "18.2.0",
|
|
67
|
+
"typescript": "5.3.2"
|
|
68
|
+
},
|
|
69
|
+
"peerDependencies": {
|
|
70
|
+
"react": ">= 18.2.0"
|
|
71
|
+
},
|
|
72
|
+
"dependencies": {
|
|
73
|
+
"html-entities": "2.4.0",
|
|
74
|
+
"next-impl-getters": "0.3.1",
|
|
75
|
+
"object-path": "0.11.8"
|
|
76
|
+
}
|
|
77
|
+
}
|
package/types.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export type Translates = {
|
|
2
|
+
[key: string]: Translates | string;
|
|
3
|
+
};
|
|
4
|
+
export type GetLangOpts = {
|
|
5
|
+
pathname: string | null;
|
|
6
|
+
params: {
|
|
7
|
+
[key: string]: string | string[];
|
|
8
|
+
};
|
|
9
|
+
};
|
|
10
|
+
export type Query = {
|
|
11
|
+
[key: string]: string | number;
|
|
12
|
+
};
|
|
13
|
+
export type I18nOptions = {
|
|
14
|
+
query?: Query;
|
|
15
|
+
removeUnusedQueries?: boolean;
|
|
16
|
+
};
|
|
17
|
+
export type I18nContextType = {
|
|
18
|
+
lang: string;
|
|
19
|
+
translates: {
|
|
20
|
+
[key: string]: string;
|
|
21
|
+
};
|
|
22
|
+
} | null;
|
package/withI18n.d.ts
ADDED