From 2e7f198a8108c53c6162be0d156edfce85195aa3 Mon Sep 17 00:00:00 2001 From: Joseph Eiba Date: Tue, 30 Sep 2025 15:26:40 +0100 Subject: feat: Add initial language support for English and Japanese - Add translation framework for UI elements - Implement language switching functionality - Add English and Japanese translations for UI components - Card names and effects translation to be implemented in future commits --- src/components/left_panel.svelte | 5 +- src/components/loading.svelte | 3 +- src/components/main_panel.svelte | 41 +++++++----- src/components/right_panel.svelte | 7 ++- src/language.js | 31 ++++++++++ src/loading.js | 2 + src/translations.js | 127 ++++++++++++++++++++++++++++++++++++++ 7 files changed, 193 insertions(+), 23 deletions(-) create mode 100644 src/language.js create mode 100644 src/translations.js diff --git a/src/components/left_panel.svelte b/src/components/left_panel.svelte index a2c14c5..bcfdba7 100644 --- a/src/components/left_panel.svelte +++ b/src/components/left_panel.svelte @@ -9,6 +9,7 @@ } from '../left_panel'; import { cardImageUrl } from '../utils'; + import { currentTranslations } from '../language'; @@ -24,8 +25,8 @@
{$leftPanelCardDesc}

- 源代码 -
关注 Mistivia 谢谢喵~ + {$currentTranslations.sourceCode} +
{$currentTranslations.followMistivia} Mistivia {$currentTranslations.thankYou}

diff --git a/src/components/loading.svelte b/src/components/loading.svelte index fb49495..42b3404 100644 --- a/src/components/loading.svelte +++ b/src/components/loading.svelte @@ -1,5 +1,6 @@ {#if $isLoading} @@ -7,7 +8,7 @@

-

加载中...

+

{$currentTranslations.loading}

{/if} diff --git a/src/components/main_panel.svelte b/src/components/main_panel.svelte index 0d959c2..9029a16 100644 --- a/src/components/main_panel.svelte +++ b/src/components/main_panel.svelte @@ -8,6 +8,7 @@ genYdke, downloadStringAsFile, } from '../utils'; + import { language, setLanguage, currentTranslations } from '../language'; let fileInput; @@ -44,10 +45,10 @@ let deckString = genYdk($deck); navigator.clipboard.writeText(deckString) .then(() => { - alert('YDK卡组码已复制到剪贴板'); + alert($currentTranslations.ydkCopied); }) .catch(err => { - alert("失败!"); + alert($currentTranslations.failed); }); } @@ -57,10 +58,10 @@ url = url + '#' + genYdke($deck); navigator.clipboard.writeText(url) .then(() => { - alert('分享链接已复制到剪贴板'); + alert($currentTranslations.shareLinkCopied); }) .catch(err => { - alert("失败!"); + alert($currentTranslations.failed); }); } @@ -91,27 +92,32 @@
- - - - - + + + + + + {#if $format === 'genesys'} - 点数:{$deck.point} + {$currentTranslations.points}{$deck.point} {/if}
-

主卡组({$deck.main.length})

+

{$currentTranslations.mainDeck}({$deck.main.length})

e.preventDefault()} ondrop={(e)=>onDrop("main", e, -1)} class="card-grid main-deck"> {#each $deck.main as card, i}
e.preventDefault()} ondrop={(e)=>onDrop("main", e, i)}> @@ -121,7 +127,7 @@
-

额外卡组({$deck.extra.length})

+

{$currentTranslations.extraDeck}({$deck.extra.length})

e.preventDefault()} ondrop={(e)=>onDrop("extra", e, -1)} class="card-grid extra-deck"> {#each $deck.extra as card, i}
e.preventDefault()} ondrop={(e)=>onDrop("extra", e, i)}> @@ -131,7 +137,7 @@
-

副卡组({$deck.side.length})

+

{$currentTranslations.sideDeck}({$deck.side.length})

e.preventDefault()} ondrop={(e)=>onDrop("side", e, -1)} class="card-grid side-deck"> {#each $deck.side as card, i}
e.preventDefault()} ondrop={(e)=>onDrop("side", e, i)}> @@ -172,7 +178,8 @@ } - .select-format { + .select-format, + .select-language { padding: 8px 8px; margin-right: 10px; cursor: pointer; diff --git a/src/components/right_panel.svelte b/src/components/right_panel.svelte index b0b8553..28f4307 100644 --- a/src/components/right_panel.svelte +++ b/src/components/right_panel.svelte @@ -3,6 +3,7 @@ import { changeInput, showingCards, onPrevPage, onNextPage } from '../search' import { deckOps, format } from '../deck'; import { cardLimit, cornerMark } from '../card_db'; + import { currentTranslations } from '../language'; function onChange(event) { changeInput(event.target.value); @@ -19,7 +20,7 @@
e.preventDefault()} ondrop={onDrop}>
{#each $showingCards as card} @@ -32,8 +33,8 @@ {/each}
diff --git a/src/language.js b/src/language.js new file mode 100644 index 0000000..b4b9605 --- /dev/null +++ b/src/language.js @@ -0,0 +1,31 @@ +import { writable, derived } from "svelte/store"; +import translations from './translations.js'; + +let defaultLanguage = 'chinese'; +let language = writable(defaultLanguage); +let languageState = defaultLanguage; + +function setLanguage(newLanguage) { + localStorage.setItem('language', newLanguage); + languageState = newLanguage; + language.set(newLanguage); +} + +function initLanguage() { + let cachedLanguage = localStorage.getItem('language'); + if (cachedLanguage !== null) { + setLanguage(cachedLanguage); + } +} + +const currentTranslations = derived(language, ($language) => { + return translations[$language] || translations.chinese; +}); + +export { + language, + languageState, + setLanguage, + initLanguage, + currentTranslations, +}; \ No newline at end of file diff --git a/src/loading.js b/src/loading.js index 523f26c..94fb08c 100644 --- a/src/loading.js +++ b/src/loading.js @@ -1,6 +1,7 @@ import { writable } from 'svelte/store'; import { initSearch } from './search'; import { initDeck } from './deck'; +import { initLanguage } from './language'; import { setCardDb, setAltId } from './card_db'; import idChangelog from './id_changelog.json'; @@ -53,6 +54,7 @@ async function setidxdbitem(key, value) { } async function fetchCardDb() { + initLanguage(); // Load language before showing loading screen let localVer = localStorage.getItem('card_db_ver'); try { // load card db diff --git a/src/translations.js b/src/translations.js new file mode 100644 index 0000000..af430cb --- /dev/null +++ b/src/translations.js @@ -0,0 +1,127 @@ +const translations = { + chinese: { + // Buttons + open: "打开", + save: "保存", + clear: "清空", + copyToClipboard: "复制到剪贴板", + share: "分享", + + // Format options + noLimit: "无禁限", + masterDuel: "大师决斗", + cnSimplified: "简中", + + // Deck sections + mainDeck: "主卡组", + extraDeck: "额外卡组", + sideDeck: "副卡组", + + // Search + searchPlaceholder: "搜索卡牌...", + + // Pagination + prevPage: "上一页", + nextPage: "下一页", + + // Points display + points: "点数:", + + // Footer links + sourceCode: "源代码", + followMistivia: "关注", + thankYou: "谢谢喵~", + + // Alert messages + ydkCopied: "YDK卡组码已复制到剪贴板", + shareLinkCopied: "分享链接已复制到剪贴板", + failed: "失败!", + + // Loading + loading: "加载中..." + }, + english: { + // Buttons + open: "Open", + save: "Save", + clear: "Clear", + copyToClipboard: "Copy to Clipboard", + share: "Share", + + // Format options + noLimit: "No Limit", + masterDuel: "Master Duel", + cnSimplified: "Chinese OCG", + + // Deck sections + mainDeck: "Main Deck", + extraDeck: "Extra Deck", + sideDeck: "Side Deck", + + // Search + searchPlaceholder: "Search cards...", + + // Pagination + prevPage: "Previous", + nextPage: "Next", + + // Points display + points: "Points: ", + + // Footer links + sourceCode: "Source Code", + followMistivia: "Follow", + thankYou: "Thank you~", + + // Alert messages + ydkCopied: "YDK deck code copied to clipboard", + shareLinkCopied: "Share link copied to clipboard", + failed: "Failed!", + + // Loading + loading: "Loading..." + }, + japanese: { + // Buttons + open: "開く", + save: "保存", + clear: "クリア", + copyToClipboard: "クリップボードにコピー", + share: "シェア", + + // Format options + noLimit: "制限なし", + masterDuel: "マスターデュエル", + cnSimplified: "簡体字中国語", + + // Deck sections + mainDeck: "メインデッキ", + extraDeck: "エクストラデッキ", + sideDeck: "サイドデッキ", + + // Search + searchPlaceholder: "カードを検索...", + + // Pagination + prevPage: "前のページ", + nextPage: "次のページ", + + // Points display + points: "ポイント:", + + // Footer links + sourceCode: "ソースコード", + followMistivia: "フォロー", + thankYou: "ありがとうにゃ〜", + + // Alert messages + ydkCopied: "YDKデッキコードをクリップボードにコピーしました", + shareLinkCopied: "シェアリンクをクリップボードにコピーしました", + failed: "失敗!", + + // Loading + loading: "読み込み中..." + } +}; + +export default translations; \ No newline at end of file -- cgit v1.0