@xxanderwp/translate-module 1.0.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/.github/workflows/deploy.yml +64 -0
- package/.github/workflows/test.yml +46 -0
- package/LICENSE +21 -0
- package/README.md +129 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +52 -0
- package/jest.config.js +30 -0
- package/logo.png +0 -0
- package/package.json +30 -0
- package/src/index.ts +64 -0
- package/tests/index.test.ts +79 -0
- package/tsconfig.json +17 -0
- package/tsconfig.test.json +11 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
name: Publish npm Package
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main, beta]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
publish:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
steps:
|
|
11
|
+
- name: Checkout repository
|
|
12
|
+
uses: actions/checkout@v3
|
|
13
|
+
|
|
14
|
+
- name: Setup Node.js
|
|
15
|
+
uses: actions/setup-node@v3
|
|
16
|
+
with:
|
|
17
|
+
node-version: 20
|
|
18
|
+
registry-url: https://registry.npmjs.org/
|
|
19
|
+
|
|
20
|
+
- name: Install dependencies
|
|
21
|
+
run: npm ci
|
|
22
|
+
|
|
23
|
+
- name: Build package
|
|
24
|
+
run: npm run build
|
|
25
|
+
|
|
26
|
+
- name: Check if version exists on npm
|
|
27
|
+
id: check_version
|
|
28
|
+
run: |
|
|
29
|
+
PACKAGE_NAME=$(node -p "require('./package.json').name")
|
|
30
|
+
PACKAGE_VERSION=$(node -p "require('./package.json').version")
|
|
31
|
+
echo "Package: $PACKAGE_NAME"
|
|
32
|
+
echo "Version: $PACKAGE_VERSION"
|
|
33
|
+
|
|
34
|
+
# Проверяем, есть ли уже такая версия на npm
|
|
35
|
+
if npm view "$PACKAGE_NAME@$PACKAGE_VERSION" >/dev/null 2>&1; then
|
|
36
|
+
echo "Version $PACKAGE_VERSION already exists on npm"
|
|
37
|
+
echo "exists=true" >> $GITHUB_OUTPUT
|
|
38
|
+
else
|
|
39
|
+
echo "Version $PACKAGE_VERSION is new"
|
|
40
|
+
echo "exists=false" >> $GITHUB_OUTPUT
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
- name: Publish to npm (latest)
|
|
44
|
+
if: steps.check_version.outputs.exists == 'false' && github.ref == 'refs/heads/main'
|
|
45
|
+
env:
|
|
46
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
47
|
+
run: |
|
|
48
|
+
npm publish --access=public
|
|
49
|
+
|
|
50
|
+
- name: Mark existing version as latest
|
|
51
|
+
if: steps.check_version.outputs.exists == 'true' && github.ref == 'refs/heads/main'
|
|
52
|
+
env:
|
|
53
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
54
|
+
run: |
|
|
55
|
+
PACKAGE_NAME=$(node -p "require('./package.json').name")
|
|
56
|
+
PACKAGE_VERSION=$(node -p "require('./package.json').version")
|
|
57
|
+
npm dist-tag add "$PACKAGE_NAME@$PACKAGE_VERSION" latest
|
|
58
|
+
|
|
59
|
+
- name: Publish to npm (beta)
|
|
60
|
+
if: steps.check_version.outputs.exists == 'false' && github.ref == 'refs/heads/beta'
|
|
61
|
+
env:
|
|
62
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
63
|
+
run: |
|
|
64
|
+
npm publish --tag beta --access=public
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
name: Tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [develop]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main, beta, develop]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
|
|
13
|
+
strategy:
|
|
14
|
+
matrix:
|
|
15
|
+
node-version: [16.x, 18.x, 20.x, 22.x]
|
|
16
|
+
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v3
|
|
19
|
+
|
|
20
|
+
- name: Use Node.js ${{ matrix.node-version }}
|
|
21
|
+
uses: actions/setup-node@v3
|
|
22
|
+
with:
|
|
23
|
+
node-version: ${{ matrix.node-version }}
|
|
24
|
+
|
|
25
|
+
- name: Install dependencies
|
|
26
|
+
run: npm ci
|
|
27
|
+
|
|
28
|
+
- name: Run linter
|
|
29
|
+
run: npm run lint
|
|
30
|
+
|
|
31
|
+
- name: Run tests
|
|
32
|
+
run: npm test
|
|
33
|
+
|
|
34
|
+
- name: Run coverage
|
|
35
|
+
run: npm run test:coverage
|
|
36
|
+
|
|
37
|
+
- name: Upload coverage to Codecov
|
|
38
|
+
uses: codecov/codecov-action@v3
|
|
39
|
+
if: matrix.node-version == '22.x'
|
|
40
|
+
with:
|
|
41
|
+
file: ./coverage/lcov.info
|
|
42
|
+
flags: unittests
|
|
43
|
+
name: codecov-umbrella
|
|
44
|
+
|
|
45
|
+
- name: Build
|
|
46
|
+
run: npm run build
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 XXanderWP
|
|
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,129 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<img src="./logo.png" alt="translate-module logo" width="320" />
|
|
3
|
+
|
|
4
|
+
[](https://www.npmjs.com/package/@xxanderwp/translate-module)
|
|
5
|
+
[](https://www.npmjs.com/package/@xxanderwp/translate-module)
|
|
6
|
+
[](https://github.com/XXanderWP/LangModule/actions/workflows/deploy.yml)
|
|
7
|
+
[](LICENSE)
|
|
8
|
+
[](https://www.typescriptlang.org/)
|
|
9
|
+
</div>
|
|
10
|
+
|
|
11
|
+
# @xxanderwp/translate-module
|
|
12
|
+
|
|
13
|
+
A lightweight, type-safe TypeScript module for managing multi-language translations with support for interpolation placeholders.
|
|
14
|
+
|
|
15
|
+
## Features
|
|
16
|
+
|
|
17
|
+
- Full TypeScript generic type safety — language keys and translation keys are statically inferred
|
|
18
|
+
- Runtime language switching via a simple setter
|
|
19
|
+
- String interpolation with indexed placeholders (`{0}`, `{1}`, ...)
|
|
20
|
+
- Zero runtime dependencies
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install @xxanderwp/translate-module
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Usage
|
|
29
|
+
|
|
30
|
+
### Basic setup
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
import { LanguageCore } from "@xxanderwp/translate-module";
|
|
34
|
+
|
|
35
|
+
const translations = {
|
|
36
|
+
en: {
|
|
37
|
+
greeting: "Hello, {0}!",
|
|
38
|
+
farewell: "Goodbye!",
|
|
39
|
+
},
|
|
40
|
+
es: {
|
|
41
|
+
greeting: "¡Hola, {0}!",
|
|
42
|
+
farewell: "¡Adiós!",
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const lang = new LanguageCore(translations, "en");
|
|
47
|
+
|
|
48
|
+
lang.translate("greeting", "Alice"); // "Hello, Alice!"
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Switching language
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
lang.currentLanguage = "es";
|
|
55
|
+
lang.translate("greeting", "Alice"); // "¡Hola, Alice!"
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Accessing available languages
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
lang.langKeys; // ["en", "es"]
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## API
|
|
65
|
+
|
|
66
|
+
### `new LanguageCore(data, defaultLanguage?)`
|
|
67
|
+
|
|
68
|
+
| Parameter | Type | Description |
|
|
69
|
+
| ----------------- | ----------- | ------------------------------------------------------------------------ |
|
|
70
|
+
| `data` | `T` | An object mapping language keys to their translation dictionaries. |
|
|
71
|
+
| `defaultLanguage` | `keyof T` | *(Optional)* The language to activate on construction. Defaults to the first key in `data`. |
|
|
72
|
+
|
|
73
|
+
Throws if `data` is empty or `defaultLanguage` is not a key of `data`.
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
### `translate(key, ...args)`
|
|
78
|
+
|
|
79
|
+
Returns the translated string for `key` in the current language, with `{0}`, `{1}`, ... placeholders replaced by the provided `args`.
|
|
80
|
+
|
|
81
|
+
Returns `null` if the key does not exist or no language is set.
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
### `currentLanguage` *(getter / setter)*
|
|
86
|
+
|
|
87
|
+
Gets or sets the active language key. Setting an unsupported key throws an error.
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
### `langKeys`
|
|
92
|
+
|
|
93
|
+
Returns an array of all available language keys.
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
### `languagesData`
|
|
98
|
+
|
|
99
|
+
Returns the full translations object passed to the constructor.
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
### `currentLanguageData`
|
|
104
|
+
|
|
105
|
+
Returns the translation dictionary for the currently active language, or `null` if none is set.
|
|
106
|
+
|
|
107
|
+
## Development
|
|
108
|
+
|
|
109
|
+
**Build**
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
npm run build
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
**Run tests**
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
npm test
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
**Run tests with coverage**
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
npm run test:coverage
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## License
|
|
128
|
+
|
|
129
|
+
[MIT](LICENSE)
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare class LanguageCore<T extends Record<string, Record<string, string>>, LangKey extends keyof T = keyof T> {
|
|
2
|
+
private readonly _languages_data;
|
|
3
|
+
private _currentLanguage;
|
|
4
|
+
constructor(data: T, defaultLanguage?: keyof T);
|
|
5
|
+
get languagesData(): T;
|
|
6
|
+
get currentLanguage(): LangKey | null;
|
|
7
|
+
set currentLanguage(lang: LangKey);
|
|
8
|
+
get langKeys(): LangKey[];
|
|
9
|
+
get currentLanguageData(): T[LangKey] | null;
|
|
10
|
+
translate<K extends keyof T[LangKey]>(key: K, ...args: (string | number)[]): string | null;
|
|
11
|
+
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LanguageCore = void 0;
|
|
4
|
+
class LanguageCore {
|
|
5
|
+
constructor(data, defaultLanguage) {
|
|
6
|
+
this._currentLanguage = null;
|
|
7
|
+
if (Object.keys(data).length === 0) {
|
|
8
|
+
throw new Error("Languages data cannot be empty.");
|
|
9
|
+
}
|
|
10
|
+
this._languages_data = data;
|
|
11
|
+
if (defaultLanguage) {
|
|
12
|
+
if (!this.langKeys.includes(defaultLanguage)) {
|
|
13
|
+
throw new Error(`Default language ${String(defaultLanguage)} is not supported.`);
|
|
14
|
+
}
|
|
15
|
+
this._currentLanguage = defaultLanguage;
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
this._currentLanguage = Object.keys(data)[0];
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
get languagesData() {
|
|
22
|
+
return this._languages_data;
|
|
23
|
+
}
|
|
24
|
+
get currentLanguage() {
|
|
25
|
+
return this._currentLanguage;
|
|
26
|
+
}
|
|
27
|
+
set currentLanguage(lang) {
|
|
28
|
+
if (!this.langKeys.includes(lang)) {
|
|
29
|
+
throw new Error(`Language ${String(lang)} is not supported.`);
|
|
30
|
+
}
|
|
31
|
+
this._currentLanguage = lang;
|
|
32
|
+
}
|
|
33
|
+
get langKeys() {
|
|
34
|
+
return Object.keys(this._languages_data);
|
|
35
|
+
}
|
|
36
|
+
get currentLanguageData() {
|
|
37
|
+
if (!this._currentLanguage)
|
|
38
|
+
return null;
|
|
39
|
+
return this._languages_data[this._currentLanguage];
|
|
40
|
+
}
|
|
41
|
+
translate(key, ...args) {
|
|
42
|
+
if (!this._currentLanguage)
|
|
43
|
+
return null;
|
|
44
|
+
const langData = this._languages_data[this._currentLanguage];
|
|
45
|
+
let res = langData[key];
|
|
46
|
+
args.forEach((arg, index) => {
|
|
47
|
+
res = res.replace(new RegExp(`\\{${index}\\}`, "g"), String(arg));
|
|
48
|
+
});
|
|
49
|
+
return res || null;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
exports.LanguageCore = LanguageCore;
|
package/jest.config.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
preset: 'ts-jest',
|
|
3
|
+
testEnvironment: 'node',
|
|
4
|
+
roots: ['<rootDir>/src', '<rootDir>/tests'],
|
|
5
|
+
testMatch: ['**/__tests__/**/*.ts', '**/?(*.)+(spec|test).ts'],
|
|
6
|
+
testTimeout: 10000,
|
|
7
|
+
collectCoverageFrom: [
|
|
8
|
+
'src/**/*.ts',
|
|
9
|
+
'!src/**/*.d.ts',
|
|
10
|
+
'!src/**/*.test.ts',
|
|
11
|
+
'!src/**/*.spec.ts',
|
|
12
|
+
],
|
|
13
|
+
coverageThreshold: {
|
|
14
|
+
global: {
|
|
15
|
+
branches: 50,
|
|
16
|
+
functions: 50,
|
|
17
|
+
lines: 50,
|
|
18
|
+
statements: 50,
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
moduleFileExtensions: ['ts', 'js', 'json'],
|
|
22
|
+
transform: {
|
|
23
|
+
'^.+\\.ts$': [
|
|
24
|
+
'ts-jest',
|
|
25
|
+
{
|
|
26
|
+
tsconfig: 'tsconfig.test.json',
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
},
|
|
30
|
+
};
|
package/logo.png
ADDED
|
Binary file
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@xxanderwp/translate-module",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"directories": {
|
|
7
|
+
"test": "tests"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsc",
|
|
11
|
+
"test:coverage": "jest --coverage",
|
|
12
|
+
"test": "jest"
|
|
13
|
+
},
|
|
14
|
+
"author": "XXanderWP",
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "https://github.com/XXanderWP/LangModule.git"
|
|
19
|
+
},
|
|
20
|
+
"bugs": {
|
|
21
|
+
"url": "https://github.com/XXanderWP/LangModule/issues"
|
|
22
|
+
},
|
|
23
|
+
"homepage": "https://github.com/XXanderWP/LangModule#readme",
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@types/jest": "^30.0.0",
|
|
26
|
+
"jest": "^30.4.2",
|
|
27
|
+
"ts-jest": "^29.4.11",
|
|
28
|
+
"ts-loader": "^9.6.0"
|
|
29
|
+
}
|
|
30
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
export class LanguageCore<
|
|
2
|
+
T extends Record<string, Record<string, string>>,
|
|
3
|
+
LangKey extends keyof T = keyof T
|
|
4
|
+
> {
|
|
5
|
+
private readonly _languages_data: T;
|
|
6
|
+
private _currentLanguage: LangKey | null = null;
|
|
7
|
+
|
|
8
|
+
constructor(data: T, defaultLanguage?: keyof T) {
|
|
9
|
+
if (Object.keys(data).length === 0) {
|
|
10
|
+
throw new Error("Languages data cannot be empty.");
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
this._languages_data = data;
|
|
14
|
+
if(defaultLanguage) {
|
|
15
|
+
if (!this.langKeys.includes(defaultLanguage as LangKey)) {
|
|
16
|
+
throw new Error(`Default language ${String(defaultLanguage)} is not supported.`);
|
|
17
|
+
}
|
|
18
|
+
this._currentLanguage = defaultLanguage as LangKey;
|
|
19
|
+
} else {
|
|
20
|
+
this._currentLanguage = Object.keys(data)[0] as LangKey;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
get languagesData(): T {
|
|
25
|
+
return this._languages_data;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
get currentLanguage(): LangKey | null {
|
|
29
|
+
return this._currentLanguage;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
set currentLanguage(lang: LangKey) {
|
|
33
|
+
if (!this.langKeys.includes(lang)) {
|
|
34
|
+
throw new Error(`Language ${String(lang)} is not supported.`);
|
|
35
|
+
}
|
|
36
|
+
this._currentLanguage = lang;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
get langKeys(): LangKey[] {
|
|
40
|
+
return Object.keys(this._languages_data) as LangKey[];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
get currentLanguageData(): T[LangKey] | null {
|
|
44
|
+
if (!this._currentLanguage) return null;
|
|
45
|
+
return this._languages_data[this._currentLanguage];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
translate<K extends keyof T[LangKey]>(
|
|
49
|
+
key: K,
|
|
50
|
+
...args: (string | number)[]
|
|
51
|
+
): string | null {
|
|
52
|
+
if (!this._currentLanguage) return null;
|
|
53
|
+
|
|
54
|
+
const langData = this._languages_data[this._currentLanguage];
|
|
55
|
+
let res = langData[key] as string;
|
|
56
|
+
|
|
57
|
+
args.forEach((arg, index) => {
|
|
58
|
+
res = res.replace(new RegExp(`\\{${index}\\}`, "g"), String(arg));
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
return res || null;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { describe, it, expect } from "@jest/globals";
|
|
2
|
+
import { LanguageCore } from "../src/index";
|
|
3
|
+
|
|
4
|
+
describe("LangModule", () => {
|
|
5
|
+
it("should initialize with valid data and default language", () => {
|
|
6
|
+
const data = {
|
|
7
|
+
en: { greeting: "Hello" },
|
|
8
|
+
es: { greeting: "Hola" },
|
|
9
|
+
};
|
|
10
|
+
const langModule = new LanguageCore(data, "es");
|
|
11
|
+
expect(langModule.languagesData).toEqual(data);
|
|
12
|
+
expect(langModule.currentLanguage).toBe("es");
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it("should throw an error if initialized with empty data", () => {
|
|
16
|
+
expect(() => new LanguageCore({})).toThrow("Languages data cannot be empty.");
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("should throw an error if default language is not supported", () => {
|
|
20
|
+
const data = {
|
|
21
|
+
en: { greeting: "Hello" },
|
|
22
|
+
es: { greeting: "Hola" },
|
|
23
|
+
};
|
|
24
|
+
// @ts-ignore
|
|
25
|
+
expect(() => new LanguageCore(data, "fr")).toThrow("Default language fr is not supported.");
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it("should return the correct translation for the current language", () => {
|
|
29
|
+
const data = {
|
|
30
|
+
en: { greeting: "Hello, {0}!" },
|
|
31
|
+
es: { greeting: "¡Hola, {0}!" },
|
|
32
|
+
};
|
|
33
|
+
const langModule = new LanguageCore(data, "en");
|
|
34
|
+
expect(langModule.translate("greeting", "John")).toBe("Hello, John!");
|
|
35
|
+
langModule.currentLanguage = "es";
|
|
36
|
+
expect(langModule.translate("greeting", "John")).toBe("¡Hola, John!");
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("should throw an error when setting an unsupported language", () => {
|
|
40
|
+
const data = {
|
|
41
|
+
en: { greeting: "Hello" },
|
|
42
|
+
es: { greeting: "Hola" },
|
|
43
|
+
};
|
|
44
|
+
const langModule = new LanguageCore(data);
|
|
45
|
+
// @ts-ignore
|
|
46
|
+
expect(() => (langModule.currentLanguage = "fr")).toThrow("Language fr is not supported.");
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("should return null for translation if current language is not set", () => {
|
|
50
|
+
const data = {
|
|
51
|
+
en: { greeting: "Hello" },
|
|
52
|
+
es: { greeting: "Hola" },
|
|
53
|
+
};
|
|
54
|
+
const langModule = new LanguageCore(data);
|
|
55
|
+
// @ts-ignore
|
|
56
|
+
expect(() => (langModule.currentLanguage = null)).toThrow("Language null is not supported.");
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("should return null for translation if key does not exist", () => {
|
|
60
|
+
const data = {
|
|
61
|
+
en: { greeting: "Hello" },
|
|
62
|
+
es: { greeting: "Hola" },
|
|
63
|
+
};
|
|
64
|
+
const langModule = new LanguageCore(data);
|
|
65
|
+
// @ts-ignore
|
|
66
|
+
expect(langModule.translate("farewell")).toBeNull();
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it("should return the correct language keys", () => {
|
|
70
|
+
const data = {
|
|
71
|
+
en: { greeting: "Hello" },
|
|
72
|
+
es: { greeting: "Hola" },
|
|
73
|
+
};
|
|
74
|
+
const langModule = new LanguageCore(data);
|
|
75
|
+
expect(langModule.langKeys).toEqual(["en", "es"]);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
});
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"declaration": true,
|
|
4
|
+
"target": "ES2020",
|
|
5
|
+
"module": "CommonJS",
|
|
6
|
+
"lib": ["ES2020", "DOM"],
|
|
7
|
+
"strict": true,
|
|
8
|
+
"esModuleInterop": true,
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"resolveJsonModule": true,
|
|
11
|
+
"outDir": "./dist",
|
|
12
|
+
"rootDir": "./src",
|
|
13
|
+
"types": ["node"]
|
|
14
|
+
},
|
|
15
|
+
"include": ["src/**/*"],
|
|
16
|
+
"exclude": ["node_modules", "dist"]
|
|
17
|
+
}
|