Skip to content

Commit 126bc13

Browse files
committed
feat: i18n improvements and auth restrictions
1 parent 2cb6b41 commit 126bc13

30 files changed

Lines changed: 299 additions & 265 deletions

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,7 @@ bun.lock
2727
pnpm-lock.yaml
2828

2929
# wrangler
30-
.wrangler/
30+
.wrangler/
31+
32+
# pnpm workspace
33+
pnpm-workspace.yaml

public/404.png

1.98 MB
Loading

src/components/editor/CodeEditor.astro

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,10 @@ const { slug, initialCode = 'fn main() {\n println!("Hola, mundo!");\n}\n' }
2424
import { basicSetup } from "codemirror";
2525
import { indentWithTab } from "@codemirror/commands";
2626
import { gruvbox } from "~/components/editor/theme";
27-
import { db, editorCode } from "~/lib/stores/editor-store";
27+
import { editorCode } from "~/lib/stores/editor-store";
2828
import { rust } from "~/components/editor/config";
2929

3030
let editorView: EditorView | null = null;
31-
let saveTimeout: number | undefined;
3231

3332
async function initEditor() {
3433
const editorElement = document.getElementById("editor");
@@ -44,7 +43,7 @@ const { slug, initialCode = 'fn main() {\n println!("Hola, mundo!");\n}\n' }
4443
initialCode: string;
4544
};
4645

47-
const initialDoc = (await db.getCode(slug)) ?? initialCode;
46+
const initialDoc = initialCode;
4847

4948
editorView = new EditorView({
5049
state: EditorState.create({
@@ -58,11 +57,6 @@ const { slug, initialCode = 'fn main() {\n println!("Hola, mundo!");\n}\n' }
5857
if (update.docChanged) {
5958
const value = update.state.doc.toString();
6059
editorCode.set(value);
61-
clearTimeout(saveTimeout);
62-
saveTimeout = window.setTimeout(
63-
() => db.saveCode(slug, value),
64-
500,
65-
);
6660
}
6761
}),
6862
],
@@ -89,7 +83,6 @@ const { slug, initialCode = 'fn main() {\n println!("Hola, mundo!");\n}\n' }
8983
document.addEventListener("astro:page-load", initEditor);
9084

9185
document.addEventListener("astro:before-swap", () => {
92-
clearTimeout(saveTimeout);
9386
window.removeEventListener("editor:reset", handleReset);
9487

9588
if (editorView) {

src/components/editor/output/Terminal.astro

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
---
2+
import { getLangFromUrl, useTranslations } from "~/i18n/utils"
23
import IconLoader2 from "~icons/tabler/loader-2"
34
import IconPlayerPlay from "~icons/tabler/player-play"
45
import IconRestore from "~icons/tabler/restore"
@@ -11,6 +12,8 @@ interface Props {
1112
1213
const { expectedOutput, testCode } = Astro.props
1314
const isTestMode = !!testCode
15+
const lang = getLangFromUrl(Astro.url)
16+
const t = useTranslations(lang)
1417
---
1518

1619
<terminal-output
@@ -41,22 +44,22 @@ const isTestMode = !!testCode
4144
id="btn-reset"
4245
class="btn-control hover:text-yellow"
4346
type="button"
44-
aria-label="Reset code"
47+
aria-label={t("editor.reset")}
4548
>
4649
<IconRestore font-size={14} />
47-
<span>Reiniciar</span>
50+
<span>{t("editor.reset")}</span>
4851
</button>
4952

5053
<button
5154
id="btn-run"
5255
class="btn-control hover:text-green-500"
5356
type="button"
54-
aria-label="Run code"
57+
aria-label={t("editor.run")}
5558
>
5659
<IconPlayerPlay class="group-data-running:hidden" font-size={14} />
5760
<IconLoader2 class="hidden animate-spin group-data-running:block" font-size={14} />
58-
<span class="group-data-running:hidden">Ejecutar</span>
59-
<span class="hidden group-data-running:inline">Ejecutando...</span>
61+
<span class="group-data-running:hidden">{t("editor.run")}</span>
62+
<span class="hidden group-data-running:inline">{t("editor.running")}</span>
6063
</button>
6164
</div>
6265
</header>
@@ -78,22 +81,22 @@ const isTestMode = !!testCode
7881
<button
7982
id="btn-reset-mobile"
8083
type="button"
81-
aria-label="Reset code"
84+
aria-label={t("editor.reset")}
8285
class="flex-1 flex items-center justify-center gap-2 py-3 text-sm text-secondary/60 border-r border-stroke-color hover:bg-stroke-color/20 active:bg-stroke-color/40 transition-colors cursor-pointer touch-manipulation"
8386
>
8487
<IconRestore font-size={18} />
85-
<span>Reiniciar</span>
88+
<span>{t("editor.reset")}</span>
8689
</button>
8790
<button
8891
id="btn-run-mobile"
8992
type="button"
90-
aria-label="Run code"
93+
aria-label={t("editor.run")}
9194
class="flex-1 flex items-center justify-center gap-2 py-3 text-sm text-green-400 hover:bg-green-500/10 active:bg-green-500/20 transition-colors cursor-pointer touch-manipulation"
9295
>
9396
<IconPlayerPlay class="group-data-running:hidden" font-size={18} />
9497
<IconLoader2 class="hidden animate-spin group-data-running:block" font-size={18} />
95-
<span class="group-data-running:hidden">Ejecutar</span>
96-
<span class="hidden group-data-running:inline">Ejecutando...</span>
98+
<span class="group-data-running:hidden">{t("editor.run")}</span>
99+
<span class="hidden group-data-running:inline">{t("editor.running")}</span>
97100
</button>
98101
</div>
99102
<div class="hidden items-center gap-2 border-t border-stroke-color px-3 py-2 group-data-running:flex">

src/const/tracks.ts

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,31 @@
1-
import type { Track } from "~/entities/track"
1+
import type { Track, TrackI18n } from "~/entities/track"
2+
3+
export const tracksI18n: Record<string, TrackI18n> = {
4+
"fundamentos-de-rust": {
5+
es: {
6+
title: "Fundamentos de Rust",
7+
description:
8+
"Aprende Rust desde los fundamentos: sintaxis, ownership, borrowing y más. Ideal si nunca has programado en Rust.",
9+
},
10+
en: {
11+
title: "Rust Fundamentals",
12+
description:
13+
"Learn Rust from the ground up: syntax, ownership, borrowing and more. Ideal if you've never programmed in Rust.",
14+
},
15+
},
16+
"rust-avanzado-diseno-abstraccion": {
17+
es: {
18+
title: "Rust Avanzado: Diseño y Abstracción",
19+
description:
20+
"Profundiza en Rust con temas avanzados como lifetimes, traits, macros y programación asíncrona. Ideal para quienes ya conocen los fundamentos.",
21+
},
22+
en: {
23+
title: "Advanced Rust: Design and Abstraction",
24+
description:
25+
"Deep dive into Rust with advanced topics like lifetimes, traits, macros and async programming. Ideal for those who already know the fundamentals.",
26+
},
27+
},
28+
}
229

330
export const tracks: Track[] = [
431
{

src/entities/track.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
11
export type TrackBadgeKey = "track.badge.beginner" | "track.badge.advanced"
22

3+
export type TrackI18n = {
4+
es: {
5+
title: string
6+
description: string
7+
}
8+
en: {
9+
title: string
10+
description: string
11+
}
12+
}
13+
314
export type Track = {
415
id: string
516
title: string

src/features/content/components/NavButtons.astro

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
---
2+
import { getLangFromUrl, useTranslations } from "~/i18n/utils"
23
import IconLeft from "~icons/tabler/arrow-big-left-lines-filled"
34
import IconRight from "~icons/tabler/arrow-big-right-lines-filled"
45
@@ -9,6 +10,7 @@ interface Props {
910
}
1011
1112
const { lang, previous, next } = Astro.props
13+
const t = useTranslations(lang as "es" | "en")
1214
const isNextDisabled = true
1315
1416
const btnBaseClass =
@@ -21,7 +23,7 @@ const iconBaseClass = "block h-6 w-6 rounded-lg transition-all duration-200 tran
2123
<nav class="not-prose mt-auto flex w-full items-center justify-between pb-2" aria-label="Navegación de lecciones">
2224
<a
2325
href={previous ? `/${lang}/${previous.slug}` : undefined}
24-
aria-label={previous ? `Anterior: ${previous.title}` : undefined}
26+
aria-label={previous ? `${t("nav.previous")}: ${previous.title}` : undefined}
2527
aria-disabled={!previous}
2628
class={btnBaseClass}
2729
>
@@ -30,21 +32,21 @@ const iconBaseClass = "block h-6 w-6 rounded-lg transition-all duration-200 tran
3032
</span>
3133
<div class="flex flex-col text-left">
3234
<span class:list={["text-md font-medium", previous ? "text-yellow" : "text-[#ccc]"]}>
33-
Anterior
35+
{t("nav.previous")}
3436
</span>
3537
<span class="text-xs text-[#ccc]">{previous?.title || "-"}</span>
3638
</div>
3739
</a>
3840
<a
3941
id="next-lesson-btn"
4042
href={next ? `/${lang}/${next.slug}` : undefined}
41-
aria-label={next ? `Siguiente: ${next.title}` : undefined}
43+
aria-label={next ? `${t("nav.next")}: ${next.title}` : undefined}
4244
aria-disabled={isNextDisabled}
4345
class={nextBtnClass}
4446
>
4547
<div class="flex flex-col text-right">
4648
<span class:list={["text-md font-medium", next ? "text-yellow" : "text-[#ccc]"]}>
47-
Siguiente
49+
{t("nav.next")}
4850
</span>
4951
<span class="text-xs text-[#ccc]">{next?.title || "-"}</span>
5052
</div>
@@ -57,6 +59,7 @@ const iconBaseClass = "block h-6 w-6 rounded-lg transition-all duration-200 tran
5759
<script>
5860
import { db } from "~/lib/stores/editor-store"
5961
import { toast } from "~/utils/toasts"
62+
import { useTranslations, getLangFromUrl } from "~/i18n/utils"
6063

6164
const initNextButton = async () => {
6265
const btn = document.getElementById("next-lesson-btn") as HTMLAnchorElement | null
@@ -65,9 +68,11 @@ const iconBaseClass = "block h-6 w-6 rounded-lg transition-all duration-200 tran
6568
btn.addEventListener("click", (e) => {
6669
if (btn.ariaDisabled === "true") {
6770
e.preventDefault()
71+
const currentLang = getLangFromUrl(new URL(window.location.href))
72+
const t = useTranslations(currentLang)
6873
toast.info(
69-
"Ejecuta el código",
70-
"El código debe ser ejecutado al menos una vez para poder avanzar a la siguiente sección",
74+
t("toast.run_required_title"),
75+
t("toast.run_required_desc"),
7176
)
7277
}
7378
})

src/features/content/components/Navbar.astro

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,8 +127,8 @@ const sidebarItems = [...groupMap.values()].map(({ parent, children }) => ({
127127
// Setup reactive navbar (this handles all user state changes automatically)
128128
setupNavbar({
129129
profileLinkId: "nav-profile-link",
130-
loginLinkId: null, // This navbar doesn't have a login link
131-
logoutLinkId: null // This navbar doesn't have a logout link directly
130+
loginLinkId: undefined,
131+
logoutLinkId: undefined
132132
})
133133

134134
// Update username in profile link dynamically by targeting the span

src/features/home/components/Community.astro

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ interface Props {
1313
}
1414
1515
const { lang: langProp } = Astro.props
16-
const lang = langProp ?? getLangFromUrl(Astro.url)
16+
const lang = (langProp ?? getLangFromUrl(Astro.url)) as "es" | "en"
1717
const t = useTranslations(lang)
1818
---
1919

src/features/home/components/FAQ.astro

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ interface Props {
88
}
99
1010
const { lang: langProp } = Astro.props
11-
const lang = langProp ?? getLangFromUrl(Astro.url)
11+
const lang = (langProp ?? getLangFromUrl(Astro.url)) as "es" | "en"
1212
const t = useTranslations(lang)
1313
1414
const faqs = [

0 commit comments

Comments
 (0)