@nimpl/i18n 0.0.0-experimental-0b32faf
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/I18nTransmitter.d.ts +10 -0
- package/LICENSE +21 -0
- package/README.md +49 -0
- package/ServerTranslation.d.ts +13 -0
- package/clientConfig.d.ts +0 -0
- package/dist/ClientTranslation.js +15 -0
- package/dist/I18nTransmitter.js +43 -0
- package/dist/ServerTranslation.js +14 -0
- package/dist/clientConfig.js +2 -0
- package/dist/configuration/getConfig.js +68 -0
- package/dist/configuration/types.js +2 -0
- package/dist/getTranslation.js +33 -0
- package/dist/helpers/isPromise.js +10 -0
- package/dist/i18n.js +29 -0
- package/dist/lib/ClientI18nContext.js +6 -0
- package/dist/lib/ClientI18nProvider.js +46 -0
- package/dist/lib/Translation.js +56 -0
- package/dist/lib/formatServerTranslate.js +18 -0
- package/dist/lib/injectQuery.js +17 -0
- package/dist/lib/loadI18nData.js +25 -0
- package/dist/lib/parseEntities.js +7 -0
- package/dist/types.js +2 -0
- package/dist/useTranslation.js +38 -0
- package/env.d.ts +1 -0
- package/getTranslation.d.ts +10 -0
- package/i18n.d.ts +2 -0
- package/package.json +75 -0
- package/types.d.ts +22 -0
- package/useTranslation.d.ts +9 -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]: React.ReactElement;
|
|
7
|
+
};
|
|
8
|
+
query?: I18nOptions["query"];
|
|
9
|
+
removeUnusedQueries?: I18nOptions["removeUnusedQueries"];
|
|
10
|
+
};
|
|
11
|
+
declare const ClientTranslation: React.FC<ClientTranslationProps>;
|
|
12
|
+
export default ClientTranslation;
|
|
@@ -0,0 +1,10 @@
|
|
|
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
|
+
language?: string;
|
|
8
|
+
};
|
|
9
|
+
declare const I18nTransmitter: React.FC<I18nTransmitterProps>;
|
|
10
|
+
export default I18nTransmitter;
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 Alex Savelyev <dev@alexdln.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,49 @@
|
|
|
1
|
+
# @nimpl/i18n
|
|
2
|
+
|
|
3
|
+
(Former [next-translation](https://www.npmjs.com/package/next-translation))
|
|
4
|
+
|
|
5
|
+
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).
|
|
6
|
+
|
|
7
|
+
Visit https://nimpl.dev/docs/i18n to view the full documentation.
|
|
8
|
+
|
|
9
|
+
## Why one more library?
|
|
10
|
+
|
|
11
|
+
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.
|
|
12
|
+
|
|
13
|
+
This library is an attempt to create a highly optimized solution exclusively using the current capabilities of React.js, Next.js and node.js.
|
|
14
|
+
|
|
15
|
+
## Features
|
|
16
|
+
|
|
17
|
+
Support for loading translations during page rendering (_instead of the application build step_), allowing for up-to-date translations with ISR or SSR;
|
|
18
|
+
|
|
19
|
+
Support for revalidation logic, i.e. you can specify how often translations should be updated (_or that they should not be updated at all_);
|
|
20
|
+
|
|
21
|
+
Optimized caching system - not a single extra request will be sent, even with parallel building (which is now enabled by default);
|
|
22
|
+
|
|
23
|
+
Passing only those translations to the client that are needed on the client;
|
|
24
|
+
|
|
25
|
+
Embedding parameters in client translations on the server;
|
|
26
|
+
|
|
27
|
+
Support for html entities.
|
|
28
|
+
|
|
29
|
+
## Installation
|
|
30
|
+
|
|
31
|
+
**Using npm:**
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npm i @nimpl/i18n
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
**Using yarn:**
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
yarn add @nimpl/i18n
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Additionally
|
|
44
|
+
|
|
45
|
+
Create tasks with wishes, ideas, difficulties, etc. All of them will definitely be considered and thought over.
|
|
46
|
+
|
|
47
|
+
## License
|
|
48
|
+
|
|
49
|
+
[MIT](https://github.com/alexdln/nimpl-i18n/blob/main/LICENSE)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { type I18nOptions } from "./types";
|
|
3
|
+
type ServerTranslationProps = {
|
|
4
|
+
term: string;
|
|
5
|
+
components?: {
|
|
6
|
+
[key: string]: React.ReactElement;
|
|
7
|
+
};
|
|
8
|
+
query?: I18nOptions["query"];
|
|
9
|
+
removeUnusedQueries?: I18nOptions["removeUnusedQueries"];
|
|
10
|
+
language?: string;
|
|
11
|
+
};
|
|
12
|
+
declare const ServerTranslation: React.FC<ServerTranslationProps>;
|
|
13
|
+
export default ServerTranslation;
|
|
File without changes
|
|
@@ -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,43 @@
|
|
|
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 ClientI18nProvider_1 = __importDefault(require("./lib/ClientI18nProvider"));
|
|
9
|
+
const formatServerTranslate_1 = __importDefault(require("./lib/formatServerTranslate"));
|
|
10
|
+
const loadI18nData_1 = __importDefault(require("./lib/loadI18nData"));
|
|
11
|
+
const formatServerTranslates = (result, targetKey, translates, opts = {}) => {
|
|
12
|
+
if (!translates)
|
|
13
|
+
return;
|
|
14
|
+
if (typeof translates === "string") {
|
|
15
|
+
result[targetKey] = (0, formatServerTranslate_1.default)({ term: targetKey, text: translates, parseEntities: true, ...opts });
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
Object.entries(translates).forEach(([subKey, translate]) => {
|
|
19
|
+
formatServerTranslates(result, `${targetKey}.${subKey}`, translate, opts);
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
const I18nTransmitter = async ({ language: argLanguage, terms, children, cleanThread, }) => {
|
|
24
|
+
const { dictionary, language: configLanguage } = await (0, loadI18nData_1.default)();
|
|
25
|
+
const language = argLanguage || configLanguage;
|
|
26
|
+
if (!language) {
|
|
27
|
+
throw new Error("Unable to get the language in the createTranslation function. Please check the getLanguage method in the configuration file or pass the language as an argument.");
|
|
28
|
+
}
|
|
29
|
+
const result = {};
|
|
30
|
+
terms.forEach((term) => {
|
|
31
|
+
if (Array.isArray(term)) {
|
|
32
|
+
const [termKey, opts] = term;
|
|
33
|
+
const translates = object_path_1.default.get(dictionary, termKey);
|
|
34
|
+
formatServerTranslates(result, termKey, translates, opts);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
const translates = object_path_1.default.get(dictionary, term);
|
|
38
|
+
formatServerTranslates(result, term, translates);
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
return (react_1.default.createElement(ClientI18nProvider_1.default, { language: language, translates: result, cleanThread: cleanThread }, children));
|
|
42
|
+
};
|
|
43
|
+
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 = async ({ term, components, query, removeUnusedQueries, language, }) => {
|
|
10
|
+
const { t } = await (0, getTranslation_1.default)({ language });
|
|
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,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
const fs_1 = __importDefault(require("fs"));
|
|
40
|
+
const path_1 = __importDefault(require("path"));
|
|
41
|
+
const url_1 = require("url");
|
|
42
|
+
const CONFIG_PATH = path_1.default.join(process.cwd(), "nimpl-i18n.js");
|
|
43
|
+
// Crutch bypass of conversion by the assembler to require
|
|
44
|
+
const dynamicImport = new Function("p", "return import(p)");
|
|
45
|
+
const getConfig = async () => {
|
|
46
|
+
let clientConfig;
|
|
47
|
+
if (fs_1.default.existsSync(CONFIG_PATH)) {
|
|
48
|
+
const config = await dynamicImport((0, url_1.pathToFileURL)(CONFIG_PATH).href);
|
|
49
|
+
clientConfig = config.default;
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
// @ts-expect-error will be imported from the alias configuration in webpack
|
|
53
|
+
const config = await Promise.resolve().then(() => __importStar(require("@nimpl/i18n/clientConfig")));
|
|
54
|
+
clientConfig = config.default;
|
|
55
|
+
}
|
|
56
|
+
const { load, getLanguage, languages } = clientConfig;
|
|
57
|
+
if (!load) {
|
|
58
|
+
throw new Error(`Can't find "load" method in configuration file - https://github.com/alexdln/nimpl-i18n#configuration`);
|
|
59
|
+
}
|
|
60
|
+
if (!languages) {
|
|
61
|
+
throw new Error(`Can't find "languages" list in configuration file - https://github.com/alexdln/nimpl-i18n#configuration`);
|
|
62
|
+
}
|
|
63
|
+
if (!getLanguage) {
|
|
64
|
+
throw new Error(`Can't find "getLanguage" method in configuration file - https://github.com/alexdln/nimpl-i18n#configuration`);
|
|
65
|
+
}
|
|
66
|
+
return clientConfig;
|
|
67
|
+
};
|
|
68
|
+
exports.default = getConfig;
|
|
@@ -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 formatServerTranslate_1 = __importDefault(require("./lib/formatServerTranslate"));
|
|
8
|
+
const loadI18nData_1 = __importDefault(require("./lib/loadI18nData"));
|
|
9
|
+
const getTranslation = async (options) => {
|
|
10
|
+
const { language: argLanguage, namespace } = options || {};
|
|
11
|
+
const { dictionary, language: configLanguage } = await (0, loadI18nData_1.default)();
|
|
12
|
+
const language = argLanguage || configLanguage;
|
|
13
|
+
if (!language) {
|
|
14
|
+
throw new Error("Unable to get the language in the createTranslation function. Please check the getLanguage method in the configuration file or pass the language as an argument.");
|
|
15
|
+
}
|
|
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(termDictionary, 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, language };
|
|
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;
|
package/dist/i18n.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
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 = require("fs");
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const CONFIG_PATH = path_1.default.join(process.cwd(), "nimpl-i18n.js");
|
|
9
|
+
const i18n = () => {
|
|
10
|
+
return (nextConfig = {}) => {
|
|
11
|
+
return {
|
|
12
|
+
...nextConfig,
|
|
13
|
+
webpack: (config, options) => {
|
|
14
|
+
if (!(0, fs_1.existsSync)(CONFIG_PATH))
|
|
15
|
+
throw new Error("Please create and configure nimpl-i18n.js");
|
|
16
|
+
config.resolve ||= {};
|
|
17
|
+
config.resolve.alias = {
|
|
18
|
+
...config.resolve.alias,
|
|
19
|
+
"@nimpl/i18n/clientConfig": CONFIG_PATH,
|
|
20
|
+
};
|
|
21
|
+
if (typeof nextConfig.webpack === "function") {
|
|
22
|
+
return nextConfig.webpack(config, options);
|
|
23
|
+
}
|
|
24
|
+
return config;
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
exports.default = i18n;
|
|
@@ -0,0 +1,46 @@
|
|
|
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 () {
|
|
20
|
+
var ownKeys = function(o) {
|
|
21
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
+
var ar = [];
|
|
23
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
+
return ar;
|
|
25
|
+
};
|
|
26
|
+
return ownKeys(o);
|
|
27
|
+
};
|
|
28
|
+
return function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
})();
|
|
36
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
|
+
const react_1 = __importStar(require("react"));
|
|
38
|
+
const ClientI18nContext_1 = require("./ClientI18nContext");
|
|
39
|
+
const ClientI18nProvider = ({ translates, children, language, cleanThread }) => {
|
|
40
|
+
const prevTranslates = (0, react_1.useContext)(ClientI18nContext_1.ClientI18nContext);
|
|
41
|
+
if (cleanThread) {
|
|
42
|
+
Object.assign(translates, prevTranslates?.translates);
|
|
43
|
+
}
|
|
44
|
+
return react_1.default.createElement(ClientI18nContext_1.ClientI18nContext.Provider, { value: { language, translates } }, children);
|
|
45
|
+
};
|
|
46
|
+
exports.default = ClientI18nProvider;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
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 = ({ term, text, components }) => {
|
|
9
|
+
const parts = text
|
|
10
|
+
.split(/<\/?[a-zA-Z0-9]+>|<[a-zA-Z0-9]+ ?\/>/gm)
|
|
11
|
+
.map((el, i) => react_1.default.createElement(react_1.default.Fragment, { key: `p-${i}` }, el));
|
|
12
|
+
if (components) {
|
|
13
|
+
const tags = text.match(/<\/?[a-zA-Z0-9]+>|<[a-zA-Z0-9]+ ?\/>/gm);
|
|
14
|
+
const openedTags = [];
|
|
15
|
+
tags?.forEach((tag, tagIndex) => {
|
|
16
|
+
const tagName = tag.match(/[a-zA-Z0-9]+/)[0];
|
|
17
|
+
if (tag.match(/<[a-zA-Z0-9]+ ?\/>/)) {
|
|
18
|
+
const component = components[tagName];
|
|
19
|
+
if (component) {
|
|
20
|
+
parts.splice(tagIndex + 1, 1, react_1.default.cloneElement(component, { key: `c-${tagIndex}` }, component.props.children));
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
console.warn(`Unknown component for term "${term}" - ${tagName}`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
else if (tag.match(/<\/[a-zA-Z0-9]+>/)) {
|
|
27
|
+
const openedTagIndex = openedTags.findIndex((i) => i.tag === tagName);
|
|
28
|
+
if (openedTagIndex !== -1) {
|
|
29
|
+
const lastOpenedIndex = openedTags.length - 1 - openedTagIndex;
|
|
30
|
+
const openedTagsLength = openedTags.length;
|
|
31
|
+
for (let i = openedTagsLength; i > lastOpenedIndex; i--) {
|
|
32
|
+
const targetIndex = i - 1;
|
|
33
|
+
const targetTag = openedTags[targetIndex];
|
|
34
|
+
const component = components[targetTag.tag];
|
|
35
|
+
if (component) {
|
|
36
|
+
const children = parts
|
|
37
|
+
.slice(targetTag.position + 1, tagIndex + 1)
|
|
38
|
+
.filter((c) => Boolean(c && typeof c === "object" && "props" in c && c.props.children));
|
|
39
|
+
parts.splice(targetTag.position + 1, // parts на 1 больше
|
|
40
|
+
tagIndex - targetTag.position, react_1.default.cloneElement(component, { key: `${tagIndex}-${targetIndex}` }, children.length ? children : component.props.children));
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
console.warn(`Unknown component for term "${term}" - ${targetTag}`);
|
|
44
|
+
}
|
|
45
|
+
openedTags.splice(targetIndex, 1);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
openedTags.push({ tag: tagName, position: tagIndex });
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
return parts;
|
|
55
|
+
};
|
|
56
|
+
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,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,25 @@
|
|
|
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 getConfig_1 = __importDefault(require("../configuration/getConfig"));
|
|
7
|
+
const get_pathname_1 = require("@nimpl/getters/get-pathname");
|
|
8
|
+
const get_params_1 = require("@nimpl/getters/get-params");
|
|
9
|
+
const loadI18nData = async () => {
|
|
10
|
+
const config = await (0, getConfig_1.default)();
|
|
11
|
+
const language = await config.getLanguage({
|
|
12
|
+
get pathname() {
|
|
13
|
+
return (0, get_pathname_1.getPathname)();
|
|
14
|
+
},
|
|
15
|
+
get params() {
|
|
16
|
+
return (0, get_params_1.getParams)();
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
if (!language || !config.languages.includes(language)) {
|
|
20
|
+
throw new Error(`Can\' load data for language "${language}", valid languages are: ${config.languages.join(", ")}`);
|
|
21
|
+
}
|
|
22
|
+
const dictionary = await config.load(language);
|
|
23
|
+
return { dictionary, language };
|
|
24
|
+
};
|
|
25
|
+
exports.default = loadI18nData;
|
|
@@ -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,38 @@
|
|
|
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.dev/docs/i18n/usage#client-components");
|
|
13
|
+
}
|
|
14
|
+
const { language, 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)({
|
|
28
|
+
term,
|
|
29
|
+
text: translation,
|
|
30
|
+
query: opts.query,
|
|
31
|
+
removeUnusedQueries: opts.removeUnusedQueries,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
return translation;
|
|
35
|
+
};
|
|
36
|
+
return { t, language };
|
|
37
|
+
};
|
|
38
|
+
exports.default = useTranslation;
|
package/env.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/// <reference types="react/experimental" />
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type I18nOptions } from "./types";
|
|
2
|
+
type GetTranslationReturnType = {
|
|
3
|
+
t: (term: string, opts?: I18nOptions) => string;
|
|
4
|
+
language: string;
|
|
5
|
+
};
|
|
6
|
+
declare const getTranslation: (options?: {
|
|
7
|
+
language?: string;
|
|
8
|
+
namespace?: string;
|
|
9
|
+
}) => Promise<GetTranslationReturnType>;
|
|
10
|
+
export default getTranslation;
|
package/i18n.d.ts
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@nimpl/i18n",
|
|
3
|
+
"version": "0.0.0-experimental-0b32faf",
|
|
4
|
+
"description": "i18n library for working with translations in server and client components",
|
|
5
|
+
"exports": {
|
|
6
|
+
"./ClientTranslation": {
|
|
7
|
+
"default": "./dist/ClientTranslation.js"
|
|
8
|
+
},
|
|
9
|
+
"./getTranslation": {
|
|
10
|
+
"default": "./dist/getTranslation.js"
|
|
11
|
+
},
|
|
12
|
+
"./i18n": {
|
|
13
|
+
"default": "./dist/i18n.js"
|
|
14
|
+
},
|
|
15
|
+
"./I18nTransmitter": {
|
|
16
|
+
"default": "./dist/I18nTransmitter.js"
|
|
17
|
+
},
|
|
18
|
+
"./ServerTranslation": {
|
|
19
|
+
"default": "./dist/ServerTranslation.js"
|
|
20
|
+
},
|
|
21
|
+
"./types": {
|
|
22
|
+
"default": "./dist/types.js"
|
|
23
|
+
},
|
|
24
|
+
"./useTranslation": {
|
|
25
|
+
"default": "./dist/useTranslation.js"
|
|
26
|
+
},
|
|
27
|
+
"./clientConfig": {
|
|
28
|
+
"default": "./dist/clientConfig.js"
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"files": [
|
|
32
|
+
"dist",
|
|
33
|
+
"*.d.ts"
|
|
34
|
+
],
|
|
35
|
+
"scripts": {
|
|
36
|
+
"build": "tsc"
|
|
37
|
+
},
|
|
38
|
+
"keywords": [
|
|
39
|
+
"next",
|
|
40
|
+
"next.js",
|
|
41
|
+
"server components",
|
|
42
|
+
"react.js",
|
|
43
|
+
"i18n",
|
|
44
|
+
"internationalization",
|
|
45
|
+
"localization",
|
|
46
|
+
"l10n",
|
|
47
|
+
"intl"
|
|
48
|
+
],
|
|
49
|
+
"repository": {
|
|
50
|
+
"type": "git",
|
|
51
|
+
"url": "git://github.com/alexdln/nimpl-i18n.git"
|
|
52
|
+
},
|
|
53
|
+
"author": {
|
|
54
|
+
"name": "Alex Savelyev",
|
|
55
|
+
"email": "dev@alexdln.com",
|
|
56
|
+
"url": "https://github.com/alexdln/"
|
|
57
|
+
},
|
|
58
|
+
"license": "MIT",
|
|
59
|
+
"devDependencies": {
|
|
60
|
+
"@types/node": "25.1.0",
|
|
61
|
+
"@types/object-path": "0.11.4",
|
|
62
|
+
"@types/react": "19.2.10",
|
|
63
|
+
"@types/webpack": "5.28.5",
|
|
64
|
+
"react": "19.2.4",
|
|
65
|
+
"typescript": "5.9.3"
|
|
66
|
+
},
|
|
67
|
+
"peerDependencies": {
|
|
68
|
+
"react": ">= 19.1.0"
|
|
69
|
+
},
|
|
70
|
+
"dependencies": {
|
|
71
|
+
"@nimpl/getters": "2.2.2",
|
|
72
|
+
"html-entities": "2.6.0",
|
|
73
|
+
"object-path": "0.11.8"
|
|
74
|
+
}
|
|
75
|
+
}
|
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;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type I18nOptions } from "./types";
|
|
2
|
+
type GetTranslationReturnType = {
|
|
3
|
+
t: (term: string, opts?: I18nOptions) => string;
|
|
4
|
+
language: string;
|
|
5
|
+
};
|
|
6
|
+
declare const useTranslation: ({ namespace }?: {
|
|
7
|
+
namespace?: string;
|
|
8
|
+
}) => GetTranslationReturnType;
|
|
9
|
+
export default useTranslation;
|