summaryrefslogtreecommitdiffstats
path: root/apps
diff options
context:
space:
mode:
authorivarlovlie <git@ivarlovlie.no>2022-06-10 00:35:22 +0200
committerivarlovlie <git@ivarlovlie.no>2022-06-10 00:35:22 +0200
commit21764214c257949844d87e445f1a9f2736a20561 (patch)
tree9fb2369ea92a53c6ba19825424693c73aab7c1e1 /apps
parentf6156d9137d4c07dd7afc8c3288dc00879db0b73 (diff)
downloadgreatoffice-21764214c257949844d87e445f1a9f2736a20561.tar.xz
greatoffice-21764214c257949844d87e445f1a9f2736a20561.zip
feat: Add translations to stopwatch.svelte
This commit also demonstrates how to do i18n across apps.
Diffstat (limited to 'apps')
-rw-r--r--apps/projects/src/app/index.svelte21
-rw-r--r--apps/projects/src/app/lib/i18n/i18n-types.ts16
-rw-r--r--apps/projects/src/app/lib/i18n/nb/index.ts8
-rw-r--r--apps/projects/src/app/pages/_layout.svelte16
-rw-r--r--apps/projects/src/app/pages/home.svelte2
-rw-r--r--apps/projects/src/app/pages/views/settings-labels-tile.svelte10
-rw-r--r--apps/web-shared/.typesafe-i18n.json5
-rw-r--r--apps/web-shared/package.json7
-rw-r--r--apps/web-shared/pnpm-lock.yaml561
-rw-r--r--apps/web-shared/src/components/blowout-toolbelt.svelte2
-rw-r--r--apps/web-shared/src/components/stopwatch.svelte35
-rw-r--r--apps/web-shared/src/lib/i18n/en/index.ts13
-rw-r--r--apps/web-shared/src/lib/i18n/formatters.ts11
-rw-r--r--apps/web-shared/src/lib/i18n/i18n-types.ts66
-rw-r--r--apps/web-shared/src/lib/i18n/i18n-util.async.ts27
-rw-r--r--apps/web-shared/src/lib/i18n/i18n-util.sync.ts27
-rw-r--r--apps/web-shared/src/lib/i18n/i18n-util.ts31
-rw-r--r--apps/web-shared/src/lib/i18n/nb/index.ts13
-rw-r--r--apps/web-shared/src/lib/locale.ts (renamed from apps/projects/src/app/lib/stores/locale.ts)8
19 files changed, 835 insertions, 44 deletions
diff --git a/apps/projects/src/app/index.svelte b/apps/projects/src/app/index.svelte
index e397de3..5c02004 100644
--- a/apps/projects/src/app/index.svelte
+++ b/apps/projects/src/app/index.svelte
@@ -2,8 +2,9 @@
<svelte:window bind:online={online}/>
<script lang="ts">
+ import {Locales} from "$app/lib/i18n/i18n-types";
import {logout_user} from "$app/lib/services/user-service";
- import {currentLocale, preffered_or_default} from "$app/lib/stores/locale";
+ import {currentLocale, preffered_or_default} from "$shared/lib/locale";
import {CookieNames} from "$shared/lib/configuration";
import {get_cookie} from "$shared/lib/helpers";
import {Temporal} from "@js-temporal/polyfill";
@@ -29,23 +30,23 @@
console.log("Projects Startup Report", {
prefferedLocale: navigator.language,
timeZone: Temporal.Now.timeZone().id,
- go_theme: get_cookie(CookieNames.theme),
- go_locale: get_cookie(CookieNames.locale),
+ themeCookie: {name: CookieNames.theme, value: get_cookie(CookieNames.theme)},
+ localeCookie: {name: CookieNames.locale, value: get_cookie(CookieNames.locale)},
prefersColorScheme: window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"
});
currentLocale.subscribe(async locale => {
- locale = locale === "preffered" ? preffered_or_default() : locale;
- await loadLocaleAsync(locale);
- LL = i18nObject(locale);
- setLocale(locale);
+ locale = (locale === "preffered" ? preffered_or_default() : locale);
+ await loadLocaleAsync(locale as Locales);
+ LL = i18nObject(locale as Locales);
+ setLocale(locale as Locales);
});
onMount(async () => {
const locale = $currentLocale === "preffered" ? preffered_or_default() : $currentLocale;
- await loadLocaleAsync(locale);
- LL = i18nObject(locale);
- setLocale(locale);
+ await loadLocaleAsync(locale as Locales);
+ LL = i18nObject(locale as Locales);
+ setLocale(locale as Locales);
notOnlineText = LL.messages.noInternet();
});
diff --git a/apps/projects/src/app/lib/i18n/i18n-types.ts b/apps/projects/src/app/lib/i18n/i18n-types.ts
index f9fd9cc..b0031f6 100644
--- a/apps/projects/src/app/lib/i18n/i18n-types.ts
+++ b/apps/projects/src/app/lib/i18n/i18n-types.ts
@@ -117,6 +117,10 @@ type RootTranslation = {
* No categories
*/
noCategories: string
+ /**
+ * Categories
+ */
+ categories: string
}
settingsLabelsTile: {
/**
@@ -152,6 +156,10 @@ type RootTranslation = {
* No labels
*/
noLabels: string
+ /**
+ * Labels
+ */
+ labels: string
}
entryForm: {
/**
@@ -516,6 +524,10 @@ export type TranslationFunctions = {
* No categories
*/
noCategories: () => LocalizedString
+ /**
+ * Categories
+ */
+ categories: () => LocalizedString
}
settingsLabelsTile: {
/**
@@ -551,6 +563,10 @@ export type TranslationFunctions = {
* No labels
*/
noLabels: () => LocalizedString
+ /**
+ * Labels
+ */
+ labels: () => LocalizedString
}
entryForm: {
/**
diff --git a/apps/projects/src/app/lib/i18n/nb/index.ts b/apps/projects/src/app/lib/i18n/nb/index.ts
index af3a487..28e4bc1 100644
--- a/apps/projects/src/app/lib/i18n/nb/index.ts
+++ b/apps/projects/src/app/lib/i18n/nb/index.ts
@@ -104,16 +104,16 @@ const nb: Translation = {
hourSingleChar: "t",
minSingleChar: "m",
confirmDeleteEntry: "Er du sikker på at du vil slette denne raden?",
- newEntry: "Ny rad",
+ newEntry: "Ny tidsoppføring",
editEntry: "Rediger rad",
deleteEntry: "Slett rad",
loggedTimeToday: "Registrert tid hittil idag",
currentTime: "Klokken",
loading: "Laster",
stopwatch: "Stoppeklokke",
- todayEntries: "Dagens rader",
- noEntriesToday: "Ingen rader i dag",
- refreshTodayEntries: "Last inn dagens rader på nytt",
+ todayEntries: "Dagens tidsoppføringer",
+ noEntriesToday: "Ingen oppføringer i dag",
+ refreshTodayEntries: "Last inn dagens tidsoppføringer på nytt",
category: "Kategori",
timespan: "Tidsrom",
},
diff --git a/apps/projects/src/app/pages/_layout.svelte b/apps/projects/src/app/pages/_layout.svelte
index fb34593..b75c062 100644
--- a/apps/projects/src/app/pages/_layout.svelte
+++ b/apps/projects/src/app/pages/_layout.svelte
@@ -1,16 +1,16 @@
<script>
- import {onMount} from "svelte";
- import {location, link} from "svelte-spa-router";
- import {logout_user} from "$app/lib/services/user-service";
- import {random_string} from "$shared/lib/helpers";
- import {get_session_data} from "$shared/lib/session";
+ import { onMount } from "svelte";
+ import { location, link } from "svelte-spa-router";
+ import { logout_user } from "$app/lib/services/user-service";
+ import { random_string } from "$shared/lib/helpers";
+ import { get_session_data } from "$shared/lib/session";
import ProfileModal from "$app/pages/views/profile-modal.svelte";
- import {Menu, MenuItem, MenuItemSeparator} from "$shared/components/menu";
+ import { Menu, MenuItem, MenuItemSeparator } from "$shared/components/menu";
import Button from "$shared/components/button.svelte";
- import {IconNames} from "$shared/lib/configuration";
+ import { IconNames } from "$shared/lib/configuration";
import LL from "$app/lib/i18n/i18n-svelte";
import BlowoutToolbelt from "$shared/components/blowout-toolbelt.svelte";
- import {currentLocale} from "$app/lib/stores/locale";
+ import { currentLocale } from "$shared/lib/locale";
let ProfileModalFunctions = {};
let showUserMenu = false;
diff --git a/apps/projects/src/app/pages/home.svelte b/apps/projects/src/app/pages/home.svelte
index 33bb0d8..ff52275 100644
--- a/apps/projects/src/app/pages/home.svelte
+++ b/apps/projects/src/app/pages/home.svelte
@@ -1,6 +1,6 @@
<script lang="ts">
import LL from "$app/lib/i18n/i18n-svelte";
- import {delete_time_entry, get_time_entries, get_time_entry, update_time_entry} from "$shared/lib/api/time-entry";
+ import {delete_time_entry, get_time_entries, get_time_entry} from "$shared/lib/api/time-entry";
import {IconNames, QueryKeys} from "$shared/lib/configuration";
import {TimeEntryDto} from "$shared/lib/models/TimeEntryDto";
import {Temporal} from "@js-temporal/polyfill";
diff --git a/apps/projects/src/app/pages/views/settings-labels-tile.svelte b/apps/projects/src/app/pages/views/settings-labels-tile.svelte
index 59b5e30..3d5a567 100644
--- a/apps/projects/src/app/pages/views/settings-labels-tile.svelte
+++ b/apps/projects/src/app/pages/views/settings-labels-tile.svelte
@@ -7,15 +7,15 @@
import {Table, THead, TBody, TCell, TRow} from "$shared/components/table";
import LL from "$app/lib/i18n/i18n-svelte";
- let is_loading = true;
+ let isLoadingLabels = true;
$: active_labels = $labels.filter(c => !c.archived);
$: archived_labels = $labels.filter(c => c.archived);
async function load_labels() {
- is_loading = true;
+ isLoadingLabels = true;
await reload_labels();
- is_loading = false;
+ isLoadingLabels = false;
}
async function handle_edit_label_click(event) {
@@ -38,7 +38,7 @@
});
</script>
-<Tile class="col-6@md col-12 {is_loading ? 'c-disabled loading' : ''}">
+<Tile class="col-6@md col-12 {isLoadingLabels ? 'c-disabled loading' : ''}">
<h2 class="margin-bottom-xxs">{$LL.views.settingsLabelsTile.labels()}</h2>
{#if active_labels.length > 0 && archived_labels.length > 0}
<nav class="s-tabs text-sm">
@@ -101,7 +101,7 @@
<TCell type="th"
thScope="row"
colspan="3">
- No labels
+ {$LL.views.settingsLabelsTile.noLabels()}
</TCell>
</TRow>
{/if}
diff --git a/apps/web-shared/.typesafe-i18n.json b/apps/web-shared/.typesafe-i18n.json
new file mode 100644
index 0000000..574db64
--- /dev/null
+++ b/apps/web-shared/.typesafe-i18n.json
@@ -0,0 +1,5 @@
+{
+ "esmImports": true,
+ "outputPath": "./src/lib/i18n",
+ "$schema": "https://unpkg.com/typesafe-i18n@5.5.2/schema/typesafe-i18n.json"
+} \ No newline at end of file
diff --git a/apps/web-shared/package.json b/apps/web-shared/package.json
index fb14b4b..746ec06 100644
--- a/apps/web-shared/package.json
+++ b/apps/web-shared/package.json
@@ -2,12 +2,19 @@
"name": "greatoffice-web-shared",
"version": "0.0.1",
"private": "true",
+ "scripts": {
+ "dev": "npm-run-all --parallel vite typesafe-i18n",
+ "vite": "vite",
+ "typesafe-i18n": "typesafe-i18n"
+ },
"devDependencies": {
"@js-temporal/polyfill": "^0.4.1",
"fuzzysort": "^1.9.0",
+ "npm-run-all": "^4.1.5",
"svelte": "^3.48.0",
"svelte-feather-icons": "^4.0.0",
"svelte-spa-router": "^3.2.0",
+ "typesafe-i18n": "^5.5.2",
"typescript": "4.6.4"
}
}
diff --git a/apps/web-shared/pnpm-lock.yaml b/apps/web-shared/pnpm-lock.yaml
index 88ebee5..4f9a54f 100644
--- a/apps/web-shared/pnpm-lock.yaml
+++ b/apps/web-shared/pnpm-lock.yaml
@@ -3,17 +3,21 @@ lockfileVersion: 5.4
specifiers:
'@js-temporal/polyfill': ^0.4.1
fuzzysort: ^1.9.0
+ npm-run-all: ^4.1.5
svelte: ^3.48.0
svelte-feather-icons: ^4.0.0
svelte-spa-router: ^3.2.0
+ typesafe-i18n: ^5.5.2
typescript: 4.6.4
devDependencies:
'@js-temporal/polyfill': 0.4.1
fuzzysort: 1.9.0
+ npm-run-all: 4.1.5
svelte: 3.48.0
svelte-feather-icons: 4.0.0
svelte-spa-router: 3.2.0
+ typesafe-i18n: 5.5.2_typescript@4.6.4
typescript: 4.6.4
packages:
@@ -26,19 +30,534 @@ packages:
tslib: 2.4.0
dev: true
+ /ansi-styles/3.2.1:
+ resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==}
+ engines: {node: '>=4'}
+ dependencies:
+ color-convert: 1.9.3
+ dev: true
+
+ /balanced-match/1.0.2:
+ resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
+ dev: true
+
+ /brace-expansion/1.1.11:
+ resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
+ dependencies:
+ balanced-match: 1.0.2
+ concat-map: 0.0.1
+ dev: true
+
+ /call-bind/1.0.2:
+ resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==}
+ dependencies:
+ function-bind: 1.1.1
+ get-intrinsic: 1.1.1
+ dev: true
+
+ /chalk/2.4.2:
+ resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
+ engines: {node: '>=4'}
+ dependencies:
+ ansi-styles: 3.2.1
+ escape-string-regexp: 1.0.5
+ supports-color: 5.5.0
+ dev: true
+
+ /color-convert/1.9.3:
+ resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
+ dependencies:
+ color-name: 1.1.3
+ dev: true
+
+ /color-name/1.1.3:
+ resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==}
+ dev: true
+
+ /concat-map/0.0.1:
+ resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=}
+ dev: true
+
+ /cross-spawn/6.0.5:
+ resolution: {integrity: sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==}
+ engines: {node: '>=4.8'}
+ dependencies:
+ nice-try: 1.0.5
+ path-key: 2.0.1
+ semver: 5.7.1
+ shebang-command: 1.2.0
+ which: 1.3.1
+ dev: true
+
+ /define-properties/1.1.4:
+ resolution: {integrity: sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ has-property-descriptors: 1.0.0
+ object-keys: 1.1.1
+ dev: true
+
+ /error-ex/1.3.2:
+ resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==}
+ dependencies:
+ is-arrayish: 0.2.1
+ dev: true
+
+ /es-abstract/1.20.1:
+ resolution: {integrity: sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ call-bind: 1.0.2
+ es-to-primitive: 1.2.1
+ function-bind: 1.1.1
+ function.prototype.name: 1.1.5
+ get-intrinsic: 1.1.1
+ get-symbol-description: 1.0.0
+ has: 1.0.3
+ has-property-descriptors: 1.0.0
+ has-symbols: 1.0.3
+ internal-slot: 1.0.3
+ is-callable: 1.2.4
+ is-negative-zero: 2.0.2
+ is-regex: 1.1.4
+ is-shared-array-buffer: 1.0.2
+ is-string: 1.0.7
+ is-weakref: 1.0.2
+ object-inspect: 1.12.2
+ object-keys: 1.1.1
+ object.assign: 4.1.2
+ regexp.prototype.flags: 1.4.3
+ string.prototype.trimend: 1.0.5
+ string.prototype.trimstart: 1.0.5
+ unbox-primitive: 1.0.2
+ dev: true
+
+ /es-to-primitive/1.2.1:
+ resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ is-callable: 1.2.4
+ is-date-object: 1.0.5
+ is-symbol: 1.0.4
+ dev: true
+
+ /escape-string-regexp/1.0.5:
+ resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
+ engines: {node: '>=0.8.0'}
+ dev: true
+
+ /function-bind/1.1.1:
+ resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==}
+ dev: true
+
+ /function.prototype.name/1.1.5:
+ resolution: {integrity: sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ call-bind: 1.0.2
+ define-properties: 1.1.4
+ es-abstract: 1.20.1
+ functions-have-names: 1.2.3
+ dev: true
+
+ /functions-have-names/1.2.3:
+ resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==}
+ dev: true
+
/fuzzysort/1.9.0:
resolution: {integrity: sha512-MOxCT0qLTwLqmEwc7UtU045RKef7mc8Qz8eR4r2bLNEq9dy/c3ZKMEFp6IEst69otkQdFZ4FfgH2dmZD+ddX1g==}
dev: true
+ /get-intrinsic/1.1.1:
+ resolution: {integrity: sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==}
+ dependencies:
+ function-bind: 1.1.1
+ has: 1.0.3
+ has-symbols: 1.0.3
+ dev: true
+
+ /get-symbol-description/1.0.0:
+ resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ call-bind: 1.0.2
+ get-intrinsic: 1.1.1
+ dev: true
+
+ /graceful-fs/4.2.10:
+ resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==}
+ dev: true
+
+ /has-bigints/1.0.2:
+ resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==}
+ dev: true
+
+ /has-flag/3.0.0:
+ resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
+ engines: {node: '>=4'}
+ dev: true
+
+ /has-property-descriptors/1.0.0:
+ resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==}
+ dependencies:
+ get-intrinsic: 1.1.1
+ dev: true
+
+ /has-symbols/1.0.3:
+ resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==}
+ engines: {node: '>= 0.4'}
+ dev: true
+
+ /has-tostringtag/1.0.0:
+ resolution: {integrity: sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ has-symbols: 1.0.3
+ dev: true
+
+ /has/1.0.3:
+ resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==}
+ engines: {node: '>= 0.4.0'}
+ dependencies:
+ function-bind: 1.1.1
+ dev: true
+
+ /hosted-git-info/2.8.9:
+ resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==}
+ dev: true
+
+ /internal-slot/1.0.3:
+ resolution: {integrity: sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ get-intrinsic: 1.1.1
+ has: 1.0.3
+ side-channel: 1.0.4
+ dev: true
+
+ /is-arrayish/0.2.1:
+ resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==}
+ dev: true
+
+ /is-bigint/1.0.4:
+ resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==}
+ dependencies:
+ has-bigints: 1.0.2
+ dev: true
+
+ /is-boolean-object/1.1.2:
+ resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ call-bind: 1.0.2
+ has-tostringtag: 1.0.0
+ dev: true
+
+ /is-callable/1.2.4:
+ resolution: {integrity: sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==}
+ engines: {node: '>= 0.4'}
+ dev: true
+
+ /is-core-module/2.9.0:
+ resolution: {integrity: sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==}
+ dependencies:
+ has: 1.0.3
+ dev: true
+
+ /is-date-object/1.0.5:
+ resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ has-tostringtag: 1.0.0
+ dev: true
+
+ /is-negative-zero/2.0.2:
+ resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==}
+ engines: {node: '>= 0.4'}
+ dev: true
+
+ /is-number-object/1.0.7:
+ resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ has-tostringtag: 1.0.0
+ dev: true
+
+ /is-regex/1.1.4:
+ resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ call-bind: 1.0.2
+ has-tostringtag: 1.0.0
+ dev: true
+
+ /is-shared-array-buffer/1.0.2:
+ resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==}
+ dependencies:
+ call-bind: 1.0.2
+ dev: true
+
+ /is-string/1.0.7:
+ resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ has-tostringtag: 1.0.0
+ dev: true
+
+ /is-symbol/1.0.4:
+ resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ has-symbols: 1.0.3
+ dev: true
+
+ /is-weakref/1.0.2:
+ resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==}
+ dependencies:
+ call-bind: 1.0.2
+ dev: true
+
+ /isexe/2.0.0:
+ resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
+ dev: true
+
/jsbi/4.3.0:
resolution: {integrity: sha512-SnZNcinB4RIcnEyZqFPdGPVgrg2AcnykiBy0sHVJQKHYeaLUvi3Exj+iaPpLnFVkDPZIV4U0yvgC9/R4uEAZ9g==}
dev: true
+ /json-parse-better-errors/1.0.2:
+ resolution: {integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==}
+ dev: true
+
+ /load-json-file/4.0.0:
+ resolution: {integrity: sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==}
+ engines: {node: '>=4'}
+ dependencies:
+ graceful-fs: 4.2.10
+ parse-json: 4.0.0
+ pify: 3.0.0
+ strip-bom: 3.0.0
+ dev: true
+
+ /memorystream/0.3.1:
+ resolution: {integrity: sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==}
+ engines: {node: '>= 0.10.0'}
+ dev: true
+
+ /minimatch/3.1.2:
+ resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
+ dependencies:
+ brace-expansion: 1.1.11
+ dev: true
+
+ /nice-try/1.0.5:
+ resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==}
+ dev: true
+
+ /normalize-package-data/2.5.0:
+ resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==}
+ dependencies:
+ hosted-git-info: 2.8.9
+ resolve: 1.22.0
+ semver: 5.7.1
+ validate-npm-package-license: 3.0.4
+ dev: true
+
+ /npm-run-all/4.1.5:
+ resolution: {integrity: sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==}
+ engines: {node: '>= 4'}
+ hasBin: true
+ dependencies:
+ ansi-styles: 3.2.1
+ chalk: 2.4.2
+ cross-spawn: 6.0.5
+ memorystream: 0.3.1
+ minimatch: 3.1.2
+ pidtree: 0.3.1
+ read-pkg: 3.0.0
+ shell-quote: 1.7.3
+ string.prototype.padend: 3.1.3
+ dev: true
+
+ /object-inspect/1.12.2:
+ resolution: {integrity: sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==}
+ dev: true
+
+ /object-keys/1.1.1:
+ resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==}
+ engines: {node: '>= 0.4'}
+ dev: true
+
+ /object.assign/4.1.2:
+ resolution: {integrity: sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ call-bind: 1.0.2
+ define-properties: 1.1.4
+ has-symbols: 1.0.3
+ object-keys: 1.1.1
+ dev: true
+
+ /parse-json/4.0.0:
+ resolution: {integrity: sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==}
+ engines: {node: '>=4'}
+ dependencies:
+ error-ex: 1.3.2
+ json-parse-better-errors: 1.0.2
+ dev: true
+
+ /path-key/2.0.1:
+ resolution: {integrity: sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==}
+ engines: {node: '>=4'}
+ dev: true
+
+ /path-parse/1.0.7:
+ resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
+ dev: true
+
+ /path-type/3.0.0:
+ resolution: {integrity: sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==}
+ engines: {node: '>=4'}
+ dependencies:
+ pify: 3.0.0
+ dev: true
+
+ /pidtree/0.3.1:
+ resolution: {integrity: sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==}
+ engines: {node: '>=0.10'}
+ hasBin: true
+ dev: true
+
+ /pify/3.0.0:
+ resolution: {integrity: sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==}
+ engines: {node: '>=4'}
+ dev: true
+
+ /read-pkg/3.0.0:
+ resolution: {integrity: sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==}
+ engines: {node: '>=4'}
+ dependencies:
+ load-json-file: 4.0.0
+ normalize-package-data: 2.5.0
+ path-type: 3.0.0
+ dev: true
+
+ /regexp.prototype.flags/1.4.3:
+ resolution: {integrity: sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ call-bind: 1.0.2
+ define-properties: 1.1.4
+ functions-have-names: 1.2.3
+ dev: true
+
/regexparam/2.0.0:
resolution: {integrity: sha512-gJKwd2MVPWHAIFLsaYDZfyKzHNS4o7E/v8YmNf44vmeV2e4YfVoDToTOKTvE7ab68cRJ++kLuEXJBaEeJVt5ow==}
engines: {node: '>=8'}
dev: true
+ /resolve/1.22.0:
+ resolution: {integrity: sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==}
+ hasBin: true
+ dependencies:
+ is-core-module: 2.9.0
+ path-parse: 1.0.7
+ supports-preserve-symlinks-flag: 1.0.0
+ dev: true
+
+ /semver/5.7.1:
+ resolution: {integrity: sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==}
+ hasBin: true
+ dev: true
+
+ /shebang-command/1.2.0:
+ resolution: {integrity: sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=}
+ engines: {node: '>=0.10.0'}
+ dependencies:
+ shebang-regex: 1.0.0
+ dev: true
+
+ /shebang-regex/1.0.0:
+ resolution: {integrity: sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=}
+ engines: {node: '>=0.10.0'}
+ dev: true
+
+ /shell-quote/1.7.3:
+ resolution: {integrity: sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==}
+ dev: true
+
+ /side-channel/1.0.4:
+ resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==}
+ dependencies:
+ call-bind: 1.0.2
+ get-intrinsic: 1.1.1
+ object-inspect: 1.12.2
+ dev: true
+
+ /spdx-correct/3.1.1:
+ resolution: {integrity: sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==}
+ dependencies:
+ spdx-expression-parse: 3.0.1
+ spdx-license-ids: 3.0.11
+ dev: true
+
+ /spdx-exceptions/2.3.0:
+ resolution: {integrity: sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==}
+ dev: true
+
+ /spdx-expression-parse/3.0.1:
+ resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==}
+ dependencies:
+ spdx-exceptions: 2.3.0
+ spdx-license-ids: 3.0.11
+ dev: true
+
+ /spdx-license-ids/3.0.11:
+ resolution: {integrity: sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==}
+ dev: true
+
+ /string.prototype.padend/3.1.3:
+ resolution: {integrity: sha512-jNIIeokznm8SD/TZISQsZKYu7RJyheFNt84DUPrh482GC8RVp2MKqm2O5oBRdGxbDQoXrhhWtPIWQOiy20svUg==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ call-bind: 1.0.2
+ define-properties: 1.1.4
+ es-abstract: 1.20.1
+ dev: true
+
+ /string.prototype.trimend/1.0.5:
+ resolution: {integrity: sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==}
+ dependencies:
+ call-bind: 1.0.2
+ define-properties: 1.1.4
+ es-abstract: 1.20.1
+ dev: true
+
+ /string.prototype.trimstart/1.0.5:
+ resolution: {integrity: sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==}
+ dependencies:
+ call-bind: 1.0.2
+ define-properties: 1.1.4
+ es-abstract: 1.20.1
+ dev: true
+
+ /strip-bom/3.0.0:
+ resolution: {integrity: sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=}
+ engines: {node: '>=4'}
+ dev: true
+
+ /supports-color/5.5.0:
+ resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
+ engines: {node: '>=4'}
+ dependencies:
+ has-flag: 3.0.0
+ dev: true
+
+ /supports-preserve-symlinks-flag/1.0.0:
+ resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
+ engines: {node: '>= 0.4'}
+ dev: true
+
/svelte-feather-icons/4.0.0:
resolution: {integrity: sha512-4ieUsjp+VYa1r6y80jDt9zRiRUZyJNbESpRdHdJJhiBubyuXX96A7f1UZSK4olxzP6Qsg5ZAuyZlnmvD+/swAA==}
dependencies:
@@ -60,8 +579,50 @@ packages:
resolution: {integrity: sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==}
dev: true
+ /typesafe-i18n/5.5.2_typescript@4.6.4:
+ resolution: {integrity: sha512-EcqEHyiSujfIIxqTHuWvhd36Dmbp0rngYp+Z0IilCzRoZXSWGm/CdS5/jhkFySNGUEqtfgdD0gw1EnHbp6/54g==}
+ hasBin: true
+ peerDependencies:
+ typescript: '>=3.5.1'
+ dependencies:
+ typescript: 4.6.4
+ dev: true
+
/typescript/4.6.4:
resolution: {integrity: sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==}
engines: {node: '>=4.2.0'}
hasBin: true
dev: true
+
+ /unbox-primitive/1.0.2:
+ resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==}
+ dependencies:
+ call-bind: 1.0.2
+ has-bigints: 1.0.2
+ has-symbols: 1.0.3
+ which-boxed-primitive: 1.0.2
+ dev: true
+
+ /validate-npm-package-license/3.0.4:
+ resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==}
+ dependencies:
+ spdx-correct: 3.1.1
+ spdx-expression-parse: 3.0.1
+ dev: true
+
+ /which-boxed-primitive/1.0.2:
+ resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==}
+ dependencies:
+ is-bigint: 1.0.4
+ is-boolean-object: 1.1.2
+ is-number-object: 1.0.7
+ is-string: 1.0.7
+ is-symbol: 1.0.4
+ dev: true
+
+ /which/1.3.1:
+ resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==}
+ hasBin: true
+ dependencies:
+ isexe: 2.0.0
+ dev: true
diff --git a/apps/web-shared/src/components/blowout-toolbelt.svelte b/apps/web-shared/src/components/blowout-toolbelt.svelte
index 69e9902..b611a2d 100644
--- a/apps/web-shared/src/components/blowout-toolbelt.svelte
+++ b/apps/web-shared/src/components/blowout-toolbelt.svelte
@@ -32,7 +32,7 @@
right: 0 !important;
}
</style>
-<aside class="blowout position-fixed bg-light inner-glow shadow-xs padding-xxs bottom-50% right-0 z-index-2 {expanded ? 'expanded' : ''}">
+<aside class="blowout position-fixed bg-light inner-glow shadow-xs padding-xxs bottom-50% right-0 z-index-popover {expanded ? 'expanded' : ''}">
<LocaleSwitcher bind:show="{localeSwitcher.show}"
glow="{false}"
on:change={locale_change}
diff --git a/apps/web-shared/src/components/stopwatch.svelte b/apps/web-shared/src/components/stopwatch.svelte
index 3ed0d0e..1c602c8 100644
--- a/apps/web-shared/src/components/stopwatch.svelte
+++ b/apps/web-shared/src/components/stopwatch.svelte
@@ -2,6 +2,10 @@
import Button from "$shared/components/button.svelte";
import {Textarea} from "$shared/components/form";
import {StorageKeys} from "$shared/lib/configuration";
+ import {TranslationFunctions} from "$shared/lib/i18n/i18n-types";
+ import {loadLocaleAsync} from "$shared/lib/i18n/i18n-util.async";
+ import {i18nObject} from "$shared/lib/i18n/i18n-util";
+ import {currentLocale} from "$shared/lib/locale";
import {StoreType, writable_persistent} from "$shared/lib/persistent-store";
import {Temporal} from "@js-temporal/polyfill";
import {createEventDispatcher, onMount} from "svelte";
@@ -24,6 +28,7 @@
});
let timeString;
+ let LL = i18nObject($currentLocale);
$: if ($state.hours || $state.minutes || $state.seconds) {
timeString = $state.hours.toLocaleString(undefined, {minimumIntegerDigits: 2})
@@ -33,15 +38,22 @@
timeString = "--:--:--";
}
- onMount(() => {
+ currentLocale.subscribe(async val => {
+ await loadLocaleAsync(val);
+ LL = i18nObject(val);
+ });
+
+ onMount(async () => {
start_if_running();
+ await loadLocaleAsync($currentLocale);
+ LL = i18nObject($currentLocale);
});
function start_if_running() {
if ($state.isRunning) {
if (Temporal.PlainDateTime.compare($state.lastStep, Temporal.Now.plainDateTimeISO()) == -1) {
const duration = Temporal.Now.plainDateTimeISO().since($state.lastStep, {smallestUnit: "second"});
- console.log("lastStep",$state.lastStep.toString());
+ console.log("lastStep", $state.lastStep.toString());
console.log("duration", duration.toString());
console.log(duration.seconds);
// for (let i = 0; i < steps; i++) {
@@ -152,23 +164,23 @@
on:click={on_start_stop}/>
{#if $state.startTime}
- <Button title="Reset"
- text="Reset"
+ <Button title="{LL.stopwatch.reset()}"
+ text="{LL.stopwatch.reset()}"
variant="link"
class="bg-error-lighter@hover color-white@hover"
on:click={reset}/>
{#if !$state.isRunning}
- <Button title="Round up"
- text="Round up"
+ <Button title="{LL.stopwatch.roundUp()}"
+ text="{LL.stopwatch.roundUp()}"
variant="link"
on:click={on_round_up}/>
- <Button title="Round down"
- text="Round down"
+ <Button title="{LL.stopwatch.roundDown()}"
+ text="{LL.stopwatch.roundDown()}"
variant="link"
on:click={on_round_down}/>
{#if $state.minutes > 0 || $state.hours > 0}
- <Button title="Create entry"
- text="Create entry"
+ <Button title="{LL.stopwatch.createEntry()}"
+ text="{LL.stopwatch.createEntry()}"
variant="link"
on:click={on_create_entry}/>
{/if}
@@ -176,8 +188,9 @@
{/if}
</div>
</div>
+
<Textarea class="width-100% margin-top-xs"
- placeholder="What's your focus?"
+ placeholder="{LL.stopwatch.whatsYourFocus()}"
rows="1"
bind:value={$state.note}
/>
diff --git a/apps/web-shared/src/lib/i18n/en/index.ts b/apps/web-shared/src/lib/i18n/en/index.ts
new file mode 100644
index 0000000..65fc0df
--- /dev/null
+++ b/apps/web-shared/src/lib/i18n/en/index.ts
@@ -0,0 +1,13 @@
+import type {BaseTranslation} from "../i18n-types";
+
+const en: BaseTranslation = {
+ stopwatch: {
+ roundUp: "Round up",
+ roundDown: "Round down",
+ createEntry: "Create entry",
+ whatsYourFocus: "What's your focus?",
+ reset: "Reset"
+ }
+};
+
+export default en;
diff --git a/apps/web-shared/src/lib/i18n/formatters.ts b/apps/web-shared/src/lib/i18n/formatters.ts
new file mode 100644
index 0000000..78734f9
--- /dev/null
+++ b/apps/web-shared/src/lib/i18n/formatters.ts
@@ -0,0 +1,11 @@
+import type { FormattersInitializer } from 'typesafe-i18n'
+import type { Locales, Formatters } from './i18n-types'
+
+export const initFormatters: FormattersInitializer<Locales, Formatters> = (locale: Locales) => {
+
+ const formatters: Formatters = {
+ // add your formatter functions here
+ }
+
+ return formatters
+}
diff --git a/apps/web-shared/src/lib/i18n/i18n-types.ts b/apps/web-shared/src/lib/i18n/i18n-types.ts
new file mode 100644
index 0000000..2048802
--- /dev/null
+++ b/apps/web-shared/src/lib/i18n/i18n-types.ts
@@ -0,0 +1,66 @@
+// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten.
+/* eslint-disable */
+import type { BaseTranslation as BaseTranslationType, LocalizedString } from 'typesafe-i18n'
+
+export type BaseTranslation = BaseTranslationType
+export type BaseLocale = 'en'
+
+export type Locales =
+ | 'en'
+ | 'nb'
+
+export type Translation = RootTranslation
+
+export type Translations = RootTranslation
+
+type RootTranslation = {
+ stopwatch: {
+ /**
+ * Round up
+ */
+ roundUp: string
+ /**
+ * Round down
+ */
+ roundDown: string
+ /**
+ * Create entry
+ */
+ createEntry: string
+ /**
+ * What's your focus?
+ */
+ whatsYourFocus: string
+ /**
+ * Reset
+ */
+ reset: string
+ }
+}
+
+export type TranslationFunctions = {
+ stopwatch: {
+ /**
+ * Round up
+ */
+ roundUp: () => LocalizedString
+ /**
+ * Round down
+ */
+ roundDown: () => LocalizedString
+ /**
+ * Create entry
+ */
+ createEntry: () => LocalizedString
+ /**
+ * What's your focus?
+ */
+ whatsYourFocus: () => LocalizedString
+ /**
+ * Reset
+ */
+ reset: () => LocalizedString
+ }
+}
+
+export type Formatters = {}
diff --git a/apps/web-shared/src/lib/i18n/i18n-util.async.ts b/apps/web-shared/src/lib/i18n/i18n-util.async.ts
new file mode 100644
index 0000000..90e55a7
--- /dev/null
+++ b/apps/web-shared/src/lib/i18n/i18n-util.async.ts
@@ -0,0 +1,27 @@
+// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten.
+/* eslint-disable */
+
+import { initFormatters } from './formatters'
+import type { Locales, Translations } from './i18n-types.js'
+import { loadedFormatters, loadedLocales, locales } from './i18n-util'
+
+const localeTranslationLoaders = {
+ en: () => import('./en/index.js'),
+ nb: () => import('./nb/index.js'),
+}
+
+const updateDictionary = (locale: Locales, dictionary: Partial<Translations>) =>
+ loadedLocales[locale] = { ...loadedLocales[locale], ...dictionary }
+
+export const loadLocaleAsync = async (locale: Locales): Promise<void> => {
+ updateDictionary(
+ locale,
+ (await localeTranslationLoaders[locale]()).default as unknown as Translations
+ )
+ loadFormatters(locale)
+}
+
+export const loadAllLocalesAsync = (): Promise<void[]> => Promise.all(locales.map(loadLocaleAsync))
+
+export const loadFormatters = (locale: Locales): void =>
+ void (loadedFormatters[locale] = initFormatters(locale))
diff --git a/apps/web-shared/src/lib/i18n/i18n-util.sync.ts b/apps/web-shared/src/lib/i18n/i18n-util.sync.ts
new file mode 100644
index 0000000..8909831
--- /dev/null
+++ b/apps/web-shared/src/lib/i18n/i18n-util.sync.ts
@@ -0,0 +1,27 @@
+// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten.
+/* eslint-disable */
+
+import { initFormatters } from './formatters'
+import type { Locales, Translations } from './i18n-types.js'
+import { loadedFormatters, loadedLocales, locales } from './i18n-util'
+
+import en from './en/index.js'
+import nb from './nb/index.js'
+
+const localeTranslations = {
+ en,
+ nb,
+}
+
+export const loadLocale = (locale: Locales): void => {
+ if (loadedLocales[locale]) return
+
+ loadedLocales[locale] = localeTranslations[locale] as unknown as Translations
+ loadFormatters(locale)
+}
+
+export const loadAllLocales = (): void => locales.forEach(loadLocale)
+
+export const loadFormatters = (locale: Locales): void => {
+ loadedFormatters[locale] = initFormatters(locale)
+}
diff --git a/apps/web-shared/src/lib/i18n/i18n-util.ts b/apps/web-shared/src/lib/i18n/i18n-util.ts
new file mode 100644
index 0000000..5a9dd0d
--- /dev/null
+++ b/apps/web-shared/src/lib/i18n/i18n-util.ts
@@ -0,0 +1,31 @@
+// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten.
+/* eslint-disable */
+
+import { i18n as initI18n, i18nObject as initI18nObject, i18nString as initI18nString } from 'typesafe-i18n'
+import type { LocaleDetector } from 'typesafe-i18n/detectors'
+import { detectLocale as detectLocaleFn } from 'typesafe-i18n/detectors'
+import type { Formatters, Locales, Translations, TranslationFunctions } from './i18n-types.js'
+
+export const baseLocale: Locales = 'en'
+
+export const locales: Locales[] = [
+ 'en',
+ 'nb'
+]
+
+export const loadedLocales = {} as Record<Locales, Translations>
+
+export const loadedFormatters = {} as Record<Locales, Formatters>
+
+export const i18nString = (locale: Locales) => initI18nString<Locales, Formatters>(locale, loadedFormatters[locale])
+
+export const i18nObject = (locale: Locales) =>
+ initI18nObject<Locales, Translations, TranslationFunctions, Formatters>(
+ locale,
+ loadedLocales[locale],
+ loadedFormatters[locale]
+ )
+
+export const i18n = () => initI18n<Locales, Translations, TranslationFunctions, Formatters>(loadedLocales, loadedFormatters)
+
+export const detectLocale = (...detectors: LocaleDetector[]) => detectLocaleFn<Locales>(baseLocale, locales, ...detectors)
diff --git a/apps/web-shared/src/lib/i18n/nb/index.ts b/apps/web-shared/src/lib/i18n/nb/index.ts
new file mode 100644
index 0000000..d7f557d
--- /dev/null
+++ b/apps/web-shared/src/lib/i18n/nb/index.ts
@@ -0,0 +1,13 @@
+import type {Translation} from "../i18n-types";
+
+const nb: Translation = {
+ stopwatch: {
+ roundUp: "Rund opp",
+ roundDown: "Rund ned",
+ createEntry: "Opprett tidsoppføring",
+ whatsYourFocus: "Hva skal du fokusere på?",
+ reset: "Tilbakestill"
+ }
+};
+
+export default nb;
diff --git a/apps/projects/src/app/lib/stores/locale.ts b/apps/web-shared/src/lib/locale.ts
index 1215c20..acb9ae5 100644
--- a/apps/projects/src/app/lib/stores/locale.ts
+++ b/apps/web-shared/src/lib/locale.ts
@@ -1,9 +1,8 @@
-import {base_domain, CookieNames} from "$shared/lib/configuration";
-import {get_cookie, set_cookie} from "$shared/lib/helpers";
import {writable} from "svelte/store";
-import type {Locales} from "$app/lib/i18n/i18n-types";
+import {base_domain, CookieNames} from "./configuration";
+import {get_cookie, set_cookie} from "./helpers";
-export function preffered_or_default(): Locales {
+export function preffered_or_default() {
if (/^en\b/i.test(navigator.language)) {
return "en";
}
@@ -13,6 +12,7 @@ export function preffered_or_default(): Locales {
return "en";
}
+type Locales = "en"|"nb";
export const currentLocale = writable<Locales>((get_cookie(CookieNames.locale) ?? preffered_or_default()) as Locales);
currentLocale.subscribe(locale => {
//@ts-ignore