Sven 1 rok temu
rodzic
commit
715426e303
2 zmienionych plików z 221 dodań i 4 usunięć
  1. 8 0
      README-summary.md
  2. 213 4
      README.md

+ 8 - 0
README-summary.md

@@ -38,11 +38,19 @@ If you like using this library, please consider [supporting the development ❤
     - [pauseFor()](https://github.com/Sv443-Network/UserUtils#pausefor) - pause the execution of a function for a given amount of time
     - [debounce()](https://github.com/Sv443-Network/UserUtils#debounce) - call a function only once, after a given amount of time
     - [fetchAdvanced()](https://github.com/Sv443-Network/UserUtils#fetchadvanced) - wrapper around the fetch API with a timeout option
+    - [insertValues()](https://github.com/Sv443-Network/UserUtils#insertvalues) - insert values into a string at specified placeholders
 - Arrays:
     - [randomItem()](https://github.com/Sv443-Network/UserUtils#randomitem) - returns a random item from an array
     - [randomItemIndex()](https://github.com/Sv443-Network/UserUtils#randomitemindex) - returns a tuple of a random item and its index from an array
     - [takeRandomItem()](https://github.com/Sv443-Network/UserUtils#takerandomitem) - returns a random item from an array and mutates it to remove the item
     - [randomizeArray()](https://github.com/Sv443-Network/UserUtils#randomizearray) - returns a copy of the array with its items in a random order
+- Translation:
+    - [tr()](https://github.com/Sv443-Network/UserUtils#tr) - simple translation of a string to another language
+    - [tr.addLanguage()](https://github.com/Sv443-Network/UserUtils#traddlanguage) - add a language and its translations
+    - [tr.setLanguage()](https://github.com/Sv443-Network/UserUtils#trsetlanguage) - set the currently active language
+    - [tr.getLanguage()](https://github.com/Sv443-Network/UserUtils#trgetlanguage) - returns the currently active language
+- Utility types for TypeScript
+    - [Stringifiable](https://github.com/Sv443-Network/UserUtils#stringifiable) - any value that is a string or can be converted to one (implicitly or explicitly)
 
 <br><br>
 

+ 213 - 4
README.md

@@ -14,7 +14,7 @@ If you like using this library, please consider [supporting the development ❤
 - [**Preamble**](#preamble)
 - [**License**](#license)
 - [**Features**](#features)
-  - [DOM:](#dom)
+  - [**DOM:**](#dom)
     - [onSelector()](#onselector) - call a listener once a selector is found in the DOM
     - [initOnSelector()](#initonselector) - needs to be called once to be able to use `onSelector()`
     - [getSelectorMap()](#getselectormap) - returns all currently registered selectors, listeners and options
@@ -28,21 +28,29 @@ If you like using this library, please consider [supporting the development ❤
     - [interceptWindowEvent()](#interceptwindowevent) - conditionally intercepts events registered by `addEventListener()` on the window object
     - [amplifyMedia()](#amplifymedia) - amplify an audio or video element's volume past the maximum of 100%
     - [isScrollable()](#isscrollable) - check if an element has a horizontal or vertical scroll bar
-  - [Math:](#math)
+  - [**Math:**](#math)
     - [clamp()](#clamp) - constrain a number between a min and max value
     - [mapRange()](#maprange) - map a number from one range to the same spot in another range
     - [randRange()](#randrange) - generate a random number between a min and max boundary
-  - [Misc:](#misc)
+  - [**Misc:**](#misc)
     - [ConfigManager()](#configmanager) - class that manages persistent userscript configurations, including data migration
     - [autoPlural()](#autoplural) - automatically pluralize a string
     - [pauseFor()](#pausefor) - pause the execution of a function for a given amount of time
     - [debounce()](#debounce) - call a function only once, after a given amount of time
     - [fetchAdvanced()](#fetchadvanced) - wrapper around the fetch API with a timeout option
-  - [Arrays:](#arrays)
+    - [insertValues()](#insertvalues) - insert values into a string at specified placeholders
+  - [**Arrays:**](#arrays)
     - [randomItem()](#randomitem) - returns a random item from an array
     - [randomItemIndex()](#randomitemindex) - returns a tuple of a random item and its index from an array
     - [takeRandomItem()](#takerandomitem) - returns a random item from an array and mutates it to remove the item
     - [randomizeArray()](#randomizearray) - returns a copy of the array with its items in a random order
+  - [**Translation:**](#translation)
+    - [tr()](#tr) - simple translation of a string to another language
+    - [tr.addLanguage()](#traddlanguage) - add a language and its translations
+    - [tr.setLanguage()](#trsetlanguage) - set the currently active language for translations
+    - [tr.getLanguage()](#trgetlanguage) - returns the currently active language
+  - [**Utility types for TypeScript:**](#utility-types)
+    - [Stringifiable](#stringifiable) - any value that is a string or can be converted to one (implicitly or explicitly)
 
 <br><br>
 
@@ -968,8 +976,209 @@ console.log(foo); // [1, 2, 3, 4, 5, 6] - original array is not mutated
 
 </details>
 
+<br><br>
+
+## Translation:
+This is a very lightweight translation function that can be used to translate simple strings.  
+Pluralization is not supported but can be achieved manually by adding variations to the translations, identified by a different suffix. See the example section of [`tr.addLanguage()`](#traddlanguage) for an example on how this might be done.
+
+<br>
+
+### tr()
+Usage:  
+```ts
+tr(key: string, ...values: Stringifiable[]): string
+```
+  
+The function returns the translation of the passed key in the language added by [`tr.addLanguage()`](#traddlanguage) and set by [`tr.setLanguage()`](#trsetlanguage)  
+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 `values` rest parameter.  
+  
+If the key is not found or no language has been added or set before calling this function, it will return the key itself.  
+If the key is found and the translation contains placeholders but no values are passed, it will return the translation as-is, including unmodified placeholders.  
+If the key is found, the translation doesn't contain placeholders but values are still passed, they will be ignored and the translation will be returned as-is.  
+  
+<details><summary><h4>Example - click to view</h4></summary>
+
+```ts
+import { tr } from "@sv443-network/userutils";
+
+tr.addLanguage("en", {
+  "welcome": "Welcome",
+  "welcome_name": "Welcome, %1",
+});
+tr.addLanguage("de", {
+  "welcome": "Willkommen",
+  "welcome_name": "Willkommen, %1",
+});
+
+// this has to be called at least once before calling tr()
+tr.setLanguage("en");
+
+console.log(tr("welcome"));              // "Welcome"
+console.log(tr("welcome_name", "John")); // "Welcome, John"
+console.log(tr("non_existent_key"));     // "non_existent_key"
+
+// language can be changed at any time, synchronously
+tr.setLanguage("de");
+
+console.log(tr("welcome"));              // "Willkommen"
+console.log(tr("welcome_name", "John")); // "Willkommen, John"
+```
+
+</details>
+
 <br>
 
+### tr.addLanguage()
+Usage:  
+```ts
+tr.addLanguage(language: string, translations: Record<string, string>): void
+```
+
+Adds a language and its associated translations to the translation function.  
+The passed language can be any unique identifier, though I recommend sticking to the [ISO 639-1 standard.](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes)  
+The passed translations should be an object where the key is the translation key used in `tr()` and the value is the translation itself.  
+If `tr.addLanguage()` is called multiple times with the same language, the previous translations of that language will be overwritten.  
+  
+The translation values may contain placeholders in the format `%n`, where `n` is the number of the value starting at 1.  
+These can be used to inject values into the translation when calling `tr()`  
+  
+<details><summary><h4>Example - click to view</h4></summary>
+
+```ts
+import { tr } from "@sv443-network/userutils";
+
+// add a language with associated translations:
+
+tr.addLanguage("de", {
+  "color": "Farbe",
+});
+
+
+// with placeholders:
+
+tr.addLanguage("en", {
+  "welcome_generic": "Welcome!",
+  "welcome_name": "Welcome, %1!",
+  "welcome_extended": "Welcome, %1!\nYour last login was on %2\nYou have %3 unread messages",
+});
+
+
+// can work for multiple locales too:
+
+tr.addLanguage("en-US", {
+  "fries": "french fries",
+});
+tr.addLanguage("en-GB", {
+  "fries": "chips",
+});
+
+
+// apply default values for different locales to reduce redundancy:
+
+const translation_de = {
+  "greeting": "Guten Tag!",
+  "foo": "Foo",
+};
+tr.addLanguage("de-DE", translation_de);
+tr.addLanguage("de-CH", {
+  ...translation_de,
+  // overwrite the "greeting" but keep other keys as they are
+  "greeting": "Grüezi!",
+});
+tr.addLanguage("de-AT", {
+  ...translation_de,
+  // overwrite "greeting" again but keep other keys as they are
+  "greeting": "Grüß Gott!",
+});
+
+
+// example for custom pluralization:
+
+tr.addLanguage("en", {
+  "items_added-1": "Added 1 item to your cart",
+  "items_added-n": "Added %1 items to your cart",
+});
+
+/** Returns the custom pluralization identifier */
+function pl(num: number | unknown[] | NodeList) {
+  if(Array.isArray(num))
+    num = num.length;
+  return num === 1 ? "1" : "n";
+};
+
+const items = ["foo"];
+tr(`items_added-${pl(items)}`, items.length); // "Added 1 item to your cart"
+
+items.push("bar");
+tr(`items_added-${pl(items)}`, items.length); // "Added 2 items to your cart"
+```
+
+</details>
+
+<br>
+
+### tr.setLanguage()
+Usage:  
+```ts
+tr.setLanguage(language: string): void
+```
+
+Synchronously sets the language that will be used for translations.  
+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()`  
+  
+For an example, see [`tr()`](#tr)
+
+<br>
+
+### tr.getLanguage()
+Usage:  
+```ts
+tr.getLanguage(): string | undefined
+```
+
+Returns the currently active language set by [`tr.setLanguage()`](#trsetlanguage)  
+If no language has been set yet, it will return undefined.
+
+<br><br>
+
+## Utility types:
+UserUtils also offers some utility types that can be used in TypeScript projects.  
+They don't alter the runtime behavior of the code, but they can be used to make the code more readable and to prevent errors.
+
+### Stringifiable
+This type describes any value that either is a string itself or can be converted to a string.  
+To be considered stringifiable, the object needs to have a `toString()` method that returns a string (all primitive types have this method).  
+This method allows not just explicit conversion by calling it, but also implicit conversion by passing it into the `String()` constructor or by interpolating it in a template string.  
+  
+<details><summary><h4>Example - click to view</h4></summary>
+
+```ts
+import type { Stringifiable } from "@sv443-network/userutils";
+
+function logSomething(value: Stringifiable) {
+  console.log(`Log: ${value}`); // implicit conversion using `value.toString()`
+}
+
+const fooObject = {
+  toString: () => "hello world",
+};
+
+const barObject = {
+  baz: "",
+};
+
+logSomething("foo");     // "Log: foo"
+logSomething(42);        // "Log: 42"
+logSomething(true);      // "Log: true"
+logSomething({});        // "Log: [object Object]"
+logSomething(Symbol(1)); // "Log: Symbol(1)"
+logSomething(fooObject); // "Log: hello world"
+logSomething(barObject); // type error
+```
+
+</details>
+
 <br><br><br><br>
 
 <div style="text-align: center;" align="center">