Add Locale URL

This commit is contained in:
Restray
2022-04-02 16:15:00 +02:00
parent 741db1778b
commit d910b21185
187 changed files with 683 additions and 532 deletions

View File

@@ -6,6 +6,9 @@ import { getUserDetails, sentry } from '$lib/common';
import { version } from '$lib/common';
import cookie from 'cookie';
import { dev } from '$app/env';
import { locales } from '$lib/translations';
const routeRegex = new RegExp(/^\/[^.]*([?#].*)?$/);
export const handle = handleSession(
{
@@ -62,9 +65,45 @@ export const handle = handleSession(
expires: new Date('Thu, 01 Jan 1970 00:00:01 GMT')
})
);
} finally {
return response;
}
const { url, request } = event;
const { pathname } = url;
// If this request is a route request
if (routeRegex.test(pathname)) {
// Get defined locales
const supportedLocales = locales.get();
// Try to get locale from `pathname`.
let locale = supportedLocales.find(
(l) => `${l}`.toLowerCase() === `${pathname.match(/[^/]+?(?=\/|$)/)}`.toLowerCase()
);
// If route locale is not supported
if (!locale) {
// Get user preferred locale
locale = `${`${request.headers['accept-language']}`.match(
/[a-zA-Z]+?(?=-|_|,|;)/
)}`.toLowerCase();
// Set default locale if user preferred locale does not match
if (!supportedLocales.includes(locale)) locale = 'en';
// 301 redirect
return new Response(undefined, {
headers: { location: `/${locale}${pathname}` },
status: 301
});
}
// Add html `lang` attribute
const body = await response.text();
return new Response(`${body}`.replace(/<html.*>/, `<html lang="${locale}">`), response);
}
return response;
}
);

View File

@@ -1,10 +1,15 @@
import { toast } from '@zerodevx/svelte-toast';
export function errorNotification(message: string) {
import { t } from '$lib/translations';
let formatMessage;
t.subscribe((storeFormat) => (formatMessage = storeFormat));
export function errorNotification(message: string): void {
console.error(message);
if (typeof message !== 'string') {
toast.push('Ooops, something is not okay, are you okay?');
toast.push(formatMessage('error.generic_message'));
} else {
toast.push(message);
toast.push(formatMessage(message));
}
}
export function enhance(

4
src/lib/lang.json Normal file
View File

@@ -0,0 +1,4 @@
{
"en": "English",
"fr": "Français"
}

View File

@@ -1,4 +1,4 @@
export const publicPaths = [
const publicPaths = [
'/login',
'/register',
'/reset',
@@ -8,3 +8,7 @@ export const publicPaths = [
'/webhooks/github/install',
'/webhooks/gitlab'
];
export function isPublicPath(path: string): boolean {
return publicPaths.includes(path);
}

26
src/lib/translations.ts Normal file
View File

@@ -0,0 +1,26 @@
import i18n from 'sveltekit-i18n';
import lang from './lang.json';
/** @type {import('sveltekit-i18n').Config} */
export const config = {
fallbackLocale: 'en',
translations: {
en: { lang },
fr: { lang }
},
loaders: [
{
locale: 'en',
key: '',
loader: async () => (await import('../../static/locales/en.json')).default
},
{
locale: 'fr',
key: '',
loader: async () => (await import('../../static/locales/fr.json')).default
}
]
};
export const { t, loading, locales, locale, loadTranslations } = new i18n(config);
loading.subscribe(($loading) => $loading && console.log('Loading translations...'));

View File

@@ -2,6 +2,7 @@ import { getUserDetails } from '$lib/common';
import * as db from '$lib/database';
import { ErrorHandler } from '$lib/database';
import type { RequestHandler } from '@sveltejs/kit';
import { _ } from 'svelte-i18n';
export const post: RequestHandler = async (event) => {
const { teamId, status, body } = await getUserDetails(event);
@@ -14,8 +15,7 @@ export const post: RequestHandler = async (event) => {
const isDouble = await db.checkDoubleBranch(branch, projectId);
if (isDouble && autodeploy) {
throw {
message:
'Cannot activate automatic deployments until only one application is defined for this repository / branch.'
message: $_('application.app.error_double_app_for_one_branch')
};
}
await db.setApplicationSettings({ id, debug, previews, dualCerts, autodeploy });

View File

@@ -15,6 +15,7 @@
import Docker from '$lib/components/svg/applications/Docker.svelte';
import Astro from '$lib/components/svg/applications/Astro.svelte';
import Eleventy from '$lib/components/svg/applications/Eleventy.svelte';
import { t } from '$lib/translations';
const buildPack = application?.buildPack?.toLowerCase();
</script>
@@ -59,7 +60,7 @@
{/if}
{#if !application.gitSourceId || !application.destinationDockerId}
<div class="truncate text-center font-bold text-red-500 group-hover:text-white">
Configuration missing
{$t('application.configuration_missing')}
</div>
{/if}
</div>

View File

@@ -4,6 +4,7 @@
import Application from './_Application.svelte';
import { post } from '$lib/api';
import { goto } from '$app/navigation';
import { t } from '$lib/translations';
async function newApplication() {
const { id } = await post('/applications/new', {});
return await goto(`/applications/${id}`, { replaceState: true });
@@ -11,7 +12,7 @@
</script>
<div class="flex space-x-1 p-6 font-bold">
<div class="mr-4 text-2xl ">Applications</div>
<div class="mr-4 text-2xl ">{$t('index.applications')}</div>
{#if $session.isAdmin}
<div on:click={newApplication} class="add-icon cursor-pointer bg-green-600 hover:bg-green-500">
<svg
@@ -33,7 +34,7 @@
<div class="flex flex-wrap justify-center">
{#if !applications || applications.length === 0}
<div class="flex-col">
<div class="text-center text-xl font-bold">No applications found</div>
<div class="text-center text-xl font-bold">{$t('application.no_applications_found')}</div>
</div>
{:else}
{#each applications as application}

View File

@@ -5,6 +5,8 @@
import { post } from '$lib/api';
import { errorNotification } from '$lib/form';
import { onMount } from 'svelte';
import { t } from '$lib/translations';
let loading = false;
let emailEl;
let email, password;
@@ -39,7 +41,7 @@
<div class="flex h-screen flex-col items-center justify-center">
{#if $session.userId}
<div class="flex justify-center px-4 text-xl font-bold">Already logged in...</div>
<div class="flex justify-center px-4 text-xl font-bold">{$t('login.already_logged_in')}</div>
{:else}
<div class="flex justify-center px-4">
<form on:submit|preventDefault={handleSubmit} class="flex flex-col py-4 space-y-2">
@@ -48,7 +50,7 @@
<input
type="email"
name="email"
placeholder="Email"
placeholder={$t('forms.email')}
autocomplete="off"
required
bind:this={emailEl}
@@ -57,7 +59,7 @@
<input
type="password"
name="password"
placeholder="Password"
placeholder={$t('forms.password')}
bind:value={password}
required
/>
@@ -69,16 +71,19 @@
class="hover:opacity-90 text-white"
class:bg-transparent={loading}
class:text-stone-600={loading}
class:bg-coollabs={!loading}>{loading ? 'Authenticating...' : 'Login'}</button
class:bg-coollabs={!loading}
>{loading ? $t('login.authenticating') : $t('login.login')}</button
>
<button
on:click|preventDefault={() => goto('/register')}
class="bg-transparent hover:bg-coolgray-300 text-white ">Register</button
class="bg-transparent hover:bg-coolgray-300 text-white "
>{$t('register.register')}</button
>
<button
class="bg-transparent hover:bg-coolgray-300"
on:click|preventDefault={() => goto('/reset')}>Reset password</button
on:click|preventDefault={() => goto('/reset')}
>{$t('reset_password.reset_password')}</button
>
</div>
</form>

View File

@@ -7,6 +7,7 @@
import { post } from '$lib/api';
import { errorNotification } from '$lib/form';
import { onMount } from 'svelte';
import { t } from '$lib/translations';
let loading = false;
let emailEl;
@@ -20,7 +21,7 @@
});
async function handleSubmit() {
if (password !== passwordCheck) {
return errorNotification('Passwords do not match.');
return errorNotification($t('register.passwords_do_not_match'));
}
loading = true;
try {
@@ -57,7 +58,7 @@
</div>
<div class="flex h-screen flex-col items-center justify-center">
{#if $session.userId}
<div class="flex justify-center px-4 text-xl font-bold">Already logged in...</div>
<div class="flex justify-center px-4 text-xl font-bold">{$t('login.already_logged_in')}</div>
{:else}
<div class="flex justify-center px-4">
<form on:submit|preventDefault={handleSubmit} class="flex flex-col py-4 space-y-2">
@@ -66,7 +67,7 @@
<input
type="email"
name="email"
placeholder="Email"
placeholder={$t('forms.email')}
autocomplete="off"
required
bind:this={emailEl}
@@ -75,28 +76,28 @@
<input
type="password"
name="password"
placeholder="Password"
placeholder={$t('forms.password')}
bind:value={password}
required
/>
<input
type="password"
name="passwordCheck"
placeholder="Password again"
placeholder={$t('forms.password_again')}
bind:value={passwordCheck}
required
/>
<div class="flex space-x-2 h-8 items-center justify-center pt-8">
<button type="submit" class="hover:bg-coollabs-100 text-white bg-coollabs"
>Register</button
>{$t('register.register')}</button
>
</div>
</form>
</div>
{#if userCount === 0}
<div class="pt-5">
You are registering the first user. It will be the administrator of your Coolify instance.
{$t('register.register_first_user')}
</div>
{/if}
{/if}

View File

@@ -16,7 +16,7 @@ export const post: RequestHandler = async (event) => {
return {
status: 500,
body: {
error: 'Invalid secret key.'
error: 'reset_password.invalid_secret_key'
}
};
}

Some files were not shown because too many files have changed in this diff Show More