feat: i18next

This commit is contained in:
Torsten Dittmann
2022-09-21 18:29:50 +02:00
parent 7abaf42b8b
commit 6922610ff6
9 changed files with 187 additions and 198 deletions
+68 -164
View File
@@ -12,6 +12,8 @@
"@aw-labs/icons": "0.0.0-54",
"@aw-labs/ui": "0.0.0-54",
"echarts": "^5.3.3",
"i18next": "^21.9.2",
"i18next-resources-to-backend": "^1.0.0",
"tippy.js": "^6.3.7",
"web-vitals": "^2.1.4"
},
@@ -37,7 +39,6 @@
"sass": "^1.54.9",
"svelte": "^3.50.1",
"svelte-check": "^2.9.0",
"svelte-i18n": "^3.4.0",
"svelte-jester": "^2.3.2",
"svelte-preprocess": "^4.10.7",
"tslib": "^2.4.0",
@@ -641,7 +642,6 @@
"version": "7.17.2",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.2.tgz",
"integrity": "sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw==",
"dev": true,
"dependencies": {
"regenerator-runtime": "^0.13.4"
},
@@ -762,55 +762,6 @@
"url": "https://opencollective.com/eslint"
}
},
"node_modules/@formatjs/ecma402-abstract": {
"version": "1.11.4",
"resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz",
"integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==",
"dev": true,
"dependencies": {
"@formatjs/intl-localematcher": "0.2.25",
"tslib": "^2.1.0"
}
},
"node_modules/@formatjs/fast-memoize": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-1.2.1.tgz",
"integrity": "sha512-Rg0e76nomkz3vF9IPlKeV+Qynok0r7YZjL6syLz4/urSg0IbjPZCB/iYUMNsYA643gh4mgrX3T7KEIFIxJBQeg==",
"dev": true,
"dependencies": {
"tslib": "^2.1.0"
}
},
"node_modules/@formatjs/icu-messageformat-parser": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.1.0.tgz",
"integrity": "sha512-Qxv/lmCN6hKpBSss2uQ8IROVnta2r9jd3ymUEIjm2UyIkUCHVcbUVRGL/KS/wv7876edvsPe+hjHVJ4z8YuVaw==",
"dev": true,
"dependencies": {
"@formatjs/ecma402-abstract": "1.11.4",
"@formatjs/icu-skeleton-parser": "1.3.6",
"tslib": "^2.1.0"
}
},
"node_modules/@formatjs/icu-skeleton-parser": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.3.6.tgz",
"integrity": "sha512-I96mOxvml/YLrwU2Txnd4klA7V8fRhb6JG/4hm3VMNmeJo1F03IpV2L3wWt7EweqNLES59SZ4d6hVOPCSf80Bg==",
"dev": true,
"dependencies": {
"@formatjs/ecma402-abstract": "1.11.4",
"tslib": "^2.1.0"
}
},
"node_modules/@formatjs/intl-localematcher": {
"version": "0.2.25",
"resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.2.25.tgz",
"integrity": "sha512-YmLcX70BxoSopLFdLr1Ds99NdlTI2oWoLbaUW2M406lxOIPzE1KQhRz2fPUkq34xVZQaihCoU29h0KK7An3bhA==",
"dev": true,
"dependencies": {
"tslib": "^2.1.0"
}
},
"node_modules/@humanwhocodes/config-array": {
"version": "0.10.4",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.4.tgz",
@@ -4414,6 +4365,44 @@
"node": ">=10.17.0"
}
},
"node_modules/i18next": {
"version": "21.9.2",
"resolved": "https://registry.npmjs.org/i18next/-/i18next-21.9.2.tgz",
"integrity": "sha512-00fVrLQOwy45nm3OtC9l1WiLK3nJlIYSljgCt0qzTaAy65aciMdRy9GsuW+a2AtKtdg9/njUGfRH30LRupV7ZQ==",
"funding": [
{
"type": "individual",
"url": "https://locize.com"
},
{
"type": "individual",
"url": "https://locize.com/i18next.html"
},
{
"type": "individual",
"url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project"
}
],
"dependencies": {
"@babel/runtime": "^7.17.2"
}
},
"node_modules/i18next-resources-to-backend": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/i18next-resources-to-backend/-/i18next-resources-to-backend-1.0.0.tgz",
"integrity": "sha512-mxntiPK84guqzYP/0T4OwU6BWda57ar7Tw5pU8BjM8dS4rULOtYU4JHMXfCbqZhNQEPCrp1WrVBEPk6yOze8jw==",
"dependencies": {
"@babel/runtime": "7.14.0"
}
},
"node_modules/i18next-resources-to-backend/node_modules/@babel/runtime": {
"version": "7.14.0",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.0.tgz",
"integrity": "sha512-JELkvo/DlpNdJ7dlyw/eY7E0suy5i5GQH+Vlxaq1nsNJ+H7f4Vtv3jMeCEgRhZZQFXTjldYfQgv2qmM6M1v5wA==",
"dependencies": {
"regenerator-runtime": "^0.13.4"
}
},
"node_modules/iconv-lite": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
@@ -4513,18 +4502,6 @@
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"dev": true
},
"node_modules/intl-messageformat": {
"version": "9.13.0",
"resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-9.13.0.tgz",
"integrity": "sha512-7sGC7QnSQGa5LZP7bXLDhVDtQOeKGeBFGHF2Y8LVBwYZoQZCgWeKoPGTa5GMG8g/TzDgeXuYJQis7Ggiw2xTOw==",
"dev": true,
"dependencies": {
"@formatjs/ecma402-abstract": "1.11.4",
"@formatjs/fast-memoize": "1.2.1",
"@formatjs/icu-messageformat-parser": "2.1.0",
"tslib": "^2.1.0"
}
},
"node_modules/is-arrayish": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
@@ -6839,8 +6816,7 @@
"node_modules/regenerator-runtime": {
"version": "0.13.9",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz",
"integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==",
"dev": true
"integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA=="
},
"node_modules/regexparam": {
"version": "2.0.1",
@@ -7453,28 +7429,6 @@
"svelte": ">=3.19.0"
}
},
"node_modules/svelte-i18n": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/svelte-i18n/-/svelte-i18n-3.4.0.tgz",
"integrity": "sha512-590N+YIRlebDT3fXmuAxd4guQZLR3vm4kCs5UhWYmw3SxOlJNZ7HwYYiw6d4jDr7P+Cx7DSopk1Z1K9wn8B6EA==",
"dev": true,
"dependencies": {
"deepmerge": "^4.2.2",
"estree-walker": "^2.0.1",
"intl-messageformat": "^9.3.15",
"sade": "^1.7.4",
"tiny-glob": "^0.2.6"
},
"bin": {
"svelte-i18n": "dist/cli.js"
},
"engines": {
"node": ">= 11.15.0"
},
"peerDependencies": {
"svelte": "^3.25.1"
}
},
"node_modules/svelte-jester": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/svelte-jester/-/svelte-jester-2.3.2.tgz",
@@ -8720,7 +8674,6 @@
"version": "7.17.2",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.2.tgz",
"integrity": "sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw==",
"dev": true,
"requires": {
"regenerator-runtime": "^0.13.4"
}
@@ -8813,55 +8766,6 @@
"strip-json-comments": "^3.1.1"
}
},
"@formatjs/ecma402-abstract": {
"version": "1.11.4",
"resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz",
"integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==",
"dev": true,
"requires": {
"@formatjs/intl-localematcher": "0.2.25",
"tslib": "^2.1.0"
}
},
"@formatjs/fast-memoize": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-1.2.1.tgz",
"integrity": "sha512-Rg0e76nomkz3vF9IPlKeV+Qynok0r7YZjL6syLz4/urSg0IbjPZCB/iYUMNsYA643gh4mgrX3T7KEIFIxJBQeg==",
"dev": true,
"requires": {
"tslib": "^2.1.0"
}
},
"@formatjs/icu-messageformat-parser": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.1.0.tgz",
"integrity": "sha512-Qxv/lmCN6hKpBSss2uQ8IROVnta2r9jd3ymUEIjm2UyIkUCHVcbUVRGL/KS/wv7876edvsPe+hjHVJ4z8YuVaw==",
"dev": true,
"requires": {
"@formatjs/ecma402-abstract": "1.11.4",
"@formatjs/icu-skeleton-parser": "1.3.6",
"tslib": "^2.1.0"
}
},
"@formatjs/icu-skeleton-parser": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.3.6.tgz",
"integrity": "sha512-I96mOxvml/YLrwU2Txnd4klA7V8fRhb6JG/4hm3VMNmeJo1F03IpV2L3wWt7EweqNLES59SZ4d6hVOPCSf80Bg==",
"dev": true,
"requires": {
"@formatjs/ecma402-abstract": "1.11.4",
"tslib": "^2.1.0"
}
},
"@formatjs/intl-localematcher": {
"version": "0.2.25",
"resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.2.25.tgz",
"integrity": "sha512-YmLcX70BxoSopLFdLr1Ds99NdlTI2oWoLbaUW2M406lxOIPzE1KQhRz2fPUkq34xVZQaihCoU29h0KK7An3bhA==",
"dev": true,
"requires": {
"tslib": "^2.1.0"
}
},
"@humanwhocodes/config-array": {
"version": "0.10.4",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.4.tgz",
@@ -11560,6 +11464,32 @@
"dev": true,
"peer": true
},
"i18next": {
"version": "21.9.2",
"resolved": "https://registry.npmjs.org/i18next/-/i18next-21.9.2.tgz",
"integrity": "sha512-00fVrLQOwy45nm3OtC9l1WiLK3nJlIYSljgCt0qzTaAy65aciMdRy9GsuW+a2AtKtdg9/njUGfRH30LRupV7ZQ==",
"requires": {
"@babel/runtime": "^7.17.2"
}
},
"i18next-resources-to-backend": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/i18next-resources-to-backend/-/i18next-resources-to-backend-1.0.0.tgz",
"integrity": "sha512-mxntiPK84guqzYP/0T4OwU6BWda57ar7Tw5pU8BjM8dS4rULOtYU4JHMXfCbqZhNQEPCrp1WrVBEPk6yOze8jw==",
"requires": {
"@babel/runtime": "7.14.0"
},
"dependencies": {
"@babel/runtime": {
"version": "7.14.0",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.0.tgz",
"integrity": "sha512-JELkvo/DlpNdJ7dlyw/eY7E0suy5i5GQH+Vlxaq1nsNJ+H7f4Vtv3jMeCEgRhZZQFXTjldYfQgv2qmM6M1v5wA==",
"requires": {
"regenerator-runtime": "^0.13.4"
}
}
}
},
"iconv-lite": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
@@ -11632,18 +11562,6 @@
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"dev": true
},
"intl-messageformat": {
"version": "9.13.0",
"resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-9.13.0.tgz",
"integrity": "sha512-7sGC7QnSQGa5LZP7bXLDhVDtQOeKGeBFGHF2Y8LVBwYZoQZCgWeKoPGTa5GMG8g/TzDgeXuYJQis7Ggiw2xTOw==",
"dev": true,
"requires": {
"@formatjs/ecma402-abstract": "1.11.4",
"@formatjs/fast-memoize": "1.2.1",
"@formatjs/icu-messageformat-parser": "2.1.0",
"tslib": "^2.1.0"
}
},
"is-arrayish": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
@@ -13406,8 +13324,7 @@
"regenerator-runtime": {
"version": "0.13.9",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz",
"integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==",
"dev": true
"integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA=="
},
"regexparam": {
"version": "2.0.1",
@@ -13866,19 +13783,6 @@
"dev": true,
"requires": {}
},
"svelte-i18n": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/svelte-i18n/-/svelte-i18n-3.4.0.tgz",
"integrity": "sha512-590N+YIRlebDT3fXmuAxd4guQZLR3vm4kCs5UhWYmw3SxOlJNZ7HwYYiw6d4jDr7P+Cx7DSopk1Z1K9wn8B6EA==",
"dev": true,
"requires": {
"deepmerge": "^4.2.2",
"estree-walker": "^2.0.1",
"intl-messageformat": "^9.3.15",
"sade": "^1.7.4",
"tiny-glob": "^0.2.6"
}
},
"svelte-jester": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/svelte-jester/-/svelte-jester-2.3.2.tgz",
+2 -1
View File
@@ -22,6 +22,8 @@
"@aw-labs/icons": "0.0.0-54",
"@aw-labs/ui": "0.0.0-54",
"echarts": "^5.3.3",
"i18next": "^21.9.2",
"i18next-resources-to-backend": "^1.0.0",
"tippy.js": "^6.3.7",
"web-vitals": "^2.1.4"
},
@@ -47,7 +49,6 @@
"sass": "^1.54.9",
"svelte": "^3.50.1",
"svelte-check": "^2.9.0",
"svelte-i18n": "^3.4.0",
"svelte-jester": "^2.3.2",
"svelte-preprocess": "^4.10.7",
"tslib": "^2.4.0",
+76
View File
@@ -0,0 +1,76 @@
import i18next from 'i18next';
import type { i18n } from 'i18next';
import { writable, type Readable, type Writable } from 'svelte/store';
import resourcesToBackend from 'i18next-resources-to-backend';
export const supportedLanguages = ['de', 'en'];
interface TranslationService {
i18n: Readable<i18n>;
}
export const isLoading = writable(true);
export class I18NextTranslationStore implements TranslationService {
public i18n: Readable<i18n>;
public isLoading: Writable<boolean>;
constructor(i18n: i18n) {
this.i18n = this.createInstance(i18n);
this.isLoading = this.createLoadingInstance(i18n);
}
private createInstance(i18n: i18n): Writable<i18n> {
const i18nWritable = writable(i18n);
i18n.on('initialized', () => {
i18nWritable.set(i18n);
});
i18n.on('loaded', () => {
i18nWritable.set(i18n);
});
i18n.on('added', () => i18nWritable.set(i18n));
i18n.on('languageChanged', () => {
i18nWritable.set(i18n);
});
return i18nWritable;
}
private createLoadingInstance(i18n: i18n): Writable<boolean> {
// if loaded resources are empty || {}, set loading to true
i18n.on('loaded', (resources) => {
Object.keys(resources).length !== 0 && isLoading.set(false);
});
// if resources failed loading, set loading to true
i18n.on('failedLoading', () => {
isLoading.set(true);
});
return isLoading;
}
}
export const createI18nStore = () => {
i18next
.use(
resourcesToBackend((language, _namespace, callback) => {
import(`$lib/translations/${language}.json`)
.then((resources) => {
callback(null, resources);
})
.catch((error) => {
callback(error, null);
});
})
)
.init({
lng: 'en',
fallbackLng: 'en'
});
const i18nStore = new I18NextTranslationStore(i18next);
return i18nStore.i18n;
};
export const _ = createI18nStore();
+8
View File
@@ -5,6 +5,7 @@
"name": "Name",
"email": "Email",
"status": "Status",
"password": "Passwort",
"id": "ID",
"joined": "Beigetreten",
"verified": "verifiziert",
@@ -19,6 +20,13 @@
"title": "Benutzer",
"search": "Suche nach Name, Email oder ID",
"create": "Benutzer erstellen"
},
"create": {
"users": {
"name": "$t(models.user.name) eingeben",
"email": "$t(models.user.email) eingeben",
"password": "$t(models.user.password) eingeben"
}
}
}
}
+8
View File
@@ -5,6 +5,7 @@
"name": "Name",
"email": "Email",
"status": "Status",
"password": "Password",
"id": "ID",
"joined": "Joined",
"verified": "verified",
@@ -19,6 +20,13 @@
"title": "Users",
"search": "Search by name, email, or ID",
"create": "Create user"
},
"create": {
"users": {
"name": "Enter $t(models.user.name)",
"email": "Enter $t(models.user.email)",
"password": "Enter $t(models.user.password)"
}
}
}
}
+1 -2
View File
@@ -10,7 +10,6 @@
import Notifications from '$lib/layout/notifications.svelte';
import Loading from './_loading.svelte';
import { webVitals } from '$lib/helpers/vitals';
import { isLoading, _ } from 'svelte-i18n';
let loaded = false;
@@ -84,7 +83,7 @@
<Notifications />
{#if loaded && !$isLoading}
{#if loaded}
<slot />
{:else}
<Loading />
-9
View File
@@ -1,11 +1,2 @@
import '@aw-labs/ui/src/_index.scss';
import 'tippy.js/dist/tippy.css';
import { register, init, getLocaleFromNavigator } from 'svelte-i18n';
register('en', () => import('$lib/translations/en.json'));
register('de', () => import('$lib/translations/de.json'));
init({
fallbackLocale: 'en',
initialLocale: getLocaleFromNavigator()
});
@@ -22,7 +22,7 @@
import { usersList } from './store';
import type { Models } from '@aw-labs/appwrite-console';
import { pageLimit } from '$lib/stores/layout';
import { _ } from 'svelte-i18n';
import { _ } from '$lib/i18n';
let showCreate = false;
let search = '';
@@ -39,7 +39,7 @@
</script>
<Container>
<Search bind:search placeholder={$_('services.authentication.users.search')}>
<Search bind:search placeholder={$_.t('services.authentication.users.search')}>
<span
use:event={{
name: 'console_users',
@@ -50,43 +50,44 @@
}}>
<Button on:click={() => (showCreate = true)}>
<span class="icon-plus" aria-hidden="true" />
<span class="text">{$_('services.authentication.users.create')}</span>
<span class="text">{$_.t('services.authentication.users.create')}</span>
</Button>
</span>
</Search>
{#if $usersList?.total}
<Table>
<TableHeader>
<TableCellHead>{$_('models.user.name')}</TableCellHead>
<TableCellHead>{$_('models.user.email')}</TableCellHead>
<TableCellHead width={100}>{$_('models.user.status')}</TableCellHead>
<TableCellHead width={100}>{$_('models.user.id')}</TableCellHead>
<TableCellHead>{$_('models.user.joined')}</TableCellHead>
<TableCellHead>{$_.t('models.user.name')}</TableCellHead>
<TableCellHead>{$_.t('models.user.email')}</TableCellHead>
<TableCellHead width={100}>{$_.t('models.user.status')}</TableCellHead>
<TableCellHead width={100}>{$_.t('models.user.id')}</TableCellHead>
<TableCellHead>{$_.t('models.user.joined')}</TableCellHead>
</TableHeader>
<TableBody>
{#each $usersList.users as user}
<TableRowLink
href={`${base}/console/project-${project}/authentication/user/${user.$id}`}>
<TableCell title={$_('models.user.name')}>
<TableCell title={$_.t('models.user.name')}>
<div class="u-flex u-gap-12 u-cross-center">
<Avatar size={32} src={getAvatar(user.name)} name={user.name} />
<span class="text u-trim">{user.name ? user.name : 'n/a'}</span>
</div>
</TableCell>
<TableCellText title={$_('models.user.email')}>{user.email}</TableCellText>
<TableCell title={$_('models.user.status')}>
<TableCellText title={$_.t('models.user.email')}
>{user.email}</TableCellText>
<TableCell title={$_.t('models.user.status')}>
{#if user.status}
{@const verified = user.emailVerification || user.phoneVerification}
<Pill success={verified}>
{verified
? $_('models.user.verified')
: $_('models.user.unverified')}
? $_.t('models.user.verified')
: $_.t('models.user.unverified')}
</Pill>
{:else}
<Pill danger>{$_('models.user.blocked')}</Pill>
<Pill danger>{$_.t('models.user.blocked')}</Pill>
{/if}
</TableCell>
<TableCell showOverflow title={$_('models.user.id')}>
<TableCell showOverflow title={$_.t('models.user.id')}>
<Copy value={user.$id}>
<Pill button>
<span class="icon-duplicate" aria-hidden="true" />
@@ -94,7 +95,7 @@
</Pill>
</Copy>
</TableCell>
<TableCellText title={$_('models.user.joined')}>
<TableCellText title={$_.t('models.user.joined')}>
{toLocaleDateTime(user.registration)}
</TableCellText>
</TableRowLink>
@@ -9,6 +9,7 @@
Form,
FormList
} from '$lib/elements/forms';
import { _ } from '$lib/i18n';
import { addNotification } from '$lib/stores/notifications';
import { sdkForProject } from '$lib/stores/sdk';
import { createEventDispatcher } from 'svelte';
@@ -52,20 +53,20 @@
<FormList>
<InputText
id="name"
label="Name"
placeholder="Enter name"
label={$_.t('models.user.name')}
placeholder={$_.t('services.authentication.create.users.name')}
autofocus={true}
bind:value={name} />
<InputEmail
id="email"
label="Email"
placeholder="Enter email"
label={$_.t('models.user.email')}
placeholder={$_.t('services.authentication.create.users.email')}
required={true}
bind:value={mail} />
<InputPassword
id="password"
label="Password"
placeholder="Enter password"
label={$_.t('models.user.password')}
placeholder={$_.t('services.authentication.create.users.password')}
required={true}
showPasswordButton={true}
bind:value={pass} />