···11+# sv
22+33+Everything you need to build a Svelte project, powered by [`sv`](https://github.com/sveltejs/cli).
44+55+## Creating a project
66+77+If you're seeing this, you've probably already done this step. Congrats!
88+99+```sh
1010+# create a new project
1111+npx sv create my-app
1212+```
1313+1414+To recreate this project with the same configuration:
1515+1616+```sh
1717+# recreate this project
1818+pnpm dlx sv@0.15.2 create --template minimal --types ts --install pnpm japanese
1919+```
2020+2121+## Developing
2222+2323+Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
2424+2525+```sh
2626+npm run dev
2727+2828+# or start the server and open the app in a new browser tab
2929+npm run dev -- --open
3030+```
3131+3232+## Building
3333+3434+To create a production version of your app:
3535+3636+```sh
3737+npm run build
3838+```
3939+4040+You can preview the production build with `npm run preview`.
4141+4242+> To deploy your app, you may need to install an [adapter](https://svelte.dev/docs/kit/adapters) for your target environment.
···11+// place files you want to import through the `$lib` alias in this folder.
+191
src/lib/kana.ts
···11+export const hiraganaMap = {
22+33+ // Simple
44+ "あ": "a",
55+ "い": "i",
66+ "う": "u",
77+ "え": "e",
88+ "お": "o",
99+1010+ // K
1111+ "か": "ka",
1212+ "き": "ki",
1313+ "く": "ku",
1414+ "け": "ke",
1515+ "こ": "ko",
1616+1717+ // S
1818+ "さ": "sa",
1919+ "し": "shi",
2020+ "す": "su",
2121+ "せ": "se",
2222+ "そ": "so",
2323+2424+ // T
2525+ "た": "ta",
2626+ "ち": "chi",
2727+ "つ": "tsu",
2828+ "て": "te",
2929+ "と": "to",
3030+3131+ // N
3232+ "な": "na",
3333+ "に": "ni",
3434+ "ぬ": "nu",
3535+ "ね": "ne",
3636+ "の": "no",
3737+3838+ // H
3939+ "は": "ha",
4040+ "ひ": "hi",
4141+ "ふ": "fu",
4242+ "へ": "he",
4343+ "ほ": "ho",
4444+4545+4646+ // M
4747+ "ま": "ma",
4848+ "み": "mi",
4949+ "む": "mu",
5050+ "め": "me",
5151+ "も": "mo",
5252+5353+ // Y
5454+ "や": "ya",
5555+ "ゆ": "yu",
5656+ "よ": "yo",
5757+5858+ // R
5959+ "ら": "ra",
6060+ "り": "ri",
6161+ "る": "ru",
6262+ "れ": "re",
6363+ "ろ": "ro",
6464+6565+ // W
6666+ "わ": "wa",
6767+ "を": "wo",
6868+6969+ // N
7070+ "ん": "n",
7171+7272+ // Dakuten: G
7373+ "が": "ga",
7474+ "ぎ": "gi",
7575+ "ぐ": "gu",
7676+ "げ": "ge",
7777+ "ご": "go",
7878+7979+ // Dakuten: Z
8080+ "ざ": "za",
8181+ "じ": "ji",
8282+ "ず": "zu",
8383+ "ぜ": "ze",
8484+ "ぞ": "zo",
8585+8686+ // Dakuten: D
8787+ "だ": "da",
8888+ "ぢ": "ji",
8989+ "づ": "zu",
9090+ "で": "de",
9191+ "ど": "do",
9292+9393+ // Dakuten: B
9494+ "ば": "ba",
9595+ "び": "bi",
9696+ "ぶ": "bu",
9797+ "べ": "be",
9898+ "ぼ": "bo",
9999+100100+ // Handakuten: P
101101+ "ぱ": "pa",
102102+ "ぴ": "pi",
103103+ "ぷ": "pu",
104104+ "ぺ": "pe",
105105+ "ぽ": "po",
106106+};
107107+108108+export const hiragana = Object.keys(hiraganaMap);
109109+export const hiraganaReadings = Object.values(hiraganaMap);
110110+111111+export const hiraganaCombinationMap = {
112112+ // K + small Y
113113+ "きゃ": "kya",
114114+ "きゅ": "kyu",
115115+ "きょ": "kyo",
116116+117117+ // S + small Y
118118+ "しゃ": "sha",
119119+ "しゅ": "shu",
120120+ "しょ": "sho",
121121+122122+ // T + small Y
123123+ "ちゃ": "cha",
124124+ "ちゅ": "chu",
125125+ "ちょ": "cho",
126126+127127+ // N + small Y
128128+ "にゃ": "nya",
129129+ "にゅ": "nyu",
130130+ "にょ": "nyo",
131131+132132+ // H + small Y
133133+ "ひゃ": "hya",
134134+ "ひゅ": "hyu",
135135+ "ひょ": "hyo",
136136+137137+ // M + small Y
138138+ "みゃ": "mya",
139139+ "みゅ": "myu",
140140+ "みょ": "myo",
141141+142142+ // R + small Y
143143+ "りゃ": "rya",
144144+ "りゅ": "ryu",
145145+ "りょ": "ryo",
146146+147147+ // G + small Y
148148+ "ぎゃ": "gya",
149149+ "ぎゅ": "gyu",
150150+ "ぎょ": "gyo",
151151+152152+ // J/Z + small Y
153153+ "じゃ": "ja",
154154+ "じゅ": "ju",
155155+ "じょ": "jo",
156156+157157+ // D + small Y; uncommon, usually romanized like J
158158+ "ぢゃ": "ja",
159159+ "ぢゅ": "ju",
160160+ "ぢょ": "jo",
161161+162162+ // B + small Y
163163+ "びゃ": "bya",
164164+ "びゅ": "byu",
165165+ "びょ": "byo",
166166+167167+ // P + small Y
168168+ "ぴゃ": "pya",
169169+ "ぴゅ": "pyu",
170170+ "ぴょ": "pyo",
171171+};
172172+173173+export const hiraganaCombinations = Object.keys(hiraganaMap);
174174+export const hiraganaCombinationReadings = Object.values(hiraganaCombinationMap);
175175+176176+export function randomHiragana() {
177177+ const entries = Object.entries(hiraganaMap)
178178+ return entries[Math.floor(Math.random() * entries.length)]
179179+}
180180+181181+export function randomHiraganaCombination() {
182182+ const entries = Object.entries(hiraganaCombinationMap)
183183+ return entries[Math.floor(Math.random() * entries.length)]
184184+}
185185+186186+export function getNRandomHiraganaReadingsExcept(n: number, excluded: string) {
187187+ return hiraganaReadings
188188+ .filter(h => h !== excluded)
189189+ .sort(() => Math.random() - 0.5)
190190+ .slice(0, 2);
191191+}
+5
src/routes/+layout.svelte
···11+<script lang="ts">
22+ let { children } = $props();
33+</script>
44+55+{@render children()}
+8
src/routes/+page.svelte
···11+<script>
22+ import TypedAnswerQuestion from "$lib/components/TypedAnswerQuestion.svelte";
33+ import MultipleChoiceQuestion from "$lib/components/MultipleChoiceQuestion.svelte"
44+55+ let questionsAnswered = $state(0)
66+</script>
77+88+<MultipleChoiceQuestion />
+3
static/robots.txt
···11+# allow crawling everything by default
22+User-agent: *
33+Disallow:
+17
svelte.config.js
···11+import adapter from '@sveltejs/adapter-auto';
22+33+/** @type {import('@sveltejs/kit').Config} */
44+const config = {
55+ compilerOptions: {
66+ // Force runes mode for the project, except for libraries. Can be removed in svelte 6.
77+ runes: ({ filename }) => (filename.split(/[/\\]/).includes('node_modules') ? undefined : true)
88+ },
99+ kit: {
1010+ // adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list.
1111+ // If your environment is not supported, or you settled on a specific environment, switch out the adapter.
1212+ // See https://svelte.dev/docs/kit/adapters for more information about adapters.
1313+ adapter: adapter()
1414+ }
1515+};
1616+1717+export default config;
+20
tsconfig.json
···11+{
22+ "extends": "./.svelte-kit/tsconfig.json",
33+ "compilerOptions": {
44+ "rewriteRelativeImportExtensions": true,
55+ "allowJs": true,
66+ "checkJs": true,
77+ "esModuleInterop": true,
88+ "forceConsistentCasingInFileNames": true,
99+ "resolveJsonModule": true,
1010+ "skipLibCheck": true,
1111+ "sourceMap": true,
1212+ "strict": true,
1313+ "moduleResolution": "bundler"
1414+ }
1515+ // Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias
1616+ // except $lib which is handled by https://svelte.dev/docs/kit/configuration#files
1717+ //
1818+ // To make changes to top-level options such as include and exclude, we recommend extending
1919+ // the generated config; see https://svelte.dev/docs/kit/configuration#typescript
2020+}
+6
vite.config.ts
···11+import { sveltekit } from '@sveltejs/kit/vite';
22+import { defineConfig } from 'vite';
33+44+export default defineConfig({
55+ plugins: [sveltekit()]
66+});