Skip to content

Commit 836f47a

Browse files
feat(ui): implement translator mode, with reports and dynamic message loading for translators
1 parent 0df12da commit 836f47a

11 files changed

Lines changed: 372 additions & 19 deletions

File tree

package-lock.json

Lines changed: 10 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@
7373
"@tauri-apps/api": "^1.5.3",
7474
"@turf/turf": "^6.5.0",
7575
"@zip.js/zip.js": "^2.7.41",
76+
"acorn": "^8.11.3",
7677
"alea": "^1.0.1",
7778
"d3-color": "^3.1.0",
7879
"d3-delaunay": "^6.0.4",
@@ -92,6 +93,7 @@
9293
"polylabel": "^1.1.0",
9394
"rambda": "^9.2.0",
9495
"steam-path": "^1.0.2",
96+
"tauri-plugin-fs-watch-api": "github:tauri-apps/tauri-plugin-fs-watch#v1",
9597
"topojson-client": "^3.1.0",
9698
"topojson-server": "^3.0.1",
9799
"topojson-simplify": "^3.0.3",

src-tauri/Cargo.lock

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

src-tauri/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ image_dds = { version = "0.4.0", default-features = false, features = ["ddsfile"
2424
ddsfile = "0.5.2"
2525
logos = "0.14.0"
2626
tauri = { version = "1.6", features = [ "fs-read-file", "fs-exists", "fs-create-dir", "dialog-open", "dialog-save", "fs-write-file", "os-all", "path-all", "shell-open"] }
27+
tauri-plugin-fs-watch = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" }
2728

2829
[features]
2930
# this feature is used for production builds or when `devPath` points to the filesystem and the built-in dev server is disabled.

src-tauri/src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ mod parser;
2525

2626
fn main() {
2727
tauri::Builder::default()
28+
.plugin(tauri_plugin_fs_watch::init())
2829
.invoke_handler(tauri::generate_handler![
2930
get_stellaris_colors_cmd,
3031
get_stellaris_loc_cmd,

src/renderer/src/intl/en-US.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,11 @@ export default {
8888
app_settings: {
8989
title: 'Settings',
9090
description: 'All changes are automatically saved and applied.',
91+
select_translator_mode_file: 'Select Translation File',
92+
translator_mode_no_file: 'No file selected',
93+
translator_mode_file: 'Watching {filePath}',
94+
translator_mode_untranslated_messages: 'Untranslated messages: {number, number}',
95+
translator_mode_extra_messages: 'Invalid message IDs: {number, number}',
9196
},
9297
// labels for various types of setting controls
9398
// (shared by multiple settings)
@@ -336,7 +341,17 @@ export default {
336341
starScapeStarsColor: 'Stars Color',
337342
starScapeStarsCount: 'Star Count',
338343
appLocale: 'StellarMaps Language',
344+
appLocale_tooltip: 'Join the Discord server (link the top bar) if you want to help translate!',
339345
appStellarisLanguage: 'Stellaris Language',
340346
appTranslatorMode: 'Translator Mode',
347+
appTranslatorMode_tooltip: `
348+
<strong>For StellarMaps translators:</strong>
349+
<ul>
350+
<li>Hold Alt to view message IDs.</li>
351+
<li>Select a translation file to use for messages.</li>
352+
<li>Reloads messages when the file is edited.</li>
353+
<li>Shows warnings about missing and extra messages.</li>
354+
<li>You need to reselect the file if you reload the application.</li>
355+
</ul>`,
341356
},
342357
};

src/renderer/src/intl/index.ts

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ const locales = {
2626
ENGLISH: Object.fromEntries(
2727
Object.entries(flattenMessages(enUS)).map(([k, v]) => [k, v.toUpperCase()]),
2828
) as Partial<Record<MessageID, string>>,
29+
MessageID: Object.fromEntries(Object.keys(flattenMessages(enUS)).map((k) => [k, k])) as Record<
30+
MessageID,
31+
string
32+
>,
2933
};
3034
type Locale = keyof typeof locales;
3135

@@ -43,18 +47,34 @@ function getBestLocale(): Locale {
4347

4448
export const locale = writable<Locale>(getBestLocale());
4549

46-
export const t = derived(locale, (localeKey) => {
47-
const messages: Record<string, IntlMessageFormat> = {};
48-
return function t(
49-
messageId: MessageID,
50-
values?: Record<string, PrimitiveType | FormatXMLElementFn<string>>,
51-
) {
52-
const message =
53-
messages[messageId] ??
54-
new IntlMessageFormat(
55-
locales[localeKey][messageId] ?? locales['en-US'][messageId],
56-
localeKey,
57-
);
58-
return `${message.format(values)}`;
59-
};
60-
});
50+
export const translatorModeMessages = writable<Record<string, string>>({});
51+
export const translatorModeExtraMessageIDs = derived(translatorModeMessages, (messages) =>
52+
Object.keys(messages).filter((key) => !(key in locales['en-US'])),
53+
);
54+
export const translatorModeUntranslatedMessageIDs = derived(translatorModeMessages, (messages) =>
55+
Object.keys(locales['en-US']).filter(
56+
(key) => !(key in messages) || messages[key] === locales['en-US'][key as MessageID],
57+
),
58+
);
59+
60+
export const t = derived(
61+
[locale, translatorModeMessages],
62+
([localeKey, translatorModeMessages]) => {
63+
const messages: Record<string, IntlMessageFormat> = {};
64+
return function t(
65+
messageId: MessageID,
66+
values?: Record<string, PrimitiveType | FormatXMLElementFn<string>>,
67+
) {
68+
if (localeKey === 'MessageID') return messageId;
69+
const message =
70+
messages[messageId] ??
71+
new IntlMessageFormat(
72+
translatorModeMessages[messageId] ??
73+
locales[localeKey][messageId] ??
74+
locales['en-US'][messageId],
75+
localeKey,
76+
);
77+
return `${message.format(values)}`;
78+
};
79+
},
80+
);
Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
11
<script>
2-
import { t } from '../intl';
2+
import { getModalStore, getToastStore } from '@skeletonlabs/skeleton';
3+
import { t, translatorModeExtraMessageIDs, translatorModeUntranslatedMessageIDs } from '../intl';
34
import SettingControl from './SettingControl/index.svelte';
45
import { appSettings, appSettingsConfig, asUnknownSettingConfig } from './settings';
6+
import { selectTranslatorModeFile, translatorModeFilePath } from './translatorMode';
7+
8+
const _props = $$props; // this suppresses warning about unknown prop 'parent'
9+
10+
const modalStore = getModalStore();
11+
const toastStore = getToastStore();
512
</script>
613

714
<form
8-
class="bg-surface-100-800-token modal block h-auto w-[32rem] space-y-4 overflow-y-auto p-4 shadow-xl rounded-container-token"
15+
class="bg-surface-100-800-token modal block h-auto max-h-[90vh] w-[32rem] space-y-4 overflow-y-auto p-4 shadow-xl rounded-container-token"
916
role="dialog"
1017
aria-modal="true"
1118
novalidate
@@ -16,8 +23,52 @@
1623
{#each appSettingsConfig as config}
1724
<SettingControl config={asUnknownSettingConfig(config)} settings={appSettings} />
1825
{/each}
26+
{#if $appSettings.appTranslatorMode}
27+
<button
28+
class="variant-ghost-primary btn -my-3"
29+
type="button"
30+
on:click={() => selectTranslatorModeFile(toastStore)}
31+
>
32+
{$t('app_settings.select_translator_mode_file')}
33+
</button>
34+
{#if $translatorModeFilePath}
35+
<small>
36+
{$t('app_settings.translator_mode_file', { filePath: $translatorModeFilePath })}
37+
</small>
38+
{:else}
39+
<small>{$t('app_settings.translator_mode_no_file')}</small>
40+
{/if}
41+
{#if $translatorModeFilePath && $translatorModeUntranslatedMessageIDs.length > 0}
42+
<strong class="block text-warning-400">
43+
{$t('app_settings.translator_mode_untranslated_messages', {
44+
number: $translatorModeUntranslatedMessageIDs.length,
45+
})}
46+
</strong>
47+
<ul class="list-disc ps-4">
48+
{#each $translatorModeUntranslatedMessageIDs.slice(0, 5) as messageId}<li>
49+
{messageId}
50+
</li>{/each}
51+
{#if $translatorModeUntranslatedMessageIDs.length > 5}<li>...</li>{/if}
52+
</ul>
53+
{/if}
54+
{#if $translatorModeExtraMessageIDs.length > 0}
55+
<strong class="block text-warning-400">
56+
{$t('app_settings.translator_mode_extra_messages', {
57+
number: $translatorModeExtraMessageIDs.length,
58+
})}
59+
</strong>
60+
<ul class="list-disc ps-4">
61+
{#each $translatorModeExtraMessageIDs.slice(0, 5) as messageId}<li>
62+
{messageId}
63+
</li>{/each}
64+
{#if $translatorModeExtraMessageIDs.length > 5}<li>...</li>{/if}
65+
</ul>
66+
{/if}
67+
{/if}
1968
</div>
2069
<footer class="modal-footer flex justify-end space-x-2">
21-
<button class="variant-ghost-surface btn" type="button">{$t('generic.close_button')}</button>
70+
<button class="variant-ghost-surface btn" type="button" on:click={() => modalStore.close()}>
71+
{$t('generic.close_button')}
72+
</button>
2273
</footer>
2374
</form>

0 commit comments

Comments
 (0)