@sv443-network/userutils 8.3.2 → 8.4.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/CHANGELOG.md +15 -0
- package/README.md +322 -84
- package/dist/index.cjs +1548 -0
- package/dist/index.global.js +54 -27
- package/dist/index.js +48 -24
- package/dist/lib/DataStore.d.ts +0 -1
- package/dist/lib/math.d.ts +5 -0
- package/dist/lib/misc.d.ts +22 -1
- package/dist/lib/translation.d.ts +41 -8
- package/dist/lib/types.d.ts +3 -1
- package/package.json +35 -34
package/README.md
CHANGED
|
@@ -5,15 +5,25 @@
|
|
|
5
5
|
Lightweight library with various utilities for userscripts - register listeners for when CSS selectors exist, intercept events, create persistent & synchronous data stores, modify the DOM more easily and more.
|
|
6
6
|
|
|
7
7
|
Contains builtin TypeScript declarations. Supports ESM and CJS imports via a bundler and global declaration via `@require`
|
|
8
|
+
|
|
9
|
+
You may want to check out my [template for userscripts in TypeScript](https://github.com/Sv443/Userscript.ts) that you can use to get started quickly. It also includes this library by default.
|
|
8
10
|
If you like using this library, please consider [supporting the development ❤️](https://github.com/sponsors/Sv443)
|
|
9
11
|
|
|
10
12
|
<br>
|
|
13
|
+
|
|
14
|
+
[](https://bundlephobia.com/package/@sv443-network/userutils)
|
|
15
|
+
[](https://bundlephobia.com/package/@sv443-network/userutils)
|
|
16
|
+
[](https://bundlephobia.com/package/@sv443-network/userutils)
|
|
17
|
+
|
|
18
|
+
[](https://github.com/Sv443-Network/UserUtils/stargazers)
|
|
19
|
+
[](https://dc.sv443.net/)
|
|
20
|
+
|
|
11
21
|
<sup>
|
|
12
22
|
View the documentation of previous major releases:
|
|
13
23
|
</sup>
|
|
14
24
|
<sub>
|
|
15
25
|
|
|
16
|
-
<a href="https://github.com/Sv443-Network/UserUtils/blob/v7.
|
|
26
|
+
<a href="https://github.com/Sv443-Network/UserUtils/blob/v8.0.0/README.md" rel="noopener noreferrer">8.0.0</a>, <a href="https://github.com/Sv443-Network/UserUtils/blob/v7.0.0/README.md" rel="noopener noreferrer">7.0.0</a>, <a href="https://github.com/Sv443-Network/UserUtils/blob/v6.0.0/README.md" rel="noopener noreferrer">6.0.0</a>, <a href="https://github.com/Sv443-Network/UserUtils/blob/v5.0.0/README.md" rel="noopener noreferrer">5.0.0</a>, <a href="https://github.com/Sv443-Network/UserUtils/blob/v4.0.0/README.md" rel="noopener noreferrer">4.0.0</a>, <a href="https://github.com/Sv443-Network/UserUtils/blob/v3.0.0/README.md" rel="noopener noreferrer">3.0.0</a>, <a href="https://github.com/Sv443-Network/UserUtils/blob/v2.0.0/README.md" rel="noopener noreferrer">2.0.0</a>, <a href="https://github.com/Sv443-Network/UserUtils/blob/v1.0.0/README.md" rel="noopener noreferrer">1.0.0</a>, <a href="https://github.com/Sv443-Network/UserUtils/blob/v0.5.3/README.md" rel="noopener noreferrer">0.5.3</a>
|
|
17
27
|
|
|
18
28
|
</sub>
|
|
19
29
|
</div>
|
|
@@ -42,6 +52,7 @@ View the documentation of previous major releases:
|
|
|
42
52
|
- [`clamp()`](#clamp) - constrain a number between a min and max value
|
|
43
53
|
- [`mapRange()`](#maprange) - map a number from one range to the same spot in another range
|
|
44
54
|
- [`randRange()`](#randrange) - generate a random number between a min and max boundary
|
|
55
|
+
- [`digitCount()`](#digitcount) - calculate the amount of digits in a number
|
|
45
56
|
- [**Misc:**](#misc)
|
|
46
57
|
- [`DataStore`](#datastore) - class that manages a hybrid sync & async persistent JSON database, including data migration
|
|
47
58
|
- [`DataStoreSerializer`](#datastoreserializer) - class for importing & exporting data of multiple DataStore instances, including compression, checksumming and running migrations
|
|
@@ -56,14 +67,16 @@ View the documentation of previous major releases:
|
|
|
56
67
|
- [`decompress()`](#decompress) - decompress a previously compressed string
|
|
57
68
|
- [`computeHash()`](#computehash) - compute the hash / checksum of a string or ArrayBuffer
|
|
58
69
|
- [`randomId()`](#randomid) - generate a random ID of a given length and radix
|
|
70
|
+
- [`consumeGen()`](#consumegen) - consumes a ValueGen and returns the value
|
|
71
|
+
- [`consumeStringGen()`](#consumestringgen) - consumes a StringGen and returns the string
|
|
59
72
|
- [**Arrays:**](#arrays)
|
|
60
73
|
- [`randomItem()`](#randomitem) - returns a random item from an array
|
|
61
74
|
- [`randomItemIndex()`](#randomitemindex) - returns a tuple of a random item and its index from an array
|
|
62
75
|
- [`takeRandomItem()`](#takerandomitem) - returns a random item from an array and mutates it to remove the item
|
|
63
76
|
- [`randomizeArray()`](#randomizearray) - returns a copy of the array with its items in a random order
|
|
64
77
|
- [**Translation:**](#translation)
|
|
65
|
-
- [`tr()`](#tr) - simple translation
|
|
66
|
-
- [`tr.forLang()`](#trforlang) -
|
|
78
|
+
- [`tr()`](#tr) - simple JSON-based translation system with placeholder and nesting support
|
|
79
|
+
- [`tr.forLang()`](#trforlang) - translate with the specified language instead of the currently active one
|
|
67
80
|
- [`tr.addLanguage()`](#traddlanguage) - add a language and its translations
|
|
68
81
|
- [`tr.setLanguage()`](#trsetlanguage) - set the currently active language for translations
|
|
69
82
|
- [`tr.getLanguage()`](#trgetlanguage) - returns the currently active language
|
|
@@ -79,6 +92,8 @@ View the documentation of previous major releases:
|
|
|
79
92
|
- [`NonEmptyString`](#nonemptystring) - any string that should have at least one character
|
|
80
93
|
- [`LooseUnion`](#looseunion) - a union that gives autocomplete in the IDE but also allows any other value of the same type
|
|
81
94
|
- [`Prettify`](#prettify) - expands a complex type into a more readable format while keeping functionality the same
|
|
95
|
+
- [`ValueGen`](#valuegen) - a "generator" value that allows for super flexible value typing and declaration
|
|
96
|
+
- [`StringGen`](#stringgen) - a "generator" string that allows for super flexible string typing and declaration, including enhanced support for unions
|
|
82
97
|
|
|
83
98
|
<br><br>
|
|
84
99
|
|
|
@@ -86,12 +101,16 @@ View the documentation of previous major releases:
|
|
|
86
101
|
## Installation:
|
|
87
102
|
Shameless plug: I made a [template for userscripts in TypeScript](https://github.com/Sv443/Userscript.ts) that you can use to get started quickly. It also includes this library by default.
|
|
88
103
|
|
|
89
|
-
- If you are using a bundler (like webpack, rollup, vite, etc.), you can install this package
|
|
104
|
+
- If you are using a bundler (like webpack, rollup, vite, etc.), you can install this package in one of the following ways:
|
|
90
105
|
```
|
|
91
106
|
npm i @sv443-network/userutils
|
|
107
|
+
pnpm i @sv443-network/userutils
|
|
108
|
+
yarn add @sv443-network/userutils
|
|
109
|
+
npx jsr install @sv443-network/userutils
|
|
110
|
+
deno add jsr:@sv443-network/userutils
|
|
92
111
|
```
|
|
93
|
-
|
|
94
|
-
|
|
112
|
+
Then import it in your script as usual:
|
|
113
|
+
|
|
95
114
|
```ts
|
|
96
115
|
import { addGlobalStyle } from "@sv443-network/userutils";
|
|
97
116
|
|
|
@@ -102,16 +121,26 @@ Shameless plug: I made a [template for userscripts in TypeScript](https://github
|
|
|
102
121
|
|
|
103
122
|
<br>
|
|
104
123
|
|
|
105
|
-
- If you are not using a bundler
|
|
124
|
+
- If you are not using a bundler, want to reduce the size of your userscript, or declared the package as external in your bundler, you can include the latest release by adding one of these directives to the userscript header, depending on your preferred CDN:
|
|
125
|
+
|
|
126
|
+
Versioned (recommended):
|
|
106
127
|
```
|
|
107
|
-
// @require https://
|
|
128
|
+
// @require https://cdn.jsdelivr.net/npm/@sv443-network/userutils@INSERT_VERSION/dist/index.global.js
|
|
129
|
+
// @require https://unpkg.com/@sv443-network/userutils@INSERT_VERSION/dist/index.global.js
|
|
108
130
|
```
|
|
131
|
+
Non-versioned (not recommended because auto-updating):
|
|
109
132
|
```
|
|
133
|
+
// @require https://update.greasyfork.org/scripts/472956/UserUtils.js
|
|
110
134
|
// @require https://openuserjs.org/src/libs/Sv443/UserUtils.js
|
|
111
135
|
```
|
|
112
|
-
(in order for your userscript not to break on a major library update, instead use the versioned URL at the top of the [GreasyFork page](https://greasyfork.org/scripts/472956-userutils))
|
|
113
136
|
|
|
114
|
-
|
|
137
|
+
> [!NOTE]
|
|
138
|
+
> In order for your userscript not to break on a major library update, use one the versioned URLs above after replacing `INSERT_VERSION` with the desired version (e.g. `8.3.2`) or the versioned URL that's shown at the top of the [GreasyFork page.](https://greasyfork.org/scripts/472956-userutils)
|
|
139
|
+
|
|
140
|
+
<br>
|
|
141
|
+
|
|
142
|
+
- Then, access the functions on the global variable `UserUtils`:
|
|
143
|
+
|
|
115
144
|
```ts
|
|
116
145
|
UserUtils.addGlobalStyle("body { background-color: red; }");
|
|
117
146
|
|
|
@@ -120,10 +149,27 @@ Shameless plug: I made a [template for userscripts in TypeScript](https://github
|
|
|
120
149
|
const { clamp } = UserUtils;
|
|
121
150
|
console.log(clamp(1, 5, 10)); // 5
|
|
122
151
|
```
|
|
123
|
-
|
|
152
|
+
|
|
153
|
+
<br>
|
|
154
|
+
|
|
155
|
+
- If you're using TypeScript and it complains about the missing global variable `UserUtils`, install the library using the package manager of your choice and add the following inside a `.d.ts` file somewhere in the directory (or a subdirectory) defined in your `tsconfig.json`'s `baseUrl` option or `include` array:
|
|
156
|
+
|
|
124
157
|
```ts
|
|
158
|
+
declare const UserUtils: typeof import("@sv443-network/userutils");
|
|
159
|
+
|
|
125
160
|
declare global {
|
|
126
|
-
|
|
161
|
+
interface Window {
|
|
162
|
+
UserUtils: typeof UserUtils;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
<br>
|
|
168
|
+
|
|
169
|
+
- If you're using a linter like ESLint, it might complain about the global variable `UserUtils` not being defined. To fix this, add the following to your ESLint configuration file:
|
|
170
|
+
```json
|
|
171
|
+
"globals": {
|
|
172
|
+
"UserUtils": "readonly"
|
|
127
173
|
}
|
|
128
174
|
```
|
|
129
175
|
|
|
@@ -137,8 +183,11 @@ Each feature has example code that can be expanded by clicking on the text "Exam
|
|
|
137
183
|
The usages and examples are written in TypeScript and use ESM import syntax, but the library can also be used in plain JavaScript after removing the type annotations (and changing the imports if you are using CommonJS or the global declaration).
|
|
138
184
|
If the usage section contains multiple usages of the function, each occurrence represents an overload and you can choose which one you want to use.
|
|
139
185
|
|
|
140
|
-
Some features require the `@run-at` or `@grant` directives to be tweaked in the userscript header or have other requirements.
|
|
141
|
-
|
|
186
|
+
Some features require the `@run-at` or `@grant` directives to be tweaked in the userscript header or have other specific requirements and limitations.
|
|
187
|
+
Those will be listed in a section marked by a warning emoji (⚠️) each.
|
|
188
|
+
|
|
189
|
+
If you need help with something, please [create a new discussion](https://github.com/Sv443-Network/UserUtils/discussions) or [join my Discord server.](https://dc.sv443.net/)
|
|
190
|
+
For submitting bug reports or feature requests, please use the [GitHub issue tracker.](https://github.com/Sv443-Network/UserUtils/issues)
|
|
142
191
|
|
|
143
192
|
<br><br>
|
|
144
193
|
|
|
@@ -908,9 +957,11 @@ setInnerHtmlUnsafe(myXssElement, userModifiableVariable);
|
|
|
908
957
|
Usage:
|
|
909
958
|
```ts
|
|
910
959
|
clamp(num: number, min: number, max: number): number
|
|
960
|
+
clamp(num: number, max: number): number
|
|
911
961
|
```
|
|
912
962
|
|
|
913
963
|
Clamps a number between a min and max boundary (inclusive).
|
|
964
|
+
If only the `num` and `max` arguments are passed, the `min` boundary will be set to 0.
|
|
914
965
|
|
|
915
966
|
<details><summary><b>Example - click to view</b></summary>
|
|
916
967
|
|
|
@@ -918,13 +969,14 @@ Clamps a number between a min and max boundary (inclusive).
|
|
|
918
969
|
import { clamp } from "@sv443-network/userutils";
|
|
919
970
|
|
|
920
971
|
clamp(7, 0, 10); // 7
|
|
921
|
-
clamp(
|
|
972
|
+
clamp(7, 10); // 7 (equivalent to the above)
|
|
973
|
+
clamp(-1, 10); // 0
|
|
922
974
|
clamp(5, -5, 0); // 0
|
|
923
975
|
clamp(99999, 0, 10); // 10
|
|
924
976
|
|
|
925
|
-
// clamp without a min or max boundary:
|
|
926
|
-
clamp(
|
|
927
|
-
clamp(
|
|
977
|
+
// use Infinity to clamp without a min or max boundary:
|
|
978
|
+
clamp(Number.MAX_SAFE_INTEGER, Infinity); // 9007199254740991
|
|
979
|
+
clamp(Number.MIN_SAFE_INTEGER, -Infinity, 0); // -9007199254740991
|
|
928
980
|
```
|
|
929
981
|
</details>
|
|
930
982
|
|
|
@@ -998,6 +1050,40 @@ benchmark(true); // Generated 100k in 461ms
|
|
|
998
1050
|
```
|
|
999
1051
|
</details>
|
|
1000
1052
|
|
|
1053
|
+
<br>
|
|
1054
|
+
|
|
1055
|
+
### digitCount()
|
|
1056
|
+
Usage:
|
|
1057
|
+
```ts
|
|
1058
|
+
digitCount(num: number | Stringifiable): number
|
|
1059
|
+
```
|
|
1060
|
+
|
|
1061
|
+
Calculates and returns the amount of digits in the given number.
|
|
1062
|
+
The given value will be converted by being passed to `String()` and then `Number()` before the calculation.
|
|
1063
|
+
Returns `NaN` if the number is invalid.
|
|
1064
|
+
|
|
1065
|
+
<details><summary><b>Example - click to view</b></summary>
|
|
1066
|
+
|
|
1067
|
+
```ts
|
|
1068
|
+
import { digitCount } from "@sv443-network/userutils";
|
|
1069
|
+
|
|
1070
|
+
const num1 = 123;
|
|
1071
|
+
const num2 = 123456789;
|
|
1072
|
+
const num3 = " 123456789 ";
|
|
1073
|
+
const num4 = Number.MAX_SAFE_INTEGER;
|
|
1074
|
+
const num5 = "a123b456c789d";
|
|
1075
|
+
const num6 = parseInt("0x123456789abcdef", 16);
|
|
1076
|
+
|
|
1077
|
+
digitCount(num1); // 3
|
|
1078
|
+
digitCount(num2); // 9
|
|
1079
|
+
digitCount(num3); // 9
|
|
1080
|
+
digitCount(num4); // 16
|
|
1081
|
+
digitCount(num5); // NaN (because hex conversion has to be done through parseInt(str, 16)), like below:
|
|
1082
|
+
digitCount(num6); // 17
|
|
1083
|
+
```
|
|
1084
|
+
|
|
1085
|
+
</details>
|
|
1086
|
+
|
|
1001
1087
|
<br><br>
|
|
1002
1088
|
|
|
1003
1089
|
<!-- #region Misc -->
|
|
@@ -1684,15 +1770,15 @@ If an array or NodeList is passed, the amount of contained items will be used.
|
|
|
1684
1770
|
```ts
|
|
1685
1771
|
import { autoPlural } from "@sv443-network/userutils";
|
|
1686
1772
|
|
|
1687
|
-
autoPlural("
|
|
1688
|
-
autoPlural("
|
|
1689
|
-
autoPlural("
|
|
1773
|
+
autoPlural("item", 0); // "items"
|
|
1774
|
+
autoPlural("item", 1); // "item"
|
|
1775
|
+
autoPlural("item", 2); // "items"
|
|
1690
1776
|
|
|
1691
|
-
autoPlural("
|
|
1692
|
-
autoPlural("
|
|
1777
|
+
autoPlural("element", document.querySelectorAll("html")); // "element"
|
|
1778
|
+
autoPlural("element", document.querySelectorAll("*")); // "elements"
|
|
1693
1779
|
|
|
1694
1780
|
const items = [1, 2, 3, 4, "foo", "bar"];
|
|
1695
|
-
console.log(
|
|
1781
|
+
console.log(items.length, autoPlural("item", items)); // "6 items"
|
|
1696
1782
|
```
|
|
1697
1783
|
</details>
|
|
1698
1784
|
|
|
@@ -1993,6 +2079,85 @@ benchmark(true, true); // Generated 10k in 1054ms
|
|
|
1993
2079
|
|
|
1994
2080
|
<br><br>
|
|
1995
2081
|
|
|
2082
|
+
### consumeGen()
|
|
2083
|
+
Usage:
|
|
2084
|
+
```ts
|
|
2085
|
+
consumeGen(valGen: ValueGen):
|
|
2086
|
+
```
|
|
2087
|
+
|
|
2088
|
+
Turns a [`ValueGen`](#valuegen) into its final value.
|
|
2089
|
+
ValueGen allows for tons of flexibility in how the value can be obtained. Calling this function will resolve the final value.
|
|
2090
|
+
|
|
2091
|
+
<details><summary><b>Example - click to view</b></summary>
|
|
2092
|
+
|
|
2093
|
+
```ts
|
|
2094
|
+
import { consumeGen, type ValueGen } from "@sv443-network/userutils";
|
|
2095
|
+
|
|
2096
|
+
async function doSomething(value: ValueGen<number>) {
|
|
2097
|
+
// type gets inferred as `number` because above `value` is typed as a ValueGen<number>
|
|
2098
|
+
const finalValue = await consumeGen(value);
|
|
2099
|
+
console.log(finalValue);
|
|
2100
|
+
}
|
|
2101
|
+
|
|
2102
|
+
// the following are all valid and yield 42:
|
|
2103
|
+
doSomething(42);
|
|
2104
|
+
doSomething(() => 42);
|
|
2105
|
+
doSomething(Promise.resolve(42));
|
|
2106
|
+
doSomething(async () => 42);
|
|
2107
|
+
|
|
2108
|
+
// throws a typescript error:
|
|
2109
|
+
doSomething("foo");
|
|
2110
|
+
```
|
|
2111
|
+
|
|
2112
|
+
</details>
|
|
2113
|
+
|
|
2114
|
+
<br><br>
|
|
2115
|
+
|
|
2116
|
+
### consumeStringGen()
|
|
2117
|
+
Usage:
|
|
2118
|
+
```ts
|
|
2119
|
+
consumeStringGen(strGen: StringGen): Promise<string>
|
|
2120
|
+
```
|
|
2121
|
+
|
|
2122
|
+
Turns a [`StringGen`](#stringgen) into its final string value.
|
|
2123
|
+
StringGen allows for tons of flexibility in how the string can be obtained. Calling this function will resolve the final string.
|
|
2124
|
+
Optionally you can use the template parameter to define the union of strings that the StringGen should yield.
|
|
2125
|
+
|
|
2126
|
+
<details><summary><b>Example - click to view</b></summary>
|
|
2127
|
+
|
|
2128
|
+
```ts
|
|
2129
|
+
import { consumeStringGen, type StringGen } from "@sv443-network/userutils";
|
|
2130
|
+
|
|
2131
|
+
export class MyTextPromptThing {
|
|
2132
|
+
// full flexibility on how the string can be passed to the constructor,
|
|
2133
|
+
// because it can be obtained synchronously or asynchronously,
|
|
2134
|
+
// in string or function form:
|
|
2135
|
+
constructor(private text: StringGen) {}
|
|
2136
|
+
|
|
2137
|
+
/** Shows the prompt dialog */
|
|
2138
|
+
public async showPrompt() {
|
|
2139
|
+
const promptText = await consumeStringGen(this.text);
|
|
2140
|
+
const promptHtml = promptText.trim().replace(/\n/g, "<br>");
|
|
2141
|
+
|
|
2142
|
+
// ...
|
|
2143
|
+
}
|
|
2144
|
+
}
|
|
2145
|
+
|
|
2146
|
+
// all valid:
|
|
2147
|
+
const myText = "Hello, World!";
|
|
2148
|
+
new MyTextPromptThing(myText);
|
|
2149
|
+
new MyTextPromptThing(() => myText);
|
|
2150
|
+
new MyTextPromptThing(Promise.resolve(myText));
|
|
2151
|
+
new MyTextPromptThing(async () => myText);
|
|
2152
|
+
|
|
2153
|
+
// throws a typescript error:
|
|
2154
|
+
new MyTextPromptThing(420);
|
|
2155
|
+
```
|
|
2156
|
+
|
|
2157
|
+
</details>
|
|
2158
|
+
|
|
2159
|
+
<br><br>
|
|
2160
|
+
|
|
1996
2161
|
<!-- #region Arrays -->
|
|
1997
2162
|
## Arrays:
|
|
1998
2163
|
|
|
@@ -2100,43 +2265,63 @@ Pluralization is not supported but can be achieved manually by adding variations
|
|
|
2100
2265
|
### tr()
|
|
2101
2266
|
Usage:
|
|
2102
2267
|
```ts
|
|
2103
|
-
tr(key: string, ...
|
|
2268
|
+
tr(key: string, ...insertValues: Stringifiable[]): string
|
|
2104
2269
|
```
|
|
2105
2270
|
|
|
2106
2271
|
The function returns the translation of the passed key in the language added by [`tr.addLanguage()`](#traddlanguage) and set by [`tr.setLanguage()`](#trsetlanguage)
|
|
2107
|
-
Should the translation contain placeholders in the format `%n`, where `n` is the number of the value starting at 1, they will be replaced with the respective item of the `
|
|
2108
|
-
The items of the `
|
|
2272
|
+
Should the translation contain placeholders in the format `%n`, where `n` is the number of the value starting at 1, they will be replaced with the respective item of the `insertValues` rest parameter.
|
|
2273
|
+
The items of the `insertValues` rest parameter will be stringified using `toString()` (see [Stringifiable](#stringifiable)) before being inserted into the translation.
|
|
2274
|
+
|
|
2275
|
+
Should you be using nested objects in your translations, you can use the dot notation to access them.
|
|
2276
|
+
First, the key will be split by dots and the parts will be used to traverse the translation object.
|
|
2277
|
+
If that doesn't yield a result, the function will try to access the key including dots on the top level of the translation object.
|
|
2278
|
+
If that also doesn't yield a result, the key itself will be returned.
|
|
2279
|
+
|
|
2280
|
+
If no language has been added or set before calling this function, it will also return the key itself.
|
|
2109
2281
|
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2282
|
+
To check if a translation has been found, compare the returned value with the key. If they are the same, the translation was not found.
|
|
2283
|
+
You could also write a wrapper function that can then return a default value or `null` if the translation was not found instead.
|
|
2284
|
+
|
|
2285
|
+
If the key is found and the translation contains placeholders but none or an insufficient amount of values are passed, it will try to insert as many values as were passed and leave the rest of the placeholders untouched in their `%n` format.
|
|
2286
|
+
If the key is found, the translation doesn't contain placeholders but values are still passed, the values will be ignored and the translation will be returned without modification.
|
|
2113
2287
|
|
|
2114
2288
|
<details><summary><b>Example - click to view</b></summary>
|
|
2115
2289
|
|
|
2116
2290
|
```ts
|
|
2117
2291
|
import { tr } from "@sv443-network/userutils";
|
|
2118
2292
|
|
|
2293
|
+
// add languages and translations:
|
|
2119
2294
|
tr.addLanguage("en", {
|
|
2120
|
-
|
|
2121
|
-
|
|
2295
|
+
welcome: {
|
|
2296
|
+
generic: "Welcome",
|
|
2297
|
+
with_name: "Welcome, %1",
|
|
2298
|
+
},
|
|
2122
2299
|
});
|
|
2300
|
+
|
|
2123
2301
|
tr.addLanguage("de", {
|
|
2124
|
-
|
|
2125
|
-
|
|
2302
|
+
welcome: {
|
|
2303
|
+
generic: "Willkommen",
|
|
2304
|
+
with_name: "Willkommen, %1",
|
|
2305
|
+
},
|
|
2126
2306
|
});
|
|
2127
2307
|
|
|
2128
2308
|
// this has to be called at least once before calling tr()
|
|
2129
2309
|
tr.setLanguage("en");
|
|
2130
2310
|
|
|
2131
|
-
console.log(tr("welcome"));
|
|
2132
|
-
console.log(tr("
|
|
2133
|
-
|
|
2311
|
+
console.log(tr("welcome.generic")); // "Welcome"
|
|
2312
|
+
console.log(tr("welcome.with_name", "John")); // "Welcome, John"
|
|
2313
|
+
|
|
2314
|
+
console.log(tr("non_existent_key")); // "non_existent_key"
|
|
2315
|
+
console.log(tr("welcome")); // "welcome" (because anything that isn't a string will make the function return the key itself)
|
|
2134
2316
|
|
|
2135
2317
|
// language can be changed at any time, synchronously
|
|
2136
2318
|
tr.setLanguage("de");
|
|
2137
2319
|
|
|
2138
|
-
console.log(tr("welcome"));
|
|
2139
|
-
|
|
2320
|
+
console.log(tr("welcome.generic")); // "Willkommen"
|
|
2321
|
+
|
|
2322
|
+
// or without overwriting the current language:
|
|
2323
|
+
|
|
2324
|
+
console.log(tr.forLang("en", "welcome.generic")); // "Welcome"
|
|
2140
2325
|
```
|
|
2141
2326
|
</details>
|
|
2142
2327
|
|
|
@@ -2145,7 +2330,7 @@ console.log(tr("welcome_name", "John")); // "Willkommen, John"
|
|
|
2145
2330
|
### tr.forLang()
|
|
2146
2331
|
Usage:
|
|
2147
2332
|
```ts
|
|
2148
|
-
tr.forLang(language: string, key: string, ...
|
|
2333
|
+
tr.forLang(language: string, key: string, ...insertValues: Stringifiable[]): string
|
|
2149
2334
|
```
|
|
2150
2335
|
|
|
2151
2336
|
Returns the translation of the passed key in the specified language. Otherwise behaves exactly like [`tr()`](#tr)
|
|
@@ -2167,8 +2352,8 @@ tr.addLanguage("de", {
|
|
|
2167
2352
|
// the language is set to "en"
|
|
2168
2353
|
tr.setLanguage("en");
|
|
2169
2354
|
|
|
2170
|
-
console.log(tr("welcome_name", "John")); // "Welcome"
|
|
2171
|
-
//
|
|
2355
|
+
console.log(tr("welcome_name", "John")); // "Welcome, John"
|
|
2356
|
+
// no need to call tr.setLanguage():
|
|
2172
2357
|
console.log(tr.forLang("de", "welcome_name", "John")); // "Willkommen, John"
|
|
2173
2358
|
```
|
|
2174
2359
|
</details>
|
|
@@ -2178,12 +2363,12 @@ console.log(tr.forLang("de", "welcome_name", "John")); // "Willkommen, John"
|
|
|
2178
2363
|
### tr.addLanguage()
|
|
2179
2364
|
Usage:
|
|
2180
2365
|
```ts
|
|
2181
|
-
tr.addLanguage(language: string, translations: Record<string, string>): void
|
|
2366
|
+
tr.addLanguage(language: string, translations: Record<string, string | object>): void
|
|
2182
2367
|
```
|
|
2183
2368
|
|
|
2184
|
-
Adds a language and its associated translations to the translation function.
|
|
2185
|
-
The passed language can be any unique identifier, though I recommend sticking to the [ISO 639-1
|
|
2186
|
-
The passed translations
|
|
2369
|
+
Adds or overwrites a language and its associated translations to the translation function.
|
|
2370
|
+
The passed language can be any unique identifier, though I highly recommend sticking to a standard like [BCP 47 / RFC 5646](https://www.rfc-editor.org/rfc/rfc5646.txt) (which is used by the [`Intl` namespace](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl) and methods like [`Number.toLocaleString()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toLocaleString)), or [ISO 639-1.](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes)
|
|
2371
|
+
The passed translations can either be a flat object where the key is the translation key used in `tr()` and the value is the translation itself, or an infinitely nestable object structure containing the same.
|
|
2187
2372
|
If `tr.addLanguage()` is called multiple times with the same language, the previous translations of that language will be overwritten.
|
|
2188
2373
|
|
|
2189
2374
|
The translation values may contain placeholders in the format `%n`, where `n` is the number of the value starting at 1.
|
|
@@ -2192,54 +2377,63 @@ These can be used to inject values into the translation when calling `tr()`
|
|
|
2192
2377
|
<details><summary><b>Example - click to view</b></summary>
|
|
2193
2378
|
|
|
2194
2379
|
```ts
|
|
2195
|
-
import { tr } from "@sv443-network/userutils";
|
|
2380
|
+
import { tr, type Stringifiable } from "@sv443-network/userutils";
|
|
2196
2381
|
|
|
2197
2382
|
// add a language with associated translations:
|
|
2198
2383
|
|
|
2199
|
-
tr.addLanguage("
|
|
2200
|
-
|
|
2384
|
+
tr.addLanguage("en", {
|
|
2385
|
+
lang_name: "Eglis", // no worries, the example below will overwrite this value
|
|
2201
2386
|
});
|
|
2202
2387
|
|
|
2203
|
-
|
|
2204
|
-
// with placeholders:
|
|
2388
|
+
// overwriting previous translation, now with nested objects and placeholders:
|
|
2205
2389
|
|
|
2206
2390
|
tr.addLanguage("en", {
|
|
2207
|
-
"
|
|
2208
|
-
|
|
2209
|
-
|
|
2391
|
+
// to get this value, you could call `tr.forLang("en", "lang_name")`
|
|
2392
|
+
lang_name: "English",
|
|
2393
|
+
home_page: {
|
|
2394
|
+
welcome: {
|
|
2395
|
+
generic: "Welcome!",
|
|
2396
|
+
// this can be accessed with `tr("home_page.welcome.name", "John")`
|
|
2397
|
+
name: "Welcome, %1!",
|
|
2398
|
+
extended: "Welcome, %1!\nYour last login was on %2\nYou have %3 unread messages",
|
|
2399
|
+
},
|
|
2400
|
+
},
|
|
2210
2401
|
});
|
|
2211
2402
|
|
|
2212
|
-
|
|
2213
2403
|
// can be used for different locales too:
|
|
2214
2404
|
|
|
2215
2405
|
tr.addLanguage("en-US", {
|
|
2216
|
-
|
|
2406
|
+
fries: "fries",
|
|
2407
|
+
color: "color",
|
|
2217
2408
|
});
|
|
2409
|
+
|
|
2218
2410
|
tr.addLanguage("en-GB", {
|
|
2219
|
-
|
|
2411
|
+
fries: "chips",
|
|
2412
|
+
color: "colour",
|
|
2220
2413
|
});
|
|
2221
2414
|
|
|
2222
|
-
|
|
2223
2415
|
// apply default values for different locales to reduce redundancy in shared translation values:
|
|
2224
2416
|
|
|
2225
2417
|
const translation_de = {
|
|
2226
|
-
|
|
2227
|
-
|
|
2418
|
+
greeting: "Guten Tag!",
|
|
2419
|
+
foo: "Foo",
|
|
2228
2420
|
};
|
|
2421
|
+
|
|
2229
2422
|
tr.addLanguage("de-DE", translation_de);
|
|
2423
|
+
|
|
2230
2424
|
tr.addLanguage("de-CH", {
|
|
2425
|
+
// overwrite the "greeting" but keep other keys as they are:
|
|
2231
2426
|
...translation_de,
|
|
2232
|
-
|
|
2233
|
-
"greeting": "Grüezi!",
|
|
2427
|
+
greeting: "Grüezi!",
|
|
2234
2428
|
});
|
|
2429
|
+
|
|
2235
2430
|
tr.addLanguage("de-AT", {
|
|
2431
|
+
// overwrite "greeting" again but keep other keys as they are:
|
|
2236
2432
|
...translation_de,
|
|
2237
|
-
|
|
2238
|
-
"greeting": "Grüß Gott!",
|
|
2433
|
+
greeting: "Grüß Gott!",
|
|
2239
2434
|
});
|
|
2240
2435
|
|
|
2241
|
-
|
|
2242
|
-
// example for custom pluralization:
|
|
2436
|
+
// example for custom pluralization using a predefined suffix:
|
|
2243
2437
|
|
|
2244
2438
|
tr.addLanguage("en", {
|
|
2245
2439
|
"cart_items_added-0": "No items were added to the cart",
|
|
@@ -2247,27 +2441,47 @@ tr.addLanguage("en", {
|
|
|
2247
2441
|
"cart_items_added-n": "Added %1 items to the cart",
|
|
2248
2442
|
});
|
|
2249
2443
|
|
|
2250
|
-
/**
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2444
|
+
/** A number or any object with a length or size property */
|
|
2445
|
+
type Numberish = number | Array<unknown> | NodeList | { length: number } | { size: number };
|
|
2446
|
+
|
|
2447
|
+
/**
|
|
2448
|
+
* Returns the translated value given the key with a common pluralization identifier appended to it,
|
|
2449
|
+
* given the number of items (or size of Array/NodeList or anything else with a `length` or `size` property).
|
|
2450
|
+
*/
|
|
2451
|
+
function trpl(key: string, num: Numberish, ...values: Stringifiable[]): string {
|
|
2452
|
+
if(typeof num !== "number") {
|
|
2453
|
+
if("length" in num)
|
|
2454
|
+
num = num.length;
|
|
2455
|
+
else if("size" in num)
|
|
2456
|
+
num = num.size;
|
|
2457
|
+
}
|
|
2254
2458
|
|
|
2459
|
+
let plKey = key;
|
|
2255
2460
|
if(num === 0)
|
|
2256
|
-
|
|
2461
|
+
plKey = `${key}-0`;
|
|
2257
2462
|
else if(num === 1)
|
|
2258
|
-
|
|
2463
|
+
plKey = `${key}-1`;
|
|
2259
2464
|
else
|
|
2260
|
-
|
|
2465
|
+
plKey = `${key}-n`; // will be the fallback for everything like non-numeric values or NaN
|
|
2466
|
+
|
|
2467
|
+
return tr(plKey, ...values);
|
|
2261
2468
|
};
|
|
2262
2469
|
|
|
2470
|
+
// this has to be called once for tr("key") to work - otherwise you can use tr.forLang("en", "key")
|
|
2471
|
+
tr.setLanguage("en");
|
|
2472
|
+
|
|
2263
2473
|
const items = [];
|
|
2264
|
-
console.log(
|
|
2474
|
+
console.log(trpl("cart_items_added", items, items.length)); // "No items were added to the cart"
|
|
2265
2475
|
|
|
2266
2476
|
items.push("foo");
|
|
2267
|
-
console.log(
|
|
2477
|
+
console.log(trpl("cart_items_added", items, items.length)); // "Added 1 item to the cart"
|
|
2268
2478
|
|
|
2269
2479
|
items.push("bar");
|
|
2270
|
-
console.log(
|
|
2480
|
+
console.log(trpl("cart_items_added", items, items.length)); // "Added 2 items to the cart"
|
|
2481
|
+
|
|
2482
|
+
// if you run across cases like this, you need to modify your implementation of `trpl()` accordingly:
|
|
2483
|
+
const someVal = parseInt("not a number");
|
|
2484
|
+
console.log(trpl("cart_items_added", someVal, someVal)); // "Added NaN items to the cart"
|
|
2271
2485
|
```
|
|
2272
2486
|
</details>
|
|
2273
2487
|
|
|
@@ -2279,10 +2493,11 @@ Usage:
|
|
|
2279
2493
|
tr.setLanguage(language: string): void
|
|
2280
2494
|
```
|
|
2281
2495
|
|
|
2282
|
-
Synchronously sets the language that will be used for translations.
|
|
2496
|
+
Synchronously sets the language that will be used for translations by default.
|
|
2497
|
+
Alternatively, you can use [`tr.forLang()`](#trforlang) to get translations in a different language without changing the current language.
|
|
2283
2498
|
No validation is done on the passed language, so make sure it is correct and it has been added with `tr.addLanguage()` before calling `tr()`
|
|
2284
2499
|
|
|
2285
|
-
For an example, see [`tr()`](#tr)
|
|
2500
|
+
For an example, please see [`tr()`](#tr)
|
|
2286
2501
|
|
|
2287
2502
|
<br>
|
|
2288
2503
|
|
|
@@ -2300,7 +2515,7 @@ If no language has been set yet, it will return undefined.
|
|
|
2300
2515
|
### tr.getTranslations()
|
|
2301
2516
|
Usage:
|
|
2302
2517
|
```ts
|
|
2303
|
-
tr.getTranslations(language?: string): Record<string, string> | undefined
|
|
2518
|
+
tr.getTranslations(language?: string): Record<string, string | object> | undefined
|
|
2304
2519
|
```
|
|
2305
2520
|
|
|
2306
2521
|
Returns the translations of the specified language.
|
|
@@ -2313,7 +2528,7 @@ If no translations are found, it will return undefined.
|
|
|
2313
2528
|
import { tr } from "@sv443-network/userutils";
|
|
2314
2529
|
|
|
2315
2530
|
tr.addLanguage("en", {
|
|
2316
|
-
|
|
2531
|
+
welcome: "Welcome",
|
|
2317
2532
|
});
|
|
2318
2533
|
|
|
2319
2534
|
console.log(tr.getTranslations()); // undefined
|
|
@@ -2469,7 +2684,7 @@ logSomething(barObject); // Type error
|
|
|
2469
2684
|
|
|
2470
2685
|
<br>
|
|
2471
2686
|
|
|
2472
|
-
|
|
2687
|
+
### NonEmptyArray
|
|
2473
2688
|
Usage:
|
|
2474
2689
|
```ts
|
|
2475
2690
|
NonEmptyArray<TItem = unknown>
|
|
@@ -2499,7 +2714,7 @@ logFirstItem(["04abc", "69"]); // 4
|
|
|
2499
2714
|
|
|
2500
2715
|
<br>
|
|
2501
2716
|
|
|
2502
|
-
|
|
2717
|
+
### NonEmptyString
|
|
2503
2718
|
Usage:
|
|
2504
2719
|
```ts
|
|
2505
2720
|
NonEmptyString<TString extends string>
|
|
@@ -2523,7 +2738,7 @@ convertToNumber(""); // type error: Argument of type 'string' is not assign
|
|
|
2523
2738
|
|
|
2524
2739
|
<br>
|
|
2525
2740
|
|
|
2526
|
-
|
|
2741
|
+
### LooseUnion
|
|
2527
2742
|
Usage:
|
|
2528
2743
|
```ts
|
|
2529
2744
|
LooseUnion<TUnion extends string | number | object>
|
|
@@ -2550,7 +2765,7 @@ foo(1); // type error: Argument of type '1' is not assignable to parameter of
|
|
|
2550
2765
|
|
|
2551
2766
|
<br>
|
|
2552
2767
|
|
|
2553
|
-
|
|
2768
|
+
### Prettify
|
|
2554
2769
|
Usage:
|
|
2555
2770
|
```ts
|
|
2556
2771
|
Prettify<T>
|
|
@@ -2603,6 +2818,29 @@ const bar: Bar = {
|
|
|
2603
2818
|
```
|
|
2604
2819
|
</details>
|
|
2605
2820
|
|
|
2821
|
+
<br><br>
|
|
2822
|
+
|
|
2823
|
+
### ValueGen
|
|
2824
|
+
Usage:
|
|
2825
|
+
```ts
|
|
2826
|
+
ValueGen<TValueType>
|
|
2827
|
+
```
|
|
2828
|
+
|
|
2829
|
+
Describes a value that can be obtained in various ways, including via the type itself, a function that returns the type, a Promise that resolves to the type or either a sync or an async function that returns the type.
|
|
2830
|
+
Use it in the [`consumeGen()`](#consumegen) function to convert the given ValueGen value to the type it represents. Also refer to that function for an example.
|
|
2831
|
+
|
|
2832
|
+
<br><br>
|
|
2833
|
+
|
|
2834
|
+
### StringGen
|
|
2835
|
+
Usage:
|
|
2836
|
+
```ts
|
|
2837
|
+
StringGen<TStrUnion>
|
|
2838
|
+
```
|
|
2839
|
+
|
|
2840
|
+
Describes a string that can be obtained in various ways, including via the type itself, a function that returns the type, a Promise that resolves to the type or either a sync or an async function that returns the type.
|
|
2841
|
+
Contrary to [`ValueGen`](#valuegen), this type allows for specifying a union of strings that the StringGen should yield, as long as it is loosely typed as just `string`.
|
|
2842
|
+
Use it in the [`consumeStringGen()`](#consumestringgen) function to convert the given StringGen value to a plain string. Also refer to that function for an example.
|
|
2843
|
+
|
|
2606
2844
|
<br><br><br><br>
|
|
2607
2845
|
|
|
2608
2846
|
<!-- #region Footer -->
|