Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support lang switch #98

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions src/lib/components/header.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import { afterUpdate, onMount } from 'svelte';
import { query, result, searching } from '$lib/search/stores';
import { LL } from '$i18n/i18n-svelte';
import LocaleSwitcher from './locale_switcher.svelte'

function resetHome() {
tagsCur.init();
Expand Down Expand Up @@ -123,6 +124,7 @@
}} />

<header id="header" class="fixed w-screen ease-in-out z-40" aria-label="Header Nav">

{#if !$searching}
<nav
id="header-nav"
Expand Down Expand Up @@ -173,7 +175,7 @@
in:fly|global={{ x: -50, duration: 300, delay: 300 }}
out:fly|global={{ x: -50, duration: 300 }}>
<div class="lg:hidden rounded-lg btn btn-ghost !p0">
<Dropdown nav={mobilenavConfig} class="text-sm p2 ">
<Dropdown nav={mobilenavConfig.hasOwnProperty(siteConfig.lang) ? mobilenavConfig[siteConfig.lang] : mobilenavConfig['en']} class="text-sm p2 ">
<button aria-label="nav menu" class="flex items-center">
<div class="i-mdi-hamburger-menu !w-[1.5rem] !h-[1.5rem]" />
</button>
Expand All @@ -185,13 +187,13 @@
</a>

<div class="hidden lg:(flex)">
{#each navConfig as n}
{#each (navConfig.hasOwnProperty(siteConfig.lang) ? navConfig[siteConfig.lang] : navConfig['en']) as n}
<Dropdown class="text-lg px3 py2 btn btn-ghost " nav={n} />
{/each}
</div>

<div class="ml-auto flex">
{#if $page.route?.id && $page.route.id === '/'}
{#if $page.route?.id && ['/', '/[lang=lang]'].includes($page.route.id)}
{#key $page}
<button
id="search"
Expand All @@ -212,7 +214,7 @@
</button>
{/key}
{/if}
{#if $page.route?.id && $page.route.id === '/'}
{#if $page.route?.id && ['/', '/[lang=lang]'].includes($page.route.id)}
<button
in:fade|global={{ duration: 300, delay: 300 }}
out:fade|global={{ duration: 300 }}
Expand Down Expand Up @@ -249,6 +251,7 @@
class="!w8 !h8 i-line-md-sunny-outline-loop dark:i-line-md-moon group-hover:(transition-transform duration-300 scale-120 ease-in-out)" />
</button>
{/key}
<LocaleSwitcher />
</div>
</div>
{/if}
Expand Down Expand Up @@ -323,6 +326,7 @@
</svg>
</div>
</button>

{/if}

{#if !scrollingUp && scrollPercent > topPercent && scrollPercent < botPercent}
Expand Down Expand Up @@ -354,6 +358,7 @@
</svg>
</div>
</button>

{/if}

<style>
Expand Down
57 changes: 57 additions & 0 deletions src/lib/components/locale_switcher.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<script lang="ts">
import { browser } from '$app/environment'
import { invalidateAll } from '$app/navigation'
import { page } from '$app/stores'
import { setLocale, locale } from '$i18n/i18n-svelte'
import type { Locales } from '$i18n/i18n-types'
import { locales } from '$i18n/i18n-util'
import { loadLocaleAsync } from '$i18n/i18n-util.async'
import { replaceLocaleInUrl } from './utils.js'

const switchLocale = async (newLocale: Locales, updateHistoryState = true) => {
if (!newLocale || $locale === newLocale) return

// load new dictionary from server
await loadLocaleAsync(newLocale)

// select locale
setLocale(newLocale)

if (updateHistoryState) {
// update url to reflect locale changes
history.pushState({ locale: newLocale }, '', replaceLocaleInUrl($page.url, newLocale))
}

// run the `load` function again
invalidateAll()
}

// update `lang` attribute
$: browser && document.querySelector('html')!.setAttribute('lang', $locale)

// update locale when navigating via browser back/forward buttons
const handlePopStateEvent = async ({ state }: PopStateEvent) => switchLocale(state.locale, false)

// update locale when page store changes
$: if (browser) {
const lang = $page.params.lang as Locales
switchLocale(lang, false)
history.replaceState({ ...history.state, locale: lang }, '', replaceLocaleInUrl($page.url, lang))
}
</script>

<select onChange="window.location.assign(this.value);" style="background-color: var(--qwer-bg-color);">
{#each locales as l}
{#if l === $locale}
<option value={l} selected>
{l}
</option>
{:else}
<option value={l}>
{l}
</option>
{/if}
{/each}
</select>

<svelte:window on:popstate={handlePopStateEvent} />
26 changes: 26 additions & 0 deletions src/lib/components/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Replaces the locale slug in a URL.
//
// If the `full` argument is set to `true`, the full URL is returned as a string.
// e.g. https://mywebsite.com/en/blog/article-1 => https://mywebsite.com/de/blog/article-1
//
// Otherwise (default) the URL relative to the base is returned.

import { base } from '$app/paths'

// e.g. https://mywebsite.com/en/blog/article-1 => /de/blog/article-1
export const replaceLocaleInUrl = (url: URL, locale: string, full = false): string => {
const [, , ...rest] = getPathnameWithoutBase(url).split('/')
const new_pathname = `/${[locale, ...rest].join('/')}`
if (!full) {
return `${new_pathname}${url.search}`
}
const newUrl = new URL(url.toString())
newUrl.pathname = base + new_pathname
return newUrl.toString()
}

// ----------------------------------------------------------------------------

const REGEX_START_WITH_BASE = new RegExp(`^${base}`)

export const getPathnameWithoutBase = (url: URL) => url.pathname.replace(REGEX_START_WITH_BASE, '')
7 changes: 7 additions & 0 deletions src/params/lang.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import type { ParamMatcher } from '@sveltejs/kit'
import { isLocale } from '$i18n/i18n-util'

// only accept valid languages as a segment in the URL
export const match: ParamMatcher = (param) => {
return isLocale(param)
}
13 changes: 12 additions & 1 deletion src/routes/+layout.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,18 @@
import { siteConfig } from '$config/site';
import { locales, baseLocale } from '$i18n/i18n-util';

if (locales.includes(siteConfig.lang)) {
import type { Locales } from '$i18n/i18n-types.js'

import { page } from '$app/stores';
let lang = $page.url.pathname.split('/')[1];
let specifyLang: Locales = lang as Locales;

if (locales.includes(specifyLang)) {
loadLocale(specifyLang);
setLocale(specifyLang);
siteConfig.lang = specifyLang;
}
else if (locales.includes(siteConfig.lang)) {
loadLocale(siteConfig.lang);
setLocale(siteConfig.lang);
} else {
Expand Down
5 changes: 5 additions & 0 deletions src/routes/[lang=lang]/+page.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<script lang="ts">
import Home from '../+page.svelte';
</script>

<Home />
8 changes: 8 additions & 0 deletions svelte.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@ const config = {
$static: './static',
$i18n: './src/i18n',
},
prerender: {
entries: ['/en', '/ja', '/zh', '/zh-Hans', '/zh-Hant',
'/example',
'/hello-QWER',
'/quick-start',
'/versions.json'
]
}
},
};

Expand Down
92 changes: 73 additions & 19 deletions user/config/site.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,30 +106,31 @@ export const giscusConfig: Giscus.Config = {
'data-strict': '0',
};

export const navConfig: (DD.Nav | DD.Link)[] = [
{
name: 'About',
url: '/about',
},
{
name: 'See Docs 📄',
url: 'https://docs-svelte-qwer.vercel.app/',
rel: 'external',
},
{
name: 'Get QWER 🚀',
url: 'https://github.com/kwchang0831/svelte-QWER',
rel: 'external',
},
];
type NavConfigType = {
[key: string]: (DD.Nav | DD.Link)[];
};

export const mobilenavConfig: DD.Nav = {
orientation: 2,
links: [
// export const navConfig: (DD.Nav | DD.Link)[] =
export const navConfig: NavConfigType =
{
en: [
{
name: 'About',
url: '/about',
},
{
name: 'Menu',
links: [
{
name: 'first',
url: '/first-page'
},
{
name: 'test',
url: '/test-page'
}
]
},
{
name: 'See Docs 📄',
url: 'https://docs-svelte-qwer.vercel.app/',
Expand All @@ -141,4 +142,57 @@ export const mobilenavConfig: DD.Nav = {
rel: 'external',
},
],
zh: [
{
name: '關於',
url: '/about',
},
{
name: '查看文件 📄',
url: 'https://docs-svelte-qwer.vercel.app/',
rel: 'external',
}
]
}
;

type MobileNavConfigType = {
[key: string]: DD.Nav;
};

// export const mobilenavConfig: DD.Nav = {
export const mobilenavConfig: MobileNavConfigType = {
en: {
orientation: 2,
links: [
{
name: 'About',
url: '/about',
},
{
name: 'See Docs 📄',
url: 'https://docs-svelte-qwer.vercel.app/',
rel: 'external',
},
{
name: 'Get QWER 🚀',
url: 'https://github.com/kwchang0831/svelte-QWER',
rel: 'external',
},
],
},
zh: {
orientation: 2,
links: [
{
name: '關於',
url: '/about',
},
{
name: '查看文件 📄',
url: 'https://docs-svelte-qwer.vercel.app/',
rel: 'external',
}
],
}
};