Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 87 additions & 0 deletions src/lib/i18n.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { derived, writable } from "svelte/store"

const translations = import.meta.glob<{ default: { [key: string]: string } }>(
`../translation/*.json`,
{ eager: true },
)
const localesArray: string[] = []
Object.entries(translations).map(([path]) => {
const from = "/translation/"
const localeFileName = path.slice(
path.indexOf(from) + from.length,
path.lastIndexOf(".json"),
)
localesArray.push(localeFileName)
})

const path = "../translation"
export const defaultLang = "en"
export const currentLang = writable(defaultLang)
export const langs = localesArray

function translate(
currentLang: string,
key: string,
vars: { [key: string]: string },
returnFallback: boolean,
) {
if (!key) throw new Error("no key provided to $t()")

let text = translations[`${path}/${currentLang}.json`].default[key]

if (!currentLang) throw new Error(`no translation for key "${key}"`)

if (!text) {
if (translations[`${path}/${currentLang}.json`].default[key] == undefined) {
if (
translations[`${path}/${defaultLang}.json`].default[key] == undefined
) {
return key
} else if (returnFallback === false) {
return key
} else {
console.warn(
`"${currentLang}.${key}" translation not found. Showing "${defaultLang}.${key}" instead.`,
)
return translations[`${path}/${defaultLang}.json`].default[key]
}
}
}

Object.keys(vars).map((k) => {
const regex = new RegExp(`{{${k}}}`, "g")
text = text.replace(regex, vars[k])
})

return text
}

export const t = derived(
currentLang,
($currentLang) =>
(key: string, vars = {}, lang = $currentLang, returnFallback = true) =>
translate(lang, key, vars, returnFallback),
)

export const setLang = (lang: string | null, replaceQuery = true) => {
if (!lang || !langs.includes(lang)) {
return null
}
currentLang.set(lang)
if (replaceQuery) {
const url = new URL(window.location.toString())
if (lang) {
url.searchParams.set(encodeURIComponent("lang"), encodeURIComponent(lang))
} else {
url.searchParams.delete("lang")
}
history.replaceState({}, "", url)
}
localStorage.setItem("lang", lang)
document.documentElement.setAttribute("lang", lang)
// set direction
document.documentElement.setAttribute(
"dir",
translations[`${path}/${lang}.json`].default["__direction"],
)
}
8 changes: 8 additions & 0 deletions src/lib/translate.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<script lang="ts">
import { t } from "$lib/i18n"
export let k: string
export let v = {}
</script>

<!-- eslint-disable-next-line svelte/no-at-html-tags -->
{@html $t(k, v)}
2 changes: 2 additions & 0 deletions src/routes/(marketing)/+layout.svelte
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<script>
import "../../app.css"
import LangChange from "./lang-change.svelte"
</script>

<div class="navbar bg-base-100 container mx-auto">
Expand All @@ -17,6 +18,7 @@
<li class="md:mx-2"><a href="/blog">Blog</a></li>
<li class="md:mx-2"><a href="/pricing">Pricing</a></li>
<li class="md:mx-2"><a href="/account">Account</a></li>
<li class="md:mx-2"><LangChange /></li>
</ul>
<div class="dropdown dropdown-end sm:hidden">
<!-- svelte-ignore a11y-label-has-associated-control -->
Expand Down
3 changes: 2 additions & 1 deletion src/routes/(marketing)/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<script lang="ts">
import { WebsiteName } from "./../../config"
import T from "$lib/translate.svelte"

const features = [
{
Expand Down Expand Up @@ -229,7 +230,7 @@
href="https://github.com/CriticalMoments/CMSaasStarter/tree/main#saas-starter"
>
<button class="btn btn-outline btn-primary btn-sm px-6 mt-3 mx-2"
>Read the Docs</button
><T k="Read the Docs" /></button
>
</a>
</div>
Expand Down
64 changes: 64 additions & 0 deletions src/routes/(marketing)/lang-change.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<script>
import { currentLang, langs, setLang } from "$lib/i18n"
import { t } from "$lib/i18n"
</script>

<div title="Change Language" class="dropdown dropdown-end">
<div tabindex="0" role="button" class="flex items-center">
<svg
class="h-5 w-5 fill-current"
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
viewBox="0 0 512 512"
>
<path
d="M363,176,246,464h47.24l24.49-58h90.54l24.49,58H480ZM336.31,362,363,279.85,389.69,362Z"
/>
<path
d="M272,320c-.25-.19-20.59-15.77-45.42-42.67,39.58-53.64,62-114.61,71.15-143.33H352V90H214V48H170V90H32v44H251.25c-9.52,26.95-27.05,69.5-53.79,108.36-32.68-43.44-47.14-75.88-47.33-76.22L143,152l-38,22,6.87,13.86c.89,1.56,17.19,37.9,54.71,86.57.92,1.21,1.85,2.39,2.78,3.57-49.72,56.86-89.15,79.09-89.66,79.47L64,368l23,36,19.3-11.47c2.2-1.67,41.33-24,92-80.78,24.52,26.28,43.22,40.83,44.3,41.67L255,362Z"
/>
</svg>

<svg
width="12px"
height="12px"
class="hidden h-2 w-2 fill-current opacity-60 sm:inline-block"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 2048 2048"
>
<path d="M1799 349l242 241-1017 1017L7 590l242-241 775 775 775-775z" />
</svg>
</div>
<div
class="dropdown-content bg-base-200 text-base-content rounded-box top-px mt-16 max-h-[calc(100vh-10rem)] w-56 overflow-y-auto border border-white/5 shadow-2xl outline outline-1 outline-black/5"
>
<ul class="menu menu-sm gap-1">
{#each langs as langItem}
{#if $t("__name", {}, langItem, false) !== "__name"}
<li>
<button
class:active={$currentLang == langItem}
on:click={() => setLang(langItem)}
>
{#if $t("__code", {}, langItem, false) !== "__code"}
<span
class="badge badge-sm badge-outline !pl-1.5 !pr-1 pt-px font-mono !text-[.6rem] font-bold tracking-widest opacity-50"
>
{$t("__code", {}, langItem)}
</span>
{/if}
<span class="font-[sans-serif]">{$t("__name", {}, langItem)}</span
>
{#if $t("__status", {}, langItem) !== "__status" && $t("__status", {}, langItem) !== ""}
<span class="badge badge-sm badge-ghost">
{$t("__status", {}, langItem)}
</span>
{/if}
</button>
</li>
{/if}
{/each}
</ul>
</div>
</div>
10 changes: 10 additions & 0 deletions src/routes/+layout.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,16 @@
import { navigating } from "$app/stores"
import { expoOut } from "svelte/easing"
import { slide } from "svelte/transition"
import { onMount } from "svelte"
import { setLang } from "$lib/i18n"

onMount(() => {
let lang = new URL(document.location.toString()).searchParams.get("lang")
setLang(lang, false)
if (localStorage.getItem("lang")) {
setLang(localStorage.getItem("lang"), false)
}
})
</script>

{#if $navigating}
Expand Down
8 changes: 8 additions & 0 deletions src/translation/af.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"__name": "Afrikaans",
"__code": "AF",
"__direction": "ltr",
"__status": "",
"": "",
"Read the Docs": "Lees die dokumentasie"
}
8 changes: 8 additions & 0 deletions src/translation/en.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"__name": "English",
"__code": "EN",
"__direction": "ltr",
"__status": "",
"": "",
"Read the Docs": "Read the Docs"
}