Skip to content

Commit 4de1525

Browse files
committed
add russian translation
1 parent 961c043 commit 4de1525

24 files changed

Lines changed: 2912 additions & 927 deletions

docs/index.html

Lines changed: 389 additions & 125 deletions
Large diffs are not rendered by default.

package-lock.json

Lines changed: 709 additions & 23 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818
"@tauri-apps/plugin-opener": "^2",
1919
"@tauri-apps/plugin-process": "^2",
2020
"@tauri-apps/plugin-updater": "^2",
21-
"chart.js": "^4.5.1"
21+
"chart.js": "^4.5.1",
22+
"svelte-i18n": "^4.0.1"
2223
},
2324
"devDependencies": {
2425
"@sveltejs/adapter-static": "^3.0.6",

src/lib/AnalyticsConsentModal.svelte

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<script>
22
import { invoke } from "@tauri-apps/api/core";
33
import { updateAnalyticsConsent, identifyUser } from "$lib/analytics.js";
4+
import { _ } from "svelte-i18n";
45
56
let { onClose = () => {} } = $props();
67
let isSaving = $state(false);
@@ -28,35 +29,35 @@
2829
<div class="modal-backdrop" onclick={() => {}}>
2930
<div class="modal-card" onclick={(e) => e.stopPropagation()}>
3031
<div class="modal-header">
31-
<h2>Help Improve Dota Keeper</h2>
32+
<h2>{$_('analytics_consent.title')}</h2>
3233
</div>
3334

3435
<div class="modal-body">
3536
<p class="intro">
36-
Would you like to help improve Dota Keeper by sending anonymous usage data?
37+
{$_('analytics_consent.intro')}
3738
</p>
3839

3940
<div class="info-section">
40-
<h3>✅ What we collect:</h3>
41+
<h3>{$_('analytics_consent.collect_title')}</h3>
4142
<ul>
42-
<li>App version and platform (Windows/macOS/Linux)</li>
43-
<li>Page views (e.g. "opened Goals page")</li>
44-
<li>Feature usage (e.g. "goal created", "challenge accepted")</li>
45-
<li>Error events (type only, no personal details)</li>
43+
<li>{$_('analytics_consent.collect_1')}</li>
44+
<li>{$_('analytics_consent.collect_2')}</li>
45+
<li>{$_('analytics_consent.collect_3')}</li>
46+
<li>{$_('analytics_consent.collect_4')}</li>
4647
</ul>
4748
</div>
4849

4950
<div class="info-section">
50-
<h3>🚫 What we DON'T collect:</h3>
51+
<h3>{$_('analytics_consent.not_collect_title')}</h3>
5152
<ul>
52-
<li>Steam ID or any personally identifiable information</li>
53-
<li>Match IDs, hero choices, or game data</li>
54-
<li>Goal descriptions, targets, or any content you enter</li>
53+
<li>{$_('analytics_consent.not_collect_1')}</li>
54+
<li>{$_('analytics_consent.not_collect_2')}</li>
55+
<li>{$_('analytics_consent.not_collect_3')}</li>
5556
</ul>
5657
</div>
5758

5859
<p class="note">
59-
You can change this preference anytime in Settings → Privacy.
60+
{$_('analytics_consent.note')}
6061
</p>
6162
</div>
6263

@@ -66,14 +67,14 @@
6667
onclick={() => handleConsent("Accepted")}
6768
disabled={isSaving}
6869
>
69-
{isSaving ? 'Saving...' : 'Accept'}
70+
{isSaving ? $_('analytics_consent.saving') : $_('analytics_consent.accept')}
7071
</button>
7172
<button
7273
class="btn btn-ghost"
7374
onclick={() => handleConsent("Declined")}
7475
disabled={isSaving}
7576
>
76-
Decline
77+
{$_('analytics_consent.decline')}
7778
</button>
7879
</div>
7980
</div>

src/lib/BottomNav.svelte

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,41 @@
11
<script>
22
import { page } from "$app/stores";
3+
import { _ } from "svelte-i18n";
34
45
const navItems = [
56
{
67
href: "/",
7-
label: "Home",
8+
labelKey: "home",
89
exact: true,
910
icon: `<path stroke-linecap="round" stroke-linejoin="round" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />`
1011
},
1112
{
1213
href: "/matches",
13-
label: "Matches",
14+
labelKey: "matches",
1415
exact: false,
1516
icon: `<path stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" />`
1617
},
1718
{
1819
href: "/analysis",
19-
label: "Analysis",
20+
labelKey: "analysis",
2021
exact: false,
2122
icon: `<path stroke-linecap="round" stroke-linejoin="round" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />`
2223
},
2324
{
2425
href: "/goals",
25-
label: "Goals",
26+
labelKey: "goals",
2627
exact: false,
2728
icon: `<path stroke-linecap="round" stroke-linejoin="round" d="M5 3v4M3 5h4M6 17v4m-2-2h4m5-16l2.286 6.857L21 12l-5.714 2.143L13 21l-2.286-6.857L5 12l5.714-2.143L13 3z" />`
2829
},
2930
{
3031
href: "/mental-health",
31-
label: "Mind",
32+
labelKey: "mind",
3233
exact: false,
3334
icon: `<path stroke-linecap="round" stroke-linejoin="round" d="M21 8.25c0-2.485-2.099-4.5-4.688-4.5-1.935 0-3.597 1.126-4.312 2.733-.715-1.607-2.377-2.733-4.313-2.733C5.1 3.75 3 5.765 3 8.25c0 7.22 9 12 9 12s9-4.78 9-12z" />`
3435
},
3536
{
3637
href: "/settings",
37-
label: "Settings",
38+
labelKey: "settings",
3839
exact: false,
3940
icon: `<path stroke-linecap="round" stroke-linejoin="round" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" /><path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />`
4041
}
@@ -52,7 +53,7 @@
5253
<svg class="tab-icon" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5">
5354
{@html item.icon}
5455
</svg>
55-
<span class="tab-label">{item.label}</span>
56+
<span class="tab-label">{$_('nav.' + item.labelKey)}</span>
5657
</a>
5758
{/each}
5859
</nav>

src/lib/FeedbackModal.svelte

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import { getVersion } from "@tauri-apps/api/app";
44
import { onMount } from "svelte";
55
import { showToast } from "$lib/toast.js";
6+
import { _ } from "svelte-i18n";
67
78
import { PUBLIC_SUPABASE_URL as SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY as SUPABASE_ANON_KEY } from '$env/static/public';
89
@@ -22,10 +23,10 @@
2223
}
2324
});
2425
25-
const followUpPrompts = {
26-
bug: "What happened? What did you expect?",
27-
feature: "What were you trying to do that wasn't possible?",
28-
positive: "What specifically do you like? Why is it useful?",
26+
const followUpPromptKeys = {
27+
bug: "feedback.prompt_bug",
28+
feature: "feedback.prompt_feature",
29+
positive: "feedback.prompt_positive",
2930
};
3031
3132
function getPlatform() {
@@ -40,7 +41,7 @@
4041
if (!category || !feedbackText.trim()) return;
4142
4243
if (!SUPABASE_URL || !SUPABASE_ANON_KEY) {
43-
showToast("Feedback is not configured yet.", "error");
44+
showToast($_('feedback.not_configured'), "error");
4445
return;
4546
}
4647
@@ -68,13 +69,13 @@
6869
6970
if (!res.ok) throw new Error(`HTTP ${res.status}`);
7071
71-
showToast("Feedback submitted — thank you!", "success");
72+
showToast($_('feedback.toast_success'), "success");
7273
category = "";
7374
feedbackText = "";
7475
priority = "";
7576
onClose();
7677
} catch (e) {
77-
showToast("Failed to submit feedback. Please try again.", "error");
78+
showToast($_('feedback.toast_error'), "error");
7879
} finally {
7980
isSubmitting = false;
8081
}
@@ -85,14 +86,14 @@
8586
<div class="modal-card" onclick={(e) => e.stopPropagation()} role="dialog" aria-modal="true" aria-label="Send feedback">
8687

8788
<div class="modal-header">
88-
<h2>Send Feedback</h2>
89+
<h2>{$_('feedback.title')}</h2>
8990
<button class="close-btn" onclick={onClose} aria-label="Close">✕</button>
9091
</div>
9192

9293
<div class="modal-body">
9394
<!-- Step 1: Category -->
9495
<div class="field-group">
95-
<label class="field-label">What kind of feedback is this?</label>
96+
<label class="field-label">{$_('feedback.category_label')}</label>
9697
<div class="category-options">
9798
<button
9899
class="category-btn"
@@ -101,7 +102,7 @@
101102
type="button"
102103
>
103104
<span class="cat-icon">🐛</span>
104-
<span>Bug / Something broken</span>
105+
<span>{$_('feedback.bug')}</span>
105106
</button>
106107
<button
107108
class="category-btn"
@@ -110,7 +111,7 @@
110111
type="button"
111112
>
112113
<span class="cat-icon">💡</span>
113-
<span>Missing feature or improvement</span>
114+
<span>{$_('feedback.feature')}</span>
114115
</button>
115116
<button
116117
class="category-btn"
@@ -119,7 +120,7 @@
119120
type="button"
120121
>
121122
<span class="cat-icon">⭐</span>
122-
<span>Works great — tell us what you like</span>
123+
<span>{$_('feedback.positive')}</span>
123124
</button>
124125
</div>
125126
</div>
@@ -128,13 +129,13 @@
128129
{#if category}
129130
<div class="field-group">
130131
<label class="field-label" for="feedback-text">
131-
{followUpPrompts[category]}
132+
{$_(followUpPromptKeys[category])}
132133
</label>
133134
<textarea
134135
id="feedback-text"
135136
class="feedback-textarea"
136137
bind:value={feedbackText}
137-
placeholder="Your answer..."
138+
placeholder={$_('feedback.placeholder')}
138139
rows="3"
139140
maxlength="500"
140141
></textarea>
@@ -143,12 +144,12 @@
143144
<!-- Step 3: Priority (only for bug / feature) -->
144145
{#if category !== "positive"}
145146
<div class="field-group">
146-
<label class="field-label">How much is this affecting your use of the app? <span class="optional">(optional)</span></label>
147+
<label class="field-label">{$_('feedback.priority_label')} <span class="optional">{$_('feedback.optional')}</span></label>
147148
<div class="priority-options">
148149
{#each [
149-
{ value: "blocking", label: "Blocking — I can't use the app properly" },
150-
{ value: "annoying", label: "Annoying — I work around it" },
151-
{ value: "minor", label: "Minor — Nice to have" },
150+
{ value: "blocking", tkey: "feedback.priority_blocking" },
151+
{ value: "annoying", tkey: "feedback.priority_annoying" },
152+
{ value: "minor", tkey: "feedback.priority_minor" },
152153
] as opt}
153154
<label class="priority-radio">
154155
<input
@@ -157,7 +158,7 @@
157158
value={opt.value}
158159
bind:group={priority}
159160
/>
160-
<span>{opt.label}</span>
161+
<span>{$_(opt.tkey)}</span>
161162
</label>
162163
{/each}
163164
</div>
@@ -167,14 +168,14 @@
167168
</div>
168169

169170
<div class="modal-actions">
170-
<button class="btn btn-ghost" onclick={onClose} type="button">Cancel</button>
171+
<button class="btn btn-ghost" onclick={onClose} type="button">{$_('feedback.cancel')}</button>
171172
<button
172173
class="btn btn-primary"
173174
onclick={handleSubmit}
174175
disabled={!category || !feedbackText.trim() || isSubmitting}
175176
type="button"
176177
>
177-
{isSubmitting ? "Sending..." : "Send Feedback"}
178+
{isSubmitting ? $_('feedback.sending') : $_('feedback.send')}
178179
</button>
179180
</div>
180181
</div>

src/lib/MoodCheckin.svelte

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<script>
22
import { invoke } from "@tauri-apps/api/core";
3+
import { _ } from "svelte-i18n";
34
45
let { matchId, onComplete } = $props();
56
@@ -13,12 +14,19 @@
1314
1415
const energyEmojis = ["😴", "😐", "🙂", "😄", ""];
1516
const calmEmojis = ["😤", "😠", "😐", "😊", "🧘"];
17+
// English values stored in DB; tkeys used for display only
1618
const attributionOptions = [
1719
"Teammates",
1820
"The enemy",
1921
"My own mistakes",
2022
"Nothing — I was fine actually",
2123
];
24+
const attributionTKeys = [
25+
"mood_checkin.attribution_teammates",
26+
"mood_checkin.attribution_enemy",
27+
"mood_checkin.attribution_own",
28+
"mood_checkin.attribution_fine",
29+
];
2230
2331
async function submit() {
2432
if (energy === null || calm === null) return;
@@ -51,11 +59,11 @@
5159
<div class="checkin-card">
5260
<div class="checkin-header">
5361
<span class="checkin-icon">🧠</span>
54-
<h3 class="checkin-title">How was that session?</h3>
62+
<h3 class="checkin-title">{$_('mood_checkin.title')}</h3>
5563
</div>
5664

5765
<div class="question-block">
58-
<p class="question-label">How energised did you feel?</p>
66+
<p class="question-label">{$_('mood_checkin.energy_q')}</p>
5967
<div class="emoji-scale">
6068
{#each energyEmojis as emoji, i}
6169
<button
@@ -72,7 +80,7 @@
7280
</div>
7381

7482
<div class="question-block">
75-
<p class="question-label">How calm were you?</p>
83+
<p class="question-label">{$_('mood_checkin.calm_q')}</p>
7684
<div class="emoji-scale">
7785
{#each calmEmojis as emoji, i}
7886
<button
@@ -90,15 +98,15 @@
9098

9199
{#if showQ3}
92100
<div class="question-block">
93-
<p class="question-label">What got under your skin?</p>
101+
<p class="question-label">{$_('mood_checkin.skin_q')}</p>
94102
<div class="attribution-options">
95-
{#each attributionOptions as option}
103+
{#each attributionOptions as option, i}
96104
<button
97105
class="attribution-btn"
98106
class:selected={attribution === option}
99107
onclick={() => (attribution = attribution === option ? null : option)}
100108
>
101-
{option}
109+
{$_(attributionTKeys[i])}
102110
</button>
103111
{/each}
104112
</div>
@@ -107,14 +115,14 @@
107115

108116
<div class="checkin-actions">
109117
<button class="skip-btn" onclick={skip} disabled={isSubmitting}>
110-
Skip
118+
{$_('mood_checkin.skip')}
111119
</button>
112120
<button
113121
class="submit-btn"
114122
onclick={submit}
115123
disabled={isSubmitting || energy === null || calm === null}
116124
>
117-
{isSubmitting ? "Saving..." : "Submit"}
125+
{isSubmitting ? $_('mood_checkin.saving') : $_('mood_checkin.submit')}
118126
</button>
119127
</div>
120128
</div>

0 commit comments

Comments
 (0)